pax_global_header00006660000000000000000000000064127103765030014516gustar00rootroot0000000000000052 comment=98188eb57e5b5422afa95d8ab7b75348e3cb7480 Indigo-indigo-1.2.3/000077500000000000000000000000001271037650300142015ustar00rootroot00000000000000Indigo-indigo-1.2.3/.gitignore000066400000000000000000000001631271037650300161710ustar00rootroot00000000000000*.pyc build/ dist/ api/libs/ api/src/indigo_version.h third_party/cairo-src/cairo/src/cairo-features.h _projects/ Indigo-indigo-1.2.3/README.md000066400000000000000000000054061271037650300154650ustar00rootroot00000000000000# EPAM Indigo projects # Copyright (c) 2009-2015 EPAM Systems GNU General Public License version 3 ## Introduction ## This repository includes: * Bingo: Chemistry search engine for Oracle, Microsoft SQL Server and PostgreSQL databases * Indigo: Universal cheminformatics library, and the following tools: - Legio: GUI application for combinatorial chemistry - ChemDiff: Visual comparison of two SDF or SMILES files - indigo-depict: Molecule and reaction rendering utility - indigo-cano: Canonical SMILES generator - indigo-deco: R-Group deconvolution utility Detailed documentations is available at http://lifescience.opensource.epam.com Main directory structure layout: * api: Indigo API sources * bingo: Bingo sources * build_scripts: CMake and python scripts for building all the sources * third_party: sources for third-party libraries * utils: utilities sources * common|graph|layout|molecule|reaction|render2d: indigo-core sources ## Source code organization ## Each project is placed in the corresponding directory with CMakeList.txt configuration file, that does not include other projects. In order to build the whole project with the correct references you need to use CMake configurations from the build_scripts directory. ## Build instructions ## All the cmake projects are placed in build_scripts directories. You can use them manually, or execute preconfigured scripts that does all the job. ## Bingo build instructions ## To generate project configuration, build the source code, and create the archives for installation you need to execute build_scripts\bingo-release.py: build_scripts\bingo-release.py --preset=linux32 --dbms=[postgres|oracle|sqlserver] The are different cmake presets: linux32, linux64, win32, win64, mac10.5, mac10.6 (for 10.7 also) ## Indigo build instructions ## To generate project configuration, build the source code, and create the archives for installation you need to execute build_scripts\bingo-release.py: ### Linux (gcc 4.7+) build_scripts\indigo-release-libs.py --preset=linux64 build_scripts\indigo-release-utils.py --preset=linux64 ### Windows (Microsoft Visual Studio 2013+) build_scripts\indigo-release-libs.py --preset=win64-2013 build_scripts\indigo-release-utils.py --preset=win64-2013 ### Mac OS (Clang 3.0+) build_scripts\indigo-release-libs.py --preset=mac10.10 build_scripts\indigo-release-utils.py --preset=mac10.10 ### Other There are different cmake presets: win32-2013, win64-2013, win32-mingw, linux32, linux32-universal, linux64, linux64-universal, mac10.6, mac10.7, mac10.8, mac10.9, mac10.10, mac-universal ### Wrappers To generate Java, C#, or Python wrappers after build the binaries you need to execute build_scripts\indigo-make-by-libs.py --type=java Available types: java, dotnet, python Indigo-indigo-1.2.3/api/000077500000000000000000000000001271037650300147525ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/CHANGELOG.md000066400000000000000000000676601271037650300166020ustar00rootroot00000000000000############ Indigo 1.2.1 ############ *30 April 2015* ******* Summary ******* **New features and improvements**: * Bingo NoSQL new plugin for Indigo was released. * ``standardize()`` function was implemented * SGroup better support. Loading, saving, searching, editing different types of SGroups * ``canonicalSmiles()`` for *Reactions* * Molfile V3000 type9 and type10 bonds support was implemented * ChemDraw CDX reader was implemented * Stereo and cis-trans configuration better support * Bidirectional stereocenters mode (used by ChemDraw) support was implemented. :optref:`stereochemistry-bidirectional-mode` option was added. * Stereocenters detection for the haworth projection was implemented. New option :optref:`stereochemistry-detect-haworth-projection` was added **Bugfixes**: * SGroups releated bugs were fixed * Indigo now calculates stereocenters for SMARTS like ``[*@H](~*)~*`` * Bug with ``countComponents()`` was fixed * Custom collection names loading in molfile V3000 * Bug with superatoms saving was fixed * Other small bugfixes Indigo 1.1.10 ---------- *19 April 2013* This release has two major additions: 1. We started to support Mono and provide a .NET version that runs on Linux and Mac OS X. This was done with help of developers from Royal Society of Chemistry () who also helped us to localize and fix several stability issues in our .NET version. 2. Indigo now understand aromatic rings with external double bonds such as O=C1C=CC=CC=C1. To enable this mode you have to set "aromaticity-model" option to "generic" (while default version is "basic"). All changes: * Mono support: now we provide .NET modules for Windows, Linux, Mac OS X, and a universal bundle that works on all the specified platforms. Thanks to Dimitry Ivanov for various suggestions: * Indigo Renderer automatically selects output format based on the file extension. It is not necessary to specify "render-output-format" option if you are rendering into a file. * Original hydrogens are not folded during molecule transformations. Thanks to Ken for the bug report: * Either cis/trans bond marked is preserved during molfile loading even if substitutes are collinear. Again thanks to Ken for the suggestion: * Indigo Renderer now has additional options for partial image size specification. These options are: "render-image-width", "render-image-height", "render-image-max-width", "render-image-max-height" * Fixed an issue in the SMILES loader module that set invalid number of implicit hydrogens: * Generic aromaticity model that can be enabled by "aromaticity-model" option. Thanks to Daniel for pushing us to implement this functionality. * Another aromaticity option for find any kekulize configuration if Indigo cannot dearomatize a structure. For example Indigo cannot dearomatize the structure c1ccc2c(ccc2)cc1, because there are no bonds configuration such that the structure is aromatic. But you can try to find approximate kekulize configuration if you specify option "dearomatize-verification" to "false". * Indigo now uses dearomatization module to find number of hydrogens in aromatic rings in the IndigoObject.canonicalSmiles() method. If hydrogens configuration is ambiguous then Indigo throws an exception about this. * Additional "unique-dearomatization" option. If this option is set to true Indigo will throw an exception of dearomatization configuration is ambiguous (that means that canonical SMILES cannot be generated): * IndigoInchi.loadMolecule can now load molecules from InChI Aux infromation. Thanks to Nico: * Indigo Renderer doesn't have a dependency on the new GLIBC any more. This dependency prevented loading of Indigo Renderer on CentOS 5.9 and less. * Minor changes in Java and C# bindings: expandAbbreviations method has been add, typo in countDataSGroups has been fixed * New method to get stereocenter pyramid. Thanks to Daniel for the feature request: * Fingerprints computation now works 30% faster. * All stereocenter exceptions now includes also atom index: Indigo 1.1.9 ---------- *25 March 2013* New features: * Single atoms are encoded into Indigo fingerprint. In the previous versions we enumerated subgraphs starting from 2 atoms. * new method IndigoObject.resetSymmetricStereocenters to clear stereocenters that are not real stereocenters like in `CC[C@@H](CN)CC` Changes: * Implicit hydrogens are rendered better if they are on the bottom of an aromatic ring * Missing dependency file msvcp100.dll is loaded automatically. * Smiles saver doesn't throw exception about implicit hydrogens if they are not saving in SMILES * Workaround for a clang 3.2 compiler bug that caused incorrect similarity values on Mac OS X platforms Indigo 1.1.8 ---------- *10 March 2013* New features: * For superatoms IndigoObject.name() returns superatom label * New method indigoExpandAbbreviations in the Indigo C interface. Bindings for Java, C# will be ready soon. * Single record in SDF file is limited to 100Mb to prevent accident out-of-memory exceptions when loading non-SDF files. * Indigo compact molecule and reaction format is compatible with previous versions. KNIME workflows that are using Indigo nodes will work fine with previously saved workflows. * Indigo.loadMolecule reads molecule properties from SDF file too. Karen Karapetyan: Fixed: * Issues with transformations. Thanks to Karen Karapetyan for various bug reports! * Exception on invalid options in the InChI plugin * Issues with long multiline in molfile data s-groups * Aromaticity matcher issue. Thanks to James Davidson for the bug report: * Atom-to-atom mapping timeout issue * File handlers leak in indigo.iterateSDFile method in case of empty file Indigo 1.1.7 ---------- *24 December 2012* New features: * stack usage has been significantly reduced. Almost all the test works find under 256Kb stack limit, meaning that everything should work in .NET and Java environment without any additional settings. Problem appeared in using Indigo in WCF service in IIS. * initial implementation of `indigoNormalize` method in Indigo API. It removed hydrogens and neutralize [N+][O-] into N=O. Other transformation are coming soon and suggestions are welcome. * new similarity methods `normalized-edit` to return an approximate measure of changes that needs to be applied to convert one molecule into another. Used in Imago testing framework to measure recognition quality based on reference files. * reaction catalysts serialization * layout method flips a molecule to ensure that that first atom is right to the last one: * query molfile saver outputs a number of implicit hydrogens Fixed: * substructure matcher throws an exception if molecule has invalid valences: * aromatization method throws an exception if molecule has invalid valences: * molecule dearomatization with radicals doesn't work * several issues in reaction product enumerator * layout issue: * another issue with molecule R-groups layout * issue with saving a molfile with R-site with index 32 causing high memory consumption. Additional internal check has been added to prohibit unexpectedly large memory allocations (that usually means bug) * regression in the R-group label method for an R-site without any number * bug in the highlightedTarget method if a molecule has been changed before * SVG multithreaded rending has been disabled due to the potential issue with Cairo libraries. Need to update Cairo libraries to check if problem still appear. * issue with tautomer substructure matching for the aromatic compounds * molecule aromatization method doesn't affect R-group fragments Indigo 1.1.6 ---------- *15 October 2012 (no public announcement)* * option to preserve atom and bond ordering during serialization process. Used in KNIME: * reaction product enumerator handles larger class of transformations * option `smarts` for indigo-depict to draw SMARTS Indigo 1.1.5 ---------- *28 September 2012* New features: * Rutherfordium isotopes atomic weights added * Additional check for an invalid stereocenter when an angle between bonds is small. Thanks to Karen: * Options passed to the InChI plugin are being automatically corrected independent of OS. You can use both '-' and '/' prefixes on all the systems. Look more at Thanks to Karen for the suggestion: * Build scripts now work with Visual Studio 2012 * Cross-like layout of atom with four bonds for molecules like CCS(=O)(=O)CC Fixed: * Isotope values in the InChI -> Molecule conversion method are being shifted by 10000 * Issues in the Reaction Product Enumerator and the Transformation algorithm causing less correct results to appear * Exceptions during Molecule -> InChI conversion on Mac OS X if a molecule is passed in an aromatic form that cannot be dearomatized Indigo 1.1.4 ---------- *13 September 2012* * Allene stereocenters detection algorithm is not throwing exception in case there are not atom coordinates * Aromatic [si] can be loaded from SMILES * Dearomatization improvements for B, Si, P atoms. There was an issue that the dearomatization method didn't work with the molecule CB1OB(C)OB(C)O1 if it was loaded from SMILES in aromatic form. New methods: * setExplicitValence. * radical, setRadical. This methods returns and accepts Indigo.SINGLET, Indigo.DOUBLET or Indigo.TRIPLET radicals. Thanks to Ferenc for suggestions to add these methods: Indigo 1.1.3 ---------- *23 August 2012* * JNA has been updated to 3.4.1. This fixed an issue with permissions for the temporary directory. Thanks to Ingo: * Transformation method automatically calls a layout algorithm if necessary. * Minor bug in the reaction exact matching algorithm has been fixed. * Improvements in the layout of the atoms with four bonds attached. * GrossFormula now uses Hill notation: * Improvements in the SMARTS saving procedure. * Molfile saver now correctly saves query bond topology constraints, unsaturation flag, and atom ring bond count constraint. * Issues with rendering query bond topology constraints and atom ring bond count constraint have been fixed. * Data SGroups with absolute coordinates are treated as relative for the layout procedure. * SRU unit in the molfile now has a label. * Issues causing infinite loop due to the numeric errors in the layout algorithm have been fixed. * Issue with loading a molecule with 3D coordinates has been fixed. Thanks to Colin Batchelor: * Allene centers now are recognized if the angle between double bonds are greater than 165 degrees. Indigo 1.1.2 ---------- *10 July 2012* * Layout algorithm now doesn't apply Fischer projection for atoms with 4 bonds. For example, now the CC(C)(C)C(C)(C)C(C)(C)C(C)(C)C molecule is cleaned up in a zigzag way. * Bug with a missing stereocenter in the transformation and reaction product enumeration algorithms has been fixed: * Layout algorithm for molecules with R-groups has been fixed. Indigo 1.1.1 ---------- *18 June 2012* * symmetryClasses methods was added. Now the molecule object has a method symmetryClasses() that returns an array with a symmetry class ID for each atom. Thanks to Karen for the suggestion: * Query molecules can now have a highlighting constraint on atoms and bonds to match only (un)highlighted target atoms or bonds. Here is an exmaple: query.getAtom(0).addConstraint("highlighting", "true"). Again thanks to Karen: Indigo 1.1 ---------- *07 June 2012* * ChemDiff and Legio now supports the Indigo 1.1 version, installation scripts were fixed. Indigo 1.1 Release Candidate 3 ---------- *17 May 2012* * Aromatic Te can be read from SMILES as [te]. Thanks to Andrew Dalke: * Improvements in atom-to-atom mapping algorithm. Indigo 1.1 Release Candidate 2 ---------- *05 May 2012* Fixed: * Molecule with generic s-groups serialization * Missed IndigoRenderer within Java bundle Indigo 1.1 Release Candidate ---------- *30 April 2012* Highlights: * InChI stereochemistry layer is supported both for loading and saving molecules. The only difference with the standard utility occurs when stereochemistry is defined not in a proper way. Allenes and cumulenes are not supported yet. * new RGroup-Decomposition API was added: createDecomposer(), addDecomposition(), decomposeMolecule(), iterateDecompositions() See more details at Thanks to Gerhard: , Mederich: , and Simon: * We completely switched to CMake project configurations. Changes: * AAM new algorithm heuristic was implemented for disconnected reactant and product molecules. * correctReactingCenters() method was added for reactions. It highlights bond reacting centers according to AAM. Thanks to James: * "timeout" option is used for MCS computation. Fixes: * The bug with aam for query reactions was fixed * The bug with aam timeout was fixed. Thanks to Daniel: * clearStereocenters() method now resets bond directions. After calling this method molecule is saved into Molfile format without tetrahedral bond directions. * Exception during saving Molfile with pseudoatoms within aromatic rings * Exception when loading a molecule from Molfile with 3D coordinates with invalid valences during automatic stereocenters detection. * Some other issues. Indigo 1.1-beta10 ---------- *29 March 2012* Changes: * IndigoObject is Java now have dispose() method to dispose Indigo object before garbage collection. * Molfile atom lists now support pseudoatoms * Global timeout for all the most time consuming operations: substructure search, canonical smiles generation and etc. Option is called "timeout" and corresponts to milliseconds. * explicit hydrogen near Nitrogen is handled correctly to calculate cis-trans and tetrahedral stereo configuration. * InChI plugin now have "version" methods to return an actual InChI implementation version * Arial font is used on Linux systems to render molecules. Previously this font was used only on Windows and Mac OS X, and rendered images on Windows and Linux were different. * "deco-ignore-errors" option was added. Now there are no exceptions like 'no embeddings obtained' during the RGroup Decomposition if the flag set true. Exception is raised only for the end getters (e.g. decomposedMoleculeWithRGroups()) * "deco-save-ap-bond-orders" option was added. Within the option output molecule RGroup attachment points are saved as pseudo atoms (named 'AP1', 'AP2' etc). Therefore, the option allows to save initial bond orders. Thanks to Mederich: * bug with the time hang was fixed for AAM. Thanks to Daniel: * minor bug fixes in AAM * minor bug fixes in RGroup Decomposition Fixed: * automatic 2D coordinates generation procedure (layout) changes molecule components position if they have fixed atoms * cycle enumeration fixed. Thanks to Casey: * memory leak in the InChI computation procedure. Thanks to Hinnerk: * different minor exception when loading a molecule from a molfile * different minor exception when rendering a molecule Indigo 1.1-beta9 ---------- *25 February 2012* Changes: * if a molecule contains only R-group #2 then empty R-rgroup #1 is not rendered any more. * molecules with bad valences and charges can be serialized now * timeout option was added for AAM. A new option was added named "aam-timeout". The integer parameter (time in milliseconds) corresponds for the AAM algorithm working time. The automap method returns a current state solution for a reaction when time is over. Thanks to Daniel: * default layout call was added for the deconvolultion scaffold getter (decomposedMoleculeScaffold()) * empty RGroup handling (one single bond) was implemented for deco. * minor bug fixes in AAM * minor bug fixes in RGroup Decomposition Fixed: * incorrect empty R-Group logic loading from molfile * incorrect attachmement points loading from molfile if the number of attachments points is greater then 2 * memory leak in reaction substructure matcher. * infinite loop in reaction substructure matcher. Thanks to fab for the bug report for both issues: * invalid stereo configuration when atom are being changed. Thank to Lionel for the bug report: * bug with AAM not respecting atom type. Thanks to Daniel: Indigo 1.1-beta8 ---------- *29 January 2012* We have released our first version of InChI plugin that allows to load InChI strings and generate InChI and InChIKey for molecules (this version discards stereoinformation, but we are working on it). The plugin is statically linked with the official InChI library and can be loaded on demand, as it is done with IndigoRenderer plugin. Usage example : IndigoInchi inchi = new IndigoInchi(indigo); IndigoObject molecule = indigo.loadMolecule("InChI=1S/C3H9NO/c1-3(5)2-4/h3,5H,2,4H2,1H3"); String inchi_string = indigo.getInchi(molecule); New methods and functionallity: * InChI support! (without stereochemistry yet) * mapMolecule(queryReactoinMolecule) to retrieve mapped molecule for the query reaction for the reaction substrcuture match object * getMolecule(index) to get the reaction molecule * QueryMolecules can now be constructed with the following methods: 1. addAtom, resetAtom methods for the QueryMolecule now parses arbitrary SMARTS 2. addBond method for QueryMolecule 3. atom.addConstraintOr method has been added 4. a lot of query atom constraints: atomic-number, charge, isotope, radical, valence, connectivity, total-bond-order, hygrogens, substituents, ring, smallest-ring, ring-bonds, rsite-mask Fixed: * Issue with loading molecule attachment points if the bond orders are not marked. * Better handling of molecules with invalid valence: canonical SMILES, unfoldHydrogens, invalid stereocenters detection. Thanks to Mederich for the bug report: * Molecule serialization with more than 8 R-groups. Thanks to James Davidson for the bug report: Indigo 1.1-beta7 ---------- *29 December 2011* Changelog: * Fixed bug: render-grid-title-offset options is not initialized. * Fixed bug: all images are rendered as grid, after grid has been rendered. * Possible memory issue in IndigoRenderer for Java has been fixed. Indigo 1.1-beta6 ---------- *12 December 2011* New functionality: * Indigo.transform(reaction, molecule) method for transformation a molecule according to a rule, specified with a reaction. Examples are available here: * New IndigoObject methods for working with reaction atom-to-atom mapping: atomMappingNumber, setAtomMappingNumber, clearAAM * New IndigoObject methods for working with attachment points: iterateAttachmentPoints, countAttachmentPoints, clearAttachmentPoints. See for more details * Other new IndigoObject methods with documentation has been added: changeStereocenterType, addStereocenter, reactingCenter, setReactingCenter, loadSmartsFromFile, loadReactionSmartsFromFile, getSuperatom, getDataSGroup, description, decomposedMoleculeHighlighted, getSubmolecule, addSuperatom * Smiles saver might throw an exception on a molecule with explicit hydrogens. Thanks to Colin Batchelor: Changelog: * Improvements in the automatic atom-to-atom assignment. Thanks to Ernst-Georg Schmid: And to Daniel Lowe: * Improvements in the molecule decomposition algorithm. * Python 2.4 support. * A lot of bugs has been fixed due to some internal inconsistency in explicit hydrogens handing for cis-trans bonds: - Substructure matcher result can be incorrect for matching query molecule with cis-trans bonds. - Substructure matcher result can be incorrect in case of explicit hydrogens for cis-trans bonds in the target molecule. - If a molecule has explicit hydrogens near cis-trans bonds, after been unserialized cis-trans configuration might flip. - Canonical SMILES may also produce different results for a molecule with explicit hydrogens and without them. * Better stability of Indigo Java wrapper * Better rendering of a SMARTS query molecules * Indigo now informs with an exception that both cis- and trans- specification in the SMARTS expression is not supported yet. For example, such SMARTS is not supported yet: `*/,\[R]=;@[R]/,\*` * Fixed issue with saving query molecules in Molfile format with the atom lists. Thanks to Francesca: * unfoldHydrogens how works with reaction properly. * Some fixes of the dearomation algorithm bug arisen during tautomer substructure matching. * Better support of sgroups in Molfile * Highlighting is taken into account for the computation of canonical SMILES * Indigo.countHydrogens method doesn't throw an exception is case of existence of R-groups and pseudoatoms. * Fixed some issues with loading and saving of polymer repetition in SMILES * SGroups and R-sites are saving during serialization/unserilization. Thanks to Hinnerk Rey: * Faster matching of SMARTS queries with unspecified bonds. This change also improves efficiency for our fingerprints for query molecules. * Substructure matching counter now property counts queries with explicit hydrogens, like N-[#1]. Thanks to James Davidson for this bug report: * Stereocenter parities are now saved into molfile. Thanks to Lionel: * R-group iterator now skips R-groups that are empty. * Molfile loader now accepts left- and right-bounded atom labels. Thanks to Ernst-Georg Schmid: * renderGridToBuffer method now support null value as the second parameter. Thanks to Mederich: Indigo 1.1-beta5 ---------- *11 August 2011* New functionality: * Methods for specifing reacting centers on bonds: reaction.reactingCenter(bond), reaction.setReactingCenter(bond, mask) All reacting centers types are describes in Indigo namespace for Java and Python, and in ReactingCenter enum for C#. Code examples can be found in this thread: * Method to add stereocenter at atom: atom.addStereocenter(type, atom_index1, atom_index2, atom_index3, atom_index4). Last parameter is optional. Code examples can be found in this thread: Note: this new methods have preliminary interface, and interface may be changed in the next version. Fixed: * Molecule to Smiles conversion with explicit hydrogens connected to cis-trans bonds. Thanks to Colin Batchelor: Indigo 1.1-beta4 ---------- *29 July 2011* New functionality: * New methods for Indigo: resetAtom, setRSite, isHighlighted for atoms. Code example: atom.resetAtom("N"), atom.setRSite("R1, R3"), atom.isHighlighted() * Reaction product enumerator now supports recursive SMARTS Fixed: * Exception during Indigo for Java and Indigo for Python initialization on Mac OS X Lion 10.7 * Different AAM issues. Thanks to Daniel Lowe: * Exception when calling hasCoord and hasZCoord on a reaction object * Reaction product enumerator exception when monomers have no name Indigo 1.1-alpha3 ---------- *7 July 2011* New functionality since Indigo-1.0.0 stable version: * atomMappingNumber and setAtomMappingNumber methods for atoms to retrieve and change atom-to-atom numbers. New clearAAM method to clear atom-to-atom mapping information. Thanks to Daniel Lowe for pointing out that this functionality is missing. Code examples can be found in this thread: * addRSite method for adding R-site atoms to the molecule. This method is similar to addAtom. Code example: atom = mol.addRSite("R1") Fixed: * foldHydrogens on [H][H] and molecules with isotopic hydrogens ([2H]C). Thanks to Daniel Lowe: * Reaction layout for reactions with empty reactants * Saving molecule with s-group into molfile format * Substructure matcher with special query with recursive smarts beginning with hydrogen * Unbounded memory usage during reaction automapping. Thanks to Daniel Lowe again: * Indigo Python API module loading on Mac OS X from different directories might cause error messages * Reaction substructure match throws an exception in some cases when these is no pair of AAM numbers. For example, reactant has AAM number while product hasn't it. Indigo-indigo-1.2.3/api/CMakeLists.txt000066400000000000000000000063351271037650300175210ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.8) project(Indigo) include(indigo-version.cmake) include(DefineTest) MESSAGE(STATUS "Indigo version is ${INDIGO_VERSION}") SET(INDIGO_VERSION "${INDIGO_VERSION}" PARENT_SCOPE) # promote Indigo version configure_file(${Indigo_SOURCE_DIR}/src/indigo_version.h.in ${Indigo_SOURCE_DIR}/src/indigo_version.h) file (GLOB Indigo_src src/*.c*) file (GLOB Indigo_headers *.h* src/*.h*) # Get defined variable with the headers for TinyXML get_directory_property(TinyXML_HEADERS_DIR DIRECTORY ../third_party/tinyxml DEFINITION TinyXML_HEADERS_DIR) include_directories(${Indigo_SOURCE_DIR} ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${TinyXML_HEADERS_DIR} ${InChI_SOURCE_DIR}/inchi_dll) add_library(indigoObj OBJECT ${Indigo_src} ${Indigo_headers}) set_target_properties(indigoObj PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") #Indigo static if (NOT NO_STATIC) add_library(indigo STATIC $ $ $ $ $ $) SET_TARGET_PROPERTIES(indigo PROPERTIES OUTPUT_NAME "indigo-static") target_link_libraries(indigo tinyxml z inchi) set_property(TARGET indigo PROPERTY FOLDER "indigo") # No exports in case of static library: define empty EXPORT_SYMBOL definition set_target_properties(indigo PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DEXPORT_SYMBOL= ") pack_static(indigo) endif() # Indigo shared add_library(indigo-shared SHARED $ $ $ $ $ $ ${Common_SOURCE_DIR}/hacks/memcpy.c) SET_TARGET_PROPERTIES(indigo-shared PROPERTIES OUTPUT_NAME "indigo") set_target_properties(indigo-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if (APPLE) set_target_properties(indigo-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -undefined dynamic_lookup") endif() if (UNIX AND NOT APPLE) if(${SUBSYSTEM_NAME} MATCHES "x64") set_target_properties(indigo-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() target_link_libraries(indigo-shared -lpthread -lrt) endif() if (MSVC OR MINGW) set_target_properties(indigo-shared PROPERTIES PREFIX "") endif() target_link_libraries(indigo-shared z tinyxml inchi) if(UNIX AND NOT APPLE) set_property(TARGET indigo-shared PROPERTY LINK_INTERFACE_LIBRARIES "-lpthread;-lrt;z;tinyxml;inchi") else() set_property(TARGET indigo-shared PROPERTY LINK_INTERFACE_LIBRARIES "z;tinyxml;inchi") endif() set_property(TARGET indigo-shared PROPERTY FOLDER "indigo") if (NOT PACK_INDIGO_NOT) pack_shared(indigo-shared) endif() DEFINE_TEST(indigo-c-test-shared "tests/c/indigo-test.c" indigo-shared) add_executable(dlopen-test ${Indigo_SOURCE_DIR}/tests/c/dlopen-test.c) if (UNIX AND NOT APPLE) set_target_properties(dlopen-test PROPERTIES LINK_FLAGS "-rdynamic -ldl -pthread") if (NOT APPLE AND ${SUBSYSTEM_NAME} MATCHES "x64") set_target_properties(dlopen-test PROPERTIES LINK_FLAGS "-rdynamic -ldl -pthread -Wl,--wrap=memcpy") endif() TARGET_LINK_LIBRARIES(dlopen-test "-rdynamic -ldl -lpthread") endif() set_property(TARGET dlopen-test PROPERTY FOLDER "tests") Indigo-indigo-1.2.3/api/LICENSE.GPL000066400000000000000000001004421271037650300164010ustar00rootroot00000000000000 Indigo is Copyright (C) of EPAM Systems 2009-2015. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License below for more details. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS Indigo-indigo-1.2.3/api/dotnet/000077500000000000000000000000001271037650300162475ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/dotnet/Indigo.cs000066400000000000000000000600551271037650300200150ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.IO; using System.Drawing; using System.Diagnostics; using System.Collections; namespace com.epam.indigo { [Flags] public enum ReactingCenter { NOT_CENTER = -1, UNMARKED = 0, CENTER = 1, UNCHANGED = 2, MADE_OR_BROKEN = 4, ORDER_CHANGED = 8 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public unsafe class Indigo : IDisposable { public const int ABS = 1; public const int OR = 2; public const int AND = 3; public const int EITHER = 4; public const int UP = 5; public const int DOWN = 6; public const int CIS = 7; public const int TRANS = 8; public const int CHAIN = 9; public const int RING = 10; public const int ALLENE = 11; public const int SINGLET = 101; public const int DOUBLET = 102; public const int TRIPLET = 103; public const int RC_NOT_CENTER = -1; public const int RC_UNMARKED = 0; public const int RC_CENTER = 1; public const int RC_UNCHANGED = 2; public const int RC_MADE_OR_BROKEN = 4; public const int RC_ORDER_CHANGED = 8; private IndigoDllLoader dll_loader; public float checkResult(float result) { if (result < 0) { throw new IndigoException(new String(_indigo_lib.indigoGetLastError())); } return result; } public int checkResult(int result) { if (result < 0) { throw new IndigoException(new String(_indigo_lib.indigoGetLastError())); } return result; } public sbyte* checkResult(sbyte* result) { if (result == null) { throw new IndigoException(new String(_indigo_lib.indigoGetLastError())); } return result; } public float* checkResult(float* result) { if (result == null) { throw new IndigoException(new String(_indigo_lib.indigoGetLastError())); } return result; } public int* checkResult(int* result) { if (result == null) { throw new IndigoException(new String(_indigo_lib.indigoGetLastError())); } return result; } public long getSID() { return _sid; } public String version() { return new String(_indigo_lib.indigoVersion()); } public Indigo(string lib_path) { init(lib_path); } public Indigo() : this(null) { } public int countReferences() { setSessionID(); return checkResult(_indigo_lib.indigoCountReferences()); } public void setOption(string name, string value) { setSessionID(); checkResult(_indigo_lib.indigoSetOption(name, value)); } public void setOption(string name, int x, int y) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionXY(name, x, y)); } public void setOption(string name, bool value) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionBool(name, value ? 1 : 0)); } public void setOption(string name, float value) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionFloat(name, value)); } public void setOption(string name, int value) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionInt(name, value)); } public void setOption(string name, float valuer, float valueg, float valueb) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionColor(name, valuer / 255.0f, valueg / 255.0f, valueb / 255.0f)); } public void setOption(string name, Color value) { setSessionID(); checkResult(_indigo_lib.indigoSetOptionColor(name, value.R / 255.0f, value.G / 255.0f, value.B / 255.0f)); } public IndigoObject writeFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoWriteFile(filename))); } public IndigoObject writeBuffer() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoWriteBuffer())); } public IndigoObject createMolecule() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateMolecule())); } public IndigoObject createQueryMolecule() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateQueryMolecule())); } public IndigoObject loadMolecule(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadMoleculeFromString(str))); } public IndigoObject loadMolecule(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadMoleculeFromBuffer(buf, buf.Length))); } public IndigoObject loadMoleculeFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadMoleculeFromFile(path))); } public IndigoObject loadQueryMolecule(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryMoleculeFromString(str))); } public IndigoObject loadQueryMolecule(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryMoleculeFromBuffer(buf, buf.Length))); } public IndigoObject loadQueryMoleculeFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryMoleculeFromFile(path))); } public IndigoObject loadSmarts(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadSmartsFromString(str))); } public IndigoObject loadSmarts(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadSmartsFromBuffer(buf, buf.Length))); } public IndigoObject loadSmartsFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadSmartsFromFile(path))); } public IndigoObject loadReaction(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionFromString(str))); } public IndigoObject loadReaction(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionFromBuffer(buf, buf.Length))); } public IndigoObject loadReactionFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionFromFile(path))); } public IndigoObject loadQueryReaction(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryReactionFromString(str))); } public IndigoObject loadQueryReaction(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryReactionFromBuffer(buf, buf.Length))); } public IndigoObject loadQueryReactionFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadQueryReactionFromFile(path))); } public IndigoObject loadReactionSmarts(String str) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionSmartsFromString(str))); } public IndigoObject loadReactionSmarts(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionSmartsFromBuffer(buf, buf.Length))); } public IndigoObject loadReactionSmartsFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadReactionSmartsFromFile(path))); } public IndigoObject createReaction() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateReaction())); } public IndigoObject createQueryReaction() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateQueryReaction())); } public IndigoObject exactMatch(IndigoObject obj1, IndigoObject obj2, string flags) { setSessionID(); if (flags == null) flags = ""; int match = checkResult(_indigo_lib.indigoExactMatch(obj1.self, obj2.self, flags)); if (match == 0) return null; return new IndigoObject(this, match, new IndigoObject[] { obj1, obj2 }); } public IndigoObject exactMatch(IndigoObject obj1, IndigoObject obj2) { return exactMatch(obj1, obj2, ""); } public void setTautomerRule(int id, string beg, string end) { setSessionID(); checkResult(_indigo_lib.indigoSetTautomerRule(id, beg, end)); } public void removeTautomerRule(int id) { setSessionID(); checkResult(_indigo_lib.indigoRemoveTautomerRule(id)); } public void clearTautomerRules() { setSessionID(); checkResult(_indigo_lib.indigoClearTautomerRules()); } public IndigoObject unserialize(byte[] buf) { return new IndigoObject(this, checkResult(_indigo_lib.indigoUnserialize(buf, buf.Length))); } public IndigoObject createArray() { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateArray())); } public float similarity(IndigoObject obj1, IndigoObject obj2, string metrics) { setSessionID(); if (metrics == null) metrics = ""; return checkResult(_indigo_lib.indigoSimilarity(obj1.self, obj2.self, metrics)); } public int commonBits(IndigoObject obj1, IndigoObject obj2) { setSessionID(); return checkResult(_indigo_lib.indigoCommonBits(obj1.self, obj2.self)); } public IndigoObject iterateSDFile(string filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoIterateSDFile(filename))); } public IndigoObject iterateRDFile(string filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoIterateRDFile(filename))); } public IndigoObject iterateSmilesFile(string filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoIterateSmilesFile(filename))); } public IndigoObject iterateCMLFile(string filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoIterateCMLFile(filename))); } public IndigoObject iterateCDXFile(string filename) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoIterateCDXFile(filename))); } public IndigoObject substructureMatcher(IndigoObject target, string mode) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoSubstructureMatcher(target.self, mode)), target); } public IndigoObject substructureMatcher(IndigoObject target) { return substructureMatcher(target, ""); } public IndigoObject extractCommonScaffold(IndigoObject structures, string options) { setSessionID(); int res = checkResult(_indigo_lib.indigoExtractCommonScaffold(structures.self, options)); if (res == 0) return null; return new IndigoObject(this, res); } public IndigoObject toIndigoArray(IEnumerable collection) { setSessionID(); IndigoObject arr = createArray(); foreach (IndigoObject obj in collection) arr.arrayAdd(obj); return arr; } public static int[] toIntArray(ICollection collection) { if (collection == null) return new int[0]; int[] res = new int[collection.Count]; int i = 0; foreach (object x in collection) res[i++] = Convert.ToInt32(x); return res; } public static float[] toFloatArray(ICollection collection) { if (collection == null) return new float[0]; float[] res = new float[collection.Count]; int i = 0; foreach (object x in collection) res[i++] = Convert.ToSingle(x); return res; } public IndigoObject extractCommonScaffold(IEnumerable structures, string options) { return extractCommonScaffold(toIndigoArray(structures), options); } public IndigoObject decomposeMolecules(IndigoObject scaffold, IndigoObject structures) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoDecomposeMolecules(scaffold.self, structures.self))); } public IndigoObject decomposeMolecules(IndigoObject scaffold, IEnumerable structures) { return decomposeMolecules(scaffold, toIndigoArray(structures)); } public IndigoObject createDecomposer(IndigoObject scaffold) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateDecomposer(scaffold.self))); } public IndigoObject reactionProductEnumerate(IndigoObject reaction, IndigoObject monomers) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoReactionProductEnumerate(reaction.self, monomers.self))); } public IndigoObject reactionProductEnumerate(IndigoObject reaction, IEnumerable monomers) { setSessionID(); IndigoObject indigoArrayArray = createArray(); foreach (IEnumerable iter in monomers) { IndigoObject indigoArray = createArray(); foreach(IndigoObject monomer in iter) { indigoArray.arrayAdd(monomer); } indigoArrayArray.arrayAdd(indigoArray); } return new IndigoObject(this, checkResult(_indigo_lib.indigoReactionProductEnumerate(reaction.self, indigoArrayArray.self))); } public IndigoObject createSaver(IndigoObject output, string format) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoCreateSaver(output.self, format)), output); } public IndigoObject createFileSaver(string filename, string format) { setSessionID(); return new IndigoObject(this, checkResult(checkResult(_indigo_lib.indigoCreateFileSaver(filename, format)))); } public void transform(IndigoObject reaction, IndigoObject monomer) { setSessionID(); checkResult(_indigo_lib.indigoTransform(reaction.self, monomer.self)); } public IndigoObject loadBuffer(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadBuffer(buf, buf.Length))); } public IndigoObject loadString(string s) { setSessionID(); return new IndigoObject(this, checkResult(_indigo_lib.indigoLoadString(s))); } public IndigoObject iterateSDF(IndigoObject reader) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateSDF(reader.self)); if (result == 0) { return null; } return new IndigoObject(this, result, reader); } public IndigoObject iterateRDF(IndigoObject reader) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateRDF(reader.self)); if (result == 0) { return null; } return new IndigoObject(this, result, reader); } public IndigoObject iterateCML(IndigoObject reader) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateCML(reader.self)); if (result == 0) { return null; } return new IndigoObject(this, result, reader); } public IndigoObject iterateCDX(IndigoObject reader) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateCDX(reader.self)); if (result == 0) { return null; } return new IndigoObject(this, result, reader); } public IndigoObject iterateSmiles(IndigoObject reader) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateSmiles(reader.self)); if (result == 0) { return null; } return new IndigoObject(this, result, reader); } public IndigoObject tautomerEnumerate(IndigoObject molecule, string parameters) { setSessionID(); int result = checkResult(_indigo_lib.indigoIterateTautomers(molecule.self, parameters)); if (result == 0) { return null; } return new IndigoObject(this, result, molecule); } public void free(int id) { setSessionID(); checkResult(_indigo_lib.indigoFree(id)); } public String getDllPath() { return _dllpath; } public String dbgProfiling (bool whole_session) { setSessionID(); return new String(checkResult(_indigo_lib.indigoDbgProfiling(whole_session ? 1 : 0))); } public void dbgResetProfiling (bool whole_session) { setSessionID(); checkResult(_indigo_lib.indigoDbgResetProfiling(whole_session ? 1 : 0)); } private static void _handleError(sbyte* message, Indigo self) { throw new IndigoException(new String(message)); } private void init(string lib_path) { string libraryName; dll_loader = IndigoDllLoader.Instance; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: libraryName = "indigo.dll"; bool vs2010 = true; bool vs2012 = true; bool vs2013 = true; try { dll_loader.loadLibrary(lib_path, "msvcr100.dll", "com.epam.indigo.Properties.ResourcesWin2010", false); } catch { vs2010 = false; } try { dll_loader.loadLibrary(lib_path, "msvcr110.dll", "com.epam.indigo.Properties.ResourcesWin2012", false); } catch { vs2012 = false; } try { dll_loader.loadLibrary(lib_path, "msvcr120.dll", "com.epam.indigo.Properties.ResourcesWin2013", false); } catch { vs2013 = false; } if (vs2010) { dll_loader.loadLibrary(lib_path, "msvcr100.dll", "com.epam.indigo.Properties.ResourcesWin2010", false); dll_loader.loadLibrary(lib_path, "msvcp100.dll", "com.epam.indigo.Properties.ResourcesWin2010", false); dll_loader.loadLibrary(lib_path, libraryName, "com.epam.indigo.Properties.ResourcesWin2010", false); } else if (vs2012) { dll_loader.loadLibrary(lib_path, "msvcr110.dll", "com.epam.indigo.Properties.ResourcesWin2012", false); dll_loader.loadLibrary(lib_path, "msvcp110.dll", "com.epam.indigo.Properties.ResourcesWin2012", false); dll_loader.loadLibrary(lib_path, libraryName, "com.epam.indigo.Properties.ResourcesWin2012", false); } else if (vs2013) { dll_loader.loadLibrary(lib_path, "msvcr120.dll", "com.epam.indigo.Properties.ResourcesWin2013", false); dll_loader.loadLibrary(lib_path, "msvcp120.dll", "com.epam.indigo.Properties.ResourcesWin2013", false); dll_loader.loadLibrary(lib_path, libraryName, "com.epam.indigo.Properties.ResourcesWin2013", false); } break; case PlatformID.Unix: if (IndigoDllLoader.isMac()) { libraryName = "libindigo.dylib"; dll_loader.loadLibrary(lib_path, libraryName, "com.epam.indigo.Properties.ResourcesMac", false); } else { libraryName = "libindigo.so"; dll_loader.loadLibrary(lib_path, libraryName, "com.epam.indigo.Properties.ResourcesLinux", false); } break; default: throw new PlatformNotSupportedException(String.Format("Unsupported platform: {0}", Environment.OSVersion.Platform)); } // Save instance id to check if session was allocated for this instance _dll_loader_id = IndigoDllLoader.InstanceId; _dllpath = lib_path; _indigo_lib = dll_loader.getInterface(libraryName); _sid = _indigo_lib.indigoAllocSessionId(); _indigo_lib.indigoSetSessionId(_sid); } ~Indigo() { Dispose(); } public void Dispose() { if (dll_loader.isValid()) { if (_sid >= 0) { if (IndigoDllLoader.InstanceId == _dll_loader_id) _indigo_lib.indigoReleaseSessionId(_sid); _sid = -1; } } } public void setSessionID() { _indigo_lib.indigoSetSessionId(_sid); } public void dbgBreakpoint() { setSessionID(); _indigo_lib.indigoDbgBreakpoint(); } private long _sid = -1; private String _dllpath; private int _dll_loader_id; public IndigoLib _indigo_lib = null; } } Indigo-indigo-1.2.3/api/dotnet/IndigoDllLoader.cs000066400000000000000000000504201271037650300215730ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Resources; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; namespace com.epam.indigo { class LibraryLoader { class WindowsLoader { [DllImport("kernel32")] public static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll")] public static extern int FreeLibrary(IntPtr module); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] public static extern int GetLastError(); } class LinuxLoader { [DllImport("libdl.so.2")] public static extern IntPtr dlopen([MarshalAs(UnmanagedType.LPTStr)] string filename, int flags); [DllImport("libdl.so.2")] public static extern int dlclose(IntPtr handle); [DllImport("libdl.so.2")] public static extern IntPtr dlsym(IntPtr libraryPointer, string procedureName); [DllImport("libdl.so.2")] public static extern string dlerror(); } class MacLoader { [DllImport("libdl.dylib")] public static extern IntPtr dlopen(string filename, int flags); [DllImport("libdl.dylib")] public static extern int dlclose(IntPtr handle); [DllImport("libdl.dylib")] public static extern IntPtr dlsym(IntPtr libraryPointer, string procedureName); [DllImport("libdl.dylib")] public static extern string dlerror(); } public static IntPtr LoadLibrary(string filename) { switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: return WindowsLoader.LoadLibrary(filename); case PlatformID.Unix: if (IndigoDllLoader.isMac()) { return MacLoader.dlopen(filename.Replace("\\", "/"), 0x8 | 0x1); // RTLD_GLOBAL | RTLD_NOW } else { return LinuxLoader.dlopen(filename.Replace("\\", "/"), 0x00100 | 0x00002); // RTLD_GLOBAL | RTLD_NOW } } return IntPtr.Zero; } public static int FreeLibrary(IntPtr handle) { switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: return WindowsLoader.FreeLibrary(handle); case PlatformID.Unix: if (IndigoDllLoader.isMac()) { return MacLoader.dlclose(handle); } else { return LinuxLoader.dlclose(handle); } } return 0; } public static IntPtr GetProcAddress(IntPtr library, string procedureName) { switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: return WindowsLoader.GetProcAddress(library, procedureName); case PlatformID.Unix: if (IndigoDllLoader.isMac()) { return MacLoader.dlsym(library, procedureName); } else { return LinuxLoader.dlsym(library, procedureName); } } return IntPtr.Zero; } public static string GetLastError() { switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: return WindowsLoader.GetLastError().ToString(); case PlatformID.Unix: if (IndigoDllLoader.isMac()) { return MacLoader.dlerror(); } else { return LinuxLoader.dlerror(); } } return null; } } // Singleton DLL loader public class IndigoDllLoader { private static volatile IndigoDllLoader _instance; private static object _global_sync_root = new Object(); private static volatile int _instance_id = 0; public static IndigoDllLoader Instance { get { if (_instance == null) { lock (_global_sync_root) { if (_instance == null) _instance = new IndigoDllLoader(); } } return _instance; } } // Returns Id of the instance. When DllLoader is being finalized this value gets increased. public static int InstanceId { get { return _instance_id; } } class WrappedInterface { // Dictionary with delegates for calling unmanaged functions public Dictionary delegates = new Dictionary(); // Interface instance with wrappers for calling unmanaged functions public object instance = null; } class DllData { public IntPtr handle; public string file_name; public string lib_path; public Dictionary interfaces = new Dictionary(); } // Mapping from the DLL name to the handle. Dictionary _loaded_dlls = new Dictionary(); // DLL handles in the loading order List _dll_handles = new List(); // Local synchronization object Object _sync_object = new Object(); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct utsname { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string sysname; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string nodename; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string release; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string version; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string machine; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] public string extraJustInCase; } [DllImport("libc")] private static extern void uname(out utsname uname_struct); private static string detectUnixKernel() { utsname uts = new utsname(); uname(out uts); return uts.sysname.ToString(); } static public bool isMac() { return (detectUnixKernel() == "Darwin"); } public void loadLibrary(String path, String dll_name, string resource_name, bool make_unique_dll_name) { lock (_sync_object) { DllData data = null; if (_loaded_dlls.TryGetValue(dll_name, out data)) { // Library has already been loaded if (data.lib_path != path) throw new IndigoException( String.Format("Library {0} has already been loaded by different path {1}", dll_name, data.lib_path) ); return; } String subprefix = null; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: subprefix = (IntPtr.Size == 8) ? "Win/x64/" : "Win/x86/"; break; case PlatformID.Unix: if (isMac()) { subprefix = "Mac/10.6/"; } else { subprefix = (IntPtr.Size == 8) ? "Linux/x64/" : "Linux/x86/"; } break; default: throw new PlatformNotSupportedException(String.Format( "Unsupported platform: {0}", Environment.OSVersion.Platform ) ); } data = new DllData(); data.lib_path = path; data.file_name = _getPathToBinary(path, subprefix + dll_name, resource_name, Assembly.GetCallingAssembly(), make_unique_dll_name); data.file_name = data.file_name.Replace('/', '\\'); data.handle = LibraryLoader.LoadLibrary(data.file_name); if (data.handle == IntPtr.Zero) throw new Exception("Cannot load library " + dll_name + " from the temporary file " + data.file_name.Replace('\\', '/') + ": " + LibraryLoader.GetLastError() ); _loaded_dlls.Add(dll_name, data); _dll_handles.Add(data); } } ~IndigoDllLoader() { lock (_global_sync_root) { _instance = null; _instance_id++; // Unload all loaded libraries in the reverse order _dll_handles.Reverse(); foreach (DllData dll in _dll_handles) LibraryLoader.FreeLibrary(dll.handle); } } public bool isValid() { return (_instance != null); } string _getPathToBinary(String path, String filename, String resource_name, Assembly resource_assembly, bool make_unique_dll_name) { if (path == null) return _extractFromAssembly(filename, resource_name, resource_assembly, make_unique_dll_name); return Path.Combine(path, filename); } String _getTemporaryDirectory(Assembly resource_assembly) { String dir; dir = Path.Combine(Path.GetTempPath(), "GGA_indigo"); dir = Path.Combine(dir, resource_assembly.GetName().Name); dir = Path.Combine(dir, resource_assembly.GetName().Version.ToString()); return dir; } String _extractFromAssembly(String filename, String resource_name, Assembly resource_assembly, bool make_unique_dll_name) { ResourceManager manager = new ResourceManager(resource_name, resource_assembly); Object file_data = manager.GetObject(filename); if (file_data == null) throw new IndigoException("Internal error: there is no resource " + filename); String tmpdir_path = _getTemporaryDirectory(resource_assembly); String version = resource_assembly.GetName().Version.ToString(); // Make per-version-unique dependent dll name String path = Path.Combine(tmpdir_path, filename); String dir = Path.GetDirectoryName(path); String name = Path.GetFileName(path); String new_dll_name; if (make_unique_dll_name) new_dll_name = version + "_" + name; else new_dll_name = name; // This temporary file is used to avoid inter-process // race condition when concurrently stating many processes // on the same machine for the first time. String tmp_filename = Path.GetTempFileName(); String new_full_path = Path.Combine(dir, new_dll_name); FileInfo file = new FileInfo(new_full_path); file.Directory.Create(); // Check if file already exists if (!file.Exists || file.Length == 0) { File.WriteAllBytes(tmp_filename, (byte[]) file_data); // file is ready to be moved.. lets check again if (!file.Exists || file.Length == 0) { File.Move(tmp_filename, file.FullName); } else { File.Delete(tmp_filename); } } return file.FullName; } // Returns implementation of a given interface for wrapping function the specified DLL public IT getInterface(string dll_name) where IT : class { lock (_sync_object) { Type itype = typeof(IT); // Check if such interface was already loaded WrappedInterface interf = null; if (_loaded_dlls.ContainsKey(dll_name)) { if (!_loaded_dlls[dll_name].interfaces.TryGetValue(itype, out interf)) { interf = createInterface(dll_name); _loaded_dlls[dll_name].interfaces.Add(itype, interf); } } else { interf = createInterface(dll_name); _loaded_dlls[dll_name].interfaces.Add(itype, interf); } return (IT)interf.instance; } } string getDelegateField(MethodInfo m) { return m.Name + "_ptr"; } Type createDelegateType(string delegate_type_name, ModuleBuilder mb, Type ret_type, Type[] arg_types) { // Create delegate TypeBuilder delegate_type = mb.DefineType(delegate_type_name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(System.MulticastDelegate)); ConstructorBuilder constructorBuilder = delegate_type.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(System.IntPtr) }); constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); MethodBuilder methodBuilder = delegate_type.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, ret_type, arg_types); methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); // Add [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute for the delegate ConstructorInfo func_pointer_constructor = typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new Type[] { typeof(CallingConvention) }); CustomAttributeBuilder ca_builder = new CustomAttributeBuilder(func_pointer_constructor, new object[] { CallingConvention.Cdecl }); delegate_type.SetCustomAttribute(ca_builder); return delegate_type.CreateType(); } private class TypeListComparer : IEqualityComparer> { public bool Equals(List x, List y) { if (x.Count != y.Count) return false; for (int i = 0; i < x.Count; i++) if (x[i] != y[i]) return false; return true; } public int GetHashCode(List obj) { int hash = 0; foreach (Type t in obj) hash ^= t.GetHashCode(); return hash; } } // Creates implementation of a given interface for wrapping function the specified DLL WrappedInterface createInterface(string dll_name) where IT : class { WrappedInterface result = new WrappedInterface(); Type itype = typeof(IT); AppDomain cd = System.Threading.Thread.GetDomain(); AssemblyName an = new AssemblyName(); an.Name = itype.Name + "_" + dll_name.Replace('.', '_'); AssemblyBuilder ab = cd.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); ModuleBuilder mb = ab.DefineDynamicModule(an.Name, false); TypeBuilder tb = mb.DefineType(an.Name, TypeAttributes.Class | TypeAttributes.Public); tb.AddInterfaceImplementation(itype); IntPtr dll_handle = _loaded_dlls[dll_name].handle; Dictionary, Type> signature_to_name = new Dictionary, Type>(new TypeListComparer()); // Set delegate references foreach (MethodInfo m in itype.GetMethods()) { ParameterInfo[] parameters = m.GetParameters(); Type[] arg_types = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) arg_types[i] = parameters[i].ParameterType; Type delegate_ret_type = m.ReturnType; if (delegate_ret_type == typeof(String)) delegate_ret_type = typeof(sbyte*); List signature = new List(); signature.Add(delegate_ret_type); signature.AddRange(arg_types); Type call_delegate = null; if (!signature_to_name.TryGetValue(signature, out call_delegate)) { // Check if type was already created string delegate_type_name = String.Format("delegate_{0}", signature_to_name.Count); call_delegate = createDelegateType(delegate_type_name, mb, delegate_ret_type, arg_types); signature_to_name.Add(signature, call_delegate); } string delegate_field_name = m.Name + "_ptr"; FieldBuilder delegate_field = tb.DefineField(delegate_field_name, typeof(Delegate), FieldAttributes.Private); IntPtr proc = LibraryLoader.GetProcAddress(dll_handle, m.Name); if (proc == IntPtr.Zero) throw new IndigoException(String.Format("Cannot find procedure {0} in the library {1}", m.Name, dll_name)); Delegate proc_delegate = Marshal.GetDelegateForFunctionPointer(proc, call_delegate); result.delegates.Add(delegate_field_name, proc_delegate); MethodBuilder meth = tb.DefineMethod(m.Name, MethodAttributes.Public | MethodAttributes.Virtual, m.ReturnType, arg_types); ILGenerator il = meth.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, delegate_field); for (int i = 1; i < arg_types.Length + 1; i++) il.Emit(OpCodes.Ldarg, i); MethodInfo infoMethod = proc_delegate.GetType().GetMethod("Invoke", arg_types); il.EmitCall(OpCodes.Callvirt, infoMethod, null); // Automatically convert sbyte* to String if (m.ReturnType == typeof(String)) { Type str_type = typeof(String); ConstructorInfo ci = str_type.GetConstructor(new Type[] { typeof(sbyte*) }); il.Emit(OpCodes.Newobj, ci); } il.Emit(OpCodes.Ret); tb.DefineMethodOverride(meth, m); } // ab.Save(an.Name + ".dll"); Type impl_class = tb.CreateType(); IT impl = (IT)Activator.CreateInstance(impl_class); // Set references to the delegates foreach (string field_name in result.delegates.Keys) { impl_class.GetField(field_name, BindingFlags.Instance | BindingFlags.NonPublic) .SetValue(impl, result.delegates[field_name]); } result.instance = impl; return result; } } } Indigo-indigo-1.2.3/api/dotnet/IndigoException.cs000066400000000000000000000011361271037650300216670ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.Serialization; namespace com.epam.indigo { public class IndigoException : Exception { public IndigoException () : base() { } public IndigoException (string message) : base(message) { } protected IndigoException (SerializationInfo info, StreamingContext context) : base(info, context) { } public IndigoException (string message, Exception innerException) : base(message, innerException) { } } } Indigo-indigo-1.2.3/api/dotnet/IndigoLib.cs000066400000000000000000000436321271037650300204460ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; namespace com.epam.indigo { public unsafe interface IndigoLib { sbyte* indigoVersion(); long indigoAllocSessionId(); void indigoSetSessionId(long id); void indigoReleaseSessionId(long id); sbyte* indigoGetLastError(); void indigoSetErrorMessage(String message); int indigoFree(int id); int indigoClone(int id); int indigoCountReferences(); int indigoSetOption(string name, string value); int indigoSetOptionInt(string name, int value); int indigoSetOptionBool(string name, int value); int indigoSetOptionFloat(string name, float value); int indigoSetOptionColor(string name, float r, float g, float b); int indigoSetOptionXY(string name, int x, int y); int indigoReadFile(string filename); int indigoReadString(string str); int indigoLoadString(string str); int indigoReadBuffer(byte[] buffer, int size); int indigoLoadBuffer(byte[] buffer, int size); int indigoWriteFile(string filename); int indigoWriteBuffer(); int indigoClose(int item); int indigoCreateMolecule(); int indigoCreateQueryMolecule(); int indigoLoadMoleculeFromString(string str); int indigoLoadMoleculeFromFile(string path); int indigoLoadMoleculeFromBuffer(byte[] buf, int bufsize); int indigoLoadQueryMoleculeFromString(string str); int indigoLoadQueryMoleculeFromFile(string path); int indigoLoadQueryMoleculeFromBuffer(byte[] buf, int bufsize); int indigoLoadSmartsFromString(string str); int indigoLoadSmartsFromFile(string filename); int indigoLoadSmartsFromBuffer(byte[] buffer, int size); int indigoSaveMolfile(int molecule, int output); int indigoSaveMolfileToFile(int molecule, string filename); sbyte* indigoMolfile(int molecule); int indigoSaveCml(int molecule, int output); int indigoSaveCmlToFile(int molecule, string filename); sbyte* indigoCml(int molecule); int indigoSaveMDLCT(int item, int output); int indigoCreateSaver(int output, string format); int indigoCreateFileSaver(string filename, string format); int indigoAppend(int saver, int obj); int indigoLoadReactionFromString(string str); int indigoLoadReactionFromFile(string path); int indigoLoadReactionFromBuffer(byte[] buf, int bufsize); int indigoLoadQueryReactionFromString(string str); int indigoLoadQueryReactionFromFile(string path); int indigoLoadQueryReactionFromBuffer(byte[] buf, int bufsize); int indigoLoadReactionSmartsFromString(string str); int indigoLoadReactionSmartsFromFile(string path); int indigoLoadReactionSmartsFromBuffer(byte[] buf, int bufsize); int indigoCreateReaction(); int indigoCreateQueryReaction(); int indigoAddReactant(int reaction, int molecule); int indigoAddProduct(int reaction, int molecule); int indigoAddCatalyst(int reaction, int molecule); int indigoCountReactants(int reaction); int indigoCountProducts(int reaction); int indigoCountCatalysts(int reaction); int indigoCountMolecules(int reaction); int indigoIterateReactants(int reaction); int indigoIterateProducts(int reaction); int indigoIterateCatalysts(int reaction); int indigoIterateMolecules(int reader); int indigoSaveRxnfile(int reaction, int output); int indigoSaveRxnfileToFile(int reaction, string filename); sbyte* indigoRxnfile(int reaction); int indigoAutomap(int reaction, string filename); int indigoGetAtomMappingNumber(int reaction, int reaction_atom); int indigoSetAtomMappingNumber(int reaction, int reaction_atom, int number); int indigoClearAAM(int reaction); int indigoCorrectReactingCenters(int reaction); int indigoGetReactingCenter(int reaction, int reaction_bond, int* rc); int indigoSetReactingCenter(int reaction, int reaction_bond, int rc); int indigoOptimize(int query, string options); int indigoNormalize(int structure, string options); int indigoStandardize(int item); int indigoIonize(int item, float pH, float pH_toll); float* indigoGetAcidPkaValue(int item, int atom, int level, int min_level); float* indigoGetBasicPkaValue(int item, int atom, int level, int min_level); int indigoBuildPkaModel(int level, float theshold, String filename); int indigoIterateAtoms(int molecule); int indigoIteratePseudoatoms(int molecule); int indigoIterateRSites(int molecule); int indigoIterateStereocenters(int molecule); int indigoIterateAlleneCenters(int molecule); int indigoIterateRGroups(int molecule); int indigoIterateRGroupFragments(int rgroup); int indigoCountAttachmentPoints(int rgroup); int indigoIsPseudoatom(int atom); int indigoIsRSite(int atom); int indigoStereocenterType(int atom); int* indigoStereocenterPyramid(int atom); int indigoSingleAllowedRGroup(int rsite); int indigoChangeStereocenterType(int atom, int type); int indigoAddStereocenter(int atom, int type, int v1, int v2, int v3, int v4); int indigoStereocenterGroup(int atom); int indigoSetStereocenterGroup(int atom, int group); sbyte* indigoSymbol(int atom); int indigoDegree(int atom); int indigoGetCharge(int atom, int* charge); int indigoGetRadical(int atom, int* radical); int indigoGetExplicitValence(int atom, int* valence); int indigoGetRadicalElectrons(int atom, int* electrons); int indigoAtomicNumber(int atom); int indigoIsotope(int atom); int indigoValence(int atom); int indigoCountHydrogens(int atom, int* hydro); int indigoCountImplicitHydrogens(int item); int indigoCountSuperatoms(int item); int indigoCountDataSGroups(int item); int indigoCountGenericSGroups(int item); int indigoCountRepeatingUnits(int item); int indigoCountMultipleGroups(int item); int indigoIterateSuperatoms(int item); int indigoIterateDataSGroups(int item); int indigoIterateGenericSGroups(int item); int indigoIterateRepeatingUnits(int item); int indigoIterateMultipleGroups(int item); int indigoGetDataSGroup(int mol, int idx); int indigoGetSuperatom(int mol, int idx); int indigoGetGenericSGroup (int molecule, int index); int indigoGetMultipleGroup (int molecule, int index); int indigoGetRepeatingUnit (int molecule, int index); sbyte* indigoDescription(int item); sbyte* indigoData(int item); int indigoAddDataSGroup(int molecule, int natoms, int[] atoms, int nbonds, int[] bonds, string description, string data); int indigoSetDataSGroupXY(int sgroup, float x, float y, string options); int indigoAddSuperatom(int molecule, int natoms, int[] atoms, string name); int indigoCreateSGroup(string type, int mapping, string name); int indigoSetSGroupClass(int sgroup, string sgclass); int indigoSetSGroupName(int sgroup, string sgname); sbyte* indigoGetSGroupClass(int sgroup); sbyte* indigoGetSGroupName(int sgroup); int indigoGetSGroupNumCrossBonds(int sgroup); int indigoAddSGroupAttachmentPoint(int sgroup, int aidx, int lvidx, string apid); int indigoDeleteSGroupAttachmentPoint(int sgroup, int apidx); int indigoGetSGroupDisplayOption(int sgroup); int indigoSetSGroupDisplayOption(int sgroup, int option); int indigoGetSGroupMultiplier(int sgroup); int indigoSetSGroupMultiplier(int sgroup, int mult); int indigoSetSGroupData (int sgroup, string data); int indigoSetSGroupCoords (int sgroup, float x, float y); int indigoSetSGroupDescription (int sgroup, string description); int indigoSetSGroupFieldName (int sgroup, string name); int indigoSetSGroupQueryCode (int sgroup, string querycode); int indigoSetSGroupQueryOper (int sgroup, string queryoper); int indigoSetSGroupDisplay (int sgroup, string option); int indigoSetSGroupLocation (int sgroup, string option); int indigoSetSGroupTag (int sgroup, string tag); int indigoSetSGroupTagAlign (int sgroup, int tag_align); int indigoSetSGroupDataType (int sgroup, string type); int indigoSetSGroupXCoord (int sgroup, float x); int indigoSetSGroupYCoord (int sgroup, float y); int indigoSetSGroupBrackets(int sgroup, int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); int indigoFindSGroups(int molecule, string property, string value); int indigoGetSGroupType(int sgroup); int indigoGetSGroupIndex(int sgroup); int indigoTransformSCSRtoCTAB(int molecule); int indigoTransformCTABtoSCSR(int molecule, int templates); float* indigoXYZ(int atom); int indigoSetXYZ(int atom, float x, float y, float z); int indigoResetCharge(int atom); int indigoResetExplicitValence(int atom); int indigoResetRadical(int atom); int indigoResetIsotope(int atom); int indigoSetAttachmentPoint(int atom, int order); int indigoIterateAttachmentPoints(int item, int order); int indigoClearAttachmentPoints(int item); int indigoRemoveConstraints(int item, string type); int indigoAddConstraint(int item, string type, string value); int indigoAddConstraintNot(int item, string type, string value); int indigoAddConstraintOr(int item, string type, string value); int indigoInvertStereo(int atom); int indigoResetStereo(int atom); int indigoCountAtoms(int molecule); int indigoCountBonds(int molecule); int indigoCountPseudoatoms(int molecule); int indigoCountRSites(int molecule); int indigoIterateBonds(int molecule); int indigoBondOrder(int molecule); int indigoBondStereo(int molecule); int indigoTopology(int bond); int indigoIterateNeighbors(int atom); int indigoBond(int nei); int indigoGetAtom(int molecule, int idx); int indigoGetBond(int molecule, int idx); int indigoGetMolecule(int reaction, int idx); int indigoSource(int bond); int indigoDestination(int bond); int indigoClearCisTrans(int item); int indigoClearStereocenters(int item); int indigoClearAlleneCenters(int item); int indigoCountStereocenters(int item); int indigoCountAlleneCenters(int item); int indigoResetSymmetricCisTrans(int handle); int indigoResetSymmetricStereocenters(int handle); int indigoMarkEitherCisTrans(int handle); int indigoMarkStereobonds(int handle); int indigoValidateChirality(int handle); int indigoAddAtom(int molecule, string symbol); int indigoResetAtom(int atom, string symbol); int indigoAddRSite(int molecule, string name); int indigoSetRSite(int atom, string name); int indigoSetCharge(int atom, int charge); int indigoSetExplicitValence(int atom, int valence); int indigoSetIsotope(int atom, int isotope); int indigoSetImplicitHCount(int atom, int implh); int indigoSetRadical(int atom, int radical); int indigoAddBond(int source, int destination, int order); int indigoSetBondOrder(int bond, int order); int indigoMerge(int where_to, int what); int indigoHighlight(int item); int indigoUnhighlight(int item); int indigoIsHighlighted(int item); int indigoCountComponents(int molecule); int indigoComponentIndex(int atom); int indigoIterateComponents(int molecule); int indigoComponent(int molecule, int index); int indigoCountSSSR(int molecule); int indigoIterateSSSR(int molecule); int indigoIterateSubtrees(int molecule, int min_atoms, int max_atoms); int indigoIterateRings(int molecule, int min_atoms, int max_atoms); int indigoIterateEdgeSubmolecules(int molecule, int min_bonds, int max_bonds); int indigoCountHeavyAtoms(int molecule); int indigoGrossFormula(int molecule); float indigoMolecularWeight(int molecule); float indigoMostAbundantMass(int molecule); float indigoMonoisotopicMass(int molecule); sbyte* indigoCanonicalSmiles(int molecule); sbyte* indigoLayeredCode(int molecule); int indigoHasCoord(int molecule); int indigoHasZCoord(int molecule); int indigoIsChiral(int molecule); int indigoIsPossibleFischerProjection(int molecule, string options); int indigoCreateSubmolecule(int molecule, int nvertices, int[] vertices); int indigoCreateEdgeSubmolecule(int molecule, int nvertices, int[] vertices, int nedges, int[] edges); int indigoRemoveAtoms(int molecule, int nvertices, int[] vertices); int indigoRemoveBonds(int molecule, int nbonds, int[] bonds); int indigoGetSubmolecule(int molecule, int nvertices, int[] vertices); float indigoAlignAtoms(int molecule, int natoms, int[] atom_ids, float[] desired_xyz); int indigoAromatize(int item); int indigoDearomatize(int item); int indigoFoldHydrogens(int item); int indigoUnfoldHydrogens(int item); int indigoLayout(int item); sbyte* indigoSmiles(int item); int indigoExactMatch(int item1, int item2, string flags); int indigoSetTautomerRule(int id, string beg, string end); int indigoRemoveTautomerRule(int id); int indigoClearTautomerRules(); sbyte* indigoName(int item); int indigoSetName(int item, string name); int indigoSerialize(int handle, byte** buf, int* size); int indigoUnserialize(byte[] buf, int size); int indigoHasProperty(int handle, string field); sbyte* indigoGetProperty(int handle, string field); int indigoSetProperty(int handle, string field, string value); int indigoRemoveProperty(int item, string prop); int indigoIterateProperties(int handle); int indigoClearProperties(int handle); sbyte* indigoCheckBadValence(int handle); sbyte* indigoCheckAmbiguousH(int handle); int indigoFingerprint(int item, string type); int indigoCountBits(int fingerprint); int indigoCommonBits(int fingerprint1, int fingerprint2); float indigoSimilarity(int molecule1, int molecule2, string metrics); int indigoIterateSDF(int reader); int indigoIterateRDF(int reader); int indigoIterateSmiles(int reader); int indigoIterateCML(int reader); int indigoIterateCDX(int reader); int indigoIterateSDFile(string filename); int indigoIterateRDFile(string filename); int indigoIterateSmilesFile(string filename); int indigoIterateCMLFile(string filename); int indigoIterateCDXFile(string filename); sbyte* indigoRawData(int item); int indigoTell(int item); int indigoSdfAppend(int output, int item); int indigoSmilesAppend(int output, int item); int indigoRdfHeader(int output); int indigoRdfAppend(int output, int item); int indigoCmlHeader(int output); int indigoCmlAppend(int output, int item); int indigoCmlFooter(int output); int indigoCreateArray(); int indigoArrayAdd(int arr, int item); int indigoAt(int item, int index); int indigoCount(int arr); int indigoClear(int arr); int indigoIterateArray(int arr); int indigoSubstructureMatcher(int target, string mode); int indigoIgnoreAtom(int matcher, int atom); int indigoUnignoreAtom(int matcher, int atom); int indigoUnignoreAllAtoms(int matcher); int indigoMatch(int matcher, int query); int indigoCountMatches(int matcher, int query); int indigoCountMatchesWithLimit(int matcher, int query, int embeddings_limit); int indigoIterateMatches(int matcher, int query); int indigoHighlightedTarget(int match); int indigoMapAtom(int match, int query_atom); int indigoMapBond(int match, int query_bond); int indigoMapMolecule(int match, int query_reaction_molecule); int indigoExtractCommonScaffold(int structures, string options); int indigoAllScaffolds(int extracted); int indigoDecomposeMolecules(int scaffold, int structures); int indigoDecomposedMoleculeScaffold(int decomp); int indigoIterateDecomposedMolecules(int decomp); int indigoDecomposedMoleculeHighlighted(int decomp); int indigoDecomposedMoleculeSubstituents(int decomp); int indigoDecomposedMoleculeWithRGroups(int decomp); int indigoCreateDecomposer(int scaffold); int indigoDecomposeMolecule(int decomp, int mol); int indigoIterateDecompositions(int deco_item); int indigoAddDecomposition(int decomp, int q_match); int indigoNext(int iter); int indigoHasNext(int iter); int indigoIndex(int item); int indigoRemove(int item); sbyte* indigoToString(int handle); int indigoToBuffer(int handle, byte** buf, int* size); int* indigoSymmetryClasses(int molecule, int* size); int indigoReactionProductEnumerate(int reaction, int monomers); int indigoTransform(int reaction, int monomers); int indigoExpandAbbreviations (int structure); int indigoIterateTautomers(int structure, string parameters); sbyte* indigoDbgInternalType(int item); sbyte* indigoDbgProfiling (int whole_sessoin); int indigoDbgResetProfiling (int whole_sessoin); int indigoDbgBreakpoint (); } } Indigo-indigo-1.2.3/api/dotnet/IndigoObject.cs000066400000000000000000001652611271037650300211510ustar00rootroot00000000000000using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.IO; namespace com.epam.indigo { public unsafe class IndigoObject : IEnumerable, IDisposable { public int self; private Indigo dispatcher; private object parent; // to prevent GC killing the parent object private IndigoLib _indigo_lib; private IndigoDllLoader dll_loader; public IndigoObject(Indigo dispatcher, int id) : this(dispatcher, id, null) { dll_loader = IndigoDllLoader.Instance; } public IndigoObject(Indigo dispatcher, int id, object parent) { this.dispatcher = dispatcher; this.self = id; this.parent = parent; _indigo_lib = dispatcher._indigo_lib; dll_loader = IndigoDllLoader.Instance; } ~IndigoObject() { Dispose(); } public void Dispose() { if (dispatcher == null) { // This happens exclusively in 32-bit .NET environment // after an IndigoObject constructor throws an exception. // In fact, the object is not created in this case, // but for some reason the .NET VM disposes it, despite it // has not been initialized. return; } if (self >= 0) { // Check that the session is still alive // (.NET has no problem disposing referenced // objects before the objects that reference to them) if (dll_loader.isValid()) { if (dispatcher.getSID() >= 0) { dispatcher.setSessionID(); dispatcher.free(self); self = -1; } } } } public void dispose() { Dispose(); } public Indigo indigo () { return dispatcher; } public IndigoObject clone() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoClone(self))); } public void close() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClose(self)); } public String molfile() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoMolfile(self))); } public void saveMolfile(String filename) { dispatcher.setSessionID(); int s = dispatcher.checkResult(_indigo_lib.indigoWriteFile(filename)); dispatcher.checkResult(_indigo_lib.indigoSaveMolfile(self, s)); dispatcher.checkResult(_indigo_lib.indigoFree(s)); } public String cml() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoCml(self))); } public void saveCml(String filename) { dispatcher.setSessionID(); int s = dispatcher.checkResult(_indigo_lib.indigoWriteFile(filename)); dispatcher.checkResult(_indigo_lib.indigoSaveCml(self, s)); dispatcher.checkResult(_indigo_lib.indigoFree(s)); } public byte[] mdlct() { dispatcher.setSessionID(); IndigoObject buf = dispatcher.writeBuffer(); dispatcher.checkResult(_indigo_lib.indigoSaveMDLCT(self, buf.self)); return buf.toBuffer(); } public void addReactant(IndigoObject molecule) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddReactant(self, molecule.self)); } public void addProduct(IndigoObject molecule) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddProduct(self, molecule.self)); } public void addCatalyst(IndigoObject molecule) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddCatalyst(self, molecule.self)); } public int countReactants() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountReactants(self)); } public int countProducts() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountProducts(self)); } public int countCatalysts() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountCatalysts(self)); } public int countMolecules() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountMolecules(self)); } public System.Collections.IEnumerable iterateReactants() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateReactants(self)), this); } public System.Collections.IEnumerable iterateProducts() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateProducts(self)), this); } public System.Collections.IEnumerable iterateCatalysts() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateCatalysts(self)), this); } public System.Collections.IEnumerable iterateMolecules() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateMolecules(self)), this); } public String rxnfile() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoRxnfile(self))); } public void saveRxnfile(String filename) { dispatcher.setSessionID(); int s = dispatcher.checkResult(_indigo_lib.indigoWriteFile(filename)); dispatcher.checkResult(_indigo_lib.indigoSaveRxnfile(self, s)); dispatcher.checkResult(_indigo_lib.indigoFree(s)); } public void automap() { automap(""); } public void automap(String mode) { if (mode == null) mode = ""; dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAutomap(self, mode)); } public int atomMappingNumber(IndigoObject reaction_atom) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetAtomMappingNumber(self, reaction_atom.self)); } public void setAtomMappingNumber(IndigoObject reaction_atom, int number) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetAtomMappingNumber(self, reaction_atom.self, number)); } public ReactingCenter reactingCenter(IndigoObject bond) { int c; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoGetReactingCenter(self, bond.self, &c)) == 1) return (ReactingCenter)c; throw new IndigoException("reactingCenter(): unexpected result"); } public void setReactingCenter(IndigoObject bond, ReactingCenter type) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetReactingCenter(self, bond.self, (int)type)); } public void clearAAM() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearAAM(self)); } public void correctReactingCenters() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoCorrectReactingCenters(self)); } public System.Collections.IEnumerable iterateAtoms() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateAtoms(self)), this); } public System.Collections.IEnumerable iteratePseudoatoms() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIteratePseudoatoms(self)), this); } public System.Collections.IEnumerable iterateRSites() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateRSites(self)), this); } public System.Collections.IEnumerable iterateStereocenters() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateStereocenters(self)), this); } public System.Collections.IEnumerable iterateAlleneCenters() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateAlleneCenters(self)), this); } public System.Collections.IEnumerable iterateRGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateRGroups(self)), this); } public System.Collections.IEnumerable iterateRGroupFragments() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateRGroupFragments(self)), this); } public int countAttachmentPoints() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountAttachmentPoints(self)); } public bool isPseudoatom() { dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoIsPseudoatom(self)) == 1) return true; return false; } public bool isRSite() { dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoIsRSite(self)) == 1) return true; return false; } public int stereocenterType() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoStereocenterType(self)); } public int stereocenterGroup() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoStereocenterGroup(self)); } public int[] stereocenterPyramid() { dispatcher.setSessionID(); int* pyramid_ptr = dispatcher.checkResult(_indigo_lib.indigoStereocenterPyramid(self)); int[] res = new int[4]; for (int i = 0; i < 4; ++i) res[i] = pyramid_ptr[i]; return res; } public void changeStereocenterType(int type) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoChangeStereocenterType(self, type)); } public void setStereocenterGroup(int group) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetStereocenterGroup(self, group)); } public int singleAllowedRGroup() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSingleAllowedRGroup(self)); } public string symbol() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoSymbol(self))); } public int degree() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoDegree(self)); } public int? charge() { int c; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoGetCharge(self, &c)) == 1) return c; return null; } public int? explicitValence() { int c; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoGetExplicitValence(self, &c)) == 1) return c; return null; } public int? radicalElectrons() { int c; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoGetRadicalElectrons(self, &c)) == 1) return c; return null; } public int? radical() { int c; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoGetRadical(self, &c)) == 1) return c; return null; } public int atomicNumber() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoAtomicNumber(self)); } public int isotope() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoIsotope(self)); } public int valence() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoValence(self)); } public int? countHydrogens() { int h; dispatcher.setSessionID(); if (dispatcher.checkResult(_indigo_lib.indigoCountHydrogens(self, &h)) == 1) return h; return null; } public int countImplicitHydrogens() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountImplicitHydrogens(self)); } public int countSuperatoms() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountSuperatoms(self)); } public int countDataSGroups() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountDataSGroups(self)); } public int countGenericSGroups() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountGenericSGroups(self)); } public int countRepeatingUnits() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountRepeatingUnits(self)); } public int countMultipleGroups() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountMultipleGroups(self)); } public IndigoObject iterateSuperatoms() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateSuperatoms(self)), this); } public IndigoObject iterateAttachmentPoints(int order) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateAttachmentPoints(self, order)), this); } public IndigoObject iterateDataSGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateDataSGroups(self)), this); } public IndigoObject iterateGenericSGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateGenericSGroups(self)), this); } public IndigoObject iterateRepeatingUnits() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateRepeatingUnits(self)), this); } public IndigoObject iterateMultipleGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateMultipleGroups(self)), this); } public IndigoObject getDataSGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetDataSGroup(self, index)), this); } public IndigoObject getSuperatom(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetSuperatom(self, index)), this); } public IndigoObject getGenericSGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetGenericSGroup(self, index)), this); } public IndigoObject getMultipleGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetMultipleGroup(self, index)), this); } public IndigoObject getRepeatingUnit(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetRepeatingUnit(self, index)), this); } public string description() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoDescription(self))); } public string data() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoData(self))); } public IndigoObject addDataSGroup(int[] atoms, int[] bonds, String description, String data) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAddDataSGroup(self, atoms.Length, atoms, bonds.Length, bonds, description, data))); } public IndigoObject addDataSGroup(ICollection atoms, ICollection bonds, String description, String data) { return addDataSGroup(Indigo.toIntArray(atoms), Indigo.toIntArray(bonds), description, data); } public IndigoObject addSuperatom(int[] atoms, String name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAddSuperatom(self, atoms.Length, atoms, name))); } public IndigoObject addSuperatom(ICollection atoms, String name) { return addSuperatom(Indigo.toIntArray(atoms), name); } public IndigoObject createSGroup(String type, IndigoObject mapping, String name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoCreateSGroup(type, mapping.self, name))); } public void setSGroupClass(String sgclass) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetSGroupClass(self, sgclass)); } public void setSGroupName(String sgname) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetSGroupName(self, sgname)); } public string getSGroupClass() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoGetSGroupClass(self))); } public string getSGroupName() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoGetSGroupName(self))); } public int getSGroupNumCrossBonds() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetSGroupNumCrossBonds(self)); } public int addSGroupAttachmentPoint(int aidx, int lvidx, String apid) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoAddSGroupAttachmentPoint(self, aidx, lvidx, apid)); } public int deleteSGroupAttachmentPoint(int apidx) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoDeleteSGroupAttachmentPoint(self, apidx)); } public int getSGroupDisplayOption() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetSGroupDisplayOption(self)); } public int setSGroupDisplayOption(int option) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupDisplayOption(self, option)); } public int getSGroupMultiplier() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetSGroupMultiplier(self)); } public int setSGroupMultiplier(int mult) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupMultiplier(self, mult)); } public int setSGroupData (String data) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupData(self, data)); } public int setSGroupCoords (float x, float y) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupCoords(self, x, y)); } public int setSGroupDescription (String description) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupDescription(self, description)); } public int setSGroupFieldName (String name) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupFieldName(self, name)); } public int setSGroupQueryCode (String querycode) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupQueryCode(self, querycode)); } public int setSGroupQueryOper (String queryoper) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupQueryOper(self, queryoper)); } public int setSGroupDisplay (String option) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupDisplay(self, option)); } public int setSGroupLocation (String option) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupLocation(self, option)); } public int setSGroupTag (String tag) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupTag(self, tag)); } public int setSGroupTagAlign (int tag_align) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupTagAlign(self, tag_align)); } public int setSGroupDataType (String data_type) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupDataType(self, data_type)); } public int setSGroupXCoord (float x) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupXCoord(self, x)); } public int setSGroupYCoord (float y) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupYCoord(self, y)); } public int setSGroupBrackets(int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoSetSGroupBrackets(self, brk_style, x1, y1, x2, y2, x3, y3, x4, y4)); } public IndigoObject findSGroups(String property, String value) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoFindSGroups(self, property, value)), this); } public int getSGroupType() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetSGroupType(self)); } public int getSGroupIndex() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoGetSGroupIndex(self)); } public int transformSCSRtoCTAB() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoTransformSCSRtoCTAB(self)); } public int transformCTABtoSCSR(IndigoObject templates) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoTransformCTABtoSCSR(self, templates.self)); } public void addStereocenter(int type, int v1, int v2, int v3) { addStereocenter(type, v1, v2, v3, -1); } public void addStereocenter(int type, int v1, int v2, int v3, int v4) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddStereocenter(self, type, v1, v2, v3, v4)); } public void setDataSGroupXY(float x, float y) { setDataSGroupXY(x, y, ""); } public void setDataSGroupXY(float x, float y, String options) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetDataSGroupXY(self, x, y, options)); } public float[] xyz() { dispatcher.setSessionID(); float* ptr = dispatcher.checkResult(_indigo_lib.indigoXYZ(self)); float[] res = new float[3]; res[0] = ptr[0]; res[1] = ptr[1]; res[2] = ptr[2]; return res; } public void setXYZ(float x, float y, float z) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetXYZ(self, x, y, z)); } public void resetCharge() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoResetCharge(self)); } public void resetExplicitValence() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoResetExplicitValence(self)); } public void resetRadical() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoResetRadical(self)); } public void resetIsotope() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoResetIsotope(self)); } public void setAttachmentPoint(int order) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetAttachmentPoint(self, order)); } public void clearAttachmentPoints() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearAttachmentPoints(self)); } public void removeConstraints(string type) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRemoveConstraints(self, type)); } public void addConstraint(string type, string value) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddConstraint(self, type, value)); } public void addConstraintNot(string type, string value) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddConstraintNot(self, type, value)); } public void addConstraintOr(string type, string value) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddConstraintOr(self, type, value)); } public void invertStereo() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoInvertStereo(self)); } public void resetStereo() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoResetStereo(self)); } public int countAtoms() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountAtoms(self)); } public int countBonds() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountBonds(self)); } public int countPseudoatoms() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountPseudoatoms(self)); } public int countRSites() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountRSites(self)); } public System.Collections.IEnumerable iterateBonds() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateBonds(self)), this); } public int bondOrder() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoBondOrder(self)); } public int bondStereo() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoBondStereo(self)); } public int topology() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoTopology(self)); } public System.Collections.IEnumerable iterateNeighbors() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateNeighbors(self)), this); } public IndigoObject bond() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoBond(self))); } public IndigoObject getAtom(int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetAtom(self, idx)), this); } public IndigoObject getBond(int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetBond(self, idx)), this); } public IndigoObject getMolecule(int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetMolecule(self, idx)), this); } public IndigoObject source() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoSource(self))); } public IndigoObject destination() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoDestination(self))); } public void clearCisTrans() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearCisTrans(self)); } public void clearStereocenters() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearStereocenters(self)); } public void clearAlleneCenters() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearAlleneCenters(self)); } public int countStereocenters() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountStereocenters(self)); } public int countAlleneCenters() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountAlleneCenters(self)); } public int resetSymmetricCisTrans() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoResetSymmetricCisTrans(self)); } public int resetSymmetricStereocenters() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoResetSymmetricStereocenters(self)); } public int markEitherCisTrans() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoMarkEitherCisTrans(self)); } public int markStereobonds() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoMarkStereobonds(self)); } public int validateChirality() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoValidateChirality(self)); } public IndigoObject addAtom(string symbol) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAddAtom(self, symbol)), this); } public IndigoObject resetAtom(string symbol) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoResetAtom(self, symbol)), this); } public IndigoObject addRSite(string name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAddRSite(self, name)), this); } public IndigoObject setRSite(string name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoSetRSite(self, name)), this); } public void setCharge(int charge) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetCharge(self, charge)); } public void setRadical(int radical) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetRadical(self, radical)); } public void setExplicitValence(int valence) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetExplicitValence(self, valence)); } public void setIsotope(int isotope) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetIsotope(self, isotope)); } public void setImplicitHCount(int implh) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetImplicitHCount(self, implh)); } public IndigoObject addBond(IndigoObject dest, int order) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAddBond(self, dest.self, order)), this); } public void setBondOrder(int order) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetBondOrder(self, order)); } public IndigoObject merge(IndigoObject what) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoMerge(self, what.self)), this); } public void highlight() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoHighlight(self)); } public void unhighlight() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoUnhighlight(self)); } public bool isHighlighted() { dispatcher.setSessionID(); return (dispatcher.checkResult(_indigo_lib.indigoIsHighlighted(self)) == 1); } public int countComponents() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountComponents(self)); } public int componentIndex() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoComponentIndex(self)); } public void remove() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRemove(self)); } public IndigoObject iterateComponents() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateComponents(self)), this); } public IndigoObject component(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoComponent(self, index)), this); } public int countSSSR() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountSSSR(self)); } public IndigoObject iterateSSSR() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateSSSR(self)), this); } public IndigoObject iterateSubtrees(int min_vertices, int max_vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateSubtrees(self, min_vertices, max_vertices)), this); } public IndigoObject iterateRings(int min_vertices, int max_vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateRings(self, min_vertices, max_vertices)), this); } public IndigoObject iterateEdgeSubmolecules(int min_edges, int max_edges) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateEdgeSubmolecules(self, min_edges, max_edges)), this); } public int countHeavyAtoms() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountHeavyAtoms(self)); } public String grossFormula() { dispatcher.setSessionID(); int gf = -1; try { gf = dispatcher.checkResult(_indigo_lib.indigoGrossFormula(self)); String result = new String(dispatcher.checkResult(_indigo_lib.indigoToString(gf))); return result; } finally { dispatcher.checkResult(_indigo_lib.indigoFree(gf)); } } public float molecularWeight() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoMolecularWeight(self)); } public float mostAbundantMass() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoMostAbundantMass(self)); } public float monoisotopicMass() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoMonoisotopicMass(self)); } public string canonicalSmiles() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoCanonicalSmiles(self))); } public int[] symmetryClasses() { dispatcher.setSessionID(); int count; int* classes = dispatcher.checkResult(_indigo_lib.indigoSymmetryClasses(self, &count)); int[] res = new int[count]; for (int i = 0; i < count; ++i) res[i] = classes[i]; return res; } public string layeredCode() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoLayeredCode(self))); } public bool hasCoord() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoHasCoord(self)) == 1; } public bool hasZCoord() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoHasZCoord(self)) == 1; } public bool isChiral() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoIsChiral(self)) == 1; } public bool isPossibleFischerProjection(string options) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoIsPossibleFischerProjection(self, options)) == 1; } public IndigoObject createSubmolecule(int[] vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoCreateSubmolecule(self, vertices.Length, vertices))); } public IndigoObject createSubmolecule(ICollection vertices) { return createSubmolecule(Indigo.toIntArray(vertices)); } public IndigoObject getSubmolecule(int[] vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoGetSubmolecule(self, vertices.Length, vertices))); } public IndigoObject getSubmolecule(ICollection vertices) { return getSubmolecule(Indigo.toIntArray(vertices)); } public IndigoObject createEdgeSubmolecule(int[] vertices, int[] edges) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoCreateEdgeSubmolecule(self, vertices.Length, vertices, edges.Length, edges))); } public IndigoObject createEdgeSubmolecule(ICollection vertices, ICollection edges) { return createEdgeSubmolecule(vertices, edges); } public void removeAtoms(int[] vertices) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRemoveAtoms(self, vertices.Length, vertices)); } public void removeAtoms(ICollection vertices) { removeAtoms(Indigo.toIntArray(vertices)); } public void removeBonds(int[] bonds) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRemoveBonds(self, bonds.Length, bonds)); } public void removeBonds(ICollection bonds) { removeBonds(Indigo.toIntArray(bonds)); } public float alignAtoms(int[] atom_ids, float[] desired_xyz) { dispatcher.setSessionID(); if (atom_ids.Length * 3 != desired_xyz.Length) throw new IndigoException("alignAtoms(): desired_xyz[] must be exactly 3 times bigger than atom_ids[]"); return dispatcher.checkResult(_indigo_lib.indigoAlignAtoms(self, atom_ids.Length, atom_ids, desired_xyz)); } public float alignAtoms(ICollection atom_ids, ICollection desired_xyz) { return alignAtoms(Indigo.toIntArray(atom_ids), Indigo.toFloatArray(desired_xyz)); } public void aromatize() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAromatize(self)); } public void dearomatize() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoDearomatize(self)); } public void foldHydrogens() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoFoldHydrogens(self)); } public void unfoldHydrogens() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoUnfoldHydrogens(self)); } public void layout() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoLayout(self)); } public string smiles() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoSmiles(self))); } public String name() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoName(self))); } public void setName(string name) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetName(self, name)); } public byte[] serialize() { dispatcher.setSessionID(); byte* buf; int bufsize; dispatcher.checkResult(_indigo_lib.indigoSerialize(self, &buf, &bufsize)); byte[] res = new byte[bufsize]; for (int i = 0; i < bufsize; ++i) res[i] = buf[i]; return res; } public bool hasProperty(string name) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoHasProperty(self, name)) == 1; } public string getProperty(string name) { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoGetProperty(self, name))); } public void setProperty(string name, string value) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSetProperty(self, name, value)); } public void removeProperty(string name) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRemoveProperty(self, name)); } public System.Collections.IEnumerable iterateProperties() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateProperties(self)), this); } public void clearProperties() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClearProperties(self)); } public string checkBadValence() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoCheckBadValence(self))); } public string checkAmbiguousH() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoCheckAmbiguousH(self))); } public IndigoObject fingerprint(string type) { dispatcher.setSessionID(); if (type == null) type = ""; return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoFingerprint(self, type))); } public int countBits() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountBits(self)); } public string rawData() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoRawData(self))); } public int tell() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoTell(self)); } public void sdfAppend(IndigoObject item) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSdfAppend(self, item.self)); } public void smilesAppend(IndigoObject item) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoSmilesAppend(self, item.self)); } public void rdfHeader() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRdfHeader(self)); } public void rdfAppend(IndigoObject item) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoRdfAppend(self, item.self)); } public void cmlHeader() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoCmlHeader(self)); } public void cmlAppend(IndigoObject item) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoCmlAppend(self, item.self)); } public void cmlFooter() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoCmlFooter(self)); } public int arrayAdd(IndigoObject item) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoArrayAdd(self, item.self)); } public IndigoObject at(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAt(self, index)), this); } public int count() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCount(self)); } public void clear() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoClear(self)); } public System.Collections.IEnumerable iterateArray() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateArray(self)), this); } public void ignoreAtom(IndigoObject atom) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoIgnoreAtom(self, atom.self)); } public void unignoreAtom(IndigoObject atom) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoUnignoreAtom(self, atom.self)); } public void unignoreAllAtoms() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoUnignoreAllAtoms(self)); } public IndigoObject match(IndigoObject query) { dispatcher.setSessionID(); int res = dispatcher.checkResult(_indigo_lib.indigoMatch(self, query.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public int countMatches(IndigoObject query) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountMatches(self, query.self)); } public int countMatchesWithLimit(IndigoObject query, int embeddings_limit) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoCountMatchesWithLimit(self, query.self, embeddings_limit)); } public System.Collections.IEnumerable iterateMatches(IndigoObject query) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateMatches(self, query.self)), this); } public IndigoObject highlightedTarget() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoHighlightedTarget(self))); } public IndigoObject mapAtom(IndigoObject query_atom) { dispatcher.setSessionID(); int mapped = dispatcher.checkResult(_indigo_lib.indigoMapAtom(self, query_atom.self)); if (mapped == 0) return null; return new IndigoObject(dispatcher, mapped); } public IndigoObject mapMolecule(IndigoObject query_reaction_molecule) { dispatcher.setSessionID(); int mapped = dispatcher.checkResult(_indigo_lib.indigoMapMolecule(self, query_reaction_molecule.self)); if (mapped == 0) return null; return new IndigoObject(dispatcher, mapped); } public IndigoObject mapBond(IndigoObject query_bond) { dispatcher.setSessionID(); int mapped = dispatcher.checkResult(_indigo_lib.indigoMapBond(self, query_bond.self)); if (mapped == 0) return null; return new IndigoObject(dispatcher, mapped); } public IndigoObject allScaffolds() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoAllScaffolds(self))); } public IndigoObject decomposedMoleculeScaffold() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoDecomposedMoleculeScaffold(self))); } public System.Collections.IEnumerable iterateDecomposedMolecules() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoIterateDecomposedMolecules(self)), this); } public IndigoObject decomposedMoleculeHighlighted() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoDecomposedMoleculeHighlighted(self))); } public IndigoObject decomposedMoleculeWithRGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, dispatcher.checkResult(_indigo_lib.indigoDecomposedMoleculeWithRGroups(self))); } public IndigoObject decomposeMolecule(IndigoObject mol) { dispatcher.setSessionID(); int res = dispatcher.checkResult(_indigo_lib.indigoDecomposeMolecule(self, mol.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public System.Collections.IEnumerable iterateDecompositions() { dispatcher.setSessionID(); int res = dispatcher.checkResult(_indigo_lib.indigoIterateDecompositions(self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public void addDecomposition(IndigoObject q_match) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAddDecomposition(self, q_match.self)); } public IEnumerator GetEnumerator() { while (true) { dispatcher.setSessionID(); int next = dispatcher.checkResult(_indigo_lib.indigoNext(self)); if (next == 0) break; yield return new IndigoObject(dispatcher, next, this); } } public IndigoObject next() { dispatcher.setSessionID(); int next = dispatcher.checkResult(_indigo_lib.indigoNext(self)); if (next == 0) return null; return new IndigoObject(dispatcher, next, this); } public bool hasNext() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoHasNext(self)) == 1; } public int index() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoIndex(self)); } public String toString() { dispatcher.setSessionID(); return new String(_indigo_lib.indigoToString(self)); } public byte[] toBuffer() { dispatcher.setSessionID(); byte* buf; int bufsize; dispatcher.checkResult(_indigo_lib.indigoToBuffer(self, &buf, &bufsize)); byte[] res = new byte[bufsize]; for (int i = 0; i < bufsize; ++i) res[i] = buf[i]; return res; } public void append(IndigoObject obj) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoAppend(self, obj.self)); } public void optimize() { optimize(null); } public void optimize(string options) { dispatcher.setSessionID(); if (options == null) options = ""; dispatcher.checkResult(_indigo_lib.indigoOptimize(self, options)); } public bool normalize() { return normalize(null); } public bool normalize(string options) { dispatcher.setSessionID(); if (options == null) options = ""; return (dispatcher.checkResult(_indigo_lib.indigoNormalize(self, options)) == 1); } public void standardize() { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoStandardize(self)); } public void ionize(float pH, float pHToll) { dispatcher.setSessionID(); dispatcher.checkResult(_indigo_lib.indigoIonize(self, pH, pHToll)); } public float getAcidPkaValue(IndigoObject atom, int level, int min_level) { dispatcher.setSessionID(); float* ptr = dispatcher.checkResult(_indigo_lib.indigoGetAcidPkaValue(self, atom.self, level, min_level)); float pka = ptr[0]; return pka; } public float getBasicPkaValue(IndigoObject atom, int level, int min_level) { dispatcher.setSessionID(); float* ptr = dispatcher.checkResult(_indigo_lib.indigoGetBasicPkaValue(self, atom.self, level, min_level)); float pka = ptr[0]; return pka; } public int buildPkaModel(int level, float threshold, String filename) { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoBuildPkaModel(level, threshold, filename)); } public int expandAbbreviations() { dispatcher.setSessionID(); return dispatcher.checkResult(_indigo_lib.indigoExpandAbbreviations(self)); } public String dbgInternalType() { dispatcher.setSessionID(); return new String(dispatcher.checkResult(_indigo_lib.indigoDbgInternalType(self))); } } } Indigo-indigo-1.2.3/api/dotnet/Properties/000077500000000000000000000000001271037650300204035ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/dotnet/Properties/AssemblyInfo.cs000066400000000000000000000026451271037650300233340ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Indigo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010-2011")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("af18add7-0c63-44cd-adcd-cf20a548a8bd")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.*")] [assembly: AssemblyFileVersion("1.0.0.0")] Indigo-indigo-1.2.3/api/dotnet/Properties/Resources.resx000066400000000000000000000162251271037650300232660ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcr100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcr100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcp100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcp100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/Properties/ResourcesLinux.resx000066400000000000000000000143051271037650300243030ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x86\libindigo.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x64\libindigo.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/Properties/ResourcesMac.resx000066400000000000000000000146701271037650300237110ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Yes ..\Resource\Mac\10.7\libindigo.dylib;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/Properties/ResourcesWin2010.resx000066400000000000000000000162251271037650300242470ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcr100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcr100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcp100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcp100.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/Properties/ResourcesWin2012.resx000066400000000000000000000162221271037650300242460ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcr110.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcr110.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcp110.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcp110.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/Properties/ResourcesWin2013.resx000066400000000000000000000162221271037650300242470ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcr120.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcr120.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\msvcp120.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\msvcp120.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/dotnet/app.config000066400000000000000000000001051271037650300202120ustar00rootroot00000000000000 Indigo-indigo-1.2.3/api/dotnet/indigo-dotnet.csproj000066400000000000000000000230451271037650300222410ustar00rootroot00000000000000 Debug AnyCPU 9.0.30729 2.0 {9813B608-3372-4D46-B432-1533CBF54C62} Library Properties com.epam.indigo indigo-dotnet v2.0 512 true indigo.snk 3.5 false publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false true true true full false bin\Debug\ DEBUG;TRACE prompt 4 true AllRules.ruleset pdbonly true bin\Release\ TRACE prompt 4 true AllRules.ruleset False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 true False Windows Installer 3.1 true $(Copy) $(LibraryPath)\Win\x64\msvcr100.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp100.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr100.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp100.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x64\msvcr110.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp110.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr110.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp110.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x64\msvcr120.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp120.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr120.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp120.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\msvcr100.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp100.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr100.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp100.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\msvcr110.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp110.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr110.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp110.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.5\libindigo.dylib $(ProjectDir)Resource\Mac\10.5 $(Copy) $(LibraryPath)\Mac\10.7\libindigo.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\msvcr120.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\msvcp120.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x64\indigo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\msvcr120.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\msvcp120.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Win\x86\indigo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.5\libindigo.dylib $(ProjectDir)Resource\Mac\10.5 $(Copy) $(LibraryPath)\Mac\10.7\libindigo.dylib $(ProjectDir)Resource\Mac\10.7 Indigo-indigo-1.2.3/api/dotnet/indigo-dotnet.sln000066400000000000000000000015771271037650300215430ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indigo-dotnet", "indigo-dotnet.csproj", "{9813B608-3372-4D46-B432-1533CBF54C62}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9813B608-3372-4D46-B432-1533CBF54C62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9813B608-3372-4D46-B432-1533CBF54C62}.Debug|Any CPU.Build.0 = Debug|Any CPU {9813B608-3372-4D46-B432-1533CBF54C62}.Release|Any CPU.ActiveCfg = Release|Any CPU {9813B608-3372-4D46-B432-1533CBF54C62}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Indigo-indigo-1.2.3/api/dotnet/indigo.snk000066400000000000000000000011241271037650300202330ustar00rootroot00000000000000$RSA2ÅB©}­žvG¼gÝv§Y± +(lˆï»ðG icI)ÊèLz£]&oØ÷h»Ë2/ª×ErJWƒÛÆb›ï†ßÍe:àá9=Þá‹Xžéêq¨’ŠÙµDÙÃä²z ?¾ Z²gK›xðJ3n‘ê}¦·{Zî´!N04´a|/蟌Æó@ ˆ—®®ÅaÀ®V^=ÿzºÇ>©ÛÛª5ñ®bÊD6ÝÜÚþj)LÑ–jæ¤Ì ŸÁökÄÅÕ‘xý„¸̧Íá&H. è8D@T/r…µ‡Wý¼GÀøt¦}«:kmEpt׊²ò5x¨¥QHUôºí-€™]÷q®¯—òÔòD A$U/qA!H ÒTe—ëðs‡hÅ«hÛŸÖ(“ÓÂz&/˜iSÜ 8UiÐ¥vLyÚfkyùKiäèè1=ªeçtp’ùQ)˜Ñ©%T¡À•nc™çv0„*ÁIþ̳tÛzÏÍCìeLꜬýåˆöî×ÕK’ý§ŠÏšßÇTœBxæK{Hp®·©½f塱hû«üÞ¿å‹Qùn!Òw˜‡×9IÅ5Fˆš|# §¢Cˆªx9¬œªeiÅ4²ÿ`ÄöÇÆ[û–Éц>(„ífÄ'„c…±}²ZIndigo-indigo-1.2.3/api/get_indigo_version.py000066400000000000000000000004741271037650300212060ustar00rootroot00000000000000import os import re # # Searches for Indigo version # def getIndigoVersion(): version = "unknown" for line in open(os.path.join(os.path.dirname(__file__), "src", "indigo_version.h")): m = re.search('INDIGO_VERSION "(.*)-.*"', line) if m: version = m.group(1) return version Indigo-indigo-1.2.3/api/indigo-version.cmake000066400000000000000000000016521271037650300207140ustar00rootroot00000000000000find_package(Git) if(GIT_EXECUTABLE) EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} describe --long --tags --match "indigo-*" OUTPUT_VARIABLE INDIGO_FULL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX REPLACE "indigo-(.+)-(.+)-(.+)" "\\1.r\\2-\\3" INDIGO_FULL_VERSION ${INDIGO_FULL_VERSION}) string(REGEX REPLACE "(.+)-(.+)" "\\1" INDIGO_VERSION ${INDIGO_FULL_VERSION}) else() SET(INDIGO_VERSION "1.2.2beta.r0") SET(INDIGO_FULL_VERSION "1.2.2beta.r0-31cdd494") endif() message(STATUS "Indigo full version: " ${INDIGO_FULL_VERSION}) # Do not forget to launch build_scripts/indigo-update-version.py after changing the version because it should be ${RV} changed in the Java and .NET files as well IF($ENV{BUILD_NUMBER}) SET(INDIGO_BUILD_VERSION $ENV{BUILD_NUMBER}) ELSE() SET(INDIGO_BUILD_VERSION 0) ENDIF() SET(INDIGO_VERSION_EXT "${INDIGO_FULL_VERSION} ${PACKAGE_SUFFIX}") Indigo-indigo-1.2.3/api/indigo.h000066400000000000000000001103411271037650300163740ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo__ #define __indigo__ #if defined(_WIN32) && !defined(__MINGW32__) #define qword unsigned _int64 #else #define qword unsigned long long #endif #ifndef EXPORT_SYMBOL #ifdef _WIN32 #define EXPORT_SYMBOL __declspec(dllexport) #elif (defined __GNUC__ || defined __APPLE__) #define EXPORT_SYMBOL __attribute__ ((visibility ("default"))) #else #define EXPORT_SYMBOL #endif #endif #ifndef CEXPORT #ifndef __cplusplus #define CEXPORT EXPORT_SYMBOL #else #define CEXPORT extern "C" EXPORT_SYMBOL #endif #endif #ifndef __byte_typedef__ #define __byte_typedef__ typedef unsigned char byte; #endif /* All integer and float functions return -1 on error. */ /* All string functions return zero pointer on error. */ /* Almost all string functions return the same pointer on success; you should not free() it, but rather strdup() it if you want to keep it. */ /* System */ CEXPORT const char * indigoVersion (); // Allocate a new session. Each session has its own // set of objects created and options set up. CEXPORT qword indigoAllocSessionId (); // Switch to another session. The session, if was not allocated // previously, is allocated automatically and initialized with // empty set of objects and default options. CEXPORT void indigoSetSessionId (qword id); // Release session. The memory used by the released session // is not freed, but the number will be reused on // further allocations. CEXPORT void indigoReleaseSessionId (qword id); // Get the last error message CEXPORT const char * indigoGetLastError (void); typedef void (*INDIGO_ERROR_HANDLER)(const char *message, void *context); CEXPORT void indigoSetErrorHandler (INDIGO_ERROR_HANDLER handler, void *context); // Free an object CEXPORT int indigoFree (int handle); // Clone an object CEXPORT int indigoClone (int object); // Count object currently allocated CEXPORT int indigoCountReferences (void); // Deallocate all the objects in the current session CEXPORT int indigoFreeAllObjects (); /* Options */ CEXPORT int indigoSetOption (const char *name, const char *value); CEXPORT int indigoSetOptionInt (const char *name, int value); CEXPORT int indigoSetOptionBool (const char *name, int value); CEXPORT int indigoSetOptionFloat (const char *name, float value); CEXPORT int indigoSetOptionColor (const char *name, float r, float g, float b); CEXPORT int indigoSetOptionXY (const char *name, int x, int y); /* Basic input-output */ // indigoRead*** return a new reader object. // indigoLoad*** return a new reader object which already // contains all the data and does not depend on the given // string/buffer. All these functions are low-level and // rarely needed to anyone. CEXPORT int indigoReadFile (const char *filename); CEXPORT int indigoReadString (const char *str); CEXPORT int indigoLoadString (const char *str); CEXPORT int indigoReadBuffer (const char *buffer, int size); CEXPORT int indigoLoadBuffer (const char *buffer, int size); // indigoWrite*** return a new writer object. CEXPORT int indigoWriteFile (const char *filename); CEXPORT int indigoWriteBuffer (void); // Closes the file output stream but does not delete the object CEXPORT int indigoClose (int output); /* Iterators */ /* Iterators work in the following way: * * int item, iter = indigoIterate***(...) * * if (iter == -1) * { * fprintf(stderr, "%s", indigoGetLastError()); * return; * } * * while (item = indigoNext(iter)) * { * if (item == -1) * { * fprintf(stderr, "%s", indigoGetLastError()); * break; * } * * printf("on item #%d\n", indigoIndex(item)); * * // do something with item * * indigoFree(item); * } * indigoFree(iter); */ // Obtains the next element, returns zero if there is no next element CEXPORT int indigoNext (int iter); // Does not obtain the next element, just tells if there is one CEXPORT int indigoHasNext (int iter); // Returns the index of the element CEXPORT int indigoIndex (int item); // Removes the item from its container (usually a molecule) CEXPORT int indigoRemove (int item); /* Molecules, query molecules, SMARTS */ CEXPORT int indigoCreateMolecule (void); CEXPORT int indigoCreateQueryMolecule (void); CEXPORT int indigoLoadMolecule (int source); CEXPORT int indigoLoadMoleculeFromString (const char *string); CEXPORT int indigoLoadMoleculeFromFile (const char *filename); CEXPORT int indigoLoadMoleculeFromBuffer (const char *buffer, int size); CEXPORT int indigoLoadQueryMolecule (int source); CEXPORT int indigoLoadQueryMoleculeFromString (const char *string); CEXPORT int indigoLoadQueryMoleculeFromFile (const char *filename); CEXPORT int indigoLoadQueryMoleculeFromBuffer (const char *buffer, int size); CEXPORT int indigoLoadSmarts (int source); CEXPORT int indigoLoadSmartsFromString (const char *string); CEXPORT int indigoLoadSmartsFromFile (const char *filename); CEXPORT int indigoLoadSmartsFromBuffer (const char *buffer, int size); CEXPORT int indigoSaveMolfile (int molecule, int output); CEXPORT int indigoSaveMolfileToFile (int molecule, const char *filename); CEXPORT const char * indigoMolfile (int molecule); // accepts molecules and reactions (but not query ones) CEXPORT int indigoSaveCml (int object, int output); CEXPORT int indigoSaveCmlToFile (int object, const char *filename); CEXPORT const char * indigoCml (int object); // the output must be a file or a buffer, but not a string // (because MDLCT data usually contains zeroes) CEXPORT int indigoSaveMDLCT (int item, int output); /* Reactions, query reactions */ /* * Reaction centers */ enum { INDIGO_RC_NOT_CENTER = -1, INDIGO_RC_UNMARKED = 0, INDIGO_RC_CENTER = 1, INDIGO_RC_UNCHANGED = 2, INDIGO_RC_MADE_OR_BROKEN = 4, INDIGO_RC_ORDER_CHANGED = 8 }; CEXPORT int indigoLoadReaction (int source); CEXPORT int indigoLoadReactionFromString (const char *string); CEXPORT int indigoLoadReactionFromFile (const char *filename); CEXPORT int indigoLoadReactionFromBuffer (const char *buffer, int size); CEXPORT int indigoLoadQueryReaction (int source); CEXPORT int indigoLoadQueryReactionFromString (const char *string); CEXPORT int indigoLoadQueryReactionFromFile (const char *filename); CEXPORT int indigoLoadQueryReactionFromBuffer (const char *buffer, int size); CEXPORT int indigoLoadReactionSmarts (int source); CEXPORT int indigoLoadReactionSmartsFromString (const char *string); CEXPORT int indigoLoadReactionSmartsFromFile (const char *filename); CEXPORT int indigoLoadReactionSmartsFromBuffer (const char *buffer, int size); CEXPORT int indigoCreateReaction (void); CEXPORT int indigoCreateQueryReaction (void); CEXPORT int indigoAddReactant (int reaction, int molecule); CEXPORT int indigoAddProduct (int reaction, int molecule); CEXPORT int indigoAddCatalyst (int reaction, int molecule); CEXPORT int indigoCountReactants (int reaction); CEXPORT int indigoCountProducts (int reaction); CEXPORT int indigoCountCatalysts (int reaction); // Counts reactants, products, and catalysts. CEXPORT int indigoCountMolecules (int reaction); CEXPORT int indigoGetMolecule (int reaction, int index); CEXPORT int indigoIterateReactants (int reaction); CEXPORT int indigoIterateProducts (int reaction); CEXPORT int indigoIterateCatalysts (int reaction); // Returns an iterator for reactants, products, and catalysts. CEXPORT int indigoIterateMolecules (int reaction); CEXPORT int indigoSaveRxnfile (int reaction, int output); CEXPORT int indigoSaveRxnfileToFile (int reaction, const char *filename); CEXPORT const char * indigoRxnfile (int reaction); // Method for query optimizations for faster substructure search // (works for both query molecules and query reactions) CEXPORT int indigoOptimize (int query, const char *options); // Methods for structure normalization // It neutrailzes charges, resolves 5-valence Nitrogen, removes hydrogens and etc. // Default options is empty. CEXPORT int indigoNormalize (int structure, const char *options); // Method for molecule and query standardizing // It stadrdize charges, stereo and etc. CEXPORT int indigoStandardize (int item); // Method for structure ionization at specified pH and pH tollerance CEXPORT int indigoIonize (int item, float pH, float pH_toll); // Method for building PKA model CEXPORT int indigoBuildPkaModel (int max_level, float threshold, const char *filename); CEXPORT float * indigoGetAcidPkaValue (int item, int atom, int level, int min_level); CEXPORT float * indigoGetBasicPkaValue (int item, int atom, int level, int min_level); // Automatic reaction atom-to-atom mapping // mode is one of the following (separated by a space): // "discard" : discards the existing mapping entirely and considers only // the existing reaction centers (the default) // "keep" : keeps the existing mapping and maps unmapped atoms // "alter" : alters the existing mapping, and maps the rest of the // reaction but may change the existing mapping // "clear" : removes the mapping from the reaction. // // "ignore_charges" : do not consider atom charges while searching // "ignore_isotopes" : do not consider atom isotopes while searching // "ignore_valence" : do not consider atom valence while searching // "ignore_radicals" : do not consider atom radicals while searching CEXPORT int indigoAutomap (int reaction, const char *mode); // Returns mapping number. It might appear that there is more them // one atom with the same number in AAM // Value 0 means no mapping number has been specified. CEXPORT int indigoGetAtomMappingNumber (int reaction, int reaction_atom); CEXPORT int indigoSetAtomMappingNumber (int reaction, int reaction_atom, int number); // Getters and setters for reacting centers CEXPORT int indigoGetReactingCenter (int reaction, int reaction_bond, int*rc); CEXPORT int indigoSetReactingCenter (int reaction, int reaction_bond, int rc); // Clears all reaction AAM information CEXPORT int indigoClearAAM (int reaction); // Corrects reacting centers according to AAM CEXPORT int indigoCorrectReactingCenters (int reaction); /* Accessing a molecule */ enum { INDIGO_ABS = 1, INDIGO_OR = 2, INDIGO_AND = 3, INDIGO_EITHER = 4, INDIGO_UP = 5, INDIGO_DOWN = 6, INDIGO_CIS = 7, INDIGO_TRANS = 8, INDIGO_CHAIN = 9, INDIGO_RING = 10, INDIGO_ALLENE = 11, INDIGO_SINGLET = 101, INDIGO_DOUBLET = 102, INDIGO_TRIPLET = 103, }; // Returns an iterator for all atoms of the given // molecule, including r-sites and pseudoatoms. CEXPORT int indigoIterateAtoms (int molecule); CEXPORT int indigoIteratePseudoatoms (int molecule); CEXPORT int indigoIterateRSites (int molecule); CEXPORT int indigoIterateStereocenters (int molecule); CEXPORT int indigoIterateAlleneCenters (int molecule); CEXPORT int indigoIterateRGroups (int molecule); CEXPORT int indigoIsPseudoatom (int atom); CEXPORT int indigoIsRSite (int atom); // returns INDIGO_{ABS,OR,AND,EITHER} // or zero if the atom is not a stereoatom CEXPORT int indigoStereocenterType (int atom); CEXPORT int indigoChangeStereocenterType (int atom, int type); CEXPORT int indigoStereocenterGroup (int atom); CEXPORT int indigoSetStereocenterGroup (int atom, int group); // returns 4 integers with atom indices that defines stereocenter pyramid CEXPORT const int* indigoStereocenterPyramid (int atom); CEXPORT int indigoSingleAllowedRGroup (int rsite); CEXPORT int indigoAddStereocenter (int atom, int type, int v1, int v2, int v3, int v4); // Applicable to an R-Group, but not to a molecule CEXPORT int indigoIterateRGroupFragments (int rgroup); // Applicable to an R-Group and to a molecule // Returns maximal order of attachment points CEXPORT int indigoCountAttachmentPoints (int item); CEXPORT int indigoIterateAttachmentPoints (int item, int order); CEXPORT const char * indigoSymbol (int atom); CEXPORT int indigoDegree (int atom); // Returns zero if the charge is ambiguous // If the charge is nonambiguous, returns 1 and writes *charge CEXPORT int indigoGetCharge (int atom, int *charge); // Same as indigoGetCharge CEXPORT int indigoGetExplicitValence (int atom, int *valence); CEXPORT int indigoSetExplicitValence (int atom, int valence); // Returns a number of element from the periodic table. // Returns zero on ambiguous atom. // Can not be applied to pseudo-atoms and R-sites. CEXPORT int indigoAtomicNumber (int atom); // Returns zero on unspecified or ambiguous isotope CEXPORT int indigoIsotope (int atom); // Not applicable to query molecules. CEXPORT int indigoValence (int atom); // Applicable to atoms, query atoms, and molecules. Can fail // (return zero) on query atoms where the number of hydrogens // is not definitely known. Otherwise, returns one and writes *hydro. CEXPORT int indigoCountHydrogens (int item, int *hydro); // Applicable to non-query molecules and atoms. CEXPORT int indigoCountImplicitHydrogens (int item); // On success, returns always the same pointer to a 3-element array; // you should not free() it, but rather memcpy() it if you want to keep it. CEXPORT float * indigoXYZ (int atom); CEXPORT int indigoSetXYZ (int atom, float x, float y, float z); CEXPORT int indigoCountSuperatoms (int molecule); CEXPORT int indigoCountDataSGroups (int molecule); CEXPORT int indigoCountRepeatingUnits (int molecule); CEXPORT int indigoCountMultipleGroups (int molecule); CEXPORT int indigoCountGenericSGroups (int molecule); CEXPORT int indigoIterateDataSGroups (int molecule); CEXPORT int indigoIterateSuperatoms (int molecule); CEXPORT int indigoIterateGenericSGroups (int molecule); CEXPORT int indigoIterateRepeatingUnits (int molecule); CEXPORT int indigoIterateMultipleGroups (int molecule); CEXPORT int indigoGetSuperatom (int molecule, int index); CEXPORT int indigoGetDataSGroup (int molecule, int index); CEXPORT int indigoGetGenericSGroup (int molecule, int index); CEXPORT int indigoGetMultipleGroup (int molecule, int index); CEXPORT int indigoGetRepeatingUnit (int molecule, int index); CEXPORT const char * indigoDescription (int data_sgroup); CEXPORT const char * indigoData (int data_sgroup); CEXPORT int indigoAddDataSGroup (int molecule, int natoms, int *atoms, int nbonds, int *bonds, const char *description, const char *data); CEXPORT int indigoAddSuperatom (int molecule, int natoms, int *atoms, const char *name); CEXPORT int indigoSetDataSGroupXY (int sgroup, float x, float y, const char *options); CEXPORT int indigoSetSGroupData (int sgroup, const char *data); CEXPORT int indigoSetSGroupCoords (int sgroup, float x, float y); CEXPORT int indigoSetSGroupDescription (int sgroup, const char *description); CEXPORT int indigoSetSGroupFieldName (int sgroup, const char *name); CEXPORT int indigoSetSGroupQueryCode (int sgroup, const char *querycode); CEXPORT int indigoSetSGroupQueryOper (int sgroup, const char *queryoper); CEXPORT int indigoSetSGroupDisplay (int sgroup, const char *option); CEXPORT int indigoSetSGroupLocation (int sgroup, const char *option); CEXPORT int indigoSetSGroupTag (int sgroup, const char *tag); CEXPORT int indigoSetSGroupTagAlign (int sgroup, int tag_align); CEXPORT int indigoSetSGroupDataType (int sgroup, const char *type); CEXPORT int indigoSetSGroupXCoord (int sgroup, float x); CEXPORT int indigoSetSGroupYCoord (int sgroup, float y); CEXPORT int indigoCreateSGroup (const char *type, int mapping, const char *name); CEXPORT const char * indigoGetSGroupClass (int sgroup); CEXPORT const char * indigoGetSGroupName (int sgroup); CEXPORT int indigoSetSGroupClass (int sgroup, const char *sgclass); CEXPORT int indigoSetSGroupName (int sgroup, const char *sgname); CEXPORT int indigoGetSGroupNumCrossBonds (int sgroup); CEXPORT int indigoAddSGroupAttachmentPoint (int sgroup, int aidx, int lvidx, const char *apid); CEXPORT int indigoDeleteSGroupAttachmentPoint (int sgroup, int index); CEXPORT int indigoGetSGroupDisplayOption (int sgroup); CEXPORT int indigoSetSGroupDisplayOption (int sgroup, int option); CEXPORT int indigoGetSGroupMultiplier (int sgroup); CEXPORT int indigoSetSGroupMultiplier (int sgroup, int multiplier); CEXPORT int indigoSetSGroupBrackets (int sgroup, int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); CEXPORT int indigoFindSGroups (int item, const char *property, const char *value); CEXPORT int indigoGetSGroupType (int item); CEXPORT int indigoGetSGroupIndex (int item); CEXPORT int indigoTransformSCSRtoCTAB (int item); CEXPORT int indigoTransformCTABtoSCSR (int molecule, int templates); CEXPORT int indigoResetCharge (int atom); CEXPORT int indigoResetExplicitValence (int atom); CEXPORT int indigoResetIsotope (int atom); CEXPORT int indigoSetAttachmentPoint (int atom, int order); CEXPORT int indigoClearAttachmentPoints (int item); CEXPORT int indigoRemoveConstraints (int item, const char *type); CEXPORT int indigoAddConstraint (int item, const char *type, const char *value); CEXPORT int indigoAddConstraintNot (int item, const char *type, const char *value); CEXPORT int indigoAddConstraintOr (int atom, const char* type, const char* value); CEXPORT int indigoResetStereo (int item); CEXPORT int indigoInvertStereo (int item); CEXPORT int indigoCountAtoms (int molecule); CEXPORT int indigoCountBonds (int molecule); CEXPORT int indigoCountPseudoatoms (int molecule); CEXPORT int indigoCountRSites (int molecule); CEXPORT int indigoIterateBonds (int molecule); // Returns 1/2/3 if the bond is a single/double/triple bond // Returns 4 if the bond is an aromatic bond // Returns zero if the bond is ambiguous (query bond) CEXPORT int indigoBondOrder (int bond); // Returns INDIGO_{UP/DOWN/EITHER/CIS/TRANS}, // or zero if the bond is not a stereobond CEXPORT int indigoBondStereo (int bond); // Returns INDIGO_{CHAIN/RING}, CEXPORT int indigoTopology (int bond); // Returns an iterator whose elements can be treated as atoms. // At the same time, they support indigoBond() call. CEXPORT int indigoIterateNeighbors (int atom); // Applicable exclusively to the "atom neighbors iterator". // Returns a bond to the neighbor atom. CEXPORT int indigoBond (int nei); // Accessing atoms and bonds by index CEXPORT int indigoGetAtom (int molecule, int idx); CEXPORT int indigoGetBond (int molecule, int idx); CEXPORT int indigoSource (int bond); CEXPORT int indigoDestination (int bond); CEXPORT int indigoClearCisTrans (int handle); CEXPORT int indigoClearStereocenters (int handle); CEXPORT int indigoCountStereocenters (int molecule); CEXPORT int indigoClearAlleneCenters (int molecule); CEXPORT int indigoCountAlleneCenters (int molecule); CEXPORT int indigoResetSymmetricCisTrans (int handle); CEXPORT int indigoResetSymmetricStereocenters (int handle); CEXPORT int indigoMarkEitherCisTrans (int handle); CEXPORT int indigoMarkStereobonds (int handle); CEXPORT int indigoValidateChirality (int handle); // Accepts a symbol from the periodic table (like "C" or "Br"), // or a pseudoatom symbol, like "Pol". Returns the added atom. CEXPORT int indigoAddAtom (int molecule, const char *symbol); // Set a new atom instead of specified CEXPORT int indigoResetAtom (int atom, const char *symbol); // Accepts Rsite name "R" (or just ""), "R1", "R2" or list with names "R1 R3" CEXPORT int indigoAddRSite (int molecule, const char *name); CEXPORT int indigoSetRSite (int atom, const char *name); CEXPORT int indigoSetCharge (int atom, int charge); CEXPORT int indigoSetIsotope (int atom, int isotope); // If the radical is nonambiguous, returns 1 and writes *electrons CEXPORT int indigoGetRadicalElectrons (int atom, int *electrons); // If the radical is nonambiguous, returns 1 and writes *radical CEXPORT int indigoGetRadical (int atom, int *radical); CEXPORT int indigoSetRadical (int atom, int radical); CEXPORT int indigoResetRadical (int atom); // Used for hacks with aromatic molecules; not recommended to use // in other situations CEXPORT int indigoSetImplicitHCount (int atom, int impl_h); // Accepts two atoms (source and destination) and the order of the new bond // (1/2/3/4 = single/double/triple/aromatic). Returns the added bond. CEXPORT int indigoAddBond (int source, int destination, int order); CEXPORT int indigoSetBondOrder (int bond, int order); CEXPORT int indigoMerge (int where_to, int what); /* Highlighting */ // Access atoms and bonds CEXPORT int indigoHighlight (int item); // Access atoms, bonds, molecules, and reactions CEXPORT int indigoUnhighlight (int item); // Access atoms and bonds CEXPORT int indigoIsHighlighted (int item); /* Connected components of molecules */ CEXPORT int indigoCountComponents (int molecule); CEXPORT int indigoComponentIndex (int atom); CEXPORT int indigoIterateComponents (int molecule); // Returns a 'molecule component' object, which can not be used as a // [query] molecule, but supports the indigo{Count,Iterate}{Atoms,Bonds} calls, // and also the indigoClone() call, which returns a [query] molecule. CEXPORT int indigoComponent (int molecule, int index); /* Smallest Set of Smallest Rings */ CEXPORT int indigoCountSSSR (int molecule); CEXPORT int indigoIterateSSSR (int molecule); CEXPORT int indigoIterateSubtrees (int molecule, int min_atoms, int max_atoms); CEXPORT int indigoIterateRings (int molecule, int min_atoms, int max_atoms); CEXPORT int indigoIterateEdgeSubmolecules (int molecule, int min_bonds, int max_bonds); /* Calculation on molecules */ CEXPORT int indigoCountHeavyAtoms (int molecule); CEXPORT int indigoGrossFormula (int molecule); CEXPORT float indigoMolecularWeight (int molecule); CEXPORT float indigoMostAbundantMass (int molecule); CEXPORT float indigoMonoisotopicMass (int molecule); CEXPORT const char * indigoCanonicalSmiles (int molecule); CEXPORT const char * indigoLayeredCode (int molecule); CEXPORT const int * indigoSymmetryClasses (int molecule, int *count_out); CEXPORT int indigoHasCoord (int molecule); CEXPORT int indigoHasZCoord (int molecule); CEXPORT int indigoIsChiral (int molecule); CEXPORT int indigoIsPossibleFischerProjection (int molecule, const char *options); CEXPORT int indigoCreateSubmolecule (int molecule, int nvertices, int *vertices); CEXPORT int indigoCreateEdgeSubmolecule (int molecule, int nvertices, int *vertices, int nedges, int *edges); CEXPORT int indigoGetSubmolecule (int molecule, int nvertices, int *vertices); CEXPORT int indigoRemoveAtoms (int molecule, int nvertices, int *vertices); CEXPORT int indigoRemoveBonds (int molecule, int nbonds, int *bonds); // Determines and applies the best transformation to the given molecule // so that the specified atoms move as close as possible to the desired // positions. The size of desired_xyz is equal to 3 * natoms. // The return value is the root-mean-square measure of the difference // between the desired and obtained positions. CEXPORT float indigoAlignAtoms (int molecule, int natoms, int *atom_ids, float *desired_xyz); /* Things that work for both molecules and reactions */ CEXPORT int indigoAromatize (int item); CEXPORT int indigoDearomatize (int item); CEXPORT int indigoFoldHydrogens (int item); CEXPORT int indigoUnfoldHydrogens (int item); CEXPORT int indigoLayout (int object); CEXPORT const char * indigoSmiles (int item); // Returns a "mapping" if there is an exact match, zero otherwise // The flags string consists of space-separated flags. // The more flags, the more restrictive matching is done. // "ELE": Distribution of electrons: bond types, atom charges, radicals, valences // "MAS": Atom isotopes // "STE": Stereochemistry: chiral centers, stereogroups, and cis-trans bonds // "FRA": Connected fragments: disallows match of separate ions in salts // "ALL": All of the above // By default (with null or empty flags string) all flags are on. CEXPORT int indigoExactMatch (int item1, int item2, const char *flags); // "beg" and "end" refer to the two ends of the tautomeric chain. Allowed // elements are separated by commas. '1' at the beginning means an aromatic // atom, while '0' means an aliphatic atom. CEXPORT int indigoSetTautomerRule (int id, const char *beg, const char *end); CEXPORT int indigoRemoveTautomerRule (int id); CEXPORT int indigoClearTautomerRules (); CEXPORT const char * indigoName (int handle); CEXPORT int indigoSetName (int handle, const char *name); // You should not free() the obtained buffer, but rather memcpy() it if you want to keep it CEXPORT int indigoSerialize (int handle, byte **buf, int *size); CEXPORT int indigoUnserialize (const byte *buf, int size); // Applicable to molecules/reactions obtained from SDF or RDF files, // and to their clones, and to their R-Group deconvolutions. CEXPORT int indigoHasProperty (int handle, const char *prop); CEXPORT const char * indigoGetProperty (int handle, const char *prop); // Applicable to newly created or cloned molecules/reactions, // and also to molecules/reactions obtained from SDF or RDF files. // If the property with the given name does not exist, it is created automatically. CEXPORT int indigoSetProperty (int item, const char *prop, const char *value); // Does not raise an error if the given property does not exist CEXPORT int indigoRemoveProperty (int item, const char *prop); // Returns an iterator that one can pass to indigoName() to // know the name of the property. The value of the property can be // obtained via indigoGetProperty() call to the object CEXPORT int indigoIterateProperties (int handle); // Clears all properties of the molecule CEXPORT int indigoClearProperties (int handle); // Accepts a molecule or reaction (but not query molecule or query reaction). // Returns a string describing the first encountered mistake with valence. // Returns an empty string if the input molecule/reaction is fine. CEXPORT const char * indigoCheckBadValence (int handle); // Accepts a molecule or reaction (but not query molecule or query reaction). // Returns a string describing the first encountered mistake with ambiguous H counter. // Returns an empty string if the input molecule/reaction is fine. CEXPORT const char * indigoCheckAmbiguousH (int handle); /* Fingerprints */ // Returns a 'fingerprint' object, which can then be passed to: // indigoToString() -- to get hexadecimal representation // indigoToBuffer() -- to get raw byte data // indigoSimilarity() -- to calculate similarity with another fingerprint // The following fingerprint types are available: // "sim" -- "Similarity fingerprint", useful for calculating // similarity measures (the default) // "sub" -- "Substructure fingerprint", useful for substructure screening // "sub-res" -- "Resonance substructure fingerprint", useful for resonance // substructure screening // "sub-tau" -- "Tautomer substructure fingerprint", useful for tautomer // substructure screening // "full" -- "Full fingerprint", which has all the mentioned // fingerprint types included CEXPORT int indigoFingerprint (int item, const char *type); // Counts the nonzero (i.e. one) bits in a fingerprint CEXPORT int indigoCountBits (int fingerprint); // Counts the number of the coinincident in two fingerprints CEXPORT int indigoCommonBits (int fingerprint1, int fingerprint2); //Return one bits string for the fingerprint object CEXPORT const char* indigoOneBitsList (int fingerprint); // Accepts two molecules, two reactions, or two fingerprints. // Returns the similarity measure between them. // Metrics: "tanimoto", "tversky", "tversky ", "euclid-sub" or "normalized-edit" // Zero pointer or empty string defaults to "tanimoto". // "tversky" without numbers defaults to alpha = beta = 0.5 CEXPORT float indigoSimilarity (int item1, int item2, const char *metrics); /* Working with SDF/RDF/SMILES/CML/CDX files */ CEXPORT int indigoIterateSDF (int reader); CEXPORT int indigoIterateRDF (int reader); CEXPORT int indigoIterateSmiles (int reader); CEXPORT int indigoIterateCML (int reader); CEXPORT int indigoIterateCDX (int reader); CEXPORT int indigoIterateSDFile (const char *filename); CEXPORT int indigoIterateRDFile (const char *filename); CEXPORT int indigoIterateSmilesFile (const char *filename); CEXPORT int indigoIterateCMLFile (const char *filename); CEXPORT int indigoIterateCDXFile (const char *filename); // Applicable to items returned by SDF/RDF iterators. // Returns the content of SDF/RDF item. CEXPORT const char * indigoRawData (int item); // Applicable to items returned by SDF/RDF iterators. // Returns the offset in the SDF/RDF file. CEXPORT int indigoTell (int handle); // Saves the molecule to an SDF output stream CEXPORT int indigoSdfAppend (int output, int item); // Saves the molecule to a multiline SMILES output stream CEXPORT int indigoSmilesAppend (int output, int item); // Similarly for RDF files, except that the header should be written first CEXPORT int indigoRdfHeader (int output); CEXPORT int indigoRdfAppend (int output, int item); // Similarly for CML files, except that they have both header and footer CEXPORT int indigoCmlHeader (int output); CEXPORT int indigoCmlAppend (int output, int item); CEXPORT int indigoCmlFooter (int output); // Create saver objects that can be used to save molecules or reactions // Supported formats: 'sdf', 'smi' or 'smiles', 'cml', 'rdf' // Format argument is case-insensitive // Saver should be closed with indigoClose function CEXPORT int indigoCreateSaver (int output, const char *format); CEXPORT int indigoCreateFileSaver (const char *filename, const char *format); // Append object to a specified saver stream CEXPORT int indigoAppend (int saver, int object); /* Arrays */ CEXPORT int indigoCreateArray (); // Note: a clone of the object is added, not the object itself CEXPORT int indigoArrayAdd (int arr, int object); CEXPORT int indigoAt (int item, int index); CEXPORT int indigoCount (int item); CEXPORT int indigoClear (int arr); CEXPORT int indigoIterateArray (int arr); /* Substructure matching */ // Returns a new 'matcher' object // 'mode' is reserved for future use; currently its value is ignored CEXPORT int indigoSubstructureMatcher (int target, const char *mode); // Ignore target atom in the substructure matcher CEXPORT int indigoIgnoreAtom (int matcher, int atom_object); // Ignore target atom in the substructure matcher CEXPORT int indigoUnignoreAtom (int matcher, int atom_object); // Clear list of ignored target atoms in the substructure matcher CEXPORT int indigoUnignoreAllAtoms (int matcher); // Returns a new 'match' object on success, zero on fail // matcher is an matcher object returned by indigoSubstructureMatcher CEXPORT int indigoMatch (int matcher, int query); // Counts the number of embeddings of the query structure into the target CEXPORT int indigoCountMatches (int matcher, int query); // Counts the number of embeddings of the query structure into the target // If number of embeddings is more then limit then limit is returned CEXPORT int indigoCountMatchesWithLimit (int matcher, int query, int embeddings_limit); // Returns substructure matches iterator CEXPORT int indigoIterateMatches (int matcher, int query); // Accepts a 'match' object obtained from indigoMatchSubstructure. // Returns a new molecule which has the query highlighted. CEXPORT int indigoHighlightedTarget (int match); // Accepts an atom from the query, not an atom index. // You can use indigoGetAtom() to obtain the atom by its index. // Returns the corresponding target atom, not an atom index. If query // atom doesn't match particular atom in the target (R-group or explicit // hydrogen) then return value is zero. // You can use indigoIndex() to obtain the index of the returned atom. CEXPORT int indigoMapAtom (int handle, int atom); // Accepts a bond from the query, not a bond index. // You can use indigoGetBond() to obtain the bond by its index. // Returns the corresponding target bond, not a bond index. If query // bond doesn't match particular bond in the target (R-group or explicit // hydrogen) then return value is zero. // You can use indigoIndex() to obtain the index of the returned bond. CEXPORT int indigoMapBond (int handle, int bond); // Accepts a molecule from the query reaction, not a molecule index. // You can use indigoGetMolecule() to obtain the bond by its index. // Returns the corresponding target molecule, not a reaction index. If query // molecule doesn't match particular molecule in the target then return // value is zero. // You can use indigoIndex() to obtain the index of the returned molecule. CEXPORT int indigoMapMolecule (int handle, int molecule); // Accepts a molecule and options for tautomer enumeration algorithms // Returns an iterator object over the molecules that are tautomers of this molecule. CEXPORT int indigoIterateTautomers (int molecule, const char *options); /* Scaffold detection */ // Returns zero if no common substructure is found. // Otherwise, it returns a new object, which can be // (i) treated as a structure: the maximum (by the number of rings) common // substructure of the given structures. // (ii) passed to indigoAllScaffolds() CEXPORT int indigoExtractCommonScaffold (int structures, const char *options); // Returns an array of all possible scaffolds. // The input parameter is the value returned by indigoExtractCommonScaffold(). CEXPORT int indigoAllScaffolds (int extracted); /* R-Group deconvolution */ // Returns a ``decomposition'' object that can be passed to // indigoDecomposedMoleculeScaffold() and // indigoIterateDecomposedMolecules() CEXPORT int indigoDecomposeMolecules (int scaffold, int structures); // Returns a scaffold molecule with r-sites marking the place // for substituents to add to form the structures given above. CEXPORT int indigoDecomposedMoleculeScaffold (int decomp); // Returns an iterator which corresponds to the given collection of structures. // indigoDecomposedMoleculeHighlighted() and // indigoDecomposedMoleculeWithRGroups() are applicable to the // values returned by the iterator. CEXPORT int indigoIterateDecomposedMolecules (int decomp); // Returns a molecule with highlighted scaffold CEXPORT int indigoDecomposedMoleculeHighlighted (int decomp); // Returns a query molecule with r-sites and "R1=...", "R2=..." // substituents defined. The 'scaffold' part of the molecule // is identical to the indigoDecomposedMoleculeScaffold() CEXPORT int indigoDecomposedMoleculeWithRGroups (int decomp); /* * Decomposition Iteration API */ // Returns a 'decomposition' object CEXPORT int indigoCreateDecomposer(int scaffold); // Returns a 'decomposition' item CEXPORT int indigoDecomposeMolecule(int decomp, int mol); // Returns decomposition iterator CEXPORT int indigoIterateDecompositions(int deco_item); // Adds the input decomposition to a full scaffold CEXPORT int indigoAddDecomposition(int decomp, int q_match); /* R-Group convolution */ CEXPORT int indigoGetFragmentedMolecule(int elem, const char *options); CEXPORT int indigoRGroupComposition(int molecule, const char *options); /* * Abbreviations */ CEXPORT int indigoExpandAbbreviations (int molecule); /* Other */ CEXPORT const char * indigoToString (int handle); CEXPORT int indigoToBuffer (int handle, char **buf, int *size); /* Reaction products enumeration */ // Accepts a query reaction with markd R-sites, and array of arrays // of substituents corresponding to the R-Sites. Returns an array of // reactions with R-Sites replaced by the actual substituents. CEXPORT int indigoReactionProductEnumerate (int reaction, int monomers); CEXPORT int indigoTransform (int reaction, int monomers); /* Debug functionality */ // Returns internal type of an object CEXPORT const char * indigoDbgInternalType (int object); // Internal breakpoint CEXPORT void indigoDbgBreakpoint (void); // Methods that returns profiling infromation in a human readable format CEXPORT const char * indigoDbgProfiling (int /*bool*/ whole_session); // Reset profiling counters either for the current state or for the whole session CEXPORT int indigoDbgResetProfiling (int /*bool*/ whole_session); // Methods that returns profiling counter value for a particular counter CEXPORT qword indigoDbgProfilingGetCounter (const char *name, int /*bool*/ whole_session); #endif Indigo-indigo-1.2.3/api/indigo_cpp.h000066400000000000000000000022461271037650300172420ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_cpp__ #define __indigo_cpp__ #include "indigo.h" class IndigoAutoObj { public: IndigoAutoObj (int obj_id = -1) { _obj_id = obj_id; } ~IndigoAutoObj () { free(); } void free () { if (_obj_id != -1) { indigoFree(_obj_id); _obj_id = -1; } } operator int () const { return _obj_id; } IndigoAutoObj& operator= (int id) { free(); _obj_id = id; return *this; } private: int _obj_id; }; #endif // __indigo_cpp__ Indigo-indigo-1.2.3/api/java/000077500000000000000000000000001271037650300156735ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/pom.xml000066400000000000000000000137661271037650300172250ustar00rootroot00000000000000 4.0.0 com.epam.indigo indigo 1.2.2beta.r117 jar Indigo Universal organic chemistry toolkit http://lifescience.opensource.epam.com/indigo/api GNU General Public License v3 http://www.gnu.org/licenses/gpl.html repo engineer Mikhail Kviatkovskii mikhail_kviatkovskii@epam.com UTC+3 scm:git:git@github.com:epam/Indigo.git scm:git:git@github.com:epam/Indigo.git git@github.com:epam/Indigo.git net.java.dev.jna jna 3.5.0 org.apache.maven.plugins maven-compiler-plugin 3.1 1.5 1.5 org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources package jar org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs package jar org.apache.maven.plugins maven-jar-plugin 2.3.1 false org.sonatype.plugins nexus-staging-maven-plugin 1.6.3 true deploy ossrh https://oss.sonatype.org/ false ../libs/shared **/*.lib **/*indigo.* **/msvc*.* ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.4 ${gpg.passhprase} --no-tty sign-artifacts verify sign Indigo-indigo-1.2.3/api/java/src/000077500000000000000000000000001271037650300164625ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/000077500000000000000000000000001271037650300174065ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/java/000077500000000000000000000000001271037650300203275ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/java/com/000077500000000000000000000000001271037650300211055ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/000077500000000000000000000000001271037650300220275ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/indigo/000077500000000000000000000000001271037650300233005ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/indigo/Indigo.java000066400000000000000000000674221271037650300253670ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Native; import com.sun.jna.Pointer; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Collection; public class Indigo { public static final int ABS = 1; public static final int OR = 2; public static final int AND = 3; public static final int EITHER = 4; public static final int UP = 5; public static final int DOWN = 6; public static final int CIS = 7; public static final int TRANS = 8; public static final int CHAIN = 9; public static final int RING = 10; public static final int ALLENE = 11; public static final int SINGLET = 101; public static final int DOUBLET = 102; public static final int TRIPLET = 103; public static final int RC_NOT_CENTER = -1; public static final int RC_UNMARKED = 0; public static final int RC_CENTER = 1; public static final int RC_UNCHANGED = 2; public static final int RC_MADE_OR_BROKEN = 4; public static final int RC_ORDER_CHANGED = 8; // JNA does not allow throwing exception from callbacks, thus we can not // use the error handler and we have to check the error codes. Below are // four functions to ease checking them. public static final int OS_WINDOWS = 1; public static final int OS_MACOS = 2; public static final int OS_LINUX = 3; public static final int OS_SOLARIS = 4; private boolean _session_released = false; private static int _os = 0; private static String _dllpath = ""; private static IndigoLib _lib = null; private String _path; private long _sid; public Indigo(String path) { _path = path; loadIndigo(path); _sid = _lib.indigoAllocSessionId(); } public Indigo() { this(null); } static public int checkResult(Object obj, int result) { if (result < 0) throw new IndigoException(obj, _lib.indigoGetLastError()); return result; } static public int checkResult(Object obj, Object obj2, int result) { if (result < 0) throw new IndigoException(new Object[]{obj, obj2}, _lib.indigoGetLastError()); return result; } static public float checkResultFloat(Object obj, float result) { if (result < 0) throw new IndigoException(obj, _lib.indigoGetLastError()); return result; } static public String checkResultString(Object obj, Pointer result) { if (result == Pointer.NULL) throw new IndigoException(obj, _lib.indigoGetLastError()); return result.getString(0, false); } static public Pointer checkResultPointer(Object obj, Pointer result) { if (result == Pointer.NULL) throw new IndigoException(obj, _lib.indigoGetLastError()); return result; } public static int[] toIntArray(Collection collection) { if (collection == null) return new int[0]; int[] res = new int[collection.size()]; int i = 0; for (Integer x : collection) res[i++] = x.intValue(); return res; } public static float[] toFloatArray(Collection collection) { if (collection == null) return new float[0]; float[] res = new float[collection.size()]; int i = 0; for (Float x : collection) res[i++] = x.floatValue(); return res; } private static String getHashString(InputStream input) throws NoSuchProviderException, NoSuchAlgorithmException, IOException { String res = ""; MessageDigest algorithm = MessageDigest.getInstance("MD5"); algorithm.reset(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[4096]; while ((nRead = input.read(data, 0, data.length)) != -1) { buffer.write(data, 0, nRead); } buffer.flush(); algorithm.update(buffer.toByteArray()); byte[] hashArray = algorithm.digest(); String tmp = ""; for (int i = 0; i < hashArray.length; i++) { tmp = (Integer.toHexString(0xFF & hashArray[i])); if (tmp.length() == 1) { res += "0" + tmp; } else { res += tmp; } } return res; } public static String extractFromJar(Class cls, String path, String filename) { InputStream stream = cls.getResourceAsStream(path + "/" + filename); if (stream == null) return null; String tmpdir_path; final File tmpdir; final File dllfile; try { // Clone input stream to calculate its hash and copy to temporary folder ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int len; while ((len = stream.read(buffer)) > -1 ) { baos.write(buffer, 0, len); } baos.flush(); InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); baos.close(); // Calculate md5 hash string to name temporary folder String streamHashString = getHashString(is1); is1.close(); tmpdir_path = System.getProperty("java.io.tmpdir") + File.separator + "indigo" + streamHashString; tmpdir = new File(tmpdir_path); if (!tmpdir.exists()) { if (!tmpdir.mkdir()) { return null; } } // Copy library to temporary folder dllfile = new File(tmpdir.getAbsoluteFile() + File.separator + filename); if (!dllfile.exists()) { FileOutputStream outstream = new FileOutputStream(dllfile); byte buf[] = new byte[4096]; while ((len = is2.read(buf)) > 0) outstream.write(buf, 0, len); outstream.close(); is2.close(); } } catch (IOException e) { return null; } catch (NoSuchAlgorithmException e) { return null; } catch (NoSuchProviderException e) { return null; } String p; try { p = dllfile.getCanonicalPath(); } catch (IOException e) { return null; } final String fullpath = p; return fullpath; } private static String getPathToBinary(String path, String filename) { if (path == null) { String res = extractFromJar(Indigo.class, "/" + _dllpath, filename); if (res != null) return res; path = "lib"; } path = path + File.separator + _dllpath + File.separator + filename; try { return (new File(path)).getCanonicalPath(); } catch (IOException e) { return path; } } private synchronized static void loadIndigo(String path) { if (_lib != null) return; if (_os == OS_LINUX || _os == OS_SOLARIS) _lib = (IndigoLib) Native.loadLibrary(getPathToBinary(path, "libindigo.so"), IndigoLib.class); else if (_os == OS_MACOS) _lib = (IndigoLib) Native.loadLibrary(getPathToBinary(path, "libindigo.dylib"), IndigoLib.class); else // _os == OS_WINDOWS { if ((new File(getPathToBinary(path, "msvcr100.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcr100.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } if ((new File(getPathToBinary(path, "msvcp100.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcp100.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } if ((new File(getPathToBinary(path, "msvcr110.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcr110.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } if ((new File(getPathToBinary(path, "msvcp110.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcp110.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } if ((new File(getPathToBinary(path, "msvcr120.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcr120.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } if ((new File(getPathToBinary(path, "msvcp120.dll"))).exists()) { try { System.load(getPathToBinary(path, "msvcp120.dll")); } catch (UnsatisfiedLinkError e) { // File could have been already loaded } } _lib = (IndigoLib) Native.loadLibrary(getPathToBinary(path, "indigo.dll"), IndigoLib.class); } } static public String getPlatformDependentPath() { return _dllpath; } public boolean sessionReleased() { return _session_released; } public static IndigoLib getLibrary() { return _lib; } public static int getOs() { String namestr = System.getProperty("os.name"); if (namestr.matches("^Windows.*")) return OS_WINDOWS; else if (namestr.matches("^Mac OS.*")) return OS_MACOS; else if (namestr.matches("^Linux.*")) return OS_LINUX; else if (namestr.matches("^SunOS.*")) return OS_SOLARIS; else throw new Error("Operating system not recognized"); } private static String getDllPath() { String path = ""; switch (_os) { case OS_WINDOWS: path += "Win"; break; case OS_LINUX: path += "Linux"; break; case OS_SOLARIS: path += "Sun"; break; case OS_MACOS: path += "Mac"; break; default: throw new Error("OS not set"); } path += "/"; if (_os == OS_MACOS) { String version = System.getProperty("os.version"); int minorVersion = Integer.parseInt(version.split("\\.")[1]); Integer usingVersion = null; for (int i = minorVersion; i >= 5; i--) { if (Indigo.class.getResourceAsStream("/" + path + "10." + i + "/libindigo.dylib") != null) { usingVersion = i; break; } } if (usingVersion == null) { throw new Error("Indigo cannot find native libraries for Mac OS X 10." + minorVersion); } path += "10." + usingVersion; } else if (_os == OS_SOLARIS) { String model = System.getProperty("sun.arch.data.model"); if (model.equals("32")) path += "sparc32"; else path += "sparc64"; } else { String archstr = System.getProperty("os.arch"); if (archstr.equals("x86") || archstr.equals("i386")) path += "x86"; else if (archstr.equals("x86_64") || archstr.equals("amd64")) path += "x64"; else throw new Error("architecture not recognized"); } return path; } static { _os = getOs(); _dllpath = getDllPath(); } public String version() { return _lib.indigoVersion(); } public int countReferences() { setSessionID(); return checkResult(this, _lib.indigoCountReferences()); } public void setSessionID() { _lib.indigoSetSessionId(_sid); } public void setOption(String option, String value) { setSessionID(); checkResult(this, _lib.indigoSetOption(option, value)); } public void setOption(String option, int value) { setSessionID(); checkResult(this, _lib.indigoSetOptionInt(option, value)); } public void setOption(String option, int x, int y) { setSessionID(); checkResult(this, _lib.indigoSetOptionXY(option, x, y)); } public void setOption(String option, float r, float g, float b) { setSessionID(); checkResult(this, _lib.indigoSetOptionColor(option, r, g, b)); } public void setOption(String option, boolean value) { setSessionID(); checkResult(this, _lib.indigoSetOptionBool(option, value ? 1 : 0)); } public void setOption(String option, float value) { setSessionID(); checkResult(this, _lib.indigoSetOptionFloat(option, value)); } public void setOption(String option, double value) { setSessionID(); checkResult(this, _lib.indigoSetOptionFloat(option, (float) value)); } public IndigoObject writeFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoWriteFile(filename))); } public IndigoObject writeBuffer() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoWriteBuffer())); } public IndigoObject createMolecule() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateMolecule())); } public IndigoObject createQueryMolecule() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateQueryMolecule())); } public IndigoObject loadMolecule(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadMoleculeFromString(str))); } public IndigoObject loadMolecule(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadMoleculeFromBuffer(buf, buf.length))); } public IndigoObject loadMoleculeFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadMoleculeFromFile(path))); } public IndigoObject loadQueryMolecule(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryMoleculeFromString(str))); } public IndigoObject loadQueryMolecule(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryMoleculeFromBuffer(buf, buf.length))); } public IndigoObject loadQueryMoleculeFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryMoleculeFromFile(path))); } public IndigoObject loadSmarts(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadSmartsFromString(str))); } public IndigoObject loadSmarts(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadSmartsFromBuffer(buf, buf.length))); } public IndigoObject loadSmartsFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadSmartsFromFile(path))); } public IndigoObject loadReaction(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionFromString(str))); } public IndigoObject loadReaction(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionFromBuffer(buf, buf.length))); } public IndigoObject loadReactionFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionFromFile(path))); } public IndigoObject loadQueryReaction(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryReactionFromString(str))); } public IndigoObject loadQueryReaction(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryReactionFromBuffer(buf, buf.length))); } public IndigoObject loadQueryReactionFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadQueryReactionFromFile(path))); } public IndigoObject loadReactionSmarts(String str) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionSmartsFromString(str))); } public IndigoObject loadReactionSmarts(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionSmartsFromBuffer(buf, buf.length))); } public IndigoObject loadReactionSmartsFromFile(String path) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadReactionSmartsFromFile(path))); } public IndigoObject createReaction() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateReaction())); } public IndigoObject createQueryReaction() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateQueryReaction())); } public IndigoObject exactMatch(IndigoObject obj1, IndigoObject obj2, String flags) { setSessionID(); if (flags == null) flags = ""; IndigoObject[] parent = new IndigoObject[]{obj1, obj2}; int match = checkResult(this, parent, _lib.indigoExactMatch(obj1.self, obj2.self, flags)); if (match == 0) return null; return new IndigoObject(this, match, parent); } public IndigoObject exactMatch(IndigoObject obj1, IndigoObject obj2) { return exactMatch(obj1, obj2, ""); } public void setTautomerRule(int id, String beg, String end) { setSessionID(); checkResult(this, _lib.indigoSetTautomerRule(id, beg, end)); } public void removeTautomerRule(int id) { setSessionID(); checkResult(this, _lib.indigoRemoveTautomerRule(id)); } public void clearTautomerRules() { setSessionID(); checkResult(this, _lib.indigoClearTautomerRules()); } public float similarity(IndigoObject obj1, IndigoObject obj2) { return similarity(obj1, obj2, ""); } public float similarity(IndigoObject obj1, IndigoObject obj2, String metrics) { if (metrics == null) metrics = ""; setSessionID(); Object[] guard = new Object[]{this, obj1, obj2}; return checkResultFloat(guard, _lib.indigoSimilarity(obj1.self, obj2.self, metrics)); } public int commonBits(IndigoObject fingerprint1, IndigoObject fingerprint2) { setSessionID(); Object[] guard = new Object[]{this, fingerprint1, fingerprint2}; return checkResult(guard, _lib.indigoCommonBits(fingerprint1.self, fingerprint2.self)); } public IndigoObject unserialize(byte[] data) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoUnserialize(data, data.length))); } public IndigoObject createArray() { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateArray())); } public IndigoObject iterateSDFile(String filename) { setSessionID(); int result = checkResult(this, _lib.indigoIterateSDFile(filename)); if (result == 0) return null; return new IndigoObject(this, result); } public IndigoObject iterateRDFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoIterateRDFile(filename))); } public IndigoObject iterateSmilesFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoIterateSmilesFile(filename))); } public IndigoObject iterateCMLFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoIterateCMLFile(filename))); } public IndigoObject iterateCDXFile(String filename) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoIterateCDXFile(filename))); } public IndigoObject substructureMatcher(IndigoObject target, String mode) { setSessionID(); return new IndigoObject(this, checkResult(this, target, _lib.indigoSubstructureMatcher(target.self, mode)), target); } public IndigoObject substructureMatcher(IndigoObject target) { return substructureMatcher(target, ""); } public IndigoObject extractCommonScaffold(IndigoObject structures, String options) { setSessionID(); int res = checkResult(this, structures, _lib.indigoExtractCommonScaffold(structures.self, options)); if (res == 0) return null; return new IndigoObject(this, res); } public IndigoObject extractCommonScaffold(Collection structures, String options) { return extractCommonScaffold(toIndigoArray(structures), options); } /** * Use createDecomposer() and decomposeMolecule() */ @Deprecated public IndigoObject decomposeMolecules(IndigoObject scaffold, IndigoObject structures) { setSessionID(); Object[] guard = new Object[]{this, scaffold, structures}; int res = checkResult(guard, _lib.indigoDecomposeMolecules(scaffold.self, structures.self)); if (res == 0) return null; return new IndigoObject(this, res); } /** * Use createDecomposer() and decomposeMolecule() */ @Deprecated public IndigoObject decomposeMolecules(IndigoObject scaffold, Collection structures) { return decomposeMolecules(scaffold, toIndigoArray(structures)); } public IndigoObject createDecomposer(IndigoObject scaffold) { setSessionID(); Object[] guard = new Object[]{this, scaffold}; int res = checkResult(guard, _lib.indigoCreateDecomposer(scaffold.self)); if (res == 0) return null; return new IndigoObject(this, res); } public IndigoObject reactionProductEnumerate(IndigoObject reaction, IndigoObject monomers) { setSessionID(); Object[] guard = new Object[]{this, reaction, monomers}; int res = checkResult(guard, _lib.indigoReactionProductEnumerate(reaction.self, monomers.self)); if (res == 0) return null; return new IndigoObject(this, res); } public IndigoObject reactionProductEnumerate(IndigoObject reaction, Iterable monomers) { setSessionID(); Object[] guard = new Object[]{this, reaction, monomers}; IndigoObject monomersArrayArray = createArray(); for (Iterable iter: monomers) { IndigoObject monomersArray = createArray(); for (IndigoObject monomer: iter) { monomersArray.arrayAdd(monomer); } monomersArrayArray.arrayAdd(monomersArray); } int res = checkResult(guard, _lib.indigoReactionProductEnumerate(reaction.self, monomersArrayArray.self)); if (res == 0) return null; return new IndigoObject(this, res); } public void transform(IndigoObject reaction, IndigoObject monomer) { setSessionID(); Object[] guard = new Object[]{this, reaction, monomer}; checkResult(guard, _lib.indigoTransform(reaction.self, monomer.self)); } public IndigoObject createSaver(IndigoObject output, String format) { setSessionID(); return new IndigoObject(this, checkResult(this, output, _lib.indigoCreateSaver(output.self, format)), output); } public IndigoObject createFileSaver(String filename, String format) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoCreateFileSaver(filename, format))); } public void dbgBreakpoint() { setSessionID(); _lib.indigoDbgBreakpoint(); } public IndigoObject toIndigoArray(Collection coll) { setSessionID(); IndigoObject arr = createArray(); for (IndigoObject obj : coll) arr.arrayAdd(obj); return arr; } public String getUserSpecifiedPath() { return _path; } public long getSid() { return _sid; } public IndigoObject loadBuffer(byte[] buf) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadBuffer(buf, buf.length))); } public IndigoObject loadString(String string) { setSessionID(); return new IndigoObject(this, checkResult(this, _lib.indigoLoadString(string))); } public IndigoObject iterateSDF(IndigoObject reader) { setSessionID(); int result = checkResult(this, _lib.indigoIterateSDF(reader.self)); if (result == 0) return null; return new IndigoObject(this, result, reader); } public IndigoObject iterateRDF(IndigoObject reader) { setSessionID(); int result = checkResult(this, _lib.indigoIterateRDF(reader.self)); if (result == 0) return null; return new IndigoObject(this, result, reader); } public IndigoObject iterateCML(IndigoObject reader) { setSessionID(); int result = checkResult(this, _lib.indigoIterateCML(reader.self)); if (result == 0) return null; return new IndigoObject(this, result, reader); } public IndigoObject iterateCDX(IndigoObject reader) { setSessionID(); int result = checkResult(this, _lib.indigoIterateCDX(reader.self)); if (result == 0) return null; return new IndigoObject(this, result, reader); } public IndigoObject iterateSmiles(IndigoObject reader) { setSessionID(); int result = checkResult(this, _lib.indigoIterateSmiles(reader.self)); if (result == 0) return null; return new IndigoObject(this, result, reader); } public IndigoObject iterateTautomers(IndigoObject molecule, String params) { setSessionID(); int result = checkResult(this, _lib.indigoIterateTautomers(molecule.self, params)); if (result == 0) return null; return new IndigoObject(this, result, molecule); } public int buildPkaModel(int level, float threshold, String filename) { setSessionID(); return checkResult(this, _lib.indigoBuildPkaModel(level, threshold, filename)); } @Override protected void finalize() throws Throwable { if (!sessionReleased()) { _lib.indigoReleaseSessionId(_sid); _session_released = true; } super.finalize(); } } Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/indigo/IndigoException.java000066400000000000000000000032571271037650300272420ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; public class IndigoException extends RuntimeException { Object obj; // You may wonder what object we are keeping here and why. // Here is the answer: we are keeping the object that raised the exception, // for prohibiting the garbage collector to destroy it while the method // is running. // Here is an example: // { // IndigoObject object = ...; // object.someMethod(); // does a native call // } // In this situation, the JVM is perfectly OK with deleting the object // *WHILE THE NATIVE CALL IS STILL RUNNING*. At least, it happened // on 64-bit Windows Server 2008 within KNIME. // To prevent that, we keep a reference to the object in the IndigoException // object (which can be thrown from every Indigo or IndigoObject method). // As long as the JVM sees that the reference is still used somewhere // afterwards the method call, it does not garbage-collect the object. public IndigoException (Object obj_, String message) { super(message); obj = obj_; } } Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/indigo/IndigoLib.java000066400000000000000000000426521271037650300260140ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Library; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; public interface IndigoLib extends Library { String indigoVersion (); long indigoAllocSessionId (); void indigoSetSessionId (long id); void indigoReleaseSessionId (long id); String indigoGetLastError (); int indigoFree (int handle); int indigoClone (int handle); int indigoCountReferences (); int indigoSetOption (String name, String value); int indigoSetOptionInt (String name, int value); int indigoSetOptionBool (String name, int value); int indigoSetOptionFloat (String name, float value); int indigoSetOptionColor (String name, float r, float g, float b); int indigoSetOptionXY (String name, int x, int y); int indigoReadFile (String filename); int indigoLoadString (String str); int indigoLoadBuffer (byte[] buf, int size); int indigoWriteFile (String filename); int indigoWriteBuffer (); int indigoClose (int handle); int indigoNext (int iter); int indigoHasNext (int iter); int indigoIndex (int item); int indigoRemove (int item); int indigoCreateMolecule (); int indigoCreateQueryMolecule (); int indigoLoadMolecule (int source); int indigoLoadMoleculeFromString (String str); int indigoLoadMoleculeFromFile (String filename); int indigoLoadMoleculeFromBuffer (byte[] buffer, int size); int indigoLoadQueryMolecule (int source); int indigoLoadQueryMoleculeFromString (String str); int indigoLoadQueryMoleculeFromFile (String filename); int indigoLoadQueryMoleculeFromBuffer (byte[] buffer, int size); int indigoLoadSmarts (int source); int indigoLoadSmartsFromString (String str); int indigoLoadSmartsFromFile (String filename); int indigoLoadSmartsFromBuffer (byte[] buffer, int size); int indigoSaveMolfile (int molecule, int output); int indigoSaveMolfileToFile (int molecule, String filename); Pointer indigoMolfile (int molecule); int indigoSaveCml (int object, int output); int indigoSaveCmlToFile (int object, String filename); Pointer indigoCml (int object); int indigoSaveMDLCT (int item, int output); int indigoLoadReaction (int source); int indigoLoadReactionFromString (String string); int indigoLoadReactionFromFile (String filename); int indigoLoadReactionFromBuffer (byte[] buf, int size); int indigoLoadQueryReaction (int source); int indigoLoadQueryReactionFromString (String str); int indigoLoadQueryReactionFromFile (String filename); int indigoLoadQueryReactionFromBuffer (byte[] buf, int size); int indigoLoadReactionSmarts (int source); int indigoLoadReactionSmartsFromString (String str); int indigoLoadReactionSmartsFromFile (String filename); int indigoLoadReactionSmartsFromBuffer (byte[] buf, int size); int indigoCreateReaction (); int indigoCreateQueryReaction (); int indigoAddReactant (int reaction, int molecule); int indigoAddProduct (int reaction, int molecule); int indigoAddCatalyst (int reaction, int molecule); int indigoCountReactants (int reaction); int indigoCountProducts (int reaction); int indigoCountCatalysts (int reaction); int indigoCountMolecules (int reaction); int indigoIterateReactants (int reaction); int indigoIterateProducts (int reaction); int indigoIterateCatalysts (int reaction); int indigoIterateMolecules (int reaction); int indigoSaveRxnfile (int reaction, int output); int indigoSaveRxnfileToFile (int reaction, String filename); Pointer indigoRxnfile (int reaction); int indigoOptimize (int query, String options); int indigoNormalize (int structure, String options); int indigoStandardize (int item); int indigoIonize(int item, float pH, float pH_toll); Pointer indigoGetAcidPkaValue(int item, int atom, int level, int min_level); Pointer indigoGetBasicPkaValue(int item, int atom, int level, int min_level); int indigoBuildPkaModel(int level, float theshold, String filename); int indigoAutomap (int reaction, String mode); int indigoGetAtomMappingNumber (int reaction, int reaction_atom); int indigoSetAtomMappingNumber (int reaction, int reaction_atom, int number); int indigoClearAAM (int reaction); int indigoCorrectReactingCenters (int reaction); int indigoIterateAtoms (int molecule); int indigoIteratePseudoatoms (int molecule); int indigoIterateRSites (int molecule); int indigoIterateStereocenters (int molecule); int indigoIterateAlleneCenters (int molecule); int indigoIterateRGroups (int molecule); int indigoIsPseudoatom (int atom); int indigoIsRSite (int atom); int indigoStereocenterType (int atom); int indigoStereocenterGroup (int atom); Pointer indigoStereocenterPyramid (int atom); int indigoSetStereocenterGroup (int atom, int group); int indigoChangeStereocenterType (int atom, int type); int indigoSingleAllowedRGroup (int rsite); int indigoAddStereocenter (int atom, int type, int v1, int v2, int v3, int v4); int indigoIterateRGroupFragments (int rgroup); int indigoCountAttachmentPoints (int rgroup); Pointer indigoSymbol (int atom); int indigoDegree (int atom); int indigoGetCharge (int atom, IntByReference charge); int indigoGetRadical (int atom, IntByReference radical); int indigoGetExplicitValence (int atom, IntByReference valence); int indigoGetRadicalElectrons (int atom, IntByReference electrons); int indigoAtomicNumber (int atom); int indigoIsotope (int atom); int indigoValence (int atom); int indigoCountHydrogens (int atom, IntByReference valence); int indigoCountImplicitHydrogens (int item); int indigoGetReactingCenter (int reaction, int reaction_bond, IntByReference rc); int indigoSetReactingCenter (int reaction, int reaction_bond, int rc); Pointer indigoXYZ (int atom); int indigoSetXYZ (int atom, float x, float y, float z); int indigoCountSuperatoms (int molecule); int indigoCountDataSGroups (int molecule); int indigoCountRepeatingUnits (int molecule); int indigoCountMultipleGroups (int molecule); int indigoCountGenericSGroups (int molecule); int indigoIterateDataSGroups (int molecule); int indigoIterateSuperatoms (int molecule); int indigoIterateGenericSGroups (int molecule); int indigoIterateRepeatingUnits (int molecule); int indigoIterateMultipleGroups (int molecule); int indigoGetSuperatom (int molecule, int index); int indigoGetDataSGroup (int molecule, int index); int indigoGetGenericSGroup (int molecule, int index); int indigoGetMultipleGroup (int molecule, int index); int indigoGetRepeatingUnit (int molecule, int index); Pointer indigoDescription (int data_sgroup); Pointer indigoData (int data_sgroup); int indigoAddDataSGroup (int molecule, int natoms, int atoms[], int nbonds, int bonds[], String description, String data); int indigoSetDataSGroupXY (int sgroup, float x, float y, String options); int indigoCreateSGroup (String type, int mapping, String name); int indigoSetSGroupClass (int sgroup, String sgclass); int indigoSetSGroupName (int sgroup, String sgname); Pointer indigoGetSGroupClass(int sgroup); Pointer indigoGetSGroupName(int sgroup); int indigoGetSGroupNumCrossBonds (int sgroup); int indigoAddSGroupAttachmentPoint (int sgroup, int aidx, int lvidx, String apid); int indigoDeleteSGroupAttachmentPoint (int sgroup, int apidx); int indigoGetSGroupDisplayOption (int sgroup); int indigoSetSGroupDisplayOption (int sgroup, int option); int indigoGetSGroupMultiplier (int sgroup); int indigoSetSGroupMultiplier (int sgroup, int mult); int indigoSetSGroupBrackets(int sgroup, int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); int indigoSetSGroupData (int sgroup, String data); int indigoSetSGroupCoords (int sgroup, float x, float y); int indigoSetSGroupDescription (int sgroup, String description); int indigoSetSGroupFieldName (int sgroup, String name); int indigoSetSGroupQueryCode (int sgroup, String querycode); int indigoSetSGroupQueryOper (int sgroup, String queryoper); int indigoSetSGroupDisplay (int sgroup, String option); int indigoSetSGroupLocation (int sgroup, String option); int indigoSetSGroupTag (int sgroup, String tag); int indigoSetSGroupTagAlign (int sgroup, int tag_align); int indigoSetSGroupDataType (int sgroup, String type); int indigoSetSGroupXCoord (int sgroup, float x); int indigoSetSGroupYCoord (int sgroup, float y); int indigoFindSGroups (int molecule, String property, String value); int indigoGetSGroupType (int sgroup); int indigoGetSGroupIndex (int sgroup); int indigoTransformSCSRtoCTAB (int molecule); int indigoTransformCTABtoSCSR (int molecule, int templates); int indigoResetCharge (int atom); int indigoResetExplicitValence (int atom); int indigoResetRadical (int atom); int indigoResetIsotope (int atom); int indigoSetAttachmentPoint (int atom, int order); int indigoClearAttachmentPoints (int item); int indigoIterateAttachmentPoints (int item, int order); int indigoRemoveConstraints (int item, String type); int indigoAddConstraint (int item, String type, String value); int indigoAddConstraintNot (int item, String type, String value); int indigoAddConstraintOr (int item, String type, String value); int indigoResetStereo (int item); int indigoInvertStereo (int item); int indigoCountAtoms (int molecule); int indigoCountBonds (int molecule); int indigoCountPseudoatoms (int molecule); int indigoCountRSites (int molecule); int indigoIterateBonds (int molecule); int indigoBondOrder (int bond); int indigoBondStereo (int bond); int indigoTopology (int bond); int indigoIterateNeighbors (int atom); int indigoBond (int nei); int indigoGetAtom (int molecule, int idx); int indigoGetBond (int molecule, int idx); int indigoGetMolecule (int reaction, int idx); int indigoSource (int bond); int indigoDestination (int bond); int indigoClearCisTrans (int handle); int indigoClearStereocenters (int handle); int indigoClearAlleneCenters (int handle); int indigoCountStereocenters (int molecule); int indigoCountAlleneCenters (int molecule); int indigoResetSymmetricCisTrans (int handle); int indigoResetSymmetricStereocenters (int handle); int indigoMarkEitherCisTrans (int handle); int indigoMarkStereobonds (int handle); int indigoValidateChirality (int handle); int indigoAddAtom (int molecule, String symbol); int indigoResetAtom (int atom, String symbol); int indigoAddRSite (int molecule, String name); int indigoSetRSite (int atom, String name); int indigoSetCharge (int atom, int charge); int indigoSetExplicitValence (int atom, int valence); int indigoSetIsotope (int atom, int isotope); int indigoSetImplicitHCount (int atom, int impl_h); int indigoSetRadical (int atom, int radical); int indigoAddBond (int source, int destination, int order); int indigoSetBondOrder (int bond, int order); int indigoMerge (int where_to, int what); int indigoHighlight (int atom); int indigoUnhighlight (int item); int indigoIsHighlighted (int item); int indigoCountComponents (int molecule); int indigoComponentIndex (int atom); int indigoIterateComponents (int molecule); int indigoComponent (int molecule, int index); int indigoCountSSSR (int molecule); int indigoIterateSSSR (int molecule); int indigoIterateSubtrees (int molecule, int min_atoms, int max_atoms); int indigoIterateRings (int molecule, int min_atoms, int max_atoms); int indigoIterateEdgeSubmolecules (int molecule, int min_bonds, int max_bonds); int indigoCountHeavyAtoms (int molecule); int indigoGrossFormula (int molecule); float indigoMolecularWeight (int molecule); float indigoMostAbundantMass (int molecule); float indigoMonoisotopicMass (int molecule); Pointer indigoCanonicalSmiles (int molecule); Pointer indigoLayeredCode (int molecule); Pointer indigoSymmetryClasses (int molecule, IntByReference count); int indigoHasCoord (int molecule); int indigoHasZCoord (int molecule); int indigoIsChiral (int molecule); int indigoIsPossibleFischerProjection (int molecule, String options); int indigoCreateSubmolecule (int molecule, int nvertices, int vertices[]); int indigoGetSubmolecule (int molecule, int nvertices, int vertices[]); int indigoCreateEdgeSubmolecule (int molecule, int nvertices, int vertices[], int nedges, int edges[]); int indigoRemoveAtoms (int molecule, int nvertices, int vertices[]); int indigoRemoveBonds (int molecule, int nbonds, int bonds[]); float indigoAlignAtoms (int molecule, int natoms, int atom_ids[], float desired_xyz[]); int indigoAromatize (int item); int indigoDearomatize (int item); int indigoFoldHydrogens (int item); int indigoUnfoldHydrogens (int item); int indigoLayout (int object); Pointer indigoSmiles (int item); int indigoExactMatch (int item1, int item2, String flags); int indigoSetTautomerRule (int id, String beg, String end); int indigoRemoveTautomerRule (int id); int indigoClearTautomerRules (); Pointer indigoName (int handle); int indigoSetName (int handle, String name); int indigoSerialize (int handle, PointerByReference buf, IntByReference size); int indigoUnserialize (byte[] buf, int size); int indigoHasProperty (int handle, String prop); Pointer indigoGetProperty (int handle, String prop); int indigoSetProperty (int item, String prop, String value); int indigoRemoveProperty (int item, String prop); int indigoIterateProperties (int handle); int indigoClearProperties (int handle); Pointer indigoCheckBadValence (int handle); Pointer indigoCheckAmbiguousH (int handle); int indigoFingerprint (int item, String type); int indigoCountBits (int fingerprint); int indigoCommonBits (int fingerprint1, int fingerprint2); float indigoSimilarity (int item1, int item2, String metrics); int indigoIterateSDF (int reader); int indigoIterateRDF (int reader); int indigoIterateSmiles (int reader); int indigoIterateCML (int reader); int indigoIterateCDX (int reader); int indigoIterateSDFile (String filename); int indigoIterateRDFile (String filename); int indigoIterateSmilesFile (String filename); int indigoIterateCMLFile (String filename); int indigoIterateCDXFile (String filename); Pointer indigoRawData (int item); int indigoTell (int handle); int indigoSdfAppend (int output, int item); int indigoSmilesAppend (int output, int item); int indigoRdfHeader (int output); int indigoRdfAppend (int output, int item); int indigoCmlHeader (int output); int indigoCmlAppend (int output, int item); int indigoCmlFooter (int output); int indigoCreateSaver (int output, String format); int indigoCreateFileSaver (String filename, String format); int indigoAppend (int saver, int object); int indigoCreateArray (); int indigoArrayAdd (int arr, int object); int indigoAt (int item, int index); int indigoCount (int item); int indigoClear (int arr); int indigoIterateArray (int arr); int indigoSubstructureMatcher (int target, String mode); int indigoIgnoreAtom (int matcher, int atom); int indigoUnignoreAtom (int matcher, int atom); int indigoUnignoreAllAtoms (int matcher); int indigoMatch (int matcher, int query); int indigoCountMatches (int matcher, int query); int indigoCountMatchesWithLimit (int matcher, int query, int embeddings_limit); int indigoIterateMatches (int matcher, int query); int indigoHighlightedTarget (int match); int indigoMapAtom (int match, int query_atom); int indigoMapBond (int match, int query_bond); int indigoMapMolecule (int match, int query_reaction_molecule); int indigoExtractCommonScaffold (int structures, String options); int indigoAllScaffolds (int extracted); int indigoDecomposeMolecules (int scaffold, int structures); int indigoDecomposedMoleculeScaffold (int decomp); int indigoIterateDecomposedMolecules (int decomp); int indigoDecomposedMoleculeHighlighted (int decomp); int indigoDecomposedMoleculeWithRGroups (int decomp); int indigoCreateDecomposer(int scaffold); int indigoDecomposeMolecule(int decomp, int mol); int indigoIterateDecompositions(int deco_item); int indigoAddDecomposition(int decomp, int q_match); Pointer indigoToString (int handle); int indigoToBuffer (int handle, PointerByReference buf, IntByReference size); int indigoReactionProductEnumerate (int reaction, int monomers); int indigoTransform (int reaction, int monomers); int indigoExpandAbbreviations (int structure); int indigoIterateTautomers(int structure, String params); int indigoDbgBreakpoint (); Pointer indigoDbgInternalType (int object); } Indigo-indigo-1.2.3/api/java/src/main/java/com/epam/indigo/IndigoObject.java000066400000000000000000001434411271037650300265120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; public class IndigoObject implements Iterator, Iterable { public int self; protected Indigo dispatcher; private Object parent; // should keep the parent so that GC will not remove it protected IndigoLib _lib; public IndigoObject (Indigo dispatcher, int id) { this.dispatcher = dispatcher; this.self = id; _lib = Indigo.getLibrary(); } public IndigoObject (Indigo dispatcher, int id, Object parent) { this.dispatcher = dispatcher; this.self = id; this.parent = parent; _lib = Indigo.getLibrary(); } public Indigo getIndigo () { return dispatcher; } public void dispose () { if (self >= 0) { dispatcher.setSessionID(); _lib.indigoFree(self); self = -1; } } @Override @SuppressWarnings("FinalizeDeclaration") protected void finalize () throws Throwable { if (!dispatcher.sessionReleased()) { dispose(); } super.finalize(); } @Override public IndigoObject clone () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoClone(self))); } public String molfile () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoMolfile(self)); } public void saveMolfile (String filename) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSaveMolfileToFile(self, filename)); } public String cml () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoCml(self)); } public void saveCml (String filename) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSaveCmlToFile(self, filename)); } public byte[] mdlct () { dispatcher.setSessionID(); IndigoObject buf = dispatcher.writeBuffer(); Indigo.checkResult(this, _lib.indigoSaveMDLCT(self, buf.self)); return buf.toBuffer(); } public void addReactant (IndigoObject molecule) { dispatcher.setSessionID(); Indigo.checkResult(this, molecule, _lib.indigoAddReactant(self, molecule.self)); } public void addProduct (IndigoObject molecule) { dispatcher.setSessionID(); Indigo.checkResult(this, molecule, _lib.indigoAddProduct(self, molecule.self)); } public void addCatalyst (IndigoObject molecule) { dispatcher.setSessionID(); Indigo.checkResult(this, molecule, _lib.indigoAddCatalyst(self, molecule.self)); } public int countReactants () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountReactants(self)); } public int countProducts () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountProducts(self)); } public int countCatalysts () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountCatalysts(self)); } public int countMolecules () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountMolecules(self)); } public IndigoObject iterateAttachmentPoints (int order) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateAttachmentPoints(self, order)), this); } public IndigoObject iterateReactants () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateReactants(self)), this); } public IndigoObject iterateProducts () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateProducts(self)), this); } public IndigoObject iterateCatalysts () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateCatalysts(self)), this); } public IndigoObject iterateMolecules () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateMolecules(self)), this); } public String rxnfile () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoRxnfile(self)); } public void saveRxnfile (String filename) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSaveRxnfileToFile(self, filename)); } public void automap () { automap(""); } public void automap (String mode) { if (mode == null) mode = ""; dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAutomap(self, mode)); } public int atomMappingNumber (IndigoObject reaction_atom) { dispatcher.setSessionID(); return Indigo.checkResult(this, reaction_atom, _lib.indigoGetAtomMappingNumber(self, reaction_atom.self)); } public void setAtomMappingNumber (IndigoObject reaction_atom, int number) { dispatcher.setSessionID(); Indigo.checkResult(this, reaction_atom, _lib.indigoSetAtomMappingNumber(self, reaction_atom.self, number)); } public void clearAAM () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearAAM(self)); } public void correctReactingCenters () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoCorrectReactingCenters(self)); } public IndigoObject iterateAtoms () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateAtoms(self)), this); } public IndigoObject iteratePseudoatoms () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIteratePseudoatoms(self)), this); } public IndigoObject iterateRSites () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateRSites(self)), this); } public IndigoObject iterateStereocenters () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateStereocenters(self)), this); } public IndigoObject iterateAlleneCenters () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateAlleneCenters(self)), this); } public IndigoObject iterateRGroups () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateRGroups(self)), this); } public IndigoObject iterateRGroupFragments () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateRGroupFragments(self)), this); } public int countAttachmentPoints () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountAttachmentPoints(self)); } public boolean isPseudoatom () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIsPseudoatom(self)) == 1; } public boolean isRSite () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIsRSite(self)) == 1; } public int stereocenterType () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoStereocenterType(self)); } public int stereocenterGroup () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoStereocenterGroup(self)); } public int[] stereocenterPyramid () { dispatcher.setSessionID(); Pointer ptr = Indigo.checkResultPointer(this, _lib.indigoStereocenterPyramid(self)); return ptr.getIntArray(0, 4); } public void changeStereocenterType (int type) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoChangeStereocenterType(self, type)); } public void setStereocenterGroup (int group) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetStereocenterGroup(self, group)); } public int singleAllowedRGroup () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSingleAllowedRGroup(self)); } public String symbol () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoSymbol(self)); } public int degree () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoDegree(self)); } public Integer charge () { IntByReference res = new IntByReference(); dispatcher.setSessionID(); if (Indigo.checkResult(this, _lib.indigoGetCharge(self, res)) == 1) return res.getValue(); return null; } public int reactingCenter (IndigoObject bond) { dispatcher.setSessionID(); IntByReference res = new IntByReference(); if (Indigo.checkResult(this, bond, _lib.indigoGetReactingCenter(self, bond.self, res)) == 1) return res.getValue(); throw new IndigoException(this, "reactingCenter(): unexpected result"); } public void setReactingCenter (IndigoObject bond, int type) { dispatcher.setSessionID(); Indigo.checkResult(this, bond, _lib.indigoSetReactingCenter(self, bond.self, type)); } public Integer explicitValence () { IntByReference res = new IntByReference(); dispatcher.setSessionID(); if (Indigo.checkResult(this, _lib.indigoGetExplicitValence(self, res)) == 1) return res.getValue(); return null; } public Integer radicalElectrons () { IntByReference res = new IntByReference(); dispatcher.setSessionID(); if (Indigo.checkResult(this, _lib.indigoGetRadicalElectrons(self, res)) == 1) return res.getValue(); return null; } public Integer radical () { IntByReference res = new IntByReference(); dispatcher.setSessionID(); if (Indigo.checkResult(this, _lib.indigoGetRadical(self, res)) == 1) return res.getValue(); return null; } public int atomicNumber () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoAtomicNumber(self)); } public int isotope () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIsotope(self)); } public int valence () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoValence(self)); } public Integer countHydrogens () { IntByReference res = new IntByReference(); dispatcher.setSessionID(); if (Indigo.checkResult(this, _lib.indigoCountHydrogens(self, res)) == 1) return res.getValue(); return null; } public int countImplicitHydrogens () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountImplicitHydrogens(self)); } public void resetCharge () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoResetCharge(self)); } public void resetExplicitValence () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoResetExplicitValence(self)); } public void resetRadical () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoResetRadical(self)); } public void resetIsotope () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoResetIsotope(self)); } public void setAttachmentPoint (int order) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetAttachmentPoint(self, order)); } public void clearAttachmentPoints () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearAttachmentPoints(self)); } public void removeConstraints (String type) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRemoveConstraints(self, type)); } public void addConstraint (String type, String value) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAddConstraint(self, type, value)); } public void addConstraintNot (String type, String value) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAddConstraintNot(self, type, value)); } public void addConstraintOr (String type, String value) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAddConstraintOr(self, type, value)); } public void resetStereo () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoResetStereo(self)); } public void invertStereo () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoInvertStereo(self)); } public int countAtoms () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountAtoms(self)); } public int countBonds () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountBonds(self)); } public int countPseudoatoms () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountPseudoatoms(self)); } public int countRSites () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountRSites(self)); } public IndigoObject iterateBonds () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateBonds(self)), this); } public int bondOrder () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoBondOrder(self)); } public int bondStereo () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoBondStereo(self)); } public int topology () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoTopology(self)); } public IndigoObject iterateNeighbors () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateNeighbors(self)), this); } public IndigoObject bond () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoBond(self))); } public IndigoObject getAtom (int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetAtom(self, idx)), this); } public IndigoObject getMolecule (int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetMolecule(self, idx)), this); } public IndigoObject getBond (int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetBond(self, idx)), this); } public IndigoObject source () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoSource(self))); } public IndigoObject destination () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoDestination(self))); } public void clearCisTrans () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearCisTrans(self)); } public void clearStereocenters () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearStereocenters(self)); } public void clearAlleneCenters () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearAlleneCenters(self)); } public int countStereocenters () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountStereocenters(self)); } public int countAlleneCenters () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountAlleneCenters(self)); } public int resetSymmetricCisTrans () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoResetSymmetricCisTrans(self)); } public int resetSymmetricStereocenters () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoResetSymmetricStereocenters(self)); } public int markEitherCisTrans () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoMarkEitherCisTrans(self)); } public int markStereobonds () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoMarkStereobonds(self)); } public int validateChirality () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoValidateChirality(self)); } public IndigoObject addAtom (String symbol) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAddAtom(self, symbol)), this); } public IndigoObject resetAtom (String symbol) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoResetAtom(self, symbol)), this); } public IndigoObject addRSite (String name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAddRSite(self, name)), this); } public IndigoObject setRSite (String name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoSetRSite(self, name)), this); } public void setCharge (int charge) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetCharge(self, charge)); } public void setRadical (int radical) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetRadical(self, radical)); } public void setExplicitValence (int valence) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetExplicitValence(self, valence)); } public void setIsotope (int isotope) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetIsotope(self, isotope)); } public void setImplicitHCount (int hcount) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetImplicitHCount(self, hcount)); } public IndigoObject addBond (IndigoObject atom, int order) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAddBond(self, atom.self, order)), atom); } public void setBondOrder (int order) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetBondOrder(self, order)); }; public IndigoObject merge (IndigoObject other) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, other, _lib.indigoMerge(self, other.self)), this); } public void highlight () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoHighlight(self)); } public void unhighlight () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoUnhighlight(self)); } public boolean isHighlighted () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIsHighlighted(self)) == 1; } public int countComponents () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountComponents(self)); } public int componentIndex () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoComponentIndex(self)); } public IndigoObject iterateComponents () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateComponents(self)), this); } public IndigoObject component (int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoComponent(self, index)), this); } public int countSSSR () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountSSSR(self)); } public IndigoObject iterateSSSR () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateSSSR(self)), this); } public IndigoObject iterateSubtrees (int min_vertices, int max_vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateSubtrees(self, min_vertices, max_vertices)), this); } public IndigoObject iterateRings (int min_vertices, int max_vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateRings(self, min_vertices, max_vertices)), this); } public IndigoObject iterateEdgeSubmolecules (int min_edges, int max_edges) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateEdgeSubmolecules(self, min_edges, max_edges)), this); } public int countHeavyAtoms () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountHeavyAtoms(self)); } public String grossFormula () { dispatcher.setSessionID(); int gf = -1; try { gf = Indigo.checkResult(this, _lib.indigoGrossFormula(self)); String result = Indigo.checkResultString(this, _lib.indigoToString(gf)); return result; } finally { _lib.indigoFree(gf); } } public float molecularWeight () { dispatcher.setSessionID(); return Indigo.checkResultFloat(this, _lib.indigoMolecularWeight(self)); } public float mostAbundantMass() { dispatcher.setSessionID(); return Indigo.checkResultFloat(this, _lib.indigoMostAbundantMass(self)); } public float monoisotopicMass() { dispatcher.setSessionID(); return Indigo.checkResultFloat(this, _lib.indigoMonoisotopicMass(self)); } public String canonicalSmiles() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoCanonicalSmiles(self)); } public String layeredCode() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoLayeredCode(self)); } public boolean hasCoord() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoHasCoord(self)) == 1; } public boolean hasZCoord() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoHasZCoord(self)) == 1; } public boolean isChiral() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIsChiral(self)) == 1; } public boolean isPossibleFischerProjection(String options) { dispatcher.setSessionID(); if (options == null) options = ""; return Indigo.checkResult(this, _lib.indigoIsPossibleFischerProjection(self, options)) == 1; } public float[] xyz() { dispatcher.setSessionID(); Pointer ptr = Indigo.checkResultPointer(this, _lib.indigoXYZ(self)); return ptr.getFloatArray(0, 3); } public void setXYZ (float x, float y, float z) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetXYZ(self, x, y, z)); } public void setXYZ (float[] xyz) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetXYZ(self, xyz[0], xyz[1], xyz[2])); } public int countSuperatoms() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountSuperatoms(self)); } public int countDataSGroups() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountDataSGroups(self)); } public int countRepeatingUnits() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountRepeatingUnits(self)); } public int countMultipleGroups() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountMultipleGroups(self)); } public int countGenericSGroups() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountGenericSGroups(self)); } public IndigoObject iterateSuperatoms() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateSuperatoms(self)), this); } public IndigoObject iterateDataSGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateDataSGroups(self)), this); } public IndigoObject iterateRepeatingUnits() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateRepeatingUnits(self)), this); } public IndigoObject iterateMultipleGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateMultipleGroups(self)), this); } public IndigoObject iterateGenericSGroups() { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateGenericSGroups(self)), this); } public IndigoObject getDataSGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetDataSGroup(self, index)), this); } public IndigoObject getSuperatom(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetSuperatom(self, index)), this); } public IndigoObject getGenericSGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetGenericSGroup(self, index)), this); } public IndigoObject getMultipleGroup(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetMultipleGroup(self, index)), this); } public IndigoObject getRepeatingUnit(int index) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetRepeatingUnit(self, index)), this); } public String description() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoDescription(self)); } public String data() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoData(self)); } public IndigoObject addDataSGroup (int[] atoms, int[] bonds, String description, String data) { dispatcher.setSessionID(); if (description == null) description = ""; if (data == null) data = ""; return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAddDataSGroup(self, atoms.length, atoms, bonds.length, bonds, description, data)), this); } public IndigoObject addDataSGroup (Collection atoms, Collection bonds, String description, String data) { return addDataSGroup(Indigo.toIntArray(atoms), Indigo.toIntArray(bonds), description, data); } public IndigoObject createSGroup (String type, IndigoObject mapping, String name) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoCreateSGroup(type, mapping.self, name)), this); } public void setSGroupClass(String sgclass) { dispatcher.setSessionID(); if (sgclass == null) sgclass = ""; Indigo.checkResult(this, _lib.indigoSetSGroupClass(self, sgclass)); } public void setSGroupName(String sgname) { dispatcher.setSessionID(); if (sgname == null) sgname = ""; Indigo.checkResult(this, _lib.indigoSetSGroupName(self, sgname)); } public String getSGroupClass() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoGetSGroupClass(self)); } public String getSGroupName() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoGetSGroupName(self)); } public int getSGroupNumCrossBonds() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoGetSGroupNumCrossBonds(self)); } public int addSGroupAttachmentPoint(int aidx, int lvidx, String apid) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoAddSGroupAttachmentPoint(self, aidx, lvidx, apid)); } public int deleteSGroupAttachmentPoint(int apidx) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoDeleteSGroupAttachmentPoint(self, apidx)); } public int getSGroupDisplayOption() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoGetSGroupDisplayOption(self)); } public int setSGroupDisplayOption(int option) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupDisplayOption(self, option)); } public int getSGroupMultiplier() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoGetSGroupMultiplier(self)); } public int setSGroupMultiplier(int mult) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupMultiplier(self, mult)); } public int setSGroupData (String data) { dispatcher.setSessionID(); if (data == null) data = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupData(self, data)); } public int setSGroupCoords (float x, float y) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupCoords(self, x, y)); } public int setSGroupDescription (String description) { dispatcher.setSessionID(); if (description == null) description = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupDescription(self, description)); } public int setSGroupFieldName (String name) { dispatcher.setSessionID(); if (name == null) name = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupFieldName(self, name)); } public int setSGroupQueryCode (String querycode) { dispatcher.setSessionID(); if (querycode == null) querycode = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupQueryCode(self, querycode)); } public int setSGroupQueryOper (String queryoper) { dispatcher.setSessionID(); if (queryoper == null) queryoper = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupQueryOper(self, queryoper)); } public int setSGroupDisplay (String option) { dispatcher.setSessionID(); if (option == null) option = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupDisplay(self, option)); } public int setSGroupLocation (String option) { dispatcher.setSessionID(); if (option == null) option = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupLocation(self, option)); } public int setSGroupTag (String tag) { dispatcher.setSessionID(); if (tag == null) tag = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupTag(self, tag)); } public int setSGroupTagAlign (int tag_align) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupTagAlign(self, tag_align)); } public int setSGroupDataType (String data_type) { dispatcher.setSessionID(); if (data_type == null) data_type = ""; return Indigo.checkResult(this, _lib.indigoSetSGroupDataType(self, data_type)); } public int setSGroupXCoord (float x) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupXCoord(self, x)); } public int setSGroupYCoord (float y) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupYCoord(self, y)); } public IndigoObject findSGroups (String property, String value) { dispatcher.setSessionID(); if (property == null) property = ""; if (value == null) value = ""; return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoFindSGroups(self, property, value)), this); } public int getSGroupType() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoGetSGroupType(self)); } public int getSGroupIndex() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoGetSGroupIndex(self)); } public int transformSCSRtoCTAB() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoTransformSCSRtoCTAB(self)); } public int transformCTABtoSCSR(IndigoObject templates) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoTransformCTABtoSCSR(self, templates.self)); } public int setSGroupBrackets(int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoSetSGroupBrackets(self, brk_style, x1, y1, x2, y2, x3, y3, x4, y4)); } public void setDataSGroupXY(float x, float y) { setDataSGroupXY(x, y, ""); } public void setDataSGroupXY(float x, float y, String options) { dispatcher.setSessionID(); if (options == null) options = ""; Indigo.checkResult(this, _lib.indigoSetDataSGroupXY(self, x, y, options)); } public void addStereocenter (int type, int v1, int v2, int v3) { addStereocenter(type, v1, v2, v3, -1); } public void addStereocenter (int type, int v1, int v2, int v3, int v4) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAddStereocenter(self, type, v1, v2, v3, v4)); } public IndigoObject createSubmolecule (int[] vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoCreateSubmolecule(self, vertices.length, vertices))); } public IndigoObject createSubmolecule (Collection vertices) { return createSubmolecule(Indigo.toIntArray(vertices)); } public IndigoObject getSubmolecule (int[] vertices) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoGetSubmolecule(self, vertices.length, vertices))); } public IndigoObject getSubmolecule (Collection vertices) { return getSubmolecule(Indigo.toIntArray(vertices)); } public IndigoObject createEdgeSubmolecule (int[] vertices, int[] edges) { return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoCreateEdgeSubmolecule(self, vertices.length, vertices, edges.length, edges))); } public IndigoObject createEdgeSubmolecule (Collection vertices, Collection edges) { return createEdgeSubmolecule(Indigo.toIntArray(vertices), Indigo.toIntArray(edges)); } public void removeAtoms (int[] vertices) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRemoveAtoms(self, vertices.length, vertices)); } public void removeAtoms (Collection vertices) { removeAtoms(Indigo.toIntArray(vertices)); } public void removeBonds (int[] bonds) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRemoveBonds(self, bonds.length, bonds)); } public void removeBonds (Collection bonds) { removeBonds(Indigo.toIntArray(bonds)); } public float alignAtoms (int[] atom_ids, float[] desired_xyz) { if (atom_ids.length * 3 != desired_xyz.length) throw new IndigoException(this, "desired_xyz[] must be exactly 3 times bigger than atom_ids[]"); dispatcher.setSessionID(); return Indigo.checkResultFloat(this, _lib.indigoAlignAtoms(self, atom_ids.length, atom_ids, desired_xyz)); } public float alignAtoms (Collection atom_ids, Collection desired_xyz) { return alignAtoms(Indigo.toIntArray(atom_ids), Indigo.toFloatArray(desired_xyz)); } public void aromatize () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAromatize(self)); } public void dearomatize () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoDearomatize(self)); } public void foldHydrogens() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoFoldHydrogens(self)); } public void unfoldHydrogens() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoUnfoldHydrogens(self)); } public void layout() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoLayout(self)); } public String smiles() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoSmiles(self)); } public String name() { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoName(self)); } public void setName(String name) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetName(self, name)); } public byte[] serialize () { PointerByReference ptr = new PointerByReference(); IntByReference size = new IntByReference(); dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSerialize(self, ptr, size)); Pointer p = ptr.getValue(); return p.getByteArray(0, size.getValue()); } public boolean hasProperty(String prop) { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoHasProperty(self, prop)) == 1; } public String getProperty(String prop) { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoGetProperty(self, prop)); } public void setProperty(String prop, String value) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoSetProperty(self, prop, value)); } public void removeProperty(String prop) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRemoveProperty(self, prop)); } public IndigoObject iterateProperties () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateProperties(self)), this); } public void clearProperties () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClearProperties(self)); } public String checkBadValence () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoCheckBadValence(self)); } public String checkAmbiguousH () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoCheckAmbiguousH(self)); } public IndigoObject fingerprint () { return fingerprint(""); } public IndigoObject fingerprint (String type) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoFingerprint(self, type))); } public int countBits () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCountBits(self)); } public String rawData () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoRawData(self)); } public int tell () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoTell(self)); } public void sdfAppend (IndigoObject item) { dispatcher.setSessionID(); Indigo.checkResult(this, item, _lib.indigoSdfAppend(self, item.self)); } public void smilesAppend (IndigoObject item) { dispatcher.setSessionID(); Indigo.checkResult(this, item, _lib.indigoSmilesAppend(self, item.self)); } public void rdfHeader() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRdfHeader(self)); } public void rdfAppend(IndigoObject item) { dispatcher.setSessionID(); Indigo.checkResult(this, item, _lib.indigoRdfAppend(self, item.self)); } public void cmlHeader() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoCmlHeader(self)); } public void cmlAppend(IndigoObject item) { dispatcher.setSessionID(); Indigo.checkResult(this, item, _lib.indigoCmlAppend(self, item.self)); } public void cmlFooter() { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoCmlFooter(self)); } public IndigoObject iterateArray () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateArray(self)), this); } public int count () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoCount(self)); } public void clear () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClear(self)); } public int arrayAdd (IndigoObject other) { dispatcher.setSessionID(); return Indigo.checkResult(this, other, _lib.indigoArrayAdd(self, other.self)); } public IndigoObject at (int idx) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAt(self, idx)), this); } public void ignoreAtom (IndigoObject atom) { dispatcher.setSessionID(); Indigo.checkResult(this, atom, _lib.indigoIgnoreAtom(self, atom.self)); } public void unignoreAtom (IndigoObject atom) { dispatcher.setSessionID(); Indigo.checkResult(this, atom, _lib.indigoUnignoreAtom(self, atom.self)); } public void unignoreAllAtoms () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoUnignoreAllAtoms(self)); } public IndigoObject match (IndigoObject query) { dispatcher.setSessionID(); int res = Indigo.checkResult(this, query, _lib.indigoMatch(self, query.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public int countMatches (IndigoObject query) { dispatcher.setSessionID(); return Indigo.checkResult(this, query, _lib.indigoCountMatches(self, query.self)); } public int countMatchesWithLimit (IndigoObject query, int embeddings_limit) { dispatcher.setSessionID(); return Indigo.checkResult(this, query, _lib.indigoCountMatchesWithLimit(self, query.self, embeddings_limit)); } public IndigoObject iterateMatches (IndigoObject query) { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, query, _lib.indigoIterateMatches(self, query.self)), this); } public IndigoObject highlightedTarget () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoHighlightedTarget(self))); } public IndigoObject mapAtom (IndigoObject query_atom) { dispatcher.setSessionID(); int res = Indigo.checkResult(this, query_atom, _lib.indigoMapAtom(self, query_atom.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public IndigoObject mapMolecule (IndigoObject query_reaction_molecule) { dispatcher.setSessionID(); int res = Indigo.checkResult(this, query_reaction_molecule, _lib.indigoMapMolecule(self, query_reaction_molecule.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public IndigoObject mapBond (IndigoObject query_bond) { Object[] guard = { this, query_bond }; dispatcher.setSessionID(); int res = Indigo.checkResult(this, query_bond, _lib.indigoMapBond(self, query_bond.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public IndigoObject allScaffolds () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoAllScaffolds(self))); } public IndigoObject decomposedMoleculeScaffold () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoDecomposedMoleculeScaffold(self))); } @Deprecated public IndigoObject iterateDecomposedMolecules () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoIterateDecomposedMolecules(self)), this); } public IndigoObject decomposedMoleculeHighlighted () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoDecomposedMoleculeHighlighted(self))); } public IndigoObject decomposedMoleculeWithRGroups () { dispatcher.setSessionID(); return new IndigoObject(dispatcher, Indigo.checkResult(this, _lib.indigoDecomposedMoleculeWithRGroups(self))); } public IndigoObject decomposeMolecule (IndigoObject mol) { dispatcher.setSessionID(); int res = Indigo.checkResult(this, _lib.indigoDecomposeMolecule(self, mol.self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public IndigoObject iterateDecompositions () { dispatcher.setSessionID(); int res = Indigo.checkResult(this, _lib.indigoIterateDecompositions(self)); if (res == 0) return null; return new IndigoObject(dispatcher, res, this); } public void addDecomposition (IndigoObject q_match) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoAddDecomposition(self, q_match.self)); } public Iterator iterator () { return this; } public void remove () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoRemove(self)); } public void close () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoClose(self)); } public IndigoObject next () throws NoSuchElementException { dispatcher.setSessionID(); int next = Indigo.checkResult(this, _lib.indigoNext(self)); if (next == 0) throw new NoSuchElementException("iterator has ended"); return new IndigoObject(dispatcher, next, this); } public boolean hasNext () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoHasNext(self)) == 1; } public int index () { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoIndex(self)); } @Override public String toString () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoToString(self)); } public byte[] toBuffer () { PointerByReference ptr = new PointerByReference(); IntByReference size = new IntByReference(); dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoToBuffer(self, ptr, size)); Pointer p = ptr.getValue(); return p.getByteArray(0, size.getValue()); } public int[] symmetryClasses () { IntByReference count = new IntByReference(); dispatcher.setSessionID(); Pointer p = Indigo.checkResultPointer(this, _lib.indigoSymmetryClasses(self, count)); return p.getIntArray(0, count.getValue()); } public void append (IndigoObject obj) { dispatcher.setSessionID(); Indigo.checkResult(this, obj, _lib.indigoAppend(self, obj.self)); } public void optimize () { optimize(null); } public void optimize (String options) { dispatcher.setSessionID(); if (options == null) options = ""; Indigo.checkResult(this, _lib.indigoOptimize(self, options)); } public boolean normalize () { return normalize(null); } public boolean normalize (String options) { dispatcher.setSessionID(); if (options == null) options = ""; return (Indigo.checkResult(this, _lib.indigoNormalize(self, options)) == 1); } public void standardize () { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoStandardize(self)); } public void ionize(float pH, float pHToll) { dispatcher.setSessionID(); Indigo.checkResult(this, _lib.indigoIonize(self, pH, pHToll)); } public float getAcidPkaValue(IndigoObject atom, int level, int min_level) { dispatcher.setSessionID(); Pointer ptr = Indigo.checkResultPointer(this, _lib.indigoGetAcidPkaValue(self, atom.self, level, min_level)); return ptr.getFloat(0); } public float getBasicPkaValue(IndigoObject atom, int level, int min_level) { dispatcher.setSessionID(); Pointer ptr = Indigo.checkResultPointer(this, _lib.indigoGetBasicPkaValue(self, atom.self, level, min_level)); return ptr.getFloat(0); } public int expandAbbreviations() { dispatcher.setSessionID(); return Indigo.checkResult(this, _lib.indigoExpandAbbreviations(self)); } public String dbgInternalType () { dispatcher.setSessionID(); return Indigo.checkResultString(this, _lib.indigoDbgInternalType(self)); } } Indigo-indigo-1.2.3/api/libs/000077500000000000000000000000001271037650300157035ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/libs/readme.txt000066400000000000000000000002021271037650300176730ustar00rootroot00000000000000This directory is used to get the binary files by Python, Java and .NET wrappers Simply extract the indigo-shared libs here: ...Indigo-indigo-1.2.3/api/make-dotnet-wrappers.py000066400000000000000000000153701271037650300214030ustar00rootroot00000000000000from optparse import OptionParser import os from os.path import join, abspath, dirname import re import shutil import subprocess if os.name == 'nt': msbuildcommand = 'msbuild /t:Rebuild /p:Configuration=Release' else: # Mono msbuildcommand = 'xbuild /t:Rebuild /p:Configuration=Release' parser = OptionParser(description='Indigo .NET libraries build script') parser.add_option('--suffix', '-s', help='archive suffix', default="") parser.add_option('--doc', default=False, action='store_true', help='Put documentation into the archive') (args, left_args) = parser.parse_args() wrappers = (args.suffix[1:], ) print(wrappers) if 'universal' in wrappers: wrappers = ('win', 'linux', 'mac') api_dir = abspath(dirname(__file__)) root = join(api_dir, "..") dist_dir = join(root, "dist") if not os.path.exists(dist_dir): os.mkdir(dist_dir) cur_dir = os.path.abspath(os.curdir) # Find indigo version from get_indigo_version import getIndigoVersion version = getIndigoVersion() os.chdir(dist_dir) if os.path.exists("dotnet"): shutil.rmtree("dotnet") os.mkdir('dotnet') libraryPath = join(api_dir, 'libs', 'shared') # Build Indigo-dotnet indigoDotNetPath = join(api_dir, "dotnet") if os.path.exists(join(indigoDotNetPath, "Resource")): shutil.rmtree(join(indigoDotNetPath, "Resource")) if 'win' in wrappers: os.makedirs(join(indigoDotNetPath, "Resource", 'Win', 'x64')) os.makedirs(join(indigoDotNetPath, "Resource", 'Win', 'x86')) if os.path.exists(join(libraryPath, 'Win', 'x64', 'msvcr100.dll')): win2010 = 1 win2012 = 0 win2013 = 0 elif os.path.exists(join(libraryPath, 'Win', 'x64', 'msvcr110.dll')): win2010 = 0 win2012 = 1 win2013 = 0 elif os.path.exists(join(libraryPath, 'Win', 'x64', 'msvcr120.dll')): win2010 = 0 win2012 = 0 win2013 = 1 else: win2010 = 0 win2012 = 0 win2013 = 0 else: win2010 = 0 win2012 = 0 win2013 = 0 if 'linux' in wrappers: os.makedirs(join(indigoDotNetPath, "Resource", 'Linux', 'x64')) os.makedirs(join(indigoDotNetPath, "Resource", 'Linux', 'x86')) linux = 1 else: linux = 0 if 'mac' in wrappers: #os.makedirs(join(indigoDotNetPath, "Resource", 'Mac', '10.5')) os.makedirs(join(indigoDotNetPath, "Resource", 'Mac', '10.7')) mac = 1 else: mac = 0 os.chdir(indigoDotNetPath) command = '%s /property:LibraryPath=%s /property:Win2010=%s /property:Win2012=%s /property:Win2013=%s /property:Linux=%s /property:Mac=%s /property:Copy=%s' % (msbuildcommand, libraryPath, win2010, win2012, win2013, linux, mac, 'copy' if os.name == 'nt' else 'cp') print(command) subprocess.check_call(command, shell=True) # Build IndigoRenderer-dotnet indigoRendererDotNetPath = join(api_dir, "plugins", "renderer", "dotnet") if os.path.exists(join(indigoRendererDotNetPath, "Resource")): shutil.rmtree(join(indigoRendererDotNetPath, "Resource")) if 'win' in wrappers: os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Win', 'x64')) os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Win', 'x86')) win = 1 else: win = 0 if 'linux' in wrappers: os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Linux', 'x64')) os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Linux', 'x86')) linux = 1 else: linux = 0 if 'mac' in wrappers: #os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Mac', '10.5')) os.makedirs(join(indigoRendererDotNetPath, "Resource", 'Mac', '10.7')) mac = 1 else: mac = 0 os.chdir(indigoRendererDotNetPath) command = '%s /property:LibraryPath=%s /property:Win=%s /property:Linux=%s /property:Mac=%s /property:Copy=%s' % (msbuildcommand, join(api_dir, 'libs', 'shared'), win, linux, mac, 'copy' if os.name == 'nt' else 'cp') print(command) subprocess.check_call(command, shell=True) # Build IndigoInchi-dotnet indigoInchiDotNetPath = join(api_dir, "plugins", "inchi", "dotnet") if os.path.exists(join(indigoInchiDotNetPath, "Resource")): shutil.rmtree(join(indigoInchiDotNetPath, "Resource")) if 'win' in wrappers: os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Win', 'x64')) os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Win', 'x86')) win = 1 else: win = 0 if 'linux' in wrappers: os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Linux', 'x64')) os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Linux', 'x86')) linux = 1 else: linux = 0 if 'mac' in wrappers: #os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Mac', '10.5')) os.makedirs(join(indigoInchiDotNetPath, "Resource", 'Mac', '10.7')) mac = 1 else: mac = 0 os.chdir(indigoInchiDotNetPath) command = '%s /property:LibraryPath=%s /property:Win=%s /property:Linux=%s /property:Mac=%s /property:Copy=%s' % (msbuildcommand, join(api_dir, 'libs', 'shared'), win, linux, mac, 'copy' if os.name == 'nt' else 'cp') print(command) subprocess.check_call(command, shell=True) # Build Bingo-dotnet bingoDotNetPath = join(api_dir, "plugins", "bingo", "dotnet") if os.path.exists(join(bingoDotNetPath, "Resource")): shutil.rmtree(join(bingoDotNetPath, "Resource")) if 'win' in wrappers: os.makedirs(join(bingoDotNetPath, "Resource", 'Win', 'x64')) os.makedirs(join(bingoDotNetPath, "Resource", 'Win', 'x86')) win = 1 else: win = 0 if 'linux' in wrappers: os.makedirs(join(bingoDotNetPath, "Resource", 'Linux', 'x64')) os.makedirs(join(bingoDotNetPath, "Resource", 'Linux', 'x86')) linux = 1 else: linux = 0 if 'mac' in wrappers: #os.makedirs(join(bingoDotNetPath, "Resource", 'Mac', '10.5')) os.makedirs(join(bingoDotNetPath, "Resource", 'Mac', '10.7')) mac = 1 else: mac = 0 os.chdir(bingoDotNetPath) command = '%s /property:LibraryPath=%s /property:Win=%s /property:Linux=%s /property:Mac=%s /property:Copy=%s' % (msbuildcommand, join(api_dir, 'libs', 'shared'), win, linux, mac, 'copy' if os.name == 'nt' else 'cp') print(command) subprocess.check_call(command, shell=True) # Zip results doc_dir = join(api_dir, '..', 'doc') os.chdir(dist_dir) shutil.copy(os.path.join(api_dir, "LICENSE.GPL"), "dotnet") shutil.copy(join(indigoDotNetPath, 'bin', 'Release', 'indigo-dotnet.dll'), "dotnet") shutil.copy(join(indigoRendererDotNetPath, 'bin', 'Release', 'indigo-renderer-dotnet.dll'), "dotnet") shutil.copy(join(indigoInchiDotNetPath, 'bin', 'Release', 'indigo-inchi-dotnet.dll'), "dotnet") shutil.copy(join(bingoDotNetPath, 'bin', 'Release', 'bingo-dotnet.dll'), "dotnet") shutil.copy(join(bingoDotNetPath, 'bin', 'Release', 'bingo-dotnet.XML'), "dotnet") if args.doc: shutil.copytree(os.path.join(doc_dir, 'build', 'html'), os.path.join('dotnet', 'doc')) archive_name = "indigo-dotnet-%s" % (version + args.suffix) os.rename("dotnet", archive_name) os.system("zip -r -9 -m %s.zip %s" % (archive_name, archive_name)) Indigo-indigo-1.2.3/api/make-java-wrappers.py000066400000000000000000000053261271037650300210270ustar00rootroot00000000000000import os import shutil from os.path import * import re import subprocess from optparse import OptionParser parser = OptionParser(description='Indigo Java libraries build script') parser.add_option('--suffix', '-s', help='archive suffix', default="") parser.add_option('--doc', default=False, action='store_true', help='Put documentation into the archive') (args, left_args) = parser.parse_args() # Find indigo version from get_indigo_version import getIndigoVersion version = getIndigoVersion() api_dir = abspath(dirname(__file__)) root = join(api_dir, "..") dist_dir = join(root, "dist") if not os.path.exists(dist_dir): os.mkdir(dist_dir) os.chdir(dist_dir) if os.path.exists("java"): shutil.rmtree("java") os.mkdir('java') os.chdir(os.path.join(api_dir, "java")) subprocess.check_call("mvn versions:set -DnewVersion=%s" % version, shell=True) subprocess.check_call("mvn clean package install -Dmaven.test.skip=true", shell=True) shutil.copy(os.path.join(os.path.abspath(os.curdir), 'target', 'indigo-%s.jar' % version), os.path.join(dist_dir, 'java', 'indigo.jar')) os.chdir(os.path.join(api_dir, "plugins", "renderer", "java")) subprocess.check_call("mvn versions:set -DnewVersion=%s" % version, shell=True) subprocess.check_call("mvn clean package -Dmaven.test.skip=true", shell=True) shutil.copy(os.path.join(os.path.abspath(os.curdir), 'target', 'indigo-renderer-%s.jar' % version), os.path.join(dist_dir, 'java', 'indigo-renderer.jar')) os.chdir(os.path.join(api_dir, "plugins", "inchi", "java")) subprocess.check_call("mvn versions:set -DnewVersion=%s" % version, shell=True) subprocess.check_call("mvn clean package -Dmaven.test.skip=true", shell=True) shutil.copy(os.path.join(os.path.abspath(os.curdir), 'target', 'indigo-inchi-%s.jar' % version), os.path.join(dist_dir, 'java', 'indigo-inchi.jar')) os.chdir(os.path.join(api_dir, "plugins", "bingo", "java")) # TODO: Update when folder will be changed to nosql subprocess.check_call("mvn versions:set -DnewVersion=%s" % version, shell=True) subprocess.check_call("mvn clean package -Dmaven.test.skip=true", shell=True) shutil.copy(os.path.join(os.path.abspath(os.curdir), 'target', 'bingo-nosql-%s.jar' % version), os.path.join(dist_dir, 'java', 'bingo-nosql.jar')) os.chdir(dist_dir) shutil.copy(os.path.join(api_dir, "LICENSE.GPL"), "java") if args.doc: doc_dir = join(api_dir, '..', 'doc') shutil.copytree(os.path.join(doc_dir, 'build', 'html'), os.path.join('java', 'doc')) shutil.copy(os.path.join(root, "common", "jna", "jna.jar"), "java") archive_name = "indigo-java-%s" % (version + args.suffix) os.rename("java", archive_name) subprocess.check_call("zip -r -9 -m %s.zip %s" % (archive_name, archive_name), shell=True) Indigo-indigo-1.2.3/api/make-python-wrappers.py000066400000000000000000000032051271037650300214210ustar00rootroot00000000000000import os import shutil from os.path import * import re from optparse import OptionParser parser = OptionParser(description='Indigo Java libraries build script') parser.add_option('--suffix', '-s', help='archive suffix', default="") parser.add_option('--doc', default=False, action='store_true', help='Put documentation into the archive') (args, left_args) = parser.parse_args() # Find indigo version from get_indigo_version import getIndigoVersion version = getIndigoVersion() api_dir = abspath(dirname(__file__)) doc_dir = join(api_dir, '..', 'doc') root = join(api_dir, "..") dist_dir = join(root, "dist") if not os.path.exists(dist_dir): os.mkdir(dist_dir) archive_name = "indigo-python-" + version + args.suffix dest = os.path.join(dist_dir, archive_name) if os.path.exists(dest): shutil.rmtree(dest) os.mkdir(dest) shutil.copy(os.path.join(api_dir, "python", "indigo.py"), dest) shutil.copy(os.path.join(api_dir, "plugins", "renderer", "python", "indigo_renderer.py"), dest) shutil.copy(os.path.join(api_dir, "plugins", "inchi", "python", "indigo_inchi.py"), dest) shutil.copy(os.path.join(api_dir, "plugins", "bingo", "python", "bingo.py"), dest) if args.doc: shutil.copytree(os.path.join(doc_dir, 'build', 'html'), os.path.join(dest, 'doc')) shutil.copytree(os.path.join(api_dir, "libs", "shared"), os.path.join(dest, "lib"), ignore = shutil.ignore_patterns("*.lib")) shutil.copy(os.path.join(api_dir, "LICENSE.GPL"), dest) os.chdir(dist_dir) if os.path.exists(archive_name + ".zip"): os.remove(archive_name + ".zip") os.system("zip -r -9 -m %s.zip %s" % (archive_name, archive_name)) Indigo-indigo-1.2.3/api/plugins/000077500000000000000000000000001271037650300164335ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/000077500000000000000000000000001271037650300175315ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/CMakeLists.txt000066400000000000000000000053041271037650300222730ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(Bingo CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../../common/cmake/) file (GLOB Bingo_src src/*.c*) file (GLOB Bingo_headers *.h src/*.h*) include(ConfigureCommon) include_directories(${Bingo_SOURCE_DIR} ${Indigo_SOURCE_DIR} ${Indigo_SOURCE_DIR}/src ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/..) include(DefineTest) # Bingo static if (NOT NO_STATIC) add_library(bingo STATIC ${Bingo_src} ${Bingo_headers}) if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(bingo PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/bingo.so.map) endif() if(APPLE) SET_TARGET_PROPERTIES(bingo PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/bingo.explist") endif() set_target_properties(bingo PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") SET_TARGET_PROPERTIES(bingo PROPERTIES OUTPUT_NAME "bingo-static") set_property(TARGET bingo PROPERTY FOLDER "bingo") # No exports in case of static library: define empty EXPORT_SYMBOL definition set_target_properties(bingo PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DEXPORT_SYMBOL=") PACK_STATIC(bingo) #DEFINE_TEST(bingo-test-static "tests/c/bingo-test.c" bingo indigo) # Add stdc++ library required by indigo #SET_TARGET_PROPERTIES(bingo-test-static PROPERTIES LINKER_LANGUAGE CXX) endif() # Bingo shared add_library(bingo-shared SHARED ${Bingo_src} ${Bingo_headers} ${Common_SOURCE_DIR}/hacks/memcpy.c) if (UNIX AND NOT APPLE) if(${SUBSYSTEM_NAME} MATCHES "x64") set(LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() endif() SET_TARGET_PROPERTIES(bingo-shared PROPERTIES OUTPUT_NAME "bingo") if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(bingo-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/bingo.so.map") endif() if(APPLE) SET_TARGET_PROPERTIES(bingo-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -undefined dynamic_lookup -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/bingo.explist") endif() set_target_properties(bingo-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if(MSVC) # This should be set only for a shared library to avoid warnings set_target_properties(bingo-shared PROPERTIES COMPILE_FLAGS "-D_WINDLL -DINDIGO_PLUGIN") endif() if (MSVC OR MINGW) target_link_libraries(bingo-shared indigo-shared) endif() set_property(TARGET bingo-shared PROPERTY LINK_INTERFACE_LIBRARIES "") set_property(TARGET bingo-shared PROPERTY FOLDER "bingo") IF (NOT PACK_INDIGO_NOT) PACK_SHARED(bingo-shared) ENDIF() #DEFINE_TEST(bingo-test-shared "tests/c/bingo-test.c" bingo-shared indigo-shared) # Add stdc++ library required by indigo #SET_TARGET_PROPERTIES(bingo-test-shared PROPERTIES LINKER_LANGUAGE CXX) Indigo-indigo-1.2.3/api/plugins/bingo/bingo.explist000066400000000000000000000000101271037650300222300ustar00rootroot00000000000000_bingo* Indigo-indigo-1.2.3/api/plugins/bingo/bingo.h000066400000000000000000000046161271037650300210070ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_bingo__ #define __indigo_bingo__ #include "indigo.h" CEXPORT const char * bingoVersion (); // options = "id: " CEXPORT int bingoCreateDatabaseFile (const char *location, const char *type, const char *options); CEXPORT int bingoLoadDatabaseFile (const char *location, const char *options); CEXPORT int bingoCloseDatabase (int db); // // Record insertion/deletion // CEXPORT int bingoInsertRecordObj (int db, int obj); CEXPORT int bingoInsertRecordObjWithId (int db, int obj, int id); CEXPORT int bingoDeleteRecord (int db, int id); CEXPORT int bingoGetRecordObj (int db, int id); CEXPORT int bingoOptimize (int db); // Search methods that returns search object // Search object is an iterator CEXPORT int bingoSearchSub (int db, int query_obj, const char *options); CEXPORT int bingoSearchExact (int db, int query_obj, const char *options); CEXPORT int bingoSearchMolFormula (int db, const char *query, const char *options); CEXPORT int bingoSearchSim (int db, int query_obj, float min, float max, const char *options); // // Search object methods // CEXPORT int bingoNext (int search_obj); CEXPORT int bingoGetCurrentId (int search_obj); CEXPORT float bingoGetCurrentSimilarityValue (int search_obj); // Estimation methods CEXPORT int bingoEstimateRemainingResultsCount (int search_obj); CEXPORT int bingoEstimateRemainingResultsCountError (int search_obj); CEXPORT int bingoEstimateRemainingTime (int search_obj, float *time_sec); // This method return IndigoObject that represents current object. // After calling bingoNext this object automatically points to the next found result CEXPORT int bingoGetObject (int search_obj); CEXPORT int bingoEndSearch (int search_obj); #endif // __indigo_bingo__ Indigo-indigo-1.2.3/api/plugins/bingo/bingo.so.map000066400000000000000000000000511271037650300217420ustar00rootroot00000000000000{ global: bingo*; local: *; }; Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/000077500000000000000000000000001271037650300210265ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Bingo.cs000066400000000000000000000313731271037650300224220ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.IO; namespace com.epam.indigo { /// /// Bingo instance corresponds to a single chemical database /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public unsafe class Bingo : IDisposable { private Indigo _indigo; private BingoLib _lib; private IndigoDllLoader _dllLoader; private int _id; private Bingo(Indigo indigo, int id, BingoLib lib) { _indigo = indigo; _lib = lib; _id = id; _dllLoader = IndigoDllLoader.Instance; } /// /// Destructor /// ~Bingo() { Dispose(); } /// /// Dispose method that closes the database /// public void Dispose() { if (_id >= 0 && _dllLoader != null && _dllLoader.isValid() && _indigo != null && _lib != null) { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoCloseDatabase(_id)); _id = -1; } } /// /// Method to close a database /// public void close() { Dispose(); } internal static int checkResult(Indigo indigo, int result) { if (result < 0) { throw new BingoException(new String(indigo._indigo_lib.indigoGetLastError())); } return result; } internal static float checkResult(Indigo indigo, float result) { if (result < 0.0) { throw new BingoException(new String(indigo._indigo_lib.indigoGetLastError())); } return result; } internal static string checkResult(Indigo indigo, string result) { if (result == null) { throw new BingoException(new String(indigo._indigo_lib.indigoGetLastError())); } return result; } private static BingoLib getLib(Indigo indigo) { String dllpath = indigo.getDllPath(); string libraryName; IndigoDllLoader dll_loader = IndigoDllLoader.Instance; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: libraryName = "bingo.dll"; dll_loader.loadLibrary(dllpath, libraryName, "com.epam.indigo.Properties.ResourcesWin", false); break; case PlatformID.Unix: if (IndigoDllLoader.isMac()) { libraryName = "libbingo.dylib"; dll_loader.loadLibrary(dllpath, libraryName, "com.epam.indigo.Properties.ResourcesMac", false); } else { libraryName = "libbingo.so"; dll_loader.loadLibrary(dllpath, libraryName, "com.epam.indigo.Properties.ResourcesLinux", false); } break; default: throw new PlatformNotSupportedException(String.Format("Unsupported platform: {0}", Environment.OSVersion.Platform)); } return dll_loader.getInterface(libraryName); } /// /// Creates a chemical storage of a specifed type in a specified location /// /// Indigo instance /// Directory with the files location /// "molecule" or "reaction" /// Additional options separated with a semicolon. See the Bingo documentation for more details. /// Bingo database instance public static Bingo createDatabaseFile(Indigo indigo, string location, string type, string options) { indigo.setSessionID(); if (options == null) { options = ""; } BingoLib lib = Bingo.getLib(indigo); int databaseID = Bingo.checkResult(indigo, lib.bingoCreateDatabaseFile(location, type, options)); return new Bingo(indigo, databaseID, lib); } /// /// Creates a chemical storage of a specifed type in a specified location /// /// Indigo instance /// Directory with the files location /// "molecule" or "reaction" /// Bingo database instance public static Bingo createDatabaseFile(Indigo indigo, string location, string type) { return createDatabaseFile(indigo, location, type, null); } /// /// Loads a chemical storage of a specifed type from a specified location /// /// Indigo instance /// Directory with the files location /// Additional options separated with a semicolon. See the Bingo documentation for more details. /// Bingo database instance public static Bingo loadDatabaseFile(Indigo indigo, string location, string options) { if (options == null) { options = ""; } BingoLib lib = Bingo.getLib(indigo); indigo.setSessionID(); int databaseID = Bingo.checkResult(indigo, lib.bingoLoadDatabaseFile(location, options)); return new Bingo(indigo, databaseID, lib); } /// /// Loads a chemical storage of a specifed type from a specified location /// /// Indigo instance /// Directory with the files location /// Bingo database instance public static Bingo loadDatabaseFile(Indigo indigo, string location) { return loadDatabaseFile(indigo, location, null); } /// /// Insert a structure into the database and returns id of this structure /// /// Indigo object with a chemical structure (molecule or reaction) /// record id public int insert(IndigoObject record) { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoInsertRecordObj(_id, record.self)); } /// /// Inserts a structure under a specified id /// /// Indigo object with a chemical structure (molecule or reaction) /// record id /// inserted record id public int insert(IndigoObject record, int id) { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoInsertRecordObjWithId(_id, record.self, id)); } /// /// Delete a record by id /// /// Record id public void delete(int id) { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoDeleteRecord(_id, id)); } /// /// Execute substructure search operation /// /// Indigo query object (molecule or reaction) /// Search options /// Bingo search object instance public BingoObject searchSub(IndigoObject query, string options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchSub(_id, query.self, options)), _indigo, _lib); } /// /// Execute substructure search operation /// /// Indigo query object (molecule or reaction) /// Bingo search object instance public BingoObject searchSub(IndigoObject query) { return searchSub(query, null); } /// /// Execute similarity search operation /// /// indigo object (molecule or reaction) /// Minimum similarity value /// Maximum similairy value /// Default value is "tanimoto" /// Bingo search object instance public BingoObject searchSim(IndigoObject query, float min, float max, string metric) { if (metric == null) { metric = "tanimoto"; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchSim(_id, query.self, min, max, metric)), _indigo, _lib); } /// /// Execute similarity search operation /// /// indigo object (molecule or reaction) /// Minimum similarity value /// Maximum similairy value /// Bingo search object instance public BingoObject searchSim(IndigoObject query, float min, float max) { return searchSim(query, min, max, null); } /// /// Perform exact search operation /// /// indigo object (molecule or reaction) /// Bingo search object instance public BingoObject searchExact(IndigoObject query) { return searchExact(query, null); } /// /// Perform exact search operation /// /// indigo object (molecule or reaction) /// exact search options /// Bingo search object instance public BingoObject searchExact(IndigoObject query, string options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchExact(_id, query.self, options)), _indigo, _lib); } /// /// Perform search by molecular formula /// /// string with formula to search. For example, 'C22 H23 N3 O2'. /// search options /// Bingo search object instance public BingoObject searchMolFormula(string query, string options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchMolFormula(_id, query, options)), _indigo, _lib); } /// /// Perform search by molecular formula /// /// string with formula to search. For example, 'C22 H23 N3 O2'. /// Bingo search object instance public BingoObject searchMolFormula(string query) { return searchMolFormula(query, null); } /// /// Post-process index optimization /// public void optimize () { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoOptimize(_id)); } /// /// Returns an IndigoObject for the record with the specified id /// /// record id /// Indigo object public IndigoObject getRecordById(int id) { _indigo.setSessionID(); return new IndigoObject(_indigo, Bingo.checkResult(_indigo, _lib.bingoGetRecordObj(_id, id))); ; } /// /// Returns Bingo version /// /// Bingo version public string version() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoVersion()); } } } Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/BingoException.cs000066400000000000000000000015521271037650300242750ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.Serialization; namespace com.epam.indigo { /// /// Bingo exception /// public class BingoException : Exception { /// /// public BingoException() : base() { } /// /// public BingoException(string message) : base(message) { } /// /// protected BingoException(SerializationInfo info, StreamingContext context) : base(info, context) { } /// /// public BingoException(string message, Exception innerException) : base(message, innerException) { } } } Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/BingoLib.cs000066400000000000000000000026111271037650300230420ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; #pragma warning disable 1591 namespace com.epam.indigo { public unsafe interface BingoLib { int bingoCreateDatabaseFile(string location, string type, string options); int bingoLoadDatabaseFile(string location, string options); int bingoCloseDatabase(int db); int bingoInsertRecordObj (int db, int obj); int bingoInsertRecordObjWithId(int db, int obj, int id); int bingoDeleteRecord (int db, int index); int bingoOptimize (int db); int bingoSearchSub (int db, int query_obj, string options); int bingoSearchSim (int db, int query_obj, float min, float max, string options); int bingoSearchExact (int db, int query_obj, string options); int bingoSearchMolFormula (int db, string query, string options); int bingoNext (int search_obj); int bingoGetCurrentId (int search_obj); float bingoGetCurrentSimilarityValue(int search_obj); int bingoEstimateRemainingResultsCount (int search_obj); int bingoEstimateRemainingResultsCountError (int search_obj); int bingoEstimateRemainingTime (int search_obj, float *time_sec); int bingoGetObject (int search_obj); int bingoEndSearch (int search_obj); int bingoGetRecordObj (int db, int obj_id); string bingoVersion(); } } Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/BingoObject.cs000066400000000000000000000064311271037650300235460ustar00rootroot00000000000000using System; using System.Collections; namespace com.epam.indigo { /// /// Bingo search object /// public unsafe class BingoObject : IDisposable { private int _id; private Indigo _indigo; private BingoLib _bingoLib; private IndigoDllLoader _dllLoader; private IDisposable _reference; internal BingoObject(int id, Indigo indigo, BingoLib bingo_lib) { this._id = id; this._indigo = indigo; this._bingoLib = bingo_lib; this._dllLoader = IndigoDllLoader.Instance; } /// /// Destructor /// ~BingoObject() { Dispose(); } /// /// public void Dispose() { if (_id >= 0 && _dllLoader != null && _dllLoader.isValid() && _bingoLib != null && _indigo != null) { _indigo.setSessionID(); Bingo.checkResult(_indigo, _bingoLib.bingoEndSearch(_id)); _id = -1; } } /// /// Close method /// public void close() { Dispose(); } internal int id { get { return _id; } set { _id = value; } } /// /// Method to move to the next record /// /// True if there are any more records public bool next() { _indigo.setSessionID(); return (Bingo.checkResult(_indigo, _bingoLib.bingoNext(_id)) == 1) ? true : false; } /// /// Method to return current record id. Should be called after next() method. /// /// Record id public int getCurrentId() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoGetCurrentId(_id)); } /// /// Method to return current similarity value. Should be called after next() method. /// /// Similarity value public float getCurrentSimilarityValue() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoGetCurrentSimilarityValue(_id)); } /// /// Returns a shared IndigoObject for the matched target /// /// Shared Indigo object for the current search operation public IndigoObject getIndigoObject() { _indigo.setSessionID(); IndigoObject res = new IndigoObject(_indigo, Bingo.checkResult(_indigo, _bingoLib.bingoGetObject(_id))); _reference = res; return res; } /// /// Method to estimate remaining hits count /// /// Estimated hits count public int estimateRemainingResultsCount () { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingResultsCount(_id)); } /// /// Method to estimate remaining hits count error /// /// Estimated hits count error public int estimateRemainingResultsCountError () { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingResultsCountError(_id)); } /// /// Method to estimate remaining search time /// /// Estimated remainin search time public float estimateRemainingTime () { float esimated_time; _indigo.setSessionID(); Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingTime(_id, &esimated_time)); return esimated_time; } } } Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/000077500000000000000000000000001271037650300231625ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/AssemblyInfo.cs000066400000000000000000000026001271037650300261020ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Indigo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010-2012")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("96c7fefe-a81f-448d-94cf-61cf4a5dd8c6")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.*")] [assembly: AssemblyFileVersion("1.1.0.0")] Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/Resources.Designer.cs000066400000000000000000000063021271037650300272230ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.261 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace com.epam.indigo.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("com.epam.indigo.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } internal static byte[] Win_x64_bingo_dll { get { object obj = ResourceManager.GetObject("Win/x64/bingo.dll", resourceCulture); return ((byte[])(obj)); } } internal static byte[] Win_x86_bingo_dll { get { object obj = ResourceManager.GetObject("Win/x86/ibingo.dll", resourceCulture); return ((byte[])(obj)); } } } } Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/ResourcesLinux.resx000066400000000000000000000142761271037650300270710ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x64\libbingo.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x86\libbingo.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/ResourcesMac.resx000066400000000000000000000143231271037650300264630ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Mac\10.7\libbingo.dylib;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/Properties/ResourcesWin.resx000066400000000000000000000142611271037650300265210ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\bingo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\bingo.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/bingo-dotnet.csproj000066400000000000000000000131601271037650300246420ustar00rootroot00000000000000 Debug AnyCPU 8.0.30703 2.0 {A88F82B7-F522-4238-80D6-DE033CD7892E} Library Properties com.epam.indigo bingo-dotnet v2.0 512 true true full false bin\Debug\ DEBUG;TRACE prompt 4 true bin\Debug\bingo-dotnet.XML pdbonly true bin\Release\ TRACE prompt 4 true bin\Release\bingo-dotnet.XML true bin\x64\Debug\ DEBUG;TRACE full x64 prompt true 4 false bin\x64\Release\ TRACE true true pdbonly x64 prompt false false 4 true bingo.snk false ..\..\..\dotnet\bin\Release\indigo-dotnet.dll True True $(Copy) $(LibraryPath)\Win\x64\bingo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\bingo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libbingo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libbingo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libbingo.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\bingo.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\bingo.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libbingo.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libbingo.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libbingo.dylib $(ProjectDir)Resource\Mac\10.7 Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/bingo-dotnet.sln000066400000000000000000000023311271037650300241340ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bingo-dotnet", "bingo-dotnet.csproj", "{A88F82B7-F522-4238-80D6-DE033CD7892E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|x64.ActiveCfg = Debug|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|x64.Build.0 = Debug|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|Any CPU.Build.0 = Release|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|x64.ActiveCfg = Release|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Indigo-indigo-1.2.3/api/plugins/bingo/dotnet/bingo.snk000066400000000000000000000011241271037650300226370ustar00rootroot00000000000000$RSA2a}ö'_uß× ¨#Ȇ'P} À°»—«×ëpö©®g`\ë¾kìèâk¾ÝÛEhî˜È"í,θ‹ä ’G°ÎIÒ%0DT°Ã.v¹ù?ý„“ß¹Q¾Þz<<‹@›ŒH˜ÍΪÏÏ”EfaÿâɾUK´ÜËäÏc°@¬Ù84`•äxWÞý3-ËýSÙV¥Ë4ßQŸpáF‰¨û9¼IïÛN6õfß„,®ì$zÕbÃq™ü2zœ­ÖÔÉÓ¼ ¦éWã!&ñÇÉ»=Ö`“Ħéãqs2&œCIàɪnhZl>¶Z>÷\Z‚ žI””¦’/ϱö$+ ˪Asn"ÔÙaç»üŠÓ” Y’‹Ý]'GcÓٷĆ@)©ï#Hk•äÜÈ+®iÑÙÛ·7Âúö@+"€1M-ÜË쇕Œ·S%ÂJ]`øÔŒF›^¶Õ¡{ào ¡3°¼U´«¼:_ÒéëÎØ2"øqà¼3â=zíôý7ñ˜'‹/W„´GÿîŒVúª2hXţ؈›»M–T¡ Ë«,ït2-jY±«Ù¤‰¼'k§Pö<ÛÉWv ÈæÌp18Ë]N%_¶)%ñïÆÁó4‹KRø37ý.%ÙÌà¥øå/Íg'M5´‡vk\-(ï4A¼|Eš÷7ÛïOËúÖµseq£ŽîËd£VÄQcNÔõÙ‹PÓ×½N2kK€jÕâ Indigo-indigo-1.2.3/api/plugins/bingo/java/000077500000000000000000000000001271037650300204525ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/pom.xml000066400000000000000000000142271271037650300217750ustar00rootroot00000000000000 4.0.0 com.epam.indigo bingo-nosql 1.2.2beta.r117 jar Indigo NoSQL NoSQL chemistry database search tool http://lifescience.opensource.epam.com/indigo/api GNU General Public License v3 http://www.gnu.org/licenses/gpl.html repo engineer Mikhail Kviatkovskii mikhail_kviatkovskii@epam.com UTC+3 scm:git:git@github.com:epam/Indigo.git scm:git:git@github.com:epam/Indigo.git git@github.com:epam/Indigo.git net.java.dev.jna jna 3.5.0 com.epam.indigo indigo ${project.version} org.apache.maven.plugins maven-compiler-plugin 3.1 1.5 1.5 org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources package jar org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs package jar org.apache.maven.plugins maven-jar-plugin 2.3.1 false org.sonatype.plugins nexus-staging-maven-plugin 1.6.3 true deploy ossrh https://oss.sonatype.org/ false ../../../libs/shared **/*.lib **/*bingo.* ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.4 ${gpg.passhprase} --no-tty sign-artifacts verify sign Indigo-indigo-1.2.3/api/plugins/bingo/java/src/000077500000000000000000000000001271037650300212415ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/000077500000000000000000000000001271037650300221655ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/000077500000000000000000000000001271037650300231065ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/000077500000000000000000000000001271037650300236645ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/000077500000000000000000000000001271037650300246065ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/indigo/000077500000000000000000000000001271037650300260575ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/indigo/Bingo.java000066400000000000000000000230771271037650300277710ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Native; import java.io.File; import java.io.IOException; public class Bingo { private Indigo _indigo; private static BingoLib _lib; private int _id; public Bingo (Indigo indigo, String location, String type, String options) { loadLibrary(indigo.getUserSpecifiedPath()); _id = Bingo.checkResult(indigo, _lib.bingoCreateDatabaseFile(location, type, options)); _indigo = indigo; } public Bingo (Indigo indigo, String location, String options) { loadLibrary(indigo.getUserSpecifiedPath()); _id = Bingo.checkResult(indigo, _lib.bingoLoadDatabaseFile(location, options)); _indigo = indigo; } protected void finalize() { dispose(); } private synchronized static void loadLibrary (String path) { if (_lib != null) return; int os = Indigo.getOs(); if (os == Indigo.OS_LINUX || os == Indigo.OS_SOLARIS) _lib = (BingoLib)Native.loadLibrary(getPathToBinary(path, "libbingo.so"), BingoLib.class); else if (os == Indigo.OS_MACOS) _lib = (BingoLib)Native.loadLibrary(getPathToBinary(path, "libbingo.dylib"), BingoLib.class); else // os == OS_WINDOWS _lib = (BingoLib)Native.loadLibrary(getPathToBinary(path, "bingo.dll"), BingoLib.class); } private static String getPathToBinary (String path, String filename) { String dllpath = Indigo.getPlatformDependentPath(); if (path == null) { String res = Indigo.extractFromJar(Bingo.class, "/" + dllpath, filename); if (res != null) return res; path = "lib"; } path = path + File.separator + dllpath + File.separator + filename; try { return (new File(path)).getCanonicalPath(); } catch (IOException e) { return path; } } public void dispose() { if (_id >= 0) { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoCloseDatabase(_id)); _id = -1; } } public void close() { dispose(); } public static int checkResult(Indigo indigo, int result) { if (result < 0) { throw new BingoException(indigo, indigo.getLibrary().indigoGetLastError()); } return result; } public static float checkResult(Indigo indigo, float result) { if (result < 0.0) { throw new BingoException(indigo, indigo.getLibrary().indigoGetLastError()); } return result; } public static String checkResult(Indigo indigo, String result) { if (result == null) { throw new BingoException(indigo, indigo.getLibrary().indigoGetLastError()); } return result; } /** Creates a chemical storage of a specified type in a specified location @param indigo Indigo instance @param location Directory with the files location @param type molecule" or "reaction" @param options additional options separated with a semicolon. See the Bingo documentation for more detail @return Bingo database instance */ public static Bingo createDatabaseFile(Indigo indigo, String location, String type, String options) { indigo.setSessionID(); if (options == null) { options = ""; } return new Bingo(indigo, location, type, options); } /** Creates a chemical storage of a specified type in a specified location @param indigo Indigo instance @param location Directory with the files location @param type molecule" or "reaction" @return Bingo database instance */ public static Bingo createDatabaseFile(Indigo indigo, String location, String type) { return createDatabaseFile(indigo, location, type, null); } /** Loads a chemical storage of a specified type from a specified location @param indigo Indigo instance @param location Directory with the files location @param options Additional options separated with a semicolon. See the Bingo documentation for more details @return Bingo database instance */ public static Bingo loadDatabaseFile(Indigo indigo, String location, String options) { if (options == null) { options = ""; } return new Bingo(indigo, location, options); } /** Loads a chemical storage of a specified type from a specified location @param indigo Indigo instance @param location Directory with the files location @return Bingo database instance */ public static Bingo loadDatabaseFile(Indigo indigo, String location) { return loadDatabaseFile(indigo, location, null); } /** Insert a structure into the database and returns id of this structure @param record Indigo object with a chemical structure (molecule or reaction) @return record id */ public int insert(IndigoObject record) { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoInsertRecordObj(_id, record.self)); } /** Inserts a structure under a specified id @param record Indigo object with a chemical structure (molecule or reaction) @param id record id @return inserted record id */ public int insert(IndigoObject record, int id) { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoInsertRecordObjWithId(_id, record.self, id)); } /** Delete a record by id @param id Record id */ public void delete(int id) { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoDeleteRecord(_id, id)); } /** Execute substructure search operation @param query Indigo query object (molecule or reaction) @param options Search options @return Bingo search object instance */ public BingoObject searchSub(IndigoObject query, String options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchSub(_id, query.self, options)), _indigo, _lib); } /** Execute substructure search operation @param query Indigo query object (molecule or reaction) @return Bingo search object instance */ public BingoObject searchSub(IndigoObject query) { return searchSub(query, null); } /** Execute similarity search operation @param query indigo object (molecule or reaction) @param min Minimum similarity value @param max Maximum similairy value @param metric Default value is "tanimoto" @return Bingo search object instance */ public BingoObject searchSim(IndigoObject query, float min, float max, String metric) { if (metric == null) { metric = "tanimoto"; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchSim(_id, query.self, min, max, metric)), _indigo, _lib); } /** Execute Tanimoto similarity search operation @param query indigo object (molecule or reaction) @param min Minimum similarity value @param max Maximum similairy value @return Bingo search object instance */ public BingoObject searchSim(IndigoObject query, float min, float max) { return searchSim(query, min, max, null); } /** Perform exact search operation @param query indigo object (molecule or reaction) @param options search options @return Bingo search object instance */ public BingoObject searchExact(IndigoObject query, String options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchExact(_id, query.self, options)), _indigo, _lib); } /** Perform exact search operation @param query indigo object (molecule or reaction) @return Bingo search object instance */ public BingoObject searchExact(IndigoObject query) { return searchExact(query, null); } /** Perform search by molecular formula @param query string with formula to search. For example, "C22 H23 N3 O2" @param options search options @return Bingo search object instance */ public BingoObject searchMolFormula(String query, String options) { if (options == null) { options = ""; } _indigo.setSessionID(); return new BingoObject(Bingo.checkResult(_indigo, _lib.bingoSearchMolFormula(_id, query, options)), _indigo, _lib); } /** Perform search by molecular formula @param query string with formula to search. For example, "C22 H23 N3 O2" @return Bingo search object instance */ public BingoObject searchMolFormula(String query) { return searchMolFormula(query, null); } /** Post-process index optimization */ public void optimize () { _indigo.setSessionID(); Bingo.checkResult(_indigo, _lib.bingoOptimize(_id)); } /** Returns an IndigoObject for the record with the specified id @param id record id @return Indigo object */ public IndigoObject getRecordById(int id) { _indigo.setSessionID(); return new IndigoObject(_indigo, Bingo.checkResult(_indigo, _lib.bingoGetRecordObj(_id, id))); } /** Returns Bingo version @return Bingo version */ public String version() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _lib.bingoVersion()); } }Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/indigo/BingoException.java000066400000000000000000000032521271037650300316410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; public class BingoException extends RuntimeException { Object obj; // You may wonder what object we are keeping here and why. // Here is the answer: we are keeping the object that raised the exception, // for prohibiting the garbage collector to destroy it while the method // is running. // Here is an example: // { // IndigoObject object = ...; // object.someMethod(); // does a native call // } // In this situation, the JVM is perfectly OK with deleting the object // *WHILE THE NATIVE CALL IS STILL RUNNING*. At least, it happened // on 64-bit Windows Server 2008 within KNIME. // To prevent that, we keep a reference to the object in the IndigoException // object (which can be thrown from every Indigo or IndigoObject method). // As long as the JVM sees that the reference is still used somewhere // afterwards the method call, it does not garbage-collect the object. public BingoException (Object obj_, String message) { super(message); obj = obj_; } } Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/indigo/BingoLib.java000066400000000000000000000037451271037650300304200ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2013 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Library; import com.sun.jna.ptr.FloatByReference; public interface BingoLib extends Library { int bingoCreateDatabaseFile(String location, String type, String options); int bingoLoadDatabaseFile(String location, String options); int bingoCloseDatabase(int db); int bingoInsertRecordObj (int db, int obj); int bingoInsertRecordObjWithId(int db, int obj, int id); int bingoDeleteRecord (int db, int index); int bingoOptimize (int db); int bingoSearchSub (int db, int query_obj, String options); int bingoSearchSim (int db, int query_obj, float min, float max, String options); int bingoSearchExact (int db, int query_obj, String options); int bingoSearchMolFormula (int db, String query, String options); int bingoNext (int search_obj); int bingoGetCurrentId (int search_obj); float bingoGetCurrentSimilarityValue(int search_obj); int bingoEstimateRemainingResultsCount (int search_obj); int bingoEstimateRemainingResultsCountError (int search_obj); int bingoEstimateRemainingTime (int search_obj, FloatByReference time_sec); int bingoGetObject (int search_obj); int bingoEndSearch (int search_obj); int bingoGetRecordObj (int db, int obj_id); String bingoVersion(); }Indigo-indigo-1.2.3/api/plugins/bingo/java/src/main/java/com/epam/indigo/BingoObject.java000066400000000000000000000051101271037650300311040ustar00rootroot00000000000000package com.epam.indigo; import com.sun.jna.ptr.FloatByReference; /** Bingo search object */ public class BingoObject { private int _id; private Indigo _indigo; private BingoLib _bingoLib; private Object _reference; BingoObject(int id, Indigo indigo, BingoLib bingo_lib) { _id = id; _indigo = indigo; _bingoLib = bingo_lib; } protected void finalize() { dispose(); } public void dispose() { if (_id >= 0) { Bingo.checkResult(_indigo, _bingoLib.bingoEndSearch(_id)); _id = -1; } } public void close() { dispose(); } /** Move to the next record return True if there are any more records */ public boolean next() { _indigo.setSessionID(); return (_bingoLib.bingoNext(_id) == 1); } /** Return current record id. Should be called after next() method. @return Record id */ public int getCurrentId() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoGetCurrentId(_id)); } /** Return current similarity value. Should be called after next() method. @return Similarity value */ public float getCurrentSimilarityValue() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoGetCurrentSimilarityValue(_id)); } /** Return a shared IndigoObject for the matched target @return Shared Indigo object for the current search operation **/ public IndigoObject getIndigoObject() { _indigo.setSessionID(); IndigoObject res = new IndigoObject(_indigo, Bingo.checkResult(_indigo, _bingoLib.bingoGetObject(_id))); _reference = res; return res; } /** Estimate remaining hits count @return Estimated hits count */ public int estimateRemainingResultsCount() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingResultsCount(_id)); } /** Estimate remaining hits count error @return Estimated hits count error */ public int estimateRemainingResultsCountError() { _indigo.setSessionID(); return Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingResultsCountError(_id)); } /** Estimate remaining search time @return Estimated remaining search time */ public float estimateRemainingTime () { FloatByReference estimated_time = new FloatByReference(); _indigo.setSessionID(); Bingo.checkResult(_indigo, _bingoLib.bingoEstimateRemainingTime(_id, estimated_time)); return estimated_time.getValue(); } } Indigo-indigo-1.2.3/api/plugins/bingo/python/000077500000000000000000000000001271037650300210525ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/python/bingo.py000066400000000000000000000231321271037650300225230ustar00rootroot00000000000000# # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. import os from indigo import * class BingoException(Exception): def __init__(self, value): self.value = value def __str__(self): if sys.version_info > (3, 0): return repr(self.value.decode('ascii')) else: return repr(self.value) class Bingo(object): def __init__(self, bingoId, indigo, lib): self._id = bingoId self._indigo = indigo self._lib = lib self._lib.bingoVersion.restype = c_char_p self._lib.bingoVersion.argtypes = None self._lib.bingoCreateDatabaseFile.restype = c_int self._lib.bingoCreateDatabaseFile.argtypes = [c_char_p, c_char_p, c_char_p] self._lib.bingoLoadDatabaseFile.restype = c_int self._lib.bingoLoadDatabaseFile.argtypes = [c_char_p, c_char_p] self._lib.bingoCloseDatabase.restype = c_int self._lib.bingoCloseDatabase.argtypes = [c_int] self._lib.bingoInsertRecordObj.restype = c_int self._lib.bingoInsertRecordObj.argtypes = [c_int, c_int] self._lib.bingoGetRecordObj.restype = c_int self._lib.bingoGetRecordObj.argtypes = [c_int, c_int] self._lib.bingoInsertRecordObjWithId.restype = c_int self._lib.bingoInsertRecordObjWithId.argtypes = [c_int, c_int, c_int] self._lib.bingoDeleteRecord.restype = c_int self._lib.bingoDeleteRecord.argtypes = [c_int, c_int] self._lib.bingoSearchSub.restype = c_int self._lib.bingoSearchSub.argtypes = [c_int, c_int, c_char_p] self._lib.bingoSearchExact.restype = c_int self._lib.bingoSearchExact.argtypes = [c_int, c_int, c_char_p] self._lib.bingoSearchMolFormula.restype = c_int self._lib.bingoSearchMolFormula.argtypes = [c_int, c_char_p, c_char_p] self._lib.bingoSearchSim.restype = c_int self._lib.bingoSearchSim.argtypes = [c_int, c_int, c_float, c_float, c_char_p] self._lib.bingoNext.restype = c_int self._lib.bingoNext.argtypes = [c_int] self._lib.bingoGetCurrentId.restype = c_int self._lib.bingoGetCurrentId.argtypes = [c_int] self._lib.bingoGetObject.restype = c_int self._lib.bingoGetObject.argtypes = [c_int] self._lib.bingoEndSearch.restype = c_int self._lib.bingoEndSearch.argtypes = [c_int] self._lib.bingoGetCurrentSimilarityValue.restype = c_float self._lib.bingoGetCurrentSimilarityValue.argtypes = [c_int] self._lib.bingoOptimize.restype = c_int self._lib.bingoOptimize.argtypes = [c_int] self._lib.bingoEstimateRemainingResultsCount.restype = c_int self._lib.bingoEstimateRemainingResultsCount.argtypes = [c_int] self._lib.bingoEstimateRemainingResultsCountError.restype = c_int self._lib.bingoEstimateRemainingResultsCountError.argtypes = [c_int] self._lib.bingoEstimateRemainingTime.restype = c_int self._lib.bingoEstimateRemainingTime.argtypes = [c_int, POINTER(c_float)] def __del__(self): self.close() def close(self): self._indigo._setSessionId() if self._id >= 0: Bingo._checkResult(self._indigo, self._lib.bingoCloseDatabase(self._id)) self._id = -1 @staticmethod def _checkResult(indigo, result): if result < 0: raise BingoException(indigo._lib.indigoGetLastError()) return result @staticmethod def _checkResultPtr (indigo, result): if result is None: raise BingoException(indigo._lib.indigoGetLastError()) return result @staticmethod def _checkResultString (indigo, result): res = Bingo._checkResultPtr(indigo, result) if sys.version_info >= (3, 0): return res.decode('ascii') else: return res.encode('ascii') @staticmethod def _getLib(indigo): if os.name == 'posix' and not platform.mac_ver()[0] and not platform.system().startswith("CYGWIN"): _lib = CDLL(indigo.dllpath + "/libbingo.so") elif os.name == 'nt' or platform.system().startswith("CYGWIN"): _lib = CDLL(indigo.dllpath + "/bingo.dll") elif platform.mac_ver()[0]: _lib = CDLL(indigo.dllpath + "/libbingo.dylib") else: raise BingoException("unsupported OS: " + os.name) return _lib @staticmethod def createDatabaseFile(indigo, path, databaseType, options=''): indigo._setSessionId() if not options: options = '' lib = Bingo._getLib(indigo) lib.bingoCreateDatabaseFile.restype = c_int lib.bingoCreateDatabaseFile.argtypes = [c_char_p, c_char_p, c_char_p] return Bingo(Bingo._checkResult(indigo, lib.bingoCreateDatabaseFile(path.encode('ascii'), databaseType.encode('ascii'), options.encode('ascii'))), indigo, lib) @staticmethod def loadDatabaseFile(indigo, path, options=''): indigo._setSessionId() if not options: options = '' lib = Bingo._getLib(indigo) lib.bingoLoadDatabaseFile.restype = c_int lib.bingoLoadDatabaseFile.argtypes = [c_char_p, c_char_p] return Bingo(Bingo._checkResult(indigo, lib.bingoLoadDatabaseFile(path.encode('ascii'), options.encode('ascii'))), indigo, lib) def version(self): self._indigo._setSessionId() return Bingo._checkResultString(self._indigo, self._lib.bingoVersion()) def insert(self, indigoObject, index=None): self._indigo._setSessionId() if not index: return Bingo._checkResult(self._indigo, self._lib.bingoInsertRecordObj(self._id, indigoObject.id)) else: return Bingo._checkResult(self._indigo, self._lib.bingoInsertRecordObjWithId(self._id, indigoObject.id, index)) def delete(self, index): self._indigo._setSessionId() Bingo._checkResult(self._indigo, self._lib.bingoDeleteRecord(self._id, index)) def searchSub(self, query, options=''): self._indigo._setSessionId() if not options: options = '' return BingoObject(Bingo._checkResult(self._indigo, self._lib.bingoSearchSub(self._id, query.id, options.encode('ascii'))), self._indigo, self) def searchExact(self, query, options=''): self._indigo._setSessionId() if not options: options = '' return BingoObject(Bingo._checkResult(self._indigo, self._lib.bingoSearchExact(self._id, query.id, options.encode('ascii'))), self._indigo, self) def searchSim(self, query, minSim, maxSim, metric='tanimoto'): self._indigo._setSessionId() if not metric: metric = 'tanimoto' return BingoObject( Bingo._checkResult(self._indigo, self._lib.bingoSearchSim(self._id, query.id, minSim, maxSim, metric.encode('ascii'))), self._indigo, self) def searchMolFormula(self, query, options=''): self._indigo._setSessionId() if not options: options = '' return BingoObject(Bingo._checkResult(self._indigo, self._lib.bingoSearchMolFormula(self._id, query.encode('ascii'), options.encode('ascii'))), self._indigo, self) def optimize(self): self._indigo._setSessionId() Bingo._checkResult(self._indigo, self._lib.bingoOptimize(self._id)) def getRecordById (self, id): self._indigo._setSessionId() return IndigoObject(self._indigo, Bingo._checkResult(self._indigo, self._lib.bingoGetRecordObj(self._id, id))) class BingoObject(object): def __init__(self, objId, indigo, bingo): self._id = objId self._indigo = indigo self._bingo = bingo def __del__(self): self.close() def close(self): self._indigo._setSessionId() if self._id >= 0: Bingo._checkResult(self._indigo, self._bingo._lib.bingoEndSearch(self._id)) self._id = -1 def next(self): self._indigo._setSessionId() return (Bingo._checkResult(self._indigo, self._bingo._lib.bingoNext(self._id)) == 1) def getCurrentId(self): self._indigo._setSessionId() return Bingo._checkResult(self._indigo, self._bingo._lib.bingoGetCurrentId(self._id)) def getIndigoObject(self): self._indigo._setSessionId() return IndigoObject(self._indigo, Bingo._checkResult(self._indigo, self._bingo._lib.bingoGetObject(self._id))) def getCurrentSimilarityValue(self): self._indigo._setSessionId() return Bingo._checkResult(self._indigo, self._bingo._lib.bingoGetCurrentSimilarityValue(self._id)) def estimateRemainingResultsCount(self): self._indigo._setSessionId() return Bingo._checkResult(self._indigo, self._bingo._lib.bingoEstimateRemainingResultsCount(self._id)) def estimateRemainingResultsCountError(self): self._indigo._setSessionId() return Bingo._checkResult(self._indigo, self._bingo._lib.bingoEstimateRemainingResultsCountError(self._id)) def estimateRemainingTime(self): self._indigo._setSessionId() value = c_float() Bingo._checkResult(self._indigo, self._bingo._lib.bingoEstimateRemainingTime(self._id, pointer(value))) return value.value Indigo-indigo-1.2.3/api/plugins/bingo/src/000077500000000000000000000000001271037650300203205ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo.cpp000066400000000000000000000371471271037650300221360ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo.h" #include "bingo_object.h" #include "indigo_internal.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "indigo_cpp.h" #include "bingo_internal.h" #include "bingo_index.h" #include "bingo_lock.h" #include #include #include "base_cpp/profiling.h" #include "base_cpp/ptr_array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/exception.h" #include "base_cpp/os_sync_wrapper.h" using namespace indigo; using namespace bingo; // TODO: warning C4273: 'indigo::BingoException::BingoException' : inconsistent dll linkage IMPL_EXCEPTION(indigo, BingoException, "bingo"); static PtrPool _bingo_instances; static OsLock _bingo_lock; static PtrArray _lockers; static PtrPool _searches; static OsLock _searches_lock; static Array _searches_db; static int _bingoCreateOrLoadDatabaseFile (const char *location, const char *options, bool create, const char *type = 0) { MoleculeFingerprintParameters fp_params; fp_params.ext = 0; fp_params.any_qwords = 15; fp_params.ord_qwords = 25; fp_params.tau_qwords = 0; fp_params.sim_qwords = 8; AutoPtr context; std::string loc_dir(location); if (loc_dir.find_last_of('/') != loc_dir.length() - 1) loc_dir += '/'; BaseIndex::IndexType ind_type = BaseIndex::UNKNOWN; if (!create) ind_type = BaseIndex::determineType(location); else if (type && strcmp(type, "molecule") == 0) ind_type = BaseIndex::MOLECULE; else if (type && strcmp(type, "reaction") == 0) ind_type = BaseIndex::REACTION; if (ind_type == BaseIndex::MOLECULE) context.reset(new MoleculeIndex()); else if (ind_type == BaseIndex::REACTION) context.reset(new ReactionIndex()); else throw BingoException("Unknown database type"); int db_id; { OsLocker bingo_locker(_bingo_lock); db_id = _bingo_instances.add(0); } if (create) context->create(loc_dir.c_str(), fp_params, options, db_id); else context->load(loc_dir.c_str(), options, db_id); { OsLocker bingo_locker(_bingo_lock); _bingo_instances[db_id] = context.release(); AutoPtr locker_ptr; locker_ptr.reset(new DatabaseLockData()); _lockers.expand(db_id + 1); _lockers[db_id] = locker_ptr.release(); } return db_id; } static int _insertObjectToDatabase (int db, Indigo &self, Index &bingo_index, IndigoObject &indigo_obj, int obj_id) { profTimerStart(t, "_insertObjectToDatabase"); if (bingo_index.getType() == Index::MOLECULE) { profTimerStart(t1, "_preadd"); if (!IndigoMolecule::is(indigo_obj)) throw BingoException("bingoInsertRecordObj: Only molecule objects can be added to molecule index"); indigo_obj.getBaseMolecule().aromatize(self.arom_options); IndexMolecule ind_mol(indigo_obj.getMolecule()); profTimerStop(t1); int id = bingo_index.add(ind_mol, obj_id, *_lockers[db]); return id; } else if (bingo_index.getType() == Index::REACTION) { if (!IndigoReaction::is(indigo_obj)) throw BingoException("bingoInsertRecordObj: Only reaction objects can be added to reaction index"); indigo_obj.getBaseReaction().aromatize(self.arom_options); IndexReaction ind_rxn(indigo_obj.getReaction()); int id = bingo_index.add(ind_rxn, obj_id, *_lockers[db]); return id; } else throw BingoException("bingoInsertRecordObj: Incorrect database"); return -1; } Matcher& getMatcher (int id) { if (id < _searches.begin() || id >= _searches.end() || !_searches.hasElement(id)) throw BingoException("Incorrect search object id=%d", id); return *_searches[id]; } CEXPORT const char * bingoVersion () { return BINGO_VERSION; } CEXPORT int bingoCreateDatabaseFile (const char *location, const char *type, const char *options) { INDIGO_BEGIN { return _bingoCreateOrLoadDatabaseFile(location, options, true, type); } INDIGO_END(-1); } CEXPORT int bingoLoadDatabaseFile (const char *location, const char *options) { INDIGO_BEGIN { return _bingoCreateOrLoadDatabaseFile(location, options, false); } INDIGO_END(-1); } CEXPORT int bingoCloseDatabase (int db) { BINGO_BEGIN_DB(db) { _bingo_instances.remove(db); return 1; } BINGO_END(-1); } CEXPORT int bingoInsertRecordObj (int db, int obj) { BINGO_BEGIN_DB(db) { IndigoObject &indigo_obj = self.getObject(obj); Index &bingo_index = _bingo_instances.ref(db); long obj_id = -1; auto& properties = indigo_obj.getProperties(); const char *key_name = bingo_index.getIdPropertyName(); if (key_name != 0 && properties.contains(key_name)) { obj_id = strtol(properties.at(key_name), NULL, 10); } return _insertObjectToDatabase (db, self, bingo_index, indigo_obj, obj_id); } BINGO_END(-1); } CEXPORT int bingoInsertRecordObjWithId (int db, int obj, int id) { BINGO_BEGIN_DB(db) { IndigoObject &indigo_obj = self.getObject(obj); Index &bingo_index = _bingo_instances.ref(db); return _insertObjectToDatabase (db, self, bingo_index, indigo_obj, id); } BINGO_END(-1); } CEXPORT int bingoDeleteRecord (int db, int id) { BINGO_BEGIN_DB(db) { Index &bingo_index = _bingo_instances.ref(db); WriteLock wlock(*_lockers[db]); bingo_index.remove(id); return id; } BINGO_END(-1); } CEXPORT int bingoGetRecordObj (int db, int id) { BINGO_BEGIN_DB(db) { Index &bingo_index = _bingo_instances.ref(db); ReadLock rlock(*_lockers[db]); int cf_len; const byte * cf_buf = bingo_index.getObjectCf(id, cf_len); int indigo_obj_id = -1; BufferScanner buf_scn(cf_buf, cf_len); if (bingo_index.getType() == Index::MOLECULE) { AutoPtr molptr(new IndigoMolecule()); Molecule &mol = molptr->mol; CmfLoader cmf_loader(buf_scn); cmf_loader.loadMolecule(mol); indigo_obj_id = self.addObject(molptr.release()); } else if (bingo_index.getType() == Index::REACTION) { AutoPtr rxnptr(new IndigoReaction()); Reaction &rxn = rxnptr->rxn; CrfLoader crf_loader(buf_scn); crf_loader.loadReaction(rxn); indigo_obj_id = self.addObject(rxnptr.release()); } else throw BingoException("bingoInsertRecordObj: Incorrect database"); return indigo_obj_id; } BINGO_END(-1); } CEXPORT int bingoOptimize (int db) { BINGO_BEGIN_DB(db) { Index &bingo_index = _bingo_instances.ref(db); WriteLock wlock(*_lockers[db]); bingo_index.optimize(); return 0; } BINGO_END(-1); } CEXPORT int bingoSearchSub (int db, int query_obj, const char *options) { BINGO_BEGIN_DB(db) { IndigoObject &obj = *(self.getObject(query_obj).clone()); if (IndigoQueryMolecule::is(obj)) { obj.getBaseMolecule().aromatize(self.arom_options); AutoPtr query_data(new MoleculeSubstructureQueryData(obj.getQueryMolecule())); MoleculeIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); MoleculeSubMatcher *matcher = dynamic_cast(bingo_index.createMatcher("sub", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else if (IndigoQueryReaction::is(obj)) { obj.getBaseReaction().aromatize(self.arom_options); AutoPtr query_data(new ReactionSubstructureQueryData(obj.getQueryReaction())); ReactionIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); ReactionSubMatcher *matcher = dynamic_cast(bingo_index.createMatcher("sub", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else throw BingoException("bingoSearchSub: only query molecule and query reaction can be set as query object"); } BINGO_END(-1); } CEXPORT int bingoSearchExact (int db, int query_obj, const char *options) { BINGO_BEGIN_DB(db) { IndigoObject &obj = *(self.getObject(query_obj).clone()); if (IndigoMolecule::is(obj)) { obj.getBaseMolecule().aromatize(self.arom_options); AutoPtr query_data(new MoleculeExactQueryData(obj.getMolecule())); MoleculeIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); MolExactMatcher *matcher = dynamic_cast(bingo_index.createMatcher("exact", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else if (IndigoReaction::is(obj)) { obj.getBaseReaction().aromatize(self.arom_options); AutoPtr query_data(new ReactionExactQueryData(obj.getReaction())); ReactionIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); RxnExactMatcher *matcher = dynamic_cast(bingo_index.createMatcher("exact", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else throw BingoException("bingoSearchExact: only non-query molecules and reactions can be set as query object"); } BINGO_END(-1); } CEXPORT int bingoSearchMolFormula (int db, const char *query, const char *options) { BINGO_BEGIN_DB(db) { Array gross_str; gross_str.copy(query, (int)(strlen(query) + 1)); AutoPtr query_data(new GrossQueryData(gross_str)); BaseIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); MolGrossMatcher *matcher = dynamic_cast(bingo_index.createMatcher("formula", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } BINGO_END(-1); } CEXPORT int bingoSearchSim (int db, int query_obj, float min, float max, const char *options) { BINGO_BEGIN_DB(db) { IndigoObject &obj = *(self.getObject(query_obj).clone()); if (IndigoMolecule::is(obj)) { obj.getBaseMolecule().aromatize(self.arom_options); AutoPtr query_data(new MoleculeSimilarityQueryData(obj.getMolecule(), min, max)); MoleculeIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); MoleculeSimMatcher *matcher = dynamic_cast(bingo_index.createMatcher("sim", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else if (IndigoReaction::is(obj)) { obj.getBaseReaction().aromatize(self.arom_options); AutoPtr query_data(new ReactionSimilarityQueryData(obj.getReaction(), min, max)); ReactionIndex &bingo_index = dynamic_cast(_bingo_instances.ref(db)); ReactionSimMatcher *matcher = dynamic_cast(bingo_index.createMatcher("sim", query_data.release(), options)); int search_id; { OsLocker searches_locker(_searches_lock); search_id = _searches.add(matcher); _searches_db.expand(search_id + 1); _searches_db[search_id] = db; } return search_id; } else throw BingoException("bingoSearchSub: only query molecule and query reaction can be set as query object"); } BINGO_END(-1); } CEXPORT int bingoEndSearch (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { // Ensure that such matcher exists OsLocker searches_locker(_searches_lock); getMatcher(search_obj); _searches.remove(search_obj); return 1; } BINGO_END(-1); } CEXPORT int bingoNext (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { ReadLock rlock(*_lockers[ _searches_db[search_obj] ]); return getMatcher(search_obj).next(); } BINGO_END(-1); } CEXPORT int bingoGetCurrentId (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { return getMatcher(search_obj).currentId(); } BINGO_END(-1); } CEXPORT float bingoGetCurrentSimilarityValue (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { return getMatcher(search_obj).currentSimValue(); } BINGO_END(-1); } CEXPORT int bingoEstimateRemainingResultsCount (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { int delta; return getMatcher(search_obj).esimateRemainingResultsCount(delta); } BINGO_END(-1); } CEXPORT int bingoEstimateRemainingResultsCountError (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { int delta; getMatcher(search_obj).esimateRemainingResultsCount(delta); return delta; } BINGO_END(-1); } CEXPORT int bingoEstimateRemainingTime (int search_obj, float *time_sec) { BINGO_BEGIN_SEARCH(search_obj) { float delta; *time_sec = getMatcher(search_obj).esimateRemainingTime(delta); return 1; } BINGO_END(-1); } CEXPORT int bingoGetObject (int search_obj) { BINGO_BEGIN_SEARCH(search_obj) { Matcher &matcher = getMatcher(search_obj); const Index &bingo_index = matcher.getIndex(); return self.addObject(matcher.currentObject()); } BINGO_END(-1); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_base_index.cpp000066400000000000000000000354461271037650300243170ustar00rootroot00000000000000#include "bingo_base_index.h" #include "bingo_mmf.h" #include "bingo_ptr.h" #include #include #include #include "base_cpp/profiling.h" #include "base_cpp/output.h" #include "base_c/os_dir.h" using namespace bingo; static const char *_cf_data_filename = "cf_data"; static const char *_cf_offset_filename = "cf_offset"; static const char *_id_mapping_filename = "id_mapping"; static const char *_reaction_type = "reaction_" BINGO_VERSION; static const char *_molecule_type = "molecule_" BINGO_VERSION; static const int _type_len = 30; static const char *_mmf_file = "mmf_storage"; static const char *_version_prop = "version"; static const char *_read_only_prop = "read_only"; static const char *_max_mmf_size_prop = "mmf_size"; static const char *_min_mmf_size_prop = "first_mmf_size"; static const char *_mt_size_prop = "mt_size"; static const char *_id_key_prop = "key"; static const size_t _min_mmf_size = 33554432; // 32Mb static const size_t _max_mmf_size = 536870912; // 500Mb static const int _small_base_size = 10000; static const int _sim_mt_size = 50000; BaseIndex::BaseIndex (IndexType type) { _type = type; _read_only = false; _index_id = -1; } void BaseIndex::create (const char *location, const MoleculeFingerprintParameters &fp_params, const char *options, int index_id) { // TODO: introduce global parameters table, local parameters table and constants MMFStorage::setDatabaseId(index_id); int sub_block_size = 8192; int sim_block_size = 8192; int cf_block_size = 1048576; osDirCreate(location); _location = location; std::string _cf_data_path = _location + _cf_data_filename; std::string _cf_offset_path = _location + _cf_offset_filename; std::string _mapping_path = _location + _id_mapping_filename; std::string _mmf_path = _location + _mmf_file; _fp_params = fp_params; std::map option_map; Properties::parseOptions(options, option_map); _checkOptions(option_map, true); _read_only = _getAccessType(option_map); size_t min_mmf_size = _getMinMMfSize(option_map); size_t max_mmf_size = _getMaxMMfSize(option_map); if (_type == MOLECULE) _mmf_storage.create(_mmf_path.c_str(), min_mmf_size, max_mmf_size, _molecule_type, index_id); else if (_type == REACTION) _mmf_storage.create(_mmf_path.c_str(), min_mmf_size, max_mmf_size, _reaction_type, index_id); else throw Exception("incorrect index type"); _header.allocate(); _header->properties_offset = Properties::create(_properties); _saveProperties(fp_params, sub_block_size, sim_block_size, cf_block_size, option_map); _properties->add(_version_prop, BINGO_VERSION); unsigned long prop_mt_size = _properties->getULongNoThrow("mt_size"); int mt_size = (prop_mt_size != ULONG_MAX ? prop_mt_size : _sim_mt_size); _mappingCreate(); _header->cf_offset = ByteBufferStorage::create(_cf_storage, cf_block_size); _header->sub_offset = TranspFpStorage::create(_sub_fp_storage, _fp_params.fingerprintSize(), sub_block_size, _small_base_size); _header->sim_offset = SimStorage::create(_sim_fp_storage, _fp_params.fingerprintSizeSim(), mt_size, _small_base_size); _header->exact_offset = ExactStorage::create(_exact_storage); _header->gross_offset = GrossStorage::create(_gross_storage, cf_block_size); _header->first_free_id = 0; _header->object_count = 0; } void BaseIndex::load (const char *location, const char *options, int index_id) { MMFStorage::setDatabaseId(index_id); if (osDirExists(location) == OS_DIR_NOTFOUND) throw Exception("database directory missed"); osDirCreate(location); _location = location; std::string _cf_data_path = _location + _cf_data_filename; std::string _cf_offset_path = _location + _cf_offset_filename; std::string _mapping_path = _location + _id_mapping_filename; std::string _mmf_path = _location + _mmf_file; std::map option_map; Properties::parseOptions(options, option_map); _checkOptions(option_map, false); _read_only = _getAccessType(option_map); BingoPtr h_ptr; _mmf_storage.load(_mmf_path.c_str(), h_ptr, index_id, _read_only); _header = BingoPtr<_Header>(BingoAddr(0, MMFStorage::max_header_len + BingoAllocator::getAllocatorDataSize())); Properties::load(_properties, _header->properties_offset); const char *ver = _properties->get(_version_prop); if (strcmp(ver, BINGO_VERSION) != 0) throw Exception("BaseIndex: load(): incorrect database version"); const char *type_str = (_type == MOLECULE ? _molecule_type : _reaction_type); if (strcmp(_properties->get("base_type"), type_str) != 0) throw Exception("Loading databse: wrong type propety"); _fp_params.ext = (_properties.ref().getULong("fp_ext") != 0); _fp_params.ord_qwords = _properties.ref().getULong("fp_ord"); _fp_params.any_qwords = _properties.ref().getULong("fp_any"); _fp_params.tau_qwords = _properties.ref().getULong("fp_tau"); _fp_params.sim_qwords = _properties.ref().getULong("fp_sim"); //unsigned long cf_block_size = _properties->getULong("cf_block_size"); _mappingLoad(); SimStorage::load(_sim_fp_storage, _header.ptr()->sim_offset); ExactStorage::load(_exact_storage, _header.ptr()->exact_offset); TranspFpStorage::load(_sub_fp_storage, _header.ptr()->sub_offset); ByteBufferStorage::load(_cf_storage, _header.ptr()->cf_offset); GrossStorage::load(_gross_storage, _header.ptr()->gross_offset); } int BaseIndex::add (/* const */ IndexObject &obj, int obj_id, DatabaseLockData &lock_data) { if (_read_only) throw Exception("insert fail: Read only index can't be changed"); BingoMapping & back_id_mapping = _back_id_mapping_ptr.ref(); { WriteLock wlock(lock_data); if (obj_id != -1 && back_id_mapping.get(obj_id) != (size_t)-1) throw Exception("insert fail: This id was already used"); } _ObjectIndexData _obj_data; { profTimerStart(t_in, "prepare_obj_data"); _prepareIndexData(obj, _obj_data); } WriteLock wlock(lock_data); profTimerStart(t_after, "exclusive_write"); { profTimerStart(t_in, "add_obj_data"); _insertIndexData(_obj_data); } { profTimerStart(t_in, "mapping_changing_1"); if (obj_id == -1) { int i = _header->first_free_id; while (back_id_mapping.get(i) != (size_t)-1) i++; _header->first_free_id = i; obj_id = _header->first_free_id; } } int base_id = _header->object_count; _header->object_count++; { profTimerStart(t_in, "mapping_changing_2"); _mappingAdd(obj_id, base_id); } return obj_id; } void BaseIndex::optimize () { if (_read_only) throw Exception("optimize fail: Read only index can't be changed"); _sim_fp_storage.ptr()->optimize(); } void BaseIndex::remove (int obj_id) { if (_read_only) throw Exception("remove fail: Read only index can't be changed"); BingoMapping & back_id_mapping = _back_id_mapping_ptr.ref(); if (obj_id < 0 || back_id_mapping.get(obj_id) == (size_t)-1) throw Exception("There is no object with this id"); _cf_storage->remove(back_id_mapping.get(obj_id)); _mappingRemove(obj_id); } const MoleculeFingerprintParameters & BaseIndex::getFingerprintParams () const { return _fp_params; } TranspFpStorage & BaseIndex::getSubStorage () { return _sub_fp_storage.ref(); } SimStorage & BaseIndex::getSimStorage () { return _sim_fp_storage.ref(); } ExactStorage & BaseIndex::getExactStorage () { return _exact_storage.ref(); } GrossStorage & BaseIndex::getGrossStorage () { return _gross_storage.ref(); } BingoArray & BaseIndex::getIdMapping () { return _id_mapping_ptr.ref(); } BingoMapping & BaseIndex::getBackIdMapping () { return _back_id_mapping_ptr.ref(); } /*const */ByteBufferStorage & BaseIndex::getCfStorage ()// const { return _cf_storage.ref(); } int BaseIndex::getObjectsCount () const { return _header->object_count; } const byte * BaseIndex::getObjectCf (int id, int &len) { const byte *cf_buf = _cf_storage->get(_back_id_mapping_ptr.ref().get(id), len); if (len == -1) throw Exception("There is no object with this id"); return cf_buf; } const char * BaseIndex::getIdPropertyName () { return _properties.ref().getNoThrow(_id_key_prop); } const char * BaseIndex::getVersion () { return BINGO_VERSION; } Index::IndexType BaseIndex::getType () const { return _type; } Index::IndexType BaseIndex::determineType (const char *location) { std::string path(location); path += '/'; path += _mmf_file; path += '0'; std::ifstream file(path, std::ios::binary | std::ios::in); //bool res = file.good(); char type[_type_len]; file.seekg(0); file.read(type, _type_len); if (strcmp(type, _molecule_type) == 0) return MOLECULE; else if (strcmp(type, _reaction_type) == 0) return REACTION; else throw Exception("BingoIndex: determineType(): Database format is not compatible with this version."); } BaseIndex::~BaseIndex() { _mmf_storage.close(); } void BaseIndex::_checkOptions (std::map &option_map, bool is_create) { for(std::map::iterator it = option_map.begin(); it != option_map.end(); it++) { if (is_create) { if ((it->first.compare(_read_only_prop) != 0) && (it->first.compare(_mt_size_prop) != 0) && (it->first.compare(_min_mmf_size_prop) != 0) && (it->first.compare(_max_mmf_size_prop) != 0) && (it->first.compare(_id_key_prop) != 0)) throw Exception("Creating index error: incorrect input options"); } else if ((it->first.compare(_read_only_prop)) != 0 && (it->first.compare(_id_key_prop) != 0)) throw Exception("Loading index error: incorrect input options"); } } size_t BaseIndex::_getMinMMfSize (std::map &option_map) { size_t mmf_size = _min_mmf_size; if (option_map.find(_min_mmf_size_prop) != option_map.end()) { unsigned long u_dec; std::istringstream isstr(option_map[_min_mmf_size_prop]); isstr >> u_dec; if (u_dec != ULONG_MAX) mmf_size = u_dec * 1048576; if (mmf_size < _min_mmf_size) mmf_size = _min_mmf_size; } return mmf_size; } size_t BaseIndex::_getMaxMMfSize (std::map &option_map) { size_t mmf_size = _max_mmf_size; if (option_map.find(_max_mmf_size_prop) != option_map.end()) { unsigned long u_dec; std::istringstream isstr(option_map[_max_mmf_size_prop]); isstr >> u_dec; if (u_dec != ULONG_MAX) mmf_size = u_dec * 1048576; if (mmf_size < _min_mmf_size) mmf_size = _min_mmf_size; } return mmf_size; } bool BaseIndex::_getAccessType (std::map &option_map) { if (option_map.find("read_only") != option_map.end()) { if (option_map["read_only"].compare("true") == 0) return true; } return false; } void BaseIndex::_saveProperties (const MoleculeFingerprintParameters &fp_params, int sub_block_size, int sim_block_size, int cf_block_size, std::map &option_map) { _properties.ref().add("base_type", (_type == MOLECULE ? _molecule_type : _reaction_type)); _properties.ref().add("fp_ext", _fp_params.ext); _properties.ref().add("fp_ord", _fp_params.ord_qwords); _properties.ref().add("fp_any", _fp_params.any_qwords); _properties.ref().add("fp_tau", _fp_params.tau_qwords); _properties.ref().add("fp_sim", _fp_params.sim_qwords); _properties.ref().add("cf_block_size", cf_block_size); std::map::iterator it; for (it = option_map.begin(); it != option_map.end(); it++) { _properties->add(it->first.c_str(), it->second.c_str()); } } bool BaseIndex::_prepareIndexData (IndexObject &obj, _ObjectIndexData &obj_data) { { profTimerStart(t, "prepare_cf"); if (!obj.buildCfString(obj_data.cf_str)) return false; } { profTimerStart(t, "prepare_formula"); if (!obj.buildGrossString(obj_data.gross_str)) return false; } { profTimerStart(t, "prepare_fp"); if (!obj.buildFingerprint(_fp_params, &obj_data.sub_fp, &obj_data.sim_fp)) return false; } if (!obj.buildHash(obj_data.hash)) return false; return true; } void BaseIndex::_insertIndexData (_ObjectIndexData &obj_data) { _sub_fp_storage.ptr()->add(obj_data.sub_fp.ptr()); _sim_fp_storage.ptr()->add(obj_data.sim_fp.ptr(), _header->object_count); _cf_storage.ptr()->add((byte *)obj_data.cf_str.ptr(), obj_data.cf_str.size(), _header->object_count); _exact_storage.ptr()->add(obj_data.hash, _header->object_count); _gross_storage.ptr()->add(obj_data.gross_str, _header->object_count); } void BaseIndex::_mappingLoad () { _id_mapping_ptr = BingoPtr< BingoArray >(_header->mapping_offset); _back_id_mapping_ptr = BingoPtr< BingoMapping >(_header->back_mapping_offset); return; } void BaseIndex::_mappingCreate () { _id_mapping_ptr.allocate(); new(_id_mapping_ptr.ptr()) BingoArray(); _header->mapping_offset = (BingoAddr)_id_mapping_ptr; _back_id_mapping_ptr.allocate(); new(_back_id_mapping_ptr.ptr()) BingoMapping(); _header->back_mapping_offset = (BingoAddr)_back_id_mapping_ptr; } void BaseIndex::_mappingAssign (int obj_id, int base_id) { BingoArray & id_mapping = _id_mapping_ptr.ref(); BingoMapping & back_id_mapping = _back_id_mapping_ptr.ref(); int old_size = id_mapping.size(); if (id_mapping.size() <= base_id) id_mapping.resize(base_id + 1); for (int i = old_size; i < id_mapping.size(); i++) id_mapping[i] = -1; id_mapping[base_id] = obj_id; if (obj_id == -1) return; if (back_id_mapping.get(obj_id) != (size_t)-1) throw Exception("insert fail: this id was already used"); back_id_mapping.add(obj_id, base_id); } void BaseIndex::_mappingAdd (int obj_id, int base_id) { _mappingAssign(obj_id, base_id); } void BaseIndex::_mappingRemove (int obj_id) { //BingoArray & id_mapping = _id_mapping_ptr.ref(); BingoMapping & back_id_mapping = _back_id_mapping_ptr.ref(); back_id_mapping.remove(obj_id); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_base_index.h000066400000000000000000000113761271037650300237600ustar00rootroot00000000000000#ifndef __bingo_base_index__ #define __bingo_base_index__ #include "molecule/molecule_fingerprint.h" #include "bingo_object.h" #include "bingo_fp_storage.h" #include "bingo_cf_storage.h" #include "bingo_mmf_storage.h" #include "bingo_mapping.h" #include "bingo_properties.h" #include "bingo_exact_storage.h" #include "bingo_gross_storage.h" #include "bingo_sim_storge.h" #include "bingo_lock.h" #define BINGO_VERSION "v0.72" using namespace indigo; namespace bingo { class Matcher; class MatcherQueryData; class Index { public: typedef enum {MOLECULE, REACTION, UNKNOWN} IndexType; virtual Matcher* createMatcher (const char *type, MatcherQueryData *query_data, const char *options) = 0; virtual void create (const char *location, const MoleculeFingerprintParameters &fp_params, const char *options, int index_id) = 0; virtual void load (const char *location, const char *options, int index_id) = 0; virtual int add (IndexObject &obj, int obj_id, DatabaseLockData &lock_data) = 0; virtual void optimize () = 0; virtual void remove (int id) = 0; virtual const byte * getObjectCf (int id, int &len) = 0; virtual const char * getIdPropertyName () = 0; virtual const char * getVersion () = 0; virtual IndexType getType () const = 0; virtual ~Index () {}; }; class BaseIndex : public Index { private: struct _Header { BingoAddr properties_offset; BingoAddr mapping_offset; BingoAddr back_mapping_offset; BingoAddr cf_offset; BingoAddr sub_offset; BingoAddr sim_offset; BingoAddr exact_offset; BingoAddr gross_offset; int object_count; int first_free_id; }; public: virtual void create (const char *location, const MoleculeFingerprintParameters &fp_params, const char *options, int index_id); virtual void load (const char *location, const char *options, int index_id); virtual int add (IndexObject &obj, int obj_id, DatabaseLockData &lock_data); virtual void optimize (); virtual void remove (int id); const MoleculeFingerprintParameters & getFingerprintParams () const; TranspFpStorage & getSubStorage (); SimStorage & getSimStorage (); ExactStorage & getExactStorage (); GrossStorage & getGrossStorage (); BingoArray & getIdMapping (); BingoMapping & getBackIdMapping (); ByteBufferStorage & getCfStorage (); int getObjectsCount () const; virtual const byte * getObjectCf (int id, int &len); virtual const char * getIdPropertyName (); virtual const char * getVersion (); virtual IndexType getType () const; static IndexType determineType (const char *location); virtual ~BaseIndex (); protected: BaseIndex (IndexType type); IndexType _type; bool _read_only; private: struct _ObjectIndexData { Array sub_fp; Array sim_fp; Array cf_str; Array gross_str; dword hash; }; MMFStorage _mmf_storage; BingoPtr<_Header> _header; BingoPtr< BingoArray > _id_mapping_ptr; BingoPtr _back_id_mapping_ptr; BingoPtr _sub_fp_storage; BingoPtr _sim_fp_storage; BingoPtr _exact_storage; BingoPtr _gross_storage; BingoPtr _cf_storage; BingoPtr _properties; MoleculeFingerprintParameters _fp_params; std::string _location; int _index_id; static void _checkOptions (std::map &option_map, bool is_create); static size_t _getMinMMfSize (std::map &option_map); static size_t _getMaxMMfSize (std::map &option_map); static bool _getAccessType (std::map &option_map); void _saveProperties (const MoleculeFingerprintParameters &fp_params, int sub_block_size, int sim_block_size, int cf_block_size, std::map &option_map); bool _prepareIndexData (IndexObject &obj, _ObjectIndexData &obj_data); void _insertIndexData(_ObjectIndexData &obj_data); void _mappingCreate (); void _mappingLoad (); void _mappingAssign (int obj_id, int base_id); void _mappingAdd (int obj_id, int base_id); void _mappingRemove (int obj_id); }; }; #endif // __bingo_base_index__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_cell_container.h000066400000000000000000000013531271037650300246320ustar00rootroot00000000000000#ifndef __cell_container__ #define __cell_container__ #include "base_cpp/d_bitset.h" #include "bingo_sim_coef.h" #include "bingo_ptr.h" using namespace indigo; namespace bingo { class CellContainer { protected: int _min_fp_bit_number; int _max_fp_bit_number; int _fp_size; public: CellContainer (int fp_size) : _min_fp_bit_number(-1), _max_fp_bit_number(-1), _fp_size(fp_size) { } virtual void build (BingoPtr fingerprints, int fp_count, int min_fp_bit_number, int max_fp_bit_number) = 0; virtual void findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices) = 0; }; }; #endif /* _cell_container_ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_cf_storage.cpp000066400000000000000000000031561271037650300243230ustar00rootroot00000000000000#include "bingo_cf_storage.h" using namespace bingo; ByteBufferStorage::ByteBufferStorage (int block_size) : _block_size(block_size) { _free_pos = 0; } BingoAddr ByteBufferStorage::create (BingoPtr &cf_ptr, int block_size) { cf_ptr.allocate(); new(cf_ptr.ptr()) ByteBufferStorage(block_size); return (BingoAddr)cf_ptr; } void ByteBufferStorage::load (BingoPtr &cf_ptr, BingoAddr offset) { cf_ptr = BingoPtr(offset); } const byte * ByteBufferStorage::get (int idx, int &len) { if (_addresses.size() <= idx) throw Exception("ByteBufferStorage: incorrect buffer id"); if (_addresses[idx].len < 0) { len = -1; return 0; } len = _addresses[idx].len; return _blocks[_addresses[idx].block_idx].ptr() + _addresses[idx].offset; } void ByteBufferStorage::add (const byte *data, int len, int idx) { if ((_blocks.size() == 0) || (_block_size - _free_pos < len)) { BingoPtr &new_block = _blocks.push(); new_block.allocate(_block_size); _free_pos = 0; } if (_addresses.size() <= idx) _addresses.resize(idx + 1); _addresses[idx].block_idx = _blocks.size() - 1; _addresses[idx].len = len; _addresses[idx].offset = _free_pos; memcpy(_blocks.top().ptr() + _free_pos, data, len); _free_pos += len; } void ByteBufferStorage::remove (int idx) { if (_addresses.size() <= idx) throw Exception("ByteBufferStorage: incorrect buffer id"); _addresses[idx].len = -1; } ByteBufferStorage::~ByteBufferStorage() { } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_cf_storage.h000066400000000000000000000020511271037650300237610ustar00rootroot00000000000000#ifndef __cf_storage__ #define __cf_storage__ #include "base_cpp/obj_array.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/tlscont.h" #include "bingo_ptr.h" #include #include #include #include using namespace indigo; namespace bingo { class ByteBufferStorage { public: ByteBufferStorage (int block_size); static BingoAddr create (BingoPtr &cf_ptr, int block_size); static void load (BingoPtr &cf_ptr, BingoAddr offset); const byte * get (int idx, int &len); void add (const byte *data, int len, int idx); void remove (int idx); ~ByteBufferStorage(); private: struct _Addr { unsigned long block_idx; unsigned long offset; long len; }; int _block_size; int _free_pos; BingoArray< BingoPtr > _blocks; BingoArray<_Addr> _addresses; }; }; #endif /* __cf_storage__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_container_set.cpp000066400000000000000000000130061271037650300250370ustar00rootroot00000000000000#include "bingo_container_set.h" #include "bingo_multibit_tree.h" using namespace bingo; ContainerSet::ContainerSet() { _inc_count = 0; _inc_total_ones_count = 0; } void ContainerSet::setParams( int fp_size, int container_size, int min_ones_count, int max_ones_count) { profTimerStart(t, "cs_set_params"); _fp_size = fp_size; _min_ones_count = min_ones_count; _max_ones_count = max_ones_count; _container_size = container_size; _increment.allocate(_container_size * _fp_size); _indices.allocate(_container_size); } int ContainerSet::getContCount() const { return _set.size() + 1; } int ContainerSet::getMinBorder() const { return _min_ones_count; } int ContainerSet::getMaxBorder() const { return _max_ones_count; } bool ContainerSet::add (const byte *fingerprint, int id, int fp_ones_count) { if (_inc_count == _container_size) throw Exception("ContainerSet: Increment is full"); byte *inc = _increment.ptr(); int *indices = _indices.ptr(); memcpy(inc + _inc_count * _fp_size, fingerprint, _fp_size); indices[_inc_count] = id; _inc_total_ones_count += (fp_ones_count == -1 ? bitGetOnesCount(fingerprint, _fp_size) : fp_ones_count); _inc_count++; if (_inc_count == _container_size) return true; return false; } void ContainerSet::buildContainer () { profIncCounter("trees_count", 1); MultibitTree &cont = _set.push(_fp_size); cont.build(_increment, _indices, _container_size, _min_ones_count, _max_ones_count); _increment.allocate(_container_size * _fp_size); _indices.allocate(_container_size); _inc_count = 0; } void ContainerSet::splitSet (ContainerSet &new_set) { if (_set.size() > 0) throw Exception("ContainerSet: Set with built containers can't be splited"); int new_border = (_inc_total_ones_count / _inc_count) + 1; int inc_count_cur = 0; new_set._inc_count = 0; _inc_total_ones_count = 0; new_set._inc_total_ones_count = 0; for (int i = 0; i < _inc_count; i++) { int ones_count = bitGetOnesCount(_increment.ptr() + i * _fp_size, _fp_size); if (ones_count < new_border) { memcpy(_increment.ptr() + inc_count_cur * _fp_size, _increment.ptr() + i * _fp_size, _fp_size); _indices[inc_count_cur] = (int)_indices[i]; inc_count_cur++; _inc_total_ones_count += ones_count; } else { memcpy(new_set._increment.ptr() + new_set._inc_count * _fp_size, _increment.ptr() + i * _fp_size, _fp_size); new_set._indices[new_set._inc_count] = (int)_indices[i]; new_set._inc_count++; new_set._inc_total_ones_count += ones_count; } } new_set._min_ones_count = new_border; new_set._max_ones_count = _max_ones_count; _max_ones_count = new_border - 1; _inc_count = inc_count_cur; } void ContainerSet::findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_indices) { sim_indices.clear(); static int idx = 0; //int query_bit_number = bitGetOnesCount(query, _fp_size); QS_DEF(Array, cell_sim_indices); for (int i = 0; i < _set.size(); i++) { MultibitTree &container = _set[i]; cell_sim_indices.clear(); container.findSimilar(query, sim_coef, min_coef, cell_sim_indices); sim_indices.concat(cell_sim_indices); } cell_sim_indices.clear(); _findSimilarInc(query, sim_coef, min_coef, cell_sim_indices); sim_indices.concat(cell_sim_indices); idx++; } void ContainerSet::optimize() { if (_inc_count < _container_size / 10) return; profIncCounter("trees_count", 1); MultibitTree &cont = _set.push(_fp_size); cont.build(_increment, _indices, _inc_count, _min_ones_count, _max_ones_count); _increment.allocate(_container_size * _fp_size); _indices.allocate(_container_size); _inc_count = 0; } int ContainerSet::getSimilar( const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cont_idx ) { profTimerStart(cs_s, "getSimilar"); if (cont_idx >= getContCount()) throw Exception("ContainerSet: Incorrect container index"); if (cont_idx == _set.size()) { { profTimerStart(cs_s, "inc_findSimilar"); _findSimilarInc(query, sim_coef, min_coef, sim_fp_indices); profIncCounter("inc_findSimilar_count", sim_fp_indices.size()); } return sim_fp_indices.size(); } MultibitTree &container = _set[cont_idx]; { profTimerStart(cs_s, "set_findSimilar"); container.findSimilar(query, sim_coef, min_coef, sim_fp_indices); profIncCounter("set_findSimilar_count", sim_fp_indices.size()); } return sim_fp_indices.size(); } int ContainerSet::_findSimilarInc (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_indices) { byte *inc = _increment.ptr(); int *indices = _indices.ptr(); sim_indices.clear(); int query_bit_number = bitGetOnesCount(query, _fp_size); for (int i = 0; i < _inc_count; i++) { byte *fp = inc + i * _fp_size; int fp_bit_number = bitGetOnesCount(fp, _fp_size); double coef = sim_coef.calcCoef(fp, query, query_bit_number, fp_bit_number); if (coef < min_coef) continue; sim_indices.push(SimResult(indices[i], (float)coef)); } return sim_indices.size(); }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_container_set.h000066400000000000000000000027661271037650300245170ustar00rootroot00000000000000#ifndef __container_set__ #define __container_set__ #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "bingo_cell_container.h" #include "bingo_multibit_tree.h" #include "bingo_ptr.h" #include #include "base_cpp/profiling.h" using namespace indigo; namespace bingo { class ContainerSet { public: ContainerSet (); void setParams (int fp_size, int container_size, int min_ones_count, int max_ones_count); int getContCount () const; int getMinBorder () const; int getMaxBorder () const; bool add (const byte *fingerprint, int id, int fp_ones_count = -1); void buildContainer (); void splitSet (ContainerSet &new_set); void findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_indices); void optimize (); int getSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cont_idx); private: BingoArray _set; int _fp_size; int _container_size; BingoPtr _increment; BingoPtr _indices; int _inc_count; int _inc_total_ones_count; int _min_ones_count; int _max_ones_count; int _findSimilarInc (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_indices); }; }; #endif /* __container_set__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_euclid_coef.cpp000066400000000000000000000020031271037650300244360ustar00rootroot00000000000000#include "bingo_euclid_coef.h" using namespace bingo; EuclidCoef::EuclidCoef( int fp_size ) : _fp_size(fp_size) { } double EuclidCoef::calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ) { int common_bits = bitCommonOnes(target, query, _fp_size); if (target_bit_count == -1) target_bit_count = bitGetOnesCount(target, _fp_size); return (double)common_bits / target_bit_count; } double EuclidCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ) { int min = (query_bit_count < max_target_bit_count ? query_bit_count : max_target_bit_count); return (double)min / query_bit_count; } double EuclidCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ) { int max_a = max_target_bit_count - m10; int b = query_bit_count - m01; int min = (b > max_a ? max_a : b); return (double)min / min_target_bit_count; }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_euclid_coef.h000066400000000000000000000012261271037650300241110ustar00rootroot00000000000000#ifndef __euclid_coef__ #define __euclid_coef__ #include "bingo_sim_coef.h" #include #include "base_c/bitarray.h" namespace bingo { class EuclidCoef : public SimCoef { public: EuclidCoef (int fp_size); double calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ); private: int _fp_size; }; }; #endif /* __euclid_coef__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_exact_storage.cpp000066400000000000000000000056431271037650300250420ustar00rootroot00000000000000#include "bingo_exact_storage.h" #include "molecule/elements.h" #include "base_cpp/crc32.h" #include "bingo_mmf_storage.h" #include "graph/subgraph_hash.h" using namespace bingo; ExactStorage::ExactStorage () { } BingoAddr ExactStorage::create(BingoPtr &exact_ptr) { exact_ptr.allocate(); new (exact_ptr.ptr()) ExactStorage(); return (BingoAddr)exact_ptr; } void ExactStorage::load (BingoPtr &exact_ptr, BingoAddr offset) { exact_ptr = BingoPtr(offset); } void ExactStorage::add( dword hash, int id ) { _molecule_hashes.add(hash, id); } void ExactStorage::findCandidates (dword query_hash, Array &candidates, int part_id, int part_count) { profTimerStart(tsingle, "exact_filter"); dword first_hash = 0; dword last_hash = (dword)(-1); if (part_id != -1 && part_count != -1) { first_hash = (part_id - 1) * last_hash / part_count; last_hash = part_id * last_hash / part_count; } if (query_hash < first_hash || query_hash > last_hash) return; Array indices; _molecule_hashes.getAll(query_hash, indices); for (int i = 0; i < indices.size(); i++) candidates.push(indices[i]); } dword ExactStorage::calculateMolHash (Molecule &mol) { QS_DEF(Molecule, mol_without_h); QS_DEF(Array, vertices); int i; vertices.clear(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.getAtomNumber(i) != ELEM_H) vertices.push(i); mol_without_h.makeSubmolecule(mol, vertices, 0); QS_DEF(Array, vertex_codes); vertex_codes.clear_resize(mol_without_h.vertexEnd()); SubgraphHash hh(mol_without_h); for (int v = mol_without_h.vertexBegin(); v != mol_without_h.vertexEnd(); v = mol_without_h.vertexNext(v)) vertex_codes[v] = _vertexCode(mol_without_h, v); hh.vertex_codes = &vertex_codes; hh.max_iterations = (mol_without_h.edgeCount() + 1) / 2; return hh.getHash(); } dword ExactStorage::calculateRxnHash (Reaction &rxn) { QS_DEF(Molecule, mol_without_h) ; QS_DEF(Array, vertices); int i, j; dword hash = 0; for (j = rxn.begin(); j != rxn.end(); j = rxn.next(j)) { Molecule &mol = rxn.getMolecule(j); vertices.clear(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.getAtomNumber(i) != ELEM_H) vertices.push(i); mol_without_h.makeSubmolecule(mol, vertices, 0); SubgraphHash hh(mol_without_h); hash += hh.getHash(); } return hash; } int ExactStorage::_vertexCode (Molecule &mol, int vertex_idx) { if (mol.isPseudoAtom(vertex_idx)) return indigo::CRC32::get(mol.getPseudoAtom(vertex_idx)); if (mol.isRSite(vertex_idx)) return ELEM_RSITE; return mol.getAtomNumber(vertex_idx); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_exact_storage.h000066400000000000000000000016161271037650300245030ustar00rootroot00000000000000#ifndef __bingo_exact_storage__ #define __bingo_exact_storage__ #include "molecule/molecule.h" #include "reaction/reaction.h" #include "bingo_ptr.h" #include "bingo_mapping.h" using namespace indigo; namespace bingo { class ExactStorage { public: ExactStorage (); static BingoAddr create(BingoPtr &exact_ptr); static void load (BingoPtr &exact_ptr, BingoAddr offset); size_t getOffset (); void add (dword hash, int id); void findCandidates (dword query_hash, Array &candidates, int part_id = -1, int part_count = -1); static dword calculateMolHash (Molecule &mol); static dword calculateRxnHash (Reaction &rxn); private: BingoMapping _molecule_hashes; static int _vertexCode (Molecule &mol, int vertex_idx); }; } #endif //__bingo_exact_storage__Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_fingerprint_table.cpp000066400000000000000000000122771271037650300257110ustar00rootroot00000000000000#include "bingo_fingerprint_table.h" #include "bingo_container_set.h" #include using namespace bingo; FingerprintTable::FingerprintTable (int fp_size, const Array &borders, int mt_size) : _table(100), _fp_size(fp_size), _mt_size(mt_size) { _table.resize(borders.size() - 1); profTimerStart(tfp, "FingerprintTable constructing"); _max_cell_count = 100; for (int i = 0; i < _table.size(); i++) { { profTimerStart(tfp, "FingerprintTable element pushing"); _table[i].setParams(_fp_size, mt_size, borders[i], borders[i + 1] - 1); } } } BingoAddr FingerprintTable::create (BingoPtr &ptr, int fp_size, int mt_size ) { Array borders; borders.push(0); borders.push(fp_size * 8 + 1); ptr.allocate(); new(ptr.ptr()) FingerprintTable(fp_size, borders, mt_size); return (BingoAddr)ptr; } void FingerprintTable::load (BingoPtr &ptr, BingoAddr offset) { ptr = BingoPtr(offset); } void FingerprintTable::add (const byte *fingerprint, int id) { int fp_bit_count = bitGetOnesCount(fingerprint, _fp_size); for (int i = 0; i < _table.size(); i++) { if ((fp_bit_count >= _table[i].getMinBorder()) && (fp_bit_count <= _table[i].getMaxBorder())) { if (_table[i].add(fingerprint, id)) { if (_table[i].getMinBorder() == _table[i].getMaxBorder() || _table[i].getContCount() > 1 || _table.size() >= _max_cell_count) _table[i].buildContainer(); else { _table.resize(_table.size() + 1); for (int j = _table.size() - 2; j >= i + 1; j--) _table[j + 1] =_table[j]; _table[i + 1].setParams(_fp_size, _mt_size, -1, -1); _table[i].splitSet(_table[i + 1]); } } break; } } } void FingerprintTable::findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices) { sim_fp_indices.clear(); int query_bit_number = bitGetOnesCount(query, _fp_size); QS_DEF(Array, cell_sim_indices); for (int i = 0; i < _table.size(); i++) { if (sim_coef.calcUpperBound(query_bit_number, _table[i].getMinBorder(), _table[i].getMaxBorder()) < min_coef) continue; cell_sim_indices.clear(); _table[i].findSimilar(query, sim_coef, min_coef, cell_sim_indices); sim_fp_indices.concat(cell_sim_indices); } } void FingerprintTable::optimize () { for (int i = 0; i < _table.size(); i++) _table[i].optimize(); } int FingerprintTable::getCellCount () const { return _table.size(); } int FingerprintTable::getCellSize (int cell_idx) const { if (cell_idx >= _table.size()) throw Exception("FingerprintTable: Incorrect cell index"); return _table[cell_idx].getContCount(); } void FingerprintTable::getCellsInterval (const byte *query, SimCoef &sim_coef, double min_coef, int &min_cell, int &max_cell) { min_cell = -1; max_cell = -1; int query_bit_count = bitGetOnesCount(query, _fp_size); for (int i = 0; i < _table.size(); i++) { if ((min_cell == -1) && (sim_coef.calcUpperBound(query_bit_count, _table[i].getMinBorder(), _table[i].getMaxBorder()) > min_coef)) min_cell = i; if ((min_cell != -1)&& (sim_coef.calcUpperBound(query_bit_count, _table[i].getMinBorder(), _table[i].getMaxBorder()) > min_coef)) max_cell = i; } } int FingerprintTable::firstFitCell (int query_bit_count, int min_cell, int max_cell) const { int first_cell = -1; for (int i = min_cell; i <= max_cell; i++) { if (query_bit_count >= _table[i].getMinBorder() && query_bit_count <= _table[i].getMaxBorder()) first_cell = i; } return first_cell; } int FingerprintTable::nextFitCell (int query_bit_count, int first_fit_cell, int min_cell, int max_cell, int idx) const { int next_idx; if (first_fit_cell == idx) next_idx = first_fit_cell + 1; else if (first_fit_cell < idx) next_idx = first_fit_cell - (idx - first_fit_cell); else next_idx = first_fit_cell + (first_fit_cell - idx) + 1; if (next_idx < min_cell || next_idx > max_cell) { if (idx < min_cell || idx > max_cell) return -1; else return nextFitCell(query_bit_count, first_fit_cell, min_cell, max_cell, next_idx ); } return next_idx; } int FingerprintTable::getSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cell_idx, int cont_idx) { if (cell_idx >= _table.size()) throw Exception("FingerprintTable: Incorrect cell index"); int query_bit_number = bitGetOnesCount(query, _fp_size); if (sim_coef.calcUpperBound(query_bit_number, _table[cell_idx].getMinBorder(), _table[cell_idx].getMaxBorder()) < min_coef) return 0; _table[cell_idx].getSimilar( query, sim_coef, min_coef, sim_fp_indices, cont_idx); return sim_fp_indices.size(); } FingerprintTable::~FingerprintTable () { }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_fingerprint_table.h000066400000000000000000000034201271037650300253440ustar00rootroot00000000000000#ifndef __fingerprint_table__ #define __fingerprint_table__ #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "math/algebra.h" #include "time.h" #include "new" #include "bingo_sim_coef.h" #include "bingo_container_set.h" #include "bingo_ptr.h" #include using namespace indigo; namespace bingo { class FingerprintTable { public: FingerprintTable (int fp_size, const Array &borders, int mt_size); static BingoAddr create (BingoPtr &ptr, int fp_size, int mt_size ); static void load (BingoPtr &ptr, BingoAddr offset); void add (const byte *fingerprint, int id); void findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices); void optimize (); int getCellCount () const; int getCellSize (int cell_idx) const; void getCellsInterval (const byte *query, SimCoef &sim_coef, double min_coef, int &min_cell, int &max_cell); int firstFitCell (int query_bit_count, int min_cell, int max_cell ) const; int nextFitCell (int query_bit_count, int first_fit_cell, int min_cell, int max_cell, int idx) const; int getSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cell_idx, int cont_idx); ~FingerprintTable(); private: BingoArray _table; int _max_cell_count; int _fp_size; int _mt_size; BingoPtr< byte > _inc_buffer; BingoPtr< size_t > _inc_id_buffer; int _inc_size; int _inc_fp_count; }; }; #endif /* __fingerprint_table__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_fp_storage.cpp000066400000000000000000000101161271037650300243320ustar00rootroot00000000000000#include "bingo_fp_storage.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/profiling.h" #include #include using namespace bingo; TranspFpStorage::TranspFpStorage (int fp_size, int block_size, int small_base_size) : _fp_size(fp_size), _block_size(block_size) { _pack_count = 0; _storage.resize(fp_size * 8); _block_count = 0; _inc_fp_count = 0; _inc_size = block_size * 8; _small_inc_size = small_base_size; _inc_buffer.allocate(_small_inc_size * _fp_size); _small_flag = true; // Resize bit usage counts information for the all bits _fp_bit_usage_counts.resize(_fp_size * 8); } BingoAddr TranspFpStorage::create (BingoPtr &ptr, int fp_size, int block_size, int small_base_size) { ptr.allocate(); new(ptr.ptr()) TranspFpStorage(fp_size, block_size, small_base_size); return (BingoAddr)ptr; } void TranspFpStorage::load (BingoPtr &ptr, BingoAddr offset) { ptr = BingoPtr(offset); } void TranspFpStorage::add (const byte *fp) { memcpy(_inc_buffer.ptr() + (_inc_fp_count * _fp_size), fp, _fp_size); _inc_fp_count++; if ((_inc_fp_count == _small_inc_size) && _small_flag) { byte * old_inc_ptr = _inc_buffer.ptr(); _inc_buffer.allocate(_inc_size * _fp_size); memcpy(_inc_buffer.ptr(), old_inc_ptr, _small_inc_size * _fp_size); _small_flag = false; } if (_inc_fp_count == _inc_size) { _addIncToStorage(); _inc_fp_count = 0; } } int TranspFpStorage::getBlockSize () const { return _block_size; } const byte * TranspFpStorage::getBlock (int idx) { return _storage[idx].ptr(); } int TranspFpStorage::getBlockCount () const { return _block_count; } const byte *TranspFpStorage::getIncrement () const { return _inc_buffer.ptr(); } int TranspFpStorage::getIncrementSize () const { return _inc_fp_count; } int TranspFpStorage::getIncrementCapacity () const { return _inc_size; } TranspFpStorage::~TranspFpStorage () { } void TranspFpStorage::_addIncToStorage () { profTimerStart(t0, "fp_inc_to_transp"); std::vector block_buf; block_buf.resize(_block_size); //byte inc_mask = 0x80; for (int bit_idx = 0; bit_idx < 8 * _fp_size; bit_idx++) { memset(&block_buf[0], 0, _block_size); for (int fp_idx = 0; fp_idx < _inc_fp_count; fp_idx++) bitSetBit(&block_buf[0], fp_idx, bitGetBit(_inc_buffer.ptr() + fp_idx * _fp_size, bit_idx)); if (_pack_count == 0) { // Update bit usage count _fp_bit_usage_counts[bit_idx] = bitGetOnesCount(&block_buf[0], _block_size); } int block_idx = (_pack_count * _fp_size * 8) + bit_idx; _storage.resize(block_idx + 1); _storage[block_idx].allocate(_block_size); memcpy(_storage[block_idx].ptr(), &block_buf[0], _block_size); _block_count++; } _pack_count++; } /* void TranspFpStorage::create (int fp_size, const char *info_filename) { _createFpStorage(fp_size, storage->getBlockSize() * 8, info_filename); fp_bit_usage_counts.resize(fp_size * 8); fp_bit_usage_counts.zerofill(); } void TranspFpStorage::load (int fp_size, const char *info_filename) { _loadFpStorage(fp_size, storage->getBlockSize() * 8, info_filename); _pack_count = _block_count / (fp_size * 8); fp_bit_usage_counts.resize(fp_size * 8); if (_pack_count > 0) { // Calculate fingerprints frequencies from the first block QS_DEF(Array, _items_mask); _items_mask.resize(storage->getBlockSize()); for (int i = 0; i < fp_size * 8; i++) { getBlock(i, _items_mask.ptr()); fp_bit_usage_counts[i] = bitGetOnesCount(_items_mask.ptr(), _items_mask.size()); } } else fp_bit_usage_counts.zerofill(); }*/ BingoArray& TranspFpStorage::getFpBitUsageCounts () { return _fp_bit_usage_counts; } int TranspFpStorage::getPackCount () const { return _pack_count; }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_fp_storage.h000066400000000000000000000026321271037650300240030ustar00rootroot00000000000000#ifndef __bingo_fp_storage__ #define __bingo_fp_storage__ #include "bingo_ptr.h" #include #include #include namespace bingo { class TranspFpStorage { public: TranspFpStorage (int fp_size, int block_size, int small_base_size); static BingoAddr create (BingoPtr &ptr, int fp_size, int block_size, int small_base_size); static void load (BingoPtr &ptr, BingoAddr offset); void add (const byte *fp); int getBlockSize (void) const; const byte * getBlock (int idx); int getBlockCount () const; const byte *getIncrement () const; int getIncrementSize (void) const; int getIncrementCapacity (void) const; int getPackCount () const; virtual ~TranspFpStorage (); BingoArray &getFpBitUsageCounts (); protected: int _fp_size; int _block_count; int _block_size; int _pack_count; bool _small_flag; BingoArray< BingoPtr< byte > > _storage; BingoPtr< byte > _inc_buffer; int _inc_size; int _inc_fp_count; int _small_inc_size; BingoArray _fp_bit_usage_counts; void _createFpStorage( int fp_size, int inc_fp_capacity, const char *inc_filename ); void _addIncToStorage (); }; }; #endif /* __bingo_fp_storage__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_gross_storage.cpp000066400000000000000000000111651271037650300250670ustar00rootroot00000000000000#include "bingo_gross_storage.h" #include using namespace indigo; using namespace bingo; GrossStorage::GrossStorage (size_t gross_block_size) : _gross_formulas(gross_block_size) { } BingoAddr GrossStorage::create(BingoPtr &gross_ptr, size_t gross_block_size) { gross_ptr.allocate(); new (gross_ptr.ptr()) GrossStorage(gross_block_size); return (BingoAddr)gross_ptr; } void GrossStorage::load (BingoPtr &gross_ptr, BingoAddr offset) { gross_ptr = BingoPtr(offset); } void GrossStorage::add (Array &gross_formula, int id) { _gross_formulas.add((byte *)gross_formula.ptr(), gross_formula.size(), id); dword hash = _calculateGrossHash(gross_formula.ptr(), gross_formula.size()); _hashes.add(hash, id); } void GrossStorage::find (Array &query_formula, Array &indices, int part_id, int part_count) { Array candidates; findCandidates(query_formula, candidates, part_id, part_count); int cur_candidate = 0; int match_id; while ((match_id = findNext(query_formula, candidates, cur_candidate)) != -1) { indices.push(match_id); } } void GrossStorage::findCandidates (Array &query_formula, Array &candidates, int part_id, int part_count) { dword query_hash = _calculateGrossHash(query_formula.ptr(), query_formula.size()); dword first_hash = 0; dword last_hash = (dword)(-1); if (part_id != -1 && part_count != -1) { first_hash = (part_id - 1) * last_hash / part_count; last_hash = part_id * last_hash / part_count; } if (query_hash < first_hash || query_hash > last_hash) return; Array indices; _hashes.getAll(query_hash, indices); for (int i = 0; i < indices.size(); i++) candidates.push(indices[i]); } int GrossStorage::findNext (Array &query_formula, Array &candidates, int &cur_candidate) { Array query_array; GrossFormula::fromString(query_formula.ptr(), query_array); while(++cur_candidate) { if (cur_candidate >= candidates.size()) return -1; if (tryCandidate(query_array, candidates[cur_candidate])) return candidates[cur_candidate]; } return -1; } bool GrossStorage::tryCandidate (Array &query_array, int id) { Array cand_array; const char *cand_formula; int len; cand_formula = (const char *)_gross_formulas.get(id, len); Array cand_fstr; cand_fstr.copy(cand_formula, len); GrossFormula::fromString(cand_fstr.ptr(), cand_array); if (GrossFormula::equal(query_array, cand_array)) return true; return false; } void GrossStorage::calculateMolFormula (Molecule &mol, Array &gross_formula) { Array gross_array; GrossFormula::collect(mol, gross_array); GrossFormula::toString(gross_array, gross_formula); } void GrossStorage::calculateRxnFormula (Reaction &rxn, Array &gross_formula) { gross_formula.clear(); for (int i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) { Array mol_array; Array mol_formula; GrossFormula::collect(rxn.getBaseMolecule(i), mol_array); GrossFormula::toString(mol_array, mol_formula); gross_formula.concat(mol_formula); if (rxn.next(i) != rxn.end()) { if (rxn.getSideType(i) != rxn.getSideType(rxn.next(i))) gross_formula.concat(">>", 2); else gross_formula.concat("+", 1); } } } dword GrossStorage::_calculateGrossHashForMolArray (Array &gross_array) { dword hash = 0; for (int i = 0; i < gross_array.size(); i++) hash += gross_array[i] * (i + 1); return hash; } dword GrossStorage::_calculateGrossHashForMol (const char *gross_str, int len) { Array gross_array; GrossFormula::fromString(gross_str, gross_array); return _calculateGrossHashForMolArray(gross_array); } dword GrossStorage::_calculateGrossHash (const char *gross_str, int len) { dword hash = 0; std::string rxn_str(gross_str, len); int delim_pos = rxn_str.find('>'); if (delim_pos == -1) hash = _calculateGrossHashForMol(gross_str, len); else { rxn_str[delim_pos] = '+'; rxn_str.erase(rxn_str.begin() + delim_pos + 1); std::stringstream rxn_stream; rxn_stream << rxn_str; std::string mol_str; while (rxn_stream.good()) { std::getline(rxn_stream, mol_str, '+'); hash += _calculateGrossHashForMol(mol_str.c_str(), len); } } return hash; }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_gross_storage.h000066400000000000000000000030541271037650300245320ustar00rootroot00000000000000#ifndef __bingo_gross_storage__ #define __bingo_gross_storage__ #include "molecule/molecule.h" #include "base_cpp/scanner.h" #include "molecule/gross_formula.h" #include "reaction/reaction.h" #include "bingo_ptr.h" #include "bingo_mapping.h" #include "bingo_cf_storage.h" using namespace indigo; namespace bingo { class GrossStorage { public: GrossStorage (size_t gross_block_size); static BingoAddr create(BingoPtr &gross_ptr, size_t gross_block_size); static void load (BingoPtr &gross_ptr, BingoAddr offset); void add (Array &gross_formula, int id); void find (Array &query_formula, Array &indices, int part_id = -1, int part_count = -1); void findCandidates (Array &query_formula, Array &candidates, int part_id = -1, int part_count = -1); int findNext (Array &query_formula, Array &candidates, int &cur_candidate); bool tryCandidate (Array &query_array, int id); static void calculateMolFormula (Molecule &mol, Array &gross_formula); static void calculateRxnFormula (Reaction &rxn, Array &gross_formula); private: BingoMapping _hashes; ByteBufferStorage _gross_formulas; static dword _calculateGrossHashForMolArray (Array &gross_array); static dword _calculateGrossHashForMol (const char *gross_str, int len); static dword _calculateGrossHash (const char *gross_str, int len); }; } #endif //__bingo_gross_storage__Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_index.cpp000066400000000000000000000047201271037650300233140ustar00rootroot00000000000000#include "bingo_index.h" #include #include using namespace bingo; MoleculeIndex::MoleculeIndex() : BaseIndex(MOLECULE) { } Matcher* MoleculeIndex::createMatcher (const char *type, MatcherQueryData *query_data, const char *options) { if (strcmp(type, "sub") == 0) { AutoPtr matcher(new MoleculeSubMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else if (strcmp(type, "sim") == 0) { AutoPtr matcher(new MoleculeSimMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else if (strcmp(type, "exact") == 0) { AutoPtr matcher(new MolExactMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else if (strcmp(type, "formula") == 0) { AutoPtr matcher(new MolGrossMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else throw Exception("createMatcher: undefined type"); return 0; } ReactionIndex::ReactionIndex () : BaseIndex(REACTION) { } Matcher* ReactionIndex::createMatcher (const char *type, MatcherQueryData *query_data, const char *options) { if (strcmp(type, "sub") == 0) { AutoPtr matcher(new ReactionSubMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else if (strcmp(type, "sim") == 0) { AutoPtr matcher(new ReactionSimMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else if (strcmp(type, "exact") == 0) { AutoPtr matcher(new RxnExactMatcher(*this)); matcher->setOptions(options); matcher->setQueryData(dynamic_cast(query_data)); return matcher.release(); } else throw Exception("createMatcher: undefined type"); return 0; } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_index.h000066400000000000000000000011071271037650300227550ustar00rootroot00000000000000#ifndef __bingo_index__ #define __bingo_index__ #include "bingo_base_index.h" #include "bingo_matcher.h" namespace bingo { class MoleculeIndex : public BaseIndex { public: MoleculeIndex(); virtual Matcher* createMatcher (const char *type, MatcherQueryData *query_data, const char *options); }; class ReactionIndex : public BaseIndex { public: ReactionIndex(); virtual Matcher* createMatcher (const char *type, MatcherQueryData *query_data, const char *options); }; }; #endif // __bingo_index__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_internal.h000066400000000000000000000040211271037650300234600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_internal__ #define __bingo_internal__ #include "base_cpp/exception.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { DECL_EXCEPTION_NO_EXP(BingoException); }; #define BINGO_BEGIN_DB(db_id) \ INDIGO_BEGIN \ { \ if (((db_id) < _bingo_instances.begin()) || \ ((db_id) >= _bingo_instances.end()) || \ !_bingo_instances.hasElement(db_id)) \ throw BingoException("Incorrect database object"); \ MMFStorage::setDatabaseId(db_id); #define BINGO_BEGIN_SEARCH(search_id) \ INDIGO_BEGIN \ { \ if (((search_id) < 0) || \ ((search_id) >= _searches_db.size()) || \ (_searches_db[(search_id)] == -1)) \ throw BingoException("Incorrect search object"); \ MMFStorage::setDatabaseId(_searches_db[(search_id)]); \ #define BINGO_END(fail) \ MMFStorage::setDatabaseId(-1); \ } \ INDIGO_END(fail) #ifdef _WIN32 #pragma warning(pop) #endif #endif // __bingo_internal__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_lock.cpp000066400000000000000000000024041271037650300231320ustar00rootroot00000000000000#include "bingo_lock.h" DatabaseLockData::DatabaseLockData() : writers_count(0), readers_count(0) { osSemaphoreCreate(&rc_sem, 1, 1); osSemaphoreCreate(&wc_sem, 1, 1); osSemaphoreCreate(&w_sem, 1, 1); osSemaphoreCreate(&r_sem, 1, 1); } ReadLock::ReadLock(DatabaseLockData &data) : _data(data) { osSemaphoreWait(&_data.r_sem); osSemaphoreWait(&_data.rc_sem); _data.readers_count++; if (_data.readers_count == 1) osSemaphoreWait(&_data.w_sem); osSemaphorePost(&_data.rc_sem); osSemaphorePost(&_data.r_sem); } ReadLock::~ReadLock() { osSemaphoreWait(&_data.rc_sem); _data.readers_count--; if (_data.readers_count == 0) osSemaphorePost(&_data.w_sem); osSemaphorePost(&_data.rc_sem); } WriteLock::WriteLock(DatabaseLockData &data) : _data(data) { osSemaphoreWait(&_data.wc_sem); _data.writers_count++; if (_data.writers_count == 1) osSemaphoreWait(&_data.r_sem); osSemaphorePost(&_data.wc_sem); osSemaphoreWait(&_data.w_sem); } WriteLock::~WriteLock() { osSemaphorePost(&_data.w_sem); osSemaphoreWait(&_data.wc_sem); _data.writers_count--; if (_data.writers_count == 0) osSemaphorePost(&_data.r_sem); osSemaphorePost(&_data.wc_sem); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_lock.h000066400000000000000000000010061271037650300225740ustar00rootroot00000000000000#ifndef __bingo_lock__ #define __bingo_lock__ #include "base_c/os_sync.h" struct DatabaseLockData { os_semaphore rc_sem, wc_sem, w_sem, r_sem; int writers_count, readers_count; DatabaseLockData(); }; struct ReadLock { public: ReadLock(DatabaseLockData &data); ~ReadLock(); private: DatabaseLockData &_data; }; struct WriteLock { public: WriteLock(DatabaseLockData &data); ~WriteLock(); private: DatabaseLockData &_data; }; #endif //__bingo_lock__Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mapping.cpp000066400000000000000000000054231271037650300236410ustar00rootroot00000000000000#include "bingo_mapping.h" using namespace bingo; BingoMapping::BingoMapping (size_t safe_prime) : _prime(safe_prime) { _block_size = 100; _mapping_table.resize(safe_prime); } size_t BingoMapping::get (size_t id) { _MapIterator iter; int idx_in_block; if (_findElem (id, iter, idx_in_block)) return iter->buf[idx_in_block].second; return (size_t)-1; } void BingoMapping::getAll (size_t id1, Array &id2_array) { id2_array.clear(); if ((BingoAddr)(_mapping_table[_hashFunc(id1)]) == BingoAddr::bingo_null) return; _MapList::Iterator it; _MapList &cur_list = _mapping_table[_hashFunc(id1)].ref(); int i; for (it = cur_list.begin(); it != cur_list.end(); it++) { for (i = 0; i < it->count; i++) { if (it->buf[i].first == id1) id2_array.push(it->buf[i].second); } } } void BingoMapping::add (size_t id1, size_t id2) { if ((BingoAddr)(_mapping_table[_hashFunc(id1)]) == BingoAddr::bingo_null) { _mapping_table[_hashFunc(id1)].allocate(); new(_mapping_table[_hashFunc(id1)].ptr()) _MapList(); } _MapList &cur_list = _mapping_table[_hashFunc(id1)].ref(); if (cur_list.size() == 0 || cur_list.top()->count == _block_size) { BingoPtr< _ListCell > new_array_ptr; new_array_ptr.allocate(); new(new_array_ptr.ptr()) _ListCell(_block_size); cur_list.pushBack(new_array_ptr); } int &top_size = cur_list.top()->count; cur_list.top()->buf[top_size++] = _KeyPair(id1, id2); } void BingoMapping::remove (size_t id) { if ((BingoAddr)(_mapping_table[_hashFunc(id)]) == BingoAddr::bingo_null) throw Exception("BingoMapping: There is no such id"); _MapList::Iterator it; //_MapList &cur_list = _mapping_table[_hashFunc(id)].ref(); int idx_in_block; bool res = _findElem(id, it, idx_in_block); if (!res) throw Exception("BingoMapping: There is no such id"); it->buf[idx_in_block].first = -1; it->buf[idx_in_block].second = -1; } size_t BingoMapping::_hashFunc (size_t id) { return (id % _prime); } bool BingoMapping::_findElem (size_t id, _MapIterator &iter, int &idx_in_block) { if ((BingoAddr)(_mapping_table[_hashFunc(id)]) == BingoAddr::bingo_null) return false; _MapList::Iterator it; _MapList &cur_list = _mapping_table[_hashFunc(id)].ref(); int i; for (it = cur_list.begin(); it != cur_list.end(); it++) { for (i = 0; i < it->count; i++) { if (it->buf[i].first == id) break; } if (i < it->count) break; } if (it == cur_list.end()) return false; iter = it; idx_in_block = i; return true; } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mapping.h000066400000000000000000000020201271037650300232740ustar00rootroot00000000000000#ifndef __bingo_mapping__ #define __bingo_mapping__ #include #include "bingo_ptr.h" namespace bingo { class BingoMapping { public: BingoMapping (size_t safe_prime = 200087); size_t get (size_t id); void getAll (size_t id1, Array &id2_array); void add (size_t id1, size_t id2); void remove (size_t id); private: typedef std::pair _KeyPair; struct _ListCell { BingoPtr<_KeyPair> buf; int count; _ListCell( int size ) { buf.allocate(size); count = 0; } }; typedef BingoList<_ListCell> _MapList; typedef BingoList<_ListCell>::Iterator _MapIterator; size_t _hashFunc (size_t id); bool _findElem (size_t id, _MapIterator &iter, int &idx_in_block); size_t _prime; int _block_size; BingoArray< BingoPtr<_MapList> > _mapping_table; }; } #endif //__bingo_mapping__Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_matcher.cpp000066400000000000000000000752571271037650300236450ustar00rootroot00000000000000#include "bingo_matcher.h" #include "bingo_tanimoto_coef.h" #include "bingo_tversky_coef.h" #include "bingo_euclid_coef.h" #include "molecule/molecule_substructure_matcher.h" #include "base_c/nano.h" #include "base_c/bitarray.h" #include "base_cpp/profiling.h" #include #include #include using namespace indigo; using namespace bingo; static const char *_matcher_params_prop = ""; static const char *_matcher_part_prop = "part"; GrossQueryData::GrossQueryData (Array &gross_str) : _obj(gross_str) { } QueryObject &GrossQueryData::getQueryObject () { return _obj; } MoleculeSimilarityQueryData::MoleculeSimilarityQueryData (/* const */ Molecule &qmol, float min_coef, float max_coef) : _obj(qmol), _min(min_coef), _max(max_coef) { } /*const*/ QueryObject & MoleculeSimilarityQueryData::getQueryObject () /*const*/ { return _obj; } float MoleculeSimilarityQueryData::getMin () const { return _min; } float MoleculeSimilarityQueryData::getMax () const { return _max; } ReactionSimilarityQueryData::ReactionSimilarityQueryData (/* const */ Reaction &qrxn, float min_coef, float max_coef) : _obj(qrxn), _min(min_coef), _max(max_coef) { } /*const*/ QueryObject & ReactionSimilarityQueryData::getQueryObject() /*const*/ { return _obj; } float ReactionSimilarityQueryData::getMin () const { return _min; } float ReactionSimilarityQueryData::getMax () const { return _max; } MoleculeExactQueryData::MoleculeExactQueryData (/* const */ Molecule &mol) : _obj(mol) { } /*const*/ QueryObject & MoleculeExactQueryData::getQueryObject () /*const*/ { return _obj; } ReactionExactQueryData::ReactionExactQueryData (/* const */ Reaction &rxn) : _obj(rxn) { } /*const*/ QueryObject & ReactionExactQueryData::getQueryObject () /*const*/ { return _obj; } MoleculeSubstructureQueryData::MoleculeSubstructureQueryData (/* const */ QueryMolecule &qmol) : _obj(qmol) { } /*const*/ QueryObject & MoleculeSubstructureQueryData::getQueryObject () /*const*/ { return _obj; } ReactionSubstructureQueryData::ReactionSubstructureQueryData (/* const */ QueryReaction &qrxn) : _obj(qrxn) { } /*const*/ QueryObject & ReactionSubstructureQueryData::getQueryObject() /*const*/ { return _obj; } IndexCurrentMolecule::IndexCurrentMolecule ( IndexCurrentMolecule *& ptr ) : _ptr(ptr) { matcher_exist = true; } IndexCurrentMolecule::~IndexCurrentMolecule () { if (matcher_exist) _ptr = 0; } IndexCurrentReaction::IndexCurrentReaction ( IndexCurrentReaction *& ptr ) : _ptr(ptr) { matcher_exist = true; } IndexCurrentReaction::~IndexCurrentReaction () { if (matcher_exist) _ptr = 0; } BaseMatcher::BaseMatcher(BaseIndex &index, IndigoObject *& current_obj) : _index(index), _current_obj(current_obj) { _current_obj_used = false; _current_id = 0; _part_id = -1; _part_count = -1; } BaseMatcher::~BaseMatcher () { if (_current_obj && IndigoMolecule::is(*_current_obj)) ((IndexCurrentMolecule *)_current_obj)->matcher_exist = false; else if (_current_obj && IndigoReaction::is(*_current_obj)) ((IndexCurrentReaction *)_current_obj)->matcher_exist = false; if (!_current_obj_used) delete _current_obj; } int BaseMatcher::currentId () { BingoArray &id_mapping = _index.getIdMapping(); return id_mapping[_current_id]; } IndigoObject * BaseMatcher::currentObject () { if (_current_obj_used) throw Exception("BaseMatcher: Object has been already gotten"); _current_obj_used = true; return _current_obj; } const Index & BaseMatcher::getIndex () { return _index; } float BaseMatcher::currentSimValue () { throw Exception("BaseMatcher: Matcher does not support this method"); } void BaseMatcher::setOptions (const char * options) { std::map option_map; std::vector allowed_props; allowed_props.push_back(_matcher_params_prop); allowed_props.push_back(_matcher_part_prop); Properties::parseOptions(options, option_map, &allowed_props); if (option_map.find(_matcher_params_prop) != option_map.end()) _setParameters(option_map[_matcher_params_prop].c_str()); if (option_map.find(_matcher_part_prop) != option_map.end()) { std::stringstream part_str; part_str << option_map[_matcher_part_prop]; int part_count, part_id; char sep; part_str >> part_id; part_str >> sep; part_str >> part_count; //bool ef = part_str.eof(); if (part_str.fail() || sep != '/' || part_id <= 0 || part_count <= 0 || part_id > part_count) throw Exception("BaseMatcher: setOptions: incorrect partitioning parameters"); _part_id = part_id; _part_count = part_count; _initPartition(); } } bool BaseMatcher::_isCurrentObjectExist() { int cf_len; _index.getCfStorage().get(_current_id, cf_len); if (cf_len == -1) return false; return true; } bool BaseMatcher::_loadCurrentObject() { try { if (_current_obj == 0) throw Exception("BaseMatcher: Matcher's current object was destroyed"); profTimerStart(t_get_cmf, "loadCurObj_get_cf"); ByteBufferStorage &cf_storage = _index.getCfStorage(); int cf_len; const char *cf_str = (const char *)cf_storage.get(_current_id, cf_len); if (cf_len == -1) return false; profTimerStop(t_get_cmf); profTimerStart(t_load_cmf, "loadCurObj_load_cf"); BufferScanner buf_scn(cf_str, cf_len); if (IndigoMolecule::is(*_current_obj)) { Molecule &mol = _current_obj->getMolecule(); CmfLoader cmf_loader(buf_scn); cmf_loader.loadMolecule(mol); } else if (IndigoReaction::is(*_current_obj)) { Reaction &rxn = _current_obj->getReaction(); CrfLoader crf_loader(buf_scn); crf_loader.loadReaction(rxn); } else throw Exception("BaseMatcher::unknown current object type"); profTimerStop(t_load_cmf); return true; } catch (Exception &ex) { int db_id = _index.getIdMapping()[_current_id]; ex.appendMessage(" on id=%d", db_id); ex.throwSelf(); return false; // This statement is dummy because throwSelf always throws an exception } } int BaseMatcher::esimateRemainingResultsCount (int &delta) { _match_probability_esimate.setCount(_current_id + 1); float p = _match_probability_esimate.mean(); float error = _match_probability_esimate.meanEsimationError(); int left_obj_count = _index.getObjectsCount() - _match_time_esimate.getCount(); delta = (int)(error * left_obj_count); return (int)(left_obj_count * p); } float BaseMatcher::esimateRemainingTime (float &delta) { _match_time_esimate.setCount(_current_id + 1); float mean_time = _match_time_esimate.mean(); float error = _match_time_esimate.meanEsimationError(); int left_obj_count = _index.getObjectsCount() - _match_time_esimate.getCount(); delta = error * left_obj_count; return left_obj_count * mean_time; } // // BaseSubstructureMatcher // BaseSubstructureMatcher::BaseSubstructureMatcher (/*const */ BaseIndex &index, IndigoObject *& current_obj) : BaseMatcher(index, current_obj), _fp_storage(_index.getSubStorage()) { _fp_size = _index.getFingerprintParams().fingerprintSize(); _current_id = -1; _current_cand_id = -1; _current_pack = -1; _final_pack = _fp_storage.getPackCount() + 1; _cand_count = 0; } bool BaseSubstructureMatcher::next () { //int fp_size_in_bits = _fp_size * 8; static int sub_cnt = 0; _current_cand_id++; while (!((_current_pack == _final_pack) && (_current_cand_id == _candidates.size()))) { profTimerStart(tsingle, "sub_single"); if (_current_cand_id == _candidates.size()) { profTimerStart(tf, "sub_find_cand"); _current_pack++; if (_current_pack < _final_pack) { _findPackCandidates(_current_pack); _cand_count += _candidates.size(); } else break; _current_cand_id = 0; profTimerStop(tf); } if (_candidates.size() == 0) continue; _current_id = _candidates[_current_cand_id]; profTimerStart(tt, "sub_try"); bool status = _tryCurrent(); profTimerStop(tt); if (status) profIncCounter("sub_found", 1); _match_probability_esimate.addValue((float)status); _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); if (status) { sub_cnt++; return true; } _current_cand_id++; } profIncCounter("sub_count_cand", _cand_count); return false; } void BaseSubstructureMatcher::setQueryData (SubstructureQueryData *query_data) { _query_data.reset(query_data); const MoleculeFingerprintParameters & fp_params = _index.getFingerprintParams(); _query_data->getQueryObject().buildFingerprint(fp_params, &_query_fp, 0); int bit_cnt = bitGetOnesCount(_query_fp.ptr(), _fp_size); profIncCounter("query_bit_count", bit_cnt); _query_fp_bits_used.clear(); for (int i = 0; i < _fp_size * 8; i++) { if (bitGetBit(_query_fp.ptr(), i)) _query_fp_bits_used.push(i); } // Sort bits accoring to the bits frequency BingoArray &fp_bit_usage = _index.getSubStorage().getFpBitUsageCounts(); std::sort(_query_fp_bits_used.ptr(), _query_fp_bits_used.ptr() + _query_fp_bits_used.size(), [&](int i1, int i2) { return fp_bit_usage[i1] < fp_bit_usage[i2]; }); } void BaseSubstructureMatcher::_findPackCandidates (int pack_idx) { if (pack_idx == _fp_storage.getPackCount()) { _findIncCandidates(); return; } profTimerStart(t, "sub_find_cand_pack"); _candidates.clear(); TranspFpStorage &fp_storage = _index.getSubStorage(); //const byte *query_fp = _query_fp.ptr(); const byte * block; int fp_size_in_bits = _fp_size * 8; Array fit_bits; fit_bits.clear_resize(fp_storage.getBlockSize()); fit_bits.fill(255); profTimerStart(tgs, "sub_find_cand_pack_get_search"); int left = 0, right = fp_storage.getBlockSize() - 1; // Filter only based on the first 10 bits // TODO: collect time infromation about the reading and matching measurements and // and balance between reading new block or check filtered items without reading new block for (int i = 0; i < _query_fp_bits_used.size() && i < 15; i++) { int j = _query_fp_bits_used[i]; profTimerStart(tgb, "sub_find_cand_pack_get_block"); block = fp_storage.getBlock(pack_idx * fp_size_in_bits + j); profTimerStop(tgb); profTimerStart(tgu, "sub_find_cand_pack_fit_update"); bitAnd(fit_bits.ptr() + left, &block[0] + left, right - left + 1); while(left <= right && fit_bits[left] == 0) left++; while(left <= right && fit_bits[right] == 0) right--; if (left > right) // Not more results break; profTimerStop(tgu); } profTimerStop(tgs); for (int k = 0; k < 8 * fp_storage.getBlockSize(); k++) if (bitGetBit(fit_bits.ptr(), k)) _candidates.push(k + pack_idx * fp_storage.getBlockSize() * 8); } void BaseSubstructureMatcher::_findIncCandidates () { profTimerStart(t, "sub_find_cand_inc"); _candidates.clear(); const TranspFpStorage &fp_storage = _index.getSubStorage(); int inc_block_id_offset = fp_storage.getPackCount() * fp_storage.getBlockSize() * 8; const byte *inc = fp_storage.getIncrement(); for (int i = 0; i < fp_storage.getIncrementSize(); i++) { const byte *fp = inc + i * _fp_size; if (bitTestOnes(_query_fp.ptr(), fp, _fp_size)) _candidates.push(i + inc_block_id_offset); } } void BaseSubstructureMatcher::_setParameters (const char * params) { } void BaseSubstructureMatcher::_initPartition () { int pack_count_with_inc = _fp_storage.getPackCount() + 1; if (_part_count > pack_count_with_inc) { if (_part_id > pack_count_with_inc) { _current_pack = -1; _final_pack = -1; } else { _current_pack = (_part_id - 1) - 1; _final_pack = _part_id; } } else { _current_pack = (_part_id - 1) * pack_count_with_inc / _part_count - 1; _final_pack = _part_id * pack_count_with_inc / _part_count; } } MoleculeSubMatcher::MoleculeSubMatcher (/*const */ BaseIndex &index) : BaseSubstructureMatcher(index, (IndigoObject *&)_current_mol), _current_mol(new IndexCurrentMolecule(_current_mol)) { _mapping.clear(); } const Array & MoleculeSubMatcher::currentMapping () { return _mapping; } bool MoleculeSubMatcher::_tryCurrent ()// const { SubstructureMoleculeQuery &query = (SubstructureMoleculeQuery &)(_query_data->getQueryObject()); QueryMolecule &query_mol = (QueryMolecule &)(query.getMolecule()); if (!_loadCurrentObject()) return false; if (_current_obj == 0) throw Exception("MoleculeSubMatcher: Matcher's current object was destroyed"); Molecule &target_mol = _current_obj->getMolecule(); profTimerStart(tr_m, "sub_try_matching"); MoleculeSubstructureMatcher msm(target_mol); msm.setQuery(query_mol); bool find_res = msm.find(); profTimerStop(tr_m); if (find_res) { _mapping.copy(msm.getTargetMapping(), target_mol.vertexCount()); return true; } //return true; return false; } ReactionSubMatcher::ReactionSubMatcher (/*const */ BaseIndex &index) : BaseSubstructureMatcher(index, (IndigoObject *&)_current_rxn), _current_rxn(new IndexCurrentReaction(_current_rxn)) { _mapping.clear(); } const ObjArray > & ReactionSubMatcher::currentMapping () { return _mapping; } bool ReactionSubMatcher::_tryCurrent ()// const { SubstructureReactionQuery &query = (SubstructureReactionQuery &)_query_data->getQueryObject(); QueryReaction &query_rxn = (QueryReaction &)(query.getReaction()); if (!_loadCurrentObject()) return false; if (_current_obj == 0) throw Exception("ReactionSubMatcher: Matcher's current object was destroyed"); Reaction &target_rxn = _current_obj->getReaction(); ReactionSubstructureMatcher rsm(target_rxn); rsm.setQuery(query_rxn); if (rsm.find()) { _mapping.resize(target_rxn.end()); for (int i = target_rxn.begin(); i != target_rxn.end(); i = target_rxn.next(i)) _mapping[i].clear(); for (int i = query_rxn.begin(); i != query_rxn.end(); i = query_rxn.next(i)) { int target_mol_idx = rsm.getTargetMoleculeIndex(i); _mapping[target_mol_idx].copy(rsm.getQueryMoleculeMapping(i), query_rxn.getQueryMolecule(i).vertexCount()); } return true; } return false; } BaseSimilarityMatcher::BaseSimilarityMatcher (/*const */ BaseIndex &index, IndigoObject *& current_obj ) : BaseMatcher(index, current_obj) { _min_cell = -1; _max_cell = -1; _first_cell = -1; _containers_count = 0; _current_id = -1; _current_cell = 0; _current_container = -1; _current_portion_id = 0; _current_portion.clear(); _current_sim_value = -1; _fp_size = _index.getFingerprintParams().fingerprintSizeSim(); _sim_coef.reset(new TanimotoCoef(_fp_size)); } bool BaseSimilarityMatcher::next () { profTimerStart(tsimnext, "sim_next"); SimStorage &sim_storage = _index.getSimStorage(); int query_bit_count = bitGetOnesCount(_query_fp.ptr(), _fp_size); if (_current_cell == -1) return false; while (true) { profTimerStart(tsingle, "sim_single"); if (_current_portion_id >= _current_portion.size()) { _current_portion_id = 0; _current_container++; if (!sim_storage.isSmallBase()) { if (_current_container == sim_storage.getCellSize(_current_cell)) { _current_cell = sim_storage.nextFitCell(query_bit_count, _first_cell, _min_cell, _max_cell, _current_cell); if (_part_count != -1 && _part_id != -1) while ((_current_cell % _part_count != _part_id - 1) && (_current_cell != -1)) _current_cell = sim_storage.nextFitCell(query_bit_count, _first_cell, _min_cell, _max_cell, _current_cell); if (_current_cell == -1) return false; _current_container = 0; } _current_portion.clear(); sim_storage.getSimilar(_query_fp.ptr(), _sim_coef.ref(), _query_data->getMin(), _current_portion, _current_cell, _current_container); } else { if (_current_container > 0) return false; _current_portion.clear(); sim_storage.getIncSimilar(_query_fp.ptr(), _sim_coef.ref(), _query_data->getMin(), _current_portion); } _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); _match_probability_esimate.addValue((float)_current_portion.size()); continue; } _current_id = _current_portion[_current_portion_id].id; _current_sim_value = _current_portion[_current_portion_id].sim_value; _current_portion_id++; bool is_obj_exist = _isCurrentObjectExist(); if (!is_obj_exist) { _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); continue; } _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); _loadCurrentObject(); return true; } } void BaseSimilarityMatcher::setQueryData (SimilarityQueryData *query_data) { _query_data.reset(query_data); const MoleculeFingerprintParameters & fp_params = _index.getFingerprintParams(); _query_data->getQueryObject().buildFingerprint(fp_params, 0, &_query_fp); SimStorage &sim_storage = _index.getSimStorage(); int query_bit_count = bitGetOnesCount(_query_fp.ptr(), _fp_size); if (sim_storage.isSmallBase()) return; sim_storage.getCellsInterval(_query_fp.ptr(), *_sim_coef.get(), _query_data->getMin(), _min_cell, _max_cell); _first_cell = sim_storage.firstFitCell(query_bit_count, _min_cell, _max_cell); _current_cell = _first_cell; if (_part_count != -1 && _part_id != -1) { while (((_current_cell % _part_count) != _part_id - 1) && (_current_cell != -1)) _current_cell = sim_storage.nextFitCell(query_bit_count, _first_cell, _min_cell, _max_cell, _current_cell); } _containers_count = 0; for (int i = _min_cell; i <= _max_cell; i++) _containers_count += sim_storage.getCellSize(i); } void BaseSimilarityMatcher::_setParameters (const char *parameters) { if (_query_data.get() != 0) throw Exception("BaseSimilarityMatcher: setParameters: query data have been already set"); std::stringstream param_str; param_str << parameters; std::string type; param_str >> type; if (param_str.fail()) throw Exception("BaseSimilarityMatcher: setParameters: incorrect similarity parameters"); if (type.compare("tanimoto") == 0) { if (!param_str.eof()) throw Exception("BaseSimilarityMatcher: setParameters: tanimoto metric has no parameters"); _sim_coef.reset(new TanimotoCoef(_fp_size)); } else if (type.compare("euclid-sub") == 0) { if (!param_str.eof()) throw Exception("BaseSimilarityMatcher: setParameters: euclid-sub metric has no parameters"); _sim_coef.reset(new EuclidCoef(_fp_size)); } else if (type.compare("tversky") == 0) { double alpha, beta; if (!param_str.eof()) { param_str >> alpha; if (param_str.fail()) throw Exception("BaseSimilarityMatcher: setParameters: incorrect similarity parameters. Allowed 'tversky '"); param_str >> beta; if (param_str.fail()) throw Exception("BaseSimilarityMatcher: setParameters: incorrect similarity parameters. Allowed 'tversky '"); } else { alpha = beta = 0.5; } if (fabs(alpha + beta - 1) > EPSILON) throw Exception("BaseSimilarityMatcher: setParameters: Tversky parameters have to satisfy the condition: alpha + beta = 1 "); _sim_coef.reset(new TverskyCoef(_fp_size, alpha, beta)); } else throw Exception("BaseSimilarityMatcher: setParameters: incorrect similarity parameters. Allowed types: tanimoto, euclid-sub, tversky [ ]"); } void BaseSimilarityMatcher::_initPartition () { } int BaseSimilarityMatcher::esimateRemainingResultsCount (int &delta) { int left_cont_count = _containers_count - _match_probability_esimate.getCount(); float error = _match_probability_esimate.meanEsimationError(); delta = (int)(error * left_cont_count); return (int)(_match_probability_esimate.mean() * left_cont_count); } float BaseSimilarityMatcher::esimateRemainingTime (float &delta) { _match_time_esimate.setCount(_match_probability_esimate.getCount()); int left_cont_count = _containers_count - _match_probability_esimate.getCount(); float error = _match_time_esimate.meanEsimationError(); delta = error * left_cont_count; return _match_time_esimate.mean() * left_cont_count; } float BaseSimilarityMatcher::currentSimValue () { return _current_sim_value; } BaseSimilarityMatcher::~BaseSimilarityMatcher () { _current_block.clear(); } MoleculeSimMatcher::MoleculeSimMatcher (/*const */ BaseIndex &index) : BaseSimilarityMatcher(index, (IndigoObject *&)_current_mol), _current_mol(new IndexCurrentMolecule(_current_mol)) { } ReactionSimMatcher::ReactionSimMatcher (/*const */ BaseIndex &index) : BaseSimilarityMatcher(index, (IndigoObject *&)_current_rxn), _current_rxn(new IndexCurrentReaction(_current_rxn)) { } BaseExactMatcher::BaseExactMatcher (BaseIndex &index, IndigoObject *& current_obj) : BaseMatcher(index, current_obj) { _candidates.clear(); _current_cand_id = 0; _flags = 0; } bool BaseExactMatcher::next () { ExactStorage &exact_storage = _index.getExactStorage(); if (_candidates.size() == 0) exact_storage.findCandidates(_query_hash, _candidates, _part_id, _part_count); while (_current_cand_id < _candidates.size()) { profTimerStart(tsingle, "exact_single"); _current_id = _candidates[_current_cand_id]; _current_cand_id++; bool status = _tryCurrent(); if (status) profIncCounter("exact_found", 1); _match_probability_esimate.addValue((float)status); _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); if (status) return true; } return false; } void BaseExactMatcher::setQueryData (ExactQueryData *query_data) { _query_data.reset(query_data); _query_hash = _calcHash(); } void BaseExactMatcher::_initPartition () { } BaseExactMatcher::~BaseExactMatcher() { } MolExactMatcher::MolExactMatcher (/*const */ BaseIndex &index) : BaseExactMatcher(index, (IndigoObject *&)_current_mol), _current_mol(new IndexCurrentMolecule(_current_mol)) { _tautomer = false; } void MolExactMatcher::_setParameters (const char *parameters) { if (_indigoParseTautomerFlags(parameters, _tautomer_params)) { _tautomer = true; } else { _tautomer = false; MoleculeExactMatcher::parseConditions(parameters, _flags, _rms_threshold); } } dword MolExactMatcher::_calcHash () { SimilarityMoleculeQuery &query = (SimilarityMoleculeQuery &)(_query_data->getQueryObject()); Molecule &query_mol = (Molecule &)(query.getMolecule()); return ExactStorage::calculateMolHash(query_mol); } bool MolExactMatcher::_tryCurrent ()/* const */ { SimilarityMoleculeQuery &query = (SimilarityMoleculeQuery &)(_query_data->getQueryObject()); Molecule &query_mol = (Molecule &)(query.getMolecule()); if (!_loadCurrentObject()) return false; if (_current_obj == 0) throw Exception("MoleculeExactMatcher: Matcher's current object was destroyed"); Molecule &target_mol = _current_obj->getMolecule(); if (_tautomer) { MoleculeTautomerMatcher matcher(target_mol, false); Indigo &indigo = indigoGetInstance(); matcher.arom_options = indigo.arom_options; matcher.setRulesList(&indigo.tautomer_rules); matcher.setRules(_tautomer_params.conditions, _tautomer_params.force_hydrogens, _tautomer_params.ring_chain, _tautomer_params.method); matcher.setQuery(query_mol); return matcher.find(); } else { MoleculeExactMatcher mem(query_mol, target_mol); mem.flags = _flags; mem.rms_threshold = _rms_threshold; return mem.find(); } } RxnExactMatcher::RxnExactMatcher(/*const */ BaseIndex &index) : BaseExactMatcher(index, (IndigoObject *&)_current_rxn), _current_rxn(new IndexCurrentReaction(_current_rxn)) { } void RxnExactMatcher::_setParameters (const char *flags) { // TODO: merge Indigo code into Bingo and stop this endless copy-paste if (flags == 0) flags = ""; static struct { const char *token; int value; } token_list[] = { {"ELE", MoleculeExactMatcher::CONDITION_ELECTRONS}, {"MAS", MoleculeExactMatcher::CONDITION_ISOTOPE}, {"STE", MoleculeExactMatcher::CONDITION_STEREO}, {"AAM", ReactionExactMatcher::CONDITION_AAM}, {"RCT", ReactionExactMatcher::CONDITION_REACTING_CENTERS} }; int i, res = 0, count = 0; bool had_none = false; bool had_all = false; BufferScanner scanner(flags); QS_DEF(Array, word); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "NONE") == 0) { if (had_all) throw Exception("RxnExactMatcher: setParameters: NONE conflicts with ALL"); had_none = true; count++; continue; } if (strcasecmp(word.ptr(), "ALL") == 0) { if (had_none) throw Exception("RxnExactMatcher: setParameters: ALL conflicts with NONE"); had_all = true; res = MoleculeExactMatcher::CONDITION_ALL | ReactionExactMatcher::CONDITION_ALL; count++; continue; } for (i = 0; i < NELEM(token_list); i++) { if (strcasecmp(token_list[i].token, word.ptr()) == 0) { if (had_all) throw Exception("RxnExactMatcher: setParameters: only negative flags are allowed together with ALL"); res |= token_list[i].value; break; } if (word[0] == '-' && strcasecmp(token_list[i].token, word.ptr() + 1) == 0) { res &= ~token_list[i].value; break; } } if (i == NELEM(token_list)) throw Exception("RxnExactMatcher: setParameters: unknown token %s", word.ptr()); count++; } if (had_none && count > 1) throw Exception("RxnExactMatcher: setParameters: no flags are allowed together with NONE"); if (count == 0) res = MoleculeExactMatcher::CONDITION_ALL | ReactionExactMatcher::CONDITION_ALL; _flags = res; } dword RxnExactMatcher::_calcHash () { SimilarityReactionQuery &query = (SimilarityReactionQuery &)_query_data->getQueryObject(); Reaction &query_rxn = (Reaction &)(query.getReaction()); return ExactStorage::calculateRxnHash(query_rxn); } bool RxnExactMatcher::_tryCurrent ()/* const */ { SimilarityReactionQuery &query = (SimilarityReactionQuery &)_query_data->getQueryObject(); Reaction &query_rxn = (Reaction &)(query.getReaction()); if (!_loadCurrentObject()) return false; if (_current_obj == 0) throw Exception("ReactionExactMatcher: Matcher's current object was destroyed"); Reaction &target_rxn = _current_obj->getReaction(); ReactionExactMatcher rem(query_rxn, target_rxn); rem.flags = _flags; if (rem.find()) return true; return false; } BaseGrossMatcher::BaseGrossMatcher (BaseIndex &index, IndigoObject *& current_obj) : BaseMatcher(index, current_obj) { _candidates.clear(); _current_cand_id = 0; } bool BaseGrossMatcher::next () { GrossStorage &gross_storage = _index.getGrossStorage(); GrossQuery &gross_qobj = (GrossQuery &)_query_data->getQueryObject(); if (_candidates.size() == 0) gross_storage.findCandidates(gross_qobj.getGrossString(), _candidates, _part_id, _part_count); while (_current_cand_id < _candidates.size()) { profTimerStart(tsingle, "exact_single"); _current_id = _candidates[_current_cand_id]; _current_cand_id++; bool status = _tryCurrent(); if (status) profIncCounter("exact_found", 1); _match_probability_esimate.addValue((float)status); _match_time_esimate.addValue(profTimerGetTimeSec(tsingle)); if (status) return true; } return false; } void BaseGrossMatcher::setQueryData (GrossQueryData *query_data) { _query_data.reset(query_data); GrossQuery &gross_qobj = (GrossQuery &)_query_data->getQueryObject(); GrossFormula::fromString(gross_qobj.getGrossString().ptr(), _query_array); _calcFormula(); } void BaseGrossMatcher::_initPartition () { } BaseGrossMatcher::~BaseGrossMatcher() { } MolGrossMatcher::MolGrossMatcher (/*const */ BaseIndex &index) : BaseGrossMatcher(index, (IndigoObject *&)_current_mol), _current_mol(new IndexCurrentMolecule(_current_mol)) { } void MolGrossMatcher::_setParameters (const char *parameters) { } void MolGrossMatcher::_calcFormula () { GrossQuery &query = (GrossQuery &)(_query_data->getQueryObject()); GrossFormula::fromString(query.getGrossString().ptr(), _query_array); } bool MolGrossMatcher::_tryCurrent ()/* const */ { GrossQuery &query = (GrossQuery &)(_query_data->getQueryObject()); if (!_loadCurrentObject()) return false; if (_current_obj == 0) throw Exception("MolGrossMatcher: Matcher's current object was destroyed"); GrossStorage &gross_storage = _index.getGrossStorage(); return gross_storage.tryCandidate(_query_array, _current_id); }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_matcher.h000066400000000000000000000247631271037650300233060ustar00rootroot00000000000000#ifndef __bingo_matcher__ #define __bingo_matcher__ #include "bingo_object.h" #include "bingo_base_index.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "indigo_match.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "reaction/reaction_exact_matcher.h" #include "math/statistics.h" using namespace indigo; namespace bingo { /////////////////////////////////////// // Query data classes /////////////////////////////////////// class MatcherQueryData { public: virtual /*const*/ QueryObject &getQueryObject () /*const*/ = 0; virtual ~MatcherQueryData () {}; }; class SimilarityQueryData : public MatcherQueryData { public: virtual float getMin () const = 0; virtual float getMax () const = 0; }; class SubstructureQueryData : public MatcherQueryData { }; class ExactQueryData : public MatcherQueryData { }; class GrossQueryData : public MatcherQueryData { public: GrossQueryData (Array &gross_str); virtual /*const*/ QueryObject &getQueryObject () /*const*/ ; private: GrossQuery _obj; }; class MoleculeSimilarityQueryData : public SimilarityQueryData { public: MoleculeSimilarityQueryData (/* const */ Molecule &mol, float min_coef, float max_coef); virtual /*const*/ QueryObject &getQueryObject () /*const*/ ; virtual float getMin () const ; virtual float getMax () const ; private: SimilarityMoleculeQuery _obj; float _min; float _max; }; class ReactionSimilarityQueryData : public SimilarityQueryData { public: ReactionSimilarityQueryData (/* const */ Reaction &rxn, float min_coef, float max_coef); virtual /*const*/ QueryObject &getQueryObject () /*const*/ ; virtual float getMin () const ; virtual float getMax () const ; protected: SimilarityReactionQuery _obj; float _min; float _max; }; class MoleculeExactQueryData : public ExactQueryData { public: MoleculeExactQueryData (/* const */ Molecule &mol); virtual /*const*/ QueryObject &getQueryObject (); private: SimilarityMoleculeQuery _obj; }; class ReactionExactQueryData : public ExactQueryData { public: ReactionExactQueryData (/* const */ Reaction &rxn); virtual /*const*/ QueryObject &getQueryObject () /*const*/; private: SimilarityReactionQuery _obj; }; class MoleculeSubstructureQueryData : public SubstructureQueryData { public: MoleculeSubstructureQueryData (/* const */ QueryMolecule &qmol); virtual /*const*/ QueryObject &getQueryObject () /*const*/; private: SubstructureMoleculeQuery _obj; }; class ReactionSubstructureQueryData : public SubstructureQueryData { public: ReactionSubstructureQueryData (/* const */ QueryReaction &qrxn); virtual /*const*/ QueryObject &getQueryObject () /*const*/; private: SubstructureReactionQuery _obj; }; /////////////////////////////////////// // Matcher classes /////////////////////////////////////// class IndexCurrentMolecule : public IndigoMolecule { public: IndexCurrentMolecule ( IndexCurrentMolecule *& ptr ); ~IndexCurrentMolecule (); bool matcher_exist; private: IndexCurrentMolecule *& _ptr; }; class IndexCurrentReaction : public IndigoReaction { public: IndexCurrentReaction ( IndexCurrentReaction *& ptr ); ~IndexCurrentReaction (); bool matcher_exist; private: IndexCurrentReaction *& _ptr; }; class Matcher { public: virtual bool next () = 0; virtual int currentId () = 0; virtual IndigoObject * currentObject () = 0; virtual const Index & getIndex () = 0; virtual float currentSimValue () = 0; virtual void setOptions (const char * options) = 0; virtual int esimateRemainingResultsCount (int &delta) = 0; virtual float esimateRemainingTime (float &delta) = 0; virtual ~Matcher () {}; }; class BaseMatcher : public Matcher { public: BaseMatcher(BaseIndex &index, IndigoObject *& current_obj); virtual int currentId (); virtual IndigoObject * currentObject (); virtual const Index & getIndex (); virtual float currentSimValue (); virtual void setOptions (const char * options); virtual int esimateRemainingResultsCount (int &delta); virtual float esimateRemainingTime (float &delta); protected: BaseIndex &_index; IndigoObject *& _current_obj; bool _current_obj_used; int _current_id; int _part_id; int _part_count; // Variables used for estimation MeanEstimator _match_probability_esimate, _match_time_esimate; bool _isCurrentObjectExist(); bool _loadCurrentObject(); virtual void _setParameters (const char * params) = 0; virtual void _initPartition () = 0; ~BaseMatcher (); }; class BaseSubstructureMatcher : public BaseMatcher { public: BaseSubstructureMatcher (/*const */ BaseIndex &index, IndigoObject *& current_obj); virtual bool next (); void setQueryData (SubstructureQueryData *query_data); protected: int _fp_size; int _cand_count; /*const*/ AutoPtr _query_data; Array _query_fp; Array _query_fp_bits_used; void _findPackCandidates (int pack_idx); void _findIncCandidates (); virtual bool _tryCurrent ()/* const */ = 0; virtual void _setParameters (const char * params); virtual void _initPartition (); private: Array _candidates; int _current_cand_id; int _current_pack; int _final_pack; const TranspFpStorage &_fp_storage; }; class MoleculeSubMatcher : public BaseSubstructureMatcher { public: MoleculeSubMatcher (/*const */ BaseIndex &index); const Array & currentMapping (); private: Array _mapping; virtual bool _tryCurrent () /*const*/; IndexCurrentMolecule *_current_mol; }; class ReactionSubMatcher : public BaseSubstructureMatcher { public: ReactionSubMatcher(/*const */ BaseIndex &index); const ObjArray > & currentMapping (); private: ObjArray > _mapping; virtual bool _tryCurrent () /*const*/; IndexCurrentReaction *_current_rxn; }; class BaseSimilarityMatcher : public BaseMatcher { public: BaseSimilarityMatcher (BaseIndex &index, IndigoObject *& current_obj); virtual bool next (); void setQueryData (SimilarityQueryData *query_data); ~BaseSimilarityMatcher(); virtual int esimateRemainingResultsCount (int &delta); virtual float esimateRemainingTime (float &delta); virtual float currentSimValue (); private: /* const */ AutoPtr _query_data; int _fp_size; int _min_cell; int _max_cell; int _first_cell; int _containers_count; int _current_cell; int _current_container; Array _current_portion; int _current_portion_id; float _current_sim_value; AutoPtr _sim_coef; Array _current_block; const byte *_cur_loc; Array _query_fp; virtual void _setParameters (const char * params); virtual void _initPartition (); }; class MoleculeSimMatcher : public BaseSimilarityMatcher { public: MoleculeSimMatcher (/*const */ BaseIndex &index); private: IndexCurrentMolecule *_current_mol; }; class ReactionSimMatcher : public BaseSimilarityMatcher { public: ReactionSimMatcher(/*const */ BaseIndex &index); private: IndexCurrentReaction *_current_rxn; }; class BaseExactMatcher : public BaseMatcher { public: BaseExactMatcher (BaseIndex &index, IndigoObject *& current_obj); virtual bool next (); void setQueryData (ExactQueryData *query_data); ~BaseExactMatcher(); protected: int _current_cand_id; dword _query_hash; int _flags; Array _candidates; /* const */ AutoPtr _query_data; virtual dword _calcHash () = 0; virtual bool _tryCurrent ()/* const */ = 0; virtual void _initPartition (); }; class MolExactMatcher : public BaseExactMatcher { public: MolExactMatcher (/*const */ BaseIndex &index); private: IndexCurrentMolecule *_current_mol; float _rms_threshold; virtual dword _calcHash (); virtual bool _tryCurrent ()/* const */; virtual void _setParameters (const char *params); bool _tautomer; IndigoTautomerParams _tautomer_params; }; class RxnExactMatcher : public BaseExactMatcher { public: RxnExactMatcher (/*const */ BaseIndex &index); private: IndexCurrentReaction *_current_rxn; virtual dword _calcHash (); virtual bool _tryCurrent ()/* const */; virtual void _setParameters (const char *params); }; class BaseGrossMatcher : public BaseMatcher { public: BaseGrossMatcher (BaseIndex &index, IndigoObject *& current_obj); virtual bool next (); void setQueryData (GrossQueryData *query_data); ~BaseGrossMatcher(); protected: int _current_cand_id; Array _query_array; Array _candidates; /* const */ AutoPtr _query_data; virtual void _calcFormula() = 0; virtual bool _tryCurrent ()/* const */ = 0; virtual void _initPartition (); }; class MolGrossMatcher : public BaseGrossMatcher { public: MolGrossMatcher (/*const */ BaseIndex &index); private: IndexCurrentMolecule *_current_mol; virtual void _calcFormula(); virtual bool _tryCurrent ()/* const */; virtual void _setParameters (const char *params); }; }; #endif // __bingo_matcher__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mmf.cpp000066400000000000000000000105151271037650300227630ustar00rootroot00000000000000#include "bingo_mmf.h" #include "base_cpp/exception.h" #ifdef _WIN32 #include #undef min #undef max #elif (defined __GNUC__ || defined __APPLE__) #include #include #include #include #include #include #include #include #endif using namespace bingo; using namespace indigo; MMFile::MMFile() : _ptr(0) { #ifdef _WIN32 _h_file = 0; _h_map_file = 0; #elif (defined __GNUC__ || defined __APPLE__) _fd = -1; #endif } MMFile::~MMFile () { //close(); } void * MMFile::ptr () { return _ptr; } const char * MMFile::name () { return _filename.c_str(); } char * MMFile::_getSystemErrorMsg () { #ifdef _WIN32 char * msg; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL ); return msg; #elif (defined __GNUC__ || defined __APPLE__) return strerror(errno); #endif } size_t MMFile::size() { return _len; } void MMFile::open (const char *filename, size_t buf_size, bool create_flag, bool read_only) { _len = buf_size; _filename.assign(filename); if (create_flag) std::remove(filename); #ifdef _WIN32 DWORD dwflags = GENERIC_READ | GENERIC_WRITE; if (read_only) dwflags = GENERIC_READ; _h_file = CreateFile((LPCSTR)_filename.c_str(), dwflags, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (_h_file == INVALID_HANDLE_VALUE) throw Exception("BingoMMF: Could not open file. Error message: %s", _getSystemErrorMsg()); dword access_info = PAGE_READWRITE; if (read_only) access_info = PAGE_READONLY; _h_map_file = CreateFileMapping( _h_file, // use paging file NULL, // default security access_info, // read/write access buf_size >> 32, // maximum object size (high-order DWORD) buf_size & 0xFFFFFFFF, // maximum object size (low-order DWORD) 0); // name of mapping object if (_h_map_file == NULL) throw Exception("BingoMMF: Could not create file mapping object. Error message: %s", _getSystemErrorMsg()); dword map_access_permission = FILE_MAP_ALL_ACCESS; if (read_only) map_access_permission = FILE_MAP_READ; _ptr = (char *)MapViewOfFile(_h_map_file, // handle to map object map_access_permission, 0, 0, buf_size); if (_ptr == NULL) throw Exception("BingoMMF: Could not map view of file. Error message: %s", _getSystemErrorMsg()); #elif (defined __GNUC__ || defined __APPLE__) int flags; mode_t permissions = 0; if (read_only) flags = O_RDONLY; else { flags = O_RDWR | O_CREAT; permissions = S_IRUSR | S_IWUSR; } if ((_fd = ::open(_filename.c_str(), flags, permissions)) == -1) throw Exception("BingoMMF: Could not open file. Error message: %s", _getSystemErrorMsg()); auto trunc_res = ftruncate(_fd, _len); if(trunc_res < 0) { //TODO check result } int prot_flags = PROT_READ | PROT_WRITE; if (read_only) prot_flags = PROT_READ; _ptr = mmap((caddr_t)0, _len, prot_flags, MAP_SHARED, _fd, 0); if (_ptr == (void *)MAP_FAILED) throw Exception("BingoMMF: Could not map view of file. Error message: %s", _getSystemErrorMsg()); #endif } void MMFile::close () { #ifdef _WIN32 if (_filename.size() != 0) { DeleteFile(_filename.c_str()); _filename.clear(); } if (_h_file != 0) { CloseHandle(_h_file); _h_file = 0; } if (_h_map_file != 0) { CloseHandle(_h_map_file); _h_map_file = 0; } if (_ptr != 0) { UnmapViewOfFile(_ptr); _ptr = 0; } #elif (defined __GNUC__ || defined __APPLE__) if (_ptr != 0) { munmap((caddr_t)_ptr, _len); _ptr = 0; } if (_fd != -1) { ::close(_fd); _fd = -1; } #endif }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mmf.h000066400000000000000000000013661271037650300224340ustar00rootroot00000000000000#ifndef __bingo_mmf__ #define __bingo_mmf__ #include #include #include namespace bingo { class MMFile { public: MMFile (); ~MMFile (); void open (const char *filename, size_t buf_size, bool create_flag, bool read_only); void resize (size_t new_size); void * ptr (); const char * name (); size_t size(); void close (); private: #ifdef _WIN32 void *_h_map_file; void *_h_file; #elif (defined __GNUC__ || defined __APPLE__) int _fd; #endif void *_ptr; std::string _filename; size_t _len; static char * _getSystemErrorMsg (); }; }; #endif // __bingo_mmf__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mmf_storage.cpp000066400000000000000000000025151271037650300245100ustar00rootroot00000000000000#include #include #include #include "base_cpp/exception.h" #include "bingo_mmf_storage.h" #include "base_cpp/tlscont.h" using namespace bingo; // TODO: implement real thread local storage - not session local static _SIDManager _database_id; int MMFStorage::getDatabaseId () { return (int)_database_id.getSessionId(); } void MMFStorage::setDatabaseId (int db) { _database_id.setSessionId((int)db); } MMFStorage::MMFStorage() { } void MMFStorage::create (const char *filename, size_t min_size, size_t max_size, const char *header, int index_id) { size_t header_len = strlen(header); _mm_files.clear(); if (header_len >= max_header_len) throw Exception("MMfStorage: create(): Too long header"); BingoAllocator::_create(filename, min_size, max_size, max_header_len, &_mm_files, index_id); BingoPtr header_ptr(0, 0); strcpy(header_ptr.ptr(), header); } void MMFStorage::load (const char *filename, BingoPtr header_ptr, int index_id, bool read_only) { _mm_files.clear(); BingoAllocator::_load(filename, max_header_len, &_mm_files, index_id, read_only); header_ptr = BingoPtr(0, 0); } void MMFStorage::close () { for (int i = 0; i < _mm_files.size(); i++) _mm_files[i].close(); _mm_files.clear(); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_mmf_storage.h000066400000000000000000000013421271037650300241520ustar00rootroot00000000000000#ifndef __bingo_mmf_storage__ #define __bingo_mmf_storage__ #include "base_cpp/obj_array.h" #include "bingo_mmf.h" #include "bingo_ptr.h" using namespace indigo; namespace bingo { class MMFStorage { public: static int getDatabaseId (); static void setDatabaseId (int db); static const int max_header_len = 128; MMFStorage(); void create (const char *filename, size_t min_size, size_t max_size, const char *header, int index_id); void load (const char *filename, BingoPtr header_ptr, int index_id, bool read_only); void close (); private: ObjArray _mm_files; bool _read_only; }; }; #endif // __bingo_mmf_storage__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_multibit_tree.cpp000066400000000000000000000164141271037650300250600ustar00rootroot00000000000000#include "bingo_multibit_tree.h" using namespace bingo; int MultibitTree::_compareBitWeights (_DistrWeight &bw1, _DistrWeight &bw2, void *context) { if (bw1.weight > bw2.weight) return 1; else if (bw1.weight < bw2.weight) return -1; else return 0; } BingoPtr MultibitTree::_buildNode (Array &fit_fp_indices, const Array &is_parrent_mb, int level) { BingoPtr<_MultibitNode> node_ptr; node_ptr.allocate(); _MultibitNode *node = new(node_ptr.ptr()) _MultibitNode(); byte *fingerprints = _fingerprints_ptr.ptr(); int fp_bitsize = _fp_size * 8; if (fit_fp_indices.size() <= 10 || level == _max_level) { node->fp_indices_array.allocate(fit_fp_indices.size()); int *fp_indices = node->fp_indices_array.ptr(); node->fp_indices_count = fit_fp_indices.size(); for (int i = 0; i < fit_fp_indices.size(); i++) fp_indices[i] = fit_fp_indices[i]; return node_ptr; } QS_DEF(Array<_DistrWeight>, bit_weights); bit_weights.clear_resize(fp_bitsize); bit_weights.zerofill(); double bit_con = 1.0 / fit_fp_indices.size(); for (int j = 0; j < fp_bitsize; j++) bit_weights[j].idx = j; for (int i = 0; i < fit_fp_indices.size(); i++) { for (int j = 0; j < fp_bitsize; j++) { bit_weights[j].weight += bitGetBit(fingerprints + fit_fp_indices[i] * _fp_size, j) * bit_con; } } bit_weights.qsort(_compareBitWeights, 0); QS_DEF(Array, mb_indices); mb_indices.clear(); double distr_coef = 1; for (int k = 0; k < bit_weights.size(); k++) { if (is_parrent_mb[bit_weights[k].idx]) continue; distr_coef *= (1 - bit_weights[k].weight); mb_indices.push(bit_weights[k].idx); if (distr_coef < 0.5) break; } QS_DEF(Array, left_fit); left_fit.clear(); QS_DEF(Array, right_fit); right_fit.clear(); QS_DEF(Array, is_mb); is_mb.copy(is_parrent_mb); for (int i = 0; i < mb_indices.size(); i++) { is_mb[mb_indices[i]] = 1; } for (int i = 0; i < fit_fp_indices.size(); i++) { bool is_fit = true; for (int j = 0; j < mb_indices.size(); j++) { if (bitGetBit(fingerprints + fit_fp_indices[i] * _fp_size, mb_indices[j])) { is_fit = false; break; } } if (is_fit) right_fit.push(fit_fp_indices[i]); else left_fit.push(fit_fp_indices[i]); } if (left_fit.size() == 0 || right_fit.size() == 0) { node->fp_indices_array.allocate(fit_fp_indices.size()); int *fp_indices = (int *)node->fp_indices_array.ptr(); node->fp_indices_count = fit_fp_indices.size(); for (int i = 0; i < fit_fp_indices.size(); i++) fp_indices[i] = fit_fp_indices[i]; return node_ptr; } node->match_bits_array.allocate(mb_indices.size()); _MatchBit *match_bits = node->match_bits_array.ptr(); node->match_bits_count = mb_indices.size(); for (int i = 0; i < mb_indices.size(); i++) { if (mb_indices[i] < 0 || mb_indices[i] > 511) i = i; match_bits[i] = _MatchBit(mb_indices[i], 0); } node->left = _buildNode(left_fit, is_mb, level + 1); node->right = _buildNode(right_fit, is_mb, level + 1); return node_ptr; } void MultibitTree::_build () { QS_DEF(Array, indices); indices.clear_resize(_fp_count); for (int i = 0; i < indices.size(); i++) indices[i] = i; QS_DEF(Array, is_mb); is_mb.clear_resize(_fp_size * 8); is_mb.zerofill(); _tree_ptr = _buildNode(indices, is_mb, 0); } void MultibitTree::_findLinear (_MultibitNode *node, const byte *query, int query_bit_number, SimCoef &sim_coef, double min_coef, Array &sim_indices, int fp_bit_number) { profTimerStart(tmsl, "multibit_tree_search_linear"); byte *fingerprints = _fingerprints_ptr.ptr(); int *indices = _indices_ptr.ptr(); int *fp_indices = node->fp_indices_array.ptr(); for (int i = 0; i < node->fp_indices_count; i++) { const byte *fp = fingerprints + fp_indices[i] * _fp_size; int f_bit_number = bitGetOnesCount(fp, _fp_size); double coef = sim_coef.calcCoef(query, fp, query_bit_number, f_bit_number); if (coef < min_coef) continue; sim_indices.push(SimResult(indices[fp_indices[i]], (float)coef)); } } void MultibitTree::_findSimilarInNode (BingoPtr<_MultibitNode> node_ptr, const byte *query, int query_bit_number, SimCoef &sim_coef, double min_coef, Array &sim_indices, int m01, int m10) { if (node_ptr.isNull()) return; _MultibitNode *node = node_ptr.ptr(); if (node->fp_indices_count != 0) { if (_min_fp_bit_number == _max_fp_bit_number)//if fingerpint bits_count is fixed _findLinear(node, query, query_bit_number, sim_coef, min_coef, sim_indices, _min_fp_bit_number); else _findLinear(node, query, query_bit_number, sim_coef, min_coef, sim_indices); return; } _MatchBit *match_bits = node->match_bits_array.ptr(); QS_DEF(Array, left_indices); left_indices.clear(); QS_DEF(Array, right_indices); right_indices.clear(); int right_m01 = m01, right_m10 = m10; for (int i = 0; i < node->match_bits_count; i++) if (match_bits[i].val == 0) { if (bitGetBit(query, match_bits[i].idx)) right_m01++; } else if (!bitGetBit(query, match_bits[i].idx)) right_m10++; double right_upper_bound = sim_coef.calcUpperBound(query_bit_number, _min_fp_bit_number, _max_fp_bit_number, right_m10, right_m01); if (!node->left.isNull()) _findSimilarInNode(node->left, query, query_bit_number, sim_coef, min_coef, left_indices, m01, m10); if ((!node->left.isNull()) && right_upper_bound + EPSILON > min_coef) _findSimilarInNode(node->right, query, query_bit_number, sim_coef, min_coef, right_indices, right_m01, right_m10); for (int i = 0; i < left_indices.size(); i++) sim_indices.push(left_indices[i]); for (int i = 0; i < right_indices.size(); i++) sim_indices.push(right_indices[i]); } MultibitTree::MultibitTree (int fp_size) : _fp_size(fp_size) { _tree_ptr.allocate(); new(_tree_ptr.ptr()) _MultibitNode(); _query_bit_number = -1; _max_level = 6; } void MultibitTree::build (BingoPtr fingerprints, BingoPtr indices, int fp_count, int min_fp_bit_number, int max_fp_bit_number) { _fingerprints_ptr = fingerprints; _indices_ptr = indices; _min_fp_bit_number = min_fp_bit_number; _max_fp_bit_number = max_fp_bit_number; _fp_count = fp_count; _build(); } int MultibitTree::findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices) { profTimerStart(tms, "multibit_tree_search"); int query_bit_number = bitGetOnesCount(query, _fp_size); sim_fp_indices.clear(); _findSimilarInNode(_tree_ptr, query, query_bit_number, sim_coef, min_coef, sim_fp_indices, 0, 0); return sim_fp_indices.size(); }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_multibit_tree.h000066400000000000000000000046361271037650300245300ustar00rootroot00000000000000#ifndef __multibit_tree__ #define __multibit_tree__ #include "bingo_cell_container.h" #include "math/algebra.h" #include "base_cpp/obj_array.h" #include "base_cpp/tlscont.h" #include "base_cpp/profiling.h" #include "base_c/bitarray.h" #include "bingo_sim_coef.h" #include "bingo_ptr.h" using namespace indigo; namespace bingo { class MultibitTree { public: MultibitTree (int fp_size); void build (BingoPtr fingerprints, BingoPtr indices, int fp_count, int min_fp_bit_number, int max_fp_bit_number); int findSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices); private: struct _MatchBit { int idx; bool val; _MatchBit() { idx = -1; val = 0; } _MatchBit (int new_idx, bool new_val) : idx(new_idx), val(new_val) { } }; struct _DistrWeight { int idx; double weight; }; struct _MultibitNode { BingoPtr<_MatchBit> match_bits_array; int match_bits_count; BingoPtr fp_indices_array; int fp_indices_count; BingoPtr<_MultibitNode> left; BingoPtr<_MultibitNode> right; _MultibitNode() { match_bits_count = 0; fp_indices_count = 0; } }; int _min_fp_bit_number; int _max_fp_bit_number; int _fp_size; BingoPtr _fingerprints_ptr; BingoPtr _indices_ptr; int _fp_count; BingoPtr<_MultibitNode> _tree_ptr; int _query_bit_number; int _max_level; static int _compareBitWeights (_DistrWeight &bw1, _DistrWeight &bw2, void *context); BingoPtr<_MultibitNode> _buildNode (Array &fit_fp_indices, const Array &is_parrent_mb, int level); void _build (); void _findLinear (_MultibitNode *node, const byte *query, int query_bit_number, SimCoef &sim_coef, double min_coef, Array &sim_indices, int fp_bit_number = -1); void _findSimilarInNode (BingoPtr<_MultibitNode> node_ptr, const byte *query, int query_bit_number, SimCoef &sim_coef, double min_coef, Array &sim_indices, int m01, int m10); }; }; #endif /* __multibit_tree__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_object.cpp000066400000000000000000000124241271037650300234530ustar00rootroot00000000000000#include "bingo_object.h" #include "bingo_exact_storage.h" #include "bingo_gross_storage.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/crf_saver.h" #include "reaction/crf_loader.h" #include "reaction/reaction_fingerprint.h" #include "reaction/reaction_substructure_matcher.h" #include "molecule/molecule.h" #include "molecule/gross_formula.h" #include "molecule/cmf_saver.h" #include "molecule/cmf_loader.h" #include "molecule/molecule_fingerprint.h" #include "molecule/molecule_substructure_matcher.h" using namespace indigo; using namespace bingo; static const int _fp_calc_timeout = 10000; BaseMoleculeQuery::BaseMoleculeQuery (BaseMolecule &mol, bool needs_query_fingerprint) : _base_mol(mol), _needs_query_fingerprint(needs_query_fingerprint) { } bool BaseMoleculeQuery::buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp)// const { MoleculeFingerprintBuilder fp_builder(_base_mol, fp_params); TimeoutCancellationHandler canc_handler(_fp_calc_timeout); fp_builder.query = _needs_query_fingerprint; fp_builder.cancellation = &canc_handler; fp_builder.process(); if (sub_fp) sub_fp->copy(fp_builder.get(), fp_params.fingerprintSize()); if (sim_fp) sim_fp->copy(fp_builder.getSim(), fp_params.fingerprintSizeSim()); return true; } const BaseMolecule & BaseMoleculeQuery::getMolecule () { return _base_mol; } SubstructureMoleculeQuery::SubstructureMoleculeQuery (/* const */ QueryMolecule &mol) : BaseMoleculeQuery(_mol, true) { _mol.clone(mol, 0, 0); } SimilarityMoleculeQuery::SimilarityMoleculeQuery (/* const */ Molecule &mol) : BaseMoleculeQuery(_mol, false) { _mol.clone(mol, 0, 0); } GrossQuery::GrossQuery (/* const */ Array &str) { _gross_str.copy(str); } bool GrossQuery::buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/ { throw Exception("GrossQuery::buildFingerprint can\t be called"); } Array &GrossQuery::getGrossString() { return _gross_str; } BaseReactionQuery::BaseReactionQuery (BaseReaction &rxn) : _base_rxn(rxn) { } bool BaseReactionQuery::buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) // const { ReactionFingerprintBuilder fp_builder(_base_rxn, fp_params); TimeoutCancellationHandler canc_handler(_fp_calc_timeout); fp_builder.cancellation = &canc_handler; fp_builder.process(); if (sub_fp) sub_fp->copy(fp_builder.get(), fp_params.fingerprintSize()); if (sim_fp) sim_fp->copy(fp_builder.getSim(), fp_params.fingerprintSizeSim()); return true; } const BaseReaction & BaseReactionQuery::getReaction() { return _base_rxn; } SubstructureReactionQuery::SubstructureReactionQuery (/* const */ QueryReaction &rxn) : BaseReactionQuery(_rxn) { _rxn.clone(rxn, 0, 0, 0); } SimilarityReactionQuery::SimilarityReactionQuery (/* const */ Reaction &rxn) : BaseReactionQuery(_rxn) { _rxn.clone(rxn, 0, 0, 0); } IndexMolecule::IndexMolecule (/* const */ Molecule &mol) { _mol.clone(mol, 0, 0); } bool IndexMolecule::buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) // const { MoleculeFingerprintBuilder fp_builder(_mol, fp_params); TimeoutCancellationHandler canc_handler(_fp_calc_timeout); fp_builder.cancellation = &canc_handler; fp_builder.process(); if (sub_fp) sub_fp->copy(fp_builder.get(), fp_params.fingerprintSize()); if (sim_fp) sim_fp->copy(fp_builder.getSim(), fp_params.fingerprintSizeSim()); return true; } bool IndexMolecule::buildGrossString (Array &gross)/* const */ { GrossStorage::calculateMolFormula(_mol, gross); return true; } bool IndexMolecule::buildCfString (Array &cf)// const { ArrayOutput arr_out(cf); CmfSaver cmf_saver(arr_out); cmf_saver.saveMolecule(_mol); return true; } bool IndexMolecule::buildHash (dword &hash) { hash = ExactStorage::calculateMolHash(_mol); return true; } IndexReaction::IndexReaction (/* const */ Reaction &rxn) { _rxn.clone(rxn, 0, 0, 0); } bool IndexReaction::buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) // const { ReactionFingerprintBuilder fp_builder(_rxn, fp_params); TimeoutCancellationHandler canc_handler(_fp_calc_timeout); fp_builder.cancellation = &canc_handler; fp_builder.process(); if (sub_fp) sub_fp->copy(fp_builder.get(), fp_params.fingerprintSize()); if (sim_fp) sim_fp->copy(fp_builder.getSim(), fp_params.fingerprintSizeSim()); return true; } bool IndexReaction::buildGrossString (Array &gross)/* const */ { GrossStorage::calculateRxnFormula(_rxn, gross); return true; } bool IndexReaction::buildCfString (Array &cf)// const { ArrayOutput arr_out(cf); CrfSaver crf_saver(arr_out); crf_saver.saveReaction(_rxn); return true; } bool IndexReaction::buildHash (dword &hash) { hash = ExactStorage::calculateRxnHash(_rxn); return true; } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_object.h000066400000000000000000000105401271037650300231150ustar00rootroot00000000000000#ifndef __bingo_object__ #define __bingo_object__ //#include "base_c/defs.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/crf_saver.h" #include "reaction/crf_loader.h" #include "reaction/reaction_fingerprint.h" #include "reaction/reaction_substructure_matcher.h" #include "molecule/molecule.h" #include "molecule/cmf_saver.h" #include "molecule/cmf_loader.h" #include "molecule/molecule_fingerprint.h" #include "molecule/molecule_substructure_matcher.h" using namespace indigo; namespace bingo { class QueryObject { public: virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp)/* const */ = 0; virtual ~QueryObject () {}; }; ////////////////////////// // Molecule query objects ////////////////////////// class BaseMoleculeQuery : public QueryObject { private: BaseMolecule &_base_mol; bool _needs_query_fingerprint; public: BaseMoleculeQuery (BaseMolecule &mol, bool needs_query_fingerprint); virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/; const BaseMolecule &getMolecule (); }; class SubstructureMoleculeQuery : public BaseMoleculeQuery { private: QueryMolecule _mol; public: SubstructureMoleculeQuery (/* const */ QueryMolecule &mol); }; class SimilarityMoleculeQuery : public BaseMoleculeQuery { private: Molecule _mol; public: SimilarityMoleculeQuery (/* const */ Molecule &mol); }; class GrossQuery : public QueryObject { private: Array _gross_str; virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/; public: GrossQuery (/* const */ Array &str); Array &getGrossString(); }; ////////////////////////// // Reaction query objects ////////////////////////// class BaseReactionQuery : public QueryObject { private: BaseReaction &_base_rxn; public: BaseReactionQuery (BaseReaction &rxn); virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/; const BaseReaction &getReaction (); }; class SubstructureReactionQuery : public BaseReactionQuery { public: SubstructureReactionQuery (/* const */ QueryReaction &rxn); private: QueryReaction _rxn; }; class SimilarityReactionQuery : public BaseReactionQuery { public: SimilarityReactionQuery (/* const */ Reaction &rxn); private: Reaction _rxn; }; class IndexObject { public: virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /* const */ = 0; virtual bool buildGrossString (Array &cf)/* const */ = 0; virtual bool buildCfString (Array &cf)/* const */ = 0; virtual bool buildHash (dword &hash)/* const */ = 0; virtual ~IndexObject () {}; }; class IndexMolecule : public IndexObject { protected: Molecule _mol; public: IndexMolecule (/* const */ Molecule &mol); virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/; virtual bool buildGrossString (Array &gross_string)/* const */; virtual bool buildCfString (Array &cf) /*const*/; virtual bool buildHash (dword &hash)/* const */; }; class IndexReaction : public IndexObject { protected: Reaction _rxn; public: IndexReaction (/* const */ Reaction &rxn); virtual bool buildFingerprint (const MoleculeFingerprintParameters &fp_params, Array *sub_fp, Array *sim_fp) /*const*/; virtual bool buildGrossString (Array &gross_string)/* const */; virtual bool buildCfString (Array &cf) /*const*/; virtual bool buildHash (dword &hash)/* const */; }; }; #endif //__bingo_object__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_properties.cpp000066400000000000000000000071651271037650300244070ustar00rootroot00000000000000#include "bingo_properties.h" #include "base_cpp/exception.h" #include "base_cpp/profiling.h" #include #include #include #include #include #include #include #include using namespace bingo; using namespace indigo; Properties::Properties () { } BingoAddr Properties::create (BingoPtr &ptr) { ptr.allocate(); new(ptr.ptr()) Properties(); return (BingoAddr)ptr; } void Properties::load (BingoPtr &ptr, BingoAddr offset) { ptr = BingoPtr(offset); } void Properties::parseOptions (const char *options, std::map &option_map, std::vector *allowed_props) { if (options == 0 || strlen(options) == 0) return; option_map.clear(); std::stringstream options_stream; options_stream << options; std::string line; while (options_stream.good()) { std::getline(options_stream, line, ';'); if (line.size() == 0) continue; std::string opt_name, opt_value; int sep = (int)line.find_first_of(':'); if (sep != -1) opt_name.assign(line.substr(0, sep)); opt_value.assign(line.substr(sep + 1, std::string::npos)); if (allowed_props) { if (std::find(allowed_props->begin(), allowed_props->end(), opt_name) == allowed_props->end()) throw Exception("Properties: Incorrect parameters"); } option_map.insert(std::pair(opt_name, opt_value)); } } void Properties::add (const char *prop_name, const char *value) { int prop_id; for (prop_id = 0; prop_id < _props.size(); prop_id++) if (strcmp(_props[prop_id].name.ptr(), prop_name) == 0) break; if (prop_id == _props.size()) { _PropertyPair &new_pair = _props.push(); new_pair.name.allocate(strlen(prop_name) + 1); strcpy(new_pair.name.ptr(), prop_name); new_pair.value.allocate(max_prop_len); } if (strlen(value) >= max_prop_len) throw Exception("BingoProperties: Too long property value"); strcpy(_props[prop_id].value.ptr(), value); } void Properties::add (const char *prop_name, unsigned long value) { std::ostringstream osstr; osstr << value; add(prop_name, osstr.str().c_str()); } const char * Properties::getNoThrow (const char *prop_name) { int prop_id; for (prop_id = 0; prop_id < _props.size(); prop_id++) if (strcmp(_props[prop_id].name.ptr(), prop_name) == 0) break; if (prop_id == _props.size()) return 0; return _props[prop_id].value.ptr(); } const char * Properties::get (const char *prop_name) { const char *res = getNoThrow(prop_name); if (res == 0) throw Exception("Unknown property field"); return res; } unsigned long Properties::getULongNoThrow (const char *prop_name) { const char *value = getNoThrow(prop_name); if (value == 0) return ULONG_MAX; unsigned long u_dec; std::istringstream isstr(value); isstr >> u_dec; return u_dec; } unsigned long Properties::getULong (const char *prop_name) { unsigned long res = getULongNoThrow(prop_name); if (res == ULONG_MAX) throw Exception("Unknown property field"); return res; } void Properties::_parseProperty (const std::string &line, std::string &prop_out, std::string &value_out) { int sep = (int)line.find_first_of('='); prop_out.assign(line.substr(0, sep)); value_out.assign(line.substr(sep + 1, std::string::npos)); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_properties.h000066400000000000000000000024371271037650300240510ustar00rootroot00000000000000#ifndef __bingo_parameters__ #define __bingo_parameters__ #include #include #include #include #include #include "bingo_ptr.h" namespace bingo { class Properties { public: Properties (); static BingoAddr create (BingoPtr &ptr); static void load (BingoPtr &ptr, BingoAddr offset); static void parseOptions (const char *options, std::map &option_map, std::vector *allowed_props = 0); void add (const char *prop_name, const char *value); void add (const char *prop_name, unsigned long value); const char * get (const char *prop_name); const char * getNoThrow (const char *prop_name); unsigned long getULong (const char *prop_name); unsigned long getULongNoThrow (const char *prop_name); private: struct _PropertyPair { BingoPtr name; BingoPtr value; }; void _rewritePropFile (); static void _parseProperty (const std::string &line, std::string &prop_out, std::string &value_out); static const int max_prop_len = 1024; BingoArray<_PropertyPair> _props; }; }; #endif /* __bingo_parameters__ */Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_ptr.cpp000066400000000000000000000134111271037650300230070ustar00rootroot00000000000000#include #include #include #include #include #include "bingo_ptr.h" #include "bingo_mmf_storage.h" #include "base_c/os_sync.h" #include "base_c/bitarray.h" using namespace indigo; using namespace bingo; PtrArray BingoAllocator::_instances; OsLock BingoAllocator::_instances_lock; const BingoAddr BingoAddr::bingo_null = BingoAddr(-1, -1); int BingoAllocator::getAllocatorDataSize () { return sizeof(_BingoAllocatorData); } void BingoAllocator::_create (const char *filename, size_t min_size, size_t max_size, size_t alloc_off, ObjArray *mm_files, int index_id) { MMFile file; std::string name; _genFilename(0, filename, name); file.open(name.c_str(), min_size, true, false); byte *mmf_ptr = (byte *)file.ptr(); if ((mmf_ptr == 0) || (min_size == 0) || (min_size < sizeof(BingoAllocator))) throw Exception("BingoAllocator: Incorrect instance initialization"); _instances_lock.Lock(); _instances.expand(index_id + 1); _instances[index_id] = new BingoAllocator(); _instances_lock.Unlock(); BingoAllocator *inst = _instances[index_id]; inst->_data_offset = alloc_off; _BingoAllocatorData *allocator_data = (_BingoAllocatorData *)(mmf_ptr + alloc_off); new(allocator_data) _BingoAllocatorData(); inst->_mm_files = mm_files; allocator_data->_free_off = alloc_off + sizeof(_BingoAllocatorData); allocator_data->_min_file_size = min_size; allocator_data->_max_file_size = max_size; allocator_data->_cur_file_id = 0; inst->_mm_files->push(file); inst->_filename.assign(filename); inst->_index_id = index_id; } void BingoAllocator::_load (const char *filename, size_t alloc_off, ObjArray *mm_files, int index_id, bool read_only) { std::string name; _genFilename(0, filename, name); std::ifstream fstream(name.c_str(), std::ios::binary | std::ios::ate); size_t size = fstream.tellg(); MMFile file; file.open(name.c_str(), size, false, read_only); byte *mmf_ptr = (byte *)file.ptr(); if ((mmf_ptr == 0) || (size == 0) || (size < sizeof(BingoAllocator))) throw Exception("BingoAllocator: Incorrect instance initialization"); _instances_lock.Lock(); _instances.expand(index_id + 1); _instances[index_id] = new BingoAllocator(); _instances_lock.Unlock(); BingoAllocator *inst = _instances[index_id]; _BingoAllocatorData *allocator_data = (_BingoAllocatorData *)(mmf_ptr + alloc_off); inst->_data_offset = alloc_off; inst->_mm_files = mm_files; inst->_mm_files->push(file); inst->_filename.assign(filename); inst->_index_id = index_id; for (int i = 1; i < (int)allocator_data->_cur_file_id + 1; i++) { _genFilename(i, inst->_filename.c_str(), name); MMFile &file = inst->_mm_files->push(); size_t file_size = _getFileSize(i, allocator_data->_min_file_size, allocator_data->_max_file_size, allocator_data->_existing_files); file.open(name.c_str(), file_size, false, read_only); } } BingoAllocator *BingoAllocator::_getInstance () { int database_id = MMFStorage::getDatabaseId(); if (_instances.size() <= database_id) throw Exception("BingoAllocator: Incorrect session id"); if (_instances[database_id] == 0) throw Exception("BingoAllocator: instance is not initialized"); return _instances[database_id]; } byte * BingoAllocator::_get (size_t file_id, size_t offset) { //byte * mmf_ptr = (byte *)_mm_files->at(0).ptr(); //_BingoAllocatorData *allocator_data = (_BingoAllocatorData *)(mmf_ptr + _data_offset); byte * file_ptr = (byte *)_mm_files->at((int)file_id).ptr(); return file_ptr + offset; } BingoAllocator::BingoAllocator () { } size_t BingoAllocator::_getFileSize(size_t idx, size_t min_size, size_t max_size, dword existing_files) { int incr_f_count = (int)log(max_size / min_size); int i; size_t bit_cnt = 0; for (i = 0; i < incr_f_count; i++) { if (bitGetBit(&existing_files, i)) bit_cnt++; if (bit_cnt == idx) break; } size_t file_size = (i == incr_f_count ? max_size : min_size * ((size_t)1 << idx)); return file_size; } void BingoAllocator::_addFile (size_t alloc_size) { byte * mmf_ptr = (byte *)_mm_files->at(0).ptr(); _BingoAllocatorData *allocator_data = (_BingoAllocatorData *)(mmf_ptr + _data_offset); size_t cur_file_size = _getFileSize(allocator_data->_cur_file_id, allocator_data->_min_file_size, allocator_data->_max_file_size, allocator_data->_existing_files); size_t file_size = cur_file_size * 2; while (file_size <= allocator_data->_max_file_size) { if (alloc_size <= file_size) { int dgr = log(file_size / allocator_data->_min_file_size); bitSetBit(&allocator_data->_existing_files, dgr, 1); break; } file_size *= 2; } if (file_size > allocator_data->_max_file_size) file_size = allocator_data->_max_file_size; if (alloc_size > file_size) throw Exception("BingoAllocator: Too big allocation size"); MMFile &file = _mm_files->push(); std::string name; _genFilename(_mm_files->size() - 1, _filename.c_str(), name); file.open(name.c_str(), file_size, true, false); allocator_data->_cur_file_id++; allocator_data->_free_off = 0; } void BingoAllocator::_genFilename (int idx, const char *filename, std::string &out_name) { std::ostringstream name_str; name_str << filename; name_str << idx; out_name.assign(name_str.str()); } Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_ptr.h000066400000000000000000000313731271037650300224630ustar00rootroot00000000000000#ifndef __bingo_ptr__ #define __bingo_ptr__ #include "base_cpp/obj_array.h" #include "base_cpp/exception.h" #include "base_cpp/tlscont.h" #include "bingo_mmf.h" #include "base_cpp/profiling.h" #include "base_cpp/os_sync_wrapper.h" #include #include #include using namespace indigo; namespace bingo { class BingoAllocator; struct BingoAddr { BingoAddr () { file_id = (size_t)-1; offset = (size_t)-1; } BingoAddr (size_t f_id, size_t off) : file_id(f_id), offset(off) { } size_t file_id; size_t offset; bool operator== (const BingoAddr &other) { return (file_id == other.file_id) && (offset == other.offset); } static const BingoAddr bingo_null; }; template class BingoPtr { public: BingoPtr () { } explicit BingoPtr (BingoAddr addr) : _addr(addr) { } explicit BingoPtr (size_t file_id, size_t offset) : _addr(file_id, offset) { } T * ptr(); const T * ptr() const; T & ref() { return *ptr(); } const T & ref() const { return *ptr(); } T * operator->() { return ptr(); } const T * operator->() const { return ptr(); } BingoPtr operator+ (int off) { return BingoPtr(_addr.file_id, _addr.offset + off * sizeof(T)); } T & operator[] (int idx) { return *(ptr() + idx); } /* qword serialize () const { return _offset; } void unserialize (qword data) { _offset = data; } */ bool isNull () { return (_addr.offset == (size_t)-1) && (_addr.file_id == (size_t)-1); } void allocate ( int count = 1 ); operator BingoAddr() const { return _addr; } private: BingoAddr _addr; }; template class BingoArray { public: BingoArray( int block_size = 10000 ) : _block_size(block_size) { _size = 0; _block_count = 0; } void resize (int new_size) { if (new_size > reserved()) { int blocks_count = (_size + _block_size - 1) / _block_size; int new_blocks_count = (new_size + _block_size - 1) / _block_size; if (new_blocks_count > _max_block_count) throw Exception("BingoArray: block count limit is exceeded"); for (int i = blocks_count; i < new_blocks_count; i++) { _blocks[i].allocate(_block_size); for (int j = 0; j < _block_size; j++) new((_blocks[i] + j).ptr()) T(); } _block_count = new_blocks_count; } _size = new_size; } T & at (int index) { if (index < 0 || index >= _size) throw Exception("BingoArray: incorrect idx %d (size=%d)", index, _size); return *(_blocks[index / _block_size].ptr() + index % _block_size); } const T & at (int index) const { if (index < 0 || index >= _size) throw Exception("BingoArray: incorrect idx %d (size=%d)", index, _size); return *(_blocks[index / _block_size].ptr() + index % _block_size); } T & operator [] (int index) { return at(index); } const T & operator [] (int index) const { return at(index); } T & top () { int index = _size - 1; return *(_blocks[index / _block_size].ptr() + index % _block_size); } size_t find (const T & elem) { for (size_t i = 0; i < size(); i++) { if (at(i) == elem) return i; } return (size_t)-1; } T & push () { if (_size % _block_size == 0) { int blocks_count = (_size + _block_size - 1) / _block_size; _blocks[blocks_count].allocate(_block_size); } T * arr = _blocks[_size / _block_size].ptr(); int idx_in_block = _size % _block_size; _size++; new(arr + idx_in_block) T(); return arr[idx_in_block]; } template T & push (A &a) { if (_size % _block_size == 0) { int blocks_count = (_size + _block_size - 1) / _block_size; _blocks[blocks_count].allocate(_block_size); } T * arr = _blocks[_size / _block_size].ptr(); int idx_in_block = _size % _block_size; _size++; new(arr + idx_in_block) T(a); return arr[idx_in_block]; } void push (T elem) { T & new_elem = push(); new_elem = elem; } int size () const { return _size; } int reserved () const { return _block_count * _block_size; } private: static const int _max_block_count = 40000; int _block_size; int _block_count; int _size; BingoPtr _blocks[_max_block_count]; }; template class BingoList { private: struct _Link { BingoPtr data_ptr; BingoPtr<_Link> next_link; BingoPtr<_Link> prev_link; _Link() { data_ptr = BingoPtr(BingoAddr::bingo_null); next_link = BingoPtr<_Link>(BingoAddr::bingo_null); prev_link = BingoPtr<_Link>(BingoAddr::bingo_null); } }; public: struct Iterator { BingoPtr<_Link> cur_link; Iterator () : cur_link(BingoPtr(-1, -1)) { } Iterator (BingoPtr<_Link> link) : cur_link(link) { } Iterator (const Iterator &it) : cur_link(it.cur_link) { } T & operator*() { return cur_link->data_ptr.ref(); } T * operator->() { return cur_link->data_ptr.ptr(); } Iterator & operator= (const Iterator & it) { cur_link = it.cur_link; return *this; } bool operator== (const Iterator & it) const { if ((BingoAddr)cur_link == (BingoAddr)it.cur_link) return true; return false; } bool operator!= (const Iterator & it) const { return !(*this == it); } Iterator & operator++ (int) { if ((BingoAddr)(cur_link->next_link) == BingoAddr::bingo_null) throw Exception("BingoList::Iterator:operator++ There's no next link"); cur_link = cur_link->next_link; return *this; } Iterator & operator-- (int) { if ((BingoAddr)(cur_link->prev_link) == BingoAddr::bingo_null) throw Exception("BingoList::Iterator:operator-- There's no previous link"); cur_link = cur_link->prev_link; return *this; } }; BingoList() { _size = 0; _begin_link.allocate(); new(_begin_link.ptr()) _Link(); _end_link = _begin_link; } bool empty () const { if (_end_link == _begin_link) return true; return false; } unsigned int size () const { return _size; } void insertBefore(Iterator pos, const BingoPtr x) { BingoPtr<_Link> new_link; new_link.allocate(); new(new_link.ptr()) _Link(); new_link->data_ptr = x; new_link->next_link = pos.cur_link; new_link->prev_link = pos.cur_link->prev_link; if (!((BingoAddr)(pos.cur_link->prev_link) == BingoAddr::bingo_null)) pos.cur_link->prev_link->next_link = new_link; pos.cur_link->prev_link = new_link; if ((BingoAddr)(pos.cur_link) == (BingoAddr)(_begin_link)) _begin_link = new_link; _size++; } void insertBefore(Iterator pos, const T &x) { BingoPtr data; data.allocate(); data.ref() = x; insertBefore(pos, data); } void erase (Iterator & pos) { if (pos.cur_link->prev_link != -1) pos.cur_link->prev_link->next_link = pos.cur_link->next_link; if (pos.cur_link->next_link != -1) pos.cur_link->next_link->prev_link = pos.cur_link->prev_link; if (pos.cur_link == _begin_link) _begin_link = pos.cur_link->next_link; if (pos.cur_link == _end_link) throw Exception("BingoList:erase End link can't be removed"); _size--; } void pushBack (const T &x) { insertBefore(end(), x); } void pushBack (const BingoPtr x) { insertBefore(end(), x); } Iterator begin () const { return Iterator(_begin_link); } Iterator top () const { Iterator end_it(_end_link); end_it--; return end_it; } Iterator end () const { return Iterator(_end_link); } private: BingoPtr<_Link> _begin_link; BingoPtr<_Link> _end_link; int _size; }; class MMFStorage; class BingoAllocator { public: template friend class BingoPtr; friend class MMFStorage; static int getAllocatorDataSize (); private: struct _BingoAllocatorData { _BingoAllocatorData() : _min_file_size(0), _max_file_size(0), _cur_file_id(0), _existing_files(0), _free_off(0) { } size_t _min_file_size; size_t _max_file_size; size_t _cur_file_id; dword _existing_files; size_t _free_off; }; ObjArray *_mm_files; size_t _data_offset; static PtrArray _instances; std::string _filename; int _index_id; static OsLock _instances_lock; static void _create (const char *filename, size_t min_size, size_t max_size, size_t alloc_off, ObjArray *mm_files, int index_id); static void _load (const char *filename, size_t alloc_off, ObjArray *mm_files, int index_id, bool read_only); template BingoAddr allocate ( int count = 1 ) { byte * mmf_ptr = (byte *)_mm_files->at(0).ptr(); _BingoAllocatorData *allocator_data = (_BingoAllocatorData *)(mmf_ptr + _data_offset); size_t alloc_size = sizeof(T) * count; size_t file_idx = allocator_data->_cur_file_id; size_t file_off = allocator_data->_free_off; size_t file_size = _mm_files->at((int)file_idx).size(); if (alloc_size > file_size - file_off) _addFile(alloc_size); file_idx = allocator_data->_cur_file_id; file_size = _mm_files->at((int)file_idx).size(); size_t res_off = allocator_data->_free_off; size_t res_id = allocator_data->_cur_file_id; allocator_data->_free_off += alloc_size; if (allocator_data->_free_off == file_size) _addFile(0); return BingoAddr(res_id, res_off); } static BingoAllocator *_getInstance (); byte * _get (size_t file_id, size_t offset); BingoAllocator (); void _addFile (size_t alloc_size); static size_t _getFileSize(size_t idx, size_t min_size, size_t max_size, dword sizes); static void _genFilename (int idx, const char *filename, std::string &out_name); }; // Implementations for BingoPtr and BingoAllocator are dependent and thus implementation is here template T * BingoPtr::ptr() { BingoAllocator *_allocator = BingoAllocator::_getInstance(); return (T *)(_allocator->_get(_addr.file_id, _addr.offset)); } template const T * BingoPtr::ptr() const { BingoAllocator *_allocator = BingoAllocator::_getInstance(); return (T *)(_allocator->_get(_addr.file_id, _addr.offset)); } template void BingoPtr::allocate ( int count ) { BingoAllocator *_allocator = BingoAllocator::_getInstance(); _addr = _allocator->allocate(count); } }; #endif //__bingo_ptr__ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_sim_coef.h000066400000000000000000000014431271037650300234350ustar00rootroot00000000000000#ifndef __sim_coef__ #define __sim_coef__ #include "base_c/defs.h" namespace bingo { struct SimResult { int id; float sim_value; SimResult (int new_id, float new_sim_value) : id(new_id), sim_value(new_sim_value) { } }; class SimCoef { public: SimCoef () { } virtual ~SimCoef () {}; virtual double calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ) = 0; virtual double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ) = 0; virtual double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ) = 0; }; }; #endif /* __sim_coef__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_sim_storage.cpp000066400000000000000000000100571271037650300245210ustar00rootroot00000000000000#include "bingo_sim_storge.h" #include using namespace bingo; SimStorage::SimStorage (int fp_size, int mt_size, int inc_size) : _fingerprint_table(BingoAddr::bingo_null), _inc_size(inc_size), _mt_size(mt_size), _fp_size(fp_size) { _inc_buffer.allocate(_inc_size * _fp_size); _inc_id_buffer.allocate(_inc_size * _fp_size); } BingoAddr SimStorage::create (BingoPtr &ptr, int fp_size, int mt_size, int inc_size) { ptr.allocate(); new(ptr.ptr()) SimStorage(fp_size, mt_size, inc_size); return (BingoAddr)ptr; } void SimStorage::load (BingoPtr &ptr, BingoAddr offset) { ptr = BingoPtr(offset); } void SimStorage::add (const byte *fingerprint, int id) { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) { memcpy(_inc_buffer.ptr() + (_inc_fp_count * _fp_size), fingerprint, _fp_size); _inc_id_buffer[_inc_fp_count] = id; _inc_fp_count++; if (_inc_fp_count == _inc_size) { FingerprintTable::create(_fingerprint_table, _fp_size, _mt_size); for (int i = 0; i < _inc_fp_count; i++) _fingerprint_table->add(_inc_buffer.ptr() + (i * _fp_size), _inc_id_buffer[i]); _inc_fp_count = 0; } } else { _fingerprint_table->add(fingerprint, id); } } void SimStorage::optimize () { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) return; _fingerprint_table->optimize(); } int SimStorage::getCellCount () const { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); return _fingerprint_table->getCellCount(); } int SimStorage::getCellSize (int cell_idx) const { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); return _fingerprint_table->getCellSize(cell_idx); } void SimStorage::getCellsInterval (const byte *query, SimCoef &sim_coef, double min_coef, int &min_cell, int &max_cell) { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); _fingerprint_table->getCellsInterval (query, sim_coef, min_coef, min_cell, max_cell); } int SimStorage::firstFitCell (int query_bit_count, int min_cell, int max_cell) const { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); return _fingerprint_table->firstFitCell(query_bit_count, min_cell, max_cell); } int SimStorage::nextFitCell (int query_bit_count, int first_fit_cell, int min_cell, int max_cell, int idx) const { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); return _fingerprint_table->nextFitCell(query_bit_count, first_fit_cell, min_cell, max_cell, idx); } int SimStorage::getSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cell_idx, int cont_idx) { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) throw Exception("SimStorage: fingerptint table wasn't built"); return _fingerprint_table->getSimilar(query, sim_coef, min_coef, sim_fp_indices, cell_idx, cont_idx); } bool SimStorage::isSmallBase () { if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null) return true; return false; } int SimStorage::getIncSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices) { for (int i = 0; i < _inc_fp_count; i++) { double coef = sim_coef.calcCoef(_inc_buffer.ptr() + (i * _fp_size), query, -1, -1); if (coef < min_coef) continue; size_t id = _inc_id_buffer[i]; sim_fp_indices.push(SimResult(id, coef)); } return sim_fp_indices.size(); } SimStorage::~SimStorage () { }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_sim_storge.h000066400000000000000000000033061271037650300240240ustar00rootroot00000000000000#ifndef __sim_storage__ #define __sim_storage__ #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "math/algebra.h" #include "time.h" #include "new" #include "bingo_fingerprint_table.h" #include using namespace indigo; namespace bingo { class SimStorage { public: SimStorage (int fp_size, int mt_size, int inc_size); static BingoAddr create (BingoPtr &ptr, int fp_size, int mt_size, int inc_size); static void load (BingoPtr &ptr, BingoAddr offset); void add (const byte *fingerprint, int id); void optimize (); int getCellCount () const; int getCellSize (int cell_idx) const; void getCellsInterval (const byte *query, SimCoef &sim_coef, double min_coef, int &min_cell, int &max_cell); int firstFitCell (int query_bit_count, int min_cell, int max_cell ) const; int nextFitCell (int query_bit_count, int first_fit_cell, int min_cell, int max_cell, int idx) const; int getSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices, int cell_idx, int cont_idx); bool isSmallBase (); int getIncSimilar (const byte *query, SimCoef &sim_coef, double min_coef, Array &sim_fp_indices); ~SimStorage(); private: BingoPtr< FingerprintTable > _fingerprint_table; BingoPtr< byte > _inc_buffer; BingoPtr< size_t > _inc_id_buffer; int _inc_size; int _inc_fp_count; int _mt_size; int _fp_size; }; }; #endif /* __fingerprint_table__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_tanimoto_coef.cpp000066400000000000000000000022421271037650300250300ustar00rootroot00000000000000#include "bingo_tanimoto_coef.h" using namespace bingo; TanimotoCoef::TanimotoCoef( int fp_size ) : _fp_size(fp_size) { } double TanimotoCoef::calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ) { int common_bits = bitCommonOnes(target, query, _fp_size); int unique_bits = bitDifferentOnes(target, query, _fp_size); return (double)common_bits / (common_bits + unique_bits); } double TanimotoCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ) { int min = (query_bit_count < max_target_bit_count ? query_bit_count : max_target_bit_count); int max = (query_bit_count > min_target_bit_count ? query_bit_count : min_target_bit_count); return (double)min / max; } double TanimotoCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ) { int b = query_bit_count - m01; int min_a = min_target_bit_count - m10; int max_a = max_target_bit_count - m10; int min = (b > max_a ? max_a : b ); int max = (b < min_a ? min_a : b ); return (double)min / (m10 + m01 + max); }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_tanimoto_coef.h000066400000000000000000000012401271037650300244720ustar00rootroot00000000000000#ifndef __tanimoto_coef__ #define __tanimoto_coef__ #include "bingo_sim_coef.h" #include #include "base_c/bitarray.h" namespace bingo { class TanimotoCoef : public SimCoef { public: TanimotoCoef (int fp_size); double calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ); private: int _fp_size; }; }; #endif /* __tanimoto_coef__ */ Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_tversky_coef.cpp000066400000000000000000000030461271037650300247100ustar00rootroot00000000000000#include "bingo_tversky_coef.h" using namespace bingo; TverskyCoef::TverskyCoef (int fp_size) : _fp_size(fp_size) { _alpha = 1; _beta = 0; } TverskyCoef::TverskyCoef (int fp_size, double a, double b) : _fp_size(fp_size) { _alpha = a; _beta = b; } double TverskyCoef::calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ) { int common_bits = bitCommonOnes(target, query, _fp_size); if (target_bit_count == -1) target_bit_count = bitGetOnesCount(target, _fp_size); if (query_bit_count == -1) query_bit_count = bitGetOnesCount(query, _fp_size); return (double)common_bits / ((target_bit_count - common_bits) * _alpha + (query_bit_count - common_bits) * _beta + common_bits); } double TverskyCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ) { if (fabs(_alpha + _beta - 1) > 1e-7) return 1; int min = (query_bit_count < max_target_bit_count ? query_bit_count : max_target_bit_count); return min / (_alpha * min_target_bit_count + _beta * query_bit_count); } double TverskyCoef::calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ) { if (fabs(_alpha + _beta - 1) > 1e-7) return 1; int max_a = max_target_bit_count - m10; int b = query_bit_count - m01; int min = (b > max_a ? max_a : b ); return (double)min / (_alpha * min_target_bit_count + _beta * query_bit_count); }Indigo-indigo-1.2.3/api/plugins/bingo/src/bingo_tversky_coef.h000066400000000000000000000013761271037650300243610ustar00rootroot00000000000000#ifndef __tversky_coef__ #define __tversky_coef__ #include "bingo_sim_coef.h" #include #include "base_c/bitarray.h" namespace bingo { class TverskyCoef : public SimCoef { public: TverskyCoef (int fp_size); TverskyCoef (int fp_size, double a, double b); double calcCoef (const byte *target, const byte *query, int target_bit_count, int query_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count ); double calcUpperBound (int query_bit_count, int min_target_bit_count, int max_target_bit_count, int m10, int m01 ); private: int _fp_size; double _alpha; double _beta; }; }; #endif /* __tversky_coef__ */ Indigo-indigo-1.2.3/api/plugins/bingo/tests/000077500000000000000000000000001271037650300206735ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/tests/c/000077500000000000000000000000001271037650300211155ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/bingo/tests/c/bingo-test.c000066400000000000000000000004771271037650300233440ustar00rootroot00000000000000#include #include #include #include "indigo.h" #include "bingo.h" void onError (const char *message, void *context) { fprintf(stderr, "Error: %s\n", message); exit(-1); } int main (void) { indigoSetErrorHandler(onError, 0); printf("%s\n", indigoVersion()); return 0; } Indigo-indigo-1.2.3/api/plugins/inchi/000077500000000000000000000000001271037650300175255ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/CMakeLists.txt000066400000000000000000000074401271037650300222720ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoInchi CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../../common/cmake/) file (GLOB IndigoInchi_src src/*.c*) file (GLOB IndigoInchi_headers *.h src/*.h*) include_directories(${IndigoInchi_SOURCE_DIR} ${Indigo_SOURCE_DIR} ${Indigo_SOURCE_DIR}/src ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${InChI_SOURCE_DIR}/inchi_dll) include(DefineTest) add_library(indigo-inchi-Obj OBJECT ${IndigoInchi_src} ${IndigoInchi_headers}) set_target_properties(indigo-inchi-Obj PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") # Indigo InChI static if (NOT NO_STATIC) add_library(indigo-inchi STATIC $) if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(indigo-inchi PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/indigo_inchi.so.map) endif() if(APPLE) SET_TARGET_PROPERTIES(indigo-inchi PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/indigo_inchi.explist") endif() set_target_properties(indigo-inchi PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") SET_TARGET_PROPERTIES(indigo-inchi PROPERTIES OUTPUT_NAME "indigo-inchi-static") set_property(TARGET indigo-inchi PROPERTY FOLDER "indigo-inchi") # No exports in case of static library: define empty EXPORT_SYMBOL definition set_target_properties(indigo-inchi PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DEXPORT_SYMBOL=") PACK_STATIC(indigo-inchi) # DEFINE_TEST(indigo-inchi-c-test-static "tests/c/indigo-inchi-test.c;${Common_SOURCE_DIR}/hacks/memcpy.c" indigo-inchi) # target_link_libraries(indigo-inchi-c-test-static inchi) # #Add stdc++ library required by indigo # SET_TARGET_PROPERTIES(indigo-inchi-c-test-static PROPERTIES LINKER_LANGUAGE CXX) # if (UNIX AND NOT APPLE) # if(${SUBSYSTEM_NAME} MATCHES "x64") # set_target_properties(indigo-inchi-c-test-static PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") # endif() # endif() endif() # Indigo InChI shared add_library(indigo-inchi-shared SHARED $ ${Common_SOURCE_DIR}/hacks/memcpy.c) target_link_libraries(indigo-inchi-shared inchi) SET_TARGET_PROPERTIES(indigo-inchi-shared PROPERTIES OUTPUT_NAME "indigo-inchi") if (MSVC OR MINGW) set_target_properties(indigo-inchi-shared PROPERTIES PREFIX "") endif() if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(indigo-inchi-shared PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/indigo_inchi.so.map) endif() if(APPLE) SET_TARGET_PROPERTIES(indigo-inchi-shared PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/indigo_inchi.explist") endif() set_target_properties(indigo-inchi-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if (UNIX AND NOT APPLE) if(${SUBSYSTEM_NAME} MATCHES "x64") set_target_properties(indigo-inchi-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() endif() if(MSVC) # This should be set only for a shared library to avoid warnings set_target_properties(indigo-inchi-shared PROPERTIES COMPILE_FLAGS "-D_WINDLL -DINDIGO_PLUGIN") endif() if (MSVC OR MINGW) target_link_libraries(indigo-inchi-shared indigo-shared) set_property(TARGET indigo-inchi-shared PROPERTY LINK_INTERFACE_LIBRARIES "inchi;indigo-shared") else() set_property(TARGET indigo-inchi-shared PROPERTY LINK_INTERFACE_LIBRARIES "inchi") endif() set_property(TARGET indigo-inchi-shared PROPERTY FOLDER "indigo-inchi") IF (NOT PACK_INDIGO_NOT) PACK_SHARED(indigo-inchi-shared) ENDIF() DEFINE_TEST(indigo-inchi-c-test-shared "tests/c/indigo-inchi-test.c" "") ADD_DEPENDENCIES(indigo-inchi-c-test-shared indigo-inchi-shared) target_link_libraries(indigo-inchi-c-test-shared indigo-shared indigo-inchi-shared) if (UNIX AND NOT APPLE) target_link_libraries(indigo-inchi-c-test-shared pthread) endif() Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/000077500000000000000000000000001271037650300210225ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/IndigoInchi.cs000066400000000000000000000051061271037650300235370ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.IO; namespace com.epam.indigo { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public unsafe class IndigoInchi { private Indigo _indigo; private IndigoInchiLib _inchi_lib; public IndigoInchi (Indigo indigo) { String dllpath = indigo.getDllPath (); string libraryName; IndigoDllLoader dll_loader = IndigoDllLoader.Instance; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: libraryName = "indigo-inchi.dll"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesWin", false); break; case PlatformID.Unix: if (IndigoDllLoader.isMac()) { libraryName = "libindigo-inchi.dylib"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesMac", false); } else { libraryName = "libindigo-inchi.so"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesLinux", false); } break; default: throw new PlatformNotSupportedException (String.Format ("Unsupported platform: {0}", Environment.OSVersion.Platform)); } _inchi_lib = dll_loader.getInterface (libraryName); _indigo = indigo; } public String version () { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiVersion ())); } public int resetOptions () { _indigo.setSessionID (); return _indigo.checkResult (_inchi_lib.indigoInchiResetOptions ()); } public IndigoObject loadMolecule (String inchi_string) { _indigo.setSessionID (); return new IndigoObject (_indigo, _indigo.checkResult (_inchi_lib.indigoInchiLoadMolecule (inchi_string))); } public String getInchi (IndigoObject molecule) { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiGetInchi (molecule.self))); } public String getInchiKey (String inchi_string) { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiGetInchiKey (inchi_string))); } public String getWarning () { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiGetWarning ())); } public String getLog () { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiGetLog ())); } public String getAuxInfo () { _indigo.setSessionID (); return new String (_indigo.checkResult (_inchi_lib.indigoInchiGetAuxInfo ())); } } } Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/IndigoInchiLib.cs000066400000000000000000000010031271037650300241560ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; namespace com.epam.indigo { public unsafe interface IndigoInchiLib { sbyte* indigoInchiVersion(); int indigoInchiResetOptions(); int indigoInchiLoadMolecule(String inchi_string); sbyte* indigoInchiGetInchi(int molecule); sbyte* indigoInchiGetInchiKey(String inchi_string); sbyte* indigoInchiGetWarning(); sbyte* indigoInchiGetLog(); sbyte* indigoInchiGetAuxInfo(); } }Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/000077500000000000000000000000001271037650300231565ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/AssemblyInfo.cs000066400000000000000000000026001271037650300260760ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Indigo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010-2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("96c7fefe-a81f-448d-94cf-61cf4a5dd8c6")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.*")] [assembly: AssemblyFileVersion("1.1.0.0")] Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/Resources.Designer.cs000066400000000000000000000063351271037650300272250ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.261 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace com.epam.indigo.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("com.epam.indigo.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } internal static byte[] Win_x64_indigo_inchi_dll { get { object obj = ResourceManager.GetObject("Win/x64/indigo-inchi.dll", resourceCulture); return ((byte[])(obj)); } } internal static byte[] Win_x86_indigo_inchi_dll { get { object obj = ResourceManager.GetObject("Win/x86/indigo-inchi.dll", resourceCulture); return ((byte[])(obj)); } } } } Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/ResourcesLinux.resx000066400000000000000000000143321271037650300270560ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x64\libindigo-inchi.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x86\libindigo-inchi.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/ResourcesMac.resx000066400000000000000000000143571271037650300264660ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Mac\10.7\libindigo-inchi.dylib;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/Properties/ResourcesWin.resx000066400000000000000000000143151271037650300265150ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo-inchi.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo-inchi.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/indigo-inchi-dotnet.csproj000066400000000000000000000126461271037650300261110ustar00rootroot00000000000000 Debug AnyCPU 8.0.30703 2.0 {A88F82B7-F522-4238-80D6-DE033CD7892E} Library Properties com.epam.indigo indigo-inchi-dotnet v2.0 512 true true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 true true bin\x64\Debug\ DEBUG;TRACE full x64 prompt true 4 false bin\x64\Release\ TRACE true true pdbonly x64 prompt false false 4 true indigo-inchi.snk false ..\..\..\dotnet\bin\Release\indigo-dotnet.dll True True $(Copy) $(LibraryPath)\Win\x64\indigo-inchi.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\indigo-inchi.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo-inchi.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo-inchi.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo-inchi.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\indigo-inchi.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\indigo-inchi.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo-inchi.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo-inchi.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo-inchi.dylib $(ProjectDir)Resource\Mac\10.7 Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/indigo-inchi-dotnet.sln000066400000000000000000000023471271037650300254020ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indigo-inchi-dotnet", "indigo-inchi-dotnet.csproj", "{A88F82B7-F522-4238-80D6-DE033CD7892E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|x64.ActiveCfg = Debug|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Debug|x64.Build.0 = Debug|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|Any CPU.Build.0 = Release|Any CPU {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|x64.ActiveCfg = Release|x64 {A88F82B7-F522-4238-80D6-DE033CD7892E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Indigo-indigo-1.2.3/api/plugins/inchi/dotnet/indigo-inchi.snk000066400000000000000000000011241271037650300240760ustar00rootroot00000000000000$RSA2a}ö'_uß× ¨#Ȇ'P} À°»—«×ëpö©®g`\ë¾kìèâk¾ÝÛEhî˜È"í,θ‹ä ’G°ÎIÒ%0DT°Ã.v¹ù?ý„“ß¹Q¾Þz<<‹@›ŒH˜ÍΪÏÏ”EfaÿâɾUK´ÜËäÏc°@¬Ù84`•äxWÞý3-ËýSÙV¥Ë4ßQŸpáF‰¨û9¼IïÛN6õfß„,®ì$zÕbÃq™ü2zœ­ÖÔÉÓ¼ ¦éWã!&ñÇÉ»=Ö`“Ħéãqs2&œCIàɪnhZl>¶Z>÷\Z‚ žI””¦’/ϱö$+ ˪Asn"ÔÙaç»üŠÓ” Y’‹Ý]'GcÓٷĆ@)©ï#Hk•äÜÈ+®iÑÙÛ·7Âúö@+"€1M-ÜË쇕Œ·S%ÂJ]`øÔŒF›^¶Õ¡{ào ¡3°¼U´«¼:_ÒéëÎØ2"øqà¼3â=zíôý7ñ˜'‹/W„´GÿîŒVúª2hXţ؈›»M–T¡ Ë«,ït2-jY±«Ù¤‰¼'k§Pö<ÛÉWv ÈæÌp18Ë]N%_¶)%ñïÆÁó4‹KRø37ý.%ÙÌà¥øå/Íg'M5´‡vk\-(ï4A¼|Eš÷7ÛïOËúÖµseq£ŽîËd£VÄQcNÔõÙ‹PÓ×½N2kK€jÕâ Indigo-indigo-1.2.3/api/plugins/inchi/indigo-inchi.h000066400000000000000000000022451271037650300222420ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_inchi__ #define __indigo_inchi__ #include "indigo.h" CEXPORT const char* indigoInchiVersion (); CEXPORT int indigoInchiResetOptions (); CEXPORT int indigoInchiLoadMolecule (const char *inchi_string); CEXPORT const char* indigoInchiGetInchi (int molecule); CEXPORT const char* indigoInchiGetInchiKey (const char *inchi_string); CEXPORT const char* indigoInchiGetWarning (); CEXPORT const char* indigoInchiGetLog (); CEXPORT const char* indigoInchiGetAuxInfo (); #endif // __indigo_inchi__ Indigo-indigo-1.2.3/api/plugins/inchi/indigo_inchi.explist000066400000000000000000000000161271037650300235570ustar00rootroot00000000000000_indigoInchi* Indigo-indigo-1.2.3/api/plugins/inchi/indigo_inchi.so.map000066400000000000000000000000571271037650300232710ustar00rootroot00000000000000{ global: indigoInchi*; local: *; }; Indigo-indigo-1.2.3/api/plugins/inchi/java/000077500000000000000000000000001271037650300204465ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/pom.xml000066400000000000000000000142441271037650300217700ustar00rootroot00000000000000 4.0.0 com.epam.indigo indigo-inchi 1.2.2beta.r117 jar Indigo InChI Universal organic chemistry toolkit InChI plugin http://lifescience.opensource.epam.com/indigo/api GNU General Public License v3 http://www.gnu.org/licenses/gpl.html repo engineer Mikhail Kviatkovskii mikhail_kviatkovskii@epam.com UTC+3 scm:git:git@github.com:epam/Indigo.git scm:git:git@github.com:epam/Indigo.git git@github.com:epam/Indigo.git net.java.dev.jna jna 3.5.0 com.epam.indigo indigo ${project.version} org.apache.maven.plugins maven-compiler-plugin 3.1 1.5 1.5 org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources package jar org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs package jar org.apache.maven.plugins maven-jar-plugin 2.3.1 false org.sonatype.plugins nexus-staging-maven-plugin 1.6.3 true deploy ossrh https://oss.sonatype.org/ false ../../../libs/shared **/*.lib **/*inchi.* ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.4 ${gpg.passhprase} --no-tty sign-artifacts verify sign Indigo-indigo-1.2.3/api/plugins/inchi/java/src/000077500000000000000000000000001271037650300212355ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/000077500000000000000000000000001271037650300221615ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/000077500000000000000000000000001271037650300231025ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/com/000077500000000000000000000000001271037650300236605ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/com/epam/000077500000000000000000000000001271037650300246025ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/com/epam/indigo/000077500000000000000000000000001271037650300260535ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/com/epam/indigo/IndigoInchi.java000066400000000000000000000065321271037650300311100ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Native; import java.io.File; import java.io.IOException; public class IndigoInchi { public IndigoInchi (Indigo indigo) { loadLibrary(indigo.getUserSpecifiedPath()); _indigo = indigo; } public String version () { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiVersion()); } public void resetOptions () { _indigo.setSessionID(); Indigo.checkResult(this, _lib.indigoInchiResetOptions()); } public IndigoObject loadMolecule (String inchi) { _indigo.setSessionID(); return new IndigoObject(_indigo, Indigo.checkResult(this, _lib.indigoInchiLoadMolecule(inchi))); } public String getInchi (IndigoObject molecule) { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiGetInchi(molecule.self)); } public String getInchiKey (String inchi) { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiGetInchiKey(inchi)); } public String getWarning () { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiGetWarning()); } public String getLog () { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiGetLog()); } public String getAuxInfo () { _indigo.setSessionID(); return Indigo.checkResultString(this, _lib.indigoInchiGetAuxInfo()); } private static String getPathToBinary (String path, String filename) { String dllpath = Indigo.getPlatformDependentPath(); if (path == null) { String res = Indigo.extractFromJar(IndigoInchi.class, "/" + dllpath, filename); if (res != null) return res; path = "lib"; } path = path + File.separator + dllpath + File.separator + filename; try { return (new File(path)).getCanonicalPath(); } catch (IOException e) { return path; } } private synchronized static void loadLibrary (String path) { if (_lib != null) return; int os = Indigo.getOs(); if (os == Indigo.OS_LINUX || os == Indigo.OS_SOLARIS) _lib = (IndigoInchiLib)Native.loadLibrary(getPathToBinary(path, "libindigo-inchi.so"), IndigoInchiLib.class); else if (os == Indigo.OS_MACOS) _lib = (IndigoInchiLib)Native.loadLibrary(getPathToBinary(path, "libindigo-inchi.dylib"), IndigoInchiLib.class); else // os == OS_WINDOWS _lib = (IndigoInchiLib)Native.loadLibrary(getPathToBinary(path, "indigo-inchi.dll"), IndigoInchiLib.class); } Indigo _indigo; static IndigoInchiLib _lib; } Indigo-indigo-1.2.3/api/plugins/inchi/java/src/main/java/com/epam/indigo/IndigoInchiLib.java000066400000000000000000000021261271037650300315320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Library; import com.sun.jna.Pointer; public interface IndigoInchiLib extends Library { Pointer indigoInchiVersion (); int indigoInchiResetOptions (); int indigoInchiLoadMolecule (String inchi_string); Pointer indigoInchiGetInchi (int molecule); Pointer indigoInchiGetInchiKey (String inchi_string); Pointer indigoInchiGetWarning (); Pointer indigoInchiGetLog (); Pointer indigoInchiGetAuxInfo (); } Indigo-indigo-1.2.3/api/plugins/inchi/python/000077500000000000000000000000001271037650300210465ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/python/indigo_inchi.py000066400000000000000000000063521271037650300240510ustar00rootroot00000000000000# # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. from indigo import * class IndigoInchi(object): def __init__(self, indigo): self.indigo = indigo if os.name == 'posix' and not platform.mac_ver()[0] and not platform.system().startswith("CYGWIN"): self._lib = CDLL(indigo.dllpath + "/libindigo-inchi.so") elif os.name == 'nt' or platform.system().startswith("CYGWIN"): self._lib = CDLL(indigo.dllpath + "\indigo-inchi.dll") elif platform.mac_ver()[0]: self._lib = CDLL(indigo.dllpath + "/libindigo-inchi.dylib") else: raise IndigoException("unsupported OS: " + os.name) self._lib.indigoInchiVersion.restype = c_char_p self._lib.indigoInchiVersion.argtypes = [] self._lib.indigoInchiResetOptions.restype = c_int self._lib.indigoInchiResetOptions.argtypes = [] self._lib.indigoInchiLoadMolecule.restype = c_int self._lib.indigoInchiLoadMolecule.argtypes = [c_char_p] self._lib.indigoInchiGetInchi.restype = c_char_p self._lib.indigoInchiGetInchi.argtypes = [c_int] self._lib.indigoInchiGetInchiKey.restype = c_char_p self._lib.indigoInchiGetInchiKey.argtypes = [c_char_p] self._lib.indigoInchiGetWarning.restype = c_char_p self._lib.indigoInchiGetWarning.argtypes = [] self._lib.indigoInchiGetLog.restype = c_char_p self._lib.indigoInchiGetLog.argtypes = [] self._lib.indigoInchiGetAuxInfo.restype = c_char_p self._lib.indigoInchiGetAuxInfo.argtypes = [] def resetOptions(self): self.indigo._setSessionId() self.indigo._checkResult(self._lib.indigoInchiResetOptions()) def loadMolecule(self, inchi): self.indigo._setSessionId() res = self.indigo._checkResult(self._lib.indigoInchiLoadMolecule(inchi.encode('ascii'))) if res == 0: return None return self.indigo.IndigoObject(self.indigo, res) def version(self): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiVersion()) def getInchi(self, molecule): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiGetInchi(molecule.id)) def getInchiKey(self, inchi): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiGetInchiKey(inchi.encode('ascii'))) def getWarning(self): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiGetWarning()) def getLog(self): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiGetLog()) def getAuxInfo(self): self.indigo._setSessionId() return self.indigo._checkResultString(self._lib.indigoInchiGetAuxInfo()) Indigo-indigo-1.2.3/api/plugins/inchi/src/000077500000000000000000000000001271037650300203145ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/src/indigo_inchi_api.cpp000066400000000000000000000073271271037650300243050ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/inchi_wrapper.h" #include "indigo_internal.h" #include "indigo_molecule.h" using namespace indigo; CEXPORT const char* indigoInchiVersion() { return InchiWrapper::version(); } // // Session Inchi instance // class IndigoInchiContext : public IndigoPluginContext { public: InchiWrapper inchi; virtual void init() { inchi.clear(); } }; _SessionLocalContainer inchi_wrapper_self; IndigoInchiContext &indigoInchiGetInstance() { IndigoInchiContext &inst = inchi_wrapper_self.getLocalCopy(); inst.validate(); return inst; } // // C interface functions // CEXPORT int indigoInchiResetOptions(void) { IndigoInchiContext &inchi_context = indigoInchiGetInstance(); inchi_context.init(); return 0; } CEXPORT int indigoInchiLoadMolecule(const char *inchi_string) { INDIGO_BEGIN { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; AutoPtr mol_obj(new IndigoMolecule()); const char *aux_prefix = "AuxInfo"; if (strncmp(inchi_string, aux_prefix, strlen(aux_prefix)) == 0) inchi_wrapper.loadMoleculeFromAux(inchi_string, mol_obj->mol); else inchi_wrapper.loadMoleculeFromInchi(inchi_string, mol_obj->mol); return self.addObject(mol_obj.release()); } INDIGO_END(-1) } CEXPORT const char* indigoInchiGetInchi(int molecule) { INDIGO_BEGIN { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; IndigoObject &obj = self.getObject(molecule); auto &tmp = self.getThreadTmpData(); inchi_wrapper.saveMoleculeIntoInchi(obj.getMolecule(), tmp.string); return tmp.string.ptr(); } INDIGO_END(0) } CEXPORT const char* indigoInchiGetInchiKey(const char *inchi_string) { INDIGO_BEGIN { auto &tmp = self.getThreadTmpData(); InchiWrapper::InChIKey(inchi_string, tmp.string); return tmp.string.ptr(); } INDIGO_END(0) } CEXPORT const char* indigoInchiGetWarning() { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; if (inchi_wrapper.warning.size() != 0) return inchi_wrapper.warning.ptr(); return ""; } CEXPORT const char* indigoInchiGetLog() { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; if (inchi_wrapper.log.size() != 0) return inchi_wrapper.log.ptr(); return ""; } CEXPORT const char* indigoInchiGetAuxInfo() { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; if (inchi_wrapper.auxInfo.size() != 0) return inchi_wrapper.auxInfo.ptr(); return ""; } // // Options // void indigoInchiSetInchiOptions (const char *options) { InchiWrapper &inchi_wrapper = indigoInchiGetInstance().inchi; inchi_wrapper.setOptions(options); } class _IndigoInchiOptionsHandlersSetter { public: _IndigoInchiOptionsHandlersSetter (); }; _IndigoInchiOptionsHandlersSetter::_IndigoInchiOptionsHandlersSetter () { OptionManager &mgr = indigoGetOptionManager(); OsLocker locker(mgr.lock); mgr.setOptionHandlerString("inchi-options", indigoInchiSetInchiOptions); } _IndigoInchiOptionsHandlersSetter _indigo_inchi_options_handlers_setter; Indigo-indigo-1.2.3/api/plugins/inchi/tests/000077500000000000000000000000001271037650300206675ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/tests/c/000077500000000000000000000000001271037650300211115ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/inchi/tests/c/indigo-inchi-test.c000066400000000000000000000013761271037650300246020ustar00rootroot00000000000000#include #include #include #include "indigo.h" #include "indigo-inchi.h" void onError (const char *message, void *context) { fprintf(stderr, "Error: %s\n", message); exit(-1); } int main (void) { int m; const char *inchi = "InChI=1S/C10H20N2O2/c11-7-1-5-2-8(12)10(14)4-6(5)3-9(7)13/h5-10,13-14H,1-4,11-12H2"; const char *res_inchi; indigoSetErrorHandler(onError, 0); printf("%s\n", indigoVersion()); m = indigoInchiLoadMolecule(inchi); printf("%s\n", indigoCanonicalSmiles(m)); res_inchi = indigoInchiGetInchi(m); if (strcmp(res_inchi, inchi) != 0) { printf("Converted Inchi is invalid: %s != %s\n", inchi, res_inchi); exit(-1); } return 0; } Indigo-indigo-1.2.3/api/plugins/renderer/000077500000000000000000000000001271037650300202415ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/CMakeLists.txt000066400000000000000000000105111271037650300227770ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoRenderer CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) file (GLOB IndigoRenderer_src src/*.c*) file (GLOB IndigoRenderer_headers *.h src/*.h*) include_directories(${IndigoRenderer_SOURCE_DIR} ${Indigo_SOURCE_DIR} ${Indigo_SOURCE_DIR}/src ${Cairo_SOURCE_DIR} ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/..) include(DefineTest) add_library(indigo-renderer-Obj OBJECT ${IndigoRenderer_src} ${IndigoRenderer_headers}) set_target_properties(indigo-renderer-Obj PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") # Indigo Renderer static if (NOT NO_STATIC) add_library(indigo-renderer STATIC $ $) if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(indigo-renderer PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/indigo_renderer.so.map) endif() if(APPLE) SET_TARGET_PROPERTIES(indigo-renderer PROPERTIES LINK_FLAGS "-Xlinker -framework -Xlinker ApplicationServices -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/indigo_renderer.explist") endif() set_target_properties(indigo-renderer PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") SET_TARGET_PROPERTIES(indigo-renderer PROPERTIES OUTPUT_NAME "indigo-renderer-static") set_property(TARGET indigo-renderer PROPERTY FOLDER "indigo-renderer") # No exports in case of static library: define empty EXPORT_SYMBOL definition set_target_properties(indigo-renderer PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -DEXPORT_SYMBOL=") pack_static(indigo-renderer) # DEFINE_TEST(indigo-renderer-c-test-static "tests/c/indigo-renderer-test.c;${Common_SOURCE_DIR}/hacks/memcpy.c" indigo-renderer) # # Add stdc++ library required by indigo # SET_TARGET_PROPERTIES(indigo-renderer-c-test-static PROPERTIES LINKER_LANGUAGE CXX) # if (UNIX AND NOT APPLE) # if(${SUBSYSTEM_NAME} MATCHES "x64") # set_target_properties(indigo-renderer-c-test-static PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") # endif() # endif() # if(APPLE) # INCLUDE(GetSystemVersion) # INCLUDE(MacFrameworks) # FIND_FRAMEWORK(ApplicationServices) # target_link_libraries(indigo-renderer-c-test-static ${FRAMEWORK_ApplicationServices}) # endif() endif() # Indigo Renderer shared add_library(indigo-renderer-shared SHARED $ $ ${Common_SOURCE_DIR}/hacks/memcpy.c) SET_TARGET_PROPERTIES(indigo-renderer-shared PROPERTIES OUTPUT_NAME "indigo-renderer") if (MSVC OR MINGW) set_target_properties(indigo-renderer-shared PROPERTIES PREFIX "") endif() if(UNIX AND NOT APPLE) SET_TARGET_PROPERTIES(indigo-renderer-shared PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/indigo_renderer.so.map") endif() if(APPLE) SET_TARGET_PROPERTIES(indigo-renderer-shared PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -Xlinker -framework -Xlinker ApplicationServices -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/indigo_renderer.explist") endif() set_target_properties(indigo-renderer-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if (UNIX AND NOT APPLE) if(${SUBSYSTEM_NAME} MATCHES "x64") set_target_properties(indigo-renderer-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() endif() if (MSVC OR MINGW) target_link_libraries(indigo-renderer-shared cairo pixman png z indigo-shared) set_property(TARGET indigo-renderer-shared PROPERTY LINK_INTERFACE_LIBRARIES "cairo;pixman;png;z;indigo-shared") else() target_link_libraries(indigo-renderer-shared cairo pixman png z) set_property(TARGET indigo-renderer-shared PROPERTY LINK_INTERFACE_LIBRARIES "cairo;pixman;png;z") endif() set_property(TARGET indigo-renderer-shared PROPERTY FOLDER "indigo-renderer") if(MSVC) # This should be set only for a shared library to avoid warnings set_target_properties(indigo-renderer-shared PROPERTIES COMPILE_FLAGS "-D_WINDLL -DINDIGO_PLUGIN") endif() if (NOT PACK_INDIGO_NOT) pack_shared(indigo-renderer-shared) endif() DEFINE_TEST(indigo-renderer-c-test-shared "tests/c/indigo-renderer-test.c" indigo-renderer-shared) target_link_libraries(indigo-renderer-c-test-shared indigo-shared) if(APPLE) INCLUDE(MacFrameworks) FIND_FRAMEWORK(ApplicationServices) target_link_libraries(indigo-renderer-c-test-shared ${FRAMEWORK_ApplicationServices}) endif() Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/000077500000000000000000000000001271037650300215365ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/IndigoRenderer.cs000066400000000000000000000110201271037650300247570ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace com.epam.indigo { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public unsafe class IndigoRenderer { private Indigo _indigo; private IndigoRendererLib _renderer_lib; public IndigoRenderer (Indigo indigo) { String dllpath = indigo.getDllPath (); string libraryName; IndigoDllLoader dll_loader = IndigoDllLoader.Instance; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: libraryName = "indigo-renderer.dll"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesWin", false); break; case PlatformID.Unix: if (IndigoDllLoader.isMac()) { libraryName = "libindigo-renderer.dylib"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesMac", false); } else { libraryName = "libindigo-renderer.so"; dll_loader.loadLibrary (dllpath, libraryName, "com.epam.indigo.Properties.ResourcesLinux", false); } break; default: throw new PlatformNotSupportedException (String.Format ("Unsupported platform: {0}", Environment.OSVersion.Platform)); } _renderer_lib = dll_loader.getInterface (libraryName); _indigo = indigo; } public void renderToFile (IndigoObject obj, string filename) { _indigo.setSessionID (); _indigo.checkResult (_renderer_lib.indigoRenderToFile (obj.self, filename)); } public byte[] renderToBuffer (IndigoObject obj) { _indigo.setSessionID (); using (IndigoObject bufh = _indigo.writeBuffer()) { _indigo.checkResult (_renderer_lib.indigoRender (obj.self, bufh.self)); byte* buf; int bufsize; _indigo.checkResult (_indigo._indigo_lib.indigoToBuffer (bufh.self, &buf, &bufsize)); byte[] res = new byte[bufsize]; for (int i = 0; i < bufsize; ++i) res [i] = buf [i]; return res; } } public void renderToHDC (IndigoObject obj, IntPtr hdc, bool printing) { _indigo.setSessionID (); int hdch = _indigo.checkResult (_renderer_lib.indigoRenderWriteHDC ((void*)hdc, printing ? 1 : 0)); _indigo.checkResult (_renderer_lib.indigoRender (obj.self, hdch)); } public Bitmap renderToBitmap (IndigoObject obj) { _indigo.setSessionID (); _indigo.checkResult (_indigo._indigo_lib.indigoSetOption ("render-output-format", "png")); byte[] res = renderToBuffer (obj); MemoryStream stream = new MemoryStream (); stream.Write (res, 0, res.Length); stream.Seek (0, SeekOrigin.Begin); return (Bitmap)Image.FromStream (stream); } public Metafile renderToMetafile (IndigoObject obj) { _indigo.setSessionID (); _indigo.checkResult (_indigo._indigo_lib.indigoSetOption ("render-output-format", "emf")); byte[] res = renderToBuffer (obj); MemoryStream ms = new MemoryStream (res); Metafile mf = new Metafile (ms); return mf; } public byte[] renderGridToBuffer (IndigoObject items, int[] refatoms, int ncolumns) { using (IndigoObject bufh = _indigo.writeBuffer()) { if (refatoms != null) if (refatoms.Length != items.count ()) throw new IndigoException ("renderGridToFile(): refatoms[] size must be equal to the number of objects"); _indigo.checkResult (_renderer_lib.indigoRenderGrid (items.self, refatoms, ncolumns, bufh.self)); return bufh.toBuffer (); } } public void renderGridToFile (IndigoObject items, int[] refatoms, int ncolumns, string filename) { if (refatoms != null) if (refatoms.Length != items.count ()) throw new IndigoException ("renderGridToFile(): refatoms[] size must be equal to the number of objects"); _indigo.checkResult(_renderer_lib.indigoRenderGridToFile (items.self, refatoms, ncolumns, filename)); } public static void SaveMetafile (Metafile mf, Stream stream) { IntPtr henh = mf.GetHenhmetafile (); int size = GetEnhMetaFileBits (henh, 0, null); byte[] buffer = new byte[size]; if (GetEnhMetaFileBits (henh, size, buffer) <= 0) throw new SystemException ("GetEnhMetaFileBits"); stream.Write (buffer, 0, buffer.Length); stream.Flush (); } public static void SaveMetafile (Metafile mf, String path) { FileStream fs = new FileStream (path, FileMode.OpenOrCreate); SaveMetafile (mf, fs); fs.Close (); } [DllImport("gdi32")] private static extern int GetEnhMetaFileBits (IntPtr hemf, int cbBuffer, byte[] lpbBuffer); } } Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/IndigoRendererLib.cs000066400000000000000000000007611271037650300254200ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; namespace com.epam.indigo { public unsafe interface IndigoRendererLib { int indigoRenderWriteHDC (void* hdc, int printingHdc); int indigoRender (int item, int output); int indigoRenderToFile (int item, string filename); int indigoRenderGrid (int items, int[] refAtoms, int nColumns, int output); int indigoRenderGridToFile (int items, int[] refAtoms, int nColumns, string filename); } } Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/Properties/000077500000000000000000000000001271037650300236725ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/Properties/AssemblyInfo.cs000066400000000000000000000026561271037650300266250ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Indigo Renderer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010-2011")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("cc423006-f787-4422-8a4a-dbc058c4621c")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.*")] [assembly: AssemblyFileVersion("1.0.0.0")] Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/Properties/ResourcesLinux.resx000066400000000000000000000143461271037650300275770ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x64\libindigo-renderer.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Linux\x86\libindigo-renderer.so;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/Properties/ResourcesMac.resx000066400000000000000000000143731271037650300272000ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Mac\10.7\libindigo-renderer.dylib;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/Properties/ResourcesWin.resx000066400000000000000000000143311271037650300272270ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x64\indigo-renderer.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resource\Win\x86\indigo-renderer.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/app.config000066400000000000000000000001051271037650300235010ustar00rootroot00000000000000 Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/indigo-renderer-dotnet.csproj000066400000000000000000000136431271037650300273370ustar00rootroot00000000000000 Debug AnyCPU 9.0.30729 2.0 {D7D9B92E-80BE-449C-8D98-C2ABA620B59C} Library Properties com.epam.indigo indigo-renderer-dotnet v2.0 512 true indigo-renderer.snk 3.5 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true true full false bin\Debug\ DEBUG;TRACE prompt 4 true AllRules.ruleset pdbonly true bin\Release\ TRACE prompt 4 true AllRules.ruleset ..\..\..\dotnet\bin\Release\indigo-dotnet.dll False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 true False Windows Installer 3.1 true $(Copy) $(LibraryPath)\Win\x64\indigo-renderer.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\indigo-renderer.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo-renderer.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo-renderer.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo-renderer.dylib $(ProjectDir)Resource\Mac\10.7 $(Copy) $(LibraryPath)\Win\x64\indigo-renderer.dll $(ProjectDir)Resource\Win\x64 $(Copy) $(LibraryPath)\Win\x86\indigo-renderer.dll $(ProjectDir)Resource\Win\x86 $(Copy) $(LibraryPath)\Linux\x64\libindigo-renderer.so $(ProjectDir)Resource\Linux\x64 $(Copy) $(LibraryPath)\Linux\x86\libindigo-renderer.so $(ProjectDir)Resource\Linux\x86 $(Copy) $(LibraryPath)\Mac\10.7\libindigo-renderer.dylib $(ProjectDir)Resource\Mac\10.7 Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/indigo-renderer-dotnet.sln000066400000000000000000000016211271037650300266240ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "indigo-renderer-dotnet", "indigo-renderer-dotnet.csproj", "{D7D9B92E-80BE-449C-8D98-C2ABA620B59C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D7D9B92E-80BE-449C-8D98-C2ABA620B59C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D9B92E-80BE-449C-8D98-C2ABA620B59C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7D9B92E-80BE-449C-8D98-C2ABA620B59C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7D9B92E-80BE-449C-8D98-C2ABA620B59C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Indigo-indigo-1.2.3/api/plugins/renderer/dotnet/indigo-renderer.snk000066400000000000000000000011241271037650300253260ustar00rootroot00000000000000$RSA2‰#èÞlÍ ªUÁìùdâÍ¢(šW8HÃj8ë;•¡NR}B‰ÐÈ í[XƒhèÀ߈…É8á¼Îú–Vl·uzt#¬Ó i¤%)¨º>«ƒöÔXs—5\õÀúàÑÁò6"Ž©ê\äûÄéñùârZ-7²û¼ýÄ[È%LBÊj+ë%tËp•Nëµ¶õ(Iñ¢X†Ÿf  Ü3•ß2sÙ_lÔïV‚èzˆÎºP$óu’r¾Þ:Æ4F©ŒÜöÙ%ý=qœDƒv·…JVÿ”eX5…F2ÚÈ@ÒEìj"èóop3èÎ¥¢ssßÝq~f¥Ñ02û€5VRD sš%7й ðÛx0¼ëm“h(6ÇsŽÁQ w:Ç—µÚÅHV]ÎEîîÆ&_B+¥ÌÏ@ÞIúf”îD¬N¬2Ž8Œ cåíŠ6£ÆÙÛUV!ÉÉ uu`$­z6‹B… y2–+ImcC,Ø&,¡ëJùšj›R(7Š˜+ž£q^+¾Û«!Indigo-indigo-1.2.3/api/plugins/renderer/indigo-renderer.h000066400000000000000000000034771271037650300235020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_render__ #define __indigo_render__ #include "indigo.h" /* Rendering */ // Returns an 'output' object for the given HDC CEXPORT int indigoRenderWriteHDC (void* hdc, int printingHdc); // output is either a file output obtained via indigoWriteFile(), or // a buffer obtained via indigoWriteBuffer(), or // an HDC obtained via indigoRenderWriteHDC CEXPORT int indigoRender (int object, int output); // objects is an array of molecules created with indigoCreateArray) // refAtoms is an array of integers, whose size must be equal to the number // of molecules if the array // nColumns is the number of columns in the grid // output -- see the comment for indigoRender CEXPORT int indigoRenderGrid (int objects, int* refAtoms, int nColumns, int output); // Works like indigoRender(), but renders directly to file CEXPORT int indigoRenderToFile (int object, const char *filename); // Works like indigoRenderGrid(), but renders directly to file CEXPORT int indigoRenderGridToFile (int objects, int* refAtoms, int nColumns, const char *filename); // Resets all the rendering settings CEXPORT int indigoRenderReset (); #endif Indigo-indigo-1.2.3/api/plugins/renderer/indigo_renderer.explist000066400000000000000000000001651271037650300250140ustar00rootroot00000000000000_indigoRenderWriteHDC _indigoRender _indigoRenderGrid _indigoRenderToFile _indigoRenderGridToFile _indigoRenderReset Indigo-indigo-1.2.3/api/plugins/renderer/indigo_renderer.so.map000066400000000000000000000003021271037650300245120ustar00rootroot00000000000000libindigo_renderer_shared.so { global: indigoRenderWriteHDC; indigoRender; indigoRenderGrid; indigoRenderToFile; indigoRenderGridToFile; indigoRenderReset; local: *; }; Indigo-indigo-1.2.3/api/plugins/renderer/java/000077500000000000000000000000001271037650300211625ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/pom.xml000066400000000000000000000142601271037650300225020ustar00rootroot00000000000000 4.0.0 com.epam.indigo indigo-renderer 1.2.2beta.r117 jar Indigo Renderer Universal organic chemistry toolkit renderer plugin http://lifescience.opensource.epam.com/indigo/api GNU General Public License v3 http://www.gnu.org/licenses/gpl.html repo engineer Mikhail Kviatkovskii mikhail_kviatkovskii@epam.com UTC+3 scm:git:git@github.com:epam/Indigo.git scm:git:git@github.com:epam/Indigo.git git@github.com:epam/Indigo.git net.java.dev.jna jna 3.5.0 com.epam.indigo indigo ${project.version} org.apache.maven.plugins maven-compiler-plugin 3.1 1.5 1.5 org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources package jar org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs package jar org.apache.maven.plugins maven-jar-plugin 2.3.1 false org.sonatype.plugins nexus-staging-maven-plugin 1.6.3 true deploy ossrh https://oss.sonatype.org/ false ../../../libs/shared **/*.lib **/*renderer.* ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.4 ${gpg.passhprase} --no-tty sign-artifacts verify sign Indigo-indigo-1.2.3/api/plugins/renderer/java/src/000077500000000000000000000000001271037650300217515ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/000077500000000000000000000000001271037650300226755ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/000077500000000000000000000000001271037650300236165ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/com/000077500000000000000000000000001271037650300243745ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/com/epam/000077500000000000000000000000001271037650300253165ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/com/epam/indigo/000077500000000000000000000000001271037650300265675ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/com/epam/indigo/IndigoRenderer.java000066400000000000000000000076231271037650300323420ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Native; import java.io.File; import java.io.IOException; public class IndigoRenderer { public IndigoRenderer (Indigo indigo) { loadLibrary(indigo.getUserSpecifiedPath()); _indigo = indigo; } public void render (IndigoObject obj, IndigoObject output) { _indigo.setSessionID(); Object[] guard = new Object[]{this, obj, output}; Indigo.checkResult(guard, _lib.indigoRender(obj.self, output.self)); } public void renderToFile (IndigoObject obj, String filename) { _indigo.setSessionID(); Indigo.checkResult(this, obj, _lib.indigoRenderToFile(obj.self, filename)); } public byte[] renderToBuffer (IndigoObject obj) { _indigo.setSessionID(); IndigoObject buf = _indigo.writeBuffer(); try { Indigo.checkResult(this, obj, _lib.indigoRender(obj.self, buf.self)); return buf.toBuffer(); } finally { buf.dispose(); } } public void renderGridToFile (IndigoObject objects, int[] refAtoms, int ncolumns, String filename) { _indigo.setSessionID(); if (refAtoms != null && objects.count() != refAtoms.length) throw new IndigoException(this, "refAtoms size does not match the number of objects"); Indigo.checkResult(this, objects, _lib.indigoRenderGridToFile(objects.self, refAtoms, ncolumns, filename)); } public byte[] renderGridToBuffer (IndigoObject objects, int[] refAtoms, int ncolumns) { _indigo.setSessionID(); if (refAtoms != null && objects.count() != refAtoms.length) throw new IndigoException(this, "refAtoms size does not match the number of objects"); IndigoObject buf = _indigo.writeBuffer(); try { Indigo.checkResult(this, objects, _lib.indigoRenderGrid(objects.self, refAtoms, ncolumns, buf.self)); return buf.toBuffer(); } finally { buf.dispose(); } } public void renderResetSettings(){ _indigo.setSessionID(); _lib.indigoRenderReset(); } private static String getPathToBinary (String path, String filename) { String dllpath = Indigo.getPlatformDependentPath(); if (path == null) { String res = Indigo.extractFromJar(IndigoRenderer.class, "/" + dllpath, filename); if (res != null) return res; path = "lib"; } path = path + File.separator + dllpath + File.separator + filename; try { return (new File(path)).getCanonicalPath(); } catch (IOException e) { return path; } } private synchronized static void loadLibrary (String path) { if (_lib != null) return; int os = Indigo.getOs(); if (os == Indigo.OS_LINUX || os == Indigo.OS_SOLARIS) _lib = (IndigoRendererLib)Native.loadLibrary(getPathToBinary(path, "libindigo-renderer.so"), IndigoRendererLib.class); else if (os == Indigo.OS_MACOS) _lib = (IndigoRendererLib)Native.loadLibrary(getPathToBinary(path, "libindigo-renderer.dylib"), IndigoRendererLib.class); else // os == OS_WINDOWS _lib = (IndigoRendererLib)Native.loadLibrary(getPathToBinary(path, "indigo-renderer.dll"), IndigoRendererLib.class); } Indigo _indigo; static IndigoRendererLib _lib; } Indigo-indigo-1.2.3/api/plugins/renderer/java/src/main/java/com/epam/indigo/IndigoRendererLib.java000066400000000000000000000022131271037650300327570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ package com.epam.indigo; import com.sun.jna.Library; import com.sun.jna.Pointer; public interface IndigoRendererLib extends Library { int indigoRenderWriteHDC (Pointer hdc, int printingHdc); int indigoRender (int object, int output); int indigoRenderGrid (int objects, int[] refAtoms, int nColumns, int output); int indigoRenderToFile (int object, String filename); int indigoRenderGridToFile (int objects, int[] refAtoms, int nColumns, String filename); int indigoRenderReset(); } Indigo-indigo-1.2.3/api/plugins/renderer/python/000077500000000000000000000000001271037650300215625ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/python/indigo_renderer.py000066400000000000000000000065261271037650300253040ustar00rootroot00000000000000# # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. from indigo import * class IndigoRenderer(object): def __init__(self, indigo): self.indigo = indigo if os.name == 'posix' and not platform.mac_ver()[0] and not platform.system().startswith("CYGWIN"): self._lib = CDLL(indigo.dllpath + "/libindigo-renderer.so") elif os.name == 'nt' or platform.system().startswith("CYGWIN"): self._lib = CDLL(indigo.dllpath + "\indigo-renderer.dll") elif platform.mac_ver()[0]: self._lib = CDLL(indigo.dllpath + "/libindigo-renderer.dylib") else: raise IndigoException("unsupported OS: " + os.name) self._lib.indigoRender.restype = c_int self._lib.indigoRender.argtypes = [c_int, c_int] self._lib.indigoRenderToFile.restype = c_int self._lib.indigoRenderToFile.argtypes = [c_int, c_char_p] self._lib.indigoRenderGrid.restype = c_int self._lib.indigoRenderGrid.argtypes = [c_int, POINTER(c_int), c_int, c_int] self._lib.indigoRenderGridToFile.restype = c_int self._lib.indigoRenderGridToFile.argtypes = [c_int, POINTER(c_int), c_int, c_char_p] self._lib.indigoRenderReset.restype = c_int self._lib.indigoRenderReset.argtypes = [c_int] def renderToBuffer(self, obj): self.indigo._setSessionId() wb = self.indigo.writeBuffer() try: self.indigo._checkResult(self._lib.indigoRender(obj.id, wb.id)) return wb.toBuffer() finally: wb.dispose() def renderToFile(self, obj, filename): self.indigo._setSessionId() self.indigo._checkResult(self._lib.indigoRenderToFile(obj.id, filename.encode('ascii'))) def renderGridToFile(self, objects, refatoms, ncolumns, filename): self.indigo._setSessionId() arr = None if refatoms: if len(refatoms) != objects.count(): raise IndigoException("renderGridToFile(): refatoms[] size must be equal to the number of objects") arr = (c_int * len(refatoms))() for i in range(len(refatoms)): arr[i] = refatoms[i] self.indigo._checkResult( self._lib.indigoRenderGridToFile(objects.id, arr, ncolumns, filename.encode('ascii'))) def renderGridToBuffer(self, objects, refatoms, ncolumns): self.indigo._setSessionId() arr = None if refatoms: if len(refatoms) != objects.count(): raise IndigoException("renderGridToBuffer(): refatoms[] size must be equal to the number of objects") arr = (c_int * len(refatoms))() for i in range(len(refatoms)): arr[i] = refatoms[i] wb = self.indigo.writeBuffer() try: self.indigo._checkResult( self._lib.indigoRenderGrid(objects.id, arr, ncolumns, wb.id)) return wb.toBuffer() finally: wb.dispose() Indigo-indigo-1.2.3/api/plugins/renderer/src/000077500000000000000000000000001271037650300210305ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/src/indigo_render2d.cpp000066400000000000000000000652551271037650300246070ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "indigo_io.h" #include "indigo_array.h" #include "indigo_renderer_internal.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "option_manager.h" #include "indigo-renderer.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "render2d/render_cdxml.h" #include "base_cpp/properties_map.h" using namespace indigo; _SessionLocalContainer indigo_renderer_self; IndigoRenderer &indigoRendererGetInstance () { IndigoRenderer &inst = indigo_renderer_self.getLocalCopy(); inst.validate(); return inst; } void IndigoRenderer::init () { renderParams.clear(); } #define CHECKRGB(r, g, b) \ if (__min3(r, g, b) < 0 || __max3(r, g, b) > 1.0 + 1e-6) \ throw IndigoError("Some of the color components are out of range [0..1]") typedef RedBlackStringMap StringIntMap; IndigoRenderer::IndigoRenderer () { } IndigoRenderer::~IndigoRenderer () { } void indigoRenderSetCommentOffset (int offset) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.commentOffset = offset; } void indigoRenderSetImageWidth (int v) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.width = v; } void indigoRenderSetImageHeight (int v) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.height = v; } void indigoRenderSetImageMaxWidth (int v) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.maxWidth = v; } void indigoRenderSetImageMaxHeight (int v) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.maxHeight = v; } void indigoRenderSetTitleOffset (int offset) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.titleOffset = offset; } DINGO_MODE indigoRenderMapOutputFormat (const char *format) { TL_DECL_GET(StringIntMap, outFmtMap); if (outFmtMap.size() == 0) { outFmtMap.insert("pdf", MODE_PDF); outFmtMap.insert("png", MODE_PNG); outFmtMap.insert("svg", MODE_SVG); outFmtMap.insert("emf", MODE_EMF); outFmtMap.insert("cdxml", MODE_CDXML); } return outFmtMap.find(format) ? (DINGO_MODE)outFmtMap.at(format) : MODE_NONE; } void indigoRenderSetOutputFormat (const char *format) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.mode = indigoRenderMapOutputFormat(format); } void indigoRenderSetImageSize (int width, int height) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.width = width; rp.cnvOpt.height = height; } void indigoRenderSetHDCOffset (int x, int y) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.xOffset = x; rp.cnvOpt.yOffset = y; } void indigoRenderSetMargins (int x, int y) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.marginX = x; rp.cnvOpt.marginY = y; } void indigoRenderSetGridMargins (int x, int y) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.gridMarginX = x; rp.cnvOpt.gridMarginY = y; } void indigoRenderSetBondLength (float length) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.bondLength = length; } void indigoRenderSetRelativeThickness (float rt) { RenderParams& rp = indigoRendererGetInstance().renderParams; if (rt <= 0.0f) throw IndigoError("relative thickness must be positive"); rp.relativeThickness = rt; } void indigoRenderSetBondLineWidth (float w) { RenderParams& rp = indigoRendererGetInstance().renderParams; if (w <= 0.0f) throw IndigoError("bond line width factor must be positive"); rp.bondLineWidthFactor = w; } void indigoRenderSetBackgroundColor (float r, float g, float b) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.backgroundColor.set((float)r, (float)g, (float)b); } void indigoRenderSetBaseColor (float r, float g, float b) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.baseColor.set((float)r, (float)g, (float)b); } void indigoRenderSetImplicitHydrogenVisible (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.implHVisible = enabled != 0; } void indigoRenderSetHighlightedLabelsVisible (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.highlightedLabelsVisible = enabled != 0; } void indigoRenderSetBoldBondDetection (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.boldBondDetection = enabled != 0; } void indigoRenderSetColoring (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.atomColoring = enabled != 0; } void indigoRenderSetValencesVisible (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.showValences = enabled != 0; } void indigoRenderSetAtomIdsVisible (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.showAtomIds = enabled != 0; } void indigoRenderSetBondIdsVisible (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.showBondIds = enabled != 0; } void indigoRenderSetAtomBondIdsFromOne (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.atomBondIdsFromOne = enabled != 0; } void indigoRenderSetHighlightThicknessEnabled (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.highlightThicknessEnable = enabled != 0; } void indigoRenderSetHighlightColorEnabled (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.highlightColorEnable = enabled != 0; } void indigoRenderSetHighlightColor (float r, float g, float b) { CHECKRGB(r, g, b); RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.highlightColor.set(r, g, b); } void indigoRenderSetStereoStyle (const char* mode) { TL_DECL_GET(StringIntMap, stereoStyleMap); if (stereoStyleMap.size() == 0) { stereoStyleMap.insert("ext", STEREO_STYLE_EXT); stereoStyleMap.insert("old", STEREO_STYLE_OLD); stereoStyleMap.insert("none", STEREO_STYLE_NONE); } RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.stereoMode = (STEREO_STYLE)stereoStyleMap.at(mode); } void indigoRenderSetLabelMode (const char* mode) { TL_DECL_GET(StringIntMap, labelMap); if (labelMap.size() == 0) { labelMap.insert("none", LABEL_MODE_NONE); labelMap.insert("hetero", LABEL_MODE_HETERO); labelMap.insert("terminal-hetero", LABEL_MODE_TERMINAL_HETERO); labelMap.insert("all", LABEL_MODE_ALL); } RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.labelMode = (LABEL_MODE)labelMap.at(mode); } void indigoRenderSetCatalystsPlacement (const char* mode) { TL_DECL_GET(StringIntMap, agentPlacementMap); if (agentPlacementMap.size() == 0) { agentPlacementMap.insert("above", 0); agentPlacementMap.insert("above-and-below", 1); } RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.agentsBelowArrow = agentPlacementMap.at(mode) != 0; } void indigoRenderSetSuperatomMode (const char* mode) { TL_DECL_GET(StringIntMap, stereoAtomMode); if (stereoAtomMode.size() == 0) { stereoAtomMode.insert("expand", 0); stereoAtomMode.insert("collapse", 1); } RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.collapseSuperatoms = stereoAtomMode.at(mode) != 0; } void indigoRenderSetAAMColor (float r, float g, float b) { CHECKRGB(r, g, b); RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.aamColor.set(r, g, b); } void indigoRenderSetCommentColor (float r, float g, float b) { CHECKRGB(r, g, b); RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.commentColor.set(r, g, b); } void indigoRenderSetDataSGroupColor (float r, float g, float b) { CHECKRGB(r, g, b); RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.dataGroupColor.set(r, g, b); } void indigoRenderSetCenterDoubleBondWhenStereoAdjacent (int enabled) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.centerDoubleBondWhenStereoAdjacent = enabled != 0; } void indigoRenderSetComment (const char* comment) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.comment.clear(); rp.cnvOpt.comment.appendString(comment, true); } void indigoRenderSetAtomColorProperty (const char* prop) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.atomColorProp.clear(); rp.rOpt.atomColorProp.appendString(prop, true); } void indigoRenderSetCommentFontSize (float fontSize) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.commentFontFactor = fontSize; } void indigoRenderSetTitleFontSize (float fontSize) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.titleFontFactor = fontSize; } static MultilineTextLayout _parseTextLayout (const char *text) { // Try to read as float for compatibility with previous versions BufferScanner scanner(text); float val; if (scanner.tryReadFloat(val)) { float eps = 1e-6f; if (fabs(val) < eps) text = "left"; else if (fabs(val - 0.5f) < eps) text = "center"; else if (fabs(val - 1.0f) < eps) text = "right"; else throw IndigoError("Alignment allow only 0.0, 0.5, or 1.0 values"); } if (strcasecmp(text, "left") == 0) return MultilineTextLayout(MultilineTextLayout::Left, MultilineTextLayout::Left); else if (strcasecmp(text, "right") == 0) return MultilineTextLayout(MultilineTextLayout::Right, MultilineTextLayout::Right); else if (strcasecmp(text, "center") == 0) return MultilineTextLayout(MultilineTextLayout::Center, MultilineTextLayout::Center); else if (strcasecmp(text, "center-left") == 0) return MultilineTextLayout(MultilineTextLayout::Center, MultilineTextLayout::Left); else if (strcasecmp(text, "center-right") == 0) return MultilineTextLayout(MultilineTextLayout::Center, MultilineTextLayout::Right); else throw IndigoError("Option value is invalid"); } void indigoRenderSetCommentSpacing (float spacing) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.commentSpacing = spacing; } void indigoRenderSetCommentAlignment (const char *text) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.commentAlign = _parseTextLayout(text); } void indigoRenderSetTitleSpacing (float spacing) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.rOpt.titleSpacing = spacing; } void indigoRenderSetTitleAlignment (const char *text) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.titleAlign = _parseTextLayout(text); } void indigoRenderSetCommentPosition (const char* pos) { TL_DECL_GET(StringIntMap, map); if (map.size() == 0) { map.insert("top", COMMENT_POS_TOP); map.insert("bottom", COMMENT_POS_BOTTOM); } RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.commentPos = (COMMENT_POS)map.at(pos); } void indigoRenderSetGridTitleProperty (const char* prop) { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.cnvOpt.titleProp.clear(); rp.cnvOpt.titleProp.appendString(prop, true); } RenderCdxmlContext& getCdxmlContext() { RenderParams& rp = indigoRendererGetInstance().renderParams; if (rp.rOpt.cdxml_context.get() == NULL) { rp.rOpt.cdxml_context.create(); } return rp.rOpt.cdxml_context.ref(); } void indigoRenderSetCdxmlTitleFont(const char* font) { RenderCdxmlContext& context = getCdxmlContext(); context.titleFont.readString(font, true); } void indigoRenderSetCdxmlTitleFace(const char* face) { RenderCdxmlContext& context = getCdxmlContext(); context.titleFace.readString(face, true); } void indigoRenderSetCdxmlPropertiesSize(float size) { RenderCdxmlContext& context = getCdxmlContext(); context.propertyFontSize = size; } void indigoRenderSetCdxmlPropertiesFontTable(const char* fonttable) { RenderCdxmlContext& context = getCdxmlContext(); context.fonttable.readString(fonttable, true); } void indigoRenderSetCdxmlPropertiesColorTable(const char* colortable) { RenderCdxmlContext& context = getCdxmlContext(); context.colortable.readString(colortable, true); } void indigoRenderSetCdxmlPropertiesNameProperty(const char* name) { RenderCdxmlContext& context = getCdxmlContext(); context.propertyNameCaption.readString(name, true); } void indigoRenderSetCdxmlPropertiesValueProperty(const char* value) { RenderCdxmlContext& context = getCdxmlContext(); context.propertyValueCaption.readString(value, true); } void indigoRenderSetCdxmlPropertiesKeyAlignment(const char* value) { RenderCdxmlContext& context = getCdxmlContext(); if (strcasecmp(value, "left") == 0) context.keyAlignment = RenderCdxmlContext::ALIGNMENT_LEFT; else if (strcasecmp(value, "right") == 0) context.keyAlignment = RenderCdxmlContext::ALIGNMENT_RIGHT; else throw IndigoError("Option value alignment is invalid"); } void indigoRenderSetCdxmlPropertiesEnabled(int enabled) { RenderCdxmlContext& context = getCdxmlContext(); context.enabled = (enabled != 0); } CEXPORT int indigoRender (int object, int output) { INDIGO_BEGIN { RenderParams& rp = indigoRendererGetInstance().renderParams; // If there are molecules/reactions in the arrays then current call will // rendere a grid -> needs to clear it rp.clearArrays(); IndigoObject &obj = self.getObject(object); if (IndigoBaseMolecule::is(obj)) { if (obj.getBaseMolecule().isQueryMolecule()) rp.mol.reset(new QueryMolecule()); else rp.mol.reset(new Molecule()); rp.mol->clone_KeepIndices(self.getObject(object).getBaseMolecule()); rp.rmode = RENDER_MOL; } else if (IndigoBaseReaction::is(obj)) { if (obj.getBaseReaction().isQueryReaction()) rp.rxn.reset(new QueryReaction()); else rp.rxn.reset(new Reaction()); rp.rxn->clone(self.getObject(object).getBaseReaction(), 0, 0, 0); rp.rmode = RENDER_RXN; } else { throw IndigoError("The object provided should be a molecule, a reaction or an array of such"); } IndigoObject& out = self.getObject(output); if (out.type == IndigoHDCOutput::HDC_OUTPUT) { IndigoHDCOutput& hdcOut = (IndigoHDCOutput&)self.getObject(output); rp.rOpt.hdc = hdcOut.dc; rp.rOpt.mode = hdcOut.prn ? MODE_PRN : MODE_HDC; } else if (out.type == IndigoObject::OUTPUT) { rp.rOpt.output = &IndigoOutput::get(out); } else { throw IndigoError("Invalid output object type"); } RenderParamInterface::render(rp); return 1; } INDIGO_END(-1) } CEXPORT int indigoRenderGrid (int objects, int* refAtoms, int nColumns, int output) { INDIGO_BEGIN { RenderParams& rp = indigoRendererGetInstance().renderParams; rp.clearArrays(); PtrArray& objs = IndigoArray::cast(self.getObject(objects)).objects; if (rp.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = rp.rOpt.cdxml_context.ref(); context.property_data.clear(); } if (IndigoBaseMolecule::is(*objs[0])) { for (int i = 0; i < objs.size(); ++i) { if (objs[i]->getBaseMolecule().isQueryMolecule()) rp.mols.add(new QueryMolecule()); else rp.mols.add(new Molecule()); Array& title = rp.titles.push(); if (objs[i]->getProperties().contains(rp.cnvOpt.titleProp.ptr())) title.copy(objs[i]->getProperties().valueBuf(rp.cnvOpt.titleProp.ptr())); if (rp.rOpt.mode == DINGO_MODE::MODE_CDXML) { if (rp.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = rp.rOpt.cdxml_context.ref(); RenderCdxmlContext::PropertyData& data = context.property_data.push(); auto& properties = objs[i]->getProperties(); if (context.propertyNameCaption.size() > 0 && context.propertyValueCaption.size() > 0) if (properties.contains(context.propertyNameCaption.ptr())) { if (properties.contains(context.propertyValueCaption.ptr())) { data.propertyName.readString(properties.at(context.propertyNameCaption.ptr()), true); data.propertyValue.readString(properties.at(context.propertyValueCaption.ptr()), true); } } } } rp.mols.top()->clone_KeepIndices(objs[i]->getBaseMolecule()); rp.rmode = RENDER_MOL; } } else if (IndigoBaseReaction::is(*objs[0])) { for (int i = 0; i < objs.size(); ++i) { if (objs[i]->getBaseReaction().isQueryReaction()) rp.rxns.add(new QueryReaction()); else rp.rxns.add(new Reaction()); Array& title = rp.titles.push(); if (objs[i]->getProperties().contains(rp.cnvOpt.titleProp.ptr())) title.copy(objs[i]->getProperties().valueBuf(rp.cnvOpt.titleProp.ptr())); rp.rxns.top()->clone(objs[i]->getBaseReaction(), 0, 0, 0); rp.rmode = RENDER_RXN; } } else { throw IndigoError("The array elements should be molecules or reactions"); } if (refAtoms != NULL) { rp.refAtoms.copy(refAtoms, objs.size()); } rp.cnvOpt.gridColumnNumber = nColumns; bool hasNonemptyTitles = false; for (int i = 0; i < rp.titles.size(); ++i) { if (rp.titles[i].size() > 0) { hasNonemptyTitles = true; break; } } if (!hasNonemptyTitles) rp.titles.clear(); IndigoObject& out = self.getObject(output); if (out.type == IndigoHDCOutput::HDC_OUTPUT) { IndigoHDCOutput& hdcOut = (IndigoHDCOutput&)self.getObject(output); rp.rOpt.hdc = hdcOut.dc; rp.rOpt.mode = hdcOut.prn ? MODE_PRN : MODE_HDC; } else if (out.type == IndigoObject::OUTPUT) { rp.rOpt.output = &IndigoOutput::get(out); } else { throw IndigoError("Invalid output object type"); } RenderParamInterface::render(rp); // Release memory for arrays with molecules/reactions rp.clearArrays(); return 1; } INDIGO_END(-1) } DINGO_MODE indigoRenderGuessOutputFormat(const char* filename) { const char *ext = strrchr(filename, '.'); if (ext == NULL) return MODE_NONE; return indigoRenderMapOutputFormat(ext + 1); } CEXPORT int indigoRenderToFile (int object, const char *filename) { int f = indigoWriteFile(filename); if (f == -1) return -1; RenderParams& rp = indigoRendererGetInstance().renderParams; DINGO_MODE setMode = rp.rOpt.mode; rp.rOpt.mode = (setMode == MODE_NONE) ? indigoRenderGuessOutputFormat(filename) : setMode; int res = indigoRender(object, f); rp.rOpt.mode = setMode; indigoFree(f); return res; } CEXPORT int indigoRenderGridToFile (int objects, int* refAtoms, int nColumns, const char *filename) { int f = indigoWriteFile(filename); if (f == -1) return -1; RenderParams& rp = indigoRendererGetInstance().renderParams; DINGO_MODE setMode = rp.rOpt.mode; rp.rOpt.mode = (setMode == MODE_NONE) ? indigoRenderGuessOutputFormat(filename) : setMode; int res = indigoRenderGrid(objects, refAtoms, nColumns, f); rp.rOpt.mode = setMode; indigoFree(f); return res; } CEXPORT int indigoRenderReset () { INDIGO_BEGIN { IndigoRenderer& rp = indigoRendererGetInstance(); rp.init(); return 1; } INDIGO_END(-1) } CEXPORT int indigoRenderWriteHDC (void* hdc, int printingHdc) { INDIGO_BEGIN { return self.addObject(new IndigoHDCOutput(hdc, printingHdc != 0)); } INDIGO_END(-1) } class _IndigoRenderingOptionsHandlersSetter { public: _IndigoRenderingOptionsHandlersSetter (); }; _IndigoRenderingOptionsHandlersSetter::_IndigoRenderingOptionsHandlersSetter () { OptionManager &mgr = indigoGetOptionManager(); OsLocker locker(mgr.lock); mgr.setOptionHandlerInt("render-comment-offset", indigoRenderSetCommentOffset); mgr.setOptionHandlerInt("render-image-width", indigoRenderSetImageWidth); mgr.setOptionHandlerInt("render-image-height", indigoRenderSetImageHeight); mgr.setOptionHandlerInt("render-image-max-width", indigoRenderSetImageMaxWidth); mgr.setOptionHandlerInt("render-image-max-height", indigoRenderSetImageMaxHeight); mgr.setOptionHandlerString("render-output-format", indigoRenderSetOutputFormat); mgr.setOptionHandlerString("render-label-mode", indigoRenderSetLabelMode); mgr.setOptionHandlerString("render-comment", indigoRenderSetComment); mgr.setOptionHandlerString("render-comment-position", indigoRenderSetCommentPosition); mgr.setOptionHandlerString("render-stereo-style", indigoRenderSetStereoStyle); mgr.setOptionHandlerString("render-catalysts-placement", indigoRenderSetCatalystsPlacement); mgr.setOptionHandlerString("render-superatom-mode", indigoRenderSetSuperatomMode); mgr.setOptionHandlerString("render-atom-color-property", indigoRenderSetAtomColorProperty); mgr.setOptionHandlerBool("render-coloring", indigoRenderSetColoring); mgr.setOptionHandlerBool("render-valences-visible", indigoRenderSetValencesVisible); mgr.setOptionHandlerBool("render-atom-ids-visible", indigoRenderSetAtomIdsVisible); mgr.setOptionHandlerBool("render-bond-ids-visible", indigoRenderSetBondIdsVisible); mgr.setOptionHandlerBool("render-atom-bond-ids-from-one", indigoRenderSetAtomBondIdsFromOne); mgr.setOptionHandlerBool("render-highlight-thickness-enabled", indigoRenderSetHighlightThicknessEnabled); mgr.setOptionHandlerBool("render-highlight-color-enabled", indigoRenderSetHighlightColorEnabled); mgr.setOptionHandlerBool("render-center-double-bond-when-stereo-adjacent", indigoRenderSetCenterDoubleBondWhenStereoAdjacent); mgr.setOptionHandlerBool("render-implicit-hydrogens-visible", indigoRenderSetImplicitHydrogenVisible); mgr.setOptionHandlerBool("render-highlighted-labels-visible", indigoRenderSetHighlightedLabelsVisible); mgr.setOptionHandlerBool("render-bold-bond-detection", indigoRenderSetBoldBondDetection); mgr.setOptionHandlerFloat("render-bond-length", indigoRenderSetBondLength); mgr.setOptionHandlerFloat("render-relative-thickness", indigoRenderSetRelativeThickness); mgr.setOptionHandlerFloat("render-bond-line-width", indigoRenderSetBondLineWidth); mgr.setOptionHandlerFloat("render-comment-font-size", indigoRenderSetCommentFontSize); mgr.setOptionHandlerString("render-comment-alignment", indigoRenderSetCommentAlignment); mgr.setOptionHandlerFloat("render-comment-spacing", indigoRenderSetCommentSpacing); mgr.setOptionHandlerColor("render-background-color", indigoRenderSetBackgroundColor); mgr.setOptionHandlerColor("render-base-color", indigoRenderSetBaseColor); mgr.setOptionHandlerColor("render-highlight-color", indigoRenderSetHighlightColor); mgr.setOptionHandlerColor("render-aam-color", indigoRenderSetAAMColor); mgr.setOptionHandlerColor("render-comment-color", indigoRenderSetCommentColor); mgr.setOptionHandlerColor("render-data-sgroup-color", indigoRenderSetDataSGroupColor); mgr.setOptionHandlerXY("render-image-size", indigoRenderSetImageSize); mgr.setOptionHandlerXY("render-hdc-offset", indigoRenderSetHDCOffset); mgr.setOptionHandlerXY("render-margins", indigoRenderSetMargins); mgr.setOptionHandlerXY("render-grid-margins", indigoRenderSetGridMargins); mgr.setOptionHandlerFloat("render-grid-title-spacing", indigoRenderSetTitleSpacing); mgr.setOptionHandlerString("render-grid-title-alignment", indigoRenderSetTitleAlignment); mgr.setOptionHandlerFloat("render-grid-title-font-size", indigoRenderSetTitleFontSize); mgr.setOptionHandlerString("render-grid-title-property", indigoRenderSetGridTitleProperty); mgr.setOptionHandlerInt("render-grid-title-offset", indigoRenderSetTitleOffset); mgr.setOptionHandlerBool("render-cdxml-properties-enabled", indigoRenderSetCdxmlPropertiesEnabled); mgr.setOptionHandlerString("render-cdxml-properties-fonttable", indigoRenderSetCdxmlPropertiesFontTable); mgr.setOptionHandlerString("render-cdxml-properties-colortable", indigoRenderSetCdxmlPropertiesColorTable); mgr.setOptionHandlerString("render-cdxml-properties-name-property", indigoRenderSetCdxmlPropertiesNameProperty); mgr.setOptionHandlerString("render-cdxml-properties-value-property", indigoRenderSetCdxmlPropertiesValueProperty); mgr.setOptionHandlerString("render-cdxml-properties-key-alignment", indigoRenderSetCdxmlPropertiesKeyAlignment); mgr.setOptionHandlerFloat("render-cdxml-properties-size", indigoRenderSetCdxmlPropertiesSize); mgr.setOptionHandlerString("render-cdxml-title-font", indigoRenderSetCdxmlTitleFont); mgr.setOptionHandlerString("render-cdxml-title-face", indigoRenderSetCdxmlTitleFace); } _IndigoRenderingOptionsHandlersSetter _indigo_rendering_options_handlers_setter; Indigo-indigo-1.2.3/api/plugins/renderer/src/indigo_renderer_internal.h000066400000000000000000000024631271037650300262410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_renderer_internal__ #define __indigo_renderer_internal__ #include "render2d/render_params.h" #include "base_cpp/tlscont.h" #include "indigo_internal.h" class IndigoRenderer : public IndigoPluginContext { public: IndigoRenderer (); ~IndigoRenderer (); RenderParams renderParams; virtual void init (); }; class IndigoHDCOutput : public IndigoObject { public: enum { HDC_OUTPUT = 110 }; IndigoHDCOutput (void* hdc, bool printing) : IndigoObject(HDC_OUTPUT), dc(hdc), prn(printing) {} void* dc; bool prn; virtual ~IndigoHDCOutput () {} protected: RenderParams params; }; //TL_DECL_EXT(IndigoRenderer, indigo_renderer_self); #endif Indigo-indigo-1.2.3/api/plugins/renderer/tests/000077500000000000000000000000001271037650300214035ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/tests/c/000077500000000000000000000000001271037650300216255ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/plugins/renderer/tests/c/indigo-renderer-test.c000066400000000000000000000033061271037650300260250ustar00rootroot00000000000000#include #include #include #include "indigo.h" #include "indigo-renderer.h" void onError (const char *message, void *context) { fprintf(stderr, "Error: %s\n", message); exit(-1); } #ifdef WIN32 #include #endif void testHDC () { int buffer_object; char *raw_ptr; int size; #ifdef WIN32 HDC hdc, hdc2; #endif int hdc_buffer_object; int molecule; molecule = indigoLoadMoleculeFromString("C1=CC=CC=C1"); indigoSetOption("render-output-format", "png"); indigoSetOption("render-background-color", "255, 255, 255"); indigoRenderToFile(molecule, "indigo-renderer-test.png"); buffer_object = indigoWriteBuffer(); indigoRender(molecule, buffer_object); indigoToBuffer(buffer_object, &raw_ptr, &size); // indigoFree(buffer_object); #ifdef WIN32 hdc = GetWindowDC(NULL); hdc2 = CreateCompatibleDC(hdc); hdc_buffer_object = indigoRenderWriteHDC(hdc2, 0); indigoRender(molecule, hdc_buffer_object); indigoFree(hdc_buffer_object); #endif indigoFree(molecule); } int main (void) { int m; indigoSetErrorHandler(onError, 0); printf("%s\n", indigoVersion()); m = indigoLoadMoleculeFromString("COC1=CC2=C(NC(=C2)C(O)(CC2=CN=CC=C2)CC2=CN=CC=C2)C=C1"); printf("%s\n", indigoCanonicalSmiles(m)); indigoSetOption("render-output-format", "png"); indigoSetOption("render-atom-ids-visible", "true"); indigoSetOption("render-bond-ids-visible", "true"); indigoSetOption("render-background-color", "255, 255, 255"); indigoRenderToFile(m, "indigo-renderer-test.png"); testHDC(); return 0; } Indigo-indigo-1.2.3/api/python/000077500000000000000000000000001271037650300162735ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/python/copy-libs.py000066400000000000000000000004041271037650300205440ustar00rootroot00000000000000import os import shutil from os.path import * cur_dir = split(__file__)[0] lib_dir = join(cur_dir, "lib") if exists(lib_dir): shutil.rmtree(lib_dir) srclibs_dir = join(cur_dir, "..", "libs", "shared") shutil.copytree(srclibs_dir, lib_dir) Indigo-indigo-1.2.3/api/python/indigo.py000066400000000000000000003312041271037650300201210ustar00rootroot00000000000000# # # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. import sys import os import platform from array import array from ctypes import c_int, c_char_p, c_float, POINTER, pointer, CDLL, RTLD_GLOBAL, c_ulonglong, c_byte DECODE_ENCODING = 'ascii' ENCODE_ENCODING = 'ascii' class IndigoException (Exception): def __init__(self, value): self.value = value def __str__(self): if sys.version_info > (3, 0): return str(self.value.decode(DECODE_ENCODING)) else: return str(self.value) class IndigoObject(object): def __init__(self, dispatcher, id, parent=None): self.id = id self.dispatcher = dispatcher self.parent = parent def __enter__ (self): return self def __exit__ (self, exc_type, exc_value, traceback): self.dispatcher._setSessionId() self.dispatcher._lib.indigoClose(self.id) def __del__ (self): self.dispose() def dispose(self): if self.id >= 0: if getattr(Indigo, "_lib", None) is not None: self.dispatcher._setSessionId() Indigo._lib.indigoFree(self.id) self.id = -1 def __iter__ (self): return self def _next(self): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoNext(self.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def __next__ (self): obj = self._next() if obj == None: raise StopIteration return obj def next (self): return self.__next__() def oneBitsList(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoOneBitsList(self.id)) def mdlct(self): self.dispatcher._setSessionId() buf = self.dispatcher.writeBuffer() self.dispatcher._checkResult(Indigo._lib.indigoSaveMDLCT(self.id, buf.id)) return buf.toBuffer() def xyz (self): self.dispatcher._setSessionId() xyz = Indigo._lib.indigoXYZ(self.id) if xyz is None: raise IndigoException(Indigo._lib.indigoGetLastError()) return [xyz[0], xyz[1], xyz[2]] def alignAtoms (self, atom_ids, desired_xyz): self.dispatcher._setSessionId() if len(atom_ids) * 3 != len(desired_xyz): raise IndigoException("alignAtoms(): desired_xyz[] must be exactly 3 times bigger than atom_ids[]") atoms = (c_int * len(atom_ids))() for i in range(len(atoms)): atoms[i] = atom_ids[i] xyz = (c_float * len(desired_xyz))() for i in range(len(desired_xyz)): xyz[i] = desired_xyz[i] return self.dispatcher._checkResultFloat( self.dispatcher._lib.indigoAlignAtoms(self.id, len(atoms), atoms, xyz)) def addStereocenter(self, type, v1, v2, v3, v4=-1): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddStereocenter(self.id, type, v1, v2, v3, v4)) def clone(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoClone(self.id))) def close(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClose(self.id)) def _next(self): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoNext(self.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def hasNext(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoHasNext(self.id))) def index(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoIndex(self.id)) def remove(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoRemove(self.id)) def saveMolfile(self, filename): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSaveMolfileToFile(self.id, filename.encode(ENCODE_ENCODING))) def molfile(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoMolfile(self.id)) def saveCml(self, filename): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSaveCmlToFile(self.id, filename.encode(ENCODE_ENCODING))) def cml(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoCml(self.id)) def saveMDLCT(self, output): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSaveMDLCT(self.id, output.id)) def addReactant(self, molecule): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddReactant(self.id, molecule.id)) def addProduct(self, molecule): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddProduct(self.id, molecule.id)) def addCatalyst(self, molecule): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddCatalyst(self.id, molecule.id)) def countReactants(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountReactants(self.id)) def countProducts(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountProducts(self.id)) def countCatalysts(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountCatalysts(self.id)) def countMolecules(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountMolecules(self.id)) def getMolecule(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetMolecule(self.id, index))) def iterateReactants(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateReactants(self.id))) def iterateProducts(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateProducts(self.id))) def iterateCatalysts(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateCatalysts(self.id))) def iterateMolecules(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateMolecules(self.id))) def saveRxnfile(self, filename): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSaveRxnfileToFile(self.id, filename.encode(ENCODE_ENCODING))) def rxnfile(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoRxnfile(self.id)) def optimize(self, options=''): self.dispatcher._setSessionId() if options is None: options = '' return self.dispatcher._checkResult(Indigo._lib.indigoOptimize(self.id, options.encode(ENCODE_ENCODING))) def normalize(self, options=''): self.dispatcher._setSessionId() if options is None: options = '' return bool(self.dispatcher._checkResult(Indigo._lib.indigoNormalize(self.id, options.encode(ENCODE_ENCODING)))) def standardize(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoStandardize(self.id)) def ionize(self, pH, pH_toll): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoIonize(self.id, pH, pH_toll)) def getAcidPkaValue(self, atom, level, min_level): self.dispatcher._setSessionId() result = self.dispatcher._checkResultPtr(Indigo._lib.indigoGetAcidPkaValue(self.id, atom.id, level, min_level)) return result[0] def getBasicPkaValue(self, atom, level, min_level): self.dispatcher._setSessionId() result = self.dispatcher._checkResultPtr(Indigo._lib.indigoGetBasicPkaValue(self.id, atom.id, level, min_level)) return result[0] def automap(self, mode=''): self.dispatcher._setSessionId() if mode is None: mode = '' return self.dispatcher._checkResult(Indigo._lib.indigoAutomap(self.id, mode.encode(ENCODE_ENCODING))) def atomMappingNumber(self, reaction_atom): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetAtomMappingNumber(self.id, reaction_atom.id)) def setAtomMappingNumber(self, reaction_atom, number): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetAtomMappingNumber(self.id, reaction_atom.id, number)) def reactingCenter(self, reaction_bond): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoGetReactingCenter(self.id, reaction_bond.id, pointer(value))) if res == 0: return None return value.value def setReactingCenter(self, reaction_bond, rc): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetReactingCenter(self.id, reaction_bond.id, rc)) def clearAAM(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearAAM(self.id)) def correctReactingCenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCorrectReactingCenters(self.id)) def iterateAtoms(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateAtoms(self.id))) def iteratePseudoatoms(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIteratePseudoatoms(self.id))) def iterateRSites(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateRSites(self.id))) def iterateStereocenters(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateStereocenters(self.id))) def iterateAlleneCenters(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateAlleneCenters(self.id))) def iterateRGroups(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateRGroups(self.id))) def isPseudoatom(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoIsPseudoatom(self.id))) def isRSite(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoIsRSite(self.id))) def stereocenterType(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoStereocenterType(self.id)) def stereocenterGroup(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoStereocenterGroup(self.id)) def setStereocenterGroup(self, group): self.dispatcher._setSessionId() self.dispatcher._checkResult(Indigo._lib.indigoSetStereocenterGroup(self.id, group)) def changeStereocenterType(self, type): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoChangeStereocenterType(self.id, type)) def validateChirality(self): self.dispatcher._setSessionId() self.dispatcher._checkResult(Indigo._lib.indigoValidateChirality(self.id)) def singleAllowedRGroup(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSingleAllowedRGroup(self.id)) def iterateRGroupFragments(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateRGroupFragments(self.id))) def countAttachmentPoints(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountAttachmentPoints(self.id)) def iterateAttachmentPoints(self, order): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateAttachmentPoints(self.id, order))) def symbol(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoSymbol(self.id)) def degree(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoDegree(self.id)) def charge(self): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoGetCharge(self.id, pointer(value))) if res == 0: return None return value.value def getExplicitValence(self): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoGetExplicitValence(self.id, pointer(value))) if res == 0: return None return value.value def setExplicitValence(self, valence): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetExplicitValence(self.id, valence)) def radicalElectrons(self): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoGetRadicalElectrons(self.id, pointer(value))) if res == 0: return None return value.value def radical(self): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoGetRadical(self.id, pointer(value))) if res == 0: return None return value.value def setRadical(self, radical): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetRadical(self.id, radical)) def atomicNumber(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAtomicNumber(self.id)) def isotope(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoIsotope(self.id)) def valence(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoValence(self.id)) def countHydrogens(self): self.dispatcher._setSessionId() value = c_int() res = self.dispatcher._checkResult(Indigo._lib.indigoCountHydrogens(self.id, pointer(value))) if res == 0: return None return value.value def countImplicitHydrogens(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountImplicitHydrogens(self.id)) def setXYZ(self, x, y, z): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetXYZ(self.id, x, y, z)) def countSuperatoms(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountSuperatoms(self.id)) def countDataSGroups(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountDataSGroups(self.id)) def countRepeatingUnits(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountRepeatingUnits(self.id)) def countMultipleGroups(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountMultipleGroups(self.id)) def countGenericSGroups(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountGenericSGroups(self.id)) def iterateDataSGroups(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateDataSGroups(self.id))) def iterateSuperatoms(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateSuperatoms(self.id))) def iterateGenericSGroups(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateGenericSGroups(self.id))) def iterateRepeatingUnits(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateRepeatingUnits(self.id))) def iterateMultipleGroups(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateMultipleGroups(self.id))) def getSuperatom(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetSuperatom(self.id, index))) def getDataSGroup(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetDataSGroup(self.id, index))) def getGenericSGroup(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetGenericSGroup(self.id, index))) def getMultipleGroup(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetMultipleGroup(self.id, index))) def getRepeatingUnit(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetRepeatingUnit(self.id, index))) def description(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoDescription(self.id)) def data(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoData(self.id)) def addDataSGroup(self, atoms, bonds, description, data): self.dispatcher._setSessionId() arr2 = (c_int * len(atoms))() for i in range(len(atoms)): arr2[i] = atoms[i] arr4 = (c_int * len(bonds))() for i in range(len(bonds)): arr4[i] = bonds[i] return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAddDataSGroup(self.id, len(arr2), arr2, len(arr4), arr4, description.encode(ENCODE_ENCODING), data.encode(ENCODE_ENCODING)))) def addSuperatom(self, atoms, name): self.dispatcher._setSessionId() arr2 = (c_int * len(atoms))() for i in range(len(atoms)): arr2[i] = atoms[i] return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAddSuperatom(self.id, len(arr2), arr2, name.encode(ENCODE_ENCODING)))) def setDataSGroupXY(self, x, y, options=''): self.dispatcher._setSessionId() if options is None: options = '' return self.dispatcher._checkResult(Indigo._lib.indigoSetDataSGroupXY(self.id, x, y, options.encode(ENCODE_ENCODING))) def setSGroupData(self, data): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupData(self.id, data.encode(ENCODE_ENCODING))) def setSGroupCoords(self, x, y): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupCoords(self.id, x, y)) def setSGroupDescription(self, description): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupDescription(self.id, description.encode(ENCODE_ENCODING))) def setSGroupFieldName(self, name): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupFieldName(self.id, name.encode(ENCODE_ENCODING))) def setSGroupQueryCode(self, code): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupQueryCode(self.id, code.encode(ENCODE_ENCODING))) def setSGroupQueryOper(self, oper): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupQueryOper(self.id, oper.encode(ENCODE_ENCODING))) def setSGroupDisplay(self, option): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupDisplay(self.id, option.encode(ENCODE_ENCODING))) def setSGroupLocation(self, option): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupLocation(self.id, option.encode(ENCODE_ENCODING))) def setSGroupTag(self, tag): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupTag(self.id, tag.encode(ENCODE_ENCODING))) def setSGroupTagAlign(self, tag_align): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupTagAlign(self.id, tag_align)) def setSGroupDataType(self, data_type): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupDataType(self.id, data_type.encode(ENCODE_ENCODING))) def setSGroupXCoord(self, x): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupXCoord(self.id, x)) def setSGroupYCoord(self, y): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupYCoord(self.id, y)) def createSGroup(self, sgtype, mapping, name): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoCreateSGroup(sgtype.encode(ENCODE_ENCODING), mapping.id, name.encode(ENCODE_ENCODING)))) def setSGroupClass(self, sgclass): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupClass(self.id, sgclass.encode(ENCODE_ENCODING))) def setSGroupName(self, sgname): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupName(self.id, sgname.encode(ENCODE_ENCODING))) def getSGroupClass(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoGetSGroupClass(self.id)) def getSGroupName(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoGetSGroupName(self.id)) def getSGroupNumCrossBonds(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetSGroupNumCrossBonds(self.id)) def addSGroupAttachmentPoint(self, aidx, lvidx, apid): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddSGroupAttachmentPoint(self.id, aidx, lvidx, apid.encode(ENCODE_ENCODING))) def deleteSGroupAttachmentPoint(self, apidx): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoDeleteSGroupAttachmentPoint(self.id, apidx)) def getSGroupDisplayOption(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetSGroupDisplayOption(self.id)) def setSGroupDisplayOption(self, option): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupDisplayOption(self.id, option)) def getSGroupMultiplier(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetSGroupMultiplier(self.id)) def setSGroupMultiplier(self, mult): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupMultiplier(self.id, mult)) def setSGroupBrackets(self, style, x1, y1, x2, y2, x3, y3, x4, y4): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetSGroupBrackets(self.id, style, x1, y1, x2, y2, x3, y3, x4, y4)) def findSGroups(self, prop, val): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoFindSGroups(self.id, prop.encode(ENCODE_ENCODING), val.encode(ENCODE_ENCODING)))) def getSGroupType(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetSGroupType(self.id)) def getSGroupIndex(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoGetSGroupIndex(self.id)) def transformSCSRtoCTAB(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoTransformSCSRtoCTAB(self.id)) def transformCTABtoSCSR(self, templates): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoTransformCTABtoSCSR(self.id, templates.id)) def resetCharge(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetCharge(self.id)) def resetExplicitValence(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetExplicitValence(self.id)) def resetRadical(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetRadical(self.id)) def resetIsotope(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetIsotope(self.id)) def setAttachmentPoint(self, order): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetAttachmentPoint(self.id, order)) def clearAttachmentPoints(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearAttachmentPoints(self.id)) def removeConstraints(self, type): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoRemoveConstraints(self.id, type.encode(ENCODE_ENCODING))) def addConstraint(self, type, value): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddConstraint(self.id, type.encode(ENCODE_ENCODING), value.encode(ENCODE_ENCODING))) def addConstraintNot(self, type, value): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddConstraintNot(self.id, type.encode(ENCODE_ENCODING), value.encode(ENCODE_ENCODING))) def addConstraintOr(self, type, value): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddConstraintOr(self.id, type.encode(ENCODE_ENCODING), value.encode(ENCODE_ENCODING))) def resetStereo(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetStereo(self.id)) def invertStereo(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoInvertStereo(self.id)) def countAtoms(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountAtoms(self.id)) def countBonds(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountBonds(self.id)) def countPseudoatoms(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountPseudoatoms(self.id)) def countRSites(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountRSites(self.id)) def iterateBonds(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateBonds(self.id))) def bondOrder(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoBondOrder(self.id)) def bondStereo(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoBondStereo(self.id)) def topology(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoTopology(self.id)) def iterateNeighbors(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateNeighbors(self.id))) def bond(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoBond(self.id))) def getAtom(self, idx): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetAtom(self.id, idx))) def getBond(self, idx): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetBond(self.id, idx))) def source(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoSource(self.id))) def destination(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoDestination(self.id))) def clearCisTrans(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearCisTrans(self.id)) def clearStereocenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearStereocenters(self.id)) def countStereocenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountStereocenters(self.id)) def clearAlleneCenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearAlleneCenters(self.id)) def countAlleneCenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountAlleneCenters(self.id)) def resetSymmetricCisTrans(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetSymmetricCisTrans(self.id)) def resetSymmetricStereocenters(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoResetSymmetricStereocenters(self.id)) def markEitherCisTrans(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoMarkEitherCisTrans(self.id)) def markStereobonds(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoMarkStereobonds(self.id)) def addAtom(self, symbol): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAddAtom(self.id, symbol.encode(ENCODE_ENCODING)))) def resetAtom(self, symbol): self.dispatcher._setSessionId() self.dispatcher._checkResult(Indigo._lib.indigoResetAtom(self.id, symbol.encode(ENCODE_ENCODING))) def addRSite(self, name): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAddRSite(self.id, name.encode(ENCODE_ENCODING)))) def setRSite(self, name): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetRSite(self.id, name.encode(ENCODE_ENCODING))) def setCharge(self, charge): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetCharge(self.id, charge)) def setIsotope(self, isotope): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetIsotope(self.id, isotope)) def setImplicitHCount(self, impl_h): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetImplicitHCount(self.id, impl_h)) def addBond(self, destination, order): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAddBond(self.id, destination.id, order))) def setBondOrder(self, order): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoSetBondOrder(self.id, order))) def merge(self, what): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoMerge(self.id, what.id))) def highlight(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoHighlight(self.id)) def unhighlight(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoUnhighlight(self.id)) def isHighlighted(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoIsHighlighted(self.id))) def countComponents(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountComponents(self.id)) def componentIndex(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoComponentIndex(self.id)) def iterateComponents(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateComponents(self.id))) def component(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoComponent(self.id, index))) def countSSSR(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountSSSR(self.id)) def iterateSSSR(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateSSSR(self.id))) def iterateSubtrees(self, min_atoms, max_atoms): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateSubtrees(self.id, min_atoms, max_atoms))) def iterateRings(self, min_atoms, max_atoms): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateRings(self.id, min_atoms, max_atoms))) def iterateEdgeSubmolecules(self, min_bonds, max_bonds): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateEdgeSubmolecules(self.id, min_bonds, max_bonds))) def countHeavyAtoms(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountHeavyAtoms(self.id)) def grossFormula(self): self.dispatcher._setSessionId() gfid = self.dispatcher._checkResult(Indigo._lib.indigoGrossFormula(self.id)) gf = self.dispatcher.IndigoObject(self.dispatcher, gfid) return self.dispatcher._checkResultString(Indigo._lib.indigoToString(gf.id)) def molecularWeight(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultFloat(Indigo._lib.indigoMolecularWeight(self.id)) def mostAbundantMass(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultFloat(Indigo._lib.indigoMostAbundantMass(self.id)) def monoisotopicMass(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultFloat(Indigo._lib.indigoMonoisotopicMass(self.id)) def canonicalSmiles(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoCanonicalSmiles(self.id)) def layeredCode(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoLayeredCode(self.id)) def symmetryClasses(self): self.dispatcher._setSessionId() c_size = c_int() c_buf = self.dispatcher._checkResultPtr(Indigo._lib.indigoSymmetryClasses(self.id, pointer(c_size))) res = array("i") for i in range(c_size.value): res.append(c_buf[i]) return res def hasCoord(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoHasCoord(self.id))) def hasZCoord(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoHasZCoord(self.id))) def isChiral(self): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoIsChiral(self.id))) def isPossibleFischerProjection(self, options): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoIsPossibleFischerProjection(self.id, options.encode(ENCODE_ENCODING)))) def createSubmolecule(self, vertices): self.dispatcher._setSessionId() arr2 = (c_int * len(vertices))() for i in range(len(vertices)): arr2[i] = vertices[i] return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoCreateSubmolecule(self.id, len(arr2), arr2))) def createEdgeSubmolecule(self, vertices, edges): self.dispatcher._setSessionId() arr2 = (c_int * len(vertices))() for i in range(len(vertices)): arr2[i] = vertices[i] arr4 = (c_int * len(edges))() for i in range(len(edges)): arr4[i] = edges[i] return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoCreateEdgeSubmolecule(self.id, len(arr2), arr2, len(arr4), arr4))) def getSubmolecule(self, vertices): self.dispatcher._setSessionId() arr2 = (c_int * len(vertices))() for i in range(len(vertices)): arr2[i] = vertices[i] return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoGetSubmolecule(self.id, len(arr2), arr2))) def removeAtoms(self, vertices): self.dispatcher._setSessionId() arr2 = (c_int * len(vertices))() for i in range(len(vertices)): arr2[i] = vertices[i] return self.dispatcher._checkResult(Indigo._lib.indigoRemoveAtoms(self.id, len(arr2), arr2)) def removeBonds(self, bonds): self.dispatcher._setSessionId() arr2 = (c_int * len(bonds))() for i in range(len(bonds)): arr2[i] = bonds[i] return self.dispatcher._checkResult(Indigo._lib.indigoRemoveBonds(self.id, len(arr2), arr2)) def aromatize(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAromatize(self.id)) def dearomatize(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoDearomatize(self.id)) def foldHydrogens(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoFoldHydrogens(self.id)) def unfoldHydrogens(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoUnfoldHydrogens(self.id)) def layout(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoLayout(self.id)) def smiles(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoSmiles(self.id)) def name(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoName(self.id)) def setName(self, name): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetName(self.id, name.encode(ENCODE_ENCODING))) def serialize(self): self.dispatcher._setSessionId() c_size = c_int() c_buf = POINTER(c_byte)() self.dispatcher._checkResult(Indigo._lib.indigoSerialize(self.id, pointer(c_buf), pointer(c_size))) res = array('b') for i in range(c_size.value): res.append(c_buf[i]) return res def hasProperty(self, prop): self.dispatcher._setSessionId() return bool(self.dispatcher._checkResult(Indigo._lib.indigoHasProperty(self.id, prop))) def getProperty(self, prop): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoGetProperty(self.id, prop.encode(ENCODE_ENCODING))) def setProperty(self, prop, value): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSetProperty(self.id, prop.encode(ENCODE_ENCODING), value.encode(ENCODE_ENCODING))) def removeProperty(self, prop): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoRemoveProperty(self.id, prop.encode(ENCODE_ENCODING))) def iterateProperties(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateProperties(self.id))) def clearProperties(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClearProperties(self.id)) def checkBadValence(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoCheckBadValence(self.id)) def checkAmbiguousH(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoCheckAmbiguousH(self.id)) def fingerprint(self, type): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoFingerprint(self.id, type.encode(ENCODE_ENCODING))) if newobj == 0: return None return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def countBits(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountBits(self.id)) def rawData(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoRawData(self.id)) def tell(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoTell(self.id)) def sdfAppend(self, item): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSdfAppend(self.id, item.id)) def smilesAppend(self, item): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoSmilesAppend(self.id, item.id)) def rdfHeader(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoRdfHeader(self.id)) def rdfAppend(self, item): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoRdfAppend(self.id, item.id)) def cmlHeader(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCmlHeader(self.id)) def cmlAppend(self, item): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCmlAppend(self.id, item.id)) def cmlFooter(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCmlFooter(self.id)) def append(self, object): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAppend(self.id, object.id)) def arrayAdd(self, object): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoArrayAdd(self.id, object.id)) def at(self, index): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAt(self.id, index))) def count(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCount(self.id)) def clear(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoClear(self.id)) def iterateArray(self): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoIterateArray(self.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def ignoreAtom(self, atom_object): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoIgnoreAtom(self.id, atom_object.id)) def unignoreAtom(self, atom_object): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoUnignoreAtom(self.id, atom_object.id)) def unignoreAllAtoms(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoUnignoreAllAtoms(self.id)) def match(self, query): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoMatch(self.id, query.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def countMatches(self, query): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountMatches(self.id, query.id)) def countMatchesWithLimit(self, query, embeddings_limit): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoCountMatchesWithLimit(self.id, query.id, embeddings_limit)) def iterateMatches(self, query): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateMatches(self.id, query.id))) def highlightedTarget(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoHighlightedTarget(self.id))) def mapAtom(self, atom): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoMapAtom(self.id, atom.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def mapBond(self, bond): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoMapBond(self.id, bond.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def mapMolecule(self, molecule): self.dispatcher._setSessionId() newobj = self.dispatcher._checkResult(Indigo._lib.indigoMapMolecule(self.id, molecule.id)) if newobj == 0: return None else: return self.dispatcher.IndigoObject(self.dispatcher, newobj, self) def allScaffolds(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoAllScaffolds(self.id))) def decomposedMoleculeScaffold(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoDecomposedMoleculeScaffold(self.id))) def iterateDecomposedMolecules(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateDecomposedMolecules(self.id))) def decomposedMoleculeHighlighted(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoDecomposedMoleculeHighlighted(self.id))) def decomposedMoleculeWithRGroups(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoDecomposedMoleculeWithRGroups(self.id))) def decomposeMolecule(self, mol): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoDecomposeMolecule(self.id, mol.id))) def iterateDecompositions(self): self.dispatcher._setSessionId() return self.dispatcher.IndigoObject(self.dispatcher, self.dispatcher._checkResult(Indigo._lib.indigoIterateDecompositions(self.id))) def addDecomposition(self, q_match): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoAddDecomposition(self.id, q_match.id)) def toString(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoToString(self.id)) def toBuffer(self): self.dispatcher._setSessionId() c_size = c_int() c_buf = POINTER(c_byte)() self.dispatcher._checkResult(Indigo._lib.indigoToBuffer(self.id, pointer(c_buf), pointer(c_size))) res = array("b") for i in range(c_size.value): res.append(c_buf[i]) return res def stereocenterPyramid(self): self.dispatcher._setSessionId() ptr = self.dispatcher._checkResultPtr(Indigo._lib.indigoStereocenterPyramid(self.id)) res = [0] * 4 for i in range(4): res[i] = ptr[i] return res def expandAbbreviations(self): self.dispatcher._setSessionId() return self.dispatcher._checkResult(Indigo._lib.indigoExpandAbbreviations(self.id)) def dbgInternalType(self): self.dispatcher._setSessionId() return self.dispatcher._checkResultString(Indigo._lib.indigoDbgInternalType(self.id)) class Indigo(object): ABS = 1 OR = 2 AND = 3 EITHER = 4 UP = 5 DOWN = 6 CIS = 7 TRANS = 8 CHAIN = 9 RING = 10 ALLENE = 11 SINGLET = 101 DOUBLET = 102 TRIPLET = 103 RC_NOT_CENTER = -1 RC_UNMARKED = 0 RC_CENTER = 1 RC_UNCHANGED = 2 RC_MADE_OR_BROKEN = 4 RC_ORDER_CHANGED = 8 _crt = None _crtp = None _lib = None # Python embeds path into .pyc code if method is marked with @staticmethod # This causes an error when Indigo is loaded from different places by relative path def _initStatic (self, path = None): if not path: cur_file = os.path.abspath(__file__) dirname = os.path.dirname(cur_file) if not dirname: dirname = '.' path = dirname + '/lib' if os.name == 'posix' and not platform.mac_ver()[0] and not platform.system().startswith("CYGWIN"): arch = platform.architecture()[0] path += "/Linux" if arch == '32bit': path += "/x86" elif arch == '64bit': path += "/x64" else: raise IndigoException("unknown platform " + arch) Indigo._lib = CDLL(path + "/libindigo.so", mode=RTLD_GLOBAL) elif os.name == 'nt' or platform.system().startswith("CYGWIN"): arch = platform.architecture()[0] path += "/Win" if arch == '32bit': path += "/x86" elif arch == '64bit': path += "/x64" else: raise IndigoException("unknown platform " + arch) if os.path.exists(path + "/msvcr100.dll"): Indigo._crt = CDLL(path + "/msvcr100.dll") Indigo._crtp = CDLL(path + "/msvcp100.dll") if os.path.exists(path + "/msvcr110.dll"): Indigo._crt = CDLL(path + "/msvcr110.dll") Indigo._crtp = CDLL(path + "/msvcp110.dll") if os.path.exists(path + "/msvcr120.dll"): Indigo._crt = CDLL(path + "/msvcr120.dll") Indigo._crtp = CDLL(path + "/msvcp120.dll") Indigo._lib = CDLL(path + "/indigo.dll") elif platform.mac_ver()[0]: path += "/Mac/" mac_ver = '.'.join(platform.mac_ver()[0].split('.')[:2]) current_mac_ver = int(mac_ver.split('.')[1]) using_mac_ver = None for version in reversed(range(5, current_mac_ver + 1)): if os.path.exists(path + '/10.' + str(version)): using_mac_ver = str(version) break if (using_mac_ver is None): raise IndigoException("no binaries for this version of Mac OS X: " + mac_ver) path += '/10.' + using_mac_ver Indigo._lib = CDLL(path + "/libindigo.dylib", mode=RTLD_GLOBAL) else: raise IndigoException("unsupported OS: " + os.name) Indigo.dllpath = path def _setSessionId (self): Indigo._lib.indigoSetSessionId(self._sid) def __init__ (self, path=None): if Indigo._lib is None: self._initStatic(path) self._sid = Indigo._lib.indigoAllocSessionId() # Capture a reference to the _lib to access it in the __del__ method because # at interpreter shutdown, the module's global variables are set to None self._lib = Indigo._lib self._setSessionId() self.IndigoObject = IndigoObject Indigo._lib.indigoVersion.restype = c_char_p Indigo._lib.indigoVersion.argtypes = None Indigo._lib.indigoAllocSessionId.restype = c_ulonglong Indigo._lib.indigoAllocSessionId.argtypes = None Indigo._lib.indigoSetSessionId.restype = None Indigo._lib.indigoSetSessionId.argtypes = [c_ulonglong] Indigo._lib.indigoReleaseSessionId.restype = None Indigo._lib.indigoReleaseSessionId.argtypes = [c_ulonglong] Indigo._lib.indigoGetLastError.restype = c_char_p Indigo._lib.indigoGetLastError.argtypes = None Indigo._lib.indigoFree.restype = c_int Indigo._lib.indigoFree.argtypes = [c_int] Indigo._lib.indigoCountReferences.restype = c_int Indigo._lib.indigoCountReferences.argtypes = None Indigo._lib.indigoFreeAllObjects.restype = c_int Indigo._lib.indigoFreeAllObjects.argtypes = None Indigo._lib.indigoSetOption.restype = c_int Indigo._lib.indigoSetOption.argtypes = [c_char_p, c_char_p] Indigo._lib.indigoSetOptionInt.restype = c_int Indigo._lib.indigoSetOptionInt.argtypes = [c_char_p, c_int] Indigo._lib.indigoSetOptionBool.restype = c_int Indigo._lib.indigoSetOptionBool.argtypes = [c_char_p, c_int] Indigo._lib.indigoSetOptionFloat.restype = c_int Indigo._lib.indigoSetOptionFloat.argtypes = [c_char_p, c_float] Indigo._lib.indigoSetOptionColor.restype = c_int Indigo._lib.indigoSetOptionColor.argtypes = [c_char_p, c_float, c_float, c_float] Indigo._lib.indigoSetOptionXY.restype = c_int Indigo._lib.indigoSetOptionXY.argtypes = [c_char_p, c_int, c_int] Indigo._lib.indigoReadFile.restype = c_int Indigo._lib.indigoReadFile.argtypes = [c_char_p] Indigo._lib.indigoLoadString.restype = c_int Indigo._lib.indigoLoadString.argtypes = [c_char_p] Indigo._lib.indigoLoadBuffer.restype = c_int Indigo._lib.indigoLoadBuffer.argtypes = [POINTER(c_byte), c_int] Indigo._lib.indigoWriteFile.restype = c_int Indigo._lib.indigoWriteFile.argtypes = [c_char_p] Indigo._lib.indigoWriteBuffer.restype = c_int Indigo._lib.indigoWriteBuffer.argtypes = None Indigo._lib.indigoCreateMolecule.restype = c_int Indigo._lib.indigoCreateMolecule.argtypes = None Indigo._lib.indigoCreateQueryMolecule.restype = c_int Indigo._lib.indigoCreateQueryMolecule.argtypes = None Indigo._lib.indigoLoadMoleculeFromString.restype = c_int Indigo._lib.indigoLoadMoleculeFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadMoleculeFromFile.restype = c_int Indigo._lib.indigoLoadMoleculeFromFile.argtypes = [c_char_p] Indigo._lib.indigoLoadQueryMoleculeFromString.restype = c_int Indigo._lib.indigoLoadQueryMoleculeFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadQueryMoleculeFromFile.restype = c_int Indigo._lib.indigoLoadQueryMoleculeFromFile.argtypes = [c_char_p] Indigo._lib.indigoLoadSmartsFromString.restype = c_int Indigo._lib.indigoLoadSmartsFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadSmartsFromFile.restype = c_int Indigo._lib.indigoLoadSmartsFromFile.argtypes = [c_char_p] Indigo._lib.indigoLoadReactionFromString.restype = c_int Indigo._lib.indigoLoadReactionFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadReactionFromFile.restype = c_int Indigo._lib.indigoLoadReactionFromFile.argtypes = [c_char_p] Indigo._lib.indigoLoadQueryReactionFromString.restype = c_int Indigo._lib.indigoLoadQueryReactionFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadQueryReactionFromFile.restype = c_int Indigo._lib.indigoLoadQueryReactionFromFile.argtypes = [c_char_p] Indigo._lib.indigoLoadReactionSmartsFromString.restype = c_int Indigo._lib.indigoLoadReactionSmartsFromString.argtypes = [c_char_p] Indigo._lib.indigoLoadReactionSmartsFromFile.restype = c_int Indigo._lib.indigoLoadReactionSmartsFromFile.argtypes = [c_char_p] Indigo._lib.indigoCreateReaction.restype = c_int Indigo._lib.indigoCreateReaction.argtypes = None Indigo._lib.indigoCreateQueryReaction.restype = c_int Indigo._lib.indigoCreateQueryReaction.argtypes = None Indigo._lib.indigoExactMatch.restype = c_int Indigo._lib.indigoExactMatch.argtypes = [c_int, c_int, c_char_p] Indigo._lib.indigoSetTautomerRule.restype = c_int Indigo._lib.indigoSetTautomerRule.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoRemoveTautomerRule.restype = c_int Indigo._lib.indigoRemoveTautomerRule.argtypes = [c_int] Indigo._lib.indigoClearTautomerRules.restype = c_int Indigo._lib.indigoClearTautomerRules.argtypes = None Indigo._lib.indigoUnserialize.restype = c_int Indigo._lib.indigoUnserialize.argtypes = [POINTER(c_byte), c_int] Indigo._lib.indigoCommonBits.restype = c_int Indigo._lib.indigoCommonBits.argtypes = [c_int, c_int] Indigo._lib.indigoSimilarity.restype = c_float Indigo._lib.indigoSimilarity.argtypes = [c_int, c_int, c_char_p] Indigo._lib.indigoIterateSDF.restype = c_int Indigo._lib.indigoIterateSDF.argtypes = [c_int] Indigo._lib.indigoIterateRDF.restype = c_int Indigo._lib.indigoIterateRDF.argtypes = [c_int] Indigo._lib.indigoIterateSmiles.restype = c_int Indigo._lib.indigoIterateSmiles.argtypes = [c_int] Indigo._lib.indigoIterateCML.restype = c_int Indigo._lib.indigoIterateCML.argtypes = [c_int] Indigo._lib.indigoIterateCDX.restype = c_int Indigo._lib.indigoIterateCDX.argtypes = [c_int] Indigo._lib.indigoIterateSDFile.restype = c_int Indigo._lib.indigoIterateSDFile.argtypes = [c_char_p] Indigo._lib.indigoIterateRDFile.restype = c_int Indigo._lib.indigoIterateRDFile.argtypes = [c_char_p] Indigo._lib.indigoIterateSmilesFile.restype = c_int Indigo._lib.indigoIterateSmilesFile.argtypes = [c_char_p] Indigo._lib.indigoIterateCMLFile.restype = c_int Indigo._lib.indigoIterateCMLFile.argtypes = [c_char_p] Indigo._lib.indigoIterateCDXFile.restype = c_int Indigo._lib.indigoIterateCDXFile.argtypes = [c_char_p] Indigo._lib.indigoCreateSaver.restype = c_int Indigo._lib.indigoCreateSaver.argtypes = [c_int, c_char_p] Indigo._lib.indigoCreateFileSaver.restype = c_int Indigo._lib.indigoCreateFileSaver.argtypes = [c_char_p, c_char_p] Indigo._lib.indigoCreateArray.restype = c_int Indigo._lib.indigoCreateArray.argtypes = None Indigo._lib.indigoSubstructureMatcher.restype = c_int Indigo._lib.indigoSubstructureMatcher.argtypes = [c_int, c_char_p] Indigo._lib.indigoExtractCommonScaffold.restype = c_int Indigo._lib.indigoExtractCommonScaffold.argtypes = [c_int, c_char_p] Indigo._lib.indigoDecomposeMolecules.restype = c_int Indigo._lib.indigoDecomposeMolecules.argtypes = [c_int, c_int] Indigo._lib.indigoRGroupComposition.restype = c_int Indigo._lib.indigoRGroupComposition.argtypes = [c_int, c_char_p] Indigo._lib.indigoGetFragmentedMolecule.restype = c_int Indigo._lib.indigoGetFragmentedMolecule.argtypes = [c_int, c_char_p] Indigo._lib.indigoCreateDecomposer.restype = c_int Indigo._lib.indigoCreateDecomposer.argtypes = [c_int] Indigo._lib.indigoReactionProductEnumerate.restype = c_int Indigo._lib.indigoReactionProductEnumerate.argtypes = [c_int, c_int] Indigo._lib.indigoTransform.restype = c_int Indigo._lib.indigoTransform.argtypes = [c_int, c_int] Indigo._lib.indigoDbgBreakpoint.restype = None Indigo._lib.indigoDbgBreakpoint.argtypes = None Indigo._lib.indigoClone.restype = c_int Indigo._lib.indigoClone.argtypes = [c_int] Indigo._lib.indigoClose.restype = c_int Indigo._lib.indigoClose.argtypes = [c_int] Indigo._lib.indigoNext.restype = c_int Indigo._lib.indigoNext.argtypes = [c_int] Indigo._lib.indigoHasNext.restype = c_int Indigo._lib.indigoHasNext.argtypes = [c_int] Indigo._lib.indigoIndex.restype = c_int Indigo._lib.indigoIndex.argtypes = [c_int] Indigo._lib.indigoRemove.restype = c_int Indigo._lib.indigoRemove.argtypes = [c_int] Indigo._lib.indigoSaveMolfileToFile.restype = c_int Indigo._lib.indigoSaveMolfileToFile.argtypes = [c_int, c_char_p] Indigo._lib.indigoMolfile.restype = c_char_p Indigo._lib.indigoMolfile.argtypes = [c_int] Indigo._lib.indigoSaveCmlToFile.restype = c_int Indigo._lib.indigoSaveCmlToFile.argtypes = [c_int, c_char_p] Indigo._lib.indigoCml.restype = c_char_p Indigo._lib.indigoCml.argtypes = [c_int] Indigo._lib.indigoSaveMDLCT.restype = c_int Indigo._lib.indigoSaveMDLCT.argtypes = [c_int, c_int] Indigo._lib.indigoAddReactant.restype = c_int Indigo._lib.indigoAddReactant.argtypes = [c_int, c_int] Indigo._lib.indigoAddProduct.restype = c_int Indigo._lib.indigoAddProduct.argtypes = [c_int, c_int] Indigo._lib.indigoAddCatalyst.restype = c_int Indigo._lib.indigoAddCatalyst.argtypes = [c_int, c_int] Indigo._lib.indigoCountReactants.restype = c_int Indigo._lib.indigoCountReactants.argtypes = [c_int] Indigo._lib.indigoCountProducts.restype = c_int Indigo._lib.indigoCountProducts.argtypes = [c_int] Indigo._lib.indigoCountCatalysts.restype = c_int Indigo._lib.indigoCountCatalysts.argtypes = [c_int] Indigo._lib.indigoCountMolecules.restype = c_int Indigo._lib.indigoCountMolecules.argtypes = [c_int] Indigo._lib.indigoGetMolecule.restype = c_int Indigo._lib.indigoGetMolecule.argtypes = [c_int, c_int] Indigo._lib.indigoIterateReactants.restype = c_int Indigo._lib.indigoIterateReactants.argtypes = [c_int] Indigo._lib.indigoIterateProducts.restype = c_int Indigo._lib.indigoIterateProducts.argtypes = [c_int] Indigo._lib.indigoIterateCatalysts.restype = c_int Indigo._lib.indigoIterateCatalysts.argtypes = [c_int] Indigo._lib.indigoIterateMolecules.restype = c_int Indigo._lib.indigoIterateMolecules.argtypes = [c_int] Indigo._lib.indigoSaveRxnfileToFile.restype = c_int Indigo._lib.indigoSaveRxnfileToFile.argtypes = [c_int, c_char_p] Indigo._lib.indigoRxnfile.restype = c_char_p Indigo._lib.indigoRxnfile.argtypes = [c_int] Indigo._lib.indigoOptimize.restype = c_int Indigo._lib.indigoOptimize.argtypes = [c_int, c_char_p] Indigo._lib.indigoNormalize.restype = c_int Indigo._lib.indigoNormalize.argtypes = [c_int, c_char_p] Indigo._lib.indigoStandardize.restype = c_int Indigo._lib.indigoStandardize.argtypes = [c_int] Indigo._lib.indigoIonize.restype = c_int Indigo._lib.indigoIonize.argtypes = [c_int, c_float, c_float] Indigo._lib.indigoBuildPkaModel.restype = c_int Indigo._lib.indigoBuildPkaModel.argtypes = [c_int, c_float, c_char_p] Indigo._lib.indigoGetAcidPkaValue.restype = POINTER(c_float) Indigo._lib.indigoGetAcidPkaValue.argtypes = [c_int, c_int, c_int, c_int] Indigo._lib.indigoGetBasicPkaValue.restype = POINTER(c_float) Indigo._lib.indigoGetBasicPkaValue.argtypes = [c_int, c_int, c_int, c_int] Indigo._lib.indigoAutomap.restype = c_int Indigo._lib.indigoAutomap.argtypes = [c_int, c_char_p] Indigo._lib.indigoGetAtomMappingNumber.restype = c_int Indigo._lib.indigoGetAtomMappingNumber.argtypes = [c_int, c_int] Indigo._lib.indigoSetAtomMappingNumber.restype = c_int Indigo._lib.indigoSetAtomMappingNumber.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoGetReactingCenter.restype = c_int Indigo._lib.indigoGetReactingCenter.argtypes = [c_int, c_int, POINTER(c_int)] Indigo._lib.indigoSetReactingCenter.restype = c_int Indigo._lib.indigoSetReactingCenter.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoClearAAM.restype = c_int Indigo._lib.indigoClearAAM.argtypes = [c_int] Indigo._lib.indigoCorrectReactingCenters.restype = c_int Indigo._lib.indigoCorrectReactingCenters.argtypes = [c_int] Indigo._lib.indigoIterateAtoms.restype = c_int Indigo._lib.indigoIterateAtoms.argtypes = [c_int] Indigo._lib.indigoIteratePseudoatoms.restype = c_int Indigo._lib.indigoIteratePseudoatoms.argtypes = [c_int] Indigo._lib.indigoIterateRSites.restype = c_int Indigo._lib.indigoIterateRSites.argtypes = [c_int] Indigo._lib.indigoIterateStereocenters.restype = c_int Indigo._lib.indigoIterateStereocenters.argtypes = [c_int] Indigo._lib.indigoIterateAlleneCenters.restype = c_int Indigo._lib.indigoIterateAlleneCenters.argtypes = [c_int] Indigo._lib.indigoIterateRGroups.restype = c_int Indigo._lib.indigoIterateRGroups.argtypes = [c_int] Indigo._lib.indigoIsPseudoatom.restype = c_int Indigo._lib.indigoIsPseudoatom.argtypes = [c_int] Indigo._lib.indigoIsRSite.restype = c_int Indigo._lib.indigoIsRSite.argtypes = [c_int] Indigo._lib.indigoStereocenterType.restype = c_int Indigo._lib.indigoStereocenterType.argtypes = [c_int] Indigo._lib.indigoStereocenterGroup.restype = c_int Indigo._lib.indigoStereocenterGroup.argtypes = [c_int] Indigo._lib.indigoSetStereocenterGroup.restype = c_int Indigo._lib.indigoSetStereocenterGroup.argtypes = [c_int, c_int] Indigo._lib.indigoChangeStereocenterType.restype = c_int Indigo._lib.indigoChangeStereocenterType.argtypes = [c_int, c_int] Indigo._lib.indigoValidateChirality.restype = c_int Indigo._lib.indigoValidateChirality.argtypes = [c_int] Indigo._lib.indigoSingleAllowedRGroup.restype = c_int Indigo._lib.indigoSingleAllowedRGroup.argtypes = [c_int] Indigo._lib.indigoAddStereocenter.restype = c_int Indigo._lib.indigoAddStereocenter.argtypes = [c_int, c_int, c_int, c_int, c_int, c_int] Indigo._lib.indigoIterateRGroupFragments.restype = c_int Indigo._lib.indigoIterateRGroupFragments.argtypes = [c_int] Indigo._lib.indigoCountAttachmentPoints.restype = c_int Indigo._lib.indigoCountAttachmentPoints.argtypes = [c_int] Indigo._lib.indigoIterateAttachmentPoints.restype = c_int Indigo._lib.indigoIterateAttachmentPoints.argtypes = [c_int, c_int] Indigo._lib.indigoSymbol.restype = c_char_p Indigo._lib.indigoSymbol.argtypes = [c_int] Indigo._lib.indigoDegree.restype = c_int Indigo._lib.indigoDegree.argtypes = [c_int] Indigo._lib.indigoGetCharge.restype = c_int Indigo._lib.indigoGetCharge.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoGetExplicitValence.restype = c_int Indigo._lib.indigoGetExplicitValence.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoSetExplicitValence.restype = c_int Indigo._lib.indigoSetExplicitValence.argtypes = [c_int, c_int] Indigo._lib.indigoGetRadicalElectrons.restype = c_int Indigo._lib.indigoGetRadicalElectrons.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoGetRadical.restype = c_int Indigo._lib.indigoGetRadical.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoSetRadical.restype = c_int Indigo._lib.indigoSetRadical.argtypes = [c_int, c_int] Indigo._lib.indigoAtomicNumber.restype = c_int Indigo._lib.indigoAtomicNumber.argtypes = [c_int] Indigo._lib.indigoIsotope.restype = c_int Indigo._lib.indigoIsotope.argtypes = [c_int] Indigo._lib.indigoValence.restype = c_int Indigo._lib.indigoValence.argtypes = [c_int] Indigo._lib.indigoCountHydrogens.restype = c_int Indigo._lib.indigoCountHydrogens.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoCountImplicitHydrogens.restype = c_int Indigo._lib.indigoCountImplicitHydrogens.argtypes = [c_int] Indigo._lib.indigoXYZ.restype = POINTER(c_float) Indigo._lib.indigoXYZ.argtypes = [c_int] Indigo._lib.indigoSetXYZ.restype = c_int Indigo._lib.indigoSetXYZ.argtypes = [c_int, c_float, c_float, c_float] Indigo._lib.indigoCountSuperatoms.restype = c_int Indigo._lib.indigoCountSuperatoms.argtypes = [c_int] Indigo._lib.indigoCountDataSGroups.restype = c_int Indigo._lib.indigoCountDataSGroups.argtypes = [c_int] Indigo._lib.indigoCountRepeatingUnits.restype = c_int Indigo._lib.indigoCountRepeatingUnits.argtypes = [c_int] Indigo._lib.indigoCountMultipleGroups.restype = c_int Indigo._lib.indigoCountMultipleGroups.argtypes = [c_int] Indigo._lib.indigoCountGenericSGroups.restype = c_int Indigo._lib.indigoCountGenericSGroups.argtypes = [c_int] Indigo._lib.indigoIterateDataSGroups.restype = c_int Indigo._lib.indigoIterateDataSGroups.argtypes = [c_int] Indigo._lib.indigoIterateSuperatoms.restype = c_int Indigo._lib.indigoIterateSuperatoms.argtypes = [c_int] Indigo._lib.indigoIterateGenericSGroups.restype = c_int Indigo._lib.indigoIterateGenericSGroups.argtypes = [c_int] Indigo._lib.indigoIterateRepeatingUnits.restype = c_int Indigo._lib.indigoIterateRepeatingUnits.argtypes = [c_int] Indigo._lib.indigoIterateMultipleGroups.restype = c_int Indigo._lib.indigoIterateMultipleGroups.argtypes = [c_int] Indigo._lib.indigoGetSuperatom.restype = c_int Indigo._lib.indigoGetSuperatom.argtypes = [c_int, c_int] Indigo._lib.indigoGetDataSGroup.restype = c_int Indigo._lib.indigoGetDataSGroup.argtypes = [c_int, c_int] Indigo._lib.indigoGetGenericSGroup.restype = c_int Indigo._lib.indigoGetGenericSGroup.argtypes = [c_int, c_int] Indigo._lib.indigoGetMultipleGroup.restype = c_int Indigo._lib.indigoGetMultipleGroup.argtypes = [c_int, c_int] Indigo._lib.indigoGetRepeatingUnit.restype = c_int Indigo._lib.indigoGetRepeatingUnit.argtypes = [c_int, c_int] Indigo._lib.indigoDescription.restype = c_char_p Indigo._lib.indigoDescription.argtypes = [c_int] Indigo._lib.indigoData.restype = c_char_p Indigo._lib.indigoData.argtypes = [c_int] Indigo._lib.indigoAddDataSGroup.restype = c_int Indigo._lib.indigoAddDataSGroup.argtypes = [c_int, c_int, POINTER(c_int), c_int, POINTER(c_int), c_char_p, c_char_p] Indigo._lib.indigoAddSuperatom.restype = c_int Indigo._lib.indigoAddSuperatom.argtypes = [c_int, c_int, POINTER(c_int), c_char_p] Indigo._lib.indigoSetDataSGroupXY.restype = c_int Indigo._lib.indigoSetDataSGroupXY.argtypes = [c_int, c_float, c_float, c_char_p] Indigo._lib.indigoSetSGroupData.restype = c_int Indigo._lib.indigoSetSGroupData.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupCoords.restype = c_int Indigo._lib.indigoSetSGroupCoords.argtypes = [c_int, c_float, c_float] Indigo._lib.indigoSetSGroupDescription.restype = c_int Indigo._lib.indigoSetSGroupDescription.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupFieldName.restype = c_int Indigo._lib.indigoSetSGroupFieldName.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupQueryCode.restype = c_int Indigo._lib.indigoSetSGroupQueryCode.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupQueryOper.restype = c_int Indigo._lib.indigoSetSGroupQueryOper.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupDisplay.restype = c_int Indigo._lib.indigoSetSGroupDisplay.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupLocation.restype = c_int Indigo._lib.indigoSetSGroupLocation.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupTag.restype = c_int Indigo._lib.indigoSetSGroupTag.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupTagAlign.restype = c_int Indigo._lib.indigoSetSGroupTagAlign.argtypes = [c_int, c_int] Indigo._lib.indigoSetSGroupDataType.restype = c_int Indigo._lib.indigoSetSGroupDataType.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupXCoord.restype = c_int Indigo._lib.indigoSetSGroupXCoord.argtypes = [c_int, c_float] Indigo._lib.indigoSetSGroupYCoord.restype = c_int Indigo._lib.indigoSetSGroupYCoord.argtypes = [c_int, c_float] Indigo._lib.indigoCreateSGroup.restype = c_int Indigo._lib.indigoCreateSGroup.argtypes = [c_char_p, c_int, c_char_p] Indigo._lib.indigoSetSGroupClass.restype = c_int Indigo._lib.indigoSetSGroupClass.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetSGroupName.restype = c_int Indigo._lib.indigoSetSGroupName.argtypes = [c_int, c_char_p] Indigo._lib.indigoGetSGroupClass.restype = c_char_p Indigo._lib.indigoGetSGroupClass.argtypes = [c_int] Indigo._lib.indigoGetSGroupName.restype = c_char_p Indigo._lib.indigoGetSGroupName.argtypes = [c_int] Indigo._lib.indigoGetSGroupNumCrossBonds.restype = c_int Indigo._lib.indigoGetSGroupNumCrossBonds.argtypes = [c_int] Indigo._lib.indigoAddSGroupAttachmentPoint.restype = c_int Indigo._lib.indigoAddSGroupAttachmentPoint.argtypes = [c_int, c_int, c_int, c_char_p] Indigo._lib.indigoDeleteSGroupAttachmentPoint.restype = c_int Indigo._lib.indigoDeleteSGroupAttachmentPoint.argtypes = [c_int, c_int] Indigo._lib.indigoGetSGroupDisplayOption.restype = c_int Indigo._lib.indigoGetSGroupDisplayOption.argtypes = [c_int] Indigo._lib.indigoSetSGroupDisplayOption.restype = c_int Indigo._lib.indigoSetSGroupDisplayOption.argtypes = [c_int, c_int] Indigo._lib.indigoGetSGroupMultiplier.restype = c_int Indigo._lib.indigoGetSGroupMultiplier.argtypes = [c_int] Indigo._lib.indigoSetSGroupMultiplier.restype = c_int Indigo._lib.indigoSetSGroupMultiplier.argtypes = [c_int, c_int] Indigo._lib.indigoSetSGroupBrackets.restype = c_int Indigo._lib.indigoSetSGroupBrackets.argtypes = [c_int, c_int, c_float, c_float, c_float, c_float, c_float, c_float, c_float, c_float] Indigo._lib.indigoFindSGroups.restype = c_int Indigo._lib.indigoFindSGroups.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoGetSGroupType.restype = c_int Indigo._lib.indigoGetSGroupType.argtypes = [c_int] Indigo._lib.indigoGetSGroupIndex.restype = c_int Indigo._lib.indigoGetSGroupIndex.argtypes = [c_int] Indigo._lib.indigoTransformSCSRtoCTAB.restype = c_int Indigo._lib.indigoTransformSCSRtoCTAB.argtypes = [c_int] Indigo._lib.indigoTransformCTABtoSCSR.restype = c_int Indigo._lib.indigoTransformCTABtoSCSR.argtypes = [c_int, c_int] Indigo._lib.indigoResetCharge.restype = c_int Indigo._lib.indigoResetCharge.argtypes = [c_int] Indigo._lib.indigoResetExplicitValence.restype = c_int Indigo._lib.indigoResetExplicitValence.argtypes = [c_int] Indigo._lib.indigoResetRadical.restype = c_int Indigo._lib.indigoResetRadical.argtypes = [c_int] Indigo._lib.indigoResetIsotope.restype = c_int Indigo._lib.indigoResetIsotope.argtypes = [c_int] Indigo._lib.indigoSetAttachmentPoint.restype = c_int Indigo._lib.indigoSetAttachmentPoint.argtypes = [c_int, c_int] Indigo._lib.indigoClearAttachmentPoints.restype = c_int Indigo._lib.indigoClearAttachmentPoints.argtypes = [c_int] Indigo._lib.indigoRemoveConstraints.restype = c_int Indigo._lib.indigoRemoveConstraints.argtypes = [c_int, c_char_p] Indigo._lib.indigoAddConstraint.restype = c_int Indigo._lib.indigoAddConstraint.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoAddConstraintNot.restype = c_int Indigo._lib.indigoAddConstraintNot.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoAddConstraintOr.restype = c_int Indigo._lib.indigoAddConstraintOr.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoResetStereo.restype = c_int Indigo._lib.indigoResetStereo.argtypes = [c_int] Indigo._lib.indigoInvertStereo.restype = c_int Indigo._lib.indigoInvertStereo.argtypes = [c_int] Indigo._lib.indigoCountAtoms.restype = c_int Indigo._lib.indigoCountAtoms.argtypes = [c_int] Indigo._lib.indigoCountBonds.restype = c_int Indigo._lib.indigoCountBonds.argtypes = [c_int] Indigo._lib.indigoCountPseudoatoms.restype = c_int Indigo._lib.indigoCountPseudoatoms.argtypes = [c_int] Indigo._lib.indigoCountRSites.restype = c_int Indigo._lib.indigoCountRSites.argtypes = [c_int] Indigo._lib.indigoIterateBonds.restype = c_int Indigo._lib.indigoIterateBonds.argtypes = [c_int] Indigo._lib.indigoBondOrder.restype = c_int Indigo._lib.indigoBondOrder.argtypes = [c_int] Indigo._lib.indigoBondStereo.restype = c_int Indigo._lib.indigoBondStereo.argtypes = [c_int] Indigo._lib.indigoTopology.restype = c_int Indigo._lib.indigoTopology.argtypes = [c_int] Indigo._lib.indigoIterateNeighbors.restype = c_int Indigo._lib.indigoIterateNeighbors.argtypes = [c_int] Indigo._lib.indigoBond.restype = c_int Indigo._lib.indigoBond.argtypes = [c_int] Indigo._lib.indigoGetAtom.restype = c_int Indigo._lib.indigoGetAtom.argtypes = [c_int, c_int] Indigo._lib.indigoGetBond.restype = c_int Indigo._lib.indigoGetBond.argtypes = [c_int, c_int] Indigo._lib.indigoSource.restype = c_int Indigo._lib.indigoSource.argtypes = [c_int] Indigo._lib.indigoDestination.restype = c_int Indigo._lib.indigoDestination.argtypes = [c_int] Indigo._lib.indigoClearCisTrans.restype = c_int Indigo._lib.indigoClearCisTrans.argtypes = [c_int] Indigo._lib.indigoClearStereocenters.restype = c_int Indigo._lib.indigoClearStereocenters.argtypes = [c_int] Indigo._lib.indigoCountStereocenters.restype = c_int Indigo._lib.indigoCountStereocenters.argtypes = [c_int] Indigo._lib.indigoClearAlleneCenters.restype = c_int Indigo._lib.indigoClearAlleneCenters.argtypes = [c_int] Indigo._lib.indigoCountAlleneCenters.restype = c_int Indigo._lib.indigoCountAlleneCenters.argtypes = [c_int] Indigo._lib.indigoResetSymmetricCisTrans.restype = c_int Indigo._lib.indigoResetSymmetricCisTrans.argtypes = [c_int] Indigo._lib.indigoResetSymmetricStereocenters.restype = c_int Indigo._lib.indigoResetSymmetricStereocenters.argtypes = [c_int] Indigo._lib.indigoMarkEitherCisTrans.restype = c_int Indigo._lib.indigoMarkEitherCisTrans.argtypes = [c_int] Indigo._lib.indigoMarkStereobonds.restype = c_int Indigo._lib.indigoMarkStereobonds.argtypes = [c_int] Indigo._lib.indigoAddAtom.restype = c_int Indigo._lib.indigoAddAtom.argtypes = [c_int, c_char_p] Indigo._lib.indigoResetAtom.restype = c_int Indigo._lib.indigoResetAtom.argtypes = [c_int, c_char_p] Indigo._lib.indigoAddRSite.restype = c_int Indigo._lib.indigoAddRSite.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetRSite.restype = c_int Indigo._lib.indigoSetRSite.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetCharge.restype = c_int Indigo._lib.indigoSetCharge.argtypes = [c_int, c_int] Indigo._lib.indigoSetIsotope.restype = c_int Indigo._lib.indigoSetIsotope.argtypes = [c_int, c_int] Indigo._lib.indigoSetImplicitHCount.restype = c_int Indigo._lib.indigoSetImplicitHCount.argtypes = [c_int, c_int] Indigo._lib.indigoAddBond.restype = c_int Indigo._lib.indigoAddBond.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoSetBondOrder.restype = c_int Indigo._lib.indigoSetBondOrder.argtypes = [c_int, c_int] Indigo._lib.indigoMerge.restype = c_int Indigo._lib.indigoMerge.argtypes = [c_int, c_int] Indigo._lib.indigoHighlight.restype = c_int Indigo._lib.indigoHighlight.argtypes = [c_int] Indigo._lib.indigoUnhighlight.restype = c_int Indigo._lib.indigoUnhighlight.argtypes = [c_int] Indigo._lib.indigoIsHighlighted.restype = c_int Indigo._lib.indigoIsHighlighted.argtypes = [c_int] Indigo._lib.indigoCountComponents.restype = c_int Indigo._lib.indigoCountComponents.argtypes = [c_int] Indigo._lib.indigoComponentIndex.restype = c_int Indigo._lib.indigoComponentIndex.argtypes = [c_int] Indigo._lib.indigoIterateComponents.restype = c_int Indigo._lib.indigoIterateComponents.argtypes = [c_int] Indigo._lib.indigoComponent.restype = c_int Indigo._lib.indigoComponent.argtypes = [c_int, c_int] Indigo._lib.indigoCountSSSR.restype = c_int Indigo._lib.indigoCountSSSR.argtypes = [c_int] Indigo._lib.indigoIterateSSSR.restype = c_int Indigo._lib.indigoIterateSSSR.argtypes = [c_int] Indigo._lib.indigoIterateSubtrees.restype = c_int Indigo._lib.indigoIterateSubtrees.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoIterateRings.restype = c_int Indigo._lib.indigoIterateRings.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoIterateEdgeSubmolecules.restype = c_int Indigo._lib.indigoIterateEdgeSubmolecules.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoCountHeavyAtoms.restype = c_int Indigo._lib.indigoCountHeavyAtoms.argtypes = [c_int] Indigo._lib.indigoGrossFormula.restype = c_int Indigo._lib.indigoGrossFormula.argtypes = [c_int] Indigo._lib.indigoMolecularWeight.restype = c_float Indigo._lib.indigoMolecularWeight.argtypes = [c_int] Indigo._lib.indigoMostAbundantMass.restype = c_float Indigo._lib.indigoMostAbundantMass.argtypes = [c_int] Indigo._lib.indigoMonoisotopicMass.restype = c_float Indigo._lib.indigoMonoisotopicMass.argtypes = [c_int] Indigo._lib.indigoCanonicalSmiles.restype = c_char_p Indigo._lib.indigoCanonicalSmiles.argtypes = [c_int] Indigo._lib.indigoLayeredCode.restype = c_char_p Indigo._lib.indigoLayeredCode.argtypes = [c_int] Indigo._lib.indigoSymmetryClasses.restype = POINTER(c_int) Indigo._lib.indigoSymmetryClasses.argtypes = [c_int, POINTER(c_int)] Indigo._lib.indigoHasCoord.restype = c_int Indigo._lib.indigoHasCoord.argtypes = [c_int] Indigo._lib.indigoHasZCoord.restype = c_int Indigo._lib.indigoHasZCoord.argtypes = [c_int] Indigo._lib.indigoIsChiral.restype = c_int Indigo._lib.indigoIsChiral.argtypes = [c_int] Indigo._lib.indigoIsPossibleFischerProjection.restype = c_int Indigo._lib.indigoIsPossibleFischerProjection.argtypes = [c_int, c_char_p] Indigo._lib.indigoCreateSubmolecule.restype = c_int Indigo._lib.indigoCreateSubmolecule.argtypes = [c_int, c_int, POINTER(c_int)] Indigo._lib.indigoCreateEdgeSubmolecule.restype = c_int Indigo._lib.indigoCreateEdgeSubmolecule.argtypes = [c_int, c_int, POINTER(c_int), c_int, POINTER(c_int)] Indigo._lib.indigoGetSubmolecule.restype = c_int Indigo._lib.indigoGetSubmolecule.argtypes = [c_int, c_int, POINTER(c_int)] Indigo._lib.indigoRemoveAtoms.restype = c_int Indigo._lib.indigoRemoveAtoms.argtypes = [c_int, c_int, POINTER(c_int)] Indigo._lib.indigoRemoveBonds.restype = c_int Indigo._lib.indigoRemoveBonds.argtypes = [c_int, c_int, POINTER(c_int)] Indigo._lib.indigoAlignAtoms.restype = c_float Indigo._lib.indigoAlignAtoms.argtypes = [c_int, c_int, POINTER(c_int), POINTER(c_float)] Indigo._lib.indigoAromatize.restype = c_int Indigo._lib.indigoAromatize.argtypes = [c_int] Indigo._lib.indigoDearomatize.restype = c_int Indigo._lib.indigoDearomatize.argtypes = [c_int] Indigo._lib.indigoFoldHydrogens.restype = c_int Indigo._lib.indigoFoldHydrogens.argtypes = [c_int] Indigo._lib.indigoUnfoldHydrogens.restype = c_int Indigo._lib.indigoUnfoldHydrogens.argtypes = [c_int] Indigo._lib.indigoLayout.restype = c_int Indigo._lib.indigoLayout.argtypes = [c_int] Indigo._lib.indigoSmiles.restype = c_char_p Indigo._lib.indigoSmiles.argtypes = [c_int] Indigo._lib.indigoName.restype = c_char_p Indigo._lib.indigoName.argtypes = [c_int] Indigo._lib.indigoSetName.restype = c_int Indigo._lib.indigoSetName.argtypes = [c_int, c_char_p] Indigo._lib.indigoSerialize.restype = c_int Indigo._lib.indigoSerialize.argtypes = [c_int, POINTER(POINTER(c_byte)), POINTER(c_int)] Indigo._lib.indigoHasProperty.restype = c_int Indigo._lib.indigoHasProperty.argtypes = [c_int, c_char_p] Indigo._lib.indigoGetProperty.restype = c_char_p Indigo._lib.indigoGetProperty.argtypes = [c_int, c_char_p] Indigo._lib.indigoSetProperty.restype = c_int Indigo._lib.indigoSetProperty.argtypes = [c_int, c_char_p, c_char_p] Indigo._lib.indigoRemoveProperty.restype = c_int Indigo._lib.indigoRemoveProperty.argtypes = [c_int, c_char_p] Indigo._lib.indigoIterateProperties.restype = c_int Indigo._lib.indigoIterateProperties.argtypes = [c_int] Indigo._lib.indigoClearProperties.restype = c_int Indigo._lib.indigoClearProperties.argtypes = [c_int] Indigo._lib.indigoCheckBadValence.restype = c_char_p Indigo._lib.indigoCheckBadValence.argtypes = [c_int] Indigo._lib.indigoCheckAmbiguousH.restype = c_char_p Indigo._lib.indigoCheckAmbiguousH.argtypes = [c_int] Indigo._lib.indigoFingerprint.restype = c_int Indigo._lib.indigoFingerprint.argtypes = [c_int, c_char_p] Indigo._lib.indigoCountBits.restype = c_int Indigo._lib.indigoCountBits.argtypes = [c_int] Indigo._lib.indigoRawData.restype = c_char_p Indigo._lib.indigoRawData.argtypes = [c_int] Indigo._lib.indigoTell.restype = c_int Indigo._lib.indigoTell.argtypes = [c_int] Indigo._lib.indigoSdfAppend.restype = c_int Indigo._lib.indigoSdfAppend.argtypes = [c_int, c_int] Indigo._lib.indigoSmilesAppend.restype = c_int Indigo._lib.indigoSmilesAppend.argtypes = [c_int, c_int] Indigo._lib.indigoRdfHeader.restype = c_int Indigo._lib.indigoRdfHeader.argtypes = [c_int] Indigo._lib.indigoRdfAppend.restype = c_int Indigo._lib.indigoRdfAppend.argtypes = [c_int, c_int] Indigo._lib.indigoCmlHeader.restype = c_int Indigo._lib.indigoCmlHeader.argtypes = [c_int] Indigo._lib.indigoCmlAppend.restype = c_int Indigo._lib.indigoCmlAppend.argtypes = [c_int, c_int] Indigo._lib.indigoCmlFooter.restype = c_int Indigo._lib.indigoCmlFooter.argtypes = [c_int] Indigo._lib.indigoAppend.restype = c_int Indigo._lib.indigoAppend.argtypes = [c_int, c_int] Indigo._lib.indigoArrayAdd.restype = c_int Indigo._lib.indigoArrayAdd.argtypes = [c_int, c_int] Indigo._lib.indigoAt.restype = c_int Indigo._lib.indigoAt.argtypes = [c_int, c_int] Indigo._lib.indigoCount.restype = c_int Indigo._lib.indigoCount.argtypes = [c_int] Indigo._lib.indigoClear.restype = c_int Indigo._lib.indigoClear.argtypes = [c_int] Indigo._lib.indigoIterateArray.restype = c_int Indigo._lib.indigoIterateArray.argtypes = [c_int] Indigo._lib.indigoIgnoreAtom.restype = c_int Indigo._lib.indigoIgnoreAtom.argtypes = [c_int, c_int] Indigo._lib.indigoUnignoreAtom.restype = c_int Indigo._lib.indigoUnignoreAtom.argtypes = [c_int, c_int] Indigo._lib.indigoUnignoreAllAtoms.restype = c_int Indigo._lib.indigoUnignoreAllAtoms.argtypes = [c_int] Indigo._lib.indigoMatch.restype = c_int Indigo._lib.indigoMatch.argtypes = [c_int, c_int] Indigo._lib.indigoCountMatches.restype = c_int Indigo._lib.indigoCountMatches.argtypes = [c_int, c_int] Indigo._lib.indigoCountMatchesWithLimit.restype = c_int Indigo._lib.indigoCountMatchesWithLimit.argtypes = [c_int, c_int, c_int] Indigo._lib.indigoIterateMatches.restype = c_int Indigo._lib.indigoIterateMatches.argtypes = [c_int, c_int] Indigo._lib.indigoHighlightedTarget.restype = c_int Indigo._lib.indigoHighlightedTarget.argtypes = [c_int] Indigo._lib.indigoMapAtom.restype = c_int Indigo._lib.indigoMapAtom.argtypes = [c_int, c_int] Indigo._lib.indigoMapBond.restype = c_int Indigo._lib.indigoMapBond.argtypes = [c_int, c_int] Indigo._lib.indigoMapMolecule.restype = c_int Indigo._lib.indigoMapMolecule.argtypes = [c_int, c_int] Indigo._lib.indigoIterateTautomers.restype = c_int Indigo._lib.indigoIterateTautomers.argtypes = [c_int, c_char_p] Indigo._lib.indigoAllScaffolds.restype = c_int Indigo._lib.indigoAllScaffolds.argtypes = [c_int] Indigo._lib.indigoDecomposedMoleculeScaffold.restype = c_int Indigo._lib.indigoDecomposedMoleculeScaffold.argtypes = [c_int] Indigo._lib.indigoIterateDecomposedMolecules.restype = c_int Indigo._lib.indigoIterateDecomposedMolecules.argtypes = [c_int] Indigo._lib.indigoDecomposedMoleculeHighlighted.restype = c_int Indigo._lib.indigoDecomposedMoleculeHighlighted.argtypes = [c_int] Indigo._lib.indigoDecomposedMoleculeWithRGroups.restype = c_int Indigo._lib.indigoDecomposedMoleculeWithRGroups.argtypes = [c_int] Indigo._lib.indigoDecomposeMolecule.restype = c_int Indigo._lib.indigoDecomposeMolecule.argtypes = [c_int, c_int] Indigo._lib.indigoIterateDecompositions.restype = c_int Indigo._lib.indigoIterateDecompositions.argtypes = [c_int] Indigo._lib.indigoAddDecomposition.restype = c_int Indigo._lib.indigoAddDecomposition.argtypes = [c_int, c_int] Indigo._lib.indigoToString.restype = c_char_p Indigo._lib.indigoToString.argtypes = [c_int] Indigo._lib.indigoOneBitsList.restype = c_char_p Indigo._lib.indigoOneBitsList.argtypes = [c_int] Indigo._lib.indigoToBuffer.restype = c_int Indigo._lib.indigoToBuffer.argtypes = [c_int, POINTER(POINTER(c_byte)), POINTER(c_int)] Indigo._lib.indigoStereocenterPyramid.restype = POINTER(c_int) Indigo._lib.indigoStereocenterPyramid.argtypes = [c_int] Indigo._lib.indigoExpandAbbreviations.restype = c_int Indigo._lib.indigoExpandAbbreviations.argtypes = [c_int] Indigo._lib.indigoDbgInternalType.restype = c_char_p Indigo._lib.indigoDbgInternalType.argtypes = [c_int] def __del__ (self): if hasattr(self, '_lib'): self._lib.indigoReleaseSessionId(self._sid) def writeBuffer (self): self._setSessionId() id = self._checkResult(Indigo._lib.indigoWriteBuffer()) return self.IndigoObject(self, id) def writeFile (self, filename): self._setSessionId() id = self._checkResult(Indigo._lib.indigoWriteFile(filename.encode(ENCODE_ENCODING))) return self.IndigoObject(self, id) def unserialize(self, arr): self._setSessionId() values = (c_byte * len(arr))() for i in range(len(arr)): values[i] = arr[i] res = Indigo._lib.indigoUnserialize(values, len(arr)) return self.IndigoObject(self, self._checkResult(res)) def setOption (self, option, value1, value2=None, value3=None): self._setSessionId() if (type(value1).__name__ == 'str' or type(value1).__name__ == 'unicode') and value2 is None and value3 is None: self._checkResult(Indigo._lib.indigoSetOption(option.encode(ENCODE_ENCODING), value1.encode(ENCODE_ENCODING))) elif type(value1).__name__ == 'int' and value2 is None and value3 is None: self._checkResult(Indigo._lib.indigoSetOptionInt(option.encode(ENCODE_ENCODING), value1)) elif type(value1).__name__ == 'float' and value2 is None and value3 is None: self._checkResult(Indigo._lib.indigoSetOptionFloat(option.encode(ENCODE_ENCODING), value1)) elif type(value1).__name__ == 'bool' and value2 is None and value3 is None: value1_b = 0 if value1: value1_b = 1 self._checkResult(Indigo._lib.indigoSetOptionBool(option.encode(ENCODE_ENCODING), value1_b)) elif type(value1).__name__ == 'int' and value2 and \ type(value2).__name__ == 'int' and value3 is None: self._checkResult(Indigo._lib.indigoSetOptionXY(option.encode(ENCODE_ENCODING), value1, value2)) elif type(value1).__name__ == 'float' and value2 and \ type(value2).__name__ == 'float' and value3 and \ type(value3).__name__ == 'float': self._checkResult(Indigo._lib.indigoSetOptionColor(option.encode(ENCODE_ENCODING), value1, value2, value3)) else: raise IndigoException("bad option") def _checkResult (self, result): if result < 0: raise IndigoException(Indigo._lib.indigoGetLastError()) return result def _checkResultFloat (self, result): if result < -0.5: raise IndigoException(Indigo._lib.indigoGetLastError()) return result def _checkResultPtr (self, result): if result is None: raise IndigoException(Indigo._lib.indigoGetLastError()) return result def _checkResultString (self, result): if sys.version_info >= (3, 0): return self._checkResultPtr(result).decode(DECODE_ENCODING) else: return self._checkResultPtr(result).encode(ENCODE_ENCODING) def convertToArray (self, iteratable): if isinstance(iteratable, IndigoObject): return iteratable try: some_object_iterator = iter(iteratable) res = self.createArray() for obj in some_object_iterator: res.arrayAdd(self.convertToArray(obj)) return res except TypeError: raise IndigoException("Cannot convert object %s to an array" % (iteratable)) def dbgBreakpoint(self): self._setSessionId() return Indigo._lib.indigoDbgBreakpoint() def version(self): self._setSessionId() return self._checkResultString(Indigo._lib.indigoVersion()) def countReferences(self): self._setSessionId() return self._checkResult(Indigo._lib.indigoCountReferences()) def writeFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoWriteFile(filename.encode(ENCODE_ENCODING)))) def writeBuffer(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoWriteBuffer())) def createMolecule(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateMolecule())) def createQueryMolecule(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateQueryMolecule())) def loadMolecule(self, string): self._setSessionId() #sys.__stdout__.write(string) return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadMoleculeFromString(string.encode(ENCODE_ENCODING)))) def loadMoleculeFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadMoleculeFromFile(filename.encode(ENCODE_ENCODING)))) def loadQueryMolecule(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadQueryMoleculeFromString(string.encode(ENCODE_ENCODING)))) def loadQueryMoleculeFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadQueryMoleculeFromFile(filename.encode(ENCODE_ENCODING)))) def loadSmarts(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadSmartsFromString(string.encode(ENCODE_ENCODING)))) def loadSmartsFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadSmartsFromFile(filename.encode(ENCODE_ENCODING)))) def loadReaction(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadReactionFromString(string.encode(ENCODE_ENCODING)))) def loadReactionFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadReactionFromFile(filename.encode(ENCODE_ENCODING)))) def loadQueryReaction(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadQueryReactionFromString(string.encode(ENCODE_ENCODING)))) def loadQueryReactionFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadQueryReactionFromFile(filename.encode(ENCODE_ENCODING)))) def loadReactionSmarts(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadReactionSmartsFromString(string.encode(ENCODE_ENCODING)))) def loadReactionSmartsFromFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadReactionSmartsFromFile(filename.encode(ENCODE_ENCODING)))) def createReaction(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateReaction())) def createQueryReaction(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateQueryReaction())) def exactMatch(self, item1, item2, flags=''): self._setSessionId() if flags is None: flags = '' newobj = self._checkResult(Indigo._lib.indigoExactMatch(item1.id, item2.id, flags.encode(ENCODE_ENCODING))) if newobj == 0: return None else: return self.IndigoObject(self, newobj, [item1, item2, self]) def setTautomerRule(self, id, beg, end): self._setSessionId() return self._checkResult(Indigo._lib.indigoSetTautomerRule(id, beg.encode(ENCODE_ENCODING), end.encode(ENCODE_ENCODING))) def removeTautomerRule(self, id): self._setSessionId() return self._checkResult(Indigo._lib.indigoRemoveTautomerRule(id)) def clearTautomerRules(self): self._setSessionId() return self._checkResult(Indigo._lib.indigoClearTautomerRules()) def commonBits(self, fingerprint1, fingerprint2): self._setSessionId() return self._checkResult(Indigo._lib.indigoCommonBits(fingerprint1.id, fingerprint2.id)) def similarity(self, item1, item2, metrics=''): self._setSessionId() if metrics is None: metrics = '' return self._checkResultFloat(Indigo._lib.indigoSimilarity(item1.id, item2.id, metrics.encode(ENCODE_ENCODING))) def iterateSDFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateSDFile(filename.encode(ENCODE_ENCODING)))) def iterateRDFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateRDFile(filename.encode(ENCODE_ENCODING)))) def iterateSmilesFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateSmilesFile(filename.encode(ENCODE_ENCODING)))) def iterateCMLFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateCMLFile(filename.encode(ENCODE_ENCODING)))) def iterateCDXFile(self, filename): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateCDXFile(filename.encode(ENCODE_ENCODING)))) def createFileSaver(self, filename, format): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateFileSaver(filename.encode(ENCODE_ENCODING), format.encode(ENCODE_ENCODING)))) def createSaver(self, obj, format): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateSaver(obj.id, format.encode(ENCODE_ENCODING)))) def createArray(self): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateArray())) def substructureMatcher(self, target, mode=''): self._setSessionId() if mode is None: mode = '' return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoSubstructureMatcher(target.id, mode.encode(ENCODE_ENCODING))), target) def extractCommonScaffold(self, structures, options=''): self._setSessionId() structures = self.convertToArray(structures) if options is None: options = '' newobj = self._checkResult(Indigo._lib.indigoExtractCommonScaffold(structures.id, options.encode(ENCODE_ENCODING))) if newobj == 0: return None else: return self.IndigoObject(self, newobj, self) def decomposeMolecules(self, scaffold, structures): self._setSessionId() structures = self.convertToArray(structures) return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoDecomposeMolecules(scaffold.id, structures.id)), scaffold) def rgroupComposition(self, molecule, options=''): self._setSessionId() if options is None: options = '' newobj = self._checkResult(Indigo._lib.indigoRGroupComposition(molecule.id, options.encode(ENCODE_ENCODING))) if newobj == 0: return None else: return self.IndigoObject(self, newobj, self) def getFragmentedMolecule(self, elem, options=''): self._setSessionId() if options is None: options = '' newobj = self._checkResult(Indigo._lib.indigoGetFragmentedMolecule(elem.id, options.encode(ENCODE_ENCODING))) if newobj == 0: return None else: return self.IndigoObject(self, newobj, self) def createDecomposer(self, scaffold): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoCreateDecomposer(scaffold.id)), scaffold) def reactionProductEnumerate(self, replacedaction, monomers): self._setSessionId() monomers = self.convertToArray(monomers) return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoReactionProductEnumerate(replacedaction.id, monomers.id)), replacedaction) def transform(self, reaction, monomers): self._setSessionId() newobj = self._checkResult(Indigo._lib.indigoTransform(reaction.id, monomers.id)) if newobj == 0: return None else: return self.IndigoObject(self, newobj, self) def loadBuffer(self, buf): self._setSessionId() buf = list(buf) values = (c_byte * len(buf))() for i in range(len(buf)): values[i] = buf[i] return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadBuffer(values, len(buf)))) def loadString(self, string): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoLoadString(string.encode(ENCODE_ENCODING)))) def iterateSDF(self, reader): self._setSessionId() result = self._checkResult(Indigo._lib.indigoIterateSDF(reader.id)) if not result: return None return self.IndigoObject(self, result, reader) def iterateSmiles(self, reader): self._setSessionId() result = self._checkResult(Indigo._lib.indigoIterateSmiles(reader.id)) if not result: return None return self.IndigoObject(self, result, reader) def iterateCML(self, reader): self._setSessionId() result = self._checkResult(Indigo._lib.indigoIterateCML(reader.id)) if not result: return None return self.IndigoObject(self, result, reader) def iterateCDX(self, reader): self._setSessionId() result = self._checkResult(Indigo._lib.indigoIterateCDX(reader.id)) if not result: return None return self.IndigoObject(self, result, reader) def iterateRDF(self, reader): self._setSessionId() result = self._checkResult(Indigo._lib.indigoIterateRDF(reader.id)) if not result: return None return self.IndigoObject(self, result, reader) def iterateTautomers(self, molecule, params): self._setSessionId() return self.IndigoObject(self, self._checkResult(Indigo._lib.indigoIterateTautomers(molecule.id, params)), molecule) def buildPkaModel(self, level, threshold, filename): self._setSessionId() return self._checkResult(Indigo._lib.indigoBuildPkaModel(level, threshold, filename.encode(ENCODE_ENCODING))) Indigo-indigo-1.2.3/api/r/000077500000000000000000000000001271037650300152135ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/r/DESCRIPTION000066400000000000000000000007531271037650300167260ustar00rootroot00000000000000Package: rindigo Version: 0.2 Date: 2014-11-10 Title: R Package for Indigo Toolkit Authors@R: c(person(given = "Mikhail", family = "Kviatkovskii", role = c("cre"), email = "Mikhail_Kviatkovskii@epam.com")) Description: R Package for Indigo Toolkit NeedsCompile: Yes License: GPL-2 | GPL-3 URL: http://lifescience.opensource.epam.com/indigo/api Author: Mikhail Kviatkovskii [cre] Maintainer: Mikhail Kviatkovskii Packaged: 2014-12-18 15:26:37 UTC; mk Indigo-indigo-1.2.3/api/r/NAMESPACE000066400000000000000000000000541271037650300164310ustar00rootroot00000000000000exportPattern("^[^\\.]") useDynLib(rindigo) Indigo-indigo-1.2.3/api/r/R/000077500000000000000000000000001271037650300154145ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/r/R/indigo.R000066400000000000000000000114441271037650300170140ustar00rootroot00000000000000checkResult = function(result){ if (result < 0) { stop(.Call("r_indigoGetLastError")) } return(result) } checkResultFloat = function(result){ if (result < -0.5) { stop(.Call("r_indigoGetLastError")) } return(result) } checkResultPtr = function(result){ if (is.na(result)) { stop(.Call("r_indigoGetLastError")) } return(result) } Indigo = setRefClass( Class = "Indigo", fields = list(id = "numeric")) Indigo$methods( initialize = function(){ id<<-(.Call("r_indigoAllocSessionId")) }) Indigo$methods( setSession = function(){ .Call("r_indigoSetSessionId", id) }) Indigo$methods( version = function(){ setSession() return(checkResultPtr(.Call("r_indigoVersion"))); }) Indigo$methods( finalize = function(){ .Call("r_indigoReleaseSessionId", id) }) Indigo$methods( loadMolecule = function(data){ setSession() obj_id = checkResult(.Call("r_indigoLoadMolecule", data)) return(IndigoObject$new(id, obj_id)) }) Indigo$methods( loadQueryMolecule = function(data){ setSession() obj_id = checkResult(.Call("r_indigoLoadQueryMolecule", data)) return(IndigoObject$new(id, obj_id)) }) Indigo$methods( setOption = function(option, value){ setSession() obj_id = checkResult(.Call("r_indigoSetOption", option, value)) }) IndigoObject = setRefClass( Class = "IndigoObject", fields = list(indigo_id = "numeric", obj_id = "numeric")) IndigoObject$methods( initialize = function(new_indigo_id, new_obj_id){ indigo_id<<-new_indigo_id obj_id<<-new_obj_id }) IndigoObject$methods( canonicalSmiles = function(){ .Call("r_indigoSetSessionId", indigo_id) return(checkResultPtr(.Call("r_indigoCanonicalSmiles", obj_id))) }) IndigoObject$methods( fingerprint = function(mode){ .Call("r_indigoSetSessionId", indigo_id) return(checkResult(.Call("r_indigoFingerprint", obj_id, mode))) }) IndigoObject$methods( molecularWeight = function(){ .Call("r_indigoSetSessionId", indigo_id) return(checkResult(.Call("r_indigoMolecularWeight", obj_id))) }) IndigoObject$methods( aromatize = function(){ .Call("r_indigoSetSessionId", indigo_id) return(checkResult(.Call("r_indigoAromatize", obj_id))) }) IndigoObject$methods( finalize = function(){ .Call("r_indigoSetSessionId", indigo_id) (checkResult(.Call("r_indigoFree", obj_id))) }) ################################################################# ################################################################# version <- function() { .Call("r_indigoVersion") } setOption <- function(option, value){ stopifnot(is.character(option)) stopifnot(length(option) == 1, length(value) == 1) obj_id = checkResult(.Call("r_indigoSetOption", option, value)) } canonicalSmiles <- function(data) { stopifnot(is.character(data)) stopifnot(length(data) == 1) return(.Call("canonicalSmiles", data)) } smiles <- function(data) { stopifnot(is.character(data)) stopifnot(length(data) == 1) return(.Call("smiles", data)) } aromatize <- function(data) { stopifnot(is.character(data)) stopifnot(length(data) == 1) return(.Call("aromatize", data)) } aromatizeQuery <- function(data) { stopifnot(is.character(data)) stopifnot(length(data) == 1) return(.Call("aromatizeQuery", data)) } checkSub <- function(query, target, mode) { stopifnot(is.character(query), is.character(target), is.character(mode)) stopifnot(length(query) == 1, length(target) == 1, length(mode) == 1) result <- .Call("checkSub", query, target, mode) if (result == -1) warning(paste("checkSub(\"", query, "\", \"", target, "\", \"", mode, "\") was failed", sep = "", collapse = NULL)) return(result > 0) } fingerprint <- function(item, mode) { stopifnot(is.character(item), is.character(mode)) stopifnot(length(item) == 1, length(mode) == 1) return(.Call("fingerprint", item, mode)) } fingerprintQuery <- function(item, mode) { stopifnot(is.character(item), is.character(mode)) stopifnot(length(item) == 1, length(mode) == 1) return(.Call("fingerprintQuery", item, mode)) } molecularWeight <- function(item) { stopifnot(is.character(item)) stopifnot(length(item) == 1) return(.Call("molecularWeight", item)) } render <- function(item) { stopifnot(is.character(item)) stopifnot(length(item) == 1) return(.Call("render", item)) } renderQuery <- function(item) { stopifnot(is.character(item)) stopifnot(length(item) == 1) return(.Call("renderQuery", item)) } renderHighlightedTarget <- function(target, query) { stopifnot(is.character(target), is.character(query)) stopifnot(length(target) == 1, length(query) == 1) return(.Call("renderHighlightedTarget", target, query)) }Indigo-indigo-1.2.3/api/r/R/onLoad.R000066400000000000000000000003411271037650300167510ustar00rootroot00000000000000.onLoad <- function(libname, pkgname) { basedir <- system.file(package="rindigo") packageStartupMessage('Loading rindigo') packageStartupMessage('For a complete list of package functions, use ls("package:rindigo")') }Indigo-indigo-1.2.3/api/r/src/000077500000000000000000000000001271037650300160025ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/r/src/Makefile000066400000000000000000000013031271037650300174370ustar00rootroot00000000000000export MAKEFLAGS="-j $(grep -c ^processor /proc/cpuinfo)" ifeq ($(shell uname), Linux) INDIGO_LIBRARIES := -lfontconfig -lfreetype endif all:indigo PKG_CXXFLAGS="-I/usr/local/Cellar/r/3.1.1/R.framework/Resources/include" R CMD SHLIB -o rindigo.so rindigo.cpp -L$(PWD)/dist/ -L$(PWD)/dist/static/Linux/x64/ -lindigo-renderer-static -lindigo-static -ltinyxml -lcairo -lpixman -lz -lpng $(INDIGO_LIBRARIES) ./linkhack.py "|gcc|-Ldist/static/Linux/x64/|rindigo.o|-lindigo-renderer-static -lindigo-static -ltinyxml -lcairo -lpixman -lz -lfreetype -lfontconfig -lpng|./rindigo.so" indigo:clean pwd && ls && cd dist && unzip "indigo-libs-1.2.1-dev5-linux64-static.zip" && cd .. clean: rm -f *.o rm -f *.so Indigo-indigo-1.2.3/api/r/src/Makefile.win000066400000000000000000000014601271037650300202370ustar00rootroot00000000000000all:indigo cl.exe /D_USRDLL /D_WINDLL /I $(R_HOME)/include/ rindigo.cpp indigo.lib indigo-renderer.lib z.lib tinyxml.lib R.lib /link /DLL /OUT:rindigo.dll indigo:clean cd dist && unzip "indigo-libs-1.2.1-dev5-win64-shared.zip" && cd .. cd dist && unzip -o "indigo-libs-1.2.1-dev5-win64-static.zip" && cd .. cp dist/shared/Win/x64/indigo.lib indigo.lib cp dist/shared/Win/x64/indigo.dll indigo.dll cp dist/shared/Win/x64/indigo-renderer.lib indigo-renderer.lib cp dist/shared/Win/x64/indigo-renderer.dll indigo-renderer.dll cp dist/static/Win/x64/z.lib z.lib cp dist/static/Win/x64/tinyxml.lib tinyxml.lib pexports $(R_HOME)/Bin/x64/R.dll >R.def dlltool --as-flags=--64 -m i386:x86-64 -D $(R_HOME)/Bin/x64/R.dll -d R.def -l R.lib rm -f *.zip rm -rf static rm -rf shared clean: rm -f *.o rm -f *.soIndigo-indigo-1.2.3/api/r/src/rindigo.cpp000066400000000000000000000225631271037650300201510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "indigo.h" #include "indigo-renderer.h" #ifndef REXPORT_SYMBOL #ifdef _WIN32 #define REXPORT_SYMBOL __declspec(dllexport) #else #define REXPORT_SYMBOL #endif #endif #ifndef REXPORT #ifndef __cplusplus #define REXPORT REXPORT_SYMBOL #else #define REXPORT extern "C" REXPORT_SYMBOL #endif #endif static void _setStringToSTRSXP(SEXP *result, const char *str) { if (str != NULL) SET_STRING_ELT(*result, 0, mkChar(str)); else SET_STRING_ELT(*result, 0, NA_STRING); } REXPORT SEXP r_indigoVersion() { SEXP result = PROTECT(allocVector(STRSXP, 1)); _setStringToSTRSXP(&result, indigoVersion()); UNPROTECT(1); return result; } REXPORT SEXP r_indigoGetLastError() { SEXP result = PROTECT(allocVector(STRSXP, 1)); _setStringToSTRSXP(&result, indigoGetLastError()); UNPROTECT(1); return result; } REXPORT SEXP r_indigoAllocSessionId() { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoAllocSessionId(); UNPROTECT(1); return result; } REXPORT void r_indigoSetSessionId(SEXP id) { indigoSetSessionId(INTEGER(id)[0]); } REXPORT void r_indigoReleaseSessionId(SEXP id) { indigoReleaseSessionId(INTEGER(id)[0]); } REXPORT void r_indigoFree(SEXP obj_id) { indigoFree(INTEGER(obj_id)[0]); } REXPORT SEXP r_indigoLoadMolecule(SEXP data) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoLoadMoleculeFromString(CHAR(STRING_ELT(data, 0))); UNPROTECT(1); return result; } REXPORT SEXP r_indigoLoadQueryMolecule(SEXP data) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(data, 0))); UNPROTECT(1); return result; } REXPORT SEXP r_indigoCanonicalSmiles(SEXP mol) { const char *con_smiles = indigoCanonicalSmiles(INTEGER(mol)[0]); SEXP result = PROTECT(allocVector(STRSXP, 1)); _setStringToSTRSXP(&result, con_smiles); UNPROTECT(1); return result; } REXPORT SEXP r_indigoFingerprint(SEXP mol, SEXP mode) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoFingerprint(INTEGER(mol)[0], CHAR(STRING_ELT(mode, 0))); UNPROTECT(1); return result; } REXPORT SEXP r_indigoMolecularWeight(SEXP mol) { SEXP result = PROTECT(allocVector(REALSXP, 1)); REAL(result)[0] = indigoMolecularWeight(INTEGER(mol)[0]); UNPROTECT(1); return result; } REXPORT SEXP r_indigoSetOption(SEXP option, SEXP value) { SEXP result = PROTECT(allocVector(INTSXP, 1)); SEXPTYPE val_type = TYPEOF(value); const char *opt_str = CHAR(STRING_ELT(option, 0)); int set_result = -1; printf("val_type=%d\n", val_type); if (val_type == LGLSXP) set_result = indigoSetOptionBool(opt_str, LOGICAL(value)[0]); else if (val_type == INTSXP) set_result = indigoSetOptionInt(opt_str, INTEGER(value)[0]); else if (val_type == REALSXP) set_result = indigoSetOptionFloat(opt_str, REAL(value)[0]); else if (val_type == STRSXP) set_result = indigoSetOption(opt_str, CHAR(STRING_ELT(value, 0))); INTEGER(result)[0] = set_result; UNPROTECT(1); return result; } REXPORT SEXP r_indigoLayout(SEXP item) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoLayout(INTEGER(item)[0]); UNPROTECT(1); return result; } REXPORT SEXP r_indigoAromatize(SEXP obj) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = indigoAromatize(INTEGER(obj)[0]); UNPROTECT(1); return result; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// REXPORT SEXP canonicalSmiles(SEXP data) { SEXP result = PROTECT(allocVector(STRSXP, 1)); int m = indigoLoadMoleculeFromString(CHAR(STRING_ELT(data, 0))); _setStringToSTRSXP(&result, indigoCanonicalSmiles(m)); indigoFree(m); UNPROTECT(1); return result; } REXPORT SEXP smiles(SEXP data) { SEXP result = PROTECT(allocVector(STRSXP, 1)); int m = indigoLoadMoleculeFromString(CHAR(STRING_ELT(data, 0))); _setStringToSTRSXP(&result, indigoSmiles(m)); indigoFree(m); UNPROTECT(1); return result; } REXPORT void setFingerprintParams() { indigoSetOptionInt("fp-ord-qwords", 25); indigoSetOptionInt("fp-sim-qwords", 8); indigoSetOptionInt("fp-any-qwords", 15); indigoSetOptionInt("fp-tau-qwords", 0); indigoSetOptionInt("fp-ext-enabled", 0); } REXPORT SEXP aromatize(SEXP mol) { SEXP result = PROTECT(allocVector(STRSXP, 1)); int m = indigoLoadMoleculeFromString(CHAR(STRING_ELT(mol, 0))); indigoAromatize(m); const char *ar_m = indigoCanonicalSmiles(m); _setStringToSTRSXP(&result, ar_m); indigoFree(m); UNPROTECT(1); return result; } REXPORT SEXP aromatizeQuery(SEXP mol) { SEXP result = PROTECT(allocVector(STRSXP, 1)); int m = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(mol, 0))); indigoAromatize(m); const char *ar_m = indigoSmiles(m); _setStringToSTRSXP(&result, ar_m); indigoFree(m); UNPROTECT(1); return result; } REXPORT SEXP checkSub(SEXP query, SEXP target, SEXP mode) { int q = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(query, 0))); int t = indigoLoadMoleculeFromString(CHAR(STRING_ELT(target, 0))); int matcher = indigoSubstructureMatcher(t, CHAR(STRING_ELT(mode, 0))); SEXP smatch; PROTECT( smatch = NEW_INTEGER(1)) ; int match_res = indigoMatch(matcher, q); INTEGER(smatch)[0] = match_res; indigoFree(q); indigoFree(t); indigoFree(matcher); indigoFree(match_res); UNPROTECT(1); return smatch; } const char * _makeOnesListStr(int mol, const char *mode) { if (mol == -1) return NULL; int fp = indigoFingerprint(mol, mode); if (fp == -1) return NULL; const char *ones_list = indigoOneBitsList(fp); indigoFree(fp); return ones_list; } REXPORT SEXP fingerprint(SEXP item, SEXP mode) { SEXP result = PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(result, 0, mkChar("")); int i = indigoLoadMoleculeFromString(CHAR(STRING_ELT(item, 0))); const char *ones_list = _makeOnesListStr(i, CHAR(STRING_ELT(mode, 0))); if (ones_list != NULL) SET_STRING_ELT(result, 0, mkChar(ones_list)); indigoFree(i); UNPROTECT(1); return result; } REXPORT SEXP fingerprintQuery(SEXP item, SEXP mode) { SEXP result = PROTECT(allocVector(STRSXP, 1)); SET_STRING_ELT(result, 0, mkChar("")); int i = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(item, 0))); const char *ones_list = _makeOnesListStr(i, CHAR(STRING_ELT(mode, 0))); if (ones_list != NULL) SET_STRING_ELT(result, 0, mkChar(ones_list)); indigoFree(i); UNPROTECT(1); return result; } REXPORT SEXP molecularWeight(SEXP item) { SEXP result = PROTECT(allocVector(REALSXP, 1)); int i = indigoLoadMoleculeFromString(CHAR(STRING_ELT(item, 0))); REAL(result)[0] = indigoMolecularWeight(i); indigoFree(i); UNPROTECT(1); return result; } REXPORT SEXP render(SEXP item) { SEXP result = PROTECT(allocVector(STRSXP, 1)); char *raw_ptr; int size; indigoSetOption("render-output-format", "svg"); indigoSetOption("render-background-color", "255, 255, 255"); int mol = indigoLoadMoleculeFromString(CHAR(STRING_ELT(item, 0))); indigoLayout(mol); int buffer_object = indigoWriteBuffer(); int render_res = indigoRender(mol, buffer_object); const char *svg_str =indigoToString(buffer_object); SET_STRING_ELT(result, 0, mkChar(svg_str)); indigoFree(buffer_object); indigoFree(mol); UNPROTECT(1); return result; } REXPORT SEXP renderQuery(SEXP item) { SEXP result = PROTECT(allocVector(STRSXP, 1)); char *raw_ptr; int size; indigoSetOption("render-output-format", "svg"); indigoSetOption("render-background-color", "255, 255, 255"); int mol = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(item, 0))); indigoLayout(mol); int buffer_object = indigoWriteBuffer(); int render_res = indigoRender(mol, buffer_object); const char *svg_str =indigoToString(buffer_object); SET_STRING_ELT(result, 0, mkChar(svg_str)); indigoFree(buffer_object); indigoFree(mol); UNPROTECT(1); return result; } REXPORT SEXP renderHighlightedTarget(SEXP target, SEXP query) { SEXP result = PROTECT(allocVector(STRSXP, 1)); char *raw_ptr; int size; indigoSetOption("render-output-format", "svg"); indigoSetOption("render-background-color", "255, 255, 255"); int q = indigoLoadQueryMoleculeFromString(CHAR(STRING_ELT(query, 0))); int t = indigoLoadMoleculeFromString(CHAR(STRING_ELT(target, 0))); int m = indigoSubstructureMatcher(t, ""); int res = 0; res = indigoMatch(m, q); int ht = indigoHighlightedTarget(res); indigoLayout(ht); int buffer_object = indigoWriteBuffer(); int render_res = indigoRender(ht, buffer_object); const char *svg_str =indigoToString(buffer_object); SET_STRING_ELT(result, 0, mkChar(svg_str)); indigoFree(buffer_object); indigoFree(ht); UNPROTECT(1); return result; } Indigo-indigo-1.2.3/api/resources/000077500000000000000000000000001271037650300167645ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/resources/abbreviations.inc000066400000000000000000000057701271037650300223200ustar00rootroot00000000000000static const char *default_abbreviations_xml = "\n" "\n" " \n" " CO2O2CCOOOOC\n" " \n" " \n" " C(O)COOC\n" " \n" " O2S\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " NO2O2N\n" " \n" " \n" " SCN\n" " \n" " NC\n" " \n" " CHOOHC\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " Bun-Bu\n" " \n" " \n" " \n" " FMOCFmoc\n" " \n" " \n" " SO3HHO3S\n" " \n" " \n" " \n" " SiPriPrS\n" " \n" " \n" " \n" " PO3H2H2O3P\n" " \n" " AsO3H2H2O3As\n" " \n" " SbO3H2H2O3Sb\n" "\n"; Indigo-indigo-1.2.3/api/resources/abbreviations.xml000066400000000000000000000047311271037650300223430ustar00rootroot00000000000000 CO2O2CCOOOOC C(O)COOC O2S NO2O2N SCN NC CHOOHC Bun-Bu FMOCFmoc SO3HHO3S SiPriPrS PO3H2H2O3P AsO3H2H2O3As SbO3H2H2O3Sb Indigo-indigo-1.2.3/api/resources/convert.py000066400000000000000000000004431271037650300210170ustar00rootroot00000000000000with open("abbreviations.xml") as inf: with open("abbreviations.inc", "w") as outf: outf.write("static const char *default_abbreviations_xml = ") for line in inf: outf.write("\n \"%s\\n\"" % line[:-1].replace("\"", "\\\"")) outf.write(";\n") Indigo-indigo-1.2.3/api/src/000077500000000000000000000000001271037650300155415ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/src/indigo.cpp000066400000000000000000000166031271037650300175240ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "base_cpp/output.h" #include "base_cpp/profiling.h" #include "base_cpp/temporary_thread_obj.h" #include "molecule/molecule_fingerprint.h" #include "reaction/rxnfile_saver.h" #include "molecule/molfile_saver.h" _SessionLocalContainer indigo_self; DLLEXPORT Indigo & indigoGetInstance () { return indigo_self.getLocalCopy(); } CEXPORT const char * indigoVersion () { return INDIGO_VERSION; } void Indigo::init () { timeout_cancellation_handler = 0; error_handler = 0; error_handler_context = 0; _next_id = 1001; stereochemistry_options.reset(); ignore_noncritical_query_features = false; treat_x_as_pseudoatom = false; skip_3d_chirality = false; deconvolution_aromatization = true; deco_save_ap_bond_orders = false; deco_ignore_errors = true; molfile_saving_mode = 0; molfile_saving_no_chiral = false; filename_encoding = ENCODING_ASCII; fp_params.any_qwords = 15; fp_params.sim_qwords = 8; fp_params.tau_qwords = 10; fp_params.ord_qwords = 25; fp_params.ext = true; embedding_edges_uniqueness = false; find_unique_embeddings = true; max_embeddings = 10000; layout_max_iterations = 0; molfile_saving_skip_date = false; molfile_saving_add_stereo_desc = false; smiles_saving_write_name = false; aam_cancellation_timeout = 0; cancellation_timeout = 0; preserve_ordering_in_serialize = false; unique_dearomatization = false; arom_options = AromaticityOptions(); // Update global index static ThreadSafeStaticObj lock; { OsLocker locker(lock.ref()); static int global_id; _indigo_id = global_id++; } } Indigo::Indigo () { init(); } void Indigo::removeAllObjects () { OsLocker lock(_objects_lock); int i; for (i = _objects.begin(); i != _objects.end(); i = _objects.next(i)) delete _objects.value(i); _objects.clear(); } void Indigo::resetCancellationHandler () { timeout_cancellation_handler.reset(cancellation_timeout); setCancellationHandler(&timeout_cancellation_handler); } void Indigo::initMolfileSaver (MolfileSaver &saver) { saver.mode = molfile_saving_mode; saver.skip_date = molfile_saving_skip_date; saver.no_chiral = molfile_saving_no_chiral; saver.add_stereo_desc = molfile_saving_add_stereo_desc; } void Indigo::initRxnfileSaver (RxnfileSaver &saver) { saver.molfile_saving_mode = molfile_saving_mode; saver.skip_date = molfile_saving_skip_date; saver.add_stereo_desc = molfile_saving_add_stereo_desc; } Indigo::~Indigo () { removeAllObjects(); } int Indigo::getId () const { return _indigo_id; } CEXPORT qword indigoAllocSessionId () { qword id = TL_ALLOC_SESSION_ID(); Indigo &indigo = indigo_self.getLocalCopy(id); indigo.init(); return id; } CEXPORT void indigoSetSessionId (qword id) { TL_SET_SESSION_ID(id); } CEXPORT void indigoReleaseSessionId (qword id) { TL_SET_SESSION_ID(id); indigoGetInstance().removeAllObjects(); TL_RELEASE_SESSION_ID(id); } CEXPORT const char * indigoGetLastError (void) { Indigo &self = indigoGetInstance(); return self.error_message.ptr(); } CEXPORT void indigoSetErrorHandler (INDIGO_ERROR_HANDLER handler, void *context) { Indigo &self = indigoGetInstance(); self.error_handler = handler; self.error_handler_context = context; } CEXPORT int indigoFree (int handle) { try { Indigo &self = indigoGetInstance(); self.removeObject(handle); } catch (Exception &) { } return 1; } CEXPORT int indigoFreeAllObjects () { indigoGetInstance().removeAllObjects(); return 1; } CEXPORT int indigoCountReferences (void) { INDIGO_BEGIN { return self.countObjects(); } INDIGO_END(-1); } CEXPORT void indigoSetErrorMessage (const char *message) { Indigo &self = indigoGetInstance(); self.error_message.readString(message, true); } int Indigo::addObject (IndigoObject *obj) { OsLocker lock(_objects_lock); int id = _next_id++; _objects.insert(id, obj); return id; } void Indigo::removeObject (int id) { OsLocker lock(_objects_lock); if (_objects.at2(id) == 0) return; delete _objects.at(id); _objects.remove(id); } IndigoObject & Indigo::getObject (int handle) { OsLocker lock(_objects_lock); try { return *_objects.at(handle); } catch (RedBlackMap::Error &e) { throw IndigoError("can not access object #%d: %s", handle, e.message()); } } int Indigo::countObjects () { OsLocker lock(_objects_lock); return _objects.size(); } static TemporaryThreadObjManager _indigo_temporary_obj_manager; Indigo::TmpData& Indigo::getThreadTmpData () { return _indigo_temporary_obj_manager.getObject(); } // // IndigoError // IndigoError::IndigoError (const char *format, ...) : Exception() { va_list args; va_start(args, format); _init("core", format, args); va_end(args); } IndigoError::IndigoError (const IndigoError &other) : Exception() { other._cloneTo(this); } // // IndigoPluginContext // IndigoPluginContext::IndigoPluginContext () { indigo_id = -1; } void IndigoPluginContext::validate () { Indigo &indigo = indigoGetInstance(); if (indigo.getId() != indigo_id) { init(); indigo_id = indigo.getId(); } } // // Options registrator // // Options registrator is placed in the main module to avoid being ignored by a // linker as an unused object // http://stackoverflow.com/questions/1229430/how-do-i-prevent-my-unused-global-variables-being-compiled-out static _IndigoBasicOptionsHandlersSetter _indigo_basic_options_handlers_setter; // // Debug methods // #ifdef _WIN32 #include #endif CEXPORT void indigoDbgBreakpoint (void) { #ifdef _WIN32 if (!IsDebuggerPresent()) { char msg[200]; sprintf(msg, "Wait for a debugger?\nPID=%d", GetCurrentProcessId()); int ret = MessageBox(NULL, msg, "Debugging (indigoDbgBreakpoint)", MB_OKCANCEL); if (ret == IDOK) { while (!IsDebuggerPresent()) Sleep(100); } } #else #endif } CEXPORT const char * indigoDbgProfiling (int whole_session) { INDIGO_BEGIN { auto &tmp = self.getThreadTmpData(); ArrayOutput out(tmp.string); profGetStatistics(out, whole_session != 0); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoDbgResetProfiling (int whole_session) { INDIGO_BEGIN { if (whole_session) profTimersResetSession(); else profTimersReset(); return 1; } INDIGO_END(-1); } CEXPORT qword indigoDbgProfilingGetCounter (const char *name, int whole_session) { INDIGO_BEGIN { return indigo::ProfilingSystem::getInstance().getLabelCallCount(name, whole_session != 0); } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_abbreviations.h000066400000000000000000000023361271037650300220770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_arreviations__ #define __indigo_arreviations__ #include #include #include "base_cpp/obj_array.h" namespace indigo { namespace abbreviations { struct Abbreviation { std::string name, expansion; std::vector left_aliases, right_aliases, left_aliases2, right_aliases2; int connections; }; class IndigoAbbreviations { public: IndigoAbbreviations (); void clear(); ObjArray abbreviations; private: void loadDefault (); }; IndigoAbbreviations& indigoGetAbbreviationsInstance (); }} #endif // __indigo_arreviations__ Indigo-indigo-1.2.3/api/src/indigo_abbreviations_core.cpp000066400000000000000000000060301271037650300234350ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_abbreviations.h" #include "indigo_internal.h" #include "tinyxml.h" #include "resources/abbreviations.inc" namespace indigo { namespace abbreviations { static void readXmlIntoArray (TiXmlElement *root, const char *node_name, std::vector &dest) { TiXmlNode *child = root->FirstChild(node_name); for (; child; child = child->NextSiblingElement(node_name)) dest.push_back(child->FirstChild()->ToText()->Value()); } // // IndigoAbbreviations // IndigoAbbreviations::IndigoAbbreviations () { clear(); } void IndigoAbbreviations::clear () { loadDefault(); } void IndigoAbbreviations::loadDefault () { TiXmlDocument xml; xml.Parse(default_abbreviations_xml); if (xml.Error()) throw IndigoError("XML parsing error: %s", xml.ErrorDesc()); TiXmlHandle hxml(&xml); TiXmlHandle handle = hxml.FirstChild("abbreviations"); TiXmlElement *elem = handle.FirstChild("item").ToElement(); for (; elem; elem = elem->NextSiblingElement()) { Abbreviation &abbr = abbreviations.push(); const char *name = elem->Attribute("name"); if (name) abbr.name = name; const char *expansion = elem->Attribute("expansion"); if (expansion) abbr.expansion = expansion; readXmlIntoArray(elem, "right", abbr.right_aliases); readXmlIntoArray(elem, "left", abbr.left_aliases); readXmlIntoArray(elem, "right2", abbr.right_aliases2); readXmlIntoArray(elem, "left2", abbr.left_aliases2); // Read "alias" into both arrays readXmlIntoArray(elem, "alias", abbr.right_aliases); readXmlIntoArray(elem, "alias", abbr.left_aliases); // Add name if alias is empty if (abbr.right_aliases.size() == 0) abbr.right_aliases.push_back(name); if (abbr.left_aliases.size() == 0) abbr.left_aliases.push_back(name); if (abbr.expansion.find("*:4") != std::string::npos) abbr.connections = 4; else if (abbr.expansion.find("*:3") != std::string::npos) abbr.connections = 3; else if (abbr.expansion.find("*:2") != std::string::npos) abbr.connections = 2; else abbr.connections = 1; } } // // Session IndigoName instance // _SessionLocalContainer indigo_abbreviations_self; IndigoAbbreviations& indigoGetAbbreviationsInstance () { return indigo_abbreviations_self.getLocalCopy(); } }} // namespace indigo::abbreviations Indigo-indigo-1.2.3/api/src/indigo_abbreviations_expand.cpp000066400000000000000000000571511271037650300237760ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_abbreviations.h" #include "base_c/bitarray.h" #include "base_cpp/scanner.h" #include "molecule/elements.h" #include "molecule/molecule.h" #include "molecule/smiles_loader.h" #include "indigo.h" #include "indigo_internal.h" #include "indigo_molecule.h" #include "layout/molecule_layout.h" #include namespace indigo { namespace abbreviations { enum Direction { LEFT, RIGHT }; // Single abbreviation token // Each abbreviation is tokenized first: (C2H5)2N- -> N (C*2 H*5)*2 struct Token; typedef std::vector TokenChain; struct Token { enum Type { Pattern, Element, Branch }; Type type; int index; // Index in the abbreviations array or element index TokenChain branch; // Branch if brackets are present int multiplier; }; // Attachment point with order struct AttPoint { explicit AttPoint (int index_, int order_) : index(index_), order(order_) { } int index, order; }; // // AbbreviationExpander // class AbbreviationExpander { public: AbbreviationExpander (ObjArray &abbreviations) : abbreviations(abbreviations) { ignore_case = false; tokenize_level = 0; tokenize_direction = expand_direction = RIGHT; } // Settings ObjArray &abbreviations; Direction tokenize_direction, expand_direction; int tokenize_level; bool ignore_case; // Results int input_index, output_index; Array added_atoms; bool expandAtomAbbreviation (Molecule &mol, int v); private: bool expand (const char *label, int input_order, int output_order, Molecule &dest); int tokensizeSubExpression (const char *label, TokenChain &tokens); int scanSinlgeToken (const char *label, Token &dest); bool tokensizeAbbreviation (const char *label, TokenChain &tokens); bool expandParsedTokens (TokenChain &tokens, Molecule &m, AttPoint &attach_to); bool tryApplyExpansions (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to); bool tryCarbonChain (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to); bool tryRepetition (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to); bool tryExpandToken (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to); bool expandParsedTokensWithRev (TokenChain &tokens, Molecule &m, AttPoint &attach_to); void attachBond (Molecule &m, AttPoint &attach_to, int index); bool compareStrings (const char *s1, const char *s2, size_t len); }; bool AbbreviationExpander::compareStrings (const char *s1, const char *s2, size_t len) { if (ignore_case) return strncasecmp(s1, s2, len) == 0; else return strncmp(s1, s2, len) == 0; } int AbbreviationExpander::tokensizeSubExpression (const char *label, TokenChain &tokens) { size_t size = strlen(label); size_t offset = 0; while (offset < size) { Token t; int offset2 = scanSinlgeToken(label + offset, t); if (offset2 < 0) return -1; tokens.push_back(t); offset += offset2; if (label[offset] == ')') break; } return offset; } int AbbreviationExpander::scanSinlgeToken (const char *label, Token &dest) { size_t size = strlen(label); int offset = 0; if (label[0] == '(') { dest.type = Token::Branch; int offset2 = tokensizeSubExpression(label + 1, dest.branch); if (offset2 == -1 || label[offset2 + 1] != ')') return -1; offset = offset2 + 2; } else { // Trying to apply geedy algorithm size_t best_matched_length = 0; int best_abbreviation = -1; for (int i = 0; i < abbreviations.size(); i++) { Abbreviation &cur = abbreviations[i]; std::vector *aliases[2] = {NULL, NULL}; if (tokenize_direction == LEFT) { aliases[0] = &cur.left_aliases; if (tokenize_level == 2) aliases[1] = &cur.left_aliases2; } else if (tokenize_direction == RIGHT) { aliases[0] = &cur.right_aliases; if (tokenize_level == 2) aliases[1] = &cur.right_aliases2; } for (int k = 0; k < 2; k++) { if (aliases[k] == NULL) break; for (size_t j = 0; j < aliases[k]->size(); j++) { std::string &alias = aliases[k]->at(j); if (alias.length() < best_matched_length) continue; if (compareStrings(label, alias.c_str(), alias.length())) { best_matched_length = alias.length(); best_abbreviation = i; } } } } if (best_abbreviation != -1) { dest.type = Token::Pattern; dest.index = best_abbreviation; offset += best_matched_length; } else { // Try to match the tail with any known element: H, O, C, N, Sn, Mg // Try to read until hydrogen and digit or other captial letter size_t len = 1; while (len < size) { char ch = label[len]; if (ch == 'H' || !isalpha(ch) || (ch >= 'A' && ch <= 'Z')) break; len++; } char symb[4] = {0}; strncpy(symb, label, __min(NELEM(symb) - 1, len)); int id = Element::fromString2(symb); if (id < 0) return -1; dest.type = Token::Element; dest.index = id; offset += len; } } // Try to read multiplier if (isdigit(label[offset])) { int shift; sscanf(label + offset, "%d%n", &dest.multiplier, &shift); offset += shift; } else dest.multiplier = 1; return offset; } bool AbbreviationExpander::tokensizeAbbreviation (const char *label, TokenChain &tokens) { size_t size = strlen(label); size_t offset = 0; while (offset < size) { Token t; int offset2 = scanSinlgeToken(label + offset, t); if (offset2 < 0) return false; tokens.push_back(t); offset += offset2; } return true; } bool AbbreviationExpander::tryApplyExpansions (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to) { if (tryCarbonChain(tokens, offset, m, attach_to)) return true; if (tryRepetition(tokens, offset, m, attach_to)) return true; if (tryExpandToken(tokens, offset, m, attach_to)) return true; return false; } void AbbreviationExpander::attachBond (Molecule &m, AttPoint &attach_to, int idx) { if (attach_to.index != -1) m.addBond(attach_to.index, idx, attach_to.order); else input_index = idx; } bool AbbreviationExpander::tryCarbonChain (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to) { if (attach_to.order != 1) return false; Token &cur = tokens[offset]; if (cur.type != Token::Element) return false; if (cur.multiplier == 1 || cur.index != ELEM_C || offset + 1 == tokens.size()) return false; Token &next = tokens[offset + 1]; // Check CnH(2n+1), CnH2n pattern if (next.multiplier > 1 && next.index == ELEM_H) { bool tail; if (next.multiplier == cur.multiplier * 2) { // Intermediate carbon chain tail = false; } else if (next.multiplier == cur.multiplier * 2 + 1) { // Terminator carbon chain tail = true; } else return false; for (int i = 0; i < cur.multiplier; i++) { int idx = m.addAtom(ELEM_C); attachBond(m, attach_to, idx); attach_to = AttPoint(idx, 1); } if (tail) attach_to = AttPoint(-1, 0); offset += 2; return true; } return false; } bool AbbreviationExpander::expandParsedTokensWithRev (TokenChain &tokens, Molecule &m, AttPoint &attach_to) { if (expandParsedTokens(tokens, m, attach_to)) return true; if (expand_direction != LEFT) return false; std::reverse(tokens.begin(), tokens.end()); bool ret = expandParsedTokens(tokens, m, attach_to); std::reverse(tokens.begin(), tokens.end()); return ret; } bool AbbreviationExpander::tryRepetition (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to) { if (attach_to.order != 1) return false; Token &cur = tokens[offset]; if (cur.type != Token::Branch || cur.multiplier == 1) return false; int atom_bound = m.vertexCount(); AttPoint cur_attach_to = attach_to; int i; for (i = 0; i < cur.multiplier; i++) { if (!expandParsedTokensWithRev(cur.branch, m, cur_attach_to)) break; if (cur_attach_to.index == -1) break; } if (i != cur.multiplier) { // Rollback Array new_atoms; for (int v = m.vertexBegin(); v != m.vertexEnd(); v = m.vertexNext(v)) if (v >= atom_bound) new_atoms.push(v); m.removeAtoms(new_atoms); return false; } offset++; attach_to = cur_attach_to; return true; } bool AbbreviationExpander::tryExpandToken (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to) { Token &cur = tokens[offset]; if (cur.multiplier != 1) return false; Array connection_points; if (cur.type == Token::Element) { if (cur.index == ELEM_H) { offset++; attach_to = AttPoint(-1, 0); return true; } int added = m.addAtom(cur.index); // Get the number of bonds to connect int valence, hyd; int conn = attach_to.order; if (offset + 1 < tokens.size()) { Token &next = tokens[offset + 1]; conn += next.multiplier; } if (!Element::calcValence(cur.index, 0, 0, conn, valence, hyd, false)) { // Ignore next atom // Appear in the OH3C case when H3 is belong to C conn = attach_to.order; if (!Element::calcValence(cur.index, 0, 0, conn, valence, hyd, false)) return false; } for (int i = 0; i < hyd + conn; i++) connection_points.push(added); } else if (cur.type == Token::Pattern) { // Add pattern BufferScanner scanner(abbreviations[cur.index].expansion.c_str()); SmilesLoader loader(scanner); Molecule abbr; loader.loadMolecule(abbr); Array mapping; Array rsites; m.mergeWithMolecule(abbr, &mapping); for (int v = abbr.vertexBegin(); v != abbr.vertexEnd(); v = abbr.vertexNext(v)) { int mapped = mapping[v]; if (m.isRSite(mapped)) { dword bits = m.getRSiteBits(mapped); int id1 = bitGetOneHOIndex(bits); int id2 = bitGetOneHOIndex(bits); if (id1 != id2) throw Exception("Invalid abbreviations specification: %s", abbreviations[cur.index].expansion.c_str()); if (id1 != 0) id1--; // R == R1 const Vertex &vertex = m.getVertex(mapped); int nei = vertex.neiBegin(); connection_points.expandFill(id1 + 1, -1); connection_points[id1] = vertex.neiVertex(nei); // Point connected to the RSite rsites.push(mapped); } } m.removeAtoms(rsites); } else return false; bool rollback = false; int atom_bound = m.vertexCount(); size_t offset2 = offset + 1; attachBond(m, attach_to, connection_points[0]); int i = attach_to.order; while (i < connection_points.size() - 1 && !rollback) { if (offset2 >= tokens.size()) { // If we are at the end then there can be an implicit double bond // Example: -CH2CH= // When we read C H there are no more tokens break; } Token &next = tokens[offset2]; for (int j = 0; j < next.multiplier; j++) { if (i >= connection_points.size()) { rollback = true; break; } if (next.type == Token::Branch) { AttPoint point(connection_points[i], 1); if (!expandParsedTokensWithRev(next.branch, m, point) || point.index != -1) { rollback = true; break; } } else { TokenChain chain; chain.push_back(next); chain[0].multiplier = 1; size_t local_offset = 0; AttPoint point(connection_points[i], 1); if (!tryExpandToken(chain, local_offset, m, point) || point.index != -1) { rollback = true; break; } } i++; } offset2++; } if (i > connection_points.size()) rollback = true; if (!rollback) { if (i == connection_points.size()) { // This is terminal attach_to = AttPoint(-1, 0); } else if (i == connection_points.size() - 1) attach_to = AttPoint(connection_points[i], 1); // Last attachment point else { // Number of tokens are incomlete means that there are double bonds after attach_to = AttPoint(connection_points[i], connection_points.size() - i); } } if (rollback) { // Rollback Array new_atoms; for (int v = m.vertexBegin(); v != m.vertexEnd(); v = m.vertexNext(v)) if (v >= atom_bound) new_atoms.push(v); m.removeAtoms(new_atoms); return false; } offset = offset2; return true; } bool AbbreviationExpander::expandParsedTokens (TokenChain &tokens, Molecule &m, AttPoint &attach_to) { AttPoint attach_to_saved = attach_to; size_t index = 0; bool try_swapped = false; int atom_bound = m.vertexCount(); bool first = true; while (index < tokens.size()) { if (!first && attach_to.index == -1) { attach_to = attach_to_saved; // Rollback Array new_atoms; for (int v = m.vertexBegin(); v != m.vertexEnd(); v = m.vertexNext(v)) if (v >= atom_bound) new_atoms.push(v); m.removeAtoms(new_atoms); return false; } if (tryApplyExpansions(tokens, index, m, attach_to)) { first = false; try_swapped = false; continue; } if (try_swapped) return false; if (expand_direction == LEFT && index + 1 < tokens.size()) { // Try to swap two tokens if H is the first // Happens when left labels are written with right hydrogens: CHO-, C2H5-, etc Token &cur = tokens[index]; Token &next = tokens[index + 1]; if (cur.type == Token::Element && next.type == Token::Element) std::swap(tokens[index], tokens[index + 1]); try_swapped = true; continue; } return false; } return true; } bool AbbreviationExpander::expand (const char *label, int input_order, int output_order, Molecule &m) { TokenChain tokens; if (!tokensizeAbbreviation(label, tokens)) return false; if (expand_direction == LEFT) std::reverse(tokens.begin(), tokens.end()); m.clear(); AttPoint begin_att_point(-1, input_order); AttPoint end_att_point = begin_att_point; if (!expandParsedTokens(tokens, m, end_att_point)) return false; if (end_att_point.order != output_order) return false; output_index = end_att_point.index; return true; } struct Options { Options (Direction expand_direction, Direction tokenize_direction, int tokenize_level = 1, bool ignore_case = false) : expand_direction(expand_direction), tokenize_direction(tokenize_direction), tokenize_level(tokenize_level) {} Direction expand_direction, tokenize_direction; int tokenize_level; bool ignore_case; void set (AbbreviationExpander &expander) { expander.expand_direction = expand_direction; expander.tokenize_direction = tokenize_direction; expander.tokenize_level = tokenize_level; expander.ignore_case = ignore_case; } }; bool AbbreviationExpander::expandAtomAbbreviation (Molecule &mol, int v) { // Determine bonds configuration: number of bonds on the left and on the right Vec3f &pos = mol.getAtomXyz(v); const Vertex &vertex = mol.getVertex(v); int count_left = 0, count_right = 0, count_middle = 0; Array left_atoms, right_atoms; for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_atom = vertex.neiVertex(nei); Vec3f &nei_pos = mol.getAtomXyz(nei_atom); int order = mol.getBondOrder(vertex.neiEdge(nei)); if (nei_pos.x < pos.x) { count_left += order; left_atoms.push(nei_atom); } else { count_right += order; right_atoms.push(nei_atom); } Vec3f diff = nei_pos; diff.sub(pos); if (fabs(diff.y) > fabs(diff.x)) count_middle += order; } int input_order, output_order; bool on_left = false, on_right = false, is_single = false; if (vertex.degree() == 1) { if (count_middle) on_left = on_right = true; if (count_left) on_right = true; if (count_right) on_left = true; input_order = std::max(count_left, count_right); output_order = 0; is_single = true; } else { on_right = true; input_order = count_left; output_order = count_right; } // Try to expand according to the directions Array options; if (on_right && !on_left) { options.push(Options(RIGHT, RIGHT)); options.push(Options(LEFT, LEFT)); } else { if (on_right) options.push(Options(RIGHT, RIGHT)); if (on_left) { options.push(Options(LEFT, LEFT)); options.push(Options(LEFT, RIGHT)); options.push(Options(RIGHT, RIGHT)); options.push(Options(LEFT, LEFT, 2)); } } // Duplicate all the options with ignore case flag int opt_cnt = options.size(); for (int i = 0; i < opt_cnt; i++) { Options opt = options[i]; opt.ignore_case = true; options.push(opt); } bool found = false; Molecule expanded; for (int i = 0; i < options.size(); i++) { options[i].set(*this); if (expand(mol.getPseudoAtom(v), input_order, output_order, expanded)) { found = true; break; } } if (!found) return false; // Merge expanded abbreviation with the source molecule and connect bonds Array mapping; mol.mergeWithMolecule(expanded, &mapping); //if (right_atoms.size() == 0) // right_atoms.swap(left_atoms); for (int i = 0; i < left_atoms.size(); i++) mol.flipBond(left_atoms[i], v, mapping[input_index]); int target_end = output_index; if (is_single == 1) target_end = input_index; for (int i = 0; i < right_atoms.size(); i++) mol.flipBond(right_atoms[i], v, mapping[target_end]); // Collect added atoms and set their coordinate to the initial atom // Layout procedure will find correct atom coordinates, but neighbours // should know correct relative position for (int ve = expanded.vertexBegin(); ve != expanded.vertexEnd(); ve = expanded.vertexNext(ve)) { int idx = mapping[ve]; mol.setAtomXyz(idx, mol.getAtomXyz(v)); added_atoms.push(mapping[ve]); } int sid = mol.sgroups.addSGroup(SGroup::SG_TYPE_SUP); Superatom &super = (Superatom &)mol.sgroups.getSGroup(sid); super.subscript.readString(mol.getPseudoAtom(v), true); for (int ve = expanded.vertexBegin(); ve != expanded.vertexEnd(); ve = expanded.vertexNext(ve)) super.atoms.push(mapping[ve]); mol.removeAtom(v); return true; } // // Interface functions // CEXPORT int indigoExpandAbbreviations (int molecule) { INDIGO_BEGIN { AbbreviationExpander expander(indigoGetAbbreviationsInstance().abbreviations); IndigoObject &obj = self.getObject(molecule); Molecule &mol = obj.getMolecule(); float avg_bond_length = 0; if (Molecule::hasCoord(mol)) { // Detect average bond length for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { const Edge &edge = mol.getEdge(e); Vec3f p1 = mol.getAtomXyz(edge.beg); Vec3f p2 = mol.getAtomXyz(edge.end); Vec3f diff; diff.diff(p1, p2); avg_bond_length += diff.length(); } avg_bond_length /= mol.edgeCount(); } // Collect pseudoatoms first because abbreviation can change atom ordering because // it deletes atoms and adds new ones Array pseudoatoms; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.isPseudoAtom(v)) pseudoatoms.push(v); } int count = 0; for (int i = 0; i < pseudoatoms.size(); i++) { // Try to expand this pseudoatom count += expander.expandAtomAbbreviation(mol, pseudoatoms[i]); } if (count > 0 && Molecule::hasCoord(mol)) { // Layout expanded parts MoleculeLayout ml(mol, self.smart_layout); ml.max_iterations = self.layout_max_iterations; ml.respect_existing_layout = true; // Check if there are not bonds at all or if the coordinates are invalid if (fabs(avg_bond_length) > 0.001) ml.bond_length = avg_bond_length; Filter f; f.initNone(mol.vertexEnd()); for (int i = 0; i < expander.added_atoms.size(); i++) f.unhide(expander.added_atoms[i]); ml.filter = &f; ml.make(); RedBlackSet atoms_with_nei; for (int i = 0; i < expander.added_atoms.size(); i++) { const Vertex &vertex = mol.getVertex(expander.added_atoms[i]); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_atom = vertex.neiVertex(nei); atoms_with_nei.find_or_insert(nei_atom); } atoms_with_nei.find_or_insert(expander.added_atoms[i]); } for (int i = atoms_with_nei.begin(); i != atoms_with_nei.end(); i = atoms_with_nei.next(i)) mol.stereocenters.markBond(atoms_with_nei.key(i)); } return count; } INDIGO_END(-1) } }} // namespace indigo::abbreviations Indigo-indigo-1.2.3/api/src/indigo_array.cpp000066400000000000000000000071421271037650300207200ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "api/indigo.h" #include "indigo_array.h" #include "base_cpp/auto_ptr.h" #include "indigo_loaders.h" IndigoArray::IndigoArray () : IndigoObject(ARRAY) { } IndigoArray::~IndigoArray () { } bool IndigoArray::is (IndigoObject &obj) { if (obj.type == IndigoObject::ARRAY) return true; if (obj.type == IndigoObject::ARRAY_ELEMENT) return is(((IndigoArrayElement &)obj).get()); return false; } IndigoArray & IndigoArray::cast (IndigoObject &obj) { if (obj.type == IndigoObject::ARRAY) return (IndigoArray &)obj; if (obj.type == IndigoObject::ARRAY_ELEMENT) return cast(((IndigoArrayElement &)obj).get()); throw IndigoError("%s is not an array", obj.debugInfo()); } IndigoObject * IndigoArray::clone () { AutoPtr res(new IndigoArray()); int i; for (i = 0; i < objects.size(); i++) res->objects.add(objects.at(i)->clone()); return res.release(); } IndigoArrayElement::IndigoArrayElement (IndigoArray &arr, int idx_) : IndigoObject(ARRAY_ELEMENT) { array = &arr; idx = idx_; } IndigoArrayElement::~IndigoArrayElement () { } IndigoObject & IndigoArrayElement::get () { return *array->objects[idx]; } BaseMolecule & IndigoArrayElement::getBaseMolecule () { return array->objects[idx]->getBaseMolecule(); } Molecule & IndigoArrayElement::getMolecule () { return array->objects[idx]->getMolecule(); } QueryMolecule & IndigoArrayElement::getQueryMolecule () { return array->objects[idx]->getQueryMolecule(); } BaseReaction & IndigoArrayElement::getBaseReaction () { return array->objects[idx]->getBaseReaction(); } Reaction & IndigoArrayElement::getReaction () { return array->objects[idx]->getReaction(); } IndigoObject * IndigoArrayElement::clone () { return array->objects[idx]->clone(); } const char * IndigoArrayElement::getName () { return array->objects[idx]->getName(); } int IndigoArrayElement::getIndex () { return idx; } IndigoArrayIter::IndigoArrayIter (IndigoArray &arr) : IndigoObject(ARRAY_ITER) { _arr = &arr; _idx = -1; } IndigoArrayIter::~IndigoArrayIter () { } IndigoObject * IndigoArrayIter::next () { if (!hasNext()) return 0; _idx++; AutoPtr elem(new IndigoArrayElement(*_arr, _idx)); return elem.release(); } bool IndigoArrayIter::hasNext () { return _idx + 1 < _arr->objects.size(); } CEXPORT int indigoCreateArray () { INDIGO_BEGIN { return self.addObject(new IndigoArray()); } INDIGO_END(-1); } CEXPORT int indigoArrayAdd (int arr, int handle) { INDIGO_BEGIN { IndigoArray &array = IndigoArray::cast(self.getObject(arr)); IndigoObject &obj = self.getObject(handle); int res = array.objects.size(); array.objects.add(obj.clone()); return res; } INDIGO_END(-1); } CEXPORT int indigoIterateArray (int arr) { INDIGO_BEGIN { IndigoArray &array = IndigoArray::cast(self.getObject(arr)); return self.addObject(new IndigoArrayIter(array)); } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_array.h000066400000000000000000000035531271037650300203670ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_array__ #define __indigo_array__ #include "indigo_internal.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class DLLEXPORT IndigoArray : public IndigoObject { public: IndigoArray (); virtual ~IndigoArray (); virtual IndigoObject * clone (); static bool is (IndigoObject &obj); static IndigoArray & cast (IndigoObject &obj); PtrArray objects; }; class DLLEXPORT IndigoArrayElement : public IndigoObject { public: IndigoArrayElement (IndigoArray &arr, int idx_); virtual ~IndigoArrayElement (); IndigoObject & get (); virtual BaseMolecule & getBaseMolecule (); virtual Molecule & getMolecule (); virtual QueryMolecule & getQueryMolecule (); virtual BaseReaction & getBaseReaction (); virtual Reaction & getReaction (); virtual IndigoObject * clone (); virtual const char * getName (); virtual int getIndex (); IndigoArray *array; int idx; }; class IndigoArrayIter : public IndigoObject { public: IndigoArrayIter (IndigoArray &arr); virtual ~IndigoArrayIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: IndigoArray *_arr; int _idx; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_basic.cpp000066400000000000000000000026461271037650300206670ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/auto_ptr.h" CEXPORT int indigoNext (int iter) { INDIGO_BEGIN { IndigoObject *nextobj = self.getObject(iter).next(); if (nextobj == 0) return 0; return self.addObject(nextobj); } INDIGO_END(-1); } CEXPORT int indigoHasNext (int iter) { INDIGO_BEGIN { return self.getObject(iter).hasNext() ? 1 : 0; } INDIGO_END(-1); } CEXPORT int indigoIndex (int handle) { INDIGO_BEGIN { return self.getObject(handle).getIndex(); } INDIGO_END(-1); } CEXPORT int indigoClone (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); return self.addObject(obj.clone()); } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_composition.cpp000066400000000000000000000064351271037650300221510ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "indigo_molecule.h" #include "molecule/molecule_rgroups_composition.h" using MoleculeIter = MoleculeRGroupsComposition::MoleculeIter; class DLLEXPORT IndigoCompositionElem : public IndigoObject { public: IndigoCompositionElem() : IndigoObject(COMPOSITION_ELEM) {} virtual ~IndigoCompositionElem() {} Molecule molecule; MoleculeRGroups variants[RGCOMP_OPT_COUNT]; }; CEXPORT int indigoGetFragmentedMolecule(int elem, const char* options) { INDIGO_BEGIN { if (!strcmp(options, "")) { options = MoleculeIter::OPTION(RGCOMP_OPT::ERASE); } IndigoObject &obj = self.getObject(elem); IndigoCompositionElem& elem = dynamic_cast(obj); MoleculeRGroups *rgroups = nullptr; RGCOMP_OPT OPTS[RGCOMP_OPT_COUNT] = RGCOMP_OPT_ENUM; for (auto i = 0; i < RGCOMP_OPT_COUNT; i++) { if (!strcmp(options, MoleculeIter::OPTION(OPTS[i]))) { rgroups = &elem.variants[i]; break; } } if (rgroups == nullptr) { throw IndigoError("indigoGetFragmentedMolecule(): weird options \"%s\"", options); } AutoPtr result(new IndigoMolecule()); result.ref().mol.clone(elem.molecule, nullptr, nullptr); result.ref().mol.rgroups.copyRGroupsFromMolecule(*rgroups); return self.addObject(result.release()); } INDIGO_END(-1); } class DLLEXPORT IndigoCompositionIter : public IndigoObject { public: IndigoCompositionIter(BaseMolecule& mol) : IndigoObject(COMPOSITION_ITER), _composition(mol), _it(_composition.begin()), _end(_composition.end()) {} virtual ~IndigoCompositionIter() {} virtual IndigoObject* next() { if (!_hasNext) { return nullptr; } AutoPtr result(new IndigoCompositionElem()); _it.dump(result.ref().molecule); RGCOMP_OPT OPTS[RGCOMP_OPT_COUNT] = RGCOMP_OPT_ENUM; for (auto i = 0; i < RGCOMP_OPT_COUNT; i++) { result.ref().variants[i].copyRGroupsFromMolecule( *_it.modifyRGroups(MoleculeIter::OPTION(OPTS[i]))); } _hasNext = _it.next(); return result.release(); } virtual bool hasNext() { return _hasNext; } protected: MoleculeRGroupsComposition _composition; MoleculeRGroupsComposition::MoleculeIter _it; MoleculeRGroupsComposition::MoleculeIter _end; bool _hasNext = true; }; CEXPORT int indigoRGroupComposition(int molecule, const char* options) { INDIGO_BEGIN { BaseMolecule& target = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoCompositionIter(target)); } INDIGO_END(-1); }Indigo-indigo-1.2.3/api/src/indigo_deconvolution.cpp000066400000000000000000001571171271037650300225020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_deconvolution.h" #include "indigo_array.h" #include "indigo_molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/tlscont.h" #include "molecule/molfile_loader.h" #include "molecule/molfile_saver.h" #include "molecule/molecule_rgroups.h" #include "molecule/molecule_scaffold_detection.h" #include "molecule/max_common_submolecule.h" #include "base_cpp/obj_list.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_arom.h" #include "molecule/molfile_saver.h" #include "molecule/elements.h" #include "molecule/molecule_substructure_matcher.h" #include "base_cpp/red_black.h" #include "graph/automorphism_search.h" IMPL_ERROR(IndigoDeconvolution, "R-Group deconvolution"); IndigoDeconvolution::IndigoDeconvolution(): IndigoObject(IndigoObject::DECONVOLUTION), save_ap_bond_orders(false), ignore_errors(false), aromatize(true), cbEmbedding(0), embeddingUserdata(0), _userDefinedScaffold(false) { } void IndigoDeconvolution::setScaffold (QueryMolecule& scaffold) { /* * Clone molecules */ _scaffold.clone_KeepIndices(scaffold, 0); _fullScaffold.clone_KeepIndices(scaffold, 0); if(aromatize) { Indigo &indigo = indigoGetInstance(); QueryMoleculeAromatizer::aromatizeBonds(_scaffold, indigo.arom_options); QueryMoleculeAromatizer::aromatizeBonds(_fullScaffold, indigo.arom_options); } /* * Define user scaffold */ _userDefinedScaffold = false; for (int i = _scaffold.vertexBegin(); i != _scaffold.vertexEnd(); i = _scaffold.vertexNext(i)) { if(_scaffold.isRSite(i)) { _userDefinedScaffold = true; break; } } /* * Replace and set hydrogen count */ if(_userDefinedScaffold) { for (int i = _scaffold.vertexBegin(); i != _scaffold.vertexEnd(); i = _scaffold.vertexNext(i)) { /* * Atom is not a rsite than set implicit hydrogens count */ if(_scaffold.getAtomNumber(i) < 0) continue; int subst_count = 0; const Vertex &vertex = _scaffold.getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { if (_scaffold.getAtomNumber(vertex.neiVertex(j)) != ELEM_H) ++subst_count; } AutoPtr qatom; qatom.reset(QueryMolecule::Atom::und(_scaffold.releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, subst_count))); _scaffold.resetAtom(i, qatom.release()); } } } void IndigoDeconvolution::makeRGroups (QueryMolecule& scaffold) { setScaffold(scaffold); for (int mol_idx = 0; mol_idx < _deconvolutionElems.size(); ++mol_idx) { IndigoDeconvolutionElem& elem = _deconvolutionElems[mol_idx]; makeRGroup(elem, false, true); } } void IndigoDeconvolution::makeRGroup(IndigoDeconvolutionElem& elem, bool all_matches, bool change_scaffold) { if(_fullScaffold.vertexCount() == 0) throw Error("error: scaffold vertex count equals 0"); Molecule& mol_in = elem.mol_in; DecompositionEnumerator& deco_enum = elem.deco_enum; if (mol_in.vertexCount() == 0) { deco_enum.contexts.clear(); return; } if(aromatize) { Indigo &indigo = indigoGetInstance(); MoleculeAromatizer::aromatizeBonds(mol_in, indigo.arom_options); } /* * Set enumerator parameters */ if (aromatize && AromaticityMatcher::isNecessary(_scaffold)) { Indigo &indigo = indigoGetInstance(); deco_enum.am.reset(new AromaticityMatcher(_scaffold, mol_in, indigo.arom_options)); } deco_enum.fmcache.reset(new MoleculeSubstructureMatcher::FragmentMatchCache); deco_enum.fmcache->clear(); deco_enum.all_matches = all_matches; deco_enum.remove_rsites = _userDefinedScaffold; deco_enum.contexts.clear(); deco_enum.deco = this; deco_enum.calculateAutoMaps(_scaffold); /* * Create substructure enumerator and set up options */ EmbeddingEnumerator emb_enum(mol_in); emb_enum.setSubgraph(_scaffold); emb_enum.cb_embedding = _rGroupsEmbedding; emb_enum.cb_match_edge = _matchBonds; emb_enum.cb_match_vertex = _matchAtoms; emb_enum.cb_vertex_remove = _removeAtom; emb_enum.cb_edge_add = _addBond; emb_enum.userdata = &deco_enum; /* * Find subgraph */ emb_enum.process(); if(deco_enum.contexts.size() == 0) { if(ignore_errors) { return; } else throw Error("no embeddings obtained"); } else { for (int match_idx = 0; match_idx < deco_enum.contexts.size(); ++match_idx) { IndigoDecompositionMatch& deco_match = deco_enum.contexts[match_idx]; deco_match.mol_out.clone_KeepIndices(mol_in); createRgroups(deco_match, change_scaffold); deco_match.mol_scaffold.makeEdgeSubmolecule(deco_match.mol_out, deco_match.scaffoldAtoms, deco_match.scaffoldBonds, 0, 0); deco_match.mol_scaffold.unhighlightAll(); deco_match.mol_out.highlightSubmolecule(_scaffold, deco_match.lastMapping.ptr(), true); } } } int IndigoDeconvolution::_rGroupsEmbedding(Graph &graph1, Graph &graph2, int *map, int *inv_map, void *userdata) { QS_DEF(Array, queue); QS_DEF(Array, queue_markers); int n_rgroups = 0; DecompositionEnumerator& deco_enum = *(DecompositionEnumerator*)userdata; int result = deco_enum.all_matches ? 1 : 0; IndigoDecompositionMatch deco_match; deco_match.lastMapping.copy(map, graph1.vertexEnd()); deco_match.lastInvMapping.copy(inv_map, graph2.vertexEnd()); if(deco_enum.remove_rsites) deco_match.removeRsitesFromMaps(graph1); if(deco_enum.shouldContinue(deco_match.lastMapping.ptr(), graph1.vertexEnd())) return result; /* * Visited atom = 1 in case of scaffold and > 1 in case of rgroup */ Array& visited_atoms = deco_match.visitedAtoms; /* * Each array corresponds to a separate Rgroup. * Order - atom number for scaffold * Index - atom number for Rgroup */ ObjArray< Array >& attachment_order = deco_match.attachmentOrder; ObjArray< Array >& attachment_index = deco_match.attachmentIndex; visited_atoms.clear_resize(graph2.vertexEnd()); visited_atoms.zerofill(); attachment_index.clear(); attachment_order.clear(); attachment_index.push(); attachment_order.push(); /* * Calculate scaffold atoms and Rgroup atoms */ for (int atom_idx = graph1.vertexBegin(); atom_idx < graph1.vertexEnd(); atom_idx = graph1.vertexNext(atom_idx)) { int start_idx = deco_match.lastMapping[atom_idx]; if(start_idx == -1) continue; if (visited_atoms[start_idx] > 0) continue; const Vertex &start_vertex = graph2.getVertex(start_idx); for (int cc = start_vertex.neiBegin(); cc != start_vertex.neiEnd(); cc = start_vertex.neiNext(cc)) { int cc_start_idx = start_vertex.neiVertex(cc); if (deco_match.lastInvMapping[cc_start_idx] >= 0 || visited_atoms[cc_start_idx] > 1) continue; int top = 1, bottom = 0; queue.clear(); queue_markers.clear_resize(graph2.vertexEnd()); queue_markers.zerofill(); queue.push(cc_start_idx); queue_markers[cc_start_idx] = 1; while (top != bottom) { int cur_idx = queue[bottom]; const Vertex &vertex = graph2.getVertex(cur_idx); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int nei_idx = vertex.neiVertex(i); if (visited_atoms[nei_idx] > 1) continue; if (queue_markers[nei_idx] != 0) continue; if (deco_match.lastInvMapping[nei_idx] >= 0) { attachment_index[n_rgroups].push(cur_idx); attachment_order[n_rgroups].push(nei_idx); } else { queue.push(nei_idx); queue_markers[nei_idx] = 1; ++top; } } visited_atoms[cur_idx] = n_rgroups + SHIFT_IDX; ++bottom; } ++n_rgroups; attachment_index.push(); attachment_order.push(); } visited_atoms[start_idx] = 1; } /* * Calculate scaffold bonds for a scaffold in the molecule * Also define empty Rgroups */ Array& scaf_bonds = deco_match.scaffoldBonds; scaf_bonds.clear(); int v_idx1, v_idx2, e_idx_mol, e_idx_scaf; for (e_idx_mol = graph2.edgeBegin(); e_idx_mol != graph2.edgeEnd(); e_idx_mol = graph2.edgeNext(e_idx_mol)) { const Edge& edge = graph2.getEdge(e_idx_mol); v_idx1 = deco_match.lastInvMapping[edge.beg]; v_idx2 = deco_match.lastInvMapping[edge.end]; if (v_idx1 >= 0 && v_idx2 >= 0) { e_idx_scaf = graph1.findEdgeIndex(v_idx1, v_idx2); if (e_idx_scaf >= 0) { /* * Append existing edge */ scaf_bonds.push(e_idx_mol); } else { /* * Append non existent bond as a Rgroup with the same att idx and att order */ attachment_index[n_rgroups].push(edge.beg); attachment_order[n_rgroups].push(edge.end); attachment_index[n_rgroups].push(edge.end); attachment_order[n_rgroups].push(edge.beg); attachment_index.push(); attachment_order.push(); ++n_rgroups; } } } /* * Calculate scaffold atoms */ Array& scaf_atoms = deco_match.scaffoldAtoms; scaf_atoms.clear(); for (int a_idx = graph2.vertexBegin(); a_idx != graph2.vertexEnd(); a_idx = graph2.vertexNext(a_idx)) { if(deco_match.lastInvMapping[a_idx] >= 0) scaf_atoms.push(a_idx); } deco_enum.addMatch(deco_match, graph1, graph2); return result; } void IndigoDeconvolution::createRgroups(IndigoDecompositionMatch& deco_match, bool change_scaffold) { Molecule& mol_set = deco_match.mol_out; Molecule& r_molecule = deco_match.rgroup_mol; QS_DEF(Array, inv_scaf_map); QS_DEF(Array, rg_mapping); QS_DEF(Array, rgidx_map); RedBlackMap att_ord_map; RedBlackMap att_bond_map; int att_order, att_idx; Array& visited_atoms = deco_match.visitedAtoms; ObjArray< Array >& attachment_order = deco_match.attachmentOrder; ObjArray< Array >& attachment_index = deco_match.attachmentIndex; int n_rgroups = deco_match.getRgroupNumber(); /* * Create a submolecule for a scaffold */ r_molecule.makeEdgeSubmolecule(mol_set, deco_match.scaffoldAtoms, deco_match.scaffoldBonds, &inv_scaf_map, 0); r_molecule.unhighlightAll(); /* * Add rgroups (if not exist) to the full scaffold */ addCompleteRGroup(deco_match, change_scaffold, &rgidx_map); /* * Add all Rgroups */ for (int rg_idx = 0; rg_idx < n_rgroups; ++rg_idx) { Array& att_indexes = attachment_index[rg_idx]; Array& att_orders = attachment_order[rg_idx]; /* * Add new atom r-site */ int new_atom_idx = r_molecule.addAtom(ELEM_RSITE); Vec3f& atom_xyz = mol_set.getAtomXyz(att_indexes[0]); /* * Copy coordinates */ r_molecule.setAtomXyz(new_atom_idx, atom_xyz.x, atom_xyz.y, atom_xyz.z); /* * Add Rsites */ r_molecule.allowRGroupOnRSite(new_atom_idx, rgidx_map[rg_idx]); /* * Add all bonds and order maps */ int att_val = 0; att_ord_map.clear(); for(int p_idx = 0; p_idx < att_indexes.size(); ++p_idx) { att_idx = att_indexes[p_idx]; att_order = att_orders[p_idx]; /* * Fulfil attachment order map */ if (!att_ord_map.find(att_order)) { ++att_val; att_ord_map.insert(att_order, att_val); } /* * Add a bond with the same order */ if(r_molecule.findEdgeIndex(new_atom_idx, inv_scaf_map[att_order]) == -1) { int edge_idx = mol_set.findEdgeIndex(att_order, att_idx); if(edge_idx == -1) throw Error("internal error: can not find the edge for a scaffold"); r_molecule.addBond(new_atom_idx, inv_scaf_map[att_order], mol_set.getBondOrder(edge_idx)); } /* * Set Rsite attachment order one lower since old api was not changed */ if(!save_ap_bond_orders) r_molecule.setRSiteAttachmentOrder(new_atom_idx, inv_scaf_map[att_order], att_ord_map.at(att_order) - 1); } MoleculeRGroups & mol_rgroups = r_molecule.rgroups; /* * Get an internal rg_idx */ int rg_allow_idx = r_molecule.getSingleAllowedRGroup(new_atom_idx); RGroup & r_group = mol_rgroups.getRGroup(rg_allow_idx); /* * Add a fragment */ Molecule & fragment = r_group.fragments.at(r_group.fragments.add(new Molecule()))->asMolecule(); Filter sub_filter_fr(visited_atoms.ptr(), Filter::EQ, rg_idx + SHIFT_IDX); fragment.makeSubmolecule(mol_set, sub_filter_fr, 0, &rg_mapping); /* * Add attachment points */ att_bond_map.clear(); for (int p_idx = 0; p_idx < att_indexes.size(); ++p_idx) { att_idx = att_indexes[p_idx]; att_order = att_orders[p_idx]; int att_idx_m = rg_mapping.at(att_idx); int att_order_m = att_ord_map.at(att_order); int edge_idx = mol_set.findEdgeIndex(att_order, att_idx); if (edge_idx == -1) throw Error("internal error: can not find the edge for a fragment"); if(save_ap_bond_orders) { /* * TODO change to elem AP */ int ap_atom_idx = fragment.addAtom(ELEM_PSEUDO); /* * Write AP pseudo */ QS_DEF(Array, buf); ArrayOutput ap_out(buf); ap_out.printf("AP%d", att_order_m); ap_out.writeChar(0); fragment.setPseudoAtom(ap_atom_idx, buf.ptr()); /* * Add AP bond * Check for empty RGroup first */ if(att_idx_m == -1) { /* * Add AP saved on the previous iteration or add as a new for the following AP */ if(att_bond_map.find(edge_idx)) { fragment.addBond(ap_atom_idx, att_bond_map.at(edge_idx), mol_set.getBondOrder(edge_idx)); } else { att_bond_map.insert(edge_idx, ap_atom_idx); } } else { /* * Add bond order */ fragment.addBond(ap_atom_idx, att_idx_m, mol_set.getBondOrder(edge_idx)); } } else { fragment.addAttachmentPoint(att_order_m, att_idx_m); } } } } IndigoDeconvolutionElem::IndigoDeconvolutionElem (Molecule& mol) : IndigoObject(DECONVOLUTION_ELEM), idx(-1) { mol_in.clone_KeepIndices(mol, 0); } IndigoDeconvolutionElem::IndigoDeconvolutionElem (Molecule& mol, int* index) : IndigoObject(DECONVOLUTION_ELEM), idx(*index) { mol_in.clone_KeepIndices(mol, 0); } IndigoDeconvolutionElem::IndigoDeconvolutionElem (Molecule& mol, PropertiesMap& props) : IndigoObject(DECONVOLUTION_ELEM), idx(-1) { mol_in.clone_KeepIndices(mol, 0); copyProperties(props); } IndigoDeconvolutionElem::IndigoDeconvolutionElem (IndigoDeconvolutionElem& other): IndigoObject(DECONVOLUTION_ELEM), idx(other.idx) { mol_in.clone_KeepIndices(other.mol_in, 0); copyProperties(other.getProperties()); deco_enum.contexts.clear(); for (int i = 0; i < other.deco_enum.contexts.size(); ++i) { deco_enum.contexts.push().copy(other.deco_enum.contexts[i]); } deco_enum.deco = other.deco_enum.deco; } IndigoDeconvolutionElem::~IndigoDeconvolutionElem () { } IndigoDeconvolutionIter::IndigoDeconvolutionIter(ObjArray& items) : IndigoObject(DECONVOLUTION_ITER), _items(items) { _index = -1; } IndigoObject * IndigoDeconvolutionIter::next () { if (!hasNext()) return 0; _index++; return new IndigoDeconvolutionElem(_items[_index]); } bool IndigoDeconvolutionIter::hasNext () { return _index + 1 < _items.size(); } IndigoDeconvolutionIter::~IndigoDeconvolutionIter () { } IndigoDecompositionMatchIter::IndigoDecompositionMatchIter(ObjArray& matches): IndigoObject(DECOMPOSITION_MATCH_ITER), _matches(matches) { _index = -1; } IndigoObject * IndigoDecompositionMatchIter::next () { if (!hasNext()) return 0; ++_index; AutoPtr result; result.reset(new IndigoDecompositionMatch()); result->copy(_matches[_index]); return result.release(); } bool IndigoDecompositionMatchIter::hasNext () { return _index + 1 < _matches.size(); } void IndigoDeconvolution::addMolecule(Molecule& mol, PropertiesMap& props, int idx) { IndigoDeconvolutionElem & item = _deconvolutionElems.push(mol, &idx); item.copyProperties(props); } void IndigoDecompositionMatch::renumber(Array& map, Array& inv_map) { QS_DEF(Array, tmp_array); for(int i = 0; i < lastMapping.size(); ++i) { int old_value = lastMapping[i]; if(old_value >= 0) { lastMapping[i] = inv_map[old_value]; } } tmp_array.resize(map.size()); tmp_array.zerofill(); for(int i = 0; i < inv_map.size(); ++i) { if(inv_map[i] >= 0) tmp_array.at(inv_map[i]) = visitedAtoms[i]; } visitedAtoms.copy(tmp_array); for(int i = 0; i < attachmentIndex.size(); ++i) { for(int j = 0; j < attachmentIndex[i].size(); ++j) { attachmentIndex[i][j] = inv_map.at(attachmentIndex[i][j]); } } for(int i = 0; i < attachmentOrder.size(); ++i) { for(int j = 0; j < attachmentOrder[i].size(); ++j) { attachmentOrder[i][j] = inv_map.at(attachmentOrder[i][j]); } } } void IndigoDecompositionMatch::copy(IndigoDecompositionMatch& other) { visitedAtoms.copy(other.visitedAtoms); scaffoldBonds.copy(other.scaffoldBonds); scaffoldAtoms.copy(other.scaffoldAtoms); lastMapping.copy(other.lastMapping); lastInvMapping.copy(other.lastInvMapping); attachmentOrder.clear(); attachmentIndex.clear(); for (int i = 0; i < other.attachmentOrder.size(); ++i) { attachmentOrder.push().copy(other.attachmentOrder[i]); } for (int i = 0; i < other.attachmentIndex.size(); ++i) { attachmentIndex.push().copy(other.attachmentIndex[i]); } mol_out.clone_KeepIndices(other.mol_out, 0); rgroup_mol.clone_KeepIndices(other.rgroup_mol, 0); mol_scaffold.clone_KeepIndices(other.mol_scaffold, 0); copyScafAutoMaps(other.scafAutoMaps); deco = other.deco; _completeScaffold = other._completeScaffold; } void IndigoDecompositionMatch::removeRsitesFromMaps(Graph& query_graph) { QueryMolecule& qmol = (QueryMolecule&)query_graph; if(lastMapping.size() != qmol.vertexEnd()) throw IndigoDeconvolution::Error("internal error: undefined mapping"); for (int i = qmol.vertexBegin(); i != qmol.vertexEnd(); i = qmol.vertexNext(i)) { if(qmol.isRSite(i)) { int inv_idx = lastMapping[i]; lastInvMapping[inv_idx] = -1; lastMapping[i] = -1; } } } void IndigoDecompositionMatch::copyScafAutoMaps(ObjList< Array >& autoMaps) { scafAutoMaps.clear(); for (int i = autoMaps.begin(); i != autoMaps.end(); i = autoMaps.next(i)) { int idx = scafAutoMaps.add(); scafAutoMaps.at(idx).copy(autoMaps[i]); } } void IndigoDecompositionMatch::completeScaffold() { if(_completeScaffold) return; if (deco) deco->addCompleteRGroup(*this, true, 0); _completeScaffold = true; } ///* // * Add rgroup if not exists (OLD VERSION) // */ //void IndigoDeconvolution::addCompleteRGroup(IndigoDecompositionMatch& deco_match, bool change_scaffold, Array* rg_map) { // Molecule& mol_set = deco_match.mol_out; // ObjArray< Array >& attachment_order = deco_match.attachmentOrder; // ObjArray< Array >& attachment_index = deco_match.attachmentIndex; // Array& map = deco_match.lastInvMapping; // //// saveMoleculeAsReaction(mol_set, "res/i_mol_set.rxn"); //// saveMoleculeAsReaction(_fullScaffold, "res/i_full_scaf.rxn", true); //// printf("****map\n"); //// for (int i = 0; i < map.size(); ++i) { //// printf("%d %d\n", i ,map[i]); //// } //// printf("**** end map\n"); // // int n_rgroups = deco_match.getRgroupNumber(); // /* // * Search for existing rgroups // */ // QS_DEF(RedBlackStringObjMap< Array >, match_rgroups); // match_rgroups.clear(); // /* // * A set to keep attachment orders // */ // QS_DEF(RedBlackSet, str_keys); // /* // * A string to keep attachemnt orders strings // */ // QS_DEF(Array, str_key); // ArrayOutput str_out(str_key); // // // int new_rg_idx = 0; // /* // * Create match strings // */ // int vert_idx = _fullScaffold.vertexBegin(); // for (; vert_idx != _fullScaffold.vertexEnd(); vert_idx = _fullScaffold.vertexNext(vert_idx)) { // if (!_fullScaffold.isRSite(vert_idx)) // continue; // int cur_rg_idx = _fullScaffold.getSingleAllowedRGroup(vert_idx); // // if(new_rg_idx < cur_rg_idx) // new_rg_idx = cur_rg_idx; // // const Vertex& vert = _fullScaffold.getVertex(vert_idx); // /* // * Strings contain only attachment indexes // */ // str_keys.clear(); // for (int nei_idx = vert.neiBegin(); nei_idx != vert.neiEnd(); nei_idx = vert.neiNext(nei_idx)) { // int nei_vert = vert.neiVertex(nei_idx); // str_keys.find_or_insert(nei_vert); // } // /* // * Call sort and create string // */ // str_out.clear(); // for (int key_idx = str_keys.begin(); key_idx != str_keys.end(); key_idx = str_keys.next(key_idx)) { // str_out.printf("%d;", str_keys.key(key_idx)); // } // str_out.writeChar(0); // /* // * Insert match string // */ // if(!match_rgroups.find(str_key.ptr())) { // match_rgroups.insert(str_key.ptr()); // } // match_rgroups.at(str_key.ptr()).push(cur_rg_idx); // } //// { //// printf("***************\nmatch keys\n"); //// for (int i = match_rgroups.begin(); i != match_rgroups.end(); i = match_rgroups.next(i)) { //// Array& value = match_rgroups.value(i); //// printf("key = %s val = ", match_rgroups.key(i)); //// for (int j = 0; j < value.size(); ++j) { //// printf("%d, ", value[j] ); //// } //// printf("\n"); //// } //// FileOutput fo("res/fullscaf.mol"); //// MolfileSaver ms(fo); //// ms.saveQueryMolecule(_fullScaffold); //// } // // /* // * Loop through all rgroups and seek for matchings // */ // bool match_not_found; // if(rg_map) // rg_map->clear_resize(n_rgroups); // int map_rg_idx; // for (int rg_idx = 0; rg_idx < n_rgroups; ++rg_idx) { // Array& att_orders = attachment_order[rg_idx]; // /* // * Create match string // */ // str_keys.clear(); // for (int a_x = 0; a_x < att_orders.size(); ++a_x) { // str_keys.find_or_insert(map.at(att_orders[a_x])); // } // /* // * Call sort and create string // */ // str_out.clear(); // for (int key_idx = str_keys.begin(); key_idx != str_keys.end(); key_idx = str_keys.next(key_idx)) { // str_out.printf("%d;", str_keys.key(key_idx)); // } // str_out.writeChar(0); // /* // * Search for matches // */ // match_not_found = false; // if(match_rgroups.find(str_key.ptr())) { // Array& match_r = match_rgroups.at(str_key.ptr()); // /* // * Remove matches // */ // if(match_r.size() == 0 ) { // match_not_found = true; // } else { // map_rg_idx = match_r.pop(); // } // } else { // match_not_found = true; // } // /* // * Add rgroup to full scaffold // */ // if(match_not_found) { // ++new_rg_idx; // map_rg_idx = new_rg_idx; // if(change_scaffold && !_userDefinedScaffold) // _addFullRGroup(deco_match, rg_idx, new_rg_idx); // } // /* // * Set up out map for rgroup // */ // if(rg_map) // rg_map->at(rg_idx) = map_rg_idx; // } //// { //// FileOutput fo("res/fullscaf.mol"); //// MolfileSaver ms(fo); //// ms.saveQueryMolecule(_fullScaffold); //// } //} /* * Add rgroup if not exists */ void IndigoDeconvolution::addCompleteRGroup(IndigoDecompositionMatch& deco_match, bool change_scaffold, Array* rg_map) { // saveMoleculeAsReaction(mol_set, "res/i_mol_set.rxn"); // saveMoleculeAsReaction(_fullScaffold, "res/i_full_scaf.rxn", true); // printf("****map\n"); // for (int i = 0; i < map.size(); ++i) { // printf("%d %d\n", i ,map[i]); // } // printf("**** end map\n"); /* * Search for existing rgroups */ QS_DEF(RedBlackStringObjMap< Array >, match_rgroups); match_rgroups.clear(); /* * A set to keep attachment orders */ QS_DEF(RedBlackSet, str_keys); /* * A string to keep attachemnt orders strings */ QS_DEF(Array, str_key); ArrayOutput str_out(str_key); int max_rg_idx = 0; /* * Create match strings */ int vert_idx = _fullScaffold.vertexBegin(); for (; vert_idx != _fullScaffold.vertexEnd(); vert_idx = _fullScaffold.vertexNext(vert_idx)) { if (!_fullScaffold.isRSite(vert_idx)) continue; int cur_rg_idx = _fullScaffold.getSingleAllowedRGroup(vert_idx); if(max_rg_idx < cur_rg_idx) max_rg_idx = cur_rg_idx; const Vertex& vert = _fullScaffold.getVertex(vert_idx); /* * Strings contain only attachment indexes */ str_keys.clear(); for (int nei_idx = vert.neiBegin(); nei_idx != vert.neiEnd(); nei_idx = vert.neiNext(nei_idx)) { int nei_vert = vert.neiVertex(nei_idx); str_keys.find_or_insert(nei_vert); } /* * Call sort and create string */ str_out.clear(); for (int key_idx = str_keys.begin(); key_idx != str_keys.end(); key_idx = str_keys.next(key_idx)) { str_out.printf("%d;", str_keys.key(key_idx)); } str_out.writeChar(0); /* * Insert match string */ if(!match_rgroups.find(str_key.ptr())) { match_rgroups.insert(str_key.ptr()); } match_rgroups.at(str_key.ptr()).push(cur_rg_idx); } match_rgroups.insert("max_rg_idx"); match_rgroups.at("max_rg_idx").push(max_rg_idx); // { // printf("***************\nmatch keys\n"); // for (int i = match_rgroups.begin(); i != match_rgroups.end(); i = match_rgroups.next(i)) { // Array& value = match_rgroups.value(i); // printf("key = %s val = ", match_rgroups.key(i)); // for (int j = 0; j < value.size(); ++j) { // printf("%d, ", value[j] ); // } // printf("\n"); // } // FileOutput fo("res/fullscaf.mol"); // MolfileSaver ms(fo); // ms.saveQueryMolecule(_fullScaffold); // } /* * Search all automorphism matchings for scaffold and select the best one */ int best_idx = 0, min_rg_num = 1<<15, best_rg_score = 1<<15; QS_DEF(Array, rg_map_buf); if(deco_match.scafAutoMaps.size() == 0) throw Error("internal error: can not calculate scaffold matchings for null automorphism"); for (int aut_idx = deco_match.scafAutoMaps.begin(); aut_idx != deco_match.scafAutoMaps.end(); aut_idx = deco_match.scafAutoMaps.next(aut_idx)) { int new_rg_num = _createRgMap(deco_match, aut_idx, match_rgroups, &rg_map_buf, false); /* * New RGroup number should be minimized */ if(new_rg_num < min_rg_num) { min_rg_num = new_rg_num; best_rg_score = _getRgScore(rg_map_buf); best_idx = aut_idx; } else if(new_rg_num == min_rg_num) { /* * If new RGroup number is the same then minimize RGroup order */ int score = _getRgScore(rg_map_buf); if(score < best_rg_score) { best_rg_score = score; best_idx = aut_idx; } } } _createRgMap(deco_match, best_idx, match_rgroups, rg_map, change_scaffold); // { // FileOutput fo("res/fullscaf.mol"); // MolfileSaver ms(fo); // ms.saveQueryMolecule(_fullScaffold); // } } /* */ void IndigoDeconvolution::_addFullRGroup(IndigoDecompositionMatch& deco_match, Array& auto_map, int rg_idx, int new_rg_idx) { Molecule& qmol = deco_match.mol_out; Array& map = deco_match.lastInvMapping; Array& att_orders = deco_match.attachmentOrder[rg_idx]; Array& att_indexes = deco_match.attachmentIndex[rg_idx]; /* * If not found then add Rsite to the full scaffold */ int att_order, att_idx, att_self, new_atom_idx; if (att_indexes.size() > 0) { new_atom_idx = _fullScaffold.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); /* * Copy coordinates */ Vec3f& atom_xyz = qmol.getAtomXyz(att_indexes[0]); _fullScaffold.setAtomXyz(new_atom_idx, atom_xyz.x, atom_xyz.y, atom_xyz.z); /* * Add Rsites */ _fullScaffold.allowRGroupOnRSite(new_atom_idx, new_rg_idx); } /* * Add all bonds */ for (int point_att = 0; point_att < att_indexes.size(); ++point_att) { att_order = att_orders[point_att]; att_idx = att_indexes[point_att]; att_self = auto_map.at(map[att_order]); if (_fullScaffold.findEdgeIndex(new_atom_idx, att_self) == -1) { int edge_idx = qmol.findEdgeIndex(att_order, att_idx); if (edge_idx == -1) throw Error("internal error while converting molecule to query"); _fullScaffold.addBond(new_atom_idx, att_self, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } } } IndigoDecompositionMatch::IndigoDecompositionMatch() : IndigoObject(DECOMPOSITION_MATCH), deco(0), _completeScaffold(false){ } bool IndigoDeconvolution::_matchAtoms (Graph &g1, Graph &g2, const int *, int sub_idx, int super_idx, void* userdata){ if(userdata == 0) throw Error("internal error: undefined DecompositionEnumerator in _matchAtoms"); QueryMolecule::Atom* q_atom = &((BaseMolecule &)g1).asQueryMolecule().getAtom(sub_idx); BaseMolecule &target = (BaseMolecule &)g2; DecompositionEnumerator& deco_enum = *(DecompositionEnumerator*)userdata; return MoleculeSubstructureMatcher::matchQueryAtom(q_atom, target, super_idx, deco_enum.fmcache.get(), 0xFFFFFFFF); } bool IndigoDeconvolution::_matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void* userdata){ if(userdata == 0) throw Error("internal error: undefined DecompositionEnumerator in _matchBonds"); DecompositionEnumerator& deco_enum = *(DecompositionEnumerator*)userdata; QueryMolecule &query = (QueryMolecule &)subgraph; BaseMolecule &target = (BaseMolecule &)supergraph; QueryMolecule::Bond &sub_bond = query.getBond(sub_idx); if (!MoleculeSubstructureMatcher::matchQueryBond(&sub_bond, target, sub_idx, super_idx, deco_enum.am.get(), 0xFFFFFFFF)) return false; return true; } void IndigoDeconvolution::_removeAtom (Graph &, int sub_idx, void *userdata){ if(userdata == 0) throw Error("internal error: undefined DecompositionEnumerator in _removeAtom"); DecompositionEnumerator& deco_enum = *(DecompositionEnumerator*)userdata; if (deco_enum.am.get() != 0) deco_enum.am->unfixNeighbourQueryBond(sub_idx); } void IndigoDeconvolution::_addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { if(userdata == 0) throw Error("internal error: undefined DecompositionEnumerator in _addBond"); DecompositionEnumerator& deco_enum = *(DecompositionEnumerator*)userdata; BaseMolecule &target = (BaseMolecule &)supergraph; if (deco_enum.am.get() != 0) deco_enum.am->fixQueryBond(sub_idx, target.getBondOrder(super_idx) == BOND_AROMATIC); } void IndigoDeconvolution::_makeInvertMap(Array& map, Array& invmap) { for(int i = 0;i < map.size();i++){ if(map[i] != -1){ invmap[map[i]] = i; } } } int IndigoDeconvolution::_createRgMap(IndigoDecompositionMatch& deco_match, int aut_idx, RedBlackStringObjMap >& match_rgroups_in, Array* rg_map, bool change_scaffold) { /* * Copy match RGroups map each time */ QS_DEF(RedBlackStringObjMap< Array >, match_rgroups); match_rgroups.clear(); for (int m_key = match_rgroups_in.begin(); m_key != match_rgroups_in.end(); m_key = match_rgroups_in.next(m_key)) { int i_key = match_rgroups.insert(match_rgroups_in.key(m_key)); match_rgroups.value(i_key).copy(match_rgroups_in.value(m_key)); } Array& auto_map = deco_match.scafAutoMaps[aut_idx]; /* * A set to keep attachment orders */ QS_DEF(RedBlackSet, str_keys); /* * A string to keep attachemnt orders strings */ QS_DEF(Array, str_key); ArrayOutput str_out(str_key); int max_rg_idx = match_rgroups.at("max_rg_idx").at(0); int n_rgroups = deco_match.getRgroupNumber(); ObjArray< Array >& attachment_order = deco_match.attachmentOrder; Array& map = deco_match.lastInvMapping; int result_num = 0; /* * Loop over all rgroups and seek for matchings */ bool match_not_found; if(rg_map) rg_map->clear_resize(n_rgroups); int map_rg_idx; for (int rg_idx = 0; rg_idx < n_rgroups; ++rg_idx) { Array& att_orders = attachment_order[rg_idx]; /* * Create match string */ str_keys.clear(); int att_order; for (int a_x = 0; a_x < att_orders.size(); ++a_x) { att_order = auto_map.at(map.at(att_orders[a_x])); str_keys.find_or_insert(att_order); } /* * Call sort and create string */ str_out.clear(); for (int key_idx = str_keys.begin(); key_idx != str_keys.end(); key_idx = str_keys.next(key_idx)) { str_out.printf("%d;", str_keys.key(key_idx)); } str_out.writeChar(0); /* * Search for matches */ match_not_found = false; if(match_rgroups.find(str_key.ptr())) { Array& match_r = match_rgroups.at(str_key.ptr()); /* * Remove matches */ if(match_r.size() == 0 ) { match_not_found = true; } else { map_rg_idx = match_r.pop(); } } else { match_not_found = true; } /* * Add rgroup to full scaffold */ if(match_not_found) { ++max_rg_idx; ++result_num; map_rg_idx = max_rg_idx; if(change_scaffold && !_userDefinedScaffold) _addFullRGroup(deco_match, auto_map, rg_idx, max_rg_idx); } /* * Set up out map for rgroup */ if(rg_map) rg_map->at(rg_idx) = map_rg_idx; } return result_num; } int IndigoDeconvolution::_getRgScore(Array& rg_map) const { int result = 0; for (int i = 0; i < rg_map.size(); ++i) { result += rg_map[i]; } /* * Get strictly increasing seq */ if(rg_map.size() > 1) { int dif; for (int i = 1; i < rg_map.size(); ++i) { dif = rg_map[i] - rg_map[i-1]; if(dif < 0) result += (100 * dif) * (-1); } } return result; } void IndigoDeconvolution::DecompositionEnumerator::calculateAutoMaps(Graph& sub) { /* * Set callbacks */ AutomorphismSearch auto_search; auto_search.cb_check_automorphism = _cbAutoCheckAutomorphism; auto_search.getcanon = false; auto_search.context = &_scafAutoMaps; /* * Add direct order automap */ _scafAutoMaps.clear(); int l_idx = _scafAutoMaps.add(); Array& d_map = _scafAutoMaps.at(l_idx); d_map.resize(sub.vertexEnd()); for (int i = 0; i < d_map.size(); ++i) { d_map[i] = i; } /* * Search automorphisms if the scaffold not user-defined */ auto_search.process(sub); } bool IndigoDeconvolution::DecompositionEnumerator::shouldContinue(int* map, int size) { if(contexts.size() == 0) return false; RedBlackSet map_set; for (int i = 0; i < size; ++i) { if(map[i] >= 0) map_set.find_or_insert(map[i]); } for (int m_idx = 0; m_idx < contexts.size(); ++m_idx) { Array& scaf_atoms = contexts[m_idx].scaffoldAtoms; if(map_set.size() != scaf_atoms.size()) continue; int i = 0; for (; i < scaf_atoms.size(); ++i) { if(!map_set.find(scaf_atoms[i])) break; } if(i == scaf_atoms.size()) return true; } return false; } void IndigoDeconvolution::DecompositionEnumerator::addMatch(IndigoDecompositionMatch& match, Graph& sub, Graph& super) { match.deco = deco; /* * Add automaps to the match */ match.copyScafAutoMaps(_scafAutoMaps); /* * Check initial conditions and refine automaps */ _refineAutoMaps(match.scafAutoMaps, sub, super, match.lastMapping); /* * Add match itself */ contexts.push().copy(match); /* * Add all other automorphisms matches */ QueryMolecule r_molecule; ObjArray< Array > rsite_orders; RedBlackMap r_sites; QS_DEF(RedBlackSet, processed_r); QS_DEF(Array, swap_order); Array& direct_order = rsite_orders.push(); r_molecule.clone_KeepIndices((BaseMolecule&)sub, 0); if(remove_rsites) { QS_DEF(Array, atoms_r); atoms_r.clear(); for (int i = r_molecule.vertexBegin(); i != r_molecule.vertexEnd(); i = r_molecule.vertexNext(i)) { if(r_molecule.isRSite(i)) atoms_r.push(i); } for (int i = 0; i < atoms_r.size(); ++i) { r_molecule.removeAtom(atoms_r[i]); } } _addAllRsites(r_molecule, match, r_sites); /* * Set callbacks */ AutomorphismSearch auto_search; auto_search.cb_check_automorphism = _cbAutoCheckAutomorphism; auto_search.getcanon = false; auto_search.context = &_autoMaps; _autoMaps.clear(); auto_search.process(r_molecule); /* * Check initial conditions and refine automaps */ _refineAutoMaps(_autoMaps, r_molecule, super, match.lastMapping); /* * Add direct order automap */ for (int rs_idx = r_sites.begin(); rs_idx != r_sites.end(); rs_idx = r_sites.next(rs_idx)) { direct_order.push(r_sites.key(rs_idx)); } for (int auto_idx = _autoMaps.begin(); auto_idx != _autoMaps.end(); auto_idx = _autoMaps.next(auto_idx)) { Array& auto_map = _autoMaps[auto_idx]; /* * Check for correctness and condition */ swap_order.clear(); for (int rs_idx = r_sites.begin(); rs_idx != r_sites.end(); rs_idx = r_sites.next(rs_idx)) { int rs_key = r_sites.key(rs_idx); if (!r_sites.find(auto_map[rs_key])) throw IndigoDeconvolution::Error("internal error: incorrect automorphism for a scaffold"); /* * Create swap order */ swap_order.push(auto_map[rs_key]); } if(!_foundOrder(rsite_orders, swap_order)) { IndigoDecompositionMatch& nu_match = contexts.push(); nu_match.copy(match); rsite_orders.push().copy(swap_order); /* * Swap RGroup indexes */ processed_r.clear(); for (int rs_idx = r_sites.begin(); rs_idx != r_sites.end(); rs_idx = r_sites.next(rs_idx)) { int rs_key = r_sites.key(rs_idx); if(!processed_r.find(rs_key) && auto_map[rs_key] != rs_key) { processed_r.find_or_insert(rs_key); processed_r.find_or_insert(auto_map[rs_key]); _swapIndexes(nu_match, r_sites.value(rs_idx), r_sites.at(auto_map[rs_key])); } } } } } bool IndigoDeconvolution::DecompositionEnumerator::_foundOrder(ObjArray< Array >& rsite_orders, Array& swap_order) { bool found = false; for (int o_idx = 0; o_idx < rsite_orders.size(); ++o_idx) { Array& r_order = rsite_orders[o_idx]; if(r_order.size() != swap_order.size()) continue; int r_idx = 0; for (; r_idx < r_order.size(); ++r_idx) { if(r_order[r_idx] != swap_order[r_idx]) break; } if(r_idx == r_order.size()) { found = true; break; } } return found; } void IndigoDeconvolution::DecompositionEnumerator::_swapIndexes(IndigoDecompositionMatch& match, int old_idx, int new_idx) { QS_DEF(Array, tmp_buf); ObjArray< Array >& attachment_order = match.attachmentOrder; ObjArray< Array >& attachment_index = match.attachmentIndex; tmp_buf.copy(attachment_order[old_idx]); attachment_order[old_idx].copy(attachment_order[new_idx]); attachment_order[new_idx].copy(tmp_buf); tmp_buf.copy(attachment_index[old_idx]); attachment_index[old_idx].copy(attachment_index[new_idx]); attachment_index[new_idx].copy(tmp_buf); Array& visited_atoms = match.visitedAtoms; for (int i = 0; i < visited_atoms.size(); ++i) { if(visited_atoms[i] == (old_idx + SHIFT_IDX)) { visited_atoms[i] = new_idx + SHIFT_IDX; } else if(visited_atoms[i] == (new_idx + SHIFT_IDX)) { visited_atoms[i] = old_idx + SHIFT_IDX; } } } void IndigoDeconvolution::DecompositionEnumerator::_refineAutoMaps(ObjList >& auto_maps, Graph& sub_in, Graph& super, Array& scaf_map) { QS_DEF(Array, indices_to_remove); indices_to_remove.clear(); BaseMolecule& sub = (BaseMolecule&)sub_in; int sub_idx, super_idx, ae_idx_beg, ae_idx_end, e_beg, e_end; for (int auto_idx = auto_maps.begin(); auto_idx != auto_maps.end(); auto_idx = auto_maps.next(auto_idx)) { Array& auto_map = auto_maps.at(auto_idx); bool should_remove = false; /* * Check atom and bond conditioins */ for (int i = sub.vertexBegin(); i != sub.vertexEnd(); i = sub.vertexNext(i)) { if(sub.isRSite(i)) continue; if (scaf_map[i] < 0) continue; sub_idx = auto_map[i]; if (sub_idx < 0) throw IndigoDeconvolution::Error("internal error: auto map doesn't have scaffold idx for vertex"); if(sub.isRSite(sub_idx)) { should_remove = true; break; } super_idx = scaf_map[sub_idx]; if (!IndigoDeconvolution::_matchAtoms(sub, super, 0, sub_idx, super_idx, this)) { should_remove = true; break; } } for (int i = sub.edgeBegin(); i != sub.edgeEnd() && !should_remove; i = sub.edgeNext(i)) { const Edge& edge = sub.getEdge(i); e_beg = edge.beg; e_end = edge.end; if(sub.isRSite(e_beg) || sub.isRSite(e_end)) continue; if (scaf_map[e_beg] < 0 || scaf_map[e_end] < 0) continue; super_idx = super.findEdgeIndex(scaf_map[e_beg], scaf_map[e_end]); if (super_idx < 0) throw IndigoDeconvolution::Error("internal error: scaf map doesn't have map edge"); ae_idx_beg = auto_map[e_beg]; ae_idx_end = auto_map[e_end]; if(ae_idx_beg < 0 || ae_idx_end < 0) throw IndigoDeconvolution::Error("internal error: auto map doesn't have idx for edge atoms"); sub_idx = sub.findEdgeIndex(ae_idx_beg, ae_idx_end); if(sub_idx < 0) throw IndigoDeconvolution::Error("internal error: auto map doesn't have idx for edge"); if (!IndigoDeconvolution::_matchBonds(sub, super, sub_idx, super_idx, this)) { should_remove = true; break; } } if (should_remove) { indices_to_remove.push(auto_idx); } } for (int i = 0; i < indices_to_remove.size(); ++i) { auto_maps.remove(indices_to_remove[i]); } } void IndigoDeconvolution::DecompositionEnumerator::_addAllRsites(QueryMolecule& r_molecule, IndigoDecompositionMatch& deco_match, RedBlackMap& r_sites) { r_sites.clear(); Array& map = deco_match.lastInvMapping; int ngroups = deco_match.getRgroupNumber(); int att_order, att_self, new_atom_idx; for (int rg_idx = 0; rg_idx < ngroups; ++rg_idx) { Array& att_orders = deco_match.attachmentOrder[rg_idx]; /* * If not found then add Rsite to the full scaffold */ if (att_orders.size() > 0) { new_atom_idx = r_molecule.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); r_sites.insert(new_atom_idx, rg_idx); } /* * Add all bonds */ for (int point_att = 0; point_att < att_orders.size(); ++point_att) { att_order = att_orders[point_att]; att_self = map[att_order]; if (r_molecule.findEdgeIndex(new_atom_idx, att_self) == -1) { r_molecule.addBond(new_atom_idx, att_self, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } } } } bool IndigoDeconvolution::DecompositionEnumerator::_cbAutoCheckAutomorphism (Graph &, const Array &mapping, const void *context) { ObjList< Array > &auto_maps = *(ObjList< Array > *) context; int l_idx = auto_maps.add(); auto_maps.at(l_idx).copy(mapping); return false; } CEXPORT int indigoDecomposeMolecules (int scaffold, int structures) { INDIGO_BEGIN { IndigoArray& mol_array = IndigoArray::cast(self.getObject(structures)); AutoPtr deco(new IndigoDeconvolution()); deco->save_ap_bond_orders = self.deco_save_ap_bond_orders; deco->ignore_errors = self.deco_ignore_errors; deco->aromatize = self.deconvolution_aromatization; int i; for (i = 0; i < mol_array.objects.size(); i++) { IndigoObject &obj = *mol_array.objects[i]; /* * Add molecule */ deco->addMolecule(obj.getMolecule(), obj.getProperties(), i); } QueryMolecule& scaf = self.getObject(scaffold).getQueryMolecule(); deco->makeRGroups(scaf); return self.addObject(deco.release()); } INDIGO_END(-1) } CEXPORT int indigoIterateDecomposedMolecules (int decomp) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); if (obj.type != IndigoObject::DECONVOLUTION) throw IndigoError("indigoIterateDecomposedMolecules(): not applicable to %s", obj.debugInfo()); IndigoDeconvolution& deco = (IndigoDeconvolution &)obj; return self.addObject(new IndigoDeconvolutionIter(deco.getItems())); } INDIGO_END(-1) } CEXPORT int indigoDecomposedMoleculeScaffold (int decomp) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); AutoPtr mol_ptr; if (obj.type == IndigoObject::DECONVOLUTION) { /* * Create query scaffold */ IndigoDeconvolution& deco = (IndigoDeconvolution &) obj; mol_ptr.reset(new IndigoQueryMolecule()); IndigoQueryMolecule& qmol = (IndigoQueryMolecule&) mol_ptr.ref(); qmol.qmol.clone(deco.getDecomposedScaffold(), 0, 0); } else if (obj.type == IndigoObject::DECONVOLUTION_ELEM) { /* * Create simple scaffold with rsites */ IndigoDeconvolutionElem& elem = (IndigoDeconvolutionElem&)obj; IndigoDeconvolution::DecompositionEnumerator& deco_enum = elem.deco_enum; if(deco_enum.contexts.size() == 0) { throw IndigoError("indigoDecomposedMoleculeScaffold(): no embeddings were found for the molecule %d", elem.idx); } IndigoDecompositionMatch& deco_match = deco_enum.contexts[0]; mol_ptr.reset(new IndigoMolecule()); IndigoMolecule& mol = (IndigoMolecule&) mol_ptr.ref(); mol.mol.clone(deco_match.mol_scaffold, 0, 0); deco_match.completeScaffold(); } else if (obj.type == IndigoObject::DECOMPOSITION_MATCH) { IndigoDecompositionMatch& deco_match = (IndigoDecompositionMatch&)obj; mol_ptr.reset(new IndigoMolecule()); IndigoMolecule& mol = (IndigoMolecule&) mol_ptr.ref(); mol.mol.clone(deco_match.mol_scaffold, 0, 0); } else { throw IndigoError("indigoDecomposedMoleculeScaffold(): not applicable to %s", obj.debugInfo()); } int obj_idx = self.addObject(mol_ptr.release()); /* * Call layout */ indigoLayout(obj_idx); return obj_idx; } INDIGO_END(-1) } CEXPORT int indigoDecomposedMoleculeHighlighted (int decomp) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); AutoPtr mol; if (obj.type == IndigoObject::DECONVOLUTION_ELEM) { IndigoDeconvolutionElem& elem = (IndigoDeconvolutionElem&) obj; IndigoDeconvolution::DecompositionEnumerator& deco_enum = elem.deco_enum; if (deco_enum.contexts.size() == 0) { throw IndigoError("indigoDecomposedMoleculeHighlighted(): no embeddings were found for the molecule %d", elem.idx); } IndigoDecompositionMatch& deco_match = deco_enum.contexts[0]; mol.create(); mol->mol.clone_KeepIndices(deco_match.mol_out, 0); mol->copyProperties(elem.getProperties()); deco_match.completeScaffold(); } else if (obj.type == IndigoObject::DECOMPOSITION_MATCH) { IndigoDecompositionMatch& deco_match = (IndigoDecompositionMatch&)obj; mol.create(); mol->mol.clone_KeepIndices(deco_match.mol_out, 0); } else { throw IndigoError("indigoDecomposedMoleculeHighlighted(): not applicable to %s", obj.debugInfo()); } return self.addObject(mol.release()); } INDIGO_END(-1) } CEXPORT int indigoDecomposedMoleculeSubstituents (int decomp) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); if (obj.type == IndigoObject::DECONVOLUTION_ELEM) { IndigoDeconvolutionElem& elem = (IndigoDeconvolutionElem&) obj; IndigoDeconvolution::DecompositionEnumerator& deco_enum = elem.deco_enum; if (deco_enum.contexts.size() == 0) { throw IndigoError("indigoDecomposedMoleculeSubstituents(): no embeddings were found for the molecule %d", elem.idx); } IndigoDecompositionMatch& deco_match = deco_enum.contexts[0]; Molecule* qmol = &deco_match.rgroup_mol; deco_match.completeScaffold(); return self.addObject(new IndigoRGroupsIter(qmol)); } else if (obj.type == IndigoObject::DECOMPOSITION_MATCH) { IndigoDecompositionMatch& deco_match = (IndigoDecompositionMatch&)obj; Molecule* qmol = &deco_match.rgroup_mol; return self.addObject(new IndigoRGroupsIter(qmol)); } else { throw IndigoError("indigoDecomposedMoleculeSubstituents(): not applicable to %s", obj.debugInfo()); } } INDIGO_END(-1) } CEXPORT int indigoDecomposedMoleculeWithRGroups (int decomp) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); AutoPtr mol_ptr; if (obj.type == IndigoObject::DECONVOLUTION_ELEM) { IndigoDeconvolutionElem& elem = (IndigoDeconvolutionElem&) obj; IndigoDeconvolution::DecompositionEnumerator& deco_enum = elem.deco_enum; if (deco_enum.contexts.size() == 0) { throw IndigoError("indigoDecomposedMoleculeWithRGroups(): no embeddings were found for the molecule %d", elem.idx); } IndigoDecompositionMatch& deco_match = deco_enum.contexts[0]; mol_ptr.reset(new IndigoMolecule()); mol_ptr->mol.clone(deco_match.rgroup_mol, 0, 0); mol_ptr->copyProperties(elem.getProperties()); deco_match.completeScaffold(); } else if (obj.type == IndigoObject::DECOMPOSITION_MATCH) { IndigoDecompositionMatch& deco_match = (IndigoDecompositionMatch&)obj; if(deco_match.deco) deco_match.deco->createRgroups(deco_match, false); mol_ptr.reset(new IndigoMolecule()); mol_ptr->mol.clone(deco_match.rgroup_mol, 0, 0); } else { throw IndigoError("indigoDecomposedMoleculeWithRGroups(): not applicable to %s", obj.debugInfo()); } return self.addObject(mol_ptr.release()); } INDIGO_END(-1) } CEXPORT int indigoCreateDecomposer(int scaffold) { INDIGO_BEGIN { AutoPtr deco(new IndigoDeconvolution()); deco->save_ap_bond_orders = self.deco_save_ap_bond_orders; deco->ignore_errors = self.deco_ignore_errors; deco->aromatize = self.deconvolution_aromatization; QueryMolecule& scaf = self.getObject(scaffold).getQueryMolecule(); deco->setScaffold(scaf); return self.addObject(deco.release()); } INDIGO_END(-1) } CEXPORT int indigoDecomposeMolecule(int decomp, int mol) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(decomp); if (obj.type != IndigoObject::DECONVOLUTION) throw IndigoError("indigoDecomposeMolecule(): not applicable to %s", obj.debugInfo()); IndigoDeconvolution& deco = (IndigoDeconvolution &)obj; AutoPtr deco_elem; // deco_elem.reset(new IndigoDeconvolutionElem(self.getObject(mol).getMolecule(), self.getObject(mol).getProperties())); deco_elem.reset(new IndigoDeconvolutionElem(self.getObject(mol).getMolecule())); /* * Calculate only first match */ deco.makeRGroup(deco_elem.ref(), false, false); return self.addObject(deco_elem.release()); } INDIGO_END(-1) } CEXPORT int indigoIterateDecompositions(int deco_item) { INDIGO_BEGIN { IndigoObject& in_elem = self.getObject(deco_item); if (in_elem.type != IndigoObject::DECONVOLUTION_ELEM) throw IndigoError("indigoIterateDecompositions(): not applicable to %s", in_elem.debugInfo()); IndigoDeconvolutionElem& elem = (IndigoDeconvolutionElem&)in_elem; IndigoDeconvolution::DecompositionEnumerator& deco_enum = elem.deco_enum; IndigoDeconvolution* deco = deco_enum.deco; /* * Calculate all matches */ if(deco) deco->makeRGroup(elem, true, false); AutoPtr match_iter; match_iter.reset(new IndigoDecompositionMatchIter(deco_enum.contexts)); int obj_idx = self.addObject(match_iter.release()); return obj_idx; } INDIGO_END(-1) } CEXPORT int indigoAddDecomposition(int decomp, int q_match) { INDIGO_BEGIN { IndigoObject& in_deco = self.getObject(decomp); IndigoObject& in_match = self.getObject(q_match); if (in_deco.type != IndigoObject::DECONVOLUTION) throw IndigoError("indigoAddDecomposition(): not applicable to %s", in_deco.debugInfo()); if (in_match.type != IndigoObject::DECOMPOSITION_MATCH) throw IndigoError("indigoAddDecomposition(): not applicable to %s", in_match.debugInfo()); IndigoDeconvolution& deco = (IndigoDeconvolution&)in_deco; IndigoDecompositionMatch& match = (IndigoDecompositionMatch&)in_match; deco.addCompleteRGroup(match, true, 0); return 0; } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_deconvolution.h000066400000000000000000000152731271037650300221430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_deconvolution__ #define __indigo_deconvolution__ #include "indigo_internal.h" #include "molecule/query_molecule.h" #include "molecule/molecule.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_substructure_matcher.h" #include "base_cpp/obj_list.h" #include "base_cpp/properties_map.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class IndigoDeconvolutionElem; class IndigoDecompositionMatch; class DLLEXPORT IndigoDeconvolution : public IndigoObject { private: enum { SHIFT_IDX = 2 }; public: IndigoDeconvolution(); virtual ~IndigoDeconvolution(){} void addMolecule(Molecule& mol, PropertiesMap& props, int idx); void setScaffold (QueryMolecule& scaffold); void makeRGroups (QueryMolecule& scaffold); void makeRGroup (IndigoDeconvolutionElem& elem, bool all_matches, bool change_scaffold); QueryMolecule& getDecomposedScaffold() { return _fullScaffold; } ObjArray& getItems () {return _deconvolutionElems;} /* * Save AP as sepearate atoms */ bool save_ap_bond_orders; /* * Ignore match errors */ bool ignore_errors; /* * Aromatize */ bool aromatize; int (*cbEmbedding) (const int *sub_vert_map, const int *sub_edge_map, const void* info, void* userdata); void *embeddingUserdata; public: class DecompositionEnumerator { public: DecompositionEnumerator():all_matches(false), remove_rsites(false), deco(0){} ~DecompositionEnumerator(){} AutoPtr am; AutoPtr fmcache; void calculateAutoMaps(Graph& sub); bool shouldContinue(int* map, int size); void addMatch(IndigoDecompositionMatch& match, Graph& sub, Graph& super); bool all_matches; bool remove_rsites; IndigoDeconvolution* deco; ObjArray contexts; private: DecompositionEnumerator(const DecompositionEnumerator&); //no implicit copy bool _foundOrder(ObjArray< Array >& rsite_orders, Array& swap_order); void _swapIndexes(IndigoDecompositionMatch&, int old_idx, int new_idx); void _refineAutoMaps(ObjList >& auto_maps, Graph& sub, Graph& super, Array& scaf_map); void _addAllRsites(QueryMolecule&, IndigoDecompositionMatch&, RedBlackMap&); static bool _cbAutoCheckAutomorphism (Graph &graph, const Array &mapping, const void *context); ObjList< Array > _autoMaps; ObjList< Array > _scafAutoMaps; }; void addCompleteRGroup(IndigoDecompositionMatch& emb_context, bool change_scaffold, Array* rg_map); void createRgroups(IndigoDecompositionMatch& emb_context, bool change_scaffold); DECL_ERROR; private: void _parseOptions(const char* options); void _addFullRGroup(IndigoDecompositionMatch& deco_match, Array& auto_map, int rg_idx, int new_rg_idx); static int _rGroupsEmbedding(Graph &g1, Graph &g2, int *core1, int *core2, void *userdata); static bool _matchAtoms (Graph &g1, Graph &g2, const int *, int sub_idx, int super_idx, void* userdata); static bool _matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void* userdata); static void _addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _removeAtom (Graph &subgraph, int sub_idx, void *userdata); void _makeInvertMap(Array& map, Array& invmap); int _createRgMap(IndigoDecompositionMatch& deco_match, int aut_idx, RedBlackStringObjMap >& match_rgroups, Array* rg_map_buf, bool change_scaffold); int _getRgScore(Array& rg_map) const; QueryMolecule _scaffold; QueryMolecule _fullScaffold; bool _userDefinedScaffold; ObjArray _deconvolutionElems; }; class DLLEXPORT IndigoDeconvolutionElem : public IndigoObject { public: IndigoDeconvolutionElem (Molecule& mol); IndigoDeconvolutionElem (Molecule& mol, PropertiesMap& props); IndigoDeconvolutionElem (Molecule& mol, int* index); IndigoDeconvolutionElem (IndigoDeconvolutionElem& elem); virtual ~IndigoDeconvolutionElem (); virtual int getIndex () {return idx;} virtual indigo::PropertiesMap& getProperties() { return _properties;} int idx; Molecule mol_in; IndigoDeconvolution::DecompositionEnumerator deco_enum; indigo::PropertiesMap _properties; }; class DLLEXPORT IndigoDecompositionMatch : public IndigoObject { public: IndigoDecompositionMatch(); Array visitedAtoms; Array scaffoldBonds; Array scaffoldAtoms; Array lastMapping; Array lastInvMapping; ObjArray< Array > attachmentOrder; ObjArray< Array > attachmentIndex; ObjList< Array > scafAutoMaps; int getRgroupNumber() const { return attachmentIndex.size() - 1; } void renumber(Array& map, Array& inv_map); void copy(IndigoDecompositionMatch& other); void removeRsitesFromMaps(Graph& query_graph); void copyScafAutoMaps(ObjList< Array >& autoMaps); void completeScaffold(); Molecule mol_out; Molecule rgroup_mol; Molecule mol_scaffold; IndigoDeconvolution* deco; private: IndigoDecompositionMatch(const IndigoDecompositionMatch&); //no implicit copy bool _completeScaffold; }; class DLLEXPORT IndigoDeconvolutionIter : public IndigoObject { public: IndigoDeconvolutionIter(ObjArray& items); virtual ~IndigoDeconvolutionIter(); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _index; ObjArray& _items; }; class DLLEXPORT IndigoDecompositionMatchIter : public IndigoObject { public: IndigoDecompositionMatchIter(ObjArray& matches); virtual ~IndigoDecompositionMatchIter(){} virtual IndigoObject * next (); virtual bool hasNext (); virtual int getIndex() {return _index;} protected: int _index; ObjArray& _matches; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_fingerprints.cpp000066400000000000000000000332131271037650300223120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_fingerprints.h" #include #include "molecule/molecule_fingerprint.h" #include "base_cpp/output.h" #include "base_c/bitarray.h" #include "reaction/reaction_fingerprint.h" #include "base_cpp/auto_ptr.h" #include "reaction/reaction.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "base_cpp/scanner.h" IndigoFingerprint::IndigoFingerprint () : IndigoObject(FINGERPRINT) { } IndigoFingerprint::~IndigoFingerprint () { } IndigoFingerprint & IndigoFingerprint::cast (IndigoObject &obj) { if (obj.type == IndigoObject::FINGERPRINT) return (IndigoFingerprint &)obj; throw IndigoError("%s is not a fingerprint", obj.debugInfo()); } void _indigoParseMoleculeFingerprintType (MoleculeFingerprintBuilder &builder, const char *type, bool query) { builder.query = query; if (type == 0 || *type == 0 || strcasecmp(type, "sim") == 0) { // similarity builder.skip_tau = true; builder.skip_ext = true; builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; } else if (strcasecmp(type, "sub") == 0) { // substructure builder.skip_sim = true; builder.skip_tau = true; } else if (strcasecmp(type, "sub-res") == 0) { // resonance substructure builder.skip_sim = true; builder.skip_tau = true; builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_ext_charge = true; } else if (strcasecmp(type, "sub-tau") == 0) { // tautomer builder.skip_ord = true; builder.skip_sim = true; // tautomer fingerprint part does already contain all necessary any-bits builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; } else if (strcasecmp(type, "full") == 0) { if (query) throw IndigoError("there can not be 'full' fingerprint of a query molecule"); // full (non-query) fingerprint, do not skip anything } else throw IndigoError("unknown molecule fingerprint type: %s", type); } void _indigoParseReactionFingerprintType (ReactionFingerprintBuilder &builder, const char *type, bool query) { builder.query = query; if (type == 0 || *type == 0 || strcasecmp(type, "sim") == 0) { // similarity builder.skip_ext = true; builder.skip_ord = true; } else if (strcasecmp(type, "sub") == 0) // substructure builder.skip_sim = true; else if (strcasecmp(type, "full") == 0) { if (query) throw IndigoError("there can not be 'full' fingerprint of a query reaction"); // full (non-query) fingerprint, do not skip anything } else throw IndigoError("unknown molecule fingerprint type: %s", type); } CEXPORT int indigoFingerprint (int item, const char *type) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &mol = obj.getBaseMolecule(); MoleculeFingerprintBuilder builder(mol, self.fp_params); _indigoParseMoleculeFingerprintType(builder, type, mol.isQueryMolecule()); builder.process(); AutoPtr fp(new IndigoFingerprint()); fp->bytes.copy(builder.get(), self.fp_params.fingerprintSize()); return self.addObject(fp.release()); } else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); ReactionFingerprintBuilder builder(rxn, self.fp_params); _indigoParseReactionFingerprintType(builder, type, rxn.isQueryReaction()); builder.process(); AutoPtr fp(new IndigoFingerprint()); fp->bytes.copy(builder.get(), self.fp_params.fingerprintSizeExtOrdSim() * 2); return self.addObject(fp.release()); } else throw IndigoError("indigoFingerprint(): accepting only molecules and reactions, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } void IndigoFingerprint::toString (Array &str) { ArrayOutput output(str); int i; for (i = 0; i < bytes.size(); i++) output.printf("%02x", bytes[i]); } void IndigoFingerprint::toBuffer (Array &buf) { buf.copy((char *)bytes.ptr(), bytes.size()); } static float _indigoSimilarity2 (const byte *arr1, const byte *arr2, int size, const char *metrics) { int ones1 = bitGetOnesCount(arr1, size); int ones2 = bitGetOnesCount(arr2, size); int common_ones = bitCommonOnes(arr1, arr2, size); if (metrics == 0 || metrics[0] == 0 || strcasecmp(metrics, "tanimoto") == 0) { if (common_ones == 0) return 0; return (float)common_ones / (ones1 + ones2 - common_ones); } else if (strlen(metrics) >= 7 && strncasecmp(metrics, "tversky", 7) == 0) { float alpha = 0.5f, beta = 0.5f; const char *params = metrics + 7; if (*params != 0) { BufferScanner scanner(params); if (!scanner.tryReadFloat(alpha)) throw IndigoError("unknown metrics: %s", metrics); scanner.skipSpace(); if (!scanner.tryReadFloat(beta)) throw IndigoError("unknown metrics: %s", metrics); } if (common_ones == 0) return 0; float denom = (ones1 - common_ones) * alpha + (ones2 - common_ones) * beta + common_ones; if (denom < 1e-6f) throw IndigoError("bad denominator"); return common_ones / denom; } else if (strcasecmp(metrics, "euclid-sub") == 0) { if (common_ones == 0) return 0; return (float)common_ones / ones1; } else throw IndigoError("unknown metrics: %s", metrics); } static float _indigoSimilarity (Array &arr1, Array &arr2, const char *metrics) { int size = arr1.size(); if (size != arr2.size()) throw IndigoError("fingerprint sizes do not match (%d and %d)", arr1.size(), arr2.size()); return _indigoSimilarity2(arr1.ptr(), arr2.ptr(), size, metrics); } static void _collectAtomFeatures (BaseMolecule &m, RedBlackStringMap &counters, bool with_degrees) { QS_DEF(Array, symbol); for (int i = m.vertexBegin(); i != m.vertexEnd(); i = m.vertexNext(i)) { int iso = 0, charge = 0, radical = 0; if (!m.isRSite(i) && !m.isPseudoAtom(i)) { iso = m.getAtomIsotope(i); charge = m.getAtomCharge(i); radical = m.getAtomRadical_NoThrow(i, -1); } int degree = 0; if (with_degrees) degree = m.getVertex(i).degree(); m.getAtomSymbol(i, symbol); char key[100]; snprintf(key, NELEM(key), "e:%s i:%d c:%d r:%d d:%d", symbol.ptr(), iso, charge, radical, degree); int *ptr = counters.at2(key); if (ptr) (*ptr)++; else counters.insert(key, 1); } } static void _collectBondFeatures (BaseMolecule &m, RedBlackStringMap &counters, bool with_degrees) { QS_DEF(Array, symbol); for (int i = m.edgeBegin(); i != m.edgeEnd(); i = m.edgeNext(i)) { const Edge &e = m.getEdge(i); int d1 = m.getVertex(e.beg).degree(); int d2 = m.getVertex(e.end).degree(); const char *stereo = ""; int parity = m.cis_trans.getParity(i); if (parity) { // Set cis-trans parity only there is one substituent // In other case atoms ordering is important if (d1 == 2 && d2 == 2) stereo = (parity == MoleculeCisTrans::CIS) ? "cis" : "trans"; } int dir = m.getBondDirection(i); if (dir == BOND_UP) stereo = "up"; else if (dir == BOND_DOWN) stereo = "down"; else if (dir == BOND_EITHER) stereo = "up-down"; if (!with_degrees) d1 = d2 = 0; char key[100]; snprintf(key, NELEM(key), "o:%d s:%s d:%d %d", m.getBondOrder(i), stereo, __min(d1, d2), __max(d1, d2)); int *ptr = counters.at2(key); if (ptr) (*ptr)++; else counters.insert(key, 1); } } static void _getCountersDifference (RedBlackStringMap &c1, RedBlackStringMap &c2, int &common, int &diff1, int &diff2) { common = 0; diff1 = 0; diff2 = 0; int* diff[2] = { &diff1, &diff2 }; RedBlackStringMap* cnt[2] = { &c1, &c2 }; for (int i = 0; i < 2; i++) { RedBlackStringMap &a = *cnt[i], &b = *cnt[1 - i]; for (int node = a.begin(); node != a.end(); node = a.next(node)) { const char *key = a.key(node); int val1 = a.value(node); int* val2_ptr = b.at2(key); int val2 = val2_ptr ? *val2_ptr : 0; int c = __min(val1, val2); common += c; *diff[i] += val1 - c; } } // Common values were counted twice common /= 2; } static float _indigoSimilarityNormalizedEdit (BaseMolecule &mol1, BaseMolecule &mol2) { QS_DEF(RedBlackStringMap, c1); QS_DEF(RedBlackStringMap, c2); QS_DEF(RedBlackStringMap, c1b); QS_DEF(RedBlackStringMap, c2b); for (int iter = 0; iter < 2; iter++) { c1.clear(); c2.clear(); bool need_degree = (iter == 1); _collectAtomFeatures(mol1, c1, need_degree); _collectAtomFeatures(mol2, c2, need_degree); _collectBondFeatures(mol1, c1, need_degree); _collectBondFeatures(mol2, c2, need_degree); int common, diff1, diff2; _getCountersDifference(c1, c2, common, diff1, diff2); if (diff1 != 0 || diff2 != 0) { // Use Tversky Index float alpha = 0.7f, beta = 0.7f; float sim = common / (alpha * diff1 + beta * diff2 + common); float scaling = sim * sim * (3 - 2 * sim); return sim * scaling; } } return 1; } CEXPORT float indigoSimilarity (int item1, int item2, const char *metrics) { INDIGO_BEGIN { IndigoObject &obj1 = self.getObject(item1); IndigoObject &obj2 = self.getObject(item2); if (strcasecmp(metrics, "normalized-edit") == 0) { return _indigoSimilarityNormalizedEdit(obj1.getBaseMolecule(), obj2.getBaseMolecule()); } if (IndigoBaseMolecule::is(obj1)) { Molecule &mol1 = obj1.getMolecule(); Molecule &mol2 = obj2.getMolecule(); MoleculeFingerprintBuilder builder1(mol1, self.fp_params); MoleculeFingerprintBuilder builder2(mol2, self.fp_params); _indigoParseMoleculeFingerprintType(builder1, "sim", false); _indigoParseMoleculeFingerprintType(builder2, "sim", false); builder1.process(); builder2.process(); return _indigoSimilarity2(builder1.getSim(), builder2.getSim(), self.fp_params.fingerprintSizeSim(), metrics); } else if (IndigoBaseReaction::is(obj1)) { Reaction &rxn1 = obj1.getReaction(); Reaction &rxn2 = obj2.getReaction(); ReactionFingerprintBuilder builder1(rxn1, self.fp_params); ReactionFingerprintBuilder builder2(rxn2, self.fp_params); _indigoParseReactionFingerprintType(builder1, "sim", false); _indigoParseReactionFingerprintType(builder2, "sim", false); builder1.process(); builder2.process(); return _indigoSimilarity2(builder1.getSim(), builder2.getSim(), self.fp_params.fingerprintSizeSim() * 2, metrics); } else if (self.getObject(item1).type == IndigoObject::FINGERPRINT) { IndigoFingerprint &fp1 = IndigoFingerprint::cast(obj1); IndigoFingerprint &fp2 = IndigoFingerprint::cast(obj2); return _indigoSimilarity(fp1.bytes, fp2.bytes, metrics); } else throw IndigoError("indigoSimilarity(): can not accept %s", obj1.debugInfo()); } INDIGO_END(-1); } CEXPORT int indigoCountBits (int fingerprint) { INDIGO_BEGIN { IndigoFingerprint &fp = IndigoFingerprint::cast(self.getObject(fingerprint)); return bitGetOnesCount(fp.bytes.ptr(), fp.bytes.size()); } INDIGO_END(-1); } CEXPORT int indigoCommonBits (int fingerprint1, int fingerprint2) { INDIGO_BEGIN { Array &fp1 = IndigoFingerprint::cast(self.getObject(fingerprint1)).bytes; Array &fp2 = IndigoFingerprint::cast(self.getObject(fingerprint2)).bytes; if (fp1.size() != fp2.size()) throw IndigoError("fingerprint sizes do not match (%d and %d)", fp1.size(), fp2.size()); return bitCommonOnes(fp1.ptr(), fp2.ptr(), fp1.size()); } INDIGO_END(-1); } CEXPORT const char* indigoOneBitsList (int fingerprint) { INDIGO_BEGIN { Array &fp = IndigoFingerprint::cast(self.getObject(fingerprint)).bytes; auto &tmp = self.getThreadTmpData(); ArrayOutput out(tmp.string); tmp.string.clear(); for(int i=0; i < fp.sizeInBytes() * 8; ++i) { if(bitGetBit(fp.ptr(), i) > 0) { if(tmp.string.size() > 0) { out.writeString(" "); } out.printf("%d", i); } } tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); }Indigo-indigo-1.2.3/api/src/indigo_fingerprints.h000066400000000000000000000022101271037650300217500ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_fingerprints__ #define __indigo_fingerprints__ #include "indigo_internal.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class DLLEXPORT IndigoFingerprint : public IndigoObject { public: IndigoFingerprint (); virtual ~IndigoFingerprint (); virtual void toString (Array &str); virtual void toBuffer (Array &buf); static IndigoFingerprint & cast (IndigoObject &obj); Array bytes; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_internal.h000066400000000000000000000234071271037650300210650ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_internal__ #define __indigo_internal__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "indigo.h" #include "base_cpp/exception.h" #include "base_cpp/io_base.h" #include "base_cpp/cancellation_handler.h" #include "option_manager.h" #include "molecule/molecule_fingerprint.h" #include "molecule/molecule_tautomer.h" #include "molecule/molecule_stereocenter_options.h" #include "molecule/molecule_standardize_options.h" #include "molecule/molecule_ionize.h" /* When Indigo internal code is used dynamically the INDIGO_VERSION define * should be compared with indigoVersion() to ensure libraries binary * compatibility. */ #include "indigo_version.h" using namespace indigo; namespace indigo { class BaseReaction; class QueryReaction; class Reaction; class Output; class Scanner; class SdfLoader; class RdfLoader; class MolfileSaver; class RxnfileSaver; class PropertiesMap; } extern DLLEXPORT OptionManager & indigoGetOptionManager (); class DLLEXPORT IndigoObject { public: explicit IndigoObject (int type_); virtual ~IndigoObject (); enum { SCANNER = 1, MOLECULE, QUERY_MOLECULE, REACTION, QUERY_REACTION, OUTPUT, REACTION_ITER, REACTION_MOLECULE, GROSS, SDF_LOADER, SDF_SAVER, RDF_MOLECULE, RDF_REACTION, RDF_LOADER, SMILES_MOLECULE, SMILES_REACTION, MULTILINE_SMILES_LOADER, ATOM, ATOMS_ITER, RGROUP, RGROUPS_ITER, RGROUP_FRAGMENT, RGROUP_FRAGMENTS_ITER, ARRAY, ARRAY_ITER, ARRAY_ELEMENT, MOLECULE_SUBSTRUCTURE_MATCH_ITER, MOLECULE_SUBSTRUCTURE_MATCHER, REACTION_SUBSTRUCTURE_MATCHER, SCAFFOLD, DECONVOLUTION, DECONVOLUTION_ELEM, DECONVOLUTION_ITER, COMPOSITION_ELEM, COMPOSITION_ITER, PROPERTIES_ITER, PROPERTY, FINGERPRINT, BOND, BONDS_ITER, ATOM_NEIGHBOR, ATOM_NEIGHBORS_ITER, SUPERATOM, SUPERATOMS_ITER, DATA_SGROUP, DATA_SGROUPS_ITER, REPEATING_UNIT, REPEATING_UNITS_ITER, MULTIPLE_GROUP, MULTIPLE_GROUPS_ITER, GENERIC_SGROUP, GENERIC_SGROUPS_ITER, SGROUP_ATOMS_ITER, SGROUP_BONDS_ITER, DECOMPOSITION, COMPONENT, COMPONENTS_ITER, COMPONENT_ATOMS_ITER, COMPONENT_BONDS_ITER, SUBMOLECULE, SUBMOLECULE_ATOMS_ITER, SUBMOLECULE_BONDS_ITER, MAPPING, REACTION_MAPPING, SSSR_ITER, SUBTREES_ITER, RINGS_ITER, EDGE_SUBMOLECULE_ITER, CML_MOLECULE, CML_REACTION, MULTIPLE_CML_LOADER, SAVER, ATTACHMENT_POINTS_ITER, DECOMPOSITION_MATCH, DECOMPOSITION_MATCH_ITER, CDX_MOLECULE, CDX_REACTION, MULTIPLE_CDX_LOADER, CDX_SAVER, SGROUP, SGROUPS_ITER, TAUTOMER_ITER, TAUTOMER_MOLECULE, TGROUP, TGROUPS_ITER }; int type; virtual const char * debugInfo (); virtual void toString (Array &str); virtual void toBuffer (Array &buf); virtual BaseMolecule & getBaseMolecule (); virtual QueryMolecule & getQueryMolecule (); virtual Molecule & getMolecule (); virtual BaseReaction & getBaseReaction (); virtual QueryReaction & getQueryReaction (); virtual Reaction & getReaction (); virtual IndigoObject * clone (); virtual const char * getName (); virtual int getIndex (); virtual IndigoObject * next (); virtual bool hasNext (); virtual void remove (); // virtual RedBlackStringObjMap< Array > * getProperties(); // void copyProperties (RedBlackStringObjMap< Array > &other); virtual PropertiesMap& getProperties(); virtual void copyProperties(PropertiesMap&); virtual void copyProperties (RedBlackStringObjMap< Array > &other); protected: AutoPtr< Array > _dbg_info; // allocated by debugInfo() on demand private: IndigoObject (const IndigoObject &); }; class IndigoGross : public IndigoObject { public: IndigoGross (); virtual ~IndigoGross (); virtual void toString (Array &str); Array gross; }; struct DLLEXPORT ProductEnumeratorParams { ProductEnumeratorParams () { clear(); } void clear () { is_multistep_reactions = false; is_one_tube = false; is_self_react = false; is_layout = true; transform_is_layout = true; max_deep_level = 2; max_product_count = 1000; } bool is_multistep_reactions; bool is_one_tube; bool is_self_react; bool is_layout; bool transform_is_layout; int max_deep_level; int max_product_count; }; class DLLEXPORT Indigo { public: Indigo (); ~Indigo (); Array error_message; INDIGO_ERROR_HANDLER error_handler; void *error_handler_context; IndigoObject & getObject (int handle); int countObjects (); int addObject (IndigoObject *obj); void removeObject (int id); void removeAllObjects (); void init (); int getId () const; struct TmpData { Array string; float xyz[3]; }; // Method that returns temporary buffer that can be returned from Indigo C API methods TmpData& getThreadTmpData (); ProductEnumeratorParams rpe_params; MoleculeFingerprintParameters fp_params; PtrArray tautomer_rules; StereocentersOptions stereochemistry_options; bool ignore_noncritical_query_features; bool treat_x_as_pseudoatom; bool skip_3d_chirality; bool deconvolution_aromatization; bool deco_save_ap_bond_orders; bool deco_ignore_errors; int molfile_saving_mode; // MolfileSaver::MODE_***, default is zero bool molfile_saving_no_chiral; bool molfile_saving_skip_date; bool molfile_saving_add_stereo_desc; bool smiles_saving_write_name; Encoding filename_encoding; bool embedding_edges_uniqueness, find_unique_embeddings; int max_embeddings; int layout_max_iterations; // default is zero -- no limit bool smart_layout = false; int aam_cancellation_timeout; //default is zero - no timeout int cancellation_timeout; // default is 0 seconds - no timeout TimeoutCancellationHandler timeout_cancellation_handler; void resetCancellationHandler (); void initMolfileSaver (MolfileSaver &saver); void initRxnfileSaver (RxnfileSaver &saver); bool preserve_ordering_in_serialize; AromaticityOptions arom_options; // This option is moved out of arom_options because it should be used only in indigoDearomatize method bool unique_dearomatization; StandardizeOptions standardize_options; IonizeOptions ionize_options; protected: RedBlackMap _objects; int _next_id; OsLock _objects_lock; int _indigo_id; }; class DLLEXPORT IndigoPluginContext { public: IndigoPluginContext (); void validate (); protected: virtual void init () = 0; private: int indigo_id; }; #define INDIGO_BEGIN { Indigo &self = indigoGetInstance(); \ try { self.error_message.clear(); self.resetCancellationHandler(); #define INDIGO_END(fail) } \ catch (Exception &ex) \ { \ self.error_message.readString(ex.message(), true); \ if (self.error_handler != 0) \ self.error_handler(ex.message(), \ self.error_handler_context); \ return fail; \ } } #define INDIGO_END_CHECKMSG(success, fail) } \ catch (Exception &ex) \ { \ self.error_message.readLine(ex.message(), true); \ if (self.error_handler != 0) \ self.error_handler(ex.message(), \ self.error_handler_context); \ return fail; \ } \ if (self.error_message.size() > 0) \ { \ if (self.error_handler != 0) \ self.error_handler(self.error_message.ptr(), \ self.error_handler_context); \ return fail; \ } \ return success; } DLLEXPORT Indigo & indigoGetInstance (); class DLLEXPORT IndigoError : public Exception { public: explicit IndigoError (const char *format, ...); IndigoError (const IndigoError &); private: }; class _IndigoBasicOptionsHandlersSetter { public: _IndigoBasicOptionsHandlersSetter (); ~_IndigoBasicOptionsHandlersSetter (); }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_io.cpp000066400000000000000000000107371271037650300202150ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_io.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/auto_ptr.h" #include "molecule/gross_formula.h" #include "indigo_savers.h" IndigoScanner::IndigoScanner (Scanner *scanner) : IndigoObject(SCANNER), ptr(scanner) { } IndigoScanner::IndigoScanner (const char *str) : IndigoObject(SCANNER) { _buf.readString(str, false); ptr = new BufferScanner(_buf); } IndigoScanner::IndigoScanner (const char *buf, int size) : IndigoObject(SCANNER) { _buf.copy(buf, size); ptr = new BufferScanner(_buf); } IndigoScanner::~IndigoScanner () { if (ptr != 0) // can be zero after indigoClose() delete ptr; } Scanner & IndigoScanner::get (IndigoObject &obj) { if (obj.type == SCANNER) return *((IndigoScanner &)obj).ptr; throw IndigoError("%s is not a scanner", obj.debugInfo()); } IndigoOutput::IndigoOutput (Output *output) : IndigoObject(OUTPUT), ptr(output) { _own_buf = false; } IndigoOutput::IndigoOutput () : IndigoObject(OUTPUT) { ptr = new ArrayOutput(_buf); _own_buf = true; } void IndigoOutput::toString (Array &str) { if (_own_buf) str.copy(_buf); else throw IndigoError("can not convert %s to string", debugInfo()); } IndigoOutput::~IndigoOutput () { delete ptr; } Output & IndigoOutput::get (IndigoObject &obj) { if (obj.type == OUTPUT) { Output *ptr = ((IndigoOutput &)obj).ptr; if (ptr == 0) throw IndigoError("output stream has been closed"); return *ptr; } throw IndigoError("%s is not an output", obj.debugInfo()); } CEXPORT int indigoReadFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoScanner(new FileScanner(self.filename_encoding, filename))); } INDIGO_END(-1) } CEXPORT int indigoReadString (const char *str) { INDIGO_BEGIN { return self.addObject(new IndigoScanner(new BufferScanner(str))); } INDIGO_END(-1); } CEXPORT int indigoReadBuffer (const char *buffer, int size) { INDIGO_BEGIN { return self.addObject(new IndigoScanner(new BufferScanner(buffer, size))); } INDIGO_END(-1) } CEXPORT int indigoLoadString (const char *str) { INDIGO_BEGIN { return self.addObject(new IndigoScanner(str)); } INDIGO_END(-1) } CEXPORT int indigoLoadBuffer (const char *buffer, int size) { INDIGO_BEGIN { return self.addObject(new IndigoScanner(buffer, size)); } INDIGO_END(-1) } CEXPORT int indigoWriteFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoOutput(new FileOutput(filename))); } INDIGO_END(-1) } CEXPORT int indigoClose (int output) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(output); if (obj.type == IndigoObject::OUTPUT) { IndigoOutput &out = ((IndigoOutput &)obj); delete out.ptr; out.ptr = 0; return 1; } else if (obj.type == IndigoObject::SAVER) { IndigoSaver &saver = ((IndigoSaver &)obj); saver.close(); return 1; } else throw IndigoError("indigoClose(): does not accept %s", obj.debugInfo()); } INDIGO_END(-1) } CEXPORT int indigoWriteBuffer (void) { INDIGO_BEGIN { return self.addObject(new IndigoOutput()); } INDIGO_END(-1) } CEXPORT const char * indigoToString (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); auto &tmp = self.getThreadTmpData(); obj.toString(tmp.string); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoToBuffer (int handle, char **buf, int *size) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); auto &tmp = self.getThreadTmpData(); obj.toBuffer(tmp.string); *buf = tmp.string.ptr(); *size = tmp.string.size(); return 1; } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_io.h000066400000000000000000000026661271037650300176640ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_io__ #define __indigo_io__ #include "indigo_internal.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class DLLEXPORT IndigoScanner : public IndigoObject { public: IndigoScanner (Scanner *scanner); IndigoScanner (const char *str); IndigoScanner (const char *buf, int size); static Scanner & get (IndigoObject &obj); virtual ~IndigoScanner (); Scanner *ptr; protected: Array _buf; }; class DLLEXPORT IndigoOutput : public IndigoObject { public: IndigoOutput (); IndigoOutput (Output *output); virtual ~IndigoOutput (); virtual void toString (Array &str); static Output & get (IndigoObject &obj); Output *ptr; protected: bool _own_buf; Array _buf; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_layout.cpp000066400000000000000000000060421271037650300211150ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "base_cpp/cancellation_handler.h" #include "layout/reaction_layout.h" #include "layout/molecule_layout.h" #include "reaction/base_reaction.h" #include "indigo_molecule.h" #include "indigo_reaction.h" CEXPORT int indigoLayout (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); int i; if (IndigoBaseMolecule::is(obj) || obj.type == IndigoObject::SUBMOLECULE) { BaseMolecule &mol = obj.getBaseMolecule(); MoleculeLayout ml(mol, self.smart_layout); ml.max_iterations = self.layout_max_iterations; ml.bond_length = 1.6f; TimeoutCancellationHandler cancellation(self.cancellation_timeout); ml.setCancellationHandler(&cancellation); Filter f; if (obj.type == IndigoObject::SUBMOLECULE) { IndigoSubmolecule &submol = (IndigoSubmolecule &)obj; f.initNone(mol.vertexEnd()); for (int i = 0; i < submol.vertices.size(); i++) f.unhide(submol.vertices[i]); ml.filter = &f; } ml.make(); if (obj.type != IndigoObject::SUBMOLECULE) { // Not for submolecule yet mol.clearBondDirections(); mol.stereocenters.markBonds(); mol.allene_stereo.markBonds(); } if (obj.type != IndigoObject::SUBMOLECULE) { for (i = 1; i <= mol.rgroups.getRGroupCount(); i++) { RGroup &rgp = mol.rgroups.getRGroup(i); for (int j = rgp.fragments.begin(); j != rgp.fragments.end(); j = rgp.fragments.next(j)) { rgp.fragments[j]->clearBondDirections(); rgp.fragments[j]->stereocenters.markBonds(); rgp.fragments[j]->allene_stereo.markBonds(); } } } } else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); ReactionLayout rl(rxn, self.smart_layout); rl.max_iterations = self.layout_max_iterations; rl.bond_length = 1.6f; rl.make(); rxn.markStereocenterBonds(); } else { throw IndigoError("The object provided is neither a molecule, nor a reaction"); } return 0; } INDIGO_END(-1) }Indigo-indigo-1.2.3/api/src/indigo_loaders.cpp000066400000000000000000000517541271037650300212430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_loaders.h" #include "indigo_io.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "molecule/sdf_loader.h" #include "molecule/rdf_loader.h" #include "molecule/molfile_loader.h" #include "molecule/smiles_loader.h" #include "reaction/rsmiles_loader.h" #include "base_cpp/scanner.h" #include "reaction/rxnfile_loader.h" #include "molecule/sdf_loader.h" #include "molecule/multiple_cml_loader.h" #include "molecule/molecule_cml_loader.h" #include "reaction/reaction_cml_loader.h" #include "molecule/multiple_cdx_loader.h" #include "molecule/molecule_cdx_loader.h" #include "reaction/reaction_cdx_loader.h" IndigoSdfLoader::IndigoSdfLoader (Scanner &scanner) : IndigoObject(SDF_LOADER) { _own_scanner = 0; sdf_loader = 0; sdf_loader = new SdfLoader(scanner); } IndigoSdfLoader::IndigoSdfLoader (const char *filename) : IndigoObject(SDF_LOADER) { _own_scanner = 0; sdf_loader = 0; // AutoPtr guard in case of exception in SdfLoader (happens in case of empty file) AutoPtr scanner(new FileScanner(indigoGetInstance().filename_encoding, filename)); sdf_loader = new SdfLoader(*scanner.get()); _own_scanner = scanner.release(); } IndigoSdfLoader::~IndigoSdfLoader () { delete sdf_loader; if (_own_scanner != 0) delete _own_scanner; } IndigoRdfData::IndigoRdfData (int type, Array &data, int index, int offset) : IndigoObject(type) { _loaded = false; _data.copy(data); _index = index; _offset = offset; } IndigoRdfData::IndigoRdfData (int type, Array &data, PropertiesMap &properties, int index, int offset) : IndigoObject(type) { _loaded = false; _data.copy(data); _properties.copy(properties); _index = index; _offset = offset; } IndigoRdfData::~IndigoRdfData () { } Array & IndigoRdfData::getRawData () { return _data; } int IndigoRdfData::tell () { return _offset; } int IndigoRdfData::getIndex () { return _index; } IndigoRdfMolecule::IndigoRdfMolecule (Array &data, PropertiesMap &properties, int index, int offset) : IndigoRdfData(RDF_MOLECULE, data, properties, index, offset) { } Molecule & IndigoRdfMolecule::getMolecule () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); MolfileLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.skip_3d_chirality = self.skip_3d_chirality; loader.loadMolecule(_mol); _loaded = true; } return _mol; } BaseMolecule & IndigoRdfMolecule::getBaseMolecule () { return getMolecule(); } const char * IndigoRdfMolecule::getName () { if (_loaded) return _mol.name.ptr(); Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); auto &tmp = self.getThreadTmpData(); scanner.readLine(tmp.string, true); return tmp.string.ptr(); } IndigoObject * IndigoRdfMolecule::clone () { return IndigoMolecule::cloneFrom(*this); } IndigoRdfMolecule::~IndigoRdfMolecule () { } IndigoRdfReaction::IndigoRdfReaction (Array &data, PropertiesMap &properties, int index, int offset) : IndigoRdfData(RDF_REACTION, data, properties, index, offset) { } Reaction & IndigoRdfReaction::getReaction () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); RxnfileLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.loadReaction(_rxn); _loaded = true; } return _rxn; } BaseReaction & IndigoRdfReaction::getBaseReaction () { return getReaction(); } const char * IndigoRdfReaction::getName () { if (_loaded) return _rxn.name.ptr(); Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); auto &tmp = self.getThreadTmpData(); scanner.readLine(tmp.string, true); if (strcmp(tmp.string.ptr(), "$RXN") != 0) throw IndigoError("IndigoRdfReaction::getName(): unexpected first line in the files with reactions." "'%s' has been found but '$RXN' has been expected."); // Read next line with the name scanner.readLine(tmp.string, true); return tmp.string.ptr(); } IndigoObject * IndigoRdfReaction::clone () { return IndigoReaction::cloneFrom(*this); } IndigoRdfReaction::~IndigoRdfReaction () { } IndigoObject * IndigoSdfLoader::next () { if (sdf_loader->isEOF()) return 0; int counter = sdf_loader->currentNumber(); int offset = sdf_loader->tell(); sdf_loader->readNext(); return new IndigoRdfMolecule(sdf_loader->data, sdf_loader->properties, counter, offset); } IndigoObject * IndigoSdfLoader::at (int index) { sdf_loader->readAt(index); return new IndigoRdfMolecule(sdf_loader->data, sdf_loader->properties, index, 0); } bool IndigoSdfLoader::hasNext () { return !sdf_loader->isEOF(); } int IndigoSdfLoader::tell () { return sdf_loader->tell(); } IndigoRdfLoader::IndigoRdfLoader (Scanner &scanner) : IndigoObject(RDF_LOADER) { _own_scanner = 0; rdf_loader = 0; rdf_loader = new RdfLoader(scanner); } IndigoRdfLoader::IndigoRdfLoader (const char *filename) : IndigoObject(RDF_LOADER) { _own_scanner = 0; rdf_loader = 0; _own_scanner = new FileScanner(indigoGetInstance().filename_encoding, filename); rdf_loader = new RdfLoader(*_own_scanner); } IndigoRdfLoader::~IndigoRdfLoader () { delete rdf_loader; if (_own_scanner != 0) delete _own_scanner; } IndigoObject * IndigoRdfLoader::next () { if (rdf_loader->isEOF()) return 0; int counter = rdf_loader->currentNumber(); int offset = rdf_loader->tell(); rdf_loader->readNext(); if (rdf_loader->isMolecule()) return new IndigoRdfMolecule(rdf_loader->data, rdf_loader->properties, counter, offset); else return new IndigoRdfReaction(rdf_loader->data, rdf_loader->properties, counter, offset); } IndigoObject * IndigoRdfLoader::at (int index) { rdf_loader->readAt(index); if (rdf_loader->isMolecule()) return new IndigoRdfMolecule(rdf_loader->data, rdf_loader->properties, index, 0); else return new IndigoRdfReaction(rdf_loader->data, rdf_loader->properties, index, 0); } int IndigoRdfLoader::tell () { return rdf_loader->tell(); } bool IndigoRdfLoader::hasNext () { return !rdf_loader->isEOF(); } IndigoSmilesMolecule::IndigoSmilesMolecule (Array &smiles, int index, int offset) : IndigoRdfData(SMILES_MOLECULE, smiles, index, offset) { } IndigoSmilesMolecule::~IndigoSmilesMolecule () { } Molecule & IndigoSmilesMolecule::getMolecule () { Indigo &self = indigoGetInstance(); if (!_loaded) { BufferScanner scanner(_data); SmilesLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.loadMolecule(_mol); _loaded = true; } return _mol; } BaseMolecule & IndigoSmilesMolecule::getBaseMolecule () { return getMolecule(); } const char * IndigoSmilesMolecule::getName () { if (getMolecule().name.ptr() == 0) return ""; return getMolecule().name.ptr(); } IndigoObject * IndigoSmilesMolecule::clone () { return IndigoMolecule::cloneFrom(*this); } IndigoSmilesReaction::IndigoSmilesReaction (Array &smiles, int index, int offset) : IndigoRdfData(SMILES_REACTION, smiles, index, offset) { } IndigoSmilesReaction::~IndigoSmilesReaction () { } Reaction & IndigoSmilesReaction::getReaction () { if (!_loaded) { BufferScanner scanner(_data); RSmilesLoader loader(scanner); loader.loadReaction(_rxn); _loaded = true; } return _rxn; } BaseReaction & IndigoSmilesReaction::getBaseReaction () { return getReaction(); } const char * IndigoSmilesReaction::getName () { if (getReaction().name.ptr() == 0) return ""; return getReaction().name.ptr(); } IndigoObject * IndigoSmilesReaction::clone () { return IndigoReaction::cloneFrom(*this); } CP_DEF(IndigoMultilineSmilesLoader); IndigoMultilineSmilesLoader::IndigoMultilineSmilesLoader (Scanner &scanner) : IndigoObject(MULTILINE_SMILES_LOADER), CP_INIT, TL_CP_GET(_offsets) { _own_scanner = false; _scanner = &scanner; _current_number = 0; _max_offset = 0; _offsets.clear(); } IndigoMultilineSmilesLoader::IndigoMultilineSmilesLoader (const char *filename) : IndigoObject(MULTILINE_SMILES_LOADER), CP_INIT, TL_CP_GET(_offsets) { _scanner = 0; _scanner = new FileScanner(indigoGetInstance().filename_encoding, filename); _own_scanner = true; _current_number = 0; _max_offset = 0; _offsets.clear(); } IndigoMultilineSmilesLoader::~IndigoMultilineSmilesLoader () { if (_own_scanner) delete _scanner; } void IndigoMultilineSmilesLoader::_advance () { _offsets.expand(_current_number + 1); _offsets[_current_number++] = _scanner->tell(); _scanner->readLine(_str, false); if (_scanner->tell() > _max_offset) _max_offset = _scanner->tell(); } IndigoObject * IndigoMultilineSmilesLoader::next () { if (_scanner->isEOF()) return 0; int offset = _scanner->tell(); int counter = _current_number; _advance(); if (_str.find('>') == -1) return new IndigoSmilesMolecule(_str, counter, offset); else return new IndigoSmilesReaction(_str, counter, offset); } bool IndigoMultilineSmilesLoader::hasNext () { return !_scanner->isEOF(); } int IndigoMultilineSmilesLoader::tell () { return _scanner->tell(); } int IndigoMultilineSmilesLoader::count () { int offset = _scanner->tell(); int cn = _current_number; if (offset != _max_offset) { _scanner->seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); } while (!_scanner->isEOF()) _advance(); int res = _current_number; if (res != cn) { _scanner->seek(offset, SEEK_SET); _current_number = cn; } return res; } IndigoObject * IndigoMultilineSmilesLoader::at (int index) { if (index < _offsets.size()) { _scanner->seek(_offsets[index], SEEK_SET); _current_number = index; return next(); } _scanner->seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); while (index > _offsets.size()) _advance(); return next(); } CEXPORT int indigoIterateSDF (int reader) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(reader); return self.addObject(new IndigoSdfLoader(IndigoScanner::get(obj))); } INDIGO_END(-1) } CEXPORT int indigoIterateRDF (int reader) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(reader); return self.addObject(new IndigoRdfLoader(IndigoScanner::get(obj))); } INDIGO_END(-1) } CEXPORT int indigoIterateSmiles (int reader) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(reader); return self.addObject(new IndigoMultilineSmilesLoader(IndigoScanner::get(obj))); } INDIGO_END(-1) } CEXPORT int indigoTell (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (obj.type == IndigoObject::SDF_LOADER) return ((IndigoSdfLoader &)obj).tell(); if (obj.type == IndigoObject::RDF_LOADER) return ((IndigoRdfLoader &)obj).tell(); if (obj.type == IndigoObject::MULTILINE_SMILES_LOADER) return ((IndigoMultilineSmilesLoader &)obj).tell(); if (obj.type == IndigoObject::RDF_MOLECULE || obj.type == IndigoObject::RDF_REACTION || obj.type == IndigoObject::SMILES_MOLECULE || obj.type == IndigoObject::SMILES_REACTION) return ((IndigoRdfData &)obj).tell(); if (obj.type == IndigoObject::MULTIPLE_CML_LOADER) return ((IndigoMultipleCmlLoader &)obj).tell(); if (obj.type == IndigoObject::CML_MOLECULE) return ((IndigoCmlMolecule &)obj).tell(); if (obj.type == IndigoObject::CML_REACTION) return ((IndigoCmlReaction &)obj).tell(); if (obj.type == IndigoObject::MULTIPLE_CDX_LOADER) return ((IndigoMultipleCdxLoader &)obj).tell(); if (obj.type == IndigoObject::CDX_MOLECULE) return ((IndigoCdxMolecule &)obj).tell(); if (obj.type == IndigoObject::CDX_REACTION) return ((IndigoCdxReaction &)obj).tell(); throw IndigoError("indigoTell(): not applicable to %s", obj.debugInfo()); } INDIGO_END(-1) } CEXPORT int indigoIterateSDFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoSdfLoader(filename)); } INDIGO_END(-1) } CEXPORT int indigoIterateRDFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoRdfLoader(filename)); } INDIGO_END(-1) } CEXPORT int indigoIterateSmilesFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoMultilineSmilesLoader(filename)); } INDIGO_END(-1) } IndigoCmlMolecule::IndigoCmlMolecule (Array &data, int index, int offset) : IndigoRdfData(CML_MOLECULE, data, index, offset) { } IndigoCmlMolecule::~IndigoCmlMolecule () { } Molecule & IndigoCmlMolecule::getMolecule () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); MoleculeCmlLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.loadMolecule(_mol); _loaded = true; } return _mol; } BaseMolecule & IndigoCmlMolecule::getBaseMolecule () { return getMolecule(); } const char * IndigoCmlMolecule::getName () { return getMolecule().name.ptr(); } IndigoObject * IndigoCmlMolecule::clone () { return IndigoMolecule::cloneFrom(*this); } const char * IndigoCmlMolecule::debugInfo () { return ""; } IndigoCmlReaction::IndigoCmlReaction (Array &data, int index, int offset) : IndigoRdfData(CML_REACTION, data, index, offset) { } IndigoCmlReaction::~IndigoCmlReaction () { } Reaction & IndigoCmlReaction::getReaction () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); ReactionCmlLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.loadReaction(_rxn); _loaded = true; } return _rxn; } BaseReaction & IndigoCmlReaction::getBaseReaction () { return getReaction(); } const char * IndigoCmlReaction::getName () { return getReaction().name.ptr(); } IndigoObject * IndigoCmlReaction::clone () { return IndigoReaction::cloneFrom(*this); } const char * IndigoCmlReaction::debugInfo () { return ""; } IndigoMultipleCmlLoader::IndigoMultipleCmlLoader (Scanner &scanner) : IndigoObject(MULTIPLE_CML_LOADER) { _own_scanner = 0; loader = new MultipleCmlLoader(scanner); } IndigoMultipleCmlLoader::IndigoMultipleCmlLoader (const char *filename) : IndigoObject(MULTIPLE_CML_LOADER) { _own_scanner = new FileScanner(filename); loader = new MultipleCmlLoader(*_own_scanner); } IndigoMultipleCmlLoader::~IndigoMultipleCmlLoader () { delete loader; if (_own_scanner != 0) delete _own_scanner; } int IndigoMultipleCmlLoader::tell () { return loader->tell(); } bool IndigoMultipleCmlLoader::hasNext () { return !loader->isEOF(); } IndigoObject * IndigoMultipleCmlLoader::next () { if (!hasNext()) return 0; int counter = loader->currentNumber(); int offset = loader->tell(); loader->readNext(); if (loader->isReaction()) return new IndigoCmlReaction(loader->data, counter, offset); else return new IndigoCmlMolecule(loader->data, counter, offset); } CEXPORT int indigoIterateCML (int reader) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(reader); return self.addObject(new IndigoMultipleCmlLoader(IndigoScanner::get(obj))); } INDIGO_END(-1) } CEXPORT int indigoIterateCMLFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoMultipleCmlLoader(filename)); } INDIGO_END(-1) } IndigoCdxMolecule::IndigoCdxMolecule (Array &data, PropertiesMap &properties, int index, int offset) : IndigoRdfData(CDX_MOLECULE, data, properties, index, offset) { } IndigoCdxMolecule::~IndigoCdxMolecule () { } Molecule & IndigoCdxMolecule::getMolecule () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); MoleculeCdxLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.loadMolecule(_mol); _loaded = true; } return _mol; } BaseMolecule & IndigoCdxMolecule::getBaseMolecule () { return getMolecule(); } const char * IndigoCdxMolecule::getName () { return getMolecule().name.ptr(); } IndigoObject * IndigoCdxMolecule::clone () { return IndigoMolecule::cloneFrom(*this); } const char * IndigoCdxMolecule::debugInfo () { return ""; } IndigoCdxReaction::IndigoCdxReaction (Array &data, PropertiesMap &properties, int index, int offset) : IndigoRdfData(CDX_REACTION, data, properties, index, offset) { } IndigoCdxReaction::~IndigoCdxReaction () { } Reaction & IndigoCdxReaction::getReaction () { if (!_loaded) { Indigo &self = indigoGetInstance(); BufferScanner scanner(_data); ReactionCdxLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.loadReaction(_rxn); _loaded = true; } return _rxn; } BaseReaction & IndigoCdxReaction::getBaseReaction () { return getReaction(); } const char * IndigoCdxReaction::getName () { return getReaction().name.ptr(); } IndigoObject * IndigoCdxReaction::clone () { return IndigoReaction::cloneFrom(*this); } const char * IndigoCdxReaction::debugInfo () { return ""; } IndigoMultipleCdxLoader::IndigoMultipleCdxLoader (Scanner &scanner) : IndigoObject(MULTIPLE_CDX_LOADER) { _own_scanner = 0; loader = new MultipleCdxLoader(scanner); } IndigoMultipleCdxLoader::IndigoMultipleCdxLoader (const char *filename) : IndigoObject(MULTIPLE_CDX_LOADER) { _own_scanner = new FileScanner(filename); loader = new MultipleCdxLoader(*_own_scanner); } IndigoMultipleCdxLoader::~IndigoMultipleCdxLoader () { delete loader; if (_own_scanner != 0) delete _own_scanner; } int IndigoMultipleCdxLoader::tell () { return loader->tell(); } bool IndigoMultipleCdxLoader::hasNext () { return !loader->isEOF(); } IndigoObject * IndigoMultipleCdxLoader::next () { if (!hasNext()) return 0; int counter = loader->currentNumber(); int offset = loader->tell(); loader->readNext(); if (loader->isReaction()) return new IndigoCdxReaction(loader->data, loader->properties, counter, offset); else return new IndigoCdxMolecule(loader->data, loader->properties, counter, offset); } IndigoObject * IndigoMultipleCdxLoader::at (int index) { loader->readAt(index); if (loader->isReaction()) return new IndigoCdxReaction(loader->data, loader->properties, index, 0); else return new IndigoCdxMolecule(loader->data, loader->properties, index, 0); } CEXPORT int indigoIterateCDX (int reader) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(reader); return self.addObject(new IndigoMultipleCdxLoader(IndigoScanner::get(obj))); } INDIGO_END(-1) } CEXPORT int indigoIterateCDXFile (const char *filename) { INDIGO_BEGIN { return self.addObject(new IndigoMultipleCdxLoader(filename)); } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_loaders.h000066400000000000000000000150651271037650300207030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_loaders__ #define __indigo_loaders__ #include "indigo_internal.h" #include "molecule/molecule.h" #include "reaction/reaction.h" #include "base_cpp/properties_map.h" class IndigoRdfData : public IndigoObject { public: IndigoRdfData (int type, Array &data, int index, int offset); IndigoRdfData (int type, Array &data, PropertiesMap &properties, int index, int offset); virtual ~IndigoRdfData (); Array & getRawData (); // virtual RedBlackStringObjMap< Array > * getProperties () {return &_properties.getProperties();} virtual PropertiesMap& getProperties(){return _properties;} virtual int getIndex (); int tell (); protected: Array _data; PropertiesMap _properties; bool _loaded; int _index; int _offset; }; class IndigoRdfMolecule : public IndigoRdfData { public: IndigoRdfMolecule (Array &data, PropertiesMap &properties, int index, int offset); virtual ~IndigoRdfMolecule (); virtual Molecule & getMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual const char * getName (); virtual IndigoObject * clone (); protected: Molecule _mol; }; class IndigoRdfReaction : public IndigoRdfData { public: IndigoRdfReaction (Array &data, PropertiesMap &properties, int index, int offset); virtual ~IndigoRdfReaction (); virtual Reaction & getReaction (); virtual BaseReaction & getBaseReaction (); virtual const char * getName (); virtual IndigoObject * clone (); protected: Reaction _rxn; }; class IndigoSdfLoader : public IndigoObject { public: IndigoSdfLoader (Scanner &scanner); IndigoSdfLoader (const char *filename); virtual ~IndigoSdfLoader (); virtual IndigoObject * next (); virtual bool hasNext (); IndigoObject * at (int index); int tell (); SdfLoader *sdf_loader; protected: Scanner *_own_scanner; }; class IndigoRdfLoader : public IndigoObject { public: IndigoRdfLoader (Scanner &scanner); IndigoRdfLoader (const char *filename); virtual ~IndigoRdfLoader (); virtual IndigoObject * next (); virtual bool hasNext (); IndigoObject * at (int index); int tell (); RdfLoader *rdf_loader; protected: Scanner *_own_scanner; }; class IndigoSmilesMolecule : public IndigoRdfData { public: IndigoSmilesMolecule (Array &smiles, int index, int offset); virtual ~IndigoSmilesMolecule (); virtual Molecule & getMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual const char * getName (); virtual IndigoObject * clone (); protected: Molecule _mol; }; class IndigoSmilesReaction : public IndigoRdfData { public: IndigoSmilesReaction (Array &data, int index, int offset); virtual ~IndigoSmilesReaction (); virtual Reaction & getReaction (); virtual BaseReaction & getBaseReaction (); virtual const char * getName (); virtual IndigoObject * clone (); protected: Reaction _rxn; }; class IndigoMultilineSmilesLoader : public IndigoObject { public: IndigoMultilineSmilesLoader (Scanner &scanner); IndigoMultilineSmilesLoader (const char *filename); virtual ~IndigoMultilineSmilesLoader (); int tell (); virtual IndigoObject * next (); virtual bool hasNext (); IndigoObject * at (int index); int count (); protected: Scanner *_scanner; Array _str; bool _own_scanner; void _advance (); CP_DECL; TL_CP_DECL(Array, _offsets); int _current_number; int _max_offset; }; namespace indigo { class MultipleCmlLoader; } class IndigoCmlMolecule : public IndigoRdfData { public: IndigoCmlMolecule (Array &data_, int index, int offset); virtual ~IndigoCmlMolecule (); virtual Molecule & getMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual const char * getName (); virtual IndigoObject * clone (); virtual const char * debugInfo (); protected: Molecule _mol; }; class IndigoCmlReaction : public IndigoRdfData { public: IndigoCmlReaction (Array &data_, int index, int offset); virtual ~IndigoCmlReaction (); virtual Reaction & getReaction (); virtual BaseReaction & getBaseReaction (); virtual const char * getName (); virtual IndigoObject * clone (); virtual const char * debugInfo (); protected: Reaction _rxn; }; class IndigoMultipleCmlLoader : public IndigoObject { public: IndigoMultipleCmlLoader (Scanner &scanner); IndigoMultipleCmlLoader (const char *filename); virtual ~IndigoMultipleCmlLoader (); virtual IndigoObject * next (); virtual bool hasNext (); IndigoObject * at (int index); int tell (); MultipleCmlLoader *loader; protected: Scanner *_own_scanner; }; namespace indigo { class MultipleCdxLoader; } class IndigoCdxMolecule : public IndigoRdfData { public: IndigoCdxMolecule (Array &data_, PropertiesMap &properties, int index, int offset); virtual ~IndigoCdxMolecule (); virtual Molecule & getMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual const char * getName (); virtual IndigoObject * clone (); virtual const char * debugInfo (); protected: Molecule _mol; }; class IndigoCdxReaction : public IndigoRdfData { public: IndigoCdxReaction (Array &data_, PropertiesMap &properties, int index, int offset); virtual ~IndigoCdxReaction (); virtual Reaction & getReaction (); virtual BaseReaction & getBaseReaction (); virtual const char * getName (); virtual IndigoObject * clone (); virtual const char * debugInfo (); protected: Reaction _rxn; }; class IndigoMultipleCdxLoader : public IndigoObject { public: IndigoMultipleCdxLoader (Scanner &scanner); IndigoMultipleCdxLoader (const char *filename); virtual ~IndigoMultipleCdxLoader (); virtual IndigoObject * next (); virtual bool hasNext (); IndigoObject * at (int index); int tell (); MultipleCdxLoader *loader; protected: Scanner *_own_scanner; }; #endif Indigo-indigo-1.2.3/api/src/indigo_macros.c000066400000000000000000000114211271037650300205210ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo.h" #define WRAPPER_LOAD_FROM_STRING(name) \ CEXPORT int name##FromString (const char *string) \ { \ int source = indigoReadString(string); \ int result; \ \ if (source <= 0) \ return -1; \ \ result = name(source); \ indigoFree(source); \ return result; \ } #define WRAPPER_LOAD_FROM_FILE(name) \ CEXPORT int name##FromFile(const char *filename) \ { \ int source = indigoReadFile(filename); \ int result; \ \ if (source <= 0) \ return -1; \ \ result = name(source); \ indigoFree(source); \ return result; \ } #define WRAPPER_LOAD_FROM_BUFFER(name) \ CEXPORT int name##FromBuffer(const char *buf, int size) \ { \ int source = indigoReadBuffer(buf, size); \ int result; \ \ if (source <= 0) \ return -1; \ \ result = name(source); \ indigoFree(source); \ return result; \ } WRAPPER_LOAD_FROM_STRING(indigoLoadMolecule) WRAPPER_LOAD_FROM_FILE(indigoLoadMolecule) WRAPPER_LOAD_FROM_BUFFER(indigoLoadMolecule) WRAPPER_LOAD_FROM_STRING(indigoLoadQueryMolecule) WRAPPER_LOAD_FROM_FILE(indigoLoadQueryMolecule) WRAPPER_LOAD_FROM_BUFFER(indigoLoadQueryMolecule) WRAPPER_LOAD_FROM_STRING(indigoLoadSmarts) WRAPPER_LOAD_FROM_FILE(indigoLoadSmarts) WRAPPER_LOAD_FROM_BUFFER(indigoLoadSmarts) WRAPPER_LOAD_FROM_STRING(indigoLoadReaction) WRAPPER_LOAD_FROM_FILE(indigoLoadReaction) WRAPPER_LOAD_FROM_BUFFER(indigoLoadReaction) WRAPPER_LOAD_FROM_STRING(indigoLoadQueryReaction) WRAPPER_LOAD_FROM_FILE(indigoLoadQueryReaction) WRAPPER_LOAD_FROM_BUFFER(indigoLoadQueryReaction) WRAPPER_LOAD_FROM_STRING(indigoLoadReactionSmarts) WRAPPER_LOAD_FROM_FILE(indigoLoadReactionSmarts) WRAPPER_LOAD_FROM_BUFFER(indigoLoadReactionSmarts) CEXPORT int indigoSaveMolfileToFile (int molecule, const char *filename) { int f = indigoWriteFile(filename); int res; if (f == -1) return -1; res = indigoSaveMolfile(molecule, f); indigoFree(f); return res; } CEXPORT int indigoSaveCmlToFile (int molecule, const char *filename) { int f = indigoWriteFile(filename); int res; if (f == -1) return -1; res = indigoSaveCml(molecule, f); indigoFree(f); return res; } CEXPORT const char * indigoMolfile (int molecule) { int b = indigoWriteBuffer(); const char *res; if (b == -1) return 0; if (indigoSaveMolfile(molecule, b) == -1) return 0; res = indigoToString(b); indigoFree(b); return res; } CEXPORT const char * indigoCml (int molecule) { int b = indigoWriteBuffer(); const char *res; if (b == -1) return 0; if (indigoSaveCml(molecule, b) == -1) return 0; res = indigoToString(b); indigoFree(b); return res; } CEXPORT int indigoSaveRxnfileToFile (int reaction, const char *filename) { int f = indigoWriteFile(filename); int res; if (f == -1) return -1; res = indigoSaveRxnfile(reaction, f); indigoFree(f); return res; } CEXPORT const char * indigoRxnfile (int molecule) { int b = indigoWriteBuffer(); const char *res; if (b == -1) return 0; if (indigoSaveRxnfile(molecule, b) == -1) return 0; res = indigoToString(b); indigoFree(b); return res; } Indigo-indigo-1.2.3/api/src/indigo_mapping.cpp000066400000000000000000000153341271037650300212370ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_mapping.h" #include "indigo_molecule.h" #include "indigo_reaction.h" IndigoMapping::IndigoMapping (BaseMolecule &from_, BaseMolecule &to_) : IndigoObject(MAPPING), from(from_), to(to_) { } IndigoMapping::~IndigoMapping () { } IndigoObject * IndigoMapping::clone () { AutoPtr res_ptr(new IndigoMapping(from, to)); res_ptr->mapping.copy(mapping); return res_ptr.release(); } IndigoMapping & IndigoMapping::cast (IndigoObject &obj) { if (obj.type == IndigoObject::MAPPING) return (IndigoMapping &)obj; throw IndigoError("%s is not a mapping", obj.debugInfo()); } IndigoReactionMapping::IndigoReactionMapping (BaseReaction &from_, BaseReaction &to_) : IndigoObject(REACTION_MAPPING), from(from_), to(to_) { } IndigoReactionMapping::~IndigoReactionMapping () { } IndigoObject * IndigoReactionMapping::clone () { AutoPtr res_ptr(new IndigoReactionMapping(from, to)); res_ptr->mol_mapping.copy(mol_mapping); for (int i = 0; i < mappings.size(); ++i) { res_ptr->mappings.push().copy(mappings[i]); } return res_ptr.release(); } CEXPORT int indigoMapAtom (int handle, int atom) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (obj.type == IndigoObject::MAPPING) { IndigoMapping &mapping = (IndigoMapping &)obj; int mapped = mapping.mapping[ia.idx]; if (mapped < 0) return 0; return self.addObject(new IndigoAtom(mapping.to, mapped)); } if (obj.type == IndigoObject::REACTION_MAPPING) { IndigoReactionMapping &mapping = (IndigoReactionMapping &)obj; int mol_idx = mapping.from.findMolecule(&ia.mol); if (mol_idx == -1) throw IndigoError("indigoMapAtom(): input atom not found in the reaction"); if (mapping.mol_mapping[mol_idx] < 0) // can happen for catalysts return 0; BaseMolecule &mol = mapping.to.getBaseMolecule(mapping.mol_mapping[mol_idx]); int idx = mapping.mappings[mol_idx][ia.idx]; if (idx < 0) return 0; return self.addObject(new IndigoAtom(mol, idx)); } throw IndigoError("indigoMapAtom(): not applicable to %s", obj.debugInfo()); } INDIGO_END(-1) } CEXPORT int indigoMapBond (int handle, int bond) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); if (obj.type == IndigoObject::MAPPING) { IndigoMapping &mapping = (IndigoMapping &)obj; const Edge &edge = ib.mol.getEdge(ib.idx); int beg = mapping.mapping[edge.beg]; int end = mapping.mapping[edge.end]; if (beg < 0 || end < 0) return 0; int idx = mapping.to.findEdgeIndex(beg, end); if (idx < 0) return 0; return self.addObject(new IndigoBond(mapping.to, idx)); } if (obj.type == IndigoObject::REACTION_MAPPING) { IndigoReactionMapping &mapping = (IndigoReactionMapping &)obj; int mol_idx = mapping.from.findMolecule(&ib.mol); if (mol_idx == -1) throw IndigoError("indigoMapBond(): input bond not found in the reaction"); if (mapping.mol_mapping[mol_idx] < 0) return 0; // can happen with catalysts BaseMolecule &mol = mapping.to.getBaseMolecule(mapping.mol_mapping[mol_idx]); const Edge &edge = ib.mol.getEdge(ib.idx); int beg = mapping.mappings[mol_idx][edge.beg]; int end = mapping.mappings[mol_idx][edge.end]; if (beg < 0 || end < 0) return 0; int idx = mol.findEdgeIndex(beg, end); if (idx < 0) return 0; return self.addObject(new IndigoBond(mol, idx)); } throw IndigoError("indigoMapBond(): not applicable to %s", obj.debugInfo()); } INDIGO_END(-1) } void _indigoHighlightSubstructure (BaseMolecule &query, BaseMolecule &mol, Array &qmapping, Array &mapping) { int i; for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) { int mapped = qmapping[i]; if (mapped >= 0 && mapping[mapped] >= 0) mol.highlightAtom(mapping[mapped]); } for (i = query.edgeBegin(); i != query.edgeEnd(); i = query.edgeNext(i)) { const Edge &edge = query.getEdge(i); int beg = qmapping[edge.beg]; int end = qmapping[edge.end]; if (beg < 0 || end < 0) continue; int idx = mol.findEdgeIndex(mapping[beg], mapping[end]); if (idx >= 0) mol.highlightBond(idx); } } CEXPORT int indigoHighlightedTarget (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (obj.type == IndigoObject::MAPPING) { IndigoMapping &im = (IndigoMapping &)obj; AutoPtr mol(new IndigoMolecule()); QS_DEF(Array, mapping); mol->mol.clone(im.to, 0, &mapping); _indigoHighlightSubstructure(im.from, mol->mol, im.mapping, mapping); return self.addObject(mol.release()); } if (obj.type == IndigoObject::REACTION_MAPPING) { IndigoReactionMapping &im = (IndigoReactionMapping &)obj; AutoPtr rxn(new IndigoReaction()); QS_DEF(ObjArray< Array >, mappings); QS_DEF(Array, mol_mapping); int i; rxn->rxn.clone(im.to, &mol_mapping, 0, &mappings); for (i = im.from.begin(); i != im.from.end(); i = im.from.next(i)) { if (im.mol_mapping[i] < 0) // can happen with catalysts continue; _indigoHighlightSubstructure( im.from.getBaseMolecule(i), rxn->rxn.getBaseMolecule(mol_mapping[im.mol_mapping[i]]), im.mappings[i], mappings[im.mol_mapping[i]]); } return self.addObject(rxn.release()); } throw IndigoError("indigoHighlightedTarget(): no idea what to do with %s", obj.debugInfo()); } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_mapping.h000066400000000000000000000025131271037650300206770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_mapping__ #define __indigo_mapping__ #include "indigo_internal.h" class IndigoMapping : public IndigoObject { public: IndigoMapping (BaseMolecule &from, BaseMolecule &to); virtual ~IndigoMapping (); static IndigoMapping & cast (IndigoObject &obj); BaseMolecule &from; BaseMolecule &to; Array mapping; virtual IndigoObject * clone (); protected: }; class IndigoReactionMapping : public IndigoObject { public: IndigoReactionMapping (BaseReaction &from, BaseReaction &to); virtual ~IndigoReactionMapping (); BaseReaction &from; BaseReaction &to; Array< int > mol_mapping; ObjArray< Array > mappings; virtual IndigoObject * clone (); }; #endif Indigo-indigo-1.2.3/api/src/indigo_match.cpp000066400000000000000000001034101271037650300206710ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_match.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "reaction/reaction_substructure_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "reaction/reaction_exact_matcher.h" #include "molecule/molecule_tautomer_matcher.h" #include "base_cpp/scanner.h" #include "indigo_mapping.h" #include "molecule/elements.h" #include "reaction/reaction_automapper.h" void _indigoParseTauCondition (const char *list_ptr, int &aromaticity, Array &label_list) { if (list_ptr == 0 || *list_ptr == 0) throw IndigoError("null or empty tautomer rule description is not allowed"); if (isdigit(*list_ptr)) { if (*list_ptr == '1') aromaticity = 1; else if (*list_ptr == '0') aromaticity = 0; else throw IndigoError("bad tautomer rule format"); list_ptr++; } else { aromaticity = -1; } label_list.clear(); QS_DEF(Array, buf); buf.clear(); while (*list_ptr != 0) { if (isalpha((unsigned)*list_ptr)) buf.push(*list_ptr); else if (*list_ptr == ',') { buf.push(0); label_list.push(Element::fromString(buf.ptr())); buf.clear(); } else throw IndigoError("bad label list format in the tautomer rule"); list_ptr++; } buf.push(0); label_list.push(Element::fromString(buf.ptr())); } CEXPORT int indigoSetTautomerRule (int n, const char *beg, const char *end) { INDIGO_BEGIN { if (n < 1 || n >= 32) throw IndigoError("tautomer rule index %d is out of range", n); self.tautomer_rules.expand(n); if (self.tautomer_rules[n - 1] != 0) { delete self.tautomer_rules[n - 1]; self.tautomer_rules[n - 1] = 0; } AutoPtr rule(new TautomerRule()); _indigoParseTauCondition(beg, rule->aromaticity1, rule->list1); _indigoParseTauCondition(end, rule->aromaticity2, rule->list2); self.tautomer_rules.set(n - 1, rule.release()); return 1; } INDIGO_END(-1) } CEXPORT int indigoClearTautomerRules () { INDIGO_BEGIN { self.tautomer_rules.clear(); return 1; } INDIGO_END(-1) } CEXPORT int indigoRemoveTautomerRule (int n) { INDIGO_BEGIN { self.tautomer_rules.remove(n - 1); return 1; } INDIGO_END(-1) } DLLEXPORT bool _indigoParseTautomerFlags (const char *flags, IndigoTautomerParams ¶ms) { if (flags == 0) return false; BufferScanner scanner(flags); scanner.skipSpace(); QS_DEF(Array, word); if (scanner.isEOF()) return false; scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "TAU") != 0) return false; MoleculeTautomerMatcher::parseConditions(flags, params.conditions, params.force_hydrogens, params.ring_chain, params.method); return true; } DLLEXPORT int _indigoParseExactFlags (const char *flags, bool reaction, float *rms_threshold) { if (flags == 0) throw IndigoError("_indigoParseExactFlags(): zero string pointer"); if (!reaction && rms_threshold == 0) throw IndigoError("_indigoParseExactFlags(): zero float pointer"); static struct { const char *token; int type; // 1 -- molecule, 2 -- reaction, 3 -- both int value; } token_list[] = { {"ELE", 3, MoleculeExactMatcher::CONDITION_ELECTRONS}, {"MAS", 3, MoleculeExactMatcher::CONDITION_ISOTOPE}, {"STE", 3, MoleculeExactMatcher::CONDITION_STEREO}, {"FRA", 1, MoleculeExactMatcher::CONDITION_FRAGMENTS}, {"AAM", 2, ReactionExactMatcher::CONDITION_AAM}, {"RCT", 2, ReactionExactMatcher::CONDITION_REACTING_CENTERS} }; int i, res = 0, count = 0; bool had_float = false; bool had_none = false; bool had_all = false; if (!reaction) *rms_threshold = 0; BufferScanner scanner(flags); QS_DEF(Array, word); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; if (had_float) throw IndigoError("_indigoParseExactFlags(): no value is allowed after the number"); scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "NONE") == 0) { if (had_all) throw IndigoError("_indigoParseExactFlags(): NONE conflicts with ALL"); had_none = true; count++; continue; } if (strcasecmp(word.ptr(), "ALL") == 0) { if (had_none) throw IndigoError("_indigoParseExactFlags(): ALL conflicts with NONE"); had_all = true; res = MoleculeExactMatcher::CONDITION_ALL; if (reaction) res |= ReactionExactMatcher::CONDITION_ALL; count++; continue; } if (strcasecmp(word.ptr(), "TAU") == 0) // should have been handled before throw IndigoError("_indigoParseExactFlags(): no flags are allowed together with TAU"); for (i = 0; i < NELEM(token_list); i++) { if (strcasecmp(token_list[i].token, word.ptr()) == 0) { if (!reaction && !(token_list[i].type & 1)) throw IndigoError("_indigoParseExactFlags(): %s flag is allowed only for reaction matching", word.ptr()); if (reaction && !(token_list[i].type & 2)) throw IndigoError("_indigoParseExactFlags(): %s flag is allowed only for molecule matching", word.ptr()); if (had_all) throw IndigoError("_indigoParseExactFlags(): only negative flags are allowed together with ALL"); res |= token_list[i].value; break; } if (word[0] == '-' && strcasecmp(token_list[i].token, word.ptr() + 1) == 0) { if (!reaction && !(token_list[i].type & 1)) throw IndigoError("_indigoParseExactFlags(): %s flag is allowed only for reaction matching", word.ptr()); if (reaction && !(token_list[i].type & 2)) throw IndigoError("_indigoParseExactFlags(): %s flag is allowed only for molecule matching", word.ptr()); res &= ~token_list[i].value; break; } } if (i == NELEM(token_list)) { BufferScanner scanner2(word.ptr()); if (!reaction && scanner2.tryReadFloat(*rms_threshold)) { res |= MoleculeExactMatcher::CONDITION_3D; had_float = true; } else throw IndigoError("_indigoParseExactFlags(): unknown token %s", word.ptr()); } else count++; } if (had_none && count > 1) throw IndigoError("_indigoParseExactFlags(): no flags are allowed together with NONE"); if (count == 0) res |= MoleculeExactMatcher::CONDITION_ALL | ReactionExactMatcher::CONDITION_ALL; return res; } CEXPORT int indigoExactMatch (int handler1, int handler2, const char *flags) { INDIGO_BEGIN { IndigoObject &obj1 = self.getObject(handler1); IndigoObject &obj2 = self.getObject(handler2); if (flags == 0) flags = ""; if (IndigoBaseMolecule::is(obj1)) { Molecule &mol1 = obj1.getMolecule(); Molecule &mol2 = obj2.getMolecule(); IndigoTautomerParams params; AutoPtr mapping(new IndigoMapping(mol1, mol2)); if (_indigoParseTautomerFlags(flags, params)) { MoleculeTautomerMatcher matcher(mol2, false); matcher.arom_options = self.arom_options; matcher.setRulesList(&self.tautomer_rules); matcher.setRules(params.conditions, params.force_hydrogens, params.ring_chain, params.method); matcher.setQuery(mol1); if (!matcher.find()) return 0; mapping->mapping.copy(matcher.getQueryMapping(), mol1.vertexEnd()); return self.addObject(mapping.release()); } MoleculeExactMatcher matcher(mol1, mol2); matcher.flags = _indigoParseExactFlags(flags, false, &matcher.rms_threshold); if (!matcher.find()) return 0; mapping->mapping.copy(matcher.getQueryMapping(), mol1.vertexEnd()); return self.addObject(mapping.release()); } if (IndigoBaseReaction::is(obj1)) { Reaction &rxn1 = obj1.getReaction(); Reaction &rxn2 = obj2.getReaction(); int i; ReactionExactMatcher matcher(rxn1, rxn2); matcher.flags = _indigoParseExactFlags(flags, true, 0); if (!matcher.find()) return 0; AutoPtr mapping(new IndigoReactionMapping(rxn1, rxn2)); mapping->mol_mapping.clear_resize(rxn1.end()); mapping->mol_mapping.fffill(); mapping->mappings.expand(rxn1.end()); for (i = rxn1.begin(); i != rxn1.end(); i = rxn1.next(i)) { if (rxn1.getSideType(i) == BaseReaction::CATALYST) continue; mapping->mol_mapping[i] = matcher.getTargetMoleculeIndex(i); mapping->mappings[i].copy(matcher.getQueryMoleculeMapping(i), rxn1.getBaseMolecule(i).vertexEnd()); } return self.addObject(mapping.release()); } throw IndigoError("indigoExactMatch(): %s is neither a molecule nor a reaction", obj1.debugInfo()); } INDIGO_END(-1); } IndigoMoleculeSubstructureMatchIter::IndigoMoleculeSubstructureMatchIter (Molecule &target_, QueryMolecule &query_, Molecule &original_target_, bool resonance, bool disable_folding_query_h) : IndigoObject(MOLECULE_SUBSTRUCTURE_MATCH_ITER), matcher(target_), target(target_), original_target(original_target_), query(query_) { matcher.disable_folding_query_h = disable_folding_query_h; matcher.setQuery(query); matcher.fmcache = &fmcache; matcher.use_pi_systems_matcher = resonance; _initialized = false; _found = false; _need_find = true; _embedding_index = 0; } IndigoMoleculeSubstructureMatchIter::~IndigoMoleculeSubstructureMatchIter () { } const char * IndigoMoleculeSubstructureMatchIter::debugInfo () { return ""; } IndigoObject * IndigoMoleculeSubstructureMatchIter::next () { if (!hasNext()) return 0; AutoPtr mptr(new IndigoMapping(query, original_target)); // Expand mapping to fit possible implicit hydrogens mapping.expandFill(target.vertexEnd(), -1); if (!matcher.getEmbeddingsStorage().isEmpty()) { const GraphEmbeddingsStorage& storage = matcher.getEmbeddingsStorage(); int count; const int *query_mapping = storage.getMappingSub(_embedding_index, count); mptr->mapping.copy(query_mapping, query.vertexEnd()); } else mptr->mapping.copy(matcher.getQueryMapping(), query.vertexEnd()); for (int v = query.vertexBegin(); v != query.vertexEnd(); v = query.vertexNext(v)) { int mapped = mptr->mapping[v]; if (mapped >= 0) mptr->mapping[v] = mapping[mapped]; } _need_find = true; return mptr.release(); } bool IndigoMoleculeSubstructureMatchIter::hasNext () { if (!_need_find) return _found; if (!_initialized) { _initialized = true; _found = matcher.find(); } else { _embedding_index++; int cur_count = matcher.getEmbeddingsStorage().count(); if (_embedding_index < cur_count) _found = true; else _found = matcher.findNext(); } if (_embedding_index >= max_embeddings) throw IndigoError("Number of embeddings exceeded maximum allowed limit (%d). " "Adjust options to raise this limit.", max_embeddings); _need_find = false; return _found; } IndigoTautomerSubstructureMatchIter::IndigoTautomerSubstructureMatchIter(Molecule &target_, QueryMolecule &query_, Molecule &tautomerFound_, TautomerMethod method) : IndigoObject(MOLECULE_SUBSTRUCTURE_MATCH_ITER), matcher(target_, method), query(query_), tautomerFound(tautomerFound_) { matcher.setQuery(query); _initialized = false; _found = false; _need_find = true; _embedding_index = 0; _mask_index = 0; } IndigoTautomerSubstructureMatchIter::~IndigoTautomerSubstructureMatchIter() { } const char * IndigoTautomerSubstructureMatchIter::debugInfo() { return ""; } IndigoObject * IndigoTautomerSubstructureMatchIter::next() { if (!hasNext()) return NULL; matcher.getTautomerFound(tautomerFound, _embedding_index, _mask_index); AutoPtr mptr(new IndigoMapping(query, tautomerFound)); // Expand mapping to fit possible implicit hydrogens mapping.expandFill(tautomerFound.vertexEnd(), -1); if (!matcher.getEmbeddingsStorage().isEmpty()) { const GraphEmbeddingsStorage& storage = matcher.getEmbeddingsStorage(); int count; const int *query_mapping = storage.getMappingSub(_embedding_index, count); mptr->mapping.copy(query_mapping, query.vertexEnd()); } else mptr->mapping.copy(matcher.getQueryMapping(), query.vertexEnd()); for (int v = query.vertexBegin(); v != query.vertexEnd(); v = query.vertexNext(v)) { int mapped = mptr->mapping[v]; if (mapped >= 0) mptr->mapping[v] = mapping[mapped]; } _need_find = true; return mptr.release(); } bool IndigoTautomerSubstructureMatchIter::hasNext() { if (!_need_find) return _found; if (!_initialized) { _initialized = true; _found = matcher.find(); if (_found) { _embedding_index = 0; _mask_index = matcher.getMask(_embedding_index).nextSetBit(0); } } else { int cur_count = matcher.getEmbeddingsStorage().count(); _mask_index = matcher.getMask(_embedding_index).nextSetBit(_mask_index + 1); if (_mask_index == -1) { ++_embedding_index; } if (_embedding_index < cur_count) _found = true; else { _found = matcher.findNext(); if (_found) { _mask_index = matcher.getMask(_embedding_index).nextSetBit(0); } } } if (_embedding_index >= max_embeddings) throw IndigoError("Number of embeddings exceeded maximum allowed limit (%d). " "Adjust options to raise this limit.", max_embeddings); _need_find = false; return _found; } struct MatchCountContext { int embeddings_count, max_count; }; static bool _matchCountEmbeddingsCallback (Graph &sub, Graph &super, const int *core1, const int *core2, void *context_) { MatchCountContext *context = (MatchCountContext *)context_; context->embeddings_count++; if (context->embeddings_count >= context->max_count) return false; return true; } int IndigoMoleculeSubstructureMatchIter::countMatches (int embeddings_limit) { if (max_embeddings <= 0) throw IndigoError("Maximum allowed embeddings limit must be positive " "Adjust options to raise this limit."); MatchCountContext context; context.embeddings_count = 0; if (embeddings_limit != 0) context.max_count = __min(max_embeddings, embeddings_limit); else context.max_count = max_embeddings; matcher.find_all_embeddings = true; matcher.cb_embedding = _matchCountEmbeddingsCallback; matcher.cb_embedding_context = &context; matcher.find(); if (embeddings_limit != 0 && context.embeddings_count >= embeddings_limit) return embeddings_limit; if (context.embeddings_count >= max_embeddings) throw IndigoError("Number of embeddings exceeded maximum allowed limit (%d). " "Adjust options to raise this limit.", max_embeddings); return context.embeddings_count; } IndigoMoleculeSubstructureMatcher::IndigoMoleculeSubstructureMatcher (Molecule &target, int mode_) : IndigoObject(MOLECULE_SUBSTRUCTURE_MATCHER), target(target) { _arom_h_unfolded_prepared = false; _arom_prepared = false; _aromatized = false; mode = mode_; } IndigoMoleculeSubstructureMatcher::~IndigoMoleculeSubstructureMatcher () { } const char * IndigoMoleculeSubstructureMatcher::debugInfo () { return ""; } void IndigoMoleculeSubstructureMatcher::ignoreAtom (int atom_index) { _ignored_atoms.push(atom_index); } void IndigoMoleculeSubstructureMatcher::unignoreAtom (int atom_index) { int pos = _ignored_atoms.find(atom_index); if (pos == -1) throw IndigoError("Atom with index %d wasn't ignored", atom_index); _ignored_atoms.remove(pos); } void IndigoMoleculeSubstructureMatcher::unignoreAllAtoms () { _ignored_atoms.clear(); } IndigoMoleculeSubstructureMatchIter* IndigoMoleculeSubstructureMatcher::iterateQueryMatches (IndigoObject &query_object, bool embedding_edges_uniqueness, bool find_unique_embeddings, bool for_iteration, int max_embeddings) { QueryMolecule &query = query_object.getQueryMolecule(); Molecule *target_prepared; Array *mapping; bool *prepared; MoleculeAtomNeighbourhoodCounters *nei_counters; // If max_embeddings is 1 then it is only check for substructure // and not enumeration of number of matches if (MoleculeSubstructureMatcher::shouldUnfoldTargetHydrogens(query, max_embeddings != 1)) { if (!_arom_h_unfolded_prepared) _target_arom_h_unfolded.clone(target, &_mapping_arom_h_unfolded, 0); target_prepared = &_target_arom_h_unfolded; mapping = &_mapping_arom_h_unfolded; prepared = &_arom_h_unfolded_prepared; nei_counters = &_nei_counters_h_unfolded; } else { if (!_arom_prepared) _target_arom.clone(target, &_mapping_arom, 0); target_prepared = &_target_arom; mapping = &_mapping_arom; prepared = &_arom_prepared; nei_counters = &_nei_counters; } if (!*prepared) { if (!target.isAromatized()) { Indigo &indigo = indigoGetInstance(); target_prepared->aromatize(indigo.arom_options); } nei_counters->calculate(*target_prepared); *prepared = true; } AutoPtr iter(new IndigoMoleculeSubstructureMatchIter(*target_prepared, query, target, (mode == RESONANCE), max_embeddings != 1)); if (query_object.type == IndigoObject::QUERY_MOLECULE) { IndigoQueryMolecule &qm_object = (IndigoQueryMolecule &)query_object; iter->matcher.setNeiCounters(&qm_object.getNeiCounters(), nei_counters); } Indigo &indigo = indigoGetInstance(); iter->matcher.arom_options = indigo.arom_options; iter->matcher.find_unique_embeddings = find_unique_embeddings; iter->matcher.find_unique_by_edges = embedding_edges_uniqueness; iter->matcher.save_for_iteration = for_iteration; for (int i = 0; i < _ignored_atoms.size(); i++) iter->matcher.ignoreTargetAtom(mapping->at(_ignored_atoms[i])); iter->matcher.restore_unfolded_h = false; iter->mapping.copy(*mapping); iter->max_embeddings = max_embeddings; return iter.release(); } IndigoTautomerSubstructureMatchIter* IndigoMoleculeSubstructureMatcher::iterateTautomerQueryMatches (IndigoObject &query_object, bool embedding_edges_uniqueness, bool find_unique_embeddings, bool for_iteration, int max_embeddings, TautomerMethod method) { QueryMolecule &query = query_object.getQueryMolecule(); Molecule *target_prepared; Array *mapping; bool *prepared; MoleculeAtomNeighbourhoodCounters *nei_counters; { _target_arom_h_unfolded.clone(target, &_mapping_arom_h_unfolded, 0); target_prepared = &_target_arom_h_unfolded; mapping = &_mapping_arom_h_unfolded; prepared = &_arom_h_unfolded_prepared; nei_counters = &_nei_counters_h_unfolded; } AutoPtr iter(new IndigoTautomerSubstructureMatchIter(target, query, moleculeFound, method)); iter->matcher.find_unique_embeddings = find_unique_embeddings; iter->matcher.find_unique_by_edges = embedding_edges_uniqueness; iter->matcher.save_for_iteration = for_iteration; Array simpleMapping; simpleMapping.expand(mapping->size()); for (int i = 0; i < simpleMapping.size(); ++i) { simpleMapping[i] = i; } iter->mapping.copy(simpleMapping); iter->max_embeddings = max_embeddings; return iter.release(); } bool IndigoMoleculeSubstructureMatcher::findTautomerMatch ( QueryMolecule &query, PtrArray &tautomer_rules, Array &mapping_out) { // shameless copy-paste from the method above bool *prepared; Molecule *target_prepared; Array *mapping; if (MoleculeSubstructureMatcher::shouldUnfoldTargetHydrogens(query, false)) { if (!_arom_h_unfolded_prepared) _target_arom_h_unfolded.clone(target, &_mapping_arom_h_unfolded, 0); target_prepared = &_target_arom_h_unfolded; mapping = &_mapping_arom_h_unfolded; prepared = &_arom_h_unfolded_prepared; } else { if (!_arom_prepared) _target_arom.clone(target, &_mapping_arom, 0); target_prepared = &_target_arom; mapping = &_mapping_arom; prepared = &_arom_prepared; } Indigo &indigo = indigoGetInstance(); if (!target.isAromatized() && !*prepared) target_prepared->aromatize(indigo.arom_options); *prepared = true; if (tau_matcher.get() == 0) { bool substructure = true; tau_matcher.create(*target_prepared, substructure); } tau_matcher->setRulesList(&tautomer_rules); tau_matcher->setRules(tau_params.conditions, tau_params.force_hydrogens, tau_params.ring_chain, tau_params.method); tau_matcher->setQuery(query); tau_matcher->arom_options = indigo.arom_options; if (!tau_matcher->find()) return false; mapping_out.clear_resize(query.vertexEnd()); mapping_out.fffill(); const int *qm = tau_matcher->getQueryMapping(); for (int i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) if (qm[i] >= 0) mapping_out[i] = mapping->at(qm[i]); return true; } CEXPORT int indigoSubstructureMatcher (int target, const char *mode_str) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(target); if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); int mode = IndigoMoleculeSubstructureMatcher::NORMAL; IndigoTautomerParams tau_params; if (mode_str != 0 && *mode_str != 0) { if (_indigoParseTautomerFlags(mode_str, tau_params)) mode = IndigoMoleculeSubstructureMatcher::TAUTOMER; else if (strcasecmp(mode_str, "RES") == 0) mode = IndigoMoleculeSubstructureMatcher::RESONANCE; else throw IndigoError("indigoSubstructureMatcher(): unsupported mode %s", mode_str); } AutoPtr matcher( new IndigoMoleculeSubstructureMatcher(mol, mode)); if (mode == IndigoMoleculeSubstructureMatcher::TAUTOMER) matcher->tau_params = tau_params; return self.addObject(matcher.release()); } if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); bool daylight_aam = false; if (mode_str != 0 && *mode_str != 0) { if (strcasecmp(mode_str, "DAYLIGHT-AAM") == 0) daylight_aam = true; else throw IndigoError("reaction substructure matcher: unknown mode %s", mode_str); } AutoPtr matcher(new IndigoReactionSubstructureMatcher(rxn)); matcher->daylight_aam = daylight_aam; return self.addObject(matcher.release()); } throw IndigoError("indigoSubstructureMatcher(): %s is neither a molecule not a reaction", obj.debugInfo()); } INDIGO_END(-1) } IndigoMoleculeSubstructureMatcher & IndigoMoleculeSubstructureMatcher::cast (IndigoObject &obj) { if (obj.type != IndigoObject::MOLECULE_SUBSTRUCTURE_MATCHER) throw IndigoError("%s is not a matcher object", obj.debugInfo()); return (IndigoMoleculeSubstructureMatcher &)obj; } IndigoMoleculeSubstructureMatchIter * IndigoMoleculeSubstructureMatcher::getMatchIterator ( Indigo &self, int query, bool for_iteration, int max_embeddings) { return iterateQueryMatches(self.getObject(query), self.embedding_edges_uniqueness, self.find_unique_embeddings, for_iteration, max_embeddings); } IndigoTautomerSubstructureMatchIter * IndigoMoleculeSubstructureMatcher::getTautomerMatchIterator( Indigo &self, int query, bool for_iteration, int max_embeddings, TautomerMethod method) { return iterateTautomerQueryMatches(self.getObject(query), self.embedding_edges_uniqueness, self.find_unique_embeddings, for_iteration, max_embeddings, method); } CEXPORT int indigoIgnoreAtom (int target_matcher, int atom_object) { INDIGO_BEGIN { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(self.getObject(target_matcher)); IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom_object)); matcher.ignoreAtom(ia.idx); return 0; } INDIGO_END(-1) } // Ignore target atom in the substructure matcher CEXPORT int indigoUnignoreAtom (int target_matcher, int atom_object) { INDIGO_BEGIN { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(self.getObject(target_matcher)); IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom_object)); matcher.unignoreAtom(ia.idx); return 0; } INDIGO_END(-1) } CEXPORT int indigoUnignoreAllAtoms (int target_matcher) { INDIGO_BEGIN { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(self.getObject(target_matcher)); matcher.unignoreAllAtoms(); return 0; } INDIGO_END(-1) } CEXPORT int indigoMatch (int target_matcher, int query) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(target_matcher); if (obj.type == IndigoObject::MOLECULE_SUBSTRUCTURE_MATCHER) { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(obj); if(matcher.mode == IndigoMoleculeSubstructureMatcher::TAUTOMER) { switch(matcher.tau_params.method) { case BASIC: { QueryMolecule &qmol = self.getObject(query).getQueryMolecule(); AutoPtr mptr(new IndigoMapping(qmol, matcher.target)); if (!matcher.findTautomerMatch(qmol, self.tautomer_rules, mptr->mapping)) return 0; return self.addObject(mptr.release()); } case INCHI: case RSMARTS: { AutoPtr match_iter(matcher.getTautomerMatchIterator(self, query, true, 1, matcher.tau_params.method)); match_iter->matcher.find_unique_embeddings = false; if (!match_iter->hasNext()) return 0; return self.addObject(match_iter->next()); } } } else // NORMAL or RESONANCE { AutoPtr match_iter(matcher.getMatchIterator(self, query, false, 1)); match_iter->matcher.find_unique_embeddings = false; if (!match_iter->hasNext()) return 0; return self.addObject(match_iter->next()); } } if (obj.type == IndigoObject::REACTION_SUBSTRUCTURE_MATCHER) { IndigoReactionSubstructureMatcher &matcher = IndigoReactionSubstructureMatcher::cast(obj); QueryReaction &qrxn = self.getObject(query).getQueryReaction(); int i, j; ReactionAutomapper ram(qrxn); ram.arom_options = self.arom_options; ram.correctReactingCenters(true); for (i = qrxn.begin(); i != qrxn.end(); i = qrxn.next(i)) if (MoleculeSubstructureMatcher::shouldUnfoldTargetHydrogens(qrxn.getQueryMolecule(i), false)) break; if (i != qrxn.end()) { matcher.target.unfoldHydrogens(); // expand mappings to include unfolded hydrogens for (i = matcher.target.begin(); i != matcher.target.end(); i = matcher.target.next(i)) matcher.mappings[i].expandFill(matcher.target.getBaseMolecule(i).vertexEnd(), -1); } if (matcher.matcher.get() == 0) matcher.matcher.create(matcher.target); matcher.matcher->use_daylight_aam_mode = matcher.daylight_aam; matcher.matcher->setQuery(qrxn); matcher.matcher->arom_options = self.arom_options; if (!matcher.matcher->find()) return 0; AutoPtr mapping(new IndigoReactionMapping(qrxn, matcher.original_target)); mapping->mol_mapping.clear_resize(qrxn.end()); mapping->mol_mapping.fffill(); mapping->mappings.expand(qrxn.end()); for (i = qrxn.begin(); i != qrxn.end(); i = qrxn.next(i)) { if (qrxn.getSideType(i) == BaseReaction::CATALYST) continue; int tmol_idx = matcher.matcher->getTargetMoleculeIndex(i); mapping->mol_mapping[i] = matcher.mol_mapping[tmol_idx]; BaseMolecule &qm = qrxn.getBaseMolecule(i); mapping->mappings[i].clear_resize(qm.vertexEnd()); mapping->mappings[i].fffill(); for (j = qm.vertexBegin(); j != qm.vertexEnd(); j = qm.vertexNext(j)) { int mapped = matcher.matcher->getQueryMoleculeMapping(i)[j]; if (mapped >= 0) // hydrogens are ignored mapping->mappings[i][j] = matcher.mappings[tmol_idx][mapped]; } } return self.addObject(mapping.release()); } throw IndigoError("indigoIterateMatches(): expected a matcher, got %s", obj.debugInfo()); } INDIGO_END(-1) } int indigoCountMatches (int target_matcher, int query) { return indigoCountMatchesWithLimit(target_matcher, query, 0); } CEXPORT int indigoCountMatchesWithLimit (int target_matcher, int query, int embeddings_limit) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(target_matcher); if (obj.type == IndigoObject::MOLECULE_SUBSTRUCTURE_MATCHER) { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(obj); if (matcher.mode == IndigoMoleculeSubstructureMatcher::TAUTOMER) throw IndigoError("count matches: not supported in this mode"); if (embeddings_limit > self.max_embeddings) throw IndigoError("count matches: embeddings limit is more then maximum " "allowed embeddings specified by options"); AutoPtr match_iter(matcher.getMatchIterator(self, query, false, self.max_embeddings)); return match_iter->countMatches(embeddings_limit); } if (obj.type == IndigoObject::REACTION_SUBSTRUCTURE_MATCHER) throw IndigoError("count matches: can not work with reactions"); throw IndigoError("count matches: expected a matcher, got %s", obj.debugInfo()); } INDIGO_END(-1) } int indigoIterateMatches (int target_matcher, int query) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(target_matcher); if (obj.type == IndigoObject::MOLECULE_SUBSTRUCTURE_MATCHER) { IndigoMoleculeSubstructureMatcher &matcher = IndigoMoleculeSubstructureMatcher::cast(obj); if (matcher.tau_params.method != BASIC && matcher.mode == IndigoMoleculeSubstructureMatcher::TAUTOMER) { AutoPtr match_iter(matcher.getTautomerMatchIterator(self, query, true, self.max_embeddings, matcher.tau_params.method)); return self.addObject(match_iter.release()); } else if (matcher.mode == IndigoMoleculeSubstructureMatcher::TAUTOMER) throw IndigoError("indigoIterateMatches(): not supported in this mode"); AutoPtr match_iter(matcher.getMatchIterator(self, query, true, self.max_embeddings)); return self.addObject(match_iter.release()); } if (obj.type == IndigoObject::REACTION_SUBSTRUCTURE_MATCHER) throw IndigoError("indigoIterateMatches(): can not work with reactions"); throw IndigoError("indigoIterateMatches(): expected a matcher, got %s", obj.debugInfo()); } INDIGO_END(-1) } const char * IndigoReactionSubstructureMatcher::debugInfo () { return ""; } IndigoReactionSubstructureMatcher::IndigoReactionSubstructureMatcher (Reaction &target_) : IndigoObject(REACTION_SUBSTRUCTURE_MATCHER), original_target(target_) { target.clone(target_, &mol_mapping, &mappings, 0); Indigo &indigo = indigoGetInstance(); target.aromatize(indigo.arom_options); daylight_aam = false; } IndigoReactionSubstructureMatcher::~IndigoReactionSubstructureMatcher () { } IndigoReactionSubstructureMatcher & IndigoReactionSubstructureMatcher::cast (IndigoObject &obj) { if (obj.type != IndigoObject::REACTION_SUBSTRUCTURE_MATCHER) throw IndigoError("%s is not a reaction matcher object", obj.debugInfo()); return (IndigoReactionSubstructureMatcher &)obj; } Indigo-indigo-1.2.3/api/src/indigo_match.h000066400000000000000000000123021271037650300203350ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_match__ #define __indigo_match__ #include "indigo_internal.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_tautomer_matcher.h" #include "molecule/molecule_tautomer_substructure_matcher.h" #include "reaction/reaction_substructure_matcher.h" #include "reaction/reaction.h" #include "molecule/molecule_neighbourhood_counters.h" class IndigoQueryMolecule; #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif struct IndigoTautomerParams { int conditions; bool force_hydrogens; bool ring_chain; TautomerMethod method; }; // Iterator for all possible matches class IndigoMoleculeSubstructureMatchIter : public IndigoObject { public: IndigoMoleculeSubstructureMatchIter (Molecule &target, QueryMolecule &query, Molecule &original_target, bool resonance, bool disable_folding_query_h); virtual ~IndigoMoleculeSubstructureMatchIter (); virtual IndigoObject * next (); virtual bool hasNext (); int countMatches (int embeddings_limit); const char * debugInfo (); MoleculeSubstructureMatcher matcher; MoleculeSubstructureMatcher::FragmentMatchCache fmcache; Molecule &target, &original_target; QueryMolecule &query; Array mapping; int max_embeddings; private: bool _initialized, _found, _need_find; int _embedding_index; }; // Iterator for all possible matches in tautomers class IndigoTautomerSubstructureMatchIter : public IndigoObject { public: IndigoTautomerSubstructureMatchIter(Molecule &target, QueryMolecule &query, Molecule &tautomerFound, TautomerMethod method); virtual ~IndigoTautomerSubstructureMatchIter(); virtual IndigoObject * next(); virtual bool hasNext(); int countMatches(int embeddings_limit); const char * debugInfo(); MoleculeTautomerSubstructureMatcher matcher; Molecule &tautomerFound; QueryMolecule &query; Array mapping; int max_embeddings; private: bool _initialized, _found, _need_find; int _embedding_index; int _mask_index; }; // Matcher class for matching queries on a specified target molecule class DLLEXPORT IndigoMoleculeSubstructureMatcher : public IndigoObject { public: enum { NORMAL = 1, RESONANCE = 2, TAUTOMER = 3 }; IndigoMoleculeSubstructureMatcher (Molecule &target, int mode); virtual ~IndigoMoleculeSubstructureMatcher (); IndigoMoleculeSubstructureMatchIter* iterateQueryMatches (IndigoObject &query_object, bool embedding_edges_uniqueness, bool find_unique_embeddings, bool for_iteration, int max_embeddings); IndigoTautomerSubstructureMatchIter* iterateTautomerQueryMatches(IndigoObject &query_object, bool embedding_edges_uniqueness, bool find_unique_embeddings, bool for_iteration, int max_embeddings, TautomerMethod method); static IndigoMoleculeSubstructureMatcher & cast (IndigoObject &obj); void ignoreAtom (int atom_index); void unignoreAtom (int atom_index); void unignoreAllAtoms (); const char * debugInfo (); Molecule ⌖ Molecule moleculeFound; Obj tau_matcher; IndigoTautomerParams tau_params; bool findTautomerMatch (QueryMolecule &query, PtrArray &tautomer_rules, Array &mapping_out); IndigoMoleculeSubstructureMatchIter * getMatchIterator (Indigo &self, int query, bool for_iteration, int max_embeddings); IndigoTautomerSubstructureMatchIter * getTautomerMatchIterator(Indigo &self, int query, bool for_iteration, int max_embeddings, TautomerMethod method); int mode; // NORMAL, TAUTOMER, or RESONANCE private: Molecule _target_arom_h_unfolded, _target_arom; Array _mapping_arom_h_unfolded, _mapping_arom, _ignored_atoms; bool _arom_h_unfolded_prepared, _arom_prepared, _aromatized; MoleculeAtomNeighbourhoodCounters _nei_counters, _nei_counters_h_unfolded; }; class DLLEXPORT IndigoReactionSubstructureMatcher : public IndigoObject { public: IndigoReactionSubstructureMatcher (Reaction &target); virtual ~IndigoReactionSubstructureMatcher (); static IndigoReactionSubstructureMatcher & cast (IndigoObject &obj); const char * debugInfo (); Reaction &original_target; Reaction target; bool daylight_aam; Obj matcher; ObjArray< Array > mappings; Array mol_mapping; }; DLLEXPORT bool _indigoParseTautomerFlags (const char *flags, IndigoTautomerParams ¶ms); DLLEXPORT int _indigoParseExactFlags (const char *flags, bool reaction, float *rms_threshold); #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_misc.cpp000066400000000000000000000634071271037650300205430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "indigo_properties.h" #include "indigo_io.h" #include "indigo_loaders.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_dearom.h" #include "molecule/elements.h" #include "indigo_molecule.h" #include "molecule/sdf_loader.h" #include "molecule/rdf_loader.h" #include "indigo_array.h" #include "molecule/icm_saver.h" #include "molecule/icm_loader.h" #include "reaction/icr_saver.h" #include "reaction/icr_loader.h" #include "indigo_reaction.h" #include "indigo_mapping.h" #include "indigo_savers.h" #include "molecule/molecule_standardize.h" #include "molecule/molecule_ionize.h" #define CHECKRGB(r, g, b) \ if (__min3(r, g, b) < 0 || __max3(r, g, b) > 1.0 + 1e-6) \ throw IndigoError("Some of the color components are out of range [0..1]") CEXPORT int indigoAromatize (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (IndigoBaseMolecule::is(obj)) return obj.getBaseMolecule().aromatize(self.arom_options) ? 1 : 0; if (IndigoBaseReaction::is(obj)) return obj.getBaseReaction().aromatize(self.arom_options) ? 1 : 0; throw IndigoError("Only molecules and reactions can be aromatized"); } INDIGO_END(-1) } CEXPORT int indigoDearomatize (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); AromaticityOptions arom_options = self.arom_options; arom_options.unique_dearomatization = self.unique_dearomatization; if (IndigoBaseMolecule::is(obj)) return obj.getBaseMolecule().dearomatize(arom_options) ? 1 : 0; if (IndigoBaseReaction::is(obj)) return obj.getBaseReaction().dearomatize(arom_options) ? 1 : 0; throw IndigoError("Only molecules and reactions can be dearomatized"); } INDIGO_END(-1) } #define INDIGO_SET_OPTION(SUFFIX, TYPE) \ CEXPORT int indigoSetOption##SUFFIX (const char *name, TYPE value) \ { \ INDIGO_BEGIN \ { \ indigoGetOptionManager().callOptionHandler##SUFFIX(name, value); \ return 1; \ } \ INDIGO_END(-1) \ } INDIGO_SET_OPTION(, const char *) INDIGO_SET_OPTION(Int, int) INDIGO_SET_OPTION(Bool, int) INDIGO_SET_OPTION(Float, float) CEXPORT int indigoSetOptionColor (const char *name, float r, float g, float b) { INDIGO_BEGIN { indigoGetOptionManager().callOptionHandlerColor(name, r, g, b); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetOptionXY (const char *name, int x, int y) { INDIGO_BEGIN { indigoGetOptionManager().callOptionHandlerXY(name, x, y); return 1; } INDIGO_END(-1) } void _indigoCheckBadValence (Molecule &mol) { mol.restoreAromaticHydrogens(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.isPseudoAtom(i) || mol.isRSite(i)) continue; mol.getAtomValence(i); mol.getImplicitH(i); } } CEXPORT const char * indigoCheckBadValence (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &bmol = obj.getBaseMolecule(); if (bmol.isQueryMolecule()) throw IndigoError("indigoCheckBadValence(): query molecules not allowed"); Molecule &mol = bmol.asMolecule(); try { _indigoCheckBadValence(mol); } catch (Exception &e) { auto &tmp = self.getThreadTmpData(); tmp.string.readString(e.message(), true); return tmp.string.ptr(); } } else if (IndigoBaseReaction::is(obj)) { BaseReaction &brxn = obj.getBaseReaction(); if (brxn.isQueryReaction()) throw IndigoError("indigoCheckBadValence(): query reactions not allowed"); Reaction &rxn = brxn.asReaction(); try { for (int j = rxn.begin(); j != rxn.end(); j = rxn.next(j)) { Molecule &mol = rxn.getMolecule(j); _indigoCheckBadValence(mol); } } catch (Exception &e) { auto &tmp = self.getThreadTmpData(); tmp.string.readString(e.message(), true); return tmp.string.ptr(); } } else throw IndigoError("object %s is neither a molecule nor a reaction", obj.debugInfo()); return ""; } INDIGO_END(0); } void _indigoCheckAmbiguousH (Molecule &mol) { mol.restoreAromaticHydrogens(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.getAtomAromaticity(i) == ATOM_AROMATIC) { int atom_number = mol.getAtomNumber(i); if (atom_number != ELEM_C && atom_number != ELEM_O) mol.getAtomTotalH(i); } } CEXPORT const char * indigoCheckAmbiguousH (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &bmol = obj.getBaseMolecule(); if (bmol.isQueryMolecule()) throw IndigoError("indigoCheckAmbiguousH(): query molecules not allowed"); Molecule &mol = bmol.asMolecule(); try { _indigoCheckAmbiguousH(mol); } catch (Exception &e) { auto &tmp = self.getThreadTmpData(); tmp.string.readString(e.message(), true); return tmp.string.ptr(); } } else if (IndigoBaseReaction::is(obj)) { BaseReaction &brxn = obj.getBaseReaction(); if (brxn.isQueryReaction()) throw IndigoError("indigoCheckAmbiguousH(): query molecules not allowed"); Reaction &rxn = brxn.asReaction(); try { int j; for (j = rxn.begin(); j != rxn.end(); j = rxn.next(j)) _indigoCheckAmbiguousH(rxn.getMolecule(j)); } catch (Exception &e) { auto &tmp = self.getThreadTmpData(); tmp.string.readString(e.message(), true); return tmp.string.ptr(); } } else throw IndigoError("object %s is meither a molecule nor a reaction", obj.debugInfo()); return ""; } INDIGO_END(0); } CEXPORT const char * indigoSmiles (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); auto &tmp = self.getThreadTmpData(); IndigoSmilesSaver::generateSmiles(obj, tmp.string); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT const char * indigoCanonicalSmiles (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); auto &tmp = self.getThreadTmpData(); IndigoCanonicalSmilesSaver::generateSmiles(obj, tmp.string); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoUnfoldHydrogens (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoBaseMolecule::is(obj)) { QS_DEF(Array, markers); obj.getMolecule().unfoldHydrogens(&markers, -1); } else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); rxn.unfoldHydrogens(); } else throw IndigoError("indigoUnfoldHydrogens(): %s given", obj.debugInfo()); return 1; } INDIGO_END(-1) } static bool _removeHydrogens (Molecule &mol) { QS_DEF(Array, to_remove); QS_DEF(Array, sterecenters_to_validate); int i; sterecenters_to_validate.clear(); to_remove.clear(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.convertableToImplicitHydrogen(i)) { const Vertex &v = mol.getVertex(i); int nei = v.neiBegin(); if (nei != v.neiEnd()) { if (mol.getBondDirection(v.neiEdge(nei))) sterecenters_to_validate.push(v.neiVertex(nei)); } to_remove.push(i); } if (to_remove.size() > 0) mol.removeAtoms(to_remove); for (int i = 0; i < sterecenters_to_validate.size(); i++) mol.stereocenters.markBond(sterecenters_to_validate[i]); return to_remove.size() > 0; } CEXPORT int indigoFoldHydrogens (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoBaseMolecule::is(obj)) _removeHydrogens(obj.getMolecule()); else if (IndigoBaseReaction::is(obj)) { int i; Reaction &rxn = obj.getReaction(); for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) _removeHydrogens(rxn.getMolecule(i)); } else throw IndigoError("indigoFoldHydrogens(): %s given", obj.debugInfo()); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetName (int handle, const char *name) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) obj.getBaseMolecule().name.readString(name, true); else if (IndigoBaseReaction::is(obj)) obj.getBaseReaction().name.readString(name, true); else throw IndigoError("The object provided is neither a molecule, nor a reaction"); return 1; } INDIGO_END(-1); } CEXPORT const char * indigoName (int handle) { INDIGO_BEGIN { return self.getObject(handle).getName(); } INDIGO_END(0); } CEXPORT const char * indigoRawData (int handler) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handler); auto &tmp = self.getThreadTmpData(); if (obj.type == IndigoObject::RDF_MOLECULE || obj.type == IndigoObject::RDF_REACTION || obj.type == IndigoObject::SMILES_MOLECULE || obj.type == IndigoObject::SMILES_REACTION || obj.type == IndigoObject::CML_MOLECULE || obj.type == IndigoObject::CML_REACTION || obj.type == IndigoObject::CDX_MOLECULE || obj.type == IndigoObject::CDX_REACTION) { IndigoRdfData &data = (IndigoRdfData &)obj; tmp.string.copy(data.getRawData()); } else if (obj.type == IndigoObject::PROPERTY) tmp.string.readString(((IndigoProperty &)obj).getValue(), false); else if (obj.type == IndigoObject::DATA_SGROUP) { tmp.string.copy(((IndigoDataSGroup &)obj).get().data); } else throw IndigoError("%s does not have raw data", obj.debugInfo()); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0) } CEXPORT int indigoRemove (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); obj.remove(); return 1; } INDIGO_END(-1) } CEXPORT int indigoAt (int item, int index) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (obj.type == IndigoObject::SDF_LOADER) { IndigoObject * newobj = ((IndigoSdfLoader &)obj).at(index); if (newobj == 0) return 0; return self.addObject(newobj); } if (obj.type == IndigoObject::RDF_LOADER) { IndigoObject * newobj = ((IndigoRdfLoader &)obj).at(index); if (newobj == 0) return 0; return self.addObject(newobj); } else if (obj.type == IndigoObject::MULTILINE_SMILES_LOADER) { IndigoObject * newobj = ((IndigoMultilineSmilesLoader &)obj).at(index); if (newobj == 0) return 0; return self.addObject(newobj); } else if (obj.type == IndigoObject::MULTIPLE_CDX_LOADER) { IndigoObject * newobj = ((IndigoMultipleCdxLoader &)obj).at(index); if (newobj == 0) return 0; return self.addObject(newobj); } else if (IndigoArray::is(obj)) { IndigoArray &arr = IndigoArray::cast(obj); return self.addObject(new IndigoArrayElement(arr, index)); } else throw IndigoError("indigoAt(): not accepting %s", obj.debugInfo()); } INDIGO_END(-1); } CEXPORT int indigoCount (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoArray::is(obj)) return IndigoArray::cast(obj).objects.size(); if (obj.type == IndigoObject::SDF_LOADER) return ((IndigoSdfLoader &)obj).sdf_loader->count(); if (obj.type == IndigoObject::RDF_LOADER) return ((IndigoRdfLoader &)obj).rdf_loader->count(); if (obj.type == IndigoObject::MULTILINE_SMILES_LOADER) return ((IndigoMultilineSmilesLoader &)obj).count(); throw IndigoError("indigoCount(): can not handle %s", obj.debugInfo()); } INDIGO_END(-1); } CEXPORT int indigoSerialize (int item, byte **buf, int *size) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); auto &tmp = self.getThreadTmpData(); ArrayOutput out(tmp.string); if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); IcmSaver saver(out); saver.save_xyz = mol.have_xyz; saver.save_bond_dirs = true; saver.save_highlighting = true; saver.save_ordering = self.preserve_ordering_in_serialize; saver.saveMolecule(mol); } else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); IcrSaver saver(out); saver.save_xyz = BaseReaction::haveCoord(rxn); saver.save_bond_dirs = true; saver.save_highlighting = true; saver.save_ordering = self.preserve_ordering_in_serialize; saver.saveReaction(rxn); } *buf = (byte *)tmp.string.ptr(); *size = tmp.string.size(); return 1; } INDIGO_END(-1) } CEXPORT int indigoUnserialize (const byte *buf, int size) { INDIGO_BEGIN { if (IcmSaver::checkVersion((const char *)buf)) { BufferScanner scanner(buf, size); IcmLoader loader(scanner); AutoPtr im(new IndigoMolecule()); loader.loadMolecule(im->mol); return self.addObject(im.release()); } else if (IcrSaver::checkVersion((const char *)buf)) { BufferScanner scanner(buf, size); IcrLoader loader(scanner); AutoPtr ir(new IndigoReaction()); loader.loadReaction(ir->rxn); return self.addObject(ir.release()); } else throw IndigoError("indigoUnserialize(): format not recognized"); } INDIGO_END(-1) } CEXPORT int indigoClear (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoArray::is(obj)) { IndigoArray &array = IndigoArray::cast(obj); array.objects.clear(); } else if (IndigoBaseMolecule::is(obj)) obj.getBaseMolecule().clear(); else if (IndigoBaseReaction::is(obj)) obj.getBaseReaction().clear(); else throw IndigoError("indigoClear(): do not know how to clear %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoHighlight (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(obj); ia.mol.highlightAtom(ia.idx); } else if (IndigoBond::is(obj)) { IndigoBond &ib = IndigoBond::cast(obj); ib.mol.highlightBond(ib.idx); } else throw IndigoError("indigoHighlight(): expected atom or bond, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoUnhighlight (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(obj); ia.mol.unhighlightAtom(ia.idx); } else if (IndigoBond::is(obj)) { IndigoBond &ib = IndigoBond::cast(obj); ib.mol.unhighlightBond(ib.idx); } else if (IndigoBaseMolecule::is(obj)) { obj.getBaseMolecule().unhighlightAll(); } else if (IndigoBaseReaction::is(obj)) { BaseReaction &reaction = obj.getBaseReaction(); int i; for (i = reaction.begin(); i != reaction.end(); i = reaction.next(i)) reaction.getBaseMolecule(i).unhighlightAll(); } else throw IndigoError("indigoUnhighlight(): expected atom/bond/molecule/reaction, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoIsHighlighted (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(obj); return ia.mol.isAtomHighlighted(ia.idx) ? 1 : 0; } else if (IndigoBond::is(obj)) { IndigoBond &ib = IndigoBond::cast(obj); return ib.mol.isBondHighlighted(ib.idx) ? 1 : 0; } else throw IndigoError("indigoHighlight(): expected atom or bond, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoOptimize (int query, const char *options) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(query); if (obj.type == IndigoObject::QUERY_MOLECULE) { IndigoQueryMolecule &qm_obj = (IndigoQueryMolecule &)obj; QueryMolecule &q = qm_obj.getQueryMolecule(); q.optimize(); QS_DEF(Array, transposition); QS_DEF(QueryMolecule, transposed_q); qm_obj.getNeiCounters().makeTranspositionForSubstructure(q, transposition); transposed_q.makeSubmolecule(q, transposition, 0); q.clone(transposed_q, 0, 0); } else if (IndigoBaseReaction::is(obj)) obj.getQueryReaction().optimize(); else throw IndigoError("indigoOptimize: expected molecule or reaction, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } static int _indigoHasCoord (int item, bool (*has_coord_func)(BaseMolecule &mol), const char *func_name) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &mol = obj.getBaseMolecule(); return has_coord_func(mol) ? 1 : 0; } else if (IndigoBaseReaction::is(obj)) { BaseReaction &reaction = obj.getBaseReaction(); for (int i = reaction.begin(); i != reaction.end(); i = reaction.next(i)) { BaseMolecule &mol = reaction.getBaseMolecule(i); if (has_coord_func(mol)) return 1; } return 0; } else throw IndigoError("%s: expected molecule or reaction, got %s", func_name, obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoHasZCoord (int item) { return _indigoHasCoord(item, BaseMolecule::hasZCoord, "indigoHasZCoord"); } CEXPORT int indigoHasCoord (int item) { return _indigoHasCoord(item, BaseMolecule::hasCoord, "indigoHasCoord"); } CEXPORT const char * indigoDbgInternalType (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); char tmp_str[1024]; snprintf(tmp_str, 1023, "#%02d: %s", obj.type, obj.debugInfo()); auto &tmp = self.getThreadTmpData(); tmp.string.readString(tmp_str, true); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoNormalize (int structure, const char *options) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(structure); Molecule &mol = obj.getMolecule(); bool changed = false; // Fold hydrogens changed |= _removeHydrogens(mol); // Neutralize charges for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { int charge = mol.getAtomCharge(i); if (charge == 1 && mol.getAtomNumber(i) == ELEM_N) { const Vertex &v = mol.getVertex(i); for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int j = v.neiVertex(nei); int charge2 = mol.getAtomCharge(j); if (charge2 == -1 && mol.getAtomNumber(j) == ELEM_O) { int edge_idx = v.neiEdge(nei); if (mol.getBondOrder(edge_idx) == BOND_SINGLE) { mol.setAtomCharge(i, 0); mol.setAtomCharge(j, 0); mol.setBondOrder(edge_idx, BOND_DOUBLE); changed = true; break; } } } } } if (changed) { // Validate cs-trans because it can disappear // For example: [O-]/[N+](=C\C1C=CC=CC=1)/C1C=CC=CC=1 mol.cis_trans.validate(); } return changed; } INDIGO_END(-1); } CEXPORT int indigoStandardize (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (obj.type == IndigoObject::QUERY_MOLECULE) { IndigoQueryMolecule &qm_obj = (IndigoQueryMolecule &)obj; QueryMolecule &q = qm_obj.getQueryMolecule(); q.standardize(self.standardize_options); } else if (obj.type == IndigoObject::MOLECULE) { IndigoMolecule &m_obj = (IndigoMolecule &)obj; Molecule &m = m_obj.getMolecule(); m.standardize(self.standardize_options); } else throw IndigoError("indigoStandardize: expected molecule or query, got %s", obj.debugInfo()); return 1; } INDIGO_END(-1); } CEXPORT int indigoIonize (int object, float pH, float pH_toll) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); Molecule &mol = obj.getMolecule(); mol.ionize(pH, pH_toll, self.ionize_options); return 1; } INDIGO_END(-1); } CEXPORT int indigoBuildPkaModel (int max_level, float threshold, const char * filename) { INDIGO_BEGIN { int level = MoleculePkaModel::buildPkaModel(max_level, threshold, filename); if (level > 0) return 1; return 0; } INDIGO_END(-1); } CEXPORT float * indigoGetAcidPkaValue (int object, int atom, int level, int min_level) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (obj.type == IndigoObject::MOLECULE) { IndigoMolecule &m_obj = (IndigoMolecule &)obj; Molecule &mol = m_obj.getMolecule(); IndigoAtom &site = IndigoAtom::cast(self.getObject(atom)); auto &tmp = self.getThreadTmpData(); float pka = MoleculePkaModel::getAcidPkaValue(mol, site.getIndex(), level, min_level); tmp.xyz[0] = pka; return tmp.xyz; } else throw IndigoError("indigoGetAcidPkaValue: expected molecule, got %s", obj.debugInfo()); return 0; } INDIGO_END(0); } CEXPORT float * indigoGetBasicPkaValue (int object, int atom, int level, int min_level) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (obj.type == IndigoObject::MOLECULE) { IndigoMolecule &m_obj = (IndigoMolecule &)obj; Molecule &mol = m_obj.getMolecule(); IndigoAtom &site = IndigoAtom::cast(self.getObject(atom)); auto &tmp = self.getThreadTmpData(); float pka = MoleculePkaModel::getBasicPkaValue(mol, site.getIndex(), level, min_level); tmp.xyz[0] = pka; return tmp.xyz; } else throw IndigoError("indigoGetBasicPkaValue: expected molecule, got %s", obj.debugInfo()); return 0; } INDIGO_END(0); } CEXPORT int indigoIsPossibleFischerProjection (int object, const char *options) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (obj.type == IndigoObject::MOLECULE) { IndigoMolecule &m_obj = (IndigoMolecule &)obj; Molecule &mol = m_obj.getMolecule(); if (mol.isPossibleFischerProjection(options)) return 1; return 0; } else throw IndigoError("indigoIsPossibleFischerProjection: expected molecule, got %s", obj.debugInfo()); return -1; } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_molecule.cpp000066400000000000000000002777411271037650300214250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_molecule.h" #include "indigo_io.h" #include "indigo_array.h" #include "molecule/molecule_auto_loader.h" #include "base_cpp/output.h" #include "molecule/gross_formula.h" #include "molecule/molecule_mass.h" #include "molecule/query_molecule.h" #include "molecule/smiles_loader.h" #include "molecule/smiles_saver.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/molecule_inchi.h" #include "base_c/bitarray.h" #include "molecule/molecule_fingerprint.h" #include "molecule/elements.h" #include "molecule/molecule_automorphism_search.h" #include "base_cpp/scanner.h" #include "indigo_mapping.h" IndigoGross::IndigoGross() : IndigoObject(GROSS) { } IndigoGross::~IndigoGross () { } void IndigoGross::toString (Array &str) { GrossFormula::toString_Hill(gross, str); } IndigoBaseMolecule::IndigoBaseMolecule (int type_) : IndigoObject(type_) { } IndigoBaseMolecule::~IndigoBaseMolecule () { } const char * IndigoBaseMolecule::debugInfo () { return ""; } IndigoMolecule::IndigoMolecule () : IndigoBaseMolecule(MOLECULE) { } bool IndigoBaseMolecule::is (IndigoObject &object) { int type = object.type; if (type == MOLECULE || type == QUERY_MOLECULE || type == REACTION_MOLECULE || type == SCAFFOLD || type == RGROUP_FRAGMENT || type == RDF_MOLECULE || type == SMILES_MOLECULE || type == CML_MOLECULE || type == CDX_MOLECULE) return true; if (type == ARRAY_ELEMENT) return is(((IndigoArrayElement &)object).get()); return false; } IndigoMolecule::~IndigoMolecule () { } Molecule & IndigoMolecule::getMolecule () { return mol; } BaseMolecule & IndigoMolecule::getBaseMolecule () { return mol; } const char * IndigoMolecule::getName () { if (mol.name.ptr() == 0) return ""; return mol.name.ptr(); } IndigoMolecule * IndigoMolecule::cloneFrom (IndigoObject & obj) { AutoPtr molptr(new IndigoMolecule()); QS_DEF(Array, mapping); molptr->mol.clone(obj.getMolecule(), 0, &mapping); auto& props = obj.getProperties(); molptr->copyProperties(props); return molptr.release(); } IndigoQueryMolecule::IndigoQueryMolecule () : IndigoBaseMolecule(QUERY_MOLECULE) { _nei_counters_edit_revision = -1; } IndigoQueryMolecule::~IndigoQueryMolecule () { } QueryMolecule & IndigoQueryMolecule::getQueryMolecule () { return qmol; } IndigoQueryMolecule * IndigoQueryMolecule::cloneFrom( IndigoObject & obj ) { AutoPtr molptr(new IndigoQueryMolecule()); QS_DEF(Array, mapping); molptr->qmol.clone(obj.getQueryMolecule(), 0, &mapping); auto& props = obj.getProperties(); molptr->copyProperties(props); return molptr.release(); } void IndigoQueryMolecule::parseAtomConstraint (const char* type, const char* value, AutoPtr& atom) { enum KeyType { Int, Bool }; struct Mapping { const char *key; QueryMolecule::OpType value; KeyType key_type; }; static Mapping mappingForKeys[] = { { "atomic-number", QueryMolecule::ATOM_NUMBER, Int }, { "charge", QueryMolecule::ATOM_CHARGE, Int }, { "isotope", QueryMolecule::ATOM_ISOTOPE, Int }, { "radical", QueryMolecule::ATOM_RADICAL, Int }, { "valence", QueryMolecule::ATOM_VALENCE, Int }, { "connectivity", QueryMolecule::ATOM_CONNECTIVITY, Int }, { "total-bond-order", QueryMolecule::ATOM_TOTAL_BOND_ORDER, Int }, { "hydrogens", QueryMolecule::ATOM_TOTAL_H, Int }, { "substituents", QueryMolecule::ATOM_SUBSTITUENTS, Int }, { "ring", QueryMolecule::ATOM_SSSR_RINGS, Int }, { "smallest-ring-size", QueryMolecule::ATOM_SMALLEST_RING_SIZE, Int }, { "ring-bonds", QueryMolecule::ATOM_RING_BONDS, Int }, { "rsite-mask", QueryMolecule::ATOM_RSITE, Int }, { "highlighting", QueryMolecule::HIGHLIGHTING, Bool }, }; for (int i = 0; i < NELEM(mappingForKeys); i++) { if(strcasecmp(type, mappingForKeys[i].key) == 0) { int int_value = 0; if (value != NULL) { if (mappingForKeys[i].key_type == Int) { BufferScanner buf_scanner(value); int_value = buf_scanner.readInt(); } else if (mappingForKeys[i].key_type == Bool) { if (strcasecmp(value, "true") == 0) int_value = 1; else if (strcasecmp(value, "false") == 0) int_value = 0; else { BufferScanner buf_scanner(value); int_value = buf_scanner.readInt(); } } } atom.reset(new QueryMolecule::Atom(mappingForKeys[i].value, int_value)); return; } } if (strcasecmp(type, "rsite") == 0) { int int_value = 0; if (value != NULL) { BufferScanner buf_scanner(value); int_value = buf_scanner.readInt(); } atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 1 << int_value)); return; } else if (strcasecmp(type, "smarts") == 0) { if (value == NULL) throw IndigoError("Internal error: value argument in parseAtomConstraint has null value"); atom.reset(parseAtomSMARTS(value)); return; } else if (strcasecmp(type, "aromaticity") == 0) { int int_value = 0; if (value != NULL) { if (strcasecmp(value, "aromatic") == 0) int_value = ATOM_AROMATIC; else if (strcasecmp(value, "aliphatic") == 0) int_value = ATOM_ALIPHATIC; else throw IndigoError("unsupported aromaticity type: %s", value); } atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, int_value)); return; } throw IndigoError("unsupported constraint type: %s", type); } QueryMolecule::Atom* IndigoQueryMolecule::parseAtomSMARTS (const char *string) { if (strlen(string) == 0) return new QueryMolecule::Atom(); QS_DEF(QueryMolecule, qmol); qmol.clear(); BufferScanner scanner(string); SmilesLoader loader(scanner); loader.loadSMARTS(qmol); if (qmol.vertexCount() != 1) throw IndigoError("cannot parse '%s' as a single-atom", string); return qmol.releaseAtom(qmol.vertexBegin()); } BaseMolecule & IndigoQueryMolecule::getBaseMolecule () { return qmol; } const char * IndigoQueryMolecule::getName () { if (qmol.name.ptr() == 0) return ""; return qmol.name.ptr(); } IndigoAtom::IndigoAtom (BaseMolecule &mol_, int idx_) : IndigoObject (ATOM), mol(mol_) { idx = idx_; } IndigoAtom::~IndigoAtom () { } int IndigoAtom::getIndex () { return idx; } const char * IndigoAtom::debugInfo () { return ""; } bool IndigoAtom::is (IndigoObject &obj) { if (obj.type == IndigoObject::ATOM || obj.type == IndigoObject::ATOM_NEIGHBOR) return true; if (obj.type == IndigoObject::ARRAY_ELEMENT) return is(((IndigoArrayElement &)obj).get()); return false; } IndigoAtom & IndigoAtom::cast (IndigoObject &obj) { if (obj.type == IndigoObject::ATOM || obj.type == IndigoObject::ATOM_NEIGHBOR) return (IndigoAtom &)obj; if (obj.type == IndigoObject::ARRAY_ELEMENT) return cast(((IndigoArrayElement &)obj).get()); throw IndigoError("%s does not represent an atom", obj.debugInfo()); } void IndigoAtom::remove () { mol.removeAtom(idx); } IndigoObject * IndigoAtom::clone () { return new IndigoAtom(mol, idx); } IndigoAtomsIter::IndigoAtomsIter (BaseMolecule *mol, int type_) : IndigoObject(ATOMS_ITER) { _mol = mol; _type = type_; _idx = -1; } IndigoAtomsIter::~IndigoAtomsIter () { } int IndigoAtomsIter::_shift (int idx) { if (_type == PSEUDO) { for (; idx != _mol->vertexEnd(); idx = _mol->vertexNext(idx)) if (_mol->isPseudoAtom(idx)) break; } else if (_type == RSITE) { for (; idx != _mol->vertexEnd(); idx = _mol->vertexNext(idx)) if (_mol->isRSite(idx)) break; } else if (_type == STEREOCENTER) { for (; idx != _mol->vertexEnd(); idx = _mol->vertexNext(idx)) if (_mol->stereocenters.getType(idx) != 0) break; } else if (_type == ALLENE_CENTER) { for (; idx != _mol->vertexEnd(); idx = _mol->vertexNext(idx)) if (_mol->allene_stereo.isCenter(idx)) break; } return idx; } bool IndigoAtomsIter::hasNext () { if (_idx == _mol->vertexEnd()) return false; int next_idx; if (_idx == -1) next_idx = _shift(_mol->vertexBegin()); else next_idx = _shift(_mol->vertexNext(_idx)); return next_idx != _mol->vertexEnd(); } IndigoObject * IndigoAtomsIter::next () { if (_idx == -1) _idx = _mol->vertexBegin(); else _idx = _mol->vertexNext(_idx); _idx = _shift(_idx); if (_idx == _mol->vertexEnd()) return 0; AutoPtr atom(new IndigoAtom(*_mol, _idx)); return atom.release(); } IndigoBond::IndigoBond (BaseMolecule &mol_, int idx_) : IndigoObject(BOND), mol(mol_) { idx = idx_; } IndigoBond::~IndigoBond () { } int IndigoBond::getIndex () { return idx; } const char * IndigoBond::debugInfo () { return ""; } bool IndigoBond::is (IndigoObject &obj) { if (obj.type == IndigoObject::BOND) return true; if (obj.type == IndigoObject::ARRAY_ELEMENT) return is(((IndigoArrayElement &)obj).get()); return false; } IndigoBond & IndigoBond::cast (IndigoObject &obj) { if (obj.type == IndigoObject::BOND) return (IndigoBond &)obj; if (obj.type == IndigoObject::ARRAY_ELEMENT) return cast(((IndigoArrayElement &)obj).get()); throw IndigoError("%s does not represent a bond", obj.debugInfo()); } void IndigoBond::remove () { mol.removeBond(idx); } IndigoBondsIter::IndigoBondsIter (BaseMolecule &mol) : IndigoObject(BONDS_ITER), _mol(mol) { _idx = -1; } IndigoBondsIter::~IndigoBondsIter () { } bool IndigoBondsIter::hasNext () { if (_idx == _mol.edgeEnd()) return false; int next_idx; if (_idx == -1) next_idx = _mol.edgeBegin(); else next_idx = _mol.edgeNext(_idx); return next_idx != _mol.edgeEnd(); } IndigoObject * IndigoBondsIter::next () { if (_idx == -1) _idx = _mol.edgeBegin(); else _idx = _mol.edgeNext(_idx); if (_idx == _mol.edgeEnd()) return 0; AutoPtr bond(new IndigoBond(_mol, _idx)); return bond.release(); } CEXPORT int indigoLoadMolecule (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); MoleculeAutoLoader loader(IndigoScanner::get(obj)); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.ignore_noncritical_query_features = self.ignore_noncritical_query_features; loader.skip_3d_chirality = self.skip_3d_chirality; AutoPtr molptr(new IndigoMolecule()); Molecule &mol = molptr->mol; loader.loadMolecule(mol); molptr->getProperties().copy(loader.properties); return self.addObject(molptr.release()); } INDIGO_END(-1); } CEXPORT int indigoLoadQueryMolecule (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); MoleculeAutoLoader loader(IndigoScanner::get(obj)); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; AutoPtr molptr(new IndigoQueryMolecule()); QueryMolecule &qmol = molptr->qmol; loader.loadQueryMolecule(qmol); molptr->copyProperties(loader.properties); return self.addObject(molptr.release()); } INDIGO_END(-1); } CEXPORT int indigoLoadSmarts (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); SmilesLoader loader(IndigoScanner::get(obj)); AutoPtr molptr(new IndigoQueryMolecule()); QueryMolecule &qmol = molptr->qmol; loader.loadSMARTS(qmol); return self.addObject(molptr.release()); } INDIGO_END(-1); } CEXPORT int indigoGrossFormula (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); AutoPtr grossptr(new IndigoGross()); GrossFormula::collect(mol, grossptr->gross); return self.addObject(grossptr.release()); } INDIGO_END(-1) } CEXPORT float indigoMolecularWeight (int molecule) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); MoleculeMass mass; return mass.molecularWeight(mol); } INDIGO_END(-1) } CEXPORT float indigoMostAbundantMass (int molecule) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); MoleculeMass mass; return mass.mostAbundantMass(mol); } INDIGO_END(-1) } CEXPORT float indigoMonoisotopicMass (int molecule) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); MoleculeMass mass; return mass.monoisotopicMass(mol); } INDIGO_END(-1) } IndigoMoleculeComponent::IndigoMoleculeComponent (BaseMolecule &mol_, int index_) : IndigoObject(COMPONENT), mol(mol_) { index = index_; } IndigoMoleculeComponent::~IndigoMoleculeComponent () { } int IndigoMoleculeComponent::getIndex () { return index; } IndigoObject * IndigoMoleculeComponent::clone () { AutoPtr res; BaseMolecule *newmol; if (mol.isQueryMolecule()) { res.reset(new IndigoQueryMolecule()); newmol = &(((IndigoQueryMolecule *)res.get())->qmol); } else { res.reset(new IndigoMolecule()); newmol = &(((IndigoMolecule *)res.get())->mol); } Filter filter(mol.getDecomposition().ptr(), Filter::EQ, index); newmol->makeSubmolecule(mol, filter, 0, 0); return res.release(); } IndigoComponentsIter::IndigoComponentsIter (BaseMolecule &mol_) : IndigoObject(COMPONENT), mol(mol_) { _idx = -1; } IndigoComponentsIter::~IndigoComponentsIter () { } bool IndigoComponentsIter::hasNext () { return _idx + 1 < mol.countComponents(); } IndigoObject * IndigoComponentsIter::next () { if (!hasNext()) return 0; _idx++; return new IndigoMoleculeComponent(mol, _idx); } IndigoSGroupAtomsIter::IndigoSGroupAtomsIter (BaseMolecule &mol, SGroup &sgroup) : IndigoObject(SGROUP_ATOMS_ITER), _mol(mol), _sgroup(sgroup) { _idx = -1; } IndigoSGroupAtomsIter::~IndigoSGroupAtomsIter () { } bool IndigoSGroupAtomsIter::hasNext () { return _idx + 1 < _sgroup.atoms.size(); } IndigoObject * IndigoSGroupAtomsIter::next () { if (!hasNext()) return 0; _idx++; return new IndigoAtom(_mol, _sgroup.atoms[_idx]); } IndigoSGroupBondsIter::IndigoSGroupBondsIter (BaseMolecule &mol, SGroup &sgroup) : IndigoObject(SGROUP_ATOMS_ITER), _mol(mol), _sgroup(sgroup) { _idx = -1; } IndigoSGroupBondsIter::~IndigoSGroupBondsIter () { } bool IndigoSGroupBondsIter::hasNext () { return _idx + 1 < _sgroup.bonds.size(); } IndigoObject * IndigoSGroupBondsIter::next () { if (!hasNext()) return 0; _idx++; return new IndigoBond(_mol, _sgroup.bonds[_idx]); } int _indigoIterateAtoms (Indigo &self, int molecule, int type) { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); AutoPtr newiter(new IndigoAtomsIter(&mol, type)); return self.addObject(newiter.release()); } CEXPORT int indigoIterateAtoms (int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); if (obj.type == IndigoObject::COMPONENT) { IndigoMoleculeComponent &mc = (IndigoMoleculeComponent &)obj; return self.addObject(new IndigoComponentAtomsIter(mc.mol, mc.index)); } if (obj.type == IndigoObject::SUBMOLECULE) { IndigoSubmolecule &sm = (IndigoSubmolecule &)obj; return self.addObject(new IndigoSubmoleculeAtomsIter(sm)); } if (obj.type == IndigoObject::DATA_SGROUP) { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(obj); return self.addObject(new IndigoSGroupAtomsIter(dsg.mol, dsg.mol.sgroups.getSGroup(dsg.idx))); } if (obj.type == IndigoObject::SUPERATOM) { IndigoSuperatom &sa = IndigoSuperatom::cast(obj); return self.addObject(new IndigoSGroupAtomsIter(sa.mol, sa.mol.sgroups.getSGroup(sa.idx))); } if (obj.type == IndigoObject::REPEATING_UNIT) { IndigoRepeatingUnit &ru = IndigoRepeatingUnit::cast(obj); return self.addObject(new IndigoSGroupAtomsIter(ru.mol, ru.mol.sgroups.getSGroup(ru.idx))); } if (obj.type == IndigoObject::MULTIPLE_GROUP) { IndigoMultipleGroup &mr = IndigoMultipleGroup::cast(obj); return self.addObject(new IndigoSGroupAtomsIter(mr.mol, mr.mol.sgroups.getSGroup(mr.idx))); } if (obj.type == IndigoObject::GENERIC_SGROUP) { IndigoGenericSGroup &gg = IndigoGenericSGroup::cast(obj); return self.addObject(new IndigoSGroupAtomsIter(gg.mol, gg.mol.sgroups.getSGroup(gg.idx))); } return _indigoIterateAtoms(self, molecule, IndigoAtomsIter::ALL); } INDIGO_END(-1); } CEXPORT int indigoIterateBonds (int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); if (obj.type == IndigoObject::COMPONENT) { IndigoMoleculeComponent &mc = (IndigoMoleculeComponent &)obj; return self.addObject(new IndigoComponentBondsIter(mc.mol, mc.index)); } if (obj.type == IndigoObject::SUBMOLECULE) { IndigoSubmolecule &sm = (IndigoSubmolecule &)obj; return self.addObject(new IndigoSubmoleculeBondsIter(sm)); } if (obj.type == IndigoObject::DATA_SGROUP) { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(obj); return self.addObject(new IndigoSGroupBondsIter(dsg.mol, dsg.mol.sgroups.getSGroup(dsg.idx))); } if (obj.type == IndigoObject::SUPERATOM) { IndigoSuperatom &sa = IndigoSuperatom::cast(obj); return self.addObject(new IndigoSGroupBondsIter(sa.mol, sa.mol.sgroups.getSGroup(sa.idx))); } if (obj.type == IndigoObject::REPEATING_UNIT) { IndigoRepeatingUnit &ru = IndigoRepeatingUnit::cast(obj); return self.addObject(new IndigoSGroupBondsIter(ru.mol, ru.mol.sgroups.getSGroup(ru.idx))); } if (obj.type == IndigoObject::MULTIPLE_GROUP) { IndigoMultipleGroup &mr = IndigoMultipleGroup::cast(obj); return self.addObject(new IndigoSGroupBondsIter(mr.mol, mr.mol.sgroups.getSGroup(mr.idx))); } if (obj.type == IndigoObject::GENERIC_SGROUP) { IndigoGenericSGroup &gg = IndigoGenericSGroup::cast(obj); return self.addObject(new IndigoSGroupBondsIter(gg.mol, gg.mol.sgroups.getSGroup(gg.idx))); } BaseMolecule &mol = obj.getBaseMolecule(); AutoPtr newiter(new IndigoBondsIter(mol)); return self.addObject(newiter.release()); } INDIGO_END(-1); } CEXPORT int indigoCountAtoms (int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); if (obj.type == IndigoObject::COMPONENT) { IndigoMoleculeComponent &mc = (IndigoMoleculeComponent &)obj; return mc.mol.countComponentVertices(mc.index); } if (obj.type == IndigoObject::SUBMOLECULE) { IndigoSubmolecule &sm = (IndigoSubmolecule &)obj; return sm.vertices.size(); } if (obj.type == IndigoObject::DATA_SGROUP) { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(obj); return dsg.get().atoms.size(); } if (obj.type == IndigoObject::SUPERATOM) { IndigoSuperatom &sa = IndigoSuperatom::cast(obj); return sa.get().atoms.size(); } BaseMolecule &mol = obj.getBaseMolecule(); return mol.vertexCount(); } INDIGO_END(-1); } CEXPORT int indigoCountBonds (int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); if (obj.type == IndigoObject::COMPONENT) { IndigoMoleculeComponent &mc = (IndigoMoleculeComponent &)obj; return mc.mol.countComponentEdges(mc.index); } if (obj.type == IndigoObject::SUBMOLECULE) { IndigoSubmolecule &sm = (IndigoSubmolecule &)obj; return sm.edges.size(); } if (obj.type == IndigoObject::DATA_SGROUP) { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(obj); return dsg.get().bonds.size(); } if (obj.type == IndigoObject::SUPERATOM) { IndigoSuperatom &sa = IndigoSuperatom::cast(obj); return sa.get().bonds.size(); } BaseMolecule &mol = obj.getBaseMolecule(); return mol.edgeCount(); } INDIGO_END(-1); } CEXPORT int indigoCountPseudoatoms (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); int i, res = 0; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.isPseudoAtom(i)) res++; return res; } INDIGO_END(-1); } CEXPORT int indigoCountRSites (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); int i, res = 0; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.isRSite(i)) res++; return res; } INDIGO_END(-1); } CEXPORT int indigoIteratePseudoatoms (int molecule) { INDIGO_BEGIN { return _indigoIterateAtoms(self, molecule, IndigoAtomsIter::PSEUDO); } INDIGO_END(-1); } CEXPORT int indigoIterateRSites (int molecule) { INDIGO_BEGIN { return _indigoIterateAtoms(self, molecule, IndigoAtomsIter::RSITE); } INDIGO_END(-1); } CEXPORT int indigoIterateStereocenters (int molecule) { INDIGO_BEGIN { return _indigoIterateAtoms(self, molecule, IndigoAtomsIter::STEREOCENTER); } INDIGO_END(-1); } CEXPORT int indigoIterateAlleneCenters (int molecule) { INDIGO_BEGIN { return _indigoIterateAtoms(self, molecule, IndigoAtomsIter::ALLENE_CENTER); } INDIGO_END(-1); } CEXPORT const char * indigoSymbol (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); auto &tmp = self.getThreadTmpData(); ia.mol.getAtomSymbol(ia.idx, tmp.string); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoIsPseudoatom (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.isPseudoAtom(ia.idx)) return 1; return 0; } INDIGO_END(-1); } CEXPORT int indigoIsRSite (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.isRSite(ia.idx)) return 1; return 0; } INDIGO_END(-1); } CEXPORT int indigoSingleAllowedRGroup (int rsite) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(rsite)); return ia.mol.getSingleAllowedRGroup(ia.idx); } INDIGO_END(-1); } IndigoRGroup::IndigoRGroup () : IndigoObject(RGROUP) { } IndigoRGroup::~IndigoRGroup () { } int IndigoRGroup::getIndex () { return idx; } IndigoRGroup & IndigoRGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::RGROUP) return (IndigoRGroup &)obj; throw IndigoError("%s is not an rgroup", obj.debugInfo()); } IndigoRGroupsIter::IndigoRGroupsIter (BaseMolecule *mol) : IndigoObject(RGROUPS_ITER) { _mol = mol; _idx = 0; } IndigoRGroupsIter::~IndigoRGroupsIter () { } CEXPORT int indigoIterateRGroups (int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &mol = obj.getBaseMolecule(); return self.addObject(new IndigoRGroupsIter(&mol)); } throw IndigoError("%s can not have r-groups", obj.debugInfo()); } INDIGO_END(-1); } IndigoRGroupFragment::IndigoRGroupFragment (IndigoRGroup &rgp, int idx) : IndigoObject(RGROUP_FRAGMENT) { rgroup.idx = rgp.idx; rgroup.mol = rgp.mol; frag_idx = idx; } IndigoRGroupFragment::IndigoRGroupFragment (BaseMolecule *mol, int rgroup_idx, int fragment_idx) : IndigoObject(RGROUP_FRAGMENT) { rgroup.mol = mol; rgroup.idx = rgroup_idx; frag_idx = fragment_idx; } IndigoRGroupFragment::~IndigoRGroupFragment () { } int IndigoRGroupFragment::getIndex () { return frag_idx; } void IndigoRGroupFragment::remove () { rgroup.mol->rgroups.getRGroup(rgroup.idx).fragments.remove(frag_idx); } QueryMolecule & IndigoRGroupFragment::getQueryMolecule () { return rgroup.mol->rgroups.getRGroup(rgroup.idx).fragments[frag_idx]->asQueryMolecule(); } Molecule & IndigoRGroupFragment::getMolecule () { return rgroup.mol->rgroups.getRGroup(rgroup.idx).fragments[frag_idx]->asMolecule(); } BaseMolecule & IndigoRGroupFragment::getBaseMolecule () { return *rgroup.mol->rgroups.getRGroup(rgroup.idx).fragments[frag_idx]; } IndigoObject * IndigoRGroupFragment::clone () { BaseMolecule *mol = rgroup.mol->rgroups.getRGroup(rgroup.idx).fragments[frag_idx]; AutoPtr molptr; if (mol->isQueryMolecule()) { molptr.reset(new IndigoQueryMolecule()); molptr->getQueryMolecule().clone(*mol, 0, 0); } else { molptr.reset(new IndigoMolecule()); molptr->getMolecule().clone(*mol, 0, 0); } return molptr.release(); } IndigoRGroupFragmentsIter::IndigoRGroupFragmentsIter (IndigoRGroup& rgp) : IndigoObject(RGROUP_FRAGMENTS_ITER) { _mol = rgp.mol; _rgroup_idx = rgp.idx; _frag_idx = -1; } IndigoRGroupFragmentsIter::~IndigoRGroupFragmentsIter () { } bool IndigoRGroupFragmentsIter::hasNext () { PtrPool &frags = _mol->rgroups.getRGroup(_rgroup_idx).fragments; if (_frag_idx == -1) return frags.begin() != frags.end(); return frags.next(_frag_idx) != frags.end(); } IndigoObject * IndigoRGroupFragmentsIter::next () { if (!hasNext()) return 0; PtrPool &frags = _mol->rgroups.getRGroup(_rgroup_idx).fragments; if (_frag_idx == -1) _frag_idx = frags.begin(); else _frag_idx = frags.next(_frag_idx); AutoPtr rgroup(new IndigoRGroupFragment(_mol, _rgroup_idx, _frag_idx)); return rgroup.release(); } CEXPORT int indigoIterateRGroupFragments (int rgroup) { INDIGO_BEGIN { IndigoRGroup &rgp = IndigoRGroup::cast(self.getObject(rgroup)); AutoPtr newiter(new IndigoRGroupFragmentsIter(rgp)); return self.addObject(newiter.release()); } INDIGO_END(-1); } bool IndigoRGroupsIter::hasNext () { bool result = false; /* * Skip empty fragments */ while((_idx < _mol->rgroups.getRGroupCount()) && (_mol->rgroups.getRGroup(_idx + 1).fragments.size() == 0)) { ++_idx; } if(_idx < _mol->rgroups.getRGroupCount()) result = true; return result; } IndigoObject * IndigoRGroupsIter::next () { if (!hasNext()) return 0; _idx += 1; AutoPtr rgroup(new IndigoRGroup()); rgroup->mol = _mol; rgroup->idx = _idx; return rgroup.release(); } CEXPORT int indigoCountAttachmentPoints (int rgroup) { INDIGO_BEGIN { IndigoObject &object = self.getObject(rgroup); if (IndigoBaseMolecule::is(object)) return object.getBaseMolecule().attachmentPointCount(); IndigoRGroup &rgp = IndigoRGroup::cast(object); return rgp.mol->rgroups.getRGroup(rgp.idx).fragments[0]->attachmentPointCount(); } INDIGO_END(-1); } CEXPORT int indigoDegree (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); return ia.mol.getVertex(ia.idx).degree(); } INDIGO_END(-1); } CEXPORT int indigoGetCharge (int atom, int *charge) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int ch = ia.mol.getAtomCharge(ia.idx); if (ch == CHARGE_UNKNOWN) { *charge = 0; return 0; } *charge = ch; return 1; } INDIGO_END(-1); } CEXPORT int indigoValence (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); return ia.mol.asMolecule().getAtomValence(ia.idx); } INDIGO_END(-1); } CEXPORT int indigoGetExplicitValence (int atom, int *valence) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int val = ia.mol.getExplicitValence(ia.idx); if (val == -1) { *valence = 0; return 0; } *valence = val; return 1; } INDIGO_END(-1); } CEXPORT int indigoSetExplicitValence (int atom, int valence) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.asMolecule().setExplicitValence(ia.idx, valence); return 1; } INDIGO_END(-1); } CEXPORT int indigoIsotope (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int iso = ia.mol.getAtomIsotope(ia.idx); return iso == -1 ? 0 : iso; } INDIGO_END(-1); } CEXPORT int indigoAtomicNumber (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.isPseudoAtom(ia.idx)) throw IndigoError("indigoAtomicNumber() called on a pseudoatom"); if (ia.mol.isRSite(ia.idx)) throw IndigoError("indigoAtomicNumber() called on an R-site"); int num = ia.mol.getAtomNumber(ia.idx); return num == -1 ? 0 : num; } INDIGO_END(-1); } CEXPORT int indigoGetRadicalElectrons (int atom, int *electrons) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int rad = ia.mol.getAtomRadical(ia.idx); if (rad == -1) { *electrons = 0; return 0; } *electrons = Element::radicalElectrons(rad); return 1; } INDIGO_END(-1); } static int mapRadicalToIndigoRadical (int radical) { switch (radical) { case 0: return 0; case RADICAL_SINGLET: return INDIGO_SINGLET; case RADICAL_DOUBLET: return INDIGO_DOUBLET; case RADICAL_TRIPLET: return INDIGO_TRIPLET; default: throw IndigoError("Unknown radical type"); } } static int mapIndigoRadicalToRadical (int indigo_radical) { switch (indigo_radical) { case 0: return 0; case INDIGO_SINGLET: return RADICAL_SINGLET; case INDIGO_DOUBLET: return RADICAL_DOUBLET; case INDIGO_TRIPLET: return RADICAL_TRIPLET; default: throw IndigoError("Unknown radical type"); } } CEXPORT int indigoGetRadical (int atom, int *radical) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int rad = ia.mol.getAtomRadical(ia.idx); if (rad == -1) { *radical = 0; return 0; } *radical = mapRadicalToIndigoRadical(rad); return 1; } INDIGO_END(-1); } CEXPORT int indigoSetRadical (int atom, int radical) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.asMolecule().setAtomRadical(ia.idx, mapIndigoRadicalToRadical(radical)); return 1; } INDIGO_END(-1); } CEXPORT float * indigoXYZ (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; Vec3f &pos = mol.getAtomXyz(ia.idx); auto &tmp = self.getThreadTmpData(); tmp.xyz[0] = pos.x; tmp.xyz[1] = pos.y; tmp.xyz[2] = pos.z; return tmp.xyz; } INDIGO_END(0) } CEXPORT int indigoSetXYZ (int atom, float x, float y, float z) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; Vec3f &pos = mol.getAtomXyz(ia.idx); pos.set(x, y, z); return 1; } INDIGO_END(0) } CEXPORT int indigoResetCharge (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; if (mol.isQueryMolecule()) mol.asQueryMolecule().getAtom(ia.idx).removeConstraints(QueryMolecule::ATOM_CHARGE); else mol.asMolecule().setAtomCharge(ia.idx, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoResetExplicitValence (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; if (mol.isQueryMolecule()) mol.asQueryMolecule().getAtom(ia.idx).removeConstraints(QueryMolecule::ATOM_VALENCE); else mol.asMolecule().resetExplicitValence(ia.idx); return 1; } INDIGO_END(-1); } CEXPORT int indigoResetRadical (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; if (mol.isQueryMolecule()) mol.asQueryMolecule().getAtom(ia.idx).removeConstraints(QueryMolecule::ATOM_RADICAL); else mol.asMolecule().setAtomRadical(ia.idx, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoResetIsotope (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; if (mol.isQueryMolecule()) mol.asQueryMolecule().getAtom(ia.idx).removeConstraints(QueryMolecule::ATOM_ISOTOPE); else mol.asMolecule().setAtomIsotope(ia.idx, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoResetRsite (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &mol = ia.mol; mol.asQueryMolecule().getAtom(ia.idx).removeConstraints(QueryMolecule::ATOM_RSITE); return 1; } INDIGO_END(-1); } CEXPORT int indigoSetAttachmentPoint (int atom, int order) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.addAttachmentPoint(order, ia.idx); return 1; } INDIGO_END(-1); } CEXPORT int indigoClearAttachmentPoints (int item) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(item).getBaseMolecule(); mol.removeAttachmentPoints(); return 1; } INDIGO_END(-1); } CEXPORT int indigoRemoveConstraints (int item, const char *str_type) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(item)); QueryMolecule &qmol = ia.mol.asQueryMolecule(); if(strcasecmp(str_type, "smarts") == 0) throw IndigoError("indigoRemoveConstraints(): type 'smarts' is not supported", str_type); AutoPtr atom; IndigoQueryMolecule::parseAtomConstraint(str_type, NULL, atom); if (atom->children.size() != 0) throw IndigoError("indigoRemoveConstraints(): can not parse type: %s", str_type); qmol.getAtom(ia.idx).removeConstraints(atom->type); qmol.invalidateAtom(ia.idx, BaseMolecule::CHANGED_ALL); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddConstraint (int atom, const char *type, const char *value) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); QueryMolecule& qmol = ia.mol.asQueryMolecule(); AutoPtr atom_constraint; IndigoQueryMolecule::parseAtomConstraint(type, value, atom_constraint); qmol.resetAtom(ia.idx, QueryMolecule::Atom::und(qmol.releaseAtom(ia.idx), atom_constraint.release())); qmol.invalidateAtom(ia.idx, BaseMolecule::CHANGED_ALL); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddConstraintNot (int atom, const char *type, const char *value) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); QueryMolecule& qmol = ia.mol.asQueryMolecule(); AutoPtr atom_constraint; IndigoQueryMolecule::parseAtomConstraint(type, value, atom_constraint); qmol.resetAtom(ia.idx, QueryMolecule::Atom::und(qmol.releaseAtom(ia.idx), QueryMolecule::Atom::nicht(atom_constraint.release()))); qmol.invalidateAtom(ia.idx, BaseMolecule::CHANGED_ALL); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddConstraintOr(int atom, const char* type, const char* value) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); QueryMolecule& qmol = ia.mol.asQueryMolecule(); AutoPtr atom_constraint; IndigoQueryMolecule::parseAtomConstraint(type, value, atom_constraint); qmol.resetAtom(ia.idx, QueryMolecule::Atom::oder(qmol.releaseAtom(ia.idx), atom_constraint.release())); qmol.invalidateAtom(ia.idx, BaseMolecule::CHANGED_ALL); return 1; } INDIGO_END(-1); } /* CEXPORT int indigoAddConstraintOrNot(int atom, const char* type, const char* value) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule *mol = ia.mol; QueryMolecule& qmol = mol->asQueryMolecule(); AutoPtr atom_constraint; IndigoQueryMolecule::parseAtomConstraint(type, value, atom_constraint); qmol.resetAtom(ia.idx, QueryMolecule::Atom::oder(qmol.releaseAtom(ia.idx), QueryMolecule::Atom::nicht(atom_constraint.release()))); return 1; } INDIGO_END(-1); } * */ CEXPORT const int * indigoSymmetryClasses (int molecule, int *count_out) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); QS_DEF(Molecule, m2); m2.clone_KeepIndices(mol); m2.aromatize(self.arom_options); QS_DEF(Array, ignored); ignored.clear_resize(m2.vertexEnd()); ignored.zerofill(); for (int i = m2.vertexBegin(); i < m2.vertexEnd(); i = m2.vertexNext(i)) if (m2.convertableToImplicitHydrogen(i)) ignored[i] = 1; MoleculeAutomorphismSearch of; QS_DEF(Array, orbits); of.find_canonical_ordering = true; of.ignored_vertices = ignored.ptr(); of.process(m2); of.getCanonicallyOrderedOrbits(orbits); auto &tmp = self.getThreadTmpData(); tmp.string.resize(orbits.sizeInBytes()); tmp.string.copy((char*)orbits.ptr(), orbits.sizeInBytes()); if (count_out != 0) *count_out= orbits.size(); return (const int*)tmp.string.ptr(); } INDIGO_END(0); } CEXPORT const char * indigoLayeredCode (int molecule) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); auto &tmp = self.getThreadTmpData(); ArrayOutput output(tmp.string); MoleculeInChI inchi_saver(output); inchi_saver.outputInChI(mol); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); } CEXPORT int indigoCreateSubmolecule (int molecule, int nvertices, int *vertices) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, vertices_arr); vertices_arr.copy(vertices, nvertices); if (mol.isQueryMolecule()) { AutoPtr molptr(new IndigoQueryMolecule()); molptr->qmol.makeSubmolecule(mol, vertices_arr, 0, 0); return self.addObject(molptr.release()); } else { AutoPtr molptr(new IndigoMolecule()); molptr->mol.makeSubmolecule(mol, vertices_arr, 0, 0); return self.addObject(molptr.release()); } } INDIGO_END(-1) } CEXPORT int indigoGetSubmolecule (int molecule, int nvertices, int *vertices) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, vertices_arr); vertices_arr.copy(vertices, nvertices); // Collect edges by vertices QS_DEF(Array, vertices_mask); vertices_mask.clear_resize(mol.vertexEnd()); vertices_mask.zerofill(); for (int i = 0; i < nvertices; i++) vertices_mask[vertices[i]] = 1; QS_DEF(Array, edges); edges.clear(); for (int i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); if (vertices_mask[edge.beg] && vertices_mask[edge.end]) edges.push(i); } AutoPtr subptr(new IndigoSubmolecule(mol, vertices_arr, edges)); return self.addObject(subptr.release()); } INDIGO_END(-1) } CEXPORT int indigoCreateEdgeSubmolecule (int molecule, int nvertices, int *vertices, int nedges, int *edges) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, vertices_arr); QS_DEF(Array, edges_arr); vertices_arr.copy(vertices, nvertices); edges_arr.copy(edges, nedges); if (mol.isQueryMolecule()) { AutoPtr molptr(new IndigoQueryMolecule()); molptr->qmol.makeEdgeSubmolecule(mol, vertices_arr, edges_arr, 0, 0); return self.addObject(molptr.release()); } else { AutoPtr molptr(new IndigoMolecule()); molptr->mol.makeEdgeSubmolecule(mol, vertices_arr, edges_arr, 0, 0); return self.addObject(molptr.release()); } } INDIGO_END(-1) } CEXPORT int indigoRemoveAtoms (int molecule, int nvertices, int *vertices) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, indices); indices.copy(vertices, nvertices); mol.removeAtoms(indices); return 1; } INDIGO_END(-1) } CEXPORT int indigoRemoveBonds (int molecule, int nbonds, int *bonds) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, indices); indices.copy(bonds, nbonds); mol.removeBonds(indices); return 1; } INDIGO_END(-1) } IndigoObject * IndigoMolecule::clone () { return cloneFrom(*this); } const char * IndigoMolecule::debugInfo () { return ""; } IndigoObject * IndigoQueryMolecule::clone () { return cloneFrom(*this); } const char * IndigoQueryMolecule::debugInfo () { return ""; } const MoleculeAtomNeighbourhoodCounters& IndigoQueryMolecule::getNeiCounters () { // TODO: implement query.getAtomEdit(...) instead of getAtom(...) to update nei counters // automatically. Current approach is too complictated because // we need to call updateEditRevision manually after changing an atom. //if (_nei_counters_edit_revision != qmol.getEditRevision()) { _nei_counters.calculate(qmol); _nei_counters_edit_revision = qmol.getEditRevision(); } return _nei_counters; } CEXPORT int indigoIsChiral (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.isChrial(); } INDIGO_END(-1) } CEXPORT int indigoBondOrder (int bond) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); int num = ib.mol.getBondOrder(ib.idx); return num == -1 ? 0 : num; } INDIGO_END(-1); } CEXPORT int indigoTopology (int bond) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); int topology = ib.mol.getBondTopology(ib.idx); if (topology == TOPOLOGY_RING) return INDIGO_RING; if (topology == TOPOLOGY_CHAIN) return INDIGO_CHAIN; return 0; } INDIGO_END(-1); } CEXPORT int indigoGetAtom (int molecule, int idx) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoAtom(mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoGetBond (int molecule, int idx) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoBond(mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoSource (int bond) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); return self.addObject(new IndigoAtom(ib.mol, ib.mol.getEdge(ib.idx).beg)); } INDIGO_END(-1) } CEXPORT int indigoDestination (int bond) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); return self.addObject(new IndigoAtom(ib.mol, ib.mol.getEdge(ib.idx).end)); } INDIGO_END(-1) } IndigoAtomNeighbor::IndigoAtomNeighbor (BaseMolecule &mol_, int atom_idx, int bond_idx_) : IndigoAtom(mol_, atom_idx) { type = ATOM_NEIGHBOR; bond_idx = bond_idx_; } IndigoAtomNeighbor::~IndigoAtomNeighbor () { } IndigoAtomNeighborsIter::IndigoAtomNeighborsIter (BaseMolecule &molecule, int atom_idx) : IndigoObject(ATOM_NEIGHBORS_ITER), _mol(molecule) { _atom_idx = atom_idx; _nei_idx = -1; } IndigoAtomNeighborsIter::~IndigoAtomNeighborsIter () { } IndigoObject * IndigoAtomNeighborsIter::next () { const Vertex &vertex = _mol.getVertex(_atom_idx); if (_nei_idx == -1) _nei_idx = vertex.neiBegin(); else if (_nei_idx != vertex.neiEnd()) _nei_idx = vertex.neiNext(_nei_idx); if (_nei_idx == vertex.neiEnd()) return 0; return new IndigoAtomNeighbor(_mol, vertex.neiVertex(_nei_idx), vertex.neiEdge(_nei_idx)); } bool IndigoAtomNeighborsIter::hasNext () { const Vertex &vertex = _mol.getVertex(_atom_idx); if (_nei_idx == -1) return vertex.neiBegin() != vertex.neiEnd(); if (_nei_idx == vertex.neiEnd()) return false; return vertex.neiNext(_nei_idx) != vertex.neiEnd(); } CEXPORT int indigoIterateNeighbors (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); return self.addObject(new IndigoAtomNeighborsIter(ia.mol, ia.idx)); } INDIGO_END(-1) } CEXPORT int indigoBond (int nei) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(nei); if (obj.type != IndigoObject::ATOM_NEIGHBOR) throw IndigoError("indigoBond(): not applicable to %s", obj.debugInfo()); IndigoAtomNeighbor &atomnei = (IndigoAtomNeighbor &)obj; return self.addObject(new IndigoBond(atomnei.mol, atomnei.bond_idx)); } INDIGO_END(-1) } CEXPORT float indigoAlignAtoms (int molecule, int natoms, int *atom_ids, float *desired_xyz) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); QS_DEF(Array, points); QS_DEF(Array, goals); int i; if (natoms < 1) throw IndigoError("indigoAlignAtoms(): can not align %d atoms", natoms); if (atom_ids == 0 || desired_xyz == 0) throw IndigoError("indigoAlignAtoms(): zero pointer given as input"); points.clear(); goals.clear(); for (i= 0; i < natoms; i++) { points.push(mol.getAtomXyz(atom_ids[i])); goals.push(Vec3f(desired_xyz[i * 3], desired_xyz[i * 3 + 1], desired_xyz[i * 3 + 2])); } if (points.size() < 1) return true; float sqsum; Transform3f matr; if (!matr.bestFit(points.size(), points.ptr(), goals.ptr(), &sqsum)) return false; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) mol.getAtomXyz(i).transformPoint(matr); return (float)(sqrt(sqsum / natoms)); } INDIGO_END(-1) } CEXPORT int indigoCountSuperatoms (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sgroups.getSGroupCount(SGroup::SG_TYPE_SUP); } INDIGO_END(-1) } CEXPORT int indigoCountDataSGroups (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sgroups.getSGroupCount(SGroup::SG_TYPE_DAT); } INDIGO_END(-1) } CEXPORT int indigoCountRepeatingUnits (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sgroups.getSGroupCount(SGroup::SG_TYPE_SRU); } INDIGO_END(-1) } CEXPORT int indigoCountMultipleGroups (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sgroups.getSGroupCount(SGroup::SG_TYPE_MUL); } INDIGO_END(-1) } CEXPORT int indigoCountGenericSGroups (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sgroups.getSGroupCount(SGroup::SG_TYPE_GEN); } INDIGO_END(-1) } IndigoDataSGroupsIter::IndigoDataSGroupsIter (BaseMolecule &molecule, Array &refs) : IndigoObject(DATA_SGROUPS_ITER), _mol(molecule), _refs(refs) { _idx = -1; } IndigoDataSGroupsIter::~IndigoDataSGroupsIter () { } bool IndigoDataSGroupsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoDataSGroupsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoDataSGroup(_mol, _refs[_idx])); return sgroup.release(); } CEXPORT int indigoIterateDataSGroups (int molecule) { INDIGO_BEGIN { QS_DEF(Array, sgs); sgs.clear(); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.sgroups.findSGroups(SGroup::SG_TYPE, SGroup::SG_TYPE_DAT, sgs); return self.addObject(new IndigoDataSGroupsIter(mol, sgs)); } INDIGO_END(-1) } IndigoDataSGroup::IndigoDataSGroup (BaseMolecule &mol_, int idx_) : IndigoObject(DATA_SGROUP), mol(mol_) { idx = idx_; } IndigoDataSGroup & IndigoDataSGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::DATA_SGROUP) return (IndigoDataSGroup &)obj; throw IndigoError("%s is not a data sgroup", obj.debugInfo()); } DataSGroup & IndigoDataSGroup::get () { return (DataSGroup &)mol.sgroups.getSGroup(idx); } void IndigoDataSGroup::remove () { mol.removeSGroup(idx); } IndigoDataSGroup::~IndigoDataSGroup () { } int IndigoDataSGroup::getIndex () { return idx; } IndigoSuperatom::IndigoSuperatom (BaseMolecule &mol_, int idx_) : IndigoObject(SUPERATOM), mol(mol_) { idx = idx_; } IndigoSuperatom::~IndigoSuperatom () { } int IndigoSuperatom::getIndex () { return idx; } void IndigoSuperatom::remove () { mol.removeSGroup(idx); } const char * IndigoSuperatom::getName () { return ((Superatom &)mol.sgroups.getSGroup(idx)).subscript.ptr(); } IndigoSuperatom & IndigoSuperatom::cast (IndigoObject &obj) { if (obj.type == IndigoObject::SUPERATOM) return (IndigoSuperatom &)obj; throw IndigoError("%s is not a superatom", obj.debugInfo()); } Superatom & IndigoSuperatom::get () { return (Superatom &)mol.sgroups.getSGroup(idx); } IndigoSuperatomsIter::IndigoSuperatomsIter (BaseMolecule &molecule, Array &refs) : IndigoObject(SUPERATOMS_ITER), _mol(molecule), _refs(refs) { _idx = -1; } IndigoSuperatomsIter::~IndigoSuperatomsIter () { } bool IndigoSuperatomsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoSuperatomsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoSuperatom(_mol, _refs[_idx])); return sgroup.release(); } IndigoRepeatingUnit::IndigoRepeatingUnit (BaseMolecule &mol_, int idx_) : IndigoObject(REPEATING_UNIT), mol(mol_) { idx = idx_; } IndigoRepeatingUnit::~IndigoRepeatingUnit () { } int IndigoRepeatingUnit::getIndex () { return idx; } void IndigoRepeatingUnit::remove () { mol.removeSGroup(idx); } IndigoRepeatingUnit & IndigoRepeatingUnit::cast (IndigoObject &obj) { if (obj.type == IndigoObject::REPEATING_UNIT) return (IndigoRepeatingUnit &)obj; throw IndigoError("%s is not a repeating unit", obj.debugInfo()); } RepeatingUnit & IndigoRepeatingUnit::get () { return (RepeatingUnit &)mol.sgroups.getSGroup(idx); } IndigoRepeatingUnitsIter::IndigoRepeatingUnitsIter (BaseMolecule &molecule, Array &refs) : IndigoObject(REPEATING_UNITS_ITER), _mol(molecule), _refs(refs) { _idx = -1; } IndigoRepeatingUnitsIter::~IndigoRepeatingUnitsIter () { } bool IndigoRepeatingUnitsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoRepeatingUnitsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoRepeatingUnit(_mol, _refs[_idx])); return sgroup.release(); } IndigoMultipleGroup::IndigoMultipleGroup (BaseMolecule &mol_, int idx_) : IndigoObject(MULTIPLE_GROUP), mol(mol_) { idx = idx_; } IndigoMultipleGroup::~IndigoMultipleGroup () { } int IndigoMultipleGroup::getIndex () { return idx; } void IndigoMultipleGroup::remove () { mol.removeSGroup(idx); } IndigoMultipleGroup & IndigoMultipleGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::MULTIPLE_GROUP) return (IndigoMultipleGroup &)obj; throw IndigoError("%s is not a multiple group", obj.debugInfo()); } MultipleGroup & IndigoMultipleGroup::get () { return (MultipleGroup &)mol.sgroups.getSGroup(idx); } IndigoMultipleGroupsIter::IndigoMultipleGroupsIter (BaseMolecule &molecule, Array &refs) : IndigoObject(MULTIPLE_GROUPS_ITER), _mol(molecule), _refs(refs) { _idx = -1; } IndigoMultipleGroupsIter::~IndigoMultipleGroupsIter () { } bool IndigoMultipleGroupsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoMultipleGroupsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoMultipleGroup(_mol, _refs[_idx])); return sgroup.release(); } IndigoGenericSGroup::IndigoGenericSGroup (BaseMolecule &mol_, int idx_) : IndigoObject(GENERIC_SGROUP), mol(mol_) { idx = idx_; } IndigoGenericSGroup::~IndigoGenericSGroup () { } int IndigoGenericSGroup::getIndex () { return idx; } void IndigoGenericSGroup::remove () { mol.removeSGroup(idx); } IndigoGenericSGroup & IndigoGenericSGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::GENERIC_SGROUP) return (IndigoGenericSGroup &)obj; throw IndigoError("%s is not a generic sgroup", obj.debugInfo()); } SGroup & IndigoGenericSGroup::get () { return (SGroup &)mol.sgroups.getSGroup(idx); } IndigoGenericSGroupsIter::IndigoGenericSGroupsIter (BaseMolecule &molecule, Array &refs) : IndigoObject(GENERIC_SGROUPS_ITER), _mol(molecule), _refs(refs) { _idx = -1; } IndigoGenericSGroupsIter::~IndigoGenericSGroupsIter () { } bool IndigoGenericSGroupsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoGenericSGroupsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoGenericSGroup(_mol, _refs[_idx])); return sgroup.release(); } CEXPORT int indigoIterateGenericSGroups (int molecule) { INDIGO_BEGIN { QS_DEF(Array, sgs); sgs.clear(); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.sgroups.findSGroups(SGroup::SG_TYPE, SGroup::SG_TYPE_GEN, sgs); return self.addObject(new IndigoGenericSGroupsIter(mol, sgs)); } INDIGO_END(-1) } IndigoSGroup::IndigoSGroup (BaseMolecule &mol_, int sg_idx_) : IndigoObject(SGROUP), mol(mol_) { idx = sg_idx_; } IndigoSGroup::~IndigoSGroup () { } const char * IndigoSGroup::debugInfo () { return ""; } int IndigoSGroup::getIndex () { return idx; } void IndigoSGroup::remove () { mol.removeSGroup(idx); } IndigoSGroup & IndigoSGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::SGROUP) return (IndigoSGroup &)obj; throw IndigoError("%s is not a sgroup", obj.debugInfo()); } SGroup & IndigoSGroup::get () { return (SGroup &)mol.sgroups.getSGroup(idx); } IndigoSGroupsIter::IndigoSGroupsIter (BaseMolecule &molecule, Array &sg_refs) : IndigoObject(SGROUPS_ITER), _mol(molecule), _refs(sg_refs) { _idx = -1; } IndigoSGroupsIter::~IndigoSGroupsIter () { } const char * IndigoSGroupsIter::debugInfo () { return ""; } bool IndigoSGroupsIter::hasNext () { if (_idx == -1) return _refs.size() > 0; return _idx + 1 < _refs.size(); } IndigoObject * IndigoSGroupsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr sgroup(new IndigoSGroup(_mol, _refs[_idx])); return sgroup.release(); } IndigoTGroup::IndigoTGroup (BaseMolecule &mol_, int tg_idx_) : IndigoObject(TGROUP), mol(mol_) { idx = tg_idx_; } IndigoTGroup::~IndigoTGroup () { } const char * IndigoTGroup::debugInfo () { return ""; } int IndigoTGroup::getIndex () { return idx; } void IndigoTGroup::remove () { mol.tgroups.remove(idx); } IndigoTGroup & IndigoTGroup::cast (IndigoObject &obj) { if (obj.type == IndigoObject::TGROUP) return (IndigoTGroup &)obj; throw IndigoError("%s is not a tgroup", obj.debugInfo()); } TGroup & IndigoTGroup::get () { return (TGroup &)mol.tgroups.getTGroup(idx); } IndigoTGroupsIter::IndigoTGroupsIter (BaseMolecule &molecule) : IndigoObject(TGROUPS_ITER), _mol(molecule) { _idx = -1; } IndigoTGroupsIter::~IndigoTGroupsIter () { } const char * IndigoTGroupsIter::debugInfo () { return ""; } bool IndigoTGroupsIter::hasNext () { if (_idx == -1) return _mol.tgroups.getTGroupCount() > 0; return _idx + 1 < _mol.tgroups.getTGroupCount(); } IndigoObject * IndigoTGroupsIter::next () { if (!hasNext()) return 0; if (_idx == -1) _idx = 0; else _idx++; AutoPtr tgroup(new IndigoTGroup(_mol, _idx)); return tgroup.release(); } CEXPORT int indigoIterateTGroups (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoTGroupsIter(mol)); } INDIGO_END(-1) } CEXPORT int indigoIterateRepeatingUnits (int molecule) { INDIGO_BEGIN { QS_DEF(Array, sgs); sgs.clear(); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.sgroups.findSGroups(SGroup::SG_TYPE, SGroup::SG_TYPE_SRU, sgs); return self.addObject(new IndigoRepeatingUnitsIter(mol, sgs)); } INDIGO_END(-1) } CEXPORT int indigoIterateMultipleGroups (int molecule) { INDIGO_BEGIN { QS_DEF(Array, sgs); sgs.clear(); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.sgroups.findSGroups(SGroup::SG_TYPE, SGroup::SG_TYPE_MUL, sgs); return self.addObject(new IndigoMultipleGroupsIter(mol, sgs)); } INDIGO_END(-1) } CEXPORT int indigoIterateSuperatoms (int molecule) { INDIGO_BEGIN { QS_DEF(Array, sgs); sgs.clear(); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.sgroups.findSGroups(SGroup::SG_TYPE, SGroup::SG_TYPE_SUP, sgs); return self.addObject(new IndigoSuperatomsIter(mol, sgs)); } INDIGO_END(-1) } CEXPORT int indigoGetSuperatom (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index > (mol.sgroups.getSGroupCount() - 1)) throw IndigoError("Invalid Sgroup index %d (size = %d)", index, mol.sgroups.getSGroupCount()); SGroup &sg = mol.sgroups.getSGroup(index); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) return self.addObject(new IndigoSuperatom(mol, index)); throw IndigoError("Sgroup with index %d is not a Superatom", index); } INDIGO_END(-1) } CEXPORT int indigoGetDataSGroup (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index > (mol.sgroups.getSGroupCount() - 1)) throw IndigoError("Invalid Sgroup index %d (size = %d)", index, mol.sgroups.getSGroupCount()); SGroup &sg = mol.sgroups.getSGroup(index); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) return self.addObject(new IndigoDataSGroup(mol, index)); throw IndigoError("Sgroup with index %d is not a DataSGroup", index); } INDIGO_END(-1) } CEXPORT int indigoGetGenericSGroup (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index > (mol.sgroups.getSGroupCount() - 1)) throw IndigoError("Invalid Sgroup index %d (size = %d)", index, mol.sgroups.getSGroupCount()); SGroup &sg = mol.sgroups.getSGroup(index); if (sg.sgroup_type == SGroup::SG_TYPE_GEN) return self.addObject(new IndigoGenericSGroup(mol, index)); throw IndigoError("Sgroup with index %d is not a GenericSGroup", index); } INDIGO_END(-1) } CEXPORT int indigoGetMultipleGroup (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index > (mol.sgroups.getSGroupCount() - 1)) throw IndigoError("Invalid Sgroup index %d (size = %d)", index, mol.sgroups.getSGroupCount()); SGroup &sg = mol.sgroups.getSGroup(index); if (sg.sgroup_type == SGroup::SG_TYPE_MUL) return self.addObject(new IndigoMultipleGroup(mol, index)); throw IndigoError("Sgroup with index %d is not a MultipleGroup", index); } INDIGO_END(-1) } CEXPORT int indigoGetRepeatingUnit (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index > (mol.sgroups.getSGroupCount() - 1)) throw IndigoError("Invalid Sgroup index %d (size = %d)", index, mol.sgroups.getSGroupCount()); SGroup &sg = mol.sgroups.getSGroup(index); if (sg.sgroup_type == SGroup::SG_TYPE_SRU) return self.addObject(new IndigoRepeatingUnit(mol, index)); throw IndigoError("Sgroup with index %d is not a RepeatingUnit", index); } INDIGO_END(-1) } CEXPORT const char * indigoDescription (int data_sgroup) { INDIGO_BEGIN { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(data_sgroup)); if (dsg.get().name.size() < 1) return ""; return dsg.get().name.ptr(); } INDIGO_END(0) } CEXPORT const char * indigoData (int data_sgroup) { INDIGO_BEGIN { IndigoDataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(data_sgroup)); if (dsg.get().data.size() < 1) return ""; return dsg.get().data.ptr(); } INDIGO_END(0) } CEXPORT int indigoAddDataSGroup (int molecule, int natoms, int *atoms, int nbonds, int *bonds, const char *name, const char *data) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); int idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_DAT); DataSGroup &dsg = (DataSGroup &)mol.sgroups.getSGroup(idx); int i; if (atoms != 0) for (i = 0; i < natoms; i++) dsg.atoms.push(atoms[i]); if (bonds != 0) for (i = 0; i < nbonds; i++) dsg.bonds.push(bonds[i]); if (data != 0) dsg.data.readString(data, false); if (name != 0) dsg.name.readString(name, true); return self.addObject(new IndigoDataSGroup(mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoAddSuperatom (int molecule, int natoms, int *atoms, const char *name) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); int idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_SUP); Superatom &satom = (Superatom &)mol.sgroups.getSGroup(idx); satom.subscript.appendString(name, true); if (atoms == 0) throw IndigoError("indigoAddSuperatom(): atoms were not specified"); for (int i = 0; i < natoms; i++) satom.atoms.push(atoms[i]); return self.addObject(new IndigoSuperatom(mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoSetDataSGroupXY (int sgroup, float x, float y, const char *options) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); dsg.display_pos.x = x; dsg.display_pos.y = y; dsg.detached = true; if (options != 0 && options[0] != 0) { if (strcasecmp(options, "absolute") == 0) dsg.relative = false; else if (strcasecmp(options, "relative") == 0) dsg.relative = true; else throw IndigoError("indigoSetDataSGroupXY(): invalid options string"); } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupData (int sgroup, const char *data) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (data != 0) dsg.data.readString(data, false); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupCoords (int sgroup, float x, float y) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); dsg.display_pos.x = x; dsg.display_pos.y = y; return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupDescription (int sgroup, const char *description) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (description != 0) dsg.description.readString(description, true); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupFieldName (int sgroup, const char *name) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (name != 0) dsg.name.readString(name, true); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupQueryCode (int sgroup, const char *querycode) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (querycode != 0) dsg.querycode.readString(querycode, true); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupQueryOper (int sgroup, const char *queryoper) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (queryoper != 0) dsg.queryoper.readString(queryoper, true); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupDisplay (int sgroup, const char *option) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (option != 0 && option[0] != 0) { if (strcasecmp(option, "attached") == 0) dsg.detached = false; else if (strcasecmp(option, "detached") == 0) dsg.detached = true; else throw IndigoError("indigoSetSgroupDisplay(): invalid option string"); } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupLocation (int sgroup, const char *option) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (option != 0 && option[0] != 0) { if (strcasecmp(option, "absolute") == 0) dsg.relative = false; else if (strcasecmp(option, "relative") == 0) dsg.relative = true; else throw IndigoError("indigoSetSgroupLocation(): invalid option string"); } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupTag (int sgroup, const char *tag) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (tag != 0 && tag[0] != 0) { dsg.tag = tag[0]; } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupTagAlign (int sgroup, int tag_align) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (tag_align > 0 && tag_align < 10) { dsg.dasp_pos = tag_align; } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupDataType (int sgroup, const char *data_type) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); if (data_type != 0 && data_type[0] != 0) { dsg.type.readString(data_type, true); } return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupXCoord (int sgroup, float x) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); dsg.display_pos.x = x; return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupYCoord (int sgroup, float y) { INDIGO_BEGIN { DataSGroup &dsg = IndigoDataSGroup::cast(self.getObject(sgroup)).get(); dsg.display_pos.y = y; return 1; } INDIGO_END(-1) } CEXPORT int indigoCreateSGroup (const char *type, int mapping, const char *name) { INDIGO_BEGIN { IndigoMapping &map = IndigoMapping::cast(self.getObject(mapping)); BaseMolecule &mol = map.to; BaseMolecule &temp = map.from; Array &m = map.mapping; int idx = mol.sgroups.addSGroup(type); if (idx != -1) { SGroup &sgroup = mol.sgroups.getSGroup(idx); for (auto i : temp.vertices()) { sgroup.atoms.push(m[i]); } for (auto i : mol.edges()) { const Edge &edge = mol.getEdge(i); if (((sgroup.atoms.find(edge.beg) != -1) && (sgroup.atoms.find(edge.end) == -1)) || ((sgroup.atoms.find(edge.end) != -1) && (sgroup.atoms.find(edge.beg) == -1))) { sgroup.bonds.push(i); } } if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sgroup; sa.subscript.appendString(name, true); return self.addObject(new IndigoSuperatom(mol, idx)); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &ru = (RepeatingUnit &)sgroup; ru.subscript.appendString(name, true); return self.addObject(new IndigoRepeatingUnit(mol, idx)); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { return self.addObject(new IndigoMultipleGroup(mol, idx)); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { return self.addObject(new IndigoDataSGroup(mol, idx)); } else { return self.addObject(new IndigoGenericSGroup(mol, idx)); } } } INDIGO_END(-1) } CEXPORT int indigoSetSGroupClass (int sgroup, const char *sgclass) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); sup.sa_class.readString(sgclass, true); return 1; } INDIGO_END(-1) } CEXPORT const char * indigoGetSGroupClass (int sgroup) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); if (sup.sa_class.size() < 1) return ""; return sup.sa_class.ptr(); } INDIGO_END(0) } CEXPORT int indigoSetSGroupName (int sgroup, const char *sgname) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); sup.subscript.readString(sgname, true); return 1; } INDIGO_END(-1) } CEXPORT const char * indigoGetSGroupName (int sgroup) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); if (sup.subscript.size() < 1) return ""; return sup.subscript.ptr(); } INDIGO_END(0) } CEXPORT int indigoGetSGroupNumCrossBonds (int sgroup) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); return sup.bonds.size(); } INDIGO_END(-1) } CEXPORT int indigoAddSGroupAttachmentPoint (int sgroup, int aidx, int lvidx, const char *apid) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); int ap_idx = sup.attachment_points.add(); Superatom::_AttachmentPoint &ap = sup.attachment_points.at(ap_idx); ap.aidx = aidx; ap.lvidx = lvidx; ap.apid.readString(apid, true); return ap_idx; } INDIGO_END(-1) } CEXPORT int indigoDeleteSGroupAttachmentPoint (int sgroup, int ap_idx) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); sup.attachment_points.remove(ap_idx); return 1; } INDIGO_END(-1) } CEXPORT int indigoGetSGroupDisplayOption (int sgroup) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); if (sup.contracted > -1) return sup.contracted; return 0; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupDisplayOption (int sgroup, int option) { INDIGO_BEGIN { Superatom &sup = IndigoSuperatom::cast(self.getObject(sgroup)).get(); sup.contracted = option; return 1; } INDIGO_END(-1) } CEXPORT int indigoGetSGroupMultiplier (int sgroup) { INDIGO_BEGIN { MultipleGroup &mg = IndigoMultipleGroup::cast(self.getObject(sgroup)).get(); return mg.multiplier; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupMultiplier (int sgroup, int multiplier) { INDIGO_BEGIN { MultipleGroup &mg = IndigoMultipleGroup::cast(self.getObject(sgroup)).get(); mg.multiplier = multiplier; return 1; } INDIGO_END(-1) } CEXPORT int indigoSetSGroupBrackets (int sgroup, int brk_style, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { INDIGO_BEGIN { SGroup *psg = 0; if (self.getObject(sgroup).type == IndigoObject::GENERIC_SGROUP) psg = &(IndigoGenericSGroup::cast(self.getObject(sgroup)).get()); else if (self.getObject(sgroup).type == IndigoObject::REPEATING_UNIT) psg = &(IndigoRepeatingUnit::cast(self.getObject(sgroup)).get()); else if (self.getObject(sgroup).type == IndigoObject::MULTIPLE_GROUP) psg = &(IndigoMultipleGroup::cast(self.getObject(sgroup)).get()); else throw IndigoError("indigoSetSgroupBrackets(): brackets properties are not supported for this Sgroup type"); psg->brk_style = brk_style; psg->brackets.clear(); Vec2f *brackets = psg->brackets.push(); brackets[0].set(x1, y1); brackets[1].set(x2, y2); brackets = psg->brackets.push(); brackets[0].set(x3, y3); brackets[1].set(x4, y4); return 1; } INDIGO_END(-1) } CEXPORT int indigoFindSGroups (int item, const char *property, const char *value) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(item).getBaseMolecule(); QS_DEF(Array, sgs); sgs.clear(); mol.sgroups.findSGroups(property, value, sgs); return self.addObject(new IndigoSGroupsIter(mol, sgs)); } INDIGO_END(-1) } CEXPORT int indigoGetSGroupType (int sgroup) { INDIGO_BEGIN { IndigoSGroup &sg = IndigoSGroup::cast(self.getObject(sgroup)); return sg.get().sgroup_type; } INDIGO_END(-1) } CEXPORT int indigoGetSGroupIndex (int sgroup) { INDIGO_BEGIN { IndigoSGroup &sg = IndigoSGroup::cast(self.getObject(sgroup)); return sg.idx; } INDIGO_END(-1) } CEXPORT int indigoTransformSCSRtoCTAB (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.transformSCSRtoFullCTAB(); return 1; } INDIGO_END(-1) } CEXPORT int indigoTransformCTABtoSCSR (int molecule, int templates) { INDIGO_BEGIN { QS_DEF(ObjArray, tgs); BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); BaseMolecule &temp = self.getObject(templates).getBaseMolecule(); for (auto i = temp.tgroups.begin(); i != temp.tgroups.end(); i = temp.tgroups.next(i)) { TGroup &tg = tgs.push(); tg.copy(temp.tgroups.getTGroup(i)); } mol.transformFullCTABtoSCSR(tgs); return 1; } INDIGO_END(-1) } CEXPORT int indigoCountHeavyAtoms (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); int i, cnt = 0; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (!mol.possibleAtomNumber(i, ELEM_H)) cnt++; return cnt; } INDIGO_END(-1) } CEXPORT int indigoCountComponents (int molecule) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return bm.countComponents(); } INDIGO_END(-1) } CEXPORT int indigoCloneComponent (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index >= bm.countComponents()) throw IndigoError("indigoCloneComponent(): bad index %d (0-%d allowed)", bm.countComponents() - 1); Filter filter(bm.getDecomposition().ptr(), Filter::EQ, index); AutoPtr im(new IndigoMolecule()); im->mol.makeSubmolecule(bm, filter, 0, 0); return self.addObject(im.release()); } INDIGO_END(-1) } CEXPORT int indigoComponentIndex (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); return ia.mol.vertexComponent(ia.idx); } INDIGO_END(-1) } CEXPORT int indigoComponent (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); if (index < 0 || index >= bm.countComponents()) throw IndigoError("indigoComponent(): bad index %d (0-%d allowed)", bm.countComponents() - 1); return self.addObject(new IndigoMoleculeComponent(bm, index)); } INDIGO_END(-1) } IndigoComponentAtomsIter::IndigoComponentAtomsIter (BaseMolecule &mol, int cidx) : IndigoObject(COMPONENT_ATOMS_ITER), _mol(mol) { if (cidx < 0 || cidx > mol.countComponents()) throw IndigoError("%d is not a valid component number (0-%d allowed)", cidx, _mol.countComponents() - 1); _idx = -1; _cidx = cidx; } IndigoComponentAtomsIter::~IndigoComponentAtomsIter () { } bool IndigoComponentAtomsIter::hasNext () { return _next() != _mol.vertexEnd(); } IndigoObject * IndigoComponentAtomsIter::next () { int idx = _next(); if (idx == _mol.vertexEnd()) return 0; _idx = idx; return new IndigoAtom(_mol, idx); } int IndigoComponentAtomsIter::_next () { int idx; if (_idx == -1) idx = _mol.vertexBegin(); else idx = _mol.vertexNext(_idx); for (; idx != _mol.vertexEnd(); idx = _mol.vertexNext(idx)) if (_mol.vertexComponent(idx) == _cidx) break; return idx; } IndigoComponentBondsIter::IndigoComponentBondsIter (BaseMolecule &mol, int cidx) : IndigoObject(COMPONENT_BONDS_ITER), _mol(mol) { if (cidx < 0 || cidx > _mol.countComponents()) throw IndigoError("%d is not a valid component number (0-%d allowed)", cidx, _mol.countComponents() - 1); _idx = -1; _cidx = cidx; } IndigoComponentBondsIter::~IndigoComponentBondsIter () { } bool IndigoComponentBondsIter::hasNext () { return _next() != _mol.edgeEnd(); } IndigoObject * IndigoComponentBondsIter::next () { int idx = _next(); if (idx == _mol.edgeEnd()) return 0; _idx = idx; return new IndigoBond(_mol, idx); } int IndigoComponentBondsIter::_next () { int idx; if (_idx == -1) idx = _mol.edgeBegin(); else idx = _mol.edgeNext(_idx); for (; idx != _mol.edgeEnd(); idx = _mol.edgeNext(idx)) { const Edge &edge = _mol.getEdge(idx); int comp = _mol.vertexComponent(edge.beg); if (comp != _mol.vertexComponent(edge.end)) throw IndigoError("internal: edge ends belong to different components"); if (comp == _cidx) break; } return idx; } CEXPORT int indigoIterateComponents (int molecule) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoComponentsIter(bm)); } INDIGO_END(-1) } CEXPORT int indigoIterateComponentAtoms (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoComponentAtomsIter(bm, index)); } INDIGO_END(-1) } CEXPORT int indigoIterateComponentBonds (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoComponentBondsIter(bm, index)); } INDIGO_END(-1) } CEXPORT int indigoCountComponentAtoms (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return bm.countComponentVertices(index); } INDIGO_END(-1) } CEXPORT int indigoCountComponentBonds (int molecule, int index) { INDIGO_BEGIN { BaseMolecule &bm = self.getObject(molecule).getBaseMolecule(); return bm.countComponentEdges(index); } INDIGO_END(-1) } CEXPORT int indigoCreateMolecule () { INDIGO_BEGIN { AutoPtr obj(new IndigoMolecule()); return self.addObject(obj.release()); } INDIGO_END(-1) } CEXPORT int indigoCreateQueryMolecule () { INDIGO_BEGIN { return self.addObject(new IndigoQueryMolecule()); } INDIGO_END(-1) } CEXPORT int indigoMerge (int where, int what) { INDIGO_BEGIN { BaseMolecule &mol_where = self.getObject(where).getBaseMolecule(); BaseMolecule &mol_what = self.getObject(what).getBaseMolecule(); AutoPtr res(new IndigoMapping(mol_what, mol_where)); mol_where.mergeWithMolecule(mol_what, &res->mapping, 0); return self.addObject(res.release()); } INDIGO_END(-1) } CEXPORT int indigoAddAtom (int molecule, const char *symbol) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); BaseMolecule &bmol = obj.getBaseMolecule(); int idx; if (bmol.isQueryMolecule()) { QueryMolecule &qmol = bmol.asQueryMolecule(); idx = qmol.addAtom(IndigoQueryMolecule::parseAtomSMARTS(symbol)); } else { Molecule &mol = bmol.asMolecule(); int elem = Element::fromString2(symbol); if (elem > 0) idx = mol.addAtom(elem); else { idx = mol.addAtom(ELEM_PSEUDO); mol.setPseudoAtom(idx, symbol); } } return self.addObject(new IndigoAtom(bmol, idx)); } INDIGO_END(-1) } CEXPORT int indigoResetAtom (int atom, const char *symbol) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); BaseMolecule &bmol = ia.mol; if (bmol.isQueryMolecule()) { QueryMolecule &qmol = bmol.asQueryMolecule(); qmol.resetAtom(ia.idx, IndigoQueryMolecule::parseAtomSMARTS(symbol)); } else { Molecule &mol = ia.mol.asMolecule(); int elem = Element::fromString2(symbol); if (elem > 0) mol.resetAtom(ia.idx, elem); else { mol.resetAtom(ia.idx, ELEM_PSEUDO); mol.setPseudoAtom(ia.idx, symbol); } } bmol.invalidateAtom(ia.idx, BaseMolecule::CHANGED_ATOM_NUMBER); return 1; } INDIGO_END(-1) } static void _parseRSites (const char *name, Array &rsites) { BufferScanner scanner(name); rsites.clear(); while (!scanner.isEOF()) { scanner.skipSpace(); if (scanner.lookNext() != 'R') throw IndigoError("indigoAddRSite(): cannot parse '%s' as r-site name(s)", name); scanner.readChar(); if (scanner.isEOF()) break; if (isdigit(scanner.lookNext())) { int idx = scanner.readInt(); rsites.push(idx); } scanner.skipSpace(); if (scanner.lookNext() == ',' || scanner.lookNext() == ';') scanner.readChar(); } } static void _indigoSetRSite (Molecule &mol, int atom_index, const char *name) { // Parse r-sites QS_DEF(Array, rsites); _parseRSites(name, rsites); mol.resetAtom(atom_index, ELEM_RSITE); mol.setRSiteBits(atom_index, 0); for (int i = 0; i < rsites.size(); i++) mol.allowRGroupOnRSite(atom_index, rsites[i]); } CEXPORT int indigoAddRSite (int molecule, const char *name) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); int idx = mol.addAtom(ELEM_RSITE); try { _indigoSetRSite(mol, idx, name); } catch (...) { // Remove atom if there is an exception (r-site index is very big for example) mol.removeAtom(idx); throw; } return self.addObject(new IndigoAtom(mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoSetRSite (int atom, const char *name) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); Molecule &mol = ia.mol.asMolecule(); _indigoSetRSite(mol, ia.idx, name); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetCharge (int atom, int charge) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.asMolecule().setAtomCharge(ia.idx, charge); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetIsotope (int atom, int isotope) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.asMolecule().setAtomIsotope(ia.idx, isotope); return 1; } INDIGO_END(-1) } CEXPORT int indigoSetImplicitHCount (int atom, int impl_h) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); ia.mol.asMolecule().setImplicitH(ia.idx, impl_h); return 1; } INDIGO_END(-1) } CEXPORT int indigoAddBond (int source, int destination, int order) { INDIGO_BEGIN { IndigoAtom &s_atom = IndigoAtom::cast(self.getObject(source)); IndigoAtom &d_atom = IndigoAtom::cast(self.getObject(destination)); if (&s_atom.mol != &d_atom.mol) throw IndigoError("indigoAddBond(): molecules do not match"); int idx; if (s_atom.mol.isQueryMolecule()) idx = s_atom.mol.asQueryMolecule().addBond(s_atom.idx, d_atom.idx, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, order)); else idx = s_atom.mol.asMolecule().addBond(s_atom.idx, d_atom.idx, order); return self.addObject(new IndigoBond(s_atom.mol, idx)); } INDIGO_END(-1) } CEXPORT int indigoSetBondOrder (int bond, int order) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); ib.mol.asMolecule().setBondOrder(ib.idx, order, false); return 1; } INDIGO_END(-1) } IndigoSubmolecule::IndigoSubmolecule (BaseMolecule &mol_, Array &vertices_, Array &edges_) : IndigoObject(SUBMOLECULE), mol(mol_) { vertices.copy(vertices_); edges.copy(edges_); idx = -1; } IndigoSubmolecule::IndigoSubmolecule (BaseMolecule &mol_, List &vertices_, List &edges_) : IndigoObject(SUBMOLECULE), mol(mol_) { int i; vertices.clear(); edges.clear(); for (i = vertices_.begin(); i != vertices_.end(); i = vertices_.next(i)) vertices.push(vertices_[i]); for (i = edges_.begin(); i != edges_.end(); i = edges_.next(i)) edges.push(edges_[i]); idx = -1; } IndigoSubmolecule::~IndigoSubmolecule () { } BaseMolecule & IndigoSubmolecule::getBaseMolecule () { return mol; } int IndigoSubmolecule::getIndex () { if (idx == -1) throw IndigoError("index not set"); return idx; } IndigoObject * IndigoSubmolecule::clone () { AutoPtr res; BaseMolecule *newmol; if (mol.isQueryMolecule()) { res.reset(new IndigoQueryMolecule()); newmol = &(((IndigoQueryMolecule *)res.get())->qmol); } else { res.reset(new IndigoMolecule()); newmol = &(((IndigoMolecule *)res.get())->mol); } newmol->makeEdgeSubmolecule(mol, vertices, edges, 0, 0); return res.release(); } IndigoSubmoleculeAtomsIter::IndigoSubmoleculeAtomsIter (IndigoSubmolecule &submol) : IndigoObject(SUBMOLECULE_ATOMS_ITER), _submol(submol) { _idx = -1; } IndigoSubmoleculeAtomsIter::~IndigoSubmoleculeAtomsIter () { } bool IndigoSubmoleculeAtomsIter::hasNext () { return _idx + 1 < _submol.vertices.size(); } IndigoObject * IndigoSubmoleculeAtomsIter::next () { if (!hasNext()) return 0; _idx++; return new IndigoAtom(_submol.mol, _submol.vertices[_idx]); } IndigoSubmoleculeBondsIter::IndigoSubmoleculeBondsIter (IndigoSubmolecule &submol) : IndigoObject(SUBMOLECULE_BONDS_ITER), _submol(submol) { _idx = -1; } IndigoSubmoleculeBondsIter::~IndigoSubmoleculeBondsIter () { } bool IndigoSubmoleculeBondsIter::hasNext () { return _idx + 1 < _submol.edges.size(); } IndigoObject * IndigoSubmoleculeBondsIter::next () { if (!hasNext()) return 0; _idx++; return new IndigoBond(_submol.mol, _submol.edges[_idx]); } CEXPORT int indigoCountSSSR (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.sssrCount(); } INDIGO_END(-1) } CEXPORT int indigoIterateSSSR (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoSSSRIter(mol)); } INDIGO_END(-1) } IndigoSSSRIter::IndigoSSSRIter (BaseMolecule &mol) : IndigoObject(SSSR_ITER), _mol(mol) { _idx = -1; } IndigoSSSRIter::~IndigoSSSRIter () { } bool IndigoSSSRIter::hasNext () { return _idx + 1 < _mol.sssrCount(); } IndigoObject * IndigoSSSRIter::next () { if (!hasNext()) return 0; _idx++; List &vertices = _mol.sssrVertices(_idx); List &edges = _mol.sssrEdges(_idx); AutoPtr res(new IndigoSubmolecule(_mol, vertices, edges)); res->idx = _idx; return res.release(); } IndigoSubtreesIter::IndigoSubtreesIter (BaseMolecule &mol, int min_vertices, int max_vertices) : IndigoObject(SUBTREES_ITER), _mol(mol), _enumerator(mol) { _enumerator.min_vertices = min_vertices; _enumerator.max_vertices = max_vertices; _enumerator.context = this; _enumerator.callback = _handleTree; _enumerator.process(); _idx = -1; } IndigoSubtreesIter::~IndigoSubtreesIter () { } void IndigoSubtreesIter::_handleTree (Graph &graph, const Array &vertices, const Array &edges, void *context) { IndigoSubtreesIter *self = (IndigoSubtreesIter *)context; Array &self_vertices = self->_vertices.push(); Array &self_edges = self->_edges.push(); self_vertices.copy(vertices); self_edges.copy(edges); } bool IndigoSubtreesIter::hasNext () { return _idx + 1 < _vertices.size(); } IndigoObject * IndigoSubtreesIter::next () { if (!hasNext()) return 0; _idx++; AutoPtr res(new IndigoSubmolecule(_mol, _vertices[_idx], _edges[_idx])); res->idx = _idx; return res.release(); } CEXPORT int indigoIterateSubtrees (int molecule, int min_atoms, int max_atoms) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoSubtreesIter(mol, min_atoms, max_atoms)); } INDIGO_END(-1) } IndigoRingsIter::IndigoRingsIter (BaseMolecule &mol, int min_vertices, int max_vertices) : IndigoObject(RINGS_ITER), _mol(mol), _enumerator(mol) { _enumerator.min_length = min_vertices; _enumerator.max_length = max_vertices; _enumerator.context = this; _enumerator.cb_handle_cycle = _handleCycle; _enumerator.process(); _idx = -1; } IndigoRingsIter::~IndigoRingsIter () { } bool IndigoRingsIter::_handleCycle (Graph &graph, const Array &vertices, const Array &edges, void *context) { IndigoRingsIter *self = (IndigoRingsIter *)context; self->_vertices.push().copy(vertices); self->_edges.push().copy(edges); return true; } bool IndigoRingsIter::hasNext () { return _idx + 1 < _vertices.size(); } IndigoObject * IndigoRingsIter::next () { if (!hasNext()) return 0; _idx++; AutoPtr res(new IndigoSubmolecule(_mol, _vertices[_idx], _edges[_idx])); res->idx = _idx; return res.release(); } CEXPORT int indigoIterateRings (int molecule, int min_atoms, int max_atoms) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoRingsIter(mol, min_atoms, max_atoms)); } INDIGO_END(-1) } IndigoEdgeSubmoleculeIter::IndigoEdgeSubmoleculeIter (BaseMolecule &mol, int min_edges, int max_edges) : IndigoObject(EDGE_SUBMOLECULE_ITER), _mol(mol), _enumerator(mol) { _enumerator.min_edges = min_edges; _enumerator.max_edges = max_edges; _enumerator.userdata = this; _enumerator.cb_subgraph = _handleSubgraph; _enumerator.process(); _idx = -1; } IndigoEdgeSubmoleculeIter::~IndigoEdgeSubmoleculeIter () { } void IndigoEdgeSubmoleculeIter::_handleSubgraph (Graph &graph, const int *v_mapping, const int *e_mapping, void *context) { IndigoEdgeSubmoleculeIter *self = (IndigoEdgeSubmoleculeIter *)context; Array &vertices = self->_vertices.push(); Array &edges = self->_edges.push(); Graph::filterVertices(graph, v_mapping, FILTER_NEQ, -1, vertices); Graph::filterEdges(graph, e_mapping, FILTER_NEQ, -1, edges); } bool IndigoEdgeSubmoleculeIter::hasNext () { return _idx + 1 < _vertices.size(); } IndigoObject * IndigoEdgeSubmoleculeIter::next () { if (!hasNext()) return 0; _idx++; AutoPtr res(new IndigoSubmolecule(_mol, _vertices[_idx], _edges[_idx])); res->idx = _idx; return res.release(); } CEXPORT int indigoIterateEdgeSubmolecules (int molecule, int min_bonds, int max_bonds) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoEdgeSubmoleculeIter(mol, min_bonds, max_bonds)); } INDIGO_END(-1) } CEXPORT int indigoCountHydrogens (int item, int *hydro) { INDIGO_BEGIN { if (hydro == 0) throw IndigoError("indigoCountHydrogens(): null pointer"); IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(obj); int res = ia.mol.getAtomTotalH(ia.idx); if (res == -1) return 0; *hydro = res; } else if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); *hydro = 0; for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.getAtomNumber(i) == ELEM_H) (*hydro)++; else if (!mol.isPseudoAtom(i) && !mol.isRSite(i)) (*hydro) += mol.getImplicitH(i); } } return 1; } INDIGO_END(-1) } CEXPORT int indigoCountImplicitHydrogens (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(obj); return ia.mol.asMolecule().getImplicitH(ia.idx); } else if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); int i, sum = 0; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) sum += mol.getImplicitH(i); return sum; } else throw IndigoError("indigoCountImplicitHydrogens: %s is not a molecule nor an atom", obj.debugInfo()); } INDIGO_END(-1) } // // IndigoAttachmentPointsIter // IndigoAttachmentPointsIter::IndigoAttachmentPointsIter (BaseMolecule &mol, int order) : IndigoObject(ATTACHMENT_POINTS_ITER), _mol(mol) { _order = order; _index = -1; } IndigoObject * IndigoAttachmentPointsIter::next () { if (!hasNext()) return 0; _index++; int atom_index = _mol.getAttachmentPoint(_order, _index); if (atom_index == -1) throw IndigoError("Internal error in IndigoAttachmentPointsIter::next"); return new IndigoAtom(_mol, atom_index); } bool IndigoAttachmentPointsIter::hasNext () { return _mol.getAttachmentPoint(_order, _index + 1) != -1; } CEXPORT int indigoIterateAttachmentPoints (int molecule, int order) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return self.addObject(new IndigoAttachmentPointsIter(mol, order)); } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_molecule.h000066400000000000000000000355011271037650300210540ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_molecule__ #define __indigo_molecule__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "indigo_internal.h" #include "molecule/query_molecule.h" #include "molecule/molecule.h" #include "graph/graph_subtree_enumerator.h" #include "graph/cycle_enumerator.h" #include "graph/edge_subgraph_enumerator.h" #include "molecule/molecule_neighbourhood_counters.h" #include "base_cpp/properties_map.h" class DLLEXPORT IndigoBaseMolecule : public IndigoObject { public: explicit IndigoBaseMolecule (int type_); virtual ~IndigoBaseMolecule (); virtual PropertiesMap& getProperties() { return _properties;} const char * debugInfo (); static bool is (IndigoObject &object); indigo::PropertiesMap _properties; }; class DLLEXPORT IndigoQueryMolecule : public IndigoBaseMolecule { public: IndigoQueryMolecule (); virtual ~IndigoQueryMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual QueryMolecule & getQueryMolecule (); virtual const char * getName (); static IndigoQueryMolecule * cloneFrom (IndigoObject & obj); static void parseAtomConstraint (const char* type, const char* value, AutoPtr&); static QueryMolecule::Atom* parseAtomSMARTS (const char *string); const char * debugInfo (); virtual IndigoObject * clone (); const MoleculeAtomNeighbourhoodCounters& getNeiCounters (); QueryMolecule qmol; private: MoleculeAtomNeighbourhoodCounters _nei_counters; int _nei_counters_edit_revision; }; class DLLEXPORT IndigoMolecule : public IndigoBaseMolecule { public: IndigoMolecule (); virtual ~IndigoMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual Molecule & getMolecule (); virtual const char * getName (); static IndigoMolecule * cloneFrom (IndigoObject & obj); const char * debugInfo (); virtual IndigoObject * clone (); Molecule mol; }; class DLLEXPORT IndigoAtom : public IndigoObject { public: IndigoAtom (BaseMolecule &mol_, int idx_); virtual ~IndigoAtom (); static bool is (IndigoObject &obj); static IndigoAtom & cast (IndigoObject &obj); virtual void remove (); virtual IndigoObject * clone (); BaseMolecule &mol; int idx; virtual int getIndex (); const char * debugInfo (); }; class DLLEXPORT IndigoRGroup : public IndigoObject { public: IndigoRGroup (); virtual ~IndigoRGroup (); virtual int getIndex (); static IndigoRGroup & cast (IndigoObject &obj); BaseMolecule *mol; int idx; }; class DLLEXPORT IndigoRGroupFragment : public IndigoObject { public: IndigoRGroupFragment (IndigoRGroup &rgp, int idx); IndigoRGroupFragment (BaseMolecule *mol, int rgroup_idx, int fragment_idx); virtual ~IndigoRGroupFragment (); virtual QueryMolecule & getQueryMolecule (); virtual Molecule & getMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual int getIndex (); virtual void remove (); virtual IndigoObject * clone (); IndigoRGroup rgroup; int frag_idx; }; class DLLEXPORT IndigoBond : public IndigoObject { public: IndigoBond (BaseMolecule &mol_, int idx_); virtual ~IndigoBond (); static bool is (IndigoObject &obj); static IndigoBond & cast (IndigoObject &obj); virtual void remove (); BaseMolecule &mol; int idx; virtual int getIndex (); const char * debugInfo (); }; class IndigoAtomNeighbor : public IndigoAtom { public: explicit IndigoAtomNeighbor (BaseMolecule &mol_, int atom_idx, int bond_idx); virtual ~IndigoAtomNeighbor (); int bond_idx; }; class IndigoAtomNeighborsIter : public IndigoObject { public: IndigoAtomNeighborsIter (BaseMolecule &molecule, int atom_idx); virtual ~IndigoAtomNeighborsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _atom_idx; int _nei_idx; BaseMolecule &_mol; }; class IndigoRGroupsIter : public IndigoObject { public: IndigoRGroupsIter (BaseMolecule *mol); virtual ~IndigoRGroupsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule *_mol; int _idx; }; class IndigoRGroupFragmentsIter : public IndigoObject { public: IndigoRGroupFragmentsIter (IndigoRGroup &rgroup); virtual ~IndigoRGroupFragmentsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule *_mol; int _rgroup_idx; int _frag_idx; }; class IndigoAtomsIter : public IndigoObject { public: enum { ALL, PSEUDO, RSITE, STEREOCENTER, ALLENE_CENTER }; IndigoAtomsIter (BaseMolecule *molecule, int type); virtual ~IndigoAtomsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _shift (int idx); int _type; int _idx; BaseMolecule *_mol; }; class IndigoBondsIter : public IndigoObject { public: IndigoBondsIter (BaseMolecule &molecule); virtual ~IndigoBondsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; }; class IndigoTGroup : public IndigoObject { public: IndigoTGroup (BaseMolecule &mol_, int idx_); virtual ~IndigoTGroup (); virtual int getIndex (); virtual void remove (); const char * debugInfo (); static IndigoTGroup & cast (IndigoObject &obj); TGroup & get(); BaseMolecule &mol; int idx; }; class IndigoTGroupsIter : public IndigoObject { public: IndigoTGroupsIter (BaseMolecule &molecule); virtual ~IndigoTGroupsIter (); const char * debugInfo (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; }; class IndigoSGroup : public IndigoObject { public: IndigoSGroup (BaseMolecule &mol_, int idx_); virtual ~IndigoSGroup (); virtual int getIndex (); virtual void remove (); const char * debugInfo (); static IndigoSGroup & cast (IndigoObject &obj); SGroup & get(); BaseMolecule &mol; int idx; }; class IndigoSGroupsIter : public IndigoObject { public: IndigoSGroupsIter (BaseMolecule &molecule, Array &sgs); virtual ~IndigoSGroupsIter (); const char * debugInfo (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoDataSGroup : public IndigoObject { public: IndigoDataSGroup (BaseMolecule &mol_, int idx_); virtual ~IndigoDataSGroup (); virtual int getIndex (); virtual void remove (); static IndigoDataSGroup & cast (IndigoObject &obj); DataSGroup & get(); BaseMolecule &mol; int idx; }; class IndigoDataSGroupsIter : public IndigoObject { public: IndigoDataSGroupsIter (BaseMolecule &molecule, Array &refs); virtual ~IndigoDataSGroupsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoSuperatom : public IndigoObject { public: IndigoSuperatom (BaseMolecule &mol_, int idx_); virtual ~IndigoSuperatom (); virtual int getIndex (); virtual void remove (); virtual const char * getName (); static IndigoSuperatom & cast (IndigoObject &obj); Superatom & get(); BaseMolecule &mol; int idx; }; class IndigoSuperatomsIter : public IndigoObject { public: IndigoSuperatomsIter (BaseMolecule &molecule, Array &refs); virtual ~IndigoSuperatomsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoRepeatingUnit : public IndigoObject { public: IndigoRepeatingUnit (BaseMolecule &mol_, int idx_); virtual ~IndigoRepeatingUnit (); virtual int getIndex (); virtual void remove (); static IndigoRepeatingUnit & cast (IndigoObject &obj); RepeatingUnit & get(); BaseMolecule &mol; int idx; }; class IndigoRepeatingUnitsIter : public IndigoObject { public: IndigoRepeatingUnitsIter (BaseMolecule &molecule, Array &refs); virtual ~IndigoRepeatingUnitsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoMultipleGroup : public IndigoObject { public: IndigoMultipleGroup (BaseMolecule &mol_, int idx_); virtual ~IndigoMultipleGroup (); virtual int getIndex (); virtual void remove (); static IndigoMultipleGroup & cast (IndigoObject &obj); MultipleGroup & get(); BaseMolecule &mol; int idx; }; class IndigoMultipleGroupsIter : public IndigoObject { public: IndigoMultipleGroupsIter (BaseMolecule &molecule, Array &refs); virtual ~IndigoMultipleGroupsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoGenericSGroup : public IndigoObject { public: IndigoGenericSGroup (BaseMolecule &mol_, int idx_); virtual ~IndigoGenericSGroup (); virtual int getIndex (); virtual void remove (); static IndigoGenericSGroup & cast (IndigoObject &obj); SGroup & get(); BaseMolecule &mol; int idx; }; class IndigoGenericSGroupsIter : public IndigoObject { public: IndigoGenericSGroupsIter (BaseMolecule &molecule, Array &refs); virtual ~IndigoGenericSGroupsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _idx; BaseMolecule &_mol; Array &_refs; }; class IndigoSGroupAtomsIter : public IndigoObject { public: IndigoSGroupAtomsIter (BaseMolecule &mol, SGroup &sgroup); virtual ~IndigoSGroupAtomsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule &_mol; SGroup &_sgroup; int _idx; }; class IndigoSGroupBondsIter : public IndigoObject { public: IndigoSGroupBondsIter (BaseMolecule &mol, SGroup &sgroup); virtual ~IndigoSGroupBondsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule &_mol; SGroup &_sgroup; int _idx; }; class IndigoMoleculeComponent : public IndigoObject { public: IndigoMoleculeComponent (BaseMolecule &mol_, int index_); virtual ~IndigoMoleculeComponent (); virtual int getIndex (); virtual IndigoObject * clone (); int index; BaseMolecule &mol; }; class IndigoComponentsIter : public IndigoObject { public: IndigoComponentsIter (BaseMolecule &mol_); virtual ~IndigoComponentsIter (); virtual IndigoObject * next (); virtual bool hasNext (); BaseMolecule &mol; protected: int _idx; }; class IndigoComponentAtomsIter : public IndigoObject { public: IndigoComponentAtomsIter (BaseMolecule &mol, int cidx); virtual ~IndigoComponentAtomsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _next (); BaseMolecule &_mol; int _cidx; int _idx; }; class IndigoComponentBondsIter : public IndigoObject { public: IndigoComponentBondsIter (BaseMolecule &mol, int cidx); virtual ~IndigoComponentBondsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: int _next (); BaseMolecule &_mol; int _cidx; int _idx; }; class IndigoSubmolecule : public IndigoObject { public: IndigoSubmolecule (BaseMolecule &mol_, Array &vertices_, Array &edges_); IndigoSubmolecule (BaseMolecule &mol_, List &vertices_, List &edges_); virtual ~IndigoSubmolecule (); virtual BaseMolecule & getBaseMolecule (); virtual int getIndex (); virtual IndigoObject * clone (); int idx; // not really a submolecule property, but included for convenience // of iterators that return submolecules Array vertices; Array edges; BaseMolecule &mol; protected: }; class IndigoSubmoleculeAtomsIter : public IndigoObject { public: IndigoSubmoleculeAtomsIter (IndigoSubmolecule &submol); virtual ~IndigoSubmoleculeAtomsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: IndigoSubmolecule &_submol; int _idx; }; class IndigoSubmoleculeBondsIter : public IndigoObject { public: IndigoSubmoleculeBondsIter (IndigoSubmolecule &submol); virtual ~IndigoSubmoleculeBondsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: IndigoSubmolecule &_submol; int _idx; }; class IndigoSSSRIter : public IndigoObject { public: IndigoSSSRIter (BaseMolecule &mol); virtual ~IndigoSSSRIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule &_mol; int _idx; }; class IndigoSubtreesIter : public IndigoObject { public: IndigoSubtreesIter (BaseMolecule &mol, int min_vertices, int max_vertices); virtual ~IndigoSubtreesIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: static void _handleTree (Graph &graph, const Array &vertices, const Array &edges, void *context); BaseMolecule &_mol; GraphSubtreeEnumerator _enumerator; int _idx; ObjArray< Array > _vertices; ObjArray< Array > _edges; }; class IndigoRingsIter : public IndigoObject { public: IndigoRingsIter (BaseMolecule &mol, int min_vertices, int max_vertices); virtual ~IndigoRingsIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: static bool _handleCycle (Graph &graph, const Array &vertices, const Array &edges, void *context); BaseMolecule &_mol; CycleEnumerator _enumerator; int _idx; ObjArray< Array > _vertices; ObjArray< Array > _edges; }; class IndigoEdgeSubmoleculeIter : public IndigoObject { public: IndigoEdgeSubmoleculeIter (BaseMolecule &mol, int min_edges, int max_edges); virtual ~IndigoEdgeSubmoleculeIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: static void _handleSubgraph (Graph &graph, const int *v_mapping, const int *e_mapping, void *context); BaseMolecule &_mol; EdgeSubgraphEnumerator _enumerator; int _idx; ObjArray< Array > _vertices; ObjArray< Array > _edges; }; class IndigoAttachmentPointsIter : public IndigoObject { public: IndigoAttachmentPointsIter (BaseMolecule &mol, int order); virtual IndigoObject * next (); virtual bool hasNext (); protected: BaseMolecule &_mol; int _order, _index; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_object.cpp000066400000000000000000000073231271037650300210510ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "indigo_array.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "molecule/sdf_loader.h" #include "molecule/rdf_loader.h" #include "reaction/reaction.h" #include "base_cpp/properties_map.h" IndigoObject::IndigoObject (int type_) { type = type_; } IndigoObject::~IndigoObject () { } const char * IndigoObject::debugInfo () { if (_dbg_info.get() != 0) return _dbg_info->ptr(); _dbg_info.create(); ArrayOutput out(_dbg_info.ref()); out.printf("", type); out.writeChar(0); return _dbg_info->ptr(); } void IndigoObject::toString (Array &str) { throw IndigoError("can not convert %s to string", debugInfo()); } void IndigoObject::toBuffer (Array &buf) { return toString(buf); } Molecule & IndigoObject::getMolecule () { throw IndigoError("%s is not a molecule", debugInfo()); } BaseMolecule & IndigoObject::getBaseMolecule () { throw IndigoError("%s is not a base molecule", debugInfo()); } QueryMolecule & IndigoObject::getQueryMolecule () { throw IndigoError("%s is not a query molecule", debugInfo()); } //RedBlackStringObjMap< Array > * IndigoObject::getProperties () //{ // throw IndigoError("%s can not have properties", debugInfo()); //} //void IndigoObject::copyProperties (RedBlackStringObjMap< Array > &other) //{ // RedBlackStringObjMap< Array > *props = getProperties(); // // if (props == 0) // throw IndigoError("copyProperties(): zero destination"); // // int i; // // props->clear(); // // for (i = other.begin(); i != other.end(); i = other.next(i)) // props->value(props->insert(other.key(i))).copy(other.value(i)); //} void IndigoObject::copyProperties (RedBlackStringObjMap< Array > &other) { auto& props = getProperties(); props.copy(other); } void IndigoObject::copyProperties (PropertiesMap& other) { auto& props = getProperties(); props.copy(other); } PropertiesMap& IndigoObject::getProperties() { throw IndigoError("%s can not have properties", debugInfo()); } Reaction & IndigoObject::getReaction () { throw IndigoError("%s is not a reaction", debugInfo()); } BaseReaction & IndigoObject::getBaseReaction () { throw IndigoError("%s is not a base reaction", debugInfo()); } QueryReaction & IndigoObject::getQueryReaction () { throw IndigoError("%s is not a query reaction", debugInfo()); } IndigoObject * IndigoObject::next () { throw IndigoError("%s is not iterable", debugInfo()); } bool IndigoObject::hasNext () { throw IndigoError("%s is not iterable", debugInfo()); } void IndigoObject::remove () { throw IndigoError("%s is not removeable", debugInfo()); } const char * IndigoObject::getName () { throw IndigoError("%s does not have a name", debugInfo()); } int IndigoObject::getIndex () { throw IndigoError("%s does not have an index", debugInfo()); } IndigoObject * IndigoObject::clone () { throw IndigoError("%s is not cloneable", debugInfo()); } Indigo-indigo-1.2.3/api/src/indigo_options.cpp000066400000000000000000000472421271037650300213020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "molecule/molfile_saver.h" static void indigoIgnoreStereochemistryErrors (int enabled) { Indigo &self = indigoGetInstance(); self.stereochemistry_options.ignore_errors = (enabled != 0); } static void indigoIgnoreNoncricicalQueryFeatures (int enabled) { Indigo &self = indigoGetInstance(); self.ignore_noncritical_query_features = (enabled != 0); } static void indigoTreatXAsPseudoatom (int enabled) { Indigo &self = indigoGetInstance(); self.treat_x_as_pseudoatom = (enabled != 0); } static void indigoSkip3dChirality (int enabled) { Indigo &self = indigoGetInstance(); self.skip_3d_chirality = (enabled != 0); } static void indigoDeconvolutionAromatization (int enabled) { Indigo &self = indigoGetInstance(); self.deconvolution_aromatization = (enabled != 0); } static void indigoDecoSaveAPBondOrders (int enabled) { Indigo &self = indigoGetInstance(); self.deco_save_ap_bond_orders = (enabled != 0); } static void indigoDecoIgnoreErrors (int enabled) { Indigo &self = indigoGetInstance(); self.deco_ignore_errors = (enabled != 0); } static void indigoSetMolfileSavingMode (const char *mode) { Indigo &self = indigoGetInstance(); if (strcasecmp(mode, "2000") == 0) self.molfile_saving_mode = MolfileSaver::MODE_2000; else if (strcasecmp(mode, "3000") == 0) self.molfile_saving_mode = MolfileSaver::MODE_3000; else if (strcasecmp(mode, "auto") == 0) self.molfile_saving_mode = MolfileSaver::MODE_AUTO; else throw IndigoError("unknown value: %s", mode); } static void indigoSetMolfileSavingNoChiral (int enabled) { Indigo &self = indigoGetInstance(); self.molfile_saving_no_chiral = (enabled != 0); } static void indigoSetMolfileSavingSkipDate (int enabled) { Indigo &self = indigoGetInstance(); self.molfile_saving_skip_date = (enabled != 0); } static void indigoSetMolfileSavingAddStereoDesc (int enabled) { Indigo &self = indigoGetInstance(); self.molfile_saving_add_stereo_desc = (enabled != 0); } static void indigoSetSmilesSavingWriteName (int enabled) { Indigo &self = indigoGetInstance(); self.smiles_saving_write_name = (enabled != 0); } static void indigoSetFilenameEncoding (const char *encoding) { Indigo &self = indigoGetInstance(); if (strcasecmp(encoding, "ASCII") == 0) self.filename_encoding = ENCODING_ASCII; else if (strcasecmp(encoding, "UTF-8") == 0) self.filename_encoding = ENCODING_UTF8; else throw IndigoError("unknown value: %s", encoding); } static void indigoSetFPOrdQwords (int qwords) { Indigo &self = indigoGetInstance(); self.fp_params.ord_qwords = qwords; } static void indigoSetFPSimQwords (int qwords) { Indigo &self = indigoGetInstance(); self.fp_params.sim_qwords = qwords; } static void indigoSetFPTauQwords (int qwords) { Indigo &self = indigoGetInstance(); self.fp_params.tau_qwords = qwords; } static void indigoSetFPAnyQwords (int qwords) { Indigo &self = indigoGetInstance(); self.fp_params.any_qwords = qwords; } static void indigoSetFPExt(int enabled) { Indigo &self = indigoGetInstance(); self.fp_params.ext = (enabled != 0); } static void indigoSetSmartLayout(int enabled) { Indigo &self = indigoGetInstance(); self.smart_layout = (enabled != 0); } static void indigoSetEmbeddingUniqueness(const char *mode) { Indigo &self = indigoGetInstance(); if (strcasecmp(mode, "atoms") == 0) { self.embedding_edges_uniqueness = false; self.find_unique_embeddings = true; } else if (strcasecmp(mode, "bonds") == 0) { self.embedding_edges_uniqueness = true; self.find_unique_embeddings = true; } else if (strcasecmp(mode, "none") == 0) { self.find_unique_embeddings = false; } else throw IndigoError("unknown value: %s", mode); } static void indigoSetMaxEmbeddings (int value) { Indigo &self = indigoGetInstance(); if (value <= 0) throw IndigoError("Maximum allowed embeddings limit must be positive."); self.max_embeddings = value; } static void indigoSetLayoutMaxIterations (int value) { Indigo &self = indigoGetInstance(); self.layout_max_iterations = value; } static void indigoAAMSetCancellationTimeout (int value) { Indigo &self = indigoGetInstance(); self.aam_cancellation_timeout = value; } static void indigoSetCancellationTimeout (int value) { Indigo &self = indigoGetInstance(); self.cancellation_timeout = value; } static void indigoSetPreserveOrderingInSerialize (int enabled) { Indigo &self = indigoGetInstance(); self.preserve_ordering_in_serialize = (enabled != 0); } static void indigoSetAromaticityModel (const char *model) { Indigo &self = indigoGetInstance(); if (strcasecmp(model, "basic") == 0) self.arom_options.method = AromaticityOptions::BASIC; else if (strcasecmp(model, "generic") == 0) self.arom_options.method = AromaticityOptions::GENERIC; else throw IndigoError("unknown value: %s. Allowed values are \"basic\", \"generic\"", model); } static void indigoSetDearomatizeVerification (int enabled) { Indigo &self = indigoGetInstance(); self.arom_options.dearomatize_check = (enabled != 0); } static void indigoSetDearomatizeUnique (int enabled) { Indigo &self = indigoGetInstance(); self.unique_dearomatization = (enabled != 0); } static void indigoSetBidirectionalMode (int enabled) { Indigo &self = indigoGetInstance(); self.stereochemistry_options.bidirectional_mode = (enabled != 0); } static void indigoSetDetectHaworthProjection (int enabled) { Indigo &self = indigoGetInstance(); self.stereochemistry_options.detect_haworth_projection = (enabled != 0); } static void indigoSetStandardizeStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.standardize_stereo = (enabled != 0); } static void indigoSetStandardizeCharges (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.standardize_charges = (enabled != 0); } static void indigoSetStandardizeCenterMolecule (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.center_molecule = (enabled != 0); } static void indigoSetStandardizeRemoveSingleAtoms (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.remove_single_atom_fragments = (enabled != 0); } static void indigoSetStandardizeKeepSmallestFragment (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.keep_smallest_fragment = (enabled != 0); } static void indigoSetStandardizeKeepLargestFragment (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.keep_largest_fragment = (enabled != 0); } static void indigoSetStandardizeRemoveLargestFragment (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.remove_largest_fragment = (enabled != 0); } static void indigoSetStandardizeMakeNonHtoCAtoms (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.make_non_h_atoms_c_atoms = (enabled != 0); } static void indigoSetStandardizeMakeNonHtoAAtoms (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.make_non_h_atoms_a_atoms = (enabled != 0); } static void indigoSetStandardizeMakeNonHCtoQAtoms (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.make_non_c_h_atoms_q_atoms = (enabled != 0); } static void indigoSetStandardizeMakeAllBondsSingle (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.make_all_bonds_single = (enabled != 0); } static void indigoSetStandardizeClearCoordinates (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_coordinates = (enabled != 0); } static void indigoSetStandardizeStraightenTripleBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.straighten_triple_bonds = (enabled != 0); } static void indigoSetStandardizeStraightenAllens (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.straighten_allenes = (enabled != 0); } static void indigoSetStandardizeClearMolecule (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_molecule = (enabled != 0); } static void indigoSetStandardizeClearStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_stereo = (enabled != 0); } static void indigoSetStandardizeClearEnhancedStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_enhanced_stereo = (enabled != 0); } static void indigoSetStandardizeClearUnknownStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_unknown_stereo = (enabled != 0); } static void indigoSetStandardizeClearUnknownAtomStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_unknown_atom_stereo = (enabled != 0); } static void indigoSetStandardizeClearUnknownBondStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_unknown_cis_trans_bond_stereo = (enabled != 0); } static void indigoSetStandardizeClearCisTransStereo (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_cis_trans_bond_stereo = (enabled != 0); } static void indigoSetStandardizeStereoFromCoordinates (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.set_stereo_from_coordinates = (enabled != 0); } static void indigoSetStandardizeRepositonStereoBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.reposition_stereo_bonds = (enabled != 0); } static void indigoSetStandardizeRepositonAxialStereoBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.reposition_axial_stereo_bonds = (enabled != 0); } static void indigoSetStandardizeFixDirectionWedgeBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.fix_direction_of_wedge_bonds = (enabled != 0); } static void indigoSetStandardizeClearCharges (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_charges = (enabled != 0); } static void indigoSetStandardizeClearHighlightColors (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_highlight_colors = (enabled != 0); } static void indigoSetStandardizeNeutralizeZwitterions (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.neutralize_bonded_zwitterions = (enabled != 0); } static void indigoSetStandardizeClearUnususalValences (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_unusual_valence = (enabled != 0); } static void indigoSetStandardizeClearIsotopes (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_isotopes = (enabled != 0); } static void indigoSetStandardizeClearDativeBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_dative_bonds = (enabled != 0); } static void indigoSetStandardizeClearHydrogenBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.clear_hydrogen_bonds = (enabled != 0); } static void indigoSetStandardizeLocalizeMarkushRAtomsOnRings (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.localize_markush_r_atoms_on_rings = (enabled != 0); } static void indigoSetStandardizeCreateDativeBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.create_coordination_bonds = (enabled != 0); } static void indigoSetStandardizeCreateHydrogenBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.create_hydrogen_bonds = (enabled != 0); } static void indigoSetStandardizeRemoveExtraStereoBonds (int enabled) { Indigo &self = indigoGetInstance(); self.standardize_options.remove_extra_stereo_bonds = (enabled != 0); } static void indigoSetPkaModel (const char *model) { Indigo &self = indigoGetInstance(); if (strcasecmp(model, "simple") == 0) self.ionize_options.model = IonizeOptions::PKA_MODEL_SIMPLE; else if (strcasecmp(model, "advanced") == 0) self.ionize_options.model = IonizeOptions::PKA_MODEL_ADVANCED; else throw IndigoError("unknown value: %s. Allowed values are \"simple\", \"advanced\"", model); } static void indigoSetPkaModelLevel (int value) { Indigo &self = indigoGetInstance(); self.ionize_options.level = value; } static void indigoSetPkaModelMinLevel (int value) { Indigo &self = indigoGetInstance(); self.ionize_options.min_level = value; } _IndigoBasicOptionsHandlersSetter::_IndigoBasicOptionsHandlersSetter () { OptionManager &mgr = indigoGetOptionManager(); OsLocker locker(mgr.lock); mgr.setOptionHandlerBool("ignore-stereochemistry-errors", indigoIgnoreStereochemistryErrors); mgr.setOptionHandlerBool("ignore-noncritical-query-features", indigoIgnoreNoncricicalQueryFeatures); mgr.setOptionHandlerBool("treat-x-as-pseudoatom", indigoTreatXAsPseudoatom); mgr.setOptionHandlerBool("skip-3d-chirality", indigoSkip3dChirality); mgr.setOptionHandlerBool("deconvolution-aromatization", indigoDeconvolutionAromatization); mgr.setOptionHandlerBool("deco-save-ap-bond-orders", indigoDecoSaveAPBondOrders); mgr.setOptionHandlerBool("deco-ignore-errors", indigoDecoIgnoreErrors); mgr.setOptionHandlerString("molfile-saving-mode", indigoSetMolfileSavingMode); mgr.setOptionHandlerBool("molfile-saving-no-chiral", indigoSetMolfileSavingNoChiral); mgr.setOptionHandlerBool("molfile-saving-skip-date", indigoSetMolfileSavingSkipDate); mgr.setOptionHandlerBool("molfile-saving-add-stereo-desc", indigoSetMolfileSavingAddStereoDesc); mgr.setOptionHandlerBool("smiles-saving-write-name", indigoSetSmilesSavingWriteName); mgr.setOptionHandlerString("filename-encoding", indigoSetFilenameEncoding); mgr.setOptionHandlerInt("fp-ord-qwords", indigoSetFPOrdQwords); mgr.setOptionHandlerInt("fp-sim-qwords", indigoSetFPSimQwords); mgr.setOptionHandlerInt("fp-any-qwords", indigoSetFPAnyQwords); mgr.setOptionHandlerInt("fp-tau-qwords", indigoSetFPTauQwords); mgr.setOptionHandlerBool("fp-ext-enabled", indigoSetFPExt); mgr.setOptionHandlerBool("smart-layout", indigoSetSmartLayout); mgr.setOptionHandlerString("embedding-uniqueness", indigoSetEmbeddingUniqueness); mgr.setOptionHandlerInt("max-embeddings", indigoSetMaxEmbeddings); mgr.setOptionHandlerInt("layout-max-iterations", indigoSetLayoutMaxIterations); mgr.setOptionHandlerInt("aam-timeout", indigoAAMSetCancellationTimeout); mgr.setOptionHandlerInt("timeout", indigoSetCancellationTimeout); mgr.setOptionHandlerBool("serialize-preserve-ordering", indigoSetPreserveOrderingInSerialize); mgr.setOptionHandlerString("aromaticity-model", indigoSetAromaticityModel); mgr.setOptionHandlerBool("dearomatize-verification", indigoSetDearomatizeVerification); mgr.setOptionHandlerBool("unique-dearomatization", indigoSetDearomatizeUnique); mgr.setOptionHandlerBool("stereochemistry-bidirectional-mode", indigoSetBidirectionalMode); mgr.setOptionHandlerBool("stereochemistry-detect-haworth-projection", indigoSetDetectHaworthProjection); mgr.setOptionHandlerBool("standardize-stereo", indigoSetStandardizeStereo); mgr.setOptionHandlerBool("standardize-charges", indigoSetStandardizeCharges); mgr.setOptionHandlerBool("standardize-center-molecule", indigoSetStandardizeCenterMolecule); mgr.setOptionHandlerBool("standardize-remove-single-atoms", indigoSetStandardizeRemoveSingleAtoms); mgr.setOptionHandlerBool("standardize-keep-smallest", indigoSetStandardizeKeepSmallestFragment); mgr.setOptionHandlerBool("standardize-keep-largest", indigoSetStandardizeKeepLargestFragment); mgr.setOptionHandlerBool("standardize-remove-largest", indigoSetStandardizeRemoveLargestFragment); mgr.setOptionHandlerBool("standardize-make-non-h-to-c-atoms", indigoSetStandardizeMakeNonHtoCAtoms); mgr.setOptionHandlerBool("standardize-make-non-h-to-a-atoms", indigoSetStandardizeMakeNonHtoAAtoms); mgr.setOptionHandlerBool("standardize-make-non-h-c-to-q-atoms", indigoSetStandardizeMakeNonHCtoQAtoms); mgr.setOptionHandlerBool("standardize-make-all-bonds-single", indigoSetStandardizeMakeAllBondsSingle); mgr.setOptionHandlerBool("standardize-clear-coordinates", indigoSetStandardizeClearCoordinates); mgr.setOptionHandlerBool("standardize-straighten-triple-bonds", indigoSetStandardizeStraightenTripleBonds); mgr.setOptionHandlerBool("standardize-straighten-allens", indigoSetStandardizeStraightenAllens); mgr.setOptionHandlerBool("standardize-clear-molecule", indigoSetStandardizeClearMolecule); mgr.setOptionHandlerBool("standardize-clear-stereo", indigoSetStandardizeClearStereo); mgr.setOptionHandlerBool("standardize-clear-enhanced-stereo", indigoSetStandardizeClearEnhancedStereo); mgr.setOptionHandlerBool("standardize-clear-unknown-stereo", indigoSetStandardizeClearUnknownStereo); mgr.setOptionHandlerBool("standardize-clear-unknown-atom-stereo", indigoSetStandardizeClearUnknownAtomStereo); mgr.setOptionHandlerBool("standardize-clear-unknown-bond-stereo", indigoSetStandardizeClearUnknownBondStereo); mgr.setOptionHandlerBool("standardize-clear-cis-trans", indigoSetStandardizeClearCisTransStereo); mgr.setOptionHandlerBool("standardize-stereo-from-coordinates", indigoSetStandardizeStereoFromCoordinates); mgr.setOptionHandlerBool("standardize-reposition-stereo-bonds", indigoSetStandardizeRepositonStereoBonds); mgr.setOptionHandlerBool("standardize-reposition-axial-stereo-bonds", indigoSetStandardizeRepositonAxialStereoBonds); mgr.setOptionHandlerBool("standardize-fix-direction-wedge-bonds", indigoSetStandardizeFixDirectionWedgeBonds); mgr.setOptionHandlerBool("standardize-clear-charges", indigoSetStandardizeClearCharges); mgr.setOptionHandlerBool("standardize-highlight-colors", indigoSetStandardizeClearHighlightColors); mgr.setOptionHandlerBool("standardize-neutralize-zwitterions", indigoSetStandardizeNeutralizeZwitterions); mgr.setOptionHandlerBool("standardize-clear-unusual-valences", indigoSetStandardizeClearUnususalValences); mgr.setOptionHandlerBool("standardize-clear-isotopes", indigoSetStandardizeClearIsotopes); mgr.setOptionHandlerBool("standardize-clear-dative-bonds", indigoSetStandardizeClearDativeBonds); mgr.setOptionHandlerBool("standardize-clear-hydrogen-bonds", indigoSetStandardizeClearHydrogenBonds); mgr.setOptionHandlerBool("standardize-localize-markush-r-atoms-on-rings", indigoSetStandardizeLocalizeMarkushRAtomsOnRings); mgr.setOptionHandlerBool("standardize-create-dative-bonds", indigoSetStandardizeCreateDativeBonds); mgr.setOptionHandlerBool("standardize-create-hydrogen-bonds", indigoSetStandardizeCreateHydrogenBonds); mgr.setOptionHandlerBool("standardize-remove-extra-stereo-bonds", indigoSetStandardizeRemoveExtraStereoBonds); mgr.setOptionHandlerString("pKa-model", indigoSetPkaModel); mgr.setOptionHandlerInt("pKa-model-level", indigoSetPkaModelLevel); mgr.setOptionHandlerInt("pKa-model-min-level", indigoSetPkaModelMinLevel); } _IndigoBasicOptionsHandlersSetter::~_IndigoBasicOptionsHandlersSetter () { } Indigo-indigo-1.2.3/api/src/indigo_product_enumerator.cpp000066400000000000000000000223161271037650300235230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_internal.h" #include "indigo_reaction.h" #include "indigo_array.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/cancellation_handler.h" #include "layout/molecule_layout.h" #include "layout/reaction_layout.h" #include "molecule/molecule.h" #include "molecule/molfile_loader.h" #include "molecule/sdf_loader.h" #include "molecule/molfile_saver.h" #include "molecule/molecule_auto_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/rxnfile_saver.h" #include "reaction/reaction_auto_loader.h" #include "reaction/reaction_product_enumerator.h" #include "reaction/reaction_transformation.h" #include "base_cpp/properties_map.h" #include "indigo_mapping.h" struct ProductEnumeratorCallbackData { ReactionProductEnumerator *rpe; ObjArray *out_reactions; }; static void product_proc( Molecule &product, Array &monomers_indices, Array &mapping, void *userdata ) { ProductEnumeratorCallbackData *rpe_data = (ProductEnumeratorCallbackData *)userdata; Reaction &reaction = rpe_data->out_reactions->push(); QS_DEF(Molecule, new_product); new_product.clear(); new_product.clone(product, NULL, NULL); reaction.clear(); for (int i = 0; i < monomers_indices.size(); i++) reaction.addReactantCopy(rpe_data->rpe->getMonomer(monomers_indices[i]), NULL, NULL); reaction.addProductCopy(new_product, NULL, NULL); reaction.name.copy(product.name); } CEXPORT int indigoReactionProductEnumerate (int reaction, int monomers) { INDIGO_BEGIN { bool has_coord = false; QueryReaction &query_rxn = self.getObject(reaction).getQueryReaction(); IndigoArray &monomers_object = IndigoArray::cast(self.getObject(monomers)); ReactionProductEnumerator rpe(query_rxn); rpe.arom_options = self.arom_options; ObjArray out_reactions; if (monomers_object.objects.size() < query_rxn.reactantsCount()) throw IndigoError("Too small monomers array"); int user_reactant_idx = 0; for (int i = query_rxn.reactantBegin(); i != query_rxn.reactantEnd(); i = query_rxn.reactantNext(i)) { IndigoArray &reactant_monomers_object = IndigoArray::cast(*monomers_object.objects[i]); for (int j = 0; j < reactant_monomers_object.objects.size(); j++) { Molecule &monomer = reactant_monomers_object.objects[j]->getMolecule(); if (monomer.have_xyz) has_coord = true; rpe.addMonomer(i, monomer); } user_reactant_idx++; } rpe.is_multistep_reaction = self.rpe_params.is_multistep_reactions; rpe.is_one_tube = self.rpe_params.is_one_tube; rpe.is_self_react = self.rpe_params.is_self_react; rpe.max_deep_level = self.rpe_params.max_deep_level; rpe.max_product_count = self.rpe_params.max_product_count; rpe.product_proc = product_proc; ProductEnumeratorCallbackData rpe_data; rpe_data.out_reactions = &out_reactions; rpe_data.rpe = &rpe; rpe.userdata = &rpe_data; rpe.buildProducts(); int out_array = indigoCreateArray(); for (int i = 0; i < out_reactions.size(); i++) { if (has_coord && self.rpe_params.is_layout) { ReactionLayout layout(out_reactions[i], self.smart_layout); layout.make(); out_reactions[i].markStereocenterBonds(); } QS_DEF(IndigoReaction, indigo_rxn); indigo_rxn.rxn.clone(out_reactions[i], NULL, NULL, NULL); indigoArrayAdd(out_array, self.addObject(indigo_rxn.clone())); } return out_array; } INDIGO_END(-1) } CEXPORT int indigoTransform (int reaction, int monomers) { INDIGO_BEGIN { IndigoObject &monomers_object = self.getObject(monomers); QueryReaction &query_rxn = self.getObject(reaction).getQueryReaction(); ReactionTransformation rt; rt.arom_options = self.arom_options; rt.layout_flag = self.rpe_params.transform_is_layout; rt.smart_layout = self.smart_layout; // Try to work with molecule first bool is_mol = false; try { monomers_object.getMolecule(); is_mol = true; } catch (IndigoError) { } TimeoutCancellationHandler cancellation(self.cancellation_timeout); rt.cancellation = &cancellation; bool transformed_flag = false; IndigoObject *out_mapping = 0; if (is_mol) { Array mapping; Molecule &mol = monomers_object.getMolecule(); Molecule input_mol; input_mol.clone(mol, 0, 0); transformed_flag = rt.transform(mol, query_rxn, &mapping); AutoPtr mptr(new IndigoMapping(input_mol, mol)); mptr.get()->mapping.copy(mapping); out_mapping = mptr.release(); } else if (monomers_object.type == IndigoObject::ARRAY) { IndigoArray &monomers_array = IndigoArray::cast(self.getObject(monomers)); AutoPtr out_array(new IndigoArray()); for (int i = 0; i < monomers_array.objects.size(); i++) { Array mapping; Molecule &mol = monomers_object.getMolecule(); Molecule input_mol; input_mol.clone(mol, 0, 0); if (rt.transform(monomers_array.objects[i]->getMolecule(), query_rxn, &mapping)) transformed_flag = true; AutoPtr mptr(new IndigoMapping(input_mol, mol)); mptr.get()->mapping.copy(mapping); out_array.get()->objects.add(mptr.release()); } out_mapping = out_array.release(); } else throw IndigoError("%s is not a molecule or array of molecules", self.getObject(monomers).debugInfo()); if (transformed_flag) return self.addObject(out_mapping); else { return 0; } } INDIGO_END(-1) } void indigoProductEnumeratorSetMultistepReactionFlag (int is_multistep_reactions) { Indigo &self = indigoGetInstance(); self.rpe_params.is_multistep_reactions = (is_multistep_reactions != 0); } void indigoProductEnumeratorSetOneTubeMode (const char *mode_string) { Indigo &self = indigoGetInstance(); if (strcmp(mode_string, "one-tube") == 0) self.rpe_params.is_one_tube = true; else if (strcmp(mode_string, "grid") == 0) self.rpe_params.is_one_tube = false; else throw IndigoError("%s is bad reaction product enumerator mode string", mode_string); } void indigoProductEnumeratorSetSelfReactionFlag (int is_self_react) { Indigo &self = indigoGetInstance(); self.rpe_params.is_self_react = (is_self_react != 0); } void indigoProductEnumeratorSetMaximumSearchDepth (int max_depth) { Indigo &self = indigoGetInstance(); self.rpe_params.max_deep_level = max_depth; } void indigoProductEnumeratorSetMaximumProductsCount (int max_pr_cnt) { Indigo &self = indigoGetInstance(); self.rpe_params.max_product_count = max_pr_cnt; } void indigoProductEnumeratorSetLayoutFlag (int layout_flag) { Indigo &self = indigoGetInstance(); self.rpe_params.is_layout = (layout_flag != 0); } void indigoProductEnumeratorSetTransformLayoutFlag (int transform_layout_flag) { Indigo &self = indigoGetInstance(); self.rpe_params.transform_is_layout = (transform_layout_flag != 0); } class _IndigoRPEOptionsHandlersSetter { public: _IndigoRPEOptionsHandlersSetter (); }; _IndigoRPEOptionsHandlersSetter::_IndigoRPEOptionsHandlersSetter () { OptionManager &mgr = indigoGetOptionManager(); OsLocker locker(mgr.lock); mgr.setOptionHandlerBool("rpe-multistep-reactions", indigoProductEnumeratorSetMultistepReactionFlag); mgr.setOptionHandlerString("rpe-mode", indigoProductEnumeratorSetOneTubeMode); mgr.setOptionHandlerBool("rpe-self-reaction", indigoProductEnumeratorSetSelfReactionFlag); mgr.setOptionHandlerInt("rpe-max-depth", indigoProductEnumeratorSetMaximumSearchDepth); mgr.setOptionHandlerInt("rpe-max-products-count", indigoProductEnumeratorSetMaximumProductsCount); mgr.setOptionHandlerBool("rpe-layout", indigoProductEnumeratorSetLayoutFlag); mgr.setOptionHandlerBool("transform-layout", indigoProductEnumeratorSetTransformLayoutFlag); } _IndigoRPEOptionsHandlersSetter _indigo_rpe_options_handlers_setter; Indigo-indigo-1.2.3/api/src/indigo_properties.cpp000066400000000000000000000074201271037650300217750ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_properties.h" #include "base_cpp/properties_map.h" CEXPORT int indigoHasProperty (int handle, const char *prop) { INDIGO_BEGIN { if (prop == 0 || *prop == 0) throw IndigoError("indigoHasProperty(): null or empty property given"); IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); return props.contains(prop); } INDIGO_END(-1) } CEXPORT const char * indigoGetProperty (int handle, const char *prop) { INDIGO_BEGIN { if (prop == 0 || *prop == 0) throw IndigoError("indigoGetProperty(): null or empty property given"); IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); auto &tmp = self.getThreadTmpData(); tmp.string.readString(props.at(prop), true); return tmp.string.ptr(); } INDIGO_END(0) } CEXPORT int indigoSetProperty (int handle, const char *prop, const char *value) { INDIGO_BEGIN { if (prop == 0 || *prop == 0) throw IndigoError("indigoSetProperty(): null or empty property given"); if (value == 0) value = ""; IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); props.insert(prop, value); return 1; } INDIGO_END(-1) } CEXPORT int indigoRemoveProperty (int handle, const char *prop) { INDIGO_BEGIN { if (prop == 0 || *prop == 0) throw IndigoError("indigoRemoveProperty(): null or empty property given"); IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); props.remove(prop); return 1; } INDIGO_END(-1) } IndigoPropertiesIter::IndigoPropertiesIter (indigo::PropertiesMap &props) : IndigoObject(PROPERTIES_ITER), _props(props) { _idx = -1; } IndigoPropertiesIter::~IndigoPropertiesIter () { } IndigoProperty::IndigoProperty (indigo::PropertiesMap &props, int idx) : IndigoObject(PROPERTY), _props(props), _idx(idx) { } IndigoProperty::~IndigoProperty () { } const char * IndigoProperty::getName () { return _props.key(_idx); } const char* IndigoProperty::getValue () { return _props.value(_idx); } int IndigoProperty::getIndex () { return _idx; } bool IndigoPropertiesIter::hasNext () { if (_idx == -1) return *_props.elements().begin() != *_props.elements().end(); return _props.elements().next(_idx) != *_props.elements().end(); } IndigoObject * IndigoPropertiesIter::next () { if (_idx == -1) _idx = *_props.elements().begin(); else if (_idx != *_props.elements().end()) _idx = _props.elements().next(_idx); if (_idx == *_props.elements().end()) return 0; return new IndigoProperty(_props, _idx); } CEXPORT int indigoIterateProperties (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); return self.addObject(new IndigoPropertiesIter(props)); } INDIGO_END(-1) } CEXPORT int indigoClearProperties (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); auto& props = obj.getProperties(); props.clear(); return 0; } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_properties.h000066400000000000000000000025271271037650300214450ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_properties__ #define __indigo_properties__ #include "indigo_internal.h" namespace indigo { class PropertiesMap; } class DLLEXPORT IndigoProperty : public IndigoObject { public: IndigoProperty (indigo::PropertiesMap &props, int idx); virtual ~IndigoProperty (); virtual const char * getName (); virtual int getIndex (); const char* getValue (); protected: indigo::PropertiesMap &_props; int _idx; }; class IndigoPropertiesIter : public IndigoObject { public: IndigoPropertiesIter (indigo::PropertiesMap &props); virtual ~IndigoPropertiesIter (); virtual IndigoObject * next (); virtual bool hasNext (); protected: indigo::PropertiesMap &_props; int _idx; }; #endif Indigo-indigo-1.2.3/api/src/indigo_reaction.cpp000066400000000000000000000403131271037650300214030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_io.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "indigo_mapping.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rxnfile_saver.h" #include "base_cpp/output.h" #include "reaction/reaction_automapper.h" #include "base_cpp/auto_ptr.h" #include "indigo_array.h" #include "reaction/rsmiles_loader.h" #include "reaction/canonical_rsmiles_saver.h" // // IndigoBaseReaction // IndigoBaseReaction::IndigoBaseReaction (int type_) : IndigoObject(type_) { } IndigoBaseReaction::~IndigoBaseReaction () { } bool IndigoBaseReaction::is (IndigoObject &obj) { int type = obj.type; if (type == REACTION || type == QUERY_REACTION || type == RDF_REACTION || type == SMILES_REACTION || type == CML_REACTION) return true; if (type == ARRAY_ELEMENT) return is(((IndigoArrayElement &)obj).get()); return false; } const char * IndigoBaseReaction::debugInfo () { return ""; } // // IndigoBaseReaction // IndigoReaction::IndigoReaction () : IndigoBaseReaction(REACTION) { } const char * IndigoReaction::debugInfo () { return ""; } IndigoReaction::~IndigoReaction () { } Reaction & IndigoReaction::getReaction () { return rxn; } BaseReaction & IndigoReaction::getBaseReaction () { return rxn; } const char * IndigoReaction::getName () { if (rxn.name.ptr() == 0) return ""; return rxn.name.ptr(); } // // IndigoQueryReaction // IndigoQueryReaction::IndigoQueryReaction () : IndigoBaseReaction(QUERY_REACTION) { } const char * IndigoQueryReaction::debugInfo () { return ""; } IndigoQueryReaction::~IndigoQueryReaction () { } BaseReaction & IndigoQueryReaction::getBaseReaction () { return rxn; } QueryReaction & IndigoQueryReaction::getQueryReaction () { return rxn; } const char * IndigoQueryReaction::getName () { if (rxn.name.ptr() == 0) return ""; return rxn.name.ptr(); } // // IndigoReactionMolecule // IndigoReactionMolecule::IndigoReactionMolecule (BaseReaction &reaction, int index) : IndigoObject(REACTION_MOLECULE), rxn(reaction), idx(index) { } const char * IndigoReactionMolecule::debugInfo () { return ""; } IndigoReactionMolecule::~IndigoReactionMolecule () { } BaseMolecule & IndigoReactionMolecule::getBaseMolecule () { return rxn.getBaseMolecule(idx); } Molecule & IndigoReactionMolecule::getMolecule () { return rxn.getBaseMolecule(idx).asMolecule(); } QueryMolecule & IndigoReactionMolecule::getQueryMolecule () { return rxn.getBaseMolecule(idx).asQueryMolecule(); } int IndigoReactionMolecule::getIndex () { return idx; } IndigoObject * IndigoReactionMolecule::clone () { if (rxn.isQueryReaction()) return IndigoQueryMolecule::cloneFrom(*this); else return IndigoMolecule::cloneFrom(*this); } void IndigoReactionMolecule::remove () { rxn.remove(idx); } // // IndigoReactionIter // IndigoReactionIter::IndigoReactionIter (BaseReaction &rxn, int subtype) : IndigoObject(REACTION_ITER), _rxn(rxn) { _subtype = subtype; _idx = -1; } const char * IndigoReactionIter::debugInfo () { return ""; } IndigoReactionIter::~IndigoReactionIter () { } int IndigoReactionIter::_begin () { if (_subtype == REACTANTS) return _rxn.reactantBegin(); if (_subtype == PRODUCTS) return _rxn.productBegin(); if (_subtype == CATALYSTS) return _rxn.catalystBegin(); return _rxn.begin(); } int IndigoReactionIter::_end () { if (_subtype == REACTANTS) return _rxn.reactantEnd(); if (_subtype == PRODUCTS) return _rxn.productEnd(); if (_subtype == CATALYSTS) return _rxn.catalystEnd(); return _rxn.end(); } int IndigoReactionIter::_next (int i) { if (_subtype == REACTANTS) return _rxn.reactantNext(i); if (_subtype == PRODUCTS) return _rxn.productNext(i); if (_subtype == CATALYSTS) return _rxn.catalystNext(i); return _rxn.next(i); } IndigoObject * IndigoReactionIter::next () { if (_idx == -1) { _idx = _begin(); } else _idx = _next(_idx); if (_idx == _end()) return 0; return new IndigoReactionMolecule(_rxn, _idx); } bool IndigoReactionIter::hasNext () { if (_idx == -1) return _begin() != _end(); return _next(_idx) != _end(); } IndigoReaction * IndigoReaction::cloneFrom (IndigoObject & obj) { Reaction &rxn = obj.getReaction(); AutoPtr rxnptr; rxnptr.reset(new IndigoReaction()); rxnptr->rxn.clone(rxn, 0, 0, 0); auto& props = obj.getProperties(); rxnptr->copyProperties(props); return rxnptr.release(); } IndigoQueryReaction * IndigoQueryReaction::cloneFrom (IndigoObject & obj) { QueryReaction &rxn = obj.getQueryReaction(); AutoPtr rxnptr; rxnptr.reset(new IndigoQueryReaction()); rxnptr->rxn.clone(rxn, 0, 0, 0); auto& props = obj.getProperties(); rxnptr->copyProperties(props); return rxnptr.release(); } IndigoObject * IndigoReaction::clone () { return cloneFrom(*this); } IndigoObject * IndigoQueryReaction::clone () { return cloneFrom(*this); } int _indigoIterateReaction (int reaction, int subtype) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); return self.addObject(new IndigoReactionIter(rxn, subtype)); } INDIGO_END(-1) } CEXPORT int indigoLoadReaction (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); Scanner &scanner = IndigoScanner::get(obj); ReactionAutoLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.ignore_noncritical_query_features = self.ignore_noncritical_query_features; AutoPtr rxnptr(new IndigoReaction()); loader.loadReaction(rxnptr->rxn); return self.addObject(rxnptr.release()); } INDIGO_END(-1) } CEXPORT int indigoLoadQueryReaction (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); Scanner &scanner = IndigoScanner::get(obj); ReactionAutoLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; AutoPtr rxnptr(new IndigoQueryReaction()); loader.loadQueryReaction(rxnptr->rxn); return self.addObject(rxnptr.release()); } INDIGO_END(-1) } CEXPORT int indigoIterateReactants (int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::REACTANTS); } CEXPORT int indigoIterateProducts (int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::PRODUCTS); } CEXPORT int indigoIterateCatalysts (int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::CATALYSTS); } CEXPORT int indigoIterateMolecules (int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::MOLECULES); } CEXPORT int indigoCreateReaction (void) { INDIGO_BEGIN { return self.addObject(new IndigoReaction()); } INDIGO_END(-1); } CEXPORT int indigoCreateQueryReaction (void) { INDIGO_BEGIN { return self.addObject(new IndigoQueryReaction()); } INDIGO_END(-1); } CEXPORT int indigoAddReactant (int reaction, int molecule) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); rxn.addReactantCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddProduct (int reaction, int molecule) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); rxn.addProductCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddCatalyst (int reaction, int molecule) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); rxn.addCatalystCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoCountReactants (int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().reactantsCount(); } INDIGO_END(-1); } CEXPORT int indigoCountProducts (int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().productsCount(); } INDIGO_END(-1); } CEXPORT int indigoCountCatalysts (int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().catalystCount(); } INDIGO_END(-1); } CEXPORT int indigoCountMolecules (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseReaction::is(obj)) return obj.getBaseReaction().count(); throw IndigoError("can not count molecules of %s", obj.debugInfo()); } INDIGO_END(-1); } CEXPORT int indigoGetMolecule (int reaction, int index) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); return self.addObject(new IndigoReactionMolecule(rxn, index)); } INDIGO_END(-1) } CEXPORT int indigoMapMolecule (int handle, int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (obj.type != IndigoObject::REACTION_MAPPING) throw IndigoError("%s is not a reaction mapping object", obj.debugInfo()); IndigoReactionMapping &mapping = (IndigoReactionMapping &)obj; IndigoObject &mol_obj = self.getObject(molecule); if (mol_obj.type != IndigoObject::REACTION_MOLECULE) throw IndigoError("%s is not a reaction molecule object", mol_obj.debugInfo()); IndigoReactionMolecule &mol = (IndigoReactionMolecule &)mol_obj; if (&mol.rxn != &mapping.from) throw IndigoError("%s molecule doesn't correspond to a mapping %s", mol.debugInfo(), mapping.debugInfo()); int target_index = mapping.mol_mapping[mol.getIndex()]; return self.addObject(new IndigoReactionMolecule(mapping.to, target_index)); } INDIGO_END(-1) } static int readAAMOptions(const char* mode, ReactionAutomapper& ram) { int nmode = ReactionAutomapper::AAM_REGEN_DISCARD; if (mode == 0 || mode[0] == 0) return nmode; QS_DEF(Array, word); BufferScanner scanner(mode); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "discard") == 0) nmode = ReactionAutomapper::AAM_REGEN_DISCARD; else if (strcasecmp(word.ptr(), "alter") == 0) nmode = ReactionAutomapper::AAM_REGEN_ALTER; else if (strcasecmp(word.ptr(), "keep") == 0) nmode = ReactionAutomapper::AAM_REGEN_KEEP; else if (strcasecmp(word.ptr(), "clear") == 0) nmode = ReactionAutomapper::AAM_REGEN_CLEAR; else if (strcasecmp(word.ptr(), "ignore_charges") == 0) ram.ignore_atom_charges = true; else if (strcasecmp(word.ptr(), "ignore_isotopes") == 0) ram.ignore_atom_isotopes = true; else if (strcasecmp(word.ptr(), "ignore_radicals") == 0) ram.ignore_atom_radicals = true; else if (strcasecmp(word.ptr(), "ignore_valence") == 0) ram.ignore_atom_valence = true; else throw IndigoError("indigoAutomap(): unknown mode: %s", word.ptr()); } return nmode; } CEXPORT int indigoAutomap (int reaction, const char *mode) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); ReactionAutomapper ram(rxn); ram.arom_options = self.arom_options; /* * Read options */ int nmode = readAAMOptions(mode, ram); /* * Clear AAM if required */ if(nmode == ReactionAutomapper::AAM_REGEN_CLEAR) { rxn.clearAAM(); return 0; } /* * Set timeout */ AutoPtr timeout; if(self.aam_cancellation_timeout > 0) { timeout.create(self.aam_cancellation_timeout); ram.cancellation = timeout.get(); } /* * Launch automap */ ram.automap(nmode); return 1; } INDIGO_END(-1); } CEXPORT int indigoGetAtomMappingNumber (int reaction, int reaction_atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(reaction_atom)); BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ia.mol); if (mol_idx == -1) throw IndigoError("indigoGetAtomMapping(): input atom not found in the reaction"); return rxn.getAAM(mol_idx, ia.idx); } INDIGO_END(-1); } CEXPORT int indigoSetAtomMappingNumber (int reaction, int reaction_atom, int number) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(reaction_atom)); BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ia.mol); if (mol_idx == -1) throw IndigoError("indigoSetAtomMapping(): input atom not found in the reaction"); if (number < 0) throw IndigoError("indigoSetAtomMapping(): mapping number cannot be negative"); rxn.getAAMArray(mol_idx).at(ia.idx) = number; return 0; } INDIGO_END(-1); } CEXPORT int indigoGetReactingCenter (int reaction, int reaction_bond, int*rc) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(reaction_bond)); BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ib.mol); if (mol_idx == -1) throw IndigoError("indigoGetReactingCenter(): input bond not found in the reaction"); *rc = rxn.getReactingCenter(mol_idx, ib.idx); return 1; } INDIGO_END(-1); } CEXPORT int indigoSetReactingCenter (int reaction, int reaction_bond, int rc) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(reaction_bond)); BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ib.mol); if (mol_idx == -1) throw IndigoError("indigoSetReactingCenter(): input bond not found in the reaction"); if (rc < -1 || rc > RC_TOTAL) throw IndigoError("indigoSetReactingCenter(): invalid or unsupported reacting center: %d", rc); rxn.getReactingCenterArray(mol_idx).at(ib.idx) = rc; return 1; } INDIGO_END(-1); } CEXPORT int indigoClearAAM (int reaction) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); rxn.clearAAM(); return 0; } INDIGO_END(-1); } CEXPORT int indigoCorrectReactingCenters (int reaction) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); ReactionAutomapper ram(rxn); ram.arom_options = self.arom_options; ram.correctReactingCenters(true); return 0; } INDIGO_END(-1); } CEXPORT int indigoLoadReactionSmarts (int source) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(source); RSmilesLoader loader(IndigoScanner::get(obj)); AutoPtr rxnptr(new IndigoQueryReaction()); QueryReaction &qrxn = rxnptr->rxn; loader.smarts_mode = true; loader.loadQueryReaction(qrxn); return self.addObject(rxnptr.release()); } INDIGO_END(-1); } CEXPORT const char * indigoCanonicalRSmiles(int reaction) { INDIGO_BEGIN { Reaction &react = self.getObject(reaction).getReaction(); auto &tmp = self.getThreadTmpData(); ArrayOutput output(tmp.string); CanonicalRSmilesSaver saver(output); saver.saveReaction(react); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); } Indigo-indigo-1.2.3/api/src/indigo_reaction.h000066400000000000000000000061511271037650300210520ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_reaction__ #define __indigo_reaction__ #include "indigo_internal.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "base_cpp/properties_map.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class DLLEXPORT IndigoBaseReaction : public IndigoObject { public: explicit IndigoBaseReaction (int type_); virtual ~IndigoBaseReaction (); virtual indigo::PropertiesMap& getProperties() { return _properties;} static bool is (IndigoObject &obj); virtual const char * debugInfo (); indigo::PropertiesMap _properties; }; class DLLEXPORT IndigoReaction : public IndigoBaseReaction { public: IndigoReaction (); virtual ~IndigoReaction (); virtual BaseReaction & getBaseReaction (); virtual Reaction & getReaction (); virtual const char * getName (); virtual IndigoObject * clone (); static IndigoReaction * cloneFrom (IndigoObject & obj); virtual const char * debugInfo (); Reaction rxn; }; class DLLEXPORT IndigoQueryReaction : public IndigoBaseReaction { public: IndigoQueryReaction (); virtual ~IndigoQueryReaction (); virtual BaseReaction & getBaseReaction (); virtual QueryReaction & getQueryReaction (); virtual const char * getName (); virtual IndigoObject * clone(); static IndigoQueryReaction * cloneFrom (IndigoObject & obj); virtual const char * debugInfo (); QueryReaction rxn; }; class IndigoReactionMolecule : public IndigoObject { public: IndigoReactionMolecule (BaseReaction &reaction, int index); virtual ~IndigoReactionMolecule (); virtual BaseMolecule & getBaseMolecule (); virtual QueryMolecule & getQueryMolecule (); virtual Molecule & getMolecule (); virtual int getIndex (); virtual IndigoObject * clone (); virtual void remove (); virtual indigo::PropertiesMap& getProperties() { return _properties;} virtual const char * debugInfo (); BaseReaction &rxn; int idx; indigo::PropertiesMap _properties; }; class IndigoReactionIter : public IndigoObject { public: enum { REACTANTS, PRODUCTS, CATALYSTS, MOLECULES }; IndigoReactionIter (BaseReaction &rxn, int subtype); virtual ~IndigoReactionIter (); virtual IndigoObject * next (); virtual bool hasNext (); virtual const char * debugInfo (); protected: int _begin (); int _end (); int _next (int i); int _subtype; BaseReaction &_rxn; int _idx; }; #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/api/src/indigo_savers.cpp000066400000000000000000000317121271037650300211050ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_savers.h" #include "indigo_io.h" #include "indigo_molecule.h" #include "indigo_reaction.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "base_cpp/auto_ptr.h" #include "molecule/molecule_cml_saver.h" #include "molecule/molfile_saver.h" #include "molecule/smiles_saver.h" #include "molecule/canonical_smiles_saver.h" #include "reaction/rxnfile_saver.h" #include "reaction/rsmiles_saver.h" #include "reaction/canonical_rsmiles_saver.h" #include "reaction/reaction_cml_saver.h" #include // // IndigoSaver // IndigoSaver::IndigoSaver (Output &output) : IndigoObject(IndigoObject::SAVER), _output(output) { _closed = false; _own_output = 0; } IndigoSaver::~IndigoSaver () { close(); } void IndigoSaver::acquireOutput (Output *output) { if (_own_output) delete _own_output; _own_output = output; } void IndigoSaver::close () { if (_closed) return; _appendFooter(); delete _own_output; _own_output = 0; _closed = true; } IndigoSaver* IndigoSaver::create (Output &output, const char *type) { AutoPtr saver; if (strcasecmp(type, "sdf") == 0) saver = new IndigoSdfSaver(output); else if (strcasecmp(type, "smiles") == 0 || strcasecmp(type, "smi") == 0) saver = new IndigoSmilesSaver(output); else if (strcasecmp(type, "cml") == 0) saver = new IndigoCmlSaver(output); else if (strcasecmp(type, "rdf") == 0) saver = new IndigoRdfSaver(output); else throw IndigoError("unsupported saver type: '%s'. Supported formats are sdf, smiles, cml, rdf", type); saver->_appendHeader(); return saver.release(); } void IndigoSaver::appendObject (IndigoObject &object) { if (_closed) throw IndigoError("save %s has already been closed", debugInfo()); _append(object); } // // IndigoSDFSaver // void IndigoSdfSaver::appendMolfile (Output &out, IndigoObject &obj) { Indigo &indigo = indigoGetInstance(); MolfileSaver saver(out); indigo.initMolfileSaver(saver); saver.saveBaseMolecule(obj.getBaseMolecule()); } void IndigoSdfSaver::append (Output &out, IndigoObject &obj) { appendMolfile(out, obj); auto& props = obj.getProperties(); for (auto i : props.elements()) { out.printf("> <%s>\n%s\n\n", props.key(i), props.value(i)); } out.printfCR("$$$$"); out.flush(); } const char * IndigoSdfSaver::debugInfo () { return ""; } void IndigoSdfSaver::_append (IndigoObject &object) { append(_output, object); } CEXPORT int indigoSdfAppend (int output, int molecule) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); Output &out = IndigoOutput::get(self.getObject(output)); IndigoSdfSaver::append(out, obj); return 1; } INDIGO_END(-1) } // // IndigoSmilesSaver // void IndigoSmilesSaver::generateSmiles (IndigoObject &obj, Array &out_buffer) { ArrayOutput output(out_buffer); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &mol = obj.getBaseMolecule(); SmilesSaver saver(output); if (mol.isQueryMolecule()) saver.saveQueryMolecule(mol.asQueryMolecule()); else saver.saveMolecule(mol.asMolecule()); } else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); RSmilesSaver saver(output); if (rxn.isQueryReaction()) saver.saveQueryReaction(rxn.asQueryReaction()); else saver.saveReaction(rxn.asReaction()); } else throw IndigoError("%s can not be converted to SMILES", obj.debugInfo()); out_buffer.push(0); } void IndigoSmilesSaver::append (Output &output, IndigoObject &object) { QS_DEF(Array, tmp_buffer); IndigoSmilesSaver::generateSmiles(object, tmp_buffer); output.writeString(tmp_buffer.ptr()); Indigo &indigo = indigoGetInstance(); if (indigo.smiles_saving_write_name) { output.writeString(" "); output.writeString(object.getName()); } output.writeCR(); output.flush(); } const char * IndigoSmilesSaver::debugInfo () { return ""; } void IndigoSmilesSaver::_append (IndigoObject &object) { append(_output, object); } CEXPORT int indigoSmilesAppend (int output, int item) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); IndigoObject &obj = self.getObject(item); IndigoSmilesSaver::append(out, obj); out.flush(); return 1; } INDIGO_END(-1) } // // IndigoCanonicalSmilesSaver // void IndigoCanonicalSmilesSaver::generateSmiles(IndigoObject &obj, Array &out_buffer) { ArrayOutput output(out_buffer); if (IndigoBaseMolecule::is(obj)) { BaseMolecule &mol = obj.getBaseMolecule(); CanonicalSmilesSaver saver(output); if (mol.isQueryMolecule()) saver.saveQueryMolecule(mol.asQueryMolecule()); else saver.saveMolecule(mol.asMolecule()); } else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); CanonicalRSmilesSaver saver(output); if (rxn.isQueryReaction()) saver.saveQueryReaction(rxn.asQueryReaction()); else saver.saveReaction(rxn.asReaction()); } else throw IndigoError("%s can not be converted to SMILES", obj.debugInfo()); out_buffer.push(0); } const char * IndigoCanonicalSmilesSaver::debugInfo() { return ""; } // // IndigoCMLSaver // void IndigoCmlSaver::append (Output &out, IndigoObject &obj) { if (IndigoBaseMolecule::is(obj)) { MoleculeCmlSaver saver(out); saver.skip_cml_tag = true; saver.saveMolecule(obj.getMolecule()); } else if (IndigoBaseReaction::is(obj)) { ReactionCmlSaver saver(out); saver.skip_cml_tag = true; saver.saveReaction(obj.getReaction()); } else throw IndigoError("%s can not be saved to CML", obj.debugInfo()); } void IndigoCmlSaver::appendHeader (Output &out) { out.printf("\n"); out.printf("\n"); } void IndigoCmlSaver::appendFooter (Output &out) { out.printf("\n"); } const char * IndigoCmlSaver::debugInfo () { return ""; } void IndigoCmlSaver::_append (IndigoObject &object) { append(_output, object); } void IndigoCmlSaver::_appendHeader () { appendHeader(_output); } void IndigoCmlSaver::_appendFooter () { appendFooter(_output); } CEXPORT int indigoCmlHeader (int output) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); IndigoCmlSaver::appendHeader(out); return 1; } INDIGO_END(-1) } CEXPORT int indigoCmlFooter (int output) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); IndigoCmlSaver::appendFooter(out); return 1; } INDIGO_END(-1) } CEXPORT int indigoCmlAppend (int output, int item) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); IndigoObject &obj = self.getObject(item); IndigoCmlSaver::append(out, obj); return 1; } INDIGO_END(-1) } // // IndigoRDFSaver // void IndigoRdfSaver::appendRXN (Output &out, IndigoObject &obj) { Indigo &indigo = indigoGetInstance(); RxnfileSaver saver(out); indigo.initRxnfileSaver(saver); saver.saveBaseReaction(obj.getBaseReaction()); } void IndigoRdfSaver::append (Output &out, IndigoObject &obj) { if (IndigoBaseMolecule::is(obj)) { out.writeStringCR("$MFMT"); IndigoSdfSaver::appendMolfile(out, obj); } else if (IndigoBaseReaction::is(obj)) { out.writeStringCR("$RFMT"); IndigoRdfSaver::appendRXN(out, obj); } else throw IndigoError("%s can not be saved to RDF", obj.debugInfo()); auto& props = obj.getProperties(); for (auto i : props.elements()) { out.printf("$DTYPE %s\n$DATUM %s\n", props.key(i), props.value(i)); } } void IndigoRdfSaver::appendHeader (Output &out) { Indigo &indigo = indigoGetInstance(); out.printfCR("$RDFILE 1"); struct tm lt; if (indigo.molfile_saving_skip_date) memset(<, 0, sizeof(lt)); else { time_t tm = time(NULL); lt = *localtime(&tm); } out.printfCR("$DATM %02d/%02d/%02d %02d:%02d", lt.tm_mon + 1, lt.tm_mday, lt.tm_year % 100, lt.tm_hour, lt.tm_min); } const char * IndigoRdfSaver::debugInfo () { return ""; } void IndigoRdfSaver::_append (IndigoObject &object) { append(_output, object); } void IndigoRdfSaver::_appendHeader () { appendHeader(_output); } CEXPORT int indigoRdfHeader (int output) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); IndigoRdfSaver::appendHeader(out); return 1; } INDIGO_END(-1) } CEXPORT int indigoRdfAppend (int output, int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); Output &out = IndigoOutput::get(self.getObject(output)); IndigoRdfSaver::append(out, obj); return 1; } INDIGO_END(-1) } // // Saving functions // CEXPORT int indigoCreateSaver (int output, const char *format) { INDIGO_BEGIN { Output &out = IndigoOutput::get(self.getObject(output)); return self.addObject(IndigoSaver::create(out, format)); } INDIGO_END(-1) } CEXPORT int indigoCreateFileSaver (const char *filename, const char *format) { INDIGO_BEGIN { AutoPtr output(new FileOutput(self.filename_encoding, filename)); AutoPtr saver(IndigoSaver::create(output.ref(), format)); saver->acquireOutput(output.release()); return self.addObject(saver.release()); } INDIGO_END(-1) } CEXPORT int indigoSaveMolfile (int molecule, int output) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(molecule); Output &out = IndigoOutput::get(self.getObject(output)); IndigoSdfSaver::appendMolfile(out, obj); out.flush(); return 1; } INDIGO_END(-1) } CEXPORT int indigoSaveCml (int item, int output) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); Output &out = IndigoOutput::get(self.getObject(output)); if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); MoleculeCmlSaver saver(out); saver.saveMolecule(mol); out.flush(); return 1; } if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); ReactionCmlSaver saver(out); saver.saveReaction(rxn); out.flush(); return 1; } throw IndigoError("indigoSaveCml(): expected molecule or reaction, got %s", obj.debugInfo()); } INDIGO_END(-1) } CEXPORT int indigoSaveMDLCT (int item, int output) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); QS_DEF(Array, buf); ArrayOutput out(buf); if (IndigoBaseMolecule::is(obj)) IndigoSdfSaver::appendMolfile(out, obj); else if (IndigoBaseReaction::is(obj)) IndigoRdfSaver::appendRXN(out, obj); Output &out2 = IndigoOutput::get(self.getObject(output)); BufferScanner scanner(buf); QS_DEF(Array, line); while (!scanner.isEOF()) { scanner.readLine(line, false); if (line.size() > 255) throw IndigoError("indigoSaveMDLCT: line too big (%d)", line.size()); out2.writeChar(line.size()); out2.writeArray(line); } return 1; } INDIGO_END(-1) } CEXPORT int indigoSaveRxnfile (int reaction, int output) { INDIGO_BEGIN { BaseReaction &rxn = self.getObject(reaction).getBaseReaction(); Output &out = IndigoOutput::get(self.getObject(output)); RxnfileSaver saver(out); self.initRxnfileSaver(saver); if (rxn.isQueryReaction()) saver.saveQueryReaction(rxn.asQueryReaction()); else saver.saveReaction(rxn.asReaction()); out.flush(); return 1; } INDIGO_END(-1) } CEXPORT int indigoAppend (int saver_id, int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); IndigoObject &saver_obj = self.getObject(saver_id); if (saver_obj.type != IndigoObject::SAVER) throw IndigoError("indigoAppend() is only applicable to saver objects. %s object was passed as a saver", saver_obj.debugInfo()); IndigoSaver &saver = (IndigoSaver &)saver_obj; saver.appendObject(obj); return 1; } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_savers.h000066400000000000000000000061671271037650300205600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_savers__ #define __indigo_savers__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "indigo_internal.h" class DLLEXPORT IndigoSaver : public IndigoObject { public: IndigoSaver (Output &output); ~IndigoSaver (); void acquireOutput (Output *output); void close (); void appendObject (IndigoObject &object); static IndigoSaver* create (Output &output, const char *type); protected: virtual void _appendHeader () {}; virtual void _appendFooter () {}; virtual void _append (IndigoObject &object) = 0; Output &_output; private: bool _closed; Output *_own_output; }; class IndigoSdfSaver : public IndigoSaver { public: IndigoSdfSaver (Output &output) : IndigoSaver(output) {} virtual const char * debugInfo (); static void append (Output &output, IndigoObject &object); static void appendMolfile (Output &output, IndigoObject &object); protected: virtual void _append (IndigoObject &object); }; class IndigoSmilesSaver : public IndigoSaver { public: IndigoSmilesSaver (Output &output) : IndigoSaver(output) {} virtual const char * debugInfo (); static void generateSmiles (IndigoObject &obj, Array &out_buffer); static void append (Output &output, IndigoObject &object); protected: virtual void _append (IndigoObject &object); }; class IndigoCanonicalSmilesSaver : public IndigoSaver { public: IndigoCanonicalSmilesSaver(Output &output) : IndigoSaver(output) {} virtual const char * debugInfo(); static void generateSmiles(IndigoObject &obj, Array &out_buffer); protected: }; class IndigoCmlSaver : public IndigoSaver { public: IndigoCmlSaver (Output &output) : IndigoSaver(output) {} virtual const char * debugInfo (); static void append (Output &output, IndigoObject &object); static void appendHeader (Output &output); static void appendFooter (Output &output); protected: virtual void _append (IndigoObject &object); virtual void _appendHeader (); virtual void _appendFooter (); }; class IndigoRdfSaver : public IndigoSaver { public: IndigoRdfSaver (Output &output) : IndigoSaver(output) {} virtual const char * debugInfo (); static void append (Output &output, IndigoObject &object); static void appendRXN (Output &output, IndigoObject &object); static void appendHeader (Output &output); protected: virtual void _append (IndigoObject &object); virtual void _appendHeader (); }; #ifdef _WIN32 #pragma warning(pop) #endif #endif // __indigo_savers__ Indigo-indigo-1.2.3/api/src/indigo_scaffold.cpp000066400000000000000000000067401271037650300213660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_scaffold.h" #include "indigo_array.h" #include "indigo_molecule.h" #include "molecule/molecule_scaffold_detection.h" #include "molecule/molecule_exact_matcher.h" #include "base_cpp/scanner.h" IndigoScaffold::IndigoScaffold () : IndigoObject(SCAFFOLD) { } IndigoScaffold::~IndigoScaffold () { } CEXPORT int indigoExtractCommonScaffold (int structures, const char* options) { INDIGO_BEGIN { QS_DEF(ObjArray, mol_set); IndigoArray &arr = IndigoArray::cast(self.getObject(structures)); int i; mol_set.clear(); for (i = 0; i < arr.objects.size(); i++) mol_set.push().clone(arr.objects[i]->getMolecule(), 0, 0); if (self.deconvolution_aromatization) for(int i = 0; i < mol_set.size(); ++i) MoleculeAromatizer::aromatizeBonds(mol_set[i], self.arom_options); AutoPtr scaf(new IndigoScaffold()); MoleculeScaffoldDetection msd(&mol_set); msd.basketStructures = &scaf->all_scaffolds; bool approximate = false; int max_iterations = 0; if (options != 0) { BufferScanner scanner(options); QS_DEF(Array, word); scanner.skipSpace(); if (!scanner.isEOF()) { scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "APPROX") == 0) approximate = true; else if (strcasecmp(word.ptr(), "EXACT") == 0) approximate = false; else throw IndigoError("indigoExtractCommonScaffold: unknown option %s\n", word.ptr()); scanner.skipSpace(); if (!scanner.isEOF()) { max_iterations = scanner.readInt(); } } } if(max_iterations > 0) msd.maxIterations = max_iterations; if (approximate) msd.extractApproximateScaffold(scaf->max_scaffold); else msd.extractExactScaffold(scaf->max_scaffold); return self.addObject(scaf.release()); } INDIGO_END(-1); } QueryMolecule & IndigoScaffold::getQueryMolecule () { return max_scaffold; } BaseMolecule & IndigoScaffold::getBaseMolecule () { return max_scaffold; } CEXPORT int indigoAllScaffolds (int extracted) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(extracted); if (obj.type != IndigoObject::SCAFFOLD) throw IndigoError("indigoAllScaffolds(): can not accept %s", obj.debugInfo()); IndigoScaffold &scaf = (IndigoScaffold &)obj; AutoPtr arr(new IndigoArray()); int i; for (i = 0; i < scaf.all_scaffolds.size(); i++) { AutoPtr mol(new IndigoQueryMolecule); mol->qmol.clone(scaf.all_scaffolds[i], 0, 0); arr->objects.add(mol.release()); } return self.addObject(arr.release()); } INDIGO_END(-1); } Indigo-indigo-1.2.3/api/src/indigo_scaffold.h000066400000000000000000000020761271037650300210310ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_scaffold__ #define __indigo_scaffold__ #include "indigo_internal.h" #include "molecule/query_molecule.h" class IndigoScaffold : public IndigoObject { public: IndigoScaffold (); virtual ~IndigoScaffold (); void extractScaffold (); virtual QueryMolecule & getQueryMolecule (); virtual BaseMolecule & getBaseMolecule (); QueryMolecule max_scaffold; ObjArray all_scaffolds; }; #endifIndigo-indigo-1.2.3/api/src/indigo_stereo.cpp000066400000000000000000000354051271037650300211060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_molecule.h" #include "indigo_reaction.h" #include "reaction/reaction.h" #include "molecule/molecule_automorphism_search.h" #include "molecule/molecule_exact_matcher.h" CEXPORT int indigoStereocenterType (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.allene_stereo.isCenter(ia.idx)) return INDIGO_ALLENE; switch (ia.mol.stereocenters.getType(ia.idx)) { case MoleculeStereocenters::ATOM_ABS: return INDIGO_ABS; case MoleculeStereocenters::ATOM_OR: return INDIGO_OR; case MoleculeStereocenters::ATOM_AND: return INDIGO_AND; case MoleculeStereocenters::ATOM_ANY: return INDIGO_EITHER; default: return 0; } } INDIGO_END(-1); } static int mapStereocenterType (int api_stereocenter_type) { switch (api_stereocenter_type) { case INDIGO_ABS: return MoleculeStereocenters::ATOM_ABS; case INDIGO_OR: return MoleculeStereocenters::ATOM_OR; case INDIGO_AND: return MoleculeStereocenters::ATOM_AND; case INDIGO_EITHER: return MoleculeStereocenters::ATOM_ANY; default: throw IndigoError("Unknown stereocenter type"); } } CEXPORT int indigoStereocenterGroup (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.stereocenters.getType(ia.idx) == 0) throw IndigoError("Atom is not a stereocenter"); return ia.mol.stereocenters.getGroup(ia.idx); } INDIGO_END(-1); } CEXPORT int indigoSetStereocenterGroup (int atom, int group) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.stereocenters.getType(ia.idx) == 0) throw IndigoError("Atom is not a stereocenter"); ia.mol.stereocenters.setGroup(ia.idx, group); return 0; } INDIGO_END(-1); } CEXPORT int indigoChangeStereocenterType (int atom, int type) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); if (ia.mol.stereocenters.getType(ia.idx) == 0) throw IndigoError("Atom is not a stereocenter"); int group = ia.mol.stereocenters.getGroup(ia.idx); ia.mol.stereocenters.setType(ia.idx, mapStereocenterType(type), group); if (ia.mol.have_xyz) ia.mol.stereocenters.markBond(ia.idx); return 0; } INDIGO_END(-1); } CEXPORT int indigoAddStereocenter (int atom, int type, int v1, int v2, int v3, int v4) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int core_type; switch (type) { case INDIGO_ABS: core_type = MoleculeStereocenters::ATOM_ABS; break; case INDIGO_OR: core_type = MoleculeStereocenters::ATOM_OR; break; case INDIGO_AND: core_type = MoleculeStereocenters::ATOM_AND; break; case INDIGO_EITHER: core_type = MoleculeStereocenters::ATOM_ANY; break; default: throw IndigoError("Unknown stereocenter type"); } int pyramid[4] = { v1, v2, v3, v4 }; ia.mol.stereocenters.add(ia.idx, core_type, 0, pyramid); return 1; } INDIGO_END(-1); } CEXPORT const int* indigoStereocenterPyramid (int atom) { INDIGO_BEGIN { IndigoAtom &ia = IndigoAtom::cast(self.getObject(atom)); int *pyramid = ia.mol.stereocenters.getPyramid(ia.idx); if (pyramid == 0) throw IndigoError("No stereocenter at the atom %d", atom); return ia.mol.stereocenters.getPyramid(ia.idx); } INDIGO_END(NULL); } CEXPORT int indigoCountStereocenters (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.stereocenters.size(); } INDIGO_END(-1) } CEXPORT int indigoClearAlleneCenters (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); mol.allene_stereo.clear(); return 1; } INDIGO_END(-1) } CEXPORT int indigoCountAlleneCenters (int molecule) { INDIGO_BEGIN { BaseMolecule &mol = self.getObject(molecule).getBaseMolecule(); return mol.allene_stereo.size(); } INDIGO_END(-1) } CEXPORT int indigoBondStereo (int bond) { INDIGO_BEGIN { IndigoBond &ib = IndigoBond::cast(self.getObject(bond)); BaseMolecule &mol = ib.mol; int dir = mol.getBondDirection(ib.idx); if (dir == BOND_UP) return INDIGO_UP; if (dir == BOND_DOWN) return INDIGO_DOWN; if (dir == BOND_EITHER) return INDIGO_EITHER; int parity = mol.cis_trans.getParity(ib.idx); if (parity == MoleculeCisTrans::CIS) return INDIGO_CIS; if (parity == MoleculeCisTrans::TRANS) return INDIGO_TRANS; return 0; } INDIGO_END(-1); } CEXPORT int indigoInvertStereo (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(self.getObject(item)); if (ia.mol.stereocenters.getType(ia.idx) > 0) { int k, *pyramid = ia.mol.stereocenters.getPyramid(ia.idx); if (pyramid == 0) throw IndigoError("indigoInvertStereo: internal"); __swap(pyramid[0], pyramid[1], k); } else if (ia.mol.allene_stereo.isCenter(ia.idx)) ia.mol.allene_stereo.invert(ia.idx); else throw IndigoError("indigoInvertStereo: not a stereo atom"); } else if (IndigoBond::is(obj)) { IndigoBond &ib = IndigoBond::cast(self.getObject(item)); int parity = ib.mol.cis_trans.getParity(ib.idx); if (parity == 0) throw IndigoError("indigoInvertStereo: not a stereobond"); if (parity == MoleculeCisTrans::CIS) ib.mol.cis_trans.setParity(ib.idx, MoleculeCisTrans::TRANS); else ib.mol.cis_trans.setParity(ib.idx, MoleculeCisTrans::CIS); } else throw IndigoError("indigoInvertStereo(): %s given", obj.debugInfo()); return 1; } INDIGO_END(-1) } CEXPORT int indigoResetStereo (int item) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(item); if (IndigoAtom::is(obj)) { IndigoAtom &ia = IndigoAtom::cast(self.getObject(item)); if (ia.mol.stereocenters.getType(ia.idx) != 0) ia.mol.stereocenters.remove(ia.idx); if (ia.mol.allene_stereo.isCenter(ia.idx)) ia.mol.allene_stereo.reset(ia.idx); } else if (IndigoBond::is(obj)) { IndigoBond &ib = IndigoBond::cast(self.getObject(item)); ib.mol.setBondDirection(ib.idx, 0); ib.mol.cis_trans.setParity(ib.idx, 0); } else throw IndigoError("indigoResetStereo(): %s given", obj.debugInfo()); return 1; } INDIGO_END(-1) } CEXPORT int indigoClearStereocenters (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (IndigoBaseMolecule::is(obj)) { obj.getBaseMolecule().stereocenters.clear(); obj.getBaseMolecule().clearBondDirections(); } else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); int i; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) { rxn.getBaseMolecule(i).stereocenters.clear(); rxn.getBaseMolecule(i).clearBondDirections(); } } else throw IndigoError("only molecules and reactions have stereocenters"); return 1; } INDIGO_END(-1) } CEXPORT int indigoClearCisTrans (int object) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(object); if (IndigoBaseMolecule::is(obj)) obj.getBaseMolecule().cis_trans.clear(); else if (IndigoBaseReaction::is(obj)) { BaseReaction &rxn = obj.getBaseReaction(); int i; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) rxn.getBaseMolecule(i).cis_trans.clear(); } else throw IndigoError("only molecules and reactions have cis-trans"); return 1; } INDIGO_END(-1) } static int _resetSymmetric (Molecule &mol, bool cistrans, bool stereo) { MoleculeAutomorphismSearch am; int sum = 0; if (cistrans) am.detect_invalid_cistrans_bonds = true; if (stereo) am.detect_invalid_stereocenters = true; am.allow_undefined = true; am.process(mol); if (cistrans) { for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (mol.cis_trans.getParity(i) == 0) continue; if (am.invalidCisTransBond(i)) { mol.cis_trans.setParity(i, 0); sum++; } } } if (stereo) { QS_DEF(Array, to_remove); to_remove.clear(); for (int i = mol.stereocenters.begin(); i != mol.stereocenters.end(); i = mol.stereocenters.next(i)) { int atom_index = mol.stereocenters.getAtomIndex(i); if (am.invalidStereocenter(atom_index)) { to_remove.push(atom_index); sum++; } } for (int i = 0; i < to_remove.size(); i++) mol.stereocenters.remove(to_remove[i]); if (to_remove.size() > 0) { mol.clearBondDirections(); mol.stereocenters.markBonds(); } } return sum; } static int _markEitherCisTrans (Molecule &mol) { MoleculeAutomorphismSearch am; int i, sum = 0; for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { int substituents[4]; if (mol.cis_trans.getParity(i) != 0) continue; if (!MoleculeCisTrans::isGeomStereoBond(mol, i, substituents, false)) continue; if (mol.getEdgeTopology(i) == TOPOLOGY_RING) { // Mark all bonds in large cycles if (mol.edgeSmallestRingSize(i) >= 8) mol.cis_trans.ignore(i); continue; } am.possible_cis_trans_to_check.push(i); } am.allow_undefined = true; am.process(mol); for (i = 0; i < am.possible_cis_trans_to_check.size(); i++) { int bond = am.possible_cis_trans_to_check[i]; mol.cis_trans.ignore(bond); sum++; } return sum; } CEXPORT int indigoResetSymmetricCisTrans (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) return _resetSymmetric(obj.getMolecule(), true, false); else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); int i, sum = 0; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) sum += _resetSymmetric(rxn.getMolecule(i), true, false); return sum; } throw IndigoError("only molecules and reactions have cis-trans"); } INDIGO_END(-1) } CEXPORT int indigoResetSymmetricStereocenters (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) return _resetSymmetric(obj.getMolecule(), false, true); else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); int i, sum = 0; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) sum += _resetSymmetric(rxn.getMolecule(i), false, true); return sum; } throw IndigoError("only molecules and reactions have cis-trans"); } INDIGO_END(-1) } CEXPORT int indigoMarkEitherCisTrans (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) return _markEitherCisTrans(obj.getMolecule()); else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); int i, sum = 0; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) sum += _markEitherCisTrans(rxn.getMolecule(i)); return sum; } throw IndigoError("only molecules and reactions have cis-trans"); } INDIGO_END(-1) } CEXPORT int indigoMarkStereobonds (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) obj.getMolecule().stereocenters.markBonds(); else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); for (int i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) rxn.getMolecule(i).stereocenters.markBonds(); } else throw IndigoError("only molecules and reactions have stereocenters"); return 0; } INDIGO_END(-1) } static void _indigoValidateMoleculeChirality (Molecule &mol) { if (mol.stereocenters.size() == 0) return; if (!mol.stereocenters.haveAbs()) return; QS_DEF(Molecule, mirror); mirror.clone(mol, 0, 0); for (int s = mirror.stereocenters.begin(); s != mirror.stereocenters.end(); s = mirror.stereocenters.next(s)) { int atom = mirror.stereocenters.getAtomIndex(s); if (mirror.stereocenters.getType(atom) == MoleculeStereocenters::ATOM_ABS) mirror.stereocenters.invertPyramid(atom); } // Check exact matching MoleculeExactMatcher matcher(mol, mirror); matcher.flags = MoleculeExactMatcher::CONDITION_ALL; if (!matcher.find()) return; for (int s = mol.stereocenters.begin(); s != mol.stereocenters.end(); s = mol.stereocenters.next(s)) { int atom = mol.stereocenters.getAtomIndex(s); if (mol.stereocenters.getType(atom) == MoleculeStereocenters::ATOM_ABS) mol.stereocenters.setType(atom, MoleculeStereocenters::ATOM_AND); } } CEXPORT int indigoValidateChirality (int handle) { INDIGO_BEGIN { IndigoObject &obj = self.getObject(handle); if (IndigoBaseMolecule::is(obj)) { Molecule &mol = obj.getMolecule(); _indigoValidateMoleculeChirality(mol); } else if (IndigoBaseReaction::is(obj)) { Reaction &rxn = obj.getReaction(); for (int i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) _indigoValidateMoleculeChirality(rxn.getMolecule(i)); } else throw IndigoError("only molecules and reactions have stereocenters"); return 0; } INDIGO_END(-1) } Indigo-indigo-1.2.3/api/src/indigo_stereo.h000066400000000000000000000012721271037650300205460ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_stereo__ #define __indigo_stereo__ #endif Indigo-indigo-1.2.3/api/src/indigo_tautomer_enumerator.cpp000066400000000000000000000054411271037650300237030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "indigo_tautomer_enumerator.h" #include "indigo_molecule.h" CEXPORT int indigoIterateTautomers(int molecule, const char *options) { INDIGO_BEGIN { Molecule &mol = self.getObject(molecule).getMolecule(); TautomerMethod method; if(strncasecmp(options, "INCHI", 5) == 0) method = INCHI; else if(strncasecmp(options, "RSMARTS", 7) == 0) method = RSMARTS; else method = RSMARTS; return self.addObject(new IndigoTautomerIter(mol, method)); } INDIGO_END(-1) } IndigoTautomerIter::IndigoTautomerIter(Molecule &molecule, TautomerMethod method) : IndigoObject(TAUTOMER_ITER), _enumerator(molecule, method), _complete(false) { bool needAromatize = molecule.isAromatized(); if(needAromatize) _currentPosition = _enumerator.beginAromatized(); else _currentPosition = _enumerator.beginNotAromatized(); } const char * IndigoTautomerIter::debugInfo() { return ""; } IndigoTautomerIter::~IndigoTautomerIter() { } int IndigoTautomerIter::getIndex() { return _currentPosition > 0? _currentPosition: -_currentPosition; } IndigoObject * IndigoTautomerIter::next() { if (hasNext()) { AutoPtr result(new IndigoMoleculeTautomer(_enumerator, _currentPosition)); _currentPosition = _enumerator.next(_currentPosition); return result.release(); } return NULL; } bool IndigoTautomerIter::hasNext() { return _enumerator.isValid(_currentPosition); } IndigoMoleculeTautomer::IndigoMoleculeTautomer(TautomerEnumerator &enumerator, int index) : IndigoObject(TAUTOMER_MOLECULE), _index(index) { enumerator.constructMolecule(_molInstance, index); } const char * IndigoMoleculeTautomer::debugInfo() { return ""; } IndigoMoleculeTautomer::~IndigoMoleculeTautomer() { } IndigoObject *IndigoMoleculeTautomer::clone() { return IndigoMolecule::cloneFrom(*this); } int IndigoMoleculeTautomer::getIndex() { return (_index > 0? _index: -_index) - 1; } Molecule & IndigoMoleculeTautomer::getMolecule() { return _molInstance; } Indigo-indigo-1.2.3/api/src/indigo_tautomer_enumerator.h000066400000000000000000000032611271037650300233460ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_tautomer_enumerator__ #define __indigo_tautomer_enumerator__ #include "indigo_internal.h" #include "molecule/molecule_tautomer_enumerator.h" #include "base_cpp/properties_map.h" class IndigoMoleculeTautomer : public IndigoObject { public: IndigoMoleculeTautomer(TautomerEnumerator &enumerator, int index); virtual ~IndigoMoleculeTautomer(); virtual int getIndex(); virtual Molecule & getMolecule(); virtual IndigoObject * clone(); virtual const char * debugInfo(); virtual PropertiesMap& getProperties() { return _properties;} private: Molecule _molInstance; int _index; indigo::PropertiesMap _properties; }; class IndigoTautomerIter : public IndigoObject { public: IndigoTautomerIter(Molecule &molecule, TautomerMethod method); virtual ~IndigoTautomerIter(); virtual int getIndex(); virtual IndigoObject * next(); virtual bool hasNext(); virtual const char * debugInfo(); protected: TautomerEnumerator _enumerator; int _currentPosition; bool _complete; }; #endif /* __indigo_tautomer_enumerator__ */ Indigo-indigo-1.2.3/api/src/indigo_version.h.in000066400000000000000000000014031271037650300213330ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __indigo_version__ #define __indigo_version__ #define INDIGO_VERSION "${INDIGO_VERSION_EXT}" #endif Indigo-indigo-1.2.3/api/src/option_manager.cpp000066400000000000000000000115221271037650300212500ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "option_manager.h" #include "base_cpp/scanner.h" ThreadSafeStaticObj indigo_option_manager; DLLEXPORT OptionManager & indigoGetOptionManager () { return indigo_option_manager.ref(); } IMPL_ERROR(OptionManager, "option manager"); OptionManager::OptionManager () { } void OptionManager::callOptionHandlerInt (const char* name, int value) { CHECK_OPT_DEFINED(name); if (typeMap.at(name) == OPTION_BOOL && (value == 0 || value == 1)) { hMapBool.at(name)(value); return; } if (typeMap.at(name) == OPTION_INT) hMapInt.at(name)(value); else callOptionHandlerT(name, value); } void OptionManager::callOptionHandlerBool (const char* name, int value) { CHECK_OPT_DEFINED(name); if (typeMap.at(name) == OPTION_BOOL) hMapBool.at(name)(value); else callOptionHandlerT(name, value); } void OptionManager::callOptionHandlerFloat (const char* name, float value) { CHECK_OPT_DEFINED(name); if (typeMap.at(name) == OPTION_FLOAT) hMapFloat.at(name)(value); else callOptionHandlerT(name, value); } void OptionManager::callOptionHandlerColor (const char* name, float r, float g, float b) { CHECK_OPT_DEFINED(name); CHECK_OPT_TYPE(name, OPTION_COLOR); hMapColor.at(name)(r, g, b); } void OptionManager::callOptionHandlerXY (const char* name, int x, int y) { CHECK_OPT_DEFINED(name); CHECK_OPT_TYPE(name, OPTION_XY); hMapXY.at(name)(x, y); } bool OptionManager::hasOptionHandler (const char* name) { return typeMap.find(name); } void OptionManager::callOptionHandler (const char* name, const char* value) { if (!typeMap.find(name)) throw Error("Property \"%s\" not defined", name); OPTION_TYPE type = typeMap.at(name); int x = 0, y = 0; float f = 0, r = 0, g = 0, b = 0; switch (type) { case OPTION_STRING: hMapString.at(name)(value); break; case OPTION_INT: if (_parseInt(value, x) < 0) throw Error("Cannot recognize \"%s\" as an integer value", value); hMapInt.at(name)(x); break; case OPTION_BOOL: if (_parseBool(value, x) < 0) throw Error("Cannot recognize \"%s\" as a boolean value", value); hMapBool.at(name)(x); break; case OPTION_FLOAT: if (_parseFloat(value, f) < 0) throw Error("Cannot recognize \"%s\" as a float value", value); hMapFloat.at(name)(f); break; case OPTION_COLOR: if (_parseColor(value, r, g, b) < 0) throw Error("Cannot recognize \"%s\" as a color value", value); hMapColor.at(name)(r, g, b); break; case OPTION_XY: if (_parseSize(value, x, y) < 0) throw Error("Cannot recognize \"%s\" as a pair of integers", value); hMapXY.at(name)(x, y); break; default: throw Error("Option type not supported"); } } int OptionManager::nOptions () const { return typeMap.size(); } int OptionManager::_parseInt (const char *str, int &val) { if (sscanf(str, "%d", &val) != 1) return -1; return 1; } int OptionManager::_parseBool (const char *str, int &val) { if (strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0) { val = 1; return 1; } else if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { val = 0; return 1; } else return _parseInt(str, val); } int OptionManager::_parseFloat (const char *str, float& val) { BufferScanner scanner(str); if (!scanner.tryReadFloat(val)) return -1; return 1; } int OptionManager::_parseColor (const char *str, float& r, float& g, float& b) { BufferScanner scanner(str); if (!scanner.tryReadFloat(r)) return -1; scanner.skipSpace(); if (scanner.isEOF()) return -1; if (scanner.readChar() != ',') return -1; scanner.skipSpace(); if (!scanner.tryReadFloat(g)) return -1; scanner.skipSpace(); if (scanner.isEOF()) return -1; if (scanner.readChar() != ',') return -1; scanner.skipSpace(); if (!scanner.tryReadFloat(b)) return -1; return 1; } int OptionManager::_parseSize (const char *str, int& w, int& h) { if (sscanf(str, "%d,%d", &w, &h) != 2) return -1; return 1; } Indigo-indigo-1.2.3/api/src/option_manager.h000066400000000000000000000100001271037650300207030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __otion_manager_h__ #define __otion_manager_h__ #include "base_cpp/red_black.h" #include "base_cpp/os_sync_wrapper.h" #include using namespace indigo; #define DECL_SET_OPT_HANDLER(suffix, ftype, type, map) \ DLLEXPORT void setOptionHandler##suffix (const char* name, ftype func); \ #define DEF_SET_OPT_HANDLER(suffix, ftype, type, map) \ void setOptionHandler##suffix (const char* name, ftype func) { \ if (typeMap.find(name)) \ throw Error("Option \"%s\" already defined", name); \ typeMap.insert(name, type); \ map.insert(name, func); \ } #define CHECK_OPT_DEFINED(name) \ if (!typeMap.find(name)) \ throw Error("Property \"%s\" not defined", name) #define CHECK_OPT_TYPE(name, type) \ if (typeMap.at(name) != type) \ throw Error("Property type mismatch", name) class OptionManager { public: typedef void (*optf_string_t) (const char* value); typedef void (*optf_int_t) (int value); typedef void (*optf_bool_t) (int value); typedef void (*optf_float_t) (float value); typedef void (*optf_color_t) (float r, float g, float b); typedef void (*optf_xy_t) (int x, int y); OptionManager (); DECL_ERROR; DEF_SET_OPT_HANDLER(String, optf_string_t, OPTION_STRING, hMapString) DEF_SET_OPT_HANDLER(Int, optf_int_t, OPTION_INT, hMapInt) DEF_SET_OPT_HANDLER(Bool, optf_bool_t, OPTION_BOOL, hMapBool) DEF_SET_OPT_HANDLER(Float, optf_float_t, OPTION_FLOAT, hMapFloat) DEF_SET_OPT_HANDLER(Color, optf_color_t, OPTION_COLOR, hMapColor) DEF_SET_OPT_HANDLER(XY, optf_xy_t, OPTION_XY, hMapXY) bool hasOptionHandler (const char* name); void callOptionHandlerInt (const char* name, int value); void callOptionHandlerBool (const char* name, int value); void callOptionHandlerFloat (const char* name, float value); void callOptionHandlerColor (const char* name, float r, float g, float b); void callOptionHandlerXY (const char* name, int x, int y); void callOptionHandler (const char* name, const char* value); int nOptions () const; OsLock lock; protected: enum OPTION_TYPE {OPTION_STRING, OPTION_INT, OPTION_BOOL, OPTION_FLOAT, OPTION_COLOR, OPTION_XY}; int _parseInt (const char *str, int &val); int _parseBool (const char *str, int &val); int _parseFloat (const char *str, float& val); int _parseColor (const char *str, float& r, float& g, float& b); int _parseSize (const char *str, int& w, int& h); RedBlackStringMap typeMap; RedBlackStringMap hMapString; RedBlackStringMap hMapInt; RedBlackStringMap hMapBool; RedBlackStringMap hMapFloat; RedBlackStringMap hMapColor; RedBlackStringMap hMapXY; template void callOptionHandlerT (const char *name, T arg) { // Convert to string for default string parsing std::stringstream ss; ss << arg; std::string converted = ss.str(); callOptionHandler(name, converted.c_str()); } private: OptionManager (const OptionManager&); }; #endif //__otion_manager_h__ Indigo-indigo-1.2.3/api/tests/000077500000000000000000000000001271037650300161145ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/tests/c/000077500000000000000000000000001271037650300163365ustar00rootroot00000000000000Indigo-indigo-1.2.3/api/tests/c/dlopen-test.c000066400000000000000000000132101271037650300207350ustar00rootroot00000000000000#include #include #ifdef _WIN32 #include #define DLOPEN(a) LoadLibrary(a) #define DLSYM(a, b) GetProcAddress(a, b) #define DLERROR "" #define DLCLOSE(a) FreeLibrary(a) #define HANDLE HMODULE #else #include #define DLOPEN(a) dlopen(a, RTLD_GLOBAL | RTLD_NOW) #define DLSYM(a, b) dlsym(a, b) #define DLERROR dlerror() #define DLCLOSE(a) dlclose(a) #define HANDLE void* #endif typedef int (*INT_RET_STR) (const char *); typedef int (*INT_RET) (); typedef const char* (*STR_RET_INT) (int); typedef const char* (*STR_RET_VOID) (void); typedef int (*INT_RET_INT_INT) (int, int); typedef int (*INT_RET_STR_STR) (const char *, const char *); typedef int (*INT_RET_STR_STR_STR) (const char *, const char *, const char *); typedef int (*INT_RET_INT) (int); /* Try to dynamically load library and check load status. */ HANDLE dlOpenWithCheck(const char *libraryPath) { HANDLE handle = DLOPEN(libraryPath); #ifdef _WIN32 /* On Windows error code is returned by LoadLibrary() and can be between 0 and 31 */ if (handle < (HANDLE)32) { printf("Error in LoadLibrary. Error code: %d\n", handle); return NULL; } #else if (!handle) { printf("Error in dlopen: %s\n", dlerror()); return NULL; } #endif return handle; } int main(int argc, char **argv) { HANDLE indigoHandle; HANDLE indigoInChIHandle; HANDLE indigoRendererHandle; HANDLE bingoHandle; STR_RET_VOID indigoVersion; INT_RET_STR indigoLoadMoleculeFromString; INT_RET_STR_STR indigoSetOption; INT_RET indigoWriteBuffer; INT_RET_INT_INT indigoRender; STR_RET_INT indigoInchiGetInchi; INT_RET_STR_STR_STR bingoCreateDatabaseFile; INT_RET_INT bingoCloseDatabase; STR_RET_VOID bingoVersion; int indigoTest = 0; int indigoInChITest = 0; int indigoRendererTest = 0; int bingoTest = 0; const char *indigoLibraryPath; const char *indigoInChILibraryPath; const char *indigoRendererLibraryPath; const char *bingoLibraryPath; int i = 0; /* Parse arguments and set variables*/ for (i = 0; i < argc; i++) { if (strstr(argv[i], "indigo.")) { indigoTest = 1; indigoLibraryPath = argv[i]; } if (strstr(argv[i], "indigo-inchi")) { indigoInChITest = 1; indigoInChILibraryPath = argv[i]; } if (strstr(argv[i], "indigo-renderer")) { indigoRendererTest = 1; indigoRendererLibraryPath = argv[i]; } if (strstr(argv[i], "bingo")) { bingoTest = 1; bingoLibraryPath = argv[i]; } } /* Tests */ if (indigoTest) { /* Load Indigo */ indigoHandle = dlOpenWithCheck(indigoLibraryPath); if (!indigoHandle) { printf("Cannot load %s\n", indigoLibraryPath); return 1; } printf("Indigo instance: %lu\n", (unsigned long)indigoHandle); /* Execute Indigo function */ indigoVersion = (STR_RET_VOID)DLSYM(indigoHandle, "indigoVersion"); printf("Indigo version: %s\n", indigoVersion()); } if (indigoInChITest) { int m; /* Load IndigoInChI */ indigoInChIHandle = dlOpenWithCheck(indigoInChILibraryPath); if (!indigoInChIHandle) { printf("Cannot load %s\n", indigoInChILibraryPath); return 1; } printf("IndigoInChI address: %lu\n", (unsigned long)indigoInChIHandle); indigoInchiGetInchi = (STR_RET_INT)DLSYM(indigoInChIHandle, "indigoInchiGetInchi"); indigoLoadMoleculeFromString = (INT_RET_STR)DLSYM(indigoHandle, "indigoLoadMoleculeFromString"); m = indigoLoadMoleculeFromString("C"); printf("indigoInChI InChI: %s\n", indigoInchiGetInchi(m)); } if (indigoRendererTest) { int m, buf, res; /* Load IndigoRenderer */ indigoRendererHandle = dlOpenWithCheck(indigoRendererLibraryPath); if (!indigoRendererHandle) { printf("Cannot load %s\n", indigoRendererLibraryPath); return 1; } printf("IndigoRenderer address: %lu\n", (unsigned long)indigoRendererHandle); indigoLoadMoleculeFromString = (INT_RET_STR)DLSYM(indigoHandle, "indigoLoadMoleculeFromString"); indigoWriteBuffer = (INT_RET)DLSYM(indigoHandle, "indigoWriteBuffer"); indigoRender = (INT_RET_INT_INT)DLSYM(indigoRendererHandle, "indigoRender"); indigoSetOption = (INT_RET_STR_STR)DLSYM(indigoHandle, "indigoSetOption"); indigoSetOption("render-output-format", "png"); m = indigoLoadMoleculeFromString("C"); buf = indigoWriteBuffer(); res = indigoRender(m, buf); printf("indigoRender result: %d\n", res); } if (bingoTest) { int db; /* Load Bingo */ bingoHandle = dlOpenWithCheck(bingoLibraryPath); if (!bingoHandle) { printf("Cannot load %s\n", bingoLibraryPath); return 1; } printf("Bingo address: %lu\n", (unsigned long)bingoHandle); bingoVersion = (STR_RET_VOID)DLSYM(bingoHandle, "bingoVersion"); printf("Bingo version: %s\n", bingoVersion()); bingoCreateDatabaseFile = (INT_RET_STR_STR_STR)DLSYM(bingoHandle, "bingoCreateDatabaseFile"); bingoCloseDatabase = (INT_RET_INT)DLSYM(bingoHandle, "bingoCloseDatabase"); db = bingoCreateDatabaseFile("test.db", "molecule", ""); printf("Bingo database ID: %d\n", db); printf("Bingo close database status: %d\n", bingoCloseDatabase(db)); } /* Close libraries */ if (bingoTest) { DLCLOSE(bingoHandle); } if (indigoRendererTest) { DLCLOSE(indigoRendererHandle); } if (indigoInChITest) { DLCLOSE(indigoInChIHandle); } if (indigoTest) { DLCLOSE(indigoHandle); } return 0; } Indigo-indigo-1.2.3/api/tests/c/indigo-test.c000066400000000000000000000024621271037650300207340ustar00rootroot00000000000000#include #include #include #include "indigo.h" void onError (const char *message, void *context) { fprintf(stderr, "Error: %s\n", message); exit(-1); } void testTransform () { int molecule = indigoLoadMoleculeFromString("[O-][C+]1CCCC1[N+]([O-])=O"); int transformation = indigoLoadReactionSmartsFromString("[*;+:1]-[*;-:2]>>[*:1]=[*:2]"); printf("Input: %s\n", indigoSmiles(molecule)); // This methods accepts a single molecule or Indigo array of molecules // and modifies the source molecule in-place. indigoTransform(transformation, molecule); printf("Output: %s\n", indigoSmiles(molecule)); indigoFree(molecule); indigoFree(transformation); } int main (void) { int m; const char *simple_mol = "CCCCCCCCCCC"; indigoSetErrorHandler(onError, 0); printf("%s\n", indigoVersion()); m = indigoLoadMoleculeFromString("COC1=CC2=C(NC(=C2)C(O)(CC2=CN=CC=C2)CC2=CN=CC=C2)C=C1"); printf("%s\n", indigoCanonicalSmiles(m)); m = indigoLoadMoleculeFromString(simple_mol); if (strcmp(indigoCanonicalSmiles(m), simple_mol) != 0) { printf("Canonical SMILES is invalid for a molecule: %s != %s\n", simple_mol, indigoCanonicalSmiles(m)); exit(-1); } testTransform(); return 0; } Indigo-indigo-1.2.3/bingo/000077500000000000000000000000001271037650300152775ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/CHANGELOG.md000066400000000000000000000127301271037650300171130ustar00rootroot00000000000000Bingo 1.7.9 ---------- *3 April 2013* General changes: * Fingerprints now include information about single-atom fragments. This resolved an issue with empty fingerprints for single-atom molecules. Also this change makes substruture search more efficient in case of rare atoms in a query. * Timeout parameter for exact search method for a single structure. Default value is 1 min. * Smiles saver doesn't throw exception about implicit hydrogens if they are not saving in SMILES Bingo PostgreSQL-specific changes: * bug with similarity search for null structures was fixed * performance was improved for join queries * bug with exact join query (windows) was fixed * bitmap scan feature was turned off by default for bingo Bingo 1.7.8b2 ---------- *15 February 2013* General changes for Bingo Oracle, SQL Server and PostgreSQL: * all the bug fixes from the Indigo branch (AAM cancellation timeout, aromatization bug fixed and others) Bingo PostgreSQL-specific changes: * import sdf bug was fixed (throw error on incorrect format) * bug with the rescan queries was fixed (join queries now work properly) * errors while binary matching were fixed * multi-threading was implemented for index building (creating index performance was improved) Bingo 1.7.7 ---------- *29 January 2013* This is a stable Bingo release for the all three databases: Oracle, MS SQL Server, and PostgreSQL. It includes all the bug fixes from the Indigo branch. New methods for Bingo Oracle, SQL Server and PostgreSQL: * `bingo.InChI(molecule, options)` method to compute InChI identifier. You can pass any options that are supported by InChI library via `options` argument. For example you can provide options in the following way: bingo.InChI(molecule, '/DoNotAddH /SUU /SLUUD') * `bingo.InChIKey(inchi)` method to compute InChI Key. To get an InChI key from a molecule with a default options use the following syntax: select bingo.InChIKey(bingo.InChI(molecule, '')) * `bingo.fingerprint(molecule, options)` and `bingo.rfingerprint(reaction, options)` methods to compute fingerprints. The options are the same as we have for the IndigoObject.fingerprint method in Indigo: * sim - "Similarity fingerprint", useful for calculating similarity measures (the default) * sub - "Substructure fingerprint", useful for substructure screening * sub-res - "Resonance substructure fingerprint", useful for resonance substructure screening * sub-tau - "Tautomer substructure fingerprint", useful for tautomer substructure screening * full - "Full fingerprint", which has all the mentioned fingerprint types included Bingo PostgreSQL-specific changes: * Postgres 9.2 support was added * Bug with cost estimation engine was fixed * `bingo.compactMolecule(molecule, xyz)` and `bingo.compactReaction(reaction, xyz)` methods were added * All utility methods now return text (not `cstring`) * All utility methods now accept binary byte parameter for input structures(returned by `bingo.compactMolecule()` and `bingo.compactReaction()`) Bingo Oracle-specific changes: * rowid is includeed into the message in case of exceptions Bingo SQL Server-specific changes: * primary keys are added to the Bingo-specific table to support replication procedure. * more details in the diagnostics log: thread id, process id, and etc. * better support for query cancellation Also we switch to a continues version numbering like we are doing with Indigo releases. Bingo 1.7 beta4 ---------- *24 April 2012* All the improvements have been merged from the Indigo branch (versions above 1.1 beta6), including: * Better handling of molecules with invalid valence. * Molecule serialization with more than 8 R-groups * incorrect empty R-Group logic loading attachment points loading from molfile was fixed The new release contains bingo-specific changes including critical bugfixes for PostgreSQL: * Bug with inserting to a table contained more than 64k molecules was fixed. * Build and search queries now support cancel requests. * Bug with possible dead lock for PostgreSQL hang queries was fixed. * Binary data storing was fixed. * Possible segmentation fault during raising exceptions was fixed. * The issue with queries contained several bingo functions was resolved. Also, bug with Oracle 11.2 on Windows 7 x64 was fixed. Bingo 1.7 beta3 ---------- *28 December 2011* All the bugfixes and improvements from the Indigo branch have been merged into the Bingo branch including: * Cis-trans issues has been fixed. * More accurate query fingerprints for SMARTS queries. Bingo-specific changes: * Support of the PostgresSQL 9.1 * Now binary files for Oracle are available for download, because we eliminated a linkage with OCI libraries. No building from source is required any more for getting Bingo for Oracle on Windows. Bingo 1.7 beta2 ---------- *30 November 2011 (no public announcement)* Bugfix release. Bingo 1.7 beta1 ---------- *29 September 2011* First Bingo version for PostgreSQL. The cartridge is covered almost all the functionality taken from the Bingo Oracle and the Bingo Sql Server parts. The Bingo library for the PostgreSQL supports index building for molecules and reactions. The algorithm for the index search includes both the basic Bingo search engine principles and the unique heuristic algorithms for the PostgreSQL database. Thus the PostgreSQL Bingo cartridge can show a great performance comparable to or greater than other Bingo implementations. Bingo 1.6.0 ---------- *17 June 2011* The stable Bingo release. Indigo-indigo-1.2.3/bingo/LICENSE.GPL000066400000000000000000001017541271037650300167350ustar00rootroot00000000000000 Bingo is Copyright (C) of EPAM Systems 2009-2011. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License below for more details. Additional permissions under GNU GPL version 3 section 7: If you modify this program, or any covered work, by linking or combining it with Oracle Database, containing parts covered by the terms of the Oracle Technology Network Developer License, the licensors of this program grant you additional permission to convey the resulting work. If you modify this program, or any covered work, by linking or combining it with a Microsoft SQL Server Standard Edition or Microsoft SQL Server Express Edition, containing parts covered by the terms of the appropriate Microsoft SQL Server End User License Agreement, the licensors of this program grant you additional permission to convey the resulting work. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS Indigo-indigo-1.2.3/bingo/README-BUILD.txt000066400000000000000000000025671271037650300176440ustar00rootroot000000000000001. Copy the OCI files On all systems: Copy oci.h, ociextp.h, and other header files from your Oracle installation (usually located in $ORACLE_HOME/rdbms/public/ and/or $ORACLE_HOME/oci/include/ and/or $ORACLE_HOME/rdbms/demo/) into ../common/oci/include directory. On Solaris, copy them to ../common/oci/include.sun directory On 32-bit Windows: Copy oci.lib from your Oracle installation (usually located in %ORACLE_HOME%\oci\lib\msvc\) into ../common/oci/lib32/oci32.lib (note that you should change the file name) On 64-bit Windows: Copy oci.lib from your Oracle installation (usually located in %ORACLE_HOME%\oci\lib\msvc\) into ../common/oci/lib64/oci64.lib (note that you should change the file name) On Mac OS X: Copy liborasdk.dylib from your Oracle installation (usually located in %ORACLE_HOME%\lib) into ../common/oci/lib64/liborasdk.dylib 2. Build Note: you should have the "zip" command-line utility installed on your system. On Linux, run ./all-release-linux.sh $version On Windows, run all-release-windows.bat $version On Mac OS X, run ./all-release-osx.sh $version On Solaris, run: ./all-release-sun.sh $version You will get a number of .zip archives for your selected platform, both 32-bit and 64-bit versions. (In case of Mac OS X, both 10.5 and 10.6 versions.) Indigo-indigo-1.2.3/bingo/bingo-core-c/000077500000000000000000000000001271037650300175435ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/bingo-core-c/CMakeLists.txt000066400000000000000000000033551271037650300223110ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(BingoCoreC) file(GLOB BingoCoreC_src src/*.cpp) file(GLOB BingoCoreC_headers src/*.h) set(BingoCoreC_HEADERS_DIR ${BingoCoreC_SOURCE_DIR}/src CACHE INTERNAL "include directories" FORCE) include(ConfigureCommon) include_directories(../src ../src/core ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${ZLib_HEADERS_DIR} ${BingoCore_HEADERS_DIR} ${Common_SOURCE_DIR}/../api/plugins/inchi/src ${InChI_SOURCE_DIR}/inchi_dll) add_library(bingo-core-c STATIC ${BingoCoreC_src} ${BingoCoreC_headers}) target_link_libraries(bingo-core-c bingo-core inchi) if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lc++") endif() set_target_properties(bingo-core-c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET bingo-core-c PROPERTY FOLDER "bingo-core-c") set_property(TARGET bingo-core-c PROPERTY OUTPUT_NAME "bingo-core-c-static") if(BINGO_SQLSERVER) add_library(bingo-core-c-shared SHARED ${BingoCoreC_src} ${BingoCoreC_headers} ${Common_SOURCE_DIR}/hacks/memcpy.c $ $ $ $ $) target_link_libraries(bingo-core-c-shared bingo-core inchi tinyxml z) if (APPLE) set_target_properties(bingo-core-c-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -lc++") endif() if (UNIX AND NOT APPLE) if(${SUBSYSTEM_NAME} MATCHES "x64") set_target_properties(bingo-core-c-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() endif() set_target_properties(bingo-core-c-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET bingo-core-c-shared PROPERTY FOLDER "bingo-core-c-shared") set_property(TARGET bingo-core-c-shared PROPERTY OUTPUT_NAME "bingo-core-c") endif() Indigo-indigo-1.2.3/bingo/bingo-core-c/src/000077500000000000000000000000001271037650300203325ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/bingo-core-c/src/bingo_core_c.cpp000066400000000000000000000527721271037650300234630ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "base_cpp/profiling.h" #include "gzip/gzip_scanner.h" using namespace indigo::bingo_core; BingoCore::BingoCore () { bingo_context = 0; mango_context = 0; ringo_context = 0; reset(); } void BingoCore::reset () { if (bingo_context != 0) { int id = bingo_context->id; bingo_context->reset(); } mango_search_type = _UNDEF; mango_search_type_non = false; bingo_context = 0; mango_context = 0; ringo_context = 0; error_handler = 0; error_handler_context = 0; skip_calculate_fp = false; smiles_scanner = 0; // Clear warning and error message warning.clear(); warning.push(0); error.clear(); error.push(0); } TL_DECL(BingoCore, self); BingoCore& BingoCore::getInstance () { TL_GET(BingoCore, self); return self; } CEXPORT const char * bingoGetVersion () { return BINGO_VERSION; } CEXPORT const char * bingoGetError () { BINGO_BEGIN { return self.error.ptr(); } BINGO_END("", "") } CEXPORT const char * bingoGetWarning () { BINGO_BEGIN { return self.warning.ptr(); } BINGO_END("", "") } CEXPORT qword bingoAllocateSessionID () { qword id = TL_ALLOC_SESSION_ID(); TL_GET_BY_ID(BingoCore, self, id); self.reset(); return id; } CEXPORT void bingoReleaseSessionID (qword session_id) { return TL_RELEASE_SESSION_ID(session_id); } CEXPORT void bingoSetSessionID (qword session_id) { TL_SET_SESSION_ID(session_id); } CEXPORT qword bingoGetSessionID () { return TL_GET_SESSION_ID(); } CEXPORT void bingoSetErrorHandler (BINGO_ERROR_HANDLER handler, void *context) { BingoCore &self = BingoCore::getInstance(); self.error_handler = handler; self.error_handler_context = context; } CEXPORT int bingoSetContext (int id) { BINGO_BEGIN { self.bingo_context = BingoContext::get(id); self.mango_context = MangoContext::get(id); self.ringo_context = RingoContext::get(id); } BINGO_END(1, 0); } CEXPORT int bingoSetConfigInt (const char *name, int value) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (strcasecmp(name, "treat-x-as-pseudoatom") == 0 || strcasecmp(name, "treat_x_as_pseudoatom") == 0) self.bingo_context->treat_x_as_pseudoatom = (value != 0); else if (strcasecmp(name, "ignore-closing-bond-direction-mismatch") == 0 || strcasecmp(name, "ignore_closing_bond_direction_mismatch") == 0) self.bingo_context->ignore_closing_bond_direction_mismatch = (value != 0); else if (strcasecmp(name, "nthreads") == 0) self.bingo_context->nthreads = value; else if (strcasecmp(name, "timeout") == 0) self.bingo_context->timeout = value; else if (strcasecmp(name, "ignore-cistrans-errors") == 0 || strcasecmp(name, "ignore_cistrans_errors") == 0) self.bingo_context->ignore_cistrans_errors = (value != 0); else if (strcasecmp(name, "ignore-stereocenter-errors") == 0 || strcasecmp(name, "ignore_stereocenter_errors") == 0) self.bingo_context->ignore_stereocenter_errors = (value != 0); else if (strcasecmp(name, "stereochemistry-bidirectional-mode") == 0 || strcasecmp(name, "stereochemistry_bidirectional_mode") == 0) self.bingo_context->stereochemistry_bidirectional_mode = (value != 0); else if (strcasecmp(name, "stereochemistry-detect-haworth-projection") == 0 || strcasecmp(name, "stereochemistry_detect_haworth_projection") == 0) self.bingo_context->stereochemistry_detect_haworth_projection = (value != 0); else if (strcasecmp(name, "allow-non-unique-dearomatization") == 0 || strcasecmp(name, "allow_non_unique_dearomatization") == 0) self.bingo_context->allow_non_unique_dearomatization = (value != 0); else if (strcasecmp(name, "zero-unknown-aromatic-hydrogens") == 0 || strcasecmp(name, "zero_unknown_aromatic_hydrogens") == 0) self.bingo_context->zero_unknown_aromatic_hydrogens = (value != 0); else if (strcasecmp(name, "reject-invalid-structures") == 0 || strcasecmp(name, "reject_invalid_structures") == 0) self.bingo_context->reject_invalid_structures = (value != 0); else { bool set = true; if (strcasecmp(name, "FP_ORD_SIZE") == 0) self.bingo_context->fp_parameters.ord_qwords = value; else if (strcasecmp(name, "FP_ANY_SIZE") == 0) self.bingo_context->fp_parameters.any_qwords = value; else if (strcasecmp(name, "FP_TAU_SIZE") == 0) self.bingo_context->fp_parameters.tau_qwords = value; else if (strcasecmp(name, "FP_SIM_SIZE") == 0) self.bingo_context->fp_parameters.sim_qwords = value; else if (strcasecmp(name, "SUB_SCREENING_MAX_BITS") == 0) self.sub_screening_max_bits = value; else if (strcasecmp(name, "SIM_SCREENING_PASS_MARK") == 0) self.sim_screening_pass_mark = value; else set = false; if (set) { self.bingo_context->fp_parameters.ext = true; self.bingo_context->fp_parameters_ready = true; } if (!set) throw BingoError("Unknown parameter name: '%s'", name); } } BINGO_END(1, 0); } CEXPORT int bingoGetConfigInt (const char *name, int *value) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (strcasecmp(name, "treat-x-as-pseudoatom") == 0 || strcasecmp(name, "treat_x_as_pseudoatom") == 0) *value = (int)self.bingo_context->treat_x_as_pseudoatom; else if (strcasecmp(name, "ignore-closing-bond-direction-mismatch") == 0 || strcasecmp(name, "ignore_closing_bond_direction_mismatch") == 0) *value = (int) self.bingo_context->ignore_closing_bond_direction_mismatch; else if (strcasecmp(name, "fp-size-bytes") == 0 || strcasecmp(name, "fp_size_bytes") == 0) *value = self.bingo_context->fp_parameters.fingerprintSize(); else if (strcasecmp(name, "reaction-fp-size-bytes") == 0 || strcasecmp(name, "reaction_fp_size_bytes") == 0) *value = self.bingo_context->fp_parameters.fingerprintSizeExtOrd() * 2; else if (strcasecmp(name, "SUB_SCREENING_MAX_BITS") == 0) *value = self.sub_screening_max_bits; else if (strcasecmp(name, "SIM_SCREENING_PASS_MARK") == 0) *value = self.sim_screening_pass_mark; else if (strcasecmp(name, "nthreads") == 0) *value = self.bingo_context->nthreads; else if (strcasecmp(name, "timeout") == 0) *value = self.bingo_context->timeout; else if (strcasecmp(name, "ignore-cistrans-errors") == 0 || strcasecmp(name, "ignore_cistrans_errors") == 0) *value = (int)self.bingo_context->ignore_cistrans_errors; else if (strcasecmp(name, "ignore-stereocenter-errors") == 0 || strcasecmp(name, "ignore_stereocenter_errors") == 0) *value = (int)self.bingo_context->ignore_stereocenter_errors; else if (strcasecmp(name, "stereochemistry-bidirectional-mode") == 0 || strcasecmp(name, "stereochemistry_bidirectional_mode") == 0) *value = (int) self.bingo_context->stereochemistry_bidirectional_mode; else if (strcasecmp(name, "stereochemistry-detect-haworth-projection") == 0 || strcasecmp(name, "stereochemistry_detect_haworth_projection") == 0) *value = (int) self.bingo_context->stereochemistry_detect_haworth_projection; else if (strcasecmp(name, "allow-non-unique-dearomatization") == 0 || strcasecmp(name, "allow_non_unique_dearomatization") == 0) *value = (int)self.bingo_context->allow_non_unique_dearomatization; else if (strcasecmp(name, "zero-unknown-aromatic-hydrogens") == 0 || strcasecmp(name, "zero_unknown_aromatic_hydrogens") == 0) *value = (int)self.bingo_context->zero_unknown_aromatic_hydrogens; else if (strcasecmp(name, "reject-invalid-structures") == 0 || strcasecmp(name, "reject_invalid_structures") == 0) *value = (int) self.bingo_context->reject_invalid_structures; else throw BingoError("unknown parameter name: %s", name); } BINGO_END(1, 0); } CEXPORT int bingoGetConfigBin (const char *name, const char **value, int *len) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (strcasecmp(name, "cmf-dict") == 0 || strcasecmp(name, "cmf_dict") == 0) { ArrayOutput output(self.buffer); self.bingo_context->cmf_dict.save(output); *value = self.buffer.ptr(); *len = self.buffer.size(); } else throw BingoError("unknown parameter name: %s", name); } BINGO_END(1, 0); } CEXPORT int bingoSetConfigBin (const char *name, const char *value, int len) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (strcasecmp(name, "cmf-dict") == 0 || strcasecmp(name, "cmf_dict") == 0) { BufferScanner scanner(value, len); self.bingo_context->cmf_dict.load(scanner); } else throw BingoError("unknown parameter name: %s", name); } BINGO_END(1, 0); } CEXPORT int bingoClearTautomerRules () { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); self.bingo_context->tautomer_rules.clear(); self.bingo_context->tautomer_rules_ready = false; } BINGO_END(1, 0); } CEXPORT int bingoAddTautomerRule (int n, const char *beg, const char *end) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (n < 1 || n >= 32) throw BingoError("tautomer rule index %d is out of range", n); AutoPtr rule(new TautomerRule()); bingoGetTauCondition(beg, rule->aromaticity1, rule->list1); bingoGetTauCondition(end, rule->aromaticity2, rule->list2); self.bingo_context->tautomer_rules.expand(n); self.bingo_context->tautomer_rules.reset(n - 1); self.bingo_context->tautomer_rules.set(n - 1, rule.release()); } BINGO_END(1, 0); } CEXPORT int bingoTautomerRulesReady (int n, const char *beg, const char *end) { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); self.bingo_context->tautomer_rules_ready = true; } BINGO_END(1, 0); } CEXPORT int bingoImportParseFieldList(const char *fields_str) { BINGO_BEGIN { QS_DEF(Array, prop); QS_DEF(Array, column); BufferScanner scanner(fields_str); self.import_properties.free(); self.import_columns.free(); self.import_properties.create(); self.import_columns.create(); scanner.skipSpace(); while (!scanner.isEOF()) { scanner.readWord(prop, " ,"); scanner.skipSpace(); scanner.readWord(column, " ,"); scanner.skipSpace(); self.import_properties.ref().add(prop.ptr()); self.import_columns.ref().add(column.ptr()); if (scanner.isEOF()) break; if (scanner.readChar() != ',') throw BingoError("importParseFieldList(): comma expected"); scanner.skipSpace(); } return self.import_properties.ref().size(); } BINGO_END(0, -1); } CEXPORT const char* bingoImportGetColumnName(int idx) { BINGO_BEGIN { if (self.import_columns.get() == 0) throw BingoError("bingo import list has not been parsed yet"); return self.import_columns.ref().at(idx); } BINGO_END("", "") } CEXPORT const char* bingoImportGetPropertyName(int idx) { BINGO_BEGIN { if (self.import_properties.get() == 0) throw BingoError("bingo import list has not been parsed yet"); return self.import_properties.ref().at(idx); } BINGO_END("", "") } /* * Get value by parsed field list */ CEXPORT const char * bingoImportGetPropertyValue (int idx) { BINGO_BEGIN { if (self.import_properties.get() == 0) throw BingoError("bingo import list has not been parsed yet"); const char* property_name = self.import_properties.ref().at(idx); if(self.sdf_loader.get()) { return self.sdf_loader->properties.at(property_name); } else if(self.rdf_loader.get()) { return self.rdf_loader->properties.at(property_name); } else { throw BingoError("bingo import has not been initialized yet"); } } BINGO_END("", 0) } CEXPORT int bingoSDFImportOpen (const char *file_name) { BINGO_BEGIN { bingoSDFImportClose(); self.file_scanner.create(file_name); self.sdf_loader.create(self.file_scanner.ref()); return 1; } BINGO_END(-1, -1) } CEXPORT int bingoSDFImportClose () { BINGO_BEGIN { self.sdf_loader.free(); self.file_scanner.free(); } BINGO_END(0, -1); } CEXPORT int bingoSDFImportEOF () { BINGO_BEGIN { return self.sdf_loader->isEOF() ? 1 : 0; } BINGO_END(0, -1) } CEXPORT const char * bingoSDFImportGetNext () { BINGO_BEGIN { profTimerStart(t, "sdf_loader.readNext"); self.sdf_loader->readNext(); self.sdf_loader->data.push(0); return self.sdf_loader->data.ptr(); } BINGO_END("", 0) } CEXPORT const char * bingoSDFImportGetProperty (const char *param_name) { BINGO_BEGIN { return self.sdf_loader->properties.at(param_name); } BINGO_END("", 0) } CEXPORT int bingoRDFImportOpen (const char *file_name) { BINGO_BEGIN { bingoRDFImportClose(); self.file_scanner.create(file_name); self.rdf_loader.create(self.file_scanner.ref()); return 1; } BINGO_END(-1, -1) } CEXPORT int bingoRDFImportClose () { BINGO_BEGIN { self.rdf_loader.free(); self.file_scanner.free(); } BINGO_END(0, -1); } CEXPORT int bingoRDFImportEOF () { BINGO_BEGIN { return self.rdf_loader->isEOF() ? 1 : 0; } BINGO_END(0, -1) } CEXPORT const char * bingoRDFImportGetNext () { BINGO_BEGIN { self.rdf_loader->readNext(); self.rdf_loader->data.push(0); return self.rdf_loader->data.ptr(); } BINGO_END("", 0) } CEXPORT const char * bingoRDFImportGetProperty (const char *param_name) { BINGO_BEGIN { return self.rdf_loader->properties.at(param_name); } BINGO_END("", 0) } CEXPORT void bingoProfilingReset (byte reset_whole_session) { ProfilingSystem::getInstance().reset(reset_whole_session != 0); } CEXPORT const char* bingoProfilingGetStatistics (bool for_session) { BINGO_BEGIN { ArrayOutput output(self.buffer); profGetStatistics(output, for_session); output.writeByte(0); return self.buffer.ptr(); } BINGO_END("", "") } CEXPORT float bingoProfilingGetTime (const char *counter_name, byte for_session) { BINGO_BEGIN { return ProfilingSystem::getInstance().getLabelExecTime(counter_name, for_session != 0); } BINGO_END(-1, -1) } CEXPORT qword bingoProfilingGetValue (const char *counter_name, byte for_session) { BINGO_BEGIN { return ProfilingSystem::getInstance().getLabelValue(counter_name, for_session != 0); } BINGO_END(-1, -1) } CEXPORT qword bingoProfilingGetCount (const char *counter_name, byte for_session) { BINGO_BEGIN { return ProfilingSystem::getInstance().getLabelCallCount(counter_name, for_session != 0); } BINGO_END(-1, -1) } #include CEXPORT int bingoCheckMemoryAllocate (int size) { BINGO_BEGIN { try { self.test_ptr = 0; self.test_ptr = (byte*)malloc(size); if (self.test_ptr == 0) { self.error.readString("self.test_ptr == 0", true); return -1; } for (int i = 0; i < size; i++) self.test_ptr[i] = i; return 1; } catch (std::exception &ex) { self.error.readString(ex.what(), true); return -1; } } BINGO_END(-1, -1) } CEXPORT int bingoCheckMemoryFree () { BINGO_BEGIN { if (self.test_ptr != 0) { free(self.test_ptr); } self.test_ptr = 0; return 1; } BINGO_END(-1, -1) } CEXPORT qword bingoProfNanoClock () { return nanoClock(); } CEXPORT void bingoProfIncTimer (const char *name, qword dt) { ProfilingSystem &inst = ProfilingSystem::getInstance(); int name_index = inst.getNameIndex(name); inst.addTimer(name_index, dt); } CEXPORT void bingoProfIncCounter (const char *name, int dv) { ProfilingSystem &inst = ProfilingSystem::getInstance(); int name_index = inst.getNameIndex(name); inst.addCounter(name_index, dv); } CEXPORT const char * bingoGetNameCore (const char *target_buf, int target_buf_len) { BINGO_BEGIN { QS_DEF(Array, source); QS_DEF(Array, name); BufferScanner scanner(target_buf, target_buf_len); bingoGetName(scanner, self.buffer); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT int bingoIndexMarkTermintate () { BINGO_BEGIN { if (self.parallel_indexing_dispatcher.get()) self.parallel_indexing_dispatcher->markToTerminate(); return 1; } BINGO_END(-2, -2) } static void _bingoIndexEnd (BingoCore &self) { if (self.parallel_indexing_dispatcher.get()) { self.parallel_indexing_dispatcher->terminate(); self.parallel_indexing_dispatcher.reset(0); } if (self.single_mango_index.get()) self.single_mango_index.free(); if (self.single_ringo_index.get()) self.single_ringo_index.free(); self.mango_index = 0; self.ringo_index = 0; self.index_record_data_id = -1; self.index_record_data.free(); } CEXPORT int bingoIndexEnd () { BINGO_BEGIN { _bingoIndexEnd(self); return 1; } BINGO_END(-2, -2) } CEXPORT int bingoIndexBegin () { BINGO_BEGIN { if (!self.bingo_context->fp_parameters_ready) throw BingoError("fingerprint parameters not set"); _bingoIndexEnd(self); self.index_record_data.create(); return 1; } BINGO_END(-2, -2) } CEXPORT int bingoIndexSetSkipFP (bool skip) { BINGO_BEGIN { self.skip_calculate_fp = skip; return 1; } BINGO_END(-2, -2) } CEXPORT int bingoSMILESImportOpen (const char *file_name) { BINGO_BEGIN { self.file_scanner.free(); self.file_scanner.create(file_name); // detect if input is gzipped byte magic[2]; int pos = self.file_scanner->tell(); self.file_scanner->readCharsFix(2, (char *)magic); self.file_scanner->seek(pos, SEEK_SET); if (magic[0] == 0x1f && magic[1] == 0x8b) { self.gz_scanner.reset(new GZipScanner(self.file_scanner.ref())); self.smiles_scanner = self.gz_scanner.get(); } else self.smiles_scanner = self.file_scanner.get(); return 1; } BINGO_END(-2, -2) } CEXPORT int bingoSMILESImportClose () { BINGO_BEGIN { self.gz_scanner.reset(0); self.file_scanner.free(); self.smiles_scanner = 0; } BINGO_END(0, -1) } CEXPORT int bingoSMILESImportEOF () { BINGO_BEGIN { if (self.smiles_scanner == 0) throw BingoError("SMILES import wasn't initialized"); return self.smiles_scanner->isEOF() ? 1 : 0; } BINGO_END(-2, -2) } CEXPORT const char * bingoSMILESImportGetNext () { BINGO_BEGIN { if (self.smiles_scanner == 0) throw BingoError("SMILES import wasn't initialized"); // TODO: Name should be also extracted here... self.smiles_scanner->readLine(self.buffer, true); return self.buffer.ptr(); } BINGO_END("", 0) } CEXPORT const char * bingoSMILESImportGetId () { BINGO_BEGIN { if (self.smiles_scanner == 0) throw BingoError("SMILES import wasn't initialized"); /* * Extract id name by skipping | symbols */ BufferScanner strscan(self.buffer.ptr()); strscan.skipSpace(); while (!strscan.isEOF() && !isspace(strscan.readChar())); strscan.skipSpace(); if (strscan.lookNext() == '|') { strscan.readChar(); while (!strscan.isEOF() && strscan.readChar() != '|'); strscan.skipSpace(); } if (strscan.isEOF()) return 0; else return (const char*)strscan.curptr(); } BINGO_END("", 0) } Indigo-indigo-1.2.3/bingo/bingo-core-c/src/bingo_core_c.h000066400000000000000000000221171271037650300231160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_core_c_h___ #define __bingo_core_c_h___ #include "base_c/defs.h" /* * Bingo common core functions */ CEXPORT const char * bingoGetVersion (); CEXPORT const char * bingoGetError (); CEXPORT const char * bingoGetWarning (); CEXPORT qword bingoAllocateSessionID (); CEXPORT void bingoReleaseSessionID (qword session_id); CEXPORT void bingoSetSessionID (qword session_id); CEXPORT qword bingoGetSessionID (); typedef void (*BINGO_ERROR_HANDLER)(const char *message, void *context); CEXPORT void bingoSetErrorHandler (BINGO_ERROR_HANDLER handler, void *context); CEXPORT int bingoSetContext (int id); CEXPORT int bingoSetConfigInt (const char *name, int value); CEXPORT int bingoGetConfigInt (const char *name, int *value); CEXPORT int bingoGetConfigBin (const char *name, const char **value, int *len); CEXPORT int bingoSetConfigBin (const char *name, const char *value, int len); CEXPORT int bingoClearTautomerRules (); CEXPORT int bingoAddTautomerRule (int n, const char *beg, const char *end); CEXPORT int bingoTautomerRulesReady (int n, const char *beg, const char *end); /* * Returns number of parsed field mappings */ CEXPORT int bingoImportParseFieldList(const char *fields_str); CEXPORT const char* bingoImportGetColumnName(int idx); CEXPORT const char* bingoImportGetPropertyName(int idx); /* * Get value by parsed field list */ CEXPORT const char * bingoImportGetPropertyValue (int idx); /* * SDF import */ CEXPORT int bingoSDFImportOpen (const char *file_name); CEXPORT int bingoSDFImportClose (); CEXPORT int bingoSDFImportEOF (); CEXPORT const char * bingoSDFImportGetNext (); CEXPORT const char * bingoSDFImportGetProperty (const char *param_name); /* * RDF import */ CEXPORT int bingoRDFImportOpen (const char *file_name); CEXPORT int bingoRDFImportClose (); CEXPORT int bingoRDFImportEOF (); CEXPORT const char * bingoRDFImportGetNext (); CEXPORT const char * bingoRDFImportGetProperty (const char *param_name); /* * SMILES import */ CEXPORT int bingoSMILESImportOpen (const char *file_name); CEXPORT int bingoSMILESImportClose (); CEXPORT int bingoSMILESImportEOF (); CEXPORT const char * bingoSMILESImportGetNext (); CEXPORT const char * bingoSMILESImportGetId (); CEXPORT void bingoProfilingReset (byte reset_whole_session); CEXPORT const char* bingoProfilingGetStatistics (bool for_session); CEXPORT float bingoProfilingGetTime (const char *counter_name, byte for_session); CEXPORT qword bingoProfilingGetValue (const char *counter_name, byte for_session); CEXPORT qword bingoProfilingGetCount (const char *counter_name, byte for_session); CEXPORT int bingoCheckMemoryAllocate (int size); CEXPORT int bingoCheckMemoryFree (); CEXPORT qword bingoProfNanoClock (); CEXPORT void bingoProfIncTimer (const char *name, qword dt); CEXPORT void bingoProfIncCounter (const char *name, int dv); CEXPORT const char * bingoGetNameCore (const char *target_buf, int target_buf_len); CEXPORT int bingoSetIndexRecordData (int id, const char *data, int data_size); CEXPORT int bingoIndexEnd (); CEXPORT int bingoIndexBegin (); CEXPORT int bingoIndexMarkTermintate (); CEXPORT int bingoIndexProcess (bool is_reaction, int (*get_next_record_cb) (void *context), void (*process_result_cb) (void *context), void (*process_error_cb) (int id, void *context), void *context ); /* * Mango core interface */ CEXPORT int mangoIndexProcessSingleRecord (); CEXPORT int mangoIndexReadPreparedMolecule (int *id, const char **cmf_buf, int *cmf_buf_len, const char **xyz_buf, int *xyz_buf_len, const char **gross_str, const char **counter_elements_str, const char **fingerprint_buf, int *fingerprint_buf_len, const char **fingerprint_sim_str, float *mass, int *sim_fp_bits_count); CEXPORT int mangoGetHash (bool for_index, int index, int *count, dword *hash); CEXPORT int mangoGetAtomCount (const char *target_buf, int target_buf_len); CEXPORT int mangoGetBondCount (const char *target_buf, int target_buf_len); CEXPORT int mangoSetupMatch (const char *search_type, const char *query, const char *options); CEXPORT int mangoSimilarityGetBitMinMaxBoundsArray (int count, int* target_ones, int **min_bound_ptr, int **max_bound_ptr); CEXPORT int mangoSimilarityGetScore (float *score); CEXPORT int mangoSimilaritySetMinMaxBounds (float min_bound, float max_bound); // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int mangoMatchTarget (const char *target, int target_buf_len); // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int mangoMatchTargetBinary (const char *target_bin, int target_bin_len, const char *target_xyz, int target_xyz_len); CEXPORT int mangoLoadTargetBinaryXyz (const char *target_xyz, int target_xyz_len); CEXPORT int mangoSetHightlightingMode (int enable); CEXPORT const char* mangoGetHightlightedMolecule (); CEXPORT const char * mangoSMILES (const char *target_buf, int target_buf_len, int canonical); CEXPORT const char * mangoMolfile (const char *molecule, int molecule_len); CEXPORT const char * mangoCML (const char *molecule, int molecule_len); CEXPORT int mangoGetQueryFingerprint (const char **query_fp, int *query_fp_len); CEXPORT const char* mangoGetCountedElementName (int index); CEXPORT int mangoNeedCoords (); CEXPORT byte mangoExactNeedComponentMatching (); CEXPORT const char * mangoTauGetQueryGross (); CEXPORT int mangoMass (const char *target_buf, int target_buf_len, const char *type, float *out); CEXPORT const char* mangoGross (const char *target_buf, int target_buf_len); CEXPORT const char* mangoGrossGetConditions (); CEXPORT const char * mangoCheckMolecule (const char *molecule, int molecule_len); CEXPORT const char* mangoICM (const char* molecule, int molecule_len, bool save_xyz, int *out_len); CEXPORT const char* mangoFingerprint (const char* molecule, int molecule_len, const char* options, int *out_len); CEXPORT const char* mangoInChI(const char* molecule, int molecule_len, const char* options, int *out_len); CEXPORT const char* mangoInChIKey(const char* inchi); CEXPORT int mangoIndexProcess ( int (*get_next_record_cb) (void *context), void (*process_result_cb) (void *context), void (*process_error_cb) (int id, void *context), void *context ); /* * Ringo core interface */ CEXPORT int ringoIndexProcessSingleRecord (); CEXPORT int ringoIndexReadPreparedReaction (int *id, const char **crf_buf, int *crf_buf_len, const char **fingerprint_buf, int *fingerprint_buf_len); CEXPORT int ringoSetupMatch (const char *search_type, const char *query, const char *options); // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int ringoMatchTarget (const char *target, int target_buf_len); // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int ringoMatchTargetBinary (const char *target_bin, int target_bin_len); CEXPORT const char * ringoRSMILES (const char *target_buf, int target_buf_len); CEXPORT const char * ringoRxnfile (const char *reaction, int reaction_len); CEXPORT const char * ringoRCML (const char *reaction, int reaction_len); CEXPORT const char * ringoAAM (const char *reaction, int reaction_len, const char *mode); CEXPORT const char * ringoCheckReaction (const char *reaction, int reaction_len); CEXPORT int ringoGetQueryFingerprint (const char **query_fp, int *query_fp_len); CEXPORT int ringoSetHightlightingMode (int enable); CEXPORT const char* ringoGetHightlightedReaction (); CEXPORT const char* ringoICR (const char* reaction, int reaction_len, bool save_xyz, int *out_len); CEXPORT int ringoGetHash (bool for_index, dword *hash); CEXPORT const char* ringoFingerprint (const char* reaction, int reaction_len, const char* options, int *out_len); #endif // __bingo_core_c_h___ Indigo-indigo-1.2.3/bingo/bingo-core-c/src/bingo_core_c_internal.h000066400000000000000000000105611271037650300250120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_core_c_internal_h___ #define __bingo_core_c_internal_h___ #include "base_c/defs.h" #include "base_cpp/auto_ptr.h" #include "core/bingo_context.h" #include "core/mango_context.h" #include "core/ringo_context.h" #include "core/mango_matchers.h" #include "core/bingo_error.h" #include "base_cpp/scanner.h" #include "base_cpp/auto_ptr.h" #include "molecule/smiles_loader.h" #include "molecule/smiles_loader.h" #include "molecule/elements.h" #include "molecule/sdf_loader.h" #include "molecule/rdf_loader.h" #include "molecule/cmf_saver.h" #include "molecule/molfile_loader.h" #include "molecule/molecule_auto_loader.h" #include "reaction/rsmiles_loader.h" #include "reaction/reaction.h" #include "reaction/rxnfile_loader.h" #include "reaction/reaction_auto_loader.h" #include "gzip/gzip_scanner.h" #include "bingo_core_c.h" #include "bingo_core_c_parallel.h" namespace indigo { namespace bingo_core { class BingoCore { public: BingoCore (); void reset (); static BingoCore& getInstance (); Array error; Array warning; BINGO_ERROR_HANDLER error_handler; void *error_handler_context; BingoContext * bingo_context; MangoContext * mango_context; RingoContext * ringo_context; Obj import_properties; Obj import_columns; Obj file_scanner; Obj sdf_loader; Obj rdf_loader; AutoPtr gz_scanner; Scanner *smiles_scanner; Array buffer; MangoIndex *mango_index; RingoIndex *ringo_index; Obj< Array > index_record_data; int index_record_data_id; Obj single_mango_index; Obj single_ringo_index; AutoPtr parallel_indexing_dispatcher; bool skip_calculate_fp; enum { _UNDEF, _SUBSTRUCTRE, _TAUTOMER, _EXACT, _SIMILARITY, _GROSS } mango_search_type, ringo_search_type; bool mango_search_type_non; int sub_screening_max_bits, sim_screening_pass_mark; byte *test_ptr; }; #define BINGO_BEGIN { BingoCore &self = BingoCore::getInstance(); try { self.error.clear(); #define BINGO_END(success, fail) } catch (Exception &ex) \ { self.error.readString(ex.message(), true); \ if (self.error_handler != 0) \ self.error_handler(ex.message(), \ self.error_handler_context); \ return fail; } \ return success; } #define TRY_READ_TARGET_MOL \ try { #define CATCH_READ_TARGET_MOL(action) \ } \ catch (Scanner::Error &e) { action;} \ catch (MolfileLoader::Error &e) { action;} \ catch (Element::Error &e) { action;} \ catch (Graph::Error &e) { action;} \ catch (MoleculeStereocenters::Error &e) { action;} \ catch (MoleculeCisTrans::Error &e) { action;} \ catch (SmilesLoader::Error &e) { action;} \ catch (Molecule::Error &e) { action;} \ catch (MoleculeAutoLoader::Error &e) { action;} \ catch (EmbeddingEnumerator::TimeoutException &e) { action;} \ catch (DearomatizationsGroups::Error&e) {action;} //catch (IcmLoader::Error &e) { action;} \ #define TRY_READ_TARGET_RXN \ try { #define CATCH_READ_TARGET_RXN(action) \ } \ catch (Scanner::Error &e) { action;} \ catch (MolfileLoader::Error &e) { action;} \ catch (RxnfileLoader::Error &e) { action;} \ catch (Element::Error &e) { action;} \ catch (Graph::Error &e) { action;} \ catch (MoleculeStereocenters::Error &e) { action;} \ catch (MoleculeCisTrans::Error &e) { action;} \ catch (SmilesLoader::Error &e) { action;} \ catch (RSmilesLoader::Error &e) { action;} \ catch (Molecule::Error &e) { action;} \ catch (Reaction::Error &e) { action;} \ catch (ReactionAutoLoader::Error &e) { action;} } } #endif // __bingo_core_c_h___ Indigo-indigo-1.2.3/bingo/bingo-core-c/src/bingo_core_c_parallel.cpp000066400000000000000000000140761271037650300253320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "bingo_core_c_parallel.h" #include "mango_core_c_parallel.h" #include "ringo_core_c_parallel.h" #include "base_cpp/profiling.h" #include "molecule/cmf_saver.h" #include "reaction/crf_saver.h" using namespace indigo::bingo_core; CEXPORT int bingoSetIndexRecordData (int id, const char *data, int data_size) { BINGO_BEGIN { self.index_record_data->copy(data, data_size); self.index_record_data_id = id; } BINGO_END(0, -1) } // Method for index molecules CEXPORT int bingoIndexProcess (bool is_reaction, int (*get_next_record_cb) (void *context), void (*process_result_cb) (void *context), void (*process_error_cb) (int id, void *context), void *context ) { BINGO_BEGIN { if(self.parallel_indexing_dispatcher.get() == 0) { if (is_reaction) self.parallel_indexing_dispatcher.reset(new RingoIndexingDispatcher(self)); else self.parallel_indexing_dispatcher.reset(new MangoIndexingDispatcher(self)); } self.parallel_indexing_dispatcher->context = context; self.parallel_indexing_dispatcher->get_next_record_cb = get_next_record_cb; self.parallel_indexing_dispatcher->process_result_cb = process_result_cb; self.parallel_indexing_dispatcher->process_error_cb = process_error_cb; self.parallel_indexing_dispatcher->_finished = false; self.parallel_indexing_dispatcher->run(self.bingo_context->nthreads); // self.parallel_indexing_dispatcher.reset(0); } BINGO_END(0, -1) } // // IndexingDispatcher // IndexingDispatcher::IndexingDispatcher (BingoCore &core, int method, bool set_parent_SID_for_threads, int records_per_command) : _core(core), OsCommandDispatcher(method, set_parent_SID_for_threads) { _finished = false; _records_per_command = records_per_command; process_error_cb = 0; get_next_record_cb = 0; process_result_cb = 0; } OsCommand* IndexingDispatcher::_allocateCommand () { return new IndexingCommand(); } bool IndexingDispatcher::_setupCommand (OsCommand &cmd) { if (_finished) return false; profTimerStart(tfing, "parallel.setupCommand"); IndexingCommand &command = (IndexingCommand &)cmd; command.core = &_core; command.lock_for_exclusive_access = &_lock_for_exclusive_access; while (command.ids.size() < _records_per_command) { if (!get_next_record_cb(context)) { _finished = true; break; } command.records.add(_core.index_record_data.ref()); command.ids.push(_core.index_record_data_id); } return command.ids.size() != 0; } void IndexingDispatcher::_handleResult (OsCommandResult &res) { profTimerStart(tfing, "parallel.handleResult"); IndexingCommandResult &result = (IndexingCommandResult &)res; for (int i = 0; i < result.ids.size(); i++) { _exposeCurrentResult(i, result); process_result_cb(context); } if (process_error_cb) { // Process results with errors for (int i = 0; i < result.error_ids.size(); i++) { _core.warning.copy((const char *)result.error_messages.get(i), result.error_messages.getSize(i)); process_error_cb(result.error_ids[i], context); } } } // // IndexingCommand // void IndexingCommand::clear () { records.clear(); ids.clear(); } void IndexingCommand::execute (OsCommandResult &result_) { IndexingCommandResult &result = (IndexingCommandResult &)result_; for (int i = 0; i < ids.size(); i++) { BufferScanner scanner(records.get(i), records.getSize(i)); NullOutput output; try { bool exception_found = false; TRY_READ_TARGET_RXN { TRY_READ_TARGET_MOL { try { BingoIndex &index = result.getIndex(result.ids.size()); index.skip_calculate_fp = core->skip_calculate_fp; index.init(*core->bingo_context); index.prepare(scanner, output, lock_for_exclusive_access); } catch (CmfSaver::Error &e) { if (core->bingo_context->reject_invalid_structures) throw; exception_found = true; result.error_messages.add(e.message()); } catch (CrfSaver::Error &e) { if (core->bingo_context->reject_invalid_structures) throw; exception_found = true; result.error_messages.add(e.message()); } } CATCH_READ_TARGET_MOL({ if (core->bingo_context->reject_invalid_structures) throw; result.error_messages.add(e.message()); exception_found = true; }); } CATCH_READ_TARGET_RXN({ if (core->bingo_context->reject_invalid_structures) throw; result.error_messages.add(e.message()); exception_found = true; }); if (exception_found) result.error_ids.push(ids[i]); else result.ids.push(ids[i]); } catch (Exception &e) { // Check unhandled exceptions e.appendMessage(" ERROR ON id=%d", ids[i]); e.throwSelf(); } } } // // IndexingCommandResult // void IndexingCommandResult::clear () { ids.clear(); error_ids.clear(); error_messages.clear(); } Indigo-indigo-1.2.3/bingo/bingo-core-c/src/bingo_core_c_parallel.h000066400000000000000000000055451271037650300250000ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_core_c_parallel_h___ #define __bingo_core_c_parallel_h___ #include "base_cpp/os_thread_wrapper.h" #include "base_cpp/chunk_storage.h" // Helper classes for parallelized indexing namespace indigo { namespace bingo_core { class BingoCore; class IndexingCommandResult; // This command contains pack of the binary data // (molecules or reactions in the raw format) class IndexingCommand : public OsCommand { public: virtual void execute (OsCommandResult &result); virtual void clear (); // Molecules or reactions ChunkStorage records; // Array of their internal indices Array ids; BingoCore *core; OsLock *lock_for_exclusive_access; }; class IndexingCommandResult : public OsCommandResult { public: virtual void clear (); virtual BingoIndex& getIndex (int index) = 0; // Array of processed indices Array ids; // Array with error messages ChunkStorage error_messages; Array error_ids; }; // Dispatcher for creating commands. // Subclasses should override _handleResult for result // handling (if necessary) // Each thread has the same Session ID as parent thread. class IndexingDispatcher : public OsCommandDispatcher { public: // Parameters: // method - HANDLING_ORDER_*** // set_parent_SID_for_threads - true if set SID for all // child threads to the parent' SID IndexingDispatcher (BingoCore &core, int method, bool set_parent_SID_for_threads, int records_per_command); void *context; // If this function returns 0, then data is finished int (*get_next_record_cb) (void *context); void (*process_result_cb) (void *context); void (*process_error_cb) (int id, void *context); bool _finished; protected: // This method should be overridden to setup current processed record so // it can be processed in process_result_cb callback. virtual void _exposeCurrentResult (int index, IndexingCommandResult &result) = 0; protected: BingoCore &_core; private: virtual OsCommand* _allocateCommand (); virtual bool _setupCommand (OsCommand &command); virtual void _handleResult (OsCommandResult &result); int _records_per_command; OsLock _lock_for_exclusive_access; }; } } #endif // __bingo_core_c_parallel_h___ Indigo-indigo-1.2.3/bingo/bingo-core-c/src/mango_core_c.cpp000066400000000000000000000653631271037650300234660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "base_cpp/profiling.h" #include "molecule/molfile_loader.h" #include "molecule/molecule_auto_loader.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/smiles_saver.h" #include "molecule/cmf_saver.h" #include "molecule/molfile_saver.h" #include "molecule/molecule_mass.h" #include "molecule/gross_formula.h" #include "molecule/icm_saver.h" #include "molecule/molecule_cml_saver.h" #include "molecule/inchi_wrapper.h" using namespace indigo::bingo_core; CEXPORT int mangoIndexProcessSingleRecord () { BINGO_BEGIN { BufferScanner scanner(self.index_record_data.ref()); NullOutput output; TRY_READ_TARGET_MOL { try { if (self.single_mango_index.get() == NULL) { self.single_mango_index.create(); self.single_mango_index->init(*self.bingo_context); self.single_mango_index->skip_calculate_fp = self.skip_calculate_fp; } self.mango_index = self.single_mango_index.get(); self.mango_index->prepare(scanner, output, NULL); } catch (CmfSaver::Error &e) { if (self.bingo_context->reject_invalid_structures) throw; self.warning.readString(e.message(), true); return 0; } } CATCH_READ_TARGET_MOL({ if (self.bingo_context->reject_invalid_structures) throw; self.warning.readString(e.message(), true); return 0; }); } BINGO_END(1, -1) } CEXPORT int mangoIndexReadPreparedMolecule (int *id, const char **cmf_buf, int *cmf_buf_len, const char **xyz_buf, int *xyz_buf_len, const char **gross_str, const char **counter_elements_str, const char **fingerprint_buf, int *fingerprint_buf_len, const char **fingerprint_sim_str, float *mass, int *sim_fp_bits_count) { BINGO_BEGIN { if (id) *id = self.index_record_data_id; const Array &cmf = self.mango_index->getCmf(); const Array &xyz = self.mango_index->getXyz(); *cmf_buf = cmf.ptr(); *cmf_buf_len = cmf.size(); *xyz_buf = xyz.ptr(); *xyz_buf_len = xyz.size(); *fingerprint_buf = (const char *)self.mango_index->getFingerprint(); *fingerprint_buf_len = self.bingo_context->fp_parameters.fingerprintSize(); *fingerprint_sim_str = self.mango_index->getFingerprint_Sim_Str(); *mass = self.mango_index->getMolecularMass(); *gross_str = self.mango_index->getGrossString(); *counter_elements_str = self.mango_index->getCountedElementsString(); *sim_fp_bits_count = self.mango_index->getFpSimilarityBitsCount(); return 1; } BINGO_END(-2, -2) } CEXPORT int mangoGetHash (bool for_index, int index, int *count, dword *hash) { BINGO_BEGIN { if (for_index) { // For index if (index == -1) { *count = self.mango_index->getHash().size(); } else { const MangoExact::HashElement &elem = self.mango_index->getHash()[index]; *count = elem.count; *hash = elem.hash; } return 1; } else { if (self.mango_search_type != BingoCore::_EXACT) throw BingoError("Hash is valid only for exact search type"); MangoExact &exact = self.mango_context->exact; const MangoExact::Hash &hash_components = exact.getQueryHash(); if (index == -1) *count = hash_components.size(); else { *count = hash_components[index].count; *hash = hash_components[index].hash; } return 1; } } BINGO_END(-2, -2) } void _mangoCheckPseudoAndCBDM (BingoCore &self) { if (self.bingo_context == 0) throw BingoError("context not set"); if (self.mango_context == 0) throw BingoError("context not set"); // TODO: pass this check inside MangoSubstructure if (!self.bingo_context->treat_x_as_pseudoatom.hasValue()) throw BingoError("treat_x_as_pseudoatom option not set"); if (!self.bingo_context->ignore_closing_bond_direction_mismatch.hasValue()) throw BingoError("ignore_closing_bond_direction_mismatch option not set"); } CEXPORT int mangoGetAtomCount (const char *target_buf, int target_buf_len) { BINGO_BEGIN { BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); loader.loadMolecule(target); return target.vertexCount(); } BINGO_END(-1, -1) } CEXPORT int mangoGetBondCount (const char *target_buf, int target_buf_len) { BINGO_BEGIN { BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); loader.loadMolecule(target); return target.edgeCount(); } BINGO_END(-1, -1) } CEXPORT int mangoSetupMatch (const char *search_type, const char *query, const char *options) { profTimerStart(t0, "match.setup_match"); BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); TRY_READ_TARGET_MOL { if (strcasecmp(search_type, "SUB") == 0) { MangoSubstructure &substructure = self.mango_context->substructure; MangoTautomer &tautomer = self.mango_context->tautomer; if (substructure.parse(options)) { substructure.loadQuery(query); self.mango_search_type = BingoCore::_SUBSTRUCTRE; return 1; } if (tautomer.parseSub(options)) { if (!self.bingo_context->tautomer_rules_ready) throw BingoError("tautomer rules not set"); tautomer.loadQuery(query); self.mango_search_type = BingoCore::_TAUTOMER; return 1; } } else if (strcasecmp(search_type, "SMARTS") == 0) { MangoSubstructure &substructure = self.mango_context->substructure; if (substructure.parse(options)) { substructure.loadSMARTS(query); self.mango_search_type = BingoCore::_SUBSTRUCTRE; return 1; } } else if (strcasecmp(search_type, "EXACT") == 0) { MangoExact &exact = self.mango_context->exact; MangoTautomer &tautomer = self.mango_context->tautomer; if (exact.parse(options)) { exact.loadQuery(query); self.mango_search_type = BingoCore::_EXACT; return 1; } if (tautomer.parseExact(options)) { // TODO: pass this check inside MangoSubstructure if (!self.bingo_context->tautomer_rules_ready) throw BingoError("tautomer rules not set"); tautomer.loadQuery(query); self.mango_search_type = BingoCore::_TAUTOMER; return 1; } } else if (strcasecmp(search_type, "SIM") == 0) { MangoSimilarity &similarity = self.mango_context->similarity; similarity.loadQuery(query); similarity.setMetrics(options); self.mango_search_type = BingoCore::_SIMILARITY; return 1; } else if (strcasecmp(search_type, "GROSS") == 0) { MangoGross &gross = self.mango_context->gross; gross.parseQuery(query); self.mango_search_type = BingoCore::_GROSS; return 1; } self.mango_search_type = BingoCore::_UNDEF; throw BingoError("Unknown search type '%s' or options string '%s'", search_type, options); } CATCH_READ_TARGET_MOL(self.error.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } CEXPORT int mangoSimilarityGetBitMinMaxBoundsArray (int count, int* target_ones, int **min_bound_ptr, int **max_bound_ptr) { BINGO_BEGIN { if (self.mango_search_type != BingoCore::_SIMILARITY) throw BingoError("Undefined search type"); MangoSimilarity &similarity = self.mango_context->similarity; self.buffer.resize(sizeof(int) * 2 * count); int *min_bounds = (int *)self.buffer.ptr(); int *max_bounds = min_bounds + count; for (int i = 0; i < count; i++) { max_bounds[i] = similarity.getUpperBound(target_ones[i]); min_bounds[i] = similarity.getLowerBound(target_ones[i]); } *min_bound_ptr = min_bounds; *max_bound_ptr = max_bounds; } BINGO_END(1, -2) } CEXPORT int mangoSimilarityGetScore (float *score) { BINGO_BEGIN { if (self.mango_search_type != BingoCore::_SIMILARITY) throw BingoError("Undefined search type"); MangoSimilarity &similarity = self.mango_context->similarity; *score = similarity.getSimilarityScore(); } BINGO_END(-2, 1) } CEXPORT int mangoSimilaritySetMinMaxBounds (float min_bound, float max_bound) { BINGO_BEGIN { if (self.mango_search_type != BingoCore::_SIMILARITY) throw BingoError("Undefined search type"); MangoSimilarity &similarity = self.mango_context->similarity; similarity.bottom = min_bound; similarity.top = max_bound; similarity.include_bottom = true; similarity.include_top = true; } BINGO_END(1, -2) } // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int mangoMatchTarget (const char *target, int target_buf_len) { profTimerStart(t0, "match.match_target"); BINGO_BEGIN { if (self.mango_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); TRY_READ_TARGET_MOL { BufferScanner scanner(target, target_buf_len); if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; substructure.loadTarget(scanner); return substructure.matchLoadedTarget() ? 1 : 0; } else if (self.mango_search_type == BingoCore::_TAUTOMER) { MangoTautomer &tautomer = self.mango_context->tautomer; tautomer.loadTarget(scanner); return tautomer.matchLoadedTarget() ? 1 : 0; } else if (self.mango_search_type == BingoCore::_EXACT) { MangoExact &exact = self.mango_context->exact; exact.loadTarget(scanner); return exact.matchLoadedTarget() ? 1 : 0; } else if (self.mango_search_type == BingoCore::_SIMILARITY) { MangoSimilarity &simlarity = self.mango_context->similarity; simlarity.calc(scanner); // Score should be obtained by calling mangoSimilarityGetScore return 1; } else if (self.mango_search_type == BingoCore::_GROSS) { MangoGross &gross = self.mango_context->gross; return gross.checkGross(target) ? 1 : 0; } else throw BingoError("Invalid search type"); } CATCH_READ_TARGET_MOL(self.warning.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int mangoMatchTargetBinary (const char *target_bin, int target_bin_len, const char *target_xyz, int target_xyz_len) { profTimerStart(t0, "match.match_target_binary"); BINGO_BEGIN { if (self.mango_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); TRY_READ_TARGET_MOL { BufferScanner scanner(target_bin, target_bin_len); BufferScanner *xyz_scanner = 0; Obj xyz_scanner_obj; if (target_xyz_len != 0) { xyz_scanner_obj.create(target_xyz, target_xyz_len); xyz_scanner = xyz_scanner_obj.get(); } if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; return substructure.matchBinary(scanner, xyz_scanner) ? 1 : 0; } else if (self.mango_search_type == BingoCore::_TAUTOMER) { MangoTautomer &tautomer = self.mango_context->tautomer; return tautomer.matchBinary(scanner) ? 1 : 0; } else if (self.mango_search_type == BingoCore::_EXACT) { MangoExact &exact = self.mango_context->exact; return exact.matchBinary(scanner, xyz_scanner) ? 1 : 0; } else if (self.mango_search_type == BingoCore::_SIMILARITY) { MangoSimilarity &similarity = self.mango_context->similarity; return similarity.matchBinary(scanner) ? 1 : 0; } else throw BingoError("Invalid search type"); } CATCH_READ_TARGET_MOL(self.warning.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } CEXPORT int mangoLoadTargetBinaryXyz (const char *target_xyz, int target_xyz_len) { profTimerStart(t0, "match.match_target_binary"); BINGO_BEGIN { if (self.mango_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); BufferScanner xyz_scanner(target_xyz, target_xyz_len); if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; substructure.loadBinaryTargetXyz(xyz_scanner); } else throw BingoError("Invalid search type"); } BINGO_END(1, -2) } CEXPORT int mangoSetHightlightingMode (int enable) { BINGO_BEGIN { if (self.mango_context == 0) throw BingoError("mango_context not set"); if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; substructure.preserve_bonds_on_highlighting = (enable != 0); } else if (self.mango_search_type == BingoCore::_TAUTOMER) { MangoTautomer &tautomer = self.mango_context->tautomer; tautomer.preserve_bonds_on_highlighting = (enable != 0); } else throw BingoError("Unsupported search type in mangoSetHightlightingMode"); } BINGO_END(1, -2) } CEXPORT const char* mangoGetHightlightedMolecule () { BINGO_BEGIN { if (self.mango_context == 0) throw BingoError("mango_context not set"); if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; substructure.getHighlightedTarget(self.buffer); } else if (self.mango_search_type == BingoCore::_TAUTOMER) { MangoTautomer &tautomer = self.mango_context->tautomer; tautomer.getHighlightedTarget(self.buffer); } else throw BingoError("Unsupported search type in mangoGetHightlightedMolecule"); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * mangoSMILES (const char *target_buf, int target_buf_len, int canonical) { profTimerStart(t0, "smiles"); BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); if (canonical) MoleculeAromatizer::aromatizeBonds(target, AromaticityOptions::BASIC); ArrayOutput out(self.buffer); if (canonical) { CanonicalSmilesSaver saver(out); saver.saveMolecule(target); } else { SmilesSaver saver(out); saver.saveMolecule(target); } out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * mangoMolfile (const char *molecule, int molecule_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(molecule, molecule_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); ArrayOutput out(self.buffer); MolfileSaver saver(out); saver.saveMolecule(target); out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * mangoCML (const char *molecule, int molecule_len) { BINGO_BEGIN { // TODO: remove copy/paste in mangoCML, mangoMolfile and etc. _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(molecule, molecule_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); ArrayOutput out(self.buffer); MoleculeCmlSaver saver(out); saver.saveMolecule(target); out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT int mangoGetQueryFingerprint (const char **query_fp, int *query_fp_len) { profTimerStart(t0, "match.query_fingerprint"); BINGO_BEGIN { if (self.mango_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; self.buffer.copy((const char*)substructure.getQueryFingerprint(), self.bingo_context->fp_parameters.fingerprintSize()); } else if (self.mango_search_type == BingoCore::_TAUTOMER) { MangoTautomer &tautomer = self.mango_context->tautomer; self.buffer.copy((const char*)tautomer.getQueryFingerprint(), self.bingo_context->fp_parameters.fingerprintSize()); } else if (self.mango_search_type == BingoCore::_SIMILARITY) { MangoSimilarity &similarity = self.mango_context->similarity; self.buffer.copy((const char*)similarity.getQueryFingerprint(), self.bingo_context->fp_parameters.fingerprintSize()); } else throw BingoError("Invalid search type"); *query_fp = self.buffer.ptr(); *query_fp_len = self.buffer.size(); } BINGO_END(1, -2) } CEXPORT const char* mangoGetCountedElementName (int index) { BINGO_BEGIN { ArrayOutput output(self.buffer); output.printf("cnt_%s", Element::toString(MangoIndex::counted_elements[index])); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT int mangoNeedCoords () { profTimerStart(t0, "match.query_fingerprint"); BINGO_BEGIN { if (self.mango_search_type == BingoCore::_SUBSTRUCTRE) { MangoSubstructure &substructure = self.mango_context->substructure; return substructure.needCoords(); } else if (self.mango_search_type == BingoCore::_EXACT) { MangoExact &exact = self.mango_context->exact; return exact.needCoords(); } else if (self.mango_search_type == BingoCore::_TAUTOMER) return 0; else if (self.mango_search_type == BingoCore::_SIMILARITY) return 0; else throw BingoError("Invalid search type"); } BINGO_END(-2, -2) } CEXPORT byte mangoExactNeedComponentMatching () { BINGO_BEGIN { MangoExact &exact = self.mango_context->exact; return exact.needComponentMatching(); } BINGO_END(-2, -2) } CEXPORT const char * mangoTauGetQueryGross () { BINGO_BEGIN { MangoTautomer &tautomer = self.mango_context->tautomer; return tautomer.getQueryGross(); } BINGO_END(0, 0) } CEXPORT int mangoMass (const char *target_buf, int target_buf_len, const char *type, float *out) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.skip_3d_chirality = true; loader.loadMolecule(target); MoleculeMass mass_calulator; mass_calulator.relative_atomic_mass_map = &self.bingo_context->relative_atomic_mass_map; if (type == 0 || strlen(type) == 0 || strcasecmp(type, "molecular-weight") == 0) *out = mass_calulator.molecularWeight(target); else if (strcasecmp(type, "most-abundant-mass") == 0) *out = mass_calulator.mostAbundantMass(target); else if (strcasecmp(type, "monoisotopic-mass") == 0) *out = mass_calulator.monoisotopicMass(target); else throw BingoError("unknown mass specifier: %s", type); return 1; } BINGO_END(-1, -1) } CEXPORT const char* mangoGross (const char *target_buf, int target_buf_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); QS_DEF(Array, gross); GrossFormula::collect(target, gross); GrossFormula::toString(gross, self.buffer); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char* mangoGrossGetConditions () { BINGO_BEGIN { if (self.bingo_context == 0) throw BingoError("context not set"); if (self.mango_search_type != BingoCore::_GROSS) throw BingoError("Search type must be 'GROSS'"); return self.mango_context->gross.getConditions(); } BINGO_END(0, 0) } CEXPORT const char * mangoCheckMolecule (const char *molecule, int molecule_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); TRY_READ_TARGET_MOL { QS_DEF(Molecule, mol); BufferScanner molecule_scanner(molecule, molecule_len); MoleculeAutoLoader loader(molecule_scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(mol); Molecule::checkForConsistency(mol); } CATCH_READ_TARGET_MOL( self.buffer.readString(e.message(), true); return self.buffer.ptr()) catch (Exception &e) { e.appendMessage(" INTERNAL ERROR"); self.buffer.readString(e.message(), true); return self.buffer.ptr(); } catch (...) { return "INTERNAL UNKNOWN ERROR"; } } BINGO_END(0, 0) } CEXPORT const char* mangoICM (const char* molecule, int molecule_len, bool save_xyz, int *out_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(molecule, molecule_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); ArrayOutput out(self.buffer); if ((save_xyz != 0) && !target.have_xyz) throw BingoError("molecule has no XYZ"); IcmSaver saver(out); saver.save_xyz = (save_xyz != 0); saver.saveMolecule(target); *out_len = self.buffer.size(); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char* mangoFingerprint(const char* molecule, int molecule_len, const char* options, int *out_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); if (!self.bingo_context->fp_parameters_ready) throw BingoError("Fingerprint settings not ready"); BufferScanner scanner(molecule, molecule_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); MoleculeFingerprintBuilder builder(target, self.bingo_context->fp_parameters); builder.parseFingerprintType(options, false); builder.process(); const char* buf = (const char*)builder.get(); int buf_len = self.bingo_context->fp_parameters.fingerprintSize(); self.buffer.copy(buf, buf_len); *out_len = self.buffer.size(); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char* mangoInChI(const char* molecule, int molecule_len, const char* options, int *out_len) { BINGO_BEGIN { _mangoCheckPseudoAndCBDM(self); BufferScanner scanner(molecule, molecule_len); QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadMolecule(target); InchiWrapper inchi; inchi.setOptions(options); inchi.saveMoleculeIntoInchi(target, self.buffer); *out_len = self.buffer.size(); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char* mangoInChIKey(const char* inchi) { BINGO_BEGIN { InchiWrapper::InChIKey(inchi, self.buffer); return self.buffer.ptr(); } BINGO_END(0, 0) } Indigo-indigo-1.2.3/bingo/bingo-core-c/src/mango_core_c_parallel.cpp000066400000000000000000000031241271037650300253250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "mango_core_c_parallel.h" #include using namespace indigo::bingo_core; // // MangoIndexingCommandResult // void MangoIndexingCommandResult::clear () { IndexingCommandResult::clear(); per_molecule_index.clear(); } BingoIndex& MangoIndexingCommandResult::getIndex (int index) { per_molecule_index.resize(index + 1); return per_molecule_index[index]; } // // MangoIndexingDispatcher // MangoIndexingDispatcher::MangoIndexingDispatcher (BingoCore &core) : IndexingDispatcher(core, HANDLING_ORDER_ANY, true, 30) { } void MangoIndexingDispatcher::_exposeCurrentResult (int index, IndexingCommandResult &res) { MangoIndexingCommandResult &result = (MangoIndexingCommandResult &)res; _core.mango_index = &result.per_molecule_index[index]; _core.index_record_data_id = result.ids[index]; } OsCommandResult* MangoIndexingDispatcher::_allocateResult () { return new MangoIndexingCommandResult(); } Indigo-indigo-1.2.3/bingo/bingo-core-c/src/mango_core_c_parallel.h000066400000000000000000000025421271037650300247750ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_core_c_parallel_h___ #define __mango_core_c_parallel_h___ #include "bingo_core_c_internal.h" #include "bingo_core_c_parallel.h" #include "base_cpp/reusable_obj_array.h" namespace indigo { namespace bingo_core { class MangoIndexingCommandResult : public IndexingCommandResult { public: virtual void clear (); virtual BingoIndex& getIndex (int index); ReusableObjArray per_molecule_index; }; class MangoIndexingDispatcher : public IndexingDispatcher { public: MangoIndexingDispatcher (BingoCore &core); protected: virtual void _exposeCurrentResult (int index, IndexingCommandResult &result); virtual OsCommandResult* _allocateResult (); }; } } #endif // __mango_core_c_parallel_h___ Indigo-indigo-1.2.3/bingo/bingo-core-c/src/ringo_core_c.cpp000066400000000000000000000346451271037650300235020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "base_cpp/profiling.h" #include "molecule/molfile_loader.h" #include "molecule/smiles_saver.h" #include "molecule/cmf_saver.h" #include "reaction/rsmiles_saver.h" #include "reaction/rsmiles_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rxnfile_saver.h" #include "reaction/crf_saver.h" #include "reaction/icr_saver.h" #include "reaction/reaction_cml_saver.h" #include "reaction/reaction_fingerprint.h" using namespace indigo::bingo_core; CEXPORT int ringoIndexProcessSingleRecord () { BINGO_BEGIN { BufferScanner scanner(self.index_record_data.ref()); NullOutput output; TRY_READ_TARGET_RXN { try { if (self.single_ringo_index.get() == NULL) { self.single_ringo_index.create(); self.single_ringo_index->init(*self.bingo_context); self.single_ringo_index->skip_calculate_fp = self.skip_calculate_fp; } self.ringo_index = self.single_ringo_index.get(); self.ringo_index->prepare(scanner, output, NULL); } catch (CmfSaver::Error &e) { if (self.bingo_context->reject_invalid_structures) throw; self.warning.readString(e.message(), true); return 0; } catch (CrfSaver::Error &e) { if (self.bingo_context->reject_invalid_structures) throw; self.warning.readString(e.message(), true); return 0; } } CATCH_READ_TARGET_RXN({ if (self.bingo_context->reject_invalid_structures) throw; self.warning.readString(e.message(), true); return 0; }); } BINGO_END(1, -1) } CEXPORT int ringoIndexReadPreparedReaction (int *id, const char **crf_buf, int *crf_buf_len, const char **fingerprint_buf, int *fingerprint_buf_len) { profTimerStart(t0, "index.prepare_reaction"); BINGO_BEGIN { if (id) *id = self.index_record_data_id; const Array &crf = self.ringo_index->getCrf(); *crf_buf = crf.ptr(); *crf_buf_len = crf.size(); *fingerprint_buf = (const char *)self.ringo_index->getFingerprint(); *fingerprint_buf_len = self.bingo_context->fp_parameters.fingerprintSizeExtOrd() * 2; return 1; } BINGO_END(-2, -2) } void _ringoCheckPseudoAndCBDM (BingoCore &self) { if (self.bingo_context == 0) throw BingoError("context not set"); if (self.ringo_context == 0) throw BingoError("context not set"); // TODO: pass this check inside RingoSubstructure if (!self.bingo_context->treat_x_as_pseudoatom.hasValue()) throw BingoError("treat_x_as_pseudoatom option not set"); if (!self.bingo_context->ignore_closing_bond_direction_mismatch.hasValue()) throw BingoError("ignore_closing_bond_direction_mismatch option not set"); } CEXPORT int ringoSetupMatch (const char *search_type, const char *query, const char *options) { profTimerStart(t0, "match.setup_match"); BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); TRY_READ_TARGET_RXN { if (strcasecmp(search_type, "RSUB") == 0 || strcasecmp(search_type, "RSMARTS") == 0) { RingoSubstructure &substructure = self.ringo_context->substructure; if (substructure.parse(options)) { if (strcasecmp(search_type, "RSUB") == 0) substructure.loadQuery(query); else substructure.loadSMARTS(query); self.ringo_search_type = BingoCore::_SUBSTRUCTRE; return 1; } } else if (strcasecmp(search_type, "REXACT") == 0) { RingoExact &exact = self.ringo_context->exact; exact.setParameters(options); exact.loadQuery(query); self.ringo_search_type = BingoCore::_EXACT; return 1; } self.ringo_search_type = BingoCore::_UNDEF; throw BingoError("Unknown search type '%s' or options string '%s'", search_type, options); } CATCH_READ_TARGET_RXN(self.error.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int ringoMatchTarget (const char *target, int target_buf_len) { profTimerStart(t0, "match.match_target"); BINGO_BEGIN { if (self.ringo_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); TRY_READ_TARGET_RXN { BufferScanner scanner(target, target_buf_len); if (self.ringo_search_type == BingoCore::_SUBSTRUCTRE) { RingoSubstructure &substructure = self.ringo_context->substructure; substructure.loadTarget(scanner); return substructure.matchLoadedTarget() ? 1 : 0; } else if (self.ringo_search_type == BingoCore::_EXACT) { RingoExact &exact = self.ringo_context->exact; exact.loadTarget(scanner); return exact.matchLoadedTarget() ? 1 : 0; } else throw BingoError("Invalid search type"); } CATCH_READ_TARGET_RXN(self.warning.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } // Return value: // 1 if the query is a substructure of the taret // 0 if it is not // -1 if something is bad with the target ("quiet" error) // -2 if some other thing is bad ("sound" error) CEXPORT int ringoMatchTargetBinary (const char *target_bin, int target_bin_len) { profTimerStart(t0, "match.match_target_binary"); BINGO_BEGIN { if (self.ringo_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); TRY_READ_TARGET_RXN { BufferScanner scanner(target_bin, target_bin_len); if (self.ringo_search_type == BingoCore::_SUBSTRUCTRE) { RingoSubstructure &substructure = self.ringo_context->substructure; return substructure.matchBinary(scanner) ? 1 : 0; } else if (self.ringo_search_type == BingoCore::_EXACT) { RingoExact &exact = self.ringo_context->exact; return exact.matchBinary(scanner) ? 1 : 0; } else throw BingoError("Invalid search type"); } CATCH_READ_TARGET_RXN(self.warning.readString(e.message(), 1); return -1;); } BINGO_END(-2, -2) } CEXPORT const char * ringoRSMILES (const char *target_buf, int target_buf_len) { profTimerStart(t0, "rsmiles"); BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); BufferScanner scanner(target_buf, target_buf_len); QS_DEF(Reaction, target); ReactionAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(target); ArrayOutput out(self.buffer); RSmilesSaver saver(out); saver.saveReaction(target); out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * ringoRxnfile (const char *reaction, int reaction_len) { BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); BufferScanner scanner(reaction, reaction_len); QS_DEF(Reaction, target); ReactionAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(target); ArrayOutput out(self.buffer); RxnfileSaver saver(out); saver.saveReaction(target); out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * ringoRCML (const char *reaction, int reaction_len) { BINGO_BEGIN { // TODO: remove copy/paste in ringoRCML, ringoRxnfile and etc. _ringoCheckPseudoAndCBDM(self); BufferScanner scanner(reaction, reaction_len); QS_DEF(Reaction, target); ReactionAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(target); ArrayOutput out(self.buffer); ReactionCmlSaver saver(out); saver.saveReaction(target); out.writeByte(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * ringoAAM (const char *reaction, int reaction_len, const char *mode) { BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); self.ringo_context->ringoAAM.parse(mode); BufferScanner reaction_scanner(reaction, reaction_len); self.ringo_context->ringoAAM.loadReaction(reaction_scanner); self.ringo_context->ringoAAM.getResult(self.buffer); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT const char * ringoCheckReaction (const char *reaction, int reaction_len) { BINGO_BEGIN { TRY_READ_TARGET_RXN { _ringoCheckPseudoAndCBDM(self); QS_DEF(Reaction, rxn); BufferScanner reaction_scanner(reaction, reaction_len); ReactionAutoLoader loader(reaction_scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(rxn); Reaction::checkForConsistency(rxn); } CATCH_READ_TARGET_RXN( self.buffer.readString(e.message(), true); return self.buffer.ptr()) catch (Exception &e) { e.appendMessage(" INTERNAL ERROR"); self.buffer.readString(e.message(), true); return self.buffer.ptr(); } catch (...) { return "INTERNAL UNKNOWN ERROR"; } } BINGO_END(0, 0) } CEXPORT int ringoGetQueryFingerprint (const char **query_fp, int *query_fp_len) { profTimerStart(t0, "match.query_fingerprint"); BINGO_BEGIN { if (self.ringo_search_type == BingoCore::_UNDEF) throw BingoError("Undefined search type"); if (self.ringo_search_type == BingoCore::_SUBSTRUCTRE) { RingoSubstructure &substructure = self.ringo_context->substructure; self.buffer.copy((const char*)substructure.getQueryFingerprint(), self.bingo_context->fp_parameters.fingerprintSizeExtOrd() * 2); } else throw BingoError("Invalid search type"); *query_fp = self.buffer.ptr(); *query_fp_len = self.buffer.size(); } BINGO_END(1, -2) } CEXPORT int ringoSetHightlightingMode (int enable) { BINGO_BEGIN { if (self.ringo_search_type == BingoCore::_SUBSTRUCTRE) { RingoSubstructure &substructure = self.ringo_context->substructure; return substructure.preserve_bonds_on_highlighting = (enable != 0); } else throw BingoError("Invalid search type"); } BINGO_END(1, -2); } CEXPORT const char* ringoGetHightlightedReaction () { BINGO_BEGIN { if (self.ringo_search_type == BingoCore::_SUBSTRUCTRE) { RingoSubstructure &substructure = self.ringo_context->substructure; substructure.getHighlightedTarget(self.buffer); } else throw BingoError("Invalid search type"); self.buffer.push(0); return self.buffer.ptr(); } BINGO_END(0, 0); } CEXPORT const char* ringoICR (const char* reaction, int reaction_len, bool save_xyz, int *out_len) { BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); BufferScanner scanner(reaction, reaction_len); QS_DEF(Reaction, target); ReactionAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(target); ArrayOutput out(self.buffer); if ((save_xyz != 0) && !Reaction::haveCoord(target)) throw BingoError("reaction has no XYZ"); IcrSaver saver(out); saver.save_xyz = (save_xyz != 0); saver.saveReaction(target); *out_len = self.buffer.size(); return self.buffer.ptr(); } BINGO_END(0, 0) } CEXPORT int ringoGetHash (bool for_index, dword *hash) { BINGO_BEGIN { if (for_index) { *hash = self.ringo_index->getHash(); return 1; } else { if (self.ringo_search_type != BingoCore::_EXACT) throw BingoError("Hash is valid only for exact search type"); RingoExact &exact = self.ringo_context->exact; *hash = exact.getQueryHash(); return 1; } } BINGO_END(-2, -2) } CEXPORT const char* ringoFingerprint(const char* reaction, int reaction_len, const char* options, int *out_len) { BINGO_BEGIN { _ringoCheckPseudoAndCBDM(self); if (!self.bingo_context->fp_parameters_ready) throw BingoError("Fingerprint settings not ready"); BufferScanner scanner(reaction, reaction_len); QS_DEF(Reaction, target); ReactionAutoLoader loader(scanner); self.bingo_context->setLoaderSettings(loader); loader.loadReaction(target); ReactionFingerprintBuilder builder(target, self.bingo_context->fp_parameters); builder.parseFingerprintType(options, false); builder.process(); const char* buf = (const char*)builder.get(); int buf_len = self.bingo_context->fp_parameters.fingerprintSizeExtOrdSim() * 2; self.buffer.copy(buf, buf_len); *out_len = self.buffer.size(); return self.buffer.ptr(); } BINGO_END(0, 0) }Indigo-indigo-1.2.3/bingo/bingo-core-c/src/ringo_core_c_parallel.cpp000066400000000000000000000031231271037650300253410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_core_c_internal.h" #include "ringo_core_c_parallel.h" #include using namespace indigo::bingo_core; // // MangoIndexingCommandResult // void RingoIndexingCommandResult::clear () { IndexingCommandResult::clear(); per_reaction_index.clear(); } BingoIndex& RingoIndexingCommandResult::getIndex (int index) { per_reaction_index.resize(index + 1); return per_reaction_index[index]; } // // MangoIndexingDispatcher // RingoIndexingDispatcher::RingoIndexingDispatcher (BingoCore &core) : IndexingDispatcher(core, HANDLING_ORDER_ANY, true, 30) { } void RingoIndexingDispatcher::_exposeCurrentResult (int index, IndexingCommandResult &res) { RingoIndexingCommandResult &result = (RingoIndexingCommandResult &)res; _core.ringo_index = &result.per_reaction_index[index]; _core.index_record_data_id = result.ids[index]; } OsCommandResult* RingoIndexingDispatcher::_allocateResult () { return new RingoIndexingCommandResult(); } Indigo-indigo-1.2.3/bingo/bingo-core-c/src/ringo_core_c_parallel.h000066400000000000000000000025421271037650300250120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_core_c_parallel_h___ #define __ringo_core_c_parallel_h___ #include "bingo_core_c_internal.h" #include "bingo_core_c_parallel.h" #include "base_cpp/reusable_obj_array.h" namespace indigo { namespace bingo_core { class RingoIndexingCommandResult : public IndexingCommandResult { public: virtual void clear (); virtual BingoIndex& getIndex (int index); ReusableObjArray per_reaction_index; }; class RingoIndexingDispatcher : public IndexingDispatcher { public: RingoIndexingDispatcher (BingoCore &core); protected: virtual void _exposeCurrentResult (int index, IndexingCommandResult &result); virtual OsCommandResult* _allocateResult (); }; } } #endif // __mango_core_c_parallel_h___ Indigo-indigo-1.2.3/bingo/bingo-core/000077500000000000000000000000001271037650300173235ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/bingo-core/CMakeLists.txt000066400000000000000000000015321271037650300220640ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(BingoCore) SET(BingoCore_HEADERS_DIR ${BingoCore_SOURCE_DIR}/src CACHE INTERNAL "include directories" FORCE) CMAKE_POLICY(SET CMP0011 NEW) INCLUDE(${Bingo_SOURCE_DIR}/bingo-version.cmake) MESSAGE(STATUS "BingoCore version is ${BINGO_VERSION}") configure_file(${BingoCore_HEADERS_DIR}/core/bingo_version.h.in ${BingoCore_HEADERS_DIR}/core/bingo_version.h) file(GLOB BingoCore_src src/core/*.cpp) file(GLOB BingoCore_headers ${BingoCore_HEADERS_DIR}/core/*.h) include(SetBuildParameters) include_directories(src ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/..) add_library(bingo-core STATIC ${BingoCore_src} ${BingoCore_headers}) set_target_properties(bingo-core PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET bingo-core PROPERTY FOLDER "bingo-core") Indigo-indigo-1.2.3/bingo/bingo-core/src/000077500000000000000000000000001271037650300201125ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/bingo-core/src/core/000077500000000000000000000000001271037650300210425ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/bingo-core/src/core/bingo_context.cpp000066400000000000000000000154721271037650300244210ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/bingo_context.h" #include "base_cpp/scanner.h" #include "core/bingo_error.h" #include "molecule/elements.h" #include "molecule/molecule_auto_loader.h" #include "molecule/smiles_loader.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rsmiles_loader.h" // const char * bingo_version_string = "1.7-beta3"; TL_DEF(BingoContext, PtrArray, _instances); OsLock BingoContext::_instances_lock; IMPL_ERROR(BingoContext, "bingo context"); BingoContext::BingoContext (int id_) { id = id_; reset(); treat_x_as_pseudoatom.setName("treat-x-as-pseudoatom"); ignore_closing_bond_direction_mismatch.setName("ignore-closing-bond-direction-mismatch"); ignore_stereocenter_errors.setName("ignore-stereocenter-errors"); stereochemistry_bidirectional_mode.setName("stereochemistry-bidirectional-mode"); stereochemistry_detect_haworth_projection.setName("stereochemistry-detect-haworth-projection"); ignore_cistrans_errors.setName("ignore-cistrans-errors"); allow_non_unique_dearomatization.setName("allow-non-unique-dearomatization"); zero_unknown_aromatic_hydrogens.setName("zero-unknown-aromatic-hydrogens"); reject_invalid_structures.setName("reject-invalid-structures"); } void BingoContext::reset () { cmf_dict.reset(); nthreads = 0; timeout = DEFAULT_TIMEOUT; tautomer_rules_ready = false; fp_parameters_ready = false; atomic_mass_map_ready = false; treat_x_as_pseudoatom.reset(); ignore_closing_bond_direction_mismatch.reset(); ignore_stereocenter_errors.reset(); stereochemistry_bidirectional_mode.reset(); stereochemistry_detect_haworth_projection.reset(); ignore_cistrans_errors.reset(); allow_non_unique_dearomatization.reset(); zero_unknown_aromatic_hydrogens.reset(); reject_invalid_structures.reset(); } BingoContext::~BingoContext () { } void BingoContext::remove (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) break; //if (i == _instances.size()) // throw Error("remove(): context #%d not found", id); if (i != _instances.size()) _instances.remove(i); } BingoContext * BingoContext::_get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) return _instances[i]; return 0; } BingoContext * BingoContext::existing (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) return _instances[i]; throw Error("context #%d not found", id); } BingoContext * BingoContext::get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) return _instances[i]; return &_instances.add(new BingoContext(id)); } void bingoGetTauCondition (const char *list_ptr, int &aromaticity, Array &label_list) { if (isdigit(*list_ptr)) { if (*list_ptr == '1') aromaticity = 1; else if (*list_ptr == '0') aromaticity = 0; else throw BingoError("Bad tautomer configuration string format"); list_ptr++; } else { aromaticity = -1; } label_list.clear(); QS_DEF(Array, buf); buf.clear(); while (*list_ptr != 0) { if (isalpha((unsigned)*list_ptr)) buf.push(*list_ptr); else if (*list_ptr == ',') { buf.push(0); label_list.push(Element::fromString(buf.ptr())); buf.clear(); } else throw BingoError("Bad label list format"); list_ptr++; } buf.push(0); label_list.push(Element::fromString(buf.ptr())); } void bingoGetName (Scanner &scanner, Array &result) { QS_DEF(Array, str); bool single_line = Scanner::isSingleLine(scanner); result.clear(); if (single_line) { scanner.readLine(str, true); int idx = str.find('|'); int idx2 = 0; if (idx >= 0) { int tmp = str.find(idx + 1, str.size(), '|'); if (tmp > 0) idx2 = tmp + 1; } BufferScanner scan2(str.ptr() + idx2); while (!scan2.isEOF()) { if (isspace(scan2.readChar())) break; } scan2.skipSpace(); if (!scan2.isEOF()) scan2.readLine(result, false); } else { scanner.readLine(result, false); if (result.size() >= 4 && strncmp(result.ptr(), "$RXN", 4) == 0) scanner.readLine(result, false); } } StereocentersOptions BingoContext::getStereocentersOptions() { StereocentersOptions opt; opt.ignore_errors = ignore_stereocenter_errors; opt.bidirectional_mode = stereochemistry_bidirectional_mode; opt.detect_haworth_projection = stereochemistry_detect_haworth_projection; return opt; } void BingoContext::setLoaderSettings (MoleculeAutoLoader &loader) { loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.stereochemistry_options = getStereocentersOptions(); loader.ignore_cistrans_errors = ignore_cistrans_errors; } void BingoContext::setLoaderSettings(SmilesLoader &loader) { loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.stereochemistry_options = getStereocentersOptions(); loader.ignore_cistrans_errors = ignore_cistrans_errors; } void BingoContext::setLoaderSettings(ReactionAutoLoader &loader) { loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.stereochemistry_options = getStereocentersOptions(); loader.ignore_cistrans_errors = ignore_cistrans_errors; } void BingoContext::setLoaderSettings(RSmilesLoader &loader) { loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.stereochemistry_options = getStereocentersOptions(); loader.ignore_cistrans_errors = ignore_cistrans_errors; } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/bingo_context.h000066400000000000000000000056531271037650300240660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_context__ #define __bingo_context__ #include "base_cpp/nullable.h" #include "molecule/molecule_fingerprint.h" #include "molecule/molecule_tautomer.h" #include "lzw/lzw_dictionary.h" #include "bingo_version.h" using namespace indigo; // extern const char *bingo_version_string; namespace indigo { class MoleculeAutoLoader; class ReactionAutoLoader; class SmilesLoader; class RSmilesLoader; } class BingoContext { public: enum { /* * Default operation timeout = 60000 ms */ DEFAULT_TIMEOUT = 60000 }; explicit BingoContext (int id_); virtual ~BingoContext (); bool tautomer_rules_ready; bool fp_parameters_ready; bool atomic_mass_map_ready; int id; LzwDict cmf_dict; LzwDict rid_dict; int fp_chunk_qwords; int nthreads; int timeout; Nullable treat_x_as_pseudoatom; Nullable ignore_closing_bond_direction_mismatch; Nullable ignore_stereocenter_errors; Nullable stereochemistry_bidirectional_mode; Nullable stereochemistry_detect_haworth_projection; Nullable ignore_cistrans_errors; Nullable allow_non_unique_dearomatization; Nullable zero_unknown_aromatic_hydrogens; // Throw exception when invalid structure is being added to the index Nullable reject_invalid_structures; MoleculeFingerprintParameters fp_parameters; PtrArray tautomer_rules; RedBlackMap relative_atomic_mass_map; void setLoaderSettings (MoleculeAutoLoader &loader); void setLoaderSettings (ReactionAutoLoader &loader); void setLoaderSettings (SmilesLoader &loader); void setLoaderSettings (RSmilesLoader &loader); StereocentersOptions getStereocentersOptions(); static void remove (int id); void reset (); DECL_ERROR; static BingoContext * get (int id); static BingoContext * existing (int id); protected: TL_DECL(PtrArray, _instances); static BingoContext * _get (int id); static OsLock _instances_lock; Array _relative_atomic_mass; private: BingoContext (const BingoContext &); // no implicit copy }; void bingoGetTauCondition (const char *list_ptr, int &aromaticity, Array &label_list); void bingoGetName (Scanner &scanner, Array &result); #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/bingo_error.h000066400000000000000000000020471271037650300235250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_error__ #define __bingo_error__ #include "base_cpp/exception.h" using namespace indigo; class BingoError : public Exception { public: explicit BingoError (const char *format, ...) { va_list args; va_start(args, format); _init("core", format, args); va_end(args); } BingoError (const BingoError &other) : Exception() { other._cloneTo(this); } }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/bingo_index.h000066400000000000000000000024521271037650300235030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_index__ #define __bingo_index__ #include "base_cpp/array.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "core/bingo_error.h" using namespace indigo; class BingoContext; namespace indigo { class OsLock; } class BingoIndex { public: BingoIndex () { _context = 0; skip_calculate_fp = false; } virtual ~BingoIndex () {} void init (BingoContext &context) { _context = &context; }; virtual void prepare (Scanner &scanner, Output &output, OsLock *lock_for_exclusive_access) = 0; bool skip_calculate_fp; protected: BingoContext *_context; private: BingoIndex (const BingoIndex &); // no implicit copy }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/bingo_version.h.in000066400000000000000000000013531271037650300244650ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_version__ #define __bingo_version__ #define BINGO_VERSION "${BINGO_VERSION_EXT}" #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_context.cpp000066400000000000000000000066451271037650300244260ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_context.h" #include "core/bingo_context.h" TL_DEF(MangoContext, PtrArray, _instances); OsLock MangoContext::_instances_lock; IMPL_ERROR(MangoContext, "mango context"); MangoContext::MangoContext (BingoContext &context) : substructure(context), similarity(context), exact(context), tautomer(context), gross(context), _context(context) { } MangoContext::~MangoContext () { } MangoContext * MangoContext::_get (int id, BingoContext &context) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; return 0; } MangoContext * MangoContext::existing (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; throw Error("context #%d not found", id); } MangoContext * MangoContext::get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; BingoContext *bingo_context = BingoContext::get(id); return &_instances.add(new MangoContext(*bingo_context)); } void MangoContext::remove (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) break; //if (i == _instances.size()) // throw Error("remove(): context #%d not found", id); if (i != _instances.size()) _instances.remove(i); } int MangoContext::begin () { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); if (_instances.size() < 1) return 0; int i, min_id = _instances[0]->_context.id; for (i = 1; i < _instances.size(); i++) if (_instances[i]->_context.id < min_id) min_id = _instances[i]->_context.id; return min_id; } int MangoContext::end () { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); if (_instances.size() < 1) return 0; int i, max_id = _instances[0]->_context.id; for (i = 1; i < _instances.size(); i++) if (_instances[i]->_context.id > max_id) max_id = _instances[i]->_context.id; return max_id + 1; } int MangoContext::next (int k) { int i, next_id = end(); OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id > k && _instances[i]->_context.id < next_id) next_id = _instances[i]->_context.id; return next_id; } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_context.h000066400000000000000000000027131271037650300240630ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_context__ #define __mango_context__ #include "core/mango_matchers.h" #include "core/mango_index.h" #include "base_cpp/tlscont.h" using namespace indigo; class MangoContext { public: explicit MangoContext (BingoContext &context); virtual ~MangoContext (); MangoSubstructure substructure; MangoSimilarity similarity; MangoExact exact; MangoTautomer tautomer; MangoGross gross; static int begin (); static int end (); static int next (int k); DECL_ERROR; static void remove (int id); static MangoContext * get (int id); static MangoContext * existing (int id); protected: static MangoContext * _get (int id, BingoContext &context); TL_DECL(PtrArray, _instances); static OsLock _instances_lock; BingoContext &_context; }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_exact.cpp000066400000000000000000000143011271037650300240320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_matchers.h" #include "base_cpp/scanner.h" #include "graph/filter.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molecule_substructure_matcher.h" #include "graph/subgraph_hash.h" #include "molecule/cmf_loader.h" #include "core/bingo_error.h" #include "core/bingo_context.h" #include "base_cpp/crc32.h" #include "molecule/elements.h" #include "core/mango_index.h" #include "base_cpp/cancellation_handler.h" MangoExact::MangoExact (BingoContext &context) : _context(context) { _flags = 0; _rms_threshold = 0; } void MangoExact::loadQuery (Scanner &scanner) { MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(_query); Molecule::checkForConsistency(_query); _initQuery(_query); calculateHash(_query, _query_hash); } void MangoExact::calculateHash (Molecule &mol, Hash &hash) { hash.clear(); QS_DEF(Molecule, mol_without_h); QS_DEF(Array, vertices); int i; vertices.clear(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.getAtomNumber(i) != ELEM_H) vertices.push(i); mol_without_h.makeSubmolecule(mol, vertices, 0); // Decompose into connected components int n_comp = mol_without_h.countComponents(); QS_DEF(Molecule, component); QS_DEF(Array, vertex_codes); for (int i = 0; i < n_comp; i++) { Filter filter(mol_without_h.getDecomposition().ptr(), Filter::EQ, i); component.makeSubmolecule(mol_without_h, filter, 0, 0); SubgraphHash hh(component); vertex_codes.clear_resize(component.vertexEnd()); for (int v = component.vertexBegin(); v != component.vertexEnd(); v = component.vertexNext(v)) vertex_codes[v] = vertexCode(component, v); hh.vertex_codes = &vertex_codes; hh.max_iterations = (component.edgeCount() + 1) / 2; dword component_hash = hh.getHash(); // Find component hash in all hashes bool found = false; for (int j = 0; j < hash.size(); j++) if (hash[j].hash == component_hash) { hash[j].count++; found = true; break; } if (!found) { HashElement &hash_element = hash.push(); hash_element.count = 1; hash_element.hash = component_hash; } } } void MangoExact::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void MangoExact::loadQuery (const char *buf) { BufferScanner scanner(buf); loadQuery(scanner); } const MangoExact::Hash& MangoExact::getQueryHash () const { return _query_hash; } void MangoExact::setParameters (const char *conditions) { MoleculeExactMatcher::parseConditions(conditions, _flags, _rms_threshold); } void MangoExact::loadTarget (Scanner &scanner) { MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(_target); Molecule::checkForConsistency(_target); _initTarget(_target, false); } void MangoExact::loadTarget (const Array &target_buf) { BufferScanner scanner(target_buf); loadTarget(scanner); } void MangoExact::loadTarget (const char *target) { BufferScanner scanner(target); loadTarget(scanner); } bool MangoExact::matchLoadedTarget () { MoleculeExactMatcher matcher(_query, _target); matcher.flags = _flags; matcher.rms_threshold = _rms_threshold; return matcher.find(); } void MangoExact::_initQuery (Molecule &query) { int i; MoleculeAromatizer::aromatizeBonds(query, AromaticityOptions::BASIC); if (_flags & MoleculeExactMatcher::CONDITION_STEREO) { for (i = query.edgeBegin(); i != query.edgeEnd(); i = query.edgeNext(i)) if (query.getEdgeTopology(i) == TOPOLOGY_RING) query.cis_trans.setParity(i, 0); } } void MangoExact::_initTarget (Molecule &target, bool from_database) { if (!from_database) MoleculeAromatizer::aromatizeBonds(target, AromaticityOptions::BASIC); } bool MangoExact::matchBinary (Scanner &scanner, Scanner *xyz_scanner) { CmfLoader loader(_context.cmf_dict, scanner); loader.loadMolecule(_target); if (xyz_scanner != 0) loader.loadXyz(*xyz_scanner); _initTarget(_target, true); /* * Set up timeout for matching */ TimeoutCancellationHandler timeout(_context.timeout); AutoCancellationHandler auto_cancel(timeout); MoleculeExactMatcher matcher(_query, _target); matcher.flags = _flags; matcher.rms_threshold = _rms_threshold; return matcher.find(); } bool MangoExact::matchBinary (const Array &target_buf, const Array *xyz_buf) { BufferScanner scanner(target_buf); if (xyz_buf == 0) return matchBinary(scanner, 0); BufferScanner xyz_scanner(*xyz_buf); return matchBinary(scanner, &xyz_scanner); } bool MangoExact::needCoords () const { return (_flags & MoleculeExactMatcher::CONDITION_3D) != 0; } bool MangoExact::needComponentMatching () const { return (_flags & MoleculeExactMatcher::CONDITION_FRAGMENTS) == 0; } bool MangoExact::parse (const char *params) { if (params == 0) { setParameters(""); return true; } QS_DEF(Array, params_upper); params_upper.upper(params); if (strstr(params_upper.ptr(), "TAU") != NULL) return false; setParameters(params); return true; } int MangoExact::vertexCode (Molecule &mol, int vertex_idx) { if (mol.isPseudoAtom(vertex_idx)) return CRC32::get(mol.getPseudoAtom(vertex_idx)); if (mol.isRSite(vertex_idx)) return ELEM_RSITE; return mol.getAtomNumber(vertex_idx); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_gross.cpp000066400000000000000000000067731271037650300241010ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "core/bingo_context.h" #include "core/mango_index.h" #include "core/mango_matchers.h" #include "molecule/molecule_auto_loader.h" #include "molecule/gross_formula.h" #include "molecule/elements.h" IMPL_ERROR(MangoGross, "gross formula"); MangoGross::MangoGross (BingoContext &context) : _context(context) { } void MangoGross::parseQuery (const Array &query) { BufferScanner scanner(query); parseQuery(scanner); } void MangoGross::parseQuery (const char *query) { BufferScanner scanner(query); parseQuery(scanner); } void MangoGross::parseQuery (Scanner &scanner) { scanner.skipSpace(); char c = scanner.readChar(); if (c == '>') { if (scanner.readChar() != '=') throw Error("expecting '=' after '>'"); _sign = 1; } else if (c == '<') { if (scanner.readChar() != '=') throw Error("expecting '=' after '<'"); _sign = -1; } else if (c == '=') _sign = 0; else throw Error("expected one of '<= >= =', got %c", c); GrossFormula::fromString(scanner, _query_gross); ArrayOutput out(_conditions); bool first = true; if (_sign == 0) { QS_DEF(Array, query_gross_str); GrossFormula::toString(_query_gross, query_gross_str); out.printf("gross = '%s'", query_gross_str.ptr()); } else for (int i = 0; i < NELEM(MangoIndex::counted_elements); i++) { int elem = MangoIndex::counted_elements[i]; if (_query_gross[elem] <= 0 && _sign == 1) continue; if (!first) out.printf(" AND "); first = false; if (_sign == 1) out.printf("cnt_%s >= %d", Element::toString(elem), _query_gross[elem]); else // _sign == -1 out.printf("cnt_%s <= %d", Element::toString(elem), _query_gross[elem]); } out.writeChar(0); } const char * MangoGross::getConditions () { return _conditions.ptr(); } bool MangoGross::checkGross (const Array &target_gross) { if (_sign == 1) return GrossFormula::geq(target_gross, _query_gross); else if (_sign == -1) return GrossFormula::leq(target_gross, _query_gross); // _sign == 0 return GrossFormula::equal(target_gross, _query_gross); } bool MangoGross::checkGross (const char *target_gross_str) { QS_DEF(Array, target_gross); GrossFormula::fromString(target_gross_str, target_gross); return checkGross(target_gross); } bool MangoGross::checkMolecule (const Array &target_buf) { BufferScanner scanner(target_buf); return checkMolecule(scanner); } bool MangoGross::checkMolecule (Scanner &scanner) { QS_DEF(Molecule, target); QS_DEF(Array, target_gross); MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(target); GrossFormula::collect(target, target_gross); return checkGross(target_gross); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_index.cpp000066400000000000000000000120061271037650300240350ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_index.h" #include "molecule/elements.h" #include "base_cpp/scanner.h" #include "base_cpp/os_sync_wrapper.h" #include "molecule/molecule.h" #include "molecule/molecule_auto_loader.h" #include "core/bingo_error.h" #include "molecule/gross_formula.h" #include "molecule/molecule_mass.h" #include "core/bingo_context.h" #include "core/mango_matchers.h" #include "molecule/cmf_saver.h" #include "base_cpp/profiling.h" #include "molecule/molecule_arom.h" const int MangoIndex::counted_elements[6] = {ELEM_C, ELEM_N, ELEM_O, ELEM_P, ELEM_S, ELEM_H}; void MangoIndex::prepare (Scanner &molfile, Output &output, OsLock *lock_for_exclusive_access) { QS_DEF(Molecule, mol); QS_DEF(Array, gross); MoleculeAutoLoader loader(molfile); _context->setLoaderSettings(loader); loader.loadMolecule(mol); // Skip all SGroups mol.clearSGroups(); if (_context->allow_non_unique_dearomatization) MoleculeDearomatizer::restoreHydrogens(mol, false); if (_context->zero_unknown_aromatic_hydrogens) { mol.restoreAromaticHydrogens(); for (int i : mol.vertices()) { if (mol.isRSite(i) || mol.isPseudoAtom(i)) continue; if (mol.getAtomAromaticity(i) == ATOM_AROMATIC && mol.getImplicitH_NoThrow(i, -1) == -1) mol.setImplicitH(i, 0); } } Molecule::checkForConsistency(mol); // Make aromatic molecule MoleculeAromatizer::aromatizeBonds(mol, AromaticityOptions::BASIC); MangoExact::calculateHash(mol, _hash); if (!skip_calculate_fp) { MoleculeFingerprintBuilder builder(mol, _context->fp_parameters); profTimerStart(tfing, "moleculeIndex.createFingerprint"); builder.process(); profTimerStop(tfing); _fp.copy(builder.get(), _context->fp_parameters.fingerprintSize()); _fp_sim_bits_count = builder.countBits_Sim(); output.writeBinaryWord((word)_fp_sim_bits_count); const byte *fp_sim_ptr = builder.getSim(); int fp_sim_size = _context->fp_parameters.fingerprintSizeSim(); ArrayOutput fp_sim_output(_fp_sim_str); for (int i = 0; i < fp_sim_size; i++) fp_sim_output.printf("%02X", fp_sim_ptr[i]); fp_sim_output.writeChar(0); } ArrayOutput output_cmf(_cmf); { // CmfSaver modifies _context->cmf_dict and // requires exclusive access for this OsLockerNullable locker(lock_for_exclusive_access); CmfSaver saver(_context->cmf_dict, output_cmf); saver.saveMolecule(mol); if (mol.have_xyz) { ArrayOutput output_xyz(_xyz); saver.saveXyz(output_xyz); } else _xyz.clear(); } output.writeArray(_cmf); // Save gross formula GrossFormula::collect(mol, gross); GrossFormula::toString(gross, _gross_str); _counted_elems_str.clear(); _counted_elem_counters.clear(); ArrayOutput ce_output(_counted_elems_str); for (int i = 0; i < (int)NELEM(counted_elements); i++) { _counted_elem_counters.push(gross[counted_elements[i]]); ce_output.printf(", %d", gross[counted_elements[i]]); } ce_output.writeByte(0); // Calculate molecular mass MoleculeMass mass_calulator; mass_calulator.relative_atomic_mass_map = &_context->relative_atomic_mass_map; _molecular_mass = mass_calulator.molecularWeight(mol); } const MangoExact::Hash& MangoIndex::getHash () const { return _hash; } const char * MangoIndex::getGrossString () const { return _gross_str.ptr(); } const char * MangoIndex::getCountedElementsString () const { return (const char *)_counted_elems_str.ptr(); } const Array & MangoIndex::getCountedElements () const { return _counted_elem_counters; } const Array & MangoIndex::getCmf () const { return _cmf; } const Array & MangoIndex::getXyz () const { return _xyz; } const byte * MangoIndex::getFingerprint () const { return _fp.ptr(); } const char * MangoIndex::getFingerprint_Sim_Str () const { return _fp_sim_str.ptr(); } float MangoIndex::getMolecularMass () const { return _molecular_mass; } int MangoIndex::getFpSimilarityBitsCount () const { return _fp_sim_bits_count; } void MangoIndex::clear () { _cmf.clear(); _xyz.clear(); _hash.clear(); _gross.clear(); _gross_str.clear(); _fp.clear(); _fp_sim_str.clear(); _counted_elems_str.clear(); _molecular_mass = -1; _fp_sim_bits_count = -1; } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_index.h000066400000000000000000000041261271037650300235060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_index__ #define __mango_index__ #include "base_cpp/array.h" #include "base_cpp/output.h" #include "core/mango_matchers.h" #include "core/bingo_index.h" using namespace indigo; namespace indigo { class Scanner; } class BingoContext; class MangoIndex : public BingoIndex { public: void prepare (Scanner &molfile, Output &output, OsLock *lock_for_exclusive_access); const Array & getCmf () const; const Array & getXyz () const; const MangoExact::Hash & getHash () const; const char * getGrossString () const; const char * getCountedElementsString () const; const Array & getCountedElements () const; const byte * getFingerprint () const; const char * getFingerprint_Sim_Str () const; float getMolecularMass() const; int getFpSimilarityBitsCount () const; static const int counted_elements[6]; void clear (); private: // CMF-packed aromatized molecule and coordinates Array _cmf; Array _xyz; // hash for exact match MangoExact::Hash _hash; // gross formula Array _gross; Array _gross_str; Array _fp; Array _fp_sim_str; // comma-separated list of selected counters // (for non-exact gross formula search) Array _counted_elems_str; Array _counted_elem_counters; // Molecular mass float _molecular_mass; // Number of one bits in similarity fingerprint int _fp_sim_bits_count; }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_matchers.h000066400000000000000000000176561271037650300242210ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_matchers__ #define __mango_matchers__ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/tlscont.h" #include "molecule/molecule_neighbourhood_counters.h" #include "molecule/molecule_tautomer.h" #include "molecule/cmf_loader.h" #include "base_cpp/auto_ptr.h" #include "molecule/molecule_substructure_matcher.h" using namespace indigo; class BingoContext; class MangoSubstructure { public: MangoSubstructure (BingoContext &context); void loadQuery (const Array &buf); void loadQuery (const char *str); void loadQuery (Scanner &scanner); void loadSMARTS (const Array &buf); void loadSMARTS (const char *str); void loadSMARTS (Scanner &scanner); void loadTarget (const Array &molfile_buf); void loadTarget (const char *target); void loadTarget (Scanner &scanner); bool matchLoadedTarget (); bool matchBinary (const Array &target_buf, const Array *xyz_buf); bool matchBinary (Scanner &scanner, Scanner *xyz_scanner); void loadBinaryTargetXyz (Scanner &xyz_scanner); const byte * getQueryFingerprint (); void getHighlightedTarget (Array &molfile_buf); void getHighlightedTarget_Smiles (Array &smiles_buf); bool needCoords (); // parameters to pass to MoleculeSubstructureMatcher int match_3d; float rms_threshold; bool preserve_bonds_on_highlighting; bool parse (const char *params); DECL_ERROR; protected: BingoContext &_context; Molecule _target; QueryMolecule _query; Array _target_bond_types; Array _query_fp; bool _use_pi_systems_matcher; MoleculeAtomNeighbourhoodCounters _nei_target_counters; MoleculeAtomNeighbourhoodCounters _nei_query_counters; ObjArray< RedBlackStringMap > _fmcache; // cmf loader for delayed xyz loading Obj cmf_loader; bool _query_has_stereocare_bonds; bool _query_has_stereocenters; bool _query_fp_valid; bool _query_extra_valid; void _validateQueryFP (); void _validateQueryExtraData (); void _initQuery (QueryMolecule &query_in, QueryMolecule &query_out); void _initSmartsQuery (QueryMolecule &query_in, QueryMolecule &query_out); void _initTarget (bool from_database); void _correctQueryStereo (QueryMolecule &query); }; class MangoExact { public: MangoExact (BingoContext &context); void loadQuery (const Array &buf); void loadQuery (Scanner &scanner); void loadQuery (const char *buf); // Molecule hash consists of connected components hash with counter struct HashElement { HashElement () {} int count; dword hash; void clear() { hash = 0; count = 0; } }; typedef ReusableObjArray Hash; const Hash& getQueryHash () const; void loadTarget (const Array &molfile_buf); void loadTarget (Scanner &scanner); void loadTarget (const char *target); bool matchLoadedTarget (); bool matchBinary (Scanner &scanner, Scanner *xyz_scanner); bool matchBinary (const Array &target_buf, const Array *xyz_buf); void setParameters (const char *conditions); bool parse (const char *params); bool needCoords () const; bool needComponentMatching () const; static void calculateHash (Molecule &mol, Hash &hash); protected: BingoContext &_context; Molecule _query; Molecule _target; Hash _query_hash; int _flags; float _rms_threshold; void _initQuery (Molecule &query); static void _initTarget (Molecule &target, bool from_database); static int vertexCode (Molecule &mol, int vertex_idx); }; class MangoSimilarity { public: MangoSimilarity (BingoContext &context); enum { BIT_METRICS_TANIMOTO, BIT_METRICS_TVERSKY, BIT_METRICS_EUCLID_SUB }; struct Metrics { Metrics(int type = -1) : type(type) {} // Metric type int type; // Additional parameters for tversky metric float tversky_alpha, tversky_beta; }; Metrics metrics; float bottom, top; bool include_bottom; bool include_top; static Metrics whichMetrics (const char *metrics_str); void setMetrics (const char *metrics_str); void loadQuery (const Array &buf); void loadQuery (Scanner &scanner); void loadQuery (const char *str); float calc (const Array &target_buf); float calc (Scanner &scanner); // upper and lower bounds for common ones int getLowerBound (int target_ones); int getUpperBound (int target_ones); bool match (int ones_target, int ones_common); bool matchBinary (Scanner &scanner); const byte * getQueryFingerprint (); // Returns stored similarity score after calc, match or matchBinary float getSimilarityScore (); DECL_ERROR; protected: BingoContext &_context; Array _query_fp; int _query_ones; float _numerator_value, _denominator_value; static void _initQuery (Molecule &query); static float _similarity (int ones1, int ones2, int ones_common, Metrics metrics, float &num, float &den); static float _numerator (int ones1, int ones2, int ones_common, Metrics metrics); static float _denominator (int ones1, int ones2, int ones_common, Metrics metrics); }; class MangoTautomer { public: struct Params { int conditions; bool force_hydrogens; bool ring_chain; bool substructure; }; MangoTautomer (BingoContext &context); void loadQuery (const Array &buf); void loadQuery (Scanner &scanner); void loadQuery (const char *str); void setParams (int conditions, bool force_hydrogens, bool ring_chain, bool substructure); void setParameters (const char *conditions); const char * getQueryGross (); const byte * getQueryFingerprint (); void loadTarget (Scanner &scanner); void loadTarget (const Array &molfile_buf); void loadTarget (const char *target); bool matchLoadedTarget (); bool matchBinary (const Array &target_buf); bool matchBinary (Scanner &scanner); void getHighlightedTarget (Array &molfile_buf); bool preserve_bonds_on_highlighting; DECL_ERROR; Params _params; bool parseSub (const char *params); bool parseExact (const char *params); protected: BingoContext &_context; AutoPtr _query; Molecule _target; Array _query_gross_str; Array _query_fp; bool _query_data_valid; Array _target_bond_types; void _validateQueryData (); void _initTarget (bool from_database); }; class MangoGross { public: MangoGross (BingoContext &context); // query like '> C6 H6' void parseQuery (const char *query); void parseQuery (const Array &query); void parseQuery (Scanner &scanner); // SLQ conditions like 'CNT_C > 6 AND CNT_H > 6' const char * getConditions (); bool checkGross (const Array &target_gross); bool checkGross (const char *target_gross_str); bool checkMolecule (const Array &target_buf); bool checkMolecule (Scanner &scanner); DECL_ERROR; protected: BingoContext &_context; Array _query_gross; int _sign; Array _conditions; }; class MangoMass { public: // Mass bounds float top, bottom; }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_similarity.cpp000066400000000000000000000220421271037650300251150ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_matchers.h" #include "base_c/bitarray.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molecule_fingerprint.h" #include "core/bingo_error.h" #include "base_cpp/scanner.h" #include "core/bingo_context.h" #include "molecule/cmf_loader.h" #include "molecule/molecule_arom.h" IMPL_ERROR(MangoSimilarity, "mango similarity"); MangoSimilarity::MangoSimilarity (BingoContext &context) : _context(context) { metrics.type = BIT_METRICS_TANIMOTO; bottom = -0.1f; // to ensure that 0 is included top = 1.1f; // to ensure that 1 is included _numerator_value = 0; _denominator_value = 0; } MangoSimilarity::Metrics MangoSimilarity::whichMetrics (const char *metrics_str) { if (metrics_str == 0 || metrics_str[0] == 0) return Metrics(BIT_METRICS_TANIMOTO); // Expecting "tversky" or "tversky " const char *TVERSKY = "tversky"; if (strncasecmp(metrics_str, TVERSKY, strlen(TVERSKY)) == 0) { Metrics metrics(BIT_METRICS_TVERSKY); metrics.tversky_alpha = 0.5f; metrics.tversky_beta = 0.5f; if (strcasecmp(metrics_str, TVERSKY) != 0) { // Try to parse alpha and beta const char *params = metrics_str + strlen(TVERSKY); BufferScanner scanner(params); if (!scanner.tryReadFloat(metrics.tversky_alpha)) throw Error("unknown metrics: %s", metrics_str); scanner.skipSpace(); if (!scanner.tryReadFloat(metrics.tversky_beta)) throw Error("unknown metrics: %s", metrics_str); } return metrics; } if (strcasecmp(metrics_str, "tanimoto") == 0) return Metrics(BIT_METRICS_TANIMOTO); if (strcasecmp(metrics_str, "euclid-sub") == 0) return Metrics(BIT_METRICS_EUCLID_SUB); throw Error("unknown metrics: %s", metrics_str); } void MangoSimilarity::setMetrics (const char *metrics_str) { metrics = whichMetrics(metrics_str); } void MangoSimilarity::loadQuery (Scanner &scanner) { QS_DEF(Molecule, query); MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(query); _initQuery(query); MoleculeFingerprintBuilder builder(query, _context.fp_parameters); builder.skip_tau = true; builder.skip_ext = true; builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); _query_fp.copy(builder.get(), _context.fp_parameters.fingerprintSize()); _query_ones = builder.countBits_Sim(); } void MangoSimilarity::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void MangoSimilarity::loadQuery (const char *str) { BufferScanner scanner(str); loadQuery(scanner); } void MangoSimilarity::_initQuery (Molecule &query) { MoleculeAromatizer::aromatizeBonds(query, AromaticityOptions::BASIC); } float MangoSimilarity::_similarity (int ones1, int ones2, int ones_common, Metrics metrics, float &numerator, float &denominator) { numerator = _numerator(ones1, ones2, ones_common, metrics); if (numerator < 1e-6f) return 0; denominator = _denominator(ones1, ones2, ones_common, metrics); if (denominator < 1e-6f) throw Error("attempt to divide by zero"); return (float)numerator / denominator; } float MangoSimilarity::getSimilarityScore () { return (float)_numerator_value / _denominator_value; } float MangoSimilarity::_numerator (int ones1, int ones2, int ones_common, Metrics metrics) { switch (metrics.type) { case BIT_METRICS_TANIMOTO: case BIT_METRICS_EUCLID_SUB: return (float)ones_common; case BIT_METRICS_TVERSKY: return (float)ones_common; default: throw Error("bad metrics: %d", metrics.type); } } float MangoSimilarity::_denominator (int ones1, int ones2, int ones_common, Metrics metrics) { switch (metrics.type) { case BIT_METRICS_TANIMOTO: return (float)(ones1 + ones2 - ones_common); case BIT_METRICS_TVERSKY: return metrics.tversky_alpha * ones1 + metrics.tversky_beta * ones2 + (1 - metrics.tversky_alpha - metrics.tversky_beta) * ones_common; case BIT_METRICS_EUCLID_SUB: return (float)ones1; default: throw Error("bad metrics: %d", metrics.type); } } int MangoSimilarity::getLowerBound (int target_ones) { switch (metrics.type) { case BIT_METRICS_TVERSKY: return (int)(ceil( (metrics.tversky_alpha * _query_ones + metrics.tversky_beta * target_ones) * bottom / (1 - bottom * (1 - metrics.tversky_alpha - metrics.tversky_beta)))); case BIT_METRICS_TANIMOTO: if (bottom < -0.5) throw Error("bad lower bound: %lf", bottom); return (int)(ceil((_query_ones + target_ones) * bottom / (bottom + 1.0))); case BIT_METRICS_EUCLID_SUB: return (int)(ceil(_query_ones * bottom)); default: throw Error("bad metrics: %d", metrics.type); } } int MangoSimilarity::getUpperBound (int target_ones) { switch (metrics.type) { case BIT_METRICS_TVERSKY: return (int)(floor( (metrics.tversky_alpha * _query_ones + metrics.tversky_beta * target_ones) * top / (1 - top * (1 - metrics.tversky_alpha - metrics.tversky_beta)))); case BIT_METRICS_TANIMOTO: return (int)(floor((_query_ones + target_ones) * top / (top + 1.0))); case BIT_METRICS_EUCLID_SUB: return (int)(floor(_query_ones * top)); default: throw Error("bad metrics: %d", metrics.type); } } bool MangoSimilarity::match (int ones_target, int ones_common) { _numerator_value = _numerator(_query_ones, ones_target, ones_common, metrics); _denominator_value = _denominator(_query_ones, ones_target, ones_common, metrics); if (_denominator_value < 1e-6f && _numerator_value > 1e-6f) throw Error("attempt to divide by zero"); float top_lim = top * _denominator_value; if (include_top && _numerator_value > top_lim + 1e-6f) return false; if (!include_top && _numerator_value > top_lim - 1e-6f) return false; float bottom_lim = bottom * _denominator_value; if (_numerator_value < 1e-6 && _denominator_value < 1e-6) { // Similarity is zero if (bottom > 0 || (!include_bottom && bottom >= 0)) return false; return true; } if (include_bottom && _numerator_value < bottom_lim - 1e-6f) return false; if (!include_bottom && _numerator_value < bottom_lim + 1e-6f) return false; return true; } float MangoSimilarity::calc (Scanner &scanner) { QS_DEF(Molecule, target); MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(target); MoleculeAromatizer::aromatizeBonds(target, AromaticityOptions::BASIC); QS_DEF(Array, target_fp); MoleculeFingerprintBuilder builder(target, _context.fp_parameters); builder.skip_tau = true; builder.skip_ext = true; builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); target_fp.copy(builder.get(), _context.fp_parameters.fingerprintSize()); int target_ones = builder.countBits_Sim(); int common = bitCommonOnes(_query_fp.ptr(), target_fp.ptr(), _context.fp_parameters.fingerprintSize()); return _similarity(_query_ones, target_ones, common, metrics, _numerator_value, _denominator_value); } float MangoSimilarity::calc (const Array &target_buf) { BufferScanner scanner(target_buf); return calc(scanner); } bool MangoSimilarity::matchBinary (Scanner &scanner) { QS_DEF(Molecule, target); CmfLoader loader(_context.cmf_dict, scanner); loader.skip_cistrans = true; loader.skip_stereocenters = true; loader.skip_valence = true; loader.loadMolecule(target); MoleculeFingerprintBuilder builder(target, _context.fp_parameters); builder.skip_tau = true; builder.skip_ext = true; builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); int common_ones = bitCommonOnes(builder.get(), _query_fp.ptr(), _context.fp_parameters.fingerprintSize()); int target_ones = builder.countBits_Sim(); return match(target_ones, common_ones); } const byte * MangoSimilarity::getQueryFingerprint () { return _query_fp.ptr(); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_substructure.cpp000066400000000000000000000233651271037650300255120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_matchers.h" #include "base_cpp/scanner.h" #include "core/bingo_error.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molfile_saver.h" #include "core/bingo_context.h" #include "base_c/nano.h" #include "base_cpp/output.h" #include "layout/molecule_layout.h" #include "molecule/smiles_loader.h" #include "molecule/smiles_saver.h" #include "core/mango_index.h" #include "base_cpp/profiling.h" #include "base_cpp/ptr_pool.h" IMPL_ERROR(MangoSubstructure, "substructure"); MangoSubstructure::MangoSubstructure (BingoContext &context) : _context(context) { match_3d = 0; rms_threshold = 0; preserve_bonds_on_highlighting = false; _use_pi_systems_matcher = false; } void MangoSubstructure::loadQuery (Scanner &scanner) { MoleculeAutoLoader loader(scanner); QS_DEF(QueryMolecule, source); _context.setLoaderSettings(loader); loader.loadQueryMolecule(source); if (!source.have_xyz && match_3d != 0) throw Error("cannot do 3D match without XYZ in the query"); _initQuery(source, _query); _query_fp_valid = false; _query_extra_valid = false; } void MangoSubstructure::loadSMARTS (Scanner &scanner) { SmilesLoader loader(scanner); QS_DEF(QueryMolecule, source); _context.setLoaderSettings(loader); loader.loadSMARTS(source); if (!source.have_xyz && match_3d != 0) throw Error("cannot do 3D match without XYZ in the query"); _initSmartsQuery(source, _query); _query_fp_valid = false; _query_extra_valid = false; _use_pi_systems_matcher = false; } void MangoSubstructure::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void MangoSubstructure::loadQuery (const char *str) { BufferScanner scanner(str); loadQuery(scanner); } void MangoSubstructure::loadSMARTS (const Array &buf) { BufferScanner scanner(buf); loadSMARTS(scanner); } void MangoSubstructure::loadSMARTS (const char *str) { BufferScanner scanner(str); loadSMARTS(scanner); } void MangoSubstructure::_validateQueryFP () { if (_query_fp_valid) return; MoleculeFingerprintBuilder builder(_query, _context.fp_parameters); builder.query = true; builder.skip_sim = true; builder.skip_tau = true; // atom charges and bond types may not match in pi-systems if (_use_pi_systems_matcher) { builder.skip_ord = true; builder.skip_any_atoms = true; builder.skip_ext_charge = true; } builder.process(); _query_fp.copy(builder.get(), _context.fp_parameters.fingerprintSize()); _query_fp_valid = true; } void MangoSubstructure::_validateQueryExtraData () { if (_query_extra_valid) return; _query_has_stereocenters = _query.stereocenters.size() > 0; _query_has_stereocare_bonds = _query.cis_trans.count() > 0; _query_extra_valid = true; } void MangoSubstructure::loadTarget (const Array &molfile_buf) { BufferScanner scanner(molfile_buf); loadTarget(scanner); } void MangoSubstructure::loadTarget (const char *target) { BufferScanner scanner(target); loadTarget(scanner); } void MangoSubstructure::loadTarget (Scanner &scanner) { MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(_target); _initTarget(false); Molecule::checkForConsistency(_target); } bool MangoSubstructure::matchLoadedTarget () { MoleculeSubstructureMatcher matcher(_target); matcher.match_3d = match_3d; matcher.rms_threshold = rms_threshold; matcher.highlight = true; matcher.use_pi_systems_matcher = _use_pi_systems_matcher; matcher.setNeiCounters(&_nei_query_counters, &_nei_target_counters); matcher.fmcache = &_fmcache; _fmcache.clear(); matcher.setQuery(_query); profTimerStart(temb, "match.embedding"); bool res = matcher.find(); profTimerStop(temb); if (res) { profIncTimer("match.embedding_found", profTimerGetTime(temb)); } else { profIncTimer("match.embedding_not_found", profTimerGetTime(temb)); } return res; } void MangoSubstructure::loadBinaryTargetXyz (Scanner &xyz_scanner) { cmf_loader->loadXyz(xyz_scanner); } void MangoSubstructure::getHighlightedTarget (Array &molfile_buf) { ArrayOutput output(molfile_buf); MolfileSaver saver(output); if (!_target.have_xyz) { MoleculeLayout ml(_target); ml.make(); _target.clearBondDirections(); _target.stereocenters.markBonds(); _target.allene_stereo.markBonds(); } if (preserve_bonds_on_highlighting) Molecule::loadBondOrders(_target, _target_bond_types); saver.saveMolecule(_target); } void MangoSubstructure::getHighlightedTarget_Smiles (Array &smiles_buf) { ArrayOutput output(smiles_buf); SmilesSaver saver(output); if (preserve_bonds_on_highlighting) Molecule::loadBondOrders(_target, _target_bond_types); saver.saveMolecule(_target); } void MangoSubstructure::_correctQueryStereo (QueryMolecule &query) { // Remove stereobond marks that are connected with R-groups for (int v = query.vertexBegin(); v != query.vertexEnd(); v = query.vertexNext(v)) { if (!query.isRSite(v)) continue; const Vertex &vertex = query.getVertex(v); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int edge = vertex.neiEdge(nei); if (query.cis_trans.getParity(edge) != 0) query.cis_trans.setParity(edge, 0); } } MoleculeRGroups &rgroups = query.rgroups; int n_rgroups = rgroups.getRGroupCount(); for (int i = 1; i <= n_rgroups; i++) { PtrPool &frags = rgroups.getRGroup(i).fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { QueryMolecule &fragment = frags[j]->asQueryMolecule(); _correctQueryStereo(fragment); } } } void MangoSubstructure::_initQuery (QueryMolecule &query_in, QueryMolecule &query_out) { _correctQueryStereo(query_in); QueryMoleculeAromatizer::aromatizeBonds(query_in, AromaticityOptions::BASIC); _nei_query_counters.calculate(query_in); QS_DEF(Array, transposition); _nei_query_counters.makeTranspositionForSubstructure(query_in, transposition); query_out.makeSubmolecule(query_in, transposition, 0); _nei_query_counters.calculate(query_out); } void MangoSubstructure::_initSmartsQuery (QueryMolecule &query_in, QueryMolecule &query_out) { QS_DEF(Array, transposition); MoleculeSubstructureMatcher::makeTransposition(query_in, transposition); query_out.makeSubmolecule(query_in, transposition, 0); _nei_query_counters.calculate(query_out); query_out.optimize(); } void MangoSubstructure::_initTarget (bool from_database) { if (preserve_bonds_on_highlighting) Molecule::saveBondOrders(_target, _target_bond_types); if (!from_database) MoleculeAromatizer::aromatizeBonds(_target, AromaticityOptions::BASIC); _nei_target_counters.calculate(_target); } bool MangoSubstructure::needCoords () { return MoleculeSubstructureMatcher::needCoords(match_3d, _query); } bool MangoSubstructure::matchBinary (const Array &target_buf, const Array *xyz_buf) { BufferScanner scanner(target_buf); if (xyz_buf == 0) return matchBinary(scanner, 0); BufferScanner xyz_scanner(*xyz_buf); return matchBinary(scanner, &xyz_scanner); } bool MangoSubstructure::matchBinary (Scanner &scanner, Scanner *xyz_scanner) { _validateQueryExtraData(); profTimerStart(tcmf, "match.cmf"); cmf_loader.free(); cmf_loader.create(_context.cmf_dict, scanner); if (!_query_has_stereocare_bonds) cmf_loader->skip_cistrans = true; if (!_query_has_stereocenters) cmf_loader->skip_stereocenters = true; cmf_loader->loadMolecule(_target); if (xyz_scanner != 0) cmf_loader->loadXyz(*xyz_scanner); profTimerStop(tcmf); profTimerStart(tinit, "match.init_target"); _initTarget(true); profTimerStop(tinit); return matchLoadedTarget(); } bool MangoSubstructure::parse (const char *params) { match_3d = 0; rms_threshold = 0; _use_pi_systems_matcher = false; preserve_bonds_on_highlighting = false; if (params == 0) return true; BufferScanner scanner(params); QS_DEF(Array, word); scanner.skipSpace(); while (!scanner.isEOF()) { scanner.skipSpace(); scanner.readWord(word, 0); bool is_aff = strcasecmp(word.ptr(), "AFF") == 0; bool is_conf = strcasecmp(word.ptr(), "CONF") == 0; if (is_aff || is_conf) { if (match_3d != 0) return false; if (is_aff) match_3d = MoleculeSubstructureMatcher::AFFINE; if (is_conf) match_3d = MoleculeSubstructureMatcher::CONFORMATION; scanner.skipSpace(); rms_threshold = scanner.readFloat(); } else if (strcasecmp(word.ptr(), "RES") == 0) _use_pi_systems_matcher = true; else return false; } return true; } const byte * MangoSubstructure::getQueryFingerprint () { _validateQueryFP(); return _query_fp.ptr(); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/mango_tautomer.cpp000066400000000000000000000154351271037650300245770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/mango_matchers.h" #include "molecule/molecule_auto_loader.h" #include "base_cpp/scanner.h" #include "molecule/molecule_fingerprint.h" #include "core/bingo_error.h" #include "molecule/gross_formula.h" #include "molecule/molecule_tautomer_matcher.h" #include "molecule/molfile_saver.h" #include "core/bingo_context.h" #include "base_cpp/output.h" #include "molecule/cmf_loader.h" #include "layout/molecule_layout.h" #include "molecule/elements.h" #include "base_cpp/profiling.h" IMPL_ERROR(MangoTautomer, "tautomer matcher"); MangoTautomer::MangoTautomer (BingoContext &context) : _context(context) { preserve_bonds_on_highlighting = false; } void MangoTautomer::loadQuery (Scanner &scanner) { MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); if (_params.substructure) { _query.reset(new QueryMolecule()); loader.loadQueryMolecule((QueryMolecule &)_query.ref()); } else { _query.reset(new Molecule()); loader.loadMolecule((Molecule &)_query.ref()); } _query_data_valid = false; } void MangoTautomer::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void MangoTautomer::loadQuery (const char *str) { BufferScanner scanner(str); loadQuery(scanner); } const char * MangoTautomer::getQueryGross () { _validateQueryData(); return _query_gross_str.ptr(); } void MangoTautomer::setParams (int conditions, bool force_hydrogens, bool ring_chain, bool substructure) { _params.conditions = conditions; _params.force_hydrogens = force_hydrogens; _params.ring_chain = ring_chain; _params.substructure = substructure; } void MangoTautomer::setParameters (const char *conditions) { TautomerMethod m = RSMARTS; MoleculeTautomerMatcher::parseConditions(conditions, _params.conditions, _params.force_hydrogens, _params.ring_chain, m); } void MangoTautomer::_validateQueryData () { if (_query_data_valid) return; if (_params.substructure) { QS_DEF(QueryMolecule, aromatized_query); aromatized_query.clone(_query.ref(), 0, 0); QueryMoleculeAromatizer::aromatizeBonds(aromatized_query, AromaticityOptions::BASIC); MoleculeFingerprintBuilder builder(aromatized_query, _context.fp_parameters); builder.query = true; builder.skip_ord = true; builder.skip_sim = true; // Tautomer fingerprint part does already contain all necessary any-bits builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); _query_fp.copy(builder.get(), _context.fp_parameters.fingerprintSize()); } else { QS_DEF(Array, gross); GrossFormula::collect(_query.ref(), gross); gross[ELEM_H] = 0; GrossFormula::toString(gross, _query_gross_str); } _query_data_valid = true; } void MangoTautomer::loadTarget (const char *target) { BufferScanner scanner(target); loadTarget(scanner); } void MangoTautomer::loadTarget (Scanner &scanner) { MoleculeAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadMolecule(_target); _initTarget(false); } void MangoTautomer::loadTarget (const Array &molfile_buf) { BufferScanner scanner(molfile_buf); loadTarget(scanner); } bool MangoTautomer::matchLoadedTarget () { MoleculeTautomerMatcher matcher(_target, _params.substructure); TautomerMethod m = RSMARTS; matcher.setRulesList(&_context.tautomer_rules); matcher.setRules(_params.conditions, _params.force_hydrogens, _params.ring_chain, m); matcher.setQuery(_query.ref()); matcher.highlight = true; return matcher.find(); } void MangoTautomer::getHighlightedTarget (Array &molfile_buf) { ArrayOutput output(molfile_buf); MolfileSaver saver(output); if (!_target.have_xyz) { MoleculeLayout ml(_target); ml.make(); _target.clearBondDirections(); _target.stereocenters.markBonds(); _target.allene_stereo.markBonds(); } if (preserve_bonds_on_highlighting) Molecule::loadBondOrders(_target, _target_bond_types); saver.saveMolecule(_target); } void MangoTautomer::_initTarget (bool from_database) { if (preserve_bonds_on_highlighting) Molecule::saveBondOrders(_target, _target_bond_types); if (!from_database) MoleculeAromatizer::aromatizeBonds(_target, AromaticityOptions::BASIC); } bool MangoTautomer::matchBinary (const Array &target_buf) { BufferScanner scanner(target_buf); return matchBinary(scanner); } bool MangoTautomer::matchBinary (Scanner &scanner) { CmfLoader loader(_context.cmf_dict, scanner); loader.loadMolecule(_target); _initTarget(true); TautomerMethod m = RSMARTS; MoleculeTautomerMatcher matcher(_target, _params.substructure); matcher.setRulesList(&_context.tautomer_rules); matcher.setRules(_params.conditions, _params.force_hydrogens, _params.ring_chain, m); matcher.setQuery(_query.ref()); profTimerStart(temb, "match.embedding"); bool res = matcher.find(); profTimerStop(temb); if (res) { profIncTimer("match.embedding_found", profTimerGetTime(temb)); } else { profIncTimer("match.embedding_not_found", profTimerGetTime(temb)); } return res; } bool MangoTautomer::parseSub (const char *params) { if (params == 0) return false; preserve_bonds_on_highlighting = false; BufferScanner scanner(params); scanner.skipSpace(); if (scanner.isEOF()) return false; QS_DEF(Array, word); scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "TAU") != 0) return false; setParameters(params); _params.substructure = true; return true; } bool MangoTautomer::parseExact (const char *params) { if (params == 0) return false; preserve_bonds_on_highlighting = false; BufferScanner scanner(params); scanner.skipSpace(); QS_DEF(Array, word); scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "TAU") != 0) return false; setParameters(params); _params.substructure = false; return true; } const byte * MangoTautomer::getQueryFingerprint () { _validateQueryData(); return _query_fp.ptr(); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_aam.cpp000066400000000000000000000041701271037650300235040ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/ringo_matchers.h" #include "core/bingo_context.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rxnfile_saver.h" #include "reaction/reaction_automapper.h" #include "layout/reaction_layout.h" IMPL_ERROR(RingoAAM, "ringo AAM"); RingoAAM::RingoAAM(BingoContext &context) : _context(context) { } void RingoAAM::loadReaction (const Array &buf) { BufferScanner scanner(buf); loadReaction(scanner); } void RingoAAM::loadReaction (const char *str) { BufferScanner scanner(str); loadReaction(scanner); } void RingoAAM::loadReaction (Scanner &scanner) { ReactionAutoLoader rxd(scanner); _context.setLoaderSettings(rxd); rxd.loadReaction(_reaction); } void RingoAAM::parse(const char *mode) { if (strcasecmp(mode, "CLEAR") == 0){ _reaction.clearAAM(); return; } ReactionAutomapper ram(_reaction); if (strcasecmp(mode, "DISCARD") == 0){ ram.automap(ReactionAutomapper::AAM_REGEN_DISCARD); } else if (strcasecmp(mode, "ALTER") == 0){ ram.automap(ReactionAutomapper::AAM_REGEN_ALTER); } else if (strcasecmp(mode, "KEEP") == 0){ ram.automap(ReactionAutomapper::AAM_REGEN_KEEP); } else throw Error("unknown mode: %s", mode); } void RingoAAM::getResult(Array &buf) { ArrayOutput output_r(buf); RxnfileSaver rcs(output_r); if (!Reaction::haveCoord(_reaction)){ ReactionLayout layout(_reaction); layout.make(); _reaction.markStereocenterBonds(); } rcs.saveReaction(_reaction); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_context.cpp000066400000000000000000000066001271037650300244320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/ringo_context.h" #include "core/bingo_context.h" TL_DEF(RingoContext, PtrArray, _instances); OsLock RingoContext::_instances_lock; IMPL_ERROR(RingoContext, "ringo context"); RingoContext::RingoContext (BingoContext &context) : substructure(context), exact(context), ringoAAM(context), _context(context) { } RingoContext::~RingoContext () { } RingoContext * RingoContext::_get (int id, BingoContext &context) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; return 0; } RingoContext * RingoContext::existing (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; throw Error("context #%d not found", id); } RingoContext * RingoContext::get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) return _instances[i]; BingoContext *bingo_context = BingoContext::get(id); return &_instances.add(new RingoContext(*bingo_context)); } void RingoContext::remove (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id == id) break; //if (i == _instances.size()) // throw Error("remove(): context #%d not found", id); if (i != _instances.size()) _instances.remove(i); } int RingoContext::begin () { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); if (_instances.size() < 1) return 0; int i, min_id = _instances[0]->_context.id; for (i = 1; i < _instances.size(); i++) if (_instances[i]->_context.id < min_id) min_id = _instances[i]->_context.id; return min_id; } int RingoContext::end () { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); if (_instances.size() < 1) return 0; int i, max_id = _instances[0]->_context.id; for (i = 1; i < _instances.size(); i++) if (_instances[i]->_context.id > max_id) max_id = _instances[i]->_context.id; return max_id + 1; } int RingoContext::next (int k) { int i, next_id = end(); OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (i = 0; i < _instances.size(); i++) if (_instances[i]->_context.id > k && _instances[i]->_context.id < next_id) next_id = _instances[i]->_context.id; return next_id; } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_context.h000066400000000000000000000026521271037650300241020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_context__ #define __ringo_context__ #include "core/ringo_matchers.h" #include "core/ringo_index.h" #include "base_cpp/tlscont.h" using namespace indigo; namespace ingido { class BingoContext; } class RingoContext { public: explicit RingoContext (BingoContext &context); virtual ~RingoContext (); RingoSubstructure substructure; RingoExact exact; RingoAAM ringoAAM; DECL_ERROR; static int begin (); static int end (); static int next (int k); static void remove (int id); static RingoContext * existing (int id); static RingoContext * get (int id); protected: static RingoContext * _get (int id, BingoContext &context); TL_DECL(PtrArray, _instances); static OsLock _instances_lock; BingoContext &_context; }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_exact.cpp000066400000000000000000000150711271037650300240540ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/ringo_matchers.h" #include "reaction/reaction_auto_loader.h" #include "molecule/elements.h" #include "graph/subgraph_hash.h" #include "base_cpp/scanner.h" #include "reaction/reaction_exact_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "reaction/crf_loader.h" #include "core/bingo_context.h" #include "base_cpp/crc32.h" #include "base_cpp/output.h" IMPL_ERROR(RingoExact, "Ringo exact"); RingoExact::RingoExact (BingoContext &context) : _context(context) { _flags = 0; } void RingoExact::loadQuery (Scanner &scanner) { ReactionAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadReaction(_query); Reaction::checkForConsistency(_query); _initQuery(_query); _query_hash = calculateHash(_query); } dword RingoExact::calculateHash (Reaction &rxn) { QS_DEF(Molecule, mol_without_h) ; QS_DEF(Array, vertices); int i, j; dword hash = 0; for (j = rxn.begin(); j != rxn.end(); j = rxn.next(j)) { Molecule &mol = rxn.getMolecule(j); vertices.clear(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.getAtomNumber(i) != ELEM_H) vertices.push(i); mol_without_h.makeSubmolecule(mol, vertices, 0); SubgraphHash hh(mol_without_h); hash += hh.getHash(); } return hash; } void RingoExact::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void RingoExact::loadQuery (const char *buf) { BufferScanner scanner(buf); loadQuery(scanner); } dword RingoExact::getQueryHash () { return _query_hash; } const char * RingoExact::getQueryHashStr () { ArrayOutput out(_query_hash_str); out.printf("%02X", getQueryHash()); _query_hash_str.push(0); return _query_hash_str.ptr(); } void RingoExact::setParameters (const char *flags) { // TODO: merge Indigo code into Bingo and stop this endless copy-paste if (flags == 0) flags = ""; static struct { const char *token; int value; } token_list[] = { {"ELE", MoleculeExactMatcher::CONDITION_ELECTRONS}, {"MAS", MoleculeExactMatcher::CONDITION_ISOTOPE}, {"STE", MoleculeExactMatcher::CONDITION_STEREO}, {"AAM", ReactionExactMatcher::CONDITION_AAM}, {"RCT", ReactionExactMatcher::CONDITION_REACTING_CENTERS} }; int i, res = 0, count = 0; bool had_none = false; bool had_all = false; BufferScanner scanner(flags); QS_DEF(Array, word); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "NONE") == 0) { if (had_all) throw Error("NONE conflicts with ALL"); had_none = true; count++; continue; } if (strcasecmp(word.ptr(), "ALL") == 0) { if (had_none) throw Error("ALL conflicts with NONE"); had_all = true; res = MoleculeExactMatcher::CONDITION_ALL | ReactionExactMatcher::CONDITION_ALL; count++; continue; } for (i = 0; i < NELEM(token_list); i++) { if (strcasecmp(token_list[i].token, word.ptr()) == 0) { if (had_all) throw Error("only negative flags are allowed together with ALL"); res |= token_list[i].value; break; } if (word[0] == '-' && strcasecmp(token_list[i].token, word.ptr() + 1) == 0) { res &= ~token_list[i].value; break; } } if (i == NELEM(token_list)) throw Error("unknown token %s", word.ptr()); count++; } if (had_none && count > 1) throw Error("no flags are allowed together with NONE"); if (count == 0) res = MoleculeExactMatcher::CONDITION_ALL | ReactionExactMatcher::CONDITION_ALL; _flags = res; } void RingoExact::loadTarget (Scanner &scanner) { ReactionAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadReaction(_target); Reaction::checkForConsistency(_target); _initTarget(_target, false); } void RingoExact::loadTarget (const Array &target_buf) { BufferScanner scanner(target_buf); loadTarget(scanner); } void RingoExact::loadTarget (const char *target) { BufferScanner scanner(target); loadTarget(scanner); } bool RingoExact::matchLoadedTarget () { ReactionExactMatcher matcher(_query, _target); matcher.flags = _flags; return matcher.find(); } void RingoExact::_initQuery (Reaction &query) { int i, j; query.aromatize(AromaticityOptions::BASIC); if (_flags & MoleculeExactMatcher::CONDITION_STEREO) { for (j = query.begin(); j != query.end(); j = query.next(j)) { Molecule &mol = query.getMolecule(j); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.getEdgeTopology(i) == TOPOLOGY_RING) mol.cis_trans.setParity(i, 0); } } } void RingoExact::_initTarget (Reaction &target, bool from_database) { if (!from_database) target.aromatize(AromaticityOptions::BASIC); } bool RingoExact::matchBinary (Scanner &scanner) { CrfLoader loader(_context.cmf_dict, scanner); loader.loadReaction(_target); _initTarget(_target, true); ReactionExactMatcher matcher(_query, _target); matcher.flags = _flags; return matcher.find(); } bool RingoExact::matchBinary (const Array &target_buf) { BufferScanner scanner(target_buf); return matchBinary(scanner); } int RingoExact::_vertex_code (Graph &graph, int vertex_idx, void *context) { Molecule &mol = (Molecule &)graph; if (mol.isPseudoAtom(vertex_idx)) return CRC32::get(mol.getPseudoAtom(vertex_idx)); if (mol.isRSite(vertex_idx)) return ELEM_RSITE; return mol.getAtomNumber(vertex_idx); } int RingoExact::_edge_code (Graph &graph, int edge_idx, void *context) { Molecule &mol = (Molecule &)graph; return mol.getBondOrder(edge_idx); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_index.cpp000066400000000000000000000051111271037650300240510ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/ringo_index.h" #include "base_cpp/output.h" #include "base_cpp/os_sync_wrapper.h" #include "core/mango_index.h" #include "core/bingo_context.h" #include "reaction/reaction_fingerprint.h" #include "reaction/reaction_automapper.h" #include "reaction/crf_saver.h" #include "reaction/reaction_auto_loader.h" #include "reaction/reaction.h" #include "core/ringo_matchers.h" void RingoIndex::prepare (Scanner &rxnfile, Output &output, OsLock *lock_for_exclusive_access) { QS_DEF(Reaction, reaction); ReactionAutoLoader rrd(rxnfile); _context->setLoaderSettings(rrd); rrd.loadReaction(reaction); // Skip all SGroups for (int mol_idx = reaction.begin(); mol_idx != reaction.end(); mol_idx = reaction.next(mol_idx)) reaction.getBaseMolecule(mol_idx).clearSGroups(); Reaction::checkForConsistency(reaction); ReactionAutomapper ram(reaction); ram.correctReactingCenters(true); reaction.aromatize(AromaticityOptions::BASIC); _hash = RingoExact::calculateHash(reaction); { ArrayOutput out(_hash_str); out.printf("%02X", _hash); _hash_str.push(0); } if (!skip_calculate_fp) { ReactionFingerprintBuilder builder(reaction, _context->fp_parameters); builder.process(); _fp.copy(builder.get(), _context->fp_parameters.fingerprintSizeExtOrdSim() * 2); } ArrayOutput output_crf(_crf); { // CrfSaver modifies _context->cmf_dict and // requires exclusive access for this OsLockerNullable locker(lock_for_exclusive_access); CrfSaver saver(_context->cmf_dict, output_crf); saver.saveReaction(reaction); } output.writeArray(_crf); } const byte * RingoIndex::getFingerprint () { return _fp.ptr(); } const Array & RingoIndex::getCrf () { return _crf; } dword RingoIndex::getHash () { return _hash; } const char * RingoIndex::getHashStr () { return _hash_str.ptr(); } void RingoIndex::clear () { _fp.clear(); _crf.clear(); } Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_index.h000066400000000000000000000023451271037650300235240ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_index__ #define __ringo_index__ #include "base_cpp/array.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "core/bingo_index.h" using namespace indigo; class BingoContext; class RingoIndex : public BingoIndex { public: virtual void prepare (Scanner &rxnfile, Output &fi_output, OsLock *lock_for_exclusive_access); const byte * getFingerprint (); const Array & getCrf (); dword getHash (); const char * getHashStr (); void clear (); private: Array _fp; Array _crf; dword _hash; Array _hash_str; }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_matchers.h000066400000000000000000000072321271037650300242230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_matchers__ #define __ringo_matchers__ #include "molecule/molecule.h" #include "base_cpp/tlscont.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/reaction_neighborhood_counters.h" using namespace indigo; namespace indigo { class Scanner; } class BingoContext; class RingoSubstructure { public: explicit RingoSubstructure (BingoContext &context); virtual ~RingoSubstructure (); bool parse (const char *params); bool matchBinary (const Array &buf); bool matchBinary (Scanner &scanner); void loadTarget (const Array &buf); void loadTarget (Scanner &scanner); void loadTarget (const char *str); bool matchLoadedTarget (); void loadQuery (const Array &buf); void loadQuery (const char *str); void loadQuery (Scanner &scanner); void loadSMARTS (const Array &buf); void loadSMARTS (const char *str); void loadSMARTS (Scanner &scanner); bool preserve_bonds_on_highlighting; void getHighlightedTarget (Array &buf); const byte * getQueryFingerprint (); DECL_ERROR; protected: BingoContext &_context; QueryReaction _query_reaction; Reaction _target_reaction; ReactionAtomNeighbourhoodCounters _nei_target_counters; ReactionAtomNeighbourhoodCounters _nei_query_counters; ObjArray< Array > _target_bond_types; Array _query_fp; bool _query_data_valid; bool _smarts; void _validateQueryData (); void _initQuery (QueryReaction &query_in, QueryReaction &query_out); void _initSmartsQuery (QueryReaction &query_in, QueryReaction &query_out); void _initTarget (bool from_database); }; class RingoAAM { public: RingoAAM(BingoContext &context); void parse (const char* mode); void loadReaction (const Array &buf); void loadReaction (const char *str); void loadReaction (Scanner &scanner); void getResult (Array &buf); DECL_ERROR; protected: BingoContext &_context; Reaction _reaction; }; class RingoExact { public: RingoExact (BingoContext &context); void loadQuery (const Array &buf); void loadQuery (Scanner &scanner); void loadQuery (const char *buf); dword getQueryHash (); const char * getQueryHashStr (); void loadTarget (const Array &molfile_buf); void loadTarget (Scanner &scanner); void loadTarget (const char *target); bool matchLoadedTarget (); bool matchBinary (Scanner &scanner); bool matchBinary (const Array &target_buf); void setParameters (const char *conditions); static dword calculateHash (Reaction &rxn); DECL_ERROR; protected: BingoContext &_context; Reaction _query; Reaction _target; dword _query_hash; int _flags; Array _query_hash_str; void _initQuery (Reaction &query); static void _initTarget (Reaction &target, bool from_database); static int _vertex_code (Graph &graph, int vertex_idx, void *context); static int _edge_code (Graph &graph, int edge_idx, void *context); }; #endif Indigo-indigo-1.2.3/bingo/bingo-core/src/core/ringo_substructure.cpp000066400000000000000000000134711271037650300255240ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "core/ringo_matchers.h" #include "core/bingo_context.h" #include "base_cpp/scanner.h" #include "base_cpp/profiling.h" #include "reaction/rxnfile_loader.h" #include "reaction/rxnfile_saver.h" #include "reaction/reaction_substructure_matcher.h" #include "reaction/reaction_automapper.h" #include "molecule/molecule_fingerprint.h" #include "reaction/reaction_fingerprint.h" #include "reaction/crf_loader.h" #include "base_cpp/output.h" #include "reaction/reaction_auto_loader.h" #include "layout/reaction_layout.h" #include "reaction/rsmiles_loader.h" IMPL_ERROR(RingoSubstructure, "reaction substructure"); RingoSubstructure::RingoSubstructure (BingoContext &context) : _context(context) { preserve_bonds_on_highlighting = false; _smarts = false; } RingoSubstructure::~RingoSubstructure () { } bool RingoSubstructure::parse (const char *params) { preserve_bonds_on_highlighting = false; return true; } void RingoSubstructure::loadQuery (const Array &buf) { BufferScanner scanner(buf); loadQuery(scanner); } void RingoSubstructure::loadQuery (const char *str) { BufferScanner scanner(str); loadQuery(scanner); } void RingoSubstructure::loadQuery (Scanner &scanner) { QS_DEF(QueryReaction, source); ReactionAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadQueryReaction(source); _initQuery(source, _query_reaction); _query_data_valid = false; _smarts = false; } void RingoSubstructure::loadSMARTS (Scanner &scanner) { RSmilesLoader loader(scanner); QS_DEF(QueryReaction, source); loader.smarts_mode = true; loader.loadQueryReaction(source); _initSmartsQuery(source, _query_reaction); _query_data_valid = false; _smarts = true; } void RingoSubstructure::loadSMARTS (const Array &buf) { BufferScanner scanner(buf); loadSMARTS(scanner); } void RingoSubstructure::loadSMARTS (const char *str) { BufferScanner scanner(str); loadSMARTS(scanner); } void RingoSubstructure::_initQuery (QueryReaction &query_in, QueryReaction &query_out) { query_out.makeTransposedForSubstructure(query_in); ReactionAutomapper ram(query_out); ram.correctReactingCenters(true); query_out.aromatize(AromaticityOptions::BASIC); _nei_query_counters.calculate(query_out); } void RingoSubstructure::_initSmartsQuery (QueryReaction &query_in, QueryReaction &query_out) { query_out.makeTransposedForSubstructure(query_in); ReactionAutomapper ram(query_out); ram.correctReactingCenters(true); _nei_query_counters.calculate(query_out); } void RingoSubstructure::_validateQueryData () { if (_query_data_valid) return; ReactionFingerprintBuilder builder(_query_reaction, _context.fp_parameters); builder.query = true; builder.skip_sim = true; builder.process(); _query_fp.copy(builder.get(), _context.fp_parameters.fingerprintSizeExtOrdSim() * 2); _query_data_valid = true; } void RingoSubstructure::_initTarget (bool from_database) { if (preserve_bonds_on_highlighting) Reaction::saveBondOrders(_target_reaction, _target_bond_types); if (!from_database) { ReactionAutomapper ram(_target_reaction); ram.correctReactingCenters(true); _target_reaction.aromatize(AromaticityOptions::BASIC); } _nei_target_counters.calculate(_target_reaction); } bool RingoSubstructure::matchBinary (const Array &buf) { BufferScanner scanner(buf); return matchBinary(scanner); } bool RingoSubstructure::matchBinary (Scanner &scanner) { CrfLoader loader(_context.cmf_dict, scanner); loader.loadReaction(_target_reaction); _initTarget(true); ReactionSubstructureMatcher rsm(_target_reaction); rsm.setQuery(_query_reaction); rsm.highlight = true; if (_smarts) rsm.use_daylight_aam_mode = true; //rsm.setNeiCounters(&_nei_query_counters, &_nei_target_counters); return rsm.find(); } void RingoSubstructure::loadTarget (const Array &buf) { BufferScanner scanner(buf); return loadTarget(scanner); } void RingoSubstructure::loadTarget (const char *str) { BufferScanner scanner(str); return loadTarget(scanner); } void RingoSubstructure::loadTarget (Scanner &scanner) { ReactionAutoLoader loader(scanner); _context.setLoaderSettings(loader); loader.loadReaction(_target_reaction); _initTarget(false); } bool RingoSubstructure::matchLoadedTarget () { ReactionSubstructureMatcher rsm(_target_reaction); rsm.highlight = true; rsm.setQuery(_query_reaction); //rsm.setNeiCounters(&_nei_query_counters, &_nei_target_counters); if (_smarts) rsm.use_daylight_aam_mode = true; return rsm.find(); } void RingoSubstructure::getHighlightedTarget (Array &buf) { ArrayOutput output(buf); RxnfileSaver saver(output); if (!Reaction::haveCoord(_target_reaction)) { profTimerStart(t, "match.layout"); ReactionLayout layout(_target_reaction); layout.make(); _target_reaction.markStereocenterBonds(); } if (preserve_bonds_on_highlighting) Reaction::loadBondOrders(_target_reaction, _target_bond_types); saver.saveReaction(_target_reaction); } const byte * RingoSubstructure::getQueryFingerprint () { _validateQueryData(); return _query_fp.ptr(); } Indigo-indigo-1.2.3/bingo/bingo-version.cmake000066400000000000000000000005071271037650300210640ustar00rootroot00000000000000SET(BINGO_VERSION "1.7.10-dev7" CACHE INTERNAL "versions" FORCE) CMAKE_POLICY(SET CMP0012 NEW) IF($ENV{BUILD_NUMBER}) SET(BINGO_BUILD_VERSION $ENV{BUILD_NUMBER}) ELSE() SET(BINGO_BUILD_VERSION 0) ENDIF() SET(BINGO_VERSION_EXT "${BINGO_VERSION}.${BINGO_BUILD_VERSION} ${PACKAGE_SUFFIX}" CACHE INTERNAL "versions" FORCE) Indigo-indigo-1.2.3/bingo/oracle/000077500000000000000000000000001271037650300165445ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/CMakeLists.txt000066400000000000000000000031531271037650300213060ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(BingoOracle) include(../bingo-version.cmake) include(ConfigureCommon) message(STATUS "BingoOracle version is ${BINGO_VERSION_EXT}") file(GLOB Bingo_src src/oracle/*.cpp) file(GLOB Bingo_headers src/oracle/*.h) include_directories(src ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${OCI_INCLUDE_DIRS} ${ZLib_HEADERS_DIR} ${BingoCore_HEADERS_DIR} ${Common_SOURCE_DIR}/../api/plugins/inchi/src ${InChI_SOURCE_DIR}/inchi_dll) add_library(bingo-oracle-shared SHARED ${Bingo_src} ${Bingo_headers} ${Common_SOURCE_DIR}/../api/plugins/inchi/src/indigo_inchi_core.cpp ${Common_SOURCE_DIR}/hacks/memcpy.c $ $ $ $ $) if (UNIX AND NOT APPLE) message(STATUS "UNIX AND NOT APPLE") if(${SUBSYSTEM_NAME} MATCHES "x64") message(STATUS "SUBSYSTEM_NAME EQUALS x64") set_target_properties(bingo-oracle-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy") endif() endif() set_target_properties(bingo-oracle-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") target_link_libraries(bingo-oracle-shared bingo-core common-oracle inchi tinyxml z) if (MSVC) target_link_libraries(bingo-oracle-shared odbc32 odbccp32 ${OCI_LIBRARY}) elseif(APPLE) target_link_libraries(bingo-oracle-shared ${OCI_LIBRARY}) endif() set_property(TARGET bingo-oracle-shared PROPERTY LINK_INTERFACE_LIBRARIES "") SET_TARGET_PROPERTIES(bingo-oracle-shared PROPERTIES OUTPUT_NAME "bingo-oracle") set_property(TARGET bingo-oracle-shared PROPERTY FOLDER "bingo-oracle") pack_bingo(bingo-oracle-shared) Indigo-indigo-1.2.3/bingo/oracle/setup/000077500000000000000000000000001271037650300177045ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/setup/bingo-oracle-install.bat000066400000000000000000000072431271037650300244070ustar00rootroot00000000000000@rem Copyright (C) 2009-2015 EPAM Systems @rem @rem This file is part of Indigo toolkit. @rem @rem This file may be distributed and/or modified under the terms of the @rem GNU General Public License version 3 as published by the Free Software @rem Foundation and appearing in the file LICENSE.GPL included in the @rem packaging of this file. @rem @rem This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE @rem WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @echo off set libdir=%ORACLE_HOME%\bin set dbaname=system set dbapass= set instance= set bingoname=bingo set bingopass=bingo set y= goto L2 :L1 shift :L2 if "%1" == "" goto L3 if "%1" == "-libdir" goto got_libdir if "%1" == "-dbaname" goto got_dbaname if "%1" == "-dbapass" goto got_dbapass if "%1" == "-instance" goto got_instance if "%1" == "-bingoname" goto got_bingoname if "%1" == "-bingopass" goto got_bingopass if "%1" == "-y" goto got_y if "%1" == "-help" goto usage if "%1" == "-?" goto usage if "%1" == "/?" goto usage goto badparam :got_libdir shift set libdir=%1 goto L1 :got_dbaname shift set dbaname=%1 goto L1 :got_dbapass shift set dbapass=%1 goto L1 :got_instance shift set instance=%1 goto L1 :got_bingoname shift set bingoname=%1 goto L1 :got_bingopass shift set bingopass=%1 goto L1 :got_y set y=1 goto L1 :badparam echo Unknown parameter: %1 goto end :L3 echo Target directory : %libdir% echo DBA name : %dbaname% if not "%dbapass%" == "" echo DBA password : %DBApass% if "%instance%"=="" echo Oracle instance : [default] if not "%instance%"=="" echo Oracle instance : %instance% echo Bingo name : %BINGOname% echo Bingo password : %BINGOpass% if "%y%"=="1" goto L4 set /p proceed=Proceed (y/N)? if "%proceed%"=="y" goto L4 if "%proceed%"=="Y" goto L4 echo Aborting goto end :L4 echo set verify off >sql\bingo\bingo_lib.sql echo spool bingo_lib; >>sql\bingo\bingo_lib.sql echo create or replace LIBRARY bingolib AS '%libdir%\bingo-oracle.dll' >>sql\bingo\bingo_lib.sql echo / >>sql\bingo\bingo_lib.sql echo spool off; >>sql\bingo\bingo_lib.sql md %libdir% copy lib\bingo-oracle.dll %libdir% /y if not %errorlevel%==0 goto end if not "%instance%"=="" set instance=@%instance% cd sql\system if "%dbapass%"=="" goto emptypass sqlplus %dbaname%/%dbapass%%instance% @bingo_init.sql %bingoname% %bingopass% goto L5 :emptypass sqlplus %dbaname%%instance% @bingo_init.sql %bingoname% %bingopass% :L5 cd ..\bingo sqlplus %bingoname%/%bingopass%%instance% @makebingo.sql sqlplus %bingoname%/%bingopass%%instance% @bingo_config.sql cd .. sqlplus %bingoname%/%bingopass%%instance% @dbcheck.sql cd .. goto end :usage echo Usage: bingo-oracle-install.bat [parameters] echo Parameters: echo -?, -help echo Print this help message echo -libdir path echo Target directory to install bingo-oracle.dll (defaut %%ORACLE_HOME%%\bin). echo If the directory does not exist, it will be created. echo -dbaname name echo Database administrator login (default "system"). echo -dbapass password echo Database administrator password (no default). echo If the password is not specified, you will have to enter it later. echo -instance instance echo Database instance (default instance by default). echo You can specify full address like "server:1521/instance" as well. echo -bingoname name echo Name of cartridge pseudo-user (default "bingo"). echo -bingopass password echo Password of the pseudo-user (default "bingo"). echo -y echo Do not ask for confirmation. goto end :end set libdir= set dbaname= set dbapass= set instance= set bingoname= set bingopass= set y= set initsql= Indigo-indigo-1.2.3/bingo/oracle/setup/bingo-oracle-install.sh000077500000000000000000000067761271037650300242700ustar00rootroot00000000000000#!/bin/sh # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. libdir=$ORACLE_HOME/lib dbaname="system" dbapass= instance= bingoname="bingo" bingopass="bingo" y= usage () { echo 'Usage: bingo-oracle-install.sh [parameters]' echo 'Parameters:' echo ' -?, -help' echo ' Print this help message' echo ' -libdir path' echo ' Target directory to install libbingo-oracle'$libext' (defaut $ORACLE_HOME/lib).' echo ' If the directory does not exist, it will be created.' echo ' -dbaname name' echo ' Database administrator login (default "system").' echo ' -dbapass password' echo ' Database administrator password (no default).' echo ' If the password is not specified, you will have to enter it later.' echo ' -instance instance' echo ' Database instance (default instance by default).' echo ' You can specify full address like "server:1521/instance" as well.' echo ' -bingoname name' echo ' Name of cartridge pseudo-user (default "bingo").' echo ' -bingopass password' echo ' Password of the pseudo-user (default "bingo").' echo ' -y' echo ' Do not ask for confirmation.' } libext=".so" if [ -f "lib/libbingo-oracle.dylib" ]; then libext=".dylib" fi while [ "$#" != 0 ]; do case "$1" in -help | '-?' | '/?') usage exit 0 ;; -libdir) shift libdir=$1 ;; -dbaname) shift dbaname=$1 ;; -dbapass) shift dbapass=$1 ;; -instance) shift instance=$1 ;; -bingoname) shift bingoname=$1 ;; -bingopass) shift bingopass=$1 ;; -y) y=1 ;; *) echo "Unknown parameter: $1"; usage; exit -1 esac shift done echo "Target directory : $libdir"; echo "DBA name : $dbaname"; if [ ! "$dbapass" = "" ]; then echo "DBA password : $dbapass"; fi if [ ! "$instance" = "" ]; then echo "Oracle instance : $instance"; else echo "Oracle instance : "; fi echo "Bingo name : $bingoname"; echo "Bingo password : $bingopass"; if [ "$y" != "1" ]; then echo "Proceed (y/N)?" read proceed if [ "$proceed" != "y" ] && [ "$proceed" != "Y" ]; then echo 'Aborting'; exit 0; fi fi if [ ! "$instance" = "" ]; then instance=@$instance fi mkdir -p $libdir echo set verify off >sql/bingo/bingo_lib.sql echo spool bingo_lib\; >>sql/bingo/bingo_lib.sql echo create or replace LIBRARY bingolib AS \'$libdir/libbingo-oracle$libext\' >>sql/bingo/bingo_lib.sql echo / >>sql/bingo/bingo_lib.sql echo spool off\; >>sql/bingo/bingo_lib.sql cp lib/libbingo-oracle$libext $libdir if [ $? != 0 ]; then echo 'Cannot copy libbingo-oracle'$libext' to '$libdir exit fi cd sql/system if [ "$dbapass" = "" ]; then sqlplus $dbaname$instance @bingo_init.sql $bingoname $bingopass else sqlplus $dbaname/$dbapass$instance @bingo_init.sql $bingoname $bingopass fi cd ../bingo sqlplus $bingoname/$bingopass$instance @makebingo.sql sqlplus $bingoname/$bingopass$instance @bingo_config.sql cd .. sqlplus $bingoname/$bingopass$instance @dbcheck.sql cd .. Indigo-indigo-1.2.3/bingo/oracle/sql/000077500000000000000000000000001271037650300173435ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/000077500000000000000000000000001271037650300204415ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/alter_routines.sql000066400000000000000000000035721271037650300242300ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool alter_routines; CREATE OR REPLACE PACKAGE AlterPackage IS mangoIndexType constant pls_integer := 1; ringoIndexType constant pls_integer := 2; procedure AlterIndex(ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, index_type PLS_INTEGER); procedure renameColumn(i_schema_name in varchar2, i_table_name in varchar2, i_old_column_name in varchar2, i_new_column_name in varchar2); procedure renameTable(i_schema_name in varchar2, i_old_table_name in varchar2, i_new_table_name in varchar2); END AlterPackage; / create or replace procedure renameColumn(i_schema_name in varchar2, i_table_name in varchar2, i_old_column_name in varchar2, i_new_column_name in varchar2) is begin AlterPackage.renameColumn(i_schema_name, i_table_name, i_old_column_name, i_new_column_name); end; / create or replace procedure renameTable(i_schema_name in varchar2, i_old_table_name in varchar2, i_new_table_name in varchar2) is begin AlterPackage.renameTable(i_schema_name, i_old_table_name, i_new_table_name); end; / begin $if dbms_db_version.version < 11 $then execute immediate 'grant execute on renameColumn to public'; execute immediate 'grant execute on renameTable to public'; $else null; $end end; / spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/alter_routines2.sql000066400000000000000000000066571271037650300243210ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool alter_routines2; CREATE OR REPLACE PACKAGE BODY AlterPackage IS /* column rename */ procedure renameColumn(i_schema_name in varchar2, i_table_name in varchar2, i_old_column_name in varchar2, i_new_column_name in varchar2) is l_old_column_name varchar2(30) := trim(both '"' from i_old_column_name); l_new_column_name varchar2(30) := trim(both '"' from i_new_column_name); begin update context set column_name = l_new_column_name where schema_name = i_schema_name and table_name = i_table_name and column_name = l_old_column_name; commit; end; procedure renameColumn(ia sys.ODCIIndexInfo, i_new_column_name VARCHAR2) is begin renameColumn(ia.IndexCols(1).TableSchema, ia.IndexCols(1).TableName, ia.IndexCols(1).ColName, i_new_column_name); end; /* table rename */ procedure renameTable(i_schema_name in varchar2, i_old_table_name in varchar2, i_new_table_name in varchar2) is begin update context set table_name = i_new_table_name where schema_name = i_schema_name and table_name = i_old_table_name; commit; end; procedure renameTable(ia sys.ODCIIndexInfo, i_new_table_name VARCHAR2) is begin renameTable(ia.IndexCols(1).TableSchema, ia.IndexCols(1).TableName, i_new_table_name); end; /* rebuild index */ procedure rebuildIndex(ia sys.ODCIIndexInfo, parms VARCHAR2, index_type PLS_INTEGER) is context_id number; col sys.ODCIColInfo := ia.IndexCols(1); full_table_name varchar2(100) := '"'||col.TableSchema||'"."'||col.TableName||'"'; begin context_id := BingoPackage.getContextID(ia); case index_type when mangoIndexType then mangoDropIndex(context_id); mangoCreateIndex(context_id, parms, full_table_name, col.ColName, col.ColTypeName); when ringoIndexType then ringoDropIndex(context_id); ringoCreateIndex(context_id, parms, full_table_name, col.ColName, col.ColTypeName); else raise_application_error(-20356, 'Index type is not supported'); end case; end; /* common alter */ procedure AlterIndex(ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, index_type PLS_INTEGER) is begin case alter_option when ODCIConst.AlterIndexNone then raise_application_error(-20356, 'Alter option is not supported, please use REBUILD INDEX instead'); when ODCIConst.AlterIndexRename then null; when ODCIConst.AlterIndexRebuild then rebuildIndex(ia, parms, index_type); $if dbms_db_version.version >= 11 $then when ODCIConst.AlterIndexRenameCol then renameColumn(ia, parms); when ODCIConst.AlterIndexRenameTab then renameTable(ia, parms); $end else raise_application_error(-20356, 'Alter option is not supported'); end case; end; end AlterPackage; / spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/bingo_calls.sql000066400000000000000000000237511271037650300234460ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool bingo_calls; create or replace procedure DropTable (tname in varchar2) is table_exists boolean; shema_name varchar2(100); begin select SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') into shema_name from dual; table_exists := false; for item in (select table_name from user_tables where table_name in (tname)) loop table_exists := true; end loop; for item in (select ttt from (select (shema_name || '.' || table_name) ttt from user_tables) where ttt in (tname)) loop table_exists := true; end loop; if table_exists then execute immediate 'DROP TABLE ' || tname; end if; end DropTable; / create or replace function FileToCLOB (filename in string) return clob AS language C name "oraLoadFileToCLOB" library bingolib with context parameters(context, filename, filename indicator short, return indicator short, return OCILobLocator); / grant execute on FileToCLOB to public; / create or replace function FileToBLOB (filename in string) return blob AS language C name "oraLoadFileToBLOB" library bingolib with context parameters(context, filename, filename indicator short, return indicator short, return OCILobLocator); / grant execute on FileToBLOB to public; / create or replace function FileToString(filename in varchar2) return VARCHAR2 as language C name "oraLoadFileToString" library bingolib with context parameters(context, filename, filename indicator short, return indicator short, return OCIString); / grant execute on FileToString to public; / create or replace procedure CLOBToFile(lob in CLOB, filename in varchar2) as language C name "oraSaveLOBToFile" library bingolib with context parameters(context, lob, lob indicator short, filename, filename indicator short); / grant execute on CLOBToFile to public; / create or replace procedure LogPrint(str in varchar2) as language C name "oraLogPrint" library bingolib with context parameters (context, str); / create or replace procedure ConfigResetAll (context_id in binary_integer) AS language C name "oraConfigResetAll" library bingolib with context parameters(context, context_id); / create or replace procedure ConfigReset (context_id in binary_integer, key_name in string) AS language C name "oraConfigReset" library bingolib with context parameters(context, context_id, key_name, key_name indicator short); / create or replace procedure ConfigSetInt (context_id in binary_integer, key_name in string, value in Number) AS language C name "oraConfigSetInt" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, value, value indicator short); / create or replace function ConfigGetInt (context_id in binary_integer, key_name in string) return NUMBER AS language C name "oraConfigGetInt" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, return OCINumber); / create or replace procedure ConfigSetFloat (context_id in binary_integer, key_name in string, value in Number) AS language C name "oraConfigSetFloat" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, value, value indicator short); / create or replace function ConfigGetFloat (context_id in binary_integer, key_name in string) return NUMBER AS language C name "oraConfigGetFloat" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, return OCINumber); / create or replace procedure ConfigSetString (context_id in binary_integer, key_name in string, value in string) AS language C name "oraConfigSetString" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, value, value indicator short); / create or replace function ConfigGetString (context_id in binary_integer, key_name in string) return string AS language C name "oraConfigGetString" library bingolib with context parameters(context, context_id, key_name, key_name indicator short, return OCIString); / create or replace function ProfilingGetCount (key_name in string) return NUMBER AS language C name "oraProfilingGetCount" library bingolib with context parameters(context, key_name, key_name indicator short, return OCINumber); / grant execute on ProfilingGetCount to public; / create or replace function ProfilingGetTime (key_name in string) return NUMBER AS language C name "oraProfilingGetTime" library bingolib with context parameters(context, key_name, key_name indicator short, return OCINumber); / grant execute on ProfilingGetTime to public; / create or replace procedure ProfilingPrint (print_all in Number) AS language C name "oraProfilingPrint" library bingolib with context parameters(context, print_all, print_all indicator short); / grant execute on ProfilingPrint to public; / create or replace procedure ExportSDF (table_name in string, clob_col in string, other_cols in string, filename in string) AS language C name "oraExportSDF" library bingolib with context parameters(context, table_name, table_name indicator short, clob_col, clob_col indicator short, other_cols, other_cols indicator short, filename, filename indicator short); / grant execute on ExportSDF to public; / create or replace procedure ExportSDFZip (table_name in string, clob_col in string, other_cols in string, filename in string) AS language C name "oraExportSDFZip" library bingolib with context parameters(context, table_name, table_name indicator short, clob_col, clob_col indicator short, other_cols, other_cols indicator short, filename, filename indicator short); / grant execute on ExportSDFZip to public; / create or replace procedure ImportSDF (table_name in string, clob_col in string, other_cols in string, filename in string) AS language C name "oraImportSDF" library bingolib with context parameters(context, table_name, table_name indicator short, clob_col, clob_col indicator short, other_cols, other_cols indicator short, filename, filename indicator short); / grant execute on ImportSDF to public; create or replace procedure ImportRDF (table_name in string, clob_col in string, other_cols in string, filename in string) AS language C name "oraImportRDF" library bingolib with context parameters(context, table_name, table_name indicator short, clob_col, clob_col indicator short, other_cols, other_cols indicator short, filename, filename indicator short); / grant execute on ImportRDF to public; create or replace procedure ImportSMILES (table_name in string, smiles_col in string, id_col in string, filename in string) AS language C name "oraImportSMILES" library bingolib with context parameters(context, table_name, table_name indicator short, smiles_col, smiles_col indicator short, id_col, id_col indicator short, filename, filename indicator short); / grant execute on ImportSMILES to public; create or replace procedure RingoFlushInserts (docommit in binary_integer) AS language C name "oraRingoFlushInserts" library bingolib with context parameters(context, docommit); / create or replace procedure MangoFlushInserts (docommit in binary_integer) AS language C name "oraMangoFlushInserts" library bingolib with context parameters(context, docommit); / create or replace procedure Zip2 (src in CLOB, dest in BLOB) AS language C name "oraBingoZip" library bingolib with context parameters (context, src, src indicator short, dest, dest indicator short); / grant execute on Zip2 to public; / create or replace function Zip (src in CLOB) return BLOB IS lob BLOB; BEGIN dbms_lob.createtemporary(lob, TRUE, dbms_lob.call); Zip2(src, lob); return lob; END Zip; / grant execute on Zip to public; / create or replace procedure Unzip2 (src in BLOB, dest in CLOB) AS language C name "oraBingoUnzip" library bingolib with context parameters (context, src, src indicator short, dest, dest indicator short); / grant execute on Unzip2 to public; / create or replace function Unzip (src in BLOB) return CLOB IS lob CLOB; BEGIN dbms_lob.createtemporary(lob, TRUE, dbms_lob.call); Unzip2(src, lob); return lob; END Unzip; / grant execute on Unzip to public; / create or replace procedure FlushInserts is begin RingoFlushInserts(1); MangoFlushInserts(1); end FlushInserts; / grant execute on FlushInserts to public; / create or replace trigger LogoffTrigger BEFORE LOGOFF ON DATABASE begin RingoFlushInserts(0); MangoFlushInserts(0); exception when others then null; end; / create or replace function GetVersion return VARCHAR2 AS language C name "oraGetVersion" library bingolib with context parameters(context, return indicator short, return OCIString); / grant execute on GetVersion to public; / create or replace function Name (target in CLOB) return VARCHAR2 AS language C name "oraBingoGetName" library bingolib with context parameters(context, target, target indicator short, return indicator short, return OCIString); / grant execute on Name to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/bingo_config.sql000066400000000000000000000062521271037650300236120ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Set Verify Off; spool bingo_config; create table CONFIG_STR (n int, name varchar2(100), value varchar2(4000)); create index CONFIG_STR_N on CONFIG_STR(n); create table CONFIG_INT (n int, name varchar2(100), value int); create index CONFIG_INT_N on CONFIG_INT(n); create table CONFIG_FLOAT (n int, name varchar2(100), value float); create index CONFIG_FLOAT_N on CONFIG_FLOAT(n); create table CONFIG_CLOB (n int, name varchar2(100), value CLOB); create index CONFIG_CLOB_N on CONFIG_CLOB(n); create table CONFIG_BLOB (n int, name varchar2(100), value BLOB) NOLOGGING LOB(value) STORE AS (NOCACHE NOLOGGING); create index CONFIG_BLOB_N on CONFIG_BLOB(n); create table TAUTOMER_RULES (id INT PRIMARY KEY, beg VARCHAR2(100), end VARCHAR2(100)); insert into TAUTOMER_RULES values (1, 'N,O,P,S,As,Se,Sb,Te', 'N,O,P,S,As,Se,Sb,Te'); insert into TAUTOMER_RULES values (2, '0C', 'N,O,P,S'); insert into TAUTOMER_RULES values (3, '1C', 'N,O'); grant insert, select, delete on TAUTOMER_RULES to public; insert into CONFIG_INT values(0, 'FP_ORD_SIZE', 25); insert into CONFIG_INT values(0, 'FP_ANY_SIZE', 15); insert into CONFIG_INT values(0, 'FP_TAU_SIZE', 10); insert into CONFIG_INT values(0, 'FP_SIM_SIZE', 8); insert into CONFIG_INT values(0, 'FP_STORAGE_CHUNK', 1024); insert into CONFIG_INT values(0, 'SUB_SCREENING_MAX_BITS', 8); insert into CONFIG_INT values(0, 'SUB_SCREENING_PASS_MARK', 128); insert into CONFIG_INT values(0, 'SIM_SCREENING_PASS_MARK', 128); insert into CONFIG_INT values(0, 'TREAT_X_AS_PSEUDOATOM', 0); insert into CONFIG_INT values(0, 'IGNORE_CLOSING_BOND_DIRECTION_MISMATCH', 0); insert into CONFIG_INT values(0, 'IGNORE_STEREOCENTER_ERRORS', 0); insert into CONFIG_INT values(0, 'IGNORE_CISTRANS_ERRORS', 0); insert into CONFIG_INT values(0, 'ALLOW_NON_UNIQUE_DEAROMATIZATION', 0); insert into CONFIG_INT values(0, 'ZERO_UNKNOWN_AROMATIC_HYDROGENS', 0); insert into CONFIG_INT values(0, 'STEREOCHEMISTRY_BIDIRECTIONAL_MODE', 0); insert into CONFIG_INT values(0, 'STEREOCHEMISTRY_DETECT_HAWORTH_PROJECTION', 0); insert into CONFIG_INT values(0, 'REJECT_INVALID_STRUCTURES', 0); create or replace procedure SetRelativeAtomicMass (list in VARCHAR2) is begin ConfigSetString(0, 'RELATIVE_ATOMIC_MASS', list); end SetRelativeAtomicMass; / grant execute on SetRelativeAtomicMass to public; create or replace procedure TreatXAsPseudoatom (k in number) is begin ConfigSetInt(0, 'TREAT_X_AS_PSEUDOATOM', k); end TreatXAsPseudoatom; / grant execute on TreatXAsPseudoatom to public; create or replace procedure IgnoreCBDirectionMismatch (k in number) is begin ConfigSetInt(0, 'IGNORE_CLOSING_BOND_DIRECTION_MISMATCH', k); end IgnoreCBDirectionMismatch; / grant execute on IgnoreCBDirectionMismatch to public; spool off; exit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/bingo_context.sql000066400000000000000000000017161271037650300240310ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool bingo_context; create table context ( id int, schema_name varchar2(30), table_name varchar2(30), column_name varchar2(30), constraint pk_context primary key (id), constraint uk_context unique (schema_name, table_name, column_name) ); column context_id new_value context_id select nvl(max(id) + 1, 1) context_id from context; create sequence s_context minvalue 1 increment by 1 start with &context_id nocache order nocycle; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/bingo_package.sql000066400000000000000000000063701271037650300237410ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool bingo_package; CREATE OR REPLACE PACKAGE BingoPackage IS function GetContextID (ia sys.ODCIIndexInfo) return NUMBER; function GetContextID (col sys.ODCIColInfo) return NUMBER; function GetContextID (i_schema_name in varchar2, i_table_name in varchar2, i_column_name in varchar2) return NUMBER; function createContextID(ia sys.ODCIIndexInfo) return NUMBER; procedure deleteContextID(i_context_id NUMBER); END BingoPackage; / CREATE OR REPLACE PACKAGE BODY BingoPackage IS function GetContextID (ia sys.ODCIIndexInfo) return NUMBER IS begin return GetContextID(ia.IndexCols(1)); end GetContextID; function GetContextID (col sys.ODCIColInfo) return NUMBER IS begin return GetContextID(col.TableSchema, col.TableName, col.ColName); end GetContextID; function GetContextID (i_schema_name in varchar2, i_table_name in varchar2, i_column_name in varchar2) return NUMBER is l_column_name varchar2(30) := trim(both '"' from i_column_name); l_context_id pls_integer; begin select id into l_context_id from context where schema_name = i_schema_name and table_name = i_table_name and column_name = l_column_name; return l_context_id; exception when no_data_found then return null; end; function createContextID(ia sys.ODCIIndexInfo) return NUMBER IS l_schema_name varchar2(30) := ia.IndexCols(1).TableSchema; l_table_name varchar2(30) := ia.IndexCols(1).TableName; l_column_name varchar2(30) := trim(both '"' from ia.IndexCols(1).ColName); l_context_id pls_integer; l_dummy number; l_bingo_name varchar2(30); begin begin select 1 into l_dummy from all_tables t where t.owner = l_schema_name and t.table_name = l_table_name; exception when no_data_found then select sys_context('USERENV', 'CURRENT_SCHEMA') into l_bingo_name from dual; raise_application_error(-20351, 'Table "'||l_schema_name||'"."'||l_table_name ||'" not accessible for reading (did you forget GRANT SELECT ON "' ||l_schema_name||'"."'||l_table_name||'" TO "' ||l_bingo_name||'" ?)'); end; insert into context (id, schema_name, table_name, column_name) values (s_context.nextval, l_schema_name, l_table_name, l_column_name) returning id into l_context_id; commit; return l_context_id; end; procedure deleteContextID(i_context_id NUMBER) is begin delete from context where id = i_context_id; commit; end; END BingoPackage; / spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/dropbingo.sql000066400000000000000000000010731271037650300231460ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @@ringo_drop.sql @@mango_drop.sql drop table context; drop library bingolib; commit / Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/makebingo.sql000066400000000000000000000012121271037650300231120ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @@bingo_lib.sql @@bingo_calls.sql @@bingo_context.sql @@bingo_package.sql @@alter_routines.sql @@mango_make.sql @@ringo_make.sql @@alter_routines2.sql commit; exit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_calls.sql000066400000000000000000000446351271037650300234550ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool mango_calls; create or replace function Sub_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoSub" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function SubHi_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraMangoSubHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function Sub_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoSub" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function SubHi_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraMangoSubHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function Smarts_clob (context_id in binary_integer, target in CLOB, query in VARCHAR2) return NUMBER AS language C name "oraMangoSmarts" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function Smarts_blob (context_id in binary_integer, target in BLOB, query in VARCHAR2) return NUMBER AS language C name "oraMangoSmarts" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function SmartsHi_clob (context_id in binary_integer, target in CLOB, query in VARCHAR2) return CLOB AS language C name "oraMangoSmartsHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCILobLocator); / create or replace function SmartsHi_blob (context_id in binary_integer, target in BLOB, query in VARCHAR2) return CLOB AS language C name "oraMangoSmartsHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCILobLocator); / create or replace function Exact_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoExact" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function ExactHi_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraMangoExactHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function Exact_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoExact" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function ExactHi_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraMangoExactHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function Sim_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoSim" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function Sim_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraMangoSim" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function GrossCalc_clob (target in CLOB) return VARCHAR2 AS language C name "oraMangoGrossCalc" library bingolib with context parameters(context, target, target indicator short, return indicator short, return OCIString); / create or replace function GrossCalc_blob (target in BLOB) return VARCHAR2 AS language C name "oraMangoGrossCalc" library bingolib with context parameters(context, target, target indicator short, return indicator short, return OCIString); / create or replace function Gross_clob (context_id in binary_integer, target in CLOB, query in VARCHAR2) return NUMBER AS language C name "oraMangoGross" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function Gross_blob (context_id in binary_integer, target in BLOB, query in VARCHAR2) return NUMBER AS language C name "oraMangoGross" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function Mass_clob (context_id in binary_integer, target in CLOB, typee in VARCHAR2) return NUMBER AS language C name "oraMangoMolecularMass" library bingolib with context parameters(context, context_id, target, target indicator short, typee, typee indicator short, return indicator short, return OCINumber); / create or replace function Mass_blob (context_id in binary_integer, target in BLOB, typee in VARCHAR2) return NUMBER AS language C name "oraMangoMolecularMass" library bingolib with context parameters(context, context_id, target, target indicator short, typee, typee indicator short, return indicator short, return OCINumber); / create or replace procedure mangoCreateIndex(context_id in binary_integer, params in varchar2, full_table_name in varchar2, column_name in varchar2, column_data_type in varchar2) AS language C name "oraMangoCreateIndex" library bingolib with context parameters(context, context_id, params, params indicator short, full_table_name, full_table_name indicator short, column_name, column_name indicator short, column_data_type, column_data_type indicator short); / create or replace procedure mangoDropIndex (context_id in binary_integer) AS language C name "oraMangoDropIndex" library bingolib with context parameters(context, context_id); / create or replace procedure mangoTruncateIndex (context_id in binary_integer) AS language C name "oraMangoTruncateIndex" library bingolib with context parameters(context, context_id); / create or replace procedure mangoIndexInsert_clob (context_id in binary_integer, rid in VARCHAR2, item in CLOB) AS language C name "oraMangoIndexInsert" library bingolib with context parameters(context, context_id, rid, rid indicator short, item, item indicator short); / create or replace procedure mangoIndexInsert_blob (context_id in binary_integer, rid in VARCHAR2, item in BLOB) AS language C name "oraMangoIndexInsert" library bingolib with context parameters(context, context_id, rid, rid indicator short, item, item indicator short); / create or replace procedure mangoIndexDelete (context_id in binary_integer, rid in VARCHAR2) AS language C name "oraMangoIndexDelete" library bingolib with context parameters(context, context_id, rid, rid indicator short); / create or replace function mangoIndexStart (context_id in binary_integer, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, flags in binary_integer, params in VARCHAR2) return binary_integer AS language C name "oraMangoIndexStart" library bingolib with context parameters(context, context_id, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, flags, params, params indicator short); / create or replace function mangoIndexFetch (fetch_id in binary_integer, maxnrows in BINARY_INTEGER, arr in out sys.ODCIRidList) return binary_integer AS language C name "oraMangoIndexFetch" library bingolib with context parameters(context, fetch_id, maxnrows, arr, arr indicator short); / create or replace function mangoIndexSelectivity (context_id in binary_integer, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, flags in binary_integer, params in VARCHAR2) return NUMBER AS language C name "oraMangoIndexSelectivity" library bingolib with context parameters(context, context_id, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, flags, params, params indicator short, return indicator short, return OCINumber); / create or replace procedure mangoIndexCost (context_id in binary_integer, sel in NUMBER, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, flags in binary_integer, params in VARCHAR2, iocost out binary_integer, cpucost out binary_integer) AS language C name "oraMangoIndexCost" library bingolib with context parameters(context, context_id, sel, sel indicator short, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, flags, params, params indicator short, iocost, cpucost); / create or replace procedure mangoIndexClose (fetch_id in binary_integer) AS language C name "oraMangoIndexClose" library bingolib with context parameters(context, fetch_id); / create or replace procedure mangoCollectStatistics (context_id in binary_integer) AS language C name "oraMangoCollectStatistics" library bingolib with context parameters(context, context_id); / create or replace procedure mangoAnalyzeMolecules(context_id in binary_integer) as language C name "oraMangoAnalyzeMolecules" library bingolib with context parameters (context, context_id); / create or replace function Molfile_clob (m in CLOB, options in VARCHAR2) return CLOB AS language C name "oraMangoMolfile" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function Molfile_blob (m in BLOB, options in VARCHAR2) return CLOB AS language C name "oraMangoMolfile" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function CML_clob (m in CLOB) return CLOB AS language C name "oraMangoCML" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCILobLocator); / create or replace function CML_blob (m in BLOB) return CLOB AS language C name "oraMangoCML" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCILobLocator); / create or replace function SMILES_clob (m in CLOB, options in VARCHAR2) return VARCHAR2 AS language C name "oraMangoSMILES" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCIString); / create or replace function SMILES_blob (m in BLOB, options in VARCHAR2) return VARCHAR2 AS language C name "oraMangoSMILES" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCIString); / create or replace function InChI_clob (m in CLOB, options in VARCHAR2) return CLOB AS language C name "oraMangoInchi" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function InChI_blob (m in BLOB, options in VARCHAR2) return CLOB AS language C name "oraMangoInchi" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function InChIKey_clob (inchi in CLOB) return VARCHAR2 AS language C name "oraMangoInchiKey" library bingolib with context parameters (context, inchi, inchi indicator short, return indicator short, return OCIString); / create or replace function Fingerprint_clob (m in CLOB, options in VARCHAR2) return BLOB AS language C name "oraMangoFingerprint" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function Fingerprint_blob (m in BLOB, options in VARCHAR2) return BLOB AS language C name "oraMangoFingerprint" library bingolib with context parameters (context, m, m indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function CANSMILES_clob (m in CLOB) return VARCHAR2 AS language C name "oraMangoCanonicalSMILES" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCIString); / create or replace function CANSMILES_blob (m in BLOB) return VARCHAR2 AS language C name "oraMangoCanonicalSMILES" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCIString); / create or replace function CheckMolecule (m in CLOB) return VARCHAR2 AS language C name "oraMangoCheckMolecule" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCIString); / grant execute on CheckMolecule to public; / create or replace procedure CompactMolecule2 (m in CLOB, res in BLOB, save_xyz in binary_integer) AS language C name "oraMangoICM2" library bingolib with context parameters (context, m, m indicator short, res, res indicator short, save_xyz); / grant execute on CompactMolecule2 to public; / create or replace function CompactMolecule (m in CLOB, save_xyz in binary_integer) return BLOB IS lob BLOB; BEGIN IF m is null THEN return NULL; END IF; dbms_lob.createtemporary(lob, TRUE, dbms_lob.call); CompactMolecule2(m, lob, save_xyz); return lob; END CompactMolecule; / grant execute on CompactMolecule to public; / spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_drop.sql000066400000000000000000000013451271037650300233120ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. drop indextype MoleculeIndex force; drop operator Exact force; drop operator Sim force; drop operator Sub force; drop operator Gross force; drop operator Mass force; drop package MangoPackage; drop type MangoIndex force; drop type MangoStat force; commit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_index.sql000066400000000000000000000256241271037650300234630ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool mango_index; create or replace type MangoIndex as object ( fetch_id number, static function ODCIGetInterfaces (ifclist OUT sys.ODCIObjectList) return NUMBER, static function ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexAlter (ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDrop (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIIndexTruncate (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, newval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval BLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, newval BLOB, env sys.ODCIEnv) return NUMBER, member function ODCIIndexFetch (nrows NUMBER, rids OUT sys.ODCIRidList, env sys.ODCIEnv) return NUMBER, member function ODCIIndexClose (env sys.ODCIEnv) return NUMBER ); / create or replace type body MangoIndex is static function ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList) return NUMBER is begin ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCIINDEX2')); return ODCIConst.Success; end; static function ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; col sys.ODCIColInfo := ia.IndexCols(1); begin context_id := BingoPackage.createContextID(ia); mangoCreateIndex(context_id, parms, '"'||col.TableSchema||'"."'||col.TableName||'"', col.ColName, col.ColTypeName); return ODCICONST.Success; end; static function ODCIIndexAlter (ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, env sys.ODCIEnv) return NUMBER is begin AlterPackage.AlterIndex(ia, parms, alter_option, AlterPackage.mangoIndexType); return ODCICONST.Success; end; static function ODCIIndexDrop (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); if context_id is not null then mangoDropIndex(context_id); BingoPackage.deleteContextID(context_id); end if; return ODCICONST.Success; end; static function ODCIIndexTruncate (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoTruncateIndex(context_id); return ODCICONST.Success; end; static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, to_clob(query), null, env); end; static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, to_clob(query), params, env); end; static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, query, null, env); end; static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, to_clob(''), null, env); end; static function ODCIIndexStart (sctx IN OUT MangoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; fetch_id binary_integer; flags binary_integer; begin context_id := BingoPackage.getContextID(ia); flags := 0; if bitand(op.Flags, ODCIConst.PredIncludeStart) != 0 then flags := flags + 16 - bitand(flags, 16); end if; if bitand(op.Flags, ODCIConst.PredIncludeStop) != 0 then flags := flags + 32 - bitand(flags, 32); end if; if bitand(op.Flags, ODCIConst.PredExactMatch) != 0 then flags := flags + 64 - bitand(flags, 64); end if; fetch_id := mangoIndexStart(context_id, op.ObjectName, query, strt, stop, flags, params); sctx := MangoIndex(fetch_id); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexInsert_clob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, newval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); mangoIndexInsert_clob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexInsert_clob(context_id, rid, to_clob(newval)); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); mangoIndexInsert_clob(context_id, rid, to_clob(newval)); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexInsert_blob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, newval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoIndexDelete(context_id, rid); mangoIndexInsert_blob(context_id, rid, newval); return ODCICONST.Success; end; member function ODCIIndexFetch (nrows NUMBER, rids OUT NOCOPY sys.ODCIRidList, env sys.ODCIEnv) return NUMBER is res int; begin rids := sys.odciridlist(); res := mangoIndexFetch(fetch_id, nrows, rids); if res = 0 then -- fetch is complete rids.extend; -- add zero to the end of fetched list end if; return ODCICONST.Success; end; member function ODCIIndexClose (env sys.ODCIEnv) return NUMBER is begin mangoIndexClose(fetch_id); return ODCICONST.Success; end; end; / -- this is necessary for Oracle 9 grant execute on MangoIndex to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_indextype.sql000066400000000000000000000312211271037650300243530ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool mango_indextype; create or replace operator Sub binding (VARCHAR2, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (VARCHAR2, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (VARCHAR2, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (CLOB, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (CLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (CLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (BLOB, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (BLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (BLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub, (BLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Sub; create or replace operator Smarts binding (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Smarts, (CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Smarts, (BLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Smarts; create or replace operator Exact binding (VARCHAR2, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (VARCHAR2, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (VARCHAR2, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (CLOB, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (CLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (CLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (BLOB, CLOB) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (BLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (BLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact, (BLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex compute ancillary data using MangoPackage.Exact; -- 'Sim' and 'Gross' operators never return ancillary data create or replace operator Sim binding (VARCHAR2, CLOB) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (VARCHAR2, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (VARCHAR2, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (CLOB, CLOB) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (CLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (CLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (BLOB, CLOB) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (BLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (BLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim, (BLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Sim; -- 'Gross' operator can be used in two ways -- (1) select Gross(structure) from table -- (2) select * from table where Gross(structure, '>= C6H6')=1 create or replace operator Gross binding (VARCHAR2) return VARCHAR2 using MangoPackage.GrossCalc, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Gross, (CLOB) return VARCHAR2 using MangoPackage.GrossCalc, (CLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Gross, (BLOB) return VARCHAR2 using MangoPackage.GrossCalc, (BLOB, VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Gross; -- 'Mass' operator create or replace operator Mass binding (VARCHAR2) return NUMBER with index context, scan context MangoIndex using MangoPackage.Mass, (CLOB) return NUMBER with index context, scan context MangoIndex using MangoPackage.Mass, (BLOB) return NUMBER with index context, scan context MangoIndex using MangoPackage.Mass, (VARCHAR2, VARCHAR2) return NUMBER using MangoPackage.Mass, (CLOB, VARCHAR2) return NUMBER using MangoPackage.Mass, (BLOB, VARCHAR2) return NUMBER using MangoPackage.Mass; create or replace operator Molfile binding (VARCHAR2) return CLOB using MangoPackage.Molfile, (CLOB) return CLOB using MangoPackage.Molfile, (BLOB) return CLOB using MangoPackage.Molfile, (VARCHAR2, VARCHAR2) return CLOB using MangoPackage.Molfile, (CLOB, VARCHAR2) return CLOB using MangoPackage.Molfile, (BLOB, VARCHAR2) return CLOB using MangoPackage.Molfile; create or replace operator CML binding (VARCHAR2) return CLOB using MangoPackage.CML, (CLOB) return CLOB using MangoPackage.CML, (BLOB) return CLOB using MangoPackage.CML; create or replace operator SMILES binding (VARCHAR2) return VARCHAR2 using MangoPackage.SMILES, (CLOB) return VARCHAR2 using MangoPackage.SMILES, (BLOB) return VARCHAR2 using MangoPackage.SMILES, (VARCHAR2, VARCHAR2) return VARCHAR2 using MangoPackage.SMILES, (CLOB, VARCHAR2) return VARCHAR2 using MangoPackage.SMILES, (BLOB, VARCHAR2) return VARCHAR2 using MangoPackage.SMILES; create or replace operator InChI binding (VARCHAR2, VARCHAR2) return CLOB using MangoPackage.InChI, (CLOB, VARCHAR2) return CLOB using MangoPackage.InChI, (BLOB, VARCHAR2) return CLOB using MangoPackage.InChI; create or replace operator InChIKey binding (VARCHAR2) return VARCHAR2 using MangoPackage.InChIKey, (CLOB) return VARCHAR2 using MangoPackage.InChIKey; create or replace operator Fingerprint binding (VARCHAR2, VARCHAR2) return BLOB using MangoPackage.Fingerprint, (CLOB, VARCHAR2) return BLOB using MangoPackage.Fingerprint, (BLOB, VARCHAR2) return BLOB using MangoPackage.Fingerprint; create or replace operator CANSMILES binding (VARCHAR2) return VARCHAR2 using MangoPackage.CANSMILES, (CLOB) return VARCHAR2 using MangoPackage.CANSMILES, (BLOB) return VARCHAR2 using MangoPackage.CANSMILES; create or replace operator SubHi binding (NUMBER) return CLOB ANCILLARY TO Sub (VARCHAR2, CLOB), Sub (VARCHAR2, CLOB, VARCHAR2), Sub (VARCHAR2, VARCHAR2), Sub (VARCHAR2, VARCHAR2, VARCHAR2), Sub (CLOB, CLOB), Sub (CLOB, CLOB, VARCHAR2), Sub (CLOB, VARCHAR2), Sub (CLOB, VARCHAR2, VARCHAR2), Sub (BLOB, CLOB), Sub (BLOB, CLOB, VARCHAR2), Sub (BLOB, VARCHAR2), Sub (BLOB, VARCHAR2, VARCHAR2) using MangoPackage.SubHi; create or replace operator SmartsHi binding (NUMBER) return CLOB ANCILLARY TO Smarts (VARCHAR2, VARCHAR2), Smarts (CLOB, VARCHAR2), Smarts (BLOB, VARCHAR2) using MangoPackage.SmartsHi; create or replace operator ExactHi binding (NUMBER) return CLOB ANCILLARY TO Exact (VARCHAR2, CLOB), Exact (VARCHAR2, CLOB, VARCHAR2), Exact (VARCHAR2, VARCHAR2), Exact (VARCHAR2, VARCHAR2, VARCHAR2), Exact (CLOB, CLOB), Exact (CLOB, CLOB, VARCHAR2), Exact (CLOB, VARCHAR2), Exact (CLOB, VARCHAR2, VARCHAR2), Exact (BLOB, CLOB), Exact (BLOB, CLOB, VARCHAR2), Exact (BLOB, VARCHAR2), Exact (BLOB, VARCHAR2, VARCHAR2) using MangoPackage.ExactHi; grant execute on Sub to public; grant execute on Smarts to public; grant execute on Exact to public; grant execute on Sim to public; grant execute on Gross to public; grant execute on Mass to public; grant execute on Molfile to public; grant execute on CML to public; grant execute on SMILES to public; grant execute on InChI to public; grant execute on InChIKey to public; grant execute on Fingerprint to public; grant execute on CANSMILES to public; grant execute on SubHi to public; grant execute on SmartsHi to public; grant execute on ExactHi to public; create or replace indextype MoleculeIndex for Sub(VARCHAR2, CLOB), Sub(VARCHAR2, CLOB, VARCHAR2), Sub(VARCHAR2, VARCHAR2), Sub(VARCHAR2, VARCHAR2, VARCHAR2), Sub(CLOB, CLOB), Sub(CLOB, CLOB, VARCHAR2), Sub(CLOB, VARCHAR2), Sub(CLOB, VARCHAR2, VARCHAR2), Sub(BLOB, CLOB), Sub(BLOB, CLOB, VARCHAR2), Sub(BLOB, VARCHAR2), Sub(BLOB, VARCHAR2, VARCHAR2), Smarts(VARCHAR2, VARCHAR2), Smarts(CLOB, VARCHAR2), Smarts(BLOB, VARCHAR2), Exact(VARCHAR2, CLOB), Exact(VARCHAR2, CLOB, VARCHAR2), Exact(VARCHAR2, VARCHAR2), Exact(VARCHAR2, VARCHAR2, VARCHAR2), Exact(CLOB, CLOB), Exact(CLOB, CLOB, VARCHAR2), Exact(CLOB, VARCHAR2), Exact(CLOB, VARCHAR2, VARCHAR2), Exact(BLOB, CLOB), Exact(BLOB, CLOB, VARCHAR2), Exact(BLOB, VARCHAR2), Exact(BLOB, VARCHAR2, VARCHAR2), Sim(VARCHAR2, CLOB), Sim(VARCHAR2, CLOB, VARCHAR2), Sim(VARCHAR2, VARCHAR2), Sim(VARCHAR2, VARCHAR2, VARCHAR2), Sim(CLOB, CLOB), Sim(CLOB, CLOB, VARCHAR2), Sim(CLOB, VARCHAR2), Sim(CLOB, VARCHAR2, VARCHAR2), Sim(BLOB, CLOB), Sim(BLOB, CLOB, VARCHAR2), Sim(BLOB, VARCHAR2), Sim(BLOB, VARCHAR2, VARCHAR2), Gross(VARCHAR2, VARCHAR2), Gross(CLOB, VARCHAR2), Gross(BLOB, VARCHAR2), Mass(VARCHAR2), Mass(CLOB), Mass(BLOB) using MangoIndex; associate statistics with packages MangoPackage using MangoStat; associate statistics with indextypes MoleculeIndex using MangoStat; grant execute on MoleculeIndex to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_make.sql000066400000000000000000000011141271037650300232550ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @@mango_calls.sql @@mango_index.sql @@mango_stat.sql @@mango_package.sql @@mango_indextype.sql commit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_package.sql000066400000000000000000001207501271037650300237430ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool mango_package; CREATE OR REPLACE PACKAGE MangoPackage IS function Sub (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sub (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function SubHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SubHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function Smarts (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Smarts (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Smarts (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function SmartsHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SmartsHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function SmartsHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function Exact (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Exact (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function ExactHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function ExactHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB; function Sim (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Sim (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function GrossCalc (target in VARCHAR2) return VARCHAR2; function GrossCalc (target in CLOB) return VARCHAR2; function GrossCalc (target in BLOB) return VARCHAR2; function Gross (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Gross (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Gross (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Mass (target in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Mass (target in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Mass (target in BLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER; function Mass (target in VARCHAR2, typee in VARCHAR2) return NUMBER; function Mass (target in CLOB, typee in VARCHAR2) return NUMBER; function Mass (target in BLOB, typee in VARCHAR2) return NUMBER; function Molfile (target in VARCHAR2) return CLOB; function Molfile (target in CLOB) return CLOB; function Molfile (target in BLOB) return CLOB; function Molfile (target in VARCHAR2, options in VARCHAR2) return CLOB; function Molfile (target in CLOB, options in VARCHAR2) return CLOB; function Molfile (target in BLOB, options in VARCHAR2) return CLOB; function CML (target in VARCHAR2) return CLOB; function CML (target in CLOB) return CLOB; function CML (target in BLOB) return CLOB; function SMILES (target in VARCHAR2) return VARCHAR2; function SMILES (target in CLOB) return VARCHAR2; function SMILES (target in BLOB) return VARCHAR2; function SMILES (target in VARCHAR2, options in VARCHAR2) return VARCHAR2; function SMILES (target in CLOB, options in VARCHAR2) return VARCHAR2; function SMILES (target in BLOB, options in VARCHAR2) return VARCHAR2; function CANSMILES (target in VARCHAR2) return VARCHAR2; function CANSMILES (target in CLOB) return VARCHAR2; function CANSMILES (target in BLOB) return VARCHAR2; function InChI (target in VARCHAR2, options in VARCHAR2) return CLOB; function InChI (target in CLOB, options in VARCHAR2) return CLOB; function InChI (target in BLOB, options in VARCHAR2) return CLOB; function InChIKey (inchi in VARCHAR2) return VARCHAR2; function InChIKey (inchi in CLOB) return VARCHAR2; function Fingerprint (target in VARCHAR2, options in VARCHAR2) return BLOB; function Fingerprint (target in CLOB, options in VARCHAR2) return BLOB; function Fingerprint (target in BLOB, options in VARCHAR2) return BLOB; END MangoPackage; / CREATE OR REPLACE PACKAGE BODY MangoPackage IS function Sub (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end Sub; function Sub (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end Sub; function Sub (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(to_clob(target), query, null, indexctx, scanctx, scanflg); end Sub; function Sub (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(to_clob(target), query, params, indexctx, scanctx, scanflg); end Sub; function Sub (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, to_clob(query), null, indexctx, scanctx, scanflg); end Sub; function Sub (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, to_clob(query), params, indexctx, scanctx, scanflg); end Sub; function Sub (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, query, null, indexctx, scanctx, scanflg); end Sub; function Sub (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Sub_clob(context_id, target, query, params); end Sub; function Sub (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, to_clob(query), null, indexctx, scanctx, scanflg); end Sub; function Sub (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, to_clob(query), params, indexctx, scanctx, scanflg); end Sub; function Sub (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sub(target, query, null, indexctx, scanctx, scanflg); end Sub; function Sub (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Sub_blob(context_id, target, query, params); end Sub; function SubHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(to_clob(target), query, null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(to_clob(target), query, params, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, query, null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return SubHi_clob(context_id, target, query, params); end SubHi; function SubHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return SubHi(target, query, null, indexctx, scanctx, scanflg); end SubHi; function SubHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return SubHi_blob(context_id, target, query, params); end SubHi; function Smarts (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin return Smarts(to_clob(target), query, indexctx, scanctx, scanflg); end Smarts; function Smarts (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Smarts_clob(context_id, target, query); end Smarts; function Smarts (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Smarts_blob(context_id, target, query); end Smarts; function SmartsHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return SmartsHi_clob(context_id, to_clob(target), query); end SmartsHi; function SmartsHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return SmartsHi_clob(context_id, target, query); end SmartsHi; function SmartsHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return SmartsHi_blob(context_id, target, query); end SmartsHi; function Exact (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end Exact; function Exact (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end Exact; function Exact (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(to_clob(target), query, null, indexctx, scanctx, scanflg); end Exact; function Exact (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(to_clob(target), query, params, indexctx, scanctx, scanflg); end Exact; function Exact (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, to_clob(query), null, indexctx, scanctx, scanflg); end Exact; function Exact (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, to_clob(query), params, indexctx, scanctx, scanflg); end Exact; function Exact (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, query, null, indexctx, scanctx, scanflg); end Exact; function Exact (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Exact_clob(context_id, target, query, params); end Exact; function Exact (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, to_clob(query), null, indexctx, scanctx, scanflg); end Exact; function Exact (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, to_clob(query), params, indexctx, scanctx, scanflg); end Exact; function Exact (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Exact(target, query, null, indexctx, scanctx, scanflg); end Exact; function Exact (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Exact_blob(context_id, target, query, params); end Exact; function ExactHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(to_clob(target), query, null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(to_clob(target), query, params, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, query, null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return ExactHi_clob(context_id, target, query, params); end ExactHi; function ExactHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS begin return ExactHi(target, query, null, indexctx, scanctx, scanflg); end ExactHi; function ExactHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return ExactHi_blob(context_id, target, query, params); end ExactHi; function Sim (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end Sim; function Sim (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end Sim; function Sim (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(to_clob(target), query, null, indexctx, scanctx, scanflg); end Sim; function Sim (target in VARCHAR2, query in CLOB, params VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(to_clob(target), query, params, indexctx, scanctx, scanflg); end Sim; function Sim (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, to_clob(query), null, indexctx, scanctx, scanflg); end Sim; function Sim (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, to_clob(query), params, indexctx, scanctx, scanflg); end Sim; function Sim (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, query, null, indexctx, scanctx, scanflg); end Sim; function Sim (target in CLOB, query in CLOB, params VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Sim_clob(context_id, target, query, params); end Sim; function Sim (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, to_clob(query), null, indexctx, scanctx, scanflg); end Sim; function Sim (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, to_clob(query), params, indexctx, scanctx, scanflg); end Sim; function Sim (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS begin return Sim(target, query, null, indexctx, scanctx, scanflg); end Sim; function Sim (target in BLOB, query in CLOB, params VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Sim_blob(context_id, target, query, params); end Sim; function GrossCalc (target in VARCHAR2) return VARCHAR2 IS begin return GrossCalc(to_clob(target)); end GrossCalc; function GrossCalc (target in CLOB) return VARCHAR2 IS begin return GrossCalc_clob(target); end GrossCalc; function GrossCalc (target in BLOB) return VARCHAR2 IS begin return GrossCalc_blob(target); end GrossCalc; function Gross (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is begin return Gross(to_clob(target), query, indexctx, scanctx, scanflg); end Gross; function Gross (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Gross_clob(context_id, target, query); end Gross; function Gross (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Gross_blob(context_id, target, query); end Gross; function Mass (target in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is begin return Mass(to_clob(target), indexctx, scanctx, scanflg); end Mass; function Mass (target in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Mass_clob(context_id, target, NULL); end Mass; function Mass (target in BLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out MangoIndex, scanflg IN NUMBER) return NUMBER is context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return Mass_blob(context_id, target, NULL); end Mass; function Mass (target in VARCHAR2, typee in VARCHAR2) return NUMBER is begin return Mass(to_clob(target), typee); end Mass; function Mass (target in CLOB, typee in VARCHAR2) return NUMBER is begin return Mass_clob(0, target, typee); end Mass; function Mass (target in BLOB, typee in VARCHAR2) return NUMBER is begin return Mass_blob(0, target, typee); end Mass; function Molfile (target in VARCHAR2) return CLOB is begin return Molfile_clob(to_clob(target), NULL); end Molfile; function Molfile (target in CLOB) return CLOB is begin return Molfile_clob(target, NULL); end Molfile; function Molfile (target in BLOB) return CLOB is begin return Molfile_blob(target, NULL); end Molfile; function Molfile (target in VARCHAR2, options in VARCHAR2) return CLOB is begin return Molfile_clob(to_clob(target), options); end Molfile; function Molfile (target in CLOB, options in VARCHAR2) return CLOB is begin return Molfile_clob(target, options); end Molfile; function Molfile (target in BLOB, options in VARCHAR2) return CLOB is begin return Molfile_blob(target, options); end Molfile; function CML (target in VARCHAR2) return CLOB is begin return CML_clob(to_clob(target)); end CML; function CML (target in CLOB) return CLOB is begin return CML_clob(target); end CML; function CML (target in BLOB) return CLOB is begin return CML_blob(target); end CML; function SMILES (target in VARCHAR2) return VARCHAR2 is begin return SMILES_clob(to_clob(target), NULL); end SMILES; function SMILES (target in CLOB) return VARCHAR2 is begin return SMILES_clob(target, NULL); end SMILES; function SMILES (target in BLOB) return VARCHAR2 is begin return SMILES_blob(target, NULL); end SMILES; function SMILES (target in VARCHAR2, options in VARCHAR2) return VARCHAR2 is begin return SMILES_clob(to_clob(target), options); end SMILES; function SMILES (target in CLOB, options in VARCHAR2) return VARCHAR2 is begin return SMILES_clob(target, options); end SMILES; function SMILES (target in BLOB, options in VARCHAR2) return VARCHAR2 is begin return SMILES_blob(target, options); end SMILES; function InChI (target in VARCHAR2, options in VARCHAR2) return CLOB is begin return InChI_clob(to_clob(target), options); end InChI; function InChI (target in CLOB, options in VARCHAR2) return CLOB is begin return InChI_clob(target, options); end InChI; function InChI (target in BLOB, options in VARCHAR2) return CLOB is begin return InChI_blob(target, options); end InChI; function InChIKey (inchi in VARCHAR2) return VARCHAR2 is begin return InChIKey_clob(to_clob(inchi)); end InChIKey; function InChIKey (inchi in CLOB) return VARCHAR2 is begin return InChIKey_clob(inchi); end InChIKey; function Fingerprint (target in VARCHAR2, options in VARCHAR2) return BLOB is begin return Fingerprint_clob(to_clob(target), options); end Fingerprint; function Fingerprint (target in CLOB, options in VARCHAR2) return BLOB is begin return Fingerprint_clob(target, options); end Fingerprint; function Fingerprint (target in BLOB, options in VARCHAR2) return BLOB is begin return Fingerprint_blob(target, options); end Fingerprint; function CANSMILES (target in VARCHAR2) return VARCHAR2 is begin return CANSMILES_clob(to_clob(target)); end CANSMILES; function CANSMILES (target in CLOB) return VARCHAR2 is begin return CANSMILES_clob(target); end CANSMILES; function CANSMILES (target in BLOB) return VARCHAR2 is begin return CANSMILES_blob(target); end CANSMILES; END MangoPackage; / -- necessary for Oracle 9 on Solaris grant execute on mangopackage to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/mango_stat.sql000066400000000000000000000466351271037650300233340ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool mango_stat; create or replace type MangoStat as object ( dummy number, static function ODCIGetInterfaces(ifclist out sys.ODCIObjectList) return NUMBER, static function ODCIStatsCollect(col sys.ODCIColInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER, static function ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv) return NUMBER, static function ODCIStatsCollect(ia sys.ODCIIndexInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER, static function ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function Selectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function FunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER ); / create or replace type body MangoStat is static function ODCIGetInterfaces (ifclist out sys.ODCIObjectList) return NUMBER is begin ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCISTATS2')); return ODCIConst.Success; end ODCIGetInterfaces; static function ODCIStatsCollect (col sys.ODCIColInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(col); mangoCollectStatistics(context_id); return ODCIConst.Success; end; static function ODCIStatsCollect (ia sys.ODCIIndexInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); mangoCollectStatistics(context_id); return ODCIConst.Success; end; static function ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv) return NUMBER is begin return ODCIConst.Success; end; static function ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is begin return ODCIConst.Success; end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function Selectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; flags binary_integer; begin if (args(3).ArgType != ODCIConst.ArgCol) THEN LogPrint('ODCIStatsSelectivity: args(3) type mismatch, returning error'); return ODCIConst.Error; end if; context_id := BingoPackage.getContextID(args(3).tableSchema, args(3).tableName, args(3).colName); flags := 0; if bitand(pred.Flags, ODCIConst.PredIncludeStart) != 0 then flags := flags + 16 - bitand(flags, 16); end if; if bitand(pred.Flags, ODCIConst.PredIncludeStop) != 0 then flags := flags + 32 - bitand(flags, 32); end if; if bitand(pred.Flags, ODCIConst.PredExactMatch) != 0 then flags := flags + 64 - bitand(flags, 64); end if; sel := mangoIndexSelectivity(context_id, pred.MethodName, query, strt, stop, flags, params); sel := least(100, ceil(100*sel)); return ODCIConst.Success; end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function FunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin cost := sys.ODCICost(NULL, NULL, NULL, NULL); cost.IOCost := 1; cost.CPUCost := 1; return ODCIConst.Success; end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, query, null, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is flags binary_integer; context_id binary_integer; iocost binary_integer; cpucost binary_integer; begin if args(3).ArgType != ODCIConst.ArgCol then LogPrint('ODCIStatsIndexCost: args(3) type mismatch, returning error'); return ODCIConst.Error; end if; context_id := BingoPackage.getContextID(args(3).tableSchema, args(3).tableName, args(3).colName); flags := 0; if bitand(pred.Flags, ODCIConst.PredIncludeStart) != 0 then flags := flags + 16 - bitand(flags, 16); end if; if bitand(pred.Flags, ODCIConst.PredIncludeStop) != 0 then flags := flags + 32 - bitand(flags, 32); end if; if bitand(pred.Flags, ODCIConst.PredExactMatch) != 0 then flags := flags + 64 - bitand(flags, 64); end if; mangoIndexCost(context_id, sel / 100.0, pred.ObjectName, query, strt, stop, flags, params, iocost, cpucost); cost := sys.ODCICost(NULL, NULL, NULL, NULL); cost.IOCost := iocost; cost.CPUCost := cpucost; return ODCIConst.Success; end; end; / grant execute on MangoStat to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_calls.sql000066400000000000000000000324431271037650300234640ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool ringo_calls; create or replace function AutoAAM_clob (target in CLOB, params in VARCHAR2) return CLOB AS language C name "oraRingoAAM" library bingolib with context parameters(context, target, target indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function AutoAAM_blob (target in BLOB, params in VARCHAR2) return CLOB AS language C name "oraRingoAAM" library bingolib with context parameters(context, target, target indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function RSub_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraRingoSub" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function RSub_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraRingoSub" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function RSubHi_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraRingoSubHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace function RSubHi_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return CLOB AS language C name "oraRingoSubHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCILobLocator); / create or replace procedure ringoCreateIndex(context_id in binary_integer, params in varchar2, full_table_name in varchar2, column_name in varchar2, column_data_type in varchar2) AS language C name "oraRingoCreateIndex" library bingolib with context parameters(context, context_id, params, params indicator short, full_table_name, full_table_name indicator short, column_name, column_name indicator short, column_data_type, column_data_type indicator short); / create or replace function RSmarts_clob (context_id in binary_integer, target in CLOB, query in VARCHAR2) return NUMBER AS language C name "oraRingoRSmarts" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function RSmarts_blob (context_id in binary_integer, target in BLOB, query in VARCHAR2) return NUMBER AS language C name "oraRingoRSmarts" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCINumber); / create or replace function RSmartsHi_clob (context_id in binary_integer, target in CLOB, query in VARCHAR2) return CLOB AS language C name "oraRingoRSmartsHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCILobLocator); / create or replace function RSmartsHi_blob (context_id in binary_integer, target in BLOB, query in VARCHAR2) return CLOB AS language C name "oraRingoRSmartsHi" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, return indicator short, return OCILobLocator); / create or replace function RExact_clob (context_id in binary_integer, target in CLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraRingoExact" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace function RExact_blob (context_id in binary_integer, target in BLOB, query in CLOB, params in VARCHAR2) return NUMBER AS language C name "oraRingoExact" library bingolib with context parameters(context, context_id, target, target indicator short, query, query indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace procedure ringoDropIndex (context_id in binary_integer) AS language C name "oraRingoDropIndex" library bingolib with context parameters(context, context_id); / create or replace procedure ringoTruncateIndex (context_id in binary_integer) AS language C name "oraRingoTruncateIndex" library bingolib with context parameters(context, context_id); / create or replace procedure ringoIndexInsert_clob (context_id in binary_integer, rid in VARCHAR2, item in CLOB) AS language C name "oraRingoIndexInsert" library bingolib with context parameters(context, context_id, rid, rid indicator short, item, item indicator short); / create or replace procedure ringoIndexInsert_blob (context_id in binary_integer, rid in VARCHAR2, item in BLOB) AS language C name "oraRingoIndexInsert" library bingolib with context parameters(context, context_id, rid, rid indicator short, item, item indicator short); / create or replace procedure ringoIndexDelete (context_id in binary_integer, rid in VARCHAR2) AS language C name "oraRingoIndexDelete" library bingolib with context parameters(context, context_id, rid, rid indicator short); / create or replace function ringoIndexStart (context_id in binary_integer, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, params in VARCHAR2) return binary_integer AS language C name "oraRingoIndexStart" library bingolib with context parameters(context, context_id, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, params, params indicator short); / create or replace function ringoIndexFetch (fetch_id in binary_integer, maxnrows in BINARY_INTEGER, arr in out sys.ODCIRidList) return binary_integer AS language C name "oraRingoIndexFetch" library bingolib with context parameters(context, fetch_id, maxnrows, arr, arr indicator short); / create or replace function ringoIndexSelectivity (context_id in binary_integer, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, params in VARCHAR2) return NUMBER AS language C name "oraRingoIndexSelectivity" library bingolib with context parameters(context, context_id, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, params, params indicator short, return indicator short, return OCINumber); / create or replace procedure ringoIndexCost (context_id in binary_integer, sel in NUMBER, oper in VARCHAR2, query in CLOB, strt in NUMBER, stop in NUMBER, params in VARCHAR2, iocost out binary_integer, cpucost out binary_integer) AS language C name "oraRingoIndexCost" library bingolib with context parameters(context, context_id, sel, sel indicator short, oper, oper indicator short, query, query indicator short, strt, strt indicator short, stop, stop indicator short, params, params indicator short, iocost, cpucost); / create or replace procedure ringoIndexClose (fetch_id in binary_integer) AS language C name "oraRingoIndexClose" library bingolib with context parameters(context, fetch_id); / create or replace procedure ringoCollectStatistics (context_id in binary_integer) AS language C name "oraRingoCollectStatistics" library bingolib with context parameters(context, context_id); / create or replace function Rxnfile_clob (r in CLOB) return CLOB AS language C name "oraRingoRxnfile" library bingolib with context parameters (context, r, r indicator short, return indicator short, return OCILobLocator); / create or replace function Rxnfile_blob (r in BLOB) return CLOB AS language C name "oraRingoRxnfile" library bingolib with context parameters (context, r, r indicator short, return indicator short, return OCILobLocator); / create or replace function RCML_clob (m in CLOB) return CLOB AS language C name "oraRingoCML" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCILobLocator); / create or replace function RCML_blob (m in BLOB) return CLOB AS language C name "oraRingoCML" library bingolib with context parameters (context, m, m indicator short, return indicator short, return OCILobLocator); / create or replace function RSMILES_clob (r in CLOB) return VARCHAR2 AS language C name "oraRingoRSMILES" library bingolib with context parameters (context, r, r indicator short, return indicator short, return OCIString); / create or replace function RSMILES_blob (r in BLOB) return VARCHAR2 AS language C name "oraRingoRSMILES" library bingolib with context parameters (context, r, r indicator short, return indicator short, return OCIString); / create or replace function RFingerprint_clob (r in CLOB, options in VARCHAR2) return BLOB AS language C name "oraRingoFingerprint" library bingolib with context parameters (context, r, r indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function RFingerprint_blob (r in BLOB, options in VARCHAR2) return BLOB AS language C name "oraRingoFingerprint" library bingolib with context parameters (context, r, r indicator short, options, options indicator short, return indicator short, return OCILobLocator); / create or replace function CheckReaction (r in CLOB) return VARCHAR2 AS language C name "oraRingoCheckReaction" library bingolib with context parameters (context, r, r indicator short, return indicator short, return OCIString); / grant execute on CheckReaction to public; / create or replace procedure CompactReaction2 (m in CLOB, res in BLOB, save_xyz in binary_integer) AS language C name "oraRingoICR2" library bingolib with context parameters (context, m, m indicator short, res, res indicator short, save_xyz); / grant execute on CompactReaction2 to public; / create or replace function CompactReaction (m in CLOB, save_xyz in binary_integer) return BLOB IS lob BLOB; BEGIN IF m is NULL THEN return NULL; END IF; dbms_lob.createtemporary(lob, TRUE, dbms_lob.call); CompactReaction2(m, lob, save_xyz); return lob; END CompactReaction; / grant execute on CompactReaction to public; / spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_drop.sql000066400000000000000000000012661271037650300233310ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. drop indextype ReactionIndex force; drop operator RSub force; drop operator RExact force; drop operator RSmarts force; drop package RingoPackage; drop type RingoIndex force; drop type RingoStat force; commit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_index.sql000066400000000000000000000241001271037650300234640ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool ringo_index; create or replace type RingoIndex as object ( fetch_id number, static function ODCIGetInterfaces (ifclist OUT sys.ODCIObjectList) return NUMBER, static function ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexAlter (ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDrop (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIIndexTruncate (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, newval CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval BLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, env sys.ODCIEnv) return NUMBER, static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, newval BLOB, env sys.ODCIEnv) return NUMBER, member function ODCIIndexFetch (nrows NUMBER, rids OUT sys.ODCIRidList, env sys.ODCIEnv) return NUMBER, member function ODCIIndexClose (env sys.ODCIEnv) return NUMBER ); / create or replace type body RingoIndex is static function ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList) return NUMBER is begin ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCIINDEX2')); return ODCIConst.Success; end; static function ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; col sys.ODCIColInfo := ia.IndexCols(1); begin context_id := BingoPackage.createContextID(ia); ringoCreateIndex(context_id, parms, '"'||col.TableSchema||'"."'||col.TableName||'"', col.ColName, col.ColTypeName); return ODCICONST.Success; end; static function ODCIIndexAlter (ia sys.ODCIIndexInfo, parms VARCHAR2, alter_option VARCHAR2, env sys.ODCIEnv) return NUMBER is begin AlterPackage.AlterIndex(ia, parms, alter_option, AlterPackage.ringoIndexType); return ODCICONST.Success; end; static function ODCIIndexDrop (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); if context_id is not null then ringoDropIndex(context_id); BingoPackage.deleteContextID(context_id); end if; return ODCICONST.Success; end; static function ODCIIndexTruncate (ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoTruncateIndex(context_id); return ODCICONST.Success; end; static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, to_clob(query), null, env); end; static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, to_clob(query), params, env); end; static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER is begin return ODCIIndexStart(sctx, ia, op, qi, strt, stop, query, null, env); end; static function ODCIIndexStart (sctx IN OUT RingoIndex, ia sys.ODCIIndexInfo, op sys.ODCIPredInfo, qi sys.ODCIQueryInfo, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; fetch_id binary_integer; begin context_id := BingoPackage.getContextID(ia); if bitand(op.Flags, ODCIConst.PredExactMatch) != 0 then fetch_id := ringoIndexStart(context_id, op.ObjectName, query, strt, strt, params); else fetch_id := ringoIndexStart(context_id, op.ObjectName, query, strt, stop, params); end if; sctx := RingoIndex(fetch_id); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexInsert_clob(context_id, rid, to_clob(newval)); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval VARCHAR2, newval VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); ringoIndexInsert_clob(context_id, rid, to_clob(newval)); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexInsert_clob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval CLOB, newval CLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); ringoIndexInsert_clob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexInsert (ia sys.ODCIIndexInfo, rid VARCHAR2, newval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexInsert_blob(context_id, rid, newval); return ODCICONST.Success; end; static function ODCIIndexDelete (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); return ODCICONST.Success; end; static function ODCIIndexUpdate (ia sys.ODCIIndexInfo, rid VARCHAR2, oldval BLOB, newval BLOB, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoIndexDelete(context_id, rid); ringoIndexInsert_blob(context_id, rid, newval); return ODCICONST.Success; end; member function ODCIIndexFetch (nrows NUMBER, rids OUT sys.ODCIRidList, env sys.ODCIEnv) return NUMBER is rlist sys.odciridlist := sys.odciridlist(); res int; begin res := ringoIndexFetch(fetch_id, nrows, rlist); if res = 0 then -- fetch is complete rlist.extend; -- add zero to the end of fetched list end if; rids := rlist; return ODCICONST.Success; end; member function ODCIIndexClose (env sys.ODCIEnv) return NUMBER is begin ringoIndexClose(fetch_id); return ODCICONST.Success; end; end; / -- this is necessary for Oracle 9 grant execute on RingoIndex to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_indextype.sql000066400000000000000000000200171271037650300243710ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool ringo_indextype; create or replace operator RSub binding (VARCHAR2, CLOB) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (VARCHAR2, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (VARCHAR2, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (CLOB, CLOB) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (CLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (CLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (BLOB, CLOB) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (BLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (BLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub, (BLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSub; create or replace operator RSubHi binding (NUMBER) return CLOB ANCILLARY TO RSub (VARCHAR2, CLOB), RSub (VARCHAR2, CLOB, VARCHAR2), RSub (VARCHAR2, VARCHAR2), RSub (VARCHAR2, VARCHAR2, VARCHAR2), RSub (CLOB, CLOB), RSub (CLOB, CLOB, VARCHAR2), RSub (CLOB, VARCHAR2), RSub (CLOB, VARCHAR2, VARCHAR2), RSub (BLOB, CLOB), RSub (BLOB, CLOB, VARCHAR2), RSub (BLOB, VARCHAR2), RSub (BLOB, VARCHAR2, VARCHAR2) using RingoPackage.RSubHi; create or replace operator RSmarts binding (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSmarts, (CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSmarts, (BLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex compute ancillary data using RingoPackage.RSmarts; create or replace operator RSmartsHi binding (NUMBER) return CLOB ANCILLARY TO RSmarts (VARCHAR2, VARCHAR2), RSmarts (CLOB, VARCHAR2), RSmarts (BLOB, VARCHAR2) using RingoPackage.RSmartsHi; create or replace operator RExact binding (VARCHAR2, CLOB) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (VARCHAR2, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (VARCHAR2, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (CLOB, CLOB) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (CLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (CLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (BLOB, CLOB) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (BLOB, CLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (BLOB, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact, (BLOB, VARCHAR2, VARCHAR2) return NUMBER with index context, scan context RingoIndex using RingoPackage.RExact; create or replace operator AAM binding (VARCHAR2, VARCHAR2) return CLOB using RingoPackage.AAM, (CLOB, VARCHAR2) return CLOB using RingoPackage.AAM, (BLOB, VARCHAR2) return CLOB using RingoPackage.AAM; create or replace operator Rxnfile binding (VARCHAR2) return CLOB using RingoPackage.Rxnfile, (CLOB) return CLOB using RingoPackage.Rxnfile, (BLOB) return CLOB using RingoPackage.Rxnfile; create or replace operator RCML binding (VARCHAR2) return CLOB using RingoPackage.RCML, (CLOB) return CLOB using RingoPackage.RCML, (BLOB) return CLOB using RingoPackage.RCML; create or replace operator RSMILES binding (VARCHAR2) return VARCHAR2 using RingoPackage.RSMILES, (CLOB) return VARCHAR2 using RingoPackage.RSMILES, (BLOB) return VARCHAR2 using RingoPackage.RSMILES; create or replace operator RFingerprint binding (VARCHAR2, VARCHAR2) return BLOB using RingoPackage.RFingerprint, (CLOB, VARCHAR2) return BLOB using RingoPackage.RFingerprint, (BLOB, VARCHAR2) return BLOB using RingoPackage.RFingerprint; grant execute on RSub to public; grant execute on RSubHi to public; grant execute on RExact to public; grant execute on RSmarts to public; grant execute on RSmartsHi to public; grant execute on AAM to public; grant execute on RSMILES to public; grant execute on Rxnfile to public; grant execute on RCML to public; grant execute on RFingerprint to public; create or replace indextype ReactionIndex for RSub(VARCHAR2, CLOB), RSub(VARCHAR2, CLOB, VARCHAR2), RSub(VARCHAR2, VARCHAR2), RSub(VARCHAR2, VARCHAR2, VARCHAR2), RSub(CLOB, CLOB), RSub(CLOB, CLOB, VARCHAR2), RSub(CLOB, VARCHAR2), RSub(CLOB, VARCHAR2, VARCHAR2), RSub(BLOB, CLOB), RSub(BLOB, CLOB, VARCHAR2), RSub(BLOB, VARCHAR2), RSub(BLOB, VARCHAR2, VARCHAR2), RSmarts(VARCHAR2, VARCHAR2), RSmarts(CLOB, VARCHAR2), RSmarts(BLOB, VARCHAR2), RExact(VARCHAR2, CLOB), RExact(VARCHAR2, CLOB, VARCHAR2), RExact(VARCHAR2, VARCHAR2), RExact(VARCHAR2, VARCHAR2, VARCHAR2), RExact(CLOB, CLOB), RExact(CLOB, CLOB, VARCHAR2), RExact(CLOB, VARCHAR2), RExact(CLOB, VARCHAR2, VARCHAR2), RExact(BLOB, CLOB), RExact(BLOB, CLOB, VARCHAR2), RExact(BLOB, VARCHAR2), RExact(BLOB, VARCHAR2, VARCHAR2) using RingoIndex; associate statistics with packages RingoPackage using RingoStat; associate statistics with indextypes ReactionIndex using RingoStat; grant execute on ReactionIndex to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_make.sql000066400000000000000000000011141271037650300232720ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @@ringo_calls.sql @@ringo_index.sql @@ringo_stat.sql @@ringo_package.sql @@ringo_indextype.sql commit; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_package.sql000066400000000000000000000565551271037650300237730ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool ringo_package; CREATE OR REPLACE PACKAGE RingoPackage IS function RSub (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSub (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSubHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSubHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSmarts (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSmarts (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSmarts (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RSmartsHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSmartsHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RSmartsHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB; function RExact (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function RExact (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER; function AAM (target in VARCHAR2, params in VARCHAR2) return CLOB; function AAM (target in CLOB, params in VARCHAR2) return CLOB; function AAM (target in BLOB, params in VARCHAR2) return CLOB; function Rxnfile (target in VARCHAR2) return CLOB; function Rxnfile (target in CLOB) return CLOB; function Rxnfile (target in BLOB) return CLOB; function RCML (target in VARCHAR2) return CLOB; function RCML (target in CLOB) return CLOB; function RCML (target in BLOB) return CLOB; function RSMILES (target in VARCHAR2) return VARCHAR2; function RSMILES (target in CLOB) return VARCHAR2; function RSMILES (target in BLOB) return VARCHAR2; function RFingerprint (target in VARCHAR2, options in VARCHAR2) return BLOB; function RFingerprint (target in CLOB, options in VARCHAR2) return BLOB; function RFingerprint (target in BLOB, options in VARCHAR2) return BLOB; END RingoPackage; / CREATE OR REPLACE PACKAGE BODY RingoPackage IS function RSub (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end RSub; function RSub (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end RSub; function RSub (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(to_clob(target), query, null, indexctx, scanctx, scanflg); end RSub; function RSub (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(to_clob(target), query, params, indexctx, scanctx, scanflg); end RSub; function RSub (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, to_clob(query), null, indexctx, scanctx, scanflg); end RSub; function RSub (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, to_clob(query), params, indexctx, scanctx, scanflg); end RSub; function RSub (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, query, null, indexctx, scanctx, scanflg); end RSub; function RSub (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSub_clob(context_id, target, query, params); end RSub; function RSub (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, to_clob(query), null, indexctx, scanctx, scanflg); end RSub; function RSub (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, to_clob(query), params, indexctx, scanctx, scanflg); end RSub; function RSub (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RSub(target, query, null, indexctx, scanctx, scanflg); end RSub; function RSub (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSub_blob(context_id, target, query, params); end RSub; function RSubHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(to_clob(target), query, null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(to_clob(target), query, params, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, query, null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSubHi_clob(context_id, target, query, params); end RSubHi; function RSubHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, to_clob(query), null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, to_clob(query), params, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS begin return RSubHi(target, query, null, indexctx, scanctx, scanflg); end RSubHi; function RSubHi (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSubHi_blob(context_id, target, query, params); end RSubHi; function RSmarts (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin return RSmarts(to_clob(target), query, indexctx, scanctx, scanflg); end RSmarts; function RSmarts (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSmarts_clob(context_id, target, query); end RSmarts; function RSmarts (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSmarts_blob(context_id, target, query); end RSmarts; function RSmartsHi (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSmartsHi_clob(context_id, to_clob(target), query); end RSmartsHi; function RSmartsHi (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSmartsHi_clob(context_id, target, query); end RSmartsHi; function RSmartsHi (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return CLOB IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RSmartsHi_blob(context_id, target, query); end RSmartsHi; function RExact (target in VARCHAR2, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(to_clob(target), to_clob(query), null, indexctx, scanctx, scanflg); end RExact; function RExact (target in VARCHAR2, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(to_clob(target), to_clob(query), params, indexctx, scanctx, scanflg); end RExact; function RExact (target in VARCHAR2, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(to_clob(target), query, null, indexctx, scanctx, scanflg); end RExact; function RExact (target in VARCHAR2, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(to_clob(target), query, params, indexctx, scanctx, scanflg); end RExact; function RExact (target in CLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, to_clob(query), null, indexctx, scanctx, scanflg); end RExact; function RExact (target in CLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, to_clob(query), params, indexctx, scanctx, scanflg); end RExact; function RExact (target in CLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, query, null, indexctx, scanctx, scanflg); end RExact; function RExact (target in CLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RExact_clob(context_id, target, query, params); end RExact; function RExact (target in BLOB, query in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, to_clob(query), null, indexctx, scanctx, scanflg); end RExact; function RExact (target in BLOB, query in VARCHAR2, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, to_clob(query), params, indexctx, scanctx, scanflg); end RExact; function RExact (target in BLOB, query in CLOB, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS begin return RExact(target, query, null, indexctx, scanctx, scanflg); end RExact; function RExact (target in BLOB, query in CLOB, params in VARCHAR2, indexctx IN sys.ODCIIndexCtx, scanctx in out RingoIndex, scanflg IN NUMBER) return NUMBER IS context_id binary_integer := 0; begin if indexctx.IndexInfo is not null then context_id := BingoPackage.getContextID(indexctx.IndexInfo); end if; return RExact_blob(context_id, target, query, params); end RExact; function AAM (target in VARCHAR2, params in VARCHAR2) return CLOB IS begin return AutoAAM_clob(to_clob(target), params); end AAM; function AAM (target in CLOB, params in VARCHAR2) return CLOB IS begin return AutoAAM_clob(target, params); end AAM; function AAM (target in BLOB, params in VARCHAR2) return CLOB IS begin return AutoAAM_blob(target, params); end AAM; function Rxnfile (target in VARCHAR2) return CLOB is begin return Rxnfile_clob(to_clob(target)); end Rxnfile; function Rxnfile (target in CLOB) return CLOB is begin return Rxnfile_clob(target); end Rxnfile; function Rxnfile (target in BLOB) return CLOB is begin return Rxnfile_blob(target); end Rxnfile; function RCML (target in VARCHAR2) return CLOB is begin return RCML_clob(to_clob(target)); end RCML; function RCML (target in CLOB) return CLOB is begin return RCML_clob(target); end RCML; function RCML (target in BLOB) return CLOB is begin return RCML_blob(target); end RCML; function RSMILES (target in VARCHAR2) return VARCHAR2 is begin return RSMILES_clob(to_clob(target)); end RSMILES; function RSMILES (target in CLOB) return VARCHAR2 is begin return RSMILES_clob(target); end RSMILES; function RSMILES (target in BLOB) return VARCHAR2 is begin return RSMILES_blob(target); end RSMILES; function RFingerprint (target in VARCHAR2, options in VARCHAR2) return BLOB is begin return RFingerprint_clob(to_clob(target), options); end RFingerprint; function RFingerprint (target in CLOB, options in VARCHAR2) return BLOB is begin return RFingerprint_clob(target, options); end RFingerprint; function RFingerprint (target in BLOB, options in VARCHAR2) return BLOB is begin return RFingerprint_blob(target, options); end RFingerprint; END RingoPackage; / -- necessary for Oracle 9 on Solaris grant execute on ringopackage to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/bingo/ringo_stat.sql000066400000000000000000000452171271037650300233440ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. set verify off spool ringo_stat; create or replace type RingoStat as object ( dummy number, static function ODCIGetInterfaces(ifclist out sys.ODCIObjectList) return NUMBER, static function ODCIStatsCollect(col sys.ODCIColInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER, static function ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv) return NUMBER, static function ODCIStatsCollect(ia sys.ODCIIndexInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER, static function ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function Selectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function FunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER, static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER ); / create or replace type body RingoStat is static function ODCIGetInterfaces (ifclist out sys.ODCIObjectList) return NUMBER is begin ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCISTATS2')); return ODCIConst.Success; end ODCIGetInterfaces; static function ODCIStatsCollect (col sys.ODCIColInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(col); ringoCollectStatistics(context_id); return ODCIConst.Success; end; static function ODCIStatsCollect (ia sys.ODCIIndexInfo, options sys.ODCIStatsOptions, rawstats out RAW, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin context_id := BingoPackage.getContextID(ia); ringoCollectStatistics(context_id); return ODCIConst.Success; end; static function ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv) return NUMBER is begin return ODCIConst.Success; end; static function ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv) return NUMBER is begin return ODCIConst.Success; end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, null, env); end; static function ODCIStatsSelectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return Selectivity(pred, sel, args, strt, stop, query, params, env); end; static function Selectivity (pred sys.ODCIPredInfo, sel out NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; begin if (args(3).ArgType != ODCIConst.ArgCol) THEN LogPrint('ODCIStatsSelectivity: args(3) type mismatch, returning error'); return ODCIConst.Error; end if; context_id := BingoPackage.getContextID(args(3).tableSchema, args(3).tableName, args(3).colName); if bitand(pred.Flags, ODCIConst.PredExactMatch) != 0 then sel := ringoIndexSelectivity(context_id, pred.MethodName, query, strt, strt, params); else sel := ringoIndexSelectivity(context_id, pred.MethodName, query, strt, stop, params); end if; sel := least(100, ceil(100*sel)); return ODCIConst.Success; end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target VARCHAR2, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target CLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, to_clob(query), params, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, null, env); end; static function ODCIStatsFunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, target BLOB, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return FunctionCost(func, cost, args, query, params, env); end; static function FunctionCost(func sys.ODCIFuncInfo, cost out sys.ODCICost, args sys.ODCIArgDescList, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin cost := sys.ODCICost(NULL, NULL, NULL, NULL); cost.IOCost := 1; cost.CPUCost := 1; return ODCIConst.Success; end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, to_clob(query), null, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query VARCHAR2, params VARCHAR2, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, to_clob(query), params, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, env sys.ODCIEnv) return NUMBER is begin return ODCIStatsIndexCost(ia, sel, cost, qi, pred, args, strt, stop, query, null, env); end; static function ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, cost out sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, query CLOB, params VARCHAR2, env sys.ODCIEnv) return NUMBER is context_id binary_integer; iocost binary_integer; cpucost binary_integer; begin if args(3).ArgType != ODCIConst.ArgCol then LogPrint('ODCIStatsIndexCost: args(3) type mismatch, returning error'); return ODCIConst.Error; end if; context_id := BingoPackage.getContextID(args(3).tableSchema, args(3).tableName, args(3).colName); ringoIndexCost(context_id, sel / 100.0, pred.ObjectName, query, strt, stop, params, iocost, cpucost); cost := sys.ODCICost(NULL, NULL, NULL, NULL); cost.IOCost := iocost; cost.CPUCost := cpucost; return ODCIConst.Success; end; end; / grant execute on RingoStat to public; spool off; Indigo-indigo-1.2.3/bingo/oracle/sql/dbcheck.sql000066400000000000000000000046251271037650300214560ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Set Verify Off Set Heading Off Set Feedback Off set arraysize 1 set maxdata 6000 Create Or Replace Function CHECK_DB_OBJECTS Return Varchar2 Is nTables Number ; nViews Number ; nProcedures Number ; nFunctions Number ; nPackages Number ; nSequences Number ; nInvalids Number ; Begin Select Count (*) Into nTables From USER_OBJECTS Where OBJECT_TYPE = 'TABLE'; Select Count (*) Into nViews From USER_OBJECTS Where OBJECT_TYPE = 'VIEW'; Select Count (*) Into nProcedures From USER_OBJECTS Where OBJECT_TYPE = 'PROCEDURE'; Select Count (*) Into nFunctions From USER_OBJECTS Where OBJECT_TYPE = 'FUNCTION'; Select Count (*) Into nPackages From USER_OBJECTS Where OBJECT_TYPE = 'PACKAGE'; Select Count (*) Into nSequences From USER_OBJECTS Where OBJECT_TYPE = 'SEQUENCE'; Select Count (*) Into nInvalids From USER_OBJECTS Where STATUS = 'INVALID'; Return ( '--------- REPORT ---------' || chr (13) || chr (10) || 'Tables: ' || chr (09) || chr (09) || nTables || chr (13) || chr (10) || 'Views: ' || chr (09) || chr (09) || chr (09) || nViews || chr (13) || chr (10) || 'Packages: ' || chr (09) || chr (09) || nPackages || chr (13) || chr (10) || 'Procedures: ' || chr (09) || chr (09) || nProcedures || chr (13) || chr (10) || 'Functions: ' || chr (09) || chr (09) || nFunctions || chr (13) || chr (10) || 'Sequences: ' || chr (09) || chr (09) || nSequences || chr (13) || chr (10) || '--------------------------' ) ; End ; / Select CHECK_DB_OBJECTS From Sys.Dual ; Drop Function CHECK_DB_OBJECTS; Select OBJECT_TYPE || ' ' || OBJECT_NAME From USER_OBJECTS Where STATUS = 'INVALID' Order By OBJECT_TYPE ; Select * From USER_ERRORS; Prompt DB check executed. --Prompt Type QUIT to leave SQLPLUS command prompt. exit; Indigo-indigo-1.2.3/bingo/oracle/sql/system/000077500000000000000000000000001271037650300206675ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/sql/system/bingo_init.sql000066400000000000000000000032201271037650300235260ustar00rootroot00000000000000-- Copyright (C) 2009-2015 EPAM Systems -- -- This file is part of Indigo toolkit. -- -- This file may be distributed and/or modified under the terms of the -- GNU General Public License version 3 as published by the Free Software -- Foundation and appearing in the file LICENSE.GPL included in the -- packaging of this file. -- -- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Define USER_NAME = &1 Define USER_PASS = &2 Set Verify Off spool bingo_init; column TBS_TYPE new_value TBS_TYPE select case when version like '9.%' then null else 'BIGFILE' end TBS_TYPE from v$instance; CREATE &TBS_TYPE TABLESPACE &USER_NAME NOLOGGING DATAFILE '&USER_NAME..ora' SIZE 64M REUSE AUTOEXTEND ON NEXT 64M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL ; CREATE TEMPORARY TABLESPACE &USER_NAME._TEMP TEMPFILE '&USER_NAME._temp.ora' SIZE 64M REUSE AUTOEXTEND ON NEXT 64M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M ; create user &USER_NAME identified by &USER_PASS default tablespace &USER_NAME temporary tablespace &USER_NAME._TEMP ; grant connect to &USER_NAME; grant create type to &USER_NAME; grant create table to &USER_NAME; grant create library to &USER_NAME; grant create operator to &USER_NAME; grant create procedure to &USER_NAME; grant create sequence to &USER_NAME; grant create indextype to &USER_NAME; grant unlimited tablespace to &USER_NAME; grant create trigger to &USER_NAME; grant administer database trigger to &USER_NAME; grant select any table to &USER_NAME; spool off; exit; Indigo-indigo-1.2.3/bingo/oracle/src/000077500000000000000000000000001271037650300173335ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/src/oracle/000077500000000000000000000000001271037650300206005ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_fetch_engine.cpp000066400000000000000000000014621271037650300251030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "bingo_fetch_engine.h" using namespace indigo; CP_DEF(BingoFetchEngine); BingoFetchEngine::BingoFetchEngine () : CP_INIT, TL_CP_GET(matched) { matched.clear(); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_fetch_engine.h000066400000000000000000000025401271037650300245460ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_fetch_engine__ #define __bingo_fetch_engine__ #include "oracle/ora_wrap.h" #include "base_cpp/list.h" #include "base_cpp/tlscont.h" using namespace indigo; class BingoFetchEngine { public: explicit BingoFetchEngine (); virtual ~BingoFetchEngine () {} virtual float calcSelectivity (OracleEnv &env, int total_count) = 0; virtual void fetch (OracleEnv &env, int maxrows) = 0; virtual bool end () = 0; virtual int getIOCost (OracleEnv &env, float selectivity) = 0; // In case of exceptions this rowid corresponds to the structure that was tried to be matched last time virtual bool getLastRowid (OraRowidText &id) = 0; CP_DECL; TL_CP_DECL(List, matched); }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_fingerprints.cpp000066400000000000000000000624471271037650300252110ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ora_wrap.h" #include "base_c/bitarray.h" #include "oracle/bingo_fingerprints.h" #include "base_cpp/output.h" #include "oracle/ora_wrap.h" #include "core/bingo_context.h" #include "base_cpp/scanner.h" #include "base_c/nano.h" #include "bingo_storage.h" #include "base_cpp/profiling.h" IMPL_ERROR(BingoFingerprints, "screening"); CP_DEF(BingoFingerprints); BingoFingerprints::BingoFingerprints (int context_id) : CP_INIT, TL_CP_GET(_table_name) { ArrayOutput output(_table_name); output.printf("FINGERPRINTS_%d", context_id); output.writeChar(0); _part_adding = -1; _total_count_cached = -1; } void BingoFingerprints::init (BingoContext &context, int fp_bytes, int fp_priority_bytes_min, int fp_priority_bytes_max) { _fp_bytes = fp_bytes; if (fp_priority_bytes_min != -1) { _fp_priority_bytes_min = fp_priority_bytes_min; _fp_priority_bytes_max = fp_priority_bytes_max; } else { _fp_priority_bytes_min = 0; _fp_priority_bytes_max = fp_bytes; } _chunk_qwords = context.fp_chunk_qwords; } void BingoFingerprints::create (OracleEnv &env) { const char *tn = _table_name.ptr(); const char *securefile = ""; if (env.serverMajorVersion() >= 11) securefile = "securefile"; OracleStatement::executeSingle(env, "CREATE TABLE %s(part number, used number, counters BLOB, mapping BLOB, " "bit_starts BLOB, bit_ends BLOB, bits BLOB) PCTFREE 0 NOLOGGING " "lob(bits) store as %s (DISABLE STORAGE IN ROW CACHE READS NOLOGGING PCTVERSION 0) " "lob(counters) store as %s (DISABLE STORAGE IN ROW CACHE READS NOLOGGING PCTVERSION 0) " "lob(bit_starts) store as %s (DISABLE STORAGE IN ROW CACHE READS NOLOGGING PCTVERSION 0) " "lob(bit_ends) store as %s (DISABLE STORAGE IN ROW CACHE READS NOLOGGING PCTVERSION 0) " "lob(mapping) store as %s (DISABLE STORAGE IN ROW CACHE READS NOLOGGING PCTVERSION 0) ", tn, securefile, securefile, securefile, securefile, securefile); OracleStatement::executeSingle(env, "CREATE INDEX %s_part ON %s(part)", tn, tn); } void BingoFingerprints::drop (OracleEnv &env) { OracleStatement::executeSingle(env, "BEGIN DropTable('%s'); END;", _table_name.ptr()); } void BingoFingerprints::truncate (OracleEnv &env) { OracleStatement::executeSingle(env, "TRUNCATE TABLE %s", _table_name.ptr()); } void BingoFingerprints::addFingerprint (OracleEnv &env, const byte *fp) { if (_pending_block.used == _chunk_qwords * 64) flush(env); byte *ptr = (byte *)_pending_bits.ptr(); for (int i = 0; i < _fp_bytes * 8; i++) { if (bitGetBit(fp, i)) { bitSetBit(ptr, _pending_block.used, 1); if (_pending_block.counters[i] < 65535) _pending_block.counters[i]++; _pending_block.bit_ends[i] = _pending_block.used; } ptr += 8 * _chunk_qwords; } _pending_block.mapping.push(_pending_block.mapping.size()); _pending_block.used++; } void BingoFingerprints::flush (OracleEnv &env) { if (_pending_block.used < 1) return; env.dbgPrintfTS("flushing %d items\n", _pending_block.used); // Transpose bits env.dbgPrintf(" optimizing pending block..."); _optimizePendingBlock(); env.dbgPrintf("ok\n"); if (!_flush_Update(env, true)) _flush_Insert(env); _initBlock(_pending_block, true); _part_adding++; _all_blocks.clear(); } void BingoFingerprints::_initBlock (Block &block, bool is_pending_block) { block.used = 0; if (is_pending_block) { _pending_bits.clear_resize(_fp_bytes * _chunk_qwords * 8); _pending_bits.zerofill(); } block.bit_starts.clear_resize(_fp_bytes * 8); block.bit_starts.zerofill(); block.bit_ends.clear_resize(_fp_bytes * 8); block.bit_ends.zerofill(); block.counters.clear_resize(_fp_bytes * 8); block.counters.zerofill(); block.mapping.clear(); } bool BingoFingerprints::_flush_Update (OracleEnv &env, bool update_counter) { OracleStatement statement(env); OracleLOB lob_counters(env), lob_bits(env), lob_mapping(env), lob_bit_starts(env), lob_bit_ends(env); statement.append("SELECT counters, mapping, bit_starts, bit_ends, bits from %s WHERE part = :part FOR UPDATE", _table_name.ptr()); statement.prepare(); statement.bindIntByName(":part", &_part_adding); statement.defineBlobByPos(1, lob_counters); statement.defineBlobByPos(2, lob_mapping); statement.defineBlobByPos(3, lob_bit_starts); statement.defineBlobByPos(4, lob_bit_ends); statement.defineBlobByPos(5, lob_bits); if (!statement.executeAllowNoData()) return false; lob_bits.write(0, (char *)_pending_bits.ptr(), _pending_bits.size() * 8); Block &block = _pending_block; lob_counters.write(0, (char *)block.counters.ptr(), block.counters.sizeInBytes()); lob_mapping.write(0, (char *)block.mapping.ptr(), block.mapping.sizeInBytes()); lob_bit_starts.write(0, (char *)block.bit_starts.ptr(), block.bit_starts.sizeInBytes()); lob_bit_ends.write(0, (char *)block.bit_ends.ptr(), block.bit_ends.sizeInBytes()); if (update_counter) { OracleStatement statement1(env); statement1.append("UPDATE %s SET used = :used WHERE part = :part", _table_name.ptr()); statement1.prepare(); statement1.bindIntByName(":used", &_pending_block.used); statement1.bindIntByName(":part", &_part_adding); statement1.execute(); } return true; } void BingoFingerprints::_flush_Insert (OracleEnv &env) { OracleStatement statement(env); statement.append("INSERT INTO %s VALUES(:part, :used, " "empty_blob(), empty_blob(), empty_blob(), empty_blob(), empty_blob())", _table_name.ptr()); statement.prepare(); statement.bindIntByName(":part", &_part_adding); statement.bindIntByName(":used", &_pending_block.used); statement.execute(); _flush_Update(env, false); } void BingoFingerprints::_flush_Insert_OLD (OracleEnv &env) { OracleStatement statement(env); OracleLOB lob_counters(env), lob_bits(env), lob_mapping(env), lob_bit_starts(env), lob_bit_ends(env); lob_counters.createTemporaryBLOB(); lob_bits.createTemporaryBLOB(); lob_mapping.createTemporaryBLOB(); lob_bit_starts.createTemporaryBLOB(); lob_bit_ends.createTemporaryBLOB(); Block &block = _pending_block; lob_bits.write(0, (char *)_pending_bits.ptr(), _pending_bits.size() * 8); lob_counters.write(0, (char *)block.counters.ptr(), block.counters.sizeInBytes()); lob_mapping.write(0, (char *)block.mapping.ptr(), block.mapping.sizeInBytes()); lob_bit_starts.write(0, (char *)block.bit_starts.ptr(), block.bit_starts.sizeInBytes()); lob_bit_ends.write(0, (char *)block.bit_ends.ptr(), block.bit_ends.sizeInBytes()); statement.append("INSERT INTO %s VALUES(:part, :used, :counters, " ":mapping, :bit_starts, :bit_ends, :bits)", _table_name.ptr()); statement.prepare(); statement.bindIntByName(":used", &_pending_block.used); statement.bindIntByName(":part", &_part_adding); statement.bindBlobByName(":counters", lob_counters); statement.bindBlobByName(":mapping", lob_mapping); statement.bindBlobByName(":bit_starts", lob_bit_starts); statement.bindBlobByName(":bit_ends", lob_bit_ends); statement.bindBlobByName(":bits", lob_bits); statement.execute(); } void BingoFingerprints::screenInit (const byte *fp, Screening &screening) { screening.query_ones.clear(); screening.passed.clear(); for (int i = 0; i < _fp_bytes * 8; i++) if (bitGetBit(fp, i)) screening.query_ones.push(i); screening.part = 0; screening.block = 0; screening.items_read = 0; screening.items_passed = 0; } bool BingoFingerprints::ableToScreen (Screening &screening) { return screening.query_ones.size() > 0; } bool BingoFingerprints::screenPart_Init (OracleEnv &env, Screening &screening) { screening.passed.clear(); if (screening.query_ones.size() < 1) throw Error("no screening bits"); int used_qwords; if (screening.part != _part_adding) { if (screening.part >= _all_blocks.size()) return false; screening.block = &_all_blocks[screening.part]; screening.statement.create(env); screening.bits_lob.create(env); screening.statement->append("SELECT bits FROM %s WHERE part = :part", _table_name.ptr()); screening.statement->prepare(); screening.statement->bindIntByName(":part", &screening.part); screening.statement->defineBlobByPos(1, screening.bits_lob.ref()); screening.statement->execute(); } else screening.block = &_pending_block; used_qwords = (screening.block->used + 63) / 64; screening.items_read += screening.block->used; if (used_qwords == 0) return false; screening.data = this; screening.query_ones.qsort(_cmp_counters, &screening); screening.fp_final.clear_resize(used_qwords); screening.fp_final.zerofill(); screening.query_bit_idx = 0; return true; } bool BingoFingerprints::screenPart_Next (OracleEnv &env, Screening &screening) { QS_DEF(Array, fp_inter); // i.e. intermediate int used_bytes = (screening.block->used + 7) / 8; if (screening.query_bit_idx >= screening.query_ones.size()) return false; int used_qwords = screening.fp_final.size(); fp_inter.clear_resize(used_qwords); screening.passed_pre.clear(); int query_bit = screening.query_ones[screening.query_bit_idx]; if (screening.query_bit_idx == 0) { profTimerStart(tread, "fingerprints.read"); word bit_start_read = screening.block->bit_starts[query_bit]; word bit_end_read = screening.block->bit_ends[query_bit]; if (bit_start_read > bit_end_read) { // This bit wasn't used in fingerprints in current block screening.query_bit_idx++; screening.fp_final.zerofill(); screening.start_offset = -1; screening.end_offset = -1; return true; } screening.start_offset = bit_start_read / 8; screening.end_offset = bit_end_read / 8; int read_bytes_count = screening.end_offset - screening.start_offset + 1; if (screening.part != _part_adding) screening.bits_lob->read(query_bit * _chunk_qwords * 8 + screening.start_offset, (char *)screening.fp_final.ptr() + screening.start_offset, read_bytes_count); else memcpy(screening.fp_final.ptr(), _pending_bits.ptr() + query_bit * _chunk_qwords, used_qwords * 8); profIncCounter("fingerprints.read_nbytes", read_bytes_count); profIncCounter("fingerprints.read_nbytes_old", used_qwords * 8); } else { profTimerStart(tread, "fingerprints.read"); if (screening.start_offset < 0) { // The first bit was not used in fingerprints in current block screening.query_bit_idx++; return true; } // reduce range to reduce number of reading bytes const char *bits_final = (const char *)screening.fp_final.ptr(); while (bits_final[screening.start_offset] == 0) { screening.start_offset++; if (screening.start_offset >= used_qwords * 8) break; } while (screening.end_offset >= 0 && bits_final[screening.end_offset] == 0) { screening.end_offset--; } int read_bytes_count = screening.end_offset - screening.start_offset + 1; if (read_bytes_count <= 0) { screening.fp_final.zerofill(); screening.query_bit_idx++; return true; } else { if (screening.part != _part_adding) screening.bits_lob->read(query_bit * _chunk_qwords * 8 + screening.start_offset, (char *)fp_inter.ptr() + screening.start_offset, read_bytes_count); else memcpy(fp_inter.ptr(), _pending_bits.ptr() + query_bit * _chunk_qwords, used_qwords * 8); profTimerStop(tread); profIncCounter("fingerprints.read_nbytes", read_bytes_count); profIncCounter("fingerprints.read_nbytes_old", used_qwords * 8); bitAnd((byte*)screening.fp_final.ptr(), (byte*)fp_inter.ptr(), used_bytes); } } for (int i = 0; i < screening.block->used; i++) if (bitGetBit(screening.fp_final.ptr(), i)) screening.passed_pre.push(i); screening.query_bit_idx++; return true; } void BingoFingerprints::screenPart_End (OracleEnv &env, Screening &screening) { int i; screening.bits_lob.free(); screening.statement.free(); profTimerStart(tsort, "fingerprints.end"); for (i = 0; i < screening.passed_pre.size(); i++) screening.passed_pre[i] = getStorageIndex(screening, screening.passed_pre[i]); screening.passed_pre.qsort(_cmp_int, 0); for (i = 0; i < screening.passed_pre.size(); i++) screening.passed.add(screening.passed_pre[i]); screening.part++; } int BingoFingerprints::_cmp_int (int a, int b, void *context) { return a - b; } int BingoFingerprints::_cmp_counters (int a, int b, void *context) { const Screening &screening = *(const Screening *)context; const BingoFingerprints &self = *(const BingoFingerprints *)screening.data; if (screening.part == self._part_adding) return self._pending_block.counters[a] - self._pending_block.counters[b]; const Block &block = self._all_blocks[screening.part]; return block.counters[a] - block.counters[b]; } int BingoFingerprints::getStorageIndex (Screening &screening, int local_idx) { return screening.block->mapping[local_idx] + screening.part * (_chunk_qwords * 64); } int BingoFingerprints::getStorageIndex_NoMap (Screening &screening, int local_idx) { return local_idx + screening.part * (_chunk_qwords * 64); } bool BingoFingerprints::countOnes_Init (OracleEnv &env, Screening &screening) { if (screening.query_ones.size() < 1) throw Error("no screening bits"); if (screening.part != _part_adding) { if (screening.part >= _all_blocks.size()) return false; screening.statement.create(env); screening.bits_lob.create(env); screening.statement->append("SELECT bits FROM %s WHERE part = :part", _table_name.ptr()); screening.statement->prepare(); screening.statement->bindIntByName(":part", &screening.part); screening.statement->defineBlobByPos(1, screening.bits_lob.ref()); screening.statement->execute(); screening.block = &_all_blocks[screening.part]; } else screening.block = &_pending_block; screening.data = this; screening.query_ones.qsort(_cmp_counters, &screening); screening.one_counters.clear_resize(screening.block->used); screening.one_counters.zerofill(); screening.query_bit_idx = 0; return true; } bool BingoFingerprints::countOnes_Next (OracleEnv &env, Screening &screening) { int used_qwords = (screening.block->used + 63) / 64; int used_bytes = (screening.block->used + 7) / 8; QS_DEF(Array, fp); fp.clear_resize(used_qwords); if (screening.query_bit_idx >= screening.query_ones.size()) return false; if (screening.part != _part_adding) screening.bits_lob->read(screening.query_ones[screening.query_bit_idx] * _chunk_qwords * 8, (char *)fp.ptr(), used_qwords * 8); else memcpy(fp.ptr(), _pending_bits.ptr() + screening.query_ones[screening.query_bit_idx] * _chunk_qwords, used_qwords * 8); for (int j = 0; j < 8 * used_bytes; j++) { if (bitGetBit(fp.ptr(), j)) screening.one_counters[screening.block->mapping[j]]++; } screening.query_bit_idx++; return true; } void BingoFingerprints::countOnes_End (OracleEnv &env, Screening &screening) { screening.bits_lob.free(); screening.statement.free(); screening.part++; } void BingoFingerprints::validateForUpdate (OracleEnv &env) { OracleStatement statement(env); OracleLOB counters_lob(env), mapping_lob(env), bit_starts_lob(env), bit_ends_lob(env), bits_lob(env); int used, part = -1; statement.append("SELECT used, counters, mapping, bit_starts, bit_ends, part, bits FROM %s ORDER BY part DESC", _table_name.ptr()); statement.prepare(); statement.defineIntByPos(1, &used); statement.defineBlobByPos(2, counters_lob); statement.defineBlobByPos(3, mapping_lob); statement.defineBlobByPos(4, bit_starts_lob); statement.defineBlobByPos(5, bit_ends_lob); statement.defineIntByPos(6, &part); statement.defineBlobByPos(7, bits_lob); bool ex = statement.executeAllowNoData(); if (ex && used < _chunk_qwords * 64) { if (_part_adding != part) { _part_adding = part; _initBlock(_pending_block, true); Block &block = _pending_block; block.used = used; counters_lob.read(0, (char *)block.counters.ptr(), block.counters.sizeInBytes()); bit_starts_lob.read(0, (char *)block.bit_starts.ptr(), block.bit_starts.sizeInBytes()); bit_ends_lob.read(0, (char *)block.bit_ends.ptr(), block.bit_ends.sizeInBytes()); block.mapping.clear_resize(used); mapping_lob.read(0, (char *)block.mapping.ptr(), block.mapping.sizeInBytes()); _pending_bits_2.clear_resize(_pending_bits.size()); _pending_bits_2.zerofill(); bits_lob.read(0, (char *)_pending_bits_2.ptr(), _pending_bits.size() * 8); int i; for (i = 0; i < _fp_bytes * 8; i++) { int offset = i * _chunk_qwords * 8; const byte *fp_row_src = (byte *)_pending_bits_2.ptr() + offset; byte *fp_row_dest = (byte *)_pending_bits.ptr() + offset; for (int j = 0; j < _pending_block.used; j++) { int mapped_bit = _pending_block.mapping[j]; if (bitGetBit(fp_row_src, j)) bitSetBit(fp_row_dest, mapped_bit, 1); } } for (i = 0; i < _pending_block.mapping.size(); i++) _pending_block.mapping[i] = i; } } else { if (ex) { if (_part_adding == part + 1) // are we already inserting into pending block? ; else _pending_block.used = 0; } if (_pending_block.used == 0) { _part_adding = part + 1; // will start from 0 if there are no rows in the table _initBlock(_pending_block, true); } } } void BingoFingerprints::validate (OracleEnv &env) { env.dbgPrintf("validating screening data... "); if (_all_blocks.size() > 0) { // TODO: reload data if table was updated env.dbgPrintf("already loaded\n"); return; } profTimerStart(tread, "fingerprints.validate"); OracleStatement statement(env); OracleLOB counters_lob(env), mapping_lob(env), bit_starts_lob(env), bit_ends_lob(env); int used; statement.append("SELECT used, counters, mapping, bit_starts, bit_ends FROM %s ORDER BY part", _table_name.ptr()); statement.prepare(); statement.defineIntByPos(1, &used); statement.defineBlobByPos(2, counters_lob); statement.defineBlobByPos(3, mapping_lob); statement.defineBlobByPos(4, bit_starts_lob); statement.defineBlobByPos(5, bit_ends_lob); if (!statement.executeAllowNoData()) { env.dbgPrintf("no data?\n"); return; } do { Block &block = _all_blocks.push(); _initBlock(block, false); block.used = used; counters_lob.read(0, (char *)block.counters.ptr(), block.counters.sizeInBytes()); bit_starts_lob.read(0, (char *)block.bit_starts.ptr(), block.bit_starts.sizeInBytes()); bit_ends_lob.read(0, (char *)block.bit_ends.ptr(), block.bit_ends.sizeInBytes()); block.mapping.clear_resize(used); mapping_lob.read(0, (char *)block.mapping.ptr(), block.mapping.sizeInBytes()); } while (statement.fetch()); env.dbgPrintf("\n"); } void BingoFingerprints::analyze (OracleEnv &env) { env.dbgPrintf("analyzing fingerprints table\n"); OracleStatement::executeSingle(env, "ANALYZE TABLE %s ESTIMATE STATISTICS", _table_name.ptr()); } int BingoFingerprints::countOracleBlocks (OracleEnv &env) { int res; if (_chunk_qwords < 1) return 0; if (!OracleStatement::executeSingleInt(res, env, "select sum(length(bits)) / %d from %s", _chunk_qwords * 8, _table_name.ptr())) return 0; return res; } float BingoFingerprints::queryOnesRatio (Screening &screening) { if (_fp_bytes == 0) throw Error("_fp_bytes = 0 -> division by zero"); return (float)screening.query_ones.size() / (_fp_bytes * 8); } int BingoFingerprints::getTotalCount (OracleEnv &env) { if (_total_count_cached >= 0) return _total_count_cached; _total_count_cached = 0; if (_all_blocks.size() > 0) { for (int i = 0; i < _all_blocks.size(); i++) _total_count_cached += _all_blocks[i].used; } else { OracleStatement::executeSingleInt(_total_count_cached, env, "SELECT sum(used) FROM %s", _table_name.ptr()); } return _total_count_cached; } int BingoFingerprints::_cmp_optimize_counters (int a, int b, void *context) { const BingoFingerprints &self = *(const BingoFingerprints *)context; bool a_priority = a >= self._fp_priority_bytes_min * 8 && a < self._fp_priority_bytes_max * 8; bool b_priority = b >= self._fp_priority_bytes_min * 8 && b < self._fp_priority_bytes_max * 8; if (a_priority && !b_priority) return -1; if (!a_priority && b_priority) return 1; return self._pending_block.counters[a] - self._pending_block.counters[b]; } void BingoFingerprints::_optimizePendingBlock () { profTimerStart(tall, "fingerprints.optimize"); // Find better ordering for molecules to reduce // number of bytes to read during screening // Find bit order for better screening QS_DEF(Array, counters_order); counters_order.clear_resize(_fp_bytes * 8); for (int i = 0; i < _fp_bytes * 8; i++) counters_order[i] = i; counters_order.qsort(_cmp_optimize_counters, this); // Find better mapping QS_DEF(Array, post_mapping); post_mapping.clear_resize(_pending_block.used); for (int i = 0; i < _pending_block.used; i++) post_mapping[i] = i; int last_zero_start = 0; for (int i = 0; i < _fp_bytes * 8; i++) { int bit = counters_order[i]; int offset = bit * _chunk_qwords * 8; const byte *fp_row = (byte *)_pending_bits.ptr() + offset; // Split molecules by specified bit int low_start = last_zero_start; int high_start = _pending_block.used - 1; while (true) { int low = low_start; while (low < _pending_block.used && bitGetBit(fp_row, post_mapping[low]) != 0) low++; int high = high_start; while (high > low && bitGetBit(fp_row, post_mapping[high]) != 1) high--; if (high <= low) { last_zero_start = low; break; } // swap int tmp = post_mapping[low]; post_mapping[low] = post_mapping[high]; post_mapping[high] = tmp; low_start = low + 1; high_start = high - 1; } } // Apply post_mapping to the existing mapping for (int i = 0; i < _pending_block.used; i++) _pending_block.mapping[i] = post_mapping[_pending_block.mapping[i]]; // Find bit start and end positions _pending_block.bit_starts.fffill(); _pending_block.bit_ends.zerofill(); for (int i = 0; i < _fp_bytes * 8; i++) { int offset = i * _chunk_qwords * 8; const byte *fp_row = (byte *)_pending_bits.ptr() + offset; for (int j = 0; j < _pending_block.used; j++) { int mapped_bit = post_mapping[j]; if (bitGetBit(fp_row, mapped_bit)) { word &start_index = _pending_block.bit_starts[i]; if (start_index > j) start_index = j; word &end_index = _pending_block.bit_ends[i]; if (end_index < j) end_index = j; } } } // Reorder bits according to the found mapping _pending_bits_2.clear_resize(_pending_bits.size()); _pending_bits_2.zerofill(); for (int i = 0; i < _fp_bytes * 8; i++) { int offset = i * _chunk_qwords * 8; const byte *fp_row_src = (byte *)_pending_bits.ptr() + offset; byte *fp_row_dest = (byte *)_pending_bits_2.ptr() + offset; for (int j = 0; j < _pending_block.used; j++) { //int mapped_bit = _pending_block.mapping[j]; int mapped_bit = post_mapping[j]; if (bitGetBit(fp_row_src, mapped_bit)) bitSetBit(fp_row_dest, j, 1); } } _pending_bits.swap(_pending_bits_2); } // // BingoFingerprints::Screening // CP_DEF(BingoFingerprints::Screening); BingoFingerprints::Screening::Screening () : CP_INIT, TL_CP_GET(query_ones), TL_CP_GET(passed), TL_CP_GET(one_counters), TL_CP_GET(passed_pre), TL_CP_GET(fp_final) { } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_fingerprints.h000066400000000000000000000102521271037650300246410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_fingerprints__ #define __bingo_fingerprints__ #include "base_cpp/array.h" #include "base_cpp/ptr_array.h" #include "base_cpp/obj_array.h" #include "base_cpp/tlscont.h" #include "core/bingo_context.h" using namespace indigo; namespace indigo { class OracleEnv; class SharedMemory; } class BingoFingerprints { public: struct Block { Block () : used(0) {} // Number of molecules in this block (max. 65536) // All blocks (except maybe the last one) are full int used; Array counters; // Mapping between original molecules order and sorted Array mapping; // Low and high bounds for data in transposed fingerprint row Array bit_starts, bit_ends; }; class Screening { public: Screening (); int part; int items_read; int items_passed; int start_offset, end_offset; CP_DECL; TL_CP_DECL(Array, query_ones); TL_CP_DECL(List, passed); TL_CP_DECL(Array, one_counters); TL_CP_DECL(Array, passed_pre); TL_CP_DECL(Array, fp_final); void *data; Obj statement; Obj bits_lob; Block *block; int query_bit_idx; }; explicit BingoFingerprints (int context_id); void create (OracleEnv &env); void drop (OracleEnv &env); void truncate (OracleEnv &env); void analyze (OracleEnv &env); void addFingerprint (OracleEnv &env, const byte *fp); void flush (OracleEnv &env); void validate (OracleEnv &env); void validateForUpdate (OracleEnv &env); // fp_bytes - size of the one fingerprint // fp_priority_bytes - size of the range of fingerprint bits for the bigenning // that are used more frequentry than other bits. // -1 - default value. void init (BingoContext &context, int fp_bytes, int fp_priority_bytes_min = -1, int fp_priority_bytes_max = -1); void screenInit (const byte *fp, Screening &screening); bool ableToScreen (Screening &screening); bool screenPart_Init (OracleEnv &env, Screening &screening); bool screenPart_Next (OracleEnv &env, Screening &screening); void screenPart_End (OracleEnv &env, Screening &screening); bool countOnes_Init (OracleEnv &env, Screening &screening); bool countOnes_Next (OracleEnv &env, Screening &screening); void countOnes_End (OracleEnv &env, Screening &screening); int getStorageIndex (Screening &screening, int local_idx); int getStorageIndex_NoMap (Screening &screening, int local_idx); float queryOnesRatio (Screening &screening); int countOracleBlocks (OracleEnv &env); int getTotalCount (OracleEnv &env); DECL_ERROR; protected: // constant configuration parameters int _fp_bytes, _fp_priority_bytes_min, _fp_priority_bytes_max; int _chunk_qwords; CP_DECL; TL_CP_DECL(Array, _table_name); // when adding fingerprints int _part_adding; Array _pending_bits, _pending_bits_2; Block _pending_block; int _total_count_cached; void _optimizePendingBlock (); void _initBlock (Block &block, bool is_pending_block); bool _flush_Update (OracleEnv &env, bool update_counter); void _flush_Insert (OracleEnv &env); void _flush_Insert_OLD (OracleEnv &env); static int _cmp_optimize_counters (int a, int b, void *context); // when screening ObjArray< Block > _all_blocks; static int _cmp_counters (int a, int b, void *context); static int _cmp_int (int a, int b, void *context); }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle.cpp000066400000000000000000000051621271037650300237330ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "base_cpp/output.h" OracleLogger logger; const char *log_filename = "bingo.log"; int bingoPopRowidsToArray (OracleEnv &env, List &matched, int maxrows, OCIArray *array) { OCIString *rid_string = 0; int count = 0; while (matched.size() > 0 && maxrows > 0) { const char *rid_text = matched.at(matched.begin()).ptr(); env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (OraText *)rid_text, (ub4)strlen(rid_text), &rid_string)); env.callOCI(OCICollAppend(env.envhp(), env.errhp(), rid_string, 0, array)); maxrows--; count++; matched.remove(matched.begin()); } return count; } int bingoGetExactRightPart (OracleEnv &env, OCINumber *p_strt, OCINumber *p_stop, int flags) { if ((flags & 64) == 0) throw BingoError("only exact match allowed"); if (p_strt == 0 || p_stop == 0) throw BingoError("only exact match allowed"); int strt = OracleUtil::numberToInt(env, p_strt); int stop = OracleUtil::numberToInt(env, p_stop); if (strt != stop) throw BingoError("only exact match allowed"); if (strt != 0 && strt != 1) throw BingoError("only =0 and =1 allowed"); return strt; } void bingoBuildQueryID (OracleEnv &env, const char *oper, const Array &query_buf, OCINumber *p_strt, OCINumber *p_stop, int flags, const char *params, Array &id) { ArrayOutput output(id); output.printf("%s ", oper); if (params == 0) output.printf(" "); else output.printf("%s ", params); if (p_strt == 0) output.printf(" "); else output.printf("%.2f ", OracleUtil::numberToFloat(env, p_strt)); if (p_stop == 0) output.printf(" "); else output.printf("%.2f ", OracleUtil::numberToFloat(env, p_stop)); output.printf("%d ", flags); output.writeArray(query_buf); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle.h000066400000000000000000000050171271037650300233770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_oracle__ #define __bingo_oracle__ #include // oci.h on Solaris has typedef-ed dword. // We define it in order to avoid conflict with base_c/defs.h #define dword unsigned int #include "base_cpp/list.h" #include "core/bingo_error.h" #include "oracle/ora_wrap.h" #define ORAEXT CEXPORT using namespace indigo; extern OracleLogger logger; extern const char *log_filename; #define ORABLOCK_BEGIN \ logger.initIfClosed(log_filename); \ try { try #define ORABLOCK_END \ catch (Exception &e) { throw OracleError(-1, "Error: %s", e.message());} \ } \ catch (OracleError &error) { error.raise(logger, ctx); } \ catch (...) { OracleError(-1, "unknown exception").raise(logger, ctx); } #define ORA_TRY_FETCH_BEGIN \ try #define ORA_TRY_FETCH_END \ catch (Exception &e) \ { \ const char *rid_text = ""; \ OraRowidText rid; \ if (fetch_engine.getLastRowid(rid)) \ rid_text = rid.ptr(); \ throw Exception("%s. Last rowid was %s", e.message(), rid_text); \ } int bingoPopRowidsToArray (OracleEnv &env, List &matched, int maxrows, OCIArray *array); int bingoGetExactRightPart (OracleEnv &env, OCINumber *p_strt, OCINumber *p_stop, int flags); void bingoBuildQueryID (OracleEnv &env, const char *oper, const Array &query_buf, OCINumber *p_strt, OCINumber *p_stop, int flags, const char *params, Array &id); #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_config.cpp000066400000000000000000000146361271037650300252660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/tlscont.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/bingo_oracle_context.h" // // Oracle wrappers for bingo config // ORAEXT void oraConfigResetAll (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); context.configResetAll(env); } ORABLOCK_END } ORAEXT void oraConfigSetInt (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, OCINumber *value, short value_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); if (value_indicator != OCI_IND_NOTNULL) throw BingoError("Null value is given"); ub4 value_int; env.callOCI(OCINumberToInt(env.errhp(), value, sizeof(value_int), OCI_NUMBER_UNSIGNED, &value_int)); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); context.configSetInt(env, key_name, value_int); } ORABLOCK_END } ORAEXT void oraConfigSetFloat (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, OCINumber *value, short value_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); if (value_indicator != OCI_IND_NOTNULL) throw BingoError("Null value is given"); double value_float; env.callOCI(OCINumberToReal(env.errhp(), value, sizeof(value_float), &value_float)); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); context.configSetFloat(env, key_name, (float)value_float); } ORABLOCK_END } ORAEXT void oraConfigSetString (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, char *value, short value_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); if (value_indicator != OCI_IND_NOTNULL) throw BingoError("Null value is given"); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); context.configSetString(env, key_name, value); } ORABLOCK_END } ORAEXT OCINumber * oraConfigGetInt (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, short *return_indicator) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); int value; BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); if (!context.configGetInt(env, key_name, value)) throw BingoError("Key wasn't found"); result = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (result == NULL) throw BingoError("can't allocate memory for number"); env.callOCI(OCINumberFromInt(env.errhp(), &value, sizeof(int), OCI_NUMBER_SIGNED, result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCINumber * oraConfigGetFloat (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, short *return_indicator) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); float value; BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); if (!context.configGetFloat(env, key_name, value)) throw BingoError("Key wasn't found"); result = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (result == NULL) throw BingoError("can't allocate memory for number"); double value_double = value; env.callOCI(OCINumberFromReal(env.errhp(), &value_double, sizeof(value_double), result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCIString * oraConfigGetString (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator, short *return_indicator) { OCIString *result = NULL; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); QS_DEF(Array, value); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); if (!context.configGetString(env, key_name, value)) throw BingoError("Key wasn't found"); env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)value.ptr(), value.size() - 1, &result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT void oraConfigReset (OCIExtProcContext *ctx, int context_id, char *key_name, short key_name_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); BingoOracleContext &context = BingoOracleContext::get(env, context_id, false, 0); context.configReset(env, key_name); } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_context.cpp000066400000000000000000000405761271037650300255070ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/molecule_tautomer.h" #include "oracle/bingo_oracle_context.h" #include "core/bingo_error.h" #include "core/bingo_context.h" #include "oracle/ora_wrap.h" #include "base_cpp/scanner.h" #include "oracle/ora_logger.h" #include "base_cpp/shmem.h" #include "base_cpp/auto_ptr.h" #include "molecule/elements.h" BingoOracleContext::BingoOracleContext (OracleEnv &env, int id_) : BingoContext(id_), storage(env, id_), _config_changed(false) { ArrayOutput output(_id); output.printf("BINGO_%d", id_); output.writeChar(0); _shmem = 0; } BingoOracleContext::~BingoOracleContext () { delete _shmem; } BingoOracleContext & BingoOracleContext::get (OracleEnv &env, int id, bool lock, bool *config_reloaded) { BingoOracleContext *already = (BingoOracleContext *)_get(id); if (config_reloaded != 0) *config_reloaded = false; if (already != 0) { if (lock) already->lock(env); if (already->_config_changed) { env.dbgPrintfTS("reloading config\n"); already->_loadConfigParameters(env); if (config_reloaded != 0) *config_reloaded = true; } return *already; } AutoPtr res(new BingoOracleContext(env, id)); if (lock) res->lock(env); res->_loadConfigParameters(env); if (config_reloaded != 0) *config_reloaded = true; OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); _instances.add(res.release()); return *(BingoOracleContext *)_instances.top(); } void BingoOracleContext::_loadConfigParameters (OracleEnv &env) { fingerprintLoadParameters(env); tautomerLoadRules(env); atomicMassLoad(env); int val; configGetIntDef(env, "TREAT_X_AS_PSEUDOATOM", val, 0); treat_x_as_pseudoatom = (val != 0); configGetIntDef(env, "IGNORE_CLOSING_BOND_DIRECTION_MISMATCH", val, 0); ignore_closing_bond_direction_mismatch = (val != 0); configGetIntDef(env, "IGNORE_STEREOCENTER_ERRORS", val, 0); ignore_stereocenter_errors = (val != 0); configGetIntDef(env, "IGNORE_CISTRANS_ERRORS", val, 0); ignore_cistrans_errors = (val != 0); configGetIntDef(env, "STEREOCHEMISTRY_BIDIRECTIONAL_MODE", val, 0); stereochemistry_bidirectional_mode = (val != 0); configGetIntDef(env, "STEREOCHEMISTRY_DETECT_HAWORTH_PROJECTION", val, 0); stereochemistry_detect_haworth_projection = (val != 0); configGetIntDef(env, "ALLOW_NON_UNIQUE_DEAROMATIZATION", val, 0); allow_non_unique_dearomatization = (val != 0); configGetIntDef(env, "ZERO_UNKNOWN_AROMATIC_HYDROGENS", val, 0); zero_unknown_aromatic_hydrogens = (val != 0); configGetIntDef(env, "REJECT_INVALID_STRUCTURES", val, 0); reject_invalid_structures = (val != 0); QS_DEF(Array, cmfdict); if (configGetBlob(env, "CMFDICT", cmfdict)) { BufferScanner scanner(cmfdict); cmf_dict.load(scanner); } QS_DEF(Array, riddict); if (configGetBlob(env, "RIDDICT", riddict)) { BufferScanner scanner(riddict); rid_dict.load(scanner); } configGetInt(env, "SUB_SCREENING_PASS_MARK", sub_screening_pass_mark); configGetInt(env, "SIM_SCREENING_PASS_MARK", sim_screening_pass_mark); configGetInt(env, "SUB_SCREENING_MAX_BITS", sub_screening_max_bits); QS_DEF(Array, log_table); if (configGetString(env, "LOG_TABLE", log_table)) warnings.setTableNameAndColumns(env, log_table.ptr()); else warnings.reset(); _config_changed = false; } void BingoOracleContext::saveCmfDict (OracleEnv &env) { env.dbgPrintfTS("saving cmf dictionary\n"); QS_DEF(Array, cmfdict); ArrayOutput output(cmfdict); cmf_dict.saveFull(output); cmf_dict.resetModified(); configSetBlob(env, "CMFDICT", cmfdict); } void BingoOracleContext::saveRidDict (OracleEnv &env) { env.dbgPrintfTS("saving rowid dictionary\n"); QS_DEF(Array, riddict); ArrayOutput output(riddict); rid_dict.saveFull(output); rid_dict.resetModified(); configSetBlob(env, "RIDDICT", riddict); } bool BingoOracleContext::configGetIntDef (OracleEnv &env, const char *name, int &value, int default_value) { if (!OracleStatement::executeSingleInt(value, env, "SELECT value FROM " "(SELECT value FROM config_int WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) { return false; value = default_value; } return true; } bool BingoOracleContext::configGetInt (OracleEnv &env, const char *name, int &value) { if (!OracleStatement::executeSingleInt(value, env, "SELECT value FROM " "(SELECT value FROM config_int WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) return false; return true; } void BingoOracleContext::configResetAll (OracleEnv &env) { OracleStatement::executeSingle(env, "DELETE FROM CONFIG_INT WHERE n = %d", id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_STR WHERE n = %d", id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_FLOAT WHERE n = %d", id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_BLOB WHERE n = %d", id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_CLOB WHERE n = %d", id); _config_changed = true; } void BingoOracleContext::configReset (OracleEnv &env, const char *name) { OracleStatement::executeSingle(env, "DELETE FROM CONFIG_INT WHERE name = upper('%s') AND n = %d", name, id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_STR WHERE name = upper('%s') AND n = %d", name, id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_FLOAT WHERE name = upper('%s') AND n = %d", name, id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_BLOB WHERE name = upper('%s') AND n = %d", name, id); OracleStatement::executeSingle(env, "DELETE FROM CONFIG_CLOB WHERE name = upper('%s') AND n = %d", name, id); _config_changed = true; } void BingoOracleContext::configSetInt (OracleEnv &env, const char *name, int value) { configReset(env, name); OracleStatement::executeSingle(env, "INSERT INTO CONFIG_INT VALUES(%d, upper('%s'), %d)", id, name, value); _config_changed = true; } bool BingoOracleContext::configGetFloat (OracleEnv &env, const char *name, float &value) { if (!OracleStatement::executeSingleFloat(value, env, "SELECT value FROM " "(SELECT value FROM CONFIG_FLOAT WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) return false; return true; } void BingoOracleContext::configSetFloat (OracleEnv &env, const char *name, float value) { configReset(env, name); OracleStatement::executeSingle(env, "INSERT INTO CONFIG_FLOAT VALUES(%d, upper('%s'), %f)", id, name, value); _config_changed = true; } bool BingoOracleContext::configGetString (OracleEnv &env, const char *name, Array &value) { if (!OracleStatement::executeSingleString(value, env, "SELECT value FROM " "(SELECT value FROM config_str WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) return false; return true; } void BingoOracleContext::configSetString (OracleEnv &env, const char *name, const char *value) { configReset(env, name); OracleStatement::executeSingle(env, "INSERT INTO CONFIG_STR VALUES(%d, upper('%s'), '%s')", id, name, value); _config_changed = true; } bool BingoOracleContext::configGetBlob (OracleEnv &env, const char *name, Array &value) { if (!OracleStatement::executeSingleBlob(value, env, "SELECT value FROM " "(SELECT value FROM CONFIG_BLOB WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) return false; return true; } void BingoOracleContext::configSetBlob (OracleEnv &env, const char *name, const Array &value) { configReset(env, name); OracleLOB lob(env); OracleStatement statement(env); lob.createTemporaryBLOB(); lob.write(0, value.ptr(), value.size()); statement.append("INSERT INTO config_blob VALUES (%d, upper('%s'), :value)", id, name); statement.prepare(); statement.bindBlobByName(":value", lob); statement.execute(); _config_changed = true; } bool BingoOracleContext::configGetClob (OracleEnv &env, const char *name, Array &value) { if (!OracleStatement::executeSingleClob(value, env, "SELECT value FROM " "(SELECT value FROM CONFIG_CLOB WHERE name = upper('%s') AND n in (0, %d) " "ORDER BY n DESC) WHERE rownum <= 1", name, id)) return false; return true; } void BingoOracleContext::configSetClob (OracleEnv &env, const char *name, const Array &value) { configReset(env, name); OracleLOB lob(env); OracleStatement statement(env); lob.createTemporaryCLOB(); lob.write(0, value.ptr(), value.size()); statement.append("INSERT INTO config_clob VALUES (%d, upper('%s'), :value)", id, name); statement.prepare(); statement.bindBlobByName(":value", lob); statement.execute(); _config_changed = true; } void BingoOracleContext::tautomerLoadRules (OracleEnv &env) { tautomer_rules.clear(); OracleStatement statement(env); int n; char param1[128], param2[128]; statement.append("SELECT id, beg, end FROM tautomer_rules ORDER BY id ASC"); statement.prepare(); statement.defineIntByPos(1, &n); statement.defineStringByPos(2, param1, sizeof(param1)); statement.defineStringByPos(3, param2, sizeof(param2)); if (statement.executeAllowNoData()) do { if (n < 1 || n >= 32) throw BingoError("tautomer rule index %d is out of range", n); AutoPtr rule(new TautomerRule()); bingoGetTauCondition(param1, rule->aromaticity1, rule->list1); bingoGetTauCondition(param2, rule->aromaticity2, rule->list2); tautomer_rules.expand(n); tautomer_rules[n - 1] = rule.release(); } while (statement.fetch()); } void BingoOracleContext::fingerprintLoadParameters (OracleEnv &env) { configGetInt(env, "FP_ORD_SIZE", fp_parameters.ord_qwords); configGetInt(env, "FP_TAU_SIZE", fp_parameters.tau_qwords); configGetInt(env, "FP_SIM_SIZE", fp_parameters.sim_qwords); configGetInt(env, "FP_ANY_SIZE", fp_parameters.any_qwords); fp_parameters.ext = true; fp_parameters_ready = true; configGetInt(env, "FP_STORAGE_CHUNK", fp_chunk_qwords); } void BingoOracleContext::longOpInit (OracleEnv &env, int total, const char *operation, const char *target, const char *units) { _longop_slno = 0; _longop_total = total; _longop_operation.readString(operation, true); _longop_target.readString(target, true); _longop_units.readString(units, true); OracleStatement statement(env); statement.append("BEGIN :longop_rindex := DBMS_APPLICATION_INFO.set_session_longops_nohint; END;"); statement.prepare(); statement.bindIntByName(":longop_rindex", &_longop_rindex); statement.execute(); } void BingoOracleContext::longOpUpdate (OracleEnv &env, int sofar) { OracleStatement statement(env); statement.append("BEGIN dbms_application_info.set_session_longops(" "rindex => :longop_rindex, " "slno => :longop_slno, " "op_name => '%s', sofar => %d, totalwork => %d, target_desc => '%s', " "units => '%s'); END;", _longop_operation.ptr(), sofar, _longop_total, _longop_target.ptr(), _longop_units.ptr()); statement.prepare(); statement.bindIntByName(":longop_rindex", &_longop_rindex); statement.bindIntByName(":longop_slno", &_longop_slno); statement.execute(); } void BingoOracleContext::parseParameters (OracleEnv &env, const char *str) { BufferScanner scanner(str); QS_DEF(Array, param_name); QS_DEF(Array, param_value); static const char *PARAMETERS_INT[] = { "FP_ORD_SIZE", "FP_TAU_SIZE", "FP_ORD_BPC", "FP_TAU_BPC", "FP_MAX_CYCLE_LEN", "FP_MIN_TREE_EDGES", "FP_MAX_TREE_EDGES", "FP_STORAGE_CHUNK", "TREAT_X_AS_PSEUDOATOM", "IGNORE_CLOSING_BOND_DIRECTION_MISMATCH", "IGNORE_STEREOCENTER_ERRORS", "IGNORE_CISTRANS_ERRORS", "ALLOW_NON_UNIQUE_DEAROMATIZATION", "ZERO_UNKNOWN_AROMATIC_HYDROGENS", "REJECT_INVALID_STRUCTURES", }; bool config_changed = false; scanner.skipSpace(); while (!scanner.isEOF()) { scanner.readWord(param_name, " ="); scanner.skipSpace(); if (scanner.readChar() != '=') throw Error("can't parse parameters: '%s'", str); scanner.skipSpace(); bool parameter_found = false; for (int i = 0; i < NELEM(PARAMETERS_INT); i++) if (strcasecmp(param_name.ptr(), PARAMETERS_INT[i]) == 0) { int value = scanner.readInt(); configSetInt(env, PARAMETERS_INT[i], value); parameter_found = true; config_changed = true; break; } if (strcasecmp(param_name.ptr(), "NTHREADS") == 0) { nthreads = scanner.readInt(); parameter_found = true; } if (strcasecmp(param_name.ptr(), "LOG_TABLE") == 0) { scanner.readWord(param_value, " "); setLogTableWithColumns(env, param_value.ptr()); parameter_found = true; } if (!parameter_found) throw Error("unknown parameter %s", param_name.ptr()); scanner.skipSpace(); } if (config_changed) _loadConfigParameters(env); } void BingoOracleContext::atomicMassLoad (OracleEnv &env) { relative_atomic_mass_map.clear(); if (!configGetString(env, "RELATIVE_ATOMIC_MASS", _relative_atomic_mass)) return; const char *buffer = _relative_atomic_mass.ptr(); QS_DEF(Array, element_str); element_str.resize(_relative_atomic_mass.size()); float mass; int pos; while (sscanf(buffer, "%s%f%n", element_str.ptr(), &mass, &pos) > 1) { int elem = Element::fromString(element_str.ptr()); if (relative_atomic_mass_map.find(elem)) throw Error("element '%s' duplication in atomic mass list", element_str.ptr()); relative_atomic_mass_map.insert(elem, mass); buffer += pos; while (*buffer == ' ') buffer++; if (buffer[0] == ';') buffer++; } // Print debug information if (relative_atomic_mass_map.size() != 0) { env.dbgPrintfTS("Relative atomic mass read: "); for (int i = relative_atomic_mass_map.begin(); i != relative_atomic_mass_map.end(); i = relative_atomic_mass_map.next(i)) { int elem = relative_atomic_mass_map.key(i); float mass = relative_atomic_mass_map.value(i); env.dbgPrintf("%s %g; ", Element::toString(elem), mass); } env.dbgPrintf("\n"); } } void BingoOracleContext::atomicMassSave (OracleEnv &env) { if (_relative_atomic_mass.size() > 1) configSetString(env, "RELATIVE_ATOMIC_MASS", _relative_atomic_mass.ptr()); else configSetString(env, "RELATIVE_ATOMIC_MASS", ""); } void BingoOracleContext::setLogTableWithColumns (OracleEnv &env, const char *tableWithColumns) { configSetString(env, "LOG_TABLE", tableWithColumns); } void BingoOracleContext::lock (OracleEnv &env) { // TODO: implement a semaphore? env.dbgPrintf(" locking %s... ", _id.ptr()); if (_shmem != 0) { env.dbgPrintf("already locked\n"); return; } while (1) { _shmem = new SharedMemory(_id.ptr(), 1, false); if (_shmem->wasFirst()) break; delete _shmem; } env.dbgPrintf("locked\n"); } void BingoOracleContext::unlock (OracleEnv &env) { if (_shmem == 0) { env.dbgPrintf("%s is not locked by this process\n", _id.ptr()); return; } env.dbgPrintf("unlocking %s\n", _id.ptr()); delete _shmem; _shmem = 0; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_context.h000066400000000000000000000064241271037650300251460ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_oracle_context__ #define __bingo_oracle_context__ #include "base_cpp/exception.h" #include "core/bingo_context.h" #include "oracle/bingo_storage.h" #include "oracle/warnings_table.h" using namespace indigo; namespace ingido { class BingoContext; class OracleEnv; class SharedMemory; } class BingoOracleContext : public BingoContext { public: explicit BingoOracleContext (OracleEnv &env, int id); virtual ~BingoOracleContext (); BingoStorage storage; WarningsTable warnings; int sim_screening_pass_mark; int sub_screening_pass_mark; int sub_screening_max_bits; static BingoOracleContext & get (OracleEnv &env, int id, bool lock, bool *config_reloaded); bool configGetInt (OracleEnv &env, const char *name, int &value); void configSetInt (OracleEnv &env, const char *name, int value); bool configGetIntDef (OracleEnv &env, const char *name, int &value, int default_value); bool configGetFloat (OracleEnv &env, const char *name, float &value); void configSetFloat (OracleEnv &env, const char *name, float value); bool configGetString (OracleEnv &env, const char *name, Array &value); void configSetString (OracleEnv &env, const char *name, const char *value); bool configGetBlob (OracleEnv &env, const char *name, Array &value); void configSetBlob (OracleEnv &env, const char *name, const Array &value); bool configGetClob (OracleEnv &env, const char *name, Array &value); void configSetClob (OracleEnv &env, const char *name, const Array &value); void configResetAll (OracleEnv &env); void configReset (OracleEnv &env, const char *name); void tautomerLoadRules (OracleEnv &env); void fingerprintLoadParameters (OracleEnv &env); void saveCmfDict (OracleEnv &env); void saveRidDict (OracleEnv &env); void longOpInit (OracleEnv &env, int total, const char *operation, const char *target, const char *units); void longOpUpdate (OracleEnv &env, int sofar); void parseParameters (OracleEnv &env, const char *str); void atomicMassLoad (OracleEnv &env); void atomicMassSave (OracleEnv &env); void setLogTableWithColumns (OracleEnv &env, const char *tableWithColumns); void lock (OracleEnv &env); void unlock (OracleEnv &env); protected: bool _config_changed; int _longop_slno; int _longop_rindex; Array _longop_operation; Array _longop_units; Array _longop_target; // actually, it is the source table int _longop_total; Array _id; SharedMemory *_shmem; void _loadConfigParameters (OracleEnv &env); }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_parallel.cpp000066400000000000000000000043151271037650300256060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle_parallel.h" #include "base_cpp/tlscont.h" #include "oracle/ora_wrap.h" // // BingoOracleCommand // void BingoOracleCommand::clear () { blob_storage.clear(); OsCommand::clear(); } // // BingoOracleDispatcher // BingoOracleDispatcher::BingoOracleDispatcher (int method, bool set_parent_SID_for_threads, int blobs_per_command) : OsCommandDispatcher(method, set_parent_SID_for_threads) { _blobs_per_command = blobs_per_command; } void BingoOracleDispatcher::setup (OracleStatement *statement, OracleLOB *lob, char *varchar2_text, bool read_from_LOB) { _statement = statement; _lob = lob; _varchar2_text = varchar2_text; _read_from_LOB = read_from_LOB; _finished = false; } bool BingoOracleDispatcher::_setupCommand (OsCommand &command) { if (_finished) return false; BingoOracleCommand &cmd = (BingoOracleCommand &)command; cmd.blob_storage.clear(); int count = 0; bool is_fetched; do { _addCurrentRecordToCommand(cmd); count++; } while ((is_fetched = _statement->fetch()) && count < _blobs_per_command); if (!is_fetched) _finished = true; return true; } void BingoOracleDispatcher::_addCurrentRecordToCommand (BingoOracleCommand &cmd) { int size; if (_read_from_LOB) size = _lob->getLength(); else size = strlen(_varchar2_text); byte *buffer = cmd.blob_storage.add(size); if (_read_from_LOB) _lob->read(0, (char*)buffer, size); else memcpy(buffer, _varchar2_text, size); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_parallel.h000066400000000000000000000045511271037650300252550ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_parallel_h__ #define __bingo_parallel_h__ #include "base_cpp/os_thread_wrapper.h" #include "base_cpp/chunk_storage.h" // Helper class for parallelizing algorithms with // fetching packs of BLOBs or CLOBS from the database using namespace indigo; namespace indigo { class OracleStatement; class OracleLOB; class OracleEnv; } // Base class for command. // This command contains pack of the blobs (molecules in the raw format) // Subclasses should have additional data per each blob record from // blob_storage. class BingoOracleCommand : public OsCommand { public: virtual void execute (OsCommandResult &result) = 0; virtual void clear (); ChunkStorage blob_storage; }; // Dispatcher for creating commands. // Subclasses should overload _handleResult for result // handling (if necessary) // Each thread has the same Session ID as parent thread. class BingoOracleDispatcher : public OsCommandDispatcher { public: // Parameters: // method - HANDLING_ORDER_*** // set_parent_SID_for_threads - true if set SID for all // child threads to the parent' SID BingoOracleDispatcher (int method, bool set_parent_SID_for_threads, int blobs_per_command); void setup (OracleStatement *statement, OracleLOB *lob, char *varchar2_text, bool read_from_LOB); protected: virtual bool _setupCommand (OsCommand &command); // This function is call _blobs_per_command times to // initialize additional data for each molecule virtual void _addCurrentRecordToCommand (BingoOracleCommand &cmd); protected: OracleStatement *_statement; OracleLOB *_lob; char *_varchar2_text; bool _read_from_LOB; bool _finished; int _blobs_per_command; }; #endif // __bingo_parallel_h__ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_oracle_util.cpp000066400000000000000000000515221271037650300247710ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/bingo_profiling.h" #include "oracle/bingo_oracle_context.h" #include "molecule/sdf_loader.h" #include "molecule/rdf_loader.h" #include "gzip/gzip_output.h" #include "gzip/gzip_scanner.h" #include "base_cpp/string_pool.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/profiling.h" ORAEXT void oraLogPrint (OCIExtProcContext *ctx, char *str) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintf("%s\n", str); } ORABLOCK_END } ORAEXT OCILobLocator *oraLoadFileToCLOB (OCIExtProcContext *ctx, char *filename, short filename_indicator, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (filename_indicator == OCI_IND_NULL) throw BingoError("Null filename given"); FileScanner scanner(filename); QS_DEF(Array, buf); scanner.readAll(buf); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, buf); *return_indicator = OCI_IND_NOTNULL; lob.doNotDelete(); result = lob.get(); } ORABLOCK_END return result; } ORAEXT OCILobLocator *oraLoadFileToBLOB (OCIExtProcContext *ctx, char *filename, short filename_indicator, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (filename_indicator != OCI_IND_NOTNULL) throw BingoError("Null filename given"); FileScanner scanner(filename); QS_DEF(Array, buf); scanner.readAll(buf); OracleLOB lob(env); lob.createTemporaryBLOB(); lob.write(0, buf.ptr(), buf.size()); *return_indicator = OCI_IND_NOTNULL; lob.doNotDelete(); result = lob.get(); } ORABLOCK_END return result; } ORAEXT OCIString *oraLoadFileToString (OCIExtProcContext *ctx, char *filename, short filename_indicator, short *return_indicator) { OCIString *result = 0; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_indicator = OCI_IND_NULL; if (filename_indicator != OCI_IND_NOTNULL) throw BingoError("Null filename given"); FileScanner scanner(filename); QS_DEF(Array, buf); scanner.readAll(buf); env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)buf.ptr(), buf.size(), &result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT void oraSaveLOBToFile (OCIExtProcContext *ctx, OCILobLocator *lob_locator, short lob_indicator, char *file_name, short filename_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (lob_indicator == OCI_IND_NULL) throw BingoError("Null LOB given"); if (filename_indicator == OCI_IND_NULL) throw BingoError("Null file name given"); OracleLOB lob(env, lob_locator); QS_DEF(Array, buf); lob.readAll(buf, false); FileOutput output(file_name); output.writeArray(buf); } ORABLOCK_END } void _exportSDF (OracleEnv &env, const char *table, const char *clob_col, const char *other_cols, Output &output) { QS_DEF(StringPool, col_names); QS_DEF(Array, col_values); QS_DEF(Array, word); QS_DEF(Array, lob_value); BufferScanner scanner(other_cols); int i; int max_size = 1024; col_names.clear(); col_values.clear(); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; word.clear(); scanner.readWord(word, 0); if (word.size() < 2) break; col_names.add(word.ptr()); } col_values.resize(col_names.end() * max_size); OracleStatement statement(env); OracleLOB lob(env); lob.createTemporaryCLOB(); statement.append("SELECT %s", clob_col); for (i = col_names.begin(); i != col_names.end(); i = col_names.next(i)) statement.append(", to_char(%s)", col_names.at(i)); statement.append(" FROM %s", table); statement.prepare(); statement.defineClobByPos(1, lob); for (i = col_names.begin(); i != col_names.end(); i = col_names.next(i)) statement.defineStringByPos(i + 2, col_values.ptr() + i * max_size, max_size); if (statement.executeAllowNoData()) do { lob.readAll(lob_value, false); if (lob_value.size() < 4) continue; // hack to handle molfiles which have newline (and those which have not) if (lob_value[lob_value.size() - 1] == '\n') lob_value.pop(); // hack to handle molfiles which have $$$$ if (strncmp(lob_value.ptr() + lob_value.size() - 4, "$$$$", 4) == 0) lob_value.resize(lob_value.size() - 4); output.writeArray(lob_value); for (i = col_names.begin(); i != col_names.end(); i = col_names.next(i)) output.printf("\n> <%s>\n%s\n", col_names.at(i), col_values.ptr() + i * max_size); output.printf("\n$$$$\n"); } while (statement.fetch()); } void _parseFieldList (const char *str, StringPool &props, StringPool &columns) { QS_DEF(Array, prop); QS_DEF(Array, column); BufferScanner scanner(str); props.clear(); columns.clear(); scanner.skipSpace(); while (!scanner.isEOF()) { scanner.readWord(prop, " ,"); scanner.skipSpace(); scanner.readWord(column, " ,"); scanner.skipSpace(); props.add(prop.ptr()); columns.add(column.ptr()); if (scanner.isEOF()) break; if (scanner.readChar() != ',') throw BingoError("_parseFieldList(): comma expected"); scanner.skipSpace(); } } void _importSDF (OracleEnv &env, const char *table, const char *clob_col, const char *other_cols, const char *file_name) { FileScanner scanner(file_name); int i, nwritten = 0; QS_DEF(Array, word); QS_DEF(StringPool, props); QS_DEF(StringPool, columns); env.dbgPrintfTS("importing into table %s\n", table); SdfLoader loader(scanner); _parseFieldList(other_cols, props, columns); while (!loader.isEOF()) { profTimerStart(tread, "import.read_next"); loader.readNext(); profTimerStop(tread); OracleStatement statement(env); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, loader.data); statement.append("INSERT INTO %s(%s", table, clob_col); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) statement.append(", %s", columns.at(i)); statement.append(") VALUES(:clobdata"); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) { if (loader.properties.at2(props.at(i)) == 0) statement.append(", NULL"); else statement.append(",:%s", columns.at(i)); } statement.append(")"); statement.prepare(); statement.bindClobByName(":clobdata", lob); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) { if (loader.properties.at2(props.at(i)) == 0) continue; ArrayOutput out(word); out.printf(":%s", columns.at(i)); out.writeChar(0); Array &val = loader.properties.at(props.at(i)); statement.bindStringByName(word.ptr(), val.ptr(), val.size()); } profTimerStart(tinsert, "import.sql_insert"); statement.execute(); profTimerStop(tinsert); nwritten++; if (nwritten % 1000 == 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } if (nwritten % 1000 != 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } void _importSMILES (OracleEnv &env, const char *table, const char *smiles_col, const char *id_col, const char *file_name) { FileScanner fscanner(file_name); AutoPtr gzscanner; Scanner *scanner; int nwritten = 0; QS_DEF(Array, id); QS_DEF(Array, str); env.dbgPrintfTS("importing into table %s\n", table); // detect if input is gzipped byte magic[2]; int pos = fscanner.tell(); fscanner.readCharsFix(2, (char *)magic); fscanner.seek(pos, SEEK_SET); if (magic[0] == 0x1f && magic[1] == 0x8b) { gzscanner.reset(new GZipScanner(fscanner)); scanner = gzscanner.get(); } else scanner = &fscanner; while (!scanner->isEOF()) { id.clear(); scanner->readLine(str, false); BufferScanner strscan(str); strscan.skipSpace(); while (!strscan.isEOF() && !isspace(strscan.readChar())) ; strscan.skipSpace(); if (strscan.lookNext() == '|') { strscan.readChar(); while (!strscan.isEOF() && strscan.readChar() != '|') ; strscan.skipSpace(); } if (!strscan.isEOF() && id_col != 0) strscan.readLine(id, true); OracleStatement statement(env); statement.append("INSERT INTO %s(%s", table, smiles_col); if (id_col != 0) statement.append(", %s", id_col); statement.append(") VALUES(:smiles"); if (id_col != 0) { if (id.size() > 1) statement.append(", :id"); else statement.append(", NULL"); } statement.append(")"); statement.prepare(); str.push(0); statement.bindStringByName(":smiles", str.ptr(), str.size()); if (id.size() > 1) statement.bindStringByName(":id", id.ptr(), id.size()); statement.execute(); nwritten++; if (nwritten % 1000 == 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } if (nwritten % 1000 != 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } void _importRDF (OracleEnv &env, const char *table, const char *clob_col, const char *other_cols, const char *file_name) { FileScanner scanner(file_name); int i, nwritten = 0; QS_DEF(Array, word); QS_DEF(StringPool, props); QS_DEF(StringPool, columns); env.dbgPrintfTS("importing into table %s\n", table); _parseFieldList(other_cols, props, columns); RdfLoader loader(scanner); while (!loader.isEOF()) { loader.readNext(); OracleStatement statement(env); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, loader.data); statement.append("INSERT INTO %s(%s", table, clob_col); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) statement.append(", %s", columns.at(i)); statement.append(") VALUES(:clobdata"); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) { if (loader.properties.at2(props.at(i)) == 0) statement.append(", NULL"); else statement.append(",:%s", columns.at(i)); } statement.append(")"); statement.prepare(); statement.bindClobByName(":clobdata", lob); for (i = columns.begin(); i != columns.end(); i = columns.next(i)) { if (loader.properties.at2(props.at(i)) == 0) continue; ArrayOutput out(word); out.printf(":%s", columns.at(i)); out.writeChar(0); Array &val = loader.properties.at(props.at(i)); statement.bindStringByName(word.ptr(), val.ptr(), val.size()); } statement.execute(); nwritten++; if (nwritten % 1000 == 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } if (nwritten % 1000 != 0) { env.dbgPrintfTS("imported %d items, commiting\n", nwritten); OracleStatement::executeSingle(env, "COMMIT"); } } ORAEXT void oraExportSDF (OCIExtProcContext *ctx, const char *table_name, short table_name_ind, const char *clob_col_name, short clob_col_ind, const char *other_col_names, short other_col_ind, const char *file_name, short file_name_ind) { logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null CLOB column name"); if (file_name_ind != OCI_IND_NOTNULL) throw BingoError("null file name"); if (other_col_ind != OCI_IND_NOTNULL) other_col_names = 0; FileOutput output(file_name); _exportSDF(env, table_name, clob_col_name, other_col_names, output); } ORABLOCK_END } ORAEXT void oraExportSDFZip (OCIExtProcContext *ctx, const char *table_name, short table_name_ind, const char *clob_col_name, short clob_col_ind, const char *other_col_names, short other_col_ind, const char *file_name, short file_name_ind) { logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null CLOB column name"); if (file_name_ind != OCI_IND_NOTNULL) throw BingoError("null file name"); if (other_col_ind != OCI_IND_NOTNULL) other_col_names = 0; FileOutput output(file_name); GZipOutput gzoutput(output, 9); _exportSDF(env, table_name, clob_col_name, other_col_names, gzoutput); } ORABLOCK_END } ORAEXT void oraImportSDF (OCIExtProcContext *ctx, const char *table_name, short table_name_ind, const char *clob_col_name, short clob_col_ind, const char *other_col_names, short other_col_ind, const char *file_name, short file_name_ind) { logger.initIfClosed(log_filename); ORABLOCK_BEGIN { profTimersReset(); profTimerStart(ttotal, "total"); OracleEnv env(ctx, logger); if (table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null file name"); if (other_col_ind != OCI_IND_NOTNULL) other_col_names = 0; _importSDF(env, table_name, clob_col_name, other_col_names, file_name); profTimerStop(ttotal); bingoProfilingPrintStatistics(false); } ORABLOCK_END } ORAEXT void oraImportRDF (OCIExtProcContext *ctx, const char *table_name, short table_name_ind, const char *clob_col_name, short clob_col_ind, const char *other_col_names, short other_col_ind, const char *file_name, short file_name_ind) { logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (clob_col_ind != OCI_IND_NOTNULL) throw BingoError("null file name"); if (other_col_ind != OCI_IND_NOTNULL) other_col_names = 0; _importRDF(env, table_name, clob_col_name, other_col_names, file_name); } ORABLOCK_END } ORAEXT void oraImportSMILES (OCIExtProcContext *ctx, const char *table_name, short table_name_ind, const char *smiles_col_name, short smiles_col_ind, const char *id_col_name, short id_col_ind, const char *file_name, short file_name_ind) { logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name"); if (smiles_col_ind != OCI_IND_NOTNULL) throw BingoError("null SMILES column name"); if (file_name_ind != OCI_IND_NOTNULL) throw BingoError("null file name"); if (id_col_ind != OCI_IND_NOTNULL) id_col_name = 0; _importSMILES(env, table_name, smiles_col_name, id_col_name, file_name); } ORABLOCK_END } ORAEXT OCIString *oraGetVersion (OCIExtProcContext *ctx, short *return_indicator) { OCIString *result = 0; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); // env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (const oratext *)bingo_version_string, // strlen(bingo_version_string), &result)); env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (const oratext *)BINGO_VERSION, strlen(BINGO_VERSION), &result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT void oraBingoZip (OCIExtProcContext *ctx, OCILobLocator *source_locator, short source_indicator, OCILobLocator *dest_locator, short dest_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (source_indicator == OCI_IND_NULL) throw BingoError("null source given"); if (dest_indicator == OCI_IND_NULL) throw BingoError("null destination given"); OracleLOB source_lob(env, source_locator); QS_DEF(Array, source); QS_DEF(Array, dest); source_lob.readAll(source, false); { ArrayOutput output(dest); GZipOutput gzoutput(output, 9); gzoutput.write(source.ptr(), source.size()); gzoutput.flush(); } OracleLOB result_lob(env, dest_locator); result_lob.write(0, dest); result_lob.trim(dest.size()); } ORABLOCK_END } ORAEXT void oraBingoUnzip (OCIExtProcContext *ctx, OCILobLocator *source_locator, short source_indicator, OCILobLocator *dest_locator, short dest_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (source_indicator == OCI_IND_NULL) throw BingoError("null source given"); if (dest_indicator == OCI_IND_NULL) throw BingoError("null destination given"); OracleLOB source_lob(env, source_locator); QS_DEF(Array, source); QS_DEF(Array, dest); source_lob.readAll(source, false); { BufferScanner scanner(source); GZipScanner gzscanner(scanner); gzscanner.readAll(dest); } OracleLOB result_lob(env, dest_locator); result_lob.write(0, dest); result_lob.trim(dest.size()); } ORABLOCK_END } ORAEXT OCIString *oraBingoGetName (OCIExtProcContext *ctx, OCILobLocator *source_locator, short source_indicator, short *return_indicator) { OCIString *result = NULL; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (source_indicator == OCI_IND_NULL) throw BingoError("null source given"); QS_DEF(Array, source); QS_DEF(Array, name); OracleLOB source_lob(env, source_locator); source_lob.readAll(source, false); BufferScanner scanner(source); bingoGetName(scanner, name); if (name.size() < 1) { // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); *return_indicator = OCI_IND_NULL; } else { name.push(0); OCIStringAssignText(env.envhp(), env.errhp(), (text *)name.ptr(), strlen(name.ptr()), &result); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_profiling.cpp000066400000000000000000000073021271037650300244550ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_profiling.h" #include "oracle/bingo_oracle.h" #include "oracle/ora_logger.h" #include "base_cpp/profiling.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" void bingoProfilingPrintStatistics (bool print_all) { // Print profiling statistics QS_DEF(Array, buffer); ArrayOutput output(buffer); profGetStatistics(output, print_all); buffer.push(0); logger.dbgPrintf("Profiling statistics:\n"); logger.dbgPrintf(buffer.ptr()); logger.dbgPrintf("\n"); } ORAEXT OCINumber * oraProfilingGetCount (OCIExtProcContext *ctx, char *key_name, short key_name_indicator, short *return_indicator) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); qword value; // Try to find in profiling data ProfilingSystem &inst = ProfilingSystem::getInstance(); if (inst.hasLabel(key_name)) //throw BingoError("Key wasn't found"); value = inst.getLabelCallCount(key_name); else value = 0; result = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (result == NULL) throw BingoError("can't allocate memory for number"); env.callOCI(OCINumberFromInt(env.errhp(), &value, sizeof(qword), OCI_NUMBER_SIGNED, result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCINumber * oraProfilingGetTime (OCIExtProcContext *ctx, char *key_name, short key_name_indicator, short *return_indicator) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (key_name_indicator != OCI_IND_NOTNULL) throw BingoError("Null key is given"); float value; // Try to find in profiling data ProfilingSystem &inst = ProfilingSystem::getInstance(); if (inst.hasLabel(key_name)) //throw BingoError("Key wasn't found"); value = inst.getLabelExecTime(key_name); else value = 0; result = (OCINumber *)OCIExtProcAllocCallMemory(ctx, sizeof(OCINumber)); if (result == NULL) throw BingoError("can't allocate memory for number"); double value_double = value; env.callOCI(OCINumberFromReal(env.errhp(), &value_double, sizeof(value_double), result)); *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT void oraProfilingPrint (OCIExtProcContext *ctx, OCINumber *print_all, short print_all_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (print_all_indicator != OCI_IND_NOTNULL) throw BingoError("Null value is given"); ub4 print_all_int; env.callOCI(OCINumberToInt(env.errhp(), print_all, sizeof(print_all_int), OCI_NUMBER_UNSIGNED, &print_all_int)); bingoProfilingPrintStatistics(print_all_int != 0); } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_profiling.h000066400000000000000000000017121271037650300241210ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_profiling_h__ #define __bingo_profiling_h__ // Print current profiling information to the debug output // Parameters: // get_all - true if print all counters, // false - print only counters with nonzero values void bingoProfilingPrintStatistics (bool print_all); #endif // __bingo_profiling_h__ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_storage.cpp000066400000000000000000000245661271037650300241430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_storage.h" #include "base_cpp/tlscont.h" #include "oracle/ora_wrap.h" #include "base_cpp/output.h" #include "base_cpp/shmem.h" #include "base_cpp/auto_ptr.h" #include "oracle/ora_logger.h" IMPL_ERROR(BingoStorage, "storage"); BingoStorage::BingoStorage (OracleEnv &env, int context_id) { _shmem_state = 0; _age_loaded = -1; _top_lob_pending_mark = 0; _index_lob_pending_mark = 0; QS_DEF(Array, instance); QS_DEF(Array, schema); OracleStatement::executeSingleString(instance, env, "SELECT PROPERTY_VALUE from database_properties where property_name = 'GLOBAL_DB_NAME'"); OracleStatement::executeSingleString(schema, env, "SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') from dual"); ArrayOutput output1(_shmem_id); output1.printf("%s#%s#%d#bs2", instance.ptr(), schema.ptr(), context_id); output1.writeChar(0); ArrayOutput output2(_table_name); output2.printf("STORAGE_%d", context_id); output2.writeChar(0); } BingoStorage::~BingoStorage () { delete _shmem_state; } void BingoStorage::create (OracleEnv &env) { const char *tn = _table_name.ptr(); OracleStatement::executeSingle(env, "CREATE TABLE %s(id number, bindata BLOB) " "NOLOGGING lob(bindata) store as (CACHE READS NOLOGGING PCTVERSION 0)", tn); OracleStatement::executeSingle(env, "CREATE INDEX %s_id ON %s(id)", tn, tn); OracleStatement::executeSingle(env, "INSERT INTO %s VALUES(0, EMPTY_BLOB())", tn); } void BingoStorage::drop (OracleEnv &env) { OracleStatement::executeSingle(env, "BEGIN DropTable('%s'); END;", _table_name.ptr()); delete _shmem_state; _shmem_state = 0; _age_loaded = -1; } void BingoStorage::truncate (OracleEnv &env) { const char *tn = _table_name.ptr(); OracleStatement::executeSingle(env, "TRUNCATE TABLE %s", tn); OracleStatement::executeSingle(env, "INSERT INTO %s VALUES(0, EMPTY_BLOB())", tn); delete _shmem_state; _shmem_state = 0; _age_loaded = -1; } void BingoStorage::validateForInsert (OracleEnv &env) { _blocks.clear(); OracleStatement statement(env); int id, length; OracleLOB lob(env); statement.append("SELECT id, length(bindata), bindata FROM %s ORDER BY id", _table_name.ptr()); statement.prepare(); statement.defineIntByPos(1, &id); statement.defineIntByPos(2, &length); statement.defineBlobByPos(3, lob); statement.execute(); _n_added = -1; do { if (id == 0) { if ((length % sizeof(_Addr)) != 0) throw Error("unexpected LOB size %d is not multiple of %d", length, sizeof(_Addr)); _n_added = length / sizeof(_Addr); continue; } _Block &block = _blocks.push(); block.size = length; } while (statement.fetch()); if (_n_added < 0) throw Error("missing index LOB"); if (_blocks.size() > 0) _top_lob_pending_mark = _blocks.top().size; _index_lob_pending_mark = _n_added * sizeof(_Addr); } void BingoStorage::validate (OracleEnv &env) { env.dbgPrintfTS("validating storage... "); if (_shmem_state != 0 && strcmp(_shmem_state->getID(), _shmem_id.ptr()) != 0) { delete _shmem_state; _shmem_state = 0; _age_loaded = -1; } _State *state = _getState(true); // TODO: implement a semaphore while (state->state == _STATE_LOADING) { delete _shmem_state; _shmem_state = 0; _age_loaded = -1; state = _getState(true); if (state == 0) throw Error("can't get shared info"); env.dbgPrintf("."); } if (state->state == _STATE_READY) { if (state->age_loaded == state->age) { if (_age_loaded == state->age) { env.dbgPrintf("up to date\n"); return; } else env.dbgPrintf("loaded by the other process\n"); } else { env.dbgPrintf("has changed, reloading\n"); state->state = _STATE_LOADING; } } else { state->state = _STATE_LOADING; env.dbgPrintf("loading ... \n"); } _shmem_array.clear(); _blocks.clear(); OracleStatement statement(env); int id, length; OracleLOB lob(env); QS_DEF(Array, block_name); statement.append("SELECT id, length(bindata), bindata FROM %s ORDER BY id", _table_name.ptr()); statement.prepare(); statement.defineIntByPos(1, &id); statement.defineIntByPos(2, &length); statement.defineBlobByPos(3, lob); statement.execute(); do { ArrayOutput output(block_name); output.printf("%s_%d_%d", _shmem_id.ptr(), id, state->age); output.writeByte(0); if (length < 1) { if (id == 0) { _index.clear(); break; } throw Error("cannot validate block #%d: length=%d", id, length); } _shmem_array.add(new SharedMemory(block_name.ptr(), length, state->state == _STATE_READY)); void *ptr = _shmem_array.top()->ptr(); if (ptr == 0) { if (state->state == _STATE_READY) { // That's rare case, but possible. // Reload the storage. env.dbgPrintf("shared memory is gone, resetting... \n"); state->state = _STATE_EMPTY; validate(env); return; } else throw Error("can't map block #%d", id); } if (state->state != _STATE_READY) lob.read(0, (char *)ptr, length); if (id == 0) { if ((length % sizeof(_Addr)) != 0) throw Error("LOB size %d (expected a multiple of %d)", length, sizeof(_Addr)); if (length > 0) _index.copy((_Addr *)_shmem_array[0]->ptr(), length / sizeof(_Addr)); } _Block &block = _blocks.push(); block.size = length; } while (statement.fetch()); state->state = _STATE_READY; state->age_loaded = state->age; _age_loaded = state->age; } OracleLOB * BingoStorage::_getLob (OracleEnv &env, int no) { OracleStatement statement(env); AutoPtr lob(new OracleLOB(env)); statement.append("SELECT bindata FROM %s where ID = :id FOR UPDATE", _table_name.ptr()); statement.prepare(); statement.bindIntByName(":id", &no); statement.defineBlobByPos(1, lob.ref()); statement.execute(); if (statement.fetch()) env.dbgPrintf("WARNING: more than 1 row have id = %d in table %s\n", no, _table_name.ptr()); lob->enableBuffering(); return lob.release(); } void BingoStorage::_finishTopLob (OracleEnv &env) { OracleLOB *top_lob = _getLob(env, _blocks.size()); env.dbgPrintf("flushing storage LOB\n"); top_lob->write(_top_lob_pending_mark, _top_lob_pending_data); _top_lob_pending_mark += _top_lob_pending_data.size(); _top_lob_pending_data.clear(); delete top_lob; top_lob = 0; } void BingoStorage::_finishIndexLob (OracleEnv &env) { env.dbgPrintf("flushing index LOB\n"); OracleLOB *index_lob = _getLob(env, 0); index_lob->write(_index_lob_pending_mark, _index_lob_pending_data); _index_lob_pending_mark += _index_lob_pending_data.size(); _index_lob_pending_data.clear(); delete index_lob; } void BingoStorage::_insertLOB (OracleEnv &env, int no) { _finishTopLob(env); OracleStatement statement(env); statement.append("INSERT INTO %s VALUES(%d, EMPTY_BLOB())", _table_name.ptr(), no); statement.prepare(); statement.execute(); if (no > 0) { _Block &block = _blocks.push(); block.size = 0; } _top_lob_pending_mark = 0; } void BingoStorage::add (OracleEnv &env, const Array &data, int &blockno, int &offset) { if (_blocks.size() < 1 || _blocks.top().size + data.size() > _MAX_BLOCK_SIZE) _insertLOB(env, _blocks.size() + 1); _Block &top = _blocks.top(); blockno = _blocks.size() - 1; offset = top.size; _top_lob_pending_data.concat(data); _Addr addr; addr.blockno = _blocks.size() - 1; addr.length = data.size(); addr.offset = top.size; top.size += data.size(); _index_lob_pending_data.concat((char *)&addr, sizeof(_Addr)); _n_added++; _State *state = _getState(true); if (state != 0) state->age++; } int BingoStorage::count () { return _index.size(); } void BingoStorage::get (int n, Array &out) { const _Addr &addr = _index[n]; const char *ptr = (const char *)_shmem_array[addr.blockno + 1]->ptr(); out.copy(ptr + addr.offset, addr.length); } BingoStorage::_State * BingoStorage::_getState (bool allow_first) { return (_State *)_getShared(_shmem_state, _shmem_id.ptr(), sizeof(_State), allow_first); } void * BingoStorage::_getShared (SharedMemory * &sh_mem, char *name, int shared_size, bool allow_first) { if (sh_mem != 0 && strcmp(sh_mem->getID(), name) != 0) { delete sh_mem; sh_mem = 0; } if (sh_mem == 0) sh_mem = new SharedMemory(name, shared_size, !allow_first); if (sh_mem->ptr() == 0) { delete sh_mem; sh_mem = 0; return 0; } return sh_mem->ptr(); } void BingoStorage::flush (OracleEnv &env) { finish(env); OracleStatement::executeSingle(env, "COMMIT"); } void BingoStorage::finish (OracleEnv &env) { _finishTopLob(env); _finishIndexLob(env); } void BingoStorage::lock (OracleEnv &env) { OracleStatement::executeSingle(env, "LOCK TABLE %s IN EXCLUSIVE MODE", _table_name.ptr()); } void BingoStorage::markRemoved (OracleEnv &env, int blockno, int offset) { OracleStatement statement(env); OracleLOB lob(env); statement.append("SELECT bindata FROM %s WHERE id = :id FOR UPDATE", _table_name.ptr()); statement.prepare(); statement.bindIntByName(":id", &blockno); statement.defineBlobByPos(1, lob); statement.execute(); byte mark = 1; lob.write(offset, (char *)&mark, 1); _State *state = _getState(true); if (state != 0) state->age++; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/bingo_storage.h000066400000000000000000000052161271037650300235770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bingo_storage__ #define __bingo_storage__ #include "base_cpp/array.h" #include "base_cpp/ptr_array.h" #include "base_cpp/shmem.h" using namespace indigo; namespace indigo { class OracleEnv; class OracleLOB; class SharedMemory; } class BingoStorage { public: explicit BingoStorage (OracleEnv &env, int context_id); virtual ~BingoStorage (); void validateForInsert (OracleEnv &env); void add (OracleEnv &env, const Array &data, int &blockno, int &offset); void create (OracleEnv &env); void drop (OracleEnv &env); void truncate (OracleEnv &env); void validate (OracleEnv &env); void flush (OracleEnv &env); void finish (OracleEnv &env); void lock (OracleEnv &env); void markRemoved (OracleEnv &env, int blockno, int offset); int count (); void get (int n, Array &out); DECL_ERROR; protected: enum { _STATE_EMPTY = 0, _STATE_BULDING = 1, _STATE_LOADING = 2, _STATE_READY = 3 }; enum { _MAX_BLOCK_SIZE = 5 * 1024 * 1024 }; struct _Block { int size; }; struct _State { int state; // see STATE_*** int age; int age_loaded; }; struct _Addr { short blockno; short length; int offset; }; SharedMemory *_shmem_state; Array<_Block> _blocks; int _n_added; int _age_loaded; PtrArray _shmem_array; void * _getShared (SharedMemory * &sh_mem, char *name, int shared_size, bool allow_first); _State * _getState (bool allow_first); void _insertLOB (OracleEnv &env, int no); OracleLOB * _getLob (OracleEnv &env, int no); void _finishTopLob (OracleEnv &env); void _finishIndexLob (OracleEnv &env); Array _shmem_id; Array _table_name; Array<_Addr> _index; Array _top_lob_pending_data; Array _index_lob_pending_data; int _top_lob_pending_mark; int _index_lob_pending_mark; }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/dll_main.cpp000066400000000000000000000017071271037650300230700ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifdef _WIN32 #include BOOL APIENTRY DllMain (HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } #endifIndigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_fast_index.cpp000066400000000000000000000311441271037650300246140ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "base_cpp/profiling.h" #include "oracle/mango_fast_index.h" #include "core/mango_matchers.h" #include "oracle/mango_oracle.h" #include "oracle/mango_shadow_table.h" #include "oracle/mango_fetch_context.h" #include "oracle/bingo_oracle_context.h" #include "base_cpp/scanner.h" #include "oracle/rowid_loader.h" #include "base_c/bitarray.h" IMPL_ERROR(MangoFastIndex, "mango fast fetch"); MangoFastIndex::MangoFastIndex (MangoFetchContext &context) : _context(context) { _fetch_type = 0; _last_id = -1; } MangoFastIndex::~MangoFastIndex () { } void MangoFastIndex::_decompressRowid (const Array &stored, OraRowidText &rid) { BufferScanner scanner(stored.ptr() + 2, stored[1]); RowIDLoader loader(_context.context().context().rid_dict, scanner); QS_DEF(Array, rowid); loader.loadRowID(rowid); if (rowid.size() != 18) throw Error("rowid size=%d?", rowid.size()); memcpy(rid.ptr(), rowid.ptr(), 18); rid.ptr()[18] = 0; } bool MangoFastIndex::getLastRowid (OraRowidText &id) { if (_last_id < 0) return false; BingoStorage &storage = this->_context.context().context().storage; QS_DEF(Array, stored); storage.get(_last_id, stored); _decompressRowid(stored, id); return true; } void MangoFastIndex::_match (OracleEnv &env, int idx) { _last_id = idx; BingoStorage &storage = this->_context.context().context().storage; QS_DEF(Array, stored); storage.get(idx, stored); if (stored[0] != 0) return; // molecule was removed from index BufferScanner scanner(stored); scanner.skip(1); // skip the deletion mark scanner.skip(scanner.readByte()); // skip the compessed rowid scanner.skip(2); // skip 'ord' bits count bool res = false; profTimerStart(tall, "match"); if (_fetch_type == _SUBSTRUCTURE) { QS_DEF(Array, xyz_buf); if (_context.substructure.needCoords()) { OraRowidText rid; _decompressRowid(stored, rid); if (_loadCoords(env, rid.ptr(), xyz_buf)) { BufferScanner xyz_scanner(xyz_buf); res = _context.substructure.matchBinary(scanner, &xyz_scanner); } else // no XYZ --> skip the molecule res = false; } else res = _context.substructure.matchBinary(scanner, 0); } else if (_fetch_type == _TAUTOMER_SUBSTRUCTURE) res = _context.tautomer.matchBinary(scanner); else // _fetch_type == _SIMILARITY res = _context.similarity.matchBinary(scanner); profTimerStop(tall); if (res) { OraRowidText & rid = matched.at(matched.add()); _decompressRowid(stored, rid); profIncTimer("match.found", profTimerGetTime(tall)); _matched++; } else { profIncTimer("match.not_found", profTimerGetTime(tall)); _unmatched++; } } void MangoFastIndex::fetch (OracleEnv &env, int max_matches) { env.dbgPrintf("requested %d hits\n", max_matches); matched.clear(); if (_fetch_type == _SUBSTRUCTURE || _fetch_type == _TAUTOMER_SUBSTRUCTURE) _fetchSubstructure(env, max_matches); else if (_fetch_type == _SIMILARITY) _fetchSimilarity(env, max_matches); else throw Error("unexpected fetch type: %d", _fetch_type); } void MangoFastIndex::_fetchSubstructure (OracleEnv &env, int max_matches) { BingoFingerprints &fingerprints = _context.context().fingerprints; if (fingerprints.ableToScreen(_screening)) { while (matched.size() < max_matches) { if (_screening.passed.size() > 0) { int idx = _screening.passed.begin(); _match(env, _screening.passed.at(idx)); _screening.passed.remove(idx); continue; } if (fingerprints.screenPart_Init(env, _screening)) { while (fingerprints.screenPart_Next(env, _screening)) { if (_screening.passed_pre.size() <= _context.context().context().sub_screening_pass_mark || _screening.query_bit_idx >= _context.context().context().sub_screening_max_bits) { env.dbgPrintfTS("stopping at bit #%d; ", _screening.query_bit_idx); break; } } fingerprints.screenPart_End(env, _screening); _unmatched += _screening.block->used - _screening.passed.size(); } else { env.dbgPrintfTS("screening ended\n"); break; } _screening.items_passed += _screening.passed.size(); env.dbgPrintf("%d molecules passed screening\n", _screening.passed.size()); } } else { while (matched.size() < max_matches && _cur_idx < _context.context().context().storage.count()) _match(env, _cur_idx++); env.dbgPrintfTS("%d molecules matched of tested %d\n", matched.size(), _cur_idx); } } void MangoFastIndex::_fetchSimilarity (OracleEnv &env, int max_matches) { BingoFingerprints &fingerprints = _context.context().fingerprints; int i; if (!fingerprints.ableToScreen(_screening)) { env.dbgPrintfTS("no bits in query fingerprint, can not do similarity search\n"); return; } profTimerStart(tsimfetch, "sim.fetch"); while (matched.size() < max_matches) { if (!fingerprints.countOnes_Init(env, _screening)) { env.dbgPrintfTS("screening ended\n"); break; } BingoStorage &storage = _context.context().context().storage; QS_DEF(Array, max_common_ones); QS_DEF(Array, min_common_ones); QS_DEF(Array, target_ones); QS_DEF(Array, stored); max_common_ones.clear_resize(_screening.block->used); min_common_ones.clear_resize(_screening.block->used); target_ones.clear_resize(_screening.block->used); for (i = 0; i < _screening.block->used; i++) { storage.get(fingerprints.getStorageIndex_NoMap(_screening, i), stored); BufferScanner scanner(stored); scanner.skip(1); // skip the deletion mark scanner.skip(scanner.readByte()); // skip the compessed rowid target_ones[i] = scanner.readBinaryWord(); max_common_ones[i] = _context.similarity.getUpperBound(target_ones[i]); min_common_ones[i] = _context.similarity.getLowerBound(target_ones[i]); } bool first = true; bool entire = false; _screening.passed.clear(); while (true) { if (!fingerprints.countOnes_Next(env, _screening)) { env.dbgPrintf("read all %d bits, writing %d results... ", _screening.query_ones.size(), _screening.passed.size()); entire = true; break; } if (first) { first = false; for (i = 0; i < _screening.block->used; i++) { int min_possible_ones = _screening.one_counters[i]; int max_possible_ones = _screening.one_counters[i] + _screening.query_ones.size() - _screening.query_bit_idx; if (min_possible_ones <= max_common_ones[i] && max_possible_ones >= min_common_ones[i]) _screening.passed.add(i); } } else { int j; for (j = _screening.passed.begin(); j != _screening.passed.end(); ) { i = _screening.passed[j]; int min_possible_ones = _screening.one_counters[i]; int max_possible_ones = _screening.one_counters[i] + _screening.query_ones.size() - _screening.query_bit_idx; int next_j = _screening.passed.next(j); if (min_possible_ones > max_common_ones[i] || max_possible_ones < min_common_ones[i]) _screening.passed.remove(j); j = next_j; } } if (_screening.passed.size() <= _context.context().context().sim_screening_pass_mark) { env.dbgPrintfTS("stopping reading fingerprints on bit %d/%d; have %d molecules to check... ", _screening.query_bit_idx, _screening.query_ones.size(), _screening.passed.size()); _unmatched += _screening.block->used - _screening.passed.size(); break; } } if (entire) { for (i = 0; i < _screening.block->used; i++) { if (_context.similarity.match(target_ones[i], _screening.one_counters[i])) { OraRowidText &rid = matched.at(matched.add()); storage.get(fingerprints.getStorageIndex_NoMap(_screening, i), stored); _decompressRowid(stored, rid); _matched++; } else _unmatched++; } } else if (_screening.passed.size() > 0) { profTimerStart(tfine, "sim.fetch.fine"); for (i = _screening.passed.begin(); i != _screening.passed.end(); i = _screening.passed.next(i)) _match(env, fingerprints.getStorageIndex_NoMap(_screening, _screening.passed[i])); profTimerStop(tfine); } env.dbgPrintf("done\n"); fingerprints.countOnes_End(env, _screening); } profTimerStop(tsimfetch); } bool MangoFastIndex::_loadCoords (OracleEnv &env, const char *rowid, Array &coords) { MangoOracleContext &moc = MangoOracleContext::get(env, _context.context_id, false); return moc.shadow_table.getXyz(env, rowid, coords); } void MangoFastIndex::prepareSubstructure (OracleEnv &env) { env.dbgPrintf("preparing fastindex for substructure search\n"); _context.context().context().storage.validate(env); _context.context().fingerprints.validate(env); _context.context().fingerprints.screenInit(_context.substructure.getQueryFingerprint(), _screening); env.dbgPrintfTS("Have %d bits in query fingerprint\n", _screening.query_ones.size()); _fetch_type = _SUBSTRUCTURE; _cur_idx = 0; _matched = 0; _unmatched = 0; } void MangoFastIndex::prepareSimilarity (OracleEnv &env) { env.dbgPrintfTS("preparing fastindex for similarity search\n"); _context.context().context().storage.validate(env); _context.context().fingerprints.validate(env); _context.context().fingerprints.screenInit(_context.similarity.getQueryFingerprint(), _screening); _fetch_type = _SIMILARITY; _cur_idx = 0; _matched = 0; _unmatched = 0; } void MangoFastIndex::prepareTautomerSubstructure (OracleEnv &env) { env.dbgPrintfTS("preparing fastindex for tautomer substructure search\n"); _context.context().context().storage.validate(env); _context.context().fingerprints.validate(env); _context.context().fingerprints.screenInit(_context.tautomer.getQueryFingerprint(), _screening); _fetch_type = _TAUTOMER_SUBSTRUCTURE; _cur_idx = 0; _matched = 0; _unmatched = 0; } float MangoFastIndex::calcSelectivity (OracleEnv &env, int total_count) { if (_matched + _unmatched == 0) throw Error("calcSelectivity() called before fetch()"); BingoFingerprints &fingerprints = _context.context().fingerprints; if (_fetch_type == _SUBSTRUCTURE || _fetch_type == _TAUTOMER_SUBSTRUCTURE) { if (fingerprints.ableToScreen(_screening)) { return (float)_matched * _screening.items_passed / (_screening.items_read * (_matched + _unmatched)); } else { if (_matched == 0) return 0; return (float)_matched / (_matched + _unmatched); } } else // _fetch_type == _SIMILARITY { if (_matched == 0) return 0; return (float)_matched / (_matched + _unmatched); } } int MangoFastIndex::getIOCost (OracleEnv &env, float selectivity) { BingoFingerprints &fingerprints = _context.context().fingerprints; int blocks = fingerprints.countOracleBlocks(env); float ratio = fingerprints.queryOnesRatio(_screening); return (int)(blocks * ratio); } bool MangoFastIndex::end () { return false; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_fast_index.h000066400000000000000000000042331271037650300242600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_fast_index__ #define __mango_fast_index__ #include "oracle/ora_wrap.h" #include "base_cpp/tlscont.h" #include "molecule/molecule.h" #include "oracle/bingo_fetch_engine.h" #include "oracle/bingo_fingerprints.h" using namespace indigo; class MangoFetchContext; class MangoFastIndex : public BingoFetchEngine { public: MangoFastIndex (MangoFetchContext &context); virtual ~MangoFastIndex (); void prepareSubstructure (OracleEnv &env); void prepareSimilarity (OracleEnv &env); void prepareTautomerSubstructure (OracleEnv &env); virtual void fetch (OracleEnv &env, int maxrows); virtual bool end (); virtual float calcSelectivity (OracleEnv &env, int total_count); virtual int getIOCost (OracleEnv &env, float selectivity); virtual bool getLastRowid (OraRowidText &id); DECL_ERROR; protected: enum { _SUBSTRUCTURE = 1, _SIMILARITY = 2, _TAUTOMER_SUBSTRUCTURE = 3 }; MangoFetchContext &_context; int _fetch_type; int _cur_idx; int _matched; int _unmatched; int _last_id; BingoFingerprints::Screening _screening; bool _loadCoords (OracleEnv &env, const char *rowid, Array &coords); void _match (OracleEnv &env, int idx); int _countOnes (int idx); void _decompressRowid (const Array &stored, OraRowidText &rid); void _fetchSubstructure (OracleEnv &env, int maxrows); void _fetchSimilarity (OracleEnv &env, int maxrows); private: MangoFastIndex (const MangoFastIndex &); // noimplicitcopy }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_fetch_context.cpp000066400000000000000000000071301271037650300253230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/mango_fetch_context.h" #include "core/mango_matchers.h" #include "core/bingo_context.h" #include "oracle/mango_shadow_fetch.h" #include "oracle/bingo_oracle_context.h" TL_DEF(MangoFetchContext, PtrArray, _instances); OsLock MangoFetchContext::_instances_lock; IMPL_ERROR(MangoFetchContext, "mango fetch context"); MangoFetchContext::MangoFetchContext (int id_, MangoOracleContext &context, const Array &query_id) : substructure(context.context()), similarity(context.context()), exact(context.context()), tautomer(context.context()), gross(context.context()), _context(context) { id = id_; context_id = context.context().id; _query_id.copy(query_id); fresh = false; fetch_engine = 0; shadow_fetch.reset(new MangoShadowFetch(*this)); fast_index.reset(new MangoFastIndex(*this)); } MangoFetchContext & MangoFetchContext::get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) return *_instances[i]; throw Error("context #%d not found", id); } MangoFetchContext & MangoFetchContext::create (MangoOracleContext &context, const Array &query_id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int id = 1; for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id >= id) id = _instances[i]->id + 1; AutoPtr new_context(new MangoFetchContext(id, context, query_id)); const BingoOracleContext &boc = context.context(); new_context->id = id; _instances.add(new_context.release()); return *_instances.top(); } MangoFetchContext * MangoFetchContext::findFresh (int context_id, const Array &query_id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) { MangoFetchContext *instance = _instances[i]; if (!instance->fresh) continue; if (instance->context_id != context_id) continue; if (instance->_query_id.memcmp(query_id) != 0) continue; return instance; } return 0; } void MangoFetchContext::remove (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) break; if (i == _instances.size()) throw Error("remove(): context #%d not found", id); _instances.remove(i); } void MangoFetchContext::removeByContextID (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = _instances.size() - 1; i >= 0; i--) if (_instances[i]->context_id == id) _instances.remove(i); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_fetch_context.h000066400000000000000000000037731271037650300250010ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_fetch_context__ #define __mango_fetch_context__ #include "core/mango_matchers.h" #include "base_cpp/auto_ptr.h" #include "oracle/mango_fast_index.h" #include "oracle/mango_shadow_fetch.h" #include "oracle/mango_oracle.h" using namespace indigo; class MangoShadowFetch; class MangoFetchContext { public: MangoFetchContext (int id, MangoOracleContext &context, const Array &query_id); AutoPtr fast_index; AutoPtr shadow_fetch; BingoFetchEngine *fetch_engine; MangoSubstructure substructure; MangoSimilarity similarity; MangoExact exact; MangoTautomer tautomer; MangoGross gross; MangoMass mass; int id; int context_id; bool fresh; // 'true' after selectivity calculation and before index start static MangoFetchContext & create (MangoOracleContext &context, const Array &query_id); static MangoFetchContext & get (int id); static MangoFetchContext * findFresh (int context_id, const Array &query_id); static void remove (int id); static void removeByContextID (int id); inline MangoOracleContext & context () {return _context;} DECL_ERROR; protected: Array _query_id; MangoOracleContext & _context; TL_DECL(PtrArray, _instances); static OsLock _instances_lock; }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_operators.cpp000066400000000000000000000437261271037650300245170ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molfile_loader.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_tautomer_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_mass.h" #include "molecule/gross_formula.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "core/mango_matchers.h" #include "oracle/mango_oracle.h" #include "oracle/bingo_oracle_context.h" #include "molecule/smiles_loader.h" #include "molecule/icm_loader.h" #include "molecule/elements.h" using namespace indigo; static OCINumber * _mangoSub (OracleEnv &env, MangoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params) { if (context.substructure.parse(params)) { context.substructure.loadQuery(query_buf); TRY_READ_TARGET_MOL { context.substructure.loadTarget(target_buf); } CATCH_READ_TARGET_MOL(return 0) int result = context.substructure.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } if (context.tautomer.parseSub(params)) { context.tautomer.loadQuery(query_buf); TRY_READ_TARGET_MOL { context.tautomer.loadTarget(target_buf); } CATCH_READ_TARGET_MOL(return 0) int result = context.tautomer.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } throw BingoError("cannot parse parameters"); } static OCINumber * _mangoExact (OracleEnv &env, MangoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params) { if (context.exact.parse(params)) { context.exact.loadQuery(query_buf); TRY_READ_TARGET_MOL { context.exact.loadTarget(target_buf); } CATCH_READ_TARGET_MOL(return 0) int result = context.exact.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } if (context.tautomer.parseExact(params)) { context.tautomer.loadQuery(query_buf); TRY_READ_TARGET_MOL { context.tautomer.loadTarget(target_buf); } CATCH_READ_TARGET_MOL(return 0) int result = context.tautomer.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } throw BingoError("cannot parse parameters"); } static OCINumber * _mangoSim (OracleEnv &env, MangoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params) { MangoSimilarity &instance = context.similarity; instance.setMetrics(params); instance.loadQuery(query_buf); TRY_READ_TARGET_MOL { float result = instance.calc(target_buf); return OracleExtproc::createDouble(env, (double)result); } CATCH_READ_TARGET_MOL(return 0) } // used for SUB(), EXACT() and SIM() but not for GROSS() static OCINumber * _mangoCommon (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind, OCINumber * (*callback) (OracleEnv &env, MangoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params)) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); result = callback(env, context, query_buf, target_buf, params); } if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createInt(env, 0); else *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCINumber * oraMangoSub (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { return _mangoCommon(ctx, context_id, target_loc, target_ind, query_loc, query_ind, params, params_ind, return_ind, _mangoSub); } ORAEXT OCINumber * oraMangoSmarts (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *query, short query_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); query_buf.readString(query, false); context.substructure.loadSMARTS(query_buf); TRY_READ_TARGET_MOL { context.substructure.loadTarget(target_buf); } CATCH_READ_TARGET_MOL(return OracleExtproc::createInt(env, 0)) int match = context.substructure.matchLoadedTarget() ? 1 : 0; result = OracleExtproc::createInt(env, match); *return_ind = OCI_IND_NOTNULL; } else // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createInt(env, 0); } ORABLOCK_END return result; } ORAEXT OCINumber * oraMangoExact (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { return _mangoCommon(ctx, context_id, target_loc, target_ind, query_loc, query_ind, params, params_ind, return_ind, _mangoExact); } ORAEXT OCINumber * oraMangoSim (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { return _mangoCommon(ctx, context_id, target_loc, target_ind, query_loc, query_ind, params, params_ind, return_ind, _mangoSim); } ORAEXT OCILobLocator * oraMangoSubHi (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); if (context.substructure.parse(params)) { context.substructure.preserve_bonds_on_highlighting = true; context.substructure.loadQuery(query_buf); context.substructure.loadTarget(target_buf); if (!context.substructure.matchLoadedTarget()) throw BingoError("SubHi: match not found"); context.substructure.getHighlightedTarget(target_buf); } else if (context.tautomer.parseSub(params)) { context.tautomer.preserve_bonds_on_highlighting = true; context.tautomer.loadQuery(query_buf); context.tautomer.loadTarget(target_buf); if (!context.tautomer.matchLoadedTarget()) throw BingoError("SubHi: match not found"); context.tautomer.getHighlightedTarget(target_buf); } else throw BingoError("SubHi: can't parse params '%s'", params); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind = OCI_IND_NOTNULL; return lob.get(); } } ORABLOCK_END return 0; } ORAEXT OCILobLocator * oraMangoSmartsHi (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *query, short query_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); query_buf.readString(query, false); context.substructure.preserve_bonds_on_highlighting = true; context.substructure.loadSMARTS(query_buf); context.substructure.loadTarget(target_buf); if (!context.substructure.matchLoadedTarget()) throw BingoError("SmartsHi: match not found"); context.substructure.getHighlightedTarget(target_buf); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind = OCI_IND_NOTNULL; return lob.get(); } } ORABLOCK_END return 0; } ORAEXT OCILobLocator * oraMangoExactHi (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); if (context.tautomer.parseExact(params)) { context.tautomer.preserve_bonds_on_highlighting = true; context.tautomer.loadQuery(query_buf); context.tautomer.loadTarget(target_buf); if (!context.tautomer.matchLoadedTarget()) throw BingoError("ExactHi: match not found"); context.tautomer.getHighlightedTarget(target_buf); } else throw BingoError("ExactHi: can't parse params '%s'", params); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind = OCI_IND_NOTNULL; return lob.get(); } } ORABLOCK_END return 0; } static OCIString * _mangoGrossCalc (OracleEnv &env, MangoOracleContext &context, const Array &target_buf) { QS_DEF(Molecule, target); MoleculeAutoLoader loader(target_buf); context.context().setLoaderSettings(loader); loader.loadMolecule(target); OCIString *result = 0; QS_DEF(Array, gross); QS_DEF(Array, gross_str); GrossFormula::collect(target, gross); GrossFormula::toString(gross, gross_str); if (gross_str.size() == 1) // We can not return empty string to Oracle, as empty string is NULL to Oracle. // So we return a string containing one space env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)" ", 1, &result)); else env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)gross_str.ptr(), gross_str.size() - 1, &result)); return result; } static OCINumber * _mangoGross (OracleEnv &env, MangoOracleContext &context, const Array &target_buf, const char *query) { MangoGross &instance = context.gross; instance.parseQuery(query); int res = 0; TRY_READ_TARGET_MOL { res = instance.checkMolecule(target_buf); } CATCH_READ_TARGET_MOL(return 0) return OracleExtproc::createInt(env, res ? 1 : 0); } ORAEXT OCIString * oraMangoGrossCalc (OCIExtProcContext *ctx, OCILobLocator *target_loc, short target_ind, short *return_ind) { OCIString *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, 0, false); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); result = _mangoGrossCalc(env, context, target_buf); *return_ind = OCI_IND_NOTNULL; } else // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); } ORABLOCK_END return result; } ORAEXT OCINumber * oraMangoGross (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *query, short query_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); result = _mangoGross(env, context, target_buf, query); } if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createInt(env, 0); else *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } static OCINumber * _mangoMass (OracleEnv &env, MangoOracleContext &context, const Array &target_buf, const char *type) { float molmass = 0; TRY_READ_TARGET_MOL { QS_DEF(Molecule, target); BufferScanner scanner(target_buf); MoleculeAutoLoader loader(scanner); BingoOracleContext &bingo_context = context.context(); bingo_context.setLoaderSettings(loader); loader.loadMolecule(target); MoleculeMass mass_calulator; mass_calulator.relative_atomic_mass_map = &bingo_context.relative_atomic_mass_map; if (type == 0 || strcasecmp(type, "molecular-weight") == 0) molmass = mass_calulator.molecularWeight(target); else if (strcasecmp(type, "most-abundant-mass") == 0) molmass = mass_calulator.mostAbundantMass(target); else if (strcasecmp(type, "monoisotopic-mass") == 0) molmass = mass_calulator.monoisotopicMass(target); else throw BingoError("unknown mass specifier: %s", type); } CATCH_READ_TARGET_MOL(return 0) return OracleExtproc::createDouble(env, molmass); } ORAEXT OCINumber * oraMangoMolecularMass (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *type, short type_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (type_ind != OCI_IND_NOTNULL) type = 0; if (target_ind == OCI_IND_NOTNULL) { MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); result = _mangoMass(env, context, target_buf, type); } if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createDouble(env, 0); else *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle.cpp000066400000000000000000000042371271037650300237400ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/mango_oracle.h" #include "base_cpp/output.h" #include "oracle/mango_shadow_table.h" #include "oracle/bingo_oracle_context.h" #include "base_cpp/auto_ptr.h" const char *bad_molecule_warning = "WARNING: bad molecule: %s\n"; const char *bad_molecule_warning_rowid = "WARNING: bad molecule %s: %s\n"; MangoOracleContext::MangoOracleContext (BingoContext &context) : MangoContext(context), shadow_table(context.id), fingerprints(context.id) { } MangoOracleContext::~MangoOracleContext () { } BingoOracleContext & MangoOracleContext::context () { return (BingoOracleContext &)_context; } MangoOracleContext & MangoOracleContext::get (OracleEnv &env, int id, bool lock) { bool config_reloaded; BingoOracleContext &context = BingoOracleContext::get(env, id, lock, &config_reloaded); MangoContext *already = _get(id, context); MangoOracleContext *moc; AutoPtr res; if (already == 0) { res.reset(new MangoOracleContext(context)); moc = res.get(); config_reloaded = true; } else moc = (MangoOracleContext *)already; if (config_reloaded) { moc->fingerprints.init(context, context.fp_parameters.fingerprintSize(), context.fp_parameters.fingerprintSizeExt(), context.fp_parameters.fingerprintSizeExtOrd()); } if (already == 0) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); _instances.add(res.release()); return *(MangoOracleContext *)_instances.top(); } return *moc; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle.h000066400000000000000000000067241271037650300234100ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_oracle__ #define __mango_oracle__ #include "core/mango_context.h" #include "oracle/mango_shadow_table.h" #include "oracle/bingo_fingerprints.h" using namespace indigo; namespace indigo { class OracleEnv; } class BingoOracleContext; class MangoOracleContext : public MangoContext { public: explicit MangoOracleContext (BingoContext &context); virtual ~MangoOracleContext (); BingoOracleContext & context (); MangoShadowTable shadow_table; BingoFingerprints fingerprints; static MangoOracleContext & get (OracleEnv &env, int id, bool lock); }; extern const char *bad_molecule_warning; extern const char *bad_molecule_warning_rowid; #define TRY_READ_TARGET_MOL \ try { #define CATCH_READ_TARGET_MOL(action) \ } \ catch (Scanner::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (MolfileLoader::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (Element::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (Graph::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (MoleculeStereocenters::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (MoleculeCisTrans::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (SmilesLoader::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (IcmLoader::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (Molecule::Error &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ catch (DearomatizationException &e) { env.dbgPrintf(bad_molecule_warning, e.message()); action;} \ #define CATCH_READ_TARGET_MOL_ROWID(rowid, action) \ } \ catch (Scanner::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (MolfileLoader::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (Element::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (Graph::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (MoleculeStereocenters::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (MoleculeCisTrans::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (SmilesLoader::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (IcmLoader::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (Molecule::Error &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ catch (DearomatizationException &e) { env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); action;} \ #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle_fetch.cpp000066400000000000000000000324311271037650300251060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "core/mango_matchers.h" #include "oracle/mango_fetch_context.h" #include "oracle/bingo_oracle_context.h" #include "oracle/mango_oracle.h" #include "base_c/nano.h" #include "base_cpp/profiling.h" #include "oracle/bingo_profiling.h" static void _mangoIndexStart (OracleEnv &env, MangoFetchContext &context, const char *oper, const Array &query_buf, OCINumber *p_strt, OCINumber *p_stop, int flags, const char *params) { MangoShadowFetch &shadow_fetch = context.shadow_fetch.ref(); MangoFastIndex &fast_index = context.fast_index.ref(); if (strcasecmp(oper, "SUB") == 0) { if (context.substructure.parse(params)) { context.substructure.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); if (right == 1) { fast_index.prepareSubstructure(env); context.fetch_engine = &fast_index; } else // right == 0 { shadow_fetch.prepareNonSubstructure(env); context.fetch_engine = &shadow_fetch; } } else if (context.tautomer.parseSub(params)) { context.tautomer.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); if (right == 1) { fast_index.prepareTautomerSubstructure(env); context.fetch_engine = &fast_index; } else // right == 0 { shadow_fetch.prepareNonTautomerSubstructure(env); context.fetch_engine = &shadow_fetch; } } else throw BingoError("can't parse parameters: '%s'", params); } else if (strcasecmp(oper, "SMARTS") == 0) { context.substructure.loadSMARTS(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); if (right == 1) { fast_index.prepareSubstructure(env); context.fetch_engine = &fast_index; } else // right == 0 { shadow_fetch.prepareNonSubstructure(env); context.fetch_engine = &shadow_fetch; } } else if (strcasecmp(oper, "EXACT") == 0) { if (context.tautomer.parseExact(params)) { context.tautomer.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); shadow_fetch.prepareTautomer(env, right); context.fetch_engine = &shadow_fetch; } else if (context.exact.parse(params)) { context.exact.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); shadow_fetch.prepareExact(env, right); context.fetch_engine = &shadow_fetch; } else throw BingoError("can't parse parameters: '%s'", params); } else if (strcasecmp(oper, "SIM") == 0) { context.similarity.setMetrics(params); context.similarity.loadQuery(query_buf); float bottom = -0.1f; float top = 1.1f; if (p_strt == 0 && p_stop == 0) throw BingoError("no bounds for similarity search"); if (p_strt != 0) bottom = OracleUtil::numberToFloat(env, p_strt); if (p_stop != 0) top = OracleUtil::numberToFloat(env, p_stop); if (flags & 64) throw BingoError("exact match not allowed"); context.similarity.include_bottom = ((flags & 16) != 0); context.similarity.include_top = ((flags & 32) != 0); context.similarity.bottom = bottom; context.similarity.top = top; fast_index.prepareSimilarity(env); context.fetch_engine = &fast_index; } else if (strcasecmp(oper, "GROSS") == 0) { int right = bingoGetExactRightPart(env, p_strt, p_stop, flags); MangoGross &instance = context.gross; instance.parseQuery(query_buf); shadow_fetch.prepareGross(env, right); context.fetch_engine = &shadow_fetch; } else if (strcasecmp(oper, "MASS") == 0) { float bottom = 0; float top = 1e10; if (p_strt == 0 && p_stop == 0) throw BingoError("no bounds for molecular mass search"); if (p_strt != 0) bottom = OracleUtil::numberToFloat(env, p_strt); if (p_stop != 0) top = OracleUtil::numberToFloat(env, p_stop); context.mass.bottom = bottom; context.mass.top = top; shadow_fetch.prepareMass(env); context.fetch_engine = &shadow_fetch; } else throw BingoError("unknown operator: %s", oper); } ORAEXT int oraMangoIndexStart (OCIExtProcContext *ctx, int context_id, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, int flags, const char *params, short params_ind) { ORABLOCK_BEGIN { profTimerStart(tstart, "fetch_start"); OracleEnv env(ctx, logger); env.dbgPrintfTS("IndexStart... "); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); QS_DEF(Array, query_id); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, flags, params, query_id); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); MangoFetchContext *fetch_context = MangoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { profTimersReset(); fetch_context = &MangoFetchContext::create(context, query_id); _mangoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, flags, params); env.dbgPrintfTS("created fetcher #%d\n", fetch_context->id); } else { fetch_context->fresh = false; env.dbgPrintfTS("found fresh fetcher #%d\n", fetch_context->id); } profTimerStop(tstart); return fetch_context->id; } ORABLOCK_END return -1; } ORAEXT int oraMangoIndexFetch (OCIExtProcContext *ctx, int fetch_id, int maxrows, OCIArray **array, short array_indicator) { profTimerStart(t0, "fetch"); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("oraMangoIndexFetch started\n"); MangoFetchContext &context = MangoFetchContext::get(fetch_id); if (context.fetch_engine == 0) throw BingoError("fetch_engine = 0 in oraMangoIndexFetch()"); BingoFetchEngine &fetch_engine = *context.fetch_engine; ORA_TRY_FETCH_BEGIN { if (maxrows > 100) maxrows = 100; // can have fetched rowid-s from the selectivity computation phase maxrows -= bingoPopRowidsToArray(env, fetch_engine.matched, maxrows, *array); if (maxrows > 0 && !fetch_engine.end()) { env.dbgPrintfTS("[fetcher #%d] ", context.id); fetch_engine.fetch(env, maxrows); maxrows -= bingoPopRowidsToArray(env, fetch_engine.matched, maxrows, *array); } return fetch_engine.end() ? 0 : 1; } ORA_TRY_FETCH_END } ORABLOCK_END return 0; } ORAEXT void oraMangoIndexClose (OCIExtProcContext *ctx, int fetch_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("closing fetch #%d\n", fetch_id); MangoFetchContext::remove(fetch_id); // Print statistics bingoProfilingPrintStatistics(false); } ORABLOCK_END } ORAEXT OCINumber * oraMangoIndexSelectivity (OCIExtProcContext *ctx, int context_id, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, int flags, const char *params, short params_ind, short *return_ind) { profTimersReset(); *return_ind = OCI_IND_NULL; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("IndexSelectivity... "); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); QS_DEF(Array, query_id); QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, flags, params, query_id); MangoFetchContext * fetch_context = MangoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { fetch_context = &MangoFetchContext::create(context, query_id); env.dbgPrintf("created fresh fetcher #%d\n", fetch_context->id); } else env.dbgPrintf("found fresh fetcher #%d\n", fetch_context->id); _mangoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, flags, params); env.dbgPrintfTS("[fetcher #%d] ", fetch_context->id); fetch_context->fetch_engine->fetch(env, 100); fetch_context->fresh = true; float res = fetch_context->fetch_engine->calcSelectivity(env, context.fingerprints.getTotalCount(env)); env.dbgPrintfTS("calculated selectivity = %.2f\n", res); OCINumber *result = OracleExtproc::createDouble(env, (double)res); *return_ind = OCI_IND_NOTNULL; return result; } ORABLOCK_END return 0; } ORAEXT void oraMangoIndexCost (OCIExtProcContext *ctx, int context_id, OCINumber *p_sel, short sel_ind, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, int flags, const char *params, short params_ind, int *iocost, int *cpucost) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("IndexCost... "); if (sel_ind != OCI_IND_NOTNULL) throw BingoError("null selectivity given"); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; float sel = OracleUtil::numberToFloat(env, p_sel); QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); QS_DEF(Array, query_id); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, flags, params, query_id); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); MangoFetchContext *fetch_context = MangoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { fetch_context = &MangoFetchContext::create(context, query_id); _mangoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, flags, params); fetch_context->fresh = true; env.dbgPrintf("created fresh fetcher #%d\n", fetch_context->id); } else env.dbgPrintf("found fresh fetcher #%d\n", fetch_context->id); *iocost = fetch_context->fetch_engine->getIOCost(env, sel); *cpucost = (int)(context.fingerprints.getTotalCount(env) * sel); env.dbgPrintfTS("calculated iocost = %d, cpucost = %d\n", *iocost, *cpucost); } ORABLOCK_END } ORAEXT void oraMangoCollectStatistics (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); MangoOracleContext & context = MangoOracleContext::get(env, context_id, false); context.shadow_table.analyze(env); } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle_index.cpp000066400000000000000000000352401271037650300251250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/scanner.h" #include "base_cpp/profiling.h" #include "base_cpp/os_sync_wrapper.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molfile_loader.h" #include "molecule/molecule_stereocenters.h" #include "molecule/cmf_saver.h" #include "molecule/smiles_loader.h" #include "molecule/icm_loader.h" #include "core/mango_index.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/mango_oracle.h" #include "oracle/mango_shadow_table.h" #include "oracle/bingo_oracle_context.h" #include "oracle/bingo_fingerprints.h" #include "oracle/rowid_saver.h" #include "oracle/mango_oracle_index_parallel.h" #include "oracle/bingo_profiling.h" #include "molecule/elements.h" #include "base_cpp/auto_ptr.h" #include "oracle/mango_fetch_context.h" bool mangoPrepareMolecule (OracleEnv &env, const char *rowid, const Array &molfile_buf, MangoOracleContext &context, MangoIndex &index, Array &data, OsLock *lock_for_exclusive_access, std::string &failure_message) { profTimerStart(tall, "moleculeIndex.prepare"); ArrayOutput output(data); output.writeChar(0); // 0 -- present, 1 -- removed from index QS_DEF(Array, compressed_rowid); ArrayOutput rid_output(compressed_rowid); { // RowIDSaver modifies context.context().rid_dict and // requires exclusive access for this OsLockerNullable locker(lock_for_exclusive_access); RowIDSaver rid_saver(context.context().rid_dict, rid_output); rid_saver.saveRowID(rowid); } output.writeByte((byte)compressed_rowid.size()); output.writeArray(compressed_rowid); TRY_READ_TARGET_MOL { BufferScanner scanner(molfile_buf); try { index.prepare(scanner, output, lock_for_exclusive_access); } catch (CmfSaver::Error &e) { if (context.context().reject_invalid_structures) throw; // Rethrow this exception further OsLockerNullable locker(lock_for_exclusive_access); env.dbgPrintf(bad_molecule_warning_rowid, rowid, e.message()); failure_message = e.message(); return false; } } CATCH_READ_TARGET_MOL_ROWID(rowid, { if (context.context().reject_invalid_structures) throw; // Rethrow this exception further failure_message = e.message(); return false; }); // some magic: round it up to avoid ora-22282 if (data.size() % 2 == 1) output.writeChar(0); return true; } void mangoRegisterMolecule (OracleEnv &env, const char *rowid, MangoOracleContext &context, const MangoIndex &index, BingoFingerprints &fingerprints, const Array &prepared_data, bool append) { profTimerStart(tall, "moleculeIndex.register"); int blockno, offset; profTimerStart(tstor, "moleculeIndex.register_storage"); context.context().storage.add(env, prepared_data, blockno, offset); profTimerStop(tstor); profTimerStart(tfing, "moleculeIndex.register_fingerprint"); fingerprints.addFingerprint(env, index.getFingerprint()); profTimerStop(tfing); profTimerStart(tshad, "moleculeIndex.register_shadow"); context.shadow_table.addMolecule(env, index, rowid, blockno + 1, offset, append); profTimerStop(tshad); } bool mangoPrepareAndRegisterMolecule (OracleEnv &env, const char *rowid, const Array &molfile_buf, MangoOracleContext &context, MangoIndex &index, BingoFingerprints &fingerprints, bool append) { QS_DEF(Array, prepared_data); std::string failure_message; if (mangoPrepareMolecule(env, rowid, molfile_buf, context, index, prepared_data, NULL, failure_message)) { mangoRegisterMolecule(env, rowid, context, index, fingerprints, prepared_data, append); return true; } else { context.context().warnings.add(env, rowid, failure_message.c_str()); return false; } } void mangoRegisterTable (OracleEnv &env, MangoOracleContext &context, const char *source_table, const char *source_column, const char *target_datatype) { profTimerStart(tall, "total"); QS_DEF(Array, molfile_buf); OracleStatement statement(env); AutoPtr molfile_lob; OraRowidText rowid; char varchar2_text[4001]; // Oracle's BLOB and CLOB types always come uppercase bool blob = (strcmp(target_datatype, "BLOB") == 0); bool clob = (strcmp(target_datatype, "CLOB") == 0); int total_count = 0; OracleStatement::executeSingleInt(total_count, env, "SELECT COUNT(*) FROM %s WHERE %s IS NOT NULL AND LENGTH(%s) > 0", source_table, source_column, source_column); context.context().longOpInit(env, total_count, "Building molecule index", source_table, "molecules"); statement.append("SELECT %s, RowidToChar(rowid) FROM %s WHERE %s IS NOT NULL AND LENGTH(%s) > 0", source_column, source_table, source_column, source_column); //"ORDER BY dbms_rowid.rowid_block_number(rowid), dbms_rowid.rowid_row_number(rowid)", statement.prepare(); if (blob) { molfile_lob.reset(new OracleLOB(env)); statement.defineBlobByPos(1, molfile_lob.ref()); } else if (clob) { molfile_lob.reset(new OracleLOB(env)); statement.defineClobByPos(1, molfile_lob.ref()); } else statement.defineStringByPos(1, varchar2_text, sizeof(varchar2_text)); statement.defineStringByPos(2, rowid.ptr(), sizeof(rowid)); BingoFingerprints &fingerprints = context.fingerprints; fingerprints.validateForUpdate(env); if (context.context().nthreads == 1) { int n = 0; QS_DEF(MangoIndex, index); index.init(context.context()); if (statement.executeAllowNoData()) do { env.dbgPrintf("inserting molecule #%d with rowid %s\n", n, rowid.ptr()); if (blob || clob) molfile_lob->readAll(molfile_buf, false); else molfile_buf.readString(varchar2_text, false); try { mangoPrepareAndRegisterMolecule(env, rowid.ptr(), molfile_buf, context, index, fingerprints, true); } catch (Exception &ex) { char buf[4096]; snprintf(buf, NELEM(buf), "Failed on record with rowid=%s. Error message is '%s'", rowid.ptr(), ex.message()); throw Exception(buf); } n++; if ((n % 50) == 0) context.context().longOpUpdate(env, n); if ((n % 1000) == 0) { env.dbgPrintf("done %d molecules; flushing\n", n); context.context().storage.flush(env); } } while (statement.fetch()); } else { if (statement.executeAllowNoData()) { MangoRegisterDispatcher dispatcher(context, env, rowid.ptr()); dispatcher.setup(&statement, molfile_lob.get(), varchar2_text, blob || clob); int nthreads = context.context().nthreads; if (nthreads <= 0) dispatcher.run(); else dispatcher.run(nthreads); } } fingerprints.flush(env); context.shadow_table.flush(env); } ORAEXT void oraMangoCreateIndex (OCIExtProcContext *ctx, int context_id, const char *params, short params_ind, const char *full_table_name, short full_table_name_ind, const char *column_name, short column_name_ind, const char *column_data_type, short column_data_type_ind) { ORABLOCK_BEGIN { profTimersReset(); OracleEnv env(ctx, logger); env.dbgPrintfTS("Creating index\n"); BingoOracleContext &bcontext = BingoOracleContext::get(env, context_id, false, 0); // parse parameters before creating MangoOracleContext because // it creates the BingoSreening according to fingerprintSize() if (params_ind == OCI_IND_NOTNULL) bcontext.parseParameters(env, params); if (full_table_name_ind != OCI_IND_NOTNULL) throw BingoError("null table name given"); if (column_name_ind != OCI_IND_NOTNULL) throw BingoError("null column name given"); if (column_data_type_ind != OCI_IND_NOTNULL) throw BingoError("null data type given"); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); // save atomic mass parameters for the indexed table bcontext.atomicMassSave(env); BingoStorage &storage = context.context().storage; BingoFingerprints &fingerprints = context.fingerprints; context.shadow_table.drop(env); context.shadow_table.create(env); fingerprints.drop(env); fingerprints.create(env); storage.drop(env); storage.create(env); storage.validateForInsert(env); mangoRegisterTable(env, context, full_table_name, column_name, column_data_type); storage.finish(env); context.context().saveCmfDict(env); context.context().saveRidDict(env); env.dbgPrintfTS("Creating internal indices... "); context.shadow_table.createIndices(env); env.dbgPrintfTS("done\n"); OracleStatement::executeSingle(env, "COMMIT"); MangoFetchContext::removeByContextID(context_id); MangoContext::remove(context_id); BingoContext::remove(context_id); bingoProfilingPrintStatistics(false); } ORABLOCK_END } ORAEXT void oraMangoDropIndex (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("Dropping index #%d\n", context_id); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); context.shadow_table.drop(env); context.context().storage.drop(env); context.fingerprints.drop(env); MangoFetchContext::removeByContextID(context_id); MangoContext::remove(context_id); BingoContext::remove(context_id); // TEMP: remove CMF dictionary OracleStatement::executeSingle(env, "DELETE FROM CONFIG_BLOB WHERE n=%d", context_id); } ORABLOCK_END } ORAEXT void oraMangoTruncateIndex (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("Truncating index #%d\n", context_id); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); context.shadow_table.truncate(env); context.context().storage.truncate(env); context.fingerprints.truncate(env); MangoFetchContext::removeByContextID(context_id); MangoContext::remove(context_id); BingoContext::remove(context_id); } ORABLOCK_END } ORAEXT void oraMangoIndexInsert (OCIExtProcContext *ctx, int context_id, const char *rowid, short rowid_ind, OCILobLocator *target_loc, short target_ind) { ORABLOCK_BEGIN { profTimersReset(); OracleEnv env(ctx, logger); if (rowid_ind != OCI_IND_NOTNULL) throw BingoError("null rowid given"); if (target_ind != OCI_IND_NOTNULL) // somebody added a NULL value into the table; ignore it return; MangoOracleContext &context = MangoOracleContext::get(env, context_id, true); env.dbgPrintf("inserting molecule with rowid %s\n", rowid); QS_DEF(MangoIndex, index); index.init(context.context()); BingoFingerprints &fingerprints = context.fingerprints; BingoStorage &storage = context.context().storage; storage.lock(env); storage.validateForInsert(env); fingerprints.validateForUpdate(env); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); mangoPrepareAndRegisterMolecule(env, rowid, target_buf, context, index, fingerprints, false); storage.finish(env); context.shadow_table.flush(env); //fingerprints.flush(env); if (context.context().cmf_dict.isModified()) context.context().saveCmfDict(env); if (context.context().rid_dict.isModified()) context.context().saveRidDict(env); bingoProfilingPrintStatistics(false); } ORABLOCK_END } ORAEXT void oraMangoIndexDelete (OCIExtProcContext *ctx, int context_id, const char *rowid, short rowid_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (rowid_ind != OCI_IND_NOTNULL) throw BingoError("null rowid given"); MangoOracleContext &context = MangoOracleContext::get(env, context_id, false); int blockno, offset; if (context.shadow_table.getMoleculeLocation(env, rowid, blockno, offset)) { env.dbgPrintf("deleting molecule that has rowid %s\n", rowid); BingoStorage &storage = context.context().storage; storage.lock(env); storage.markRemoved(env, blockno, offset); context.shadow_table.deleteMolecule(env, rowid); } else env.dbgPrintf("molecule with rowid %s not found\n", rowid); } ORABLOCK_END } ORAEXT void oraMangoFlushInserts (OCIExtProcContext *ctx, int commit) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); int i; for (i = MangoContext::begin(); i != MangoContext::end(); i = MangoContext::next(i)) { env.dbgPrintfTS("flushing inserts of context #%d\n", i); MangoOracleContext &context = MangoOracleContext::get(env, i, false); context.fingerprints.flush(env); if (commit) OracleStatement::executeSingle(env, "COMMIT"); context.context().unlock(env); } } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle_index_parallel.cpp000066400000000000000000000120261271037650300267760ustar00rootroot00000000000000#include "oracle/mango_oracle_index_parallel.h" #include "oracle/bingo_oracle_context.h" #include "base_cpp/profiling.h" // // MangoRegisterDispatcher // MangoRegisterDispatcher::MangoRegisterDispatcher (MangoOracleContext &context, OracleEnv &env, const char *rowid) : BingoOracleDispatcher(OsCommandDispatcher::HANDLING_ORDER_SERIAL, true, 70), _context(context), _env(env) { _molecules_prepared = 0; _molecules_saved = 0; _rowid = rowid; } OsCommand * MangoRegisterDispatcher::_allocateCommand () { return new MangoRegisterCommand(_env, _context, _lock_for_exclusive_access, &_molecules_prepared); } OsCommandResult * MangoRegisterDispatcher::_allocateResult () { return new MangoRegisterResult; } bool MangoRegisterDispatcher::_setupCommand (OsCommand &command) { bool ret = BingoOracleDispatcher::_setupCommand(command); return ret; } void MangoRegisterDispatcher::_addCurrentRecordToCommand (BingoOracleCommand &command) { profTimerStart(t, "dispatcher.prepare_task"); BingoOracleDispatcher::_addCurrentRecordToCommand(command); MangoRegisterCommand &cmd = (MangoRegisterCommand &)command; cmd.rowids.add(_rowid); } void MangoRegisterDispatcher::_handleResult (OsCommandResult &result) { profTimerStart(t, "dispatcher.handle_result"); // Handle result MangoRegisterResult &res = (MangoRegisterResult &)result; BingoFingerprints &fingerprints = _context.fingerprints; for (auto &warning : res.warnings) _context.context().warnings.add(_env, warning.rowid.c_str(), warning.message.c_str()); QS_DEF(Array, prepared_data); for (int i = 0; i < res.valid_molecules; i++) { const char *rowid = (const char *)res.rowids.get(i); prepared_data.copy((char *)res.per_molecule_data.get(i), res.per_molecule_data.getSize(i)); mangoRegisterMolecule(_env, rowid, _context, res.per_molecule_index[i], fingerprints, prepared_data, true); _molecules_saved++; if ((_molecules_saved % 100) == 0) { OsLocker locker(_lock_for_exclusive_access); _context.context().longOpUpdate(_env, _molecules_prepared); } if ((_molecules_saved % 2000) == 0) { { OsLocker locker(_lock_for_exclusive_access); _env.dbgPrintfTS("done %d molecules; flushing\n", _molecules_prepared); } _context.context().storage.flush(_env); } } } // // MangoRegisterCommand // MangoRegisterCommand::MangoRegisterCommand (OracleEnv &env, MangoOracleContext &context, OsLock &lock_for_exclusive_access, int *molecules_prepared_counter) : _context(context), _env(env), _lock_for_exclusive_access(lock_for_exclusive_access) { _molecules_prepared_counter = molecules_prepared_counter; } void MangoRegisterCommand::clear () { rowids.clear(); BingoOracleCommand::clear(); } void MangoRegisterCommand::execute (OsCommandResult &result) { MangoRegisterResult &res = (MangoRegisterResult &)result; res.valid_molecules = 0; QS_DEF(Array, molfile_buf); QS_DEF(Array, prepared_data); std::string failure_message; for (int i = 0; i < blob_storage.count(); i++) { molfile_buf.copy((char *)blob_storage.get(i), blob_storage.getSize(i)); const char *rowid = (const char *)rowids.get(i); { OsLocker locker(_lock_for_exclusive_access); _env.dbgPrintf("preparing molecule #%d with rowid %s\n", *_molecules_prepared_counter, rowid); (*_molecules_prepared_counter)++; } try { if (res.per_molecule_index.size() <= res.valid_molecules) { // Push new MangoIndex res.per_molecule_index.push(); } MangoIndex &index = res.per_molecule_index[res.valid_molecules]; index.init(_context.context()); if (mangoPrepareMolecule(_env, rowid, molfile_buf, _context, index, prepared_data, &_lock_for_exclusive_access, failure_message)) { res.per_molecule_data.add((byte*)prepared_data.ptr(), prepared_data.size()); res.valid_molecules++; res.rowids.add((byte *)rowid, rowids.getSize(i)); } else { res.warnings.emplace_back(); MangoRegisterFailure &f = res.warnings.back(); f.rowid = rowid; f.message = failure_message; } } catch (Exception &ex) { char buf[4096]; snprintf(buf, NELEM(buf), "Failed on record with rowid=%s. Error message is '%s'", rowid, ex.message()); throw Exception(buf); } } } // // MangoRegisterResult // void MangoRegisterResult::clear () { rowids.clear(); warnings.clear(); per_molecule_data.clear(); valid_molecules = 0; // Don't clear per_molecule_index } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle_index_parallel.h000066400000000000000000000050021271037650300264370ustar00rootroot00000000000000#ifndef __mango_oracle_index_parallel_h__ #define __mango_oracle_index_parallel_h__ #include "base_cpp/os_sync_wrapper.h" #include "oracle/bingo_oracle_parallel.h" #include "oracle/mango_oracle.h" #include #include using namespace indigo; // // Classes for parallelized index creation // class MangoRegisterDispatcher : public BingoOracleDispatcher { public: MangoRegisterDispatcher (MangoOracleContext &context, OracleEnv &env, const char *rowid); protected: virtual OsCommand* _allocateCommand (); virtual OsCommandResult* _allocateResult (); virtual bool _setupCommand (OsCommand &command); virtual void _addCurrentRecordToCommand (BingoOracleCommand &command); virtual void _handleResult (OsCommandResult &result); MangoOracleContext &_context; OracleEnv &_env; const char *_rowid; int _molecules_prepared, _molecules_saved; OsLock _lock_for_exclusive_access; }; class MangoRegisterCommand : public BingoOracleCommand { public: MangoRegisterCommand (OracleEnv &env, MangoOracleContext &context, OsLock &lock_for_exclusive_access, int *molecules_prepared_counter); virtual void execute (OsCommandResult &result); virtual void clear (); ChunkStorage rowids; private: MangoOracleContext &_context; OracleEnv &_env; OsLock &_lock_for_exclusive_access; int *_molecules_prepared_counter; }; struct MangoRegisterFailure { std::string rowid, message; }; class MangoRegisterResult : public OsCommandResult { public: int valid_molecules; virtual void clear (); ObjArray per_molecule_index; std::vector warnings; ChunkStorage per_molecule_data; ChunkStorage rowids; }; bool mangoPrepareMolecule (OracleEnv &env, const char *rowid, const Array &molfile_buf, MangoOracleContext &context, MangoIndex &index, Array &data, OsLock *lock_for_exclusive_access, std::string &failure_message); void mangoRegisterMolecule (OracleEnv &env, const char *rowid, MangoOracleContext &context, const MangoIndex &index, BingoFingerprints &fingerprints, const Array &prepared_data, bool append); #endif // __mango_oracle_index_parallel_h__ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_oracle_util.cpp000066400000000000000000000374641271037650300250050ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/profiling.h" #include "molecule/molecule_auto_loader.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/smiles_saver.h" #include "molecule/molfile_loader.h" #include "molecule/smiles_loader.h" #include "molecule/icm_loader.h" #include "molecule/icm_saver.h" #include "molecule/molecule_cml_saver.h" #include "molecule/molfile_saver.h" #include "layout/molecule_layout.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/bingo_oracle_context.h" #include "oracle/mango_oracle.h" #include "molecule/elements.h" #include "indigo_inchi_core.h" static void _mangoUpdateMolecule(Molecule &target, const char *options, BingoOracleContext &context) { if (strlen(options) > 0) { AromaticityOptions opt; opt.unique_dearomatization = false; if (strcasecmp(options, "aromatize") == 0) target.aromatize(opt); else if (strcasecmp(options, "dearomatize") == 0) target.dearomatize(opt); else throw BingoError("unsupport options: %s. Can be either 'aromatize' or 'dearomatize'", options); } } static OCIString * _mangoSMILES (OracleEnv &env, const Array &target_buf, const char *options, BingoOracleContext &context, bool canonical) { QS_DEF(Molecule, target); profTimerStart(tload, "smiles.load_molecule"); MoleculeAutoLoader loader(target_buf); context.setLoaderSettings(loader); loader.loadMolecule(target); profTimerStop(tload); _mangoUpdateMolecule(target, options, context); if (canonical) MoleculeAromatizer::aromatizeBonds(target, AromaticityOptions::BASIC); QS_DEF(Array, smiles); ArrayOutput out(smiles); if (canonical) { profTimerStart(tload, "smiles.cano_saver"); CanonicalSmilesSaver saver(out); saver.saveMolecule(target); } else { profTimerStart(tload, "smiles.saver"); SmilesSaver saver(out); saver.saveMolecule(target); } if (smiles.size() == 0) // Oracle would treat empty string as NULL value. // To give it non-NULL, we give it a space (which is correct SMILES) smiles.push(' '); OCIString *result = 0; env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)smiles.ptr(), smiles.size(), &result)); return result; } ORAEXT OCIString * oraMangoSMILES (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, const char *options, short options_ind, short *return_indicator) { OCIString *result = NULL; logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_indicator = OCI_IND_NULL; if (options_ind != OCI_IND_NOTNULL) options = ""; if (target_indicator == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); OracleLOB target_lob(env, target_locator); QS_DEF(Array, buf); target_lob.readAll(buf, false); result = _mangoSMILES(env, buf, options, context, false); } if (result == 0) { // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); } else *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCIString *oraMangoCanonicalSMILES (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { profTimersReset(); profTimerStart(tall, "smiles.all"); OCIString *result = NULL; logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_indicator = OCI_IND_NULL; if (target_indicator == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); OracleLOB target_lob(env, target_locator); QS_DEF(Array, buf); profTimerStart(treadlob, "smiles.read_lob"); target_lob.readAll(buf, false); profTimerStop(treadlob); result = _mangoSMILES(env, buf, "", context, true); } if (result == 0) { // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); } else *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCIString * oraMangoCheckMolecule (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCIString *result = NULL; logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_indicator = OCI_IND_NULL; if (target_indicator != OCI_IND_NOTNULL) { static const char *msg = "null molecule given"; env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)msg, strlen(msg), &result)); *return_indicator = OCI_IND_NOTNULL; } else { OracleLOB target_lob(env, target_locator); QS_DEF(Array, buf); QS_DEF(Molecule, mol); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); target_lob.readAll(buf, false); TRY_READ_TARGET_MOL { MoleculeAutoLoader loader(buf); context.setLoaderSettings(loader); loader.loadMolecule(mol); Molecule::checkForConsistency(mol); } CATCH_READ_TARGET_MOL ( OCIStringAssignText(env.envhp(), env.errhp(), (text *)e.message(), strlen(e.message()), &result); *return_indicator = OCI_IND_NOTNULL; ) catch (Exception &e) { char buf[4096]; snprintf(buf, NELEM(buf), "INTERNAL ERROR: %s", e.message()); OCIStringAssignText(env.envhp(), env.errhp(), (text *)buf, strlen(buf), &result); *return_indicator = OCI_IND_NOTNULL; } if (*return_indicator == OCI_IND_NULL) // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); } } ORABLOCK_END return result; } void _ICM (BingoOracleContext &context, OracleLOB &target_lob, int save_xyz, Array &icm) { QS_DEF(Array, target); QS_DEF(Molecule, mol); target_lob.readAll(target, false); MoleculeAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadMolecule(mol); if ((save_xyz != 0) && !mol.have_xyz) throw BingoError("molecule has no XYZ"); ArrayOutput output(icm); IcmSaver saver(output); saver.save_xyz = (save_xyz != 0); saver.saveMolecule(mol); } ORAEXT OCILobLocator *oraMangoICM (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, int save_xyz, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (target_indicator == OCI_IND_NOTNULL) { OracleLOB target_lob(env, target_locator); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, icm); _ICM(context, target_lob, save_xyz, icm); OracleLOB lob(env); lob.createTemporaryBLOB(); lob.write(0, icm); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT void oraMangoICM2 (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, OCILobLocator *result_locator, short result_indicator, int save_xyz) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (target_indicator == OCI_IND_NULL) throw BingoError("null molecule given"); if (result_indicator == OCI_IND_NULL) throw BingoError("null LOB given"); OracleLOB target_lob(env, target_locator); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, icm); _ICM(context, target_lob, save_xyz, icm); OracleLOB result_lob(env, result_locator); result_lob.write(0, icm); result_lob.trim(icm.size()); } ORABLOCK_END } ORAEXT OCILobLocator *oraMangoMolfile (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, const char *options, short options_ind, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (options_ind != OCI_IND_NOTNULL) options = ""; if (target_indicator == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); OracleLOB target_lob(env, target_locator); QS_DEF(Array, target); QS_DEF(Array, icm); QS_DEF(Molecule, mol); target_lob.readAll(target, false); MoleculeAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadMolecule(mol); _mangoUpdateMolecule(mol, options, context); if (!mol.have_xyz) { MoleculeLayout layout(mol); layout.make(); mol.clearBondDirections(); mol.stereocenters.markBonds(); mol.allene_stereo.markBonds(); } ArrayOutput output(icm); MolfileSaver saver(output); saver.saveMolecule(mol); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, icm); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT OCILobLocator *oraMangoCML (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); if (target_indicator == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); OracleLOB target_lob(env, target_locator); QS_DEF(Array, target); QS_DEF(Array, icm); QS_DEF(Molecule, mol); target_lob.readAll(target, false); MoleculeAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadMolecule(mol); if (!mol.have_xyz) { MoleculeLayout layout(mol); layout.make(); mol.clearBondDirections(); mol.stereocenters.markBonds(); mol.allene_stereo.markBonds(); } ArrayOutput output(icm); MoleculeCmlSaver saver(output); saver.saveMolecule(mol); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, icm); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT OCILobLocator * oraMangoInchi (OCIExtProcContext *ctx, OCILobLocator *target_loc, short target_ind, const char *options, short options_ind, short *return_ind) { OCILobLocator *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (options_ind != OCI_IND_NOTNULL) options = ""; if (target_ind == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); QS_DEF(Molecule, target); MoleculeAutoLoader loader(target_buf); context.setLoaderSettings(loader); loader.loadMolecule(target); QS_DEF(Array, inchi); IndigoInchi inchi_calc; inchi_calc.setOptions(options); inchi_calc.saveMoleculeIntoInchi(target, inchi); OracleLOB lob(env); lob.createTemporaryCLOB(); // Exclude terminating zero if (inchi.top() == 0) inchi.pop(); lob.write(0, inchi); lob.doNotDelete(); result = lob.get(); *return_ind = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT OCIString * oraMangoInchiKey (OCIExtProcContext *ctx, OCILobLocator *inchi_loc, short inchi_ind, short *return_ind) { OCIString *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (inchi_ind == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, inchi); OracleLOB inchi_lob(env, inchi_loc); inchi_lob.readAll(inchi, true); QS_DEF(Array, inchikey_buf); IndigoInchi::InChIKey(inchi.ptr(), inchikey_buf); env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)inchikey_buf.ptr(), inchikey_buf.size() - 1, &result)); } if (result != 0) *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCILobLocator * oraMangoFingerprint (OCIExtProcContext *ctx, OCILobLocator *target_loc, short target_ind, const char *options, short options_ind, short *return_ind) { OCILobLocator *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (options_ind != OCI_IND_NOTNULL) options = ""; if (target_ind == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); QS_DEF(Molecule, target); MoleculeAutoLoader loader(target_buf); context.setLoaderSettings(loader); loader.loadMolecule(target); MoleculeFingerprintBuilder builder(target, context.fp_parameters); builder.parseFingerprintType(options, false); builder.process(); const char* buf = (const char*)builder.get(); int buf_len = context.fp_parameters.fingerprintSize(); OracleLOB lob(env); lob.createTemporaryBLOB(); lob.write(0, buf, buf_len); lob.doNotDelete(); result = lob.get(); *return_ind = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_shadow_fetch.cpp000066400000000000000000000357561271037650300251430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ora_wrap.h" #include "base_cpp/output.h" #include "oracle/mango_shadow_fetch.h" #include "core/mango_matchers.h" #include "oracle/bingo_oracle_context.h" #include "oracle/mango_fetch_context.h" #include "base_cpp/profiling.h" IMPL_ERROR(MangoShadowFetch, "mango shadow fetch"); MangoShadowFetch::MangoShadowFetch (MangoFetchContext &context) : _context(context) { _total_count = -1; _table_name.push(0); ArrayOutput output(_table_name); output.printf(context.context().shadow_table.getName()); output.writeChar(0); ArrayOutput output2(_components_table_name); output2.printf(context.context().shadow_table.getComponentsName()); output2.writeChar(0); _executed = false; _fetch_type = 0; _processed_rows = 0; _end = false; _rowid.ptr()[0] = 0; } MangoShadowFetch::~MangoShadowFetch () { } int MangoShadowFetch::getTotalCount (OracleEnv &env) { if (_total_count < 0) { if (!OracleStatement::executeSingleInt(_total_count, env, "SELECT COUNT(*) FROM %s", _table_name.ptr())) throw Error("getTotalCount() failed"); } return _total_count; } bool MangoShadowFetch::getLastRowid (OraRowidText &id) { if (_rowid.ptr()[0] == 0) return false; memcpy(&id, &_rowid, sizeof(_rowid)); return true; } int MangoShadowFetch::countOracleBlocks (OracleEnv &env) { int res; env.dbgPrintf("countOracleBlocks\n"); if (!OracleStatement::executeSingleInt(res, env, "select blocks from user_tables where " "table_name = upper('%s')", _table_name.ptr())) return 0; return res; } int MangoShadowFetch::getIOCost (OracleEnv &env, float selectivity) { return (int)(countOracleBlocks(env) * selectivity); } bool MangoShadowFetch::end () { return _end; } float MangoShadowFetch::calcSelectivity (OracleEnv &env, int total_count) { int nrows_select_total; if (_counting_select.size() > 1) { _counting_select.push(0); OracleStatement statement(env); statement.append("%s", _counting_select.ptr()); statement.prepare(); statement.defineIntByPos(1, &nrows_select_total); if (_fetch_type == _MASS) { statement.bindFloatByName(":mass_min", &_context.mass.bottom); statement.bindFloatByName(":mass_max", &_context.mass.top); } else if (_fetch_type == _TAUTOMER) { const char *gross = _context.tautomer.getQueryGross(); statement.bindStringByName(":gross", gross, strlen(gross) + 1); } if (!statement.executeAllowNoData()) throw Error("selectivity: cannot count rows"); } else nrows_select_total = total_count; if (_processed_rows == 0) { if (nrows_select_total == 0) return 0; throw Error("selectivity: no processed rows"); } return (float)nrows_select_total * matched.size() / (total_count * _processed_rows); } void MangoShadowFetch::prepareNonSubstructure (OracleEnv &env) { env.dbgPrintf("preparing shadow table for non-substructure match\n"); _fetch_type = _NON_SUBSTRUCTURE; _need_xyz = _context.substructure.needCoords(); _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); if (_need_xyz) { _lob_cmf.reset(new OracleLOB(_env.ref())); _lob_xyz.reset(new OracleLOB(_env.ref())); _statement->append("SELECT mol_rowid, cmf, xyz FROM %s", _table_name.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_cmf.ref()); _statement->defineBlobByPos(3, _lob_xyz.ref()); } else { _lob_cmf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT mol_rowid, cmf FROM %s", _table_name.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_cmf.ref()); } _counting_select.clear(); } void MangoShadowFetch::prepareNonTautomerSubstructure (OracleEnv &env) { env.dbgPrintf("preparing shadow table for non-tautomer-substructure match\n"); _fetch_type = _NON_TAUTOMER_SUBSTRUCTURE; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _lob_cmf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT mol_rowid, cmf FROM %s", _table_name.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_cmf.ref()); _counting_select.clear(); } void MangoShadowFetch::prepareTautomer (OracleEnv &env, int right_part) { if (right_part == 1) env.dbgPrintf("preparing shadow table for tautomer match\n"); else env.dbgPrintf("preparing shadow table for non-tautomer match\n"); _fetch_type = _TAUTOMER; _right_part = right_part; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _lob_cmf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT mol_rowid, cmf FROM %s", _table_name.ptr()); if (right_part == 1) _statement->append(" WHERE gross = :gross OR gross LIKE :grossh"); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_cmf.ref()); if (right_part == 1) { ArrayOutput output(_counting_select); output.printf("SELECT COUNT(*) FROM %s WHERE gross = :gross", _table_name.ptr()); } else _counting_select.clear(); } void MangoShadowFetch::prepareExact (OracleEnv &env, int right_part) { const MangoExact & instance = _context.exact; if (right_part == 1) env.dbgPrintf("preparing shadow table for exact\n"); else env.dbgPrintf("preparing shadow table for non-exact\n"); _fetch_type = _EXACT; _right_part = right_part; _need_xyz = instance.needCoords(); _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _lob_cmf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT sh.mol_rowid, sh.cmf"); if (_need_xyz) _statement->append(", sh.xyz", _table_name.ptr()); _statement->append(" FROM %s sh", _table_name.ptr()); QS_DEF(Array, table_copies); QS_DEF(Array, where_clause); _prepareExactQueryStrings(table_copies, where_clause); _statement->append(table_copies.ptr()); _statement->append(where_clause.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_cmf.ref()); if (_need_xyz) { _lob_xyz.reset(new OracleLOB(_env.ref())); _statement->defineBlobByPos(3, _lob_xyz.ref()); } ArrayOutput output_cnt(_counting_select); output_cnt.printf("SELECT COUNT(*) FROM %s sh", _table_name.ptr()); output_cnt.printf("%s", table_copies.ptr()); output_cnt.printf("%s", where_clause.ptr()); } void MangoShadowFetch::_prepareExactQueryStrings (Array &table_copies, Array &where_clause) { const MangoExact & instance = _context.exact; const MangoExact::Hash &hash = instance.getQueryHash(); ArrayOutput output_tables(table_copies); if (_right_part == 1) { for (int i = 0; i < hash.size(); i++) output_tables.printf(", %s t%d", _components_table_name.ptr(), i); } output_tables.writeChar(0); // Create complex WHERE clause ArrayOutput output(where_clause); if (_right_part == 1) { bool where_was_added = false; if (hash.size() > 0) { output.printf(" WHERE "); where_was_added = true; // molecule ids must be same output.printf("sh.mol_rowid = t0.mol_rowid and "); for (int i = 1; i < hash.size(); i++) output.printf("t%d.mol_rowid = t%d.mol_rowid and ", i - 1, i); // query components must match target components for (int i = 0; i < hash.size(); i++) output.printf("t%d.hash = '%08X' and ", i, hash[i].hash); // components count mast must target components count const char *rel; if (instance.needComponentMatching()) rel = ">="; else rel = "="; for (int i = 0; i < hash.size(); i++) { if (i != 0) output.printf("and "); output.printf("t%d.count %s %d ", i, rel, hash[i].count); } } if (!instance.needComponentMatching()) { if (!where_was_added) output.printf(" WHERE "); else output.printf("and "); // There must be no other components in target int query_fragments_count = 0; for (int i = 0; i < hash.size(); i++) query_fragments_count += hash[i].count; output.printf("sh.fragments = %d", query_fragments_count); } } output.writeChar(0); } void MangoShadowFetch::prepareGross (OracleEnv &env, int right_part) { MangoGross & instance = _context.gross; env.dbgPrintf("preparing shadow table for gross formula match\n"); _fetch_type = _GROSS; _right_part = right_part; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _statement->append("SELECT mol_rowid, gross FROM %s ", _table_name.ptr()); if (*instance.getConditions() != 0 && right_part == 1) _statement->append("WHERE %s", instance.getConditions()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineStringByPos(2, _gross, sizeof(_gross)); ArrayOutput output(_counting_select); output.printf("SELECT COUNT(*) FROM %s WHERE %s", _table_name.ptr(), instance.getConditions()); } void MangoShadowFetch::prepareMass (OracleEnv &env) { env.dbgPrintf("preparing shadow table for molecular mass match\n"); QS_DEF(Array, where); { ArrayOutput where_out(where); where_out.printf(""); where_out.writeChar(0); } _fetch_type = _MASS; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _statement->append("SELECT mol_rowid FROM %s WHERE mass >= :mass_min AND mass <= :mass_max", _table_name.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); ArrayOutput output(_counting_select); output.printf("SELECT COUNT(*) FROM %s WHERE WHERE mass >= :mass_min AND mass <= :mass_max", _table_name.ptr()); } void MangoShadowFetch::fetch (OracleEnv &env, int maxrows) { matched.clear(); if (_statement.get() == 0) return; if (maxrows < 1 || _end) return; env.dbgPrintf("fetching up to %d rows using shadowtable... ", maxrows); while (matched.size() < maxrows) { bool fetch_res; if (_fetch_type == _MASS) { _statement->bindFloatByName(":mass_min", &_context.mass.bottom); _statement->bindFloatByName(":mass_max", &_context.mass.top); } else if (_fetch_type == _TAUTOMER && _right_part == 1) { const char *gross = _context.tautomer.getQueryGross(); _statement->bindStringByName(":gross", gross, strlen(gross) + 1); QS_DEF(Array, grossh); grossh.readString(gross, false); grossh.appendString(" H%", true); _statement->bindStringByName(":grossh", grossh.ptr(), grossh.size()); } if (!_executed) { fetch_res = _statement->executeAllowNoData(); _executed = true; } else fetch_res = _statement->fetch(); if (!fetch_res) { _end = true; break; } bool have_match = false; if (_fetch_type == _NON_SUBSTRUCTURE) { MangoSubstructure &instance = _context.substructure; QS_DEF(Array, cmf); _lob_cmf->readAll(cmf, false); if (_need_xyz) { if (_statement->gotNull(3)) // xyz == NULL? have_match = true; else { QS_DEF(Array, xyz); _lob_xyz->readAll(xyz, false); if (!instance.matchBinary(cmf, &xyz)) have_match = true; } } else if (!instance.matchBinary(cmf, 0)) have_match = true; } else if (_fetch_type == _NON_TAUTOMER_SUBSTRUCTURE) { MangoTautomer &instance = _context.tautomer; QS_DEF(Array, cmf); _lob_cmf->readAll(cmf, false); if (!instance.matchBinary(cmf)) have_match = true; } else if (_fetch_type == _TAUTOMER) { MangoTautomer &instance = _context.tautomer; QS_DEF(Array, cmf); _lob_cmf->readAll(cmf, false); if (instance.matchBinary(cmf) == (_right_part == 1)) have_match = true; } else if (_fetch_type == _EXACT) { MangoExact &instance = _context.exact; QS_DEF(Array, cmf); profTimerStart(tlobread, "exact.lobread"); _lob_cmf->readAll(cmf, false); profTimerStop(tlobread); if (_need_xyz) { if (_statement->gotNull(3)) // xyz == NULL? have_match = (_right_part == 0); else { QS_DEF(Array, xyz); profTimerStart(txyzlobread, "exact.xyzlobread"); _lob_xyz->readAll(xyz, false); profTimerStop(txyzlobread); profTimerStart(tmatch, "exact.match"); if (instance.matchBinary(cmf, &xyz) == (_right_part == 1)) have_match = true; } } else { profTimerStart(tmatch, "exact.match"); if (instance.matchBinary(cmf, 0) == (_right_part == 1)) have_match = true; } } else if (_fetch_type == _GROSS) { MangoGross &instance = _context.gross; if (instance.checkGross(_gross) == (_right_part == 1)) have_match = true; } else if (_fetch_type == _MASS) { have_match = true; } else throw Error("unexpected fetch type %d", _fetch_type); if (have_match) matched.add(_rowid); _processed_rows++; } env.dbgPrintf("fetched %d\n", matched.size()); return; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_shadow_fetch.h000066400000000000000000000051361271037650300245750ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_shadow_fetch__ #define __mango_shadow_fetch__ #include "base_cpp/auto_ptr.h" #include "oracle/bingo_fetch_engine.h" #include "oracle/mango_shadow_table.h" using namespace indigo; class MangoFetchContext; class MangoShadowFetch : public BingoFetchEngine { public: MangoShadowFetch (MangoFetchContext &context); virtual ~MangoShadowFetch (); virtual float calcSelectivity (OracleEnv &env, int total_count); virtual void fetch (OracleEnv &env, int maxrows); virtual bool end (); virtual int getIOCost (OracleEnv &env, float selectivity); virtual int getTotalCount (OracleEnv &env); virtual bool getLastRowid (OraRowidText &id); int countOracleBlocks (OracleEnv &env); void prepareNonSubstructure (OracleEnv &env); void prepareNonTautomerSubstructure (OracleEnv &env); void prepareTautomer (OracleEnv &env, int right_part); void prepareExact (OracleEnv &env, int right_part); void prepareGross (OracleEnv &env, int right_part); void prepareMass (OracleEnv &env); DECL_ERROR; protected: enum { _NON_SUBSTRUCTURE = 1, _NON_TAUTOMER_SUBSTRUCTURE = 2, _TAUTOMER = 3, _EXACT = 4, _GROSS = 5, _MASS = 6 }; void _prepareExactQueryStrings (Array &table_copies, Array &where_clause); MangoFetchContext &_context; Array _table_name; Array _components_table_name; int _total_count; Array _counting_select; int _processed_rows; bool _end; AutoPtr _env; AutoPtr _statement; AutoPtr _lob_cmf; AutoPtr _lob_xyz; bool _executed; int _fetch_type; char _gross[1024]; OraRowidText _rowid; bool _need_xyz; int _right_part; }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_shadow_table.cpp000066400000000000000000000336331271037650300251310ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/mango_shadow_table.h" #include "base_cpp/profiling.h" #include "molecule/elements.h" #include "core/mango_index.h" IMPL_ERROR(MangoShadowTable, "shadow table"); MangoShadowTable::MangoShadowTable (int context_id) { _table_name.push(0); ArrayOutput output(_table_name); output.printf("SHADOW_%d", context_id); output.writeChar(0); ArrayOutput output2(_components_table_name); output2.printf("HASHES_%d", context_id); output2.writeChar(0); _main_table_statement_count = 0; _components_table_statement_count = 0; _commit_main = false; _commit_comp = false; } MangoShadowTable::~MangoShadowTable () { } void MangoShadowTable::addMolecule (OracleEnv &env, const char *rowid, int blockno, int offset, const char *data_cmf, int len_cmf, const char *data_xyz, int len_xyz, const MangoExact::Hash &hash, const char *gross, const Array &counters, float molecular_mass, const char *fp_sim, bool append) { if (_main_table_statement_count >= 4096) _flushMain(env); int i; if (_main_table_statement.get() == 0) { _main_table_statement.create(env); _main_table_statement_count = 0; /* APPEND_VALUES hint causes Oracle internal error: OCI call error: ORA-00600: internal error code, arguments: [koklGetLocAndFlag1], [], [], [], [], [], [], [], [], [], [], [] But it should be faster. Replace 'flase' with append when Oracle bug is fixed. The error appears on Oracle 11.2 Win 7 x64 */ _main_table_statement->append("INSERT %s INTO %s VALUES (" ":rid, :blockno, :offset, :gross, :cmf, :xyz, :mass, :fragcount", false ? "/*+ APPEND_VALUES */" : "", _table_name.ptr()); if (append) _commit_main = true; for (i = 0; i < counters.size(); i++) { char name[10] = {0}; snprintf(name, sizeof(name), ":counter%d", i); _main_table_statement->append(", %s", name); } _main_table_statement->append(")"); } _pending_rid.push(); strncpy(_pending_rid.top(), rowid, 19); _pending_blockno.push(blockno); _pending_offset.push(offset); _pending_gross.push(); strncpy(_pending_gross.top(), gross, 512); _pending_mass.push(molecular_mass); _pending_cmf.push(env); _pending_cmf.top().assignBytes(data_cmf, len_cmf); _pending_xyz.push(env); if (len_xyz > 0) _pending_xyz.top().assignBytes(data_xyz, len_xyz); int fragments_count = 0; for (i = 0; i < hash.size(); i++) fragments_count += hash[i].count; _pending_fragcount.push(fragments_count); if (_pending_counters.size() != counters.size()) _pending_counters.resize(counters.size()); for (i = 0; i < counters.size(); i++) _pending_counters[i].push(counters[i]); _main_table_statement_count++; // Insert into components shadow table if (_components_table_statement_count >= 8192) _flushComponents(env); if (_components_table_statement.get() == 0) { _components_table_statement.create(env); _components_table_statement->append( "INSERT %s INTO %s VALUES (:rid, :hash, :count)", append ? "/*+ APPEND_VALUES */" : "", _components_table_name.ptr()); _components_table_statement_count = 0; if (append) _commit_comp = true; } for (int i = 0; i < hash.size(); i++) { _pending_comp_hash.push(); snprintf(_pending_comp_hash.top(), 9, "%08X", hash[i].hash); _pending_comp_rid.push(); strncpy(_pending_comp_rid.top(), rowid, 19); _pending_comp_count.push(hash[i].count); _components_table_statement_count++; } } void MangoShadowTable::flush (OracleEnv &env) { _flushMain(env); _flushComponents(env); } void MangoShadowTable::_flushMain (OracleEnv &env) { // Flusing data to the main table if (_main_table_statement.get() != 0) { if (_main_table_statement_count != 0) { int i; profTimerStart(tmain, "moleculeIndex.register_shadow_main"); _main_table_statement->prepare(); _main_table_statement->bindStringByName(":rid", _pending_rid[0], 19); _main_table_statement->bindIntByName(":blockno", _pending_blockno.ptr()); _main_table_statement->bindIntByName(":offset", _pending_offset.ptr()); _main_table_statement->bindStringByName(":gross", _pending_gross[0], 512); QS_DEF(Array, cmf); QS_DEF(Array, xyz); QS_DEF(Array, xyz_ind); int maxallocsize_cmf = 0; int maxallocsize_xyz = 0; cmf.clear(); for (i = 0; i < _pending_cmf.size(); i++) { int allocsize = _pending_cmf[i].getAllocSize(); if (allocsize > maxallocsize_cmf) maxallocsize_cmf = allocsize; } cmf.clear_resize((maxallocsize_cmf + 4) * _pending_cmf.size()); cmf.zerofill(); for (i = 0; i < _pending_cmf.size(); i++) memcpy(cmf.ptr() + i * (maxallocsize_cmf + 4), _pending_cmf[i].get(), _pending_cmf[i].getAllocSize() + 4); xyz.clear(); xyz_ind.clear(); for (i = 0; i < _pending_xyz.size(); i++) { if (_pending_xyz[i].get() == 0) continue; int allocsize = _pending_xyz[i].getAllocSize(); if (allocsize > maxallocsize_xyz) maxallocsize_xyz = allocsize; } if (maxallocsize_xyz == 0) maxallocsize_xyz = 8; // or we get ORA-01459 xyz.clear_resize((maxallocsize_xyz + 4) * _pending_xyz.size()); xyz.zerofill(); for (i = 0; i < _pending_xyz.size(); i++) { if (_pending_xyz[i].get() != 0) { memcpy(xyz.ptr() + i * (maxallocsize_xyz + 4), _pending_xyz[i].get(), _pending_xyz[i].getAllocSize() + 4); xyz_ind.push(0); // OCI_IND_NOTNULL } else xyz_ind.push(-1); // OCI_IND_NULL } _main_table_statement->bindRawPtrByName(":cmf", (OCIRaw *)cmf.ptr(), maxallocsize_cmf, 0); _main_table_statement->bindRawPtrByName(":xyz", (OCIRaw *)xyz.ptr(), maxallocsize_xyz, xyz_ind.ptr()); _main_table_statement->bindFloatByName(":mass", _pending_mass.ptr()); _main_table_statement->bindIntByName(":fragcount", _pending_fragcount.ptr()); for (i = 0; i < _pending_counters.size(); i++) { char name[10] = {0}; snprintf(name, sizeof(name), ":counter%d", i); _main_table_statement->bindIntByName(name, _pending_counters[i].ptr()); } _main_table_statement->executeMultiple(_main_table_statement_count); if (_commit_main) { OracleStatement::executeSingle(env, "COMMIT"); _commit_main = false; } profTimerStop(tmain); _main_table_statement.free(); _pending_rid.clear(); _pending_blockno.clear(); _pending_offset.clear(); _pending_gross.clear(); _pending_cmf.clear(); _pending_xyz.clear(); _pending_mass.clear(); _pending_fragcount.clear(); for (i = 0; i < _pending_counters.size(); i++) _pending_counters[i].clear(); } _main_table_statement_count = 0; } } void MangoShadowTable::_flushComponents (OracleEnv &env) { // Flusing components table if (_components_table_statement.get() != 0) { if (_components_table_statement_count != 0) { profTimerStart(tcomp, "moleculeIndex.register_shadow_comp"); _components_table_statement->prepare(); _components_table_statement->bindIntByName(":count", _pending_comp_count.ptr()); _components_table_statement->bindStringByName(":rid", _pending_comp_rid[0], 19); _components_table_statement->bindStringByName(":hash", _pending_comp_hash[0], 9); _components_table_statement->executeMultiple(_components_table_statement_count); if (_commit_comp) { OracleStatement::executeSingle(env, "COMMIT"); _commit_comp = false; } _pending_comp_count.clear(); _pending_comp_rid.clear(); _pending_comp_hash.clear(); profTimerStop(tcomp); } _components_table_statement.free(); _components_table_statement_count = 0; } } void MangoShadowTable::addMolecule (OracleEnv &env, const MangoIndex &index, const char *rowid, int blockno, int offset, bool append) { addMolecule(env, rowid, blockno, offset, index.getCmf().ptr(), index.getCmf().size(), index.getXyz().ptr(), index.getXyz().size(), index.getHash(), index.getGrossString(), index.getCountedElements(), index.getMolecularMass(), index.getFingerprint_Sim_Str(), append); } void MangoShadowTable::create (OracleEnv &env) { OracleStatement s1(env); const char *mi = _table_name.ptr(); int i; s1.append("CREATE TABLE %s " "(mol_rowid VARCHAR2(18), blockno NUMBER, offset NUMBER, " " gross VARCHAR2(500), cmf BLOB, xyz BLOB, MASS number, fragments NUMBER", mi); for (i = 0; i < (int)NELEM(MangoIndex::counted_elements); i++) s1.append(", cnt_%s INTEGER", Element::toString(MangoIndex::counted_elements[i])); s1.append(") NOLOGGING"); s1.append(" LOB(cmf) STORE AS (NOCACHE NOLOGGING PCTVERSION 0)" " LOB(xyz) STORE AS (NOCACHE NOLOGGING PCTVERSION 0)"); s1.prepare(); s1.execute(); // Create shadow table for molecule components const char *cmi = _components_table_name.ptr(); OracleStatement::executeSingle(env, "CREATE TABLE %s " " (mol_rowid VARCHAR2(18), hash VARCHAR2(8), count INT) NOLOGGING", cmi); } void MangoShadowTable::createIndices (OracleEnv &env) { const char *mi = _table_name.ptr(); OracleStatement::executeSingle(env, "CREATE UNIQUE INDEX %s_rid ON %s(mol_rowid) NOLOGGING", mi, mi); OracleStatement::executeSingle(env, "CREATE INDEX %s_gross ON %s(gross) NOLOGGING", mi, mi); OracleStatement::executeSingle(env, "CREATE INDEX %s_mass ON %s(mass) NOLOGGING", mi, mi); if (NELEM(MangoIndex::counted_elements) > 0) { OracleStatement s2(env); int i; s2.append("CREATE INDEX %s_CNT ON %s(cnt_%s", mi, mi, Element::toString(MangoIndex::counted_elements[0])); for (i = 1; i < NELEM(MangoIndex::counted_elements); i++) s2.append(", cnt_%s", Element::toString(MangoIndex::counted_elements[i])); s2.append(") NOLOGGING"); s2.prepare(); s2.execute(); } const char *cmi = _components_table_name.ptr(); OracleStatement::executeSingle(env, "CREATE INDEX %s_rid ON %s(mol_rowid) NOLOGGING", cmi, cmi); OracleStatement::executeSingle(env, "CREATE INDEX %s_hash ON %s(hash) NOLOGGING", cmi, cmi); OracleStatement::executeSingle(env, "CREATE INDEX %s_count ON %s(count) NOLOGGING", cmi, cmi); } void MangoShadowTable::drop (OracleEnv &env) { OracleStatement::executeSingle(env, "BEGIN DropTable('%s'); DropTable('%s'); END;", _table_name.ptr(), _components_table_name.ptr()); } void MangoShadowTable::truncate (OracleEnv &env) { OracleStatement::executeSingle(env, "TRUNCATE TABLE %s", _table_name.ptr()); OracleStatement::executeSingle(env, "TRUNCATE TABLE %s", _components_table_name.ptr()); } void MangoShadowTable::analyze (OracleEnv &env) { env.dbgPrintf("analyzing shadow table\n"); OracleStatement::executeSingle(env, "ANALYZE TABLE %s ESTIMATE STATISTICS", _table_name.ptr()); OracleStatement::executeSingle(env, "ANALYZE TABLE %s ESTIMATE STATISTICS", _components_table_name.ptr()); } bool MangoShadowTable::getXyz (OracleEnv &env, const char *rowid, Array &xyz) { if (!OracleStatement::executeSingleBlob(xyz, env, "SELECT xyz FROM %s where mol_rowid='%s'", _table_name.ptr(), rowid)) return false; return true; } const char * MangoShadowTable::getName () { return _table_name.ptr(); } const char * MangoShadowTable::getComponentsName () { return _components_table_name.ptr(); } bool MangoShadowTable::getMoleculeLocation (OracleEnv &env, const char *rowid, int &blockno, int &offset) { OracleStatement statement(env); statement.append("SELECT blockno, offset FROM %s WHERE mol_rowid = :rid", _table_name.ptr()); statement.prepare(); statement.bindStringByName(":rid", rowid, strlen(rowid) + 1); statement.defineIntByPos(1, &blockno); statement.defineIntByPos(2, &offset); return statement.executeAllowNoData(); } void MangoShadowTable::deleteMolecule (OracleEnv &env, const char *rowid) { OracleStatement::executeSingle_BindString(env, ":rid", rowid, "DELETE FROM %s WHERE mol_rowid = :rid", _table_name.ptr()); OracleStatement::executeSingle_BindString(env, ":rid", rowid, "DELETE FROM %s WHERE mol_rowid = :rid", _components_table_name.ptr()); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/mango_shadow_table.h000066400000000000000000000057741271037650300246030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __mango_shadow_table__ #define __mango_shadow_table__ #include "base_cpp/tlscont.h" #include "oracle/ora_wrap.h" #include "oracle/bingo_fetch_engine.h" #include "core/mango_matchers.h" #include "base_cpp/queue.h" using namespace indigo; class MangoIndex; class MangoShadowTable { public: MangoShadowTable (int context_id); virtual ~MangoShadowTable (); bool getXyz (OracleEnv &env, const char *rowid, Array &xyz); void drop (OracleEnv &env); void truncate (OracleEnv &env); void create (OracleEnv &env); void createIndices (OracleEnv &env); void addMolecule (OracleEnv &env, const MangoIndex &index, const char *rowid, int blockno, int offset, bool append); bool getMoleculeLocation (OracleEnv &env, const char *rowid, int &blockno, int &offset); void deleteMolecule (OracleEnv &env, const char *rowid); void addMolecule (OracleEnv &env, const char *rowid, int blockno, int offset, const char *data_cmf, int len_cmf, const char *data_xyz, int len_xyz, const MangoExact::Hash &hash, const char *gross, const Array &counters, float molecular_mass, const char *fp_ord, bool append); void flush (OracleEnv &env); void analyze (OracleEnv &env); const char * getName (); const char * getComponentsName (); DECL_ERROR; protected: Array _table_name, _components_table_name; void _flushMain (OracleEnv &env); void _flushComponents (OracleEnv &env); Obj _main_table_statement; Obj _components_table_statement; int _main_table_statement_count; int _components_table_statement_count; Array _pending_rid; Array _pending_blockno; Array _pending_offset; Array _pending_gross; ObjArray _pending_cmf; ObjArray _pending_xyz; Array _pending_mass; Array _pending_fragcount; ObjArray< Array > _pending_counters; Array _pending_comp_rid; Array _pending_comp_hash; Array _pending_comp_count; bool _commit_main; bool _commit_comp; private: MangoShadowTable (MangoShadowTable &); // no implicit copy }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_fast_index.cpp000066400000000000000000000130351271037650300246300ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ringo_fast_index.h" #include "base_cpp/profiling.h" #include "core/ringo_matchers.h" #include "oracle/ringo_oracle.h" #include "oracle/ringo_fetch_context.h" #include "oracle/bingo_oracle_context.h" #include "oracle/rowid_loader.h" IMPL_ERROR(RingoFastIndex, "ringo fast fetch"); RingoFastIndex::RingoFastIndex (RingoFetchContext &context) : _context(context) { _fetch_type = 0; _last_id = -1; } RingoFastIndex::~RingoFastIndex () { } void RingoFastIndex::_decompressRowid (const Array &stored, OraRowidText &rid) { BufferScanner scanner(stored.ptr() + 2, stored[1]); RowIDLoader loader(_context.context().context().rid_dict, scanner); QS_DEF(Array, rowid); loader.loadRowID(rowid); if (rowid.size() != 18) throw Error("rowid size=%d?", rowid.size()); memcpy(rid.ptr(), rowid.ptr(), 18); rid.ptr()[18] = 0; } void RingoFastIndex::_match (OracleEnv &env, int idx) { _last_id = idx; BingoStorage &storage = this->_context.context().context().storage; QS_DEF(Array, stored); storage.get(idx, stored); if (stored[0] != 0) return; // reaction was removed from index BufferScanner scanner(stored); scanner.skip(1); // skip the deletion mark scanner.skip(scanner.readByte()); // skip the compessed rowid profTimerStart(tall, "match"); bool res = _context.substructure.matchBinary(scanner); profTimerStop(tall); if (res) { OraRowidText & rid = matched.at(matched.add()); _decompressRowid(stored, rid); profIncTimer("match.found", profTimerGetTime(tall)); _matched++; } else { profIncTimer("match.not_found", profTimerGetTime(tall)); _unmatched++; } } void RingoFastIndex::fetch (OracleEnv &env, int max_matches) { env.dbgPrintf("requested %d hits\n", max_matches); matched.clear(); BingoFingerprints &fingerprints = _context.context().fingerprints; if (_fetch_type == _SUBSTRUCTURE) { if (fingerprints.ableToScreen(_screening)) { while (matched.size() < max_matches) { if (_screening.passed.size() > 0) { int idx = _screening.passed.begin(); _match(env, _screening.passed.at(idx)); _screening.passed.remove(idx); continue; } if (fingerprints.screenPart_Init(env, _screening)) { while (fingerprints.screenPart_Next(env, _screening)) ; fingerprints.screenPart_End(env, _screening); _unmatched += _screening.block->used - _screening.passed.size(); } else { env.dbgPrintfTS("screening ended\n"); break; } _screening.items_passed += _screening.passed.size(); env.dbgPrintfTS("%d reactions passed screening\n", _screening.passed.size()); } } else { while (matched.size() < max_matches && _cur_idx < _context.context().context().storage.count()) _match(env, _cur_idx++); env.dbgPrintfTS("%d reactions matched\n", matched.size()); } } else throw Error("unexpected fetch type: %d", _fetch_type); } void RingoFastIndex::prepareSubstructure (OracleEnv &env) { env.dbgPrintf("preparing fastindex for reaction substructure search\n"); _context.context().context().storage.validate(env); _context.context().fingerprints.validate(env); _context.context().fingerprints.screenInit(_context.substructure.getQueryFingerprint(), _screening); _fetch_type = _SUBSTRUCTURE; _cur_idx = 0; _matched = 0; _unmatched = 0; } float RingoFastIndex::calcSelectivity (OracleEnv &env, int total_count) { if (_matched + _unmatched == 0) throw Error("calcSelectivity() called before fetch()"); BingoFingerprints &fingerprints = _context.context().fingerprints; if (fingerprints.ableToScreen(_screening)) { return (float)_matched * _screening.items_passed / (_screening.items_read * (_matched + _unmatched)); } else { if (_matched == 0) return 0; return (float)_matched / (_matched + _unmatched); } } int RingoFastIndex::getIOCost (OracleEnv &env, float selectivity) { BingoFingerprints &fingerprints = _context.context().fingerprints; int blocks = fingerprints.countOracleBlocks(env); float ratio = fingerprints.queryOnesRatio(_screening); return (int)(blocks * ratio); } bool RingoFastIndex::getLastRowid (OraRowidText &id) { if (_last_id < 0) return false; BingoStorage &storage = this->_context.context().context().storage; QS_DEF(Array, stored); storage.get(_last_id, stored); _decompressRowid(stored, id); return true; } int RingoFastIndex::getTotalCount (OracleEnv &env) { return _context.context().fingerprints.getTotalCount(env); } bool RingoFastIndex::end () { return false; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_fast_index.h000066400000000000000000000033361271037650300243000ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_fast_index__ #define __ringo_fast_index__ #include "bingo_fetch_engine.h" #include "bingo_fingerprints.h" using namespace indigo; class RingoFetchContext; class RingoFastIndex : public BingoFetchEngine { public: explicit RingoFastIndex (RingoFetchContext &context); virtual ~RingoFastIndex (); void prepareSubstructure (OracleEnv &env); virtual void fetch (OracleEnv &env, int maxrows); virtual bool end (); virtual float calcSelectivity (OracleEnv &env, int total_count); virtual int getIOCost (OracleEnv &env, float selectivity); virtual bool getLastRowid (OraRowidText &id); int getTotalCount (OracleEnv &env); DECL_ERROR; protected: enum { _SUBSTRUCTURE = 1 }; RingoFetchContext &_context; int _fetch_type; int _cur_idx; int _matched; int _unmatched; int _last_id; BingoFingerprints::Screening _screening; void _match (OracleEnv &env, int idx); void _decompressRowid (const Array &stored, OraRowidText &rid); private: RingoFastIndex (const RingoFastIndex &); // noimplicitcopy }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_fetch_context.cpp000066400000000000000000000067311271037650300253460ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ringo_fetch_context.h" #include "core/ringo_matchers.h" #include "core/bingo_context.h" #include "oracle/bingo_oracle_context.h" TL_DEF(RingoFetchContext, PtrArray, _instances); OsLock RingoFetchContext::_instances_lock; IMPL_ERROR(RingoFetchContext, "ringo fetch context"); RingoFetchContext::RingoFetchContext (int id_, RingoOracleContext &context, const Array &query_id) : substructure(context.context()), exact(context.context()), _context(context) { id = id_; context_id = context.context().id; _query_id.copy(query_id); fresh = false; fetch_engine = 0; shadow_fetch.reset(new RingoShadowFetch(*this)); fast_index.reset(new RingoFastIndex(*this)); } RingoFetchContext & RingoFetchContext::get (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) return *_instances[i]; throw Error("context #%d not found", id); } RingoFetchContext & RingoFetchContext::create (RingoOracleContext &context, const Array &query_id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int id = 1; for (int i = 0; i < _instances.size(); i++) if (_instances[i]->id >= id) id = _instances[i]->id + 1; AutoPtr new_context(new RingoFetchContext(id, context, query_id)); const BingoOracleContext &boc = context.context(); new_context->id = id; _instances.add(new_context.release()); return *_instances.top(); } RingoFetchContext * RingoFetchContext::findFresh (int context_id, const Array &query_id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) { RingoFetchContext *instance = _instances[i]; if (!instance->fresh) continue; if (instance->context_id != context_id) continue; if (instance->_query_id.memcmp(query_id) != 0) continue; return instance; } return 0; } void RingoFetchContext::remove (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = 0; i < _instances.size(); i++) if (_instances[i]->id == id) break; if (i == _instances.size()) throw Error("remove(): context #%d not found", id); _instances.remove(i); } void RingoFetchContext::removeByContextID (int id) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); int i; for (i = _instances.size() - 1; i >= 0; i--) if (_instances[i]->context_id == id) _instances.remove(i); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_fetch_context.h000066400000000000000000000036341271037650300250120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_fetch_context__ #define __ringo_fetch_context__ #include "core/ringo_matchers.h" #include "base_cpp/auto_ptr.h" #include "oracle/ringo_fast_index.h" #include "oracle/ringo_shadow_fetch.h" #include "oracle/ringo_oracle.h" using namespace indigo; namespace ingido { class RingoShadowFetch; } class RingoFetchContext { public: RingoFetchContext (int id, RingoOracleContext &context, const Array &query_id); AutoPtr fast_index; AutoPtr shadow_fetch; BingoFetchEngine *fetch_engine; RingoSubstructure substructure; RingoExact exact; int id; int context_id; bool fresh; // 'true' after selectivity calculation and before index start static RingoFetchContext & create (RingoOracleContext &context, const Array &query_id); static RingoFetchContext & get (int id); static RingoFetchContext * findFresh (int context_id, const Array &query_id); static void remove (int id); static void removeByContextID (int id); inline RingoOracleContext & context () {return _context;} DECL_ERROR; protected: Array _query_id; RingoOracleContext & _context; TL_DECL(PtrArray, _instances); static OsLock _instances_lock; }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_operators.cpp000066400000000000000000000233271271037650300245270ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "base_cpp/tlscont.h" #include "core/ringo_matchers.h" #include "oracle/ringo_oracle.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molfile_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/reaction_auto_loader.h" #include "molecule/cmf_saver.h" #include "reaction/crf_saver.h" #include "molecule/elements.h" static OCINumber * _ringoSub (OracleEnv &env, RingoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params) { context.substructure.loadQuery(query_buf); TRY_READ_TARGET_RXN { context.substructure.loadTarget(target_buf); } CATCH_READ_TARGET_RXN(return 0) int result = context.substructure.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } ORAEXT OCINumber * oraRingoSub (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); if (params_ind != OCI_IND_NOTNULL) params = 0; RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); if (!context.substructure.parse(params)) throw BingoError("can not parse parameters: %s", params); result = _ringoSub(env, context, query_buf, target_buf, params); if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createInt(env, 0); else *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCILobLocator * oraRingoSubHi (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); if (params_ind != OCI_IND_NOTNULL) params = 0; RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); if (!context.substructure.parse(params)) throw BingoError("can not parse parameters: %s", params); context.substructure.preserve_bonds_on_highlighting = true; context.substructure.loadQuery(query_buf); context.substructure.loadTarget(target_buf); if (!context.substructure.matchLoadedTarget()) throw BingoError("SubHi: match not found"); context.substructure.getHighlightedTarget(target_buf); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind= OCI_IND_NOTNULL; return lob.get(); } ORABLOCK_END return 0; } ORAEXT OCINumber * oraRingoRSmarts (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *query, short query_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); query_buf.readString(query, false); context.substructure.loadSMARTS(query_buf); TRY_READ_TARGET_RXN { context.substructure.loadTarget(target_buf); } CATCH_READ_TARGET_RXN(return OracleExtproc::createInt(env, 0)) int match = context.substructure.matchLoadedTarget() ? 1 : 0; result = OracleExtproc::createInt(env, match); *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCILobLocator * oraRingoRSmartsHi (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, const char *query, short query_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); query_buf.readString(query, false); context.substructure.preserve_bonds_on_highlighting = true; context.substructure.loadSMARTS(query_buf); context.substructure.loadTarget(target_buf); if (!context.substructure.matchLoadedTarget()) throw BingoError("SubHi: match not found"); context.substructure.getHighlightedTarget(target_buf); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind= OCI_IND_NOTNULL; return lob.get(); } ORABLOCK_END return 0; } ORAEXT OCILobLocator * oraRingoAAM (OCIExtProcContext *ctx, OCILobLocator *target_loc, short target_ind, const char *params, short params_ind, short *return_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); *return_ind = OCI_IND_NULL; if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); if (params_ind != OCI_IND_NOTNULL) params = 0; RingoOracleContext &context = RingoOracleContext::get(env, 0, false); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); context.ringoAAM.loadReaction(target_buf); context.ringoAAM.parse(params); context.ringoAAM.getResult(target_buf); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, target_buf); lob.doNotDelete(); *return_ind = OCI_IND_NOTNULL; return lob.get(); } ORABLOCK_END return 0; } static OCINumber * _ringoExact (OracleEnv &env, RingoOracleContext &context, const Array &query_buf, const Array &target_buf, const char *params) { context.exact.setParameters(params); context.exact.loadQuery(query_buf); TRY_READ_TARGET_RXN { context.exact.loadTarget(target_buf); } CATCH_READ_TARGET_RXN(return 0) int result = context.exact.matchLoadedTarget() ? 1 : 0; return OracleExtproc::createInt(env, result); } ORAEXT OCINumber * oraRingoExact (OCIExtProcContext *ctx, int context_id, OCILobLocator *target_loc, short target_ind, OCILobLocator *query_loc, short query_ind, const char *params, short params_ind, short *return_ind) { OCINumber *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (query_ind != OCI_IND_NOTNULL) throw BingoError("Null query given"); if (target_ind != OCI_IND_NOTNULL) throw BingoError("Null target given"); if (params_ind != OCI_IND_NOTNULL) params = 0; RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_buf); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); OracleLOB query_lob(env, query_loc); target_lob.readAll(target_buf, false); query_lob.readAll(query_buf, false); result = _ringoExact(env, context, query_buf, target_buf, params); if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. result = OracleExtproc::createInt(env, 0); else *return_ind = OCI_IND_NOTNULL; } ORABLOCK_END return result; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_oracle.cpp000066400000000000000000000040721271037650300237520ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ringo_oracle.h" #include "base_cpp/output.h" #include "oracle/ringo_shadow_table.h" #include "oracle/bingo_oracle_context.h" #include "base_cpp/auto_ptr.h" const char *bad_reaction_warning = "WARNING: bad reaction: %s\n"; const char *bad_reaction_warning_rowid = "WARNING: bad reaction %s: %s\n"; RingoOracleContext::RingoOracleContext (BingoContext &context) : RingoContext(context), shadow_table(context.id), fingerprints(context.id) { } RingoOracleContext::~RingoOracleContext () { } BingoOracleContext & RingoOracleContext::context () { return (BingoOracleContext &)_context; } RingoOracleContext & RingoOracleContext::get (OracleEnv &env, int id, bool lock) { bool config_reloaded; BingoOracleContext &context = BingoOracleContext::get(env, id, lock, &config_reloaded); RingoContext *already = _get(id, context); RingoOracleContext *roc; AutoPtr res; if (already != 0) roc = (RingoOracleContext *)already; else { res.reset(new RingoOracleContext(context)); roc = res.get(); config_reloaded = true; } if (config_reloaded) { roc->fingerprints.init(context, context.fp_parameters.fingerprintSizeExtOrdSim() * 2); } if (already == 0) { OsLocker locker(_instances_lock); TL_GET(PtrArray, _instances); _instances.add(res.release()); return *(RingoOracleContext *)_instances.top(); } return *roc; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_oracle.h000066400000000000000000000060561271037650300234230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_oracle__ #define __ringo_oracle__ #include "core/ringo_context.h" #include "oracle/ringo_shadow_table.h" #include "bingo_fingerprints.h" using namespace indigo; namespace indigo { class OracleEnv; } class BingoOracleContext; class RingoOracleContext : public RingoContext { public: explicit RingoOracleContext (BingoContext &context); virtual ~RingoOracleContext (); BingoOracleContext & context (); RingoShadowTable shadow_table; BingoFingerprints fingerprints; static RingoOracleContext & get (OracleEnv &env, int id, bool lock); }; extern const char *bad_reaction_warning; extern const char *bad_reaction_warning_rowid;; #define TRY_READ_TARGET_RXN \ try { #define CATCH_READ_TARGET_RXN(action) \ } \ catch (Scanner::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (MolfileLoader::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (Element::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (Graph::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (MoleculeStereocenters::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (MoleculeCisTrans::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (RxnfileLoader::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ catch (Molecule::Error &e) { env.dbgPrintf(bad_reaction_warning, e.message()); action;} \ #define CATCH_READ_TARGET_RXN_ROWID(rowid, action) \ } \ catch (Scanner::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (MolfileLoader::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (Element::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (Graph::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (MoleculeStereocenters::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (MoleculeCisTrans::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (RxnfileLoader::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ catch (Molecule::Error &e) { env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); action;} \ #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_oracle_fetch.cpp000066400000000000000000000242431271037650300251250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "core/ringo_matchers.h" #include "oracle/ringo_fetch_context.h" #include "oracle/bingo_oracle_context.h" #include "oracle/ringo_oracle.h" static void _ringoIndexStart (OracleEnv &env, RingoFetchContext &context, const char *oper, const Array &query_buf, OCINumber *p_strt, OCINumber *p_stop, const char *params) { RingoShadowFetch &shadow_fetch = context.shadow_fetch.ref(); RingoFastIndex &fast_index = context.fast_index.ref(); if (strcasecmp(oper, "RSUB") == 0) { if (!context.substructure.parse(params)) throw BingoError("can not parse parameters: %s", params); context.substructure.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, 64); if (right == 1) { fast_index.prepareSubstructure(env); context.fetch_engine = &fast_index; } else // right == 0 { shadow_fetch.prepareNonSubstructure(env); context.fetch_engine = &shadow_fetch; } } else if (strcasecmp(oper, "RSMARTS") == 0) { context.substructure.loadSMARTS(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, 64); if (right == 1) { fast_index.prepareSubstructure(env); context.fetch_engine = &fast_index; } else // right == 0 { shadow_fetch.prepareNonSubstructure(env); context.fetch_engine = &shadow_fetch; } } else if (strcasecmp(oper, "REXACT") == 0) { context.exact.setParameters(params); context.exact.loadQuery(query_buf); int right = bingoGetExactRightPart(env, p_strt, p_stop, 64); shadow_fetch.prepareExact(env, right); context.fetch_engine = &shadow_fetch; } else throw BingoError("unknown operator: %s", oper); } ORAEXT int oraRingoIndexStart (OCIExtProcContext *ctx, int context_id, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, const char *params, short params_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintf("IndexStart... "); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); QS_DEF(Array, query_id); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, 0, params, query_id); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); RingoFetchContext *fetch_context = RingoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { fetch_context = &RingoFetchContext::create(context, query_id); _ringoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, params); env.dbgPrintf("created fetcher #%d\n", fetch_context->id); } else { fetch_context->fresh = false; env.dbgPrintf("found fresh fetcher #%d\n", fetch_context->id); } return fetch_context->id; } ORABLOCK_END return -1; } ORAEXT int oraRingoIndexFetch (OCIExtProcContext *ctx, int fetch_id, int maxrows, OCIArray **array, short array_indicator) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); RingoFetchContext &context = RingoFetchContext::get(fetch_id); if (context.fetch_engine == 0) throw BingoError("fetch_engine = 0 in oraRingoIndexFetch()"); BingoFetchEngine &fetch_engine = *context.fetch_engine; ORA_TRY_FETCH_BEGIN { if (maxrows > 100) maxrows = 100; // can have fetched rowid-s from the selectivity computation phase maxrows -= bingoPopRowidsToArray(env, fetch_engine.matched, maxrows, *array); if (maxrows > 0 && !fetch_engine.end()) { env.dbgPrintf("[fetcher #%d] ", context.id); fetch_engine.fetch(env, maxrows); maxrows -= bingoPopRowidsToArray(env, fetch_engine.matched, maxrows, *array); } return fetch_engine.end() ? 0 : 1; } ORA_TRY_FETCH_END } ORABLOCK_END return 0; } ORAEXT void oraRingoIndexClose (OCIExtProcContext *ctx, int fetch_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("closing fetch #%d\n", fetch_id); RingoFetchContext::remove(fetch_id); } ORABLOCK_END } ORAEXT OCINumber * oraRingoIndexSelectivity (OCIExtProcContext *ctx, int context_id, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, const char *params, short params_ind, short *return_ind) { *return_ind = OCI_IND_NULL; ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintf("IndexSelectivity... "); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); QS_DEF(Array, query_id); QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, 0, params, query_id); RingoFetchContext * fetch_context = RingoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { fetch_context = &RingoFetchContext::create(context, query_id); env.dbgPrintf("created fresh fetcher #%d\n", fetch_context->id); } else env.dbgPrintf("found fresh fetcher #%d\n", fetch_context->id); _ringoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, params); env.dbgPrintf("[fetcher #%d] ", fetch_context->id); fetch_context->fetch_engine->fetch(env, 100); fetch_context->fresh = true; float res = fetch_context->fetch_engine->calcSelectivity(env, context.fingerprints.getTotalCount(env)); env.dbgPrintfTS("calculated selectivity = %.2f\n", res); OCINumber *result = OracleExtproc::createDouble(env, (double)res); *return_ind = OCI_IND_NOTNULL; return result; } ORABLOCK_END return 0; } ORAEXT void oraRingoIndexCost (OCIExtProcContext *ctx, int context_id, OCINumber *p_sel, short sel_ind, const char *oper, short oper_ind, OCILobLocator *query_loc, short query_ind, OCINumber *p_strt, short strt_ind, OCINumber *p_stop, short stop_ind, const char *params, short params_ind, int *iocost, int *cpucost) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("IndexCost... "); if (sel_ind != OCI_IND_NOTNULL) throw BingoError("null selectivity given"); if (oper_ind != OCI_IND_NOTNULL) throw BingoError("null operator given"); if (query_ind != OCI_IND_NOTNULL) throw BingoError("null query given"); if (params_ind != OCI_IND_NOTNULL) params = 0; if (strt_ind != OCI_IND_NOTNULL) p_strt = 0; if (stop_ind != OCI_IND_NOTNULL) p_stop = 0; float sel = OracleUtil::numberToFloat(env, p_sel); QS_DEF(Array, query_buf); OracleLOB query_lob(env, query_loc); query_lob.readAll(query_buf, false); QS_DEF(Array, query_id); bingoBuildQueryID(env, oper, query_buf, p_strt, p_stop, 0, params, query_id); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); RingoFetchContext *fetch_context = RingoFetchContext::findFresh(context_id, query_id); if (fetch_context == 0) { fetch_context = &RingoFetchContext::create(context, query_id); _ringoIndexStart(env, *fetch_context, oper, query_buf, p_strt, p_stop, params); fetch_context->fresh = true; env.dbgPrintf("created fresh fetcher #%d\n", fetch_context->id); } else env.dbgPrintf("found fresh fetcher #%d\n", fetch_context->id); *iocost = fetch_context->fetch_engine->getIOCost(env, sel); *cpucost = (int)(context.fingerprints.getTotalCount(env) * sel); env.dbgPrintfTS("calculated iocost = %d, cpucost = %d\n", *iocost, *cpucost); } ORABLOCK_END } ORAEXT void oraRingoCollectStatistics (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); RingoOracleContext & context = RingoOracleContext::get(env, context_id, false); context.shadow_table.analyze(env); } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_oracle_index.cpp000066400000000000000000000271411271037650300251430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/ringo_oracle.h" #include "oracle/bingo_fingerprints.h" #include "oracle/bingo_oracle_context.h" #include "base_cpp/output.h" #include "molecule/cmf_saver.h" #include "reaction/crf_saver.h" #include "molecule/molfile_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/reaction_auto_loader.h" #include "oracle/rowid_saver.h" #include "molecule/elements.h" #include "base_cpp/auto_ptr.h" #include "oracle/ringo_fetch_context.h" bool _ringoRegisterReaction (OracleEnv &env, const char *rowid, const Array &reaction_buf, RingoOracleContext &context, RingoIndex &index, BingoFingerprints &fingerprints) { QS_DEF(Array, data); QS_DEF(Array, compressed_rowid); ArrayOutput output(data); output.writeChar(0); // 0 -- present, 1 -- removed from index ArrayOutput rid_output(compressed_rowid); RowIDSaver rid_saver(context.context().rid_dict, rid_output); rid_saver.saveRowID(rowid); output.writeByte((byte)compressed_rowid.size()); output.writeArray(compressed_rowid); TRY_READ_TARGET_RXN { BufferScanner scanner(reaction_buf); try { index.prepare(scanner, output, NULL); } catch (CmfSaver::Error &e) { if (context.context().reject_invalid_structures) throw; // Rethrow this exception further env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); return false; } catch (CrfSaver::Error &e) { if (context.context().reject_invalid_structures) throw; // Rethrow this exception further env.dbgPrintf(bad_reaction_warning_rowid, rowid, e.message()); return false; } } CATCH_READ_TARGET_RXN_ROWID(rowid, { if (context.context().reject_invalid_structures) throw; // Rethrow this exception further return false; }); // some magic: round it up to avoid ora-22282 if (data.size() % 2 == 1) output.writeChar(0); int blockno, offset; context.context().storage.add(env, data, blockno, offset); fingerprints.addFingerprint(env, index.getFingerprint()); context.shadow_table.addReaction(env, index, rowid, blockno + 1, offset); return true; } void ringoRegisterTable (OracleEnv &env, RingoOracleContext &context, const char *source_table, const char *source_column, const char *target_datatype) { QS_DEF(Array, reaction_buf); OracleStatement statement(env); AutoPtr reaction_lob; OraRowidText rowid; char varchar2_text[4001]; // Oracle's BLOB and CLOB types always come uppercase bool blob = (strcmp(target_datatype, "BLOB") == 0); bool clob = (strcmp(target_datatype, "CLOB") == 0); int total_count = 0; OracleStatement::executeSingleInt(total_count, env, "SELECT COUNT(*) FROM %s WHERE %s IS NOT NULL AND LENGTH(%s) > 0", source_table, source_column, source_column); context.context().longOpInit(env, total_count, "Building reaction index", source_table, "reactions"); statement.append("SELECT %s, RowidToChar(rowid) FROM %s WHERE %s IS NOT NULL AND LENGTH(%s) > 0", source_column, source_table, source_column, source_column); statement.prepare(); if (blob) { reaction_lob.reset(new OracleLOB(env)); statement.defineBlobByPos(1, reaction_lob.ref()); } else if (clob) { reaction_lob.reset(new OracleLOB(env)); statement.defineClobByPos(1, reaction_lob.ref()); } else statement.defineStringByPos(1, varchar2_text, sizeof(varchar2_text)); statement.defineStringByPos(2, rowid.ptr(), sizeof(rowid)); BingoFingerprints &fingerprints = context.fingerprints; int nthreads = 0; fingerprints.validateForUpdate(env); context.context().configGetInt(env, "NTHREADS", nthreads); nthreads = 1; //if (nthreads == 1) { int n = 0; QS_DEF(RingoIndex, index); index.init(context.context()); if (statement.executeAllowNoData()) do { env.dbgPrintf("inserting reaction #%d with rowid %s\n", n, rowid.ptr()); if (blob || clob) reaction_lob->readAll(reaction_buf, false); else reaction_buf.readString(varchar2_text, false); try { if (_ringoRegisterReaction(env, rowid.ptr(), reaction_buf, context, index, fingerprints)) n++; } catch (Exception &ex) { char buf[4096]; snprintf(buf, NELEM(buf), "Failed on record with rowid=%s. Error message is '%s'", rowid.ptr(), ex.message()); throw Exception(buf); } if ((n % 50) == 0) context.context().longOpUpdate(env, n); if ((n % 1000) == 0) { env.dbgPrintfTS("done %d reactions ; flushing\n", n); context.context().storage.flush(env); } } while (statement.fetch()); fingerprints.flush(env); } } ORAEXT void oraRingoCreateIndex (OCIExtProcContext *ctx, int context_id, const char *params, short params_ind, const char *full_table_name, short full_table_name_ind, const char *column_name, short column_name_ind, const char *column_data_type, short column_data_type_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("Creating index\n"); BingoOracleContext &bcontext = BingoOracleContext::get(env, context_id, false, 0); // parse parameters before creating RingoOracleContext because // it creates the BingoSreening according to fingerprintSizeNoTau() if (params_ind == OCI_IND_NOTNULL) bcontext.parseParameters(env, params); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); BingoStorage &storage = context.context().storage; BingoFingerprints &fingerprints = context.fingerprints; context.shadow_table.drop(env); context.shadow_table.create(env); fingerprints.drop(env); fingerprints.create(env); storage.drop(env); storage.create(env); storage.validateForInsert(env); ringoRegisterTable(env, context, full_table_name, column_name, column_data_type); storage.finish(env); context.context().saveCmfDict(env); context.context().saveRidDict(env); OracleStatement::executeSingle(env, "COMMIT"); RingoFetchContext::removeByContextID(context_id); RingoContext::remove(context_id); BingoContext::remove(context_id); } ORABLOCK_END } ORAEXT void oraRingoDropIndex (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("Dropping index #%d\n", context_id); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); context.shadow_table.drop(env); context.context().storage.drop(env); context.fingerprints.drop(env); RingoFetchContext::removeByContextID(context_id); RingoContext::remove(context_id); BingoContext::remove(context_id); // TEMP: remove CMF dictionary OracleStatement::executeSingle(env, "DELETE FROM CONFIG_BLOB WHERE n=%d", context_id); } ORABLOCK_END } ORAEXT void oraRingoTruncateIndex (OCIExtProcContext *ctx, int context_id) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); env.dbgPrintfTS("Truncating index #%d\n", context_id); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); context.shadow_table.truncate(env); context.context().storage.truncate(env); context.fingerprints.truncate(env); RingoFetchContext::removeByContextID(context_id); RingoContext::remove(context_id); BingoContext::remove(context_id); } ORABLOCK_END } ORAEXT void oraRingoIndexInsert (OCIExtProcContext *ctx, int context_id, const char *rowid, short rowid_ind, OCILobLocator *target_loc, short target_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (rowid_ind != OCI_IND_NOTNULL) throw BingoError("null rowid given"); if (target_ind != OCI_IND_NOTNULL) // somebody added a NULL value into the table; ignore it return; RingoOracleContext &context = RingoOracleContext::get(env, context_id, true); env.dbgPrintf("inserting reaction with rowid %s\n", rowid); QS_DEF(RingoIndex, index); index.init(context.context()); BingoFingerprints &fingerprints = context.fingerprints; BingoStorage &storage = context.context().storage; storage.lock(env); storage.validateForInsert(env); fingerprints.validateForUpdate(env); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); _ringoRegisterReaction(env, rowid, target_buf, context, index, fingerprints); storage.finish(env); //fingerprints.flush(env); if (context.context().cmf_dict.isModified()) context.context().saveCmfDict(env); if (context.context().rid_dict.isModified()) context.context().saveRidDict(env); } ORABLOCK_END } ORAEXT void oraRingoIndexDelete (OCIExtProcContext *ctx, int context_id, const char *rowid, short rowid_ind) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); if (rowid_ind != OCI_IND_NOTNULL) throw BingoError("null rowid given"); RingoOracleContext &context = RingoOracleContext::get(env, context_id, false); int blockno, offset; if (context.shadow_table.getReactionLocation(env, rowid, blockno, offset)) { env.dbgPrintf("deleting reaction that has rowid %s\n", rowid); BingoStorage &storage = context.context().storage; storage.lock(env); storage.markRemoved(env, blockno, offset); context.shadow_table.deleteReaction(env, rowid); } else env.dbgPrintf("reaction with rowid %s not found\n", rowid); } ORABLOCK_END } ORAEXT void oraRingoFlushInserts (OCIExtProcContext *ctx, int commit) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); int i; for (i = RingoContext::begin(); i != RingoContext::end(); i = RingoContext::next(i)) { env.dbgPrintfTS("flushing inserts of context #%d\n", i); RingoOracleContext &context = RingoOracleContext::get(env, i, false); context.fingerprints.flush(env); if (commit) OracleStatement::executeSingle(env, "COMMIT"); context.context().unlock(env); } } ORABLOCK_END } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_oracle_util.cpp000066400000000000000000000251001271037650300250020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/bingo_oracle.h" #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "oracle/mango_oracle.h" #include "reaction/reaction.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rsmiles_saver.h" #include "reaction/icr_saver.h" #include "reaction/rxnfile_saver.h" #include "reaction/reaction_cml_saver.h" #include "reaction/reaction_auto_loader.h" #include "ringo_oracle.h" #include "molecule/molfile_loader.h" #include "reaction/rxnfile_loader.h" #include "layout/reaction_layout.h" #include "oracle/bingo_oracle_context.h" #include "molecule/elements.h" #include "reaction/reaction_fingerprint.h" static OCIString * _ringoRSMILES (OracleEnv &env, const Array &target_buf, BingoOracleContext &context) { QS_DEF(Reaction, target); ReactionAutoLoader loader(target_buf); context.setLoaderSettings(loader); loader.loadReaction(target); QS_DEF(Array, rsmiles); ArrayOutput out(rsmiles); RSmilesSaver saver(out); saver.saveReaction(target); OCIString *result = 0; env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)rsmiles.ptr(), rsmiles.size(), &result)); return result; } ORAEXT OCIString *oraRingoRSMILES (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCIString *result = NULL; logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); *return_indicator = OCI_IND_NULL; if (target_indicator == OCI_IND_NOTNULL) { OracleLOB target_lob(env, target_locator); QS_DEF(Array, buf); target_lob.readAll(buf, false); result = _ringoRSMILES(env, buf, context); } if (result == 0) // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); else *return_indicator = OCI_IND_NOTNULL; } ORABLOCK_END return result; } ORAEXT OCIString * oraRingoCheckReaction (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCIString *result = NULL; logger.initIfClosed(log_filename); ORABLOCK_BEGIN { OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); *return_indicator = OCI_IND_NULL; if (target_indicator != OCI_IND_NOTNULL) { static const char *msg = "null reaction given"; env.callOCI(OCIStringAssignText(env.envhp(), env.errhp(), (text *)msg, strlen(msg), &result)); *return_indicator = OCI_IND_NOTNULL; } else { OracleLOB target_lob(env, target_locator); QS_DEF(Array, buf); QS_DEF(Reaction, reaction); target_lob.readAll(buf, false); TRY_READ_TARGET_RXN { ReactionAutoLoader loader(buf); context.setLoaderSettings(loader); loader.loadReaction(reaction); Reaction::checkForConsistency(reaction); } CATCH_READ_TARGET_RXN( OCIStringAssignText(env.envhp(), env.errhp(), (text *)e.message(), strlen(e.message()), &result); *return_indicator = OCI_IND_NOTNULL;); if (*return_indicator == OCI_IND_NULL) // This is needed for Oracle 9. Returning NULL drops the extproc. OCIStringAssignText(env.envhp(), env.errhp(), (text *)"nil", 3, &result); } } ORABLOCK_END return result; } void _ICR (OracleLOB &target_lob, int save_xyz, Array &icr, BingoOracleContext &context) { QS_DEF(Array, target); QS_DEF(Reaction, reaction); target_lob.readAll(target, false); ReactionAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadReaction(reaction); if ((save_xyz != 0) && !Reaction::haveCoord(reaction)) throw BingoError("reaction has no XYZ"); ArrayOutput output(icr); IcrSaver saver(output); saver.save_xyz = (save_xyz != 0); saver.saveReaction(reaction); } ORAEXT OCILobLocator *oraRingoICR (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, int save_xyz, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); if (target_indicator == OCI_IND_NOTNULL) { OracleLOB target_lob(env, target_locator); QS_DEF(Array, icr); _ICR(target_lob, save_xyz, icr, context); OracleLOB lob(env); lob.createTemporaryBLOB(); lob.write(0, icr); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT void oraRingoICR2 (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, OCILobLocator *result_locator, short result_indicator, int save_xyz) { ORABLOCK_BEGIN { OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); if (target_indicator == OCI_IND_NULL) throw BingoError("null reaction given"); if (result_indicator == OCI_IND_NULL) throw BingoError("null LOB given"); OracleLOB target_lob(env, target_locator); QS_DEF(Array, icr); _ICR(target_lob, save_xyz, icr, context); OracleLOB result_lob(env, result_locator); result_lob.write(0, icr); result_lob.trim(icr.size()); } ORABLOCK_END } ORAEXT OCILobLocator *oraRingoRxnfile (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); if (target_indicator == OCI_IND_NOTNULL) { OracleLOB target_lob(env, target_locator); QS_DEF(Array, target); QS_DEF(Array, icm); QS_DEF(Reaction, reaction); target_lob.readAll(target, false); ReactionAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadReaction(reaction); if (!Reaction::haveCoord(reaction)) { ReactionLayout layout(reaction); layout.make(); reaction.markStereocenterBonds(); } ArrayOutput output(icm); RxnfileSaver saver(output); saver.saveReaction(reaction); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, icm); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT OCILobLocator *oraRingoCML (OCIExtProcContext *ctx, OCILobLocator *target_locator, short target_indicator, short *return_indicator) { OCILobLocator *result = 0; ORABLOCK_BEGIN { *return_indicator = OCI_IND_NULL; OracleEnv env(ctx, logger); BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); if (target_indicator == OCI_IND_NOTNULL) { OracleLOB target_lob(env, target_locator); QS_DEF(Array, target); QS_DEF(Array, icm); QS_DEF(Reaction, reaction); target_lob.readAll(target, false); ReactionAutoLoader loader(target); context.setLoaderSettings(loader); loader.loadReaction(reaction); if (!Reaction::haveCoord(reaction)) { ReactionLayout layout(reaction); layout.make(); reaction.markStereocenterBonds(); } ArrayOutput output(icm); ReactionCmlSaver saver(output); saver.saveReaction(reaction); OracleLOB lob(env); lob.createTemporaryCLOB(); lob.write(0, icm); lob.doNotDelete(); result = lob.get(); *return_indicator = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } ORAEXT OCILobLocator * oraRingoFingerprint (OCIExtProcContext *ctx, OCILobLocator *target_loc, short target_ind, const char *options, short options_ind, short *return_ind) { OCILobLocator *result = NULL; ORABLOCK_BEGIN { *return_ind = OCI_IND_NULL; OracleEnv env(ctx, logger); if (options_ind != OCI_IND_NOTNULL) options = ""; if (target_ind == OCI_IND_NOTNULL) { BingoOracleContext &context = BingoOracleContext::get(env, 0, false, 0); QS_DEF(Array, target_buf); OracleLOB target_lob(env, target_loc); target_lob.readAll(target_buf, false); QS_DEF(Reaction, target); ReactionAutoLoader loader(target_buf); context.setLoaderSettings(loader); loader.loadReaction(target); ReactionFingerprintBuilder builder(target, context.fp_parameters); builder.parseFingerprintType(options, false); builder.process(); const char* buf = (const char*)builder.get(); int buf_len = context.fp_parameters.fingerprintSize(); OracleLOB lob(env); lob.createTemporaryBLOB(); lob.write(0, buf, buf_len); lob.doNotDelete(); result = lob.get(); *return_ind = OCI_IND_NOTNULL; } } ORABLOCK_END return result; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_shadow_fetch.cpp000066400000000000000000000140531271037650300251430ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "oracle/ringo_shadow_fetch.h" #include "core/ringo_matchers.h" #include "oracle/ringo_fetch_context.h" IMPL_ERROR(RingoShadowFetch, "ringo shadow fetch"); RingoShadowFetch::RingoShadowFetch (RingoFetchContext &context) : _context(context) { _total_count = -1; _table_name.push(0); ArrayOutput output(_table_name); output.printf("SHADOW_%d", context.context_id); output.writeChar(0); _executed = false; _fetch_type = 0; _processed_rows = 0; _end = false; _rowid.ptr()[0] = 0; } RingoShadowFetch::~RingoShadowFetch () { } bool RingoShadowFetch::getLastRowid (OraRowidText &id) { if (_rowid.ptr()[0] == 0) return false; memcpy(&id, &_rowid, sizeof(_rowid)); return true; } int RingoShadowFetch::getTotalCount (OracleEnv &env) { if (_total_count < 0) { if (!OracleStatement::executeSingleInt(_total_count, env, "SELECT COUNT(*) FROM %s", _table_name.ptr())) throw Error("getTotalCount() error"); } return _total_count; } int RingoShadowFetch::countOracleBlocks (OracleEnv &env) { int res; env.dbgPrintf("countOracleBlocks\n"); if (!OracleStatement::executeSingleInt(res, env, "select blocks from user_tables where " "table_name = upper('%s')", _table_name.ptr())) return 0; return res; } int RingoShadowFetch::getIOCost (OracleEnv &env, float selectivity) { return (int)(countOracleBlocks(env) * selectivity); } bool RingoShadowFetch::end () { return _end; } float RingoShadowFetch::calcSelectivity (OracleEnv &env, int total_count) { int nrows_select_total; if (_counting_select.size() > 0) { _counting_select.push(0); OracleStatement statement(env); statement.append("%s", _counting_select.ptr()); statement.prepare(); statement.defineIntByPos(1, &nrows_select_total); if (_fetch_type == _EXACT && _right_part == 1) { const char *hash = _context.exact.getQueryHashStr(); statement.bindStringByName(":hash", hash, strlen(hash) + 1); } if (!statement.executeAllowNoData()) throw Error("selectivity: cannot count rows"); } else nrows_select_total = total_count; if (_processed_rows == 0) { if (nrows_select_total == 0) return 0; throw Error("selectivity: no processed rows"); } return (float)nrows_select_total * matched.size() / (total_count * _processed_rows); } void RingoShadowFetch::prepareNonSubstructure (OracleEnv &env) { env.dbgPrintf("preparing shadow table for non-substructure match\n"); _fetch_type = _NON_SUBSTRUCTURE; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _lob_crf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT rid, crf FROM %s", _table_name.ptr()); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_crf.ref()); _counting_select.clear(); } void RingoShadowFetch::prepareExact (OracleEnv &env, int right_part) { RingoExact & instance = _context.exact; if (right_part == 1) env.dbgPrintf("preparing shadow table for exact\n"); else env.dbgPrintf("preparing shadow table for non-exact\n"); _fetch_type = _EXACT; _right_part = right_part; _env.reset(new OracleEnv(env.ctx(), env.logger())); _statement.reset(new OracleStatement(_env.ref())); _lob_crf.reset(new OracleLOB(_env.ref())); _statement->append("SELECT sh.rid, sh.crf FROM %s sh", _table_name.ptr()); if (right_part == 1) _statement->append(" WHERE hash = :hash"); _statement->prepare(); _statement->defineStringByPos(1, _rowid.ptr(), sizeof(_rowid)); _statement->defineBlobByPos(2, _lob_crf.ref()); if (_right_part == 1) { const char *hash_str = instance.getQueryHashStr(); _statement->bindStringByName(":hash", hash_str, strlen(hash_str) + 1); } ArrayOutput output_cnt(_counting_select); output_cnt.printf("SELECT COUNT(*) FROM %s sh", _table_name.ptr()); if (right_part == 1) output_cnt.printf(" WHERE hash = :hash"); } void RingoShadowFetch::fetch (OracleEnv &env, int maxrows) { matched.clear(); if (_statement.get() == 0) return; if (maxrows < 1 || _end) return; env.dbgPrintf("fetching up to %d rows using shadowtable... ", maxrows); while (matched.size() < maxrows) { bool fetch_res; if (!_executed) { fetch_res = _statement->executeAllowNoData(); _executed = true; } else fetch_res = _statement->fetch(); if (!fetch_res) { _end = true; break; } bool have_match = false; if (_fetch_type == _NON_SUBSTRUCTURE) { RingoSubstructure &instance = _context.substructure; QS_DEF(Array, crf); _lob_crf->readAll(crf, false); if (!instance.matchBinary(crf)) have_match = true; } else if (_fetch_type == _EXACT) { RingoExact &instance = _context.exact; QS_DEF(Array, crf); _lob_crf->readAll(crf, false); have_match = (instance.matchBinary(crf) == (_right_part == 1)); } else throw Error("unexpected fetch type %d", _fetch_type); if (have_match) matched.add(_rowid); _processed_rows++; } env.dbgPrintf("fetched %d\n", matched.size()); return; } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_shadow_fetch.h000066400000000000000000000037761271037650300246220ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_shadow_fetch__ #define __ringo_shadow_fetch__ #include "base_cpp/auto_ptr.h" #include "oracle/bingo_fetch_engine.h" using namespace indigo; class RingoFetchContext; class RingoShadowFetch : public BingoFetchEngine { public: RingoShadowFetch (RingoFetchContext &context); virtual ~RingoShadowFetch (); virtual float calcSelectivity (OracleEnv &env, int total_count); virtual void fetch (OracleEnv &env, int maxrows); virtual bool end (); virtual int getIOCost (OracleEnv &env, float selectivity); virtual int getTotalCount (OracleEnv &env); virtual bool getLastRowid (OraRowidText &id); int countOracleBlocks (OracleEnv &env); void prepareNonSubstructure (OracleEnv &env); void prepareExact (OracleEnv &env, int right_part); DECL_ERROR; protected: enum { _NON_SUBSTRUCTURE = 1, _EXACT = 2 }; RingoFetchContext &_context; Array _table_name; int _total_count; Array _counting_select; int _processed_rows; bool _end; AutoPtr _env; AutoPtr _statement; AutoPtr _lob_crf; bool _executed; int _fetch_type; OraRowidText _rowid; int _right_part; }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_shadow_table.cpp000066400000000000000000000070251271037650300251420ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/ringo_shadow_table.h" #include "core/ringo_index.h" #include "molecule/elements.h" #include "base_cpp/output.h" IMPL_ERROR(RingoShadowTable, "ringo shadow table"); RingoShadowTable::RingoShadowTable (int context_id) { _table_name.push(0); ArrayOutput output(_table_name); output.printf("SHADOW_%d", context_id); output.writeChar(0); } void RingoShadowTable::addReaction (OracleEnv &env, RingoIndex &index, const char *rowid, int blockno, int offset) { OracleLOB crf(env); crf.createTemporaryBLOB(); crf.write(0, index.getCrf()); OracleStatement statement(env); statement.append("INSERT INTO %s VALUES(:rxnrowid, :blockno, :offset, :crf, :rxnhash)", _table_name.ptr()); statement.prepare(); statement.bindStringByName(":rxnrowid", rowid, strlen(rowid) + 1); statement.bindIntByName(":blockno", &blockno); statement.bindIntByName(":offset", &offset); statement.bindBlobByName(":crf", crf); statement.bindStringByName(":rxnhash", index.getHashStr(), strlen(index.getHashStr()) + 1); statement.execute(); } void RingoShadowTable::create (OracleEnv &env) { OracleStatement statement(env); const char *tn = _table_name.ptr(); statement.append("CREATE TABLE %s " "(rid VARCHAR2(18), blockno NUMBER, offset NUMBER, crf BLOB, hash VARCHAR2(8)) NOLOGGING", tn); statement.prepare(); statement.execute(); OracleStatement::executeSingle(env, "CREATE UNIQUE INDEX %s_rid ON %s(rid)", tn, tn); } void RingoShadowTable::drop (OracleEnv &env) { OracleStatement::executeSingle(env, "BEGIN DropTable('%s'); END;", _table_name.ptr()); } void RingoShadowTable::truncate (OracleEnv &env) { OracleStatement::executeSingle(env, "TRUNCATE TABLE %s", _table_name.ptr()); } void RingoShadowTable::analyze (OracleEnv &env) { env.dbgPrintf("analyzing shadow table\n"); OracleStatement::executeSingle(env, "ANALYZE TABLE %s ESTIMATE STATISTICS", _table_name.ptr()); } int RingoShadowTable::countOracleBlocks (OracleEnv &env) { int res; if (!OracleStatement::executeSingleInt(res, env, "select blocks from user_tables where " "table_name = upper('%s')", _table_name.ptr())) return 0; return res; } const char * RingoShadowTable::getName () { return _table_name.ptr(); } bool RingoShadowTable::getReactionLocation (OracleEnv &env, const char *rowid, int &blockno, int &offset) { OracleStatement statement(env); statement.append("SELECT blockno, offset FROM %s WHERE rid = :rid", _table_name.ptr()); statement.prepare(); statement.bindStringByName(":rid", rowid, strlen(rowid) + 1); statement.defineIntByPos(1, &blockno); statement.defineIntByPos(2, &offset); return statement.executeAllowNoData(); } void RingoShadowTable::deleteReaction (OracleEnv &env, const char *rowid) { OracleStatement::executeSingle_BindString(env, ":rid", rowid, "DELETE FROM %s WHERE rid = :rid", _table_name.ptr()); } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/ringo_shadow_table.h000066400000000000000000000030051271037650300246010ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ringo_shadow_table__ #define __ringo_shadow_table__ #include "base_cpp/tlscont.h" #include "oracle/ora_wrap.h" #include "oracle/bingo_fetch_engine.h" using namespace indigo; class RingoIndex; class RingoShadowTable { public: explicit RingoShadowTable (int context_id); void drop (OracleEnv &env); void truncate (OracleEnv &env); void create (OracleEnv &env); void addReaction (OracleEnv &env, RingoIndex &index, const char *rowid, int blockno, int offset); bool getReactionLocation (OracleEnv &env, const char *rowid, int &blockno, int &offset); void deleteReaction (OracleEnv &env, const char *rowid); void analyze (OracleEnv &env); int countOracleBlocks (OracleEnv &env); const char * getName (); DECL_ERROR; protected: Array _table_name; private: RingoShadowTable (RingoShadowTable &); // no implicit copy }; #endif Indigo-indigo-1.2.3/bingo/oracle/src/oracle/rowid_loader.cpp000066400000000000000000000041421271037650300237570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "base_cpp/array.h" #include "molecule/molecule.h" #include "oracle/rowid_loader.h" #include "oracle/rowid_symbol_codes.h" IMPL_ERROR(RowIDLoader, "rowID loader"); RowIDLoader::RowIDLoader( LzwDict &NewDict, Scanner &NewIn ) : _decoder(NewDict, NewIn) { } void RowIDLoader::loadRowID( Array &RowId ) { RowId.clear(); int CurCode; while (!_decoder.isEOF()) { CurCode = _decoder.get(); if (CurCode == ROW_ID_SLASH) { RowId.push('/'); continue; } if (CurCode == ROW_ID_PLUS) { RowId.push('+'); continue; } if (CurCode >= ROW_ID_DIGITS && CurCode < ROW_ID_DIGITS + ROW_ID_NUM_OF_DIGITS) { CurCode = CurCode + 48 - ROW_ID_DIGITS; RowId.push((char)CurCode); continue; } if (CurCode >= ROW_ID_LOWER_CASE && CurCode < ROW_ID_LOWER_CASE + ROW_ID_NUM_OF_SMALL_LETTERS) { CurCode = CurCode + 97 - ROW_ID_LOWER_CASE; RowId.push((char)CurCode); continue; } if (CurCode >= ROW_ID_UPPER_CASE && CurCode < ROW_ID_UPPER_CASE + ROW_ID_NUM_OF_BIG_LETTERS) { CurCode = CurCode + 65 - ROW_ID_UPPER_CASE; RowId.push((char)CurCode); continue; } throw Error("invalid rowID"); } } /* END OF 'ROWID_LOADER.CPP' FILE */ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/rowid_loader.h000066400000000000000000000023001271037650300234160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rowid_loader_h__ #define __rowid_loader_h__ #include "base_cpp/bitinworker.h" #include "lzw/lzw_dictionary.h" #include "lzw/lzw_decoder.h" using namespace indigo; namespace indigo { class Molecule; class Scanner; } class RowIDLoader { public: DECL_ERROR; RowIDLoader( LzwDict &NewDict, Scanner &NewIn ); void loadRowID( Array &RowId ); private: int _getNextCode( void ); LzwDecoder _decoder; // no implicit copy RowIDLoader( const RowIDLoader & ); }; #endif /* __rowid_loader_h__ */ /* END OF 'ROWID_LOADER.H' FILE */ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/rowid_saver.cpp000066400000000000000000000040101271037650300236230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "oracle/rowid_saver.h" #include "oracle/rowid_symbol_codes.h" IMPL_ERROR(RowIDSaver, "rowID saver"); RowIDSaver::RowIDSaver( LzwDict &NewDict, Output &NewOut ) { if (!NewDict.isInitialized()) NewDict.init(ROW_ID_ALPHABET_SIZE, ROW_ID_BIT_CODE_SIZE); _encoder_obj.create(NewDict, NewOut); _encoder = _encoder_obj.get(); }; void RowIDSaver::saveRowID( const char *RowId ) { _encoder->start(); if (strlen(RowId) != ROW_ID_SIZE) throw Error("invalid RowID"); for (int i = 0; i < ROW_ID_SIZE; i++) _encodeSymbol(RowId[i]); /* Write last symbol */ _encoder->finish(); } void RowIDSaver::_encode( int NextSymbol ) { _encoder->send(NextSymbol); } void RowIDSaver::_encodeSymbol( char Symbol ) { int RowIDCode = -1; if (Symbol == '/') RowIDCode = ROW_ID_SLASH; if (Symbol >= '0' && Symbol <= '9') RowIDCode = ROW_ID_DIGITS + Symbol - 48; if (Symbol >= 'a' && Symbol <= 'z') RowIDCode = ROW_ID_LOWER_CASE + Symbol - 97; if (Symbol >= 'A' && Symbol <= 'Z') RowIDCode = ROW_ID_UPPER_CASE + Symbol - 65; if (Symbol == '+') RowIDCode = ROW_ID_PLUS; if (RowIDCode == -1) throw Error("invalid rowID symbol"); _encode(RowIDCode); } /* END OF 'ROWID_SAVER.CPP' FILE */ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/rowid_saver.h000066400000000000000000000024671271037650300233060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rowid_saver_h__ #define __rowid_saver_h__ #include "base_cpp/bitoutworker.h" #include "lzw/lzw_dictionary.h" #include "lzw/lzw_encoder.h" #include "oracle/rowid_symbol_codes.h" #include "base_cpp/obj.h" using namespace indigo; namespace indigo { class Output; } class RowIDSaver { public: DECL_ERROR; RowIDSaver( LzwDict &NewDict, Output &NewOut ); void saveRowID( const char *RowID ); private: void _encodeSymbol( char Symbol ); void _encode( int NextSymbol ); Obj _encoder_obj; LzwEncoder *_encoder; // no implicit copy RowIDSaver( const RowIDSaver & ); }; #endif /* __rowid_saver_h__ */ /* END OF 'ROWID_SAVER.H' FILE */ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/rowid_symbol_codes.h000066400000000000000000000027031271037650300246410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rowid_symbol_codes_h__ #define __rowid_symbol_codes_h__ /* RowId symbol constants * RowId: 18-byte string containing: * A-Z, a-z, 0-9, /, + */ enum { ROW_ID_BIT_CODE_SIZE = 11, ROW_ID_NUM_OF_DIGITS = 10, ROW_ID_NUM_OF_SMALL_LETTERS = 26, ROW_ID_NUM_OF_BIG_LETTERS = 26, ROW_ID_SLASH = 0, ROW_ID_DIGITS = 1, ROW_ID_LOWER_CASE = (ROW_ID_DIGITS + ROW_ID_NUM_OF_DIGITS), ROW_ID_UPPER_CASE = (ROW_ID_LOWER_CASE + ROW_ID_NUM_OF_SMALL_LETTERS), ROW_ID_PLUS = 63, /* ROW_ID_NUM_OF_DIGITS = 10 + ROW_ID_NUM_OF_SMALL_LETTERS = 26 + ROW_ID_NUM_OF_BIG_LETTERS = 26 + '/' & '+' */ ROW_ID_ALPHABET_SIZE = 64, ROW_ID_SIZE = 18 }; #endif /* __rowid_symbol_codes_h__ */ /* END OF 'ROWID_SYMBOL_CODES.H' FILE */ Indigo-indigo-1.2.3/bingo/oracle/src/oracle/warnings_table.cpp000066400000000000000000000036001271037650300243020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "oracle/warnings_table.h" #include "base_cpp/scanner.h" void WarningsTable::reset () { _table_name.clear(); _rowid_column.clear(); _message_column.clear(); } void WarningsTable::setTableNameAndColumns (OracleEnv &env, const char *table_name_with_columns) { try { BufferScanner scanner(table_name_with_columns); scanner.readWord(_table_name, "("); scanner.skip(1); scanner.readWord(_rowid_column, ","); scanner.skip(1); scanner.readWord(_message_column, ")"); } catch (Scanner::Error &ex) { if (strlen(table_name_with_columns) > 0) env.dbgPrintfTS("Cannot parse %s table name with columns. The format is (,)", table_name_with_columns); reset(); } } void WarningsTable::add (OracleEnv &env, const char *rowid, const char *message) { if (_table_name.size() != 0) { OracleStatement statement(env); statement.append("insert into %s (%s, %s) values (:id, :msg)", _table_name.ptr(), _rowid_column.ptr(), _message_column.ptr()); statement.prepare(); statement.bindStringByName(":id", rowid, strlen(rowid) + 1); statement.bindStringByName(":msg", message, strlen(message) + 1); statement.executeAllowNoData(); } } Indigo-indigo-1.2.3/bingo/oracle/src/oracle/warnings_table.h000066400000000000000000000020371271037650300237520ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __warnings_table__ #define __warnings_table__ #include "oracle/ora_wrap.h" #include using namespace indigo; class WarningsTable { public: void reset (); void setTableNameAndColumns (OracleEnv &env, const char *table_name_with_columns); void add (OracleEnv &env, const char *rowid, const char *message); private: Array _table_name, _rowid_column, _message_column; }; #endif Indigo-indigo-1.2.3/bingo/postgres/000077500000000000000000000000001271037650300171455ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/CMakeLists.txt000066400000000000000000000055571271037650300217210ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(BingoPostgres C CXX) include(../bingo-version.cmake) include(ConfigureCommon) message(STATUS "BingoPostgres version is ${BINGO_VERSION_EXT}") find_package(POSTGRES) if (NOT POSTGRES_FOUND) message(FATAL_ERROR "Postgres not found!") endif() message(STATUS "Postgres found at ${POSTGRES_INCLUDE_DIRS}") file(GLOB PG_AM_src src/pg_am/*.cpp src/pg_am/*.c) file(GLOB PG_AM_headers src/pg_am/*.h) file(GLOB PG_COMMON_src src/pg_common/*.cpp src/pg_common/*.c) file(GLOB PG_COMMON_headers src/pg_common/*.h) file(GLOB PG_CORE_src src/pg_core/*.cpp src/pg_core/*.c) file(GLOB PG_CORE_headers src/pg_core/*.h) include_directories(src ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${ZLib_HEADERS_DIR} ${BingoCoreC_HEADERS_DIR} ${BingoCore_HEADERS_DIR} ${POSTGRES_INCLUDE_DIRS} src/pg_common/ src/pg_am/ src/pg_core/) if (MSVC) include_directories(${POSTGRES_INCLUDE_DIRS}/port/win32_msvc ${POSTGRES_INCLUDE_DIRS}/port/win32 ${POSTGRES_INCLUDE_DIRS}/..) endif() if (NOT MSVC) add_library(bingo-postgres-shared SHARED ${PG_AM_src} ${PG_AM_headers} ${PG_COMMON_src} ${PG_COMMON_headers} ${PG_CORE_src} ${PG_CORE_headers} ${Common_SOURCE_DIR}/hacks/memcpy.c $ $ $ $ $) else() add_library(bingo-postgres-shared SHARED ${PG_AM_src} ${PG_AM_headers} ${PG_COMMON_src} ${PG_COMMON_headers} ${PG_CORE_src} ${PG_CORE_headers} ${POSTGRES_INCLUDE_DIRS}/port/win32.h $ $ $ $ $) endif() SET_TARGET_PROPERTIES(bingo-postgres-shared PROPERTIES OUTPUT_NAME "bingo_postgres") SET_TARGET_PROPERTIES(bingo-postgres-shared PROPERTIES PREFIX "") set_target_properties(bingo-postgres-shared PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if (APPLE) set_target_properties(bingo-postgres-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -undefined suppress -flat_namespace") elseif(UNIX) set_target_properties(bingo-postgres-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -pthread") endif() if (UNIX AND NOT APPLE) message(STATUS "UNIX AND NOT APPLE") if(${SUBSYSTEM_NAME} MATCHES "x64") message(STATUS "SUBSYSTEM_NAME EQUALS x64") set_target_properties(bingo-postgres-shared PROPERTIES LINK_FLAGS "${LINK_FLAGS} -pthread -Wl,--wrap=memcpy") endif() endif() target_link_libraries(bingo-postgres-shared bingo-core-c bingo-core z tinyxml) if (MSVC) target_link_libraries(bingo-postgres-shared ${POSTGRES_LIBRARY}) endif() set_property(TARGET bingo-postgres-shared PROPERTY LINK_INTERFACE_LIBRARIES "") set_property(TARGET bingo-postgres-shared PROPERTY FOLDER "bingo-postgres") pack_bingo(bingo-postgres-shared) Indigo-indigo-1.2.3/bingo/postgres/README-BUILD.md000066400000000000000000000015211271037650300212600ustar00rootroot00000000000000## Prerequisites You should specify the PostgreSQL directory by setting the special environment variables: BINGO_PG_DIR, BINGO_PG_VERSION. ``` export BINGO_PG_DIR=`` export BINGO_PG_VERSION=`` ``` For example ``` export BINGO_PG_DIR=/var/lib/postgresql export BINGO_PG_VERSION=9.5 ``` ## Build Run python installation script ``` python build_scripts/bingo-release.py --preset=linux64 --dbms=postgres ``` ## Build Debug: On Linux (minimum required gcc version 4.7) cd /build_scripts/bingo-postgres cmake . make On Windows, (Microsoft Visual Studio 2013 is required) set BINGO_PG_DIR= set BINGO_PG_VERSION=9.2 cd /build_scripts/bingo-postgres cmake-gui . Indigo-indigo-1.2.3/bingo/postgres/README.md000066400000000000000000000141761271037650300204350ustar00rootroot00000000000000System Requirements ------------------- ### Operating Systems ### * Linux (all modern distributions, 32-bit and 64-bit) * Windows (all modern distributions, 32-bit and 64-bit) * Mac OS X (10.5 Leopard and 10.6 Snow Leopard 64 bit) ### Database Servers ### * PostreSQL 9.0 * PostreSQL 9.1 * PostreSQL 9.2 * PostreSQL 9.3 * PostreSQL 9.4 * PostreSQL 9.5 ### Tested Configurations ### Bingo has been successfully tested for 9.0-9.5 PostgreSQL versions Installation Prerequisities --------------------------- ### All Systems ### The shared buffer parameter in the PostgreSQL database configuration file (postgresql.conf) should be increased. For the optimal performance EPAM recommends to increase the value . shared_buffers=64MB ### Linux ### The Bingo engine requires a lot of shared memory. For linux systems EPAM recommends to change kernel.shmmax and kernel.shmall Add the following line to `/etc/sysctl.conf` file: kernel.shmmax= kernel.shmall= The recommended value is appr. 50% of the RAM (in bytes) Execute (with the root privilegies) sysctl -p /etc/sysctl.conf Installation Procedure ---------------------- ### All Systems ### Download and unzip the cartridge archive. The Bingo library is located in the 'bin' directory (bingo_postgres.dll for WINDOWS, or bingo_postgres.so for LINUX, or bingo_postgres.dylib for MacOSX). The Bingo library is built with specific PostgreSQL headers, so there is no need to run the building procedure. You can simply run the pre-generated SQL script with specified path to the Bingo library. 1. Copy the library file to a desired directory. The library can be copied into the PostgreSQL package directory (`{POSTGRES_HOME}/lib` by default). In the second case the `-pglibdir` option should be added to the SQL-gen script (see the full options list below) 2. Run SQL-gen script (specific for an OS). The script generates two SQL scripts: bingo_install.sql bingo_uninstall.sql The `bingo_install.sql` installs all the functions and procedures related to the Bingo cartridge. 3. Execute the script from the database. Usually it can be done by the following command: psql -U $admin -d $database -f bingo_install.sql There are several important notes below. **Note:** The installation can be done only to an existing database. **Note:** You must have an admin role to install bingo cartridge on your database. **Note:** The installation script creates a new schema (usually `bingo`),with the default tablespace. Please consider, that the specifying schema will be deleted while calling the uninstall script, thus the installer checks for a schema name and will raise an error if a schema already exists. **Note:** You cannot install Bingo on top of the existing installation. You have to drop the cartridge schema (usually `bingo`) of the existing installation. **Note:** You cannot install Bingo on a different PostgreSQL version, say, if you have PostgreSQL 9.1 and the Bingo library for the version 9.0 then you can get unexpected crashes. ### Linux ### Run the `bingo-pg-install.sh` file located in the root folder of the Bingo installation file set. The help message from the script is the following: Usage: bingo-pg-install.sh [parameters] Parameters: -?, -help Print this help message -libdir path Target directory with the installed bingo_postgres.so (defaut {CURRENT_DIR}/bin/). -schema name Postgres schema name (default "bingo"). -pglibdir Use postgreSQL $libdir option (default "false") Notice: bingo_postgres.so must be placed in the package library directory -y Process default options (default "false") Execute bingo_install.sql for your database. ### Windows ### Run the `bingo-pg-install.bat` file located in the root folder of the Bingo installation file set. The help message from the script is the following: Usage: bingo-pg-install.bat [parameters] Parameters: -?, -help Print this help message -libdir path Target directory with the installed bingo_postgres.dll (defaut {CURRENT_DIR}/bin/). -schema name Postgres schema name (default "bingo"). -pglibdir Use postgreSQL $libdir option (default "false") Notice: bingo_postgres.dll must be placed in the package library directory -y Process default options (default "false") Execute bingo_install.sql for your database. ### Mac OS X ### Run the `bingo-pg-install.sh` file located in the root folder of the Bingo installation file set. The help message from the script is the following: Usage: bingo-pg-install.sh [parameters] Parameters: -?, -help Print this help message -libdir path Target directory with the installed bingo_postgres.dylib (defaut {CURRENT_DIR}/bin/). -schema name Postgres schema name (default "bingo"). -pglibdir Use postgreSQL $libdir option (default "false") Notice: bingo_postgres.dylib must be placed in the package library directory -y Process default options (default "false") Execute bingo_install.sql for your database. ### Examples ### For the most simple installation, the defaults are taken: `CURRENT_DIR/bin` directory for binary, `bingo` for the schema name, `test` for the database name and `postgres` for the admin user. bingo-pg-install.sh psql -U postgres -d test -f bingo_install.sql If you have copied the library to the directory '/home/myself/': bingo-pg-install.sh -libdir /home/myself psql -U postgres -d test -f bingo_install.sql If you have copied the library to the Postges package directory '/usr/lib/postgresql/9.0/lib/' and want to create another `bingo2` schema for storing the procedures : bingo-pg-install.sh -pglibdir -schema bingo2 psql -U postgres -d test -f bingo_install.sql ### Checking the Installation ### To check that the shared library file is loaded properly by Postgres, you can try this simple query: SELECT Bingo.GetVersion(); Uninstalling the Cartridge -------------------------- To uninstall the cartridge, you must: 1. Execute bingo_uninstall.sql (generated on the installation step) for your database. Indigo-indigo-1.2.3/bingo/postgres/bingo-pg-install.bat000066400000000000000000000113671271037650300230130ustar00rootroot00000000000000@rem Copyright (C) 2009-2015 EPAM Systems @rem @rem This file is part of Indigo toolkit. @rem @rem This file may be distributed and/or modified under the terms of the @rem GNU General Public License version 3 as published by the Free Software @rem Foundation and appearing in the file LICENSE.GPL included in the @rem packaging of this file. @rem @rem This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE @rem WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @echo off set libdir=%CD%/lib set schemaname=bingo set libext=".dll" set y= set pglibdir= goto L2 :L1 shift :L2 if "%1" == "" goto L3 if "%1" == "-libdir" goto got_libdir if "%1" == "-schemaname" goto got_schemaname if "%1" == "-y" goto got_y if "%1" == "-pglibdir" goto :got_pglibdir if "%1" == "-help" goto usage if "%1" == "-?" goto usage if "%1" == "/?" goto usage goto badparam :got_libdir shift set libdir=%1 goto L1 :got_schemaname shift set schemaname=%1 goto L1 :got_y set y=1 goto L1 :got_pglibdir set pglibdir=1 goto L1 :badparam echo Unknown parameter: %1 goto end :L3 if "%pglibdir%"=="1" set libdir=$libdir set libdir=%libdir:\=/% echo Target directory : %libdir% echo Schema name : %schemaname% if "%y%"=="1" goto L4 set /p proceed=Proceed (y/N)? if "%proceed%"=="y" goto L4 if "%proceed%"=="Y" goto L4 goto end :L4 @rem Delete install and uninstall scripts if exist bingo_install.sql del bingo_install.sql @rem Generate replace.vbs script echo Dim FileName, OutputFileName, Find, ReplaceWith, FileContents, dFileContents >> replace.vbs echo Find = WScript.Arguments(0) >> replace.vbs echo ReplaceWith = WScript.Arguments(1) >> replace.vbs echo FileName = WScript.Arguments(2) >> replace.vbs echo OutputFileName = WScript.Arguments(3) >> replace.vbs echo. >> replace.vbs echo FileContents = GetFile(FileName) >> replace.vbs echo. >> replace.vbs echo dFileContents = replace(FileContents, Find, ReplaceWith, 1, -1, 1) >> replace.vbs echo. >> replace.vbs echo WriteFile OutputFileName, dFileContents >> replace.vbs echo. >> replace.vbs echo function GetFile(FileName) >> replace.vbs echo Dim FS, FileStream >> replace.vbs echo Set FS = CreateObject("Scripting.FileSystemObject") >> replace.vbs echo on error resume Next >> replace.vbs echo Set FileStream = FS.OpenTextFile(FileName) >> replace.vbs echo GetFile = FileStream.ReadAll >> replace.vbs echo End Function >> replace.vbs echo. >> replace.vbs echo function WriteFile(FileName, Contents) >> replace.vbs echo Dim OutStream, FS >> replace.vbs echo. >> replace.vbs echo on error resume Next >> replace.vbs echo Set FS = CreateObject("Scripting.FileSystemObject") >> replace.vbs echo Set OutStream = FS.OpenTextFile(FileName, 8, True) >> replace.vbs echo OutStream.Write Contents >> replace.vbs echo End Function >> replace.vbs @rem Generate install script cscript //b replace.vbs BINGO_SCHEMANAME %schemaname% sql\bingo_schema.sql.in bingo_install.sql cscript //b replace.vbs BINGO_SCHEMANAME %schemaname% sql\bingo_pg.sql.in bingo_install.sql cscript //b replace.vbs BINGO_PATHNAME %libdir%/bingo_postgres sql\bingo_internal.sql.in bingo_install.sql cscript //b replace.vbs BINGO_PATHNAME %libdir%/bingo_postgres sql\mango_internal.sql.in bingo_install.sql cscript //b replace.vbs BINGO_SCHEMANAME %schemaname% sql\mango_pg.sql.in bingo_install.sql cscript //b replace.vbs BINGO_PATHNAME %libdir%/bingo_postgres sql\ringo_internal.sql.in bingo_install.sql cscript //b replace.vbs BINGO_SCHEMANAME %schemaname% sql\ringo_pg.sql.in bingo_install.sql cscript //b replace.vbs BINGO_PATHNAME %libdir%/bingo_postgres sql\bingo_am.sql.in bingo_install.sql cscript //b replace.vbs BINGO_PATHNAME %libdir%/bingo_postgres sql\bingo_config.sql.in bingo_install.sql @rem Generate uninstall script if exist bingo_uninstall.sql del bingo_uninstall.sql cscript //b replace.vbs BINGO_SCHEMANAME %schemaname% sql\bingo_uninstall.quick.sql.in bingo_uninstall.sql @rem Delete replace.vbs script del replace.vbs goto L5 :L5 @rem Run install script @rem psql... goto end :usage echo Usage: bingo-pg-install.bat [parameters] echo Parameters: echo -?, -help echo Print this help message echo -libdir path echo Target directory with the installed bingo_postgres%libext% (defaut %CD%\bin). echo -schema name echo Postgres schema name (default "bingo"). echo -pglibdir echo Use postgreSQL $libdir option (default "false") echo Notice: bingo_postgres%libext% must be placed in the package library directory echo -y echo Process default options (default "false") goto end :end set libdir= set schemaname= set libext= Indigo-indigo-1.2.3/bingo/postgres/bingo-pg-install.sh000077500000000000000000000056641271037650300226650ustar00rootroot00000000000000#!/bin/sh # Copyright (C) 2009-2015 EPAM Systems # # This file is part of Indigo toolkit. # # This file may be distributed and/or modified under the terms of the # GNU General Public License version 3 as published by the Free Software # Foundation and appearing in the file LICENSE.GPL included in the # packaging of this file. # # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. libdir=$PWD/lib schema_name="bingo" libext=".so" y="0" pglibdir="0" bingo_pg_name="bingo_postgres" if [ -f "lib/bingo_postgres.dylib" ]; then libext=".dylib" bingo_pg_name="bingo_postgres.dylib" fi usage () { echo 'Usage: bingo-pg-install.sh [parameters]' echo 'Parameters:' echo ' -?, -help' echo ' Print this help message' echo ' -libdir path' echo ' Target directory with the installed bingo_postgres'$libext' (defaut {CURRENT_DIR}/bin/).' echo ' -schema name' echo ' Postgres schema name (default "bingo").' echo ' -pglibdir' echo ' Use postgreSQL $libdir option (default "false")' echo ' Notice: bingo_postgres'$libext' must be placed in the package library directory' echo ' -y' echo ' Process default options (default "false")' } while [ "$#" != 0 ]; do case "$1" in -help | '-?' | '/?') usage exit 0 ;; -libdir) shift libdir=$1 ;; -schema) shift schema_name=$1 ;; -y) y="1" ;; -pglibdir) pglibdir="1" ;; *) echo "Unknown parameter: $1"; usage; exit -1 esac shift done if [ "$pglibdir" != "0" ]; then libdir='$libdir'; fi echo "Target directory : $libdir"; echo "DBA schema name : $schema_name"; if [ "$y" != "1" ]; then echo "Proceed (y/N)?" read proceed if [ "$proceed" != "y" ] && [ "$proceed" != "Y" ]; then echo 'Aborting'; exit 0; fi fi # Generate install script sed 's,BINGO_SCHEMANAME,'$schema_name',g' bingo_install.sql sed 's,BINGO_PATHNAME,'$libdir'/'$bingo_pg_name',g' >bingo_install.sql sed 's,BINGO_SCHEMANAME,'$schema_name',g' >bingo_install.sql sed 's,BINGO_PATHNAME,'$libdir'/'$bingo_pg_name',g' >bingo_install.sql sed 's,BINGO_SCHEMANAME,'$schema_name',g' >bingo_install.sql sed 's,BINGO_PATHNAME,'$libdir'/'$bingo_pg_name',g' >bingo_install.sql sed 's,BINGO_SCHEMANAME,'$schema_name',g' >bingo_install.sql sed 's,BINGO_PATHNAME,'$libdir'/'$bingo_pg_name',g' >bingo_install.sql sed 's,BINGO_PATHNAME,'$libdir'/'$bingo_pg_name',g' >bingo_install.sql #Generate uninstall script sed 's,BINGO_SCHEMANAME,'$schema_name',g' bingo_uninstall.sql Indigo-indigo-1.2.3/bingo/postgres/sql/000077500000000000000000000000001271037650300177445ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.0/000077500000000000000000000000001271037650300202525ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.0/bingo_am.sql.in000066400000000000000000000123431271037650300231560ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amindexnulls, amsearchnulls, amstorage, amclusterable, amkeytype ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, false, 23 ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/9.1/000077500000000000000000000000001271037650300202535ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.1/bingo_am.sql.in000066400000000000000000000126501271037650300231600ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_buildempty(internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, ambuildempty, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amsearchnulls, amstorage, amclusterable, amkeytype, amcanorderbyop, ampredlocks ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 'bingo_buildempty(internal)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, 23, false, false ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/9.2/000077500000000000000000000000001271037650300202545ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.2/bingo_am.sql.in000066400000000000000000000127311271037650300231610ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_buildempty(internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, ambuildempty, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amsearcharray, amsearchnulls, amstorage, amclusterable, amkeytype, amcanorderbyop, ampredlocks, amcanreturn ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 'bingo_buildempty(internal)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, false, 23, false, false, 0 ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/9.3/000077500000000000000000000000001271037650300202555ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.3/bingo_am.sql.in000066400000000000000000000127311271037650300231620ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_buildempty(internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, ambuildempty, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amsearcharray, amsearchnulls, amstorage, amclusterable, amkeytype, amcanorderbyop, ampredlocks, amcanreturn ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 'bingo_buildempty(internal)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, false, 23, false, false, 0 ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/9.4/000077500000000000000000000000001271037650300202565ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.4/bingo_am.sql.in000066400000000000000000000127311271037650300231630ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_buildempty(internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, ambuildempty, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amsearcharray, amsearchnulls, amstorage, amclusterable, amkeytype, amcanorderbyop, ampredlocks, amcanreturn ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 'bingo_buildempty(internal)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, false, 23, false, false, 0 ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/9.5/000077500000000000000000000000001271037650300202575ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/9.5/bingo_am.sql.in000066400000000000000000000127311271037650300231640ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION bingo_build(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_buildempty(internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_insert(internal, internal, internal, internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_beginscan(internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_gettuple(internal, internal) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_rescan(internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_endscan(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_markpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_restrpos(internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_bulkdelete(internal, internal, internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_vacuumcleanup(internal, internal) RETURNS internal AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_options(_text, bool) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; CREATE OR REPLACE FUNCTION bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT VOLATILE; --delete from pg_am where amname='bingo_idx' INSERT INTO pg_am( amname, aminsert, ambeginscan, amgettuple, amgetbitmap, amrescan, amendscan, ammarkpos, amrestrpos, ambuild, ambulkdelete, amvacuumcleanup, amcostestimate, amoptions, ambuildempty, amstrategies, amsupport, amcanorder, amcanbackward, amcanunique, amcanmulticol, amoptionalkey, amsearcharray, amsearchnulls, amstorage, amclusterable, amkeytype, amcanorderbyop, ampredlocks, amcanreturn ) VALUES ( 'bingo_idx', 'bingo_insert(internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_beginscan(internal, internal, internal)'::regprocedure::oid, 'bingo_gettuple(internal, internal)'::regprocedure::oid, 0, 'bingo_rescan(internal, internal)'::regprocedure::oid, 'bingo_endscan(internal)'::regprocedure::oid, 'bingo_markpos(internal)'::regprocedure::oid, 'bingo_restrpos(internal)'::regprocedure::oid, 'bingo_build(internal, internal, internal)'::regprocedure::oid, 'bingo_bulkdelete(internal, internal, internal, internal)'::regprocedure::oid, 'bingo_vacuumcleanup(internal, internal)'::regprocedure::oid, 'bingo_costestimate(internal, internal, internal, internal, internal, internal, internal, internal)'::regprocedure::oid, 'bingo_options(_text, bool)'::regprocedure::oid, 'bingo_buildempty(internal)'::regprocedure::oid, 7, 7, false, true, false, false, false, false, false, false, false, 23, false, false, 0 ); --**************************** MANGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS molecule FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, sub), OPERATOR 2 public.@ (text, exact), OPERATOR 3 public.@ (text, smarts), OPERATOR 4 public.@ (text, gross), OPERATOR 5 public.< (text, mass), OPERATOR 6 public.> (text, mass), OPERATOR 7 public.@ (text, sim), FUNCTION 1 matchSub(text, sub), FUNCTION 2 matchExact(text, exact), FUNCTION 3 matchSmarts(text, smarts), FUNCTION 4 matchGross(text, gross), FUNCTION 5 _match_mass_less(text, mass), FUNCTION 6 _match_mass_great(text, mass), FUNCTION 7 matchSim(text, sim); CREATE OPERATOR CLASS bmolecule FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, sub), OPERATOR 2 public.@ (bytea, exact), OPERATOR 3 public.@ (bytea, smarts), OPERATOR 4 public.@ (bytea, gross), OPERATOR 5 public.< (bytea, mass), OPERATOR 6 public.> (bytea, mass), OPERATOR 7 public.@ (bytea, sim), FUNCTION 1 matchSub(bytea, sub), FUNCTION 2 matchExact(bytea, exact), FUNCTION 3 matchSmarts(bytea, smarts), FUNCTION 4 matchGross(bytea, gross), FUNCTION 5 _match_mass_less(bytea, mass), FUNCTION 6 _match_mass_great(bytea, mass), FUNCTION 7 matchSim(bytea, sim); --**************************** RINGO OPERATOR CLASS ********************* CREATE OPERATOR CLASS reaction FOR TYPE text USING bingo_idx AS OPERATOR 1 public.@ (text, rsub), OPERATOR 2 public.@ (text, rexact), OPERATOR 3 public.@ (text, rsmarts), FUNCTION 1 matchRSub(text, rsub), FUNCTION 2 matchRExact(text, rexact), FUNCTION 3 matchRSmarts(text, rsmarts); CREATE OPERATOR CLASS breaction FOR TYPE bytea USING bingo_idx AS OPERATOR 1 public.@ (bytea, rsub), OPERATOR 2 public.@ (bytea, rexact), OPERATOR 3 public.@ (bytea, rsmarts), FUNCTION 1 matchRSub(bytea, rsub), FUNCTION 2 matchRExact(bytea, rexact), FUNCTION 3 matchRSmarts(bytea, rsmarts); Indigo-indigo-1.2.3/bingo/postgres/sql/common/000077500000000000000000000000001271037650300212345ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/sql/common/bingo_config.sql.in000066400000000000000000000034221271037650300250060ustar00rootroot00000000000000 create table bingo_config(cname varchar(255), cvalue varchar(255)); insert into bingo_config(cname, cvalue) values ('TREAT_X_AS_PSEUDOATOM', '0'); insert into bingo_config(cname, cvalue) values ('IGNORE_CLOSING_BOND_DIRECTION_MISMATCH', '0'); insert into bingo_config(cname, cvalue) values ('IGNORE_CISTRANS_ERRORS', '0'); insert into bingo_config(cname, cvalue) values ('IGNORE_STEREOCENTER_ERRORS', '0'); insert into bingo_config(cname, cvalue) values ('ALLOW_NON_UNIQUE_DEAROMATIZATION', '0'); insert into bingo_config(cname, cvalue) values ('ZERO_UNKNOWN_AROMATIC_HYDROGENS', '0'); insert into bingo_config(cname, cvalue) values ('STEREOCHEMISTRY_BIDIRECTIONAL_MODE', '0'); insert into bingo_config(cname, cvalue) values ('STEREOCHEMISTRY_DETECT_HAWORTH_PROJECTION', '0'); insert into bingo_config(cname, cvalue) values ('REJECT_INVALID_STRUCTURES', '0'); insert into bingo_config(cname, cvalue) values ('FP_ORD_SIZE', '25'); insert into bingo_config(cname, cvalue) values ('FP_ANY_SIZE', '15'); insert into bingo_config(cname, cvalue) values ('FP_TAU_SIZE', '10'); insert into bingo_config(cname, cvalue) values ('FP_SIM_SIZE', '8'); insert into bingo_config(cname, cvalue) values ('SUB_SCREENING_MAX_BITS', '8'); insert into bingo_config(cname, cvalue) values ('SIM_SCREENING_PASS_MARK', '128'); insert into bingo_config(cname, cvalue) values ('NTHREADS', '-1'); insert into bingo_config(cname, cvalue) values ('TIMEOUT', '60000'); create table bingo_tau_config(rule_idx integer, tau_beg text, tau_end text); insert into bingo_tau_config(rule_idx, tau_beg, tau_end) values (1, 'N,O,P,S,As,Se,Sb,Te', 'N,O,P,S,As,Se,Sb,Te'); insert into bingo_tau_config(rule_idx, tau_beg, tau_end) values (2, '0C', 'N,O,P,S'); insert into bingo_tau_config(rule_idx, tau_beg, tau_end) values (3, '1C', 'N,O'); COMMIT; Indigo-indigo-1.2.3/bingo/postgres/sql/common/bingo_internal.sql.in000066400000000000000000000036411271037650300253600ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION getVersion() RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION importSDF(text, text, text, text) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C VOLATILE; CREATE OR REPLACE FUNCTION importRDF(text, text, text, text) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C VOLATILE; CREATE OR REPLACE FUNCTION importSmiles(text, text, text, text) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C VOLATILE; CREATE OR REPLACE FUNCTION exportSDF(text, text, text, text) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C IMMUTABLE; CREATE OR REPLACE FUNCTION exportRDF(text, text, text, text) RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C IMMUTABLE; CREATE OR REPLACE FUNCTION fileToText(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION fileToBlob(text) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getName(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getIndexStructuresCount(oid) RETURNS integer AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _get_structures_count(oid) RETURNS integer AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _get_block_count(oid) RETURNS integer AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _precache_database(oid, text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _internal_func_check(integer) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _get_profiling_info() RETURNS cstring AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _reset_profiling_info() RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _print_profiling_info() RETURNS void AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; Indigo-indigo-1.2.3/bingo/postgres/sql/common/bingo_pg.sql.in000066400000000000000000000022421271037650300241460ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION getstructurescount(text) RETURNS int AS $$ begin return BINGO_SCHEMANAME._get_structures_count($1::regclass::oid); end; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION getblockcount(text) RETURNS int AS $$ begin return BINGO_SCHEMANAME._get_block_count($1::regclass::oid); end; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION precachedatabase(text, text) RETURNS text AS $$ begin return BINGO_SCHEMANAME._precache_database($1::regclass::oid, $2); end; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION _internal_func_011(integer, text, text) RETURNS void AS $$ BEGIN IF NOT BINGO_SCHEMANAME._internal_func_check($1) THEN RETURN; END IF; INSERT INTO pg_depend (classid, objid, objsubid, refclassid, refobjid, refobjsubid, deptype) VALUES ( 'pg_class'::regclass::oid, $2::regclass::oid, 0, 'pg_class'::regclass::oid, $3::regclass::oid, 0, 'i'); end; $$ LANGUAGE 'plpgsql' SECURITY DEFINER; CREATE OR REPLACE FUNCTION _internal_func_012(integer, text) RETURNS void AS $$ BEGIN IF NOT BINGO_SCHEMANAME._internal_func_check($1) THEN RETURN; END IF; DELETE FROM pg_depend WHERE objid=$2::regclass::oid; end; $$ LANGUAGE 'plpgsql' SECURITY DEFINER; Indigo-indigo-1.2.3/bingo/postgres/sql/common/bingo_schema.sql.in000066400000000000000000000001131271037650300247730ustar00rootroot00000000000000BEGIN; CREATE SCHEMA BINGO_SCHEMANAME; SET search_path = BINGO_SCHEMANAME; Indigo-indigo-1.2.3/bingo/postgres/sql/common/bingo_uninstall.quick.sql.in000066400000000000000000000001411271037650300266600ustar00rootroot00000000000000BEGIN; drop schema BINGO_SCHEMANAME cascade; delete from pg_am where amname='bingo_idx'; COMMIT; Indigo-indigo-1.2.3/bingo/postgres/sql/common/mango_internal.sql.in000066400000000000000000000117121271037650300253610ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION getWeight(text, text) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getWeight(bytea, text) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getMass(text) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getMass(bytea) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION smiles(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION smiles(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION cansmiles(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION cansmiles(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION molfile(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION molfile(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION cml(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION cml(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION checkMolecule(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION checkMolecule(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION gross(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION gross(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION fingerprint(text, text) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION fingerprint(bytea, text) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION compactmolecule(text, boolean) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION compactmolecule(bytea, boolean) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION inchi(text, text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION inchi(bytea, text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION inchikey(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _sub_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _sub_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _smarts_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _smarts_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _exact_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _exact_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _gross_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _gross_internal(text, text, bytea) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; --******************* MASS ******************* CREATE TYPE mass; CREATE OR REPLACE FUNCTION _mass_in(cstring) RETURNS mass AS 'BINGO_PATHNAME' LANGUAGE C IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION _mass_out(mass) RETURNS cstring AS 'BINGO_PATHNAME' LANGUAGE C IMMUTABLE STRICT; CREATE TYPE mass ( internallength = variable, input = _mass_in, output = _mass_out ); CREATE OR REPLACE FUNCTION _match_mass_great(text, mass) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _match_mass_great(bytea, mass) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _match_mass_less(text, mass) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _match_mass_less(bytea, mass) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; --******************* SIMILARITY ******************* CREATE OR REPLACE FUNCTION getSimilarity(text, text, text) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION getSimilarity(bytea, text, text) RETURNS real AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE TYPE sim AS (min_bound real, max_bound real, query_mol text, query_options text); CREATE OR REPLACE FUNCTION _sim_internal(real, real, text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _sim_internal(real, real, text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; Indigo-indigo-1.2.3/bingo/postgres/sql/common/mango_pg.sql.in000066400000000000000000000116201271037650300241510ustar00rootroot00000000000000CREATE TYPE sub AS (query_mol text, query_options text); CREATE OR REPLACE FUNCTION matchSub(text, sub) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._sub_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchSub(bytea, sub) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._sub_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE TYPE exact AS (query_mol text, query_options text); CREATE OR REPLACE FUNCTION matchExact(text, exact) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._exact_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchExact(bytea, exact) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._exact_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE TYPE smarts AS (query_mol text, query_options text); CREATE OR REPLACE FUNCTION matchSmarts(text, smarts) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._smarts_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchSmarts(bytea, smarts) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._smarts_internal($2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE TYPE gross AS (sign text, query_mol text); CREATE OR REPLACE FUNCTION matchGross(bytea, gross) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._gross_internal($2.sign, $2.query_mol, $1); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchGross(text, gross) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._gross_internal($2.sign, $2.query_mol, $1); END; $$ LANGUAGE 'plpgsql'; CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = sub, PROCEDURE = matchSub, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = sub, PROCEDURE = matchSub, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = exact, PROCEDURE = matchExact, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = exact, PROCEDURE = matchExact, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = smarts, PROCEDURE = matchSmarts, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = smarts, PROCEDURE = matchSmarts, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = gross, PROCEDURE = matchGross, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = gross, PROCEDURE = matchGross, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); --******************* MASS ******************* CREATE OPERATOR public.< ( LEFTARG = text, RIGHTARG = mass, PROCEDURE = _match_mass_less, COMMUTATOR = '<', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.< ( LEFTARG = bytea, RIGHTARG = mass, PROCEDURE = _match_mass_less, COMMUTATOR = '<', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.> ( LEFTARG = text, RIGHTARG = mass, PROCEDURE = _match_mass_great, COMMUTATOR = '>', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.> ( LEFTARG = bytea, RIGHTARG = mass, PROCEDURE = _match_mass_great, COMMUTATOR = '>', RESTRICT = contsel, JOIN = contjoinsel ); --******************* SIMILARITY ******************* CREATE OR REPLACE FUNCTION matchSim(text, sim) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._sim_internal($2.min_bound, $2.max_bound, $2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchSim(bytea, sim) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._sim_internal($2.min_bound, $2.max_bound, $2.query_mol, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = sim, PROCEDURE = matchSim, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = sim, PROCEDURE = matchSim, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); Indigo-indigo-1.2.3/bingo/postgres/sql/common/ringo_internal.sql.in000066400000000000000000000043761271037650300254060ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION aam(text, text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION aam(bytea, text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rxnfile(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rxnfile(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rcml(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rcml(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION checkReaction(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION checkReaction(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rsmiles(text) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rsmiles(bytea) RETURNS text AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rfingerprint(text, text) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION rfingerprint(bytea, text) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION compactreaction(text, boolean) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION compactreaction(bytea, boolean) RETURNS bytea AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rsub_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rsub_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rsmarts_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rsmarts_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rexact_internal(text, text, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; CREATE OR REPLACE FUNCTION _rexact_internal(text, bytea, text) RETURNS boolean AS 'BINGO_PATHNAME' LANGUAGE C STRICT IMMUTABLE; Indigo-indigo-1.2.3/bingo/postgres/sql/common/ringo_pg.sql.in000066400000000000000000000050041271037650300241650ustar00rootroot00000000000000CREATE TYPE rsub AS (query_reaction text, query_options text); CREATE OR REPLACE FUNCTION matchRSub(text, rsub) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rsub_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchRSub(bytea, rsub) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rsub_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE TYPE rexact AS (query_reaction text, query_options text); CREATE OR REPLACE FUNCTION matchRExact(text, rexact) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rexact_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchRExact(bytea, rexact) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rexact_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE TYPE rsmarts AS (query_reaction text, query_options text); CREATE OR REPLACE FUNCTION matchRSmarts(text, rsmarts) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rsmarts_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION matchRSmarts(bytea, rsmarts) RETURNS boolean AS $$ BEGIN RETURN BINGO_SCHEMANAME._rsmarts_internal($2.query_reaction, $1, $2.query_options); END; $$ LANGUAGE 'plpgsql'; CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = rsub, PROCEDURE = matchRSub, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = rsub, PROCEDURE = matchRSub, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = rexact, PROCEDURE = matchRExact, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = rexact, PROCEDURE = matchRExact, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = text, RIGHTARG = rsmarts, PROCEDURE = matchRSmarts, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); CREATE OPERATOR public.@ ( LEFTARG = bytea, RIGHTARG = rsmarts, PROCEDURE = matchRSmarts, COMMUTATOR = '@', RESTRICT = contsel, JOIN = contjoinsel ); Indigo-indigo-1.2.3/bingo/postgres/src/000077500000000000000000000000001271037650300177345ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/000077500000000000000000000000001271037650300210175ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_build.cpp000066400000000000000000000105141271037650300244670ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "access/htup.h" #include "utils/rel.h" #include "utils/relcache.h" #include "storage/bufmgr.h" #include "catalog/index.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "pg_bingo_context.h" #include "bingo_pg_build.h" #include "bingo_pg_common.h" #include "bingo_pg_text.h" #include "base_cpp/tlscont.h" using namespace indigo; extern "C" { #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif BINGO_FUNCTION_EXPORT(bingo_build); BINGO_FUNCTION_EXPORT(bingo_buildempty); } static void bingoIndexCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state); //#include //void error_handler(int i) { // elog(ERROR, "query was cancelled"); //} /* * Bingo build the index */ Datum bingo_build(PG_FUNCTION_ARGS) { Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); // BlockNumber relpages; IndexBuildResult *result = 0; double reltuples = 0; // signal(SIGINT, &error_handler); elog(DEBUG1, "bingo: build: start building index"); /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. */ if (RelationGetNumberOfBlocks(index) != 0) elog(ERROR, "index \"%s\" already contains data", RelationGetRelationName(index)); // /* // * Estimate the number of rows currently present in the table // */ // estimate_rel_size(heap, NULL, &relpages, &reltuples); PG_BINGO_BEGIN { /* * Initialize the bingo index metadata page and initial blocks */ BingoPgWrapper func_namespace; const char* schema_name = func_namespace.getFuncNameSpace(fcinfo->flinfo->fn_oid); BingoPgWrapper rel_namespace; const char* index_schema = rel_namespace.getRelNameSpace(index->rd_id); BingoPgBuild build_engine(index, schema_name, index_schema, true); /* * Do the heap scan and build index */ BINGO_PG_TRY { reltuples = IndexBuildHeapScan(heap, index, indexInfo, true, bingoIndexCallback, (void *) &build_engine); } BINGO_PG_HANDLE(throw BingoPgError("Error while executing build index procedure %s", message)); build_engine.flush(); /* * Return statistics */ result = (IndexBuildResult *) palloc(sizeof (IndexBuildResult)); result->heap_tuples = reltuples; /* * Index is always cost cheaper so set tuples number 1 */ result->index_tuples = 1; } PG_BINGO_END PG_RETURN_POINTER(result); } /* * Bingo build callback. Accepts heap relation. */ static void bingoIndexCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state) { /* * Skip inserting null tuples */ if(*isnull) return; /* * Get bingo state */ BingoPgBuild &build_engine = *(BingoPgBuild *) state; /* * Insert a new structure (single or parallel) */ PG_BINGO_BEGIN { build_engine.insertStructure(&htup->t_self, values[0]); } PG_BINGO_END } Datum bingo_buildempty(PG_FUNCTION_ARGS) { Relation index = (Relation) PG_GETARG_POINTER(0); elog(NOTICE, "start bingo empty build "); /* * We expect to be called exactly once for any index relation. If that's * not the case, big trouble's what we have. */ if (RelationGetNumberOfBlocks(index) != 0) elog(ERROR, "index \"%s\" already contains data", RelationGetRelationName(index)); // /* // * Estimate the number of rows currently present in the table // */ // estimate_rel_size(heap, NULL, &relpages, &reltuples); PG_BINGO_BEGIN { /* * Initialize the bingo index metadata page and initial blocks */ BingoPgWrapper func_namespace; const char* schema_name = func_namespace.getFuncNameSpace(fcinfo->flinfo->fn_oid); BingoPgWrapper rel_namespace; const char* index_schema = rel_namespace.getRelNameSpace(index->rd_id); BingoPgBuild build_engine(index, schema_name, index_schema, true); } PG_BINGO_END PG_RETURN_VOID(); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_costestimate.c000077500000000000000000000567451271037650300255570ustar00rootroot00000000000000#include #include #include "postgres.h" #include "fmgr.h" #include "nodes/relation.h" #include "optimizer/cost.h" /* #include "catalog/index.h" #include "access/sysattr.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "executor/executor.h" #include "mb/pg_wchar.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/plancat.h" #include "optimizer/predtest.h" #include "optimizer/restrictinfo.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" #include "parser/parsetree.h" #include "utils/builtins.h" #include "utils/bytea.h" #include "utils/date.h" #include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/nabstime.h" #include "utils/pg_locale.h" #include "utils/selfuncs.h" #include "utils/spccache.h" #include "utils/syscache.h" #include "utils/tqual.h" */ /*------------------------------------------------------------------------- * * Index cost estimation functions * * genericcostestimate is a general-purpose estimator for use when we * don't have any better idea about how to estimate. Index-type-specific * knowledge can be incorporated in the type-specific routines. * * One bit of index-type-specific knowledge we can relatively easily use * in genericcostestimate is the estimate of the number of index tuples * visited. If numIndexTuples is not 0 then it is used as the estimate, * otherwise we compute a generic estimate. * *------------------------------------------------------------------------- */ static void genericcostestimate(PlannerInfo *root, IndexOptInfo *index, List *indexQuals, RelOptInfo *outer_rel, double numIndexTuples, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation) { double numIndexPages; double num_sa_scans; double num_outer_scans; double num_scans; QualCost index_qual_cost; double qual_op_cost; double qual_arg_cost; double spc_random_page_cost; List *selectivityQuals; ListCell *l; /*---------- * If the index is partial, AND the index predicate with the explicitly * given indexquals to produce a more accurate idea of the index * selectivity. However, we need to be careful not to insert redundant * clauses, because clauselist_selectivity() is easily fooled into * computing a too-low selectivity estimate. Our approach is to add * only the index predicate clause(s) that cannot be proven to be implied * by the given indexquals. This successfully handles cases such as a * qual "x = 42" used with a partial index "WHERE x >= 40 AND x < 50". * There are many other cases where we won't detect redundancy, leading * to a too-low selectivity estimate, which will bias the system in favor * of using partial indexes where possible. That is not necessarily bad * though. * * Note that indexQuals contains RestrictInfo nodes while the indpred * does not. This is OK for both predicate_implied_by() and * clauselist_selectivity(). *---------- */ if (index->indpred != NIL) { List *predExtraQuals = NIL; foreach(l, index->indpred) { Node *predQual = (Node *) lfirst(l); List *oneQual = list_make1(predQual); if (!predicate_implied_by(oneQual, indexQuals)) predExtraQuals = list_concat(predExtraQuals, oneQual); } /* list_concat avoids modifying the passed-in indexQuals list */ selectivityQuals = list_concat(predExtraQuals, indexQuals); } else selectivityQuals = indexQuals; /* * Check for ScalarArrayOpExpr index quals, and estimate the number of * index scans that will be performed. */ num_sa_scans = 1; foreach(l, indexQuals) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); if (IsA(rinfo->clause, ScalarArrayOpExpr)) { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause; int alength = estimate_array_length(lsecond(saop->args)); if (alength > 1) num_sa_scans *= alength; } } /* Estimate the fraction of main-table tuples that will be visited */ *indexSelectivity = clauselist_selectivity(root, selectivityQuals, index->rel->relid, JOIN_INNER, NULL); /* * If caller didn't give us an estimate, estimate the number of index * tuples that will be visited. We do it in this rather peculiar-looking * way in order to get the right answer for partial indexes. */ if (numIndexTuples <= 0.0) { numIndexTuples = *indexSelectivity * index->rel->tuples; /* * The above calculation counts all the tuples visited across all * scans induced by ScalarArrayOpExpr nodes. We want to consider the * average per-indexscan number, so adjust. This is a handy place to * round to integer, too. (If caller supplied tuple estimate, it's * responsible for handling these considerations.) */ numIndexTuples = rint(numIndexTuples / num_sa_scans); } /* * We can bound the number of tuples by the index size in any case. Also, * always estimate at least one tuple is touched, even when * indexSelectivity estimate is tiny. */ /* if (numIndexTuples > index->tuples) numIndexTuples = index->tuples;a if (numIndexTuples < 1.0) */ numIndexTuples = 1.0; /* * Estimate the number of index pages that will be retrieved. * * We use the simplistic method of taking a pro-rata fraction of the total * number of index pages. In effect, this counts only leaf pages and not * any overhead such as index metapage or upper tree levels. In practice * this seems a better approximation than charging for access to the upper * levels, perhaps because those tend to stay in cache under load. */ /* if (index->pages > 1 && index->tuples > 1) numIndexPages = ceil(numIndexTuples * index->pages / index->tuples); else */ numIndexPages = 1.0; /* fetch estimated page cost for schema containing index */ get_tablespace_page_costs(index->reltablespace, &spc_random_page_cost, NULL); /* * Now compute the disk access costs. * * The above calculations are all per-index-scan. However, if we are in a * nestloop inner scan, we can expect the scan to be repeated (with * different search keys) for each row of the outer relation. Likewise, * ScalarArrayOpExpr quals result in multiple index scans. This creates * the potential for cache effects to reduce the number of disk page * fetches needed. We want to estimate the average per-scan I/O cost in * the presence of caching. * * We use the Mackert-Lohman formula (see costsize.c for details) to * estimate the total number of page fetches that occur. While this * wasn't what it was designed for, it seems a reasonable model anyway. * Note that we are counting pages not tuples anymore, so we take N = T = * index size, as if there were one "tuple" per page. */ if (outer_rel != NULL && outer_rel->rows > 1) { num_outer_scans = outer_rel->rows; num_scans = num_sa_scans * num_outer_scans; } else { num_outer_scans = 1; num_scans = num_sa_scans; } if (num_scans > 1) { double pages_fetched; /* total page fetches ignoring cache effects */ pages_fetched = numIndexPages * num_scans; /* use Mackert and Lohman formula to adjust for cache effects */ pages_fetched = index_pages_fetched(pages_fetched, index->pages, (double) index->pages, root); /* * Now compute the total disk access cost, and then report a pro-rated * share for each outer scan. (Don't pro-rate for ScalarArrayOpExpr, * since that's internal to the indexscan.) */ *indexTotalCost = (pages_fetched * spc_random_page_cost) / num_outer_scans; } else { /* * For a single index scan, we just charge spc_random_page_cost per * page touched. */ *indexTotalCost = numIndexPages * spc_random_page_cost; } /* * A difficulty with the leaf-pages-only cost approach is that for small * selectivities (eg, single index tuple fetched) all indexes will look * equally attractive because we will estimate exactly 1 leaf page to be * fetched. All else being equal, we should prefer physically smaller * indexes over larger ones. (An index might be smaller because it is * partial or because it contains fewer columns; presumably the other * columns in the larger index aren't useful to the query, or the larger * index would have better selectivity.) * * We can deal with this by adding a very small "fudge factor" that * depends on the index size. The fudge factor used here is one * spc_random_page_cost per 100000 index pages, which should be small * enough to not alter index-vs-seqscan decisions, but will prevent * indexes of different sizes from looking exactly equally attractive. */ *indexTotalCost += index->pages * spc_random_page_cost / 100000.0; //*indexTotalCost += 1000; /* * CPU cost: any complex expressions in the indexquals will need to be * evaluated once at the start of the scan to reduce them to runtime keys * to pass to the index AM (see nodeIndexscan.c). We model the per-tuple * CPU costs as cpu_index_tuple_cost plus one cpu_operator_cost per * indexqual operator. Because we have numIndexTuples as a per-scan * number, we have to multiply by num_sa_scans to get the correct result * for ScalarArrayOpExpr cases. * * Note: this neglects the possible costs of rechecking lossy operators * and OR-clause expressions. Detecting that that might be needed seems * more expensive than it's worth, though, considering all the other * inaccuracies here ... */ cost_qual_eval(&index_qual_cost, indexQuals, root); qual_op_cost = cpu_operator_cost * list_length(indexQuals); qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple - qual_op_cost; if (qual_arg_cost < 0) /* just in case... */ qual_arg_cost = 0; if(indexStartupCost) *indexStartupCost = qual_arg_cost; *indexTotalCost += qual_arg_cost; *indexTotalCost += numIndexTuples * num_sa_scans * (cpu_index_tuple_cost + qual_op_cost); /* * We also add a CPU-cost component to represent the general costs of * starting an indexscan, such as analysis of btree index keys and initial * tree descent. This is estimated at 100x cpu_operator_cost, which is a * bit arbitrary but seems the right order of magnitude. (As noted above, * we don't charge any I/O for touching upper tree levels, but charging * nothing at all has been found too optimistic.) * * Although this is startup cost with respect to any one scan, we add it * to the "total" cost component because it's only very interesting in the * many-ScalarArrayOpExpr-scan case, and there it will be paid over the * life of the scan node. */ *indexTotalCost += num_sa_scans * 100.0 * cpu_operator_cost; /* * Generic assumption about index correlation: there isn't any. */ *indexCorrelation = -1.0; } #if PG_VERSION_NUM / 100 >= 902 static List * add_predicate_to_quals(IndexOptInfo *index, List *indexQuals) { List *predExtraQuals = NIL; ListCell *lc; if (index->indpred == NIL) return indexQuals; foreach(lc, index->indpred) { Node *predQual = (Node *) lfirst(lc); List *oneQual = list_make1(predQual); if (!predicate_implied_by(oneQual, indexQuals)) predExtraQuals = list_concat(predExtraQuals, oneQual); } /* list_concat avoids modifying the passed-in indexQuals list */ return list_concat(predExtraQuals, indexQuals); } static void genericcostestimate92(PlannerInfo *root, IndexPath *path, double loop_count, double numIndexTuples, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation) { IndexOptInfo *index = path->indexinfo; List *indexQuals = path->indexquals; List *indexOrderBys = path->indexorderbys; double numIndexPages; double num_sa_scans; double num_outer_scans; double num_scans; QualCost index_qual_cost; double qual_op_cost; double qual_arg_cost; double spc_random_page_cost; List *selectivityQuals; ListCell *l; /* * If the index is partial, AND the index predicate with the explicitly * given indexquals to produce a more accurate idea of the index * selectivity. */ selectivityQuals = add_predicate_to_quals(index, indexQuals); /* * Check for ScalarArrayOpExpr index quals, and estimate the number of * index scans that will be performed. */ num_sa_scans = 1; foreach(l, indexQuals) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); if (IsA(rinfo->clause, ScalarArrayOpExpr)) { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause; int alength = estimate_array_length(lsecond(saop->args)); if (alength > 1) num_sa_scans *= alength; } } /* Estimate the fraction of main-table tuples that will be visited */ *indexSelectivity = clauselist_selectivity(root, selectivityQuals, index->rel->relid, JOIN_INNER, NULL); /* * If caller didn't give us an estimate, estimate the number of index * tuples that will be visited. We do it in this rather peculiar-looking * way in order to get the right answer for partial indexes. */ if (numIndexTuples <= 0.0) { numIndexTuples = *indexSelectivity * index->rel->tuples; /* * The above calculation counts all the tuples visited across all * scans induced by ScalarArrayOpExpr nodes. We want to consider the * average per-indexscan number, so adjust. This is a handy place to * round to integer, too. (If caller supplied tuple estimate, it's * responsible for handling these considerations.) */ numIndexTuples = rint(numIndexTuples / num_sa_scans); } numIndexTuples = 1.0; /* * We can bound the number of tuples by the index size in any case. Also, * always estimate at least one tuple is touched, even when * indexSelectivity estimate is tiny. */ if (numIndexTuples > index->tuples) numIndexTuples = index->tuples; if (numIndexTuples < 1.0) numIndexTuples = 1.0; /* * Estimate the number of index pages that will be retrieved. * * We use the simplistic method of taking a pro-rata fraction of the total * number of index pages. In effect, this counts only leaf pages and not * any overhead such as index metapage or upper tree levels. In practice * this seems a better approximation than charging for access to the upper * levels, perhaps because those tend to stay in cache under load. */ /* if (index->pages > 1 && index->tuples > 1) numIndexPages = ceil(numIndexTuples * index->pages / index->tuples); else */ numIndexPages = 1.0; /* fetch estimated page cost for schema containing index */ get_tablespace_page_costs(index->reltablespace, &spc_random_page_cost, NULL); /* * Now compute the disk access costs. * * The above calculations are all per-index-scan. However, if we are in a * nestloop inner scan, we can expect the scan to be repeated (with * different search keys) for each row of the outer relation. Likewise, * ScalarArrayOpExpr quals result in multiple index scans. This creates * the potential for cache effects to reduce the number of disk page * fetches needed. We want to estimate the average per-scan I/O cost in * the presence of caching. * * We use the Mackert-Lohman formula (see costsize.c for details) to * estimate the total number of page fetches that occur. While this * wasn't what it was designed for, it seems a reasonable model anyway. * Note that we are counting pages not tuples anymore, so we take N = T = * index size, as if there were one "tuple" per page. */ num_outer_scans = loop_count; num_scans = num_sa_scans * num_outer_scans; if (num_scans > 1) { double pages_fetched; /* total page fetches ignoring cache effects */ pages_fetched = numIndexPages * num_scans; /* use Mackert and Lohman formula to adjust for cache effects */ pages_fetched = index_pages_fetched(pages_fetched, index->pages, (double) index->pages, root); /* * Now compute the total disk access cost, and then report a pro-rated * share for each outer scan. (Don't pro-rate for ScalarArrayOpExpr, * since that's internal to the indexscan.) */ *indexTotalCost = (pages_fetched * spc_random_page_cost) / num_outer_scans; } else { /* * For a single index scan, we just charge spc_random_page_cost per * page touched. */ *indexTotalCost = numIndexPages * spc_random_page_cost; } /* * A difficulty with the leaf-pages-only cost approach is that for small * selectivities (eg, single index tuple fetched) all indexes will look * equally attractive because we will estimate exactly 1 leaf page to be * fetched. All else being equal, we should prefer physically smaller * indexes over larger ones. (An index might be smaller because it is * partial or because it contains fewer columns; presumably the other * columns in the larger index aren't useful to the query, or the larger * index would have better selectivity.) * * We can deal with this by adding a very small "fudge factor" that * depends on the index size. The fudge factor used here is one * spc_random_page_cost per 10000 index pages, which should be small * enough to not alter index-vs-seqscan decisions, but will prevent * indexes of different sizes from looking exactly equally attractive. */ *indexTotalCost += index->pages * spc_random_page_cost / 100000.0; /* * CPU cost: any complex expressions in the indexquals will need to be * evaluated once at the start of the scan to reduce them to runtime keys * to pass to the index AM (see nodeIndexscan.c). We model the per-tuple * CPU costs as cpu_index_tuple_cost plus one cpu_operator_cost per * indexqual operator. Because we have numIndexTuples as a per-scan * number, we have to multiply by num_sa_scans to get the correct result * for ScalarArrayOpExpr cases. Similarly add in costs for any index * ORDER BY expressions. * * Note: this neglects the possible costs of rechecking lossy operators * and OR-clause expressions. Detecting that that might be needed seems * more expensive than it's worth, though, considering all the other * inaccuracies here ... */ cost_qual_eval(&index_qual_cost, indexQuals, root); qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple; cost_qual_eval(&index_qual_cost, indexOrderBys, root); qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple; qual_op_cost = cpu_operator_cost * (list_length(indexQuals) + list_length(indexOrderBys)); qual_arg_cost -= qual_op_cost; if (qual_arg_cost < 0) /* just in case... */ qual_arg_cost = 0; *indexStartupCost = qual_arg_cost; *indexTotalCost += qual_arg_cost; *indexTotalCost += numIndexTuples * num_sa_scans * (cpu_index_tuple_cost + qual_op_cost); /* * We also add a CPU-cost component to represent the general costs of * starting an indexscan, such as analysis of btree index keys and initial * tree descent. This is estimated at 100x cpu_operator_cost, which is a * bit arbitrary but seems the right order of magnitude. (As noted above, * we don't charge any I/O for touching upper tree levels, but charging * nothing at all has been found too optimistic.) * * Although this is startup cost with respect to any one scan, we add it * to the "total" cost component because it's only very interesting in the * many-ScalarArrayOpExpr-scan case, and there it will be paid over the * life of the scan node. */ *indexTotalCost += num_sa_scans * 100.0 * cpu_operator_cost; *indexTotalCost = 1.0; /* * Generic assumption about index correlation: there isn't any. */ *indexCorrelation = -1.0; } #endif #if PG_VERSION_NUM / 100 == 904 PGDLLEXPORT PG_FUNCTION_INFO_V1(bingo_costestimate); #else PG_FUNCTION_INFO_V1(bingo_costestimate); PGDLLEXPORT Datum bingo_costestimate(PG_FUNCTION_ARGS); #endif Datum bingo_costestimate(PG_FUNCTION_ARGS) { #if PG_VERSION_NUM / 100 >= 902 PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1); double loop_count = PG_GETARG_FLOAT8(2); Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3); Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4); Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5); double *indexCorrelation = (double *) PG_GETARG_POINTER(6); genericcostestimate92(root, path, loop_count, 1.0, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); #else struct PlannerInfo *root; struct IndexOptInfo *index; struct List *indexQuals; struct RelOptInfo *outer_rel; Cost *indexStartupCost; Cost *indexTotalCost; Selectivity *indexSelectivity ; double *indexCorrelation; root = (PlannerInfo *) PG_GETARG_POINTER(0); index = (IndexOptInfo *) PG_GETARG_POINTER(1); indexQuals = (List *) PG_GETARG_POINTER(2); outer_rel = (RelOptInfo *) PG_GETARG_POINTER(3); indexStartupCost = (Cost *) PG_GETARG_POINTER(4); indexTotalCost = (Cost *) PG_GETARG_POINTER(5); indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6); indexCorrelation = (double *) PG_GETARG_POINTER(7); /* elog(INFO, "bingo cost estimate %f", root->); */ genericcostestimate(root, index, indexQuals, outer_rel, 1.0, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); #endif /* elog(INFO, "start up cost %f", *indexStartupCost); elog(INFO, "start total cost %f", *indexTotalCost); elog(INFO, "correlation %f", *indexCorrelation); elog(INFO, "selectivity %f", *indexSelectivity); *indexStartupCost = 0.0; *indexTotalCost = 1.0; *indexSelectivity = 0.0; *indexCorrelation = 0.0; */ //*indexStartupCost = (Cost)1.0; //indexTotalCost = 1.0; /* elog(INFO, "pointer = %d", root); */ /* *indexSelectivity = clauselist_selectivity(root, indexQuals, index->rel->relid, JOIN_INNER, NULL); */ /* QualCost index_qual_cost; int numIndexPages = 0, numIndexTuples = 0; cost_qual_eval(&index_qual_cost, indexQuals, root); *indexStartupCost = index_qual_cost.startup; *indexTotalCost = seq_page_cost * numIndexPages + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples; */ /* *indexStartupCost = 0.0; *indexTotalCost = 0.1; *indexCorrelation = 0.0; *indexSelectivity = 0.0; */ PG_RETURN_VOID(); } /* Datum bingo_costestimate(PG_FUNCTION_ARGS) { struct PlannerInfo *root; struct IndexOptInfo *index; struct List *indexQuals; struct RelOptInfo *outer_rel; struct Cost *indexStartupCost; struct Cost *indexTotalCost; struct Selectivity *indexSelectivity; double *indexCorrelation; QualCost index_qual_cost; int numIndexPages; int numIndexTuples; root = (PlannerInfo *) PG_GETARG_POINTER(0); index = (IndexOptInfo *) PG_GETARG_POINTER(1); indexQuals = (List *) PG_GETARG_POINTER(2); outer_rel = (RelOptInfo *) PG_GETARG_POINTER(3); indexStartupCost = (Cost *) PG_GETARG_POINTER(4); indexTotalCost = (Cost *) PG_GETARG_POINTER(5); indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6); indexCorrelation = (double *) PG_GETARG_POINTER(7); *indexSelectivity = clauselist_selectivity(root, indexQuals, index->rel->relid, JOIN_INNER, NULL); numIndexPages = 10; numIndexPages = 100; cost_qual_eval(&index_qual_cost, indexQuals, root); *indexStartupCost = index_qual_cost.startup; *indexTotalCost = seq_page_cost * numIndexPages + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples; PG_RETURN_VOID(); } */ //Node *newNodeMacroHolder; char *BufferBlocks;Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_gist.c000066400000000000000000000412351271037650300240020ustar00rootroot00000000000000#include "postgres.h" #include "fmgr.h" // ///* //#include "bingo_postgres.h" //*/ // // // //PG_FUNCTION_INFO_V1(bingo_in); //Datum bingo_in(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_out); //Datum bingo_out(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_compress); //Datum bingo_compress(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_decompress); //Datum bingo_decompress(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_consistent); //Datum bingo_consistent(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_union); //Datum bingo_union(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_same); //Datum bingo_same(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_penalty); //Datum bingo_penalty(PG_FUNCTION_ARGS); // //PG_FUNCTION_INFO_V1(bingo_picksplit); //Datum bingo_picksplit(PG_FUNCTION_ARGS); // //Datum //bingo_in(PG_FUNCTION_ARGS) //{ // elog(ERROR, "not implemented"); // PG_RETURN_DATUM(0); //} // //Datum //bingo_out(PG_FUNCTION_ARGS) //{ // elog(ERROR, "not implemented"); // PG_RETURN_DATUM(0); //} // ///* //static void //makesign(BITVECP sign, TRGM *a) //{ // int4 k, // len = ARRNELEM(a); // trgm *ptr = GETARR(a); // int4 tmp = 0; // // MemSet((void *) sign, 0, sizeof(BITVEC)); // SETBIT(sign, SIGLENBIT); // set last unused bit // for (k = 0; k < len; k++) // { // CPTRGM(((char *) &tmp), ptr + k); // HASH(sign, tmp); // } //} //*/ // //Datum //bingo_compress(PG_FUNCTION_ARGS) //{ ///* // GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); // GISTENTRY *retval = entry; // // if (entry->leafkey) // { /* trgm */ // /*TRGM *res; // text *val = DatumGetTextP(entry->key); // // res = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); // retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); // gistentryinit(*retval, PointerGetDatum(res), // entry->rel, entry->page, // entry->offset, FALSE); // } // else if (ISSIGNKEY(DatumGetPointer(entry->key)) && // !ISALLTRUE(DatumGetPointer(entry->key))) // { // int4 i, // len; // TRGM *res; // BITVECP sign = GETSIGN(DatumGetPointer(entry->key)); // // LOOPBYTE // { // if ((sign[i] & 0xff) != 0xff) // PG_RETURN_POINTER(retval); // } // // len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0); // res = (TRGM *) palloc(len); // SET_VARSIZE(res, len); // res->flag = SIGNKEY | ALLISTRUE; // // retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); // gistentryinit(*retval, PointerGetDatum(res), // entry->rel, entry->page, // entry->offset, FALSE); // } // PG_RETURN_POINTER(retval); //*/ // //} // //Datum //bingo_decompress(PG_FUNCTION_ARGS) //{ ///* // GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); // GISTENTRY *retval; // text *key; // // key = DatumGetTextP(entry->key); // // if (key != (text *) DatumGetPointer(entry->key)) // { // /* need to pass back the decompressed item */ // /*retval = palloc(sizeof(GISTENTRY)); // gistentryinit(*retval, PointerGetDatum(key), // entry->rel, entry->page, entry->offset, entry->leafkey); // PG_RETURN_POINTER(retval); // } // else // { // /* we can return the entry as-is */ // /*PG_RETURN_POINTER(entry); // } //*/ //} // //Datum //bingo_consistent(PG_FUNCTION_ARGS) //{ // /*GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); // text *query = PG_GETARG_TEXT_P(1); // // /* StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); */ // /* Oid subtype = PG_GETARG_OID(3); */ // /*bool *recheck = (bool *) PG_GETARG_POINTER(4); // TRGM *key = (TRGM *) DatumGetPointer(entry->key); // TRGM *qtrg; // bool res = false; // char *cache = (char *) fcinfo->flinfo->fn_extra; // // /* All cases served by this function are exact */ // /**recheck = false; // // if (cache == NULL || VARSIZE(cache) != VARSIZE(query) || memcmp(cache, query, VARSIZE(query)) != 0) // { // qtrg = generate_trgm(VARDATA(query), VARSIZE(query) - VARHDRSZ); // // if (cache) // pfree(cache); // // fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, // MAXALIGN(VARSIZE(query)) + VARSIZE(qtrg)); // cache = (char *) fcinfo->flinfo->fn_extra; // // memcpy(cache, query, VARSIZE(query)); // memcpy(cache + MAXALIGN(VARSIZE(query)), qtrg, VARSIZE(qtrg)); // } // // qtrg = (TRGM *) (cache + MAXALIGN(VARSIZE(query))); // // if (GIST_LEAF(entry)) // { /* all leafs contains orig trgm */ // /*float4 tmpsml = cnt_sml(key, qtrg); // // /* strange bug at freebsd 5.2.1 and gcc 3.3.3 */ // /*res = (*(int *) &tmpsml == *(int *) &trgm_limit || tmpsml > trgm_limit) ? true : false; // } // else if (ISALLTRUE(key)) // { /* non-leaf contains signature */ // /*res = true; // } // else // { /* non-leaf contains signature */ // /*int4 count = 0; // int4 k, // len = ARRNELEM(qtrg); // trgm *ptr = GETARR(qtrg); // BITVECP sign = GETSIGN(key); // int4 tmp = 0; // // for (k = 0; k < len; k++) // { // CPTRGM(((char *) &tmp), ptr + k); // count += GETBIT(sign, HASHVAL(tmp)); // } //#ifdef DIVUNION // res = (len == count) ? true : ((((((float4) count) / ((float4) (len - count)))) >= trgm_limit) ? true : false); //#else // res = (len == 0) ? false : ((((((float4) count) / ((float4) len))) >= trgm_limit) ? true : false); //#endif // } // // PG_RETURN_BOOL(res);*/ //} // ///* //static int4 //unionkey(BITVECP sbase, TRGM *add) //{ // int4 i; // // if (ISSIGNKEY(add)) // { // BITVECP sadd = GETSIGN(add); // // if (ISALLTRUE(add)) // return 1; // // LOOPBYTE // sbase[i] |= sadd[i]; // } // else // { // trgm *ptr = GETARR(add); // int4 tmp = 0; // // for (i = 0; i < ARRNELEM(add); i++) // { // CPTRGM(((char *) &tmp), ptr + i); // HASH(sbase, tmp); // } // } // return 0; //} //*/ // // //Datum //bingo_union(PG_FUNCTION_ARGS) //{ ///* // GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); // int4 len = entryvec->n; // int *size = (int *) PG_GETARG_POINTER(1); // BITVEC base; // int4 i; // int4 flag = 0; // TRGM *result; // // MemSet((void *) base, 0, sizeof(BITVEC)); // for (i = 0; i < len; i++) // { // if (unionkey(base, GETENTRY(entryvec, i))) // { // flag = ALLISTRUE; // break; // } // } // // flag |= SIGNKEY; // len = CALCGTSIZE(flag, 0); // result = (TRGM *) palloc(len); // SET_VARSIZE(result, len); // result->flag = flag; // if (!ISALLTRUE(result)) // memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC)); // *size = len; // // PG_RETURN_POINTER(result); //*/ //} // //Datum //bingo_same(PG_FUNCTION_ARGS) //{ ///* // TRGM *a = (TRGM *) PG_GETARG_POINTER(0); // TRGM *b = (TRGM *) PG_GETARG_POINTER(1); // bool *result = (bool *) PG_GETARG_POINTER(2); // // if (ISSIGNKEY(a)) // { // then b also ISSIGNKEY // if (ISALLTRUE(a) && ISALLTRUE(b)) // *result = true; // else if (ISALLTRUE(a)) // *result = false; // else if (ISALLTRUE(b)) // *result = false; // else // { // int4 i; // BITVECP sa = GETSIGN(a), // sb = GETSIGN(b); // // *result = true; // LOOPBYTE // { // if (sa[i] != sb[i]) // { // *result = false; // break; // } // } // } // } // else // { //a and b ISARRKEY // int4 lena = ARRNELEM(a), // lenb = ARRNELEM(b); // // if (lena != lenb) // *result = false; // else // { // trgm *ptra = GETARR(a), // *ptrb = GETARR(b); // int4 i; // // *result = true; // for (i = 0; i < lena; i++) // if (CMPTRGM(ptra + i, ptrb + i)) // { // *result = false; // break; // } // } // } // // PG_RETURN_POINTER(result); //*/ //} // ///* //static int4 //sizebitvec(BITVECP sign) //{ // int4 size = 0, // i; // // LOOPBYTE // size += number_of_ones[(unsigned char) sign[i]]; // return size; //} // //static int //hemdistsign(BITVECP a, BITVECP b) //{ // int i, // diff, // dist = 0; // // LOOPBYTE // { // diff = (unsigned char) (a[i] ^ b[i]); // dist += number_of_ones[diff]; // } // return dist; //} // // //static int //hemdist(TRGM *a, TRGM *b) //{ // if (ISALLTRUE(a)) // { // if (ISALLTRUE(b)) // return 0; // else // return SIGLENBIT - sizebitvec(GETSIGN(b)); // } // else if (ISALLTRUE(b)) // return SIGLENBIT - sizebitvec(GETSIGN(a)); // // return hemdistsign(GETSIGN(a), GETSIGN(b)); //} // */ // //Datum //bingo_penalty(PG_FUNCTION_ARGS) //{ ///* // GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); // always ISSIGNKEY // GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); // float *penalty = (float *) PG_GETARG_POINTER(2); // TRGM *origval = (TRGM *) DatumGetPointer(origentry->key); // TRGM *newval = (TRGM *) DatumGetPointer(newentry->key); // BITVECP orig = GETSIGN(origval); // // *penalty = 0.0; // // if (ISARRKEY(newval)) // { // BITVEC sign; // // makesign(sign, newval); // // if (ISALLTRUE(origval)) // *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1); // else // *penalty = hemdistsign(sign, orig); // } // else // *penalty = hemdist(origval, newval); // PG_RETURN_POINTER(penalty); //*/ //} // ///* //typedef struct //{ // bool allistrue; // BITVEC sign; //} CACHESIGN; //*/ // ///* //static void //fillcache(CACHESIGN *item, TRGM *key) //{ // item->allistrue = false; // if (ISARRKEY(key)) // makesign(item->sign, key); // else if (ISALLTRUE(key)) // item->allistrue = true; // else // memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC)); //} // //#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) ) //typedef struct //{ // OffsetNumber pos; // int4 cost; //} SPLITCOST; // //static int //comparecost(const void *a, const void *b) //{ // if (((SPLITCOST *) a)->cost == ((SPLITCOST *) b)->cost) // return 0; // else // return (((SPLITCOST *) a)->cost > ((SPLITCOST *) b)->cost) ? 1 : -1; //} //*/ // // ///* //static int //hemdistcache(CACHESIGN *a, CACHESIGN *b) //{ // if (a->allistrue) // { // if (b->allistrue) // return 0; // else // return SIGLENBIT - sizebitvec(b->sign); // } // else if (b->allistrue) // return SIGLENBIT - sizebitvec(a->sign); // // return hemdistsign(a->sign, b->sign); //} //*/ // //Datum //bingo_picksplit(PG_FUNCTION_ARGS) //{ ///* // GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); // OffsetNumber maxoff = entryvec->n - 2; // GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); // OffsetNumber k, // j; // TRGM *datum_l, // *datum_r; // BITVECP union_l, // union_r; // int4 size_alpha, // size_beta; // int4 size_waste, // waste = -1; // int4 nbytes; // OffsetNumber seed_1 = 0, // seed_2 = 0; // OffsetNumber *left, // *right; // BITVECP ptr; // int i; // CACHESIGN *cache; // SPLITCOST *costvector; // // nbytes = (maxoff + 2) * sizeof(OffsetNumber); // v->spl_left = (OffsetNumber *) palloc(nbytes); // v->spl_right = (OffsetNumber *) palloc(nbytes); // // cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2)); // fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber)); // // for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) // { // for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) // { // if (k == FirstOffsetNumber) // fillcache(&cache[j], GETENTRY(entryvec, j)); // // size_waste = hemdistcache(&(cache[j]), &(cache[k])); // if (size_waste > waste) // { // waste = size_waste; // seed_1 = k; // seed_2 = j; // } // } // } // // left = v->spl_left; // v->spl_nleft = 0; // right = v->spl_right; // v->spl_nright = 0; // // if (seed_1 == 0 || seed_2 == 0) // { // seed_1 = 1; // seed_2 = 2; // } // // // form initial .. // if (cache[seed_1].allistrue) // { // datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); // SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); // datum_l->flag = SIGNKEY | ALLISTRUE; // } // else // { // datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0)); // SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0)); // datum_l->flag = SIGNKEY; // memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC)); // } // if (cache[seed_2].allistrue) // { // datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); // SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0)); // datum_r->flag = SIGNKEY | ALLISTRUE; // } // else // { // datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0)); // SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0)); // datum_r->flag = SIGNKEY; // memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC)); // } // // union_l = GETSIGN(datum_l); // union_r = GETSIGN(datum_r); // maxoff = OffsetNumberNext(maxoff); // fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff)); // // sort before ... // costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff); // for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) // { // costvector[j - 1].pos = j; // size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j])); // size_beta = hemdistcache(&(cache[seed_2]), &(cache[j])); // costvector[j - 1].cost = abs(size_alpha - size_beta); // } // qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost); // // for (k = 0; k < maxoff; k++) // { // j = costvector[k].pos; // if (j == seed_1) // { // *left++ = j; // v->spl_nleft++; // continue; // } // else if (j == seed_2) // { // *right++ = j; // v->spl_nright++; // continue; // } // // if (ISALLTRUE(datum_l) || cache[j].allistrue) // { // if (ISALLTRUE(datum_l) && cache[j].allistrue) // size_alpha = 0; // else // size_alpha = SIGLENBIT - sizebitvec( // (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign) // ); // } // else // size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l)); // // if (ISALLTRUE(datum_r) || cache[j].allistrue) // { // if (ISALLTRUE(datum_r) && cache[j].allistrue) // size_beta = 0; // else // size_beta = SIGLENBIT - sizebitvec( // (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign) // ); // } // else // size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r)); // // if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) // { // if (ISALLTRUE(datum_l) || cache[j].allistrue) // { // if (!ISALLTRUE(datum_l)) // MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC)); // } // else // { // ptr = cache[j].sign; // LOOPBYTE // union_l[i] |= ptr[i]; // } // *left++ = j; // v->spl_nleft++; // } // else // { // if (ISALLTRUE(datum_r) || cache[j].allistrue) // { // if (!ISALLTRUE(datum_r)) // MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC)); // } // else // { // ptr = cache[j].sign; // LOOPBYTE // union_r[i] |= ptr[i]; // } // *right++ = j; // v->spl_nright++; // } // } // // *right = *left = FirstOffsetNumber; // v->spl_ldatum = PointerGetDatum(datum_l); // v->spl_rdatum = PointerGetDatum(datum_r); // // PG_RETURN_POINTER(v); //*/ //}Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_import.cpp000066400000000000000000000416241271037650300247100ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "executor/spi.h" #include "catalog/pg_type.h" #include "utils/int8.h" #include "utils/builtins.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_common.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "bingo_core_c.h" #include "bingo_pg_text.h" #include "base_cpp/array.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "bingo_pg_cursor.h" extern "C" { BINGO_FUNCTION_EXPORT(importsdf); BINGO_FUNCTION_EXPORT(importrdf); BINGO_FUNCTION_EXPORT(importsmiles); } using namespace indigo; static void checkImportNull(Datum text_datum, const char* message, BingoPgText& text) { if(text_datum == 0) throw BingoPgError("can not import structures: %s is empty", message); text.init(text_datum); int text_size = 0; text.getText(text_size); if(text_size == 0) throw BingoPgError("can not import structures: %s is empty", message); } static void checkImportEmpty(Datum text_datum, BingoPgText& text) { if(text_datum == 0) text.initFromString(""); else text.init(text_datum); } class BingoImportHandler : public BingoPgCommon::BingoSessionHandler { public: class ImportColumn { public: ImportColumn() {} ~ImportColumn() {} Array columnName; Oid type; private: ImportColumn(const ImportColumn&); //no implicit copy }; class ImportData { public: ImportData() {} virtual ~ImportData() {} virtual uintptr_t getDatum() = 0; virtual void convert(const char* str) = 0; private: ImportData(const ImportData&); //no implicit copy }; class ImportTextData : public ImportData { public: ImportTextData() {} virtual ~ImportTextData() {} BingoPgText data; virtual uintptr_t getDatum() { return data.getDatum(); } virtual void convert(const char* str) { if(str != 0) data.initFromString(str); else data.clear(); } private: ImportTextData(const ImportTextData&); //no implicit copy }; class ImportInt8Data : public ImportData{ public: ImportInt8Data():data(0) {} virtual ~ImportInt8Data() {} AutoPtr data; virtual void convert(const char* str) { if(str == 0) data.reset(0); else { BINGO_PG_TRY { data.reset(new int64); scanint8(str, false, data.get()); } BINGO_PG_HANDLE(data.reset(0); throw BingoPgError("error while converting to int64: %s", message)); } } virtual uintptr_t getDatum() { if(data.get() == 0) return 0; else return Int64GetDatum(data.ref()); } private: ImportInt8Data(const ImportInt8Data&); //no implicit copy }; class ImportInt4Data : public ImportData{ public: ImportInt4Data():data(0) {} virtual ~ImportInt4Data() {} AutoPtr data; virtual void convert(const char* str) { if (str == 0) data.reset(0); else { /* * Pg atoi workaround */ QS_DEF(Array, str2); str2.readString(str, true); BINGO_PG_TRY { data.reset(new int32); data.ref() = pg_atoi(str2.ptr(), sizeof (int32), 0); } BINGO_PG_HANDLE(data.reset(0); throw BingoPgError("error while converting to int32: %s", message)); } } virtual uintptr_t getDatum() { if(data.get() == 0) return 0; else return Int32GetDatum(data.ref()); } private: ImportInt4Data(const ImportInt4Data&); //no implicit copy }; public: BingoImportHandler(unsigned int func_id):BingoSessionHandler(func_id) { SPI_connect(); } virtual ~BingoImportHandler() { _importData.clear(); SPI_finish(); } virtual bool hasNext() = 0; virtual void getNextData() = 0; void init(Datum table_datum, Datum column_datum, Datum other_columns_datum) { BingoPgText tablename_text; BingoPgText column_text; BingoPgText other_columns_text; checkImportNull(table_datum, "table name", tablename_text); checkImportNull(column_datum, "column name", column_text); checkImportEmpty(other_columns_datum, other_columns_text); /* * Add the data column */ _importColumns.clear(); _importColumns.push().columnName.readString(column_text.getString(), true); /* * Prepare query table with column name */ ArrayOutput column_names(_columnNames); column_names.printf("%s(%s", tablename_text.getString(), column_text.getString()); /* * Add additional columns */ if (_parseColumns) { int column_count = bingoImportParseFieldList(other_columns_text.getString()); for (int col_idx = 0; col_idx < column_count; ++col_idx) { ImportColumn& idCol = _importColumns.push(); idCol.columnName.readString(bingoImportGetColumnName(col_idx), true); column_names.printf(", %s", idCol.columnName.ptr()); } } else { if (other_columns_datum != 0) { if (strcmp(other_columns_text.getString(), "") != 0) { ImportColumn& idCol = _importColumns.push(); idCol.columnName.readString(other_columns_text.getString(), true); column_names.printf(", %s", other_columns_text.getString()); } } } column_names.printf(")"); column_names.writeChar(0); _defineColumnTypes(tablename_text.getString()); } void _defineColumnTypes(const char* table_name) { /* * Read column names for query */ Array column_names; for (int i = 0; i < _importColumns.size(); ++i) { if(i != 0) column_names.appendString(", ", true); column_names.appendString(_importColumns[i].columnName.ptr(), true); } /* * Make a query for types definition */ BingoPgCursor table_first("select %s from %s", column_names.ptr(), table_name); table_first.next(); /* * Set up all types */ for (int i = 0; i < _importColumns.size(); ++i) { int arg_type = table_first.getArgOid(i); ImportColumn& dataCol = _importColumns.at(i); if((arg_type != INT4OID) && (arg_type != INT8OID) && (arg_type != TEXTOID) && (arg_type != BYTEAOID)) throw BingoPgError("can not import a structure: unsupported column '%s' type; supported values: 'text', 'bytea', 'integer'", dataCol.columnName.ptr()); dataCol.type = arg_type; } } void _addData(const char* data, int col_idx) { AutoPtr import_data; ImportColumn& import_column = _importColumns[col_idx]; /* * Detect the types and correspond class */ switch(import_column.type) { case INT4OID: import_data.reset(new ImportInt4Data()); break; case INT8OID: import_data.reset(new ImportInt8Data()); break; case TEXTOID: import_data.reset(new ImportTextData()); break; case BYTEAOID: import_data.reset(new ImportTextData()); break; default: throw BingoPgError("internal error: unknown data type %d", import_column.type); } /* * Convert the string */ import_data->convert(data); /* * Add a data to the array */ _importData.add(import_data.release()); } void import() { QS_DEF(Array, query_str); QS_DEF(Array, q_values); QS_DEF(Array, q_oids); QS_DEF(Array, q_nulls); int spi_success = 0; /* * Prepare query string */ ArrayOutput query_string(query_str); query_string.printf("INSERT INTO "); query_string.printf("%s", _columnNames.ptr()); query_string.printf(" VALUES ("); q_nulls.clear(); q_oids.clear(); for (int col_idx = 0; col_idx < _importColumns.size(); ++col_idx) { q_nulls.push(0); q_oids.push(_importColumns[col_idx].type); if(col_idx != 0) query_string.printf(", "); query_string.printf("$%d", col_idx + 1); } query_string.printf(")"); query_string.writeChar(0); int debug_idx = 0; /* * Loop through the data */ while (hasNext()) { ++debug_idx; elog(DEBUG1, "bingo: %s: processing data entry with index %d", getFunctionName(), debug_idx); /* * Initialize the data */ try { getNextData(); } catch (Exception& e) { /* * Handle incorrect format errors */ if(strstr(e.message(), "data size exceeded the acceptable size") != 0) throw BingoPgError(e.message()); elog(WARNING, "can not import a structure: %s", e.message()); continue; } /* * Initialize values for the query */ q_values.clear(); for (int q_idx = 0; q_idx < _importData.size(); ++q_idx) { q_values.push(_importData[q_idx]->getDatum()); if(q_values[q_idx] == 0) { q_nulls[q_idx] = 'n'; } else { q_nulls[q_idx] = 0; } } BINGO_PG_TRY { /* * Execute query */ spi_success = SPI_execute_with_args(query_str.ptr(), q_values.size(), q_oids.ptr(), q_values.ptr(), q_nulls.ptr(), false, 1); if (spi_success < 0) elog(WARNING, "can not insert a structure into a table: %s", SPI_result_code_string(spi_success)); } BINGO_PG_HANDLE(throw BingoPgError("can not import all the structures: SQL error: %s", message)); if(debug_idx % 1000 == 0) elog(NOTICE, "bingo.import: %d structures processed", debug_idx); /* * Return back session id and error handler */ refresh(); } } protected: int bingo_res; bool _parseColumns; Array _columnNames; ObjArray _importColumns; PtrArray _importData; private: BingoImportHandler(const BingoImportHandler&); //no implicit copy }; class BingoImportSdfHandler : public BingoImportHandler { public: BingoImportSdfHandler(unsigned int func_id, const char* fname): BingoImportHandler(func_id) { _parseColumns = true; setFunctionName("importSDF"); bingo_res = bingoSDFImportOpen(fname); CORE_HANDLE_ERROR(bingo_res, 1, "importSDF", bingoGetError()); } virtual ~BingoImportSdfHandler() { bingo_res = bingoSDFImportClose(); CORE_HANDLE_WARNING(bingo_res, 0, "importSDF close", bingoGetError()); } virtual bool hasNext() { bingo_res = bingoSDFImportEOF(); CORE_HANDLE_ERROR(bingo_res, 0, "importSDF", bingoGetError()); return !bingo_res; } virtual void getNextData() { const char* data = 0; _importData.clear(); for (int col_idx = 0; col_idx < _importColumns.size(); ++col_idx) { if (col_idx == 0) data = bingoSDFImportGetNext(); else data = bingoImportGetPropertyValue(col_idx - 1); if (data == 0) { /* * Handle incorrect format errors */ if(strstr(bingoGetError(), "data size exceeded the acceptable size") != 0) throw BingoPgError(bingoGetError()); CORE_HANDLE_WARNING(0, 1, "importSDF", bingoGetError()); } _addData(data, col_idx); } } private: BingoImportSdfHandler(const BingoImportSdfHandler&); //no implicit copy }; Datum importsdf(PG_FUNCTION_ARGS) { Datum table_datum = PG_GETARG_DATUM(0); Datum column_datum = PG_GETARG_DATUM(1); Datum other_columns_datum = PG_GETARG_DATUM(2); Datum file_name_datum = PG_GETARG_DATUM(3); PG_BINGO_BEGIN { /* * Check file name */ BingoPgText fname_text; checkImportNull(file_name_datum, "file name", fname_text); /* * Initialize import */ BingoImportSdfHandler bingo_handler(fcinfo->flinfo->fn_oid, fname_text.getString()); bingo_handler.init(table_datum, column_datum, other_columns_datum); /* * Perform import */ bingo_handler.import(); } PG_BINGO_END PG_RETURN_VOID(); } class BingoImportRdfHandler : public BingoImportHandler { public: BingoImportRdfHandler(unsigned int func_id, const char* fname): BingoImportHandler(func_id) { _parseColumns = true; setFunctionName("importRDF"); bingo_res = bingoRDFImportOpen(fname); CORE_HANDLE_ERROR(bingo_res, 1, "importRDF", bingoGetError()); } virtual ~BingoImportRdfHandler() { bingo_res = bingoRDFImportClose(); CORE_HANDLE_WARNING(bingo_res, 0, "importRDF close", bingoGetError()); } virtual bool hasNext() { bingo_res = bingoRDFImportEOF(); CORE_HANDLE_ERROR(bingo_res, 0, "importRDF", bingoGetError()); return !bingo_res; } virtual void getNextData() { const char* data = 0; _importData.clear(); for (int col_idx = 0; col_idx < _importColumns.size(); ++col_idx) { if (col_idx == 0) data = bingoRDFImportGetNext(); else data = bingoImportGetPropertyValue(col_idx - 1); if (data == 0) { /* * Handle incorrect format errors */ if(strstr(bingoGetError(), "data size exceeded the acceptable size") != 0) throw BingoPgError(bingoGetError()); CORE_HANDLE_WARNING(0, 1, "importRDF", bingoGetError()); } _addData(data, col_idx); } } private: BingoImportRdfHandler(const BingoImportSdfHandler&); //no implicit copy }; Datum importrdf(PG_FUNCTION_ARGS) { Datum table_datum = PG_GETARG_DATUM(0); Datum column_datum = PG_GETARG_DATUM(1); Datum other_columns_datum = PG_GETARG_DATUM(2); Datum file_name_datum = PG_GETARG_DATUM(3); PG_BINGO_BEGIN { /* * Check file name */ BingoPgText fname_text; checkImportNull(file_name_datum, "file name", fname_text); /* * Initialize import */ BingoImportRdfHandler bingo_handler(fcinfo->flinfo->fn_oid, fname_text.getString()); bingo_handler.init(table_datum, column_datum, other_columns_datum); /* * Perform import */ bingo_handler.import(); } PG_BINGO_END PG_RETURN_VOID(); } class BingoImportSmilesHandler : public BingoImportHandler { public: BingoImportSmilesHandler(unsigned int func_id, const char* fname):BingoImportHandler(func_id) { _parseColumns = false; setFunctionName("importSMILES"); bingo_res = bingoSMILESImportOpen(fname); CORE_HANDLE_ERROR(bingo_res, 1, "importSmiles", bingoGetError()); } virtual ~BingoImportSmilesHandler() { bingo_res = bingoSMILESImportClose(); CORE_HANDLE_WARNING(bingo_res, 0, "importSmiles close", bingoGetError()); } virtual bool hasNext() { bingo_res = bingoSMILESImportEOF(); CORE_HANDLE_ERROR(bingo_res, 0, "importSmiles", bingoGetError()); return !bingo_res; } virtual void getNextData() { const char* data = 0; _importData.clear(); for (int col_idx = 0; col_idx < _importColumns.size(); ++col_idx) { if (col_idx == 0) { data = bingoSMILESImportGetNext(); if (data == 0) CORE_HANDLE_WARNING(0, 1, "importSMILES", bingoGetError()); } else { data = bingoSMILESImportGetId(); if(data == 0) CORE_HANDLE_WARNING(0, 1, "importSMILES", "can not get smiles id"); } _addData(data, col_idx); } } private: BingoImportSmilesHandler(const BingoImportSmilesHandler&); //no implicit copy }; Datum importsmiles(PG_FUNCTION_ARGS) { Datum table_datum = PG_GETARG_DATUM(0); Datum column_datum = PG_GETARG_DATUM(1); Datum other_column_datum = PG_GETARG_DATUM(2); Datum file_name_datum = PG_GETARG_DATUM(3); PG_BINGO_BEGIN { /* * Check file name */ BingoPgText fname_text; checkImportNull(file_name_datum, "file name", fname_text); /* * Initialize import */ BingoImportSmilesHandler bingo_handler(fcinfo->flinfo->fn_oid, fname_text.getString()); bingo_handler.init(table_datum, column_datum, other_column_datum); /* * Perform import */ bingo_handler.import(); } PG_BINGO_END PG_RETURN_VOID(); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_options.cpp000066400000000000000000000473201271037650300250700ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" #include "float.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "catalog/index.h" #include "access/relscan.h" #include "optimizer/cost.h" #include "optimizer/plancat.h" #include "catalog/pg_tablespace.h" #include "commands/tablespace.h" #include "access/reloptions.h" #include "storage/bufmgr.h" #include "utils/tuplesort.h" #include "utils/array.h" #include "catalog/pg_type.h" #include "utils/rel.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_common.h" #include "pg_bingo_context.h" extern "C" { BINGO_FUNCTION_EXPORT(bingo_options); } #define RELOPT_KIND_BINGO 1<<8 static relopt_bool boolRelOpts[] = { { { "autovacuum_enabled", "Enables autovacuum in this relation", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, true }, /* list terminator */ {{NULL}} }; static relopt_int intRelOpts[] = { { { "fillfactor", "Packs table pages only to this percentage", RELOPT_KIND_HEAP }, HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100 }, { { "autovacuum_vacuum_threshold", "Minimum number of tuple updates or deletes prior to vacuum", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 0, INT_MAX }, { { "autovacuum_analyze_threshold", "Minimum number of tuple inserts, updates or deletes prior to analyze", RELOPT_KIND_HEAP }, -1, 0, INT_MAX }, { { "autovacuum_vacuum_cost_delay", "Vacuum cost delay in milliseconds, for autovacuum", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 0, 100 }, { { "autovacuum_vacuum_cost_limit", "Vacuum cost amount available before napping, for autovacuum", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 1, 10000 }, { { "autovacuum_freeze_min_age", "Minimum age at which VACUUM should freeze a table row, for autovacuum", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 0, 1000000000 }, { { "autovacuum_freeze_max_age", "Age at which to autovacuum a table to prevent transaction ID wraparound", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 100000000, 2000000000 }, { { "autovacuum_freeze_table_age", "Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 0, 2000000000 }, { { "treat_x_as_pseudoatom", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "ignore_closing_bond_direction_mismatch", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "ignore_stereocenter_errors", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "stereochemistry_bidirectional_mode", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "stereochemistry_detect_haworth_projection", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "ignore_cistrans_errors", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "allow_non_unique_dearomatization", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "zero_unknown_aromatic_hydrogens", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "reject_invalid_structures", "", RELOPT_KIND_BINGO }, -1, 0, 1 }, { { "fp_ord_size", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "fp_any_size", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "fp_tau_size", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "fp_sim_size", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "sub_screening_max_bits", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "sim_screening_pass_mark", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, { { "nthreads", "", RELOPT_KIND_BINGO }, -1, 0, 2000000000 }, /* list terminator */ {{NULL}} }; static relopt_real realRelOpts[] = { { { "autovacuum_vacuum_scale_factor", "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples", RELOPT_KIND_HEAP | RELOPT_KIND_TOAST }, -1, 0.0, 100.0 }, { { "autovacuum_analyze_scale_factor", "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples", RELOPT_KIND_HEAP }, -1, 0.0, 100.0 }, { { "seq_page_cost", "Sets the planner's estimate of the cost of a sequentially fetched disk page.", RELOPT_KIND_TABLESPACE }, -1, 0.0, DBL_MAX }, { { "random_page_cost", "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.", RELOPT_KIND_TABLESPACE }, -1, 0.0, DBL_MAX }, { { "n_distinct", "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).", RELOPT_KIND_ATTRIBUTE }, 0, -1.0, DBL_MAX }, { { "n_distinct_inherited", "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).", RELOPT_KIND_ATTRIBUTE }, 0, -1.0, DBL_MAX }, /* list terminator */ {{NULL}} }; static relopt_string stringRelOpts[] = { /* list terminator */ {{NULL}} }; static relopt_gen **relOpts = NULL; static int num_custom_options = 0; static relopt_gen **custom_options = NULL; static bool need_initialization = true; static void initialize_reloptions(void); static void parse_one_reloption(relopt_value *option, char *text_str, int text_len, bool validate); static void parse_one_reloption(relopt_value *option, char *text_str, int text_len, bool validate) { char *value; int value_len; bool parsed; bool nofree = false; if (option->isset && validate) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("parameter \"%s\" specified more than once", option->gen->name))); value_len = text_len - option->gen->namelen - 1; value = (char *) palloc(value_len + 1); memcpy(value, text_str + option->gen->namelen + 1, value_len); value[value_len] = '\0'; switch (option->gen->type) { case RELOPT_TYPE_BOOL: { parsed = parse_bool(value, &option->values.bool_val); if (validate && !parsed) ereport(ERROR, (errmsg("invalid value for boolean option \"%s\": %s", option->gen->name, value))); } break; case RELOPT_TYPE_INT: { relopt_int *optint = (relopt_int *) option->gen; parsed = parse_int(value, &option->values.int_val, 0, NULL); if (validate && !parsed) ereport(ERROR, (errmsg("invalid value for integer option \"%s\": %s", option->gen->name, value))); if (validate && (option->values.int_val < optint->min || option->values.int_val > optint->max)) ereport(ERROR, (errmsg("value %s out of bounds for option \"%s\"", value, option->gen->name), errdetail("Valid values are between \"%d\" and \"%d\".", optint->min, optint->max))); } break; case RELOPT_TYPE_REAL: { relopt_real *optreal = (relopt_real *) option->gen; parsed = parse_real(value, &option->values.real_val); if (validate && !parsed) ereport(ERROR, (errmsg("invalid value for floating point option \"%s\": %s", option->gen->name, value))); if (validate && (option->values.real_val < optreal->min || option->values.real_val > optreal->max)) ereport(ERROR, (errmsg("value %s out of bounds for option \"%s\"", value, option->gen->name), errdetail("Valid values are between \"%f\" and \"%f\".", optreal->min, optreal->max))); } break; case RELOPT_TYPE_STRING: { relopt_string *optstring = (relopt_string *) option->gen; option->values.string_val = value; nofree = true; if (validate && optstring->validate_cb) (optstring->validate_cb) (value); parsed = true; } break; default: elog(ERROR, "unsupported reloption type %d", option->gen->type); parsed = true; /* quiet compiler */ break; } if (parsed) option->isset = true; if (!nofree) pfree(value); } static void initialize_reloptions(void) { int i; int j; j = 0; for (i = 0; boolRelOpts[i].gen.name; i++) j++; for (i = 0; intRelOpts[i].gen.name; i++) j++; for (i = 0; realRelOpts[i].gen.name; i++) j++; for (i = 0; stringRelOpts[i].gen.name; i++) j++; j += num_custom_options; if (relOpts) pfree(relOpts); relOpts = (relopt_gen **)MemoryContextAlloc(TopMemoryContext, (j + 1) * sizeof (relopt_gen *)); j = 0; for (i = 0; boolRelOpts[i].gen.name; i++) { relOpts[j] = &boolRelOpts[i].gen; relOpts[j]->type = RELOPT_TYPE_BOOL; relOpts[j]->namelen = strlen(relOpts[j]->name); j++; } for (i = 0; intRelOpts[i].gen.name; i++) { relOpts[j] = &intRelOpts[i].gen; relOpts[j]->type = RELOPT_TYPE_INT; relOpts[j]->namelen = strlen(relOpts[j]->name); j++; } for (i = 0; realRelOpts[i].gen.name; i++) { relOpts[j] = &realRelOpts[i].gen; relOpts[j]->type = RELOPT_TYPE_REAL; relOpts[j]->namelen = strlen(relOpts[j]->name); j++; } for (i = 0; stringRelOpts[i].gen.name; i++) { relOpts[j] = &stringRelOpts[i].gen; relOpts[j]->type = RELOPT_TYPE_STRING; relOpts[j]->namelen = strlen(relOpts[j]->name); j++; } for (i = 0; i < num_custom_options; i++) { relOpts[j] = custom_options[i]; j++; } /* add a list terminator */ relOpts[j] = NULL; /* flag the work is complete */ need_initialization = false; } relopt_value * bingoParseRelOptions(Datum options, bool validate, int kind, int *numrelopts) { relopt_value *reloptions; int numoptions = 0; int i; int j; if (need_initialization) initialize_reloptions(); /* Build a list of expected options, based on kind */ for (i = 0; relOpts[i]; i++) if (relOpts[i]->kinds & kind) numoptions++; if (numoptions == 0) { *numrelopts = 0; return NULL; } reloptions = (relopt_value *)palloc(numoptions * sizeof (relopt_value)); for (i = 0, j = 0; relOpts[i]; i++) { if (relOpts[i]->kinds & kind) { reloptions[j].gen = relOpts[i]; reloptions[j].isset = false; j++; } } /* Done if no options */ if (PointerIsValid(DatumGetPointer(options))) { ArrayType *array; Datum *optiondatums; int noptions; array = DatumGetArrayTypeP(options); Assert(ARR_ELEMTYPE(array) == TEXTOID); deconstruct_array(array, TEXTOID, -1, false, 'i', &optiondatums, NULL, &noptions); for (i = 0; i < noptions; i++) { text *optiontext = DatumGetTextP(optiondatums[i]); char *text_str = VARDATA(optiontext); int text_len = VARSIZE(optiontext) - VARHDRSZ; int j; /* Search for a match in reloptions */ for (j = 0; j < numoptions; j++) { int kw_len = reloptions[j].gen->namelen; if (text_len > kw_len && text_str[kw_len] == '=' && pg_strncasecmp(text_str, reloptions[j].gen->name, kw_len) == 0) { parse_one_reloption(&reloptions[j], text_str, text_len, validate); break; } } if (j >= numoptions && validate) { char *s; char *p; s = TextDatumGetCString(optiondatums[i]); p = strchr(s, '='); if (p) *p = '\0'; ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized parameter \"%s\"", s))); } } } *numrelopts = numoptions; return reloptions; } bytea * bingo_reloptions(Datum reloptions, bool validate) { relopt_value *options; void* rdopts; int numoptions; static const relopt_parse_elt tab[] = { {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}, {"autovacuum_enabled", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)}, {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)}, {"autovacuum_analyze_threshold", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)}, {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)}, {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)}, {"autovacuum_freeze_min_age", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)}, {"autovacuum_freeze_max_age", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)}, {"autovacuum_freeze_table_age", RELOPT_TYPE_INT, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)}, {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)}, {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)}, {"treat_x_as_pseudoatom", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, treat_x_as_pseudoatom)}, {"ignore_closing_bond_direction_mismatch", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, ignore_closing_bond_direction_mismatch)}, {"ignore_stereocenter_errors", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, ignore_stereocenter_errors)}, {"stereochemistry_bidirectional_mode", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, stereochemistry_bidirectional_mode)}, {"stereochemistry_detect_haworth_projection", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, stereochemistry_detect_haworth_projection)}, {"ignore_cistrans_errors", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, ignore_cistrans_errors)}, {"allow_non_unique_dearomatization", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, allow_non_unique_dearomatization)}, {"zero_unknown_aromatic_hydrogens", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, zero_unknown_aromatic_hydrogens)}, {"reject_invalid_structures", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, reject_invalid_structures)}, {"fp_ord_size", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, fp_ord_size)}, {"fp_any_size", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, fp_any_size)}, {"fp_tau_size", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, fp_tau_size)}, {"fp_sim_size", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, fp_sim_size)}, {"sub_screening_max_bits", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, sub_screening_max_bits)}, {"sim_screening_pass_mark", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, sim_screening_pass_mark)}, {"nthreads", RELOPT_TYPE_INT, offsetof(BingoStdRdOptions, index_parameters) + offsetof(BingoIndexOptions, nthreads)} }; options = bingoParseRelOptions(reloptions, validate, RELOPT_KIND_BINGO, &numoptions); /* if none set, we're done */ if (numoptions == 0) return NULL; rdopts = allocateReloptStruct(sizeof (BingoStdRdOptions), options, numoptions); fillRelOptions(rdopts, sizeof (BingoStdRdOptions), options, numoptions, validate, tab, lengthof(tab)); pfree(options); return (bytea *) rdopts; } Datum bingo_options(PG_FUNCTION_ARGS) { Datum reloptions = PG_GETARG_DATUM(0); bool validate = PG_GETARG_BOOL(1); bytea *result; result = bingo_reloptions(reloptions, validate); if (result) PG_RETURN_BYTEA_P(result); PG_RETURN_NULL(); } //Datum //bingo_options(PG_FUNCTION_ARGS) { // Datum reloptions = PG_GETARG_DATUM(0); // bool validate = PG_GETARG_BOOL(1); // elog(INFO, "bingo options"); // // bytea *result; // elog(INFO, "bingo options"); // // result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH); // // if (result) // PG_RETURN_BYTEA_P(result); // PG_RETURN_NULL(); //} //Datum //bingo_options(PG_FUNCTION_ARGS) { // Datum reloptions = PG_GETARG_DATUM(0); // bool validate = PG_GETARG_BOOL(1); // elog(INFO, "bingo options"); // // int noptions = 0; // BingoStdRdOptions *rdopts = 0; // // try { // RedBlackStringObjMap< Array > attributes; // // /* // * Done if no options // */ // if (PointerIsValid(DatumGetPointer(reloptions))) { // ArrayType *array; // Datum *optiondatums; // // array = DatumGetArrayTypeP(reloptions); // // Assert(ARR_ELEMTYPE(array) == TEXTOID); // // deconstruct_array(array, TEXTOID, -1, false, 'i', // &optiondatums, NULL, &noptions); // // // attributes.clear(); // for (int i = 0; i < noptions; i++) { // int text_len; // char* text_data = BingoPgCommon::getTextData(&optiondatums[i], text_len); // Helpers::addAttributeAndValue(text_data, text_len, attributes); // } // } // // rdopts = (BingoStdRdOptions*)palloc0(sizeof(BingoStdRdOptions)); // // Helpers::mapParameter(attributes, "treat_x_as_pseudoatom", rdopts->index_parameters.treat_x_as_pseudoatom); // Helpers::mapParameter(attributes, "ignore_closing_bond_direction_mismatch", rdopts->index_parameters.ignore_closing_bond_direction_mismatch); // Helpers::mapParameter(attributes, "fp_ord_size", rdopts->index_parameters.fp_ord_size); // Helpers::mapParameter(attributes, "fp_any_size", rdopts->index_parameters.fp_any_size); // Helpers::mapParameter(attributes, "fp_tau_size", rdopts->index_parameters.fp_tau_size); // Helpers::mapParameter(attributes, "fp_sim_size", rdopts->index_parameters.fp_sim_size); // Helpers::mapParameter(attributes, "sub_screening_max_bits", rdopts->index_parameters.sub_screening_max_bits); // Helpers::mapParameter(attributes, "sim_screening_pass_mark", rdopts->index_parameters.sim_screening_pass_mark); // // if(validate && attributes.size() > 0) // elog(ERROR, "Error while loading options: unknown option %s\n", attributes.key(attributes.begin())); // // } catch (Exception& e) { // elog(ERROR, "Error while loading options: %s\n", e.message()); // } // // /* if none set, we're done */ // if (noptions == 0 || rdopts == 0) // PG_RETURN_NULL(); // // int xx = sizeof(BingoStdRdOptions); // SET_VARSIZE(rdopts, xx); // // PG_RETURN_BYTEA_P((bytea*)rdopts); //} Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_search.cpp000066400000000000000000000112361271037650300246370ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "access/skey.h" #include "access/relscan.h" #include "utils/rel.h" #include "utils/relcache.h" #include "miscadmin.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_search.h" #include "bingo_postgres.h" #include "pg_bingo_context.h" #include "bingo_pg_search_engine.h" #include "bingo_pg_common.h" #include "base_cpp/tlscont.h" extern "C" { BINGO_FUNCTION_EXPORT(bingo_beginscan); BINGO_FUNCTION_EXPORT(bingo_gettuple); /* * Turn off bitmap scan */ // BINGO_FUNCTION_EXPORT(bingo_getbitmap); BINGO_FUNCTION_EXPORT(bingo_rescan); BINGO_FUNCTION_EXPORT(bingo_endscan); } //#include //__sighandler_t old_handler = 0; //static void error_handler(int i) { //// signal(SIGINT, old_handler); //// elog(WARNING, "aaa"); //// elog(ERROR, "query was cancelled"); //// throw CancelException(); //} /* * Bingo searching initialization */ Datum bingo_beginscan(PG_FUNCTION_ARGS) { Relation rel = (Relation) PG_GETARG_POINTER(0); int keysz = PG_GETARG_INT32(1); #if PG_VERSION_NUM / 100 == 900 ScanKey norderbys = (ScanKey) PG_GETARG_POINTER(2); #elif PG_VERSION_NUM / 100 >= 901 int norderbys = PG_GETARG_INT32(2); #else elog(ERROR, "unsupported version %s", PG_VERSION) #endif IndexScanDesc scan = RelationGetIndexScan(rel, keysz, norderbys); scan->opaque = 0; BingoPgSearch* so = 0; PG_BINGO_BEGIN { /* * Prepare search context */ so = new BingoPgSearch(rel); /* * Setting bingo search context */ scan->opaque = so; BingoPgWrapper rel_namespace; const char* index_schema = rel_namespace.getRelNameSpace(rel->rd_id); BingoPgCommon::appendPath(index_schema); } PG_BINGO_HANDLE(delete so; scan->opaque=NULL); PG_RETURN_POINTER(scan); } /* * Rescan an index relation */ Datum bingo_rescan(PG_FUNCTION_ARGS) { IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1); BingoPgSearch* so = 0; PG_BINGO_BEGIN { /* * Update scan key, if a new one is given */ if (scankey && scan->numberOfKeys > 0) { memmove(scan->keyData, scankey, scan->numberOfKeys * sizeof (ScanKeyData)); } so = (BingoPgSearch*) scan->opaque; if (so != NULL) { so->prepareRescan(scan); } } PG_BINGO_HANDLE(delete so; scan->opaque=NULL); PG_RETURN_VOID(); } /* * Close down a scan */ Datum bingo_endscan(PG_FUNCTION_ARGS) { IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); elog(DEBUG1, "bingo: search: finish searching"); PG_BINGO_BEGIN { BingoPgSearch* so = (BingoPgSearch*) scan->opaque; /* * Delete bingo search context */ if(so != NULL) delete so; scan->opaque = NULL; } PG_BINGO_END PG_RETURN_VOID(); } using namespace indigo; /* * Get all tuples at once */ //Datum //bingo_getbitmap(PG_FUNCTION_ARGS) { // IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); // TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1); // // qword item_size = 0; // BingoPgSearch* search_engine = (BingoPgSearch*) scan->opaque; // PG_BINGO_BEGIN // { // /* // * Create search engine // */ // /* // * Fetch to the next item and add result to the bitmap // */ // indigo::Array found_items; // ItemPointer item_ptr; // do { // item_ptr = &found_items.push(); // } while (search_engine->next(scan, item_ptr)); // /* // * Pop the last element // */ // found_items.pop(); // item_size = found_items.size(); // BINGO_PG_TRY { // tbm_add_tuples(tbm, found_items.ptr(), found_items.size(), false); // } BINGO_PG_HANDLE(throw BingoPgError("internal error: can not add bitmap solution: %s", message)); // } // PG_BINGO_HANDLE(delete search_engine); // // PG_RETURN_INT64(item_size); //} /* * Get a tuples by a chain */ Datum bingo_gettuple(PG_FUNCTION_ARGS) { IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1); bool result = false; BingoPgSearch* search_engine = (BingoPgSearch*) scan->opaque; if(search_engine == NULL) elog(ERROR, "bingo: search error: search context was deleted"); PG_BINGO_BEGIN { scan->xs_recheck = false; /* * Fetch to the next item */ result = search_engine->next(scan, &scan->xs_ctup.t_self); } PG_BINGO_HANDLE(delete search_engine; scan->opaque=NULL); /* * If true then searching was successfull */ PG_RETURN_BOOL(result); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_update.cpp000066400000000000000000000130461271037650300246550ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/relcache.h" #include "utils/rel.h" #include "catalog/index.h" #include "storage/bufmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_build.h" #include "bingo_pg_common.h" #include "bingo_postgres.h" #include "bingo_pg_text.h" extern "C" { BINGO_FUNCTION_EXPORT(bingo_insert); BINGO_FUNCTION_EXPORT(bingo_bulkdelete); BINGO_FUNCTION_EXPORT(bingo_vacuumcleanup); } /* * Insert an index tuple into a hash table. * */ Datum bingo_insert(PG_FUNCTION_ARGS) { Relation index = (Relation) PG_GETARG_POINTER(0); Datum *values = (Datum *) PG_GETARG_POINTER(1); bool isnull = *((bool*) PG_GETARG_POINTER(2)); ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3); /* * Skip inserting null tuples */ if(isnull) PG_RETURN_BOOL(false); bool result = false; PG_BINGO_BEGIN { BingoPgWrapper rel_namespace; const char* index_schema = rel_namespace.getRelNameSpace(index->rd_id); BingoPgBuild build_engine(index, 0, index_schema, false); /* * Insert a new structure */ result = build_engine.insertStructureSingle(ht_ctid, values[0]); } PG_BINGO_END //#ifdef NOT_USED // Relation heapRel = (Relation) PG_GETARG_POINTER(4); // IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); //#endif // IndexTuple itup; // // /* generate an index tuple */ // itup = _hash_form_tuple(rel, values, isnull); // itup->t_tid = *ht_ctid; // // /* // * If the single index key is null, we don't insert it into the index. // * Hash tables support scans on '='. Relational algebra says that A = B // * returns null if either A or B is null. This means that no // * qualification used in an index scan could ever return true on a null // * attribute. It also means that indices can't be used by ISNULL or // * NOTNULL scans, but that's an artifact of the strategy map architecture // * chosen in 1986, not of the way nulls are handled here. // */ // if (IndexTupleHasNulls(itup)) // { // pfree(itup); // PG_RETURN_BOOL(false); // } // // _hash_doinsert(rel, itup); // // pfree(itup); PG_RETURN_BOOL(result); } /* * Bulk deletion of all index entries pointing to a set of heap tuples. * The set of target tuples is specified via a callback routine that tells * whether any given heap tuple (identified by ItemPointer) is being deleted. * * Result: a palloc'd struct containing statistical info for VACUUM displays. */ Datum bingo_bulkdelete(PG_FUNCTION_ARGS) { IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0); IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1); IndexBulkDeleteCallback bulk_del_cb = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2); void *cb_state = (void *) PG_GETARG_POINTER(3); elog(NOTICE, "bingo.index: start bulk delete"); PG_BINGO_BEGIN { /* * Initialize local variables */ Relation index_rel = info->index; double tuples_removed = 0; ItemPointerData item_data; ItemPointer item_ptr = &item_data; BingoPgExternalBitset section_bitset(BINGO_MOLS_PER_SECTION); /* * Create index manager */ BingoPgIndex bingo_index(index_rel); /* * Iterate through all the sections and search for removed tuples */ int section_idx = bingo_index.readBegin(); for (; section_idx != bingo_index.readEnd(); section_idx = bingo_index.readNext(section_idx)) { bingo_index.getSectionBitset(section_idx, section_bitset); /* * Iterate through section structures */ for (int mol_idx = section_bitset.begin(); mol_idx != section_bitset.end(); mol_idx = section_bitset.next(mol_idx)) { bingo_index.readTidItem(section_idx, mol_idx, item_ptr); if (bulk_del_cb(item_ptr, cb_state)) { /* * Remove the structure from the bingo index */ bingo_index.removeStructure(section_idx, mol_idx); tuples_removed += 1; } } } /* * Write new structures number */ bingo_index.writeMetaInfo(); if (stats == NULL) stats = (IndexBulkDeleteResult *) palloc0(sizeof (IndexBulkDeleteResult)); stats->estimated_count = false; stats->tuples_removed = tuples_removed; } PG_BINGO_END /* * Always return null since no index values are removed */ PG_RETURN_POINTER(NULL); // PG_RETURN_POINTER(stats); } /* * Post-VACUUM cleanup. * * Result: a palloc'd struct containing statistical info for VACUUM displays. */ Datum bingo_vacuumcleanup(PG_FUNCTION_ARGS) { IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0); IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1); Relation rel = info->index; BlockNumber num_pages = 0; elog(NOTICE, "bingo.index: start post-vacuum"); /* * Always return null since no index values are removed */ PG_RETURN_POINTER(NULL); // /* // * If bulkdelete wasn't called, return NULL signifying no change // * Note: this covers the analyze_only case too // */ // if (stats == NULL) { // PG_RETURN_POINTER(NULL); // } // /* // * update statistics // */ // num_pages = RelationGetNumberOfBlocks(rel); // stats->num_pages = num_pages; // stats->num_index_tuples = 1; // stats->estimated_count = false; // // PG_RETURN_POINTER(stats); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_bingo_utils.cpp000077500000000000000000000520611271037650300245360ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/relcache.h" #include "storage/lock.h" #include "access/heapam.h" #include "storage/bufmgr.h" #if PG_VERSION_NUM / 100 >= 903 #include "lib/stringinfo.h" #include "access/xlog_internal.h" #endif } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "base_cpp/scanner.h" #include "bingo_core_c.h" #include "bingo_pg_common.h" #include "pg_bingo_context.h" #include "bingo_pg_config.h" #include "bingo_pg_text.h" #include "bingo_pg_buffer.h" #include "bingo_pg_config.h" #include "bingo_pg_cursor.h" #include "bingo_pg_index.h" extern "C" { BINGO_FUNCTION_EXPORT(bingo_markpos); BINGO_FUNCTION_EXPORT(bingo_restrpos); BINGO_FUNCTION_EXPORT(getindexstructurescount); BINGO_FUNCTION_EXPORT(_get_structures_count); BINGO_FUNCTION_EXPORT(_get_block_count); BINGO_FUNCTION_EXPORT(_precache_database); BINGO_FUNCTION_EXPORT(getversion); BINGO_FUNCTION_EXPORT(filetotext); BINGO_FUNCTION_EXPORT(filetoblob); BINGO_FUNCTION_EXPORT(getname); BINGO_FUNCTION_EXPORT(exportsdf); BINGO_FUNCTION_EXPORT(exportrdf); BINGO_FUNCTION_EXPORT(_reset_profiling_info); BINGO_FUNCTION_EXPORT(_get_profiling_info); BINGO_FUNCTION_EXPORT(_print_profiling_info); } using namespace indigo; Datum getindexstructurescount(PG_FUNCTION_ARGS){ Oid relOid = PG_GETARG_OID(0); int result = 0; Relation rel; rel = relation_open(relOid, AccessShareLock); PG_BINGO_BEGIN { BingoPgBuffer meta_buffer; meta_buffer.readBuffer(rel, BINGO_METAPAGE, BINGO_PG_READ); BingoMetaPage meta_page = BingoPageGetMeta(BufferGetPage(meta_buffer.getBuffer())); result = meta_page->n_molecules; } PG_BINGO_END relation_close(rel, AccessShareLock); PG_RETURN_INT32(result); } Datum _get_structures_count(PG_FUNCTION_ARGS){ Oid relOid = PG_GETARG_OID(0); int result = 0; Relation rel; rel = relation_open(relOid, AccessShareLock); PG_BINGO_BEGIN { BingoPgBuffer meta_buffer; meta_buffer.readBuffer(rel, BINGO_METAPAGE, BINGO_PG_READ); BingoMetaPage meta_page = BingoPageGetMeta(BufferGetPage(meta_buffer.getBuffer())); result = meta_page->n_molecules; } PG_BINGO_END relation_close(rel, AccessShareLock); PG_RETURN_INT32(result); } Datum _get_block_count(PG_FUNCTION_ARGS){ Oid relOid = PG_GETARG_OID(0); int result = 0; Relation rel; rel = relation_open(relOid, AccessShareLock); PG_BINGO_BEGIN { BingoPgBuffer meta_buffer; meta_buffer.readBuffer(rel, BINGO_METAPAGE, BINGO_PG_READ); BingoMetaPage meta_page = BingoPageGetMeta(BufferGetPage(meta_buffer.getBuffer())); result = meta_page->n_sections; } PG_BINGO_END relation_close(rel, AccessShareLock); PG_RETURN_INT32(result); } class CacheParams { public: enum { SIZE_IN_BYTES, SIZE_IN_KB, SIZE_IN_MB, SIZE_IN_GB }; int size_in; CacheParams(){ size_in = SIZE_IN_MB; } void getSizeIn(Array& out) { switch(size_in) { case SIZE_IN_BYTES: out.readString("B", true); break; case SIZE_IN_KB: out.readString("KB", true); break; case SIZE_IN_MB: out.readString("MB", true); break; case SIZE_IN_GB: out.readString("GB", true); break; default: break; } } qword getSize(qword size_b) { qword res = 0; switch(size_in) { case SIZE_IN_BYTES: res = size_b; break; case SIZE_IN_KB: res = size_b>>10; break; case SIZE_IN_MB: res = size_b>>20; break; case SIZE_IN_GB: res = size_b>>30; break; default: break; } if(res == 0 && size_b > 0) res = 1; return res; } void parseParameters(const char* params_str) { BufferScanner scanner(params_str); QS_DEF(Array, buf_word); scanner.skipSpace(); while (!scanner.isEOF()) { scanner.readWord(buf_word, 0); scanner.skipSpace(); if(strcasecmp(buf_word.ptr(), "B")==0) { size_in = CacheParams::SIZE_IN_BYTES; } else if(strcasecmp(buf_word.ptr(), "KB")==0) { size_in = CacheParams::SIZE_IN_KB; } else if(strcasecmp(buf_word.ptr(), "MB")==0) { size_in = CacheParams::SIZE_IN_MB; } else if(strcasecmp(buf_word.ptr(), "GB")==0) { size_in = CacheParams::SIZE_IN_GB; } else { throw BingoPgError("unknown parameter: %s", buf_word.ptr()); } if (scanner.isEOF()) break; scanner.skipSpace(); } } ~CacheParams(){} private: CacheParams(const CacheParams&); }; Datum _precache_database(PG_FUNCTION_ARGS){ Oid relOid = PG_GETARG_OID(0); Datum parameters_datum = PG_GETARG_DATUM(1); void* res = 0; Relation rel; /* * */ rel = relation_open(relOid, AccessShareLock); PG_BINGO_BEGIN { BingoPgText parameters_text(parameters_datum); /* * Parse parameters */ CacheParams params; params.parseParameters(parameters_text.getString()); Array tmp_buffer; Array tmp_buffer2; Array result_buf; ArrayOutput result(result_buf); ItemPointerData item_buf; int buf_size = 0; int processed_num = 0; qword total_cache_size = 0; qword total_index_size = 0; qword index_metapages_size = 0; qword block_metapages_size = 0; qword cmf_real_size = 0; qword cmf_cache_size = 0; qword sim_real_size = 0; qword sim_cache_size = 0; qword xyz_real_size = 0; qword xyz_cache_size = 0; qword fp_cache_size = 0; result.printfCR("{"); BingoPgIndex bingo_index(rel); int section_idx = bingo_index.readBegin(); int section_num = bingo_index.readEnd(); result.printfCR("structures_number : %d,", bingo_index.getStructuresNumber()); result.printfCR("blocks_number : %d,", bingo_index.getSectionNumber()); params.getSizeIn(tmp_buffer); result.printfCR("size_in : '%s',", tmp_buffer.ptr()); /* * Calc dictionary buf size */ buf_size = bingo_index.getDictCount() * BingoPgBufferCacheBin::BUFFER_SIZE; result.printfCR("dict_cache_size : %d,", params.getSize(buf_size)); total_cache_size += buf_size; total_index_size += BINGO_DICTIONARY_BLOCKS_NUM * BingoPgBufferCacheBin::BUFFER_SIZE; /* * Read config and offset blocks as meta */ BingoPgConfig bingo_config; bingo_index.readConfigParameters(bingo_config); index_metapages_size += BINGO_METABLOCKS_NUM * BingoPgBufferCacheBin::BUFFER_SIZE; total_cache_size += index_metapages_size; total_index_size += index_metapages_size; buf_size = section_num / BINGO_SECTION_OFFSET_PER_BLOCK + 1; index_metapages_size += buf_size * BingoPgBufferCacheBin::BUFFER_SIZE; total_cache_size += buf_size * BingoPgBufferCacheBin::BUFFER_SIZE; total_index_size += BINGO_SECTION_OFFSET_BLOCKS_NUM * BingoPgBufferCacheBin::BUFFER_SIZE; BingoPgExternalBitset bitset_buf; int str_num = 0; for (; section_idx < bingo_index.readEnd(); section_idx = bingo_index.readNext(section_idx)) { str_num = bingo_index.getSectionStructuresNumber(section_idx); /* * Exist structures as section metapages */ bingo_index.getSectionBitset(section_idx, bitset_buf); block_metapages_size += 2* BingoPgBufferCacheBin::BUFFER_SIZE; cmf_real_size = 0; for(int str_idx = 0; str_idx < str_num; ++str_idx) { /* * Cmf real size */ bingo_index.readCmfItem(section_idx, str_idx, tmp_buffer); cmf_real_size += tmp_buffer.sizeInBytes(); /* * Mapping buffers */ bingo_index.readTidItem(section_idx, str_idx, &item_buf); } /* * Sim buffers */ //bingo_index.getSectionBitsCount(section_idx, tmp_buffer2); //sim_real_size += tmp_buffer2.sizeInBytes(); /* * Cmf buffer size */ cmf_cache_size += ((cmf_real_size / BingoPgBufferCacheBin::MAX_SIZE) + 1) * BingoPgBufferCacheBin::BUFFER_SIZE; /* * Block mapping as metapages size */ buf_size = str_num / BINGO_MOLS_PER_MAPBLOCK + 1; block_metapages_size += buf_size * BingoPgBufferCacheBin::BUFFER_SIZE; /* * Sim buffers */ buf_size = str_num / BingoPgSection::SECTION_BITS_PER_BLOCK + 1; sim_cache_size += buf_size * BingoPgBufferCacheBin::BUFFER_SIZE; int fp_size = bingo_index.getSectionInfo(section_idx).n_blocks_for_fp; bitset_buf.clear(); for(int fp_idx = 0; fp_idx < fp_size; ++fp_idx) { bingo_index.andWithBitset(section_idx, fp_idx, bitset_buf); } fp_cache_size += (fp_size * BingoPgBufferCacheBin::BUFFER_SIZE); /* * Total section size */ buf_size = bingo_index.getSectionInfo(section_idx).section_size; total_index_size += buf_size * BingoPgBufferCacheBin::BUFFER_SIZE; elog(NOTICE, "%d blocks processed", section_idx + 1); } total_cache_size += sim_cache_size; total_cache_size += fp_cache_size; total_cache_size += block_metapages_size; total_cache_size += cmf_cache_size; result.printfCR("index_metapages_size : %d,", params.getSize(index_metapages_size)); result.printfCR("block_metapages_size : %d,", params.getSize(block_metapages_size)); result.printfCR("cmf_cache_size : %d,", params.getSize(cmf_cache_size)); result.printfCR("sim_cache_size : %d,", params.getSize(sim_cache_size)); result.printfCR("fp_cache_size : %d,", params.getSize(fp_cache_size)); result.printfCR("total_cache_size : %d", params.getSize(total_cache_size)); result.printfCR("total_index_size : %d", params.getSize(total_index_size)); result.printf("}"); result_buf.push(0); elog(NOTICE, "%s", result_buf.ptr()); BingoPgText res_text; res_text.initFromString(result_buf.ptr()); res = res_text.release(); } PG_BINGO_END relation_close(rel, AccessShareLock); if(res == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(res); } //Datum bingo_test(PG_FUNCTION_ARGS) { // elog(NOTICE, "start test function 3"); // PG_RETURN_VOID(); //} // //Datum bingo_test_tid(PG_FUNCTION_ARGS) { // elog(NOTICE, "start test function tid"); // // ItemPointer pp = (ItemPointer) palloc0(sizeof(ItemPointerData)); // // ItemPointerSet(pp, 1, 2); // // PG_RETURN_POINTER(pp); //} // //static Oid getFunc(const char* name, Array& types) { // Array fname; // fname.readString(name, true); // Value* func_name = makeString(fname.ptr()); // // List* func_list = list_make1(func_name); // Oid func_oid = LookupFuncName(func_list, types.size(), types.ptr(), false); // // if(func_oid == InvalidOid) // elog(ERROR, "can not find the function %s", name); // // list_free(func_list); // return func_oid; //} //Datum bingo_test_select(PG_FUNCTION_ARGS) { // elog(INFO, "start test select"); // // Array func_type; // func_type.push(TEXTOID); // // Oid func_begin_oid = getFunc("bingo_test_cur_begin", func_type); // // FmgrInfo f_begin_info; // fmgr_info(func_begin_oid, &f_begin_info); // // func_type.clear(); // func_type.push(REFCURSOROID); // // Oid func_next_oid = getFunc("bingo_test_cur_next", func_type); // // FmgrInfo f_next_info; // fmgr_info(func_next_oid, &f_next_info); // // elog(INFO, "func = %d", func_begin_oid); // // BingoPgText test_select; // test_select.initFromString("btest_shadow"); // // Datum cursor_ref = FunctionCall1(&f_begin_info, PointerGetDatum(test_select.ptr())); // // BingoPgText res_text(cursor_ref); // elog(INFO, "res text = %s", res_text.getString()); // // // Datum record; // ItemPointer tup; // for (int i = 0; i < 5; ++i) { // record = FunctionCall1(&f_next_info, cursor_ref); // if(record == 0) { // elog(INFO, "Rec is null"); // continue; // } // tup = (ItemPointer) DatumGetPointer(record); // elog(INFO, "block = %d off = %d", ItemPointerGetBlockNumber(tup), ItemPointerGetOffsetNumber(tup)); // } // // PG_RETURN_VOID(); //} /* * Save current scan position */ Datum bingo_markpos(PG_FUNCTION_ARGS) { elog(ERROR, "bingo does not support mark/restore"); PG_RETURN_VOID(); } /* * Restore scan to last saved position */ Datum bingo_restrpos(PG_FUNCTION_ARGS) { elog(ERROR, "bingo does not support mark/restore"); PG_RETURN_VOID(); } void bingo_redo(XLogRecPtr lsn, XLogRecord *record) { elog(PANIC, "bingo_redo: unimplemented"); } void bingo_desc(StringInfo buf, uint8 xl_info, char *rec) { } Datum getversion(PG_FUNCTION_ARGS) { BingoPgText result_text; result_text.initFromString(bingoGetVersion()); PG_RETURN_TEXT_P(result_text.release()); } Datum filetotext(PG_FUNCTION_ARGS) { Datum file_name_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgText fname_text(file_name_datum); BingoPgText result_text; QS_DEF(Array, buffer); buffer.clear(); /* * Read file */ FileScanner f_scanner(fname_text.getString()); f_scanner.readAll(buffer); result_text.initFromArray(buffer); result = result_text.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum filetoblob(PG_FUNCTION_ARGS) { Datum file_name_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgText fname_text(file_name_datum); BingoPgText result_text; QS_DEF(Array, buffer); buffer.clear(); /* * Read file */ FileScanner f_scanner(fname_text.getString()); f_scanner.readAll(buffer); result_text.initFromArray(buffer); result = result_text.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_BYTEA_P(result); } Datum getname(PG_FUNCTION_ARGS) { Datum target_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("getname"); BingoPgText mol_text(target_datum); int buf_size; const char* target_buf = mol_text.getText(buf_size); const char* bingo_result = bingoGetNameCore(target_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.getname", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } static void _parseQueryFieldList(const char* fields_str, RedBlackStringMap& field_list) { BufferScanner scanner(fields_str); QS_DEF(Array, buf_word); scanner.skipSpace(); int column_idx = field_list.size(); while (!scanner.isEOF()) { scanner.readWord(buf_word, " ,"); scanner.skipSpace(); if (field_list.find(buf_word.ptr())) throw BingoPgError("parseQueryFieldList(): key %s is already presented in the query list", buf_word.ptr()); ++column_idx; field_list.insert(buf_word.ptr(), column_idx); if (scanner.isEOF()) break; if (scanner.readChar() != ',') throw BingoPgError("parseQueryFieldList(): comma expected"); scanner.skipSpace(); } } static void checkExportNull(Datum text_datum, const char* message, BingoPgText& text) { if(text_datum == 0) throw BingoPgError("can not export structures: %s is empty", message); text.init(text_datum); int text_size = 0; text.getText(text_size); if(text_size == 0) throw BingoPgError("can not export structures: %s is empty", message); } static void checkExportEmpty(Datum text_datum, BingoPgText& text) { if(text_datum == 0) text.initFromString(""); else text.init(text_datum); } static int _initializeColumnQuery(Datum table_datum, Datum column_datum, Datum other_column_datum, Array& query_str, RedBlackStringMap& field_list) { BingoPgText tablename_text; BingoPgText column_text; BingoPgText other_column_text; checkExportNull(table_datum, "table name", tablename_text); checkExportNull(column_datum, "column name", column_text); checkExportEmpty(other_column_datum, other_column_text); field_list.clear(); field_list.insert(column_text.getString(), 0); int data_key = field_list.begin(); ArrayOutput query_out(query_str); query_out.printf("SELECT %s", column_text.getString()); if(other_column_datum != 0 ) { const char* columns_list = other_column_text.getString(); if(strcmp(columns_list, "") !=0) { _parseQueryFieldList(columns_list, field_list); query_out.printf(", %s", columns_list); } } query_out.printf(" FROM %s", tablename_text.getString()); query_out.writeChar(0); return data_key; } //class BingoExportSdfHandler : public BingoPgCommon::BingoSessionHandler { //public: // BingoExportSdfHandler(unsigned int func_id, const char* fname):BingoSessionHandler(func_id, true) { // setFunctionName("exportSDF"); // bingoSDFExportOpen(fname); //// SPI_connect(); // } // virtual ~BingoImportSdfHandler() { //// SPI_finish(); // bingoSDFExportClose(); // } // //private: // BingoExportSdfHandler(const BingoExportSdfHandler&); //no implicit copy //}; Datum exportsdf(PG_FUNCTION_ARGS) { Datum table_datum = PG_GETARG_DATUM(0); Datum column_datum = PG_GETARG_DATUM(1); Datum other_columns_datum = PG_GETARG_DATUM(2); Datum file_name_datum = PG_GETARG_DATUM(3); PG_BINGO_BEGIN { QS_DEF(Array, query_str); RedBlackStringMap field_list; BingoPgText fname_text; checkExportNull(file_name_datum, "file name", fname_text); FileOutput file_output(fname_text.getString()); int data_key = _initializeColumnQuery(table_datum, column_datum, other_columns_datum, query_str, field_list); BingoPgCursor table_cursor(query_str.ptr()); BingoPgText buf_text; while (table_cursor.next()) { table_cursor.getText(1, buf_text); file_output.writeStringCR(buf_text.getString()); for (int k = field_list.begin(); k != field_list.end(); k = field_list.next(k)) { if(data_key == k) continue; int col_idx = field_list.value(k); const char* col_name = field_list.key(k); table_cursor.getText(col_idx, buf_text); file_output.printf("> <%s>\n", col_name); file_output.printf("%s\n\n", buf_text.getString()); } file_output.printf("\n$$$$\n"); } } PG_BINGO_END PG_RETURN_VOID(); } Datum exportrdf(PG_FUNCTION_ARGS) { Datum table_datum = PG_GETARG_DATUM(0); Datum column_datum = PG_GETARG_DATUM(1); Datum other_columns_datum = PG_GETARG_DATUM(2); Datum file_name_datum = PG_GETARG_DATUM(3); PG_BINGO_BEGIN { QS_DEF(Array, query_str); RedBlackStringMap field_list; BingoPgText fname_text; checkExportNull(file_name_datum, "file name", fname_text); FileOutput file_output(fname_text.getString()); int data_key = _initializeColumnQuery(table_datum, column_datum, other_columns_datum, query_str, field_list); BingoPgCursor table_cursor(query_str.ptr()); BingoPgText buf_text; file_output.printf("$RDFILE 1\n"); int str_idx = 0; while (table_cursor.next()) { ++str_idx; table_cursor.getText(1, buf_text); file_output.printf("$MFMT $MIREG %d\n", str_idx); file_output.writeStringCR(buf_text.getString()); for (int k = field_list.begin(); k != field_list.end(); k = field_list.next(k)) { if(data_key == k) continue; int col_idx = field_list.value(k); const char* col_name = field_list.key(k); table_cursor.getText(col_idx, buf_text); file_output.printf("$DTYPE %s\n", col_name); file_output.printf("$DATUM %s\n", buf_text.getString()); } } } PG_BINGO_END PG_RETURN_VOID(); } Datum _reset_profiling_info(PG_FUNCTION_ARGS) { bingoProfilingReset(true); PG_RETURN_VOID(); } Datum _get_profiling_info(PG_FUNCTION_ARGS) { char* result = 0; PG_BINGO_BEGIN { const char* bingo_result = bingoProfilingGetStatistics(true); result = BingoPgCommon::releaseString(bingo_result); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_CSTRING(result); } Datum _print_profiling_info(PG_FUNCTION_ARGS) { PG_BINGO_BEGIN { const char* bingo_result = bingoProfilingGetStatistics(true); elog(NOTICE, "\n%s", bingo_result); } PG_BINGO_END PG_RETURN_VOID(); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_mango_match.cpp000066400000000000000000000213331271037650300244700ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_common.h" #include "pg_bingo_context.h" #include "bingo_core_c.h" #include "bingo_pg_config.h" #include "bingo_pg_text.h" extern "C" { BINGO_FUNCTION_EXPORT(_sub_internal); BINGO_FUNCTION_EXPORT(_smarts_internal); BINGO_FUNCTION_EXPORT(_exact_internal); BINGO_FUNCTION_EXPORT(getsimilarity); BINGO_FUNCTION_EXPORT(_gross_internal); BINGO_FUNCTION_EXPORT(_sim_internal); BINGO_FUNCTION_EXPORT(_match_mass_less); BINGO_FUNCTION_EXPORT(_match_mass_great); BINGO_FUNCTION_EXPORT(_mass_in); BINGO_FUNCTION_EXPORT(_mass_out); } using namespace indigo; /* * Helper class for searching setup and perfoming */ class _MangoContextHandler: public BingoPgCommon::BingoSessionHandler { public: _MangoContextHandler(int type, unsigned int func_oid) : BingoSessionHandler(func_oid), _type(type) { BingoPgCommon::getSearchTypeString(_type, _typeStr, true); setFunctionName(_typeStr.ptr()); } virtual ~_MangoContextHandler() { } /* * Match method * Returns true if matching is successfull * Throws an error if query can not be loaded */ int matchInternal(Datum query_datum, Datum target_datum, Datum options_datum) { BingoPgText query_text(query_datum); BingoPgText target_text(target_datum); BingoPgText options_text(options_datum); /* * Set up match parameters */ int res = mangoSetupMatch(_typeStr.ptr(), query_text.getString(), options_text.getString()); if (res < 0) throw BingoPgError("Error while bingo%s loading molecule: %s", _typeStr.ptr(), bingoGetError()); int target_size; const char* target_data = target_text.getText(target_size); QS_DEF(Array, buffer_warn); if(_type == BingoPgCommon::MOL_GROSS) { buffer_warn.readString(_typeStr.ptr(), true); const char* mol_name = bingoGetNameCore(target_data, target_size); if(mol_name != 0 && strlen(mol_name) > 0) { buffer_warn.appendString(" molecule with name='", true); buffer_warn.appendString(mol_name, true); buffer_warn.appendString("'", true); } setFunctionName(buffer_warn.ptr()); target_data = mangoGross(target_data, target_size); if(target_data == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.gross", bingoGetError()); return -1; } } res = mangoMatchTarget(target_data, target_size); if (res < 0) { buffer_warn.readString(bingoGetWarning(), true); const char* mol_name = bingoGetNameCore(target_data, target_size); if(mol_name != 0 && strlen(mol_name) > 0) elog(WARNING, "warning while bingo%s loading molecule with name ='%s': %s", _typeStr.ptr(), mol_name, buffer_warn.ptr()); else elog(WARNING, "warning while bingo%s loading molecule: %s", _typeStr.ptr(), buffer_warn.ptr()); } return res; } private: _MangoContextHandler(const _MangoContextHandler&);//no implicit copy int _type; indigo::Array _typeStr; }; Datum _sub_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _MangoContextHandler bingo_context(BingoPgCommon::MOL_SUB, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum _smarts_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _MangoContextHandler bingo_context(BingoPgCommon::MOL_SMARTS, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum _exact_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _MangoContextHandler bingo_context(BingoPgCommon::MOL_EXACT, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum getsimilarity(PG_FUNCTION_ARGS) { Datum target_datum = PG_GETARG_DATUM(0); Datum query_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); float res = 0; PG_BINGO_BEGIN { int result = 0; _MangoContextHandler bingo_context(BingoPgCommon::MOL_SIM, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); if (result > 0) mangoSimilarityGetScore(&res); } PG_BINGO_END PG_RETURN_FLOAT4(res); } Datum _gross_internal(PG_FUNCTION_ARGS) { Datum query_sign = PG_GETARG_DATUM(0); Datum query_datum = PG_GETARG_DATUM(1); Datum target_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { BingoPgText query_text(query_datum); BingoPgText sign_text(query_sign); QS_DEF(indigo::Array, bingo_query); bingo_query.readString(sign_text.getString(), false); bingo_query.appendString(" ", false); bingo_query.appendString(query_text.getString(), false); query_text.initFromArray(bingo_query); _MangoContextHandler bingo_context(BingoPgCommon::MOL_GROSS, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_text.getDatum(), target_datum, 0); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum _sim_internal(PG_FUNCTION_ARGS) { float min_bound = PG_GETARG_FLOAT4(0); float max_bound = PG_GETARG_FLOAT4(1); Datum query_datum = PG_GETARG_DATUM(2); Datum target_datum = PG_GETARG_DATUM(3); Datum options_datum = PG_GETARG_DATUM(4); int result = 0; bool res_bool = false; PG_BINGO_BEGIN { _MangoContextHandler bingo_context(BingoPgCommon::MOL_SIM, fcinfo->flinfo->fn_oid); float mol_sim = 0; result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); if (result > 0) mangoSimilarityGetScore(&mol_sim); res_bool = (mol_sim <= max_bound) && (mol_sim >= min_bound); } PG_BINGO_END PG_RETURN_BOOL(res_bool); } Datum _match_mass_less(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); char* mass_datum = PG_GETARG_CSTRING(1); bool result = false; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("mass less"); BufferScanner scanner(mass_datum); float usr_mass = scanner.readFloat(); BingoPgText mol_text(mol_datum); float mol_mass = 0; int buf_len, bingo_res; const char* buf = mol_text.getText(buf_len); bingo_res = mangoMass(buf, buf_len, 0, &mol_mass); CORE_HANDLE_ERROR(bingo_res, 1, "mass matcher: error while calculating mass", bingoGetError()); result = mol_mass < usr_mass; } PG_BINGO_END PG_RETURN_BOOL(result); } Datum _match_mass_great(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); char* mass_datum = PG_GETARG_CSTRING(1); bool result = false; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("mass great"); BufferScanner scanner(mass_datum); float usr_mass = scanner.readFloat(); BingoPgText mol_text(mol_datum); float mol_mass = 0; int buf_len, bingo_res; const char* buf = mol_text.getText(buf_len); bingo_res = mangoMass(buf, buf_len, 0, &mol_mass); CORE_HANDLE_ERROR(bingo_res, 1, "mass matcher: error while calculating mass", bingoGetError()); result = mol_mass > usr_mass; } PG_BINGO_END PG_RETURN_BOOL(result); } Datum _mass_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); int size = strlen(str) + 1; char* result = (char*) palloc(size); memcpy(result, str, size); PG_RETURN_POINTER(result); } Datum _mass_out(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); int size = strlen(str) + 1; char* result = (char*) palloc(size); memcpy(result, str, size); PG_RETURN_CSTRING(result); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_mango_utils.cpp000066400000000000000000000236431271037650300245420ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_text.h" #include "bingo_core_c.h" #include "bingo_pg_common.h" extern "C" { BINGO_FUNCTION_EXPORT(smiles); BINGO_FUNCTION_EXPORT(cansmiles); BINGO_FUNCTION_EXPORT(molfile); BINGO_FUNCTION_EXPORT(cml); BINGO_FUNCTION_EXPORT(checkmolecule); BINGO_FUNCTION_EXPORT(gross); BINGO_FUNCTION_EXPORT(getweight); BINGO_FUNCTION_EXPORT(getmass); BINGO_FUNCTION_EXPORT(fingerprint); BINGO_FUNCTION_EXPORT(compactmolecule); BINGO_FUNCTION_EXPORT(inchi); BINGO_FUNCTION_EXPORT(inchikey); } Datum smiles(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("smiles"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoSMILES(mol_buf, buf_size, 0); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.smiles", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum cansmiles(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("cansmiles"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoSMILES(mol_buf, buf_size, 1); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.cansmiles", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum molfile(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("molfile"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoMolfile(mol_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.molfile", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum cml(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("cml"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoCML(mol_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.cml", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum checkmolecule(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("checkmolecule"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoCheckMolecule(mol_buf, buf_size); if(bingo_result == 0) PG_RETURN_NULL(); BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum gross(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("gross"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); const char* bingo_result = mangoGross(mol_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.gross", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum getweight(PG_FUNCTION_ARGS){ Datum mol_datum = PG_GETARG_DATUM(0); Datum options_datum = PG_GETARG_DATUM(1); float result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("getweight"); BingoPgText mol_text(mol_datum); BingoPgText mol_options(options_datum); int buf_len, bingo_res; const char* buf = mol_text.getText(buf_len); bingo_res = mangoMass(buf, buf_len, mol_options.getString(), &result); if(bingo_res < 1) { CORE_HANDLE_WARNING(0, 1, "bingo.getweight", bingoGetError()); PG_RETURN_NULL(); } } PG_BINGO_END PG_RETURN_FLOAT4(result); } Datum getmass(PG_FUNCTION_ARGS){ Datum mol_datum = PG_GETARG_DATUM(0); float result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("getmass"); BingoPgText mol_text(mol_datum); int buf_len, bingo_res; const char* buf = mol_text.getText(buf_len); bingo_res = mangoMass(buf, buf_len, 0, &result); if(bingo_res < 1) { CORE_HANDLE_WARNING(0, 1, "bingo.getmass", bingoGetError()); PG_RETURN_NULL(); } } PG_BINGO_END PG_RETURN_FLOAT4(result); } Datum fingerprint(PG_FUNCTION_ARGS){ Datum mol_datum = PG_GETARG_DATUM(0); Datum options_datum = PG_GETARG_DATUM(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("fingerprint"); BingoPgText mol_text(mol_datum); BingoPgText mol_options(options_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); int res_buf; const char* bingo_result = mangoFingerprint(mol_buf, buf_size, mol_options.getString(), &res_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.fingerprint", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_data; result_data.initFromBuffer(bingo_result, res_buf); result = result_data.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_BYTEA_P(result); } Datum compactmolecule(PG_FUNCTION_ARGS){ Datum mol_datum = PG_GETARG_DATUM(0); bool options_xyz = PG_GETARG_BOOL(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("compactmolecule"); BingoPgText mol_text(mol_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); int res_buf; const char* bingo_result = mangoICM(mol_buf, buf_size, options_xyz, &res_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.compactmolecule", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_data; result_data.initFromBuffer(bingo_result, res_buf); result = result_data.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_BYTEA_P(result); } Datum inchi(PG_FUNCTION_ARGS){ Datum mol_datum = PG_GETARG_DATUM(0); Datum options_datum = PG_GETARG_DATUM(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("inchi"); BingoPgText mol_text(mol_datum); BingoPgText mol_options(options_datum); int buf_size; const char* mol_buf = mol_text.getText(buf_size); int res_buf; const char* bingo_result = mangoInChI(mol_buf, buf_size, mol_options.getString(), &res_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.inchi", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum inchikey(PG_FUNCTION_ARGS) { Datum mol_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("inchikey"); BingoPgText mol_text(mol_datum); const char* mol_buf = mol_text.getString(); const char* bingo_result = mangoInChIKey(mol_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.inchikey", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_ringo_match.cpp000066400000000000000000000073351271037650300245130ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_common.h" #include "pg_bingo_context.h" #include "bingo_core_c.h" #include "bingo_pg_config.h" #include "bingo_pg_text.h" extern "C" { BINGO_FUNCTION_EXPORT(_rsub_internal); BINGO_FUNCTION_EXPORT(_rsmarts_internal); BINGO_FUNCTION_EXPORT(_rexact_internal); } using namespace indigo; /* * Helper class for searching setup and perfoming */ class _RingoContextHandler :public BingoPgCommon::BingoSessionHandler { public: _RingoContextHandler(int type, unsigned int func_oid) : BingoSessionHandler(func_oid), _type(type) { BingoPgCommon::getSearchTypeString(_type, _typeStr, false); setFunctionName(_typeStr.ptr()); } virtual ~_RingoContextHandler() { } /* * Match method * Returns true if matching is successfull * Throws an error if query can not be loaded */ int matchInternal(Datum query_datum, Datum target_datum, Datum options_datum) { BingoPgText query_text(query_datum); BingoPgText target_text(target_datum); BingoPgText options_text(options_datum); /* * Set up match parameters */ int res = ringoSetupMatch(_typeStr.ptr(), query_text.getString(), options_text.getString()); if (res < 0) throw BingoPgError("Error while bingo%s loading a reaction: %s", _typeStr.ptr(), bingoGetError()); int target_size; const char* target_data = target_text.getText(target_size); res = ringoMatchTarget(target_data, target_size); if (res < 0) { QS_DEF(Array, buffer_warn); buffer_warn.readString(bingoGetWarning(), true); const char* react_name = bingoGetNameCore(target_data, target_size); if(react_name != 0 && strlen(react_name) > 0) elog(WARNING, "warning while bingo%s loading a reaction with name='%s': %s", _typeStr.ptr(), react_name, buffer_warn.ptr()); else elog(WARNING, "warning while bingo%s loading a reaction: %s", _typeStr.ptr(), buffer_warn.ptr()); } return res; } private: _RingoContextHandler(const _RingoContextHandler&);//no implicit copy int _type; indigo::Array _typeStr; }; Datum _rsub_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _RingoContextHandler bingo_context(BingoPgCommon::REACT_SUB, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum _rsmarts_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _RingoContextHandler bingo_context(BingoPgCommon::REACT_SMARTS, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Datum _rexact_internal(PG_FUNCTION_ARGS) { Datum query_datum = PG_GETARG_DATUM(0); Datum target_datum = PG_GETARG_DATUM(1); Datum options_datum = PG_GETARG_DATUM(2); int result = 0; PG_BINGO_BEGIN { _RingoContextHandler bingo_context(BingoPgCommon::REACT_EXACT, fcinfo->flinfo->fn_oid); result = bingo_context.matchInternal(query_datum, target_datum, options_datum); if(result < 0) PG_RETURN_NULL(); } PG_BINGO_END PG_RETURN_BOOL(result>0); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_am/pg_ringo_utils.cpp000066400000000000000000000143121271037650300245500ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_postgres.h" #include "bingo_pg_text.h" #include "bingo_core_c.h" #include "bingo_pg_common.h" extern "C" { BINGO_FUNCTION_EXPORT(aam); BINGO_FUNCTION_EXPORT(rxnfile); BINGO_FUNCTION_EXPORT(rcml); BINGO_FUNCTION_EXPORT(checkreaction); BINGO_FUNCTION_EXPORT(rsmiles); BINGO_FUNCTION_EXPORT(rfingerprint); BINGO_FUNCTION_EXPORT(compactreaction); } Datum aam(PG_FUNCTION_ARGS) { Datum react_datum = PG_GETARG_DATUM(0); Datum mode_datum = PG_GETARG_DATUM(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("aam"); BingoPgText react_text(react_datum); BingoPgText aam_mode(mode_datum); int buf_size; const char* react_buf = react_text.getText(buf_size); const char* bingo_result = ringoAAM(react_buf, buf_size, aam_mode.getString()); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.AAM", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum rxnfile(PG_FUNCTION_ARGS) { Datum react_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("rxnfile"); BingoPgText react_text(react_datum); int buf_size; const char* react_buf = react_text.getText(buf_size); const char* bingo_result = ringoRxnfile(react_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.rxnfile", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum rcml(PG_FUNCTION_ARGS) { Datum react_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("rcml"); BingoPgText react_text(react_datum); int buf_size; const char* react_buf = react_text.getText(buf_size); const char* bingo_result = ringoRCML(react_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.rcml", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum checkreaction(PG_FUNCTION_ARGS) { Datum react_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("checkreaction"); BingoPgText react_text(react_datum); int buf_size; const char* react_buf = react_text.getText(buf_size); const char* bingo_result = ringoCheckReaction(react_buf, buf_size); if(bingo_result == 0) PG_RETURN_NULL(); BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum rsmiles(PG_FUNCTION_ARGS) { Datum react_datum = PG_GETARG_DATUM(0); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("rsmiles"); BingoPgText react_text(react_datum); int buf_size; const char* react_buf = react_text.getText(buf_size); const char* bingo_result = ringoRSMILES(react_buf, buf_size); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.rsmiles", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_text; result_text.initFromString(bingo_result); result = result_text.release(); } PG_BINGO_END if (result == 0) PG_RETURN_NULL(); PG_RETURN_TEXT_P(result); } Datum rfingerprint(PG_FUNCTION_ARGS){ Datum react_datum = PG_GETARG_DATUM(0); Datum options_datum = PG_GETARG_DATUM(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("rfingerprint"); BingoPgText r_text(react_datum); BingoPgText react_options(options_datum); int buf_size; const char* r_buf = r_text.getText(buf_size); int res_buf; const char* bingo_result = ringoFingerprint(r_buf, buf_size, react_options.getString(), &res_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.rfingerprint", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_data; result_data.initFromBuffer(bingo_result, res_buf); result = result_data.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_BYTEA_P(result); } Datum compactreaction(PG_FUNCTION_ARGS){ Datum react_datum = PG_GETARG_DATUM(0); Datum options_xyz = PG_GETARG_BOOL(1); void* result = 0; PG_BINGO_BEGIN { BingoPgCommon::BingoSessionHandler bingo_handler(fcinfo->flinfo->fn_oid); bingo_handler.setFunctionName("compactreaction"); BingoPgText r_text(react_datum); int buf_size; const char* r_buf = r_text.getText(buf_size); int res_buf; const char* bingo_result = ringoICR(r_buf, buf_size, options_xyz, &res_buf); if(bingo_result == 0) { CORE_HANDLE_WARNING(0, 1, "bingo.compactreaction", bingoGetError()); PG_RETURN_NULL(); } BingoPgText result_data; result_data.initFromBuffer(bingo_result, res_buf); result = result_data.release(); } PG_BINGO_END if(result == 0) PG_RETURN_NULL(); PG_RETURN_BYTEA_P(result); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/000077500000000000000000000000001271037650300217125ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_buffer.cpp000066400000000000000000000171611271037650300255410ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "storage/bufmgr.h" #include "utils/rel.h" #include "storage/bufmgr.h" #include "access/itup.h" #include "utils/relcache.h" #include "storage/lock.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_buffer.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "bingo_pg_common.h" #include "base_cpp/tlscont.h" using namespace indigo; IMPL_ERROR(BingoPgBuffer, "bingo buffer"); /* * Empty buffer constructor */ BingoPgBuffer::BingoPgBuffer(): _buffer(InvalidBuffer), _lock(BINGO_PG_NOLOCK), _blockIdx(0){ } /* * New buffer constructor */ BingoPgBuffer::BingoPgBuffer(PG_OBJECT rel_ptr, unsigned int block_num): _buffer(InvalidBuffer), _lock(BINGO_PG_NOLOCK), _blockIdx(0) { writeNewBuffer(rel_ptr, block_num); } /* * Existing buffer constructor */ BingoPgBuffer::BingoPgBuffer(PG_OBJECT rel_ptr, unsigned int block_num, int lock): _buffer(InvalidBuffer), _lock(BINGO_PG_NOLOCK), _blockIdx(0) { readBuffer(rel_ptr, block_num, lock); } /* * Destructor */ BingoPgBuffer::~BingoPgBuffer() { clear(); } /* * Changes an access for the buffer */ void BingoPgBuffer::changeAccess(int lock) { if(_buffer == InvalidBuffer) return; if (_lock == BINGO_PG_WRITE) { BINGO_PG_TRY { MarkBufferDirty(_buffer); } BINGO_PG_HANDLE(throw Error("internal error: can not set buffer dirty %d: %s", _buffer, message)); } BINGO_PG_TRY { if (_lock != BINGO_PG_NOLOCK) { LockBuffer(_buffer, BUFFER_LOCK_UNLOCK); } if (lock != BINGO_PG_NOLOCK) { LockBuffer(_buffer, _getAccess(lock)); } } BINGO_PG_HANDLE(throw Error("internal error: can not lock the buffer %d: %s", _buffer, message)); _lock = lock; } /* * Writes a new buffer with WRITE lock */ int BingoPgBuffer::writeNewBuffer(PG_OBJECT rel_ptr, unsigned int block_num) { /* * Clear if it is a new buffer */ if(_buffer != InvalidBuffer) { if(_blockIdx != block_num) clear(); else return _buffer; } Relation rel = (Relation) rel_ptr; BlockNumber nblocks = 0; BINGO_PG_TRY { nblocks = RelationGetNumberOfBlocks(rel); } BINGO_PG_HANDLE(throw Error("internal error: can not get number of blocks: %s", message)); /* * Bingo forbids noncontiguous access */ if (block_num > nblocks) { throw Error("internal error: access to noncontiguous page in bingo index"); } // if(block_num < nblocks) // throw Error("internal error: access to already pinned block in bingo index"); /* * smgr insists we use P_NEW to extend the relation */ if (block_num == nblocks) { int buffer_block_num = 0; BINGO_PG_TRY { _buffer = ReadBuffer(rel, P_NEW); buffer_block_num = BufferGetBlockNumber(_buffer); } BINGO_PG_HANDLE(throw Error("internal error: can not create a new buffer %s", message)); if (buffer_block_num != block_num) throw Error("internal error: unexpected relation size: %u, should be %u", buffer_block_num, block_num); } else { BINGO_PG_TRY { _buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block_num, RBM_NORMAL, NULL); } BINGO_PG_HANDLE(throw Error("internal error: can not extend the existing buffer: %s", message)); } /* * Lock buffer on writing */ BINGO_PG_TRY { LockBuffer(_buffer, BUFFER_LOCK_EXCLUSIVE); } BINGO_PG_HANDLE(throw Error("internal error: can not lock the buffer: %s", message)); /* * initialize the page */ // PageInit(BufferGetPage(buf), BufferGetPageSize(buf), sizeof (HashPageOpaqueData)); BINGO_PG_TRY { PageInit(BufferGetPage(_buffer), BufferGetPageSize(buf), 0); } BINGO_PG_HANDLE(throw Error("internal error: can not initialize the page %d: %s", _buffer, message)); _lock = BINGO_PG_WRITE; /* * Store block index */ _blockIdx = block_num; return _buffer; } /* * Reads a buffer */ int BingoPgBuffer::readBuffer(PG_OBJECT rel_ptr, unsigned int block_num, int lock) { /* * Clear a buffer if it is different */ if(_buffer != InvalidBuffer) { if(_blockIdx != block_num) clear(); else { changeAccess(lock); return _buffer; } } Relation rel = (Relation) rel_ptr; Buffer buf = 0; BINGO_PG_TRY { buf = ReadBuffer(rel, block_num); } BINGO_PG_HANDLE(throw Error("internal error: can not read the buffer %d: %s", block_num, message)); /* * Lock buffer */ if (lock != BINGO_PG_NOLOCK) { BINGO_PG_TRY { LockBuffer(buf, _getAccess(lock)); } BINGO_PG_HANDLE(throw Error("internal error: can not lock the buffer %d: %s", buf, message)); } _lock = lock; _buffer = buf; /* * Store block index */ _blockIdx = block_num; return _buffer; } /* * Clears and releases the buffer */ void BingoPgBuffer::clear() { if (_buffer == InvalidBuffer) return; BINGO_PG_TRY { switch (_lock) { case BINGO_PG_WRITE: MarkBufferDirty(_buffer); UnlockReleaseBuffer(_buffer); break; case BINGO_PG_READ: UnlockReleaseBuffer(_buffer); break; case BINGO_PG_NOLOCK: ReleaseBuffer(_buffer); break; default: break; } } BINGO_PG_HANDLE(throw Error("internal error: can not release the buffer %d: %s", _buffer, message)); _buffer = InvalidBuffer; _lock = BINGO_PG_NOLOCK; _blockIdx = 0; } int BingoPgBuffer::_getAccess(int lock) { switch(lock) { case BINGO_PG_WRITE: return BUFFER_LOCK_EXCLUSIVE; case BINGO_PG_READ: return BUFFER_LOCK_SHARE; } return BUFFER_LOCK_UNLOCK; } void* BingoPgBuffer::getIndexData(int& data_len) { char* data_ptr = 0; BINGO_PG_TRY { Page page = BufferGetPage(getBuffer()); IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, BINGO_TUPLE_OFFSET)); int hoff = IndexInfoFindDataOffset(itup->t_info); data_ptr = (char *) itup + hoff; data_len = IndexTupleSize(itup) - hoff; } BINGO_PG_HANDLE(throw Error("internal error: can not get index data from the block %d: %s", _blockIdx, message)); if(data_ptr == 0) throw Error("internal error: empty ptr data for the block %d", _blockIdx); if(data_len < 0) throw Error("internal error: corrupted block %d data len is %d", _blockIdx, data_len); return data_ptr; } void BingoPgBuffer::formIndexTuple(void* map_data, int size) { BINGO_PG_TRY { Page page = BufferGetPage(getBuffer()); Datum map_datum = PointerGetDatum(map_data); TupleDesc index_desc = CreateTemplateTupleDesc(1, false); index_desc->attrs[0]->attlen = size; index_desc->attrs[0]->attalign = 'c'; index_desc->attrs[0]->attbyval = false; bool isnull = false; IndexTuple itup = index_form_tuple(index_desc, &map_datum, &isnull); int itemsz = IndexTupleDSize(*itup); itemsz = MAXALIGN(itemsz); if (PageAddItem(page, (Item) itup, itemsz, 0, false, false) == InvalidOffsetNumber) { pfree(itup); FreeTupleDesc(index_desc); throw Error("internal error: failed to add index item"); } pfree(itup); FreeTupleDesc(index_desc); } BINGO_PG_HANDLE(throw Error("internal error: can not form index tuple: %s", message)); } using namespace indigo; void BingoPgBuffer::formEmptyIndexTuple(int size) { QS_DEF(Array, buf); buf.resize(size); buf.zerofill(); formIndexTuple(buf.ptr(), buf.sizeInBytes()); } bool BingoPgBuffer::isReady() const { return _buffer != InvalidBuffer; }Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_buffer.h000066400000000000000000000025001271037650300251750ustar00rootroot00000000000000#ifndef _BINGO_PG_BUFFER_H__ #define _BINGO_PG_BUFFER_H__ #include "bingo_postgres.h" #include "base_cpp/exception.h" /* * Class for postgres buffers handling */ class BingoPgBuffer { public: /* * Empty buffer constructor */ BingoPgBuffer(); /* * New buffer constructor */ BingoPgBuffer(PG_OBJECT rel, unsigned int block_num); /* * Existing buffer constructor */ BingoPgBuffer(PG_OBJECT rel, unsigned int block_num, int lock); /* * Destructor */ ~BingoPgBuffer(); /* * Changes an access for the buffer */ void changeAccess(int lock); /* * Buffer getter */ int getBuffer() const {return _buffer;} /* * Writes a new buffer with WRITE lock */ int writeNewBuffer(PG_OBJECT rel, unsigned int block_num); /* * Reads a buffer */ int readBuffer(PG_OBJECT rel, unsigned int block_num, int lock); /* * Clears and releases the buffer */ void clear(); void* getIndexData(int& data_len); void formIndexTuple(void* map_data, int size); void formEmptyIndexTuple(int size); bool isReady() const; DECL_ERROR; private: BingoPgBuffer(const BingoPgBuffer&); //no implicit copy int _getAccess(int lock); int _buffer; int _lock; unsigned int _blockIdx; }; #endif /* BINGO_PG_BUFFER_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_buffer_cache.cpp000066400000000000000000000261421271037650300266630ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" #include "bingo_pg_fix_post.h" #include "bingo_pg_buffer_cache.h" #include "base_cpp/output.h" #include "bingo_pg_common.h" #include "base_cpp/scanner.h" using namespace indigo; BingoPgBufferCache::BingoPgBufferCache(int block_id, PG_OBJECT index_ptr, bool write): _blockId(block_id), _index(index_ptr), _write(write){ /* * Create a new buffer if the write strategy */ if(_write) { _buffer.writeNewBuffer(_index, _blockId); /* * Release pin on buffer */ _buffer.clear(); } } IMPL_ERROR(BingoPgBufferCacheMap, "bingo buffer cache"); BingoPgBufferCacheMap::BingoPgBufferCacheMap(int block_id, PG_OBJECT index_ptr, bool write): BingoPgBufferCache(block_id, index_ptr, write) { /* * Prepare cache */ if(_write) { _cache.resize(BINGO_MOLS_PER_MAPBLOCK); _cache.zerofill(); } } BingoPgBufferCacheMap::~BingoPgBufferCacheMap() { if(_write) { /* * Write the cache only in the end */ if(!_buffer.isReady()) { _buffer.readBuffer(_index, _blockId, BINGO_PG_NOLOCK); } _buffer.changeAccess(BINGO_PG_WRITE); _buffer.formIndexTuple(_cache.ptr(), _cache.sizeInBytes()); _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::setTidItem(int map_idx, ItemPointerData& tid_item) { _checkMapIdx(map_idx); if(_write){ _cache[map_idx].tid_map = tid_item; } else { /* * If read strategy then it is an update, so add data directly to the buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_WRITE); int data_len; BingoMapData* map_data = (BingoMapData*)_buffer.getIndexData(data_len); map_data[map_idx].tid_map = tid_item; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::setCmfItem(int map_idx, ItemPointerData& cmf_item) { _checkMapIdx(map_idx); if(_write){ _cache[map_idx].cmf_map = cmf_item; } else { /* * If read strategy then it is an update, so add data directly to the buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_WRITE); int data_len; BingoMapData* map_data = (BingoMapData*)_buffer.getIndexData(data_len); map_data[map_idx].cmf_map = cmf_item; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::setXyzItem(int map_idx, ItemPointerData& xyz_item) { _checkMapIdx(map_idx); if(_write){ _cache[map_idx].xyz_map = xyz_item; } else { /* * If read strategy then it is an update, so add data directly to the buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_WRITE); int data_len; BingoMapData* map_data = (BingoMapData*)_buffer.getIndexData(data_len); map_data[map_idx].xyz_map = xyz_item; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::getTidItem(int map_idx, ItemPointerData& tid_item) { _checkMapIdx(map_idx); if(_write) tid_item = _cache[map_idx].tid_map; else { /* * Read data for a buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; BingoMapData* map_data = (BingoMapData*)_buffer.getIndexData(data_len); tid_item = map_data[map_idx].tid_map; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::getCmfItem(int map_idx, ItemPointerData& cmf_item) { _checkMapIdx(map_idx); if(_write) cmf_item = _cache[map_idx].cmf_map; else { /* * Read data for a buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; BingoMapData* map_data = (BingoMapData*) _buffer.getIndexData(data_len); cmf_item = map_data[map_idx].cmf_map; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::getXyzItem(int map_idx, ItemPointerData& xyz_item) { _checkMapIdx(map_idx); if(_write) xyz_item = _cache[map_idx].xyz_map; else { /* * Read data for a buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; BingoMapData* map_data = (BingoMapData*) _buffer.getIndexData(data_len); xyz_item = map_data[map_idx].xyz_map; _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheMap::_checkMapIdx(int map_idx) { if(map_idx >= BINGO_MOLS_PER_MAPBLOCK) throw Error("internal error: map index %d is out of range %d", map_idx, BINGO_MOLS_PER_MAPBLOCK); } // Revview IMPL_ERROR IMPL_ERROR(BingoPgBufferCacheFp, "bingo buffer cache fingerprints"); BingoPgBufferCacheFp::BingoPgBufferCacheFp(int block_id, PG_OBJECT index_ptr, bool write): BingoPgBufferCache(block_id, index_ptr, write), _cache(BINGO_MOLS_PER_FINGERBLOCK) { /* * Cache already prepared. Clean it */ if(_write) { _cache.zeroFill(); } } BingoPgBufferCacheFp::~BingoPgBufferCacheFp(){ if(_write) { /* * Write the cache only in the end */ if(!_buffer.isReady()) { _buffer.readBuffer(_index, _blockId, BINGO_PG_NOLOCK); } _buffer.changeAccess(BINGO_PG_WRITE); int data_len; void* data = _cache.serialize(data_len); _buffer.formIndexTuple(data, data_len); _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheFp::setBit(int str_idx, bool value) { if(_write) { _cache.set(str_idx, value); } else { /* * If read strategy then it is an update, so add data directly to the buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_WRITE); int data_len; void* data = _buffer.getIndexData(data_len); _cache.deserialize(data, data_len, true); _cache.set(str_idx, value); _buffer.changeAccess(BINGO_PG_NOLOCK); } } bool BingoPgBufferCacheFp::getBit(int str_idx) { bool result; if(_write) { result = _cache.get(str_idx); } else { /* * If read strategy then it is an update, so add data directly to the buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_WRITE); int data_len; void* data = _buffer.getIndexData(data_len); _cache.deserialize(data, data_len, true); result = _cache.get(str_idx); _buffer.changeAccess(BINGO_PG_NOLOCK); } return result; } void BingoPgBufferCacheFp::andWithBitset(BingoPgExternalBitset& ext_bitset) { if(_write) { ext_bitset.andWith(_cache); } else { /* * Read data for a buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; void* data = _buffer.getIndexData(data_len); _cache.deserialize(data, data_len, true); /* * And with bitset */ ext_bitset.andWith(_cache); _buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgBufferCacheFp::getCopy(BingoPgExternalBitset& other) { if(_write) { other.copy(_cache); } else { /* * Read data for a buffer */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; void* data = _buffer.getIndexData(data_len); _cache.deserialize(data, data_len, true); /* * And with bitset */ other.copy(_cache); _buffer.changeAccess(BINGO_PG_NOLOCK); } } // Revview IMPL_ERROR IMPL_ERROR(BingoPgBufferCacheBin, "bingo buffer cache binary data"); BingoPgBufferCacheBin::BingoPgBufferCacheBin(int block_id, PG_OBJECT index_ptr, bool write) : BingoPgBufferCache(block_id, index_ptr, write) { } BingoPgBufferCacheBin::~BingoPgBufferCacheBin() { if(_write) { /* * Write the cache only in the end */ if(!_buffer.isReady()) { _buffer.readBuffer(_index, _blockId, BINGO_PG_NOLOCK); } /* * Store max size always */ _buffer.changeAccess(BINGO_PG_WRITE); _buffer.formEmptyIndexTuple(BUFFER_SIZE); _buffer.changeAccess(BINGO_PG_NOLOCK); _writeCache(); } } bool BingoPgBufferCacheBin::isEnoughSpace(int size) { bool result; if(!_write) { /* * Read cache size from a buffer */ _readCache(); } result = (size + _cache.size()) < MAX_SIZE; return result; } unsigned short BingoPgBufferCacheBin::addBin(indigo::Array& bin_buf) { /* * If read strategy then it is an update so read the buffer in this function also */ if(!isEnoughSpace(bin_buf.sizeInBytes())) throw Error("internal error: can not add cmf to the cache because is not enough space"); /* * Prepare output offset */ unsigned short result = _cache.size(); /* * Prepare new array = size + buf */ indigo::Array out_arr; indigo::ArrayOutput ao(out_arr); BingoPgCommon::DataProcessing::handleArray(bin_buf, 0, &ao); /* * Store data with it size */ _cache.concat(out_arr); /* * If read strategy then it is an update so write the buffer */ if(!_write) { _writeCache(); } return result; } unsigned short BingoPgBufferCacheBin::writeBin(indigo::Array& bin_buf) { /* * If read strategy then it is an update so read the buffer in this function also */ if(bin_buf.sizeInBytes() > MAX_SIZE) throw Error("internal error: can not add bin to the cache because is not enough space"); /* * Prepare output offset */ unsigned short result = 0; /* * Prepare new array = size + buf */ indigo::Array out_arr; indigo::ArrayOutput ao(out_arr); BingoPgCommon::DataProcessing::handleArray(bin_buf, 0, &ao); /* * Store data with it size */ _cache.copy(out_arr); /* * If read strategy then it is an update so write the buffer */ if(!_write) { _writeCache(); } return result; } void BingoPgBufferCacheBin::readBin(unsigned short offset, indigo::Array& result) { if(!_write) { _readCache(); } /* * Read buffer from the given offset */ const char* data = _cache.ptr(); int data_len = _cache.sizeInBytes(); BufferScanner sc(data + offset, data_len - offset); BingoPgCommon::DataProcessing::handleArray(result, &sc, 0); } void BingoPgBufferCacheBin::_writeCache() { /* * Write size and cache itself */ if(!_buffer.isReady()) _buffer.readBuffer(_index, _blockId, BINGO_PG_NOLOCK); _buffer.changeAccess(BINGO_PG_WRITE); int data_len; char* buf_data = (char*)_buffer.getIndexData(data_len); int cache_size = _cache.sizeInBytes(); /* * Store size */ memcpy(buf_data, &cache_size, sizeof (int)); /* * Store cache data */ memcpy(buf_data + sizeof (int), _cache.ptr(), cache_size); _buffer.changeAccess(BINGO_PG_NOLOCK); } void BingoPgBufferCacheBin::_readCache() { /* * If buffer was readed then cache is already fulfiled */ if(_buffer.isReady()) return; /* * Read size and cache itself */ _buffer.readBuffer(_index, _blockId, BINGO_PG_READ); int data_len; const char* data = (const char*) _buffer.getIndexData(data_len); int cache_size; /* * Read size */ memcpy(&cache_size, data, sizeof(int)); _cache.clear(); _cache.resize(cache_size); /* * Read cache data */ memcpy(_cache.ptr(), data + sizeof(int), cache_size); _buffer.changeAccess(BINGO_PG_NOLOCK); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_buffer_cache.h000066400000000000000000000064171271037650300263330ustar00rootroot00000000000000#ifndef _BINGO_PG_BUFFER_CASHE__ #define _BINGO_PG_BUFFER_CASHE__ extern "C" { #include "c.h" #include "storage/itemptr.h" } #ifdef qsort #undef qsort #endif #include "bingo_pg_ext_bitset.h" #include "bingo_pg_buffer.h" #include "bingo_postgres.h" #include "base_cpp/exception.h" /* * Class for data buffers handling */ class BingoPgBufferCache { public: BingoPgBufferCache(int block_id, PG_OBJECT index_ptr, bool write); virtual ~BingoPgBufferCache(){} int getBlockIdx() const {return _blockId;} BingoPgBuffer& getBuffer() {return _buffer;} private: BingoPgBufferCache(const BingoPgBufferCache&); //no implicit copy protected: int _blockId; PG_OBJECT _index; bool _write; BingoPgBuffer _buffer; }; /* * Map buffers handling */ class BingoPgBufferCacheMap: public BingoPgBufferCache { public: /* * Tid mapping = tid + cmf + xyz */ typedef struct BingoMapData { ItemPointerData tid_map; ItemPointerData cmf_map; ItemPointerData xyz_map; } BingoMapData; BingoPgBufferCacheMap(int block_id, PG_OBJECT index_ptr, bool write); virtual ~BingoPgBufferCacheMap(); /* * Setters */ void setTidItem(int map_idx, ItemPointerData& tid_item); void setCmfItem(int map_idx, ItemPointerData& cmf_item); void setXyzItem(int map_idx, ItemPointerData& xyz_item); /* * Getters */ void getTidItem(int map_idx, ItemPointerData& tid_item); void getCmfItem(int map_idx, ItemPointerData& cmf_item); void getXyzItem(int map_idx, ItemPointerData& xyz_item); DECL_ERROR; private: BingoPgBufferCacheMap(const BingoPgBufferCacheMap&); //no implicit copy void _checkMapIdx(int map_idx); indigo::Array _cache; }; /* * Finger buffers handling */ class BingoPgBufferCacheFp : public BingoPgBufferCache { public: BingoPgBufferCacheFp(int block_id, PG_OBJECT index_ptr, bool write); virtual ~BingoPgBufferCacheFp(); void setBit(int str_idx, bool value); bool getBit(int str_idx); /* * Main bit processing */ void andWithBitset(BingoPgExternalBitset& ext_bitset); void getCopy(BingoPgExternalBitset& other); DECL_ERROR; private: BingoPgBufferCacheFp(const BingoPgBufferCacheFp&); //no implicit copy BingoPgExternalBitset _cache; }; /* * Biniary buffers handling */ class BingoPgBufferCacheBin: public BingoPgBufferCache { public: /* * Max size is rewrite BLCKSZ because there is int for keeping data length (stored in the begining of the buffer) */ enum { MAX_SIZE = 8140, BUFFER_SIZE = 8150 }; BingoPgBufferCacheBin(int block_id, PG_OBJECT index_ptr, bool write); virtual ~BingoPgBufferCacheBin(); /* * Returns true if enough space for adding a new structure with given size */ bool isEnoughSpace(int size); /* * Add cmf to the buffer. Returns offset for a added cmf */ unsigned short addBin(indigo::Array& bin_buf); unsigned short writeBin(indigo::Array& bin_buf); /* * Get cmf from a buffer */ void readBin(unsigned short offset, indigo::Array& result); DECL_ERROR; private: BingoPgBufferCacheBin(const BingoPgBufferCacheBin&); //no implicit copy void _writeCache(); void _readCache(); indigo::Array _cache; }; #endif /* BUFFER_CASHE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_common.cpp000066400000000000000000000226731271037650300255640ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "storage/bufmgr.h" #include "access/itup.h" #include "utils/relcache.h" #include "storage/lock.h" #include "access/heapam.h" #include "storage/bufmgr.h" #include "catalog/pg_type.h" #include "executor/spi.h" #include "catalog/namespace.h" #include "utils/lsyscache.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_common.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molecule_fingerprint.h" #include "base_c/bitarray.h" #include "base_cpp/output.h" #include "bingo_pg_ext_bitset.h" #include "bingo_pg_buffer.h" #include "bingo_pg_text.h" #include "bingo_core_c.h" #include "base_cpp/tlscont.h" #include "bingo_pg_config.h" #include extern "C" { BINGO_FUNCTION_EXPORT(_internal_func_check); } using namespace indigo; IMPL_ERROR(BingoPgCommon, "bingo postgres"); void BingoPgCommon::getSearchTypeString(int type, indigo::Array& result, bool molecule) { result.clear(); if (molecule) { switch (type) { case(MOL_SUB): result.readString("SUB", true); break; case(MOL_SIM): result.readString("SIM", true); break; case(MOL_SMARTS): result.readString("SMARTS", true); break; case(MOL_EXACT): result.readString("EXACT", true); break; case(MOL_GROSS): result.readString("GROSS", true); break; default: break; } } else { switch (type) { case(REACT_SUB): result.readString("RSUB", true); break; case(REACT_EXACT): result.readString("REXACT", true); break; case(REACT_SMARTS): result.readString("RSMARTS", true); break; default: break; } } } void BingoPgCommon::printBitset(const char* name, BingoPgExternalBitset& bitset) { elog(NOTICE, "bitset = %s", name); indigo::Array bits; indigo::ArrayOutput ao(bits); for (int x = bitset.begin(); x != bitset.end(); x = bitset.next(x)) { ao.printf("%d ", x); } bits.push(0); elog(NOTICE, "%s", bits.ptr()); } void BingoPgCommon::printFPBitset(const char* name, unsigned char* bitset, int size) { elog(NOTICE, "bitset = %s", name); indigo::Array bits; indigo::ArrayOutput ao(bits); for (int fp_idx = 0; fp_idx < size; fp_idx++) { if (bitGetBit(bitset, fp_idx)) { ao.printf("%d ", fp_idx); } } bits.push(0); elog(NOTICE, "%s", bits.ptr()); } void BingoPgCommon::setDefaultOptions() { bingoSetConfigInt("treat-x-as-pseudoatom", 0); bingoSetConfigInt("ignore-closing-bond-direction-mismatch", 0); bingoSetConfigInt("FP_ORD_SIZE", 25); bingoSetConfigInt("FP_ANY_SIZE", 15); bingoSetConfigInt("FP_TAU_SIZE", 10); bingoSetConfigInt("FP_SIM_SIZE", 8); bingoSetConfigInt("SUB_SCREENING_MAX_BITS", 8); bingoSetConfigInt("SIM_SCREENING_PASS_MARK", 128); bingoAddTautomerRule(1, "N,O,P,S,As,Se,Sb,Te", "N,O,P,S,As,Se,Sb,Te"); bingoAddTautomerRule(2, "0C", "N,O,P,S"); bingoAddTautomerRule(3, "1C", "N,O"); } static int rnd_check = rand(); int BingoPgCommon::executeQuery(indigo::Array& query_str) { int result = 0; BINGO_PG_TRY { SPI_connect(); int success = SPI_exec(query_str.ptr(), 1); result = SPI_processed; SPI_finish(); if (success < 0) { elog(ERROR, "error (%d) while executing query: %s res", success, query_str.ptr()); } } BINGO_PG_HANDLE(throw BingoPgError("internal error: can not execute query: %s", message)); return result; } int BingoPgCommon::executeQuery(const char *format, ...) { QS_DEF(Array, buf); ArrayOutput output(buf); va_list args; va_start(args, format); output.vprintf(format, args); va_end(args); output.writeChar(0); return executeQuery(buf); } bool BingoPgCommon::tableExists(const char* schema_name, const char* table_name) { return (executeQuery("select * from information_schema.tables where " "table_catalog = CURRENT_CATALOG and table_schema = '%s' " "and table_name = '%s'", schema_name, table_name) > 0); } Datum _internal_func_check(PG_FUNCTION_ARGS) { int check_value = PG_GETARG_INT32(0); bool result = (check_value == rnd_check); rnd_check = rand(); PG_RETURN_BOOL(result); } void BingoPgCommon::createDependency(const char* schema_name, const char* index_schema, const char* child_table, const char* parent_table) { rnd_check = rand(); executeQuery("SELECT %s._internal_func_011(%d, '%s.%s', '%s.%s')", schema_name, rnd_check, index_schema, child_table, index_schema, parent_table); } void BingoPgCommon::dropDependency(const char* schema_name, const char* index_schema, const char* table_name) { rnd_check = rand(); executeQuery("SELECT %s._internal_func_012(%d, '%s.%s')", schema_name, rnd_check, index_schema, table_name); } void BingoPgCommon::appendPath(const char* schema_name) { executeQuery("SELECT set_config('search_path', current_setting('search_path') ||',%s'::text , true)", schema_name); } char* BingoPgCommon::releaseString(const char* str) { if (str == 0) return 0; int size = strlen(str) + 1; char* result = (char*) palloc(size); memcpy(result, str, size); return result; } BingoPgCommon::BingoSessionHandler::BingoSessionHandler(Oid func_id) { char* schema_name = 0; BINGO_PG_TRY { schema_name = get_namespace_name(get_func_namespace(func_id)); pfree(schema_name); } BINGO_PG_HANDLE(throw Error("internal error while trying get namespace name: %s", message)); BingoPgConfig bingo_config; bingo_config.readDefaultConfig(schema_name); _sessionId = bingoAllocateSessionID(); refresh(); bingo_config.setUpBingoConfiguration(); bingoTautomerRulesReady(0,0,0); } BingoPgCommon::BingoSessionHandler::~BingoSessionHandler() { bingoReleaseSessionID(_sessionId); } void BingoPgCommon::BingoSessionHandler::refresh() { bingoSetSessionID(_sessionId); bingoSetContext(0); // bingoSetErrorHandler(bingoErrorHandler, this); } //void BingoPgCommon::BingoSessionHandler::bingoErrorHandler(const char* message, void* self_ptr) { // BingoSessionHandler* self = (BingoSessionHandler*)self_ptr; // // const char* func = self->getFunctionName(); // // if(self->raise_error) { // if (func) // throw BingoPgError("runtime error in bingo.'%s': %s", func, message); // else // throw BingoPgError("runtime error: %s", message); // } else { // self->error_raised = true; // if (func) // elog(WARNING, "warning in bingo.'%s': %s", func, message); // else // elog(WARNING, "warning: %s", message); // } // // //} //dword BingoPgCommon::getFunctionOid(const char* name, indigo::Array& types) { // indigo::Array fname; // fname.readString(name, true); // Value* func_name = makeString(fname.ptr()); // // List* func_list = list_make1(func_name); // Oid func_oid = LookupFuncName(func_list, types.size(), types.ptr(), false); // // if(func_oid == InvalidOid) // throw Error( "can not find the function %s", name); // // list_free(func_list); // return func_oid; //} //void BingoPgCommon::executeQuery(const char* query_str) { // int result = SPI_exec(query_str, 1); // if(result < 0) // throw Error( "error while executing query: %s", query_str); //// Oid func_oid = getFunctionOid1("bingo_execute_func", TEXTOID); //// //// BingoPgText text_query; //// text_query.initFromString(query_str); //// //// OidFunctionCall1(func_oid, text_query.getDatum()); //} //dword BingoPgCommon::getFunctionOid1(const char* name, dword type1) { // QS_DEF(indigo::Array, types); // types.clear(); // types.push(type1); // return getFunctionOid(name, types); //} // //dword BingoPgCommon::callFunction(dword functionId, indigo::Array& args) { // FmgrInfo flinfo; // FunctionCallInfoData fcinfo; // // int args_len = args.size(); // // fmgr_info(functionId, &flinfo); // InitFunctionCallInfoData(fcinfo, &flinfo, args_len, NULL, NULL); // // for (int arg_idx = 0; arg_idx < args_len; ++arg_idx) { // fcinfo.arg[arg_idx] = args[arg_idx]; // fcinfo.argnull[arg_idx] = false; // } // // Datum result = FunctionCallInvoke(&fcinfo); // // /* Do not Check for */ //// if (fcinfo.isnull) //// throw Error( "function %u returned NULL", flinfo.fn_oid); // // return result; //} //dword BingoPgCommon::callFunction1(dword oid, dword arg1) { // QS_DEF(indigo::Array, args); // args.clear(); // args.push(arg1); // return callFunction(oid, args); //} BingoPgWrapper::BingoPgWrapper():_ptr(0) { } BingoPgWrapper::~BingoPgWrapper() { clear(); } void BingoPgWrapper::clear() { if(_ptr != 0) pfree(_ptr); _ptr = 0; } const char* BingoPgWrapper::getFuncNameSpace(dword oid_func) { clear(); _ptr = get_namespace_name(get_func_namespace(oid_func)); return (const char*)_ptr; } const char* BingoPgWrapper::getRelNameSpace(dword oid_rel) { clear(); _ptr = get_namespace_name(get_rel_namespace(oid_rel)); return (const char*)_ptr; } const char* BingoPgWrapper::getFuncName(dword oid_func) { clear(); _ptr = get_func_name(oid_func); return (const char*)_ptr; } const char* BingoPgWrapper::getRelName(dword oid_rel) { clear(); _ptr = get_rel_name(oid_rel); return (const char*)_ptr; }Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_common.h000066400000000000000000000345061271037650300252270ustar00rootroot00000000000000#ifndef _BINGO_PG_COMMON_H__ #define _BINGO_PG_COMMON_H__ #ifdef min #undef min #endif #ifdef max #undef max #endif #include #include "bingo_postgres.h" #include "base_cpp/array.h" #include "base_c/bitarray.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "base_cpp/tlscont.h" namespace indigo { struct MoleculeFingerprintParameters; } class BingoPgExternalBitset; class BingoPgBuffer; class BingoPgCommon { public: enum { MOL_SUB = 1, MOL_EXACT = 2, MOL_SMARTS = 3, MOL_GROSS = 4, MOL_MASS_LESS = 5, MOL_MASS_GREAT = 6, MOL_SIM = 7, REACT_SUB = 1, REACT_EXACT = 2, REACT_SMARTS = 3, MOL_MASS = 100/*pseudo types*/ }; static void getSearchTypeString(int, indigo::Array& result, bool molecule); // static float getBingoSim(char*, int, char*, int); // static void formIndexTuple(BingoPgBuffer& buffer, void* map_data, int size); // static void* getIndexData(BingoPgBuffer& pg_buffer, int& data_len); static void printBitset(const char* name, BingoPgExternalBitset& bitset); static void printFPBitset(const char* name, unsigned char* bitset, int size); // static char* getTextData(PG_OBJECT text_datum, int& size); static void setDefaultOptions(); // static dword getFunctionOid(const char* name, indigo::Array& types); // static dword getFunctionOid1(const char* name, dword type1); // static dword callFunction(dword oid, indigo::Array& args); // static dword callFunction1(dword oid, dword arg1); // static void executeQuery(const char* query_str); static int executeQuery(indigo::Array& query_str); static int executeQuery(const char *format, ...); static bool tableExists(const char* schema_name,const char* table_name); static void createDependency(const char* schema_name,const char* index_schema, const char* child_table, const char* parent_table); static void dropDependency(const char* schema_name, const char* index_schema, const char* table_name); static void appendPath(const char* schema_name); static char* releaseString(const char* str); /* * This class allows to avoid save-load sequence errors * You can save or load structures by calling one method * Numbers, arrays, double arrays etc are can be handled by this helper */ class DataProcessing { public: template static void handleNumber(T& number, indigo::Scanner* scanner, indigo::Output* output) { _handleNumber(number, scanner, output); } template static void handleArray(indigo::Array& data, indigo::Scanner* scanner, indigo::Output* output) { int size = data.size(); handleNumber(size, scanner, output); if (scanner) data.resize(size); for (int i = 0; i < size; ++i) { handleNumber(data[i], scanner, output); } } template static void handleDArray(indigo::ObjArray< indigo::Array >& data, indigo::Scanner* scanner, indigo::Output* output) { int size = data.size(); handleNumber(size, scanner, output); if (scanner) data.resize(size); for (int i = 0; i < size; ++i) { handleArray(data[i], scanner, output); } } template static void handleRedBlackString(indigo::RedBlackStringMap& data, indigo::Scanner* scanner, indigo::Output* output) { indigo::Array key_tmp; int size = data.size(); handleNumber(size, scanner, output); if (scanner) { data.clear(); for (int i = 0; i < size; ++i) { handleArray(key_tmp, scanner, output); T data_value; handleNumber(data_value, scanner, output); data.insert(key_tmp.ptr(), data_value); } } if (output) { for (int i = data.begin(); i != data.end(); i = data.next(i)) { key_tmp.readString(data.key(i), true); handleArray(key_tmp, scanner, output); handleNumber(data.value(i), scanner, output); } } } template static void handleRedBlackArr(indigo::RedBlackObjMap >& data, indigo::Scanner* scanner, indigo::Output* output) { int size = data.size(); handleNumber(size, scanner, output); if (scanner) { T key; data.clear(); for (int i = 0; i < size; ++i) { handleNumber(key, scanner, output); handleArray(data.insert(key), scanner, output); } } if (output) { for (int i = data.begin(); i != data.end(); i = data.next(i)) { handleNumber(data.key(i), scanner, output); handleArray(data.value(i), scanner, output); } } } template static void handleRedBlackStringArr(indigo::RedBlackStringObjMap< indigo::Array >& data, indigo::Scanner* scanner, indigo::Output* output) { indigo::Array key_tmp; int size = data.size(); handleNumber(size, scanner, output); if (scanner) { data.clear(); for (int i = 0; i < size; ++i) { handleArray(key_tmp, scanner, output); int key_idx = data.insert(key_tmp.ptr()); handleArray(data.value(key_idx), scanner, output); } } if (output) { for (int i = data.begin(); i != data.end(); i = data.next(i)) { key_tmp.readString(data.key(i), true); handleArray(key_tmp, scanner, output); handleArray(data.value(i), scanner, output); } } } template static void handleRedBlackObject(indigo::RedBlackObjMap& data, indigo::Scanner* scanner, indigo::Output* output) { int size = data.size(); handleNumber(size, scanner, output); if (scanner) { T key; data.clear(); for (int i = 0; i < size; ++i) { handleNumber(key, scanner, output); data.insert(key).serialize(scanner, output); } } if (output) { for (int i = data.begin(); i != data.end(); i = data.next(i)) { handleNumber(data.key(i), scanner, output); data.value(i).serialize(scanner, output); } } } private: DataProcessing(); DataProcessing(const DataProcessing&); static void _handleNumber(bool& number, indigo::Scanner* scanner, indigo::Output* output) { char b; if (scanner) { b = scanner->readChar(); number = (b == 1); } if (output) { b = number ? 1 : 0; output->writeChar(b); } } static void _handleNumber(char& number, indigo::Scanner* scanner, indigo::Output* output) { if (scanner) { number = scanner->readChar(); } if (output) { output->writeChar(number); } } static void _handleNumber(byte& number, indigo::Scanner* scanner, indigo::Output* output) { if (scanner) { number = scanner->readByte(); } if (output) { output->writeByte(number); } } static void _handleNumber(int& number, indigo::Scanner* scanner, indigo::Output* output) { if (scanner) { number = scanner->readBinaryInt(); } if (output) { output->writeBinaryInt(number); } } static void _handleNumber(float& number, indigo::Scanner* scanner, indigo::Output* output) { if (scanner) { number = scanner->readBinaryFloat(); } if (output) { output->writeBinaryFloat(number); } } static void _handleNumber(double& number, indigo::Scanner* scanner, indigo::Output* output) { /* * Double workaround with string saving */ indigo::Array d_string; d_string.clear(); if (scanner) { handleArray(d_string, scanner, output); number = atof(d_string.ptr()); } if (output) { indigo::bprintf(d_string, "%.16e", number); handleArray(d_string, scanner, output); } } }; static void convertTo(const indigo::Array& value_str, float& val) { indigo::BufferScanner scanner(value_str); if (!scanner.tryReadFloat(val)) throw Error("can not read float value in string %s\n", value_str.ptr()); } static void convertTo(const indigo::Array& value_str, bool& val) { if (strcasecmp("true", value_str.ptr()) == 0) { val = true; } else if (strcasecmp("false", value_str.ptr()) == 0) { val = false; } else { throw Error("unknown value '%s' expected boolean 'true' or 'false' ", value_str.ptr()); } } static void convertTo(const indigo::Array& value_str, int& val) { indigo::BufferScanner scanner(value_str); val = scanner.readInt(); } static void convertTo(const indigo::Array& value_str, indigo::Array& val) { val.copy(value_str); } class BingoSessionHandler { public: BingoSessionHandler(unsigned int func_id); virtual ~BingoSessionHandler(); // static void bingoErrorHandler(const char *message, void *context); const char* getFunctionName() const { return _functionName.size() ? _functionName.ptr() : 0; } void setFunctionName(const char* name) { _functionName.readString(name, true); } void refresh(); private: BingoSessionHandler(const BingoSessionHandler&); //no implicit copy qword _sessionId; indigo::Array _functionName; }; DECL_ERROR; private: BingoPgCommon(); BingoPgCommon(const BingoPgCommon&); //no implicit copy }; /* * Class for handling PG objects */ class BingoPgWrapper { public: BingoPgWrapper(); ~BingoPgWrapper(); void clear(); const char* getFuncNameSpace(dword oid_func); const char* getRelNameSpace(dword oid_rel); const char* getFuncName(dword oid_func); const char* getRelName(dword oid_rel); private: BingoPgWrapper(const BingoPgWrapper&); //no implicit copy PG_OBJECT _ptr; }; //class CancelException { //public: // explicit CancelException(){} // virtual ~CancelException(){} // CancelException(const CancelException&){} //private: //}; #define BINGO_PG_TRY {\ bool pg_error_raised = false; \ QS_DEF(Array, pg_message); \ PG_TRY(); #define BINGO_PG_HANDLE(handle_statement) \ PG_CATCH(); { \ ErrorData *err = CopyErrorData(); \ pg_message.readString(err->message, true); \ FreeErrorData(err); \ FlushErrorState(); \ pg_error_raised = true; \ } PG_END_TRY(); \ if(pg_error_raised) { \ const char* message = pg_message.ptr(); \ handle_statement;\ }} #define PG_BINGO_BEGIN { \ int pg_err_mess = 0; \ bool pg_raise_error = false; \ try #define PG_BINGO_END \ catch (indigo::Exception& e) { \ pg_raise_error = true; \ errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN); \ pg_err_mess = errmsg("error: %s", e.message()); \ } catch (...) { \ pg_raise_error = true; \ errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN); \ pg_err_mess = errmsg("bingo unknown error"); \ } \ if (pg_raise_error) { \ errfinish((errcode(ERRCODE_INTERNAL_ERROR), pg_err_mess)); \ }} #define PG_BINGO_HANDLE(statement) \ catch (indigo::Exception& e) { \ pg_raise_error = true; \ errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN); \ pg_err_mess = errmsg("error: %s", e.message()); \ } catch (...) { \ pg_raise_error = true; \ errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN); \ pg_err_mess = errmsg("bingo unknown error"); \ } \ if (pg_raise_error) { \ statement; \ errfinish((errcode(ERRCODE_INTERNAL_ERROR), pg_err_mess)); \ }} class DLLEXPORT BingoPgError : public indigo::Exception { public: explicit BingoPgError (const char *format, ...) { va_list args; va_start(args, format); _init("bingo", format, args); va_end(args); } }; #define CORE_HANDLE_ERROR(res, success_res, suffix, message)\ if (res < success_res) {\ throw BingoPgError("%s: %s", suffix, message);\ } #define CORE_HANDLE_ERROR_TID_NO_INDEX(res, success_res, suffix, block, offset, message)\ if (res < success_res) {\ throw BingoPgError("%s with ctid='(%d,%d)'::tid: %s", suffix, block, offset, message);\ } #define CORE_HANDLE_ERROR_TID(res, success_res, suffix, section_idx, structure_idx, message)\ if (res < success_res) {\ ItemPointerData target_item;\ _bufferIndexPtr->readTidItem(section_idx, structure_idx, &target_item);\ int block_number = ItemPointerGetBlockNumber(&target_item);\ int offset_number = ItemPointerGetOffsetNumber(&target_item);\ throw BingoPgError("%s with ctid='(%d,%d)'::tid: %s", suffix, block_number, offset_number, message);\ } #define CORE_HANDLE_WARNING(res, success_res, suffix, message)\ if (res < success_res) {\ elog(WARNING, "%s: %s", suffix, message);\ } #define CORE_HANDLE_WARNING_TID_NO_INDEX(res, success_res, suffix, block, offset, message)\ if (res < success_res) {\ elog(WARNING, "%s with ctid='(%d,%d)'::tid: %s", suffix, block, offset, message);\ } #define CORE_RETURN_WARNING(res, success_res, suffix, message)\ if (res < success_res) {\ elog(WARNING, "%s: %s", suffix, message);\ return false;\ } #define CORE_RETURN_WARNING_TID(res, success_res, suffix, section_idx, structure_idx, message)\ if (res < success_res) {\ ItemPointerData target_item;\ _bufferIndexPtr->readTidItem(section_idx, structure_idx, &target_item);\ int block_number = ItemPointerGetBlockNumber(&target_item);\ int offset_number = ItemPointerGetOffsetNumber(&target_item);\ elog(WARNING, "%s with ctid='(%d,%d)'::tid: %s", suffix, block_number, offset_number, message);\ return false;\ } #endif /* BINGO_PG_COMMON_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_cursor.cpp000066400000000000000000000123461271037650300256050ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "catalog/pg_type.h" #include "storage/itemptr.h" #include "executor/spi.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_cursor.h" #include "bingo_pg_common.h" #include "pg_bingo_context.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "bingo_pg_text.h" using namespace indigo; static int cursor_idx = 0; IMPL_ERROR(BingoPgCursor, "bingo cursor access"); BingoPgCursor::BingoPgCursor(const char *format, ...) { Array buf; va_list args; va_start(args, format); ArrayOutput output(buf); output.vprintf(format, args); output.writeChar(0); va_end(args); _init(buf); } BingoPgCursor::BingoPgCursor(indigo::Array& query_str) { _init(query_str); } BingoPgCursor::~BingoPgCursor() { /* * JDBC drivers workaround */ BINGO_PG_TRY { Portal cur_ptr = SPI_cursor_find(_cursorName.ptr()); if (cur_ptr != NULL) SPI_cursor_close((Portal) _cursorPtr); SPI_finish(); SPI_pop_conditional(_pushed); } BINGO_PG_HANDLE(throw Error("internal error: can not close the cursor: %s", message)); } bool BingoPgCursor::next() { BINGO_PG_TRY { SPI_cursor_fetch((Portal)_cursorPtr, true, 1); } BINGO_PG_HANDLE(throw Error("internal error: can not fetch the cursor: %s", message)); if(SPI_processed == 0) return false; if(SPI_tuptable == NULL) return false; return true; } void BingoPgCursor::getId(int arg_idx, ItemPointerData& data) { BINGO_PG_TRY { Datum record = getDatum(arg_idx); ItemPointer tup = (ItemPointer) DatumGetPointer(record); int block_num = ItemPointerGetBlockNumber(tup); int off_num = ItemPointerGetOffsetNumber(tup); ItemPointerSetBlockNumber(&data, block_num); ItemPointerSetOffsetNumber(&data, off_num); } BINGO_PG_HANDLE(throw Error("internal error: can not get the id from the data: %s", message)); } void BingoPgCursor::getText(int arg_idx, BingoPgText& data) { if(SPI_processed == 0) throw Error("internal error: can not get not processed tuple"); if(SPI_tuptable == NULL) throw Error("internal error: can not get null tuple"); Datum record = 0; BINGO_PG_TRY { TupleDesc tupdesc = SPI_tuptable->tupdesc; /* * Tuple index is always 0 */ int tuple_idx = 0; HeapTuple tuple = SPI_tuptable->vals[tuple_idx]; if (arg_idx > tupdesc->natts) elog(ERROR, "internal error: can not get tuple was not in query %d > %d", arg_idx, tupdesc->natts); char *result = SPI_getvalue(tuple, tupdesc, arg_idx); if (result == NULL) { if (SPI_result == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "internal error: colnumber is out of range (SPI_getvalue)"); else if (SPI_result == SPI_ERROR_NOOUTFUNC) elog(ERROR, "internal error: no output function is available (SPI_getvalue)"); else data.initFromString("\0"); } else { data.initFromString(result); pfree(result); } } BINGO_PG_HANDLE(throw Error("internal error: can not get datum from the tuple: %s", message)); } uintptr_t BingoPgCursor::getDatum(int arg_idx) { if(SPI_processed == 0) throw Error("internal error: can not get not processed tuple"); if(SPI_tuptable == NULL) throw Error("internal error: can not get null tuple"); Datum record = 0; BINGO_PG_TRY { TupleDesc tupdesc = SPI_tuptable->tupdesc; /* * Tuple index is always 0 */ int tuple_idx = 0; HeapTuple tuple = SPI_tuptable->vals[tuple_idx]; bool isnull; if (arg_idx > tupdesc->natts) elog(ERROR, "internal error: can not get tuple was not in query %d > %d", arg_idx, tupdesc->natts); record = SPI_getbinval(tuple, tupdesc, arg_idx, &isnull); if (isnull) elog(ERROR, "internal error: can not get null tuple"); } BINGO_PG_HANDLE(throw Error("internal error: can not get datum from the tuple: %s", message)); return record; } unsigned int BingoPgCursor::getArgOid(int arg_idx) { if(SPI_tuptable == NULL) throw Error("internal error: can not get null tuple"); Oid result = 0; BINGO_PG_TRY { TupleDesc tupdesc = SPI_tuptable->tupdesc; if(arg_idx >= tupdesc->natts) elog(ERROR, "internal error: can not get argument %d natts = %d", arg_idx, tupdesc->natts); result = tupdesc->attrs[arg_idx]->atttypid; } BINGO_PG_HANDLE(throw Error("internal error: can not get datum from the tuple: %s", message)); return result; } void BingoPgCursor::_init(indigo::Array& query_str) { Array arg_types; BINGO_PG_TRY { _pushed = SPI_push_conditional(); SPI_connect(); SPIPlanPtr plan_ptr = SPI_prepare_cursor(query_str.ptr(), arg_types.size(), arg_types.ptr(), 0); ++cursor_idx; ArrayOutput cursor_name_out(_cursorName); cursor_name_out.printf("bingo_cursor_%d", cursor_idx); cursor_name_out.writeChar(0); _cursorPtr = SPI_cursor_open(_cursorName.ptr(), plan_ptr, 0, 0, true); } BINGO_PG_HANDLE(throw Error("internal error: can not prepare or open a cursor: %s", message)); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_cursor.h000066400000000000000000000014141271037650300252440ustar00rootroot00000000000000#ifndef _BINGO_PG_CURSOR_H__ #define _BINGO_PG_CURSOR_H__ #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "bingo_postgres.h" struct ItemPointerData; class BingoPgText; class BingoPgCursor { public: BingoPgCursor(const char *format, ...); BingoPgCursor(indigo::Array& query_str); virtual ~BingoPgCursor(); bool next(); void getId(int arg_idx, ItemPointerData&); void getText(int arg_idx, BingoPgText&); uintptr_t getDatum(int arg_idx); unsigned int getArgOid(int arg_idx); DECL_ERROR; private: BingoPgCursor(const BingoPgCursor&); //no implicit copy void _init(indigo::Array& query_str); indigo::Array _cursorName; PG_OBJECT _cursorPtr; bool _pushed; }; #endif /* BINGO_PG_CURSOR_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_ext_bitset.cpp000066400000000000000000000332021271037650300264340ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" #include "bingo_pg_fix_post.h" #include "bingo_pg_ext_bitset.h" #include "base_c/bitarray.h" #include "base_cpp/obj_array.h" BingoPgExternalBitset::BingoPgExternalBitset() { _initWords(BITS_PER_WORD); } BingoPgExternalBitset::BingoPgExternalBitset(int nbits) { _initWords(nbits); } void* BingoPgExternalBitset::serialize(int& size) { size = (_length + 2) * sizeof(qword); _serializeWords.resize(_length + 2); _serializeWords[0] = _bitsNumber; _serializeWords[1] = (*_lastWordPtr); memcpy(&_serializeWords[2], _words, _length * sizeof(qword)); return _serializeWords.ptr(); } void BingoPgExternalBitset::deserialize(void* data_ptr, int data_len, bool ext) { qword* data = (qword*)data_ptr; _bitsNumber = data[0]; _lastWordPtr = &(data[1]); _length = _wordIndex(_bitsNumber - 1) + 1; qword* words_ptr = &(data[2]); if(ext) { _words = words_ptr; } else { _internalWords.resize(_length); _internalWords.copy(words_ptr, _length); _words = _internalWords.ptr(); _lastWord = data[1]; _lastWordPtr = &_lastWord; } } void BingoPgExternalBitset::_recalculateWordsInUse() { // Traverse the bitset until a used word is found int i = _length - 1; for (; i >= 0; --i) if (_words[i] != 0) break; (*_lastWordPtr) = i + 1; // The new logical size } void BingoPgExternalBitset::_initWords(int nbits) { _lastWordPtr = &_lastWord; (*_lastWordPtr) = 0; _length = _wordIndex(nbits - 1) + 1; _internalWords.clear_resize(_length); _internalWords.zerofill(); _bitsNumber = nbits; _words = _internalWords.ptr(); } void BingoPgExternalBitset::_expandTo(int wordIndex) { int wordsRequired = wordIndex + 1; if ((*_lastWordPtr) < wordsRequired) { (*_lastWordPtr) = wordsRequired; } } int BingoPgExternalBitset::_bitCount(qword b) const { b = (b & 0x5555555555555555LL) + ((b >> 1) & 0x5555555555555555LL); b = (b & 0x3333333333333333LL) + ((b >> 2) & 0x3333333333333333LL); b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FLL; b = b + (b >> 8); b = b + (b >> 16); b = (b + (b >> 32)) & 0x0000007F; return (int) b; } int BingoPgExternalBitset::_leastSignificantBitPosition(qword n) const { if (n == 0) return -1; int pos = 63; if (n & 0x00000000FFFFFFFFLL) { pos -= 32; } else { n >>= 32; } if (n & 0x000000000000FFFFLL) { pos -= 16; } else { n >>= 16; } if (n & 0x00000000000000FFLL) { pos -= 8; } else { n >>= 8; } if (n & 0x000000000000000FLL) { pos -= 4; } else { n >>= 4; } if (n & 0x0000000000000003LL) { pos -= 2; } else { n >>= 2; } if (n & 0x0000000000000001LL) { pos -= 1; } return pos; } //public------------------------------------------------------------------------------------------------------ void BingoPgExternalBitset::copy(const BingoPgExternalBitset& set) { if (_length != set._length) { // _length = set._length; // _words.resize(_length); // throw Error("different length"); return; } _bitsNumber = set._bitsNumber; (*_lastWordPtr) = (*set._lastWordPtr); memcpy(_words, set._words, _length * sizeof(qword)); } void BingoPgExternalBitset::copySubset(const BingoPgExternalBitset &set) { if (_bitsNumber == set._bitsNumber) copy(set); if (_bitsNumber < set._bitsNumber) return; (*_lastWordPtr) = __max((*_lastWordPtr), (*set._lastWordPtr)); for (int i = 0; i < set._length; ++i) { _words[i] = set._words[i]; } } void BingoPgExternalBitset::flip() { flip(0, _bitsNumber); } void BingoPgExternalBitset::flip(int bitIndex) { int wordindex = _wordIndex(bitIndex); bitIndex -= (wordindex << ADDRESS_BITS_PER_WORD); _expandTo(wordindex); _words[wordindex] ^= ((qword) 1 << bitIndex); _recalculateWordsInUse(); } void BingoPgExternalBitset::flip(int fromIndex, int toIndex) { if (fromIndex == toIndex) return; int start_word_index = _wordIndex(fromIndex); int end_word_index = _wordIndex(toIndex - 1); _expandTo(end_word_index); fromIndex -= (start_word_index << ADDRESS_BITS_PER_WORD); toIndex -= (end_word_index << ADDRESS_BITS_PER_WORD); qword first_word_mask = WORD_MASK << fromIndex; qword last_word_mask = ((qword) 1 << toIndex) - 1; if (start_word_index == end_word_index) { _words[start_word_index] ^= (first_word_mask & last_word_mask); } else { _words[start_word_index] ^= first_word_mask; for (int i = start_word_index + 1; i < end_word_index; ++i) _words[i] ^= WORD_MASK; _words[end_word_index] ^= last_word_mask; } _recalculateWordsInUse(); } void BingoPgExternalBitset::set(int bitIndex) { int wordindex = _wordIndex(bitIndex); bitIndex -= (wordindex << ADDRESS_BITS_PER_WORD); _expandTo(wordindex); _words[wordindex] |= ((qword) 1 << bitIndex); // Restores invariants } void BingoPgExternalBitset::set(int fromIndex, int toIndex) { if (fromIndex == toIndex) return; int start_word_index = _wordIndex(fromIndex); int end_word_index = _wordIndex(toIndex - 1); fromIndex -= (start_word_index << ADDRESS_BITS_PER_WORD); toIndex -= (end_word_index << ADDRESS_BITS_PER_WORD); qword first_word_mask = WORD_MASK << fromIndex; qword last_word_mask = shiftOne(toIndex) - 1; if (start_word_index == end_word_index) { _words[start_word_index] |= (first_word_mask & last_word_mask); } else { _words[start_word_index] |= first_word_mask; for (int i = start_word_index + 1; i < end_word_index; ++i) _words[i] |= WORD_MASK; _words[end_word_index] |= last_word_mask; } _recalculateWordsInUse(); } void BingoPgExternalBitset::set() { set(0, _bitsNumber); } void BingoPgExternalBitset::set(int bitIndex, bool value) { if (value) set(bitIndex); else reset(bitIndex); } void BingoPgExternalBitset::reset(int bitIndex) { int wordindex = _wordIndex(bitIndex); bitIndex -= (wordindex << ADDRESS_BITS_PER_WORD); if (wordindex >= (*_lastWordPtr)) return; _words[wordindex] &= ~((qword) 1 << bitIndex); _recalculateWordsInUse(); } void BingoPgExternalBitset::clear() { while ((*_lastWordPtr) > 0) _words[--(*_lastWordPtr)] = 0; } bool BingoPgExternalBitset::get(int bitIndex) const { int word_index = _wordIndex(bitIndex); bitIndex -= (word_index << ADDRESS_BITS_PER_WORD); return (word_index < (*_lastWordPtr)) && ((_words[word_index] & ((qword) 1 << bitIndex)) != 0); } int BingoPgExternalBitset::nextSetBit(int fromIndex) const { int u = _wordIndex(fromIndex); if (u >= (*_lastWordPtr)) return -1; fromIndex -= (u << ADDRESS_BITS_PER_WORD); qword word = _words[u] & (WORD_MASK << fromIndex); for (; word == 0; word = _words[u]) { if (++u >= (*_lastWordPtr)) return -1; } return _leastSignificantBitPosition(word) + (u << ADDRESS_BITS_PER_WORD); } bool BingoPgExternalBitset::intersects(const BingoPgExternalBitset& set) const { for (int i = __min((*_lastWordPtr), (*set._lastWordPtr)) - 1; i >= 0; --i) if ((_words[i] & set._words[i]) != 0) return true; return false; } void BingoPgExternalBitset::andWith(const BingoPgExternalBitset& set) { while ((*_lastWordPtr) > (*set._lastWordPtr)) _words[--(*_lastWordPtr)] = 0; // Perform logical AND on words in common for (int i = 0; i < (*_lastWordPtr); ++i) _words[i] &= set._words[i]; _recalculateWordsInUse(); } void BingoPgExternalBitset::orWith(const BingoPgExternalBitset& set) { if ((*_lastWordPtr) < (*set._lastWordPtr)) (*_lastWordPtr) = (*set._lastWordPtr); // Perform logical OR on words in common for (int i = 0; i < (*_lastWordPtr); ++i) { _words[i] |= set._words[i]; } } void BingoPgExternalBitset::xorWith(const BingoPgExternalBitset& set) { if ((*_lastWordPtr) < (*set._lastWordPtr)) { (*_lastWordPtr) = (*set._lastWordPtr); } // Perform logical XOR on words in common for (int i = 0; i < (*_lastWordPtr); ++i) _words[i] ^= set._words[i]; _recalculateWordsInUse(); } void BingoPgExternalBitset::andNotWith(const BingoPgExternalBitset& set) { for (int i = __min((*_lastWordPtr), (*set._lastWordPtr)) - 1; i >= 0; --i) _words[i] &= ~set._words[i]; _recalculateWordsInUse(); } bool BingoPgExternalBitset::equals(const BingoPgExternalBitset& set) const { if ((*_lastWordPtr) != (*set._lastWordPtr)) return false; // Check words in use by both BitSets for (int i = 0; i < (*_lastWordPtr); ++i) if (_words[i] != set._words[i]) return false; return true; } //void ExternalBitset::resize(int size) { // int new_length = _wordIndex(size - 1) + 1; // _words.resize(new_length); // if (new_length > _length) // for (int i = _length; i < new_length; ++i) // _words[i] = 0; // _bitsNumber = size; // _length = new_length; //} bool BingoPgExternalBitset::isSubsetOf(const BingoPgExternalBitset& set) const { for (int i = 0; i < (*_lastWordPtr); ++i) if (_words[i] & ~set._words[i]) return false; return true; } bool BingoPgExternalBitset::isProperSubsetOf(const BingoPgExternalBitset &set) const { bool proper = false; for (int i = 0; i < (*_lastWordPtr); ++i) { if (set._words[i] & ~_words[i]) proper = true; if (_words[i] & ~set._words[i]) return false; } return proper; } void BingoPgExternalBitset::zeroFill() { memset(_words, 0, _length * sizeof(qword)); _recalculateWordsInUse(); } void BingoPgExternalBitset::bsOrBs(const BingoPgExternalBitset& set1, const BingoPgExternalBitset& set2) { int max_words = __max((*set1._lastWordPtr), (*set2._lastWordPtr)); for (int i = 0; i < max_words; ++i) { _words[i] = set1._words[i] | set2._words[i]; } for (int i = max_words; i < (*_lastWordPtr); ++i) { _words[i] = 0; } (*_lastWordPtr) = max_words; } void BingoPgExternalBitset::bsAndNotBs(const BingoPgExternalBitset& set1, const BingoPgExternalBitset& set2) { for (int i = 0; i < (*set1._lastWordPtr); ++i) { _words[i] = set1._words[i] & ~set2._words[i]; } for (int i = (*set1._lastWordPtr); i < (*_lastWordPtr); ++i) { _words[i] = 0; } _recalculateWordsInUse(); } void BingoPgExternalBitset::bsAndBs(const BingoPgExternalBitset& set1, const BingoPgExternalBitset& set2) { for (int i = 0; i < (*set1._lastWordPtr); ++i) { _words[i] = set1._words[i] & set2._words[i]; } for (int i = (*set1._lastWordPtr); i < (*_lastWordPtr); ++i) { _words[i] = 0; } _recalculateWordsInUse(); } int BingoPgExternalBitset::bitsNumber() const { int bits_num = 0; // for (int i = 0; i < (*_lastWordPtr); ++i) // bits_num += _bitCount(_words[i]); int b_count = 0; byte q_size = sizeof(qword), byte_idx; byte* cur_word; for (int w_idx = 0; w_idx < (*_lastWordPtr); ++w_idx) { cur_word = (byte*)(_words + w_idx); b_count = 0; for (byte_idx = 0; byte_idx < q_size; ++byte_idx) { b_count += bitGetOnesCountByte(cur_word[byte_idx]); } bits_num += b_count; } return bits_num; } bool BingoPgExternalBitset::hasBits() const { return (*_lastWordPtr) != 0; } // some 64-bit compilators can not correctly work with big values shift. So it must be processed manually qword BingoPgExternalBitset::shiftOne(int shiftNumber) { qword result = (qword) 1; while (shiftNumber > MAX_SHIFT_NUMBER) { result = result << MAX_SHIFT_NUMBER; shiftNumber -= MAX_SHIFT_NUMBER; } result = result << shiftNumber; return result; } BingoPgExternalBitset::Iterator::Iterator(BingoPgExternalBitset& self): _fromWordIdx(0), _fromByteIdx(-1), _fromBitIdx(-1), _fromIndexes(0) { _words = self._words; _wordsInUse = *self._lastWordPtr; } static indigo::ObjArray< indigo::Array > all_indexes; int BingoPgExternalBitset::Iterator::begin() { if(all_indexes.size() == 0) { for (unsigned int buf = 0; buf < 256; ++buf) { indigo::Array& indexes = all_indexes.push(); _fillIndexes(buf, indexes); } } if (_wordsInUse == 0) return -1; _fromWordIdx = -1; _fromBitIdx = -1; _fromByteIdx = -1; _fromIndexes = 0; _fromWord = 0; return next(); } int BingoPgExternalBitset::Iterator::next() { if(_fromIndexes) { ++_fromBitIdx; if(_fromBitIdx < _fromIndexes->size()) return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } _fromIndexes = 0; if(_fromWord) { for (++_fromByteIdx; _fromByteIdx < 8; ++_fromByteIdx) { int from_byte = ((byte*) _fromWord)[_fromByteIdx]; if (from_byte == 0) continue; _fromIndexes = &(all_indexes.at(from_byte)); _fromBitIdx = 0; _shiftByte = _fromByteIdx << 3; return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } } _fromWord = 0; for (++_fromWordIdx; _fromWordIdx < _wordsInUse; ++_fromWordIdx) { _fromWord = &_words[_fromWordIdx]; if(*_fromWord == 0) continue; for (_fromByteIdx = 0; _fromByteIdx < 8; ++_fromByteIdx) { int from_byte = ((byte*) _fromWord)[_fromByteIdx]; if (from_byte == 0) continue; _fromIndexes = &(all_indexes.at(from_byte)); _fromBitIdx = 0; _shiftByte = _fromByteIdx << 3; _shiftWord = _fromWordIdx << 6; return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } } return -1; } void BingoPgExternalBitset::Iterator::_fillIndexes(byte buf, indigo::Array&indexes) { byte test_buf; for (int buf_idx = 0; buf_idx < 8; ++buf_idx) { test_buf = (byte)1 << buf_idx; if(buf & test_buf) { indexes.push(buf_idx); } } }Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_ext_bitset.h000066400000000000000000000135201271037650300261020ustar00rootroot00000000000000/* */ #ifndef _BINGO_PG_EXT_BITSET_H__ #define _BINGO_PG_EXT_BITSET_H__ #include "base_cpp/array.h" #include "base_cpp/exception.h" class BingoPgExternalBitset { public: BingoPgExternalBitset(); BingoPgExternalBitset(int nbits); inline int begin() const {return nextSetBit(0);} inline int end() const {return -1;} inline int next(int idx) const {return nextSetBit(idx + 1);} virtual ~BingoPgExternalBitset(){} void* serialize(int& size); void deserialize(void* data, int data_len, bool ext); //sets the bit at the specified index to the complement of its current value void flip(int bitIndex); //sets each bit from the specified fromIndex to the specified toIndex (exclusive) //to the complement of its current value void flip(int fromIndex, int toIndex); //sets all bits to the complement values void flip(); //sets all bits to true void set(); //sets the bit at the specified index to true void set(int bitIndex); //sets the bit at the specified index to the specified value. void set(int bitIndex, bool value); //sets the bits from the specified fromIndex to the specified toIndex(exclusive) void set(int fromIndex, int toIndex); //sets the bit specified by the index to false void reset(int bitIndex); //sets all of the bits in this BitSet to false void clear(); //returns the value of the bit with the specified index bool get(int bitIndex) const; //returns the index of the first bit that is set to true //that occurs on or after the specified starting index. If no such //bit exists then -1 is returned int nextSetBit(int fromIndex) const; //returns true if BitSet contains no bits that are set true bool isEmpty() const {return (*_lastWordPtr) == 0; }; //Returns true if the specified BitSet has any bits set to true //that are also set to true in this BitSet bool intersects(const BingoPgExternalBitset& set) const; //Performs a logical AND of this target BitSet with the argument BitSet void andWith(const BingoPgExternalBitset& set); //Performs a logical OR of this target BitSet with the argument BitSet void orWith(const BingoPgExternalBitset& set); //Performs a logical XOR of this target BitSet with the argument BitSet void xorWith(const BingoPgExternalBitset& set); //Clears all of the bits in this BitSet whose corresponding // bit is set in the specified BitSet. void andNotWith(const BingoPgExternalBitset& set); //Returns the number of bits of space actually in use by this //BitSet to represent bit values int size() const {return _bitsNumber; }; //Compares this BitSet against the specified BitSet. bool equals(const BingoPgExternalBitset& set) const; //Cloning this BitSet produces a new BitSet void copy(const BingoPgExternalBitset& set); //copy part of BitSet void copySubset(const BingoPgExternalBitset& set); //resizes this BitSet // void resize(int size); //checks if this BitSet is subset of argument BitSet bool isSubsetOf(const BingoPgExternalBitset& set) const; //checks if this BitSet is proper subset of argument BitSet bool isProperSubsetOf(const BingoPgExternalBitset& set) const; //fills with false all bits in this BitSet void zeroFill(); void bsOrBs(const BingoPgExternalBitset& set1,const BingoPgExternalBitset& set2); //Clears all of the bits in first argument BitSet whose corresponding // bits is set in the specified second argument BitSet. Result -> this void bsAndNotBs(const BingoPgExternalBitset& set1,const BingoPgExternalBitset& set2); //Performs a logical AND of two argument BitSets. result saves to this Bitset void bsAndBs(const BingoPgExternalBitset& set1,const BingoPgExternalBitset& set2); int bitsNumber() const; bool hasBits() const; qword shiftOne(int shiftNumber); private: BingoPgExternalBitset(const BingoPgExternalBitset&); //no implicit copy //bitsets are packed into arrays of "words." Currently a word is //a long long, which consists of 64 bits, requiring 6 address bits. //The choice of word size is determined purely by performance concerns. enum { ADDRESS_BITS_PER_WORD = 6, BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD, MAX_SHIFT_NUMBER = 63 }; static const qword WORD_MASK = 0xFFFFFFFFFFFFFFFFULL; //the number of words in the logical size of this BitSet. qword* _lastWordPtr; qword _lastWord; //bits number in bitset int _bitsNumber; //words size int _length; //given a bit index, return word index containing it. static int _wordIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_WORD; } //set the field wordsInUse with the logical size in words of the bit //set. WARNING:This method assumes that the number of words actually //in use is less than or equal to the current value of wordsInUse! void _recalculateWordsInUse(); //creates a new bit set. All bits are initially false. void _initWords(int nbits); // ensures that the BitSet can accommodate a given wordIndex void _expandTo(int wordIndex); int _bitCount(qword b) const; int _leastSignificantBitPosition(qword n) const; inline int _wordsInUse() {return (int)*_lastWordPtr;}; qword* _words; indigo::Array _internalWords; indigo::Array _serializeWords; public: class Iterator { public: Iterator(BingoPgExternalBitset&); ~Iterator() { } int begin(); int next(); inline int end() { return -1; } private: void _fillIndexes(byte buf, indigo::Array&indexes); int _wordsInUse; qword* _words; int _fromWordIdx; int _fromByteIdx; int _fromBitIdx; qword* _fromWord; indigo::Array* _fromIndexes; int _shiftByte; int _shiftWord; private: Iterator(const Iterator&); //no implicit copy }; }; #endif /* EXT_BITSET_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_fix_post.h000066400000000000000000000003651271037650300255660ustar00rootroot00000000000000#ifndef __bingo_pg_fix_post_h__ #define __bingo_pg_fix_post_h__ #ifdef qsort #undef qsort #endif #ifdef printf #undef printf #endif #ifdef vprintf #undef vprintf #endif #ifdef snprintf #undef snprintf #endif #endif //__bingo_pg_fix_post_h__Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_fix_pre.h000066400000000000000000000007411271037650300253650ustar00rootroot00000000000000#ifndef __bingo_pg_fix_pre_h__ #define __bingo_pg_fix_pre_h__ // Visual Studio 2013 has isnan and isinf functions defined in math.h. // PostgeSQL defines isnan and isind macroses, so we need to include math.h // before PostgreSQL includes // // See also: // http://www.postgresql.org/message-id/529D05CC.7070806@gmx.de // http://www.postgresql.org/message-id/attachment/31194/VS2013_01.patch #if (_MSC_VER >= 1800) #include #endif #endif //#ifndef __bingo_pg_fix_pre_h__Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_section.cpp000066400000000000000000000274131271037650300257350ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "storage/itemptr.h" #include "storage/block.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_section.h" #include "bingo_pg_index.h" #include "bingo_pg_search_engine.h" #include "bingo_pg_common.h" #include "base_cpp/tlscont.h" #include "base_cpp/profiling.h" using namespace indigo; IMPL_ERROR(BingoPgSection, "bingo postgres section"); BingoPgSection::BingoPgSection(BingoPgIndex& bingo_idx, int idx_strategy, int offset): _index(bingo_idx.getIndexPtr()), _offset(offset), _idxStrategy(idx_strategy){ /* * Clear section metainfo */ clear(); /* * Prepare section strategy */ bool write = (_idxStrategy == BingoPgIndex::BUILDING_STRATEGY); if(write) { /* * If write then create new buffers */ _sectionInfoBuffer.writeNewBuffer(_index, offset); _sectionInfoBuffer.changeAccess(BINGO_PG_NOLOCK); _sectionInfo.n_blocks_for_map = bingo_idx.getMapSize(); _sectionInfo.n_blocks_for_fp = bingo_idx.getFpSize(); /* * Initialize existing structures fingerprint */ _existStructures.reset(new BingoPgBufferCacheFp(offset + 1, _index, true)); /* * Initialize bits number buffers */ for (int idx = 0; idx < SECTION_BITSNUMBER_PAGES; ++idx) { BingoPgBuffer& bits_buffer = _bitsCountBuffers.push(); bits_buffer.writeNewBuffer(_index, offset + idx + SECTION_META_PAGES); bits_buffer.formEmptyIndexTuple(SECTION_BITS_PER_BLOCK * sizeof(unsigned short)); bits_buffer.changeAccess(BINGO_PG_NOLOCK); } } else { /* * Read section meta info */ _sectionInfoBuffer.readBuffer(_index, _offset, BINGO_PG_READ); int data_len; BingoSectionInfoData* data = (BingoSectionInfoData*)_sectionInfoBuffer.getIndexData(data_len); _sectionInfo = *data; _sectionInfoBuffer.changeAccess(BINGO_PG_NOLOCK); _existStructures.reset(new BingoPgBufferCacheFp(offset + 1, _index, false)); } int map_count = _sectionInfo.n_blocks_for_map; int fp_count = _sectionInfo.n_blocks_for_fp; int bin_count = _sectionInfo.n_blocks_for_bin; /* * Prepare cache arrays */ _buffersMap.expand(map_count); _buffersFp.expand(fp_count); _buffersBin.expand(bin_count); /* * Prepare offset arrays */ _offsetMap.expand(map_count); _offsetFp.expand(fp_count); _offsetBin.expand(bin_count); /* * Prepare for reading or writing all the data buffers */ int block_offset = offset + SECTION_META_PAGES + SECTION_BITSNUMBER_PAGES; for (int i = 0; i < map_count; ++i) { _offsetMap[i] = block_offset; ++block_offset; } for (int i = 0; i < fp_count; ++i) { _offsetFp[i] = block_offset; ++block_offset; } for (int i = 0; i < bin_count; ++i) { _offsetBin[i] = block_offset; ++block_offset; } if (_idxStrategy != BingoPgIndex::READING_STRATEGY) { for (int i = 0; i < map_count; ++i) { getMapBufferCache(i); } for (int i = 0; i < fp_count; ++i) { getFpBufferCache(i); } for (int i = 0; i < bin_count; ++i) { getBinBufferCache(i); } } } BingoPgSection::~BingoPgSection() { /* * Write meta info */ _sectionInfo.n_blocks_for_bin = _buffersBin.size(); _sectionInfo.section_size = getPagesCount(); if(_idxStrategy == BingoPgIndex::BUILDING_STRATEGY) { _sectionInfoBuffer.changeAccess(BINGO_PG_WRITE); _sectionInfoBuffer.formIndexTuple(&_sectionInfo, sizeof(_sectionInfo)); _sectionInfoBuffer.changeAccess(BINGO_PG_NOLOCK); } else if(_idxStrategy == BingoPgIndex::UPDATING_STRATEGY){ _sectionInfoBuffer.changeAccess(BINGO_PG_WRITE); int data_len; BingoSectionInfoData* data = (BingoSectionInfoData*)_sectionInfoBuffer.getIndexData(data_len); *data = _sectionInfo; _sectionInfoBuffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgSection::clear() { _sectionInfo.n_blocks_for_bin = 0; _sectionInfo.n_blocks_for_fp = 0; _sectionInfo.n_blocks_for_map = 0; _sectionInfo.n_structures = 0; _sectionInfo.section_size = 0; _sectionInfo.last_cmf = -1; _sectionInfo.last_xyz = -1; _sectionInfo.has_removed = 0; _sectionInfoBuffer.clear(); _existStructures.reset(0); _buffersMap.clear(); _buffersFp.clear(); _buffersBin.clear(); _offsetBin.clear(); _offsetFp.clear(); _offsetMap.clear(); } bool BingoPgSection::isExtended() { return _sectionInfo.n_structures < BINGO_MOLS_PER_SECTION; } void BingoPgSection::addStructure(BingoPgFpData& item_data) { int current_str = _sectionInfo.n_structures; elog(DEBUG1, "bingo: section: insert a structure %d", current_str); /* * Set fp bits */ for (int idx = item_data.bitBegin(); idx != item_data.bitEnd(); idx = item_data.bitNext(idx)) { int bit_idx = item_data.getBit(idx); BingoPgBufferCacheFp& buffer_fp = getFpBufferCache(bit_idx); buffer_fp.setBit(current_str, true); } int map_buf_idx = current_str / BINGO_MOLS_PER_MAPBLOCK; int map_idx = current_str % BINGO_MOLS_PER_MAPBLOCK; /* * Set tid map */ elog(DEBUG1, "bingo: section: set tid map: map buffer idx = %d offset = %d", map_buf_idx, map_idx); BingoPgBufferCacheMap& buffer_map = getMapBufferCache(map_buf_idx); buffer_map.setTidItem(map_idx, item_data.getTidItem()); /* * Prepare and set cmf map */ _setCmfData(item_data.getCmfBuf(), map_buf_idx, map_idx); /* * Prepare and set xyz map */ _setXyzData(item_data.getXyzBuf(), map_buf_idx, map_idx); /* * Set bits number */ _setBitsCountData(item_data.getBitsCount()); /* * Set structure index */ item_data.setStructureIdx(_sectionInfo.n_structures); /* * Set structure bit */ _existStructures->setBit(current_str, true); /* * Increment structures number */ ++_sectionInfo.n_structures; } int BingoPgSection::getPagesCount() const { return _buffersMap.size() + _buffersFp.size() + _buffersBin.size() + SECTION_META_PAGES + SECTION_BITSNUMBER_PAGES; } void BingoPgSection::getSectionStructures(BingoPgExternalBitset& section_bitset) { _existStructures->getCopy(section_bitset); } void BingoPgSection::removeStructure(int mol_idx) { _existStructures->setBit(mol_idx, false); _sectionInfo.has_removed = 1; } bool BingoPgSection::isStructureRemoved(int mol_idx) { if(_sectionInfo.has_removed == 0) return false; return (!_existStructures->getBit(mol_idx)); } BingoPgBufferCacheFp& BingoPgSection::getFpBufferCache(int fp_idx) { BingoPgBufferCacheFp* elem = _buffersFp.at(fp_idx); if(elem == 0) { bool write = (_idxStrategy == BingoPgIndex::BUILDING_STRATEGY); int block_offset = _offsetFp[fp_idx]; elem = new BingoPgBufferCacheFp(block_offset, _index, write); _buffersFp.set(fp_idx, elem); } return *elem; } BingoPgBufferCacheMap& BingoPgSection::getMapBufferCache(int map_idx) { BingoPgBufferCacheMap* elem = _buffersMap.at(map_idx); if(elem == 0) { bool write = (_idxStrategy == BingoPgIndex::BUILDING_STRATEGY); int block_offset = _offsetMap[map_idx]; elem = new BingoPgBufferCacheMap(block_offset, _index, write); _buffersMap.set(map_idx, elem); } return *elem; } BingoPgBufferCacheBin& BingoPgSection::getBinBufferCache(int bin_idx) { return *_getBufferBin(bin_idx); } void BingoPgSection::readSectionBitsCount(indigo::Array& bits_number) { bits_number.resize(_sectionInfo.n_structures); bits_number.zerofill(); if(_bitsCountBuffers.size() == 0) _bitsCountBuffers.resize(SECTION_BITSNUMBER_PAGES); int data_len, str_idx; unsigned short* buffer_data; for (int buf_idx = 0; buf_idx < SECTION_BITSNUMBER_PAGES; ++buf_idx) { if(buf_idx * SECTION_BITS_PER_BLOCK >= _sectionInfo.n_structures) break; BingoPgBuffer& bits_buffer = _bitsCountBuffers[buf_idx]; bits_buffer.readBuffer(_index, _offset + buf_idx + SECTION_META_PAGES, BINGO_PG_READ); buffer_data = (unsigned short*) bits_buffer.getIndexData(data_len); for (int page_str_idx = 0; page_str_idx < SECTION_BITS_PER_BLOCK; ++page_str_idx) { str_idx = buf_idx * SECTION_BITS_PER_BLOCK + page_str_idx; if (str_idx >= _sectionInfo.n_structures) break; bits_number[str_idx] = buffer_data[page_str_idx]; } bits_buffer.changeAccess(BINGO_PG_NOLOCK); } } void BingoPgSection::_setCmfData(indigo::Array& cmf_buf, int map_buf_idx, int map_idx) { /* * Set binary info */ ItemPointerData cmf_item; _setBinData(cmf_buf, _sectionInfo.last_cmf, cmf_item); /* * Set mappings */ BingoPgBufferCacheMap& buffer_map = getMapBufferCache(map_buf_idx); buffer_map.setCmfItem(map_idx, cmf_item); elog(DEBUG1, "bingo: section: set cmf map: buffer = %d, offset = %d", ItemPointerGetBlockNumber(&cmf_item), ItemPointerGetOffsetNumber(&cmf_item)); } void BingoPgSection::_setXyzData(indigo::Array& xyz_buf, int map_buf_idx, int map_idx) { /* * Set binary info */ ItemPointerData xyz_item; _setBinData(xyz_buf, _sectionInfo.last_xyz, xyz_item); /* * Set mappings */ BingoPgBufferCacheMap& buffer_map = getMapBufferCache(map_buf_idx); buffer_map.setXyzItem(map_idx, xyz_item); elog(DEBUG1, "bingo: section: set xyz map: buffer = %d, offset = %d", ItemPointerGetBlockNumber(&xyz_item), ItemPointerGetOffsetNumber(&xyz_item)); } void BingoPgSection::_setBinData(indigo::Array& buf, int& last_buf, ItemPointerData& item_data) { if(buf.size() == 0) { BINGO_PG_TRY { ItemPointerSet(&item_data, InvalidBlockNumber, 0); } BINGO_PG_HANDLE(throw Error("internal error: can not set block data: %s", message)); return; } /* * Dynamic binary buffers */ if(last_buf == -1) { int block_off = _offset + getPagesCount(); _buffersBin.add(new BingoPgBufferCacheBin(block_off, _index, true)); last_buf = _buffersBin.size() - 1; _offsetBin.push(block_off); } BingoPgBufferCacheBin* cache_bin = _getBufferBin(last_buf); /* * If not enough space for inserting a new structure - then create and new buffer */ if(!cache_bin->isEnoughSpace(buf.sizeInBytes())) { int block_off = _offset + getPagesCount(); _buffersBin.add(new BingoPgBufferCacheBin(block_off, _index, true)); last_buf = _buffersBin.size() - 1; _offsetBin.push(block_off); } cache_bin = _getBufferBin(last_buf); /* * Get cmf offset for storing cmf mapping */ unsigned short bin_offset = cache_bin->addBin(buf); /* * Set mappings */ BINGO_PG_TRY { ItemPointerSet(&item_data, last_buf, bin_offset); } BINGO_PG_HANDLE(throw Error("internal error: can not set block data: %s", message)); } void BingoPgSection::_setBitsCountData(unsigned short bits_count) { if(_bitsCountBuffers.size() == 0) _bitsCountBuffers.resize(SECTION_BITSNUMBER_PAGES); int data_len; int buf_idx = _sectionInfo.n_structures / SECTION_BITS_PER_BLOCK; int page_str_idx = _sectionInfo.n_structures % SECTION_BITS_PER_BLOCK; BingoPgBuffer& bits_buffer = _bitsCountBuffers[buf_idx]; bits_buffer.readBuffer(_index, _offset + buf_idx + SECTION_META_PAGES, BINGO_PG_WRITE); unsigned short* buffer_data = (unsigned short*) bits_buffer.getIndexData(data_len); buffer_data[page_str_idx] = bits_count; bits_buffer.changeAccess(BINGO_PG_NOLOCK); } BingoPgBufferCacheBin* BingoPgSection::_getBufferBin(int idx) { BingoPgBufferCacheBin* elem = _buffersBin.at(idx); if(elem == 0) { bool write = (_idxStrategy == BingoPgIndex::BUILDING_STRATEGY); int block_offset = _offsetBin[idx]; elem = new BingoPgBufferCacheBin(block_offset, _index, write); _buffersBin.set(idx, elem); } return elem; } Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_section.h000066400000000000000000000053031271037650300253740ustar00rootroot00000000000000#ifndef _BINGO_PG_SECTION_H__ #define _BINGO_PG_SECTION_H__ #include "base_cpp/array.h" #include "base_cpp/ptr_array.h" #include "base_cpp/obj_array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/exception.h" #include "pg_bingo_context.h" #include "bingo_pg_buffer_cache.h" #include "bingo_postgres.h" class BingoPgIndex; class BingoPgFpData; class BingoPgExternalBitset; /* * Class for handling bingo postgres section * Section consists of: * section meta info (1 block) | * section removed bitset (1 block) | * bits count buffers (16 blocks) | * map buffers (64k / 500) | * fp buffers (fp count) | * binary buffers (dynamic) */ class BingoPgSection { public: enum { SECTION_META_PAGES = 2, SECTION_BITSNUMBER_PAGES = 16, SECTION_BITS_PER_BLOCK = 4000 /* 4000 * sizeof(unsigned short) < 8K*/ }; BingoPgSection(BingoPgIndex& bingo_idx, int idx_strategy, int offset); ~BingoPgSection(); void clear(); /* * Returns true if section can be extended */ bool isExtended(); /* * Add a structure to current section */ void addStructure(BingoPgFpData&); /* * Getters */ int getPagesCount() const; int getStructuresNumber() const {return _sectionInfo.n_structures;} void getSectionStructures(BingoPgExternalBitset& section_bitset); void removeStructure(int mol_idx); bool isStructureRemoved(int mol_idx); BingoPgBufferCacheMap& getMapBufferCache(int map_idx); BingoPgBufferCacheFp& getFpBufferCache(int fp_idx); BingoPgBufferCacheBin& getBinBufferCache(int bin_idx); void readSectionBitsCount(indigo::Array& bits_count); const BingoSectionInfoData& getSectionInfo() const { return _sectionInfo;}; DECL_ERROR; private: BingoPgSection(const BingoPgSection&); //no implicit copy void _setCmfData(indigo::Array& cmf_buf, int map_buf_idx, int map_idx); void _setXyzData(indigo::Array& xyz_buf, int map_buf_idx, int map_idx); void _setBinData(indigo::Array& buf, int& last_buf, ItemPointerData& item_data); void _setBitsCountData(unsigned short bits_count); BingoPgBufferCacheBin* _getBufferBin(int idx); PG_OBJECT _index; int _offset; int _idxStrategy; BingoSectionInfoData _sectionInfo; BingoPgBuffer _sectionInfoBuffer; indigo::AutoPtr _existStructures; indigo::PtrArray _buffersFp; indigo::PtrArray _buffersMap; indigo::PtrArray _buffersBin; indigo::Array _offsetFp; indigo::Array _offsetMap; indigo::Array _offsetBin; indigo::ObjArray _bitsCountBuffers; }; #endif /* BINGO_PG_SECTION1_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_text.cpp000066400000000000000000000046061271037650300252540ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/builtins.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_text.h" #include "bingo_pg_index.h" #include "bingo_pg_common.h" using namespace indigo; IMPL_ERROR(BingoPgText, "bingo postgres text"); BingoPgText::BingoPgText():_text(0){ } BingoPgText::BingoPgText(uintptr_t text_datum):_text(0) { init(text_datum); } BingoPgText::~BingoPgText() { clear(); } void BingoPgText::init(uintptr_t text_datum) { clear(); if (text_datum != 0) { BINGO_PG_TRY { _text = DatumGetTextPCopy(text_datum); } BINGO_PG_HANDLE(throw Error("internal error: can not get text data copy: %s", message)); } } void BingoPgText::initFromString(const char* str) { clear(); BINGO_PG_TRY { _text = cstring_to_text(str); } BINGO_PG_HANDLE(throw Error("internal error: can not initialize text from a string: %s", message)); } void BingoPgText::initFromArray(indigo::Array& str) { clear(); BINGO_PG_TRY { _text = cstring_to_text_with_len(str.ptr(), str.sizeInBytes()); } BINGO_PG_HANDLE(throw Error("internal error: can not initialize text from a buffer: %s", message)); } void BingoPgText::initFromBuffer(const char* buf, int buf_len) { clear(); BINGO_PG_TRY { _text = cstring_to_text_with_len(buf, buf_len); } BINGO_PG_HANDLE(throw Error("internal error: can not initialize text from a buffer: %s", message)); } void BingoPgText::clear() { if(_text != 0) { BINGO_PG_TRY { pfree(_text); /* * Warning since can not throw an error from destructor */ } BINGO_PG_HANDLE(elog(WARNING, "internal : can not free text from a buffer: %s", message)); } _text = 0; _cstr.clear(); } const char* BingoPgText::getText(int& size) { if(_text == 0) { size = 0; return 0; } text* t = (text*)_text; size = VARSIZE(t) - VARHDRSZ; return VARDATA(t); } const char* BingoPgText::getString() { if(_cstr.size() == 0) { int text_size; const char* text_data = getText(text_size); if(text_data == 0) return 0; _cstr.copy(text_data, text_size); _cstr.push(0); } return _cstr.ptr(); } uintptr_t BingoPgText::getDatum() { return PointerGetDatum(_text); } PG_OBJECT BingoPgText::release() { PG_OBJECT res_text = _text; _text = 0; return res_text; } Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_pg_text.h000066400000000000000000000013241271037650300247130ustar00rootroot00000000000000#ifndef _BINGO_PG_TEXT_H__ #define _BINGO_PG_TEXT_H__ #include "bingo_postgres.h" #include "base_cpp/array.h" #include "base_cpp/exception.h" class BingoPgText { public: BingoPgText(); BingoPgText(uintptr_t text_datum); ~BingoPgText(); void clear(); void init(uintptr_t text_datum); void initFromString(const char* str); void initFromArray(indigo::Array& str); void initFromBuffer(const char* buf, int buf_len); const char* getText(int& size); const char* getString(); uintptr_t getDatum(); PG_OBJECT release(); DECL_ERROR; private: BingoPgText(const BingoPgText&); //no implicit copy PG_OBJECT _text; indigo::Array _cstr; }; #endif /* BINGO_PG_TEXT_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/bingo_postgres.h000077500000000000000000000025651271037650300251220ustar00rootroot00000000000000#ifndef _BINGO_POSTGRES_H__ #define _BINGO_POSTGRES_H__ #ifdef _WIN32 #define strcasestr strstr #endif #define BINGO_METAPAGE 0 /* metapage is always block 0 */ #define BINGO_CONFIG_PAGE 1 /* configuration page is always block 1 */ #define BINGO_SECTION_OFFSET_PER_BLOCK 2000 /* 2000*sizeof(int) < 8KB*/ #define BINGO_SECTION_OFFSET_BLOCKS_NUM 10 #define BINGO_METABLOCKS_NUM 2 #define BINGO_DICTIONARY_BLOCKS_NUM 100 /* 800KB for dictionary*/ #define BINGO_MOLS_PER_MAPBLOCK 440 /* sizeof(BingoTidData) = 18 * 440 < 8KB */ #define BINGO_MOLS_PER_FINGERBLOCK 64000 /* 64000 bits < 8KB */ #define BINGO_MOLS_PER_SECTION 64000 #define BINGO_TUPLE_OFFSET 1 /*INDEX tuple offset is always 1*/ #define BINGO_PG_NOLOCK 0 #define BINGO_PG_READ 1 #define BINGO_PG_WRITE 2 #define BINGO_INDEX_TYPE_MOLECULE 1 #define BINGO_INDEX_TYPE_REACTION 2 #if PG_VERSION_NUM / 100 == 904 #define BINGO_FUNCTION_EXPORT(funcname) \ PGDLLEXPORT PG_FUNCTION_INFO_V1(funcname); #else #define BINGO_FUNCTION_EXPORT(funcname) \ PG_FUNCTION_INFO_V1(funcname); \ PGDLLEXPORT Datum funcname(PG_FUNCTION_ARGS); #endif //#define PG_OBJECT void* /* * Postgres internal conflicts with the indigo library * That is why there is a workaround with void pointer */ typedef void* PG_OBJECT; #endif /* BINGO_POSTGRES_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_common/pg_bingo_context.h000066400000000000000000000036711271037650300254220ustar00rootroot00000000000000#ifndef PG_BINGO_CONTEXT_H__ #define PG_BINGO_CONTEXT_H__ extern "C" { #include "c.h" } #ifdef qsort #undef qsort #endif #ifdef printf #undef printf #endif typedef struct BingoMetaPageData { int bingo_index_version; int n_molecules; int n_blocks_for_map; int n_blocks_for_fp; int n_blocks_for_dictionary; int offset_dictionary; int n_sections; int n_pages; int index_type; } BingoMetaPageData; typedef BingoMetaPageData *BingoMetaPage; #define BingoPageGetMeta(page) \ ((BingoMetaPage) PageGetContents(page)) typedef struct BingoAutoVacOpts { bool enabled; int vacuum_threshold; int analyze_threshold; int vacuum_cost_delay; int vacuum_cost_limit; int freeze_min_age; int freeze_max_age; int freeze_table_age; double vacuum_scale_factor; double analyze_scale_factor; } BingoAutoVacOpts; typedef struct BingoIndexOptions { int treat_x_as_pseudoatom; int ignore_closing_bond_direction_mismatch; int ignore_stereocenter_errors; int stereochemistry_bidirectional_mode; int stereochemistry_detect_haworth_projection; int ignore_cistrans_errors; int allow_non_unique_dearomatization; int zero_unknown_aromatic_hydrogens; int reject_invalid_structures; int fp_ord_size; int fp_any_size; int fp_tau_size; int fp_sim_size; int sub_screening_max_bits; int sim_screening_pass_mark; int nthreads; } BingoIndexOptions; typedef struct BingoStdRdOptions { int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ BingoAutoVacOpts autovacuum; /* autovacuum-related options */ BingoIndexOptions index_parameters; } BingoStdRdOptions; typedef struct BingoSectionInfoData { int n_structures; int n_blocks_for_map; int n_blocks_for_fp; int n_blocks_for_bin; int section_size; int last_cmf; int last_xyz; char has_removed; } BingoSectionInfoData; #endif /* BINGO_PG_CONTEXT_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/000077500000000000000000000000001271037650300213525ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_build.cpp000066400000000000000000000141771271037650300250330ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/relcache.h" #include "utils/rel.h" #include "storage/bufmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_build.h" #include "bingo_core_c.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/profiling.h" #include "pg_bingo_context.h" #include "bingo_pg_buffer.h" #include "bingo_pg_config.h" #include "bingo_pg_common.h" #include "bingo_pg_text.h" #include "bingo_pg_ext_bitset.h" #include "bingo_pg_search_engine.h" #include "mango_pg_build_engine.h" #include "ringo_pg_build_engine.h" IMPL_ERROR(BingoPgBuild, "build engine"); BingoPgBuild::BingoPgBuild(PG_OBJECT index_ptr, const char* schema_name, const char* index_schema, bool new_index): _index(index_ptr), _bufferIndex(index_ptr), _buildingState(new_index) { /* * Prepare buffer section for building or updating */ BingoPgCommon::appendPath(index_schema); if (_buildingState) { _bufferIndex.setStrategy(BingoPgIndex::BUILDING_STRATEGY); _prepareBuilding(schema_name, index_schema); } else { _bufferIndex.setStrategy(BingoPgIndex::UPDATING_STRATEGY); _prepareUpdating(); } } BingoPgBuild::~BingoPgBuild() { /* * Finish building stage */ if(_buildingState) { fp_engine.ref().finishShadowProcessing(); } /* * Write meta info in desctructor */ _bufferIndex.writeDictionary(fp_engine.ref()); _bufferIndex.writeMetaInfo(); } /* * Inserts a new structure into the index * Returns true if insertion was successfull */ void BingoPgBuild::_prepareBuilding(const char* schema_name, const char* index_schema) { Relation index = (Relation) _index; BingoPgWrapper func_wr; const char* func_name = func_wr.getFuncName(index->rd_support[0]); BingoPgWrapper rel_wr; const char* rel_name = rel_wr.getRelName(index->rd_id); elog(DEBUG1, "bingo: index build: start create index '%s'", rel_name); BingoPgConfig bingo_config; /* * Safety check */ if (RelationGetNumberOfBlocks(index) != 0) throw Error("cannot initialize non-empty bingo index \"%s\"", RelationGetRelationName(index)); /* * Set up configuration from postgres table */ bingo_config.readDefaultConfig(schema_name); /* * Update configuration from pg_class.reloptions */ bingo_config.updateByIndexConfig(index); /* * Define index type */ if (strcasecmp(func_name, "matchsub") == 0) { fp_engine.reset(new MangoPgBuildEngine(bingo_config, rel_name)); } else if (strcasecmp(func_name, "matchrsub") == 0) { fp_engine.reset(new RingoPgBuildEngine(bingo_config, rel_name)); } else { throw Error("internal error: unknown index build function %s", func_name); } /* * If new build then create a metapage and initial section */ _bufferIndex.writeBegin(fp_engine.ref(), bingo_config); fp_engine.ref().prepareShadowInfo(schema_name, index_schema); } void BingoPgBuild::_prepareUpdating() { Relation index = (Relation) _index; BingoPgWrapper rel_wr; const char* rel_name = rel_wr.getRelName(index->rd_id); elog(DEBUG1, "bingo: index build: start update index '%s'", rel_name); BingoPgConfig bingo_config; _bufferIndex.readMetaInfo(); _bufferIndex.readConfigParameters(bingo_config); /* * Define index type */ if (_bufferIndex.getIndexType() == BINGO_INDEX_TYPE_MOLECULE) fp_engine.reset(new MangoPgBuildEngine(bingo_config, rel_name)); else if (_bufferIndex.getIndexType() == BINGO_INDEX_TYPE_REACTION) fp_engine.reset(new RingoPgBuildEngine(bingo_config, rel_name)); else throw Error("internal error: unknown index type %d", _bufferIndex.getIndexType()); /* * Prepare for an update */ _bufferIndex.updateBegin(); /* * Load cmf dictionary */ fp_engine->loadDictionary(_bufferIndex); } void BingoPgBuild::insertStructure(PG_OBJECT item_ptr, uintptr_t text_ptr) { if(fp_engine->getNthreads() == 1) { insertStructureSingle(item_ptr, text_ptr); } else { insertStructureParallel(item_ptr, text_ptr); } } bool BingoPgBuild::insertStructureSingle(PG_OBJECT item_ptr, uintptr_t text_ptr) { /* * Insert a new structure */ int block_number = ItemPointerGetBlockNumber((ItemPointer) item_ptr); int offset_number = ItemPointerGetOffsetNumber((ItemPointer)item_ptr); profTimerStart(t0, "bingo_pg.insert"); BingoPgBuildEngine::StructCache struct_cache; struct_cache.text.reset(new BingoPgText(text_ptr)); struct_cache.ptr = *((ItemPointer) item_ptr); elog(DEBUG1, "bingo: insert structure: processing the table entry with ctid='(%d,%d)'::tid", block_number, offset_number); if (!fp_engine->processStructure(struct_cache)) { return false; } if(struct_cache.data.get() == 0) return false; BingoPgFpData& data_ref = struct_cache.data.ref(); _bufferIndex.insertStructure(data_ref); fp_engine->insertShadowInfo(data_ref); elog(DEBUG1, "bingo: insert structure: finish processing the table entry with ctid='(%d,%d)'::tid", block_number, offset_number); return true; } void BingoPgBuild::insertStructureParallel(PG_OBJECT item_ptr, uintptr_t text_ptr) { /* * Insert a new structure */ BingoPgBuildEngine::StructCache& struct_cache = _parrallelCache.push(); struct_cache.text.reset(new BingoPgText(text_ptr)); struct_cache.ptr = *((ItemPointer) item_ptr); /* * Flush cache */ if(_parrallelCache.size() > MAX_CACHE_SIZE) flush(); } void BingoPgBuild::flush() { profTimerStart(t0, "bingo_pg.flush"); if(_parrallelCache.size() == 0) return; /* * Process cache structures */ fp_engine->processStructures(_parrallelCache); for (int c_idx = 0; c_idx < _parrallelCache.size(); ++c_idx) { profTimerStart(t1, "bingo_pg.insert_idx"); BingoPgBuildEngine::StructCache& struct_cache = _parrallelCache[c_idx]; if(struct_cache.data.get() == 0) { continue; } BingoPgFpData& data_ref = struct_cache.data.ref(); _bufferIndex.insertStructure(data_ref); fp_engine->insertShadowInfo(data_ref); } _parrallelCache.clear(); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_build.h000066400000000000000000000031311271037650300244640ustar00rootroot00000000000000/* */ #ifndef _BINGO_PG_BUILD_H__ #define _BINGO_PG_BUILD_H__ #include "base_cpp/auto_ptr.h" #include "base_cpp/obj_array.h" #include "base_cpp/exception.h" #include "bingo_postgres.h" #include "bingo_pg_index.h" #include "bingo_pg_build_engine.h" class BingoPgText; class BingoPgConfig; /* * Class for building and updating the bingo index */ class BingoPgBuild { public: enum { MAX_CACHE_SIZE=1000 }; BingoPgBuild(PG_OBJECT index, const char* schema_name,const char* index_schema, bool new_index); ~BingoPgBuild(); /* * Inserts a new structure into the index * Returns true if insertion was successfull */ void insertStructure(PG_OBJECT item_ptr, uintptr_t text_ptr); bool insertStructureSingle(PG_OBJECT item_ptr, uintptr_t text_ptr); void insertStructureParallel(PG_OBJECT item_ptr, uintptr_t text_ptr); void flush(); DECL_ERROR; private: BingoPgBuild(const BingoPgBuild&); //no implicit copy // static void _errorHandler(const char* message, void* context); void _prepareBuilding(const char* schema_name, const char* index_schema); void _prepareUpdating(); /* * Index relation */ PG_OBJECT _index; /* * Buffers section handler */ BingoPgIndex _bufferIndex; indigo::AutoPtr fp_engine; /* * There are two possible uses - build(true) and update(false) */ bool _buildingState; indigo::ObjArray _parrallelCache; //#ifdef BINGO_PG_INTEGRITY_DEBUG // indigo::AutoPtr debug_fileoutput; //#endif }; #endif /* BINGO_PG_BUILD_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_build_engine.cpp000066400000000000000000000044341271037650300263530ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_build_engine.h" #include "bingo_core_c.h" #include "base_cpp/tlscont.h" #include "base_cpp/array.h" #include "bingo_pg_index.h" using namespace indigo; BingoPgBuildEngine::BingoPgBuildEngine(): _bufferIndexPtr(0) { _bingoSession = bingoAllocateSessionID(); } BingoPgBuildEngine::~BingoPgBuildEngine(){ bingoReleaseSessionID(_bingoSession); } void BingoPgBuildEngine::_setBingoContext() { bingoSetSessionID(_bingoSession); bingoSetContext(0); } void BingoPgBuildEngine::loadDictionary(BingoPgIndex& bingo_index) { _setBingoContext(); QS_DEF(Array, dict); bingo_index.readDictionary(dict); bingoSetConfigBin("cmf_dict", dict.ptr(), dict.sizeInBytes()); } const char* BingoPgBuildEngine::getDictionary(int& size) { _setBingoContext(); const char* dict_buf; bingoGetConfigBin("cmf-dict", &dict_buf, &size); return dict_buf; } int BingoPgBuildEngine::getNthreads() { // TO DISABLE THREADS UNCOMMENT THIS // return 1; int result; _setBingoContext(); bingoGetConfigInt("nthreads", &result); return result; } int BingoPgBuildEngine::_getNextRecordCb (void *context) { BingoPgBuildEngine* engine = (BingoPgBuildEngine*)context; int& cache_idx = engine->_currentCache; ObjArray& struct_caches = *(engine->_structCaches); if(cache_idx >= struct_caches.size()) return 0; StructCache& struct_cache = struct_caches[cache_idx]; int struct_size; const char* struct_ptr = struct_cache.text->getText(struct_size); /* * Set target data. There is no need to handle errors */ bingoSetIndexRecordData(cache_idx, struct_ptr, struct_size); ++cache_idx; return 1; } void BingoPgBuildEngine::_processErrorCb (int id, void *context) { BingoPgBuildEngine* engine = (BingoPgBuildEngine*)context; ObjArray& struct_caches = *(engine->_structCaches); ItemPointer item_ptr = &(struct_caches[id].ptr); int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); elog(WARNING, "build engine: error while processing record with ctid='(%d,%d)'::tid: %s", block_number, offset_number, bingoGetWarning()); }Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_build_engine.h000066400000000000000000000033571271037650300260230ustar00rootroot00000000000000#ifndef _BINGO_PG_BUILD_ENGINE_H__ #define _BINGO_PG_BUILD_ENGINE_H__ extern "C" { #include "c.h" #include "storage/itemptr.h" } #ifdef qsort #undef qsort #endif /* * Interface class for procession fingerprint data */ #include "bingo_postgres.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/obj_array.h" #include "bingo_pg_text.h" #include "bingo_pg_search_engine.h" //class BingoPgText; class BingoPgIndex; class BingoPgConfig; //class BingoPgFpData; class BingoPgBuildEngine { public: class StructCache { public: StructCache(){} ~StructCache(){} ItemPointerData ptr; indigo::AutoPtr text; indigo::AutoPtr data; private: StructCache(const StructCache&); //no implicit copy }; BingoPgBuildEngine(); virtual ~BingoPgBuildEngine(); virtual bool processStructure(StructCache& struct_cache){return true;} virtual void processStructures(indigo::ObjArray& struct_caches){} virtual int getType() const {return 0;} virtual int getFpSize() {return 0;} virtual void prepareShadowInfo(const char* schema_name, const char* index_schema){} virtual void insertShadowInfo(BingoPgFpData&){} virtual void finishShadowProcessing(){} void loadDictionary(BingoPgIndex&); const char* getDictionary(int& size); int getNthreads(); private: BingoPgBuildEngine(const BingoPgBuildEngine&); //no implicit copy protected: void _setBingoContext(); static int _getNextRecordCb (void *context); static void _processErrorCb (int id, void *context); qword _bingoSession; BingoPgIndex* _bufferIndexPtr; indigo::ObjArray* _structCaches; int _currentCache; int _fpSize; }; #endif /* BINGO_PG_BUILD_ENGINE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_config.cpp000066400000000000000000000164101271037650300251710ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/relcache.h" #include "utils/rel.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_config.h" #include "bingo_pg_common.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "bingo_core_c.h" #include "pg_bingo_context.h" #include "bingo_pg_text.h" #include "bingo_pg_cursor.h" using namespace indigo; IMPL_ERROR(BingoPgConfig, "bingo postgres config"); void BingoPgConfig::readDefaultConfig(const char* schema_name) { _rawConfig.clear(); _tauParameters.clear(); /* * Seek for default config table */ { BingoPgCursor config_table("SELECT cname, cvalue FROM %s.bingo_config", schema_name); while (config_table.next()) { Datum name_datum = config_table.getDatum(1); Datum value_datum = config_table.getDatum(2); replaceInsertParameter(name_datum, value_datum); } } { BingoPgCursor config_table("SELECT rule_idx, tau_beg, tau_end FROM %s.bingo_tau_config", schema_name); while (config_table.next()) { Datum rule_datum = config_table.getDatum(1); Datum beg_datum = config_table.getDatum(2); Datum end_datum = config_table.getDatum(3); _replaceInsertTauParameter(rule_datum, beg_datum, end_datum); } } } void BingoPgConfig::updateByIndexConfig(PG_OBJECT index_ptr) { Relation relation = (Relation) index_ptr; if(relation->rd_options == 0) return; BingoStdRdOptions* opt = (BingoStdRdOptions*)relation->rd_options; BingoIndexOptions& options = opt->index_parameters; //TODO use isset instead of -1 not set for variables int name_key; if(options.treat_x_as_pseudoatom >= 0) { name_key = _rawConfig.findOrInsert("treat_x_as_pseudoatom"); _toString(options.treat_x_as_pseudoatom, _rawConfig.value(name_key)); } if(options.ignore_closing_bond_direction_mismatch >= 0) { name_key = _rawConfig.findOrInsert("ignore_closing_bond_direction_mismatch"); _toString(options.ignore_closing_bond_direction_mismatch, _rawConfig.value(name_key)); } if (options.ignore_stereocenter_errors >= 0) { name_key = _rawConfig.findOrInsert("ignore_stereocenter_errors"); _toString(options.ignore_stereocenter_errors, _rawConfig.value(name_key)); } if (options.stereochemistry_bidirectional_mode >= 0) { name_key = _rawConfig.findOrInsert("stereochemistry_bidirectional_mode"); _toString(options.stereochemistry_bidirectional_mode, _rawConfig.value(name_key)); } if (options.stereochemistry_detect_haworth_projection >= 0) { name_key = _rawConfig.findOrInsert("stereochemistry_detect_haworth_projection"); _toString(options.stereochemistry_detect_haworth_projection, _rawConfig.value(name_key)); } if (options.ignore_cistrans_errors >= 0) { name_key = _rawConfig.findOrInsert("ignore_cistrans_errors"); _toString(options.ignore_cistrans_errors, _rawConfig.value(name_key)); } if (options.allow_non_unique_dearomatization >= 0) { name_key = _rawConfig.findOrInsert("allow_non_unique_dearomatization"); _toString(options.allow_non_unique_dearomatization, _rawConfig.value(name_key)); } if (options.zero_unknown_aromatic_hydrogens >= 0) { name_key = _rawConfig.findOrInsert("zero_unknown_aromatic_hydrogens"); _toString(options.zero_unknown_aromatic_hydrogens, _rawConfig.value(name_key)); } if (options.reject_invalid_structures >= 0) { name_key = _rawConfig.findOrInsert("reject_invalid_structures"); _toString(options.reject_invalid_structures, _rawConfig.value(name_key)); } if (options.fp_any_size >= 0) { name_key = _rawConfig.findOrInsert("fp_any_size"); _toString(options.fp_any_size, _rawConfig.value(name_key)); } if(options.fp_ord_size >= 0) { name_key = _rawConfig.findOrInsert("fp_ord_size"); _toString(options.fp_ord_size, _rawConfig.value(name_key)); } if(options.fp_sim_size >= 0) { name_key = _rawConfig.findOrInsert("fp_sim_size"); _toString(options.fp_sim_size, _rawConfig.value(name_key)); } if(options.fp_tau_size >= 0) { name_key = _rawConfig.findOrInsert("fp_tau_size"); _toString(options.fp_tau_size, _rawConfig.value(name_key)); } if(options.sim_screening_pass_mark >= 0) { name_key = _rawConfig.findOrInsert("sim_screening_pass_mark"); _toString(options.sim_screening_pass_mark, _rawConfig.value(name_key)); } if(options.sub_screening_max_bits >= 0) { name_key = _rawConfig.findOrInsert("sub_screening_max_bits"); _toString(options.sub_screening_max_bits, _rawConfig.value(name_key)); } if(options.nthreads >= 0) { name_key = _rawConfig.findOrInsert("nthreads"); _toString(options.nthreads, _rawConfig.value(name_key)); } } void BingoPgConfig::replaceInsertParameter(uintptr_t name_datum, uintptr_t value_datum) { /* * Name and value are strings */ BingoPgText pname_text(name_datum); BingoPgText value_text(value_datum); int name_key = _rawConfig.findOrInsert(pname_text.getString()); _rawConfig.value(name_key).readString(value_text.getString(), false); } void BingoPgConfig::setUpBingoConfiguration() { if(_rawConfig.size() == 0) throw Error("configuration not set yet"); /* * Iterate through all the configs */ for (int c_idx = _rawConfig.begin(); c_idx != _rawConfig.end(); c_idx = _rawConfig.next(c_idx)) { bingoSetConfigInt(_rawConfig.key(c_idx), _getNumericValue(c_idx)); } for (int c_idx = _tauParameters.begin(); c_idx != _tauParameters.end(); c_idx = _tauParameters.next(c_idx)) { TauParameter& param = _tauParameters.value(c_idx); bingoAddTautomerRule(_tauParameters.key(c_idx), param.beg.ptr(), param.end.ptr()); } } void BingoPgConfig::serialize(indigo::Array& config_data) { ArrayOutput data_out(config_data); BingoPgCommon::DataProcessing::handleRedBlackStringArr(_rawConfig, 0, &data_out); BingoPgCommon::DataProcessing::handleRedBlackObject(_tauParameters, 0, &data_out); } void BingoPgConfig::deserialize(void* data, int data_len) { BufferScanner data_in((char*)data, data_len); BingoPgCommon::DataProcessing::handleRedBlackStringArr(_rawConfig, &data_in, 0); BingoPgCommon::DataProcessing::handleRedBlackObject(_tauParameters, &data_in, 0); } int BingoPgConfig::_getNumericValue(int c_idx) { BufferScanner scanner(_rawConfig.value(c_idx)); return scanner.readInt(); } void BingoPgConfig::_replaceInsertTauParameter(uintptr_t rule_datum, uintptr_t beg_datum, uintptr_t end_datum) { /* * tau parameter rule integer = begin string : end string */ int rule_idx = DatumGetInt32(rule_datum); BingoPgText beg_text(beg_datum); BingoPgText end_text(end_datum); TauParameter& param = _tauParameters.findOrInsert(rule_idx); param.beg.readString(beg_text.getString(), true); param.end.readString(end_text.getString(), true); } void BingoPgConfig::_toString(int value, Array& a) { ArrayOutput ao(a); ao.printf("%d", value); } void BingoPgConfig::TauParameter::serialize(Scanner* scanner, Output* output) { BingoPgCommon::DataProcessing::handleArray(this->beg, scanner, output); BingoPgCommon::DataProcessing::handleArray(this->end, scanner, output); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_config.h000066400000000000000000000024271271037650300246410ustar00rootroot00000000000000#ifndef _BINGO_PG_CONFIG_H__ #define _BINGO_PG_CONFIG_H__ #include "bingo_postgres.h" #include "base_cpp/red_black.h" #include "base_cpp/exception.h" namespace indigo { class Scanner; class Output; } class BingoPgConfig { public: BingoPgConfig(){} ~BingoPgConfig(){} void readDefaultConfig(const char* schema_name); void updateByIndexConfig(PG_OBJECT index); void replaceInsertParameter(uintptr_t name_datum, uintptr_t value_datum); void setUpBingoConfiguration(); void serialize(indigo::Array& config_data); void deserialize(void* data, int data_len); DECL_ERROR; private: BingoPgConfig(const BingoPgConfig&); //no implicit copy void _readTable(uintptr_t id, bool tau); int _getNumericValue(int c_idx); void _replaceInsertTauParameter(uintptr_t rule_datum, uintptr_t beg_datum, uintptr_t end_datum); void _toString(int value, indigo::Array&); indigo::RedBlackStringObjMap< indigo::Array > _rawConfig; class TauParameter{ public: TauParameter(){}; ~TauParameter(){}; indigo::Array beg; indigo::Array end; void serialize(indigo::Scanner*, indigo::Output*); }; indigo::RedBlackObjMap _tauParameters; }; #endif /* BINGO_PG_CONFIG_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_index.cpp000066400000000000000000000414431271037650300250370ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "storage/bufmgr.h" } #include "bingo_pg_fix_post.h" #include "base_cpp/profiling.h" #include "bingo_pg_index.h" #include "pg_bingo_context.h" #include "bingo_pg_ext_bitset.h" #include "bingo_pg_build_engine.h" #include "bingo_pg_search_engine.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_core_c.h" IMPL_ERROR(BingoPgIndex, "bingo index"); BingoPgIndex::BingoPgIndex(PG_OBJECT index) : _index(index), _strategy(READING_STRATEGY) { /* * Clean meta info */ _metaInfo.bingo_index_version = 0; _metaInfo.n_blocks_for_fp = 0; _metaInfo.n_blocks_for_map = 0; _metaInfo.n_blocks_for_dictionary = 0; _metaInfo.offset_dictionary = 0; _metaInfo.n_sections = 0; _metaInfo.n_molecules = 0; _metaInfo.index_type = 0; _metaInfo.n_pages = 0; _currentSectionIdx = -1; } /* * Begins to write a sections */ void BingoPgIndex::writeBegin(BingoPgBuildEngine& fp_engine, BingoPgConfig& bingo_config) { /* * Prepare meta info for writing */ _metaInfo.n_blocks_for_map = BINGO_MOLS_PER_FINGERBLOCK / BINGO_MOLS_PER_MAPBLOCK + 1; _metaInfo.n_blocks_for_fp = fp_engine.getFpSize(); _metaInfo.index_type = fp_engine.getType(); _metaInfo.n_pages = 0; /* * Prepare meta pages */ _initializeMetaPages(bingo_config); /* * Prepare the first section */ _metaInfo.n_sections = 0; _initializeNewSection(); /* * Set up write strategy */ _strategy = BUILDING_STRATEGY; } void BingoPgIndex::updateBegin() { _strategy = UPDATING_STRATEGY; /* * Read meta information */ readMetaInfo(); /* * Jump to the last section */ _currentSectionIdx = -1; _jumpToSection(_metaInfo.n_sections - 1); } /* * Begins to read sections */ int BingoPgIndex::readBegin() { _strategy = READING_STRATEGY; /* * Read meta information */ readMetaInfo(); /* * Jump to the first section */ _jumpToSection(0); return _currentSectionIdx; } int BingoPgIndex::readNext(int section_idx) { return section_idx + 1; } /* * Reads meta information */ void BingoPgIndex::readMetaInfo() { /* * Read meta buffer */ _metaBuffer.readBuffer(_index, BINGO_METAPAGE, BINGO_PG_READ); /* * Copy meta info */ BingoMetaPage meta_page = BingoPageGetMeta(BufferGetPage(_metaBuffer.getBuffer())); _metaInfo = *meta_page; /* * Return buffer pin */ _metaBuffer.changeAccess(BINGO_PG_NOLOCK); /* * Prepare section buffers */ _sectionOffsetBuffers.expand(BINGO_SECTION_OFFSET_BLOCKS_NUM); } void BingoPgIndex::readConfigParameters(BingoPgConfig& bingo_config) { /* * Read configuration page */ BingoPgBuffer config_buffer(_index, BINGO_CONFIG_PAGE, BINGO_PG_READ); /* * Deserialize binary stored parameters */ int data_len; void* data = config_buffer.getIndexData(data_len); bingo_config.deserialize(data, data_len); } /* * Writes meta information */ void BingoPgIndex::writeMetaInfo() { _metaBuffer.changeAccess(BINGO_PG_WRITE); BingoMetaPage meta_page = BingoPageGetMeta(BufferGetPage(_metaBuffer.getBuffer())); *meta_page = _metaInfo; _metaBuffer.changeAccess(BINGO_PG_NOLOCK); } void BingoPgIndex::_initializeMetaPages(BingoPgConfig& bingo_config) { /* * Initialize meta buffer */ _metaBuffer.writeNewBuffer(_index, BINGO_METAPAGE); _metaBuffer.formIndexTuple(&_metaInfo, sizeof (_metaInfo)); _metaBuffer.changeAccess(BINGO_PG_NOLOCK); ++_metaInfo.n_pages; /* * Initialize config buffer */ indigo::Array config_data; bingo_config.serialize(config_data); BingoPgBuffer config_buffer; config_buffer.writeNewBuffer(_index, BINGO_CONFIG_PAGE); config_buffer.formIndexTuple(config_data.ptr(), config_data.sizeInBytes()); config_buffer.clear(); ++_metaInfo.n_pages; /* * Write section mapping buffers * Fulfil by max size */ for (int block_idx = 0; block_idx < BINGO_SECTION_OFFSET_BLOCKS_NUM; ++block_idx) { BingoPgBuffer buffer; buffer.writeNewBuffer(_index, _metaInfo.n_pages); buffer.formEmptyIndexTuple(BINGO_SECTION_OFFSET_PER_BLOCK * sizeof(int)); buffer.clear(); ++_metaInfo.n_pages; } _sectionOffsetBuffers.expand(BINGO_SECTION_OFFSET_BLOCKS_NUM); /* * Write dictionary buffers * Fulfil by max size */ _metaInfo.offset_dictionary = _metaInfo.n_pages; for (int block_idx = 0; block_idx < BINGO_DICTIONARY_BLOCKS_NUM; ++block_idx) { BingoPgBuffer buffer; buffer.writeNewBuffer(_index, _metaInfo.n_pages); buffer.formEmptyIndexTuple(BingoPgBufferCacheBin::BUFFER_SIZE); buffer.clear(); ++_metaInfo.n_pages; } } void BingoPgIndex::writeDictionary(BingoPgBuildEngine& fp_engine) { if(_strategy == READING_STRATEGY) throw Error("can not write dictionary while there is no building stage"); int dict_size; const char* dict_buf = fp_engine.getDictionary(dict_size); _metaInfo.n_blocks_for_dictionary = 0; elog(DEBUG1, "bingo: index: update dictionary with size = %d", dict_size); if(dict_size == 0) return; if(dict_size > BingoPgBufferCacheBin::MAX_SIZE * BINGO_DICTIONARY_BLOCKS_NUM) throw Error("can not insert a dictionary with size = %d", dict_size); /* * Fulfil dictionary buffers */ indigo::Array buffer_dict; int dict_offset = 0; int dict_buf_size; /* * Set offset */ dict_buf_size = __min(BingoPgBufferCacheBin::MAX_SIZE, dict_size - dict_offset); while(dict_buf_size > 0) { /* * Write buffers immediately */ int blck_off = _metaInfo.offset_dictionary + _metaInfo.n_blocks_for_dictionary; BingoPgBufferCacheBin buffer_cache(blck_off, _index, false); buffer_dict.copy(dict_buf + dict_offset, dict_buf_size); buffer_cache.writeBin(buffer_dict); dict_offset += dict_buf_size; dict_buf_size = __min(BingoPgBufferCacheBin::MAX_SIZE, dict_size - dict_offset); ++_metaInfo.n_blocks_for_dictionary; } } void BingoPgIndex::clearAllBuffers() { _metaBuffer.clear(); int offset_size = _sectionOffsetBuffers.size(); _sectionOffsetBuffers.clear(); _sectionOffsetBuffers.expand(offset_size); _currentSection.free(); _currentSectionIdx = -1; } /* * Initializes and fulfils a new section */ void BingoPgIndex::_initializeNewSection() { if(_currentSection.get() != 0) { _metaInfo.n_pages += _currentSection->getPagesCount(); } int section_offset = _metaInfo.n_pages; /* * Set up section offset mapping */ _currentSectionIdx = _metaInfo.n_sections; _setSectionOffset(_currentSectionIdx, section_offset); /* * Prepare a new section */ _currentSection.reset(new BingoPgSection(*this, BUILDING_STRATEGY, section_offset)); ++_metaInfo.n_sections; } void BingoPgIndex::_setSectionOffset(int section_idx, int section_offset) { int data_len; int section_off_idx = section_idx % BINGO_SECTION_OFFSET_PER_BLOCK; BingoPgBuffer& off_buffer = _getOffsetBuffer(section_idx); /* * Update section offset mapping buffer */ off_buffer.changeAccess(BINGO_PG_WRITE); int* section_offsets = (int*)off_buffer.getIndexData(data_len); section_offsets[section_off_idx] = section_offset; off_buffer.changeAccess(BINGO_PG_NOLOCK); } BingoPgBuffer& BingoPgIndex::_getOffsetBuffer(int section_idx) { int section_buf_idx = section_idx / BINGO_SECTION_OFFSET_PER_BLOCK; /* * There is the maximum limit of sections */ if(section_buf_idx >= BINGO_SECTION_OFFSET_BLOCKS_NUM) throw Error("internal error: can not add new section, max limit reached: %d", section_idx* BINGO_MOLS_PER_SECTION); BingoPgBuffer* buffer = 0; if(_sectionOffsetBuffers.at(section_buf_idx) == 0) { _sectionOffsetBuffers.set(section_buf_idx, new BingoPgBuffer()); buffer = _sectionOffsetBuffers.at(section_buf_idx); buffer->readBuffer(_index, section_buf_idx + BINGO_METABLOCKS_NUM, BINGO_PG_NOLOCK); } buffer = _sectionOffsetBuffers.at(section_buf_idx); return *buffer; } BingoPgSection& BingoPgIndex::_jumpToSection(int section_idx) { /* * Return if current section is already set */ if (_currentSectionIdx == section_idx) return _currentSection.ref(); if (section_idx >= getSectionNumber()) { if (_strategy == READING_STRATEGY) { throw Error("could not get the buffer: section %d is out of bounds %d", section_idx, getSectionNumber()); } else { /* * If strategy is writing or updating then append new sections */ while (section_idx >= getSectionNumber()) { _initializeNewSection(); } return _currentSection.ref(); } } profTimerStart(t0, "bingo_pg.read_section"); /* * Read the section using offset mapping */ _currentSectionIdx = section_idx; profTimerStart(t1, "bingo_pg.get_offset"); int offset = _getSectionOffset(section_idx); profTimerStop(t1); _currentSection.reset(new BingoPgSection(*this, _strategy, offset)); return _currentSection.ref(); } int BingoPgIndex::_getSectionOffset(int section_idx) { /* * Prepare section offset mapping */ int section_off_idx = section_idx % BINGO_SECTION_OFFSET_PER_BLOCK; int data_len; int result; BingoPgBuffer& off_buffer = _getOffsetBuffer(section_idx); /* * Read mapping */ off_buffer.changeAccess(BINGO_PG_READ); int* section_offsets = (int*)off_buffer.getIndexData(data_len); result = section_offsets[section_off_idx] ; off_buffer.changeAccess(BINGO_PG_NOLOCK); return result; } void BingoPgIndex::readDictionary(indigo::Array& dictionary) { dictionary.clear(); if(_metaInfo.n_blocks_for_dictionary == 0) return; indigo::Array buffer_dict; int block_size = _metaInfo.n_blocks_for_dictionary + _metaInfo.offset_dictionary; /* * Read all buffers for dictionary */ for (int block_idx = _metaInfo.offset_dictionary; block_idx < block_size; ++block_idx) { BingoPgBufferCacheBin buffer_cache(block_idx, _index, false); /* * Read and concat buffers */ buffer_cache.readBin(0, buffer_dict); dictionary.concat(buffer_dict); } } void BingoPgIndex::insertStructure(BingoPgFpData& data_item) { /* * Assuming that insert only for a write strategy */ if(_strategy == READING_STRATEGY) throw Error("can not insert a structure while reading"); /* * Jump to the last section */ _jumpToSection(getSectionNumber() - 1); /* * If a structure can not be added to the current section then initialize the new */ if (!_currentSection->isExtended()) { _initializeNewSection(); } elog(DEBUG1, "bingo: index: start adding a structure to the section %d", _currentSectionIdx); //#ifdef BINGO_PG_INTEGRITY_DIR // Relation index = (Relation) _index; // // BingoPgWrapper rel_wr; // const char* rel_name = rel_wr.getRelName(index->rd_id); // // indigo::FileOutput fout(false, "%s/insert/%s_%d_%d_%d", BINGO_PG_INTEGRITY_DIR, rel_name, _currentSectionIdx, ); //#endif /* * Add a structure */ _currentSection->addStructure(data_item); elog(DEBUG1, "bingo: index: finish adding a structure to the section %d", _currentSectionIdx); /* * Return cmf map to the output */ data_item.setSectionIdx(_currentSectionIdx); /* * Increment structures common number */ ++_metaInfo.n_molecules; if (_metaInfo.n_molecules % 1000 == 0) { elog(NOTICE, "bingo.index: %d structures processed", _metaInfo.n_molecules); } } void BingoPgIndex::readTidItem(ItemPointerData& cmf_item, PG_OBJECT result_ptr) { readTidItem(ItemPointerGetBlockNumber(&cmf_item), ItemPointerGetOffsetNumber(&cmf_item), result_ptr); } void BingoPgIndex::readTidItem(int section_idx, int mol_idx, PG_OBJECT result_ptr) { profTimerStart(t0, "bingo_pg.read_tid"); /* * Prepare info for reading */ BingoPgSection& current_section = _jumpToSection(section_idx); int map_block_idx = mol_idx / BINGO_MOLS_PER_MAPBLOCK; int map_mol_idx = mol_idx % BINGO_MOLS_PER_MAPBLOCK; /* * Prepare result item */ ItemPointerData& result_item = (ItemPointerData&) (*(ItemPointer) result_ptr); BingoPgBufferCacheMap& map_cache = current_section.getMapBufferCache(map_block_idx); /* * Read tid map */ map_cache.getTidItem(map_mol_idx, result_item); } void BingoPgIndex::andWithBitset(int section_idx, int fp_idx, BingoPgExternalBitset& ext_bitset) { profTimerStart(t0, "bingo_pg.read_fp_and_with"); /* * Prepare info for reading */ BingoPgSection& current_section = _jumpToSection(section_idx); BingoPgBufferCacheFp& fp_buffer = current_section.getFpBufferCache(fp_idx); /* * And with a bitset */ fp_buffer.andWithBitset(ext_bitset); } int BingoPgIndex::getSectionStructuresNumber(int section_idx) { BingoPgSection& current_section = _jumpToSection(section_idx); return current_section.getStructuresNumber(); } const BingoSectionInfoData& BingoPgIndex::getSectionInfo (int section_idx) { BingoPgSection& current_section = _jumpToSection(section_idx); return current_section.getSectionInfo(); } void BingoPgIndex::getSectionBitset(int section_idx, BingoPgExternalBitset& section_bitset) { BingoPgSection& current_section = _jumpToSection(section_idx); current_section.getSectionStructures(section_bitset); } void BingoPgIndex::getSectionBitsCount(int section_idx, indigo::Array& bits_count){ BingoPgSection& current_section = _jumpToSection(section_idx); current_section.readSectionBitsCount(bits_count); } void BingoPgIndex::removeStructure(int section_idx, int mol_idx) { BingoPgSection& current_section = _jumpToSection(section_idx); current_section.removeStructure(mol_idx); --_metaInfo.n_molecules; } bool BingoPgIndex::isStructureRemoved(int section_idx, int mol_idx) { BingoPgSection& current_section = _jumpToSection(section_idx); return current_section.isStructureRemoved(mol_idx); } bool BingoPgIndex::isStructureRemoved(ItemPointerData& cmf_item) { return isStructureRemoved(ItemPointerGetBlockNumber(&cmf_item), ItemPointerGetOffsetNumber(&cmf_item)); } void BingoPgIndex::readCmfItem(int section_idx, int mol_idx, indigo::Array& cmf_buf) { profTimerStart(t0, "bingo_pg.read_cmf"); /* * Prepare info for reading */ BingoPgSection& current_section = _jumpToSection(section_idx); int map_block_idx = mol_idx / BINGO_MOLS_PER_MAPBLOCK; int map_mol_idx = mol_idx % BINGO_MOLS_PER_MAPBLOCK; BingoPgBufferCacheMap& map_cache = current_section.getMapBufferCache(map_block_idx); elog(DEBUG1, "bingo: index: read cmf: start read structure %d for section = %d", mol_idx, section_idx); /* * Get cmf item */ ItemPointerData cmf_item; map_cache.getCmfItem(map_mol_idx, cmf_item); /* * Check for correct block num */ dword block_num = ItemPointerGetBlockNumber(&cmf_item); if(block_num == InvalidBlockNumber) { cmf_buf.clear(); elog(DEBUG1, "bingo: index: read cmf: cmf is empty for structure %d for section = %d", mol_idx, section_idx); return; } unsigned short block_offset = ItemPointerGetOffsetNumber(&cmf_item); /* * Read cmf buffer for a given offset */ BingoPgBufferCacheBin& bin_cache = current_section.getBinBufferCache(block_num); bin_cache.readBin(block_offset, cmf_buf); elog(DEBUG1, "bingo: index: read cmf: successfully read cmf of size %d for block %d offset %d", cmf_buf.size(), block_num, block_offset); } void BingoPgIndex::readXyzItem(int section_idx, int mol_idx, indigo::Array& xyz_buf) { /* * Prepare info for reading */ BingoPgSection& current_section = _jumpToSection(section_idx); int map_block_idx = mol_idx / BINGO_MOLS_PER_MAPBLOCK; int map_mol_idx = mol_idx % BINGO_MOLS_PER_MAPBLOCK; BingoPgBufferCacheMap& map_cache = current_section.getMapBufferCache(map_block_idx); elog(DEBUG1, "bingo: index: read xyz: start read structure %d for section = %d", mol_idx, section_idx); /* * Get xyz item */ ItemPointerData xyz_item; map_cache.getXyzItem(map_mol_idx, xyz_item); /* * Check for correct block num */ dword block_num = ItemPointerGetBlockNumber(&xyz_item); if(block_num == InvalidBlockNumber) { xyz_buf.clear(); elog(DEBUG1, "bingo: index: read xyz: xyz is empty for structure %d for section = %d", mol_idx, section_idx); return; } unsigned short block_offset = ItemPointerGetOffsetNumber(&xyz_item); /* * Read xyz buffer for a given offset */ BingoPgBufferCacheBin& bin_cache = current_section.getBinBufferCache(block_num); bin_cache.readBin(block_offset, xyz_buf); elog(DEBUG1, "bingo: index: read xyz: successfully read xyz of size %d for block %d offset %d", xyz_buf.size(), block_num, block_offset); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_index.h000066400000000000000000000071611271037650300245030ustar00rootroot00000000000000#ifndef _BINGO_PG_INDEX_H__ #define _BINGO_PG_INDEX_H__ #include "base_cpp/obj_array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/exception.h" #include "bingo_postgres.h" #include "pg_bingo_context.h" #include "bingo_pg_buffer.h" #include "bingo_pg_section.h" /* * Class for handling bingo meta info and sections * Bingo index block are the following: * meta info(1 block) | config (1 block) | section mapping (10 blocks) | dictionary(100 blocks) | sections */ class BingoPgBuildEngine; class BingoPgConfig; class BingoPgFpData; class BingoPgExternalBitset; class BingoPgIndex { public: enum INDEX_STRATEGY{ BUILDING_STRATEGY, UPDATING_STRATEGY, READING_STRATEGY }; BingoPgIndex(PG_OBJECT index); ~BingoPgIndex(){} /* * Begins write section. Create meta buffer */ void writeBegin(BingoPgBuildEngine&, BingoPgConfig&); void updateBegin(); /* * Read section iterator */ int readBegin(); int readNext(int section_idx); int readEnd() {return _metaInfo.n_sections;} /* * Handle with meta info */ void readMetaInfo(); void readConfigParameters(BingoPgConfig&); void writeMetaInfo(); /* * Getters */ int getStructuresNumber() const {return _metaInfo.n_molecules;} int getSectionNumber() const {return _metaInfo.n_sections;} int getIndexType() const {return _metaInfo.index_type;} int getPagesCount() const {return _metaInfo.n_pages;} int getFpSize() const {return _metaInfo.n_blocks_for_fp;} int getMapSize() const {return _metaInfo.n_blocks_for_map;} int getDictCount() const {return _metaInfo.n_blocks_for_dictionary;} PG_OBJECT getIndexPtr() const {return _index;} INDEX_STRATEGY getIndexStrategy() const {return _strategy;} /* * Strategies */ void setStrategy(INDEX_STRATEGY strategy) {_strategy = strategy;} /* * Insert a new structure in the index */ void insertStructure(BingoPgFpData&); /* * Read all the data for the index */ void readTidItem(ItemPointerData&, PG_OBJECT result_ptr); void readTidItem(int section_idx, int mol_idx, PG_OBJECT result_ptr); void readCmfItem(int section_idx, int mol_idx, indigo::Array& cmf_buf); void readXyzItem(int section_idx, int mol_idx, indigo::Array& xyz_buf); void andWithBitset(int section_idx, int fp_idx, BingoPgExternalBitset& ext_bitset); int getSectionStructuresNumber(int section_idx); const BingoSectionInfoData& getSectionInfo (int section_idx); void getSectionBitset(int section_idx, BingoPgExternalBitset& section_bitset); void getSectionBitsCount(int section_idx, indigo::Array& bits_count); void removeStructure(int section_idx, int mol_idx); bool isStructureRemoved(int section_idx, int mol_idx); bool isStructureRemoved(ItemPointerData&); void readDictionary(indigo::Array& _dictionary); void writeDictionary(BingoPgBuildEngine&); /* * Clear all buffers */ void clearAllBuffers(); DECL_ERROR; private: BingoPgIndex(const BingoPgIndex&); //no implicit copy void _initializeMetaPages(BingoPgConfig&); void _initializeNewSection(); void _setSectionOffset(int section_idx, int section_offset); BingoPgBuffer& _getOffsetBuffer(int section_idx); BingoPgSection& _jumpToSection(int section_idx); int _getSectionOffset(int section_idx); PG_OBJECT _index; INDEX_STRATEGY _strategy; BingoMetaPageData _metaInfo; BingoPgBuffer _metaBuffer; indigo::PtrArray _sectionOffsetBuffers; indigo::AutoPtr _currentSection; int _currentSectionIdx; }; #endif /* BINGO_PG_SECTION_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_search.cpp000066400000000000000000000053071271037650300251740ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "utils/relcache.h" #include "access/genam.h" #include "access/relscan.h" #include "utils/rel.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_search.h" #include "bingo_core_c.h" #include "base_cpp/tlscont.h" #include "bingo_pg_common.h" #include "bingo_pg_buffer.h" #include "bingo_pg_text.h" #include "bingo_pg_config.h" #include "bingo_pg_ext_bitset.h" #include "mango_pg_search_engine.h" #include "ringo_pg_search_engine.h" using namespace indigo; IMPL_ERROR(BingoPgSearch, "bingo search engine"); BingoPgSearch::BingoPgSearch(PG_OBJECT rel): _initSearch(true), _indexScanDesc(0), _bufferIndex(rel) { Relation rel_idx = (Relation)rel; BingoPgWrapper rel_wr; const char* rel_name = rel_wr.getRelName(rel_idx->rd_id); elog(DEBUG1, "bingo: search: start searching for idx '%s'", rel_name); _bufferIndex.setStrategy(BingoPgIndex::READING_STRATEGY); } BingoPgSearch::~BingoPgSearch() { } /* * Searches for the next match. Return true if search was successfull * Sets up item pointer */ bool BingoPgSearch::next(PG_OBJECT scan_desc_ptr, PG_OBJECT result_ptr) { /* * All the state keeps in the context */ _indexScanDesc = scan_desc_ptr; /* * Init searching */ if(_initSearch) { _initScanSearch(); } /* * Search and return next element */ return _fpEngine->searchNext(result_ptr); } void BingoPgSearch::prepareRescan(PG_OBJECT scan_desc_ptr) { _indexScanDesc = scan_desc_ptr; if(_initSearch) { _initScanSearch(); } else { /* * Prepare query */ _fpEngine->prepareQuerySearch(_bufferIndex, _indexScanDesc); } } void BingoPgSearch::_initScanSearch() { _initSearch = false; Relation index = ((IndexScanDesc)_indexScanDesc)->indexRelation; BingoPgWrapper rel_wr; const char* rel_name = rel_wr.getRelName(index->rd_id); /* * Read configuration from index tuple */ BingoPgConfig bingo_config; _bufferIndex.readConfigParameters(bingo_config); /* * Set up search engine */ _bufferIndex.readMetaInfo(); _bufferIndex.setStrategy(BingoPgIndex::READING_STRATEGY); int index_type = _bufferIndex.getIndexType(); if (index_type == BINGO_INDEX_TYPE_MOLECULE) _fpEngine.reset(new MangoPgSearchEngine(bingo_config, rel_name)); else if (index_type == BINGO_INDEX_TYPE_REACTION) _fpEngine.reset(new RingoPgSearchEngine(bingo_config, rel_name)); else throw Error("unknown index type %d", index_type); /* * Process query structure with parameters */ _fpEngine->prepareQuerySearch(_bufferIndex, _indexScanDesc); _fpEngine->loadDictionary(_bufferIndex); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_search.h000066400000000000000000000023341271037650300246360ustar00rootroot00000000000000/* */ #ifndef _BINGO_PG_SEARCH_H__ #define _BINGO_PG_SEARCH_H__ #include "base_cpp/auto_ptr.h" #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "bingo_postgres.h" #include "bingo_pg_index.h" #include "bingo_pg_text.h" #include "bingo_pg_search_engine.h" class BingoPgText; class BingoPgBuffer; /* * Class for searcing molecular structures */ class BingoPgSearch { public: BingoPgSearch(PG_OBJECT rel); ~BingoPgSearch(); /* * Searches for the next match. Return true if search was successfull * Sets up item pointer */ bool next(PG_OBJECT scan_desc_ptr, PG_OBJECT result_item); void setItemPointer(PG_OBJECT result_ptr); void readCmfItem(indigo::Array& cmf_buf); BingoPgIndex& getIndex() {return _bufferIndex;} const char* getFuncName() const {return _funcName.ptr();} void prepareRescan(PG_OBJECT scan_desc_ptr); DECL_ERROR; private: BingoPgSearch(const BingoPgSearch&); //no implicit copy void _initScanSearch(); // void _defineQueryOptions(); bool _initSearch; PG_OBJECT _indexScanDesc; BingoPgIndex _bufferIndex; indigo::AutoPtr _fpEngine; indigo::Array _funcName; }; #endif /* BINGO_PG_SEARCH_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_search_engine.cpp000066400000000000000000000200531271037650300265140ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "storage/bufmgr.h" #include "access/itup.h" } #include "bingo_pg_fix_post.h" #include "bingo_pg_search_engine.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/array.h" #include "base_cpp/profiling.h" #include "bingo_core_c.h" #include "bingo_pg_text.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_pg_index.h" using namespace indigo; void BingoPgFpData::setTidItem(PG_OBJECT item_ptr) { ItemPointerData& item_p = *(ItemPointer) item_ptr; _mapData.tid_map = (ItemPointerData&)item_p; } void BingoPgFpData::setFingerPrints(const char* fp_buf, int size_bits) { _fingerprintBits.clear(); for (int bit_idx = 0; bit_idx < size_bits; ++bit_idx) { if (bitGetBit(fp_buf, bit_idx)) { _fingerprintBits.push(bit_idx); } } } void BingoPgFpData::setCmf(const char* cmf_buf, int cmf_len) { _cmfBuf.copy(cmf_buf, cmf_len); } void BingoPgFpData::setXyz(const char* xyz_buf, int xyz_len) { _xyzBuf.copy(xyz_buf, xyz_len); } BingoPgSearchEngine::BingoPgSearchEngine(): _fetchFound(false), _currentSection(-1), _currentIdx(-1), _blockBegin(0), _blockEnd(0), _bufferIndexPtr(0), _sectionBitset(BINGO_MOLS_PER_SECTION){ _bingoSession = bingoAllocateSessionID(); } BingoPgSearchEngine::~BingoPgSearchEngine(){ bingoReleaseSessionID(_bingoSession); } void BingoPgSearchEngine::setItemPointer(PG_OBJECT result_ptr) { _bufferIndexPtr->readTidItem(_currentSection, _currentIdx, result_ptr); } void BingoPgSearchEngine::loadDictionary(BingoPgIndex& bingo_index) { QS_DEF(Array, dict); bingo_index.readDictionary(dict); bingoSetConfigBin("cmf_dict", dict.ptr(), dict.sizeInBytes()); } //const char* BingoPgSearchEngine::getDictionary(int& size) { // _setBingoContext(); // // const char* dict_buf; // // bingoGetConfigBin("cmf-dict", &dict_buf, &size); // // return dict_buf; //} bool BingoPgSearchEngine::matchTarget(ItemPointerData& item_data) { return matchTarget(ItemPointerGetBlockNumber(&item_data), ItemPointerGetOffsetNumber(&item_data)); } void BingoPgSearchEngine::prepareQuerySearch(BingoPgIndex& bingo_idx, PG_OBJECT) { _bufferIndexPtr = &bingo_idx; _currentSection = -1; _currentIdx = -1; _fetchFound = false; _blockBegin=0; _blockEnd=bingo_idx.getSectionNumber(); } bool BingoPgSearchEngine::_searchNextCursor(PG_OBJECT result_ptr) { profTimerStart(t0, "bingo_pg.search_cursor"); ItemPointerData cmf_item; /* * Iterate through the cursor */ while (_searchCursor->next()) { _searchCursor->getId(1, cmf_item); /* * If structure is removed from index then seek to the next */ if(_bufferIndexPtr->isStructureRemoved(cmf_item)) continue; /* * If structure is not match then seek to the next */ if (!matchTarget(cmf_item)) continue; /* * Return tid map */ _bufferIndexPtr->readTidItem(cmf_item, result_ptr); return true; } _searchCursor.free(); return false; } bool BingoPgSearchEngine::_searchNextSub(PG_OBJECT result_ptr) { profTimerStart(t0, "bingo_pg.search_sub"); BingoPgFpData& query_data = _queryFpData.ref(); BingoPgIndex& bingo_index = *_bufferIndexPtr; /* * If there are matches found on the previous steps */ if(_fetchFound) { if(_fetchForNext()) { setItemPointer(result_ptr); return true; } else { _fetchFound = false; ++_currentSection; } } profTimerStart(t1, "bingo_pg.search_fp"); if(_currentSection < 0) _currentSection = _blockBegin; /* * Iterate through the sections bingo_index.readEnd() */ for (; _currentSection < _blockEnd; ++_currentSection) { /* * Get section existing structures */ bingo_index.getSectionBitset(_currentSection, _sectionBitset); _currentIdx = -1; /* * If there is no fingerprints then check every molecule */ if (query_data.bitEnd() != 0) { /* * Iterate through the query bits */ for (int fp_idx = query_data.bitBegin(); fp_idx != query_data.bitEnd() && _sectionBitset.hasBits(); fp_idx = query_data.bitNext(fp_idx)) { int fp_block = query_data.getBit(fp_idx); /* * Get fingerprint buffer in the current section */ bingo_index.andWithBitset(_currentSection, fp_block, _sectionBitset); } } /* * If bitset is not null then matches are found */ if (_sectionBitset.hasBits()) { /* * Set first match as an answer */ if(_fetchForNext()) { profTimerStop(t1); setItemPointer(result_ptr); /* * Set fetch found to return on the next steps */ _fetchFound = true; return true; } } } /* * No matches or section ends */ return false; } using namespace indigo; void BingoPgSearchEngine::_setBingoContext() { bingoSetSessionID(_bingoSession); bingoSetContext(0); } bool BingoPgSearchEngine::_fetchForNext() { /* * Seek for next target matched by fp engine */ if(_currentIdx == -1) _currentIdx = _sectionBitset.begin(); else _currentIdx = _sectionBitset.next(_currentIdx); for (; _currentIdx != _sectionBitset.end(); _currentIdx = _sectionBitset.next(_currentIdx)) { /* * Match the next target */ if(matchTarget(_currentSection, _currentIdx)) return true; } return false; } void BingoPgSearchEngine::_getBlockParameters(Array& params) { QS_DEF(Array, block_params); QS_DEF(Array, tmp); block_params.clear(); for(int i = 0; i < params.size(); ++i) { if(params[i] == ';') { block_params.copy(params.ptr(), i); block_params.push(0); tmp.copy(params); params.clear(); for(int j = i+1; j < params.size(); ++j) { params.push(tmp[j]); } params.push(0); } } bool verify_split = false; if(block_params.size() == 0) { block_params.copy(params); verify_split = true; } BufferScanner scanner(block_params); QS_DEF(Array, word); int block_id=0,block_count=0; while(!scanner.isEOF()) { scanner.skipSpace(); scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "B_ID") == 0) { scanner.skipSpace(); block_id = scanner.readInt(); if(block_id < 1) throw BingoPgError("B_ID should be a positive value: %d", block_id); } else if(strcasecmp(word.ptr(), "B_COUNT") == 0) { scanner.skipSpace(); block_count = scanner.readInt(); if(block_count < 1) throw BingoPgError("B_COUNT should be a positive value: %d", block_count); } else if(strcasecmp(word.ptr(), "") == 0) { break; } else { if(verify_split) return; else throw BingoPgError("unknown block type: %s", word.ptr()); } } if(verify_split) { params.clear(); params.push(0); } /* * Return if block id was not specified */ if(block_id==0) return; int max_blocks = _bufferIndexPtr->getSectionNumber(); if(block_count > 0) { if(block_count > max_blocks) throw BingoPgError("B_COUNT %d can not be greater then maximum block count %d", block_count, max_blocks); if(block_id>block_count) throw BingoPgError("B_ID %d can not be greater then B_COUNT %d", block_id, block_count); double b = block_id-1; b = (double)(b / block_count) * max_blocks; double e = block_id; e = (double)(e / block_count) * max_blocks; _blockBegin = (int)b; _blockEnd = (int)e; } else { if(block_id>max_blocks) throw BingoPgError("B_ID %d can not be greater then maximum block count %d", block_id, max_blocks); _blockBegin = block_id - 1; _blockEnd = block_id; } }Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/bingo_pg_search_engine.h000066400000000000000000000060701271037650300261640ustar00rootroot00000000000000#ifndef _BINGO_PG_SEARCH_ENGINE_H__ #define _BINGO_PG_SEARCH_ENGINE_H__ /* * Interface class for procession fingerprint data */ #include "base_cpp/array.h" #include "base_cpp/auto_ptr.h" #include "bingo_postgres.h" #include "bingo_pg_cursor.h" #include "pg_bingo_context.h" #include "bingo_pg_ext_bitset.h" #include "bingo_pg_buffer_cache.h" class BingoPgText; class BingoPgIndex; class BingoPgConfig; class BingoPgFpData { public: BingoPgFpData(){} virtual ~BingoPgFpData(){} virtual int bitBegin() const {return 0;} virtual int bitEnd() const {return _fingerprintBits.size();} virtual int bitNext(int bit_idx) const {return bit_idx + 1;} virtual int getBit(int bit_idx) const { return _fingerprintBits[bit_idx];} void setTidItem(PG_OBJECT item_ptr); void setSectionIdx(int section_idx) {_sectionIdx = section_idx;} void setStructureIdx(int structure_idx) {_structureIdx = structure_idx;} void setFingerPrints(const char* fp_buf, int fp_len); void setCmf(const char* cmf_buf, int cmf_len); void setXyz(const char* xyz_buf, int xyz_len); ItemPointerData& getTidItem() {return _mapData.tid_map;} ItemPointerData& getCmfItem() {return _mapData.cmf_map;} ItemPointerData& getXyzItem() {return _mapData.xyz_map;} int getSectionIdx() const {return _sectionIdx;} int getStructureIdx() const {return _structureIdx;} indigo::Array& getCmfBuf() {return _cmfBuf;} indigo::Array& getXyzBuf() {return _xyzBuf;} void setBitsCount(unsigned short bits_count) {_bitsCount = bits_count;} unsigned short getBitsCount() const {return _bitsCount;} private: BingoPgFpData(const BingoPgFpData&); //no implicit copy protected: BingoPgBufferCacheMap::BingoMapData _mapData; int _sectionIdx; int _structureIdx; unsigned short _bitsCount; indigo::Array _cmfBuf; indigo::Array _xyzBuf; indigo::Array _fingerprintBits; }; class BingoPgSearchEngine { public: BingoPgSearchEngine(); virtual ~BingoPgSearchEngine(); virtual bool matchTarget(int section_idx, int structure_idx){return false;} virtual bool matchTarget(ItemPointerData& item_data); virtual int getType() const {return 0;} virtual void prepareQuerySearch(BingoPgIndex&, PG_OBJECT scan_desc); virtual bool searchNext(PG_OBJECT result_ptr) {return false;} void setItemPointer(PG_OBJECT result_ptr); void loadDictionary(BingoPgIndex&); // const char* getDictionary(int& size); private: BingoPgSearchEngine(const BingoPgSearchEngine&); //no implicit copy protected: bool _searchNextCursor(PG_OBJECT result_ptr); bool _searchNextSub(PG_OBJECT result_ptr); void _setBingoContext(); bool _fetchForNext(); void _getBlockParameters(indigo::Array& params); qword _bingoSession; bool _fetchFound; int _currentSection; int _currentIdx; int _blockBegin; int _blockEnd; BingoPgIndex* _bufferIndexPtr; BingoPgExternalBitset _sectionBitset; indigo::AutoPtr _queryFpData; indigo::AutoPtr _searchCursor; }; #endif /* BINGO_PG_SEARCH_ENGINE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/mango_pg_build_engine.cpp000066400000000000000000000242341271037650300263560ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "mango_pg_build_engine.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "bingo_core_c.h" #include "mango_pg_search_engine.h" #include "bingo_pg_text.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_pg_index.h" #include using namespace indigo; MangoPgBuildEngine::MangoPgBuildEngine(BingoPgConfig& bingo_config, const char* rel_name): BingoPgBuildEngine(), _searchType(-1) { _setBingoContext(); /* * Set up bingo configuration */ bingo_config.setUpBingoConfiguration(); bingoTautomerRulesReady(0,0,0); bingoIndexBegin(); _relName.readString(rel_name, true); _shadowRelName.readString(rel_name, true); _shadowRelName.appendString("_shadow", true); _shadowHashRelName.readString(rel_name, true); _shadowHashRelName.appendString("_shadow_hash", true); elog(DEBUG1, "bingo: mango build: start building '%s'", _relName.ptr()); } MangoPgBuildEngine::~MangoPgBuildEngine() { elog(DEBUG1, "bingo: mango build: finish building '%s'", _relName.ptr()); _setBingoContext(); bingoIndexEnd(); } bool MangoPgBuildEngine::processStructure(StructCache& struct_cache) { _setBingoContext(); int bingo_res; BingoPgText& struct_text = struct_cache.text.ref(); ItemPointer item_ptr = &struct_cache.ptr; int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); int struct_size; const char* struct_ptr = struct_text.getText(struct_size); /* * Set target data */ bingoSetIndexRecordData(0, struct_ptr, struct_size); /* * Process target */ bingo_res = mangoIndexProcessSingleRecord(); CORE_HANDLE_ERROR_TID_NO_INDEX(bingo_res, 0, "molecule build engine: error while processing records", block_number, offset_number, bingoGetError()); CORE_HANDLE_WARNING_TID_NO_INDEX(bingo_res, 1, "molecule build engine: error while processing record", block_number, offset_number, bingoGetWarning()); if(bingo_res < 1) return false; AutoPtr fp_data(new MangoPgFpData()); if(_readPreparedInfo(0, fp_data.ref(), getFpSize())) { struct_cache.data.reset(fp_data.release()); struct_cache.data->setTidItem(item_ptr); } else { elog(WARNING, "molecule build engine: internal error while processing record with ctid='(%d,%d)'::tid: see at the previous warning", block_number, offset_number); return false; } return true; } void MangoPgBuildEngine::processStructures(ObjArray& struct_caches) { _setBingoContext(); int bingo_res; _currentCache = 0; _structCaches = &struct_caches; _fpSize = getFpSize(); /* * Process target */ bingo_res = bingoIndexProcess(false, _getNextRecordCb, _processResultCb, _processErrorCb, this); /* * If error on structure, try to parse ids */ if(bingo_res < 0) { const char* mes = bingoGetError(); const char* ERR_MES = "ERROR ON id="; const char* id_s = strstr(mes, ERR_MES); if(id_s != NULL) { BufferScanner sc(id_s); sc.skip(strlen(ERR_MES)); int id_n = -1; try { id_n = sc.readInt(); } catch (Exception&) { } if (id_n < struct_caches.size() && id_n >= 0) { ItemPointer item_ptr = &(struct_caches[id_n].ptr); int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); CORE_HANDLE_ERROR_TID_NO_INDEX(bingo_res, 0, "molecule build engine: error while processing records", block_number, offset_number, bingoGetError()); } } } CORE_HANDLE_ERROR(bingo_res, 0, "molecule build engine: error while processing records", bingoGetError()); _setBingoContext(); } void MangoPgBuildEngine::insertShadowInfo(BingoPgFpData& item_data) { MangoPgFpData& data = (MangoPgFpData&)item_data; const char* shadow_rel_name = _shadowRelName.ptr(); const char* shadow_hash_name = _shadowHashRelName.ptr(); ItemPointerData* tid_ptr = &data.getTidItem(); BingoPgCommon::executeQuery("INSERT INTO %s(b_id,tid_map,mass,fragments,gross,cnt_C,cnt_N,cnt_O,cnt_P,cnt_S,cnt_H) VALUES (" "'(%d, %d)'::tid, '(%d, %d)'::tid, %f, %d, %s)", shadow_rel_name, data.getSectionIdx(), data.getStructureIdx(), ItemPointerGetBlockNumber(tid_ptr), ItemPointerGetOffsetNumber(tid_ptr), data.getMass(), data.getFragmentsCount(), data.getGrossStr()); const RedBlackMap& hashes = data.getHashes(); for (int h_idx = hashes.begin(); h_idx != hashes.end(); h_idx = hashes.next(h_idx)) { BingoPgCommon::executeQuery("INSERT INTO %s(b_id, ex_hash, f_count) VALUES ('(%d, %d)'::tid, %d, %d)", shadow_hash_name, data.getSectionIdx(), data.getStructureIdx(), hashes.key(h_idx), hashes.value(h_idx)); } } int MangoPgBuildEngine::getFpSize() { int result; _setBingoContext(); bingoGetConfigInt("fp-size-bytes", &result); return result * 8; } void MangoPgBuildEngine::prepareShadowInfo(const char* schema_name, const char* index_schema) { /* * Create auxialiry tables */ const char* rel_name = _relName.ptr(); const char* shadow_rel_name = _shadowRelName.ptr(); const char* shadow_hash_name = _shadowHashRelName.ptr(); /* * Drop table if exists (in case of truncate index) */ if(BingoPgCommon::tableExists(index_schema, shadow_rel_name)) { BingoPgCommon::dropDependency(schema_name, index_schema, shadow_rel_name); BingoPgCommon::executeQuery("DROP TABLE %s.%s", index_schema, shadow_rel_name); } BingoPgCommon::executeQuery("CREATE TABLE %s.%s (" "b_id tid," "tid_map tid," "mass real," "fragments integer," "gross text," "cnt_C integer," "cnt_N integer," "cnt_O integer," "cnt_P integer," "cnt_S integer," "cnt_H integer," "xyz bytea)", index_schema, shadow_rel_name); if(BingoPgCommon::tableExists(index_schema, shadow_hash_name)) { BingoPgCommon::dropDependency(schema_name, index_schema, shadow_hash_name); BingoPgCommon::executeQuery("DROP TABLE %s.%s", index_schema, shadow_hash_name); } BingoPgCommon::executeQuery("CREATE TABLE %s.%s (b_id tid, ex_hash integer, f_count integer)", index_schema, shadow_hash_name); /* * Create dependency for new tables */ BingoPgCommon::createDependency(schema_name, index_schema, shadow_rel_name, rel_name); BingoPgCommon::createDependency(schema_name, index_schema, shadow_hash_name, rel_name); } void MangoPgBuildEngine::finishShadowProcessing() { /* * Create shadow indexes */ const char* shadow_rel_name = _shadowRelName.ptr(); const char* shadow_hash_rel_name = _shadowHashRelName.ptr(); BingoPgCommon::executeQuery("CREATE INDEX %s_mass_idx ON %s using hash(mass)", shadow_rel_name, shadow_rel_name); BingoPgCommon::executeQuery("CREATE INDEX %s_cmf_idx ON %s(b_id)", shadow_rel_name, shadow_rel_name); BingoPgCommon::executeQuery("CREATE INDEX %s_hash_idx ON %s using hash(ex_hash)", shadow_hash_rel_name, shadow_hash_rel_name); } void MangoPgBuildEngine::_processResultCb (void *context) { MangoPgBuildEngine* engine = (MangoPgBuildEngine*)context; ObjArray& struct_caches = *(engine->_structCaches); int cache_idx = -1; AutoPtr fp_data(new MangoPgFpData()); /* * Prepare info */ if(_readPreparedInfo(&cache_idx, fp_data.ref(), engine->_fpSize)) { StructCache& struct_cache = struct_caches[cache_idx]; struct_cache.data.reset(fp_data.release()); struct_cache.data->setTidItem(&struct_cache.ptr); } else { if(cache_idx != -1) { ItemPointer item_ptr = &(struct_caches[cache_idx].ptr); int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); elog(WARNING, "molecule build engine: internal error while processing record with ctid='(%d,%d)'::tid: see at the previous warning", block_number, offset_number); } else { elog(WARNING, "molecule build engine: internal error while processing record: see at the previous warning"); } } } bool MangoPgBuildEngine::_readPreparedInfo(int* id, MangoPgFpData& data, int fp_size) { int bingo_res; const char* cmf_buf; int cmf_len; const char*xyz_buf; int xyz_len; const char*gross_str; const char*counter_elements_str; const char*fp_buf; int fp_len; const char *fp_sim_str; float mass; int sim_fp_bits_count; /* * Get prepared data */ bingo_res = mangoIndexReadPreparedMolecule(id, &cmf_buf, &cmf_len, &xyz_buf, &xyz_len, &gross_str, &counter_elements_str, &fp_buf, &fp_len, &fp_sim_str, &mass, &sim_fp_bits_count); CORE_HANDLE_WARNING(bingo_res, 1, "molecule build engine: error while prepare record", bingoGetError()); if(bingo_res < 1) return false; /* * Set gross formula */ data.setGrossStr(gross_str, counter_elements_str); /* * Set hash information */ dword ex_hash; int ex_hash_count; bingo_res = mangoGetHash(1, -1, &ex_hash_count, &ex_hash); CORE_HANDLE_WARNING(bingo_res, 1, "molecule build engine: error while calculating hash for a record", bingoGetError()); if(bingo_res < 1) return false; int target_fragments = 0; for (int comp_idx = 0; comp_idx < ex_hash_count; ++comp_idx) { int comp_count; bingo_res = mangoGetHash(1, comp_idx, &comp_count, &ex_hash); CORE_HANDLE_WARNING(bingo_res, 1, "molecule build engine: error while calculating hash for a record", bingoGetError()); if(bingo_res < 1) return false; data.insertHash(ex_hash, comp_count); target_fragments += comp_count; } data.setFragmentsCount(target_fragments); /* * Set common info */ data.setCmf(cmf_buf, cmf_len); data.setXyz(xyz_buf, xyz_len); data.setFingerPrints(fp_buf, fp_size); data.setMass(mass); data.setBitsCount(sim_fp_bits_count); return true; } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/mango_pg_build_engine.h000066400000000000000000000026641271037650300260260ustar00rootroot00000000000000/* */ #ifndef _MANGO_PG_BUILD_ENGINE_H__ #define _MANGO_PG_BUILD_ENGINE_H__ #include "base_cpp/array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/exception.h" #include "bingo_pg_build_engine.h" #include "bingo_postgres.h" class BingoPgText; class BingoPgIndex; class BingoPgConfig; class BingoPgFpData; class MangoPgFpData; /* * Class for procession molecule fingerprint data */ class MangoPgBuildEngine : public BingoPgBuildEngine { public: MangoPgBuildEngine(BingoPgConfig& bingo_config, const char* rel_name); virtual ~MangoPgBuildEngine(); virtual bool processStructure(StructCache& struct_cache); virtual void processStructures(indigo::ObjArray& struct_caches); virtual int getFpSize(); virtual int getType() const {return BINGO_INDEX_TYPE_MOLECULE;} virtual void prepareShadowInfo(const char* schema_name, const char* index_schema); virtual void insertShadowInfo(BingoPgFpData&); virtual void finishShadowProcessing(); private: MangoPgBuildEngine(const MangoPgBuildEngine&); // no implicit copy static void _processResultCb (void *context); static bool _readPreparedInfo(int* id, MangoPgFpData& data, int fp_size); // void _handleError(int res, int success_res, const char* message, bool only_warn); indigo::Array _relName; indigo::Array _shadowRelName; indigo::Array _shadowHashRelName; int _searchType; }; #endif /* MANGO_PG_BUILD_ENGINE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/mango_pg_search_engine.cpp000066400000000000000000000630331271037650300265240ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "access/genam.h" #include "access/relscan.h" #include "utils/typcache.h" } #include "bingo_pg_fix_post.h" #include "mango_pg_search_engine.h" #include "bingo_core_c.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "base_cpp/profiling.h" #include "bingo_pg_text.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_pg_index.h" using namespace indigo; void MangoPgFpData::insertHash(dword hash, int c_cnt) { int *comp_count = _hashes.at2(hash); if(comp_count) { (*comp_count) += c_cnt; } else { _hashes.insert(hash, c_cnt); } } void MangoPgFpData::setGrossStr(const char* gross_str, const char* counter_str) { _gross.readString("'", true); _gross.appendString(gross_str, true); _gross.appendString("'", true); _gross.appendString(counter_str, true); } IMPL_ERROR(MangoPgSearchEngine, "molecule search engine"); MangoPgSearchEngine::MangoPgSearchEngine(BingoPgConfig& bingo_config, const char* rel_name): BingoPgSearchEngine(), _searchType(-1) { _setBingoContext(); /* * Set up bingo configuration */ bingo_config.setUpBingoConfiguration(); bingoTautomerRulesReady(0,0,0); bingoIndexBegin(); _relName.readString(rel_name, true); _shadowRelName.readString(rel_name, true); _shadowRelName.appendString("_shadow", true); _shadowHashRelName.readString(rel_name, true); _shadowHashRelName.appendString("_shadow_hash", true); } MangoPgSearchEngine::~MangoPgSearchEngine() { _setBingoContext(); bingoIndexEnd(); } bool MangoPgSearchEngine::matchTarget(int section_idx, int structure_idx) { profTimerStart(t0, "mango_pg.match_target"); if(_searchType == BingoPgCommon::MOL_SIM || _searchType == BingoPgCommon::MOL_MASS) { return true; } bool result = false; int bingo_res; QS_DEF(Array, mol_buf); QS_DEF(Array, xyz_buf); mol_buf.clear(); xyz_buf.clear(); if(_searchType == BingoPgCommon::MOL_SUB || _searchType == BingoPgCommon::MOL_EXACT || _searchType == BingoPgCommon::MOL_SMARTS) { _bufferIndexPtr->readCmfItem(section_idx, structure_idx, mol_buf); bingo_res = mangoNeedCoords(); CORE_HANDLE_ERROR(bingo_res, 0, "molecule search engine: error while getting coordinates flag", bingoGetError()); if(bingo_res > 0) { _bufferIndexPtr->readXyzItem(section_idx, structure_idx, xyz_buf); } // CORE_HANDLE_WARNING_TID(0, 1, "matching binary target", section_idx, structure_idx, " "); bingo_res = mangoMatchTargetBinary(mol_buf.ptr(), mol_buf.sizeInBytes(), xyz_buf.ptr(), xyz_buf.sizeInBytes()); CORE_HANDLE_ERROR_TID(bingo_res, -1, "molecule search engine: error while matching binary target", section_idx, structure_idx, bingoGetError()); CORE_RETURN_WARNING_TID(bingo_res, 0, "molecule search engine: error while matching binary target", section_idx, structure_idx, bingoGetWarning()); result = (bingo_res > 0); } else if(_searchType == BingoPgCommon::MOL_GROSS) { BingoPgText gross_text; _searchCursor->getText(2, gross_text); int gross_len; gross_text.getText(gross_len); bingo_res = mangoMatchTarget(gross_text.getString(), gross_len); CORE_HANDLE_ERROR(bingo_res, -1, "molecule search engine: error while matching gross target", bingoGetError()); CORE_RETURN_WARNING(bingo_res, 0, "molecule search engine: error while matching gross target", bingoGetWarning()); result = (bingo_res > 0); } else { throw Error("internal error: undefined search type"); } return result; } void MangoPgSearchEngine::prepareQuerySearch(BingoPgIndex& bingo_idx, PG_OBJECT scan_desc_ptr) { profTimerStart(t0, "mango_pg.prepare_query_search"); IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; if(scan_desc->numberOfKeys >= 1 && scan_desc->numberOfKeys <= 2) { _searchType = scan_desc->keyData[0].sk_strategy; } else { throw Error("unsupported number of scan keys %d", scan_desc->numberOfKeys); } /* * Set pseudo types */ if(_searchType == BingoPgCommon::MOL_MASS_GREAT || _searchType == BingoPgCommon::MOL_MASS_LESS) _searchType = BingoPgCommon::MOL_MASS; _queryFpData.reset(new MangoPgFpData()); _setBingoContext(); BingoPgSearchEngine::prepareQuerySearch(bingo_idx, scan_desc); switch(_searchType) { case BingoPgCommon::MOL_SUB: _prepareSubSearch(scan_desc); break; case BingoPgCommon::MOL_EXACT: _prepareExactSearch(scan_desc); break; case BingoPgCommon::MOL_GROSS: _prepareGrossSearch(scan_desc); break; case BingoPgCommon::MOL_SMARTS: _prepareSmartsSearch(scan_desc); break; case BingoPgCommon::MOL_MASS: _prepareMassSearch(scan_desc); break; case BingoPgCommon::MOL_SIM: _prepareSimSearch(scan_desc); break; default: throw Error("unsupported search type %d", _searchType); break; } } bool MangoPgSearchEngine::searchNext(PG_OBJECT result_ptr) { bool result = false; _setBingoContext(); if (_searchType == BingoPgCommon::MOL_EXACT || _searchType == BingoPgCommon::MOL_GROSS || _searchType == BingoPgCommon::MOL_MASS) { result = _searchNextCursor(result_ptr); } else if(_searchType == BingoPgCommon::MOL_SUB || _searchType == BingoPgCommon::MOL_SMARTS) { result = _searchNextSub(result_ptr); } else if(_searchType == BingoPgCommon::MOL_SIM) { result = _searchNextSim(result_ptr); } return result; } void MangoPgSearchEngine::_errorHandler(const char* message, void*) { throw Error("Error while searching a molecule: %s", message); } void MangoPgSearchEngine::_prepareExactQueryStrings(indigo::Array& what_clause_str, indigo::Array& from_clause_str, indigo::Array& where_clause_str) { int hash_elements_count, count, bingo_res; dword hash; ArrayOutput what_clause(what_clause_str); ArrayOutput from_clause(from_clause_str); ArrayOutput where_clause(where_clause_str); what_clause.printf("sh.b_id"); bingo_res = mangoGetHash(false, -1, &hash_elements_count, &hash); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: error while getting hash", bingoGetError()); if(hash_elements_count > MAX_HASH_ELEMENTS) hash_elements_count = MAX_HASH_ELEMENTS; from_clause.printf("%s sh", _shadowRelName.ptr()); for (int i = 0; i < hash_elements_count; i++) from_clause.printf(", %s t%d", _shadowHashRelName.ptr(), i); /* * Create complex WHERE clause */ bool where_was_added = false; if (hash_elements_count > 0) { where_was_added = true; /* * molecule ids must be same */ where_clause.printf("sh.b_id = t0.b_id AND "); for (int i = 1; i < hash_elements_count; i++) where_clause.printf("t%d.b_id = t%d.b_id AND ", i - 1, i); /* * query components must match target components */ for (int i = 0; i < hash_elements_count; i++) { bingo_res = mangoGetHash(false, i, &count, &hash); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: error while getting hash", bingoGetError()); where_clause.printf("t%d.ex_hash = %d AND ", i, hash); } /* * components count mast must target components count */ Array rel; bingo_res = mangoExactNeedComponentMatching(); CORE_HANDLE_ERROR(bingo_res, 0, "molecule search engine: error while getting need matching", bingoGetError()); if (bingo_res > 0) rel.readString(">=", true); else rel.readString("=", true); for (int i = 0; i < hash_elements_count; i++) { if (i != 0) where_clause.printf("AND "); bingo_res = mangoGetHash(false, i, &count, &hash); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: error while getting hash", bingoGetError()); where_clause.printf("t%d.f_count %s %d ", i, rel.ptr(), count); } } bingo_res = mangoExactNeedComponentMatching(); CORE_HANDLE_ERROR(bingo_res, 0, "molecule search engine: error while getting need matching", bingoGetError()); if (bingo_res == 0) { if (where_was_added) where_clause.printf("AND "); /* * There must be no other components in target */ int query_fragments_count = 0; for (int i = 0; i < hash_elements_count; i++) { bingo_res = mangoGetHash(false, i, &count, &hash); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: error while getting hash", bingoGetError()); query_fragments_count += count; } where_clause.printf("sh.fragments = %d", query_fragments_count); } what_clause_str.push(0); from_clause_str.push(0); where_clause_str.push(0); } void MangoPgSearchEngine::_prepareExactTauStrings(indigo::Array& what_clause_str, indigo::Array& from_clause_str, indigo::Array& where_clause_str) { ArrayOutput what_clause(what_clause_str); ArrayOutput from_clause(from_clause_str); ArrayOutput where_clause(where_clause_str); what_clause.printf("b_id"); from_clause.printf("%s", _shadowRelName.ptr()); const char* query_gross = mangoTauGetQueryGross(); if(query_gross == 0) CORE_HANDLE_ERROR(0, 1, "molecule search engine: error while constructing gross string", bingoGetError()); where_clause.printf("gross='%s' OR gross LIKE '%s H%%'", query_gross, query_gross); what_clause_str.push(0); from_clause_str.push(0); where_clause_str.push(0); } void MangoPgSearchEngine::_prepareSubSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; Array search_type; Array search_query; Array search_options; if(scan_desc->numberOfKeys != 1) { throw BingoPgError("molecule search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchSub' or 'matchSmarts' functions for a secondary condition", scan_desc->numberOfKeys); } int bingo_res; BingoPgFpData& data = _queryFpData.ref(); BingoPgCommon::getSearchTypeString(_searchType, search_type, true); _getScanQueries(scan_desc->keyData[0].sk_argument, search_query, search_options); /* * Get block parameters and split search options */ _getBlockParameters(search_options); /* * Set up matching parameters */ bingo_res = mangoSetupMatch(search_type.ptr(), search_query.ptr(), search_options.ptr()); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not set sub search context", bingoGetError()); const char* fingerprint_buf; int fp_len; bingo_res = mangoGetQueryFingerprint(&fingerprint_buf, &fp_len); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not get sub query fingerprint", bingoGetError()); int size_bits = fp_len * 8; data.setFingerPrints(fingerprint_buf, size_bits); } void MangoPgSearchEngine::_prepareExactSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; QS_DEF(Array, what_clause); QS_DEF(Array, from_clause); QS_DEF(Array, where_clause); QS_DEF(Array, search_type); Array search_query; Array search_options; int bingo_res; if(scan_desc->numberOfKeys != 1) { throw BingoPgError("molecule search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchExact' for a secondary condition", scan_desc->numberOfKeys); } profTimerStart(t0, "mango_pg.prepare_exact_search"); BingoPgCommon::getSearchTypeString(_searchType, search_type, true); _getScanQueries(scan_desc->keyData[0].sk_argument, search_query, search_options); /* * Set up matching parameters */ // elog(WARNING, "processing query: %s", search_query.ptr()); bingo_res = mangoSetupMatch(search_type.ptr(), search_query.ptr(), search_options.ptr()); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not set exact search context", bingoGetError()); if (strcasestr(search_options.ptr(), "TAU") != 0) { _prepareExactTauStrings(what_clause, from_clause, where_clause); } else { _prepareExactQueryStrings(what_clause, from_clause, where_clause); } _searchCursor.free(); profTimerStart(t4, "mango_pg.exact_search_cursor"); _searchCursor.reset(new BingoPgCursor("SELECT %s FROM %s WHERE %s",what_clause.ptr(), from_clause.ptr(), where_clause.ptr())); profTimerStop(t4); // if(nanoHowManySeconds(profTimerGetTime(t4) )> 1) // elog(WARNING, "select %s from %s where %s", what_clause.ptr(), from_clause.ptr(), where_clause.ptr()); } void MangoPgSearchEngine::_prepareGrossSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; QS_DEF(Array, gross_query); QS_DEF(Array, search_type); Array search_sigh; Array search_mol; int bingo_res; if(scan_desc->numberOfKeys != 1) { throw BingoPgError("molecule search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchGross' for a secondary condition", scan_desc->numberOfKeys); } BingoPgCommon::getSearchTypeString(_searchType, search_type, true); _getScanQueries(scan_desc->keyData[0].sk_argument, search_sigh, search_mol); gross_query.readString(search_sigh.ptr(), true); gross_query.appendString(" ", true); gross_query.appendString(search_mol.ptr(), true); /* * Set up matching parameters */ bingo_res = mangoSetupMatch(search_type.ptr(), gross_query.ptr(), 0); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not set gross search context", bingoGetError()); const char* gross_conditions = mangoGrossGetConditions(); if(gross_conditions == 0) CORE_HANDLE_ERROR(0, 1, "molecule search engine: can not get gross conditions", bingoGetError()); _searchCursor.free(); _searchCursor.reset(new BingoPgCursor("SELECT b_id, gross FROM %s WHERE %s", _shadowRelName.ptr(), gross_conditions)); } void MangoPgSearchEngine::_prepareSmartsSearch(PG_OBJECT scan_desc_ptr) { _prepareSubSearch(scan_desc_ptr); } void MangoPgSearchEngine::_prepareMassSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; bool min_mass_flag = false; bool max_mass_flag = false; float min_mass = 0; float max_mass = FLT_MAX; QS_DEF(Array, where_clause_str); ArrayOutput where_clause(where_clause_str); for (int arg_idx = 0; arg_idx < scan_desc->numberOfKeys; ++arg_idx) { ScanKeyData& key_data = scan_desc->keyData[arg_idx]; if(key_data.sk_strategy == BingoPgCommon::MOL_MASS_LESS) { char* mass_string = DatumGetCString(key_data.sk_argument); BufferScanner mass_scanner(mass_string); max_mass = mass_scanner.readFloat(); max_mass_flag = true; } else if(key_data.sk_strategy == BingoPgCommon::MOL_MASS_GREAT) { char* mass_string = DatumGetCString(key_data.sk_argument); BufferScanner mass_scanner(mass_string); min_mass = mass_scanner.readFloat(); min_mass_flag = true; } else { throw Error("unsupported search mass and other types"); } } if(min_mass_flag) where_clause.printf("mass > %f", min_mass); if(max_mass_flag) where_clause.printf("AND mass < %f", max_mass); where_clause.writeChar(0); _searchCursor.free(); _searchCursor.reset(new BingoPgCursor("SELECT b_id FROM %s WHERE %s",_shadowRelName.ptr(), where_clause_str.ptr())); } void MangoPgSearchEngine::_prepareSimSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; QS_DEF(Array, search_type); Array search_query; Array search_options; int bingo_res; float min_bound = 0, max_bound = 1; BingoPgFpData& data = _queryFpData.ref(); BingoPgCommon::getSearchTypeString(_searchType, search_type, true); if(scan_desc->numberOfKeys != 1) { throw BingoPgError("molecule search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchSim' for a secondary condition", scan_desc->numberOfKeys); } _getScanQueries(scan_desc->keyData[0].sk_argument, min_bound, max_bound, search_query, search_options); /* * Get block parameters and split search options */ _getBlockParameters(search_options); /* * Set up matching parameters */ bingo_res = mangoSetupMatch(search_type.ptr(), search_query.ptr(), search_options.ptr()); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not set sim search context", bingoGetError()); if(min_bound > max_bound) throw Error("min bound %f can not be greater then max bound %f", min_bound, max_bound); bingo_res = mangoSimilaritySetMinMaxBounds(min_bound, max_bound); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not get similarity min max bounds", bingoGetError()); const char* fingerprint_buf; int fp_len; bingo_res = mangoGetQueryFingerprint(&fingerprint_buf, &fp_len); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: can not get query fingerprint", bingoGetError()); int size_bits = fp_len * 8; data.setFingerPrints(fingerprint_buf, size_bits); } void MangoPgSearchEngine::_getScanQueries(uintptr_t arg_datum, Array& str1_out, Array& str2_out) { /* * Get query info */ BINGO_PG_TRY { HeapTupleHeader query_data = DatumGetHeapTupleHeader(arg_datum); Oid tupType = HeapTupleHeaderGetTypeId(query_data); int32 tupTypmod = HeapTupleHeaderGetTypMod(query_data); TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); int ncolumns = tupdesc->natts; if (ncolumns != 2) elog(ERROR, "internal error: expecting two columns in query but was %d", ncolumns); HeapTupleData tuple; /* * Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(query_data); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = query_data; Datum *values = (Datum *) palloc(ncolumns * sizeof (Datum)); bool *nulls = (bool *) palloc(ncolumns * sizeof (bool)); /* * Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* * Query tuple consist of query and options */ BingoPgText str1, str2; str1.init(values[0]); str2.init(values[1]); str1_out.readString(str1.getString(), true); str2_out.readString(str2.getString(), true); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); } BINGO_PG_HANDLE(throw Error("internal error: can not get scan query: %s", message)); } void MangoPgSearchEngine::_getScanQueries(uintptr_t arg_datum, float& min_bound, float& max_bound, indigo::Array& str1_out, indigo::Array& str2_out) { /* * Get query info */ BINGO_PG_TRY { HeapTupleHeader query_data = DatumGetHeapTupleHeader(arg_datum); Oid tupType = HeapTupleHeaderGetTypeId(query_data); int32 tupTypmod = HeapTupleHeaderGetTypMod(query_data); TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); int ncolumns = tupdesc->natts; if (ncolumns != 4) elog(ERROR, "internal error: expecting four columns in query but was %d", ncolumns); HeapTupleData tuple; /* * Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(query_data); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = query_data; Datum *values = (Datum *) palloc(ncolumns * sizeof (Datum)); bool *nulls = (bool *) palloc(ncolumns * sizeof (bool)); /* * Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* * Query tuple consist of query and options */ min_bound = DatumGetFloat4(values[0]); max_bound = DatumGetFloat4(values[1]); BingoPgText str1, str2; str1.init(values[2]); str2.init(values[3]); str1_out.readString(str1.getString(), true); str2_out.readString(str2.getString(), true); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); } BINGO_PG_HANDLE(throw Error("internal error: can not get scan query: %s", message)); } bool MangoPgSearchEngine::_searchNextSim(PG_OBJECT result_ptr) { profTimerStart(t0, "mango_pg.search_sim"); /* * If there are mathces found on the previous steps */ if(_fetchFound) { if(_fetchForNext()) { setItemPointer(result_ptr); return true; } else { _fetchFound = false; ++_currentSection; } } BingoPgFpData& query_data = _queryFpData.ref(); BingoPgIndex& bingo_index = *_bufferIndexPtr; QS_DEF(Array, bits_count); QS_DEF(Array, common_ones); BingoPgExternalBitset screening_bitset(BINGO_MOLS_PER_SECTION); int* min_bounds, * max_bounds, bingo_res; /* * Read first section */ if(_currentSection < 0) _currentSection = _blockBegin; /* * Iterate through the sections */ for (; _currentSection < _blockEnd; ++_currentSection) { _currentIdx = -1; /* * Get section existing structures */ bingo_index.getSectionBitset(_currentSection, _sectionBitset); int possible_str_count = _sectionBitset.bitsNumber(); /* * If there is no bits then screen whole the structures */ if (query_data.bitEnd() != 0 && possible_str_count > 0) { /* * Read structures bits count */ // profTimerStart(t5, "mango_pg.get_section_bits"); bingo_index.getSectionBitsCount(_currentSection, bits_count); // profTimerStop(t5); /* * Prepare min max bounds */ // profTimerStart(t3, "mango_pg.get_min_max"); bingo_res = mangoSimilarityGetBitMinMaxBoundsArray(bits_count.size(), bits_count.ptr(), &min_bounds, &max_bounds); // profTimerStop(t3); CORE_HANDLE_ERROR(bingo_res, 1, "molecule search engine: error while getting similarity bounds array", bingoGetError()); /* * Prepare common bits array */ common_ones.resize(bits_count.size()); common_ones.zerofill(); /* * Iterate through the query bits */ int iteration_idx = 0; int fp_count = query_data.bitEnd(); for (int fp_idx = query_data.bitBegin(); fp_idx != query_data.bitEnd() && possible_str_count > 0; fp_idx = query_data.bitNext(fp_idx)) { int fp_block = query_data.getBit(fp_idx); /* * Copy passed structures on each iteration step */ // profTimerStart(t7, "mango_pg.copy"); screening_bitset.copy(_sectionBitset); // profTimerStop(t7); /* * Get commons in fingerprint buffer */ // profTimerStart(t2, "mango_pg.andwith"); bingo_index.andWithBitset(_currentSection, fp_block, screening_bitset); // profTimerStop(t2); // profTimerStart(t4, "mango_pg.new_common_ones"); int screen_idx = screening_bitset.begin(); for (; screen_idx != screening_bitset.end() && possible_str_count > 0; screen_idx = screening_bitset.next(screen_idx)) { /* * Calculate new common ones */ int& one_counter = common_ones[screen_idx]; ++one_counter; /* * If common ones is out of bounds then it is not passed the screening */ if ((one_counter > max_bounds[screen_idx]) || ((one_counter + fp_count - iteration_idx) < min_bounds[screen_idx])) { _sectionBitset.set(screen_idx, false); --possible_str_count; } } // profTimerStop(t4); ++iteration_idx; } /* * Screen the last time for all the possible structures */ // profTimerStart(t6, "mango_pg.lst_screen"); int screen_idx = _sectionBitset.begin(); for (; screen_idx != _sectionBitset.end() && possible_str_count > 0; screen_idx = _sectionBitset.next(screen_idx)) { int& one_counter = common_ones[screen_idx]; if ((one_counter > max_bounds[screen_idx]) || (one_counter < min_bounds[screen_idx])) { /* * Not passed screening */ _sectionBitset.set(screen_idx, false); --possible_str_count; } } // profTimerStop(t6); } /* * Return false on empty fingerprint */ if(query_data.bitEnd() == 0) return false; /* * If bitset is not null then matches are found */ if (_sectionBitset.hasBits()) { /* * Set first match as an answer */ if(_fetchForNext()) { profTimerStop(t0); setItemPointer(result_ptr); /* * Set fetch found to return on the next steps */ _fetchFound = true; return true; } } } /* * No matches or section ends */ return false; }Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/mango_pg_search_engine.h000066400000000000000000000057651271037650300262010ustar00rootroot00000000000000/* */ #ifndef _MANGO_PG_SEARCH_ENGINE_H__ #define _MANGO_PG_SEARCH_ENGINE_H__ #include "bingo_pg_search_engine.h" #include "base_cpp/array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/red_black.h" #include "base_cpp/exception.h" #include "pg_bingo_context.h" #include "bingo_postgres.h" #include "bingo_pg_cursor.h" #include class BingoPgText; class BingoPgIndex; class BingoPgConfig; class MangoPgFpData : public BingoPgFpData { public: MangoPgFpData():_mass(0), _fragments(0) {} virtual ~MangoPgFpData() {} void setMass(float mass) {_mass = mass;} float getMass() const {return _mass;} void insertHash(dword hash, int c_cnt); const indigo::RedBlackMap& getHashes() const {return _hashes;} void setGrossStr(const char* gross_str, const char* counter_str); const char* getGrossStr() const {return _gross.ptr();} int getFragmentsCount() const {return _fragments;} void setFragmentsCount(int fr) {_fragments = fr;} private: MangoPgFpData(const MangoPgFpData&); //no implicit copy float _mass; int _fragments; /* * Map: hash - components count */ indigo::RedBlackMap _hashes; indigo::Array _gross; }; /* * Class for procession molecule fingerprint data */ class MangoPgSearchEngine : public BingoPgSearchEngine { public: enum { MAX_HASH_ELEMENTS = 5 }; MangoPgSearchEngine(BingoPgConfig& bingo_config, const char* rel_name); virtual ~MangoPgSearchEngine(); virtual bool matchTarget(int section_idx, int structure_idx); virtual bool matchTarget(ItemPointerData& item_data) {return BingoPgSearchEngine::matchTarget(item_data);} virtual int getType() const {return BINGO_INDEX_TYPE_MOLECULE;} virtual void prepareQuerySearch(BingoPgIndex&, PG_OBJECT scan_desc); virtual bool searchNext(PG_OBJECT result_ptr); DECL_ERROR; private: MangoPgSearchEngine(const MangoPgSearchEngine&); // no implicit copy bool _searchNextSim(PG_OBJECT result_ptr); void _prepareExactQueryStrings(indigo::Array& what_clause, indigo::Array& from_clause, indigo::Array& where_clause); void _prepareExactTauStrings(indigo::Array& what_clause, indigo::Array& from_clause, indigo::Array& where_clause); void _prepareSubSearch(PG_OBJECT scan_desc); void _prepareExactSearch(PG_OBJECT scan_desc); void _prepareGrossSearch(PG_OBJECT scan_desc); void _prepareSmartsSearch(PG_OBJECT scan_desc); void _prepareMassSearch(PG_OBJECT scan_desc); void _prepareSimSearch(PG_OBJECT scan_desc); void _getScanQueries(uintptr_t arg_datum, indigo::Array& str1, indigo::Array& str2); void _getScanQueries(uintptr_t arg_datum, float& min_bound, float& max_bound, indigo::Array& str1, indigo::Array& str2); static void _errorHandler(const char* message, void* context); indigo::Array _relName; indigo::Array _shadowRelName; indigo::Array _shadowHashRelName; int _searchType; }; #endif /* MANGO_PG_SEARCH_ENGINE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/ringo_pg_build_engine.cpp000066400000000000000000000152071271037650300263730ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" } #include "bingo_pg_fix_post.h" #include "ringo_pg_build_engine.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "bingo_core_c.h" #include "ringo_pg_search_engine.h" #include "bingo_pg_text.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_pg_index.h" #include using namespace indigo; RingoPgBuildEngine::RingoPgBuildEngine(BingoPgConfig& bingo_config, const char* rel_name): BingoPgBuildEngine(), _searchType(-1) { _setBingoContext(); // bingoSetErrorHandler(_errorHandler, 0); /* * Set up bingo configuration */ bingo_config.setUpBingoConfiguration(); bingoTautomerRulesReady(0,0,0); bingoIndexBegin(); _relName.readString(rel_name, true); _shadowRelName.readString(rel_name, true); _shadowRelName.appendString("_shadow", true); elog(DEBUG1, "bingo: ringo build: start building '%s'", _relName.ptr()); } RingoPgBuildEngine::~RingoPgBuildEngine() { elog(DEBUG1, "bingo: ringo build: finish building '%s'", _relName.ptr()); bingoIndexEnd(); } bool RingoPgBuildEngine::processStructure(StructCache& struct_cache) { _setBingoContext(); ItemPointer item_ptr = &struct_cache.ptr; int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); int struct_size, bingo_res; const char* struct_ptr = struct_cache.text->getText(struct_size); /* * Set target data */ bingoSetIndexRecordData(0, struct_ptr, struct_size); /* * Process target */ bingo_res = ringoIndexProcessSingleRecord(); CORE_HANDLE_ERROR_TID_NO_INDEX(bingo_res, 0, "reaction build engine: error while processing records", block_number, offset_number, bingoGetError()); CORE_HANDLE_WARNING_TID_NO_INDEX(bingo_res, 1, "reaction build engine: error while processing record", block_number, offset_number, bingoGetWarning()); if(bingo_res < 1) return false; AutoPtr fp_data(new RingoPgFpData()); if(_readPreparedInfo(0, fp_data.ref(), getFpSize())) { struct_cache.data.reset(fp_data.release()); struct_cache.data->setTidItem(item_ptr); } else { elog(WARNING, "reaction build engine: internal error while processing record with ctid='(%d,%d)'::tid: see at the previous warning", block_number, offset_number); return false; } return true; } void RingoPgBuildEngine::processStructures(ObjArray& struct_caches) { _setBingoContext(); int bingo_res; _currentCache = 0; _structCaches = &struct_caches; _fpSize = getFpSize(); /* * Process target */ bingo_res = bingoIndexProcess(true, _getNextRecordCb, _processResultCb, _processErrorCb, this); CORE_HANDLE_ERROR(bingo_res, 0, "reaction build engine: error while processing records", bingoGetError()); _setBingoContext(); } void RingoPgBuildEngine::insertShadowInfo(BingoPgFpData& item_data) { RingoPgFpData& data = (RingoPgFpData&) item_data; const char* shadow_rel_name = _shadowRelName.ptr(); ItemPointerData* tid_ptr = &data.getTidItem(); BingoPgCommon::executeQuery("INSERT INTO %s(b_id,tid_map,ex_hash) VALUES (" "'(%d, %d)'::tid, '(%d, %d)'::tid, %d)", shadow_rel_name, data.getSectionIdx(), data.getStructureIdx(), ItemPointerGetBlockNumber(tid_ptr), ItemPointerGetOffsetNumber(tid_ptr), data.getHash()); } int RingoPgBuildEngine::getFpSize() { int result; _setBingoContext(); bingoGetConfigInt("reaction-fp-size-bytes", &result); return result * 8; } void RingoPgBuildEngine::prepareShadowInfo(const char* schema_name, const char* index_schema) { /* * Create auxialiry tables */ const char* rel_name = _relName.ptr(); const char* shadow_rel_name = _shadowRelName.ptr(); /* * Drop table if exists (in case of truncate index) */ if(BingoPgCommon::tableExists(index_schema, shadow_rel_name)) { BingoPgCommon::dropDependency(schema_name, index_schema, shadow_rel_name); BingoPgCommon::executeQuery("DROP TABLE %s.%s", index_schema, shadow_rel_name); } BingoPgCommon::executeQuery("CREATE TABLE %s.%s (" "b_id tid," "tid_map tid," "ex_hash integer)", index_schema, shadow_rel_name); /* * Create dependency for new tables */ BingoPgCommon::createDependency(schema_name, index_schema, shadow_rel_name, rel_name); } void RingoPgBuildEngine::finishShadowProcessing() { /* * Create shadow indexes */ const char* shadow_rel_name = _shadowRelName.ptr(); BingoPgCommon::executeQuery("CREATE INDEX %s_hash_idx ON %s using hash(ex_hash)", shadow_rel_name, shadow_rel_name); } void RingoPgBuildEngine::_processResultCb (void *context) { RingoPgBuildEngine* engine = (RingoPgBuildEngine*)context; ObjArray& struct_caches = *(engine->_structCaches); int cache_idx = -1; AutoPtr fp_data(new RingoPgFpData()); /* * Prepare info */ if(_readPreparedInfo(&cache_idx, fp_data.ref(), engine->_fpSize)) { StructCache& struct_cache = struct_caches[cache_idx]; struct_cache.data.reset(fp_data.release()); struct_cache.data->setTidItem(&struct_cache.ptr); } else { if(cache_idx != -1) { ItemPointer item_ptr = &(struct_caches[cache_idx].ptr); int block_number = ItemPointerGetBlockNumber(item_ptr); int offset_number = ItemPointerGetOffsetNumber(item_ptr); elog(WARNING, "reaction build engine: internal error while processing record with ctid='(%d,%d)'::tid: see at the previous warning", block_number, offset_number); } else { elog(WARNING, "reaction build engine: internal error while processing record: see at the previous warning"); } } } bool RingoPgBuildEngine::_readPreparedInfo(int* id, RingoPgFpData& data, int fp_size) { int bingo_res; const char* crf_buf; int crf_len; const char*fp_buf; int fp_len; /* * Get prepared data */ bingo_res = ringoIndexReadPreparedReaction(id, &crf_buf, &crf_len, &fp_buf, &fp_len); CORE_HANDLE_WARNING(bingo_res, 1, "reaction build engine: error while prepare record", bingoGetError()); if(bingo_res < 1) return false; /* * Set hash information */ dword ex_hash; bingo_res = ringoGetHash(1, &ex_hash); CORE_HANDLE_WARNING(bingo_res, 1, "reaction build engine: error while get hash", bingoGetError()); if(bingo_res < 1) return false; data.setHash(ex_hash); /* * Set common info */ data.setCmf(crf_buf, crf_len); data.setFingerPrints(fp_buf, fp_size); return true; } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/ringo_pg_build_engine.h000066400000000000000000000026321271037650300260360ustar00rootroot00000000000000#ifndef _RINGO_PG_BUILD_ENGINE_H__ #define _RINGO_PG_BUILD_ENGINE_H__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/auto_ptr.h" #include "bingo_pg_build_engine.h" #include "bingo_postgres.h" class BingoPgText; class BingoPgIndex; class BingoPgConfig; class BingoPgFpData; class RingoPgFpData; /* * Class for procession reaction fingerprint data */ class RingoPgBuildEngine : public BingoPgBuildEngine { public: RingoPgBuildEngine(BingoPgConfig& bingo_config, const char* rel_name); virtual ~RingoPgBuildEngine(); virtual bool processStructure(StructCache& struct_cache); virtual void processStructures(indigo::ObjArray& struct_cache); virtual int getFpSize(); virtual int getType() const {return BINGO_INDEX_TYPE_REACTION;} virtual void prepareShadowInfo(const char* schema_name, const char* index_schema); virtual void insertShadowInfo(BingoPgFpData&); virtual void finishShadowProcessing(); // hardcode return single threading for reactions due to an instable state int getNthreads() {return 1;} private: RingoPgBuildEngine(const RingoPgBuildEngine&); // no implicit copy static void _processResultCb (void *context); static bool _readPreparedInfo(int* id, RingoPgFpData& data, int fp_size); indigo::Array _relName; indigo::Array _shadowRelName; int _searchType; }; #endif /* RINGO_PG_BUILD_ENGINE_H */ Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/ringo_pg_search_engine.cpp000066400000000000000000000201431271037650300265340ustar00rootroot00000000000000#include "bingo_pg_fix_pre.h" extern "C" { #include "postgres.h" #include "fmgr.h" #include "access/genam.h" #include "access/relscan.h" #include "utils/typcache.h" } #include "bingo_pg_fix_post.h" #include "ringo_pg_search_engine.h" #include "bingo_core_c.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "bingo_pg_text.h" #include "bingo_pg_common.h" #include "bingo_pg_config.h" #include "bingo_pg_index.h" using namespace indigo; IMPL_ERROR(RingoPgSearchEngine, "reaction search engine"); RingoPgSearchEngine::RingoPgSearchEngine(BingoPgConfig& bingo_config, const char* rel_name): BingoPgSearchEngine(), _searchType(-1) { _setBingoContext(); /* * Set up bingo configuration */ bingo_config.setUpBingoConfiguration(); bingoTautomerRulesReady(0,0,0); bingoIndexBegin(); _relName.readString(rel_name, true); _shadowRelName.readString(rel_name, true); _shadowRelName.appendString("_shadow", true); } RingoPgSearchEngine::~RingoPgSearchEngine() { bingoIndexEnd(); } bool RingoPgSearchEngine::matchTarget(int section_idx, int structure_idx) { bool result = false; int bingo_res; QS_DEF(Array, react_buf); react_buf.clear(); _bufferIndexPtr->readCmfItem(section_idx, structure_idx, react_buf); bingo_res = ringoMatchTargetBinary(react_buf.ptr(), react_buf.sizeInBytes()); CORE_HANDLE_ERROR_TID(bingo_res, -1, "reaction search engine: error while matching target", section_idx, structure_idx,bingoGetError()); CORE_RETURN_WARNING_TID(bingo_res, 0, "reaction search engine: error while matching target", section_idx, structure_idx, bingoGetWarning()); result = (bingo_res == 1); return result; } void RingoPgSearchEngine::prepareQuerySearch(BingoPgIndex& bingo_idx, PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; if(scan_desc->numberOfKeys >= 1 && scan_desc->numberOfKeys <= 2) { _searchType = scan_desc->keyData[0].sk_strategy; } else { throw Error("unsupported number of scan keys %d", scan_desc->numberOfKeys); } _queryFpData.reset(new RingoPgFpData()); _setBingoContext(); BingoPgSearchEngine::prepareQuerySearch(bingo_idx, scan_desc); switch(_searchType) { case BingoPgCommon::REACT_SUB: _prepareSubSearch(scan_desc); break; case BingoPgCommon::REACT_EXACT: _prepareExactSearch(scan_desc); break; case BingoPgCommon::REACT_SMARTS: _prepareSmartsSearch(scan_desc); break; default: throw Error("unsupported search type"); } } bool RingoPgSearchEngine::searchNext(PG_OBJECT result_ptr) { bool result = false; _setBingoContext(); if (_searchType == BingoPgCommon::REACT_EXACT) { result = _searchNextCursor(result_ptr); } else if(_searchType == BingoPgCommon::REACT_SUB || _searchType == BingoPgCommon::REACT_SMARTS) { result = _searchNextSub(result_ptr); } return result; } void RingoPgSearchEngine::_errorHandler(const char* message, void*) { throw Error("Error while searching a reaction: %s", message); } void RingoPgSearchEngine::_prepareExactQueryStrings(indigo::Array& what_clause_str, indigo::Array& from_clause_str, indigo::Array& where_clause_str) { ArrayOutput what_clause(what_clause_str); ArrayOutput from_clause(from_clause_str); ArrayOutput where_clause(where_clause_str); what_clause.printf("b_id"); from_clause.printf("%s", _shadowRelName.ptr()); dword ex_hash; int bingo_res = ringoGetHash(0, &ex_hash); CORE_HANDLE_ERROR(bingo_res, 1, "reaction search engine: error while getting hash", bingoGetError()); where_clause.printf("ex_hash=%d", ex_hash); what_clause_str.push(0); from_clause_str.push(0); where_clause_str.push(0); } void RingoPgSearchEngine::_prepareSubSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; QS_DEF(Array, search_type); int bingo_res; Array search_query; Array search_options; BingoPgFpData& data = _queryFpData.ref(); if(scan_desc->numberOfKeys != 1) { throw BingoPgError("reaction search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchRSub' or 'matchRSmarts' functions for a secondary condition", scan_desc->numberOfKeys); } BingoPgCommon::getSearchTypeString(_searchType, search_type, false); _getScanQueries(scan_desc->keyData[0].sk_argument, search_query, search_options); /* * Get block parameters and split search options */ _getBlockParameters(search_options); /* * Set up matching parameters */ bingo_res = ringoSetupMatch(search_type.ptr(), search_query.ptr(), search_options.ptr()); CORE_HANDLE_ERROR(bingo_res, 1, "reaction search engine: can not set rsub search context", bingoGetError()); const char* fingerprint_buf; int fp_len; bingo_res = ringoGetQueryFingerprint(&fingerprint_buf, &fp_len); CORE_HANDLE_ERROR(bingo_res, 1, "reaction search engine: can not get query fingerprint", bingoGetError()); int size_bits = fp_len * 8; data.setFingerPrints(fingerprint_buf, size_bits); } void RingoPgSearchEngine::_prepareExactSearch(PG_OBJECT scan_desc_ptr) { IndexScanDesc scan_desc = (IndexScanDesc) scan_desc_ptr; QS_DEF(Array, what_clause); QS_DEF(Array, from_clause); QS_DEF(Array, where_clause); QS_DEF(Array, search_type); Array search_query; Array search_options; int bingo_res; if(scan_desc->numberOfKeys != 1) { throw BingoPgError("reaction search engine: unsupported condition number '%d': " "if you want to search several queries please use 'matchRExact' function for a secondary condition", scan_desc->numberOfKeys); } BingoPgCommon::getSearchTypeString(_searchType, search_type, false); _getScanQueries(scan_desc->keyData[0].sk_argument, search_query, search_options); /* * Set up matching parameters */ bingo_res = ringoSetupMatch(search_type.ptr(), search_query.ptr(), search_options.ptr()); CORE_HANDLE_ERROR(bingo_res, 1, "reaction search engine: can not set rexact search context", bingoGetError()); _prepareExactQueryStrings(what_clause, from_clause, where_clause); _searchCursor.reset(new BingoPgCursor("SELECT %s FROM %s WHERE %s",what_clause.ptr(), from_clause.ptr(), where_clause.ptr())); } void RingoPgSearchEngine::_prepareSmartsSearch(PG_OBJECT scan_desc_ptr) { _prepareSubSearch(scan_desc_ptr); } void RingoPgSearchEngine::_getScanQueries(uintptr_t arg_datum, indigo::Array& str1_out, indigo::Array& str2_out) { /* * Get query info */ BINGO_PG_TRY { HeapTupleHeader query_data = DatumGetHeapTupleHeader(arg_datum); Oid tupType = HeapTupleHeaderGetTypeId(query_data); int32 tupTypmod = HeapTupleHeaderGetTypMod(query_data); TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); int ncolumns = tupdesc->natts; if (ncolumns != 2) elog(ERROR, "internal error: expecting two columns in query but was %d", ncolumns); HeapTupleData tuple; /* * Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(query_data); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = query_data; Datum *values = (Datum *) palloc(ncolumns * sizeof (Datum)); bool *nulls = (bool *) palloc(ncolumns * sizeof (bool)); /* * Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); /* * Query tuple consist of query and options */ BingoPgText str1, str2; str1.init(values[0]); str2.init(values[1]); str1_out.readString(str1.getString(), true); str2_out.readString(str2.getString(), true); pfree(values); pfree(nulls); ReleaseTupleDesc(tupdesc); } BINGO_PG_HANDLE(throw Error("internal error: can not get scan query: %s", message)); } Indigo-indigo-1.2.3/bingo/postgres/src/pg_core/ringo_pg_search_engine.h000066400000000000000000000036561271037650300262130ustar00rootroot00000000000000#ifndef _RINGO_PG_SEARCH_ENGINE_H__ #define _RINGO_PG_SEARCH_ENGINE_H__ #include "bingo_pg_search_engine.h" #include "base_cpp/array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/red_black.h" #include "base_cpp/exception.h" #include "pg_bingo_context.h" #include "bingo_postgres.h" #include "bingo_pg_cursor.h" class BingoPgText; class BingoPgIndex; class BingoPgConfig; class RingoPgFpData : public BingoPgFpData { public: RingoPgFpData():_hash(0) {} virtual ~RingoPgFpData() {} void setHash(dword hash) {_hash = hash;} dword getHash() const {return _hash;} private: RingoPgFpData(const RingoPgFpData&); //no implicit copy dword _hash; }; /* * Class for procession reaction fingerprint data */ class RingoPgSearchEngine : public BingoPgSearchEngine { public: RingoPgSearchEngine(BingoPgConfig& bingo_config, const char* rel_name); virtual ~RingoPgSearchEngine(); virtual bool matchTarget(int section_idx, int structure_idx); virtual bool matchTarget(ItemPointerData& item_data) {return BingoPgSearchEngine::matchTarget(item_data);} virtual int getType() const {return BINGO_INDEX_TYPE_REACTION;} virtual void prepareQuerySearch(BingoPgIndex&, PG_OBJECT scan_desc); virtual bool searchNext(PG_OBJECT result_ptr); DECL_ERROR; private: RingoPgSearchEngine(const RingoPgSearchEngine&); // no implicit copy void _prepareExactQueryStrings(indigo::Array& what_clause, indigo::Array& from_clause, indigo::Array& where_clause); void _prepareSubSearch(PG_OBJECT scan_desc); void _prepareExactSearch(PG_OBJECT scan_desc); void _prepareSmartsSearch(PG_OBJECT scan_desc); void _getScanQueries(uintptr_t arg_datum, indigo::Array& str1, indigo::Array& str2); static void _errorHandler(const char* message, void* context); indigo::Array _relName; indigo::Array _shadowRelName; int _searchType; }; #endif /* RINGO_PG_SEARCH_ENGINE_H */ Indigo-indigo-1.2.3/bingo/sqlserver/000077500000000000000000000000001271037650300173255ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Properties/000077500000000000000000000000001271037650300214615ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Properties/AssemblyInfo.cs000066400000000000000000000020071271037650300244020ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Data.Sql; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Bingo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("Bingo")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010-2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] // // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.7.*")] Indigo-indigo-1.2.3/bingo/sqlserver/Resources/000077500000000000000000000000001271037650300212775ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Resources/OnDeleteTrigger.sql000066400000000000000000000004721271037650300250460ustar00rootroot00000000000000CREATE TRIGGER {0} ON {1} FOR DELETE AS IF OBJECT_ID('tempdb..##bingotmptbl_{5}_{6}') IS NOT NULL BEGIN DROP TABLE ##bingotmptbl_{5}_{6} END select {2} into ##bingotmptbl_{5}_{6} from deleted exec {4}._OnDeleteRecordTrigger {5}, {6}, '##bingotmptbl_{5}_{6}' DROP TABLE ##bingotmptbl_{5}_{6} Indigo-indigo-1.2.3/bingo/sqlserver/Resources/OnInsertTrigger.sql000066400000000000000000000005001271037650300251000ustar00rootroot00000000000000CREATE TRIGGER {0} ON {1} FOR INSERT AS IF OBJECT_ID('tempdb..##bingotmptbl_{5}_{6}') IS NOT NULL BEGIN DROP TABLE ##bingotmptbl_{5}_{6} END select {2}, {3} into ##bingotmptbl_{5}_{6} from inserted exec {4}._OnInsertRecordTrigger {5}, {6}, '##bingotmptbl_{5}_{6}' DROP TABLE ##bingotmptbl_{5}_{6} Indigo-indigo-1.2.3/bingo/sqlserver/Resources/OnUpdateTrigger.sql000066400000000000000000000011141271037650300250600ustar00rootroot00000000000000CREATE TRIGGER {0} ON {1} FOR UPDATE AS IF UPDATE({2}) BEGIN RAISERROR ('{2} column cannot be changed after Bingo index has been created.', 16, 1); ROLLBACK TRANSACTION END IF NOT UPDATE({3}) BEGIN RETURN END IF OBJECT_ID('tempdb..##bingotmptbl_{5}_{6}') IS NOT NULL BEGIN DROP TABLE ##bingotmptbl_{5}_{6} END select {2}, {3} into ##bingotmptbl_{5}_{6} from inserted exec {4}._OnDeleteRecordTrigger {5}, {6}, '##bingotmptbl_{5}_{6}' exec {4}._OnInsertRecordTrigger {5}, {6}, '##bingotmptbl_{5}_{6}' DROP TABLE ##bingotmptbl_{5}_{6} Indigo-indigo-1.2.3/bingo/sqlserver/Resources/Win/000077500000000000000000000000001271037650300220345ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Resources/Win/x64/000077500000000000000000000000001271037650300224555ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Resources/Win/x64/readme.txt000066400000000000000000000000551271037650300244530ustar00rootroot00000000000000DLL files should be placed here automaticallyIndigo-indigo-1.2.3/bingo/sqlserver/Resources/Win/x86/000077500000000000000000000000001271037650300224615ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Resources/Win/x86/readme.txt000066400000000000000000000000551271037650300244570ustar00rootroot00000000000000DLL files should be placed here automaticallyIndigo-indigo-1.2.3/bingo/sqlserver/Source/000077500000000000000000000000001271037650300205655ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/Source/Bingo.cs000066400000000000000000002473411271037650300221650ustar00rootroot00000000000000using System; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Data; using Microsoft.SqlServer.Server; using System.Data.SqlTypes; using System.Data.SqlClient; using System.Collections; using System.IO; using System.Text; using Microsoft.Win32; using System.Threading; using System.Text.RegularExpressions; using indigo.SqlAttributes; namespace indigo { public class Bingo { [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader] public static SqlString GetVersion (SqlString bingo_schema) { return BingoCore.lib.bingoGetVersion(); } private delegate void bingoCallback (); private static SqlInt32 _Match (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema, string search_type, bingoCallback prepare_match, bingoCallback process_matched) { using (BingoSession sessions = new BingoSession()) { ContextFlags flags = 0; if (options.Value.Contains("TAU")) flags |= ContextFlags.TAU_RULES; if (search_type == "SIM") flags |= ContextFlags.FINGERPRINTS; using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, flags); } int res = BingoCore.lib.mangoSetupMatch(search_type, query.Value, options.Value); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); if (prepare_match != null) prepare_match(); res = BingoCore.lib.mangoMatchTarget(target.Value, target.Value.Length); if (res == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (res == -1) { // can not use SqlContext.Pipe from inside the function, // so just returning NULL without printing the error message return SqlInt32.Null; } if (res == 1 && process_matched != null) process_matched(); return new SqlInt32(res); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 Sub (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema) { return _Match(target, query, options, bingo_schema, "SUB", null, null); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 SMARTS (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema) { return _Match(target, query, options, bingo_schema, "SMARTS", null, null); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 Exact (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema) { return _Match(target, query, options, bingo_schema, "EXACT", null, null); } private static string MatchWithHighlighting (SqlBinary target, SqlString query, SqlString parameters, SqlString bingo_schema, string search_type) { string highlighting = null; bingoCallback prepare = () => { BingoCore.lib.mangoSetHightlightingMode(1); }; bingoCallback handle = () => { highlighting = BingoCore.mangoGetHightlightedMolecule(); }; _Match(target, query, parameters, bingo_schema, search_type, prepare, handle); return highlighting; } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlString SubHi (SqlBinary target, SqlString query, SqlString parameters, SqlString bingo_schema) { return MatchWithHighlighting(target, query, parameters, bingo_schema, "SUB"); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlString SMARTSHi (SqlBinary target, SqlString query, SqlString parameters, SqlString bingo_schema) { return MatchWithHighlighting(target, query, parameters, bingo_schema, "SMARTS"); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlSingle Sim (SqlBinary target, SqlString query, SqlString metrics, SqlString bingo_schema) { SqlSingle score = SqlSingle.Null; bingoCallback handler = () => { Single score_value; BingoCore.lib.mangoSimilarityGetScore(out score_value); score = score_value; }; _Match(target, query, metrics, bingo_schema, "SIM", null, handler); return score; } private static SqlInt32 _RMatch (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema, string search_type, bool heed_highlighting, ref string highlighting) { using (BingoSession sessions = new BingoSession()) { ContextFlags flags = 0; if (options.Value.Contains("TAU")) flags |= ContextFlags.TAU_RULES; using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, flags); } int res = BingoCore.lib.ringoSetupMatch(search_type, query.Value, options.Value); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); if (heed_highlighting) BingoCore.lib.ringoSetHightlightingMode(1); res = BingoCore.lib.ringoMatchTarget(target.Value, target.Value.Length); if (res == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (res == -1) { // can not use SqlContext.Pipe from inside the function, // so just returning NULL without printing the error message return SqlInt32.Null; } if (res == 1 && heed_highlighting) highlighting = BingoCore.ringoGetHightlightedReaction(); return new SqlInt32(res); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 RSub (SqlBinary target, SqlString query, SqlString bingo_schema) { string highlighting = null; return _RMatch(target, query, "", bingo_schema, "RSUB", false, ref highlighting); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlString RSubHi (SqlBinary target, SqlString query, SqlString bingo_schema) { string highlighting = null; _RMatch(target, query, "", bingo_schema, "RSUB", true, ref highlighting); return highlighting; } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 RSMARTS (SqlBinary target, SqlString query, SqlString bingo_schema) { string highlighting = null; return _RMatch(target, query, "", bingo_schema, "RSMARTS", false, ref highlighting); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlString RSMARTSHi (SqlBinary target, SqlString query, SqlString bingo_schema) { string highlighting = null; _RMatch(target, query, "", bingo_schema, "RSMARTS", true, ref highlighting); return highlighting; } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "target")] public static SqlInt32 RExact (SqlBinary target, SqlString query, SqlString options, SqlString bingo_schema) { string highlighting = null; return _RMatch(target, query, options, bingo_schema, "REXACT", false, ref highlighting); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlString AAM (SqlBinary reaction, SqlString options, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return new SqlString(BingoCore.ringoAAM(reaction.Value, options.Value)); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlString CheckReaction (SqlBinary reaction, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } string res = BingoCore.checkReaction(reaction.Value); if (res == null) return SqlString.Null; return new SqlString(res); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString CheckMolecule (SqlBinary molecule, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } string res = BingoCore.checkMolecule(molecule.Value); if (res == null) return SqlString.Null; return new SqlString(res); } private delegate void bingoOperationDelegate (SqlConnection ctx_conn, SqlConnection conn, BingoIndexData index_data); private delegate BingoIndexData bingoGetIndexDataDelegate (SqlConnection ctx_conn, SqlConnection conn, SqlString bingo_schema); [Flags] enum BingoOp { NEED_STAT = 0x01, DROP_ON_EXCEPTION = 0x02, LOAD_TAU_RULES = 0x04, LOAD_CMF = 0x08, NON_CONTEXT_CONN = 0x10, LOCK_INDEX = 0x20 } // Method for executing abstract operation with BingoIndexData private static void _ExecuteBingoOperation(SqlString bingo_schema, bingoOperationDelegate operationDelegate, bingoGetIndexDataDelegate getBingoDataDelegate, BingoOp op_flags) { string table_name = ""; BingoIndexID id = null; SqlConnection ext_conn = null; try { using (SqlConnection ctx_conn = new SqlConnection("context connection=true")) { ctx_conn.Open(); SqlConnection conn = ctx_conn; if ((op_flags & BingoOp.NON_CONTEXT_CONN) != 0) { string conn_string = String.Format( "server={0};integrated security=true;database={1};enlist=false", getServername(ctx_conn), ctx_conn.Database); ext_conn = new SqlConnection(conn_string); ext_conn.Open(); conn = ext_conn; } using (BingoSession session = new BingoSession()) { BingoCore.lib.bingoProfilingReset(false); BingoTimer timer = new BingoTimer("total"); BingoIndexData index_data = getBingoDataDelegate(ctx_conn, conn, bingo_schema); table_name = index_data.id.FullTableName(ctx_conn); id = index_data.id; if (index_data.locked) { BingoLog.logMessage("Attempt to get locked index for the table {0}", table_name); throw new Exception("Chemical index for the table '" + table_name + "' is locked"); } if ((op_flags & BingoOp.LOCK_INDEX) != 0) index_data.locked = true; ContextFlags flags = ContextFlags.NTHREADS | ContextFlags.FINGERPRINTS; if ((op_flags & BingoOp.LOAD_TAU_RULES) != 0) flags |= ContextFlags.TAU_RULES; if ((op_flags & BingoOp.LOAD_CMF) != 0) flags |= ContextFlags.CMF; prepareContext(ctx_conn, bingo_schema.Value, index_data.id.table_id, flags); index_data.syncContextParameters(ctx_conn, bingo_schema.Value); try { operationDelegate(ctx_conn, conn, index_data); } catch (Exception ex) { if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0) Thread.ResetAbort(); BingoLog.logMessage("Exception {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); if ((op_flags & BingoOp.LOCK_INDEX) != 0) index_data.locked = false; if ((op_flags & BingoOp.DROP_ON_EXCEPTION) != 0) BingoIndexData.DropIndexData(conn, bingo_schema.Value, id, false); throw ex; } if ((op_flags & BingoOp.LOCK_INDEX) != 0) index_data.locked = false; timer.end(); if ((op_flags & BingoOp.NEED_STAT) != 0) BingoLog.logMessage("Statistics for table {0}:\n{1}\n", table_name, BingoCore.bingoProfilingGetStatistics(false)); } } } finally { if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0) Thread.ResetAbort(); if (ext_conn != null) ext_conn.Close(); } } // Method for executing abstract operation with BingoIndexData private static void _ExecuteBingoOperationChangeIndex (SqlString bingo_schema, bingoOperationDelegate operationDelegate, bingoGetIndexDataDelegate getBingoDataDelegate, BingoOp flags) { bingoOperationDelegate opWithIndex = (ctx_conn, conn, index_data) => { if (BingoCore.lib.bingoIndexBegin() != 1) throw new Exception(BingoCore.lib.bingoGetError()); try { index_data.fingerprints.init(ctx_conn); index_data.storage.validate(ctx_conn); operationDelegate(ctx_conn, conn, index_data); ContextFlags save_flags = ContextFlags.CMF; saveContext(ctx_conn, bingo_schema.Value, index_data.id.table_id, save_flags); } finally { BingoCore.lib.bingoIndexEnd(); } }; _ExecuteBingoOperation(bingo_schema, opWithIndex, getBingoDataDelegate, flags); } private static void CreateIndex (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema, bool reaction) { bingoOperationDelegate opWithIndex = getInsertRecordsDelegate(table.Value, true, true, true, null); bingoGetIndexDataDelegate createDataDelegate = (ctx_conn, conn, schema) => BingoIndexData.CreateIndexData(getSPID(ctx_conn), conn, schema.Value, table.Value, id_column.Value, data_column.Value, reaction); _ExecuteBingoOperationChangeIndex(bingo_schema, opWithIndex, createDataDelegate, BingoOp.DROP_ON_EXCEPTION | BingoOp.NEED_STAT | BingoOp.NON_CONTEXT_CONN | BingoOp.LOCK_INDEX); } // Parameters: // table: // Table name for selecting records // existing_cursor_name // Existing cursor name for selecting records // Remarks: // Only table or existing_cursor_name must be non null // private static bingoOperationDelegate getInsertRecordsDelegate (string table, bool insert_records, bool flush_and_create_index, bool create_cursor, ArrayList error_list) { UTF8Encoding encoding = new UTF8Encoding(); bingoOperationDelegate opWithIndex = (ctx_conn, conn, data) => { // Process each molecule // If ThreadAbortException occurs then Thread.ResetAbort() is // called and indexing is terminated String select_command = String.Format("SELECT {0}, {1} FROM {2}", data.id_column, data.data_column, table); using (SqlCommand command = new SqlCommand(select_command, ctx_conn)) { command.CommandTimeout = 3600 * 10; using (SqlDataReader cursor = command.ExecuteReader()) { Exception exception = null; int counter = 0; BingoCore.GetNextRecordHandler get_next_record = (IntPtr context) => { int? id = null; try { if (exception != null) return 0; while (cursor.Read()) { if (cursor[0] == DBNull.Value) { string message = String.Format("Record with id=null was skipped."); if (SqlContext.Pipe != null) SqlContext.Pipe.Send(message); BingoLog.logMessage(message); continue; } id = Convert.ToInt32(cursor[0]); if (cursor[1] == DBNull.Value) { string message = String.Format("Record with id={0} has null data. Skipped.", id); if (SqlContext.Pipe != null) SqlContext.Pipe.Send(message); BingoLog.logMessage(message); if (error_list != null) error_list.Add(new FetchedData(id.Value) { str = "null data" }); continue; } counter++; if (counter % 10000 == 0) { BingoLog.logMessage("Processing record #{0} with id = {1}", counter, id); } byte[] record_data; if (cursor[1].GetType() == typeof(byte[])) record_data = (byte[])cursor[1]; else record_data = encoding.GetBytes((string)cursor[1]); BingoCore.lib.bingoSetIndexRecordData(id.Value, record_data, record_data.Length); return 1; } // No records more return 0; } catch (Exception ex) { if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0) Thread.ResetAbort(); if (id.HasValue) BingoLog.logMessage("Failed on id = {0}", id); else BingoLog.logMessage("Exception {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); BingoCore.lib.bingoIndexMarkTermintate(); exception = ex; return 0; } }; BingoCore.ProcessResultHandler process_result = (IntPtr context) => { try { if (exception != null) return; if (insert_records) { BingoTimer add_timer = new BingoTimer("index.add_to_index"); if (data.getIndexType() == BingoIndexData.IndexType.Molecule) _AddMoleculeToIndex(conn, data); else _AddReactionToIndex(conn, data); add_timer.end(); } } catch (Exception ex) { BingoLog.logMessage("Exception {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); BingoCore.lib.bingoIndexMarkTermintate(); if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0) Thread.ResetAbort(); exception = ex; } }; BingoCore.ProcessErrorHandler process_error = (int id_with_error, IntPtr context) => { try { if (exception != null) return; string message = String.Format("Record with ID={0} wasn't added to the index: {1}", id_with_error, BingoCore.lib.bingoGetWarning()); if (SqlContext.Pipe != null) SqlContext.Pipe.Send(message); BingoLog.logMessage(message); if (error_list != null) error_list.Add( new FetchedData(id_with_error) { str = BingoCore.lib.bingoGetWarning() }); } catch (Exception ex) { BingoLog.logMessage("Exception {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); BingoCore.lib.bingoIndexMarkTermintate(); if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0) Thread.ResetAbort(); exception = ex; } }; try { int ret = BingoCore.lib.bingoIndexProcess( data.getIndexType() == BingoIndexData.IndexType.Reaction, get_next_record, process_result, process_error, IntPtr.Zero); if (ret == -1) { string msg = BingoCore.lib.bingoGetError(); BingoLog.logMessage("Internal exception: {0}", BingoCore.lib.bingoGetError()); throw new Exception(msg); } } catch (Exception ex) { // Terminate parallel indexing because it causes unhandled exception if not terminated // Index termination should be here because function pointers must be valid BingoCore.lib.bingoIndexEnd(); throw ex; } if (exception != null) throw exception; } } if (flush_and_create_index) { data.flush(conn); BingoTimer indices_timer = new BingoTimer("index.create_indices"); data.createIndices(conn); indices_timer.end(); BingoTimer fp_indices_timer = new BingoTimer("index.create_fp_indices"); data.fingerprints.createIndices(conn); fp_indices_timer.end(); data.CreateTriggers(conn); } }; return opWithIndex; } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void CreateMoleculeIndex (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema) { CreateIndex(table, id_column, data_column, bingo_schema, false); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void SetKeepCache (SqlString table, SqlBoolean value, SqlString bingo_schema) { bingoGetIndexDataDelegate getDataDelegate = (ctx_conn, conn, schema) => BingoIndexData.GetIndexData(conn, bingo_schema.Value, table.Value, getSPID(ctx_conn)); bingoOperationDelegate setOperation = (ctx_conn, conn, index_data) => { index_data.setKeepCache(conn, bingo_schema.Value, value.Value); }; _ExecuteBingoOperation(bingo_schema, setOperation, getDataDelegate, 0); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void CreateReactionIndex (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema) { CreateIndex(table, id_column, data_column, bingo_schema, true); } private static bool _AddMoleculeToIndex (SqlConnection conn, BingoIndexData bingo_data) { MangoIndexData data = (MangoIndexData)bingo_data; MangoIndex index = new MangoIndex(); int id; if (!index.readPrepared(out id)) { string message = String.Format("Molecule with ID={0} wasn't added to the index: {1}", id, BingoCore.lib.bingoGetWarning()); SqlContext.Pipe.Send(message); BingoLog.logMessage(message); return false; } // 4 bytes for Id, 2 bytes for similarity fingerprints bits counts byte[] new_data = new byte[index.cmf.Length + 4 + 2]; index.cmf.CopyTo(new_data, 6); MemoryStream stream = new MemoryStream(new_data, 0, 6, true); BinaryWriter writer = new BinaryWriter(stream); writer.Write(id); writer.Write((short)index.sim_fp_bits_count); int storage_id = data.storage.add(new_data, conn); data.fingerprints.addFingerprint(conn, index.fingerprint, storage_id); data.addToShadowTable(conn, index, id, storage_id); return true; } private static bool _AddReactionToIndex (SqlConnection conn, BingoIndexData bingo_data) { RingoIndexData data = (RingoIndexData)bingo_data; int id; RingoIndex index = new RingoIndex(); if (!index.readPrepared(out id)) { string message = String.Format("Reaction with ID={0} wasn't added to the index: {1}", id, BingoCore.lib.bingoGetWarning()); SqlContext.Pipe.Send(message); BingoLog.logMessage(message); return false; } byte[] new_data = new byte[index.crf.Length + 4]; index.crf.CopyTo(new_data, 4); MemoryStream stream = new MemoryStream(new_data, 0, 4, true); BinaryWriter writer = new BinaryWriter(stream); writer.Write(id); int storage_id = data.storage.add(new_data, conn); data.fingerprints.addFingerprint(conn, index.fingerprint, storage_id); data.addToShadowTable(conn, index, id, storage_id); return false; } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void DropIndex (SqlString table, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); BingoIndexData.DropIndexData(conn, bingo_schema.Value, table.Value, true); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void _DropIndexByID (SqlInt32 table_id, SqlInt32 database_id, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); BingoIndexID id = new BingoIndexID(table_id.Value, database_id.Value); BingoIndexData.DropIndexData(conn, bingo_schema.Value, id, false); } } public static void FillRowInt (Object obj, out SqlInt32 id) { FetchedData data = (FetchedData)obj; id = new SqlInt32((int)data.id); } public static void FillRowIntString (Object obj, out SqlInt32 id, out SqlString str) { FetchedData data = (FetchedData)obj; id = new SqlInt32(data.id); str = new SqlString(data.str); } public static void FillRowIntFloat (Object obj, out SqlInt32 id, out SqlSingle value) { FetchedData data = (FetchedData)obj; id = new SqlInt32(data.id); value = new SqlSingle(data.value); } private static Dictionary ParseOptions (ref string options) { string specific_options = null; Dictionary result = new Dictionary(); string[] allowed_parameters = { "TOP", "NEXT", "START" }; foreach (string part in options.Split(';')) { string part_trimmed = part.TrimStart(); bool found = false; foreach (string p in allowed_parameters) if (part_trimmed.StartsWith(p, StringComparison.OrdinalIgnoreCase)) { if (result.ContainsKey(p)) throw new Exception(String.Format("Only one specification of '{0}' option is allowed", p)); result.Add(p, part_trimmed.Substring(p.Length).Trim()); found = true; break; } if (!found) { if (specific_options != null) throw new Exception(String.Format("Search-specific option was specified twice: '{0}' and '{1}'", specific_options, part_trimmed)); specific_options = part_trimmed; } } options = specific_options; return result; } private static IEnumerable _MakeSearch (SqlString query, SqlString table, SqlString options, SqlString bingo_schema, string search_type, bool highlighting, params object[] ext_options) { BingoLog.logMessage("{0} query started for {1} table", search_type, table.Value); ArrayList res_list = new ArrayList(); BingoOp op_flags = BingoOp.NEED_STAT | BingoOp.LOAD_CMF; if (search_type != "SIM") { if (options.Value.Contains("TAU")) op_flags |= BingoOp.LOAD_TAU_RULES; } bingoGetIndexDataDelegate getDataDelegate = (ctx_conn, conn, schema) => BingoIndexData.GetIndexData(conn, bingo_schema.Value, table.Value, getSPID(ctx_conn)); bingoOperationDelegate searchOperation = (ctx_conn, conn, index_data) => { if (index_data.needFlush()) throw new Exception("Index has been changed. FlushOperations must be called before search"); string options_str = options.Value; Dictionary common_options = ParseOptions(ref options_str); int max_count = -1; if (common_options.ContainsKey("TOP")) { max_count = Convert.ToInt32(common_options["TOP"]); if (max_count < 0) throw new Exception("Limit for 'TOP' option cannot be negative"); } int? next_from = null; if (common_options.ContainsKey("NEXT")) next_from = Convert.ToInt32(common_options["NEXT"]); if (common_options.ContainsKey("START")) next_from = -1; IEnumerable fetched; fetched = _Fetch(query, search_type, highlighting, ext_options, conn, index_data, options_str, next_from); foreach (FetchedData id in fetched) { res_list.Add(id); if (res_list.Count == max_count) break; } }; _ExecuteBingoOperation(bingo_schema, searchOperation, getDataDelegate, op_flags); return res_list; } private static IEnumerable _Fetch (SqlString query, string search_type, bool highlighting, object[] ext_options, SqlConnection conn, BingoIndexData index_data, string options_str, int? id_next_from) { int? storage_id_next_from = null; if (id_next_from.HasValue) { // -1 means start of the iterations if (id_next_from.Value != -1) { storage_id_next_from = index_data.getStorageIdById(conn, id_next_from.Value); if (!storage_id_next_from.HasValue) throw new Exception(String.Format("Cannot find record with id={0}", id_next_from)); } else storage_id_next_from = -1; } IEnumerable fetched; if (search_type == "SUB" || search_type == "SMARTS") { MangoFastIndexFetch fetch_sub = new MangoFastIndexFetch((MangoIndexData)index_data); fetch_sub.prepareSub(query.Value, options_str, highlighting, search_type == "SMARTS"); fetch_sub.nextAfterStorageId = storage_id_next_from; fetched = fetch_sub.fetch(conn); } else if (search_type == "EXACT") { MangoShadowFetch fetch_exact = new MangoShadowFetch((MangoIndexData)index_data); fetch_exact.prepareExact(query.Value, options_str); fetch_exact.nextAfterStorageId = storage_id_next_from; fetched = fetch_exact.fetch(conn); } else if (search_type == "SIM") { MangoFastIndexFetch fetch_sim = new MangoFastIndexFetch((MangoIndexData)index_data); float min = (float)ext_options[0]; float max = (float)ext_options[1]; fetch_sim.prepareSimilarity(query.Value, options_str, min, max); fetch_sim.nextAfterStorageId = storage_id_next_from; fetched = fetch_sim.fetch(conn); } else if (search_type == "MASS") { MangoShadowFetch fetch_mass = new MangoShadowFetch((MangoIndexData)index_data); float? min = (float?)ext_options[0]; float? max = (float?)ext_options[1]; fetch_mass.prepareMass(min, max); fetch_mass.nextAfterStorageId = storage_id_next_from; fetched = fetch_mass.fetch(conn); } else if (search_type == "GROSS") { MangoShadowFetch fetch_gross = new MangoShadowFetch((MangoIndexData)index_data); fetch_gross.prepareGross(query.Value); fetch_gross.nextAfterStorageId = storage_id_next_from; fetched = fetch_gross.fetch(conn); } else if (search_type == "RSUB" || search_type == "RSMARTS") { RingoFastIndexFetch fetch_sub = new RingoFastIndexFetch((RingoIndexData)index_data); fetch_sub.prepareSub(query.Value, options_str, highlighting, search_type == "RSMARTS"); fetch_sub.nextAfterStorageId = storage_id_next_from; fetched = fetch_sub.fetch(conn); } else if (search_type == "REXACT") { RingoShadowFetch fetch_exact = new RingoShadowFetch((RingoIndexData)index_data); fetch_exact.prepareExact(query.Value, options_str); fetch_exact.nextAfterStorageId = storage_id_next_from; fetched = fetch_exact.fetch(conn); } else throw new Exception("Unknown search type: " + search_type); return fetched; } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchSub (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "SUB", false); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, highlighting nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable SearchSubHi (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "SUB", true); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchSMARTS (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "SMARTS", false); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, highlighting nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable SearchSMARTSHi (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "SMARTS", true); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchRSub (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "RSUB", false); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, highlighting nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable SearchRSubHi (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "RSUB", true); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchRSMARTS (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "RSMARTS", false); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, highlighting nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable SearchRSMARTSHi (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "RSMARTS", true); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchExact (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "EXACT", false); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchRExact (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "REXACT", false); } [SqlFunction(FillRowMethodName = "FillRowInt", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int")] [BingoSqlFunctionForReader] public static IEnumerable SearchGross (SqlString table, SqlString query, SqlString options, SqlString bingo_schema) { return _MakeSearch(query, table, options, bingo_schema, "GROSS", false); } [SqlFunction(FillRowMethodName = "FillRowIntFloat", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, similarity real")] [BingoSqlFunctionForReader] public static IEnumerable SearchSim (SqlString table, SqlString query, SqlString metric, SqlString bingo_schema, SqlDouble min_bound, SqlDouble max_bound) { float min = -0.1f, max = 1.1f; if (!min_bound.IsNull) min = (float)min_bound.Value; if (!max_bound.IsNull) max = (float)max_bound.Value; return _MakeSearch(query, table, metric, bingo_schema, "SIM", false, min, max); } [SqlFunction(FillRowMethodName = "FillRowIntFloat", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, weight real")] [BingoSqlFunctionForReader] public static IEnumerable SearchMolecularWeight (SqlString table, SqlDouble min_bound, SqlDouble max_bound, SqlString options, SqlString bingo_schema) { float? min = null, max = null; if (!min_bound.IsNull) min = (float)min_bound.Value; if (!max_bound.IsNull) max = (float)max_bound.Value; return _MakeSearch(null, table, options.Value, bingo_schema, "MASS", false, min, max); } private delegate bool bingoImportPopulateDataRow (DataRow row, List parameters); private static void _ImportData (string table_name, string data_column_name, string additional_parameters, bingoImportPopulateDataRow populateRowFunc) { using (SqlConnection ctx_conn = new SqlConnection("context connection=true")) { ctx_conn.Open(); List parameters = new List(); string[] params_array = additional_parameters.Split(new char[] {',', ';'}); foreach (string p in params_array) { if (p != "") { string[] name_and_column = p.Trim().Split(' '); parameters.Add(name_and_column); } } DataTable dt = new DataTable(); dt.Columns.Add(new DataColumn(data_column_name, Type.GetType("System.String"))); foreach (string[] p in parameters) dt.Columns.Add(new DataColumn(p[1], Type.GetType("System.String"))); using (BingoSession session = new BingoSession()) { BingoCore.lib.bingoProfilingReset(true); BingoTimer timer = new BingoTimer("total"); SqlConnection ext_conn = null; try { string conn_string = String.Format( "server={0};integrated security=true;database={1};enlist=false", getServername(ctx_conn), ctx_conn.Database); ext_conn = new SqlConnection(conn_string); ext_conn.Open(); int imported_count = 0; bool has_data = false; do { DataRow new_row = dt.NewRow(); has_data = populateRowFunc(new_row, parameters); if (has_data) { dt.Rows.Add(new_row); imported_count++; } if (dt.Rows.Count >= 10000 || !has_data) { // Flush data table via SqlBulkCopy BingoTimer timer_sql = new BingoTimer("import.sql_bulk_copy"); using (SqlTransaction transaction = ext_conn.BeginTransaction()) { using (SqlBulkCopy bulkCopy = new SqlBulkCopy(ext_conn, SqlBulkCopyOptions.FireTriggers, transaction)) { bulkCopy.DestinationTableName = table_name; bulkCopy.ColumnMappings.Add(data_column_name, data_column_name); foreach (string[] p in parameters) bulkCopy.ColumnMappings.Add(p[1], p[1]); bulkCopy.BatchSize = dt.Rows.Count; bulkCopy.BulkCopyTimeout = 3600; bulkCopy.WriteToServer(dt); } transaction.Commit(); } timer_sql.end(); BingoCore.lib.bingoProfIncCounter("import.sql_bulk_copy_size", dt.Rows.Count); dt.Rows.Clear(); BingoLog.logMessage(" {0} molecules imported", imported_count); BingoLog.logMessage("Intermediate statistics for import into table {0}:\n{1}\n", table_name, BingoCore.bingoProfilingGetStatistics(true)); BingoCore.lib.bingoProfilingReset(false); } } while (has_data); BingoLog.logMessage(" Done."); timer.end(); BingoLog.logMessage("Statistics for import into table {0}:\n{1}\n", table_name, BingoCore.bingoProfilingGetStatistics(true)); } catch (Exception ex) { BingoLog.logMessage("Exception {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); throw; } finally { if (ext_conn != null) ext_conn.Close(); // Close import BingoCore.lib.bingoSDFImportClose(); BingoCore.lib.bingoRDFImportClose(); BingoCore.lib.bingoSMILESImportClose(); } } } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void ImportSDF(SqlString table_name, SqlString mol_column_name, SqlString file_name, SqlString additional_parameters, SqlString bingo_schema) { bool reader_opened = false; bingoImportPopulateDataRow populateRowFunc = (row, parameters) => { if (!reader_opened) { if (BingoCore.lib.bingoSDFImportOpen(file_name.Value) != 1) throw new Exception(BingoCore.lib.bingoGetError()); reader_opened = true; } if (BingoCore.lib.bingoSDFImportEOF() != 0) return false; BingoTimer timer_mol = new BingoTimer("import.read_mol"); row[mol_column_name.Value] = BingoCore.bingoSDFImportGetNext(); timer_mol.end(); foreach (string[] p in parameters) row[p[1]] = BingoCore.bingoSDFImportGetParameter(p[0]); return true; }; BingoLog.logMessage("Importing into {0} from {1}", table_name, file_name); _ImportData(table_name.Value, mol_column_name.Value, additional_parameters.Value, populateRowFunc); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void ImportRDF (SqlString table_name, SqlString react_column_name, SqlString file_name, SqlString additional_parameters, SqlString bingo_schema) { bool reader_opened = false; bingoImportPopulateDataRow populateRowFunc = (row, parameters) => { if (!reader_opened) { if (BingoCore.lib.bingoRDFImportOpen(file_name.Value) != 1) throw new Exception(BingoCore.lib.bingoGetError()); reader_opened = true; } if (BingoCore.lib.bingoRDFImportEOF() != 0) return false; BingoTimer timer_mol = new BingoTimer("import.read_mol"); row[react_column_name.Value] = BingoCore.bingoRDFImportGetNext(); timer_mol.end(); foreach (string[] p in parameters) row[p[1]] = BingoCore.bingoRDFImportGetParameter(p[0]); return true; }; BingoLog.logMessage("Importing into {0} from {1}", table_name, file_name); _ImportData(table_name.Value, react_column_name.Value, additional_parameters.Value, populateRowFunc); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void ImportSMILES (SqlString table_name, SqlString mol_column_name, SqlString file_name, SqlString id_column_name, SqlString bingo_schema) { bool have_id_col = !id_column_name.IsNull && id_column_name.Value.Length > 1; bool reader_opened = false; Regex regex1 = new Regex(@"[\s]*[^\s]*[\s]*\|.*\|[\s]+([^\s].*)", RegexOptions.Compiled); Regex regex2 = new Regex(@"[\s]*[^\s]*[\s]+([^\s\|].*)", RegexOptions.Compiled); bingoImportPopulateDataRow populateRowFunc = (row, parameters) => { if (!reader_opened) { if (BingoCore.lib.bingoSMILESImportOpen(file_name.Value) != 1) throw new Exception(BingoCore.lib.bingoGetError()); reader_opened = true; } if (BingoCore.lib.bingoSMILESImportEOF() != 0) return false; String line = BingoCore.lib.bingoSMILESImportGetNext(); row[mol_column_name.Value] = line; if (have_id_col) { Match match = regex1.Match(line); string id_data; if (match != Match.Empty) id_data = match.Groups[1].Value; else { match = regex2.Match(line); if (match != Match.Empty) id_data = match.Groups[1].Value; else id_data = null; } row[id_column_name.Value] = id_data; } return true; }; string additional_parameters = ""; if (have_id_col) additional_parameters = id_column_name.Value + " " + id_column_name.Value; _ImportData(table_name.Value, mol_column_name.Value, additional_parameters, populateRowFunc); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void ExportSDF (SqlString table_name, SqlString mol_column_name, SqlString file_name, SqlString additional_parameters) { FileInfo exported_file_info = new FileInfo(file_name.Value); using (StreamWriter exported_file = exported_file_info.CreateText()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); string parameters_str = additional_parameters.Value; if (parameters_str.Length > 0) parameters_str = ", " + parameters_str; SqlDataReader reader = BingoSqlUtils.ExecReader(conn, "select {0}{1} from {2}", mol_column_name, parameters_str, table_name); using (reader) { while (reader.Read()) { exported_file.WriteLine(reader[0]); for (int i = 1; i < reader.FieldCount; i++) { exported_file.WriteLine("> <{0}>", reader.GetName(i)); exported_file.WriteLine("{0}", reader[i]); exported_file.WriteLine(""); } exported_file.WriteLine("$$$$"); } } } } } [Flags] enum ContextFlags { TAU_RULES = 0x01, FINGERPRINTS = 0x02, CMF = 0x04, NTHREADS = 0x08, }; private static void prepareContext (SqlConnection connection, string bingo_schema, int id, ContextFlags flags) { BingoCore.setContext(id); // Set basic paremeters for every request // TODO: cache settings BingoCore.setConfigInt("treat-x-as-pseudoatom", BingoConfig.getInt(connection, bingo_schema, "treat-x-as-pseudoatom", id)); BingoCore.setConfigInt("ignore-stereocenter-errors", BingoConfig.getInt(connection, bingo_schema, "ignore-stereocenter-errors", id)); BingoCore.setConfigInt("ignore-cistrans-errors", BingoConfig.getInt(connection, bingo_schema, "ignore-cistrans-errors", id)); BingoCore.setConfigInt("allow-non-unique-dearomatization", BingoConfig.getInt(connection, bingo_schema, "allow-non-unique-dearomatization", id)); BingoCore.setConfigInt("zero-unknown-aromatic-hydrogens", BingoConfig.getInt(connection, bingo_schema, "zero-unknown-aromatic-hydrogens", id)); BingoCore.setConfigInt("stereochemistry-bidirectional-mode", BingoConfig.getInt(connection, bingo_schema, "stereochemistry-bidirectional-mode", id)); BingoCore.setConfigInt("stereochemistry-detect-haworth-projection", BingoConfig.getInt(connection, bingo_schema, "stereochemistry-detect-haworth-projection", id)); BingoCore.setConfigInt("ignore-closing-bond-direction-mismatch", BingoConfig.getInt(connection, bingo_schema, "ignore-closing-bond-direction-mismatch", id)); BingoCore.setConfigInt("reject-invalid-structures", BingoConfig.getInt(connection, bingo_schema, "reject-invalid-structures", id)); if ((flags & ContextFlags.NTHREADS) != 0) { BingoCore.setConfigInt("nthreads", BingoConfig.getInt(connection, bingo_schema, "nthreads", id)); } if ((flags & ContextFlags.TAU_RULES) != 0) { ArrayList rules = BingoConfig.getTautomerRules(connection, bingo_schema); BingoCore.clearTautomerRules(); foreach (BingoConfig.TautomerRule rule in rules) { BingoCore.addTautomerRule(rule.n, rule.beg, rule.end); } BingoCore.tautomerRulesReady(); } if ((flags & ContextFlags.FINGERPRINTS) != 0) { BingoCore.setConfigInt("FP_ORD_SIZE", BingoConfig.getInt(connection, bingo_schema, "FP_ORD_SIZE", id)); BingoCore.setConfigInt("FP_ANY_SIZE", BingoConfig.getInt(connection, bingo_schema, "FP_ANY_SIZE", id)); BingoCore.setConfigInt("FP_TAU_SIZE", BingoConfig.getInt(connection, bingo_schema, "FP_TAU_SIZE", id)); BingoCore.setConfigInt("FP_SIM_SIZE", BingoConfig.getInt(connection, bingo_schema, "FP_SIM_SIZE", id)); BingoCore.setConfigInt("SUB_SCREENING_MAX_BITS", BingoConfig.getInt(connection, bingo_schema, "SUB_SCREENING_MAX_BITS", id)); BingoCore.setConfigInt("SIM_SCREENING_PASS_MARK", BingoConfig.getInt(connection, bingo_schema, "SIM_SCREENING_PASS_MARK", id)); } if ((flags & ContextFlags.CMF) != 0) { BingoCore.setConfigBin("cmf-dict", BingoConfig.getBinary(connection, bingo_schema, "cmf-dict", id)); } } private static void saveContext (SqlConnection connection, string bingo_schema, int id, ContextFlags flags) { BingoCore.setContext(id); if ((flags & ContextFlags.CMF) != 0) { BingoConfig.setBinary(connection, bingo_schema, "cmf-dict", id, BingoCore.getConfigBin("cmf-dict")); } } [SqlProcedure] [BingoSqlFunction(access_level = AccessLevelKind.None)] public static void OnSessionClose (SqlString spid_str) { int spid = Convert.ToInt32(spid_str.Value); BingoIndexData.OnSessionClose(spid); } private static int getSPID (SqlConnection conn) { return BingoSqlUtils.ExecIntQuery(conn, "SELECT @@spid AS spid").Value; } private static string getServername (SqlConnection conn) { return BingoSqlUtils.ExecStringQuery(conn, "SELECT @@SERVERNAME AS spid"); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString Smiles (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoSMILES(molecule.Value, false); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlString RSmiles (SqlBinary reaction, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.ringoRSMILES(reaction.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString Molfile (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoMolfile(molecule.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString InChI(SqlBinary molecule, SqlString options, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoInChI(molecule.Value, options.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader] public static SqlString InChIKey(SqlString inchi, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.mangoInChIKey(inchi.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlBinary Fingerprint(SqlBinary molecule, SqlString options, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, ContextFlags.FINGERPRINTS); } return BingoCore.mangoFingerprint(molecule.Value, options.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlBinary RFingerprint(SqlBinary reaction, SqlString options, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, ContextFlags.FINGERPRINTS); } return BingoCore.ringoFingerprint(reaction.Value, options.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString CML (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoCML(molecule.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlString RCML (SqlBinary reaction, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.ringoRCML(reaction.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlString Rxnfile (SqlBinary reaction, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.ringoRxnfile(reaction.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString CanSmiles (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoSMILES(molecule.Value, true); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlBinary CompactMolecule (SqlBinary molecule, SqlBoolean save_xyz, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoICM(molecule.Value, save_xyz.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "reaction")] public static SqlBinary CompactReaction (SqlBinary reaction, SqlBoolean save_xyz, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.ringoICR(reaction.Value, save_xyz.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlSingle Mass (SqlBinary molecule, SqlString type, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } float mass; int ret = BingoCore.lib.mangoMass(molecule.Value, molecule.Value.Length, type.Value, out mass); if (ret != 1) return SqlSingle.Null; return mass; } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString Gross (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, 0); } return BingoCore.mangoGross(molecule.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static SqlString Name(SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.bingoGetNameCore(molecule.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void _OnInsertRecordTrigger (SqlInt32 table_id, SqlInt32 database_id, SqlString tmp_table_name, SqlString bingo_schema) { bingoGetIndexDataDelegate getDataDelegate = (ctx_conn, conn, schema) => BingoIndexData.GetIndexData(conn, bingo_schema.Value, table_id.Value, database_id.Value, getSPID(ctx_conn)); bingoOperationDelegate insertOp = getInsertRecordsDelegate(tmp_table_name.Value, true, false, true, null); _ExecuteBingoOperationChangeIndex(bingo_schema, insertOp, getDataDelegate, BingoOp.LOAD_CMF | BingoOp.NON_CONTEXT_CONN | BingoOp.LOCK_INDEX); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void _OnDeleteRecordTrigger (SqlInt32 table_id, SqlInt32 database_id, SqlString tmp_table_name, SqlString bingo_schema) { bingoGetIndexDataDelegate getDataDelegate = (ctx_conn, conn, schema) => BingoIndexData.GetIndexData(conn, bingo_schema.Value, table_id.Value, database_id.Value, getSPID(ctx_conn)); bingoOperationDelegate deleteOp = (ctx_conn, conn, index_data) => { index_data.prepareForDeleteRecord(conn); String select_command = String.Format("SELECT {0} FROM {1}", index_data.id_column, tmp_table_name.Value); using (SqlCommand command = new SqlCommand(select_command, ctx_conn)) { command.CommandTimeout = 3600 * 10; using (SqlDataReader cursor = command.ExecuteReader()) { while (cursor.Read()) { int id = Convert.ToInt32(cursor[0]); index_data.deleteRecordById(id, conn); } } } }; _ExecuteBingoOperationChangeIndex(bingo_schema, deleteOp, getDataDelegate, BingoOp.LOAD_CMF | BingoOp.LOCK_INDEX | BingoOp.NON_CONTEXT_CONN); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, msg nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable CheckMoleculeTable (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema) { return CheckChemicalTable(table, id_column, data_column, bingo_schema, false); } [SqlFunction(FillRowMethodName = "FillRowIntString", DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read, TableDefinition = "id int, msg nvarchar(max)")] [BingoSqlFunctionForReader] public static IEnumerable CheckReactionTable (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema) { return CheckChemicalTable(table, id_column, data_column, bingo_schema, true); } private static IEnumerable CheckChemicalTable (SqlString table, SqlString id_column, SqlString data_column, SqlString bingo_schema, bool is_reaction) { BingoLog.logMessage("Checking chemical table {0}", table.Value); using (BingoSession session = new BingoSession()) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); prepareContext(conn, bingo_schema.Value, 0, ContextFlags.NTHREADS | ContextFlags.FINGERPRINTS); BingoIndexID id = new BingoIndexID(conn, table.Value); BingoIndexData dummy_data; if (is_reaction) dummy_data = new RingoIndexData(id, id_column.Value, data_column.Value, bingo_schema.Value); else dummy_data = new MangoIndexData(id, id_column.Value, data_column.Value, bingo_schema.Value); ArrayList errors = new ArrayList(); bingoOperationDelegate check_insert_op = getInsertRecordsDelegate(table.Value, false, false, false, errors); if (BingoCore.lib.bingoIndexBegin() != 1) throw new Exception(BingoCore.lib.bingoGetError()); BingoCore.lib.bingoIndexSetSkipFP(true); check_insert_op(conn, conn, dummy_data); BingoCore.lib.bingoIndexEnd(); return errors; } } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void FlushOperations (SqlString table_name, SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); using (SqlConnection ext_conn = new SqlConnection("server=" + getServername(conn) + ";integrated security=true;database=" + conn.Database)) { ext_conn.Open(); using (BingoSession session = new BingoSession()) { BingoIndexData index_data = BingoIndexData.GetIndexData(conn, bingo_schema.Value, table_name.Value, getSPID(conn)); if (index_data.locked) { string msg = String.Format("Chemical index for the table '{0}' is locked.", table_name); BingoLog.logMessage(msg); throw new Exception(msg); } index_data.flush(ext_conn); } } } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction(access_level=AccessLevelKind.None)] public static void _DropAllIndices (SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); using (BingoSession session = new BingoSession()) { BingoIndexData.DropAllIndices(conn, bingo_schema.Value, false); } } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction(access_level = AccessLevelKind.None)] public static void _FlushInAllSessions (SqlString bingo_schema) { BingoIndexData.FlushInAllSessions(-1, false); } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void DropInvalidIndices (SqlString bingo_schema) { using (SqlConnection conn = new SqlConnection("context connection=true")) { conn.Open(); using (BingoSession session = new BingoSession()) { BingoIndexData.DropAllIndices(conn, bingo_schema.Value, true); } } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static int GetAtomCount (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.lib.mangoGetAtomCount(molecule.Value, molecule.Value.Length); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunctionForReader(str_bin = "molecule")] public static int GetBondCount (SqlBinary molecule, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.lib.mangoGetBondCount(molecule.Value, molecule.Value.Length); } } /* * Profiling functions */ [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static SqlString GetStatistics (SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.bingoProfilingGetStatistics(true); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void ResetStatistics (SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { BingoCore.lib.bingoProfilingReset(true); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static float ProfilingGetTime (SqlString counter_name, SqlBoolean whole_session, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.lib.bingoProfilingGetTime(counter_name.Value, whole_session.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static long ProfilingGetValue (SqlString counter_name, SqlBoolean whole_session, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.lib.bingoProfilingGetValue(counter_name.Value, whole_session.Value); } } [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static long ProfilingGetCount (SqlString counter_name, SqlBoolean whole_session, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { return BingoCore.lib.bingoProfilingGetCount(counter_name.Value, whole_session.Value); } } /* * Test functions */ [SqlFunction(DataAccess = DataAccessKind.Read, SystemDataAccess = SystemDataAccessKind.Read)] [BingoSqlFunction] public static void _CheckMemoryAllocate(SqlInt32 dotnet_size_mb, SqlInt32 block_size_mb, SqlInt32 core_size_mb, SqlString bingo_schema) { using (BingoSession session = new BingoSession()) { long block_size = block_size_mb.Value; BingoLog.logMessage("Allocating {0} Mb in .NET by {1}Mb blocks...", dotnet_size_mb.Value, block_size); int sum = 0; List mem_blocks = new List(); long mem_left = dotnet_size_mb.Value; int index = 0; while (mem_left != 0) { long cur_alloc; if (mem_left > block_size) cur_alloc = block_size; else cur_alloc = mem_left; byte[] data = new byte[cur_alloc * 1024 * 1024]; foreach (byte b in data) sum += b; mem_blocks.Add(data); index++; mem_left -= cur_alloc; BingoLog.logMessage(" block {0} allocated, {1}Mb is left to allocate...", index, mem_left); } BingoLog.logMessage("Check sum is {0}", sum); BingoLog.logMessage("Allocating {0} Mb in bingo-core...", core_size_mb.Value); int ret = BingoCore.lib.bingoCheckMemoryAllocate(core_size_mb.Value * 1024 * 1024); if (ret != 1) { BingoLog.logMessage(" Failed: {0}", BingoCore.lib.bingoGetError()); throw new Exception("Failed. See Bingo log for details"); } BingoLog.logMessage(" OK"); BingoLog.logMessage(" Waiting 2 sec..."); Thread.Sleep(2000); BingoCore.lib.bingoCheckMemoryFree(); BingoLog.logMessage(" Done."); SqlContext.Pipe.Send("Done."); } } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoCommon.cs000066400000000000000000000003641271037650300233260ustar00rootroot00000000000000namespace indigo { public class FetchedData { public FetchedData (int id) { this.id = id; } public int id { get; set; } public string str { get; set; } public float value; } }Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoConfig.cs000066400000000000000000000113401271037650300232770ustar00rootroot00000000000000using System; using System.Data; using System.Collections.Generic; using System.Text; using System.Collections; using System.Data.SqlClient; using System.Data.SqlTypes; namespace indigo { class BingoConfig { public class TautomerRule { public int n; public string beg; public string end; } private static object _get (SqlConnection connection, string bingo_schema, string name, int id, string table) { using (SqlCommand cmd = new SqlCommand()) { cmd.Connection = connection; cmd.CommandText = String.Format( @"select value from {0}.{1} where name='{2}' and n in (0, {3}) order by n desc", bingo_schema, table, name, id); cmd.CommandType = CommandType.Text; cmd.CommandTimeout = 3600; object result = cmd.ExecuteScalar(); if (result == null) throw new Exception("Key " + name + " isn't present in the configuration table"); return result; } } private static object _set (SqlConnection connection, string bingo_schema, string name, int id, string table, object value) { _remove(connection, bingo_schema, name, id, table); using (SqlCommand cmd = new SqlCommand()) { cmd.Connection = connection; cmd.CommandTimeout = 3600; cmd.CommandText = String.Format( @"insert into {0}.{1} values({2}, '{3}', @data)", bingo_schema, table, id, name); cmd.CommandType = CommandType.Text; cmd.Parameters.AddWithValue("@data", value); return cmd.ExecuteScalar(); } } public static int getInt (SqlConnection connection, string bingo_schema, string name, int id) { object res = _get(connection, bingo_schema, name, id, "CONFIG"); return Convert.ToInt32(res); } public static double getDouble (SqlConnection connection, string bingo_schema, string name, int id) { object res = _get(connection, bingo_schema, name, id, "CONFIG"); return Convert.ToDouble(res); } public static string getString (SqlConnection connection, string bingo_schema, string name, int id) { object res = _get(connection, bingo_schema, name, id, "CONFIG"); return Convert.ToString(res); } public static byte[] getBinary (SqlConnection connection, string bingo_schema, string name, int id) { object res = _get(connection, bingo_schema, name, id, "CONFIG_BIN"); return (byte[])res; } public static void setBinary (SqlConnection connection, string bingo_schema, string name, int id, byte[] data) { _set(connection, bingo_schema, name, id, "CONFIG_BIN", data); } public static void setInt (SqlConnection connection, string bingo_schema, string name, int id, int data) { remove(connection, bingo_schema, name, id); _set(connection, bingo_schema, name, id, "CONFIG", data); } public static void _remove (SqlConnection connection, string bingo_schema, string name, int id, string table) { BingoSqlUtils.ExecNonQueryNoThrow(connection, "DELETE FROM {0}.{1} where name='{2}' and n={3}", bingo_schema, table, name, id); } public static void remove (SqlConnection connection, string bingo_schema, string name, int id) { _remove(connection, bingo_schema, name, id, "CONFIG"); } public static void removeBinary (SqlConnection connection, string bingo_schema, string name, int id) { _remove(connection, bingo_schema, name, id, "CONFIG_BIN"); } public static ArrayList getTautomerRules (SqlConnection connection, string bingo_schema) { ArrayList result = new ArrayList(); SqlCommand cmd = new SqlCommand(); cmd.Connection = connection; cmd.CommandText = String.Format(@"select id, begg, endd from {0}.TAUTOMER_RULES", bingo_schema); cmd.CommandType = CommandType.Text; cmd.CommandTimeout = 3600; using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { TautomerRule rule = new TautomerRule(); rule.n = Convert.ToInt32(reader[0]); rule.beg = Convert.ToString(reader[1]); rule.end = Convert.ToString(reader[2]); result.Add(rule); } } return result; } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoCore.cs000066400000000000000000000242171271037650300227710ustar00rootroot00000000000000using System; using System.Runtime.InteropServices; using System.Data; using Microsoft.SqlServer.Server; using System.Data.SqlTypes; using System.Data.SqlClient; using System.Collections; using System.IO; using System.Text; using Microsoft.Win32; using com.epam.indigo; namespace indigo { public unsafe class BingoCore { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int GetNextRecordHandler (IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessResultHandler (IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ProcessErrorHandler (int id, IntPtr context); private static BingoCoreLib _lib = null; public static BingoCoreLib lib { get { if (_lib == null) { IndigoDllLoader.Instance.loadLibrary(null, "bingo-core-c.dll", "indigo.resource", false); _lib = IndigoDllLoader.Instance.getInterface("bingo-core-c.dll"); } return _lib; } } public static string bingoGetNameCore (byte[] buffer) { sbyte* res = lib.bingoGetNameCore(buffer, buffer.Length); if ((IntPtr)res == IntPtr.Zero) throw new Exception(lib.bingoGetError()); return new String(res); } public static string mangoGrossGetConditions () { sbyte* res = lib.mangoGrossGetConditions(); if ((IntPtr)res == IntPtr.Zero) throw new Exception(lib.bingoGetError()); return new String(res); } public static string mangoGross (byte[] target_buf) { sbyte* res = lib.mangoGross(target_buf, target_buf.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string checkMolecule (byte[] molecule) { sbyte* res = lib.mangoCheckMolecule(molecule, molecule.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string checkReaction (byte[] reaction) { sbyte* res = lib.ringoCheckReaction(reaction, reaction.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string ringoAAM (byte[] reaction, string options) { sbyte* res = lib.ringoAAM(reaction, reaction.Length, options); if ((IntPtr)res == IntPtr.Zero) throw new Exception(lib.bingoGetError()); return new String(res); } public static string ringoGetHightlightedReaction () { sbyte* res = lib.ringoGetHightlightedReaction(); if ((IntPtr)res == IntPtr.Zero) throw new Exception(lib.bingoGetError()); return new String(res); } public static string mangoGetHightlightedMolecule () { sbyte* res = lib.mangoGetHightlightedMolecule(); if ((IntPtr)res == IntPtr.Zero) throw new Exception(lib.bingoGetError()); return new String(res); } public static string mangoTauGetQueryGross () { return new String(lib.mangoTauGetQueryGross()); } public static string mangoGetCountedElementName (int index) { return new String(lib.mangoGetCountedElementName(index)); } public static string bingoProfilingGetStatistics (bool for_session) { return new String(lib.bingoProfilingGetStatistics(for_session)); } public static string ringoRxnfile (byte[] reaction) { sbyte* res = lib.ringoRxnfile(reaction, reaction.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string ringoRCML (byte[] reaction) { sbyte* res = lib.ringoRCML(reaction, reaction.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string mangoMolfile (byte[] molecule) { sbyte* res = lib.mangoMolfile(molecule, molecule.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string mangoCML (byte[] molecule) { sbyte* res = lib.mangoCML(molecule, molecule.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static byte[] mangoICM (byte[] molecule, bool save_xyz) { int out_len; IntPtr icm_ptr = lib.mangoICM(molecule, molecule.Length, save_xyz, out out_len); if ((IntPtr)icm_ptr == IntPtr.Zero) return null; byte[] icm = new byte[out_len]; Marshal.Copy(icm_ptr, icm, 0, out_len); return icm; } public static byte[] ringoICR (byte[] reaction, bool save_xyz) { int out_len; IntPtr icr_ptr = lib.ringoICR(reaction, reaction.Length, save_xyz, out out_len); if ((IntPtr)icr_ptr == IntPtr.Zero) return null; byte[] icr = new byte[out_len]; Marshal.Copy(icr_ptr, icr, 0, out_len); return icr; } public static string ringoRSMILES (byte[] buffer) { sbyte* res = lib.ringoRSMILES(buffer, buffer.Length); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string mangoSMILES (byte[] buffer, bool canonical) { sbyte* res = lib.mangoSMILES(buffer, buffer.Length, canonical ? 1 : 0); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static byte[] mangoFingerprint(byte[] molecule, string options) { int fp_len; IntPtr fp_ptr = lib.mangoFingerprint(molecule, molecule.Length, options, out fp_len); if (fp_ptr == IntPtr.Zero) return null; byte[] fp = new byte[fp_len]; Marshal.Copy(fp_ptr, fp, 0, fp_len); return fp; } public static byte[] ringoFingerprint(byte[] reaction, string options) { int fp_len; IntPtr fp_ptr = lib.ringoFingerprint(reaction, reaction.Length, options, out fp_len); if (fp_ptr == IntPtr.Zero) return null; byte[] fp = new byte[fp_len]; Marshal.Copy(fp_ptr, fp, 0, fp_len); return fp; } public static string mangoInChI(byte[] molecule, string options) { int out_len; sbyte* res = lib.mangoInChI(molecule, molecule.Length, options, out out_len); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static string mangoInChIKey(string incho) { sbyte* res = lib.mangoInChIKey(incho); if ((IntPtr)res == IntPtr.Zero) return null; return new String(res); } public static int mangoGetQueryFingerprint(out byte[] fp) { IntPtr fp_ptr; int fp_len; int ret = lib.mangoGetQueryFingerprint(out fp_ptr, out fp_len); fp = new byte[fp_len]; Marshal.Copy(fp_ptr, fp, 0, fp_len); return ret; } public static int ringoGetQueryFingerprint (out byte[] fp) { IntPtr fp_ptr; int fp_len; int ret = lib.ringoGetQueryFingerprint(out fp_ptr, out fp_len); fp = new byte[fp_len]; Marshal.Copy(fp_ptr, fp, 0, fp_len); return ret; } public static string bingoRDFImportGetNext () { return new String(lib.bingoRDFImportGetNext()); } public static string bingoRDFImportGetParameter (string param_name) { return new String(lib.bingoRDFImportGetProperty(param_name)); } public static string bingoSDFImportGetNext () { return new String(lib.bingoSDFImportGetNext()); } public static string bingoSDFImportGetParameter (string param_name) { return new String(lib.bingoSDFImportGetProperty(param_name)); } public static void tautomerRulesReady () { if (lib.bingoTautomerRulesReady() == 0) throw new Exception(lib.bingoGetError()); } public static void addTautomerRule (int n, string beg, string end) { if (lib.bingoAddTautomerRule(n, beg, end) == 0) throw new Exception(lib.bingoGetError()); } public static void clearTautomerRules () { if (lib.bingoClearTautomerRules() == 0) throw new Exception(lib.bingoGetError()); } public static byte[] getConfigBin (string name) { IntPtr value_ptr; int value_len; if (lib.bingoGetConfigBin(name, out value_ptr, out value_len) == 0) throw new Exception(lib.bingoGetError()); byte[] data = new byte[value_len]; Marshal.Copy(value_ptr, data, 0, value_len); return data; } public static void setConfigBin (string name, byte[] value) { if (lib.bingoSetConfigBin(name, value, value.Length) == 0) throw new Exception(lib.bingoGetError()); } public static int getConfigInt (string name) { int value; if (lib.bingoGetConfigInt(name, out value) == 0) throw new Exception(lib.bingoGetError()); return value; } public static void setConfigInt (string name, int value) { if (lib.bingoSetConfigInt(name, value) == 0) throw new Exception(lib.bingoGetError()); } public static void setContext (int id) { if (lib.bingoSetContext(id) == 0) throw new Exception(lib.bingoGetError()); } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoCoreLib.cs000066400000000000000000000221211271037650300234100ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace indigo { public unsafe interface BingoCoreLib { String bingoGetVersion (); String bingoGetError (); String bingoGetWarning (); int bingoSetContext (int id); int bingoSetConfigInt ( [MarshalAs(UnmanagedType.LPStr)] string name, int value); int bingoGetConfigInt ( [MarshalAs(UnmanagedType.LPStr)] string name, out int value); int bingoSetConfigBin ( [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPArray)] byte[] value, int len); int bingoGetConfigBin ( [MarshalAs(UnmanagedType.LPStr)] string name, out IntPtr value, out int len); int bingoClearTautomerRules (); int bingoAddTautomerRule (int n, [MarshalAs(UnmanagedType.LPStr)] string beg, [MarshalAs(UnmanagedType.LPStr)] string end); int bingoTautomerRulesReady (); int mangoSetupMatch ( [MarshalAs(UnmanagedType.LPStr)] string search_type, [MarshalAs(UnmanagedType.LPStr)] string query, [MarshalAs(UnmanagedType.LPStr)] string options); int ringoSetupMatch ( [MarshalAs(UnmanagedType.LPStr)] string search_type, [MarshalAs(UnmanagedType.LPStr)] string query, [MarshalAs(UnmanagedType.LPStr)] string options); int mangoMatchTarget ( [MarshalAs(UnmanagedType.LPArray)] byte[] target, int target_buf_len); int mangoMatchTargetBinary ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_cmf, int target_cmf_len, [MarshalAs(UnmanagedType.LPArray)] byte[] target_xyz, int target_xyz_len); int ringoMatchTarget ( [MarshalAs(UnmanagedType.LPArray)] byte[] target, int target_buf_len); int ringoMatchTargetBinary ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_cmf, int target_cmf_len); int bingoSDFImportOpen ( [MarshalAs(UnmanagedType.LPStr)] string filename); int bingoSDFImportClose (); int bingoSDFImportEOF (); sbyte* bingoSDFImportGetNext (); sbyte* bingoSDFImportGetProperty ( [MarshalAs(UnmanagedType.LPStr)] string param_name); int bingoRDFImportOpen ( [MarshalAs(UnmanagedType.LPStr)] string filename); int bingoRDFImportClose (); int bingoRDFImportEOF (); sbyte* bingoRDFImportGetNext (); sbyte* bingoRDFImportGetProperty ( [MarshalAs(UnmanagedType.LPStr)] string param_name); /* Indexing */ int bingoIndexBegin (); int bingoIndexEnd (); int bingoIndexMarkTermintate (); int bingoSetIndexRecordData (int id, byte[] data, int data_size); int bingoIndexProcess ( [MarshalAs(UnmanagedType.I1)] bool is_reaction, BingoCore.GetNextRecordHandler get_next_record, BingoCore.ProcessResultHandler process_result, BingoCore.ProcessErrorHandler process_error, IntPtr context); int mangoIndexReadPreparedMolecule ( out int id, out IntPtr cmf_buf, out int cmf_buf_len, out IntPtr xyz_buf, out int xyz_buf_len, out IntPtr gross_str, out IntPtr counter_elements_str, out IntPtr fingerprint_buf, out int fingerprint_buf_len, out IntPtr fingerprint_sim_str, out float mass, out int sim_fp_bits_count); int ringoIndexReadPreparedReaction ( out int id, out IntPtr crf_buf, out int crf_buf_len, out IntPtr fingerprint_buf, out int fingerprint_buf_len); int bingoIndexSetSkipFP ([MarshalAs(UnmanagedType.I1)] bool skip); int mangoGetHash ( [MarshalAs(UnmanagedType.I1)] bool for_index, int index, out int count, out int hash); int mangoGetQueryFingerprint (out IntPtr query_fp, out int query_fp_len); int ringoGetQueryFingerprint (out IntPtr query_fp, out int query_fp_len); int mangoGetAtomCount ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len); int mangoGetBondCount ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len); sbyte* mangoSMILES ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len, int canonical); sbyte* ringoRSMILES ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len); sbyte* mangoMolfile ( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len); sbyte* mangoCML ( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len); IntPtr mangoICM ( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len, [MarshalAs(UnmanagedType.I1)] bool save_xyz, out int out_len); IntPtr ringoICR ( [MarshalAs(UnmanagedType.LPArray)] byte[] reaction, int reaction_len, [MarshalAs(UnmanagedType.I1)] bool save_xyz, out int out_len); sbyte* ringoRxnfile ( [MarshalAs(UnmanagedType.LPArray)] byte[] reaction, int reaction_len); sbyte* ringoRCML ( [MarshalAs(UnmanagedType.LPArray)] byte[] reaction, int reaction_len); IntPtr mangoFingerprint( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len, [MarshalAs(UnmanagedType.LPStr)] string options, out int out_len); IntPtr ringoFingerprint( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len, [MarshalAs(UnmanagedType.LPStr)] string options, out int out_len); sbyte* mangoInChI( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len, [MarshalAs(UnmanagedType.LPStr)] string options, out int out_len); sbyte* mangoInChIKey([MarshalAs(UnmanagedType.LPStr)] string inchi); sbyte* mangoGetCountedElementName(int index); int mangoNeedCoords (); [return: MarshalAs(UnmanagedType.I1)] bool mangoExactNeedComponentMatching (); sbyte* mangoTauGetQueryGross (); int mangoSimilarityGetBitMinMaxBoundsArray ( int count, [MarshalAs(UnmanagedType.LPArray)] int[] target_ones, out IntPtr min_bound, out IntPtr max_bound); int mangoSimilaritySetMinMaxBounds ( float min_bound, float max_bound); int mangoSimilarityGetScore (out Single score); int mangoSetHightlightingMode (int enable); int mangoLoadTargetBinaryXyz ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_xyz, int target_xyz_len); sbyte* mangoGetHightlightedMolecule (); int ringoSetHightlightingMode (int enable); sbyte* ringoGetHightlightedReaction (); sbyte* ringoAAM ( [MarshalAs(UnmanagedType.LPArray)] byte[] reaction, int reaction_len, [MarshalAs(UnmanagedType.LPStr)] string options); sbyte* ringoCheckReaction ( [MarshalAs(UnmanagedType.LPArray)] byte[] reaction, int reaction_len); sbyte* mangoCheckMolecule ( [MarshalAs(UnmanagedType.LPArray)] byte[] molecule, int molecule_len); sbyte* mangoGross ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len); int mangoMass ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len, [MarshalAs(UnmanagedType.LPStr)] string type, out float mass); sbyte* mangoGrossGetConditions (); int ringoGetHash ([MarshalAs(UnmanagedType.I1)] bool for_index, out int hash); /* Profiling */ void bingoProfilingReset ( [MarshalAs(UnmanagedType.I1)] bool reset_whole_session); sbyte* bingoProfilingGetStatistics ([MarshalAs(UnmanagedType.I1)] bool for_session); float bingoProfilingGetTime ( [MarshalAs(UnmanagedType.LPStr)] string counter_name, [MarshalAs(UnmanagedType.I1)] bool whole_session); long bingoProfilingGetValue ( [MarshalAs(UnmanagedType.LPStr)] string counter_name, [MarshalAs(UnmanagedType.I1)] bool whole_session); long bingoProfilingGetCount ( [MarshalAs(UnmanagedType.LPStr)] string counter_name, [MarshalAs(UnmanagedType.I1)] bool whole_session); UInt64 bingoProfNanoClock (); void bingoProfIncTimer ( [MarshalAs(UnmanagedType.LPStr)] string target_buf, UInt64 dt); void bingoProfIncCounter ( [MarshalAs(UnmanagedType.LPStr)] string target_buf, int value); sbyte* bingoGetNameCore ( [MarshalAs(UnmanagedType.LPArray)] byte[] target_buf, int target_buf_len); int bingoSMILESImportOpen ( [MarshalAs(UnmanagedType.LPStr)] string file_name); int bingoSMILESImportClose (); int bingoSMILESImportEOF (); String bingoSMILESImportGetNext (); /* Test functions */ int bingoCheckMemoryAllocate (int mem); int bingoCheckMemoryFree (); /* Session managing */ [return: MarshalAs(UnmanagedType.U8)] ulong bingoAllocateSessionID (); void bingoReleaseSessionID ([MarshalAs(UnmanagedType.U8)] ulong id); void bingoSetSessionID ([MarshalAs(UnmanagedType.U8)] ulong id); [return: MarshalAs(UnmanagedType.U8)] ulong bingoGetSessionID (); } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoFingerprints.cs000066400000000000000000000615311271037650300245530ustar00rootroot00000000000000using System.IO; using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; namespace indigo { public class BingoFingerprints { private object _sync_object = new Object(); private BingoIndexData _index_data; private int _fp_bytes, _chunk_bytes; private int _fp_sub_bits_used = 10, _sim_screening_pass_mark = 128, _fp_sim_bits_group = 10; class Block { public Block (bool pending, int fp_bytes) { this.pending = pending; counters = new int[fp_bytes * 8]; } public int part = -1; public int minimum_index = int.MaxValue, maximum_index = int.MinValue; public List indices = new List(); public int[] counters; public List bits = null; // If not null then bits are in memory public bool pending = false; public void validateMinMax () { minimum_index = int.MaxValue; maximum_index = int.MinValue; foreach (int v in indices) validateMinMax(v); } public void validateMinMax (int value) { if (minimum_index > value) minimum_index = value; if (maximum_index < value) maximum_index = value; } }; List _all_blocks = new List(); public BingoFingerprints (BingoIndexData index_data) { _index_data = index_data; _fp_bytes = -1; _chunk_bytes = 8000; } public void createTables (SqlConnection conn) { BingoSqlUtils.ExecNonQuery(conn, @"CREATE TABLE {0} ( [part] INTEGER not null, [used] INTEGER not null, [mapping] VARBINARY(MAX), [counters] VARBINARY(MAX))", _index_data.fingerprintsTable); BingoSqlUtils.ExecNonQuery(conn, @"CREATE TABLE {0} ( [part] INTEGER not null, [bit] INTEGER not null, [bits_chunk] BINARY({1}))", _index_data.fingerprintBitsTable, _chunk_bytes); } public void createIndices (SqlConnection conn) { BingoSqlUtils.ExecNonQuery(conn, "ALTER TABLE {0} ADD PRIMARY KEY (part)", _index_data.fingerprintsTable); BingoSqlUtils.ExecNonQuery(conn, "ALTER TABLE {0} ADD PRIMARY KEY (part, bit)", _index_data.fingerprintBitsTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX part ON {0}(part)", _index_data.fingerprintBitsTable); } public void dropTables (SqlConnection conn) { BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE {0}", _index_data.fingerprintsTable); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE {0}", _index_data.fingerprintBitsTable); } public void init (SqlConnection conn) { // Check if blocks have already been loaded if (_all_blocks.Count != 0) return; lock (_sync_object) { // Check again if (_all_blocks.Count != 0) return; int? max_part = BingoSqlUtils.ExecIntQuery(conn, "SELECT MAX(part) from {0}", _index_data.fingerprintsTable); if (!max_part.HasValue) return; using (SqlCommand command = new SqlCommand("SELECT [part], [used], [counters], [mapping] from " + _index_data.fingerprintsTable + " ORDER BY [part]", conn)) { command.CommandTimeout = 3600; using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { Block new_block = new Block(false, _fp_bytes); new_block.part = Convert.ToInt32(reader[0]); int used = Convert.ToInt32(reader[1]); byte[] counters = (byte[])reader[2]; byte[] mapping = (byte[])reader[3]; // Copy mapping int[] data = new int[used]; Buffer.BlockCopy(mapping, 0, data, 0, mapping.Length); new_block.indices.AddRange(data); new_block.validateMinMax(); // Copy counters Buffer.BlockCopy(counters, 0, new_block.counters, 0, counters.Length); _all_blocks.Add(new_block); } } } } } private void _validateBlockIndices (SqlConnection conn, Block block) { if (block.indices != null) return; lock (_sync_object) { if (block.indices != null) return; BingoLog.logMessage("validating fingerprint {0} block indices", block.part); using (SqlCommand command = new SqlCommand("SELECT [mapping], [used] from " + _index_data.fingerprintsTable + " WHERE [part] = " + block.part, conn)) { command.CommandTimeout = 3600; using (SqlDataReader reader = command.ExecuteReader()) { if (!reader.Read()) throw new Exception("Cannot read fingerprint " + block.part + " block"); byte[] mapping = (byte[])reader[0]; int used = Convert.ToInt32(reader[1]); // Copy mapping int[] data = new int[used]; Buffer.BlockCopy(mapping, 0, data, 0, mapping.Length); block.indices = new List(); block.indices.AddRange(data); block.validateMinMax(); } } } } private void _allocatePendingBlock (SqlConnection conn) { Block block = null; if (_all_blocks.Count == 0) { block = new Block(false, _fp_bytes); _all_blocks.Add(block); } if (block == null) { Block last = _all_blocks[_all_blocks.Count - 1]; _validateBlockIndices(conn, last); if (last.indices.Count == _chunk_bytes * 8) { flush(conn); block = new Block(false, _fp_bytes); _all_blocks.Add(block); } else block = last; } if (block.pending) return; _readBlockBits(ref block, conn); block.pending = true; } private void _readBlockBits (ref Block block, SqlConnection conn) { if (block.bits != null) return; // Bits have already been loaded BingoLog.logMessage("_readBlockBits: allocating block {1} buffer {0} size...", 8 * _fp_bytes * _chunk_bytes, _all_blocks.Count); block.bits = new List(8 * _fp_bytes); for (int i = 0; i < 8 * _fp_bytes; i++) block.bits.Add(new byte[_chunk_bytes]); BingoLog.logMessage(" Done."); string command_text = String.Format("SELECT bit, bits_chunk from {0} where part = {1}", _index_data.fingerprintBitsTable, block.part); using (SqlCommand command = new SqlCommand(command_text, conn)) { command.CommandTimeout = 3600; using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { int bit = (int)reader[0]; byte[] bit_chunk = (byte[])reader[1]; if (bit_chunk.Length != _chunk_bytes) throw new Exception("Bits block length is incorrect"); bit_chunk.CopyTo(block.bits[bit], 0); } } } } public void addFingerprint (SqlConnection conn, byte[] fp, int id) { lock (_sync_object) { _allocatePendingBlock(conn); Block last = _all_blocks[_all_blocks.Count - 1]; last = _all_blocks[_all_blocks.Count - 1]; _validateBlockIndices(conn, last); int index = last.indices.Count; last.indices.Add(id); last.validateMinMax(id); for (int i = 0; i < _fp_bytes; i++) { byte cur_byte = fp[i]; for (int j = 0; j < 8; j++) { int bit = 8 * i + j; //int offset_chunk = () * _chunk_bytes; int offset_bytes = index / 8; int offset_bit = index % 8; if ((cur_byte & (1 << j)) == 0) { last.bits[bit][offset_bytes] &= (byte)(~(1 << offset_bit)); } else { last.counters[bit]++; last.bits[bit][offset_bytes] |= (byte)(1 << offset_bit); } } } } } public bool needFlush() { lock (_sync_object) { foreach (Block b in _all_blocks) { if (!b.pending || b.indices == null || b.indices.Count == 0) continue; return true; } return false; } } public void flush (SqlConnection conn) { lock (_sync_object) { foreach (Block b in _all_blocks) { if (!b.pending || b.indices == null || b.indices.Count == 0) continue; _flushBlock(b, conn); } } } private void _flushBlock (Block block, SqlConnection conn) { BingoTimer timer = new BingoTimer("fingerprints.flush"); if (block.part == -1) { // Add new block int? max_id = BingoSqlUtils.ExecIntQuery(conn, "SELECT MAX(part) from {0}", _index_data.fingerprintsTable); if (max_id == null) max_id = 0; block.part = max_id.Value + 1; BingoSqlUtils.ExecNonQuery(conn, "INSERT INTO {0} values ({1}, 0, null, null)", _index_data.fingerprintsTable, block.part); } BingoLog.logMessage("Flushing fingerprints block {0}...", block.part); // Update used column and counters column string update_command_text = String.Format(@"UPDATE {0} SET used = @used, counters = @counters, mapping = @mapping where part = {1}", _index_data.fingerprintsTable, block.part); using (SqlCommand command = new SqlCommand(update_command_text, conn)) { command.CommandTimeout = 3600; command.Parameters.AddWithValue("@used", block.indices.Count); byte[] countes_bytes = new byte[8 * _fp_bytes * sizeof(int)]; Buffer.BlockCopy(block.counters, 0, countes_bytes, 0, countes_bytes.Length); SqlBinary countes = new SqlBinary(countes_bytes); command.Parameters.AddWithValue("@counters", countes); byte[] mapping_bytes = new byte[block.indices.Count * sizeof(int)]; Buffer.BlockCopy(block.indices.ToArray(), 0, mapping_bytes, 0, mapping_bytes.Length); SqlBinary mapping = new SqlBinary(mapping_bytes); command.Parameters.AddWithValue("@mapping", mapping); command.ExecuteNonQuery(); } // Update bit chunks BingoSqlUtils.ExecNonQuery(conn, "DELETE FROM {0} WHERE part = {1}", _index_data.fingerprintBitsTable, block.part); string update_bits_text = String.Format(@"INSERT INTO {0} VALUES ({1}, @bit, @bit_chunk)", _index_data.fingerprintBitsTable, block.part); using (SqlCommand command = new SqlCommand(update_bits_text, conn)) { command.CommandTimeout = 3600; command.Parameters.Add("@bit", SqlDbType.Int); command.Parameters.Add("@bit_chunk", SqlDbType.Binary); byte[] chunk = new byte[_chunk_bytes]; for (int i = 0; i < 8 * _fp_bytes; i++) { command.Parameters["@bit"].Value = i; Buffer.BlockCopy(block.bits[i], 0, chunk, 0, chunk.Length); SqlBinary sql_chunk = new SqlBinary(chunk); command.Parameters["@bit_chunk"].Value = sql_chunk; command.ExecuteNonQuery(); } } block.pending = false; block.bits = null; block.indices = null; BingoLog.logMessage(" Done."); timer.end(); } public bool ableToScreen (byte[] fp) { foreach (byte b in fp) if (b != 0) return true; return false; } public IEnumerable screenSub (SqlConnection conn, byte[] fp, int? next_after_storate_id) { screenInBlockDelegate screenBlockSub = new screenInBlockDelegate( (List fp_ones, Block block, byte[] chunk, byte[] chunk2, SqlConnection conn2) => _screenInBlockSub(fp_ones, block, chunk, chunk2, conn2, next_after_storate_id)); return _screen(conn, fp, next_after_storate_id, screenBlockSub); } // Get bounds on the number of bits public delegate void getBoundsDelegate (IList storage_id, ref int[] min_common_ones, ref int[] max_common_ones, SqlConnection conn); public IEnumerable screenSim (SqlConnection conn, byte[] fp, int? next_after_storate_id, getBoundsDelegate boundsDelegate) { screenInBlockDelegate screenBlockSim = new screenInBlockDelegate( (List fp_ones, Block block, byte[] chunk, byte[] chunk2, SqlConnection conn2) => _screenInBlockSim(fp_ones, block, chunk, chunk2, conn2, boundsDelegate, next_after_storate_id)); return _screen(conn, fp, next_after_storate_id, screenBlockSim); } // Delegate for screening in block private delegate List screenInBlockDelegate (List fp_ones, Block block, byte[] chunk, byte[] chunk2, SqlConnection conn); private IEnumerable _screen (SqlConnection conn, byte[] fp, int? next_after_storate_id, screenInBlockDelegate screenInBlockFunc) { // Find ones List fp_ones = new List(); for (int i = 0; i < _fp_bytes; i++) { byte b = fp[i]; for (int j = 0; j < 8; j++) if ((b & (1 << j)) != 0) fp_ones.Add(8 * i + j); } byte[] working_chunk1 = new byte[_chunk_bytes]; byte[] working_chunk2 = new byte[_chunk_bytes]; // Screen foreach (Block block in _all_blocks) { List results = null; _validateBlockIndices(conn, block); results = screenInBlockFunc(fp_ones, block, working_chunk1, working_chunk2, conn); foreach (int index in results) yield return index; } } private void _getBitChunk (Block block, int bit_index, SqlConnection conn, ref byte[] chunk) { BingoTimer timer = new BingoTimer("fingerprints.read"); if (block.bits != null) Buffer.BlockCopy(block.bits[bit_index], 0, chunk, 0, chunk.Length); else { chunk = (byte[])BingoSqlUtils.ExecObjQuery(conn, "SELECT bits_chunk from {0} where part = {1} and bit = {2}", _index_data.fingerprintBitsTable, block.part, bit_index); } timer.end(); } private List _screenInBlockSub (List fp_ones, Block block, byte[] chunk, byte[] chunk2, SqlConnection conn, int? next_after_storate_id) { List results = new List(); if (next_after_storate_id.HasValue && block.maximum_index < next_after_storate_id.Value) return results; int min_storate_id_bound = -1; if (next_after_storate_id.HasValue) min_storate_id_bound = next_after_storate_id.Value; // Sort ones fp_ones.Sort( (i1, i2) => block.counters[i1].CompareTo(block.counters[i2])); if (fp_ones.Count == 0) throw new Exception("Internal error: ableToScreen wasn't checked"); BingoTimer timer = new BingoTimer("fingerprints.screening_sub"); BingoCore.lib.bingoProfIncCounter("fingerprints.bits_total", fp_ones.Count); List fp_ones_used = new List(); for (int i = 0; i < _fp_sub_bits_used; i++) { if (i >= fp_ones.Count) break; fp_ones_used.Add(fp_ones[i]); } int iteration = 0; foreach (BitChunk bit_chunk in bitChunksReaderGrouped(conn, block, fp_ones_used, _fp_sub_bits_used)) { if (iteration == 0) { bit_chunk.chunk.CopyTo(chunk, 0); iteration++; continue; } else bit_chunk.chunk.CopyTo(chunk2, 0); iteration++; bool has_nonzero = false; for (int i = 0; i < chunk.Length; i++) { chunk[i] &= chunk2[i]; if (chunk[i] != 0) has_nonzero = true; } if (!has_nonzero) break; } BingoCore.lib.bingoProfIncCounter("fingerprints.bits_used", iteration); int max_byte_index = (block.indices.Count + 7) / 8; for (int i = 0; i < max_byte_index; i++) { byte b = chunk[i]; if (b == 0) continue; for (int j = 0; j < 8; j++) if ((b & (1 << j)) != 0) { int id = block.indices[8 * i + j]; if (id <= min_storate_id_bound) continue; results.Add(id); } } timer.end(); return results; } private List _screenInBlockSim (List fp_ones, Block block, byte[] chunk, byte[] chunk2, SqlConnection conn, getBoundsDelegate boundsDelegate, int? next_after_storate_id) { List passed_screening = new List(); if (next_after_storate_id.HasValue && block.maximum_index < next_after_storate_id.Value) return passed_screening; int min_storate_id_bound = -1; if (next_after_storate_id.HasValue) min_storate_id_bound = next_after_storate_id.Value; BingoTimer timer = new BingoTimer("fingerprints.screening_sim"); int[] max_common_ones = new int[block.indices.Count]; int[] min_common_ones = new int[block.indices.Count]; int[] one_counters = new int[block.indices.Count]; // Calculate max and min bounds BingoTimer timer2 = new BingoTimer("fingerprints.screening_bounds"); boundsDelegate(block.indices, ref min_common_ones, ref max_common_ones, conn); timer2.end(); List passed_screening_tmp = new List(); BingoCore.lib.bingoProfIncCounter("fingerprints.bits_total", fp_ones.Count); if (fp_ones.Count == 0) { for (int i = 0; i < block.indices.Count; i++) { if (block.indices[i] <= min_storate_id_bound) continue; if (min_common_ones[i] == 0) passed_screening.Add(i); } timer.end(); return passed_screening; } int iteration = 0; foreach (BitChunk bit_chunk in bitChunksReaderGrouped(conn, block, fp_ones, _fp_sim_bits_group)) { chunk = bit_chunk.chunk; BingoTimer timer3 = new BingoTimer("fingerprints.screening_one_counters"); // Calculate ones count int max_byte_index = (block.indices.Count + 7) / 8; for (int i = 0; i < max_byte_index; i++) { byte b = chunk[i]; if (b == 0) continue; for (int j = 0; j < 8; j++) if ((b & (1 << j)) != 0) one_counters[8 * i + j]++; } timer3.end(); BingoTimer timer4 = new BingoTimer("fingerprints.screening_process"); if (iteration == 0) { for (int i = 0; i < block.indices.Count; i++) { if (block.indices[i] <= min_storate_id_bound) continue; int min_possible_ones = one_counters[i]; int max_possible_ones = one_counters[i] + fp_ones.Count; if (min_possible_ones <= max_common_ones[i] && max_possible_ones >= min_common_ones[i]) passed_screening.Add(i); } } else { passed_screening_tmp.Clear(); foreach (int i in passed_screening) { int min_possible_ones = one_counters[i]; int max_possible_ones = one_counters[i] + fp_ones.Count - iteration; if (min_possible_ones <= max_common_ones[i] && max_possible_ones >= min_common_ones[i]) passed_screening_tmp.Add(i); } // Swap then List tmp = passed_screening; passed_screening = passed_screening_tmp; passed_screening_tmp = tmp; } timer4.end(); iteration++; if (passed_screening.Count < _sim_screening_pass_mark) break; } BingoCore.lib.bingoProfIncCounter("fingerprints.bits_used", iteration); for (int i = 0; i < passed_screening.Count; i++) passed_screening[i] = block.indices[passed_screening[i]]; timer.end(); return passed_screening; } public void syncContextParameters (bool is_reaction) { lock (_sync_object) { if (is_reaction) _fp_bytes = BingoCore.getConfigInt("reaction-fp-size-bytes"); else _fp_bytes = BingoCore.getConfigInt("fp-size-bytes"); _fp_sub_bits_used = BingoCore.getConfigInt("SUB_SCREENING_MAX_BITS"); _sim_screening_pass_mark = BingoCore.getConfigInt("SIM_SCREENING_PASS_MARK"); } } class BitChunk { public int bit_index; public byte[] chunk; } IEnumerable bitChunksReaderGrouped (SqlConnection conn, Block block, List bits, int size) { int offset = 0; while (offset < bits.Count) { int cur_size = size; if (offset + cur_size >= bits.Count) cur_size = bits.Count - offset; StringBuilder indices = new StringBuilder(); for (int i = offset; i < offset + cur_size; i++) { if (indices.Length != 0) indices.Append(", "); indices.AppendFormat("{0}", bits[i]); } string command_text = String.Format("select bit, bits_chunk from {0} where part={1} and bit in ({2})", _index_data.fingerprintBitsTable, block.part, indices.ToString()); using (SqlCommand command = new SqlCommand(command_text, conn)) { command.CommandTimeout = 3600; BingoTimer timer = new BingoTimer("fingerprints.read_grouped_exec"); using (SqlDataReader reader = command.ExecuteReader()) { timer.end(); while (true) { timer = new BingoTimer("fingerprints.read_grouped_read"); bool ret = reader.Read(); if (!ret) break; BitChunk bit_chunk = new BitChunk(); int bit = (int)reader[0]; bit_chunk.bit_index = bit; bit_chunk.chunk = (byte[])reader[1]; timer.end(); yield return bit_chunk; } } } offset += size; } } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoIndexData.cs000066400000000000000000000373631271037650300237500ustar00rootroot00000000000000using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Data.SqlClient; using System.Resources; using System.Reflection; namespace indigo { public abstract class BingoIndexData { public BingoIndexID id; public string id_column; public string data_column; public string bingo_schema; public BingoFingerprints fingerprints; public BingoStorage storage; public enum IndexType { Molecule, Reaction } public bool keep_cache; public bool locked = false; public BingoIndexData (BingoIndexID id, string id_column, string data_column, string bingo_schema) { this.id = id; this.id_column = id_column; this.data_column = data_column; this.bingo_schema = bingo_schema; keep_cache = false; fingerprints = new BingoFingerprints(this); storage = new BingoStorage(this); } private static BingoIndexData _extractIndexData (SqlConnection conn, string bingo_schema, BingoIndexID id, bool throw_if_not_exists) { BingoIndexData data = null; using (SqlCommand cmd = new SqlCommand(String.Format( "SELECT id_column, data_column, type FROM {0} WHERE obj_id = '{1}'", _contextTable(bingo_schema), id.table_id), conn)) { using (SqlDataReader reader = cmd.ExecuteReader()) { if (!reader.Read()) { if (throw_if_not_exists) throw new Exception("Cannot find Bingo index for table with id=" + id.table_id); else return null; } string id_column = Convert.ToString(reader[0]); string data_column = Convert.ToString(reader[1]); string type = Convert.ToString(reader[2]); if (type.ToLower().Equals("molecule")) data = new MangoIndexData(id, id_column, data_column, bingo_schema); if (type.ToLower().Equals("reaction")) data = new RingoIndexData(id, id_column, data_column, bingo_schema); if (data == null) throw new Exception("unknown type: " + type); } } data.keep_cache = (BingoConfig.getInt(conn, bingo_schema, "KEEP_CACHE", id.table_id) != 0); return data; } public abstract IndexType getIndexType (); private static string _contextTable (string bingo_schema) { return "[" + bingo_schema + "].CONTEXT"; } public class BingoIndexDataRefs { public BingoIndexData index_data; public List session_ids; } private static object index_data_list_lock = new Object(); private static ArrayList index_data_list = new ArrayList(); public static BingoIndexData CreateIndexData (int spid, SqlConnection conn, string bingo_schema, string table, string id_column, string data_column, bool reaction) { lock (index_data_list_lock) { BingoIndexID id = new BingoIndexID(conn, table); _DropIndexData(conn, bingo_schema, id, false); BingoIndexData data; if (reaction) data = new RingoIndexData(id, id_column, data_column, bingo_schema); else data = new MangoIndexData(id, id_column, data_column, bingo_schema); BingoSqlUtils.ExecNonQuery(conn, "INSERT INTO {0} VALUES({1}, {2}, '{3}', '{4}', '{5}', '{6}')", _contextTable(bingo_schema), id.table_id, id.database_id, id.FullTableName(conn), id_column, data_column, reaction ? "reaction" : "molecule"); data.CreateTables(conn); _AddIndexDataToList(spid, data); BingoLog.logMessage("Bingo index for table {0} has been initialized", id.FullTableName(conn)); return data; } } public static void DropIndexData (SqlConnection conn, string bingo_schema, string table, bool throw_if_not_exists) { BingoIndexID id = new BingoIndexID(conn, table); DropIndexData(conn, bingo_schema, id, throw_if_not_exists); } public static void DropIndexData (SqlConnection conn, string bingo_schema, BingoIndexID id, bool throw_if_not_exists) { lock (index_data_list_lock) { _DropIndexData(conn, bingo_schema, id, throw_if_not_exists); } } private static void _DropIndexData (SqlConnection conn, string bingo_schema, BingoIndexID id, bool throw_if_not_exists) { BingoIndexData data = _extractIndexData(conn, bingo_schema, id, throw_if_not_exists); if (data != null) { data.DropTables(conn); data.DropTriggers(conn); } BingoSqlUtils.ExecNonQueryNoThrow(conn, "DELETE FROM {0} WHERE obj_id = '{1}'", _contextTable(bingo_schema), id.table_id); for (int i = index_data_list.Count - 1; i >= 0; i--) { BingoIndexDataRefs refs = (BingoIndexDataRefs)index_data_list[i]; if (refs.index_data.id.Equals(id)) { index_data_list.RemoveAt(i); BingoLog.logMessage("Sessions for table {0} have been released", id.InformationName(conn)); } } if (data != null) BingoLog.logMessage("Bingo index for table {0} has been dropped", id.InformationName(conn)); } public static BingoIndexData GetIndexData (SqlConnection conn, string bingo_schema, int table_id, int database_id, int spid) { BingoIndexID id = new BingoIndexID(table_id, database_id); return GetIndexDataById(conn, bingo_schema, id, spid); } public static BingoIndexData GetIndexData (SqlConnection conn, string bingo_schema, string table, int spid) { BingoIndexID id = new BingoIndexID(conn, table); return GetIndexDataById(conn, bingo_schema, id, spid); } private static BingoIndexData GetIndexDataById (SqlConnection conn, string bingo_schema, BingoIndexID id, int spid) { lock (index_data_list_lock) { foreach (BingoIndexDataRefs index_data_refs in index_data_list) { if (index_data_refs.index_data.id.Equals(id)) { if (!index_data_refs.session_ids.Contains(spid)) { BingoLog.logMessage("Existing BingoIndexData added for spid={0} table={1}", spid, id.FullTableName(conn)); index_data_refs.session_ids.Add(spid); } return index_data_refs.index_data; } } BingoLog.logMessage("Extracting new BingoIndexData for spid={0} table={1}", spid, id.FullTableName(conn)); BingoIndexData data = _extractIndexData(conn, bingo_schema, id, true); _AddIndexDataToList(spid, data); return data; } } private static void _AddIndexDataToList (int spid, BingoIndexData data) { BingoIndexDataRefs new_refs = new BingoIndexDataRefs(); new_refs.index_data = data; new_refs.session_ids = new List(); new_refs.session_ids.Add(spid); index_data_list.Add(new_refs); } class TableWithId { public string table_name { get; set; } public int id { get; set; } }; public static void DropAllIndices (SqlConnection conn, string bingo_schema, bool only_invalid) { List tables = new List(); using (SqlCommand cmd = new SqlCommand(String.Format( "SELECT full_table_name, obj_id FROM {0}", _contextTable(bingo_schema)), conn)) { cmd.CommandTimeout = 3600; using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { string full_table_name = Convert.ToString(reader[0]); int table_id = Convert.ToInt32(reader[1]); tables.Add(new TableWithId() { table_name = full_table_name, id = table_id } ); } } } foreach (TableWithId table in tables) { if (only_invalid) { object ret = BingoSqlUtils.ExecObjQuery(conn, "SELECT OBJECT_NAME({0})", table.id); if (ret != null) continue; } DropIndexData(conn, bingo_schema, table.table_name, false); } } public static void FlushInAllSessions (int spid, bool check_spid) { lock (index_data_list_lock) { SqlConnection ctx_conn = null; try { for (int i = index_data_list.Count - 1; i >= 0; i--) { BingoIndexDataRefs refs = (BingoIndexDataRefs)index_data_list[i]; if (check_spid && !refs.session_ids.Contains(spid)) continue; if (!refs.index_data.needFlush()) continue; if (ctx_conn == null) { ctx_conn = new SqlConnection("context connection=true"); ctx_conn.Open(); } refs.index_data.flush(ctx_conn); } } finally { if (ctx_conn != null) ctx_conn.Close(); } } } public static void OnSessionClose (int spid) { FlushInAllSessions(spid, true); lock (index_data_list_lock) { for (int i = index_data_list.Count - 1; i >= 0; i--) { BingoIndexDataRefs refs = (BingoIndexDataRefs)index_data_list[i]; refs.session_ids.Remove(spid); if (refs.session_ids.Count < 1 && !refs.index_data.keep_cache) { index_data_list.RemoveAt(i); BingoLog.logMessage("Session for table {0} has been released", refs.index_data.id.InformationName()); } } } } public virtual void CreateTables (SqlConnection conn) { storage.createTables(conn); fingerprints.createTables(conn); } public virtual void DropTables (SqlConnection conn) { storage.dropTables(conn); fingerprints.dropTables(conn); } public virtual bool needFlush() { if (storage.needFlush() || fingerprints.needFlush()) return true; return false; } public virtual void flush (SqlConnection conn) { storage.flush(conn); fingerprints.flush(conn); } public virtual void prepareForDeleteRecord (SqlConnection conn) { } public void CreateTriggers (SqlConnection conn) { string cur_db_name = null; try { cur_db_name = BingoSqlUtils.ExecStringQuery(conn, "SELECT DB_NAME()"); BingoSqlUtils.ExecNonQueryNoThrow(conn, "USE {0}", id.DatabaseName(conn)); string bingo_db_schema = cur_db_name + "." + bingo_schema; string full_name = id.FullTableName(conn); object[] trigger_params = new object[] { null, full_name, id_column, data_column, bingo_db_schema, id.table_id, id.database_id }; trigger_params[0] = GetTriggerName("Insert", conn, cur_db_name); string insert_trigger = String.Format(resource.OnInsertTrigger, trigger_params); BingoSqlUtils.ExecNonQuery(conn, "{0}", insert_trigger); trigger_params[0] = GetTriggerName("Delete", conn, cur_db_name); string delete_trigger = String.Format(resource.OnDeleteTrigger, trigger_params); BingoSqlUtils.ExecNonQuery(conn, "{0}", delete_trigger); trigger_params[0] = GetTriggerName("Update", conn, cur_db_name); string update_trigger = String.Format(resource.OnUpdateTrigger, trigger_params); BingoSqlUtils.ExecNonQuery(conn, "{0}", update_trigger); } finally { if (cur_db_name != null) BingoSqlUtils.ExecNonQueryNoThrow(conn, "USE {0}", cur_db_name); } } public void DropTriggers (SqlConnection conn) { string cur_db_name = null; try { cur_db_name = BingoSqlUtils.ExecStringQuery(conn, "SELECT DB_NAME()"); BingoSqlUtils.ExecNonQueryNoThrow(conn, "USE {0}", id.DatabaseName(conn)); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TRIGGER {0}", GetTriggerName("Insert", conn, cur_db_name)); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TRIGGER {0}", GetTriggerName("Delete", conn, cur_db_name)); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TRIGGER {0}", GetTriggerName("Update", conn, cur_db_name)); } finally { if (cur_db_name != null) BingoSqlUtils.ExecNonQueryNoThrow(conn, "USE {0}", cur_db_name); } } public virtual void deleteRecordById (int id, SqlConnection conn) { int? storage_id = getStorageIdById(conn, id); if (!storage_id.HasValue) // Such molecule wasn't added to the molecule // index because it might be invalid return; storage.deleteRecord(storage_id.Value, conn); BingoSqlUtils.ExecNonQuery(conn, "DELETE from {0} where id={1}", shadowTable, id); } public virtual void createIndices (SqlConnection conn) { } private string GetTriggerName (string operation, SqlConnection conn, string bingo_db) { return String.Format("{0}.[bingo_{1}_{2}_{3}]", id.SchemaName(conn), bingo_db, id.table_id, operation); } public string fingerprintsTable { get { return "[" + bingo_schema + "].fingerprints_" + id.table_id; } } public string fingerprintBitsTable { get { return "[" + bingo_schema + "].fingerprint_bits_" + id.table_id; } } public string storageTable { get { return "[" + bingo_schema + "].storage_" + id.table_id; } } public string shadowTable { get { return "[" + bingo_schema + "].shadow_" + id.table_id; } } public void syncContextParameters (SqlConnection conn, string bingo_schema) { fingerprints.syncContextParameters(getIndexType() == IndexType.Reaction); keep_cache = (BingoConfig.getInt(conn, bingo_schema, "KEEP_CACHE", id.table_id) != 0); } public void setKeepCache (SqlConnection conn, string bingo_schema, bool keep) { if (keep_cache != keep) { keep_cache = keep; BingoConfig.setInt(conn, bingo_schema, "KEEP_CACHE", id.table_id, keep ? 1 : 0); BingoLog.logMessage("SetKeepCache has changed for {0} table. New value is {1}", id.FullTableName(conn), keep); } } public int? getStorageIdById (SqlConnection conn, int id) { return BingoSqlUtils.ExecIntQuery(conn, "SELECT storage_id from {0} where id={1}", shadowTable, id); } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoIndexID.cs000066400000000000000000000071641271037650300233670ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Data.SqlClient; using System.Data; using System.Text.RegularExpressions; namespace indigo { public class BingoIndexID { public int table_id; public int database_id; private string name; // Name for logging // Detect database, schema, and table; some from table_name, some from SQL connection public BingoIndexID (SqlConnection connection, string table_name) { table_id = BingoSqlUtils.GetTableObjectID(connection, table_name); string database = null, schema = null, table = null; parseTableName(table_name, ref database, ref schema, ref table); if (database == null) database = connection.Database; database_id = BingoSqlUtils.GetDatabaseID(connection, database); name = table_name; } public BingoIndexID (int table_id, int database_id) { this.table_id = table_id; this.database_id = database_id; name = String.Format("object_id = {0}, database_id = {1}", table_id, database_id); } public bool Equals (BingoIndexID other) { return table_id == other.table_id && database_id == other.database_id; } private void parseTableName (string table_name, ref string database, ref string schema, ref string table) { Match match = Regex.Match(table_name, @"((\[(.*)\])|(.*))\.((\[(.*)\])|(.*))\.((\[(.*)\])|(.*))"); if (match != Match.Empty) { database = (match.Groups[3].Length > 0 ? match.Groups[3].Value : match.Groups[4].Value); schema = (match.Groups[7].Length > 0 ? match.Groups[7].Value : match.Groups[8].Value); table = (match.Groups[11].Length > 0 ? match.Groups[11].Value : match.Groups[12].Value); } else { match = Regex.Match(table_name, @"((\[(.*)\])|(.*))\.((\[(.*)\])|(.*))"); if (match != Match.Empty) { schema = (match.Groups[3].Length > 0 ? match.Groups[3].Value : match.Groups[4].Value); table = (match.Groups[7].Length > 0 ? match.Groups[7].Value : match.Groups[8].Value); match = Match.Empty; } else { match = Regex.Match(table_name, @"\[(.*)\]"); if (match != Match.Empty) table = match.Groups[1].Value; else table = table_name; } } } public string SchemaName (SqlConnection connection) { return BingoSqlUtils.ExecStringQuery(connection, @"SELECT QUOTENAME(OBJECT_SCHEMA_NAME({0}, {1}))", table_id, database_id); } public string DatabaseName (SqlConnection connection) { return BingoSqlUtils.ExecStringQuery(connection, @"SELECT QUOTENAME(DB_NAME({0}))", database_id); } public string FullTableName (SqlConnection connection) { return BingoSqlUtils.ExecStringQuery(connection, @"SELECT QUOTENAME(DB_NAME({0})) + N'.' + QUOTENAME(OBJECT_SCHEMA_NAME({1}, {0})) + N'.' + QUOTENAME(OBJECT_NAME({1}, {0}))", database_id, table_id); } public string InformationName () { return String.Format("{0} (db_id={1}, obj_id={2})", name, database_id, table_id); } public string InformationName (SqlConnection connection) { return String.Format("{0} (db_id={1}, obj_id={2})", FullTableName(connection), database_id, table_id); } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoSession.cs000066400000000000000000000017411271037650300235210ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace indigo { class BingoSession : IDisposable { private ulong _session_id, _previous_session_id; public BingoSession() { _previous_session_id = BingoCore.lib.bingoGetSessionID(); _session_id = BingoCore.lib.bingoAllocateSessionID(); setSession(); } public void setSession() { BingoCore.lib.bingoSetSessionID(_session_id); } public void Dispose() { _releaseSesssion(); GC.SuppressFinalize(this); } ~BingoSession() { Dispose(); } private void _releaseSesssion() { BingoCore.lib.bingoReleaseSessionID(_session_id); // Restore session ID because of possible nested BingoSessions BingoCore.lib.bingoSetSessionID(_previous_session_id); } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoStorage.cs000066400000000000000000000402401271037650300234770ustar00rootroot00000000000000using System.IO; using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Runtime.Serialization.Formatters.Binary; namespace indigo { public class BingoStorage { object _sync_object = new Object(); BingoIndexData _index_data; const int MAX_BLOCK_SIZE = 8 * 1000 * 1000; class _Block { public int block_index; public byte[] data; public int first_index, end_index; public int[] offsets; public short[] lengths; public List pending_data; public List pending_offsets; public List pending_lengths; public bool dirty = false; } List<_Block> _blocks = new List<_Block>(); int total_items = -1; public BingoStorage (BingoIndexData index_data) { _index_data = index_data; } public IEnumerable enumerateStorageIds(int? next_after_storate_id) { int i = 0; if (next_after_storate_id.HasValue) i = next_after_storate_id.Value + 1; for (; i < total_items; i++) yield return i; } public void createTables (SqlConnection conn) { BingoSqlUtils.ExecNonQuery(conn, @"CREATE TABLE {0} ( [id] SMALLINT PRIMARY KEY, [first_index] INT, [count] INT, [offsets] VARBINARY(MAX), [lengths] VARBINARY(MAX), [data] VARBINARY(MAX))", _index_data.storageTable); } public void dropTables (SqlConnection conn) { BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE {0}", _index_data.storageTable); } private bool _blockLoaded (short block_index) { if (_blocks.Count > block_index) { _Block b = _blocks[block_index]; if (b == null) return false; return true; } return false; } private void _validateBlock (short block_index, SqlConnection conn) { if (_blockLoaded(block_index)) return; lock (_sync_object) { // Double-checked locking if (_blockLoaded(block_index)) return; BingoTimer timer = new BingoTimer("storage.validate_block"); BingoLog.logMessage("Loading storage block {0} for table {1}...", block_index, _index_data.id.InformationName()); string text = "SELECT [data], [first_index], [offsets], [lengths], [count] from " + _index_data.storageTable + " where id = " + block_index; using (SqlCommand command = new SqlCommand(text, conn)) { command.CommandTimeout = 3600; using (SqlDataReader reader = command.ExecuteReader()) { if (!reader.Read()) throw new Exception("Block cannot be found"); while (_blocks.Count <= block_index) _blocks.Add(new _Block()); if (_blocks[block_index] == null) _blocks[block_index] = new _Block(); _Block block = _blocks[block_index]; block.block_index = block_index; block.data = (byte[])reader["data"]; block.first_index = (int)reader["first_index"]; int count = (int)reader["count"]; block.offsets = new int[count]; MemoryStream mem_stream = new MemoryStream((byte[])reader["offsets"]); BinaryReader bin_reader = new BinaryReader(mem_stream); for (int i = 0; i < count; i++) block.offsets[i] = bin_reader.ReadInt32(); block.lengths = new short[count]; mem_stream = new MemoryStream((byte[])reader["lengths"]); bin_reader = new BinaryReader(mem_stream); for (int i = 0; i < count; i++) block.lengths[i] = bin_reader.ReadInt16(); block.end_index = block.first_index + count; } } BingoLog.logMessage(" Done."); timer.end(); } } private void _validateLastBlock (SqlConnection conn) { _validateBlock((short)(_blocks.Count - 1), conn); } public void validate (SqlConnection conn) { if (total_items >= 0) return; // Index already loaded lock (_sync_object) { if (total_items >= 0) return; // Index already loaded int? ret = BingoSqlUtils.ExecIntQuery(conn, "select max(id) from {0}", _index_data.storageTable); if (ret.HasValue) { while (_blocks.Count <= ret.Value) _blocks.Add(null); } int? sum = BingoSqlUtils.ExecIntQuery(conn, "select sum(count) from {0}", _index_data.storageTable); if (!sum.HasValue) total_items = 0; else total_items = sum.Value; } } public bool needFlush() { lock (_sync_object) { foreach (_Block b in _blocks) if (b != null && b.dirty) return true; return false; } } public void flush (SqlConnection conn) { lock (_sync_object) { for (int i = 0; i < _blocks.Count; i++) { _Block b = _blocks[i]; if (b != null) { if (b.dirty) { _flushBlock(conn, b); BingoLog.logMessage("Disposing memory for block {0}...", b.block_index); _blocks[i] = null; BingoLog.logMessage(" Done"); } } } } } private void _flushBlock (SqlConnection conn, _Block b) { BingoLog.logMessage("Flushing storage block {0}", b.block_index); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DELETE FROM {0} WHERE id={1}", _index_data.storageTable, b.block_index); string text = String.Format(@"INSERT INTO {0} ([id], [first_index], [count], [offsets], [lengths], [data]) VALUES ({1}, {2}, {3}, @offsets, @lengths, @data)", _index_data.storageTable, b.block_index, b.first_index, b.end_index - b.first_index); using (SqlCommand command = new SqlCommand(text, conn)) { command.CommandTimeout = 3600; byte[] data; if (b.pending_data != null) data = b.pending_data.ToArray(); else data = b.data; SqlBinary binary_data = new SqlBinary(data); command.Parameters.AddWithValue("@data", binary_data); ICollection offsets; if (b.pending_offsets != null) offsets = b.pending_offsets; else offsets = b.offsets; MemoryStream mem_stream = new MemoryStream(offsets.Count * 4); BinaryWriter writer = new BinaryWriter(mem_stream); foreach (int offset in offsets) writer.Write(offset); byte[] buffer = mem_stream.GetBuffer(); command.Parameters.AddWithValue("@offsets", buffer); ICollection lengths; if (b.pending_lengths != null) lengths = b.pending_lengths; else lengths = b.lengths; mem_stream = new MemoryStream(offsets.Count * 2); writer = new BinaryWriter(mem_stream); foreach (short length in lengths) writer.Write(length); buffer = mem_stream.GetBuffer(); command.Parameters.AddWithValue("@lengths", buffer); command.ExecuteNonQuery(); } _convertPendingBlockToNormal(b); } public int add (byte[] data, SqlConnection conn) { lock (_sync_object) { _Block b = _getPendingBlock(data.Length, conn); // Add one byte for remove mark b.pending_data.Add(1); b.pending_offsets.Add(b.pending_data.Count); if (data.Length > short.MaxValue) throw new Exception("Data length is to long. Unexpected."); b.pending_lengths.Add((short)data.Length); b.pending_data.AddRange(data); total_items++; b.end_index++; b.dirty = true; return total_items - 1; } } private _Block _getPendingBlock (int data_length, SqlConnection conn) { bool need_new_block = false; if (_blocks.Count == 0) need_new_block = true; else { _Block last = _blocks[_blocks.Count - 1]; if (last == null) _validateBlock((short)(_blocks.Count - 1), conn); last = _blocks[_blocks.Count - 1]; ICollection collection = null; if (last.data != null) collection = last.data; else collection = last.pending_data; if (collection.Count + data_length > MAX_BLOCK_SIZE) need_new_block = true; } _Block block = null; if (need_new_block) { // Flush all dirty blocks and dispose memory flush(conn); _blocks.Add(new _Block()); block = _blocks[_blocks.Count - 1]; block.block_index = _blocks.Count - 1; if (_blocks.Count == 1) block.first_index = 0; else block.first_index = total_items; block.end_index = block.first_index; block.dirty = true; } else block = _blocks[_blocks.Count - 1]; _convertBlockToPendingBlock(block); return block; } private void _convertBlockToPendingBlock (_Block block) { if (block.pending_data != null) return; block.pending_data = new List(); if (block.data != null) block.pending_data.AddRange(block.data); block.data = null; block.pending_offsets = new List(); if (block.offsets != null) block.pending_offsets.AddRange(block.offsets); block.offsets = null; block.pending_lengths = new List(); if (block.lengths != null) block.pending_lengths.AddRange(block.lengths); block.lengths = null; } private void _convertPendingBlockToNormal (_Block block) { if (block.data != null) return; block.data = block.pending_data.ToArray(); block.pending_data = null; block.offsets = block.pending_offsets.ToArray(); block.pending_offsets = null; block.lengths = block.pending_lengths.ToArray(); block.lengths = null; } private _Block _getBlockByIndex (SqlConnection conn, int index, ref int cache_index) { if (cache_index >= _blocks.Count || cache_index < 0) cache_index = 0; _validateBlock((short)cache_index, conn); _Block b = _blocks[cache_index]; if (index < b.first_index || index >= b.end_index) { // Find another block cache_index = -1; do { cache_index++; if (cache_index >= _blocks.Count) throw new Exception( String.Format("index {0} wasn't found in the storage", index)); _validateBlock((short)cache_index, conn); b = _blocks[cache_index]; } while (index < b.first_index || index >= b.end_index); } return b; } // If length if -1 then all rest data is returned private void _getDataWithOffset(int index, int offset, int length, out byte[] dest_data, out int dest_offset, out int dest_len, SqlConnection conn, ref int cache_index) { _Block b = _getBlockByIndex(conn, index, ref cache_index); int sub_offset = index - b.first_index; if (length == -1) { if (b.lengths != null) length = b.lengths[sub_offset] - offset; else length = b.pending_lengths[sub_offset] - offset; } int data_offset; if (b.offsets != null) data_offset = b.offsets[sub_offset]; else data_offset = b.pending_offsets[sub_offset]; if (b.data != null) { dest_data = b.data; dest_len = length; dest_offset = data_offset + offset; } else { dest_data = new byte[length]; b.pending_data.CopyTo(data_offset + offset, dest_data, 0, dest_data.Length); dest_len = length; dest_offset = 0; } } // If length if -1 then all rest data is returned public byte[] get(int index, int offset, int length, SqlConnection conn, ref int cache_index) { byte[] all_data; int data_offset, data_length; _getDataWithOffset(index, offset, length, out all_data, out data_offset, out data_length, conn, ref cache_index); if (data_offset == 0 && data_length == all_data.Length) // Copy isn't necessary return all_data; byte[] data = new byte[data_length]; Array.Copy(all_data, data_offset, data, 0, data.Length); return data; } public int getInt(int index, int offset, SqlConnection conn, ref int cache_index) { byte[] all_data; int data_offset, data_length; _getDataWithOffset(index, offset, 4, out all_data, out data_offset, out data_length, conn, ref cache_index); return BitConverter.ToInt32(all_data, data_offset); } public short getShort(int index, int offset, SqlConnection conn, ref int cache_index) { byte[] all_data; int data_offset, data_length; _getDataWithOffset(index, offset, 2, out all_data, out data_offset, out data_length, conn, ref cache_index); return BitConverter.ToInt16(all_data, data_offset); } public byte getByte (int index, int offset, SqlConnection conn, ref int cache_index) { byte[] all_data; int data_offset, data_length; _getDataWithOffset(index, offset, 1, out all_data, out data_offset, out data_length, conn, ref cache_index); return all_data[data_offset]; } public bool isDeleted (int storage_id, SqlConnection conn, ref int cache_index) { return getByte(storage_id, -1, conn, ref cache_index) == 0; } public void deleteRecord (int storage_id, SqlConnection conn) { lock (_sync_object) { int cache_index = 0; _Block b = _getBlockByIndex(conn, storage_id, ref cache_index); int data_offset; if (b.offsets != null) data_offset = b.offsets[storage_id - b.first_index]; else data_offset = b.pending_offsets[storage_id - b.first_index]; if (b.data != null) b.data[data_offset - 1] = 0; else if (b.pending_data != null) b.pending_data[data_offset - 1] = 0; else throw new Exception("Internal error: cannot delete record"); b.dirty = true; } } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/BingoUtils.cs000066400000000000000000000165051271037650300232020ustar00rootroot00000000000000using System; using System.IO; using System.Data; using System.Collections.Generic; using Microsoft.SqlServer.Server; using System.Data.SqlTypes; using System.Data.SqlClient; using System.Text; using indigo.SqlAttributes; using System.Diagnostics; using System.Threading; namespace indigo { public class BingoSqlUtils { public static void ExecNonQuery (SqlConnection conn, string command, params object[] args) { string query = String.Format(command, args); using (SqlCommand cmd = new SqlCommand(query, conn)) { cmd.CommandTimeout = 3600 * 10; cmd.ExecuteNonQuery(); } } public static SqlDataReader ExecReader (SqlConnection conn, string command, params object[] args) { string query = String.Format(command, args); using (SqlCommand cmd = new SqlCommand(query, conn)) { cmd.CommandTimeout = 3600 * 10; return cmd.ExecuteReader(); } } public static void ExecNonQueryNoThrow (SqlConnection conn, string command, params object[] args) { try { ExecNonQuery(conn, command, args); } catch (SqlException) { } catch (Exception ex) { BingoLog.logMessage("Exception in ExecNonQueryNoThrow: {0} in {1}:\n{2}", ex.Message, ex.Source, ex.StackTrace); } } public static object ExecObjQuery (SqlConnection conn, string command, params object[] args) { using (SqlCommand cmd = new SqlCommand(String.Format(command, args), conn)) { cmd.CommandTimeout = 3600 * 10; object obj = cmd.ExecuteScalar(); if (obj == DBNull.Value) return null; return obj; } } public static int? ExecIntQuery (SqlConnection conn, string command, params object[] args) { object obj = ExecObjQuery(conn, command, args); if (obj == null) return null; return Convert.ToInt32(obj); } public static string ExecStringQuery (SqlConnection conn, string command, params object[] args) { object obj = ExecObjQuery(conn, command, args); if (obj == null) return null; return Convert.ToString(obj); } public static List ExecQuery (SqlConnection conn, string command, params object[] args) { using (SqlCommand cmd = new SqlCommand(String.Format(command, args), conn)) { cmd.CommandTimeout = 3600; using (SqlDataReader reader = cmd.ExecuteReader()) { if (!reader.Read()) return null; List ret = new List(); for (int i = 0; i < reader.FieldCount; i++) ret.Add(reader[i]); return ret; } } } public static int GetTableObjectID (SqlConnection conn, string table_name) { int? id = ExecIntQuery(conn, "SELECT OBJECT_ID('{0}', 'U')", table_name); if (id == null) throw new Exception("Cannot get ID for table " + table_name); return id.Value; } public static int GetDatabaseID (SqlConnection conn, string database_name) { int? id = ExecIntQuery(conn, "select DB_ID('{0}')", database_name); if (id == null) throw new Exception("Cannot get ID for database " + database_name); return id.Value; } public static string GetConnectionSchema (SqlConnection conn) { return ExecStringQuery(conn, "SELECT SCHEMA_NAME()"); } [SqlFunction] [BingoSqlFunction] public static SqlString ReadFileAsText (SqlString filename) { return File.ReadAllText(filename.Value); } [SqlFunction] [BingoSqlFunction] public static SqlBinary ReadFileAsBinary (SqlString filename) { return File.ReadAllBytes(filename.Value); } [SqlFunction] [BingoSqlFunction(access_level = AccessLevelKind.None)] public static void _ForceGC () { GC.Collect(); GC.WaitForPendingFinalizers(); } } public class BingoLog { static object sync_obj = new object(); // Functionality to detect moment when assembly gets unloaded static BingoLog _log_instance = new BingoLog(); ~BingoLog () { logMessage("Log has been released"); } [SqlFunction] [BingoSqlFunction(access_level = AccessLevelKind.None)] public static void _WriteLog (SqlString message) { try { logMessage(message.Value); SqlContext.Pipe.Send(String.Format( "Message '{0}' should be saved to '{1}'", message.Value, getLogFileName())); } catch (Exception ex) { SqlContext.Pipe.Send(String.Format(@"Error: Cannot save message '{0}' to '{1}'", message.Value, getLogFileName())); SqlContext.Pipe.Send(String.Format("Exception '{0}' in '{1}':\n{2}", ex.Message, ex.Source, ex.StackTrace)); } } public static string getLogFileName () { return System.IO.Path.GetTempPath() + "bingo_sql_server.log"; } public static void logMessageWithThrow (string message, params object[] args) { lock (sync_obj) { try { using (StreamWriter sw = System.IO.File.AppendText(getLogFileName())) { BingoSqlUtils._ForceGC(); string logLine = String.Format( "{0:G}: {1}", DateTime.Now, String.Format(message, args)); sw.WriteLine(logLine); double size_mb = GC.GetTotalMemory(false) / 1024 / 1024.0; int processId = Process.GetCurrentProcess().Id; int threadId = Thread.CurrentThread.ManagedThreadId; Process proc = Process.GetCurrentProcess(); double proc_size_mb = proc.PrivateMemorySize64 / 1024 / 1024.0; sw.WriteLine(String.Format(" GC Memory: {0:0.00} Mb. Process id: {1}. Thread id: {2}. AppDomain: {3}. PrivateMemory: {4:0.00} Mb", size_mb, processId, threadId, AppDomain.CurrentDomain.FriendlyName, proc_size_mb)); } } catch { } } } public static void logMessage (string message, params object[] args) { try { logMessageWithThrow(message, args); } catch { } } } // Profiling timer public class BingoTimer { UInt64 _start; string _name; public BingoTimer (string name) { _start = BingoCore.lib.bingoProfNanoClock(); _name = name; } public void end () { UInt64 end = BingoCore.lib.bingoProfNanoClock(); BingoCore.lib.bingoProfIncTimer(_name, end - _start); } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/MangoCommon.cs000066400000000000000000000004351271037650300233300ustar00rootroot00000000000000using System.Collections.Generic; namespace indigo { public class MoleculeHashElement { public int count; public int hash; } public class MoleculeHash { public List elements = new List(); } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/MangoFastIndexFetch.cs000066400000000000000000000132211271037650300247340ustar00rootroot00000000000000using System; using System.IO; using System.Collections.Generic; using System.Collections; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.InteropServices; namespace indigo { public class MangoFastIndexFetch { MangoIndexData _index_data; enum SearchType { UNDEF, SUB, SIM } ; SearchType search_type = SearchType.UNDEF; bool highlighting = false; public MangoFastIndexFetch (MangoIndexData index_data) { _index_data = index_data; } public int? nextAfterStorageId { get; set; } public void prepareSub (string query, string options, bool highlighting, bool smarts) { int res = BingoCore.lib.mangoSetupMatch(smarts ? "SMARTS" : "SUB", query, options); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); search_type = SearchType.SUB; BingoCore.lib.mangoSetHightlightingMode(highlighting ? 1 : 0); this.highlighting = highlighting; } public void prepareSimilarity (string query, string options, float min, float max) { int res = BingoCore.lib.mangoSetupMatch("SIM", query, options); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); BingoCore.lib.mangoSimilaritySetMinMaxBounds(min, max); search_type = SearchType.SIM; } private void simGetMinMaxBounds (IList storage_ids, ref int[] min_common_ones, ref int[] max_common_ones, SqlConnection conn) { BingoTimer timer2 = new BingoTimer("fingerprints.screening_bounds_ones"); int cache_index = 0; int[] targets_ones = new int[storage_ids.Count]; for (int i = 0; i < storage_ids.Count; i++) { int storage_id = storage_ids[i]; targets_ones[i] = _index_data.storage.getShort(storage_id, 4, conn, ref cache_index); } timer2.end(); BingoTimer timer3 = new BingoTimer("fingerprints.screening_bounds_get"); IntPtr min_common_ones_ptr, max_common_ones_ptr; BingoCore.lib.mangoSimilarityGetBitMinMaxBoundsArray(targets_ones.Length, targets_ones, out min_common_ones_ptr, out max_common_ones_ptr); if (min_common_ones.Length != storage_ids.Count || max_common_ones.Length != storage_ids.Count) throw new Exception("Internal error in simGetMinMaxBounds: array lengths mismatch"); Marshal.Copy(min_common_ones_ptr, min_common_ones, 0, targets_ones.Length); Marshal.Copy(max_common_ones_ptr, max_common_ones, 0, targets_ones.Length); timer3.end(); } public IEnumerable fetch (SqlConnection conn) { byte[] fp; BingoCore.mangoGetQueryFingerprint(out fp); // Search using fast index _index_data.fingerprints.init(conn); _index_data.storage.validate(conn); int need_coords = BingoCore.lib.mangoNeedCoords(); byte[] xyz = new byte[0]; IEnumerable screened; if (search_type == SearchType.SUB) { if (!_index_data.fingerprints.ableToScreen(fp)) screened = _index_data.storage.enumerateStorageIds(nextAfterStorageId); else screened = _index_data.fingerprints.screenSub(conn, fp, nextAfterStorageId); } else { screened = _index_data.fingerprints.screenSim(conn, fp, nextAfterStorageId, new BingoFingerprints.getBoundsDelegate(simGetMinMaxBounds)); } int cache_index = 0; foreach (int storage_id in screened) { if (_index_data.storage.isDeleted(storage_id, conn, ref cache_index)) continue; // TODO: add match with xyz test for binary molecules byte[] data_with_cmf = _index_data.storage.get(storage_id, 6, -1, conn, ref cache_index); if (need_coords != 0) { xyz = _index_data.getXyz(storage_id, conn); } int ret = BingoCore.lib.mangoMatchTargetBinary(data_with_cmf, data_with_cmf.Length, xyz, xyz.Length); if (ret < 0) { // Exception has happend // Extract id int id = _index_data.storage.getInt(storage_id, 0, conn, ref cache_index); string msg = "Undef"; if (ret == -2) msg = BingoCore.lib.bingoGetError(); if (ret == -1) msg = BingoCore.lib.bingoGetWarning(); throw new Exception(String.Format("Id = {0}: {1}", id, msg)); } if (ret == 1) { // Extract id int id = _index_data.storage.getInt(storage_id, 0, conn, ref cache_index); FetchedData mol = new FetchedData(id); if (highlighting) { if (need_coords == 0) { // Load xyz byte[] xyz_found = _index_data.getXyz(storage_id, conn); BingoCore.lib.mangoLoadTargetBinaryXyz(xyz_found, xyz_found.Length); } mol.str = BingoCore.mangoGetHightlightedMolecule(); } if (search_type == SearchType.SIM) BingoCore.lib.mangoSimilarityGetScore(out mol.value); yield return mol; } } } } }Indigo-indigo-1.2.3/bingo/sqlserver/Source/MangoIndex.cs000066400000000000000000000043361271037650300231530ustar00rootroot00000000000000using System; using System.Runtime.InteropServices; namespace indigo { public class MangoIndex { public const int COUNTED_ELEMENTS_COUNT = 6; public float mass; public byte[] cmf; public byte[] xyz; public string gross, counted_elements_str; public byte[] fingerprint; public string fingerprint_sim_str; public int sim_fp_bits_count; public MoleculeHash hash; public bool readPrepared (out int id) { IntPtr cmf_ptr, xyz_ptr, fingerprint_ptr; int cmf_buf_len, xyz_buf_len, fingerprint_buf_len; IntPtr gross_str, counter_elements_str, fingerprint_sim_str_ptr; int ret = BingoCore.lib.mangoIndexReadPreparedMolecule(out id, out cmf_ptr, out cmf_buf_len, out xyz_ptr, out xyz_buf_len, out gross_str, out counter_elements_str, out fingerprint_ptr, out fingerprint_buf_len, out fingerprint_sim_str_ptr, out mass, out sim_fp_bits_count); if (ret == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (ret == -1) return false; cmf = new byte[cmf_buf_len]; xyz = new byte[xyz_buf_len]; fingerprint = new byte[fingerprint_buf_len]; Marshal.Copy(cmf_ptr, cmf, 0, cmf_buf_len); if (xyz_ptr != IntPtr.Zero) Marshal.Copy(xyz_ptr, xyz, 0, xyz_buf_len); Marshal.Copy(fingerprint_ptr, fingerprint, 0, fingerprint_buf_len); gross = Marshal.PtrToStringAnsi(gross_str); counted_elements_str = Marshal.PtrToStringAnsi(counter_elements_str); fingerprint_sim_str = Marshal.PtrToStringAnsi(fingerprint_sim_str_ptr); // Get each hash element hash = new MoleculeHash(); hash.elements.Clear(); int hash_count, tmp; BingoCore.lib.mangoGetHash(true, -1, out hash_count, out tmp); for (int i = 0; i < hash_count; i++) { MoleculeHashElement elem = new MoleculeHashElement(); BingoCore.lib.mangoGetHash(true, i, out elem.count, out elem.hash); hash.elements.Add(elem); } return true; } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/MangoIndexData.cs000066400000000000000000000256311271037650300237460ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Globalization; namespace indigo { public class MangoIndexData : BingoIndexData { object _sync_object = new Object(); public MangoIndexData (BingoIndexID id, string id_column, string data_column, string bingo_schema) : base(id, id_column, data_column, bingo_schema) { } public override IndexType getIndexType () { return IndexType.Molecule; } public override void CreateTables (SqlConnection conn) { base.CreateTables(conn); StringBuilder cmd = new StringBuilder(); // Create shadow table cmd.AppendFormat(@"CREATE TABLE {0} (id int not null, storage_id int not null, gross VARCHAR(500), cmf varbinary(max), xyz varbinary(max), mass real not null, fragments int not null", shadowTable); for (int i = 0; i < MangoIndex.COUNTED_ELEMENTS_COUNT; i++) cmd.AppendFormat(", {0} int not null", BingoCore.mangoGetCountedElementName(i)); cmd.Append(")"); BingoSqlUtils.ExecNonQuery(conn, cmd.ToString()); // Create shadow table for molecule components BingoSqlUtils.ExecNonQuery(conn, "CREATE TABLE {0} (id int not null, hash int not null, count int not null, primary key(id, hash))", componentsTable); } public override void createIndices (SqlConnection conn) { BingoSqlUtils.ExecNonQuery(conn, "ALTER TABLE {0} ADD PRIMARY KEY (storage_id)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE UNIQUE INDEX id ON {0}(id)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX gross ON {0}(gross)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX mass ON {0}(mass)", shadowTable); for (int i = 0; i < MangoIndex.COUNTED_ELEMENTS_COUNT; i++) { BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX {1} ON {0}({1})", shadowTable, BingoCore.mangoGetCountedElementName(i)); } // Create indices for components shadow table BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX id ON {0}(id)", componentsTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX hash ON {0}(hash)", componentsTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX count ON {0}(hash, count)", componentsTable); } public override void DropTables (SqlConnection conn) { base.DropTables(conn); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE " + shadowTable); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE " + componentsTable); } DataTable shadow_datatable = null; DataTable components_datatable = null; public void addToShadowTable (SqlConnection conn, MangoIndex index, int id, int storage_id) { lock (_sync_object) { if (shadow_datatable == null) _createDataTables(); if (shadow_datatable.Rows.Count >= 10000) _flushShadowTable(conn); DataRow shadow_row = shadow_datatable.NewRow(); shadow_row["id"] = id; shadow_row["storage_id"] = storage_id; shadow_row["gross"] = index.gross; shadow_row["cmf"] = index.cmf; shadow_row["xyz"] = index.xyz; shadow_row["mass"] = index.mass; int fragments_count = 0; for (int i = 0; i < index.hash.elements.Count; i++) fragments_count += index.hash.elements[i].count; shadow_row["fragments"] = fragments_count; string[] counted = index.counted_elements_str.Split(','); for (int i = 0; i < MangoIndex.COUNTED_ELEMENTS_COUNT; i++) shadow_row[BingoCore.mangoGetCountedElementName(i)] = Convert.ToInt32(counted[i + 1]); shadow_datatable.Rows.Add(shadow_row); foreach (MoleculeHashElement elem in index.hash.elements) { DataRow comp_row = components_datatable.NewRow(); comp_row["id"] = id; comp_row["hash"] = elem.hash; comp_row["count"] = elem.count; components_datatable.Rows.Add(comp_row); } } } private void _createDataTables () { shadow_datatable = new DataTable(); DataColumnCollection sc = shadow_datatable.Columns; sc.Add(new DataColumn("id", Type.GetType("System.Int32"))); sc.Add(new DataColumn("storage_id", Type.GetType("System.Int32"))); sc.Add(new DataColumn("gross", Type.GetType("System.String"))); sc.Add(new DataColumn("cmf", Type.GetType("System.Array"))); sc.Add(new DataColumn("xyz", Type.GetType("System.Array"))); sc.Add(new DataColumn("mass", Type.GetType("System.Single"))); sc.Add(new DataColumn("fragments", Type.GetType("System.Int32"))); for (int i = 0; i < MangoIndex.COUNTED_ELEMENTS_COUNT; i++) sc.Add(new DataColumn(BingoCore.mangoGetCountedElementName(i), Type.GetType("System.Int32"))); components_datatable = new DataTable(); DataColumnCollection cc = components_datatable.Columns; cc.Add(new DataColumn("id", Type.GetType("System.Int32"))); cc.Add(new DataColumn("hash", Type.GetType("System.Int32"))); cc.Add(new DataColumn("count", Type.GetType("System.Int32"))); } public override bool needFlush() { lock (_sync_object) { if (base.needFlush()) return true; return shadow_datatable != null && (shadow_datatable.Rows.Count > 0 || components_datatable.Rows.Count > 0); } } public override void flush(SqlConnection conn) { lock (_sync_object) { base.flush(conn); _flushShadowTable(conn); } } private void _flushShadowTable (SqlConnection conn) { if (shadow_datatable == null || (shadow_datatable.Rows.Count == 0 && components_datatable.Rows.Count == 0)) return; if (conn.ConnectionString == "context connection=true") { // SqlBulkInsert cannot be used in the context connection _flushShadowTableInContext(conn); return; } BingoTimer timer = new BingoTimer("shadow_table.flush"); using (SqlTransaction transaction = conn.BeginTransaction()) { // Copy shadow table using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, transaction)) { bulkCopy.DestinationTableName = shadowTable; foreach (DataColumn dc in shadow_datatable.Columns) bulkCopy.ColumnMappings.Add(dc.ColumnName, dc.ColumnName); bulkCopy.BatchSize = shadow_datatable.Rows.Count; bulkCopy.BulkCopyTimeout = 3600; bulkCopy.WriteToServer(shadow_datatable); } shadow_datatable.Rows.Clear(); // Copy components table using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, transaction)) { bulkCopy.DestinationTableName = componentsTable; foreach (DataColumn dc in components_datatable.Columns) bulkCopy.ColumnMappings.Add(dc.ColumnName, dc.ColumnName); bulkCopy.BatchSize = components_datatable.Rows.Count; bulkCopy.BulkCopyTimeout = 3600; bulkCopy.WriteToServer(components_datatable); } components_datatable.Rows.Clear(); transaction.Commit(); } timer.end(); } private void _flushShadowTableInContext (SqlConnection conn) { foreach (DataRow row in shadow_datatable.Rows) { using (SqlCommand cmd = new SqlCommand()) { cmd.CommandTimeout = 3600; StringBuilder cmd_text = new StringBuilder(); cmd_text.AppendFormat("INSERT INTO {0} VALUES ", shadowTable); cmd_text.AppendFormat(CultureInfo.InvariantCulture, "({0}, {1}, '{2}', @cmf, @xyz, {3}, {4} ", row["id"], row["storage_id"], row["gross"], row["mass"], row["fragments"]); for (int i = 0; i < MangoIndex.COUNTED_ELEMENTS_COUNT; i++) cmd_text.AppendFormat(", {0}", row[BingoCore.mangoGetCountedElementName(i)]); cmd_text.Append(")"); cmd.Parameters.AddWithValue("@cmf", new SqlBinary((byte[])row["cmf"])); cmd.Parameters.AddWithValue("@xyz", new SqlBinary((byte[])row["xyz"])); cmd.Connection = conn; cmd.CommandText = cmd_text.ToString(); cmd.ExecuteNonQuery(); } } shadow_datatable.Rows.Clear(); foreach (DataRow row in components_datatable.Rows) { using (SqlCommand cmd = new SqlCommand()) { cmd.CommandTimeout = 3600; StringBuilder cmd_text = new StringBuilder(); cmd_text.AppendFormat("INSERT INTO {0} VALUES ", componentsTable); cmd_text.AppendFormat("({0}, {1}, {2}) ", row["id"], row["hash"], row["count"]); cmd.Connection = conn; cmd.CommandText = cmd_text.ToString(); cmd.ExecuteNonQuery(); } } components_datatable.Rows.Clear(); } public byte[] getXyz (int storage_id, SqlConnection conn) { object ret = BingoSqlUtils.ExecObjQuery(conn, "SELECT xyz from {0} where storage_id={1}", shadowTable, storage_id); return (byte[])ret; } public override void prepareForDeleteRecord (SqlConnection conn) { lock (_sync_object) { _flushShadowTable(conn); } } public override void deleteRecordById (int id, SqlConnection conn) { lock (_sync_object) { base.deleteRecordById(id, conn); BingoSqlUtils.ExecNonQuery(conn, "DELETE from {0} where id={1}", componentsTable, id); } } public string componentsTable { get { return "[" + bingo_schema + "].components_" + id.table_id; } } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/MangoShadowFetch.cs000066400000000000000000000200251271037650300242740ustar00rootroot00000000000000using System.IO; using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Globalization; namespace indigo { public class MangoShadowFetch { MangoIndexData _index_data; enum SearchType { UNDEF, EXACT, GROSS, MASS }; SearchType search_type = SearchType.UNDEF; string table_copies, where_clause; bool need_xyz = false; public int? nextAfterStorageId { get; set; } public MangoShadowFetch (MangoIndexData index_data) { _index_data = index_data; } public void prepareExact (string query, string options) { int res = BingoCore.lib.mangoSetupMatch("EXACT", query, options); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); table_copies = ""; where_clause = ""; if (options.Contains("TAU")) { where_clause = String.Format("gross = '{0}' OR gross LIKE '{0} H%%'", BingoCore.mangoTauGetQueryGross()); } else { need_xyz = (BingoCore.lib.mangoNeedCoords() != 0); _prepareExactQueryStrings(ref table_copies, ref where_clause); } search_type = SearchType.EXACT; } public void prepareGross (string query) { search_type = SearchType.GROSS; int res = BingoCore.lib.mangoSetupMatch("GROSS", query, ""); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); where_clause = BingoCore.mangoGrossGetConditions(); } public void prepareMass (float? min, float? max) { search_type = SearchType.MASS; StringBuilder where_text = new StringBuilder(); if (min.HasValue) where_text.AppendFormat(CultureInfo.InvariantCulture, "mass >= {0} ", min.Value); if (max.HasValue) { if (where_text.Length > 0) where_text.Append(" AND "); where_text.AppendFormat(CultureInfo.InvariantCulture, " mass <= {0} ", max.Value); } where_clause = where_text.ToString(); } public IEnumerable fetch (SqlConnection conn) { ArrayList res_list = new ArrayList(); StringBuilder command_text = new StringBuilder(); command_text.Append("SELECT sh.id"); if (search_type == SearchType.EXACT) { command_text.Append(", sh.cmf"); if (need_xyz) command_text.Append(", sh.xyz"); } if (search_type == SearchType.GROSS) command_text.Append(", gross"); if (search_type == SearchType.MASS) command_text.Append(", mass"); command_text.AppendFormat(" FROM {0} sh", _index_data.shadowTable); command_text.Append(table_copies); StringBuilder final_where_clause = new StringBuilder(); if (where_clause.Length > 0) { final_where_clause.Append(" ( "); final_where_clause.Append(where_clause); final_where_clause.Append(" ) "); } if (nextAfterStorageId != null) { if (final_where_clause.Length > 0) final_where_clause.Append(" and "); final_where_clause.Append(" ("); final_where_clause.AppendFormat(" storage_id > {0} ", nextAfterStorageId.Value); final_where_clause.Append(" )"); } if (final_where_clause.Length > 0) { command_text.Append(" WHERE "); command_text.Append(final_where_clause.ToString()); } if (nextAfterStorageId.HasValue) command_text.Append(" ORDER BY storage_id"); UTF8Encoding encoding = new UTF8Encoding(); using (SqlCommand cmd = new SqlCommand(command_text.ToString(), conn)) { cmd.CommandTimeout = 3600; using (SqlDataReader reader = cmd.ExecuteReader()) { byte[] xyz = new byte[0]; while (reader.Read()) { int id = Convert.ToInt32(reader[0]); int res; if (search_type == SearchType.EXACT) { byte[] cmf = (byte[])reader[1]; if (need_xyz) xyz = (byte[])reader[2]; res = BingoCore.lib.mangoMatchTargetBinary(cmf, cmf.Length, xyz, xyz.Length); } else if (search_type == SearchType.GROSS) { byte[] gross = encoding.GetBytes((string)reader[1]); res = BingoCore.lib.mangoMatchTarget(gross, gross.Length); } else if (search_type == SearchType.MASS) res = 1; else throw new Exception("Search type is undefined"); if (res == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (res == -1) throw new Exception(BingoCore.lib.bingoGetWarning()); if (res == 1) { FetchedData data = new FetchedData(id); if (search_type == SearchType.MASS) data.value = (float)reader[1]; yield return data; } } } } } void _prepareExactQueryStrings (ref string table_copies_str, ref string where_clause_str) { int hash_elements_count, hash, count; BingoCore.lib.mangoGetHash(false, -1, out hash_elements_count, out hash); StringBuilder table_copies = new StringBuilder(); for (int i = 0; i < hash_elements_count; i++) table_copies.AppendFormat(", {0} t{1}", _index_data.componentsTable, i); // Create complex WHERE clause StringBuilder where_clause = new StringBuilder(); bool where_was_added = false; if (hash_elements_count > 0) { where_was_added = true; // molecule ids must be same where_clause.Append("sh.id = t0.id and "); for (int i = 1; i < hash_elements_count; i++) where_clause.AppendFormat("t{0}.id = t{1}.id and ", i - 1, i); // query components must match target components for (int i = 0; i < hash_elements_count; i++) { BingoCore.lib.mangoGetHash(false, i, out count, out hash); where_clause.AppendFormat("t{0}.hash = {1} and ", i, hash); } // components count mast must target components count string rel; if (BingoCore.lib.mangoExactNeedComponentMatching()) rel = ">="; else rel = "="; for (int i = 0; i < hash_elements_count; i++) { if (i != 0) where_clause.Append("and "); BingoCore.lib.mangoGetHash(false, i, out count, out hash); where_clause.AppendFormat("t{0}.count {1} {2} ", i, rel, count); } } if (!BingoCore.lib.mangoExactNeedComponentMatching()) { if (where_was_added) where_clause.Append("and "); // There must be no other components in target int query_fragments_count = 0; for (int i = 0; i < hash_elements_count; i++) { BingoCore.lib.mangoGetHash(false, i, out count, out hash); query_fragments_count += count; } where_clause.AppendFormat("sh.fragments = {0}", query_fragments_count); } table_copies_str = table_copies.ToString(); where_clause_str = where_clause.ToString(); } } }Indigo-indigo-1.2.3/bingo/sqlserver/Source/RingoFastIndexFetch.cs000066400000000000000000000047201271037650300247550ustar00rootroot00000000000000using System.IO; using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Runtime.Serialization.Formatters.Binary; namespace indigo { public class RingoFastIndexFetch { RingoIndexData _index_data; bool highlighting; public int? nextAfterStorageId { get; set; } public RingoFastIndexFetch (RingoIndexData index_data) { _index_data = index_data; } public void prepareSub (string query, string options, bool highlighting, bool smarts) { int res = BingoCore.lib.ringoSetupMatch(smarts ? "RSMARTS" : "RSUB", query, options); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); BingoCore.lib.ringoSetHightlightingMode(highlighting ? 1 : 0); this.highlighting = highlighting; } public IEnumerable fetch (SqlConnection conn) { byte[] fp; BingoCore.ringoGetQueryFingerprint(out fp); // Search using fast index _index_data.fingerprints.init(conn); _index_data.storage.validate(conn); IEnumerable screened; if (!_index_data.fingerprints.ableToScreen(fp)) screened = _index_data.storage.enumerateStorageIds(nextAfterStorageId); else screened = _index_data.fingerprints.screenSub(conn, fp, nextAfterStorageId); int cache_index = 0; foreach (int storage_id in screened) { if (_index_data.storage.isDeleted(storage_id, conn, ref cache_index)) continue; byte[] data_with_cmf = _index_data.storage.get(storage_id, 4, -1, conn, ref cache_index); int ret = BingoCore.lib.ringoMatchTargetBinary(data_with_cmf, data_with_cmf.Length); if (ret == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (ret == -1) throw new Exception(BingoCore.lib.bingoGetWarning()); if (ret == 1) { int id = _index_data.storage.getInt(storage_id, 0, conn, ref cache_index); FetchedData data = new FetchedData(id); if (highlighting) data.str = BingoCore.ringoGetHightlightedReaction(); yield return data; } } } } }Indigo-indigo-1.2.3/bingo/sqlserver/Source/RingoIndex.cs000066400000000000000000000017671271037650300231750ustar00rootroot00000000000000using System; using System.Runtime.InteropServices; namespace indigo { public class RingoIndex { public float mass; public byte[] crf; public byte[] fingerprint; public int hash; public bool readPrepared (out int id) { IntPtr crf_ptr, fingerprint_ptr; int crf_buf_len, fingerprint_buf_len; int ret = BingoCore.lib.ringoIndexReadPreparedReaction(out id, out crf_ptr, out crf_buf_len, out fingerprint_ptr, out fingerprint_buf_len); BingoCore.lib.ringoGetHash(true, out hash); if (ret == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (ret == -1) return false; crf = new byte[crf_buf_len]; fingerprint = new byte[fingerprint_buf_len]; Marshal.Copy(crf_ptr, crf, 0, crf_buf_len); Marshal.Copy(fingerprint_ptr, fingerprint, 0, fingerprint_buf_len); return true; } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/RingoIndexData.cs000066400000000000000000000126331271037650300237610ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; namespace indigo { public class RingoIndexData : BingoIndexData { object _sync_object = new Object(); public RingoIndexData (BingoIndexID id, string id_column, string data_column, string bingo_schema) : base(id, id_column, data_column, bingo_schema) { } public override IndexType getIndexType () { return IndexType.Reaction; } public override void CreateTables (SqlConnection conn) { base.CreateTables(conn); StringBuilder cmd = new StringBuilder(); // Create shadow table cmd.AppendFormat(@"CREATE TABLE {0} (id int not null, storage_id int not null, crf varbinary(max), hash int not null)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, cmd.ToString()); } public override void createIndices (SqlConnection conn) { BingoSqlUtils.ExecNonQuery(conn, "ALTER TABLE {0} ADD PRIMARY KEY (storage_id)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE UNIQUE INDEX id ON {0}(id)", shadowTable); BingoSqlUtils.ExecNonQuery(conn, "CREATE INDEX hash ON {0}(hash)", shadowTable); } public override void DropTables (SqlConnection conn) { base.DropTables(conn); BingoSqlUtils.ExecNonQueryNoThrow(conn, "DROP TABLE " + shadowTable); } DataTable shadow_datatable = null; public void addToShadowTable (SqlConnection conn, RingoIndex index, int id, int storage_id) { lock (_sync_object) { if (shadow_datatable == null) _createDataTable(); if (shadow_datatable.Rows.Count >= 10000) _flushShadowTable(conn); DataRow shadow_row = shadow_datatable.NewRow(); shadow_row["id"] = id; shadow_row["storage_id"] = storage_id; shadow_row["crf"] = index.crf; shadow_row["hash"] = index.hash; shadow_datatable.Rows.Add(shadow_row); } } private void _createDataTable () { shadow_datatable = new DataTable(); DataColumnCollection sc = shadow_datatable.Columns; sc.Add(new DataColumn("id", Type.GetType("System.Int32"))); sc.Add(new DataColumn("storage_id", Type.GetType("System.Int32"))); sc.Add(new DataColumn("crf", Type.GetType("System.Array"))); sc.Add(new DataColumn("hash", Type.GetType("System.Int32"))); } public override bool needFlush() { lock (_sync_object) { if (base.needFlush()) return true; return shadow_datatable != null && shadow_datatable.Rows.Count > 0; } } public override void flush(SqlConnection conn) { lock (_sync_object) { base.flush(conn); _flushShadowTable(conn); } } private void _flushShadowTable (SqlConnection conn) { if (shadow_datatable == null || shadow_datatable.Rows.Count == 0) return; if (conn.ConnectionString == "context connection=true") { // SqlBulkInsert cannot be used in the context connection _flushShadowTableInContext(conn); return; } BingoTimer timer = new BingoTimer("shadow_table.flush"); using (SqlTransaction transaction = conn.BeginTransaction()) { // Copy shadow table using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, transaction)) { bulkCopy.DestinationTableName = shadowTable; foreach (DataColumn dc in shadow_datatable.Columns) bulkCopy.ColumnMappings.Add(dc.ColumnName, dc.ColumnName); bulkCopy.BatchSize = shadow_datatable.Rows.Count; bulkCopy.BulkCopyTimeout = 3600; bulkCopy.WriteToServer(shadow_datatable); } shadow_datatable.Rows.Clear(); transaction.Commit(); } timer.end(); } private void _flushShadowTableInContext (SqlConnection conn) { foreach (DataRow row in shadow_datatable.Rows) { using (SqlCommand cmd = new SqlCommand()) { cmd.CommandTimeout = 3600; StringBuilder cmd_text = new StringBuilder(); cmd_text.AppendFormat("INSERT INTO {0} VALUES ", shadowTable); cmd_text.AppendFormat("({0}, {1}, @crf, {2})", row["id"], row["storage_id"], row["hash"]); cmd.Parameters.AddWithValue("@crf", new SqlBinary((byte[])row["crf"])); cmd.Connection = conn; cmd.CommandText = cmd_text.ToString(); cmd.ExecuteNonQuery(); } } shadow_datatable.Rows.Clear(); } public override void prepareForDeleteRecord (SqlConnection conn) { lock (_sync_object) { _flushShadowTable(conn); } } } } Indigo-indigo-1.2.3/bingo/sqlserver/Source/RingoShadowFetch.cs000066400000000000000000000067151271037650300243230ustar00rootroot00000000000000using System.IO; using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Globalization; namespace indigo { public class RingoShadowFetch { RingoIndexData _index_data; enum SearchType { UNDEF, EXACT }; SearchType search_type = SearchType.UNDEF; string where_clause; public int? nextAfterStorageId { get; set; } public RingoShadowFetch (RingoIndexData index_data) { _index_data = index_data; } public void prepareExact (string query, string options) { int res = BingoCore.lib.ringoSetupMatch("REXACT", query, options); if (res < 0) throw new Exception(BingoCore.lib.bingoGetError()); int query_hash; BingoCore.lib.ringoGetHash(false, out query_hash); where_clause = String.Format("hash = {0}", query_hash); search_type = SearchType.EXACT; } public IEnumerable fetch (SqlConnection conn) { ArrayList res_list = new ArrayList(); StringBuilder command_text = new StringBuilder(); command_text.Append("SELECT sh.id"); if (search_type == SearchType.EXACT) { command_text.Append(", sh.crf"); } command_text.AppendFormat(" FROM {0} sh", _index_data.shadowTable); StringBuilder final_where_clause = new StringBuilder(); if (where_clause.Length > 0) { final_where_clause.Append(" ( "); final_where_clause.Append(where_clause); final_where_clause.Append(" ) "); } if (nextAfterStorageId != null) { if (final_where_clause.Length > 0) final_where_clause.Append(" and "); final_where_clause.Append(" ("); final_where_clause.AppendFormat(" storage_id > {0} ", nextAfterStorageId.Value); final_where_clause.Append(" )"); } if (final_where_clause.Length > 0) { command_text.Append(" WHERE "); command_text.Append(final_where_clause.ToString()); } if (nextAfterStorageId.HasValue) command_text.Append(" ORDER BY storage_id"); UTF8Encoding encoding = new UTF8Encoding(); using (SqlCommand cmd = new SqlCommand(command_text.ToString(), conn)) { cmd.CommandTimeout = 3600; using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { int id = Convert.ToInt32(reader[0]); int res; if (search_type == SearchType.EXACT) { byte[] crf = (byte[])reader[1]; res = BingoCore.lib.ringoMatchTargetBinary(crf, crf.Length); } else throw new Exception("Search type is undefined"); if (res == -2) throw new Exception(BingoCore.lib.bingoGetError()); if (res == -1) throw new Exception(BingoCore.lib.bingoGetWarning()); if (res == 1) { FetchedData data = new FetchedData(id); yield return data; } } } } } } }Indigo-indigo-1.2.3/bingo/sqlserver/Source/SqlAttributes.cs000066400000000000000000000014641271037650300237270ustar00rootroot00000000000000using System; using Microsoft.SqlServer.Server; namespace indigo.SqlAttributes { public enum AccessLevelKind { None, Reader, Operator }; public class BingoSqlFunctionAttribute : Attribute { public AccessLevelKind access_level { get; set; } public bool substitute_bingo { get; set; } /* Parameter name that should converted to binary */ public string str_bin { get; set; } public BingoSqlFunctionAttribute () { access_level = AccessLevelKind.Operator; substitute_bingo = true; str_bin = null; } } public class BingoSqlFunctionForReaderAttribute : BingoSqlFunctionAttribute { public BingoSqlFunctionForReaderAttribute() { access_level = AccessLevelKind.Reader; } } } Indigo-indigo-1.2.3/bingo/sqlserver/bingo-sqlserver.csproj000066400000000000000000000153121271037650300236730ustar00rootroot00000000000000 Debug AnyCPU {c252feb5-a946-4202-b1d4-9916a0590387};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 9.0.30729 2.0 {ED96DA19-3326-4DFB-9CB1-189895A367E5} Library false bingo-sqlserver 512 indigo 3 Always true indigo.snk v2.0 3.5 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ false DEBUG;TRACE 4 true AnyCPU AllRules.ruleset true true bin\Release\ false TRACE 4 pdbonly true AllRules.ruleset True True resource.resx Code del $(ProjectDir)sql\assembly\$(TargetName).* copy $(TargetDir)$(TargetName).* $(ProjectDir)sql\assembly\ IF EXIST $(DllPath32)\bingo-core-c.dll (copy /Y $(DllPath32)\bingo-core-c.dll $(ProjectDir)Resources\Win\x86) IF EXIST $(DllPath64)\bingo-core-c.dll (copy /Y $(DllPath64)\bingo-core-c.dll $(ProjectDir)Resources\Win\x64) ResXFileCodeGenerator resource.Designer.cs Designer False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 true False Windows Installer 3.1 true Indigo-indigo-1.2.3/bingo/sqlserver/bingo-sqlserver.sln000066400000000000000000000030651271037650300231710ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bingo-sqlserver", "bingo-sqlserver.csproj", "{ED96DA19-3326-4DFB-9CB1-189895A367E5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sqlserver_script_generator", "sqlserver_script_generator\sqlserver_script_generator.csproj", "{BBB579CB-8C95-4A28-8950-348CF56104AB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Release|Any CPU.Build.0 = Release|Any CPU {ED96DA19-3326-4DFB-9CB1-189895A367E5}.Release|Any CPU.Deploy.0 = Release|Any CPU {BBB579CB-8C95-4A28-8950-348CF56104AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BBB579CB-8C95-4A28-8950-348CF56104AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {BBB579CB-8C95-4A28-8950-348CF56104AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {BBB579CB-8C95-4A28-8950-348CF56104AB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal Indigo-indigo-1.2.3/bingo/sqlserver/indigo.snk000066400000000000000000000011241271037650300213110ustar00rootroot00000000000000$RSA27÷ë‘? ‰Ê÷ê;ÿvëOŒÉç’B¡lPD:,:´Fæcº9Ÿliñ£†ºf—íTHùIÍuƒ‡¿µ÷—déÞ±m*¦Øb\ ÍS–a“¹•ÅÉù¤¤2ó㣮%<ÁÖ: ìc–¬GT{ÏÊc}¤§7j”†1v0š Þÿ¹``‘öÙ˯cý‰ÌÙlQËõ²IvžË_¤–3ùŒÊ®Öõ…ƒ‹¬îN˜¬ñ«HÓÄU/:|Òô%~ìJÄgÉÉXHá :¢j5K¯X{[…äø?e§M=Fwìœÿøý‘ºiƒ±ö8ìÕ/Α}êà½v‹=¾–Ë©È+ÞD5+ëwØCûEœê~rÓWè‚ØaP-\óÛV94ß­ D6¯j” ¶îçU-«Ñ–øüPˆ XùÚÔŠ9{u á–ŒâÀ¸¥Èjâ*\÷° ¸{Š-á±Ú0áÊ︔\ê.Äþy‹ -7ìL‰ïô 6QÚÔg\Õ";&½’ÿÖWCj¢Ç!ò>Õ*:2B&‚>Üp\,Šöžp"‘³P€-:Bòv³!Ž ì²mˆ:ýMA“ðÀWΖƒªr6Ðê­,%G¼õ´ºO˜jØ x2U¸@&ÔÒŠS¹£ ÄL(¬âø¾‡>¦¾HÔ#„2}‘ÎíÅ-˜@k¨ßѱ†Mß Íº±§½p³ÜFgÔCìÍó¯€ö†ïÞˆi‘Ü žæ%äÅ»Indigo-indigo-1.2.3/bingo/sqlserver/resource.Designer.cs000066400000000000000000000137471271037650300232560ustar00rootroot00000000000000//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.208 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace indigo { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class resource { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal resource() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("indigo.resource", typeof(resource).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to CREATE TRIGGER {0} /// ON {1} /// FOR DELETE ///AS /// -- Create global cursor for deleted records /// -- Simultaneous inserts from different sessions are not supported /// declare [bingotmpcur_del_{5}_{6}] cursor GLOBAL FORWARD_ONLY READ_ONLY for select {2} from deleted /// open [bingotmpcur_del_{5}_{6}] /// /// -- Cursor will be deallocated in the _OnInsertRecordTrigger automatically /// exec {4}._OnDeleteRecordTrigger {5}, {6}, '[bingotmpcur_del_{5}_{6}]' ///. /// internal static string OnDeleteTrigger { get { return ResourceManager.GetString("OnDeleteTrigger", resourceCulture); } } /// /// Looks up a localized string similar to CREATE TRIGGER {0} /// ON {1} /// FOR INSERT ///AS /// -- Create global cursor for inserted records /// -- Simultaneous inserts from different sessions are not supported /// declare [bingotmpcur_{5}_{6}] cursor GLOBAL FORWARD_ONLY READ_ONLY for select {2}, {3} from inserted /// open [bingotmpcur_{5}_{6}] /// /// -- Cursor will be deallocated in the _OnInsertRecordTrigger automatically /// exec {4}._OnInsertRecordTrigger {5}, {6}, '[bingotmpcur_{5}_{6}]' ///. /// internal static string OnInsertTrigger { get { return ResourceManager.GetString("OnInsertTrigger", resourceCulture); } } /// /// Looks up a localized string similar to CREATE TRIGGER {0} /// ON {1} /// FOR UPDATE ///AS /// IF UPDATE({2}) /// BEGIN /// RAISERROR ('{2} column cannot be changed after Bingo index has been created.', 16, 1); /// ROLLBACK TRANSACTION /// END /// /// IF NOT UPDATE({3}) /// BEGIN /// RETURN /// END /// /// -- At first delete records and then insert them /// /// -- Create global cursor for deleted records /// -- Simultaneous inserts from different sessions are not supported /// declare [bingotmpcur_upd_{5}_{6}] cursor GLOBAL FORWARD_ONLY READ_ONLY for select {2} from inserte [rest of string was truncated]";. /// internal static string OnUpdateTrigger { get { return ResourceManager.GetString("OnUpdateTrigger", resourceCulture); } } internal static byte[] Win_x64_bingo_core_c_dll { get { object obj = ResourceManager.GetObject("Win/x64/bingo-core-c.dll", resourceCulture); return ((byte[])(obj)); } } internal static byte[] Win_x86_bingo_core_c_dll { get { object obj = ResourceManager.GetObject("Win/x86/bingo-core-c.dll", resourceCulture); return ((byte[])(obj)); } } } } Indigo-indigo-1.2.3/bingo/sqlserver/resource.resx000066400000000000000000000161041271037650300220610ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Resources\Win\x86\bingo-core-c.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Resources\Win\x64\bingo-core-c.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Resources\OnDeleteTrigger.sql;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1251 Resources\OnInsertTrigger.sql;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1251 Resources\OnUpdateTrigger.sql;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1251 Indigo-indigo-1.2.3/bingo/sqlserver/sql/000077500000000000000000000000001271037650300201245ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/sql/assembly/000077500000000000000000000000001271037650300217435ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/sql/assembly/readme.txt000066400000000000000000000001111271037650300237320ustar00rootroot00000000000000Bingo assembly for MS SQL Server is placed here automatically after buildIndigo-indigo-1.2.3/bingo/sqlserver/sql/bingo-sqlserver-install.bat000066400000000000000000000100121271037650300253740ustar00rootroot00000000000000@rem Copyright (C) 2009-2015 EPAM Systems @rem @rem This file is part of Indigo toolkit. @rem @rem This file may be distributed and/or modified under the terms of the @rem GNU General Public License version 3 as published by the Free Software @rem Foundation and appearing in the file LICENSE.GPL included in the @rem packaging of this file. @rem @rem This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE @rem WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @echo off set dbaname= set dbapass= set database= set bingoname=bingo set bingopass=B!ngoB!ngo set y= set server= if exist bingo_saved_params.bat call bingo_saved_params.bat goto L2 :L1 shift :L2 if "%1" == "" goto L3 if "%1" == "-dbaname" goto got_dbaname if "%1" == "-dbapass" goto got_dbapass if "%1" == "-database" goto got_database if "%1" == "-bingoname" goto got_bingoname if "%1" == "-bingopass" goto got_bingopass if "%1" == "-server" goto got_server if "%1" == "-y" goto got_y if "%1" == "-help" goto usage if "%1" == "-?" goto usage if "%1" == "/?" goto usage goto badparam :got_dbaname shift set dbaname=%1 goto L1 :got_dbapass shift set dbapass=%1 goto L1 :got_database shift set database=%1 goto L1 :got_server shift set server=%1 goto L1 :got_bingoname shift set bingoname=%1 goto L1 :got_bingopass shift set bingopass=%1 goto L1 :got_y set y=1 goto L1 :badparam echo Unknown parameter: %1 goto end :L3 if "%database%" == "" goto no_database if "%dbaname%" == "" goto no_dbaname :L5 goto confirm :no_database echo Database not specified (use -database) goto end :no_dbaname if "%dbapass%" == "" goto L5 echo DBA password specfied without a DBA name (use -dbaname) goto end :confirm rem Save existing parameters echo set dbaname=%dbaname%>> bingo_saved_params.bat echo set dbapass=%dbapass%>> bingo_saved_params.bat echo set database=%database%>> bingo_saved_params.bat echo set server=%server%>> bingo_saved_params.bat echo set bingoname=%bingoname%>> bingo_saved_params.bat echo set bingopass=%bingopass%>> bingo_saved_params.bat if not "%dbaname%" == "" echo DBA name : %dbaname% if not "%dbapass%" == "" echo DBA password : %dbapass% if not "%server%" == "" echo Server : %server% echo Database : %database% echo Bingo name : %bingoname% echo Bingo password : %bingopass% if "%y%"=="1" goto L4 set /p proceed=Proceed (y/N)? if "%proceed%"=="y" goto L4 if "%proceed%"=="Y" goto L4 echo Aborting goto end :L4 rem Execute sql stripts set bingo_sqlcmd_arguments=-vbingo=%bingoname% -vbingo_pass=%bingopass% -b -vdatabase=%database% -vbingo_assembly_path="%CD%\assembly\bingo-sqlserver" -e if not "%dbaname%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -U %dbaname% if not "%dbapass%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -P %dbapass% if not "%server%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -S %server% sqlcmd -i bingo_create.sql %bingo_sqlcmd_arguments% goto end :usage echo Usage: bingo-sqlserver-install.bat [parameters] echo Parameters: echo -?, -help echo Print this help message echo -server name echo SQL Server name (default is local SQL Server) echo -database database (obligatory) echo Database to install on. echo -dbaname name echo Database administrator login (default is current user). echo -dbapass password echo Database administrator password. echo If the password is not specified, you will have to enter it later. echo -bingoname name echo Name of cartridge pseudo-user (default "bingo"). echo -bingopass password echo Password of the pseudo-user (default "bingo"). echo -y echo Do not ask for confirmation. goto end :end set dbaname= set dbapass= set instance= set bingoname= set bingopass= set database= set y= set bingo_sqlcmd_arguments= set server= Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo-sqlserver-uninstall.bat000066400000000000000000000073421271037650300257530ustar00rootroot00000000000000@rem Copyright (C) 2009-2015 EPAM Systems @rem @rem This file is part of Indigo toolkit. @rem @rem This file may be distributed and/or modified under the terms of the @rem GNU General Public License version 3 as published by the Free Software @rem Foundation and appearing in the file LICENSE.GPL included in the @rem packaging of this file. @rem @rem This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE @rem WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. @echo off set dbaname= set dbapass= set database= set bingoname=bingo set y= set server= set fulldelete=0 if exist bingo_saved_params.bat call bingo_saved_params.bat goto L2 :L1 shift :L2 if "%1" == "" goto L3 if "%1" == "-dbaname" goto got_dbaname if "%1" == "-dbapass" goto got_dbapass if "%1" == "-database" goto got_database if "%1" == "-bingoname" goto got_bingoname if "%1" == "-server" goto got_server if "%1" == "-y" goto got_y if "%1" == "-fulldelete" goto got_fulldelete if "%1" == "-help" goto usage if "%1" == "-?" goto usage if "%1" == "/?" goto usage goto badparam :got_dbaname shift set dbaname=%1 goto L1 :got_dbapass shift set dbapass=%1 goto L1 :got_database shift set database=%1 goto L1 :got_server shift set server=%1 goto L1 :got_bingoname shift set bingoname=%1 goto L1 :got_bingopass shift set bingopass=%1 goto L1 :got_y set y=1 goto L1 :got_fulldelete set fulldelete=1 goto L1 :badparam echo Unknown parameter: %1 goto end :L3 if "%database%" == "" goto no_database if "%dbaname%" == "" goto no_dbaname :L5 goto confirm :no_database echo Database not specified (use -database) goto end :no_dbaname if "%dbapass%" == "" goto L5 echo DBA password specfied without a DBA name (use -dbaname) goto end :confirm if not "%dbaname%" == "" echo DBA name : %dbaname% if not "%dbapass%" == "" echo DBA password : %dbapass% if not "%server%" == "" echo Server : %server% echo Database : %database% echo Bingo name : %bingoname% if "%fulldelete%" == "1" echo Full delete : True if "%fulldelete%" == "0" echo Full delete : False if "%y%"=="1" goto L4 set /p proceed=Proceed (y/N)? if "%proceed%"=="y" goto L4 if "%proceed%"=="Y" goto L4 echo Aborting goto end :L4 set bingo_sqlcmd_arguments= -vbingo=%bingoname% -vdatabase=%database% -vfulldelete=%fulldelete% if not "%dbaname%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -U %dbaname% if not "%dbapass%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -P %dbapass% if not "%server%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -S %server% sqlcmd -i bingo_drop.sql %bingo_sqlcmd_arguments% goto end :usage echo Usage: bingo-sqlserver-uninstall.bat [parameters] echo Parameters: echo -?, -help echo Print this help message echo -server name echo SQL Server name (default is local SQL Server) echo -database database (obligatory) echo Database to remove Bingo from. echo -dbaname name echo Database administrator login (default is current user). echo -dbapass password echo Database administrator password. echo If the password is not specified, you will have to enter it later. echo -bingoname name echo Name of cartridge pseudo-user (default "bingo"). echo -y echo Do not ask for confirmation. echo -fulldelete echo Delete all Bingo-dependant objects that are used between different Bingo installations. goto end :end set dbaname= set dbapass= set instance= set bingoname= set bingopass= set database= set y= set bingo_sqlcmd_arguments= set server= Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo_create.sql000066400000000000000000000150661271037650300232760ustar00rootroot00000000000000-- Enable CLR USE master GO sp_configure 'clr enabled', 1; GO RECONFIGURE GO if (select is_broker_enabled from sys.databases where name='$(database)') = 0 ALTER DATABASE $(database) SET ENABLE_BROKER go -- Create key for adding unsafe assembly. It might be already created for different installation BEGIN TRY CREATE ASYMMETRIC KEY bingo_assembly_key FROM EXECUTABLE FILE = '$(bingo_assembly_path).dll' CREATE LOGIN bingo_assembly_login FROM ASYMMETRIC KEY bingo_assembly_key GRANT UNSAFE ASSEMBLY TO bingo_assembly_login END TRY BEGIN CATCH PRINT ERROR_NUMBER(); PRINT ERROR_MESSAGE(); END CATCH; GO use $(database) go PRINT 'Creating certificate...'; CREATE CERTIFICATE $(bingo)_certificate ENCRYPTION BY PASSWORD = '$(bingo_pass)' WITH SUBJECT = 'Certificate for bingo procedures executions', START_DATE = '20020101', EXPIRY_DATE = '21000101' GO -- Create a bingo user PRINT 'Creating bingo user with schema...'; CREATE USER $(bingo) FROM CERTIFICATE $(bingo)_certificate; go CREATE SCHEMA $(bingo); go ALTER AUTHORIZATION ON SCHEMA::$(bingo) TO $(bingo) go GRANT create table TO $(bingo); go -- Create tables for bingo user create table [$(bingo)].CONFIG (n int not null, name varchar(100) not null, value varchar(4000), primary key(n, name)); create index CONFIG_N on [$(bingo)].CONFIG(n); insert into [$(bingo)].CONFIG values(0, 'treat-x-as-pseudoatom', '0'); insert into [$(bingo)].CONFIG values(0, 'ignore-closing-bond-direction-mismatch', '0'); insert into [$(bingo)].CONFIG values(0, 'nthreads', '-1'); insert into [$(bingo)].CONFIG values(0, 'ignore-stereocenter-errors', 0); insert into [$(bingo)].CONFIG values(0, 'ignore-cistrans-errors', 0); insert into [$(bingo)].CONFIG values(0, 'allow-non-unique-dearomatization', 0); insert into [$(bingo)].CONFIG values(0, 'zero-unknown-aromatic-hydrogens', 0); insert into [$(bingo)].CONFIG values(0, 'stereochemistry-bidirectional-mode', 0); insert into [$(bingo)].CONFIG values(0, 'stereochemistry-detect-haworth-projection', 0); insert into [$(bingo)].CONFIG values(0, 'reject-invalid-structures', 0); go insert into [$(bingo)].CONFIG values(0, 'FP_ORD_SIZE', '25'); insert into [$(bingo)].CONFIG values(0, 'FP_ANY_SIZE', '15'); insert into [$(bingo)].CONFIG values(0, 'FP_TAU_SIZE', '10'); insert into [$(bingo)].CONFIG values(0, 'FP_SIM_SIZE', '8'); insert into [$(bingo)].CONFIG values(0, 'SUB_SCREENING_MAX_BITS', '8'); insert into [$(bingo)].CONFIG values(0, 'KEEP_CACHE', '0'); insert into [$(bingo)].CONFIG values(0, 'SIM_SCREENING_PASS_MARK', '128'); go create table [$(bingo)].CONFIG_BIN (n int not null, name varchar(100) not null, value varbinary(max), primary key(n, name)); create index CONFIG_BIN_N on [$(bingo)].CONFIG_BIN(n); go -- Create context create table [$(bingo)].CONTEXT (obj_id int not null, database_id int not null, full_table_name varchar(100), id_column varchar(100), data_column varchar(100), type varchar(100), primary key (obj_id, database_id)); create index CONTEXT_ID on [$(bingo)].CONTEXT(obj_id); go -- Create table with tautomer rules create table [$(bingo)].TAUTOMER_RULES (id int identity primary key, begg varchar(100), endd varchar(100)); insert into [$(bingo)].TAUTOMER_RULES(begg, endd) values ('N,O,P,S,As,Se,Sb,Te', 'N,O,P,S,As,Se,Sb,Te'); insert into [$(bingo)].TAUTOMER_RULES(begg, endd) values ('0C', 'N,O,P,S'); insert into [$(bingo)].TAUTOMER_RULES(begg, endd) values ('1C', 'N,O'); go -- Create roles CREATE ROLE $(bingo)_reader; GO CREATE ROLE $(bingo)_operator; GO -- Operator role inherts reader role EXEC sp_addrolemember $(bingo)_reader, $(bingo)_operator GO -- -- Create assembly -- CREATE ASSEMBLY $(bingo)_assembly from '$(bingo_assembly_path).dll' WITH PERMISSION_SET = UNSAFE; GO -- -- Create functions and procedures -- :r bingo_create_methods.sql -- -- Create notification queue for OnSessionClose() -- SET quoted_identifier on; GO CREATE PROCEDURE [$(bingo)].log_events AS SET NOCOUNT ON; DECLARE @message_body XML, @message_type_name NVARCHAR(256), @dialog UNIQUEIDENTIFIER -- This procedure continues to process messages in the queue until the queue is empty. WHILE (1 = 1) BEGIN BEGIN TRANSACTION ; -- Receive the next available message WAITFOR ( RECEIVE TOP(1) -- just handle one message at a time @message_type_name=message_type_name, --the type of message received @message_body=message_body, -- the message contents @dialog = conversation_handle -- the identifier of the dialog this message was received on FROM [$(bingo)].notify_queue ), TIMEOUT 1000; -- if the queue is empty for one seconds, give up and go away -- If RECEIVE did not return a message, roll back the transaction -- and break out of the while loop, exiting the procedure. IF (@@ROWCOUNT = 0) BEGIN ROLLBACK TRANSACTION ; BREAK ; END ; -- Check to see if the message is an end dialog message. IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog') BEGIN END CONVERSATION @dialog ; END ; ELSE BEGIN DECLARE @event_type NVARCHAR(max); SET @event_type = CAST(@message_body.query('/EVENT_INSTANCE/EventType/text()') AS nvarchar(max)); -- Release session IF (@event_type = 'AUDIT_LOGOUT') BEGIN DECLARE @spid nvarchar(max); SET @spid = @message_body.value('(/EVENT_INSTANCE/SPID)[1]', 'int') EXECUTE [$(bingo)].OnSessionClose @spid; END -- Delete Bingo index if table has been deleted IF (@event_type = 'OBJECT_DELETED') BEGIN DECLARE @obj_id int, @database_id int; SET @obj_id = @message_body.value('(/EVENT_INSTANCE/ObjectID)[1]', 'int') SET @database_id = @message_body.value('(/EVENT_INSTANCE/DatabaseID)[1]', 'int') EXECUTE [$(bingo)]._DropIndexByID @obj_id, @database_id END END ; COMMIT TRANSACTION ; END ; GO create queue [$(bingo)].notify_queue with activation ( status = on, procedure_name = [$(bingo)].log_events, max_queue_readers = 1, execute as self); GO create service $(bingo)_notify_service on queue [$(bingo)].notify_queue ( [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification] ); GO -- ALTER AUTHORIZATION ON SERVICE::$(bingo)_notify_service TO $(bingo) -- go CREATE ROUTE $(bingo)_notify_route AUTHORIZATION dbo WITH SERVICE_NAME = N'$(bingo)_notify_service', ADDRESS = N'LOCAL'; GO create event notification $(bingo)_$(database)_logout_notify on server for AUDIT_LOGOUT, OBJECT_DELETED to service '$(bingo)_notify_service', 'current database' GO PRINT 'Done.' GO Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo_create_methods.sql000066400000000000000000001527051271037650300250230ustar00rootroot00000000000000-- -- This file was generated automatically -- -- use $(database) go -- -- _CheckMemoryAllocate -- CREATE PROCEDURE [$(bingo)].z__CheckMemoryAllocate @dotnet_size_mb int, @block_size_mb int, @core_size_mb int, @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._CheckMemoryAllocate GO ADD SIGNATURE TO [$(bingo)].z__CheckMemoryAllocate BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._CheckMemoryAllocate @dotnet_size_mb int, @block_size_mb int, @core_size_mb int AS BEGIN EXEC [$(bingo)].z__CheckMemoryAllocate @dotnet_size_mb, @block_size_mb, @core_size_mb, '$(bingo)' END GO grant execute on [$(bingo)]._CheckMemoryAllocate to $(bingo)_operator GO -- -- _DropAllIndices -- CREATE PROCEDURE [$(bingo)].z__DropAllIndices @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._DropAllIndices GO ADD SIGNATURE TO [$(bingo)].z__DropAllIndices BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._DropAllIndices AS BEGIN EXEC [$(bingo)].z__DropAllIndices '$(bingo)' END GO -- -- _DropIndexByID -- CREATE PROCEDURE [$(bingo)].z__DropIndexByID @table_id int, @database_id int, @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._DropIndexByID GO ADD SIGNATURE TO [$(bingo)].z__DropIndexByID BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._DropIndexByID @table_id int, @database_id int AS BEGIN EXEC [$(bingo)].z__DropIndexByID @table_id, @database_id, '$(bingo)' END GO grant execute on [$(bingo)]._DropIndexByID to $(bingo)_operator GO -- -- _FlushInAllSessions -- CREATE PROCEDURE [$(bingo)].z__FlushInAllSessions @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._FlushInAllSessions GO ADD SIGNATURE TO [$(bingo)].z__FlushInAllSessions BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._FlushInAllSessions AS BEGIN EXEC [$(bingo)].z__FlushInAllSessions '$(bingo)' END GO -- -- _ForceGC -- CREATE PROCEDURE [$(bingo)].z__ForceGC AS EXTERNAL NAME [$(bingo)_assembly].[indigo.BingoSqlUtils]._ForceGC GO ADD SIGNATURE TO [$(bingo)].z__ForceGC BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._ForceGC AS BEGIN EXEC [$(bingo)].z__ForceGC END GO -- -- _OnDeleteRecordTrigger -- CREATE PROCEDURE [$(bingo)].z__OnDeleteRecordTrigger @table_id int, @database_id int, @tmp_table_name nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._OnDeleteRecordTrigger GO ADD SIGNATURE TO [$(bingo)].z__OnDeleteRecordTrigger BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._OnDeleteRecordTrigger @table_id int, @database_id int, @tmp_table_name nvarchar(max) AS BEGIN EXEC [$(bingo)].z__OnDeleteRecordTrigger @table_id, @database_id, @tmp_table_name, '$(bingo)' END GO grant execute on [$(bingo)]._OnDeleteRecordTrigger to $(bingo)_operator GO -- -- _OnInsertRecordTrigger -- CREATE PROCEDURE [$(bingo)].z__OnInsertRecordTrigger @table_id int, @database_id int, @tmp_table_name nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo]._OnInsertRecordTrigger GO ADD SIGNATURE TO [$(bingo)].z__OnInsertRecordTrigger BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._OnInsertRecordTrigger @table_id int, @database_id int, @tmp_table_name nvarchar(max) AS BEGIN EXEC [$(bingo)].z__OnInsertRecordTrigger @table_id, @database_id, @tmp_table_name, '$(bingo)' END GO grant execute on [$(bingo)]._OnInsertRecordTrigger to $(bingo)_operator GO -- -- _WriteLog -- CREATE PROCEDURE [$(bingo)].z__WriteLog @message nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.BingoLog]._WriteLog GO ADD SIGNATURE TO [$(bingo)].z__WriteLog BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)]._WriteLog @message nvarchar(max) AS BEGIN EXEC [$(bingo)].z__WriteLog @message END GO -- -- AAM -- CREATE FUNCTION [$(bingo)].z_AAM ( @reaction varbinary(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].AAM GO ADD SIGNATURE TO [$(bingo)].z_AAM BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].AAM ( @reaction varchar(max), @options nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_AAM (cast(@reaction as VARBINARY(max)), @options, '$(bingo)') END GO grant execute on [$(bingo)].AAM to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].AAMB ( @reaction varbinary(max), @options nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_AAM (@reaction, @options, '$(bingo)') END GO grant execute on [$(bingo)].AAMB to $(bingo)_reader GO -- -- CanSmiles -- CREATE FUNCTION [$(bingo)].z_CanSmiles ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CanSmiles GO ADD SIGNATURE TO [$(bingo)].z_CanSmiles BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CanSmiles ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CanSmiles (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].CanSmiles to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CanSmilesB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CanSmiles (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].CanSmilesB to $(bingo)_reader GO -- -- CheckMolecule -- CREATE FUNCTION [$(bingo)].z_CheckMolecule ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CheckMolecule GO ADD SIGNATURE TO [$(bingo)].z_CheckMolecule BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CheckMolecule ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CheckMolecule (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].CheckMolecule to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CheckMoleculeB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CheckMolecule (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].CheckMoleculeB to $(bingo)_reader GO -- -- CheckMoleculeTable -- CREATE FUNCTION [$(bingo)].z_CheckMoleculeTable ( @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, msg nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CheckMoleculeTable GO ADD SIGNATURE TO [$(bingo)].z_CheckMoleculeTable BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CheckMoleculeTable ( @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_CheckMoleculeTable (@table, @id_column, @data_column, '$(bingo)')) GO grant select on [$(bingo)].CheckMoleculeTable to $(bingo)_reader GO -- -- CheckReaction -- CREATE FUNCTION [$(bingo)].z_CheckReaction ( @reaction varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CheckReaction GO ADD SIGNATURE TO [$(bingo)].z_CheckReaction BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CheckReaction ( @reaction varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CheckReaction (cast(@reaction as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].CheckReaction to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CheckReactionB ( @reaction varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CheckReaction (@reaction, '$(bingo)') END GO grant execute on [$(bingo)].CheckReactionB to $(bingo)_reader GO -- -- CheckReactionTable -- CREATE FUNCTION [$(bingo)].z_CheckReactionTable ( @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, msg nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CheckReactionTable GO ADD SIGNATURE TO [$(bingo)].z_CheckReactionTable BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CheckReactionTable ( @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_CheckReactionTable (@table, @id_column, @data_column, '$(bingo)')) GO grant select on [$(bingo)].CheckReactionTable to $(bingo)_reader GO -- -- CML -- CREATE FUNCTION [$(bingo)].z_CML ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CML GO ADD SIGNATURE TO [$(bingo)].z_CML BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CML ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CML (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].CML to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CMLB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_CML (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].CMLB to $(bingo)_reader GO -- -- CompactMolecule -- CREATE FUNCTION [$(bingo)].z_CompactMolecule ( @molecule varbinary(max), @save_xyz bit, @bingo_schema nvarchar(max) ) RETURNS varbinary(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CompactMolecule GO ADD SIGNATURE TO [$(bingo)].z_CompactMolecule BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CompactMolecule ( @molecule varchar(max), @save_xyz bit ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_CompactMolecule (cast(@molecule as VARBINARY(max)), @save_xyz, '$(bingo)') END GO grant execute on [$(bingo)].CompactMolecule to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CompactMoleculeB ( @molecule varbinary(max), @save_xyz bit ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_CompactMolecule (@molecule, @save_xyz, '$(bingo)') END GO grant execute on [$(bingo)].CompactMoleculeB to $(bingo)_reader GO -- -- CompactReaction -- CREATE FUNCTION [$(bingo)].z_CompactReaction ( @reaction varbinary(max), @save_xyz bit, @bingo_schema nvarchar(max) ) RETURNS varbinary(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CompactReaction GO ADD SIGNATURE TO [$(bingo)].z_CompactReaction BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].CompactReaction ( @reaction varchar(max), @save_xyz bit ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_CompactReaction (cast(@reaction as VARBINARY(max)), @save_xyz, '$(bingo)') END GO grant execute on [$(bingo)].CompactReaction to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].CompactReactionB ( @reaction varbinary(max), @save_xyz bit ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_CompactReaction (@reaction, @save_xyz, '$(bingo)') END GO grant execute on [$(bingo)].CompactReactionB to $(bingo)_reader GO -- -- CreateMoleculeIndex -- CREATE PROCEDURE [$(bingo)].z_CreateMoleculeIndex @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CreateMoleculeIndex GO ADD SIGNATURE TO [$(bingo)].z_CreateMoleculeIndex BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].CreateMoleculeIndex @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max) AS BEGIN EXEC [$(bingo)].z_CreateMoleculeIndex @table, @id_column, @data_column, '$(bingo)' END GO grant execute on [$(bingo)].CreateMoleculeIndex to $(bingo)_operator GO -- -- CreateReactionIndex -- CREATE PROCEDURE [$(bingo)].z_CreateReactionIndex @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].CreateReactionIndex GO ADD SIGNATURE TO [$(bingo)].z_CreateReactionIndex BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].CreateReactionIndex @table nvarchar(max), @id_column nvarchar(max), @data_column nvarchar(max) AS BEGIN EXEC [$(bingo)].z_CreateReactionIndex @table, @id_column, @data_column, '$(bingo)' END GO grant execute on [$(bingo)].CreateReactionIndex to $(bingo)_operator GO -- -- DropIndex -- CREATE PROCEDURE [$(bingo)].z_DropIndex @table nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].DropIndex GO ADD SIGNATURE TO [$(bingo)].z_DropIndex BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].DropIndex @table nvarchar(max) AS BEGIN EXEC [$(bingo)].z_DropIndex @table, '$(bingo)' END GO grant execute on [$(bingo)].DropIndex to $(bingo)_operator GO -- -- DropInvalidIndices -- CREATE PROCEDURE [$(bingo)].z_DropInvalidIndices @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].DropInvalidIndices GO ADD SIGNATURE TO [$(bingo)].z_DropInvalidIndices BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].DropInvalidIndices AS BEGIN EXEC [$(bingo)].z_DropInvalidIndices '$(bingo)' END GO grant execute on [$(bingo)].DropInvalidIndices to $(bingo)_operator GO -- -- Exact -- CREATE FUNCTION [$(bingo)].z_Exact ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Exact GO ADD SIGNATURE TO [$(bingo)].z_Exact BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Exact ( @target varchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_Exact (cast(@target as VARBINARY(max)), @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].Exact to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].ExactB ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_Exact (@target, @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].ExactB to $(bingo)_reader GO -- -- ExportSDF -- CREATE PROCEDURE [$(bingo)].z_ExportSDF @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ExportSDF GO ADD SIGNATURE TO [$(bingo)].z_ExportSDF BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].ExportSDF @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max) AS BEGIN EXEC [$(bingo)].z_ExportSDF @table_name, @mol_column_name, @file_name, @additional_parameters END GO grant execute on [$(bingo)].ExportSDF to $(bingo)_operator GO -- -- Fingerprint -- CREATE FUNCTION [$(bingo)].z_Fingerprint ( @molecule varbinary(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS varbinary(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Fingerprint GO ADD SIGNATURE TO [$(bingo)].z_Fingerprint BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Fingerprint ( @molecule varchar(max), @options nvarchar(max) ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_Fingerprint (cast(@molecule as VARBINARY(max)), @options, '$(bingo)') END GO grant execute on [$(bingo)].Fingerprint to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].FingerprintB ( @molecule varbinary(max), @options nvarchar(max) ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_Fingerprint (@molecule, @options, '$(bingo)') END GO grant execute on [$(bingo)].FingerprintB to $(bingo)_reader GO -- -- FlushOperations -- CREATE PROCEDURE [$(bingo)].z_FlushOperations @table_name nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].FlushOperations GO ADD SIGNATURE TO [$(bingo)].z_FlushOperations BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].FlushOperations @table_name nvarchar(max) AS BEGIN EXEC [$(bingo)].z_FlushOperations @table_name, '$(bingo)' END GO grant execute on [$(bingo)].FlushOperations to $(bingo)_operator GO -- -- GetAtomCount -- CREATE FUNCTION [$(bingo)].z_GetAtomCount ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].GetAtomCount GO ADD SIGNATURE TO [$(bingo)].z_GetAtomCount BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].GetAtomCount ( @molecule varchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_GetAtomCount (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].GetAtomCount to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].GetAtomCountB ( @molecule varbinary(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_GetAtomCount (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].GetAtomCountB to $(bingo)_reader GO -- -- GetBondCount -- CREATE FUNCTION [$(bingo)].z_GetBondCount ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].GetBondCount GO ADD SIGNATURE TO [$(bingo)].z_GetBondCount BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].GetBondCount ( @molecule varchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_GetBondCount (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].GetBondCount to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].GetBondCountB ( @molecule varbinary(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_GetBondCount (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].GetBondCountB to $(bingo)_reader GO -- -- GetStatistics -- CREATE FUNCTION [$(bingo)].z_GetStatistics ( @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].GetStatistics GO ADD SIGNATURE TO [$(bingo)].z_GetStatistics BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].GetStatistics ( ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_GetStatistics ('$(bingo)') END GO grant execute on [$(bingo)].GetStatistics to $(bingo)_operator GO -- -- GetVersion -- CREATE FUNCTION [$(bingo)].z_GetVersion ( @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].GetVersion GO ADD SIGNATURE TO [$(bingo)].z_GetVersion BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].GetVersion ( ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_GetVersion ('$(bingo)') END GO grant execute on [$(bingo)].GetVersion to $(bingo)_reader GO -- -- Gross -- CREATE FUNCTION [$(bingo)].z_Gross ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Gross GO ADD SIGNATURE TO [$(bingo)].z_Gross BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Gross ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Gross (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].Gross to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].GrossB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Gross (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].GrossB to $(bingo)_reader GO -- -- ImportRDF -- CREATE PROCEDURE [$(bingo)].z_ImportRDF @table_name nvarchar(max), @react_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ImportRDF GO ADD SIGNATURE TO [$(bingo)].z_ImportRDF BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].ImportRDF @table_name nvarchar(max), @react_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max) AS BEGIN EXEC [$(bingo)].z_ImportRDF @table_name, @react_column_name, @file_name, @additional_parameters, '$(bingo)' END GO grant execute on [$(bingo)].ImportRDF to $(bingo)_operator GO -- -- ImportSDF -- CREATE PROCEDURE [$(bingo)].z_ImportSDF @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ImportSDF GO ADD SIGNATURE TO [$(bingo)].z_ImportSDF BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].ImportSDF @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @additional_parameters nvarchar(max) AS BEGIN EXEC [$(bingo)].z_ImportSDF @table_name, @mol_column_name, @file_name, @additional_parameters, '$(bingo)' END GO grant execute on [$(bingo)].ImportSDF to $(bingo)_operator GO -- -- ImportSMILES -- CREATE PROCEDURE [$(bingo)].z_ImportSMILES @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @id_column_name nvarchar(max), @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ImportSMILES GO ADD SIGNATURE TO [$(bingo)].z_ImportSMILES BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].ImportSMILES @table_name nvarchar(max), @mol_column_name nvarchar(max), @file_name nvarchar(max), @id_column_name nvarchar(max) AS BEGIN EXEC [$(bingo)].z_ImportSMILES @table_name, @mol_column_name, @file_name, @id_column_name, '$(bingo)' END GO grant execute on [$(bingo)].ImportSMILES to $(bingo)_operator GO -- -- InChI -- CREATE FUNCTION [$(bingo)].z_InChI ( @molecule varbinary(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].InChI GO ADD SIGNATURE TO [$(bingo)].z_InChI BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].InChI ( @molecule varchar(max), @options nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_InChI (cast(@molecule as VARBINARY(max)), @options, '$(bingo)') END GO grant execute on [$(bingo)].InChI to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].InChIB ( @molecule varbinary(max), @options nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_InChI (@molecule, @options, '$(bingo)') END GO grant execute on [$(bingo)].InChIB to $(bingo)_reader GO -- -- InChIKey -- CREATE FUNCTION [$(bingo)].z_InChIKey ( @inchi nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].InChIKey GO ADD SIGNATURE TO [$(bingo)].z_InChIKey BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].InChIKey ( @inchi nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_InChIKey (@inchi, '$(bingo)') END GO grant execute on [$(bingo)].InChIKey to $(bingo)_reader GO -- -- Mass -- CREATE FUNCTION [$(bingo)].z_Mass ( @molecule varbinary(max), @type nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS real AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Mass GO ADD SIGNATURE TO [$(bingo)].z_Mass BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Mass ( @molecule varchar(max), @type nvarchar(max) ) RETURNS real AS BEGIN RETURN [$(bingo)].z_Mass (cast(@molecule as VARBINARY(max)), @type, '$(bingo)') END GO grant execute on [$(bingo)].Mass to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].MassB ( @molecule varbinary(max), @type nvarchar(max) ) RETURNS real AS BEGIN RETURN [$(bingo)].z_Mass (@molecule, @type, '$(bingo)') END GO grant execute on [$(bingo)].MassB to $(bingo)_reader GO -- -- Molfile -- CREATE FUNCTION [$(bingo)].z_Molfile ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Molfile GO ADD SIGNATURE TO [$(bingo)].z_Molfile BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Molfile ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Molfile (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].Molfile to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].MolfileB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Molfile (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].MolfileB to $(bingo)_reader GO -- -- Name -- CREATE FUNCTION [$(bingo)].z_Name ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Name GO ADD SIGNATURE TO [$(bingo)].z_Name BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Name ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Name (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].Name to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].NameB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Name (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].NameB to $(bingo)_reader GO -- -- OnSessionClose -- CREATE PROCEDURE [$(bingo)].z_OnSessionClose @spid_str nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].OnSessionClose GO ADD SIGNATURE TO [$(bingo)].z_OnSessionClose BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].OnSessionClose @spid_str nvarchar(max) AS BEGIN EXEC [$(bingo)].z_OnSessionClose @spid_str END GO -- -- ProfilingGetCount -- CREATE FUNCTION [$(bingo)].z_ProfilingGetCount ( @counter_name nvarchar(max), @whole_session bit, @bingo_schema nvarchar(max) ) RETURNS bigint AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ProfilingGetCount GO ADD SIGNATURE TO [$(bingo)].z_ProfilingGetCount BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].ProfilingGetCount ( @counter_name nvarchar(max), @whole_session bit ) RETURNS bigint AS BEGIN RETURN [$(bingo)].z_ProfilingGetCount (@counter_name, @whole_session, '$(bingo)') END GO grant execute on [$(bingo)].ProfilingGetCount to $(bingo)_operator GO -- -- ProfilingGetTime -- CREATE FUNCTION [$(bingo)].z_ProfilingGetTime ( @counter_name nvarchar(max), @whole_session bit, @bingo_schema nvarchar(max) ) RETURNS real AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ProfilingGetTime GO ADD SIGNATURE TO [$(bingo)].z_ProfilingGetTime BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].ProfilingGetTime ( @counter_name nvarchar(max), @whole_session bit ) RETURNS real AS BEGIN RETURN [$(bingo)].z_ProfilingGetTime (@counter_name, @whole_session, '$(bingo)') END GO grant execute on [$(bingo)].ProfilingGetTime to $(bingo)_operator GO -- -- ProfilingGetValue -- CREATE FUNCTION [$(bingo)].z_ProfilingGetValue ( @counter_name nvarchar(max), @whole_session bit, @bingo_schema nvarchar(max) ) RETURNS bigint AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ProfilingGetValue GO ADD SIGNATURE TO [$(bingo)].z_ProfilingGetValue BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].ProfilingGetValue ( @counter_name nvarchar(max), @whole_session bit ) RETURNS bigint AS BEGIN RETURN [$(bingo)].z_ProfilingGetValue (@counter_name, @whole_session, '$(bingo)') END GO grant execute on [$(bingo)].ProfilingGetValue to $(bingo)_operator GO -- -- RCML -- CREATE FUNCTION [$(bingo)].z_RCML ( @reaction varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RCML GO ADD SIGNATURE TO [$(bingo)].z_RCML BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RCML ( @reaction varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RCML (cast(@reaction as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].RCML to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RCMLB ( @reaction varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RCML (@reaction, '$(bingo)') END GO grant execute on [$(bingo)].RCMLB to $(bingo)_reader GO -- -- ReadFileAsBinary -- CREATE FUNCTION [$(bingo)].z_ReadFileAsBinary ( @filename nvarchar(max) ) RETURNS varbinary(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.BingoSqlUtils].ReadFileAsBinary GO ADD SIGNATURE TO [$(bingo)].z_ReadFileAsBinary BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].ReadFileAsBinary ( @filename nvarchar(max) ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_ReadFileAsBinary (@filename) END GO grant execute on [$(bingo)].ReadFileAsBinary to $(bingo)_operator GO -- -- ReadFileAsText -- CREATE FUNCTION [$(bingo)].z_ReadFileAsText ( @filename nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.BingoSqlUtils].ReadFileAsText GO ADD SIGNATURE TO [$(bingo)].z_ReadFileAsText BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].ReadFileAsText ( @filename nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_ReadFileAsText (@filename) END GO grant execute on [$(bingo)].ReadFileAsText to $(bingo)_operator GO -- -- ResetStatistics -- CREATE PROCEDURE [$(bingo)].z_ResetStatistics @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].ResetStatistics GO ADD SIGNATURE TO [$(bingo)].z_ResetStatistics BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].ResetStatistics AS BEGIN EXEC [$(bingo)].z_ResetStatistics '$(bingo)' END GO grant execute on [$(bingo)].ResetStatistics to $(bingo)_operator GO -- -- RExact -- CREATE FUNCTION [$(bingo)].z_RExact ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RExact GO ADD SIGNATURE TO [$(bingo)].z_RExact BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RExact ( @target varchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RExact (cast(@target as VARBINARY(max)), @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].RExact to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RExactB ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RExact (@target, @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].RExactB to $(bingo)_reader GO -- -- RFingerprint -- CREATE FUNCTION [$(bingo)].z_RFingerprint ( @reaction varbinary(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS varbinary(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RFingerprint GO ADD SIGNATURE TO [$(bingo)].z_RFingerprint BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RFingerprint ( @reaction varchar(max), @options nvarchar(max) ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_RFingerprint (cast(@reaction as VARBINARY(max)), @options, '$(bingo)') END GO grant execute on [$(bingo)].RFingerprint to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RFingerprintB ( @reaction varbinary(max), @options nvarchar(max) ) RETURNS varbinary(max) AS BEGIN RETURN [$(bingo)].z_RFingerprint (@reaction, @options, '$(bingo)') END GO grant execute on [$(bingo)].RFingerprintB to $(bingo)_reader GO -- -- RSMARTS -- CREATE FUNCTION [$(bingo)].z_RSMARTS ( @target varbinary(max), @query nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RSMARTS GO ADD SIGNATURE TO [$(bingo)].z_RSMARTS BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RSMARTS ( @target varchar(max), @query nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RSMARTS (cast(@target as VARBINARY(max)), @query, '$(bingo)') END GO grant execute on [$(bingo)].RSMARTS to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RSMARTSB ( @target varbinary(max), @query nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RSMARTS (@target, @query, '$(bingo)') END GO grant execute on [$(bingo)].RSMARTSB to $(bingo)_reader GO -- -- RSMARTSHi -- CREATE FUNCTION [$(bingo)].z_RSMARTSHi ( @target varbinary(max), @query nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RSMARTSHi GO ADD SIGNATURE TO [$(bingo)].z_RSMARTSHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RSMARTSHi ( @target varchar(max), @query nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSMARTSHi (cast(@target as VARBINARY(max)), @query, '$(bingo)') END GO grant execute on [$(bingo)].RSMARTSHi to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RSMARTSHiB ( @target varbinary(max), @query nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSMARTSHi (@target, @query, '$(bingo)') END GO grant execute on [$(bingo)].RSMARTSHiB to $(bingo)_reader GO -- -- RSmiles -- CREATE FUNCTION [$(bingo)].z_RSmiles ( @reaction varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RSmiles GO ADD SIGNATURE TO [$(bingo)].z_RSmiles BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RSmiles ( @reaction varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSmiles (cast(@reaction as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].RSmiles to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RSmilesB ( @reaction varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSmiles (@reaction, '$(bingo)') END GO grant execute on [$(bingo)].RSmilesB to $(bingo)_reader GO -- -- RSub -- CREATE FUNCTION [$(bingo)].z_RSub ( @target varbinary(max), @query nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RSub GO ADD SIGNATURE TO [$(bingo)].z_RSub BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RSub ( @target varchar(max), @query nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RSub (cast(@target as VARBINARY(max)), @query, '$(bingo)') END GO grant execute on [$(bingo)].RSub to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RSubB ( @target varbinary(max), @query nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_RSub (@target, @query, '$(bingo)') END GO grant execute on [$(bingo)].RSubB to $(bingo)_reader GO -- -- RSubHi -- CREATE FUNCTION [$(bingo)].z_RSubHi ( @target varbinary(max), @query nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].RSubHi GO ADD SIGNATURE TO [$(bingo)].z_RSubHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].RSubHi ( @target varchar(max), @query nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSubHi (cast(@target as VARBINARY(max)), @query, '$(bingo)') END GO grant execute on [$(bingo)].RSubHi to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RSubHiB ( @target varbinary(max), @query nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_RSubHi (@target, @query, '$(bingo)') END GO grant execute on [$(bingo)].RSubHiB to $(bingo)_reader GO -- -- Rxnfile -- CREATE FUNCTION [$(bingo)].z_Rxnfile ( @reaction varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Rxnfile GO ADD SIGNATURE TO [$(bingo)].z_Rxnfile BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Rxnfile ( @reaction varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Rxnfile (cast(@reaction as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].Rxnfile to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].RxnfileB ( @reaction varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Rxnfile (@reaction, '$(bingo)') END GO grant execute on [$(bingo)].RxnfileB to $(bingo)_reader GO -- -- SearchExact -- CREATE FUNCTION [$(bingo)].z_SearchExact ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchExact GO ADD SIGNATURE TO [$(bingo)].z_SearchExact BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchExact ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchExact (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchExact to $(bingo)_reader GO -- -- SearchGross -- CREATE FUNCTION [$(bingo)].z_SearchGross ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchGross GO ADD SIGNATURE TO [$(bingo)].z_SearchGross BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchGross ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchGross (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchGross to $(bingo)_reader GO -- -- SearchMolecularWeight -- CREATE FUNCTION [$(bingo)].z_SearchMolecularWeight ( @table nvarchar(max), @min_bound float, @max_bound float, @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, weight real) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchMolecularWeight GO ADD SIGNATURE TO [$(bingo)].z_SearchMolecularWeight BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchMolecularWeight ( @table nvarchar(max), @min_bound float, @max_bound float, @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchMolecularWeight (@table, @min_bound, @max_bound, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchMolecularWeight to $(bingo)_reader GO -- -- SearchRExact -- CREATE FUNCTION [$(bingo)].z_SearchRExact ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchRExact GO ADD SIGNATURE TO [$(bingo)].z_SearchRExact BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchRExact ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchRExact (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchRExact to $(bingo)_reader GO -- -- SearchRSMARTS -- CREATE FUNCTION [$(bingo)].z_SearchRSMARTS ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchRSMARTS GO ADD SIGNATURE TO [$(bingo)].z_SearchRSMARTS BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchRSMARTS ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchRSMARTS (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchRSMARTS to $(bingo)_reader GO -- -- SearchRSMARTSHi -- CREATE FUNCTION [$(bingo)].z_SearchRSMARTSHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, highlighting nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchRSMARTSHi GO ADD SIGNATURE TO [$(bingo)].z_SearchRSMARTSHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchRSMARTSHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchRSMARTSHi (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchRSMARTSHi to $(bingo)_reader GO -- -- SearchRSub -- CREATE FUNCTION [$(bingo)].z_SearchRSub ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchRSub GO ADD SIGNATURE TO [$(bingo)].z_SearchRSub BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchRSub ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchRSub (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchRSub to $(bingo)_reader GO -- -- SearchRSubHi -- CREATE FUNCTION [$(bingo)].z_SearchRSubHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, highlighting nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchRSubHi GO ADD SIGNATURE TO [$(bingo)].z_SearchRSubHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchRSubHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchRSubHi (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchRSubHi to $(bingo)_reader GO -- -- SearchSim -- CREATE FUNCTION [$(bingo)].z_SearchSim ( @table nvarchar(max), @query nvarchar(max), @metric nvarchar(max), @bingo_schema nvarchar(max), @min_bound float, @max_bound float ) RETURNS TABLE (id int, similarity real) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchSim GO ADD SIGNATURE TO [$(bingo)].z_SearchSim BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchSim ( @table nvarchar(max), @query nvarchar(max), @metric nvarchar(max), @min_bound float, @max_bound float ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchSim (@table, @query, @metric, '$(bingo)', @min_bound, @max_bound)) GO grant select on [$(bingo)].SearchSim to $(bingo)_reader GO -- -- SearchSMARTS -- CREATE FUNCTION [$(bingo)].z_SearchSMARTS ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchSMARTS GO ADD SIGNATURE TO [$(bingo)].z_SearchSMARTS BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchSMARTS ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchSMARTS (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchSMARTS to $(bingo)_reader GO -- -- SearchSMARTSHi -- CREATE FUNCTION [$(bingo)].z_SearchSMARTSHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, highlighting nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchSMARTSHi GO ADD SIGNATURE TO [$(bingo)].z_SearchSMARTSHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchSMARTSHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchSMARTSHi (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchSMARTSHi to $(bingo)_reader GO -- -- SearchSub -- CREATE FUNCTION [$(bingo)].z_SearchSub ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchSub GO ADD SIGNATURE TO [$(bingo)].z_SearchSub BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchSub ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchSub (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchSub to $(bingo)_reader GO -- -- SearchSubHi -- CREATE FUNCTION [$(bingo)].z_SearchSubHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS TABLE (id int, highlighting nvarchar(max)) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SearchSubHi GO ADD SIGNATURE TO [$(bingo)].z_SearchSubHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SearchSubHi ( @table nvarchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS TABLE AS RETURN (SELECT * FROM [$(bingo)].z_SearchSubHi (@table, @query, @options, '$(bingo)')) GO grant select on [$(bingo)].SearchSubHi to $(bingo)_reader GO -- -- SetKeepCache -- CREATE PROCEDURE [$(bingo)].z_SetKeepCache @table nvarchar(max), @value bit, @bingo_schema nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SetKeepCache GO ADD SIGNATURE TO [$(bingo)].z_SetKeepCache BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE PROCEDURE [$(bingo)].SetKeepCache @table nvarchar(max), @value bit AS BEGIN EXEC [$(bingo)].z_SetKeepCache @table, @value, '$(bingo)' END GO grant execute on [$(bingo)].SetKeepCache to $(bingo)_operator GO -- -- Sim -- CREATE FUNCTION [$(bingo)].z_Sim ( @target varbinary(max), @query nvarchar(max), @metrics nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS real AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Sim GO ADD SIGNATURE TO [$(bingo)].z_Sim BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Sim ( @target varchar(max), @query nvarchar(max), @metrics nvarchar(max) ) RETURNS real AS BEGIN RETURN [$(bingo)].z_Sim (cast(@target as VARBINARY(max)), @query, @metrics, '$(bingo)') END GO grant execute on [$(bingo)].Sim to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SimB ( @target varbinary(max), @query nvarchar(max), @metrics nvarchar(max) ) RETURNS real AS BEGIN RETURN [$(bingo)].z_Sim (@target, @query, @metrics, '$(bingo)') END GO grant execute on [$(bingo)].SimB to $(bingo)_reader GO -- -- SMARTS -- CREATE FUNCTION [$(bingo)].z_SMARTS ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SMARTS GO ADD SIGNATURE TO [$(bingo)].z_SMARTS BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SMARTS ( @target varchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_SMARTS (cast(@target as VARBINARY(max)), @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].SMARTS to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SMARTSB ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_SMARTS (@target, @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].SMARTSB to $(bingo)_reader GO -- -- SMARTSHi -- CREATE FUNCTION [$(bingo)].z_SMARTSHi ( @target varbinary(max), @query nvarchar(max), @parameters nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SMARTSHi GO ADD SIGNATURE TO [$(bingo)].z_SMARTSHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SMARTSHi ( @target varchar(max), @query nvarchar(max), @parameters nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_SMARTSHi (cast(@target as VARBINARY(max)), @query, @parameters, '$(bingo)') END GO grant execute on [$(bingo)].SMARTSHi to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SMARTSHiB ( @target varbinary(max), @query nvarchar(max), @parameters nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_SMARTSHi (@target, @query, @parameters, '$(bingo)') END GO grant execute on [$(bingo)].SMARTSHiB to $(bingo)_reader GO -- -- Smiles -- CREATE FUNCTION [$(bingo)].z_Smiles ( @molecule varbinary(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Smiles GO ADD SIGNATURE TO [$(bingo)].z_Smiles BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Smiles ( @molecule varchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Smiles (cast(@molecule as VARBINARY(max)), '$(bingo)') END GO grant execute on [$(bingo)].Smiles to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SmilesB ( @molecule varbinary(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_Smiles (@molecule, '$(bingo)') END GO grant execute on [$(bingo)].SmilesB to $(bingo)_reader GO -- -- Sub -- CREATE FUNCTION [$(bingo)].z_Sub ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS int AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].Sub GO ADD SIGNATURE TO [$(bingo)].z_Sub BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].Sub ( @target varchar(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_Sub (cast(@target as VARBINARY(max)), @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].Sub to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SubB ( @target varbinary(max), @query nvarchar(max), @options nvarchar(max) ) RETURNS int AS BEGIN RETURN [$(bingo)].z_Sub (@target, @query, @options, '$(bingo)') END GO grant execute on [$(bingo)].SubB to $(bingo)_reader GO -- -- SubHi -- CREATE FUNCTION [$(bingo)].z_SubHi ( @target varbinary(max), @query nvarchar(max), @parameters nvarchar(max), @bingo_schema nvarchar(max) ) RETURNS nvarchar(max) AS EXTERNAL NAME [$(bingo)_assembly].[indigo.Bingo].SubHi GO ADD SIGNATURE TO [$(bingo)].z_SubHi BY CERTIFICATE $(bingo)_certificate WITH PASSWORD = '$(bingo_pass)' GO CREATE FUNCTION [$(bingo)].SubHi ( @target varchar(max), @query nvarchar(max), @parameters nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_SubHi (cast(@target as VARBINARY(max)), @query, @parameters, '$(bingo)') END GO grant execute on [$(bingo)].SubHi to $(bingo)_reader GO CREATE FUNCTION [$(bingo)].SubHiB ( @target varbinary(max), @query nvarchar(max), @parameters nvarchar(max) ) RETURNS nvarchar(max) AS BEGIN RETURN [$(bingo)].z_SubHi (@target, @query, @parameters, '$(bingo)') END GO grant execute on [$(bingo)].SubHiB to $(bingo)_reader GO Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo_drop.sql000066400000000000000000000015411271037650300227700ustar00rootroot00000000000000use $(database) exec [$(bingo)]._DropAllIndices go :r bingo_drop_methods.sql drop procedure [$(bingo)].log_events go drop event notification $(bingo)_$(database)_logout_notify on server; go drop route $(bingo)_notify_route; go drop service $(bingo)_notify_service; go drop queue [$(bingo)].notify_queue; go drop table [$(bingo)].CONFIG go drop table [$(bingo)].CONFIG_BIN go drop table [$(bingo)].CONTEXT; go drop table [$(bingo)].TAUTOMER_RULES go DROP ROLE $(bingo)_operator; GO DROP ROLE $(bingo)_reader; GO DROP SCHEMA $(bingo); GO DROP USER $(bingo); GO DROP CERTIFICATE $(bingo)_certificate; GO DROP ASSEMBLY $(bingo)_assembly; GO use master; IF ($(fulldelete) = 1) BEGIN DROP LOGIN $(bingo)_assembly_login; DROP ASYMMETRIC KEY bingo_assembly_key; END GO Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo_drop_methods.sql000066400000000000000000000170501271037650300245150ustar00rootroot00000000000000-- -- This file was generated automatically -- -- use $(database) go DROP PROCEDURE [$(bingo)]._CheckMemoryAllocate GO DROP PROCEDURE [$(bingo)].z__CheckMemoryAllocate GO DROP PROCEDURE [$(bingo)]._DropAllIndices GO DROP PROCEDURE [$(bingo)].z__DropAllIndices GO DROP PROCEDURE [$(bingo)]._DropIndexByID GO DROP PROCEDURE [$(bingo)].z__DropIndexByID GO DROP PROCEDURE [$(bingo)]._FlushInAllSessions GO DROP PROCEDURE [$(bingo)].z__FlushInAllSessions GO DROP PROCEDURE [$(bingo)]._ForceGC GO DROP PROCEDURE [$(bingo)].z__ForceGC GO DROP PROCEDURE [$(bingo)]._OnDeleteRecordTrigger GO DROP PROCEDURE [$(bingo)].z__OnDeleteRecordTrigger GO DROP PROCEDURE [$(bingo)]._OnInsertRecordTrigger GO DROP PROCEDURE [$(bingo)].z__OnInsertRecordTrigger GO DROP PROCEDURE [$(bingo)]._WriteLog GO DROP PROCEDURE [$(bingo)].z__WriteLog GO DROP FUNCTION [$(bingo)].AAM GO DROP FUNCTION [$(bingo)].AAMB GO DROP FUNCTION [$(bingo)].z_AAM GO DROP FUNCTION [$(bingo)].CanSmiles GO DROP FUNCTION [$(bingo)].CanSmilesB GO DROP FUNCTION [$(bingo)].z_CanSmiles GO DROP FUNCTION [$(bingo)].CheckMolecule GO DROP FUNCTION [$(bingo)].CheckMoleculeB GO DROP FUNCTION [$(bingo)].z_CheckMolecule GO DROP FUNCTION [$(bingo)].CheckMoleculeTable GO DROP FUNCTION [$(bingo)].z_CheckMoleculeTable GO DROP FUNCTION [$(bingo)].CheckReaction GO DROP FUNCTION [$(bingo)].CheckReactionB GO DROP FUNCTION [$(bingo)].z_CheckReaction GO DROP FUNCTION [$(bingo)].CheckReactionTable GO DROP FUNCTION [$(bingo)].z_CheckReactionTable GO DROP FUNCTION [$(bingo)].CML GO DROP FUNCTION [$(bingo)].CMLB GO DROP FUNCTION [$(bingo)].z_CML GO DROP FUNCTION [$(bingo)].CompactMolecule GO DROP FUNCTION [$(bingo)].CompactMoleculeB GO DROP FUNCTION [$(bingo)].z_CompactMolecule GO DROP FUNCTION [$(bingo)].CompactReaction GO DROP FUNCTION [$(bingo)].CompactReactionB GO DROP FUNCTION [$(bingo)].z_CompactReaction GO DROP PROCEDURE [$(bingo)].CreateMoleculeIndex GO DROP PROCEDURE [$(bingo)].z_CreateMoleculeIndex GO DROP PROCEDURE [$(bingo)].CreateReactionIndex GO DROP PROCEDURE [$(bingo)].z_CreateReactionIndex GO DROP PROCEDURE [$(bingo)].DropIndex GO DROP PROCEDURE [$(bingo)].z_DropIndex GO DROP PROCEDURE [$(bingo)].DropInvalidIndices GO DROP PROCEDURE [$(bingo)].z_DropInvalidIndices GO DROP FUNCTION [$(bingo)].Exact GO DROP FUNCTION [$(bingo)].ExactB GO DROP FUNCTION [$(bingo)].z_Exact GO DROP PROCEDURE [$(bingo)].ExportSDF GO DROP PROCEDURE [$(bingo)].z_ExportSDF GO DROP FUNCTION [$(bingo)].Fingerprint GO DROP FUNCTION [$(bingo)].FingerprintB GO DROP FUNCTION [$(bingo)].z_Fingerprint GO DROP PROCEDURE [$(bingo)].FlushOperations GO DROP PROCEDURE [$(bingo)].z_FlushOperations GO DROP FUNCTION [$(bingo)].GetAtomCount GO DROP FUNCTION [$(bingo)].GetAtomCountB GO DROP FUNCTION [$(bingo)].z_GetAtomCount GO DROP FUNCTION [$(bingo)].GetBondCount GO DROP FUNCTION [$(bingo)].GetBondCountB GO DROP FUNCTION [$(bingo)].z_GetBondCount GO DROP FUNCTION [$(bingo)].GetStatistics GO DROP FUNCTION [$(bingo)].z_GetStatistics GO DROP FUNCTION [$(bingo)].GetVersion GO DROP FUNCTION [$(bingo)].z_GetVersion GO DROP FUNCTION [$(bingo)].Gross GO DROP FUNCTION [$(bingo)].GrossB GO DROP FUNCTION [$(bingo)].z_Gross GO DROP PROCEDURE [$(bingo)].ImportRDF GO DROP PROCEDURE [$(bingo)].z_ImportRDF GO DROP PROCEDURE [$(bingo)].ImportSDF GO DROP PROCEDURE [$(bingo)].z_ImportSDF GO DROP PROCEDURE [$(bingo)].ImportSMILES GO DROP PROCEDURE [$(bingo)].z_ImportSMILES GO DROP FUNCTION [$(bingo)].InChI GO DROP FUNCTION [$(bingo)].InChIB GO DROP FUNCTION [$(bingo)].z_InChI GO DROP FUNCTION [$(bingo)].InChIKey GO DROP FUNCTION [$(bingo)].z_InChIKey GO DROP FUNCTION [$(bingo)].Mass GO DROP FUNCTION [$(bingo)].MassB GO DROP FUNCTION [$(bingo)].z_Mass GO DROP FUNCTION [$(bingo)].Molfile GO DROP FUNCTION [$(bingo)].MolfileB GO DROP FUNCTION [$(bingo)].z_Molfile GO DROP FUNCTION [$(bingo)].Name GO DROP FUNCTION [$(bingo)].NameB GO DROP FUNCTION [$(bingo)].z_Name GO DROP PROCEDURE [$(bingo)].OnSessionClose GO DROP PROCEDURE [$(bingo)].z_OnSessionClose GO DROP FUNCTION [$(bingo)].ProfilingGetCount GO DROP FUNCTION [$(bingo)].z_ProfilingGetCount GO DROP FUNCTION [$(bingo)].ProfilingGetTime GO DROP FUNCTION [$(bingo)].z_ProfilingGetTime GO DROP FUNCTION [$(bingo)].ProfilingGetValue GO DROP FUNCTION [$(bingo)].z_ProfilingGetValue GO DROP FUNCTION [$(bingo)].RCML GO DROP FUNCTION [$(bingo)].RCMLB GO DROP FUNCTION [$(bingo)].z_RCML GO DROP FUNCTION [$(bingo)].ReadFileAsBinary GO DROP FUNCTION [$(bingo)].z_ReadFileAsBinary GO DROP FUNCTION [$(bingo)].ReadFileAsText GO DROP FUNCTION [$(bingo)].z_ReadFileAsText GO DROP PROCEDURE [$(bingo)].ResetStatistics GO DROP PROCEDURE [$(bingo)].z_ResetStatistics GO DROP FUNCTION [$(bingo)].RExact GO DROP FUNCTION [$(bingo)].RExactB GO DROP FUNCTION [$(bingo)].z_RExact GO DROP FUNCTION [$(bingo)].RFingerprint GO DROP FUNCTION [$(bingo)].RFingerprintB GO DROP FUNCTION [$(bingo)].z_RFingerprint GO DROP FUNCTION [$(bingo)].RSMARTS GO DROP FUNCTION [$(bingo)].RSMARTSB GO DROP FUNCTION [$(bingo)].z_RSMARTS GO DROP FUNCTION [$(bingo)].RSMARTSHi GO DROP FUNCTION [$(bingo)].RSMARTSHiB GO DROP FUNCTION [$(bingo)].z_RSMARTSHi GO DROP FUNCTION [$(bingo)].RSmiles GO DROP FUNCTION [$(bingo)].RSmilesB GO DROP FUNCTION [$(bingo)].z_RSmiles GO DROP FUNCTION [$(bingo)].RSub GO DROP FUNCTION [$(bingo)].RSubB GO DROP FUNCTION [$(bingo)].z_RSub GO DROP FUNCTION [$(bingo)].RSubHi GO DROP FUNCTION [$(bingo)].RSubHiB GO DROP FUNCTION [$(bingo)].z_RSubHi GO DROP FUNCTION [$(bingo)].Rxnfile GO DROP FUNCTION [$(bingo)].RxnfileB GO DROP FUNCTION [$(bingo)].z_Rxnfile GO DROP FUNCTION [$(bingo)].SearchExact GO DROP FUNCTION [$(bingo)].z_SearchExact GO DROP FUNCTION [$(bingo)].SearchGross GO DROP FUNCTION [$(bingo)].z_SearchGross GO DROP FUNCTION [$(bingo)].SearchMolecularWeight GO DROP FUNCTION [$(bingo)].z_SearchMolecularWeight GO DROP FUNCTION [$(bingo)].SearchRExact GO DROP FUNCTION [$(bingo)].z_SearchRExact GO DROP FUNCTION [$(bingo)].SearchRSMARTS GO DROP FUNCTION [$(bingo)].z_SearchRSMARTS GO DROP FUNCTION [$(bingo)].SearchRSMARTSHi GO DROP FUNCTION [$(bingo)].z_SearchRSMARTSHi GO DROP FUNCTION [$(bingo)].SearchRSub GO DROP FUNCTION [$(bingo)].z_SearchRSub GO DROP FUNCTION [$(bingo)].SearchRSubHi GO DROP FUNCTION [$(bingo)].z_SearchRSubHi GO DROP FUNCTION [$(bingo)].SearchSim GO DROP FUNCTION [$(bingo)].z_SearchSim GO DROP FUNCTION [$(bingo)].SearchSMARTS GO DROP FUNCTION [$(bingo)].z_SearchSMARTS GO DROP FUNCTION [$(bingo)].SearchSMARTSHi GO DROP FUNCTION [$(bingo)].z_SearchSMARTSHi GO DROP FUNCTION [$(bingo)].SearchSub GO DROP FUNCTION [$(bingo)].z_SearchSub GO DROP FUNCTION [$(bingo)].SearchSubHi GO DROP FUNCTION [$(bingo)].z_SearchSubHi GO DROP PROCEDURE [$(bingo)].SetKeepCache GO DROP PROCEDURE [$(bingo)].z_SetKeepCache GO DROP FUNCTION [$(bingo)].Sim GO DROP FUNCTION [$(bingo)].SimB GO DROP FUNCTION [$(bingo)].z_Sim GO DROP FUNCTION [$(bingo)].SMARTS GO DROP FUNCTION [$(bingo)].SMARTSB GO DROP FUNCTION [$(bingo)].z_SMARTS GO DROP FUNCTION [$(bingo)].SMARTSHi GO DROP FUNCTION [$(bingo)].SMARTSHiB GO DROP FUNCTION [$(bingo)].z_SMARTSHi GO DROP FUNCTION [$(bingo)].Smiles GO DROP FUNCTION [$(bingo)].SmilesB GO DROP FUNCTION [$(bingo)].z_Smiles GO DROP FUNCTION [$(bingo)].Sub GO DROP FUNCTION [$(bingo)].SubB GO DROP FUNCTION [$(bingo)].z_Sub GO DROP FUNCTION [$(bingo)].SubHi GO DROP FUNCTION [$(bingo)].SubHiB GO DROP FUNCTION [$(bingo)].z_SubHi GO Indigo-indigo-1.2.3/bingo/sqlserver/sql/bingo_update_assembly.sql000066400000000000000000000005471271037650300252120ustar00rootroot00000000000000use $(database) go BEGIN TRY exec [$(bingo)]._FlushInAllSessions; END TRY BEGIN CATCH END CATCH; GO ALTER ASSEMBLY $(bingo)_assembly DROP FILE ALL GO ALTER ASSEMBLY $(bingo)_assembly from '$(bingo_assembly_path).dll' WITH PERMISSION_SET = UNSAFE; GO ALTER ASSEMBLY $(bingo)_assembly ADD FILE FROM '$(bingo_assembly_path).pdb' GO Indigo-indigo-1.2.3/bingo/sqlserver/sql/run_update_assembly.bat000066400000000000000000000010311271037650300246540ustar00rootroot00000000000000@each off call bingo_saved_params.bat set bingo_sqlcmd_arguments=-vbingo=%bingoname% -vbingo_pass=%bingopass% -b -vdatabase=%database% -vbingo_assembly_path="%CD%\assembly\bingo-sqlserver" -e if not "%dbaname%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -U %dbaname% if not "%dbapass%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -P %dbapass% if not "%server%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -S %server% sqlcmd -i bingo_update_assembly.sql %bingo_sqlcmd_arguments% Indigo-indigo-1.2.3/bingo/sqlserver/sql/run_update_create_methods.bat000066400000000000000000000010631271037650300260300ustar00rootroot00000000000000@each off call bingo_saved_params.bat set bingo_sqlcmd_arguments=-vbingo=%bingoname% -vbingo_pass=%bingopass% -b -vdatabase=%database% -vdll_directory="%libdir%" -vbingo_assembly_path="%CD%\assembly\bingo-sqlserver" -e if not "%dbaname%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -U %dbaname% if not "%dbapass%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -P %dbapass% if not "%server%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -S %server% sqlcmd -i bingo_create_methods.sql %bingo_sqlcmd_arguments% Indigo-indigo-1.2.3/bingo/sqlserver/sql/run_update_drop_methods.bat000066400000000000000000000010561271037650300255330ustar00rootroot00000000000000@each off call bingo_saved_params.bat set bingo_sqlcmd_arguments=-vbingo=%bingoname% -vbingo_pass=%bingopass% -vdatabase=%database% -vdll_directory="%libdir%" -vbingo_assembly_path="%CD%\assembly\bingo-sqlserver" -e if not "%dbaname%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -U %dbaname% if not "%dbapass%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -P %dbapass% if not "%server%" == "" set bingo_sqlcmd_arguments=%bingo_sqlcmd_arguments% -S %server% sqlcmd -i bingo_drop_methods.sql %bingo_sqlcmd_arguments% Indigo-indigo-1.2.3/bingo/sqlserver/sqlserver_script_generator/000077500000000000000000000000001271037650300250055ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/sqlserver_script_generator/Program.cs000066400000000000000000000256211271037650300267510ustar00rootroot00000000000000using System; using System.Text; using System.Collections.Generic; using System.Reflection; using System.Linq; using System.IO; using Microsoft.SqlServer.Server; using indigo.SqlAttributes; namespace indigo { class SqlServerScriptGenerator { static IEnumerable GetMethodsWithSqlAttributes(Assembly assembly) { List methods = new List(); foreach (Type type in assembly.GetTypes()) { foreach (MethodInfo method in type.GetMethods()) { object[] attributes = method.GetCustomAttributes(typeof(BingoSqlFunctionAttribute), true); if (attributes.Length > 0) methods.Add(method); } } methods.Sort((i1, i2) => i1.Name.CompareTo(i2.Name)); return methods; } static Dictionary types_substitution = new Dictionary { { "Void", "" }, { "Int32", "int" }, { "SqlInt32", "int" }, { "Int64", "bigint" }, { "SqlInt64", "bigint" }, { "SqlBoolean", "bit" }, { "SqlByte", "tinyint" }, { "SqlString", "nvarchar(max)" }, { "SqlBinary", "varbinary(max)" }, { "SqlDouble", "float" }, { "SqlSingle", "real" }, { "Single", "real" }, }; static string SqlType (string ctype, MethodInfo m) { if (ctype == "IEnumerable") { object[] attributes = m.GetCustomAttributes(typeof(SqlFunctionAttribute), true); SqlFunctionAttribute sql_attr = (SqlFunctionAttribute)(attributes[0]); return String.Format("TABLE ({0})", sql_attr.TableDefinition); } if (types_substitution.ContainsKey(ctype)) return types_substitution[ctype]; String msg = String.Format("Please, add type {0} to the type substitution table for {1} method", ctype, m.Name); System.Console.WriteLine(msg); throw new Exception(msg); } class IndigoSQLFunctionInfo { public string ret_type, name; public List> arguments = new List>(); public AccessLevelKind access_level; public string code; public void writeCode (StreamWriter stream) { stream.Write("CREATE "); if (ret_type != "") stream.Write("FUNCTION "); else stream.Write("PROCEDURE "); stream.Write("{0} \n", name); if (ret_type != "") stream.Write(" (\n"); bool if_first = true; foreach (KeyValuePair p in arguments) { if (!if_first) { stream.Write(","); stream.Write("\n"); } stream.Write(" @{0} {1}", p.Key, p.Value); if_first = false; } if (!if_first) stream.Write("\n"); if (ret_type != "") { stream.Write(" )\n"); stream.Write(" RETURNS {0}\n", ret_type); } stream.Write("AS\n"); stream.Write("{0}\n", code); stream.Write("GO\n"); } public void writeSign (StreamWriter stream) { stream.Write("ADD SIGNATURE TO {0} BY CERTIFICATE $(bingo)_certificate\n", name); stream.Write(" WITH PASSWORD = '$(bingo_pass)'\n"); stream.Write("GO\n\n"); } public void writeDrop (StreamWriter stream) { stream.Write("DROP "); if (ret_type != "") stream.Write("FUNCTION "); else stream.Write("PROCEDURE "); stream.Write("{0} \n", name); stream.Write("GO\n\n"); } public void writeGrant (StreamWriter stream) { if (access_level != AccessLevelKind.None) { string permissions = "execute"; if (ret_type.StartsWith("TABLE")) permissions = "select"; string role; if (access_level == AccessLevelKind.Reader) role = "reader"; else role = "operator"; stream.Write("grant {0} on {1} to $(bingo)_{2}", permissions, name, role); stream.WriteLine("\nGO\n"); } } public void writeCreate (StreamWriter stream, bool wrapper_func) { writeCode(stream); if (!wrapper_func) writeSign(stream); if (wrapper_func) writeGrant(stream); } } class IndigoSQLFunction { public string comment; public IndigoSQLFunctionInfo main_function = new IndigoSQLFunctionInfo(); public List wrappers = new List(); public void writeCreate (StreamWriter stream) { stream.Write("--\n-- {0}\n--\n", comment); main_function.writeCreate(stream, false); foreach (IndigoSQLFunctionInfo m in wrappers) m.writeCreate(stream, true); } public void writeDrop (StreamWriter stream) { foreach (IndigoSQLFunctionInfo m in wrappers) m.writeDrop(stream); main_function.writeDrop(stream); } } static IndigoSQLFunctionInfo CreateWrapper (IndigoSQLFunctionInfo parent, bool binary, MethodInfo m) { StringBuilder code_builder = new StringBuilder(); IndigoSQLFunctionInfo w = new IndigoSQLFunctionInfo(); w.name = String.Format("[$(bingo)].{0}{1}", m.Name, binary ? "B" : ""); if (parent.ret_type.StartsWith("TABLE")) w.ret_type = "TABLE"; else { w.ret_type = parent.ret_type; code_builder.Append("BEGIN\n"); } w.access_level = parent.access_level; object[] attributes = m.GetCustomAttributes(typeof(BingoSqlFunctionAttribute), true); BingoSqlFunctionAttribute attr = (BingoSqlFunctionAttribute)(attributes[0]); if (parent.ret_type != "") code_builder.Append(" RETURN "); else code_builder.Append(" EXEC "); if (parent.ret_type.StartsWith("TABLE")) code_builder.Append("(SELECT * FROM "); code_builder.AppendFormat("{0} ", parent.name); if (parent.ret_type != "") code_builder.Append("("); bool is_first = true; bool str_bin_found = false; foreach (KeyValuePair p in parent.arguments) { if (!is_first) code_builder.Append(", "); if (attr.substitute_bingo && p.Key == "bingo_schema") code_builder.Append("'$(bingo)'"); else if (attr.substitute_bingo && p.Key == "bingo_db") code_builder.Append("'$(database)'"); else if (attr.str_bin == p.Key && !binary) { code_builder.AppendFormat("cast(@{0} as VARBINARY(max))", p.Key); w.arguments.Add(new KeyValuePair(p.Key, "varchar(max)")); str_bin_found = true; } else { code_builder.AppendFormat("@{0}", p.Key); w.arguments.Add(p); } is_first = false; } if (!binary && attr.str_bin != null && !str_bin_found) { String msg = String.Format("Cannot find {0} in {1}", attr.str_bin, m.Name); System.Console.WriteLine(msg); throw new Exception(msg); } if (parent.ret_type != "") code_builder.Append(")"); if (parent.ret_type.StartsWith("TABLE")) code_builder.Append(")"); code_builder.Append("\n"); if (!parent.ret_type.StartsWith("TABLE")) code_builder.Append("END"); w.code = code_builder.ToString(); return w; } static List GenerateFunctions (IEnumerable methods) { List functions = new List(); foreach (MethodInfo m in methods) { IndigoSQLFunction f = new IndigoSQLFunction(); f.comment = m.Name; object[] attributes = m.GetCustomAttributes(typeof(BingoSqlFunctionAttribute), true); BingoSqlFunctionAttribute attr = (BingoSqlFunctionAttribute)(attributes[0]); IndigoSQLFunctionInfo mf = f.main_function; mf.name = String.Format("[$(bingo)].z_{0}", m.Name); mf.ret_type = SqlType(m.ReturnType.Name, m); mf.access_level = attr.access_level; foreach (ParameterInfo p in m.GetParameters()) mf.arguments.Add(new KeyValuePair(p.Name, SqlType(p.ParameterType.Name, m))); mf.code = String.Format(" EXTERNAL NAME [$(bingo)_assembly].[{0}].{1}", m.ReflectedType.FullName, m.Name); // Create wrappers f.wrappers.Add(CreateWrapper(mf, false, m)); if (attr.str_bin != null) f.wrappers.Add(CreateWrapper(mf, true, m)); functions.Add(f); } return functions; } static void Main(string[] args) { string path = "."; if (args.Length == 0) System.Console.WriteLine("Usage: path to directory with scripts. Using current directory"); else path = args[0]; Assembly a = indigo.SqlAttributes.AccessLevelKind.None.GetType().Assembly; FileInfo create_script_file_info = new FileInfo(path + "\\bingo_create_methods.sql"); FileInfo drop_script_file_info = new FileInfo(path + "\\bingo_drop_methods.sql"); StreamWriter drop_file = drop_script_file_info.CreateText(); StreamWriter create_file = create_script_file_info.CreateText(); create_file.WriteLine("--\n-- This file was generated automatically --\n--\n"); drop_file.WriteLine("--\n-- This file was generated automatically --\n--\n"); create_file.WriteLine("use $(database)\ngo\n"); drop_file.WriteLine("use $(database)\ngo\n"); foreach (IndigoSQLFunction m in GenerateFunctions(GetMethodsWithSqlAttributes(a))) { m.writeCreate(create_file); m.writeDrop(drop_file); } drop_file.Close(); create_file.Close(); } } } Indigo-indigo-1.2.3/bingo/sqlserver/sqlserver_script_generator/Properties/000077500000000000000000000000001271037650300271415ustar00rootroot00000000000000Indigo-indigo-1.2.3/bingo/sqlserver/sqlserver_script_generator/Properties/AssemblyInfo.cs000066400000000000000000000027201271037650300320640ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("sqlserver_script_generator")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("EPAM Systems")] [assembly: AssemblyProduct("sqlserver_script_generator")] [assembly: AssemblyCopyright("Copyright © EPAM Systems 2010")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("23bcf4fa-f24c-40fc-a8ff-ca213a55019e")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] Indigo-indigo-1.2.3/bingo/sqlserver/sqlserver_script_generator/sqlserver_script_generator.csproj000066400000000000000000000106711271037650300337140ustar00rootroot00000000000000 Debug AnyCPU 9.0.30729 2.0 {BBB579CB-8C95-4A28-8950-348CF56104AB} Exe Properties sqlserver_script_generator sqlserver_script_generator v3.5 512 Always 3.5 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset 3.5 3.5 3.5 {ED96DA19-3326-4DFB-9CB1-189895A367E5} bingo-sqlserver False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 true False Windows Installer 3.1 true $(TargetPath) $(ProjectDir)..\sql Indigo-indigo-1.2.3/build_scripts/000077500000000000000000000000001271037650300170475ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/bingo-oracle/000077500000000000000000000000001271037650300214105ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/bingo-oracle/CMakeLists.txt000066400000000000000000000067231271037650300241600ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(BingoOracleSolution) set(Bingo_SOURCE_DIR ../../bingo) set(BINGO "YES") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) if(MSVC) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_C_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_C_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_CXX_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") endif() add_subdirectory(../../third_party/zlib-src "${CMAKE_CURRENT_BINARY_DIR}/zlib") # Get defined variable with the headers get_directory_property(ZLib_HEADERS_DIR DIRECTORY ../../third_party/zlib-src DEFINITION ZLib_HEADERS_DIR) add_subdirectory(../../common "${CMAKE_CURRENT_BINARY_DIR}/common") add_subdirectory(../../third_party/tinyxml "${CMAKE_CURRENT_BINARY_DIR}/tinyxml") # Get defined variable with the headers get_directory_property(TinyXML_HEADERS_DIR DIRECTORY ../../third_party/tinyxml DEFINITION TinyXML_HEADERS_DIR) add_subdirectory(../../graph "${CMAKE_CURRENT_BINARY_DIR}/graph") add_subdirectory(../../molecule "${CMAKE_CURRENT_BINARY_DIR}/molecule") add_subdirectory(../../reaction "${CMAKE_CURRENT_BINARY_DIR}/reaction") add_subdirectory(../../layout "${CMAKE_CURRENT_BINARY_DIR}/layout") add_subdirectory(../../third_party/oci "${CMAKE_CURRENT_BINARY_DIR}/oci") get_directory_property(OCI_INCLUDE_DIRS DIRECTORY ../../third_party/oci DEFINITION OCI_INCLUDE_DIRS) get_directory_property(OCI_LIBRARY_DIRS DIRECTORY ../../third_party/oci DEFINITION OCI_LIBRARY_DIRS) get_directory_property(OCI_LIBRARY DIRECTORY ../../third_party/oci DEFINITION OCI_LIBRARY) add_subdirectory(../../bingo/bingo-core "${CMAKE_CURRENT_BINARY_DIR}/bingo-core") add_subdirectory(../../common/oracle "${CMAKE_CURRENT_BINARY_DIR}/common-oracle") add_subdirectory(../../third_party/inchi "${CMAKE_CURRENT_BINARY_DIR}/inchi") add_subdirectory(../../bingo/oracle "${CMAKE_CURRENT_BINARY_DIR}/bingo-oracle") SET(BINGO_VERSION "${BINGO_VERSION}") SET(COMPONENTS shared) # Uncomment it in CMake 2.8.8: http://www.gccxml.org/Bug/view.php?id=12129 SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) SET(CPACK_COMPONENTS_ALL ${COMPONENTS}) set(CPACK_ARCHIVE_COMPONENT_INSTALL 1) SET(CPACK_GENERATOR "ZIP") SET(CPACK_PACKAGE_FILE_NAME "bingo-oracle-${BINGO_VERSION}-${PACKAGE_SUFFIX}") foreach(comp ${COMPONENTS}) INSTALL(FILES ${Bingo_SOURCE_DIR}/LICENSE.GPL DESTINATION . COMPONENT ${comp}) INSTALL(DIRECTORY ${BingoOracle_SOURCE_DIR}/sql/ DESTINATION sql COMPONENT ${comp}) IF(${SYSTEM_NAME} MATCHES "Win") INSTALL(FILES ${BingoOracle_SOURCE_DIR}/setup/bingo-oracle-install.bat DESTINATION . COMPONENT ${comp}) ELSE() INSTALL(FILES ${BingoOracle_SOURCE_DIR}/setup/bingo-oracle-install.sh DESTINATION . COMPONENT ${comp}) ENDIF() endforeach() SET(CMAKE_INSTALL_PREFIX ${BingoOracle_SOURCE_DIR}/libs) INCLUDE(CPack) Indigo-indigo-1.2.3/build_scripts/bingo-postgres/000077500000000000000000000000001271037650300220115ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/bingo-postgres/CMakeLists.txt000066400000000000000000000070651271037650300245610ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(BingoPostgresSolution C CXX) set(BINGO "YES") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) set(Bingo_SOURCE_DIR ../../bingo/) INCLUDE(ConfigureCommon) if(MSVC) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_C_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_C_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_CXX_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") endif() add_subdirectory(../../third_party/zlib-src "${CMAKE_CURRENT_BINARY_DIR}/zlib") # Get defined variable with the headers get_directory_property(ZLib_HEADERS_DIR DIRECTORY ../../third_party/zlib-src DEFINITION ZLib_HEADERS_DIR) add_subdirectory(../../common "${CMAKE_CURRENT_BINARY_DIR}/common") add_subdirectory(../../third_party/tinyxml "${CMAKE_CURRENT_BINARY_DIR}/tinyxml") # Get defined variable with the headers get_directory_property(TinyXML_HEADERS_DIR DIRECTORY ../../third_party/tinyxml DEFINITION TinyXML_HEADERS_DIR) add_subdirectory(../../graph "${CMAKE_CURRENT_BINARY_DIR}/graph") add_subdirectory(../../molecule "${CMAKE_CURRENT_BINARY_DIR}/molecule") add_subdirectory(../../reaction "${CMAKE_CURRENT_BINARY_DIR}/reaction") add_subdirectory(../../layout "${CMAKE_CURRENT_BINARY_DIR}/layout") add_subdirectory(../../bingo/bingo-core "${CMAKE_CURRENT_BINARY_DIR}/bingo-core") add_subdirectory(../../third_party/inchi "${CMAKE_CURRENT_BINARY_DIR}/inchi") add_subdirectory(../../bingo/bingo-core-c "${CMAKE_CURRENT_BINARY_DIR}/bingo-core-c") add_subdirectory(../../bingo/postgres "${CMAKE_CURRENT_BINARY_DIR}/bingo-postgres") SET(BINGO_VERSION "${BINGO_VERSION}") SET(BINGO_PG_VERSION "$ENV{BINGO_PG_VERSION}" CACHE STRING "PostgreSQL version (9.0, 9.1)") SET(BINGO_PG_DIR "$ENV{BINGO_PG_DIR}" CACHE STRING "PostgreSQL folder") SET(COMPONENTS shared) # Uncomment it in CMake 2.8.8: http://www.gccxml.org/Bug/view.php?id=12129 SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) SET(CPACK_COMPONENTS_ALL ${COMPONENTS}) set(CPACK_ARCHIVE_COMPONENT_INSTALL 1) SET(CPACK_GENERATOR "ZIP") SET(CPACK_PACKAGE_FILE_NAME "bingo-postgres${BINGO_PG_VERSION}-${BINGO_VERSION}-${PACKAGE_SUFFIX}") foreach(comp ${COMPONENTS}) INSTALL(FILES ${Bingo_SOURCE_DIR}/LICENSE.GPL DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${BingoPostgres_SOURCE_DIR}/INSTALL DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${BingoPostgres_SOURCE_DIR}/README DESTINATION . COMPONENT ${comp}) INSTALL(DIRECTORY ${BingoPostgres_SOURCE_DIR}/sql/common/ DESTINATION sql COMPONENT ${comp}) INSTALL(DIRECTORY ${BingoPostgres_SOURCE_DIR}/sql/${BINGO_PG_VERSION}/ DESTINATION sql COMPONENT ${comp}) IF(${SYSTEM_NAME} MATCHES "Win") INSTALL(FILES ${BingoPostgres_SOURCE_DIR}/bingo-pg-install.bat DESTINATION . COMPONENT ${comp}) ELSE() INSTALL(FILES ${BingoPostgres_SOURCE_DIR}/bingo-pg-install.sh DESTINATION . COMPONENT ${comp}) ENDIF() endforeach() SET(CMAKE_INSTALL_PREFIX ${BingoPostgres_SOURCE_DIR}/libs) INCLUDE(CPack) Indigo-indigo-1.2.3/build_scripts/bingo-release.py000066400000000000000000000227041271037650300221420ustar00rootroot00000000000000import os import re import shutil import subprocess from os.path import * from optparse import OptionParser from zipfile import ZipFile def shortenDBMS(dbms): if dbms == 'postgres': return 'pg' elif dbms == 'sqlserver': return 'mssql' elif dbms == 'oracle': return 'ora' def shortenGenerator(generator): result = generator if generator.startswith('Visual Studio '): result = generator.replace('Visual Studio ', 'VS') elif generator.startswith('Unix MakeFiles'): result = generator.replace('Unix Makefiles', 'umake') elif generator.startswith('Unix MakeFiles'): result = generator.replace('MinGW Makefiles', 'mmake') return result.replace(' ', '') presets = { "win32" : ("Visual Studio 10", ""), "win64" : ("Visual Studio 10 Win64", ""), "win32-2012" : ("Visual Studio 11", ""), "win64-2012" : ("Visual Studio 11 Win64", ""), "win32-2013" : ("Visual Studio 12", ""), "win64-2013" : ("Visual Studio 12 Win64", ""), "win32-mingw": ("MinGW Makefiles", ""), "linux32" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux32-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux64" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), "linux64-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), "mac10.5" : ("Xcode", "-DSUBSYSTEM_NAME=10.5"), "mac10.6" : ("Xcode", "-DSUBSYSTEM_NAME=10.6"), "mac10.7" : ("Xcode", "-DSUBSYSTEM_NAME=10.7"), "mac10.8" : ("Xcode", "-DSUBSYSTEM_NAME=10.8"), "mac10.9" : ("Xcode", "-DSUBSYSTEM_NAME=10.9"), "mac-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=10.6"), } parser = OptionParser(description='Bingo build script') parser.add_option('--generator', help='this option is passed as -G option for cmake') parser.add_option('--params', default="", help='additional build parameters') parser.add_option('--config', default="Release", help='project configuration') parser.add_option('--dbms', help='DMBS (oracle, postgres or sqlserver)') parser.add_option('--nobuild', default=False, action="store_true", help='configure without building', dest="nobuild") parser.add_option('--clean', default=False, action="store_true", help='delete all the build data', dest="clean") parser.add_option('--preset', type="choice", dest="preset", choices=list(presets.keys()), help='build preset %s' % (str(presets.keys()))) (args, left_args) = parser.parse_args() if len(left_args) > 0: print("Unexpected arguments: %s" % (str(left_args))) exit() if args.preset: args.generator, args.params = presets[args.preset] if not args.generator and args.dbms != 'sqlserver': print("Generator must be specified") exit() if args.preset and args.preset.find('universal') != -1: args.params += ' -DUNIVERSAL_BUILD=TRUE' cur_dir = abspath(dirname(__file__)) root = os.path.normpath(join(cur_dir, "..")) project_dir = join(cur_dir, "bingo-%s" % args.dbms) if args.dbms != 'sqlserver': build_dir = (shortenDBMS(args.dbms) + " " + shortenGenerator(args.generator) + " " + args.config + args.params.replace('-D', '')) build_dir = build_dir.replace(" ", "_").replace("=", "_").replace("-", "_") full_build_dir = os.path.join(root, "build", build_dir) if os.path.exists(full_build_dir) and args.clean: print("Removing previous project files") shutil.rmtree(full_build_dir) if not os.path.exists(full_build_dir): os.makedirs(full_build_dir) if args.generator.find("Unix Makefiles") != -1: args.params += " -DCMAKE_BUILD_TYPE=" + args.config os.chdir(full_build_dir) command = "%s cmake -G \"%s\" %s %s" % ('CC=gcc CXX=g++' if (args.preset.find('linux') != -1 and args.preset.find('universal') != -1) else '', args.generator, args.params, project_dir) print(command) subprocess.check_call(command, shell=True) if args.nobuild: exit(0) for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": os.remove(join(full_build_dir, f)) subprocess.check_call("cmake --build . --config %s" % args.config, shell=True) if args.generator.find("Unix Makefiles") != -1: subprocess.check_call("make package", shell=True) subprocess.check_call("make install", shell=True) elif args.generator.find("Xcode") != -1: subprocess.check_call("cmake --build . --target package --config %s" % args.config, shell=True) subprocess.check_call("cmake --build . --target install --config %s" % args.config, shell=True) elif args.generator.find("Visual Studio") != -1: subprocess.check_call("cmake --build . --target PACKAGE --config %s" % args.config, shell=True) subprocess.check_call("cmake --build . --target INSTALL --config %s" % args.config, shell=True) else: print("Do not know how to run package and install target") os.chdir(root) if not os.path.exists("dist"): os.mkdir("dist") dist_dir = join(root, "dist") for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": shutil.rmtree(join(full_build_dir, f.replace('-shared.zip', ''), f.replace('-shared.zip', '')), ignore_errors=True) zf = ZipFile(join(full_build_dir, f)) zf.extractall(join(full_build_dir, f.replace('-shared.zip', ''), f.replace('-shared.zip', ''))) zf = ZipFile(join(dist_dir, f.replace('-shared.zip', '.zip')), 'w') os.chdir(join(full_build_dir, f.replace('-shared.zip', ''))) for root, dirs, files in os.walk('.'): for file in files: if file.endswith('.sh') or file.endswith('.bat'): os.chmod(join(root, file), 0o755) zf.write(join(root, file)) os.chdir(root) else: dllPath = {} vsversion = 'Visual Studio' if args.preset.find("2012") != -1: vsversion += ' 11' elif args.preset.find("2013") != -1: vsversion += ' 12' else: vsversion += ' 10' for arch, generator in (('x86', vsversion), ('x64', vsversion + ' Win64')): build_dir = (shortenDBMS(args.dbms) + " " + shortenGenerator(generator) + " " + args.params) build_dir = build_dir.replace(" ", "_").replace("=", "_").replace("-", "_") full_build_dir = os.path.join(root, "build", build_dir) dllPath[arch] = os.path.normpath(os.path.join(full_build_dir, 'dist', 'Win', arch, 'lib', args.config)) if os.path.exists(full_build_dir) and args.clean: print("Removing previous project files") shutil.rmtree(full_build_dir) if not os.path.exists(full_build_dir): os.makedirs(full_build_dir) os.chdir(full_build_dir) command = "cmake -G \"%s\" %s %s" % (generator, args.params, project_dir) print(command) subprocess.check_call(command, shell=True) if args.nobuild: exit(0) for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": os.remove(join(full_build_dir, f)) subprocess.check_call("cmake --build . --config %s" % args.config, shell=True) os.chdir(join(root, 'bingo', 'sqlserver')) command = 'msbuild /t:Rebuild /p:Configuration=%s /property:DllPath32=%s /property:DllPath64=%s' % (args.config, dllPath['x86'], dllPath['x64']) print(os.path.abspath(os.curdir), command) subprocess.check_call(command) os.chdir(root) if not os.path.exists("dist"): os.mkdir("dist") dist_dir = join(root, "dist") # Get version version = "" for line in open(join('bingo', "bingo-version.cmake")): m = re.search('SET\(BINGO_VERSION "([^\"]*)\"', line) if m: version = m.group(1) if not os.path.exists(join(root, 'dist', 'bingo-sqlserver-%s' % version)): os.makedirs(join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version)) else: shutil.rmtree(join(root, 'dist', 'bingo-sqlserver-%s' % version)) os.makedirs(join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver%s' % version)) os.makedirs(join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version, 'assembly')) for item in os.listdir(join(root, 'bingo', 'sqlserver', 'sql')): if item.endswith('.sql') or item.endswith('.bat'): shutil.copyfile(join(root, 'bingo', 'sqlserver', 'sql', item), join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version, item)) if not os.path.exists(join(root, 'bingo', 'sqlserver', 'bin', args.config, 'bingo-sqlserver.dll')): print('Warning: File %s does not exist, going to use empty stub instead' % join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version, 'assembly', 'bingo-sqlserver.dll')) open(join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version, 'assembly', 'bingo-sqlserver.dll', 'w')).close() else: shutil.copyfile(join(root, 'bingo', 'sqlserver', 'bin', args.config, 'bingo-sqlserver.dll'), join(root, 'dist', 'bingo-sqlserver-%s' % version, 'bingo-sqlserver-%s' % version, 'assembly', 'bingo-sqlserver.dll')) os.chdir('dist') if os.path.exists('bingo-sqlserver-%s.zip' % version): os.remove('bingo-sqlserver-%s.zip' % version) shutil.make_archive('bingo-sqlserver-%s' % version, format='zip', root_dir=join(root, 'dist', 'bingo-sqlserver-%s' % version)) shutil.rmtree('bingo-sqlserver-%s' % version) os.chdir(root) Indigo-indigo-1.2.3/build_scripts/bingo-sqlserver/000077500000000000000000000000001271037650300221715ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/bingo-sqlserver/CMakeLists.txt000066400000000000000000000050511271037650300247320ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(BingoSqlServerSolution) set(BINGO "YES") set(BINGO_SQLSERVER "YES") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) set(Bingo_SOURCE_DIR ../../bingo/) set(BingoSqlServer_SOURCE_DIR ../../bingo/sqlserver) INCLUDE(ConfigureCommon) if(MSVC) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_C_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_C_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_CXX_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") endif() add_subdirectory(../../third_party/zlib-src "${CMAKE_CURRENT_BINARY_DIR}/zlib") # Get defined variable with the headers get_directory_property(ZLib_HEADERS_DIR DIRECTORY ../../third_party/zlib-src DEFINITION ZLib_HEADERS_DIR) add_subdirectory(../../common "${CMAKE_CURRENT_BINARY_DIR}/common") add_subdirectory(../../third_party/tinyxml "${CMAKE_CURRENT_BINARY_DIR}/tinyxml") # Get defined variable with the headers get_directory_property(TinyXML_HEADERS_DIR DIRECTORY ../../third_party/tinyxml DEFINITION TinyXML_HEADERS_DIR) add_subdirectory(../../graph "${CMAKE_CURRENT_BINARY_DIR}/graph") add_subdirectory(../../molecule "${CMAKE_CURRENT_BINARY_DIR}/molecule") add_subdirectory(../../reaction "${CMAKE_CURRENT_BINARY_DIR}/reaction") add_subdirectory(../../layout "${CMAKE_CURRENT_BINARY_DIR}/layout") add_subdirectory(../../bingo/bingo-core "${CMAKE_CURRENT_BINARY_DIR}/bingo-core") get_directory_property(BingoCore_HEADERS_DIR DIRECTORY ../../bingo/bingo-core DEFINITION BingoCore_HEADERS_DIR) add_subdirectory(../../third_party/inchi "${CMAKE_CURRENT_BINARY_DIR}/inchi") add_subdirectory(../../bingo/bingo-core-c "${CMAKE_CURRENT_BINARY_DIR}/bingo-core-c") get_directory_property(BingoCoreC_HEADERS_DIR DIRECTORY ../../bingo/bingo-core-c DEFINITION BingoCoreC_HEADERS_DIR) SET(BINGO_VERSION "${BINGO_VERSION}") #ADD_CUSTOM_COMMAND(OUTPUT ../../bingo/sqlserver COMMAND msbuild) Indigo-indigo-1.2.3/build_scripts/bingo/000077500000000000000000000000001271037650300201455ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/bingo/CMakeLists.txt000066400000000000000000000005011271037650300227010ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(Bingo C CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) add_subdirectory(../indigo "${CMAKE_CURRENT_BINARY_DIR}/indigo") add_subdirectory(../../api/plugins/bingo "${CMAKE_CURRENT_BINARY_DIR}/bingo") Indigo-indigo-1.2.3/build_scripts/indigo-all/000077500000000000000000000000001271037650300210665ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/indigo-all/CMakeLists.txt000066400000000000000000000063171271037650300236350ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoAll C CXX) ENABLE_TESTING() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) message(STATUS "**** Indigo ****") add_subdirectory(../indigo "${CMAKE_CURRENT_BINARY_DIR}/indigo") message(STATUS "**** Indigo-inchi ****") add_subdirectory(../indigo-inchi "${CMAKE_CURRENT_BINARY_DIR}/indigo-inchi") message(STATUS "**** Indigo-renderer ****") add_subdirectory(../indigo-renderer "${CMAKE_CURRENT_BINARY_DIR}/indigo-renderer") message(STATUS "**** Bingo ****") add_subdirectory(../bingo "${CMAKE_CURRENT_BINARY_DIR}/bingo") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) INCLUDE(InstallRequiredSystemLibraries) FOREACH(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}) INSTALL(FILES ${lib} DESTINATION "shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}" COMPONENT shared) ENDFOREACH() if (NOT NO_STATIC) set(COMPONENTS static shared) else() set(COMPONENTS shared) endif() # Uncomment it in CMake 2.8.8: http://www.gccxml.org/Bug/view.php?id=12129 SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) SET(CPACK_COMPONENTS_ALL ${COMPONENTS}) set(CPACK_ARCHIVE_COMPONENT_INSTALL 1) SET(CPACK_GENERATOR "ZIP") SET(CPACK_PACKAGE_FILE_NAME "indigo-libs-${INDIGO_VERSION}-${PACKAGE_SUFFIX}") foreach(comp ${COMPONENTS}) INSTALL(FILES ${Indigo_SOURCE_DIR}/LICENSE.GPL DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${Indigo_SOURCE_DIR}/indigo.h DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${Indigo_SOURCE_DIR}/plugins/renderer/indigo-renderer.h DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${Indigo_SOURCE_DIR}/plugins/inchi/indigo-inchi.h DESTINATION . COMPONENT ${comp}) INSTALL(FILES ${Indigo_SOURCE_DIR}/plugins/bingo/bingo.h DESTINATION . COMPONENT ${comp}) endforeach() SET(CMAKE_INSTALL_PREFIX ${Indigo_SOURCE_DIR}/libs) # DLOPEN TEST LIBRARY_NAME(indigo) LIBRARY_NAME(indigo-inchi) LIBRARY_NAME(indigo-renderer) LIBRARY_NAME(bingo) add_test(dlopen-test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlopen-test ${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo_NAME} ${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo-inchi_NAME} ${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo-renderer_NAME} ${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${bingo_NAME}) if(UNIX AND NOT APPLE AND CHECK_ABI) add_test(abi-test-indigo "file" "${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo_NAME}") set_tests_properties (abi-test-indigo PROPERTIES PASS_REGULAR_EXPRESSION "SYSV") add_test(abi-test-indigo-inchi "file" "${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo-inchi_NAME}") set_tests_properties (abi-test-indigo-inchi PROPERTIES PASS_REGULAR_EXPRESSION "SYSV") add_test(abi-test-indigo-renderer "file" "${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${indigo-renderer_NAME}") set_tests_properties (abi-test-indigo-renderer PROPERTIES PASS_REGULAR_EXPRESSION "SYSV") add_test(abi-test-bingo "file" "${Indigo_SOURCE_DIR}/libs/shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME}/${bingo_NAME}") set_tests_properties (abi-test-bingo PROPERTIES PASS_REGULAR_EXPRESSION "SYSV") endif() INCLUDE(CPack) INCLUDE(CTest) Indigo-indigo-1.2.3/build_scripts/indigo-inchi/000077500000000000000000000000001271037650300214105ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/indigo-inchi/CMakeLists.txt000066400000000000000000000005161271037650300241520ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoInchi C CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) add_subdirectory(../indigo "${CMAKE_CURRENT_BINARY_DIR}/indigo") add_subdirectory(../../api/plugins/inchi "${CMAKE_CURRENT_BINARY_DIR}/indigo-inchi") Indigo-indigo-1.2.3/build_scripts/indigo-install-r-pack.py000066400000000000000000000021641271037650300235140ustar00rootroot00000000000000import os import sys import shutil import subprocess from sys import platform as _platform cur_dir = os.path.dirname(os.path.abspath(__file__)) root = os.path.join(cur_dir, "..") api_dir = os.path.join(root, "api") r_dir = os.path.join(api_dir, "r") r_src_dir = os.path.join(r_dir, "src") rellibs = __import__("indigo-release-libs") indigo_pack_array = rellibs.build_libs(sys.argv[1:]) os.chdir(r_src_dir) if not os.path.exists("dist"): os.mkdir("dist") r_dist_dir = os.path.join(r_src_dir, "dist") for pack in indigo_pack_array: #if pack.find("static") >= 0: shutil.copy(pack, r_dist_dir) shutil.copy(os.path.join(api_dir, "indigo.h"), r_src_dir) shutil.copy(os.path.join(api_dir, "plugins", "renderer", "indigo-renderer.h"), r_src_dir) if _platform == "linux" or _platform == "linux2": shutil.copy(os.path.join(root, "common", "cmake", "linkhack.py"), r_src_dir) os.chmod(os.path.join(r_src_dir, "linkhack.py"), 0777) os.chdir(r_dir) if os.path.exists("package"): shutil.rmtree("package") os.mkdir("package") os.chdir(os.path.join(r_dir, "package")) subprocess.check_call("R CMD build %s" % (r_dir), shell=True)Indigo-indigo-1.2.3/build_scripts/indigo-make-by-libs.py000066400000000000000000000132731271037650300231520ustar00rootroot00000000000000# This module assumes that you have installed all the # libs files in the /dist directory import os import shutil import sys import re import subprocess import inspect from optparse import OptionParser def make_doc(): curdir = abspath(os.curdir) script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) root_dir = os.path.join(script_dir, "..") os.chdir(os.path.join(root_dir, 'api/python')) subprocess.check_call('"%s" copy-libs.py' % sys.executable, shell=True) os.chdir('../../doc') subprocess.check_call('"%s" builder.py' % sys.executable, shell=True) os.chdir(curdir) def copy_doc(destname): shutil.copytree('../doc/build/html', join(curdir, destname, 'doc')) def flatten_directory(dir): todelete = [] for f in os.listdir(dir): if f.find("python") != -1 or f.find("java") != -1 or f.find("dotnet") != -1: continue dir2 = os.path.join(dir, f) if os.path.isdir(dir2): for f2 in os.listdir(dir2): f2full = os.path.join(dir2, f2) shutil.move(f2full, dir) todelete.append(dir2) os.rmdir(dir2) def move_dir_content(src_dir, dest_dir): for f in os.listdir(src_dir): f2 = os.path.join(src_dir, f) destf2 = os.path.join(dest_dir, f) if os.path.isdir(destf2): move_dir_content(f2, destf2) elif not os.path.exists(destf2): shutil.move(f2, destf2) def join_archives(names, destname): for name in names: if not os.path.exists(name + ".zip"): return for name in names: subprocess.check_call("unzip %s.zip -d %s" % (name, name), shell=True) os.mkdir(destname) for name in names: move_dir_content(name, destname) if os.path.exists(destname + ".zip"): os.remove(destname + ".zip") if args.doc: copy_doc(destname) subprocess.check_call("zip -r -9 -m %s.zip %s" % (destname, destname), shell=True) for name in names: shutil.rmtree(name) os.remove("%s.zip" % name) def join_archives_by_pattern(pattern, destname): archives = [] for f in os.listdir("."): if re.match(pattern, f): archives.append(os.path.splitext(f)[0]) if len(archives) == 0: return print(archives) join_archives(archives, destname) def clearLibs(): for f in os.listdir(libs_dir): if f == "readme.txt": continue ffull = os.path.join(libs_dir, f) if os.path.isdir(ffull): shutil.rmtree(ffull) else: os.remove(ffull) def unpackToLibs(name): if os.path.exists("tmp"): shutil.rmtree("tmp") subprocess.check_call("unzip %s.zip -d tmp" % (name), shell=True) move_dir_content(os.path.join("tmp", name), libs_dir) shutil.rmtree("tmp") parser = OptionParser(description='Indigo libraries repacking') parser.add_option('--libonlyname', help='extract only the library into api/lib') parser.add_option('--config', default="Release", help='project configuration') parser.add_option('--type', default='python,java,dotnet', help='wrapper (dotnet, java, python)') parser.add_option('--doc', default=False, action='store_true', help='Build documentation') (args, left_args) = parser.parse_args() if not args.type: args.type = 'python,java,dotnet' if args.doc: make_doc() if len(left_args) > 0: print("Unexpected arguments: %s" % (str(left_args))) exit() suffix = "" if args.config.lower() != "release": suffix = "-" + args.config.lower() need_join_archieves = (args.libonlyname == None) need_gen_wrappers = (args.libonlyname == None) cur_dir = os.path.split(__file__)[0] # Find indigo version sys.path.append(os.path.join(os.path.dirname(__file__), "..", "api")) from get_indigo_version import getIndigoVersion version = getIndigoVersion() os.chdir(os.path.join(cur_dir, "../dist")) if need_join_archieves: flatten_directory(".") arc_joins = [ ("indigo-libs-%ver%-linux-shared", "indigo-libs-%ver%-linux.+-shared" ), ("indigo-libs-%ver%-win-shared", "indigo-libs-%ver%-win.+-shared" ), ("indigo-libs-%ver%-mac-shared", "indigo-libs-%ver%-mac.+-shared" ), ("indigo-libs-%ver%-linux-static", "indigo-libs-%ver%-linux.+-static" ), ("indigo-libs-%ver%-win-static", "indigo-libs-%ver%-win.+-static" ), ("indigo-libs-%ver%-mac-static", "indigo-libs-%ver%-mac.+-static" ), ] if need_join_archieves: for dest, pattern in arc_joins: p = pattern.replace("%ver%", version) + "\.zip" d = dest.replace("%ver%", version) + suffix join_archives_by_pattern(p, d) print("*** Making wrappers *** ") api_dir = os.path.abspath("../api") libs_dir = os.path.join(api_dir, "libs") wrappers = [ ("win", ["win"]), ("linux", ["linux"]), ("mac", ["mac"]), ("universal", ["win", "linux", "mac"]), ] wrappers_gen = ["make-java-wrappers.py", "make-python-wrappers.py", 'make-dotnet-wrappers.py'] for w, libs in wrappers: clearLibs() if args.libonlyname and w != args.libonlyname: continue any_exists = True for lib in libs: name = "indigo-libs-%s-%s-shared%s" % (version, lib, suffix) if os.path.exists(name + ".zip"): any_exists = any_exists and True unpackToLibs(name) else: any_exists = any_exists and False if not any_exists: continue if need_gen_wrappers: for gen in wrappers_gen: if args.type is not None: for g in args.type.split(','): if gen.find(g) != -1: subprocess.check_call('"%s" %s -s "-%s" %s' % (sys.executable, os.path.join(api_dir, gen), w, '--doc' if args.doc else ''), shell=True) Indigo-indigo-1.2.3/build_scripts/indigo-release-java-utils.py000066400000000000000000000140021271037650300243620ustar00rootroot00000000000000import glob import os import shutil import subprocess from os.path import * from zipfile import ZipFile from optparse import OptionParser import re version = "" cur_dir = split(__file__)[0] for line in open(join(os.path.dirname(os.path.abspath(__file__)), "..", "api", "indigo-version.cmake")): m = re.search('SET\(INDIGO_VERSION "(.*)"', line) if m: version = m.group(1) cur_dir = abspath(dirname(__file__)) root = os.path.normpath(join(cur_dir, "..")) project_dir = join(cur_dir, "indigo-utils") os.chdir(root) if not os.path.exists("dist"): os.mkdir("dist") dist_dir = join(root, "dist") # Chemdiff for filename in os.listdir(dist_dir): if filename.startswith("indigo-java-") : os.chdir(dist_dir) if os.path.exists("indigo-java"): shutil.rmtree("indigo-java") #os.mkdir("indigo-java") java_dir = join(dist_dir, "indigo-java") distVersion = filename.replace("indigo-java-%s-" % version , '').replace('.zip', '') fullChemdiffName = "chemdiff-%s-%s" % (version, distVersion) uz = ZipFile(join(dist_dir, filename)) uz.extractall(path=dist_dir) os.rename(join(dist_dir, filename)[:-4], "indigo-java") if os.path.exists(join(dist_dir, "chemdiff")): shutil.rmtree(join(dist_dir, "chemdiff")) os.mkdir(join(dist_dir, "chemdiff")) os.mkdir(join(dist_dir, "chemdiff", fullChemdiffName)) os.chdir(join(root, "utils", "chemdiff")) subprocess.check_call("ant clean", shell=True) subprocess.check_call("ant jar", shell=True) shutil.copy(join("dist", "chemdiff.jar"), join(dist_dir, "chemdiff", fullChemdiffName, "chemdiff.jar")) if filename.endswith('-win.zip'): shutil.copy(join("launch.bat"), join(dist_dir, "chemdiff", fullChemdiffName,"launch.bat")) else: shutil.copy(join("chemdiff.sh"), join(dist_dir, "chemdiff", fullChemdiffName,"chemdiff.sh")) shutil.copy(join("LICENSE.GPL"), join(dist_dir, "chemdiff", fullChemdiffName, "LICENSE.GPL")) shutil.copytree(join(root, "utils", "chemdiff", "examples"), join(dist_dir, "chemdiff", fullChemdiffName, "examples")) os.chdir(join(dist_dir, "chemdiff", fullChemdiffName)) os.mkdir("lib") for file in glob.glob("../../indigo-java/*.jar"): if not (file.endswith('indigo-inchi.jar')): shutil.copy(file, "lib") shutil.copy(join(root, "common/java/common-controls/dist/common-controls.jar"), "lib") if os.name == "nt" and os.path.exists("chemdiff.sh"): with open("chemdiff.sh", "rt") as f: text = f.read() with open("chemdiff.sh", "wt") as f: f.write(text.replace("\r\n", "\n")) os.chdir(dist_dir) shutil.make_archive(fullChemdiffName, "zip", "chemdiff") if filename.endswith('-win.zip'): os.chdir(join(dist_dir, "chemdiff", fullChemdiffName)) shutil.copy(join(root, "utils", "chemdiff", "chemdiff_installer.nsi"), join(dist_dir, "chemdiff", fullChemdiffName, "chemdiff_installer.nsi")) subprocess.check_call(["makensis", "/DVersion=%s" % version, "chemdiff_installer.nsi"], shell=True) shutil.copy("chemdiff-%s-installer.exe" % version, join(dist_dir, "chemdiff-%s-installer.exe" % version)) os.chdir(dist_dir) shutil.rmtree("chemdiff") shutil.rmtree("indigo-java") # Legio for filename in os.listdir(dist_dir): if filename.startswith("indigo-java-") : os.chdir(dist_dir) if os.path.exists("indigo-java"): shutil.rmtree("indigo-java") java_dir = join(dist_dir, "indigo-java") distVersion = filename.replace("indigo-java-%s-" % version , '').replace('.zip', '') fullLegioName = "legio-%s-%s" % (version, distVersion) uz = ZipFile(join(dist_dir, filename)) uz.extractall(path=dist_dir) os.rename(join(dist_dir, filename)[:-4], "indigo-java") if os.path.exists(join(dist_dir, "legio")): shutil.rmtree(join(dist_dir, "legio")) os.mkdir(join(dist_dir, "legio")) os.mkdir(join(dist_dir, "legio", fullLegioName)) os.chdir(join(root, "utils", "legio")) subprocess.check_call("ant clean", shell=True) subprocess.check_call("ant jar", shell=True) shutil.copy(join("dist", "legio.jar"), join(dist_dir, "legio", fullLegioName, "legio.jar")) if filename.endswith('-win.zip'): shutil.copy(join("launch.bat"), join(dist_dir, "legio", fullLegioName,"launch.bat")) else: shutil.copy(join("legio.sh"), join(dist_dir, "legio", fullLegioName,"legio.sh")) shutil.copy(join("LICENSE.GPL"), join(dist_dir, "legio", fullLegioName, "LICENSE.GPL")) shutil.copytree(join(root, "utils", "legio", "examples"), join(dist_dir, "legio", fullLegioName, "examples")) os.chdir(join(dist_dir, "legio", fullLegioName)) os.mkdir("lib") for file in glob.glob("../../indigo-java/*.jar"): if not (file.endswith('indigo-inchi.jar')): shutil.copy(file, "lib") shutil.copy(join(root, "common/java/common-controls/dist/common-controls.jar"), "lib") if os.name == "nt" and os.path.exists("legio.sh"): with open("legio.sh", "rb") as f: text = f.read() with open("legio.sh", "wb") as f: f.write(text.replace("\r\n", "\n")) os.chdir(dist_dir) shutil.make_archive(fullLegioName, "zip", "legio") if filename.endswith('-win.zip'): os.chdir(join(dist_dir, "legio", fullLegioName)) shutil.copy(join(root, "utils", "legio", "legio_installer.nsi"), join(dist_dir, "legio", fullLegioName, "legio_installer.nsi")) subprocess.check_call(["makensis", "/DVersion=%s" % version, "legio_installer.nsi"], shell=True) shutil.copy("legio-%s-installer.exe" % version, join(dist_dir, "legio-%s-installer.exe" % version)) os.chdir(dist_dir) shutil.rmtree("legio") shutil.rmtree("indigo-java")Indigo-indigo-1.2.3/build_scripts/indigo-release-libs.py000066400000000000000000000163431271037650300232460ustar00rootroot00000000000000import os import shutil import subprocess import sys from optparse import OptionParser check_call = None if not hasattr(subprocess, 'check_call'): def check_call_replacement(*popenargs, **kwargs): retcode = subprocess.call(*popenargs, **kwargs) if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] raise subprocess.CalledProcessError(retcode, cmd) return 0 check_call = check_call_replacement else: check_call = subprocess.check_call def build_libs(cl_args): presets = { "win32" : ("Visual Studio 10", ""), "win64" : ("Visual Studio 10 Win64", ""), "win32-2012" : ("Visual Studio 11", ""), "win64-2012" : ("Visual Studio 11 Win64", ""), "win32-2013" : ("Visual Studio 12", ""), "win64-2013" : ("Visual Studio 12 Win64", ""), "win32-mingw": ("MinGW Makefiles", ""), "linux32" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux32-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux64" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), "linux64-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), "mac10.6" : ("Xcode", "-DSUBSYSTEM_NAME=10.6"), "mac10.7" : ("Xcode", "-DSUBSYSTEM_NAME=10.7"), "mac10.8" : ("Xcode", "-DSUBSYSTEM_NAME=10.8"), "mac10.9" : ("Xcode", "-DSUBSYSTEM_NAME=10.9"), "mac10.10" : ("Xcode", "-DSUBSYSTEM_NAME=10.10"), "mac10.11" : ("Xcode", "-DSUBSYSTEM_NAME=10.11"), "mac-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=10.6"), } parser = OptionParser(description='Indigo libraries build script') parser.add_option('--generator', help='this option is passed as -G option for cmake') parser.add_option('--params', default="", help='additional build parameters') parser.add_option('--config', default="Release", help='project configuration') parser.add_option('--nobuild', default=False, action="store_true", help='configure without building', dest="nobuild") parser.add_option('--clean', default=False, action="store_true", help='delete all the build data', dest="clean") parser.add_option('--preset', type="choice", dest="preset", choices=list(presets.keys()), help='build preset %s' % (str(list(presets.keys())))) parser.add_option('--with-static', action='store_true', help='Build Indigo static libraries', default=False, dest='withStatic') parser.add_option('--verbose', action='store_true', help='Show verbose build information', default=False, dest='buildVerbose') parser.add_option('--cairo-gl', dest="cairogl", default=False, action="store_true", help='Build Cairo with OpenGL support') parser.add_option('--cairo-vg', dest="cairovg", default=False, action="store_true", help='Build Cairo with CairoVG support') parser.add_option('--cairo-egl', dest="cairoegl", default=False, action="store_true", help='Build Cairo with EGL support') parser.add_option('--cairo-glesv2', dest="cairoglesv2", default=False, action="store_true", help='Build Cairo with GLESv2 support') parser.add_option('--find-cairo', dest="findcairo", default=False, action="store_true", help='Find and use system Cairo') parser.add_option('--find-pixman', dest="findpixman", default=False, action="store_true", help='Find and use system Pixman') if os.name == 'posix': parser.add_option('--check-abi', dest='checkabi', default=False, action="store_true", help='Check ABI type of Indigo libraries on Linux') (args, left_args) = parser.parse_args(cl_args) if len(left_args) > 0: print("Unexpected arguments: %s" % (str(left_args))) exit() if args.preset: args.generator, args.params = presets[args.preset] if not args.generator: print("Generator must be specified") exit() cur_dir = os.path.abspath(os.path.dirname(__file__)) root = os.path.join(cur_dir, "..") project_dir = os.path.join(cur_dir, "indigo-all") if args.cairogl: args.params += ' -DWITH_CAIRO_GL=TRUE' if args.cairovg: args.params += ' -DWITH_CAIRO_VG=TRUE' if args.cairoegl: args.params += ' -DWITH_CAIRO_EGL=TRUE' if args.cairoglesv2: args.params += ' -DWITH_CAIRO_GLESV2=TRUE' if args.findcairo: args.params += ' -DUSE_SYSTEM_CAIRO=TRUE' if args.findcairo: args.params += ' -DUSE_SYSTEM_PIXMAN=TRUE' if not args.withStatic: args.params += ' -DNO_STATIC=TRUE' if args.preset and args.preset.find('universal') != -1: args.params += ' -DUNIVERSAL_BUILD=TRUE' if os.name == 'posix' and args.checkabi: args.params += ' -DCHECK_ABI=TRUE' build_dir = (args.generator + " " + args.config + args.params.replace('-D', '')) build_dir = "indigo_" + build_dir.replace(" ", "_").replace("=", "_").replace("-", "_") full_build_dir = os.path.join(root, "build", build_dir) if os.path.exists(full_build_dir) and args.clean: print("Removing previous project files") shutil.rmtree(full_build_dir) if not os.path.exists(full_build_dir): os.makedirs(full_build_dir) os.chdir(full_build_dir) environment_prefix = '' if (args.preset.find('linux') != -1 and args.preset.find('universal') != -1): environment_prefix = 'CC=gcc CXX=g++' command = "%s cmake -G \"%s\" %s %s" % (environment_prefix, args.generator, args.params, project_dir) print(command) subprocess.call(command, shell=True) if args.nobuild: exit(0) for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": os.remove(os.path.join(full_build_dir, f)) if args.generator.find("Unix Makefiles") != -1: verbose = '' if args.buildVerbose: verbose = 'VERBOSE=1' subprocess.call("make package %s" % (verbose), shell=True) subprocess.call("make install", shell=True) elif args.generator.find("Xcode") != -1: subprocess.call("cmake --build . --target package --config %s" % (args.config), shell=True) subprocess.call("cmake --build . --target install --config %s" % (args.config), shell=True) elif args.generator.find("Visual Studio") != -1: subprocess.call("cmake --build . --target PACKAGE --config %s" % (args.config), shell=True) subprocess.call("cmake --build . --target INSTALL --config %s" % (args.config), shell=True) elif args.generator.find("MinGW Makefiles") != -1: subprocess.call("mingw32-make package", shell=True) subprocess.call("mingw32-make install", shell=True) else: print("Do not know how to run package and install target") subprocess.call("ctest -V --timeout 20 -C %s ." % (args.config), shell=True) os.chdir(root) if not os.path.exists("dist"): os.mkdir("dist") dist_dir = os.path.join(root, "dist") zip_path_vec = [] for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": zip_path = os.path.join(dist_dir, f) shutil.copy(os.path.join(full_build_dir, f), zip_path) zip_path_vec.append(zip_path) return(zip_path_vec) if __name__ == '__main__': build_libs(sys.argv[1:]) Indigo-indigo-1.2.3/build_scripts/indigo-release-utils.py000066400000000000000000000106731271037650300234550ustar00rootroot00000000000000import os import shutil import subprocess from optparse import OptionParser import re version = "" cur_dir = os.path.split(__file__)[0] for line in open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "api", "indigo-version.cmake")): m = re.search(r'SET\(INDIGO_VERSION "(.*)"', line) if m: version = m.group(1) presets = { "win32" : ("Visual Studio 10", ""), "win64" : ("Visual Studio 10 Win64", ""), "win32-2012" : ("Visual Studio 11", ""), "win64-2012" : ("Visual Studio 11 Win64", ""), "win32-2013" : ("Visual Studio 12", ""), "win64-2013" : ("Visual Studio 12 Win64", ""), "win32-mingw": ("MinGW Makefiles", ""), "linux32" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux32-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x86"), "linux64" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), "linux64-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=x64"), # "mac10.6" : ("Xcode", "-DSUBSYSTEM_NAME=10.6"), "mac10.7" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=10.7"), "mac10.8" : ("Xcode", "-DSUBSYSTEM_NAME=10.8"), "mac10.9" : ("Xcode", "-DSUBSYSTEM_NAME=10.9"), "mac10.10" : ("Xcode", "-DSUBSYSTEM_NAME=10.10"), "mac-universal" : ("Unix Makefiles", "-DSUBSYSTEM_NAME=10.6"), } parser = OptionParser(description='Indigo utilities build script') parser.add_option('--generator', help='this option is passed as -G option for cmake') parser.add_option('--params', default="", help='additional build parameters') parser.add_option('--config', default="Release", help='project configuration') parser.add_option('--nobuild', default=False, action="store_true", help='configure without building', dest="nobuild") parser.add_option('--clean', default=False, action="store_true", help='delete all the build data', dest="clean") parser.add_option('--preset', type="choice", dest="preset", choices=list(presets.keys()), help='build preset %s' % (str(presets.keys()))) (args, left_args) = parser.parse_args() if len(left_args) > 0: print("Unexpected arguments: %s" % (str(left_args))) exit() if args.preset: args.generator, args.params = presets[args.preset] if not args.generator: print("Generator must be specified") exit() cur_dir = os.path.abspath(os.path.dirname(__file__)) root = os.path.normpath(os.path.join(cur_dir, "..")) project_dir = os.path.join(cur_dir, "indigo-utils") if args.generator.find("Unix Makefiles") != -1: args.params += " -DCMAKE_BUILD_TYPE=" + args.config if args.preset and args.preset.find('universal') != -1: args.params += ' -DUNIVERSAL_BUILD=TRUE' build_dir = (args.generator + " " + args.params) build_dir = "indigo_utils_" + build_dir.replace(" ", "_").replace("=", "_").replace("-", "_") full_build_dir = os.path.join(root, "build", build_dir) if os.path.exists(full_build_dir) and args.clean: print("Removing previous project files") shutil.rmtree(full_build_dir) if not os.path.exists(full_build_dir): os.makedirs(full_build_dir) os.chdir(root) if not os.path.exists("dist"): os.mkdir("dist") dist_dir = os.path.join(root, "dist") os.chdir(full_build_dir) command = "%s cmake -G \"%s\" %s %s" % ('CC=gcc CXX=g++' if (args.preset.find('linux') != -1 or args.preset.find('universal') != -1) else '', args.generator, args.params, project_dir) print(command) subprocess.check_call(command, shell=True) if args.nobuild: exit(0) for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": os.remove(os.path.join(full_build_dir, f)) command = "cmake --build . --config %s" % (args.config) print(command) subprocess.call(command, shell=True) if args.generator.find("Unix Makefiles") != -1: subprocess.check_call("make package", shell=True) elif args.generator.find("Xcode") != -1: subprocess.check_call("cmake --build . --target package --config %s" % (args.config), shell=True) elif args.generator.find("Visual Studio") != -1: subprocess.check_call("cmake --build . --target PACKAGE --config %s" % (args.config), shell=True) elif args.generator.find("MinGW Makefiles") != -1: subprocess.check_call("mingw32-make package", shell=True) else: print("Do not know how to run package and install target") subprocess.check_call("ctest -V --timeout 10 -C %s ." % (args.config), shell=True) for f in os.listdir(full_build_dir): path, ext = os.path.splitext(f) if ext == ".zip": shutil.copy(os.path.join(full_build_dir, f), os.path.join(dist_dir, f.replace('-shared', ''))) Indigo-indigo-1.2.3/build_scripts/indigo-renderer/000077500000000000000000000000001271037650300221245ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/indigo-renderer/CMakeLists.txt000066400000000000000000000027331271037650300246710ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoRenderer C CXX) if (INDIGO_RENDERER_CONFIGURED) return() endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) add_subdirectory(../indigo "${CMAKE_CURRENT_BINARY_DIR}/indigo") get_directory_property(ZLib_HEADERS_DIR DIRECTORY ../indigo DEFINITION ZLib_HEADERS_DIR) get_directory_property(ZLIB_FOUND DIRECTORY ../indigo DEFINITION ZLIB_FOUND) add_subdirectory(../../third_party/libpng-src "${CMAKE_CURRENT_BINARY_DIR}/png") get_directory_property(PNG_INCLUDE_DIR DIRECTORY ../../third_party/libpng-src DEFINITION PNG_INCLUDE_DIR) get_directory_property(PNG_LIBRARIES DIRECTORY ../../third_party/libpng-src DEFINITION PNG_LIBRARIES) get_directory_property(PNG_FOUND DIRECTORY ../../third_party/libpng-src DEFINITION PNG_FOUND) add_subdirectory(../../third_party/cairo-src/pixman "${CMAKE_CURRENT_BINARY_DIR}/pixman") get_directory_property(Pixman_headers_dir DIRECTORY ../../third_party/cairo-src/pixman DEFINITION Pixman_headers_dir) add_subdirectory(../../third_party/cairo-src/cairo "${CMAKE_CURRENT_BINARY_DIR}/cairo") get_directory_property(Cairo_headers_dir DIRECTORY ../../third_party/cairo-src/cairo DEFINITION Cairo_headers_dir) add_subdirectory(../../render2d "${CMAKE_CURRENT_BINARY_DIR}/render2d") add_subdirectory(../../api/plugins/renderer "${CMAKE_CURRENT_BINARY_DIR}/renderer") IF (PARENT_SCOPE) set(INDIGO_RENDERER_CONFIGURED TRUE PARENT_SCOPE) ENDIF()Indigo-indigo-1.2.3/build_scripts/indigo-update-version.py000066400000000000000000000037521271037650300236440ustar00rootroot00000000000000import os import re import sys import xml.etree.cElementTree as ElementTree INDIGO_PATH = os.path.normpath(os.path.join(os.path.abspath(os.path.dirname(__file__)), os.path.pardir)) def getIndigoCMakeVersion(): with open(os.path.join(INDIGO_PATH, 'api', 'indigo-version.cmake'), 'r') as f: m = re.match('SET\(INDIGO_VERSION \"(.*)\"\).*', f.read()) return m.group(1) def setIndigoCMakeVersion(newVersion): cmakeFile = os.path.join(INDIGO_PATH, 'api', 'indigo-version.cmake') with open(cmakeFile, 'r') as f: cmakeText = f.read() m = re.match('SET\(INDIGO_VERSION \"(.*)\"\).*', cmakeText) result = re.sub('SET\(INDIGO_VERSION \"(.*)\"\).*', 'SET(INDIGO_VERSION "{0}")'.format(newVersion), cmakeText) print('Updating Indigo version from {0} to {1} in {2}...'.format(m.group(1), newVersion, cmakeFile)) with open(cmakeFile, 'w') as f: f.write(result) def updatePomVersion(pomFile, newVersion): tree = ElementTree.parse(pomFile) ElementTree.register_namespace('', 'http://maven.apache.org/POM/4.0.0') root = tree.getroot() for child in root: if child.tag.endswith('version'): print('Updating Indigo version from {0} to {1} in {2}...'.format(child.text, newVersion, pomFile)) child.text = newVersion break tree.write(pomFile) def main(newVersion=None): indigoVersion = newVersion if newVersion else getIndigoCMakeVersion() updatePomVersion(os.path.join(INDIGO_PATH, 'api', 'java', 'pom.xml'), indigoVersion) updatePomVersion(os.path.join(INDIGO_PATH, 'api', 'plugins', 'bingo', 'java', 'pom.xml'), indigoVersion) updatePomVersion(os.path.join(INDIGO_PATH, 'api', 'plugins', 'inchi', 'java', 'pom.xml'), indigoVersion) updatePomVersion(os.path.join(INDIGO_PATH, 'api', 'plugins', 'renderer', 'java', 'pom.xml'), indigoVersion) if newVersion: setIndigoCMakeVersion(newVersion) if __name__ == '__main__': main(sys.argv[1]) if len(sys.argv) == 2 else main() Indigo-indigo-1.2.3/build_scripts/indigo-utils/000077500000000000000000000000001271037650300214565ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/indigo-utils/CMakeLists.txt000066400000000000000000000040421271037650300242160ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(IndigoUtils C CXX) ENABLE_TESTING() SET(PACK_INDIGO_NOT YES) if(MSVC) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_C_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_C_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_CXX_LINK_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "/NODEFAULTLIB:libc.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib") endif() set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/") INCLUDE(ConfigureCommon) message(STATUS "**** Indigo ****") add_subdirectory(../indigo "${CMAKE_CURRENT_BINARY_DIR}/indigo") message(STATUS "**** Indigo-renderer ****") add_subdirectory(../indigo-renderer "${CMAKE_CURRENT_BINARY_DIR}/indigo-renderer") message(STATUS "**** Indigo-cano ****") add_subdirectory(../../utils/indigo-cano "${CMAKE_CURRENT_BINARY_DIR}/indigo-cano") message(STATUS "**** Indigo-deco ****") add_subdirectory(../../utils/indigo-deco "${CMAKE_CURRENT_BINARY_DIR}/indigo-deco") message(STATUS "**** Indigo-depict ****") add_subdirectory(../../utils/indigo-depict "${CMAKE_CURRENT_BINARY_DIR}/indigo-depict") SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) SET(CPACK_COMPONENTS_ALL "shared") set(CPACK_ARCHIVE_COMPONENT_INSTALL 1) SET(CPACK_GENERATOR "ZIP") INSTALL(FILES ../../api/LICENSE.GPL DESTINATION . COMPONENT "shared") SET(CPACK_PACKAGE_FILE_NAME "indigo-utils-${INDIGO_VERSION}-${PACKAGE_SUFFIX}") SET(CMAKE_INSTALL_PREFIX ./libs) INCLUDE(CPack) INCLUDE(CTest) Indigo-indigo-1.2.3/build_scripts/indigo/000077500000000000000000000000001271037650300203205ustar00rootroot00000000000000Indigo-indigo-1.2.3/build_scripts/indigo/CMakeLists.txt000066400000000000000000000025561271037650300230700ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) if (INDIGO_CONFIGURED) return() endif() project(IndigoSolution) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) INCLUDE(ConfigureCommon) add_subdirectory(../../third_party/zlib-src "${CMAKE_CURRENT_BINARY_DIR}/zlib") # Get defined variable with the headers get_directory_property(ZLib_HEADERS_DIR DIRECTORY ../../third_party/zlib-src DEFINITION ZLib_HEADERS_DIR) add_subdirectory(../../common "${CMAKE_CURRENT_BINARY_DIR}/common") add_subdirectory(../../third_party/tinyxml "${CMAKE_CURRENT_BINARY_DIR}/tinyxml") # Get defined variable with the headers get_directory_property(TinyXML_HEADERS_DIR DIRECTORY ../../third_party/tinyxml DEFINITION TinyXML_HEADERS_DIR) add_subdirectory(../../third_party/inchi "${CMAKE_CURRENT_BINARY_DIR}/third_party/inchi") get_directory_property(InChI_HEADERS_DIR DIRECTORY ../../third_party/inchi DEFINITION InChI_HEADERS_DIR) add_subdirectory(../../graph "${CMAKE_CURRENT_BINARY_DIR}/graph") add_subdirectory(../../molecule "${CMAKE_CURRENT_BINARY_DIR}/molecule") add_subdirectory(../../reaction "${CMAKE_CURRENT_BINARY_DIR}/reaction") add_subdirectory(../../layout "${CMAKE_CURRENT_BINARY_DIR}/layout") add_subdirectory(../../api "${CMAKE_CURRENT_BINARY_DIR}/api") SET(INDIGO_VERSION "${INDIGO_VERSION}" PARENT_SCOPE) set(INDIGO_CONFIGURED TRUE PARENT_SCOPE) Indigo-indigo-1.2.3/common/000077500000000000000000000000001271037650300154715ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/CMakeLists.txt000066400000000000000000000026251271037650300202360ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(Common C CXX) # Collect file list for BaseC file (GLOB BaseC_src base_c/*.c*) if (MSVC OR MINGW) file (GLOB BaseC_src_excl base_c/*_posix.c*) else() file (GLOB BaseC_src_excl base_c/*_win.c* base_c/*_win32.c*) endif() list (REMOVE_ITEM BaseC_src ${BaseC_src_excl}) file (GLOB BaseC_headers base_c/*.h*) # Collect file list for BaseCpp file (GLOB BaseCpp_src base_cpp/*.c*) if (MSVC OR MINGW) file (GLOB BaseCpp_src_excl base_cpp/*_posix.c*) else() file (GLOB BaseCpp_src_excl base_cpp/*_win.c* base_cpp/*_win32.c*) endif() list (REMOVE_ITEM BaseCpp_src ${BaseCpp_src_excl}) file (GLOB BaseCpp_headers base_cpp/*.h*) # comment to regenerate files # Collect file list for LZW file (GLOB LZW_src lzw/*.c*) file (GLOB LZW_headers lzw/*.h*) # Collect file list for Math file (GLOB Math_src math/*.c*) file (GLOB Math_headers math/*.h*) source_group("BaseC" FILES ${BaseC_src} ${BaseC_headers}) source_group("BaseCpp" FILES ${BaseCpp_src} ${BaseCpp_headers}) source_group("LZW" FILES ${LZW_src} ${LZW_headers}) source_group("Math" FILES ${Math_src} ${Math_headers}) include_directories(${Common_SOURCE_DIR}) include_directories(${ZLib_HEADERS_DIR}) add_library(common OBJECT ${BaseC_src} ${BaseC_headers} ${BaseCpp_src} ${BaseCpp_headers} ${LZW_src} ${LZW_headers} ${Math_src} ${Math_headers}) set_target_properties(common PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}")Indigo-indigo-1.2.3/common/base_c/000077500000000000000000000000001271037650300167055ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/base_c/bitarray.c000066400000000000000000000237301271037650300206730ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include "base_c/bitarray.h" int bitGetBit (const void *bitarray, int bitno) { return ((((char *)bitarray)[bitno / 8] & (char)(1 << (bitno % 8))) == 0) ? 0 : 1; } void bitSetBit (void *bitarray, int bitno, int value) { if (value) ((char *)bitarray)[bitno / 8] |= (1 << (bitno % 8)); else ((char *)bitarray)[bitno / 8] &= ~(1 << (bitno % 8)); } void bitFlipBit (void *bitarray, int bitno) { ((char *)bitarray)[bitno / 8] ^= (1 << (bitno % 8)); } int bitTestEquality (const void *bits1, const void *bits2, int nbits) { char *chars1 = (char *)bits1; char *chars2 = (char *)bits2; char mask = ~(0xFF << (nbits & 7)); int i; for (i = 0; i < nbits / 8; i++) if (chars1[i] != chars2[i]) return 0; if ((chars1[nbits / 8] & mask) != (chars2[nbits / 8] & mask)) return 0; return 1; } int bitTestEquality_Array (const void *bits, const void *bitarray, int array_start, int nbits) { int i; for (i = 0; i < nbits; i++) if (bitGetBit(bits, i) != bitGetBit(bitarray, array_start + i)) return 0; return 1; } int bitTestEqualityByMask (const void *bits1, const void *bits2, const void *bitMaskVoid, int nbits) { const char *chars1 = (const char *)bits1; const char *chars2 = (const char *)bits2; const char *bitMask = (const char *)bitMaskVoid; char mask = ~(0xFF << (nbits & 7)); int i; for (i = 0; i < nbits / 8; i++) if ((chars1[i] & bitMask[i]) != (chars2[i] & bitMask[i])) return 0; if (((chars1[nbits / 8] & bitMask[nbits / 8]) & mask) != ((chars2[nbits / 8] & bitMask[nbits / 8]) & mask)) return 0; return 1; } // res = a & (b ^ ~c) int bitGetAandBxorNotC (const void *a, const void *b, const void *c, void *res, int nbits) { const char *ac = (const char *)a; const char *bc = (const char *)b; const char *cc = (const char *)c; char *rc = (char *)res; int i; for (i = 0; i < nbits / 8; i++) rc[i] = ac[i] & (bc[i] ^ ~cc[i]); if ((nbits & 7) != 0) rc[i] = ac[i] & (bc[i] ^ ~cc[i]); return 1; } int bitGetOnesCountByte (byte value) { static const int onesCount[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; return onesCount[value]; } int bitGetOnesCountDword (dword v) { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan v = v - ((v >> 1) & 0x55555555); // reuse input as temporary v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count } int bitGetOnesCountQword (qword value) { return bitGetOnesCountDword((dword)value) + bitGetOnesCountDword((dword)(value >> 32)); } int bitGetOnesCount (const byte *data, int size) { int count = 0; while (size-- > 0) count += bitGetOnesCountByte(*data++); return count; } int bitGetOneHOIndex (byte value) { static const int oneHOIndex[] = {0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8}; return oneHOIndex[value]; } int bitGetOneLOIndex (byte value) { static const int oneLOIndex[] = {8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}; return oneLOIndex[value]; } int bitGetSize (int nbits) { return (nbits + 7) / 8; } int bitTestOnes (const byte *pattern, const byte *candidate, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); const qword *pattern_ptr = (const qword *)pattern; const qword *candidate_ptr = (const qword *)candidate; while (qwords_count-- > 0) { if ((*candidate_ptr & *pattern_ptr) != *pattern_ptr) return 0; pattern_ptr++; candidate_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); if ((*candidate_ptr & *pattern_ptr & mask) != (*pattern_ptr & mask)) return 0; } return 1; } int bitIdecticalBits (const byte *bit1, const byte *bit2, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); int count = 0; const qword *bit1_ptr = (const qword *)bit1; const qword *bit2_ptr = (const qword *)bit2; while (qwords_count-- > 0) { qword id = ~(*bit1_ptr ^ *bit2_ptr); count += bitGetOnesCountQword(id); bit1_ptr++; bit2_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); qword id = (~(*bit1_ptr ^ *bit2_ptr)) & mask; count += bitGetOnesCountQword(id); } return count; } int bitCommonOnes (const byte *bit1, const byte *bit2, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); int count = 0; const qword *bit1_ptr = (const qword *)bit1; const qword *bit2_ptr = (const qword *)bit2; while (qwords_count-- > 0) { qword id = *bit1_ptr & *bit2_ptr; count += bitGetOnesCountQword(id); bit1_ptr++; bit2_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); qword id = *bit1_ptr & *bit2_ptr & mask; count += bitGetOnesCountQword(id); } return count; } int bitUniqueOnes (const byte *bit1, const byte *bit2, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); int count = 0; const qword *bit1_ptr = (const qword *)bit1; const qword *bit2_ptr = (const qword *)bit2; while (qwords_count-- > 0) { qword id = *bit1_ptr & ~*bit2_ptr; count += bitGetOnesCountQword(id); bit1_ptr++; bit2_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); qword id = *bit1_ptr & ~*bit2_ptr & mask; count += bitGetOnesCountQword(id); } return count; } int bitDifferentOnes (const byte *bit1, const byte *bit2, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); int count = 0; const qword *bit1_ptr = (const qword *)bit1; const qword *bit2_ptr = (const qword *)bit2; while (qwords_count-- > 0) { qword id = *bit1_ptr ^ *bit2_ptr; count += bitGetOnesCount((byte*)&id, sizeof(qword)); bit1_ptr++; bit2_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); qword id = (*bit1_ptr ^ *bit2_ptr) & mask; count += bitGetOnesCountQword(id); } return count; } int bitUnionOnes (const byte *bit1, const byte *bit2, int n_bytes) { int qwords_count = n_bytes / sizeof(qword); int bytes_left = n_bytes - qwords_count * sizeof(qword); int count = 0; const qword *bit1_ptr = (const qword *)bit1; const qword *bit2_ptr = (const qword *)bit2; while (qwords_count-- > 0) { qword id = *bit1_ptr | *bit2_ptr; count += bitGetOnesCountQword(id); bit1_ptr++; bit2_ptr++; } if (bytes_left != 0) { qword mask = ~(qword)0 >> (64 - 8 * bytes_left); qword id = (*bit1_ptr | *bit2_ptr) & mask; count += bitGetOnesCountQword(id); } return count; } // a &= b void bitAnd (byte *a, const byte *b, int nbytes) { while (nbytes-- > 0) { *a = *a & *b; a++; b++; } } // a |= b void bitOr (byte *a, const byte *b, int nbytes) { while (nbytes-- > 0) { *a = *a | *b; a++; b++; } } int bitIsAllZero (const void *bits, int nbytes) { const byte *a = (const byte *)bits; while (nbytes-- > 0) { if (*a != 0) return 0; a++; } return 1; } int bitLog2Dword (dword input) { int count = 0; while (input > 0) { count++; input >>= 1; } return count; } Indigo-indigo-1.2.3/common/base_c/bitarray.h000066400000000000000000000051471271037650300207020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bitarray_h__ #define __bitarray_h__ #ifdef __cplusplus extern "C" { #endif #include "base_c/defs.h" DLLEXPORT int bitGetBit (const void *bitarray, int bitno); DLLEXPORT void bitSetBit (void *bitarray, int bitno, int value); DLLEXPORT void bitFlipBit (void *bitarray, int bitno); DLLEXPORT int bitTestEquality (const void *bits1, const void *bits2, int nbits); DLLEXPORT int bitTestEquality_Array (const void *bits, const void *bitarray, int array_start, int nbits); DLLEXPORT int bitTestEqualityByMask (const void *bits1, const void *bits2, const void *bitMask, int nbits); // res = a & (b ^ ~c) DLLEXPORT int bitGetAandBxorNotC (const void *a, const void *b, const void *c, void *res, int nbits); // Get number of ones DLLEXPORT int bitGetOnesCountByte (byte value); DLLEXPORT int bitGetOnesCountQword (qword value); DLLEXPORT int bitGetOnesCountDword (dword value); DLLEXPORT int bitGetOnesCount (const byte *data, int size); // Get high-order 1-bit in byte DLLEXPORT int bitGetOneHOIndex (byte value); // Get low-order 1-bit in byte DLLEXPORT int bitGetOneLOIndex (byte value); DLLEXPORT int bitGetSize (int nbits); DLLEXPORT int bitTestOnes (const byte *pattern, const byte *candidate, int n_bytes); DLLEXPORT int bitIdecticalBits (const byte *bit1, const byte *bit2, int n_bytes); DLLEXPORT int bitCommonOnes (const byte *bit1, const byte *bit2, int n_bytes); DLLEXPORT int bitUniqueOnes (const byte *bit1, const byte *bit2, int n_bytes); DLLEXPORT int bitDifferentOnes (const byte *bit1, const byte *bit2, int n_bytes); DLLEXPORT int bitUnionOnes (const byte *bit1, const byte *bit2, int n_bytes); DLLEXPORT void bitAnd (byte *a, const byte *b, int n_bytes); DLLEXPORT void bitOr (byte *a, const byte *b, int nbytes); // Check whether bit array is zero DLLEXPORT int bitIsAllZero (const void *bits, int nbytes); DLLEXPORT int bitLog2Dword (dword input); #ifdef __cplusplus } #endif #endif Indigo-indigo-1.2.3/common/base_c/defs.h000066400000000000000000000045411271037650300200030ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __defs_h__ #define __defs_h__ #if !defined(__max) #define __max(a,b) (((a) > (b)) ? (a) : (b)) #endif #if !defined(__min) #define __min(a,b) (((a) < (b)) ? (a) : (b)) #endif #if !defined(__min3) #define __min3(a,b,c) (__min(a, __min(b, c))) #endif #if !defined(__max3) #define __max3(a,b,c) (__max(a, __max(b, c))) #endif #if !defined(__sign) #define __sign(a) (a > 0 ? 1 : (a < 0 ? -1 : 0)) #endif #define __swap(a, b, tmp) ((tmp) = (b), (b) = (a), (a) = (tmp)) #if defined(_WIN32) && !defined(__MINGW32__) //#define vsnprintf _vsnprintf #define snprintf _snprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define NELEM(arr) ((int)(sizeof(arr) / sizeof(arr[0]))) #ifndef dword typedef unsigned int dword; #endif #ifndef __byte_typedef__ #define __byte_typedef__ typedef unsigned char byte; #endif #ifndef EXPORT_SYMBOL #ifdef _WIN32 #define EXPORT_SYMBOL __declspec(dllexport) #elif (defined __GNUC__ || defined __APPLE__) #define EXPORT_SYMBOL __attribute__ ((visibility ("default"))) #else #define EXPORT_SYMBOL #endif #endif #ifndef DLLEXPORT #ifdef _WIN32 #ifdef INDIGO_PLUGIN #define DLLEXPORT __declspec(dllimport) #else #define DLLEXPORT EXPORT_SYMBOL #endif #else #define DLLEXPORT EXPORT_SYMBOL #endif #endif #ifndef CEXPORT #ifndef __cplusplus #define CEXPORT EXPORT_SYMBOL #else #define CEXPORT extern "C" EXPORT_SYMBOL #endif #endif #if defined(_WIN32) && !defined(__MINGW32__) #define qword unsigned _int64 #else #define qword unsigned long long #endif typedef unsigned short word; #if !defined(NULL) #define NULL 0L #endif #endif Indigo-indigo-1.2.3/common/base_c/nano.h000066400000000000000000000015161271037650300200140ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __nano_h__ #define __nano_h__ #include "base_c/defs.h" #ifdef __cplusplus extern "C" { #endif qword nanoClock (void); float nanoHowManySeconds (qword val); #ifdef __cplusplus } #endif #endif Indigo-indigo-1.2.3/common/base_c/nano_posix.c000066400000000000000000000016751271037650300212370ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/nano.h" #include qword nanoClock (void) { // actually returns microseconds struct timeval t; struct timezone tz; gettimeofday(&t, &tz); return t.tv_usec + t.tv_sec * 1000000ULL; } float nanoHowManySeconds (qword val) { return (float)((double)val / 1000000.); } Indigo-indigo-1.2.3/common/base_c/nano_win.c000066400000000000000000000020461271037650300206630ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "base_c/nano.h" qword nanoClock (void) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) return 0; return (qword)(counter.QuadPart); } float nanoHowManySeconds (qword val) { LARGE_INTEGER freq; double quot; if (!QueryPerformanceFrequency(&freq)) return 0; quot = (double)val / freq.QuadPart; return (float)quot; } Indigo-indigo-1.2.3/common/base_c/os_dir.h000066400000000000000000000025251271037650300203410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_dir__ #define __os_dir__ #include "base_c/defs.h" #ifdef __cplusplus extern "C" { #endif enum { OS_DIR_OK, OS_DIR_NOTDIR, OS_DIR_EXISTS, OS_DIR_NOTFOUND, OS_DIR_END, OS_DIR_OTHER }; DLLEXPORT int osDirExists (const char *dirname); DLLEXPORT int osDirCreate (const char *dirname); const char * osDirLastError (char *buf, int max_size); typedef struct { const char *dirname; char path[1024]; void *dirstream; #ifdef _WIN32 char first[1024]; #else const char *pattern; #endif } OsDirIter; int osDirSearch (const char *dirname, const char *pattern, OsDirIter *iter); int osDirNext (OsDirIter *iter); void osDirClose (OsDirIter *iter); #ifdef __cplusplus } #endif #endif Indigo-indigo-1.2.3/common/base_c/os_dir_posix.c000066400000000000000000000064021271037650300215540ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "base_c/os_dir.h" int osDirExists (const char *dirname) { if (access(dirname, 0) == 0) { struct stat status; stat(dirname, &status); if (status.st_mode & S_IFDIR) return OS_DIR_OK; else { errno = ENOTDIR; return OS_DIR_NOTDIR; } } else return OS_DIR_NOTFOUND; } int osDirCreate (const char *dirname) { int rc = osDirExists(dirname); if (rc == OS_DIR_OK) { errno = EEXIST; return OS_DIR_EXISTS; } if (rc == OS_DIR_NOTDIR) return OS_DIR_NOTDIR; errno = 0; rc = mkdir(dirname, 0777); if (rc == 0) return OS_DIR_OK; if (errno == ENOENT) return OS_DIR_NOTFOUND; return OS_DIR_OTHER; } int osDirSearch (const char *dirname, const char *pattern, OsDirIter *iter) { DIR *dirstream = opendir(dirname); if (dirstream == NULL) { if (errno == ENOENT) return OS_DIR_NOTFOUND; if (errno == ENOTDIR) return OS_DIR_NOTDIR; return OS_DIR_OTHER; } errno = 0; iter->dirname = dirname; iter->pattern = pattern; iter->dirstream = dirstream; return OS_DIR_OK; } int osDirNext (OsDirIter *iter) { DIR *dirstream = (DIR *)iter->dirstream; const char *pattern = iter->pattern; while (1) { struct dirent *entry = readdir(dirstream); int n; if (entry == NULL) { closedir(dirstream); iter->dirstream = 0; if (errno == 0) return OS_DIR_END; return OS_DIR_OTHER; } const char *name = entry->d_name; // match the pattern if (pattern != 0) { if (fnmatch(pattern, name, FNM_PATHNAME) != 0) continue; } n = snprintf(iter->path, sizeof(iter->path), "%s/%s", iter->dirname, name); if (n >= sizeof(iter->path)) { errno = ENAMETOOLONG; return OS_DIR_OTHER; } // check that it is a regular file if (access(iter->path, 0) == 0) { struct stat status; stat(iter->path, &status); if ((status.st_mode & S_IFMT) != S_IFREG) continue; } else continue; return OS_DIR_OK; } } void osDirClose (OsDirIter *iter) { if (iter->dirstream != 0) { closedir((DIR *)iter->dirstream); iter->dirstream = 0; } } const char * osDirLastError (char *buf, int max_size) { strncpy(buf, strerror(errno), max_size); return buf; } Indigo-indigo-1.2.3/common/base_c/os_dir_win32.c000066400000000000000000000074231271037650300213600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/os_dir.h" #include #include int osDirExists (const char *dirname) { DWORD attr = GetFileAttributesA((LPCSTR)dirname); if (attr == INVALID_FILE_ATTRIBUTES) { if (GetLastError() == ERROR_FILE_NOT_FOUND) return OS_DIR_NOTFOUND; return OS_DIR_OTHER; } if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { SetLastError(ERROR_DIRECTORY); return OS_DIR_NOTDIR; } return OS_DIR_OK; } int osDirCreate (const char *dirname) { if (CreateDirectoryA((LPCSTR)dirname, NULL)) return OS_DIR_OK; if (GetLastError() == ERROR_PATH_NOT_FOUND) return OS_DIR_NOTFOUND; if (GetLastError() == ERROR_ALREADY_EXISTS) return OS_DIR_EXISTS; return OS_DIR_OTHER; } int osDirSearch (const char *dirname, const char *pattern, OsDirIter *iter) { WIN32_FIND_DATAA data; char full_pattern[1024]; int n; HANDLE handle; if (pattern == 0) pattern = "*"; n = _snprintf(full_pattern, sizeof(full_pattern), "%s\\%s", dirname, pattern); if (n >= sizeof(full_pattern)) { SetLastError(ERROR_FILENAME_EXCED_RANGE); return OS_DIR_OTHER; } handle = FindFirstFileA((LPCSTR)full_pattern, &data); if (handle == INVALID_HANDLE_VALUE) { int err = GetLastError(); if (GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND) return OS_DIR_NOTFOUND; if (GetLastError() == ERROR_DIRECTORY) return OS_DIR_NOTDIR; return OS_DIR_OTHER; } SetLastError(0); iter->dirname = dirname; iter->dirstream = handle; strncpy(iter->first, data.cFileName, sizeof(iter->first)); return OS_DIR_OK; } int osDirNext (OsDirIter *iter) { WIN32_FIND_DATAA data; while (*iter->first || FindNextFileA((HANDLE)iter->dirstream, &data)) { DWORD attr; int n; if (*iter->first) n = _snprintf(iter->path, sizeof(iter->path), "%s\\%s", iter->dirname, iter->first); else n = _snprintf(iter->path, sizeof(iter->path), "%s\\%s", iter->dirname, data.cFileName); *iter->first = 0; if (n >= sizeof(iter->path)) { SetLastError(ERROR_FILENAME_EXCED_RANGE); osDirClose(iter); return OS_DIR_OTHER; } attr = GetFileAttributesA((LPCSTR)iter->path); if (attr == INVALID_FILE_ATTRIBUTES) { osDirClose(iter); return OS_DIR_OTHER; } if (attr & FILE_ATTRIBUTE_DIRECTORY) continue; return OS_DIR_OK; } if (GetLastError() == ERROR_NO_MORE_FILES) { osDirClose(iter); return OS_DIR_END; } return OS_DIR_OTHER; } void osDirClose (OsDirIter *iter) { if (iter->dirstream != NULL) { FindClose((HANDLE)iter->dirstream); iter->dirstream = NULL; } } const char * osDirLastError (char *buf, int max_size) { int err = GetLastError(); FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, (LPSTR)buf, max_size, NULL); if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; if (buf[strlen(buf) - 1] == '\r') buf[strlen(buf) - 1] = 0; return buf; } Indigo-indigo-1.2.3/common/base_c/os_sync.h000066400000000000000000000032541271037650300205370ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_sync_h__ #define __os_sync_h__ #include "base_c/defs.h" #ifndef _WIN32 #include #ifdef __APPLE__ #include #include #else #include #endif #endif #ifdef __cplusplus extern "C" { #endif // Cross-platform mutex support typedef struct tag_os_mutex { #ifdef _WIN32 void *data; #else pthread_mutex_t data; #endif } os_mutex; DLLEXPORT void osMutexCreate (os_mutex *mutex); DLLEXPORT void osMutexDelete (os_mutex *mutex); DLLEXPORT void osMutexLock (os_mutex *mutex); DLLEXPORT void osMutexUnlock (os_mutex *mutex); // // Semaphore // typedef struct tag_os_semaphore { #ifdef _WIN32 void *data; #elif __APPLE__ semaphore_t data; #else sem_t data; #endif } os_semaphore; DLLEXPORT void osSemaphoreCreate (os_semaphore *sem, int initial_count, int max_count); DLLEXPORT void osSemaphoreDelete (os_semaphore *sem); DLLEXPORT void osSemaphoreWait (os_semaphore *sem); DLLEXPORT void osSemaphorePost (os_semaphore *sem); #ifdef __cplusplus } #endif #endif // __os_sync_h__ Indigo-indigo-1.2.3/common/base_c/os_sync_posix.c000066400000000000000000000034321271037650300217520ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Synchronization primitives support for POSIX // #include "base_c/os_sync.h" #include // // Mutex // void osMutexCreate (os_mutex *mutex) { pthread_mutex_init((pthread_mutex_t *)mutex, NULL); } void osMutexDelete (os_mutex *mutex) { pthread_mutex_destroy((pthread_mutex_t *)mutex); } void osMutexLock (os_mutex *mutex) { pthread_mutex_lock((pthread_mutex_t *)mutex); } void osMutexUnlock (os_mutex *mutex) { pthread_mutex_unlock((pthread_mutex_t *)mutex); } // // Semaphore // void osSemaphoreCreate (os_semaphore *sem, int initial_count, int max_count) { #ifdef __APPLE__ semaphore_create(mach_task_self(), &sem->data, SYNC_POLICY_FIFO, initial_count); #else sem_init(&sem->data, 0, initial_count); #endif } void osSemaphoreDelete (os_semaphore *sem) { #ifdef __APPLE__ semaphore_destroy(mach_task_self(), sem->data); #else sem_destroy(&sem->data); #endif } void osSemaphoreWait (os_semaphore *sem) { #ifdef __APPLE__ semaphore_wait(sem->data); #else sem_wait(&sem->data); #endif } void osSemaphorePost (os_semaphore *sem) { #ifdef __APPLE__ semaphore_signal(sem->data); #else sem_post(&sem->data); #endif } Indigo-indigo-1.2.3/common/base_c/os_sync_win32.c000066400000000000000000000035511271037650300215540ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Synchronization primitives support for WIN32 // #include #include #include "base_c/os_sync.h" // // Mutex // void osMutexCreate (os_mutex *mutex) { mutex->data = malloc(sizeof(CRITICAL_SECTION)); InitializeCriticalSection((CRITICAL_SECTION *)mutex->data); } void osMutexDelete (os_mutex *mutex) { DeleteCriticalSection((CRITICAL_SECTION *)mutex->data); free(mutex->data); mutex->data = NULL; } void osMutexLock (os_mutex *mutex) { if (mutex->data == NULL) return; EnterCriticalSection((CRITICAL_SECTION *)mutex->data); } void osMutexUnlock (os_mutex *mutex) { if (mutex->data == NULL) return; LeaveCriticalSection((CRITICAL_SECTION *)mutex->data); } // // Semaphore // void osSemaphoreCreate (os_semaphore *sem, int initial_count, int max_count) { sem->data = malloc(sizeof(HANDLE)); *(HANDLE *)sem->data = CreateSemaphore(NULL, initial_count, max_count, NULL); } void osSemaphoreDelete (os_semaphore *sem) { CloseHandle(*(HANDLE *)sem->data); free(sem->data); } void osSemaphoreWait (os_semaphore *sem) { WaitForSingleObject(*(HANDLE *)sem->data, INFINITE); } void osSemaphorePost (os_semaphore *sem) { ReleaseSemaphore(*(HANDLE *)sem->data, 1, NULL); } Indigo-indigo-1.2.3/common/base_c/os_thread.h000066400000000000000000000021651271037650300210320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_thread_h__ #define __os_thread_h__ // // Crossplatform thread support // #ifdef _WIN32 #define THREAD_RET unsigned long #define THREAD_MOD __stdcall #define THREAD_END return 0 #else #include #define THREAD_RET void* #define THREAD_MOD #define THREAD_END pthread_exit(NULL); return 0 #endif #ifdef __cplusplus extern "C" { #endif void osThreadCreate (THREAD_RET (THREAD_MOD *func)(void *param), void *param); #ifdef __cplusplus } #endif #endif // __os_thread_h__ Indigo-indigo-1.2.3/common/base_c/os_thread_posix.c000066400000000000000000000015321271037650300222440ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/os_thread.h" void osThreadCreate (THREAD_RET (THREAD_MOD *func)(void *param), void *param) { pthread_t thread; pthread_create(&thread, NULL, func, (void *)param); pthread_detach(thread); } Indigo-indigo-1.2.3/common/base_c/os_thread_win32.c000066400000000000000000000014701271037650300220450ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/os_thread.h" #include void osThreadCreate (THREAD_RET (THREAD_MOD *func)(void *param), void *param) { CreateThread(NULL, 0, func, param, 0, NULL); } Indigo-indigo-1.2.3/common/base_c/os_tls.h000066400000000000000000000021321271037650300203570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_tls_h__ #define __os_tls_h__ #ifdef _WIN32 typedef int TLS_IDX_TYPE; #else #include typedef pthread_key_t TLS_IDX_TYPE; #endif #ifdef __cplusplus extern "C" { #endif int osTlsAlloc (TLS_IDX_TYPE* key); int osTlsFree (TLS_IDX_TYPE key); int osTlsSetValue (TLS_IDX_TYPE key, void* value); int osTlsGetValue (void** value, TLS_IDX_TYPE key); qword osGetThreadID (void); #ifdef __cplusplus } #endif #endif /*__os_tls_h__*/ Indigo-indigo-1.2.3/common/base_c/os_tls_posix.c000066400000000000000000000024161271037650300216010ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/defs.h" #include "base_c/os_tls.h" int osTlsAlloc (TLS_IDX_TYPE* key) { return !pthread_key_create(key, NULL); } int osTlsFree (TLS_IDX_TYPE key) { return pthread_key_delete(key); } int osTlsSetValue (TLS_IDX_TYPE key, void* value) { return !pthread_setspecific(key, value); } int osTlsGetValue (void** value, TLS_IDX_TYPE key) { *value = pthread_getspecific(key); return *value != NULL; } //#if sizeof(pthread_t) > 4 //#error 'pthread_t' - type is too large. Should be 4 bytes long or less. //#else qword osGetThreadID (void) { return (qword)pthread_self(); } //#endif Indigo-indigo-1.2.3/common/base_c/os_tls_win32.c000066400000000000000000000023041271037650300213750ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_c/defs.h" #include "base_c/os_tls.h" #ifdef _WIN32 #include int osTlsAlloc (TLS_IDX_TYPE* key) { if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { return FALSE; } return TRUE; } int osTlsFree (TLS_IDX_TYPE key) { return !!TlsFree(key); } int osTlsSetValue (TLS_IDX_TYPE key, void* value) { return !!TlsSetValue(key, value); } int osTlsGetValue (void** value, TLS_IDX_TYPE key) { *value = TlsGetValue(key); return *value != NULL; } qword osGetThreadID (void) { return (qword)GetCurrentThreadId(); } #endif Indigo-indigo-1.2.3/common/base_cpp/000077500000000000000000000000001271037650300172455ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/base_cpp/array.h000066400000000000000000000275501271037650300205450ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __array_h__ #define __array_h__ #include #include #include #include #include #include "base_c/defs.h" #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(ArrayError); template class Array { public: DECL_TPL_ERROR(ArrayError); explicit Array () { _reserved = 0; _length = 0; _array = NULL; } ~Array () { if (_array != NULL) { free(_array); _array = NULL; } } void clear () { _length = 0; } void reserve (int to_reserve) { // Addtional check for unexpectedly large memory allocations (larger than 512 Mb) if (to_reserve * sizeof(T) >= 1 << 30) throw Error("memory to reserve (%d x %d) is large than allowed threshold", to_reserve, (int)sizeof(T)); if (to_reserve <= 0) throw Error("to_reserve = %d", to_reserve); if (to_reserve > _reserved) { if (_length < 1) { free(_array); _array = NULL; } T *oldptr = _array; _array = (T *)realloc(_array, sizeof(T) * to_reserve); if (_array == NULL) { _array = oldptr; throw Error("reserve(): no memory"); } _reserved = to_reserve; } } void zerofill () { if (_length > 0) memset(_array, 0, _length * sizeof(T)); } void fffill () { if (_length > 0) memset(_array, 0xFF, _length * sizeof(T)); } void fill (const T &value) { for (int i = 0; i < size(); i++) _array[i] = value; } const T * ptr () const { return _array; } T * ptr () { return _array; } const T & operator [] (int index) const { if (index < 0 || _length - index <= 0) throw Error("invalid index %d (size=%d)", index, _length); return _array[index]; } T & operator [] (int index) { if (index < 0 || _length - index <= 0) throw Error("invalid index %d (size=%d)", index, _length); return _array[index]; } const T & at (int index) const { return (*this)[index]; } T & at (int index) { return (*this)[index]; } int size (void) const { return _length; } int sizeInBytes (void) const { return _length * sizeof(T); } void copy (const Array &other) { copy(other._array, other._length); } void copy (const T *other, int count) { if (count > 0) { clear_resize(count); memcpy(_array, other, count * sizeof(T)); } else _length = 0; } void concat (const Array &other) { concat(other._array, other.size()); } void concat (const T *other, int count) { if (count > 0) { int length = _length; resize(length + count); memcpy(_array + length, other, count * sizeof(T)); } } int memcmp (const Array &other) const { if (_length < other._length) return -1; if (_length > other._length) return -1; if (_length == 0) return 0; return ::memcmp(_array, other._array, _length * sizeof(T)); } void remove (int idx, int span = 1) { if (idx < 0 || idx - _length - span + 1 >= 0) throw Error("remove(): invalid index %d with span %d (size=%d)", idx, span, _length); memmove(_array + idx, _array + idx + span, sizeof(T) * (_length - idx - span)); _length -= span; } void remove_replace (int idx) { if (idx < 0 || idx >= _length) throw Error("remove_replace(): invalid index %d (size=%d)", idx, _length); if (idx < _length - 1) _array[idx] = _array[_length - 1]; _length--; } int find (const T &value) const { return find(0, _length, value); } int find (int from, int to, const T &value) const { for (int i = from; i < to; i++) if (_array[i] == value) return i; return -1; } int count (const T &value) const { return count(0, _length, value); } int count (int from, int to, const T &value) const { int cnt = 0; for (int i = from; i < to; i++) if (_array[i] == value) cnt++; return cnt; } void swap (int idx1, int idx2) { if (idx1 < 0 || idx1 >= _length) throw Error("swap(): invalid index %d (size=%d)", idx1, _length); if (idx2 < 0 || idx2 >= _length) throw Error("swap(): invalid index %d (size=%d)", idx2, _length); if (idx1 == idx2) return; T tmp; memcpy(&tmp, _array + idx1, sizeof(T)); memcpy(_array + idx1, _array + idx2, sizeof(T)); memcpy(_array + idx2, &tmp, sizeof(T)); } void push (T elem) { resize(_length + 1); _array[_length - 1] = elem; } T & push () { resize(_length + 1); return _array[_length - 1]; } T & pop () { if (_length <= 0) throw Error("stack underflow"); return _array[--_length]; } T & top () { if (_length <= 0) throw Error("stack underflow"); return _array[_length - 1]; } const T & top () const { if (_length <= 0) throw Error("stack underflow"); return _array[_length - 1]; } T & top (int offset) { if (_length - offset <= 0) throw Error("stack underflow"); return _array[_length - 1 - offset]; } const T & top (int offset) const { if (_length - offset <= 0) throw Error("stack underflow"); return _array[_length - 1 - offset]; } void resize (int newsize) { if (newsize > _reserved) reserve((newsize + 1) * 2); _length = newsize; } void expand (int newsize) { if (_length < newsize) resize(newsize); } void expandFill (int newsize, const T &value) { while (_length < newsize) push(value); } void clear_resize (int newsize) { if (_reserved < newsize) { _length = 0; reserve((newsize + 1) * 2); } _length = newsize; } void swap (Array &other) { T *tmp_t; __swap(_array, other._array, tmp_t); int tmp_int; __swap(_reserved, other._reserved, tmp_int); __swap(_length, other._length, tmp_int); } // CMP_FUNCTOR has two arguments and returns sign of comparation template void insertionSort (int start, int end, CmpFunctor cmp) { int i, j; char tmp[sizeof(T)]; // can't use T directly because it may have destructor for (i = start + 1; i <= end; i++) { j = i; while (j > start && cmp(_array[j - 1], _array[j]) > 0) { T *a1 = _array + j - 1; T *a2 = a1 + 1; memcpy(&tmp, a1, sizeof(T)); memcpy(a1, a2, sizeof(T)); memcpy(a2, &tmp, sizeof(T)); j--; } } } // CMP_FUNCTOR has two arguments and returns sign of comparation template void qsort (int start, int end, CmpFunctor cmp) { // Sort elements from start to end if (start >= end) return; if (end - start < 10) insertionSort(start, end, cmp); struct { T *lo, *hi; } stack[32], *sp; char tmp[sizeof(T)]; // can't use T directly because it may have destructor sp = stack; // push our initial values onto the stack sp->lo = _array + start; sp->hi = _array + end + 1; sp++; while (sp > stack) { // pop lo and hi off the stack sp--; T *high = sp->hi, *low = sp->lo; T *hi = high - 1; T *lo = low; T *pivot = low; while (1) { while (lo < high && lo != pivot && cmp(*lo, *pivot) < 0) lo++; while (hi > low && (hi == pivot || cmp(*hi, *pivot) >= 0)) hi--; if (lo < hi) { memcpy(&tmp, lo, sizeof(T)); memcpy(lo, hi, sizeof(T)); memcpy(hi, &tmp, sizeof(T)); if (lo == pivot) pivot = hi; else if (hi == pivot) pivot = lo; hi--; } else { hi++; if (hi == high) // done with this segment break; // push the larger segment onto the stack and continue // sorting the smaller segment. if ((hi - low) > (high - hi)) { sp->lo = low; sp->hi = hi; sp++; hi = high; low = lo; } else { sp->hi = high; sp->lo = hi; sp++; high = hi; lo = low; } pivot = lo; hi--; } } } } template void qsort (int start, int end, int (*cmp)(T1, T2, void*), void *context) { this->qsort(start, end, _CmpFunctorCaller(cmp, context)); } template void qsort (int (*cmp)(T1, T2, void*), void *context) { this->qsort(0, _length - 1, cmp, context); } // Array-specific void appendString (const char *str, bool keep_zero) { int len = (int)strlen(str); int initial_size = _length; if (initial_size > 0 && _array[initial_size - 1] == 0) initial_size--; resize(initial_size + len); memcpy(_array + initial_size, str, len); if (keep_zero) push(0); } void readString (const char *str, bool keep_zero) { clear(); appendString(str, keep_zero); } void upper (const char *source) { clear(); while (*source != 0) push(::toupper(*source++)); push(0); } void lower (const char *source) { clear(); while (*source != 0) push(::tolower(*source++)); push(0); } void toupper () { for (int i = 0; i < _length; i++) _array[i] = ::toupper(_array[i]); } void tolower () { for (int i = 0; i < _length; i++) _array[i] = ::tolower(_array[i]); } protected: T *_array; int _reserved; int _length; private: Array (const Array &); // no implicit copy Array& operator= (const Array& right); // no copy constructor template class _CmpFunctorCaller { public: _CmpFunctorCaller (int (*cmp)(T1, T2, void*), void *context) : _context(context), _cmp(cmp) {} int operator () (T1 arg1, T2 arg2) const { return _cmp(arg1, arg2, _context); } private: void *_context; int (*_cmp)(T1, T2, void*); }; }; } // operators defined here for use with ObjArray<> and ObjPool<> template void * operator new (size_t size, T *allocated_area) { return allocated_area; } template void operator delete (void *ptr, T *allocated_area) { } #endif Indigo-indigo-1.2.3/common/base_cpp/auto_iter.h000066400000000000000000000016171271037650300214160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __auto_iter_h__ #define __auto_iter_h__ namespace indigo { class AutoIterator { public: AutoIterator( int idx ); int operator* () const; bool operator!= ( const AutoIterator &other ) const; protected: int _idx; }; } #endif // __auto_iter_h__ Indigo-indigo-1.2.3/common/base_cpp/auto_ptr.h000066400000000000000000000035661271037650300212650ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __auto_ptr__ #define __auto_ptr__ #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(AutoPtrError); template class AutoPtr { public: explicit AutoPtr (T *ptr = 0) { _ptr = ptr; } ~AutoPtr () { delete _ptr; } T * get () const { return _ptr; } T & ref () const { if (_ptr == 0) throw Error("no reference"); return *_ptr; } T * operator -> () const { if (_ptr == 0) throw Error("no reference"); return _ptr; } T * release () { if (_ptr == 0) throw Error("nothing to release"); T *ptr = _ptr; _ptr = 0; return ptr; } void reset (T *ptr) { if (ptr != _ptr) { delete _ptr; _ptr = ptr; } } void free () { reset(0); } AutoPtr& operator= (T *ptr) { reset(ptr); return *this; } void create () { reset(new T()); } template void create (A &a) { reset(new T(a)); } DECL_TPL_ERROR(AutoPtrError); protected: T *_ptr; private: AutoPtr (const AutoPtr &); // no implicit copy AutoPtr & operator = (const AutoPtr &other); }; } #endif Indigo-indigo-1.2.3/common/base_cpp/bitinworker.cpp000066400000000000000000000035201271037650300223100ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/bitinworker.h" #include "base_cpp/scanner.h" using namespace indigo; BitInWorker::BitInWorker( int StartBits, Scanner &NewIn ) : _bits(StartBits), _scanner(NewIn) { _bitBuffer = 0L; _bitBufferCount = 0; } bool BitInWorker::isEOF( void ) { if (_scanner.isEOF()) { if (_bitBufferCount < _bits) return true; else return false; } return false; } bool BitInWorker::readBits( int &Code ) { unsigned int Res; if (_scanner.isEOF()) { if (_bitBufferCount < _bits) return false; else { Res = (_bitBuffer >> (sizeof(int) * 8 - _bits)); _bitBuffer <<= _bits; _bitBufferCount -= _bits; Code = Res; return true; } } while (_bitBufferCount < _bits) { int offset = (sizeof(int) - 1) * 8 - _bitBufferCount; _bitBuffer |= (byte)(_scanner.readByte()) << offset; _bitBufferCount += 8; if (_scanner.isEOF()) break; } Res = (_bitBuffer >> (sizeof(int) * 8 - _bits)); _bitBuffer <<= _bits; _bitBufferCount -= _bits; Code = Res; return true; } BitInWorker::~BitInWorker( void ) { } /* END OF 'BITINWORKER.CPP' FILE */ Indigo-indigo-1.2.3/common/base_cpp/bitinworker.h000066400000000000000000000022531271037650300217570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bitinworker_h__ #define __bitinworker_h__ #include "base_c/defs.h" namespace indigo { class Scanner; class DLLEXPORT BitInWorker { public: BitInWorker( int StartBits, Scanner &NewIn ); bool readBits( int &Code ); bool isEOF( void ); ~BitInWorker( void ); private: int _bits; /* Code size */ int _bitBufferCount; dword _bitBuffer; Scanner &_scanner; BitInWorker( const BitInWorker & ); }; } #endif /* __bitinworker_h__ */ /* END OF 'BITINWORKER.H' FILE */ Indigo-indigo-1.2.3/common/base_cpp/bitoutworker.cpp000066400000000000000000000032461271037650300225160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/bitoutworker.h" #include "base_cpp/output.h" using namespace indigo; BitOutWorker::BitOutWorker( int StartBits, Output &NewOut ) : _bits(StartBits), _output(NewOut) { _bitBuffer = 0; _bitBufferCount = 0; }; bool BitOutWorker::writeBits( int Code ) { unsigned int offset = sizeof(int) * 8 - _bits - _bitBufferCount, UCode = (unsigned)Code; _bitBuffer |= (dword) UCode << offset; _bitBufferCount += _bits; while (_bitBufferCount >= 8) { unsigned int next_byte; next_byte = _bitBuffer >> ((sizeof(int) - sizeof(char)) * 8); _output.writeByte(next_byte); _bitBuffer <<= 8; _bitBufferCount -= 8; } return true; } void BitOutWorker::close( void ) { unsigned int last_byte; if (_bitBufferCount) { last_byte = _bitBuffer >> ((sizeof(int) - sizeof(char)) * 8); _output.writeByte(last_byte); _bitBufferCount = 0; _bitBuffer = 0L; } } BitOutWorker::~BitOutWorker( void ) { close(); } /* END OF 'BITOUTWORKER.CPP' FILE */ Indigo-indigo-1.2.3/common/base_cpp/bitoutworker.h000066400000000000000000000022461271037650300221620ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __bitoutworker_h__ #define __bitoutworker_h__ #include "base_c/defs.h" namespace indigo { class Output; class BitOutWorker { public: BitOutWorker( int StartBits, Output &NewOut ); bool writeBits( int Code ); void close( void ); ~BitOutWorker( void ); private: int _bits; /* Code size */ int _bitBufferCount; dword _bitBuffer; Output &_output; BitOutWorker( const BitOutWorker & ); }; } #endif /* __bitoutworker_h__ */ /* END OF 'BITOUTWORKER.H' FILE */ Indigo-indigo-1.2.3/common/base_cpp/cancellation_handler.cpp000066400000000000000000000047451271037650300241140ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/cancellation_handler.h" #include "base_c/nano.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" namespace indigo { // // TimeoutCancellationHandler // TimeoutCancellationHandler::TimeoutCancellationHandler (int mseconds) { reset(mseconds); } TimeoutCancellationHandler::~TimeoutCancellationHandler () { } bool TimeoutCancellationHandler::isCancelled () { qword dif_time = nanoClock() - _currentTime; if (_mseconds > 0 && nanoHowManySeconds(dif_time) * 1000 > _mseconds) { ArrayOutput mes_out(_message); mes_out.printf("The operation timed out: %d ms", _mseconds); mes_out.writeChar(0); return true; } return false; } const char* TimeoutCancellationHandler::cancelledRequestMessage () { return _message.ptr(); } void TimeoutCancellationHandler::reset (int mseconds) { _mseconds = mseconds; _currentTime = nanoClock(); } // // Global thread-local cancellation handler // class CancellationHandlerWrapper { public: CancellationHandlerWrapper () : handler(0) {} CancellationHandler* handler; }; static _SessionLocalContainer cancellation_handler; CancellationHandler* getCancellationHandler () { CancellationHandlerWrapper &wrapper = cancellation_handler.getLocalCopy(); return wrapper.handler; } CancellationHandler* setCancellationHandler (CancellationHandler* handler) { CancellationHandlerWrapper &wrapper = cancellation_handler.getLocalCopy(); CancellationHandler* prev = wrapper.handler; wrapper.handler = handler; return prev; } AutoCancellationHandler::AutoCancellationHandler(CancellationHandler& hand) { _prev = setCancellationHandler(&hand); } AutoCancellationHandler::~AutoCancellationHandler() { setCancellationHandler(_prev); } } Indigo-indigo-1.2.3/common/base_cpp/cancellation_handler.h000066400000000000000000000037361271037650300235600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cancellation_handler_h__ #define __cancellation_handler_h__ #include "base_c/defs.h" #include "base_cpp/array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT CancellationHandler { public: virtual bool isCancelled () = 0; virtual const char* cancelledRequestMessage () = 0; }; class DLLEXPORT TimeoutCancellationHandler : public CancellationHandler { public: TimeoutCancellationHandler(int mseconds = 0); virtual ~TimeoutCancellationHandler(); virtual bool isCancelled(); virtual const char* cancelledRequestMessage(); void reset (int mseconds); private: Array _message; int _mseconds; qword _currentTime; }; // Global thread-local cancellation handler DLLEXPORT CancellationHandler* getCancellationHandler (); // Returns previous cancellation handler DLLEXPORT CancellationHandler* setCancellationHandler (CancellationHandler* handler); /* * Automatic cancellation handler can be used to store cancel callback within one code block */ class DLLEXPORT AutoCancellationHandler { public: AutoCancellationHandler(CancellationHandler& hand); virtual ~AutoCancellationHandler(); private: CancellationHandler* _prev; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif /* __cancellation_handler_h__ */ /* END OF 'cancellation_handler.H' FILE */ Indigo-indigo-1.2.3/common/base_cpp/chunk_storage.cpp000066400000000000000000000033661271037650300226150ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/chunk_storage.h" using namespace indigo; IMPL_ERROR(ChunkStorage, "chunk storage"); ChunkStorage::ChunkStorage () { _offset.push(0); } void ChunkStorage::clear () { _arr.clear(); _offset.clear(); _offset.push(0); } byte* ChunkStorage::add (int n_bytes) { int prev_size = _offset.top(); _arr.resize(prev_size + n_bytes); _offset.push(prev_size + n_bytes); return _arr.ptr() + prev_size; } void ChunkStorage::add (const byte *data, int n_bytes) { byte *ptr = add(n_bytes); memcpy(ptr, data, n_bytes); } void ChunkStorage::add (const Array &data) { add((const byte *)data.ptr(), data.size()); } void ChunkStorage::add (const char *str) { add((const byte *)str, strlen(str) + 1); } byte* ChunkStorage::get (int i) { return _arr.ptr() + _offset[i]; } int ChunkStorage::getSize (int i) { return _offset[i + 1] - _offset[i]; } int ChunkStorage::count (void) { return _offset.size() - 1; } void ChunkStorage::pop () { if (count() == 0) throw Error("Cannot pop element from empty chunk storage"); _offset.pop(); _arr.resize(_offset[_offset.size() - 1]); } Indigo-indigo-1.2.3/common/base_cpp/chunk_storage.h000066400000000000000000000023211271037650300222500ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __chunk_storage_h__ #define __chunk_storage_h__ // Storage for chunks of bytes #include "base_cpp/array.h" #include "base_cpp/exception.h" namespace indigo { class ChunkStorage { public: ChunkStorage (); void clear (); byte* add (int n_bytes); void add (const byte *data, int n_bytes); void add (const Array &data); void add (const char *str); int count (void); byte* get (int i); int getSize (int i); void pop (); DECL_ERROR; private: Array _arr; Array _offset; }; } #endif // __chunk_storage_h__ Indigo-indigo-1.2.3/common/base_cpp/common_exceptions_impl.cpp000066400000000000000000000027701271037650300245310ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/nullable.h" #include "base_cpp/array.h" #include "base_cpp/cyclic_array.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/d_bitset.h" #include "base_cpp/obj.h" #include "base_cpp/pool.h" #include "base_cpp/ptr_array.h" #include "base_cpp/ptr_pool.h" #include "base_cpp/queue.h" #include "base_cpp/red_black.h" IMPL_EXCEPTION(indigo, NullableError, "Nullable"); IMPL_EXCEPTION(indigo, ArrayError, "array"); IMPL_EXCEPTION(indigo, CyclicArrayError, "cyclic array"); IMPL_EXCEPTION(indigo, AutoPtrError, "autoptr"); IMPL_EXCEPTION(indigo, DbitsetError, "Dynamic bitset"); IMPL_EXCEPTION(indigo, ObjError, "obj"); IMPL_EXCEPTION(indigo, PoolError, "pool"); IMPL_EXCEPTION(indigo, PtrArrayError, "ptr array"); IMPL_EXCEPTION(indigo, PtrPoolError, "ptr pool"); IMPL_EXCEPTION(indigo, QueueError, "queue"); IMPL_EXCEPTION(indigo, RedBlackTreeError, "red-black tree"); Indigo-indigo-1.2.3/common/base_cpp/crc32.cpp000066400000000000000000000034301271037650300206650ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "base_c/defs.h" #include "base_cpp/crc32.h" using namespace indigo; CRC32 _crc; // singletone unsigned _Reflect (unsigned ref, char ch) { unsigned value = 0; // swap bits 0-7, 1-6, ... for (int i = 1; i <= ch; i++) { if(ref & 1) value |= 1 << (ch - i); ref >>= 1; } return value; } CRC32::CRC32 () { int i, polynom = 0x04c11db7; // used in PKZip, WinZip, and Ethernet. for (i = 0; i < 256; i++) { _table[i] = _Reflect(i, 8) << 24; for (int j = 0; j < 8; j++) _table[i] = (_table[i] << 1) ^ (_table[i] & (1 << 31) ? polynom : 0); _table[i] = _Reflect(_table[i], 32); } } CRC32::~CRC32 () { } unsigned CRC32::get (const char *text) { unsigned code = 0xffffffff; while (*text != 0) { code = (code >> 8) ^ _crc._table[(code & 0xFF) ^ ((byte)*text)]; text++; } return code ^ 0xffffffff; } unsigned CRC32::get (const char *text, int len) { unsigned code = 0xffffffff; for (int i = 0; i < len; i++) code = (code >> 8) ^ _crc._table[(code & 0xFF) ^ ((byte)text[i])]; return code ^ 0xffffffff; } Indigo-indigo-1.2.3/common/base_cpp/crc32.h000066400000000000000000000020011271037650300203230ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __crc32_h__ #define __crc32_h__ #include "base_c/defs.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT CRC32 { public: explicit CRC32 (); virtual ~CRC32 (); static unsigned get (const char *text); static unsigned get (const char *text, int len); private: unsigned _table[256]; }; } #endif Indigo-indigo-1.2.3/common/base_cpp/cyclic_array.h000066400000000000000000000036651271037650300220740ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cyclic_array_h__ #define __cyclic_array_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(CyclicArrayError); // Cyclic array template struct CyclicArray { public: DECL_TPL_ERROR(CyclicArrayError); explicit CyclicArray (void) { _offset = 0; } void setSize (int max_size) { _array.resize(max_size); } void zeroFill (void) { _array.zerofill(); } bool isInBound (int index) const { return index >= _offset && index < _offset + _array.size(); } const T & operator [] (int index) const { int length = _array.size(); if (length == 0) throw Error("Zero length"); int offset = index % _array.size(); return index >= 0 ? _array[offset] : _array[length + offset]; } T & operator [] (int index) { int length = _array.size(); if (length == 0) throw Error("Zero length"); int offset = index % _array.size(); return index >= 0 ? _array[offset] : _array[length + offset]; } void setOffset (int offset) { _offset = offset; } protected: Array _array; int _offset; private: CyclicArray (const CyclicArray &); // no implicit copy }; } #endif // __cyclic_array_h__ Indigo-indigo-1.2.3/common/base_cpp/d_bitset.cpp000066400000000000000000000302251271037650300215500ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/d_bitset.h" #include "base_cpp/obj_array.h" using namespace indigo; //private--------------------------------------------------------------------------------------- void Dbitset::_recalculateWordsInUse() { // Traverse the bitset until a used word is found int i; for (i = _length - 1; i >= 0; --i) if (_words[i] != 0) break; _wordsInUse = i + 1; // The new logical size } void Dbitset::_initWords(int nbits) { _wordsInUse=0; _length = _wordIndex(nbits-1) + 1; _words.clear_resize(_length); _words.zerofill(); _bitsNumber = nbits; } void Dbitset::_expandTo(int wordIndex) { int wordsRequired = wordIndex+1; if (_wordsInUse < wordsRequired) { _wordsInUse = wordsRequired; } } int Dbitset::_bitCount(qword b) const { b = (b & 0x5555555555555555LL) + ((b >> 1) & 0x5555555555555555LL); b = (b & 0x3333333333333333LL) + ((b >> 2) & 0x3333333333333333LL); b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FLL; b = b + (b >> 8); b = b + (b >> 16); b = (b + (b >> 32)) & 0x0000007F; return (int) b; } int Dbitset::_leastSignificantBitPosition(qword n) const { if (n == 0) return -1; int pos = 63; if (n & 0x00000000FFFFFFFFLL) { pos -= 32; } else { n >>= 32; } if (n & 0x000000000000FFFFLL) { pos -= 16; } else { n >>= 16; } if (n & 0x00000000000000FFLL) { pos -= 8; } else { n >>= 8; } if (n & 0x000000000000000FLL) { pos -= 4; } else { n >>= 4; } if (n & 0x0000000000000003LL) { pos -= 2; } else { n >>= 2; } if (n & 0x0000000000000001LL) { pos -= 1; } return pos; } //public------------------------------------------------------------------------------------------------------ Dbitset::Dbitset() { _initWords(BITS_PER_WORD); } Dbitset::Dbitset(int nbits) { _initWords(nbits); } Dbitset::~Dbitset() { } void Dbitset::copy(const Dbitset& set) { if (_length != set._length) { _length = set._length; _words.resize(_length); } _bitsNumber=set._bitsNumber; _wordsInUse=set._wordsInUse; _words.copy(set._words); } void Dbitset::copySubset(const Dbitset &set) { if (_bitsNumber==set._bitsNumber) copy(set); if (_bitsNumber= _wordsInUse) return; _words[wordindex] &= ~((qword)1 << bitIndex); _recalculateWordsInUse(); } void Dbitset::clear() { while (_wordsInUse > 0) _words[--_wordsInUse] = 0; } bool Dbitset::get(int bitIndex) const { int word_index = _wordIndex(bitIndex); bitIndex -= (word_index<= _wordsInUse) return -1; fromIndex -= (u << ADDRESS_BITS_PER_WORD); qword word = _words[u] & (WORD_MASK << fromIndex); for(; word == 0; word = _words[u]) { if (++u >= _wordsInUse) return -1; } return _leastSignificantBitPosition(word) + (u << ADDRESS_BITS_PER_WORD); } bool Dbitset::intersects(const Dbitset& set) const{ for (int i= __min(_wordsInUse, set._wordsInUse) - 1; i >= 0; --i) if ((_words[i] & set._words[i]) != 0) return true; return false; } bool Dbitset::complements(const Dbitset& set) const{ if (_wordsInUse > set._wordsInUse) for (int i = _wordsInUse - 1; i >= set._wordsInUse; --i) if (_words[i]) return true; for (int i = _wordsInUse - 1; i >= 0; --i) if ((_words[i] & ~set._words[i]) != 0) return true; return false; } void Dbitset::andWith(const Dbitset& set) { while (_wordsInUse > set._wordsInUse) _words[--_wordsInUse] = 0; // Perform logical AND on words in common for (int i = 0; i < _wordsInUse; ++i) _words[i] &= set._words[i]; _recalculateWordsInUse(); } void Dbitset::orWith(const Dbitset& set) { if (_wordsInUse < set._wordsInUse) _wordsInUse = set._wordsInUse; // Perform logical OR on words in common for (int i = 0; i < _wordsInUse; ++i) { _words[i] |= set._words[i]; } } void Dbitset::xorWith(const Dbitset& set) { if (_wordsInUse < set._wordsInUse) { _wordsInUse = set._wordsInUse; } // Perform logical XOR on words in common for (int i = 0; i < _wordsInUse; ++i) _words[i] ^= set._words[i]; _recalculateWordsInUse(); } void Dbitset::andNotWith(const Dbitset& set) { for (int i= __min(_wordsInUse, set._wordsInUse) - 1; i >= 0; --i) _words[i] &= ~set._words[i]; _recalculateWordsInUse(); } bool Dbitset::equals(const Dbitset& set) const { if (_wordsInUse != set._wordsInUse) return false; // Check words in use by both BitSets for (int i = 0; i < _wordsInUse; ++i) if (_words[i] != set._words[i]) return false; return true; } void Dbitset::resize(int size) { int new_length = _wordIndex(size-1) + 1; _words.resize(new_length); if(new_length > _length) for(int i = _length; i < new_length; ++i) _words[i] = 0; _bitsNumber = size; _length = new_length; } bool Dbitset::isSubsetOf(const Dbitset& set) const { for (int i = 0; i < _wordsInUse; ++i) if (_words[i] & ~set._words[i]) return false; return true; } bool Dbitset::isProperSubsetOf(const Dbitset &set) const { bool proper = false; for (int i = 0; i < _wordsInUse; ++i) { if (set._words[i] & ~_words[i]) proper = true; if (_words[i] & ~set._words[i]) return false; } return proper; } void Dbitset::zeroFill() { _words.zerofill(); _recalculateWordsInUse(); } void Dbitset::bsOrBs(const Dbitset& set1,const Dbitset& set2) { int max_words = __max(set1._wordsInUse, set2._wordsInUse); for(int i = 0; i < max_words; ++i) { _words[i] = set1._words[i] | set2._words[i]; } for(int i = max_words; i < _wordsInUse; ++i) { _words[i] = 0; } _wordsInUse = max_words; } void Dbitset::bsAndNotBs(const Dbitset& set1, const Dbitset& set2) { for(int i = 0; i < set1._wordsInUse; ++i) { _words[i] = set1._words[i] & ~set2._words[i]; } for(int i = set1._wordsInUse; i < _wordsInUse; ++i) { _words[i] = 0; } _recalculateWordsInUse(); } void Dbitset::bsAndBs(const Dbitset& set1, const Dbitset& set2) { for(int i = 0; i < set1._wordsInUse; ++i) { _words[i] = set1._words[i] & set2._words[i]; } for(int i = set1._wordsInUse; i < _wordsInUse; ++i) { _words[i] = 0; } _recalculateWordsInUse(); } int Dbitset::bitsNumber() const{ int bits_num = 0; for(int i = 0; i < _wordsInUse; ++i) bits_num += _bitCount(_words[i]); return bits_num; } // some 64-bit compilators can not correctly work with big values shift. So it must be processed manually qword Dbitset::shiftOne(int shiftNumber) { qword result = (qword)1; while(shiftNumber > MAX_SHIFT_NUMBER) { result = result << MAX_SHIFT_NUMBER; shiftNumber -= MAX_SHIFT_NUMBER; } result = result << shiftNumber; return result; } Dbitset::Iterator::Iterator(Dbitset& self): _fromWordIdx(0), _fromByteIdx(-1), _fromBitIdx(-1), _fromIndexes(0) { _words = self._words.ptr(); _wordsInUse = self._wordsInUse; } static ObjArray< Array > all_indexes; int Dbitset::Iterator::begin() { if(all_indexes.size() == 0) { for (unsigned int buf = 0; buf < 256; ++buf) { Array& indexes = all_indexes.push(); _fillIndexes(buf, indexes); } } if (_wordsInUse == 0) return -1; _fromWordIdx = -1; _fromBitIdx = -1; _fromByteIdx = -1; _fromIndexes = 0; _fromWord = 0; return next(); } int Dbitset::Iterator::next() { if(_fromIndexes) { ++_fromBitIdx; if(_fromBitIdx < _fromIndexes->size()) return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } _fromIndexes = 0; if(_fromWord) { for (++_fromByteIdx; _fromByteIdx < 8; ++_fromByteIdx) { int from_byte = ((byte*) _fromWord)[_fromByteIdx]; if (from_byte == 0) continue; _fromIndexes = &(all_indexes.at(from_byte)); _fromBitIdx = 0; _shiftByte = _fromByteIdx << 3; return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } } _fromWord = 0; for (++_fromWordIdx; _fromWordIdx < _wordsInUse; ++_fromWordIdx) { _fromWord = &_words[_fromWordIdx]; if(*_fromWord == 0) continue; for (_fromByteIdx = 0; _fromByteIdx < 8; ++_fromByteIdx) { int from_byte = ((byte*) _fromWord)[_fromByteIdx]; if (from_byte == 0) continue; _fromIndexes = &(all_indexes.at(from_byte)); _fromBitIdx = 0; _shiftByte = _fromByteIdx << 3; _shiftWord = _fromWordIdx << 6; return _fromIndexes->at(_fromBitIdx) + _shiftByte + _shiftWord; } } return -1; } void Dbitset::Iterator::_fillIndexes(byte buf, Array&indexes) { byte test_buf; for (int buf_idx = 0; buf_idx < 8; ++buf_idx) { test_buf = (byte)1 << buf_idx; if(buf & test_buf) { indexes.push(buf_idx); } } } Indigo-indigo-1.2.3/common/base_cpp/d_bitset.h000066400000000000000000000140221271037650300212120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _d_bitset_ #define _d_bitset_ #include "base_cpp/array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { DECL_EXCEPTION(DbitsetError); class DLLEXPORT Dbitset { //bitsets are packed into arrays of "words." Currently a word is //a long long, which consists of 64 bits, requiring 6 address bits. //The choice of word size is determined purely by performance concerns. private: enum { ADDRESS_BITS_PER_WORD = 6, BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD, MAX_SHIFT_NUMBER = 63 }; static const qword WORD_MASK = 0xFFFFFFFFFFFFFFFFULL; //the number of words in the logical size of this BitSet. int _wordsInUse; //bits number in bitset int _bitsNumber; //words size int _length; //given a bit index, return word index containing it. static int _wordIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_WORD; } //set the field wordsInUse with the logical size in words of the bit //set. WARNING:This method assumes that the number of words actually //in use is less than or equal to the current value of wordsInUse! void _recalculateWordsInUse(); //creates a new bit set. All bits are initially false. void _initWords(int nbits); // ensures that the BitSet can accommodate a given wordIndex void _expandTo(int wordIndex); int _bitCount(qword b) const; int _leastSignificantBitPosition(qword n) const; Array _words; Dbitset(const Dbitset&); //no implicit copy Dbitset &operator = (const Dbitset&); //no implicit assign public: Dbitset(); //creates a bit set whose initial size explicit Dbitset(int nbits); ~Dbitset(); //sets the bit at the specified index to the complement of its current value void flip(int bitIndex); //sets each bit from the specified fromIndex to the specified toIndex (exclusive) //to the complement of its current value void flip(int fromIndex, int toIndex); //sets all bits to the complement values void flip(); //sets all bits to true void set(); //sets the bit at the specified index to true void set(int bitIndex); //sets the bit at the specified index to the specified value. void set(int bitIndex, bool value); //sets the bits from the specified fromIndex to the specified toIndex(exclusive) void set(int fromIndex, int toIndex); //sets the bit specified by the index to false void reset(int bitIndex); //sets all of the bits in this BitSet to false void clear(); //returns the value of the bit with the specified index bool get(int bitIndex) const; //returns the index of the first bit that is set to true //that occurs on or after the specified starting index. If no such //bit exists then -1 is returned int nextSetBit(int fromIndex) const; //returns true if BitSet contains no bits that are set true bool isEmpty() const {return _wordsInUse == 0; }; //Returns true if the specified BitSet has any bits set to true //that are also set to true in this BitSet bool intersects(const Dbitset& set) const; bool complements(const Dbitset& set) const; //Performs a logical AND of this target BitSet with the argument BitSet void andWith(const Dbitset& set); //Performs a logical OR of this target BitSet with the argument BitSet void orWith(const Dbitset& set); //Performs a logical XOR of this target BitSet with the argument BitSet void xorWith(const Dbitset& set); //Clears all of the bits in this BitSet whose corresponding // bit is set in the specified BitSet. void andNotWith(const Dbitset& set); //Returns the number of bits of space actually in use by this //BitSet to represent bit values int size() const {return _bitsNumber; }; //Compares this BitSet against the specified BitSet. bool equals(const Dbitset& set) const; //Cloning this BitSet produces a new BitSet void copy(const Dbitset& set); //copy part of BitSet void copySubset(const Dbitset& set); //resizes this BitSet void resize(int size); //checks if this BitSet is subset of argument BitSet bool isSubsetOf(const Dbitset& set) const; //checks if this BitSet is proper subset of argument BitSet bool isProperSubsetOf(const Dbitset& set) const; //fills with false all bits in this BitSet void zeroFill(); void bsOrBs(const Dbitset& set1,const Dbitset& set2); //Clears all of the bits in first argument BitSet whose corresponding // bits is set in the specified second argument BitSet. Result -> this void bsAndNotBs(const Dbitset& set1,const Dbitset& set2); //Performs a logical AND of two argument BitSets. result saves to this Bitset void bsAndBs(const Dbitset& set1,const Dbitset& set2); int bitsNumber() const; qword shiftOne(int shiftNumber); class Iterator { public: Iterator(Dbitset&); ~Iterator() { } int begin(); int next(); inline int end() { return -1; } private: void _fillIndexes(byte buf, Array&indexes); int _wordsInUse; qword* _words; int _fromWordIdx; int _fromByteIdx; int _fromBitIdx; qword* _fromWord; Array* _fromIndexes; int _shiftByte; int _shiftWord; private: Iterator(const Iterator&); //no implicit copy }; DECL_TPL_ERROR(DbitsetError); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/common/base_cpp/exception.cpp000066400000000000000000000042261271037650300217530ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include "base_c/defs.h" #include "base_cpp/exception.h" using namespace indigo; Exception::Exception (const char *format, ...) { va_list args; va_start(args, format); _init(format, args); _code = -1; va_end(args); } Exception::Exception () { _code = -1; snprintf(_message, sizeof(_message), "unknown error"); } Exception::Exception (const Exception &other) { _code = other._code; strncpy(_message, other._message, sizeof(_message)); } const char * Exception::message () { return _message; } int Exception::code () { return _code; } void Exception::appendMessage (const char *format, ...) { va_list args; va_start(args, format); char added_message[1024]; vsnprintf(added_message, sizeof(added_message), format, args); strncat(_message, added_message, sizeof(_message)); va_end(args); } void Exception::_init (const char *format, va_list args) { vsnprintf(_message, sizeof(_message), format, args); } void Exception::_init (const char *prefix, const char *format, va_list args) { char format_full[1024]; snprintf(format_full, sizeof(format_full), "%s: %s", prefix, format); _init(format_full, args); } Exception * Exception::clone () { Exception *cloned = new Exception; _cloneTo(cloned); return cloned; } void Exception::_cloneTo (Exception *dest) const { dest->_code = _code; strncpy(dest->_message, _message, sizeof(_message)); } void Exception::throwSelf () { throw *this; } Exception::~Exception () { } Indigo-indigo-1.2.3/common/base_cpp/exception.h000066400000000000000000000134751271037650300214260ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __exception_h__ #define __exception_h__ #include #include #include "base_c/defs.h" namespace indigo { class DLLEXPORT Exception { public: explicit Exception (const char *format, ...); virtual ~Exception (); int code (); const char * message (); void appendMessage (const char *format, ...); virtual Exception * clone (); virtual void throwSelf (); Exception (const Exception &); protected: explicit Exception (); void _init (const char *format, va_list args); void _init (const char *prefix, const char *format, va_list args); void _cloneTo (Exception *dest) const; int _code; char _message[1024]; private: }; #define DECL_EXCEPTION_BODY(ExceptionName, Parent) \ ExceptionName : public Parent \ { \ public: \ explicit ExceptionName (const char *format, ...); \ virtual ~ExceptionName (); \ virtual Exception* clone (); \ virtual void throwSelf (); \ ExceptionName (const ExceptionName &other); \ protected: \ explicit ExceptionName (); \ } #define DECL_EXCEPTION2(ExceptionName, Parent) \ class DLLEXPORT DECL_EXCEPTION_BODY(ExceptionName, Parent) #define DECL_EXCEPTION(ExceptionName) DECL_EXCEPTION2(ExceptionName, indigo::Exception) #define DECL_EXCEPTION_NO_EXP2(ExceptionName, Parent) \ class DECL_EXCEPTION_BODY(ExceptionName, Parent) #define DECL_EXCEPTION_NO_EXP(ExceptionName) DECL_EXCEPTION_NO_EXP2(ExceptionName, indigo::Exception) #define IMPL_EXCEPTION2(Namespace, ExceptionName, Parent, prefix) \ Namespace::ExceptionName::ExceptionName () \ { \ } \ \ Namespace::ExceptionName::ExceptionName (const char *format, ...) \ : Parent() \ { \ va_list args; \ \ va_start(args, format); \ _init(prefix, format, args); \ va_end(args); \ } \ \ Namespace::ExceptionName::~ExceptionName () {} \ \ indigo::Exception* Namespace::ExceptionName::clone () \ { \ ExceptionName *error = new ExceptionName(""); \ _cloneTo(error); \ return error; \ } \ void Namespace::ExceptionName::throwSelf () \ { \ throw *this; \ } \ Namespace::ExceptionName::ExceptionName (const ExceptionName &other) : Parent () \ { \ other._cloneTo(this); \ } \ #define IMPL_EXCEPTION(Namespace, ExceptionName, prefix) \ IMPL_EXCEPTION2(Namespace, ExceptionName, indigo::Exception, prefix) #define DECL_ERROR2(Parent) DECL_EXCEPTION2(Error, Parent) #define DECL_ERROR DECL_ERROR2(indigo::Exception) #define DECL_ERROR_NO_EXP DECL_EXCEPTION_NO_EXP(Error) #define DECL_TPL_ERROR(CommonErrorName) typedef CommonErrorName Error #define IMPL_ERROR2(Namespace, Parent, error_prefix) \ IMPL_EXCEPTION2(Namespace, Error, Parent, error_prefix) #define IMPL_ERROR(Namespace, error_prefix) IMPL_ERROR2(Namespace, indigo::Exception, error_prefix) #define DECL_TIMEOUT_EXCEPTION DECL_EXCEPTION(TimeoutException) #define IMPL_TIMEOUT_EXCEPTION(Namespace, prefix) \ IMPL_EXCEPTION(Namespace, TimeoutException, prefix " timeout") } #endif // __exception_h__ Indigo-indigo-1.2.3/common/base_cpp/gray_codes.cpp000066400000000000000000000035611271037650300220750ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Gray code enumertator without loops // #include "base_cpp/gray_codes.h" #include "base_c/bitarray.h" using namespace indigo; CP_DEF(GrayCodesEnumerator); GrayCodesEnumerator::GrayCodesEnumerator (int length, bool needFullCode) : CP_INIT, TL_CP_GET(_indices), TL_CP_GET(_code) { _bitChangeIndex = START; _needFullCode = needFullCode; _indices.resize(length + 1); for (int i = 0; i <= length; i++) _indices[i] = i; if (needFullCode) { _code.resize(bitGetSize(length)); _code.zerofill(); } } void GrayCodesEnumerator::next (void) { if (_indices.size() - 1 == 0) { _bitChangeIndex = END; return; } _bitChangeIndex = _indices[0]; if (_bitChangeIndex == _indices.size() - 1) _bitChangeIndex = END; else { _indices[0] = 0; _indices[_bitChangeIndex] = _indices[_bitChangeIndex + 1]; _indices[_bitChangeIndex + 1] = _bitChangeIndex + 1; if (_needFullCode) bitFlipBit(_code.ptr(), _bitChangeIndex); } } bool GrayCodesEnumerator::isDone (void) { return _bitChangeIndex == END; } int GrayCodesEnumerator::getBitChangeIndex (void) { return _bitChangeIndex; } const byte* GrayCodesEnumerator::getCode( void ) { return _code.ptr(); } Indigo-indigo-1.2.3/common/base_cpp/gray_codes.h000066400000000000000000000024441271037650300215410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __gray_codes_h__ #define __gray_codes_h__ #include "base_c/defs.h" #include "base_cpp/tlscont.h" namespace indigo { // Enumerate all gray codes starting with zero without loops class GrayCodesEnumerator { public: GrayCodesEnumerator (int length, bool needFullCode = false); void next (void); bool isDone (void); enum { START = -1, END = -2 }; // Ret: START for the call before first next int getBitChangeIndex (void); // Use bitarray.h functions const byte* getCode (void); private: CP_DECL; TL_CP_DECL(Array, _indices); TL_CP_DECL(Array, _code); bool _needFullCode; int _bitChangeIndex; }; } #endif Indigo-indigo-1.2.3/common/base_cpp/io_base.cpp000066400000000000000000000032671271037650300213620ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "base_cpp/io_base.h" #if defined(_WIN32) && !defined(__MINGW32__) #include #include #include #endif using namespace indigo; FILE *indigo::openFile( Encoding filename_encoding, const char *filename, const char *mode ) { FILE *file = 0; #if defined(_WIN32) && !defined(__MINGW32__) if (filename_encoding == ENCODING_UTF8) { wchar_t w_filename[1024]; wchar_t w_mode[10]; MultiByteToWideChar(CP_UTF8, 0, filename, -1, w_filename, 1024); MultiByteToWideChar(CP_UTF8, 0, mode, -1, w_mode, 10); file = _wfopen(w_filename, w_mode); } #endif if (file == 0) { // For ASCII and linux file = fopen(filename, mode); } return file; } #if defined(_WIN32) && !defined(__MINGW32__) CLocale CLocale::instance; _locale_t indigo::getCLocale () { return CLocale::instance.get(); } CLocale::CLocale () { _locale = _create_locale(LC_ALL, "C"); } CLocale::~CLocale () { _free_locale(_locale); } _locale_t CLocale::get () { return _locale; } #endif Indigo-indigo-1.2.3/common/base_cpp/io_base.h000066400000000000000000000022601271037650300210170ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __io_base_h__ #define __io_base_h__ #include #ifdef _WIN32 #include #endif namespace indigo { enum Encoding { ENCODING_ASCII = 1, ENCODING_UTF8 = 2 }; FILE *openFile( Encoding filename_encoding, const char *filename, const char *mode); #if defined(_WIN32) && !defined(__MINGW32__) _locale_t getCLocale (); class CLocale { public: CLocale (); ~CLocale (); _locale_t get (); static CLocale instance; protected: _locale_t _locale; }; #endif }; #endif Indigo-indigo-1.2.3/common/base_cpp/list.h000066400000000000000000000102401271037650300203660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __list_h__ #define __list_h__ #include "base_cpp/pool.h" namespace indigo { template class List { public: struct Elem { int prev; int next; T item; }; explicit List () : _pool(new Pool), _size(0), _head(-1), _tail(-1), _own_pool(true) { } explicit List (Pool &pool) : _pool(&pool), _size(0), _head(-1), _tail(-1), _own_pool(false) { } ~List () { clear(); if (_own_pool) delete _pool; } int add () { if (_size == 0) { _head = _pool->add(); _tail = _head; Elem &elem = _pool->at(_head); elem.prev = -1; elem.next = -1; } else { int idx = _pool->add(); Elem &elem = _pool->at(idx); _pool->at(_tail).next = idx; elem.prev = _tail; elem.next = -1; _tail = idx; } _size++; return _tail; } int add (const T &item) { int idx = add(); _pool->at(idx).item = item; return idx; } int insertAfter (int existing) { _pool->at(existing); // will throw if the element does not exist int idx = _pool->add(); Elem &ex = _pool->at(existing); Elem &elem = _pool->at(idx); elem.next = ex.next; elem.prev = existing; ex.next = idx; if (elem.next != -1) _pool->at(elem.next).prev = idx; if (_tail == existing) _tail = idx; _size++; return idx; } int insertBefore (int existing) { _pool->at(existing); // will throw if the element does not exist int idx = _pool->add(); Elem &ex = _pool->at(existing); Elem &elem = _pool->at(idx); elem.prev = ex.prev; elem.next = existing; ex.prev = idx; if (elem.prev != -1) _pool->at(elem.prev).next = idx; if (_head == existing) _head = idx; _size++; return idx; } void remove (int idx) { Elem &elem = _pool->at(idx); if (elem.prev != -1) _pool->at(elem.prev).next = elem.next; else _head = elem.next; if (elem.next != -1) _pool->at(elem.next).prev = elem.prev; else _tail = elem.prev; _pool->remove(idx); _size--; } int size () const { return _size; } int begin () const { if (_head == -1) return _pool->end(); return _head; } int end () const { return _pool->end(); } int next (int idx) const { int res = _pool->at(idx).next; if (res == -1) return _pool->end(); return res; } int prev (int idx) const { return _pool->at(idx).prev; } int tail () const { return _tail; } void clear () { if (_own_pool) _pool->clear(); // or there may be other lists using the same _pool else while (_tail != -1) { int iter = _tail; _tail = _pool->at(iter).prev; _pool->remove(iter); } _size = 0; _head = -1; _tail = -1; } T & operator [] (int index) const { return _pool->at(index).item; } T & at (int index) const { return _pool->at(index).item; } protected: Pool *_pool; // the element pool may be shared amongst lists int _size; int _head; int _tail; bool _own_pool; private: List (const List & ); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/locale_guard.cpp000066400000000000000000000021211271037650300223660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/locale_guard.h" using namespace indigo; LocaleGuard::LocaleGuard () { #if defined(__linux__) || defined(__APPLE__) _locale = newlocale(LC_NUMERIC_MASK, "C", NULL); if (_locale != NULL) _baselocale = uselocale(_locale); else _baselocale = NULL; #endif } LocaleGuard::~LocaleGuard () { #if defined(__linux__) || defined(__APPLE__) if (_locale != NULL) { uselocale(_baselocale); freelocale(_locale); } #endif } Indigo-indigo-1.2.3/common/base_cpp/locale_guard.h000066400000000000000000000017351271037650300220450ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __locale_guard__ #define __locale_guard__ #if defined(__linux__) || defined(__APPLE__) #include #include #endif namespace indigo { class LocaleGuard { public: LocaleGuard(); ~LocaleGuard(); protected: #if defined(__linux__) || defined(__APPLE__) locale_t _locale; locale_t _baselocale; #endif }; } #endif Indigo-indigo-1.2.3/common/base_cpp/multimap.h000066400000000000000000000112221271037650300212440ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2016 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __multimap__ #define __multimap__ #include "base_cpp/red_black.h" namespace indigo { DECL_EXCEPTION(MultiMapError); template class MultiMap : public NonCopyable { public: DECL_TPL_ERROR(MultiMapError); explicit MultiMap() {} ~MultiMap() {} const RedBlackSet& keys() const; const RedBlackSet& get(const K &k) const; const RedBlackSet& operator[](const K &k) const; int size() const; bool find(const K &k, const V &v) const; void insert(const K &k, const V &v); void insert(const K &k, const Array &vs); void insert(const K &k, const RedBlackSet &vs); bool remove(const K &k); bool remove(const K &k, const V &v); void invert(MultiMap &target) const; void copy(MultiMap &target) const; void clear(); protected: RedBlackSet& _provide_set(const K &k); template void _copy(MultiMap &target, bool invert) const { for (auto i = _map.begin(); i != _map.end(); i = _map.next(i)) { const RedBlackSet &set = *_sets[_map.value(i)]; for (auto j = set.begin(); j != set.end(); j = set.next(j)) { K &k = _map.key(i); V &v = set.key(j); if (invert) { target.insert(v, k); } else { target.insert(k, v); } } } } RedBlackMap _map; RedBlackSet _keys; PtrArray> _sets; const RedBlackSet _nil; }; } #ifdef _DEBUG #include "assert.h" #define CHECK_KEYS assert(_map.size() == _keys.size()); #else #define CHECK_KEYS #endif using namespace indigo; template bool MultiMap::find(const K &k, const V &v) const { return get(k).find(v); } template const RedBlackSet& MultiMap::get(const K &k) const { int *i = _map.at2(k); if (i) { return *_sets[*i]; } return _nil; } template void MultiMap::insert(const K &k, const V &v) { _provide_set(k).insert(v); } template void MultiMap::insert(const K &k, const Array &vs) { RedBlackSet &set = _provide_set(k); for (auto i = 0; i < vs.size(); i++) { set.insert(vs[i]); } } template void MultiMap::insert(const K &k, const RedBlackSet &vs) { RedBlackSet &set = _provide_set(k); for (auto i = vs.begin(); i != vs.end(); i = vs.next(i)) { set.insert(vs.key(i)); } } template bool MultiMap::remove(const K &k) { int *i = _map.at2(k); if (!i) { return false; } _sets.remove(*i); _keys.remove(k); _map.remove(k); CHECK_KEYS; return true; } template bool MultiMap::remove(const K &k, const V &v) { int *i = _map.at2(k); if (!i) { return false; } RedBlackSet &set = *_sets[*i]; set.remove(v); if (set.size() < 1) { _sets.remove(*i); _keys.remove(k); _map.remove(k); } CHECK_KEYS; return true; } template void MultiMap::copy(MultiMap &target) const { _copy(target, false); } template void MultiMap::invert(MultiMap &target) const { _copy(target, true); } template void MultiMap::clear() { _map.clear(); _keys.clear(); _sets.clear(); } template const RedBlackSet& MultiMap::keys() const { return _keys; } template int MultiMap::size() const { return _map.size(); } template const RedBlackSet& MultiMap::operator[](const K &k) const { return get(k); } template RedBlackSet& MultiMap::_provide_set(const K &k) { int *i = _map.at2(k); if (i) { return *_sets[*i]; } _keys.insert(k); _map.insert(k, _sets.size()); CHECK_KEYS; return _sets.add(new RedBlackSet()); } #endifIndigo-indigo-1.2.3/common/base_cpp/non_copyable.h000066400000000000000000000015611271037650300220710ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __non_copyable__ #define __non_copyable__ namespace indigo { struct NonCopyable { NonCopyable() = default; NonCopyable(const NonCopyable&) = delete; NonCopyable & operator=(const NonCopyable&) = delete; }; } #endif Indigo-indigo-1.2.3/common/base_cpp/nullable.h000066400000000000000000000030551271037650300212170ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __nullable__ #define __nullable__ #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(NullableError); template class Nullable { public: Nullable () : _has_value(false), variable_name("") {} const T& get () const { if (!_has_value) throw Error("%s variable was not set"); return _value; } operator const T& () const { return get(); } Nullable& operator= (const T& value) { set(value); return *this; } void set (const T& value) { _value = value; _has_value = true; } void reset () { _has_value = false; } bool hasValue () const { return _has_value; } void setName (const char *name) { variable_name = name; } DECL_TPL_ERROR(NullableError); private: T _value; bool _has_value; const char *variable_name; }; } #endif // __nullable__ Indigo-indigo-1.2.3/common/base_cpp/obj.h000066400000000000000000000066421271037650300202000ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __obj_h__ #define __obj_h__ #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(ObjError); // Reusable storage for object template class Obj { public: Obj () : _initialized(false) { } ~Obj () { free(); } T * get () const { if (!_initialized) return 0; return _ptr(); } T * operator -> () const { if (!_initialized) throw Error("no object"); return _ptr(); } T & ref () const { if (!_initialized) throw Error("no object"); return *_ptr(); } T & create () { if (_initialized) throw Error("create(): already have object"); new (_storage) T(); _initialized = true; return *_ptr(); } template T & create (A &a) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a); _initialized = true; return *_ptr(); } template T & recreate (A &a) { free(); return create(a); } template T & create (const A &a) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a); _initialized = true; return *_ptr(); } template T & create (A &a, B &b) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a, b); _initialized = true; return *_ptr(); } template T & create (A &a, B *b) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a, b); _initialized = true; return *_ptr(); } template T & create (A &a, B &b, C *c) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a, b, c); _initialized = true; return *_ptr(); } template T & create (A &a, B &b, C &c) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a, b, c); _initialized = true; return *_ptr(); } template T & create (A &a, B *b, C &c) { if (_initialized) throw Error("create(): already have object"); new (_storage) T(a, b, c); _initialized = true; return *_ptr(); } void free () { if (_initialized) { _ptr()->~T(); _initialized = false; } } DECL_TPL_ERROR(ObjError); protected: T* _ptr () const { return (T*)_storage; } char _storage[sizeof(T)]; bool _initialized; private: Obj (const Obj &); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/obj_array.h000066400000000000000000000056401271037650300213730ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __objarray_h__ #define __objarray_h__ #include "base_cpp/array.h" namespace indigo { template class ObjArray { public: explicit ObjArray () { } ~ObjArray () { while (size() > 0) pop(); } const T & operator[] (int index) const { return _array[index]; } T & operator[] (int index) { return _array[index]; } const T & at (int index) const { return (*this)[index]; } T & at (int index) { return (*this)[index]; } int size (void) const { return _array.size(); } T & push () { void *addr = &_array.push(); new (addr) T(); return _array.top(); } template T & push (A &a) { void *addr = &_array.push(); new (addr) T(a); return _array.top(); } template T & push (A &a, B *b) { void *addr = &_array.push(); new (addr) T(a, b); return _array.top(); } template T & push (A &a, B &b, C &c) { void *addr = &_array.push(); new (addr) T(a, b, c); return _array.top(); } template T & push (A *a, B b, C c) { void *addr = &_array.push(); new (addr) T(a, b, c); return _array.top(); } void clear () { while (size() > 0) pop(); } void resize (int newSize) { while (newSize < size()) { pop(); } while (newSize > size()) { push(); } } void reserve (int size) { _array.reserve(size); } void expand (int newSize) { while (newSize > size()) { push(); } } void remove (int idx) { _array[idx].~T(); _array.remove(idx); } T & top () { return _array.top(); } const T & top () const { return _array.top(); } void pop () { _array.top().~T(); _array.pop(); } template void qsort (int (*cmp)(T1, T2, void *), void *context) { _array.qsort(cmp, context); } const T * ptr () const { return _array.ptr(); } protected: Array _array; private: ObjArray (const ObjArray &); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/obj_list.h000066400000000000000000000046771271037650300212410ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __obj_list_h__ #define __obj_list_h__ #include "base_cpp/list.h" namespace indigo { template class ObjList { public: typedef typename List::Elem Elem; ObjList () { } explicit ObjList (Pool &pool) : _list(pool) { } ~ObjList () { clear(); } int add () { int idx = _list.add(); new (&_list[idx]) T(); return idx; } template int add (A &a) { int idx = _list.add(); new (&_list[idx]) T(a); return idx; } int insertAfter (int existing) { int idx = _list.insertAfter(existing); new (&_list[idx]) T(); return idx; } template int insertAfter (int existing, A &a) { int idx = _list.insertAfter(existing); new (&_list[idx]) T(a); return idx; } int insertBefore (int existing) { int idx = _list.insertBefore(existing); new (&_list[idx]) T(); return idx; } template int insertBefore (int existing, A &a) { int idx = _list.insertBefore(existing); new (&_list[idx]) T(a); return idx; } void remove (int idx) { _list[idx].~T(); _list.remove(idx); } int size () const { return _list.size(); } int begin () const { return _list.begin(); } int end () const { return _list.end(); } int next (int idx) const { return _list.next(idx); } void clear () { while (_list.size() > 0) remove(_list.tail()); } T & operator [] (int index) const { return _list[index]; } T & at (int index) const { return _list[index]; } protected: List _list; private: ObjList (const ObjList & ); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/obj_pool.h000066400000000000000000000044321271037650300212240ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __obj_pool_h__ #define __obj_pool_h__ #include "base_cpp/pool.h" namespace indigo { template class ObjPool { public: ObjPool () { } ~ObjPool () { clear(); } int add () { int idx = _pool.add(); void *addr = &_pool[idx]; new (addr) T(); return idx; } template int add (A &a) { int idx = _pool.add(); void *addr = &_pool[idx]; new (addr) T(a); return idx; } template int add (A &a, B &b) { int idx = _pool.add(); void *addr = &_pool[idx]; new (addr) T(a, b); return idx; } void remove (int idx) { T &elem = _pool[idx]; elem.~T(); _pool.remove(idx); } int size () const { return _pool.size(); } int begin () const { return _pool.begin(); } int next (int i) const { return _pool.next(i); } int end () const { return _pool.end(); } void clear () { for (int i = _pool.begin(); i != _pool.end(); i = _pool.next(i)) _pool[i].~T(); _pool.clear(); } bool hasElement(int idx) const { return _pool.hasElement(idx); } const T & operator [] (int index) const { return _pool[index]; } T & operator [] (int index) { return _pool[index]; } const T & at (int index) const { return (*this)[index]; } T & at (int index) { return (*this)[index]; } protected: Pool _pool; private: ObjPool (const ObjPool & ); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/os_sync_wrapper.cpp000066400000000000000000000037201271037650300231700ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/os_sync_wrapper.h" using namespace indigo; // // osLock // OsLock::OsLock () { osMutexCreate(&_mutex); } OsLock::~OsLock () { osMutexDelete(&_mutex); } void OsLock::Lock () { osMutexLock(&_mutex); } void OsLock::Unlock () { osMutexUnlock(&_mutex); } // // Semaphore wrapper // OsSemaphore::OsSemaphore (int initial_count, int max_count) { osSemaphoreCreate(&_sem, initial_count, max_count); } OsSemaphore::~OsSemaphore () { osSemaphoreDelete(&_sem); } void OsSemaphore::Wait () { osSemaphoreWait(&_sem); } void OsSemaphore::Post () { osSemaphorePost(&_sem); } // // Message system // void OsMessageSystem::SendMsg (int message, void *param) { OsLocker locker(_sendLock); _localMessage = message; _localParam = param; _sendSem.Post(); _finishRecvSem.Wait(); } void OsMessageSystem::RecvMsg (int *message, void **result) { OsLocker locker(_recvLock); // Wait for sending _sendSem.Wait(); *message = _localMessage; if (result != NULL) *result = _localParam; _finishRecvSem.Post(); } OsMessageSystem::OsMessageSystem() : _sendSem(0, 1), _finishRecvSem(0, 1) { } // // ThreadSafeStaticObj // namespace indigo { DLLEXPORT OsLock & osStaticObjConstructionLock () { static OsLock _static_obj_construction_lock; return _static_obj_construction_lock; } } Indigo-indigo-1.2.3/common/base_cpp/os_sync_wrapper.h000066400000000000000000000063131271037650300226360ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_sync_wrapper_h__ #define __os_sync_wrapper_h__ #include "base_c/defs.h" #include "base_c/os_sync.h" #include "base_cpp/exception.h" namespace indigo { // os_mutex wrapper class DLLEXPORT OsLock { public: OsLock (); ~OsLock (); void Lock (); void Unlock (); private: os_mutex _mutex; }; // Automatic lock/unlock template class OsLockerT { public: OsLockerT (T *lock) : _lock(lock) { if (_lock != NULL) _lock->Lock(); else if (!lock_can_be_null) throw Exception("Passed lock object pointer is NULL"); } OsLockerT (T &lock) : _lock(&lock) { _lock->Lock(); } ~OsLockerT () { if (_lock != NULL) _lock->Unlock(); } private: T *_lock; }; typedef OsLockerT OsLocker; typedef OsLockerT OsLockerNullable; // // Semaphore wrapper // class DLLEXPORT OsSemaphore { public: OsSemaphore (int initial_count, int max_count); ~OsSemaphore (); void Wait (); void Post (); private: os_semaphore _sem; }; // // Message system // class OsMessageSystem { public: OsMessageSystem (); void SendMsg (int message, void *param = 0); void RecvMsg (int *message, void **result = 0); private: OsSemaphore _sendSem; OsSemaphore _finishRecvSem; OsLock _sendLock; OsLock _recvLock; volatile int _localMessage; void * volatile _localParam; }; // // Thread-safe static local variables initialization object // DLLEXPORT OsLock & osStaticObjConstructionLock (); // Local static variables with constructors should have OsLock // guard to avoid thread conflicts. // This object should be declared as ONLY static object because // _was_created variable should be zero by default. template class ThreadSafeStaticObj { public: ~ThreadSafeStaticObj() { if (_was_created) { _obj->~T(); _obj = 0; _was_created = false; } } T * ptr () { return _ptr(); } T & ref () { return *_ptr(); } T * operator -> () { return ptr(); } private: void _ensureInitialized () { if (!_was_created) { OsLocker locker(osStaticObjConstructionLock()); if (!_was_created) { _obj = new ((void *)_obj_data) T; _was_created = true; } } } T * _ptr () { _ensureInitialized(); return _obj; } T *_obj; char _obj_data[sizeof(T)]; volatile bool _was_created; // Zero for static objects }; } #endif // __os_sync_wrapper_h__ Indigo-indigo-1.2.3/common/base_cpp/os_thread_wrapper.cpp000066400000000000000000000275731271037650300234770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Thread support based on command dispatcher: // #ifdef _WIN32 #include #endif #ifdef __APPLE__ #include #endif #include "base_c/os_thread.h" #include "base_cpp/os_thread_wrapper.h" #include "base_cpp/exception.h" #include "base_cpp/tlscont.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/profiling.h" using namespace indigo; // Messages enum { MSG_NEED_TASK, MSG_RECV_COMMAND, MSG_RECV_RESULT, MSG_RECV_INDEX, MSG_NO_TASK, MSG_HANDLE_RESULT, MSG_SYSPEND_RESULT, MSG_SYSPEND_SEMAPHORE, MSG_OK, MSG_CONNECT, MSG_HANDLE_EXCEPTION, MSG_TERMINATE }; // Maximum number of results that are kept in queue if // _handling_order is HANDLING_ORDER_SERIAL static const int _MAX_RESULTS = 1000; OsCommandDispatcher::OsCommandDispatcher (int handling_order, bool same_session_IDs) { _storedResults.setSize(_MAX_RESULTS); _storedResults.zeroFill(); _handling_order = handling_order; _session_id = TL_GET_SESSION_ID(); _last_unique_command_id = 0; _same_session_IDs = same_session_IDs; } extern "C" THREAD_RET THREAD_MOD _threadFuncStatic (void *param) { OsCommandDispatcher *dispatcher = (OsCommandDispatcher *)param; dispatcher->_threadFunc(); THREAD_END; } void OsCommandDispatcher::run () { _run(3 * osGetProcessorsCount() / 2 + 1); } void OsCommandDispatcher::run (int nthreads) { if (nthreads < 0) // Use automatic thread count selection run(); else _run(nthreads); } void OsCommandDispatcher::_run (int nthreads) { _last_command_index = 0; _expected_command_index = 0; _need_to_terminate = false; _exception_to_forward = NULL; _left_thread_count = nthreads; if (_left_thread_count == 0) { _startStandalone(); return; } _parent_session_ID = TL_GET_SESSION_ID(); // Create handling threads for (int i = 0; i < _left_thread_count; i++) osThreadCreate(_threadFuncStatic, this); _mainLoop(); } void OsCommandDispatcher::_mainLoop () { profTimerStart(t, "dispatcher.main_loop"); // Main loop int msg; while (_left_thread_count != 0) { void *parameter; _baseMessageSystem.RecvMsg(&msg, ¶meter); if (msg == MSG_NEED_TASK) _onMsgNeedTask(); if (msg == MSG_HANDLE_RESULT) _onMsgHandleResult(); if (msg == MSG_HANDLE_EXCEPTION) _onMsgHandleException((Exception *)parameter); } if (_exception_to_forward != NULL) { Exception *cur = _exception_to_forward; _exception_to_forward = NULL; cur->throwSelf(); } } void OsCommandDispatcher::markToTerminate () { _need_to_terminate = true; } void OsCommandDispatcher::terminate () { markToTerminate(); _mainLoop(); } OsCommand* OsCommandDispatcher::_getVacantCommand () { OsCommand *command; if (_availableCommands.size() == 0) { command = _allocateCommand(); command->unique_id = _last_unique_command_id++; } else command = _availableCommands.pop(); command->clear(); return command; } OsCommandResult* OsCommandDispatcher::_getVacantResult () { OsCommandResult *result; if (_availableResults.size() == 0) result = _allocateResult(); else result = _availableResults.pop(); result->clear(); return result; } void OsCommandDispatcher::_onMsgNeedTask() { if (_need_to_terminate) { _privateMessageSystem.SendMsg(MSG_NO_TASK, NULL); _left_thread_count--; return; } OsCommandResult *result = _getVacantResult(); OsCommand *command = _getVacantCommand(); if (!_setupCommand(*command)) { _availableResults.add(result); _availableCommands.add(command); _privateMessageSystem.SendMsg(MSG_NO_TASK, NULL); _left_thread_count--; return; } _privateMessageSystem.SendMsg(MSG_RECV_INDEX, &_last_command_index); _privateMessageSystem.SendMsg(MSG_RECV_COMMAND, command); _privateMessageSystem.SendMsg(MSG_RECV_RESULT, result); _last_command_index++; } void OsCommandDispatcher::_handleResultWithCheck (OsCommandResult *result) { Exception *exception = 0; try { if (!_need_to_terminate) _handleResult(*result); } catch (Exception &e) { exception = e.clone(); } catch (...) { exception = Exception("Unknown exception").clone(); } if (exception != NULL) _handleException(exception); } void OsCommandDispatcher::_onMsgHandleResult () { OsCommandResult *result; OsCommand *command; int index, msg; void *param; _privateMessageSystem.RecvMsg(&msg, ¶m); if (msg != MSG_RECV_INDEX) throw Exception("cmdDispatcher::_OnMsgHandleResult: internal error"); index = *(int *)param; if (_handling_order == HANDLING_ORDER_SERIAL) if (!_storedResults.isInBound(index)) { profIncCounter("dispatcher.syspend_count", 1); _privateMessageSystem.SendMsg(MSG_SYSPEND_RESULT); _privateMessageSystem.RecvMsg(&msg, ¶m); if (msg != MSG_SYSPEND_SEMAPHORE) throw Exception("cmdDispatcher::_OnMsgHandleResult: internal error #2"); OsSemaphore *sem = (OsSemaphore *)param; _syspendedThreads.push(sem); return; } _privateMessageSystem.SendMsg(MSG_OK); _recvCommandAndResult(result, command); _availableCommands.add(command); if (_handling_order == HANDLING_ORDER_ANY) { // Handle result in parallel mode _handleResultWithCheck(result); _availableResults.add(result); } else { // Handle results in correct order _storedResults[index] = result; while (_storedResults[_expected_command_index] != NULL) { OsCommandResult *current = _storedResults[_expected_command_index]; _storedResults[_expected_command_index] = NULL; _handleResultWithCheck(current); _availableResults.add(current); _expected_command_index++; } _storedResults.setOffset(_expected_command_index); _wakeSuspended(); } } void OsCommandDispatcher::_wakeSuspended () { // Wake up all syspended threads for (int i = 0; i < _syspendedThreads.size(); i++) _syspendedThreads[i]->Post(); _syspendedThreads.clear(); } void OsCommandDispatcher::_onMsgHandleException (Exception *exception) { OsCommandResult *result; OsCommand *command; _recvCommandAndResult(result, command); _availableResults.add(result); _availableCommands.add(command); _handleException(exception); } void OsCommandDispatcher::_handleException (Exception *exception) { if (!_need_to_terminate) { _need_to_terminate = true; // Store exception to correct memory deallocation in the next time... TL_DECL(AutoPtr, exception_ptr); TL_GET(AutoPtr, exception_ptr); exception_ptr.reset(exception); _exception_to_forward = exception; _wakeSuspended(); } else { // This is second exception. Skip it delete exception; } } void OsCommandDispatcher::_threadFunc (void) { qword initial_SID = TL_GET_SESSION_ID(); if (_same_session_IDs) TL_SET_SESSION_ID(_parent_session_ID); _prepareThread(); OsSemaphore syspendSem(0, 1); while (true) { int msg; _baseMessageSystem.SendMsg(MSG_NEED_TASK, NULL); void *param; _privateMessageSystem.RecvMsg(&msg, ¶m); if (msg == MSG_NO_TASK) break; if (msg != MSG_RECV_INDEX) throw Exception("cmdDispatcher::_ThreadFunc: internal error"); int index; index = *(int *)param; OsCommandResult *result; OsCommand *command; _recvCommandAndResult(result, command); Exception *exception = NULL; try { result->clear(); command->execute(*result); } catch (Exception &e) { exception = e.clone(); } catch (...) { exception = Exception("Unknown exception").clone(); } if (exception != NULL) { // Forward exception into main thread _baseMessageSystem.SendMsg(MSG_HANDLE_EXCEPTION, exception); // Send current command and result to add // them to the vacant list _privateMessageSystem.SendMsg(MSG_RECV_COMMAND, command); _privateMessageSystem.SendMsg(MSG_RECV_RESULT, result); continue; } while (true) { _baseMessageSystem.SendMsg(MSG_HANDLE_RESULT, NULL); _privateMessageSystem.SendMsg(MSG_RECV_INDEX, &index); _privateMessageSystem.RecvMsg(&msg, ¶m); if (msg == MSG_SYSPEND_RESULT) { // Enter syspend mode _privateMessageSystem.SendMsg(MSG_SYSPEND_SEMAPHORE, &syspendSem); syspendSem.Wait(); continue; } else if (msg == MSG_OK) { _privateMessageSystem.SendMsg(MSG_RECV_COMMAND, command); _privateMessageSystem.SendMsg(MSG_RECV_RESULT, result); break; } else // This should terminate application throw Exception("cmdDispatcher::_ThreadFunc: internal error #2"); } } _cleanupThread(); TL_RELEASE_SESSION_ID(initial_SID); } void OsCommandDispatcher::_recvCommandAndResult (OsCommandResult *&result, OsCommand *&command) { for (int i = 0; i < 2; i++) { void *param; int msg; _privateMessageSystem.RecvMsg(&msg, ¶m); if (msg == MSG_RECV_RESULT) result = (OsCommandResult *)param; else if (msg == MSG_RECV_COMMAND) command = (OsCommand *)param; else throw Exception("cmdDispatcher::_RecvCommandAndResult"); } } OsCommandResult* OsCommandDispatcher::_allocateResult () { // Create empty results return new OsCommandResult; } void OsCommandDispatcher::_startStandalone () { OsCommandResult *result = _getVacantResult(); OsCommand *command = _getVacantCommand(); while (_setupCommand(*command)) { command->execute(*result); _handleResult(*result); command->clear(); result->clear(); } _availableResults.add(result); _availableCommands.add(command); } int osGetProcessorsCount (void) { static ThreadSafeStaticObj _processors_lock; OsLocker locker(_processors_lock.ref()); static int processors_count = 0; if (processors_count == 0) { #ifdef _WIN32 SYSTEM_INFO info; GetSystemInfo(&info); processors_count = info.dwNumberOfProcessors; #elif __APPLE__ // MacOS X int mib[2]; size_t len = sizeof(processors_count); /* set the mib for hw.ncpu */ mib[0] = CTL_HW; mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU; /* get the number of CPUs from the system */ sysctl(mib, 2, &processors_count, &len, NULL, 0); if (processors_count < 1) { mib[1] = HW_NCPU; sysctl(mib, 2, &processors_count, &len, NULL, 0); if (processors_count < 1) processors_count = 1; } #else char line[200]; FILE *cpuinfo_file = fopen("/proc/cpuinfo", "rt"); if (cpuinfo_file) { while (fgets(line, sizeof(line), cpuinfo_file)) if (!strncmp("processor", line, 9)) processors_count++; fclose(cpuinfo_file); } if (processors_count == 0) processors_count = 1; #endif } return processors_count; } Indigo-indigo-1.2.3/common/base_cpp/os_thread_wrapper.h000066400000000000000000000075151271037650300231360ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __os_thread_wrapper_h__ #define __os_thread_wrapper_h__ // // Thread wrapper based on command dispatcher: // 1. osCommandDispatcher setups a osCommand // 2. osCommand is executed (in the another thread) and // result is stored in the osCommandResult object. // 3. osCommandDispatcher handles osCommandResult result in // the main thread. // // There is two options to handle results: // OsCommandDispatcher::HANDLING_ORDER_ANY - // // Session ID for each thread, created by dispatcher, is // released before thread exit automatically via TL_RELEASE_SESSION_ID. // // Note: OsCommand and OsCommandResult objects are reusable, // so they shouldn't have specific parameters in // constructors if the same dispatcher object is // used many time. // #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/ptr_array.h" #include "base_cpp/os_sync_wrapper.h" #include "base_cpp/cyclic_array.h" namespace indigo { class OsCommandResult { public: virtual ~OsCommandResult () {}; virtual void clear () {}; }; class OsCommand { public: virtual ~OsCommand () {}; virtual void clear () {}; virtual void execute (OsCommandResult &result) = 0; int unique_id; }; class Exception; class OsCommandDispatcher { public: enum { HANDLING_ORDER_ANY, HANDLING_ORDER_SERIAL }; OsCommandDispatcher (int handling_order, bool same_session_IDs); virtual ~OsCommandDispatcher () {}; void run (); void run (int nthreads); void terminate (); void markToTerminate (); void _threadFunc (void); protected: void _run (int nthreads); // For overloading virtual OsCommand* _allocateCommand () = 0; virtual OsCommandResult* _allocateResult (); virtual bool _setupCommand (OsCommand &command) = 0; virtual void _handleResult (OsCommandResult &result) {} // Callback function to initialize thread-local variables // Custom Session ID can be set in this callback function. virtual void _prepareThread (void) {} // Callback function to cleanup thread-local variables virtual void _cleanupThread (void) {} private: // Methods void _startStandalone (); void _onMsgNeedTask (); void _onMsgHandleResult (); void _onMsgHandleException (Exception *exception); void _recvCommandAndResult (OsCommandResult * &result, OsCommand * &command); void _mainLoop (); OsCommand* _getVacantCommand (); OsCommandResult* _getVacantResult (); void _handleException (Exception *exception); void _handleResultWithCheck (OsCommandResult *result); void _wakeSuspended (); private: // Variables PtrArray _availableCommands; PtrArray _availableResults; CyclicArray _storedResults; Array _syspendedThreads; Exception *_exception_to_forward; OsMessageSystem _baseMessageSystem; OsMessageSystem _privateMessageSystem; int _last_command_index; int _expected_command_index; int _handling_order; int _left_thread_count; bool _need_to_terminate; qword _session_id; int _last_unique_command_id; bool _same_session_IDs; qword _parent_session_ID; }; } int osGetProcessorsCount (void); #endif // __cmd_thread_h__ Indigo-indigo-1.2.3/common/base_cpp/output.cpp000066400000000000000000000144511271037650300213160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include #include #include #include "base_c/defs.h" #include "base_cpp/output.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #ifndef va_copy #define va_copy(d, s) ((d) = (s)) #endif using namespace indigo; IMPL_ERROR(Output, "output"); Output::Output () { } Output::~Output () { } void Output::writeBinaryInt (int value) { //value = htonl(value); write(&value, sizeof(int)); } void Output::writeBinaryDword (dword value) { write(&value, sizeof(dword)); } void Output::writeBinaryFloat (float value) { write(&value, sizeof(float)); } void Output::writeByte (byte value) { write(&value, 1); } void Output::writeChar (char value) { write(&value, 1); } void Output::writeBinaryWord (word value) { //value = htons(value); write(&value, sizeof(word)); } void Output::printf (const char *format, ...) { va_list args; va_start(args, format); vprintf(format, args); va_end(args); } void Output::vprintf (const char *format, va_list args_orig) { QS_DEF(Array, str); if (str.size() < 2048) str.resize(2048); int n; while (true) { va_list args; // vsnprintf may change va_list argument that leads to segfault va_copy(args, args_orig); #if defined(_WIN32) && !defined(__MINGW32__) n = _vsnprintf_l(str.ptr(), str.size(), format, getCLocale(), args); #else n = vsnprintf(str.ptr(), str.size(), format, args); #endif /* If that worked, return the string. */ if (n > -1 && n < str.size()) break; /* Else try again with more space. */ int new_size; if (n > -1) /* glibc 2.1 */ new_size = n + 1; /* precisely what is needed */ else /* glibc 2.0 */ new_size = str.size() * 2; /* twice the old size */ str.resize(new_size); } write(str.ptr(), n); } void Output::writeArray (const Array &data) { write(data.ptr(), data.size()); } void Output::writeCR () { writeChar('\n'); } void Output::printfCR (const char *format, ...) { va_list args; va_start(args, format); vprintf(format, args); va_end(args); writeCR(); } void Output::writeString (const char *string) { int n = (int)strlen(string); write(string, n); } void Output::writeStringCR (const char *string) { writeString(string); writeCR(); } void Output::writePackedShort (short value) { byte low = value & 255; byte high = (value - low) >> 8; if (value < 128) writeByte(low); else { writeByte(high + 128); writeByte(low); } } void Output::writePackedUInt (unsigned int value) { if (value == 0) { writeByte(0); return; } while (value > 0) { if (value >= 128) writeByte((value & 0x7F) | 0x80); else writeByte(value); value >>= 7; } } void Output::skip (int count) { seek(count, SEEK_CUR); } FileOutput::FileOutput (Encoding filename_encoding, const char *filename) { _file = openFile(filename_encoding, filename, "wb"); if (_file == NULL) throw Error("can't open file %s. Error: %s", filename, strerror(errno)); } FileOutput::FileOutput (const char *filename) { _file = fopen(filename, "wb"); if (_file == NULL) throw Error("can't open file %s. Error: %s", filename, strerror(errno)); } FileOutput::FileOutput (bool append, const char *format, ...) { char filename[1024]; va_list args; va_start(args, format); vsnprintf(filename, sizeof(filename), format, args); va_end(args); if (append) _file = fopen(filename, "ab+"); else _file = fopen(filename, "wb"); if (_file == NULL) throw Error("can't open file %s. Error: %s", filename, strerror(errno)); } FileOutput::~FileOutput () { fclose(_file); } void FileOutput::write (const void *data, int size) { if (size < 1) return; size_t res = fwrite(data, size, 1, _file); if (res != 1) throw Error("file write error in write()"); } void FileOutput::flush () { fflush(_file); } void FileOutput::seek (int offset, int from) { fseek(_file, offset, from); } int FileOutput::tell () { return ftell(_file); } ArrayOutput::ArrayOutput (Array &arr) : _arr(arr) { _arr.clear(); } ArrayOutput::~ArrayOutput () { } void ArrayOutput::write (const void *data, int size) { int old_size = _arr.size(); _arr.resize(old_size + size); memcpy(_arr.ptr() + old_size, data, size); } int ArrayOutput::tell () { return _arr.size(); } void ArrayOutput::flush () { } void ArrayOutput::seek (int offset, int from) { throw Error("not implemented"); } void ArrayOutput::clear () { _arr.clear(); } StandardOutput::StandardOutput () { _count = 0; } StandardOutput::~StandardOutput () { } void StandardOutput::write (const void *data, int size) { if (size == 0) return; size_t res = fwrite(data, size, 1, stdout); if (res != 1) throw Error("error writing to standard output"); _count += size; } void StandardOutput::seek (int offset, int from) { throw Error("can not seek in standard output"); } int StandardOutput::tell () { return _count; } void StandardOutput::flush() { fflush(stdout); } NullOutput::NullOutput () { } NullOutput::~NullOutput () { } void NullOutput::write (const void *data, int size) { } void NullOutput::seek (int offset, int from) { throw Error("not implemented"); } int NullOutput::tell () { throw Error("not implemented"); } void NullOutput::flush () { } namespace indigo { void bprintf (Array& buf, const char *format, ...) { va_list args; va_start(args, format); ArrayOutput output(buf); output.vprintf(format, args); output.writeChar(0); va_end(args); } }Indigo-indigo-1.2.3/common/base_cpp/output.h000066400000000000000000000062031271037650300207570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __output_h__ #define __output_h__ #include #include "base_cpp/array.h" #include "base_cpp/io_base.h" namespace indigo { class DLLEXPORT Output { public: DECL_ERROR; explicit Output (); virtual ~Output (); virtual void write (const void *data, int size) = 0; virtual void seek (int offset, int from) = 0; virtual int tell () = 0; virtual void flush () = 0; virtual void writeByte (byte value); void writeChar (char value); void writeBinaryInt (int value); void writeBinaryDword (dword value); void writeBinaryWord (word value); void writeBinaryFloat (float value); void writePackedShort (short value); void writePackedUInt (unsigned int value); void writeString (const char *string); void writeStringCR (const char *string); void writeCR (); void writeArray (const Array &data); void skip (int count); void printf (const char *format, ...); void vprintf (const char *format, va_list args); void printfCR (const char *format, ...); }; class DLLEXPORT FileOutput : public Output { public: FileOutput (Encoding filename_encoding, const char *filename); explicit FileOutput (const char *name); //explicit FileOutput (const char *format, ...); explicit FileOutput (bool append, const char *format, ...); virtual ~FileOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); protected: FILE *_file; }; class DLLEXPORT ArrayOutput : public Output { public: explicit ArrayOutput (Array &arr); virtual ~ArrayOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); void clear (); protected: Array &_arr; }; class DLLEXPORT StandardOutput : public Output { public: explicit StandardOutput (); virtual ~StandardOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); protected: int _count; }; class DLLEXPORT NullOutput : public Output { public: explicit NullOutput (); virtual ~NullOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); }; DLLEXPORT void bprintf (Array& buf, const char *format, ...); } #endif Indigo-indigo-1.2.3/common/base_cpp/pool.h000066400000000000000000000062101271037650300203660ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __pool_h__ #define __pool_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" namespace indigo { DECL_EXCEPTION(PoolError); template class Pool { public: DECL_TPL_ERROR(PoolError); Pool () : _size(0), _first(-1) { } int add () { if (_first == -1) { _array.push(); _next.push(-2); _size++; return _array.size() - 1; } int idx = _first; _first = _next[_first]; if (_first == -2) throw Error("internal error: index %d is used in add()", idx); _next[idx] = -2; _size++; return idx; } int add (const T &item) { int idx = add(); _array[idx] = item; return idx; } void remove (int idx) { if (_next[idx] != -2) throw Error("trying to remove unused element #%d", idx); _next[idx] = _first; _first = idx; _size--; } bool hasElement(int idx) const { return (_next[idx] == -2); } int size () const { return _size; } int begin () const { int i; for (i = 0; i < _next.size(); i++) if (_next[i] == -2) break; return i; } int next (int i) const { for (i++; i < _next.size(); i++) if (_next[i] == -2) break; return i; } int end () const { return _array.size(); } void clear () { _array.clear(); _next.clear(); _size = 0; _first = -1; } const T & operator [] (int index) const { if (_next[index] != -2) throw Error("access to unused element %d", index); return _array[index]; } T & operator [] (int index) { if (_next[index] != -2) throw Error("access to unused element %d", index); return _array[index]; } const T & at (int index) const { return (*this)[index]; } T & at (int index) { return (*this)[index]; } protected: Array _array; // pool elements // _next[i] >= 0 => _array[i] is not used, // _next[i] contains the index of the next unused element // _next[i] == -1 => _array[i] is the last unused element // _next[i] == -2 => _array[i] is used Array _next; int _size; // number of _array items used int _first; // index of the first unused element (-1 if all are used) private: Pool (const Pool & ); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/profiling.cpp000066400000000000000000000177261271037650300217570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/profiling.h" #include #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/smart_output.h" using namespace indigo; // // _ProfilingTimer // _ProfilingTimer::_ProfilingTimer (int name_index) { _name_index = name_index; _start_time = nanoClock(); } _ProfilingTimer::~_ProfilingTimer () { stop(); } qword _ProfilingTimer::stop () { if (_name_index == -1) return 0; ProfilingSystem &inst = ProfilingSystem::getInstance(); _dt = nanoClock() - _start_time; inst.addTimer(_name_index, _dt); _name_index = -1; return _dt; } qword _ProfilingTimer::getTime () const { if (_name_index == -1) return _dt; return nanoClock() - _start_time; } float _ProfilingTimer::getTimeSec () const { return nanoHowManySeconds(getTime()); } // // Profiling functionality // IMPL_ERROR(ProfilingSystem, "Profiling system"); namespace indigo { DLLEXPORT OsLock _profiling_global_lock, _profiling_global_names_lock; } ObjArray< Array > ProfilingSystem::_names; TL_DECL(ProfilingSystem, _profiling_system); ProfilingSystem& ProfilingSystem::getInstance () { TL_GET(ProfilingSystem, _profiling_system); return _profiling_system; } int ProfilingSystem::getNameIndex (const char *name, bool add_if_not_exists) { OsLocker locker(_profiling_global_names_lock); for (int i = 0; i < _names.size(); i++) if (strcmp(_names[i].ptr(), name) == 0) return i; if (!add_if_not_exists) return -1; // Add new label Array &name_record = _names.push(); name_record.copy(name, strlen(name) + 1); return _names.size() - 1; } void ProfilingSystem::addTimer (int name_index, qword dt) { OsLocker locker(_lock); _ensureRecordExistanceLocked(name_index); Record &rec = _records[name_index]; rec.type = Record::TYPE_TIMER; rec.current.add(dt); rec.total.add(dt); } void ProfilingSystem::addCounter (int name_index, int value) { OsLocker locker(_lock); _ensureRecordExistanceLocked(name_index); Record &rec = _records[name_index]; rec.type = Record::TYPE_COUNTER; rec.current.add(value); rec.total.add(value); } void ProfilingSystem::reset (bool all) { OsLocker locker(_lock); for (int i = 0; i < _records.size(); i++) _records[i].reset(all); } int ProfilingSystem::_recordsCmp (int idx1, int idx2, void *context) { return strcmp(_names[idx1].ptr(), _names[idx2].ptr()); } void ProfilingSystem::getStatistics (Output &output, bool get_all) { OsLocker locker(_lock); OsLocker names_locker(_profiling_global_names_lock); // Print formatted statistics while (_sorted_records.size() < _records.size()) _sorted_records.push(_sorted_records.size()); _sorted_records.qsort(_recordsCmp, this); // Find maximum name length int max_len = 0; for (int i = 0; i < _records.size(); i++) { if (!_hasLabelIndex(i)) continue; if (_names[i].size() > max_len) max_len = _names[i].size(); } SmartTableOutput table_output(output, true); table_output.setLineFormat("|c|5c|5c|"); table_output.printHLine(); table_output.printf("Name\tStatistics\t\t\t\t\tSession statistics\t\t\t\t\n"); table_output.setLineFormat("|l|ccccc|ccccc|"); table_output.printf("\ttotal\tcount\tavg.\tst.dev.\tmax\ttotal\tcount\tavg.\tst.dev.\tmax\n"); table_output.printHLine(); table_output.setLineFormat("|l|rrrrr|rrrrr|"); for (int i = 0; i < _sorted_records.size(); i++) { int idx = _sorted_records[i]; if (!_hasLabelIndex(idx)) continue; Record &rec = _records[idx]; if (!get_all && rec.current.count == 0) continue; table_output.printf("%s\t", _names[idx].ptr()); if (rec.type == Record::TYPE_TIMER) { _printTimerData(rec.current, table_output); table_output.printf("\t"); _printTimerData(rec.total, table_output); table_output.printf("\n"); } else /* rec.type == Record::TYPE_COUNTER */ { _printCounterData(rec.current, table_output); table_output.printf("\t"); _printCounterData(rec.total, table_output); table_output.printf("\n"); } } table_output.printHLine(); table_output.flush(); } void ProfilingSystem::_printTimerData (const Record::Data &data, Output &output) { if (data.count == 0) { output.printf("-\t0\t\t\t"); return; } float total_sec = nanoHowManySeconds(data.value); float avg_ms = nanoHowManySeconds(data.value / data.count) * 1000; float max_ms = nanoHowManySeconds(data.max_value) * 1000; double avg_value = (double)data.value / data.count; double sigma_sq = data.square_sum / data.count - avg_value * avg_value; float sigma_ms = nanoHowManySeconds((qword)sqrt(sigma_sq)) * 1000; output.printf("%0.2fs\t%0.0lf\t%0.1fms\t%0.1lfms\t%0.1fms", total_sec, (double)data.count, avg_ms, sigma_ms, max_ms); } void ProfilingSystem::_printCounterData (const Record::Data &data, Output &output) { if (data.count == 0) { output.printf("-\t0\t\t\t"); return; } float avg_value = (float)data.value / data.count; // To avoid platform-specific code qwords were casted to doubles double sigma_sq = data.square_sum / data.count - avg_value * avg_value; output.printf("%0.0lf\t%0.0lf\t%0.1f\t%0.1lf\t%0.0lf", (double)data.value, (double)data.count, avg_value, sqrt(sigma_sq), (double)data.max_value); } bool ProfilingSystem::_hasLabelIndex (int name_index) { if (name_index >= _records.size()) return false; return _records[name_index].total.count > 0; } bool ProfilingSystem::hasLabel (const char *name) { int name_index = getNameIndex(name, false); if (name_index == -1) return false; return _hasLabelIndex(name_index); } void ProfilingSystem::_ensureRecordExistanceLocked (int name_index) { while (_records.size() <= name_index) _records.push(); } float ProfilingSystem::getLabelExecTime (const char *name, bool total) { int idx = getNameIndex(name); OsLocker locker(_lock); _ensureRecordExistanceLocked(idx); if (total) return nanoHowManySeconds(_records[idx].total.value); else return nanoHowManySeconds(_records[idx].current.value); } qword ProfilingSystem::getLabelValue (const char *name, bool total) { int idx = getNameIndex(name); OsLocker locker(_lock); _ensureRecordExistanceLocked(idx); if (total) return _records[idx].total.value; else return _records[idx].current.value; } qword ProfilingSystem::getLabelCallCount (const char *name, bool total) { int idx = getNameIndex(name); OsLocker locker(_lock); _ensureRecordExistanceLocked(idx); if (total) return _records[idx].total.count; else return _records[idx].current.count; } // // ProfilingSystem::Record // void ProfilingSystem::Record::reset (bool all) { current.reset(); if (all) total.reset(); } // // ProfilingSystem::Record::Data // ProfilingSystem::Record::Data::Data () { reset(); } void ProfilingSystem::Record::Data::reset () { count = value = max_value = 0; square_sum = 0; } void ProfilingSystem::Record::Data::add (qword adding_value) { count++; value += adding_value; max_value = __max(max_value, adding_value); double adding_value_dbl = (double)adding_value; square_sum += adding_value_dbl * adding_value_dbl; } Indigo-indigo-1.2.3/common/base_cpp/profiling.h000066400000000000000000000106421271037650300214120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __profiling_h__ #define __profiling_h__ #include "base_c/nano.h" #include "base_cpp/os_sync_wrapper.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #define _PROF_GET_NAME_INDEX(var_name, name) \ static int var_name##_name_index; \ if (var_name##_name_index == 0) \ { \ indigo::OsLocker locker(indigo::_profiling_global_lock); \ if (var_name##_name_index == 0) { \ indigo::ProfilingSystem &inst = indigo::ProfilingSystem::getInstance(); \ var_name##_name_index = inst.getNameIndex(name); \ } \ } #define profTimerStart(var_name, name) \ _PROF_GET_NAME_INDEX(var_name, name) \ indigo::_ProfilingTimer var_name##_timer(var_name##_name_index) #define profTimerStop(var_name) \ var_name##_timer.stop() #define profTimerGetTime(var_name) \ var_name##_timer.getTime() #define profTimerGetTimeSec(var_name) \ var_name##_timer.getTimeSec() #define profIncTimer(name, dt) \ do { \ _PROF_GET_NAME_INDEX(var_name, name) \ indigo::ProfilingSystem &inst = indigo::ProfilingSystem::getInstance(); \ inst.addTimer(var_name##_name_index, dt); \ } while (false) #define profIncCounter(name, count) \ do { \ _PROF_GET_NAME_INDEX(var_name, name) \ indigo::ProfilingSystem &inst = indigo::ProfilingSystem::getInstance(); \ inst.addCounter(var_name##_name_index, count); \ } while (false) #define profTimersReset() indigo::ProfilingSystem::getInstance().reset(false) #define profTimersResetSession() indigo::ProfilingSystem::getInstance().reset(true) #define profGetStatistics(output, all) indigo::ProfilingSystem::getInstance().getStatistics(output, all) namespace indigo { class Output; class DLLEXPORT ProfilingSystem { public: static ProfilingSystem& getInstance (); static int getNameIndex (const char *name, bool add_if_not_exists = true); void addTimer (int name_index, qword dt); void addCounter (int name_index, int value); void reset (bool all); void getStatistics (Output &output, bool get_all); bool hasLabel (const char *name); float getLabelExecTime (const char *name, bool total = false); qword getLabelValue (const char *name, bool total = false); qword getLabelCallCount (const char *name, bool total = false); DECL_ERROR; private: struct Record { enum { TYPE_TIMER, TYPE_COUNTER }; struct Data { qword count, value, max_value; double square_sum; Data(); void reset (); void add (qword value); }; Data current, total; int type; void reset (bool all); }; static int _recordsCmp (int idx1, int idx2, void *context); void _printTimerData (const Record::Data &data, Output &output); void _printCounterData (const Record::Data &data, Output &output); bool _hasLabelIndex (int name_index); void _ensureRecordExistanceLocked (int name_index); ObjArray _records; Array _sorted_records; OsLock _lock; static ObjArray< Array > _names; }; // This class shouldn't be used explicitly class DLLEXPORT _ProfilingTimer { public: _ProfilingTimer (int name_index); ~_ProfilingTimer (); qword stop (); qword getTime () const; float getTimeSec () const; private: int _name_index; qword _start_time, _dt; }; extern DLLEXPORT OsLock _profiling_global_lock; } #ifdef _WIN32 #pragma warning(pop) #endif #endif // __profiling_h__ Indigo-indigo-1.2.3/common/base_cpp/properties_map.cpp000066400000000000000000000063011271037650300230020ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/properties_map.h" using namespace indigo; IMPL_ERROR(PropertiesMap, "properties map"); void PropertiesMap::copy(RedBlackStringObjMap< Array > &other) { clear(); for (int i = other.begin(); i != other.end(); i = other.next(i)) { insert(other.key(i), other.value(i).ptr()); } } void PropertiesMap::copy(PropertiesMap& other) { clear(); for(auto p : other.elements()) { insert(other.key(p), other.value(p)); } } void PropertiesMap::insert(const char* key, const char* value) { int res; if (_properties.find(key)) { auto& val = _properties.at(key); if (value != 0) val.readString(value, true); } else { auto& name = _propertyNames.push(); name.readString(key, true); int k = _properties.insert(key); if (value != 0) _properties.value(k).readString(value, true); } } Array& PropertiesMap::insert(const char* key){ insert (key, 0); return valueBuf(key); } const char* PropertiesMap::key(int i) { return _propertyNames.at(i).ptr(); } const char* PropertiesMap::value(int i) { auto& buf = valueBuf(_propertyNames.at(i).ptr()); if(buf.size() > 0) { return buf.ptr(); } else { return ""; } } Array& PropertiesMap::valueBuf(const char* key) { return _properties.at(key); } void PropertiesMap::clear() { _properties.clear(); _propertyNames.clear(); } bool PropertiesMap::contains(const char* key) { return _properties.find(key); } const char* PropertiesMap::at(const char* key) { return _properties.at(key).ptr(); } void PropertiesMap::remove(const char* key) { if(_properties.find(key)) { _properties.remove(key); int to_remove = -1; for (auto i =0; i < _propertyNames.size(); i++) { if(strcmp(_propertyNames.at(i).ptr(), key) == 0) { to_remove = i; break; } } if(to_remove >= 0) { _propertyNames.remove(to_remove); } else { throw Error("internal error with properties"); } } } PropertiesMap::PrAuto PropertiesMap::elements() { return PrAuto(*this); } PropertiesMap::PrIter::PrIter(PropertiesMap &owner, int idx): _owner(owner), AutoIterator(idx) { } PropertiesMap::PrIter & PropertiesMap::PrIter::operator++(){ _idx += 1; return *this; } PropertiesMap::PrIter PropertiesMap::PrAuto::begin(){ return PropertiesMap::PrIter(_owner, 0); } int PropertiesMap::PrAuto::next(int k) { return k+1; } PropertiesMap::PrIter PropertiesMap::PrAuto::end(){ return PropertiesMap::PrIter(_owner, _owner._propertyNames.size()); }Indigo-indigo-1.2.3/common/base_cpp/properties_map.h000066400000000000000000000040421271037650300224470ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __properties_map_h__ #define __properties_map_h__ #include "base_cpp/auto_iter.h" #include "base_cpp/red_black.h" #include "base_cpp/exception.h" #include "base_cpp/obj_array.h" #include "base_cpp/array.h" namespace indigo { class DLLEXPORT PropertiesMap { public: DECL_ERROR; explicit PropertiesMap(){} ~PropertiesMap(){} // inline RedBlackStringObjMap< Array >& getProperties() { // return _properties; // } void copy(RedBlackStringObjMap< Array > &properties); void copy(PropertiesMap&); void insert(const char* key, const char* value); Array& insert(const char* key); const char* key(int); const char* value(int); Array& valueBuf(const char* key); bool contains(const char* key); const char* at(const char* key); void remove(const char* key); void clear(); class PrIter : public AutoIterator { public: PrIter(PropertiesMap &owner, int idx); PrIter & operator++(); private: PropertiesMap &_owner; }; class PrAuto { public: PrAuto(PropertiesMap &owner) : _owner(owner) { } PrIter begin(); int next(int); PrIter end(); private: PropertiesMap &_owner; }; PrAuto elements(); private: PropertiesMap(const PropertiesMap&); RedBlackStringObjMap< Array > _properties; ObjArray< Array > _propertyNames; }; } #endif // __auto_iter_h__ Indigo-indigo-1.2.3/common/base_cpp/ptr_array.h000066400000000000000000000061721271037650300214270ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ptr_array__ #define __ptr_array__ #include "base_cpp/array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { DECL_EXCEPTION(PtrArrayError); template class PtrArray { public: explicit PtrArray () { } virtual ~PtrArray () { clear(); } DECL_TPL_ERROR(PtrArrayError); T & add (T *obj) { _ptrarray.push(obj); return *obj; } T * pop (void) { return _ptrarray.pop(); } T * top (void) { return at(size() - 1); } void expand (int newsize) { while (_ptrarray.size() < newsize) _ptrarray.push(0); } void clear (void) { int i; for (i = 0; i < _ptrarray.size(); i++) { if (_ptrarray[i] == 0) continue; delete _ptrarray[i]; _ptrarray[i] = 0; } _ptrarray.clear(); } int size () const { return _ptrarray.size(); } void resize (const int newsize) { int i, oldsize = _ptrarray.size(); for (int i = newsize; i < oldsize; i++) { if (_ptrarray[i] == 0) continue; delete _ptrarray[i]; _ptrarray[i] = 0; } _ptrarray.resize(newsize); for (i = oldsize; i < newsize; i++) _ptrarray[i] = 0; } void removeLast () { delete _ptrarray.pop(); } void remove (int idx) { delete _ptrarray[idx]; _ptrarray.remove(idx); } void set (int idx, T *obj) { if (_ptrarray[idx] != 0) throw Error("object #%d already set", idx); _ptrarray[idx] = obj; } void reset (int idx) { delete _ptrarray[idx]; _ptrarray[idx] = 0; } T * release (int idx) { T *result = _ptrarray[idx]; _ptrarray[idx] = 0; return result; } void qsort (int (*cmp)(T * const &, T * const &, const void *), const void *context) { _ptrarray.qsort(cmp, context); } const T * operator[] (int index) const { return _ptrarray[index]; } T *& operator[] (int index) { return _ptrarray[index]; } const T * at (int index) const { return _ptrarray[index]; } T *& at (int index) { return _ptrarray[index]; } const T * const* ptr () const { return _ptrarray.ptr(); } T ** ptr () { return _ptrarray.ptr(); } protected: Array _ptrarray; private: PtrArray (const PtrArray &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/common/base_cpp/ptr_pool.h000066400000000000000000000042451271037650300212610ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ptr_pool__ #define __ptr_pool__ #include "base_cpp/pool.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { DECL_EXCEPTION(PtrPoolError); template class PtrPool { public: explicit PtrPool () { } virtual ~PtrPool () { clear(); } DECL_TPL_ERROR(PtrPoolError); int add (T *obj) { return _ptrpool.add(obj); } void remove (int idx) { delete _ptrpool[idx]; _ptrpool.remove(idx); } bool hasElement(int idx) const { return _ptrpool.hasElement(idx); } int size () const { return _ptrpool.size(); } int begin () const { return _ptrpool.begin(); } int end () const { return _ptrpool.end(); } int next (int i) const { return _ptrpool.next(i); } void clear (void) { int i; for (i = _ptrpool.begin(); i != _ptrpool.end(); i = _ptrpool.next(i)) delete _ptrpool[i]; _ptrpool.clear(); } const T * operator[] (int index) const { return _ptrpool[index]; } T *& operator[] (int index) { return _ptrpool[index]; } const T * at (int index) const { return _ptrpool[index]; } T *& at (int index) { return _ptrpool[index]; } const T& ref (int index) const { return *_ptrpool[index]; } T& ref (int index) { return *_ptrpool[index]; } protected: Pool _ptrpool; private: PtrPool (const PtrPool &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/common/base_cpp/queue.h000066400000000000000000000033431271037650300205450ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __queue_h__ #define __queue_h__ #include "base_c/defs.h" #include "base_cpp/exception.h" #include "base_cpp/array.h" namespace indigo { DECL_EXCEPTION(QueueError); // Queue with fixed max length template class Queue { public: DECL_TPL_ERROR(QueueError); explicit Queue (void) { _start = 0; _end = 0; } void setLength (int max_size) { _array.resize(max_size); } void clear (void) { _start = 0; _end = 0; } bool isEmpty (void) { return _start == _end; } T & push (const T& elem) { int idx = (_end + 1) % _array.size(); if (idx == _start) throw Error("queue is full"); int end = _end; _array[_end] = elem; _end = idx; return _array[end]; } T& pop (void) { if (isEmpty()) throw Error("queue is empty"); int idx = _start; _start = (_start + 1) % _array.size(); return _array[idx]; } protected: Array _array; int _start, _end; private: Queue (const Queue &); // no implicit copy }; } #endif // __queue_h__ Indigo-indigo-1.2.3/common/base_cpp/red_black.h000066400000000000000000000653311271037650300213340ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __red_black_h__ #define __red_black_h__ #include #include "base_cpp/obj_pool.h" #include "base_cpp/string_pool.h" namespace indigo { struct RedBlackNodeBase { int left; int right; int parent; int color; }; DECL_EXCEPTION(RedBlackTreeError); template class RedBlackTree { public: DECL_TPL_ERROR(RedBlackTreeError); enum { RED = 0, BLACK = 1 }; RedBlackTree () { _size = 0; _root = -1; _nodes = new Pool(); _own_nodes = true; } explicit RedBlackTree (Pool &nodes) { _size = 0; _root = -1; _nodes = &nodes; _own_nodes = false; } virtual ~RedBlackTree () { clear(); if (_own_nodes) delete _nodes; } virtual void clear () { if (_own_nodes) _nodes->clear(); else if (_size > 0) { int i = beginPost(); while (1) { int inext = nextPost(i); // end() can change after _nodes->remove() if (inext == end()) { _nodes->remove(i); break; } _nodes->remove(i); i = inext; } } _root = -1; _size = 0; } int size () const { return _size; } int begin () const { // inorder traversal: find the first element int node = _root; int parent = end(); while (node != -1) { parent = node; node = _nodes->at(node).left; } return parent; } int beginPost () const { // postorder traversal: find the first element int node = _root; int parent = end(); while (node != -1) { parent = node; if (_nodes->at(node).left != -1) node = _nodes->at(node).left; else node = _nodes->at(node).right; } return parent; } int next (int node) const { // inorder traversal: find the next element if (_nodes->at(node).right >= 0) { // go right and then left while possible node = _nodes->at(node).right; while (_nodes->at(node).left >= 0) node = _nodes->at(node).left; return node; } while (1) { int parent = _nodes->at(node).parent; if (parent == -1) break; // go up while going from the right if (_nodes->at(parent).left == node) return parent; node = parent; } return end(); } int nextPost (int node) const { // postorder traversal: find the next element int parent = _nodes->at(node).parent; if (parent == -1) return end(); int parent_right = _nodes->at(parent).right; if (parent_right == node || parent_right == -1) return parent; node = parent_right; parent = node; while (node != -1) { parent = node; if (_nodes->at(node).left != -1) node = _nodes->at(node).left; else node = _nodes->at(node).right; } return parent; } int end () const { return _nodes->end(); } bool find (Key key) const { int sign, idx = this->_findClosest(key, sign); return idx != -1 && sign == 0; } protected: virtual int _compare (Key key, const Node &node) const = 0; int _findClosest (Key key, int &sign) const { int idx = _root; int parent = -1; sign = 0; while (idx != -1) { const Node &node = _nodes->at(idx); sign = _compare(key, node); if (sign == 0) return idx; parent = idx; if (sign > 0) idx = node.right; else // sign < 0 idx = node.left; } return parent; } void _insertNode (int node_idx, int parent_idx, int sign) { Node &node = _nodes->at(node_idx); node.left = -1; node.right = -1; node.color = RED; node.parent = parent_idx; if (parent_idx == -1) _root = node_idx; else if (sign < 0) _nodes->at(parent_idx).left = node_idx; else // sign > 0 _nodes->at(parent_idx).right = node_idx; while (node_idx != _root) { parent_idx = _nodes->at(node_idx).parent; Node &parent = _nodes->at(parent_idx); if (parent.color == BLACK) break; int grandparent_idx = parent.parent; Node &grandparent = _nodes->at(grandparent_idx); if (parent_idx == grandparent.left) { int uncle_color = BLACK; if (grandparent.right >= 0) uncle_color = _nodes->at(grandparent.right).color; if (uncle_color == RED) { parent.color = BLACK; _nodes->at(grandparent.right).color = BLACK; grandparent.color = RED; node_idx = grandparent_idx; } else // uncle_color == BLACK { if (node_idx == parent.right) { node_idx = parent_idx; _rotateLeft(node_idx); } parent_idx = _nodes->at(node_idx).parent; grandparent_idx = _nodes->at(parent_idx).parent; _nodes->at(parent_idx).color = BLACK; _nodes->at(grandparent_idx).color = RED; _rotateRight(grandparent_idx); } } else // parent_idx == grandparent.right { int uncle_color = BLACK; if (grandparent.left >= 0) uncle_color = _nodes->at(grandparent.left).color; if (uncle_color == RED) { parent.color = BLACK; _nodes->at(grandparent.left).color = BLACK; grandparent.color = RED; node_idx = grandparent_idx; } else // uncle_color == BLACK { if (node_idx == parent.left) { node_idx = parent_idx; _rotateRight(node_idx); } parent_idx = _nodes->at(node_idx).parent; grandparent_idx = _nodes->at(parent_idx).parent; _nodes->at(parent_idx).color = BLACK; _nodes->at(grandparent_idx).color = RED; _rotateLeft(grandparent_idx); } } } _nodes->at(_root).color = BLACK; _size++; } void _rotateLeft (int node_idx) { Node &node = _nodes->at(node_idx); int right_idx = node.right; Node &right = _nodes->at(right_idx); node.right = right.left; if (right.left != -1) _nodes->at(right.left).parent = node_idx; right.parent = node.parent; if (node.parent == -1) _root = right_idx; else { Node &parent = _nodes->at(node.parent); if (node_idx == parent.left) parent.left = right_idx; else // node_idx == parent.right parent.right = right_idx; } right.left = node_idx; node.parent = right_idx; } void _rotateRight (int node_idx) { Node &node = _nodes->at(node_idx); int left_idx = node.left; Node &left = _nodes->at(left_idx); node.left = left.right; if (left.right != -1) _nodes->at(left.right).parent = node_idx; left.parent = node.parent; if (node.parent == -1) _root = left_idx; else { Node &parent = _nodes->at(node.parent); if (node_idx == parent.left) parent.left = left_idx; else // node_idx == parent.right parent.right = left_idx; } left.right = node_idx; node.parent = left_idx; } void _removeNode (int z) { int x, y; if (_nodes->at(z).left == -1) { y = z; x = _nodes->at(y).right; } else if (_nodes->at(z).right == -1) { y = z; x = _nodes->at(z).left; } else { y = _nodes->at(z).right; while (_nodes->at(y).left != -1) y = _nodes->at(y).left; x = _nodes->at(y).right; } // replace y with x int yparent = _nodes->at(y).parent; if (x != -1) _nodes->at(x).parent = yparent; if (yparent != -1) { if (y == _nodes->at(yparent).left) _nodes->at(yparent).left = x; else _nodes->at(yparent).right = x; } else _root = x; int ycolor = _nodes->at(y).color; if (y != z) { // replace z with y if (z == yparent) yparent = y; int zparent = _nodes->at(z).parent; _nodes->at(y).parent = zparent; if (zparent != -1) { if (_nodes->at(zparent).left == z) _nodes->at(zparent).left = y; else _nodes->at(zparent).right = y; } else _root = y; _nodes->at(y).left = _nodes->at(z).left; _nodes->at(y).right = _nodes->at(z).right; if (_nodes->at(z).left != -1) _nodes->at(_nodes->at(z).left).parent = y; if (_nodes->at(z).right != -1) _nodes->at(_nodes->at(z).right).parent = y; _nodes->at(y).color = _nodes->at(z).color; } if (ycolor == BLACK) _removeFixup(x, yparent); _nodes->remove(z); _size--; } void _removeFixup (int x, int xparent) { while (x != _root && (x == -1 || _nodes->at(x).color == BLACK)) { if (_nodes->at(xparent).left == x) { int w = _nodes->at(xparent).right; if (_nodes->at(w).color == RED) { _nodes->at(w).color = BLACK; _nodes->at(xparent).color = RED; _rotateLeft(xparent); w = _nodes->at(xparent).right; } Node &wnode = _nodes->at(w); if ((wnode.left == -1 || _nodes->at(wnode.left).color == BLACK) && (wnode.right == -1 || _nodes->at(wnode.right).color == BLACK)) { wnode.color = RED; x = xparent; xparent = _nodes->at(x).parent; } else { if (wnode.right == -1 || _nodes->at(wnode.right).color == BLACK) { if (wnode.left != -1) _nodes->at(wnode.left).color = BLACK; wnode.color = RED; _rotateRight(w); w = _nodes->at(xparent).right; } _nodes->at(w).color = _nodes->at(xparent).color; _nodes->at(xparent).color = BLACK; if (_nodes->at(w).right != -1) _nodes->at(_nodes->at(w).right).color = BLACK; _rotateLeft(xparent); x = _root; } } else { int w = _nodes->at(xparent).left; if (_nodes->at(w).color == RED) { _nodes->at(w).color = BLACK; _nodes->at(xparent).color = RED; _rotateRight(xparent); w = _nodes->at(xparent).left; } Node &wnode = _nodes->at(w); if ((wnode.left == -1 || _nodes->at(wnode.left).color == BLACK) && (wnode.right == -1 || _nodes->at(wnode.right).color == BLACK)) { wnode.color = RED; x = xparent; xparent = _nodes->at(x).parent; } else { if (wnode.left == -1 || _nodes->at(wnode.left).color == BLACK) { _nodes->at(wnode.right).color = BLACK; wnode.color = RED; _rotateLeft(w); w = _nodes->at(xparent).left; } _nodes->at(w).color = _nodes->at(xparent).color; _nodes->at(xparent).color = BLACK; if (_nodes->at(w).left != -1) _nodes->at(_nodes->at(w).left).color = BLACK; _rotateRight(xparent); x = _root; } } } if (x != -1) _nodes->at(x).color = BLACK; } Pool *_nodes; int _root; bool _own_nodes; int _size; private: RedBlackTree (const RedBlackTree &); // no implicit copy }; template struct RedBlackSetNode : public RedBlackNodeBase { Key key; }; template class RedBlackSet : public RedBlackTree< Key, RedBlackSetNode > { typedef RedBlackTree< Key, RedBlackSetNode > Parent; public: typedef RedBlackSetNode Node; RedBlackSet () { } RedBlackSet (Pool &pool) : Parent(pool) { } virtual ~RedBlackSet () { } bool find (Key key) const { int sign, idx = this->_findClosest(key, sign); return idx != -1 && sign == 0; } int insert (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key already present"); return _insert(key, idx, sign); } bool find_or_insert (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return true; _insert(key, idx, sign); return false; } Key & key (int node) const { return this->_nodes->at(node).key; } void remove (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) this->_removeNode(idx); else throw typename Parent::Error("remove(): key not found"); } void remove_if_exists (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) this->_removeNode(idx); } protected: virtual int _compare (Key key, const Node &node) const { return key > node.key ? 1 : (key < node.key ? -1 : 0); } int _insert (Key key, int parent, int sign) { int node_idx = this->_nodes->add(); Node &node = this->_nodes->at(node_idx); node.key = key; this->_insertNode(node_idx, parent, sign); return node_idx; } private: RedBlackSet (const RedBlackSet &); // no implicit copy }; template struct RedBlackMapNode : public RedBlackNodeBase { Key key; Value value; }; template class RedBlackMap : public RedBlackTree > { typedef RedBlackTree > Parent; public: RedBlackMap () { } virtual ~RedBlackMap () { } typedef RedBlackMapNode Node; Value & at (Key key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return this->_nodes->at(idx).value; throw typename Parent::Error("at(): key not found"); } Value * at2 (Key key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return &this->_nodes->at(idx).value; return 0; } void insert (Key key, Value value) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key already present"); _insert(key, value, idx, sign); } void remove (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) this->_removeNode(idx); else throw typename Parent::Error("remove(): key not found"); } void copy (const RedBlackMap &other) { int i; this->clear(); for (i = other.begin(); i != other.end(); i = other.next(i)) this->insert(other.key(i), other.value(i)); } Key & key (int node) const { return this->_nodes->at(node).key; } Value & value (int node) const { return this->_nodes->at(node).value; } protected: virtual int _compare (Key key, const Node &node) const { if (key < node.key) return -1; if (node.key < key) return 1; return 0; } void _insert (Key key, Value value, int parent, int sign) { int node_idx = this->_nodes->add(); Node &node = this->_nodes->at(node_idx); node.key = key; node.value = value; this->_insertNode(node_idx, parent, sign); } private: RedBlackMap(const RedBlackMap &); // no implicit copy }; template struct RedBlackStringMapNode : public RedBlackNodeBase { int key_idx; Value value; }; template class RedBlackStringMap : public RedBlackTree > { typedef RedBlackTree > Parent; public: typedef RedBlackStringMapNode Node; virtual void clear () { RedBlackTree::clear(); _pool.clear(); } void insert (const char *key, Value value) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key %s already present", key); _insert(key, value, idx, sign); } Value & at (const char *key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return this->_nodes->at(idx).value; throw typename Parent::Error("at(): key %s not found", key); } Value & at (const char *key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return this->_nodes->at(idx).value; throw typename Parent::Error("at(): key %s not found", key); } Value * at2 (const char *key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return &this->_nodes->at(idx).value; return 0; } const char * key (int node) const { return _pool.at(this->_nodes->at(node).key_idx); } Value & value (int node) const { return this->_nodes->at(node).value; } protected: virtual int _compare (const char *key, const RedBlackStringMapNode &node) const { return case_sensitive ? strcmp(key, _pool.at(node.key_idx)) : strcasecmp(key, _pool.at(node.key_idx)); } void _insert (const char *key, Value value, int parent, int sign) { int string_idx = _pool.add(key); int node_idx = this->_nodes->add(); Node &node = this->_nodes->at(node_idx); node.key_idx = string_idx; node.value = value; this->_insertNode(node_idx, parent, sign); } StringPool _pool; }; template class RedBlackObjMap : public RedBlackTree > { typedef RedBlackTree > Parent; public: int dummy; // for VS 2008 to generate correct x64 code RedBlackObjMap () { dummy = 123; } virtual ~RedBlackObjMap () { this->clear(); } typedef RedBlackMapNode Node; Value & at (Key key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return this->_nodes->at(idx).value; throw typename Parent::Error("at(): key not found"); } Value * at2 (Key key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return &this->_nodes->at(idx).value; return 0; } Value& insert (Key key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key already present"); return _insertObj(key, idx, sign); } template Value& insert (Key key, A& a) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key already present"); return _insertObj(key, idx, sign, a); } Value& findOrInsert (Key key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) { return this->_nodes->at(idx).value; } return _insertObj(key, idx, sign); } template Value& findOrInsert (Key key, A& a) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) { return this->_nodes->at(idx).value; } return _insertObj(key, idx, sign, a); } void remove (Key key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) { this->value(idx).~Value(); this->_removeNode(idx); } else throw typename Parent::Error("remove(): key not found"); } Key & key (int node) const { return this->_nodes->at(node).key; } Value & value (int node) const { return this->_nodes->at(node).value; } virtual void clear () { int i; for (i = this->begin(); i != this->end(); i = this->next(i)) this->value(i).~Value(); Parent::clear(); } protected: virtual int _compare (Key key, const Node &node) const { if (key < node.key) return -1; if (node.key < key) return 1; return 0; } Value* _insert (Key key, int parent, int sign) { int node_idx = this->_nodes->add(); Node &node = this->_nodes->at(node_idx); node.key = key; this->_insertNode(node_idx, parent, sign); return &node.value; } Value& _insertObj (Key key, int parent, int sign) { Value* value = _insert(key, parent, sign); new (value) Value(); return *value; } template Value& _insertObj (Key key, int parent, int sign, A& a) { Value* value = _insert(key, parent, sign); new (value) Value(a); return *value; } private: RedBlackObjMap(const RedBlackObjMap &); // no implicit copy }; template struct RedBlackStringObjMapNode : public RedBlackNodeBase { int key_idx; Value value; }; template class RedBlackStringObjMap : public RedBlackTree > { typedef RedBlackStringObjMapNode Node; typedef RedBlackTree Parent; typedef RedBlackTree GrandParent; public: RedBlackStringObjMap () { } virtual ~RedBlackStringObjMap () { this->clear(); } virtual void clear () { for (int i = this->begin(); i != this->end(); i = this->next(i)) this->value(i).~Value(); GrandParent::clear(); _pool.clear(); } Value & at (const char* key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return this->_nodes->at(idx).value; throw typename Parent::Error("at(): key %s not found", key); } Value & operator [] (const char* key) const { return at(key); } Value * at2 (const char* key) const { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return &this->_nodes->at(idx).value; return 0; } int insert (const char* key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key %s already present", key); return _insertObj(key, idx, sign); } template int insert (const char* key, A& a) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) throw typename Parent::Error("insert(): key %s already present", key); return _insertObj(key, idx, sign, a); } int findOrInsert (const char* key) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return idx; return _insertObj(key, idx, sign); } template int findOrInsert (const char* key, A& a) { int sign; int idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) return idx; return _insertObj(key, idx, sign, a); } void remove (const int idx) { _pool.remove(this->_nodes->at(idx).key_idx); this->value(idx).~Value(); this->_removeNode(idx); } void remove (const char* key) { int sign, idx = this->_findClosest(key, sign); if (idx != -1 && sign == 0) { remove(idx); } else throw typename Parent::Error("remove(): key %s not found", key); } const char * key (int node) const { return _pool.at(this->_nodes->at(node).key_idx); } Value & value (int node) const { return this->_nodes->at(node).value; } void copy (const RedBlackStringObjMap &other) { clear(); for (int i = other.begin(); i != other.end(); i = other.next(i)) { const char *key = other.key(i); int id = insert(key); // Use `copy` method if `Value` type value(id).copy(other.value(i)); } } protected: virtual int _compare (const char* key, const Node &node) const { return strcmp(key, _pool.at(node.key_idx)); } int _insert (const char *key, int parent, int sign) { int string_idx = _pool.add(key); int node_idx = this->_nodes->add(); Node &node = this->_nodes->at(node_idx); node.key_idx = string_idx; this->_insertNode(node_idx, parent, sign); return node_idx; } int _insertObj (const char *key, int parent, int sign) { int idx = _insert(key, parent, sign); Value *value = &this->value(idx); new (value) Value(); return idx; } template int _insertObj (const char *key, int parent, int sign, A &a) { int idx = _insert(key, parent, sign); Value *value = &this->value(idx); new (value) Value(a); return idx; } StringPool _pool; private: RedBlackStringObjMap(const RedBlackStringObjMap &); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/reusable_obj_array.h000066400000000000000000000041571271037650300232570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reusable_obj_array__ #define __reusable_obj_array__ #include "base_cpp/array.h" namespace indigo { template class ReusableObjArray { public: explicit ReusableObjArray () { _count = 0; } ~ReusableObjArray () { for (int i = 0; i < _array.size(); i++) { _array[i].~T(); } } const T & operator[] (int index) const { return _array[index]; } T & operator[] (int index) { return _array[index]; } const T & at (int index) const { return (*this)[index]; } T & at (int index) { return (*this)[index]; } int size (void) const { return _count; } void resize (const int newSize) { if (newSize <= _count) { _count = newSize; } else { _array.reserve(newSize); while (_count < newSize) { push(); } } } T & push () { T *addr; if (_count == _array.size()) { addr = &_array.push(); new (addr) T(); } else { addr = &_array[_count]; } _count++; addr->clear(); return *addr; } void pop () { _count--; } T& top () { return _array[_count - 1]; } void clear () { _count = 0; } void reserve (int to_reserve) { _array.reserve(to_reserve); } protected: Array _array; int _count; private: ReusableObjArray (const ReusableObjArray &); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/scanner.cpp000066400000000000000000000375731271037650300214210ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include #include #include #include "base_c/defs.h" #include "base_cpp/scanner.h" #include "base_cpp/tlscont.h" #include "reusable_obj_array.h" using namespace indigo; enum { MAX_LINE_LENGTH = 1048576 }; IMPL_ERROR(Scanner, "scanner"); Scanner::~Scanner () { } int Scanner::readIntFix (int digits) { int result; char buf[20]; if (digits >= NELEM(buf) - 1) throw Error("readIntFix(): digits = %d", digits); read(digits, buf); buf[digits] = 0; char *end; result = strtol(buf, &end, 10); // Check that some digits were read if (buf == end) throw Error("readIntFix(%d): invalid number representation: \"%s\"", digits, buf); // Check that the unread part contains only spaces while (end != buf + digits) { if (!isspace(*end)) throw Error("readIntFix(%d): invalid number representation: \"%s\"", digits, buf); end++; } return result; } int Scanner::readInt1 (void) { QS_DEF(Array, buf); char c; int result; buf.clear(); skipSpace(); while (!isEOF()) { c = readChar(); if (!isdigit(c) && c != '-' && c != '+') break; buf.push(c); if (buf.size() > MAX_LINE_LENGTH) throw Error("Line length is too long. Probably the file format is not correct."); } buf.push(0); if (sscanf(buf.ptr(), "%d", &result) < 1) throw Error("readInt(): error parsing %s", buf.ptr()); return result; } int Scanner::readInt (void) { QS_DEF(Array, buf); char c; int result; buf.clear(); c = readChar(); if (c == '+' || c == '-' || isdigit(c)) buf.push(c); while (isdigit(lookNext())) { buf.push(readChar()); if (buf.size() > MAX_LINE_LENGTH) throw Error("Line length is too long. Probably the file format is not correct."); } buf.push(0); if (sscanf(buf.ptr(), "%d", &result) < 1) throw Error("readInt(): error parsing %s", buf.ptr()); return result; } int Scanner::readUnsigned () { int result = 0; bool was_digit = false; while (!isEOF()) { char c = readChar(); if (isdigit(c)) { was_digit = true; result = (int)(c - '0') + result * 10; } else { seek(-1, SEEK_CUR); break; } } if (!was_digit) throw Error("readUnsigned(): no digits"); return result; } // This very basic floating-point number parser was written // to avoid locale problems on various platforms. bool Scanner::_readDouble (double &res, int max) { res = 0; bool plus = false; bool minus = false; bool digit = false; bool e = false; double denom = 0; int cnt = 0; while (1) { if (max > 0 && cnt == max) break; char c = (char)lookNext(); if (c == -1) // EOF break; if (c == '+') { if (plus || minus || digit || denom > 1) return false; plus = true; } else if (c == '-') { if (plus || minus || digit || denom > 1) return false; minus = true; } else if (isdigit(c)) { if (denom > 1) { res += (c - '0') / (double)denom; denom *= 10; } else res = res * 10 + (c - '0'); digit = true; } else if (c == '.') { if (denom > 1) return false; denom = 10; } else if (c == 'E' || c == 'e') { skip(1); e = true; break; } else if (isspace(c)) { if (plus || minus || digit || denom > 1) break; } else break; skip(1); cnt++; } if (minus) res *= -1; if (e) { int exponent = readInt(); if (exponent > 0) { while (exponent-- > 0) res *= 10; } while (exponent++ < 0) res /= 10; } return digit; } float Scanner::readFloat (void) { double res; if (!_readDouble(res, 0)) throw Error("readFloat(): error parsing"); return (float)res; } bool Scanner::tryReadFloat (float &value) { int pos = tell(); double res; if (!_readDouble(res, 0)) { seek(pos, SEEK_SET); return false; } value = (float)res; return true; } void Scanner::readWord (Array &word, const char *delimiters) { word.clear(); if (isEOF()) throw Error("readWord(): end of stream"); do { int next = lookNext(); if (next == -1) break; if (delimiters == 0 && isspace((char)next)) break; if (delimiters != 0 && strchr(delimiters, (char)next) != NULL) break; word.push(readChar()); if (word.size() > MAX_LINE_LENGTH) throw Error("Line length is too long. Probably the file format is not correct."); } while (!isEOF()); word.push(0); } float Scanner::readFloatFix (int digits) { int pos = tell(); double res; if (!_readDouble(res, digits)) throw Error("readFloatFix(): error parsing"); int rest = tell() - pos - digits; // Check that the unread part contains only spaces while (rest-- > 0) { if (!isspace(readChar())) throw Error("readFloatFix(): garbage after the number"); } return (float)res; } char Scanner::readChar () { char c; read(sizeof(char), &c); return c; } byte Scanner::readByte () { byte c; read(1, &c); return c; } bool Scanner::skipLine () { char c; if (isEOF()) return false; while (!isEOF()) { c = readChar(); if (c == '\n') { if (lookNext() == '\r') skip(1); return true; } if (c == '\r') { if (lookNext() == '\n') skip(1); return true; } } return false; } void Scanner::read (int length, Array &buf) { buf.resize(length); read(length, buf.ptr()); } void Scanner::skipSpace () { while (isspace(lookNext())) skip(1); } void Scanner::skipUntil (const char *delimiters) { while (strchr(delimiters, lookNext()) == nullptr) skip(1); } void Scanner::appendLine (Array &out, bool append_zero) { if (isEOF()) throw Error("appendLine(): end of stream"); if (out.size() > 0) while (out.top() == 0) out.pop(); do { char c = readChar(); if (c == '\r') { if (lookNext() == '\n') continue; break; } if (c == '\n') break; out.push(c); if (out.size() > MAX_LINE_LENGTH) throw Error("Line length is too long. Probably the file format is not correct."); } while (!isEOF()); if (append_zero) out.push(0); } void Scanner::readLine (Array &out, bool append_zero) { out.clear(); appendLine(out, append_zero); } void Scanner::readCharsFix (int n, char *chars_out) { read(n, chars_out); } word Scanner::readBinaryWord () { word res; read(sizeof(word), &res); return res; } dword Scanner::readBinaryDword () { dword res; read(sizeof(dword), &res); return res; } int Scanner::readBinaryInt () { int res; read(sizeof(int), &res); return res; //*res = ntohl(*res); } float Scanner::readBinaryFloat () { float res; read(sizeof(float), &res); return res; } short Scanner::readPackedShort () { byte high = readByte(); if (high < 128) return high; byte low = readByte(); high -= 128; return high * (short)256 + low; } unsigned int Scanner::readPackedUInt () { unsigned int value = 0; int shift = 0; while (true) { byte cur = readByte(); value |= (cur & 0x7F) << shift; if (!(cur & 0x80)) return value; shift += 7; } } void Scanner::readAll (Array &arr) { arr.clear_resize(length() - tell()); read(arr.size(), arr.ptr()); } bool Scanner::isSingleLine (Scanner &scanner) { int pos = scanner.tell(); scanner.skipLine(); bool res = scanner.isEOF(); scanner.seek(pos, SEEK_SET); return res; } // // FileScanner // FileScanner::FileScanner (Encoding filename_encoding, const char *filename) { _init(filename_encoding, filename); } FileScanner::FileScanner (const char *format, ...) { char filename[1024]; va_list args; va_start(args, format); vsnprintf(filename, sizeof(filename), format, args); va_end(args); _init(ENCODING_ASCII, filename); } void FileScanner::_init (Encoding filename_encoding, const char *filename) { _file = 0; _file_len = 0; if (filename == 0) throw Error("null filename"); _file = openFile(filename_encoding, filename, "rb"); if (_file == NULL) throw Error("can't open file %s. Error: %s", filename, strerror(errno)); fseek(_file, 0, SEEK_END); _file_len = ftell(_file); fseek(_file, 0, SEEK_SET); _invalidateCache(); } int FileScanner::lookNext () { _validateCache(); if (_cache_pos == _max_cache) return -1; return _cache[_cache_pos]; } void FileScanner::_invalidateCache () { _max_cache = 0; _cache_pos = 0; } void FileScanner::_validateCache () { if (_cache_pos < _max_cache) return; size_t nread = fread(_cache, 1, NELEM(_cache), _file); _max_cache = nread; _cache_pos = 0; } int FileScanner::tell () { _validateCache(); return ftell(_file) - _max_cache + _cache_pos; } void FileScanner::read (int length, void *res) { int to_read_from_cache = __min(length, _max_cache - _cache_pos); memcpy(res, _cache + _cache_pos, to_read_from_cache); _cache_pos += to_read_from_cache; if (to_read_from_cache != length) { int left = length - to_read_from_cache; size_t nread = fread((char*)res + to_read_from_cache, 1, left, _file); if (nread != (size_t)left) throw Error("FileScanner::read() error"); } } bool FileScanner::isEOF () { if (_file == NULL) return true; if (_cache_pos < _max_cache) return false; return tell() == _file_len; } void FileScanner::skip (int n) { _validateCache(); _cache_pos += n; if (_cache_pos > _max_cache) { int delta = _cache_pos - _max_cache; int res = fseek(_file, delta, SEEK_CUR); _invalidateCache(); if (res != 0) throw Error("skip() passes after end of file"); } } void FileScanner::seek (int pos, int from) { if (from == SEEK_CUR) fseek(_file, pos - _max_cache + _cache_pos, from); else fseek(_file, pos, from); _invalidateCache(); } int FileScanner::length () { return _file_len; } char FileScanner::readChar () { _validateCache(); if (_cache_pos == _max_cache) throw Error("readChar() passes after end of file"); return _cache[_cache_pos++]; } FileScanner::~FileScanner () { if (_file != NULL) fclose(_file); } // // BufferScanner // void BufferScanner::_init (const char *buffer, int size) { if (size < -1 || (size > 0 && buffer == 0)) throw Error("incorrect parameters in BufferScanner constructor"); _buffer = buffer; _size = size; _offset = 0; } BufferScanner::BufferScanner (const char *buffer, int buffer_size) { _init(buffer, buffer_size); } BufferScanner::BufferScanner (const byte *buffer, int buffer_size) { _init((const char *)buffer, buffer_size); } BufferScanner::BufferScanner (const char *str) { if (str == 0) throw Error("null input"); _init(str, (int)strlen(str)); } BufferScanner::BufferScanner (const Array &arr) { _init(arr.ptr(), arr.size()); } BufferScanner::~BufferScanner () { } bool BufferScanner::isEOF () { if (_size < 0) throw Error("isEOF() called to unlimited buffer"); return _offset >= _size; } void BufferScanner::read (int length, void *res) { if (_size >= 0 && _offset + length > _size) throw Error("BufferScanner::read() error"); memcpy(res, &_buffer[_offset], length); _offset += length; } int BufferScanner::lookNext () { if (_size >= 0 && _offset >= _size) return -1; return _buffer[_offset]; } int BufferScanner::length () { return _size; } int BufferScanner::tell () { return _offset; } const void * BufferScanner::curptr () { return _buffer + _offset; } void BufferScanner::skip (int n) { _offset += n; if (_size >= 0 && _offset > _size) throw Error("skip() passes after end of buffer"); } void BufferScanner::seek (int pos, int from) { if (from == SEEK_SET) _offset = pos; else if (from == SEEK_CUR) _offset += pos; else // SEEK_END { if (_size < 0) throw Error("can not seek from end: buffer is unlimited"); _offset = _size - pos; } if ((_size >= 0 && _offset > _size) || _offset < 0) throw Error("size = %d, offset = %d after seek()", _size, _offset); } byte BufferScanner::readByte () { if (_size >= 0 && _offset >= _size) throw Error("readByte(): end of buffer"); return _buffer[_offset++]; } void Scanner::_prefixFunction (Array &str, Array &prefix) { prefix.clear(); prefix.push(0); int i, k = 0; for (i = 1; i < str.size(); i++) { while ((k > 0) && (str[k] != str[i])) k = prefix[k - 1]; if (str[k] == str[i]) k++; prefix.push(k); } } bool Scanner::findWord (const char *word) { QS_DEF(ReusableObjArray< Array >, strs); strs.clear(); Array &str = strs.push(); str.readString(word, false); return findWord(strs) == 0; } int Scanner::findWord (ReusableObjArray< Array > &words) { if (isEOF()) return -1; QS_DEF(ReusableObjArray< Array >, prefixes); QS_DEF(Array, pos); int i; int pos_saved = tell(); prefixes.clear(); pos.clear(); for (i = 0; i < words.size(); i++) { _prefixFunction(words[i], prefixes.push()); pos.push(0); } while (!isEOF()) { int c = readChar(); for (i = 0; i < words.size(); i++) { while (pos[i] > 0 && words[i][pos[i]] != c) pos[i] = prefixes[i][pos[i] - 1]; if (words[i][pos[i]] == c) pos[i]++; if (pos[i] == words[i].size()) { seek(-words[i].size(), SEEK_CUR); return i; } } } seek(pos_saved, SEEK_SET); return -1; } bool Scanner::findWordIgnoreCase (const char *word) { QS_DEF(ReusableObjArray< Array >, strs); strs.clear(); Array &str = strs.push(); str.readString(word, false); return findWordIgnoreCase(strs) == 0; } int Scanner::findWordIgnoreCase (ReusableObjArray< Array > &words) { if (isEOF()) return -1; QS_DEF(ReusableObjArray< Array >, prefixes); QS_DEF(Array, pos); int i; int pos_saved = tell(); prefixes.clear(); pos.clear(); for (i = 0; i < words.size(); i++) { _prefixFunction(words[i], prefixes.push()); pos.push(0); } while (!isEOF()) { int c = readChar(); for (i = 0; i < words.size(); i++) { int c1 = ::tolower(words[i][pos[i]]); int c2 = ::tolower(c); while (pos[i] > 0 && c1 != c2) pos[i] = prefixes[i][pos[i] - 1]; if (c1 == c2) pos[i]++; if (pos[i] == words[i].size()) { seek(-words[i].size(), SEEK_CUR); return i; } } } seek(pos_saved, SEEK_SET); return -1; } Indigo-indigo-1.2.3/common/base_cpp/scanner.h000066400000000000000000000077641271037650300210650ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __scanner_h__ #define __scanner_h__ #include #include "base_cpp/array.h" #include "base_cpp/io_base.h" #include "base_cpp/obj_array.h" #include "base_cpp/reusable_obj_array.h" namespace indigo { class DLLEXPORT Scanner { public: DECL_ERROR; virtual ~Scanner (); virtual void read (int length, void *res) = 0; virtual void skip (int n) = 0; virtual bool isEOF () = 0; virtual int lookNext () = 0; virtual void seek (int pos, int from) = 0; virtual int length () = 0; virtual int tell () = 0; virtual byte readByte (); virtual void readAll (Array &arr); void read (int length, Array &buf); void readLine (Array &out, bool append_zero); void appendLine (Array &out, bool append_zero); bool skipLine (); virtual char readChar (); word readBinaryWord (); int readBinaryInt (); dword readBinaryDword (); float readBinaryFloat (); short readPackedShort (); unsigned int readPackedUInt (); void readCharsFix (int n, char *chars_out); float readFloatFix (int digits); int readIntFix (int digits); void skipSpace (); void skipUntil (const char *delimiters); float readFloat (void); bool tryReadFloat (float &value); int readInt (void); int readInt1 (void); int readUnsigned (); // when delimiters = 0, any isspace() character is considered delimiter void readWord (Array &word, const char *delimiters); bool findWord (const char *word); int findWord (ReusableObjArray< Array > &words); bool findWordIgnoreCase (const char *word); int findWordIgnoreCase (ReusableObjArray< Array > &words); static bool isSingleLine (Scanner &scanner); protected: bool _readDouble (double &res, int max); void _prefixFunction (Array &str, Array &prefix); }; class DLLEXPORT FileScanner : public Scanner { public: FileScanner (Encoding filename_encoding, const char *filename); explicit FileScanner (const char *format, ...); virtual ~FileScanner (); virtual void read (int length, void *res); virtual bool isEOF (); virtual void skip (int n); virtual int lookNext (); virtual void seek (int pos, int from); virtual int length (); virtual int tell (); virtual char readChar (); private: FILE *_file; int _file_len; unsigned char _cache[1024]; int _cache_pos, _max_cache; void _validateCache (); void _invalidateCache (); void _init (Encoding filename_encoding, const char *filename); // no implicit copy FileScanner (const FileScanner &); }; class DLLEXPORT BufferScanner : public Scanner { public: explicit BufferScanner (const char *buffer, int buffer_size); explicit BufferScanner (const byte *buffer, int buffer_size); explicit BufferScanner (const char *str); explicit BufferScanner (const Array &arr); virtual ~BufferScanner (); virtual bool isEOF (); virtual void read (int length, void *res); virtual void skip (int n); virtual int lookNext (); virtual void seek (int pos, int from); virtual int length (); virtual int tell (); virtual byte readByte (); const void * curptr (); private: const char *_buffer; int _size; int _offset; void _init (const char *buffer, int length); // no implicit copy BufferScanner (const BufferScanner &); }; } #endif Indigo-indigo-1.2.3/common/base_cpp/shared_ptr.cpp000066400000000000000000000003231271037650300221020ustar00rootroot00000000000000#include "base_cpp/shared_ptr.h" using namespace indigo; RedBlackMap SharedPtrStaticData::_counters; OsLock SharedPtrStaticData::_lock; IMPL_EXCEPTION(indigo, SharedPtrError, "SharedPtr error"); Indigo-indigo-1.2.3/common/base_cpp/shared_ptr.h000066400000000000000000000030361271037650300215530ustar00rootroot00000000000000#ifndef __shared_ptr__ #define __shared_ptr__ #include "base_cpp/exception.h" #include "base_cpp/red_black.h" #include "base_cpp/os_sync_wrapper.h" namespace indigo { class SharedPtrStaticData { protected: static RedBlackMap _counters; static OsLock _lock; }; DECL_EXCEPTION(SharedPtrError); template class SharedPtr : public SharedPtrStaticData { public: explicit SharedPtr (T *ptr = 0) { _ptr = ptr; _capture(); } ~SharedPtr () { _release(); } T * get () const { return _ptr; } T & ref () const { if (_ptr == 0) throw Error("no reference"); return *_ptr; } T * operator -> () const { if (_ptr == 0) throw Error("no reference"); return _ptr; } void reset (T *ptr) { if (ptr != _ptr) { _release(); _ptr = ptr; _capture(); } } DECL_TPL_ERROR(SharedPtrError); protected: void _release () { OsLocker locker(_lock); if (_ptr == 0) return; int &counter = _counters.at(_ptr); counter--; if (counter == 0) { _counters.remove(_ptr); delete _ptr; } } void _capture () { OsLocker locker(_lock); if (_ptr == 0) return; int *counter = _counters.at2(_ptr); if (counter == 0) _counters.insert(_ptr, 1); else (*counter)++; } T *_ptr; private: SharedPtr (const SharedPtr &); // no implicit copy }; } #endif Indigo-indigo-1.2.3/common/base_cpp/shmem.h000066400000000000000000000022741271037650300205340ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __shmem_h__ #define __shmem_h__ #include "base_cpp/ptr_array.h" namespace indigo { class SharedMemory { public: explicit SharedMemory (const char *name, int size, bool no_map_if_first); virtual ~SharedMemory (); inline bool wasFirst () {return _was_first; } inline const char * getID () {return _id; } void * ptr () {return _pointer;} DECL_ERROR; private: bool _was_first; char _id[1024]; void * _pointer; #ifdef _WIN32 void * _map_object; #else int _shm_id; char _filename[1024]; #endif }; } #endif Indigo-indigo-1.2.3/common/base_cpp/shmem_posix.cpp000066400000000000000000000052671271037650300223160ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #if !defined(_WIN32) #include #include #include #include #include #include #include #include "base_cpp/shmem.h" using namespace indigo; IMPL_ERROR(SharedMemory, "shared memory"); SharedMemory::SharedMemory (const char *name, int size, bool no_map_if_first) { snprintf(_filename, sizeof(_filename), "/tmp/indigo_shm_%s", name); _pointer = NULL; _was_first = false; int key = ftok(_filename, 'D'); // 'D' has no actual meaning if (key == -1 && errno == ENOENT) { // create file int fd = creat(_filename, 0666); if (fd == -1) throw Error("can't create %s: %s", _filename, strerror(errno)); key = ftok(_filename, 'D'); } if (key == -1) throw Error("can't get key: %s", strerror(errno)); _shm_id = -1; if (!no_map_if_first) { // try to create _shm_id = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666); if (_shm_id == -1) { if (errno != EEXIST) throw Error("can't shmget: %s", strerror(errno)); } else _was_first = true; } if (_shm_id == -1) { _shm_id = shmget(key, size, 0666); if (_shm_id == -1) { if (errno == ENOENT) return; throw Error("can't second shmget (%d): %s", key, strerror(errno)); } } _pointer = shmat(_shm_id, NULL, 0); if (_pointer == (void *)-1) throw Error("can't shmat: %s", strerror(errno)); strncpy(_id, name, sizeof(_id)); if (_was_first) memset(_pointer, 0, size); } SharedMemory::~SharedMemory () { struct shmid_ds ds; // detach from the memory segment if (shmdt((char *)_pointer) != 0) return; // throwing exceptions on destructor is bad // get information about the segment if (shmctl(_shm_id, IPC_STAT, &ds) != 0) return; if (ds.shm_nattch < 1) { // nobody is using the segment; remove it shmctl(_shm_id, IPC_RMID, NULL); // and remove the temporary file unlink(_filename); } } #endif Indigo-indigo-1.2.3/common/base_cpp/shmem_win32.cpp000066400000000000000000000050741271037650300221120ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #if defined(_WIN32) #include #include #include "base_cpp/shmem.h" using namespace indigo; IMPL_ERROR(SharedMemory, "shared memory"); SharedMemory::SharedMemory (const char *name, int size, bool no_map_if_first) { char *winapi_error; _map_object = NULL; _pointer = NULL; _map_object = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security attributes PAGE_READWRITE, // read/write access 0, // size: high 32-bits size, // size: low 32-bits name); // name of map object if (_map_object == NULL) { FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &winapi_error, 0, NULL ); throw Error("can't create map object: %s", winapi_error); } _was_first = (GetLastError() != ERROR_ALREADY_EXISTS); if (_was_first && no_map_if_first) return; _pointer = MapViewOfFile( _map_object, // object to map view of FILE_MAP_WRITE, // read/write access 0, // high offset: map from 0, // low offset: beginning 0); // default: map entire file if (_pointer == NULL) { FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &winapi_error, 0, NULL ); throw Error("can't get pointer: %s", winapi_error); } strncpy(_id, name, sizeof(_id)); if (_was_first) memset(_pointer, 0, size); } SharedMemory::~SharedMemory () { if (_pointer != NULL) UnmapViewOfFile(_pointer); if (_map_object != NULL) CloseHandle(_map_object); } #endif Indigo-indigo-1.2.3/common/base_cpp/smart_output.cpp000066400000000000000000000146051271037650300225250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "base_cpp/smart_output.h" using namespace indigo; IMPL_ERROR(SmartTableOutput, "smart table output"); CP_DEF(SmartTableOutput); SmartTableOutput::SmartTableOutput (Output &output, bool use_smart_printing) : CP_INIT, TL_CP_GET(_lines), TL_CP_GET(_line_formats), TL_CP_GET(_line_format_index), _output(output) { _lines.clear(); _line_format_index.clear(); _line_formats.clear(); _active_line = &_lines.push(); _line_format_index.push(-1); _use_smart_printing = use_smart_printing; } SmartTableOutput::~SmartTableOutput () { flush(); } void SmartTableOutput::write (const void *data, int size) { if (!_use_smart_printing) { _output.write(data, size); return; } // split data into lines const char *data_char = (const char *)data; int start = 0; int end = 0; while (end <= size) { if (end == size || data_char[end] == '\n') { for (int i = start; i < end; i++) _active_line->push(data_char[i]); if (end < size) { _active_line = &_lines.push(); _line_format_index.push(_line_formats.size() - 1); } start = end + 1; } end++; } } void SmartTableOutput::seek (int offset, int from) { throw Error("seek is not supported"); } int SmartTableOutput::tell () { throw Error("tell is not supported"); } void SmartTableOutput::flush () { // TODO: MR: merge this with flushTable? if (!_use_smart_printing || _lines.size() == 0) return; // Smart table printing QS_DEF(Array, column_widths); column_widths.clear(); for (int i = 0; i < _lines.size(); i++) _updateColumnWidths(i, column_widths); for (int i = 0; i < _lines.size(); i++) _printLineSmart(i, column_widths); _lines.clear(); } void SmartTableOutput::setLineFormat (const char *line_format) { Array &format = _line_formats.push(); format.copy(line_format, strlen(line_format)); _line_format_index.top() = _line_formats.size() - 1; } void SmartTableOutput::_updateColumnWidths (int index, Array &widths) { const Array &line = _lines[index]; if (line.size() == 0 || line[0] == HLINE_CHAR) return; QS_DEF(Array, cur_widths); cur_widths.clear(); cur_widths.push(0); for (int i = 0; i < line.size(); i++) { if (line[i] == '\t') cur_widths.push(0); else cur_widths.top()++; } // Check merged columns Array &format = _line_formats[_line_format_index[index]]; int cur_column = 0; for (int i = 0; i < format.size(); i++) { if (format[i] > '0' && format[i] < '9') { int merge_count = format[i] - '0'; int width = cur_widths[cur_column]; for (int j = 0; j < merge_count; j++) cur_widths[cur_column + j] = width / merge_count + 1; cur_column += merge_count; i++; } else if (format[i] == 'l' || format[i] == 'r' || format[i] == 'c') cur_column++; } // Update total widths while (widths.size() < cur_widths.size()) widths.push(0); for (int i = 0; i < cur_widths.size(); i++) widths[i] = __max(widths[i], cur_widths[i] + 2); } void SmartTableOutput::_printLineSmart (int index, const Array &widths) { const Array &line = _lines[index]; if (line.size() == 0) return; Array &format = _line_formats[_line_format_index[index]]; bool hline = false; int field_begin = 0, field_end = 0, cur_column = 0; for (int i = 0; i < format.size(); i++) { int width = 0; if (cur_column < widths.size()) width = widths[cur_column]; int skip_tabs = 1; if (format[i] > '0' && format[i] < '9') { int merge_count = format[i] - '0'; for (int j = 1; j < merge_count; j++) width += widths[cur_column + j]; cur_column += merge_count - 1; skip_tabs = merge_count; i++; } if (format[i] == 'l' || format[i] == 'r' || format[i] == 'c') { if (hline || line[field_end] == HLINE_CHAR) { hline = true; while (width-- > 0) _output.writeChar('-'); cur_column++; continue; } // Print current column while (field_end < line.size() && line[field_end] != '\t') field_end++; int field_width = field_end - field_begin; width -= 2; int space_begin = 0, space_end = 0; if (format[i] == 'l') space_end = width - field_width; else if (format[i] == 'r') space_begin = width - field_width; else // format[i] == 'c' { space_begin = (width - field_width) / 2; space_end = width - field_width - space_begin; } space_begin++; space_end++; while (space_begin-- > 0) _output.writeChar(' '); if (field_width != 0) _output.write(line.ptr() + field_begin, field_width); while (space_end-- > 0) _output.writeChar(' '); // Go to next column cur_column++; while (skip_tabs-- > 0) { while (field_end < line.size() && line[field_end] != '\t') field_end++; if (field_end < line.size() - 1) field_end++; } field_begin = field_end; } else _output.writeChar(format[i]); } _output.printf("\n"); } void SmartTableOutput::printHLine () { if (!_use_smart_printing) return; if (_active_line->size() != 0) _active_line = &_lines.push(); _active_line->push((char)HLINE_CHAR); _active_line = &_lines.push(); _line_format_index.push(_line_formats.size() - 1); } Indigo-indigo-1.2.3/common/base_cpp/smart_output.h000066400000000000000000000033031271037650300221630ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __smart_output_h__ #define __smart_output_h__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" #include "base_cpp/output.h" #include "base_cpp/reusable_obj_array.h" namespace indigo { class SmartTableOutput : public Output { public: SmartTableOutput (Output &output, bool use_smart_printing); virtual ~SmartTableOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); void setLineFormat (const char *line_format); void printHLine (); enum { HLINE_CHAR = '\a' } ; DECL_ERROR; private: void _updateColumnWidths (int index, Array &widths); void _printLineSmart (int index, const Array &widths); CP_DECL; TL_CP_DECL(ReusableObjArray< Array >, _lines); TL_CP_DECL(ReusableObjArray< Array >, _line_formats); TL_CP_DECL(Array, _line_format_index); Array *_active_line; bool _use_smart_printing; Output &_output; }; } #endif // __smart_output_h__ Indigo-indigo-1.2.3/common/base_cpp/string_pool.cpp000066400000000000000000000042301271037650300223070ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include "base_cpp/string_pool.h" using namespace indigo; IMPL_ERROR(StringPool, "string pool"); StringPool::StringPool () { } StringPool::~StringPool () { } int StringPool::_add (const char *str, int size) { int idx = _pool.add(); // Save self into to the pool to check used items _pool[idx] = idx; if (idx >= _storage.size()) _storage.resize(idx + 1); if (_storage.at(idx) == 0) _storage.set(idx, new Array()); if (size == -1 && str == 0) throw Error("Internal error: size == -1 && str == 0"); if (size == -1) size = strlen(str); _storage.at(idx)->resize(size + 1); if (str != 0 && size != 0) memcpy(at(idx), str, size); at(idx)[size] = 0; return idx; } int StringPool::add (const char *str) { return _add(str, -1); } int StringPool::add (int size) { return _add(0, size); } int StringPool::add (Array &str) { return _add(str.ptr(), str.size()); } void StringPool::remove (int idx) { _pool.remove(idx); } char * StringPool::at (int idx) { return _storage[_pool[idx]]->ptr(); } const char * StringPool::at (int idx) const { return _storage[_pool[idx]]->ptr(); } int StringPool::size () const { return _pool.size(); } int StringPool::begin () const { return _pool.begin(); } int StringPool::end () const { return _pool.end(); } int StringPool::next (int i) const { return _pool.next(i); } void StringPool::clear () { _pool.clear(); // Do not clear storage to enable memory reuse // _storage.clear(); } Indigo-indigo-1.2.3/common/base_cpp/string_pool.h000066400000000000000000000042161271037650300217600ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __string_pool_h__ #define __string_pool_h__ #include "base_cpp/pool.h" #include "base_cpp/ptr_array.h" #include "base_cpp/auto_iter.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT StringPool { public: DECL_ERROR; StringPool (); ~StringPool (); int add (const char *str); int add (Array &str); int add (int size); void remove (int idx); int size () const; int begin () const; int end () const; int next (int i) const; void clear (); char * at (int idx); const char * at (int idx) const; /* * Iterators */ class PoolIter : public AutoIterator { public: PoolIter(StringPool &owner, int idx): _owner(owner), AutoIterator(idx) { } PoolIter & operator++() { _idx = _owner.next(_idx); return *this; } private: StringPool &_owner; }; class PoolAuto { public: PoolAuto(StringPool &owner) : _owner(owner) { } PoolIter begin(){ return StringPool::PoolIter(_owner, _owner.begin()); } PoolIter end() { return StringPool::PoolIter(_owner, _owner.end()); } private: StringPool &_owner; }; PoolAuto elements () { return PoolAuto(*this); } protected: int _add (const char *str, int size); Pool _pool; PtrArray< Array > _storage; private: StringPool (const StringPool &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif Indigo-indigo-1.2.3/common/base_cpp/temporary_thread_obj.h000066400000000000000000000034161271037650300236250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __temporary_thread_obj__ #define __temporary_thread_obj__ #include #include #include #include #include #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { // // Various Indigo API methods returns pointers to the temporary buffers // This leads to an issues when multiple threads gets the same pointers // To avoid this we returns a temporary object that belongs to the current // thread for a short time (10 sec). // template class TemporaryThreadObjManager { public: // This method return a temporary object for this thread that lives at least 10 seconds. // Method is synchronized T& getObject (); private: typedef std::chrono::steady_clock steady_clock; struct Container { Container(); Container(Container&& that); steady_clock::time_point timestamp; std::unique_ptr obj; }; typedef std::unordered_map Map; Map _objects; std::mutex access_mutex; }; #include "temporary_thread_obj.hpp" } #ifdef _WIN32 #pragma warning(pop) #endif #endif // __tlscont_h__ Indigo-indigo-1.2.3/common/base_cpp/temporary_thread_obj.hpp000066400000000000000000000042371271037650300241670ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Implementation for temporary_thread_obj.h // #ifndef __temporary_thread_obj_hpp__ #define __temporary_thread_obj_hpp__ template T& TemporaryThreadObjManager::getObject () { std::lock_guard lock(access_mutex); std::thread::id id = std::this_thread::get_id(); steady_clock::time_point now = steady_clock::now(); // Find record for this thread auto it = _objects.find(id); if (it == _objects.end()) { // Try to find old record for (auto obj_it = _objects.begin(); obj_it != _objects.begin(); ++obj_it) { auto elapsed_seconds = std::chrono::duration_cast(now - it->second.timestamp).count(); if (elapsed_seconds > 10) { // reassign to the current thread Container container = std::move(obj_it->second); _objects.erase(obj_it); it = _objects.emplace(id, std::move(container)).first; break; } } if (it == _objects.end()) // If there are no old record found then create a new record it = _objects.emplace(id, Container()).first; } Container &c = it->second; c.timestamp = steady_clock::now(); return *c.obj.get(); } template TemporaryThreadObjManager::Container::Container (Container&& that) : obj(std::move(that.obj)) { timestamp = that.timestamp; } template TemporaryThreadObjManager::Container::Container () : obj(new T()) { } #endif // __temporary_thread_obj_hpp__ Indigo-indigo-1.2.3/common/base_cpp/tlscont.cpp000066400000000000000000000042021271037650300214350ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" using namespace indigo; _SIDManager _SIDManager::_instance; OsLock _SIDManager::_lock; IMPL_ERROR(_SIDManager, "TLS"); _SIDManager& _SIDManager::getInst (void) { return _instance; } _SIDManager::~_SIDManager (void) { qword *pId; osTlsGetValue((void**)&pId, _tlsIdx); delete pId; osTlsFree(_tlsIdx); } void _SIDManager::setSessionId (qword id) { OsLocker locker(_lock); if (!_allSIDs.find(id)) _allSIDs.insert(id); qword *pId = _getID(); if (pId == NULL) { pId = new qword(id); osTlsSetValue(_tlsIdx, (void*)pId); } else *pId = id; } qword _SIDManager::allocSessionId (void) { OsLocker locker(_lock); qword id; if (_vacantSIDs.size() > 0) id = _vacantSIDs.pop(); else { while (_allSIDs.find(_lastNewSID)) ++_lastNewSID; id = _lastNewSID; _allSIDs.insert(id); ++_lastNewSID; } return id; } qword _SIDManager::getSessionId (void) { qword *pId = _getID(); qword id; if (pId == NULL) { id = allocSessionId(); setSessionId(id); } else id = *pId; return id; } void _SIDManager::releaseSessionId (qword id) { OsLocker locker(_lock); _vacantSIDs.push(id); } qword * _SIDManager::_getID (void) const { void* pId; osTlsGetValue(&pId, _tlsIdx); return (qword *)pId; } _SIDManager::_SIDManager (void) : _lastNewSID(0) { if (osTlsAlloc(&_tlsIdx) == 0) throw Error("can't allocate thread local storage cell"); } Indigo-indigo-1.2.3/common/base_cpp/tlscont.h000066400000000000000000000241141271037650300211060ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __tlscont_h__ #define __tlscont_h__ #include #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/pool.h" #include "base_cpp/os_sync_wrapper.h" #include "base_cpp/red_black.h" #include "base_cpp/ptr_array.h" #include "base_c/os_tls.h" #include "base_cpp/auto_ptr.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { // Session identifiers manager. // Every thread have local session ID that corresponds to the all // local session variables. class DLLEXPORT _SIDManager { public: static _SIDManager& getInst (void); _SIDManager (void); ~_SIDManager (void); void setSessionId (qword id); qword allocSessionId (void); qword getSessionId (void); // Add specified SID to the vacant list. // This method should be called before thread exit if SID was // assigned automatically (not by manual TL_SET_SESSION_ID call) void releaseSessionId (qword id); DECL_ERROR; private: qword * _getID () const; // Thread local key for storing current session ID TLS_IDX_TYPE _tlsIdx; RedBlackSet _allSIDs; qword _lastNewSID; // Array with vacant SIDs Array _vacantSIDs; static _SIDManager _instance; static OsLock _lock; }; // Macros for managing session IDs for current thread #define TL_GET_SESSION_ID() _SIDManager::getInst().getSessionId() #define TL_SET_SESSION_ID(id) _SIDManager::getInst().setSessionId(id) #define TL_ALLOC_SESSION_ID() _SIDManager::getInst().allocSessionId() #define TL_RELEASE_SESSION_ID(id) _SIDManager::getInst().releaseSessionId(id) // Container that keeps one instance of specifed type per session template class _SessionLocalContainer { public: T& getLocalCopy (void) { return getLocalCopy(_SIDManager::getInst().getSessionId()); } T& getLocalCopy (const qword id) { OsLocker locker(_lock.ref()); AutoPtr& ptr = _map.findOrInsert(id); if (ptr.get() == NULL) ptr.reset(new T()); return ptr.ref(); } private: typedef RedBlackObjMap > _Map; _Map _map; ThreadSafeStaticObj _lock; }; // Helpful templates to deal with commas in template type names // to be able to write like // QS_DEF((std::unordered_map), atoms_id); // See http://stackoverflow.com/a/13842784 template struct ArgumentType; template struct ArgumentType { typedef U Type; }; #define _GET_TYPE(t) ArgumentType::Type // Macros for working with global variables per each session // By tradition this macros start with TL_, but should start with SL_ #define TL_DECL_EXT(type, name) extern _SessionLocalContainer< _GET_TYPE(type) > TLSCONT_##name #define TL_DECL(type, name) static _SessionLocalContainer< _GET_TYPE(type) > TLSCONT_##name #define TL_GET(type, name) _GET_TYPE(type)& name = (TLSCONT_##name).getLocalCopy() #define TL_DECL_GET(type, name) TL_DECL(type, name); TL_GET(type, name) #define TL_GET2(type, name, realname) _GET_TYPE(type)& name = (TLSCONT_##realname).getLocalCopy() #define TL_GET_BY_ID(type, name, id) _GET_TYPE(type)& name = (TLSCONT_##name).getLocalCopy(id) #define TL_DEF(className, type, name) _SessionLocalContainer< _GET_TYPE(type) > className::TLSCONT_##name #define TL_DEF_EXT(type, name) _SessionLocalContainer< _GET_TYPE(type) > TLSCONT_##name // Pool for local variables, reused in consecutive function calls, // but not required to preserve their state template class _ReusableVariablesPool { public: _ReusableVariablesPool () { is_valid = true; } ~_ReusableVariablesPool () { is_valid = false; } bool isValid () const { return is_valid; } T& getVacant (int& idx) { OsLocker locker(_lock); if (_vacant_indices.size() != 0) { idx = _vacant_indices.pop(); return *_objects[idx]; } _objects.add(new T); idx = _objects.size() - 1; _vacant_indices.reserve(idx + 1); return *_objects[idx]; } void release (int idx) { OsLocker locker(_lock); _vacant_indices.push(idx); } T& getByIndex (int idx) { return *_objects[idx]; } private: OsLock _lock; bool is_valid; PtrArray< T > _objects; Array _vacant_indices; }; // Utility class for automatically release call template class _ReusableVariablesAutoRelease { public: _ReusableVariablesAutoRelease () : _idx(-1), _var_pool(0) {} void init (int idx, _ReusableVariablesPool< T > *var_pool) { _idx = idx; _var_pool = var_pool; } ~_ReusableVariablesAutoRelease (void) { if (_var_pool == 0) return; // Check if the _var_pool destructor have not been called already // (this can happen on program exit) if (_var_pool->isValid()) _var_pool->release(_idx); } protected: int _idx; _ReusableVariablesPool< T >* _var_pool; }; // Abstract proxy class to call a destructor for an allocated data class Destructor { public: virtual void callDestructor (void *data) = 0; virtual ~Destructor() {}; }; // Proxy destructor class for a type T template class DestructorT : public Destructor { public: virtual void callDestructor (void *data) { ((T*)data)->~T(); } }; // Template function to create proxy destructor template Destructor *createDestructor (T *t) { return new DestructorT(); } // Variables pool that can reuse objects allocations that are initialized in the same order class _LocalVariablesPool { public: _LocalVariablesPool () { reset(); } ~_LocalVariablesPool () { for (int i = 0; i < data.size(); i++) { destructors[i]->callDestructor(data[i]); free(data[i]); } } template size_t hash () { return typeid(T).hash_code(); } template T& getVacant () { data.expandFill(index + 1, 0); destructors.expand(index + 1); type_hash.expandFill(index + 1, 0); if (data[index] == 0) { // Allocate data and destructor data[index] = malloc(sizeof(T)); T *t = new (data[index]) T(); destructors[index] = createDestructor(t); type_hash[index] = hash(); } // Class hash check to verify initialization order if (type_hash[index] != hash()) throw Exception("VariablesPool: invalid initialization order"); T *t = (T*)data[index]; index++; return *t; } void reset () { index = 0; } private: Array data; Array type_hash; PtrArray destructors; int index; }; // Auto release class that additionally calls reset method for LocalVariablesPool class _LocalVariablesPoolAutoRelease : public _ReusableVariablesAutoRelease<_LocalVariablesPool> { public: ~_LocalVariablesPoolAutoRelease () { if (_var_pool == 0) return; if (_var_pool->isValid()) { _LocalVariablesPool &local_pool = _var_pool->getByIndex(_idx); local_pool.reset(); } } }; } // "Quasi-static" variable definition #define QS_DEF(TYPE, name) \ static ThreadSafeStaticObj<_ReusableVariablesPool< _GET_TYPE(TYPE) > > _POOL_##name; \ int _POOL_##name##_idx; \ _GET_TYPE(TYPE) &name = _POOL_##name->getVacant(_POOL_##name##_idx); \ _ReusableVariablesAutoRelease< _GET_TYPE(TYPE) > _POOL_##name##_auto_release; \ _POOL_##name##_auto_release.init(_POOL_##name##_idx, _POOL_##name.ptr()) // // Reusable class members definition // By tradition this macros start with TL_, but should start with SL_ // To work with them you should first define commom pool with CP_DECL, // then define it in the source class with CP_DEF(cls), and initialize // in the constructor via CP_INIT before any TL_CP_ initializations // // Add this to class definition #define TL_CP_DECL(TYPE, name) \ typedef _GET_TYPE(TYPE) _##name##_TYPE; \ _GET_TYPE(TYPE) &name // Add this to constructor initialization list #define TL_CP_GET(name) \ name(_local_pool.getVacant<_##name##_TYPE>()) #define CP_DECL \ _LocalVariablesPoolAutoRelease _local_pool_autorelease; \ static _LocalVariablesPool& _getLocalPool (_LocalVariablesPoolAutoRelease &auto_release); \ _LocalVariablesPool &_local_pool \ #define CP_INIT _local_pool(_getLocalPool(_local_pool_autorelease)) #define CP_DEF(cls) \ _LocalVariablesPool& cls::_getLocalPool (_LocalVariablesPoolAutoRelease &auto_release) \ { \ static ThreadSafeStaticObj< _ReusableVariablesPool< _LocalVariablesPool > > _shared_pool; \ \ int idx; \ _LocalVariablesPool &var = _shared_pool->getVacant(idx); \ auto_release.init(idx, _shared_pool.ptr()); \ return var; \ } \ #ifdef _WIN32 #pragma warning(pop) #endif #endif // __tlscont_h__ Indigo-indigo-1.2.3/common/cmake/000077500000000000000000000000001271037650300165515ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/cmake/ConfigureCommon.cmake000066400000000000000000000003211271037650300226410ustar00rootroot00000000000000if (COMMON_CONFIGURED) return() endif() include(GetSystemVersion) include(SetBuildParameters) include(MakeOutputPath) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(COMMON_CONFIGURED TRUE) Indigo-indigo-1.2.3/common/cmake/DefineTest.cmake000066400000000000000000000004511271037650300216050ustar00rootroot00000000000000macro (DEFINE_TEST test files libs) add_executable(${test} ${files}) target_link_libraries(${test} ${libs}) if(UNIX OR APPLE) target_link_libraries(${test} pthread) endif() set_property(TARGET ${test} PROPERTY FOLDER "tests") add_test(NAME ${test} COMMAND ${test}) endmacro() Indigo-indigo-1.2.3/common/cmake/FindCAIRO.cmake000066400000000000000000000016701271037650300212150ustar00rootroot00000000000000# Locate cairo # This module defines # CAIRO_LIBRARY_DIRS # CAIRO_INCLUDE_DIRS, where to find the headers # FIND_PATH(CAIRO_INCLUDE_DIRS cairo.h $ENV{CAIRODIR}/include $ENV{CAIRODIR} ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /usr/include/cairo /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include ) IF (WIN32) SET(LIBCAIRO "cairo.lib") ELSEIF(UNIX) SET(LIBCAIRO "libcairo.so") ELSEIF(APPLE) SET(LIBCAIRO "libcairo.dylib") ENDIF() FIND_PATH(CAIRO_LIBRARY_DIRS ${LIBCAIRO} $ENV{CAIRODIR}/lib $ENV{CAIRODIR} ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) SET(CAIRO_FOUND "NO") IF(CAIRO_LIBRARY_DIRS AND CAIRO_INCLUDE_DIRS) SET(CAIRO_FOUND "YES") ENDIF(CAIRO_LIBRARY_DIRS AND CAIRO_INCLUDE_DIRS) Indigo-indigo-1.2.3/common/cmake/FindOCI.cmake000066400000000000000000000012451271037650300207700ustar00rootroot00000000000000# Locate oci # This module defines # OCI_LIBRARY_DIRS # OCI_INCLUDE_DIRS, where to find the headers # FIND_PATH(OCI_INCLUDE_DIRS oci.h $ENV{ORACLE_HOME}/rdbms/public/ $ENV{ORACLE_HOME}/oci/include/ ) IF (WIN32) SET(LIBORASDK "oci.lib") ELSEIF(APPLE) SET(LIBORASDK "liborasdk.dylib") ELSEIF(UNIX) SET(LIBORASDK "liborasdk.so") ENDIF() IF(MSVC OR APPLE) FIND_PATH(OCI_LIBRARY_DIRS ${LIBORASDK} $ENV{ORACLE_HOME}/lib/ $ENV{ORACLE_HOME}/oci/lib/msvc/ ) ENDIF() SET(OCI_FOUND "NO") IF((OCI_LIBRARY_DIRS OR (NOT MSVC AND NOT APPLE)) AND OCI_INCLUDE_DIRS) SET(OCI_FOUND "YES") SET(OCI_LIBRARY ${OCI_LIBRARY_DIRS}/${LIBORASDK}) ENDIF()Indigo-indigo-1.2.3/common/cmake/FindPIXMAN.cmake000066400000000000000000000017511271037650300213540ustar00rootroot00000000000000# Locate pixman # This module defines # PIXMAN_LIBRARY_DIRS # PIXMAN_INCLUDE_DIRS, where to find the headers # FIND_PATH(PIXMAN_INCLUDE_DIRS pixman.h $ENV{PIXMANDIR}/include $ENV{PIXMANDIR} ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /usr/include/pixman /usr/include/pixman-1 /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include ) IF (WIN32) SET(LIBPIXMAN "pixman.lib") ELSEIF(UNIX) SET(LIBPIXMAN "libpixman.so") ELSEIF(APPLE) SET(LIBPIXMAN "libpixman.dylib") ENDIF() FIND_PATH(PIXMAN_LIBRARY_DIRS ${LIBPIXMAN} $ENV{PIXMANDIR}/lib $ENV{PIXMANDIR} ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) SET(PIXMAN_FOUND "NO") IF(PIXMAN_LIBRARY_DIRS AND PIXMAN_INCLUDE_DIRS) SET(PIXMAN_FOUND "YES") ENDIF(PIXMAN_LIBRARY_DIRS AND PIXMAN_INCLUDE_DIRS) Indigo-indigo-1.2.3/common/cmake/FindPOSTGRES.cmake000066400000000000000000000021301271037650300216160ustar00rootroot00000000000000# Locate postgres # This module defines # POSTGRES_LIBRARY_DIRS # POSTGRES_INCLUDE_DIRS, where to find the headers FIND_PATH(POSTGRES_INCLUDE_DIRS postgres.h $ENV{BINGO_PG_DIR}/include/server $ENV{BINGO_PG_DIR}/server $ENV{BINGO_PG_DIR}/include/postgresql/server /usr/local/include/server /usr/include/postgres/ENV{BINGO_PG_VERSION}/server /usr/include/postgresql/ENV{BINGO_PG_VERSION}/server /Library/PostgreSQL/$ENV{BINGO_PG_VERSION}/include/postgresql/server /usr/include/postgresql/$ENV{BINGO_PG_VERSION}/server /opt/local/include/postgresql/$ENV{BINGO_PG_VERSION}/server ) IF (MSVC) SET(LIBPOSTGRES "postgres.lib") FIND_PATH(POSTGRES_LIBRARY_DIRS ${LIBPOSTGRES} $ENV{BINGO_PG_DIR}/lib $ENV{BINGO_PG_DIR} ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) ENDIF() SET(POSTGRES_FOUND "NO") IF((POSTGRES_LIBRARY_DIRS OR (UNIX OR APPLE)) AND POSTGRES_INCLUDE_DIRS) SET(POSTGRES_LIBRARY ${POSTGRES_LIBRARY_DIRS}/${LIBPOSTGRES}) SET(POSTGRES_FOUND "YES") ENDIF() Indigo-indigo-1.2.3/common/cmake/FindTINYXML.cmake000066400000000000000000000017501271037650300215230ustar00rootroot00000000000000# Locate tinyxml # This module defines # TINYXML_LIBRARY_DIRS # TINYXML_INCLUDE_DIRS, where to find the headers # FIND_PATH(TINYXML_INCLUDE_DIRS tinyxml.h $ENV{TINYXMLDIR}/include $ENV{TINYXMLDIR} ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /usr/include/tinyxml /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include ) IF (WIN32) SET(LIBTINYXML "tinyxml.lib") ELSEIF(UNIX) SET(LIBTINYXML "libtinyxml.so") ELSEIF(APPLE) SET(LIBTINYXML "libtinyxml.dylib") ENDIF() FIND_PATH(TINYXML_LIBRARY_DIRS ${LIBTINYXML} $ENV{TINYXMLDIR}/lib $ENV{TINYXMLDIR} ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/lib /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 ) SET(TINYXML_FOUND "NO") IF(TINYXML_LIBRARY_DIRS AND TINYXML_INCLUDE_DIRS) SET(TINYXML_FOUND "YES") ENDIF(TINYXML_LIBRARY_DIRS AND TINYXML_INCLUDE_DIRS) Indigo-indigo-1.2.3/common/cmake/GetSystemVersion.cmake000066400000000000000000000050611271037650300230470ustar00rootroot00000000000000if(MSVC OR MINGW) set(SYSTEM_NAME "Win") if (NOT SUBSYSTEM_NAME) if(CMAKE_SIZEOF_VOID_P MATCHES 8) set(SUBSYSTEM_NAME "x64") else() set(SUBSYSTEM_NAME "x86") ENDIF() endif() set(SYSTEM_DL_EXTENSION ".dll") elseif(APPLE) set(SYSTEM_NAME "Mac") if (NOT SUBSYSTEM_NAME) EXEC_PROGRAM(uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION) string(REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION}) if (DARWIN_VERSION MATCHES 8) set(SUBSYSTEM_NAME "10.4") elseif(DARWIN_VERSION MATCHES 9) set(SUBSYSTEM_NAME "10.5") elseif(DARWIN_VERSION MATCHES 10) set(SUBSYSTEM_NAME "10.6") elseif(DARWIN_VERSION MATCHES 11) set(SUBSYSTEM_NAME "10.7") elseif(DARWIN_VERSION MATCHES 12) set(SUBSYSTEM_NAME "10.8") elseif(DARWIN_VERSION MATCHES 13) set(SUBSYSTEM_NAME "10.9") elseif(DARWIN_VERSION MATCHES 14) set(SUBSYSTEM_NAME "10.10") else() message(FATAL_ERROR "Unsupported DARWIN_VERSION: ${DARWIN_VERSION}") endif() endif() set(SYSTEM_DL_EXTENSION ".dylib") elseif(UNIX) set(SYSTEM_NAME "Linux") if (NOT SUBSYSTEM_NAME) if(CMAKE_SIZEOF_VOID_P MATCHES 8) set(SUBSYSTEM_NAME "x64") else() set(SUBSYSTEM_NAME "x86") endif() endif() set(SYSTEM_DL_EXTENSION ".so") else() MESSAGE(FATAL_ERROR "Unsupported system") endif() if (SYSTEM_NAME MATCHES "Mac") set(PACKAGE_SUFFIX "mac${SUBSYSTEM_NAME}") else() if (SYSTEM_NAME MATCHES "Win") set(PACKAGE_SUFFIX_PREFIX "win") elseif (SYSTEM_NAME MATCHES "Linux") set(PACKAGE_SUFFIX_PREFIX "linux") else() MESSAGE(FATAL_ERROR "Unsupported system") endif() if (SUBSYSTEM_NAME MATCHES "x86") set(PACKAGE_SUFFIX_SUFFIX "32") elseif (SUBSYSTEM_NAME MATCHES "x64") set(PACKAGE_SUFFIX_SUFFIX "64") else() MESSAGE(FATAL_ERROR "Unsupported system") endif() set(PACKAGE_SUFFIX "${PACKAGE_SUFFIX_PREFIX}${PACKAGE_SUFFIX_SUFFIX}") endif() message(STATUS "System-specific folder name: ${SYSTEM_NAME}") message(STATUS "Subsystem-specific folder name: ${SUBSYSTEM_NAME}") macro(LIBRARY_NAME LIBRARY_BASENAME) set(LIBRARY_NAME_RESULT "") if(NOT MSVC AND NOT MINGW) set(LIBRARY_NAME_RESULT "lib") endif() set(LIBRARY_NAME_RESULT ${LIBRARY_NAME_RESULT}${LIBRARY_BASENAME}${SYSTEM_DL_EXTENSION}) set(${LIBRARY_BASENAME}_NAME ${LIBRARY_NAME_RESULT}) endmacro() Indigo-indigo-1.2.3/common/cmake/MacFrameworks.cmake000066400000000000000000000025121271037650300223140ustar00rootroot00000000000000# http://stackoverflow.com/questions/822404/how-to-set-up-cmake-to-build-an-app-for-the-iphone EXEC_PROGRAM(xcodebuild ARGS -version OUTPUT_VARIABLE XCODE_VERSION) string(REGEX MATCH "[0-9][.][0-9]" XCODE_VERSION ${XCODE_VERSION}) if(UNIVERSAL_BUILD) set(SSNAME 10.8) else() set(SSNAME ${SUBSYSTEM_NAME}) endif() if (${XCODE_VERSION} GREATER 4.2) set(FRAMEWORK_PATH /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SSNAME}.sdk/System/Library/Frameworks) else() set(FRAMEWORK_PATH /Developer/SDKs/MacOSX${SSNAME}.sdk/System/Library/Frameworks) endif() macro(ADD_FRAMEWORK fwname appname) set(FRAMEWORK_${fwname} ${FRAMEWORK_PATH}/${fwname}.framework) if(NOT IS_DIRECTORY ${FRAMEWORK_${fwname}}) message(ERROR ": Framework ${fwname} not found") else() target_link_libraries(${appname} ${FRAMEWORK_${fwname}}) message(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") endif() endmacro() macro(FIND_FRAMEWORK fwname) set(FRAMEWORK_${fwname} ${FRAMEWORK_PATH}/${fwname}.framework) if(NOT IS_DIRECTORY ${FRAMEWORK_${fwname}}) message(ERROR ": Framework ${fwname} not found") else() message(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") endif() endmacro() Indigo-indigo-1.2.3/common/cmake/MakeOutputPath.cmake000066400000000000000000000020661271037650300224720ustar00rootroot00000000000000set(COMMON_OUTPUT_PATH dist/${SYSTEM_NAME}/${SUBSYSTEM_NAME}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${COMMON_OUTPUT_PATH}/lib) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${COMMON_OUTPUT_PATH}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${COMMON_OUTPUT_PATH}/lib) message(STATUS "Common output dir: ${CMAKE_BINARY_DIR}/${COMMON_OUTPUT_PATH}") macro(PACK_STATIC proj) install(TARGETS ${proj} DESTINATION static/${SYSTEM_NAME}/${SUBSYSTEM_NAME} COMPONENT static) if(MSVC) get_target_property (output_name ${proj} "OUTPUT_NAME") if (NOT output_name) set(output_name "${proj}") endif () endif(MSVC) endmacro() macro(PACK_SHARED proj) install(TARGETS ${proj} DESTINATION shared/${SYSTEM_NAME}/${SUBSYSTEM_NAME} COMPONENT shared) endmacro() macro(PACK_EXECUTABLE proj) install(TARGETS ${proj} DESTINATION . COMPONENT shared) endmacro() macro(PACK_BINGO proj) install(TARGETS ${proj} DESTINATION "lib" COMPONENT shared) endmacro() Indigo-indigo-1.2.3/common/cmake/SetBuildParameters.cmake000066400000000000000000000116011271037650300233110ustar00rootroot00000000000000set(UNIVERSAL_BUILD FALSE CACHE BOOL "Indigo C++11 universal build without dependency on libstdc++") if(NOT MSVC AND NOT BINGO) set(VISIBILITY_HIDDEN YES) endif() if (NOT CMAKE_BUILD_TYPE) if(NOT BUILD_DEBUG) message(STATUS "Set CMAKE_BUILD_TYPE to Release") set(CMAKE_BUILD_TYPE Release) else() message(STATUS "Set CMAKE_BUILD_TYPE to Debug") set(CMAKE_BUILD_TYPE Debug) endif() endif() message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") if(MSVC) add_definitions(/D "_CRT_SECURE_NO_WARNINGS") if (CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(/Z7) endif() elseif(UNIX AND NOT APPLE OR MINGW) if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(amd64|i.86|powerpc|ppc|sparc|x86_64)") if (SUBSYSTEM_NAME MATCHES "x86") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") elseif(SUBSYSTEM_NAME MATCHES "x64") if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CMAKE_C_FLAGS "-include ${CMAKE_CURRENT_LIST_DIR}/../hacks/gcc_preinclude.h ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-include ${CMAKE_CURRENT_LIST_DIR}/../hacks/gcc_preinclude.h ${CMAKE_CXX_FLAGS}") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") endif() endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") if (DEFINED ENV{UNIVERSAL} OR UNIVERSAL_BUILD) set(CMAKE_C_CREATE_SHARED_LIBRARY "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | gcc | -shared | | | ") set(CMAKE_C_LINK_EXECUTABLE "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | gcc | | | | ") set(CMAKE_CXX_CREATE_SHARED_LIBRARY "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | g++ | -shared | | | ") set(CMAKE_CXX_LINK_EXECUTABLE "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | g++ | | | | ") endif() elseif(APPLE) include(MacFrameworks) set(CMAKE_OSX_ARCHITECTURES "i386;x86_64") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch i386 -arch x86_64 ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch i386 -arch x86_64 ") if(UNIVERSAL_BUILD) # All parameters: -o ; ; set(CMAKE_C_CREATE_SHARED_LIBRARY "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | clang | -dynamiclib | | | ") set(CMAKE_C_LINK_EXECUTABLE "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | clang | | | | ") set(CMAKE_CXX_CREATE_SHARED_LIBRARY "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | clang++ | -dynamiclib | | | ") set(CMAKE_CXX_LINK_EXECUTABLE "python ${CMAKE_CURRENT_LIST_DIR}/linkhack.py | clang++ | | | | ") set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) else() set(CMAKE_OSX_DEPLOYMENT_TARGET ${SUBSYSTEM_NAME}) message(STATUS "Deployment target: ${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() if (UNIVERSAL_BUILD) set(SDK_SUBSYSTEM_NAME 10.8) else() set(SDK_SUBSYSTEM_NAME ${SUBSYSTEM_NAME}) endif() if (${XCODE_VERSION} GREATER 4.2) set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${SDK_SUBSYSTEM_NAME}.sdk) else() set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX${SDK_SUBSYSTEM_NAME}.sdk) endif() message(STATUS "SDK: ${CMAKE_OSX_SYSROOT}") message(STATUS "CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES}") message(STATUS "CMAKE_C_FLAGS ${CMAKE_C_FLAGS}") message(STATUS "CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}") set(COMPILE_FLAGS "${COMPILE_FLAGS}") set(LINK_FLAGS "${LINK_FLAGS} -Wl,-no_compact_unwind") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") endif() if(UNIX OR APPLE) set(COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC") #Set RPATH set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) if(APPLE) set(CMAKE_INSTALL_RPATH "\@loader_path") else() set(CMAKE_INSTALL_RPATH "\$ORIGIN") endif() set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() if(VISIBILITY_HIDDEN) set(COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") endif() Indigo-indigo-1.2.3/common/cmake/linkhack.py000066400000000000000000000146461271037650300207220ustar00rootroot00000000000000#!/usr/bin/python import subprocess import os import shutil import sys import platform def getSymbols(libPath): stderr = None if not 'VERBOSE' in os.environ: stderr = subprocess.PIPE return [item.replace(' ', '').split(' ') for item in subprocess.Popen('nm %s' % (libPath), shell=True, stdout=subprocess.PIPE, stderr=stderr).communicate()[0].split('\n')] def getIndigoStdSyms(libRoot): libname = 'libc++.a' if not platform.mac_ver()[0]: libname = 'libstdc++.a' libstdcppSymbols = getSymbols(os.path.join(libRoot, libname)) renameSymbols = [] invMap = {} for item in libstdcppSymbols: if len(item) < 2: continue if item[1] != 'U' and item[2].find('pthread') == -1: newName = '_ind_' + item[2] if newName in invMap: if invMap[newName] != item[2]: exit('Duplicate symbol: %s for %s and %s' % (newName, invMap[newName], item[2])) else: invMap[newName] = item[2] renameSymbols.append((item[2], newName)) f = open('indigostd.syms', 'w') for item in renameSymbols: f.write('%s %s\n' % (item[0], item[1])) f.close() def linux(compiler, linkFlags, objFiles, linkLibraries, target): libstdcppPath = subprocess.Popen('g++ -print-file-name=libstdc++.a', shell=True, stdout=subprocess.PIPE).communicate()[0].replace('\n', '') # Find dist root libRoot = os.path.dirname(target) if not os.path.exists(libRoot): os.makedirs(libRoot) if not os.path.exists(libRoot): sys.exit("Cannot create or find a directory with library files") shutil.copy(libstdcppPath, libRoot + '/libstdc++.a') getIndigoStdSyms(libRoot) for objFile in objFiles: subprocess.call('objcopy --redefine-syms indigostd.syms %s' % (objFile), shell=True) for libname in ('libindigo.so', 'bingo_postgres.so', 'libbingo-oracle.so', 'libketcher-server.so', 'indigo-cano', 'indigo-deco', 'indigo-depict', 'rindigo.so'): if target.find(libname) != -1: subprocess.call('objcopy --redefine-syms indigostd.syms %s/libstdc++.a %s/libindigostdcpp.a' % (libRoot, libRoot), shell=True) linkLibraries = linkLibraries + ' -Wl,--whole-archive %s/libindigostdcpp.a -Wl,--no-whole-archive ' % (libRoot) break os.remove('%s/libstdc++.a' % (libRoot)) for library in os.listdir(libRoot): if not library.endswith('.a') or library == 'libindigostdcpp.a': continue symlist = [] for s in getSymbols(os.path.join(libRoot, library)): if len(s) > 1: symlist.append(s[2].find('_ind')) if 0 in symlist: continue libFile = os.path.join(libRoot, library) subprocess.call('objcopy --redefine-syms indigostd.syms %s' % (libFile), shell=True) linkCommand = '%s -v -L%s/ -static-libstdc++ %s %s %s -o %s' % (compiler, libRoot, linkFlags, ' '.join(objFiles), linkLibraries, target) stderr = None stdout = None if 'VERBOSE' in os.environ: print(linkCommand) stderr = subprocess.PIPE stdout = subprocess.PIPE subprocess.call(linkCommand, shell=True, stderr=stderr, stdout=stdout) def mac(compiler, linkFlags, objFiles, linkLibraries, target): def lipoObjconvLipo(binaryFile): subprocess.call('lipo -thin x86_64 %s -o %s' % (binaryFile, binaryFile + '.tmp.64'), shell=True) subprocess.call('lipo -thin i386 %s -o %s' % (binaryFile, binaryFile + '.tmp.32'), shell=True) os.remove(binaryFile) command = 'objconv -v0 -wd1214 -wd1106 -fmacho64 -nf:indigostd.syms %s %s' % (binaryFile + '.tmp.64', binaryFile + '.64') stderr=None if 'VERBOSE' in os.environ: print(command) stderr = subprocess.PIPE subprocess.call(command, shell=True, stderr=stderr) command = 'objconv -v0 -wd1214 -wd1106 -fmacho32 -nf:indigostd.syms %s %s' % (binaryFile + '.tmp.32', binaryFile + '.32') if 'VERBOSE' in os.environ: print(command) subprocess.call(command, shell=True, stderr=sterr) command = 'lipo -create %s %s -output %s' % (binaryFile + '.64', binaryFile + '.32', binaryFile) if 'VERBOSE' in os.environ: print(command) subprocess.call(command, shell=True, stderr=stderr) os.remove(binaryFile + '.tmp.32') os.remove(binaryFile + '.tmp.64') os.remove(binaryFile + '.32') os.remove(binaryFile + '.64') libRoot = os.path.dirname(target) shutil.copy('/usr/lib/libc++.a', libRoot + '/libc++.a') getIndigoStdSyms(libRoot) for objFile in objFiles: lipoObjconvLipo(objFile) for libname in ('libindigo.dylib', 'bingo_postgres.dylib', 'libketcher-server.dylib', 'indigo-cano', 'indigo-deco', 'indigo-depict'): if target.find(libname) != -1: lipoObjconvLipo(libRoot + '/libc++.a') linkLibraries = linkLibraries + ' -Wl,-all_load %s/libc++.a -Wl,-noall_load' % (libRoot) break for library in os.listdir(libRoot): if not library.endswith('.a') or library == 'libindigoc++.a': continue symlist = [] for s in getSymbols(os.path.join(libRoot, library)): if len(s) > 1: symlist.append(s[2].find('_ind')) if 0 in symlist: continue lipoObjconvLipo(os.path.join(libRoot, library)) cmd = ('%s -L%s/ -arch i386 -arch x86_64 -undefined dynamic_lookup -nodefaultlibs -lc -lm -std=c++11 -mmacosx-version-min=10.7 %s %s %s -o %s' % (compiler, libRoot, linkFlags, ' '.join(objFiles), linkLibraries, target)) stderr=None if 'VERBOSE' in os.environ: print(cmd) stderr = subprocess.PIPE subprocess.call(cmd, shell=True, stderr=stderr) def main(): args = ' '.join(sys.argv).split('|') print args compiler = args[1] linkFlags = args[2] objFiles = filter(len, args[3].split(' ')) linkLibraries = args[4].strip() target = args[5].strip() if platform.mac_ver()[0]: mac(compiler, linkFlags, objFiles, linkLibraries, target) else: linux(compiler, linkFlags, objFiles, linkLibraries, target) if __name__ == '__main__': main() Indigo-indigo-1.2.3/common/gzip/000077500000000000000000000000001271037650300164425ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/gzip/gzip_output.cpp000066400000000000000000000055731271037650300215510ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "gzip/gzip_output.h" using namespace indigo; IMPL_ERROR(GZipOutput, "GZip output"); CP_DEF(GZipOutput); GZipOutput::GZipOutput (Output &dest, int level) : _dest(dest), CP_INIT, TL_CP_GET(_outbuf), TL_CP_GET(_inbuf) { _zstream.zalloc = Z_NULL; _zstream.zfree = Z_NULL; _zstream.opaque = Z_NULL; _zstream.next_in = Z_NULL; _zstream.avail_in = 0; int rc = deflateInit2(&_zstream, level, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY); if (rc == Z_VERSION_ERROR) throw Error("zlib version incompatible"); if (rc == Z_MEM_ERROR) throw Error("not enough memory for zlib"); if (rc == Z_STREAM_ERROR) throw Error("invalid parameter given to zlib"); if (rc != Z_OK) throw Error("unknown zlib error code: %d", rc); _outbuf.clear_resize(CHUNK_SIZE); _inbuf.clear_resize(CHUNK_SIZE); _total_written = 0; } GZipOutput::~GZipOutput () { _zstream.avail_in = 0; _zstream.next_in = Z_NULL; int rc = _deflate(Z_FINISH); if (rc != Z_STREAM_END) return;//throw Error("unexpected zlib error (%d)", rc); deflateEnd(&_zstream); } void GZipOutput::write (const void *data, int size) { if (size < 1) return; _zstream.avail_in = size; _zstream.next_in = (Bytef *)data; do { _deflate(Z_NO_FLUSH); } while (_zstream.avail_out == 0); if (_zstream.avail_in != 0) throw Error("some data left uncompressed unexpectedly"); } void GZipOutput::flush () { _zstream.avail_in = 0; _zstream.next_in = Z_NULL; _deflate(Z_FULL_FLUSH); _dest.flush(); } int GZipOutput::_deflate (int flush) { _zstream.avail_out = _outbuf.size(); _zstream.next_out = _outbuf.ptr(); int rc = deflate(&_zstream, flush); if (rc == Z_STREAM_ERROR) throw Error("inconsistent zlib stream state"); if (rc == Z_BUF_ERROR) throw Error("Z_BUF_ERROR (workaround not implemented)"); if (rc != Z_STREAM_END && rc != Z_OK) throw Error("unexpected zlib error (%d)", rc); int n = _outbuf.size() - _zstream.avail_out; if (n > 0) { _dest.write(_outbuf.ptr(), n); _total_written += n; } return rc; } int GZipOutput::tell () { return _total_written; } void GZipOutput::seek (int offset, int from) { throw Error("not imlemented"); } Indigo-indigo-1.2.3/common/gzip/gzip_output.h000066400000000000000000000024451271037650300212110ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __gzip_output__ #define __gzip_output__ #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include namespace indigo { class GZipOutput : public Output { public: enum { CHUNK_SIZE = 32768 }; explicit GZipOutput (Output &dest, int level); virtual ~GZipOutput (); virtual void write (const void *data, int size); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); DECL_ERROR; protected: Output &_dest; z_stream _zstream; int _total_written; int _deflate (int flush); CP_DECL; TL_CP_DECL(Array, _outbuf); TL_CP_DECL(Array, _inbuf); }; } #endif Indigo-indigo-1.2.3/common/gzip/gzip_scanner.cpp000066400000000000000000000114061271037650300216320ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "gzip/gzip_scanner.h" using namespace indigo; IMPL_ERROR(GZipScanner, "GZip scanner"); CP_DEF(GZipScanner); GZipScanner::GZipScanner (Scanner &source) : _source(source), CP_INIT, TL_CP_GET(_inbuf), TL_CP_GET(_outbuf) { _zstream.zalloc = Z_NULL; _zstream.zfree = Z_NULL; _zstream.opaque = Z_NULL; _zstream.avail_in = 0; _zstream.next_in = Z_NULL; int rc = inflateInit2(&_zstream, 16 + MAX_WBITS); if (rc == Z_VERSION_ERROR) throw Error("zlib version incompatible"); if (rc == Z_MEM_ERROR) throw Error("not enough memory for zlib"); if (rc != Z_OK) throw Error("unknown zlib error code: %d", rc); _outbuf.clear_resize(CHUNK_SIZE); _inbuf.clear_resize(CHUNK_SIZE); _outbuf_start = 0; _inbuf_end = 0; _uncompressed_total = 0; _eof = false; _zstream.avail_out = _outbuf.size(); _zstream.next_out = _outbuf.ptr(); } GZipScanner::~GZipScanner () { inflateEnd(&_zstream); } void GZipScanner::read (int length, void *res) { if (res == 0) throw Error("zero pointer given"); if (_outbuf_start < _outbuf.size() - (int)_zstream.avail_out) { int n = __min(length, CHUNK_SIZE - (int)_zstream.avail_out - _outbuf_start); memcpy(res, _outbuf.ptr() + _outbuf_start, n); _outbuf_start += n; _uncompressed_total += n; length -= n; res = (char *)res + n; } if (!_read(length, res)) throw Error("end of compressed data"); } bool GZipScanner::_read (int length, void *res) { while (length > 0) { if (_eof) return false; if (_zstream.avail_in == 0) { _inbuf_end = 0; if (_source.isEOF()) throw Error("end of file in source stream"); do { _inbuf[_inbuf_end++] = _source.readChar(); } while (!_source.isEOF() && _inbuf_end < _inbuf.size()); _zstream.avail_in = _inbuf_end; _zstream.next_in = _inbuf.ptr(); } _zstream.avail_out = _outbuf.size(); _zstream.next_out = _outbuf.ptr(); _outbuf_start = 0; int rc = inflate(&_zstream, Z_NO_FLUSH); if (rc == Z_STREAM_ERROR) throw Error("inconsistent stream structure"); if (rc == Z_NEED_DICT) { inflateEnd(&_zstream); throw Error("need a dictionary"); } if (rc == Z_MEM_ERROR) { inflateEnd(&_zstream); throw Error("not enough memory"); } if (rc == Z_DATA_ERROR) { inflateEnd(&_zstream); throw Error("corrupted input data"); } if (rc == Z_BUF_ERROR) throw Error("Z_BUF_ERROR (workaround not implemented)"); if (rc != Z_OK && rc != Z_STREAM_END) throw Error("unknown zlib error code: %d", rc); int n = __min(length, CHUNK_SIZE - (int)_zstream.avail_out - _outbuf_start); if (n > 0 && res != 0) { memcpy(res, _outbuf.ptr(), n); _outbuf_start += n; _uncompressed_total += n; res = (char *)res + n; } length -= n; if (rc == Z_STREAM_END) _eof = true; } return true; } void GZipScanner::readAll (Array &arr) { arr.clear(); while (!isEOF()) arr.push(readByte()); } void GZipScanner::skip (int length) { QS_DEF(Array, dummy); dummy.clear_resize(length); read(length, dummy.ptr()); } int GZipScanner::tell () { return _uncompressed_total; } bool GZipScanner::isEOF () { if ((unsigned)_outbuf_start + _zstream.avail_out == (unsigned)_outbuf.size()) { if (_eof) return true; _read(1, 0); } return _eof && (unsigned)_outbuf_start + _zstream.avail_out == (unsigned)_outbuf.size(); } void GZipScanner::seek (int pos, int from) { throw Error("not implemented"); } int GZipScanner::lookNext () { if (_outbuf_start < _outbuf.size() - (int)_zstream.avail_out) return _outbuf[_outbuf_start]; if (_eof) return -1; _read(1, 0); if (_eof) return -1; if (_outbuf_start < _outbuf.size() - (int)_zstream.avail_out) return _outbuf[_outbuf_start]; throw Error("internal"); } int GZipScanner::length () { throw Error("not implemented"); } Indigo-indigo-1.2.3/common/gzip/gzip_scanner.h000066400000000000000000000027571271037650300213100ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __gzip_scanner__ #define __gzip_scanner__ #include "base_cpp/scanner.h" #include "base_cpp/tlscont.h" #include namespace indigo { class GZipScanner : public Scanner { public: enum { CHUNK_SIZE = 32768 }; explicit GZipScanner (Scanner &source); virtual ~GZipScanner (); virtual void read (int length, void *res); virtual int tell (); virtual bool isEOF (); virtual void seek (int pos, int from); virtual int lookNext (); virtual void skip (int length); virtual int length (); virtual void readAll (Array &arr); DECL_ERROR; protected: Scanner &_source; z_stream _zstream; bool _read (int length, void *res); CP_DECL; TL_CP_DECL(Array, _inbuf); TL_CP_DECL(Array, _outbuf); int _outbuf_start; int _inbuf_end; int _uncompressed_total; bool _eof; }; } #endif Indigo-indigo-1.2.3/common/hacks/000077500000000000000000000000001271037650300165625ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/hacks/gcc_preinclude.h000066400000000000000000000016011271037650300216770ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ /* http://rjpower.org/wordpress/fun-with-shared-libraries-version-glibc_2-14-not-found/ */ #ifndef __gcc_preinclude_h__ #define __gcc_preinclude_h__ #if defined(__GLIBC__) && defined(__x86_64__) __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); #endif #endif Indigo-indigo-1.2.3/common/hacks/memcpy.c000066400000000000000000000002211271037650300202130ustar00rootroot00000000000000#include #include "gcc_preinclude.h" void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memmove(dest, src, n); }Indigo-indigo-1.2.3/common/java/000077500000000000000000000000001271037650300164125ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/000077500000000000000000000000001271037650300215435ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/build.xml000066400000000000000000000067451271037650300234000ustar00rootroot00000000000000 Builds, tests, and runs the project common-controls. Indigo-indigo-1.2.3/common/java/common-controls/compile.bat000066400000000000000000000011061271037650300236610ustar00rootroot00000000000000@echo off if "%JAVA_HOME%"=="" goto jDef1 goto jDef2 :jDef1 echo JAVA_HOME is not defined will use 'javac' and 'jar' commands set EXEC_JAVAC=javac set EXEC_JAR=jar goto jDefEx :jDef2 set EXEC_JAVAC="%JAVA_HOME%\bin\javac" set EXEC_JAR="%JAVA_HOME%\bin\jar" :jDefEx mkdir dist cd src %EXEC_JAVAC% -cp ../../../../api/java/dist/indigo.jar;../../../../api/renderer/java/dist/indigo-renderer.jar com/epam/indigo/controls/*.java %EXEC_JAR% cvf ../dist/common-controls.jar com/epam/indigo/controls/*.class com/epam/indigo/controls/images/* del /Q com\epam\indigo\controls\*.class cd .. Indigo-indigo-1.2.3/common/java/common-controls/compile.sh000077500000000000000000000004601271037650300235320ustar00rootroot00000000000000mkdir -p dist cd src javac -cp ../../../../api/java/dist/indigo.jar:../../../../api/renderer/java/dist/indigo-renderer.jar com/epam/indigo/controls/*.java jar cvf ../dist/common-controls.jar com/epam/indigo/controls/*.class com/epam/indigo/controls/images/* rm -f com/epam/indigo/controls/*.class cd .. Indigo-indigo-1.2.3/common/java/common-controls/nbproject/000077500000000000000000000000001271037650300235315ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/nbproject/build-impl.xml000066400000000000000000002261071271037650300263210ustar00rootroot00000000000000 Must set src.src.dir Must set build.dir Must set dist.dir Must set build.classes.dir Must set dist.javadoc.dir Must set build.test.classes.dir Must set build.test.results.dir Must set build.classes.excludes Must set dist.jar Must set javac.includes No tests executed. Must set JVM to use for profiling in profiler.info.jvm Must set profiler agent JVM arguments in profiler.info.jvmargs.agent Must select some files in the IDE or set javac.includes To run this application from the command line without Ant, try: java -jar "${dist.jar.resolved}" Must select one file in the IDE or set run.class Must select one file in the IDE or set run.class Must select one file in the IDE or set debug.class Must select one file in the IDE or set debug.class Must set fix.includes This target only works when run from inside the NetBeans IDE. Must select one file in the IDE or set profile.class This target only works when run from inside the NetBeans IDE. This target only works when run from inside the NetBeans IDE. This target only works when run from inside the NetBeans IDE. Must select one file in the IDE or set run.class Must select some files in the IDE or set test.includes Must select one file in the IDE or set run.class Must select one file in the IDE or set applet.url Must select some files in the IDE or set javac.includes Some tests failed; see details above. Must select some files in the IDE or set test.includes Some tests failed; see details above. Must select some files in the IDE or set test.class Must select some method in the IDE or set test.method Some tests failed; see details above. Must select one file in the IDE or set test.class Must select one file in the IDE or set test.class Must select some method in the IDE or set test.method Must select one file in the IDE or set applet.url Must select one file in the IDE or set applet.url Indigo-indigo-1.2.3/common/java/common-controls/nbproject/genfiles.properties000066400000000000000000000007231271037650300274450ustar00rootroot00000000000000build.xml.data.CRC32=f5a760a9 build.xml.script.CRC32=af902d6a build.xml.stylesheet.CRC32=8064a381@1.75.2.48 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=f5a760a9 nbproject/build-impl.xml.script.CRC32=666c6099 nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 Indigo-indigo-1.2.3/common/java/common-controls/nbproject/project.properties000066400000000000000000000046731271037650300273270ustar00rootroot00000000000000annotation.processing.enabled=true annotation.processing.enabled.in.editor=false annotation.processing.processors.list= annotation.processing.run.all.processors=true annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output application.homepage=http://epam.com/ application.splash= application.title=common-controls application.vendor=EPAM Systems build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated build.generated.sources.dir=${build.dir}/generated-sources # Only compile against the classpath explicitly listed here: build.sysclasspath=ignore build.test.classes.dir=${build.dir}/test/classes build.test.results.dir=${build.dir}/test/results # Uncomment to specify the preferred debugger connection transport: #debug.transport=dt_socket debug.classpath=\ ${run.classpath} debug.test.classpath=\ ${run.test.classpath} # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/common-controls.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= file.reference.indigo-renderer.jar=..\\..\\..\\dist\\indigo-java\\indigo-renderer.jar file.reference.indigo.jar=..\\..\\..\\dist\\indigo-java\\indigo.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=false jar.index=${jnlp.enabled} javac.classpath=\ ${file.reference.indigo-renderer.jar}:\ ${file.reference.indigo.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false javac.processorpath=\ ${javac.classpath} javac.source=1.5 javac.target=1.5 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir} javac.test.processorpath=\ ${javac.test.classpath} javadoc.additionalparam= javadoc.author=false javadoc.encoding=${source.encoding} javadoc.noindex=false javadoc.nonavbar=false javadoc.notree=false javadoc.private=false javadoc.splitindex=true javadoc.use=true javadoc.version=false javadoc.windowtitle= jnlp.codebase.type=no.codebase jnlp.descriptor=application jnlp.enabled=false jnlp.mixed.code=defaut jnlp.offline-allowed=false jnlp.signed=false meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false platform.active=default_platform run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} run.test.classpath=\ ${javac.test.classpath}:\ ${build.test.classes.dir} source.encoding=UTF-8 src.src.dir=src Indigo-indigo-1.2.3/common/java/common-controls/nbproject/project.xml000066400000000000000000000010171271037650300257200ustar00rootroot00000000000000 org.netbeans.modules.java.j2seproject common-controls Indigo-indigo-1.2.3/common/java/common-controls/src/000077500000000000000000000000001271037650300223325ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/000077500000000000000000000000001271037650300231105ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/000077500000000000000000000000001271037650300240325ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/000077500000000000000000000000001271037650300253035ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/000077500000000000000000000000001271037650300271465ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/Announcer.java000066400000000000000000000044341271037650300317460ustar00rootroot00000000000000/* * http://nat.truemesh.com/archives/000710.html * Announcer class for event notification. * "I usually just copy it into whatever project I'm writing and * move it into one of the project's packages. Feel free to do the same." */ package com.epam.indigo.controls; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.EventListener; import java.util.List; public class Announcer { private final T proxy; private final List listeners = new ArrayList(); public Announcer (Class listenerType) { proxy = listenerType.cast(Proxy.newProxyInstance( listenerType.getClassLoader(), new Class[] { listenerType }, new InvocationHandler() { public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { announce(method, args); return null; } })); } public void addListener (T listener) { listeners.add(listener); } public void removeListener (T listener) { listeners.remove(listener); } public T announce () { return proxy; } private void announce (Method m, Object[] args) { try { for (T listener : listeners) { m.invoke(listener, args); } } catch (IllegalAccessException e) { throw new IllegalArgumentException("could not invoke listener", e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw new UnsupportedOperationException("listener threw exception", cause); } } } public static Announcer to (Class listenerType) { return new Announcer(listenerType); } }Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/CenteredTextPane.java000066400000000000000000000042231271037650300332140ustar00rootroot00000000000000package com.epam.indigo.controls; import javax.swing.JTextPane; import javax.swing.text.AbstractDocument; import javax.swing.text.BoxView; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledEditorKit; import javax.swing.text.View; import javax.swing.text.ViewFactory; public class CenteredTextPane extends JTextPane { public CenteredTextPane () { setEditorKit(new VerticallyCenteredEditorKit()); // Set horizontal center alignment SimpleAttributeSet attrib_set = new SimpleAttributeSet(); StyleConstants.setAlignment(attrib_set, StyleConstants.ALIGN_CENTER); setParagraphAttributes(attrib_set, false); } /* Centered editor kit for text pane */ class VerticallyCenteredEditorKit extends StyledEditorKit { @Override public ViewFactory getViewFactory () { return new CenteredViewFactory(super.getViewFactory()); } class CenteredViewFactory implements ViewFactory { private ViewFactory reference_view; public CenteredViewFactory (ViewFactory reference_view) { this.reference_view = reference_view; } public View create (Element elem) { if (elem.getName().equals(AbstractDocument.SectionElementName)) return new CenteredBoxView(elem, View.Y_AXIS); else return reference_view.create(elem); } } } /* Box view with center alignment */ class CenteredBoxView extends BoxView { public CenteredBoxView (Element elem, int axis) { super(elem, axis); } @Override protected void layoutMajorAxis (int target_span, int axis, int[] offsets, int[] spans) { super.layoutMajorAxis(target_span, axis, offsets, spans); int text_block_size = 0; int offset = 0; for (int i = 0; i < spans.length; i++) text_block_size += spans[i]; offset = (target_span - text_block_size) / 2; for (int i = 0; i < offsets.length; i++) offsets[i] += offset; } } } Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/CommonUtils.java000066400000000000000000000030511271037650300322610ustar00rootroot00000000000000/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import java.net.URL; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JOptionPane; /** * * @author achurinov */ public class CommonUtils { public static IndigoObject getIterator (Indigo indigo, String path) { IndigoObject iterator; if (path.endsWith(".sdf") || path.endsWith(".sd") || path.endsWith(".mol")) iterator = indigo.iterateSDFile(path); else if (path.endsWith(".rdf")) iterator = indigo.iterateRDFile(path); else if (path.endsWith(".smi")) iterator = indigo.iterateSmilesFile(path); else if (path.endsWith(".cml")) iterator = indigo.iterateCMLFile(path); else throw new RuntimeException("Unsupported file extension"); return iterator; } public static void showAboutDialog (JFrame parent, String product, String url) { StringBuilder sb = new StringBuilder(); sb.append(String.format("%s
", product)); sb.append(String.format("Indigo version: %s
", (new Indigo()).version())); sb.append("(C) 2010-2013 EPAM Systems
"); sb.append(String.format("%s", url, url)); URL icon_url = CommonUtils.class.getResource("images/gga-logo.png"); MessageBox.showHtml(parent, sb.toString(), "About", new ImageIcon(icon_url)); } } Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/FileOpener.java000066400000000000000000000026571271037650300320530ustar00rootroot00000000000000package com.epam.indigo.controls; import java.io.File; import java.io.FileFilter; import javax.swing.JFileChooser; import javax.swing.JFrame; public class FileOpener { private JFileChooser _file_chooser = new JFileChooser(); private File _file = null; public FileOpener() { _file_chooser.setAcceptAllFileFilterUsed(false); } public void addExtension( String... extensions ) { MolFileFilter mon_ff = new MolFileFilter(); for (String ext : extensions) mon_ff.addExtension(ext); _file_chooser.addChoosableFileFilter(mon_ff); } public String openFile( String approve_button_text ) { _file_chooser.setCurrentDirectory(new File(GlobalParams.getInstance().dir_path)); _file_chooser.setApproveButtonText(approve_button_text); int ret_val = _file_chooser.showDialog(_file_chooser, approve_button_text); _file = _file_chooser.getSelectedFile(); if ((_file == null) || (ret_val != JFileChooser.APPROVE_OPTION)) return null; GlobalParams.getInstance().dir_path = _file.getPath(); return _file.getPath(); } public File getFile() { return _file; } public String getFilePath() { if (_file == null) return null; return _file.getPath(); } public MolFileFilter getFileFilter() { if (_file == null) return null; return (MolFileFilter)_file_chooser.getFileFilter(); } } Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/Global.java000066400000000000000000000005101271037650300312050ustar00rootroot00000000000000package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoRenderer; public class Global { public static final Indigo indigo; public static final IndigoRenderer indigo_renderer; static { indigo = new Indigo(); indigo_renderer = new IndigoRenderer(indigo); } } Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/GlobalParams.java000066400000000000000000000012441271037650300323560ustar00rootroot00000000000000/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.epam.indigo.controls; import java.io.File; public class GlobalParams { // TODO: save config and options public String dir_path; private volatile static GlobalParams _instance; private GlobalParams() { dir_path = (new File("")).getAbsolutePath(); } public static GlobalParams getInstance() { if (_instance == null) { synchronized (GlobalParams.class) { if (_instance == null) _instance = new GlobalParams(); } } return _instance; } } IndigoCheckedException.java000066400000000000000000000005161271037650300342730ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controlspackage com.epam.indigo.controls; // Checked exception that must be handled explicitly public class IndigoCheckedException extends Exception { public IndigoCheckedException (String message) { super(message); } public IndigoCheckedException (String message, Throwable cause) { super(message, cause); } } Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/IndigoIteratorItem.java000066400000000000000000000014311271037650300335520ustar00rootroot00000000000000package com.epam.indigo.controls; import com.epam.indigo.IndigoException; import com.epam.indigo.IndigoObject; public class IndigoIteratorItem implements IndigoObjectWrapper { private final int _index; private final IndigoObject _iterator; public IndigoIteratorItem(IndigoObject iterator, int idx) { _iterator = iterator; _index = idx; } private IndigoObject _getIndigoObjectCopy() { synchronized (_iterator.getIndigo()) { return _iterator.at(_index).clone(); } } public IndigoObject getObjectCopy() throws IndigoCheckedException { try { return _getIndigoObjectCopy(); } catch (IndigoException ex) { throw new IndigoCheckedException(ex.getMessage(), ex); } } } IndigoObjectViewPanel.java000066400000000000000000000074431271037650300341150ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controlspackage com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.io.ByteArrayInputStream; import javax.imageio.ImageIO; import javax.imageio.stream.MemoryCacheImageInputStream; import javax.swing.JOptionPane; import javax.swing.JPanel; public class IndigoObjectViewPanel extends JPanel { private BufferedImage image; private ImageIO image_io; private Indigo indigo; private IndigoRenderer indigo_renderer; private IndigoObject chem_obj; private String error_string = null; int image_w; int image_h; public IndigoObjectViewPanel () { setBackground(new java.awt.Color(255, 255, 255)); setImageSize(getWidth(), getHeight()); } public void renderImage () { try { byte[] bytes; final Indigo indigo_sync = indigo; synchronized (indigo_sync) { indigo.setOption("render-output-format", "png"); indigo.setOption("render-comment-font-size", "14"); indigo.setOption("render-background-color", "1,1,1"); indigo.setOption("render-coloring", "1"); indigo.setOption("render-image-size", image_w, image_h); try { bytes = indigo_renderer.renderToBuffer(chem_obj); } catch (Exception ex) { image = null; return; } } ByteArrayInputStream bytes_is; bytes_is = new ByteArrayInputStream(bytes, 0, bytes.length); image = image_io.read(new MemoryCacheImageInputStream(bytes_is)); updateUI(); } catch (Exception ex) { JOptionPane msg_box = new JOptionPane(); msg_box.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); setErrorString(ex.getMessage()); } } public void setIndigoObject (IndigoObject chem_obj, IndigoRenderer indigo_renderer) { error_string = null; if (chem_obj == null) { image = null; this.chem_obj = null; return; } this.indigo = chem_obj.getIndigo(); this.indigo_renderer = indigo_renderer; this.chem_obj = chem_obj; renderImage(); } public void update () { revalidate(); repaint(); } public void setImageSize (int width, int height) { image_w = width; image_h = height; } public class ImageObs implements ImageObserver { @SuppressWarnings("static-access") public boolean imageUpdate (Image img, int infoflags, int x, int y, int width, int height) { return true; } } public void setErrorString (String str) { error_string = new String(str); update(); } public String getErrorString () { return error_string; } @Override public void paintComponent (Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); if (image != null) { g.setColor(Color.white); if ((image_w != getWidth()) || (image_h != getHeight())) { setImageSize(getWidth(), getHeight()); renderImage(); } double new_im_h = image.getHeight(); double new_im_w = image.getWidth(); g.drawImage(image, getWidth() / 2 - (int) new_im_w / 2, getHeight() / 2 - (int) new_im_h / 2, (int) new_im_w, (int) new_im_h, new ImageObs()); } if (error_string != null) { g.setColor(Color.black); g.drawString(error_string, 10, (int)getHeight() / 2); } } } IndigoObjectWithPropertiesViewPanel.form000066400000000000000000000150001271037650300370340ustar00rootroot00000000000000Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls
<Editor/> <Renderer/> </Column> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> </TableColumnModel> </Property> <Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor"> <JTableSelectionModel selectionMode="0"/> </Property> <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> <TableHeader reorderingAllowed="false" resizingAllowed="true"/> </Property> </Properties> </Component> </SubComponents> </Container> </SubComponents> </Container> </SubComponents> </Form>IndigoObjectWithPropertiesViewPanel.java������������������������������������������������������������0000664�0000000�0000000�00000015254�12710376503�0037025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * IndigoObjectWithPropertiesViewPanel.java * * Created on May 10, 2011, 2:55:54 AM */ package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; import javax.swing.DefaultCellEditor; import javax.swing.JTextField; import javax.swing.table.DefaultTableModel; /** * * @author rybalkin */ public class IndigoObjectWithPropertiesViewPanel extends javax.swing.JPanel { private IndigoRenderer _indigo_renderer; private IndigoObject _obj; /** Creates new form IndigoObjectWithPropertiesViewPanel */ public IndigoObjectWithPropertiesViewPanel () { initComponents(); message_field.setVisible(false); } public void setDividerLocation (int position) { splitter.setDividerLocation(position); } public int getDividerLocation () { return splitter.getDividerLocation(); } public void setInformationMessage (String message) { message_field.setText(message); message_field.setVisible(message != null && !message.equals("")); } public String getInformationMessage () { return message_field.getText(); } /** Creates new form CellFrame */ public void setObject (IndigoObject chem_obj, IndigoRenderer indigo_renderer) { this._indigo_renderer = indigo_renderer; this._obj = chem_obj; if (_obj != null && !_obj.hasCoord()) { _obj = chem_obj.clone(); _obj.layout(); } mol_view_panel.setIndigoObject(_obj, _indigo_renderer); _fillPropertyTable(); mol_view_panel.setSize(mol_view_panel.getPreferredSize()); // Set table properties to readonly JTextField tx = new JTextField(); tx.setEditable(false); property_table.setDefaultEditor(String.class, new DefaultCellEditor(tx)); } public String getSelectedPropertyName () { int sel_row = getSelectedPropertyIndex(); if (sel_row == -1) return null; return (String)property_table.getValueAt(sel_row, 0); } public int getSelectedPropertyIndex () { return property_table.getSelectedRow(); } public void setSelectedPropertyIndex (int index) { property_table.setRowSelectionInterval(index, index); } public int getPropertiesCount () { return property_table.getRowCount(); } public void addCustomProperty (int where, String prop_name, String prop_value) { DefaultTableModel model = (DefaultTableModel)property_table.getModel(); model.insertRow(where, new String[] { prop_name, prop_value }); } private void _fillPropertyTable () { DefaultTableModel model = (DefaultTableModel)property_table.getModel(); while (model.getRowCount() != 0) model.removeRow(0); if (_obj == null) return; for (IndigoObject prop : _obj.iterateProperties()) { String prop_name = prop.name(); String prop_value = _obj.getProperty(prop.name()); model.addRow(new String[] { prop_name, prop_value }); } } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { splitter = new javax.swing.JSplitPane(); subpanel = new javax.swing.JPanel(); mol_view_panel = new com.epam.indigo.controls.IndigoObjectViewPanel(); message_field = new javax.swing.JTextField(); property_scroll_panel = new javax.swing.JScrollPane(); property_table = new javax.swing.JTable(); splitter.setBorder(null); splitter.setDividerLocation(200); splitter.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); splitter.setResizeWeight(0.8); splitter.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); subpanel.setLayout(new javax.swing.BoxLayout(subpanel, javax.swing.BoxLayout.PAGE_AXIS)); mol_view_panel.setPreferredSize(new java.awt.Dimension(400, 1960)); javax.swing.GroupLayout mol_view_panelLayout = new javax.swing.GroupLayout(mol_view_panel); mol_view_panel.setLayout(mol_view_panelLayout); mol_view_panelLayout.setHorizontalGroup( mol_view_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 400, Short.MAX_VALUE) ); mol_view_panelLayout.setVerticalGroup( mol_view_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 180, Short.MAX_VALUE) ); subpanel.add(mol_view_panel); message_field.setEditable(false); subpanel.add(message_field); splitter.setLeftComponent(subpanel); property_table.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { }, new String [] { "Property name", "Value" } ) { Class[] types = new Class [] { java.lang.String.class, java.lang.String.class }; public Class getColumnClass(int columnIndex) { return types [columnIndex]; } }); property_table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); property_table.getTableHeader().setReorderingAllowed(false); property_scroll_panel.setViewportView(property_table); splitter.setBottomComponent(property_scroll_panel); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(splitter, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(splitter, javax.swing.GroupLayout.DEFAULT_SIZE, 309, Short.MAX_VALUE) ); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField message_field; private com.epam.indigo.controls.IndigoObjectViewPanel mol_view_panel; private javax.swing.JScrollPane property_scroll_panel; private javax.swing.JTable property_table; private javax.swing.JSplitPane splitter; private javax.swing.JPanel subpanel; // End of variables declaration//GEN-END:variables } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������IndigoObjectWrapper.java����������������������������������������������������������������������������0000664�0000000�0000000�00000000403�12710376503�0033630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; public interface IndigoObjectWrapper { // This method can throw an exception because it might load molecule from file IndigoObject getObjectCopy() throws IndigoCheckedException; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������IndigoObjectsFileLoader.java������������������������������������������������������������������������0000664�0000000�0000000�00000003033�12710376503�0034403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.swing.SwingWorker; public class IndigoObjectsFileLoader extends SwingWorker<List<IndigoObjectWrapper>, Void> { private Indigo indigo; private File file; private boolean useProxyObjects = true; public IndigoObjectsFileLoader (Indigo indigo, File file) { this.indigo = indigo; this.file = file; } public void setUseProxyObject (boolean useProxyObjects) { this.useProxyObjects = useProxyObjects; } @Override protected List<IndigoObjectWrapper> doInBackground () throws Exception { IndigoObject iterator_object = CommonUtils.getIterator(indigo, file.getPath()); int count = iterator_object.count(); ArrayList<IndigoObjectWrapper> objects = new ArrayList<IndigoObjectWrapper>(); for (IndigoObject item: iterator_object) { if (isCancelled()) return null; int index = item.index(); IndigoObjectWrapper obj; if (useProxyObjects) obj = new IndigoIteratorItem(iterator_object, index); else obj = new PureIndigoObject(item.clone()); objects.add(obj); setProgress(100 * index / count); } return objects; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MessageBox.form��������0000664�0000000�0000000�00000011025�12710376503�0032067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo"> <Properties> <Property name="defaultCloseOperation" type="int" value="2"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="generateCenter" type="boolean" value="false"/> </SyntheticProperties> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,67,0,0,1,80"/> </AuxValues> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/> <SubComponents> <Component class="javax.swing.JLabel" name="icon_label"> <Properties> <Property name="text" type="java.lang.String" value="icon"/> </Properties> <Constraints> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/> </Constraint> </Constraints> </Component> <Component class="javax.swing.JButton" name="ok_button"> <Properties> <Property name="text" type="java.lang.String" value="OK"/> <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> <Insets value="[2, 40, 2, 40]"/> </Property> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="ok_buttonActionPerformed"/> </Events> <Constraints> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="10" weightX="0.0" weightY="0.0"/> </Constraint> </Constraints> </Component> <Container class="javax.swing.JScrollPane" name="message_field_scroll"> <AuxValues> <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> </AuxValues> <Constraints> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="10" insetsLeft="10" insetsBottom="10" insetsRight="10" anchor="10" weightX="1.0" weightY="1.0"/> </Constraint> </Constraints> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <SubComponents> <Component class="javax.swing.JEditorPane" name="message_field"> <Properties> <Property name="editable" type="boolean" value="false"/> </Properties> <Events> <EventHandler event="hyperlinkUpdate" listener="javax.swing.event.HyperlinkListener" parameters="javax.swing.event.HyperlinkEvent" handler="message_fieldHyperlinkUpdate"/> </Events> </Component> </SubComponents> </Container> </SubComponents> </Form> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MessageBox.java��������0000664�0000000�0000000�00000017634�12710376503�0032061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * MessageBox.java * * Created on May 13, 2011, 1:35:38 AM */ package com.epam.indigo.controls; import java.awt.Color; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.io.IOException; import java.net.URISyntaxException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Icon; import javax.swing.JTextArea; import javax.swing.JTextPane; import javax.swing.UIManager; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.AttributeSet; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.html.HTML; /** * * @author rybalkin */ public class MessageBox extends javax.swing.JDialog { public static final int ICON_NONE = 0; public static final int ICON_INFORMATION = 0; public static final int ICON_WARNING = 0; public static final int ICON_ERROR = 0; private String _message_text; private String _message_context_type; public static void show (java.awt.Frame parent, String message, String title, int icon_type) { show(parent, message, title, icon_type, false); } public static void show (java.awt.Frame parent, String message, String title, Icon icon) { show(parent, message, title, icon, false); } public static void show (java.awt.Frame parent, String message, String title, int icon_type, boolean html) { Icon icon = null; if (icon_type == ICON_WARNING) icon = UIManager.getIcon("OptionPane.warningIcon"); else if (icon_type == ICON_INFORMATION) icon = UIManager.getIcon("OptionPane.informationIcon"); else if (icon_type == ICON_ERROR) icon = UIManager.getIcon("OptionPane.warningIcon"); MessageBox mbox = new MessageBox(parent, true, message, icon, html); mbox.setTitle(title); mbox.setLocationRelativeTo(parent); mbox.setVisible(true); } public static void show (java.awt.Frame parent, String message, String title, Icon icon, boolean html) { MessageBox mbox = new MessageBox(parent, true, message, icon, html); mbox.setTitle(title); mbox.setLocationRelativeTo(parent); mbox.setVisible(true); } public static void showHtml (java.awt.Frame parent, String message, String title, int icon_type) { show(parent, message, title, icon_type, true); } public static void showHtml (java.awt.Frame parent, String message, String title, Icon icon) { show(parent, message, title, icon, true); } private String getMessage () { return _message_text; } private String getContentType () { return _message_context_type; } public MessageBox (java.awt.Frame parent, boolean modal, String message, Icon icon, boolean html) { super(parent, modal); // Set _message_text before initComponents for correct pack _message_text = message; if (html) _message_context_type = "text/html"; else _message_context_type = "text/plain"; initComponents(); if (icon == null) icon_label.setVisible(false); else { icon_label.setIcon(icon); icon_label.setText(""); } if (html) message_field.setContentType("text/html"); else { message_field.setContentType("text/plain"); } message_field.setBackground(UIManager.getColor("TextField.disabledBackground")); message_field.setText(message); if (!html) { JTextPane check_size = new JTextPane(); check_size.setText(message); Dimension s = check_size.getPreferredSize(); s.width += 20; s.height += 20; //message_field.setPreferredSize(s); //message_field.setMaximumSize(s); message_field_scroll.setPreferredSize(s); message_field_scroll.setMaximumSize(s); } pack(); ok_button.requestFocus(); // TODO: Exception while removing reference: java.lang.InterruptedException // 1. Open load // 2. Cancel // 3. Close main window } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; icon_label = new javax.swing.JLabel(); ok_button = new javax.swing.JButton(); message_field_scroll = new javax.swing.JScrollPane(); message_field = new javax.swing.JEditorPane(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); getContentPane().setLayout(new java.awt.GridBagLayout()); icon_label.setText("icon"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 0); getContentPane().add(icon_label, gridBagConstraints); ok_button.setText("OK"); ok_button.setMargin(new java.awt.Insets(2, 40, 2, 40)); ok_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { ok_buttonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 2; gridBagConstraints.insets = new java.awt.Insets(0, 10, 10, 10); getContentPane().add(ok_button, gridBagConstraints); message_field.setEditable(false); message_field.addHyperlinkListener(new javax.swing.event.HyperlinkListener() { public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) { message_fieldHyperlinkUpdate(evt); } }); message_field_scroll.setViewportView(message_field); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(10, 10, 10, 10); getContentPane().add(message_field_scroll, gridBagConstraints); pack(); }// </editor-fold>//GEN-END:initComponents private void ok_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_ok_buttonActionPerformed {//GEN-HEADEREND:event_ok_buttonActionPerformed setVisible(false); }//GEN-LAST:event_ok_buttonActionPerformed private void message_fieldHyperlinkUpdate (javax.swing.event.HyperlinkEvent evt)//GEN-FIRST:event_message_fieldHyperlinkUpdate {//GEN-HEADEREND:event_message_fieldHyperlinkUpdate if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); java.net.URI uri = new java.net.URI(evt.getURL().toString()); desktop.browse(uri); } catch (URISyntaxException uRISyntaxException) { } catch (IOException iOException) { } } }//GEN-LAST:event_message_fieldHyperlinkUpdate // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel icon_label; private javax.swing.JEditorPane message_field; private javax.swing.JScrollPane message_field_scroll; private javax.swing.JButton ok_button; // End of variables declaration//GEN-END:variables } ����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MolFileFilter.java�����0000664�0000000�0000000�00000002110�12710376503�0032500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ package com.epam.indigo.controls; import java.io.File; import java.util.ArrayList; import javax.swing.filechooser.FileFilter; public class MolFileFilter extends FileFilter { ArrayList<String> extensions; public MolFileFilter() { extensions = new ArrayList<String>(); } public void addExtension( String ext ) { extensions.add(ext); } public boolean accept( File file ) { if (file.isDirectory()) return true; String name = file.getName().toLowerCase(); for (int i = 0; i < extensions.size(); i++) if (name.endsWith("." + extensions.get(i))) return true; return false; } public String getDescription() { String descr_string = new String(); descr_string = ""; for (int i = 0; i < extensions.size(); i++) { descr_string += extensions.get(i); if (i != extensions.size() - 1) descr_string += ", "; } return descr_string + " files"; } public String getDefaultExtension() { return extensions.get(0); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MolRenderer.java�������0000664�0000000�0000000�00000007435�12710376503�0032240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.*; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; import java.io.*; import com.epam.indigo.*; import java.awt.image.BufferedImage; import java.net.URL; import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; public class MolRenderer extends JPanel implements TableCellRenderer { static int call_count = 0; private BufferedImage image; private ImageIcon _exclamation_img; Color bg_color; public MolRenderer() { URL url = this.getClass().getResource("images/exclamation.png"); _exclamation_img = new ImageIcon(url); } public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value == null) return null; RenderableObject mol_image = (RenderableObject) value; Indigo indigo = mol_image.getIndigo(); IndigoRenderer indigo_renderer = mol_image.getIndigoRenderer(); // To avoid parallel rendering (maybe will be fixed) synchronized (indigo) { indigo.setOption("render-output-format", "png"); if (mol_image == null) return null; IndigoObject indigo_obj = mol_image.getRenderableObject(); int cell_h = table.getRowHeight(row); int cell_w = table.getColumnModel().getColumn(column).getWidth(); if (isSelected) bg_color = table.getSelectionBackground(); else bg_color = table.getBackground(); float[] c = bg_color.getComponents(null); indigo.setOption("render-background-color", c[0], c[1], c[2]); if (indigo_obj == null) { image = new BufferedImage(cell_w, cell_h, BufferedImage.TYPE_INT_RGB); Graphics2D gc = image.createGraphics(); gc.setColor(bg_color); gc.fillRect(0, 0, cell_w, cell_h); gc.setColor(Color.black); gc.drawString("Cannot render", 40, (int) (cell_h / 2)); gc.drawImage(_exclamation_img.getImage(), 5, 10, null); } else { indigo.setOption("render-image-size", cell_w, cell_h); byte[] bytes = null; try { bytes = indigo_renderer.renderToBuffer(indigo_obj); //System.out.print("Render: " + call_count + "\n"); call_count++; ByteArrayInputStream bytes_is = new ByteArrayInputStream(bytes, 0, bytes.length); image = ImageIO.read(new MemoryCacheImageInputStream(bytes_is)); } catch (IOException ex) { System.err.println(">>>>" + ex.getMessage()); ex.printStackTrace(); } if (mol_image.getErrorMessageToRender() != null) { // Mark molecule somehow Graphics2D gc = image.createGraphics(); gc.setColor(Color.red); gc.drawImage(_exclamation_img.getImage(), 5, 10, null); gc.drawString(mol_image.getErrorMessageToRender(), 5 + _exclamation_img.getIconWidth() + 5, 10 + _exclamation_img.getIconHeight() / 2); } } if (hasFocus) setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); else setBorder(new EmptyBorder(1, 2, 1, 2)); return this; } } @Override public void paintComponent(Graphics g) { setBackground(bg_color); if (image != null) g.drawImage(image, 0, 0, this); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MolSaver.java����������0000664�0000000�0000000�00000007300�12710376503�0031541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoException; import com.epam.indigo.IndigoObject; import java.io.File; import java.util.ArrayList; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; public class MolSaver { private Indigo _indigo; private FileOpener _fopener = new FileOpener(); private ArrayList<RenderableObject> _failed_to_save = null; private boolean saveReactionProducts = false; public MolSaver (Indigo indigo) { this._indigo = indigo; } public void addExtension (String... extensions) { _fopener.addExtension(extensions); } public ArrayList<RenderableObject> getInvalidObjects () { return _failed_to_save; } void setSaveReactionProducts (boolean state) { saveReactionProducts = state; } public String saveMols (ArrayList<? extends RenderableObject> mol_datas) { _failed_to_save = new ArrayList<RenderableObject>(); IndigoObject output_file = null; try { String out_file_path = _fopener.openFile("Save"); if (out_file_path == null) return null; MolFileFilter cur_filter = _fopener.getFileFilter(); String choosed_extension = cur_filter.getDefaultExtension(); if (!cur_filter.accept(_fopener.getFile())) out_file_path += "." + choosed_extension; if (choosed_extension.compareTo("mol") == 0) { if (mol_datas.isEmpty() && mol_datas.size() > 1) throw new IndigoCheckedException("Can save only single molecules into MOL format"); mol_datas.get(0).getRenderableObject().saveMolfile(out_file_path); return out_file_path; } if (choosed_extension.compareTo("rxn") == 0) { if (mol_datas.isEmpty() && mol_datas.size() > 1) throw new IndigoCheckedException("Can save only single item into RXN format"); mol_datas.get(0).getRenderableObject().saveRxnfile(out_file_path); return out_file_path; } IndigoObject file_saver = null; try { file_saver = _indigo.createFileSaver(out_file_path, choosed_extension); for (RenderableObject mol : mol_datas) { try { IndigoObject m = mol.getRenderableObject(); if (m == null) { _failed_to_save.add(mol); continue; } if (!m.hasCoord()) m.layout(); m.markEitherCisTrans(); if (!saveReactionProducts) file_saver.append(m); else for (IndigoObject product: m.iterateProducts()) file_saver.append(product); } catch (IndigoException ex) { _failed_to_save.add(mol); } } } finally { if (file_saver != null) file_saver.close(); } return _fopener.getFilePath(); } catch (Exception ex) { JOptionPane.showMessageDialog(JFrame.getOwnerlessWindows()[0], ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); return null; } finally { if (output_file != null) output_file.close(); } } public String saveMol (RenderableObject mol) { ArrayList<RenderableObject> mols = new ArrayList<RenderableObject>(); mols.add(mol); return saveMols(mols); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/MoleculeItem.java������0000664�0000000�0000000�00000002723�12710376503�0032401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; public class MoleculeItem extends RenderableObjectWithId implements IndigoObjectWrapper { private IndigoObjectWrapper _object; private String _id; public MoleculeItem (IndigoObjectWrapper item, String id) { _object = item; _id = id; } public void setId (String id) { _id = id; } @Override public String getId () { return _id; } @Override public String getId (int index) { if (index > 0) throw new RuntimeException("index > 0"); return getId(); } @Override public IndigoObject getRenderableObject () { final Indigo indigo = getIndigo(); synchronized (getIndigo()) { // Set ignore errors for rendering indigo.setOption("ignore-stereochemistry-errors", "true"); try { return _object.getObjectCopy(); } catch (IndigoCheckedException ex) { setErrorMessageToRender(ex.getMessage()); return null; } } } @Override public Indigo getIndigo () { return Global.indigo; } @Override public IndigoRenderer getIndigoRenderer () { return Global.indigo_renderer; } public IndigoObject getObjectCopy () throws IndigoCheckedException { return _object.getObjectCopy(); } } ���������������������������������������������MoleculeOutputTable.form����������������������������������������������������������������������������0000664�0000000�0000000�00000006552�12710376503�0033722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-99,0,0,1,88"/> </AuxValues> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"> <Property name="axis" type="int" value="3"/> </Layout> <SubComponents> <Component class="com.epam.indigo.controls.MoleculeTableWithIdPanel" name="molecules_table"> <Events> <EventHandler event="cellMouseDoubleClick" listener="com.epam.indigo.controls.TableCellMouseListener" parameters="com.epam.indigo.controls.TableCellMouseEvent" handler="molecules_tableCellMouseDoubleClick"/> <EventHandler event="cellShowPopupMenu" listener="com.epam.indigo.controls.TableCellMouseListener" parameters="com.epam.indigo.controls.TableCellMouseEvent" handler="molecules_tableCellShowPopupMenu"/> </Events> </Component> <Container class="javax.swing.JPanel" name="jPanel1"> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/> <SubComponents> <Component class="javax.swing.JButton" name="save_button"> <Properties> <Property name="text" type="java.lang.String" value="Save"/> <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> <Insets value="[2, 30, 2, 30]"/> </Property> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="save_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="save_reactions"> <Properties> <Property name="text" type="java.lang.String" value="Save reactions"/> <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor"> <Insets value="[2, 30, 2, 30]"/> </Property> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="save_reactionsActionPerformed"/> </Events> </Component> </SubComponents> </Container> </SubComponents> </Form>������������������������������������������������������������������������������������������������������������������������������������������������������MoleculeOutputTable.java����������������������������������������������������������������������������0000664�0000000�0000000�00000021175�12710376503�0033676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * MoleculeOutputTable.java * * Created on Mar 5, 2011, 10:02:17 PM */ package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; import java.awt.Component; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; /** * * @author achurinov */ public class MoleculeOutputTable extends TitledBorderPanel { protected ArrayList<? extends RenderableObjectWithId> _molecules; boolean reactions = false; public MoleculeOutputTable () { initComponents(); save_reactions.setVisible(false); } public void setIdColumnCount (int count) { molecules_table.setIdColumnCount(count); } public int getIdColumnCount () { return molecules_table.getIdColumnCount(); } public void setReactionsContentType (boolean reactions) { this.reactions = reactions; if (reactions) { molecules_table.setEntityColumnLabel("Reactions"); save_button.setText("Save products"); save_reactions.setText("Save reactions"); save_reactions.setVisible(true); } else { molecules_table.setEntityColumnLabel("Molecules"); save_reactions.setVisible(false); save_button.setText("Save"); } } public boolean getReactionsContentType () { return reactions; } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { molecules_table = new com.epam.indigo.controls.MoleculeTableWithIdPanel(); jPanel1 = new javax.swing.JPanel(); save_button = new javax.swing.JButton(); save_reactions = new javax.swing.JButton(); setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.PAGE_AXIS)); molecules_table.addTableCellMouseListener(new com.epam.indigo.controls.TableCellMouseListener() { public void cellMouseDoubleClick(com.epam.indigo.controls.TableCellMouseEvent evt) { molecules_tableCellMouseDoubleClick(evt); } public void cellShowPopupMenu(com.epam.indigo.controls.TableCellMouseEvent evt) { molecules_tableCellShowPopupMenu(evt); } }); add(molecules_table); jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.LINE_AXIS)); save_button.setText("Save"); save_button.setMargin(new java.awt.Insets(2, 30, 2, 30)); save_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { save_buttonActionPerformed(evt); } }); jPanel1.add(save_button); save_reactions.setText("Save reactions"); save_reactions.setMargin(new java.awt.Insets(2, 30, 2, 30)); save_reactions.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { save_reactionsActionPerformed(evt); } }); jPanel1.add(save_reactions); add(jPanel1); }// </editor-fold>//GEN-END:initComponents private void molecules_tableCellMouseDoubleClick (com.epam.indigo.controls.TableCellMouseEvent evt)//GEN-FIRST:event_molecules_tableCellMouseDoubleClick {//GEN-HEADEREND:event_molecules_tableCellMouseDoubleClick RenderableObjectWithId item = _molecules.get(evt.row); showMolecule(item); } protected void showMolecule(RenderableObjectWithId item) { Frame parent = (Frame) getTopLevelAncestor(); IndigoObject obj = item.getRenderableObject(); // Show details window for single molecule SingleIndigoObjectWindow details = new SingleIndigoObjectWindow(parent, obj, item.getIndigoRenderer(), reactions); if (item.getErrorMessageToRender() != null) { details.setInformationMessage(item.getErrorMessageToRender()); } String title = item.getId(0); details.setTitle(title); details.setVisible(true); }//GEN-LAST:event_molecules_tableCellMouseDoubleClick protected void addAdditionalItemsToPopup (JPopupMenu _popup_menu, TableCellMouseEvent evt) { } private void molecules_tableCellShowPopupMenu (com.epam.indigo.controls.TableCellMouseEvent evt)//GEN-FIRST:event_molecules_tableCellShowPopupMenu {//GEN-HEADEREND:event_molecules_tableCellShowPopupMenu final TableCellMouseEvent evt_final = evt; JPopupMenu _popup_menu = new JPopupMenu(); JMenuItem show_mi = new JMenuItem("Open in a new window"); show_mi.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent e) { molecules_tableCellMouseDoubleClick(evt_final); } }); _popup_menu.add(show_mi); addAdditionalItemsToPopup(_popup_menu, evt); _popup_menu.show((Component)evt.mouse_event.getSource(), evt.mouse_event.getX(), evt.mouse_event.getY()); }//GEN-LAST:event_molecules_tableCellShowPopupMenu private void saveResults (boolean products) { // TODO: save with progress bar! // TODO: if no properties then add ID if (_molecules.isEmpty()) { JOptionPane.showMessageDialog(this, "Set is empty"); return; } MolSaver saver = new MolSaver(Global.indigo); if (products) { saver.addExtension("sdf", "sd"); saver.addExtension("smi"); saver.addExtension("cml"); if (reactions) saver.setSaveReactionProducts(true); } else { saver.addExtension("rdf"); saver.addExtension("smi"); } saver.saveMols(_molecules); ArrayList<RenderableObject> invalid = saver.getInvalidObjects(); if (!invalid.isEmpty()) { StringBuilder error_messages = new StringBuilder(); for (RenderableObject abstract_obj : invalid) { RenderableObjectWithId obj = (RenderableObjectWithId)abstract_obj; error_messages.append(String.format("%s: %s\n", obj.getId(0), obj.getErrorMessageToRender())); } Frame parent = (Frame)getTopLevelAncestor(); String message = String.format("Cannot save the following entity:\n%s", error_messages.toString()); MessageBox.show(parent, message, "Some entities cannot be saved", MessageBox.ICON_WARNING); } } private void save_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_save_buttonActionPerformed {//GEN-HEADEREND:event_save_buttonActionPerformed saveResults(true); }//GEN-LAST:event_save_buttonActionPerformed private void save_reactionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_save_reactionsActionPerformed saveResults(false); }//GEN-LAST:event_save_reactionsActionPerformed public void clear () { molecules_table.clear(); } public void setMolecules (ArrayList<? extends RenderableObjectWithId> molecules) { _molecules = molecules; molecules_table.setObjects(_molecules); int errors_count = 0; for (RenderableObjectWithId obj: molecules) if (obj.getErrorMessageToRender() != null) errors_count++; StringBuilder subtitle = new StringBuilder(); subtitle.append(String.format(": %d molecule%s", molecules.size(), molecules.size() != 1 ? "s" : "")); if (errors_count != 0) subtitle.append(String.format(" (with %d not valid)", errors_count)); setSubtitle(subtitle.toString()); } public void setRowHeight (int height) { molecules_table.setRowHeight(height); } public int getRowHeight () { return molecules_table.getRowHeight(); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel jPanel1; private com.epam.indigo.controls.MoleculeTableWithIdPanel molecules_table; private javax.swing.JButton save_button; private javax.swing.JButton save_reactions; // End of variables declaration//GEN-END:variables } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MoleculeTableWithIdPanel.form�����������������������������������������������������������������������0000664�0000000�0000000�00000013347�12710376503�0034572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <EmptySpace min="0" pref="281" max="32767" attributes="0"/> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Component id="jPanel1" alignment="0" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <EmptySpace min="0" pref="292" max="32767" attributes="0"/> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Component id="jPanel1" alignment="0" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Container class="javax.swing.JPanel" name="jPanel1"> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jScrollPane2" alignment="0" pref="281" max="32767" attributes="0"/> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jScrollPane2" alignment="0" pref="292" max="32767" attributes="0"/> </Group> </DimensionLayout> </Layout> <SubComponents> <Container class="javax.swing.JScrollPane" name="jScrollPane2"> <AuxValues> <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> </AuxValues> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <SubComponents> <Component class="javax.swing.JTable" name="table"> <Properties> <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor"> <Table columnCount="3" rowCount="0"> <Column editable="true" title="Id 1" type="java.lang.String"/> <Column editable="true" title="Id 2" type="java.lang.String"/> <Column editable="false" title="Molecule" type="java.lang.Object"/> </Table> </Property> <Property name="autoResizeMode" type="int" value="3"/> <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> <TableColumnModel selectionModel="0"> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Title/> <Editor editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new com.epam.indigo.controls.MultilineCenteredTableCellEditor(false)" type="code"/> </Editor> <Renderer editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new com.epam.indigo.controls.MultilineCenteredTableCellRenderer()" type="code"/> </Renderer> </Column> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Title/> <Editor editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new com.epam.indigo.controls.MultilineCenteredTableCellEditor(false)" type="code"/> </Editor> <Renderer editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new com.epam.indigo.controls.MultilineCenteredTableCellRenderer()" type="code"/> </Renderer> </Column> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Title/> <Editor/> <Renderer editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new com.epam.indigo.controls.MolRenderer()" type="code"/> </Renderer> </Column> </TableColumnModel> </Property> <Property name="rowSelectionAllowed" type="boolean" value="false"/> <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> <TableHeader reorderingAllowed="false" resizingAllowed="true"/> </Property> </Properties> </Component> </SubComponents> </Container> </SubComponents> </Container> </SubComponents> </Form>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MoleculeTableWithIdPanel.java�����������������������������������������������������������������������0000664�0000000�0000000�00000022351�12710376503�0034543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.Color; import java.awt.Component; import java.util.Collection; import javax.swing.JPanel; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; public class MoleculeTableWithIdPanel extends JPanel { private int _id_column_count = 2; private TableCellMouseHandler molecule_cell_handler; public MoleculeTableWithIdPanel() { initComponents(); molecule_cell_handler = new TableCellMouseHandler(2); table.addMouseListener(molecule_cell_handler); table.setShowGrid(true); if (table.getGridColor() == table.getBackground()) { table.setGridColor(Color.gray); } } public void setIdColumnCount(int count) { if (count != 1 && count != 2) { throw new RuntimeException("count != 1 && count != 2"); } if (count == 1) { final TableColumn column = table.getColumnModel().getColumn(1); table.removeColumn(column); final TableColumn column0 = table.getColumnModel().getColumn(0); column0.setHeaderValue("Id"); _id_column_count = 1; molecule_cell_handler.setTargetColumn(1); } if (_id_column_count != 1) { throw new RuntimeException("Number of ID columns can be specified only once"); } } public int getIdColumnCount() { return _id_column_count; } public void setEntityColumnLabel(String label) { final TableColumn column = table.getColumnModel().getColumn(_id_column_count); column.setHeaderValue(label); } public String getEntityColumnLabel() { final TableColumn column = table.getColumnModel().getColumn(_id_column_count); return column.getHeaderValue().toString(); } public void clear() { final DefaultTableModel model = (DefaultTableModel) table.getModel(); while (model.getRowCount() != 0) { model.removeRow(model.getRowCount() - 1); } } public void setRowHeight(int height) { table.setRowHeight(height); } public int getRowHeight() { return table.getRowHeight(); } public void setObjects(Collection<? extends RenderableObjectWithId> data) { clear(); final DefaultTableModel model = (DefaultTableModel) table.getModel(); for (RenderableObjectWithId item : data) { Object row[] = new Object[3]; for (int i = 0; i < _id_column_count; i++) { row[i] = item.getId(i); } row[2] = item; model.addRow(row); } // Precalculate id columns widths System.out.println("Precalculate id columns widths"); int widths[] = new int[_id_column_count]; for (int i = 0; i < _id_column_count; i++) { widths[i] = getColumnWidth(i, 2); } DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); // Lock row width for (int i = 0; i < _id_column_count; i++) { TableColumn col = colModel.getColumn(i); int width = widths[i]; // Truncate to avoid very long columns width = Math.min(150, width); col.setMinWidth(width); col.setMaxWidth(width); col.setPreferredWidth(width); col.setWidth(width); } table.doLayout(); // Unlock after layout for (int i = 0; i < _id_column_count; i++) { TableColumn col = colModel.getColumn(i); int width = widths[i]; col.setMinWidth(10); col.setMaxWidth(10 * width); } } public int getColumnWidth(int col_index, int margin) { DefaultTableColumnModel col_model = (DefaultTableColumnModel) table.getColumnModel(); TableColumn col = col_model.getColumn(col_index); int width = 0; // Get width of column header TableCellRenderer renderer = col.getHeaderRenderer(); if (renderer == null) { renderer = table.getTableHeader().getDefaultRenderer(); } Component comp = renderer.getTableCellRendererComponent( table, col.getHeaderValue(), false, false, 0, 0); width = comp.getPreferredSize().width; // Get maximum width of column data int row_count = table.getRowCount(); for (int r = 0; r < row_count; r++) { // Calculate width for the first 100th, last 100th and some other molecules if (r > 100 && r < row_count - 100 && (r % 100) != 0) { continue; } if ((r % 10000) == 0) { System.out.println(String.format("%d done", r)); } renderer = table.getCellRenderer(r, col_index); comp = renderer.getTableCellRendererComponent( table, table.getValueAt(r, col_index), false, false, r, col_index); width = Math.max(width, comp.getPreferredSize().width); } // Add small margin return width + 2 * margin; } public void addTableCellMouseListener(TableCellMouseListener listener) { molecule_cell_handler.addTableCellMouseListener(listener); } public void removeTableCellMouseListener(TableCellMouseListener listener) { molecule_cell_handler.removeTableCellMouseListener(listener); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jPanel1 = new javax.swing.JPanel(); jScrollPane2 = new javax.swing.JScrollPane(); table = new javax.swing.JTable(); table.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { }, new String [] { "Id 1", "Id 2", "Molecule" } ) { Class[] types = new Class [] { java.lang.String.class, java.lang.String.class, java.lang.Object.class }; boolean[] canEdit = new boolean [] { true, true, false }; public Class getColumnClass(int columnIndex) { return types [columnIndex]; } public boolean isCellEditable(int rowIndex, int columnIndex) { return canEdit [columnIndex]; } }); table.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN); table.setRowSelectionAllowed(false); table.getTableHeader().setReorderingAllowed(false); jScrollPane2.setViewportView(table); table.getColumnModel().getColumn(0).setCellEditor(new com.epam.indigo.controls.MultilineCenteredTableCellEditor(false)); table.getColumnModel().getColumn(0).setCellRenderer(new com.epam.indigo.controls.MultilineCenteredTableCellRenderer()); table.getColumnModel().getColumn(1).setCellEditor(new com.epam.indigo.controls.MultilineCenteredTableCellEditor(false)); table.getColumnModel().getColumn(1).setCellRenderer(new com.epam.indigo.controls.MultilineCenteredTableCellRenderer()); table.getColumnModel().getColumn(2).setCellRenderer(new com.epam.indigo.controls.MolRenderer()); javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 292, Short.MAX_VALUE) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 281, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 292, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JTable table; // End of variables declaration//GEN-END:variables } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MoleculesInputTable.form����������������������������������������������������������������������������0000664�0000000�0000000�00000007175�12710376503�0033706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="molecules_with_id_table" pref="346" max="32767" attributes="0"/> <Component id="jPanel1" alignment="0" max="32767" attributes="0"/> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <Component id="jPanel1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="molecules_with_id_table" pref="365" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="com.epam.indigo.controls.MoleculeTableWithIdPanel" name="molecules_with_id_table"> <Properties> <Property name="idColumnCount" type="int" value="1"/> </Properties> <Events> <EventHandler event="cellMouseDoubleClick" listener="com.epam.indigo.controls.TableCellMouseListener" parameters="com.epam.indigo.controls.TableCellMouseEvent" handler="molecules_with_id_tableCellMouseDoubleClick"/> <EventHandler event="cellShowPopupMenu" listener="com.epam.indigo.controls.TableCellMouseListener" parameters="com.epam.indigo.controls.TableCellMouseEvent" handler="molecules_with_id_tableCellShowPopupMenu"/> </Events> </Component> <Container class="javax.swing.JPanel" name="jPanel1"> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/> <SubComponents> <Component class="javax.swing.JButton" name="add_button"> <Properties> <Property name="text" type="java.lang.String" value="Add"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="add_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="clear_button"> <Properties> <Property name="text" type="java.lang.String" value="Clear"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clear_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JTextField" name="file_name_field"> <Properties> <Property name="editable" type="boolean" value="false"/> </Properties> </Component> </SubComponents> </Container> </SubComponents> </Form>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MoleculesInputTable.java����������������������������������������������������������������������������0000664�0000000�0000000�00000026006�12710376503�0033656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; import java.awt.Component; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.SwingWorker; public class MoleculesInputTable extends TitledBorderPanel { private ArrayList<MoleculeItem> _molecules = new ArrayList<MoleculeItem>(); private boolean useProxyObjects = true; /** * Creates new form MoleculesInputTable */ public MoleculesInputTable() { initComponents(); } public boolean getUseProxyObjects () { return useProxyObjects; } public void setUseProxyObjects (boolean use) { useProxyObjects = use; } public ArrayList<MoleculeItem> getMolecules() { return _molecules; } public void openLoadingDialog() { FileOpener fopener = new FileOpener(); fopener.addExtension("sdf", "sd", "smi", "cml", "rdf", "mol"); if (fopener.openFile("Open") == null) { return; } loadFile(fopener.getFile()); } public void loadFile(final File f) { final ArrayList<MoleculeItem> new_molecules = new ArrayList<MoleculeItem>(); //molecules_table.clear(); Frame parent = (Frame) getTopLevelAncestor(); final ProgressStatusDialog dlg = new ProgressStatusDialog(parent, true); dlg.setTitle("Loading..."); final IndigoObjectsFileLoader loader = new IndigoObjectsFileLoader(Global.indigo, f); loader.setUseProxyObject(useProxyObjects); dlg.executeSwingWorker(loader); try { List<IndigoObjectWrapper> objects = loader.get(); int added = 0; for (IndigoObjectWrapper obj : objects) { String id = String.format("Mol #%d", added); new_molecules.add(new MoleculeItem(obj, id)); added++; } // Select ID field for the loaded molecules final SelectIDColumnDialog select_id_dlg = new SelectIDColumnDialog(parent, new_molecules, true, true); select_id_dlg.setVisible(true); if (select_id_dlg.isCanceled()) { return; } // Update molecules properties SwingWorker<Void, Void> update_properties_worker = new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { int processed = 0; int errors_count = 0; for (MoleculeItem obj : new_molecules) { boolean set_serial_on_error = false; try { // TODO: Remove bug! // 1. load valence_test // 2. load test // 3. Error! obj.setId(select_id_dlg.getMoleculeID(obj, processed)); } catch (IndigoCheckedException ex) { set_serial_on_error = true; } if (set_serial_on_error) { // Set serial number obj.setId(select_id_dlg.getSerialNumber(processed)); errors_count++; } processed++; setProgress(100 * processed / new_molecules.size()); } dlg.setStepName("Adding molecules to the table"); _molecules.addAll(new_molecules); molecules_with_id_table.setObjects(_molecules); StringBuilder subtitle = new StringBuilder(); subtitle.append(String.format(": %d molecule%s", _molecules.size(), _molecules.size() != 1 ? "s" : "")); if (errors_count != 0) { subtitle.append(String.format(" (with %d not valid)", errors_count)); } setSubtitle(subtitle.toString()); String file_name = f.getAbsolutePath(); if (file_name_field.getText().length() > 0) { file_name = "multiple files"; } file_name_field.setText(file_name); file_name_field.setCaretPosition(file_name.length()); return null; } }; dlg.setTitle("Reading molecule properties..."); dlg.executeSwingWorker(update_properties_worker); } catch (InterruptedException ex) { System.err.println(">>>>" + ex.getMessage()); ex.printStackTrace(); } catch (ExecutionException ex) { System.err.println(">>>>" + ex.getMessage()); ex.printStackTrace(); //Logger.getLogger(TestFrame.class.getName()).log(Level.SEVERE, null, ex); } } public void setRowHeight(int height) { molecules_with_id_table.setRowHeight(height); } public int getRowHeight() { return molecules_with_id_table.getRowHeight(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { molecules_with_id_table = new com.epam.indigo.controls.MoleculeTableWithIdPanel(); jPanel1 = new javax.swing.JPanel(); add_button = new javax.swing.JButton(); clear_button = new javax.swing.JButton(); file_name_field = new javax.swing.JTextField(); molecules_with_id_table.setIdColumnCount(1); molecules_with_id_table.addTableCellMouseListener(new com.epam.indigo.controls.TableCellMouseListener() { public void cellMouseDoubleClick(com.epam.indigo.controls.TableCellMouseEvent evt) { molecules_with_id_tableCellMouseDoubleClick(evt); } public void cellShowPopupMenu(com.epam.indigo.controls.TableCellMouseEvent evt) { molecules_with_id_tableCellShowPopupMenu(evt); } }); jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.LINE_AXIS)); add_button.setText("Add"); add_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { add_buttonActionPerformed(evt); } }); jPanel1.add(add_button); clear_button.setText("Clear"); clear_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { clear_buttonActionPerformed(evt); } }); jPanel1.add(clear_button); file_name_field.setEditable(false); jPanel1.add(file_name_field); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(molecules_with_id_table, javax.swing.GroupLayout.DEFAULT_SIZE, 346, Short.MAX_VALUE) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(molecules_with_id_table, javax.swing.GroupLayout.DEFAULT_SIZE, 365, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents private void add_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_add_buttonActionPerformed {//GEN-HEADEREND:event_add_buttonActionPerformed openLoadingDialog(); }//GEN-LAST:event_add_buttonActionPerformed private void molecules_with_id_tableCellMouseDoubleClick (com.epam.indigo.controls.TableCellMouseEvent evt)//GEN-FIRST:event_molecules_with_id_tableCellMouseDoubleClick {//GEN-HEADEREND:event_molecules_with_id_tableCellMouseDoubleClick MoleculeItem item = _molecules.get(evt.row); Frame parent = (Frame) getTopLevelAncestor(); IndigoObject obj = item.getRenderableObject(); if (obj == null) { String message = String.format("Exception:\n%s", item.getErrorMessageToRender()); MessageBox.show(parent, message, "Error during loading this molecule", MessageBox.ICON_ERROR); return; } SingleIndigoObjectWindow details = new SingleIndigoObjectWindow(parent, obj, item.getIndigoRenderer(), false); details.setInformationMessage(item.getErrorMessageToRender()); details.setTitle(item.getId()); details.setVisible(true); }//GEN-LAST:event_molecules_with_id_tableCellMouseDoubleClick private void molecules_with_id_tableCellShowPopupMenu (com.epam.indigo.controls.TableCellMouseEvent evt)//GEN-FIRST:event_molecules_with_id_tableCellShowPopupMenu {//GEN-HEADEREND:event_molecules_with_id_tableCellShowPopupMenu final TableCellMouseEvent evt_final = evt; JPopupMenu _popup_menu = new JPopupMenu(); JMenuItem show_mi = new JMenuItem("Open in a new window"); show_mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { molecules_with_id_tableCellMouseDoubleClick(evt_final); } }); _popup_menu.add(show_mi); _popup_menu.show((Component) evt.mouse_event.getSource(), evt.mouse_event.getX(), evt.mouse_event.getY()); }//GEN-LAST:event_molecules_with_id_tableCellShowPopupMenu public void clear() { _molecules.clear(); molecules_with_id_table.setObjects(_molecules); file_name_field.setText(""); } private void clear_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clear_buttonActionPerformed clear(); }//GEN-LAST:event_clear_buttonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton add_button; private javax.swing.JButton clear_button; private javax.swing.JTextField file_name_field; private javax.swing.JPanel jPanel1; private com.epam.indigo.controls.MoleculeTableWithIdPanel molecules_with_id_table; // End of variables declaration//GEN-END:variables } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MultilineCenteredTableCellEditor.java���������������������������������������������������������������0000664�0000000�0000000�00000001633�12710376503�0036270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.Component; import javax.swing.AbstractCellEditor; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.table.TableCellEditor; public class MultilineCenteredTableCellEditor extends AbstractCellEditor implements TableCellEditor { private CenteredTextPane component = new CenteredTextPane(); private boolean editable; public MultilineCenteredTableCellEditor (boolean editable) { this.editable = editable; } public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex) { component.setEditable(editable); component.setText((String)value); component.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); return component; } public Object getCellEditorValue () { return component.getText(); } } �����������������������������������������������������������������������������������������������������MultilineCenteredTableCellRenderer.java�������������������������������������������������������������0000664�0000000�0000000�00000002454�12710376503�0036612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.Component; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.table.TableCellRenderer; public class MultilineCenteredTableCellRenderer extends CenteredTextPane implements TableCellRenderer { public MultilineCenteredTableCellRenderer () { setOpaque(true); } public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { setForeground(table.getSelectionForeground()); setBackground(table.getSelectionBackground()); } else { setForeground(table.getForeground()); setBackground(table.getBackground()); } if (hasFocus) { setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); if (table.isCellEditable(row, column)) { setForeground(UIManager.getColor("Table.focusCellForeground")); setBackground(UIManager.getColor("Table.focusCellBackground")); } } else setBorder(new EmptyBorder(1, 2, 1, 2)); setFont(table.getFont()); setText(value.toString()); return this; } }��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/ObjectWithId.java������0000664�0000000�0000000�00000000204�12710376503�0032324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; public interface ObjectWithId { public String getId (int index); public String getId (); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ProgressStatusDialog.form���������������������������������������������������������������������������0000664�0000000�0000000�00000006250�12710376503�0034107�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo"> <Properties> <Property name="defaultCloseOperation" type="int" value="0"/> <Property name="modal" type="boolean" value="true"/> <Property name="resizable" type="boolean" value="false"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="generateCenter" type="boolean" value="false"/> </SyntheticProperties> <Events> <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosing"/> </Events> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Component id="progress_bar" pref="305" max="32767" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="cancel_button" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="1" max="-2" attributes="0"> <Component id="progress_bar" alignment="0" max="32767" attributes="1"/> <Component id="cancel_button" alignment="0" max="32767" attributes="1"/> </Group> <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="javax.swing.JButton" name="cancel_button"> <Properties> <Property name="text" type="java.lang.String" value="Cancel"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancel_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JProgressBar" name="progress_bar"> </Component> </SubComponents> </Form> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ProgressStatusDialog.java���������������������������������������������������������������������������0000664�0000000�0000000�00000013161�12710376503�0034064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������/* * ProgressStatusDialog.java * * Created on May 4, 2011, 6:26:10 PM */ package com.epam.indigo.controls; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.concurrent.ExecutionException; import javax.swing.SwingWorker; public class ProgressStatusDialog extends javax.swing.JDialog { private Announcer<ActionListener> cancel_listeners = Announcer.to(ActionListener.class); private boolean need_to_cancel = false; /** Creates new form ProgressStatusDialog */ public ProgressStatusDialog (java.awt.Frame parent, boolean modal) { super(parent, modal); initComponents(); setLocationRelativeTo(parent); progress_bar.setMaximum(100); } public boolean neenToCancel () { return need_to_cancel; } public void setProgress (int percent) { progress_bar.setValue(percent); } public void setStepName (String step_name) { progress_bar.setString(step_name); progress_bar.setStringPainted(true); } public void cancel () { need_to_cancel = true; cancel_button.setEnabled(false); setStepName("Cancelling..."); cancel_listeners.announce().actionPerformed(new ActionEvent(this, 0, "")); } /* Wrappers for cancel event listeners listeners */ public void addCancelListener (ActionListener listener) { cancel_listeners.addListener(listener); } public void removeCancelListener (ActionListener listener) { cancel_listeners.removeListener(listener); } public <T, V> void executeSwingWorker (final SwingWorker<T, V> worker) { need_to_cancel = false; cancel_button.setEnabled(true); setStepName(""); setProgress(0); worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange (PropertyChangeEvent evt) { int progress = worker.getProgress(); setProgress(progress); if (worker.isCancelled() || worker.isDone()) setVisible(false); } }); addCancelListener(new ActionListener() { public void actionPerformed (ActionEvent e) { worker.cancel(false); } }); worker.execute(); if (!neenToCancel()) setVisible(true); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { cancel_button = new javax.swing.JButton(); progress_bar = new javax.swing.JProgressBar(); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); setModal(true); setResizable(false); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); cancel_button.setText("Cancel"); cancel_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancel_buttonActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addComponent(progress_bar, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(cancel_button) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(progress_bar, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(cancel_button, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }// </editor-fold>//GEN-END:initComponents private void cancel_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_cancel_buttonActionPerformed {//GEN-HEADEREND:event_cancel_buttonActionPerformed cancel(); }//GEN-LAST:event_cancel_buttonActionPerformed private void formWindowClosing (java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowClosing {//GEN-HEADEREND:event_formWindowClosing cancel(); }//GEN-LAST:event_formWindowClosing // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton cancel_button; private javax.swing.JProgressBar progress_bar; // End of variables declaration//GEN-END:variables } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/PureIndigoObject.java��0000664�0000000�0000000�00000000531�12710376503�0033204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; public class PureIndigoObject implements IndigoObjectWrapper { private final IndigoObject _object; public PureIndigoObject (IndigoObject object) { _object = object; } public IndigoObject getObjectCopy () { return _object.clone(); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/RenderableObject.java��0000664�0000000�0000000�00000001344�12710376503�0033205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.IndigoObject; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoRenderer; public abstract class RenderableObject { private String _error_mesage; public abstract Indigo getIndigo (); public abstract IndigoRenderer getIndigoRenderer (); // Can return null if there is no renderable object or if it is not valid // If this method returns null then setErrorMessageToRender should contain // error message public abstract IndigoObject getRenderableObject(); public String getErrorMessageToRender () { return _error_mesage; } public void setErrorMessageToRender (String message) { _error_mesage = message; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RenderableObjectWithId.java�������������������������������������������������������������������������0000664�0000000�0000000�00000000242�12710376503�0034233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; public abstract class RenderableObjectWithId extends RenderableObject implements ObjectWithId, IndigoObjectWrapper { } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SelectIDColumnDialog.form���������������������������������������������������������������������������0000664�0000000�0000000�00000012064�12710376503�0033711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo"> <Properties> <Property name="title" type="java.lang.String" value="Select ID field"/> <Property name="modal" type="boolean" value="true"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> </SyntheticProperties> <Events> <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosing"/> </Events> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="1" attributes="0"> <Component id="molecule_with_properties" alignment="0" pref="476" max="32767" attributes="0"/> <Group type="102" alignment="1" attributes="0"> <Component id="prev_button" min="-2" pref="80" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="next_button" min="-2" pref="77" max="-2" attributes="0"/> <EmptySpace pref="236" max="32767" attributes="0"/> <Component id="done_button" min="-2" pref="77" max="-2" attributes="0"/> </Group> <Component id="help_info" alignment="0" pref="476" max="32767" attributes="0"/> </Group> <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Component id="help_info" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="molecule_with_properties" pref="324" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> <Component id="done_button" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="prev_button" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="next_button" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel" name="molecule_with_properties"> </Component> <Component class="javax.swing.JButton" name="prev_button"> <Properties> <Property name="text" type="java.lang.String" value="Prev"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="prev_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="next_button"> <Properties> <Property name="text" type="java.lang.String" value="Next"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="next_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="done_button"> <Properties> <Property name="text" type="java.lang.String" value="Done"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="done_buttonActionPerformed"/> </Events> </Component> <Component class="javax.swing.JLabel" name="help_info"> <Properties> <Property name="text" type="java.lang.String" value="<html>Choose a property field that will be used as an identifier for all the molecules in this set. </html>"/> </Properties> </Component> </SubComponents> </Form>����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SelectIDColumnDialog.java���������������������������������������������������������������������������0000664�0000000�0000000�00000022775�12710376503�0033701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * SelectIDColumnDialog.java * * Created on May 10, 2011, 4:01:18 PM */ package com.epam.indigo.controls; import com.epam.indigo.IndigoException; import com.epam.indigo.IndigoObject; import com.epam.indigo.controls.IndigoCheckedException; import com.epam.indigo.controls.RenderableObject; import java.awt.Frame; import java.util.Collection; import java.util.List; import javax.swing.JOptionPane; /** * * @author rybalkin */ public class SelectIDColumnDialog extends javax.swing.JDialog { List<? extends RenderableObject> _molecules; int _active_index; boolean _show_name, _show_serial; boolean _canceled = false; public static final String SERIAL_NUMBER_ID = "$Serial number$"; public static final String NAME_ID = "$Name$"; /** Creates new form SelectIDColumnDialog */ public SelectIDColumnDialog (Frame parent, List<? extends RenderableObject> molecules, boolean show_name, boolean show_serial) { super(parent); initComponents(); _show_name = show_name; _show_serial = show_serial; _molecules = molecules; _active_index = 0; showCurrent(); done_button.requestFocus(); setLocationRelativeTo(parent); pack(); } public String getSelectedProperty () { return molecule_with_properties.getSelectedPropertyName(); } public void setSelectedPropertyIndex (int index) { molecule_with_properties.setSelectedPropertyIndex(index); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { molecule_with_properties = new com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel(); prev_button = new javax.swing.JButton(); next_button = new javax.swing.JButton(); done_button = new javax.swing.JButton(); help_info = new javax.swing.JLabel(); setTitle("Select ID field"); setModal(true); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { formWindowClosing(evt); } }); prev_button.setText("Prev"); prev_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { prev_buttonActionPerformed(evt); } }); next_button.setText("Next"); next_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { next_buttonActionPerformed(evt); } }); done_button.setText("Done"); done_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { done_buttonActionPerformed(evt); } }); help_info.setText("<html>Choose a property field that will be used as an identifier for all the molecules in this set. </html>"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(molecule_with_properties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 476, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(prev_button, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(next_button, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 236, Short.MAX_VALUE) .addComponent(done_button, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(help_info, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 476, Short.MAX_VALUE)) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addComponent(help_info, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(molecule_with_properties, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(done_button) .addComponent(prev_button) .addComponent(next_button)) .addContainerGap()) ); pack(); }// </editor-fold>//GEN-END:initComponents private void done_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_done_buttonActionPerformed {//GEN-HEADEREND:event_done_buttonActionPerformed if (molecule_with_properties.getSelectedPropertyName() == null) JOptionPane.showMessageDialog(this, "You need to select property field"); else setVisible(false); }//GEN-LAST:event_done_buttonActionPerformed private void prev_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_prev_buttonActionPerformed {//GEN-HEADEREND:event_prev_buttonActionPerformed _active_index--; showCurrent(); }//GEN-LAST:event_prev_buttonActionPerformed private void next_buttonActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_next_buttonActionPerformed {//GEN-HEADEREND:event_next_buttonActionPerformed _active_index++; showCurrent(); }//GEN-LAST:event_next_buttonActionPerformed private void formWindowClosing (java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowClosing {//GEN-HEADEREND:event_formWindowClosing _canceled = true; }//GEN-LAST:event_formWindowClosing public boolean isCanceled () { return _canceled; } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton done_button; private javax.swing.JLabel help_info; private com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel molecule_with_properties; private javax.swing.JButton next_button; private javax.swing.JButton prev_button; // End of variables declaration//GEN-END:variables public String getSerialNumber (int index) { return String.format("#%d", index + 1); } public String getMoleculeID (RenderableObject rend_obj, int index) throws IndigoCheckedException { String prop = molecule_with_properties.getSelectedPropertyName(); if (prop.equals(SERIAL_NUMBER_ID)) return getSerialNumber(index); try { IndigoObject ind_obj = rend_obj.getRenderableObject(); if (ind_obj == null) return getSerialNumber(index); if (prop.equals(NAME_ID)) return ind_obj.name(); return ind_obj.getProperty(prop); } catch (IndigoException ex) { throw new IndigoCheckedException(ex.getMessage()); } } private void showCurrent () { if (_active_index >= _molecules.size()) throw new RuntimeException("Internal error"); int selected = molecule_with_properties.getSelectedPropertyIndex(); RenderableObject rend_obj = _molecules.get(_active_index); final IndigoObject ind_obj = rend_obj.getRenderableObject(); molecule_with_properties.setObject(ind_obj, rend_obj.getIndigoRenderer()); molecule_with_properties.setInformationMessage(rend_obj.getErrorMessageToRender()); if (_show_serial) { molecule_with_properties.addCustomProperty(0, "$Serial number$", getSerialNumber(_active_index)); // Select serial number by default if (selected == -1) molecule_with_properties.setSelectedPropertyIndex(0); } if (_show_name && ind_obj != null) molecule_with_properties.addCustomProperty(0, "$Name$", ind_obj.name()); if (selected >= 0 && selected < molecule_with_properties.getPropertiesCount()) molecule_with_properties.setSelectedPropertyIndex(selected); next_button.setEnabled(_active_index < _molecules.size() - 1); prev_button.setEnabled(_active_index > 0); } } ���SingleIndigoObjectWindow.form�����������������������������������������������������������������������0000664�0000000�0000000�00000011501�12710376503�0034644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������<?xml version="1.1" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JFrameFormInfo"> <NonVisualComponents> <Menu class="javax.swing.JMenuBar" name="menu_bar"> <SubComponents> <Menu class="javax.swing.JMenu" name="file_menu"> <Properties> <Property name="text" type="java.lang.String" value="File"/> </Properties> <SubComponents> <MenuItem class="javax.swing.JMenuItem" name="save_mi"> <Properties> <Property name="text" type="java.lang.String" value="Save As"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="save_miActionPerformed"/> </Events> </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="exit_mi"> <Properties> <Property name="text" type="java.lang.String" value="Close"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exit_miActionPerformed"/> </Events> </MenuItem> </SubComponents> </Menu> <Menu class="javax.swing.JMenu" name="edit_menu"> <Properties> <Property name="text" type="java.lang.String" value="Edit"/> </Properties> <SubComponents> <MenuItem class="javax.swing.JMenuItem" name="layout_mi"> <Properties> <Property name="text" type="java.lang.String" value="Layout"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="layout_miActionPerformed"/> </Events> </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="remove_hydrogens_mi"> <Properties> <Property name="text" type="java.lang.String" value="Remove hydrogens"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="remove_hydrogens_miActionPerformed"/> </Events> </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="add_hydrogens"> <Properties> <Property name="text" type="java.lang.String" value="Add hydrogens"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="add_hydrogensActionPerformed"/> </Events> </MenuItem> </SubComponents> </Menu> </SubComponents> </Menu> </NonVisualComponents> <Properties> <Property name="defaultCloseOperation" type="int" value="2"/> </Properties> <SyntheticProperties> <SyntheticProperty name="menuBar" type="java.lang.String" value="menu_bar"/> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> </SyntheticProperties> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="obj_panel" alignment="0" pref="599" max="32767" attributes="0"/> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="obj_panel" alignment="0" pref="383" max="32767" attributes="0"/> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel" name="obj_panel"> <Properties> <Property name="dividerLocation" type="int" value="250"/> </Properties> </Component> </SubComponents> </Form>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SingleIndigoObjectWindow.java�����������������������������������������������������������������������0000664�0000000�0000000�00000015456�12710376503�0034637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; import java.awt.Frame; import javax.swing.JFrame; public class SingleIndigoObjectWindow extends javax.swing.JFrame { private Indigo _indigo; private IndigoRenderer _indigo_renderer; private IndigoObject _obj; private boolean _is_reaction; // TODO: show name as $name$, because molecules loaded from SMILES have only names /** Creates new form CellFrame */ public SingleIndigoObjectWindow (Frame parent, IndigoObject chem_obj, IndigoRenderer indigo_renderer, boolean is_reaction) { initComponents(); obj_panel.setObject(chem_obj, indigo_renderer); _is_reaction = is_reaction; _indigo_renderer = indigo_renderer; if (chem_obj != null) { _indigo = chem_obj.getIndigo(); _obj = chem_obj.clone(); } else { file_menu.setEnabled(false); edit_menu.setEnabled(false); } setLocationRelativeTo(parent); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } public void setInformationMessage (String message) { obj_panel.setInformationMessage(message); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { obj_panel = new com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel(); menu_bar = new javax.swing.JMenuBar(); file_menu = new javax.swing.JMenu(); save_mi = new javax.swing.JMenuItem(); exit_mi = new javax.swing.JMenuItem(); edit_menu = new javax.swing.JMenu(); layout_mi = new javax.swing.JMenuItem(); remove_hydrogens_mi = new javax.swing.JMenuItem(); add_hydrogens = new javax.swing.JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); obj_panel.setDividerLocation(250); file_menu.setText("File"); save_mi.setText("Save As"); save_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { save_miActionPerformed(evt); } }); file_menu.add(save_mi); exit_mi.setText("Close"); exit_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { exit_miActionPerformed(evt); } }); file_menu.add(exit_mi); menu_bar.add(file_menu); edit_menu.setText("Edit"); layout_mi.setText("Layout"); layout_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { layout_miActionPerformed(evt); } }); edit_menu.add(layout_mi); remove_hydrogens_mi.setText("Remove hydrogens"); remove_hydrogens_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { remove_hydrogens_miActionPerformed(evt); } }); edit_menu.add(remove_hydrogens_mi); add_hydrogens.setText("Add hydrogens"); add_hydrogens.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { add_hydrogensActionPerformed(evt); } }); edit_menu.add(add_hydrogens); menu_bar.add(edit_menu); setJMenuBar(menu_bar); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(obj_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 599, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(obj_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE) ); pack(); }// </editor-fold>//GEN-END:initComponents private void save_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_save_miActionPerformed MolSaver mol_saver = new MolSaver(_indigo); mol_saver.addExtension("cml"); if (!_is_reaction) { mol_saver.addExtension("smi"); mol_saver.addExtension("mol"); } else { mol_saver.addExtension("rxn"); } mol_saver.saveMol(new RenderableObject() { public IndigoObject getRenderableObject () { return _obj; } public Indigo getIndigo () { return _indigo; } public IndigoRenderer getIndigoRenderer () { return _indigo_renderer; } }); }//GEN-LAST:event_save_miActionPerformed private void exit_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exit_miActionPerformed dispose(); }//GEN-LAST:event_exit_miActionPerformed private void layout_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_layout_miActionPerformed _obj = _obj.clone(); _obj.layout(); obj_panel.setObject(_obj, _indigo_renderer); }//GEN-LAST:event_layout_miActionPerformed private void remove_hydrogens_miActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_remove_hydrogens_miActionPerformed {//GEN-HEADEREND:event_remove_hydrogens_miActionPerformed _obj = _obj.clone(); _obj.foldHydrogens(); obj_panel.setObject(_obj, _indigo_renderer); }//GEN-LAST:event_remove_hydrogens_miActionPerformed private void add_hydrogensActionPerformed (java.awt.event.ActionEvent evt)//GEN-FIRST:event_add_hydrogensActionPerformed {//GEN-HEADEREND:event_add_hydrogensActionPerformed _obj = _obj.clone(); _obj.unfoldHydrogens(); obj_panel.setObject(_obj, _indigo_renderer); }//GEN-LAST:event_add_hydrogensActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem add_hydrogens; private javax.swing.JMenu edit_menu; private javax.swing.JMenuItem exit_mi; private javax.swing.JMenu file_menu; private javax.swing.JMenuItem layout_mi; private javax.swing.JMenuBar menu_bar; private com.epam.indigo.controls.IndigoObjectWithPropertiesViewPanel obj_panel; private javax.swing.JMenuItem remove_hydrogens_mi; private javax.swing.JMenuItem save_mi; // End of variables declaration//GEN-END:variables } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TableCellMouseEvent.java����������������������������������������������������������������������������0000664�0000000�0000000�00000000653�12710376503�0033600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.event.MouseEvent; import java.util.EventObject; public class TableCellMouseEvent extends EventObject { public TableCellMouseEvent(Object source, int row, int column, MouseEvent mouse_event) { super(source); this.row = row; this.column = column; this.mouse_event = mouse_event; } public int row, column; public MouseEvent mouse_event; } �������������������������������������������������������������������������������������TableCellMouseHandler.java��������������������������������������������������������������������������0000664�0000000�0000000�00000005044�12710376503�0034073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JTable; public class TableCellMouseHandler extends MouseAdapter { private Object active_object = null; private int active_object_row = -1; private int target_column_idx; private Announcer<TableCellMouseListener> cell_event_listeners = Announcer.to(TableCellMouseListener.class); public TableCellMouseHandler (int target_column_idx) { this.target_column_idx = target_column_idx; } public void setTargetColumn (int target_column_idx) { this.target_column_idx = target_column_idx; } public int getTargetColumn () { return target_column_idx; } public Object getActiveObject () { return active_object; } public int getActiveObjectRow () { return active_object_row; } @Override public void mousePressed (MouseEvent e) { maybeShowPopup(e); } @Override public void mouseReleased (MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup (MouseEvent me) { if (me.isPopupTrigger() && validateMouseEventData(me)) onShowPopup(me); } @Override public void mouseClicked (MouseEvent me) { if (me.getButton() == MouseEvent.BUTTON1) { if (me.getClickCount() == 2 && validateMouseEventData(me)) onDoubleLeftClick(me); return; } } private boolean validateMouseEventData (MouseEvent me) { JTable table = (JTable)me.getSource(); int col = table.columnAtPoint(me.getPoint()); int row = table.rowAtPoint(me.getPoint()); if (col != target_column_idx) return false; active_object_row = row; active_object = table.getValueAt(row, col); return true; } private TableCellMouseEvent createEvent (MouseEvent me) { return new TableCellMouseEvent(me.getSource(), active_object_row, target_column_idx, me); } private void onShowPopup (MouseEvent me) { cell_event_listeners.announce().cellShowPopupMenu(createEvent(me)); } private void onDoubleLeftClick (MouseEvent me) { cell_event_listeners.announce().cellMouseDoubleClick(createEvent(me)); } public void addTableCellMouseListener (TableCellMouseListener listener) { cell_event_listeners.addListener(listener); } public void removeTableCellMouseListener (TableCellMouseListener listener) { cell_event_listeners.removeListener(listener); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TableCellMouseListener.java�������������������������������������������������������������������������0000664�0000000�0000000�00000000403�12710376503�0034275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls�������������������������������������������������������������������������������������������package com.epam.indigo.controls; import java.util.EventListener; public interface TableCellMouseListener extends EventListener { public void cellMouseDoubleClick (TableCellMouseEvent event); public void cellShowPopupMenu (TableCellMouseEvent cme); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/TitledBorderPanel.java�0000664�0000000�0000000�00000001660�12710376503�0033357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package com.epam.indigo.controls; import javax.swing.JPanel; public class TitledBorderPanel extends JPanel { private String _title = null; private String _subtitle = null; private String getFullTitle () { if (_title == null) return null; if (_subtitle == null) return _title; return _title + _subtitle; } private void validateTitle () { if (getFullTitle() == null) setBorder(javax.swing.BorderFactory.createEmptyBorder()); else setBorder(javax.swing.BorderFactory.createTitledBorder(getFullTitle())); } public void setTitle (String title) { _title = title; validateTitle(); } public String getTitle () { return _title; } public void setSubtitle (String subtitle) { _subtitle = subtitle; validateTitle(); } public String getSubtitle () { return _subtitle; } } ��������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/images/����������������0000775�0000000�0000000�00000000000�12710376503�0030413�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/images/exclamation.png�0000664�0000000�0000000�00000002364�12710376503�0033432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR��������� î^���gAMA��± üa���PLTEÿE/ÿK6ÿL6ÿT?ÿ[Fÿ]Hÿ]IÿeQÿiVÿvdÿ~lÿ‚oÿ‡uÿŒ{ÿ”„ÿ›Œÿ “ÿ£•ÿ©›ÿ¯¢ÿ´©ÿ¹­ÿº°ÿ¼³ÿ¿´ÿ¿¶ÿúÿżÿÉÀÿÌÆÿÏÈÿÔÌÿÖÏÿÙÓÿÚÔÿÜ×ÿáÜÿäßÿæâÿçäÿçæÿìëÿîìÿñîÿóóÿöôÿøöÿùùÿýýÿÿÿ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ìÕ#��� pHYs��À��ÀjÖ‰ ���tEXtSoftware�Paint.NET v3.5.87;€]��eIDAT8O“[[‚@†?¡B1ĶÒL*5÷ÿÿ¹æ°»ÒMOsÃμsÞöOÁ„ŽË8õ¿âMAdÑ^=<îRižç}“ÏàpÜ5'1ö-y®\Å%POj¶12U׈B:±ï½(—7_r:Å0>:E­­<Q[³³;D½&o‘¸º·<×»º¨çhÕbeìW=p'xˆÀiX$z㔌;dÎ`:­ä¤°[7Y·ÜÚÅaýƒ:+}ôy<zEì°,½Å¾`öí•k­|ž¿…s…†ñ ñ¯+ö<çUÒÜ©[©.•¦éQaZ¾.ÕVÒá>ÆN#îynwëŠmƒD÷v3M)÷½@:ð—jÏåBÇ¥‹<$H8•—>C¬º>&Ò±ò3˜(x»§8®ø¡³¯Í‚N…w ïü¸tÏœ`ÊLþ’qgÖye¶Ò¥“éO41ÿÿ�ò:ƒy¼©ôž����IEND®B`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/java/common-controls/src/com/epam/indigo/controls/images/gga-logo.png����0000664�0000000�0000000�00000007635�12710376503�0032630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���^���N���&f6¢���sRGB�®Îé���gAMA��± üa��� pHYs��Ã��ÃÇo¨d��2IDATx^í›ytNgÇs,Õê®Ë9Ý[mÍT댶LU´%ÔNQ­Ö¾ÌØ©½(Z[m¥NQ[$’KiB"!±‡$’$±D"²I˜™>ñ¸yîòÞóÏœyßsóºïo»ßûÛïÇ¿Ý<ÜȘ!à†ÆÔ7Üи¡qž8Ü^ãö·×8GÀí5Î1sœkŠ®\9œ²reÊ￟Y³&s÷î[%%Îõs”"ðøâʼnsæ$ΛwbùòKüëÖ­{‘óÆôˆˆ¬¸8§BìBs5>~_ÿþ_{ÍÛÃC9ü{lçgŸÔìl§ê}q^Þ±ŸÞZ¯žwåÊzùkŸ~:º[·Óþþå»Áõë ™¨pdžkh®&&†·m«·XÆÿ©§Ny{;RÏÕ&ÍŸ£ù›k×¾´¿#ù££¥ä5j8âu —ºªR%­Ý>÷ßìéÉmŒîÙ3²cÇõÕ«+WÚ¸ñµ'ìQ”“Ö¢…–}¥‡Gà›oF÷ê3jÔÁaâºv xî9…`ïÀ7®]³#ŒÔ²gìÜi“2+hfÏ^u'|0:òóÏMè*ÒsŽçÎoxåi„ï£^MH°6"÷ôéÀš5%˺—^:¶`AáÕ«z®«GŽÄMž¼ú¡‡$1÷¦¸ ÀåE‚ OÕªZh¸£.¹$)4FŒBƒÞyû¬…–äç7Nºw;/-ÍŒ¥ðâÅ€gžòA?~Ú´’¢"kù×ÏÛÑ®4)¢}û›7oZ³$/]*è¹UâËêÀUm¢c MêÆÒˆmM›ÞÈ͵).ëðaÿǼ8EaV–!cxëÖ¥ò+W>íçgS8dÇ.”†YÖŒ[êÖÄGfΔj?@ëž^%ˆôdmPfT”w•*‚ÝÐ-ZTzy+^ ³‹ Œ0A¢CaÆŽ›—ze¥J‰sçŠÿ65@s`øp!eí³ÏÚOxZ}©ë× d(%逻Ì1cÆØ´R!#= ùÔ,³®gÿС‚†0„½àòeÙdÅÆÚÑ«BƒˆU*¡©vDX‡Lìwßi èâ„ðu/¿Lz*ŸüüÌLßrÈAz!d.¿jÕAÚ–-‚€z*Îì4ÈŽ^ªµà§S²ÃoF“}ô(‰Ö÷á‡/ÇÄhi‚ß{OÈwÚ)Š’—,!¹’Œ  -TÙ(ž 'éQí8š¨Î?•û^ — ¢øÅµ””Òø¯PÁ,CÛWj1@Ð[ E±ß|#bÏú_çS||\** ÌþO<!˜i‚]2;%H]»ö¿â’Özé˜D;Æ¿|×Ç}û­0 ¤aC—Æ—†NDp2¶¸ä Ûš4ÙæåeçØðúëBþ¡ñãíÈÏ;ž mÔÈѱé7„|-Ì«aÍš Ao¿]ê¶9'OZÛPšì¤$Á‰t;¦3s z§ƒµù2ºÊ/Mg¾¾h1‰oßžqã@#{MµjÙ1…Œ^GÖãkväk<GòoçÚjÕXq %ª{wC^ª„õ(_ÆkˆL!6;¦CSpéÛôðp;Çî>}„ü˜±cmÊ¿rèÉ’†ŽN¨ ¯*n3k…È|š¶i“…e !çû=òˆMœÛ´Þ>uA'1Ùç²OI×+‡8‹¡™^˜Á²Å.4БSÛ½ô{BIýÜÖ­Ú!P¦yú§ó‡r °Ós_Ü»W{>aÖ,a<Ó“Å5S|KC¬bÅüôt3Jµ¯‘å-¼Mû·KOI ãsß}X?uªöWöIÂ,GS¥^¾X 0ˆh˳ÎÌmm¼ì<f̰ %M6娧J5ѽ{ vué¢ÕÍæEœßüÖ[.· fFç_¸ …K{ö2vIB2?¹\Èy…úeèh%„ú"rX9|‡œ'w`ÊbhòY€•C8,x´@Ÿr÷ftë&N²$t)ì$¸”CzƒÉ›îFN˜,]ªQH(/¼ ¬d¢Ó³3pŠ_ ‡Ô œÊ—ëØ)=‚k*!Vž´–,ýš9Þ.4ÐÅOŸ^š¨l´FZ¹Ô5ÙòR#Ùæéµ2Ú…4hP*¿J³›fh.KRiؾ!C$ *qÞf³ #‘(XØv> 1]€²C“FloÞ<÷Ô)—·÷ÜæÍþO>)¯ùÂöíf,ì€åÚ eCT|ýºµ|üBÛÓõk§gÙÎ$þðƒK;%+za-½¥žËrdd‡ ¥xå$'*Nß±#¬U+ILs‘dmâí¸Ó<-`¯Èö/ïìY=I7nÊÙ§¡…'VZ(Ùº–ª®\™Ô>44åúœ%Ù­ž(ÐhóÐûbEyÍ|áÉÁÞ~ý°•&‚]dxË–Z£!`t`jǾÜÔTصÂùŽ7íéÛ÷àèÑT7ýô+»¹Ð‹øôS;z%M™ÕŸîñ¦ëGt”pÙ(—¡ü—Ð m;}†Io)Ÿ.XËßðê«gõ/§$–UŽ 8²S'¡4s×.…×540\§|}‘"Ÿ(×Àƒ­¢Y¸¹4—<ÂsnJ²òب4MV­JÎóu³N‚®te™»Ž“‘RväÃëaTeýÁJáðĉ,½c'M:µzµ íAÀN—äM¹¹ý:Àœ94fä‘ò=궩т̖×Ü»šÿE nhLïš74ÎCÚí5n¯q{sÜ^ã3ƒ\sëÖ­C1‡–.^¼tÉ’ð°0þ«ˆÍÎÎö]½úç…‹6n**4xe(xëV??qDFF ö¬¬,yR~I9™"~Ü™‘‘!¡4`ÍÚëšq<777"|‡þwíܹwO™ ±¤‰‹Š6m ¼¨[”””hí‰)ûl!Ðü8w^ç/¾Ú´yë–-sfÍR Áâ¦^—-]ʯ^5<xà€b1+~ûÍ šñ_íúegñ«„¥‹Ý}Cóxrrõ_ þCJîÓ¿WxÖ{ÿ#ÏEF/uÍž9sôÈ‘hA24€¨e禾û·Ú[ÐÔ©ý6~aè€ `qåÊùkRbRƒ÷ë+ô@s-ÇôMD@Ÿ:y²"ÉŸu¸;7/[ºløÐ¡cF–dãÆ|½ûÎNOžÜ°~=çG‰_è šukJ߃Áy»w)³Íð²ˆ3¯áð¡Ã†<ßMœôû¯¿*?aÙZíÉr@ƒo~èéyõÎk޽{ô<þ<!VëüŠL}ð~Þ¡c\lì‘øøŽí Þ&ÑBCŠPhÊ a_¯N•+VÜнìù±—×™²ï`7q7tð`š˜ƒ=Êq颺[2ôØ'M˜¸q#_“._|ÉâŽËæË‰'þÑ«·rKNž8Ùªy k‡ß)@ãëãC pA¯Pï×ù»0’CÞIcÜò]¸paÄWÚ5þXÑçù^½Kº5Pþ³WšQÃFS8¶oÛ¦Xl ¹à«?×½$õð…Û3ÿϧã¿.ÿ…©S¦¬Z±RœSmô‰“@ƒ¯‘7ë¾óî¿2~ͯxM­7j #9öïÛ§È·ê†Éü¸Ï5ÍÛ¤Ÿ´i›˜ ¾wC4!úê¶­EEÄTqq1™hÿŸo•ShäKÏnÝÏœ9£Üs’((Gìˆà ­ýV-åÎË€"©7ÿ¸©ROÊPZ ú÷íKý–gæÏû‘ª¤ Û«GªÕ½Cƒ„!ƒJû¶ŸÈXnÛªuZZZ‹¦Í¥ô Û·ÿiþ|yà¹tZ2m®!yጠ¸ŽÓ0_ˆ(,,ôjØèˆæej¢©aƒÈ#R.ÃU²R9Ò°È5ŠËÐÜŒy`ÒMçN„gÉmÕS/JV(ªÛ½V(¼®I#/Òê7ãÆs…HWl¢ZwíÜÇ\’”™™™ M¹¡ÉÉÉyó/%³H¨«QýÕ°²ÏmNŸ:EÔ÷¢CÒú¸ˆ[6kž|옔L@Õ|½Æ€~ýÄ1å[µŸ0È5Ä<"( yyyÊ5Ëÿž={oÒƒ"¨Jϳóóó-ºÚV¼U*â’ÒÓÓI@ZK`ØyQŒ´™-ÚKPþ+$˾’º—fwßò/[L™þ?~0öÜlKPeOÒÔïè¨(qèÛpcª¿2"éÙ!``ÑGkJJŠâÕäTýh£½/D4³Sº´³¨ IJJ’vŽ ÄéÞ={Èý/ÆWåŽ@ã³Ê›.€‘ŒZH¦¿6*“ËÜÙs8–ýÕí 5ž_ia鸴¹C¨'ǯöV_jîðI;­}ÀGÑ1ôQdŽ=†dLñfv¡âIŸBIçAuv.øiò+©‡ž€ }ÖŒ™Œc®¡¡}Öîô–¾ñÓ’…ôíÇmQ„ÂÛ¶l©=I×/†›ÐpÍÌnÚR­·hÌ¢Ÿþ˜FÁ"7x °Ür:‚fÆ´iÚb,v ÁËÖ¯[gâõ€áªH+Á%4ƒúp &rÛ —XBÐMN>ÔQ½té5t�>ø055UOC@M¼óTzZ!}ö1 (†Fe41¼B !טىðÖ-ZPW._6d7NÃ\ó>íã9£?š½]»qèçÔ� Í>‰ î»uH䆊i@pOìžèÝŒ£z23h˜Wô¹Ã0 0CØ9½ìÛ–‚˜„µhÁBffÆT}J5íkn¯ ˜2ôIÔ~@M?žuŸ™Óꢑ'k€Ž}h~Y¶üû)Ó,bAüdP’é‡-š~²wÑòõá/–ï,w¥,ûÐ06úð#³dIŽÄ&\ÿ2ìžÍ¼†2ÿA}O—/Ú„†ë"ë‰_û±‚ÝmZ´T¦8‘kìW¨Áþaò’8&].Mq5t‹âÍe³¯1lš¤(ûÐ0£1¯º€e,ÄÈ 4,LýL†}ÍÈaÃqiqè§mñf—ª]ú*ê 7f<ÆE§Ð¤X¶11’wØá³™Õg ¡/“vR×´Zˆ aC†òà„žˆõ¶~ùoà545¬æXk*Û#)—ÌŠ+ɾH±¯Õz;è˜Å~a¶‡F\bjöÁÔÐàbö$úÇÚTk§Ò.Ó ÑtüQ»véó)Ýã¥)înhÜи,õ:·×¸½Æí5Îp{sÌþÆãûP)oÖÁ����IEND®B`‚���������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/jna/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016241�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/jna/LICENSE��������������������������������������������������������������0000664�0000000�0000000�00000063642�12710376503�0017261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] 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 Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these 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 other code 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. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. 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, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) 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. d) 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. e) 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 materials to be 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 with 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 Lesser 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. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! ����������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/jna/jna.jar��������������������������������������������������������������0000664�0000000�0000000�00005162515�12710376503�0017526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PK ����v©Æ@������������ ��META-INF/þÊ��PK ���u©Æ@º8ˆ”��N�����META-INF/MANIFEST.MF•MSÛ0†ïžñБÎD&ŽƒKa8ȦÉ0…áÚQ䥨µ%UrÒÐ_ß•ãË¡§ÄÞw½»ÚMLа%}c…’$ŽÆa0“73Íø+|‡Áóh7X ½~sú4ObºOé4>£ñx‘¤É9™i¹“<ú &$½É™µ„«"²ký”,Z²Rl Â`É ¨b§;ÅØi܈(@–¨R’>‰2/¿#y™)sAî—3r È•vAò¬è×%&Ñ4ŠÉÉ*ý£×G \¼îzÏ6ŒìÜ’ç`-9ÁC:òãÚÚýùap½–YtQ_È!49„v ªŠ®ßÜ‚åF貺¿Š•aæí x|+V*üÐÛ÷¾ÕŠ ßà÷ZÈæ[àkÇžË0Jº2ð˜ÉãœÆÑ´A8Vp-šql\Uôƒóƒs“³¿ès¾ÕÊ”ôñ_ì‡oqÔ]ïùI³3îZnTÖ JE·ç)>‰LXÍJþey~I´Qڞ‡I/••ؤ«*eÔCaE–N?æÐtz •L8PfŠc ¼bŠ…ÈxüT¶ª+«&ɪ&9¨Ùƒª¤QgWÙ’óƒå5k„aºá0äÔÎ*Õÿ±6_:4äxuÖºÏ2±¥Z·Œ!ç5bMJöÌõ!º­r÷Õ‚¼7ªÂxœ\Èu™žºnª¤Q?§ã9­Ið, ²DâOrºCÕ˜¨APk¢Ó7žþD Ãp[v(ä L:{ß—áêØÞr†VƉZ$¥4È•Ív­BÎÑýk´ªÎó}½�s~’–¹«ó:0äT¼ºÝûpGDN ͘Áߟ6 ¿ã›KRgŒã¾¹¿ðPK ����'©Æ@���������������com/PK ����'©Æ@���������������com/sun/PK ����C©Æ@������������ ���com/sun/jna/PK ����C©Æ@���������������com/sun/jna/darwin/PK ����(©Æ@���������������com/sun/jna/ptr/PK ����(©Æ@���������������com/sun/jna/win32/PK ���'©Æ@%{ªNm���…���&���com/sun/jna/AltCallingConvention.class;õo×>vvVv6F®àüÒ¢äT·ÌœTFIÇœçÄœœÌ¼tçü¼²Ô¼’Ìü<½¬Ä²DF…äü\ýâÒ<ý¬¼D}lê@*õsóÒõý“²R“K€æ3201@�#3db`�PK ���'©Æ@0™T¿���'��3���com/sun/jna/Callback$UncaughtExceptionHandler.classuŽÍA„«ýì²HH<€ÆÝQ‰ÄÁϽwL–5feðlÀC +‰P‡îJúKWÝî—+€*6 6J„Zb$'Þ&ž¥:ÄÛÀº­© ö"JŒð ‹kí²Üõ§>Yh6žXlÂàÄ®VýöŠà̃$”j´ÕŠPyóÝ·Q&t~½k.¿“ÇlÖZ…„ÆÿSybŒ š£HE„ê§ÒÌõ•Œ õ_aA*ʲÈ=]ù×´à<·ƒ”±Q´ŠPK ���'©Æ@2z=ªž��a�����com/sun/jna/Callback.classu’[OAÇÿ#¸»"ÝzÁ{«ØèC»ÆWŒ ]0š $.’>4Ã2¥ã¬î¥i?Vã‹1± ÊxvI“–è<Ì™sfÎïÜæÏÓÃo�‡ø £l`ÕÀšõ6°9[:¶u¼Óñž¡|¥\GQã‡+n"ÏWg\ ¤òçJ‰À–< EÈ0Ñ蜵ë_[µ‹ƒÙóïÜ’\ -' <5¬2l_…WQ—ËXØa(ž¶/?Ÿ×ëVêè0,O<ãÈ“–íK)Ü$*yÇ®ô”0döº YÛB4=%Zñu_Þ—dÉ9~¸âÔK”‚Í¥ìs÷Û§„ËPšNŒÈ#Ž&0MÜÆ\R9FäO®uìæQÁ^%¼aXtýk+Œ•5VÜú‹þÚî)g†/½¬¼ÞOâ¥-¨ÿIùh<lz!a÷ö¿4§ƒUþiZò¬ª1¤ACžÁ*ò(€a!ÕŠ¤S!t¦ˆ0ñ–ä"iGÈ"YkÚ#X/cÎ8½¬™qz³fÖ¹Çì´_©ÏíIÀ"ÚrÊ\Á<ÉY“ï“ÓæžPK ���(©Æ@Ã*n5â��¯��*���com/sun/jna/CallbackParameterContext.classR]OA=³»ýtù(h¡ ˆ¤»(ëƒomBÒ¤ ‰¤/>M·cÙºfº%å?ù ‰ ü�”ñδ! ]Œ;s÷Þsî9sg~ÿ¹¾ð ûeä±]BOÕ²£–Ýž°ÇDzžtª­>¿à^Èãž'Å—Pø©w¢kG —½ÃÊçЇNŸ0TÌqWŒX“Ú½ â }Ëð¾>m„|08º_ £mÓi“l#é †ÅV‹ÓaÔòŒwBÊ,·Ÿ‡m.õ?MZéy@&÷[~yƒaìõcî5xv¸ÿõ#—œŽ*d#‰S1R¾‹JôìòQ—æÌÒq"†RO¤'ÓmÖÈ&è;ÙF"NÉE¥îd«H°æd^fÝ¡‰•?%Cé‹ã@¡vŸÝCÕÊÆžÛ( h£„²°öþç¸Ôzv,“è”§Á…¸­¯Î_˜Óƽ•<½"†Ò¥ÈPÒz'uÚ-úðtM(Ÿ£}Í=¸s_Œa¸/Ç0ÝŠ5†õSó©ºF­Óú˜º=!nl`‰²¯'°Œ@G«Tc:Z#†Ž” SGʇEõuê5qáÑ®ð9÷Œ·¢yÜÔ"ö0ad¡šA6ï’k™ä j:O¶¾ß!oe’kµõPK ���'©Æ@š‘S¥Æ���1�����com/sun/jna/CallbackProxy.classm±nÂ@ †šÐ(ž ÉÂugDbªÔØ:™Ó 5IuI*x5€‡B8¨û—ìÏÒ'Ž»=€w¼hhhš­]°^Þ¯„YYN—ês‘]Œ¢Û¡»4E̎צ0n¾ý19¡F—çcËy.hKЩ)J—V¡Fw¨§YV:m&ßV×ñŸRì²ÍvXÑ„ÎÖ*/S•¤¬þ„ε¢ØÜÃ}Á“ªÉjuB€¤àœ <ÊFt¤{x>PK ���(©Æ@«FŒº+��é��1���com/sun/jna/CallbackReference$AttachOptions.class•PËJA¬ÉÃqc4š¨Áƒèz„ `$oɘLÜ•ÙYÿË“àÁð£Äž5E<LWWOõóýãõ À ¶ê(c=@'À†À˜T’qç‰r$§C‰hõgôDQLf œÕfÒeÑ©6Ú ”÷ö‡,í¥c–6ûÚ¨«<){K£¸HN%ÅC²Úóy°â¦:hœ;ßèúÑéÔ0/Q¶S–)¦G}™&Q–›hf(êQH>ܨ{e•‘j÷G6T¤¹•êBû_úc¿EˆEÔl þ£¸@û»šK—[n³ýgì Äg?^Uöørs P+~$Æ%ö"F©¼@<³SBÈv¡vÑ`~ °ŒÕâ¿UØ5¬0ú2%´ÑDíPK ���(©Æ@áð ��ú��8���com/sun/jna/CallbackReference$DefaultCallbackProxy.class•Y `Tå•þÎÇLnˆ„L`€@ÄA&™ÉŠ(‰¢‚„&€I LnÂÀd&Î#@_»vmmÕ>èkÅV×¾ˆ]©Q’ ŠZ[|Õ­ÝÝnßín­Ö­}¿[ÛsîL&37É äÞÿžÿûÏþs¾sþÿϽþÈ9� t¬}¸ß8%­Ï»ñ�¾ QŠd¬áaN»ù=îÆ&Ý(Åé{¤gñ¨ŠÇDrN>—¡OÈãI_Tñ”Š/¹QS.|YÞçåñ´<žqÃgEés*žWñ7Vâ…b8ñonVýUy¼(о&­wã?ðŸÅø:þKÅ7T|Ój|KÅ·U|Ç�¾' ï«øŠŠÁÿíÂÜhÀÿÈÇÿºðc/É÷OÜØ€gEø² ¯Èû§.¼*=ÿ'?sá51éçòñ ~)º%¿vá7òñ[ÁþNZ¿—Ö¤õGyü©Æ_úW^Wñ77vÜD¤¨ds£ƒìÅä '»šTÆ’ËÍ¢"¸åQÌó’¦R‰J ÜØO¥*] ï…¬’ÊŠi•«äP…J‹UZBXE£½¡ðáv=u0ÞGXÚv(4ª†bõ ½?ª‡Sõf_Á•Šï ¥"Ã:ae[8>XŸLÇêÅBõ]ù–xlXO¤ôƒÝý‰øà|ÕþømÙ®ÜÎÔÁHÒׯ|KÆÄ½_Oè±°ÎТÎc±ÔA= ó°Ë#±Hj¡Ë?ϸÙ×6s5džôöÐÐU½—`ßïã%”¶EbúÎô`¯žè õFYRÖg÷í %"òR„Zù‡Û•9vêát"’:Ör4¬¥"ñ¯Â. &”oÕûCéhjÊà݉øÑc­5Ó[¢¡dRgÔú¹ç³R"N4VBX2Û9®¡Ä€x–²ý96sK(z*ˆ „°ÐP< g'æ™üÕ–¶2pA$6?¬Oc×øs'ÜÕ{ˆƒÒT]("¨áx,¥MåÓc—`8Ö0ÕÆë’0Yhóå[£Q} mN ¤õX*7B«,`á°žLæ‚j-xÕÊë ‡ÐÅFè3”ÚY®^d±röõP(ÌDC›JÏfc„3¡'9Ä%ÜK¨˜ÍÉ®pÖ½Ä`OÎ4]ñ#BT†•†ÍÜ›Z7áR¡EѶ Ž­7ÝÏfíßÌS²¡‹g˜Ö™J¤ÃL ™Ô1Ц94j_2eò©$cFGfm[a9§’ ¼ù3Eb͉DH¨o GÙ–…ìýÝâS«LƯåþj+ž—0´#‡ê‹üÕVÙÐO'Âú¶ˆ¤{EA"ÖÉ Q jˆ“WC?TZªá³8©a÷1ç©…á°JË4ZŽ›9Ÿ^Çð:†×™P#uû4ª¤*­Ô¨Š.Ôh]¤á "*ù4ZM.Ì(wdÎTqZ£‘Ÿª5ª¡€FAª%,›£nqRÌŸ:ì˜FT6§ûû¥mòXÕ Q{«"ɪH,ddU„™¬ Ö«’Ì—*.ñÜÇfö‡Âº�‡Ò½ÑH¸±J£:ª—GƒFki¬âM­§KäS…“Ý*Q²œÈ” ѱ=O—j¸'4¼ÿ¤Ñe´Qýø¤J5Ñ*mÒèJºŠP5_e Þ@]Ш™8{<–‰Ã[‰¥Ü·ùØÞL>eZmÎÝ‚w1}-òž¥¹švÇ ÷j´Uˆf¸®NÔ™1Ó¨…¶itµt.Êea·ÙŸ§­{zÔvZQfžå*mÒh‡tUg‚|•ÜûfjÓ¨] å¹€©Àqdh§F»àÉdÝ¢ÑnºF£ATZ"|YwuR—F{h¯FÝt­F=ÒÚGûEæUé:®ÇÍU §W²9ê¡Ø ¶™gn .•nÔ(D½y8]+¤Ôåð.¬R§.±=ý¸O£:¨ñAâF‡)ªÑ <bçÂ:KµËÛM¦\ɪ‡¦%£÷&”T)¥Qš†M‡˜«Ñ:JX÷ÆÏ\Ÿ-SÏì³ùåxµd¶CYé f$¬KI–ìö1ÊÇ(_n‘ã ÓVû ½cUÛKóDàH²™ Ó@L²Gjµì…›â>óÔK¦B†m,@mó–c6¡Âú˜ÆÓ²þ‚ƒ6‡e¾‰¬çVKàXTðû>ç›ÄúD¯±ó²…Ý8û©$©§r¥vÿ>™Û)ËŒqЂóÄ*w;iʃ[œòà|‡™ª]åþB#<Vó³nsò544G¥¹ï³7ºrö{‰Å±Uli±:©Í}*¶<C9Íó8a£…Âÿïɼ¬Ö|„ȹd7¹í¡X_Ô¸Œì‰…C郩®µ³Ü|³ ‘øÀ•<ßE¨³Žh“åéYbP&ù8sç=ƒS¹Çí¢P:ïæ£”œͺ$@Ÿ�}S”,bmSm‡%?½…U/»Wøwì³´8$Û„yë™9åÒL¥¾Y{2ÛÌb™Àb«a=…fM_ó6YÞâ2{Ö,+Ôžs:*ŽéG¦KoÍ|Å*÷BrÑЧWödä-f㫽=¡‡¸N¹ü;öon5ÒÑ-w s &¬ÖÆ—Ÿ#fø‹ÒI½]Œ'ŽÉeÙÚ{3ç sÙ󆽫gw‹œò”¸x|Æè›ù6¦¼IQÕ¸‚íê¶Ì KFZ韽$‰e•sî/„-ó•§œ1³'—”s¶wuìáe/²´Ö©ß”ÉUÏcµì“ߤììj¹º¥ã†B-­<ÿ€œ’)жæ¶Î–ér¿VÊ}…æ}³…¹Âo‘ â6¯ßú$&}ÅÌõÌÍ )¿l2¤S‘h=ïÿbÞ Sq(Í7›Ëæ¾±ÏYµe¯Ï"ËÝÚYŠ õRQ…>¬�A‡Š\t~óÔxó%–ßerfL òÇÅ­8†øy@#pBeé+5Ó š (5ÁÓ°Çao´óÓÑè¨ñÚÏ=çi¨cp=Ò²¢I¸{‚‹Ç²ÄëGñ ”Öx“ÐN£d‹jÏów­×q °È¶ÁéqÖ< õܧ°ÇZ‹Ýã\g(ò Îã<‹ÒC8 :Gàe•³§ÖŸÁB·8iäo#lgÙ]ðíãX4‚ªFìé9‹Šž ,.[2/–ŽcÙ–?þ /ü)<ƒçQÎJðÒ÷ÂÃŽxŠèÝX@·¢œÞƒ¥ô^øè64Óíh§÷áz?®§òÝô8n£á^ú>CÅ8Ë¥;ñÝ…gè<OŸÀ t7^¦{‘dÝÛÙ¹7â:¤fG¿€eÆn±Ãqo5òŠ6Åh½ o‡-û:Þà.ÅWñ¸¶šoÃfаŽß$¡d‡Ð$*G`ËZœ"§Osk&&3"7Rs¼²Šq.–½Æãméö�xÅNÛ»Çàu=öuŽ€<°qã<‚g±²Çë1Ü3Á~Vk=öL·×éUÏàB8V¥"fc«:o±s„î§F{Í8.jtxçÑZ#:¼ŽÚqø&°ºÑ>‚ ÎI\ìuxg°Æ†–æ ʳ‚qÔ2ƒTµlîšZæÃ“œÀb³¼ %ÒzqÔù¦×>Êž=‚[qÇ[Þ‡ßxŸBuÆ{è4RÂE÷1îÇ:…ôyf¨£/à*z]4†nzaî°ó£t zGèQÜJç™çp=Ž;¹ýqzwÓñ9nŸ¢/áú2£gñ4=‡ïÐWðcn¿J/Ñz?à*Žü»Ù"•Sºï1âîÕx/ÛìÄB\kðÈŽ;8¶Ã¸ew­;¸õ�J¹õ>nñª3|ãgYöÏðŽ¿ /áƒÌ-…çû!Ž³ÌÆåáøÏag/¼ ÆG ¾}tŠ/\NØÛü®¨ L n´!Ñ a Qƒgž¯>ãÇ2Öú6œô]¨ô=Ψïk¬b-*Ûù1ü³Á÷ЬuëùÅO†¥W°FëùpàI4œ ÎàÖJáXg޵FáX{¥'ÐP4Ž7µÐA­Ð%&h½Z‹ÏbCä"qé.cpƒ7 ¸Ño4À?‹åÜÑ$—›MFGÓIÔfëU™ÈvΧ+ÚFðƒ1lÌ•&f“Ù4*\¹¤mwPtMâ*¡YÀ›Mp³n6Á NbËÎ1l@‹ Øj�¶š€N£mºí†m^»Ëõ8®féöìWëŠ ÛDZƒ;Þ\Ë“^1†¶ ¯u í¢z§©ºÝPÝ<‰Jvû®¨àa»Æ±ûDfy“¸¦-0]p`1‡è%óOPE/#H?E ½Šún§Ÿã4ý_£_áGô¼N¿%ýVÒi ý‰.¡?Óú m£×i§êPˆº…º;½CqÒqÅeÐf=´Ëq§¬l¸Ÿ`âÛ„SâÖ=L`2Zÿ‰£ðß{ñÉ •V3‘„’ÇÙó%ç-¦$p=Rˆ&Ðy]6´žæ‰F¹dìiŸ# ÕsÄ Egû(“¤Íd’…{™cØË¸î(õ†´gÔšá¦KV_˜­g*XûFç"¹åÐVë'Q~û%‡®ã¸~ 3‚üo70Ko ÔžAȆîÚÑ9 ˜ÇôN"<šC¤®}P4¨J *•R\¨\€‹”…ð+eت,ÂJ9z• R–à6Å‹{”åxQ©Ä¯••´Z©¢Š"Êj:¬¬¡[?}@©1ÈSÔ9€| Ÿ6(s<K™ãÊø)‚Ï”!ù‹=Vöȼ=V©µØcIþsÁz¼#|ƒåý9ãù¯x ¿—²½}xÇXäšþ]gŸ zérgÑßPK ���(©Æ@›B¸B£��l ��9���com/sun/jna/CallbackReference$NativeFunctionHandler.classVéWWÿÍ#0!Evqi©b;‰`Z­­²¨¶h$HE­v/00ÌàdB¡{íbí¾[º¯öôCϱ XÏéé§~àêé}“È–¡žÓóæÎ}÷þîúî¼¥þøÀ#¸ÀnÄdœ€¡·õèK1Nb €ÍˆË À˜§Ä{H,OŠå´Ûpgeœ’Opüx:� #~$ƨ .L$¨Á˜àŒ‹O=€ LŠÅ†§ŠaÂÔ´X. 8[P)ŽŒ´Œ þdÚL8ºeJ¨Ž%¬©p*m†'L-Ü•ã7K­iA¥$”Æ&´-œvt#|B›¦½¢ÝÔÃTuz¿¥›·›£ë4‚C|k” 0Ýä½é©nj#qÊcVB3†4[ß9¦Ï×ÉtU¯æè3ü®[=š9jp[‚5MnG -•â$v`Í0F´Ää�Or›› ^ï‰"bÔFGmž†<ãPF®º9±ÌnfS&E)º9cM’£q5«¡™cᾑ žpšWqlž4ˆ>Áqk´ùl¾p0Ÿ%¡ åØ"3+[qÇ&7hKš’°uc ¤›0( ²U"nž„ªµsÙ\á´mÍÎQTS.¥_³Ç¤ÂÃ_ ÎÙÏ6‡Œgè{Œ;¹ŒI¨Qƒ¤2·Òv‚w颾ÕyEÚ+,)؃Y •^=)öæ< UA!Ï)x­ ^ÄK¸>UíédR8TÑ/b«s½Hj ^çXu ^Æ+b¹¤àU¼F]é–“´³ñº‚7pY–2­àM\‘ñ–‚·ñŽV§ÊxWÁ{N *x­T…í(90ÆmàCÊG >Þ"P>Uð®*øó ¾À—2¾Rð5¾Qð-¾Sð=~Pð#®(8ˆŸ$ìÿÝ.aóúRJØ•\”‘ÐÖ(V¬H ŽÛÖ3ÙZ ŠS]»Á�ÉøY¶՛1}ÄÖì¹úe`y™*ík?Ö¼0؈övS+ò‹iM4r•Ç ž!mzš›Ô« jþ1 æ±²íмNÜã ®÷;V–C]©æË‘DÉšYG‰¡ã á¡{˜Y>Me$ÞÁ†&àÜ“J_céîñÝñŸe'¤¤nŽÞåçêÕ< /èÒu,ª ¹Õ«Mñ•Úô´Å{"}”“q-5žçÔ4 }jT4CIN²óä©¶Xœä#W“ßiýÙ™£è)—Ê Vz8LÕ®$(Ö<t,oØÕbøÏhFš÷%iv¨gV+µ[–Á5ñ,ËcÒ¬ì¢Ø( žÅzŠþdmîôÜ¡n<¦ELå Ë$§¸Iϲ’ªzý#<çp åd€;iÛœ›¦Ztç§ÎCoý¯Ù ¹ÂãØ¢Ž.3»AM— &Æ0Ýz˜˜Äô. þ4ÐÚH_“(„Þ5¡H¡;`Ã{¶/ à|¡Æ[(üÝUÜKk¹c-ð±V”±Ã¨fG&~(«N—ªý€K=†.¨Çñi J˜†âqȰ4Cz2í-Ý@QCò<ÎÞxÅå 6…n—’AI“èÂòûn£”á/ln*¬-Ì låME9ZÝT^AKmQ•⣊W¹Öwýª³Jï f8”Á–Ôwk–»·±`ŸûuhÛ"¶Ï£ªÖ·ˆ¡k}¿ÀW°ˆûIáë ‹¨›‡¿qö  ag£ð/ƒ]×—sÕN9°¬;Y'ö±.´°n\`QŒ°ã0X ³¬—X.³~\e'ñ+‹ã7v 7Ùn³Ónnè¾¹GÑ„fÊÖ>Êq Z©`-TØÃ8‚"\&ê(ÚÜ,-×`‰<hsk°„i0—ê@'Õ ‹híuÓÓƒh® Âôò…"¨•@Š“sQ²9# ÇÜõ8¹%šƒQÇÇ£Ð]÷Y<âPK ���(©Æ@_5¸¶��9��#���com/sun/jna/CallbackReference.classZ |TÕÕ?çf2ofòX0,2@€d’°©ˆAPV „°Á°ˆ“É$ $3qfBî[µîZm ­ÖªJc„,¢âÒbµ­Zmk«­Ýk7­­­mõûŸûf&3™ Øï¿w—sï¹çžýÜÉK?ñ4ÍRŸwQŸ/ŸZÆ› ÞbðVåò6ù\àd»|.Ìc×ÉÇop½‹L®•AÀà ãFùìpÒqJo§ôvI¯IzÍ.|BrJXNi1ø"G\4–£‚$–Ç­¼;Ûx|Ú Þëâ‹ùY{©Á—ÉÆËås…‹Îå+…œ«|µÌ\cðµ.*µü¾N]ïàÏ äƒotÑl¾I°Ülð-.¾•osÑs|»¬½ÃÁŸsñ|—‹?Ï_pðdi¯tðeï>]Îûü%þ²“ïæ/çñ=ü™»Wz_î$÷ü€Ô!3|@Ú‡„9çÑJÞ/ly„Íãƒü¨ þš|üuÕð6wJû˜|ºü¸´‡|Dðt˧ÇÁ½.îã'dpÔÁOJû”ƒŸ–ö˜ƒŸqñ³üœ|•™çü i¿éàãÒ¾ààoIû¢ƒ_’öÛþŽƒ¿kðË.ŠpTXóŠƒ_•ö{òyM¨{]zß—Ï þ¡‹ßà¹èbþ±HéMƒßrÒ$þ‰pã•<þ)¿-"û™À~.Ÿ_<KüÒÁü+'¸ókaÑoäó[üNæ~/ì{G†¹¼+Ÿ?Џþ$s6ø/ÒþUúžôÞwòß ô>pðßükð¡ƒÿéäYƒüo'ÿÇü×Á;ù=PäPìTÊä8”Í©r­Ý¡ iå”ÖåPy†2Eý»¨K ‘É¡èñŸ 5ÌE=¼Mf†;T¾´5ŒR# Uh¨Q5ÚEÇ8j¨1L…Õ¾Xpw`ykÈ †C+|¡ú¦@„ɬ …‘%M¾h4e¹4ÐàkmŠ-ñ55Õùü»ÖFÂ{Ú åf²(óùw¬i‘íX™ç/YíkaVµÓ·Û7³5lš‰‰ù€öûâ« ×®_s~íö%‹ªª/Z²jûêeV¬YÊ4ÖÚ×ä 5ÎŒšþØÌÕØŽp=P˜ÁP0ô5÷"Àáð×Ec‘V èªüáæ™ÑÖÐÌ!ß̵á`(ˆ`Gn‹ÐË4. žv¬²7ë˜Fû"_{‘¦¡Hh(ZS·3 ä§Ð¥™ƒmΚöPlG ô3MôËdN)Â)E8¥(í¦i™+Y| †—„C»!á hÍ\X£¯Ù 0M9´hqûF_S+V¹3WÅ™ÂtJ&ÌÒÈ©%�.Œ²ô3€Æ¬X7%`™¬K@ÆÆ!Y·œoÀ9ÅápSÀÙÖg™^Ü7 3.´#‰%ïÓ?¿d‡/âók.eœ] î5 $㪰Ü$ãåMa_, 7—†[ëš@Õøh iVvD¾úÊ~õf:§8«ÎÎÏ:›±~ÉF&Û’p}@l1 T·6×"|úè‚*XbÓF_$(ãø¤ò×Úì‡Â|ƒ©ÄMÿ”T0 ïßg™¶þ/7[hD! (ÍßÌ/I1H ñ¹‘p«8›/ÒïPþ?¡Ã6!£QÐ@þƒâÏkì—ÓÂâ ÏÕ• ÆX[¬½Ügœyö§ÄµyPd¹þ:xM¸£_+süu8‘ƒÐçW] ô¾XX»Nœ°JxÚ†pÂÍÉï`:ý$LÎdÄãÖ#Ú¯òfÒ,Ã~–`!üß êQ¹Ytš›±´EH [²¸e[K@T”WBùBúüµ°êæ ÖW$�WÚ ï¦"pݓҎ[ 7[TkŸ±âˆŠ…2uC8sYNs´Q¬«Ÿ(ËÏi1ïꘕԜ|¦ï·7‹õcÒϵÚ;G,ýöƒõ9-- ~”ѢúQQ¦ •dáQŽ¿I‡ìÄëxð›[<xì-9QXÎo†êbÒ'€ûBKʦʦ¦@£¯iQ¤±µ—]¶ÇÐz$ºlðùE4ù)†• jÎ ÊpB‚ÊOÌ„Œõ®–Ö:k�*Æm9a ¢½yr±£¥9ÑMÍxj1¬5’°‚WŸL˜lÅ•¢Ð"Á ÐÏ–0”\]\2H2ã�Ÿµ#…üŠe«QŒ¶„£)Ô%ŒIjH “™@ù ÌÍN€±#‘%¦ª¸•"\€“2Θ1È›;D5À£‹.¶IdJUï‘Y$ 2,yÇD^á-Τpð#EcÓ€©ØÄî¶ò*[[P"ªÝŠéLS‹3m=›½©=³™&T‡cËí¡úTkP{fA!}~XD´hÖ, ÎÈÎÁ~g>¨èhf ÇYþ¦¸‡-L!$õlWM¸5â,J:0*ã ²Ëäå|®IñY¦Ë«™<'»2Îþ%Õa½U…µ( GL¦§Æ›ô-zѤïÒ‹†š`ªSÔDSyÔ$¸‡™lEŠ©&ËΩŸ*Q1ÕUdª©jš©¦ó8S«Sy9ÏT¥Ò+•^¨¤ècS•«çɽ%ŽôHÈò4·Fcžº€Çòhåχ뫙¦š¥fjŽ©NU§êtSÍU3Lu†š' g–+êLSU¨ù¦:‹—1M8!ƒL>ƒç™¼€šjZhÒô}H{f`Ï ì™‘­1¹J$œßïØVø¢;ˆ u¶©ÎQ‹LµX-ùC»Ã»°µÜòx&—1têÿ#7f,5Õ2µhh–¡Î5Õ U)kVšj•ª’t$:Câ¡¡V›ªZ­1ÔZS­Sëi ·"-~!¦ÜT5jƒ¡Î3ÕFú¾¡6ê|SժͦÚ",ܪ¶™ô}ÇT¨íL#²¸ 9ôB“^¦W¤n¬˜4éUúž,õªÎT~5ßP`Ç.2y©ÈiÔ@w±¸µ¡A{Ô¤¢øâZä1U@5ÈþÌã‰.jEÒõø¡CáfKŸü:ùˆŠ¬Ô¹ua•çXËLú6.G?©¦J=Y:*hªj—I?–E².JÔ—&½)‹F¤.J°‰ÞИTPjui¨&S5«I?‘uÃ…3„3,v˜ôÓ Ô› ·5ÈŠÏ©›æ›ô3ÚR•e ?èÈTX?× æžh°Ù"né À|õQj{O³oO°¹µÙÓ")d�Ë[C± j ~Qžж¶´ ¦ Ô¯i‘ I:q+L‰'õ„Â1O} vÔ{iz²Ñdª°Ú`ªh&CsÔE *ª"¦ˆ<ÃP1SµªÝ¢tm¦Ú£ÚÁýÁSíUêS] / <âv/S—›ê u¥ÉÓx:ÓyIÒ¾*ØÜÒÐÚè‡BM’¥¦ ßc™z™'ñ„Cf=!0«Þ3=‘ìN7ÕUœkª«Õ5¦ºV}Æäù8š x„©®ƒYÁUÏ3Õõ¼ÌTŸU7`‰x«E¼ØòQISËðPS݈ûðé<×P7™ôK÷Ð~-‘ÇCÝlÒ/߈¿'ê“~5`“<êV“~-€a):'†ºÍ¤ßh}ì‡$Ÿ u»I¿pXüÁPw˜ô»‡É‚I¿p’~A0é–’x@3XÂ"ºð9“^¢aîý³)àÛwà&ýPº³©_Ü‘½žalñ4ÊTwª»â÷C¤ùlÛ™féÖÓÖÂÓÚâI;hFBGŠK<‰g¸qiú«éN† ¦Òÿ¡®GhGª¡LNÉ~–øZ%_.+N/ï­Lsà£B|9zÚc¦Ô^­¨]çeÉ13gJ²¥ÊöH 9¼„LË‚#뎤¸P)ú_#˜f~Šú(ýùÂй²0Äû)ööׯ!½ÇŒ.õvˆÚ=ØöÍRßÇÙë"dÐm‘ ¼Å壕‰ÌG×1X]˜-‹F}cßh¯‘kç—dTWCÒ&„²øÛâ@Úò”7‚ÌY]ÕD«{brGÝŒ,Î*áçp¦T¬VÁ;¢8[Þ?uLTG(’<-àRˆã>©¾ ³‰Ü(È4k«6® ÖE|‘ö¤jO´ÌMyv·CUQJmñ�˜p¸¸8kIT™eéÐÄ]«Â¾zÉXÜ™7¶@8utfŠ?ƒÚt·2ù´A×eÜ#Ž'óu'Ki^‰ÜÓúY!ñ¼”UzY¹(¿“Ï+ð.Ù˜:è[LŠNÔ´Gcfëy<Ö>À¾³ˆIû>p˜bá*Éa—ø¢Z™Ü'б6‘œÄ߸°4ÛÃXŠoŽ¿MÛ6Ô®]–vv"¶ŒÊ~e�¤ÈÏ|#c:ídŒËþ²&ü_Ÿò$7Rä1ðiM¿D,YžãFúáøbñ„þŒïüÁÞŸ}÷9¡"Z•›KVÊ›ÎJQª '+Ù?j0]´OÂNTð¡”Ç’ 'ä\ƒ`Õˆ†ƒ*©U©Ì°<KÌ°Ü '4º0•¢”µòàúý÷d²NîöI™ÞÒª4R.*&Ù~îÉÐgÅWÕdìŠZãšVÇo…�yÿZÀ ¨O¾NH·ÅÌ·GlJ.ÞïvÉfQR´*ÉSÓ–AÙ®wȲù:,÷Ï- 7Éqò¼# äDQè †t<t¼§-_³~qåҥ˪·W/Z½¬F~ʺ6–m^Þ¹Ã!9JÞ9£‰'ËJÛXXßLBO¶ûd™†šÊÍpSö¨æ?r‰â•úéTx—|hÓä}¯ !Èðf0ªãP<~ÔÂJ˜Î<‰®…äy=΃Uöx¶om²~¬ÌïŸHþ®™²Èúé2ÅgÇ´LÉ»S~®LA–ü¡2™õå¨,v®Tq&•Pÿ(Óÿà=<TDÏÑx#ïyè‘ZíXy‡Ðíwõ˜ä=E·¯Ò÷tûš†;¤ú ¦è9‡+)£7ÒF?Jý8môfÚè­´ÑOÒF?M½6úYÚèçi£_¤~™6úUÚè×i£ß¤~›6ú]Úè÷i£wRFc0þ½‹ñ1º–rȆ¶¼‹µ«Ÿ+=Nù]äô–ö‘+‡6 !2ê£<E›Êž?@Žê²çË=FŠì×tlœN^Ý*ú“‘ßÙøÎ!ŠÃæÒdšGet&ýQX‡Ãè/ôWML9½Gïƒý>ˆ“ôHÊE»W{Ÿ%såyŸ!³›†¬-µÕÏY Uô []¢\¶òçݶc\]vœŠË¼Ý4¼ºTšü#T ;#ŽÐHÝ)<B£J»ity'MÓ¨•€Œ’ä*h(¾‹1ZBNZJù´+ÏÅuWÐ,ªÄÅVâR«è,ª¢e´šªño­¡-´N_Ñ«ù¹-yÅmôwú®¦°âC\VC)ý“þ…k:é#úwüÚó1æÞÒœ^Ó™$ÈŽDµ)ü3è?(éÞa ¿èc ‘šîå±Yúår'níÞOCŽÒØÚ‚q=4þ$]]^[a{®¼&0õÑ)L¹îÜ>šÈ´ª¥çapwR…Ýë¶wÓänš²N-wÛãl/ª0ÜÆqÊsÝ4Já¶÷Ò´ G©ÛÑMÓ÷‘ÓmwÛžï<ðÉ¡.*~†”ô’·öq*>@vÌx5:Ãv€l9ssRim•á¨^*·ðšØoôÑ En{ÁLÌϲ´ñ(Í®--ÌuÛ{hN…hNµ=E§ÕæxkÜŽ^:]v–»l­v;…ˆ ¾n×10×C—±ˆærΑ–mìÔc‹Ë äÖ²ÑฆPÄî§qT:+–P#½ƒ¶RZhµS]Naº £ŠÐ!ŠÒaŠ¥­pempO{àÚ¡Ù{!‹qÚ%ZŠ×�ûVšBŸ°H°…†1CŠà?K« y+;è í 4½C´‘íp€vœ±– ô `ŸÆvbï4\ïuŠäúž¥豋óp×qôf_e“70KgrÞV¬˜î-í¡¹°É½tF…­`^/ÙMFÁ|öYP¤‚º—” cÂ>j)í¥…N·³›Î®påÌÍ+Ìs»ž¾:öÑ)nWaÞœ.:§ƒ†Æ»‹$GÎÂ4Ùm;N£Ý6=ÝG‹·�ÎkòøÀ'O x$ÀÀ»¤– 87g!\á•Ð2ïaZ&‡æZ%öR鹡%ãm½´ü §w”Óyç>v”έåì¡ šPå>«ôYZ¹†zKŸ¡•GhÕ*ô%³VãvÛz¨ “À¶ªV‹‰XÝjtíBXUÎ\g¡Ó‹{ƒÆÜB§u¨œë8 “ ·«Ö0Õ\ãÄm~ˆEv} ᔨ¹³‚¶~D+e—Wãê¡u5ºÓKë÷SéQªl(8¯›6êénÚTp>ÝT[áëv»ÄºõQ‡½b$ë@§]ï.NìÞ,»í©[z«S¶Êýºh‹æ]Î y÷XÒl^†“…ÎE$J7Âáß„™›a.·Ðºþó6˜Ì´“Ücù"]Mû±úKt=`·Ð=tÝKàßCPöNzæs�æó¢ú£ˆTqÎ!¨÷×éêâ|:ÌãéO§'¸ŒŽòô$/£§x%=Í1ÞJÏr#=ÇMô<·Òq¾‚^à[è[¼^äú6?‹¹èe~…^á7èU~Ÿ^c‹JoÀq¾©Íóm¸Ùsé\ 8ÒÔÈÃ_H,¸‡ƒ(#ÏИë£Óx$ærAq)Âìì 'ŸGñhrñL*×Xœ i(a7z=˜ËãðÝݰ ïÇ眠#ÆÈãY{yô&À´Y÷NÁ¥{bÐ9ðÈnžÈf<çY´È‹¼E QÃx0t*À“±ÃßÂE–á³ßŠ:<¬‹¶Š«Þf¹ê­ÚUo-Õ®ºöpÁ¦.Ú. .´l× ¶Ë‚ýôzùVgÁ|æëì"¿ÌÖ[³~=ë·Pæe ›:K»¨Qí°5êE´€ �vZ€ ;h�»ÐdviÀ®š@³�B Yš;hb…e¾Åšëù°E‡;;å¥ýÑwšˆ‰Þx$t“Æ-@òVƒ$-ŒÔ¬ )؇ô.ÁÊdtæa ±¡7U'^.ù‘$æwÆÃ¼ñëì§ózïIXçE°ILo‚]V¥H-|qô˜·Ÿ–Z¼¥|Ÿ&Â$Š‘X$Î5’£½Za‘øÉAqAŸ†½²û`Vnxµ'‰/ˆƒ™È)hµü„öÈ$öQ®WRŠÝU9 Æ—ÂcÍÍÎÝñs4 ÐAüÞ„¦³Zܺ‚wzi0Ú,Ý^/íéÄÍ™Bˆ¡íÈ+-,¤aø~ i|Bˆš‹iâê ÅZDÇba+»¨1n/är)毆IÜÇôL&h{)‡KØ Y¬¤3µŠLÆãæ ªçRìÉ•qy\r§£î }{p¯IјÖ2m³¤Q̈ áh`®(Ú«ðÙ»ú(]\ c¸¤‡.­./C§.C²]n¥e6·-ž–•HO§e—WävÑîÜnº²®R€¹v5ÓO^/ÇÆk˜ž¢ËûèZÉÕ>ƒÿ‚æiÛ½HØl9ÇuÈ»GÙ ¾)n[aîœ {Áõ®ÔÝ)QãºÎkr!«· >[‘«CD®¨@ò¾Õ4÷™�_s n8‘FÁMâ)ð2Et6Ô~ OCºRLµ\B-`t—"ñ*§À‹ÛáïáYô�Æð©tˆO×|k€g=›FkåÍEF>K§@6ºFè9;­b-,)zµqúóL`“$øÏFOÁ îã9ÀœƒäjŸ†9bˆåc!jùÉ/.Ôe€ˆè‡ øUvÑ ×Žë¦ûÛK[t™<?%ÿ’°pô ½?•ˆ£”cì”È\n¨%g§h‰3ŽÊÆgâ˜KÑr¡ 7¤e)–.KAGP$?ŽÆÔÆ)˜(§±Ò¦[zéVMҺśÅG G¹’ ÄÑ|^En®‚úU§31~Œ’¿á‰3'N§¤â·!ûxÏu)ûñý.ùs’øþz]y,ƒº}F²GÒ²Ù"ž¡ÙÝô¹Nî@çùÀR‹¤y3´n è¼ )ša˜=›ÏÑ'z☓%Oݘ݋üæÎôRèµÇw»äå¸õNÄé̵¡0µJ+”=µ«‘“–u¢R½+‘/D…%&W[‘ûœÛæMMÈÆ^ ²£TTë€Jìó’Fz%%”rŪj¿ æ ûCòùE1È©î\Ô0¾nãˆ-…? ƒ„0ªŒp’üÅÈý‰wB±w!ýh³Ccfs\F”¦r ÉI+ÍãÝ´€Û¨©ÇnG:³—øR óeúê qíyHݤ.‘:¾-YĶédCÄÕÊKPQ(°w“NErh*­×… ÌZ Ý´˜õ€¥ƒ¼×ÛEû:¸Í~qú_²œþ~íô÷wð&�¾ÜÁkÑÜ-ð{,øÝ~wŸÀW:xš{þU ~¯†ßÛÁ£¸¯ƒ‡£¹_àXðû5üþú€Žú+š~À‚?¨ávÐ�<ÔA¯¢yXàXð‡5üáêàQä¥h ükü †ì /xuesšCÿº?¤á‡:¨ €E$÷ïøc¼SÃ;;({^eÕȳ&eV¬ž˜5sI­­¦«·ªïWoÞwž.ù»¼¸q\ æ â=.)IW-===¼º‹ŽxË,5-ë¦î ‹TQ²©å«Ý4^µÑ$Õ®ò�]> çð,bçSx%l“to•V'‡ü©`ÔEéU°ªœNä?¹H:ìHÓ øb‘6œdK{)NÚ=ŠW®æ5z¶€×ÂËSâí‹×ñúø 3“~µ‡ÔÀ÷OšO]«UE xd¡¨ˆi÷Žë%ΎÊvà°.kiyýq¼!é§;‘ Ⱦ†£ÔƒËö>NŽdïHõ‰|Ÿ°äÛ§åÛWp½õäÑEGø¤<ªGkºé©Çi˺ê(«-x¦‡ž=–Dè| g¤Õ´–žŽS}ŠNF&#²L¤°2@½´~f Ø•àÈ:èI Ÿ'·E‘%ßMô ’×¾šMEô<0˜h¿IR¡Ñ äø?PK ���(©Æ@Îûñ•D��+��'���com/sun/jna/CallbackResultContext.class…ÏNÂ@Æ¿-…ÚRAQÑ ˆ—‚‰ë]ã…„Sá „ûRV(–6)[âci¢1ñàøPÆi©Äƒ²ÙÍÌ7¿ùf²ï¯o�Îq`AÇŽ‰v ì¨3”æRM£1CÉ¥à'<–·ôïeµ ¢.ýÐWW ÇÎïXkÈ w¢±d¨º~(ûÉ|$ã¤ÔÜÈÁPÄ~šç¢®¦þ‚áÄõ¢9_$!Ÿ…‚wDŒ„ww-I :Q¨ä½¢*^^è囩¾â#§õ×þÖM”ÄžìúéÔÆÎÒvömQbhþ»Ãáwfõ…ò—r]-8­!šôÛ:ý>ƒ–úR¤Ñ5°AŠIQ—òT±Ú/`íÓghcÑ[¡^©i’R¦¬¾¢acÈ¢ ¹2:Ulåž<Ëbû ÚÃÚ®”‰åÌÆ^¹ ÃvFÕ>PK ���(©Æ@) Á‚��Ø��+���com/sun/jna/CallbackThreadInitializer.class’ßNQÆ¿ÓîöŸd[PT¤,­‰“ªD%xrÑ»Óí¦,¶[³´^xåy¡‰M/|�Ê8sºY꺤Ü|3Û3ç÷ÍÌéï??(ãq9”XVXL–Õ®àa–²5–u‹¥œÆFR é´;ž€¨ñ‡Ó•ö‰€æÉ¶#0½*?I«%½¦uØõ]¯¹% 7ýNï£ÀìÈáщïÈÆPEjÛõÜîŽ@²´rL°j§A°É}×szíºãÉzKá;¶lKßåïàG­{âž ,ïÛ¶uÖó¬SOZUÙjÕ¥ýaèóšè®l¹ŸŸÜ´R]ôRMÅYŠÿ·Í'ë±'LÁÒM§{ a–bÛÙZ‰[P® ÌÄ_˜ 舑€u ö¿û͸g/‚W›»è2½fî°Óómç•Ëk]¸p…ël“Çu,–"Ë<Ë<ò¸šÇË$¦ò¸†i¥K=ŽÀÔùïê§ŽÝÅ"ø/ hH0™²ÃU$¾ŠdA1 Á Þ¤¯ ý.(¦Lm�ñ]ÕÍæ(&t¬b–²ü° ·0G‘ F@ØERÕ¦ÍBr€DQ¦K 13, œq£BÁ ì ¤˜1 E1@2J«Ð­'ŠvgXÒ23<¡¸Å€ûžÑxxâ®X‹‚ŸÒµM6‡…!8‚³˜3ÞiRYÌ_èD§h˜èf¡O+-öi+k}šfNë#{îiÐR€-rÚ&Ö3âîi—Ÿ«*CR؃ö`„=AœñËjt¾€ÛA7›¹J7 ó-4N)Huä=ôÐD§.ª÷¸‹{1˜lór æ>–b0©¯ÌÞÌD,&żƒy ê—ÿPK ���(©Æ@é¶ÑÌQ��5��)���com/sun/jna/DefaultTypeMapper$Entry.classQËJÃ@=Ó¦Ñ>Ôú,jm«÷Š›ª T]X .§qÔ”tR&I¡Ÿå¢.ü�?J¼3¸ "ÃÜÇÌ=çÜËýø|{pŒšƒV °PÕfÕÆº +™ŒC¹3àcî…\>yíÇñ CÁäX¨D(†ÊÿÛþ@ø äO$g õÆ |¶¾Ù#µvô@jÅN ÅM:ì ÕåýPhþÈça«@çßVòÄ ¹ ™¨ ƒ{%¥P†^Ðó^dž^œJo ¹w.y&]šæšFBíuéÜE©òÅe )«3uGºQ6]äa»˜CÁÆÃÎ?èJ¿çdÈ6ô¨µ?áØ¦%äh30-KÓÊÆSϘ'ëRvOYòÅÖ+Xë`ŠLëpŠì‹.­P'»OðѤ 00:ÀDZ†™H eè,–e”É;ôga %8_PK ���(©Æ@#óÆ÷I��+��#���com/sun/jna/DefaultTypeMapper.classWi{U~oÓIuuŠ$tB dXQ³7ÄÌ&†@4Ú4h"dtŠN‘.ìTÅêj$®¸¡¢¸Í¨à¬:#¸ ˨8Æç™ù2ç̯˜™snéêîš´_nß³ÞsÏ}Ï9ÕÿÏå¯lÄ_ã°1£à!NKçÅcy…8äU܈ü<ÌË‘fãôûˆŠGñsá OzÄÑžRñ´G<ó*žóˆc1<¯âx1†ã*^òˆ—c8¡âx5†×T¼ŽÇ¼G~U‡_ãM>ñ-^Þæå$óNÕáü†—ߪ¤÷;¾Èïy÷Vù£‚w¼§àO þ¬à}š–ëÌ h£–e8Ã9=Ÿ7ò ×Në®yض­Ã†ã1S‡ôÃz²àš¹dÊÌ»›š:öt¥æŠ ;ê`õŽœnMul·íœ¡[Ë<ÌLÊÓȉ:6k¹YÃ53ä°ÂrÖ5ZËÙÃYÝÑ3tšÀòrÙXÖvÜ F-טb‹ŠCR¶5âh$gë䨥œ¿Ã.ÈQTµ[LËt· ,éìÚ+¶'‰Û2-#]˜>`8ãºÔK¤ìŒžÛ«;&Ó>3êfMJÕÚTÆžNæ Vò¥'wõBΟ1vé33†Cé©›2Ü¡œ+³%ÐÑY‘Á®œ.ÉäH¹YŸœ/I+}”DQa²™¯§fŠÖVѧ�ô§Ž©„ˆÀ«„b$ƒh¯ªE8å[SÇ%«Ý8¨.j[DNoœ³í 3U.TV.ÁwÚ}à‘á ª1¼¼nqtÈ:%}aÒ³<ÒéºksP*s}˜Ä3rl!…¾Â`˜ª'8ÆF…B®C6PÍqdj½:£\­Ç\Ç´¦BA9²I`}¹ m»#vÁšÜy$c̸¦mIÍ\†^)QcvÁÉ#&×dKEÞûÙDÁi gXWí,5E•´-•È©§ä8¶£a'F4|€šŠÏ8ä8ú,£DÃÏðs 7ñ²[¨c²R?ûë÷»¨†)d|¤áclѰ•µêZÔ1|¢a šŠ‚…ž©àS ·°´¡(•]SÁg†ÊŽõû¦‚Ï5l/;Œ;§‚9 ÃeîdïTð…†,i,J¼î©à¬À†€yNØ9ç5|‰ RØ¥awkØ=æqQÁ% —qEÃ_𕆯9'ßઆ;ñ­†}¸ÚUL ¬^œeòŠHýûK›ÇFY¹¦ÛP"å í¤õiB ÊeX/äiß,ƒñ¬c?Ì“£¤Š\ÿÅ<ö¸ŽOìÙé?˜/’ó´)Bq’óghÀçÂô 8óæfÀÌŸ˜l\›•×ÿ¿þl‡ÜxëK»&O’ÉIÎ!êû¨™~¤„wv…7ÆD%—ržÕóiãˆ+§79ŠZ’hî mÓQW¶»F3?”Ï›SçœÄ•­n_i³ñ½ >ÁA³qD¸Îéc3Â¥N¿1®v’Ý Hjk µ­„º¥„*¡¶—PÃ%ÔŽ�¥ÒŽ:­·µ‹¾oý¶uÏ£¶û ”‰yľ„ZÜÆ¿ÁÞFk=)ëEЋQ¢4Ï·ã©Ge깎#í ¾ï9‹ºïÐÐ.béÄ9ÔA-qNC9‹ú9ÖŸFk¨ ˜Ýè±$»AZ.cá2i¢Â³›<vB²Ò²™…ÍÒ2D……Ë™Ýâ±—Köri¹‚…+¤eˆ W2»Õc¯”ì•Òr WI˶1ûG»M²Û¤åj®––!*,\Ãìµ{d¯‘–ëX¸NZ†¨°°Ùë=v»d·KË ,Ü -CTæÄÜ"Ž´~´"‰ÕØD7Öƒ#¨ßƒŸâ~»Kœò× €' ÞŸŒ¿#ðþƒ@ú=Aóß„QƒQ¡áv±)±»Ä6ì#Ø#öã.QÀ˜˜ÅÝâ(öŠ·p¯øâ5øËØ/þI§0[<´ùHä]ZÂ^ðÀðáþ"j½À¦îóP¯ c¢§w×]ÂõÜÓÝ3Ò}CË5a_QX,‚rhÂAºö];Kɺ=ç×B ïîÄ]ä¡+)9[dÕÑ0 +¾XXñ*aÙÖ ò…åPXîkoIXQz·{ý°ÆˆæŠ_ÚÝÓ{rí*o³¨Å#Ô•G­ó ŽZºpÔR:j‚,#<™ýNÅaô^B·@ºïzNamz®¢ã¦hkô<úz. ÿTÞ'çÎü÷_®¢ó'ÐHÿ¹Ûp7à)ôáºü³2¢A:¡ ìÇ/èr º.È(¢À/ý( SWŽÐþ�2~”C^#ã4ðÌcãUl*P+ 1XrýIƒÁ%Ì*Ý ”»{mQw1þ$»KËœ‰î‹s©+XB};J «ùFzŠÒý#%¾û|×Ò»™„]æ&ð rôëM‰¦åjQƒ¦ýÿ¤öÄÿPK ���'©Æ@s?g=8��õ��#���com/sun/jna/FromNativeContext.class}QMOÂ@}[ …Z>/ÆxL¬w’ž<H¸/uƒKJkÚ-Ñ¥‰ÆÄƒ?Àeœ]ˆ0f³“yoß¼™Ì~~½�8Ç¡ Jè:8pÐc°Õã½`Ø—|Íý˜' ó<¿`¨\ÊDª+†î`çu8£ÒqzK¥ÍP&bR¬æ"›òyLL;L#Ïx&5Þ’¶º“9Ãq¥+?/™p?ÈÒÕ„+¹ã4QâAQÛªî55SÕBMy¦£ÁÁð9Ý›´È"Hݦ·cy¦+<8è{(£Âpôï ­ß×󥈈* †3œÐÞlÚ#ƒ¥(³è:¨S£, ¬wô6:}…õl4.ÅÕm½~Òv±G¨·QÃC0Yƒ\&Z[Oß` <zõôcW1dߨxÁÖ†¾Ò¨ÚßPK ���(©Æ@ƒ€T¬©��� ��%���com/sun/jna/FromNativeConverter.class;õo×>nvNv.F®´¢ü\¿Ä’̲TFW Ÿ¬Ä²DýœÄ¼t}ÿ¤¬ÔäkŸäü\ýâÒ<ý¬¼D}7¸bçü¼’ÔŠkML@CóÀjB* €† k «qÎI,.) Î/-JNuËÌ*‘@1·,µ¨$µH¤‡‘A§õeŒ è`cd`d`b�&Ff ÍÊÀad`‰3p��PK ���(©Æ@F9Ž5��á��,���com/sun/jna/Function$NativeMappedArray.classRÛnÓ@=ë¦qb6´IK¹Jo¤N5—¤ ŠT©UZEá§³29våK%x烠 ø�¾ƒ‹à#c;Ц)¨²vvf<çœÙÙýüëã'�7qÇÀ%̸Œ+ X(BÇUƒÌbb–t¬èXÕq¡àÊQžp柷m`…±gõ=aíˆHíËm±·'{M†ü=å©è>ÂùŸºZ‡!×ò{’aª­<¹º2x*º.e*mßnG*‰‡É\ôB… å£4ƒ@¼dà›ž'ƒ–+ÂPR‰9"»{v¤|ou HÍNˆÀ!ê@ŠfÒ•±ëÇ-7T"Zúƒ^ï‹}¡cÃDMG£ë78 œáX‡Åi ·tÜf¨ž®:éÑÂm9ðJ.ˆ~ì‡QK¸î“´W>N'íY®ðëQ·/íˆ.Ì‘Q:†³Ö>,H“ÍcÚYot`Beþ®zEqÕC¶k5k›4Cs+™^!”QG¸1A×Ì­ñÒ1¶äüÝýSp<Å0{ÒØ°Hï¸�†"­JrUôî5ò9JdÏRdc’2ÀJ½q�Öø�íõ{LÔï«ëoÉÒ¢?˜|“‚§ÈV#÷ JÚWT´oXÖ¾cšòsʘRïI2úæp~(ø€Y‘Ñu¶åŸò öeígÊË3ÄWÃ…Ô^ÄLšÕèËéñŠ´WóÅßPK ���(©Æ@•×Ûü��V��'���com/sun/jna/Function$PointerArray.class…RMOQ=ïµth™Z(X-V,‚2LÅw~‘˜F’6Å1,tã£Lè#Ó™dfjÂÞîý n\kýþW~D£õL© I‰‹wﻟçÜ÷îûŸïú�®`%3(e`àl)Ìe(ÎÅ¢l་ã~ w´§\£fËïTîWÝõTõž¯½È n¤njOG«EktÊò¦@²æo;¹¦öœ»ÝΖ<P[.=ù¦ßRî¦ tlBóÔYµu(`;ݵG³îyNPsU:Œ.]ëz­HûÞâá²L¨`‡ GmÓ°bN™ ¿´œ5Cfÿ^ÞUO”‹&–0eb–‰42&–a›8…ŠKóÿÅäh‡sÖŽì_F5åº÷ôÌ£æôˆGå(õ‡w¨¬Æ`–Љþ†ŠVãØ(YÑ¿T¯Çá™QôPæZà:ðäã×àIÞ'`Rfi=ÃÔ×íW•~òq»÷Ò®¼A"q«Té?GÁ.1ÔîUJW÷‘|*Å‹_’Û=AÏËAÏ”sÄ‚üˆ¬ü„¼üŒYùKÔ+ò+®ÉoÈ1§L´1‰)V—¼Nƒ[ü¸?ü«ÓCv«ä/¨'í´ýšŒbÑßÇØ?ÌLÜE~GNþô7ò‡]%‘b9KLÐ'ÙoÓ¬§¾JÿPK ���(©Æ@‹Œ¢���×���'���com/sun/jna/Function$PostCallRead.classmŽ1 Â@EÿhÌš˜€…,´q½C `¥(دë" ë,dgá<”˜lÄ)æÿÏ|óz?ž�6HÆBPu! —«!>º¦Ò&/¬!¤yú.¯KuW a®ÝMú†eÉJ~Ï‹½óu¦¬=ô¤ä'n™M•Yå½ñ„iÇ’VñUîÎ¥Ñ5aötCAû‚Ö 0êwˆ¸Õ]G £PK ���(©Æ@‘Ñ¥Y!��sJ�����com/sun/jna/Function.class¥; xUÒU¯'Ó=3M�C L.E x„p’€I* aBF’™˜L8D×õ\]/¼Xƒ7«ÄÛ5 F/ð>ð¾]W×ÝÕ]WwÕ]]õ¯zÝ3é™tÀý~IºßQ׫ªWUïu|ö§‡÷�Àe¾Â.»Uìqƒ‚»ùñ°{ñ~<Ê=npã^÷ñû17lg(7>®â>é†d|ÊEýýü8À§]„õ ·žaüg¹õœŠÏ«ø‚¼¸[Ãùý’†/»a$¾ÂƒÜ":Ûñ9 _e&]ø¾®â¾Éo¹a>ÅœßVñßÕð=7äÒ¾hø¡?Â?¨ø±†Tñ<Ÿjø'7½?c¬?³Ñð¯Üùœ×ý…ÿ†çÇ—˜ŠÿðàWø%Ï~­â?ÝPŒÿRñ7”à·<óŠÿVñ?n˜?¨ø_”â*þÄTÖ¸Á'¨ ¡ârZ‡PܰX8\$ŒpjBåŽæ.áæ&t·$’ITc$Eƒyd !R]Pk ÕÄ0U¤¹©?ÜõÆàMŒT…×MýQ.h4Ó5‘¡ŠL7õG» Ù£‰,UŒuSœ 6ƒã5‘­Š Ìm¢›'¹àLc&G“Uáã™\7 æ¹à\c&_ª(ä™)nœÊƒÓ41Å=‚;Gjb†*ŽrÃÅÒ'ÄLMÍ“EÜ™¥‰Ùü>FDz&ŽcBÇ«¢˜;sx¦DsÝbž˜ÏšXH~ J¹³H‹¹SÆrMTpg XªŠ4QI"ª4Qí†Ûð)Mœ¨‰eÌx9ƒ¬`àIdbQ£Š•nè«Tq’:ÅÉqŠXÍ€kX¿¦‹µÜ­e>ë4àá:U¬g±êUTÅ©nØ+6ð£ŽlŽUÑÀÀü©"¬"MîM*6òû4ÏdOhæùMDTѪŠnxIlâ¡Í𨢉әÏV~œÁ>t¦&~Å’Ÿ¥‰_s¹&ÎÖÄ9Ü<—Å;Wt>?.ÐÄo4q!í3ºˆMù[M\ÌÆ¾„;—² /ck]ÎÒ~Ænº·©X®á-ª¸‚©^É+¿JW«âUlWÅïØ)¯EЗ†ƒ¡H ¹¸¹Ù¿…º¥¡P ¹¤ÁßÒhARá7ÊýMMuQ¥á–H‰¿¡¡2à_‡à*/^±º¢¸rA–" * ‡Z"þPd™¿¡5 �Ð0è%«K–T,›WQ]º¤‚Æx4¹¸¬:aÔòâªÅ«KJ¸« ®^X¹dùê²âªêÕó*+—Tò¸ƒå¬¨ž·`^åêêÊç! -;Õ¿Ñ_Øà­/,¥å¬4Ï"Q¢@ó‹ËªJm®mö7ÓÒËjÃ…-­¡ÂSCþBc‘eÆ$áéu­¡ÚH0ªð7R-Ä«"ÍÁÐz‚qÕ’æ7ø×“–ÔpCS+Å�m Ii7|ÉR^ÝêÒŠeK—V,X]>¯zá’¹£kYÉÙ$G6É‘Mrd[•Mº·°•a®U[B‘ú@$X‹ãg{d—Y)x­µ‘Öæ@öœ-•º@s TK+È8(Â0Cf—Íì²—…ƒ$ÀˆÄá9ápCÀ²A˜³%BtÒ‡«êÃÍoâxI½¿Ù_K^gÃÄ4Ÿ “²ph½ “ù a?1ž8>7ܺ¶!`3aØ0&–U)Ë1gnržþseŽ?Ä,éIî „Qý¡xK­õ×n ˜æê/ò(C.?ÔdLè~ZX²öÔ@-émŒ V¼#N:ôŠ,~æYˆÌ7÷Âìœþ§ÿÈ中Å&_w”„×xKCŠÖƵæj¿4hjY˜6ß2sûæ ÇÜÛÆ–=æ±.w~NÜŒ©ÅeÅ&rL[„{Z3wΆ‚‘c¦æ"LÙ-gIB«Ï²Ìj!´–º``)rüææ0 åˆÔ)^ Hï@B‹”hx,^·®9ÐÂdjëµØIŠÿ!Ã9Ã]ksÏÛ\ˆL•<Æ0Ù°œÉvqvØVr&S®qCÃyfN¿`¹Ê2bøú¬Éý‡Hœæ�9r¨zKS@,næ>ÔaÁ/b“l¹bAD(Š·1±±ñär› RI‘ÀfÞ®ñk78%�ÁŠ– ÛW®G°uF®ŠÃ‰í]‚Mò9}¼­d[k†–Ú‰Oˆ±9üRqÎFžÎÈ8’¬^¹,^M²4Y´<©S„e!بk Š…Ægc²~x“Á‡àJ²YH®Ù°Y ù`kC$¦.„±ë(¦Q§…pt΀&´˜y¥½Iž*W!å&xRÌ­›ääðxÓ,Í2<;¾ssJmÚ>ä(MÂÁE¤œZcÍd·ÖFÚ<«ìÖ[:°2£½F2ãÁÉÎéªí3Ó˜`cFJ nìlâ7);u•MQ”ÔdBµHå#hM†&Zú)¸OEIk9qIŸ^Ø,Ež­ÑS’¥Ê3ز´9Ød™ÌÊwXÿí?™¼q¸F9ì醣ùy²­ WÚ†=Ǧ '?-Žb›Ä¢i{’-S;“øì!íÕ’cl/"†Éœ†P¥ìXöj (í6€ŒòÍŠ<Õc@E«9[°ù15ÄŠ;[¸¹1¦F%ë œÖêo £¦åØ@“ “ÂTU“Kjõþ–z£îH%O­õGjTkiÁV=“ms…º1Фյ64M•œ&"c¨JžfÄ/W°%F?3gàÈrâFÞbcj[›ƒ‘-±”:‹£_ß|E¸ªµ¶Þ `…ɶž—ëý ÑagV[K)ß ”o#r))Ÿ”G�Õ´Î@Ql”Ê¡2xI0λÌ#ƒ­%»•'›zn²M(›§ÆK-'*‘ùáÖPœÄæ)d™Ùµ f­E*UáÖæÚÀü ûÖ h,`zºhÃ{©¶:m:ÙYõ/æê ®¾t<ÐÅq*®×Å âF ÄMü¸™8“>6Ù˜ñŽÓ±çij ç2÷¯U†(VVˆª«¬ÆÖ–HV(ÉZÈ Ñ·è¨ã “1EÇÁð“ŽC0U;Åïuq+žÇÄ®Š$Û%ÚdNk]<!I€¬†°ÌòY­MYÑ’1k’.n»HߓвtÑŽóu¼i_gÅöUeV£‹UdUÜ®‹;0•ì{xߥ]pb¨9P^ žX—UkTY}u(s¿MÜ©c)VP°OÜÀ`ãVßÚÔDGÙÀº%”ú¤_[Xe–û7[³ü¦Ħ•žÁ–¬iGÎPÅ]º¸[ÜCŽÓT¾Q©â^]Ü':¸v¶Í¯”º£UR~£Y&(6èâ~ÑI++¥ü°Q*éø;¼VÇf¤Ö0L#tâT@œ ˆSµÒÔñ$qÀsUñ {äC:ÖŠ.]t‹UìfQÄcÔà[­¼8»³O=LŠ·óÝ~€½ºxD<ʧy»â‰tj;Þw~VÍ–.öà0Ͷ^Tébï¡�ûê\G°²²eáS—íw–.ö±šãÇã´ƒÄâI]<%öë8Rj|�"„¸GPÅÓºxÓtô2p2[·€­[ÀYKÏê¸]<§ã(žÒ7kÆH/Ïëx'Þ¥czã>&ÙWi€õþ‚Ž ›Òk^Ò¤X630ô‹:f2ôÐ>hËÕÍPK(ŒŽ2ÖK:ŽN9v©3¤ß=‡µ9:ŽIXQ-XVÀ:n/3‹WtÌJX†Y8X–!Gt¼Rd”WuË(ƒûP¢E„e÷C:^-^c¤×u—€dD:ÏÇ t/µcµqì6f¨Ma¯cv?Ó÷t aXÇ <Ÿfë4:NäÉÌÜÒÜoˆ7uñ–x;A‚(ñŽxWÇILi˜•RôöIïéâ}ñŽ9RT£¶±.œ÷Cñ‘Ž“y~x¼‡/·€üAGŸ H´ÖÔÅÇâ:^$>AÈHHª%T%Y¬^)èY?eØþ”Ž]Yë™ ý‘Úú,ã<—E›Î®³š-˜ ³‚¡¾T¤ã<N?!r)ñ'ñ%–þn)ù5…ß@³%vý™¢ƒ]±ƒý‹*þª‹ÏÅ:žƒçêâoœúÿ.¾ÔñÜ©cn‚sɇãÈ?ª€¨}tñ•ø:Îe£7²ÿEBÔëŽÿŒÛz±âB·Žtó=l|À%<3-þÅoøñ­Žy,rz¼M­É„j«‰¶AÖæSBúªo?f1»ïtÌgvcŠétÏ’—FÍSïßHåC8”z 9œÕ­Ôëâßâ?d q@ß‹d­Ðç1r,•³çp­ð_<aT< „œ+9ËÉjŠú€ô¶pÅÛÄâN¥‹tñ“ø™ ›Œ>0*|sèø7ùø)›u$5DÎ\¨+ŠâЕ$Å©+ª¢é¸ÆÅ?¹•tÅ¥K©ûÊï‡96ÄÕ¾ö§„Üÿá, +:W-j]¸Ù¸tq-^âom¡vžµâ¯®ooâ{Þ¸¢¿o”ïëº(5n¥Kå7)ö“Bۣġ>ù‹a¹8͵øª7ÍŽ4C“ÈU[׆bw¬¶ tnv48±:yׄÖ%(Îæ¨d­Ôùú“8•ÿzþ€ÓwsYn`ë·´®yåK«kV—/%'Ê)=9k\ÚÒ ÐYÏ%wTm¸iK‚*mCòÖxPÜ=*HT .vGxÛ ÉþŸûN—ƒƒ-Å--Áõ!ö¾!L( m/bfØÜ�ý¢ Ü¡9vÇÕáü½¤ÿõ$‡ãc©9æ0 Ëû¶÷ÍC_Š&^M,Køäò?R–Gﺘ¼ólŒ|˜‹o[Ðü­‘°ñÚÑ,_O0oø¸nl ‡(Šw1ŽªÒ•óŒM»z™³h Û²Q­Mëü‘@,óÄ%¤e‡QHßià|k²Þ+§ç t¡Ïz´”ÌÆå—£ºfé<yŸ•è¦rm¶÷ÊËž*å¯:s‘Se¼JÏ!Š4xˆù 1ÿPsb.CxBM}±Üw¸mbU[Jì66zÈœgÏn åHÙE«<Ðæd3r€/‡ËδÑz.áëißGàcã>ö~Åç¯,œCú 9¾j[4ÙöcĈœE+ígRú6‚IÄ+‰Øo‡É˜Ïngæ&Œ'äþ Øï ´Ã9€ÝW…9¿80â»[4ÿ ÔÏl–£EÂÑYr¸˜ømÅVË.ŽË©Ð#ŠCØ–Ñò´Úøž2,g²Ý•9E¼TËþX„LPCÌ"2ܦDYÉú‰¿Í·ÔCÞûevsÖ-7:ç' P#aÓ±§ Uþ~ªRa +9|?Ÿl©Š*,ß(=–Id•ØÎ”›ÒþóJ§›qO$¼0°9Š®Í?´"ª áÐz³MÒ,â”Ê7 }Ch=ëäGߨ\Cœè—ØãmLÓßC&¼-øƒjOü¥Q!Iþ¥Œ7l€lðÂð‚~¦ž@�/¢¥/¨¯Xú…I–¾“æUK_ƒ‘|ä¥öH>õÊ1Ñ;•¯Áåߥ &x€Á|.á†Ò¸‰Ó@ã«T@ΰÔ××óÆõFÅõÒãzq½Ì¸Þè¸Þ˜¸^V\ol\o\\o|\/;®7!®71®7)®—×›×óÅõrãzyq½|Ko,¸°� ©?…zÇŽ½]¾pævÚ!M4•žN2@N£çp§&£V²4“:Â$µà¿tKR] %Òª—´² ¨-·I‹[ì BR=Ò¤:…ú<çô)=àJ¤¸IRÔ œGIÙ p¦‰},8¤äî^p×øÒ»Á“Há"ËúÜ&…¨$4Gc‘Ië+¢•Dïõ¾nÐ}™Î_uà¼0¨’kRSºað^_îC0Ä—÷¤ú2‚¡¾ÜaØCæËÍë‚áÁˆvÈ/rôÂÈš^ðÖtèÔô.È ¹ŒÔLzx]0šÞ]0¦²öÞOì§ÀÑ0FšÏ�Þ7í$Æíà; î¤ÝzŒ†»a<Ü9p/atNauÒâ€:è– <‚D?rpÎ&|ZƒÇÊ¥®—ûe‹ !d‹—¯Èåg.ÿÂcÃ'—Ÿ!—Ÿ»tZäX×Õ;L=Œ3ôÐãY ¦ÆÒâ}Mʸ?¦ÿ‰à¢ç~"{€–ó4-óYσ^€BxŽ‚—Hä—->s\Lèãðx²Ê–á3‚?ÓÑ“EÝjZ}Vú]z4óã¿t�‰—Þ£{aBÌIódŽIRû9{ûÄL•úz 2àMoC¼gq™Y1‘fa » Î¥¶ƒXL ö<œoj¯PþQ.@’ïAHMôÁ-^œdD\€ M䩿P y¨óWw' bAWc襸ÈD/5-ç!G$ i]09Q‚Ï-ZöÄ–äÁÅ8d« ËY þxgUÞ”"Yy|E޼ý0)oÏ„[ÙrkRóº!oÞcRñ:ègOä§î†)öÁÔ¢¤üÔifgz‘³ŽÈO=Rö»`Æ U™¡¥i„´“÷Fšæã‡×éMJS»á¨ªó4lÿùãÜ" ‹\0ó�¤¤ÝE5ÀÌv Z3É gµÁˆÜ˜]äöº‹\^wS¤µC–7i?¤{“rwñhÂE~ìuí&g‚"æãÕÒÔ.8žÐhfš×¹†õBqM®/Ïëì†9EžvHé…膹E/ÑözvÃ<ŠÜ¤ˆ”ž4OIþU^šgZ‘îÕ€³Þ÷êÁü6Îï;௾æwÁÂvxœVç™ö”¶Á%²¹Jw•æøâ6¸É_\4¨ÊxÁ寂Ëä‚˼zTÈE/0›KŠ’™ÃRú)JQf NìM!¡r¼Dà„´ÁõçwAeÑo 'ósšwHTU7˜TûF;tB5³9Ñ`S-ÙT÷±‰ïeçy'Ùëîó¬7àHzþ‹<ì*.¾%_ùŽ6Ñ(T}TdÌ¢¿œkµTXl¤÷T@œEÄ9T8܈.¸•J†ÛÑ÷¡¦º1£ µŸvýK8 ÞÅðŽ‚O1¾¥4.(]§PêJ 7“ÒìxJ®9”Rsé9Òç,Jšó)ž@9â,Ä NÁœŠWá4¼ ÚñìÄ#qe‚W¨ý&Àwë{œ-’0[x(ÊðNù€¶ÿ*‹KhËkäñ\Š'Ð*ÿ Vbõ 2[û`ˆÌ+CðNÈÇjÂŒ7@žH{+Ûa .Ãå‚-°W` á‡q%®Rö7’‡‡÷Yt_RËÜ—Ô2ö%·¸|RÀ)4<‰Æ^Å“ñ ü q?®Æ5d‰Ø…~\ *iùr¬%*éú\‡ŠÆï’êˆ/•iüÚØçÎE„G‘ÚqÊM>>¯–ßFy«V°sÔαB:ÇŠ]”î¤`OŠ6ÞJBiw”è*ã$Æ8ÙÀ8Ibœ´ ÆX0NiÕA)Ž’ýjÆDHk \?ã®5pý׿ 2z¡¶ÆŠE7¬#Då5ˆuŒ¸Þ@¬“ˆu±>ñªn2âu„qª¸ Ä qƒDlŒC¼²BŒ¸–0Âb#žf 6IĦ]Þ ÍqˆÝÐÂx>Bˆx­Œ·ÑÀk•x­o“os7l!<ñ5!œnàme¼3 ¼­o«Ä;ÓŠG û,Æ{˜~màÍxçxgK¼³%Þ¹V¼óºá|Æ;‡.`„ßH„ vQž—J7\Èp+ î"†û­w‘„»hŒ3à —Ä!8¹.®ñ&uÃ%Œ7®.e´Ë ´K%Ú¥y2Ò 2P»áòŽNØÆPWPÛ$Ô6*Ø WòÜUÆÜ•rîJcnllEy=pu\Ã"Päõ&oÜ£_äð:öãvb¯clï‚ßY`Oï„k™K›ÁåZÉåÚ(—x:y’@ì`ÌãI3×1êõêuõº]0ºM*ÆãMruÁ Œ£ÎŒs“s£Ä¹q”'àU79½NJº™ùÓI”WMÓ¤²iÚ4Ò·‘=?ñªÄî ·0‹‹[$‹[ë÷ŒÓšÙ‹,Nr«\/œ’ÇU,g‡PÙ ·ÅÊ«]F¥Ûžz»YéL»¬µîØk-ÃîŒÂÝE _Ü-ˆsöz±Ü‚ãè´T\)¸�&Q4Káh\ 5X­T¡\CQó^ŠÈ/b%|†Õ¨R´ÍÂåT5® +q3®Âm;( <kðÏè§â¦VxqÈÁ:QŒëE9Ö‹J Š“qƒXƒ â l`XìÄ&±O/cD¼Ž­âmÜ(ÞÃÍâ3Ü"¾ÀÓÅ׸UüÏPÏT’ðWІg+ÙxŽRˆç*GàyÊü² /Têð"eþViÁK”x©r9^¦ìÀË•›p›r;^¡tà•J/^¥<OïƒxòþNy¯U¾Æ6åGÜáxÃ78áŽ!x“c Þ옂;+ðvÇYx·ÌS—A²(§|SO93I¼Lg£z Rëkj´”:Ê7œ’”`Œ‘ϔۡ�OÅ àT:`"6`#¨Ê˜mÂýÅ9ÁÌIÔ⬃²ÅÙIÈç:’<œW¨*翹0ËÒ yžÊd7J§ wÏyP)'zza{ÍØn¸·¯œp3Iì £Ùýri>Y—gÆŠÕÌè‘Z'ɤè¦E6ái$€à¿É2Ø&]AéŒÎQÊÖÜŒi¼WöÃF¯“ü±HÅ"ͶôªF òª\JRý8”]=™ÊDu7ÜG¤æÕöÔüý0´:j|¹ùÝp‘K‰ÜÊä╊Ì©H”|S|’q7<Ô4ÐaŠáuÊñ:¯“jC¦Ý]^êÅ68›;E.bµ,¿ º‹Üù=ÐÓ%^÷ǺTźiUÄù(*Hyh¯§vóÂ6¶[.l÷m z= í2Ðlç×%Wïôj̽·ƒå{¤X‘|ôÀ£r`O—È{kxpe‚}]𘜸86Ñ;,O´ÁHªøy|Fï'%¿§:lc¤WÝÅÚÝ/ ŸnxºÃ62ÆA>C?Ýðl‡m€3!KÈçè§ž'ÈòEò ù‚éü’~™~ºá•ûúØ�¾“eå­ñÈVïA†~Õ€>(¡JõÎp§¹Û Ø«íq¬y¶y”zšNÝ0ÎëáVš>]Žßd*°ê<Bø{R~KNàò>¡&õ5/5eÚ:j¸(•±å^_Î#Ò‚Z¼ëpo¶Á8k~‹ƒ4  Ò9{Ó’,NÚoï€"+Æ;Œá”aý]¾Ðx¸'¥¾ß?Ž;ûâ¸ѳ›N½=´±wSü0 Ã^˜ŠPýºŽÇ½°ƒ¥øTã“T!€&|ÎÄçá2|®À—àZ|nƃЎ¯ÂÝø<ˆo@/¾ {ñ-x ߆çñxß…ð:ç~ˆIøºðcÔñSL¥È_ˆ¡êÿ œ‰_R~øU¾ßÒùà;\KUÿ©ønÅŸñ\Ê× BÁÇé$ðžpâBNáÃ…GLº(ÉbH§ŠÁâ41D\*RÅ•b¨¸^ í"Mì£÷~1B¼,¼â 1J|$2Äg"S|-F‹ïÅXñ³§¨b‚’"&*b²2]ø”"O)ùÊf1M½× üòŒ¡š¼Iž1¸5äyB…aÄVÜHuýç07јž‚rÊx[( R)‰§Ó˜ ÚIw[)hâ&Ê ÷uq=gà™à§Â òT¢‰Ó`»¤ì—Âéø+ŠçnžÑ°K-3îSë,yÃ->m(²Å'‡lq.H¢ü±M¹À ׊?âÙRfÁ©ef…ÅD‹/VFäJ·’+½ðƒè± áîC̰ÜÅŒˆå‚’0ÊëŠóLÂÕœèá³=¸Ÿ—ߟmD1¤‹91æ²ÿU¢Éá&Z¨“Þ¹¾ô<Y"•M”)Ò©VJñ:\™]ðQQ%7jwÁ¸¦êKo£I –’ÆO ýWÂ`QÃD5d‰e#VH!Ž5ÈDŽȥ¼\3åqë72çj0ž’ßQ$’²ŒüOb^»º‰¹Œ²ïý03º‘?æýú ¤ò&þ£ÑÒŸp÷S£;¢þdìæŽ(ÒgýfbëJf?'ƒ*NÙÂo¹ªZ»ªºkJµÄ4RºÏ6a±‘®®D½Å>é1Õ¤›öA*8.6‰WšÄGûlOLœjõDúa ýÑ1ú£côÏÇKLúÛˆ>×$s}mÞï4º“Ÿ#mQÌ»‚²º›²úãÝêq:%¶Zêž¹1IæF7 µØôŽ¢°p)m120^†—›Òh®~¬Ïö|j¬¾¹ þœ¸Åα(`lŒíؘ¶á6,l޲‹M]ð—{X\xWâU6,lN½‹3»à¯÷&°¸ì0,®ÆklXØ çvÁç÷%°¸ú0,¶ÿ¿bÑŠE×2!ÿï&‡ëÍ;ßE¹¾]ä¸;÷�EÞ»‰p%ãŠ]0=w¸ËóèðÆ¸›a,5ÓŒËá/¨ÐÉ3Nv#¸7â®%òŽâ0$n¡-½‰ßÃHq+dŠÛ`¡¸#vœI¨äcIÅ$]$ý’%ݻܞm^n§ú aH‚¿­ñuÃß×$:b‡%x¤š45¼¯7)½Kp| ðûè êÛs#Ôø|\—åÒÆâ¥óÆ*ÏÛÅy2·‘¾¤Ÿ ßžü=kä=µï©‰/ª©ÍÍ|¾µ¦bßAm¬ò:ûú6g¼™{˜²c/EìGH R´ÞSÄ^(T¿‹Ç J€“ÄÓ°Z<û3 ñ2`Þ(ïëüIª'æàÍÆ üò†z î4ú¹ù}c*Û2õÊ£ðU|]FÕt®/ã»àŸTTß]ÖƒéW§_~edCyà¢dó/ùßÈ÷D˜ ß>øV¾ à;sÓHß ^¡cäA-^ƒ1â}'Þ€ñôž(Þ‚Iôö‰w —Þâ=(ÈE.¤t–LÏßS!€0†¢ü,¼•Zãeë6jM’­]ÔÊ•­vjÊÖíÒÂSñÂå…߉w™ Ÿi~EI%O GÈíòïĈý“ÅO’é ¼VR¹ï5?U˜ß<S}=t ,ë…’˜ƒ$i¯Äác©°RD-¶ïœ ã} ¼Ÿ?aƾ²v⦜ÓåÞ£:„¯üD7´<�OrS‘ͧxkcßÖ†ãɋЉáƒr!Á÷ôGb,¦ùÿÐïKôûý–Òï.X�óñ{§ëÿ�PK ���(©Æ@ÎϫЦ���ß��� ���com/sun/jna/FunctionMapper.classU±‚0„ïGuÑÄÍ'Ðź³“˜�<AÁŠ%Ð’ZH|5À‡2Â`Ä.—Üå¾×ûñpÀ܇ïcFX†­Ê­Ô*áµ ·Q®kvk+g ·²‘Ì 7÷ *yÇYÅUÁŒ¸T"·,öªÏÁnT¥ÖHU„Eª[“‹PVýñúˉyÓ³ö„Íö¿ ¬~Ÿ§¬ìià`¹„ Ü!aÚ»ïPK ���(©Æ@šcЬ��b��*���com/sun/jna/FunctionParameterContext.classRËNÂ@=ÓP,¾ â;ê£î5nLHHšhظʈC`šÔbü(šHL\ø~”ñN!Ä`M\ôÞÛ3÷œsï´Ÿ_ï�ޱ›F…I$°®Ã†›)l¥°Í0qÛS^(}Ű\õü®{ßSn[q·<ÄO,´înªmþÀÝW-÷¢Ñ^H‡ ©šâ‘U’§RÉðŒá°¯£P)ÕÉáÜo †™ªT¢Öë6DpÍB櫾Ç;uHý>­ðNÒ<{±—<à]ŠàÜW¡xÔ#²[†©–Ë£U³ÅÒ_Ëf¨q¤A.KÅRìÞs?û*ƒ;0‹%º…ô•ß <Q–zÖÂ_siMìØHaÂÆ$Ò6¦`3ìþg/†ü϶k¿ÆCù F§4K[ô©“ô˜0´ U†ö‰2YQ¶èÉ`tùTu×È¢óæì÷a8}˜N¡ë%bÍR\&U-¬Z–Ø9,`s„:>æ AT-’/‹*ílD•ö6 ]"•³KYw%œWÏ#«d®EÒö a(ÍÈ<C6ÇÉë±äÍû›l=‘7bÉù¨kíPK ���(©Æ@óNñ‰‘����'���com/sun/jna/FunctionResultContext.class…RËJÃ@=Ó§­Q[ë£Ö⦸÷Rèª(¨tãjÇš’N ™ˆŸ¥ .ü�?J¼3-EmDB2wÎ=çž;wòñùöà�›Ed±R@5ýYÍc-u†©ÛXºÊ $ÃRÛ NK§/¹Óᇠö"†Êu»Ïï¹ãsÙsλ}á*JæŽ<驆ãú·tÓçQt˜\0¡L£C.ÍàF0̵=)ÎâAW„W¼ë2ß\îwxèéý̨;zÚN´¸Qì«f •xÐ=N‡C@7ÅPžè“=¡ZãITë¿fañ4ìÅ!ÕZ¬7§R¼ âÐ-O÷ZKìk_Ë,±a!‡¼…)¶þ=ÃúN θòîÅ8¿0y¶èÚ³ô/¤è!?‘%­iz‹˜Ž¢Â3´–ìW0{÷){ïé'£˜¡ï¼É/Rµ%Ò-cUÌbu´/&*—™H;¦L¤=Ó„V°0rthÕ¬¬ýŒÔãØ&gÀš)m £Ò4w²ž§‹×ňUýPK ���(©Æ@.|Vü¨��ë �����com/sun/jna/IntegerType.class…VYSUþf虦+AÂ…ÈLb4à¸E‰[‰ÉbHP¢Æ4C338tO¦{HÔ¨q‰û\p/«¬X–k•ËT¹<Y廯>øòè›ñ»·›¡&ª¸÷ö¹ßùιg¹wþüïç_�tã3 Id5lB.ÊU^ Bö¸XÂðþ.©˜Ô…%¤¶/½DiQà  {ŸBPÒÐ G…«¡eÇT×°¹¦Ä<-†b8©¡O„/ù<)TžŠàTO«xF8õ¬†ÓxNÃóxAÅ‹*ÎDð’Ð}YÅ+¼* ¯‰áu{C 'Å𦊷„œü SÒ¯ Á*OŽš%̓Æ1£«`XÙ®=R¶]A¤l9ù¬eŽ|HAø˜Q( Å*Þ‘·òî]$Köw sêµÇ¸µr0o™žþc´` b;c†R^|û›Ë; ÖfìÉ.§luMXFW¿åšYêMMÚ'û bÎbnà,Çt‡=GBI¹š4œÇDÝRÙÊ®p7âÚ{ 7Œ –dGàl{G'ÌŒK~m¼dO.`ú’Ë!K|ÛY÷ÚtsÊÝ^“T!ׯÀF¿å¸†åæ©i[}S³(Û«@…‚™5 ;2Óq‚ FKZôÛ\Ë šñÜQ¿‚¿<±G'â«`Õ’¨ô Ç Ï[ ±­Ov°>¢ÛÊ.JDÜ ¶�í¤Ÿcv™i]”ÝËt™GËF9ŽÕm‹©¾”sdž†ÜRÞÊVçÉ“ —r†“óŠK²Ë¥Œ¹3/J¨)P.[„žŽ;Ð'†·uÜŒ[t܆ÛuìÄ}:z@¢‹äéi×ȳ:nÅ6VmÀnÎ.¹boFÁUÁDJcbãÎ%TƒŒÞÇœ.Oi)[ž4-·’T­ÕGL—ÇÇe´œr±HëæXB4hOBÇ;xW ïéEF˜yŸ^-°&d;&º§tÌâë&í˜cNÂKt"cLÞN$yÄÄ(ít$D¯ÜÙ=Å�V7¼ŽRñ¡Ž0¬âcŸà~«/Ó£ W*tžª×°6»‰|eÛ”§út©rÍPжÇvF¡`gH\»Šåa<¢ãQÖñŽèÃC Ö}õê·Q,Še)®ŠdÚ›†ä"ö,n^Ÿ\^zË«ÑKK©wÞåwcµÈh­Ñµw™S åãÝU«Þ›ª»…-5]Ù¡K*Õ—4Zæq/ú³ê Ýv9WCøhm‚‚›Bh¾Vu¢%äÌþ‘3»‡óJâXüïâ×=sˆ¤Ú¢õóP~È»9jR~Tl& |Ê$;–cDÚQ$[¯Ï6@¶zŸm} ¶N²]/Ùn›X ë$ï½ÞÇ›¨åe7y·^‘Wœßãíóy'‰O{,5ºTÛyÔ§b¡ó¥s/i• ›éÂ-ü‰p+C¸ùmÒ`J:«ŒU Æ|ƒb%Ž¢”·˜gZù‡œ*çt<ŠŸGCêGÔEì×ü÷Ünàÿ/›öç…ÿ©š…žŠ·@=E‚ø™·žO§. :ÂÕ<´óh<‡”î¡ÎJÔ ¢ÎÎc¥D š<”54UÓÍï–‘ø<bòsôŽ\Àê‘y¬i^;‡uÂÿ9´Íy¿úWñÕùãš·ÊMkf ÚOo Ç£3!º!>óš¹þ½­áø©èÌ,z‚”LÀºøOØÀ©yã"}ó5\·†½ ßÖbžó‡˜Þ:–·Ê(‹ íbfv37³H3ñsà0 Ïpç9Ö îÇûÄD}‰½øû0ýø ð7â"†WÒ^Q ù¾Èý]ÌmXäo!ó\‰|+¨SîA?Wõ\Ý+µ.Ù*@˜'iü¾ârƒ‘Ôºð :µÛWþ—F8›@žƒÖù6ÍáÚtj×ÍaóoHö„׆Žß÷„ƒ‘L‰èIX§°¥û7,Û§kM¼N®E‡œûزž³eU𔿰y~OUà1IT‘Q=*²‹Ñ_/;%ÍãlF;ö0²aî‹Õ>ÙMf¥cL<Àx*r5Ĩ ëe,EbØÂ~#"‚t¸:ˆÇAŒT‚ø òõ»eóC¶ZÙ¯«´O´*Ú|H+ù««ä¯á›*åS5óLJ­¶ë[¾­Ò?]Óu¾‰ýº ~×wUú/ÖÔçsêëï'Z\Dí¿#9‹5‚†%””lݳPC窯É+{¿½’®v2]Šø)Sû|[«SófMÿÆ*©©Ò¿±Ú“™šú¦DÿPK ���(©Æ@â^uï§���û���"���com/sun/jna/InvocationMapper.classmN±‚@ëCurÐÍÐÅsg7š€®.<‚wäÍÁð£Œ0aÔM“6mŸ¯ûÀ C®‹a’p¹UµŽd™jµ‘*ÎÙŽs?Òq­”È”»Æ­ÙOC#ÍÍó3YK‘K•眣R\žuì-þX?íatЕ‰xæL˜v‰@›e[B˜}>øÎÆÝÔ>Ìš%‡@°Ð‚lBv«ÐoØ‚óPK ���(©Æ@™Æ~ÈE��@��$���com/sun/jna/LastErrorException.classTÛRÓP]BÚ4\¬¥Ü*öðËE@B9”04a’Tø_ø^|À-ŒÎø~”ã>§¥T(êCvÎYgïµ÷^g'?}û  âè“ѯ ^<àï‡Ü<âæ±O<Å3 ”1¤`ÏùfDA£^Œqä…ãü=ÁÍKOʘ’1-ÁÇlÛ²'¬-&AJH¨Ù¶ìŒîÎ2ÇÑÓ„5„‘ä®þAïéf:¾äÚ†™’àɇÔ% “Íe3›Ì^Ö7÷ $­”¾·¢Ûß@OJx«ûºí°"wWø*sÙd9w”äN‰J'Slß5,“ûexþ2ÕÆi¸#¼2)W¨@wÇp$hÉ”•‰;Y3¾kêñ¤î¸“\žÒ•'M¤Yþ(/Ce8BÚyH+âR–¬¬bSï¼ñ*I/¯@Æ+ ÌH].h<»½Íl÷ðZBÓ4s‹áˆf37k›lKS‘Ä,7s*æ±@ié&MK;ÐMÆ‹XRÑŒ íÿPNÆ²Š·XPÑçí")7T¬`•ïU¼ãTkXWш& mIBËEÂŬéVr,^ØÓ]>q4ˆ†³j˜[Ö#ä\–Ô¬Ö÷÷™¹%¡û¿¦%/ÝSk¹Á-žz]+P=á²3wãK˜.K3›¢ÄüÒöšA¢¨¿ŒòÙ² jPq\Ýv©IwçšpêY& Øáü6éád7C(œ(÷¢ƒ¾äý'$úÐÍЪ ¿sBZi·N¸Äÿ#gŽÐük§¨ xr¨jÉ¡:ù¡sÔ{žPLn‘­ñ͸MVÍs¡MdRø°òÌ‹ �ô ¾3('Éè‰p–á…¿@U‹J²!ðᎠ Ñ™ž¸¿Ÿð»¢!ÂùGxDä@4–ƒz†šSÔÆu9Ô¡.æ‰näÈáf2£ì_<†o6ZA‹ÏÄàG±b ”„øEp =´ë§UÝÔZ?D]eì&1y]¼±Ü'?ˆU„*¤Ï‚b… ãbOâG¿ øé’vƒ%ÚUhx{Ý…àòæ)j£­¤Ü)‹Òñ‡l#Tø(j0V”ŸœU+î„Ñ#âzPK ���(©Æ@<KCÇ�������com/sun/jna/Library$1.class…RMoÓ@}›¤v\Ò¦4”¯*·$ÕåT *!YæªwÛnä좵ÝÅ *àÎBÌ:A ¥¶´ãyûüæíÌþüõõ;€]t븋5÷\ܯ£ŠvyèbÝÅc†V’ò,ó‡üŒû)W'þ»ÁP$9Ãrd±ÀbAh9=†…þ¥ÊOE.†­ñ‰ùY¡ü¡âþ«4yšJuju&T.µbpÆD†ÍÎŒd?7Äëuo¨R õ‘`hDR‰¸ „9àƒ”f¤žr#m>+/6®ËÄ:ß×…:zs‘ˆÖGÏ2w­Æ?ê}]˜DìK+èEr`¸¹Ü±Dž`‘¡ý¿ ëSJ¬KÒkq<&£›¶ðÔE‡a•ZPëj]0©éÓY@Þ*%L© 2†¥©îŸé4®¹apµ‰ùˆ༔Jæ{ ÕN÷†f³m=ŸÁÁ©Ñ綉MaŠ2¬Ü`m8péfU0OïÙ­Ã>ó¸5“1Ê=,ë6e1Å Åæöاèªï¿ öì3æ~¨a®d4èÛ)‘X¢µE˜C:Ëh–h+¸Cq\£ŠÕÒGk’·Ëj�Y?¿PK ���(©Æ@b|A±����.���com/sun/jna/Library$Handler$FunctionInfo.classRMOÛ@}cB‚ù†€B)i 8`z¬‚"R ‡Þ6fC62k´¶ü~'$ýýQˆYÇôC UmyÆ3óÞÎÛÙýùòüÀ.6ËA­Œj.>¹øLp{B_†ÒÖÛ}q+üPè+ßÈn(ƒÄoéÛ(‰Šôñ�Ö$”º©lŠPiѵ§ÚïkáåyÆŒ©øB˜sè;w‰nl…£©A—4Q¡ÿMÜ0¶¸§´Jö #­ Bá0º”§´<I¯;Òœ‹NÈ™Ù6K yYeã<YHz*vQçÇoñZZKsŠ8–ÜÑ{ÓÕÒ݈°ý—æ¶êaîë9¹þ'¶éb�Bµ1”ó¥iõ:w»„Åw�<‰³{ôd¢Bù,JM ”Uîå¨;˜$4þWaú÷avú|V.6ËÿX€°0T$anH5¾*¾5öEöqQÊüÊ™‡ÇÖá—åó ¦8òÙ“ål?ÁyÌÊÓl‹Yò3l½�³XÌ–™@•Q–ü•+NN¦áäÊ�ð‹<Še|àúJ9ø˜ÙU,°¯òæ°†yVlÿ*¬{‰c ô PK ���(©Æ@doÆR��‚��1���com/sun/jna/Library$Handler$FunctionNameMap.class•T[OAþ¦]XYJ (R¯Xq[(ëýVâ ØÖ ‰¾M—‘N³mv·$þÿ&*Qã³?Êxf[±ÔBB6;sæÌ9ßw.3óë÷·�îbÝ‚…[& R¸= ,[¸gE”ô°bbÕDÙÄš —!Ýæ†LµÅ¸Û¥ïÖx§Â0¾.•Œ_0䜡½â.ƒ±ì í&•¨wÛ ¾æ Ÿ4¹jàq—‡R¯ûJ#nÊÈÄ=s‹«=_„ ö¶R"Üðy‰ˆ6»Ê‹e ê¼-j:¤rÕ ÚnÔUnKq·*!ßúþ…!s 8³/âA-ös¢Îcy ú@•^R>Wûn(Þù‹ݚˆ›Á^¥8°µ‡Ríºé÷üòg€RÙÚ ÃâéT•˜ÅdíÝЛRWÎî#¯iCÓ¸Ïý׎-5){½‘±acÊÄñÈÄcOðÔÆ3<g˜æaX9GuO�¼j´(Jpà¯wDh‚òÈŸA@ÇÎÑÇhá´QÁ©½6Î9#;2uâT2LzйTÑKA]šwªÃ!WŠo‰—`–Gíþ¯b˜‘nÐ¥²@ŒôçtÕéâ¥H¦&Ð8C« Ò�ÙÒXé;RoVŽþ ãcbš¥qi›d(É¥E@ÀÅžf±�$’gô]Æbú€l š—V¿`¬^ú£üã)| ²ÞÂLá'.–¹r‰‡1´IV”@'á+õŽù–ÇâÑÒU\#ï1š¯“}šä› ÞE©S˜£×fú…™Ã%X�PK ���(©Æ@BÀÌä ��E��!���com/sun/jna/Library$Handler.class¥X xTÕþof2ïeò É„&,èdŠѬ$$: ¥“ä%˜ÌÄYXJ…VmµZi£­]‘Öj ,Y”JêBEkk÷]ºï­m­ç¼73™d^"ß×w·³ÝsþsμøÎñ“�ˆ©V¬Å^ ÷X‘„½)hÀ>÷Z±÷ñç~ Hø¤)Ø+ãA’ñ)¶BÆæø´„ÏX‘î<‚Ge|ÖJãç¬X…Çd|>_À%|É ;¾Ì‹¯0ÏA+Ç!ž}UÆ×$<ÁÓ¯KxÒŠ9è–ñ”„oðÖÓL×óÃVôâ~GXùQÞécÛûy6À³A+Žá8†¬x'Rp§$œ¶b1žåçRhv&óðMž=Ÿ βÔV2Ì{ßâÏA/0ý‹©¸/1Á˼<Ç×û¶ŒWdœ—ð*ßï5d\”pIÂë2¾cEHø®„ïIxS@ªqûZ½j@@©õùÔ@…× ªAZV‡}-!ßWëkó ¤E—õîNu»‹vÊo¨ªhÚÜÔÐØ´®¶~•@NÝ÷6w‰×ík/ ¨m^µ%T²F uø[—×”5ÖT4TV LŠìTÝxSY]#­}îg›Zçi¸;¦×µø;K‚a_ÉŸ»¤>þäMöøBj ÍÝ¢j& dÄi×¶ˆHòw±Ítš¦Ÿ†Co ™ÏÚ"7¢e;`Æ(}Õ£N‰>ÝãÛæoqÇsÌÅQ;æœxR¢:ÈË ÏZ)pµ3ÎÒÆPÀãk_ž`ûsóÖ ˜+ü­*ßÄãSëÃÍj ÉÝì¥[éõ®w<¼ŽlšCÒ:s”‰÷åF‚Îòzš}R–’`9•{iZá÷mS}|QKÎhWCõ£Ã5Ó™7qÀˆ%²ª×N!#•DW;&¶™£H£ÑµpD¶’¤Æx64o!à-‹›‰ó·èžþÑN‰X;5Aú-’»þt}‹¾©YUhg£ ‘Ô6ü‰‚’Ÿx¬­jG‹ªÃXÂ÷ä-Þ’LNÆ… +²âTÅ虹Ñ´¨Õ†„ÑTÌ´ TÜIŒ#8Û º·Ö¸ƒ„7­Ø,� ?PðC\ÄæŒ(©õzÕv·—nî$`Ät diy¸­³%›Äíõ´:¼ºÆcŽ‚ã't šœgM*~ªàfÜBH±-f—ŠŸIø¹‚_0ƒÜXLn,&7—yCcÁªà—ø•‚_ã- eÊE-±c —ü¿ªÒoFØkW¬êw ~?(ø 6Kø£‚?áÏ 6b%a4±‹:#µ güâ¡àCp \IÐcå5c¤ÜÄt̘¨Ü(hF‹‚¿°×cƒ„¿×28±Réùìï¿)ø�6(¸€¿+pñìKø§‚á-oãßþ£à¿xGÂÿ Žp^)fÉs"I&aæO2‰EHBVDа*"U(Š˜$&“µæ˜èÉ£ˆ4‘®`¶+"aš ›"29Ôé bP±žJ”!¿04¦8(bŠÈ"Š‚^5-ê­a·—³Ò QF ¨ üäë<mtTR¡n#-]oqT«3Ï¡[-‰ljVx'b}|!˜›XŸF"cŒ³¶©#àß®×öô±)Æå>àé¤t6¨„yéú U§Bƒæ“X†õ”]>F^”œjŽÍiР²Œ|°P`~ Ç$—Nsé4—Ns’•¬Ô 潇•Ñî –ƒžvû¥:àïä®’@ǘ4Ê\*ÔeÈ2#wUm³³–ohê ×Ò‰ÛÍDrìãvHT­ñCn_ Åx帯„ŸOØo%­Es‹š6^+ÃÇÀ5™$GóÔDÑkõ~¨x‚Ú,B8E»\¾—½‡þqSƒŒ™bTqéÞÔdÂjµÙlçÆxYå~¿WusWÌHؤ>^MPz•frä8 [öB ÷)ž =¾ô¦?Ó9þÃq(èÿÍÎ 7ûúEêˆzwÞr¤Qpâ@íÿobìA—'] râ×o‚@£ “P¹-&1{<uzºªJ¿+ÔX€œN£÷ŸáSm¾N …¾¦]ä”U‰¥Å€/!I $gë…ŽOrù$7ÚRHe47¯7ðÔ¦Äâ6Ñ®LÐb6ý] A¿S0Ÿ ô3{¿´Ñ¥6~æh#½Ä´‘Þ@4&ñSF[ÓcC[µ} ÉSÑFßv@lB2ýÎå@ä!É5�SÌgQbK.臥R7æAv !…N­¶Ô~(t¢Ø&ѧ“v*¿°éCÈp ÀVß‹Ìa¤Ù¦ "Ëu™‡`¡Â~dwC2‚Ùt¹È6õ¦%aY4‚Ý•e@Î1L7aC~Qfä bffåÙ®ÒhOÃчÙùG1›¤“±sXÛ\Þ*²åF(æõaþaíúô-G:}wÁŒ# ·á*ìÆu¸•¸kp'ºpÞý؇q/¥Ù¸‡ñ�NÐÎËx’°Xw¶`+ Í¼è$òŒÝž¤ÍØñ&’y |ð“NAó[uG£„F¦O&ógõÄ ´h›4%ŠNQ"@0¼(Â,3s?Þ7–ÿ‘8~9ÆBØ@yúXæÇ •ÓËŽ¨ˆ9iÝD¦-W/œünäD‘O�ÐM"<LîéE¡NÆñ¤C Oíë»ÅE¦E¥f»ù,vs?J± Yvó æ?Æ Ãû{D©™›]¥ÉÏñX‰îbæÆþ!,v‰\ÍËÂA\Ó‡%D6ÿ,²íÌ7Ÿ"ž¥&ôa™Ý|ˆ±š9(5¡Ôb· #…L[NSæše·ÐjE®µ›Ê´5ƒÐ܇•,a¥íºÂ:Ù »Y_Ø“ÏBj©D£]:Å„Kº!r¯¯×ôžE†6C™=¼º–ô”iRûQÑCŽÞ.¦‰éääéÂÁc$@» °À“°â)ªO£�=X‚^”áªq”*BÑ à†(dÏàÜ}8‰‡qŠB{ñ,q†fÏÓ¯¶a\ x/á2ûmœ2^YxUÌÅk¢�Ä\Kñº¨Æ0jè§e5æcvÎc)¥R'A‡�Mší¢S¡Í>BU)I›ÝFö›È®Ë؃ïLj”“âv‚Ó”|:6/’|†Þ=½¨ä¢Q¥J­hTÚªM'°ª5Gà4&¨Ü`&—Éð°Qc,>„É«‡Pç²­@ý©Ãš=»ÈúÕ‘8Ì"Ai=›R¼œ’|UÔÝT]÷PmÝKu5š@{(R§püîÒ¾w£‘Æ9tïòç:ðŸÐþËdnÿuó ÎÚwPK ���(©Æ@ð6,NŠ��z�����com/sun/jna/Library.classm‘ÉNÃ0†ƒhÂR–¶,eq-3§ IÕ¦Eœ"L•*uª,E}5<�…JAââ™ïÿ=¶Çóþñúà Ç ¶Ôl3€aÉ”RÄzÈ“D$ v”.ŸB3Tœ–k:¶ç>´ ï®Ùjm­s-䲯uÒ8ý †²É$å2íñ0*v6òâ«®­O“ïTì1låži÷½ùÇÝgØÎÝŽÛîên·mxM˼¶ï ÛUqÀPËý¦e9÷žsykènGÅ!C=wt²LûÚÓ»GU$©8bXèDYì‹« Ô¸<Æ<žœ~µÃPõ£¡–dRH®åÃêo¯Îã@ø)Ãú?OÎvþÓ¾r1ŒDcÈG£/ZyΤŸ‘üQÖ9Ž|þG«&iœùi‹ƒ¾ I÷—yF/húœ„æá“@Shø‘Ó:¡Äh°34íêP1O´@4ƒE⥗‰— ¼B¼Zà5âJ«Äµ¯O×麉9ŠÊfÉÝ[¢¨`þPK ���(©Æ@ÀýŽHS��ÿ��%���com/sun/jna/Memory$SharedMemory.class…RëjAþ&Ù\»¶5ƦޒƺÙÄn¼+ÕZZ,¢‘BÿÓdÒlMvËîFhŸJ¡IAÁð¡Ä3³Á–¶àž9÷ï;gæ÷Ÿ¿�4ð"ƒ9T³˜‡•!Q“fMju©Õeà~ ËY¤aIÖZ#i<”ªÂ£3$þT ùVÇÚÁȵ÷\n¿CÏ?XaÈ´ܰ/B§C¹/× WÍ‹’›Õ-mÃë †¹–㊣áŽð?ñyr-¯Ã[Üw¤=ujAo÷¹/ºQ2ß¹®ð7<EË€UNWͤ×ë"d`M†tÏqùÀ9$€¸)IÍìx#·lôEç CÂl*ªqªaˆ‡TzíÐwÜ]ÚƒYmíñ¯Üpw׎¼m{#¿#6I{&Â]–y:.#§#CÇu<•¢ŒÛ gû¬z=á븋%ÏðœaÖ05‰Ñó½¡Aü« ¥ÿÌKË<ŸAË ÔÈÚ¾>m„ïï ·ËP7ÏÏs~ÄˆÚ z>óôÄCFçÈO#-«$ÈúŒy€¢UƒYLjYµ#Ä 1†&µ„ÁÇH~§Œ $s*û2X¥~¯q kX$ÏÅ© ®á¦B)R¤H> %baÕúOÚ%•c]µÐ£à´…&7?-Ü&’ : Öq<BRžÄM”xy‚ÔI»¬bñ†Š6UKqYöUwˆSZ…´8ét…S˜T-cK?‘Þ>F†¶‘`&§“ˆ“ȼDb‚Ùog†x{jˆ¥)b ÷”4qC±cêN®#öPK ���(©Æ@Çežì¤ ��)!�����com/sun/jna/Memory.class¥Y xTÕþïd&³ð²3 a› $#,Ê!@ˆ V}I^’ÉL˜™°i—¨µnU¬b,.¸€J¢àZ­»vÇÖ¥«¶U»j[·žsß›7“Gó}Ì÷å¼{ï;÷ÿÏ9÷ž»¼¼üõG�ŒsÝ‚_ôÃ/ñ+Tãê;NüÚ…ßxàÀ»ª¿Çï;ñ¿õ ޹ð;~þžÅ\ø#?ÿäGWÿÅG,þìÄ_<(À_]ø˜u>q|cü\øÔ‰¿¹ðwVû‹²ø‹³øÌ‰Ï]ø#ð® ÿuálÞè?×¾dñU¢Éε¯5×¾!!ÀB°àN"ƒë ‹LNÜC¸YxXôc¡°Èb‘Í"ÇM"—KýYäQØÄi\Êgáu‹QÈ¥,ŠX d1ˆÅ`CX õŸð³(æê0—î#Äé.1Ò%FyDÅ‚Ä{nQ"J"àÁ…8ægxpŽyÄh1Æ)Æ (µ-jTk\ µF¢ë©ZkÑÊ‹i1g}{S“¥RÎü•구= •-PÛ¦ØcÁ š€¨p´µG›©œQRº”^TF5î kç¶·ÖkÑÅj}ˆZ2§ÃÁøtÒ(©ażù‘5´Tù½¡d·‰/~C¤µ,Ö.[VËtóˆÔcsBó"Áp\‹’Jf¤©)¦ÅŠJjúT²Å6˜ 6‡ KªK{§s†µµçiZT/ÕJ‡í­jlù¤¿jŠXýú¸Vi7ª@WS0L°¬èl ÆÚ"1*9BšÊ ÁØRzÙ(#UGsj–ÖÕÔ¸FMŽ5æ+ ©³Y‹ë|ýê<VÙ¢5­ƒ¢Øe´Æš9‚rTBj¸¹¬6 †›Éè �™ÕTBs•Ô¬¨¨®–ãR¿_dИR�VTX0ܨ­£…´ps¼E×®Õµm+jõze¢^©×«õj½^“¨×èõªD½J¯ÏJÔgáÚh0®éÞU¬çO… ½¡²…cÄ ÌÄþ·D¢q½…lɤ–ê°Q¯Ö»Ì„›õ†½KU(¢*D練Y‘v}^QYà¡&cdfTÈYž˜12¤á`¤,ùŠ¢*ê¼}¼c=ü%5u¥½ ‹}mÓBи;c ÷i8+8:<øíü¶B«Ç‚ÞVò[Á‰™¡æZÙÌQ‰Q¡Fgî­‡…{ñ —U²%– µÏ’í™XJd(½f+{ûÈ)ND¡úƒ:îíŠGJù%½ª)ªq(ÚÈ»V5Š4P¯© !cñðÔFÚ£ ZUè§§êX†QÐÍ)Ê4‰q BR Ä3,IR iÍjhf´¹½U Çg¯kÐÚâÁ-¥3™I功W6k{,î¯×üÍ”Hä ?Þ¢†ý´h„ Å™ ~‚ñ-ƘÄ_Ø_ؤÛ4;P$ ¬&&[n¥Gâ~UçÕüŠ/Îbq69îçe%¦à ¼©àjt(X¸‚kp­€ïøµjDê2ÎNðóó×K“é-IÁxpV. ' L¨ÇȦ`C"âo•þxÄ/WG.Ä[4T[Ý®Åh¡2iõ9Þ½³ªÄ»–¢iòÇ×R7ñè¼…V›Y\…+x°:†§ ¯N2¢r}¥ŒVnuX.—~}±/÷ ŒÔ•üiiô$˜`ˆ÷¬MmÐüårX§Ñ<môš¦àzÜÀâV·±¸ÅVÛYìPÐÉ*¬ÒÉ*¬ÒÉ*¬ÒÉ*w㇠vâ÷â>÷c—‚hzà!<¬`7ö(b.Sð¢ Å^ELå Ãã ºqÐ)¦(b*+Æ“ Žà¨‚gð¬‚çp­‚çñ‚‚ñ’‚—ñŠSÑ·ÐôÓÅ9 ^Ãë´%fPãŒqëœb†"fò2bó—Ðj Ï#:�T*bº,“g™¦®ªn¤aÆ×ÏUc-´³ ôOîôF›SÌVhé˜#pZ/©Oëh³ÜÂhö–ŸÝrj“;j¦ÚÖ¦…i‹Ý›ÞqMæb;¨¤æo”ô¶“Ë-soó}v´×V×Í&ÓÛÚi½œ”jÒÂú•ZC|Êñ-¥Ç7Y’â\•3Ö¬e•sg.ºX§ÈµÚ o]œ71Þ§JykÎN*é«y¿xd®¶.ÑÁÛ[ˆ=¿Çø„ÔxS$ÚJçά½¸bIUÕìE´gx“ÃU¡±sŠÏx±õᆖh$LyÒ(Ò_b9ö•Zê(¦S¶ÊGi äå—OмèÒs<ÜhA+©6’žüóì‡8›À²}TXÅmÔ˜@ú©%„V£Ïvº:dÐs^ ßØ}î-;uŽåy™Ýp øÂu�îÀãp»·lÃðÃð,?Œ~Ë»¡äeu!Û×…œ¼\*t¡7òŽî“¦2é0¸HžOæ."âZä`1а”Z†�.@ –#LÒ¸yˆ M:0OºGãÄŒah=Ù=š¤È”+$Œ¢+0‚Vó¨ÑyœÒK%à tá4ŸÖ…ü½ŒÆSÓ1i £Å ´UÄÁ·—bÇ#»˜!©àì øŠ»à=Œ‚å_7 “yä È› |"*$¦óK³ŠMºbƒŽKíTÊ ò¬Õ‰Å¢uÓ»-ƒv`(PbPôu¡HŽ€óhÆ´Á¹þhd¼în,ÔáÑò1ðÊ ÂâM,·lÅ ~êP=Zñ¦‰®ÐŸ]-phÎä$,'AÔÐ œ¹§1do`ïU6±ë›mRe(«˜¾–#›ä¥äñeða#FÑF4–¶ ‰¸³q%Í…«p :È»«)¨×RëuÔ²‘ä 2&Ói@aÖ‘†ƒ4‡a=•œÔ6ȘÓ¬¸”Ðy¨¶˜±Û‚ïPÏšæ¬oΚ.ø’³Fφ[HõÖ^fÎÞ9 €sä�YìþAønJ…$P¶¿Ûɺ;dk X–9 7›` 0w€CÝ…b«E;(aîJq› —S˜l–Dè°ݖI¼³w®”ѼÿvL&ÇY#GÏé­pÚwÁžaÛ•–cZB'3%m†%äÍKç‡{MI:e™nØL7†[;ïíÓ ;Ï þ.z—IÏ:ÊÃ;9!F˜ qº±.É„yÔW¬²[Æ(ÒÐG!'¯„µ‹U]¿ÜÁ@Eî—Ÿ’é ’‡iú=…Ñt„Xˆ§i{VÚ ËÒ(~—¦1¯ uæ´¬Ã÷Œ”®3SšŽD†š:Œì ø¼ŽO°‹Kc¼v/ñ—ZçÙ[Dð6òéXÌ”gI"¯IäÅ‘7áfÙËïSNØeé*9$ù­ùjƒ¼P’ç#Ѓ~´•þ=‘÷i…ü …¾Ð¤/4é ‰~‹ìQhÒ¦Ðß–ý+ýÇDÿ ÑšýN@{ôc{гÒAô_ýWiÐßqú­}Пك~¼…^¸¡ D¿4è·€~{ZÞŸe¥/$úD_”ý' ß‘–÷g[éGý(¢/Iƒþ®>é;ÓJ» VòrxÄäÓõáTÒ®3Í´›h¥ŸK¾W“ï5§”vi¦Ý$+ýR¢_FôœRÚu¦™v“­ôD¯}Ó)¥]gšiWn¥}Œèã§”vi¦Ý+ýf¢¿œè¯8¥´ëL3í¦Zéo$ú›ˆþæSJ;ººô³ŒÛÃ=’¸Ó’§y;‘)î9ÉY|'îéqºq/!î; ⽸Ï@¬2³>Ê ó+æ¸ÄÑÌl3ÛļŸNP½`Ž50gX1_%Ì×N‚ù�ì óLsæ æ1Â|ç$˜ÑaívV<bÁü0?: ænì9‘•Z0?#ÌÏO‚ùˆyŸ›c`æ|û1«Ã�½·'¨Í·Í‘šc‚映Òa3‘úqrˆ~‹#Ùªr]¦‹C˜“eEË•ÎæE¦­�Ù¶" ´ 4O‚bR 1¯tC(#øü—Môû°_æÆcxÜ _Huî™ð¹%ýn̵z4ŠèJRnŽY&M–I“…t4æcf7àµIpO¼Ú’í¶I>Š­ü¤‡è$̇ñ¤A°Ô Èå©éHp̳rTÑÌAŽmn G®É‘kräÇS’ãŽK ŽœdfÅ|+Åb¸lKhH–¦P?ò\:Dx¦x†ñ½PŒMP,°RÔEQ4¦Aq™¤xμµ,1¾ä$ó&Ú¹VŠ6¢XMÑ4(ôKÅóx¡o/NïÆB+ÅF¢ØD›Ó ø‘¤x/õíEi7γR\O7ÅiPüXR¼ŒWz›RF’éÆùVŽm4¥¶Ó”º3)õªäx ¯×Ð~Èn”yí[ySÓ…E•§0˜œÚjPcb§ìÆâ¤ ÜÝö�¶Qd{#m»1Ö¶'e%(3M)3M)#S®“†–ÉdåëøxÓŒ­þ™àŒÄ]u‰~WuÄR*å-K¹º^ w³|̲¥^ Ï0 ÿ @⻈M®ˆŽ¡±ÜòÂv�v[WøÛòk£üLÊÝ?Pg¹¾Ûõ謪tá§æwrÙº+¶òg¦ ÉÍoï‚ç0.¢ÒÅqÉcÖ—chG¥ŸI¶Ÿ£A¾4CPÛÿPK ���(©Æ@Ú|ðF��æ��(���com/sun/jna/MethodParameterContext.class•RKOÂ@þ ¬EA‘—&€zð†ñB‚!5Ñpñ´”JÊ6)­ági¢1ñàðG§…¨ñ°ÙÙ™ï1³»¯oÏ/�ް•€‚\ È«(¨(2ÄFÂ8]†|sÈo¹asÙ7\ѳ…é­°V%Ô‰%-ï”á¬Ô4‘1ö¥1”ܨûÒô,GVo>±/:C"W? –Û JÍé †dÓ’âÜu„{Í;6eRMÇäv›»Vpž%o`v¿¸OÕ.¹ËiáÖ鉉Gí²Cf~£$ÅÝ>I¥ç´ÌµdWLH¡AkÄï ¯5»¡b©üÛ%®ß5EÝ ú-Ìïí0àkP±©!ŠÃÎßóÐÔó&ù;øÇÛ”ÛØ¦o Ð·`ˆ½P¡¥b‘2qŠTÒ®Wöö7ŸÀ*9å‘û—òÄ2ÄØÀ2²X¢Óñ”2#IÒ ¢¬†:RH‡\kä«P}t¦®í>Zy@äîÃ,&ó¡‰6ÌLÙ¨ì;PK ���(©Æ@­šÁ…����%���com/sun/jna/MethodResultContext.class¥RÉNÃ0}.iCÂ]…¸Ñ²„·�—JœZ�•'7¸mª4‘ñY 8ð|bâFˆ¥Àƒåñó›yoÆ~y}z°‹ZŠYL¡¤£¬£Â 9ð¯Ê­!¿æ¦Ë½¾ˆž+li¶ÕE¬}Çsä!ÃÅÆZÓåahµld†‘g=nEž-ß³.?ðNºCªfý,Pï0hMÿJ0̶OG£®Îy×%d¡åÛÜíðÀ‰Ï ¨É2¬W;aäʦïIq#É{>±Y†ùoþ¦{‰k†•ÉÝ ú”¾8¡/†l_Èv2ÇêFý·IæÎü(°Å‘wQœàx'N.@Gµ€42 k´HS˜äù çàWï`þŒFˆž#vEQŠ–ŽiB² è6M»ÑØÜÚ~k”´¤î/ã””(£ŒT§ÓÞ8B "³T/Žæ0¯ Ò[T¹†ÒÕè~ ˉªI{ÌO7}Ë(°¦D cB"B¬X«oPK ���(©Æ@•4wÜP��Q�����com/sun/jna/Native$1.class}S[sÒ@þ([J/ÖkcA…–’Ö»¥Ö ££ƒÎ€Ìø¸„¥C„PôgéÑ€?Êñ,b)úsvÏ~çöåœ_¿¿ÿ°‹g1\€Å l*HÆ„AJê›RÜŠà¶Ôé2ØŠaY; r v¦mz‡ Át¦Ê*8uÁ°X4mQêµk­ðšE–DÑ1¸Uå®)ï#cÈkš]0¨ol[¸‹w»‚,kEÃiëÝž­·l®—¸g‹Ô^ža©g¼wÔô^~6DÇ3›!—>ƒ.p˪qãS¾ØâÇ\·¸}¤Wš®Ó—9ó²HfLg8õ¡Gªku¦+C¬ìô\C¼2eñó~Y9 U±ˆ¸‚;*î⟸—=×´^ô á2,¼-=ßׯÙ4÷ñ@Ї š×tEŸ¤ÐŽe9}òÓĸÍ}ðXé<<Qqˆ8ÃÊ,¢â“ ÞÕZÂð<eØ™ÕsêÃ4¥¯¹]·d¹ëÿ~ú+AùK×mš�ầܙŽþž:÷¨ÁÛÄ]˜w:®3dÓÅizò™s&Ÿ±üÜïåðˆçø¢&}Gˆåõ1(y³hœVgÕWû šôŽ…ˆ¬¸ÜSdL2­™J[F@+ æ…nKtÒIÓ`nëìëð9A2<4Ö°LRõXÁEÈmYÇ¥‘³I‹$ü†À?ž ”˜ ¼=€’ˆÐa€è�±ì�ó“Ð C—:®C`a Í3J!O—qe˜6‰«¸FžÂËX# “:DF(žŽ½pôPK ���(©Æ@âmÀ1 ��«�����com/sun/jna/Native$2.classmPANÃ0·& !ÐRJá=�õ âR )*PïŽ1à(u¤8é¡?Að N <€G!6iH`É»öìÌÎj?>ßÞDØ÷Á±í£‡}» Ω6º<chNø8¿Q X5©f‰*®E’Ò‹s)²©(tý_¼¼×– Á…1ªgÂZEÈ –ù,´• S#‰(õ\ G' Þ­6"Ó ûWyUHu®ëNKÎq*æ"Àƒ�{púÿµbèÖÔ0æ.¼LR%Kšñ/“l„”ÊÚaE´6- >-ºäB“»ô )³9z{nÊE§°N1Xàc“2ÇÖ·x45þŠÖÖo,Éðé—ž¯ô-tšØ%¼æÒú›±¼/PK ���(©Æ@3¤wÞ0��Å�����com/sun/jna/Native$3.classmPËjÂ@=“D£1­Õú¨{ÚESpÙÒP‚]T„.'qБ8<ü®v%´ÐèG•Þ‰KéÀ̽sϹç>¾¾ß?Übà ‚Kº6z6ú Õ{©döÀ`ŽÆKk¯CÓ—JÌó] ’"Š´ý8äÑ’'RÿË •mdÊ�w¦”H¦OSA‘žÆ;/Í•·UÜ›óLîÅprG<]Mj¡('Îhìoùž{Wkï)ØŠ0#–óçI(¥.Ò8¦ßhž‹*l†Ö)g¦2±‰®H￲ ݱI_ÃPÿ£ÙxI«±h3út©�ÍS#Ï#Kár}�{+àzA  Ù‡C¾{$ s²C4qQ&O`XýÆ‹y€ùúG`ðK ^ hogd¬¢¯ÚPK ���(©Æ@Î/F��B�����com/sun/jna/Native$4.class}TMoÓ@};q“˜&ýi Ô@’¦ur A\*P£ºåªâ´q—ÆÁ±+Ç Í-àÆBÌ:)jjiwgfß¼ùØ]ÿøùù €ul¤‘Â9ÝÍ@ý 2(Ë©¢¡ªaEC!;à®ÑæÞ¡+ k ÚöHaО'‚-—÷z¢ÇP²l¿köúžÙñ¸i9­€CcŒ®3¤›C/l‹Ð±Ç¬îÂ0çI©'Žç„O•¯äŽu¯0¨[þ¡`ÈYŽ'öúÝ–öyË%Ë´åÛÜ=à#õ±Q ÛÕ†ù¿÷xè „±!3r¼ÿ†°Í²ÕánºÜ;2_´:ÂëXñÚ%“¹+¶Xu\¹lbHþÉP¦³™êFd ÿDEðàˆŠ˜‰ Éyvb‹ãÐñ½ž“ô¦ßlñÜ‘ågGu®I?i\Ó‘C^ÇÖuÜÇ,0ÌÆµ†!ÿo8†åËi6¨}6— lŸ_©‡ ‹W.•rA³ßü·£ÃRÊò€óG"å`_¦R¹sz—j&&=†½�† $0-k'yò›–Mø­%hL‘hHzLºBk®ºr V­"QýåCœ¥9E@( Ì‘¬K™ÈæqòŽQ3 %iÞA%$`T?‚A}¹©~“"Dmõ É‚úýý¦œ ɯä“Å"Š„/b)ZG±&e*Ê²Š…¢²Ŭ3ñŽcJIz²H*áFTŽ›¸E¾K$«´w›¬Ë«A9Ë$è—°JQÓQ–ÀÄ/PK ���(©Æ@¦ 8ÎX�������com/sun/jna/Native$5.class}SmOÓP~î:ÖQŠƒˆ ˆ:t]ñ§‚à‚š˜T4™Y‚~êºË¸ËÖêmGä—øüŠ˜ 1ѯ&þ(ã¹í˜6é}9Ïyžsν÷üþóý'€e¬Åü0.ãJW š Y,踦L׳(ÈÀ2°ˆ%7tÜÔQÒa3dÖ„/¢u­hÕÒ›Aƒ3äáó­n§Îåk·Þ&KÞ <·]s¥Pûž1íŠ æsßçr³í†!'Ë”ã;ìúvËwí-7{¼°²JQd×g˜,ZNËÝsí¶ë7í—õ÷"Y‡a¦|§Mˆý‚G»AC9P̳}O?xü]$Ÿ0£t¥ÇŸ •ØH²¤\Mä0¦cÙÄ-Ü&¾²•½§ënƒKwp—x;Âo8¢.]¹O§p)v5±¢ÆŽª‘~ÓÄ=”uÜ7ñ�&$H5:‘žXÿ90ÌŦ{])¢}û•{TT“7*^"•?-Å0ë© Š[Pr…¾êƵЉeÃB±MJZµ¦N†H|I½É£'œPÉÉe1< úö”¨õ¿ëžþÆ0ò¨ây< Eò‹o¬õImÀp†z"E?]<íÆieÓLC‹_Á>ÇpžÆŒ2j-LÐÚL0‰sPOzç{äÐc¹Ê!R¿ËkßÞþ‚Ô'dÈ’Jÿ@f[;„®Àlê1¨W0ì,¥`,8ì ñëé%0E¡uPÖ|¬iÖµ÷ØÐdœÐ<4”)ô f‰·AŒ ¸+TzI¦0—H0TB}Ù¿PK ���(©Æ@çéaY˜��–�����com/sun/jna/Native$6.classuQ»NÃ@œ³M‚ žða*š $$¤ Ht瀋œ3²ÄŸ *J(ø�> ±gGâ]œgwovvÎûöþò `ÕlLd1™Ã¦³˜Ñ8k£lcÎÆ<CfK*o3˜Õ•ck'h †ÁºTb¿Ónˆðˆ7|ªêÇýcJw‹V|!#08{J‰pÇçQ$¨R¬{AÛ:Êm)îîóX^‰…Íãž'.c†JµÞâWÜ•»+}QK3Ÿ«s÷0¥:¯­œ’©¦ ¾Si®âíÄÓ¯&†ÜaÐ =¡™ }éèuÍsЋƒ±~m£â`‹¤Oö,a™aä/Ç ùÏ–ðÈzñ«í„0á7nj‡!+T3:‘ñÃhõï7ÚR5ÅõÁÙ?Œ=Úb†–HZ´Qƒ=ƒ²>Š\BúýèY}{L®úftѼA?ÅNJÀ�† UÀp·ù„Ø&ai­`<Á¼ÅÐZÁzBqÛº‡e>ü¼MËiSWPG#M†”PÄuO:KÈj¯™„™ý�PK ���(©Æ@®ü t��ª�����com/sun/jna/Native$7.classmPMKÃ@}›Æ¦ÆhµÖ‚wm×›‡Š—€ Ä^*½xÚÆ%nI7°ÙýWz*xðø£ÄÙT$ ;;ûfÞ›¯ïO�—„ðÑsæ8@?À CûZieoZÃÑœÁ‹'ÉÐM”–ÓjµæA,rBzI‘Š|.Œrÿ_зϪd�Ct§µ4q.ÊR2HÒbÅËJó¥|*¬Z˳« gÒÖYq¡­|± ýáè1Yеà¹Ð¯ƒ”ΊʤòV¹:{[… —aíè·© ÃéŸÜL¦•Qöõ^h‘IC;hÑôîxtI‰zÈãô2‡Œ7`ïu¸CÖ…á±K~´M@ˆ}¸‘©ƒ²÷ö|Þ@öpXÛ#ÂAm¿î©óPK ���(©Æ@b+éµx�� �����com/sun/jna/Native$8.classmS[SÓ@þ¶l“Ò†[)(*ŠX¥\S)‚XÊ¥¶u.3ÊÓRWR'IõŸø xæIêƒ8òæ‹ÿH=P[ “ì9ûowÏwöäǯ/_¤±EnGÑ;1$q7†{ˆ!…A5 éŽALjŽQeÇt˜Š›VÁq:î3h3–cù³ M©Á ž­¼‘ mË‘¥êÞ–t×Ä–MH¼P) {C¸–šŸÜß±<0+Ž#ݬ-<OÒ](WöL¯ê˜»Ž0K·öer*Ãyk9¶>ÒbF_8çøî†®Â®ØfÕ·l³(Þ%˜èÌbHÔÅV|é ¿âR(ºZ©ºe¹h©Db§GŒ)¦vtè˜4ð�u<20…Ç:¦ d0£ã‰YÌxŠy†ÎKŽ5ðL…Ú‚-œm3e «àÐfÞÀrÑAÉ]¦“¡ýÿâ[»²ìS/2I„(—¥ç%ÇÓi†ŽÔ`cHeK@õ“*ÅU韧ÔH8ë¬`t% ôºBÆ/¢ úŽðJò½ôÅkºi'˜$þnR'¶Ð¶¥ÿ\Ò=FÈÙvµNÚ„’Ö*œ+gf3¯úMså^e_R3·R/«'D] õTœ<“,5ÂCŸÁƒp'šù<ä§tá*YŽž‹‰&»~„ЫÂwkhb¨3Gj3|Â9Ã7è¥Ñ"Êk&'ªœ½G0ˆÑÂpðûçðÉ¢Ó|ø¤‡Òæò(ÒE¼ ìirýˆPYh|½<‡¾ˆ9¾„_Æ2_Áω÷¡ ¸‰k¸A^“襙ҿ~&&„[Á؇+dãäµ5ŠÐïœÄu­ùPK ���(©Æ@º±)^i��ú�����com/sun/jna/Native$AWT.class}TßSUþn»aYZBm%­ùa hmUj•-ÐH©A(­:^Â5¹Ìf7³÷&‘?ÿ|)uÄgÇ鋎:v:퓆㹛4)ݙݳ÷»ç|ç;çžÝŸÿùîG�SøÜÁ¦ bÚÁ›xËÆåÞvÇWØxÇÆ» ¼ç  WÌzÆÆµÞwÐcÖ×ñA:˜ÅæÌcÁ8ÝLà–ƒE,ÙX¶q›Áº&}©¯3tŒŽ­3Äç‚mÁp2#}±R.n‰poy„$3AŽ{ë<”fÝ�㺠…În¬1¸K¾/Â9+%Ìä‚bZ•ýôŽÏÓ+\ËŠ¸HŽ3 Ýy¡7¤¿T—æúF3;¼ÂÓ¼ªÓutfl™UNµí08 _åDIËÀ§$'ˆi.(–_øÚõ7È<îçÓw¶vDN×ÙSBÛmäHA+M“ÌdÊå0'nJSjw½‚Iãéâ ^v1Š1a…áL“bQðmO(ÕÔÉ0¼¤ü(>UêP©jAø©BÃÕÞ!í2\¬ö -åKž'òÜ› óå"9¼å\3*U,+Ú&Ae·*d¾@Lw ÓpSVs-þŸf[ª’ÇwÍ¡ÛøØEÖ0¹†i²"BEa6Ö\|š ŽéÉË66\Üæ‹ûÆs¨±"•ŒØ¸øŸ1 ?- ½Gál³O·B^*ÈœZð+2 |Ó:7©žŸA4Ö÷ŒEV‡ÒÏϘqï‘*cšSoQ´žoÕJ“ñ¢ªUë/ƒ°ÈÐ)Õ½ééCÚ²»J‹b}¶Wà$B½Ë0r\Þvè0S„QJóP« © ÿQ�Ö%Õz½•4>í-$âæ·¶È}êÉÃéÑãæ}l)úÛ Á\ˆ™õšVi²Œlçø>Ø·ôÃ9zZ´~Á+ôîÖ0Œ dxÉ«k×ûæHð£CÁ¯a„ìëtÇ ‰ }b š"MÀ5tì¡ç{Ä7“û°Æ‚™xˆD„vm&}tà~m€žÍä‰}œ<¨¡wç“§jH&_zˆ¾=ô‘OÿsŸã3QÃé–ÂIt“Â_aY¿¡×úýÖ8ký‰ë/\²ãªõËÖSܵžáõwTÉ@]#Æ1A¶·ñ.Q-­šbÄk2¤qž¬CØ ýŽSpþPK ���(©Æ@²†��Ž�� ���com/sun/jna/Native$Buffers.classuRËJÃ@=SkRc´뫾5ŠuѸ¯>„¢ EÐÝ4ŽuJ:$õ—ܸQAÅ…Áï4ÅÇ Ì{ΙûJÞÞŸ�¬bÉ‚‰Ñ>Œa<ƒ’EvÂÂ$¦LL›˜a0Ö¤’ñ:CÏrùˆ!½œ †lM*±×nÕExÈë>!…Zàqÿˆ‡Rû]0ŸËˆÁÜlŸ‰nö®R"Üòy r'k^Ðr£¶r›Š»{<–Â銫 %Cq¹ÖäÜõ¹j¸÷Õò åù&ÿ‹c°‚v艩ëèOBW´ÌF}&fmÌ¡Hh¬¢dPIR٘ǂ ÇÆ" ¥ÿ dÈ}¥Ý¯7…Ó~ë†=]”£Õer>›2œañ{wq(U£Zþ£©ìˆJÑFɆÒß ƒfaÐÕ+E›z ƒ<—,#Û»rvÝ¡ûé44h<æ»0€Ù ò(to“Zs¥¤^‘-ô< }|‹Ô% BVîÐ{õ#âË·ˆ% ¡Øá‡;ç²d-â úßa}�PK ���(©Æ@8Y§¥���Ö���%���com/sun/jna/Native$ffi_callback.classeŒ;Â0Dg!Øäƒ„¸� 4˜; !‘ $ZäXrb)¿ÃQp�…H ƒ)vfwGïùº?�l0ás„f\[äº Ë8ŽWgBp*šRé±Ý5<ÈÚ´zÉVrD„¹*n¢jœÈœßç"MÍEIk©rBô³îÓåÖʪÒaÚ“„•î*ŽI¦UM˜ý#0@/òCx]`ô™ Açú‡Ïü7PK ���(©Æ@!p|„bB��-�����com/sun/jna/Native.class•=|”EöoÚî·›/=!,Í€”ˆ‚P $! HQqIXH²1»¡xv±—Ó;õ<{‰‚† ( ö®g?ûéÙ==»þß›ïÛÝo“ ø÷wù¦½y3óæÍk3Ë=ñÛý;�`œú¯—Ý#þêÁÏ”»Ò #ÄUnñ7·ø»\b=}6xÅÕâs[\ë…q}®§7xáe„b›Äô¹É-nöB–¸…Ún5D‡WÜ&n§Ânq§ú‹nq¥wÓçúl¢Ïf‚é¤Ï¸WÜç]^&¶Ò|ºÝb›WÜ/¶»Åñ MeG #vRó.¯xH<LŸÝ^ñˆxÔ-öPa¯ÆŠÇêq·xÂ-žt‹§¼0A¬7ÄÓ”>CŸg ñœ!ž§ì ^8D\G¨_¤ÏK†x™{…†ø'Õ¼jˆ×¼âuñÕ¼ImoQîmÊýËïPá]ù=·xßâC}¯[|DÝÿmˆ ôCüÇŸRÕg†øœz}á_Rù+C|mˆo ñ_·øÖ ‹ÅwÔáDŸïéó ’þHÅŸèó³!~¡ôWCüfˆß †d†ä††”†T†tÒmHÃCzV¦Ò4d*b“i†L7dâ”™†ÌrËl¤È/œ.séÓ>yÔÜ÷\úè3€>©n[öÊ!ò�Cæ⡆FélnÈ†é… ä(¯,£ Yè•E²˜z–r ?ÖãB\gȃ 9žLõ‡r’DN4ä¡H"y˜!7d©!'r²W!4ø•n9³´s·k–”G¹e5NuËrCN3ät/ÜCM#ä CMˆg²‚F©4ä,êREŸj·¬ñÂv9Û+çÈc(7—rµnYG…ZCΣ¾óiF ¼òX¹wG.¢\=�y²§<Þ'PºØ'º¥ß O˹ѷ\ê…çñ Èer¹[Ýr…^’+ið&ú4Ó‰úÂ-Tya—l5äI†l3d˜ª"nÙî…wé@¦ÈUDãÕ„{W®•'ÓçOT<…fyª!O£‰žnÈ3(=Ógyá+y¶[®£ô:×çò|äy9Š€.0ä…^y‘¼˜>—xp›.¥ê?ò2¼Ü*üÅõÈ+¬Â•†¼Ê#ÿfþnÈõ”nð YtÍÕ†ü‡G^c®5äuy½U¸Á7zäMVáfCÞBé­†ì ô6úÜnÈ;<¸EwzÙ¹‘jî2äÝ”ÞcÈM†ÜlÈN*m¡Ï½´šûèÓ…bJn¥\7ÑbXì6äýTØnÈÜòA/+"aU$w¸åNCîò²1Èâuùu{Ø-žX¹›ð>bÈG©rußë‘ÉK ù¸Á; ù„!Ÿ4äSÛkðjC>mÈg ù¬!Ÿ#øç ùm ù’!_NEòúü“ª_¥ê× ù:¥oòM·|ËËŽÁYáäßN‘ÿ’ïÐçú¼kÈ÷ˆKÞ§Ïnù!áÿÈÿ¦•!¯|앟Èÿx K~š"?“—òó¶H^Bà_þ/ ù•!¿vËoRØrù_â[üN~êÅÏÿÛ÷nùƒ!4äO´ðŸi­¿¸å¯^–¿'üîVàVÌËV‹õˆ[qèwC CIC)„W.¯r+ƒ>Cyñà«*˜†J5T¢ÒéC"Fe"ÿ©,údS]}réÓÏ­ò¼ìR±Þ­ú{Ùe´I÷(}àBÔ@ú ¢â`C ¡ô�Cåj¨¡†ê@C 7ÔC4Ô(Cj4’Z\‘[{Ù-‚ØS•¸ Ûh”±^v¥ã¼l£Õx[w«ƒˆ²u ÌŠ––@[y“?„Ýêî©íK—Ú°0›—. .nð75-ñ7¬Ä¶ùÓçÖVÌ®aUµÂ¿Ê?¶Éß²llm¤-زlƒÔòPK8âo‰Ì÷7µ¤Ùà‹kÊê*æOgÙâWª‚KÚümkçø#ˤDÖ¶ªý­­8&ƒt o{$Ø4+©×ß\ÖÒh‰`³;Ô â <MI0@¦MŸQ6¯ªnñ̲šiUÓçºÕ¡ úÏkið·/[™¾¦! {Íô·46ÚŒ­j5 ··Œ]Ñâ[n/ox_pý£4èÌœ3»¢¦núÜŵ‹p‰¬çV5»æh»ì]P>³,Ú˜BÉâºhSÝÂ9ÓÏŸ]1mŽ@+°£®¡ÞTÁ¹®°PÔQG$ºÎÂDU P7sîô²i‹kf#dÍш;Õ®™6½®¬|¦øÿckWVM/›?}qY5MŸF¿áØKƒ-Hê“iMÎݽdE !2‰(îG¦·µ… ÏP·¼-ào¬ !‰*³-°,ŽÚ6W1ÈŽ×UÅwÍhoi ùõxåSqïjWÔTÔU”UáÒæâ¢Êç×-žWS;oΜÙsë¦OÕS½×vÉ&>“Jµus+jŽ&F´ óÊëæÍEjd'”O]8¿¬ŠH'í~Sç͘1}.Õ(äZª)›;·l!ÖM§Z—=«¶v&·ªÝ À´ITkØã[µ8=ªô$€FwØ›€wFÕì2 œÂ #^=mö¼©Uz&’×1¹Ù³q#k¨!Õ¦†£* ™‡ªÊ˪ª¦–•Ï¢ºtÜDª‹ ”a#´Îçâê²9s°!ÓF¸À¢(UeÙ“"zìN\HõÙv}ôDësìµinÕ˜5urøˆ1†ãŽgp8žÁá¶DÀé8Kó1^íÚ–Èò@$ØÀ`@ï¾å1ñ4¨wc]L¸Ð!îÕ\£åƒ~V=œÆnÉ4{6èIUÙ|;°7FìØÞioC¤£öÑ:|êÚ¹³–„Ò×ÌôÔñ¼õœÆÔP¨)àoaÓ«em1æöZÐòP[$F{Çz–ûÛü ZM¯A*Z"eÔÒkª§× 3ð@G’PsZ¨}IÎêÀ}SÄVIøcN(آ瘤mAt¯zÍr~(ˆ´Ü»‹½0bޤíöpVûð$ì•„1uHŒ–`h¸¥FQe5í¡0"£ç3塯�)º`K ¦½y ã×ÄÉn 4"§ŠD´½5íèE$¢IàÎGYJ½m²U«T¹•# «g04vF°)€É59ØŒ‰�‘åA”À9 zÐÁÜU1 Ìp 2§-A@\( Ñ R‚aG%® §“£!á@Î=®'¼Ó­m¿†ÌY–&«[C«+PÆçØëõ¯ŽŒµj'®Dµº:&-8@lÐ`9 ׌ÈÊCÍ­¡4_ž_¬ÁBÙ@HÒˆ-6¥ã$™WùlHìíœC AQò‰ô#>ËЇ§ «¦‘š‹µzM ¹n¬ÕÔ'Z¶$FF41N€01$KÞâ¾ÆÉI2v0"¡è ìWP?ut2ËP,i_Ê€×O¥ÙÖOMÂÒÉz1dé‘1&ž×nomE)hœÞÒjD˜Øæ#°°+±_˜X<ÐÖ¬gTž7¯/GÖ#$vÔFôR>£“BišKý  ÄàÞ{˜±Iñšähÿ±È3ÒfÆAðGfâ^µfÝ1»vp ÙD7‰]Qã-¯ç–vDÕÚZC2,*:(òWhÏ”aN’m@©£–M(E|ŽÖ¶ÀÒ&\Ê7l"† â¼4îÊ€ú}@æ:Úœ\$š°«mab²¦PijÏÐÞìáûæ’˜ÑÒ@…`‹Ô¥m¡æiñr&ž0éì¨?3¬O´_(ßSªmoX®×ã\€Œh¥•ŠC8mŸ¢dè»&ê`ï˜b.‹ú_É·ýµÊRÜÙŽV[ÅÒÁDdd”„é4&9ù–PØ'';AS"!ÂVÖÖæGÖRK,ÌXK–Œ]›|<ðª|Î)AÑÎ „Cím Ë'=¼ âÍ'™`QáV?±±+¶ìéok@œFµf`ip î?ñ}=Ïk÷ÅB„‘›ñˆ´¬ÔÊ’ö7ÔD5¸¢1ØFê>¨•?pi\cMª.´2ÐBŽt´ j´ð0–„Bk¹ý{Íg2l¥_KKä"cçÍ­ CÜ¿ÆÉnª!ÔNœ‘Ó³íœì GcKk;qVÀßL‡miˆÎi‚™2»=â¡ùZÂÎl³·Èš¾hokŠ™8Öôª&i§É¿’Ì †f‰Ð†+Œ«-%¦‚ø5#¶ûómam½å$ÝL2'ÊæT”/4¬ ·£>1éèÆm4~*,3ÉQI£ ÀJoomDÃÇÑ^ÛÒ°¼-D›W;ÅIÅãè>„¦»)Æ2AÜ»I„\EË*4#Þ–Àê˜l'SbA`ImÄßIÆŒLvp’ªÏ¤>kŽí^ÂøÕÈòP£=WË$Ílö·­¬  Ù£ÙK3lfA¢JDsMÑK ᧇgDw'nËŠHs+‚#mt&§-ÐZHyË…+DDZ+4i#¦"b­ºe’Ö0ºSz}ÏÁRc S‹[׃LN5ëT«Ä,D<@î”MMeþ¦²¶eí$Wç%7®Z9Öˆ–I&y‘¿"ç•÷š_ðÿ tÑð�n×ÀeûÂ{PÁèÿw͈ƀúpipä!Iü‘Øq˜O¢­ïvë¬Zu¶¦Ö“d5¡È W úÒݬ™•g2{!ÆÊ"؈:ݦ NÙ$» ™öw7„Pÿ‘ˆÎªOÒ<ȱmVø¬]®`4üvTò}›”´¶WkPîõKކ<8çp£þ ^<”í-ñmì×›%ë+il’‘µh3ø­0È~Ìê˜Hq·Z›´ê¬üCÖÀ•Táƒè3·0‰ �PÌ JÕŽ[˪¨R(NrÌû°™è°£ì²M}[Vc÷ƒ²7Ó³HL ;ì>‹/£FËbbZ"®¶‹9U›m ’%‹›±Ø.»Z)%ùè·3¼=¹´(X´V4¬ŠP[…öò¢Á²üúÄõÙ å,Qê%‹7 ?4|F¬ÅÙA¶é¡Ò"¨W;T¦+`OŽ4JÝA‰èÚ[è(Ç(Ì–ödü(‰øæ&ä^T(#© kâ>E=ü Ë1Ë|Â~½»$²ŸTY\DDàE-Ö=@Õö–]µO]Ò'¯×WÔWÖWVTVöê]Y±h´ß !¿~ÊRrI¸× Zj›Ã¡Á¦ìoRI'ñUØ/ó[°ßõõFüÇo}ÖŠ‚jqCùÌ]P; ƒ Ñ‹2ª¬Äÿˆ2âÐÖÁ"aX™„bÃ×lšøºëÒ¶@ ÞUh¤ÙqÙ½˜€¬MxJe³?Ø¢£ÉU,ú"ËHi%iF)½F««¨«šî¸V‹ÝúÅ ¦Î«¨š†ò¡uå²D·o.À¿Œl& FÈÊsÇÄi­ÇÞ‰XÌ>§ÕL¤Wõ±}©À>Uto=Û«¢b‘ÖDóxe ‚Ü–þ•õIIéµÀ¬Hw_p•18+ÖÜœŽšj8;DÞà ä1 03ï ÉŸjAÆtéVÙq1 yg‹=§'oL | W“¯Gý° p±ïŒ&/öм>Ø+¶ ŒîcÅI#a¡V²®“š”•äž6éл—:µk›—„šètT&‡v#P`Íl<ت r*ÕH²ðtTÖO­Ð^ekãÙòx¶"ž­ŒggijӬ¬Zݤ+·±ïT«‚‚VE¹9±ïm¨¦u ÖhÆ%X‹5©¥Ò‚µy‹j<ǵ™ˆª¦YNUŒ[±ú Kc#Eæ;á=VPÈôV.Jjmyв­FWŒüS ©´bw8ºRMW"G8¶2¬ªY«Çšr­@Ãöb±¢" b-k*£xìUcÕ ª¢D—uÓ¨Îv®#_G^2VXd;¢MM¡Š® Ô²‚úvTW½ÜñÑ<QñXeˆŸ"?^ÚááãÆÃZ뚨‡YœÄ€ú | Ù¯kÆÅñDx2 z‡£�ã À˜ÜÐd_÷ð*do­ŽžX®wŠ%oÇ“Ê?3Ù›ìA“ç#Lu¸¸�¼ýùNè¯9R 4-°Ô"ãÍäc[©šäV“MuØvÖÇÝé]Ôk¦:’O0ùN¾Ëä‡óR“OQSäI˜ÁÅ(“×ðÙ„§Å?&õw«£LUÆï7ù^‡ú³'m±£Â36üÜ&l¹)Žáw˜jššNÅ&?ŽŸHŸ%nu´©f²éú·ï9uª0U¥š…r¹-ݪÊTÕª¦Çm ¦ÈýL¶ ÷CÍVsLuŒšëVµ¦ªãï™j¿Ç­æ›j:'  Ç.øWÆn•MµP-¢YÔ›ê8u<Ž•$âNòþÃxÜ“»L蘔‡Ú›ó[B‘|‹÷òƒv??´4ßT'¨Åô9y0¿�móÑ´=M¶“˜ÍýÆ ÆÐfÆ(p¾¸ÀT~þ‚ÉMÊqEm·Zbª1U#†‡) Ü NO•fÏ©C›ɶT-3Õr~p†í=g0Ñî”ßÜŽä/ äûó[Q’òõ¥­‰Ì-}6ÆÐÁƒ&F—æ£>N8&{€˜$Åñ�Âä«ùûÉSIÔŸ)r�ìo`“§=ú9é÷/Mä­Ö$Þ§ÉvÐ|²ãï`ʪ*Ž®©ž^SG¡É¨>.ñÇãý‡%Ýßñ˜E2{]˜j¿Çägò³ñ£VRî,S5©f“Ÿ«ZL¢¹»[C«m­ ¦jUsÈÀlEì±+'BwJV*ƒ6ÖÐ×µ:ØrðøÊ<¾!@™FÛj2…]°Xwô77RêB©Ø¾†@D¡0fX>žuuf°:ÖI=“ïá#м :S¼|Œ|cŤSV´ézßiXŽÒGµ%µ ¾>£ò¦ ³n:¨È¿í(Õ*<!j5‰²1j©ÖRñdjø-‰“]¸Õ)¦:•OÀ7®Å)˜ê4>ZJƒºtº:ÉTg¨3Ñ%ñ›¿”äu¾?‚웿¤=¢Ï2ÝЋ‚R<Âgñnu¶©ÖÑàZRm-ʰsLu.M"ñ5[·„¬è»É7Õì7,ù"åÛDÑ£è¡éM_xŒuEB9"¬©Î#N¹€_ˆ{‚{F<Ÿåäy ³©ÎW¨ª\côJ±ÿk•tc"794?z—O›ãVšê"â>ÓyÙAä¿8*c“_€˜êêæïvØÂ£i;on’uH~CLHâ±imC“µ­im~c�ÕN ÑT—ª?#±Êý-£"ù¡%ôàHæ¸31ùÃ|72=’ÀT—Ñ&Ë1MM¦º\ýÅT%¢÷ïë2…í.·ºÂµêJSÔ««LQC=²“ÜÜ08|†4æGBù–‡–‰ÆÖó—ZŒÓ–lg‘ex²{½áV#-ôwz³©Ö“*Ú@Jìjþµl^pº/Î×’©þ¡®Á›×‚6Wh™¾2±ÛíËj’¸9I|i<ã@{w­©®S×# ­ou`I˜n;ÆÄ¼Ñd]'¸Õ ¦ºQÝ¥cG3Á¶°üSݬn1Õ­tP;hƒø<··Ñ‘½(œ¦õE04&ÒÜjÝØá˜(k<íá@›ÍñwÐn&›ÓDSÝ©ñ]x$Ôݧˆé&µ™(Ûiª-¤ñžâO38P_~†I7Z–E–ç£p YºMÙÕFÚ›L:K¹Î³óÄLžEC“6:¬™ê^uŸ…¥·?gª.:¼OñÓè…ëò€¥†áâ†åÃz:1Ë‹&”Mcöï}¸­(’[m5U·ÚfŠAÄ8÷s¯És¨‡Ö/chÆØoáÜj»És©-Íц¹[=`ò~ÔoÐÞ‡[¡’§ÍxKìEœÉg{MÞ¿Çp¶:s«&÷õ޼·Úiò=†ÓNŠ[¡};Z2â-–¯bòAT?¸/Úë§q&ÛCÜËnŠÇê!Þaª‡ÉnJï1LÔ Ò«sìý\?[^Óm©>ÞÑ3õε·¬l ­n1ÙCì“=Šþ.1ß›dxu«Ý&•ÿhªGÔ£(µ‡£¡ö0˜f‰5t…mÍÁ–€u¸óW‘=­è`¾}“O!ãü2LC{²Y$ß¾C!ùì`jª½t¾èeÁÖFP^9dYYÝrœ¿5NLÚP¼EJlT4f7#t’ü¶4âòÈQZiªÇЬfo‘)ÅêMþ9ÿÂTÓ±|+(^^C‹¡ÏÐ gèw³*ücò/ùW˜™D^Å“&? óPÉTO©§MõŒzÖTÏ©çMžO@yÎýr<^4ùÐ^Í o³ãV‡–ë6Õ â·zÑT/‘w±P½Œs*0Õ+tÀþI¼Ï«É¿ãÿC³5zšýñóŒzùµÅbœÔc6ÍQâ‘Öu«Wi¼×M^Å«Ýê %Éøm¤S½©®7ù0Zó¿!É{Ìô¥vØ?fhèûÞÀTo©·Mõ/õŽ[½kª÷P‚ ŸàaõºxAùVŠ/1Ãâêpôæ §û>©€H²î3"M,N r¥)<êCK& ¨÷ð7û¢Q·ú{Wâ|óíƒ\f¹d•5ehªƒÇ2æ Ô:Í/hµBµ¸1xÖ›õsüæ`˜^<6Õ¿ÕÇnõ‰©þCJíS²rRóËæTäÛ/&pqŸ©3Mõ9µxìJÒÈ{píwb_~!øõ¿ª/Mõ~D¹˜fŠ&ÑhòM|3ÙÌ1.%v¦?¼—œüÔdò?ó&¿söŠCöÉăHϦ/´FŸ¦š|<9}zÙãð ¡Ý´¢tÐæMGå„»„ èoA"àXhÈQç#½¢Â)j¯j·V \;½žÍG-¢1è4Ö…´=Ù„àºiØÈ_jÏoö£™Â.Ú&)³4DªI\Ê 8¿$ßz¶Â¶½ÅCÅпn¡ D~ì¹Çh"–°ÒƒÛ¶ddm~Ì,?užû£mؤÎÙt¸Þ¦öÆ€F­£¼mE§Ë <??©o#h´˜ž`‰j+üm–uH«÷#uÛÃZµHkZyÖYO€ÓïIt¨H}MžÄDÚ¯d›>Þd¯²×’·õ55ÿì“w3ÙÛ„&«w[¯Ùî "5 Iƒý)T>û¹Ìq«oPÊý¿Kàˆz=ôPxfÀOw’a‡\sãiµž@yHW•ûÛ)ÀpU]G—¨ä¿%ãµôŒ¾¯;gjöƒõ2ºÌrYÒ{DØ×Î@›fJëA÷›3)Äšô½/Âéµ°ŽØ¶£q ,WT$VGyç'ÈŠr<K†èïÁ¦WÏ©[Ha”7¶ô]ì|ûñ¸ó÷¾Þ;b[óz;s¬ç®-Õ:Óº¤×]ªgŸ·V}¿çJz’BzáYž?ˆnØ"+Œ?Ã~";¨`ô¾É&Yâ ë®Û~^j=ö¨5—õyÄm²[Áp­¶²¬7rô>O?ΙlªÉV˜tMó)LÔNѪ$­±‹4,#ŠÿиXŒ¿x?3ïž —…µ€Ç¬€†AT68~”I7WÑzûͽŽ='¶ëèíJm;ž9ûé‡Ýf?ûYSÏmOEÍa™A}‹âÖ«ô›Ñ`ì·²@¿ÆõøÉ¬lµ"Ç&!FE’*}—CýËõ»Ý* î•ûIй'µûévÒû¼Š[üÿ{”Jj®6®þGý1T8ÕÔåþpu¨- #v$jZÐÞÒh\Ú’Ó"Ñ=5&GIïÈÊ– ÍбŸ»kÐ6§.Pð­‡þšÓ䑆b0®ö7è_;4†õhIVDo.*Tç‹Û98fü9-]�VãÆ¢Ýçü8`(K¥¦–„nìçf„ V Q2»Ö’$ô£¨{¼fOÜ•ø[]g´­*¦6"¡º¤>µošjĈµ²£•…£SÝ÷¨½Þ{‚aë÷5ô&ÇŠpÑãQëzlêÿãZìõjÏG£¦¥:g·L_Œ8ÃlñyÐc}±\ËÅT\TgG[ÛórLv‰0ÉP#ª»è:=¬Õ]ÌÑwÆ=4½qß·èÚ×câ IŸ,Ç~’ü=ó|KHUD<Ö+Jö<Ò q‡ ímÁÈÚ±–,"?§4>ýæº1„<» 鼌ü¢èïob=âeÖ˯äBßzvœô‚Gód›¾.Iå-6Jµß¶V[åôVë ÑëÊ6ëGÍ+­Wûz—f?Z(Bo”£Ôrü¤Ï õÑÛ…°ýã‡$ZÛn¡¹VÙðIUiE±•ØIWžáü™›-Ðò‰=…½õýˆD†õËê¡}y¿±ðƒ%(â£LÜß(I±Ðˆtïf`l)§ß(8¢ùöÇÍ©ã粎ŽÖ£GGûM‚Ã8Ž>IH¡ß¹[¿7¯ÕQô*ulRᵯÏY13 ÐX}Å<8¹I{Ç|D’aõaœ&{Ž•3åÑÎ.ˇµlZþ”ƒ<Öۨ܂ä–=ý!ɽ¤?«ýž’’¼ë´Òá÷-ÙSBý^¨,bÙ«å–ú‹?åŒ{UúU©ð7ö^g켤jEñ1ë°dZÒO·® ÷°ãñß3*¬>îXntRñü9Ïû!š5—˜iÏ…v:%³l›Š}=Ðï›é’îzŠ;ƒiûV¸½öñ¼7'Y½ÅÑWû´æÛ“†§7·’ŠœSKú¥jo?bŸñ»äª¡|xz„ð’cÑo¶¬Pñ–&þØ2³W¥ý#¾Ö@:_–þ®³dæõ¬ýJ„ÈêOâµ¹µ)@1ØD�ЦL^A_œ–bû…-KC(þ,s¾bY ZÏ–…Ÿ“Ì݃Ø=ð2�TÂ�¶•ucÛô¿ur –ïw”׃—žé9ãö NwØéN;Ý¥ÓtK¡ËêÔGW':}ÌN·Ó't:€=‰itœ§°ü´£ü –Ÿu”ŸÃòóŽò X~±Gÿ—å—±üŠ£üO,¿Ê^Óã¾ÎÞÐó|Óžÿ[vú¶nïÏþÕcïôÀûnqßëÿ~yÐcÞ:ÊaùߎòÇXþÄQþ–?u”?ÃòçŽòXþÒQþ Ë_;Êß`ù¿Žò·XþÎQþ–¿w”ÀòŽòOXþÙQþË¿:Ê¿aù÷x™#}9s”9–…£,±¬e–ÝŽ²½RÆSèèÁ’™PJM(¥%”ÒJ ¥Ì„RVB);¡”“PÊM(õK(å%”ú'”| ¥ ¥ ¥A ¥Á ¥! ¥Jù ¥¡ ¥a ¥¥sÀKO±<KÀ,˜nð°-àÝŒyÆGá7 蟎¹¿7‚nÂ^|´Ýë8¬åtR¶CÊ­`VuAêzPrcQ7¤‰z' K?PøÝ è„ ØY°ë¶ñBݦqð"-]<΋y ·Ó#F{¤±X¦*%}s ­KW> Ñ˜�ËÇáܽt-éhº‰Ðé!|‚N'b™ñCiµX: kíxè$b¶äö€ÝqW¿IpŸŒÍG`^"ª#Í~T4™=Ñü’€¦ŒOí¦œO³ÑL¤:‚ÝY iF[!{S"6ær`3“Lj:ŸÑ¶Ì$Ø2°õ˜3øÑ¸D¢ØL^aoÑl#VésŽ:ÀsÅp³H¡AHÁKÎ| Ø€¨ä³ˆMw”(¼ôÖT2‡crˆMPoaVn7ô놼ž3ᘹ—Ïåµ6†:ܧÄt«.Ú™Û¡ÿ¢­à«î�YS¼—¨ÒŸ˜y`u±è‚AGæ×·X î‚ÁÕÅ›­ 98؃âr€A 8\ì È`C›€Úc" e‡Á(v8³R8˜MÒ;˜ƒ3žÇçkrM°§H¹¤oqóc1Ça¤ñ…(…^À"{+±L=ÛRUD=bM´H ‰VÅ©¢›ŠçºÒÙ4œÜtÈfèÉä[ˆp õúèåèÑþ´æ8ðqüx{àq6í] ·ø€žt¯qÐÝÅO@Qº÷b»÷$[X¸Y! ‡üžÝ8XÁmw'`²-˜Ft¢è‘§°¨†&Aåw òp¿+”#¤ª%6ª H¦c¶Ã0ä‚â­p` аá¥Ò'åƒ0b¡(ª-醑»`T©**ö©n(𩞴m…Ö™, ÃÙ*(b«õøGZ˜cã±ÇÇœ^‡87ð�.'÷`)_†|9yv9"^/=¦µæÉ\ˆ‹xöŒÂ=pZ'Œ.Ü…gßVØEUâˆAE;n„ʯ)é‚âÂ[a$¦8ñ1ëao‡± KXŒÛ mƒñt€gg¿?Ö%UÛá…Ûa­01ëÐ.8 ±žUŠ™"ÊLÂLLÞ GìÜŒóÏ€¹0¶)0wØÈögA.;³uÈöç »Ÿì¨Âº¹ì2˜Ç.ùìR8ý%v †Boâ͸þ<˜Ä[4¿åÂ<Ä[qœù0ÉIšbgð6Í=5¶i’‚ÐĽ§îEI² —¹°êáN8Ò¢ÍzpíÞT´»Œê¢ÝÅ;;aÊ^HÏ:ªÊn)àÂ\èTTW…Ø»œZ§Y­åºµÜj5Ù§ÏÂüŒ*äÛ£«‹÷€*Þ„å™XÚ„S0a .` ÑéPÜÄ¡19 õ,Ù<8ÿ�“]ƒ²áZÄ®‡‘ì˜Ên„iìf8‘Ý”—+Ømb·C˜Ýíl£ã˜žja ÈvÍJ!<¦«¸WÓÕ|M˜±È*¤mÏ´HRhOÚ[Ü ¸Øj$R±Í@•ÅY•]0«¦DvAÕk.‰²HqMÔ8¹£y¡„˜b6fŠ1c3†…r‡]p ’½TíöÉšŸ§ÀT¨Öé 䬅“qr'Ãi:µˆ4¹ Ø}H .<K¨âñÌôc÷#¡¶ã™zÏÔÇv¶ ö”c:ƒí†£ÑÁXɃ“Ùã1®*B)fqU¹³k1w4 ×¼$ðÌ…ô9$Ùp&?Ù–Râ§ØÄû³-žì›xs£Ä»¢8«ÖA¼¹‰Ä[UƒëH’ìa>™5ÏæË~i•‘^Qè™ džÔí„Ä’ÇZ,¹@³ä‚.XˆMúl&߆¹ÑmXDÛ pTÏmhÕÛ°È7{Ó©µ 5¨×�={·á܆q^Æmø'nÃk¸ ¯ã6¼Ûð1nÃ[¸ oã!ÿ,bïB3Öµ²à$ö!܇ÞÊ#ìÓ˜ŒoIâ!á'qs íÍ9 ¦Å6g[lsž´¹ÝÃOå§Ù›s!ö£Íéî{s꣛)Î:α9õ‰›3¿¦8asŽoN}±UvlNâ8¡Oâ×G‰¿¸¦§…@'¢Q!ŠvoÜ×Nl2h7Ã}7lç’€>›‹ýwâ[܉ïp'¾Çø)øîÄϸ¿Àô“¦¢ïUÎ~‡ $Û"ô³š±®•+8 }ª+‘†7¢Å¿½'Ú™hÃïo7âGåV{7nD›üt~†–BÝ1Q|&?ËV£uC{–5Êü›ªP@.Ù¤…·-¤¨µ¤½ ž‹Ê-RÐSŠ*ê4€É""ø m„X#œm›¬³ìÒÑdC½ß ›¬ q>H>‘è°�Òmt”ÓVÔŽÅIyøºØ"üxòiyÈVUE;䉠æÅ¢hG7ŠãCe‘*æE8ãb4K ý”¸”γ‡SÅÏAËŽ¡~HÑ6×ÃEWÔ„R›Ë¨UüÕÅz¼šbQ"Ši¼’žãM@£j"¤¢«Ò–Bmyêþ±åõ·íG—=2‡T{d‘°äsùy½–ÜK­%«},y .ù(òpÉSûXòùhIF—\S0è§Öpœô5ÙK‡àþ-«ÊZŽIp=x²VTu  [i]YMUîÐ º¬ÿèÈ´á ȼ�רmôW“Õí×R=¡!Ët!«5ؽÑêÖ„êk²NÂïß²Z¢ÓhÖÓh‹¢ ÇÐEú¢Õí Õ5( –ÕD±¹›UIÖª.X=QæJ4ÒSJD.J¥Á56¦ékØ¢#¬µ$ ÕÅ÷â,¤:ðJÜ‹Y¨þ«ð0× õ8òÑGªÂÍž‡žÎ±|4áq ñcál¾Îޏ€óP$,†«¹nF+¸ƒ7Âv´@w íù_/óð:Zïó&øˆ·À—<„ÎîIðo£Èý¾v–Ï×ÄtíO0’_„v›¤]åëpå.ÑŒH9¿vdšà1~)æžÍ?GzG¡p@O°NÎúSÖ)ÝpꬬÓP€Tá)e;œ¾ ü3¶Â™ÕHÛ³ÖÃnLή¡Xý•l꺭pÉñ.8·TQrÞzÈ Cý|Ÿ¢@F©«.\ë|*ë¢.¸x=¤e]Têʺ¤Ô¾)&.,ºm‚û”ð)Ÿ« .ÅÝAºûÜñK èS8¢Æ¨¿Ñ ŽnØeÔIÑv^N9£ þbaè‚¿vü>9ëŠn¸rƒ¶(¯ÚÜiÕYà 4@‹wêzÌÝð÷ÍÛá‚…Yë·Â†H°c Žƒ P7l‡0ý¾…0µ˜áZôŸ‰'ül<rë “Ÿƒ‡ó<Êχ~”àá›Ê/BÝp1Ã/…ùüÏPÏ/ƒãøåpÿ4òõÐÂÑ‘àWÃZ~ ژעYt=êŒPgÜ]üfd[à~+2I|Èï„ùÝð¿¾å›à;¾~à[àG~/cü>&x7KáÛX¿Ÿ¥s+0³åÐ 8ËËpL´¡w÷d¬ÂYÿsnØ£t«§Âr»õLœ›ÕZ‚:ç Í<Sá&~%J\‰šë:^DNÄ^~®„ÃÈ\ó jÿïZ$e"Ë®×Ìè¥FYŒ'ÞÀ~i8£Äsëfe]M<—õüVÛ;٠ס…p-måu–G¤Of'\OFÙ –Qv½6Ê®Ç#zc©‘ýöÂ,ÍnÓK²nÒÜ69zÆ…®±+ëæýã#d+D™ë[dÝuÈ6ìd¥.âù”=Y·iiSgÛRý[áέ°±ÔÕÙÄå)ºönäcì‡|~Þøy;Übq­$ÛfstŒÎ}ÍqK©ÛçÞ ©„ ë^‚d¥F7ÜWêÉêꆭè9eu£ùÃ|žnØfwÿvؾÐçÚ ”™0JS|n_Šð¥ìè‚Nô^‹g×ÀŠ\oìèøýC‰DÅC¸­»RÏØUt¨ê€i¥ž˜RêqÎþaš½'juî,5c}S©oªÝ—ŠæNšÏ¹tŒ)Å“œ«ÖƒiWî^°™ñ%ø>ƒ»ØÍìv¶v±-¬ý‘]l.Öì²Ó·Q~½Ã>Dãwû’}‹&Ú.¶ kÞŽÉëN(Æì#¨¾Eݹuä^8ˆ?†²úq˜ÍŸ†þ,¹ç¡_€ø‹ðÿ'<Â_…—øëð >ãoÂü-ø…ÿ‹IþÁ?`GñÙtþ›Í?asùlÿ’5ñ¯ØIükv ÿ/;›Ë®àß±ø÷ìfþ+»›ÿƶðßÙ.þ#{˜ÿÄÞÆºÿ`Ý—X÷‹\ ÉMáá™ÂËËTìB­ÿj˜y(ÜlTó+ðèyØåè~þ%½=û¡Ú¼HawkOël}0–{Ù¹ÿØu©ìÇXî()×¢.t³¹p¿ÅÊå7êcK¿¡±tH÷Ø:ä œñMZ T±gùÍ(”$ÌfOñ[Q,)t7ðÛp~.æ¥÷é:|'ߨӻ(EÃçnZ–á3Âw§vÂ#]ð(ìhª‹‡‘DóÐym‰vb>YÁÓtìº/°ötÁÞ¸é®Ý1ÒÄÁŽˆX:Ÿ§µ£‡oá÷Ú¨ž³õPº<FÁ…Ç7Ði:daÖäO –x²ºø!¶†8ý§ÝŽâ]0 •âÓ K ·Â3èywÁpü{Ö'­ÀUÜrIÚVJ”)¦B?1 Šé0LÌ€#ÄL˜** Z̉iwœ¿µe·P›ý B9ïB¡ÏQ²£WÕ¨vº±Nâê¶ñûíÕ½eǬd=‡‚ÕŽÑ$‘$8]´´žÇ¥½Ð /î‚—ª‹‹äƒðòBQˆÞõ+» ?9SéQ•ßÿÜÄ6U±M8/jÇ:ô¡æB­NY�C,¯Âb$VáòVà ±抓¡NœóÄ©±¥ˆGr;A ‚Qo=¨˜Åwh†›‡˜£¡¨¶f1èŸL°—zb Wcœ¾é‚Wí­Âìk$°_ Ósè‚ä ô( ʼ¹Yc…3Þå4´Åy¸%çÃ(q!ŒÅ íahò=¤oFŠÐÍ¡cD½ÇiHóy˜ï¶çsº=Ÿ)4Ÿ¬·üH¸YzrQ&z›æõ/lI˜Ú; Š´ã.BÁ]ØÃþ—Bªø3”ˆËÐ-¾<æa¥ÂxþˆžB ìQ=Aƒ~»mOæìëÆt)ªœÃ»8ü{Uä½_-ŽDï}Îàâ7Beñàñ–÷šqd‡uÁªÕ¥m|aË«dck®õBi*ZîÔ–º``¿wÆÏ¡ŽLŠ«Ðÿþrû? K\CĵÈ×A¡¸7@…¸Š›`1¶Ä­1ù@˜¬I-qk&hù£ÐäèÇ÷’„A×ü­ؾDKE:勸c(ÑQbA‚?©Ý½§P¾[¡‹)ö­Ã}È®GS¿>:bеS$ºáß5ƒ°úã’nødùFçQÿŒE>¥óó™u~>ÕççS+J9®>§¶/¬¶ÏuÛçÔ¶ÇØ«mÎ/«ŠvÁW]ðõF,|³±Ú9Ò­‘tté[KÀ|ûâ²ÎA›óBº~ÑÄEO‰»Üâ.$îÝ!îAq² †Š-0YܱÖàÊNÛàñ�\(vÀEbgì¼õCkõöšŽžŽãÏêÐïE:ñœæÜûìÐåNG8¦‰iÇÐ~Õ2˜ì„ÿÑ¢¿·ý?½èÿYIÁ5þÐ?Î*섟nA˜üL°¿X°?kØŸoåÞˆM¿Z¿ÄïÄoâ·[é6³°“Á-0œiF6ã„1a A DjeH "ow'sÛ24Ç24aO†y- bZ )$åVpe“jÁ¤i˜t &MäEÑdX ™$ËÉÔ ™š}°Sq'ËÖýr¬~Ùº_vœ´_u²\üë§ó,À~°ŸÅx'v²þëÁ[ØÍ| ¶Ïû8´“ ÐXZXh, qlnlµ Ò-ƒ 5As‡%› ‰óp6;ÀÁ½Q‘02‘kŸ€AâI.žAmð,,Ï!g>›Ä ð¢x¾/±âev¤x…-ÿd«Å«ìtñ:;[¼É~ïr&Þ«u>2¥|Í7KàlǪÆìƒ¾ÄSxQÒJŠn¬Â³Ä`Š›¢ØÓ—KâSœíg(¸¾„"ñ5‹ob‚·Xk¥ç¬+¥Ø<^ä/Å,ëÎ<½p/x:Y>‰ô-lhOKåg´T~I°T^ÖÏv<ü´D-Tƒl{Iv²¡›Í$I¯ò×lÈÉöaZ7ÖÍÄ­ÞÍFôSz U¦8ÆŒºk„éuÓá6¦”¦^h²Á+shRÐ|CëÂ7£×DZý)ÓTØÅFF¯Êú ßÅFe28M_ÿè«°‰w²Yziú.•dòB}ZS"1 ÙT*¸®0za6)A^NFERAzXÏw ¤à|ópßûãÒ}0@€A˜&C‰ãå0Y… y Ì’#c‘s7 ´/ÆfiÓù-\ÍdÜ"¿¶KpUö¾+XÆßæÿÂqK` G÷0è7â6è®™(”¿-ÜÊŠ»XɬÂî!m;ƒ‹»•ÛYèßCåËBH‘c!SŽÃÙŽQ:rùûü½aòì1Ž´ùÞUXÔÍê¹W“pí“aYÿ7ÿX3•‹Âÿ£%ü§1£a¤Íp^kó»Ùøø‹ ß\ðÈZÝǾ$…«ì{ñiÀõU ^ZŠÍ³ƒñèYèv‡l‚‹ßnÆ1\0 ×Dë*Ó©µš!dÌÈEà’õ%ƒòx+O€Ãå‰P.ýnœfí : ŸòÏhÞôƒuÛ0m/¢Ó2†Ä¸i¢ÅMw³CíÀòíaëa)JòÃQèe³ÒM¤ÑtveÕY„C¥³GPVèì‘”uëìÊzuö(ʦêle3tvê¦Ø$ʵ–u€n6]gô Dj@.C¶ùxŒ–+áÙSd3Tʘ'Cp¢l…•ò$X%ÛàL†‹e»ƒB±»„/ùWö.ÞoŸ×iöljŠ £‘ÓëÁ,A»ä0´Ÿ‡—[1T*–PÜtAq.ÒNìbGWwüþTIÂ\µO OCžéòTÈ•§Á@y: ‘gA¡<ÆÉuxþÎŹŸ³QÒ¡DŸ$©÷òk;Ê>c71÷_í˜h,ÎåßêàæwüvŒé;ë•‚ÈíËððâ.Îê`î¾ÌlÿuVü»O£Ã‹Ž�{ú49@ À-}šàF€u}àE€†>- HE€©}Ú�C`¦¨°�fj€™Z>«h (ÂCXÉa/¤Rn‡=h¤ì˾ÐZ´ÃA›d}˜º«+#k£¶NL²NÕ>Í¡áû2‡ÐÚBËNŸã«èÀÈ.Ve]`\tµ °ÿVáß)øw¾]Ž^¤ý5ò!ãå¾þÖî£-œ‘º1õ1ý-» ÏÆ ïÆŒ”æFK~dZBÃÈHÓöÆ>Þ;¸2Ò‘2Õz¹5Ör«õr«£íÙØ>[·Ï±ÚgëöÙÑöœû°Ë]™yüa ŽŽ!¯æ#×%o¿¼Î’wÀMòNxDn„å]Ì%ïfÃå&6YnaSºÇÉ­ì¹­‘÷³“åìoòA¶Aîdr»]>Ì•»Ù{òöÜÃ>’±Ïåã|¹|‚¯”Oòù?I>Í#ò¾Z>ËO–ÏñSåóü ù2?O¾Â¯”¯òÍò5~¯|ƒ?/ßä/É·ù×ò_ü[ù®Èï‰lùA\‰‰Ü¨›‚¹ïùZ‰½Ê´D€û2ôáê¾!Ó£z;;5ïÜš¢.Vk_þï‚E:nœ«ÈVœ«âVIZ ·±:ÔKëš'o•lcó<RªÄDW®Ë§vÜ(Kr]ÛØ|BõR) ñ¥†ÏÝÅŽ-õP²°4Í—¶{¤[I%ÞÒLLd‹J³Xi¶½çnV?1GLÌÍÍÑlËfÐÿe%frAß¹±ñ?øÓ‰uäs,Õ×9l(³ÍfÇY7.ìx­nNHbVù¤Ï£…Í»›-¾—x¨éËÞÆü 좷.K.2bðË1ŒwX¼¯Çê=Ôì€IûEC—-è®bb¿Ü~HðÙí¾´Ü~ãKó¢«6h½y1E;—‰yz?fS~ j¼CtÊÓtZ²:QçŒÌë ÕÊe_“ʳ–]š×­êÜë!Ó—…cJ_ž–¸µºþNb‚Àºw¥ÿ�mæC)þeÚi¶æØ©æ_:-ÀZü_Fn¿}mN\Ž+"á”M°±Ž¨Ý÷ÛW„SÇÒÝ{ìx”Fä˜Å s°¶0Z»/”¹®ëømžs{¢D-56ÍãÜ4܉ÆÒ~bb^nž¯ãq, GYjУ|ýróÆk è‘séù]êgôF–æ‘PÈfËluyi_ÿ=îë_äsocAœ°+J}>•ë*Ôý|È2¾ \YN?³Ÿ²ï^¶ÒçëbM¹¹¾,_v7kÆå—–p\#e³Pÿ´Ú•V²ï–Ö¹XÇÏȘ.uikÚ§¬1d0YYŸ Mæ”Ò˜úî,¤e MT°„¬‘§ÈÓY‹Ü-ß’ï ½#?¤Ô’û¬æbÝW¨Â¿F‡è,¿ƒQò0A~zà;˜!‚9òg¨—¿Àrù+4Ëß ‚vៃ³‡ ”€Ë”„¿+W+ܬÜС –£¼l¦2Y­JeÇ«4Ö¤ÒYHe³KT»Få²ëU{Hõg¨ì=5}¦±/Õö‹ÊÝjOSòQj8?Xà“ÕHÞª ø:UÈ/WÅüjUÂoWãùÇêPþ:LôS‡ Ÿš$†ªÉâ u¤˜«¦ˆeê(±BM§ªrq†š.:ÔPq—ª»U¥xSU‰÷UµøBÕÈt5[f©92WU˪NSóäÁj¾<L-ÓÔBY£êå)j©<]5Ê3U@vÉŸåj¹Ü­‚òYµB¾®VÊU“üZ5“>bÃa0äÃHí,*žÆÖkÍ”Ç~gw󟰮Ÿø&è;ˆ<y&åøÏ0�)[Ãá¢KÓ,+ø¯¸uDäTþöõ°&QÉçÕàeÇ‹…hÍV£Ùþ'9I0týÒPK"¸h&_ K„Ä\\&G %\(%þ.‡·0мZáE‰q³Ì)8ƒèéÂä+Þ‡E*âë'‡ÁÚ­Í“ÃÙ"M¤CYËE†ÈÔÊ ^›‹ô©í6PŽ~†\ô¹v9òÒ‡"Kd£ñ=5:éd‰|t£ÈôöÄKÿ¾¯í‚­Ã2ťߦÇd5mc'1Ø�iô„Ôæî¢Ø£½ÜN˜[X”ðf¯(öh ë ‹ÞŒï¡ZëámqìÑ-Úm aOQÆäYÛ&ðI”&“û$™ Ô!Cg鮺€d  •à™ô–ºJvû\¶#û"¼‚+}^שeyUâÞ€ ƒKE ]µÃ�µ F¨Õp¨ZSÕZ¨S'ñêO°B —¨³á¯j\«Îê\xLObùyu¼¥.޹LWÙïxüŽy¸¯¤7ÞŽî¼-úëçÜT·œ½œyéßú"çyd íδ¯/r(„[´…ne‘.Ö¾‹­êñÖ[Ýä)äįɱƒÆ^1(o‡Ã ;0ÜS‡Ã15lL^äáÁvÿr\‹~OVˆSZ¦Üšml­€žhîr>=ÃÃ0DO(]G»™^æ6ÂûõUF¡^c1®q;¹7ÆNÇS« Äh-1ÃÆH9š,g‘/†"c{Ä0q N‡‹:)Hä¢@ŒFx(Eö!¸±Ò!øO6ûSU6;¥:›šüQB;Íz.™âCüt¬Ä.tU³ +m;C·B÷4º s±÷5ZÏœI*Ç}ZÓÉ΢ú¿ÆúŸ­ûŸë9„‰©¯uŽÞØÍÙtÖv³s£/d»Ùyް¢›KéGÇêQj¸ S=#Õãp˜zÀSx�žÔ³p²zUÎóp…zÀK°M½ ªÂ'êÕØ…RP‹m(}$Æè:B`"ãÓ»‘ðˆ8HŒG¨Ã S¬/–êà*q½yab‚Þ©1Qªwì0q¸NKÅ$NGèôH1E§G‰2NÕí^ú×݈}1nsY�çAñßA%DÝ ÐÖ¡ôBL»ÙE>Ùãw9®ÉÐÏu¤ã¡OÌ@É:w´,R®XÌÔ éãÇŠ 8…J1KO¥JTë´ÆfºÙbŽNsuZ+êìtžÎ·Óvz¬.´ÓE:­·ûÕÛýêí~õv¿z»_½Ý¯Þîwœ8^§'ˆÅ:=Qøuº„¦ ¢Q§±T§ËÄrM Xáøe“~j9¤›]\–Ÿð˦üÞ¿lrÊu†ó—Mb¥¦`ô—M$õšìQ›E‹NC¢U§'‰6†ED§íb•NW‹5:]+NÖéŸÄ):=Uœ¦ÓÓíú3Ä™úÈŸ¥ñ{ÄÙz<b8Gýsõ¯ß¬H½%Ó¬7½¡Èø­›]Ò#lëÚ©®‡œ‘zÄСésa?edû 9<¦µ~ã6?úãCúwåmø;Pœ…’XnªÚ.<¾î¢.0vê>�TNÈnˆQÓ¦¸¿¥á²ÄE”‹p±¸$6£è}ðM}ÎèRþ™ _ê]ëùûÂùΰµ¸ÈÖ1.q!Ú±WŠØ»Fv¥)Ø@ú%åvv)êŠ?o¹±\},wd,7z;» s—oaù, ÚÍþ‚âê¯[X®ÔÉJ'.—N®ìfWe3ëõ þŽboV6[æCpœ˜Í6Г6Çã¨"ûyi6»:–ûG,wM,wm,w],w}üi*=«Êf7d³ñÛ §v±›ºÙÍÛÙ-8÷[·°Žíì6ÌݾÙÎîÀÜ[€GslÂv¶swmawÇI¼;ú%©„ãѨ<úÃb'B1øa",G6=¦À0΄j8 ¥åÙPç!äù° .€•p„àbX — Ô¥hà\ £Qöȃ_äæ’G± YÛÂ3`¾¸LËç¬Õrg".×ß¿°NLÛ mb÷ xÙLïÅô>L»\ö°šÈv»<8ÃQ¼<ð´µùð¼¾k¥¬ÈN/³Ó[ìô6;Ýh¥òøJ½ÆÿPK ���(©Æ@ `ë§ï��¬��!���com/sun/jna/NativeLibrary$1.class}S]oÓ0=n³†&¥ƒnãkŒñQ í>Òò†Šx™˜T)­*1žÜ`e.ƒ§¢ÿ $~�? qUPèh$Û×¾çžs¯¯óãç×ï�Úh—QÂ.ê.zx„ÇŠh¸hºh1”Ì™Lëm†í0J΃4SÁXñà„9¡i®g]†r¦Ì™02¢gRIóœ!l¬ˆYåó)&\ÅAßh©ân¯9dpŽ’7‚a=”Jœdç#¡|4¡“Z˜D|2äZÚýüб‰3€¡ÒSJè£ OSA';ÿW®w¨”Âû¶:–w)ëyÂÀzT¦TÓä-)=m¼^�¾EdkÈ•»§ÍeeÉuL9m\ÀÀàia2­³w¤ru‰‘ÒÑ8%`?Ét$Že~•th£*¸„r>öˆçMO íb¿‚Z@@^q= ›‹ÞãLEF&Š¡³²Õ÷³¶AÍŠ… yj^hh†b£IWí4(�w饺°­Ùzhõ`¿ Ú_&kk´V[{ŸÀZû[Îg>æ¸*Í%¬ÓÜ",!q9S›Ä[È­-\£ÿÀZ×q#ÇÝÄ­¹FŸ<öÄÿ†â«/ µÿ üß>¶q;÷ûØÁyê»TZ‘ì{9Ç}*T½°<¿�PK ���(©Æ@ç›@s-��Ù��!���com/sun/jna/NativeLibrary$2.class}S]oA=S…íÚlÛZ­-*¥ÈbEÔ`|°‰‰ ÁL­¾ ËP†,»dw é1©À_L”Müþ(ãbZZ7™½wΜ¹÷Ü;3¿ÿüø ˆ§)Ìâ¶ [¶Mİ•DFÛ;IÜÕöž‰,vLä°k oà¾Ã쀻W6j¼+ÒÕpÛåÞ‘]WôŽ* ©ú±§ÚBI‡!ñLzR=gXÎNSwâû~“ÍW¥'jýnCoxÃBûwx õ| ÆU[† `°^yžö]†‚ªãwí°ïÙÛ5®ä@Te#àÁqf4%¸ãˆžbØë¾ýRº¢rª÷ ±¦ æÎR’-²^T¹Y÷û#ôi=“° wY˜Ã¼…EØ +çs¼è·Z‚Ĩ‘Ær ¡oa ”,<BÙÀc OPfXÿOi ÿ‚¿nt„£&ùƲµ\²*Ê—Õ-Oð^OxM†üEg2Äêê•?B–²Ó¼Ê-¦*¨ð­TíK.µ{õÒ )€>¹0Ì‹EêÔ,]X†«4ÒºÁd ¿‹„1\#¯Ds˜¹Ýo`¹SÌ|‰8KôO3%,“¯c +X…¾TkXGø@ Fö0ÿ±w§ˆ§¯ ‘È}#“6è7DrˆÔGä'ŒéŬiPo%.ÿæ Œø'ÄcŸÏé)Gz6G9Çz´w‘ÆCܤjuU·¢›¸)§1“PK ���(©Æ@ë$N²��p6�����com/sun/jna/NativeLibrary.class•Z |Tå•?ç›Ç½3¹@0<¢„IB�yŽ€B$HB$Pœ$20™‰3¬ÅZmë£ÕºZ Vlµš>°FŒŠÅÖÖÚwu[mí¶uw»í¶ÛíÃv[ÙÿùîÉ$ì†÷~÷»ß=ßyþÏ9_òõ÷ž{‘ˆæªú©’·øùn4x«ÁÛüäæír¹ÖàëüdȃÁ;L¾^î¹4ùéo7¸ÙO#y»Z„JTF­rÙ)—6“c¼‹w÷Óxn—É„\’~îàLNùé.NË%cp§É{ý4÷ ©ýòáMÀO3Á�ßÌ, Ù|@¾¾ÅàÉí&XÖÞ&·Ëè#²ð£L–Ýaò&ßå§¹|·ì÷qYñ Ýcò½²é'M¾Ïä2ø~?-Ã"\0yŠÉŸ2ùAùî!ƒ»ä«ƒ&?,÷OËWÈå\•ËgäòY?ÆË’ωJžqŸ”­ºMþ¼0óQÛ þ’Ÿ6ña“Ÿ’µ_–‡§eÔ#—g„#rÑGý ù¬ŒŽù¹— ™>“OÈÌsb¯~ƒŸ7ù?Eùnƒ_ôSŸ4ø%yÿ?íâS&¿lòWE'_3ù´Ég„‰6ùYòuƒ¿!J~ÕOüÍ~¿UÀßæïÈå»~þ.àïÛs??4yŸ_ç7DÔ1øG>0úc?ÝÊoü–Ÿn?¹•"—ŸŠo‹Y¾eðÏäáMÑÒ¿šüs!ñi“aò/M~Çä“ÿîçÿà_ɺÿôó¯ù7ÿ—Ÿ²-ò[ïÉß‘þ®gò‹roðÿx¯ht^€£?øÀã þ“ÉöÓ|·ÉïšüÙî¯bˆÿ•ËßLþ»šyÏ䳦"S±©^(—\ܦò˜Ê+CÃT¦©|2ô›ªÀT–©F˜j¤©F'U²j´¡LÞ¶H¢%eâuLñXS*’ÚWiÇL fWdO¤"Iì¬hȤb‰— ,©dÚ˜|­‰æL,™H3²—wfbñŠÚHÖúš#ñxU<²o¹šÉHv8k}6•Xã‚t4’jn‚xíÐoÈM2æQ®‰¥3 =³9I§g6'Ûg¦;3w%"3k"éÌêT*™Z½·9ª7µ<*å áªa_"ÓÍÄš±w–ÿõÑ}L³JΕ¸zöpZpW&[¢"r,­ëloЦ6EšâZgI½¢É³3éNh}zZmUx—Å±Ì ¦ÅÃìwî̺!z½ÚleWé+ }¤¯¨ræ2m1l3iÐûºH&¶'Zc+‹¼IGEùVÞд+Ú,ê-–Ãæ•ÉÄžhÂ^XOFZL+ÎËû�£d€¡‘â<W'ÒX‘nE±Ib·¶$–©è|&:Ϭ£MéL$•± š;Ó™d»ã>#wF35ùN|É0|kR:ÖÞÉþP᪕ «™ÌLs‘ èø:RÑÖØÞÕ ¼õ¦;[1×ÕƒáOØ»:ÖÍø bØ/¤wkL¼e¤ý],YQ…gÌ»°3Ó”<r˜¨ØìÞm¦¢Ø«ŒxÖ4~ðQŸJ6GÓPÈ”’ oZVòÿ²ÚˆHKK~h2ÃÈ —uwØæsY_e ½¯’òüÚÊ«mÙ+/¼5t‡3WÔF3mÉ–óSå Ú®1M<?¬Šföub¶ +¥ÃƒÇyvuíÀ1[sš£mÈÂãè’Ùç€é,XO6  ËÊ––”¶ñì÷Õb}2–ÈDSÚÍ÷µ7%ã¶ A³A?çh Gk„BÒ~b[2l� f“•Q•öåÂìÒo6[c‰H<¶o]%âþ–Xº#™Ž®ŒÇ™‚C¼<ßÃ]ðpìž§™jÈÉhœðî‰Ä;£CÒPCTÀÌp6@ ð:H1óüÀŸh<š.p(HÍ£­?Ù”ÜM@aÁÉ¢Q0‹“v,º0Vê†U©ŠeìÀ¶õ:²=Ò1ÜFÅÒ›£©4œ&ÚbÏ k¾­ Ô >¸RäH"’Ú#™æ¶–{xDuÛΞ=Ñ8Øco WBÓX‘é ƒVKŽ«ÒoDåzÝ›¢éÌæì÷>yª•w`¢#’JGs¯†åÛ¹íLÓó^Ú ·*™Â&¹\/‘¼GÛzO,w®–$”çµë¦‹ß'dÃXíÇ4c苺d¦*Ù™hÉßNí ç4 ÎΜ;æ²æ¸“Ú]ÍØ}w4•ˆÆµk4¡ÜÑÞÏÄVæÐÓÕK‰"bZ!Ž3™Žv€Ö�Æz:l³m8ñ7$;SÍQ;ÞƒðzŽ,·¸œçjŒ¥Æ2RZñûIÆ45OÕI½èÊh«½H’²E?扖*RãPƒ ehb4e©ñj ò-ËE�µ_I·Õ,úgzÅ¢_Óo,ú¶èŒ\^¦¯B§�)oÎU †Â®“Ôä,%½k5`m§l8EMµèëô ‹^¥W 5ÍRÅj:Ôh[àÒù–šÁE–ºHÁ&7ÃÍ„ñ ©N$¢)-ºø°µEE¶º„øêb‹¾O?°Ô%j�)?’»¥|Ig Ub©ÙL…ÔL‹^£W¾ƒCÞR¥ª  &æ8¹{ŽXÚâ|‘¼,·¸„g[jd”á*%‹¾EßFHVEà-Å™d±TkÅÝb°É³„ó KÍUÓ-ž/„牆¼sZöa™¥æó¨¦¢a_UO…£ŠªâúÆdjwºÁ<§5ûˆ'+?ü-u©P›puBRÓp ¸f…‹-µ@\!x^€GzkËË!}™èo¡Zd©Åj‰¥–ò…-u™šj¨e–Z.îóú¥H¶B.—[ê aƒ›-ú&^ÂýVB¨ TNâR«�LË:ìŠ d~a¬´x\†ÇóK]‰vú£¥V«*‹™ý†Zc©µªÚ¢ïÐwaˆ4"ÍsžÛŠX@ÎÕ–Z§ÖޝÎxÜÉÍy8%[( ¨·Gö'’™â¦hq¢S’äØáŠ a¹ 1LÚ·8Àcä}-Kóã$w#¼Š;;Ä4AGûÅm‘4¶‰&Š;b¾h‹¥êÔ8©m¤âìºePñ¢Ýz VX<'žàH¾OSWÉÆ Õ`©MêjCm¶Ôx ºF­2T£¡¶Zj›àÉvUk¨k-uÚa©ëÅ™ƒçM¶–ŠRü˜ÎBàU+S©È>;´šd‹f‰øñ‡Â¡8e©¨Z„tsÅtYáš#‰Ñ=§%µGßDÏnÌÁåZÕNCµY*¦vYj7*n©v•¸ �p’ªÃâåŒl Ž{ƒì7í}²˜¥R*m© }Ùx`­¶DZB6¨Ä)$ßÄt=…òµ�ÒH±c'Ø ¸b6· †u„V§(iŽ಩YÑ™NUh¸í›0܉^ǵ3Ñi©½òÉ>{—,Ú±hÖÅ.]‚Ý/“¾ŽäÑTG³ÌÎØR7É+W$Õ.¥a¢3iBÍ´D¾ìn@¾[ê²ÃÍjÇ £Ùé htAg—>º¸«ŒtJ¡W–Ÿí7µ¥’7 * Jø³%ÒÑ•ήìêí$‡ï&• s<{;bP /Œf†4¤NÃ=ûÜ)˜±»Ù.>Q(W ûzÇ®"wz *Îäi½Å^A&Y#º¯Œˆ.¼Ñ:#ñ¡¥}vg,¯)¹@Ëw¡WÃô;¨ç]sÉ0›ýƒ‚ÎÝ•”·%Ú¤[íAŵ ©X°¥E7KªóJëÊd\z8)ØDÒ`Éùß¹“pó”¡Êˆ¥kì@ðÄÒµs&<š¼Ñ½@´ÝÅÕ"‹DvJ«D÷nh=QXÔ—îlJ;]Õ¸’êáÏšüú°ÃÙ©h8F¾S/N@9q[][¿©qGíJ vàœž_úóm=H·RŽ6ÊÞÇM‡øúàž0gÊa]»Œ‚žV6¥“ñÎLÔ9ãKïK4#$ßu…*<ŸÍü&)¿?›p¾†ù›äu“ݺƒÏáš÷BHNÇvêâ¥*•l—•ç¬7™wÁ`>"Ü%ët£+¡Ýn‹¹× ßhOÂâóëy “<¾€soE BfÌ霅‘|ŠyuàÜYø4*„ºèÞŒjêÛøóôëƒêânà©©h{R"ÚÓ×My~nÖµ§}HTŸB@¦2û†@ÉùϘ†Ûëkª6A ’ú¢h;mYF@–Úd*ªË ‘H?àC8„ãŸrò‰z®:ÌÞæ¶HjeÆŽ˜Êlb» ÞDšus ¼¸2¶3¦—UŠãøD¦t“ÉŠ<P³È¡p$m{ýÅ%ÛÎyùŽï“Ó†*»qÏ�ÛŠÏ>§¥7uÛ]-lŒÊ9¢ŠÏ= zeÕo¨®Û´z㎆꭫uöiHÆ#)9qƸ*®j@Ûî¶_C Ò¢ÅõhÑ‚U¢@L}}¥ 5Q’ˆªR3)‡éÛÓtª¤SúW'JúBŒÒ)êûçŽ÷BiþôýUý<Qê}͹£AÂÝ”r-Þ÷ôo^¦‚2:9<ÿO5ØEá>¡ŸŒÆãd†zÉðã2©— zÉêÁ;E¯ãêÅÞÞÀuœý ý‹æGF?'ŒY´26eÞŒ/ ðîPè8õÓHuŒ C¡Òã4úBeÇhLhú1ôŠNÐ8EaoÐûßE#ƒÞ—h|/Mè&·k‘*2ŽQ0ô£‰}4©‹Ö&‡ž¥@/M饩]´ …aóT?Mk …Åîã4=ì“Ù@1®Á>šô ‹\´%h¾ÜMa?îAÿÉgÀsœî£ÀétPßmyWÐ(\«ÈM 0Z×Òª¦´ŽÊi=4Q‹§:Ú@©…@eí§Fº‡¶Ò#´]ëi#t¿ŸŠè-ú ùDôSz[kì¶)ë‘XUé‘ØÛ¥GbQö¹~Fÿ MVÒ6ú9tlÀzhÖl»\ú™øÁ~š õ^\ê£KêÊOSAYù š¥hË* iÕ¾D³Ãî û4MºÞSe® û…D!­/îAïɲ@i• r|*ë£9ao1ÞõQÅbo7 ›eGhžý~œ×wÿAZâ,sÖØ“õÓÜFǧóáOÚ±.ÅNuœœì¦×Ãf-ì¢òý"ùþ4ÝŸÛèΰ/è ›Ýô¡>ZÜE5¡À’^Zz*³DÃY¢—9{úiYcÐsœ–÷ÒŠ.Z#wY–Ü|í?eY*—|<HŠnòÙ_Øâ”æ‹sEv畸Í^Z5 V?U6ÂOÇyáäWöÀ<óérZ“®‚ ÉýzÚI»h.í¡#t ÷W¢ß£¹<§ñ Ü×ñU¼ ó¶>F%¸î€5Ó¸ÙhŠRvÓ,¸[µƒz‚–Q.™¢Í”¦k)ƒn·`—›±ê�vº£ÓmøwÝgÿö>ŒÝï¤ã˜y.ûUºœÜ^îG�?DÿI]죇y=ÂÓèQ.¥Çy}Ž×Ñ|=É[éó¡/ðô”vôýda/?½Cÿ^€ ùwŒ|<37ÚœaÏï:ëิÉH\XÍ÷ЀE#øNúÞºhw€£oƒÂµÖáâ¡ëy±/Vþš~ãÀÙo±tiKim`u]`uU‰×V÷ÒšEž"Ï! –Ý®"O/­í¥ê.ò—Éx]mÐ]ä‘Uë¯ÇSØ[ôÊßGL®"Ô–  ám‚GÀÌQˆÿ,âüͦ^¯F?ƒ?Œè§«¡â,€µlq0@aÅi ð`ÍoµÈæw¹Auý7"ßšè÷˜ó‚r!ýFƒ œÐ8ÂÏA3t„jú¨v(vSsaÙkÅË÷šð×@ÏÀ}¼€5ü·®¦ÔÁå34Ã~ßè:NlEì$_Õ­W…4{xýÚØX{ ×¼„RÚK ˆ‡6 @(參_¢Jà;‹…ÎÐC¡34^Â'°™1(vw“?@Úv÷Ó¦Æ pýšºâAw/5lPî <f·ÚxR™·vÞvÎÚÁï·C«ìåøƒ@#”¹ŠGq!TTÈcåî¨w#Åõ§ðí·1ú] ÿÃ×Á“WÁ‡¯‡÷Þ�Ý3ï‡?3>�Å? Ó¤?Á†1ßŪ¿pý ”ÿ®Mµ�F?M«±âÏâñÔƒ5sÜ€/ÿнÜb¬\w"h)ý/ýÍq‰)ÚØÃôѵâ<àlè¥w–Îu¼ÇË¡s}‡GçùŽ×©7‚ôu¾> ¶<sH8Úþi§®ü ÕG×ÕÉ;G»å¥v–*Cò‡¡ê [gd/-† ÿWè»ÍÉ “ÁáÃSi< iS9_D 5+ùbÍe1xƒÕ. ½ŽŠ`>ÒÇøQç+°F²/üL*…^Ú1Töùy5?½~vÙ´Ü­~Ðòâ¾F(­(ë¥ëÃ(_€;A÷‹QùŠœ¡Q¦>jn<J‘nòbF ²¥‹Œ)Æ-+nóp÷Ùï„J§ bdªÌJØñJ*àÕ4Ž«h¯¡0ÆW�•ßЂxi$Íg 0‘cu.ÔÄtkØ ³ ЬÒ%› 0¶ŒM`½[+ÅïòVGÏЈ~Š6ZÓΓºÌª;U:õTØ-ORDh3·IÀ¡)ýÔÖ*|œba½ èqBÉS.¡Tî„R*§zlQº©>'hH×3×�i$ÒLo£É¼ÊøZšÇ×ÑrÞAë‘zê¹)"“iÂF°²Œf²…ZË3g³–œT̰<à‘Ž¨â"àvâPÓïÌs{C5Ø›Š‹-ì§]»—v§xOm~µÐncŠ]-” *4Y ‰=›îG 1¾©/ é39o‰„/)S<paN®…ˆJÛ<ÆašÓ«¤<+•Ëá%íd Xš|*'¤½ß¤ç[Q·æùùäÜ.“çaù•–³ËF|'¼—d…ì’èY#rÞ`ÇöR*¶…ªÖÛóÔZ’Së¸ý|›†~|ǰ6ZÅþxÃ\‘0t!Ó#E¡< ,ì"û½dð'!ÿ}yD‹¢nòD‡è¥9Žz)óLŽˆ4iÄŸÂÒ‡áÊäI9_ÇvÀk’ìÕXƒF¥³£´‡Qݸ¾hÖ–¾\v¸¹—©Ȉΰ‹Š1ØÏ’6ká<7!eJú4>3Ýgß²ÁrMåI@‰ICéÓ�ËG€‡¨˜Euù ÆgƒQ?Nëø ªå'5ç!pFYñ.�y¼O ¨½èö�lޱ5j±Ööôœ\ïâ;¼]¶\Y™NИjË!¦–e…¨ã7…žD$œ ›™ºÏþ´ÔÆþRÁþ(ØŽÛE£dÐG ùÐ!Ú‹<XäA‚ô».§•ú¾5rîMÔªï¶!´ˆ¿|‰¦ðaÔ‘OQÓYž¡+ù­å£t?K×ð1T—€-dÊlØÍ‚“ˆ>\Èê3µ$4v9ÖõË/--œO:µ¡À>º¥&p TDÆúÜïÖÆÒ#ôáãtávÌGê`ËvÑ4Ü>vHÉ2õ ªÁSɈÝgß.Rm2‚Ÿ§B~ÆòIô%*á¯@ Sèeºœ¿ aNÏäq!¢š42‚5‘X¯B®”ˆ–Ìy1_‚=Jèbž¥³¥_~ÿêÔƒ%È·bÚûuüèžêÔx¡UµðË;™êÊw”;&¾KJ54aî²ãt·$݉ùtùÕ£{¹Ueº—뢥öBDÊ\zþãº4÷>‘ÅÉsˆtŸýx^%®ûtþ6ðò;ÐÍëÈ?‚4?†³¿‰ÔöŒü8úÛÈ?§Fþ}ˆIwð;tÿŠîãßåRç\Úáèjºy»äš WÁìñ÷£ ²«ˆûuƒÂÀ‰´V–mAx¾Óݼ–µ.$-&ñÙÖ ‰hK³Š¼E¶ý<ÝÑC©KÏdõ°ÄÐÒÝ-áÖTîÕ_- õè|Lv#‡lîs±ë'³»Þ—¿ë€þÖQ�\ÿAòGšÎïÒEüWøÕ{ȶgiƒbºJ!w+7íT^Š)%•ŸöªÚ¯FÒ5šnWzP¡.5>—?JÐeKŽCŽÞüòGN¸ôbøäº,Ó«B‚ÈË&¡ÝZ4ɸ(»þ1*Ãhùd{<54¹—î_1¥8HS ç<J×áÛª¥·Ý‡]‡ŸšD>5™ Õš¦¦R©šFóT1­PÓé <W©™´VÍÊß<Å<v¾ò{tPˆ˜§OµDGöI—)DàØùE|x¡W³Ñ!øŒø¸_ »]xøTC=XÓOÉë®:§Ôéȯvbg¡}j‘ ÐJ”ðÓ4.è}ñ.£¼ fŸ»tŸ=3–^êá°!x!ÄÌ é[&#Mh‚E1‚>­\C·¶þ ¿YZ0¡`‚çÁC„ûROÐ6ºÏ¾4¼b3¼›Ô\hq´8Z\@%j9´¸‚*Õå´U]AQµ’Rª’:ÕZxC5ݬÖѽj==ªjèiUK½ªŽ^T蔪§Ój#½¢´ÆoƒÞ¶Ò¾” î–"J%Æ<hjféôÑÓ¨íb䇧ò"´úôªÔ뤇~5‹¯:±è£—‘°þ¬}ïs¼„—–óèaëcƒý´‘/ÝLx¾5`Ëå¼Â±¥TíB÷ê‘kGÁ'éÓ‚Ðiª*òÀ!‹CöAØ*ò¸E‹ë³k´Ca/¯Ÿ%Öyô£³“Ík»É#Ÿž=–>»{]÷Ùçg=M–P%UÑgåVÑh(w )…rM5’¥¶BÉÛQH_ E_Eï KU-RÍ´DµBá»à²mpÙ­S»©^Å©A%´2×B”iH—냵Øá¾s‹h¢3±´2Å´ ¼j`¨ä*^…‘4T£¯ŽÁ”¿×À Ð:çX8�¬P=ða7\׃œæµ‹VÉv®AUYU.ø½dqzYàµzgûèÙäj@€RâÏ¢×>©¨È/¦†X—-=\ËÁ„ObÝ9\>*½¼õÑãG¥8Õw”æ¡ÏéJø‰ÆÀ“Ç©ûdàó}tÉzxÄš²3nà }ôÅ3| ¸yŸ@¯ ¨ÓÇ ø%}Øq¸‹f90©.T¾Ou“'p�}„¾\h¶Ù§æu†Æ€luü4^—Û0ÞИMwžüYOàé†Fo §!ìÖ0ÿVà@Øx&ì  }tÀßGÏʆÇdÃÞ0\ov˜ï“ù2?ºžë¢‚@?(<6òÎ7|äÈÒ›ι©YèÄñ‹ÎÉæ–Ošö +ŸwXù ‘ÏÔò 4ÄUvþGÿ ȸÓY>$÷—ºhF6Ú}“Ïi”…^ ª©ZÝ)÷Ù•ÖAò¾€Þæ+[`P±¤s‚=P©¤Kp]HYJãQ=NGÒŸE—ÐR´X¥é2•¡ÕjâjmVû©QÝDÛÕÇ(¡î¤ûÔÝôšz¾¯ºè uÞTÓÛêzG¢wï©Ïp¡ú,SñDõ8OUOr¥ú"YåêY>£Žñ몗¯ŽóŸ0÷7õœò¨~e©ÕDuR;{ î ú!¯×§›oÒw¹FGïÛô× `!JwsF& ¼ Þ�°ó¢†]ïôû›¹—ëuÊmä£|•.K¶soÔlj¯ñ nàMˆÛÅ(ƒ~e£_­¯›ùF‰T¬ŸFöÏCv¨ýPK ���(©Æ@,üá[�������com/sun/jna/NativeLong.classmÏNÂ@Æ¿åOÛ­UPPà†ă7‰‰1jJ<`8x1Km°¤´Z <xð|(ãtmÔ¨—Ùovv~ßì¼½¿¼8BMGN¡¨CA‰c»*Ê** ™žy{ÁÀL¥íúnxÊnô©rÜ; 9Ëõn48“1ðè&o¶ðúbâÆyr™ Ü)Cɲƒqkù­‘/Z]º3Ç üá ½htblv&¼ˆX‡AkÛ^â©÷‚hb;—n Ë}7ŽÄLàX7 B‹ÕžŠ}Uh …ÿÝŠ? ¦:Cÿé‘ØÙ†)çÈÿmeàÖu÷ê.^ ê´/…öÇh[dL*MšC§¸Bٱ̥ɗ`sR)u:2uW°JÊø|…5ä$~’Î!À› ¤ªK¤Cjd[—‚Ìy‰Õò„Ѱù5PUê™g¤æÒÍÞq¡HqKl�PK ���(©Æ@P8ÿµ���'�����com/sun/jna/NativeMapped.classmŽÍÁ@…Ï­ŸR$„…G¨±ïRX ^`Ô65Ó´Cx5 à¡Ä”wqÏâ|÷œ{¹žÎ�hب٨œuª¶S®Ã½ Œ\?â{Îb.7l¶ŒD =?P[–í$‹$gã<TR‹ƒöz¿„ŠVÏÈŽû—päÝ_ô?˜a̳,Gæj—bÆi=ò&<IĪŸÃ„îûgï>¡ù]Y&,äS( (FK(ßÕFÅ8„ªÙœPK ���(©Æ@'Ø^“��"��'���com/sun/jna/NativeMappedConverter.classVùWÔVþ–!Īƒ ¸`Ôaf`\ÐZ±VE©R­P,j«!d†àL‚I±{í¾ï‹v_ýÕ¥‚”sª§žSÏéЦ§÷f†afÕ“sÞKÞûÞ½÷»ÛËßÿþþ€øU‚†!ºˆ„„r$y®†£#8#"%!€´S¢EKĨgyÓ–C’±N�.ogXÚ˜ˆs"Æ%Ô!ÀyžŸááYž“°Ïó™øÀ‹üöR Öâe~»Àç_ñªˆ×x=€7D¼À[¼Í˜wón�ïñÇû">Њ¤ˆE|$@Ò,sL·]Ýv,ìQÇÔXÆ5R±Cêh‡€ ÷ü¨.`qv#¥šÉXgJuÚ’LÕ5Æô>0LÇUM^—÷hV:ædÌØˆ©Æâˆ„êCt¨QãÓÍ„h&D3!š ª{Ï›î°îš€†¹à#–a’­j’º{0¯s[hŽ-óšÑ9ØùuZC:7L=žIêvŸ:˜¢•`¥©©~Õ6ø;·(â†M¶žˆÕº­“$­ÜL“ÙM÷¥¹\K‘Ç«v¦áî°Ä‡A?ûØ ˜<¤'ÔLÊíWS²ceh~v,:í$™Á¬À^×6Ì$m tzmÁFÖ…®A,sÿ¸¦òKG (•Ò“jj¦éŽS’¶•Î*°¿ÁáÁ]s;ЬìʃÉ®>îRŒæœ Èfó*G5赬�«ï¡A@m¨Å'y®5cwç½ìî³îÃêʱ¬½«þ÷(<›ÓÖ…æÆÇÏÒ²ñMÅÑð6â–ÛeeÌ¡Âh”o$b;µT.¥ÊCœAR¯•±5½Ëà^î›m,\ÄÇ2>Á ʽtQÏBâ–Ú§'² Û¶lûqZƧøLÆ&lñ¹Œ/𥀥óÔo_°æž…Ã’»¬(–Ók%ÜYûqIÆWøZÆv´ XFRÛHjIm+”*ã8NÈøß’‡ç¦»Ì¤uÓ- ]_±½™D‚»Q=wA%q\eH·I•¡ÈøßóðƒŒñ[ö³Œ-h—q�elÃ2¶â híöÁ²ù|Pÿº¥ÎЩš\E³uÕÕÕTfú²b%rpT±õ³ÃÖEUL«Uµ“ “ãÚ͵ìJ±ß§ܥ”PÔTÊ:§)®5¯º–"u£™Á”¡E}ÕÊ8Œ#2vp¼j ã•kø2úÙ9u³÷Ó1]=s@u†É9•V$_…uHáÉçµû’ §Ž’°ì¸š¦©æêéT3½G k´oضÎñ=PT¦³«ÝžT€tG XïÓ_|[È’ïrßy*ìòÑ Ißî×½îOß"ÃÙã8FÒdó¹uúßBÇ©iqšC%ñiZ…UÑQ÷1¥NM9»Rê‹üõUç§¿Ÿï ýÜÌß�Ö€±úO«Ârî?ôÏäªóæ­¹™jæ�·Â>x_;оªÑôý0}M¢’àÐ5T ôüIcx bn#p(úD'P}ÒeTñÖ»hœ†4žDMœ‘ÓZ'±` ”ãXkäΕ‘; ·PFFRK#Á]èöæ2ì¢q-©VÒî*Ô¢MX PˆÔ쥯.4ãB(ôçYKävcl¢g/:i Å>ªàÉÏ’˜ e4e ¯aÑ], .¾‰àÀu,ºŒ*Z‰L ö6OcÉÀ4ê&Q\:e¾Ø 4L`ù$VÜ Gn`e8<U7Ðÿ†Æ)¬pÊÕ<£Fˆ4n@Bdu |}ˆâ$ýŽhóØÔg­Ã£ÄÞÛ/ ÷ˇˆó;EjVN`Ím¬½Ò3ck3Ùš]o®£÷HÖÆCÌ'Ê–æ‘ !?¤gŠ„r)Ï[±¾ˆÈfÚiwî(eÒVÊ™m”=ý”/L¤¥üéÆctJ&º=ögÖ¶ykqZ#*9ÂwÁÍîM™½‰N¡¥WòάòÎíΧ�áòN“ñ8Žzû2zÉÅeôN—LNnÌs'PIr•Rû<r7ªÇr‡/š•µGî¢åœ‘lN„½œàØ+”BQ)\á|è‰P|¦ Œ¯÷ é�eÂArn7Ö‘#ÂäŠY2íy2íx2G¦È „�ßÚ4³=qš9LÁðMœåÛ eå-ïL…—ï… Cù<«"ž'ñ”·ÄÓä ÿNç7yòši´’ì¶ë¨ºê9eVd=ªGnð?PK ���(©Æ@­,‰x��× �����com/sun/jna/NativeString.classuT[WWþ&·IÆtbmª½L1ÖÒVDQ ±&FPƒP - a$ƒÉLL&\z³7zù ôøâƒíªÁU×¢«O]«§ï]Ý{2„ÂË™söìýío{ŸóÏl¸€Ÿ%œÁ˜„³q'ˆ»¼¸"cVÂî‡0‰)IašÍ3fñ±DæOx÷)/s 2Ïþšˆ>ä‚X Bñ@BÆyY "ÏÎFË|vüJ( È‹É›%¢$⑈²�±d¦­Ó®;“³Š‰JÕL,›ZâNÝ<(À·j,ê„+†iØCä«f–µ-QÐÌ¥DÖ.æÒ`t’|G-öíʦ>V-.èå m¡@%cå´Â¤V6øì}vÞ¨8±'ñ˜f+º‹I)+ÎŽöeÐ ñt“8§(É¢fk<³#‚y­’¯SôªQúÐUµB¥¥ ñ…e=gF©b¿eçYeÿo‚³­¬Ëî˜mÇO hiI·ïìˆ&¿ö2ry­<la5¥Ì†¹¨¯‘*Y²óUª Yâ«›9âß«¦RÍ)G)zç/¡ù+¶V&0¯n. QÊ’VÖ'¬*%)¤¬U-çôwæHsγ¿Œ(.òBÞ« n ɵœ^² ‹t×#ÅjÅŽ˜–YÐ#&yr|LÆ[PEØ2’¸$¢*cý¤p³,·õ¢U^g÷UoàMk2Öñ™ŒÏñ…Œ/ñ•ŒÇøZÆU Éx—hÚk!#[:s–I\VYä9;FjÖ |Ží ®S©>x —e|ƒo©‹ªŒïð=m¢2F0*bCÆøQÆ |È ~pü p¸Uo—&²Nkꂆrrÿ„²:65zsøÞ\65“ä©IsìÑ6ÃEý¯èöTXMpg‚4¨#ë¶^i¯¢|ƒü«eæÜA5=;BHab¥F?Õô[Z©äÌݹv0ûLu­ihCK»4{ÔôLÛ+uÔ±™†å´¹ËTY+ H¨] koÜà/Y zbϯÓêá¤×ÜãK_ O2­q:]£ÝC_1÷nAøÕq<縱ù ÕG»žºÎ#8» x›@ì¢ ö/yûè[ŒmÁÿ/áV|[ðÿë}Ž@ï&Ôx ¢oþ7zΆb/š>µ±é9Å~Ç¡PÜWƒü‰ø tŒ9¿û¶}óÍ}Þ¾í:yß·½á­¡k—òetÒš $r ÞC˜.ØI\"ðy\Å ®ÓšÄîSõ t/¸Ä~øÉ/L~ý¤J’ôz—¼\P£ì¢[6ïXOíé »\t‚±×päiƒ[À±'<rÝÇňÙe7>ëô Çÿ‚²‰#1Rëè/$Øø¼O½»p­4uáºq»©CáÕ0•zÅ!D› J%²×� ØDP9ö~¥;ó=Ó[”¶†ãÊ+´8Z³O 'Øv’–z3ñg-î¢ ÷Hl…—BMÙ5wH®cØ¥p5òsŽg-M5 äoDÏ–|Õ(Ä©S­ Ï6ñ5¤áê)çMÔ+±i®m¯èÑtãoQý¬@Ç¿À铽5¼ÚZOΉÔt:pӡûíxˆè¥wÇܺA~ßS†§¾Djx­µÿyuƒ2=l*ZidQýÏ8q·ÿPK ���(©Æ@jTyy��+�����com/sun/jna/Platform.classWßcWþ†ÝÍÜl&$ ¿¤E X ,²!%¨M6YHvãnH(Eâd3¡»³éîlhm-þh©ÖjkjKjµ`Iµ¥<øà£‰/¾ªß½3YLÄ<߽̽ç;çÜsνgó·ݾ `þÅL5¢ g£üLËÏ9ù9/ðU¯Eñ<¾Å xQà/I|Aà›ß’«ßøŽÀ˯\xµßÅ÷äÆkßx]à?xCàM ¼%7,pXà'—. ¼-ðSwÞ¸"0Å{ø™Ž÷¥W?ø…?Ÿ«×~)ð¡D×fåø‘Ž_éøµ†¦£éÜ`2‘êK%{5h) ͉’[ñ,×¶ U;ôoþi t'B<<4DúSé£Ç$Ð4è#©tof$'á2Â\¦¿;›R0DØ—M&{r½† 3ƒÉt�#n")h¤;¥ôêÜËfFéRoŽN§›‡ºs£=Gûú’Y®èu iX1Ð=th´?Õ“íÎ>=šîHj0ûÏXSV{ÁrO·ç¼²ãžÞ¯ay⩆ReèܤÍÉÇu¼'i¾mû°†p¢4ÎÕ–~ǵÓÕâ˜]²Æ ¶T[Ê[…a«ìH,†½ §¢aM¾Tl¯TÝö3®Õ>X°¼S¥r‘fOÛ^&ç¢~†7âT¬¼B<—îTh¨:­Ö»ŽMNeÄqÇKg+ zÙXCjž+hߟ÷•m›ÁTó̤íª9µëèàq'¬J¶êzNÑNNÛye¨«³Çñ(Rä tÝ*ç'ÔFÊõ乃ƒ ß“ì€q _B£ÑýÍuaM¬J%]òúJUw<9·'=§äîWQM[EU: 2大zê”]¦ÛÑ\©ZÎÛ}ŽŒ]óBœâR±!5Ã>c8iàËòcᤆe‡÷i0¤P|ªw©_ÇÇn঎9¿Áo5¬dðãòDñq˳âÁM¦%îŒ%oÌñäªäݢήNž½TQŸ`ž>Oïí•ëaÇ’Cdr2¯Ä*“”š¢«8ÞÕ©ãwnKG/iag÷Þ.&tZ}É!c²tÖ.ËYc0“zBV¹èuUŒ"*õþ€;ò†ÉªhèµÊg—‘ Ò¾IV^«½V‘\ÕÍÈ»P«½V¦Š•uÖ‹'JÅÉ’k»žŽ»þˆ?iØô°4È€éoQJ\§÷Óg ýr¯¡X™Ê—YLz¾T¶Ç µ–7Å—8)”“U‹Ý ­÷ìgÆÎØyᆬܹŠgSª‰g°\š´ËÞ9 [ÛþûNo_ìš·>¸F_íçªV[]¯Ä7½_ÞÁ½‹(ÿ?Í5y¥~™ß„Ua>Wµ-*dÖÇ!myÎeÁL*=”ÌŽæRÇ“ò);<u”oÙã•÷&ðwÁ‹ãAbêÈ,0²þµÛò@)Â~t°ìáKÛ‚uØ‹}|Õ?/pì'>P‡?Y‡¿@üTî&î©Ã âÞ:œ$î«Ã_$>T‡Sćëðbçëdµ©1Œ¬)5fÕøˆ|)8® ‹5 µsÔdw‰}í†R9"{‹ZÆ1~ _�Oã¸R÷ Näõj‡ê&–ͪVw{Œ’|ÉÇIƒ’—¡‡¯"zñ £5Æz"ÿšÉÏ,A9IÊWj”­e9)­úR‹kqNÃRœqrÆjœg59‘+húŸÔ Ró‹žjIʳ¤Œ/JYòP.)ö¢±å9RNÕ(± Aæ<B—Ñ<ðRiª’wºÆÛ˜Z;È%˜fƒ©ÏCÜBã%DB³á{äå?T¦±ç°ç©d¢¦ä•H5'̨ÙDú<Œ#±¿"j6ǨiÖ\®T¶ô›­;ˆ/c›¹ÂŸl0M²Ú\éO sÕßzxö&V·Þw|¿¶7@ç÷y4ñ7b ^Àkô2¯ÐEUók(ÛÄk㨫³‘×ú g½~¶æõÊÈ€t.øw kŽÄ̵¾g1óS¾g1³Õ÷,f®Zð¬Îa~_ã-{?”ßÄx«vë ´)»òÖfwÝ‚]sÁî# v]ÒîÛ´ñí¾G»ï/j·X³› ì®»ßîú%•_£¢™ìëX‹ê”7ÊË6x‡´’IŽÖb~šÊ©vÃ-l¼„hxË®âÕ˜¹Iá¦V]-œ™qAn³/RVÌüL Ù cæã¾@Dá§bæ–æVÃÜ3·ùØ7±6f¶ùX(¬/ã:`nŸGl$|€55ðè>:hîT+¹’^?‡¸z®e7‡vùJÌ@˜»®Â ž av\EÄ|b»—ÞíœÃçn0,3 äuìÀMþ‚ºÃÑñElVÏW¶±ŠÛX±1Öé¢|æãl:»Ø,:èÝ|·;ùäu¡Âëzž»Ø<.²m½Áº¾LÉwÙ’f؆>`û¹Ævsmg–ìÙjnò^̳¡Üf;¹Ã¸KaKù;ÛÉ?0¤-g ej;Ù!d”0Iï³ÿK|jdºg˜î25iÔy‚¾ ¨zòTÑTÿPK ���(©Æ@8ÈÖ“���º������com/sun/jna/Pointer$1.classm‹M Â@ …_ü[»Ľ ׎žA\)ôã´¥¦Ð™ ^Í…ðPbüYúƒ|$ßãy»X"30)!Íë¶ñ¼)*&dûºÈÍ¢tGHò«ÄÇÂŒS_ŸmhÅ–âìïs¾"@Å­7ëÊ…À0~û¶rr´»CÉ>&dÌTíà›®”zè8Ѧz' t¾�PK ���(©Æ@MTw4��&�� ���com/sun/jna/Pointer$Opaque.class¥WkhUþf³d;¶5iÒWb£F»‰µI´ºÚÔwÓ´»¦MuÓh­N6“dâff;3›l|¿ý­"‚ ‚ ´XµbADAAAAü%ž{ïl’Íî\öÏ{¾9çûι÷Üݹßþ÷ù—�zðZ;`F±fVuÈ×ã{ÚlpØà6O!‚…(¢X¬G‘ÙKQ<‰§"x:‚gÔΜTÐ84§-hÝ9Íœéθ¶aÎô)3LÃ=® KwŽÒ#iMé v ¦~¦0?©Û#ÚdNgÑVVËj¶Ál º³†C$ÃyíBl5ešºÌiŽ£Þ:”µæ»‚Ù=gjÝg-Ãtu»C8“v0¯ë¶%­ b˜SzqxZA(–Nt¶¦§Ý%`AË1n%A¶®M)¨¥'©Ïwrxš¢ê& 4&È'Ä©È?E$9ÝœqgEDRD&’ÂΔ쌰S%;%ìtÉN {°d { dè¢m¸”ddFwKlÆÖ3!€ä¬f €)™µlW ¤&$ezvJ„ YæŒ�Ò"d0giž É72`ÄDD òVXÁÂÊ­}Ÿ»¯ %–ï,×ÁEƒu2NÙ8¥ŠøÎŒ D”DH’! «ËY­‹à ‡YqŽW©R´¨ŽÞr,Ò+ AÉjtÖj$|€ã¬Pg]¡{cé²u2çæ­VÀY[Ý¿©þqž•k•œvÅÊ,RÏ*€‚ýå3èèå9Š=lè%¥-¼(ŸÌ’éÎꮑ¥â2VÁÎꃫZõ¼3qMØE[¶1‘DazZ·ÙÛçhßGè<¶çEX;M-~ÖŽ¶«x/°áEYL©¸ ®‘3B>OÛ§O çu[s Ë<QÌêy6aì/Q¢‚­¿§¨¢m¼¬â¼ª`ßÖÇ\AS™—´9eWƒrŒ-[XËçu“Nú¡Øæ•ß¼b ú6¸OÎéY·’{s9v’ß¾†ŠVÝæZ§ôb©šÙéÚˆvúÝÝ ÖlyiÖ�Û5BšÉ£ë:z¶uXA ë êÆVl -#ܵŒÈ2ê?AÃ%r …ÆíÌ9øBÁ7ÑJÏÝ„µÐ;"ÀìøŒöè\‡žLšâ‚ôT¯ :Öu +Øvu•5L^¾ÅÙÚy:ê*›Jcc³ëq½Up#!xÏ“®Âû6ç=‚ÐÞ›VyoÆAO5†N‘'ºhâZ·Hh½ãCëP­[%´Þõ¡u¸‚V·„Ö{>´z*hõJh½ïCë¶ Z·Kh}àCëH­;$´>ô¡uç–Zq©žÿ¨¦žKõüÅšz>.Õó×Ôóq©ž_®©çãR=ÿYM=—êù/jêù¸TÏ_­©çïÂÝžV¿÷7UIë«uÿF›µEŸÛ×UØŽá ¶oª°ǽlßUaëÇ}lßWaK )ÁöC¶œ`û± Û NJ°ýT…íRß+?oñ½²ÖÛiÜÏ¿W†pZ‚÷—ª¼¥ï 3–àýÕïYÎû�”àýÍo†óŽàœïï>x;9ï(ZǪÂû‡Þ6Îû0}ûÏ÷O¼ãœwHäû—ÞG9ïy<&‘ïß>¾·ç¼&=ÞùþÃy»¼ßòò¼ž*;!ABéúç) pE`_é*¢²«Èe\ó)¶ÓŒ.$7èýËõTµª×„ò*1²\Â슣\ÚýúºU¯F‡1ëe†9zûÇÈñqž”@Uøõj/¢Ðù5«þPK ���(©Æ@Ꜭeõ"�� Q�����com/sun/jna/Pointer.class¥; |“Uò3ï%ù’ô+G¡… …r—b¹DÔ‚\…Bk H9,(Úªm‚iŠà}൞뵂7*Eåд€^à¹ëê®»âºÞ·«»úßÃ=ä?óÞ—/GÓj/ï˜ëÍ{3of^þq÷^�#¾óâoðukà^ø&7¿3ð÷^pá[ÎÃÖ^8Œoz ÿà¥æ¾ÍŸ‡¸yÇ‹Âw¹ù37ïqó>7pó!7qó1£ÂŸòð3n>çæ n¾äæ+n¾ææ/ ÷ c|ëÆ¿òào,èw~Ï“ÿçÆ¿{aþƒ›ró/þ€ÿæµÿ¸ñ¿Œð?/þˆ‡=0^M p 4„ !½0^8<P¢Wœná2„Á+n/”¦ê¯[dÂä•l/LÝ<P¦Wº»ECôôB™ÈñÀÉz²—[ô6D.ƒçyiòNÑ+}Ü¢¯!|¼ÒÏ §ˆ£<°X¯íù†èÏ+¼°Xðä@·ÄŸƒÝb¯ åÁ0·nˆBð‚_ñäH·8ÆÅ^1JŒæ•1Üëãı¼<Þ-Ž3ÄÒ¨8Þ'¸Å‰^iÌ·˜hˆIY'1ædCLñÂ9b*ÏLóŠé¢”›“¸™Fº3Üb&–1ô,CÌ6D¹.Å7Ý¢‚>ÅÉÜTºEÎñÂ%¢È+æŠyžÈÇñg™¾gàþ|ßÀiüù¥üù¡3ùó#gñçÇV2§S ¬ãÏùtÊ¢š›ÖÓ} y[‹Üb±[œÊ]ÉM67n¾Bÿã^ž!j<°…ïݺ®4Ó› À×P,ñŠ¥â4¯8],ãá·Xîg?‹ð®“L.¢ëoˆnQË#YÚhc$PÀ$ÆÓb¥!V¢Á ûñÍ,q¦8‹›Ü42X“!‚t7EÈKÍžYÍ×ïlî…ùº5s/Â׫…{køNC×G¬åá:¾.çrï<¾çsïÖÂ…Ü\ÄÍņ¸Ä ‡Èté¨.e€Ë¸Yż‘ϸùœ›/¸ù’›¯¸ùš¶5q¥[\åW³^Ø I4j"ܬáf57$Ë–j ÉGÃ󸹀›_ps 7׺Åu,ûõÜ`ˆÝâ—^ø¾r7âfCÜ‚�fy0—6ú››Í®¹«ýg·ÕåKf"`9uç,¬¬DÈ­¬ 5nn Ž>3è=/ÔŒÂiyu &È „£j™Î‚BpCnHu$ÜRi Éž•gú×øG7úƒ+G+†„ì©^Œ¬ Dj†w‚=dúºùú@8¬%J}4$“Âä†L…þ Bïv+ë"„›>]½*Ž øÒçKWùÃþÚo¨“rÚðJ^iǤ2\™IYcÈOLòÒçg„ZV4lîɶԚ‰TÑÀ\2 -ޝõk¿Vêol\á¯=+U¼`ChÈô–úzæÔ¿=Ò¤aM Ê¿zu ¡[m8àJCÁæˆ?Hê[X1¢£ËPª£u¯læ´4­„øÕ^s*CµþÆEþp­É¾…årMj6D&#È‹ˆndU]Oq¦‘³™Ž‰ð]¡úúæ�ŸcaE‡"‰æs £–îmÖÑÜp.#Înñ7ÁܤK9wÅ™ÚÈÄKè6‡XävKîUþæUz“$Y‡Ñ¬ ¬[O< +¦ +p®ñ7² átâGª#º +–N//gÙ劂KiÑ©PIšÆ@ped•«Ö`biµ—ÆÇ¥z\—ëqE|\¡Çeñq™ψg MãŒZÒ0¸š5ÖÑ6ÐÌ KrŸn`«r¬˜[O{q¯ Dé-ŸPXÑÎÈ3(8“b‘N£O CÛøùkW@)Ëñ›Í‘Ðõ$µà†+x š9–Á)xÉ—›îôš@غ'Á&² AùrGÖ­¦Mšµ-arDñ=»Âæ–Fº„Ù¤‡iá°µ0<EÖFÛ©‡ïZíg<ê4[¾K;T…Ó9jig²ÊZ¾Þ‰¤= [Ît=Á>NO”êóœ!ÏÐ¥sÑLyЗkíàx¢BïSsÓÓG¥»ƒÄâDÍÀr„ŒO·ÒC3qÈSt1½4e»¾lKè¸ƒÒÆ­ôF~ktbi¢¦÷~y…K’¯–žçs;§í—°½WdAZ¦ˆÅÂ`sËêÕ¤”@ÝÌ`m¨Ž`f®­ ¬Ž4„‚L¯Îñ“ŠÉli "ÕaØ”Mk7Ó¬.¬(ÁÆïQÇX­Ü‘iJ L©Ö„:œ”:£,}F)Óåš«=eÞ:1u) tÝì£HY¡éž8k‰ŽaDG¡‡ÞyK¤¡qteC3Û³Ç_W7×rÏýÕp»ÙGh1ÊSŒ2¨²&“yeIæz«Ê;XrÖ†ZøžÍj†ÌÅÝl{2oP]âÊâ³›S-ÛѬœ¸h 4˜„‡�ªM¡°V!½‹˜nÜøÔ˰HÓ·Œ¦ª•Ï­Ö€Ú(i¶TÍ–²/°ìÐÉÛ¶èi‹s²E,Òr%["ù÷Ž ‘Áûvb¥Í¶•ñ2%C™ÞWÜTi~†šg{mN²W_×øÑ20ˆÛgŸhÆÊË´¢ ű{f4Ü,}NÖÁô-Ì,IK™qIéÑ¥C „¡…äÈ¶Šµc¥/Ì EÊèJÕ%û ±v mcRm£Îx«C-áÚ@Y«Ô´¤Å„ q«)nà º¢KA[dNHÍÔk p86q6ůÄí¶ßöæ˜Ø`¢=&ÃB„^`™ÖP7cÔM‡Çš8çf7“¹™ÊÍtnfpSf¢&®Å|«¸Š«¸Š«¸Š«øiÜk¢³8J 5" F‘£ìgÏăø¢)îwjˆö£‰ÙŒ>0#zrö`Š»Äݦ¸GÜË‚6Ä}¦Ø„Y&vcüž¬×Q¬×QVNabC†¸ßˆMìÎPÝ’ ”wKO˜¸ X¹›MìÁÝ–ýwOºg<cb#61JÔÄžŒÒ+’””ôJºñYÏijõ!sÒ6aç,=ˆÖcl1±WÚ†´'IÚO˜žmbï´½X#i/jÆÄ¶0ù‡MÌe” ”¸7é‘ÀÑS&®Áséó”’Ò¾‹}Òèi#5ñ<¼ÈľíðìÔ(åf/N`Ï,·šècÔÞɨ‰Ì©w¦°Óšâ1ñ¸‰Ç`±)¶‰í†ØaŠx¡‰ýz¢ˆeT<鞈aÔŒ)žO" H:šÆÆÀJcu„ò­$k[êø["¡•J×iµÀ_Pצˆ²`…¢U 4©¨€Sbú&o(5«ëÛQðhâ&3q.ÎCÈO* ‡š4 …Í‘ÀÚ«®Õmb—!v›bxÊO‹½äkçuþ`;½#94äÔû˜øHñÓ vûž^ÙÂjÒÖóÒýq\™žù´M 2Å~ñ 7Ï’_TPObÆUÑÐ\ E ìhÎÄgñ9C<gŠçÅ lÿ³M¼7˜â€8hâHkŠI>ñ’x™L&ÎAI^ª/ gšB>…6çìŸWS¼‚çâUS¼&~mˆß˜âuñ[rþ|&ñàÑo˜âMñ;â”2´DæÖë°Byuº*ZRP ®¹ É¿¶¡©¥©@'%¬…ß›x^™bañë>H¼…0äH¢Ü¸§Q }8¬3ÄLñG¾îo‹C&nÄ» .CleŠwÄŸL܆ÛMñ®ø³‰áS¼'Þ7ña|ĈM|3ÅG¬òÅ'&îÀ¦øT|fb+¶™âsñ…‰»q½!âKS|EÏ=ƒ¹Æ”¤ä¡Ô )¾1ñ>ô›x?_1÷bÊTYa„W u|-ªp6x:éÀû&¡¥á<Mã>ÜoŠoÄ·t!u$2uÌZSü•¼‘ø:S|£u†ýµÈ +Â~‹j’˜èB#åôtæ˜öðÅò±),T$@‘b}(<ÇßDnÖÔKý-ÍÔ?&9¸Y°*:‡«0)ñMbV¥u™"¦!cUd–Óþ®“Ð ÍÓš›V™» ÛÚg¼”‡õkYMITÀ~¡SÊ{‹Úãt gŽö:L=¤‡x˜ÞA<¨"í¤ÇÒ®,:ÔÌ›™ö”µkµµªæÃU ¼Â”$ÔÂ笱På�ŽÂjýQª?ÊõG™þPvn¦(tQš7Ž¿CIzËá4ÏɎ΋ 3WM:*—qj™xú&wq™užTšIÑ×¼F„®&]‹¬ÙÓª—O_XV6s>%@yœ†ª×L{w[øÂÂÊ´7³C¹{f Ô3]?Ò¤·Þ~Ëff¨Vvúðe®` ì²ÊOܹŽÊ6!ƒtGT«2ÈÐt², Ùœzð¡…šV‡‚ôzêœÕÅHÁº4³ÏÓ$?«ÓÀ3ìÒÀû$VÂúF‚mIÆ¥™J«°™±ÌZÎÙ[àœ„:ŠºRG²-s]Érw‰‚”.§ Ëpi3²ï•¸wIõ¦œ”RÓÜp:8íÝ­Q^aZAJÍOŒ¿š©e*.Ij´‘…™°:ªm%?àëš#&}}æ…C«é¬KÛg‡';ѪÐu|„…K§gv8ÝR«:tÛüut£ŒHÈ:ãÂÂ¥4›aŽýçü…ä?{eôž´q+‡§=+ œ%¦ÈKxj)ãIÌpñ²Á®Âz)ÒJ,V°s.’˜*#õÕ©T#17ƒwß‘Gñp¼½X—ºÝ‘PÜ]”vå.Ò‚’ÌÎÂm•›ù2eÐ>—³"¡Ùµñ Æœ7·|΂™ó—ó7s0²á0�ô§ÚÔóqà@ŸÎçéÓÍ 5 šüå)²SFÝRFÝSF=RF=SF9)£^)£Þ)£Ü”Q^ʨOʨoÊÈ—2ê—2:*i4›v|4æÓ¸?ƃ}z÷€«¦?¶‚±FPëd, ÖT}¯¥#¦0Тp¢×)ô»¼7¸/ÊDfR™îDF}7Íå‹ÌhZá5gQ+x·§!ÏHBvâu^Èõ ¹ $ýOò1rÑ€'!+A¡›Z)T�ÿÔ)åé=[”âû"p˜­™‰¤Æ4Šxb`¦ïgN!Ã&dàp›P¡EèTÂàÝæ ðܲ‘ö·md×=Yü­Ð-A׫hUƒ(ÚŠ}žM;ϢͽÔ“D¿ÈVÁTâÂðî"Ï�ƒîÛÓèÖ€K’dvÛtÝ”Ài™)#·¨H] 5bdÑfp:¶Ž<N¹uä3½òFî‡lŸ·@2QpÈ­iú®'v+ÉÖV%±a³£P8ŽÁ±ËI–bs˜n‚ øƒ/“ÿªm Nº 9öm‡ÇZ”ø¬ô™¥•œß=M#qa’†³l±²l gáx<NixoëÄIØt{5Ùâ\G®³ z¦+úÒÀµŠüxuìÝmòÝmòÝñ<Q1ïŽ%d Õ›H=§b9©S–9é,o'–Ž€åI°œÜ)Ë^é,$–›€å”NXNí”eït–;ˆåÎ#`9­–Ó;e™›Îr±Ü,K;a9£S–yé,_%–¯Ë™°,ë”eŸt–oËCGÀrV',g[,ß 1¿r‚+וëÜÓ‹äºv@ßU—ûcà+1ŠiÚqƸ·Ï}�ò|Æèé3H>Ÿ›ýÉ05€Ï¨^ïÂèá§Òö–êS’ú3ðÁç0¾„ø Ji®¾Q;˜OîÓEXN;0b VPÏM’—áɨÕQaï¯ÂÞ_í¯Rí¯ÂÞ_…½¿ª.œ@¿4•Ånˆ?Ë TuáŽJgÙXvÿYN ª 'pt:Ë£‰eþÏrU]8üt–EÄräÏrU]8þé,O –'þ,'PÕ…βŒXÎúYN ª 'PÎr±\ø³œ@•í®#†Œ;Ôvù 'À¸ÎƒÊ¾?L’Ãq9Ecg@ú‚+”@“Uh3жߡ¶hCqŽ%ÚPÛ~‡Ú¢ µD.ÊkÑ\³ˆù'ljXâ܃B÷œÁm0¤f' ŠI8¨8C7@…ϱ†•ÐÄp)Ô ÃÈp Ò«Øç*@.­ F”¸¢Ðƒ‡WÅ ÈçŠÁHŸ«Ä•oï€c„£é£˜éŒÒtŠâÍЗ ŒÞ`ÅPm0†‘î&è±i#«‘Æ)¤q›¡÷_ÈǵÂFh"Èã5 Œp¢F8A!œ JÂÄV˜Ä“ò$0™¦h„É a²B˜ª¦µÂtFÈ"ÈR0ƒfj„ a†B(S£[a!ˆC9[#”3B…F(Wå ád…PÙ UŒ°… çh„¹Œ0O#ÌUsÂ) a~+T3ÂZ‚\ 2Â"°P!,T‹©­PóvÀ\ª—(À%ú,KÕA–¸|®0ÞçPan_l:,1ô+åâWÊЯ”W0Ð }àW$ÅiLütMü4Eü´ÍÐ?‰°ésQ"±LQ$œVÂYÎ8ghœå gùf–„C»ðמ#+Z¡6Ž{Þ¨cÔ€F­S¨uz/“lôƒàahÎ;©ç]´ÁJzw]>w Vm„,ºÁmÐPbmbØ8sÔ퀳˜t£&}–"}–&=»éc}Žƒ”ôDµ¶šÚ ¨••í3Ø ˆIöÕä¬n…³÷ùŒ²o„™A³fV šÁR¦Ñ[/,àsí‚Êb°†Èº|Æ8§¦¸Öî‚uÔÖŠÛàÜJÑ|F ÎS€nbíŽÆà|;™È_°†°>çAK¨ Y(BuÒêE­pqïKjöÀ¥5­pYÎú\N+Wä\I\Õ Wïó9í| '@µg‚ij`�6Â0l‚é„ùx6eZaXލōÇ5pžwà:؃ç«x>|Àÿð"z=/¦TèRrc—ár¼œf®Àñ*܆Wã3x ~€×âwx½è†7ˆ\¼QôÃ_Šb¼YLÅ[Ä,¼MœŒ·‹9¸A¬Ä"ˆwˆ‹ðNq)Þ%îÀ»Å&¼GìÁ{Å^¼OÂÄø ø7‹¯q‹øÿÆG¤·J“=p›ì…Ûåq¸CNÂr>!ga«\‚»dî–Íø”<÷ÊsqŸ¼ ÷˻𹠟•Ûñ9ù<>/âù”‡ðEù¾,¿ÁWäßñUù¾&Ä_;Üøº#ßtLÀ·”Kß½`œ„§à|p‰br÷¦"&¨ÐÍ%‚ÐÓêmŸÕûò±š^*Cü›rà…؉`4.âU¹„V39 &â©jn;ô¢Ln4ͽF<jp õežsË»(‹_Чñ£B‚õ¨PÏzï¨w:fñ£B½e„á�I;XN='Ý3Я¹^;“®Eœà¤ ù jé;Èo†<¾Æûáú“¹Î\ãyéx{é:úSK×G…—N²—n ?µtcä¥R{é—ô§–nŠâG¼4Û^º™þÔÒ-Q|‘—æØK·ÒŸZº-ŠòÒ{éWô§–nâM}¥Ï¡ŒÔ‚ß@ ~cWd|J-øM }ý•ŸS ì86â½1¸³Ä-'xr=4 ¸ÁgPÏÁí8Ÿ›zl«×{(nø er‚;×­à&„›^Ëlþô9Úà®j5Ca‡[ubp7ÅêyÖÑëÝDå¹(lÎè,¡V±ì÷Ðù»7»µ{ÙoÞGŒ'xäo®WÉp"1óäz‰ û×óˆw;¤,îùܾ,vJ>‡öJäæª×{I‚¢0$Ùël"gÃ̯ȹ?áv1R ŠÉé¼CNçOä‚ßßƒÞø> ÇÉ>"ô å-ŸÂéø9ð X‹_ÁÅø5ÜŒßúÿA ¿ƒ—ðÿàSü;ü€ÿÄ<üàxþ›¢Îÿà|ü/ÖÓg#þˆ!x®@¼T¼V8È8q+Íín|Rxð Áÿl" ?&~/²ñGÑMä‰îb”ÈãEOq¼è%N}”ÑßNÁb Rqœ‡É¡ßU‚›\åT5ç&¨›°ëÈÀó ¯2Ò,üLV«ÄNÅ�ÖÓ{󜢌ÙßÃ4ÊTÎ)jâ&L=Ë„©Ç•&©z+ɘɄÅ"+.4E•2f™ñ*l°‚×É Ãa±Ž—Ûà´ú“¤vÓ¾†/ýIB´L§5ª Zô¨t@ks:­’.h1Ô­h:­².h­¦G®Z¥ÛÄü.h…±Ù¢5ߪ¹öß[jvÀÃ=Ü÷B– £àQñåå­ðHZ)V,K¢ßߦßߦ¡G¸Y·>–FëÌ.d]Cy´}<Öš.h­µkÍíh=–¾Çõ]ÐZG…¦5—hq×SÓØ·Á¶lO'y£"©k =m’=íl¯'™‰®2ŸGKG¥ÕédïJ"›¹´z^h‘½È"û)Iì¦Ïe9;Ûà‰9Å`^W±Ÿ<Þ‘çè n ¸§p'vËÝ0`´ÙÞrùÈ<ò–»•£ÜCÑ\žƒœüS%®=ð4ÎîÝÆñ![±øWÀXû,‰‹(ùñ0…€Wl…ñ(%ƒ!âq˜*¶C… ŒB< ‹Å.µ+ÎD“ÿ½/¡'¿²)tãï?r`&p¥šeöž—Y{v‘ßZ—«=_WZ{ö[_ ôÌÊ]â ÇÏ!ó)<ð9 U¥t±œâè&žU")&™Ïë*dó!Ǧ*Kbt5þ¢=;g‚ÝíÙ½Dì^&v¯ü$vS»kðÚöì< v×·g÷±{“Øýî'±;I±»¯oÏΛ`wS{vï»?»w»iŠÝ xc{vY v·´g÷1±û„Ø}ú“Ø•*v¿Ä›Ú³s%ØÝÖžÝ_ˆÝ7ÄîÛŸÄn¦bw3ÞÒž‘`w{{vÿ vÿ$vÿúIìf)v·âm»ÝVªrì'£~fŽœàЉ&åj`P±Ï¹ ž°8—âÕ¾gðb®C“ÑÃïï‚瞆ì]ð¼�˜&$ÄŽE¦hŸ\DÈ‘úH Ò…Ò ¤¡¤×U¯J[úJKz7Ì  ãW$}…·«ÒR”¨ŒÄ©ö±ÁÚÇ Km9ù$KBoÛéMš”™dCwÙ-Io96ç[o9–Þ\Jo•Joñû 3§õß�Aîí…4G,óõößöÅŸŒx§ýõ©.˜¥üL”tW»ôö”¸7QÉÉ4ïjOSäg 9¬Kšñgb#Þ­iâÕD“6ì´nJ‰SN0òÅ=°ˆ®„¡¯„K%Üço‚I\¨ÐÕ —'—k(%ŸÓç±®“›@òr u©Œ$ ÑÃÏD¡Äž¨)qà‚^:)7‘r[¤,"ÑïҤӺO[ò�ý%¶?‡R_£Á#Ç‚)ÇAOy,-ÇÃ�y]È 0Q3å p²<ªe ,£tÖ/O‚:9šåT¸BNƒåt¸]–Âr¦Rãjºœa0¥Ó—S$<€²€{8ž†£a¶Ná ºL¼Ê¯ìN[Ý;muï´Ž{¬xÑy屢ï$ꥢʛ-ª…¬’¿ky='”Ç<HI›£ƒzg¿¢;à`q ^Ü@1>×<cðRT.V¹²…˜±æIˆDÐQðï‡ñ1x9¯De•I[ˆkŸ©ˆ%1x5¯EÅ'*϶3Ö@S§Æà×1øMTlUY¸…˜±šŠXƒ×y¢YåèbÆš¨…èш'Çà·1x#*JTo!f¬ZˆYñ”¼ƒßEªüÞBÌX#µ»iÄÅ1ø} Þ²jòÿÍÐöCv FqCe,z&CÏPËœÉP‹:«,*æR¼³³ÚÁ`®âiû$«ŒÁˆ*¿}yðG®Í;­Ú<ߎ§ë§±%X}¬dÙ`·*‹NÌXµ0úkÞ¬EUu²< ûüΪ …º¦àdØóJ\ª¹‘k{'âJûäˆø‚ Ï(æîE­ðç´šÁ{vÍàýL5ƒÝ0Ьs. ’óàD9–ÊjË…p‹\ÈSá5Y˥蒧a¾\†Sär\$ý¸V®Àdn—|Q®Äå*üFž)Üò,Ñ[6‰Q2(Jäj±Xž-N—aq®l×Ɉ¸I¶ˆrØ$ÏQy¾xX^ Ê Å[òbñƒ¼Dzåe²§\/{ËËåXy…<A^)§È«dµ¼Z.’¿Ëä5²Q^/¯“:E™nq:T¨b¡SzIÕêñ%y”ª8É3öÆM”Ý»Ø Å½õ,ïF½ñº @½ÓU@ðÝ-ÿ&ï•EʺÐ*Ùõ+Öõ:}ŒÒç܃¢Âo•íx™kvÉËFÅt«tÇË\·K^þ(*[å;^æÚ]òòÇQá°Jx¼Ìõ»äåO¢ø‰UÆãe®á%/ÅV)—¹Ž—¼üY£•ó‡C¦dœÏ£ØÜ™I>UlUô:µÊyD‹C!z’\ƒ›`´Ï™k‡7}®\«£d¡.^¢I²Új«óöz²¦Ã¿á:£ô¹”<\ìs庑‰æRÅ>ú´Š}<Ãß1ªNR±OßV_5>…»:3ÇåÅV­Ï©íÒÅx^‰AïÕ½Å\ðsîmœ`'YŽc|ÌÎ3.a²%^Uts ÐË_I¼«j•o¤Ùë¶½~™É^„)tSo¦õŠn¥èà6Š~Ãåí0F’/’a²¼ƒTr'Ì•wÁy7œ.‚ò>X'7Áùò~X/€ëäƒ)l¦H! ÷ˇ`«Ü­òaØ-gåVxE> oÉÇàmù8üCnƒÃr;zåÌ–;±H>ÇÊ'qžŒá©²kež%waD>…—ȧñjš»AîÇò¼K>‡Éçq›|w˃ø†|ÑÜûòüH¾*@¾&<ò×¢‡üè#_CåoÅù†(•oŠjê×Èß‹ÓäÄRÿåϸö„,•È:aŒêD½¹ª7…zAÕ›F½ëT¯”z[Uo&õ^Q½YÔ;¬z•àÄ¥0^U <•"$žsá½VíÑ…Ç£ø�ÖQJS =ðAò^QcuµRx`aÔÓj(W^Ç%úö:†J”Gªˆ‹¼ˆ]…¼Ð®B^÷?ÔÓUHäj…Ê5V”mUløûù¯Ò¾Ÿ—R°øQR¾mqÙv—mUl¸Ç¿_ãüä!Üb±©jW»ù:É·à•M Ì;ÿiÜÃøH‡¤ÿ’Nú¿DúG@z«"ý(>Ö!éoÒH;<àux€ôãŠô6ÜÞ!éoÓIçé>G@Zg?;pgig‚ôÀ6øk:éÁDzÈÖ%¸'ðI‹ô*+±êoÕG9ŽŠÁß8VSõ¿£€3íWŽ1àvŒ…ÞŽc!ßq\×öõRîǘâÚŠm™u5´ ¾OßÐ4ÚÐô#ØÐ.Ez7îɬ«mðé¤çé¹G@ú)EúiÜkÿôVKgýöå�…hÔ£ÄÃÓOçr:ä:–%qÉüÓÛñ4Ç\öá~Û~õ²í_Ø´Á?Ò‰7évi¿ãU™{: c6ÏXlζê ùôýÓª/ø›pwïå~J¦^I?÷ Àå¸z:.‚£'±Ï·ÙçÛìó-ö.è£êšÌþY|Îb?ƒwMŸýâ¯Ø¿èíâ·ÁúKKñ—)~úwºý,~|_°ò-¬Ÿ|•´/ W%!;UÀ§‘Ø%sý“oWÚϽµ¶¯§ãzEA×?\î×uþ§ñ„ôæ CåÑußV¹$mÐ12Î}Š¿CýH>I:<˦͵™—ðe«^ó )ìŸÝ¿jÿº]ÿÎÀ·þMqG_õ»ˆÿÔäü·þ·¼Æ¼Lúý4W¦GBozù¾¦6÷kT"R®“'ÖÙ(ÁûÿPK ���(©Æ@bÞü ��é �����com/sun/jna/PointerType.class…UíVWÝ!“ £h€¨U1ZT HµFñ#ˆ¥Æh ÒÆÏaC‡™˜Lß ¯Ð¨ü¶®ºVµÊZ}¾FW÷ãd—dqï̽ûìsÎ>çÞùç¿?þpOÜÂ72¾•1§ ób¸­ ƒù$va!‰>|'†ï(+œïˆá®�ܦ÷e<ñPÆ24‹ ú1Ÿ@EÌKbÐÅPU²Œ• …´«bø1SÌUk2, rÍ6,G¯K,Vìµ\£iåV--wË[ÎKØW1µFc˜›ÃÜææð­w6»‹«Úc-gjÖr® `Ä'çžXΊî ñó†e8SºGF$Ä ö’.¡¯hXz©¹¶¨×çµE“+©¢]ÑÌ­nˆw1æ¬ úŠkþIM§¯½#‘A _RM‚biŽñX` ý#£Ñ&»ä‚$ t�n.®ê‡eYwÚ ï!&Z&¥€)Õº½öŽ÷êÈvÚ’™6¸`Ó~ÃÉGÒUs˜ vg­†£YŽAsÛººQÑkâ!™¦¾¬™—+½Ñ‚z=4³éV!«\ñb’pè#ASÌ­±âÕ˜ŸeýõGMÍd#4½Ãtl·sNݰ–Ã%ðVÉ÷ZPÂÑ‘íÛQUíÚ8Õ©€»Q²»i-èÚ8Ù™xÛ©2g7ë}ƽ¸+Ðw,ÃVQsÌ|Ì‹„¡÷’킦õªª×íºŠãQñ|2ñ©Š#bÆQ6-%Ÿ ä”|ÂAÅW˜UqgU4pJ…ƒÓìÌœ#¼ÓvtQÀíT_n®é–�¥ÃÒ]iV«ÂAª YÇŒÑ&×3*šx,†u3¸&òÞèŒ$²Q%짨Í4íu})ãØ!Ò˸¢¢€i^¥ÛÅ"«î5ÞA¼¾½¡Õjú¯£PÙóU»^ÒÖ¨]RÜ_­Ùàóx°ùæWêöº¸§:úïý*Ûš÷†Ï×kéëžÈòÄ…_k)DÑÍA=ó!ø»3ôaø`ûBì0?%H8Œ.þØiü$%D³qíÄ_/b¢!ù>Ê·)¢$ÎêØKÄÇ^@þ‰ç|ïÂÇür{i±vŸ +ã8ÁYpMø\3®O@q¹²L‡ˆÏ ÉèSÚCûLâ)Çxykð#zÊç¼äÇ—~äúRÊ+ô–Eò)â\ÙlóÇ]Ô±@„iŸWÂ$Nûl9—èkA gÆ=mcžÃmÆ¿!6>iüY[›)_›°.Š»z†6gšÄÛšÄÛš|Ž/|®©bŒs9»…i“¹ìhaç[ô•NdßbùOl–^#U~þòK ¤[H{¨=-ì}‰}o:¶÷oÛfq–{?v»ó9þ{ácñ€‹t‰;îO³5®y xqÃ×È㺛Ê»™à—L¥›»CÜÍó)ï>w¢ÜN´Œ ´Ü§‹~Ùyòý”/øÚ÷ íÿÆ1µpà)bÝÏB•˜ T¢¿] Þ>ÕOD דٱ_Ð{–ý }?ã(•ëcSÜÌ[Ø1¾9æÒ‹…ñ> x‚ÌñÇ»ìì{¬Ú}Äž…‡ìÍ C/i 'ê'j:ÙNu’‚M¹ ñ–l÷‡—à€ç?‘zÊsäfy(Ükz ß5!¾D ®’ßk©±W6‹¯ÑÍ2Dz-ô¼qmbì¶®ˆs“vË®²„×ÝÕЏáîÂÕ°äÆqóPK ���(©Æ@ëB›(Ò��Ÿ �����com/sun/jna/StringArray.classuU[WUþ&&†K¹‡[)$(¶´U¥…´´`ʵ‚€µÂ@Ãg& Þªk•ÑW_úª. *K¬/u-”º÷$…†‡œ9gï}¾ýí}¾sòÏ¿¿xßK¸Œq½ü}ÄCLHèÀ¤ˆ%”!$ó#¦x˜1#asÊ1_ŽÇøˆg ¼yQÄ ?–dIIJˆ uø$€'|*â©„<<“ b•gIæD¬‰ÐDlðíêkš�aY€h¨¶¾£Yª›êŽ:µõô@B·ì!Œ©o膚P»’w§Ucc`zuSKr@Ù°nèöˆ€†p±Þ6ucc(²@©âNU•Ð m*»µª™ÕÕ4Yj™¤š^PM×£ÏNéD%”Hf¶¬¬1°i¨´QÓT÷(¥h9K«„R!¥€F7&ËL%DžbÜÅ"š î®R¸BáœÇ Íg¶M9½<Ù+l ¨?0“Ñ [3Ù§Óo‚*65uM€7Ì‚Å}qé³Ç¢pÉÔì¬i,:g°3ù<ê·FÔ¸¥ùLÖLjã:w»º¨³W9XF×x¸!bSÆgˆñ‚OÿTN4ëƒ]Ã2ºqEFzetáMõCÄ–ŒQŒñ6CDFÆ6>—aÂ’a#˳»ø‚²¹±--‡‘öȞ̖ݱ›L©æS[Yy" "ob‹³n(Ý:–]_×L_â+_Ëøßòâ9÷Ê£~{i[sOÄw:‹íãY#ië£{&cÙq5žsŽW.YN†fÆÓªeñÍ«u‘)c~bù>}“,êRUð»@è•g/2©K]£¬õáóÒŠÐ mhöIÖF“»b%«(,žt ãôMÄøŒ ¥z{ž áÉeWáÖ»ÔŸeêö¶fP!}nçLù3zÛºSñZ eå;sÅízGο„}n½»8e›&ÐIwH1ðÂÃ×…þ<|œ/Ý!ú–‘?Œ ­î8‘€¨D½‡~r£4J޹Â/£f ù0ôã*àÌèGpÀ®Àæå½Jô5<­‡ð–âu!H¤¯#y‚'ðxÆÄ=òõó4}.°=D³÷Bšƒâá7'&øá£x «D}Ï~†/µ¿r„²¥Cˆ(Ñ•ÖHÞ‘¶èñb¾hÛõ7è9BùÍr©¼Š˜_ùÿ7Tz°òçPó)„Ø–Úùr¨~á^ý÷'[¢Ç©},§Ôc¨¤1‚ 4£‰ˆµ‘µ‹ŠPˆ<Ó$êqêÉ4õwV«äKá&½e·œRG¨ˆ8jÉr ~Úqïá}*­÷ð¡s³'ÈZ¯ÓßìI‡0\èïß´ÏOßYª'ø. {o·ò”j¿«´:-Ê¡fŠÚÐÿýAòI9ÔÆ|-/qéuK!ßkԢʧm­!ßü •þÇi¹ ˆX€¨ÕRêvJÞ‰Û¤Í*òÆèíž$ß ÅåÝ Bí4㢼¡Ø¢)aœ¢cÎ9ÏÊ `w©(–#ýŠZ§ŽR˜íKj_Á_Ó”8Bˆº9šC “ý­9´åОˆþx¶Òiß8ªð€”ó°H[J!gZˆQÜÑÖ=gß}èŽ"ùþ­#UüPK ���(©Æ@àºÀ..��Á�����com/sun/jna/Structure$1.classuPÁJ1}ÙnÛu­ÖÖÖ⽇փQ¯Š—‚(¬zh)xL·Á¦l³’ÝâOé© àøQâd-*‚äMfÞ›—ÉûÇË€tB”±]E+„ç°]ÅCåDi•Ÿ2”zý1ƒ?H§’¡)-¯ìb"ÍHLÊ4£4ÉXåŸÏTÆ�†Ú…ÖÒ ‘e’2»Qœ.xf5ŸkÁ‡¹±qnìÕ*×+±Ô£ÕëGsq/x"ô-¿žÌeœ+¦ÖÄòL9ŸÍïûŽZCU†F!³¹Jø¹Èf—⎡ó/CûÇd43RL‹è_|ú·<ÚÔ—† (â„4Ê{K°ç¢¼Vܸ)®}°ŽMÂ.êØZ‰ˆíjÁ+¼›%JOô¿ôÁJï¡QœMl†Tó‹gŸPK ���(©Æ@²T¥š��w��*���com/sun/jna/Structure$2$StructureSet.classUûSUþn²ÉBØò-–*…ÍÆF¤Z ¶¦¢Ñ�ÕT´àƒ%Ya™°[³›NñgýA§Öñ1£ÕZ;SqF|t,Ö)ãoÎøG©ç,K1̘™Ü=÷ÜsÏ÷Ýó»ûç_¶ `®Í¡#lÊ8Â38Ë“s!žå!ÅÃùžÃXžçá…Òx1„—iÀ8&dLʸ ãe:½ /é¦c žÉ䬥„]2‹¦–È:ÅRÎ)õa@Î*™Ž€H ÃîñŠ�”´iêÅTA³m’tÔÎÑ›¤,õÙeÓYÐ#GYF ÓpÎt©ûîˆL H)+¯ 4e SŸ(-ÍéÅ‹Ú\<áŒ•Ó SZÑà¹ç”˜q*gÉêĺo?„Þê8"ب›6MRÚe-g8Ë”OM3 Á©mã=zøÕÕ .g™Žf˜„uHÍ,jW´DA3ç“s‹zÎŽLÓ‹9þg‰2hù¼€l˜yýêä;û$ ŸhßWapÈIªdQ_²®05#•¨Ž^Ô«(ЦFvR—£H{~ÚÊZ¥bN3¸få¬'8XA;:t#« ‰_M2.*xS ¡MA ¢ˆ)8Ž>¯)x—¨ˆ(˜ÆŒŒ7¼‰˜Œ·¼Y„s O ôþi¨K*ç87g;E-礬BjeX¦ÀʲÞ¾OVW?´¹Rñì²íèKÔœZ±¨-ç¬Ë${´†$é®ôž\;~Ò`^wÜ!к«à:Y� ¸`&IBd)¤š¯·@a­5Ü$¸þnI+Ø»È;Eaòä jvư‰Âqu¦F[Uµ‡qËÿÛƒz{´ÐµÃ7�½z|¬1=ÃÜ´ÖþÕÑ¿Gh~”¬AŠòÑ3­CD7á»ëîì¡1~S|€cd+l#„GÑë®S»x>G€0Ñè=øÿ@8Ú\BZ‡önwÞFÏ®{œ=þ¸ß]ØB §À èÈú!dñÚÄ5C×qJ|‚ñ©K¦‡�U:b?ý™ü¨G‹-•ŽL·Œ[Ø#˜pç ¤{®í9ÝgU§ xißosÊ«OS4¶ Ùw²´ É¿7Ínš¶Ð2›&Äñ¸‹~¢œp`8JÆ6P÷5ŽE™–4»‰z®Hôͦ¥Ùuc¿CÊJ¤0} ¾Dø aq}â›*TµŒªz¨>¾ÝêÍôœà¤ãþ3Ý zg²;yZŠwHwo Ô!ÑðzÈn`»á’­°­làÀ º×Þ÷‹Õ¿·}n;ª­ n¢IÜB»øçÅŒÑ<#¾wyƒvâtI:‰L_º'ˆ;·ÎD™û„Ç=@¾AŠó‘ýd¹v×hǹbŒv‘q·`R~š ¶‚.>;ðù’Ù]KPý{Ùþ@þ54ˆÑ!~¢ûIqƒâ×rR™ÙÇ,H5rk.øMè1›÷Tíg4jò ?WÐâözÌå@­ÛBã}ª~Ù#ê}õ-ü†nñ°JÔ~¾Ž@¸ÙYԧݽ§ñˆ»Û‡ÃÞ­n&ë1àPK ���(©Æ@ÓEC^F�������com/sun/jna/Structure$2.classu‘ÁJÃ@†ÿMÒ¦±ÕÚZ+sh=ѣ⥠A-Ût±[Ò6‰w¾žÁ«%ÎÆ{i ;3;ÿ~³3ûýóþ à‡*hÛè80°o£k〠îM 5ŒxšŠ”ÂQ¦ò0Ë•‰Œ¡z)c™]1˜ýÁ„Á&3ÁÐ d,nóåT¨1ŸF´Ó ’G®¤ŽW›V6—„ìa²ôÓ<ö1÷K¾wvAå4^ê“QN'ÚýA°àÜxüàßM"ÌH匒\…âZjj£$œh©‹:oC o½­í1t7h:ÿÅÇs%ø¬h‹á¨¿±‡Á„¦kÑpõgÐOס±n‘wN–fŒÊñØk‘viÕ/�ö‹=c›b÷O„šd=ì`·˜E®þãž æK ©êûZÔW�­b݃M¶EžUÜ­Vx5àPK ���(©Æ@ÁY��&��)���com/sun/jna/Structure$AutoAllocated.classQ]KA=£û‘ººj¥•VBf¾a¾¯ë€#ë.ÌÎöÞê!„úý¨èîQ`w¸çž{ν3ï¯o�ºhd ¡’…ŽjvL왨1j&Âf—¡:tƒE'ŒüÎÜw:#%#WE’Ÿ3dFK_͸.Ñ/„/Ô%C­õp2fÐúÁ”3ØCáóÛh1áòÞ™x„”‡ëxcGŠ8ÿµx†|/RAÏóˆ¡ø”Áø>—}Ï CNåãõ†Í_]4®ŠGe†ì(ˆ¤Ë¯DìRøn9›;ަ… êöq`âáè´ÃOÖ _rI®­ëxqÝõ¸#Ò­8Û^«‡}…N¿BRñto�IN,ÅewHÓŠíÓ°výi…T{…ô3a)Xˤ£�ݰ‘3J°2ò„W¨N}( ”艹IX [Iï6!H|4ìÂFêPK ���(©Æ@qùˆÐ’���Ã���'���com/sun/jna/Structure$ByReference.class;õo×>nv6vNF®àüÒ¢äT·ÌœTF¾à’¢Òä’Ò¢T½¬Ä²Dv.FÅäü\ýâÒ<ý¬¼D}¸¼ŠSePjZjQj^2P7 Ç3//µÈ9'±¸8µ˜‘A�d”~Nb^º¾RVjr #ƒ(V3Ù˜ €‰L²0p�i.+;'�PK ���(©Æ@€æ×:Ž���»���#���com/sun/jna/Structure$ByValue.class;õo×>nv6vNF®àüÒ¢äT·ÌœTF¾à’¢Òä’Ò¢T½¬Ä²Dv.FÙäü\ýâÒ<ý¬¼D}¸¼ŠSeXbN)P;œÅã™——Z䜓X\œZÌÈ �2B?'1/]ß?)+5¹„‘A«YlŒ Œ L ÀÄÀ &Y8€4HŽ•�PK ���(©Æ@Dùð]G�� ��,���com/sun/jna/Structure$FFIType$FFITypes.class“YOQ†ßSº/ ¥ÈâZ±EeºQŠ .Õ&$š”pK¦Ó)LSfÈ,$Üø›Ô/üþ(ãù†ÓÒâŒñæ}Ï÷žç;=Kç×ï?”ð>†$¶Iê$;$ ’]’${$û$/IâHáU¯#xÃê÷µûêB=¹4´C¶­ç’åèÒ@—¥O†¦Ûª¹Ç0;æúCC¶Òã g8ݡʰ0N††~:Jo¾Vc2°®ƒôQ®O&Ö_ 1ÕÊmf:!¦^»ÍP27N.®ÆÞ×tÍ>`˜)‚M£Ç·nkºúÁ9ïªæ‘ì$Ó6yx,›Õ" ÚgšA“!Òjñ…’‡º®šÍ¡lYªÅ|X˜ºÜŽm:Ší˜j^#·ø}ÇeEQ-+_-•r…¢Ï³Ä:Wº}¦ÚšrÓR£–Q±=YÔ'‹É¢1YìR‘E¹4U•§ªŠÛÖ1SQ[šûÜãSm äK9‰U¤“X$É‘dI–H–IVHæIæHfIְΰñ7Å_”~FÊú©ô±;P;‚w ëÿlçsÏyÜçßGŠW)Äi/|§¹>/<#|Aø¢ð¬ð%á9áËÂï_¾êz:0îòJâθ‡6¿#ñ•¸Ç5Laè3ßø]�°Á=Š'(pŠš×Ü ø ñ/ÜÙToÑ‹bÓ‹ùóO½ø¨?ÿÌ‹øóϽø°?¿åŇüyÉ‹úó%/~ÆŸ/{ñ¾âÅ3?>€ª«5ä¹gøè!Þâ_'ÉGÿPK ���(©Æ@x ±#o��W��*���com/sun/jna/Structure$FFIType$size_t.classQËNA¬…uqA_ˆ(1ÈA¼û¸H–4B8x1Nt ìš}àã¯ôBâÁð£Œ= 1Ä㦫««k¦g>>ßÞa/ Y¬ÅÁºŠ › ÑÓ2½3†pé Ë TíÁhš–hù£žp:¼7$&Õ´û|ØåŽ)ó©xw¦«"Ç ÖëFçéž8ݰ,áT‡Üu…Kþ®ù,®=†ýfßU\ߪ ,^i{Žß÷|GgÅ©î˜LK y“Ș}òc †XÛö¾¨›òÐøwï္Ž(’*¶uä±#“Cñ?G1dçe†å‰[—*vrzЃÌ×[Ü3Çrø‹s£Õ©]^·«ÝÝ 9JF0OúW?è["+z-¨´/?&DËÚì%¨k2—lä1ÂúTE,S”ÝÉY÷)¹†)jåW„ò„<âH43(…ª‡°èÓˆSLÒ±…%²V% }PK ���(©Æ@,@XV��c��#���com/sun/jna/Structure$FFIType.classYy`”g™ÿ=“¼óÍL> g )M&$ÊÕp(¤‰ GÀ$€ µt2|$C'3qJjmÑ‚u«¶¥'¶öТ{YSš£XŽv¥‡ºº»µºj«»jW·ê¶jÕm}ž÷›dŽL ð½Çó{îç}ßgÂKï<}Àúœ¥ö`FÜ<{Zf§ |Õ …g œ‘õY7òqNfçóð,ž“Ï¿äák¸‡çñ‚|^4ð’�¾.â¾áÁ7ñ¯ÌÁ· |Û ÿæA!¾# ¾ëáå÷õ}úðïþ×ñ^Áóð#üg~ŒŸô§¢çU¯ úgnüÿåfÿíÂ/DÕ/EÁ¯dçuþG°¿–Åo\ø_axC¿uáw~/ëÿ“õ›.¼åÁðGò`Fdñ¶|~"Òþ,À¿ø«ÿ÷`=F\xGÆwùCy°ŽåxP/{…r R.rzÐHöˆ ¹Üä&ë£<™nšD¡L–).ÊwÓT{§@v¦¹hº› í"ٙᢙn*¶wfÉÎlÍqÓ\{gžìÌwQ‰¨_ ; åSê¦2[ëe.Zä¦Ëmt¹ìT¸È+c¥‹ËXå¢jk\´ÄMKm¶e.º‚`444µõõZ³)²"uA_4jE ZNp%h¼ZApF7X{b„¼ï5…ö…·úz S¶ì÷ðÕÄc` o¬áfÛÓÖ¾½~Ok[ËŽº65&Õ…Cј/Ûé Æ­®ÆI„\‘IX´ÅÆC5ûC¾šÖX$îÅ#VYB™­™E»}Á@W¨Ç ±ÔÊü1mºË Z²%¦‰Ú„bV„9k¡@ŒÓ:«<»²5;Y^]x¯%>BVs¼§ÓŠ´ù:ƒ¼S°%ì÷wú"Y'6Ñ} [BCÀ î%”NàI ˆÉ±‚ljÑî l¥�azJX›xÛ )'°÷ ¸Ý`ó/6G,6qÆsàËm5A_¨«f[ç~Ë[“²£‹AFù"_ŸÄaž0u Ç;h…ºbÝì ×H®ÄžP\žÝcQÓe1¢"‹=D)'ܹŸPûž<˜@„#äÏŒb[¸Ù °¸ZX‘D>ü™‰µ1\î½ÖÞ4d˜íòKr=BŒf¦‹ç¼h6A{|~¿–-[²„m9¨?K¹È[ûB±n+`µ®µþ`¢psÊu"üvÉ{ZÃñˆßjH!NËiµ¸nâ1<nâ6œ2q+>iÒJZÅYJµcG(™´G Ó²Ǥ+©V¸2i ­5h‰[h½Iï£÷´Á¤TgÐU&ÕSƒI F‚KV¾É÷À&àQƒ6›´…¶šÔLÛ¸ªRY·Z=áHŸIÛqˆ} šøþΤjå»Dm´ƒhFŽ ÚiÒ.ÚfЇLj§ƒv›t5}Ø kLÚCÛL| 7q`èZÂÜ‹“|´Ã N“ÓÎ&[x(¡­šcV½1¾oŸ„gu™ÔM“ö `:‹¬f‘Õ,²ºÎ vúü×™tMê@a*`L§I! ›Ô+ˆYeûZ,Öh…ü–-e¼å’êO›ô‘23UJj™ÄVG‰Ùì ê=fŒ™§&]ÏYe¡' “qn ­._pC¤+.—mýA¿Õã2â›, b£¡.;JìÓŽP4ÞÛæã±·dÌÞ’}R %RÈ%&¤>ùÜ`ÒGéFÉøÇØÏäå·Ëò]×è‹v³&Ý„Ci©¯D¬fžíCI0ÐñEúJBáX‰œš�?7XœÄ›¥dŠ9ÁZi4`Ð!“>.µþ ‰ádmQP½3`ê-&¦&IÖ‚¦6þ˜A|Ânò§„œŸ$_Žó;aWïmBÿt†ð-áP—AŸ1é³B½]¨S“Ô&NH—1è“îÀÑ í­ÝW“îâsBw à©Û{MºO€Ó’Àºn_Ä牬ûM:&Ðþ S6öŸkÆÔ »NdøfçÙ¤Ʊì²I=hÒC>m ‡ƒ–+fÑÅüÑÆƒPö^:~…WéÒ+ä.=Ñß!W¨Ùèê²¢£O¶‹_žÄtÕ|iÏwE¶'ЩKšíZ^1®!š”¶¡ornˆœ¤b–™i,ua>f~9WÌ9=Û>›Ht|ôÒ˜S:…‚ñ»Üíuû¢Í|̵ ü0‡ôbzyV§òG#Ó–høôsû^"”ý¹‘ÔÁ}‰xS³Án+ܬiK¢a(ÌÖ4ÙÖÔ…{zÃ!¾|ì–uZšÝ£­GnkSG=å›ôƒy}„£E˜S¾){÷ÑÔ$°±ÒñŒò‰|ÈÏ´Í. ­;ã!³/%N?Ó“ï>¡²ü JZ“P”ÂI±™Ý aÙ%¥gépæ^ÀÝFHïØQÏÖ4f­[˜ˆœýB&ãôÅö…#=÷Æ ­{6îhh¨oá~ž:ä£ËD^¿¸e£o‰ÓÞ'\–ªß¾lÖd«ƒü@tC4Ê¿¤[oˆ„{¤ÐLJUβE³…ela[X6ú†óIés’W_¼Ï¼Xf—>vn8�!ëú&ýËHx沈—Ú½_~Áé}üñÈÔÞ £yËÚݲ¥œÎ¤¥+/eéD=²+=uÙ‚z‘î${| γ}8EYˆÝY|‰IíRÖdÀ³¤2ÎØ;™7gBSÆ6ª_žžíÛššÛê[öØWÔè�ýhMNrH×Á˜ü†–ƒ“<B-³©)]nC’Ò–‹´ÂL®(åþ²Û“$ß á+ÊäE¥˜'}K’m¥°3ÒÆLMéív&É·J_´™|c°ÿt£“d\Õ¿QЮºÆ ™ñ½R8Ænø%²š–R¼£í·ˆ™RSh)n)EàÒ¬°AÅãOÞØ•8.Ö£U•…i×(-%¤cÝÔ¸(&((A)–pÂ#¿x6 7ã繟àÙ-8ÌsÂ|R·&FþÕÅ£Á(þuÁëÏðfËŽx‡àðæOz 9•¹ÃÈ­<ÕE•ç NÁÙ¬žÑžSY5WÕ)¸‡A­í¹Ôºõ$š*áAW÷µ Úš³ÞÞ0 #ò¡ÚÜâÜL&ôcžÌ¦Î!¿V-ž{8‡*‹Õ ¦¶ž|÷ÇÞÅC(øŠvá³ü])€@®ú2<ê)LQC(RاF°R}Wª3hTg±IÃ^u½êYܨžÃÍêk8¬.àv–Ñæa&îÀQä`%ÿ» w³ü^˜zO¡—áÜ‹\l ÜÇa”¿bÁý8èY?Žs ±.Á+¡;a‡/0Ú[SB7Œiëçê@ÔæÒâALçHÕªœ•ÎBçÜG‘_œ[è,V­‡tòÝßz‹sÓ|örJ¡^Høü"ûüf¨¯cú¼ê›XÊã*õ-¬SßÖþ5²ö2Lc鮳{=Àƒl¥Ìø?ËŸ‚|Nû7—k_r±�sñ0—„b:ÿ‚OxågYK½§QØ>€¢Ê3ÝG†0ã)ÌôžÂLweNå™A{1+iz‹ƒú Ôw9EßÃBõ}mb‘-j̰R­šï[<†Ï'îfŒ˜ºÀû<ŒaÌ~Â{sú¹~¼ç0‡Ã÷„—u͕ɘº"¶êeŽÔàR¯p¤~ˆ™êGZ¥i Ã8àˆ>†ÇmEŽsl$狎q–æm]|E‹+G0ßæª ðT „°e� Ú›Ÿå/“:tõž‡Ñ¼âÜs0ªž{BÖ¥ý˜,ëÒA”ñÖ�.ëÇü,zS .Fyû“XtNÞá³Pq\h^¡UÚ4¯¦y…Ö"ÑÅn`qvñD ¬xµ ¯Òð*~5 r1¾fó�–h© Z¢AK&–y¥íÑeC¸¢V ½XÙd9‚¶ËEÞ [Þr-o¹-O„­¬UÅŠkbUûVâJa«å¬0'ƒÖôc¡ÖP9„µZ¾7Sþi¬k?õÌþ¾‚÷bsmDÝ®:[ë¬z®Øy–Ëb!Öò¯bðyÛ¦ÇíxXàM=¾E…2Ri:m§¸нšn×ÇM˜ÏõñS(õ*Üê5LU?C¡ú9æ¨_piþUêWX¡^ç“ôkÔ«ß`»z·«ßá„ú=Qoâõ.¨?à5õG¼¡þ„·ÔŸÉ¡þB“Õ_©H½CËÔ»´ÚÉÚÚåÌ¡N§¢ÓIœ.:êtÝ>S‘‡“ø"rÈé|AÑj¾‘¾„¿çYçèªh/þë5Wª3Q¹2ûG>»ü{‡îÆ?áŸÙ7¾ÌÕìÐǦVßFüx+ÓΆSê_ÅSŽŸOØyö-q@¯\x§gãU¾˜Ï±ù4ê9C ObÁ�>p“N£±½ i›Îcóó‰õYd+ïÚiÖËf© mv5ë jNlÆS–-‚oµñ-ß’hÆŽ”åNÁï²ñ;5~g:àCÃhOYv~·ïÐøŽtÀÕÃøpÊòÁï±ñ×hü5é€k‡áKYv Þoã;5¾3°W=,O¥Å„aìÛ¬9º¼)ìݰٻ5{w*yÿ0®KYÝc£ƒL„¶! ]Ð+€Ø€^ èMDµ ˆ¤bìHÊ2.ø6>®ññ1@òuˆ`—ä ü¨ÝŽ|uf©;Q¢Žò£v?äw£C݃€º·©ûЯîÇÓê^äîãuuo«4K=@êAÚ¤¢ÝêaêVЭêQºO=FCêóô²ú½­wÌR'ꋎMêK£OíÆxJ÷= êïò¸›tR×ó1èã[‡òx#äÿJ—áNàoPK ���(©Æ@ îza��^��&���com/sun/jna/Structure$LayoutInfo.class…SmOA~¶Wzíyyc9”+â;ˆ$#I•T|ù¶\ºxì‘{!Ö凣‰?Àeœ]Ê‹Jã]nfwgæ™gfç~þúö@+.àN‰Äœ¾^Uµ˜·q×A¾ƒÜÓ'÷µx`ã‘Ç ùD~ l¡ÄC¹£ö„J ÛR„„¡·¶Ë¸Ÿ¥2ô_ñýÅc·zkŸÂœ”a¤D{~’)Wq¿~b¡ÇÀ­Ç í×w²&“”ìÅK¾j&({Ú”Ét•aøÈ4΂4‹…f±ÑRiS¤2 ÷%©dºÌ0Véâ?³I•®F ¡ ’J¼Îö¶D\?ÊØ_‹nvtóš¯ñV”¥kj;bp×”ñjÈ“DmêüdÓ§!‹6ž0€a¡±.ó†qîSU‹y†Ñ®~Dq#Êâ@¼švùÄ6§›ìbW]ŒbL¯]\DÙE/ÊÔÚ³— >ŠÆKž4éÂl,¹xŠe}¸ä¢eÏ&þW+Ý«A ¹Úñ×·vE@c4Ü…6ƒUÑž’XÂbd¤ôÔ%-4ãHÑp6ˆå¯ü5ˆ3ÿ æà¹É0AãîÒOR¤Ê‡~z;šJ4º¿³Àe£1dô0FŒ•H·xÅœå襮Òþí8,Ú·¼Ù6rÞ!,/×FÞË·Ñã}Gáý!ì¯(¶Qò¬6œ/à:É!ô�V%ë ÊÖ&¬·˜´Þ]]¦!1‰i“rœÖ9“î9i‹tÁ›=;E+èBÓÑG'ÑT }½šGÇ…Ìy7L‘9jÔCL‘¾iìÅßPK ���(©Æ@­õç`¾��Ø��'���com/sun/jna/Structure$MemberOrder.class…QËnÓ@=“8öÄu›Ô%< 44@Âg‹¨Ø*E2taT)Îxh'rÆÈ$þŠÂ >€BÜ1VUªJl|îëœ{®ç×ïï?L1颋{.\ì:q¬sô8ú›¾ƒ2سáóˆaëM¸Œ?ÆAëã *s¥Ÿ2tÞ«¼(ØŒF )2P°RI’J†µRWi©VqI™•ÆfÔÞSZ•ÏÚãÉU÷³„š½PiùªZ-dþ:^²f"Nâ\™¼)Zå‰*Hø¥4“‡y"so¦µÌ÷I¾ÔÜ E¶ ŠJK«•(«\ŽÎqȹ !‹b4NãÉ¥çu£Oº<‘¥ |O¤s7ʪ\Èe,mœmxl<¬Áó°…+ ý‹’Æ ÃÿÚû‡y¸XJAÿmp) ;ôˆ.½§›f3E ëØ¨±×`¿ÁÍýÛh›”( açÑ7´N)háj-kŠoq¾Þß\Ç-BŽmܦ)CÞ®;€õìs½â<÷NÝã¸{¶ì ¬º6ä?О·}+š[~'šw|;šÛ¾ÍŸG_ÁN/ˆ½£…;µ¹!Ý ºœ ÷qîPK ���(©Æ@ð3FÍž����'���com/sun/jna/Structure$StructField.class…TkOA=Óvûb)Py¨€ n R|?ŠˆT«h…D‰?l—),ÙÎ’Ý)Š¿J)‰&þ�”ñÎnƒ- 6éœ3÷ÜÇÞ{»ýõûûO�sxBÅ]Åî©ã~ÒÐBËCÅŠIÌ+|”ÄB•}QÝŸ$±¤°”À³Ê 1aÖ9C¶²cî™Ç[…5éÙb«HšÜß%­¯M+9¦ï“¤Õlîl2 ·i¯9Ü’…²’T¸o¦p¶Ìwk5ŸK†´ío¸Ž)mG)ïÃnn® gŸ¡Û#ZrÅ÷$÷&*–[/ø QØf¡ì¹õ ÝãG.T%óѳ%o ëZwO†$,WHþI÷í( È7.·mjŽa¨Ã•fÔ°dÃãä’ZÛr›KÛ"÷y[ØraÄø‡nƒ&Sr7©ÿžŠ-øJ£^åÞºYu‚5¸–él˜ž­î-cL=CW˜£^_‚{Á:8‰“§W›j‹¡'MJ7\.C¿‘;måé5·áY¼ì's”gV¹ê¸‚«: <×1Œ£a<že©Q«qOÇ9ôëxÚ>[Tä%‘÷ Ñㆎ *Û+²åtÜÀÍ*´ïÿ6ÁÐû·Þju‡~o”ÒPC›»»\ÇŒq²³“͆I-Ëg¨É‚g%8µŒÓû—¥×· LÍ…S#p礑8Ú ¸à¥ŽQ…¸à$¦³j)t»õ‰Ð×@Žîybeº+K~ú�,ˆH>r€¨:b_ïi:3ˆZ)­†Œ¶…²é”âpþpT-ZP+'§(¥@{{ˆxþM$³):ˆÇšHg»BU\yw™lñ&z¿•Ž«Tšh+Yl•ŒàVpÞF0MZžbøPK ���(©Æ@ŽÓ`м6��¦q�����com/sun/jna/Structure.class¥} xTÕð¹Ë̼L^ I`d “„€a'lÁ$ì ¢Â$™À`ÈÄÌ„EëŠÔ½jëŠ .±®È‚EQhkÕV­µZkÝê‚k­[­òŸsß›7o&ôÿÿ~òî~î¹çžõÞ;é~|ôq�-ëÜà[Ü|+?ÇÅÏuñ_¸Aòóès¾‹_@Òç"ú\ìfuü¼ÌmsñKÜf¶Óç—ô¹”z]FŸËés}®¤†«ès5 ÿMu›_˯£º_»øo¨p½›ßÀo¤š›ÜÇo¦‘;Üàâ;)w ån¥Ö].~›‹ß®ñ;Ü0œßI»és—‹ßí†"~žÆï¡ô^ú´Ðç>jþ-}î§ÏêAõ›?Ìqñ=TØëâ­Ô¼/&ñýôisñvªé Og*”ò”{”r]©ü1~0•?Ο Ï!’Úž¢q‡©ÃÓôyFãGR`?JŸß¹áþ´‹ÿÞÅÿ ñg©êŽ0|^ã/¸a±±š?Q¿?»ø‹”¾äæ/ó¿¸ø+ÿ«Æ_ÕøßÜp¿Sã¯iüuZÈß]ü ¢êßiè?èó&Aþ'}ÞJåoówhqïjü=jûÑþ}ªù@㦠Å?¢Â1Lé'ÿ”ÒÏ4þ9õü‚Æü›¦ù’àý‡à}åâ_Sõ7Tó-徣ϩø=}þçâ?¸a;ÿ1•€…‚`šà” MH—pθ1W '~x'Î!\šÐ4‘¢ ·&R5¡Sï4—HwÃÍđᙢWªÈÙøáP®·Kä¸D¤•èë†Û‘¢Ÿð ò¢?’NäÒ'z·¸|j@ãR/ÕD ib°&†¸ÅP1LÃ5Q@µ#¨Ýç…)ÐÆo¢ÏÕøiÔVDŸb*ޤ^£(7š>c¨a,uÞæÆâ8*Ž' ’Ü0O”V‘üb’&N¦êRBg²&¦hb*e§ibºKÌÐÄL7¼€|!f‰2üðw\b¶^FâŠ9b.}æib>MQNŸ´Û(wŠ&*4Q©‰*±Ð-‰ÅT¿DK5±LË ëšX©‰S ü*—8Í !¥±�(‘b!²šº®‰3(=Sk(]« ?¥Õš¨¡´VJë4±ŽÒõšRºAg¥àÎÖÓÌ5Ñ ‰& ™³QÜEu Ó'BÅfš°¯&6ib³[l[iØ9ô9W¨–nçÑç|MZjâ"—¸XÛ4q‰&¶kâ—š¸Ô-.—kâ M\©‰«Ülˆ¸Z¿r‰kÜì$q-}®s‰_Sýo\âz7&np‰ݬ@ÜDÀo¦ÏMìÔéq«&viâ67Ë·Ó¤wü;]ìF*âwÑçnúÜCüy¯K´¸Y)Ò‘•Šûèó["Áý´ÊÜ,(¤®Ñçaú<BŸ=Ô°—–O:Hìs‰ýšhÓD»&:4ÑIu¤{Ä£ôé¢Ïcï &§AO¸ÙFqåD<©‰§h‡5ñ´&žÑÄêwT¿£ô÷Då?¤àÞ>ët³µü<—xÎͪ)}ÞÍj)}AÚÌæHhf}}¨Æ Ô2ÐËMeõþp8v‰?1pÍ[¾lkc€AêÒHSsMdn0P=Ýþ­¡æHyC]È%þŒ­•Õ¦…Mµ&,ÍÚº$Ph 4ÔàH׬­+üõ͘K_2gÅœ%K笙[>§böRì4½—ÌY¼¼|Éœ¥Fíš…KfÏYÂÀ /ZT†£ƒá¥‹f.)S53—TÖåóªÖÌž3wæòŠe¤ëÊB ሿ!¢fhð�‘4:V-¬šC5̪™Wµ¼Œj¸US¹t…ª ²*gžªz¬QM•sªpŽô²™eË+f.›³fiùisÄqü®·¨À £bƒ“Ts$X?ªÒß8™sc`c¨i+ƒœŠšÐÆQáæ†Qü£…‚ ‘@váà9H–}p]ƒA挰"óLªÙhˆà¦„c„ãJü‘à¦�îF°a–݇ó5áûÅM´ÌjÁ¹4êg`Êà°:‚gîW¦ õŠ`8BÝýÈKþZB³+›‚DÏáojòãŠú­Ž›Éàæ¦�Žt4á(D¬¯´Þß°nÔ²õTY|VOË®n#Œá5ÄhC΄3á ±à ‰ã§^6PŠ;HÊÒ­ ‘õH°†Aî 1èß½µÌ___í¯9 ·Æh$øC‚¡!³šëêhJO÷Aæ¾1н­JmŠ"6R¬ (!=ÄØ­¤PWFÛNHŽ8ê¾"D�ú&V—#ºëáœnø¬5E,tbõeëýMþµÈn“ÌÚJ Ðm’Y¡P}Àßd’¹õ!$ 1f‡š«ë“-cE(ˆÄsN 6#Óˆ‚+YÊBµ$Á†@U31Å2¿ž¥Øi…¿)He³RFÖïõĹ=‰MæÜ¤‚¤"K}X#.ys9µé¡í„óÖ™ÒÝ«`D75’¶.YfòþاG1O ÇwN© ™ Ó±mfLÕÈ…¯î·$ÖÆ¯TJŒ$®9¨45Ûˆÿ°÷P›d–7Ô¶,lŽ,¬›jn¨ ÏÙRhŒC ¤ Cuu8;îc !Œ;a3:²à4M¸ý–¹I÷›=¢3"7 žwÔ ³á²ÜHKû&$¦`ãɹ4@šÍEªHÑC6)ýƂȋ¶nå8Ö ÑøTCKš¸çØVkˆídBJ6ø7*†ìÖŠÀë NÎCl•ÈKؘæuX²™lU «7j”šF:˜c&üŒi’ɱUÚ7-5l7ùZØšjÒÏ™ªûL´ÍŽM†+• ‘“ÿßà*þIÙª âv5¡ ²se}}`¿~fMM Ç’i¡M¦¦`m`n°Á_bÕÜX‹üfM§rWt3A=¨˜äz£G}ÄÃcHk)žgˆºË_[Û„˜â‚ïÒF¼.b¬8þ¤8xs›B duAÈzMsb1}/'Bm®G.vl6L9#kºUÁÜÖ¤ü¤ž¤>ùîûY<HÒn÷böqêlmä.l’ÃØsH’­mZ×L®’}sSjbį-CÝI•¶Þžkó‡Pלfè]{mVœ21ý¤´p|§>«“1;I=Ò?Œ†8êÚ .H„ŸXVTgì–ǯ)PW{0*ªK¤‘ˆ‡¢KÀ|6êÙîø§*#íÓõ €Ššú0Mj"‰pÔÕ#Tè1G²Ò©YOuC›¨‰Æº¦¹ž$L9½¤öI¡œ¦RT®é ø'nÐæ6µárô‘p^ÜM£Áý<R“¨˜í>œ;Ò•ŒØ¤/]-Ùæ¢» n6-—µÐZc¥èj7…£ª3“œ› :üç¢DÏH¨b0ÿÿR )©nïma£ðâD¶_Q¿\¥Fb,& yfibB•}l•Ó(Å$Êá§‘ÅsêÆP-ŠÊ~ï‚É$#Çb×øzGm ºy¹L¡¦ä_íŽEÒ¢6QîÍiÉ‹`í 2É‹Avµj†¤‹æ,Y³dáJl­nFuÉWÏ™©‹™­mF¾ˆ`‰"±ÚæQw…W`xëlD "4­ÆêåBgˈ¡ÆôDè9ºêÜ7Ó›g7›¢Û›‹ÛÛ3Œ”j8 (EÂ:¢ùq„æ'i3 QEE®Š EVCå¹ÞpÒeJúÚäÿœ`㨙µõ¦qc'+%P³^ɉ‰fFõS¬ªôç¹1Éý=Þ€RjúË<åô¢‡‹á")'IÞ.¹6F­áÒˆX): yõuqÅÉ(Ò:"T½¡76—«Ó Ò›¾dìÚÓNÅ92ÆGÐO&ÎnëÜVö3®¸¬l}€¢ÜÜžYmyAú„)2GÎ0,Ÿi„; û9‚Nrî4Bº¡LâÀFo¿JÕPŠÌ¥¾J¾e4’үܸ!cÆÝ-–‹-J9„Ú”šz3’Ô[EŠ6´¦�ªñ° <üMÄf´hsƒ?¦[@FN:û{Ö%^ÔÅK]DïOáÉ`@¬KUHuš¨3:55…štö0{D/‹¿àV%ajï Ï£ô9 ³Ëª^1yšÂ†&JgW±í:û Ã×ÓçIö”Ξ`m:{†�<GÅ+©ËÕìWºx…Ÿ¢‹¿òS\âUÓÙ/Ù¥hÿ’KZÜ© .^¯»Äßuñ—:»œmAÌÄ?hžºxSüSçý¸‡roéìv#Bý!!núOûq ,¤¼¬E«ïml mB°Ök®y«`¢Öw´ìg þè‡%²_ô`ÇI§oÞÍሷ:à]‡.6n€7²Þßà='Ð*õêâmñ}ÞÕùX>Ng‡ÙÓÄж<¯³Ä{:ÛÁnv‰éâ}ñ N\°©³›ØÍq•jçv0ä§y¥.>â•èË'ß[ø¡³—Øc:ûû³.މuÖ…Eñ‰øÔ%>ÓÅçâ —ø·.¾Äíÿ!,z€hS :{ƒýS_1'¾FG¤*ä 7׬÷*ÿ×Ï~»Íuž†Ìš$DĶZboôJ­V¯gмÃuö7öØáÞ`ƒ"í7(Ô4É;ì}]|+¾‹“¯äa›.þËžw‰ïuñ?ñ: ±þËÂÍèLj6RünW’ ¦-[ {WTzkC°·!ñšÝ½}ÂÞÍÁ­£Aç°·ÀÄmæpÕŒ¨£Õ nf$ØØñFB^åR†V\j¨ßêµÆ _ÞU>XÝÜ@ÇTqc¢ÔáÄ 9I·Nçk¹Ÿ¤9ïSv‹Îþ;t‰u>åšíb·GâÀ‘8p¤màY¼^Ç%èì6êÖÛÞ-z ªKF*ävê vtdC04ÒÝAÕÙöq–®º“šúÙ›ì>´.9iŒ7‘Ó¤R—éÔ¥Kj ò{ây¬ê|%²ºL!¤¾e߇¼«K7ù Î{â— lAÉ{X¦º¤®Ë4™®³Ý„g¦Z1ËHC#èì®nK³Ncûõè ]fÊ^¨—cºÄØÆAè)ònö‡½Æ±ª)Ì3ºŒH÷Q‚—‚}¬+,òn^Di Æ1$1ÍãM]Ç~B¥I”u§àJçîtÉ,]fËÞ.™£Ë>²/Jµìg.Ù®Ñ7ë!¾Ó¥‡sÉþºÌ•yºÌ—t9Pü K/~ØÙH{y’DÀëR±]˜öCpÉ`[wኄ¢4 {ÕRЮ ÖDû‡(XœìÅhÌоÍá@l©#ãj’”Ÿú`M0‚òƒžw¬jé(ɰR¤gªÚßM†ÚP“KÕå0‰òÇ‹°I7›šÂ¤C¸›§êì¶ÎOEŠ:ïÃûê|�¨ób2}WЧ «, õ'‘|XŽÐyÏÔ¥ó°,Dæ%Ú1Ôˆc{T= ê"¦ntYŒ†LŽ$âöñXT^cÚ¥ÏpË~¡ÕRRYçGƒ!GñSÔWÂÄO1êö&£ºßk„Ïö>Š=Ã!2„Ege&Cu*0¸°·çÃYk8åÆ`ùS»iï¥Ü+›ö*®®6Šy,ü­uÉѺ#Çö´3KŒc0q—§Ëñr‚.KäDlzÜŒ…¼ÕÁZLjhnTéj]Ʊ±ãG¯Î 8î]oÐù(N.eyÃ&DÅNƒØJñ§™%:^!ú‹áE^]N"É9Y—¥¼Hç>^D>¥5wЋªþ³B› j§àØØ÷4JÔÞÆæjdö:»›tÓIIµzüm¯Ìß0<Û k¿ŒÝ·´µr²œB×eÉÏÔæ”SIißC³§Ç4&Ý0éì^ªí«5/˜tÖ3†‚¥û%ݧôk¬Úº^ÒÙo& Û%ÝŸ0y¹¤³&PwK:{0A±WKºœF~ËHö<ºË m›· 8ÈTÆÖÁ‡.§ãöÉ´úYž‹†œI"è¬5o¬2h(Ê_c8Ÿ.9K—er6¹‡ãés2:#u9‡½ªË¹r®CRi~u¥ óŽÌÊ ÐNtãQ[y±Åå­Fb„GP1•#GŽ<ǬvÉùº,— 0þ#Gd†.O¡iØT“˜†ø©Ý’²’š«¢ñƒé‘[&ulþ›±er‘\Jí‹\"—êr™\®Ë|N‡ø¤šþ7‘C—+å©X=šP>½cœ™½Œª‘¯•«t>‡ÏÕùR/ºüš*äiè´$='Ðåjy:¹ ˆþìZ]ƒy¦\ƒ~?SçK1Àá§‘§:~ÄûT·V|@Þÿ#ñ>dÒˆIoHLÐj&é×›äÝ¥®é&js ¶ˆ u0bJètÚ©êÍS–†¥ƒŸàºæPs8°Tûk½¤ÆÉ76Oû½þH¢–öªƒ(ÔPµ¢ÕÄ–×áΠÜÎ(ÛÍ9Z%²ãk¥Ÿ¬R5²V(<’¢^]ÖÐ>ŠÆÆ¬lÄå5QÎF>­ÑY3Ù-áoÚ¨³MdùkqN HÔ¶c+þ³Ìh”®…“Ò1=¶ŒóNŒ#„ýBëV°‡È&î±€§Í3.ªú*uE—B¶¦ÌßL‡Eö#Šeë›B›É9‰;¥ˆÕ7m&¼^˜½V5벺û‹èâýÔYOÜÕmŽuðª.H­cÖÞI ”®|ìø Þ¡QZbx%²Nhsø$êâ×Fu »î=`y ôØæ$¼j™ä¬ÇO6Î&{n•ˆÌŠ(Ÿ%¾A¦\ˆ$Ç.CIØüA:(XÄßt'0)ɉ]²3¼$ê¶2L4²ß”…êë ¿‚Nd“Õ#2Aó™î„ìƒm×ËYÝk‘[ÑPU¡%¦{e•8›PqlJ<†;ÚE?Ñ1æBÝnÿydZaœˆVÆn[ûw)¢­êù–y¿JW¨óþÄÍ%]‘„g†ÃÈþ$yÔ#¹ ¬Ôù3—®™µ|îÜ9K–ª©Ì“u’UóÚsRÁ‚Ÿsv™ŒŠ®Ã%J¼AŒCš:ÐlUÉ`N2JžBRÜÁðŠ ¶:™Äy¿ 1\`0®'ÁîñúŽö-¿¿/Jx-Š.¡ì§–à6&_@Ÿ$šÁ8› [4<nƒºó¹Û+¢ö"ý†I…+Ô-äиk×ø]µ_w¦ÇׄÚÚ™õõêIOR'>ˉWY1QçädXr’¥õðf29Ý®DË• ŽY’@mô:0¯`ĉnaµ`˜Â*zö†ÙEÊQZÔ±vÒÛ×ôf¨¼çåÑqÿÒf4B5†-³lÅb£qC²¨)„}"[úyŠu§¦m2Ÿgˆe{pÞ.ó.‡Vp‚Œ”% ­7¥t#oÕÄ©Ápy406nœÊBC h[“8I/õÇîpJ~Ê®÷t•Ü'¹ñGGëì.‘cÒHòš¡]ª%S¬ã Zò‡$ôÖªÉé_ˆ *ý‘õô,ÿE]Ã×ù›ë£BWÐ3=&«-YÔÜ4t¤µ(Ø ùVúkŒÇ¾Á-q¡‹õÒŠ=;LÆ‹dp}ãþ¡$Ü\6Àr’9 ““™ö„žQTÖÑz“Þˆ¢;– Íl‰³¥ÔcfCŒX@ã$‘ õDǦ,6j)]Õbü«™…¡\ZÓO¹±4˜Ùh˜m çÌE9 ” ûì$·{=¨»‚Õ³VÏ"â:·Rô€�+è•fc€8ÌmÞÏ¥ë¹Ò;É0æ]F!&H9ñ*ÑZX:{…Ú ŠkÊÔ&úÁ†fd¨”Š…UóÔ#nyX�A:áÄ\:ÕÄ´¨´ÙL7©ÔÃ6³-ÀØVõxü,Ÿk+ÿËçÙÊçcù[ùB„s–òØÅ¶úmX Û®ò¿d—ªô2ÕÓ}›J¯0Ó+U¿,ºþSå«Ù¯TŠ1²J¯c¿Véo¾œ.Uz»Q­ï&v³Jw¨T£Hñ¸•ÌciW\鶸Òíq¥;âJwÆ•vÇ•îŠ+ÝWº'®to\©%®t_\é·q¥ûãJÄ•Œ+=d+U‚¤ËX,ïÁÒDàÀ0Mñ±Càê�m¯"}ÝØð[Ðà~ÖŠ9ÝèÈö±ý˜6HHÕ7U…’æ!LV`ú]M0”kǽd `‡ p:ÕËå+àNÖ NØgæ²€¹X'2’ì€ lbG¿qÐ|…y, jíà‚Íkô³ i&4ÊCr÷QîýØ[bZêëÀ•ó6Ð}]¶ªÒÛ Ã'Û “>½|yíå+n‡ìÂ#د°z· .¼rb˜Œ„Tü@x;!n‡Þp7xà�Â`è‚x Åõ Œ‡Cp2<¥°õ³[Ø–ZØ–šØRŽˆ‹«¢»SïQ؇z9|û¡Ï §ª|Ö¶ÕŽ(hv=ždpßÄÁJ2XÒí¼9øjĉ(<ªð(ôöµC¿vðT­¨úWø Û ¯AÇýû$äí@2áØÜ™†!q�þ†  §y2áïoBø' „·`¼¤|×ÚLÙõò>‘b”E¨QÇ=Éž2Q{ØDmFîNš€Zþ¬Ì섈sJ 8S|¹m0ðĸ–@~!®#Ÿ ®Ÿ"®Ÿá¶~ޏ~CàK\Ï×PßÀhøJá;äûïm¸÷·pŸaá>Cm-á~˜=mâ>Í?wä­òåv€7qc~´ ‹Û‚åVÚ–`=“TòÚá¤Ya.p2­É;bJÞ3–ä=ŠÓÎEÉÛÞƒ>H.ßdó¡`6è§óÛQB° å±ßê‡ÄÚéU]0|UVAŒxb¯ZÊD$Ô0'b,ÒMC_ÈeÌúÃX–™¹�Lfa;É’˜ÉØû(ûÚì¹ösMì)÷{SÒÿ`)Éñ¦’tÒ,‘&ÃׇÛ8ßiBåôˆÆ„PgnÑ b—£¨p—|-c’#Í瓹퀠Ǡ÷B…” l,ôeãà$6ÁFýAþƒØÑàõŸK޵/ëɈõ”¤X?gaÓëzq*Ë�3É9£½C)*e÷š�—ä* @y”» Ð>wAªÏ⬑í0 çÝchãs‘?Ꭰ¡¢â†„]Æ«äÆ‘MéVŽË;ÒY%2F³%ÈK¡Œ-‡ ¶³U6œ—X8/1%‚ÑCçEÈ*D þ¨ÆÅØWÆØ÷Akî,bwv:®ù ÐÙ™ÃÖÚHÜßœ‡Ñ}€ }² ]- gü?!‘ȵ,`”fúû³eÈ™iÈ 4s÷$� !VI ¹FsL ƒT Žh…’v˜x&íQÓXš„=ƒý_²L‰­ÿɪibÿßaéåèj™WK‚÷0!Ù “}` ‡ ÷…©Vúž„i; W'LÇN3°r¦ÀÊv˜u�·Mäl…P¦q\oææ24ŠóÚaþÊ–ãpÊŽ à�,à໦ZåS°±Š+{ìš×µG ½ ¶!æÛà•”¸ª—²¯@&û+ôbC%ôŠãë0šýæ³7a{jØ»fïÁ6Ìÿ ó·aþ!öŃӑ7þ‚0ˆš[†ó¯ìU“Η˜.L‰Ú\¤E¡Â—]Yt4^v*PvPPG™²S´*cüi¨”ö'(ŸBöŒaŸÛœŸKJèÝb”CØëìï¤Lظ&£í&Fÿ¯1òáVíI@é;D鿈Ò÷ˆÒÿ¥l(M´Pšh¡4ÔD 虉ҕ&JS ÷C¢´pOe,¶ÐXBh쇥˜d-Ãaä—g­À<¢´\¡·ò ÃÝC²ÈD0€r¤p¸¹f!æ†qhHÞRdª…âTö6"Fœÿîs7´ê© âÍ3ÀÅ3{p@Mh˜{ýK-øö¾)M'#\'¶íU Æ5¬j–RI¥vX]âÈqtÂé;`mþÛgX”8ÓF‰56J¬n’ÿ {÷êäÝ­ž1×ô�˜Hì‘1–<ÑivKÎ{#‘£‚íé¼/ôåý` G‹Îó` �¿à^ØÃ‡(BMÃ…÷…(B[Òa7nŇÊרko¯E¼½Qâaî#4JÉy ÅÀØž6²ö˜–#ãj¬¢f¢£ž^„,;£jwÂHTE&wªŠ@6yó¸°:túбš¯ÖUâôÆAë cÌm,k$H> —6úñq0”‡ñ|œÌ'Á4~2”ñ)0O³”¡0€}‚K N(§goj1åì3¬c*÷9º*Ä oDY#p1�Ÿ"õ+‰ÿƒUøÙP*=c<�g1¨j…ú£‘µ±Víƒúpb .%´.l…Fj;ÛhkTmF[m+4퀭¦£GXõ=fµB3µm2ÚšU[³Ñ6¬6SÛ£m³jÛl´¹1ٺÊÎiAPJaEnÞöb£Î-uúYéqâj~q�ÎPêôzœÈK§zœ1:«ð‰Ï?e´2y%äð*Èç‹áI¾ ^à§ÁË|5¼Ã×À1nØäùÅ,vùÔd|hº€nŒ$¾@ú ôÝÿAoqwžD_â?ÈLx²¯0‡vŽ®öMfúÎd¦°Ò‹OÂù´Ã+{²vÉ [že×*Qƒ_¸ {]Ôrü­˜Õj̪X©X™©áp*pú�zœx3Å7 n„TÞ€äi‚<Fò4Ãp¾¦ó­ãXÄÏ…åü<8•_Õü"úÓ8ÐÄ/¶TS9ú!ß(½[‚&.j°Â–7ò-êqƒ —ÿ™‡‹b²?i8Žàøů„>ü*ï†þ«Ÿi"­ÿšhíB´hw¦vC«ªø'Ð*F¼.&Á·!7€XˆßˆÈÝ„ÈÝŒÈí@ävÂD~ îô­¶(Ýn )gh¥,(T¨R”þ-Z@…*÷áÂ5¬™…¬¿xF‰¶’’í Òó—”:<Ž#p’ÇQÜÛWùHõwÀ/À¥ª<ŽpƒRÙ —“^aHàåJ/÷È{ ¿®¤–«Œ–+UË•y/¬õȤm÷‚K¢ Š'êÁÂ.¸zRäW9θ¦Ô…²›Aêßã2œ6«®­jlVm2\ª¨£_×Õ¥Î(Ñm3 ¿1òA4”VZÖõ-í|CÔªD ºÑÈ–ºh/=®hÜø¼ o£tûV ýÂw Ù½4~7¤qD’ßþ�ZœaÕóÃ0‹·Â&Þ7ñ¸…wÂý¼ åÁ!~ó'à~^âÏÀ›üyx›…wùïØDþ›É_P{_‡*ä~òhØÈ)7Áö#Z'›ˆAÓqä¼ g°£œœeÜië¸e–©ŠÒ0Ú3TQ6¥¸E€‡PJI¢úÌ稊8½G5Yü4ó £ŸoUÅSH蛎˜[VxøÁÊÂÃEO(•ÙØ'½¯l‹› GçEäæ—/ÛB…~Q—”ÞÜšóÔ›®ü è<G!Ý×7ãÖìhƒ›¨¦ððžØŒy½óQ`óã}?þ*Âýê¥×P~þŽ:é Û̃¬™]\3g^‡ðÈ–FÞYØ ·€[‰©NxcLõ.:ïãâ>Ài>„Aü˜- l‰è`ÖÅS(r¥³æ¤oábÉ^¬ÓòÑØÅ`7Tå€ÛH}Ü^Š"þV¡ÙR\˜ã0›î +æq¶Ãí°{¤æ“[vW ¤\â`-ÇŸ¿Dà÷‘¢†ËðzAŸ£»ð ãÿ†qüK˜ÁÿƒnÂW¨®¾@=ý¬àßYflúJ:OC.Ê… ¨KÈ'ê“”¦¡€{êê-j·VZ‹\ɺدWòtÌáÑ[^Ó¡8ˆãPûÀ1sG+É•«*NêA #´6˜K%*„»K¨!rœÇã»aœÇ‘ãK©+ÇÕ ÷ì„\JïÝ N´`Hj>�-¸…—8‘϶Â}¸Ù ¿-ä»Üº¡>PÕrý”ÑoðuÀC;`!1@U±¢ytO&äî€QvŸôaÒ tÂJ÷Ò§Õ(˜¾(Ûã+BàûŠbÞÄ™èZÿUèŽÃÁ`¬à0I(*…Î.X+4؈uaᆭ"ÎipRùZ‘»D&ì½`¯È‚6‘ o‰xGô……GmÛéèþV‚W9°.˜Œ›E[䄱¸±½p;$L‚EȇY¸•i°g+?d7ÌS7´•Ǭ­<Æ{«“ ý›µ•ƒèù¶É¹ËLqñ*‘¼G9¡RÈREåÄú!%ªÈ] °É†×šË«æg¨rú¡k;Ã0Î s1¼)L42ùy¡‰4Ðï–L®»€<sl;(KÐüì_Uêx b=ÓL·¡Q;Ü©(SŽÃçh„†à:ï‡ö;ÀCgÒ˜í¸U¦p`'zç¹yðh•(‘ؽ†ºÚà1J¶AŸbßí”ömƒJojƒNl~\y¯v4ŠMÃf á"$\OäH$!rä8äЃh>$Š0):òN†ªô:ܹ{0½ÍÁ=yV@’g1Übô%P &"M†eb éX-¦C˜g‰™Èg³à\QÛÄl¸BÌëÄ\¸]̃‡D9´ŠÐ.* KT)r'}-rŒ’ëè\Ž£guÏÅÐŠÔø£<ëPUЋc3œÙ¸}Yˆß3]жÊÇ:àI"!º=E^ÇQp°=²ÄI^Æaô2\dõÉwÃ(§œ×;J5F’ŸBÉÓ¥n[9üZ±lƒÇ»`Þ*$Û3¥©žÔœ”N8Ò¹ÓÛ`›'Uù=Ó[áèNg÷Ì~Gv_S µ‡øuÄ ƒß{R=Z:B©ª{z&©w“;„[•¥Ø ÃíS>kŸò¶iŒõŒIª©ñ9%`½²ž/Õ•C¢Ó(Y¢+²˜J}æó¤¢[WšæI; ¹&ÌlTdi]ðª¬?uÀŸQMyÜ¥éIc)ñ1w'¼XšáÉh‡—JÓq©mp‰‘lð¤vÁË«|­þÒ¿hAvFv?Qâq€W8”fD ÅB¦'ãÔz2ÀO†'M¹—´'–IèRp™†Ôo~IC@;™ÉÐ(öd`ÐM³ ŽÒúÕvcè° ´—¢Z/Ek$Nšv°þ†“§ú<é4ùkmðºÊæ ‘ý{‰Þ«K3rw*_ ¯âÒ(ä7l¼òš%Õt5ß4 Aë°iøOÓ“ÌT(d Ť;rôNx« S™£7î€LJU¾vm‰DþȆ֮îu¬ -£æñ¤º cËÿ^É‘·Á<_5wuÀÛ%.#TÔóvšá¢qÜgú3;Q]S6%ÇeÞç¸ÚàPñ;—¾c[ß»´®÷bœ ŒßÈAÖôÙ\XŒÂ]ЧÒD-ôðˆ:ÈA 6ÀqLõ0[4À)"KE#œ*š Z„¡^D`“hFƒ· .›a¿8çÁ“â|xV\�/Š‹á±åŠKY™¸œÍW±Åâ¶L\ËÖˆëXµ¸žm7°ÍâFv®¸‰m;Ù¥âVv¥ØÅî·±GÄí¬]ÜÁŠ;ÙÓb7{IÜÅ^w³7Ä=ì#q/ûF´p&îãq?Ÿ*à³Åƒüñ?C<Â׉=ü<±—_*ÚøU¢ß(åw‰.þˆxš·Šƒ|¿x‚?!ñ'ÅS¨ù ·8*úŠß‰â÷¢H<+Ɖ?Š©âQ%6ˆeâE±J¼$â/¢Q¼"šÅ«â—âuqƒxCÜ"þ!v‰·”²ý�ú²y¦O¯³G0~ðò“ #i£®ûvÑoø ƒ}ת£„Lfk&ß+Tï¥<9 éîVÎA R_¨c 7Æä«È •-cÿ僱UgØ»*2HcW²Ã|öK‡á¢XEç.ÜÓIêJÃEŠ<j Ä3Qc€u†1p#… c&:ùP!‘#ö*ÑüЇQä‚b¸uÔq¾Œîá £ì ÃWT„Ò…r»²åø; N¹øú‰`8Eâc%>±ûAH!#ˆÏ@Ç#zÞ0:zSE?b2=rAè(tK~ÊU¤Tä’Vøµ½o´ýKµý«HE•uº*_!ú‚·ÀŠR•ªÌ03ªJ8H¨’ªccªœ"RÆ’~hB-Þƒè—ÂtÙ£¨º#Ÿ_@©øL_Áñ=ÌßÀñ-Zþÿ¢‡ù\.~„kÄq¸AÜ"¹åø×˜,䀵 I¸Cb?©Èµÿ»¬ÃÐ]Š™È'¼Å )ûx¡u—lørn]Õ>†º)Á“ÎäwÉŠiÈ/ôñ"VµyÔ\,y;ôÊÍC…™š›‡iíÚ)¹ ÷fRM¦AºL‡>²—ít§Ÿ5K?sÊ×´ôs's¾9æ…g¶yK—¢.éH•>›JE¹2§ê‹Syl±f¶kŽâ£ âlãˆß¢<Ò$»]¨v;¿vÛA§–/UxE]ðòαvø¸²á“öût'`lóÁøÜ€ñ™‚ñ:å­­ð5üÛhøB5| ;ZáKjøÑð¥jø0úо6¾R _aúVø†¾5¾Q ß`JÆwÔð_£á;Õð6Lh…ï©áFÃ÷ªá{lÈk…¨áG£áÕðCá½àÎq”È6<©ÑÇá*q[œÔ3Ú&$=z2ðIr’…ø¤¶ÂqDnL*üÆMI*üF[tü ºÀ½\WYtí ¨rf¡rȲèìí9–ONÖÚ,o6cÆiáòlÆ1—Í„í䘙QÒ¨¥TrÞ™ZŽìdªn:Õ9ù; £“9v@J+sbàÒÊ\f§üàédz……í,%›¹ÕY‚ÓU"sdŒµ7ÑÑŠ< „„S†¡rÊ¡p²3dËE¬Én—ãX‹œÀËö¦œÈÞ—¥ì˜œÌ>—SØÿät.ä ž%Ëx©œËgÉy|¾,ç‹ä~º¬àµr!ÈEü|¹„ß —ò›å K …ÞJóÐIË-Q™Åé¦ræ5 ¿…Q×0‚߬äX ¾Z*:*cù8S¢'˜êËf©Logi O[äi6 Ž>> ãMSÌ'šOHÙÁÒÇŸiSg¶‹7>AYHN?\4!Ì÷^9´•EÙ,£eN+Ê—kÛY¯= ¦+Yk;αÀç˜4q@&û õ7G; ñIê†Óï))KqYˆƒt^Ͳ:ae˜|äêu°lÅo½ ×o†- *uPÅ“,GÅ2jé'ìÜÇè<¾Fg³¾ÑÞY×—:Ñäîot€S ègöq^âb-Çÿé‘¥.tIS³™§ÔÕÂ{ÅÛŠù €}C9Ã(MñyR(ÈqcJ¾�Ë.MÅ)õèL.2³Îèé­ê£Ä®Äã>c|fÅsjœ%výU¬ÐÉrÕRòÌÑq0 ì)ÀÂl>eS ¯ß£«ªfUe'hÍî~;`€ÇZ W®¥ò´æ9I>2Q©;~ ¯m$;i [•û;© êdƒ£ÝZ`õ’¤çv6Ô†J „©ç°$=‡µ³áq=Ë©gA’žílD\Ïñ=:™¯ÆNÎmmÒZ™±©;Æ RdþÑ¡×רÉþí*ѺAÊfÅq@‰CS+²‘¥)¢Äãö¤<¾nËq»wBÿx*÷qGŽ{Ü-0D¥™™»;ŽJºêÜ ŽŸo£MƒÚx„šVÖîŽ_œÁQ1l/q#÷¯íÞ#n*CÄFÓ@—ÑS /ÂxT ëAÊ èrLgÁtY5²ÖÉ4ÊF¸Ó«e\'ÃèîE`—Ür3–[àY¹^‘çÀkò\ø\þ‚<¥ËóÙHy!k³Mr»Nng÷É_²=òRö¢¼œ½&¯@¥~—òj>L^ÃGÊkQ™ÿ†×ËøÅòF~•¼‰ß'oæÈ|ŸÜÉÛå-ü¯òVþžÜÅ?—·‰ty»ðÊ;ÄDy§X)w‹ ¼Kl—»Ä•ò±}�RoB/Ø�ƒy©Š4£š§[Âx–½¡T¾^a/¢Ê{ Rá5ö<ŸLq<Ä~T>½Æ?€»Ôñ§çÇ| æ4þäSù4zÌ%¬c,Œß¦ æf¨È…r3•1Ñ1ꛥN¼'ˆËÔÙ·Ö‰¼ sN¸NñÙê΅ѳ͸¡—ùBqkôÕ”º<F¹C,§²ðqbÎõÓŠÚÙ˜üí×ßJ¿P‡Œ¥›~á[JSå´üBdÙ¢Â|RÈè;,-ÌK�òóÖo§gC¨wþØv6Võ¿L¡>…ù…m0ÔöV`d"sìEæhEÇt?úÀm0�y¯�Ø1òQd–.(•Á,y*äã°F>,Ÿ%Ÿ‚&ù4l–G­§} —áó< ›ÏWve Œ²®¶Z–h«z!OqÉte¹"O¹iòæ™Ñ@¶26¹lÜ!6ÿkgíèŸmv4ÛŸm>¨ãô;xhÈ4êS}J«º£äw ÆqƆ“föPšhoÿ£å«0I¾fs¦Zî@¯47< ·›NF/(ôÝ‹&úÁBÔô;Á!Dõ×�߃>jýVâÃ÷¨&,WÑc4ãIȈåñ´ÿwÀTžñß$»6«B–™ˆÿ&Ѹ”"SÅÉb:t'?h;l7¶ý-ù6˜wqë߃Lù>Œ¢óv —õ1L“Ÿ ÷œ"?‡¥ò X+ÿ ëå—°IþΓ_[¾k!_ÉŒ@WÐÅ«ø4ÅÛX»q_ˆÒÈ`4,TÏIh³YáSÝ$b:,ºž.VºªƒM®(´/¬MAÿgêe‰ÁÛQýˆ˜/úp1^fΞ }øb¾D]«-µžÏ4,ÅW¸ŸMkcÓÞF9$h‡ \е˜”èS¾Œ/7Á=bîîLº1®4.˃Î"JŠŒsNõ*&—n–.cPé‘Åf¸f\›u²1öD7Žp8ÜêH…,‡i0Ø‘…Ž ˜ìȲè?�é¿‚@fZˆÎTgäìM1CïT(±Bï•Ö ÎùæEîëd3+y6«0Í}~àvxl$Ñ͙֯?ÕzÃ;Ï„šêËmgeô™½7èP: OþŽ—¯R®0£¿=a@ôö63qÏŠžýÒÇiÁrZ°V[Öö_d&68Æ'ùE£¿‘ ‘^‰ˆL¦§ú?ÈI镈Ȭ$ˆhôw9º îdst£cž}0_ƒ;ìKÛ {íQÿÆIÚÙ(usQÙT*•èB˜W¸§ÂrwË Ç}¹áÂÚœýJŠF‹(ÜŒõ^`õ>¥ÇÞŠ,½1™¯Ò²øyŽÅ(}K ãlÇrÈq¬„>ŽS¡Ô± Ê«a¶ãtXï8Ó²:¤™¯ÔJQ:«¸¦³£¼ëÖ[u³©Ž}¨Èq¶ñÂ×�]1ŽN@-˜T=‚PIžOõ ´¹3*T¢L®Mîl_>Õ¢â å‘—¿~»ÿúP`€žð µmEDRÝIZ¸2ÅÅF‰¹³¸œ}!ÝÙú8=Päìóœy°Ë+-1õJTjóxiSOåëÔoÍ\°˜¯Wb˜ L›šÂ~c­i‹É²EÈ)•¸ ý0M‘®–¶¾úŠ)¹´¸ _.U\ÂÏ/ž9tF<‡@_çpè,€¡˜: -f÷@–ÂŒh\dbÆè/é™Xl5±(F–«B¢aBNËÐ#/§ÐÑßPÄÆÐ8–ˆÆH$×(Huކ,çÈwŽ…a˜9K,4zC?åtŦؤ°ë1ØÂb°…ɉq2£‰1‰1 ‰1‰1 ‰1ë'ˆq}Rb,2ˆ1©Gb˜ht#Æ$Æ\$Æ<$Æ|$F9c>£ò„ÄôG¸L4f›OwœêÌ0A9ÙNDíŠlˆqº‚uÆéŠFûÐP,Pe.,Ë× œ‰DÉ ³H§ñΙ¬¥ˆû¥ÐÙ¶NÐùFÞ j³xù¢¿'äüls†Ò˜ŽûiŒ­Â®|Fç¤|-hMÖïnDlHÞ¿ne‹Bf6[¢¼A¶˜¼A¶¸-=Å|gR!¦ægô)ôå!ƒÞi> á¸3u²epK%õ,ê`Ë«Š;á·t_·î£Wºut¶÷Eú}ð`6[ABØÊR™ÍN¥ˆo÷NHÏf«T6úBbsf³ÓTÕ>¶:›nfÏhe«wB®:´Kieg쀔NvæNúj;@ËÔökö1W[‹•ìïbÕ˜«Ù'w±ZÌöAIŒz èæΆþЄš)ŒÚ81ŠŽÝlØ •(8‹áXç¢;ý Ì7Ãùp\�ïá¯p#¼Å¶Â1v‘¥‡€GqÝü_¯^–Ö»J=þau«·#•àçõÆäÏ›'D|áÆ7³õ »ȃ�[‡Ø1Ý€ºs¦g=¦0⾘6  ™†iÈ™‚¸X£3…U{Îjôÿ�PK ���(©Æ@݃笞��C��&���com/sun/jna/StructureReadContext.class…RËNÂ@=… Ö¨Xñ ±&.5nHXbع˨5¥5íÔè_iâ#qáøQÆ;ß4¦™™;çžs眤¯oÏ/�¶±ž‡‰ù<²(çÁ‚Úu,éXfÈ…2ˆl‚¡Ô²ýžFžuáq«ýÑØeÈœ:Âí2˜­ ~Å-—{gV N]aK«©ZÄÉî9ž#÷vª sŵƒÖð»äa¢åxâ êˆàˆŸ¸„Z¾ÍÝu€š<wB†Õá/ Þmøž×RëGd0΄lÅ5«µÄÀ#Dmö3—‰–œ:ßö£ÀMG¹2‡9ØRb+X50Š5:F äg¨ü€añ;¥ø½.+ñÙ/%¸cÐ)ÅÑÍ%9+þÑpy’ùéê°Ö!›úW€}d4®È+iZ£0À0FÕ1á )Ö7Ážªo< ]ß|€v‹Æi/@£Ý¤1ó$*Ó}„TúRLb ˆ«qX\©ÇRT1=xÊ¢Sõ2õ{¤o?‡gcp)hô ƒ 3˜"Ö~‹+CÅ¥˜5÷PK ���(©Æ@ñzŒq��Ü��'���com/sun/jna/StructureWriteContext.class…R[KAþF]omYvñ– ô¢mÐcÑ‹à“ø¢=®ëh#ë.¬³ÒÏ*H‚úý¨èÌ®X„K 3söœïr³Ÿ_ï�®PÍBC!ƒŠê(¥PN¡Â\HÏ·$C¡k¹scá;ÆÌ1~õ=~àM·Ç ¥îÌ\š†m:SÃã›[Òè¨a’·ÂòŽáº¡InmwÌr]áðž?qo`ŽlÊä»®eÚCÓê{LÈG±`8Ûîtï ÉÛ®#ù“¤Îô)—› ÑhFš&h'œµB°èi³}×÷,Þª›òVçKÅÖ‘Å©Ž$R:ÒÈ0Ôÿ혬cnÏ”bùS7šCÔé5zÖ-"Ò§;N;‹ÐÜ=P^er­7°Öù ±ÖÅ ñ—€°Kgž„€¢ú!ˆ^Æ*´ZH£µ‘²aA¤Œb;´1èV5­õŠØóF<$« Ö‚ ‡8ÚBŽÿ%×¶’ÔÉ7PK ���(©Æ@ …Û���*��!���com/sun/jna/ToNativeContext.class}O±NÃ0}—¤ ¤)© b`£ ¸#ˆ¥S¢îN°ÀQjK©Sñ[LH |�…8S‡Zº{ïÞ½{’¿¾?>,0Mb’à(ANˆoµÑîŽ^ÌW„hiŸ!/´Qå°®U_ɺceVØFv+Ùk?ÿ‹‘{ÑÂYÑØµØ F´FŠÊ–Òé­ZZãÔ«»!¤vèu¯ýÍÉÎþª•[™!ˆpº'‡0õNÑIó,êV5çø/þG0ÆÌ®½2º|½1 pOqÀ,û3á7>büëÌ~�PK ���(©Æ@x-Ƨ�����#���com/sun/jna/ToNativeConverter.class;õo×>nvNv.FŽ’|¿Ä’̲TFg Ÿ¬Ä²DýœÄ¼t}ÿ¤¬ÔäkŸäü\ýâÒ<ý¬¼Dý¨Rçü¼’ÔŠkMLõŒ \y`5!•@#…5Õ8ç$ƒ”ç—%§ºeæ�•ˆ!™Z–ZT’Z¤ÒÁÈ ‹Ãjˆ"FtËÙ˜@€‰…‘™…H³2°EØAâ �PK ���(©Æ@±|B?‡���Ã������com/sun/jna/TypeConverter.class;õo×>nvvvvNv.F®àüÒ¢äT·ÌœTF¡Ê‚Tçü¼²Ô¢’Ô"½¬Ä²DFÉäü\ýâÒ<ý¬¼D}Œ  %ú9‰yéúþIY©É%Œ òÈÊÝŠòsýK2Ë5É¢˜&ÏÆÈÀÈÀ„Ì , ÀÈÀ $™Ø�PK ���(©Æ@\Fƒû­��������com/sun/jna/TypeMapper.class…Ž1‚@Eÿ ‚b¡…ñ Ú¸ÆØQšX¡r•LX² ‰W³ð�ʸ4ƒ‰ÓLòÞ›y<ow�+Œ\ ]x„YÌõN«ü ë¤á­*Ö5kÂf¤²‘"“E,¶™¬*D*Õ¥i!ÅÊ'L/TÛúŸ­Ã—wRñ.ɘ0¯%ïeY²^¶*óúÿn “Ï©ã9å¨v íX6¡0»Ç$·Í1xPK ���(©Æ@ñ”ëÅ ��ˆ�����com/sun/jna/Union.class•WktTÕþn2™™$—$L$¼ Âdò¤ D )! ›úp˜Ü.ÌÄyð°VkUŠÚ—µV@|´T"ÛðJ@T°­ÒZ­Vû´µ¨míZýÙÕÕŸ¥ß¾÷ÎÉd@šµrîyìýíÇùÎ>g^ûïó/hÃ?Jp/îsa— _-A!vKs VHoæAi¾&Í×K°ß曥ø*Å·ñp)¾ƒG¤ù® c*öS`¯ û\x¬ÕØíÆ~ù>.Ín<)ß§JP…ï Ê÷Ý8 ? OKsPš™{FšC%x?”Þaiž—$"?C£n‘é£28æÆqùžpcL$Æ¥9)N’æyY;-Ò/¸ð¢|ψÀYiθð² ?QP&õmZ·®E9êKÆSÁ¤5RýѨïŠ -¡`~O06ÔšHE[·F­¦d*®Õeé,§Òf=ÖiŒ™AQ¯£f5ë¨Ygk*˜Ú³5°-Ð Dí†÷íŒ&·hI=¨`š©-Bu"$ºz4¬ f2ì†ôšs…Õ“ ½ ë8ºbƒ4UÞ£GµÞÔÐf-ÞØጧ' DÖ⺌­IGr‹ÎX+'ÄzsTEéÚtï„éu1=šÔâËÅŠ2¬ :ï*ͯæ½â@DGûwÓ¶âg¾&ÊÊšÀð°eÅ9dôéÉŤ,ºˆµKáºZÒô¡Ê;iKŒðBü×¹l®¦’z¤ÕOØ@2&6IC¹:[ÙÜC»8$dè YŸ$B¸´SŸ$kjíæ­Z0I¥’íq=™fîÂ<Z“•Ä™¢mH*ÇsJØLÄàzShAžtäÕKLÔ›DÚx>EgÌè9ЦS°$ŸÕË9|õÞË3’2--½,…¼óìS‘@RëÓogÄEÞ¿ 'Ìq( ©XÛbú`w·ßä˜Ã”ð0×½©>r†´h2gÍÐ'[7 •é‰n=žH®Žh¦j©µwþh(¦à oÃEŽ£Ó¬9››‡iéŠT°c‘‚y¹ ½±dw,\½#¨ 'ÍQ°£¼ì‹¥z·.õ¤Ä¨-¢ëÂOUü w*¨ý8(s2"½1CèZ-d Å㱸Šëp½ŠWðªôz¥¹Iš~i6¨8'«?Ç/\xMÅ/ñº o¨øÞtá-¿ÆÛ*ÞÁoÌýØÍWñYt©ø$V2Ÿü‘ˆD:ãá”$?Ëñi¹)]• …¤dMíÕ¬«…j¥^Ôªø-~'ÍïɉZ=ʉ?àÁçT¼‹­,Ú¶N”Åc·ÈZ�›U¥=ü“ûg »ðžŠ¿à¼Š†T¼T|ˆ¿ŠTTÅ*t±B1àÜ€[ì€U| wªX-â‹øßbú¯¢[æ+³57¤—Vq¸CŸŠíÒtb¥Š¿ã#³ó§wÕN«J¸¬žŠ/â;°“ištñ˜^OÆazrxDDž:³ÎË%ØH%ØoÊ&zÿ–xl»\v¸ž™%¬M¿3á™ÔX&œÆ&ª·!ëà "•`Â…›ë2[¸+FέƒR•o^[·îòh‚rÖ]ã™<˸·½Úޤqé³â8¢Æ Ê›¿èÊÍÌILž Íàå9âyªzޏ;K¿I&:bß{…,YtVn?Šs`]�¹fÌUk“+½ùª”;a«·_NUÏ;Vè‰ÎD‚åX¸Ð å ÈÍ %Äý*û&0ê¸6h^¯¿Áï˜Ëgç >¾‹P Eò×i|Ýr"¡àZcÎ-§/kÔ5ª„CÊÇ~ŽZùUÑ7çv p[§197²UMô` ¿¢Ük)wPº@¤}ãpe´KŒÙ:ê,0¦™R‚ôÖb1ë& ËÏ…üº|³ÆáÎóR©Á�«5Ål0—&½O£ÒÛŸÏÅâ\Ô&ºØ|oÆzËÅ V?GÉ']lGI.Z=Xd ùL9Ímûè¶|”žX(äì-ØhY8cYØäC)ÿÕS˜¢àʬi:…r{1‡ gy4‡§ñ Š}Í'PydäÂùÓ¨ÚxÕÇ1ÍsŦ7Ž¡Æ3ƒbÕŒaæ8f±žG€khpf‘Xõ¤V iu“v5Iµ”ÔJS2 p®ô©Ã&|Æa“à&¹Ø *ñJ±‚y„z²U]é`Oav8¾¦éUÎ4Ñé,ÌöyŽéóô\Ÿ3™žÁ{ºæ¡ùit`s»ŠYͰ£Ëv¬ ŸÇ­tÈÑ� Øç…g¹x£ÅŽrÃEZ¾RhR;j›+3àhnŠu†*嶉rË„"—¨¼6/ðÜLcŸÙjÑPøcÁ-ðë¨Td7c^.z”mŒèÃD¹-+;6z…….=^HvÂØbÙyÓbcølôMbcµÁFŠÏ÷ùŽ£r u£#Þù¿˜YO§/ÓøÝdæW8¾‡Ì¼—̼¬ÜÅmÜͽ?‹3lv¶Ùì Ûa†mvêØj÷°ÅÎNIà‚qÔ )+ Rúš8µÐ7šÇç—rÛ俣t~¦07ìÃt<Æùdz²ßi»Õ‰/0ç|\ Ýâf¼µ,÷• h¿Dögçd Þ½(jfÂßW2$žÃº$ÚÓù Óuˆ§áY¦ó0–๬4N±Ó8ßNc»ío»•FÈÐòò‹èe²Ù¥6Ž£!ÃDó�e©9FôY/³qËÈÒ•.€î! wÈÀ}·…ïÊ='Ñ´ñ|#pr¦‘A3æ}¸î(še¹Å\n6–›íeïQ´Êr›¹Üj,·ÚËÅâô¢Q%÷Ü¿ÀCq–›ôJ–ÛC¶ÛC¶Û|²šn+˨'ÉÞï›É[ì³ žÄC…K÷oYQM‘µƒ¥gná2g³ð®ªqÇÕÕŽh¶zO¡-oô²ì1¶¼Â¤™ç ,¹pÀWíÃ’ŽÙOÍ/cé^¨³öqoÆÐ¾aöa;ÊàuzüäM9|ÞÂbþ¨YŽ·ß»ØÆwà=ÜÅß»pàVöI팬\Oíż$…8N\Ér"Ä)"ÖƒH°( …÷Û9Û$RFÎö9E?\DãCŸysÈ“ÝÚþu™«¶É1Žk2>›/“^äšMÔm•3éÝN;²›üU`¡¯´=eäÖâs(RFùúY–»ûÿ¢oÿæáüOÖ+(MZ·üÆ¡´ õZTõøNBí9BV ÏbÑCÇa<ײ½Ñæ““¸w±Ò)FѾ›ÙƒõL+`Ý“ö|ÊX+ÀTÞÏì/å÷%gñÿ�PK ���(©Æ@ÞÑ¥Wô��æ�����com/sun/jna/WString.classu“[OAÇÿÓÛ¶e+X®ZÄ" ½ x·€@­0)ÁËÛ¶ tIÙÅí®¿ ŸÀ4Ñb4Ñødâ‡2žÙ.K©%›œ=3gÎoþsÎÌŸ¿ß~�˜Âj]˜‘p3Œ[Qøp;‚îH¸¥ÿ=$s„É 3+Ìœ0óQ<Ä‚0‹–$ä%<bÕ,SÓwâk{êU©ªú®RtærÕtÍšgèMýNo1òÆ6gè\Ót¾nï—¸¹©–ª\àŒ²ZÝRMMŒÝÉ€UÑj[+ûJÍÖ•=]Už{»1Š…-£è*êI¥Ûjâ¯mµZkÑ´QÚãe+—~Eãìiܱ+j­ÒìO¥ Òq š|Ó8‡FkBU®ïZrÊÕ\´è©B:ÏÔôm~HÛÑ¢Žš]*’.®— >˜*š¥ç)ñ$J2‚5K5‰ãçú6C´hØf™¯h¢@²[ŒI‘,£Ë C§ u»Z}fhºÅÍåÃ2?°4Cgiä$E«4µª½ãfrß®YÉOê†>¡S–`õÉèFCw›ê˸‚! +2®")c£2ÆðXƸðRHKx"£€§2n`‚¡«µV }íL}l 8õn\jÁÖÐIßÛÞ5*r·3«k†Ã_²wv¸IÝxkª J꼂»8›—Ã4=˜.zA£?•†^X˜|ªÙ~½€Ÿ"À@æ,û±ïð½Œûø™É~AðÅ| G€lÙ~òÈ^¢OÌ8ù¸Œàxƒ´]QowÅÁÌg?zÈ3™p0rc‹a¢Inò­[$²¿:Bo¦)[G¸ŽÈ¤À{üZCMÊž²†qÍQBÍwá3®²° Öm% 7‰ {âÆpÝÍ_pÅɧª:Z£MjdOì©?OÜJo«†î®›?窉ˆüD±V@¦IJÄ“¡ù¬³5Ý|µJÃOÿ˜@}Å…Ä`­­›ppÉÆB䃞Bžü)'súPK ���(©Æ@´‚»ù��ù��'���com/sun/jna/WeakIdentityHashMap$1.class…R]OA=³ívÛZ¤–ªø R¡]ÀÅg /± ©ú8ÝNè–e¶Ù%ö_ibC¢‰¾û£Œw¶›Š¥I7™û5÷LÎ=wÿùþÀ6 °°¬ÍSmVŠÈ£fáY¦ö«Ö,4¬ î×ÎÄ€¡Òìñ îø\ž:‡ížpÕK†Âñ@ª®PžK±n¥ †œêzQm‹a¹éçNK§'¹óA𳃎ÊSƒ}ußð>=’Ûö¤§vöë3»¯“¸^i´²»A‡xÌ7=)ÞÆçm¾çm_è1—û-z:O‹YM— ¥)E¸ëó(TY™E¨öBp*Ôk­QµÞ˜¦Ržî[#aòÑ8\­O¡> n¦¢ƒ8tÅ+OS^œBæ¹—pó%q£„9mÊXgXû÷â~?•èöEÈ•ȽϮèë€66sb†ò$K  I5Vžïè¾=©BÒÄL}¦®÷2÷_–èßË“ìdPÑœõ  ¿Š&%£¡ÆY–N·(¯P´#©Tíõ!˜½1„ao‘±/‘ýJu dsÔ ¡J¶”ÄUÜÆ"ôÒïá~ú–C^ß™ö7_&ÀﮀÍ1øÁTpf|2üRð6uäó?`~ºDî×þc‚¿3êIñ:zœ¨dàIÒ¿„»‰Zé:ú ¨ÃÎþPK ���(©Æ@¯€`U�� ��;���com/sun/jna/WeakIdentityHashMap$IdentityWeakReference.classT]oA=à ëÚÖÚJ-j©¢Â‚]´~<Ô˜˜&FªQL›ø6l§evu?Lø/~$>hÔú`bµñÁøì2ÞY(¨%’H2;3gî=÷Ü{gøñóë7�\N#‰E §qFC y gqNbçU4š‹* †x“ûMVeHMÛÏWk–Û1ýÐ1[77TÝN`ÝÛd¼Æ¯0¤ë]'hŠÀ¶ÈñºíØÁ †«…±žµÊÍ6w¶Í»–°‚•â:©Xu7ÃdÍvİÓÞÞh2]s-Þ^çž-÷}0.…2ÌîsË8÷Å–ð„cѱ^uá­¶¹ï 2»6NS~$娸–”p@2CJ–­'Z)eñÄ“·¥ªÂˆR…]2õăVwCÏ·l™ËÜAK’@Ç!è*J:ʸ cK*L{QGÕÿJKÇ%,3d‡I“ùWÆPSܲ„O·¥B÷åæø¶k»ŠPÖzyDÙþí#ïÍÔТÞõÑ!Èþ-t¯C#[B=S¶EÀ0S(<FŽ^HŠRŒ•›¾Ó²!`8 ùShL`’öS´Ú ½D2FiÌ(—öûÅ(ï!¾‹ÄLjèHD§ï3$ðœÜ_à^še  ŽÒ Ñ*ƒ9ÂŽc¾Ĥ™Ñœ0>!±3 LFૈFïôi¨Í8ÑwîµT˜5J¯‘ˆï”¾#¹f|†Z¦!ÊržHBñ*Þí;Òóž´|ˆÂäzTµYœ$µôò¨ §¢\bXˆØr˜¥Y£³$ýÍ�¿�PK ���(©Æ@Æa“:€�� ��%���com/sun/jna/WeakIdentityHashMap.classWkWTU~ö07†#(¢¢x—t�eÐòx#Â@¹¨(Ššurp83ÎEAKKKK+S˲{ŸüÒ‡Z+%b­òSú­¾ö²ç=s8\f —ºÖÞûìýîç}Ÿ÷¶‡?ÿýõ7�uø®Ë`„P…þ�N…P ó@>ôˉY„j †¸:@,„†dÇ’!@"„bôËÇ™"$‘*BÎp®e–+#²:À…Êñ¦^ áÞ’{opY´_‘wdxW>¯ÊêZ�ï…°&«à}®ËpC¬ü €øHaAkŸa¥ÍôÈC?}Ðè7’†5´VË2’M1=•2R ¾3#ÃíåmƒúY=Ó­S‘¤ÑqoóÞëÕ£§MëTW:ž¤|IV>“6c‘v=AÿvÓ2Ó; ÂUÝ Þ¦xŸ-gZFGf¨×HÒ{cÜ)m‹GõX·ž4åÛÙô¦LZ³ª-Ф2VdÐÒ#bù‹=5Õã‹Æ =©P[iÝ´RûŒÒ O!ÐÙ;hDÓ UÇhÌi9-Í=T˜3ЭÇľ³Ù9HÉ‘.#­0/\5…'·x­€î¡p³Qït?TÚÛb¥‘=W¦BÙ™Ö´‘ÔéÂ,Pzº#³ ü4ÙÖ¾u6oTæ±@g2zŒUqê9%`kó9(Ÿ[ ŠoË#þ”�ÁZ—>S¡U!`¦š‡éû›1ñSCc,ư„gd‘$Ž¢vÒŠŸ•¼H™ç9ùíÐOù´€4Åc1ê4ãÕz“†žQ“;C\œõš¼Ò£Q#•ª¬««Sh ÏšbU³•‚g˜@…]#VzÀH›QjèŠg’Qc)©\ž³V5l@­†Ø¤°ôÿT0ï&9:"¨Ó° ›4œ„ÍÏ”¢þ¦†Q¯aês41ï4ìÂî�>Öp ·¸£áÒð)î*ÌÏ“ì>Ã] í2|.B«f³l£Ä{¾@}�_jø _khÅn…³ÜÖ°OŒoG‡†Nì×p�5táºI§¶R™D"žL} ©8æHópÔHÈBÃQ¹Ý#Ḇoð­ÂÜ™©Ì1®Â–Ù“'OtO²+;h:m@aá´ŒžÒJswYJ¬­c˜^ËžÊÂykÐÏš·ûb §¿µ<‹õùùè}}ì}ù*‘EZ’±†â}f¿Ýàm¾+Ã3º\n[õ&âÒ*ÂO,¿¬â{[ŷ΃R©&>Õ) Îó¸ÏÒâø<¿ð)d–¡¢zžêqôŒÂû�ŠK—þüd_~c)¼·pÜ„^_‚­Ø ÉQ‚'[9+)>~7g9+®þ1iÒ( '‹©h@!¶c m­ØEc :hüUáá¼H@q¡žêšQAóà6d‹í¦¡6äÂì5RV T(¦ípÁ_¡ŒH•L€×ŒaN.h3A÷L-qAK°ÓÝeóÐhq€sT@ÇQL—–´e}1Wa óÚסTávp1_áÊ:6Œa¬B±°Þ˯E õ¾q”÷T/ö.öbq½¿f± K<8rÿñ£š_Pñ£kh5MöÒÐ}tf*ÙjÑAã:ñ2öã Žóÿ œÄ«6‘šXË,iDñ+lïx)»È¦äãB~5ÁÏ 7Ñ{lêQ‡º†#ô^‹M½Õ¥þ=e$]s©/I½b‚z¨£f9Oûk:±å¶/_'¬N¥½$¥á}̃föÛdªyZ‹"’ÙK2,… s]s7»æîsÃßä†_BÿË8=ÄòÉðûí´|BèۜгÝ>E²®ð`’U6¯,ÚŸ%YÙÆðc—Ê)ϧ'Wä '‰ž²ÑWfï¹è庬$Ø®ùR8zêúÕ&ô<Ī™qŽØÃS*Ws+—ƒ³ÝíÎV«œÚº@¤7òö€Ãèv‘²q Žc Sªò÷º4ŇA—e)ZgÛpô©ô\n€.Ó¸+³¨Çu\Âks _%æµ¼„ùÜ>iʱî:‘näAZ-?‚¤‹””à‡‰¢"ÜVó–3ÕCí»Þf}dâìþã¿'Ût¶ðnÒ·ˆ›oÈ,åÏJþÚY‡»nbÍÃ|ö”½¶EaÇ‚ ONÚ,‚xÅë±m‰8¬|¢o’L6†§ñ±Ö¦×vµ‡/cÖs^B•ò§e ×e¬ü—ü…ÌbûßPK ���C©Æ@Ö¤ÝÂ]�Œ�(���com/sun/jna/darwin/libjnidispatch.jnilibì}}xTÅÕøn²„VïbSK¬©*EK„bÙ… l Š"‰ v (Aàî’\‡‹Ñ¥–*UT´TùˆÈG€”Š U¬h£ºk¨F¤ °¿sÎ̽{÷#A_ß÷y~˜ç¹¹³wæÌœ9sæÌ™3gfö]زÝd2ÅÃÓ‘¿í&“9c„mfí›ùAø6`%~ƒç2x:›âà§yâñí翟ÿ~þûùï翟ÿ~þûùï翟ÿ~þûùï翟ÿ~þûÿøïÀÙÿ\S<]àñv0™|&» æÿ¦%ðoüøÑޱ£#�›Co„ï ü?Þ?ÇJ _õ­áo‹1O‹iAfÒ,wáÔ鵑ÇÉ÷M¦Y×ðwœ! cò˜1}–Ûð;<)M&Ó ñ6ÂÿÆ÷L/š:}òø©Ó fÄÈÃ~œçoK›yäO_P8áÑüØx¬ØÜxþ6âmñ ƶBm‘5hô C"l{è­µ¥†Ëøñ“çN›lÄ%Þ*ÞmÓ ’Žáy¤�ì1ñî ®á1Á=¡<¾^ï¶ó˜8kVØïð<Vl}<ó0G„çìÈÌaƒMá1öÐ[££;~üÔG'<”¯±ALx«x·EGÌ«Š`‡:sGÞâ�»(Û*-ŸñãgΘ:Ý_8Kû Ÿ%ÞÆ² áñãö<:s¼{ÂÄiù±òX�°+ÅÛu²t5qz"þývøÐw9²†jÐ)fÓœ”л£)œæ—ÀÓž$í»)oš6uâÃÓ§Nž:kæ÷¤)½ _ æj‘ÖwOQç–k“Ž}ðü•) šÖƉï˯5›^÷þ>fӉˬ\ñYŒ(áÇžfúmïe¦¸,x¿ ï=̦4cþ·š¸As¬(+Îkï{æÎrç?Ú{øÔ‰… çöÎÆ.]4£ð‘Y½‡M˜=!ÏÙ«@ûÒ;Zk*ôšÞƒDœÈ·¯1߸bž¯gVao¨:>¼ˆ^™½ Û1~þk÷O>nÍåÇÐXVzû×&“2<Õ¡�…¬¿»» LD/¸>ÊÂþ\ÊòñîÊý'O‹RÜÊî·2ÛY¼’œÌÍ‚·;ÕBá$e¯ÊÝ ¾ÌKµê_ûÁoM¸!ãØý'™§…·î ~&u[€Å†>í–ºA'…òî‘'J÷1«2°- „¬¬|Íײ(–Ôà3}IäêÄûöî.è%uóR}9|ª€wZ™Ë–Vå;ìîÎ̘WMV* 1Vn×óœ¢åitgÊÕ©˜¡!?ª‹âi™zôLXßTÿâ`0È.‡Ì±'(PsØáecòW_Ó+“oà!>[kƺ·0O+5/ÐêOÅüž9¬òœÛìK6÷„ßZ*éŒæ]Ø>™k¡±°oã3 ž‡EŸÇàÁEŠ'áY *¾ƒgó0Êps6eñW& æ¯{øë^þJ/©[†)‚B$)¶±lxVŽü$Ì}Ð[å¾zKR¥ÎÐÖ:}5 e@ž¿k1Ë#³KÞroTv½"ó[™ß>-fed~·`Õã° EšU‘ÐÇ´˜ÕÑÐH±À¯ ÐkÚ„^ „4\AW¶ ½9&ð²zk›ÐUÑÐÈÉèê6¡k£¡‘}× ÷· ] \ðŸA×Ç€fåÖ�閭ÇÜÐ7cŒ-*f ŽÿÞ*É÷ d»{M „ `å¦Ùðì±bvaLr¬˜ÕsÌ#f)ÆŠÙŒD¤wàtk0(:2—íô'‘‡_˜-¨¯Ê-³Üå–žî«Ò‡±¿A~ k¿ˆBÂL”ñìÅèÍÑÑñ<úMŒ.ŽŽãÑ £—DG_&È>%ȉ]DI"I]t’_ˆ$¿I꣓$Š$Á <Éþè$]E’c$ðø'è>>hã‹ËÊ:3yÊBD;l$ŸSÙÂe@±`]Âå³|<)r<pÚ|{ÝW²Ë™|¤‘çƒ+ô'M!A/u‹U>• #żFm¤Øÿo1R$Â(‡#ÇCÈß(üÝ‘øŸ±Ç… Úæ7eò!³3yœ†¼ ç³HåP§ž[°n¦ÈèR¬ÛåmÀÅæÏ¶èã© à‡ÿŽ à2ÃPY{<vØ@óÈ¢Ø.F` è}ÿÖúüçC¯¤8žkà¡ Î'iUá£r;íÝYðÆ ؖ͋ʽSú<ç¿–?’áóðFEÌ£óþë…6äI$¾Ò&‡pÎ`—K›ªPA!=P¾S+E.ú¬]eQWʈ¿h~r50]š—-ù¢Ô@e·|¦Õ¬A/°ÍшÑõ "ÚÙ ³ÀêÕî_3W£ù¬ò¡ÒÆ\Fþ&!Ü T;WƒŽF2g£š8Ù6âKõ<¦jh/ÕÓ(iòª±½”1å`‘ÒÑ<¯)_4Ïo¡]T¹j+§™*²UoFÆÉ`0íp`ÿÿA{É r“h©h)ÅyŒFÜó_á… …A1Þ¿a¡W0yÓ¿ÂÛn€Ðv>HfŽçɶÄHFM¼c×·«&÷a¡ “�šZÇ¢„™²“÷òKXG¶�M „+qÀÒ×€]¼¼¦Ç T¡BŠÝü‘R+Wßtßã5~èò^íTt¨èöö+šÇ“íúUÔܬ&›bWµ1TUÉKJ%T×¢U7` Fôw¬Ï-? >{Ú¯šÎ0Ù¾ÿíúøõq×Ǫ×g@¤üÂúÜÞN}Ö‰úh¯>îy˜äðÿ¸.>ŽI¬ú7Ögþy^›^ŸiøGPB¤i{Oð4åw´])ßnQñíTünÑBŸ¶[q“|þQñ“ÆŠûZyÅõŠÏÂ/ûíóMt Ù?ÿÇ(þ­m›(~~Ž£h×Q¬Ã/[ ‡÷>ã)Ê3Û¡zï—íW«“HöõÿEµZxµ<6ß)ÉÛET+I¯Ö™³Ð…z…Ù+"ÇcVle;¨�6ÙßsÀÐÙW¥Uéóå- ´æ• VGƒ e“ÍÜ¡ñÿêyò ž}¢œï å¥@ÅÆ]<ií¤a?�þ§>oÇ‘Ýô% ³NÏÀcÿ¡¦2SÚ‘‰LÞú2 èÙ¦ty üŒƒ¼ÃA6k V4fW|’ðÉ8TÜj?Ò·>‰xå¿—ì ßK÷Vº’*\­'ïž¼õ¼@’ñ×j&A ð�@h"yT©%!fžWŠ!¯Q3kµ¼¤<³J-3;fÖ5(¢Óªä&©¢ŠÒTiiZŽBšÿ�öŠÇXOêfV»ócàç$Rô‰¼WÙKªÿ½ã�¹½JÓŽ£\º\”ÅPXƇ¢°eè îSµa“•àgÑút ýõiÙŸ9 mö5LþöûÐø¥OõxÿâýéN½?=…NÃðüRðyŠßo„ðí? ¿ 2¤‘¾ãÿqÅg L¾úd¨¶L–àW€æår7 ž¤ài L @Áëðë§¼ƒG(؃µ¨hBòás]ýAä„d—Ò¬MGnû¡¿Ç쟺%ÿlJr* ç¥Z ÍeW‡m_p&©è†Í8O<Òð†lŠ3ÃÄSÎ<ÕINe�Æ�L··nß ƒlÈÁø÷yäP^žpĹ/Ú›=I,YÑ‘Ä)ÈáŽdž{:evßT€·èlöcàûýDø¾?þ¦Ÿßç'Âw öi“3‰Ýo¯qв{ã$ùÑøVã8Ž/æhf®“¾Ãs»7 3Ø‹é«\e–ƒ@hV\'µtsR™ÃÏ\ǯ­Ëð\É?ö³Å{ð âð+®ãMU<M˜}æ×'ã'ÒcìO„ýá‡ÿDøÜŸ?¤-xcÿ§<ì Z0‰¹Ô•ˆÐlY|x‡Ç%©Œ6í1´œÃeZ3@ÕîœvXª|Ýmž³&4ßÑðª´ÃMçß‘ö°´ªÈõ&«ÿ ´Hm‚Ãz½taòÁÐJPÄz‘´iaë¾JCQl{Z =vå{Ö™ ­ — )[qø^ è{Œj̺ÿ@Y½"íP¢b³Ô3 §«.¤º†«ãŽc0ä»êôkèãQPš—nÆîç:¦&îR\5Ž.cŽF6³…e ßjÌìoLû¸©‹Þ_³‡°ÄåðUbõLÿÁéXŠ>cyÃcı¼¬X_³ûbÇ`? î¡à�®F�žÐ„àt4È6Âüàhº«Aòýžô¨.mìðÉC¨Ž4å€Z×+.6JŒGcì�4Cè Ÿ¡ö!2ˆ2ŠâöÍ$¡ËŒ„/i‡£í§C¢ìm ÌÊ÷…áTXŠžá”ÐìÐnT±¼Už»A©3•¨YjÔ 3{´‰|8äøLÄ]Éšœ„�iLs4î±ô3…å˜;[24¹ˆˆTåš7´ó¯7B=Fý å5!Ù´=ZžBGvt'!‚jhu\‡B °ô°!ú¿”r@£Ô)ÎýLö5`Ò ú±Ã¶Ý)ØR×ñwx2´5ñv5óWrCsju^j Í+çOM;ì;5ÿW¡õK‡UµÄ1g³,,P,c2ÓOxšà#sÖ³®€³ä«§û©’6 YñÔd4ðöh†zÀ;}4Òc£‚ž|ž•†ÎÆ[;{¬jþI2bùO4NÐùJá–ì†"º¸Ë¾7‡¦¢²¨ú×)À—TÿÀÿ¤þS¨´º¶ë–ŠÒñÔüËÓör{›«^zÇ2R±<5(ý;ÏwÌQ¯ZÖ¢Ô¸Hõ—CõÑÕ?í×*nì?í×?–}ûË’}?¢¿Ü³¿X|¸.‡}WŒ¡ÏL†>ƒœZcI%îp6 ÖêŸÍWIµl!¿ _ÕX¶P¢vû äÙ£¹Vë£úÒ¾×´¾ôök¡¾4èÓP_z3||ÑúÙüú €ú('”½#y>5ο¬þǿ¦´*ö˜ã‹Õ/Æ›øú ðÛÂãèïfT½A³ÿRç·=ò¤Z˜±+TùñW ÓÑ$ –üïZ¤¤ä]ß ¥¨.TKq ag(ºœ¢Õò¥¯ÒcSËË1TÑš]_A@¬·"³½¬ Á®PÙç˜X탉aޡʯjó,@7ÏÌÁy‡|Z�Oâ(¹#PúJ 4œç-¿Àr}é5ÃZ™{(¿§E~× €ï5€ ·¾!7¢ñ• V.Àê­L~]+gõëö1Àž`ï °Ó:ØïVCÈ—Dh øx¹�f«9p›H¾$�z €ÆÕ‘ Ü$Èu›5fkÑ—‹ ήÖнìjò¦}E”øIGp×Àì74²4E#úº�û»�ëü¦vÝ›1Áxi`/q° 7¿I-`ií½-Àæ˜ÛÀ0–—öbbu€Î°‡cs†='€¯'`æëÛÀ~ù—vÀ‚¬ƒ ɸ¿D¶Ý·¢qÖ$D·ùtßþ½Hñ‡XàZä<¿&üœH13¸y�~Z¤|g(ú‘Á;k4†8½&f7~GëtPòW `Ó_5*ŸŽf¼÷Øg캷4°oµ¶C€U ° :˜ýívÀvkŒ'Àæ¼I–¢Þý;Ä ªù+¾$ <(RtŒ¾3í·py}àm.¯YEFlI]#%J”ÖF–/R¤Æ.Q‹î@Ѭ¢/Fˆ‡ÖšLa%r¶ßÇöZ„üŽ*±£Èr…%ºDCt1E³P/ÝðÝZ.Ïu^bGQâŠx^âõëx[2_?] åtˆÞ¹.Ñ.š,ŒI-z»(÷¹uýi]LÙzD�,�‡—èÄåç¸õ¨8•©rG ,SåK(°\•)°B•»Q`¥*ÿ†«TùF ¬Vå[(°F•Ó)°V•3(P ³i l†®A­Š§ŠÉ‰ÃùëÉßRøA ÿ‹Â“)üw “G’üo O£ðn Ϥð »)|šÂs(ÜDáy®Á°c“RÀËäï)P D%LE"øöJÞ»A)¬ñ†_h[T]eJE_¤{‘W³0ì¨V|C0Pø+7a)*Â0J̲žj%N äè¢ ³.K ª‰‡ÊàßrÆ ›©CDZV1œãÍžZª/у6|ÖL`k§àrE©RñYÏ´Š6õo«ç#(bİ,?RýämøÿRÉ7^M7æ#ªüG¡I©òînç¯<1Ò·š=÷,¬F‘´zUn‚”Mû€¡A5iÚ"¢¦-ª<ßë¹ZÛô&h<0 7½ Ì„ï@búgM@GhzÊÿÑ1D`7D'Æìƒd`þ})(ã×ßSðc Ž `7L0˜‚½AÞúQð üz3‹ðë¯)ò=p‹A_Ñ0Sðß<E¾ÇM¼îmáë 5éMþi¡Æ '˜|8 죴ö ¼AÁ1|‘‚¯CW ”óÌ0XJÁs pÅÜ€ÁB ÎÀàC¼ƒãôewÐ×ïò$Æ’¡ËÎäk?†³*ÎÆ´½›Éñlüì;IšÊ—¥÷Ó¨ÏAM`æ±÷c.õTyÔB5Zw(ÜL–ØrÊ•¦v] sÿ•¦ïú|eáqôNˆ…ÌíŒ-ü'd–¦xÓNÉ-fÉ«@|a‘Ü'y˜¨x;Gø“× › á á$áÇ—‡6!ÂfB&ä_YNÅîO†-žjþ]MÇŒós^ò«m–üâ?µ’×J^\'Jnz:‚<?ö‡·übÑ §û‚J»²GóyÇ~­™pRŽ”(yZŸÝ#X&Õœ”væ«qìr¥N®Î¸ïþ‘ö(nDà dóÜÔ)8\€K}{@ ÈËÕEÔ4nLíʦ.7êžQbÞAD m¯R§ì‹é?Þ‘¹’ÂǹìÑ8CƒBq‰¿K~²ú£¹ÁB-_Ñ×¶àõüïR­ð‘†>øäy^8úh<½ôD"±ë/lÍG×1æIÑPYpŒFrÆeÅv_Pr¸%;ƒ”¡…LÄ ­n‰©þí“u´V…µË­fž¶múhóñ´ù¸R£T$cGU“È!ªø1à²)v 8íŠjÃÀ†œùÂvcZUïAøD¬ã§‡ÆŠŽF˜I¸™×—Ÿ~‡ü> ™K‘óåC¯„ú²®º¥2ö…g?Ôú·¯„áñ÷qä T<·¦I¾7av¢„˜Še¨–÷…²‰LUîÔe£•¾U-¾PDDø,¡†¾Ž†¯&ø ááV[ÏBy„:©{ èÙØ~ÕøRõzßtŽñlCoÄyc À(OÒÿfø(פ:˜4mj§f8X`âÞ%/ /²œ-Bù ž]`æ+ÇXS±6JÖ¡&ÿɰÑD¿Ÿke*"Æ|ˆ²2¼¬5H( =Ù‡qV¶k’¾#Üã±Ry;Y™J-¦©Â•Øb{ Ê¾¡(¢7RÉ>,¹&«¹…YÆÊ² ^úúVF%0"˜’µ®µió!KÉ ñ aš4ÍòÛUmµ÷Ëÿ@~(ª m3Ý8– }h?d¯¯ xÚ)Ÿu³{¶$˺ÜBJŠårM+}¹5Hp<y­?ý|òoŒ AMè¯è¨@ÌË*¨à,]C÷wù;4ßJÀ+ Ý“ú×´RÎ…´2 3Ä wÙ­¯[âÞg¸÷ÿõPçSD‹äõ€˜òßPµá2Œ7 J€ ›Þ:ÉZë@Þ›–�À©#hcS|ÝE-pä9½“1¹›K³õÔ‘ìlcwþšá¯¶Õ W×c3\ýjx3ܨ#”!ÿ~›Ý÷ ïÞÉ»^T39ûT û”f�ƽYGå]× “²ãŽ^ÛÈ</ìX^ü»±Ë‰åŒ,w`ƒ"‹ùQ·/P²@xõ)W7rB/m âlåçÚ¨Ûo±¬{"Ë*Å~ùNŒ~y”˜$òìÒþ}1Ï1‘y–EÐK!zaGüûK–ÿ"v†žyuìHÉ»’ æòñ[",Ç UøhVa|¸hÙhèÜîgZ+Ïq·û"ñ_A$¿2ú@++Ÿ„9ò^4º4º Fÿ»ëÚªÀ2“"‹\EúB$ûdÜ®¶adˆÅÜ 5ÙXÁeã¢ÖÀ¿ÎêõAOŸíœ6ÁIÖF.è ÃA{\ðÞ\›”¼A„ÿÉ:²[·ø'Ô@¯x@ }OHûþ.¨~_[µBñÑÿâAD1œ™ÐïwÐepHÞR–õ]‹¨}3Ñ·u­(¹[Ï TªÞç¯hSôÞP‡}þ®U}~ÁYÆzi˜fÒ¹Ô×L4Õ{uÉ×·¦>þ¬ƒœ ·¶E°¬ôÚÔ`êɳº,†!.rÈãC˪kAœkÁPî+ÇÎja¦âÕ6”ñáßVª¨„9¦5©ô=k¡Ŷšx·:úÚDÉ·êºèú£N&VÄü3zŠZŒþH¡å‚ÜyLbåOjTÃ-:œ£ ܃†û·†»oE³¤Ù9ª¼^#a.LFÍu¨%j“±[(nì8h[ÉŸ€yå.¹+šÔÍ4FÒÞð»˜L:™LÅŸ— <Ãð»­çÎvÒ,é>¾«ò#!ÜÙ"VÑ­6 –#űá,÷rm¥_ÔVô )þ Å J¡†”et-t‹–¯ Ä ePX2x{+}†¾ll A·Ð">%§F.û¬ZËþéTL”ýa(£o;G90$3}sXz{(}RìôÇÃÒÛBéýb¦?–>1”~sìô-aé“BéKc§o Ko ¥;}CXzS(}wH¶_[u4sÛçÚ}Úl'ôZÝ…I’Õßë{ÐèhƒvÌýÞ:|iðßœÐüÀÄ6ï²ø“>¹ ø·þ¤†BV[å[Ú€Ÿð9š/ _¹·úü'‘ðòñä¨ù§UZŠ)8:ªá$ÙÕÈœÉ0eŽî¾*Ïíª<çÙ»*Oy0ÿlÆùëOpG”|Q<vô¹pZ™³‘¹üHÌPú§¿kc¿eTýjc×oÄwÔ>¥» øK9|Úa-Sôz´ê#Ë*­‰!¯¢šók±L)– `îý!”¡zÑc*Ó¡á†x:Ï:�I´¬ ·Òùú’yÕ=oNÊøZsone—úöº“p5ßNj¼uUô) ãCrä`Ïa±ºÿÕ8»:ø4¿P‹¢\dÝÅ`+%t…ÄȯaSØQ\æ“{L-Í„¨É`’ÂÖ‡2?„ÙHÓ²èö“¶ïÔ8ê ª£N¸î®!t *»*´™P³/4Z¿ Ý6Ó$α•Æ*ÇfæÚfD3iŽv 8\õt ÄcH¹\ëj³û:ôwpUåzç˦?ím$Ϧ8ªòaæªfqH4 2\H%Cdoæ¨Æœóì’ÈóL!óm~Xtú¸Cÿh]ªÖÇ[ƒÁ¦ ª¼2 D·§o¬tS™*×ÇLûLTÚÀØ`¤>FíaîŒþ€va²Ô8. =íÒŽhÿ@ˆ¯q´ðl[‰îÐYâG Ë´(ŽVWø~N»–$Hù…â!‹Qðm—÷‰£%Òß1,?tx‹ðç6ÄKýáì{AŠÜËÿqáñ±1ìÛh÷ª'»—9•ŠZÔÔýô%‰±AÕºQÙ_z¢jБÓtsÇ%‡N£Ts³ |‡&ôá/?íUžŒîÜjÍ,Ò«<Âz^AÖóŠês\ü`VL­>§­ Ü, EXcѶ�ç)þÆÝþ™ i¯¾ïÚMj)Zõ„ŸáÆÝÔ%þu»5ô>Û–qÿønÍJ7la$JÞEùÛxkÇp»þ ÝQþ޶½´þš¿cwi“#•ïK&/߬Tró¥cT"·x„l îϽ##ýÁ¸*¯6nÓ>PÌeóvÝúÜS ôÇšìQ-$ýþ½ËD{”%ïæë;5ÿ2”GN³<£yö}Çů[luODORrhmÄÖ/ÙE«[ÂÓ“s)À+¶TÿNÊCkUÂâæþ;ú(›Ñ�áhI’܆ y¸ü ‰ºâ¼ŽÉ¯‰y“j¦&îæA‡M ÆÐözF;[ ZÎeE^»b3hc™š¿\è| UÎ( 3??wõ0ôyo#}Zxúcz쨛œIŠ“³Á°èý‘þט/™[õ½ðþD¾g„67±W4r<ù²Isr´q¾N ÿÙÅu WpÐ8ÁÖ–ò’7·ú_>/ltòœ—EyŽcÚXõú v$u7ÌoÐÝ (ô}…‘Þ}d_3l;$³ùá†;ª*ä†ÛQsÃuG»á0ºáö15©Æú´³GH7b£Ê%ó5žxi~h(Jؤ»†#Úç¡G±aKáµ—V&£ÖVèúsÜÄ\}Çrŵ‚¹ú2g?ÅU¦8—1Gse)ŽRŵDÚä^ê*aŽTry²pn‡.‘ªˆƒ™i@¡E=o6êhŽ54 ZýSè×ÚK*¸æ¿röÈJ¹* |愲¶2(ÝQ¦%1g*µl¢î©^­xêv~§Î‡†­S«kµ|ÐIVH‰�N‰�®­qìç©“c¤NÖSÓdαäR sU–fæ"3?´U*ƒú"ñ¿\•Jf?V¾ P•lP?%×R3( 3ÁíÜÚ%úqú{jÉ otfFMæ‘Æ5Þ/%Gë”,¥ gŽÕ¸ÆQMxïWõiÁ¦äðñ3GvÔ[ú¯–¼/bÇNÜF]µ QÕ9k™å s®R²ºÕ*Y.´oµødé ™1¶RÄ�ôˆ¾Þ"§ÿÒó~Ê‚íÄ5úev¬"²A÷Ã9"íÝÈÕ GëºD ~À\«”8;ßDTs‹«a€¾*rϯ&s:om.¾§Öw**u-O•š™z?OlLM›Á¥Dá>$´™c€&£Ÿ%âcke±Ì~4Â9«ÈéX-WÅi#.v›i#Âä{ÞEoX&¶ÈTtŸï”§'»¿2ís¬Aà{XÆZLr‚ÊQåÄÂjìb®PÉ2içÇï#°A´ãaï{ÔÜÁ´ÓN5½EÑ}ùH¼•¤Ÿk5ÖYCǶ+ þNÃÐiŨ |õã^r}iÄm1tÖïMb—³éAþYÍc̹YL™üããƒ$7g«ò(}¡¶n^ØsÏ:ÃNð%‹…Z§)pp‹àö! \Õáä¶sFrù0rt‹Q€€°‚IŠs¹â¬ªqTsµv5ÕQG;µ{yà}R×(®£Ì¹Ô€ú �j;2 _m?J㆓f„‚“Ÿ,A!ˆýmëouB_eϼ«k§iÁR¿0vwÿ½g Úê-ʶúsœ&°¹@¼~W¨½ þR¦’îmµ÷]¡»n Ü·^ê™`Ôâ–½«)‡óK#”C)‡¾ýB ĬéÒ\w-ž£)Šu"Å«oCk¡‡•ÿ7ï†ùWhúÛ81È\|î¨óUyºi3Lo¢°ïÝËp¹½Ý×&”êcœp˜ëùwpë{Y çG-òŠË¸÷[ax/‘ \‹ì.ÀEß"RäÅ×"Óø£Àç‹¿Ž®Evàk¢À‹ßt®EêÊÁ·F?%Rl‰®EþI€×E/)Ç×"§ ðÆ(ðgEŠ»ck‘· ð3Qàω¿Š®Eš¸M‰A¤øÚ\‹|ßÎÁS¢À_)6Å×"ÿ Ào‹Y¤X \‹ÌàwEß.Rü5¸!z©û fÁÈ«²{1ç~rkeÓèG-öîpgCÒÂ1ƒb 9Þ!r _-²›À÷±(|Š'¤˜øjÑG¤¾ŒcÊÚÄq`$Ž™"5F!zäL‰ã(Gá˜-R Ž£úcpÌŽÄq˜ÈåÓKcà¨Eî¼”ãølŽ#DŠe1À ÑžKŽ#"q¼[äÒ;ŽZäeÇ×¢pÌ)¾¸$&ŽZôžK~Žy‘8Þ+rY£=rÒ%ÇMQ8> RÜG-úŠƒã‘8NÔä¯-ŽZd¥ãX…cHQÜ=Åö#p,ˆÄñamü‰…£ÙAàøaŽÓEŠ£]bâ¨E¿ÛåGà8=ÇB‘ˬ…è‘y]8Ž_Eá8[¤èG-ºóÁqv$Ž‹\ª;ÇÀQ‹|½3ÇqT‘¦†åaˆÔòˆÆÎû'à“ZÞ¢ˆDb³Ùý\ü¶’Ëxë ÈSÙõ)1F È›ŸZ™yÑg òÙ(öp *¢ÌÑr”€|S@Έ,3;2îé3¤YæÀhÈ‚|V@Þ. ߊ,sz4dg‚Ü [ņ©÷#Ëœ y)A»äžâ&jX3ÓX¥W4üa¢ÓeL¨�þF¦qE†Ü3ø*€ÜQ-¿A\U4 Ûð 3ñ-ˆå¨AêÌÈáÿIðSYø>±'Dá|Sàüèb?%°gؾÁjÛ¼$ÀùmçÀŸð{ø±;«ŽE:ä—ù€# ý:©ÎëÅÞ ü KK8ðUÌ´깤ȯ ²Ÿ€làû˜ØH¡‰-ÀߣÃ/ކ¿Œ8dœ€_É·1‰æaÓ(›JªsùSÑ-t9 àIx3– xm›ÀWðÓøz¾¿ MÎ4C˜¥Ñ0¿"˜×Ì ¾Š×mýšÀÞ`ï‰M–h­ÊkHš|y®'È#r¡ÅPC¦ÕíÙè{Øl˜(ð¼V £’·ÇsÑö"È8•CÚcø\t·Øeì#±ƒª»Qà ÑÞN7 È—âcøBtwØ@6EèŠ,ðÅèä½²WÌ_Œ.0“À¦ °Óq¼@od/G˜MЀܫÀ—£ F`+ØbQàFQ º¡ç"!»Â…=ßt÷ç8<T�ÿM5Ê®ìh° ¬F€]&À‡åEƒ½L`õìc3k ö}.ÀV °a`Dƒ½I`'Ø£ìÕ"#XA4Ø[œ³—ŠñO€]²Ô6=l=I MOvuØìh°Mv�«`=—†é\dë«4óiàR“ØÜŸÏC!ïAÚÝê¨'>0‡Vk`þÊÏÿÍ¿Ñi•3c½bt!ÅÅHðƒÅZü×} ‚Tz ä”qâ“i;Úœ|Uî;¹Éñ×e&SÓ5aëjù|™*_xš—Ìä77›Ðß�r™þZ•fæªóö\bpßPšåêq†“iÅþ¹ò§—âÞÝŸOËK¸nô2]7nA»”’Œ¥TÅÎï%È/p`Ëk1˜KÁÍtPð?¼ƒ‚a°7‡Ce©4cðJ öÃà¥LÄ`<k0Ø‚[…ªÈÉ òÄ/_Rð ~LÁͬ£àkü½ÜBÁ™|›‚0¸Š‚Ë0ø öÀàR²•Ö õó,ÐÀ™Â<Iè¦ä´¡7“v¢Å%¯b›EÚ™#‘ÍÌU‹Y‚nbÔ“7˜Í<É< Ùµ$d_Ä-"äæƒí‚;­Ðª{÷pˆ*aëÌŠYŽ~àÒ¨ÉJÊ¡ðÞƒy…»ï€«’!E _kÀÕáDø™ÄÒÕ(¡ëLn/1…ŽÂÇËQèÜy +“†U.sÖûöz¬ü0’ÜDËhü6l•©MW¡¼yœßÖ¼B(°LÞQbr?ÇBö`̤؆½ïH˜žýb=2"ÿ;Âòÿú žÿðWðÀ¤qbݸ Þ®øÙ^çM–]l‡CÎqû¥MÐÄ¡ÉWú Ü¿Öì­rÿž9v6Ä¡=ßµ„Åkïâ‚A#£3wó~nTô­[´î°üe.IÂêò_Ž<Ÿˆ±ý²ÉÄs}ð³P®ž7Û9??ê|W~ÿ úU4Òq\ ¸À^êü—/èžO—\ÈîЇzu ß2†í©“ÚH}ÏîæK¡³þq¯Ò‚â™$ïW&¾k h×ô*ÏOìp‰q\»à7(/¥òú`ys"Ëóêå½…ùlçånG¦÷‰àSÞ½’æaI "K*ÕKúƒ‰ïû ’ð¸J¬¦³Q1Þ#?&Ï‹^"€Ý¿\ÎÁžÁ”ÎÆRs „‡ �|ž¸É¢_›$€H¯7"²Œ‹q~6"F¸ô™e´‘Ùõˆhi$¢´á•Q–Àqq_Ê6²¸Ž®�‰Äg9—€%ÖøÜ6á…²HVðú|r??/!i¾§v)Ê䑆 ܉$ªŸ]ýç¬mds¹–Í£Â5¤ä‰Vž!-XUÏÕFr‘|Õ‹XÉŠÄòo#ž5*õÍÛéŸÎˆþYœäÛë…ä¨^ È¡Ýv€Þ'ÊiÓ!Å•„î®DlªiøÑ|ßž‹öÿoCë‹1ÎG‹y^4HÃDt!°ùëq´+­N>©M^û=iPòø¥\|IÞWDù-~帇Â{Ø3 -d|\²iÚðe¼(㑱~Ú�`+o}Ò¾iBÜ¢÷_³>Œ¯3cæw® 0V>xÒz²ùO ]­6ìÏûþÉ8ž¤„'+<|<Ùº –ŠõYòoÙj ù·Xß\™åCJÅäsb7­ì4h½b­–ÙÙeJÅ4Ü–X@¸¥ªÆ·âœ¶«t¹Z&Blæ¡l›I)Ëôø%Z¼ ›3À”¼2NMr-;?Óm£-¯+hÄ ±±Ð øÏ j*e1*n9Á-·áhÇ!˶Dï±EìÙl Wþ-ªæ¾:âóDH}ïyä–m¸-$üüN¾ÌLÅ´,ÑŠ ¸Ø*°/³ÐÍh—JÞƒx®âÙ_J%O™1Ë󸻥ì[ ÆhèhšÞÞÅdº <ó:›Lg;™Lsà9º—žM¦™ð >¿áoL¦?ÃÓžSàíõ¡p„\_» Þ –Ç·Ÿ…Æmé)yOumVEgR³Ó„åÝ/ã:½V_«¨¯Å \1E|Ë2[ÓûH‹_"ÌQ¶Í.P±ØŸÈC0¡K`v: ÎT~o¾Ö_; /¦Nã«þ¼×ê' ©g£›ï£ÞƶQB<â4w¤Æ”©!¡2 h{b–þ˜E*ˆ#Ð6B4'Cné*y¯¶á½U¿”¼©°HÞßÙ´¶\0?×$ù^—ïɳ‘ÕHäÓGëÍëFÁˆú$‚Hp“žŒ·CJ¾aqZ?çdüRZ|Ølô‡Õù eR6àÝä¨Ûðv†`Ú)•5¡•vˆaÿ–$\&•lBHK‚Ø>fYÄÏî~:�>·/¼&¿½ÈótŒáµÆK`8¿Êû³%À �¬Ï»õ£ŠÜƒ tK‚ÚîÌ[õúÄKµúæÈ{,¼Îé[fß ôjýÀ¡°c<OÁ'À5þÙ’„¢ƒaOFæXô#1äÃËxH ±Š²s¡Ô:cùw«·„_qã­%_ZöýPíi¿òó ®sÿS-ÆÊ¬x‰Æ¯Zq*Ì+uÐÎQG£ØÈàKM¦5ðƒß~È3P¤=½åƒKøŸ+áñ—=Û9qüÓ±!Þ*y 0 <Fô$4yŸík am ï5×<ï„ÀžV„÷^‹…wDÿ¤rxã=\ÑÁdMºUZtd—|Æ"-v'è ¥)}”9–À,·‚"Hî°K™|«ÖÚÛxÒ;8&ú ˜"}ß;”]‚ ›×P%Ì¡ÌÔ•œ)ùäSf=Ç~|8…¬9W¥‹ºjìêÿðqý¹šô I´Hâ$,# žÔ;DDm‘:³Šå¡Š&ˆlðyÛ²�6ÁSÖ¬«î¢©Ó-¬_=K{ã8‚ŠåŠÀù³?ƒÄÿ NF`ðÄÂÀþ¿€AsgÎü lÿ À èGa`ù_À 1ƒÿ¶ü hO¨¥ëOÂÀÁ¬H 6r¸>ˆÁ-’Cú®KølÄ Ôë~  x4ŽÆ@ŸÿzÒì¢%?\4¡rj,}¸æºÜø#\vÙ§Û¥¯ͬáÕƒK JÎhò(\úô(Ã÷À@ZdᲨO¤H?«^Lnaªê§Ib½ªÄìÉbÃX3ñ Aü±=ˆ±øöR™ þ£OC^ýP üö”ñ\UçĹ €Ôíe¤ãÑUÞ=Õ¤[í2P'÷¬B*ÚÊpæówŒ¤¡^ÙˆÃ|àXȬ§5‹N`h$ž ‡ö |ò_ \/®?–ÅæÚ”msˆÌ4#š;\ó‡¥áÁ7G´‹¹D¿¹EWAü7=bM@·úLÞ¥Õêú,Œ\1×y>NZþË+òªN ¡‰fTtOõh#k¼xjº‘ßè !l Gé÷ïÕ_þ¯– ìÖI1|ós±3³Â¬×0k*;!0!TŸ*йDÆ…sÛG07 %jó·,m‚ê)ÍØ¨´ŽïO†—¦«”í•úé·Tê;'Ã)¢ËˆˆqýÉ}”¼ôdL깎õòúC2ùx Ÿ3bºÀß#v‚€Ì$W§£”b÷÷!:ÙšŽquDJÇÉ`MB *PìÊSgr…t"7k¨û÷ <}‚Otƒ6Ͱ芀%Àû§q8ר+H ª$EHôÄsIÅEOßæ¬»ù,ž&q¹èés– ±è‹?GGòÝ«õ5U¾šúõ¸APWÂ’¨óÞç¹£%»(WÅ›3õµmÿ-Nœ0Å\œà‹?hE¢ÆQoâV³[c­'ˆ…樗«‡–ŸÄ|^g3ùÅGE寱Ê6/ÉBdB»`ºÖ±_xTTG»W,øÑ‘ˆ sÖë{«É[ÊQCžŸbF«ÎÐ0«Î哹ã˧Љ¿ž›ÍtôÛÞo[vü¹qý‰.%i¥MèIJs´*™ý>Ìacý¾¼|?㦳¶ßWzGÏ£,ü¯û!Ä-ŸvU0ÑÀ"Iâþ:šï^®Ê‡GÒvÈHjªå &jk±k'FXUɵmîÕv0FdÑ1ù1sدçpNä ]s×W[áßíûØ}SŒ…¶‰ýB'è‹ ­Ý— þË﬈›±ñ¨üc| n{³)ã ØáìOÆjïèû_TùÓ1a¤Ù¥Èw=Bûó®¦:ó¹ÇF¤öUy Îçî7ÑUõb'bøþJÜ´PÞü0mܸ’™õZtL€Œøv³Öÿ“űúW°ìѺ–ßjBv(ï&í*“擸¥Ë¡à¾>ícãù‰yY,ñ}ÝKÜ¥b/ú‚eg!ø:ÏÎÀàk¢Rð<ý‡Á%¸üè±ÊÕÉØ‡Å%áòjáq¼‡Ô`/¥ƒv}—]© ÞìLTT:•:�?ª·8E9[Z·¦3V³J2ó-˧ºVrƒþ$ªñ£$%_ΆE*F™ê›Èøí3^í@Êo›ÄA¿HBmgÒG©O_qN™õ­¸´KÄŽÖbUBk¿Üd…ð÷ ±Ì±R”Ë<Þ_Æá\¼8=@ͦmÈS“¶M�ÌáÅo.Cºq˜eH4¤fÐpêðý&CÂ,åLé°Yf+k–kUr-ŠJ÷’–â-‰5ŽRÊŽ%üµŒT¹/ ò ÷KâÞ$j!ĤË|¬äP,×p`ƒÆ1gii"hoôsf†¼ÇïYÆŠ—ÅÏÀ2ÆIÛI9·Fe·•ïŽ4@ÀÌ!̹ Fh²âÉXrK"_H”[~!y?5iµC‹0¦4£é7NòŽ6‡pÉ«€“–™w±8Œ½äYÞÚS*;–èö¦»J%ïý šËUJÒGgÎ.¡}†Ž2•¡ì㕭àö‚’=0!_˜ʾ,l³¥’�GE'÷F¬ÿ:ʸv)ð¼ÍÂ\m¬í/™§·=F‰f2)£Í6倆ÿ�L $Øn±k«n¡¬“ù<åyí¾EÊæ rä¡4¨[c‰³sÖÁš„×÷ €/؆}ÕäîÈœK|{=t`$Ú4\ä&^©É í©Ò$bFÈZ¥ÄW‘—ä»»ê4­™ôFð<T"Š|o¦U`â¶)›°‚Žê†k'p: m#<«3—B[1ð­˜^mZd½ìv‰«$ª|çaA×ìÞ×ùÇ,G'K÷Nbc`ìÍc¿­1ök¨ˆäl—[’¤Å±Ž®å&<?F¦®ømV,¥ñR~ûÁ˜Sµ ‹t¡VçÏpõU•OgE¢{ã\JB®ØÂå$`ñPcÉ7ê±°å57Õr¦ÒX{A©)$ˆ&ë=P‡,íäªø0òÕÊU©äOu‰Fà u„/hèÝoŠ]¡„3F¤Ùèu´ad8}ÐQJîòÇ@¦@=~¡!$¼âÏÎ#3!íÓÕ})´„»¶‹ïçcÃŒ@áGß³ÏÓÑáj•Øàˆ¬Ä¡ÙaHþ™Î)#úÄ)úÔA»Xâ„ñ&'7÷vH= Rû¯‘À¼ò¹•»t )ð …çò&SOñƧ „w ýnëyßæ­ˆôOw4 ” –…5,ðc7ª€}22Ú½92ú³óÆèºÈè]aÑՑѯ„E‹Œö†Eû#£óâ×DFg‡E¯ŒŒ¾á|ÔyòñiB“vÔ+*Î*:ak+®òº63Öy~h‰×U9±Bï ø6?ØQj̳à¢Öƒ&ÉK35O_Qíi2®¨:*U¼g¯R‰Ãóº3-jÎXTM\•ò.¤GϤ¬Ð®iàÛ«¡Ú_wϱ<ÝÂE'Sˆ-ÌtZºábäYÒ]ûåq‰RÉgx³ÙZfYÈ×\v ºò»Þ‚¿ýg:¤ÅûŸ@Ø8þ~‚'˜$‡èrBès¿µüŒøºç½i§Bë{¡½ø›™e=æ öчÏ1뱃ù½(JëiŽÉ½SµzÖ›!ëlæªJ/´­ç­ç©¥…P 2MWô´ä˜±revšrò»¼Ä ¬}½{€!ÉÝY0ñ@4¾óO<ÁÏ/ü’N�¨Ò|ç$j!úo fr4Þš ùÞë`X2sTãÎ}÷"ÝéVœ°šŠW>‹‡´ºo]Œã{ˆ^AGµìØoažjæ¨JŸi‘ßi&^ Ä‘$Õ !;™–ÉC[0“¦Ýxž–¢Ÿ AbL¶`3%[G@8 Yix. IÕÀ›„Ù±€üF¯³}/ÙãÄ}ÕÐþëlºž"„ T˹Ÿ Ò˜é8žéohQÍ<ÛRfïÆë*@B·”3¡¹ÏâÌ1Êâó < ޹ÎW�„ÔµG•¼‹ƒý¶yr8 ÷s’Ð*bcž­Š§®¿]*i ÷¦JîÜ^GÎí9“1'5‡FÆ£·ØGc*úŽBƉÒâ£~Î{õ?P„AÉ£Åì<§þTÓ/¤nÀÊÎ*\Š_¼Sä ‘¬÷|dÌZo†8Žÿ-EÁ`S“¯šojÓßóú<áÿ27œg½®ó¬2‘9·¢>ù¤ üö¼ÁàJ`̸W‡Æ>ŽÄÍÂÐj%T‡ÃÈ÷°¾¾»2í ãS'x>„ Ý#ðÆo?äqŠ´ká] °%ðLKø]tÇ7¼‹ž2 Q¬Uwñ…¨ê:¶"þïÌêNA×d£ëŒ£.`£ë­Ãi{km÷å´}³h:Ç;žFïª  æ­ ã·ÃEÄoçˆXIr?hãbb¬:fìðW{·rw¾¼!øáI±óû é™áy\AyÔÁ¬7ôíûÙ„@Ì=o°�G@¤bÜs>: GËã¶¥^;[ƒ½"F¬ì¦z³>‘™Þ;Ûà®}ôâä" éáV`Ø–¾ˆÌ³þ¹õ‡6Ù_g£K{Œ6zq– ~`4dæ:;Öýaö[îŽ~zÕôÑ‘.èøÞ¥|èù}ãh¤¨qãÃxgÆÄS³ä£ó«.׎¡Íj[p yz6øöº/‹>–² ö›Ð=ˆ_féoV¹Ù•<Ó-¨ü$¦¢ Ë•èÿ®a¼ÜrÏß3<‰;̦¨úÇ0!+-Þ*·Á#=Ì +•GÙ¤[kU~ß(zÛ²ðã ºp \Q…†ìz¼w’¼ÿ!Ý$IÖbÃìÕ\¹+WóÿÜç1iþŸn4Å ·>ž¦É¿Æà?/ bú§¿æä 4 ò XtÿôîÜßι³qGKÍPŽUÌëÉovÖïÃ÷[£¹ô*v—šô5ñX~AÍ`\j½Çê;ì¹”®w\ƒÊÓ0µÐÒÔ—e·JïdX†°_ ëT—¡Ä³¼Öø8xñÏéƒ-Ò`ø§ ¶°yj]]™²Ü㇉7zÿlkzYzgp(Q›Êbß¿›¬_؃.¢ÎDv’šÜ‡zn‹yvgÜ C¶ÃÝÊÊx+®äX”‘¶šœD"kšï”{$+²²ñßÇ©éf‘ÿ]îëÐ?)°;áÿäaØNSO=Ïš®1ëÊÔî85õÿ•§­é@È~Þ¡ÑlàWkú¡¢ŽñC,éû ;² «¯Ês<ú|ÊèóäœÍi§ðV¹¡£»“²çô§ò—•ïT[gæjîqB™¤Œ±Al­Yd©ÉêjéNüÑ×%fØ•œä(Çü= öc¹v4nñI½þ[2ØðEÀ ž›øç …^i‡å–[ ýÀ¿õf¼ÈG»¯^ôí`⢦Z¿ºGÔGÝ0y4šz‘.M«2àVV“w_k²À)¦šìÄŽX‘÷’#\ê[ûv „ÐÁøå•ÇH \ƒc•l‹öóÏa2Ï8Ðj”<+°‡o—§3ü »Äœ‰fqÌl,üiBD•çÁ—ÈëØµÒ±Ä¼DþU)ÃdM%†ù‚(ë6,zWÓó1ë‹~XÊ.U^r¤üú<±ÈU,צZ.OovÛÅÚ˜ïLÂצ9“¿ ¿#wmD̙NJn± tÎU÷à¬f-›cÁe{<ñUe·à7gå©…ˆxœä£¯Ù-¨ÀÝm‡A"½NZ¤"+;ª”,ò:Nò¢¤R#ÕªoUÆo]mö<IJ[VÈGªL=éžê¢1Š§ÊŒÓÖÊþ…‰’÷¿ÐVÊÎxùL7©dyV6Û–~BòÞ€^™0ï^›š~ÈÝ)ÝU]teúwÌÆ®Ú ¨~ý‹«Šûóg�¡v¢…¤œ*ત}¤dƯTK´¶yy7ÁÝÔÍ rõ4>½É±¬6»7Au|KI›¬LŸ›$yg!Õ9ýq@÷MôömhU›CHCÚ++"¤ãq?8ü -y=ü'˱ÕJp]j`Mo–JîÂÙ>U‚96#E[¡+âëL )²0 ¬LÏKô\£Œ°6=!7\�i$Wñ°béI"‹çrp§ßŠ#íä]Šô[‹(¿ñ ZlNrMɳš î:‡Ç«Y|u’­/vΆùTŠ’”'mÊ“Ö2â²7« ‰˜« \àžéD!Õ½/•ÉH|*ìÈç˜éÍÐøêú¬4cÎó­Ê›’cUŠlM« Z̵UuÛí=v*'@~ÛŠ¬îaPhuk°ç,`·§í•ƒÝÜ ‡¾]we3PÅ# ÊÎ&›ÖŸÌ{•@0@»ôwl•¼‡: XÝéï¦Î[lg££T>F ó š´àqw‹íÊcIÊP›2Ô¸šâwh2“¯„wt3ñú¬_„ƒ »p™ÄÆØÒÏJÞ—Ñ{6ÇŠ ” ™.Àc9smJ&ú‹zoƤ ==•ªw1’>½Ÿäý€¶§T¦ä£ yú\;¨ô0!PF%âô©…49hoˆOÃì†%Ï@Ÿ—¨„_á§L»’ _içêyâëôˆ4Sd"DÖ‘ÖZÅîN¢ó«Ø([ú>É{–>Ȫ8V¦;ª¥’ºV _æXEÇg¡Ú9"™e ©[Šsezšä½ëé\¥!¾¼}¬ñàwÇ*%—pŸGÛV*ÓGA†!&3‰¯óIéƒàëøVýlB¨�ÁÈÌVý[ðs¶búCŒŒS‡Àu­‘ëÒ¶:uXGd¿Ü`VäBzµò]Ë•3G>tEJ8¶ÆgZ±šÐâÕ]ò>œ’(s ;çÆ©¿­7'²a‰ Ç\^žš™jŒñœdÃìØÆ_Çãg;³7}¥�I¾ âdÜy<Rum¶%p}«¦ÖƒÇ²)£`:^…LGCn¶%ý1(3-$úI–/ÃÚ%¡pV08Æ&Žÿ”Ïv—J¦ò”lÇqlŸâ£(¯W“5íWj+Ii�Õ¦é6æ©7ÏWöxg ÍÏP™É8Šç 7 p!@yTñ4ÂÈ¹Ž²;Ø0‹â¬g…VÅÓvXiAÝ¡£²ëô?åÏ;ª}ï€n¶:γD`š˜Àœõj–t4}Wá›È$3¡Fv”öyÄy$¯A FX¥4>ÊÅã"Ã8ÿ¹ç²ìdä†Ê…؆’÷ ê,«4¹’§Ï¶»”Á\E]Æ…¹åBtz£Â×a‚å.Óàg¸.QwFã8æªWZüO´ð9÷ßÈüT4m/q.T¯ëF€äCQ’Æ{uœ÷l¹BÔ)#’Añ ±šÍÅ* }«žB©c,JåÓX7ÕK/%;I™mSf[3±HP²‰G"툯0ä?wGÎ{MZ…‚Pá÷&Õ¡’m¥~œV#ÎÀÚ+{Ôâ®Ð€ªkUS‡Cù«”Ašu’w'9JÍá%vŹŸ.AÁù!š«@vƒRþЂ³\ˆ¯a¿%Ï2j5ì_ 1":€]›i¡MÓúˆCö�™2°ìx¹%^ò @7RѪ̴ÊY.€Ò¬„Jß[óÎp©�cŽ*±@ú.©ÄÚBÒEYvsNk2ior`ÂŒITsˆ®û°v…‰ ¿KÀÿœžøèYh‡¯:=…@û´NÏžÈ|(4ÿú¶ù·+ָù°{*±çOƒ”š  q/p¶ÖOÅ;jq€~µ…Zª&#µ×Àsø3ÇJ‡ÚÚa)éyš×ò×ðVfÚ”"«’mUæÛj²yo#)n4·FׄŽf9ƒ5Ï®f“ˆÂõŒ�C‹Ä Þƒxk£[Ù({Às†¿{`B•Ъ¿ãC¥6íðéÓŠcÅémÐNk•€³ÿ÷ßCÚ= ¦$¥’ ¬)=Ï£R˜AXÒÏ>ÉîÆ9àw7:kU 4h­¿ëit/`ŵ(‚èîGml˜GWöXD²j˜­ÇN´¡‚iö_ åÎi’TÈÓ1v5‡ª„··J‰ŽU,?y{žÁmöÀ-g‘ä$>;¶ÿ;ÂþAwdÖ+Î:ù½o0 9‹ÃódÇ”¨AÕ§6×v=Ka\²—1ê’©ù\—ü‹‰ŽÛr—0ƒšÐ÷å„!|Þ£ÊÔ±?|¿Åãä »ÝB²ó'.äKË˪€Ìz¼k?ËZ„ó¿d7åû’Pœ4{«<ï6]¢Ù¾óîõ|ªPm}‚Qû3æ[Ï TrZÍäÎ:/5IÔ‰ÎØµòù?LÏȵnªªGk‹fƒÉEß5GC†{03J !¨–cý8Žîþ#ß‘Y¬@Ñ9»õü Ú£HÑÿN¢,Ãý²‡£9qáJno5—c›ÈËËN µÆôI8y!;÷^ÇËm‰¸ïSò«Œ¹ ‘ç›ñþIzóÑffQ]܉kKõ?4ÉdІé_y,ľŸÏãço¸Lw”9lâ–¨¶³^~où``5É[Fí¿ÆAcÍíâ³ûf ?bá°)�ä:Š;h!'Æ [io¥ Mê†5ƒ¹Àß&"‰TcV�F ™�ö›Î“kÞú‹þo-±îkÅC M’¸?ÂQîpLã¾%.‡uL¤i_KÐSéÿçÐTú™¡W!ýûé×%žk‚ö-Í ­jÆÍɨX£Yi º ðÒsiu¡~f/ágò¯Ðç¦hZ]x†¥’¾¸Zq¡³Tòž™HÆq—ÖLdâaïøYå»#_+x Ý©]ñ’åfþEþܬ|`ÞÃ}8ÔÑߥâò8¿Í=à ²ØûKT¶a,iÞßÙ GH‚Óòÿé@ÅŒg*]q7Ê"·ZÌÊy5¼¿Ójþ©¢ ÿAEã”û!¬áoETšéãÍ f¥ya5’J˜$3Lþ¿ÁY.U§bZë.Ÿ†Üz8‚¡)/zn`ôJÀs`²ËBší¿nÒ¼\Ø(+{Cºkµ´øEÚ÷JÙ¶þø‚ë¯7:V+Ù6Ò¬xµj»C‘ÄíHòÁxåq‹Zh ¯¤÷ê`è¼ÝNrõƒâ²òp}[ÞÓEþ²Eñ¬Všwúã•=AÂä·/КmQ<ÇPkžŠêSñêÓÿbŽÕ Ä ß2”’h²Œ¹˜w)aÔ°5Ž£"‘÷`™ªŠ—usw‚ćüë¯ì±‹‡Ž| sÓ lµkñÑÇr’ëÜÜÔ#B¾¦×¸] Ï0KÜ¢m>̨$ÅQ_Ù‹üUëÅ«Ó/ÌÆë—½Ïáªô2çr(´ð'^è¡Ý ‚e-´ƒ�ñ=¯³âz×´2ì~ïC7:WóFgE‰òIZœNêÜjÐЕ1‰ÊH+Ln y@²u–«ãñõ< ðe`¦îuÂr­ÐÞ³×sdéà6ôÖ#×ÔqÉ §Á\…óN»Ž¼Š ¯«àÈž>zm´µ™ç¥oN6R\  ¥ï”¼¿B3?=úÍ» +˜±íü2þFhHÇj¹6}ŸY”}ñ€]³À·ï-¢b98¤ c#î'æuÞÁT¥¸‘ÉØÈÒä+_‹äs@öZúŒsyÀ8•¹šéa¤M¸Ú̄ΛÈ< “[ùþ[Þ „z$”%œ´ÅäM~ ÌÉÀñc üCSÀ€:U׊r_£›}Z“F^Þ@:~‚ Úk#Š·ô6©ä&ôÙ6“×i ѪÛÚíÚÒH‹qö¡-ui+Âhw$“§%=âÞE³»r"­ÊÿØÝ'­.MU%ßÎÒöŠõ åD]ò×vó¼aí°µqÂþÛ‘rîÊÕ¨±ú?nŸýÙ®4óÚÆ— Š¦Ðÿþ%káÿì…Þ$å$ßµ´ª’i¨JªJÉ&ÀÓ¿á>Ü¿ƒÕH;øŽ®PäR‡ý³çBÌTŶÒÔ‚Yä/[M`¸B%ÏÝ=Â1§1 ‘Ò÷€(øPÙ£ÔQ:Ï— ž bO?#•àÎzàóYŵ$ýCI¾¹#­Í)ŽRaÜy*Œ;À¶%]Ñ:) %¥Ø%Èó,'™£œ¾UÌW¿¥…ÝRñ'dGáKú˜²¾‡'¦:Kù”ÕÉ¡Y릡Ùy~i€~íB!kOXÜ|ŠÃ¹«|A’JZÉ8¹Æ z`¦)ïƒÎjŽgé2Vª|ž5póYÎß ÑÂÅ-^%_àW1«]Ígµ‰¹ŠkY`?¶k¹ÄiC]hT“s!#<›2 ÊÄI‚ Ô-³: h  Ù ¨¤çŒne³ À ¬VHÊ¿¶‘RG9H‹§]–ôu˜½¿ó(Ô¿Y-â<+Ò=G%yÉö£,#I¡kõ VXþƒÄ9“ïA¤´ç z{RR:Û’úãQ64YQ$ þ('Yºw)oÞßb)kÞA;œ²P©8]ô~ŠÒM¥+ý¨sEhÌã»).3 ˲ꭷèí 8gG6u¹Eoê 4µJöhêßr-£ø¨ÖÚ�MÍýÊéPo²¼h j>Ò&ç2˜öC¯êªá±kAéÁØÍ8‡pc%oÌÀU˜€MBkí@Í'q [™!yÓÓ‹Ê ÑäÐvŽUBíz'‘Ø2f왨–ôtÇJÉ{öšRú™c¥¹Yñq¿ý•BZ Ci5Ì.ôšŒ2J’ȳ ¸ÉPè~ÚaŽZ×âFÒ6W-³š÷ÁK9$zƒ|ôß�JÑ 6�/•9fz”Y¼_™í¨VGe®•*‰~ÿ#UMŽåj]޲EdšƒÙý"?-€WƒÈe‹È’7>6SÀ†žú<åzJ–•Ð*܉b îáß?ËÞ*ùŠèç•5YiâVºjÞŠÊbÌTÍI®1¡8—«,éŽÚ¢ý‹÷»ÇßX¼7·ÈZ.(gå}f<%z”Eé§œÑÖ2šâtõÑ}0•¬c~ߎܘOªg3‚ÀA‡òÇl‚óÙÒAI”Az ’µ•„lk#¹2RSÄ+…¨*šNÒØù*ª§É)òÚmäIÿR܇‘¹iÐÄ. MX‰Â4\§%›‘s j}ÓÙ3QiÈbç—k YûRq>²bý:„*†G©9½¸Yg˜V¹Q!u¹–€ÆøãY­lœa¢ÒBůÀ⤇Jw3·MГ–p‚®04Új4bm´ˆ[JäÇMh²Jdö¦ïåÇí&w:·_}I¢ÿ®­=W%¢½—,_à·x]Î!ÓwõV­+Ï ,J?ÿIfÕFtí#üûÄG^‡>çp)'dõ»áŒÑôÍ)†¿q¨‹,[ª%óc~CÂ,ä8Ö·„Y[fm½‚KJK„±•yVDÛ[Å…V²ð<–N»@ÅeÅ+”­K¹å•^JŽXÕ ÔœÕJ?¥•Þ£`{7“¡ü+ÃËïËËNý¯ªš¹&øŸ0误¥íW¶®ã¬ ÇàïØ¼­ý3m’o**NÅ+•;Ì…–´*蟣À ’'Ä‚c+n;rÓÕCÒ oòlÅq×xMÐê ¾*å½0DlUFZ4½;Iòµhh¦J”¼•8°åZNíŠó<*Þª¸Ö*‡4Æ]‡IçXa J’wj‚&ªq5V¡!”)滑P½í0`—ü æýIp[qÅó ÚÆØ”Çq•3Þ±–¯5W¢bŽJ¾B>%„ÖS{ÆkbS·/ºË\mèªJ ZEgÚ¸aˆ莛s cƒ ïQämç*iÑÁ3´Xî\)-Þ}ïÝþ-®·òQªÈÆÆ$)ß)-þ7>…ïÆsb>À‡‹Êh‹ºr– êTß&ë!šik‘@ ïûº®D†õÅvÅY&V$3W™°$Œ±þuZ[¾.™Ú™b®]y—†…u|Xøm+”³æ|8˜BËn0ôáÃYP8•ÌwõáMµpgdÇÌ“Jœ’Ù*ﲦïó|Ç01¾ù Tÿ¦³Ü¾<ø¿ðγ®L\§ìä+i®ºÀÛHnWCB—í¶ãÅÚœ Ù#t{m&î)^w’›óù˜ ÓAoB¼ª[y½½kCúPýRý z³r¨þe“Õ|ŠSÞ¨y"ÕÑÙM@ñEvt”½)^ÇŠ…ª“>(Ž×o¡Á•Ï_1å(î·œáj ·(ú/·(o<Ëñó>Z‰h>Ëmô$Ü\+b­DŒ?YÝjhÖÀ¦³!úp¥8"ë ‘µJYï•õS|eXP“a= v„ɰ«4ݾR[-Õ×H•ù‘24$Á<ýHz)|YUõ¾¾lõ ñ[e|&èÒ6¥0‰÷äÀã!%~Ùfê-a ÷Å•G¾jJF¡+šf ƒšý¦âÜ8¨ìãØßQéùyŠÓc¤=ýTâ;Ãuøâ3|9¤–Còôå Øžs’¸Å¨¹±»dX :õgô¥oÝÎ1‘V!6³Œd>S`Å›#&xßUmrh0²:IÔߌ†@ï$,H˜ýhü½ôœf! î]‹i¹g›µY�×´èË”P0_Þ?¨Ï‹ k¥E_"'.ʃ†€J‚XÔγ€ œ;Ë—B]ñìŠwµD¬Ÿ�Ý9j#!ï›iUZ#OkÜʳa«z·cï7j«*4Ëv§ãòù㉾`HÓ0®U6̦[BKi³Ñ’ÂûÙ#¨Ÿ5mѳHï«ÆÎ˜a¼…-7ÆcÿbìóuÑì^Çʇá$Bq6¦í%/ò»Lt»iò”®qÔã,FùJ®±»ómúeâ÷Þ…k¿­1õ.(˜Z“aA‡¯‚š ëØ±{2:“gY&fæ¨Ïp[ñ×#w™È½›‰ÙÂo Þ1Lèo|·¸3ðëaÔm»(ë^:@¶ l3âdØUÏíåè¼8Æ&½3,¡@>sÉìûkr¹o«‰·²" Ì¥;5õ¥óãv[XéA5—}uî.C:íR3ÍJŽuñ’ÉÌC@s5ïzŽ�¹˜c—«$ ħU‘]˜Œ}GÞ8ß'”ÞÙ©ä$6=ª:Åsé©j«[†ÂqØäeqr‹Ù}ÜçîIñ®w¹Åêþ’\‚3»%€w/Âo [|£À Ý÷ò'­‡ 62ÞËéft'1ÔdÚ·ÏÕ.4ïÉìèÑ,û;+™ Ê0ºšXÎcW ¡º³iÄùÜíçÿõŸ’¿ñ¾gd;d¾ý¬üá!:óá²L† mÀC`>«†Ž«ÄuaΉ~©õòœÄK$ï<”Ïð®Ò¹VPÕ{3g-î«Ù¿wZÌ»äÐÛâÝÔÌ 4¥škf™Ié:Âó:–gr'^~ý.rOsg½bKL‚¹vÜfub„¢«ñÖ®žkôm>q8³3˻ХÐ Ì@À4¤Ýû‹‘¢(ÑÎÞs瘴˽ñ&J ž¾‡(ÎcòfO/\ä÷Û5D3ÉÛ˜{H[Ty¸X x–.fÅ»¼»Tà[@.ñ¼q>‹ãXÓ‹þ±91Ï—mßê쟊/®§ ¾Ÿ›5|o0à óµK<룓t_ò¿‘Ë?ŸäƒÙÙ«(+t[§g6zb‡ùÿÿÝ:Ѹ)N9ÈÙé3> !q52ÃJµ‚wYW£*ÏGüÑ7ˆð8MnÜÀZ9ÄZs̨áű>ò…xPèÑw2d‡èçE™tôóQ±A¯ w6_‹c&<ç!|ž—áY/ ãY+™­À­9?mFZÙÑh :¹SÍ5²£Á( Šég«´ RÓxŽÉÕ7qN¥L‹oÚ³–vè_V‹<ÞÑ¿<'Òl‡/M^Òï)‰¶Eùx\åmLáj¥bVçZX\àyôóMðÕû•=¶Ü Ë*WÛƒ®dæLÌ“‹“L»|&NòáNÇOŽðÊb}}ÕûËqö›ðànx° î…†ÓDxîñø”ŠGƒA‡‹—ày ¾ïÒà—ÒöªÀXl•U\B­TÈ@r;“>º2П‡>¹2ÆC§nà!¾Oç*þ£Ô°óž ÞAKñ€rüþÝÆþÔ]uù×I•v XT×I%1O6ñƒJÈ8\Ø? Cƒä… ×Ò¥¸“ÜÒARÓ¯8É;ˆñ’÷N“v(+l’·?$É;€ví ò22*`ÂÌÐʃ™ßÀësà+x}Ü#Ѐ-ffPÐX½Ý8€ïæÀœOæ·�ð.K´|È“§²™6Ðdh‹4UÊŠ5ÔvÁØYv"U§_XuzjÕé¡U§»V´ê\êÔ�k‡?pц÷׈7¼?GÄõþI¦g?s„ kzaþ¤MŸúxþä”CG¦Lô䦸çÎÌ7=<aö„ÞÓ&L¨·£°pF!ÖaÄŒé7Ož 0î”Lžrꬔé3Ü)³<3gÎ(tçO6À†N›–ÿЄiƒ ò<š?Ýí˜3)¦{êŒé¦Á¦ßàN™0mÚŒIÜù)ƒSf¹ §NÈ�:ÒãYàÌtFá\^xHÑÔɸI3í=Ë3½÷ÃÓ'ôÎ1uº;¿ÐÔ}X<Ó€©Ó§ºšôŒ‰î S§§Lš6aÖ¬€é0½�¦—c@À5}Ö÷ÔYSó'Ÿ:ýŽÄ0ÓÌ|L‘ߌ逇g’{FaJ<íä‡y¥@Ó&§ ͺdìCÓfLœ0-¥0Ú ú¤ü6ÀG@f盺ZxfNÊ Ÿ0Ëm¤¨È/"2åÑ|÷”“ÛÏþºQl0¦ëoø0râÃÀ(ý{ 76‡uN˜93rSAáŒGµÜÂð™å†¯“t4ôdl§¢QøôˆúbšNiG#‹·Sf(ÕEË4Öoô þyðŒé³ó¡CƤHÔ'“{Æ ƒ–è"™"pÊÖÉ×Ví’NCÓ˜í=ÄôžB Ax ¦íoz(ß:½`ÆEû`(·˜|^‚žZ3¿h(ô¿ Ð "ûH¨ôöù9Tt,Ú ø¡ÙtI샳ò¹P’å™&xÜ3îΟ09o=Ñ-óS8Õ!dýC³ˆÝ~r\#ð+¢~hÆîØíÏs¾˜ô e³ñS3çæM˜æ‰lw­ÈÿYîƒaŒ™8aÒ#?˜_{éX˜bft·&£S¹Ý&MIcଋå¯ãÑnžQcQ›ùè ½Âш%Ãc1ýá=PË–:ù¥Y,ôÛ“w†¬ÛyÑYvç½m˨lÏôIHŒÐ`Û>Né8ja´"µ¼¸Ì¾‡ë8Áɘö'Ó*æ×6ø8l˜=…NNá ÏÌþ&Ô½¦N˜º%ÿÜ>]#SÿðJ‘sO´FhT9.Þ_´ºÇþ.V’…¡Ð¦åFëúˆþ.‘ïl!l.‚‹±È FßÃó Œ/&4ÙÎŒÝ_ÚÔb ÈØ8©q`Œ¼Â©SÇOŠ-[.ªsëí8lΦNŸ=ã‘hyӭËncøÉΊUÒÞ³L³gLl*˜6c‚Û4y†gâ´|Ó´ÓAРŸiýÇpÚíô^øëÖ[è¼ð×í}é¼"ðåÕ0êÅ?° QX§<:uÖ,$&Ã!süof™†N&˜:9etÃ4’¦ü:ÍofÝ‘¢Åa–(d&¡.9æsañ³´’R&çP¯ž1=¥;W§)Gøœ’³Œ�—R0aêÿcïMÀ›ª¶Gñ“ÓÊPR•jõŠV-×¢^h…j‹ 8ª€pJí`Q†Ú& *PLáÅÅEE­^QD2H[õj/ x¯8’XÔ ejÞZkï3ä$-üþ¾ÿûÞû¾æëéÖ°×Þ{íyšƒÏ´ÞE}H”²òâ²üÂÉ%<ÞPµ¥Œ…I¹1W7:rÓf§ðeEJÚÔ‚Y)½§�mäØMŽŽ)+./@ ôÁ§–,z“±§€CX½*J)ðhaPü£Æ´c¡î*ÖyòÁ°.×To…'åvH6X --€è¤”•Ož:™’³Ó± ¼¼€úXÓ½PðPS ˆ T’Àk Ì‹”Šb·L"ÆòÑ™4#rsï¢[!›#ôYôéÑn�–åÓ YÙfR ÂÈÑŽ)|xN CŠš×¬÷L‡¼KðNƒ’QÔ•g,ÚSF:&Œ3dtQyÁL”ºÂ[^R ÷çc€€ËeÛp”Õ�6½p:èÐäâ™Ï1cS&¦”M)ð@Oe½Õ~™6ÓPQV ¯iðTê q"¥TÛæ…Weß^X2 ýöþéWge^™·gd­Xèßw@ßtaü¸áYBd…?*udé˲4Òòó®‚;Šyù¤;Kusc&˜z}wyqÉÀè—K æúšyL›<½S{ƒ{¶§Øì7¬´ Üì7¶ §ÙZ³—jO³ßp¬^Íž9TÅ}IÞ±>Žž�÷÷<§kèôéSŠ ¦} Æô(z"XSŒõk >(¼ÁIrÜLdòè‹}ŸÍWÑÔ•à¬9 §rÒÆF ,Œ´a`M|Ž‚Ð´¡(aE@ÚM‘P–@aiÃ#(nŒ´œ0‹*‡#(-Ƭ öõ§O-›> ª+ÖOBÿ¾fï´>‘Ô¼ !x¦sådÚÝWuþÍC‘=F²B`Þ}U7ÂcôH€ÂŒÈÜÄn‡©ê ª›#¼é͘ ÄcN>@3îyGPÐY'©B`E¯o�8ƒ«ž)•tDÝ“âì‚îYÅdCëÔWs³G/¬}™ýSR•O{ë…™Ó˜=8Är¬`(å;ÊGGw ZùçÈ&·Ž:RÐëŽköБ‡ † ƒcGùèè9‚±*áøÑ^œà&¾¬S>”Ò;xp×0A+†Ü‡bŒ›»1ÜÊ…æ.U&î4(ëì OñTó|™ªÂ1 ªSùtèyf ØQ„áÓô"µFgeÕÐ8ðÚCk˜›7} C_¨‚û*_^+é!˜FÙ`_d;@^æ:Ÿ@µ»†cj+ÈÏÜÎè•]¬êÑ\£F׀ѕ²©þÖƒŽj‘ 5eTÕÈÚrȰO­ ›uÞ çåʾumá¨ó#‘x§ì°AÓô ìÜ•L+šR\Þ•÷)Æx¡ Z»Ÿ+µ?„Æy@¥Äñ5¦“î�» èúOK¹£ üvì±NŸ‚*S ½ÓxÕrÖÛÄ>æ4ô€®*€Ì… <vÇÔ.©Îëô⪠É!Z–F]Û˜»K?­°À{G©¾v%±´Š1ZEybNœ´ÉäA•*Š}Ô¾>71Û2}fèM-›ÑǺ†1”!%§éC!FMzji£ ìõÏ¢áP®qr"¡aàgV±¥&…«TMÐÒ†w†#)iWö1É“‰ª#4ÞdŠQjŠÕÔº»÷žÉÓ`ô}Çÿ¨ qNIgÇåæå¸núy¦– ýf”3KQñŒ~¥SiL®SpÏ“E`?f·ìÈ.Ò×Ù`§s1øÎ|] v*µ Ý öhÀ¡kô„Dú¬‚ù‡òœb¥q¿4.ŠÒÇGQ¿“¿1ÀN×]8CÈãáLzJ°T?%؆,Äæå‚Pý´`m~Z?„ ¶ Š:ðo�ÿË’ÁüÌO€î_B\Á¿ ì8Áj,–>‚hé© pÏ ‚å¾xKbw›ß0Oá2?òF8|EÅë_%ZQì+Ê·pž‹…cÅ4]ç³2Ü)ð¥Ö„Ã/ÇÄÅüòÙXƵ%S*—)ýÍpø–˜<hÓI†…[6J¯9&æé¸VŸ qaÈ/\ñV8|óià>…鸶níÇqñ2‹œõáp®xj\<_º1¾ÄrjðNÎ)µáðгcáv‰à‹;³ªw†Ã¯ž"n˜ß³�ÜsW8|iùÝV¾¤ñ|Yµ;þùy‹¸¸ÙeÊ—méLd\q«M3à>{ ÜSéñ‰=áðÑ6â•Âõx÷×áðæ6pP÷°¾®úO8üHLYh?7¯öþ#>¡ßªßa5|5ð€Ïz�êøºÃ—_Oø’á;¾øÞœµð}�_-|[᫇ïøáû¾Ýð} ß^ø~€/ß~øšá;_ |©À+ ¯Œ‡ï øÒáË‚o|CàËO‚ê!§‰ÉšöQ{žÁþwnGüÛ›ÚŠmôï¥W_¹òtqqkퟡïøuü:~¿Ž_ǯã×ñëøuü:~¿Ž_ǯã×ñëøuü:~¿Ž_ǯã×ñëøuü:~¿Ž_Çïÿëï‹&A¨„¯¾ø>€ïA¾>^ æ~ø<Üý˜{᫇oYíÑÁý:êwèÿòߟ‹]ǯã×ñëøuü:~¿Ž_ǯã×ñëøuü:~ÿwüRš<YdMåf7¯àf:7p3‹›ƒ¸9„›9Ü”¸éæf7Çqs"7oáæ$nq³”›S¸YÆM7gqó>nVr³Š›‹¸ù 7«¹ù7—qó)n®àæ Ü\ÅÍÕܬáæÛÜ\Ë͸YÛLg¦¬[›éŒ”õînäîÝÜý5wÿÀÝAînæîCÜ-üÎÜOÙ¯(ê,°á™¼ó¶l;·…öÚ‚p·7_$yÜ>i” Ìàö7»Î•üsa ·Wö„ŸT> ™üNÝ”,A¨Ú¯„n¯žpû¤TAXÅíyÀó no-'¸}à÷yX`¿–Ûk@λ¸=üs{"Øk¸½y¤ 4r{-Øçö”1‚ð—8Îdv©vÀ¹‹Ûó�g1· .AX«â\(ÿáöô>‚ÐÊí5W Â߬<^Ãa,·§€}·ïu “ܾì¹}¤ÿÜ^ öî6Ž´™Ü> ðÿÎíe ó\Õiø´j¿Q>àöôAøNåóWAˆïÄqÀ¿/·×€}·7ƒýnO‡´]¡âƒ}›ê½ üÊíÕxI|gn_�0nŸö±Üžv/·ûRnoL„÷¹=ü¿æöw B˜Û+Áÿ’xn‡ø^Ïíy`ŸÉíà<ÎíµS!îܾw�ðäöÄK*\(ù¥Ü¾âRAÍí“Àß«úƒý)nß ö-*-äûÏ*>ØÏèÊã% ÂÅÜ^=M2TÿA¸FõÿXFªöOán_Q'no|]ª´+ ^ÜŽó¢op{e¦ ¬ãö”¡AÅ_.»¸ÏAþ¢Ò>éÉí{Á?©LJpûq{u½ F{›ŸÍí87{·ã\n·ã¯ŸÛqÞöQnÇ9ß—¸çt×r;Î×s;ÎÉí8ÿû3·ã<ñqnÇùánÝ™çŒÏëÎå\$}¸½ò/P.T¨Û\ª½‡ äs{cðâö<ÀYÀí5Pé=¦úþ‹Ü> ü×s;VŒÛU0÷©´½á°Šs>T¬ \ÇP/¸½ ô.Û«« äöI ··çA\J¸½y¡ Ìçö¨Oäv<`»œÛk L¼¦Ú¡žü€Û÷– §j¸P_ýWÅI†´Uù�ÏÕ|l=xYZ;·'‚yal+,ˆ%\ÁìtL7Ó`Avv|÷&ƒ½Ø€3Å`÷ìU{À`Ô`Ú`_i°¿n°¿m°o4Ø öÝû^ƒ=h°7ìG öx»n?×`ÿ›Áî0Ø'ìûTƒÝk°Ï5Øìð«Œõ-0Á¶ƒëÃt†Ï/°kØ¡jðÍ{Ð+LwøÇ4«Ÿ!KàßCð= žïÄ6þQøƒ~¾'à{R`g€VÝO ìj÷ð=+°ûò-<"j„⸩ž ·q³7;s3ž›]¸Ù•›Ý¸Ù› ÜìÁM;7¹y7ÏäæYÜTÏÈ&qólnžÃÍdnžËÍó¸ùnžÏÍ^ܼ€›r3…›qóbn^ÂÍTnöææ_¹y)7Õ³ñ}¸y7/çæÜü7ûr³7Ó¹™ÁMõhnàf&7¯âæÕÜÌâf67rónâæ`n^ËÍë¸9„›nåæ0næpÓÉÍáÜÁM‰›.nŽäæ(nº¹™ËÍÑÜÃÍ<n^Ï͸9–›ã¸9ž›7rs7'róïܼ‰›7sónÞÊÍÛ¸™ÏÍIÜ,àæíÜ,äf7‹¹YÂÍ;¸YÊÍÉܼ“›wqs 7§rs7§s³Œ›ws³œ›ÜôpÓËÍÜœÉÍYÜœÍÍ{¸y/7ïãænÎåæ<nVrs>7ïçæb;«T÷s©ئ@ý!BÝ!B½!B!B}!B]!B=!B!Bý BÝ B½ B öÀ—Ûáƒz@„:@„ò/BÙ¡Ü‹PæE(ï"”uʹe\„ò-BÙ¡\‹P¦E(Ï"”e1>(Ã"”_Ê®åV„2+By¡¬ŠPNE(£"”OʦåR„2)By¡,ŠPE(ƒ"”?ÊžåN„2'By¡¬‰PÎD(c"”/Ê–åJ„2%By¡,‰Càƒ2$Bù¡ìˆPnD(3"”ÊŠåD„2"Bù¡lˆP.D("”Ê‚å@„2 ‚þ‹ û"è½:/‚¾‹ ë"è¹:.‚~‹ Û"èµ:-‚>‹ Ëâ$ø@‡EÐ_tW½AgEÐWtU=AGEÐOtS½A'EÐGtQ=AEÐ?tO½AçDÐ7tM=AÇDÐ/tK½A§DÐ'tI=A‡DÐÚ2 ´ehË,Øb»mšÚ4 ´ihÓ,ЦY M³@›f6ÍmšÚ3 ´ghÏ,ОY =³@{föÌí™Ú3 ´ghÏ,ОY -³@[f¶Ì²>hË,Ïñ¦,ähWþ˜iîéEª}<Ý@ ®‚ù…Ó§æWx§åß9­ ŸÝa“ŸŸqG±'‡®ìd77©7B·OqJ,¼h©¸0]ÈÃoèW¯ãi/IÍÀkgNRR^\ ˆìާSàÒ¥«xëêiá’çäiEcgO½}z{2‚xíCO7A y®a¥Å…wUx§¶‹GWµ‡€—µ‹ÀµC¿·¬]dv»W{(ìB°ö0\ÓÚ‡Ó½cí!0ÛìêÒv1óÊ‹+ŠËg i›]’Ö.»®=”  *Óg²k¯ÒÛÆ¤»§Æ”äçÚÒd+§¢}vo-Ôgº#ªt¼lùT™È°N‘ ©Ý¬d(íç&Ãáwô딵CÓ_A8"]·×6NE^ùtÝòÖ6ÒTºE®møô²âvoÞÈï?Ôå: ¬a§…•sZXÃO ËuZX#O klûXwL®€¼åw0¶‰Wqª¯âT5^Å©+±ŠSVb§¨Ä�~5NÅ©ª:@à«´‡rÊbQñ?ª û”Š_qÊÚ²âÔµ%»”½m8Þ©Èô¢mz»åÔ…HGk·éhí#­Ýr¤£µ[t´vK’ŽFE‰õ±ôûðó‹gyðÚÏ|v©j¾zY¾nËû÷±7r'ëð´áõã4Ls£?¯èŒ^¬Ãcô)/˜i`YÌM»¿ üŽ ƒ“:gFÔ¦…¥EÅeF_µcföÉפ!_ 8 BžZ Ћ�üå݃½m » Ïèžê³ºç Ò£ÿ•&«˜<² n¯™…×ÌÂkfá5± ×( j`x,ßðÜŒ£¨ê€ òã&àÇŸààÝ2õ! öüÞÕ2G¼á¢{ÐÝÍùúØÝF2­U6zæë7ÐCN0~,ï<*?-ºÌu§ÉJñ5àD½Ñ 9èˆV¡Ý9mrÑ䊲Oa©–hùèCš˜Pd)äOÑ«ÎiÅ3±êyî¡ÕÄÜ­&’îT;&Üg‚#:ïE6¼uèìÑx;m¾ö¦« Œ=Ç„qù#`Ü0aœŸ¯½üü Æ °ô®|¼x6ÒçoAyó*(w·S•^È ;òRÇvM¼2=½¿_4…ûðFW  h ëáM©˜ •’2ˆŽ§DP«oÞ± VØÀ 2°xÚ AëAM-žZX6›L¼ˆ:ê]x4È4ujA™nªwZ¤G»Á�žâY<¯"ýèe“>Ý€¾ÓT‘+<å©ür²OÁhUÌ®�   Ožè3 +€ÏŒL)N™:½È EyAQùÀ~ý2¯Ê9 ãF¼Ž_ǯã×ñëøuü:~¿Ž_ǯã×ñëøuüþþ>;öKkgA°à¾/Üã…û¹fuÂýhl‡% B~þ8çÄq1$ZbºqŸ˜MÐ÷‹åç{Šgyt43¿<¾¡ìÆ:Ýñ:‰UÛ²Âé+h©9¿Âã½=#šß‹Ÿ1ómƒÛ(i<ç§îkËÏ/¬àÏ:Æ”oR#3¯¹@wÇé`ãû(*?|¬¶Íø®ø•™Ë,º;ú5–ˆøBDóK‹§”ÑCAf~YÍÌ,µDºµ_Túy§Íœ<­(ŸÀŒ!ßïÜ´èîöå+.ÍçïCÅŠïƒÌ¬=_wÓ¯›¾%‹~ºÞå8Æ9 �Ußc»Qßâ£ÞÍžR¤“·Å/Ñà6ÊÏiSTÕ+ó”GñK4ñC·Q7Pß.0¸óó§´Ç/…ók6¸ü0ž)üÌzgâwã·Ö¢»mBÛ¿|œr-ÌŸ<µàŽbT3¿Ýg2Fñ·¹,Dò+*ðÝ‘üör~+ îö廽¢"Âɯò,ÆoQgÝÏÈÏT{‘ÞIÄÇí=Ê™ãRu8…Ëu~lwg/Ü÷Š{<“U+jUœ0eòí†ùó¾`í[EÜ+njXxæ]Aá`UÅÛ†#rÿÊ‹-´w·ôo!ËÎöÙª{(-üÃ=~¸¿/¥ÍÔ„²+,Ú^P5È÷ëNäa‰®ü¹Á~îÉ·—”Ïî7‹øÌéåwUôÃÉùsû–¨>ýøf“Š~ã|³Œ|q÷òõV”÷ƒ¨ãÇ‚è;´/ÕÉ· ¿ÿ‡ޱî@¯—Ž„ÃŽ±áÞÏt¶ö …pï§ÁRM¿ñ’ü£$ßtËÅÍnyj‹äÛŸâ’OHòA—üoIéÆC#‚K±¦JòÝò¾p^º$o‘üµžLôtË?«þe:’[Y¶¨$ø*ÀW™ÚÒào¿pH¤ß– _WgIIÚÛÝðL¹›E7eÔæ*ËJ¡‰CÊ’¾ö «(>LÞfÉؓʨžcT£€Jâd{Fqͨ…PìæûÏã q-Ƹ&Jòq—ü²sW„Ãa·rð|‘ñ¼x:ÞÂç’kÝò·’ü ò¢:#w>P”)_F­Øácx%¾y]»ÏHZ‹{ÒѳUãÝ]¥@vó¤Bëý’¥1¼wÈîÇ}àª÷vcÝ€{®qÿt1‡áÞôµ#‰ùÚáÌÈaÆPf cÆXfÜÄ (ŸcÜXJ†C&hæE],‚[þ.\] Uµžîëp³·Üx³ã–†-’( ¤‡ŠåýŠAÅ›!@“™~g–J¿5š¾ë:ŒFè^ˆ)ÃYMý5ƒ<‹c¬QßÓ&ui,jL´P¸U¥v·I=05&rh“F}A›ÔÝcQcž†|us綨ÿjT…S£ÞÜ&uM,jÔ Pgú‘¶¨“¾#ÈOÉ›¤—1ïFïuDb÷¿s2^‡å ´ú$g´$’÷æý„É»žy?iò~“y[MÞO0ï;¥ÀÁPŠ{À ]°úGÓÿà¯'@<z°¥ÂÓÙ×r™Çž±k.«?ÛÿF°µ°ôØÅ`Û#`q ö>ƒÝì;“Á| ¶8fç‰Y¨ê´ ~‡Óà3"à=9ü ><~‡Ça2�0ןT«,K‹SëÚJ­~†ÚôI±ÒxÈ–ŒZ·òxÐÂÐÌõ¯ÏYUþ['ªvÙôª¼Zco¨ÿ‰õ•*뫊ÒVýÎ*¶¤I@®F±ýJ2TÔzý-)Ý�s®žK°ÃÏR)EÓ1ïÈ6@ŸÅƒüO§ˆD*©ŽÕAãAPÞ® rSü.c‰ñ¤Ú P’8ÂK¨õ‘·’oküÍ ¡EaÌ"­ý!Ï-†ôÕøó~L®ÜN˜-¢‰ÓµÄɘ?íòcz›ÕAÒsüäÏQˆ,†y6Šá{q!ÇþΦ6ã·ªµýøA~ï…ü†>ÌTÈw/õa´|Wã±j—ü!æùµ,{Ä©™Q^‚O=: <ÊãOD-ç«±Ë Ú®ÂmnKX¸å –åneJj¢[ñ¤&£,Z~òòÍ¥Û°€õ*i0jZÃîŽBòîaühò3‡¬kHA‘EhºVt¯‰û‡Ž«WçD‡¸ŠAlÑuÐ-ùt=0 dþ€†üMð³?ÂáŒÚÐC~ðôß éy0òÀ y0ó`ô#]ò’<¾^’skså:¦0“-¨0,{ÕÇØ»Rº§ ”„ø:˜-Xî\-~@z[ÔFE aÞÉ[8(% 8?{¥ÛÝòaì¤V×Ú0&›sýaOê¯*•8à’;Kþíž‹%ßœZÁB:k%9\õÌUï–Ó¥Â-’e³$7Bv7ta÷Bº²]eN‹¦ŒiR@PEÑcºDébK[1ýÈ¢!Ými3¦WE,»ç ‘q­×âj¯º�»,¾ñ,¾¡nalÿ¸¼?†…öäýUhKÞóty?Ú”wA(†¼y§µjò&ryon5È;8†¼ÏèòŽnC^Ï4ŽÙ¶¬_i¬²úÕÌÕåÝmwÃIMÞd.ï«à…0ÿÀ"c{h³†’´°5Jzÿëºò”Eƒ«Òõt^! !rK8¬¨h‘;Ùzš‘ "÷í -ri<rŸ’±<Ð[{Uk«†ôï¨p5‘îŒ)Ò‡Ñ"í5ˆ4Z)…‹t zÕÑ´¤ö8 !©ÏÉè䜢‹vC›ò_«#e¶-ÿ+'OSþfƒü‹kò§sùï=®öG]òçá;¹fœ‰½ÓxñZ¨—ñt·6\¬Î€0¿‚ÒüÿׇwÐÜß\Î †ð°¾j:ð»‹mãômö¿ëK³`.}Úzÿ7LùŒ†Ð[¿a³ØÙͦ ö ] 3×Ç&<óÚcØÓÀ›ã]Äx˜a\aÀÀ¾ÌSÌ»¼8ì‰ê±Aç)u¬i «`tØoŠ¥…¦``g3üoŽª±~ò‘cä½û(ªX—ˆ)£¼‘ņûàÑèe48|Ù‚}é&ì÷0´L íý+jÛKò.—ü/‡ýÝÁ³ 9íšå/ÝòÏ îÁþXý±’ÈbñæQ>ò;••\åñˆyC‡ÊÔß×û¿†n”1ã<)`{ø ,ï –¨ø±Ê‡ËÂÞSß ßvЅ︉.û3¸Ûú “«áœi‰Ä¯? ú?û]€£€­×A2¸>WèŽÑ¶çÑþ+³Ÿƒöï˜}Úw2ûÆí`ÿˆÙû£}C+Îx¡J=r”ò(©óß®Žê„?€È!ÙÂVm˜€ù¹¡òhòÌT6JÙ±÷eŸ Z`|°}º瘌ãû†í ¡K#é%ùÓ¹6Gn€aKn„*Ry|H'¦ÏmÁËN/=|â)à“NÏ3Âëœ-ÔÝ®s6‡©³íldö9MÕdCo æ=ë|°4ƒŸ÷,ôÉê.âÜ€dgsS#ÂŒãѶ¯<…|5§€¯:|Ù)à+N¯Ž€ïÄž¿üŽ„;Gçõò½S°a¦³¦éIÞ7i òè›l 2<„‘¦oX{iN/ÖjÝÁq`ûN-›ï\”tÁ!6;„àöwl ÜÃ(Ï®¼…óßáêd”I¹71¸g&‰ÀKEf¦Øº>;&8ntŒÇY%ßþ4—|F@kaTü2¶Cb©]g­KqWo[þ-\=<eç'ÐóX¼ ì—–IÊøZw`Àfg†Z^r•¿º”q©e8\Év(9©VH¨ôŒ=Mݘ”wKÊ€Í8ÒNÀˆ;F ¼Å‘}KªuæW.¨fgEÃÑ»4–÷}©RpäÌ–ÂmƒW½­ê¾`_0á:—ï×øœkæ|b÷§ñ†šŒ‚E꫱8¯ЇÚNT¡¥Ýì¸Åq«ã6G¾6“cœ¯ßÕúøuªï5÷£;æøŸ5¹OÄMíØ}ˆÃ]ä¦@¼œ‡aœ;¼ ÿF¾˜y«#p_ªE* §½Î4Ñå¢ù\¹9Gþ{t +)WáÖ¡Û¬é‚ ®}¸åæ˜S&4Pí_'Õ>ü:Sí»~ ‡›6•D–ÿ] 5ny³[nh}õ<¤M„¾›ô­Õ%rÉ»äÜrdçZI±­ýu7âÛçÚ÷ƒP¹—¬kì<-óº*·®ÅÁm 'È1ôÉ··Yê³Õ%ÿ¨þÚ¹·¹åPÆvÿž¹½°¼(ÎVQZæP¬U%²%¼Yòm³¿òîSÆàR:åÊAì´ºå#áêE<„';?�f}vƒ<`fo±¹u-qŸ÷È—Lò¯—ú'zíR x·® [‚ËWc>›ôa§$×çÈ›œr=t¨BG"Û“èòç]ë’wʹoC‚Ô¸•Á/íG5„ÉYŸQ«8k(A =AK‘šX)òàBŠT(ãßF-Ε÷bd“jK¸IgÏÜKÝ  áŒÃú|âøµöw-²õÊpäÛf¥äúÆD :)”*oS2½ !`2Õ€IÉTCAÎ à$„«çèô™óê©ÓgŸ¹½m§¼ÜÛ|Šòò÷¶Ë‹´iŸø*+3åPfœòÞŒZ#ÄžD0Ÿ/'¨ ÛuP~uÖ,­N«EÔ· hÁšWH¶¦Ñ…ëýW©p=þ*+\=¾‡ÂõR{ëЯ•w/Žr2­eês¼¡< ÖÚùxðÄˈ=üþe¼0LkObÎÍß?‰R—ZÝÊßzþÌkFÒÉm¶{¡LX° döáiÙu³$ÛŠ~¥ÔµWùº¨Ë²;it™Ø™wØle’ŠHøáJŽï^¡ú:\ÝIÙVòG®¢*™"’ºRÊ%Ù^åÍ4ÁkëÞÌ)¦jáÿ×~Ã/åv=üYŒÛ •Û"ê^Ú~UE½Aÿ_(üçÅ×&Š­*Å¿ã5ЉŒ¢Ëj¢¸tu$Å*ÅuzecÅbE™J‘¦SÚOkÅp“Tõ0T©™ßs®jέÛÏqžŠo+çSQfklÞb!nY™s_«‰ÜßLÆ/ÅIS¬ÞR)ÊuŠ¿2Š’×ˆbÖk‘a,R)nÖ)5ÅF1ÎDq›J‘¯Slf½Å&Š*…¤ST3ŠûÅ’×"5ð!•"C§¸QܸŠ(ž3Q”ª™zî4¼™;©MçαrÇfUáwvÖxì3ñþÌqÞŠÍ£^…?¤ó8dâñ¶ŠóBl¨ð*Güë:T£Rç^ŽcR3I…߬ñHH¥^HÒ­¯G*Ír5Ú£:k‰ÝågJìÅEETB)™»BD‘õQÜòF$…K¥˜¦S¬dO3ŠC&Š UŠ‹ôDȪ‰L„ƒA޳ºST"ØvªÀtÒt31¸2ÄqD3@ø™*¼Dã‘PCò^©ä3ÕŸ§Ë{©)¸U‘†ÄδWUxŠži%+)85ꪾWªÁ}¥Çn„)¸"•Ý“±c—£ÂïÕc÷ûËÜSpçªÁí´jùçÝGùWÀÒã®"Ócºštqº€cÍé±ãlµÅNþ‚MO\IMdp‰jp·i¨™U†àükÙ”ÈcH.ûÓÃäZÌ\Y̵€¹†0×<æ’˜ë>æÊc.sMd®)Ì5‰¹Š™«”¹nc®2æšÀ\³˜+\ÎJ°º™uX¿¦A‚³¬µ„^ËD_¹‰\õÌõ.s52Wsíf®›Ý^æšÊ\AæšÇ\Í̵„¹Zøüs L¼5ÌÏ\uÌ•È\_2W2s51W º|ë1¥{n‹€1­`‰-ûWQ Î`­!+‹˜ì_A]F‚Tº·@Á†ê.®G\ÝŸ\¡©@îKXJL”5iB¥UŸÖ¿8NUÓeVĦ@F*Zp(ûä—î&·|À@¿Ã…ƒÛp‚2«)Ű_ ôø³hd®{–…_ÙM3@Ð[w v¿@»b-匲’S–eÓVð?òù—rÿ‰è¿üßeøyÜú?þß0ÿ,îŸþ¡Ù”üI=«^”€ Û(ôD:° @è„ö­` ]Jc Œá'ô‚(ïy½ÉAÏ"׊ƒ_>M µ–q]Âáó¾r;)‰j²ÌT‚ wè‰ t3BÐS t)£ÂAéšÏ@E Ô›ƒ’TÂ@K¨+Û  Q ô( ë·gè¥3ÐûŒê ÚŠ $ZŨÞã —tì$z½D §9h1‚¾a  äã  me g^fùÏAô2me \Œ Å 4Ÿ1¼’ƒ.FPm£ŽQæ9ÔAè{FuâiÚÿo� ¦½Y¬2¿å Ït1£êF}ÌÌmô‚:3Ð+lÈõ*=‰ ý'´‹F!™ÕT‰ ÏÈÃ@^*FÐ; Ě̛8h$‚žd Å tõCP%]Î@©ÔAÅ TÏR£ ý@#Oàyˆ:^Ó÷ÃÎߟNuÇ ¤MÿZ¤íΨ­±úމžÁsoñ‹óÜ,–7‡åÏ!€³ 6àñ Úa¼í)´}Aî|й�‚\˦ŠÇïvõ/x/6mŒUëÿöY+×âfþ¦/‚=)ĔϢÊ÷Ù£|¾c{Þ;[~'j¯z“ª«åØ*8äFIþ$ýÏ+(©E•ôí'UI’IúõÔ¢¯?jÏŽXߟÓôµq|Íd¡]Úp-´ <´ijhM©áéwAž6m>ðŠc˜ÿ¢™ž¾•70§Ñ™Q+ÉŸgl߀;ì‚çæÃ0}3„~™Bb­ž±½ªÖku(=S!)|[Sh‹_;ó‰´ÞµÓ%ÿkc òÿ²ÚÂV ¼Ã±'j=õ2üÙ#Û1LÚ=kJCkOXOçWCgLJÐ0Êވǂ«ËÐQ'ÉÀ6N’¿„ õÅ0óþÁ#zöË,ÐÏN²ˆJöœM>ã|GÌý:’<§^Þá?Í•?Ì•?Ùˆlð®ÇQ»ê()ý{ì΃¹2†r }”ë‚ëoåA>eJ™S¼”7gÔVmwÙsêÚÚ+1ÿ¢Ï×Íߟ†]4œ²[Ó| -Ý 3?°÷6ÄþÝdÑS±íÝ-èóšuþAPüØ2Œ?]·¦©Vœïû”ϲûO�‡Á¨‰s'€†a¯î=ÒË@ÒQ¨7T ªŽ>·LÕQ‘:õ¿?jÄÀ3 )pg½Ô¿“Ý¿ çó#'æ¤B D ôüÏI¾ïš¥>_Hò·RÀ:>îK3‹’¼²q¾²V³­›²¹ø·Ûã:·²Á.ù—üS¿å‹[Óì]pv=iêÒš™T÷<\$jàNÿ5€ãhÕÔ!£*Û¾~‡v#äÊeù¦@\¾ºx—o+|ÛâŠW6ù{#Cei eÈ–KÀ³”Ö7ð˜€Ý'.ÔYSYi¶íÑhz^ `öú¾³W­`eõ¯“žŽÌ„^QÖ,CC^Š™+ÉË1cÊtíÜlIYŽIèÌø!r¿„¤ŒÇEˆþ(רDGx+£‘|[’s²÷ÎûºÄ{†½©×¸9ClÐ3üËO¡}èJmárý?周­e¬[VßÄ Dý‹L]Fc{ޝ‡˜öc|­öª§=¸~© ÔñIâÀZ⸔i6é>rûU¼¥€—£¤;ý‡íUÝ(}–1Ö±æ¿9ò‘ ø(X‰©ç’wçPe9÷Ež†´Úür)¦a/öUäå,n+1¦ÔÏ2ëÏà/Íúƒç=´Ý Zñ ;ŽE¤8—¼­æk¨þ¤¾?%DóëËAø%ãÂ×Ä3u†3O²Õ$À~{w£tr%¢R¿«*õ¾j¬®ÐYÉѬþ¦–«Œ#ã³Qçóà ÏõQ|¼#9hxt·pÐ?¿ÒAéq´ä ä+=yz ÷Ðq7±‹I4UG¾AGþfWT°S9(ÇÀ'+Ž6«°`}:§ø¤Ý±UòfY*{) w˜Á?%u½ß' N¥ºïFÉa÷+ºR¬ŠW=$¨~AÇÔf>Ãü[ðâíèìŒ ¤Kîäò]£«Û޳â�z¯¨ $Ýÿ\DÝñíC¬Æa‹¼ÆPw„6¿jÎܹ‡° |KKˆ0p žÿ&ðK;£~R¸l‡Æä7U&.ß Tôm`•ê%@ÿ—âÿÜÂÚ1Ø6Hî.¨‹¨¸ëã¥ÂíÐvÜ�_ãè@Nm"ãÁ/xX_]20žáZ™Ìûf­}eÝA&r ‰ºÍ¥äbdÊwR¥®õHláuȾ±Îòôäöôó‡Jžq-ÄiÜC4’§$„ñгTSJziëK.ù|Š–ˆëŽÒÒ;äž=” ­ $Ksx/ôµ~€`7Â÷|«!^nâ—öË î¶¾NíàøÙø¼`‰Þlz½¿‚*@’{ýQ¾J½]M¢µlËïE-è‚¶—íd”{bÌþ¢ÅŒ7$˨AWÖPÎÉço‹ð–ÇAƒI9-nf>îm‰áÍ2ù…žn1 Ð!�î} Ý6#í@ÂEõÐø(¤ÝH¸+5”…Œ@ÂÝ´/8©9 ÷+†~j5#ÕG á&c:÷‰T„;¯i(o^£s;åz·¼ÉÈmvlþ×°Y ~úG8ã<_4þø× ÿé?h.¦9r?ˆ ¿ðdü§¾â4Ž¿ÚàŸÉø_ øí‰,€Êvð{3ü€ ß%oÇåýM i"”Aï_@i÷Ó”Iæ/Ë¡!4a›¡|ÕÊÜòÎ†ÐæCê~ÑáyY|ž>tñG|'L|cÿ|" 6[Ý×*!ãÖ ¡Nsfɧ¬³“@ç­õ.¥Ó:«`a5Ï‘p^º3ziû#uNØs7õÒÿQÊ-?‚.ôÇ]'=q�A;€h·Mûw…·º|'Ò¼;it±|0ö–ߨˆ3ì±ß~Pç™Òü­(›¡BPàiM߃•+!¸Sh…ß¶•Ÿ ¿øu8¶Ú@Û׃Ÿ…4–ߦWKªÛæÑÂíŽ:g E3à­ júûBæU/§TY`ðe[±cz˜Ö³“œà¤xq-Ÿâ!:U[£ªp^â*‹§7®cÇÝŒeŒÝm2v.Æ®é :O Hc“ùñ§¤ÐÒûD·’ž zuzS'± Í`=ì“O±I0Îó-Ä3Äf­“.`!îWøüƒ6½ƒcxúœƒ>à ¥�êÌ@Ù|Èû,€¨ò2—×E]#õ˾QŒ,?J¤›9u|E”'°Ø`û¹�÷×aÁj‰y¾ÌÄÏþ®É½A4Ÿ'е^?„æZqcWepÁG†]$ÁŠ=ØDlͨÍõÿเ¥ v}@;zt&I§0ipóG½«ÿ9Þ1±‡ÇÁ:«ÉMг©eí7OFL(%< J Û‘¹9nZ,“õâñ‰žÏ‚!ö.·Òû©u™§Œ‚#dšÀ¸:˜£‰ÐÈD¸%R„dUËþ[›¨îØíêoMõöˆœ îZÄw©ðý) ¡¸†ý[;qs ¼)W®ÏQríï8“egbC虆èœÎy£“xæ«ÎYOƒ*(�eUÐxËÔí9ücÍdàQ¯ÂÉâÑv>J­‚ñ Ö#s/ÌØîŒ€K’סX)‘­c¥ðf)û#o36]4÷Á,l#_=fvŸE|F ½óA(ÈM$È6ÆŠíªÔÌžøÖ"Åé 'n÷ÌeÐï¨ÊÊì†n?úöëî˜ Æþî ÝÉJô ³©×E,³lµP6Ç„Ù~&ùþãØÒ,oU‹«AþÁçÄ‚CþÙßù¼!4©Õ°?F’ÓÁØàÈV¶ Bç̼ÿ"áoü˜ëß¹ÎöÐ8Âß·Uߦ¨gÉ>§þØ6Mè(Ëép Èàw')¤æcÉŒPþ´åCm“çjãD[{»Lúg$£©¤-{i«à°Õ½#(g­©¹r³ü%m$\Ü*(n¡­‚M>ãyÓè0C—îb§CŒòa—{åá' XýêØ�yèŒQ¿â²Ï2Ì­7Z Ç9c·çËHoÖS½//¥%Lùã\y«[àê&èï¤û;Só$ßœdð%YyÂ{“Õ©´)Dó_I!&Á{Ù‚®“a%È}‹™/.nnÄùÌ`O?F)0$…–9•¥$‚"BfsN§í”]$X@¹+pökÓq …ì«…Æ`5ö¶Ù‘(ýô,#È›(x.2ñM4ó­üe =ÆWð¤ÀÈ—‘Xd ­ñÊŸ©ýiç2ç¢7*–cª0#$,ýØ=¿×ÁäD—.9/ÞU7‚^§Ã£«s– žèɬ ­rvF3Qq¦8ä“�±Œ!bã}¶.»›NZ° aÀŸ¥PZøì‹7bµ•ñƒ3ãl„Ï+©v`wéCœ¨³æ\³ÑìUOÞNÈ“”Üj—’“�<r’@¼ ø,"Ÿ‘²õ2&ÝðŸþ¸Ã³‡C¶žíð ê‘ä}Éå›W p’Ρ(s[‚ê²¶&&G0Gþ’z¢–Ôl2p›êÞ Lt²%4šV…ç,ŠÌ)Ü3¼(B Rh—ðœjÂÓ²ñª#r•ãA4RMü–©Y©¢â€jkh4F Ñy·,ÊmžÔ¥¹˜‰Jnš2~5›ãSæTë†cÁ§i¾šGÐ)ù¤ŸhØ|ƒ7Ýû)×c)žq?¡ îmh—wCgµ.¢³:O ;obð¿óQÁÂÁ`fìiZR`^¥+K1GI_§Ëq­AŽÝ?²)GUýÞz¶rP>‘VÜtšõ(®«õBoe0y&²ÁVp%«hl°ÁÇ|çïAeƒlˆg©°"hŸOITÚO5ï§*êìã±j€Âã¦àÌù‘5@½…:*Ö¼(¯˜~×9WcœßÇ[¼BçÒôÛ6!;k £iÏn²ÇÖWvÖò30¾†.pìÏ3fÿZ{Nˆ]?¶ÑÿËãý?¥×µô¦ž¿0îÇO‚†0xï1C?0-WÞ·¡ ¯-ŽÌÓ:ƒýYxhžÖ#”úŸå½­ášy¼;¶”z„òÒµ¬W¶m‰Ú+SÖ£ôÌšUᙩóY±çZè ÊÁ1óbœWh§?…ƒ9¨ïÕÁäÓHn›ÓØ{NÄðžFV~¼ºbPÏ7Út}(rƒÛ«›¶çÊ3cnpSTø™*<¹kI$é*ε±yŒQáè<ž7ñè¯â ŒÍã,~žÎc–‰ÇBGŒÍãþݧ‰ÇXç£3bòÐä|Eçq†‰Ç9*ÎC±yÙÄáå:o«#yìQqnŠÍã}>Pçñ¦‰Ç3*ÎűyÌSá¢Îã~Ü×U¨âüœ½oo¸ üW¢Æ`œ‰Áe*Κ Tà2Á¥&¿×rœÊ ¾P…:ƒãÿˆd°VŃÁ“*°¯Î ÑÄà™gN4„ÏSá·ª<”e‹°$<òªÁŸáiãt¸s7ÒÄíeb<†©<Î!ä_Uà »&äõ&!»ª8ŸØc ùó_m7 ùÛbòØâH!×n0 ùºÊ£4: Û8JrÃâH!½*Î_b 9^…ïar$r¼IÈK¢„<Wåñnh!O¬çÀ'zhB¦™„Ü«âÜÍ�áµ*|d”+iÂ-©&)äâõf!«Tby‡ %hB>ˆr”Šó^BL!/Wá'˜…˜ÝLBýÀ,äopã£Ã°mWYº¢IÈwTœøØB>ªÂƒÝÍBÞü Yò`¤EQBÞ¤òx¡{´ƒUàÂîšÓŒò"gB4„[TøÕQB¾£›”H!?{ß,ä‡ïs»E ¹Jîì¦ ù¥)äƒ*Î‹Ñ >E…/èfò &äù&!F ™®òCȳT`o]È“GÖqœ?ºÆr— ÿwW³S !g>)äÊuf!ŸTy,‰Ã6Wz´�2‡®£�ÊgR�e3#÷"zŸ¼•8aýˆÉÑ÷ˆ0wa\m"ìÆïR é§jÞ3F©uF$áW$OÒX•p“Nx#¼~h"|™f«„‹t á Fø ‰p#¼D%£þ{-^ÄÇšG0Âx•ðlð#ìÂÏ3žÍ›eNøý$ÁFø›Lßz# ¤ˆ$íT _Ð »2ÂŒð{9’p�#üJx—NØð.îa„!a#Ü£^©f±·3ÂFáv‚&mV vÒÃ,Ä÷ác&Âgá[*a­N¸¾Á_3–1ÂçTÂ…:a€>ÅO$áuŒ°J%­~ðÞËeSˆg›“tB#±ˆ0æ˜ÿMò$yT¯máFXÆBœj"ÜÏóUÂçuÂMŒp 1×Dø>#¤Þ©.b„­ŒðráŒ0E%ÌÐ '0ÂF¸gQ$áß!±EÂý¸ÅŒðAFøž‰0îT 7ê„¿¬a*Ç'›¼£ñcu½Nø6#<Ž0nc„©„¹:¡þƒYI¸”ÎP {ê„#᪅„ñoa#ÌU ÷h›ü3Ïe„ Ño"ìÏû©„Ïé„{ß&«Xˆ…&B‘vV 'ë„o0Â8âY&ÂÏIž¤ŸrÂtp#ÜJ½I¿/ˆ$\Á·¨„GDÐÉk™¨Ïš§3—T :awFx qž‰ðZF8_%ôë„ÝþI„óXˆkîå„üLÐÊ5Zš¡['üî-"¼‘Þg"ô0Â*áY:a#¼Œ3c„=T¯,a%# ³8v5žÉ¿_À ŸÕ ï`„»áç÷D~KI—ôJXª^Ë·M'Œe&ÂF¸D%ì§&1Âù,Ä|á<FX¬4Âýo²Te„5Žf„UÂõ*a ³ÿ½Oµ%2 }»Ñhðs¾‹âØ´bÎd‹/Ìl¾$qÛ´“KݤcŒ$â:¤rXòõûÙö�oµý‚^‚ð>M4ùk=YMk.…ìl:7b?yÒ—~42oÀœVlxÌVR²2jƒÏOÁÉRœ–êª^_‘—†WaÄ^ŽÓÎï<üš^×ö‹Ú–Çøu}}O@Ò<AŽ£�šVGîoOºèCw±e¶…hÏgö»Ð>–ÙAûf÷ ýf߃ö~Ì^…öK˜ý ´ŸÃìóü|Wfì~qFmˆýJ´ígö^hgG»’ö#ÙÌþ)ÚÙá°¤hÿ€Ù+ÑÎŽy$£ýfíËøÑ´?¨îÒá÷Q8ìš%ù£†ÐĻԔ(9Õþò<—Æ«$y|Kþ—¤<Në(b¼Ù—fÕ+h#'Ï0.¤1Í1þÄæ qjrÎ ‡«q: ‹Nè—²†ÝÝ‚ËÁ.y+÷&VÚ%ÆùèëVΑ”¤ TrkÜŠs­[> X¨º-ÇŒÃáê´P|ØÛ…_d—Öþz¯bs¼"Dí1‰´ÞIk)‰%¾Y]Ï“’’˜qBZŒ@“ÂïµY8ý@ýJÐÞàÖÉ€6ß¼ßÙöùËÑú»ñüÎ/ózkºvÞ!j¾u>z*áZÙùuÕÏÅùµ´LÚ´W7[§ ®XO3òx‡Q#–á”ñ_KŠ·Q*ÜŽSЗN¦„lŒÚú­Ô ÒìÕêòmð×o#8z_Þ@súûÒÙ°.ùß.ù3'VF‡Ùùv-ée«íZROrìL-©f×¥ö #.]Î «_7@ð”cò‰@[ì]²ShzÑ”_uô CÓ?´ø#— ¯Fñ¿‚AÆøÇ«üQ'%û 4”–9wYïõ 2-š9¿GÛÀ?Qåÿ°@»©‰ý÷­´Óö]êõàI¾¦“%32ÏÙ’}#£ÁuhÇ"°¼ˆÛˆô)TŒ–åJ©40MáLó¤eÄòã]&FžÆkÛ+Q¼ÒdÛ? „çÐQ(°ôÐ8<Íá".§ƒºaHwh÷˜On“ô3ƒº¨›®t™Fút4i_¾…èš±lƒæ´˜Zú´XÕ›áÑ|zr>…+q] BÅ|9Ð82ãi}ÏöìJµ-(Fòc¸U­ &Ç^?ŠÚ¯Ae ë'T¼þÞìv™©¯ê·Ë@¬œöw¶ºå¶•¿‰¹âŠ‘û ÑAñŽÄDþ¸(æ~!õ. duÉ.ùãŒZ*þ¸7(—zPIQ5›yƒÚŸÖmqîÆ}MÔw PõîÆÊiÉóápÕï­|b)hѪ­ ËùV/P&9^ág0péÛ»›_Ú_ó]ÂZÁ·5™–„ ûO%%Y /ãäZ'É_A0Á/ ¡Öxê#۷ϳú>ÅØ¾`åDy1z?˜qÿÇZÚѵžŽ¼Èk$¼–WrmUŒ©$çÖãú¹ˆ; ùÞoßæDI^: ­uþ2ý¤U©n¤ºZ.±÷Y }¢Ž“Ç­.ß܉‚½*YÄÍV7Z¥MßÅÑ¥K›‚`ßkeÇ£ÊèHÎz42¿¿½=ÜR†[Êpß%ÜáVíÈU. êPæZñNÖG-$×.Ó·e=z@q)Ã'*Ý«ý öª ˜Õ+þMâ+Ã¥‘Š5 ñО€ÆøŽ&Ùö¹^2ßE‰Ï%v„Ún‚0¾Z�çÁWÓüá+‹¼ˆ ïÀÃ;ôpï5Òà·‚ÛóÀÜ ùVß…VöN¤øgѲ·ØG`„è¤ò+h‡§T¥+ž ‡}-—Ù«î쎷06C®Ê_A2a–¼LªÈñ.%ÇÆ´ŽŸƒ ^V«”l¿ÿ�ͪ/©tOãæ*E6ÜŠk¯:·IA'‹@q”¤>t?sÝJ¬m[€½ñvw:¢ù%¾(ÁEÖB÷ÅKaÊ�ˆ‚Å‘=Üj_x/ÖÑÊðI¾–3ìU ø4C’½ja7´XíUsÁâ¨ÜÀTÜßÚƒ n*KI›•C°óñÂF¼Ÿºâ!–s^ G1jžoî¤$û‚_-æý“˜ëTò3-¨G3¬XkôÁqÏw ÷e‚ß!9Ô7hÑ™ö…0’†*ÖE®Bk•Û²òƒÃï: _ÃíîS|/Äø®³hù šW/ù²¬_©�Žô ª<ÙnæÏIdʹgýHe@hJôaážõ¦ú¯¼Í:c$Æ› L*ãŒï·Ü‰KUÒÛ=È``[Ï™ª5ã4ó ™ÈØyκ<v¸n"+¤”õ³C{ÂÏ`´n­—úŸk÷»ºc=y¢Òe%+¸î6èÍæ†Mû9’¤X1]ìþé¸[ĬTÏЋÐV£jI¦÷wºƒÊ}òOøÁ· èw:ßËw6˜ãà›Í¿“ð?¾OJ©Ñ¶@´™Þ-|D ÝÍR[+Æš ¤FvÏÓXVã¢ât(Ù~"vz˜™d>E½ Cw&@-¿œúañ”‚Ùñöû{w5q‘Ó£ÂÞy ¾ñž§RÓß  ‰Ïa9ŠÀxê("ÊL¼ߌÇiÎ¯Ç çq÷ÓpÆ„ÇÇ¥ŒK‚ž+¬Ž®Ry<C!’ñÒã§•®§b ÷ùQàqýqSEYZfdK¬h‚X °¤²h—Э¬œ-NdÙƒøÙpþ…É^ÂÎéÄ’szG×£ýQº3¿ïñ?”/íÏÈlO¾‡¡þõgåKù3òímO¾ƒGð:£R¾ä?#ßîöä‹òíhù“òÅÿùêۓA¾þV¾Ä?#_c{ò…ò=|äOÊ'üùjÛ“¯âïàáhùbÖQ?.Ã:j3ÛÆ.9°]u96^mRÿÀè6Tð‰Q¡ÿqy&éÛNÆJ•ã—¥ˆ»œUÑÑàÛÃñFN¯èèVa€ÖÃQû3Û?VRܹô´jõÆ®ÕAîýC­ÕcpÂy‡Ç£Z¸à•ŸÅfø 2¼ì> w’½¼46£dôí¡Èö&xÇxõVЀ'ŒÇALšÓÿ‹½êl<X^yÜx{ÀoÁ­ãi¶’®áÚJ Œ$‡|€|z<­-d— E�{nny%öãB¢67G‡¾Ú ôë,°ë*¢Ó„ׄV$%X¾1C‹ÝÇÿ¢Ø…&ä�ë³ß˜ýöRþìG,†´’²*<df d¥Æg.fŒâã=#ÏÿÒÆÓ²ÂÍ´Ò\en"ç«”ÌÂ}ºÓóŒZ8å>ER°A¾9è ô|¸LråáÙHGø¡jºÝÀ7lj/@š°ø¹pb„G‹nÉÍn‚´«ŠJ3{îZ/úEðïQ¨X§hh}ÄЮl/V>ȉ¦oX>{€Ô<0L¿ ¹{Wú`1ôÓï<G^h#jüNÛì ¦Å… ⺆מ¬žŒ(Öw‚à PÈ äd Ä¢¥ZåæF!&Ì«é¦èµëvt-'½ N¼áTóÝ4_uØ­¼„µÀiövË>¾äð ©ÍœÝ‘KÜGRþ©/5à:ó¦Ñl©Á™ñKU­'Cð…u?Q^ížÛÆùðgeáàMʇƒ—ÿ‚É4g7{B-i©ÛŒG±ÙÁµ6Ææ;ÍóÝò¯ê¡†ÙPŶj ›8ÂG*Õ‰£œ©xçÏõ$pèŽpÌýñ%rWÓyÉ\ÞwÕîO’sO0Oïƒö3œ'ç Ydç5€›½† ‹1ÏkšøÛßm@ã{µmÏïq±×™ SèÞd–öª·6KÈüï̤o¶•`ª°„¯§Ðšžmª>‘J[ÃHÝ<wÓ ÓÌ{‘•­*÷MŒ¶™Ñ&-»†:Þ¢åD¦qK}góãuØü!IOoÓ{r$„ ÛQÕðYÊØkn¥[®ÜL X?³Ðl@Þô°ù=>J&|áYÛÁ=þQZ!öô¢°v²e�¶×mࣴØë¯õUÒ¸RôlTŸ_‰|¯BÉL >t¤[;Îm;´”P3¾:?ÏpbyzGvŸH žÍlƒ›Fã¤á}©¥‚g¾€p>ÈÛ'czßE® ÕF*´qx‰þôÁù3¿A¥Á9£·IÁ n+cçÉ‹ÈùA*îà„ÑÔR$i^6äÝî{ó÷ãQ=:$|ï"“Yt †®¹ÌQ¦&æ,zMªØ.ÁíÅêåU”kfÑÉv‡˜Ý?‚ö>u‚z’ñ8¢¸»ÐÐ-dŸåb&°‹Gåm옒JS`ÌÏ‘ˆعw– ?ãw?{Ö¹hŠÏÒhæWóþ s‘â.Ç@ÝÔÿ¬W—k`6t¢@ߦ89ƒ^…G¥Mûâ‚}#°&aëö—\ƒ|ó©Ýô½ˆKrYÝÐYj`ûÜÆ8•æ,zyÉèÀ©›¨4/¥ Få¼D<q=X&‘³Åá¡¢IüPQ¿(¬ÏXs«3#úƒê 6ƒ$øã¼ÅÌ×¶We‹zà X 增ñLÌâ$¼PÊm9ž+ŸpÇMšCÄ´oèÏ9öL½Í&Nãæ(ÃYJ°*ÖDšßlpZ³§½ê²œe¯Ú/艢 8R±fáL³h¯ºÁb RÂW›<2ÐòQD„{ÕÝ8 ÇŽè…)ªx‹—²Ó%{é$:¸“¶ztR–²ƒ|º¦•ÚX½Åb*múr(ÀŽB~¬F:nbÛ»‹ µâhc ]˜z\•8î¦RÒ±_u”ãX`]ZÜjϨé/Ó)*Zø:œà×rãÃvsCM§Ià‰ á’Ý“Qž¸› Kî7 ³œAÝ>Îúס¥­45•Ê‹Kóò¸ÎÉÑyUgMLÔ‰bñ˜á=LH¦‰b¨’öOÜZÉ;’¤;x·š¡1ÑéXŠq€îÁõ5#Ù™­I÷‡qÍ$OUXUÙöáÞA:1v†Ý'¦Èü­Xñ&K+×÷£ã¬]•¥(§»÷j-*³Ë‚s\:¿º!eŒý¬Ò`±KW·º¡tl•s²û§“­IçònöáâéVÊ•KpÞ|C£úÃRLý¡3V_"°MGt=°²œxCaáÁ»•!Ýrˆé¥vZq¿FmU K ëkìLãzyP†ÎòµX=ƒ’ãIƒRã¹JŒçG 'uƒ¯%Ù¾`<­È…šÎŒhO™b`ÅZÉžöUkÄUl`Àä«ËÒ囤,ÍQ… ~ÜKÖ‹Kvçj’ŒöT²ó’£™º–´¡×‰b°w2kÆmÅšñ7 ¾¥Õa-AÇú©³ŒüdÇ,ŒsŒn¬.üÅ­¸/-ÀQ£Évñ<U„}“Ì$ÚV)Ý vã+Ó{Ýx£ÞdúK<ƒ ÂØ)˜0â_µ®UI™_*|¸qÈÊMü~�Ì­‚înë{Á€SeÂ/xkX™•iÇ¡a­j¦ õ¢<_…_¢ÁËbÒÇið1é¿?©ÂŤߢÁׯ„?¯Ácò÷ið‰1á…|HLø°“úýµÆ÷o²$yÎZ—üöŠåÜz„×ö’ßÔ÷P»[lø²¡³–†0j_ÕîLJ¶•ëWPu]õ³ÀV}µq1×V‹WIçZ‰îCCv¹« íŽwüÔ¿€®‰$å,Y]ÈuË­A«sÙYÆ BysB�N:áa¡fºÚ)ŸÈ‘ùU÷´/ÄœR¬ÕøX ÓÅù’¥tñAð_ĸ[õzhýÿúÎw�hàöç-ÌÃv€­eû„®TØB`FC¸z5“[Ùº gPÆžjùK—Òó3—âgÍÿ€x$Ìh¦Ò�{gƒKø?…«ËlÚ2YBܸim)ëyqæ€öN²u’-œT}9Ç^µCÔÄ çÍ2b,âÈCþ*Xw�/É´W=D;‡³ gvÿ<b`{ܧ¶&‰vÿÖ¨u7¬Pã(ÿ”ó7Iîê²7D­g<äv´)šlM7Ø ö”÷ÇÃuHoq\3ÜZÑó6”+ºŒçxä¹™ÉÝ?Xû>IÊÿåðÍ™eµW`——¦Úb `ØS#]“h_¸šü×`1pæd¡˜nù»àš,=mÔó_A ½sfõ´/xÏÂï£q)ãËèJ‘ƒ­úN�|/É™ñCÓÙ%Õ®ðÖÀ0Åîk¤:b|YŽü}޼×7èþ8ïÞÀ0ž4Vž·éÁ£P·6ÕE¦*ÁvŒã ÷Xgㇶá/÷2„-­l&FG X)ñ =Q<h¿©ö�ÝÛ(§„žÐ9±ÀX–ôlT9váïŽ@  Àél®'ÅHBS\Á­_#‘55tM{ìéfÕಯY�‰Ô Ù®ŸÏ¦Œû‹oÁ;?Fõ«šOmÛóX´04í¤:Ÿ8g­:ûª5¾Û ‹ðîz÷IÌ&ç,óšm„=îàíЫð¡ßé|K9î^0qi_„õ»O§pÝù+\s,±{>"vи‡ªé‚tã{b!aÙ.¨dIÖßdÇn…$ûö: s`ª€‰D�Uº2 58½’)BW±[MÓ$_V¼}A2¯¯gÔ\$È­¡ùMN¼ŽÍ;ôV•á¾WÏ58ÖÍã(Ÿs”Œíä±Júž‘ôÄ\îû¼Ñ÷l•GÀèÛ8‡K»M¸Ÿ{ŸýŠÒíàµÿ‰'T<“VÅ=_‚Zd_ÛιÑéøªÞãÈç¶VCàýç 5K ÁYÅ€RÒÎüÒÖJhJI²·Ò…¯Þ~¢í_öö/fóJ ÔÚß©¥MÌz¯¯sV‹¸±]½l†ßlá8œ“o±ûë>ý4¾ZeAsŽvÊHï z’1òr¼˜›Ÿ?#œø0ų>Wnœ¯ÄÛT ¹ ~{„éaÜÔj¯úik½ ŽõÁ°ž¹Ó¹êÏ©v:ĠÔ9k˜ñ¶…Et-q àû‚sÆÜu®ÈQÆ¿íôowØ—Â; Îe9 £NÜ\uïÅ4$FdŽ£ÕÔõ>çfj¸‚÷¡Wa÷Ÿµ¨Æiˬ°q<fÛtS ã~è•@)ªê<¤Òì>Æ–†PÜ•Æ÷%µ÷ápÚÌöô’|óÏÙ{p¼¤nv”•tc"×Qõá8¨;²¨À˜ß4'§Zìï‰g/|ž%…?’º4Ò#±qø|g‹'’•£•d‹/±ƒò0ho¥âMÜÈï Éwà­ â&þZÏKM íï‹ÅURõ‘ft£ç¯ óoáá"M>Xf$È-úÔׇìreg¼Couɹ‰Ž:çù”¾lÁ“Ÿ«Ç;”[­þ=ÞžMWëó{á=}èæp›Äý5ør+‰û#^ý*DQçLf“�¿æÜï‹ýŸÏ‚w3º¥MÛLù%®Šãí9vk³Ìì"ÅåÅKÙ;ËAy‡P¦ü¤¿÷ê’3¶ãþ·fßÞΞî8óyä+ßwiÈÓ½‡«O£$IsË#Ý0ÒñÕ[°ã:².Ç/1š‰-œ<"½a ¿Ðö7ô ô"l¯Ü‡½ÒF²¿bKšO[æm óqòÔ×bóv¯³ ”„·,r£yþ;ƒžážMÕû=ïG 'Æ{ðRí?` Ó€Ýöù¥u¶Ý`ëÌt]nÁO³1§?E|îß ôÝ�¾{a{ÿI6Mòú+U÷³iØî]ÞéØ<ÉIeh††%ÿfoO𞨻íU Aðp–y?Õ,áMa`ðVèl¯ÞŠ-üeÛ«àš¢©% üZ’fsÓÕ1ÊNË­À+c›æÇûò7Rܬd)ûOw¬î³°Rôò« Ï8¾›E“¦û× ëo¯Š³PΣÁÞ»uØöëýÐY°ûiÔ¸ªÃ¤¥xOö.ûý l×£2¬e•h¯z€œ›\¾oOŒ °¯²xg�¨yTþeØ ïSçÊ>6ó pY6I×Ðþ]{UjSÅI¾– í¤ã’2:Gó’Ae³Ú«î†OÀÊÆãCñÞì#f×{ºº²?šy®”]ç±!_»$φô“XRíÎÞ6sdpôÕrÏžd—aã~ªÀà‹ VÁ§¯¦&¥*5lÚÏñV/ÔlÃãWY<¯Cý4’ùÜ\}«•rô£¥’Í.ßw'Ür£#; @p­uF2Œ²Ø«þÚEQÙ\5F-öª'0-”¤è—}̾ÞåÊÞ­SöûÂËh4<"971×ò¹[®Çr뒇đ»Ç»³mV ó¦¸å„ïçBÓd肵Jòðx3"8¹t3§È›TAöÍAAlVlËçðešßùíþÝ.²m"ࢠŒLä!ÉÀ.Ñ%—%CûqØÖP‰JREýyÐÿJs†²¯ÂŽÇÒŸ)Ïãa ZÿeÈ»‚ßT [^•Ò`œ±±/ðàXFž•媒F㱺!)h*¶V(ØþFïˆ.›å¤÷!ëN Kz.È_"»¢?2pK|Š«O--Ô>AÚà…à>½¬`è:šº*£Á¾ÐÓéóßÞÌîs%ݱ†²M]éþYK=Û £AßÞîìz{Õ3x³Åç:)pß¶IÿüÛ½;¤ìÁȾg’Ürζ”Éè¡enº¤Ü˜Œ¹J¯óI8ȶ "ºatÓXdóÐDº  fP)7fá›iL_¥ìt{ÕŨ[Ùƒ­D× é²g¦Ø«ªizLš„]íä°÷^ÄÙ‡]Hy6„><S> BÝFÄ% ¡ilÉhm/Æ« # 1B÷aä”|@ŒªZu‹…õ \Ù›í éy"= ¹þ=öª;ÀO™€QÌQ£e¯:jÃʤ—ƒ‚°œ}/Da'í1Å¢€ç# ½'õ&ëß!äZâÒ5" #XšS¾½1‚Ç QØIkñðר¶ Ô‡‡FB‚o`-¶5›~Kôí‹—îØ¨šÁýk¢ï'«ü;¸ µà%ú¾×õ�…ìs¬Ï>íØ»)h·lŒìLúôíµåá)Û÷Qáj$•–ß…RI;ê¥8ÛU÷P«ÔŽ³Ú†k³äû lôV¬Bè)êøC~0R Þ³C¨4Æ÷Ù J0ZðÍN¼ÀHúH¡IGJ4-ñ˜Œþä2: _ 悔ÈöÞlPÿÒN—¤¸¤wfSøÆl’£–\jŵˋ³±š;÷o]“ô2:Ä‘`™ .î?Èv MA·ï˜d_HWÇÖ9k©Ã£ô>ÉëÿÁ^u•gº evcÊÈÎz¼Ïr±©®%äîó·É÷±ù·\µÌ‘ím.†u8å,†À!ìwå*SRãsOj"nHhÁNO½Ó8 î÷]%z·¸ä:—o´P=¯Èé·VÊÞ\þº”Ý«nFï^ª­GþŒ½þÊ^uéõ0,žËcá G ºíoðÜ£ ËreŸªÖ‹Jc B›ƒhÙUžF‡®OÃÛór¥ì¤ ~+±  ÓÔxA†d(¨qBÓTèíÍ i¥u$<Ç™[\{«6:Ö ƒ {ÕZ¤jÁ —öÄÓ°)ã0ÓÀ{³¤ºai¬ï2Œ×©¯Ì¤:uÖ©OÏÄâw~3´ ÿ ;^ªäÑ)ëaé’|3ÄýæÄÐ ªÉÆ‚V¢ŒÛ½‰¡}"fÑì/á–仇éã=)‚§x¦4… 7Τø·`gŽÔ7´†æœ4›êà^w2׿-¼æk«þ=4C8Ž{ å[À[®Ð¥®…B°ñB%á?cs³ïûÿßPô[ô¢ßª—w™½®P‹óp¬$ŽÒKb?8Ô·•º·×Ì`g°&skè,Jêáñ¾½ØlÇÙýçÁÜM8îÅ‚VßÊ›xè¯Ç–»ÛCw¶ðºMIÚ‡8Ø}hÂX;w‰‡$cK®¶âд¦SPéD9ru8Õ5ç…Ĩþnt}ƒùë`ù;ÃKúXÏßl|‚C.5xî#Ef5Ó…øÆÐ==.¡«�Yóé‚>à”%ÞtZë XÏ ½®<B©–äeãƒPåúõ)|†’àO‚à< ¾¡íZA%nKÄgÆ@E’¡$°$X~œ,ŽA/êÐËôÈBÒhŸN[DǦ€â§Pvž… 54u¶û<Zöý[CŸÐªç˜”Ð]ø†<*%D¥X‡‚ÜßU†jëÈ—\ë ×»ìo´Hòè• >0‚'pãÎÎI0ÜË­µ/|™ž«I„êóq*βa˜Ýl¿?@06 I—ç—Agà\^péAÖû¹ 1|sj¡º+%dÀé3*^ê“ppõ?WW`oý#<×TA½eŒà‹ Eèj" qÇdÝŒûÄ#Ô@ïæ{7nÄ·µÎÓÇÉø›K>"ßê?õ î62Ï“-)½6—ã÷€ •Á-Ó ÈM—³ú“ÔùÛ¦¥¿½ÚBÖ=/©®³-.gIíü-<5ê|$Œ]ï.§>Ç—r߇ñŽð¸¾¬ûÈ€ŽåP¼‘È•î›AóÏ Zg †cŸª=ÞµM]Ùø�ìßçȽÇOã÷8æÿtò7<çò©E¿Õ÷ÝQßGV—üeŽb»r^ü»[nÍ{]Îö@°ž[$ÿïô ÇcÐ=7C3a Êͨ)Èâ·©Ä»ñ^Ö9õÁ_Æ6e!Ÿ›\}>-OÉjq[ûUèk¬*€÷åÊà5¸ÍmÜe”*¡)�;ÔŠ‡üz/ŸŠQ°Wá}¡•سüÀT–@ó.Ã-ûCRëÑŠ·¨O/eðdŽÐ\¡Y­1îãnç½=Èj‡¼Ó!ÿ+WþÄ7¸°‚.íc5?ÃuyÏ`ßàNèí@—ÂãË{<¶±è¾Zgìÿ Æø?í#fú༓¬ðˆ85}0"m^˜AzÙ,ëä ‘½ŸC[é˲ثFâ @\ Þwé† 6aZ<.ÉÞzêÅÆÔö2SKcê'*hLkÆÒüãÍYxá?Û¾ð5<…÷¶Êë¬ØPá?epç2 µÈ¿;v‡kãØ6‡¯Ùâ€Öư^E#¹^e4O³Ù­ MtaM�5^ÕLì‰eÛ¾œŽaHÀÈaß</9Ð÷ár£¯>jèëÉÛ]>wb¼Å˜˜ãÿr’•ÞUòïñŒÅkЃgŸ`mÓ„®PÀzÔ"õ’liêãÁà忲Q÷™avÞ10øMàì›ÆŠ3¾¨œ±Ç2 Î2”iS{3ÿø<ä´àŸ4™‘d®U®ì/ì ¦PÍöÇ4Jü/öÞ?.Ê*{�¾Œ† îR‘‰~3Ä ÛAÈ�ÁfdH*4* B" :¶ƒŒ‚å,R”îF™´Öº+íº›•þ©l—ÊÝ¥r[++XÝýÒfE®9ï9÷žgæ™gžÆï¶ïûÇ+ž¹Ï½÷ÜsÏýuî¹çž{Ÿcú*P4œ²CØGÕ<ÁàÌo‹¼Êäœ0„!¤î27µ8ëLmwBAÃxA[¾ïòµÇo»ás[CÖ«¡óí{Ÿ²¥­Don>>м VÖÎýÎ>þ±]e|fëYÕx³ ¾u!¼¯š¿©t3ß熇úµ‡ò»¤Om[ÿ„r2x_àêµ_iÕ9ßúMò´ýˆ(%Ák_Žóó¬™oÏt˜¢ /'ùÖ¹9¸’ú.65xSEN+–ÿØ™œiÇ,É}Ëõ9üºVõ:âj½/ÞÄÕ7æ´Zö€‚†]fçÙ»°=±LûÌÔ:arÙô¼¦ÁŽž ˆUœUÜPÈQQ×:“3u¶¨ïb¨ï;¢†·|ë®Gh¡™� m¹|+n„¯½ŠÚ:ñÒeøiX}Ëd¨JçÀ׃“^Â&×ïîÕgîwõº`Ípmz ·l|p¬�äÓ3–iŸB®'¦zŸOË’#ô (k8ÐÏ·_ ór¡ñxá2쟠P¿G[»äoôNÏØÍmÑW£®«9z<®`nò,x?“”áÉûô~…ŠZ@MZ ÒŒ9Ùþ&ŒøäqØ+ʪpàýoBL¶Áª`3væºWi<þ×ìÌðZÜþ€Çlä9H«‚›Ü«].bùªÀÔ|öJýƒ«"°A´?kÉP“óv¨K„Nƒ¼©ù@”~íä3žóŸ_ ]{}d—MKÝbÌsÒ+_ †âÀLãušá$h˜¡ÿîr}ÖýZ#Ø›%yêuK‘¨#¼iÃ&-+گߛßV`ø‘iò;–äõ`| ˜Î?übHp»PžË0U>÷Ø!ã†Wë‘%‹Ð¯Í‹Ä‹f¾öŠt@a”íï“æs“3YÂÉ;”y‘îN}æEQœö”Y»—S3ˆŽ>o½|ó^ÁzBÃþÍûjØ)tR¾DG?/ì"ôSî¼!*í¤ø*ëü{åe2S™Ä›†^¿ §àwàf¾?¼û´$K~²ä(¬ï!åð:ýí•¿¹RZyL8sn+ß\è»ì8FËŽØ\ç{ûÿÍ%eõÊÍáR(UnW*b%|ÅmLÃþ·ŠW·ó†J¡Va÷Ÿø^oÓ6qÊ2\„ÃÐŒàçfÇ›Zë`Pkgš“ûô-wó­»[£p<}œÓ¦dI™µ˜_‹v8fQù¹SØ»ˆ7†‰§b+¬jÿŒ÷QUâ`ÀõíÂmC£Ÿ¢Âɦ³„Ím7=‹ü}ÍFˆïÂm¸Ø],ö-mÓC̰Ø4'¿š£Ïø{N[Ê·íÂ}¶¶öUH8sz8ÞOñ„X–îÑYÍUüÚX5„Í«W%™±Gµ†ÝÈcÒ1&U¼ÂjÖ¥¼z®¸¬ ‡g^b{Ø$ž.k×ü”L6Š´‰OçšeNå³sÂKQÌÍJ„µï¦Í½:sò¡#9ɲޗãÊ™6ݦ 15áÒŽ”A·ê,N†›5nÕòZ4•v+p?]‚5jÝŸÖöþ/ îMaBƒ{p ipEY€Y™Y™u1P¢—– _:¨ožƒâ|/tç[fý—eÜëú³†k]t'`Òßõ™'±TKro}8Ì=¸vjņrZ—Ç#ü Ÿu:…Z(‡g? ’èHç+Í 7-ÁðšþÁ߀œ³�¦œä·ôÍÝüNÄûÍ­·ÄX’‚ë¹YOº`·j=ì÷íé­fcN²ùí“¡\)?RŽë-"¿øí(׉.róÛ?‡#ªƒlÒÝüö÷<&Nëá·ëæ·<6\ëá·fÞÍPCÐfâ·fç-0½Z4:EbÏãáËG`ä¿„{Îwßù'5h—ý™“߃ Áß,BÀ«lxš¨p³•Lg?òM¯sÿ×ïÛŸþαÌÉûURf¶¥¹æòÎai»b\9ö¸HXCÞ=8ƒWcK††Ë„¯–a‘®ãj*´X3Åd´iùÅÎ}ø0˜QaJYËD>—7[.öÿêWØMþ0ü—oiY†“C†n~[*Z#’>ÂäLúô°Ü2¡ëÏ.R€ï€{ßph›vœ³7k û„ Ë,Š9 ­dŽëu†]ÀïìéÏqÞâî¸9ÎùÐü³¾)åÂÆx´8õ•›³´¯ ÀºNmˆ×ÈySã9–¶°í¥X9Ö^9O•z<êŠ#ç'¥ª#gøJ¾¢Ú—þ·é_¿•>ù›Lç„B�~ù¬¬J°F$RÌΤâ}i¥¢:XZ'ä”zWPtå´e"EzoŠî'ŠFK°Jzsœ& Èƒœ)XTɧ<fâ×à ëù·7o‰7£Æøqäp.J€ôs0¼¸õ)©i6q5Êí¤{~»á²á¸p_}P`|ƒÌƒïTž´…9yˆîçú“egÐÞÉ,Óÿ í[ÏpÈB9ä;_y+оZŒ×reX¯[Õy:D¦êLwÞo4Ê ý£ \U±Ö°½‹Qש_»÷¶ö;'þz1 ¾y_‚à¶y1ö†–TxB‹%æ8o'jø‡’F)%ìÑŸa€7 }vcòÜ‹5Ìüj¦‹r¯SÉ=r?*rO\Ìç†RÄ= ¯—ñ–Qr!KœSa ǘ“ÓFõkñóª`™þ¹vòŸõ¬GÆŠ08TC¯ø XK­³NóµÌp†ÿÜ(ä´XŸÁΜœ Èš™3ѤÉ×ÍìæÓ)ά¦¯Aa8ƒ÷{À‡ N/hÓ^ev~“3í­œi¢¨ŸƒK«ä–ãÂ÷|ýyC±ðð=¯›P_gv07|Æ2ˆ{x¾ÈOsã.ð ¾ ü¹¾å±Ïžé™",Gæþ4nÅÝÇ%‡‰. „™âÁ{ÏÊ7Mñ5“ó>7GÁåÊÍ»¦æÅn©Ê¦¨þÁáòyõ¡"÷¼Ú\ä5#i,V1µQ3,+‡_áÇÁ¿d åv¯?å6,Ã)·¿_„Üz-ç¹y¸¹XçéíßøUnŸ:GÓr»WU¹ýß“›e/Ä<+¬„ËDá%ü¼ø$”‰Æã{[ô$SJft³Cgø±|!7<q2 ¹ty7:ΉÕÂ),¤:‡¡Zˆàb47š ®á‰ÂÏ6Ÿ64|âóÞ„C_….~%×/2 ‡rV˜Ž£÷È=8z:Êø}.¼# v25ÿQ3ÜõJȹ†á{þåòOòD§1‘Ny_[[Ø‹E(ôEŽÎì…©ZùŸ=ÉÓ CôÎw>ÅFþÊ Ukº§‘¯åÏ#ØÈæ6û>ƒ¹5oŸ»‘ˆ™=Ø…g³ “§‘s€òö}ŧ‘Û¾›ÍK©‘“r_‹Fþ9‘ŸÏNúØp7Ö…ëâÌi>Ã`†Êðg0t W‘ßN a=†¡¸Uò·ÜM*y\ q•<2Sœ[qŽ•æW`™B%ÿÂWÔx­¿7Št0Î×H)WRJ$ÑÀòÊ/Å>«ÄžÑdOÆžkUŒ ž*ìùR`Ïð&þó=Ø»µÈž\ gÏižÝýq|ŠX+ônѱ”ï'ÞòÖÄ{ °‰‹ý»~ŒÕ:KþMûâp$§¾¥oyƒ f³¦ó?ç;‰«@†]ÁùíÍÏÛ:³œc­Mäuοҕ<ñŸwaLØ®¹µ ·Ç ;p^usˆÙ"!†ß=-mKÿâ.>&»îâÛÁÏß%ÔfÈ/‡§ðèa¯b¶Fg}8Ûçç¤+2Ió‚Œzª§Ç[ÛlͲ· xOã´üšÏ?™û­åzj»†Ÿ?û»Ù³ëWíñuïÑ?z'¯ŒÖ;yetÞÉ|öèõ§Ýœ¢áNÿÄÿÁר69µoñÎg9ë-¾ü ‡ËUîÛ½uµ˜ ÎìÈÃPzøkiç.ƒvîr=;wm|OIh†ÒMÍûãO\è–ÏЬ–“6!ŸÙ„|–Ë峉}w`E&ñ}4~Oëp)7¡(=`2œ8ÝÎ0Üò-¿“9‹GO|è¼?yÔk¯l1†¡ Ìý™x–ÝÒ:ëì-âÔ“ø8ïUøeµ’cÀà㦑 9^ çüÀäül赋¸R+δwFyy%ÚÛ™œ‰å‡Òtùù}iã‚üæ"üÎnèÐû7ôšZš¯û^Þ× M¾Hõ>Í¡[.úÅ0)¼6œ,·ãZ:«/Ùg*ìé¾09ß*Ç[0–ÇeÍTê‹3¹A¨ÜÄLÿÂMãaæãÊ�ÉÚ¿¼½²Á'‚oƒgz&ž ô¬!·ùÃýډО‡Bqÿ ëÌN¬=æö ã·[#4ýxÉàà èPxˆ'v7ŒŠ�‘È ðG¸õÖVaÞ÷ÏÐ,ç×éÍg™þ¡GQÐ~;½mžËäê•͇‡´x·ÃÏ ïoøÜÒZenm7(nE&€g¥œY»dö+.ý ·ë\¯›JÞ6°´Á²ù#4ÜhmØ5´‰ß`»¯y$Gþµ×‡þÊÕ(\ÙŽ_EMjF}ËL׉kDþÎ׽Ǔó{.˜×û´ææ^mfòa­|þ{é­ºä×­­}Íú°Ì×›¿/nn>=^lO5¢oéà!!éúûÆ5£Õ·< !@1Zç4ŸÖXMY-o7ÌmKÿ¦y4dùâæÓÖ»›Oë¬ùͧÇYoãz7ñ…p÷~Ç¡p´Z=4O«ãµu><ƒŠsþ½Pþ…ãVÆ¿r*jÎZ Ëwíð[Pþ—ð$ØðÕ�Ö|Z«_[è"•Âp4êVý°y4Rÿð¼A5"§ON« §C¡¡ž†^† ðüI³+¤©! šì¡´V¸„×J"?Ýñc­É98óµ™_¸–×w³QgãÚ—‰rºo·¢ñê´Z¿ojkÒò”_ArÃÞiÍ›t¦ùt(Ô÷ép뫇ðþhvb³}Ò™ëËÃD°”PýÚ>nÌõ×x4ÿXn½lø·gh?™O*ûµÍ£á°ü9ƒ¼²nn½d˜Ÿø^ÛÊ Ä<jE¬£Qú‡?…ª ©¯ó:¹÷Œ¼}üß÷û…71ûÒGf¾=vFœØfÂôUÿ?âàü`ækYøÛ÷x`°yθå†z#=ã˜]‰öA“fÔu¬ýGàO¢§Eö`¸UöÈåÍt×>×>ìÐÉo5<•åÌaYÎpy<nl¨|åÙÍ¿Òõ¿?râ=OyÓ]½>ýZTŸxN”àÄÕýû|ühfkS”þ…Âñ™úÞ)_û¾~m?¼øZVk£vf/ï7Ÿ`-gmÇø÷C¿šù>Œ|üxi¸‡ñ@¸=ø…â©rú3ý¡Y‡¡y›òZn]ØÃ©É ,À¹p{ºë ©ù€.+ù3ë}žïUgéL­©1Àºð«¾ü3Èí™Îœu™Î…Ž¡k¹M}f+x[:žàI1úéLmó]»¸ 9Œ[“~Á>Së…&×>sÛ"gDIO† µfm‡!"}õë †.2¾æþWo–salº3+?~|0_n9,+¦‹Ä «Wg'íÑoi@Ö…B»—5óíaœPË›ÿ=A¿ö—(0äô U}ˆuV[ú·Í£¡+ -o[„4p)êªÙ¾c‘éãöCõÛñ\ñÛÖÏ0ºö‰Z³v6…¢ÕÐÏöŸØƒÁ͉CÊýûÖpWú‹Ò©ˆ0hè0‡"Ì/ƒ ·•¾)¤üÐMãÍ/Yœ7E¶Î‹ŠU�°—ö–‡}c.wÞ Ñßs},,”A u.ˆ7¯þE³Ï¾Ÿ™U;÷ˆË8~'¾yûS~'ƒSo {—ŸÙwŸæß˜ o‰C­eó~É…¾å‡âØ0"jM1…ý{×uÀÓev~f^}¨‡wOþ!íX¡¤idò—@aÖ:s:¥[BZÓtøî™½xßF;òÖ¬œ™‹ãNY3ÿaj[Ó¿?ÁÔ–s¯jâ¯ØÅù)t=þÂuó[Sÿni½ÚÒºV|±™Ãòq#$ÿ¾°ø\ñ‘adµ‡fD~÷p/÷n ÍêN¯øyŽŽ_m1¿Uûwˆý G„³Ï"ŽöÓ�ô’ÇÒ›aú~�eš¦ã¥oÊœñ&_nͦ:ñ¶@žÆ¡3 *—sß_õ¹¯5ʯƃ? †º^pà{æ¶›`è0¹ð.\Q®¤!.þmèµÜ‘MâÊê0»^…Á”œÓ©_‹ø¡J¤V»%¶õ¾Óê>$x†…¾¢L·|à§­-\½¶ö&æ9làr‰×™`¿le>ç¯û ÓzÁe³rM%@ ÖOhVWzë÷ø†ì¡4#¢qemtemG!$ùîíú©ÌÍ}:sóIÝp%ª©dIÍ>IO0•4¶óª=¤Š“¶ÔÑ—P„07ŸÑ™õóŽXœ_YœkñÐÀÚ§»²Ú¡ðè=ñ £)–3<pæ›”FÈð$ú2Ì}^L>~ùiQgc‡ÉÙ�k…ëLN<6už×˜]ƒ¶oMW»¥ÜŒ*­ŒB1XÿÂ�°ùr\.}Á§”¯ ºZ3´–ÖÅ:×óP×Ëâ”[ ¹6ÞÀm‰‰XÚrzœ½üX˜¸¦Õ~tÔÓl|¤ÁBÕ"FèP$,Xœ}h=Ëýú–V†“Ј4æìÜ;*Æ\Ó}Íg5ú4×i> òáǼï¢pð¡ÙYú×QXÓ?ÔÌP0>/ݯ]rË™îÂðëL­™°ôÑ€xœüçUw™š÷éÒ[µ&½üsI_º«O�j¡IÍŸÍ­™GGõ/äªÍV îCpfgæ_GÍ­yGÅáº,Ž¡”Mü&˜µ£Ü„¦¢VS¼Åùwœ ñç®\Lj°š³q;€ ãõkã|CkVM#péYšUV$Ë ?ŽK]q•œë¾-3$‘“õ0Œ'© ¤È ð”¶×|5lö0O©%aâì @熊¿Â ³!AÔÏÐëÚÓùyGâsÃDùSN‘ Þܸ&Óç¿E)ñ @+›Ý/ÿ>òBç·œmN½o¸ñ[œQõkë(8D—ãE!ÇÁÓ£,:Jñ#¼TÔi‚i¢‚¯š§sœ`25·Î6·Ö0+,…æÇä´frZçÇc/GÉÛܶ…³+×;Pí1P+xF„”eQ™úß¿ei»™Ï‚’è 9š÷Ò]ݾxðœÖƒð …ÄMÏíð³„`VÐó0¹0~ÝßíÑÿ†HÄü¬Šp_@·¿i‚~Üí‹Àýô¡#‚Ÿ/üB¾°¡ûþ æXãN–›õ«öãÏ_ïCäÏ0ë³öƒµdú¬·áMËßöÃóŽÉ KþEº}Ñ_l¥ãµåéúšŒDüŸ†¿·æâïEø»¸ŸCºñå7íôòR£O=}m[ãø‘)gjœÉiƒ–È‹ƒ&±ÄÅXœ™q ¼Åë×|ÂûÒ_Lm;ãø¤sKÛú¸xº 5[«¢Á'Tü€OÅÏß"ÌžjxÐ@{=7Ó³ˆž{èYEny»Û¬í÷™qCPŸú„ÛŠ߇þ/ôQøÒþ/r߯’â-qC¦Ö¼¸‘/ô«,q#ÏR=¿^nÒWï{–q]#¤€ ý_®oÊŒK„_K\üæÅåÂoA\ü–ÆÕÂïö8^Ÿú¦qíâew¯Ï¿¢Jgª5 &‘©Ëù¯ÿÞÏíüwÿuðß5ü·…ÿ>À×ñߟðßõü÷!þÛÎå¿ùïÏøoÿý9ÿíä¿›ùoÿÝ·òßgøï6þûÿÝÎÆèoau]YIÍ’êÊûËJ 7›7”——Õ¬+kËØ½ÅË‹gTW/™‘UWWÃí†o®©¾¶´ÒX ²²ÞP]c5Ô7ÔÖÖÔYËJ^ORsUUÙ’âªôº% Ëʪ­Y¶’²ZkeM5›[\}µÕP\UUSRl-3Ì5Ô[ë*«—È’.h°.(Ï)[VS·R H²¢²T–®¤fÙŒú†ê÷VÏÈ­©¬¶–Õ±øùÓ±ÔÊêJëA—ÀP³ØZ\Ym(©*®¯7@ºH—�é¤t2"V×[+ëË+ËJ-•ÕK!óYm‚)ðÕT- %Öš:C9<ð{¥C\È ªÔ`Î+¥J9–TÕ,.®2Ô•A{”U—”ùAq3”cy‹7C4Ô–B ZŠë­òš%|ŠHò2kEMi`¬ôoñÔÜ\„M‘,X|/tœ”iyS‰ô9ŵµe¥)¬¼®f™£]õVˆ)q“ãå¯Ió¥kšO«æðyØõäë *_yYójDÔÜšêåe0XêTkÇ'ˆYk$”è’€‚ JI×<wU¢,`J¤ÊÑÞÆCCô9ï6§ÎœÂ–”Y±"ÍÕå5AOF¿ýÍ;'whݲfŸÅ0@”ãÇCEà¾îÉ^­d‹&^µZp|Ö— ÆU·ˆ7Xkn-+.UÐí 6;Äs{]¥UA …z;.´*è[Y‹Øê¿ìcqH*Õ—±rQqUƒ_‘²þ¿å0æ£ÅÅ%KÏ©ÿ&xQÃTÞ*ñó¸t«µ¸¤bŸ7ëƒÉÇMS@¼Áãq'IP¢ÂïÕ:ÈÞ£RBËÀ¹ÔŸZÆš'dYøc¾hãÕé½Ó?›×P]‚•â=Q¦K‘&xò¼êXo�Úo¿MÈIAÐ%‡?¢|æw½©†úéß^ÓJ^2§›êjjSÊq•ÅU «ŠàÀåPB_9±·ßæ+]ÊEµvöÍC¯6e‘Þ+G9)fhã%eu\fñåk’D„�G¸—Sƒy¶*ÎMŽ7Ž|,&+G]ëKÁÉí<‘|hˆjÇ U…ÏŠ‚Ç•—W–H<èåyw»ÎŸ+Êêå5KË‚nOï¬}èóL]óæ™±h’[Ï–×T–²òªšb++­iX\Uƪjª—ÐkÔ…‘Õó_|Ÿy=÷€ƒ¾ë~È}à ïú$î»>I­‹âÈåí ‹âC¹aYe}=V‚át[8µž™«¡CT–êaˆÖ”óYØ0•ÔÔúÙ)Ñ"#*AÙ´š¯½âë¥Ü ¥eå|Ô×T⅘αB°¡ W2Ó ¡¼¸² »ñSK§qrjëÊj K*Ë©ìÀþ ó¡å°¼‹«WŠiõZoˆ_Vl3L­‚´Šu.¬¥%ð‚Ú²ºb¤Â³Øu׈(f%J¸lö0©RC±Õ»|ÖÑ·+óà¥E¸‡¾e õVÃb¨ ±H‡^[Q ÕPl¨­«\VÉ«›본®®x¥Ôî5 0(‘"…Ìd)ynŒ¸ ¶‹¡¾ÌÚP«Ð#ø6Øl,Ð]SKïžZïÛÏÝ MO±Ø­€¶®ZÖ´~«Mà›súl©ødbÚ_ð~k ´§úEC5ŒšÒñÔØøn˜Ÿ~{ž¬ñKëŠW õõ uåÅ%^|L%PàØõ4Ë¢çÖ”Ô@ߪ,[! \p›!ßP[Ul…ê^&RL•ËynG}m±´¼ç µzìêP6Þaåó{RÉõÉ‹KÊ“_—ø#ã¬΄ÿ™‹gÏ*-.…·2v]BRB"[˜7Ïȼ'‰e0 ïPÑÌÞaÐÆK‹—”Ñ€:Çv­ œ ™Bš”ùëÊÊ«�bFÏœ)y¼¨®¬™!†„Ì¿ÒZ¦ ›[Q\§ »­¯2f#e8­2l²be`&gÇòPNï"äݾʇ¼;r³dP55UeÅÕò(‡¼~ Å%V/Ô¼rž!æRY/órºe~A2HÀ€„ú3®JóDjŠC•Rüm^�œŠÆ˜ø¹^Ñnò cã3¼@°À‹ñwzÇŠ "�Œ‹ŸçÀËFÑŸé-ŠJñ¯¢µCͲښj`cB/„á ÊàøiÞ©Iì`ÖꜢw'H~W¢ÇBÖ3œ ù1^EŠJ@áçèæBvX«éœ­ ,‘Þ…)¢¨ä<R«–=Ú»¬ê™z >€ÂËæµäôò2›A¤«¯”Í\ n¿˜<ƒ5AðßååÀ€|")Ø3˜)2€à ”·1Ù('hŸ¸™¹Ç?+üÐùÌÃVàžÇd ƒ }B<à™LÎJÞ7ˆÜÉhh$a>ƒ×y¨È7—¹‡!…ðãÀ&?–^‰hòI4‘WÖYWÖ[Ë–©éç¤n¬2P°KÕÕ€Ød]ÉP¨„eXM©ÄÕÅx•MÄAÜ„ðÓ‘ › € 'È0q&Ï„Á“Hä=ið0ï¹€)ù¾È€sx7Œb¾àaʹÆÃðÔX¤’«úrA_Ƭàឬ}f%·ôab>‡¦÷eõ|Ò‚HdæLÁ§Ö÷Ìû¾p’ÞÅ œ| [5õ(øUW—V•Õ—É @L-'ÕN’‘Щ‚Õ 0(‚w“QÃû ¬¹¬Ú°¤¸n1J0%5UØuÊJÇKý–§k µuB"E9t:_²€HKå¡Ð^…@1M[½ñWn‰XŠq‹º|E!!TÓ§Ä-¬.)nXRáÙc3‰ºSYõJt©*fü"Jau>Y¸Ç»ºþÇK£S³¢ú Ws+3’ðø¬Ådµ[íY~¸β՗§Ý«©b\1”–ÙÜË*/x¹D%±l!©\¢‰í1\áK©d½Ä]W$<{{ ñ?œæn9yÿákX1 ¼FW™T{ÓqY`­¬†•ýÙ¸<§~7›÷뼜ÜLó­l†uY-›±¼¸N¼”–-ŸQ_±Œ™äd º4ÌÛUþyâÃÆˆWq»cÄëTãµnwÜñãUãun÷‚1â#Lj¿pŒüCTã=éCUã£Ü®Ö+ÜÒvÅ#Ɔ#cÐ2èŠ=øÎÄû³ø~êûüý§ø>$ÞWãûQñ¾ ßÄûø~P¼gáû.ñ> ß·‹÷‰øÞ%Þ5ø¾Q¼ÿãJ¼ÂC¼¿‹ïvñþ*¾×Š÷çñ½T¼oÂ÷|ñîÄw‹xoÀ÷4ñ^ŒïIâ=ßãÅ{2¾ÇŠ÷©ø%Þ/Âw­xÿv¼~¿ãûIñþ'|?&Þ÷áûñþ+|ïïãûnñ¾ß{¾'>ø…מcï4RÝ£K­©y+?Ló–N«Ñ0æ"¢y‹…º‡J.=š(Û­É0“†c¯Õ±°èEã˜v�ºpíx¦í‚®”¦gÚ^=c#zšˆ{.†!ÃXûe,$êrÚDL‚8(¼ãJÚ{%d3™±ÄɸW1a à™Â4iq€?Ž…ÔNÜSv*cS™Æq KœÅ´ŽY,¤èzÀŸø3˜¦}. 9– x³˜&w ;61ÃMLSkb¬×E0³´ù€s>ÓtÃí-…¸r[Â4=÷²Ü¥ ïøÑ5²®U¶ Þ¯hv°°žÕ÷ÈkÓÖn~ ýû1 ã1¦Ùiö}{÷~(÷ /`ècšÃL—ÿñÌ‘™\’Ë´ÑTךûoe[”&*2B‡¦¶IÔnQ]üo•{@b[\õ@ˆ›bB¸GX[èzm3g`ÓáɃ'ŸÒýÀ_: Fø|Ò𿞠‚_æ^‹üf}h³Ö“~£¤‹Òu‘O,DE}OÀŸ ñ—:ÍÚõ¡žty�2#ÒMÕ—éÃï) \*Ò]D½í†¨^‚¿*ø“2øø àcC<ð—Q_€ï!øûÏ¡]Z Ý(¥‹ ‚®2ø;üæƒÝ±9áѲ2þ2o>üµAÀgÊàÇ¢ëÉ !6‚·ùí82 mÓ®kõ»bz/énó—­Ç}HÛ¶>¼9‚—b "Ó_?–µc4´q Ñç·<Þôa>h ï tû-—§Þì�?'à“ƒh÷íØg¦ xCðGeøƒÒŽ|*Àœ|À÷ž|'À÷œüa€ï"øEAÀŸ9Gú§‡ŸýáçFÿúpýÁÀïOû9ÀŸãàcAh©%ø˜ à-('ø9þàåý_†?<üÛ>—àuAÀøD‚ >F|dð©�?Bã}BðU�?@ð×Q?ȇ:þÁgùå'˜ssÄúð¶°‡´óz=éFã‚§K rã`ðü|%Àï"ø¿ø-‡/¬‚tEÄ¿¾D>�ŸKðŸøÍG¥üN÷‘n,9óÑÂÒn”òùòòI…t½çOÀ÷¼öæ½­nÝÕ"]¢_ú|劣ÎqõôÉÆc$,ak¯¾¤|.ÁoŸà?JNžù˜Ïÿ®?I¤ëòK×%L.WóùÒ%΢ñï·þ|Ó‡tºëEº¿ùá Í[Ž…*5Pº÷ý¦óm/¼[³ŸÒ­õ›Î[NÁtë1¿‰taãý¥CNæÝBºÑ4‘.ùñ” >98n‚þê àódðÓ‚€o9Gø2x¿óŒ þ8ÀëÒƒ/oô…ÁÃc»eÊàÏúƒW´¦³CºJ—$?àå‡tC4‚)?À÷ü¸ à£õŒm;øL€_wôX¾‚àƒ™—·¼‰à/þÀÏ~lš˜s€OøÑôàà±K~à÷ûƒWá“®–èjõ›Î›ßñòGyÚ;¨ùÿ"O{O¢<I2ø\¿ýÖwVézçŠt—AW'À÷œüa€ïš|¿:ð‚¿Ñoýzä±éÐùÚ >éÆk)¤ëÈé. ¢~7ü: Þï:×—Ÿ†tµf‘îc¿åñígØ…âç‹t×DûKçÛž©ÎpHí/?Y¹ª�>Šà»ƒì7\þtë Eº}~Ëå;EúŠDº’Ðàó‹Tù%"Ý#矂z)]ˆßò!Çh[¯m •ê¥Òu”Št§£ü¥ómïÝ®¨A¤›yýñU,žßÅMþ»<x~’ðñAð-�¯[ü¸Ý ðC ÁÏÇ~°!øòFÃâdWCpô¥?vžÎO¢þxÕ‡_=†LnGýñ.‚Kß,é%øßúÅïÛOQœ¶B¤[¯ó—ηŸâøŽ&‘nN„¿t|Òáwðºé¾ ²þüµÇtjÜuÁõc>žáI$øwüÖ“÷ºKº{hÒý.Èú‹n“Sà›>ÝÓ‰n ~Aðx'CÁß<ÞÐAð® êñ(âÿ‰�_zõí/¥‹ô—ηßX�dðg"Ý‘ õ&¼Ý�ÄÑ!Òù—ã}ç ܇0=.Ò]òýàó; ‰="ÝS~óóåßÓC@þ¢tc­ÇÆâOí„§ÏïüèK7ò§øÄÏAW+°ß[»ðõ-<Û~/Ò-¢¿‚gˆà÷ø-—/Ÿš µ/‰t™n¬úMÜ#ð=à.ïeðnx d¼6_#t}Zä¬b¦‘ø¿c¯Hw³;2Ü 4ãµhYq¡N{@¤‰ê-t5ÁüI®òoãçÂ]G®ÜZrKÉÍ'×Bn¹I䯓Kn¹ZrGG„{’Ücä!·ŸÜÝäö»•Ürä®'×A®•Ü r ÈÍ%7s„©þµ_"*°ƒÜ.rÑÂ5‘›Kn>¹EäV[K®\¹Û_¹»Èí%·ŸÜrÉ=Fî¹#䎒Ë¿ŽÜ(rcÈ5On"¹ëÈm'7Ür»Èí!w¹½äö“; ÁïäØþɼþFö·ÉOx‹Ÿð6E¸„ÿgŠpéï¿|î‡ê1Áýɧ¨ÿþóçÿÎÿÿ;ÿwþïüßù¿óçÿÎÿÿûëÏÀø‰ðxrÉ5’›F®‰Ü\róÉ-"·‚ÜZrmä:È]Gn;¹äv‘»Ürw‘ÛKn?¹ä’{ŒÜ!rGÈ%ÐÕ‘‹w _ˆå%7‘\#¹iäšÈÍ%7ŸÜ"r+È­%×F®ƒÜuä¶“ÛAn¹ÛÈí!w¹½äö“;@î ¹ÇÈ"w„ÜQrñ`�º:r£È!×@n<¹‰äÉM#×Dn.¹ùä…P{‡ð³á6ò;ÈßNþòo#ù{ÉßOþ)zÆð›YÅ|§Õ·zÔ£åŠþEþö áŸDþ‘IŸKþ\:/´\òÏþŸJðä?@þ(2øþ”üŽ+…ÿbð]/ü³Éï˜%üå’Ÿòû ùñìþm#ÿ1Êïùñìþ‘ü”þŠÊüsÈ?@å[JþDŠˆüò÷?Šüägäÿœüí”ÿå¡”?•×Lþ‘LÊü=ÿùÛç ÿ.)þ áÿ+ù¦ ÿYò'R}]«þÞtá¿üíä·‘¿ˆüO?‘ü¯H~jÿO$xò£Í1§Ÿàg‘Ÿ‘ÿòçRyW‘ß@íµ™üÚèØMþ(ªÿ$ø)¯ 'z(>ü¹äÏ#-ùï';ù»ÈßCþ>òÍþ’ÿµ‡>‚è!Ýl"ùÑ’ÛÈ?@þò÷’ÿ§ä¯¥öy™ü=”ü]tpÑE~FùMÑ}T·?‘ü+ÈßEø'm9Õ'ùd°z”ü½”Íñö‹Þ«ÉŸF†h7“?Šú[ùÉßIþ\ò -ùÿNþvò_4žÊ¿Zø¯"Ýk2“ü¬OøSÈ?pXø-’ÿá/$¿ã1á¿üùþµäï}MøŸ$¿ê<GþÚ¿K¢ï€ðÿA¿Aø‘ŸŸI‚¿¯ÐL“ô×ÿ±ÿþï“ß@ñÓÉßKñs¤xò/ ù‹ÈßEþû$ü¤o]M~Fñ’?üO“ßAþRþ”þ „Ÿüï\@ýÁ!ü‘¿ý2áÿ§¯þHªOòÇ¿‹ŽC^Kþ^Š¿‘üd(Kþ‚/”ÒSüròç’¿•üµ´ÜEþê¿;ÈE~öHðdÙ/åO†’¿¨Qø¿ Í¢ŸêãJò‘¶Oã;KŠ'ü‹Èo ¹ä¿IøÈŸK«Jøˆÿ?F~FüO“¿‹ò{žüµ´Ÿ¿—ü=÷Ry%|T¿ƒpþ×hý}"üîéÿTøQèÐxŽá²KþIzÃOUø¯Uø“þþy ÿÍ ÿB…ÿ…¿Tá¯QøW(üM ÿƒ ÿà ÿc —ÂÿŒÂÿk…¿Âÿg…ÿ¤Âÿ•¢÷öWø/Qø/WøqZqÀƒìíоù&®ÎÇ5‚ü‚Ú+â~o<1qe>®ОêQx"ËDùîgX/ðàšâqx~ÏðtƒHØÌÄ·Øšÿ?Ìÿ çßl.\Pm©).•ÞòÛÂÀW¼¼¸°¤fYa}Cuá½ÕÅ…â΀™Kʬ™üª5q£†tûgàcBáåe¢ùEtc³t ‚_h¼än&祿®¬ �ŽcÀòKófâ­yAÁ‰³²ºô¶•Ë×¢È l…@ ¤çšçV”•,­oXŽ_É�/i @½ÃsŸL@`qëJ qQK suàx~L �ñ¶H\/2·®¬¾¬n¹ì"ù@Ðâòš€âjž@ ·CW©Y!®IôÉïûXP^X8?#P¥ÕœY@Ü3Ãû3¿“#�8^˜9V# ¨1ÚQ�lJ¸5 Ý4Ô˜\I€ynÁ_ƒä¦>·®ÆÊoÝñ´Œßêã?¾¦¶,@ÅN ^—a657(¨Ì  æe j~PP·†ZRYmKwcù…«‹ãÕÅñêÇfbõc2±ú1˜ÄÁqêÇbu�@—ìsXÔŸäÐcvüú1¹eýØÜR\¬ë?ï·ýÂ? ¿ÃìAä 8Š<`‡‘,à8ò€H°€#ÉƇ’±<w–Ù¬x[¡¸ð®PºðØóõYi»>‰„ÈSH L$yˆZB¬´›cÊDÇòº³ØË첤¢´¬V*I^ÊB7Yü:fº:Ú .—öøe÷K{¥û¹=!tÑ´wÀu?THeueþ%Š%Š%Š ~=6¯gYûÉ>-S(û&@zi) ÞzF·UãC÷¡“<%ÝH.¾Ùà‘‘ä‰½.Ù÷ð 1 =B?úåÉÜÓ©<°Ðs½/´D±ÀÇËWg•ð¹‹+|÷*^yye0>—çà =Â͉,­¬¯-¶–T¸+­CøJD5Q²Â*Ï«.[£Ë#S€›…’_ª$W’((ävDa I/Þ\ߊ÷Èe¬¼¯ù+t GŒb¹ˆ’~{^áM ðßžÇ ¥1TXXoÅ”T,-ÄÛû¼C–4וРRH�ã©x1çÅ%4šiyVZå~!¼¥UB")­ª_ ‹òZ(…µœICȪÆ; á•ÓÉ›«¬z9s :ËÊ–•Ô®ä.ÞåY¸l)Þ¦ ,[VŒ¿ ÕÜ­×·ßZf£vðã×R+Âðîk ­¯–誷ÖqÊ ëø{Ò^¿²zDxe5€¯(©ÇˆºâÒºÙ3f̺~fÒ¬¤óF•çÿÎÿý÷ÿÎþã4îTà18ÔPã1ˆ  ëeQ\5¦ÿõ……yYùyŠ„ƒÜÅSYaô�cf`õ€ú¦¿øcŸ$ ×s± CvÇ`aameI=רÔ[ÏôÅ1ÅXè1ázôÁqÐÿ ……%õ²+ëUèø÷cW† ×ë‚D÷{a!ÿH‡ÿ²h.CSƒ­ä†2•?˜* é¾Xu—wB5דwóºÕÞÑ……™éyéòPã»ä’+Ú"”\È«teU©\==Ó‘«zIdaauU¡h `øuj8°Î“È•ãÀþt¥ÀQU<4°“+Çå˜.p(ÛÂÇ.øÉ®¿²”[‹å~%ŽÉgV½pýà’Pù⸪€ñ:宋ëë½ü>8 ÕB’OŽC£x·.Ș?WŠV<Q×Ó'4"¯ÊeÅKP‹"¾†¦žéOTæ-ÿC\ÆÂB‹ùæì¬L³ÔŸ5iðsÕz·+ò–ÓŒ;gˆ8†‡‡j9eU•‹e"`¼Cˆ€Ç´ËÄïÕôî}ãEÅ×íyõORÛÄ0Í\49ÚÂ4·ždLßÅĘɕшƒ •ÿd¦±$q¿&7‰ÇirÑté)¦Éƾ›í)ÒÏ?,‹'ó‰Þ #]C>ÃR¹¸®¸nåŒy8¼WÔÔ-­Ÿò墜„r)d):ëg¤SœÈ!RV!šÂÛP_7ŠŽÏ2[Bz _ª„OrÓÑâ/HJÈ¥9ÿwþïüßù¿óçÿÎÿÿ;ÿwþïüßù¿ÿßüÙu!Ïd<ÉÂ×…<c×…>Óè(±Ý�«ÓÕë'Ä4:CŸ±G†’_}³Ckhl-±e»\‡Ýé*Dº–ÌÒÈã•¡Ï ÆoøÐuaš†Bži×0ÝÆÉ®®Ô&¦1– m7®fFÀ³Ç¡aÆÆÒ7³û¸Ðg0ót”D<““YÀèšæs³ƒŠ×ýðóy¥ØuÒn¿Ü&åaß4Åfù›Öìö9Ø!¡ýˆñ"NGcM[OØšš?ƸNyœ½ñò•ékX¦ì} n·‡c2À@ÂAü±—‘‚Aš½Z/z1 ÆqX?å±?0ÅöÊ>×(”e€×±IÔqêsL“ú¦q— quë£-V¬^¶ëeHƒm&Ï“ãsNY©†q@YÓâBܘGãÕÙ6c/„µ\íè¾p7”íâ–•ß /^0Ë‚ùÍhaì•Õ®̋ڤǾûö¨_Ïw3ˇö/sÜMy? y¿m¿är[ÓPß-vCÞö¦§OØ’a%ï ³¼ 8ƒ©_¤‡×1Â+ê˜÷•ºxj Gº®q²ÈôM¬ u>c §EÍ6f°$û¸¨;»8óÆü˜8’ƒ*Û’d~¼ uZ¨íÁ ¥^4eš Üÿ7Ü;ÁM7ÜéàÞn¸sÁ× n4¸óÁwžT>©®Ýí»ú‹²¾;§¸ëëÖÑrA¿l<—÷åÍŒ¥mÖ±-ë¼i-¶Á‡ýÀFªÀ¨Ã†Ž¨Àñ{PvÐl— ìQ?°VØc~`sU`ûSòËT`OªÃ†QñÛ£{Êl‹ ì¨Ø"Ø3~`“|a#™Ø(X­:¬æ¸ ¬Îl¯ l¤Øö‚mØîÈ4€íQŸ2ªÀf*aŒÕî\°2•4F5ü�¥kR‡eÇU`óüÀîRö»^6Ê·Œ.Á�Oª@þå^ï?ò5 ¸œß1ÎÏRÁ½ó;ÆùÚtpçs~ÇØ<ÎïçÑàþ’ŸÛC¿÷ËÏ•¼xç}À‘ße»Nÿðjöúth{pÿê}o™#RUÚUCíT ¯ÖB ~º ¼Oý|Õéøø‹ þ° ¼E^Oð*ð*ð—|• |‘ |4Á«ôùˆ|ø‹>’úKÀ6ö7_£œâ-…Û@ŠQÊ@nyÄŸü³™%ºåÄÙ¼^k $ïøÌ¿ç$¯…U8Z´»€V‹?y qÉû3öe¬WH›/á‚ô)íd^ÍSDý!ÝÊúYS†¯Ë=>ž˜b»eƒÝaFlã%MY™¶†™Üm[ ò0´¯£[ s­6ÒK&Þö�Ãq*pyÑ@þ•ÚK^ØO8Í¢Íâ^Q'Z»ÍŽ)6¯>%«j¯•¯<å:¥Ê3„LláøI&5Cù)4ÖÑz åPŸ2•^.êó¼˜ȶFy”â”ü‹—7=8¶Üå mXp4œÝLþþòæùIý¢;Ô�}Žyõ9õ<ÓTÖ3+í¿¸Æ6F›äuîhÑlst‡Ô_ßßö[߯¤»†TÇÃϧÈÖŒ±^}ÊŽëB I™ïÐ8Ð�¬Ö¤ŒýÇDk7ùM«õÂyùGÄ;5/Þ é�þ”žøf¢ ì¨,ô›¨Û‹t©vöQ4Ì­C*éÎøÉ£ÀVÃüÀV¨ÀjýÓ5›èÉW©Kÿt1FJgú¦1¶¼—fì~/7Ûõ¿ß °Ç†Ú¤u9ïÐÖJ~¸÷C×QYû·ÛwÑÚÔkÓ¿\g³“bãkQàŽî³#ž>¡J[/ê8=¨ @Þ×í²+y£ý~ì?áZ7l)éº]Ÿþ#ƒá}SÀùö3ÔQ@Ý»ïþ=Zè)ÂK½h|i ÛÎõ!á-í—2fbaîù̇ö³ªýÊÙ¯RÎõr†Åª”sÝwQÎÆ)¢œ oЬ^á[Vm/•u(«¶ÇKžô-¯¿±£VÞõêåÕNW)oû¡¼U¾å  ò®å í£¼ªcÊ»[¥¼ÕËšªRÞŽï¶¼Q³¡Lµ¾å 9Fåmå P¡[•Wù)c§zCòUÊØõÝ–1d¤p«o5g¨ŒE5'UèöÇcÕʸU½Œ«J·ýÚѦRÆ*c•Q§B·Ÿù@£UÝ®^FÖ®RÆžÿB;Ú}ËÈ’¨Œ¢Œ,ŽÂQÈ6ÜÁüä%jÃé3ã®®}„uˆ÷ ù0•ü=šô^úä—`Nó'O[\¶ÿòiÒ›×>GZ§\ÓÂÌîµÐ¦Ð$Ô{æ9¹Þ3ÑWï©Áåmôï ‰ƒÇùì~]7~S8:üÇiÎüwéçyÄsuë5| ™írð6Z+Ö௕ëÛ¯Âû'LËמ-_vtæ«éÛ{Ã<úùÁI,þŒú5áq|ÀâgÙtHÒת¥ç°R^„à¼áÛG;àpë×oÿ/ÖºZô Íê)þxÖ|½•û§…žrl­JLs¹ìû5Q¨ï™ñ�Ð)Á®ùz» ®å8Lo°lÌ:z—÷ׯi¡Ï`ÞÓ³mʼ»Þ`!€s'úã8ÎRÀ“¨Ès·,¾òŒÜé�N._7ñö2Jø•ûŽ7pïãëƒP7ö>äë ùþ‡ÏúÂNë‹§¯±ÙÿbÃu†ý¯×‘nê³ûšºAn.¾SaE~’í:“#í/yí‹ÌÙºåT£ûË„C‚$ãò½:•ý>þ»]{¹ŽÖCyÝbì+×`׬e3аÔBàÏr`TÙûÀËôG¡Ÿã¾Ç1ìïÂåïhTëñû{4Z™¿_?:vúÿôѤA5ŽÅŸ£ÎÑK>¤ð$ï w#¼¯§÷\xwÐ;ê@­ôžŠúPzÏG}(½çÁ{.½›p|x¯qOÅ8ºO ~ûÏ$÷þ¡ÊÚVjïä©®½jú¨…†ð9×8XŒhgfhœaMMÓ„ Ž³(ƒ5Û™ y~rÚ+é¹`]í-ObŸ¼h/ùß\¹Lz¤|ío@ÿ>žbk ›kküA¶­qÉ]6¹lõšð­jú¯sH¿û?L¯º_zé{þÃô;ÿÃôÛ¤ï5¦ ¾”lp¹’‘?Cºö{€Ç¯bәà ÜÃâx{B˜Âf¬A¤“Ù ¶ÈÛsF³ËküZ Îók;>å0âÚ?Z ÜÀ'ùgÎešÆ«4!¯xÈkºW’å™ÞÉqÀ7cË•¾ÖMØÇWAØf§ìcçP?ÿÃú=ú¦üÓü‡éü‡éLÒéÃç*ÒExéà¹þo«ÝŸ_}[Ñå¨wNòÖõô1v2d»>˜€}ÛèàòsŒÿÓ†Ú¸þÏŸtÓyJüLÞǤxÄ!•I©ïGw9øîzÓŸšþ]mo�d”Ž-'c”y‚_·øÄ*<êÒAžä w>Àò =¢Ü:ÛF¦31ç#\?&Í-¤‡7±ýÛ$z±m÷>å:bÿÝ5Îv\ï .õcž6ó^»:îblÕñ—möåж%ïc|¢W›Ú MW³¡ÛYŒ1Å�4, ‰nþ¹ëŒ£“™P²Zž#–iÉÈ”yÞ¾%j¶}!â ˜‡Àå•7¤KÙýÄ1”–¶‰E"Nh—ƒàZ(m¬ß´ ·ŽcgI)ÉL“r§kÔÄö5‚ä‚} àÂÜ¿oÊcà–5=Þ |Ñ5]ª ĵêÕß {XÏ+ë‹·Âlý%Ö×ñ—°Þظ¸ûï!?J}ÁÄz‡ÜíqÊ> ´™6êÝ~ïå¶Æ)0æ3oSÝye²ë¸šüŠm&ö þnéD#•ëc®óæºÈˆhåzÆt_CKkqe¾`,Á…^ïãÓCaNBû$/øÐcrx7€8cËc÷Ù®wÿ|!o¤gåå6)=ו>òöÕo`Þ[帚îþ5ÏKÒ)(òâm5å QgPGjò—¼}µÀ[³ns·¬ÓªD[½+—×^ѸŽyé£ï÷ìÏÙ_¼Æ†òcsÈ'ÛùþÛë´G ¿3÷Ù™<Ç$Ö1Ã:c *\cZÄ�ê š7³ŽÛ×èR!l’#(]‹ë{º¦:å)–0íMÀ'’‹|ðm\X©Kµ_¼”iòŒÇ»X‚Ý|"!ƒ¹Ž¯žßÒb›æ¾ckŠ{ƒÛn5ýx‹9¼ßn†º³\"êðØcC¢æ$tØ ¢rAnèènê„öÞÄtH«(÷GÍ€ðôM®RÏ!Üâ@=»x%Ê8é[¬;w¿é¦WâÃP§ÛG6¡½›|,e»ÎVyµ÷xpðþíóµÍUÇ#yÛlfhŸ­Ð>Û—òö)PiŸm~ÚgÛí³•ó‹½l+ò ¾g"ÕuHø6/Ý“¨Ç$ÞžiÞù/LÓ¥®^Å, 3¢f'ÜÒxCHBãUw`[A›XnwàþÌÅØNðoµÀ;¸íöX– qÛ¨ ·5uFIm¶M”·U>¼8îðj'˜{^žênŸ±ÆÏ¹´Ÿý¼”4‰_}bó˯î&~…ó¡‚_ÑþV?ß×–ö½•üÊ.øU^IÔl(ß'ijR½ÓøáY\¦Ù;dì~%6Ûõ§dhcûe"μDÀ·+¥ýu%˜gúƒcËlb/ýpEaýðö¼_“àµ7¯Æ× ­¿ý2´¯±ïqË=|ŸŒÏ)/NDÛ× OÓ~›Ô¶dÃeJ’ö—±M¼ç£]ïbº`Ö³¾sÙ®M<]‰¯Ì%Ù%ï-vöØŸÁ5.ŽÕnûz!õ`;`Yš§+º÷ù[ö$ò>"Æîan›‚º!…0ö‹öÆA^àús¾Vó]¤8ËêT¡[HýäIð)!{2}{,ŽqZ]SØ/9Âòô‹?„>8Ú“÷yïùnOž±{5Ûõö·&6Ïÿî|°ßoú8‘瓱g+ÀuB>gP‡¤žÏñLÈ'Žë+|óØy<y´Èçb¬3?eN\&¯½Z®c€+p}ãöÂ8x{&ĹçGI//¯7€Ë7öí­p„„™üÔÙQÈ?^m¶ë­·L,r·„_Þ.¼ 7}œ”²xïNÀßëf±è'céh³%£OJãÚõ•Ü�ôW§ïçoAù#cÑ7û£Wvþ~¤oÕ'/û£oÛÓ7Þ¨l×À›&vÁ.wýþ÷•$ÉÖÝØ×›€ö“€W¥]_a€·ðqœOŠ81®z{üŒ!c:Ú™=†.S½/ôö½G€¯ëüÐ;BýºH¥.Nf»þ¸ÏÄÆoWiG£±{_Œ±o_\€º8¸ÓÔûù¾\À]¸UÆÓ¾vˆ3C\’Ÿ|!ßãÊ4Šë#ùX—Ò¦,ÞiìÞÏ8O)÷ÛwÏPøÒ¶úÇ~ebã:Ü{bå${sÚöçú§ë8#¼¥*x �ïBÀ›¦F÷ìÅû»�¦yÔªò'üñ(S�ºwe»ÞüÒÄtÇ$º/—åçA<Ú5œñƒ×"ð†[}ñ€~ýæ&À»Î·_HóÓ¯S¡}Š‚èשܾØo—¼iT~» PþÃPþˆcA䛿'ß“~ò5ùÏ÷` äÛùV‘¯I=߃þêQš“Ö©ä[ù†A›‘o¦þq°ËØwp»ÿ1~\ëŒþñÆj ¡Ê7îUÄÁøÏUã™_kì{µ%@¾:u>û*¬oߘ`ba£êxû"}}±ðFªãí3e»^ÿàí’ãEœ¢žú:oO�¼Qêx?yýnÀkòj÷&l÷Cªíþã#ÓÑ®l¬vpi¾ýéÝO²,µ^š_UèíÈv½üOÛD¿²¸ó§>…ó±˜Cõà“ÑÔ¯óTò„ü¯…üc|ËÕ¯õS®\”'ÔÇI?ÈTý°íwëC—)×]¦Whœ�ÿ} ¿ �Íõ_¦~X»÷ß4dª”©ÝO™ò_‹ >GûÇ™XÈ© Ê“çS€Å9ð (Ož¹¡$ÛuÈ ù[ÝòÓG¢ /ÚþµØ�mCm•¯Þÿ�_;í¢Òþ¯%ù©«|ÿ|òµ¼lW´¿¦?ˆºÊ÷Ï'_[åÚ \¢Á¡BC'Ð�í¯ñjÿÙ{™mö,Ê5 ,»X’Ýcêé_ž˜Øön _uà·¨ÇK‚°.{ícüóè×F³]¯âz¸ß7îõ4ˆùmÇr¤@ƒÌ>˶Ï^ózýým FXO®Zû´mÕÇ/ÚóWÓܺAí>α?<¨¢ãŒ•ʈp˜äsßÿÅ6x¢'ãš·„ÁüÑWŒøš²`]V.êºéÙÐgš¶ŸíòKLù1ƒ9ÈUK:˜Ã ½J”‰=ë£ÿz©Ï誯§¯YËB½Ïü»]Òñ‹˜–ë´¿eäkáËq-¬åzÆ´MÌhæ[Ê^fávñ-×)»_†ºß>N*3Ú3[~U•í:Xf,r¹uW2š#æK½ôuά…~ˆöã^4ŸuÓ,èhñЫáô¢ý”Uƒ{9élà ~R$Ñ6F<€ÜýÒV,žCR‰;(â~9Í»|ÛAÞ:°p£Q’E¢W%ý‘þ¹aEúu> Ó{ë5ʺ‚öýÅ?|ô›Î¿~äÍBç¢Ú%½êÀÇk,ºiã*\«/½”ëCÊ„.ÌØòâÑl×ÙµX¤Ó9bƒ<ågÿ�ÊZa_ØW =ζßññ@övÆM Öá#J¾„öpÞc÷—Æî­Ù®ýa¸Ÿ‘Ìu^Ì.Ío\­ÂãPWÕÞÈò€Ž$[µÒñ-çsBŸñ Ó»SvšØîºàù½XŠú7)/éüRÓs'lÆí•äû)2Ý&·7ßçTµ7_~9îUÆàžZãsmwÝFõò‹MÈÓ° qïmÿ$žÃ÷.–�½SÞ�¸Ê2‰NÎ[vŸ9ó–ÿó†*/]jí·ç)×N½§sÝgopÿï´ß¯¯±¡mõ´7cù˜›ËØêJÆYV€ûí›ÙööIlu¸àA}8¸Lb[•úÓ͆õ€¼%e¯ö¨à+ÌaìÕoy¡êãW|ì Þ›†²]½0ÿ?˜·8‡»b ‹Ì[Êm–&%/e“°ìyìúäilÒÂ¥ìz{X8]߸” Ø5U7NcÉé,Í®1WÏ~ Ý;ªS& wö‡,-m“î°¤/¼dU+í÷°. {dëÄðGV±ÛˆñÖ.ÁCøz ƒ¸ußBá³¼Mo%øZ‚¯¢ð +%ø"·ŠH4A\>Áçáõx‡û)ÔÏð9�7àæ\ ðP¯7¸@N¡ýÍU-OÛP^ö0*ößV¦¯‰Ø.Áñ» ~óÇ?¶Æq«!cÇŽcü®Ü75‡iažwË×^xžÿãz?xRǯŽ×þ”«ÃŽºØ7£í—½–Ðôó`ÍøWÏa]î<�¿ãÙ?úÈH\¼‡åyå³e`ò‰Rä“Û#ò1zò ßNùt*òÙè'ŸE>'!ŸHE>ùí"Ÿé²|2)ŸE>[Uò1Ž"E>#O´×| Ï3êÍÌÓ“_ØÊo£"¿í*ùeB~ŠüNA~1Šr•bí Ö*o&Éòª ¼ÚyíôS‡µŠ¼F!¯XE^U©¼eù0Êg½"ŸÝ~ò±)ò9ùùXE>qž|`}(òY§Èç¤Ú8‘ðƒl?ÙqÉ›QÞí ä#ÀÕ¢ÀuJW­ ׫|N³‹1²äDûý¯%à<m¿ìÅÞþwö'pۮ͡Cm ?`ã<¶{W”M¶ä 1fìÊþqdÃ^6�4õKãèr(è:¬B×6 Öf›”eDšø~îæÐ"Ç^6èø€ ºëü,Œíc‘±Ýð|Æ"í}ÏEÛ»Ÿ‹6ö1­ý3p?cºl×ëf¾Î°ó9Í ñNcƳ¹ÉÓv•"ÝÀ‘î(h¯A˜?Â:jÄK¾ºKÖÖ!Zïs”¢¬v¾æñ‘Žg¼Î–н¶õL¶k÷j{ID„Ù€>KúæZc¨ko¶–é›4C@'Ã}óäi¥v3K°gÌÆþEö´øÙ´ÝŒi`Æ×ôÃSE.>Ñâ¢U·ßÏÆÄ•Êy0Ê~Àó‹|Êî�¾¤[ªÛÂÏkøÂV¨À®Ùì¨ l• ìz´ÇU­UÅOö¶¨ÀúØ$ìF~ÞÐÖ¦Û÷¨ÀÚU`;IÆš.ëзyú£Æá-ß³Aècƒ«WÁs›#WLƒ~ÏXdãb¬ÄfÃû´ç¢›~§I@›‹öJÏE?Ât0Çâ:ÅÆÙ†Ýžq–ƒ6JL›Ò 2cGÐ6u_ åv4°m)›Ù.´GBû”/…ÝRányÊ rä`¶ë_Nº m¡w{ÉfÝ»N ¾±y¢àÏæzøk¿¯—iÓ×h@&ÿù—W}ú²gx/Ûjb¯€¸w›AnœñsÎ; `ÝtÔØ¥åûÐ)›µG±ÞMlÓŸ8¿BýÓ/NâÅ(¤ây¿ÃóÞÌ󎼫Í?üýﰾɎ$7\´)óšþ=Ú¾i[/w/Äw‚\{¬u¶p{²H<€gȆe°Éq íÃû²SKƒ2r ˆ<wï?Pnn«:€OQ°aÒ».äU"«"ÛåÊã¶áoÆ®žVàÈÀór{‰Ç¿”Ö{/‚Lîw½÷ ¬§ ›/ùº m'øúå¾ÏöU ¬ž< íâN]È^1H|Ó=ø¾x“lvMPn‹Þs§··‡ð[�ÿ2ÂïƒûðÙdôòò÷Îzÿïøb=øþõ&—QÅ'·ç´ËîžX½™UÍX/ø·«_kÜ-¨ç{nÄØýËãÂÖå·^õÇ×ßrÛ˜Kl¦Ûè¬Ñ¿2ƒ§ÿ“Œþ îöû?×ÇÞQ¾Ï÷p| £ºÏâûëiìüŒJë’ÛñH6píéL‹ö\ØòJ&æ‘ý�øÅIo&ÖñˆãN¨».àhƒ|£¨eOïµ®)aIy%ï®J€ù;íQ¨ï;I˜ûñw(«¯íO$ò Ï|¿²‰ó?ÔK&:2ööKž;éØñýJÒ?4mLpt_ö¤qP§'ù¹$s„×:×Ôí(mbÀgP¿×°&–µOœé!¹ øÀc€æXN3Úé„°8¥ýÖñêb¦•ìwkWüØs:@”Õ`ݽZ´ë‘ìyŒÐ^°N7™ØO¯ ò'Éõ.¸®4¶ü<ÍØýl?êJ³]=ka~kWÌoén=åºÒËžÛïñ\À7Høf¾4/|W0/}smÓ¤£}Õ$–‹sâÀ´þmB9þSÙ®ïB|‘˜Î‹¶•œ¶xuÚž‹´íxí³P÷ÊÏ-àÙÎÈ;MȵöËCmP_™Þyo³Bú$H{7¤õ‡ßBø“©¼Q¨Ûñjß+X´’f\§ò0YŸ’øhç¥bg'ñ¢ŽO=öígY¾dO.ÙV®nÿ.Å;ÂaŒÍóÀ@¿Á3 ñäe«¤sƒ|ޱñyá’Úxôt5û{L#·GzÐF¾?äwùrºð¬¤D›„Ós®ÑCçSøÒèŽgÞôa|W9 ñc¿Ø»Ë[aU³×ÇsÞcòßíh¿ÏulÀ÷„Mé‹»<¶hBÿžíz»LÒíÙÌe³RɦÏÄ}ï(A9Þïvë¡Ë/·¡žÞØýÌ Ù6s]ö®KûÙöl×ö=ý‚§>º©é' !êîeû ’þPÈ',ËžÓ÷þ!Ï^ÚH Ýý®ÃʽÀ½Rº ÞÛ|ËÓM2è†G¼õê²]¿ü‡ÊžÐÛÞ=–þÜËþÔ˾>m[~Ù <óU1Ú?MqÛ56FÀ|õc–ô¸éYÒY>ó Šýj»[ßÚ9e%ön[ |dêükylrû†Y¨“Í+Ñ¥BU’¼5ã ´Áå{q7ÏÁy®ec¼±{+ÔÏsoŸlªôµ÷^X5í½qÝa/pÛyŸ”æn#Æõ�á^ç#ì‹Å¾ê¡ž‘ßmçý‰Óù赘žëùiކô™^éE¶ñ|PÇýàÇ6‰ÑúËkŸÏù²²¡’£å¥<G÷Kü¾R%,âi¹AÌŸ¾ö¯nÙRÅþóW=“ó”Iž.PØ&'*ûÿéÓ¼Ó;â·?~’¢°ƒþö á׺[ à6ùܳZN6Ï-»¢Ý/V¯ =%ÙáÊÏ#þéa@‡rOG²ÿ¹¸cøNŒ’d»Þ܉{¸wcëÏ4ïÙÕ¼F t…ñ;’–n¡“A›õ¦õ»×ØœWˆó8jgI¸M<Þ5§zÞÈ}–䡲ËÎô†÷c—Í×À´»éÊvýâ)i¯žÏ¯wÑyÂ1æyIÐâ{v%˜ó$î¿3v?©è{yœ?L°¸#Ù@7ÞrÛJûiàU\žvåâ>\ãµÙü¬ç/{ùþÌÑæ½lÄq–®>«Ñ¶ÎÑèÓ×hã± ÛÏ2Óª¸V¬ñ-Êzæwf,{Pö[/Áóp9Ø®0ÞÒ0lÑÝQ³WåmÀðY)›˜Âñþ‹<[ÿ;Ç"Æ/?_v¿À1û2èK =Ž}Åå¶mŒÍ.âü/ž§ØÆ­KUõ>öúËm #Ý%ä3) ž$û½’½árÛ³…\•0ç õÛïgI‚‡­Çõ Ð&ftdü6·÷½f3c»™iÛ/c™ £ó{9ð<‡˜›Zÿ䘤‰øTÜŸÁ8¤èÚpÈÚ—±4~¦m®•4ÑH—]ÆFžŸ%m;Œc‡ë0d<x›±ñº÷¢W¼íò<ž×µÀ5 ŸÝÉ’¯ú0!ýQf:Sí±Ûì±æ{Á6Ô Oyµ´è û‰˜Us7 NHkbÎ=§ m/¶¦©ÐG7k¢}áÆæ-Š}U²WàöO�#ÎEìãúg~f®“iS~ÌtMwáÙ¸\ºÛ$^j{nKq@ýŒÜŒ XlëUeOø……rÆ‹ºdGWuÿR×.‡{ä4Käe°g”uî8ÍŒ¨3îd!P™«B›mŽp^Ëþ’ëÉŠ¤ýÖ®!{X¨-Ëf»ƒÖL!}À›Ðv„ëØ`δ/z§}ŠÉÒJë­u°+ûÓ…ïÂ8,ÀýéUÚf²aÖbböq euÛº|?ˆÎvmùï”>ÆÇ”oÀ¸—åB=h@†È…ðLpóøùœE\–͇úñâKУšî~dž} ï8pÀtg&¶n5øAÞX·R”kê»rù^/ðˆÙÇ`Œ:°Lÿ6#@~1ä/‰ ˆ»ðÉøÀº_áØŸÊb²]gba.vÝ¥àf‚{‘×=ë´Î•ôaÒ:WÎ/%þ‡s–Äÿ`þY‰s–àÿæ{µÍ¿s÷e2—5Ìp3Uo¸n“óãl×éû$œÚ¡çhçq&ö`ÉàP?¼ë+³>a|aÝ_½÷³[s³]Oñõà‹{û^ê%ƒß©*ƒG›ØÚ·äôyéhN‘ב¤oA}‰|Ÿu&’¾Ûæ·#‚òØQ¯‚í@·RŒnåz…N%Á‘ñ«4cÉ‘B§òÀ‚—^ê56o6ÈÂ~ô‰TÉ&KÝŽtǺôÍßwŒmGÊáò½ìínäøsUñ®Ü±àcâpQr¼s\o‘*Þ;ÚÓ7ol{f×ï…÷o¯ºÝëòG¾gL¼ÎËÎprûLl+”í:‹°ýÛqÒe¯€õ²m¼AÔ‰ºÍ{ÍŽ!€“v7ñ¸‚ö2Ú+ôT='!]×´Ëa¹Í'îõÈòéÇ|™Ã é (ݤk#9¬W_œ#úâ1Õú¹oÇ©ôÍÑ£cÖ€PÔÏ)Yýä*õKDÏ(¤Û=írØvßúéÔQýä)ÒåQº3.mŒ<ä°Ñ^ý‡÷ýNƒjýT÷�¯‰.³~\’¯Ím§QÝævÇÆôÍ—Œi#Ž2ÁVIç^¸½'¶+ð°Ù[:M\ß;.ìàì5¹(·4?Ö¿ê§½x,—±0ÊYd—榥~Gà{Œp¸‹c¤³TÖªu^EuÞ éÚZK°¼¼þ Âc9¸í”K*3ÚÌ6o ë?‡òu¾±ÏZ 8‹¢|6YùŠ4Qù¶¦o¾( ß•`©|ßû–oà« ¢|çuŽm» ŒëTûhíŽí�Tå°Q£ßa™z�ß¶ Ê„pE›uÊÚ,_ÑùÔf;!]Þm–/k3„ûË·+}³þHåC¸í^üJð•íªm¶lÇn€ßT› ØŠï°L½€/&ˆ2Ü…#^ý°—©WµLu;üá Ê$`»¾Ã2õ¾Ü Ê„pñŠ~8 ë‡¥Š¾UJýð0¤ÓÑKeýà' ~‡å�|ë‚(ÂyÍÃeèNu¹¢~Ç€O ªÍ,—sùº›ÊÅóxåÿP¦†ƒé›#{Ç,“€ëð9#ý莃)[~cìû‰Ÿƒí>áç<ižYŠâz-§L,2O:ÏÃñ´íØÍ×g}­©þϽôäyplŠQr"ý¦é˜ÞA¼Ì±fÇN<ù2<°þ¿ ß‹–öÛ W€ó·=2ù€£E…ÑRD´lUÁS$Ãã�<©*x: OáéTÁS*óÕÄÆŸRÁÓKxJ Ïa<2<ýîóÚRÝlØq„p83ÝS%ÃqpTxÝ•ðøŽõ|_6ds¬±{³Ž÷›pw2ô$zpmN\îûÎÅY°ë„­Òf‹±os~�š’dxjMlÜ·ŒˆåúÙŽ£)‹7·ùP«K¹js;ßùÁþè2Êðu�>OûKß±œ@Óö�4¥ÊpT=¯-è‚ò |μ'M†kÄÄtîöG\çõÔ÷¤.�=™OÂøÒõøœ{}lG»±ûÉ8Àc €Ç"Ó x*ÜçÄ!=¶µh³'«à0ÉpØGœ²nR¶<Ù…k]À³‘×Í3'lЧûÁW+Ãã#ÂÓþ? 3,ïJ¹êÉ×T¿ín•áÙ xÖKx0=k@GJÈ“ïé±Éð<|M¿z«î�àº`¯Î'ýà°Ëp�O`n˜ÏH>sb,Ž.‰…ïöâ@ƒ±¯Ë@|£€øÆˆ žžx<ßO´¨Ð2⇖u2©€#Ñ‹÷<²ãÑ“ ß¬—á(0±0~Ÿ;O« ÃOúvYú*H¿]¥>Z¨>r©>FUðl”áYxJ©>rUêcÔ-2]€Ã BË.¢%h9£‚§S†§×Ä´ƒDKž -~ÎèötÉpÝ´L¥ñóPzN¨Û­2Ð_µ\VÄt Bþð›~›'ýS:´?WÊ#)[~šˆó¤±ï©8~O7ç ~ðÊðÅã÷¨^мçͧ׺ÙêÏè·¡6“¯Àc"<;Õñ<Ïdx,€ÇÝÿ¹LâÁ“ïÿ,õóZèÿ!'Ý8PÆòਠ€C'ÃseÈ67÷<8~b €#R†äš©ÿ—*êd=ÕÉa?x¢dxÚO¬›’EļöTg�Z¢e8`i½×OùÜ™Cöë”ó·Qï~ê ä7 ¼Ÿì*òè<5#Ü£Ù®¿:¹îœ¾q+=VÞE ò7¦çß9Èv}Ø€ûWx—:¿k³#ý7vÁú(d‡£dîÆ‹½vM<ž÷åÛã‚¢þžü±ø="™ïd¤w¼7ê0½ã]Ú½ô|í¤wh;¶ÞѶ¿“Þ·¡ý9½÷ò»Ä;Ú†Ûèý8Þ¥Eïgøù~þþt$¿»@¼àÝDï‰øýBzÇûÂéi‹£÷ ¼‡–Þ‘¶Hzç´`Ýã¾ßßhyz«c1îk,»TJëL¿€©ú2Ý'·ßí÷¨í‰yÝG—Íïûõ¶kZVëeo@ûCò;)ñJio÷øˆh—Ô1…ÛÚ¢-Aó30ïà};Êý ÀÀv¸ym)ž>h¯Hi;Û‘±!ÎXâ<"öŠª®…¾Æ¿A }²�`¢èÞ¶%®´NÑ~ÞûNˆë'Q×Ò¡]N¹÷¾�Ÿã  Sà’ãã6 öËmÃË*`|¾7Š÷¿)òL¤<Ó(ÏeŽûYdW( '[…|ÇÓâ~ã¦Ð0Þ–¾‰wHôàØÇýǦ›ÞûÛÝ?lw�¿“Ì$:6Lf8?xÛŒŠº‘î´;"ãýÌ}¿`�ûbºWSn_ìkÏèjÇóÌîojá7Þóº7ØûžÅMPÖà!«˜‰q3q¾Vú¾ ÏMµs›ò__Šö[ÊýXïoÀlHÊv½ ¡2Õe"?“°pòýê{‹½ì½hÔÄ*·É÷OÚã(m¡^¾Ææ¶×{÷:ÚÙHß¹N[óÿ´÷ýñQVg¾g&™k” #?41tˆŠ:ùNBÀ AI!‘(±É$™HHÒdƒ¼C& *¶@‹4X±ØÚ½´µw©¢‹.Û‹-û¹ô^·­íEK·Øuwé®»Õ[dî÷9çygÞ™L~¸wïÞxòœ÷¼çÇsÎyÎsžsÞsž#ì{–÷2èßR•MÊ=µ©ïSK;2úÍ=Mcœ›]<:ì^̧§$„l•ùË=L)Òn#í÷Þî=´ô :¿ãyÖüÊ~™‡ù“ék_ ý“Hßž"ýª#*ý÷öî Œ‘~ŠûW÷žBú ëHŸ¦Hs¬óÎù)Ò¤1×1º¾MçR¤;ÖùçTíøNò½–”nä!ÏW§H{ß yi'Ø[?]$iNAÇž¡/îûH7A~ÈtÅû)Ò}g¬tõñ "ºû·rïÍ{õ3™ }¶9?œ#Ý,=LEt×ÕF¢LŸå¼¶4buo´F½þ-ºmn9§ 'Êä힬¼÷{&Û¨=Æ2rQÊG– ³2.#¾1êAIÓÃʦ¬“õ¿ŒßÂ{W+¢ŠÝÇ,÷KíW6†Aù‚±ý2/å’ßÚû¼ý)µYßÿ^Òorï0¡»ÂÛ¿Ž1èëÇ’uG’à {Sâ2ùe’É$+¢;}d¯˜Ê/ÏËÄüû(üº²åáMß‹÷«ý¤´——ì| ìÀë_?>ÐoAœÞ+ä^D}?)í ÞiØOz?×+çšO$‘¤ß%Œ/‰ã¶3\ºk±»±ÿe5n7þHÚ£6ÚDIâiÿäÎ*ðþw{l7µ}Úïßß¿ó”nCºoʹ\»aÌ ]ËËú>í\È€7D AÁ¸Ø¨';Ó:½ý¿+iOíñ*y6=~QXú®SºDÕ!{¡{ЏF·-ºÎÔî.ú¶aÐwšP®¯N*^½!^=â-è»_Å#ûÒ‹CüRÄ+Uö7B×Ílw›ô–<;•ô}Aû"i?£üþ¡)³[q­²‹ƒ6+5Œ¸í"¾™ºOíiq gH]i§ýäÎÐ÷zNÙ‡ÎÛ%®(90µ–öµkkéìõ«†³×SË„0¯"Í Œš6Ÿ€ûA"VXvN 1Ũ€K§œQqÒ4ŽŸ7Ý…„0⼦,¹ÖEgxÊAS?í{xI¸mY]zÝ,G¨uÄÕW*¬á ”ox!l÷^J¶¿©îú¶‰–äºÖ*Þu©ø^ÍëÏèl\öH+øa'êþ~"~J[Ì'ÒS~5D¥-i«qõ¾¤Ó÷èç‘P,\>êØIa‰ÖÞ×_¿×Þ^$ïM¶pHÝ»!ï«¢~†ôH­ˆ>‘ªœßÕËyN•sÎ8å\w_¬œÏ+6ÒîD!ô·gì.í—^—´}AgRÖC÷Ü=,÷Py¼*YþH9ù¢Ú—JöŽ(\EôßJT9-ÞØ|?Lå©oeÿ|ÎÏ*ËuFÑZgw…f{]Úk5®0ø:|Rî\ â¤]H¨Ûu5.YoªíoŠ×mÚ;ÓãjK3»Ï%Û«Èî ï¤8w“ÞÏ ém'½& õ…´Lô,ÃH¿‡×¶~äâ4<ƼÂ?Ø•˜ßi“!?‡ »…÷­ÿp÷)™g¦£(¼qÏ”Ÿ®÷5Êóùûâé˜O]¾í>C¸( ¯›GJØÞ9ñ~“—fÏtÑú˜6»ÜEóyŠ}_–7ükÌß.ŠzН½v£CûåÍíO…•Ö£ÿÊçh~I{– ¯šEAnÙ¼¯¡ýJèö„1ŒÛ‰úÓ(Œm ç„Æv¸O­ÝÑÝ»DÕÝ�Í‘‰ÇîMáÌ©·¦¡I¦±{Œ4ÚFÍ$ÓØ1F-HÃ1É4"c¤Ñ‰ú?=É4ö‘Ý ¸{’iì# Z£òN2¡1Òçy¥<yý|M|,õš¿û@öioa«°ô>vÜSr೪ðÕƒ1h&í§'»Tû0_æ±’æ®n¼»‚î$¦5ÑÈæÕ"\ž4VúbcåÊW‹JÖ›ì^qßAy|¦³OC_&]æ×í·ú¯ ak¶g 2ôùÜVô†h“6 Ó=kiS6j¿&m Ñ÷˜…žã‰¼»•ú…»´ç$õ‡½ÏRPkV;Ì»³9ò™.m.HùÚS‚2yòvÒ¸kz‡âWç’]® {ŠûD_ug ©FÝ÷)ý&û–÷ù‹øý~5W dÞʧ‰2ZC©ˆnʲþ8ٴĸ%i}ì·Ä w,>‚9ÄöÝÃáC×ô¶(;kÉãÊp–Ò=PöOÂ#j‚´©¡Æ SEôŸÈÞ$Ù¸¡ö)Óý¡SÙ*¢Ù$ÇÏ£D#}+”yHÙ¨Ž¨±uÈøN®‹É÷ÿíoø½6:îÛŸñ»ú„¸´>ôª(Sa>¹‚ÃË5‘W™¶ãDó¿|Æt9ÒŽÅ äJ¹\(Ší”wðÖÿ“Žw•ÔåÂêŒÀÐÝŠ2¥*mŽ×•³À Y±»cJß3è`²ýb`%»‡Ç=ŽÄ9æÍ±»˜/sWãÀÑÇ[¤®�}ÙЖJ† MÊõ%cZ©Ö`ÿ¦˜L·ÄüŒöNÄóñH;\œÕ7äN-½—y½^3*?ãxBöFèüïcgä½UT¿Ñ‹d_¤Fï_aZ|CT)žýÚÌð5–—ÃU"ÉžfõÛr|ÒíbÎ1æ}Àãæ£ñ3¹OÏ |K­åº‰Gâk‚©ÖeS»c~$x~T. •¹gH§Ws¤{1®Äh u7ZŸ•ïS¬ÑÊu¹¡ ×[WŽ·Þê¡34Ï£peúiØYýÃƾ.ßËþÎÆûs*½¤ïêûÉB–Öe˜Ä½Ht®$©|)×XÇ[_­m$?Õ×ïÎëIq^ßxŸ]ä¢Èä3ôà£ð§<¯™ŠwË…%Õ}ŠCú‘Ñé'Ü—wúÃyÈkTXJ{,zFÙ-DÓô4ØHe/ÀHï(û�Éë³àiÉÛ´ÆB÷i=G¶º^‰¯G'}g“mnòʵ¥çT›!üÙQá4ÎÉkPôý/öÝÃ}¨ëS÷OºÉöYwýXÿî¡Û[E¸“£Ó'Òæû„܇ºËö¨}EÌ«ã÷õM%eqǾ½ÐÞ ²±tÃϘ–Ä5‘ýwŒ;ˆè~– e©Ã§HÝÒN&¥#&ZOI²k;ëLmu§lÛæícµ­Ü³ˆñ/ ¯syú4ÉUùáhΫô •’¶¥—kÿVž=;ý$~ÿPôÈhÃð­#´Èë|Î1íÂÌÙ:2Ö½H±û‡æg±cåÝÙ9úîB«W\ù”º«°ò;bQ¦ÆpŒoÙÅúm>‘Ez³±ý(lßVu£Ô_‡ìì÷ºúòÒí¡\ƒm,¤Cóû’ÑӇΨ!¯jä =²òn`oÜžMå<C?­¼U?Ûg´É�ÿ„ûñ^ÿ zÌÐÞN*»ìÏ[П_Ë j#íHퟋ‚¡üŠ`߇›vKûJÐ}×Éu²ÝT¯dßz}Â>:GŽ>C÷ä([Àcé £{ûš°ûPÇÑŠhëA¯˜šMºÝÑ(ß×Ë3¥‰gÍ•-_èà«>¸÷WÐs6ËçÅt¤W\?úYd³ZS ´FÙ&&û›Õ¯Þ§ '¬¥n5’O¯ÞÈ•¢ùl_±;DcÿªpËó•pûà®Uç WUï]+VâÙËÏ˨ž=‰v½]xž¾RÔã]Yÿ¦ððÀ °x˜³åºúÀw&Ú‡ˆÕ푺q¢ýØ?^i€æýMd­Ó'ûÜC¹äwŽûTÓ;„-C]ŽºCŠÂêùÅÓÙpÝELûÈ^–ûy¶R°-KÚê,É mN•3䩆ע.8 Íü柡ܵÈ×3ðMQ¬½tüÏÈÆ›v(=PíW{è—ú¹ q¼šù4éw3©Ÿ;èûðåÈ{Ÿ±>¤LVzÙgRQ^Ò惭ãÓÛiÓ{Ï_{Eš\ƒ%úäž ièÍ?-íþYŽ=ýÐ í’n¦yÕ‡æžÖ×DÍÒžâ…gnÚGõKñ#faC°# ¯vµ\'Éáúñ`Þi ÚõøÓIÞm{z¦:¿Íö•-Á‹–E5¥sÙÊÞ¿ùðf²µ²uÏ•è_›éÌîÊঽÅTŽmetö¿ÎGϿѸ/—óàAû`FÕcç÷µ.U*^ëu²å÷³[×+Ë?pŸ·µËùÇó5r/ÎEqÑf”u!çe([ ^1ãVêçrÌ%]}{'Ò?xP‡æœð•òû„ó™6Õö¦&Õö¦Zj{šç¸CÑ7ò¤]O:¿*ªZinkyyôÜVì¼¢îº7e ^f7Ùå|?þ<ȹŽînJz'åïM B&4¡¾Oü²ØïˆÁo>û üŠÙo‡Á/Ÿý?/ûÕüªØo¥Á¯žýÜ¿6ö›OÏ^¾›`ÛÀ·XÞYlmç‘ç¥ÿ«H£öso&³…’ÚmGEô¹XéÏY/fËì-ºÍxÇ+–üˆ÷÷o ÉûxY¾WD?ý±Ôí$/‘~·úxE´‘Âç+~.jÏfŸË‡»¬"úùâ¤4z w‡WÅçbtW„šy týWÚoÄ6Ö|ì>eۃǷNºŸPŽoÐUµ_¨ýT†þþu,Í‘hœwˆQwŒ¦š7anº’ô@Ä«!݇ÆfZoc¯Š'6§ù4¯ —vu76ó^ï›á Ûð}¯J:ü¦žÐóß1[Ô+ÿ)•hW ­m‘ì+(ÂøE¶^–]¬*•vÚo¤ïJÆõ.íöÌà¾+!0§@»ÛoVðæáÞïõAPë÷mõ=Èt†hÝváµ >^>o $ÔoÆiZp ¿î—ëÕ´þðãú¨ÛU_“}Ò¢ü&¤öÖ y]«8θ*¼:S°Ò·ƒcÂFsB­ÿ´K{é´‹¾GTD·þJ®¥l‘ßòcaˆ§¾'Îêï¤/ßÿèç†÷'ãþù¿Þ–ï@‹œÿríùÓ®Šè‡Ó á´NÉáL˜{ËôhÝ’îÝ‹4TœÞ¥ð¼~´RO;1݆)†ts(]rSZáZ¤“›<nF%gÏ`±°Ç²Ò»ë¸Z)¢ý>½áÙJÓø ¯¥ò]¸nä…:¶mûÕƒô=ŒÖgzûÉn¿ÈëÝð3¢É½2ý]Ùd]ò„¤_ÍéGÝýо°/þ>íxÒ^Ì áÝÉQkÏ‚ßQ[ÿák,§æ'­•Ü¢úUëÕ¯–µ&ö+Óáð!eãF}ŸZö6xeêfeÞNÉÏ;âk«–•)ÖV[P½'�#p[9ÊoRPÃñ_Pã–äëÕ9­Ÿç…ùÛãÅWÑ–hí0ò¤l÷óÿeðSû?b<ºayè¤L~ò~ô3j½†ê¢"úkÚïv8§õ8Î>ÝúÒ:¤¯‡üMðœý›âi¬ÿ,î~P_fxÿnœî7mº=›ðó¶,—ì hú1¥p^eÒô©A.}¬¯…ò:a­õ‡nÿ½Š_޾#×T×û)ŽQ^ÅÖwÑz ‰ì[Ñ=öÇ(oá§„E­gu¿ xoêâÞ»JXhŒ×[’í#½ÚµÃöB)SŽö*äáûŸiÛIŸêþù=—Ö%šÓ|OÍS§'ØÄG><ß;"ËôøÖÒh­r©ÏÇî/ÿ¯úÚ?N™\_+ù<±¯‰âľV*íubü’²ôñJþüÛÏcõIvt×bí^áY{¯¯ÇbŒsüÿ ú~Âøÿçjoíµ¥ó•ÚS¼Ïö½/¾Ï–ꣷ^\¿vkô¢çY“5|µ°{„‰ìÊÔ— 3}ãÔtf*˜ wÒ Fß”à¿P†ys™ÚƒùÑ_‘ý ðW1ݦ֧óÍÑhoýL;íƒQ{NÔ¾°ØžÞ7èN±0iM̾gаcíÿº.]Ô½½Á¡öiÝùs²EK¼À¶w³åÞ]Åoö$ž™hðÂ$þqÉ<¿ú‚âŸ;_'ݘò£M`чçÈ=ÀtW7Í‹¯ÇÜåw¯Ñ^ƒxW‚ùËE9Ä<†ö«ÕË;£²d¯OêŸãìÑåïxîÓ}ü¤óZlývýì¤}ß7ï×ùÖÓ ki[Ô>/Ì/V¶uÛFü¢Õ^üþa\‚|Ôú˜´_–[?ª„¬ú¾®G¢Œ‹ûvøȬŒhÔ]Šy\iZ°`®0…æ‰Å”ñ¨n;™ë“ê¦-Œ[Æ]*,C£ãKjå~qèxfGdŽüV+ϸ ›I6Õ.`Õ2Ó‚ßGüíd³” Ñ*ÐÛóž|gyÊý¾´&ÿPìþòa·%Í Ë{Å̲®ˆ rÝîiÀÿ†Îú*ô¤÷„-rµpо{²mËõã€^3È2긾ç{à!axZX1àN§6FÑW‰w·Uͱ¢µþ z<&]˜äÒ+‰Íƒø1<ÕŸ›úèó¢€lƒºPuXDvÆË£ÑÛ·F£it_Fïë³c#] u´ï«Â.¿-ëuÛ;øe›~oYò»±ì©ºMgõ»¢zû70–¦A¦™ÍŽÞíÂS2„þØ~Smô~òº~B{¤°‡Øéú~k¹Öç‘ã»lk­éÛ4sZP+ƒ~$r*#i˜P6˜õRω,Yƒs¥ý`ÉD}•6k²\fa—ß)ÒD¶qýu¬õ]’}KšEAiãá9­Ðw+V;Ô4ʥƙùZf¥‹dFÂü qôòäªûêоSáOg„ÜÜ/"sšÅXëÂÆú‘ô¡= ô ß YVôŒŸÖ{-Ò^5ÕÕ³â„\×ÑhQi£7’-lœÈ/Ú=R+ìðsxEÑ?„gû›˜‡óeYEM É©¢×ýàO¸C¦£„A؃ÈýÑô Ñ£öÜý5èŸãÅË"è‡ì.`9açgÒ¯¬azŸ"œU3g =ÉÖäa踒‡½Â¯S2x,ï{\GÆØ.å|cS ÛJý £™þ´i!ðçÀ9ù–Æ)¯(äõ"èׯ_ƒ AÍHßDòåƒÑwŒßbùÜËa›S®_”6¡±›Û ýþ*cz²g7ž g™^é4 3ÊAwÚÈ_Ž7¯5ž¤¶?óxïÑ¿öÕ ´è§½•è‹™C²o¶Ìª42ww¢¯T¥£`Yèðô›W†k£Ÿvz¤-öÅUKE±v×Õ­ÑS ¹bWëŸwrú´¼¬Í•`¯˜v^{>«²÷±È€ÌÅá,ÈÈÃyAíw³½Ò¶±éxÕÒsóĵM} „µÏ/l½;kd¹Ãë@¿ˆ^¬Ywh 9‚²bü?F÷À¹³Ð¿JÑ^ý^Õ^y‘Ç´Ó½»ìôlë}¬†žæ ë`+­Ñ˜¶SÚá‡EVï®5–-ÖÞÇìn¨u]0R° VÔ‚‡{™p°ìuâÙŽg;?Ûi^ê.޾¹r>êPm=·û¿ ïí­g8Ås쮀OÃSDvÁÖÆ“¦…r­b…põöçÈû!(Ð&—‡¾É#Ù÷t{P¥â­Ôë4÷z%œIßaH¾fŒ„-ä–ñmƒó%}V`ûÙºÇ{ª£Œ—Qn{d't5Þg©wDcF²1+ºÃTvõ~¥á}'¿wÐxOvØ!oßà9ÏüȧÂZÿ!ôØß¸Ô;QŽ,#z­g{ñ`œÎX^.,ŠŸ2œÂìéŸz ydõ†óA¿e7ܶA<‡n0;|4þ»1¾z……ø§7ìQe ¢|AÈ‹,‘Ý»SÞá·=¼e’õk †k„ìÁæ¡O@7؇þd¥½AÄ3Ð-þãⓤcl{Þô‹ŠègårÞÑ]â‰,”ÓCÇpטΆæÉ1Ë^¶Ö„K•®„ö>‡rå/z‚ÏHÌA|Êã ð?ÜáÓËá£à9Ð¥UÛ+µa<‚òËØoú˜ö—<›.<.D᧦³4¦LDߨÖΑNc´Ó¬ÆµÞá«”ú'ê,mqÕUè£Kßshs~Je‘m4”–­êéœäSÔ€ö2ÕCdzõ=€zî?§îó A˶¥¾’¾O+¿<þ·‚¿¨´A's¸ˆõ…x†NëÙÉßSõ‡¾V„ß•Øâü”î$ÝRî+‚Ž4GdS_Õ¿å£_zÌ3-”ýaúÜIý¡†×¶dºi¦ž O“ß(¯ÜÈýáÆX¨Š÷‡©C¨û`yªþ05ëÄúƒñ}½ÞÔw«ÏúXN;InûÅ{Z¯Ôf+y¸w-}5ikóÞº“tZ4†®-uà¼]˜/~×.q†IŸ©qWDEUé£îÐ{¾³TÌpgžUâчBD¾æ±WäÕ¹ mðækåxFZÕv]ÔJïw¸Í"½ªÔZÜë´äƒw.†êg9B¹5ùÄ[ƒ!‘ þ´¨ÿXBs½ùÕ ¨{gZ04÷-GDúW rM ÷>oZ<kïë Þ8Š6½–ÂË=¹¥o¡ÝM ©«ë?Ý=ìªÞ<ô‡ ) {Ã'¥©n¸ö¡Pé ð*êí£•Ïrè{«7X‹I§œŠ×ï±÷;h›Ä¼‹é}¸ýY¶Ÿå}ÖòѲ÷yaÛûÚz dæ4(c–Ο|ÿ”£÷›˜ƒÖÒ¹“ ê{Ú—¾Ãüjñ~„²ƒ·-‹U?Ê—º7ÜÖA¿Ò¡Kš¡ŸáY³š\$—ðßZU& «vb,Û)Dz`ßrôµfи«2i,óDz#Õsc™÷lT>š3ÆûfÚ§CÔ¸&ù;íÜàƒ GöSSìüäª-ü"õï|Õ¿›ý;í•Qýûzôïå†þ}=[þÙ¿Ó<²«{ N`LH1æåó¦Ž9æ…Õ˜Gú„ù,ÆF;·mp÷ñ©ú¸ù(êÆy)U7ÅúøòT}ܬé}\ûÝ\Gýtàs®ˆ•ä;ø£_ŽGNûhx•GË1ÂÐØæ¨ú|Ñ/ù‚¾ß˜úÀ TÐqó«J³Ü!ge;Sñ�éÀ!çš µ•âôQ”™î)£q®dÈô±¼.eÎA[z%ì¨úHÊ”,7¥Æ¢õ}w©‰ÎÚoÏÛ)e‹þîÚâãawt~¾óBQ�’3+L C¹JÖ´žÕ k 3´ {¥;ã8Éžc¡\%{ªsIάˆÉíL\ÎP«[ѾS·|µCÊœÈ+ÚÀªdŽö¶”9NíxZd Ê$P–¬A+Êf•üù ɘ¾Rš³(þÒe‹ö‘:·ÂòÅ­äË\È—†ÕJ¾ Î´ù’ §%Ë1<ˆpƒ‰ù*ù²‚ä õ5¡qØ€,‡õ> a½R'±¢<1ùbÐÃkä\%h:*×h ó«¹‚ëˆÖ“UI{*´F³£"ú/_Qó¢?']¥"úÇýÍ+&Þ®¯ùpz)ï[c{Øô1f;0;¨=9O¿ rçHçªêzíGÐÅGn . c¾¥æˆrî¨æ‰˜?ôW}™½6v®/¼níÊìRÌ“Ìg\Z釴&—pö’Þ“%ÃÐûø\8Ë…yÙ17ÞL~Fú+¢çõõ[Éžè>ÒYöþ¸º¾dIÊ¡;¢Blûéƒá-Õõ´FEëî›×z{C¿ jß9çÐv~�='ïT&ðÝ?}~ï¡ôOßU×o;ñÃ`ïÈ}šB’ýëÖ_ë³Ôý¨¨ç§ä¾ƒ‡äþbZ—‘2ùP}xÅ?–ó2Ôæ:È_î£Ã\°¨>Ež[èûÇ[HgKäú@îÚ•îÒªOèÎÇPî,²Ù¯×‡µ"úO‡$]ßR6AGŒÆ—hÿÎÂ)’¦­’¦ùNίN—ùoWç™@Û纜Í;]©-ýcpnôBøÊµ^íKfGXßÿ¡hß'Iu‹ò¼lä/}o*ÿ××±çÿGÝo®Ÿ‡§µëoΓ߰iÍ TX ]µ4ž³F®?¡ª{ÔW®,›&ËvÌ+r~Ì늦9îä¼ÄÏ´?eH§ê4\ZuŽe´Òæt½”gÑëk,jïS^.éáòû=ÚiÛOÔ˜½^9æÿJîVáh}º8¡ücÄ îPGûcáy?ÛX÷«Çú£/z*Ö÷ÎÛB4p «¹ÿû¨_Ý·æ¸aîñôõûæßñÞÖŒ„gÇäŒÛØžôÞØŸŠ^Y3¢æÿ7òüÿŠüÞ×þ>˜7$>¾�ž¹–~dG2»=ÂlLÏ-2«²…{mÿ¹‹4…?Y3¢qäwfŠ4ä½>º×‰ùRî;W8ªs0–µÌrôa-å'æâÔâzÛq¬‰Ýiž­ËaÇ0Í»iþí£Ã–ÞÕŽ¢ï@¶B¾»æÈµ9únfÂü¼ãˆ}Oè+BC^šSò9“µ€–`\u¿&æ‡ÆW¹ï§i”ôÍ 7Ôœ“0ž1…(¹¶Ê ÚKot„ Þ'½í1©‹9BãÝdº³ͤO]þ1é_¤‡…²1·û»˜û †ÒKÔØt„u±uõšªÒsûªÐ&¤l…>Aô•g¦Ê‚Pîrì×Äš`èÆ =?Gô\™çc4öCkÅØó ò¥µŠ<a+zx6êêOÂAuå—ÛÞ¢êÃ:¬Ê.T¹ —¢¼¥ÂIû2èlFŸÙ”†.ÊX¤±4TKû y¿˜Y¯Z¶¨¤üé{=¡š÷r©Ÿ@·ó<xn»O…ÿzõ ŽfÔèèÝ‘ãñ Í:Ö×ýô$­¿xÔú ÆÏÞG©ýg ZÉnê5¬¿ä Ñ¼ÿÎõ•G±\y„çšV}®ùÏ5=£çšKÔC×±*ù éÎ< }Õ[—¸žõÐë zèl]ye¶GÞ6衱>03®‡z zhü}LÍ{L\Fw~i+>tÔ—S™å€.hIì'b»äGºÛªÖ`B˾â¢>ÃtÕVçž»H}ÚÍýý!¿:ü˜á.mÀxµN0OCë×Åx2ü÷Ì“OÚÏR»×m‘Uû ‚ç#¯ GÑaa%žôì±CÖNÙ9ð½C’²ˆŽ¢0GSbú@øÔ5˵Ž3¥~í®·îÔÞ§iÝ2§–æÉp“.]ùEçÇ£æÆa¥³jÐY«ThZ¸èQY?G!3¯ÕuÏ*}^»iÿô¹×¾@$ÊÛp:÷—÷©¿äS9sˆßh¯µv®ˆöÂU%èk?U÷|!žƒtW­´PòqøCa§ñõZÌ3Ó‚L“ôXú^£};/Z¾&¸h‡L¿†Þ{Þ¨caÖDV~Læyœ%ý–¬ÉÈJ«äÙ´i/“,ÕÐÿÁëž!ôÿ©iAºûïbýŒèMïôA·WôDO*zÓ†©µl¯Š�ÏëH–¹ÁÏ3^Ö¾ Ùð(ɆÃ}ë1_N'ÙàV²á.}mvÆö$ÙPßwdÃ]1Ù0Ïv<ë²Á¡é_l}vF¶”;X>¤'Ë÷hù>‘| 8ýÕµ¥”9,rRɇéûQn;æv)äÃt-&îJ%¦×ÆäÃ#,–³|¸ òá†dù`ú¸êísIFÔ¿%L´nÿju“‹dEl®Š>_€1‹Ö)´Lo~¨ÞSàžf˜«Ö/UsU]6L3ÎUm'#ÏC¼$ççV¤KsÕ@ÕÏþóÖ¿´ã)Ö¿ü˜‹Nžzg§ZÿzÏ™_ÿŠÐú×Ôí!”áäÁ#‰ë_ïZÿÒ~f\ÿÊÿbë_±6ÍÜ¡õ¯Ä¼ÓûøúWf‡•{CQß¶ÈKrĆú¶¢ŒYEßÖåð´„û©oŸI—B»·…4o%ú©#o—¸ m²Æâ“Á­ÐÏZÁ?xj¸ÏEßQIGÓ÷xPþ¾¹"-tã ª?kdø.ƒù˜ô:¥§LkB9l½á3ômó¡ßÜÊz-}g:˜ß‰wÈ>ƒ;C\«í$íC‡6Çéî= ÏPÊ»\¥MÄa‘æ>n7»ÏD£$—i~¨æ×˜w>+"õ"ø•h${j¤'¿:õoÆÐkê­Ð=ç*C^íM»Ê%¿Å–Fχòf9hÿ ­AAGNG9¬tœl§ArÑÙŠ6™mz9¼ÎtD›cvP›DðLû€}WoƒŒ6ióeÁX—«‰- åÙOåÉÈ—å±üååùç¾øþø K,¿¬Ñùõ͆ ÙÁß\ÿd:«}$Çmïk¾÷5ÓÙPè-ÇÞÙ¦sáÌè„?¯U˜ÁÃÑ à)È© û üQ~'ôyö%|ÕšãhO§Ûý$bF³<Ãn-Ñó²þFGhîX=s7Ô:9êËé-}dCu¦e®vô²øì¶Ïߢ9±-¼ùJÝQ”õo-Žè[‘X¬M{ËAéï„_CØg0–½ÑQý_¿õŠô£Fþ•éQ›PmBeårÛã´XŽÊý£á£DÃÇá+1W.'^£¹Šeˆxµû»"öì}‰ög¼å ¼åw˜LðŠ9}[ב®õÿÞ!÷€‘¿PþÔGBæWÚÙ ÊZæ©Ô4{%hÊbýÐI<]=KKãudz¾K®!íú÷­ùaÖé á¬ZºÉ¥Â¦£±™æ€ž~Ó'UÓDauylíYƒžm…žmëÝ•¯Ö1äœÃ°ö,e5¯=ÏNX{¶Sý%ÎÏä·!Ñûˆ¾þL:ïÔóƒ˜#E~&åyKLž$ê÷óI¿Ë²©Ç’Æñ¶$a’Žÿï_Žçé•ëÏÿá:ÿ”ó“×ù§œ[çŸrx|Ê}LYW;´ÆÕ©“µž»HßVµõW¹êx춪ùæ̸=ÅNº|ä#”í¥ÛËq{Ãâ¸]ŸbÜ^q;u™>Éqº<èÏŠüeøÝ$Çí Æq;+>n›yܶNfÜN;9‡q;1ï¤q;íeû‚·?„ø<tƒ”V”1«è}¬Ls÷ÿý rÖ+¢ï=EmÐ721 ²Ì Ú[Pýyg©á›qfú¶Þ ¼ûèzZÉÄmïýPݽË2»ðUZ‹|ï)Ð0;ô�dÝÕ4®™Îb\9×Wd:ß·Õô1ü¬‘ja¼üÐCrÕtVŽWòرÂ8v˜åw8÷ýh«iÑ {^3}L{Œx¼°ô>EeƘñô¹:tïéÊÐm«Õç.†îýŠ‹æ«TÿÐ=®•ûàŽÜ›‚¯hžZ“r&ñµs<¾6»÷¼%¬{ÞF9nC™îU|]]ñŸÇ×!O ¾Þ„ºB&Å×{Þ_ß&²öüeùÅäø:TaäëÊ'_‹O÷œ_'æÄ×â{ŠhA}ÛPßVı¡¾­(c–Ôû†$?zæÏ´sÍÆÇ0'ÉTcc桹ʵ­Á"‘5Ø >,•òx¾WÌêC6Ú÷N6r(LBzñuÒ,÷VZ½â&ãû’="_ï Ñ¿]&÷€Aw‹\.÷:Ô¨TÆY[dz¼,¶>J^e[ñ!µÿŒÖÞiýz`n´ý¾Jžç’÷BôFgXÜiСÛϲÜÞ¾íµò<í9–gê.ßIßBÚ省nwù⡾ÜÅC;ÌbZêæ ú~¨ø#×þL1m:ë÷3éÛäåOèßä8.Óq¼)mÞßÓNõQò<…ŸéŸ”}܃Ðõ½v(êê{¼×o~òþÅŠèÅ3 í#÷óE£cíqŒßßK{hÕ÷ú6`¢ý„¢o«Ô¿Ùž#ûÂÈ{?}ž;㚥lŒF¿Oaõôiÿ é´g¹`®ü6’`’ü1&˵˜¼~‘†þœ¾¶QXIÿVµ§ÎqÐ>FÚWKýuíN{¡Ö�>ÂûÞ—j\‹”¾8¸Níw¬.µÜ¡ïo¦8Ð/D_ò+S¡_ÿk•°ÜA{Ži ù§QþÃrM—ìâg‡J×AO:h±¢Í¬GÖ©s%±úæ³õ)Ûküú©ê“êùž¢ïÅÆô¸N¿Gõ|p‰¤öÙPOcýÐ~nª#J·`žª'MTŒUO/ÿÕÓâTõDº-ÑMü~üÑ2ÔLºþ’÷—²-‡¹ÍRlTr©"úù}.íKàÿZa¥ó9ÒÎBƒp!ìL Ëgmέ³õi’vOX#»R (Wú}çÒžü¨ô.Τx½»8½Za;8WL…¾8ö[ÆÏ”‰3)Î4“h*T yYÚ¼,:4Ú'Œ¹+}Gªb7ÉP/0Ñï`¿)4n�ÛÔ‘Ãþtw@vñ°UžÅ¨?$ÌÆú"=¿e„Þ Ï­Ò:¥û-SlØw#x#å Ëò•{L–ÓЛ g@Å)7C¥Á/Ú"îßAÏ~}ä€ê Ë5 º‹ 3ümðßî.ãýõýR‡,ÚÞ{V8ò€I—àxV÷vº‹MÖQ%ñkæ£Uçÿ‚î^ 4¨¿HžG:n«H«çßж›i]3)ãó­¢QÒý VO_ü„׌M‡Ì¢êŒµý2BãpŽÔÄ\æLÎ<ê/k²¾<¤D£‘c ¥^žºLö½ZŒñìo6ÚÒDÖË`=Àµ¯¦…)í÷@î'ô§Ï‹T²(Þgû)‡©_ß©o*ö”>WŒØ‘¿ù‘‘ù–X¸÷[ÂZ§#È‹Kœ¡–%õx¿TX¤­GOhçÑ·AÚÑ+½ùXñ™{¾ i/úrÍ/úV�cîTÿ;‹³Xôíl„Ž1:ÎR‘=˜&œˆãˆüFÌGZV–wsÜ!á¾øàéÔ‰Ÿ)/rStV…òë£52¢ó7©L+ÒÉëPãºàxã_¢=áh=Ù–ßÛ÷Î j¯ç "ZäyñÒ›´y"GêtÆw5òÿ*tfAÞÜýOÇŽÙÔL3ÛélmQ‰ðlBdõû1®J<¯8p•°ô&¬‹öC0•{"êÕ>ŒÂ;„§°_Œ^Ÿ³‡´£®áLÙg”0½ ù«ŠúãóÆ3Ù»ˆÓ€ BÚÅ +jN0ÐyÕZ÷‘Fç'µìŸ»èþ<› Ï‹{ýÜU$ï)1å`ܰömÒöâ°Y–3Mž·¨}6½€æÑ» +NÒÍXŽ;¦EË•*Mð�ÒtgRŸ„ýºì“§8’]Ç{Ÿ”ß}²ON•…É:M¶­ÞGœŸÈ÷Ö8Mb‡µ§xOî€{*žŸÆ0´ÎP ?¹VÖ×,ltÞh[Ú@Ð1ŽöU÷]ù2Œr¿]#<ƒÛÄJðAÙàdç ýkÛŠî¢} †¼böж “<›½„ì”…¯ ¥mަg‚½Ëþg0ü7"ÁŽ<¹:®;†·›²‡ž«ó÷DöéN~t^0I Ë3‘¤/?ïÚ'í üZxiO¶®3G¶¡^ú1.6nOâÛ|:.mY§¶EMvJbå¼!Ê ûNÜæµúzvx�2ï[±äZ¤‡ü¿eVS¶¶]œö aɧ0˜‡Û( …¥06Îü×±þw²±—x~tJÂù–‰ÎûÄö“¼Ñ÷“½ ²ô½]±´–*}d`�|a޾9¶Ð~ºRa X­´vi§³ˆŸ±@‡¢=˜EAôí Õ ¢æ®iÂLö/´Ò¹ŽÂ·Åá"stG,WÑ—¦Äú÷+²ßKýûÆþ=Äý»˜ÇÒ}ÜÏ­lßd¾AœQ`ʧçEßòžk²#@gÈF²¤ «îwüPøGu†šú»þ¤ oÓÃÄÃÑþkÛxá n¦‚\+ÖÃX“ÂX ?BZßUýîÿ’ö50/<@w…£>Uÿ}âcNFcCDwk"= ×ÕÙEz'ï©;¬Ü-tf`Ï3“eÕ}JVÍ…¬zJÊ*»g°9éxÈì©ûÀS­î«d”Ü£¨Ÿ¡¤ó“Tgtx@·É�!ßxÿ‹¾§IŸß†lKƒZNfp0ràºëÚC9æiêÞÑó{G?Ù{2úéÞsÑ Ú_äµ_ߤ±<”8ó3ƒôý"´SòÄu½Ÿ˜œt'lØ'²Â}";|2÷M1_ÒøÈé3‰…èÃù}%b±çQv²8ºãdm4rR‹î?9¦p'EŸ|?zää…èÑ·³£ÇÉïíâèÉ·k£§ßÖ¢#oEå\óícÑstï,å­ó­¤áÌMN²_çycó6Y÷nÏsÂ>r‡#_•í—Cß»é™tÒÓ ; G¿Oáå7Ÿg…eàÚëBôBïá´Þ~:p,úÉÀPô<Õ#ÕYòxÎò­2â'#}âTä 8yS¼ù@Œ šÄûƒ×‰3ƒ%âìÈuÂ=‚1yÄ'¼#}¢rä ¨ySÔ| ZÞ5‰Îw¯ÁwKDø]ŸØñnŸˆ¼{Pì']ÂÝ'†ÝÅI÷›CÛߌPCÆ 7bÎtP £ ¹Úó‹UÎ۴ƹ­UõS£œQu$ho\Â÷°_ÙwП/ϧôç“êù˜ñùä2Y—ò¾~‘wl?=k²?H™2ì—:qì<ÝaõÜ–”mÒóJãsøa9¶º“žå~¦…BXJˆßT<c›ß}8B¼¯Ë^ý|e¨:åj¿´Þ•£1õ¾s‚÷Ž Þ/ÿý Ç'xb‚÷ÐwCd‚÷ Þ{&xïÿýõ&ˆŸ=Aüý¼Ÿ þ®/û¿L¿m‚÷Yã¿_pj‚÷¯Lð~h‚÷»'x?,ðNð¾e‚÷ÔïëøïçOÀßówLðþeý}]]Ó–¶¦ºF_[[ÝÆŽ¦ž6]k{k Õ×Öú¿«»®¹£‹B´6@6<èÛä[Ôæk_¿hYWWG—¨nïò7v¬oGÈ&ç=嫜 =ÍÍþ.g`K'¤Š1|y[›½¯­¤k}ÏF{`Y°Ñßhíh‡ŒíhÿrS+Ò 8KUìÖng{GÀÙÝÓÙÙÑð7ÓYÕXÕ|·cG×E‚Xêk¿!àõ¾€ß¹ÔÙèjm_Ÿì¿¹µÉð¿ÆŽ‹º{Ú=Øî[TÙÑÚðw‰b*9Ƹœ¹÷&Ò_ÝÞí ´v7·ú›V¶¶oHÈ»£!àkmw6¶ùº»HÖ…d]HÖ¥'+:ýôWˆ"9NG;(êi tt9QÑ㥑t‚˜¶&gyÙD1“â®oëhðµ9»ü¨k{£Œè÷ ¼›ÐŠ=M¨¿•¾î€*tN¹ª›„4“97ú-M¤,DsWÇFý)ge¼¶—Rœ"ƒÇª†Á E¹+­¦bÞíëìô7%ÒÓÀ»Æ±l¤sl’Ú%ª’Ü;šžÜQ>£ê!1ßxräèˆ×INB«øÍÒŽöM~t†®”µ2Êk|ºôì& *™–å±jšq«-N^Bÿ[#û@Oå¹Þ +ooî9‰ Î]4Q¿3&'Úý›ËÑÉ|àô”mš’Ž¢ä>'j|ÞŽgªþ ´L6ÑÓíWònT{èÕ¡ú£¯'бÚïk’<¯Äþ©'2élezk»Zþd^êB&“OFldâS·dRÚ›)ÃÉ'Ð%9•éD2Δ|0¿t˽¾¶žQu gür0¦¿ÃRƒ¯qÃ(ºÇIÁ'%eZ«uy>¿$ð5¶¬’ãk÷DéÇH7͉éÅ•HõëX^)d}*î¸?±wêÑ'ìÿ©ÈOHK$ŽN‘–÷´7Réb#mNjzïCŒM@SRò“'hS‚zRwrF‹à'MÆd'OP\¡¬jéÒeTêºJé;o' -*建:z:'—“Éù5›Ø÷ÖrýŽ¥•L¢ëIÐX—rœœ ~BvF:ÊÑžëý]R™I”]úè? %ÓÝ”JŽŽcÌ2•~=š™ðDB4!YÑÉmrº÷h]NÕ·4G·ÛüææV9’rê êê±¶lmßÔ±ò]±BÎ$&Õ†‰Y1-_^NEÒqwRÚ*c£Þ;ÙìĨ<œ[»»Õd‰‚ÒW· [”·£ÑZ›œÝè>Ír$t.�§/è.têï(Y¤'¶«yžñ}·ž—³Éß,{cG»3G©Ë2Ex;ý4“ÈE<g³¯µ ÌœM¹Š–Î.g]cks¼@49W ÷Þ ¿ª¨i¾ö-NÏ7»9}Aç‚6J!i^§O3Wuú»|DI|r«UÌVñij MN_ –‹¬‡QsÞ5=þxš<ÉS¶±§;àl@U¨I08«Å‡jð9;»Z7¶Ê*¡&¢úôuuùH\ÚуC”$é;ˆlˆ%s¢òr‡§fqvû=´I!aî>ºÁ ©0ë4=€&OàÅØ$Ï0‡_4»Ú Í:fua|OI¡“'çR˜;õvW²8Ð6t‚zÚÛ:|MÓâmLÎ%k« mÞÔåÛL„w÷t5û zJŠwˆOŒ>öûV©IÆß/íhì�Cµú7'òÙª5Îgg›/€êÞȱˆ¿G­Etwúô鵜0u›£|’QuY±¸ñ¶‚†ÆæÅù ·äßî¾õæ›ðÿ¦†[}·6ùšà’cÎ-®Å®|j³ªånC{r7ß!­úi¢¿jéD?´òßz?w)¹&1ÙöàÖH¤û“Ôæ Ï]þæ6Zt·Ì>1ìšØZŒôkoíX¤:‡áyKÀŸì·´Åו췦]¸4Æï1 ºoÌoeGûúäøËQ¸TñË:zÚü±qÚïíhEiªî«\FáS- ĽJ;:Úü¾öD¿-4±2DC¡|&U?T(£<Qã`B:Tã³,ˆáY@/“‹ü\G­t×¹Ô;΃_ÓROΚä0’.¡^æ,M+‡SrJ“ÃQ=p~—sÿ¨0ªþ8½ÎYžF–›C¨—9eÉaT]p ù–ÿŽíy¬^$êaÜš¹’Cçȱ$Ðçㄸ¬h¿ê).=¨ŒˆÔ¨ðÝ*ÞºR¡Æ`¼vé¯d¸Tº …N«žsAÕ}‰)îRCº†WŠöœuªÒÕ{®öC•hCÈ ¥Eu+:S®©Þí…r§¸‚ãaãÞ®³£»U&”O9“®˜·>öÅE‚K÷RïV57CâzÏÞ".6âñ’ýô°(çYoq±‹4Ê#!N9ʼn‰Ž‘ô¬gAáWHÙGqº’ý t-§÷q‹4Ê/NíMFQ¦Gí#OÜOu­zžK×ÿK¹þu5z}°ÇR®SÙóÙ¯\Õ I•X:T>ýa¹N;{”Åh7ÊÄ-ÝÿFÁüÙÕ-°%eIÕIûÅ<®£É0î(Ù8–± ‹eê™Ç2WâXæÂ8á2Œ',#Æ6&iÜRñÇ7é—4fI¿ã“ÊGC±pIc›ôK16ÆeoJ™,ëGËâTcƨ1Æ@WŠqÔ ·GÉè˜^‚ÆãÆn©|(Úeyæ º§F_ËI 7¡Ž£&uݤÀ¶øÚ›Úü]Ó˜ŽU=Pµ›YÍŽ%¥ëy„Ú0Û‚äãð1:$_!¹n¨øþvçz_Wé_mÄK~¥ßÊ8=F…¼KiÔ¤G/”S-¨ã\ž æFOj¦®vÇÓš\Yu"ùMLM—³ Jl“ä.̹d+ ÑÄ­Ó†ÖÕÝ=˜g“RÚ͘žoºM=¦ç[nVÏ„éù¶Åê™pªõžùÕí¾žõ-ñï„^Õ"Õœ~/Ep‘rAiÌ ŠDOò1öZUÂêSÇf*¤(>ß7ÌI ­ÕŸ‹É™·ao‘ØŒÒG³§&PN-ÂhRD4L¦GMQ…úHGkz<¿‰x}ê“ÅÄggÎ͹"5݉É(^”óxÕ¡z§_¯à…4= ´¶ƒ3Ö¡¾Ê)ʾQuweYùj!6ÒôV,Úä늹›ü›u·Èqûên©³/jnn­‘?‘ü3©ë•cî)÷Tv›ñ/Ýà¶²Û‚÷4v[áÎ4¸m†03 n³!L»íøg‰¹‰‡V “Vb²Û$¡9€¬hô9`“C†´Ï¨|Îô¬ù@ÚefÉ6ò±N¿FÆGeÑè0Ý?¾X†µê!Ÿ™òôÔŒég›QÏñì/U¾‹U¾ÏÚÏH¡-ŸÓTIUÊž~ RTeç ¢Ñç½rÿcŠxŠ–DBŠšïŽF¿•Ë3'F{r†´çò8âì´½T>O¥ï·~3ãÉiOdÆRè”HOAÞÑþ—ÂŽbÈøËÍ©HŽ'µÏF/³ftÎ\ÞË%Èâª$zL(ÿ÷Ê;ŠÆL$N 'à =‘hô çŠó“¦gïtEQØ$k…“”©v4‰FÉ̦8›*‘ÄzÉÒëe>êíÿ6ÑT“ÈwL±ERœŠwòï¼Mû^Ë’óÅ…Š{)ÎÊhô§žT¼;Nœp4ú3ÔYšc,~Iâuª‡W¢Ñÿ~1¿3Q¥î™1xYÄKkXÖîüó-T¼ó?²å_™‹J+N/g­"¤ª«…TW åD¼ÜÓ­„œˆ£S¦RÉ«¯:9 º÷mHõ¹G$­ÖX¿”¿oœ±(yfšÝÈxù™`;»‹A@=�õüoõBÌ:óÿ LsAëÜN@�ppp pð`ð>€Âž€ö¹ ØØØ ˆ�öö†�À�‡/Ž�^œ| 88øpA˜æ¡ŽæY�V€ë.(ëÏ4y̶qÝΗõ§êÓ ¨â:Ey²&ËÈ \Âe¹�ÂfUj�ŸÕq:,Äïæê£ª5ÿá®>npŸ4¸ßQnþŒÞþ7ç»ß“üEñû"á…øW§â“K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿K¿ÿç?““ö§x€=ŒË{¯d\ɸŠq ãZÆõŒ›·0ncÜÉ8À8ÈXcf¼ñÆ»Gïc¼ŸñãaÆ/0>ÌøeÆG¿Âø¨6ÂÇg|‚ñIƧŸfüãÆï3>Ãø,ãsŒ?f|žñ'Œ?e|Aáëc c+cc;cã,ÆÙŒŒç3Îa¼q>ãŌ݌‹{—1ö2^ɸ’qc´¿…p-ãzÆMŒ[·1îd`d¬13ÞÎxc´áã}Œ÷3b<ÌøƇ¿ÌøãWe|ŒñqÅ×àçSü|šŸGøù}~>ËÏçøù<?ÂÏÔó\îsUÿ—}6¨Mñ-£íõ–Ëäv.²jWn‰k”ûV„•Êí‰MʽžüŸPîÚÿó—Ê]HûŸþN¹o£þ0K¹WSß.Tî¯Áfån¦>ý˜r»©æô©,ï(÷ƒ$$.pxÄ5Ïa7À|Ó Ì˜N¤cþ:»QóvƒóivSø?púðO›Í4ƒGÒÊ•» 4¤qš‚7Ò8Í´SÚQå¾™ÚôWÊ}'¹/*w%Ü–/+wÒ±¬a7êÄd7ÊnyF¹¿JaþB¹ë)îo¹-¨¥lì:§ÜÊá‘Δû˜f”eÊ6®CÄò¬rÜÇ”ûÔÉ”•»m=ÕÊþäv)÷b¤3µŠÝ mêCìFÜ©Ãì¦ð?Qî6¤?õ¹®àŸ~™rBøô|v£½Ò¹ì‡�é=Êýx)ùçNòýQ®ô÷•{7ê9=ÊqÑ.Öy\^Ôƒõ+ÜîÈ׺™ã¢¬O)÷7œpsÙï¡0œæm¨ç ¡Ü7¶Œ”{ Òϸ‡ýÉÍtÞ„2f ±›â2ŸßLqÏn„Ÿ6S¹_ Óæ*wõ9í&åþ…)RîƒüšÆ<Fû“¦ÝÏþõp3¿FýOã6%3‹Ó"Êý¨ÛißUîU”×UîF½Mû+å†8Óþ§rÿžÒÿH¹_E}Nû“rÿèÉœyíÇ“ûë2ç²21ófv#¯Ì»Ø šy»QöÌÙ ¹•Ý 3s7»16d±ãAæ÷Ø Ñ“ù»Q™ÿÝc2ÿ»ñ'“ùê0Ñð¹rÓ‘T[†r_Kc!ËkPÛ-Ê='LWGpò纕c ×í5¨ó¶3Ù úm/*÷lÔ§y2<`û)Ó€:´(÷—(Ìo”û ¢‡eÔópOŸÁü ž™žÍeýÓY~åš^Íüš§×s:ækÊí@^ÓûXÆ‚¶éÌû@óôçY.†é?Pî)üœ&Ê;ý”r_NaÞå¸äÏ4Ï¡ðÿb&ù$Ä aiº;Ãà¾ÒàÎ5Ŷ‰Ï(6¸ï2„¹Çà¾×àn6¸; î Á6¸5¸÷ÜÏÜ/ܯÜoÜ?5¸ÿ‡ÁýžÁýƒûŸãîËÒ nC=\–gp—Ük î:ƒ{½ÁÝipÊ.ú�ý€�íy}�Dì<Ø {½¾ø�|${�{…²óûàIÀ7ÐQÄS€§ÏÚ‹+ÄÀ³€ç�Àƒaü1~Ü$¿gÿ=ÿI¦ñ›LÏ~¦ý9ÎSÏ€iû:—cÓqÐP¾Ç¹™Î!.S?×É^.·^gúùõs ú ýü„~vB?7¡Ÿ™ÐÏKèg%ôsÓëg%.clg<“ñ,Æ—3v0¾‚ñ—_É8‹ñUŒ¯f<›q6ã9Œy ¿¸–±“ñuŒç2žÇx>㌯g|ãƹŒodœÇx!ã/3v1^Ä8ŸñMŒof| ãÅŒoe|ãÛ»0.d\ĸ˜ñÆw0¾“±‡q ãRÆK—1^Æx9ã»{—3^Á¸‚ñJÆw3¾‡ñ*Æ•Œ¿Âx5ã5Œ«W3¾—ñZÆúéŸûßÏxãZÆ0þ*ã:ÆõŒ}Œ72nbìgÜÌx=ãÆ­Œd¼qãŒÛw0îdü5Æ]Œ»÷0ÞÄx3ã ã-Œb¼•±Æ8ÄX?*ÐË8¬Ðe„Mñg)C&ø™ +Ì€4€00°2�Ó�™�`:`à2€00 p9À¸ð%À•€,ÀU€«³Ù€9€k�ל€ë�só�ó �×n�ä�r7ò� _¸èvd@>à&ÀÍ€[�‹·nÜN·¼ �…€"@1` àÀ�Y]”–Ê�Ë�Ëw‘t@9` °p7àÀ*@%à+€Õ€5€*@5à^ÀZ@ à>Àý€u€ZÀ€¯ê�õ� ÐhøÍ€õ€@+àAÀ@`# Ðè| Ðè�=€M€Í€ ` à!ÀV€¶zÍÊ{ø¥0�Øx<ñ�j†ØØ x ° °ð8àë€o�"€AÀÀ^À>À€'ß@=Ož<�@Uφå˜U·âžòºUí+;|Mº»ZZžÁ“o“¯®±cc]wO{݃í¾:uæ²®î¦õþ@™4×£Ž4ëàÆ1a(:|ìçµ<@Ê8õã£c†&;I7ÑÉ ‚4wùý¨Î=OVÚ]º‰ /M*Ü„i¶¶7­Ù²±¡c<AÞøo'[!h’Êò¥-þÆ Ý=Ç 'OÆ€ŽÊ€¹#~¼ÜÀê(õxAÔ9ùñB”·ÿ^žÈ/€rݫ̲²ËßíïÚd0 <^heB`ÜÊXÂxAÖ‚U:6«CÚùc‡”g¥W5×Õ­(/Pk ¼¬{ü�Ê^ºñ³<Ï<Np2º6Q#ªP´£ 4nSª ã·¦ Ã6-&5¡TRÁâ–L'(-RŒ¦»²«# -Œh£´¬0öûŽNÿ8 J§ÄÁ�u·”–—O"ÔÒI…*›T¨å“ U>©P+&jÍø¡Ö·v£mÙ`ɘáº'’xÝI¼î‰…X÷„B¬{!†÷“8݉:`ƒÊã™°[t!9(COÈøÝJË¥2Ì8ö{²3¢øbì0ÒóÄ(lÜ^6n7Š·ŃۑâÁÆíIñ`²+)+n³Î Qœ:e‹¨N7šwÕ•”מÊJV¯-¿‡=”TW²æîD–oF/¥ç}Zümþ®„ýŠÉb&0}]ë» R13>A’6¶4ù;¾ºR–ìS#IZúdó.qe&þl°÷Ô³Æ}Ø L¢Ç-7'yܶ8ÉÃmxîIN¢'9‰žä$z’’†ldšÖpû@ÁtISúu·ôcS¨l —U-Ý ­2ÉWŸŒ‘L0Ç=¤´ºø|€žÑb#­Ñ³.nA-áSéÉòuôôbÅUO&9ey aFeÆà Žˆ ©Û[›Z»;}Æ–X¥Õ‘œ¤¤|KIŠº¶¸8l÷o¦ŽWÎÙ#&]ùY¯¤ø£®l°ÏZCˆºVl„�™à)ÝrYaª‹Ý¨ :¸Q{)Y[Uwæk«D]Þ¥êê6ú76vn©klÙ@OÝ*êÈÜR¢Ïú_W“òjBtô._ƒÚܯå<n~õ=å57ççß"êšÚxfG†dvMmJ‡ijëÞ‚©Hs' hž¿²¬tåM7»…. @u;Y˜‚SÃ&šÕß¾IÄt%E¾ÄdÀ­nã2”Ú6nôuâmìiOôèTvuð"àr &úI»©I~dœ•|»Û“IïtÉBÖuIw³{K7ø ÑZÛmsc7½ óÂm~§ºvFtùšº -ºõ¶›ߺX®cþPK ���(©Æ@¿“ž/��¹��!���com/sun/jna/ptr/ByReference.class}PMKÃ@}›Æ¤ÆjmÔ¶ë)íÁx¯xP*±ˆ•‚Çmºâ–&)ùêÝäÅ‚‚?À%Nb9ˆ{˜Ç¼™÷fg¾¾ß?œ`߀SÇ–ÛV°££®£Á Ê@&g ªÝk .±`¨º2ýԉ莦ĘnèñéG2Ë—¤š<ʘáÀõB߉ÓÀ™Ü™%‘s>¿"'º å1Oø@>“‚õŒA˜Fž¸”™Åf¡õxŸx:š4¯hy-ü0šg•Ý ö`1XÿLdh«7¡ Úc>£JÉη´¯20b‘,« MÛýCÕmÕLCöHI(®RÖ'^!4; °Î”{ëeÒÔWb7P¢xHºj8Âeõ_*XÏ]Mꪒ'#×Õ~�PK ���(©Æ@†³¤��ï��%���com/sun/jna/ptr/ByteByReference.class…‘MOÂ@†ß…Ba­ß **âgá &zÓx¨‰FCŒQýàª5ØšRLüO^¼`âÁà2ΔZñ#±‡ÙÙ™·Ïλûöþò `e SLs6á 1‹9ó:Š:Ò;Žë»I³\Ðö¼ %0\s\uܹm(ÿÜn´¨2Vóšv«nû\;mR­éÝVÛ·zãÚÕ»À¯Z²NÕ¥ò•ÛTÛ$5-æ§îíV‡þ–@¦­‚zo›¹ŠSšƒzòÌëøMµïð1?pë7ö½m ‹%ãœ-bIDz¬Xƒ)Püg,ÂoE_W3yfIÃxŽ(_ o–¿¹äpü²€N&ùX²n…W _}V4ó¨l¡D“ù‘h·…e@º’|†x¢,ƒ¢¤CŠlRfôTÂ0­L‰¤dí@E{F¢Rè"ù…B’bŽ0yBN…¨\O¡8¥ƒèµø†#¨Ec²JVºÐ²ÄLý­@#ÌöñdÌ“1o“o“æ`Ù×Eú1Æ¥ÃF±Ïe6v™ UùPK ���(©Æ@ùù¦��ú��'���com/sun/jna/ptr/DoubleByReference.class…‘MOÂ@†ß•íZ¿@Á/ÄÂALô¦ñ€Ä(!ƨá^qÅlMiMüUÆ &üþ(ãli ¢‰{˜y÷™™Ýϯ÷�;(q$1Ï¡bAz9iò‹X’f9‰•$V–my‡ 1£ÔdPŽœÁ0Ù°lqæ?\ ÷ʼîPdºá´ÌNÓt-yƒŠwguŠ–óPéúvåÞ6+ž[©9> ªÏâV¸Ân‰}5Y!þdv|ºËj jWxÍþQmG.uB9~éønK[²PæpûÞ|2uhHKSÐ1#½5“X×±M[0 ÿ¶ÆÕüÈ*Æ©ì›SƒçŽe{ÂeÈ¥3‡ šræ0ƒFƒö Óõà!´ö ¦õR ú"r) <8Ùq:í!F(§ÞÀ^ɃN–ÓL!ŽiL§÷UHa’vIH‡„"Ĥ¢œVß0VÞê!6ंÜÝÌ:°2[YÒ›¢*ômò¡Cj•*Ë›¼Üƒ¢3>Ú[ŽzÈñxÄão–*÷y»áœZÀë!ñáAbyhL-3¨²ßPK ���(©Æ@!±²¤��ô��&���com/sun/jna/ptr/FloatByReference.class…‘MKÃ@†ßmÓ¦ÝÆomÕú]?Ò¬ 7ŃBD)"*½ÇºjJM$Mÿ”ू€?JœIBÔ"˜ÃììΛgæÝýø|{°…ª„Ži‰f8›åP–˜Ã<‡‹:–²{Žëûi³Úнk%0Òp\uÚ»¿Rþ¥}Õ¡“ñ†×²;MÛwxjÁÓXi´¼ûz·çÖÛ®]üºÕñìààé\Ý(_¹-µKZÓâ™G»Ó£_…%몠ms·IJƒPM^x=¿¥,‡ûL ò6Ûö£m QË&8[AEǪ5¬Ø€)°ôß`åAɯªfóÔ’Æ;ó7P¾@ɬþ2ÈãÄǑͰ/¹7OÂ[`»ñ‘fžT-,ÓëäÀŸA–$Åív¢ ÈÖ ¯/”¥`P”´cÈ`C”‘ á• £1ሔ¬-ÔôW¤j•>Òߘa¤)N¦HÈRˆ*FòÅÙ5¡ãKŽ¡4&«d­-OÌÌàh³4BùO&<™ð&©sÄÛ¦9Ø@>äõ‘}NpÙ°°ðÃe>qY U¥/PK ���(©Æ@þ†«��Þ��$���com/sun/jna/ptr/IntByReference.class}‘ÍNÂ@…Ï@ia¬‚ €âø[Xˆ‰î4.4Ñ`ˆ1jØW±ZS ‰ïäÆ $.|�Êx§4Œ±‹;wî=ý枙ϯ÷�ûØåÐçˆË aI†eŽVdXÕ°¦aA=¶lË;aˆåƒræ<†dݲÅU¿{/Ü;ó¾C•tÝiš†éZrïÉê1ëM§[íõíjÛ6«Ïž[­ÙÞéËx®°›âˆ”FMâc³Ó§Y!Þ^c¼·Â”Æ ¿uúnSœ[ò”Ì4m¯mL ¤tdd(¢¤aCÇ&¶tlc‡aíÿy ¿S]Nã\;–í —!g”§ì ò”ù£LJ¶è8òj\úžÕVPPŒËr %z‡8ä§€‘ Nq†v‡ˆP¨•èì²tŠœV ™¥L«0‡$­’ ¤”Ú™Š6B¤R"úƒ™C”b–09Bæ}Tv,P2›§ƒèuä½ÐSSªxe%AÌØïÑ 4Âʇ<ò°ðhi áó†P_Cœê7Ö'\&B—Y_•ûPK ���(©Æ@àæ gª��ð��%���com/sun/jna/ptr/LongByReference.class…‘MoÚ@†ßƒíËGB!Júe8„HÉ­U‰%UU[qwè†]ƒÔ•C.DÊ¡? ?ªÊŒq@•º‡ÙÙ™ÙgæÝýó÷þ7€C´$tìJØc¯Ê¦&ñ/ÙÔu4t¼È~t=7<HÛ­¾€væWùžë©O³É• ¾9WcŠl÷ü3î;Ëç8¨…?Ü©@³7ð'éÌëŒ<§ó3 :=ßžþú¢®U ¼ú@¥v—ù™¹3žÑMÑ0¦*ì/Æ0qiÊɯþ,¨s—Û”6p#gîX0Q`Ó´°ÃÞ>^ëxcá-ÞYx[ ñŸ±j›kY;ä™% ÷Ùw½P»µ¦6N„tÉmIºÝž@>FøMºhÒÇà¥AIv‹NÇH“dÛæÄ-y)Xd%í@lãyÖ² 9äigB!&\!Íí‚q‡T»±@ú‰“‹rÏéf™ª+«±­˜Å^‘ºÐwñÇÔSêÌ7e{Í$ffs¶*ÍP[áÉ„'^‰:/yG±N3â-½IpÙ(Q_‘i&2ËQUåPK ���(©Æ@r—ƒâ��Ë��+���com/sun/jna/ptr/NativeLongByReference.class’]oQ†ß Ë—¶– -´öCkZ»~\b¼¨Ñ„†4Æ6$z·Å#nCwͲô_©hâðGg–ÍR÷bvfÎ;Ï™9çüþóã€'x¦£"!°)!±ew_â¶9Üa³+±‡lêØ×ñH ûÂõÜð¥@Úª¶´WþG%°Üt=uÖ¿¾TÁ…sÙ¥ÌjÓo;Ý–¸ÇI-üìöšmÿÚîõ=ûÊsì/a`Ÿ9¡;PMßëœÜ¼SŸT ¼¶ª T¬)éDVçÍ3§Û'ìú"‘@®§ÂÖX•ë$nÙª..‘ç~?h«7.w\žÛÙñ•3pŠó&L°)˜°Pe¯f¢Èá!Žt<6q Û¤ x*°ÿ_GA'1«›ZÕ¬Óè6Î^ ˆgœ‘4ô[ßõB”fÆŽhæÂœ´@žoÒ½ëô_בïLË+ÔÔ"=vÁO?Þ‰<Ù%ŠêÈäk?!Þ#¤FH£D Ëd%ý¬a…<s,Æ]¬ÒŸA…Ô e*}‡6B¦v8DvZBšl :Ö©h#‚Ç1Œ½5Ü#œàË‹±'Ô/«dmÝ fn¶¹Mغœ O&¼í:æ=§>x#â a|MpÙhaçÖœF2çF¤*ÿPK ���(©Æ@Áüöe¯��R��(���com/sun/jna/ptr/PointerByReference.class…‘ËNÂP†ÿíôX/¨Ü¼ƒ¢¥&b¢; &bŒî*µZSЉo¥. qáøPÆ9ÐTPŒ]ÌùçÌ߯3ÓÏ·w�›Xçˆ#«b³*æd:/ç»E–ääb»–my{ a½Xeˆ8ׂa¼bÙâ¤Õ¸î…yU§›ÉŠS3ëUÓµdî_F¼;«É°R©9R³e—îm³ô๥SDz=áî?‰á »&v2ú€Ï÷ìÈÏFÍz‹€É¡†xSxÕž%~È´^üÃÏÏ–[G–ì2ý»›{óÑÔÀ‘P°¬a™¬j˜FBÃRt¥2òÿOÇ0ûÓ4PÒ&­ï¼|yÈÀÊ$õ²Ü§á‚2oö%YýøÏíeôãá‹@Ž~º ùD@@ŒPÔ(ÛFˆ3Xì…T£9@Qd1FJë¹0Ž :%!áÊä”ÞQã¡ÂÆz‘oÐÂç `žì ]Xª÷‚“jS„crë>vŸ•.n´U‰ûÙ\ŽÊ÷ñxÀã/Iµo‹ú#¨]^Ês€‹u …¾9Õ`ÎtוùPK ���(©Æ@ Åšâ¤��ô��&���com/sun/jna/ptr/ShortByReference.class…‘MOÂ@†ß…òµÖ/PPQñÛÂALô¦ñ ‰FCŒ±†{ÅUJ 5¥øŸ¼xÁă?Àeœ)MUbb³³;oŸ™w÷ãóíÀ.Ê)ÌI¤1ÏÙ‡¢Ä"–8,§PJaE yh;¶$7ÊuíĽS“5ÛQ—½Î­òn¬Û6dknÃj×-Ïæ}x¨ùM»+°Vk¸j·çT[ŽU}ô½ªÙt=ÿøéZÝ+O9 u@ZÃ䉾ÕîѯÂHw•_nÓQJƒPMšnÏk¨S›ûÌŽòvZVßÒ‘Á‡U9ÎÖ°žÂ†ŽMl騆!°òß`ÅQɯªfœóÔ’Æ»rmÇWž@Á(ÿ2Ècîã¡Í /¹7.‚[`»á‘f\”M¬Òë¤ÁŸA–$Å1Úí#F¬Ä_!^(‹A§(i²HóqÊô¡ ˜¤• S!ጔ¬«$^«ˆc&§˜'Ls*?”‡(Φ©½_r=¦1Y%+hb&FG+Ò‹?x2âɈ7ƒÙ·Gs°LÀ ùá’A¡ôÃe&r™T…/PK ���(©Æ@|³ƒ���™������com/sun/jna/win32/StdCall.class;õo×>Nv6vvvF®àüÒ¢äT·ÌœTFžà’çÄœ½¬Ä²DFÉäü\ýâÒ<ý¬¼DýòÌ<c#}¨FýœÄ¼t}ÿ¤¬ÔäFdåŽ9% …™yéÎùye©y%™ùylŒ Œ L@ÌÌ�Œ ,@’‰�PK ���(©Æ@Åð˜”��û ��-���com/sun/jna/win32/StdCallFunctionMapper.classV]SW~ Ù°¬"QTÄÚ¨X(QÔÔZÁbC  Ò¢muI–°vÓ͆k•¶öû»wÞöƧcíLtêŒ^v¦¤ÿ¢ísv¡ˆZ˜¼gÏ9ïÇsž÷=ïîŸÿþÀ!ü¤âu$ )H©`XŠ{0\‡Ýx#Œ7UŽi©6ªâƤxKNß®Ã8.ª¸„w¼«à=㲯H¡K1¡b2*²0¤˜”îraLÉ=SÅ4fTäa(˜ ÃR`+((x_`W&¯‹­{¶µX²Z§-½5¥»æœ1¤ FV 19­Ïéñ¼nåâ}R·G .½h¹S†kfB½¦eº'±¶1`Ÿ5’¦e¤J³†3ªOä¹IÚ=?¦;¦œ//Ý)³(Жdü8ãÇ?>oZGºâi7Û§çó%+ãš¶åÁq{GÎpO;¹Ò¬a¹>Ò´«gfÒæUúÛ[‡¶mP@p«µbg0Ÿ7rz~ÅMÿBÆ(È tÈä ¨AV"§ôYšÆVaô#'Í Gw{*\;ÆdÞȸñ!ò³=m[i×1­c“?ª1º¢¬"ý'M#KþfúÇ–GVò~–g€`*f½ äèé€È¹å(R Y `º“],$"r©Zú³FÆvtW–G0c[s í—‘À¾Øz¿mU|Ô,سv#e»vÉÊVf¤fယ¶KNÆ0eÕì¨ZÒ—GCýäõy®YûOTR¶§tÖ˜ô•$óö#¦ÁEIC+ö l'ù$¿“äwVÞ gѯaó 4,⪆pMǸ®à††%$|¤ácÄyêç¢ÀÖµ,ž)MNïÊkƲ筨åˆeõGɯ—4y¢“¶Õð nJñ©†Ïð¹<Î ¾Ôð•œ|o4tJ<â”TúVÁw¾Ç ~\EMµ¢¤ÑeýÿóÖ lzânxbšåÈ$V¯ÕoX“<ޞȿ‰u²ßôé¥"ŸTVÛè”cÏ˾²ªàž¬†Y<],š9KÎ{¶zǸ(°{ý=óSÝÇr7W‚¬g“´È¿•!”DGUnë/¥ú‰”N`s¬ÚeQˆÛqôE¯ÉÝæJ¿#¶iyx‚éÁ‹,üÈú ˆu¹[z2$aXÙ5 V¹¯•Å׳FÝOå³Ôîí¯H¢«ö¦jñùÙþ´F6¼³ÈRhäÓˆîð™,w,ƩֳZbƒÏÀÙüÔ¶ê§y¥>:ªà\èeúÝRmoæÝ|ó ìå/,[ Ç— ÿ‚|bÓ¡lã,ÎQp¬m¿Ð¯|¨A;eÈ[܇JÍWÀôÌ;=#iü gŽ#÷ ü†Høêƃr!®t”¡ÞB}ÇÔ—¡%9Ýp Ê=l¼Ã¥†;C±iü!Çï#Ù\Æ*4•±õ>¶=¦ÿ8Žà(¿7|<» Pư“q[‰ªƒhâü?ŠÃ8†.g”XŽ¡…ŸB‡iETˈåS—G€ð|zØÅ±«Ü[:PÆöT äØÜ]H„šB͵~ƶ¦`{smS¨«ŒWÁ›!qûŸ¿–ñ$ÜH ES°ŒÝ &”ŽæP“RÆ eìJÝF¬;ܱr¼(U›CT­P©í®;x—XÏcŒ`/òûk×8úg>‰ÊWÈ|7S؃èE3N0y½<׫8‡S´<CÛ!Z÷Óú5Ú'éá<®#åqR@=?Îv’—sz+/ã8꨻—<§_2ðOKŒÖíe{‰ñz‰£–ï˜D æc&ÈÚF8Œ|š{稆»!FL ?…z|OÑRÖGŠ£ÌD¤ýÄÝäCÈFy®}ìE ÒGÍªŠ‹y¨·r-ĺ`¹áÉNrô ˜J^žÿPK ���(©Æ@–™ΰ���#��6���com/sun/jna/win32/StdCallLibrary$StdCallCallback.class;õo×>~vvnvv^F®àüÒ¢äT·ÌœTFáà’çÄœŸÌ¤¢Ä¢J½¬Ä²Dv>Fƒäü\ýâÒ<ý¬¼DýòÌ<c#}T¥*P.'%&g32ðcˆðxæå¥9ç$§32€L×ÏIÌK×÷OÊJM.adA¶¡Q§íŒ „\ÆÆÈÀÈÀ„Ì , ÀÄÀ &Ù¸€4Hž“�PK ���(©Æ@'ÄÊi��>��&���com/sun/jna/win32/StdCallLibrary.class…QÛJÃ@=ÓÖÄÖjµ^ëµ  ¾ÑGE(ÑB!¦bkÁ§²‰‹¤Æä¢øY⃠àøQ⤩ˆ¸°3Ìî™svÏ||¾¾ØEUÇ|„…<r¨èXÔ±¤cYÇŠŽUB©_™Â÷Óí÷†Pl(%CÓQ$#B¹Õ>6k–Õ5›vçÄn7š6„q3PQ,TÜ~"³,FLW¿°ÍÓ=­œ–-7¸5¢D=%Œz¢ÜØ Ô©¸»“áaôÐõ=åÅG„ìÖv‡3ƒ+ÉD–§¤Ü:2l Çç“B+HBWÖ½´˜<ÛòœP„;=q/›ÃRžÚß3¸ŸºE¬¡\D “„êŸ=nÂdÊnøB]M§'ݘõ‡»¾•?¹»ÿélüš„vØwFcS0‚ 4è+C‘:ÎŽg0Á5ÿ#u£˜B™ó4Wë|—®±7Ðå 2ÏÈ>õA35ÎàÆ fû$s(p.ô)rÈkù/PK ���(©Æ@ÂäÐ÷ÿ��À��,���com/sun/jna/win32/W32APIFunctionMapper.classTmSQ~.o‹ë¦¶"ŠZš™‚kbiB¢…hcêè·]ƒÅY«ŸR ™>ÕT8“3ý€~TÓ¹ –/è4 ÷Ü—sžóÜ眽¿~ÿø `/EÈP¼˜ôâ^:0%"†i÷ñ@ÀŒ fùðPÄâ"˜ðHÀ¼HyÔ>$,H1Ùtjuq‰a0“¯”•jÍPö UY®yK¯+êÁfÆÜÉõT:Íà©ÖŠEý-ƒœÙWU¥¤»ÊºeêÆ.yyº¡[ó ®àNh“LªRÐ:3º¡ekåœf¾Rs%GWòjiS5u¾nnº¬=½Ê<ÃänĦ”­ØTr-}•P3ô¼‚íPš]Í:qɪeÚMŸÅʪ–~¨eôœ©šïâ§.`jÅ’–·”ÍÚ«â¡Vwc„7|ê`è\µ¨kºÞë%ӬؔJ x†+r“Te;Cÿå<Hþ‡7‘/5•u¹°âz¥fæµeëh¥ÎG•@„¨oÑ?)¡}%,aYÀS Ï@õŸ¿ëUX3%<Ç îº"!‹Uk 7¯€aìÿŠÇ™õKð¡G‚= ]ÿ€Wsû$)syG2ô]¦•€ú Q_°e)»ÎQ¨néÖCOðbLˆÚËÃ3„iåpa«!#eóZ•“,K;‚¡ýT÷2Œ·È‘µüF)…¯Õ>nÑ—.ƒ¡NR›¤¦g$ÀÕ&+óf ë„‹W‚¼hµý�_ø,<ð^ÙñnÙù®/´ïÀ Å‘\×Mq7håoÄá&†�{6Lù©í0‚ÛMôOç&›ˆÔáÎFeGžP¢²ÓžŒCØ>‚7ZG[ø\dêÉ´×!e‰Çœ+ú™°zéɛŵ&›~xh"äa:¡³1:¥÷/h3›¦“9²£¸CŒ)û_Ž ò¼Kx|DˆðܘA˜tq‹qDš¼cö}Þctl»ŽÐù]|ê´§×¹0̦"Ú"øÑND¢¶`�PK ���(©Æ@z¸Áö‰����'���com/sun/jna/win32/W32APIOptions$1.class…R[OÛ0=¦´!Œ6hYaÜ•X¸¼ MBe•ÄíÙͼá*u¢\@ûYð�ÒöøQˆ/Vaªš-ÙŸÏñçs>ÛþØÄŠ…">ñ±„)L[0Q.¢R >™¨š˜eÙ“J¦_ këç F#ü!Æ=©ÄqÖëˆø”wB/ôypÎc™¯û ‘^Ê„ vK)7ž$‚%Ï{n’)·«¸{-Õζ{±³½ÿ½ÕŽRªdy«Î`„Y싦Ôü—.¿â6J°FÓß‘Øèñ(±‰9Ÿ1oc‹äóg¦ü<á…^Êée†…ÿ`˜ÈEÜ,•{È“Ë#1,¾•wJ&Ž´ƒyvÜj´¾1” ý·‡ª+DYʰ»æi•€«_n»Ó~Z¬¿†Vß²Òìýl§:`b­Ó70éä^*¿QšG)j£ ‘Jí¬æ Ý¢pã¢æ ßb$nˆ‚M£C§”øŽ2ƨƒ"}�az¯£ÇI¼§9—1µlñ PK ���(©Æ@sþVƒ��Ž��'���com/sun/jna/win32/W32APIOptions$2.class…RÙJ1=±Õië¸tZµîKU´ #õE¨R Õ –ö9£¦L3Ã,ŠŸ¥ >ø~”xgÜ("æá&9ç&çÜä¾¾=¿�ØÁZ)äS˜HcSh(¤0Æ f5Ìi˜gÚ—J ‰ÍC²ê\†±ºTâ4ìu„×䛣îXÜnqOFûO0\KŸ zM)áUmîû‚bÝrz¦*³«¸y+ÕnÙlï–Ïj 7ŽòWˆ̹z–8’±@¿Ýå7\G†áàÎ[=îºÂÓ° cK:–±B>/CeE¾ébD¯2,ÿk€!‰˜a mó˜û×'ÜeXùë\“LœÄ" ƒ‡çÕZ¡ÐWæOÕ–pÀao£kØ\]™NWXAå7²ùbXÿËÈÑgÉ_fæúLô³jz ôOÑ{Ò<L«12]z+H<"Ù.ƒŠV÷D@§h Iqœ¢QäP@#„è–s8æ(±Œ˦ÞPK ���(©Æ@`\½l¬��‡��%���com/sun/jna/win32/W32APIOptions.classR]OA=ÓÖÝv] ¬¢©_€¢¶/¬´“²@²Ií’lÑè‹™.“fše–ìn%þÏÒˆ$ü�~”ñÎb¬Æ‡9÷Ü9÷Lî¹þþí À+´LÜ·Àð  M<²P¹!«xba+š¬šxj♉5†…ƒ¾ï;»ƒýôCÊôÆüw'¹ŒÝ7üx“a®z¾ÿ›bgw¯{ÐÌ2Õ­(–Jæ¯ÊÍÖ[†Š— í%•èOކ"ðaL+L&i$ö¤&λN»»ïǹLT¶®ïeX’#7›(w¬¸{"U§íþ![Û` m_)‘z1Ï2‘ÙxÇÆæÿ£¾mcA k'ö:Ï")M¼°ñMu}°ü†z1¢˜«‘ Ç"ÊVþ. óCÇqOSž~f0¶~ÎhqV¾$±àŠ3ù/²ÔìÍ$ažJ5Úl}0H…Ûºw”ÑÐÝÒ«7t3Ö ¤ÇÀ"Â;Ä|” Wëìý9JS”/Q¡èÖ†c^ úµ3§0ÎPž¢öµ°¸Kû<]èÿR'~Œ–ˆ—(j:±ÂºJ¬ÀPK ���(©Æ@þóuͽ��Ú��*���com/sun/jna/win32/W32APITypeMapper$1.classTmOÓP~îÞʺò6Þ‚Š 訬0Þ”!BIH&j ôS™JFKºnÈOñøuj#‰ÑO$þ(ã¹m`]ïË9ÏyÎsϽ½ÿýü`KqHPD4#%`T€*"‚GƸ)-"-N°‰&dDê'L ˜0ÃswÍ’<Î ç ö¾V*[Úž¥k‡¦5™Ñ6'3K¯VÖŒúÁádâkG–»k¸fbçMËtF”Û§6"9ûÁК7-cµ¼¿m8ëúv‘,ɼ]Ћºcòy`Œpq `V,ËprE½T2È2|‹|òÉmríUÝ5+D–Sò{zE׊ºµ£½ÜÞ3 nöÍz�ÍÙ–k|p³©«x†hE/–=½ œBÁeè»–™A|ïØûgʞߤl¹¾V[ÿ A”Öò ¼H J=‹W[Y³ËNÁX6ùt].jšHhE›„$f‰ämÇšë˜Ö‘ôÔ ñ­KŽ£ñ ÇTïÞôýž`Žû³æ%<Å‚K.M¸t ÷ ‹t\os�Ú.—‰¡÷ÂΚÊS1×p ?¤=JƒE½á:Nþ”¡óBõj+ïR®ZyäàÍz¹4¾ |i2Ae‚Ê›gùÚìUÌÇ3 5Êz%�tM4Ó×ÔBo’o"õí࿽Itм“FSш£ê7°Ñc„>Ó,„.jc„TtS+yc=¸þ•ÞŽ€áÃÔ§ÔSDYUýƒðGtœ ²¥þF˜þ‘cD«'ˆm©ß!£©ZKÐ(µcDæ G2^Ÿ4HÈG}¸ï‰H¡Ÿ¼!?À` â5õŸDP¦ó4-žkšÒÌPšÙ:úD>Q£OýC^ÆP@¿èÍIðÄOÑš ±õñOˆ‘¥z©bsuë2„0ìµ#èõêH÷œ—ÿPK ���(©Æ@³Œº³¾��²��*���com/sun/jna/win32/W32APITypeMapper$2.classTmOÓP~î6Öµ1XPÄÓ ¾˜›Y2ѰÁoe^¡d´ØuS~ ¿€¯ŠŒ&Æo&þ(ã¹]Å1°¥÷åÜsžçÜçœö÷Ÿo?�Lâ™ 1ˆK˜‘À-…†Û üHŠ!%l“2î ”Bó´„»îI¸Ït6Œš6É +ÖVªV7S›¦žzo˜SéÔ«©ôüËByg›?×··¹aK;¦³Á£B±3†i8³ 㱋ÇWYë gè*&_¬o­q»¬¯UÉ.Z½º¢Û†Ø{Æ€HŽ jÁ4¹­êµ'ËØø´4¥r¬EÝ1–7õ†žªêæzêÅÚ&¯8™c0eÏ5k™ÿàdâ'ý:zµîæ{Ê¡Ti†2 ž‰Ì ¼µ­­™åÎË,ä|fnCç­é„H ½±VW[áR²êv…ç Q€¾vQ“"@EºU„ñ€¡ç?BXÖ¹-ᡊGȨ˜Ácá5«â æTÌ##aAEO½¸¤ˆKzq*rÈS^¤² Ýí÷g8&9yÓ½Üv¸Íà‰îkIvÁ²ª\7©ÇÊKË9£xâŒÄòwu½J ×wJ‰â¯):VÀ!ÃtVšALj“ü|±D°#ç߆!ZÚk‚@š§%|J‚Mo†ÑÖœJŽm˜ë™“Å0}:éºLOXTŽæˆ_€ž0zi¡Õ4|ô”‰Ä°‰Cø>Ñ·>ƒäÌ¡ŸFÕ]+ˆâ Ä«y×<„eBôÓýÿê>‰tìB ì!à?DðcÜ‚ 7Ü ñàÄj×Ýó(†èÔGëñ(–hþ‘ÄOø íB&¢=÷jÇϵàGŽð#Gø¿éâkõðçÜ=пùºÂÊW\Zý ÙeÛ -‚ô{ >Œ¹ã8\™èÛåòâ/PK ���(©Æ@‘s!…��'��(���com/sun/jna/win32/W32APITypeMapper.classTmSW~n²ÉºH‘ÅtÕ(AÒÚV^*hÓ.«mü´„Ö »ÌfƒòSú úµêŒ82£éLÿRÇsî"$)æÃ=÷œóœ·çžÍ?ÿ½{`OTü€{ ¦̨ˆb–9˜U𣠒èE)•ä"Klù)ŸY)+øEE*„ýš€¡`™í&ëùx¤à7å±Y.=\X0jÞN¡Ñt Ï\«°²¿k/[»»¶?-Ð5_)•˃µºÕhè„Ó §N_«¾ãn ¤gÖžU¨[îV¡Ä0ŠKVöÝ`Ûœš@_Ë AúçÀ~Ë÷­}Ý8ÃÓRv{ËöÏð<ð¼ºm¹ñÇu‚9Xn}t•DÉÛ´z ÇµÍæÎ†í¯Xu²d ¯fÕW-ßaýÈØÓ¥Kž»gûW:Åʱ“ŒÛNC@o=wÜâTa­85ÿ¨ÜF£ÒtšlG¬ ¤6ž[ŠÅñnæZØ é˜=ƒàÈ‹;ÙN‡éK^ÓÝ\|Q³wÇs%r’g>•U 1S«qÍ1ejÅkú5{ÉaF.wÎ1Á)ü®¡J9r^mk'Ó“ ûiò}Ï×ðîjXÁc"ò|ušZÙum_f³œbUÃu ­\m‚«M„#j(`RÃþÐ0ˆKOŒNÌ´†*Ö¿¬þ”†,§IŸ$9Þ¾ìùñÜ«®aW4 áŠÀpk 1c5ëÁ œv²ƒaÚ£§žoZ;ô:I~¸’ÕlÐ}¼ugV¶}ï9ïtÛÚœXnç¾diyRÖæfÛâ r§–ñ¾NÒËI<Ó œ½ÖDÅóu†P2Ü ¯ú›TˆEâ‘nƒL%É®b˜¶ã µëmÚH›v£M˶hQÄø‘H¿IÚ¿¤GI:ùćþÄýC(Uº'–ó¯üˆžŒúª¯‘ü q²Œ¿Fžnö\ =ÝÒÓMžžC¤88ÍÁ†\ ! ÉpðK*Á-:³Ô0Jíäé6†4féœÃ"îÄm,ã6!ò„HãäáVÉ2&Gq0ޝi“šÞ$y4“$ã3ù·‡ˆV{ƒ®ÊÚ%ÜK\ZîÉz}d‹CÃLIkEªŽc2¿ÅÝ#òŠ’: ÿ½ÕØ.¿F_£òÚÏ£ ™^•ÜÂ*Áw’‚ï1/“Gè¥Ã_*Ÿ�PK ����v©Æ@���������������com/sun/jna/win32-x86/PK ���£Xo? )Š]ð>�„»�%���com/sun/jna/win32-x86/jnidispatch.dllì½}|TÅÕ8¾›\È‚ w• F %hZA},QÔ¬@ I6‰@$@¨E«ˆÕZ„]@%Ü]É}.«Q¡R«-)ØâKa0Y’˜¤6ª@5ê]7Ú¨Ùï9gæÞ}Mý<ýøp³÷eΙ3gΜ9sæÌLñÍ•ºdN'À ét^û—£ûîkà9¶f¤nǰ¿óêgþm\é’{–g,]öë»—Ýþ«Œ_Ü~ÿý¿¶gÜqWÆ2Çý÷ÜŸ‘?knƯ~}ç]WŽ1<“ã(±êt3õ£u¯{êoÒ]<T7v¨N×¥:î}Òh]º^§[‰Ì:Ý"üâ5Q¯£;¼Obtë9ýô/#E§š’Bå‚ ÿ˜Xú©Ôë†]¿Ûõºî!ðÛ¥×mMNPÈ^½n¿i&´éui ^]¯ëìJû]«ìð{ì"=#Ë:4:M†®dÑ•wÞn¿îs&ð²_¿ë£Òåèù~:‰Ý¯Â?‹ôÄ`lLº‰¾+ïX¾œp‚?K£¿«ÿÖ�¾+ïb€Wºtø½)_Ε÷°tÄÃí¼ ¿LoÞœR¼'^ù8}K¤³ßÇèÃ?m<ݲé–-_ö ¸§:ƒº#°'Jw×}¿†„T‡P—ºóIqér¯\n¿ý¸‚?&Èô0üþï�é–Û—=ë„4½p¹àº3)&)÷§Y¬B4Ýš!¬ŠŸî* ÝÏ@þUaüû"AºI×Ðýæüé Xžé®Í¢ûkî„?Û!Ýb,T‚t×]M÷G¡mé2 ÓÑð{ëøtÙ×Ñý?Æî'ðkOÎÂʱyüY éFÂï³±é&_²®Â{‰ø2”ñe[|YW7,C¾@:l,/%Jw51ºåÀ”ÂäàGCãÒéþÿ ÿÙ¤Oç:{Ìr*4F“»Ý>I#À3 X¯L•“Ý>û·¡6ö™^®HæL%õ|½®y¾D$Í©s%Þ:Í·”5HÇ={.åø=ÙRÙ1¹Ì(9‚²Ã •÷Éå&çi½=OZif Ò¤Lå“wt:gÑÝé¸@%Ftý0HÖne^(’­Ý2 å}ROY½Qç±ÿ³^ƒYo­×¹}[Ðþp3m ¤ÊdT>»ójÎÏPÞ ¿Pd¦|Õ ?o…7ü«ž}UlœŸCVõCæeDz|‘yǽðìø ñ`Ò¬3Âm–Ï?èæ"ù‘&[17ˆ¹"Õ’©ü+U¯kuõø*9Ø é_Ãô •‘ÿl5%X’Ê‚Äô>`º'¯H*Í48 ’ø °2ÀÊC¥ôL *oÁS%òÓ–ÕJ‘)h¶‘‚¬v€LóØ‚ðc‚ #Ë÷Fif¦±ö¡7QÞΞt·Oª¤(ˈò3Ó!i~&Òõl‚[³'W?FÙx„õŸz©âFЬAgO°ý\©C.ëkmhJÆwPÿF¢IYüÑ+}:È5A¡4P¯%ù²Õ(ÆTù$Y¡9iÎPÕ`ÿ±l3A*”8`¥Î–‡B=s! •,„¤ž»‚·ÜZÖàÙ©ñ<pcé�HqÖqbÉÚ£|l&z,öK“XJdæAzkš¤ÞH‰%z•:ø(5x¬½Y!äñÇ�â'�¥/ŠÊë ¹¸×3ûGÎ{¥6%%tf¦Á³:s¬óó^L Z5 i2 íÜ«$9ƒºR}ršdáÈ¿ëeÀŒ&Ñ}+5ªÞ<HoéXùÿ&zî©ÎÌÒ›vO‚?ÀVBn€F�} ôÎhy^Û3š Ê™«ÅÎf)¶ÿb™ÍL]:ªa“÷ç€Ôó|Pa¡½h¸‘ å³Ò—´À3äéE xí)Që z±eÊâ#<bÛ£{„¯Y‚éK9F-ÂÙ×Ìqn†4—*1}u#¦~Þ«´A0 Xl”Ë îVûEU+ô&y•ë&=3ôÔ=é&v1å‚/§nBðdlY¥Œ¡ÊÕXÒ2€£°Y­upŸåkm°©MUj�JȦªm�O­”Ú¦e.½ÃöÚE(</:ÕoU¶ƒˆLü&,1ïG*P WpùFÁ†üƒÒ¨±R*T¹)YÇå¥Ù>·JY®¡TùÒˆÚßÚCÃÚ[Õò@\ ÔÎËgÏ„„T‹Vh=âk úf)Ý@VèäÔŽˆ}vÞH‘Œ0Ža€5x FNUIèøPN †ÈˆÐTðßRŸjXöiÑÜòçRÚ´AÒ:¶É©ãû¾ž½Ÿ€üÕr!7€€¤ÊÕ§C!ÿGý ú‹ùÒ‡ó¤&²t9ÜlíÅ/TMLsLŠÌBtù°þƒXY£és>Óu$ µÜÄL¬2Øã’`}«ªkŽ› O#uúП­lëÁ/¥(Ü]ITùÐú©Ç´‡ÊkpðÚVJ‚ÚÆ ¡ìYñ³ëßfÄ/öØ#Û R’¾ Pôzî:vË­·Agr0”hàÊ:²Œ°Œiñˆ(ãeÕøïQFÒÄáo!Ù“®£¬£Ê٠唋©­P^Ñ5ÅÊ*@Yý"jF”_ePºŸDº³¥»“}ÝhtÇÔ]?¢ëž~F·éž¬­ @´½ jÁ`»Qëªö ä•: ~˜U¥ÔÊbûrÖºdßsX§›eßCPUÛ~`µ»‰æ¸ª?[õ?9ÃXhFšá¡jÉ7ÇžØwô±˜ãŸà¡jÕ lXõZUñ¥èNTŒ$õÆJÒXN” ‰Lû X_"«¼ƒ^ƒIZþ»„c _|šž†„—ÂC´½V´d$Â�Ö¡K-ZFV†zr keý/öÿk{º‘|w>JÞx°Í³:%Û1h×yÛÎÕ™$÷Løpgu’î"Ôô÷Î-¤çÝ%p&Ö„á8„‘×Ë„J6ÉÖch7ÙÌdFKÉ Zt”àBOeR/mvwÚ3¼º«ÀxS»žf!3áÞ?8G }`§å"ÌVžƒÈÑNÉ]RA¤H&�^Wƒeâ¥Ù¸ñ»P™D”i/“m/ÓTy#!K’ Ž%°{@>C_@Õe@æ²d0Ä UæÇ‹lFÕ4”‰6ÌâÔ·œëÀp(Õ‚èREÚ7`d8‚jïi¨2,gõ©ÚGO’ÂC³œª¬¤Üè²KPØÈ  U¾’¤c¦½'G™1èQ° 롇²ôBB&û&8±þ?#™4!”¦I˜¨ ã+YHo QVû\;‹Liª¯„[ÌÕÎíq°k…oÑ4E—  1T¨hТÊv3[ˆhj'áâåë’°|SS¨þší¿ÀÌÁV>ĖÁ´Š¥š<bˆÂyÅâfa%eÆ\²P Æ–“Ú4%3+¤4 ÕëïÅ@xoäÅÆz¿ò*>$€²¾¡²¿ÁÛßÚ§Š;¡•ÊŽU¸¯S›"Õ86OöTdÝ¢ EdÍÛ˜N'º D€š3•§¹X ëÉ„3Bõù’T3¸Á~?*ZûIP&ãÂ0À°ò±…*s¨ª!‰LùìåX3|1",HÕ¦XABs–¢È[»6P3}FDçÝ!ûÀß��Ÿêýì°èGûJxÙê†PÙäs™á4 ¡§Ö“~ìåx¼6¢}¿5…Úw6µïìˆößc“Ñõô7þ9‰ýL>QqáÒ—÷ñf(º^gƒCìWSkQ& E2 5^UÚò˜¡Éq3oŒM”·ºDMô!Am¢ ™taU…›§ ÝDÃø6Q¸“:à^£ÿìMâ;ZíMÿ&É} ÅHþš…ÌÅZyŠ® 0ëè; |ù ³Ì VwÛ`ûã¬êpPÆ$cÛÂ2ÆÜqùáx=J6#H‚»ÝþsÐÂÄ XÝÐç¡XP—­öx%KµþÆË¬‡�âÎ23:ê]Xêœ÷XUE&µU –qŠžjÁÁ¬õtmØ¿MhàÉ |Ó‹¢†– š§™ŒwêK±á¾Cø1åƒ]²â„䲇úÉsÕ.šzAÙžBÙ9H»iþ2ªÝDt„6*B!u‰ƒy€>n2"Ö6báoaþ¨¹û ‹ªç^‡~7CÇœ ÕTgPçN_òA 1ýYYç'±ÞStéà 9_B+6fªÿ+±©ðm¬Zà`uG2rŒ±iöÿ8Ü,UG& @ ÀTÞQ(ᄇZ3“)cU…Z•QUélÌŒ–/Í‚ ™ùD²Å>†¸j—Ò™äõ‰E&€[$²þ_| Á]–3:ŒŸùûÊ ²ÃH­uÕR†–duiQ?VÄ´¬™‡œ¾+(µ¡¯OMºi0<Ûá57#«¢ðxÃÓ’�Ïðœ¡ðß"EBÉ:á$Ä‘‡8.À ‡áD8æ"ŽOúÃ8Òâ“…qŒOˆãFÄñÇÙƒá(Jˆc:â¸%Ç‚Áp,Iˆ£�qœcÕ`8*âÈG°i¤.ˆsì”\Á’ œ¢¿ Ýùñ.gýrÿã„${ $@Hêªs^ƒ¿þé„cÛ€8ÁÇ8·—úqx‚5 0!–âY¿¿OsðÖñö6?+4/ë8èü#CpLj”Ü®Sè)6H×àÍF|tv%s;KrßÏÒÛ“P‰½5‘Œ~êÿ̼s–Ü« Ç0âõØónÄwá?±bW¯b~ÇÇs<«3/ñnzûy/~8 ƒ'Þ7ò´’{âû¨WjQŽâ×ÒÌK¤ôœo´Ã¥•Cd‡Ä“ßÂŒ$÷Rø+zv`9£ìV E¢˜-ypw$ñ !ÔÆ'ppî¡„¤2¨ e3YÆèñî48¿LU†äS§!-ÇäO­a¾eP’’pB~inÞÜT x7.%®œ„7 åâú›‘£äq êhw«èZ‡ÞÆWúš$²ª˜/%òV^…Nw4Þ¨\Ãôºætïܮӑs\rS=?‘9²T~X»³Ž[šÊ§fu’½ž*�;Ç(oDžËù£Xa„QÔ3ke*=P·³ÁšÚÒ´zŸL¸±#> Õ.sÑüg£w6:~©¦ Êìw"™=>1³×CšÅÀgt櫬Þ¬öWö3ÿ Ÿ–ÞðÑà->Á±<ô'»ÔQ;I;*ÍËúmƒ2t+›óòîàÃ-ååÏÀ�F9ûUgÂqäÛ.¹œŸ¢{:ŠI0e`3{¬^2T<ØÑ‘UîØ ÂÑ)ºßÈ5S³qªVt¿ îãöÑÌ¢Za&’{bH³‰D×PÔ™îŠ~Õ$ñÿç ºHÈÞ9Ab¡ÞŽ…/…¯»±1W7®š5Ì 2r©Ã71î•ÏÆt«å~žÊ Ÿ®îªžB^݃ Žj*ì ЬH¡(J\©•¬úW£9Mœ`Þd߆íMEAE©Úƒœ¹0r“o}ØäCÎz"øëq£ø¡É—Ä‘ªÔ$ºF‪þ(Ø;P<Óúu&9x6V@>uÆ«aØPø1‰œq7ç¼ µw“:¨:ö ÔŽKžp3÷}†áƒʵAMùÿ„] gmö ¬u”«© ZOë"*`IbþmþJbßdßiÔßAâ¤z†BYØd_&¢J$FI“fT#Ré AC»Ñn=.âªAˆ·OÑrY•8ôG&|!æ‘õøåp)¾ ·‚_}‚bpBv3dÛ©yýçdÙ‚Á-#dD2ÊÒ"†L:ÌyWù öÖ¤Ÿ„7dÈÅC:Dx]Þ@ó;ÈGÇÞIWÇw¼»xå%è.–Àwÿ~H½¸Rí/Aƒ?À9TÉ¡HIxL“é°âLPÒÈþèCš†œ¤È(ƒg}Ëñä¯åÃànö$°Q!= k¡à¼¸{»)Ôg×ÑìSwŽdíö¿TmèåA£×¦d53Gú© •PQTv2'ZV;ŒS)G‚@·Ê’o¡_®msµW´ÙÇ­95Óž.Öú²ZJ®äɇ4YíÎ`ÚʼP¦»`ú z¿JCV+YG•šMãì«—õ(X4—#cxõRÉ|ÂcŒáë ŒÛÑï£òIrßIBµ–±F*]‹L’ŸG<sNŽ´ðIt?ù·œ�Eî5ÖT\­vþ|ªAÛÀëÅ—ñ{àF]V;C#'üH<á}Iê¤iâ²ãAÒÔ¹´@ºãAhúíÈÀUâAhæëÅAzãAhŠcÅÀ =ñ 4U“70ȱxšO2õÇÍG`(ByÈ÷ƒÆè/q—×PàcŠ3x™}ÔIŒh#ã8ÈrdZüo‰Ç55ÉZŠ—6$i)¤Ä£Žšó´¶ïþM'ßÔˆáTS)•7QªQáTçSª–D©ÌáT_÷4Tª97¬“Z±—m O»¿ad#îÊê W¦@›]4Io*óâè Ýe^T6”yq\ذ¸²Ì›ËžóÔç¹ìùfõùFö<=óx.›¡Ê‹ÃU‡s脞ä&­ az¸ë¯ª…EtDy�COÅ…–ø':>@|JZÕ*G\t :‰˜£°RMo¥ô>ûõܽ¸  ˜Oˆ¼7‰\A‘Þ›ŽDxo¢âe²ÚÙˆ’âùÖrø<¶^5줄ÏÒ¹ÿIÞj 2”M<)4g&úq JIY«Œ›}gV«ÔÄ]µÜÃÝBsøP~G—MÚ{È`†fœTó“\ÑPäe|æÝø¤‡rú tÂõRÍ!.c•7Ê3¦V$r„fN*0ÞMXâd] ©‘šý÷¡Å~ÃZ‘ÝÄ\;ë×¹ßL³ }ʇ_Ò  ¸Iæ˜ø#&OXcéª • CU‘ZCDOkC„ü)ŽDòg¢©Íÿôxq§-¢7Éi5c”ˆÇqœO*7UØþmÿæ„0†æÒ(ÿys "¢bxü�P„æhb@§D»éÙ°r Xœ|@9PfóäÔñ+⾯g¹mZSþoãñÅRŠã@h¼C—‹¥Ä„ö`@Ò²6®(×ñ ²ãs­àeÜå <V‘âµ_ù „ç9µ(u¤ìÜÛ¯Yø¡ø1×1¥Ú bæ þŸôk€AèXOYüc‘ŽOÎhfø@(.&ó;†2½ CÕ�Þ¹ ø,’2³”£•&jËŽIå.,6Îã¬üB—°ÃŒ ±¬îš¤7© ”Z0¶WÐ ³�qòf«‘:2ƒ¡_P'Jý(IñÏ8ÃúzmÅ:  ½, tö œY1Uµ8¹²I6&î<À¦ÓÍl:=lº²™ ŒA×6"JPÆÚœŠc�Pô)OKÅwùu4u’`Â݆ºVYx=†üö#§Ûµ,øxwaÄÇÀ“üêØöWh\® ǸÛYüÔf/“=¥l¢‡GCÙœDšªy‹¢:">—E³A8ËEeœ‰¦¼3ð“ÓÈ’ƒ±,¹`"XRŒÆ­2G!–dT#Kj2yè(Å*høVO(ý%š)�SÆ**SpB àŽ–Ôǵ ²Ñ&4MèT.ƒŽÔ¼‘ÌÃ5P_8J$ÒBG«¾ñŸü‡+ÉòHÉj®Ú†3¬VS*Jšg‰o\ª¥}$îâ‰7ñĨ¿ÿ8š,>º§—ój ÷ú¤Luz[£áš²EM‰#pP†÷ˆwÇKS ´"äéKýÑD†<W€ÌóÙœmÕAvDV³—úV¬ãdVpt¨Üý—‡pÊÕ9Y'7úð³—ÆŽ†�¥2£ÿ0÷ßñù¥ Ùʧ˜°>>ÿ`‡.u851>+Fb¢ÜÃæ«­ÐCš«‚v†œ$’ "ùÙ<YÆ-·/™¿á3ÍíÌæyK}ó3rÁDõ¿Õ¯LÚ$º¸“Í¢£Jé­°þªw&·šx˜{“ýa9¹& êî½VÏ@dmçÝz.¶ÉlÀ€¦ ŸQ~åŸêŒ2r §˜5£[ìË.ÇéïúÈ ðGÕYƒèu •qIÏù(Nß1ûdÚ:FÍÎ`‰ò* ß‹ã[f›Òl2ÐÆ'¹iJ™;²Õ¹H3ææçŸÛa » Þ.üyÛ‹NÞÀN/ú¯yÑ»ØæE'S Ê‹.›À³^ôØžö¢?7°>ª~ø˜ íÕ2£Xk3y¦×:O¦­I³óº^tê’ôŽ4ÔòeÔ~Ò3ed”÷"”âôLÊ‘¿‹™Ïœ?4Þ”!;)CWbσqy³µ ¿7[°ZLˆ3»ÑQa37[’ñÁäÆ.tXÛ(ÕÍS0Âå#¼Í<F 1ÔÔl¢)’KÙ‰­Ëy2E.ï×á"»>iï^R–ïÛ—e"”g {?Nòäþg^Ön¢y”KáÞy*E²‘ËHm+FœhÆ!¨èþ�,ÛÊ Ï†¹‚GPgj”ŸqY!œ·±v8OéÃYÂBËÊ,ù-*˜"›½îv{·\0I¼‹«†Ž]¸–Îy*uå{¡Ì9×1}¥ä8ƒçŠ®y8KL]4]D×ÕÉj¹Ñß@Gå·æá ùKæŠ ¯#NʹÕ9ö %¥ȃ‚+ˆ4z§@þô‘PØ3`@/e§hN^Î< 5uåJïMPÎ<×£èÙ%«Ø©ÎfÁÿ@?9¾<æ· Öè•­G<Bëü;ØŒIºôõ@¨|®ìF±Pþö>N³Hõ¢»]@©9¸™Q…þæàÅ›‚n±ß0 É á‚Þ~Y½O›lwÏ[9 èì:Ñu$xeTS€y’Š}UV§l;‚žg >3âºØ ¿…$a즹Çïf•½JX¹­O9o¹:ôªäùpZ -Cÿ9ýš½'ƒ®]‰ð ÷rúÇ]܃î¾Y®lרþ°·»è#æí¡íhmc”d~½nî×ÛünxJº.û Òý/”r¦Kí¥O‰pË+š[~û>î–ÏÑq·|YE¤O~ÑFª"òÊ—µùw )¼zŒ²Îé£Vw$ ¸[ e–pÓ¿T>Åu7¢ c‡:ÿDìÇÊ}ŠJe©Zâ{¨vÒ3 Ãá!¨ú‘ãÔŸq10ú—‡bóÂ>rþ•±¨Mèßt,³›{‰v²ÿi,M™1œ@•åÁNJ°,6ÆwK0°æ É òùP56¿ËŽRêK 5‹•¶Ì٨°)¶n>Ŗ݉œ!µ™ì`q|X„ãÅŽÉK#½ÿ0Áìê‹¢Ì(?2SrwàãŠ|$i×—Tñr³q£!š„kŽ«•C£3 cÕø(“Vuò`ï&¿ÕAžÂÅÎGP^ 4ÔQyWgbÀ5M5áS¼T~Ö õ9 Rªá<&Æ ALåŸ"¨;Ô‘E¤\ðO6÷BÊ¥ zUÿHŒu³u©ú@j"Z¼V)î/±YiﵪýÛ>„î(ÿS¤à‹‰gðëw¹ör%&øfö#�…£Ý!€dò7ŸŒ#ã-Ö1Q—Ÿà;G¦H÷² d[nA™ZæÐS´Œ%ǵB­ÉI8&TWØhßm¥Èæ)É?'c²�ÙdÈù^fËÐ={™è ZA>=4LÿçäR¯AÎ3rKÒò4ÞÒÇ=‰hݪšÁ!€´O.(R•k!LcNwóê¿ÚÙŒcËó*[’™wý,Ù‚>zwÿ@ìØÅô÷Ðâ@œmTËÈ“9™lÂ&Œ@“µs¾'O Ê!]ÄÑ÷¹QéÕ(Uì"ëÓk4‘<ÓN ²úD }š ®ÿ~ààu/¯%Îÿ‚8þW=ËÿˆÂ8³ñ±’4 4H nl¶Z©¬­ÿg­ ÓÙWÎKøçŽÖ|öhq"¯;à ð×ì­2°UÆòyœ×Y‰×ÅÇã@ 4]ù¨?‚9l¤¦¼×l©í#Y­•@ l¨’€~œòçô ÊãÙ³§tüþx}‰Öpöhq>ÊÿÔiÖEdµGöšØ¦b0ì^Ói•‡’ÁðX(Ti«tz<Ž^9]!—›åÛJh¨ ×p(ˆô >q§ªµ£ÜxÀ³šFu²SˆR';&Å ÜsZ…¥òðïílw,Ag{—[ãà3²Î@ÌêL\\cf«=´¨ã C¯úå‘ÆÐ“,ÔQhžó9%˜›£ìΛU«lq%¹ßê&BòZÞ-aú”l°Ä~û‹íÍ5âPBgß*ëhä+¨öÚ-& óuæˆé�棿~`ýRÌâêÂMŒ¨+&ê„й'bâí<¶ [òªÁñ/‹BN'Ÿ“- í}›I¹£e#˜ÕÞZŸ¦ Ï÷T@4„U}VüÅ ¡ŒÇ‹ÇÃðå*ÙšA€¤ÇÓY¯“†:*ÖjPþõ!ÍòÜRV?^õgx@Ñvc€€-Á|s}i£ò‹.ïÂÊ» 2;Ooõ’ïYÞ«�þP¢òzl}ùo(ÿo>@~õÅå¿æ{æÿÁe¡ˆ˜ß.×€åxÊ<Áú q§[fÖW>´.ª+ÖÕœ™Õóã²êØP Óµfµ×g$“¶Â iëM:m½Íx×ûCQL´‚»g…Ê ö9RP¹l8©)«=Ë u"(~/.Îê$¿=ˆ„òÁ-’Ò~¤ÁÕ}Ê;퀱‡Q;|,®TÇk«J´(`ްÁ(H¥èm¨Rm°pù”LåxØ\Iô¦!½ª»TÅø†ÑÒaÑ)d´"@u\B™•ó#³ bv‡öð¸Í5ÎØôÀÛ8ƒVFó ·=1`u–“‚´‰„r[+Ù» ö‹X›ö­›^5Á+ êÕ÷'€3[Ì:åÑöXä«Dòþ/»âö+`ó 0vWÝg·¦oíûlÜ똄*5‰-ã3ɶ UôIzÓ˜ô(ì7C[Ê—hf±ïª=ƒ¬ã«O×EìÇ‘™±ößLîmÂf’á_ÕÊ«b¿î¬œñOµuq³¿GqóvGL¤ ZÞxú«#ªËó]ÕÕþ÷³¦RýHvîª0íhCàú›šï¨®ÿÿÏ;{ú¯øüo¨þ?¢Î{gMÿÄ0ý@ín,íʹ!Kr5NzŽg»6}?ùÑ’Ÿ]žüÛY`ü÷¨€\ï÷jï´‘ºÞÌ]_:(mÄEˆÍV4uîB5~¹Ùª„èCOˆÇœ¿ÊŒIÙvÌݾꦘx«"ÛzƵå8®w>HXJ™°g§›ìw±NŸÞÙŽ %\®Æ=¸·´çZoÍʉñ—l•ªïÛv…ZqMÙª³ƒ ÒJɪH¶ž€ç¬V‰}ý.ÕÏ^Ñ…Q´¡Iôz¬ÿúþÉwϺþs¾Oýïü¯èû{ÿËúþOûκ¸ ¾Gqç¾õ¤ï?n=kúK¿‡¾zÇÿ‘¾¼øìéŸù=øàÍÿ#úïh9kúK¾‡¾õæÿ•¾¯j>ë} ˜óÆ÷Ó÷íh1Ù´]“{>$z6ÜZ¦]Ü º_ ’“òz´Ú g%Ö, føxõl8³®‰8S/º–¢¦}þÄÒ»¸Ùz9й»t‹%ëñAÚÕä×X‹,î²GB£ÿLjü÷Ÿ¾Ã(´¯ xø~~ÑôI´¿F±;J‰BYÇÂláç¦íÕœ›!û¡ü¼î•ŸhüTý0µŸ._ Ž]K3µý žÞLbãì1ш8Òrè+µ¼Y­DôÑçiç.@­2ßÊDξ³à|m A:JÇrŸU Õ®(ðØâJMk¨$3ŠØ|5VÑì˜*"bTò-Sòia¥òŸW¨¢CID\:«®ä™µ~ìHîZÜ/n`yšñ}å©h beسâjo½J&† ÖEÉ®wW™‹uFS†Ù t/<k¹X÷×äB¦<Ø&6XŽÊ³*È¥õªx܃íaõ`úåÆýî4¸Ï÷¯D÷ ¦Ž)˜³26]{¹‚YXÏý`Ÿ¡YïcZTƒL0ëbü›ZÿP*¹R?;Ëô„÷Tã¸bÇn‰ò7©êuƒ4B[I°´œÝ/ Œj<#vMþ¾p} {-J ħÀjª0 "@eŠì:+‘9VÇëÃxRÛ¿•ÑH ‡J^>ö( è6Ób)‰,øFDùâKôHÄ}?a™ÏˆÛkwqêþÄ?ÔzjÕ;r™â(N“ãÙy/ ȳ´³ã™¯–ól]àIÎ/¯Ú¾€O3AŠ€M C&´vJ„Aˆ*q¢"öûøží¿Vmÿ¢"“HßÆÑ— 5ˆô¥ Z‹Ÿlûo‰Ø“oóZvUþðú›ó_#H÷ö­þÞþ˳þîÞ3Pýµ0}ûƒÚãùoµÇæÝñíã]×öøt4A&î,K—ila¤רU&ét9΃«“v!ôÞ땾Ñûh?=ƒòð›PÏöó±/f=MV§»Ý±šöõa‹zòµ�sóîïÐÇ—ÿy ½y¬ÇÁïkhß–[n-ƒZ\Éf©¿Aÿ9óø[ÜrVlÙ^£r݈Ýîí¡èõS|>þ´†Mš˜t‰¿‡ªÃ%êw¯@öðØ, ËyJoŸÿÈÝÎSI^\AgŸA)œ§’íÓÐÆÝN}ï¥æLò<Sª?é§¿fËçQT6géÕÉZåÄV(1PZÛˆ™Ü±¹»=ð—˜þwmÆ’Ò–ì¸{ÝÏ0/G°iÆ4ê#v¾JÛ‚K!XTq½} Û˜­Fû;ò£cBm©…¦p‘Î:šƒ�,Ê­[qJ$bJÇ ““«†Ïuk,<|wµ¶=@¨Ò„mÁ-¨(@Ù‘R[aÁ8Ýš: .ɨn´p×ÖhÔµ¨(Tp<×a îÞLà¢k8Åkk…ëÃÃ[‚µ¥(L½èÊCÛ!¹úHW—=ÃZ[z½=ŽœÞ¢Ø5ô#�ªfãõDÚæëUÒŠÔ¸d¾•  †ÁÊiŸSLV×PµC&Óz“Õ£[µi2Û‚«ºD×^(QM)Ô=;YÏ6P$\µ[''"—Hð|oí“«LœÜña°÷°í%×M›Âê”KÆ‚É+E÷s�_»� ýG1Â$¹k±v-C]d¬¿êŠ)j‘q…$äìJ®iš¢W·4Äü[Ë¿%2ý®®‹egð—a˜ q*S<þYDQu <×-™ÊëÝ>5†‘$•ÙaޤTÍŽ©ÄHßT•‘ 2²Hs„gošõAÚ-— ÖMú0ä?X VýLeÉ&’Сºš6¨;ý3U W­ñ†DR°-LüÈ·fÑ D¼ý•x¢ãà 1í‰`½aØÚJ©vBŽÞ5Ù‚KÕª[ÚGÑýÈnuNëÜüO«‹¶êî†wµkr–Ÿx¿@%Á1²z½Šài@P³ êÞËQ™€k;rÕà’pAæ"ì¹Óˆ ûr¬A[ÎÜ›>×Þè0õóà]í}ÓROmŸ¦Õ ôl‡¤Ñê^ŸI8 f™“2Û¦*3{0¾fZã—D´‹b¯}t¤WwLã<»íJ./ÄlŒTJ®™‘Õ²V †UûèY·(—·©a„§¶"7ŠºMƒáÙÆS½#—S·÷$Ñóm4=ƒõA"s§h%aª¶Âsmi‘eŒØ‡†KvE¸m.Ã}Z6äqÆd3Bºó¢Ù6!Û" ñêò9¦¯‚¤ò®€çÚg''Py(bÕùùª„´Pûîì£ü]ùQù{SyÞH•÷ Vós* ¿$ª›‘†yÓ0ƒ$­%Ümf›ŒAË­û;­± Ø4«ô"=ƒ‘ža"½c0ÖuD±.ÇÊÉ~ó±n9<×>dˆu.«šòßL[qÖìG€ôLOù6L^«›¨7}Õx:®ç¡}&¿}Áóöø­ŸéÓ·/àZÑõ{ìóòßÞV†ÓF4ÑÖª-(àF•?ÜŠxµ}‡Ra[Þ˜ÏÒ‡ÛrN’ºæªÉ^oeeLAøÊ=è¡a;Y²…Wá CCO-aÎQ|ì—hþõ8ÔíœΤǫF-=þæxÜþ«èÿ¢F hvtà>6ní,—#¿E‡E³µã E7‰ÍB&†­Õ܇¹•)J¥v>5Øo¡õޝQª |Õ¤,y Wõ Õ{òuH»àN¾Ë~÷Ø3/ÁR9?ëõn-ЇÏõxöPÞ}WЈ®ôtòN5E µ«ò©l{¢…¶¤ñ©ô”󫈩ˆ[øl(´ Å ¹aˆeJ¦ÇÚB¦^;Ž\|ŽË¼os†E…Çø~GîÀv¶GñFhÉOav¢m?ßó—ŸC$€(œ=Â6½ã¬›¹FŠÂ½š¦£‚; »nÃ;Ü؃Œö¾´y-ºÈäž@&<™¡ÍüÆÖ72U‰¼ÂäÀø‘¥¨n+àCâ¼ñÓ=S2kÿ] îùÍC•Ï6Áðóeå–W#„Zõó-Ъ”ó7‘¼˜¼¯2¶½ÆBÎL+ñ½~|w°z„<4¾)ò&>Ö¨Á?~.Ï™¨BSÜçî›píA(¼äæ­¿²%7qa﯑¤aÓ£Ï0!Tb~9*Jul¨œ‡øê1»Û"¿žHxKŽÉ¨=±v¤db†'¯´·{áƒwµŠk³šÏKÏн‡´ù4ÜÈ´l I†íŸ;ŸteâF_CÌ~Í^é€B—‰;}0Ú©¤ýDLeÊfà•„FZ8ZÌ÷Û04 8þÆ+´.Vòaå*<õµ2AÐÚs+y\kþXˆ‹² ‘Ž‚Þ­èo5èâü߸ƒ –q[a”ó×(½_ŒtÐ!)z UÖ+¥ÌÔ£’"bŒƒîcŒ<”« Ô�cÚã·¬·¦µº‘p¯P´4¬øç×.dg~íÓÑ©@o§6r…ý—/iç͸CöódkÚZ ,ÞŽ…+ðîçÓÙhyÚ‡>ÑTÄÑ|PAÐÝ5ðÑ:—mî‹á¼l Ÿ• ‡óN„”ÊU/ElÒ¡q•ï⸒6 wrMí]„ÈDÃΘ±“J›Í€¾¬¢(ª/]Aäðš*ÄóN<-™½ Z]ÐMDâxq¬M€CK†8jŽDÓáÂу8¦&ÀáÄQÝMGKÓ€ãä™x-QtdÞm[DáÈFo%ÀÑEÇÌ£èP¢pÜŽ8H€C‰¢cE4Á(âøŸ8‚Qt<M‡aY$ŽíˆÃßCKFt4FÓ‘…ãâx!Ž´HÕ_EÓ1> ‡0pÜž�Çø(:.œ=Žˆ’Ó ˆcLÉ쇀’ìélV=}z„“£%cH¢½øÖÆäÑÉOBsž0 Óq…p³ZÊšÕ¬hr7E‘{;’{ÿéMâ’»)ŽÜmƒ‘»-ŽÜåÑäz£È}É­;• õý@r½qä¶ FnK¹OF“ÛEî H®)¹?ÜŽ8r•ÁÈUâÈ­Ž&7En+ÉÿÉmù’Œ#×ðÀ äˆ%÷P4¹iQª'€ä¾LÐäøaäª;€‡É?¹ããÈ=Mnv¹Æ@nrr³ ¹Ùqä FnQ¹͈"wA¹—#¹¥'tÜ?Üqä.ŒÜ%qäNŽ&wU¹3‘ÜŽ'л?ÜUqäV FnE4¹äû¯áæjŒÙž†õ5e3Ôa=îø"º>ùÀ¢{>6-v…»Š`ÿ‚°OÍH�¹=>ƒ}Fƒ­ Ø{öõD°q°;5ØMûS„=˜Ö{HƒÝF°ßØoÁnŠƒ=¡Áz vÂŽš™�vA¯Òfjž'‚}a¯J»*ö: ¶ƒ`o@Ø9»xfL¾q4¯Ô`q¯UÑ•Œ°•ûB lQì«l`›a`[íCØwb`7ÅÑüžk še„íú~°'4Ø4‚…°b1Àš‹¿ 6£X…O°£öšâDõ;YƒÍ&Ø¿Øb„½#&ß%q¼²k° ö÷ߨuôÄwÂ>¯Áìm»óõÆÀzãhÞ«Á*{É7j½Ûû¡$ØÏ¿VóU¾ö˜‹‡[Š®—¿Vë(6ߎ8ØsnÒê—`=`¾ñ°éìx‚½a¯¾)A¾Jl¶›M°'zÊ7vº[D°»öŽDùã`ïÖ`ìoÌ7v¥»„`óÖ“(_òXا4ØU›2`¾ñ°[4Ø ‚}÷ß�[“(ß´8Ø: vÁ>ñïò‡}_ƒÝF°sÖŸ(ßñq°_i°^‚=À|ãaõ³4ýL°_ì…ð²'PìУ_“®ÁhÞé{Y"XoìlÁâi<Õ×%‚ ÆÁZ4Øñ{)ÂZÁvÄÁj°Ùø`ç'‚ê÷ öçlÁ¾†°8Ï›{·»€`—#ì²Dù.ˆƒµk°Kö:„]“vUì£ì*‚=Ý£ùKkž€oR‡rêõPh‹"‚ýGzœ¯Æüÿóë6Ã#:ú÷Ãïî%õt÷jæó<ÏO‹Tž~Èd^¾Ýyö‡³:Ã<+tyÎÓ—®Ì–]Ïc¸-“ýKõtJ~ÐDè©õÍRäáafH—àV´h‡T*/ÂózYàÓ ÚA×àïÉÇã ;òò$º–¨Î8`Ñs³˜S½×¸üŸ¼j|ÿAyá·:]௪çy2–95Fk‚¥Í~Í{†ÜÑY¾¬Nš]`‘DcØ\‚躋~ùo‰D7íÜv.†©oiU§´z‚ö¬(6kçÈÐÎâxʘ™¶kî=§g§‚³€‚Ç‘9•¸.,=(ç¹}ö<y†uÜyR·òÜÀu<ÛË×+.‡Ï²y»»Ýþµ\l(õì�7Èjf†�8-_¯<Dþѹ3Ýí¢ûÏèšœ›·kñ6o–õ†x›MûrÒí¼°‘ÍÔŒÃ_"í‰Åk ¥`ô²Úq³L¾5¢š9îo¢ž G'¯š´\1o¼ÊDJ¨§‘ž°ž}öc$þ‚‹óÔõÕÄíÕ²5SÜY6³Ùê#ç°Ï~˜Ë`«ãµlf ûM%Æ®í‘m¸ú^y€ž¼ 4¥(4¹ÐÕ¼EJ.Ï”šš­[É¡k›äÉI“¬;äâ+Ü!ÉöŠT¼Í>òR·w‡<£j[²n…ªn¶n&èò h\ÑÉ3b’oÆà­é´+[FTÊt-e;ŽÄ¥¼H/ºn¶TÜZ"—ùö~’„uå~0Lòä‘£%=e7ëè°�¸Éð.Ëç5àFæ:;Å—Í¢tSdëøæ‚"|-äȶRH×n§=¦‹K#Ø©îd]03«sr™¯üz’¯âÍî</[IùÏÄVyÞ$|žµ’çM‘…?­sÊSEW/ð6 Wïƒd YÆS‹¡¶o„7Tf ÛŠ•‰þfÝìöQò¬N:>’ofÉ3$Ž_æ#EœEòÒ¨"Í©ûªÜ‚‰´ß®lBs% IùÀ=v$)|߯vWð9}l[>Ûv–ùÜè’)L'Iì<W6ãòÞ#ìpŸÅ8KX0‰Ð€Qy}l{›íê^9Ofª?A¢V¾oÇ’ÁæBÓQûX èoi(¡ê—5kòt,¸ˆâç=dáÑŽn âµ6Ò®&²m\앬m’µ ‰-Þ/‘l-’­C6y¬t„iD„?çÊBjã{°mcüc%"ÂSŽ”c•¤Ñ¥Þ¬vjâ%ê|HAjWŠ#%ýˆáŒÅ%Qçlщ†(Ýì¸FÆ{y^2^x6Ïߊ1\'€+uêz‡ŽÚ_ÔÝé˜ â—ÛÃÔÍ‹£Ž…iÒܘÛ+±º‹ü¸ª�´�FjúŸE÷uiÊTÄU@êqx%ÍìfWt•Ä |ùŸ'´¼¾x"1'´xLÚüc<“¥ Æcm zöZ *±¾ÏÙc†jOC%õ< Ç“U3¤Dû}@_;Dë±?n᥹$j&ðÚÕ¸†'«ÕåsÒá¬VÜIµMÚwKY}‚ýAØþ¸<´t Òp£a¨gn éi<òû>mºõJšÔ+l÷8Ì“Ï)ù±éFÈéV\óuÖ4y´º±/kty¥Xñ` pYW”T|kG‘PÒ–A¦ší³¸¤ÓrEAyúÚ˜¢bÐÁMkå|L:X£žÇ@Á°&l^´µ®œ±ÙѓԉæHú2•—dzÞpÔãÑØ«û£`|<0(Ÿ­AÁ† UÂvƒÒªÛƱg'®mÄ 1IålÌ—Ún¡íDÔùeêó`¹)=_ Xx*šRýx­ _ìÁÛ|¨Ž±ò\Ü µÝq#¾¿Éwþ]ͳ^C�E´Î(o!¾¹ÄC'<ð.ÛoFHÔ÷yù²°½ †48–›V~ _iÿäõ`½€wG×#^Bؾ> a<b¼<VyŽtSF³uÓN†fk%õHÖç*¬[š­ëñ¡ÙZA4'yl[Iõò娚ô‰îÛ‡pEöG×’0[`;{—ÄxÎzÞNšU9[3 Þxo` ×ÌÂRš«gæMÄ×9é²1K… dëÖ½Ÿ%¡<–(½2MñÐ~SÓô¯PK¤§…(¤­t[D:¥¹p&Y°oÑ»|q'¿p‘\˜ÓœcÀ.œ÷ä¼g·m°ØÖ‹žOÑpTÏSPËnW÷#båçç’xªrWù;Ðoó¡Ò%á Ñý§$¶i©zÞîÿâ~†! ²IG]¸°Â<QžŸ7’Í%Ö c6|ºøÏˆm ådsIùoåÂ"Ù¸Ýyò\qC½ó¤†ÃÚ¶éE··õ=9JtÑVc…`"lÇ]º’ —À�㛢 7{(˜ìØ ®¿óšò-PÀJÀ†ì†z†îw¸?/|Ý)XÆòt°n"«›ï±D±;$Vœ&ÑeK¢M:ÙjJR¾|ç_ÝŸÙö<¢‹vyC3¨³mbºv¡O!òkh}À$Ö qqÃ$Uí@å~¸‚uØ"–gÖLçIHfŒ š]#„ÝX…µxÎŒ§ ÖL׉ð6”é^=[ob»zÍ|È…÷÷‡Ï«‰¨¡¯O±TÅ ˜m[…Vehãð*+Ìá{Šm‡â,LPq÷lÕ*.¢2ÜËqr¿0g7mе‹š…í&^!Ð<±¡ú»ð‚õQ›_Ââ‘<ŽmÚþ·(Ý÷þ Ûíú¥øÙQQÑsÃéÖâõ`^ Û*dëz)zë¹v ÌÑŒoíD%2æç“|C%é:Ú/1²òÿò[ ²p¦´7ÆšÅkÂ1 ÓݱñY`%ˆ’dMÝoàv¿ÖMIºX¡ºm*Tî—) ´ÔøñŸ‹£h×ðƒuÀ„B2±åBR‘ô†lqÿÏ�épùéf”èþ÷åí<Ã]Iò:½ðN¨n—¡<:}ÉÔ›$©½La&ÙY¼—y+º—ɵ3¡|k ôL‘mk(*¬íE[EZ¨•ÌúSûùÓYª,ÕdÚÔ°[‡jól}Ô¦·.º‹ú5º7`ñòBÚ3N}uø-Rÿ%ágÜÎÜ?*üŒ+2ýÉágÜÝÿm?CÎ/fϯàóð3ŽÁü \D½“fó(Êg"Ïš?¯¶ ¾à^²õ‚e£‹ÜWÑÀŽ»GJ03¹zä:Þ€_táðš6ŒO®ÉœC³sÆêÒ9|^MSc¡õöÕ,¥¤6’¯5Ûd_Dí#¹f©Šå‘XÐE¨nå-ÃÂ='H,?÷è"3TY™ŒþD‚àõ˜=É>–äàYH¬ŒùYíÌ¿GZCC£á’Õ ¾bÒN 0²ý£ˆÙÏžît-3J6E.Sd`¤µ;Ê®š}'¼Ä–jí—«3Õvš½ˆŸàu)jí2eÍËtŒ¥´…£àܨ%1Òô+°g=33/q~Ô몊»¤¿;›’Þc푦 R‡Ó·@š†0²Ãä1Ó>øÎS)+.T·¬/Kеú¤÷÷ú“¾}iΣ¡Êez‘d—g ÎS:ñ± 0ÆT]ÿúÑ4‹ëìLW§ÏÅn¸7ËÇÏbRV0ÃxÄ<,m„F%Ž(p MŽ<s4Uîà;mGá¤ü0­Ü£™¢ÔîØRh†T&óê]&³MŽèÜ)”  Ùs¾lëa/Ðv=ÿ mˆ­úÒvÌ ûÒ®Žõ¥EóG™áŒÚJSt?q¢/ÆÏ*ÎU3ÑõsÈ)Xðiiò\£¬›œ$®k§Zìb#^ŠB§‚ÞIuÕEkÛ„ˆ¢D”ѽUO!çÍ$žçè‰çKçlNô°G§Eôßâûã)9²ð,±hœ†Ý sQ¸ÞoŸ i‡|Q¡å‘ðƒp>éMâ!š&Ëaåëþé Mø‹®cMóMâÍä1t6 Î/išDÐÙ$øOÛìâî·Ð‡J¸#wþv¶%7ŒzAV…íÌ‚PäÜ|ö u;ôï•R2¦›-Tjþ¿oÄÇÖ¡†#õßG�§ÑŽÅï Te}žŸÄÔÝR²4W¨¤´$çÙýälSå|¥™w"üc4üÎ)Û“Ü :]Ö§Ÿd¾ºÙBàÝï+‡Oœ¢ º\ªST•)ùuYÇÕŸäânô©4¯Á#ÐCxxxžòMXýØ×-vxJ¤þÝš°`†wJ§løÖÔÅF²Jø (Pˆ‘OGBlíÂ*’“# y?Œ“ý£ºØ‰�XÙréëÆCIGA€^ÇSó乂ÿ.ê»»ˆC­J(pîõhÎÝóî«xÒ#Î…€óÀ R á-íQN M©j­ô½« z'Woe€<-(|í$|h^ðEóÊ*(á3LFãŠ_ÃgÿC|ßþÈóž ­¡_;Ã%g–›QNŠHóq9² ”³¿‚d4úkéì”2Iþyd[|ö>¾0‚ÊD’ð·GpÇ生 P—ð‹[î#ÒÌgð�N¦p¢!¦—S‚oûâ‰ú×½D²g6vóÐ_k]ªu'ByÝàÓŸúâ©ûÃã”׺>>¡Š¥0€VŽ–C9?J@Ú’Ö ÜÄ#¦#âÃkxü¶§¼{ᙲì±^¿µ?´¸ÁÚà(j¶v$¨vàLþ/`Hõ"jíQèµÈ#™£ÆwŸÁ¯Å]îVÑ}+í%Ý8Ð^Ò"sXR?’üLˆö‡¶6ra N¥o”·p™BÛoÑœî]8£”#º2ö¬ÖÔ%€zMyƒÎq·Ún›­-Ô_Y[š­ûõT:Gõ=äA7—lmqûdë~qc=¶Ñ í½íx=øGD—‰Öâ`yYY±Ø0ª÷ŸÃâµc7ˆÆÏ»ç’é­~ÃêˆÃn²6Àˆ$¤Ù›ß¹UtàU>xÄ ç< üáÒ«¾?'¾“ã×û?Œ8Ï´úrÚŸX]q\ÔeE8R W_ÍLÚÖÚÄ…¡C9 ½ýCHˆi¢Ï¯JÅo þÝíŽ$€ž°Éò©Yµ6}-}�äLq·K ö_zÊ["ý¡Èñjô¾p#WÙõ0·†í7ZÊZù–²ýŽÖ–wè@Æ×ZWfA²,_n Ó;4t—4¨Q²õ ¾aB“ã…ˆ4ï:FKX"+&zýˆÔ0óóæB²Šžj¼RÍÊq�W–øZ¢×_´³¤ø@Ê@© aþ Ùƒæä§š¿Æ`yeJr‘€?r®Ä£¢Ïã,ª2®¤ „Ö,_e‹y%îA©ËÐmIÃ;<±eK:ÞõÊQrGVÒŽESØ;Üç�JU=D`?]¼‹ ’yJBî6iæãHñL ¹÷ÚŸVžy+³füÛÒµ ¬.Ì@JE´[ŽÀsàÏ•áó²àößMY>ç')ö©ãD‡³+eÂ^}½Ô"ÍKs¶è¥›ŒR®àO¼.T!é2À¤«Ge©ë×p†”©Ã€'[ùQDTƒzjæŸ$º÷ÀÃÚ~–ˆëöSãïEeäìÎìE¹¬W¾f<@î $K½‡(Û’Ùú­mÎ.½¾Ác>:©x‡¿–zÑõ¸A§³ Ɇw⺿À½”kJŒá(‰k½ç6ƒs_²4_pÎÌô•! :Úèb´tØ‹Ù)xøœ=³Qß!%Iª^P.ëgF%„ç©{‘¯YŠ™èò²<†Go;ûëÄÇžÒS¡°ÎåÔ‰€y²­WtºÁ‚ªš„OÖ^ñ±=Il$QÜ;¡m ¾•Ssà/0Ü3i¸³5Ùs³AºßH‚4]Piváv1뙈åZô?\‰úÇÙtŽäà,tÞÕÄÝ©{—œ“îýQt}©gë^Vàå¡:vZšdÝ téAïœ8<¬mœuƒ ÿmNtŒk÷txŒ¬¦ÑÐ0Ç`Ù+º ÃÕp1È6Œê¤"#ô…žITrYÏ: %YÊÃ>Rß •)R/ÊŒ»&]]5‘µ˜©Ò^6ß5u ?Ùcþævày–IjsÝÛ‡êtÃIìÍCi•Q*4HóÒ5ˆ2ÃaNƒa âðÃ"¹BÌA¶¸;¥²çD×Íä/aG·•Ãh¸—Úvâ_{ƒè¾†Æåtwé*Úôvgp¬ì赟ó¾R}¤›°÷Àg¼û ¤>Ò‡q$œ»ú&´Y‚ˇIe4þÿ ÔÄ ó^e¬¼Ð‰Íbc}–ÏrHÜès·Ù“¥â6w§£A.k“mÃ;&ˆ;‹’£jƒ*A¶=7®Þ~®ÔÆ*ÁÇiªÄæ6¦` M¹öB®C W©YÎæd9Õå|~¯¼Wz_ƒŒ™pÒyR»õÛâT—9YJÅR VM.tÃŽÉè˜+�Ký»ÁœQpy=“J½èj¥úíµŒ1Sõcs¯BU‡µãY˜6Hn˜\rÿGf¢ Þn4{uMdH©jüwÕc²×nwCö_Kû@=}Œê©Ô“tØ3I?¡Þù6i¨ÔælI‚fB UÊ- úm°Œ@Y\‘,Í2UD2;&é€_iÝ+PÜC¥u8�òç3ØRƒÿÓÓ<^(‡Òz&M@º-Äuèõ´ þ:Üë¥-ë8ª$h(Í#ˆ[y ^F zdг§Q¦Ž ©¡ê¹jhâÛmÈÞé)Pcs‰d;èߌòˆ+¸ž<€Ø[ ӽ➽¼~q¤¶\9?ë›ÐDó9³MЇ'˳ÍÐ=¿/ºf£ˆæ P­X÷AKïò£ Ùg›¤&Hv$ð!²»ZïÚ6î¦A½ÊîËìœÝ¢ ÏA†‹®ó0°¦¬QNE¾IÍR‡¾ñn ¾X1d ÞˤžÜÇE÷.t½OEnËÅÒÔn¡`’|±ÏcNÙÒ ÷–üá}¢{í Ù˜u¼f,Ñ2¶¯Šº¦"챑ãzÚ£”c€j™þà1´=O9•ñÐ2}ÃI°Ë5-Jç áQ¾ñZÔx¡ó]¨R¡Óô7 P!\#íÚL*mòBRZxŸå”øX¾C%˲l“¥A\7ij}›^1ê´Ò‹hC7§ÉsŒ–}¢k,NaM3X¬›Äu?A·Q tšÐÚ#wÈÓÒ=‚©·T\¡Ç“+AwYÆ0Uy‚,æKžIt½�凉P­öBK*ûÜ2 ù TŽç©*µ”k’JÒ 8о#¿m¡oføæ ‰âºÜkUÜáÎG~¢s¦IÐCÎÀh© ÝAèAfœ¤¥µ_Ê”¿å°dƒ"Í%ÿZ¯þå]©¸[tæáó\£üpšå°èr ¥)M*ü½Vø}¹œ§•|+ùSXò©ŒJ”XnÃè,ö|,ö4µØ¡t<”Fê+\®wuZ™£ ü†N+p?x¹°z=ÂhÖÙx„Qi±m’Ëh'mзðÞȶ_ ‚Ô8OŽE…7åzqÝ_±–S‘vêŒ<“x$õº‹îd¨ða‡%úö˜töuØã=`ôoÃÐp… Ãõk’¨#ößëQr “ÅÎÏÊ¿&/§†ŠÔNƒÎ0Kí e¦‹÷RÇ øÜ#)’­‹Ñã!÷)Ñ}’tŠ“ߊ`EÌ6HÓ±:x·XÜ <` Ë?h¬ÂfZ-VÓMJ20R¾c§ÈÓù\ú ã+ÙÆ#P¦-ùç÷‰ ãržÖã¤^XïzKÓ²òl7‰:SÔ*Ë´‚­‘²�é³ Á›ÀÊCtý˜lÑ;¾•@¸Çö‘Ññ‘oÛ"Jÿs*¨+E>W²Hå>ûpêôAÉê“Ú¤po7Ð+OºI.÷I‡1îÍê›p zDxž°L*ɶGB³} *ÈœZáº×¥Pÿ¹-Éa%Ф&KV/ˆƒ¸îúd�y·�1i`^zŒT–½v£*€YÀKËû¢ûBƒ*ÿ²Õ+Û¼zȳ;»úÉèÙƒ\u½G2Šð%37d0i»ihXÚ¤GT[g% l‚ÖëŸðìphP-Án±ƒ'­ûY9 º)Õó´Èðߺ_.k‘JÒÉ!Ôœc¦1Ѥç©Óþ6Øæ€±{¤åÀÇP!î‹ B†µm¹‚Ôä~é1L)-HczÛÿâi’üÏO|8®ŸËþ‰ÿ`›ë@%£ò½f\C¾ zn,3{rÍîãö¡0lÞè•—™<¹Ð±8z-Å R>�k£ Ôù_Æ£·¾aÁâo�çA*Û.›ôàGê€>R¹Ôéÿ) †»ÐZ�« ÌÉñŠÇ|®§Àdi[ 2ð u)rñ+rÙ+б8÷Ô¾ÅÙ&oSŽ"bë+Í9ÂEXtj¶W F©­«äâmÓ¡™Võ`O7ÍPÕK¿FÄL½ßL)ºÓÑ)TÜß &ãk#N¥ôɶmR*b $Išó´b<yÏ\a²uû²‹ãhØk¿2ák0R*Þ*ísîÓ; ý’Aš´U£µúh1š¢ŠQÅè¤1Zi–¥ƒôˆÑÿ2h~©«‘¢MYI*LN¥Zž‘šŒÕ¸ÿEø,Í5ùça²M¡Çóç.¸W§«t–·Œ•NË;ŸýŠ÷ÕBv²lmSù“ÀE•R˜ÀúNù19×ä™N¦Žî¤&¹�Jhœ#¬ûô–oÄuñìÓz,Òižû¥FéVƒ´Ü(åšóÒH2&!1sÍž$[> WxȬ±âBõµó!<š_“L=zJ“©Ò“‘e_xf¤SÙø˜a6®…¼ SÎ7-Væ,û˜_êØþj©u+¶Ä‹á1ÜÍdnjŠ›eëf&Ve›› ¨Vì=ð1Œ°=SX[ü#µÅ‚p[t·9îá-PÚEÍïÖ4i…QZaðÿë80kÓJ¿'±Ù/M7I¤I7¥ þḌ³xâÜ‹{9¸YôÏÁΩP+Ï¿ðìÜâ{xyœå›ÇÊD‚\¾Õ~ùû¿ÚÌ+t3Tèç QãŒÕ‡<fg¸>¿ÕÑ,*¾¾ÉÖ­ŽN^¡`&×%Ÿdzå¤p¹Aºe±¹€ÕæE¸r®É3ƒ(¼ Ïýë Ôp;c÷pÆîˆeìž(Æî­;8cw4†[˜­ä £;Kel-1ö¶4é7Fé7?΄HS±ëGL¢a¨¾…lN¦Òý¿>füÅX°e&šcp–ï+SF²c¶‡œ};pèˆí¡ùwÜ#8¡^<E´ÃÜ€Ïù·'† 'ÿvÃ/v¤Û°Kh.düë¦Üè.D-¶‹aÄžéóXTÃ}-‘Ew£ ?…R²qà}›Tjf >JóIHñ¯§Vf”—“ÝGç½�A¼ ¼‰a¦ßqÑuÅ,Æü³ÿÆr K›„¸Õd¿GšG öïZ5ý>;޼‹5ýÚðmØô]Ï!ÿø&}DóÏï†ká9|½ÊäÇi3iž)°Qm玧Õ6Îô[X¹ÃzÄ>m@Â3˜|\S#†7ú�×êôNŸ ±ã-ÿçýZ;Œ®ó/ïÒé¢âã§ÈVZ„»0zV¹QtMÑG8Ïn@9".ZúÁ FÓ»ÌÐyhD×;?îÛ¦—ÀôM?ßq·œ‚ÁVËàƒÞÚ;¡ÙÒ/•÷ˆëž!œ™=ý8rÛ«ö2äùdéÔ ´t `Ú­[ˆ `å´S­¤ßò/Ñ™;”ÙOzÑuÜA´§Œjj'„M­jtˆs 袜ÿŽ9Ï5È·Q°6aÎÌȵ´ÅãŸ`#Y:D÷¾!8$2Js d;“=…3.)¬TŽ…$²þÿè8•{¿`î»]ý4 Ãüe;ýìåký±ëuœSØyâÜfSó°õJæóÁ®dìZ9y@{Í…nåp {ôÇHp„Ù(ãK �7h2Ç}r±é)‡ë©cLâ¬tÏУ¡–j ?jJõOk¾BŒÚÂPy)€ó<?*Q_Xk>–À'áóÿ àT;Bœå¾±òNrF”õbàã*ÌG]ú…‰ztCR¸G‡±º¦ÁX—Nl8îe|yù¡Á²)+M²*(§D×gGRøArØÆV0«‡Ñ‡RÜãì½]I– ¤St},PpƒÚT«(ö¢Ç2 >•ÑpV‘f“î™z&¬{¦TŠ”›Fø¢4ÐmÉÚà¿‘ÒŸ +¡’Õ1¨ÿ‹¾H=éLŽÒ“çk䣞´œ!=‰„ob„ÏDÖp=ÀñÑ­ð’¸LW¦DÐû.29NW¾•5LMÝ&sS’FæÛH&v1ÉÎ`²èžDVi/µç/„þÉ÷o=ƒ`dÑ› xSÍk‰)]?î׬VF¥Jí™æ¿ñŒÚ›²þÉÖ«¯GGž^J¥ÎµPPuòÝ’šO}Õ8 Ù릶C®Di/Dê‰O×½—¬ Ïvù>Íkw«T|Ð?UÏü¹ir‰A^*À á_’­-bü8% *XGõIÅûQ¡›Âc=ùWw€Ä>ú•.¬>g‘P‚”¶p)ýšK)çõš7D.n1õj¹Pë½Ì!ÒGÑ-*‡N!öõ Lf©;¢|µ¾;±à…&Äæ“¦§…«¼>ì)ŒòŒ¼¨yF¤o¤%£›·V™ëGÈ1:¡—Üz8@³µIÖƒR‰!j¿6d~èp·K¶ƒ Ø)¸¢[_|D.;âîÝ2Œøªq¶CÕäžAyzJM¦ ÍŸT¶}Âò]D¤½F`Žþ9ðÄý`]†6ô¡[18Ôì™E’>.9lÆŸž_“öÊ0@4ãÇ·jŸü…录>Þ£'7º/þyÔðàHR¯¸¸r�|¼FpA#Ç;³/ïÃ'Q—ös«´1Ö*Ý¥_aHȵ<w3åvé¬tÒðžIž°q ê~Å�éØÂ*iZ¬d röÆt>2gnF&¡£ªgÄ$ñB’¬PÖqÖ²™›È¿¾•~ãX6ˆƒ1*ýF®ôVú ô‘JŒÒÇ%Tú©ýÌlUúOŸ¡¥>6ýÀzñlÜ×Ðtç«>ÄógO—öI 3hÈ.¾öO¥ìcHQ$HÍ–Câ£4²ÖÕ·i’FüËâº/±àspæûË ‡¤Ãú%í#&äp/ëFw0îp8á6A.ë¦VÁ,æ ¬mw“ššÐ¢9¢wB¶þ 8gØ?èwryJ;£ÉÓç§#‡žiHú¬Äf)œ®"øî¹ý™3ßä™EÕû>ŽäüÁÈ¡ÓhœÜx’iä7OG¦ÆSPüwaü@|ì§`È* oÑé"ׯ¤c@K™ANr®2]ÅÊÈ —¨zåœû³—.®<ÞlæÉ 9[’= ô=–$Otö'¯Lwå ­¥­ó•!Πaå¡Lw®‚HáÛÐÓìüÖ›!µGp6 úoCŽŽWélÞŸù‡Rd”™"pDNëàeÀ7»ð¦ njñæC¸Ù‰7ïɹ‚” Ô7þü˜óÀÒ†"Ê%Fq×ô¡Î“#VÞäeóž/ž9ƒN7¢ÕhÝè&Ã~%à ‚q‹ \œA°­ž¦Áž å²?€Ñ†9&¡9ÇŒ[Ô†Ã3Ü“Ig5È«y)úÃ'ËE8¿!wå ž!÷»v“gZhX‡§HßE&y4äÓ$!+óõúY šè½ãÝ[²|,â ÍYFÌìyv/âý“ìJÀ]ÿ‘<xbNícíµO– ªôEP®b¥åˆžM=ÍL‘SÓ0ðté~iî³k »×ÖçƒæŽ:yšQÖñÈÊ¡#¡Ã(ͼÔùqoàϱû}2yÂhãÎ Áó#‹Þ?‚L3Ïê̱ÎÏ{AèPÞì·`Pí»R¿»S.2ƯG!Ë%!+ÑËzç^¤b40K_?ÃñMoäÜ4¥¬…SöP’¥2׬Š&9ëi}$°‰ <³Þ¾Í“ƹrYš<Oƒ¸A= ‘%îšgò6ÔnhÒ{¥Ÿë›¦ Q¤iC›¦ ¯_,Í>GzÀ„Ëû¦¥áŠ£ifŒg)k€”Þk ð@à|½ [ðé\Œµ O§EïÈ’§ ì{уgš’¼Þã üf}S.€ç•¦S>¹”O.æSÖP>ï êS6ë@9S<QxñȤùXƒ1ñ*?X>&¶EËÇ«zÄ’AË»5ùôœ°€¼³+‹GÁ…<Žâ0Qk”Ó3Õв±û›“©ÿoZÜœ‡æÑPx—#ãðúeWˆ‚1šä¡ tyTÑôaõ¹Ò(y¨lÐïÃ_q×\?-¶´-O’Úã|m“¨wå…“»Û)g»+ËXSµÞ�ëˆõYM¨ËiË�:HõPg+o–ΗK·QÐç,#ïáÑz¾ÀÝi/„²} 5ØÏÅö?”6iH‡ìg8¾áÝ¿Tnp6i”¶·ÚýœBNìr_¤^ÇÖB/”úìøLžo�s5P•h½ª™-Ì1ªË)BÝÀWÉ)Ø5´6pþËŦ û¥®ÃX²!†-÷?(OÓ9LΓ`oÄG;ühN*¸(ÆÒx¦]gà÷¸Îƒë[xöÄë«vö¯ °K…¹~/ƒë*¸ô­úÖdc¦³Qk}Q¬ó†‹;}!â._@‡Gé×ÀÏ¡ WÂÏÑ —ÂO§%AߤÙB`4ÜUè"ü,†Ô) H'±°,âÇxM‘lÇ<6 óòØŽár¥;È=F: ļW;\Àu®F¸jàj…«ŽÇëŸüú–? ×pý äÓû’*P†X«\/ÖýâN}`¢¸K¸\Ö.=¤\rTÛ9!p‘¬Çxl3&1épH0« ôÏsödÊKMr éid…0`‰øbÞ³\`¦b bú\€k \ÛàòÀµ®§áZÇ¿ãõW~©0oÁµ®½c‹aU7ˆuð×"î„¿W‹»àïÿÀH90þü¹ä(üùQ篔ÀìÔäÀ¹sÁ Át Ù 5ý¤Õ‡1¢>ÚO†ëÃùéV¸î€k1\¿„«®épý®Yü;^÷óK…qÀõ\¿ùG|}LB&ÿëã2¬»ÇA}Œú¸�ê#•ÕÇHL: “ 2—$M_…~|]oª¤µ®‘íBa$mIJ7…fûÛb>T§ §Q÷öSMêMIöáÍCè†öó@ÃØ¯w6š †ŽÚmöéMkOã_ûgr2äœjK‡½r|•@3ÀÃ!Õnà!`$¼Dç_ù¤O#Êgc“Ÿ"¨¦ŸÓ¤² <#a%GŸÇa„†óqôÂ;htfXÇ-%dúDšyCfƒê–p~-€áxÙ1Ð÷Ž (Æò>ékµ08òH×…—ÆÏšÍÃ8EW5™VGÀsd¨¹˜dAIô’ol?Î`޽±[7¶ÉæCÄ9:u3 ¯¼|6CØc‚äâFk3 †Ÿ¦Hä§gaXoMy ÜgÑâÖf¸³RDÃMì=÷@$A¸Є«UoX6`ØȰgú#×·aü¨$�YDËÝ¡‚£ÙãÕ-Ò›þÿ8ƒ)ÍC01VÃ#‡ÁsvãëG–ÈSñ×rŒ”¯ðî¸=åqíi|¿|{-¤7yÊ»ä<¡t}¯lJ…ñ¶.é”bŠXÒeG£²–GÑ¡r£vá¦R†nÓñ`i ®mCÙ1'Î#C :q3qç\ÆQ™äs’¡ó=e’±MöYÎS¢k Ñ)ƒý|îßAµ�ÌÙ€ÌÁõ âΉ'šÖàÇAÉÑf¿Lúfí> °oíˆGJ²u&-®wAJ 5¦”ŽÛeGÛ„÷¤¯×ú)ÙIÈY*î¶ßÙÚ/,—Óžá ÊíQ ]6Eá$æà×<\ëÙ`$Ö[:Äu—ãj1f‘] )ÕI ýSf” (Ï",ÏK0R‹µª Y00Â]qεу§DÀ“NSñXëW£–U}1¦ûr¨ýÓ!ûÅx\yÈü<=ôŽcO×!I¹ê:t�›Á0Ìê $I ž<½lvc2¨±Þ@KÄùíy„ñA =[¯yˆ‘ËS+ž'Èö“0U™$BRØÙn ¸Á¼Q‚Å45ñAÚº H¹\NFÉ÷³pì&ee1‡j¤%vÐaµÉ·Üލ8YbŽ9a¼·–}–·ÜoÀdÔiGa(wÍÄ)GŠ´"ŽàHˆ!‡A Ø<y‚2 2©B:%¢VÂm•‚ÜbŒðÃ=é«âÏ Ðò‡ñ00‚“Àø§bèIñdœíöóå$K‡cx _6XÙÇJõr.­¦o›Éw·"\`ªKÓI1/œÆù K8 °[ˆè¶@g<}¬ÿÁn"wÙ¹Ð9†(…DUØ \‘>H>¤p|Ñ<o#)¿‰çòwÿÈ -‘"Ž(º¼ª£2úßlé#çÿcïMÀšº¶†áˆ J=±¢UKkÚÆ^¸ZÌ<P@Á¢¢‚µJ+A©L…Ä¡ulHËiŒÒéÖÞÛÁÞNöÞεÖÎ ŠCm‹Ò©×¶‰8P«€#ßZ{Ÿ„� ô}ÿ÷žÿÿÞèbŸ³÷ÚÓÚk¯µöx*ƒ¢q÷±:¸ö`rºMžˆJxæù½Î `îöFÀ‡‘¡+^6Žšªñ²p*CãeTi€+E’S#8Œé(<·j•…ÃØ@ ’T8zgºLîL•™€ÃÔöÓ"ïÙBaÀâÝëxã00\›¹IÍ5bqb\HǾ}\u'dÈ_¨&t yÜòY|ÈgÈ6Хং‹ÅÀ³ áà¦Ì“ám/ã~ùÇÑÆÃõ`ȶlYGµls¥ë™Ò4áP†û "(v7…Bˆmžð†˜ñañ">È£‡ô ílZÌd*KBØÕ:#À;Çe€†÷!…¸âiöAîcaH¶ÄyÕ-Ûc­á˜p€7×Oª¿©ôº_üQÄ)vèg=ÈO GOjT®ý¹}5!k=fИž…л 3„;§ˆ[vHmAι# sÃVIÖü"±Ýäåœ2¢L<<íÕ"þnŸW]îeóÞmÔ? jï‘:_èéþkûÉd¬+O6N¨ÕšT9Œ“¹‡©pY¬eý 5Üôf\b©á\ 8¢†[qA«¶Äõ’;1F«£Z9lU"ŒAMô<Sªl÷q�$EÍ“%»siÕ_-Ü—ñb¯a9Ýø A¾“\ý›ýŒÈE p´¯ÞMÖÄá~ÕÛ¤È(Çq^<.eTð³‚�{{2;îD?IJ¬®?ÚÝ"(œ7Ö;cÙ~óÌh÷žòŽçv�E¾Å×áðs«q7½§U«WÎŽÀÝå“‚Võo™ÔOlÂ/RÑUˆ&üYûN‘;§]ÚN:ËevЪ‘üY–ýø«t3€7�  =‰ºé½r,ï¤ o„«W:E°ýÖ´¾‹<‚þ A@è†hœÉø<à§ !˜œu²3WìZ!»¡øô.~tè)|k˜ÒþŸ£ÁMea: »ýj í¤ëžÀˆ*w;–Ò‚Þ'uBZÎ<Ö !™Ö7H0©±˜&&×ôn9:­;¨}·Óº C¥¾Ð'¼÷× ‡UCIËC@�Ï‹ÀÑÎ%M±WÜd¨å’«„Y‹ñ0D„˜£éáFçñæ5VÜô\ö#]D<.[û5Ï:W6°N§ò ^°QŽ d=„ ’ ÉÐ+äe Q®‡³=•_\ÏÿhÛ é;g„8ö¯˜Öq$ŠŠòñ�¹½Bd¨µí4®läJçÒE­ãΔ Ç~n=.}âÐã…Ÿ´ê>ç}ÇAñ¾‰û¢¬ýí'D¸vžvÜ9ÒæÊp¡PbÍ rMi‡Xw­ú«ïþŽã¬ééä:DƒRM`FÐj(÷Ö ¸ *ÖÄ•C',îËîý8½¼²±øÍßÇ㫤¾$ß»tÍ$½u”ž(;%5ÆbêïãüI¬¸iýæXëk5¤è÷_µö!ÕÚ‹ï‰qŒ§wÇLgøªÃ­Ç{Â0½šrÈbvÉõ7’žïi=hÿ‘´¡g«éø Ò¹;W7‚ƨW'{ýþ;vb.ÌÞVÈ=SÉmÝÇmmàê]¡?à~>nk¥½:Ð^1ìBÅ~¾¡õz Ø….ibˆsf+>0ÀŸó>j£œÇñá-ø,pWz×Wªa ^oo”–%¢ôᶦ¿í¬k¢¼,$ªÉ°*Ø^¥˜v¨lV;÷öUEEÝ™–J)—ÐÊ%´ñƒÏÚ¾3ÜIm±Ééäõá"Ê3ðåfšB5â@›U1ÊBº<ˆ¯ =á¡ ÃNn=ž{‰Ø ¢õ¡ U2ûÊúB+ßn™Ø/È:À~ä$´5d‹ƒ¶2ÜæZÚNÑF‚wK, Ý�ů¯‰ÉË`è�Ág< sºîÝÊ»‹2 ðÀýÐ ÍdHäüJ·ä~åqüZšíÓÖV0‘û2Ђ;ÍŽŸqB í$=Ò—ÒiLIŽ)M¢)ùx2ÿa¾Bµ3–ÉîÉçA±üœã»²«Ê•FÆ�rU‹<û‘1–¶{ù¤8»c rh4cÐÖ«¸ÉÞKà6 ð¥Ž=‡Øu-¬eJI¯¡güñâöV@Å›]Øz _ƒ;ˆìË$Wاñ¸SBsÀ]äœ$á-µOe¶øI³ï] ÂbÔ­ÜÇ“BÙÝDßã\ÌÖ±Tî\«8^Ü—âQ NªEî+¸Ï« •Îêßœ–Zß Û³Ûðˆ¬ûHkÇù|áþ!d;„4}“S.äéOÀñ½hå =,ܽö+=oN?È´JÂ}œök?;”Ÿ$ñDà8òãÙ¡Mgýç¯w`ÿcúѱ5·þqÝ‚c¨ä\¸À± TòàhôŸúÓ…ë3µTwrë³U¸õ¸4 ù–¶AòŽS6Î9[ÜZUAì_ì„ðz¶xͯý9žNè¬+÷·tQ®ŽS\é[Œ$w!Âûˆ0¤öª¡ÎIþ,ÂrnçÖß„™nJïÏ-ÝoíÏ}Y…fÓYèîˆ –H£2¶j ßì>ƒ‡ñÝ*k*bô¿Vt·Ž-D(ZÈê^½íÃ4?”•.BÁ@ÛîÝË•ÒÏq •;¯ë�³#ÈVêh·~ jã'…”fçþ@T.;Óô7'%þú‡q;$¥g™ßýXg·EüW1ÁÓˆ jv7×ù®Dlç6x訛!äa¼‹é²gñUV.*K ¸ƒÅ9[¹gö,×òvPþŸ,ÌÉÉᯂMG¹Gðmü%4U[=|[eÛhvââêh~_å…€ˆJë-BúÛ0T¬ÕSyatåÅ€ˆ=ü>Û g^ 21¹Í¾3Àpqõœ ”ŽhDÒ 'f†k2½ºAÊŸàBœ â¯ø1Htû®÷‹çhïç=üYWü€vëÝI­8ÇäSÄE>-¨i³o=“õýxAÈ>‚\X¦kŠ3™M$`?ÍzáÐ$œZ¿É0Ú‚ø€w¼L؉°TY¨0è’ ƒ05=¡‡ƒwüøa²L¿öY´¯¶ÞZ4ËÕ2÷¿°5ÂdîGîÂñFü¼ûÁz é¤BåçÁ¯ŠÈ¿»œg¸³}¸Øh‹% 5 ÷ ZeXT0ù²iò`ä-®•ULËq`ZÊi¹…*\«ø8ÒrO>Û©ø5Ö)B‰ (ŸÒâ5Èoù£‹5Ü<Kë5çYÝðgVGÝÜX7¼¿À;§Ü¿~ö6­K ~óÿÉjÒVÊø ¿ªÙΪɕA~h¿²"ÆJ†«À(ˆsàÌ–4Ñ2° GK!\Y5cR?‹tyå"=kì%Œøì5³alGU½û![ žˆb1.Ä?Ù—•RÇv¢©ožÁó+Ê€Å!¢¦Ýl?½?½÷@XD¤è™é½_ÚG~;´\Ókå`â$GcNT #-Ox{§ûf½íÃô¢†Šƒ+d±t >†$ ø¥“/Fš*Kä\û+Ä9Gz³bÞ ˜9kµ¯æJ?áã‰ö(Ol´×(è…ñÎÅ!ö+ƒ…C¶?C“pñòn±Ë*‹Ä‰îãtÙ(hm ÿÌ\û ¢w+„p[“é]aöŸ¥eɲЦ¶ž­¿2Œ™øˆ&ÑÊhô î­Zqå ±c7ÞÇ•RIp!˜[SöJÝ@H¢l0 ¯ø}Á•\)Ö‡¯äwÚæøoßU^­ø!øŠíe#”déPH/ÌÑnäÄËÍ C§V ®é ùay2¼×ºUü“uh9d+Bƒkç¾ÔËv¿ØŠw¬ÂÓä8_™9¼¹Ý^)÷<èm¿µw‹×`Ó¾$ØÛÜ«U"QÅZ`¾öÕKÎ~2Yµ&ÂÃÿTùs�½˜{ëG‘b?÷ZÕ¾3u`ÛZÅ|•¢BT3ä'Lÿ ÔŸ¯¶ŠD-ÛpO1Âí ¡÷ŠñÍu'`¬ùvµ‡«¸„©ž „-VÈ(À¢ÔÁé3Ç~ê�2I¯Ÿˆ*èøËÇ+ª°{DŽýËϦåÞ¡°Æ9а£œØ¸¥ãÞæÅAŽS@µPnývv’oþ‚ÍÅ$ËF…žïÀïéŠ]!(M7½’ª¦µÇèI–‹¢‘û]+$ý¡âVþ¥* Th¥QåúȾCä§’~3®€ ]ÈÐ'4¢­g¶p›xX.·#ŸuªDÇηòµâ }qÇ9¼ÅrŽDä’LÑž¦ûEùTUÍqÿqŠ °<Æ@Ÿ°ŽöÍl0ýª‹¼@Â,Q�GIší½ W—HÁç¾ìoo‹´�U 2´ÉíM£œ¦!˜)³BÀâôåŸâÆ/@Ð ¢ÅAÀET‘¶òÍ•í£ù‹îaͨ3Ó;ÍÀàí0(Ϩñ¥†è)jh ‹ÐÐgé}b©2±çglR¡ð0ÒñÝß╇çNv©Ÿs–(Šö¬ç5Ôˆ˜]³ð<Û˜øz¼9v`/‘' ‚Ãõ|çi"IŽ¢Âý)šÞŸ~ÛÁìº7Ðë.,ìÃb°à#Q[àÚtÌÛkà=¤‰Æ×9ÊZ‹CšЖ©bûÐÆC}%M?uì‡bww¬}UF—4îÎÏeT o‘m"t"ô0®Z¤kØEÉÈ#_J¨Â±ŸÛ/pé‘Éž–áv)ç:šôœx:€Š—%Ò½¡¨æž¦Ak¾ �_4ƒÚ±ƒ¥ÐÅžXÜÈ⥠C’tðžÃm€ö ÷%Þ‚D¯V··á?bf¥4EW²,ž¿äÍJqÊ›9$Ëm½ Wìû׊öï )˜Àk.Îå} Sk3pêú£¨ äÛƒmã¹õ•ð€Cþùý0$àS:]¿zg8Œªî@×y£or ƒm?6§é }ÒŸR‹|�˜Ý¥ûmësÊüQV|Çz?Ôо}S™x>a7B‰Zëí¼M÷3'pÑk€'€.ío—=s.ž‡„)·Òu)ϸiÌ£>¶P³œ/‰=YËõ�™ÞÆi?À7Û¯ˆ¸G_@í£·×ˆ?¥ZõÇ4\#´†à÷ £Tx†„oršø£n{æHǎݶ!BË q—b‡ãĬb·k^œ³m6ÛU v6“á>vš%ƒ ý ÈÂfÈ Qúoú` ¶_ äJq)ÚÙŸ? …Ú¥òŒ4Ðk¯c}}õÕ^Å4‡~!òú¬¹¸ˆsH1¬»˜ÖxÈUÚ«cí»¤›Å” JÅÖý]«B`¸æœÄî$§ËÑÉå«î4<-ts.¿Ò-6ÞXÜclÝyoì™Ýc/é-¶áœ76^‚æm+/?sÒ+ËMìzÐ1´,lG�÷!£rÃe¦?×Bû B¥¤Í ¤„yÏê?[ÌW‚Ž€Ôy\5€¶âJ›(Ž˜ÿ‘+#¦Ê"ƒq�4F¶û›,ù‡. íÑ¥rQ/•[ç#¬î2ίÑrò– oåt†ò@Ñ ÂÙ´ï'Ë2Àë(V=>¶Ù¸ÒŒvÖ;œŒÉBeN䳇N²"þt ÓGËãNÈ é@ÓÀ74_�îÿ˜y0^:áßîÅ3üÚKÈòœCçõgÃp϶K´qÃÖO oÉàቭIb§VX¤ w´ðr®l;Ë TTÔ oP"ž¥—X {mù••ú6!¯¯/ y±¼’/±öİwqK=„Åb€Š"v³˜s,DÝÔ:h’yŠ®`}¼Aû=Õq®—­°ÑÄ”Ÿhk_åæ@ù  Õ ÜË„²9.vduÓU¿ô¤öiÍE-WŠsˆk.ª¸R¼ÕOB•ú¥LäKÃήţƒcÏ?/úæË°\_îJ£§ßšq¨ðf¤PÈ*P…‡~~º ¥ãz~¹ÐÞ^ЧX™T úh=’\ó°HÉ•â¦D¿µà>#Ü;êc8à"vígì[ØC¼ÝÃs g½;„S“Ny¨ánwË©¨÷¼ˆý¡Øk‚h\è;ž÷;Ç-£Qh±Ôœ#su�•n>¦L£…sN£ ¥ …h¿L'îé¦l@ð¸âè¹ã%ð8ôbª3Ö“•ß6†Ð=—&wâq¦LpR×÷2ø;×rÀeœXKÆ�ÿ^èyô2%péZððvÅîÖtY|WÎ~¡R$Ä%â>L^Â'бÎјYUåÅÑ>ú<q ‹•ìÔ7^fd ð'‹¹­Ã–ð¼î¿"M±Ÿ~à-f²ì¥,Äï°7°ÿò½MÌGr-±{hïIÊ*pí¶¾²¾ßçðz8]–±6s Dý *êÇT`”³a÷A÷õÝÝÐxÔs¸ÞK~p3h3<?¦€ÃõV"È·2ù`nÃMxoD²,ºo½C2…aÜQqX|*cŽHÂmxh µ`ð m¥{4^¼}VÈRðsV‚ ©¿ËáÍ Ê“Ø°Û¿ œý[À²ï€Á…¾&@áz@¤@2©¥l,Ÿˆó>8™ºPJM” Ç~äZë­06Èà‡Bh3(VˆÌ=a…Ówª+¤x]!-$Þ…(48<p™ Ä`°ðȇC¨:xpNùÚítŠ“]Më5¥24ÛûÁêh—®L`>U‰ ;W·â\óÅ[©õˆ÷WðÕ®Éí•mý\âG€Kä}]w‡òhâí¡ú'È^!æÚ«ÄÀ(i ã7ó4{ç«\ÛÉöPœíbº÷¤igœDh3z;VnKÍu;c»t0âK©ÙË9þŽZ0Ž~þì+ù/£Áë-FVá DÄR vÖáš|>4—û¯0ŒdT@€Ðª­Û‰Æ}‹ÅÑé™<¶ÈŒì\Œzfd÷¨ñŸfQ šÊ¢±¨uK÷¨T_»Xµ¼»èòÇ0¾Äa¯+~H;2-<'ó•PÝØo“erEåXø»öôP‚Û›šc Ó䵘1¢cwý˜RÈ÷Ð eø·_9ü=ÈR8tÃFx9R¹j8¿Žzøü7Qÿ†Ïe8'Á}¼Žº…ä�~.¤á-êL•ÉáÃNõ,zÙþ!´¡‰°·�û9T´»Ûp(p2ÐÊ-±4ª#?,þ;YG—áýmyŒ˜TÓ� à9Ÿí'DUPÃT®ì³þ”Br칄gQ²,P‹Á¸Ìþ¹ ×¾‚¹ÇPL oý¸õCàí-ú&VTþ`ïìÅÉ¢ qÁ>\0ÖPöYAXë¢pì§ ™›Î^ís«¼­’¯å™8Ï3g cÛ”2œÌ…¾ä&–øR%t8œntÃP [~Žõ&:^ã¦Â³ýÔPs8ÇД¥0È™r‰NŽÎå&f^Xw2¾ñ ~U¢*ÏñtŠ[ÿ<7¸z¸GñZào,l=Þ2–yÔß°X© /ÂGÙ}8¤¢þ†màÝ]Câ+p‘äõ4Ó g ý¶#©¿™Í)pŽ_ѸK©×`¢RÊ=G~I-0ÒÓ”ï^r2.ØÜ^!Øòˆ§awY�ŠùGreý]ïAŽ(ø7p½ݳ¡Ö¢)›ö§†ŠC{Ž|ݰçÏç‘�’Z„ùH¯‘ÏŒ@ªbö8YÝpe°RH«×n¼Ð£ÜÛ‘ÏÝA³£#õuTŒE€tU{¦{¢/uØg_Ä?%ágI*õ³7¦Lï¹t±Ã,¥ I9ûsZXÅn_‡ÝÌÔÏ C\#´"Û©Ë´ñë»Ï_ƒøÝ VR]ýZÙèë"ný–`¦$iA°qëÒ‚¨2,ŠØ)èμ]ve åÿpô_€Tï6À/G * ¸@»WI§V¡Yžjȃî=})/ÿ¦ ©AX‘£bÕlÀ_ƒ~¼iÀѾ*ʧWЫrÝ7†±Ë ©ý}î‘QT9àV/wÿ‘4q4ïjL1¢slìÇJé-”•nSÃ1qÕÈe™wwÔ{7ûJŸè¥ Ð:¶ãúßúQ‚,Û†'LaÐÎAÔDç ´ðZ ´íX¨’kÙeÈ<ÏþÛhžÍâð\âP»[ØgMÃén§Ea”iž'BvXàT—XxaÂê‚Ït0…u” 2´¢Z=ÈÆ‰)ø¥ÉÊ�?¡¶þÃ�*0SK‰CÃ<–’o[¤Þ°-\·ðÒ/‘–j¿®¶°v‘³/÷é=£[½ãº 3–m·›ãAiå•çž±±ìh’²þÒ4‚¹ð4Ø÷$Æ¿ÜÖ©í®„ö5 œã,ð¡Ö¶Ë΄”èü­ —…JY‘©=e¯A#é=GZh–LnƒÈ \Çd3“x€åÀ[ÌSxF_n}Cb’‰æI¼ˆ~]íȦþ‡ë©}#$÷³äÐnim mÆwrdaÄÒŠw~y¸ ŒÏ[�½ó¼óhåº$T„ýÇ›•çŽÃ ”¯õg;‹UÑ5ÅjOý[w®D¯¸«ßS6DÞiA¸9Ç ôñ›Hã±Wª¢¹ÛÄy¿ãJ¯R"Ú-å.ó¯Ë¦¢¤=r °çà‰ÃÚd{‘5?¶R•øµçàóTá­ ŒxÙÜèüa@¯ŽÎ̪é'XV€ýÂÍô¶þôRÍTðËà¿õ#!Ï6Æ{µ¾Æa*GC1ñ4³ðZoм(uÑ ô¸'ë.6£¨ÄÐl Â-"W*éG'b7|ËlŽ bˆ b†*ÞbÝJo@{˜çÃýfîÍP C+~7ëIÄ5uÇx4”®võçwºó0¤Æ.T”öï0Üã'v�£$á>vÐ|8 u· ]]øJ„Þ=¨Èï`çûÒpƒ‹ãØUf £JÛØž}èqÃo½íäkšJi#æJûû_Žè ¬cÒ …—q'WöMÎmÊÚÏ3èÚÜEþòP?Ü+=S/Rc(QQ±láL|?œŠA+òZtM2£t^€Å4&d,¿—%œÑ¤ YF í`.®»Bñ"Î1˜™VH® b´ J©¦çÖŸƒºÚQ÷ÁŸTª#h­_ _NÒó?`¿òãF«c•ÔM·_«Üo: Èì&6DZØŽEj·™:Y]åT+pá÷$Çz¹w-öÚ²9\éV||X"Z=À³õßi»PL¥í ¬zû"“­´/&þŽ¢Ñ›è ž:<\ ã¿Üé¯XzPÝ0õw¿vÿà¥^Ê`i†¢—-.ï…`PEŠv°–‰Pr¾ØJ ÖdJ/øTD>xK‚ÿ i½ÿ¼‹OŠ:›©Íàè¡j¥^CŽ¢°ÝÂ$^Ã×^™Ú„çAë÷`=X”†Šž¬Jn‡näϰık%¯é¸CdØeÛ|ø�˜“Ôxn¨ª¯d‚{ì\[{û‘c8$ârÊ߀Rš®ús¥O‰©œöj= $ò¦�~Çš•írhDëy¦•åë Ó"ŽŽuN»&£HHêqUtïDT? ú9פÚÿÒ#vuÔÛ£�ÏzlùAÏ À=6Ê* Þÿ(GæïÂßÍ"oÑ@fâlÐø6Úâsÿ;äb¢ë¹ ¹Ò Œ¹õwø¸Ó@嵞ÿ–v‡,n+p1cñkÈ;÷*ŽJÊî}†‹oÃÙ7÷}•‰ô”u5Lÿ(tæ)H­)+ûºÀ顨ޫy(M¬ eÀ®$Q'ex»ŽèbA›QšïyÉ~ûéööˆŠ ¿qçÌ9:-ΊJå¹×ÞH¯á‹g"]¡W@µÁp4D¤mP fÊ9îÁY7 V9·^ØÑü›ÚÇ«5_ÄÕ× b¯•¨½A˜JÚéZ„GÃí•!”"ãd.±C˜• roÑqd…2—)©*éE$Býå}¤ôéÔ ·Ðix\¨!¿3ªøÌK!r wˆÊ‚¼òÂ?ð?'i g®LtÊ‚ÎyF „8Øm7½âŸ[Ÿ bf<Ý9åGßÛ†t¥/w#«bÒx=NIкX³hëúÊBùÒ(bó¬^j{]¥R§Áà “{ü[dÇ.& ¼”øÕj2Ôª&Šx½ÈªáÅãJÕ¸®©¯5áà[¯IlëðÔCö/;x’u?ê{‹ðxíÚrž•Ðó̹vŸ ëùŒYê´,ž+Wi)ü—±Vã²£ã[Aüz¦ ¸è\cœôä ­´J‚xÜ•LÏÁ¨=5—Û}“ œãIÜ Hy¼¿Âå)¸ÊÝ'E6üµ&%Y_˜Ð4†·w¡žoõòü~Y8V呞æ í'“»q»Ï8[hÎÚ‹ídiF»ÁÀ¾=¹:§\8p†[òpÙÞŦêM‚0¡wßàæ.<à=£ë;è¶d8ž³’‚ð¢gœö[ßp§ Ù@üÆ=ðÖôŒßþ&<Õ€m.Ã/UÝ Y8÷ñ U&Õú—Ÿ~Ìn¹7›Ò½ceÃXßÄÍºØ£× Ã™�<yŠÍ‹ß¨Ç:WrÿÄ×㢌¸Í·È@ÊÓcÞý•x›a:èÓ.YŒé’Å-ì¢rㄌ£T£S÷Á¥:åŽk¬ã µY¶QM&ö½…jÑÎ5·k´½&À�d‰Œûx8FDšÜéô®„Õõtwµ}»‰oc—€àø_ÞùÝ~F´“JÓoo¥Cv­;€˜‘M¡9åÛ ñ]Ä*¥y½™vÊî•«š6@jX¾&gy—ó1éÐ)ø‰gh£dúõy6zÈ“Í.˜m»…o†L“°)N ûötïþ:ÇnºYÜzÀ9�ˆdÚ9€²çÖ�\?ØD¨´Y$b›Ö¬�6³&”ÅË¢q1HM/¥Áod!·øqÑPü™¡ÈA#p,„w>Ð{/<ytBò83P€*|ÄørÔ?ܪ¢‹¬3ÎîžWª,¨éɼÆCÑâ¹IØ–ç8e“@ñéÅ9| üt-Œ§:•tóP¡¤Ö4®§"ºµhÉ~ÛÎ:‡çCº.P6Àó¦oÿN×þa¥gûLØO:ºnLÁU }W^pcgž‹*¶JQÝr½J„ËÓÁËÑòi,žÆ%üȪՔnøÅª wë•ktá<‰ÐÀDB2˜–³12 eä(#»Ð.åÂóîš!Ëöå ã×½ÞBñG°îuÐÑB·Ý‡Yïw‡ZÌq´[ï‡6¹©iš°;zX•á0?|I„}g�”+Œ÷D4›OGÓE¸7•osfÜÿµ£ÂÆšhÿ,鿳¿·OÕÓ>å8…ä…RZµ¸Ï°)Ä/‹šæï×F…”qË“»ú2M{ÓGü¥¦÷;ö?µdL¦¥ËôÞ†ë&ÞÞ”t&ÎË’.âm£Å›ãà§z 8—P‡æ ®iAÞ·²”ãÁR· ¥í²y÷ W&ÌRÄó·É2©ýÂÐ%CèEQ…½ZäJ‘ö,9‡#©xÏå{…ؾ}ļ.ç›gÓÓÜxtTà,Ì´Ç *Ý]ÄWYo„Ôà ø Ào íw†üp<Ý}—ôð”`ªLâœrÙ5ñ2]N¨;Æ'Šù$¨Ä¾=Ä3WL?äÞiÿWž c…y{gŠDQaœ!á6àõû®b¯ ¶WÒÉŽ-Ë^ Ö?ú±æ8Ÿg)$—C³ýMìÊ{F¬Øo¯ÅÞú;·x~— u`:Wꦘ²9ü¿aU²à† î8ʧõN§Á«3…#¯‰"d¦W“‡€ËÙçøZú‰Ýd<áɳ‰L=ŒNô7ƒyn8¸ã¼‰óx/Úñ¼Á^)6Xeñ%¨¶&ó*ÄNëZ±£Åft‰×âesJdôÆ¡[±ì¸ÜÝÔß½e Ý-¶Ÿ*‹›q :ª—F|ÄKZaÕFøXöñt§ì5F PŠvf¥ÓÄ‘xh_ƒê {äˆ|6PžLãXùÍ W"šG^T´ÃpãW7cÒ¡_Ñj<í«ÆŠGÄŽv¬Æ#B5@ŸvªÆÇÁnÕ°ú(×ZAÃÙ·ç±S?º™zÄËÙô|y€utýáMwxg¼ÿu=%.‹¬¿!N*í^3P1Ô½áAئ×ôúœÅ†×øú¶ï5ãfxÙrµ}g¿l᪗xžK¼£aZÀPiFxc›ã!\®ð!7íóÉûIÜÄ ô+I?Vˆg»Õ[”ˆÎEåM?¬ºÛù×0{4ÍÇJƒ]âX¿rñÅóž';¦¨�y7d¡t°P÷ëh¥¶¯îTQ±œÞÞ‘ãìÏ‹éK“‚}ëòBÝ\âð;•îÀÊFzïÂ]ƒèÁ­ëíÞõðUbþþ E…; )Àn‘ß9‰ž7¥v†Z9òAË´„85R…p¯qç ø³kO‡D’Ê‹®PÉÚ‹ø±Ç¥¼K\ó FhzVÈoÞ&ɂͤ¶?,^=òþ‰Þs+v†:°üüP>Œ>Ø·CÁæyvce·Íód D=4¯Ú¿¾b7•P¨µc¸Z £¼Hîˆ}g(žÏa›®â½[Kð¶«P§xƒ÷[¿ënÍ–IDõ®ÔadÈYE /Þ€}¬Î�ÖÚê|sp­uŒ3•ÑÃŒƒVÊèÙtȈO/åq+'Ž5ù‰ºëM8‚ãœ(tF,µ-ùÖ(Ät%†àuk!2W(òÒtÀj±W°;·> üðûòì›î±Mëùb±žMÏ{í“0oçù™jSë`H\º3–^ÅÀ§„Òvê|žlJhÅ" ŒÕ òf¾ÄpxeïæƒuÙÌ_…Ayü-Ä8)ˆnÙ{ ¿¬8‰NG¬¦9€w<Ûò –¯²W…º’ËB {p]½„ãk«¸Bl¨\ú‡kz¨kÅK¡®I¡† .ë;¡<›ÞÀñ—ªdâìÏ1sMïÝ/(w†ãž xpœ².¦ÛH » z/¿º E{난Y}r~Ü0PîSy_Må½ íÇ Ò>œÂ$ zéV^„ÐÔ¦%a«Ó£H@¨XGË*Œ é';ç^©ÂkÄ6à ^Ðf�‚бu3ÏØg:ãù ÈñÞ}ååã0¨‘è/çëí¶¥•— ä85´óÚyè"žW©i©ÙÆí»dMÄ+ìlgÞ¹·á <×Dy"ƒáO»?Ào¬U‹¬ßÓÉ#“Ì=„±ÛëxãϸŸ�Sdß%Û6àï(Fì½:XÇü9å¼m—ŸrŽl.~GspOÄ\…Z•ÌÏ yzæá.êö¦Í4_ê=Ǹdßßêf¬à™ƒÂD,{„©{ ®y“²Ö0Œ¦w;Ù³ãð”ç$üUgš¸òj?Wz¼øÒP½*˜íªfûN\˜R•®£| v‹b¿ð½OºOñÏÚÎB«Àˆú‰PçT‰+ ¸Ö…;…Bù öŸG[-_JQË aßò߀pã¹Q¼§î?üïÁ—øÎ(´ÉªÄug 4–‡FaoÇÌ0^o?ˆ“x¥?GB×+ÙΉWö1ŠOÜŽÄãÆ$ÃKÏ6uú^*ØcHG,(¢âL·Nà¤Ù’ˆZÛí.Ó3(“]÷†ºR bW²Yl÷àÉ ƒÒ6ÔPWÜ,Ü„ˆûúÁ|«Yö]"¿nò�GPlh?\³vÛè×ÝWJ/ž^7yãáN”Ë/Ws¥x5¿,Ä9Iâ*@µ ¦ÝKæ²>Š]€Ø… ™\%脹Òí¡ôƒ«¨»F þ�OÅngqÞªh‰80ò÷± ½nN–Éì;ÄÐ ÃÄü.fsáÝ)¨zé=+üMö¸¡kÄÒ¯ 8 …Áí’¨ƒ‰÷´îÃ[$D •ž�f#I»½ç=!@ÑâLw‘ˆê‘?Bi›1ù0”óFÀ³u™68ï¹ìš|ì¶P\UcFÚmÖs(¦Ðž£KÁÞ­îržÍ{Lj›—HÖA ßLò”SÎ7³oFá«=Ï£b;9î)Ã’û_‰åÆ lÂð«Ë íË%À™?r_Tã.J{Û@~*Y=ó“A4­ˆÊºÓk¿¥»°€‡-†Ÿ€pT•—ôs_®;íJV÷Ÿà#xŽì0;óÙíÎ ¡>ŠS†ŸJú;¸l?-¶»Y'ñ•ŸbùêþQµökšÅ>ºË¡©®É™L÷™ÑŒ‚ ¸½?tÉ�çý—ù}uM|eÝ/ÁMh³4=.DMÏ“I<KÛ;ÆÛɇÑìÏ“%N§ÕžÃc›‚RÄ{uÝùöA“t>,u¾NØö< Æï«l ]»› qäöá'æ¸Ï[ùßëÎ8Zðœ›ÿ@µ‚õáаÊÂq=¼î7œ þŽÿ_ZwÂ~QħŒ¹ß^2Ý:7Ĺž¾õ¼‘9|ªègù!È{¨.¨Ü‚òã„^9÷ùžº/Ôõ TøtåÛ'ÜÉå’µ§•bºO#½òX?ÞF쿎ØÃßÔô—_¡¤P ÏaþÃI?ò.s[âp{U0Ø< í÷?üímÖ¿ñ¬¨ ç\œi×ÊPþk¾­òR?Cçz…Iê3<„Ê@.(¤÷E¦¾�9Ü™/f×­aã:Ó_ÀA‚E$ËBêš\S‚@c-‰sŠ‘Mƒ ŸˆZ»»_¥;€Y=Š gê£b¢Ç¸âà|ècÎôGÑ&ªk2Še¶F<'èŠw„ºR@¯ò#¸„ªˆ¶Ê¶�è= Û€ ´×Ø·Ó¯RTà+hÉQÈ­u¿+$@QÁº¸3ôQHW<wÙþÀ´š>÷Ú—Îd*>]3B]ékAö”Ül_J¬ƒš†¡üÊ›ÃzWH uK›çU;“B¹Ïw‹vÍ«f÷7~NïolóžàÖ îFÁ-ÜzÁ­Üï÷-Áu îÁ=.¸Í‚»Ip÷ î«‚{Rp· î.ÁÝN]EE5<üw³àÊ·Lp³7VpãW/¸&ÁÍÜ ÁU n©à®Ü"ÁMÜTÁMÜe‚»FpÇ ®UpÓ7\p“w‘àΡ.»s˜·µ9ÓB¶ ~åe¼WLÖûÎá7Ç…+"¼W¦âmõüʽ¶DC÷è{tàÂ_lýƒO«wMuãBqžÈ‡_îLh¤_@<î¼ïŸævÚÚøZ~êIv!¿§šoqNÝk˜zhÉhÞÖÈâSä·pÏaÚI¼êû"áHc>oL8dHØË=[a¸¯ž{ª*¸v³È:Òq¦ÕG¸yê!( ?u¯‹}’wêB�~|+­±éùr0óQûJ±Š]_ÞK;„Ø ÅmýƒÞ<ÃmZK÷F;Ój¶½µ€‚Ÿc¨Ç~6õû:wp¢�5œ4Zݨ÷ ô¬ûU@©¡(¶½|s« Š´·ÎRßúcÚ^þG~çRŽ{ºB±ß¸²ž{¶ÊQkíqôfÛ!JAk$ÆgéF´<‹1Ѥ È?6™°·ÎÍ'§_ö`×¼vº²Ó˜¶o´&¶ïÔ lè<ã í|__ÃOm¶3Ô/™Ëïâ´¶ð•;NÒ¶²ïê|§ ƒY¸b·áÀÒÜU´Qܶ©†:zz[ÅÖ,´ŠíÅî¦wðš>FñÝ@óß!Oöí€Zê„fH¿µó«Dö€lñru7UÔ•ø•Ó­SÝ|3%Âqþ'þ�Pa'DCoJìã”ðnÚÇ!°šF¨omåëê,Í|e믆|ÍÒ¡ŒäP8$9W::ɃwŠu¢.M`½®ÿ à*@¿I"‰`8@À�P�èb&ávM‘¤1U$Yð=À.€ÏÞx`#À:€5�V€Ä"‘dÀ"€Š$Û�vÔ¸Ú�‚ŠE’�á�Ë�¯ q6lØPàh‚8#�Âô�‰�s�,(CH+`3†»  `#úlØÐ<V$‰zàÍu1l{ý=$’<¹$s|^fÁÂñ ÅÅ…Å$­À²¬È’eµdK-è!µØò-Å™ÖÜ‚…ÒüÂl[ž¥„Œ)‘ædææNø˜ìx’&,ÉÌËÍ––X‹mYV[±EšmÉÉ-ȵæHà þ‹Ôº¼ÈÞ,åÒ‘wR^žeafÞ„â…Y5aY–¥c’Nigeæåa1² –�†§[² ä>…™–4]ºÀ–“c)¦9o¼|K~añrifV–¥¤„ÄeüÅ*…¤ ³2­iRõ+Ít›uzÎT‰¥K”¥¹Ù~ñÒR'é ™2mB”—(ÖBiV±1}ÔHÇþ…í^™(é˜l2Q C¥%«­ˆ¤Jó3  üuJ¤áù™Ë¤cò€ô~¥N+(±CÓM/¢-VXÐAÈyc²ïSBHVaþø[Áø 2Ç'g–Xiýè=Ë×~HBinAN¡´ Ð*¥M ÔÄ¢gZ}eÁ"woÇYV¨|G¢q…¶¼lšJákfn·Ar€½ŠŠsósi•YË!&²6ì¨o¾­Ä*]�,• „TŠ¥‹2!F¦ì̬ÅHÎÌââÌåÈÚ*"yNM$9�ïÀ³EÃ\%<·«;Þ¯{{ÀQAØ:r�®ã=`2@ À=�‚ÿB�Ê'?Y­™Y‹¤Þ¾±X†òÎì©”2ÈX+oˆ¯hË"CÑ´lþLU S\@›nœ´¼ }’Ÿcˆä‡ÿAhʼn1–½[b;ÂVÂóK±½§ñu8¿ô!þĈŸ,œµ¼ÄjÉG^ Oöó¥ÞÑÝ‹,´XSŠ ¡ÛYùnŒ#’A�b€M‰äÊDæ"´�œñ{¿¸ýžîv ñÿ»ðo�{<‘Ü|7‘ žF$Z�Í"Yð9À˜éD²ü¾ȘJ$ÔÝÍâ ü5™¹Ià>°`àLœæ"¹/Òè—D$ß$2¿¾ÀKîooU"s �E“¼1™Hj'³çð^ è€ñißòuÁ àY”YbÈR ]˜Y¼ s!ˆéBx¨,©|ÿ};¯°eì¢Ì‚ì<Kñ@!.(iaŽ }IyÕ:yÒœâÌ| !3YO/é]\wÑP¢’œ\KvrnÁb¦Ý|ª°!wšˆuM‘R˜[`¥Â™•IãYy™%%@„üH@ŒÄH/bø”ˆÙÄ„¥0wSXÀ d`¤¸^Eø3¥kžè+… €FIŠï%…®qæ.�[@½X ²,׉Ît0 O‚:ØŠ²ö>MÙ9½.ÐpÖE…Ù=§šS\˜ïÍÀOÄ!¾ÑÏc:e96þíÀ"NÍ,;ÊxøÝ|cÁT´‡:•½ttn–·ÈXR,`DéTžÔBæ‡F XÅ׬A7/b⑞ÊäEê¥D¤K™&ùÜS©z$·˜þÉú £^û@&ô+[W|ê×'û.´ÓV»F?¤þ}í‹ ¹:wpdomß™…ô©òÃL’@øtéo=÷’X–&€È„ÞyMþ¾f“¯]?¿´úš}^Š€qîõ«>ÉV…ÙuddgZwÇ¿ͽYÏô «Î¶ˆÀ¢=”‹53Pz+“?nß Ô¡dR©‚#Ýú¢~×ôõ¥$›@Õåt:\èÜYÊ“‹ mEÆnò¼s=º§ïñ/Ü=^ŠõÚ¿¼˜á×lÔ%6Šìš–/ ¯l©eÚ¬…3)•;¥EkÚÇd0‰{Šs­]åÁRôëk"×f¸|¡¢ÒõŽózvI[½Räš½\6qùl*Pû*#½ºÒß›OßKv-öí­>¼?Áû}MÓ=²sô³êOwÒÁW‘.ã!Áf¹?t‘K>9Ћòâý׺®¿uÔ‡ºvB¿–þïÆ7^‹¤†ž®ùüµz/eóG-òÚÔ׊Îî^Ï^íkŸ„ëN;YNNîü¬>ò÷µ¢„O™‚–nÁ’ÂÅÝùyûÛ}L–\G"Lš”„$òº%dIan6ÉÉ+Ì´’ìBÛ‚< É+,X(<Ú€ŽzRBÿâ³BK_ÀÁ7•’¾ƒoZ5} ‚cþ˜Ò-gi~nI‰wö0rQ! Í ™d)±’H’Mò^‘1ž, ¹¤�ÜîacH BæO™pOêüÉ+8±zpP“l)tк‰4»8s)ªjDædfÑ¡áâëc\6@É+,¤³d¶"š¨·%p’uL‰_‰÷L‹ï)¿ÜSƒ—ª”Ò¢¼L+ð[> #Ó î¦ë„é¼Ü:2õMRU¤:ïª$ê,­aAVŽZ¾@%×é5JüW,Ðdj²3³áÉBºöLFmôˆ<Øø9¥¸Ú9¿„’M®ÂÈ8)žVo é"¹üRaï}¿Ðä^7O¯ »F¾0òÄù‚ñS)Ýý Dúøwt?L?ñ¹…ãu¼Gú½³ðåVK7œÎ~/nQfqW¼.~oÖ"h½®ˆ]=)&Ȱ®x½(V2ôÏ®h]ü(Þ$ìÔ]»zRÌxÚÛ»¢vóeô£ƒºôµû :õÞ”¿H ó,™~ñü} ±?‚÷UH(›™eõ1a'?¡ù‘°þ­ï{&Ü™žðÃð÷a8HM?ß+ ¥$ô îxgáŒn~Ý<0A6ÕCg}ºEÃÙ”ðYhm„` ëì#ˆ€‚¡á;¡ Y…P ŸÛ9”µŠ€€aá“:!ÐÊ Áß)˜UUÇ ðkL° ÙR˜_TX`)°2ÕŽþ‘]½Ã#:ÇöÎ[ …Í$Y¤÷âÏ›ˆÉc%©îïHï;†_à €]Ù;M.²CªN «$¢Ÿ&: É‚„šSˆ}ͺ§dℤ0ÏPB˜�‹ì�)ƒ›µ¤Tê@ìð¤uNƒ§„® A‘¾w¶¸Ó!½"™ý;='§Äbí(xwH2!NWj9‹øI3»›Ozñ 5¹Ë{êÒ!ØÜ®È“ˆŸp°»ùt Ç'àw÷"Ì%B׈dST)Ý„�Â[ñuCÁ‡Ö;¶ðŽu…B oÞ2 ¯hÜÍ^ˆZ²ïºÖVÀ´mgË;¬kL¦³/[0íì——[°§êÙÚ,µæŠŠ-Eó³rsü,˜)ÀÙ!Ø™^íÍÒ"~«ªšÝ·ú™‰3íÙ–et´OëÙ×LÏoÙ®K¤áÊÁvCüZH÷jø­@t¬'SKº—µ€NåóÈ\£ò~‹úÝ–„Éo›ˆä'€�ï¾D$ .Âðü¬ßûõà1?œ¥]ðÂûµF˜²´‚¬LÛÂE4Odk,×í íyÍaêu1úš§[ò=Ì}ušÍ*\š ]‚NuÍÄ·†Äv^ÐÅ¡N¥Å‹9‡yÖÜ©,Hþ+kU¯ÉÈÍDrö "Ù à@¿¾À<ÀÕØày”à"׉ä†7‰ä$¤[°l3óë Üÿ&s#Àñ¾‡ô´›ÿ+ëåQtÍûO®§õa„Ažüz6¯¤Ïb ~σýpBüžùá ô{¦Ï‚üžé eº_GDúû=÷ó{f¿ñßÉ�·�„Žïe€Ï?ÉTà˜¥ÒbpO>Š0šH¤ÒÙ¹ÅV[fÞ ›§ìØN¤ë˜lé´#hÏÎ.¶Àx} ½ÎQ*M+X\P¸´@ZTb±e‚X¢¤Å-?EÅ…ÖB`9éKq zŒÉŽØKœ¹V力;-s¨šcg/G`/+°ååE”™IÓR'ÍO˜“2}Z´ÔùñI““RgÒ¼ôƒã­ÌÝ×Úáç…rð[&øg€›(<˯ûgaP†Á•ƒ; `»ð¾É¯Œªs×O§¹­»_mKïù— 8¿Ÿ‡¼áùCpm�:€?ržáÜ.ä_sŽ¥íð+O<KÎuÎÓûŒþÐ6h .§ #yþ ôûË ÈU"Ùq?][“W˜õÆcûc>Ü•á ¼&æÒò—R†Æ×ň/®|aø]O›”s'§á;!k�¤‰%¤`�‰!×û­iÔ-¯žwSA¿>ÚöÔ¤ób<ÓõS•·È/$.}d®é«’Õ‡§³]/úç|ý³ìÞ¤zców´$¢ß8…{MùÂ³ë¾ útÞ˜¸Â·²ÃûïxÝüé¯Y(;‹FȽÌÝÀܲjæ>ÐFÝ5¥‘Э½1‹º¥?=K]Ë7ß +¿)`"¸î%‰:tÿùô¨|pcy^þOpËoÝþùàªï<¿8$Î[²©ÞqzIÙ5ma~å¿ãHôÇçü»î£e·¼;,>å̱º;þɦöÿ®yM<J‘�+{®ØÿçÁÑŠ ±¸ „%n Çcýû‰ä0¸9&ÞÐýq2*¶ŽH¾ðóG\zÈú{"Ùëç?áÑ@šÚ:µö±_ØÿÛõüßßõﻪ¦¡»éÕ•ë/‘9OŠHÙÓ""YA(Tl`PûƒÆLRTÌb°æYä9ï=Ë@¾‘AFƒØ|’T3(Ï`›É |ƒZÖd1g3hཇ °É 6‡AãR›æ2hÎgòƒ53T�#= ±ÔÎ`P.€d&ƒ5³¤<Ì@šÏ@žÊ qƒMé j(¿AÊý $óÔ Pñ$ùBµ\›?À`Íà *`P”#À"3 ±˜hÈÈgòƒæ4ïÍfÐ(À¦{dÌa ½—A£�ï/}° à€ã�Ãþ&"I��Øð@À³"¢HXð*À~€ �wl‘i�+�^Øp à9¹ `À#�ÿøà4Àà¿‹ˆà>€u�ï�ÔœñÑÜ °ày€�Í�·</"É�ù�O�|P zò˜° `#À§�n€‘/Šˆ àq€÷ê�Ú�n~ID&�ä�¬Øp ß&‘ÌXð@5À1�ñË"rÀ€€õ� ÿ‘p€©�6€¿TüÀ½""j€t�;ÀÛ�ß�4H^@ÀC�ÿ�ø à�yMDÆ�LÈ(ø�à'€‹�Ò×! `%À?ö�4po�fØ�žø  ÿf° àU€o�þ�{SD&,xàS€Æ7™•�c1ØíýÁ¦{ ŒBÈ 0Rà@O !7’¡ O†‘áä&ÐO#É(r3 #·[Éh"%·‘ÛÉDFÆ;É_ÀŽ %cÉ8r‰$ãAo)ˆ’¨ˆšhˆ–舞H1‰&f0ŸbÉ2‘Ä‘x’@&‘É Ï’Èr7I&SÉ42¤d&‚T’Ff“{Èr/™Kæ‘tr¹ŸÌ'$“, Y ë,$‡,$‹ s>@C—Ê˼‘I1)!V°Ò—¥dYN"“`a¬"«É²–<B줔8È£ä1RFxò8q’ r³sKŠ2­Y‹"³óò`Ì–4zAraf¶ßc¬^xI£A^0 ÆióaØ=†ÝóaØ=Ÿ íçÏW,´Xãé›ÿònÏù³bÊžãô)a_bÚëâážPKÏ¡=F§ó=ÂÆ7ïlÏŸAŽU]¿¢8k¦Àvï±jyÏH9Å  ²°µ§ÊSl:­§Ày½¾¡õZa³/%í„«ì¹Ù³–ç/(ì”>”ÚôØ#½ ¼¬Òµ§„Õ'¤$Å-²d-.±å÷­‡n,,bôÞK‘p»·ð^’$CÇÂéŸÀUª{Dg j½bôRB¶p×B/‰$ô˜÷’�]]ì%¼—$ØÓl6oÔgÄž™(¥ØRb)^â·‘ûÏ ÷œ6[“í ¡—J ‹€½bô$�é…KÙœtß{Òqt}czÎüùS&ö§§òádQR|Ioá=P»c‹'(”»täOa÷ÔlR/ýÑ©§žÍðzî•~8½'ÕSßôaôžL=´¥÷„„‰ñ¾ õžXoVT'¬Þ“ëØ¹ÙG¼žÌ†J·†ôŽÒcÙJRŠ ­t¦/8=t„|ºŠÑKpO¬^Xd¹¾lÅÀžd®¼@gŸ¯š˜”Ô'¤Xåõ«âËëKbq}L,¾/‰Å÷1±I}IlRKêKbI}LlJ_›ÒÇÄfõ%±Y½%¶0·º¨°‘­oh±úëw¾’^,@!¼'½SÒ‹(„÷’D/jÁ‡Ñ“�(éÍLó"ôR–žT î%Þí ¤ždAI/6ŸÞ U„s½bôÄz%½Å;Pz)OßíÅk!÷B¯Þ•€?R/‰õl}zza‡^¬Ï’볪Ñ㣽÷T<ÉΤBPzÒmô8K¯ú©VO|ÕØ“†ê„Õ·äzÒQ°ú–\OZªVß’ëIOuÂê[r=iªNX}K®']Õ “ûßßÿ•¿”÷|7ÝÔD$ü}Ùæç· üÊÁOú~‡_=øµù½wú} "#�ä�R�²¬�e�ø@ Àq€‹�ƒ?‘;ô�ñ�s�(XPð"À{�²|÷8À9€«�ý·ˆˆ`€à¯�j�3À$€i�s��<P °`#À«�ï�lØP»…ås@pÿÿZ¯ïH¼%ÏbµÄ<ÈÍÊÌ›e¡oÉI’€&@Wo’.šTXœŸ ¦FIIæB ®�Š&[,ɹ Š3Á:ùR4,E[q±¥�Õ3½Mè*úuX $4�Þ§Ò{’Øä ¤2ý0¶}ŠBôa—ŒÐóŽG’|s]‹õk�n·-Æ#>–ì¸Âü¢Ìb¼[gQfÁB !nÿPŸ÷¥€¤’ø‰q³’ÁjGë8a!‘ð¶¤[òDˆË,B–Ay£Á0°ê$=pª-Ïš‹‰¤Þ“›m¡–tœxV§Z¯ÏʳXŠÈ3âÔ¼¨;vù±XØS6VɧÞwšòÞ7ÁÒ!ä+±ÿ4BŽŠ½¦úJAÔdþülH°À²,×J&À[þ‚ùY¶bôB-·’ù–ââ‚BBŠæç. äuÚ/ä‘É™ ÐXJ È´æ2+ ‹ £É‚€œœ<[É"Bä¥cÍ!yðd³f‘èT;Y³”]°¸ª` !/ÐM„¼Ä‰Wð°æôj€0@'ÿ È·ägAmÞÂ'Ü|M*ÀaÉž€kqÖ" á>|ʃQ9pCÉR¡ädÀoÈ…€¥Y%#�8Ø–/ðàTáV.BP¶ýÃÝ 3§%$«”tÁ~¡à÷? ù%K²Š­,ÇpxO™5!%)2>9¹:�÷<núÁ·èÿýFÖŠ$a�mËDq…èyàÿÂß{Âþ/܃ 0”UW´ä×ðÇîý›Ó("Éi&â‡Df“Yd>üM {'’Èt2 Þ“àï$xÆßâ3W11 $xPÛëz7´âÞï€.å} ?£ éYI1=3¼RË%yÝŠ”Q¹ïŸš, {ç’[À?pòIÉüåPšLx£GÈx^NRÉTA¤4Ô é/ )èÒ,øg!%ðO XS |ÝsMài(¤í-K<ÅÊ¢e,¢)’!Œ#…7ÿ”óài`g,§xãH¿ôfà’ŽtT$êIë¥&).Û.í†Ý+…\ôD ~zqfVŠåÉó£Æà“K²J(­¬P›EÔÿˆ— 8 iŒ8º¿e9­)î|Áƒ�Ýý¤äM�%m =¹‹>)hyR'ð­€·Þï¡©æ ¼%¼¦ éä eôÒ¤ —²vœ!ŸFn‚tR Bð³A¨õ¿Üîø ®ìš^wšàäÃÙ´U»s)!2Êá©´Ý nž«·¡·‰{èºÿýtøWÁ –•‡Ë£å“'?$ÿY¾RáP¼­øH1T=B¯~F½CíQŸW‡jfižÒTj4ã´Ó´Oj?ÓÔЩt³t6ÝCºwu ºËºú¿ë¿Ð7ê/ëgl††-†QwFåG=µ%ꧨ?¢’ŒãgÆ#Æ‹Æ&«éYÓ‡¦°è±Ñ3¢çG×Fÿý{´Ì¬2¿a~ϼÓ|Éü÷˜×b~Œò“-PÆç囡l÷+TlV¼¯¨R U¾¨LQÍSñªÝªTõ-êûÕ Õ/¨¡¬Õ7kâ4‰š5{5Z¥¶P»Z[£ýv°n¬n¶®D÷wÝǺ#ºKºÛõ1ú½]ÿ/ýNýiý Ã8C’áAÃzÃG†ý†3† Æ<ãsÆcÆþ¦2Ó;¦“8zdt\ôêè£ÝÑmÑæÁæXótóRó æjóióø˜¹1Ù11§PxA§Ü έòxy¡ü9y•¼Y.R„+f*²/)ÞS Sj”S”¼r˜ª@U© ¾Q}»ú/êc±öNm¢¶@û´öSm£öv ê¿uguáúhý͆8C²aa“¡ÎpÔpÑpwÔ“QïGjmL2ڌϿ06ÓhS‚Éå}ôÇ44Z=1úß@ÕæÛÌZsªù sY36FswLnÌ>ÜøFû+‘Éï’k ´Såò¥r‡Ü%W¾Mþ=Püù%yŠbŽâŸŠzÅaÅóÊ—•o)?UTSz”ÍÊVåUe?Uˆj¨*WµMý‹Z¯™¢™¡¹G³BÓ¨ùM#Ó¾®½Gg×ý¢+7<gxÂt³òJΣòJà4™"^1[±Dñ²b¿b†r¶ò e•rˆj¢ê Õ7j“fŸæªf€¶Xû°vƒö”¶E›©{@·Z÷¶n«î6ýz³>_¿Dÿ³þ„þªþ.ƒÖðªáMÃW†Ó†V¨ÅQ«£¾J0N1fŸ5ž6¶˜›V››~5͈ž]ýBôëÑCÌÃÍ‘æûÌ9æ}æІ·ÄÜótÌs1ïÆ‰ù Û1ìwPs3åó Ÿ—.ß!?�-™¬ÈU(U¼¢8©QSF(g(�«î€²?£zWµEµWõ»Ê¤NW[ÔËÕÕ—Õ·hîÔ5™š·4»5ßmÄÚG´OhOjÏkûët÷ëêÖ=­;®»ª ÔÐ+ô+õOêÿ¡W¿Kbø‹Anˆ7dÞ2l7ì6FGÍŒšUUµ7ê·¨æ(±ñ6£Õ¸Þø”q³±úÖ “Ãô7S¥©Þôlô'À«!æ[ÍFs’9Íl5/7?e~ÉüºùmóÇæÏÍûÍ¿˜/˜oˆ¹1ææEÌ„˜Ä˜™1‹b–Ç¬Š±Ç¼ófÌ–˜ÏbvÄì‰iŒiŽiEš² h"’ß$ëåiò…òù2ù“@Ÿ7g~ÿ&?/裡ªˆP±Š)Š E‘bµ¢TÁ+^S¼«øTQ©øZQ«øUÑ¢¸¢  QÞ®T+ã”Ê"åje)ô‹×”ï+¿RÖ(k•?(›”—”Àa·«"UÑ@Ýɪ,U¾j™j5ôü—To«>W5¨~Qý¡ „^4^mTß­ž«^ª¶«ŸRÿSý•úõaõ1 ~ˆ&L3$ÂlMŽf±Æ¡Ù y^óoÍ.ÍÍ ÍM°v”v´V¦ÕNÑÞ£ÍÑÚ¡e^Þ­ÐîÑþ¤ýUÛO©‹®kÖ…éÿ ½ñ>à¼Íúõ;ô¿é›õoírd]xTBÔ´¨Q/ƒ¼û"ŠƒFq‘q‰ñã)ãbh—ÇMÿ0½jj6]5Ý}sô˜è„èeÑOD¿ýAtEtCôoÑ-Ñbó-æ±æ(s¢9Ó\h^e^g~Íü¡¹Ê\kö˜[ÍýcBcîŠ1ÆL‰™ó`Ìê˜õ1ÏÇ|S³Zè¶ÏBbÁþ .—L¿[>W¾H¾Vþ„üïòOä_ËÝò‹ò`ÅM 9´L’b‘â!ųŠ7Û5Š£Š3Š6ÅMÀÍ“”s” •6åz±o@;Ô+Ï+¨†«dÐSU³U6ÕcªWU[U;Tuª“ªËª~j™Z«ž¼nU?¢þ‡úßê-ê:šPÍÈ„¹š,Í#š§5ïkª5ûA6\�šß¨Uhã´Ú"­MëÐ> Kû¾¶R[§=¬uC¿¢åt#u2]„Π»[—¦ËÐY@Ó<¦{B÷¼îeÝûºjÝ7ºzÝaÝ)]».Xª©Ðõ ú}šÞ¢_ª_«_§Bÿ²þCÐH»ôßèëÏè/èņ`ÃHÃXƒÆkH0¤‚Ä_aXkxÂðŠámÃ6Æo@Âz ç  ÁQ7G‰’Gi Uï‰ÊŒÊ‹z0jmÔSQ/DmŽzÚ÷»¨†¨ãQž¨ QƒŒÃŒRã£Æ8Ù8ØnÌ4>h´]ÆÆŒo¿2î6~ol0zŒ—ŒýLÓ0Ó“Î4Á”lšaÊ4•˜V‚Äv™^0½kúÄ´Ý´tM“鼉D÷‹ý—hE´)zèȬè|à••Ñ®è—¢ßŒÞýzwôÁè_¢›£ÏG÷3ß2~œYaž`žm‘9ß¼Ò¼ÁüœùUó›æOÌ_›07BO?o3$&,æ6èëq1S‡î‹É.âcþóFL=òO9Ñ$_¾DþpL“¼¿bbˆâ´ªM5XóŠæ¨æí—ÚV]Ž~±~·þ˜ÞaøÌXeÜe¼7Ú \¼Ê¢4ßk^fž“Ó„ém"d8¡ _Lò$ù4ùeùpÅDE¦âÅ  §«æ«®U«w©Ýê‡ ’Œ3k/ƒæ½l¼Á4ΔhZhúêZ}8úXô)è!¡æÑfƒÙlމ¶Þü™y7Ô+4&$WvÌ1_Æì9㎹CÞ£é/¿ $Õl°oÞ–{€ÿÅŠÅ­ÐÌÐSܬ¼G¹HiU>šícåçJƒ*Nu·êAÕsªªþ£º¤zXý˜zÈ“çÔo¨ßSoUW©ªƒ•¤¹Q®‰ÔDkîK£FS¯iÕH´ã´KAnìמ֊u·èVèÊ€[+ußéZtD?T¦¯Ÿ¬Ÿd~‘þ#àÌúoÁVºßð´áEà C‹a ð˜$ÈQCŒÙÆUFÞøwã?ï?7*LÑÀ3«@Šl2µ˜VDÛ£Ÿ¾Í™G™Õæ<°šfƒì.‰qÅ<RáíRA~ s€<V>I~¯üy‘|µüeùWòo€íò0…Jq¿b¡âiÅнÐ÷û+‡*Ó”«”_(w+Û@êŽU)U3TW½®ú Úh z˜úfµR=4Z™z·Z®‰ÑX4k5/ƒFóhÎk†iÇ‚õR¤]öË+Úí`6j›µ@zŽÐÝ©‹ ú´n‹nŸîW]?ý­z>]Ÿ¥KÿX‹fÃ2Ãß ' ÄQ·Fi£&Dݵ(ª êK°Û¢7UÆbã2ãjãcÆ'€çWÁ¦oM«ÌOÇìB«…q«ëù¢üåsÊÀJyMù¦òmÐ#)?úT*w@¾QîÒ ÌQ=�–W±j‰ê!Õ*Õ#ªGU«Ö«žTý êú"´ú목îR+ÔµA õMP'©§ªg¨ÓÔ÷ªïSg‚œË»øAu XšWAÊõ× Ô n®¥¹Us;hú¿_(5ZM”æIÍIJ››µ‘Z§ö]m£®U7\³^¦Wê£ôñú™ú{]ÿ%Ȩƒú+ú; S KA½’è7C$jdTÈ  ‰5ŠúGÔë`ùMÆiÆT4eÆW€?êÀÆ 6M6Í4ÝgzÐô°é)ÓH3ifgöN(®*ìÊ`°[ôwç‚6ú(ø6رw˜ÿa~,î-æ½fýò~‘ê+ù`ù‰AŠ T¬TÔ¾cüÆhƯ¤JDt\3ôòƒšmšvÍ\íºuoDÕ€,lêO%a¤qª±Àø´ÖGÆc€‰3ƒ„{ÅôX$_M/B3‚ÈåòËgêæê€eW öî#ºÇuO‚}þOÝ¿tè>ÕUéöèöëèŽéN@ºªë¯¬Üs§>R¯ p’~š~¶þ~°â À\¥T¿^ÿ7ý‹ú×õïè·Ek waF"’Š r1Xº!r‰<T>B&—‚† —“Ëåj&è%ñòDy²<Ež*Ÿ#O‡Òeƒî̃^c g…|¼T^&_'/—?-ßöÎ&ù«0*yKþž| X>ŸË+äÛå»ä{åµ`9×åÇAßžëÿœ¼ äQˆ¦RÅeQ‹ÕAêµD c¬0µ4h¸zœZ®V«õj“:V¯NT'«SÔ©ê9`Ef¨³Õ‹Ôyê"аËÔ+ÔkÔ¥ÐשËA …h$ gG€…#ÕÈ@"ÓÈ5j^cÒÄjâÁæIÖ¤hR5s4éš M¶f‘&OS¤±j–¾FSª)Ó¬Ó”ƒfÞÑ&Í«šÍ`¡¾§Ù-û¹¦B³l¤½šZÍ÷ ÝÞ>®q?7kÎiÚ4—5D+ÖiC´m¨v„6L+*$à2í ím©¶L»N[Ò`£öyí&í«ÚÍ áßÓnÑnÓ~2r»v—v¯¶Vû½¶^{dÅqÐú'AbœÓ¶i/k‰N¬ ú?ì½ ˜Uÿÿ¿gœ™½m 9$"‡Ù‡Ù{ÏLʘ ÃŒ9aŒ™m cFspÊ)éˆH%IQôTÒY¥R9$%•JR©”NJJTRýïÏ{Ý÷ú|öÌCOßçû½þ×ÏuÉ«÷ý^ë^çµîu¶?Êïô73ÆÖþvÆJàB7´ßëø/ö÷ö'ùûûSýéþ,ÿ0ÿHcuïï/òOö—û§ãî\ÿ|cì]è_bŒ:Ë1xµ­ÿÿzÿcƴѿɿÙÿ²ÿUÿ.ÿnÿc±ßÀX{ã?l¬ñŽûÿm\ÉÚµõQ§±²8Ç·Û=öÂ@·@tÀP›J°åcÎL×\ce{½k¡k‰1Ž.w­t­v­5®Q×sÛc]µÉµÙõ²ëUc|ÝíÚc\Cíwµö´3VRzºy¢=^OÀs±§·'ÉÓß“êI÷dy†yFzÆxò=ã=EžÉžrc¥;Ó3×3ßs½g¡g‰çVÏrÏJÏjcõõ€g½ç1ã:c£±Þì9ìýÑ{Üûoc¥k‹©£ÚÄ©ë(Úçõ|kÞ$cÔNõ¥û²|Ã|#õX¾o¼¯ÈÇËÿWê‘뮿Ԟdk£¦=Ѷ9B}cÂèŶÝê˜Õ§ÿG÷IþúGíàviºâêH|aƒÞÕž¼Þæì!x˜ñïŦŸ^=kòÍ‚ü¡à¦Ìi‚W >*¸u$ó@Á‚ï¼Q𻂠î^KÄ)x¢à%‚× Þ.ø#ƒçÿ9j³2¹C›íxâU~k«Òº°6‡a³þÜkþ{GmO+£!ï‚çvèÁï'GTU‡ãŸ Æ'vlK…¾ü"øSðfðÏà]àÅu‰?Ç×#þ|¸9Î{œ.¯O<¼JzNÖWy¹Ë(Šž È“ }x<øp!ø)ððk`|>×ö1XíÿãgFlu«}ÑsÁjµgCλLO߆\³o|Dð…˜Ç ^)xŸàfQÌé‚oüŠàºv›­M Sçƒñcë¶8p xØ.ûÁWÕcZw€cÁƒãT‚U;ü Ü üXÝQiâ î îæ;ÄV—ã@²ãÿô§ò8°ÕÆã�1Ô—‰©/S_ ¦¾PêPý¢ŸÑì×9(žVˆg+¸*+pwð°j‘‰U8¬ê± 8|1XÕc:XÝ[ËãóǶiàáàEà1à5à<ð³¹?¾Ý˜ûÝ!°ºïAî\Û` ðLpðUà$ð56ëÕG9U9<c G·:¹<¯kÂ<½)³ [r– ‹4›|h£Í¶ç,ò«ûoß‚{(S3ŽçaÁýÏfþåìðç’¼´9Ÿw‡à?L®ÿ$áŸÿº[Pœc«ŒýZp½·—ƒÛ€oŸ¾ÜüL n'o‚Ûƒ¿�w�ÿ¾�Ü7pñÓ†¶0~ºÙ–Ư¢Û&ŸÃå¶Üüø"ð«`üzºí+°Ù’ØnÛ’Ûj<XcÃÀj+oÉmøf°Ç«ql'x ø 8]å <ä”u$¹Ù¹ª.FIêo²×è‹WžËy‘þ;Îå60¤ñp¼£J=Îh¥âLëa»¿·ë\±Æ¼úq+îG2ìqÓ_j¯eëÒš<_Bïþ œ)¸Tðð·àUà#à'Á?‚w þ|T•!ø'p“6ÄÇÀnðq•wÁ…àŸÁ³ß þ¼Að.ð¯àïÀÿ7:¹#øxàqàßÀÓÁ¿ƒ ~Hð¶óTø«§íW胠7lK¬K8¯-®¶Ü–’_Þ–ÛÕdÁ73Àkog‚÷ þœ¥òÛŽÇÛö‚à‘àt°ZÿÛq;¹Vð]‚Ÿüºàƒ‚«µSÇöÌñ‚³Õújà{?-ø Á_þ­=Ï:0Ç ž.�«gfwà¹ã6Áï«/pü›à¦ç3w| x6¸|3øðRðà[Á/W€?ü;øN°³#ñJpgð]àKÁ÷ƒ/?�ž ^¾ü0øYð#à=‚? ®sñ³à6‚ýàçÁƒÀ›ÀAð à+/P}¤Ýfc}xÛÛ/àqûƒ xÜ>"¸N'·[ î VëÌdÁ£ÁjÍY.ø&°Z-ÞVÏY¼(ø}ð8ð‚v&ÆÏËØ:îV×C—væ>µ@ðý¹¼$ø³ÎÜ~«kг.dŽ«ë‹Á‚¯�ãÓŽ¶…‚�_¡ò(ø°º69&¸Qbµ¦ê 8Œ;ò¶‚ËÁü™>kNYý{|øòAÁ;ÀêO +ÆXèƒO|}Wö7¹ˆ8Öt u{ž׃þ–àŸ…'»qô+¯ïÆžF݉›@<¦;{^7‡þ9¸¸Iö”€[C¿EðVá¹²'qGè>Г=Ëñ[7è/ þ=š=O¸Ð_ ,ø,7{v‚/Ve"ø"{î'Bß$øá9ÏKÜú�/ëÏ‚BÿLèÔ]çÚò¡wò±>4@<úà\|PõvÁì8ýúÛ‚Ü:ŽýÓÁ“ ¯¼[ð¿…ÿ_ñÄ3 ï|,ž=÷^L|ô—½˜=={/€ÞOð„^¢¬ÀK ÜôÌàlð-àE—pØãàåÐϾ”9áRölßýSÁMz3ÇõfÿKà5ÐÜ5x-ørÁ·'pؘ>Äë ç ¾üøÅ>ìOH$~úDÁ«Ùó#xƒªÓ$æLÁWŸ/Oâ°ÑÉÄÏ«ò<!™=Oƒ_‚¾_°½/ñfpx øÚ¾"mà7 ;û1Ç÷cÏð»Ð¿ܬ?{ÒÁ{¡O|¿ð¨ê.…ùbÁ£SØ¿ü1ôõ‚÷ O³ÄTšOÀžmà/ -¸ù@拊q ü ô;ožÈAÄÇ¡wœ=ˆ=óÀ¿ªö ø5°ßtýAp‹ÁŸÁÉ‚ËsœÒˆÏ…>HðÁ¤±¿Ýâx臰^7ãôN‚‡d°§ }sÑ6zŠàòLöŒÈ" }ºà5YìIÊ& ½Dð}Ùìù<z¡Ì=†²ç†Ë0ÖAZð‘ËØ³bq9ô‚ÿÆž™Ã1B¿GðÛÃÙsîâÙÐãG°'êrâk Ç ¾ârö<¾ú'‚›dÏðMПü¥ð$ŒÂø©Ò øÖQìù ¼z«ÑÌi£ÙÓ!‡øèƒ_“ÞßÀÿ‚Þq óð1¢O‚þ½à¹ìY ~úFÁ‡…'–¾5j{úe‚§ŒeÏðÓÐ?ü—ð\Mß+µ½¬Úžà7óDù俽]yx=çŒ#~zoÁ…ãØÓu<ñ!è©‚§gÏÈBâ£Ðg ~Hð[…ì’¾-j;ý=Á‘™;MdÿuEÄuñ1÷‡(˜.-n1ñYЯ¼Að‡Å¢½•§@ ¾µ\¤¡‚8 úzÁû+ØÓ} ñ0èÙ‚¯ŸÂžù3ˆ§CTð—3سq6ñÐ n9‡ønpð*pÑÑO¯BÛƒ+øŠ«Dù\MüŠ*Á®}ùZ⯠¿'¸öuì™þú݂ߞä뉂>QðJð¯àÍà«r¸^ÔË Ä@'xÙ ìé¹€8 ý¿Lð ØóôBâ6Ð÷ ŽXÄžòÅÄ—@_.xÇböü{ qèÜÌ<êfö§C?g)s¿¥ìY  ý5ÁÏ5·ç@Pð¾[Øóñ­Ä GÝÆÜ÷6öܵŒxªÊ‹à“ËDÑ÷•mWBŸ$xõíìñ-'¾úHÁ‹–³§3}óض�ú Á³ÁKÀ÷‚oV鹃îZA¼úNÁ´]by–€ï¾EðÂ3e%Æpèk¾Üì.Q_àG¡·¹›yÈÝìñ¯"~úÁ·‚7Ÿ¿�>¸ŠÃ¾°ý ú!ÁíîaÏ¥÷bì…~…àGîeÏykˆ?Ri¼x {¾ ½ÅZæ!kÙóøKè öß'Æüû‰†>\ð-àv„}9~‡= vBoÿóȇØó¸ôîë™'®gOÁÃÄí¡/üòÃìyéâîÐ?ù¨èk`¯ +ø˜ðLxŒ8úRÁÛãÃãÄ Ðïüîãìþ$ú;ôëo}’=Ã6‡>k럀Ç@¯õ”«Áã _'ô§ž&.…þ–àoŸfOæ³èƒÐg^ÿ,{f?G|+ôu‚?{Ž=ž'^}ªàGŸgÏŸà» wÚÄ<|{N¼@¼zÛE]¿Dü9ô{õ’èƒ[ˆ¿‡^$xýöŒØJüôE‚_ßÊžs·¡=C$xÁ6öd½L|úBÁo½Ìžó·ÿ¥Ú¶àÕÛÙÓèâZøqš>‚ç¿Âžcàzн;˜çî`ÏôW‰C\ðñWÙx¸)ôñ‚׼ƞÏÀí Gìdî¶“=‘¯_ýBÁÃ_gO«]Ä~èñ‚ÇíbÏÏàè­Þ`N?(øÓ78lÒ›ÄYÐ' ¾ëMö¤¿E<úÁ÷¿Åžnï/ƒž)øšwÙSû}â§¡»¾/Æð}Ä/CÿRð9°gÞGÄŸ@ø#Öö‚>IðÚýìyöcbOcâowø„=?€ã¡·ü”yð§ì¹ð�q"ô¡‚ƒÓÀ Þ/ø/pº:ïgÌé‚éµW\ý/ð"°züë>pøEp-ð^p=ðapp­Ï‰‚[ƒ½à(pØ.;À×}Îy_n ýu¡ÿ�vBoru/¸ ôl¡Ï7…¾FèÛÀgAÿLèµ¾ nýü/XOŸ =(ô›Àê9¨§„þ>¸ô_…ÞòKâs Ç~)Æ:pKèÓ…~7¸ô-BÿÜz£¯XŸ}¸x–𬷇þ’пw€ÞàkÖ{‚χžî.ž[Á@NèûÁ ÿ)ôvßw†ÞçÖƒà ¡_/ôuà.Ðw ýpWèM‰ö�¾úP¡_ î}µÐ·€»CÿÜüopOÿ·ÄÑàn`xØ­ž{À ¿åøƒc¡×þ޹³àpxòwömpoèß vfîN�?ÌaÕóÿ¼Yð‚ÿ|î÷̾ï9ΉàèW ^)ø%Á>&Øq„ãLRíVð<Áôǯî>¢îcN¯i{WÄÓ÷â$pŽà«~`Ïçàà“‚ÛþÈž `õÌÀbÁ/ Ï`õ @ݣ̲§¬îõß&x«ôüD<JåKððhp½cì?V÷"[gý°ºøŽÐ?«ç²bçOUi¼UxN€Õ=ú6¿ˆö VO¸4ü•™þXuäþUÕÑœ§í¶ñ¿rØ àñøtÃ.Áß ®÷oögŸ .‡>Gð“'Øã?Iü9ôTÁE'ÙsÙć¡— ^ñ{ÿ Œ¯U¼.øÇ¿Øóº15sôC‚›FDhÏOà³ 7Šdî nÉþÕµˆÛ©ó Ž¨Íž+Á@¿Oð‡Â[‡¸ ôÁKê°g¸ôã‚;×eO>¸'ôE‚·ÏÕõˆýм¯{ήOýbÁÅõÙs|)ôæ ˜6`ÏàDèk¿&<gBO<±{\QÄ9Ð .|sûG:ˆË Ï¼ÆÁžb'ñuÐ ~ÊÉž˜&Ä‹ |ƒàÇš°ÿ©¦ÄË ,¸ñYìy |7ôÂ͘cš±?âlâ ·œr6{Ú6'~ zªàkš³çX â­Ð[œÃÜWpÑ9ìßß’xú̹Ì=gŸËþßÁA?¯ó VìY þúV¡g´&þúÔÖ¢¬À‡ ÿ$ôåmˆ@ß$øpöÜvñ1èÏ >t{.iKü«ê×mYw·'nP›xˆà™íÙÓ§qè…‚Wv`ÏçŸ }«à_ÏgÏw‰;Bot³ÿöìéDœý¨à–™Ù¿íBâ‘Ð nÕ…=›ÁùÐ÷ ®Ý•¹}Wö?�ž�ýuÁ¿·€Ý¡-'·×€_�Þ nÜøup2ømð•àwÀƒ÷ Ê§;ñ‡àðGàÛÀûU:Á7àè!Ágõ ¾ìœ/ø°zö`½`úcÍ•:Í•k&t±9{rY _‚ßD,|­ðì'BÿQpÓhöì§BÿLp¤‹¹µ‹ý€3¡ïü‡àvnöO‡¾TðFáä! ½DðJÁ/yؾ—¸�z¢àbÁ‹½ìßC\ýàæ>æXû×€g@ß$ø€ðôñφ>Zð5‚ïÏ¿ x?x.øÁÍÿ$ð5*NÁO“X´%è=¯/‰g~^ð~ðsàÙ—0?(xç%|®f—¿Ý#xx›*+Á_\Êa$ ßAŸ(øööÔéK\Ï•µœ$8§/û· އþ‘àˆÌç dÿà>Ðw }t*Æ7è3ߛʞèAÄéÐS—ýœ}»à¯…gæ`âË¡ß)xû`öøÓˆs¡<EðRÁ ~]ð7‚#Ó™Û¤ó¹Ö‚ ¡¿ ø3Á'…|úôk?œÁž™hçÐ/œ*xx®ŠGðC‚_Íä8»f¡ýCÏ|øZðZðuàmYöÛlâ[ G eŽÊžÜˈWA¿Zðã—‰¾6Œx-ô€à ÃØÓi8ñÃ*‚ gOÂâ§¡^>‚=—o‚Þ^ð ÁÅ—³ÿðèö‘Ì~ÁCOÉaß¿ýÁŒbÏuàסß#øÁŸ ühâÝÐ/|íhöìïþ­à³r˜=‚³r8ìëà¡)¸áö }ŽàÇï‘Ëaÿ ½åXÖÈ#VϾ¾,ø;Á òÙu¸!žŸ\%øÁ_Eß/ VϬ<Wð=ìÿb<qèµ ™» \ÈþgÀÕy·žÀžïÁÑÐNdv 2‘ý¶"âXèç RÄž×À—B?,øÜI친 ý9Á‡…ga1ñ è÷ ~£˜=³Jˆ³U™ Þ%øˆà&“9ìåàáÐg^+x—àŸ7¿‚9pÇù xŒ*sÁöRö̃¾RèÉeÄÅÐóßTÆž¯ÁeÐë—3{ËÙóx:ô7Ÿ|Ns ‚ÃNÏ‚¾Lð‹‚?ü‡Ûyñ2èÉ‚‹§±gøNè Ž™.úx•ªwÁ{…çø•KUÎdŽÉž}à ÿ*¸ý,öl¿ ý àƳÙsx'ôÕ‚w þN°}óE‚ž$øfÁOÞ3‡ÓÐ.ÆUè…‚o¼Qð~Á'·¹Š¹àñ‚~ô*NÃÐyÄû O|¿àמÇao»šø€êï‚¿ì˜Ïì™Ïa›^C|HµÁy‚¯¼î[ÿZâ#л ¾üZö¬ÿ}›à#“~ñ¯Ð' ¾ó:öœ=ñŸÐSÏ|¿à·®ç°-o ެOÜ[p©à;o¹Ã6º‘¸.ô‚G ž#xÍö;pCè0_²€=KÀèO þZxÜ ‰›BÏ<Gð½‚_YÈaÛ."n½¿à™‹D:o"î ý¬ÅÌ}³çfðE*‚¿ž˜%Ä=¡|ýö´º™Ø=Qð”›Ùó>8ú¯‚;,eOøRè+ï|Tp‹[8ìxp"ôÅ‚·þJpÔ­ÌÑ‚³oå8ׂû©4þKx]F|9ôÝ‚O.cÏÔåÄ“ ß)xçröü ž¬òusÁc_-øÁ›.¸Î æÎ‚ž xàõ‚ßüàw2÷œu'çñ.p9ô­‚Üt%³Gp–à)‚W Þ&ø3Á¶»˜/<Dp¹àå‚7 þà.Î˽wÏVe.øà«ØÿíjâåÐÝø‡=ï‚þšàŸ7½—ýEà{¡/üœðÔ[C|ôN‚‡¬aÏœûˆŸ‡~·àWºOŒ?¿­Æ=Áãßð�ûïƒîzõkÀû¡¯üŠàƒÂhñ!U¶1Ç<Äž.ë‰CO\±ž=Í&þºWð˜‡Ùó¸1ö?Üàö$‚Ï‚>Rè›Á-¡ï|Rx¾yc túM‹/\ð$û3Ÿ!}ºàu‚ßy†ý=7…ž%øšìiý,ñD豂sÏ}–ý-žC_€î<Rð•ωvò±z/¬¿à™‚ïý^$^ý"Á—½Èžà;¡/¸ÕK̽Á+ÁÃ_â°?m!~F½Ç´•9App+ûënG{†~¡àlÁÓ·³X½õ£Ð“^!Vï:EŒ àÏ ×ÝÁÜ]ðÀìüô×{•=3ÀßA_!øáÉ|ø(ô Ák^cw'±zwi¨àù;Ùó"øw趽ΞbÅx—a‘àÂsö.â:Ђ v±g5¸ô-<·¾A¬Þ«zVð!ÁÎ7ÙÕnâ®Ð¼_pÝ·Ù?­Þcü¶àãÂ?ébŸÊ»à‚?'‚½Ãag¾K<ú‚·>ð®‹ö§C/x¹à—ö°?ê=â,èÝ<ó=öÇî%Vïp|£àG÷²¿è}â|è‹¿ øsð8p“}Äêðxp)¸|?x‚ª#ðDðà"ðy«wÄÒÀÅà¹àðàÉàƒà+ÀgH\ î ¢Þk�g€§ ^Î?'ø€à?ÀYàV1' Î_¦Þ¼Fðfð(ð'‚ëígî ­Þ}|X½7±Lð+‚¿7ø˜9Zð`p.¸DðJðXð ‚ Žú„8Üœ¯â<¯¼<NÅ)¸ù§ÌôǺ'÷©º'8àèÙ¶Éð¼¬Þùoo¿>Þ>÷�ñ«àà×À3À;Õ½Hðëjm�Þnú±z—¶/øMðà·À÷€wƒß«yäGð;àŸ«÷m3À{ÀóÀïׂÕ\ó ø}õ®+x¸þAâÀƒÜ7‡ƒÕ|4Mè«ÀAYè¾ þ\½c+xÅìùüµZç>ïKö̆¾JðkÂsÖWÄ?Bw ù{¦}Mü§ªSÁ[¾fÏÇßG¡/ŸÜé{¦âya™à-ÂÓä[âfÐ]‚G~Ëž‡Àç¨wŸžÌïˆ[CŸ&øÁïØ3ÿ0q'¥ þð0{üß_¤ÆjÁ×~Ïž‹Ž{¡\q„=€Õ;Âm~`}!8A¥AðÛ“û#qèó?"øíÙ?ö(qšJ§à§žóó#à±àôcÌW ¾KðKà<ðÏÇ8 Ÿçñ¿ÖÏÌ.ÁYà)jÜáŽç5°z/øÁgýÊž{Áתuˆàý‚ ÿô/‚~›àMÿýâñRèÝ9!Êù7â»U ~@ð+¿±ÿý߉שôns’9þ$û¯üƒx#ô‚· þòö_ðÆXèI‚K/þ‹ýÚ"iLƒ¾Sð¯`õNnψHíO‰$þúÁ·ƒ¿QïÛF²¿¼±zw©àMàŸÀGk±}mâßT<‚ÿÜ¡ñŸj®…w3kƒëÕeî$8Yð8Á× ¾¿.§'½q”z×Uð­`;øUpKðõ™[5`¾|zÏ·!óÍ‚74äó¶hDÜz\#Ö‚ÝÐ#¢XŸ�öC_ x£ð´·_¬Þá\bgO-qôó§9Øóx z'·1stcö<΄þ‰`‡“={À#¡|nöhJ\ ½ÎY̾³Øs²ñèíÏfÎ8[”gsâTXÁ¹ÍÙÓ©ñRÕNOnÁžßÎ!^­ÒÙ’¹oKöì>—øè? nÝŠ=cÀO@Ÿ-øá9 ~zÃÖ¬_ ÞýAÁûŸþimˆ·B¿SðÎ6ìiv±zÛ%xÄyìùüô_·oËÜGpŽàYm9žsÛaœîœ#xv;öþPõëöÌöì‰ì@ü¥êv`O»ó‰„ž$¸ü|ö´íH¬Þ ï%xœàk;²ÿø$ôú0»g^Àþ?Áµ±~>¿s¦àDŸíŒ1ú¥‚Ç ¾lWï8wæ°µ/$nªÞ<Dpù…ìÿ�Üúo‚;uaNéÂþßÁíÔuVW澂Çwe«‹ˆ»@<Nðõ‰~îýÁçvcÏOà�tgwæXÁû³¿Câè}O¼¸‡(ÞÄý w<PðÄžìoMœ½‡àaÑìiâ"½»àlðpU.öw«ë¸?%xŸ›ý^bõ®}[Áý÷²ÿQ°zï~·à“ÂSC|•z÷\ðK1ìÉöß}ªàû|ì¹ØO¼úhÁ ýìi ¾WÕ£àñ‚‚‡à½×Ö±Ì)àñê}aðI|ZÿÁÏ Þþü‹àvqÌ—‚ÿç^�þ üØÖ„øMÁ'·Š'Ž�Ç .�G‚¯ü(¸–ŠSð pmð93'‚ë€ó/×?!ø3p=pD/æn‚ƒëƒK¯/?/ø+ðpÃK˜éÞ+0t|cvH-[ <¯á©kÀ;ÕûYà×Áƒww€ß�ï¿©ÞÕ¿¥Þ1¼”x7¸ømðàwÀ÷ƒß ÞnÑ›ø=ðð^ð"ðûà×ÀûÔ{^ Ä€ýàÕ{vàÀ3ÀûÁw?VïH‚? ®Û‡ø�ø"ðgàtðçàRðAðmà/À/÷á~t|zT"ëG%1+Æ{F× ~PxÆ'£ @¿ZðƒÉÂÓ—Ø©<‚ìËžUýˆÏ…þ‚à¯ú±gâŽÐ|~ {î�wþ¬à/„çÎÄ.èÏþd�{nH ýqÁûÀ/€¿v¤¿¾¼<�¼o4O¡Ûÿ¼TÕþw•éIå4ôŒñÜVÙ?v°òïöÖ²Ý1˜ýSÒˆ/¼1Mä+øIðã‚ßOgÏÄ!ÄÏ ~n{Ò2/p©àÕì‰Ë$~<Bð5™ìi›…~ ¾Tð„,ö¬ïï‘ÍžðÛàÁ˅笡è¿`Ÿàü¡ìùü8ò2æèËØsÎ0bõk¿à\°ú®õíà¯Áo€¿×Ž>îþ\þüàp>WïÄê×ãßVïÏ>)øC°z—ö¤à.—÷÷\ Nß.xXý.Æ!ÁôÇj‡ÍGªv¸%ßXÿŒ$úÎÿØ‘œþÀêÛ¼? n:Š= Àê{é÷ Þ ^ þ@°LÃo£TŠÓêØzŽæ8[å«wZcçæ°gábõ¾ê‚?Þæ¹ÄcÀ1¹¬Ëå¾9_迃ǃÛeÎËž×Àê;Ò?n—ÇžÕ7u÷¶ç³ç°zïu‹àß„§Ý8bõ~k?ÁÓlj±·€xøÁÿ.sÁxbõ}é‚ßÏž~…Äêw^ʯ/ ¾C¥m‡Vß^-ømá¹h"ñ=à!‚çMdÏ`Õ:v×€¯7*"¾ìÿ <|?øî"Žs?x8r³g{®�«ï`ß.øUáñóx›UÌúÍàgÀë¿#<sKˆÕïݬüi {æO&Þ~DðA°úÎCý+˜`õ‡ X}ÛáV°úžÃó‚€ÏUï×—2g€Õw¦ ^n ÞVß|ø ¬¾óСŒ¸-8Ü®Ê8#yZ™9÷EÛlÃü_çÛ•/Scø×*qÎ/çr; ž¥ÞC© ž nVÁê½ðUà1àyàŠ Ž30…8 ÏL¦ÓÁÀCÀWOaÿ/`õ¬¯s*êW='?•=ïM#îŠçd~™ÆºÌ×}Ó#mm¹uèwm6ÜÛ`Ò_6™J¿õ0ߦ~û†Ž%™Å“ê4ÒœWÇ.ôÆšƒuÚi.¨s±æ u²4Õ)a§jž\çJÍ%uf }®æ‰u®:F»§S~ÇÖyõ¯¿þúyÉ«SËôlåpÀø»ÔäŽ&F™lªã|Øæ¬;ƒÂnÒa“ æ¼o¯ãÔüR¶šwÔé%âL2þ6Aq¾VgŒŽó5äu"„^KèõEœ-¿ÝçÄús8û"Ήê91“#u؉õ9þ¢úB¯«¹¸>çerý6šËêÓùF#Î"Oå2ì$ÒF<i{£Ác[mæ ýh’íÍ|ÞV ¹¼Ýà,Íï4à4ìmÐYó \šl¯ù—Éšk7LÓ\·á¡Ò\§ááÉÓìl8^x& ½Ló9 gj>Öà*ÍG\-tn“Ç,ž¥B§Ynö j«µR[%}³ñ×Jÿãïøj<“„ÇJ•ÿ*ãï-(ÿ·¢Ò±ÚÆ[QTækM¦¶ñˆÁ« ¶*5É`›æ·£½‘à¦ÂÓLó»Q4¿ÕQóûQ4¿u¡Ð/Ò¼7ª‡Ð¹~?ˆòjþ0ŠúÔóHÿÛHçkf^¬ö¼ÙƨLÒE™ïE™´Œ\m”ÉÈû9xÐü¤É)ýÎuäQçm©¯¿’lm"¹ï´Œä¾Ù:²‘ÐB§Í kü|VÇéq¶ÔŸ“óëì­9Ι®9Þ™)ôáB©¹—s¬æK4÷v^¡9Á9Ms¢“ÇÕ$çrÍÉÎ{„¾Vórçc¢|F=jå¥}ä Ús~äÍD¾¡ùÂÈ÷5w‰ü@”ÉUr™|¦=+œ?kö8eeþP#]Î(ÍEγ5W8;h¾Áy‘汚:/ú¥š8ûj^äLþ4Í7934/vf Ï0v´ðçj^îÌ×ë\¢9Þ¹Lè+…¾Zsœó^¡? ¹—s½æÎ š:·iNs¾®9Ýù•æ!Î5g8iÎtþ¦9Ûi‹´x¨³æË…ÞUèÝ4tÆhåì¥y´³¿æ1Î[…~»æùÎB¿Ks‰ó~ÍÅ·…þŒæ[5/p¼¦ù&Ç.¡ïú>ÍKŸi^êøAó-«Ã'ÙÖ:œš—9šj¾ÝÑFórG{Íw8:j¾ËÑUóJG7¡÷Ô|·Ã§y•£¯æÕŽÍ÷8i~À‘®ùAÇeš×9Æk^ï(Ñü°£Bó#Žiš7:æj~Òq½æ Ž[4?í¸]óSŽ"ìJÍSœižê|JèÏkžæ|Aó ç+š§;wi¾Ò¹OóLçAÍóßkkÿYsÐþ›æ%QÍk[¼8ªUmÖÛi¾9êBÍ—×qiYg¨Ð‡ }„æ[¢Fj¾5*WómQWh^U&ô)šoºRóò¨¹šWD]¯ùΨ›5ßu»æ»£Vi^u¯æÕQÏh.°¿ y”ýeÍÏÚwk~Î~@ssÇ7Âÿ­æyΣšç:i¾Êù³Ðþ?„ÇV‡õºšç;šor4×t&hÞëHÓü¾#[óŽaš?tä=(ôÍ“5옯ùÇbá¹Eó§ŽåB_¡9»ÎjÍ›ìoh~Ѿ—Ó`ÿ@ó öO…þ§Íþ“æýö_9mö:uuÚìê²îz3ÍŸÚÏÕ|À~‰æƒQéÌö‘š±hŽr\¡Ù¹±cžÐ¯Ñìp,Ôìt,þÛ47q,úÂð¬Õ<ʾNó&Çãš_p¼¥ùEÇAÍ›ßý;¡ѼÅq\óVÇÊzosܧùUÇ&Í78¶j~Ãñ¶æ/ïiþÒñ•æ¯ß ý{¡ÿ¨ùÇ/š9Õ·ø[Ç2Íï8ÖiÞîxBóÇSBFèÏ ýͯ8¶ ÏëBSè{4':›5°x´³«æ,{¬æÃŽK4ïè§ùˆ#[ó;ŽG„þ¸æì›8¬}‹Ð_Ñü£ýsÍ'ìg5´ø7{+ÍØÛkþÝÞQè]4ÿiOÔ\Û‘Ïž¨Í‘Ž"͵ÅÂ3™ã‰š£ù¯¨%š#ì·q<öåÂs§f›ý.á_#ü÷ ýáHxÖÜÀþ˜æÆöMšÏ¶¿ª¹¹ý-Í-ìïý}¡¨ù{óF·´·lÄz[Í­ì5·¶Çj>5Có(û\Í;×iÎvžeñG'Í]ìÅš»Úghîn¿Ns7û B_ ô›„~³æžöUš{Øïú}šýöG5»ì4{ìÏÏsÂó‚Ð7 ÿ«Âóºð¼©9Þþ®æ‹ík¾Ôþ½ÐúÏBÿEó%öv‹{Ù£ì¬7µsØš{Û/ÕÜß>@s²=Us?{¦æ$ûP¡לf­yˆ½@s†½Hè%š3í¥š³ìs5oq¼¢y‡ãwáiè`Ý©ùÇõš8Ž;Ø_·1û—6æøŸhÌž9ž¡N‹[Û 57²§¹¡ý¨f£d›p<špضš¶/Óü‹}•ðla·inbU³Óþ¦æ¦ö„þ‰Ðjna?"⿹)ë÷iβûβÙê_IûçDÒ´Ö ìÅeG;ƒçÚbœ?н ÚCèžpÀÚÒƒ§·ñ·ôKpC}ˆÉõ„‡ö .‡žÖÚ+8`ü].ø±j˜ö‚ˆç|½Bñlž÷O#_t½¯Ò¹À)â±®å)º¦®)ït­­òžŠj—â\©ÎÃÆ¹®4ùƒçÃ3ÈIe~3ô4ç­"=t}<C¬K@è?ŠôÐu´*Ãì4X×Ñä¡ëèuðŒñÐuñS¦.ëeˆ¡o6u:ÕN“)ÊËÃÄ3Dœk؆-ÂÞT ßZMúož§`UÎSU¿SCÊSúéÚ¿¦ú%Ï?Q¿wU“¯»Dzî?ïAœ7:öés-pP=~^è ô‚g‘c8/íW‡ç&‡<ï.ÿÞj<û„‡ö.þ€g).ì3é\·8v‹sÑžFË™ä¹ £¿�¼,$N«2(ÎôJì‚ÿA‡ì´ç úÅÃ!ñTˆ°ÓÓ>ƒ:ï“!þë…ç±J\S›‘~ÚèƒøGÛ©üÓQ9ö—E9g$<‹£d_£}ƒèK¢jUÒ­ø[U㡽µG}«Ø£¾-ŠÇÛeQu…?WÄùÂi0]Ë×”/ºÆWeõ¬]¦m·ˆçÁßÖ®¹l ?]³ÿÝvNaõaÒ+f*]¶« ëðyNÁ5äQi{m~öLÅäWéßãõBûª=¿’Îlq^Ú‹PmøãÏ|á¹­ÏráY!xu%®©®É®®e<TãùTxhâœë@”,k?<´Ÿ î~o—÷\ „‡öTûq„ÔãáYW‰ÿn[¢}5¶¿èØ%tÚ7¸yÙ‚°«L–顽‚u¦.ë¥^=N[KƒŸž-aü¹Â¿²ÞZ‰ÿn~ßñÐEMmž<gÒæÉ¯ÊóKÇâ¼´¢Êó¤s•ɲ<ißc©Ë4Ô®Ïi¦/=%<[Âø‡ ÿ¢jx]5L{)·l)l¸¶DºZ/í@Y]n²Ló‡" ÖK8®©¾ÈSÓúœ<áÖçò\]+ñ?±.¢x­‹H·^%ýLÖœ2ͱƒk'Énì"ý „í‚+ØýÛÕþ˜ö;èÝàWszw;¥ÿôà:³ÈßÓ¾Þð;Á.û«ºó„Œ·t®V𠱯5<g‘'ÃzdF{Üг6þ,ûêJy,<ÃM®W)Õ³ªÄo•Û%ÿ ×Ô¶És&cùÃE—èvx$t^6ô±(«#ŽÂ?Z·Ã#8o‘éÙox.O¾È×Ô3 ;C„½¯~ä|&åóx5åóx5åó¸8צÓäšúÔ¦júéh“‘!síÓªñç¨ÍF<×Àó'ÖÏËÀ6œk5Ê9¾IœËÚ×¥´ýñÐÞïzèÍí…Nû«ªNÕŽÛN“eúŠ4Xû«Ä´ªü­í<G´éwý…F5<÷üwç5Ú³ 7¯‘¾q–šËxœÙáxµ’çx˜ùŽtµVßR¿kDšŸ®†­}ãp\ÓBžb.£xÂíÏÈôt:ÿݺ °áê‚ôšÖ«äÙ:Ké•ô½³ª®K{‹4ÿ ®©ï“'\ß'ý¿5ŸÒ¹>CÞ“Bú©µ7Ny¡½îšæ\ÚWýWÍ¡—_YuÞ\,â|åoðßm36\›!½¦6#Óð»½æuìïÂoíá‡ãšÚyµ Òÿ[mƒÎUS½“gc k-òÔ´Ö"O¸µ–,7ç?È·-QØpmÉé¨y.pêrPíg¸ðKϦ.ë=Z¤€.«Ð¶—%<×ÿ ®iî Ï™¬¯®w„__‘^Óµ0yNÌRzD%}שׁ׼ËD^¶VÃt¿,ܺŽôpë^ÒÏdíz\œËº÷w¹¦ñ<áÆÒÿ[㫦ñ<5ä©i| O¸ñA–›³3‘^aÆ#Û[w6¡×”¶ù­>›RóE<KÿAþ»c… 7v-m\󨵴qÍc×ÒÆáÇ.™þ'NÁ5ÍÅä9fý&ãÙqš\SÿÚQMÿÚñ_ì_;N£‘§¦u—ÌûÞÓè{O£Íï­¦?îç:PM¿°9Ùs®3|<ÏÐkšãÈs&sùÃÍq¤×4Ç‘'Ü'Ó\ø7¸¦¶Mžpm›ôp{¤ÿݽ «Ú[kÑÞZÛ7Tòs½ôÌTºLϵ"¿+*ñ`q®á³x?Á û±Ój“­Còþ±ˆç»Ó`ë‹ÿ[c«¦1<5õqòÔÔÇÉ®ÿʼ·ý®‘þwÛXÛ&5·±¶Mjnm›„o½EúG4±Æ«Ð¶žeƒÃ•Ɇÿ L(lMûiD¶‚ÿ[m{Ûi´ím§Ñ¶·FÛÞÖ$ü¼#óþêßàpõøêP6Ü^+=ÿ¦æ—¨ßU&ËóÑyl’Ç“"ÍMšZ÷•gKfSöß| ®©’§¦>Hžp}Pžë¾Sp¸²"½¦²"š‹[„œwwSëz3´L>çµ^¸ Ç5í‡K«³jN'yÂ¥³•ˆ§k%®) Òï;Mþo t®šÆòÔ4&§¦1<áÆ™÷øÿAîhríÙôŽ­+òÀ›³Ílz¯6ïØZï’Nþ$óJÓ­'ëÐÃÚó/g”öÜïäww¶Ðü¤“ßÞà ùigšÐ‡h~Ê™%<—i~F¼W»Ñy¹æ-Î<Í›ã4osŽú¡ó;ã/;ùñíÎ9šßqnÖü®ó]Í{œi>àüZó×Îcš9Ñüów¡ÿ©ù[gœ~ó;gŠæÃÎ!šwމàz™¼Í*ó¸HzVÖª‹iZÿɹ\‡=æ|XóïÎôHާ×ËϨHާ·ÖwŽþdá ŠðFòy‹5owž£ßõ;äÜ«y‡sVm‹_u.ÔüšóVÍŸ;×kþÌù„æƒÎ„¾Uè;4ow¾£ùóCÍÇœŸkÞë<¡ù=ç_šßw6¨Ã:¿÷¾³™æ}ÎüŽ˜ó|Íý=5§8|šSIšÛ925·wŒÐÜÁ‘«¹x?®ƒ£PsGÇš;9¦h¾À1]ó…Ž™Â3WèWkîâX¢ù"Çš{8žÐìq¼¨9ÆÁï¦ùÓ4ûWjŽu\§9Îq£Ð }‰æÇjÍ}k„þ/¡?¨¹­cƒæ9Žç4Ïul×ü¬ó=ÍÏ9÷i~Þ¹_èŸý3Í›œ?j6ú©æΆõ,Þîl\=š{9^ž‹ë³'GóÎyš?tÎúµB_XŸã¼]sO‡]¿“íhÜ€õ&Bo¡y°£½æ4GgÍC.¡{„îz¼Ðù]³lG¢Ðû ½¿æ©õ34O¯?Bó•õÇi昤y„£DóHÇ\Í¥Ž«5—9h.wܤ¹Â±Lè+…¾F³Ç±Ys/GgýîÕvç%š9‡4d¾ðð{g'œ×Üe6ÍÝÑÝÔZâþÐç¾lÖZbÞˆÿ©gtiîëý™}!š×2fSØÍ¸Wné4¯©õÕvøwš,×WÖwK˜ÿ_Æy¿9ï1ÿ!ĹÓd™æz⽉8Ácªá%áˇ榚ʇ<* Çœ|]y,$Í+½Ç¡ÖfÇœ¼6;æÜ,Ê<ÇÃÄsLÄi­ßÔy‡‹8-ŸHëÚá˜3¢’nÅ“~†Lóo¸²#<Áj<4çÖTž4×T>ä W>¤«ý±c!ím¦HÛm§àšêQúï=tÞ{uto5u´Uœk¤µMÏ×Âc­[Âq¸þKz¸þKúDèÛñÜ‹ºæ í¿äY7“û¸¥wç«e]G+Ï–0þÅ¿÷49ܘ@ººN<ääëÄC!iÞ«Ó|($ì"þ:͇DšCý—Öfÿ¬Sp¸2'=\™Ë°ë+q¸xÞ©&Òkª»wDüž‚Õ3é5•³ŒçóJ\S!OMý…<áú‹<׉Sð”Oÿ{´Vz;Ü˾Ü÷—‚;„Ü7¤õð](ŸŽ¸¢Þ©¼�÷AÖÁß)äÝ(Z«ñðBÜûPc`—{Ž3Å{sOÃOkcU×Ü—Tuá Ùg£õð:S—õø–8×u¬6ïÏχú{ÖeA5¼Z0­Ï¤|a¼A0­“Ãõ…÷„‡ÖÀáÚ-é5­þñXëçp\ÓüEëípé$=\Ÿ%½¦>KµÇµ=¤Í“¾wVÕqµHsö)¸¦ušôœ‚Õ9é5:_‡BòU ó:öVˆó>| ®©®¥ÿ¥Jn|`ýôÆòŸIû'Mý<jüì%žIèåx¹’G­‹z…ô_ÒÕº¨WÈø°_äýÇJ<Ø<ýBžmpÕ·Æá^!÷j]⽕‹ÿA×§H×§H¯©OÉøsNÁáÚ6é5µmϼJ®Ý^ÿÌÚùϤ‘¿¦6FžšÚyjj·WÓ6žå@¿½nfÕ¶º_xì jæö§àgP>ƒ‘Îmà4¤SÝëâ×&íõ{SëËzÌqNL×égR_ä?“ú"MõEžšÖ�äQc¬'¤.Hß;«ê\ÿ¦ÈãÕ0íKÔÔNÈSS;!O¸vÒY¼qÉ?ÈáÆÒÃ!¤×4†'ܼ,Ï;ä49Ü8CzMã̆áçPþ)8ÜXÄúéµmòŸIÛ&Mm;¿aÍm;¿aø¶]$ò8ç?äpmfN5mfNÚ×T2þëΚÜÑäÝF.´½[çßS"œß€{Õ¯?5ÂyÜ»þ…“?ŸÊd—ÍÙÀäÉ×›©y˜ñ¯õýìãï\“oüà7`N¼ZðQÁ­#™ ®|·à‚ß|\p÷Z"NÁ/¼VðvÁ¼ÕøwÈQ›íɺØlwÔVÜÊ(Í“õ߯¾OLß÷Ì¡{”=Pž)sèewMe£Mæ{ŽÖW‰ó÷½Âã³ñ÷«l­4'ÚÎÓ<À­y -Fó Û%šÇØøûÕy6¾9Ö6XèüàëŒ#|®rÍ7Øø¿‹lW Ï,Í7›¿ê@<Û¶Dx–‰xVˆxîžUš‹Í_HPñ<¨¹Ôü5¥?¡yœíi¡?¯y‚í%¡¿¬¹—ù 0Ä—à—^JçP_îi£kºs¨ÏFÛh\ºì²Ñ˜v<nõ÷{¡w·É~M…ÞÏVWèV0þ–Wã™&<³ªñ,žeÕxVϪj<k…çUÁÖ·”;š¼íy™-ý ›sÚðm6þ¦·U£ÄwÚj ½®Ð¹m?'Úç‹Fš,ÞjKѼS´É·E›|×6TèüÝõOô¯%ÙÚøþø—º?'Ù¾Ò÷’l_‹övP´·¯õï¾Nè ýIÍÇmOi~ĶIó.ÑÆ6™¿D¼Ù¶ßø÷�êâv´«Càå!õbý9`üýÆäŽ&ÿŒº¨ÑûM«.jEP]Ô«˜Æö&Ó€m2?ÛPÛšÁ\Gõ#xÌiÁcN½ˆ&ÂÓLxø‰6ü½ñv­5û#:jîÑIè"~^¢eÕé%s©Õ‰ µVÊ\*ŸZ”èl“É;Ædêw%BW†òÛõijæRNŽØl”á(Ÿ’êïOšLå¹Ùd.“É¢Ü&Gp{®ž)üíú"ïS":k^ÑCxøÙ’{#üšWG\,tÃïà1ü߉Èþl¡Óü|÷—#ø»î[#rDØ\Ís#ò5Ï‹˜¯ùêˆ5ßÁ¿5°2âV¡Ó/í¼z,ë"ú£Úÿ4V¨1vÚ¡c¢N÷¡NEȵœUžŒ¿ÿƒx¨üÕx¸6¤Í$‹ø³«ñ ž\ÁùÿAz–ŠxÖ†áŽ&…vûgÄä·lÎÈ«¨}ž@[UãÀoh‡M¡ÿ޾ø$úl4øˆstý)ÚóŸüûÄï 4<[sëÈs„οÑ,’—¤m䥚ÛEöÑÜ>²¿æó#ylïÉ¿A`¤q ÷UT>'P>ªïÿ†2L‡þ;Ò<|\ þe>Ü,RÎÅVÚ¨<­´QyÒóT ¯¢òŒŽ<`”çz”ÏE‘\žÝð{ /CïÉåÙ#’˳§(“hë´´~¿ãYW0žH+.Žä±41ò|Í}#yLè§¹doÍ"ûjˆrÞrPÏ¿}�¾e¢Ê³Òvz÷H.Ï‘\ž=#åµ¹õ‡Êp (CâŸQ†i‘Ñ»mÎÆó¨L£ Û˜Ìccš(Ÿ4Q&é(“®ó°?ZS Ö~:Íyt®‘cv[sßpœkÐ<Å<÷ G} 3už³Fˆ4ŒiÈéÌä¹otdái&<\_"yî›ÉsßœHÿK#; =CÄÃs_Aäh›5÷]ÉsßðHžû†#ýcL¦ÿ)ŸÇ:ýÙlã?l6½nï(˜Æª=»ùð€Á×Ï‹Ôüÿ®ÕŸÿäð)´ÕN(Ï·QGˆµ±Õˆ»ˆµq'[#¡sÛ Øøw‹2mçÝl]5÷׃Ý+‹˜,ö‰kÃ^6~–õ¯ zÛòEØéšÙøw²Òl 4§‹5vš×6Z¨µnçj׺Öw_;šü1Ê0Ç6÷m«¿ÂuÇÑyŠ©Ïž4™ú{ƒ«‰GãY¦&sYåèsÑub¡7Ìs_ÐÖFèç ûòxÿÞP¡(ÿ F X<Q”ÿ$qí3ÁÆk¼É6ž7¯×é×ØÒ…Îk¹U¶Ñšï5RdñÛx¡—ˆxøúýZq¾F\§O¶-Ô|…í&¡ßb³Ö6¹hêÇ¢Õ5 ×2®VLƒ‘ëjÇFá:ºôÑøi&W7¿T^󌸚ÚÀöèwlÎRÔéƒ6ž£×ÙxÍóçèõ6«ÖñÓ5Z]¡7:ÿ¢oD:´ñ¼©®Õ:D] ªys½ŽŸÒÜÍdê7#ß±é½B[ùôÉÁ²y¶žãK&{–OÍ-*ê9¡8·ç؊¢üžÅ¹å…S‚=‹ ÇŽWسŠ)ôpYi^O+º‚¼<wN^ɤɅEÁü¶Ââò¸ò £»¹ºô*Å?ñÝÝ.¯ßðø¼xþx[ÞøÜRXÝÊêîïrGŠJŠ ÚYyÔQOõU—óu¯ â5"”Œÿóøõó4UǨÀ1§8$¡¾.½z—ù¼ñ¥àøhWt¸?ñÑþpd„U’ä1û«¤ËUM„eãKJËuŠÃåC£,=n¿/OÿÕÖ*çab©v|11žîlº]Ñd Àgp|w—; jRGÉ^—ðº(RwŒ帢’\³á¸uËñGmù%c‹‚êG Ð!”–<îÕÇ]n2P³, NÓr•[( æÅÁÖÍhWîøÂI¹æÿ{Ü$p@‘,£z3<ê¼ÖQyj?…%ŽWI_�&7ò5¥¤0_©F1«l99ÔË ‹s¦äæ–©S»âíŠbËéSRR¤D³»»|FŸË+ªÈö4ºfñòÿÊsK ‚å†fpŽÁFH7õL´oˆe–ê†êQjîØÂ¸,C4Ê;Ø·oJNß”ŒÌ¬œ„>)qÑÝèÿ3‡gs)ÌJJLHMsãÿRLŸÿ›”Ü7!;U)®nñ:n:¡QW8ƒ­gEY) ,=‘¤g¡/àë>9¯{Þô‚©…Å===¼=¼=­••ççÇÙ™\^š_8n\•ÇÌ«‹Ñ.gMMeÇÛÅ6•Zž)z R9SBh�3ré¢ðe.7ŽCHÕ*r‹Œ–;)h´„¡¦aÔ.)Á !X$SYœ‘›êÉE§éŠh»ù¼ª9éÓ•:eœŽÚÚáì1f§ 4 ·OBRNÖðôd£ðÌ2&…ŠÐmaYynyE"ó!2£2ÉG¡ ú­Ü“;¯pÄ�¬Fc+s{©P*¯ÅF+(‹C³ RŒ|¹¢Í¹Ô¿^+g¥ª8p¤[¬¤±Ó˃VTÆ0@Ò¸¢\»ËÍERš;)£.OÿWá-£ÎŠ©ÒVaþ¿Õ[Ç¡F\ÖñüÜò\$Á(×ÜRŒàø÷”C¸Ñ =ñèV*"£Q!ŸêmFì&Ò:!×è’:Á~•`·YÂE%e¥ªv]ª˜](ç@yiî¤ÉhÑœ:LsúÌ®ho<U™Ìò pÑUã€Ù²ÜF 3Óf–dEY°4G—ÏÐcÝ!%’:·j|î˜*©«’’ÊIð™IðŸa¬‚ M‡*%wÕRª)³-z\gŽS¯fÄ€I ™I%ÅÆœV,Îæ›ÝÇ‹QÆK]Ü+DêpæÒ)¹EAë´f25¯|< d§ Î Z¯¼Œá Sk1J‹îŸËÑg ^™¬©~P>ZÀêû™¬ÅZ#$ù|^•Ÿhˆ~e´D5–úŒñ$5-Á îR£)5÷²òÒŠ¼ò Ž9Ô5Æ ZæÎ‹³u ~Z2×-`p|h°2æ ,&l0:›‘o„3J­Ì[%U¹å0'´BÆ„ é¯&$Ó(„ôÑÚ¡JÈ�/ªœÓ é2¶štN£:2@ÍÍW%¨QwÔ”Œ%a˜³Zac« ëª&ìä#p°©—UI²lݕªuBºÂ„TÃuØæòAÝáRŒe_øÓúJ†÷T^Íj±>1ç`ùÕBmi"&5>¨¶y”å²ð²Ù0ÃÚÃéf£ ë§› "¬?œnUd•ª–ªÈfVÑEñZÇj¾,4Gy\hÒÂÎZÐM*,.˜êq÷,›^Ö½´¤¤¼'^ÚM/3¯(Ç«àòÂIA«¶syçébê>¯©»­¡ŠTS£…°å”˜ËÃs„5³›‘ŸœüàSóYã&K~ØŒ3ä—˜ZÀrYŠ…Ír&æ[‰Ž¶†\¡¹TêŒq=gRI¾•fst5Fv)š9¡5¿q U,·xyµ+e•%å©L¯ƒc°š1íRU93ÚÆM.)ÓEê Xej”«±–j̰y%ÅùeV D[ev¦UM+Xªiú×è»>¬Á=¾²rª8ºšY Å(è8”¯¹7$*¨8U<æ2Ü‹‹ ‹'Æ¡rh¸Pb…ÑË•ˆ6¥-ÅúL©”Ïj®6 Q]ĈU«!æRc‹CK³–”¡ºÝ^SÍ“jŒÏüÌ»+åØ_%DZárìùgsì—ã˜4ǨäBÌJ>ê eÞÀÿÁzŽîf.i¦R®ÍœTÎu &×·yÍp&=b\^qy‘ÑÎ$La .õË«ôÆ�_çŒÓ]=`Ž’1¤çÓê8‡Ú_�5áDç–——Ž5¯Îv §y¥ÁÜrΔÖsóò‚ee¦®V³Ð§–j»ºTÅJ °Êº8woÈ¥;&Ö¼¢6çŽDS›±[ˆQˆÒ«.YÿÃé¶Ú†Ñ 8½hVÒ¦V*HZÆxÿ7 Ò_µ =è¦FAzÿ„¤–K2€{ÿJÒ¯JÒLÛ™4÷Šâ²òü3ì#“KK(ÍgÊ8ѯŒ@SЋ\Ö¦™—¦ýò€]V3æó )»õ%€–ì±V!²×šý±ú³Ô«3W„È>‘ s~vYÛ=>ef9`]àQ>Š‚¹eœÄØ”W:h­]\"$§×¼D È â¨[dGäÔ»<2WUŽzEæÔA΋+Fæ±ÊQŸÌê¸ÌøC³z0’Óq¡Y‰ ÉhèAséË9#nWH6+tË\Ž É†Û’ÉJ½V+İïR›9:ÆI¹Ó´î“±É~+¦3í%VW:ÝíÚÜÒ,è Š+òÄž¶Ëkö7­… KÆVŒ£½PZºsÔVœGm¨ò]NžÚÕõ9csË‚¦Ó“rh·Ñ4©áȘKŠ‚¦b 9´)L'5UµØÉ1cì²DµÖÉ)Ÿ4yœ¿ÔiÌÕN¼­oJ*v[Õ¾2Õ´*fÙ±}§sÑ”_4É8X’w†MF‘ç—L¥ñM­E‹>ãȰ¹žtYZFÅà[—¥ î“––ªT·Õ¼ÓCd”f÷®êpjèñë¸5ŸÒŒ«œ>ó’•æ×}2eŒÅV…3b磱pØ&ﲺ(ö²L‘·²Ò¥l&Þå5O�§Ée&^m#‘”�>î×Çmé)ƒ­X:Ë©BÕj:§ÖmfÁÜFò©ÙàÖCOMÜÏÔͤ»Í‚7.tÓEºÍ”»­”#¸4øµÁ–ž84-ÅÒÍÄ»­2§=¡‰jÇÕˆA8=.í´é\zt+ÉfÍcTéBôªÐž•~–8ìӇϴ9cOìŸA1ÅZ#N[fÿ´Œ,¥¹­!Þ*ÏXsš6J1‘ƒz­ éBŒQ½ÞP³Yôqã*Š8–qWóh¶HD¬ÕNÓ¥ªÚt¬yÓdz9•.½®O—ªÇ c¶:œ9Â<ÆéM·ê.VÍœ˜@R²_ËP±=Lz@ë—‰’¥xÐò„ê63`¶èX³áó¸[· ÕÃêàËÒ3³¬Ø¼¬‘°#caÙ'퉽Y(jÞ­Ô £'ZŽYµvu DwK]—‚ÇÌÇk•â³Î£ÚXô±8˜KÀãg9UÈ«±ÒœhEí5såµêÄë¶²O ÐÁ½í£à¬{YÏýÃÌ]|JÙ§å,k¬4T3^jY:ef6¼±VÊp˜ƒÅDkƒ‘$.ÆÅrz–NhŒ;ÄͺGê¦æ•Qˆ#½‰"3_1~3­1³‘hщcÌjðE[ÍÍ8ÎýÕg&Þç6]¨Âà¤T3Ï>wÃÝL±/Æ g$†X|ÚbKM´úµÏš§(¬ZC=y÷Óº9Sa*2Í ÷G[‹Ì$â€ËZ[¦W aVˆßc&وœ¨„ÉÌ—ßÊ—ßeŽzÂcfÌï²Æ>q̯Ã#ì`S4[– É Ö3{~s¥¡¢y6ïËí!àÒþ¾¢Žn.¥¾œ–€Gõ™Ì[´¢-/£+]óv ;Z\gZ·þÜÑ¡SnÔ¹£»é ¬t©zõ‚ú%èŽæ+Ìt!ú´3[F Ÿ'H‘<5£"-=VÛCdk•æ‰1�ú€‹rN::š;Z¯ÖT"…îát†ðv‘+ q †OAYG|Ö„Ÿ^逵0²JC „ξâH¬žmÙýRûò!¾dL¯|Ä¥§z[¥#º–Ó+ pGëU_À8„%ÆJdfʈäœ,Sõ©€.Š3ä€ßŒ3£éÈ qÄšAi8 9‚ÉÒ­nþuµ,¨{NæGëÄ•yô¹Q;Xn¸Õ½95̤‡È1æéÌG5|h6ú¨ÞI—j@‡Qµ©Äê+D÷šyòšë/¿™#}Ü­Ã…êN5B}ÀË“33s%d4G /ÌUòÞ–Ó/Ûž³ ûp¿3)·<ׇšV—É$¸CžZ"ÅòØ)Þ8ĺ‹èW+T}ÇçSÉ0Rã¼¶Œä¾â€Ùèc­Y.€å$b]Ú`d yprFJ¢‘¿t£‚ú©¼Äª{¿ý‚ÅÁÒ¼Œ`n~œY,*OæËÔf£yDm˜‡’§ó*ÄAµ`L(*ÒbÍû•Ó¡Rj®bi¢ï0ç¨XkŽ"kNBbrNÿ䄤ä 3?T7Þ„¼`–zB* îïâ ©¯zJi¥ebÏTÍ"úQ©:µ9½ÅúÐJ*4k'ÖZ@‘+ÇlK ©©i—%'Q2Uú\ÑÑx’¥¿QÒÁÒ83vUÖƒrË&V*ãÌÂüÌòÜÒr•D~Ž%LôæÅ‹5¤EGs‹cs[6kY€�V²“’§„¦Úû¦ZÄn¦ÆZÆF{9ÑU]Öª6Ú¯ÓL·]3‡gf%ÊIÈNJÉ’IüI®¹™kº‰ÆpÞÄW’úJ,:V$85!cH°ËýO&XGn¦ÅºBÅKu&¯e²úì•sZŸɉ²¨]ètÓL9wCì´±‚yåf—Å€‡{‡î@|Jñø =ùa,´#HžÊEá‰q‡í%"Ýf~ýV~}U;KUwÀrÇêÒñUé3U ‡–ÿ‡ §j²Í}k qGWé“UÍÖpâÖà v˜C:FÕ’ñþß,™êRmæÕ¬ÜÞÊ]¿ª×²ÜzÈr{+�UË%ð»\*§ÚÌ«5$º•G˜*^k“ÇåÑ##…2©fà™´@B^QFpJaYaI±œË3ÇÎp…ÎãEaæqcvO,©(.ײZ“ÝZR‹2ê ©VÒ¬ÒãF»×º56zôØèQccjNFòД̔´Á9)ƒû¦e JÈ2ØÊ‡¹ ͇õü*N>¸yVk„òøÌsaùæ<j¶ ͳyãì}èéð”âì² ®U·<Ô·4Œ«2[‡9›™,«®=TkØÏ7Öi9™ C“s2’ÌDy±Öq¹KŠËKKŠ.+)ÍIX&ž§—²JUVnU‚’KKKJÓÆ3.Ó­jE‰™Á"£=—”Z‡ÌN´Ü cÞs¢U¨;OÁ‚²ò`iBi07NåúT7uŸ7:>±4zðäi”]Š«ÔŽ*D¯5hzi¨LLœ•<,Ë*9zJÄïòQɧ•‡ôq•þÒèÐ2K*u…–WR©;´œ’J=•K§ÔW¹XJý•J£/=»˜™;%g%¹Ä=)XÐO§Ê¨fU§Á‚¾BTc‡!& 1à5Å$Ý.U™ù…,y‘Êä2!p’ä±Ó´äq¹U@!yU‹É’_EŸË’7ZE?v2K^}¡üV9±1Ñê!õâŠñ¨s”qè_À Y&ŠZÅ|ÒÝjoef ‡>¦ãrY­Íå3ªÚçi­æb6%kNòÒvJ¥cÖäÕsí–¦V¶ØfËI–˜œNޜ閕Ù,éÙæ@tò´¼àärc0KÄsb¢Yê#!%£Z¨>–ÌS½›nŽDëDÅêžnòóKñì‹ 7LÌv<¸bÒØ`izniYl²QëÐ)ÅãJJ'å–«A—ÎÚ]ÞxÑø½xȨJ¾Í[8º«Ò.Ru&kæ0òdou.¯p‰rNOK1ª##Ó,é¼1¾È<fa›ƒƒ<æЇ‰ÛL‚Õ"bbBrSÙfÍ=1Ö=H=EˆXaË1¦þ~4yd%÷³®Î]´_¨0k¬§–LM« ä¬aÁxSŒåÇ¡âƒ>o¼Øý·©ÈÍ×ýxí»-¡‰5oÑYê£%pX‡U™ú†�¼9Ùaóî“y÷ŸFòEç Í{àoÍ»Oç=;lÖ¬ºôù°CÆâ·Vq~½Š#sNªÞ)sùÏ´–l©Ù|/Ô*?îÞˆV±ëû°àÄ9 ƒ¿YY)}²³’­îäÇ–;«šq«ì'ài¼ ¼<ºþ ›y~«gø}fªñYcª__1Rˆ0ŒuAFÂp3XÀ*×@è€o=ìICã°F¬€¾" PËLÏHš’jTbNf²µØ =Ð2wtziᔢ`A˜¤¹€ mUÚg%–гSèYÌYsT€®›Â:¬q) gª�®–’³3R²†W­Ö€Zõ§‹ ÊLJ$¹hrf0¯Â¸Ö™ž,Ë+-œ¬zT~VÆšWCýs‹ó‹Ô<&^ wV3V×àJ§Z_¬U±ºpWûAÜDd<ePº1ʦ Æ<'5yh²uDû¦A+§ Å%ÅÓ'•T”ÅEw³´”ü`qyá¸Â<5ºøÀ¤ÉÆäYR¬tw7..£N•èé&Š!\ÌD[Ý1Ö#Ë㬦ëÓ(AuHse’“•‘8–΃Ғ¬KGk/Ö+ÏuÊV/Ô;´þXY¸C²Réß´¾†–14źw›[µaWHÙ¥§Õ.7•„ٶ̹9«47obaqÁ µPRé§&ˆO7θú(œL+.šg&«›±Ú ÈÆ&qêy k£×ÞÓð[Iè_·Ü"ÎJ˜<زêY ÅT©"3³²t Å „²J&Õ‹ÂZCÆäéiÅi“Í~uO¤ºl{é²¾RArG ›3­Öó&Ñxà”Vë”hý 2®q“rR’’g¥ôMIÎÈIÈÎêŸFeiå‘Ö¼¾¡æ‹¦ôdKè+Å1ÖòœÖ”Üê¢S©pézC®Á¬ÕáJÃC5…ór8œÄÒcdÍZ³´Û¥fǰ[-c*ÊÇ—ÐÈÀó‚¹ïb-Ƶ²å‰³2I›0ÞÐðæ1•I-t›S°r{Šâ€9�»Ýúñ+Ga§·+ÍLsò§b0wFªLþÖ2$LTêÜúé)<øT½ÍzœÊm=„�UýrÖv뇪hã°ÊÌOçwÊÐ(¬VÎOZÑã%ªƒçd¦egèñÍ|S5³¤¢4/8X=jK‚EW¾1I-Æ\.)3W<¯¦¬û–!'RiÒn¹ñ¤OƒU¨ÝÎÉj&š÷Œ4sÆs{°k1'¥ÊjÎhk”45ÛÉÃ*yÆ8LÉΗGÌw%¡Ê^œ•jl‚О¨•-+)f²­êÂ3`aÖðä±–4ðš³>Ó‘¤÷=ÝØÇó&ÇåV•'åæ™ã¡Ç«w Ã5Ïeõ Ú”K¯Öf=ŠæÖÏ¢!€™¤~iÙéV·ñª;RýJK*&W]BVçÔ^©ëˆ`FdžÓê ^·NZ¨Á°¼ÖŠÖÔ°^{­d§]6غÎrÓ>G™7mj±Ù:¹»[iSn36k¡}ô0Ç­RÕ;pšç5–±ƒ2†«šç§m†2¯±:ž”[:…TM2B«ÓY; ‹•œ°>«Dcôøã’ÉR«k«&±àòb‰¯•ñrÞzy»J¤f¬ŠÀ>Bu&k<Ò»nµ‹P?–ý¶òÜeÃVTVh !ˆÎj±±¦UƒA¥­•nëë,ÖˆpE_õ°Uø>= árÞ±ŒµDJfVJ¢U¨tõ_ãû{#Qò´É…¥8–Uh޼´á2éÛ3”pkCöTkOó‰¤éŹ“ óÇÓgˆô6ºù:¥y0aJnaQîØ"½g¾WYµ³›¯V†o;æÍ¡pãª':#pñ™¥mž>=«š¬6æÓ¨ÐìLÝëÕžHv™ÕéÝn½¿!¼f¤VóñH)[ûn½¯#_ØT¾âö["†\ÿTµY¯ øDlIÉ™‰)F*2*Í~ëѤX«·[+…\9²Ý,'세£Oç¶_õ«Ô˜÷Õ*¬æÃ@¡ãœZÌe†Ì_æ]ˆÍܳWV¾¬›-§ðY¹ÞlQ!Rªˆæ V“µ–“˜šiuvÚ‹QÃÚ˜9æ˜s »›5 Yc¨G)ª¬¼Ýäx¥Š*Fir¦÷u“k7aF8ÕõcÍ0ôªw™1ڔŹ¢•”,3·yåÁ|cÝkÈfŠ3ƒeej@r¹eŠóEò]žwFp\°4Xl¤Êeæ%3·8¿OÉ´#{åq.33 ù…åé%E…yÆ læ&­´° °8Îåï6(wšÊEñ¸’Ä¢Ü2ã,=HW-v³Šþ?öÞ­½IõO9¯»Ÿg›Ì{–ž(’²9E‰’ò¥_ôQdÊæ˜"9¼Xvÿúƒ¸ÈDÊU{z{æìôC•ÌŒÀ-õîﲂ×»Âj•yAÈf tâ™á_DçH~öÏË Ø4øwo»YžÌÔðÏÕvûûnÿ²“é‘bÕ¶24ì­V(ö2ù>Ú}_nÍ"—÷»oPÜÌèR¾¥ó„öœ‡&:OÁ‡´‚ÈBÌ$ˆjÎø“Eïa<º-”;s|À7]y?Ýï·ãÍó¢w¡ß)mðwû] ˜Vðíf·y¾<ÜÁ`1¯Ît¥Î8´ Í¾‚C —~¼Ða¯c€nx%ëÞ°˜²¦ PÎB"ÉÕQF#ï •¨D­¬�ljYEKº¾¢Ì±†££c mܳ¯[0xó†FÇåîdVM ‹­„1`—–V°#ÁÜA9Ä|£5’¯ ­±T­ ð¤òán²Ý|núD%)wÕóÐì?ý uv18;¾ ì˜b˜�N•¾ýŒI`´c”¡Ó.+Ù!³­<*Ù!JÐC+¥Æ@,õ°èM‡ÃYOäJŒÆ¿B½H­åï¹Zo–ìïªV-#Ælgª€uÍÍlMp$`Ñ5‘:ònhŽlF3>L{3#,Çè‰GÃ~_{ÐÁ½úùp$a­ßa?‡ér½6ËU¿Ñ\=ìg³„Í–ôí¤0¸Ú¿âMéz»_}³Ž5*(H4 ,"HŒ4 $¸,\ülÜåã^àÞTË3D2ï_jÞ »£š›Âp²ø¸<îÌ0ÿºßUnÃún¸²<#ò& ½P‰-ÅÔËÙù»F½Àü8ýåa¹›ÊÁIؤd²ð;I=™X-”$z¡˜™÷ç Ô�;ÿ¥Ã²Á®òå-:¹þ [¼]cõvÅr– ÓÉ|d¥JLî®™žq4°¤I?ß‘Ø!® ïìí†ß™' ®æÍ¶¹¯ò8½a[±Á£âÓˆ¦³¡!H ´ º+ü ‘!õp“òÖMž!yu¨N¨ˆJ}š·‹ ôB-þJàt'Gì%NœNãHeækØ"BÕëËYZ´,Aò;uW”Ã}N'[]Œ´q”¸lÃ9¡. XÎv²¶,Ðù3Jn«ó×½ï-ç® GënìÉ‹&ÓÀ(ÜáÕ¸]ü7ã¨p‡UÃÏÍX=7Ÿ†óѳ†·½ÙïBj -5¶Ñš!¼LðšŒ;ìVÍ=‘5ë ÉV¯MD§îögÃÞùjáÎ@L·!ôº¹r‡•s‡dǺÀÛ0e¥ÇºÒÑ ôvx;™}~¸îÍGý¦²£ÏeT\/OUÝ}Š45ˆwBêšbx”NtI ä]ÃxªíwÍ]Ýs|”ɯ€w$—4Jl눈‰ð“œþ YdB¢2Ý+o‡óyïÝða6¤‹‡‡áÝböYh!sõTœq£z:nlêW¼0z0y­€±ºq••«|oi“;ªÍÕk¨ú�½Ð1%¡1]'ýßeL%]^ï_F>ÃF9Z‡dÈbêý¦é ÓÒõNÞ‡Çè÷:ªÌdªï®¡Ps ƒÞ‚}ˆã”‚‹’Óßä ¥¶ï «Ÿ�; œ$ ‹jË·Åýɞơ¡¸˜2{ú˘Ãé|á±VŠfÉ ‡t5 À…ZO([5š ¥Á4>m‚•ºèÞ0º{gB¡ß\r§<¬¦n€ÈØ,ÌMˆ8Íè&²KȯãæwßèÅä ׳޻Πœ»…þqN7ålPœEÊ.ýÕ\Ð:Usë°×î£HWto !Èv˜w”@ ~g‹±‘ØFP÷{c0³¢Åj0¼¾ç•qN±”¬¤YÒ‡�oûãõrõ ÜBªÑn]ý¨É–¾ÑØ7«åv^­TŽs]-`@þq öá7õÊÂØkcXcM$¬#÷p‚»Ðce×v”˜–ÇŠ»ã¯±î•#ϑܯˆh› ƒ`,_£ËbËu±AÁ0J(£E ªÇ˰7þ&u¬QE½gàuò¬:soòšíâëQžÈfwj››ƒÛáëži$9Xç©:~··0ˆ?Ø{°0g·ÐG•IA©~0«p<yçºlÇ`ã>¥YÀ‰©Þ3¥Áþ¸&qëï`»Ãg½Ë³-D�6ÏÕ®ÆUÃFƒÚèñ«·Bð¢=ßî¹Ù‚vΦup1c‘¾iúËþøÓ# D*Cñ·ë(¡u·ÝŸL¡ñ•5雵ø: LÊóÍÚ'߈1Ì/Çbp‚ª¢WÍ)I—O«µù£).Dªƒûæ4Œ":úz"òÃdþÁœC ·€š$û):{v“býbÀr;,W©£ô/·ËßÅçÛì0â’õË5ä5òéL\²~™n—gpÊ·ú‡„ƒý[>°õA?ÓZâõCêv"4µ29D\ £iE4ªB5ªö„±K‹]£êG¦j‰ZwžýW§jÙ Sµ”(°èÂV"QAlË0ÖYEŽdj^Fäq vj±kd~vÅ÷üÝô¿<a‹ÔaÌ—¹‘F›U55[=vËŠ9xûdd’‡³Q§C—͹â÷ž"%áÅìñëËêì†8èFxèxñ„:~G×ÊÚœ}yd%`lƒ6$ÙÔhŽèãü¤Ó±ø™å%“€}ÜT“ÿê3[vµÑ>³ïSí3ñf˜Ùˆ·ªÐÌF°If6ŠØ>Ò /O‚Dâêt›3«H–«#+Ñ3«ø©Åó0º…óчOO½O:¸fË R </¿lV¾ ŸÍV̳’³i>¿xŸÑªŸ·ìåâÌ<|^.Z3$‡¯{«�ÚÞ÷: `ßž¼ç{Ä�8,¿Tî{=œà]ôþñßk& �UöÍ ß8pÕ7§�+XÀ>d]¾bËÞ•‹6¾]žÎ pªxÚü¸<X‘ø°ËsMåÀZ×G·P’.Ä)RhGÌxÎËG¤ÝȬìÉ!M`ÔcäÚºYG’u´õãé¸rZÉ’Ž qšÉrmÿXœ"yGÛ7ª£ ˆµýõæè²Â8mmý ¦À)SX~|:Vn—ù0óùðlm‰…eA8-•– i[¾íxLb§¦´\Ǫ Ú±¼x8VÛýÒ ¼ŽåÅO¹nÇ2äê±ìZ–Óyî  åå¼çœP ³ì¸†cÙ†Že¼ö,C"pë$,Knvf Ô†YÆðÚÄv[îüZ-½õ!Ï„™sâßHxùîéÎë[xB\’8yÙÀÈ÷OOZWb¹€îÒŽ3ôÝÑáÉ0‹¨õú›J)¥]~7¤,‹b ÜØA/§$V]“nGå{¦’¯Ê=ÑÝÅM¹s»\}Ýìjö 1Á±m¢a5g@s¸©ægJ*æë)%ÆYìç?Ÿ÷Û…ëvH‹DëF„“åy±±Ož&øv¹Õ°¢á¶ ¾ŽË•i„½ œ$.Ý…ä‰E‚nv­X—S÷IÀg:‚aða02‡²ÅD m å¼ü`Ìe¹µ¦x«Ø4oÖ¥—µú¸ ™t!ÕŽ†%Îg·Ð¾¦Ú× ¾ÜíkóŽ á¢ä¶¹]£š3Þì¾Užž%Û6ê8A0nß4wî;q{o2yí6çÍrkþ½8²‚½å~· #o0a'Oh×BF«mèpÍá÷€7t¸Uª‡­‘ |;ânèÌø®ÝÒÝÔÁ§¥ #IŠDäëáÝ—ùÏÓ¹zö´VÞêÞ@Ð_¡’ØÀZ±· XW!+´†ùåñn†+XK+RBç•›]1LK¹ºOø³³9±®AZÖºBXÝè­Vßæv‡aeC;£½Hi¶Û6IfØOjmzkÔ5µÄAèïŸÉUÍUN´»‡ZyVQ,¼VœvˆñÆé…G`EEdàìû²·[C5#ÎÄv4Ø1h yŽ$õü–é• ÔfØN\úëP¾Šù¸".õÎK…… æBd|aœtþ;‰WŠxÎÿ#ÿ·Ššf#0Í9"~¤‹à B�à!Ô*€•màQ§‘WD ŸJ¢&¯É ‰GŸˆL‚N0¿ÀÍE]`°TXá"¢ÜFݶêS4\xÉbh6¹mÑsbº ùï$‡ZÕ\[­”Õœw‡£5N©XêëW £oFޝ1Ï%ßBkWx*4=4™òæI¦ýlQÊ5Æ?–¥|2¯Í=(äYRmR朻=ÔN4æ£ýD7ͦkkËr*rƒ·<~]ÛC [£ž7;Ì/£�¶H™.üð%·xrŽ.]nòät,’õØm9ÕÃÆ‚ Ì©1çV¶OKw|ùV=ì¿ïöú• šŽyڳдw%TP=;Ç[d‡Œ?ëqˆÙI´êˆëöÏÝå•%< ¤C½ÞUäý«h×3“næ!±s^j ‰ôHô|;g¥y䳸"ÚyÈ|ý.¢Yjì`„ßåJ óɵF)gí€kb/€W2l»êáÉ»ë$þ1Ÿ}»‹e#H“Ìg´QÖy =mÒ*ËÀ>ìW$ˆ[}ùÍå®B­^]½ŽÅz6%2Ü›ê‹È¥÷žAÐ2’Ø«‹™ &i¿ö -Ø<<@,­Û~‘[@ª|ÞW}Ìê4@pÐÕêj ÎYe§'éJ Õêüó ›G‚·62›{xô¡ª¼¹1°Ã±:Ÿ¿^vßœú”5†`ÇêéñçÙm.“Þ^–๱txª£=9¸§D[4xŸ–ŠxÖ%‰³%7D$'*’k'¹›¹¬'L.™sCBtí÷ $  ;APö$aÐ0‘ª@’,/­X°MêF<ï’Ä †š˜QÔë 4†¢‡ô¸‹²äï3˜”SFÀ`T·Òì,IŒE“#žzÞaÆ´0¶Îb¨T${ >‡·{µ?Ãøð<ÏB£ï´>椤 ÞF7«æÈ<¦©§,Ô°4ëNcj§QÜ<).z§¤£ï“’¬ƒÍBH‰ÍiÅh†È—dúõç \¸B&?¶6î8¯X¬‡­…®t¶|ñT9v¶kLbÛ‚+ñïµ·[¥o¢5:nµÃãC]/ÔCE½+¿¥Q‰•äÁs•pe¶kóBS)Ïóa‚dÂÈ¿™J¡Ô)æŸo¯%šH’áÛ¶âŽgç_÷dz7û4ÓÎ'ôND<oÇûÝÿ�À>ˆ�pŠ!ºT¬ÓOƒÎ\¤^i—o#Ùù}g*ÚòçælgÎÉôÖV3ø @æ©wùáZÜñ–#{k“Š©*ºRf×›‡ ïæ}7¨LöÞý'ŸôàÖxé¦DôÙë‹åÇ3Râ^ë"ïÃK2ÞÍÿÆ¿aËp¾NJï@5.öçàBä·‹«]OÎÅ92ÅÍeg]2“ °h¼Ú~¬NMþèKi=†›çj§N y{ÌqÅÞœ‹ï5/RÒê�Ë(r¸ép­%ñ¹ò`Q3:B JJ ®#j¾h§nÈ—ð5ÙG¿ ¤ÌÔ4îÍÏì¸Ù=¢wÑãúYîu8ŽTå (Ù†$£Pá‘[ÓBrsÃøµã%8¬â¨å&‹ÄÃ÷åb³9žÎõY¡•BB [¼Éo´GÌ¢z×Ìì (ãeƒv‹z%ÓA­CŸ[¶Ó‹é,ÛwaÅÉ+ز«Ç–½c8žôW0IìÞ!¶ç¼KòÊMrm3$K çÒÒL«Ü¼™wÙèôŠ!éx {ª±44ƒžZØ »y]÷æá‚¿Ý/ïùñDMþ¼ ×Ë}ÑL|ÖÊׂ)³Yª¯´.BãÑÝðîþöZ x”ìÛÏË.I¥]Ý)4:ž¨À&{wèN7°×©8ªAd|M$žµ´£º3 Ë¶6뿇׉·C(≄¼«N™U$ÓNÒ¼­¡á³K¦G‡·ê2C/ct .1ô .0£c˜ ï»Ñ¯Ìw4‰z Ç ÐgŽi' ˜Yr:Ï«ání(âi' (*¹ž/UÐiÀÔË…z±•¹¯á‚/¾†X’©=ü4Ìõ»ö´‹9V:-º%í÷íî MÿB]ÊM÷B5âñ¬:Ò×½2fi+2]6ÿºS™À¡²ºJ®6ßFa–±Šà•f{­œ×`‰lÓ¶T¥©P•@^™º6ÔHPEha!žµÑ-ºþŒNdÎðîûcTqéJÏëÈŒ&ý¬ÕÃM§Ò´•QaDÉÚÕô¡]+¢ïïï~G‡Š8’b,¾K—ÖBEÔÍþø²'Ez3àñk~dÒ{ßtnÜÓ•d2`¯S4 Éî›v­È ¡ÉÜE‘¶ V¬)Üçž ,\Ø·1Xç–§ëW;\çc}¼nôqy£èDæ7жh2»zÉ„êø‚-R&ê*¤RºuV®‡ÕJñb)"…#Ñðš4¢¨HË-ªY °í…Øà™¥ScZÙÔ<Ÿ›’©ÙŒÞj‘@.dRçB¦7\QœSç:æzr7h':KÇKôÂ3fo»hܘÐá‚”6³ê©~ÈP}§¥;<a&çîà%d›‹ |ZüÍdö±73[QŸndàåÿ§ûo¸ZFé·Ký•( Ð„Al‘&‰J§ÜÂ(ëýÞx|Ý“—¾©ØSµ§`ë}â4ö¶ î$*vŒP—`*•5ý"Û­Û౿ƒzÚdæožýåv o2ƒæ¶¿VGsÚnk˦eiëÛ¾ðð˜4Â)ŽqµS„”ææÆ2!â©OIj#ž#yÃÔs#]5Èç¹ Ðwt ’Pî“ÿ°>H"ŒX³a´AZL‘_©ê%P&PD $Bó<\µàÉ®”Î)TŠ×ÔÓ”ÆþcÕSG³\{ïúõ¶PPF 8íA¶10¦‚ðrš9Ú6dag}ÅZ‰ç>[O3Œ_CÛ+ÙÙùßôœtÜzT².n„£“¯©Åæ0#û«»Q’Ï–¤ÇVÈMÅЬ„JhH#FB­V‘Û-(::5`nÏÄÙ>î‹ûT^ܧYç•yð‹È§U±pûlÌ3Íœb´Õ$1hꨋ)X›±ÝjŠ}{sÔY±,§YñÚøü2"$ôù<– Ë=ÜóàÐýGëΉ­—{$²%ƒ£hàˬæÿK6F�NàîZAãt7kåÔ¦ã9X§ŽK„{ñˆ—Y˜%mûÜoYèyg[´ì¢££´7ŠãIo�noFïbµ oŒ¨X}·Ý?µnëûÛjY³¿:ÐyÕ°àÇvà {ÐÃý¥‘ž°"—?ÈˈV;<púºßÚ—-d$D¼Î�ì" HÁôX=m~Èó üÃÛ1æ³€¼z´)³î¶z6R«Ñކ‰Æøàµèç€K£O‘yR8VûðŒ¯ˆ¦^íu¯á‹ª¶|,ùJAÕ$øjŠÙÞËìæý_Ë<UîU6ó—†øÌ‰ZÚÆgŒD!Ã[°ÀùÅ`1§õžžÀuò§z'¢n7rPÌÈ®þýùÀ‘‘Ë›0i×È¥i¸Þœ5¨ŠãÊëq5HÞ«W¹Ô2›èQz»l§’þþn1º>ÜÜßÑe·+ìñ¦#ê\W_6»mܪù>cjjAÊ~uT@²Ÿ¿ê©À¦_44Ýî¿4«­YZ:Mã•ÌX©{òjÙÌKÝÌK‘þ/hT¢m VœØ¸óV ÑhÆðX?¢?`ÚOfŠ#³Æ¿¦ü¹xÙ_o¤‚Ì(É~ÆKĠɽùUçxh¢”V+øeNš@ K‹ç+ÄÎiì–p1sŠŠ¨sý¹Çê€ KÛI¾áÂR—žž\:“K’Ò*$-˜¬ŒdUF ÌÃÍtâ%Ë:t~ÙËË•}]=‚ÜijY«õ‹‘ŽŽž–šùŒÉLëÇ3¬Æ0§w*+ÌçY寤ƒm÷êéýò4¾w¾ >ÝŸªë©óÑÌ×Õ±n2êF©ù¼z¼9ºö¥n”½ÅôÅ2ò3<Á\Æ”íÑ<¶¼ÍûBEŠ+ ÓÙY¸ßîw›•Üni|HOôfµ «H.¬òèm”\©4ë…Ò=8ã±ä2–´Æ3.R!H¥ØZýB²(ëRR9äsŽhÙMKíÝzòDlQwÒ'®‘ÉUNæ\å„e~õ"'s.rü"btʺ‰µØy=÷ÍMšhîà"çRÔGèTq¿{ÙìÖ$$ØÂ•X¦Â”Í6“{ Ì¹0jC&Ч´™se4‡À¼º“øÂ½úÍðAmñUΑ¡°’õ7×q»»œÚyƒ›Ø¡îMCâ·ñ´•¥oàYUí†Òõmãnÿ€€ÀÕ¯ a·1›š:<޽Õö®¨‡OcÍʆ¿ehúä®,뺃¯àË*ÑK3,ùп•dŠw“ÁÐÉF”ÁµY58n¾›½ dX÷-ø\Ñs;ü½Å'mfò²cd¸¹™èJ`fî{ëåáÌ•&o!xÙ—¡Þ‘ó@Æ”zx‰ "ö»Ž*¨Ûu³Þªëýþ ïÈ c(vtßö.ç=þ3z;Àˆ ø#~;Øœ@­Ç_‰Ó [9÷B&Ÿ3½Îf6e× ³ÆªÑ£CTÃãq4=¹ƒ¼Ý[ú K q½•ó ýŽ.P nD:G°…ß-£kiµf›_[•ú,–?ƒ[¼'/ú»Ñ|1ë¹I½Ù¢O(ñtñ»›ïB=9/Ì#›Çš~Í݈ð¶yóXÓÁÉÆwÄCQ¤Ž›óqyü Ž#¬6iq‰æXmŸ¸óQéhSL,¢c¬Ëȱ" õ¼yÀHås¶ñ\ßßܨ8ŒéNgV–ÇSµXúçü¹3ïW·\<‚ÜH3òÚ¬à×w}yzâ±g˜WÏ`]=_ÎUÝd,¢ÙǨu‡$õôhˆ(OUh­,õY$¿À~º<uúŠVÅR}UÁ6r ¾i8—¡¡‘¯ÛúÐBÃ`?!6ß¹Âà‘›U`´º óï|E%­Ã~¹¼’¡(ÄgÄrQ™axíWðDÍÑ0ÑXB9ø¤×n²1ŠŽŠ¿/s±w’›¼(þ‡ õŠ¥¯SLr<´ˆé b£Pÿ Y´!}ƒÅ”hÓÉȇFHò Æ$íÔAæ¼ß!xÝÊšv,Ív¨ò&÷0¿ÄVÑ'1XŽ‚¿Ì‡ïn*ø0á/./…Cð®„pn‰eÌ™kêµqD¬b´é×EȦrÚÇ"ÿ:¹ž\ÿ+<€0ƒsr•ep-WýëþqòøïF»2ûÒfÅù«Œ ú³Ù^9¸…[•‹ÕXA­qÃŒkÐû‘$CM1±`I.Þh µ8¨ù›_ Ôº(™EéNûä€ÄÛ ÕõÓÅÎk}ê™Ã><úÂiûǹڭ«u£¥Å1ÿ˜W°Û¤fŠà$6ë½$0=<Ÿ¢—¤¹;‹Ó¾×ǤMpÔ\Jô4©@3,ìï~îHä9„ý^wÕÖ‡ò‹¯›ÓÔ•ýº¥I@éã…*dÛ0Âáöè,Íž5ëø„A¬SÕ¯ú{˜ ¼åë,º¨Žð’Ú3šh)k,Œ4I=×D®¨ˆLž-ªW‚iÊ3˜áåbaHËc‘¨¥Eù4X†»Z0h>ñãM áëùÜX|=¡›8¢º3$pyb‚Ç‚ yÞ¾@‰Ãõ’öùêkµ¾lM«”§½9ŸMzò|ˆ…`ó©l-%"5S‘šÅYœÎ&ýá|þ`öC/sˆ¬úÔÃH%#ýÌé UtžFrÜp ;¾`¥VŠúÍ9SWì=¿ê,ULšY P¸ŒÜ·gú’ K7Ès?‚‹ÝÅl„ÆÙ~rôÉð7o’[¦¸^÷EŒŠøhç–¦È%í –vú¯IAÛÖ)çIõ6.o™á `»‚œ–¬‰Û°ëxœô 2\‹º¯i6 f—$ ³È3?u§–×+–?4&…(m˜ õÏ”]Jó©b ‡wƒ‡É |xÀ«Š&1ø|ân콦‡´;œ×ª¤nI<ô ã¡ÿ©’¢ók˜t¬ÃÙ¢'ý‡ú“Ûéxˆ}ð÷”áà3«{ü^Iþ6Mäª%5¦pÇÙÞ÷UäÆ0ÿåDŸ/rcR2À•uòº®w‘§2C-Js(dô6.’«Ñ^¿BâIŠ7[$¡½øÕÖ¸ç"¦ ÈzsKþ5È¥bVªÐ€í|Z6Z}‰éªºQX–ýæãgQN·×~T0kÖBÊQf§Õò›W‡Ùÿ׉÷ ƒ¤ÕøÍíá•q3éDÆ–¾ŒýuA´¥ ÚÒ´æ_F,@k°‰€ÙÇn€ç†PTÁ­‹l+}ÙÖ^@dš2Ç¢c³¢îúŸQrP÷r¸�¬Æ‹‡ÁÄœÇú€¯óÖüO>šíF üz¾WøL…YHå˜û3„ ‚È"D}˜ž/†·fEÎ0ºvǺšî_ª#Ù ïw§CµÂœë¦c€U9Ó3çã|[UóµktÏ‘9É>Çæç|~ÆHÐÂÍ¡ÍÅþz9¯!ñqæ~eÅÒÇ®ÞÅTH… åÚÑ2AË•X¦Àÿ#¤)„4´¹Üíw•Є¾¨á¥ë~Åa Aè“bìaÊ“ÐW¨ý,4ñA“§'! >.ÏC8+"ah¨='£K.{s̹@4›y·«Ä�)7¢¼É9p£GäTpÑà :Bþ­Ü¿•Mø·òý–)OÍȽáîÇÒ}4íh‰ ‰\Á×½Åb8û '³çÁ60ï÷ÆZHø¤‹¯óß—»Ëvy¤,«V´8©WÜÌÆ/j/¤öR»:±;GæÇxÔç«Þ<¢«ÞžóLªSÊ[1ÏÌ—»˜âFn¸­}Øgiƒz%WV9>dzOŽÆíľÀ? ÇÒyºÓî¬' µ© ò(m\·‘ 9D£äêzy>WfcÙ†ÅË0`°%z“°\9k.ÈNê¼­ô˜‡(Ó5¤‰‡– Z¡”Hê”ðç/‹âhfØ\C2Øtµ0–ëËù,su%ÛJ˜:ˆŸõŽ7kHLTY�¦ 09TFR|sFÏÇbï6ÉIæÑzëTŠˆ©>Z¿?Âç i�šC| s°—$Äì5W\ ~î–Ï›Õâëq>s½�Œtò)d[w¯ƒ³`¿ ±: Î`Eó¹g}YUë±™øÝêg ‡Ãï~ÜìÆû/änå^¤Ø,e±ýJ!wû¿Uó¤N Žº{}Ü/׫åé,bÁÐx¿½xù‡%üîæ´úº<~©\Î „B$^DeÏüwõa³®öõÖ1ò;úÔà`ólê>l9fkM3Ô‚`oÖs¸l¬¹.ELê ×™½6ŸF“.!âÒóæoðLy×¶™cp˜›åNæ~±ßVFŠ®” º9Ƈ1EÍ<Õ9¤›' …ÊlBRଠܒ3hí¬ˆ<ëPrÆÐ¤U+ s¼ƒ|O6MøŠ%üÕÝïM{×£ñh1²Í"Ç Ê<só¥Ä /¢Ï.è:(g–ný3>s`ÖÕ¯3”¾Fú}ùk¬_1 Mì×Â~Mõ+fŠA%¼ê}A©›ËvkeK‰®Q…²acTÐxïÐüÍßNõïèSµø I=¶Pð¸ßZRt }>Ùì ¥ÂCl„"Èb,41ˆƒ!ÂJbÙèäÈ/Œ±fX<Ã6§o°n åÙ¹+¤OG-He~¥üM!±MÚ÷6Uƒ8”ŠIÁ½c…¤ÀºíàŠós¾Z }â"°êsqàï–`¥ì †¿qHpäýÓÙpg F›Áì¼ò¿std3¤Ø´¶³$gïòñþ…ev­¢< ìÙÞêãU+Z/^Ûþ [ޮ扪ÀxyËåDs•`¸ï=Å‘ÐÈ[ÔLìЂï«/$貸�ZÑÀ*¼Êáb¹¹/”ÖŸö‡£‡ºÚÞ¬z^n ×dÎÆA™”éœeñdD:ÜØ šÈñòÇIêQ~îZ ¼±ÀHÄ'HRž‘´Idç,ˆ(§¼DlíX„ç×9¤{ú'\6³•£-öVê¨ä|¬ÌÉåƒYTO?ÆÈQà³ÁŽÝ:€Î›­Ñ…*¸³tñObûÔEÇOâ4ow"¼ïy³“ýËñüU¼í­ŸÍŸÎGHëªÂ%—â _9\4j;Þd×mǃDážE¿uã·¹ V*rK7Á߸µÑîðGµBEˆ¨`x¿N†»ýÙWÝÍm›þœ™ƒVóVò µ”4¿nÊGÝ`ÍBì(Òcv“ј=å\‡q,=ÙÓ0Zå”U;3«Tè<?ÙnM³V#Wýz§©×õØäÕå][öýý.Ã4|O¦Ê­]¢þ§* šQ�YŽ‹êô€Åäh2ót*¬€GŽy¸£ ]KýªçýÙ½ Œœ‹­&ˆcÐc¡ietI–»2h.T‘á?ÍDž*ã4`…®âŽáðFUú@ó·y¤êÆfDsõ5š05E¢ÇÀI¨ŽX²…úBø5Óðá³å¬Fš¤wNÇg,Is¸|'E«FÓÛíÆ=ë¤A%$å›)£N´RM û<x>´ŒQåŠ>Ït“Gw‰ÞàvtGn˜azá‹ð(iž>å»ö9e'xNËäÄ„‰ ©¶3ßÅ›V[Oel×xe`L9aòõ_£ sfz‚‚^bGAÕŒ¹$ ‚!F“Ì&Ìmf�äÒ"׳S2Š\0Çø"§Gd•ÍØÜ”‚[ô7ÁÉû †þÐw¸:Ç ¿Î÷‚¿Ã@fÃùýxÁ€ÒÞ»�Q<Íš5TçjÈå=ÕÞ¼?™üΟå¶Ñ ¿7ž\÷Æ ˆ�DÑ“ï‰ýNøï{w2?hj¡XÈfn[ƒÑäú_€ÓFéõûÃñìX«pù²÷jöÆ…P¸dI£JÉK¼y=ZÜö¦R l&ÍJ‡j¡ðæo̵¤@¤ëÙýü½V’+®#å:2©ÃÌãûþd<™Í§½þP+ÊCY<®­àÚJ©Í°ÔûA_jÁ8Z}*ñ\³ï¢ÃìÎlù8T0¸†„kH¥˜³Ápþ»Ö{`0¸†œk(¤˜ÝáÝûÛᢇ^„RQªÈA¤úh)š¿]©Ï(ïoàÚ‹+"ŽzE€Á5Ä\CÂ5À |?êEKjHC5�×q ¹Ôš~~Ö ‚üj¸<sk!Üš·Þ ‘ë(‚ìÊHTOÁüZ¿Âkñ÷æDcdmOOdYÁ⚘k áZ¸†ÿqt‡ÜALhmA¾u1¹FæÜB8·�ÑiiPò<–29Ìãíðî^Z*ƒó\Ïc)óW¹ ¶*ƒséóTÉóYÊ|–0Ÿ£;s,¼³+¶ Nª`qM<³ÝŽLmIS;¸—ƺ^•múý´7š3¨´ÑíÍXRA,ÈÈ¡˜ÁÞÙz‚Ã6RG&uäZ zöΩ#8`ƒ!uÈ`»­F;›[ÓíÙP¸–n$µwqo2º‚­$ÈÂCêH¥ŽLë�^ô¬ êvƒœ (RK!µ”Z ˆÛw­$ \ƒÁuD²½F"s¡.Ã'¶š Ü%$©)‘šR­ „ïïc[KPöþ>–r©¡Ð@øZÆïbÌ@(m¶žûÙ\K7)kðqOâàj�’þ77½(ʼnŒu"Á¼ýDõß }äÔ"¿q¿gÎ÷óò ¼wæÑÆ'f[=‘:óÏ{ÍðBÊñqóå«¢FühÎûgù&‘a°rnWæ>ÎÑß„ÉeòÕ:Gu*ƒc }$DI”( 9}“FeŒc$=”ø{R8"êb."¼’(¯�ÎØG*-’ŒS`)sL7Þuê›´ Å· <P< ?¼Aþô‡¦‘ìÚÝTÖž^ M§.Ä+#ãMUÊÎØG*,ךK4öaYÇ™ÁÍGòØ@ï:+p«Æè°W&lbŠâwm%v�S·„CU3D{8™Å¡ÂZmî@Æ>¨p@2is*i40Ή³Ù‡/\‚ª} Š2u$Ë&WY HãVl±Þüår:þe“Ù¿Vÿò¼Ù}y‰£¿œ~žþå¸ßŸÿ‚þ²Ù­¶—uõ—óýî}}óÐß›‰q(2ç ØÊƒ_ðr1â·úôŽº%®wZm6ö³}á°óù¸y¼¸™Œ„¶Å3´CÀ†ßLk„ 8‡„ñ0¿íÇ,øÌ7òÅX‚P}ÁAé>S ©z®®­@ˆl¬M§~ìBN]À«Œ¨ Pɽƒ[ £Ï'F½¢íÂ¥*†¼[¿4’T<~Øœ6’B;³ìª [Å×ÿT£¡W+VÄX±Ð®ƒÝƒg®Ô!T>yTû\çU§ S®0Áfí÷Œ¿çÒPâÐO0Æîf_ ο~Oü\ÛjÐÁÖDÍ–ÜláP¡†ƒ/ÆáoWºV8]›mTBzçô1B“S$SÅÝánö/G´Mƒ³˜ï¼Î¾XžÞLžŽÑ`ôqÜÄf‰©N^l Ðk¯›$þøÝ¥AòꌙÓ_"ó‚ŠdEa)²>Ìf“û…9©0*Ïk$ó jÒ1é›8Íá‘FžNéÈ þø{õ“ï^‘¥‘v/³êP-ÝEú”ò…CD™b6ƒ…¾¨è|µÜy`"íEEhe^VQl3³tÁmú ·£…&¯1`’\¼Ñ+ávr?¨£rff{9U ÞaÎ"G¯mâªÖ®‘Z³~AO3ï5ƒt:Ð#ê6óL 7wF­L> ¯À^ç÷åDh5\½i/JͰȈA"àÙ5@œŒª‡ˆdËÝšcvƱ¶Ñ,Gu³\Æ;æ6бHh¼X¾™ôïçžà³å^ÞìW—“ǼWo¥h‡bH0®ÊÝô¾V) “N‘—\¹nHY/ÆwïfŠí·¸+©$q@,�ô†&ÈÁQyµ»8ŸsqY„1:íÛȰ&)‚Ÿ;,‹¼$€y¾éj˜ñþ¤Nò¸<U ”À)Œ¸£u’.A¸¨ŠïÅÝíã½ÙÞë ÙA´ª3T…ûb„ʆˆ €FB×0RÁxÉHjÒ cÿnÑà�W§fÁüßµ];ýtî+¼ß¥Ncß Ófgx:Ã-noW¡.´Ì,ôãq#ã% \>à®òa¿½<Wóê¸Ynï¼ÄBì °Ã°&†_€l £g~ ÃvÅ zå·s’}«?òÃr[Ú­Trn*ЩŸXÀ½BL¤{NtÇ+Š_á„[ò,‚ô¯™‹0¬a~o›I¯®——õ¬!|Ÿ®7»¥ê [ÎÕÓÔsCÎ àÖÓärþÑ?Ÿn¶v|Y&€Áéèr� ÎGëTEß‹·¦Eƒì{s",ï@™Å'(²Ù]ªÉîÓþéI¡]nê“~pvO£ý€1Ä0Ð o‚ôGtw±!½sÏì|ªõ.‡bO½Çýñ<ÙQÀ8žËóóÏH¿™-6‡M¿%Ba|‚ -NÊcºö…yŸÉõÒR©Úœ“jT\9“�ßÐ%q~Þ®7g›ÿ.ƒ=Þ4F#îÐ*)ðÛÓ“û1‡‹j‡8ôÒè\ ÷&†b~?;ߢ4£cµ&9éòÕ`AdORÞ‘ 3ÛÏ"•b‘>¤­ßÞr˜Ifà„ÅJãñRÙÇšUK”Šõêñ7îð´ FlLûïÈryt#¸ñâ·p?67/§¸*–ÿËJ€þŠpÞ]Prooy DµtË×€f Í…Z Sk:›L™T˜“*y™.Wߪs(Fƒê„££!1Err“§‹>V›h«‰ã»T—šúˆ™áËdìÑï™CÏÚT°›Á<O'_¾Cï!÷ô¶rcªˆ·`·ažg… ܼӜmY°Ò\1ØÕ¼ËxFw%˜\#o<ìCµRâr¾y QÃîÄ]ªÙ#gqbdâ,ä ✒9§¾²0:-FrZŒä´x{ €9͉³è|Ø™UËõÂ1}_nkyÙ»IÁx{Ùž7‡íÆnÆqÂ3R÷t^Úó1ñmö¯VG¬Ö@¬×§Ï޽‘X‚C!Q§Ož2…�(‚LLŽ=™­ð½N7/³±¹ßpËšmOk÷#îZ œÝ±|œ;!åpß2²Y~㮵ø±±'±þZÔóÜtþjõ8ÚýÛ¥¶Ó¯ÍöùoÍ8Œ2<¢�«"QÆr@¬yD¢zÎâýlØ€ ålá°#<®â_á78'>Q놪¦Øb¨/ú)¾)"<Á’ÎWÍ šÉ‰é«„“¯4¼¯yy¹Ö¯4¼íâœÙX°i[¡…áþÀZñ¥>sÑNQ\©Ï"l{ 61Lª§–«ÛÜÌ&¿1QDˆmØ+Í Y€=Ùà¡…›>ÞÌV+ÙˆæÌÁqÌúW‘yŠ3™ ØN Ïkc^(˜DÚ¿ Gü9”Û¦Î2ûÑíË/pY Ò rBãÕ•àñ+Ñøz¿ðÈNgiDÑM:`ÃÄTFÓ^E|~ÖŽ½IX÷IðÄõ žÐ䄆¹è‡ŸF‹¶ÉÀÛ¢dý2ü±9»©QµKÁ¢Ô«tËó*&‹ºâ"Ô­¶ÅK!´^ïWS¦,5’R;֊ʺ&y‡rŠí8½"ŸÐ$ RDJ ¶Û˾*#ˆqÃ"‚DLh‰³di,q*¸ÄC ñòâ¦;¬v4^Ö©,kôÚ¼¿k' ÞŒ‡ˆÀ.Kñ²¤²×yMf²&Ñ?Òì`d!dN(b;FiÛR˜2™Ø È̬AH:ìœBÁ�€WoZÇg¼léZí¸¼t3YºPèa6šºƒIÉÆã,Yvu`±êhij×+]ØÕ`b?ŠyÌ©«hÐã”!ƒYÚ}¾ëvECûÔ$Ûíwâþ Šq¸å»8ô7–{ 9sÈòP銷 æoñA¤M PA»v§sÄ)—ˆ½¾«@Rƒ#qï—kä`Z/ ˜•‹{ë~ÖqXä7žrŠd5Û´3™Ø Ö©æÆ¥-YpXÑf̼”Ëñð&†³qo:h‚r|ÄÜA¥³Rr%rÒç¯d|bÍ MÆ£¦â•a#Ô¤çóÌWnZ÷zøU¶—R䮯lKe¹©Z¶3*3c×h K‹\tÈÏ¿u´¢£hoH³¼Ÿ»÷ˆN”I§€#¾3¸íÁ¾\XµcP¾©EauÃÅF¼Ðá«h…jQÒìçÆÙõ“§Ê©õs ¤´ÁÅ+!8¾5Ï©Ÿ01G@:5­6¬¡t5/ó¯û¹ˆiH&ˆÃOÔ‰¬ìàŽc Ç TϤ_çgs<\ì¼ó‘�æ8áAè}@DÄ€è]½ñf’&š×EA® M8ËÕBäjÑõ9â#sDú8"JÃß›¥ÿäˆ$G|äç}®Hk!p/…ìu€hãÂÕ¯) º°mœ;¡#£ìt-PÌ_õ-P®!CÍcoKd˜8¦‹7RôØ‚Ü`ZñcŇãÚh1ê÷Æ"Í+hôà{Š£æ½n»û#äÁ\—+7½Zk©™pg)Å0.³?NÆ{ëÅ„ìJ' g  z±7%ÔÞì@;hχf6`Õ±õ9¢'œƒ4 b1ÔèÖñ¢Ü¬ÈÞT&ò2a)@¸jþ¿q¢ƒ¬¾ÙÇI±5„­.hŠõ`Ä[“xdWãGb/¨m^=/_÷G{n‘L‡MRMš°}°Ã‰1¨Úp„S:j‡‘"‹ÄïëLðQ6ëî峦S«ô­™âz´ä—ÁòçäécU}«Û÷Kk€ew‰÷û‹­“|$^n7;‰]ÉçóbXw¿«_ÍÜívsBP ‡¨3h"7xìjÂÅ”ßÉôn#&·ƒ8¢K¼Þ®ïCÉʃHpꊳÿ”+ÔðýhíîÔ½MYÒµЉQ1ð<øKt1Ww;J»Ü¡Ýö’¡3¹ÙïþÿD3{›Òiìç{ÞËWP pص*i)iIa—:EçjÕÛž)X­¨ÿ®¿KÉ(‰±fgN•Ø|‘'|îøÓ6$aþ®2·ëÎ|?·øiþ(Ó W[iùŸã]ðŸ8ÍexšKšg£€µÏsÔ Î3„^e î?ýQ®fåÊ] Ûä7Ò\ºcjÃO,¾Lô|1önF|ÜÅ— f]¿˜#xµ|­½ –ÏÍù—¨µk„ —2˜ùÖÕÇi·\Y¨½IS»[½“<¹‹R;Ü’°u$j)¢#Y†ŸPƒ£Wüsº™k3…»õðZÏËÝzy„wö#<Û7("„n ÔFÍËMK7*m£óaoÖÿ0™J›pÃmÎͶ¼ú t¸]žW_µYúޝßm:h¸~í0ÞÊé·ÈÂëaüÇä€ ošÝáN«K�œézý±ó@ d-ê­¶ò¨ÜÏ `�0Õ~€«7á ¹iaN~eòþ#œ ÔßF¶›c�ƒŒÆáS-ƒžþöŽ Of!R6 ,ã§t‹ Õp沫·¨ÇÊ÷$Á«xTë"DöÛ¹³ K¶Y½AüæÈEã…H”ü©‘'QpäC&3(8ôZà™Üʼ "EE`ð"c™FºYiÃRW Ý¢ðbåÝpšIp}b<—wÕ¤lc‰ê÷Æ m©“;"så†î°öa|^cB¢ \ƒ\ ý'U].2_1¼h4‘žYpfC[5³·5]dºš­‡mtê]~ñd¦˜J82ïpØn( ®›ÏÎ&‰2˜œ-·S *^Œ~e}PÔÄR‹D¿·ö׃„£s…|HlÖÞ@lMÞÑmNP_ÞÕÑ…FÀN@ ÆÈ½ƒcŽÇ‡O2£zbðø�šþ§¢6ãéÞ ?¦$ŠŒPÛì0ºÕE}Àœ P”Ô•>;¹Æ›''8 ;Ž9É(»òÃB9…šÎ¦ÒÐe»­#d~&o<lÙJÓÔ¡KQ¶ÓT·S(B¡Òÿ:¹ ø£â[­S7®7Ë“œ­%Û.\+s2_YŠ»®¤2ê’ »$1Š8 ÔmIb7.náŽ×Æn„jêWkô/t vëíÆòL"<vv .E†{Ux3S=‚Üo'³ÏÏed=Z¿Œ›Î¤ë ¾_6/Š0ÊןµÄ±fA~_n<€ˆ›“e³múQ±:Td�–åM�Ek@æ ™©#{E†÷! ááL}ó�÷a<XhâTüˆPQ;¶W”ÈpÏê~$y çèv€åKÿàJ\øî”@ÑÛÍÚ]ûhöíºË¿DÞR‡rï7Þ:gÁOî¤ØB·ÞB—Zp¥kñ6½,¥ã·`í³ºyÜ”pØÜ£ ê tœHFö¼SáíázóÅN˜ùáà6å ¬ëìJoRíÜÐæºàÀÓ€ŠpÎU8gxÒi ¦Ñš ß{S(5Üb72#®ÝäÐê¦õ½:~­–:És3«¾ ê+†Úœca¾øÙYÉòÅJ¹¯Í6ø¿P^Á¯fñú¦ðµ£% bÈLÜ•HÁ’èúD%0y<‚‡.›3f©ùð®_îwA¨¨è_ÃÝeÑ-àÈîCØøÎ]ÁËYzÖ˜ š°BÄdž¹öõ&žðG¡2s‰Ln̹﾿\Fá_ñ*µKòéfó£ZC”NwY‚Oyß÷øxëäÙ³^Õ~þ=çvퟧ¿AêB=ÃÆ]_u;xÆ×>òP„G ºh­AE˜*Ì0ƒG­°hx–èg‹ÑͨO!+„Ûox³xi(OlÜíWÇóæ tÅÊ»£ >:`j’òjÀÃº× ö~•ƒ0Ý ´ è}KW/\ÞÍ€#<7Æ®\·tKp"žŒðˆæš4˜ºd·—Jà²à˜¦ÉáÝý­ipÜ»{Ì(ǵ’kQGŽk˜äåmb­ÔGñî¤T¬¥’F©»ÞíÐi+ëu¤Öë Ó-¥´-ñí¨3(Xpk¥ ¹­Û–¼ ˆº‘–Š[KI[b‡Œºê• ÆF)åz¸if¼‰ˆÄîu ­¤´1]L¦tXs¼ÍAnÙ$[§Õ{:ŠÈlÿ@ ê| ïš{Ó>dñ˜ŒÑñ<ÎÜînfŠøƒRg]µ:E‘3(çòTÜŸå:2ª]® \&(Ö›HˆNæ[øWV¾—nÁ‘aÅ© “€ÑPÐ3‹^?ó Šœ â"pä$áθTsGñ§ê}Yoà^ïº"Îè(à–«žpÅü¾F¹ë=ŸùÔg9A%g¨×hLbC?Æô‘¤ ¡Õ›ñ„œÎã·©4ýÔ#ÙÖŸ®õ“6ÿÔ×oN¨Bl1£Sî†É Rp_ÈJÄAÒ°/˜] y|fmÏYÏ7k”üÎçãóû*©„Peßtâ•<>O·ËQžH=>ƒž:5Ûª=£Ã3)©Z  Ù HÚ¡aÁZò¾wy>"/îF¬–ýÉlȩ؉5PˆWÍà+!Š»ñ=®”*Þ†·ª ž.7 ‡mjÏÊåñG£;Ôç”ûœØÑ4p˜ø‰!·áŠ‹ñjÞ]/f##gyðh¶:~y4úÙ¥rÕ øöîXU;W ‚³J•Û.EµuÒ2áùêâ|Ô<i‘L`éÜ€0pçC-u“ǧ¦Äã“ïÔöÔÔÇxvžšOÐÓäéIŸåÑô8,èôˆúŸpÿc;= žÂ(ã1rn®¸oì Še1Æ—µàyµyoÎàáG\B>˜ð½¿ß¢ °ËF"GœX¡¨bÖš Æ>Ë9<k±Ì †1cÅ´Ó `wZéÕ~çqÓ\r›ÚåЦ¶àÈCàqÓXpìð¸œy`ÒtÒ²¡‚º"M\#ÅãæÓ´ÚB~ÚÛJü+cµR<n>‡ l¦0-n”1Í»oÄï£çÃþè¾}â+G™#‡hDYæªØ  ±†Ø †n¡¸,þí¾7à Â7�fµ¤BH(Ôe~r ×j¶x#Í`f¼X$…bŽK„ãâ²ÆPÔÓŸ™-Í]^ c–‡»:àΡ±:L'Þj*X§zê=‰}¢3”ɹ“Ø#7cŠõææÓtÑÍó?R7¬¾”¸\4€‘ņuý‚,²å Íú£á§Ïem%j´ÚT?~þíÓo\[4ðãgýÈ6 üúWýª/~¨Vj¥"=nð LÀTda;}r·¢”\j¨9d(ª˜ä}fÞc€Yð !Fe�,NLòâµBýáÉ 'xIp~é@ˆ4£wùdÌ ºÛ)?$èIIHœ™ŠBòìCh’V‰ö!ù¼"Ô¨KA©ö!yU¬}H^•k¦Õ°`C@›d3P3¯þ›eé άJ¥'0µ¤Nô¶‡¯KÄ~®Ð¹kød7Wó}¸[ÀSž<&�GWÓAìÄòùy9sÞcfR !Vòu5ƒ¦ÀTPv5oæ•JŒçð™É™ƒl1SÅL=8ËN<~H<˜y0 ó`Úƒi;¦¯q`ÚÎé똾Îi¦¯r`ÚÆi;¦­˜¶s`ÚÂék˜¶r`ú ¦mh@#tÉÕïvšé.ß5aeæ½'q‘% ÿh¹”3›r»Ï£,p³R¸=·ÜîaæÅ„Â¥ÍGïîz‹ûo9y6<îOȉBôtêóGr/Ž r/ö«¡†ø Ÿ“0€À+4— ˜üo>\Ø}=çÕ‰w±ÙRÑïŸ_íhŒ¨Õ¦×ŸÖŒ O4÷9ñáP¯Ù�Ÿ r9Ð犃ºöÿz?_Üò±_åÚ¬–vÖ䨴Zú× dº^-GÛíåy³3ËÄw!§sî ø9«v–Zú :L­ }Hёڞªcµ[U×Ûåê[MÀ8àà Q—2«%Æ{Yž4J"ÛL‹MUS Dj[€2Fõ{ºl=`Ö±£ƒq,6;[i®¡}ã|Ì yIïëC(¬¢¶â+¨5úZ=ï×U&«@-,ÍÖÏä†ÅWÎ1¾•©ßn³òmÜÕúBLÔƒ‚°¿ƒ©ì¸ù^`rv0e]ÎàGéRhøÃP×r@'!àͦڮO5aI=Mñ~ŠþÑõÑóä¸1Ò†SX›ó3àúyº<HìüÌqLè»Ü: $Žï\àØ0Ïœ© ¿eÜÍþþ€qFð#ÇÌ{æÛÈùþr\IRÚ=šYÿ·Ër‹W’Ô%v"gyBËy`6.P"MùUáZ’)z÷.Îo•ôsƒn/Á`ÃÁx¶û£Ž[Æs9la•ÒWÉg7‰$‚Ò.“p±˜ÜïQ!ÔÚnñZ†>'2×ûã³ËˆFg®ï¿ ù骔DÓ^uœÚ¤Ú¼ù¢™ŽM·‚¸V4Ý|¨íݧ”rîÓ(º‹|¾»¼/W²AÙRÇê?.'®¶O­ŸGý[£R|ÝÛŒ=�âï—p/G Ûj½YzžJö6 ž¿V¾S‡½‡ŸÞÃÝ.¿øz®¤�” á–;xQâÑ®Û-êÓQbzÞúáv2öX ñy¦ˆém« +ŰÚeaHuœHq\9ö‘íÉø*?êÔä»ÎtÐq&,³”IÃ2KyÕ“Yʪ!™•fÌâÄ€ÈXp(2«Lÿ/•Yeö”Y*·)³ò8 È,ÈÌÚ"³@Ï É¬¼HB2«èt2‹™±]Shuca«¦Ôg­€ØêÆyÖ&·º å &Òçÿ‚Ëh+×%W7É£ÑÕM;I›ìâ´¿!Ù•fE«ìÊ:6Ù•‰doÊ®,K^‘]œ¸MvåòeÏG–=|t(]Ù%0–kœ¬ƒ±jHüް@sŒ®§€1§ì(’õéú™ìê‰:�Ÿ6_ÕœHÇÒ|ÞœŸX°³ ³Ò°Õ髾·”@FÄëS=øŸ™œqåÊ ²‚< &}õié5Û)ê¬E* m§¿ ö+õ+Ië—sú›ó²�â•cŠ˜ÓßY®“DV÷“ÒBƒhg¸§rQÕAƒt*WT½£<ÑGQñGw[Fw[Gwÿ؈”[”§ gD•kH|Èö?ÏpÕðV®»ÈN·1 ?Út˜ºzŸj™t¸¨œ:ßùžJ“|•ipzwïÆð,Cš&Oµê™/ˆº1ËûÃyÛ¯84ïôÞf7[®7—S*…yÚ}wM›8®š¿TÕ¡*íh´GÜû\zŸÑ¨êp¹™ì–:ºLG§£¬-æÀŽ«íõžO­ y²ÃNdÆ‹C°Ãå§Æ0”¥ðŽMh"àæ¨W‘Þ*w¸×$�üŒŠ‘g1¬¤Y¦¶ (ØÏñ‰cy¬äô€¹_p: ¤¨s@¥Sèá¸3Qc 6›T·Ý®N%Íñm7Ò/Ä©Õmd‘h©™O‹–X5øa™‚Yið³ÎBWo¨|=/´Ž¡ëDH+©c Jê!%‰Ùæz´¸/d)ÆÌiᜋ¾ v˜çÇ :©M”yû§ûí+‘m+S_îvé#~Ì+µÇ’ÒÿÓýDšþx…Ÿ™Vh\„½iu¼þ†ê*Ê{ÍæþA¢›ÍþéÉìhîwÒ*ötýí�rÒ)Võïq¦k Í!œ ¾?ÓTX7.t.É5e<yGÙ÷x¾È qû8?ÿd£f´í#ë—2ZÞ ßã8ÏŠ Q¸ÞIZ?¨Xî¡…7Ó:P˜P¯»tYÇ‹-Þ›)&Ÿ–M;F„8ëÓLý92uÖ@N®¡åy™ÂäaFwƒ‘$‰é1¬Þco¾^/§¯væ‰F¿I³|e†3ªžû"bRM_EM…²©nŠ˜_¨?ï£/  q†ÊAïz·÷‹ÛÞ¢/¤McWØTŠ¢ú¤ôž¡Ø®NóÍ—ÝÒ>•Y¼C¤r°›/“duó<wOb¾­N¢EsÇD˜HÓrR ,÷X·Ü³Á_ žåžc«̵Üst‚;÷9ñÁ82¸K#&£°Zš0ÿpd©¦ª—¥I“좟¥øš»øïKö’ÈŽï·kdÅP®ŒXu² N&ë$Óu’þBwÒ‡íñfÙœÆB˼?oPö7é Ð412Ü]æ^nR¬\¤Y¹k¼‰'¢-S4‹üŽ›3 Ë2”S‘RÝ‹üA)EÛ–of -«¦ šmÉù–uœ=L¿–‘ÝÀä£>z¥ã<f‘ÅYæÒ¦]0½â‚.‰n'ww§Ã÷@ÿ•Ic;LcÌe“Ì;.Ih¢¶åª¶åtòæ,˜ú¶&¦':§äP9ÎCàbt¨F‡ÊõCB£Ce­+Ö»Èk…»$B‘¢Õ…0Ô›RE"Æ¿ ¡•e¦¤÷ä1 S–ÛºAíUÌxñ“6¼cxD·ÅØJÄE'à¬ÃѤÓ:»zê,ænÚ„ËôvzX#ª!&‘DZ ž:p—Ñ}jà½P`Sû¦%Øx˜Æ£à`CÖÚ—Zx+´†$“Uèº,2¶¡˜€Jzû´Ýv!½Ó|vlYi"aJ�PSI%¢WO½(eNÊSÚ‚d:J޲#ô¶X‰Åòim‡¶œý «šqúý›ô̾»òjå†e]”©Gd‹ ³Pªb_¦ÒÇáx<šÎÕ͸lÑX[ñØ!©‚e;êV\Rwj`q,îXÏâÊ ®¢éA0ãZE „ÄV%£™Ü°;.&ç ñôŽ—¯“ð€(n$pD¼‚v¡1Ýjû¦)n“;”I‡x&øwd‰{ý©?¾ û†Nöô€Ï~Úæ¢¿ÝZ&£V5/F1¨”zF“ÉQ_},`ðGwfCh½D\«¸1±UÓFs7¶ÃWG§nçñfù¼ÙþT³ï4„èÉ.zÞm?Zë´l7Óã\ŠD¤Ë–óè¸%à×8ŠÏÇý·êÃò¸YºElµgNõn› 6:®àq>ùå{Š ßnÖÛÍN‘ñÝã§÷^ñ-)ì~8t¦‹ðLWÏÚàéÒÊä¨\nŸ|70’yO±íÓð´Zªçªþnûäß$9/BLM^õ¢Ô?ÎËífeI „Ù>ÝïÖÕÑ-¥ÃÙ>A˜ßoÕäb'Sâl1÷ͼ²Ÿ1'Îr MÕʾ[Ì%7Ž)bX¼¤$9Û'½^“ÏÔþts^}ííÖÄG íR'n–Ž>R ( - ®Óqƒ ¹à©*eªrÚ"\ ¼¦·kŒ6nàEÏ™o>Èá¶Sýs¾ÿ¾óÍ—Ž0µi7²SûQfE¤>©åˆ}(ÅÎÔ*^nñP²ZÔV3=)£jû4Þ¡¦ÌYfŠÍ¹÷Ê@ î‡eÓÚK‘,¾RqD÷öÕVDcDî à&ük�µ#7¢S·©Eïê0ì–Mu|©JGnó½îQ(W±ØÐn½?ŽÈŸ@ºKKº,´¦@ÿ²umr=k Óån’5šýºwEcŠKO¢x¬¯”ÐÖ„ËbSótDg¨‰Eõ'O–&š©c£RÔ&/Bß÷<Κ“W T•Ùˆqxó¯Ééc&5kHg¶6Q”GmÅ$4Qg-húW´M Ù7;ðdaXÃðdE l|þdÉòûrDÖç&¼¸œ¢8õ'KPÅPñkTh¤÷žX?eê0ÔV>È»i„3¡àËoÒe|‘•¢U©½î•p„¾šþ[4§D¼W±œ²ž¢„ Ûƒ9%Î>`>Ö{Õ|F#…_B†xŠÌwÏP/7DÛCý@lo!ãŒiÄà édçFÐÓH$q`XÂT˜$Q=9q˜ÀA,-b,éÀî}JWK—oª!zø–"FÝR“u𖽡Ío2hÒ+¢¤ÆHvD©³´ScGX,U–vœñߌ'“ÁÍh<–ñ'-gˆæí+]6¯Ž5¾àAon,zIXo”;)û%X¬§-8ÂjߎؾMøóáXÎït2‹–#ÝêqöeçºÈ³=Îí3ͧÅãH0<=9ÕZåž £¥…Ž&„'¦ãHMÇ‘5/L!š‹“Ž™�u¥¶“@'T/0½2§äÐpÇóD^Xuç�sB´§Cô¤6ÇCS…Üý:¥s<Jbnva²Cª‰9²&fƒ¨“{ÕuЬœÿ©[ÝÍ»ãòðu³:yÌÆKé;çñE5ÛB«ŸµÏtÅkZ=W?xƒÎô–×Jqí,MŸ÷f:ÉuÙžÔ|ŒÈ®Èö¼ÓZ¢tmuLCXÓÞ‚ÏQ½f‹ G§ÕpC2¥9Oi.Óšë´¦Aœ/f“߇½»A½DV«±1·UÛ ]„"X“‹Q:UX2Y¡€!‘â?Åf qÁ,V3ê3‡5DF”foR[ f]è“RÚ‹ìV«."ò 0„œŒ)!×ÐðUÞ.x‘燹R¡á~,­[�7Çy]¨¾™õn‡‘1¿úŸbÅ?GäÓß¶d‹¡ïY¬n? òƒo†_zÉãvks.ܦ†æ¨Ð  wƒäªÔw:Û›C»M“Ò~Ke§Vꃘ™£’y¤‰!l¢ÖfÄ¥^Žî> g ;1œ|âï11M>ç˯Yî£pL™ˆÕ¯† "¢ÔíPÁë©7ºs‹ZMuxi+bŒGwÃ?+;m³dö׺nƒCáÒg*-ñ:<>4¨Ìâ´ÛɃ6ü$ˆI­š:<µñ n{óßÕ*îPÂÓÀÍÀ?] (ÔÆ›PϨX´÷Úw:ïþà— 8~žùÓûF2•úç>-Ü£‚pÎ î÷Ò;3x÷ÜàWMaæ£\øˆ\ ,‘":¥ò«Ê·ÛÉ`tóùãd6,f½»¹u2Œ»m[è2sËœòHO­Ö¤mMP×äbÚâÒŽ+Ë®«Ë®«prs3ú炘"…e€ëÏ]“~Uܶ¬Ô.‹£ ’Dïи>ˆNœŽßÙuIº­N±Áu½4½¦Å‰5cDŠ·,LÇærtW(?t^¹hì|³òðÈü×\Éø¨u%ól¬d^•Ì+°¾’y6WrGÁ•Ì Ñ_É´½•Ì믹’ˬ^ÉeW²¼1 ,eycÖ\Ëò ŽYƒ˜Hî â.+}¨òѸ*]=LN'ãσY[q¿æxÖtwÔ­c‰Ÿ°¾Fôa9ØQL½KL'r4…BÑÚ7‡¬'´«ಔ"]JQ\g7“‘^×?4Œ2Ë»¨ÅÍ(L€˜ÛìÑÍ‘ZÝßéEÂ3ÅQáÕbȬj|&ĵ£õƒÇй¹ÿôX_U™?íþ˜ÃBs¢ÂÄÎD¹p™ÈXî@“¯‡i¼"±ózUù…­Lªx'Ok⸠”·` Fä¦ÚUqKº­=°8Q³Oûûlƒ"#qªð'QYm·O–m™Få11¯ÆIêwÛbÈü¨‰q=2Yä2P] Gœ‹¡Bg:-¼¨ÃCˆšÈtY¤¸µ'.VÒ¬Š&Í¢¤-rq2§;µµ5J9ÿÔ±x7Ýo6¼üœ…+’ð0ËÉÚÔYÙª-ê¢ð¸¡¶p%tVG[Ç‘IÖhJˆí Ûå'ñ”õ9(‘­.@<]ٟ߸ÚÈWw‘üù¬ü÷ ¡µ^òh„£²¸I@‹%L•©|Ìâ: -vÞR§‡TØ*2úfÖNŠˆ?3ëŠlÒ_”‰¹tͯqrŒ(hÇe½±ÄF ƒT2Õq„ÙÔ•±}dQ7ò¤½BÅIm…LöÙp>ú«ï3çYË¡#à3¹ :•]½iTÍ=îÈ3q| iX@=ìå™ÓáÅd6ðÛ¼˜âƇ®?gÕv ib~ó\G¤k\ µ(æÍXÌ› ™ 5pÆÖÀ9›Üß ¬w[\¼j¹jzžþ¶íï;y™Åú^:«Õs_d >È5DªyZóç¼ohýa4ü8à Èð“ôºíEϻ˳OÃj·×o|4q°øªæ§‡æÞ¬:Á]q]°¢ÙŠ)‘#b e¤ÈÇÑÝ`òÑ+…ªàŶb¡݉Õ^GÄeÒÆzÇ4êz5Ü Ì§Ø ÛðdZÕð³áq` 4NÄb€ÔŠÛðJ[±7âÉõ¿*W'Ö…öuòøïÕêÜ:b®æLÕHTM1(†p"Á‘Õ–°u’Fà#'µ C8©­Ð¡'ø¶Uþ!ÁçŒÏ“^‰X²±d‘$ §Ú³kÏš½YŸ\*4N@Òm¥︢\{0ÜI¿:꘭’®ÎCM¦B-V‰µX͇xõ‡ %½l[Ûw¿‡×¿S-ܪÌWW—DC➪¡ qëbä¢Y]§´Úáãœ%QÛÛboýï—ÓùYž’'ÄZ¸VêF’ÈÎH ¦LоÇ2ÚëÁèz´0ØÀHN‘V Ø>þä¥õµÃ»8VŸ\û«NžNR˜þlá†56ýÕ klþkÖÄX7¬±°aÐã—wô1Ê .¢”œ�Wð­ÍGæ²Fkž áßÈ2\QØXDI”83y y)f7ãÞ»¹LcѲÀÖ/^°y¹õpËÂïbÒ b‰á¢Jÿ ·£Åp6™? 9jé^u»9WGÌ줪v­¶.n\Ä‘íbIÄ,wüI9=tžP†BÄ8k}½ÜôÇ™H}§êþb¤ð,ÝG= òõžFx²gĺg´ Š$SGÂ$v÷éèÓpüG™tZœŠÌ!ÛI…ðk)í;ÕN]Ñð؉W>‚H©D¥TâJ©€š$m÷FGþ°±…lÊ^é[@GL4tvb—aOV¡¢èfÒЦíTÀ*m¥ÍAOfïtÐé+½“ãæËfWŸ¨ú ©6jWLRIÚi ÚÓéQ;,áÇCOšÕ°R[©ììð8ÓÃÉš55‘r§"¥^èŽ/Aß¹Ðn¾ãÚ….è1%©•ŠADq‰KÔ%.±.qÞyѯ—e‰Dèþç%ø?äÜÓ4øþÍÓ4 '�’3Y<µ²ídÖs«†!kAÝë.ÈØ´ÝËügÚ’õßK÷òXSÔ.¬vùŒÍ|æÎ#Ó'J¤ˆ8+‘Dœ«÷cÂfÉÞµ‘™Öm1Á7óELñ›™ª _"ïÕ"€Õ¼‡¨ózøntçb&µª©­ Ý6ï.4«¯s¿p<™oFï4u"9­ €Rú•%Ò(/n%t±V@éú•| |xT«¢û€ÂÇZñoHŠ:%›5RÎ{Ô ˜uJúÐ!gÃÞ¸nÜLŠ:-ÃX.9=#Ôx4u8²l5 ¸ð¦ Jj¡ÖÄs/)=›LK¨¯Þ{ˆ/§sz(˜I£¾Bj«²'!kÖPÃÈU€ÅüR]¬¢YO­lT6›L#‚¦òìÛ­Äw…íŽaIÕªåTFŒ‚ºjØD¸4í(ÜÀ©yx×»Gý[×iÜ@pIl˜Š›C)ÅЉÝâùÜÌõ|¦d8^ð\ 3ñ|®G¦ àµÏg÷Íí#Ïç»=Yë)jŸÏ·Ë³j…ݳ™.ËgÈÙÐHi›u<Lž|Λi,ïËS º˜RpÆ&fd1qÑÞ½o¬‹a×;? ±];0›ãR’§ãSˆ’#w7<«y»oøVÓŽ¿³Ùgív¿#žjÛýîýr·Þ:qCN)뿾�…Þ™ÍzuÜ\Ë$kàÀݲ:dÀJ9Rœg{Í€Nˆ·›ívãÎ;Ùµ¼­w©§y«`†€¤d>NÕîݸ¦baêÍ.s€$!êâ#Ú Šd#êj:¢®]^³a.2¯À|ql&B>®o.;ÇÜ«Kè¸6œÿÌy’ŠPW"BÓBàö¸k²ðx,n]©>Oéx3²˜ÎBðGyõ¢?´xTrÙ­Û™W-7-3¹3à£È è›ç”³Þ÷îf)(½D7ˆ=J b~"n"n’KDå7nMܪ7Bš5ÄÔ–ª© QM·@°›QÌFýždЊðíßù9ð”þüÜ;­œóDÂó3¬¶ÆCúó3=;î–Û±×ô˹Ô;?ƒÑ%�fÃië{Oº¼×ûlñ:? Ý–Üÿ™>ŸfÎϘ½|i[â3éÿæËælXcÝ;Ìl|ªpŸkÇó3¦‡!wå$ €ð'Þw p–Pè>¨@Ðõ±Z~ó�ÀÐÓ 3ct€ó³†Ð8SqImÏÏÇËê›g éPãá��I‡Ú¯E!H8¦Ë*ÌV±°œfpÍ›&gYćP3‹êóåGáK éñO¾üßæKzBS˜z3BQêë¼É@ Uïs' W_çOâ›&QÒŸqô& &E?? SI6<H9 ÀEn&š/Î}þT±Hê›s¤ƒÜОö&éS­_[ª·kîœdc—»Ùo"v<®PMcûWÁÊfDÉ&Ý^p?eIBWé§ô}G²ãúëo‚ˉw®/OOœ\2ö¿ýtÞFÖ…WªäædÙ¢¹ºË¦›“„2azh¥Eƒ•ÿ®?}˜ ç÷ãÅ\:žÒK‹-ò˹AßíÁ°>ó-!õç[ ¦>9JÖÌ”b¸vÝ÷üÛ<íE`$@µóÐ_«ótRørèo—œt).tõoï¶?_ÙwNÃê_íô35Î gdÅÍÆ&ÔÒG! ˆJâÆ ´0$ aˆ°MUØòwY„išý)ŠFiEC„üsôC:ý£Ÿ¬_±ùÃÈ}ú)†‹´TúAÖâwãÏÓ÷$ Øäšftùõås}]ï|Ò>à8,ೈ|_žgŒw÷’ÈÝË—ç~µÝŽv«O’…N–úý³|§T1f¨nïh âj Ý„¡0D‡ÎÄÚ’²ÏéïÃÙÝèîÝ´7±öèSñ‚ûˆ ™›ƒÜΞËè¾ùÝì½go+ñ®Þ¸us?4{*Ê…�‚ˆ—,׎Bš…›Ñ§¡(û`b=%OÇåªÖ¿ïKŒªé$·¹zC¹r<¦†7·½E$™ZùÎ £Ç3¦>^>¹ñãå›@^¾‰ß"6Am‹Ú‚SŽ‘¹QË-â<LîèlÝÐ×1ÎhÔög›MH9Ñ|\T?η•YËC7en4Û&€o̹¶RMÐø™"ÔÜ]žy±cœš^¬t�þtšW[Ç-(¶ft„ê–[SºœpïÞîÕls’Œrsã/;rwQ/8ACÇ=cívþ—åѶ)/©¬jØqŽØ{G7ì¸/©ŒZd4•w˃VÄ÷9Ø»åaøìĦCp&]8}ªƒJéÂqárÔ?¶Œ’·ñ=¨Œ+¯ò.Š@~?åeÁê}•ô[z9Ýnv›çËót:¼uRÍá<¿<’‘ÄÊL/ aÇŒ0òq¼ ŲX‡ê¬ Â`°µÅdh2Rt´ýåì±³äò0l("N¢¼rB­ó´Ú+'÷Šäò굤Ê:R@mš–Òf"°„J`¦|Dá}jP奃x¥@±=–:Ë‘—À·M¤¦FüM):p{‰Â–ˆÑ( g7û㢆4±„d†/jÀ÷ЍI£Î+¢ÆJ”º¨ ÉGÔX ˆ+N¢F™¢)j”!za‘R5Ê QceIPÔ¨<iˆy/5É&$j\qR5V˜EJ’6QS$¿5sðˆš¢óGDM’ü!QfŽ_ˆË%­¢&(I<Q£ óš¨qeISÔ8Ò$ j¬0 ‰奨•$°ÔI"ÈjŠ´ÛÑDÑŒ°)¥m/Ûæ€Ú›/†³Ñ_‡³‡ù¢·¸ Ÿ9d;'=!©œì†èèt»ñr÷å²üR^ºBsRmÔÍ=)ZÐi´ M¤¨¾tÀòöEô/|Ût~¸é½~zª¯#¶$“×™¬'6)Ùec=,cklÒˆTrÊÄPkZ܉ó ËöŠ{/Ê-¾H¨CåCªoÜDŒ,¢ºìeü¿?t<ïï5tá0¹!N1e^*³_ª…žð5s‹ˆ‘—Áÿ� †óþl4]ˆgzZì¼IäØ]ý>’Ólúµ7/ŠÜ0Îy…>P&O"¯fÕÚýˆš;|œÝ<©q Ÿ˜¯0個Œ¡v資Þí`‹ÀEïâ^Ÿ}t *´Â4ñ>†‡£ï~î­V—g¯@’è÷Ú¸ºhH'Pcj—ë«w:+Öì!šÔÍŽw8õ¾£Q}5?W»ÕfëAJêÇåYËlr4ªoÆËŸNªI¶ûèÝ_j˜Ýõ â»Aت±~ù°9m·^Ø6m¬_ËçZD»‚ ÊÌ,â DqÐŽ™‰CBÖQÑP’hx­Pd ÉuãÍh<œŽä‰OÖÁ;¹ìYßl‘‹àðGÍhÿÓùB2ãëí ŽÕ¬C{”và>$Ò‡X.ë©`ÈÒÏ:’“ ÞkÍGïîÌ0J—s´ènO÷§G×1-k#ˆ¶§þé‘íäÔÝœ4P-hT©Ž/`yyWoûWHÿ(Pv‡'2ëvt /;„./q�rSÙ¤UY×&­2¸~l˜2t(È£^¹üó*нÒÙ™1º»ûeîèTèØÃ�À|Y;²ˆ;Lî?/ûc\½©q%3®,tðm˜†Qd¥«#DF^aìÜb7ÖÂGY >qúçZøçõcèú±±&”õkk²}hMãׄ< «q'³°n E}MЏØdêbƒÈãìÈb7ÖÄð“ì”w!3^k¬N³(Lo‘$û9ZAHÑ èD£>nVÖvØ–†Ÿd1F”ø¤ã¶‹6z³‡†ÚÅ4î¡vu¸™´KîÓá¸7›õ>K{9^ø–ŸäÞÖ.þÃò³÷‘¿Am(2ÓùFËý°œ½»®éÍF•“æ¹w¢€Óδ”©U# 7 žDQ<rœº¿õó,dôÀ.Íþ\ž…_§T�w*§1îO,ý‰È›ª‰!Ì î#™<¿³ÈÂôì®Hþ\š?”PÀë¾pŒ<«ƒ†ýî+†Ìš¾§Ëä=E¶K _Ôu‹âïM|� !L!hºoÌ‹]„âëQ&×0ŽÈ}€—% ¾² –\’Æàþ³ãŽOó•˜rcæìj—·{ù#„qD"è»=ÄÙ0Ý-n>ÉÈ@/(~Ô. Öo ÁLAŸzYµ Û]\ D†Ê4ílÆÎ ô0ïßÏ>È!%¥G±/M7éÕáéGÍì°ÄOX,D—^½ùŒâ6ÅÝ’õ˜’WA蚪ڕFN¿ßMî\—™ Au³Õ£ìš:ä6ýeL¿ù©¬tS+ÖkæžÈÚKsÛÕ’ÌÆoBôšŒñDKgp£ŽsÜ-l*q›üÛŸa,G È=;TðfÚ‹Ì$•"z^70¡EòåùÉ÷;Ð.9ϵÞPÍó€;¨žOÖõ€K²ÆiA¶R}xÜì+M–OÑÂZ±Dˆf…R ­;-xJ[�î½ÏÃÙtÜ»Ömr'!ý§Mî¿»Mnº]îþ˜Qnu\—»ÓayÔÇà±5Ì]½ r1¦8tdøôê5D‘ êß‘‘Èke[æÍÃõxx7¸¹¿s¸dp3|J®·Õn=9È €øÉË% ãŸï/ÇUÕßïNçåîŒagÈÀñ‹óbÁ¦ õçîÉZ‡dÓ †¬ó\×yŽë<ˆ\Zä7´i}€0>²²1,W­¿×3ÑöñýÎðÖª¶×ß?€?]LgpoålÝôÊjê¤8xíRÇä‰`†‘¼B"Ð èÎm7µ¸o0‹D°ËðæÉt§4Ã7BÄË{þ¬ -ó‡wùéŸ=0ã0Wô–ËpÎaùHÏœ(ë°|+Q2/ÈG2x™fûÁ}” ¶@×Ú:T¦°Ôµ ¸†(¯Ñl|³Þ`4¼[À¨)c) º$Ljísõ£KÙóèc¤‰ô5Ö¯âZÑlû!3W‚'{+–¬ÃR$%ªB­2[Àžç–•øçþp¨Žãêéìm¼©Ž3µÙ¸º†_·&s±¶¦A ž—¼£óRæÞ�,r.oÙ¬ÃÑDÔ…˜áCKÞÁ¬<QRSáè•—4zgyÞº }£å+o–LzTny¥Þøc)‚ý^ýtjDsRGÕûÎã‹e|0ç-8‰àÈŒ#ö¸ =³èu’}’áŒ'×HÆç˜˜N1M’•üh-ê$E€dQZH…¢,âR5’•ø°Î(1¬“죌¯ñå ’)Žð˜ÞPätCF— @CÏØg“¾�#Êf™ƒõ÷ ÆiŽÒ·7ptsñSÁÏ?|¸’Æ7]2¢®Òs0”q‰Éx€§B.#Ó,Ñ´r 3žƒ!,\H*¦­<RV« 6“K‰‚V)‡¸N£þm£™ 5 å`â&R¡$~Ì5ñc'ZàĽóÉ'ñ­soÇBpðmfÐs H—Õ(’'§ÀôæÎð‡}°œ‹1"O”ÖIÌ,ÇZ¢ß›Î¥„:QB'—0£¸î¿ïÍ(ŒúÍ›Á¤?º»™@’«wPM©…¬©•ÐÈ„ÀHš–®[ÚÔ¨2Ûtt4øZbÑœ×-G ´Ý‹(ÒLq)‘«‰YXÌ7>ñå„\”¨ð¸ R“kš¢$UÇÊ,–“ÛR ’c+îz©-,ã·™Ó�qÀÌ,¦ëý/`uþkÎÿ‚ $Ȭï¿Am811z®ÎQiÈ9J°„ssë•}£¤@l ¨W‹ÀÄ©§¢”U˜«O MF/·xÀ®õh¹øB8(bÇ&Š0¡ÌÈž]Q ½|Nê—‚!ì¡¡owFN,rÝŽ/(¢Tƒ@tÌzu,Žkv?AzNÁpý®PPÕ€×mØE¼¬òÒšñ ²¿ ®P<¦aa2Ñ5nn¬‚Zì7¹œŽÙþ_«y6jÄKýåôóô/Çýþüüð—Ínµ½¬«¿¼lvs¬ú__ß ÆïDr= ª3º6m´i» /F·æàªˆ !¦Œ˜Yán”ÌÏs3_wNµ9aŒ]ÚjßO&¿;Íw‘%@¤Ù`AÌ&Soó3ð˜ $R µu»ø?cü\ð‹0>ÛÀ!à“¤Ã%¢n[ nƒ.ÕÌßXJ8ÊÌp0‚p`ƒëÙ°÷»3zRnþfRÈÑhêe¤¡‚Ë”2]V§ùx7°dyZcר!+dá�ŸÈ¡S€§7–ù3;Sµ« Oo,ó; ¨åmîpOGK¼¹Ü R¶WeÂ=N¤Ç‰ÃŠw½Ûaîp"N²0>“.á.'ÒåÄé²9Çÿ¾h°ZÊ}†ûWšJ§Ík†lhZ¯"²UøRClk˜ï½ù绾¥J*«NF™:ëîýàãôáŽæ;ظ›WˆÀõ0ã¤Â8 ¼7‚ån²Ý|–Zð\³A£º2t&3ÅoÌ@ë|7ä¨Ì¨VQ1Þ±Ö¬°X÷ßv݇t_Ÿ«Ý…ÜÂô~õ¸Ù³UW=Åß'ÿ®ß4äd³ Ôc¦"¦hÃáåøÿ²÷%àmÇÁïá"H‚h]”,Y²,Y–-ËÄI@ÊaФ$Ú¼ÌC‡A„0�Š”s9>â$ÎW[qŽæ¾ú7qâ$ÎÙºNSç>œûªÓ\n;‰Ó¤iš¦GÒ䟙ݷïáA¾Ýþý«Ï0ßîÎîÎÎÎÎÎÎÎî&åpLŠê¾þþÁan TŸ;\¨ÖuSÜñüI‡Å9·hž“^Ë¢QwF’–OzKž^)y’½ ‡ÉI”Ç b\P ©E¢¹™Æîöî©Âb~l©>2™×nÖT³/Ä7ómoD¢è`¾í•b¸Wøôô¡ÑÏb�²a¦Ü0-ŒJ}¥Â"¬sg íňY ]Ë €Gx¯áüFªx4&¨é~±#)xà%ž*Vú«y¨œ®þ§S#ò>Ž…!2¢ò¥1iµá·0ÒÈ  Ë%¼nD—£Ö¥¯ÒmšY4'}ªy£E%‹…½L•Ï€òSåº÷N±R»F9ýÇ-iŒ·ßb;ëÌ.®Xož[—øì3ì„!¦Xä g7�ü)9ø{Ó D>"ˆœŠýÏ r,҄ȱȣ$2Ëü‹£TÌId À²(%eQJÈ¢þýS32³2ŽÈt4‹ú‡I äêæ«õ¾¹º|&"ª 1Ga¢b–L¨ë Á"*-ETª·5f€tÄŽZâ1¡Æ4I³DJGP“,’ÒR$ñc �Û’þ°¢2cGbin¤¼T½–”è)ÍöåøFÌe‰ªY6¥Œœ+˧´RŠY> £…VgFž²Y8R(Í–—y@¤qZbá9ËNx…â¬c<Ëdu›xZ ð¦`<Ì#=rœ§yœŒ÷M M Žè¸FÈ <í¯mÇöSˆ$[¥mžG¨ç-óÍÙ74QÐb„c,¾ÆQÆc~Ç £ š¼*ð4ß:\Æw{†ÔÎ’Úll". !k3°„“C6Â.èýcãÇðV ;µh·'ŠnÿÖÃÃüþ©%ïfP/G’Ö´í(™IIDÐ’ë’–K®…+âé‰É±‰ÉC}ヌ(.ËjÑøÊ¡r}²R®ÛÙãøaJ)\ù¥)zås°ìû¨KkÚÆul2ã^°uÛ¸Ú_†6kŠÖD¢²5B04È£µbä»öúŒOÐuŠVÇDÈ£§8‘¯‰�´`âË3hqê&’e-R]3¶˜¯á•µ²Eã‘ã-nW*« ÆX.Y# ‰§I±WèÀ§Ç£ŽNÌ6Ÿ:gRBÇŽ‹ä'ºß­!U²’Z+Gø)ŒÁýÓq©oéX·0;µ�}wWF’B Ž[CÀÞVÑGc™ÇèSÁpÏWGF .×ìˆÚ@] $ãDãD#Âꜰ€EûñA™1%oÆžG,&…€sé9»ì´KIKZp`ÃH÷J¤“Dw ÉÊè@àÃÍàc=<šš�þî›’CRZ'"Â15UÓ5'¥OÕó¥Ùü¬]«²¸”Ü?É!±÷“‰bÛ;'Syû ]k"/÷„–¸c)û?¦úÝTØ (ûž|Uusr³òy‹ˆlÓŠ­M'íMZ±7Éj¢­ANeW+“MD8¸j Ê–¶`íÒ¦&­9 ÑßXŸ4çD”='‚ã’`4F&úŽ4Œò(§ßÁÈ>ûÍÖ¡}UçiS¬K, ô íÐR#ª9 ¥Q4Ê êãO_8Ê’\#œUÝaäLWÖKºË8eS•hHm¥odRBÈŽMˆ‹Ž\×äSÙ™á|i^MÓLfôbÉVÉÅP›á äÆ`K§E)Õìr©Q"‰7`( j‘„b¾ìj¼ohÔ>W£é¬–Œ/ÌæTQ æ«YK¥—·âŽg Ê÷+¥îă‰¾^®*hîṡRnº2+ù$©Åê¼æbn׸f^×:ŽÜ©>Š*�É ¥8òë#“eCé”BÊ>a÷¸Íì§ÑI3(J*,*aÄ]SѯTHòUõˆ ã+™L8àj ’·’J 'Q-i7D#3Ô¯Ûˆ"ôÚE´gn(ÇcV®ÆPõ¬‘š©ËÌ“ŽXžgÕ Zë¾2ˆµž¨Ñ.+Ûg(A)ðég¢ìɤ4ÅFøý†Ñ‘C²â58ìªÕò¢£» V¤Õ[9yéº%¾ö¢L®YŠî$ l[’줚›(‹âþá¾ÉI¶DÈg6ÞcMLj¸+s%XïŽWË9Z¤Ç¬ii¦¿Xƒi«šµ«ô3�®G3‘,9R°ªíMZBµ©ZSõ¨q·0SÝŸÍŸ¯ª«å;ël|Áe°ÝúelÈ0ãfÛgXôt땬(\4’¥nØ+7t°.nÁYD?"‰žøŸHôXä4D×LeÑ¥ÒЫvâ:ÑU²dse¿%ÀF¸´gÏÛÄéý,T Üæ6{WÈÅuc_H¢;úB-bí}¡hÞÐLw{g0ÑÁônÒÁG€Ã4iKdó$¡0¹¨Pš‰N>A^ià¤lCCƒ#¥GJdÜ‚Ô{JTâÿŸžr3"ÛyOÉ’ã)•°÷”#*¥FT*aë)™¶ åVê—8ÿÉqEφÄãn½5§f]«·æ×wQœ]õ½´¬iﲃ&—fl;�²f+ý ùÜqv §É\ ±ÅÊt)çHTWo 9Tsuïb©¬k¤ÌóãG¹­A¼j' ; Èñ’7>ºÈ“V#F�÷»BËQ“VŠš—ËÚœý#GyÿÏîŸhâ‘÷dí´T‘Ò½ÎþQ ÊáC©®¸ß:*MãQeö¨•mZŒŸÉþ‰±áaK½öÐež®¢Î­sJ#Že^‰k“m¥q]ïç~)Ñ~º¾õUšª‚ŒÒ¢Ó,N4 ¹YÒ¥‡–£éI™Þ«šMý ÒK¥Gj¥Q45—õÀ\@î!ãÃ}ýƒò‰\Šyõi#’í&)A¤ÚBy¹qÖÁÕ:ÐκŽM.eÒIñ8"б1‘×9ÕÜ()Úôþ>ɹh±4BG…º PLÅYÐöØ:aA+Æ–Fë¸cœnUdH’�'Ä!8yïbD; WÖ®ÌSÎ ûJ—åÊnD+o-¹X«Øžá'uX<èoõè7L§“–V䨄n/î,zªô‘"ìÆdåwUÉbh7äGšGpÛÄ Jˆ7é •Ž} k£ZXsÝ]úË¥z~…_9Ò )ýõj7 ´Ñ½€âO>Â{|i€\޵eÅcö-¥ÿŒ}µ®°ä&°f[gªìeeÞ%8X¶ï;Úß7<¼¿¯ÿR†•¦ÝhLzëEcQËIäöÙÒˆ‘]ÏÕä„«å±åRãNp£¢¦n›»ï$UjnÚZd%“ЦÖì²Íðªî%¯]Ã*CË}B¦ú¡|±Ò°YˆzèHm~y¥?[,΀|Ý+©cÍ[êöB+·ºÏË•Nr¸Ç”;`,ΖFà^ ØFuÉÕ1Ù©±ô“Duëš?'Õµ7€Õc‘ÿ ªK:I;v4n1tÚIuµ€QLON/2õÖ6&Š7Šæ†J ù*ö¥-kÎv°Q'›¼Âí,Ÿ±ˆK,„™ðÀÐðÔàÄ¥ƒÇâ4ù#÷ ;’-¸¸…òÅìI=ž-èùJ>kË 7ãñA‹\Þ–"“kXr3ä¤{‡†êÂ[;ûdKz2ß14 š¦\\¥,’ÚkaLäX‰‹G–Ü@¤‚˜P.Áü@³ÍŠx4ñ(]óÜÑŽ&ÜÑ–Ü(=K±:Ú DÊÞ„R8‚uѶɧð%±Ñ]ëMºÎe‡ÊÕk&+ÙMêú\†Ç1í Ì<S…z1¤Š÷ kJkqÎ~ý 2^€",± !Æ^rˆ8³à!Å`"¥Ú—p¶OõJZ<§ü”50ʳ^‚bk ìž¤>‰´£ B.5”«-ÁâphthdèòÁí „‹5ß×Ñ]/¸uØìƒª Ü2lòAG» }Õ*ˆÖ†õ4o)£’âÊÆ&PÊ^Myìs;2²OiÉÞ¦z–»H-²’·«N[ÌO•‰ÜDÔËlñj“R;âkVŠÓÑ(£šÀ•RF8ý6$K £\£ìû;:6*¼´ã•€cîS;Hb|ÌÅ¥Ç'sÕr±¨'0iD‚¼¶LïzžÛ]<wd’-—œäç8Ñ1âÓâÖøÂä¢K¹|‹»•h+™oq/Ωd{Ùx©•¦ ;ºâ)JJ°#Wç0©A8‰ë™ŠdYª9’ñž"‘<"váìé0þ{X¸5v¡èt¹Í^ÖMÁ¤W¾ÖQv¶v沎v=ÿñF”•o–î¼Á7á7á´,Ù…7¢|i•ÝÀ½\n#oôÊ2í¼A¨öry¼¥M)–û.¼%«|2ÎóBcr§ËÙOl–4“S Ú4‰²×;(C}Ã(b”ÄH?:)Šz‰ðƒ/Wë] mÑÌû³K³.þvx^°³¦æk‹i"Toh­ôN ÷t�9a*÷ô(û§[°r{úã#‡¥£ÙÈÑO&9Ôa09Í Gr�9Ã*Gò({’OŽML?M“ìAŽäî»1î“ìI\ ÎÍåsŽ}x[’ËBO¶_¸¤ “«Ù9{ñ’Fzšm½‘Ž6¤[{ÿÖb² œªköÒyiOÔ‹×6pl0ÃÃj°1Ú^ÈXu¶PÊ妎­¸Ï¤¢ |ðÝ ¤® |ð£ì„¯K.'üÿíÕÇӫ⑼ÇÖ«r$Ê- ¨8¾à!gyu|!Ê›=“SCý—ÓÔá´ó@Uät= øXp]rrçÓ嬢Ž*Dù¬ÂÔØÁƒÃºj.)<r\´¨®˜<夣s­îëë®Ãºs;í>Qì|¨PŸÊ×€E4ÇiN¥íà¡Ò\ÙÁ˜¬±k3zÜqˆÔÛbHö7ƒNXÐDÓ‰¾þK tð°Ú ‰õ$ùÅGFY$íõ8©1»|¨|"_Ò<½¤¯·³bFOžííIŠ›\RÈ:‰,LäSã#ìÑ'ÚiîjXÍ ®ÐÙtk£%å}«QaDh¦ ïÇ’e7Eä+âµ¥ˆµò¥‹Eh#®Çé8Ç7þV³‹ÃŽ]&Š<äØdª7P³‰w¿ªÑ”]O7'ÈD-=)Ó{U3ÄU¦ ) Ô*J&¦µÄagj´GK•{qc²Ã„—¾›·áÜ۲ýpœüX±/¬#{I—ý=¥óX¸1æ²›£ÚÆ™•*‡¡rØEõý8 0aÒ:ª¿¿o¸rèòÁŒwi'­ÎWÙã·R´ëå¢ûãöàMæ0‡Tʵ½ºÜœÚÑX#";QlÍ4“ÝëQ £md`Èåxk,FƤóD¨p­¹†lW{-!ê[¹ vŸDÚ·œæ”eÎqÌÒæ/øv'Tõ°kC3D»c²»Åõ0Í d·+?}‚o¤ÎIDubêXnyÿ­¨sD¶[ ‹XÂ…: JŠsë®v¿é;jmgÆx³¨R·ß¿¨&[Ú=·?Î&.™m¶­.^M¡y𛻉WS(³K26_8[xŠ&©»$èŠÆd9UÄ­Ë/Òl‡t@Æ,Hæ—Qº|ftšIB§"Qº•`H4-­6Ð0rTlˆ‰hk*VÑÚLL©Êg ¤$"·~—tÙÇê�@LpMY ÂG¨orºñ¤f,Nf€øã?`aÇÀ:_áxû@;ª©{ø¨­¡F<E{äíˆ0ÞÚÐJv¶:0@ðÃÍ3Ĭ âʘít],WóŸ¸NÕeœ,”Ž;§¿å‹Ž“ýx'_\Q¨–+ cDì}VËE‰ä4¨áÈl“§AœÉ’kŠk|ÈÄ ™² Éo�o–’i8ë&ÄNÓÃSC ·#œfèºÔâñuñø¥ù“ÅBï·$¥@¶BleOg¤Å5êôøp­x€ÜþÀz€Ü ä‘TjŸxÛ8n;[$§ºŠû´(-Zœm"—8IJ×ÀXȾ¡ ´]!¤ÄN*‰-.ÏvN[ÀÒCÔÍq3FÎÿÑÔ²þ&¼uŒwÅf>iÓáfíSܬcŽ[¶œÒx4Wçs #y~ð«v\Ç»Ù9›(dCW´HZÅcärï G°²‡Ç„¿l‚uGvyoò¿Ž<½näIk#߆/7IY¼ìmÜ @²DJi‹½I}$¬4˜"¬°5¶]BCKjMi+ÚÏìRäh~¹ÑÞ`ÂÕÈ.KE…Á¡@ŽÍ”º*%äsßðÔTß~MKA[¦Ý¥EâCÇòj¶ÃûÜ—K5[¿å&ÊË5[×�ì@9·T³÷`�mѬ˭ˆóº>—;©Å±>W©óÛ *Ö(n¿ºP ;µ1YöyJ-·SÔ¥ é ’ïkØ?¶LWóÈ$˜ˆºÐy°‘WãÕÜþ¥z]÷‡”ÏvÖЂ­'*kÎ’ýåÅ™²>MZ«HÛIK6œa°°2A®Ÿl­à†J1V²q7�ÉsÊ ã»C\`¬u‚N³¤ÓŠ!i6çb‹qžž©“9¼®Ûæ´—fŽÐjd”$Gà3¥ãÉ’#ÒŠ#ÒIë 2. l1i`•qŸÖ¸8×bî ±?[mà‡Wßoè=/Œ |–Š Ad¹=‘;í¾¹†ØÄ /Q‰ 7AÞ¢&¬jéò5e‹÷hŽòZKÎ=ÕúÓ›Ò-Ëæ½&o´ ©nSzù$Åâå8 û³ 48 µ„ºoÒ¶œ$Öܼ¤l^B¹°k©òb»u³€ëwò¼W÷~ÄѪv\Öa9®ÛÙmkµ¤ïWsbkØIfWNâÓâR8èòsåäÔÂÒâÌT¹b—¡¿¿ Y¢´ªV¤=šM¿:_[h„¦ýå›Ä>M ‰7ÂìD‰Èk#xæÓ"!!’ŠlQËEÞÜk#=…åI'fª91Åæ‚‹øí/òúáe)~Ê¢·ñ.*yG•¨@ìX;v-r+"Qxh®ö)f{÷ä¸ð¬ÈB‡ �µ¦„U8=¡‡¯{Ó`åªóy–¸¸ì@šùt2*VM);Ÿ–, ˜ñhDuIÊ2ôéQ R¼+3954:>­ÝO§·îšìéfs©ÞØËàz$áûvã®’Ô}Ÿñ¨ôv¢ë0uŒz›©8ÕÜH¹T¨—ǰO¹zÜGìWȧÔv‡U+c˜’²‹R€ìe†$P{ÔAÌ8!{ݧ•'¦è7‰·ÝëôðY|J¢Ò gƒ¢+ŒœL”91.͉:øÙJñdaü©i¦z@@¾`çÀ‰› ' ¶ ºÂHI¥ì‚q¶ ^º`xع'ã`´çÄqµ­¦–¹lIäóe.Í»&§Ûs¢!Ж¶¿¸0é5’ײj—É¢© tÌ‚6F&Ý äèfßê†mÈE<Œ¡›nø<Nƒ¦7.؈ UiwGHv°2 "j8çœMƒäxŽK1ž cX°ÈÄsžò(ƒ««7„7š3UŽœ„u$>¢%•`q L;²*Såe)[Vºl–²[¤ `Ê´ú“éò<«Ðø] ¤óm<©.žæÕ°:À·Ÿ·éêKl—ý dQ—GDÅUån°I Öæù-Óåæ¼xaÐ BRGÝ A°–ÃõÊ­wqÔ»1]Ýn¬¼©Rs^€²ý©”ûAÈö÷ªö#hãŃqé÷&PrrKy¼!¨í>H ÉL rƒþdñ”d„ul¤HXµuÙm‘@R”¤¬­(*šáNBÊ'=!è Ù ¥l¸ÉˆÓ (aS¬}F�jæŽ6L " ñ´Ò©XóJ—€êàAÒá¶.$,Ouº0Jhé£Öust”P’iuúáåý÷¥bmÏ‚1Ü?uLÜëMeâîï¶¢£z´¨‰Ý19|pb\ÆÆUìÁÁ1™Ð#­b“z´›‰Ý½¶ê W2¶kÄ»©»ÅpNÐ¥ùòêrgŽ#ܰˆÈ‰Ê<±æyXùIì«¡Ý©/r%O—KÖÕ˹R2—vµúpßèÁ龃ƒ'ƦÇíŠ|b7ëñ ºIÿô9¹N±×ŸØÍ[ý ºN_ÏI¹†¡¦áÁ†Z“œ·WæM=|^YoZäe8±;¦=0=$qvfŠgb7¿¿“Ø‹Ÿ6c*N&vó¡ÀÄnñcêÚ¸˜â'€D%­8Ãèw©7Þ£ i�áb⃠ŒL5”U¥8!d!±æ…(¦Œ[Lé$KJ(¤‡+ÜÐI*tœ²^ ¡*™S™²q9 Ú0žÌèo7&èm€<ß›ÏcììÞg‡҂鄇³&M*Ù ŠåeTf”î<ß «n51x¢âP¶ÈMGdŽ‹Ì*fË C`jz`poTÇFŠpŒÂC“cѽqùÛ› Ï‰ý‘ÞdroRäéQ×KŸ&@\ Ã7ö¦(jìÀ¡~Ð%(*MQHñËÇF'÷Fzl0’õ)aŸ‘ÉU ¨ ôÅm¬#Ù•þ…lµq}Χæ0Ql(ö¨ÃæÃùììþ“uvséé¥M¹4. ûÕéçÄ8bfKaVHH™† Œ\~¥ñõñ'ÁéRosæ¤]¡8ÙKp­e¿ýA,d¼õÖ\„ï߈öÐ9Ñ>5®’<ÈÅñ3g"卵?Jµÿˆh?7KÄÿû¶_ž8­ý<Ú“,WÄ!5g"Ë“¤”¼•É-U«ùRîäÜbY€Ž¦Å¢£K‹…yéï¬Ú%äò|µìðª8X-/UÄ% º_E±2ϳÅÉ|e¯ýhE±2µ�ËÌliVOã5äh~>‹ÇÆÐ<ç$ŒðÌiHLó±†~nÔäÉÅ™rQ•l]==118Ú ¦d–„â,›K:O1½r’D@xÌ?â Û“G<ë F#ñ¢‰'‹xÑ„;ñ˜³zYÚ÷FÄ“é<'öJÍAså{=,éH§¼ðq˜QØ´Š6a À¹ÃìPÊÏ:RÕ-„opbR{e#ÁÏp$øW½Rt"h¦D÷‘XGœFŠÿ·8@é‘ýƒg§XfŠH ©,4SRh"˜ÕræzqðèÉkùÁõZË™-S,-ÅY£†T—)).LŠTa¶:Ž4)Pp dNdi’L9ŒHNs—€Ð±æ$Ù=6œ9‘Ç!Z˜å:ò=£VÏÎUnèèàÀ¡áAi¿Oîûë³Ë“…ùR¶¾TuGò4i2 ŠyN™tº“i‰Ã*QZRÇ«åÙ¥\½!³²2Ú¬Ô†$Oög}/T]ΡR•u:ª¡5¦ŠS—q`´ÚÒ·§0ari¦®¥©-*LÈÖóþ¼Ae%Y˜+ÿ€†~¨c?Pcw<j;A¾¾|z83:851896=Á/#÷î3ôa®\qöë”­A,` µJ1{RO’½9ÝøF@±BTXZ‚ªX™È/–ëy{š¼Q¥¿¼¸(¶9AÎOÐé' ³ì»l›Ömm«cÛˆ^½»Åaä†Ô˜H³Aéä9"È“xJÉ£I³òPšy(Á•<¶‰ÛÖ6"@R á §öŠÔ“§¼þ±ÑÑÁþ©áƒšw~ïî´t%!‡3§xhz-¶¢Tš —î{wÇ4©Ð¸Ë€û ˜`6UQ"äÅ+Œ½»ÅJ  L„Ù#ÊML»4‘Ù {"ÚØûÄ´‘;Hض7—FJ %%›ÇăßýÎ^¤ËÖS‡¸œÈ]F¸l«ýétõ®ƒ1Ñ„7A>ÎÝ‘üh‡�µ7’û‘_ìxÜÔJ»ŒÓGÑHî§(Ë*~à ‚åUT ,ÍL¡¾Ú7œAÛDF¹Éö’ý°/V`ý‰š¶£‡X)rÍ.ªcé%ñp8–Q)(0ƒZ²ÒÍЊ&N‡S!Æ;šn‚–„ãÁ“ƒ3d&GƦHW\–Vb$çé [ªd`ƒlV3óãY:E`¿¨©±f&a±yÖ ˆÇpLŽa„nh Ó7ÖÛ´-ŠÀ®m§‚›µE¿½©±f&ÓX¯[[$Õ¸ªó.ÂXûó½dŽ>|®#•´?šéšèv§ )vÑü’ÍÕóÕB­^È5`ue¡>`—Góuk>gý2’$ ¥ºôÒUË*ÐÞ©WÒ¬5“èçq—JJ€TSä°GÈñ£§Æ'ÆP?ï›:ÄœgN‰KN‰÷j/Ð7äâ>‰sDZ¸×²s®‰A˜]ú1“È˵%xŒ%äKÄNŸëK0Óó°½ü¬• á““ãcÃCýCƒ“²:–4 ¥’h»2¹¤’÷Å{É�(s!ãñÄéä¿äég|×3ËÀu—4á´¡“ÕÆ*uô¯Ðj‚pÅJ´†I'L|§cFHJFHÒSäJÇc()» §2[$%[ÐþŠ]Ë`U‘i,Ž27afé•Ì‚À¶yŽá˜ß…ÿ›�“ WrBºL -§±d“ù‚Á˜½jx§cP–`â6Kw˜$%™.öèß6¯æçaÅ61xp²oKM‰½Ý4.ŒÙâR>+�šÁR»éÙ©ä‰|†J ³—L)æ‚R&TêU;ÛB‚¾ž•Gì÷ Ž ½+%VPX¹1n‹‹xÑA”®ð="ðMºã«f¨'ß#¯^WRáËñ)Ÿf|!]$3Ù{DrLeãøˆˆçõ<Ê>®Èáª|rpâðP?nªõMMÓ6Yz·8´%_EW9}AÉHXjµKw49ćïj}¹ªQ$)”bÑÁ•B]÷s“¢‰«Y–+ÌrN0Ë/?/«gˆ4Ë ^ÁyHOÐ.ýÑ›ZǦÒ˜Ët·ô˜H Á9(–aÑ/('Cÿ£é;¹|­Öp¬ªÕnË’g­›PŠ›„M4Þ×+àRܨàOöKH¹9›Æñ“·â¹!q'Ô‘…Jc+MKoHÇýi­@ayíVž/J“œ„,BiöVõq±ô´k²Xa¹)ûlÇqÑôe³~Iü‹ÜÝ÷c¸Õ*‹3r4uŸ.ÁpIÉï1÷ÖáÖô6i¦î;[céúº5GËcÙÛ¤5.-àxi.2¸µFö/÷Q”¯ÎúˆÎ^ÁñÔ¸TʽqB01?‰Eù#€gI•¢*Ú„e>îB±Twiì£ïÂÇÐXî©(K±àð,¢R,ÐÂÿ²éÁ‰c*#¨‚†Ê.M?¬`¦ã…nfÕÁjµ\eÑl—ÈÅÊþB)[=‰+UCn²³´%DÛMN+ôìòTv^»‹^Xò|Þ²”+äkFh‹äÕº­Ru··o*™ìN$1×±@öÓF 8DWò3“ÅbOùõ ;òGžü6#·+ù£‰Ó?šxò3SÇXH {Ìé�YJǤ”&›Œ=ÃðXÿ¥ö™‡3sCµá²z7&¢Æ8FZ³X⌞ù>>°TÍÖ]6µOS©@•Eµ°Ë<,4 ì¸Ød iš‹™+y ³D×cl÷AœÅ³°<,´T#¥pŽÛΑ¾¡Q寯´ˆs'Çe' sŠ|ŸÃ5«¬‰Î6•4ÛTDV…åTß~òg”·V¦É¦RKvÚã$~6Œ°Üs¨×¶‚ ¡+akµ “)!É”ˆ¸#Ì=O.m.kSW©Ó"ÌLHí4Ña ÇóPB©§tÃC}£�ša4Éì\ÐoBØgA±:̲—MEiv$sB1!ù†ʆ%!Ç1D\,ÔñȤc µd9U‹„ ›#ezw’iì•u¥ttfÊ÷ƒ4û+¥¥¿Rš¬/s\™êÌÁÉþ‰¡qÁðD%4Πq~ _ËU ÚÖŽkf±J`Î~ï§cË‘Á )æ0ºÏÂ)m¿À5³¨Œ¹C\q8¯½r¼b\’ô ÊZ‹´í䭄ѱÑAXŽXƒÐ×S{#¶¸ýccS{£zÔô(Ì(##Ð…{cb5£WEH¥xŽGw Fƒ®±“µ�•Šš±Q2•W”Ë=Å—W8¹{R²{Rz÷èžžär^K‰ÛÄýñõñ|µPvNù™r¹>R›oØ8Á½ì,Ýͧo™äÄs×}±R˪¬XŒ”þsSdE¹«S6–hË)-R*Ùœ̯éÈ£$†¾ó¢ÃÚsyÄÄxX0»§Yˆ¥#§¡„enIË3¹¬Z._ãM– Æ’--;áO³èà\ÜuÑèìQ\.>A7sÓå¤ ‚å~¬§™Ò'y<Fz¤ÒYš*2ÓO<-ÿ°ÐÒLÐ#Ç_,µËmš”ðršŒ6™&%\¯,W͓ѦJŒÌ#uÁÄ.7ËPÒ†Ók"b%So.jÂq’¹“OßVʲô£³¾Öê³ÅÂÌžc¶p"S‡Âb=d`‡?$Y¯^*×um¡ª.ÑBµ¨åŒ‰œq=gÌž3fåÌ”Kù•B]fNˆÌ¤dÂ_©cî2Šz)E¯¾$eI>¯Ëu$å†ûŒÅl±XÎer K¥ã —"t4 rVªù™¹2f¡­Gø=ä³³æÃÎ(èØØù"+ ¢™ã¯ ì‹¢†:FÓ2„æX4ÒSB\$�ÃÕf %PÑ;›_¡¦Fı2$&F/f+2ºWFã1d™’‘Üàz5Ÿ×[MF‡è£jµ¸N„òÚ› òú×…B‘o€ò²ÈlU8jq,_ýÍÝ+Újí×5ZÒ 8ZQ-"ÉŒ7\éäŒÄ­¦F-?O~b‚È¿:9#®v³èP£=H%qÙc‚m"+“"¿’Ïe介1}«eQV&éed@vÈ Ó'&-LÉ/ð$]=ÙSäE|¹‡:_ ‹ý«Åñ:ò„Ö>›_®hq<aæ³µz&;;[•í—fˆ{›J D½\Q1¢ÇêÕÂb†ž{å"¹×³ó…œŒÂ:¦&ÆJA’‰}\0ºFAÖQ“,p;?Ř¡>ÞÓƒSO|2r¥J›¢×â—•Lcš@jQžÝÇ!ƒ—kR×êù)Âg:k/÷ ^Ì(/°^=DýeæŒÆ$¤UGÒýÒ5Ñ“Qºz2®Óˆ/¦žÏgØn¾š--³ÕBý¤­+I,Tóµ…rqÖÖ£Ô+Î$Ù©âÄMÆÞr後9‹K(`ö²¼EþçÇ1 »q*ÐX,ˆûÒt«‘C¸ªà QL&›»z©PÍg€�Çeiµ"–B"0T5¨CÙxld°è ?ÇÄXÄìDkƒXvœk¬U°rS(Á4²È$—õž¬Õó‹3ž‰ž�‘«eµQ:à&ÆIE4•GvU„Dææ ™\±\[‚vRWï= ÓŠ À³NðÇÝP=÷"ÌPU†/͈ž2 ³+¢-Xˆ5õšˆéé/¾Eõu@}!›*:ÈÑjTÕ‡^y1?WŸ'¨äK &Ófà°*zH!µ_Å {1u®6&ðgÈ£²¬—›Aáj­®²Vµ¼õ*QÄŽŸlz„´ä±º5TŒEƒ\±¸¨·ñRÙ¦~ª!GÇ­Ø ëó€õÙߣ}G¬o`Kã ˜ÉÓE´.»,R)”Õ¾sµŠª ÂSGiM©—uòQ¥oP'0êS.—jÅ9÷Ë@ÌF4Ïø²Ž«­/ ‰’ö­°0&ÆÅ§’otúCÆ8AZ<Ù¯â­l¶Ñ2 <,HÄ ÀIW1<è+zçbX6´¤'@¿*æÒ…¥ f™Ud¯UÔ§HÖL)K #d¹y¼ÁE]”ß!kh˜¤Âg’e½‘™_̈¶ô¢…òbþ¢ú2Œë‹®*e/*Ññœ‹@íâ\T«æ.ZI%/‚ï=9"`\Éd« «%­`ÊIYQ¬ÑC¨å s"Žz'Ž2 {H7Ì€ä|WT—RÒ^$v—!»ê]šYÌæfórÐ=¢ÒméäPSý×ÃMUÈÏ•–qÆ<a™îªäÃ¢Ø +ƒ„5á&š%°•ΕÂq•½jÏÎÖoòØäá ÿ¨V•SfB»£¾‡´R‘PHä‚ ¡Þ`A-®MN ɆjŒ¹¥’ Py©–¯ffñn!U̘Y…9wUË]mÈ]uä–áL†.–,,AR=,¦½‚ Hc…qRöwÄX¯†Xr—ƒ±6k†×¹ØÆ¾œ$Y‹ 4ÝÙȘˇ©W2Ko½Fèx¿žVi`�Jn‡ˆÃ»-é2Ùùù*¦Ë£2‚DÂn'Å(b$’ˆÙ™ %éÄÌCÜV%—¬ çÌbˆ«Gdªê™ô<U{žf<çÚ‹Q‹­mTzjþm‚Ÿ‡¾|""l„?a:Ó?éoþ¦þLCþ/­’é•oáPCú;»dúµ~ØëLU·L¿ÿÛFøM ùýgªük„Ïö8Óq¶Êÿ]#|ACþÊv™þž~ACú¿œ+Óo…ô›6;Ó/{ºLßú[#üœuÎôÐEŸ.3¼­¿ð%2ý+«ÍðçÚÿ›Q•½þ—†ü¯ŸPéÍp¾Ý™>•Uí;× ?M뿇û·Ïý}|¥ß0ŠÅ‘l¡Ô?1E›ÖKã' #l¥P7Ö›„(“Ï”ìÑÑjWcjföd)S/Öh6~NQõ" ÒÙz¹jÜÕa‘:›_1î•ÁÖe|OAm3þ™•b&kø‚üy±!È x}­q0謑$>QnTe¦œq“üœ5^Ÿd^#èb½6[-×a)ZÍå7¤AÁ"éÇΤÚR™“ðßFCƒïEžc*2RênC‚�rÜäÌTò…X*™©.•ðV:\n•sxO'ÁrPü�Jú–Am,gæ‹å™,´ÀjÆjÓ›£ØÅ.ÊŠZKðçVøÂÂ3Я™‘)J=nȱ�´æ}˜5³œŒg¨¡°TEûCæxþ$Ui|Ã�åy ¦ßOõ¡¦†'U_à¿ÃT]; ÕÂ×ó©UÓ£x;If|rpz`,318<ÖŸšÄëa2ãÕ¡’Ï1¤¸xù[¦…C­D†9ÛnHYp#€ýv·Í2áY0Ò¯±à*Ðã‹ÙºF[” woüÒ‹PÈãYJ=ÏPrȇú–sÕzyqƸ¾k¨lÌ2âA€]†ôÅH^Λ½ø]£@Í8ÓG)żèÜiCŠŽ?°7Rsª'Äz73í£É”lqŠC-ŸPý ¯^*CGŠ<ðïJæüE§aÜA]ºß*b éýX…ŸNáÅ¥bÙc~@1…è …6·Èt öÑX¬”—*êçU¬-æDÄZ1"·X¡Ðp±zanŽ‚Év ÎDE]ÿI¡Y®ëœ ÕêÕœÈÚßI°…ùz¾T£˜%ŠQÁWˆ`¡tREá¿%Vÿ(ôUA[ ¿ÃD6XvŠ×¨<ÿOþ{›¡Ëÿw†eñ7~„¡ByfiÎx!P�Å?EÑvdlbÀø |Ý?66lø¡úØã‡Ç†Œ]ð5,>{áSìÿƒ˜>4‚wZxÈô÷á%Îý—7Aüá¾ Ü&3¼Àp šÒ„“™¼þkCŸgžcH$Þ`XH|À`$Î̦ñf%ãS¢³×Tè2:·JÈÌøÔ„ñÓ¹oc÷[ñCƒýScÇbQã퀷{ʧÜSŒŸi-÷+ÿÞÏnñ3~øÀajw¿¡Ï+ÏUí~£ÖîÊvŸiµq—ÕFü×bêÐIƒdUi)—9‘ÍàÃ*Æ{!J~TÕ‚Dà ðŠ’ŠÔÆdö÷Mõ“›óÄÝre\ Íkšø*jï…^no>`mJ£»n,AHΉ•Z~ æ7š3ø„QæDÄøêé¢FÔ×�Òðß™†>·b[ae”Ë� ”üCŸÝ þ)üÆÈ Hpä³å埇”âÉl ºþn„²‚ß7i:šNËL —ŠÁýÓ T‘Ç›¤½Óã’Ïx.ähˆ|¥WÍ]8[ã4]7ÞÝI-6ôIôzCuýŸ7²ËÏ-ÉpJJ†Ï˜ YåÉÙö«¼¿h,ïVy/—å}ÖQÞ7 }V~¹³èB¶úØ ÜD“,m@ˆ"<¹ƒ‚ºè<DÞ-¯ðé±0Ï× :ì×£ëÆš€žƒ• ¹æÂãCÆ‹ù/þ»ÌÐ ÁÓÃåÒ¼q)`P’âßì÷Ù™bf©T(—Œ÷Ñ´¿0ø=^-€ÿ„N!õ—jÔºÓê œè-*XÙ¤*òˆ³E ]±š ”TFëå¦LJ ]ϸIµÏÈñu¦_YƒjÙ1¨>dT÷fPŸfPý©ë :á6¨nñÊæ}Èkë‘_è°ès´VÐHª:t²¼ÌI£'ûß_Ôúß<8:½µk|ObOÔ0ÉAí.$(.d‘j]Ûí¹0bUë"J:4Ì‹ õFöä„]¡ þTðµ,•j…ùR~v+¶Óãi©-”«õ­öXŸŸÿLåµtï«Ì¿F¤}(Œýæk±Ø €Tà3€ÌkXlõ¼Wf-$ì=×ã·çæö*â_K­Î†çF‘lÉàžœáy‘HA¸P„ô›D8 Ù¡,ÞóbµŠXšƒ±bx^"bº[2õÅÊž4</oÜ¿ ©A’Mž›QÙòxü‚¢á-Ô;QÌ�‡¨e3¨’ED°Å¢-gPŒ-߯…êƒr*ó=ˆÅ÷C¨Òy|¾¹bÖ8A1Ÿùïþhõ‡õpG£ÿòºiMþ·‡²ì¡yø›ù<ø;ª‡í¨BÒ`ë›-/Í�9CR%òj¨ÿu†=2>uL|ŽN&~ E ý^ü;7|ô7eøñoÿ1#€ñ²R£¿öO‚j¤¸¡Éñ¾©þCF+?116a´6¿¿�¡¾Ñ)#„éÑKGÇŽŒ”y°9ltRí#LðÑ%>¢Æâ#n¬&”±FD¤ŒµÅ®1ðµžjC‚vãס‰ÁÉéá)cP݈“}û&&úŽgRÛÄ÷&*drpb`ðÀÐèà€±#ðe¯ ã,ñy¿·]û±Û·3Tö‰…ÎÅ2!.±{ˆÞû‡Çö£O(i°oÄø8Æ& £OhIƒ™±ý—€l4>©`¬¸O©òḑ)¦ÿ�ˆyúž"|Ö]%êþ!˜tfãÚ­¿–<61xÀ¸öZS´ý®€×^+xcxxð tÕþàÕ‚#}“—Œ(W1‚FP_l˜›×ù‘i7Áÿ;ÍU°²,3dî2¯7Qˆ !v9�„» 0¿.X½fA­0_ŒÑ)!O}ÈÈg˜ö4󆸋# rqu%”6rÕ´»­^�XÈk(Ï)c5ºjfk0Ù˜â8òœò­FMñBDî"¸`×ÌDй‹D]ÈXc²ϼ„eêøø­‡BШ·º‚¿Q†j�\ZùÌ»Lœ«ÞÜ%ä÷ÀTðwa¯Åâį3Ö£´5<¯E(ÓìÆÊÍ3)°—i«3#Ñ9 )æ÷3¼·øTÂ‘àƒ„Ói´¶Ò´j>9X½X«-U`þB»÷ü·Œ0NÐr^ñ ‘êyàÅbRkº^)g=ÞA04¥yEÔC¸xm”àM&C–ÊútØ8jӇχÆ%¯X{'E¯àÞÃ)h„ßçÖÃ^–èÞW`¨q.m:±8¦$¯µžö-`­·ÉÁûöÐ_a—ùp¯¡ÅÄvëA\ÚÓ—ÑÚdÉÝí}G—Ým4túfg«ùZmln"K/¾ÞÛCÏ¡I×h,Í6&¿““}m*mˆLÑÞwqJÐJéÏâ[¹ã5Ã{§†ÚðŒ±9¼$ò@Ʀ÷Ýœns\ÙbxßÃIkŒ€{c¼ï aÛÝR ïG¨Ö$Ù®M’h:Y“dHM’!ž$C<I†x’ ñ$âI2¤&Éš$C¶I2dM’!5I†ôI2¤O’!}’ ñ$’“dHN’!9I†x’ ÉI2$'Éš$Cj’ é“dHN’!û$Ò&És’ Y“dH›$CÚ$Ò'I‰…˜$CöI2dM’!}’ Ù&I+IŸ$%Œ>IÊò¬I2$'É6IÊ®’“dHŸ$Cú$Ò'Éc’ Ù'I-¨&Éc’ì:´ÿ…hªê$Ú}·Ð¡;I‡ößp‡ZB‡öß(’ƒB‡ö¿H¤‡:…í¿I„ÃJ‡ö¿XD­é”:´ÿ%"¦»ÓÒ¡ý/ífÚKÚ³ÿf^a63»q>óù6ÃÿW™akwˆ6(Ì—™h'DqþøÝdŠ]«®=8§¼ìM©F×�…hìžòuuSh/¥aFwlq™SöšË±’û¡‚gÜ; ?V•LÙ*™²U2Å•ÀìºTÊæaÌó6†A˜–µ½4ójiså·ÄÔtÀmk¹Xä½3àܼ;ìBHXotÓDê-‚ÁtŒû&æN4—B óqÒ€Àþ¹@ºyμð-¹³>ˆ5ø½á{`ꦸk óˆ[qÝr¯É»ƒ Òk?S«Ýïý·3|¦†‚ßÛýˆÑðð{7AÌ&;2 8Ü##¡ö=¢HµKh&#A %lFËln›ší"šÇEÞð4äÅò7ñ¦¢YäÚ| ¨ê=ÄÈfæûîMúÝJ”Úr4ßÏ@ÆI'µùh~€|�DÖñcÏ“¶šÇEІp—ÿ¹ðû©ÇEzÉ#ІÞݨ ½ñÉÕ†,mgƒMÛÙð¸´kröKEß0Wñ ´ÎKr½È ýª]B ,…~Õs…(è ýª3Yì?!KMŸ÷Éé[a*¿LåÐÁè¦ñ­oa´{¶UÏy· 2éË€ˆ.ë…’Jy;™‘;xøÚ—=‚;¼ÜKÂÌ…;|‚|Nð¹²€O²€Oc¿©ð99í–¾¯Ÿ:§Î~â쑦ÔF}>';øš²ƒÏÎÁæ#›ü¯iÃÍ‘Öý05²‚ ²°í 1ƒ¶öÉ P.í§¹óuœâÓRÆA‚äs€õëÛ>*TÕÖ‰ü<$ JjøßÀ±¡Vº.Èð¿‘#­*ç›8jM+žÇ4üoæp·ÑÖuÿ[Úþ”V ¸K×Nln¤^x!nét^¸~Z´ªƒô‚À ÷ ½ CèEr°Cè‰ôP‡Ð 7‰p¸C鋨5R/¼DÄtwXzAà¥Ó¬xH/ÜŒ;Nk{É÷JÌÔ4÷âFSGvvMÌ}‚FþqÝü4á3<§ÝŒ2Ÿ~ Ó2q[ª£vrÑ08Ë:Èeuo?ÌÜu§-†õÐkDÙ—âŽVùµBòxŠ�°Y<¿Q`˜£§¯�Ëû({Dé)†ù9Óü¨,,œqÌÏáp…Ä'õžøîB?ÿLa±b˜÷â^®ŽZO81 ¾@q'¢™…YP[¾ˆ•tÁÇ—~ gt• ó÷&ÚJV—òË'² ½æG…yÚïÃÖû}H_¿;mM°­ÀøÍäñ¦Ü0QX}~Ã}f(gíbmâ_(L&ëÖ£o­ažÂ=Nó0°\*gó‹åêIüÃÄÌ?ý#üsðÎ鯯A6ôF¨³Ä&Ïà—1ˆæ¨«m�ìÖÏÀ¬%Uvñ†rq¶cÍ0ß‹-È¡SÖF³¹·9gš¿ü!k—øù3—³R³\0‘_A™@ à2H_{Ÿ°>™EsóVø{ÖCäÏàwÖá¿Çwãÿ g} >jøñeøx|l¹>¶`5[®‚ÿm‡Â®‡ßY߆Ð_À拱ÈBè¿ ¿q6ŽêÐ)æR߯=A†—í/B4.z;iOÛgñOÐveFDà1e2¾CzŒáræºù\Žõô6|ÁßáÏùÇ®DƒöàU;Êf×—hºûö¢0ݸÌu&"ÑHü;Ú½qfíÆè÷(u>²’=³ðö2Ï5ÃñrøE€¾ÇЀ‚gì å< ,Ad°`˜»p/é¶@3½¬Õd—1ó yåß Š| ijÀýÝv#ô~²¹½ˆ�¡?ÅÀjh±œqAN ¼–ø5üÚ<‹ñ2ÄÄxÛ“Ô½¹j½VGš¿ç¡â¤ôýЋ…¾½¢3 ©V_}’ð5E}¡:˽å»Fø¡ÀãW›Te¯(矾Š0OÚìÖñ_[*ô› ½ãq©Ð~}77è½³ wtŠ…ÒqÃû> Á|Ø/"ÞÏ>ÐÓп"¨eö~  £ZQF状ßaWCMö|·úþØ‹ÎR…|·x?%æÖ�¿™Ž–¿©j6—Bß­ à�àù>ûBw¾S^Ü\´|«^TFý×÷rïÝBÝ –êÕ“ýå%Toó>W(9¼¬*_Âr8霴&0‰GrAkñ~ÉJZc[Œnß«©òÀ@~fi_!2|â=ìMA<¸Ø×x7Ò&ò¹%R8嵜 Œ-ãUÚS @€YÃ÷:o‚âCTÌ$fÖ�„^Ï aÀ° KyƒDºÉl¶×î{•7Ö[|ØIÁ†ùÞH½âmp\17ár¤Ýá»2·üD‡ðÛÜ*P0BÂ@röŠ^ á΂anóú„fÔabB'2. ñO„L+¤­B‡™°VŒqåÜ RuÆÄ!ü+øu¶Ù.Hy=‹%ÿÄñ>H¼~]t¾&jô º¹çÒÕæiýkÍ]´Ób|Ç_ ¿Û!Ï— Ô5Ô°]bqh¬aPjØ)_hLDó„ýy 5[ÑX™ÓW×¼˜ê»êê¡ÔkLtßõ]ÌõuÑqtŠÚmœ¨.àÕQïrÛÝ~Í“&.ë·~WȽß�ä;± \‹ Ì$!VÂkª¼¾†×Á!WÁÏÙÉ«àµZßä C6 Û“ßÛ ÂÜžÄ ÔÆhi ¤¯ÂÆ®•­kSó\l€ßۃƓЬôw'}!VóÁ@ü¤UöÃ9C›ž÷ˆ¹+Žs—\gŸÞ=Úôrž6ñù¨¥«Zžœ&Òßü ÞWQ(_$]ªQÍBöúð�¶“㞯Ÿ"+‚}öñºO\ „÷¼/Ò„Ôhx�ËdÊøåÒÔ÷ÂpJ!Ìwý <¡ÐÒÔwÃKY&Š¥©ïÆXŠ¥©ïE/e (–¦¾›^Ê‚O-M}/Qkriê{‰ˆéXKSßKoà¥i‹äYœÖ©¾›Ñ¡ÎÛ Gê®/6×Eˆ9tÎ š îîævq„Ÿh5Œ14> ÂæöĈkÎkq½q­s‹0&¶‰=ä¶v< c=ûž%„ªù:-ÏvbÙæ84e@;°=¨'æ!´ ä'ªü¾×^îAeÏ2hQf|÷)áÀ ⹟[à÷ÅV_þ8XðKMXðAw¼QcÁí,x³ƒot°àͼ¹‘on`Á›]XðFÅ‚6²à/+ þ…­£æ6‘”'P~Bñà¶¶I܆¢¿�Á„Û&¶ ÜöYµKÜŽEoh£éî(€†°Eèb¸�¡Nc³Ý³‰íì!ãè:dÂ? >LÈ À‚x´lýo°¿½q±é•n–Þ~ó­MxŸƒ }Ä„þ&LØàþæ'&lá­»u÷7¿d¹uw£ÍýÍ/™°EnÝ1¶h[wÌ„-ÖÖ3a‹¾u'™0HLÈ«ïã!6.%ôåŠÍª¯ALø8œã|ë‘3IXž­ÄÀîŽxÊê ù(™ƒmAócBMU>ÂŒêë§ßkÚãeÂgÞhOX’ ŸÝãq$È”Ïý«# QS>ü£§0g™÷>hÏ¢r|án{‚ÌðÅ¿±Çß`ô—°yFe_ƒË´ùågáŒà3¿†ÎÓ㈜šÒ‚“ƒS™#CS‡ SÅ„¸ñ‰Áþ!|ÐÔðØ`­x¯Š•Èð¾Fÿló;Wz …¶‚=8uˆ "'MLi¸p4-~= ±bMìŒî?Ô7aGH¹†›?þ!š[·›¿F'ñ–Ù<jž¿+´6 ÿNÀåÂ,ÊÃÿá`øÔß~/‡_µBÆÇ?ððëªVr  ÊæËÅÚ«+G‹1Ó4yìuáý[YÐ9bSW~¥²ˆÆ/ÇlÅ¥–å¸núÌ%4ˆÍ/xN�¾]N/öÅ,ÈÁZ Êü¢ç_…i€:Ê¥<"ò%¯~Õaó“_€L¸vh1tˆy=ý€¢q¯g Ön÷»€Íê°D5̯¸Wƒ×–æW=8¨Œu8LÐIýu8‘ë4Îúæ×ˆ�¾–}è·ß99515v0ƒž?VhóÀ„$Ãù’ˆñÈX’ãb$<#óda•«1¿Uâ(½ZkdÌDÚU;n´(ü¼´Êϯ˰$Õ c«ÎæêFŒ˜.Íæ«sÅò²qjÆØ ŽºÖ4xÚ`MË8Rh)r-—ÝÌÓEy§eBrk~1»b´L2³¶Tûðj £eJrlm wY2KªÒ–if^úPQËa$.ùè÷‘È•™ÀR2×:[¥<qS‹Xª�•­àly¹dxn›\’“‘ù×bHxq±µa¦­ai¨ÊÀF4ÕÂ7BÁ´€öLÐmæ*¹b¶V+Ì„Žç’²XÒ †‘%7Õ– ÏÍÞ: ùÜ à$X®á¼çý°Èa̲‘ÆvØKXÌVð6~GDã÷&À|­ù°A8­è9*z#ÝÕ*ŠÞ¨½Ñ*z£VôFUôQôf ßÅBŸMÎW ¹ Û0Þæ5½cX|¿-2½ˆÙ{EXÍçøMóAȶW7ªDÆçø1ã&Ò_èG˜Mü†œõÏqåœõyñ"d˧ 0ø”10¶¢KD†¥åºÀÍ*ëzú†ŽúÅoZy„wQ?j¥l ºðwáÐ 7dV0Bõám¢ÏÖZÅÒ,3KFæ"÷,äÞÖø~l…N”å\e ÞešÞWa…ôÝå‘Ôx—é$£€ÀšáŸeâ—€–äÎ/@T+¶òmòFžøŽ‡x5Î[¿o„wÜÛAëܾXžÍc*bÿ|ˆÜ±‚¡5ä(4¾7úDšçTx2Íw<È(žSkÎ%¾‘ÁîÇ óïòíPÊÎ<†È?ƒÐyù …~¿pÞ\¥`˜ïAYâ÷†"fx×(ãý€ÙQ,òAÏù—B¸°» »§!ô,qÁÁ…6‚@Ï·˜&6çGð{€<„M¢hêaì!ú´}Ö…ìE,c Ù�1˦ùK(ã×?0»�êEðÛNÑØ'ŸÀÏ¢e*¹³C–\¦’÷Cø<âÝózN _ôcL^17ѪåY;i’^¡Ü+�ÝS…ªn†²ù¨o˜?_bþ\ål®@u+,Wî7ÂoßÍPÐ?2ª[‰Îé´PÝJ• wJT·ª×uÚ‰@ã³PšË”« †ùa¯y'”=òwFøÃ�{ 6•úúÃ4Îåý\„ëõ«dù¦±Ÿ€ðNèà» zr•$ÌG¼XRüW”u~•lóK;¸Í^5ÔªÍ{MsPyÍðW¾[U¯‰ò`mXV/5ïBï¤ ¿n"ì0†¡Æo‰!!9ø "NW—aìÂIæÙPm}ÝŒvB11]¼»PNü:ÿ­v=曳-á”Õ n§ÕêA K]ÛƒÄö>÷"L&¾SÆN–Wã”á�8fvó21î–fwÒ)sÜ ˆçž!8éÁêþÏ¢S4¾¾¡6èË€Õ›±¢IÏ/ýó±Q×®æ†yŒž®áKá·ójÃ|6e½mµbÁîU. &F©fg +™J™Ÿõš[¡áë~l„?�™¦8!Kœ°ja$qögÈÄÿ7Sô¯‘M¸Ñ‹™V ¡ár£‡ËŸA¸ç:(ùuŠñqÊ+ÆG¯ù{�ÿ4&…+>Ê-R¹æøNV~‹^T~¿¯Ê-R¹¿_£Zˆå¤®cÜŒÖë\šÊSÖƒÐH¨çÒŸá³Ö ÃáAªóÃke?HHþ�ÂÛ '‘_'{üA/÷xCMy®ê¤Ï‡jÖ?d„ï‚l¿RU¤y~r½¬ê¤«ªBx{^¦žZ/+:éãŠ"4W¾@( Ýr2¼Ö‡œhCB6Õgn…ºï‡ßQ�{—˜ô¿ ѹRñ@„û•¿j„ï·ÂAø¢Ÿ‹0"kNîBå(e߇±ÿæ;ÿM°ó_WãÇ‹áã6üx>Nðqn3¦ßEÍ9jGšq¾Ëg"¾ý™þd¸â ó]vœï"œß±Qâü1Âñ«%Î"üO%Î8ÞõÈök(?ˆÁ{}çÿnvœ þ# üø!|LãÇ·ác >.Ø °é�ZO²ã<ÏH·ûÍ{�áÁŸáWBŽðjt»ß†t;©D+›$ÒkH˹c“DZ„ïÛ$‘^ãg¤‘Ðßòñ؆yŽŸíÛÌ„ŽmfBÏlfB_Œ›ŠÐ¤Wx©œ0Çå#¿2ÂïÙ,®AA´¯ ØÐ¾6€h: Æ8ZGp;€Cìmyè˜/ °‚±•±”¹?ó¥·%öu¶Qi=2~7õ~sŽÝ½¨)Ü q¯FøWîxÒZ·€L ¤1ômÁ »PIýkˆCÇó¯ç?ÂStÁŸ`ƒo‚÷ÔG_°û6øx/D?í ð±ûvøßôV^hcÜÍÜý—s'ÄɃ¨AŒú4Dý¢vßѳ±2T¸¿ùK#ÜFÅùÀù)ß )çï‡oŸM*ŒñtÓywŒùÉ�ÍFWþJ˜ÿ C �v°!à“¡;„¡à“±×·—Z*9(fžOP8µ „ƒ°”~2€ÍØst¨FŸ&èí�®¡îúl�W4ÞSw¼àÒl’C5à¾@Uák§lÎ7)m‚ïÝxýJy'6æôr:ëÁï±q!u@“Ïu;…ô'›î4Á<ˆQ¸X¯ÒžàC„rî<ƒgÃL­’T@år¢¨$ý5讈ÊH˧•óO@âùï…oãõ: T¿+�ö;Ðòs{ Ä­Ú/@õoQë9úoFøZøí”ˆ_ЂóŸ¸bmÚŒ×ýŸi\Ìþ騦yq¦’ÃÝŒà\’"‚>\< ñç4ã~±ýɵ-ÓECâÿ{rt)ÙëºÌðàZË=äy3yÅtoâ/&vNŸÏ»/"ðÛŒ~‡ñÃß`üð7?üºñÃï4~øŒ~§ñï?üã‡ßaüð;~ㇿ©ñÃ;Ž)Ùøá½ìZC3~x'DÞIÔŒÞ)åbüðN‹¤°áÅŠ¼‡ï! ói¼É‚¾#š—}S»s«vAгÚ| ‘›5à+ƒ_ÃÇí¸Ž—G„ðÆÓè@/™nÏçÍh±%G ϽÀƒ=Ç ÏLno'4wy¶fÅ;‘S¬p¨Ó–î„…õÍ­^áôa­T=^WAÕÃ`Åê=_61-œÉ,zæt©’ç“JòæÐ{ô$†ÉAâ „¨Ã(4·™ß‡$úßjaÝÛö#ü.q�s¯žáÀCè= k•í»æ¸ü€bv`kf`Ò;³¯YQ=üµ¶Šn¬ÐD=èC<‚>̿Δ×~™W™xGÆ È—# ÖÓ øª#BKëÆZ®ÂŒœ®£„ÚU?"A\¼PÂe÷UQ8Ô-Öë¾pôRIÚ(껵~6Ÿ«�jÇE–îõÕãø³©¶M¸í· ø»ÑðåÐó)”àÐÉ 1”ïáà·1uq.m(pÀòY,Þ•µ¡P,,rx@†ea·G8"¢œÆá{0|•aÞŠ#q D8ôï çÀuI ôpèc½÷ÜsÔ®FÐŽAáça/¬®æê¹. ‘‹Q}‚B ýñ^1óPD÷>ˆ¨Î`•q ÃßÃp-ʡ۞†¡‡^ÿt Á\–É‘Ÿ4Eþ#ëÕ“™«— ˆ'EÞñ ˆ6Ì—ã½p¾g"é ó6bå‹1áÐtvC¾ˆö{ŠXÀˆEw"ø~ ‚¸Áßî7Œ”Ù͉ “ü¹mK¼_Ò v�C5½í€01½¹÷£1Ðá¿¿Jj¾ ïžñÖ?kl¤~çàýÆ™0,„Öü‹—ü',ßæðÕ‚¹l¡ˆ^;§ÌwÂXØT.å…mÌ0/ôìúÄ”ÊÊXvžç—~3¼ ʬ?î©aòÌÒbm»æ]æOC˜+æqC o[é ¸}Ê wˆL†y·'yHöfÒ”ízÏî0$géYdÃ|žçwå9X‡¾Óóµ6,v¡\AÜ‹yŽwšáÍ(g>¥Ýƒ Å{/†1tÖ)ˆÚrüï–Kác>¦†aŒßg¿B7Cèì·ÃÇßãÇHõø¸ >ŽáÇ'àã6üø||>¶} Ë�Zó4ø¸ØcÂL�™úÏ:*nÝŽÑÛ|fø|ø™ë<ôá„ô]F‚ð¿`·Ã8×ÓÇ™žç¢ÃÜ™ƒ?ç®Q·hQ õÖF¨YQt…¨Çø\Ç“«ºð=ˆx¦¤äÇW›á»6âH!žå|Úv8z»ûdøÙÃûutTnðãàãeºCƒ{ƒm7ÙÚùõí4¿$ŠtqÇuœwoðÃmºËìµüo±QL†i‘É×¶S•H“g˜áߌ`«q·×�íè×3 ÁeДÛñ^¦Ë1Dùì$„‚P-B^±Bk½‡ k+”@ñ-ÌÒήanÄŠ^ 9ÚÄÎÇ& ÿù(r@;R24£¯ªÆ·/ O€cxQVuä*x¹"ê8r³5:My¯¥¹ËÄ;¢z ¾;à÷y(ïãȲÔ�é¾Düw½”¾ Ê’]Ž«ðпyÁg)Tj«ƒ œ»1'Þ,BÒQ(ùø™¶ÊÏ.¼n·uŒLJwS¬„×l®þ(i8ˆ£¼oÓŒ–wž!Xoäû°W’9‚uMÒtà#òa±ð ’…'r'a$¬£ëPÉ 8ð-¯ à÷oãD,ÃLb_´ÉžéÅÐñË ¦‚„H}*x×`ÛîœþÛ–| D·Ê µÏ?Í[ÛûÈD¨qA¨?Â6c)ßÇÑé�)»Ôú+ˆ“i®— ”À\ó™§O5þžd�šŸLÀ÷›‚ Àû_»Þ /oúÿU0-H´R•H“KÖÂÐ…Nß‚2€˜¥?3Õü׺·a�8® 9î3 Ölë£?HÂ!é,,9#‚­>³™®VÄ8l€KÑ¥vÕ>Z%ˆËN‰†þ ÙžXÓØõ]€ë‡ëÔˆ !ñ;0|)z_QW«\Å^on#]¦¼òÖ<FtÚÿSøýš…·à‰ǶpÛ�Ïc;¸i@Ëõ±y¹ @è ´r™ÏBüüÞ‹Ošá30âÙBú”VÓÿ#,qç$ AÄüùPã‡àg^¹V~®C§Æ½“†ÄR^ÆkΞ¿õÀ¿y€Xë“‚dKýä$s˜¢ɼŽì<#»šI»÷®`@ÇG¸ r\4äiÐÕÀIùj)[Ì`E,âÓSDVÃ\Ä^-»»„¡ m=6oÝ:3Œësñ·xŒíüš÷ïtôÚ¯­mÙ@tºé´aZˆU¤5õµà6júÖõbè¦ævŸArÛî „ºSó¯í ¶×2<Ä@-J©®y5w õE/]‚Ÿù'kå'!ûJBÉØØ«É±3rìô©Æ[Qb/ ?FšÙÅB %ÞìþÊføÅg=áFšÓÚ|¾_À~àϚ ð\øf€ç ¯€n4˜3@Ànà:S˜|>T=[p¦· 0´bõž/›˜Öf:.²6Ï5ÿÁwã ~øtMƒ…Ï¹× Å"ëÇóðûß§ñ,º£×2¼ú|5ØïÅ6STÅýPü¶n3|`¯B…X7… €ˆvZ[§®r§X<%jvVOæ^„3ÇCuþü¹¨?W™ÚÛæ%fTq~ëŽÆ{P£‡j.ùÑ’NP¥†1²å”hýݶ7A/gªhu›ÓDÞ+»Wí?"Žª¶Cîé÷ˆAØ>©¾}!Àöfø2�†f@4Eð+ŽR(ÏÁ`ðj`ï˜x¯PMBöŠ„¼ƒ3åjµ¼ ÷fˆy+Æä²U<RK?ÃX}9jÚw9‘†Ž³5*vâ(}v×S1Jå RúÏ5Ã-[ ç =ï¯8Íø#ívi®ó¼ÿôuL|BæùÆ~EtÄ-ü>y Ï÷Š q,=æyÛ<ßmè¶Ú"äùÞG!´…Šy¾÷s„ðÀ»Yõy¾´aT+q¼š¤í÷¤šÈóÿ±/bmwÜâýŒ!êr?ç¿•<-ÎyþSÞKÄѯ–†yþ—{?ÄîjÚ<ÿmÞ›„¸jqÈó¿‚“Ö´ˆyþWzÉýœn—¶ÝþWSå-Ö<ÿŸðù@£Å:ç÷Lö¢sÈó¿–S‚-¶yþ×y/b¯:û<ÿë9!ÜbÈó¿A¢ ]ƒdmz Ïÿ*oïña'5Èó¿‘z…ç¡@‚ÆA³y¤Ýa‡FëSˆìЯÇ/²CcœÑŠ÷è╹ÌWñtÕ(9ñ ²Ñt»˜€Z¬ HÄ[ä$¡[r¸…& gû$1°Vª/é¥êAi½JL@˜¶ªÑ%yߌ—#3#X±¬}Á¾Î`ß3¿N€fçù%Aݘó 9ô´¢a0Qæ9Ó ?„bÄ/t¿ýUˆXƒÎÌkk¨¶Œà`÷_:Û¯!Çyc=L€cyÎÔ«Ü ÚÌJh {šy%@¾~c�ô|¿,íXÚs°´@Ú¸´‹±´?¾Â*l©$ÐÌšwÈ·á÷ž+Ä›ˆfÁ…àݦísÅl Àt ð·o‘ú, x@˜rWnóÜz˜Y®Áï§Câzœ@Ÿƒ W`ˆ´ÌçaÇ|öYD5쨦þê„ùVóN(ù_à÷ Àà# Œ·¶ò­Wˆþ&…y?adl6ÃÁ¯úlƒž©X óåý˜ú#´¥B@ çÛUßA¤Ìßã÷�­GÞA\~c3~„ýü ðü÷À(ø‡!gfÐÚ1ÌÀÊbøn ù#ò»fÂ$ô~ ¨­=Ë _�ønƦ&1Å6®½fŽSnõ€†Ù'²o…¬¯Ûb†ŸÙñ³²˜„)mWBhF†Bh ,Lú)t† g¨¢·ah9+ƒ§àŒ ~“‚9ü ƒÄ껲ÊÊà‚æeøž‘Á¿A•ü{ çdZ‹Á.aø$BoÁSû‰þ0Æ\#C÷Î0…m˜· :½ht×V3|?@Ý”¹R ~ã¬ãÂæ}B[°×ÞE‰ƒ³¬$d0'‚*ù[Þ\À E0oà”/Ûø¦Í•Èðnó'A:¨zÌ ­D¹ ‹±Q@\µṖ¦÷�–<”Ò¼A¯¢lšÁ$frDô!ªì#¼ªþ ýCÇ#2øÌ9–dh™B*ñ•sÜÄŸS©3':ôDóßP("ƒóÌËàõó‚â¿ yÏ<S\>ÛbnôP ÷�ú;¶™áû��} 6e1‰›€í‘ñÎ&–½ix§G†˜wD0'‚*ù5 ¢ý›=XãÇ0t• ýë#¦^1“jl`uÏ9h5èU.IBæçÁ"´ã*1\RzöU¢®4•~ÓU<\Dðî«x¸ˆà^Å#b/¡¹ù¸"¼ï8·J/?.G„_sœG„¾ƒRç~šÙüŸ‹A!BûŠÜRù8Žy‹‡¦]lä_n7Ã9�Às° nQ]Ç�/Ö ÙSTÝ늲 Dø§îZÆv7Ö*¾ZˆÐÓ…0y9µÿ mž5ÌÛ<xW¾§>wŒ âOðÙvÊý ÷kø™oðœóJ?gÑ&¥ã§EÝY¾»Ã ¿ ð¥ŸM³³˜öz¯jǧ¹ÁMbO†C(;?ë²ú#tö佄ëÏsÜoý"[K¢!"´³$ÆÅ—ˆ“%A€/Q“—J‚�"ôqmGMa3`ŠFæ=? }óß ç~¾Î-c+ ðV�|þ³'Ô!ï‘€¸)õ.�¼£Þ¯¤÷ÞÎ3õ׎ÌC^OÊß¿C¬b<u_¾C°õ!/&) ¶>äÅø–in=Ÿ[q ÑŠ·2\Û¡4²]¤ j=‰:‰JаkOšaN™äSöIÒL¼³ÒŠ$þ¤,£ÕØ…¶8k_þ å¿¡°Ë~Áój TU©NdÅ¥L†¹ô·i\5U &#®oX:³£·F̰χ%l®$ ßN¼á÷^) ÆGŸ¨zŸqýñ'óÞ+;é8üýGÌðÖ|SŽ%™cæ°À÷{oàWð<|_àx,Ê|¦÷ýdëQ.Ñø.¹¿Zý”¸�ñÂßýÞi†Ÿ±ý©µ.=¬]Éw-«õ'Ð"'Õúbé×íJþ»’ßaWòÛíJ~RëŸËv¥€¯CÚ•^¦Ù•®j=¦µ5êëX’÷^´%‘¾Ž_!Ö×1 Åò:LÛë_&L@îV ÷Ý0„ÆÑäÎ(ãÂì²ê8{žLV`�2c;;O‡Þ@¡<‡¾Z¡ƒ¹+û`¶ÞEl¼šÀu™¶ÇÌg†á¢ß'¯H<ƒ‘ƒ2ŽújÄÅXÑ;¯æj)ôÍ«EM}XÐ?Cà t“„ÒÁÏZýA®‘áµÅeÃeÆÿþûŸôÏì2wW·{ƒ]æf‚íF{û3 Æ:ÃÜÛ¾Ï?„¢lÄ㇭±3„Õ¦7ØÞ�í&[:4�¥@Ë ®SA:)ÑjGÛZsgaÛýF‡|¶:΃,›@ôwb°¾ÞcŠ"v†»‹n71�ã!®*Àû»ÎØc>3$"nº8ˆZyVû-P@mj‡[kÓãÇ:ôÞ]oUðÌÐÓ�ŸnˆèÔ#6œKÍØ·ÿˆhÚn§© O@'x‹$VP«U‘Û±m ÉÝn'eH’²ÃÞIöNZ%¶­“£Ë¯Ú‹í:-…íÑœÂöžX' ¾ Þí$ø'Á”>SOLjMz ±YôУìè&m…>ò?\gëÅy±8oJ¶jôË~œ %´ˆ*ƒöþmµ÷o›Îí¢U!{ßvØû¶sµi¥iCi+E„ý–ÚP‚nn7­þ¤êÖ¸CgOvÛG ÆuûmL±ÁÎ#‰ýz¹gZ5Sn6#«'ÌéÀÑöcxCe;|‚7"TÈ?|€ú*…ÂÙ’¡¶!Cãd¨íN†z,cUŒ™€Îâú-vímnÝc83áÆãŒOã¡G€¢“˽ΞÓíl°³]‹luPç¿V‰qÛÙÀˆA)ívV ÙY±CвN!ÊV%èOدœb]öìêf"e×>¬0~tÔ³f Òg l¿½í{Û- 6k@«½m8’ÚíTÙÕ!Ƨì†UÈ8a'³ÿ·hŒ]8›%ÚÑ!ÛщíXõÚÑLGÑÛç·õ0šŠlT«@§m½&8Û;´"CbÈtø4€N9Ó®Òåm¸Cb¬¢Ë>ªÎ`Pq«×k5ZsÖv>ú$)F[7?Ò^Èó ~-ãFž¸)p¦.g%ø&]¾o¶Š©gŵü[ÚMSˆX¯º¢ `·ésÇ9:bSl·Ãnß¡ÛÐ;UÕ†Áqðyöù`—}>8ßÏÁbbØ-'† -L,<öh5ÌEq[Å=RÔDâ6°¨˜cbøgP?~6uKBÇKO" øÑ7â!‘7E]qiB jًٰ™mŸŸSž§zžnï/ ûg´1% ã™8P.~LEÈ~ƒý’ƒé0YßL…á"y5$yÐN ºÓ>V­Ó8-ÜÈÖ]:[ŸÑÈ}«u5fΊk™Z€ÂºfÂj½]X=Œ¾âÐ=eù]À1›d×``³Æb8 Ú"yu«àÕ³%¯nk3%`øè9aØè¬Ã;ý\‹7E§ïlªáÊþÒ½.-ý:GÜ—�-v’ík•#¦ÍN*©Ÿ6öW‡ÞEÍúeUܶðkth' AÐí Él«%³­Ñ!IK¤^çTªÖvÿ5ôÓgš6}¦i·3›ËD)¼ª‘ÄaÄ]ÍH|†ÄÎõ›âke?¯“¤_ïäñnAhmÅ&ÇŒîg>™¥p»Dqj¬’FAI£V]Æ´5#H»½kBv‚tØû±ÓÞ=«Ü×Ã`WÊ[ìM|8]Ü.ŸCº|vJc4«ÅoX¿]ºÎp†›î¾ÚN¥5‚'ÖÚybd¦õvw7ë™ öžÙèNì3›M››ìÚÆf»>q–]5ÙbW>¶Šqw¶.Þ·Ù¥ò9Rªowê;„Ø?WŽ™vÕå<„Û0ß.;sOý .°p —GÁ_ön÷Ûù) ¹ïáÇŽ“AÚu é ÒáÞMöÎ\e瘰%\¥ðž]:­t^#ùj­Tµþ÷ßÃÿ{1nÝäý[PÎLøgÐe8OÈn•ñ„lyJ¹âÒl^VH¦’Vr*±v²vaµ\®‹¬ Ö°Þ‡ÆÐ]¬{ðQø,fÄ÷r¡4›Ÿƒo/}—êü‰Þâ5ñ £Åƒïþzo0Í¥7Ÿ.z—¯{º7tÉõÿçnï­/øQð’gz/:êíî¹ìûïõ~äÁ=w_x,6Å˃}oÇÑ`aÜ»î¨÷ª§y×íñžø‘·m÷¯æÝÞŸÁKŽ߼嘷õÖãÁ7y<@ùcOE_<Bò=¡ýf¨gtMæ‹æð‰×ÎÖk³ª¨»ô¾ðˆ¾À—½/3͹ýÞ ·~#x¹÷ö¼±~ñ©7_îm?ê½æ³ŸŸ¿àýÞŸõ®~Ë»~«·°Ç»~÷‰ûã¿q ë%½æÁÆâÃqÞUæ¶Ã=> Hˇž\Á/ä4´…ö°ÓP|byÉñRÜÙê|ãˆþ¿ì½ xUÕÕ?|ïÝ7ád \H@PÔ08 DE „ d�QÆ$ b&!‚€Ö!TÚbnÚW-XœpÂhÕbµ8¡ÒŠmµV)¢âPýÖo¯½ÏÙçž°ùÛ¾ßÿ{¾û<ÉouÖ^kkgŸ}ˆïÉXá8Wó_ðùÅ3¾Fñx}™‘™è“beˆä’ËÎh$º¤‰+Z½{¬ýR,94%0¤ëa«ÜÚyÃ0Q-îó[{çݘ+Þ­kýµ¸ÅŸ)>ªkF]Ÿ)N,I)VÍWËÒ/Ì&–Öˆƒ¾Õâ_u­"¾yžèÚ*w wˆªìKú‰¤L±$sèq¨VÌ;4¬´<]œ°ºA”7IüÂÀóF<.–‹ÄqA«8ÁÊÝè~\‰x´þ@ Dž ÷¹øJÿcþís¾šäçŒÃw‹D7ÿü{ÓëGÌž´VÄa«€_Äm!LÀ¿ª;%kA.ý±i±¢›UR~Ç™·$ëä<çÙÿ¹8ëOA«ü´ªï¡ÿw4�Î7Ún�ˆD£è´øöˆø3ÎM 5ŠªšÇæ Ÿðá9kÈqDBö‡âÁz+{q“è”&ªJ¬©Å¸"}îÔ)¥çÜ9`˜uÜ’ÉYâøl1¯õñÙ#'ˆ„C¢:S„²ÿ"jkæ^-j­.â «¹l˜è²c©žÒ‹­’Eçf± Õ¢¼ª•Eµð?WTѾ §ËbëôŸjóܧµÿÛ-àOÖªŸ*ð»[.³u3úEY÷‘Côõ˱þ*û‡Á2¯Bÿ_Ï«EíÊ,|‡@ôöxœ*‹õƱh‡ˆ·)—2q™ïºÿf¾ý¤Câÿb!¸vŸQ­µ¿n@™EÄ\;ëÍ^˜‹D¸†áòín&ù+Ù!/–/j:MN°ïÄøË‚qÝ k u&Ÿ²åEv-]|ÈÄKÉ"¶Qþ‹[©tÅq%âÒÑ; Î:y£Dþ¿ ÿ™ÉbvÉ´DíqR³¨)+n×.·Ò&Ê.juì:êUï-Kʬ«ËÅk>«Ë–|ë^y¯&–nÝw¶Xš6Š»3ù_$6£¯‹m¶Å¹Ô’¦g‰Vö£/ŠÒÕù¤üQqm½E~«£g©GOBYlvì ý">û0(x¹ þk þY‚wÊW¤‰Ž‡ ’q°Ž™06þ}pIX̳2ï‡D·lqYD‰/®´R‹ÓONË2§Œ¤M4ˆ°.Z"¤öuû}“ÅR«5âCó“°ì±‹EFfõ¼[¤‰½±½4WÄíÅÅ¥VùóÖ´‰"¹ñU\NÿF,iqVš˜Ó:M”lg”L—JÅÖH+<tØP‘m•I•'zGìöY©ây_6ZÒS%Æe‹Å;D\¦8\Gz®õ§‘–>4=<4‡®Jþzæˆ3GÌ iX½šÄ¢C¢c™Š§ÿ’VÑ/­Z$–m²Î’yš.ùe±7—Dv¬8c¯˜‘©¹9ãÓçñÖØ+‹‡‰õ54“·7XÇ=#ãšÐ\KwÖÒ¯}™DNãš"Qjq95Çþ^œµEÌ—æÅ½õV‰xË—V-¯¦YÙûDÁ‘’)¾ñºOôØ{ɹ#­ü!K¬}b"yÆÀì¼Q«vŠ¥{Eñ¡%~ÝÖÑdR|î;ôr¿IÛD—å‚"~ fŠ‹¨<2Ë.ËÒ.P±·ÒGö)éFù¿©üÑ—×Ò ²Ö¼N2vÀÅˤ·#ÄPü›‚Lè˜V» šD§æHÉêÅmõÍ¢GÍúõke:N¶ºÌÏUŽ˜èÖÜ'εRåÍŽV²*:®yùªW‹w}VM.9Åx2Yñ«Ï½pJ–•3”êCQ“Iž·l©•;²ß°m4N¤„e/«7н¾C"±Fܾ|oÃøñg‘-n]ž->«EóòÕt]C˜-ª›—7A^“övƒ¨(£6¼‹ˆkm÷ÓÈ=±Q¼µ|‹ØBá__ž3^Q«%µZü‘tlSüâ5R²U*ùúÑWÄœÕRMgRÓ¯Ytl|R|ue™¸>˜-] 5L­–•וÙ↠ÔüóÊf¢Ò*d~–Ä.§¬ V¶uö£¢/é*; BeoÕ þ§~5y;Yx°~‹H%\x@œ|h«Ø°²'Qkvý¢²©”‡ãJ «7k­44n[âKDâÑ}‹øý ª7Cvˆ¬Vq˜jϵõâeÂjj 虜-¦”ˆÓ²EåÞ¬ ý'̬ìÛÓ û}¹5ø½;Æ9«Ÿè²ÅzJÜêoëë•ФÕbi ª ý­ÎýázªYâþÀi~ëjkî=â÷u4ËH *Ùõ�†˜ŸÔ¥‰›ü%Ũ¯KÒòG޹ øCë~*rÒ@žãŽË게¼ìί^€‡¼´l‡x0¦&G<µ¬ì±+¦ñÑWDÜ¡œ3‡Õ6QKP"¾¯[ME'²(C2¬fkØ£4;Øbuû눳—b²2ůÑ(<í;ó";¨}[–-ž÷ É†X¼Eü£zïõ# ÅM‰;Äwþ±¦®†Ðj&"“ˆÀ�¿ôÏuÙ±[bGQ£U™×_T­Ii«r΋²åÝÎiĪvîX+5öFjï]+’(Ë÷-£ÌnïÅìßÏ?´F|MâÖe½ý¢uÔ”)"‡J(SÔ”œ5Lœ=¥x¸ø|y‰x¸>S¼à£²i·Ö”ˆ·ã÷Šÿ©Ý!^IT-UÆ$«Kúø)«æöÏi -¹ùæKE|£x)`5Š7WdNóšIïTÈß4l¼¸µ~õrdùeÖ¥¢Àʾ’J-t>•òM(®ŸFåeŠ´´!ý²ô† Èïù¬ì57‹¸ÆðІ˩´‹¸²†;Å!_³Rt‰¸£Î*»…¢›>ôà *̳E|¢ó°¬Ý*ŽÔ•ˆžâäLÑgGƒ¨=Ô ÆjxGt¶ÊÄÂLqR¶øÓ•­9ÄÝÁ’1ÀÊl7_Ù*¾÷gS# ެ8 >`òñ:ÊÌøÕSnH@GyøP€â¿}l¦ˆÛ!’§Š½T1â‰Ö+‰Ã5%añ3ªV⋚4ñBR³8iË%²à>¦©tRóTñÌŠfq>5Ãwû)J/ˆZ·œÔÓ%0U½ì;‡NÉ™b~ Õ¥Ñ$¬ìœ¢­bïŠÆœUT–V²hX^Rp–o¥ÒÿÍÂê²®A,jDò¶™·ë¬èÉD ©'Uñ™bQ‰hð—0Z¬Äçƒ}×ü§&G‘ƒ.}ì§_‘óæWÒˆÉï ¤Ô'R׿OQOûd}Z£8«&‡ºät±Ý¿7cÜΆ5 ‡g})7Ï·æÏPk-²;l)þ7àß­’¢ÎG4úkæž)®÷¯þ|g‹5bgÎÖñé[›Öˆ©”¾¡š·–ºîoëÒÂéçç7T§¿` 4dZùçVrN-yÛê@ïã¶®©­®A«F¤¦³1˜&ö_™Y[].Âàý‰xaâ½Ë¼ÛÁ{x·ïó+W[éNݳÅ\«¤\ôlž9©ŸõUº55pf²êwÞX[<~Mºž²fÂÊaO?¾rMÞ[o4œ…¥kJCÿZê„R+ʰ¦ÒÈ¢b8e¯˜™½•JXõºÖ”¿ ª,Ýz‘Õ‹€'&y±z«uÒkkíЭéë¬,r‘êlyU¾ÎZ$ž¨³í|c<5׬Tò²w–— £«;E&]ì[ž)vø©Iܲ¨¿XÖÚ` þMàÔjºJv¾[®ó³ErößÇåäXÃwnÍI»´agŽ5l|NÃΖ]Ÿßš“n-]Õ@½‡ŒZk0ÀC¼š 4!Ò1Â(.1ÿZš¬[­­T‹¬&.Ã@f—Xñý ª £(¯ûy°èÛ%öóœ5Å#ÄÒÆ«Åëu;®².:ó·äÇÖj±ÞOÙ}dE¸.6 îrñòŠš5«h¼U.þìo;A´Ê‘Zæo¶Ò’©=ÊÜDmõ oŠjªlwˆê;­Õ2º!Â|c`µ¸Ê¿CÌÙ!NL˱8"Çà½<&#u VtbŸÛû¢è{ˆrëøaÕ9s­Úéó¬Zk@: ³j¾¬\2¥:§–Ǿ±¢êË­Vën±SPwûºTk¯õá«ö†t*¯²@?ÙPãoÝH­õªÉßšÅU+¶ˆÍVåÚ3­ñ0q_õ7ïT’¼€…ŪÏ'T®­«êj>ÿËJñjÝÑ£U<í·öNèŸC~Þhýv¥èMÍÒmõÙâ}_c­è8ß/ªÒ½“­aåÕÃÖXSß°ÊW‰\Ši”ÎCÕ9b™ÕœÛ"fXÙ<PåF*Î)[«šÕâ.?GÁZ¶.ßj-ʰúoZ•¾r®”XZC#îLëEêÄ­)â@]Iºx†|Íšò9» õZ·†m£WÕúp|ºõ!õ¢VóVêÛ{k¨§—H¸š’õ¿%Gž¢ Q$77XMTg[ó†8´‚Üb[u§{ü«ÅͲ}'Ê¡§øöJn]¯ ÊᾸ»~ËçÖ„û¦¬ùp|-µ—%¢Ë!ñöŠV ÛZ»JìòíSˆ[¨™øÈÏû¥"k3þ׋©VIÎ*yã•å<cØ´ä°\üM™ÚD£¯Öå[ñÔÂîk§þ§çìÿ{«Î9‰˜ZÏšŽ#ÂΟ¿p7ð®Åm5—ÆÓ-aL¦Õ Ήþa¦d,M¡ª¦+Æ®¡fǨ&k¶Z§ß;îÅ)Ô]Rè›Ò?‡²½wë‹VV ±Š³‰µ¬þ¦‘>÷"„šðÑ€ŒËEmæ\‘‚ñH‰XÐ,º”цFýÇqAVïøŠfŸ5 høa]ˆ£²êøÿxé9'ÜýT¥‡SáD‚ŸF|¨áµRó©>§‰‘ÖŽ…ƒËçÞïâ*€-Ö¯ÄÂæÀ©Ýn³¬Æ)¢ß–œÑbIÉš@z÷_NZ.æ•L¡áÓoÇ>>µp1è=õiÔ+ÉBþ£o ®UQ3±î ºÓ\O}ø³ï€¤³­ ²Ø?ð¸x@ÿ¹ª}”Óè »ÒÐLo0ý5DÄNõ¿@ý–XÔ< v]ú€~gõK³}ýâÌÖ‹ 'Ô’¶CuiÚ±®ó·’ù@,ÿ•þÿêJÊu¬!Žœ§úûž‘9±%ýü¡ÒGZ/{Uƒ•<ôÆœ ´Ã™†{aÒ¹ŸÐvé lqÅ⪡µã–Ô_tFVa~¿ubiY2ŠÆ?�™y²É³dƒìÁÖx›g “ÿåàF¤”ÈpÖT9ï*‹6tÍÛÔÓМ4'áçÈ6R®Tõ—#È¥¤Z¥ã2Éšbêx+'€åý³ä±oý—²ÿßzTÒVIý˜§&‘¥y<•ænŸ¿üvš}pûT± Ä’+9yµS$4Ó `QI“(·²‡ñý‹©³=ÉJvãxº•²eMœ¾¾XÍú·þöæ_o¾qØðÚ7ÅBj—”ÝÜbM]¬.Ù99ójw.9oà‰‹%Ù3ÄY‡D§T¹†WvѸñ#ú? wårÁ9&IÜï]ÁÐ)­¼|µDÔîxc@zFàôdë$1àЈrªGüµ£€ΟÐÔ0 NàE@®ÿJ— {A¿ñ999}(½0Cz¡4ït»Ü”r¬9eÊ^—Ô�¯1vë½Öq5ë®\$zÒL|qÚô skï vLÙI‘¹ ëŒã›­0ÑâV‡ïÝŒÛ9€*Ƭ-;Ôfˆ3Ô\}~úÐá9Rc¯¡Ýa’ÁÑ…“ª«×Ö°hJ©Ì‡5öÔ�wU‘UcJKuºè”–RVþ‡­Ó¬iÃF]T=l„Ç&¾Ö¹#­y5¢ãê›æ§o…½a#sfŽ›4lÈ„ãdýø +ÇM7âß8»"R^LÚ+Y‡Þ¨-K¡wcì^ª1-÷ŠGå.œŒZñŒ¯FdíT”Nã›<í*I‡DUvþÐa9ÔÖ½R—M!ñ—eœ?<ÝÊßÀ-'ƾ±SDÂäÞ,‹×ãx)®|J _òøC®öñ°èq@Î8i^œÿ=±äâìå†ý–,}½"{™Z'Ç¿^‘Æt9̰³Åy[ÄØ4q^æÀñaÑ©“”–!FWHñŸ2Ï›*¢ŸB2^”Ì­’·1£ù]€f4õ­â]ß!ÇC7ȸþ»­vùlµñv¶èí¯Yþ°éÃr†Î=?§6œ3~ç,–%YÍ ÅS®KEÇìâô vÊæq…¸¼Lt;tƒ¨ÌÝ[S'Qr-è‚»<„—n7 å”-¿½—g”Yñ¾ë墿èç÷ù—[‰Á öƒ¥:7CÅ#“¬«tŒÝ Ÿù‰ùÆ)‘$ð;26Ä\¸6xá:ß1øünO ŽMÔÄ?³ûùŽÁ‘IÁçF%\ä\dã4¥«²³/tºaoº´äÜo¡û}¢Ü–†¾Ä{aþè 0ÂÆ#и„iôÿ9â÷WrûIn…;­óâ9­£@A~T‚Œ ägìõ…:hùÑcF%.ƒ#\±›'Q‡{ì0k±;%×gæopüZŽS÷÷ÐýFþkˆ³68f˜ädÿÈ„À'ÇG'np®FRþQº¾á ýÅЕ·N¬ 6ĸ5-u‡m+îYÔûbƒÈFÜËTzûªû¡·}¡lwž_®òªm¹$w²!71žErŽ’(' ‹=UFØ1 Áµ‚üoäne'P ZzCª ã{Ǻ¯IÒä3#úk½$Óä–Ég™±2·¡‰8]ɧîó…Vò—¨4ÆÅCtlBéÀG쮥òÆ£þ3}8Û$µ<)ضÏo'Y<é®—²£×Æ5 .&©.f7n«ÎüÉPûÀ˜¤Ü†@à7ñIÖÈgPô2-Ræ=_h2§…L4æ)‘ f‘|†îe*Y|g3ÈúF&%ŽL ­ ®WF%¥£&Í<Ÿ”BaC#wÓÝçIHz™^~üRçÛ([ƒ Ÿ˜ÓFø¾*<>ny+çEþUvèÀ$ÊŽçàÒ ²þC_pÊçi¥!F„ýÒYž—îraB†’Çw ë ù‘$Míx pA‚‘ø|â—¾$°X…`§É5Âu…ÿ+ù@ÀÉ dCh$r‚j|RªÈôGÉ j³tx|nðyŸQ*, 䤬À¶(Á‘€ÁÚþÇ>ù5r ¿öG‘ƒˆÀ5ñ$ý IïFÎσbëò‚ݾ\Ÿì{¯ »ðÛ£–á<b&…HSân ø<´£J'¾¨w•¿mïÄ{ƒKXÕ’?ÿ 6=¦-•âCÝÙtx|Ñ Ÿÿ2ÃJjôk¢2ZIˆVW¢MîÀ_º²›ªkJç…I»×@ç¨ÔÆ%Æ$Týh.uÌÑâ…—“{ZüŠ«d½Ê/½ŒÛÜÃ' –s×·.@=V`z:ß\%ƒ0tñGæU®NVàçQR52a<¸E…ñrGµÑ7¡ ÛgñûËüˆãŒÑkƒÔkÒeÜU‹UË0N§²8‹þçNzÖ JR`§'SÉàøÓŽ’ž×£¦Ç‰S_f×4«bm PåT­£Œ¶ÄñÙØƒ8|9kêeÜ$Ëv÷q°õ†þTè_æ¨OQr8™úT™ÖÐ¥0š©ø8gúì@dúV:~Ø/àuí6âŒ6wF¼O¶üˆç+ì®&PÏ=‰¤(yd:’ûÀPÞ]ñq2i?æOˆGð’¤`_u'¾À÷.\˜°V4©¹•e7º3´ŽžþÐݧ…—S¨¼¨vŽR±"Jåi¦ #Ïõ¹ë樤,»Ì×´Ñ7éôà°ÏS8n3‘ž!ŠS<¯ôøR¥Õ ý^½:N8Ù²‡'l†§ú6üP‡Çy“‡}‘á4½%Mc•Ëð½ü¡RöŸÑkŤu†à2Îî‘ 4rD:'+Y¿Øè‰ë ;â5—ãh4îm­Féñ¢ŒC讯…ÜçSý̵«¶-××ê)Û€\ªú“Õàà·Jî‚„DíC§p9Eóeä[ÿŽ>yþÚמ|ëkçÛ³møBOþ4(S×…ÊxY ÆóPê?ú›>}ÎÒÓk*ªåQ~ÓgϬ¨À§:}•U¥Õ¥U‹JçøÔ]œ¸=wtEŸ™ æT”2·ºffUoÎâüÒ™Õ 0¯t™7fyét,ÄMŸN6ò 'Nјé¹c GOšž?f¤Qú”ñÑÇÀÝÑ“'å;W£Œ+Šæìy89ÛWº¤r:]»º¦ªtæ|Ÿ<<nÞ‚&Ì_œX4³¢¶‚ ”.¨!MÌ+Ÿ' ó@ã�@|eúìÚ*ù1YKk(Iµ³//­iû†ýeý• È"*ð4}v¢Íý_üaí"àóeŸ|Ø@´,’t­,V2:Ìñô—.Ã�Æ0©ïΠ¿™*p¶Ò Ü®´U|à.%ä̵ðl?Ë�‡0ŽQ2À • 0WÉ�g)¯Fú»[F5ŸA’ÏŸ|¹Ð6ŒI-XI—W(EÀje�x£R ÜäwgK ]ߦxÀ;T8àÓ*ð™ˆpG+†î*À©J˜©øÀÅ“E#Ov™Jð X£d€ÍмMñûÜ™¸”þ®åòÜê.zßS*4ð9e¸[i>ï;zX*0N…&#œ™s¹1Çuž’(Ù;'j-`­â)yàN%|LÉ<æs»î$UÊ“W´€yªÄóüÞxÍ“éÈTpŽ.RyM\ÀÌEiÈåø ´*#÷Mù�ù¾†¯h‘]bù€‡™ Mceð"GS‘WS J’ÅŽ¦<À£Ì…¦l¼ØÑT¬5õµSß4MóŽV¿6¨ÜnT|àMJ˜``§�Ë�CLEÂËW_¬6¢¶ÞVÛ4mœºmFs Ý{‰[†ýüÑ"¡_c$²(Àz‰\Kð)“ÿÄ×±¤¥ÜÞ8V¸Ú|j@ÀUl™t9“YOüSÝÅZŽ [�à¸Ûa¯ré¾õÃ÷«—„AĺÕï÷ëœi*ñ¿¶êM?åçÀ3|îú†ó@Åž¥Âg«pÀ9J¸Ìç. L/cw¾LÝ6b‹iÊE̺H9"%¬> b–[t%ý­R†€ *2À&ex£’ÞªøÀpD⎖)Ç©pÀîJh6§UúÎP™ãKý!%œÀBwb²èo´då¦ù\õñhI½YEx‹’þ3"ßqìýlá~uÛ°/Û¨:ªî{¨: LU2ÀRÅ–)>°)¢…Cç=Rò2ÂÀñÒqóÇ«F’H„ª`ò*[AF8RÊIGG.`œ`«Ë-S ‘Z hmGë¦w©¨ŸR2À8ÕäÄE43½î¤ú¿ Ÿ]RgFøž tâœ*o7UœªD…¯)1Ü–Þ|¦3€ª# ëXa¿Þl$Å>V`‹6٢ÕZ£áR>b׎Xå‚ÀûdìjöHâ~uø€ ð€ -…Ì_<ȯNùòµ¨Q`Ùv"›*Êm­M»_Rô›„ï)úJhÓX¹Ý áw)Zëúžy2 ê[uç[;"ö±ÇHë¯ØHî¯T”¾JŸŽ3øZÕ}§q¿„IH>Ç$lícÿ0"»#êcP9)p�ÓÀ4Åf(y`¾’(àd„S¨ªaz¤»fÐåL8KiÎf:q¶Ó „€—3ìe¿_¸þ¤�Ó˜Ó™Nf¨n8PÕ7à­ÈŒÙbŽ?àIédMÛëA|ïG%£‹Ø>°¼¨»Ž*º0U]ÃéÜ"É"ÀãLêPFÃì„É»F™§F0Õ?VÛ÷èþŸÝ™^IW¨„kT�kUÆ�—¨ ®P üÓ©À?+>ð%< ô�?TúQvë1ãµ’6€_I²PKi ^-ïæœⰠ#ûp'TŠEóÂ*¶”»ü’¹_Úaì<O¥Ë>Í p ÓÀ«ø¸â›á&òh ·oe2Lp§{ŒÁÁ½‘ÒcÔÜ­”¬<ç®ÝDLTa¾³ìÆ‘Ä7ÁȦ…³måÅ~“æƒø9_\f!ÀZÄ#ZèÛº=¤Åw#žRY�|†i xY wU@~g¡&”1º0èøƒ@c—»!˜¿!hòOÊÄ8˜3ZÆGE¿ÜqŸ¢Y)@ý½R…!)p´T[ ¨gò:|B#F’'ò9K 󘼜àJ&70 xIÀ{L>‰asÀ`¬dâb]ù‡ïl¬1È_¡"#|UõvI☥αœwÀd¦-à{8û;° Ÿb7ó:Αfs|Ûé7pûT5í£¼Ïs²ñÞŧ.hlÛ'ä‡RK>௱*¦1dñ¸R;àLI²˜T1¹–àV&;:p‰_ì"ÀžP½È±ùï|ÀV|-Eâ~ƒèhÉXõ&¸P’Å€q8•Ê·ØÉÊQãl™€Àb©-ï}•aFZJw¯ŠeÇþLÒÕo°"œL £Ž4³À‹LêÌ"òp¬Î”À&§uБ)�ÜÈ üš¹€§˜|_¥ÈU‰œC°„IÀ &+-÷4ÌÈÍú™7Ö²íÔ÷IE“ÅÞÜÀ´ìÇžìÇ2À~ŠÌP|àÚ8·gB÷Ý2¶¹€ä8™th8É3T £1~?’qXú”ð‡‡ä¹R20<ÎUÍ^ ¡—,.R LP1Nˆ=CÉÊ -ýš¥“ÓT¢Ì\ÔQˆ¥(ÄÆýÐ[ó‘%{i%áJEG¹_2ÖÎGo#]7©ìndÚÞÂt"pÓ!à­L§�ߊç®ø6Ó)Àw˜÷1|—i ¸?^5 „;Ü]2âµ…ó�qê‡ßS’F— Ág¥`> A �žfòi3"ü,¼�0>§h Zi©´· “X/ÉbÀuñ²Šûà 60ƒJ#Za‚Ï8  ¿T[ ÀYs2 ŽD±‰<'N?G—O%¨~Žði¦-à3L'Ÿe:|‘éà²D9ŒÜ%‰+ùp9Ó!à ¦uL[À•Lë´"3fïÈÈæÖ%º} ·?à¼ýÀ¹=ͼý<§ðN5à —Â_áŒHßTwz†Aïøª|ƒNW¥ ì7ë‚Óêéæt t7y+0„¥êU2Œ&w—Òð [v â^8DŸÉ¾*ˆtÂÛÅÓH—M*—€UîoQ¹ ìÕQM AôîÈ7€}˜¶€}™/Ò@LT€“T�`nG÷±ù…Œ`.�:©ë«BmY¼m­©â\[‘3‡ü&Ѿ?÷lçþ\}ÿWê>µ^a- /¢HÌ5%æº$çȾfÉŠT@<ŸÛýp§ÒÌWù,PùœÌt8…éà·Õü Äwêð_*�ð{¥øƒ2�ô%©Œ'ì—¤8ƒo¤�Ïd:ìÏt"p�Ó0-É]‚HÕ4YV¹ÓT¼Ð")ÍFãô‚‹¦iɶ"§~=êÜ/éìÜ·«çu_¶µ€¼ˆ"QbJ”¸$ÖsdÛ¹„·ÕGj@±q°‹0ƒ®ª �R<Kep°ÊHày*ƒ)T΃èÚ‰o�»1Çt"°;Ó0•é p’V"W)æ)EÀ|¥X '+EÀ‡´"3‰™²ärˆbe†HÊ(ÚwI°G'.ÎÁ¶¦i7†;¹‹¹%ɾ_RJx¹¢W)µf‘ç(YYÊZX^¬²£n”¿!^bŠ—˜â%.ñ…œR@8)Ârœ˜úŒ˜úŒ˜Ú¹¶‹®ŸRÙ |Ze7ðU ÀgUñ�_Pż7YÃAIü6™o�·1ÞÇt"ð~¦-àLϧ(EfÌޔŕ ( É’ät– ÞX¢G.g‚³º(~6éº.Eòï%x EñMÕÿ‰Èw!…Ç…°¦Så‹÷­<> ¥Ó:ŒB ~y K«RŒ›Z%¢øn'׌ÿÛNUd7þ&™i¤¶0…iè{>Å]$Ä&äR•ŠžP»§sô (ä ¨;F^@õ•!¬U6=Qõ”4THÝE€&µE‡Ñ‚dyÐÄ$˜-M鈰¸Ž _ÝLT3_=Yu› ”{›Š§Ñ‹¦‡t^ ë(€FnèÂô­„wKo(|•bðI2³u@×ÙÙ=È.9$y»Lrà±2èŠñá?q¡LtÞ…už_‘k‡+$S¥Ò\.`)“€M’,¼Ì$räI™9ňÖé)®‘ÍÅ$t©ÌüL—"ˆëº8ã²ëTbq®Z8ä¿êâŒË~eçFþÝvDòœˆä!"ß$»†hcè2G²r‹.Iv9!îæJVîÎIv5¥{(M¯¦p}þi øÓ‰À×™ß`:8¶›¬ÿ~ãºñ àx¦CÀl¦9L[À\¦ƒÀu'*EfÌj»Ê:–\ÒUæ>`eW5IØNÄs’_�8·›$#d¼ªýá DÌêÆjª Ã,øåq’äõ”$à&wÌ=Q’€u'z²ME.Ü.#Q/ vCÀVfºJ»E€S$Y (b.à’ãä2K0 âIæÞeiÀ¿˜ 8¡»ä†J²0…¹€«˜ x’IÀ~Iæº÷\À9’,Le.`±$ó'/¹€“G4+—‡Aô‘ü\ÀHÉoº¶ÄÍ,h–d1à>æe.àeæÞf®„$'ÉbÀæ.b. ”¹€Ÿ1°›¹€ß ²Ä>æþ¤ù >g>àÍq¼t"@ïž’o…A¤3pNO%b6ó—ky»˜ø§$ó'œ¨² Ä©'*= †Hç+Œd>`s“™ XÆÜe'ê*E µ×¢îlžšû$A/é’¹¨.£™„£^Æ$êÊ&¿è†o!Iò‚&/'ø “|Ädr§Ó¤gåÎ$(cr3ÁcL~K”î”;œà&&› žbò‚ ;ü'‡ÉÉW3 ÿ¹“I8M+“ 3 ÷"‹0>1•É­ï1‰‚ý‚É8Êø,ƒ\Àñ²˜*3 z2Å:ÉÑË™¼‰àçL¢Åø-“oÙEš‹Rì# #w’].v«ü5 ‰]]¬kU;‚ ð1y;ï;‚ÓdAä¡6"WÜ(›—Áá»Rt\Oí¦yW –f¯ÝF¡£¸ÐQ<(ŒFòIŸ¾C!4˜ÿsÛÓ%�ñ‰Êå!^ª’lwµË‘1¢áÄõ2N,Ò´©.xuOu¡SCrwÕ‡A)÷Þd»7ÚVø*‰lvVIÁýƒLD¥Äw%½h9ðÏ2AÅ€Oº¹ -Hã ‘ºÀ½ì^âBI/^\{‚kèƒ^a·ÿ—ÎRý›¹âˆŸù =È´Œa:¨ž­ãQ€Þò–¼™¡ïòƒø¹z¾ ü…zî ü¥z ܬžÓ[Ôó[àæ€RâVõ¸ج6«çrPlQ [Ôó<(¾¡xS)¾¥ßVŠ€ï(EÀýJ°‹PŠ@$óc«` Ó!`W¦ÝÔ£-`ªzä¼²›RdfzºÌĦ ðz~D¼¹þV"ŠxcT8/ n a'ðf° ð³XuD ?% {ðC ÀyŠ ä(|”(„`õ{~ö‚¾’ĈW£ ‡ý)aŸ¦u¤ˆñrA(–ƼƒBL_Ñ‚6m^Ï{R`ó¶™üžq¶²ùàý¸‘¸¿ƒ­.ÅŒ¾Ê¹5õxŠ?8Àƒœ[SÜêÑA] VÙB–Œ\YSoªŒc•[ê ƒv>o©‡xß8uÑ×y”ð1V×ã™õ’¾@.%$¨‹n œed0&QñuöÉ ,›è‹?ѹ“ºx•ˆ˜.êâ«.ø¾¥º8‡²cxŠS­¹¼¸ˆ-úâQ"¾ÕºläE”ÜÜV?“’^äܬu²a›Ì•Üm2¹Ÿê‹ØN.:ß[ÿ6~x¤. íÍ="<ÕÜ÷ƒåÅ:^°ZY¿™ˆ^ÈÒü ÊŸ±<žIPÇ$ ¥“AöÝÞE]€xN_€Ø×EîËXYÛÇÆÅWú.rúÊduñ;|þ'E] Ûo× N1”<¬f4ònOÊúºª Ê£ú…rE7uaæÕ­œWÓÆR>MáÝSì–®i²m„Ú:žð—ARíÜœ†Ü¼ƒŸˆ¢*ÁOú¦ ´Ÿ‚æ\©§éÊŽ­_µŠVe x˜El–¥}¨£Š9m,·�Ó’ Æ Óš7ËÙp1à .ãiÀEîI_ï㻜:žûc˜¾Ãú‹ªš?†¬Ô…ܼÖAyÚX½wÉ äÒçAõdøsw³³1F m´M5eÝïø{c}ƒõ2(¯Tø ?Húj(¥ˆùY“Ž4¶Y9õ¦±ÞhëAŒç¥v€òïF£™ c:“åÉ!uÄ©¼£…t&aÇ eÿ9)Š?ÜñÑÆúDÇ-ë_'â}aøh£»>ëLFI©6¢±>¯RéäédÛñ(í¬.ŒÆªQ¶O¯ë‹œÆª1Zû‘u´bü,Ö]r  ‡Îò”›òî,ó¢æ7nx²[¹r–ÙÇÅ©½¢qj£ªðY©á~Û׸^_ ¦³;g„_²#”rך!ã08I�Ñ¢ Ñ3”vfXÞY X©/@ìëÂRFëv¸DQ2ß�ÎHfÛÀYÉJh–Ó®ñ™ ñ™ÝRÏQ- n�w¥¨(èd5ØöìªB€˜«n�+õJ§¹<\wƒòKHi7A_W^"7Fvë¦t³‡®&Auìë¡¶$áA.yLN×½°$ºÉèþ7Ê~ïµêÄ_¹a݈W-,ª¡ \LŒÁFs@°QTMßXotkGê¶±»µmN·v¤Îl”c€A!uâ®Ñ稚N$ F¯ÿÅ8å|¤n ³6gôaGêŒáÃF³98RgTϲmàb£;g9¥s¤Îel4{¶#uÆc£lB¦vSwŒñÇFO{‚RÓÍ hÝš€vþŒ02p[¢l]š”[§Àí8á ǯ n«Ê#B1ì6ë@°ÕÖ…ßvq°N÷²U€«tŠWwŒ.ã`ö—pk3ºÄi’Fë±µrºŒB3¹ºaˆ¡plªÝ6È hî¤ÃWé€ Dp¡z‚ƒuNwéj.ÊæBõ ë G8Xgô ëŒ~á`2Q•ãÁºö–£Yx?¦Ä¢ŒW¬?B¥q¾¯ ŸïÔÛÍñêƒõÎ|f`¨âþ íƒ½}ú%ÂB4Ê•¼³C®˜ìá™Eb,æˆÝxÇ)FcƒåξB˜¾_6…ΪP1‘zöD¤®ßD깑ºzÛfŒÄ>&1Ï©Ä>§:?yã{'åÕ#—Çêu(/@¬PٰŠ<V¯{@yáLìš#ŽÇìnèPÞБ…åo:¨éˆãåh³p.“€“åªp{ZàžZ_ï0"1DO›Ð\$:7õ ‰Â0GÒ·ô|‰nuVO“õ­AÎXhK=ºÂ:S©¯õ¼êk5¯2òý~µ©PÎd>Pé¢Áœ“ªâãífÅFÔ·Õ;Q-v¢VŒ¨d…”H–ŠÜÚˆÖ6{†g<å¼.]³ø7äϱ3øbAÌ“ÎY 'üÇ{^±®wD:NhŒF‹Qü§ñ®ž!j¯‘Žk£À‡H×,Ö••HDøgLêj‹åsUS‰tܸ8Šoª?…êKYgrCbÄpæw†•›¤C§.@|Á3pÀ¿4ÿ_ª÷5f1’ÿ°ãç›L?ßToøö&Ù­^¡×ý¢Ž<£‚ qêD G 0›ùÏÖ}@ V6Ú+pcN°±Þ50–6šKÍ%‚æÁÆzWïêÖ]·«¿n»‹Öo3è%ÙøkÛÍ6w»¹F°Ý^#ÐiTœ÷Þ¤Šô©Å@UÞ›eC Šls½®úòDP †;qÓÔYõƒRÈ m–3zÕÕm6Ç<›å0Gl6›ƒ™ÍæÈYCk Tÿ\5ÄÀ_ªà‡<‡ÞÇþèÔ¨\øÛ�²9ëdí® £kdÚ4.#ÖžÒŒëdw¡ÄW R1ØJŒk \}Ž—æÆ_ç9:`�ïÛÃ'äÏ!^+¨IÎ¸Ç _QôÇ!žF€FûêµOíôÚMãnpílPζә7ÓCqúõ&}~Ÿ¬iüΊž%y 9ÃFÞY½¯Nº‰#+]àc&õ` \Ji&oÎÊT˰’Bð:sgîßÕØ›¸z¾EäD»!Ïü’¹€Û™ ÈçAùÊ鈌Q߉lä|Û¨‘¸pÃ^üp ÎOQÎI\äï*&Wñ¨ö*å°ÄÕîJ¤vV"µ«‰ë(—®¶\£\ Ÿ;ò Fm £»Ûí|)DüC¼3†?qLj±ÿÄ®®>RÏráù=ìÂ+Ö͵{V›a÷ïrìG‘¨Ndø+nóZäìh¡ŒG1 šcXÄ\@“€O™tJ­P§š…:»TŠEérA#µËx·Å2»ˆŠu3 ½ˆ·;®æ½€ß%«XÿÎ.»âáv);¥Tì”R±SJvï˜oL<öø¢-# c¡Ev‰Ç«æ¸ÈR7Ù›üsW8Íh‹ì#gòF´R‘7@4è‹UF”p�—EÑ"»�ŠP�{’”ôÕLS$�ª1n©7Ëàbc>YÝe!Fðþ“v)é\"õú- D+0i3¢È5ÄÍ\ó�»¹€w«RU³åxcÒGjâŒÇ+)vÒ"» ¼áPÇ"zÊL$ମJDoÓ(²=#ÏñŒ<LG§òÓU€êuZꌛ(ÏX_ÿ•úïÙ�oZŠ¢"N]€xN_€x™‡//ÇÙ.³¾-—YovÉëëõ©õ²V¥½¾Þ,a£^ovÃëÍnx½Ù ¯7»áõf7¼>ÚJäzs%r½¹¹Þ\‰\o®D®7ÇDëÍ•ÈõæJ¤Û Þ׬ÞS98BwÙoÿ(óg9d¿+N÷È©âÅ÷¼Ï¤Ž«©Q>5ÞÍ;U%ÃTŽbÙ.»¸‹uÂÌ ¨Ë™²ï¿IíÙÙ}×¥Ýu.˜r:3LžÎ3ldÖø}ù:G¨§¸Fy)q˜ìd÷î…)#ºóÁ/‘~+ÉÂߪQ‘hó:óû\Ûƒý| Rx8ÊSmÀTYäµõá©j˜D|íÓÐ*Ès üŒÐC6t…=ìáR!Ú¼ãÙã¿«q®lõòÎíh¯å}WWç,à}WçtH…ô(¡PÎd¹ý´ªP?­"p;Ë�w3ðh'•šGÕxøhO—d> ENÊ S:ë©Z¡^è!u€ÌTvVê*Õj1v^¨f˜Ô9>9ÑE% D6ó³Õc5"o·ÛáBÀß™ü»Ý:ÓÚüµÖL\½ÔŒ}?ó˜ ¸<YÅîr»ï•cŠŠn‘ 'ñé¤Ý êa*æ’YÜ4g9ÍwuÎäµÌd²j²Õ§¾«’®WƒZâ Ówuμ·ÐXêü®N·àk9ª6Dø={x~ÛÎî›o¸¯ž)ùªíùƒBºÜò<w2#ßItþ vŠòD䛑ý¿~Ù1ÿ„YÀ/ø=wÀ72A•Ëà âø1«ì!ýøæ"žàpxeŸEó]8 +s»ª½ÄųØS:DU‹q‡­Õ•ãìqª…´°E!6î“ÞáßÓÕ8°K �%°‘çC�¤Þ âê%õRö>">`=øíÍëNQVt?VÙ‡]¿~}èP> ðDÝÞʵóï?ó,‘åuÍI¤ªÀ«ô ów¼ë½Ɣاÿq?™ëÌ}Sg¥É|AÃQ”w’ mìmÆ“8DE¦Ú~ çxTû\åÜHTÔ ¼Ù)˜¨6>iYRâãB×,œÃ¡à�|"^i¼÷Mâ•`µ¯ ˜¬ö[SÔ>,àqjp›_å$ˆÔ àƒ*Àƒ¶ŸÖ$V€(ÃÀ]ZuŽ\ ðY¥èYÕp@p·R|^)¾¡x[)¾£÷)EÀw•"à~¥Ø;á$È+þ O“ÄÕœÕ8cþ.&Qq'òyeo *eæûŽ®Œ0pW£; ¶2 ¿c >U¹UH°^Ã[LµklµÅÐõ “¯¨ÔËç Þã¶Ë6­²­÷ èÉdπΕÅΖÇ+U¡Rßôˆ*"ŸW‹÷wý:švç5OE“jo0„üH¹¯è:;+‹QEO䳪„¥ôóª$‰¯#Lä×*¾Â·(`Çl1'r ý¾ï󹛋!òºf¿$²Ô à2¿ºaþúp­ô“dÁy9ÌÍQ¡Ìs¨ì0ù¼‘Iü2“>U£¦Ð “ç„ÉsÂä9aì6f%]6(7v罈ÀwƒÑB÷;ºOpyX{9ÓöÚe˜8 sÁ83"5 ‰ª ö h¯±€}ÔÎH`_µcxšÚI üVW0ß«ÀT� OíŒúÕÎH‰jg$p¬ˆ¨©ˆÙ=ªÆŸd§ „µ´Yጰ¦Ôã¼™#Lì›aàÉ|ØÇ0a‚7ÊÖ›ަ÷A[o©ì8[YA4 Ø©u G ">Zß¾ÇáMм>=K6k>qÖ5]šàd†½‘7—®óTÖóU– UQ�ŸWGÐ�[ø‚’¾(ÜÞö3 08¿�¿Qê#ù˜Oo"6âÊ`:Õ8§Ïò¹mà—"yƒÃ=|ÜëûÝg}ñí ã¶íü8îy�‡ ”Ëa¾ŸåsŸ?zŠ p Ó‰@u@Y*p¥âW)yàꈘC÷ »&°\æ�ŽDñ‡AWrT‹Ü±ž¢¤11#˜/ÉüùʆќDŽ%îc:|8":»|îOŸVQ>£’|Vé�ªÃOS€¹r¯ôƒø§ªÜÀCªr?W­ð°j=€_D´S8í §ð­,—E~y’Y/¿Ì§Éȧ^ì>ÈÃÉÀDYR˜Ü«$î#|^ÑÀ×Xð“€h}êwÙ˜Á£€’v÷;Õ¯Y@!Š9T ðF©,°‰IÀGJBëÅHWMâTÔÉ÷‚áD§—¿Oí rНyM©3}°Š%±�l,WGâPì¤Í½ö²C�~Æ+€‡ùE)·c€Y2ÕËóTV^y†­« LÙ× {„ÕtW½Ž–³Ä××P¶E‚Å‚×ý¨q©O TF˜zôï!%ë£À…‚8ØtsÀ{Ôz'b›êg)…2–¦¶¶Ú©s™NQ|àyJØ¢d€·)àíJøˆ’1m&H»¹ JµqÜÆ¤qjîtãܵ=Í“›Œ0ùê­³Zv—Ï®ô|v»_Ååëw×Õ·éïc·©÷éïSe ø/eJßǨUM6‚À“•z`ªâÕ9Ù©Àó"ÌBG¿Ël]ŽRbÀ—TpàÅßã×G`:y]G¼_s-xß®”v®âÀZ?JƒÚæA}Å®¥þPoœ¥÷s¶xŽk àeæ¾l«Š�?D*øAŠñ‰Ô~û]‡õv-ÌýÈ’;£}ºì¬šÊΪƒ3G¨fxâÇ*y`¶’æ(`®’(àd%,Œh¢gø¬_ð€s”,°T5óÀ2¥8Ou óTS$Ï›ñ©ºüL�TŠŒ>Å2ú” 0[½oã3cö 7ÿ¢hŸ¯Æ‚À,>q1DÄ0ê¦�;0÷qå’ñ¾¢>ü2C|hþ!à•¯Qwù³È·Uãû¶ŠubÓЉéD˜ ³Ž€±µFõlR“øÊ7}­Õèkî7Ä<J»‡)ËœRY`Xø°@ç€5L§k#Æy+éz•âW«1!°AêxÃpz1 ø¥ÜRsP_ùðÓ!à×L'¿aÚ~ôFæWª8~¹£¢p‚´SYñYPnv,q­vƒÀ#œ)�|…+p–+0†Æ8C1g}JÝ‹‚À]ÂU·%ô¡„…^–Ým|†"ìa¹À}¼ÿp€Évö8‡òíµ#™øëü“Ilm¾’6»íïü(òt@ kŸÔ1ÍÕÙcuâìÛEÆm{hБ®ðyŒ€ñA;"ÛX2 ¸‡%ö8 ²´‚ζþbC¿=G‚¦z£¤<èÜ­â¿[éï€*÷]ï0.^ãx¼fgh®×^þfKªÌãbûÃÛ”}¡¦ØÎqª’ð3b›ƒ›™–8Lc‚å~uÔ¥D¦- `:d:ìÀt p-ÓÀ«x’þLé^«ô¯Sv=ø˜È 3^Ýø¥‰ËøœJ@óÕ60¶(iàƒüðK¼£l-Lôà@Aà¹R²�p^ îVûžYÏw�«œ;«lû×)›Põ mÉvîjk¼æDZßí£6R=ƒzpƒ¤D` “—Ìæ½³mE°õ,“€VG Èx‹¢N±:B¶Ÿ96r‡Œ•Q_äË&ÌWt¾cy÷w&ÿAðMŒKW*©î¥üöV~û0ìËtxÓ)À/bU7 âKuø• �<¢¿V€>uŠ(0¥CD˜mä½jAà§òÌЦ`ì¤ãÃßäwSƒ*¬¾¥ƒ$‘1öq¢ȱœ8²ÉR‚ËÕ–òe„kc]åó]:‚cìÓ,óùwìã²2õ0ˉ©}w8±îe»W…E«ü€Ù¨äÊwÊ¿Qïä9X¥N Ô»Î;÷Ä{–#Ø-5U-ƒøó¡4Ž_]$uP"I´1;t‘1D÷)&û™ *D:ÁíÈ èn–‚y7«d¡ûûƉ·ÝÞ¥2¥|‚kün;ƒíJ—J!û(çV2^¡øÀ5мJ;›úM¤ëN*`“W(Iótvûnî.r—E>]²+*4†yŽÎ<Óø,íµÔh}­å°û5³ƒ=^‰ ñ|[åëÆuµ;ϵ˜¹æC—M*šÔ±¯¨†À L'7ªÞÂt ðuK­}ƒø#ŸÉšÜËtøÓ‰o¨MB0�ܯÎsÆÅ)E âÕi¢ÀuÊ(0Q> ì¨N%†Ôi¥ÀT­ÈLâcìR?æjè(c_[~Ëâ€æ¨Þ ~ø àp‹\álz™dß⌼ÅoÄNÆöý¤8WyüFY `??ßF€¿ó®*Èwq‡ùTE;à«]¼_­ ÜÎÏóO3 x9âðÝ#*-_uH'gD˜úó€¥–k”‰Ðßr”rç4ìú ä¢ŽÊ¶ëµ m2Ò¸¿Ýu?‡_tȱßIp“÷¨4 ßâåaqV=¤Ò/W¼@¼èÞŠª ›k?¸¾„¼Ñræ=7)“‘ó ~*Œ>¦úz¢ï´¼ó(xEå–À»˜Î�nS|àññî!ÒDâ­â³kW©³ye |71p˜O#|+E-wˆguFŸr!Ý»Ï ¾;Nß-6mñA¿U>Ü~‘76COGÞØÜ1^ǰʮ0ÿ¿VãX,4ï>)cTY~Ò¶™ûf3¯3ūܴ÷Öì‘ÄAu˜™ñ¬Gýþž·±Äѽ\Íß‚x>–˜Ì~¯7³˜aùnÂv“w‹�ýpˆÉ¢û-»E.P²”î”ðgvL*6{)1¯áô%À_pa�žeö„ÏâõñŰ0˜ÉÁj›9I)Žg…ñ˜‚$þðC'ßña\ßÏIì`r‡­¿²;ôÎlÀñú,¶SÂýì½|væ¬T!)ÍÎ]Ûµ>·ãj÷^Yt9™#*q*¿�(Mpy$'±dp’ Å}œ? úŽ `¶;Rø™««ŸšÚøì§µø•>b&ìàSSÂß1Ýø’â_VòÀW”àï•~àeøµÒcÆ‹¿Gçô=ÊÀ‹­E€ÛEA¹·ö/>WÆöP÷)ÔC>{]‚o1ù–Æ®ÉXðÍ”¬|ÀC‘9ND›æhQ˜ÿ¹-hV¢†ËãËœg2 çS+ø—8Zçj­ò†Y(æw[2m)>p¨â3#iõÇ —Â>–~üÐ߯šÃþ*¸á”UŠº6ñ§ý<ˆª¯ BO“*¦DÎVÂçÛ–\ ÿÿßÿÅ?|ýîv?¯ãƒyøâüøûûý¼µD‚NÿzûøsVgøx“رø/“î?Òß›ô÷áðGÄòDßI˜ëðï¥Æ}Ì +Ž¿�€1¢þaï0ëv¤æ´ã±ù8’GC?CÏý>NíÅÃ÷Ðß–¤cóq$jng~[gšâhÏbÂÔ.|Ôì uLj{å'„øÌÙß©póB|4ìtl夿)ôwqÈ‘¿@2ª8…þ]ÉÎ}:!™«˜cð’l÷®|†'Î7;ÿdâJÕê,4œû†7.®èÊg¥¤¿Ñ]yÛ¶_Ñö…¡Góômú÷?äswûy¾‹Ox`·çàÀ¿Ÿª6Œâó(i>6WaÙ~õSÿ.TOt°ãäêÁǃG<$ÔÏõ/‚¯¡¿µj(t,þA5HùŠþ¾ù|ŒGBô—ª^smë—V6¯¢”°ý}ÿƒÏWîŸSQ1»ªf`Úl—ÜF…~_ªü0`´_‹Bôb\…ïUWŸN¡ô|ÓgÖ”.™W#?ª8Ó.�«Å椑Hmôø±qÐp ¤bÎÌš™Š–ß(1L›U]­i¿çÉ·ü½ª°³”™5­ï+ I¬ôt÷Èü]a)s­Ÿcïþ}¥0YÊ`gˆ7NA¬«”Y5>7IÊð@+òw²bu’2ÿU&]±:J™Tç¦û7\ɤHoºµG!NÊ£æUWά™=×íRøn#RëøF±ŸóÑáÌQ æuUªw’2’ƒýœ©Âäàó±YAíQø…‰³>Ö´uq6¹8ØA¹2ÆÔó–Ÿ×àô{xƒÞá`Š/Ö …í|¦ÌyÄiõ›<Y~ÓŹ¡\œùÄ9ââ`çÆûçú�odt8-tuº‹ƒ§±-Ì>àGvŽ mÄ¡”[º»8Øwº‹sÁg8Á8çb‚ŸRÛäâàáæ].¾Ï6×ÅyRð#‡ƒñÃzæ¶»8x¢ÖêŽ3‘¡ŽfnàD“¿¹dŠˆçÊÃÙDf¹BÍò¶8Gf14'™2«ˆ³ßÅÁWÎ,WÎßä#ï™û¡Ù%ó"‘C\œ?ùcäçS"gt1õà™WwW(¼³2Ù”98-ÉfMw¿éòp<F¹Ëe«„8]¡æ"”+ñhkˆ+×ãݳ3Ô/ˆ3Îeë!º*q…z.†µ¾Ã1p8‡Ÿ®¦fËö|am½K3;äv3CM"Î]®P‹ˆ³ÄUÊ«b¹‡w87g“‹sô¸8;ˆ³ÝÅyŽ8­.Î[±¼¡Ïá|Dœ¿¹8ªÇG\<a°\¥ƒõÿî.Î@âœîâŒëÀN‡S@œq.N9Vï]¬ªÏuqÖg‰‹sSÞãp°Ø;וóOwàÏZ:œW‰s—‹s€8©Ç›¾ŠåYËÕ£K?ÝÅéBœî.¾J7Ä•c8gœK&2.ÎtâÌuq*‰SââàÛnK\,£nw¥=Lœ¿¹Òµ“8-§˜^·¡\2&N«‹s˜8¡ÓÌPXý¯tqzg»+>ç§Å%“KœV—Ì<âìwÉ,%ÎW-¸ON7ep¤Ð&—ÌKˆKæ½8Þ×iÔSÄÐ Ë»ú™¡úÇrÉda¥Ó%SDœ¿¹d–§q€)³:ž ;2·B&Íô(ÒÝ%³‡8w¹ÚÌ÷‰³ÉÅÁúß›.N07{;œâ,qqN%Î\çlâ¬wqÆg»‹SŒqý@3]X<âni‰Sé’¹Š8+]œ Äih¦ý.È 2eÆüá,“ƒ#NVže†úc÷Žõ/xó¼1NÀÑq#ÌP=‰sÄêlâ4Ž4mM'Îg=qB˜œñ-6ç]âìrqb0˜erÎ$N–‹ƒoc­tq–g—‹sqB£MÎ[ÄÉpq¾!Î~§'õö3ƘœQÄ9èâÌ'ζ MÎ-ÄÙïâ<…u…±&çïÐ<Îäà#J©ãMÎâìwqæá­çl“Ó™“³2LÎÄÙãâ$ÒÔ§ñ"“s6V&šœK‰šdrÖg‹³ zrMÎë8²+Ïä!Î~§MÎóMNÖC\œÅxźÀäà(ÜŒÉ&çIâìwq> NK¡É±h •UdrÒˆSéâL&NF±ÉYBœ=.Îf„*19Ï`j8Åä|„/ĸ8I4ªÊ˜jrp´ä.§ëA›œe˜PN39Ó¥^â²Nœ,ç�ô¸8htvÐÅ9#…y7FMÄÙ_nÊà3Ioºú”¥ÄÙs™)ƒ¯¥^nrðY™ çCâäº8xýx†‹3Œ8•.Î|â¬tqn&Î6çiâìwq>!N¨ÂU—‰CÕËHE–¸8ÉÄ™ëâ¤BÆÕb'Î8§„8»ê]£eâä®297@fµ«Vgå:“ƒ5±m.NÂq”‡ëMÎùÄñ]íjˆ“ëâàÃÛÒM>óz†«/€Œklüqv…\ãâdŸJÖ]œ¯*¹¶Ãs|ÊÿÛëxË¡óó®í`t{Ï%£ÖvìõÿþX_Ä/­Šõõk=N¦y“[F­]ôö뵋Ù3+*fÍœ}¹{í"ØÚŸkT*ü‘–ø×•dBMf¯‡¤äÞhràcsÕ”!$³çç¦L>ÚóK3ñu•¬Í&g q¶]™«ÌAJwQ»RëN©ÊUËÎ o t®Ú1ô‡¼2*W§j==‚²»’«iv®Ö,­,­ve©Žsåfç(ñù7â|ÄŽs™ê𙳠eR‚:ÿ#Ó•ÛWÛÚìÕ£Ò5Úñ–Š…ÕµUIómè®Ó•ÚöÚ)¼åV³Ü$Î §•8+]œ·Pî6gúüÊ™U3çW[Lúkw#7|Ó§—ÏÇsÏÓ]2_Ã3›M͢ũÙô±nÄÙ_¤9åõÝÉünŠ‘?í+¯€GF•J‰ËËzÖç;Á-£ÊbŠ]eeó"Ö°ñ;RQùØÑkî¹$“û¸™ö‹ˆ“õ„É)&Î.g!4?éu#Ç\¼J™±ËÌçû‰3c—'WÓ¹:'È«±íÌUëØµ q·ÎÕ—:1eȨZð‰] ˆðŹeTΗÛ9_YUZ9}ö¼2wöïBnì:z-ØK2+ŸrIcOGæëA|–û#רêüÙøºÎŸþø6oÚW9^7óòR¯®/QÊOÝëb§ÑÌ3œ.¶r<q²ž1s£?qþå’¹�'e<kÊ”ç¥gM™*âìqɬ#κ紌ÎUŽ!RŠuÜìÿH®â×r<£2z5ÍУâÓ¨b(¼Ö½¶|ió Ô÷Ü×c8Ý6çl¬™»8ƒS#9ç¼´9ªLo8J™Î]0s>XŽž²š¹µ .çõy¥G¥b“NÅÿiv4_åT Âú³´îOñyz´¶ÒnæJû­GIûÿBº÷Õ1ôÛ’9û»cËœû¦ö ¼WF¥ýUg”RQ]][Ù‰ÜCÞÛòÜÑÛ±ÇIfÿsºÆMŸ>}Éœé3åÓB<bгL>Cc~{(Ԍݑm]‹ÏÏ+i#ÎíÊgï(?<÷ÄÓN~î¹!jÊðÜO;ù¹'VO÷Èà¹'žvòsÏKÔÈÞˆekß‘#GâIŸ%e¼¦¤ÌÔ©S{MfTþä¾%F‚–y­ÜYfjâÑdÈ–¦Ù–~iȨ~0tŠÎgÜHÛtÙŠ*3Ú:¦Ìè‘!ß±Ò5zjÊQdðÃóejøÕóåk}‘ý~x¾Œ§Êü|9zï„g¹xªÌÏ—-O™â‡çËxªÌÏ—xÒ…ž/§úŽý|%ÀÏ—‡{îëzúw¿±caþÌy "*ê»T›*w·ÝçÚc­Ýº~%zeþúeŽ×Cýúk@ׯ©QeP¿^é¤ëNIèà‘Aý›ö\¿Pº^ƒ(÷\¿.wì³òæ3Ê;U¸Ü‡DÊ}¬_—ûIQm¡Üñ4Ë}lT”{–¯ír×eš°ÇÕ¥µs¨*­X8Û)بLWî>zÛû5F;ZFrðaЃϛ㟾'åe{ÂJÛ°Ó­—;†íò„赞0RhOx²Í–öžÎÚ>¡j{ŠGž°UhOࣧ\ñQ­VŠÝjÝ5>ð–5¶·ÐÔ£Vâ)Ç[„GÀÇÞRj{ öñG÷–¿ØÞ’µ%·Ì𽕬¦ݤL‰ç>~hI°—˜[’‰žûöº„íuå³gGiI. [û_h»%™^™6ðœg£Sü )TèEc �;/š~8÷~“Çí‡l qÆžb÷HÅë‡Qz凉?±:Ç~xGTøáÍݵ¾¯ú ÷~øBŒöÃ=ñÁ>–Ð>¶Æ×–}£}¬k›=ÑM¶j >†ÃØÇNŽ*[é;vOt§OûOOÏ}í?Ãmÿ™]US]u]kå‹Ç-'þˆ¶%ô#Êô»LӢʠLk×eгŽb<2(Ó±ºLçGÕƒ²xï(½ƒÎŸKƈºfnÕœˆúU{‚Ο¶[õH&%Î\³ºƒ8Ý]œûOàýÀç)´¿3kÜû°åâ`£êK‘£n§¼ämîtýåeÇÙŸ5](¯Wíò µÙÄœ Ë+6–ÇtÿP^Ûå5<j»*GƒvÄ^“è» ±ƒ„ëàè@´zrÿÎ.÷…¾¶ê NÇà:øMTÔÁß±GËmÿ·««"ÚkÝ/ý4õk°]^Þtã‡òúSì±ëWB/]^ø¢µu(¯Ó-]^K£êA>_ÐùŒ£;â•U g!~I¯äÌY¸@ÎÖ_¶9ÞüàMû¿‘?CŽ2ʵû¯Ÿ¨,ŽjKùÆ-¶oT/¨¬š· &bÕ±_OŸ¶WÞ†š2QRÁoý'R<ªSíQ/G•‘çBÙµ:­æÂ£n³=ª›-ÀµB·�ý}mÍ òãt }dÏ|ÎöÌÓ|mµ�ñAÝäF•A °Ëwìà~»”E-æ1T‚x_íh¥\bÊØ¥ÌX¯÷Zÿ‰KyŸ]Ê­QePÊ•}u)_ˆ\iÇ¥ŒïEp)i³”·Û¥œéI~(å{ìR^5>(åOìRÆJOôRžú#Jy¿ïØ¥ü'göW¶°jþÌšˆqÂl*ÁÆ—>N¨êi”—ä,G¨WÌùàµÄ9èâ܆>eÉyˆ8Û\œ=ÄÉ}Õä¼Oœ–×LÎgKì59 'ç “Ó‹8xqÑá ÌÛ&'›8û]œ‹‰³ë“3zÞ59«ˆ3ãÏ‘Þh{8Þ¿9ÙóOñäè¿ãáxý›=¼_Ô"<ü‹Sµ‡O£ÉÛ—xxL‚öð¬ø¶fµ»ìYm \ìуZpØ®‚wÿP úÇÛ£“=>®)B×¼»½Ücׂ·¢ÊÈw+üÇžÕ¢ÅåYíܨCáªÍäYI¦ç¾®M8ûWÍjçÔ,œéªK4•ÌÔ¿{}Œe` ÇÈ_ä–Qþ“úµ/'hÿù®Í‘pÞ0í?Ø9›ç‘ÿŒéªý§&¦-ÿ™qšŽ3vðxg@rU¤ƒöìçŽÞŠVöÓþ³¶o´8ÃÎÇ^CÑGì?…QeäÚéðì®9ÖªÈw>í?½=÷í7ÂlÿYLÓÚ…ógE´Æ7žˆc±ým¶ÆÓU°Œ€ß˜mµP¨m6Ç~Æ!õÈøÚZñø©|ì…ŽÚDzÚô±Ä µƒ|j¹û{ôxícO´Ù ÷´´ÿìòµµâñåEÚÒ£Néá?%¶ÿ`ÌÝÞ´ýçܨ2r öGøÞaÿñ¾Û…üç$¿öŸžûÚÆØþ39ÂâÙþóò„=â(þ£‚åÆ˜þó(…ÊrÍñ_%ÎA—Ì>âdÚ2ÐSQºÀ—ÚÁ”9|¢á-Î>4¡ýðEÕséj—zŸ)È8Ó_\'í‡KÚôÃP¶öÿÝïKó~¸¸§öÃΞ`üà‡ûl?ĉÑgý§äk?llsuwí‡Ñ} ~ˆãØŽå‡3~„fØ~˜á¹üð\ÛÏðÜ×~8ÇöÃ9óçUGz!µü'ám8ÿQç]O™D¦ÿœJœ Ëä "Î ËÓ²uÐ…÷ç"žÈÿ5úG´l«BÚ£öE•G}9Q{Ô¯=Ï8ðƒG­?I{T}›-Û–8íQXïîQjº«Í–m§íQ8œ=ºG]h{Ô9QeäZî1< Þ2Óöï¼J{ËÏV+š³ø²à-ñm·ZøåÌW5…8+]œrxK‚ß½/#Î6g¼.Áô±›ˆ³Ë¥'|’á?’s†ñ¦Ì#Мhêy2MÎkÄ9èâ¼ÍI&çc¤½“æL¯<[zæ W|¾!™îýÌøÄœŒ}¦ždp:›œ“ˆ“ÑÅ䜙d“s68)&'‹8û»zj\¼®q8içTw){j\ÛOOìtù[¼îâãwIg]ãfD«£ÆmÎ×5n{Bîþ¡ÆuHÕ5îÜ`[ãÕýöxutT¿C­œ¯kåK¶æ;—LÕµò񨄮½r¾ÔµRDmP+—Ûµ'•G¯•-?¢Ÿa·óÑ’%kîevÍíæ¹¯k.΄Uó¨U÷"ò–P·£·óCæ¸HâPr¶å §=ÊÙUîÕjŸ<|ì6üñdíQ©mŽ &ÏÔ…“èφ÷Õý+¼¥Gí-H¥·‰†·ì\¨½eMT‡·£½…?ùƒ·\ÖáØmø®ц¯<Z®Ê+÷8U^ÞÒñ–…½7¬Ñ³oð1ä¾Á¾È}ƒ#÷û•´Ëúõmëoz¬Ÿá±>£]ÖïôXßã±~Äc}™ÇúÜvY¯ðXßä±¾Ëck;në•í²>Çc½Ñc}»Çú^õ%í²^é±¾ÙcýYõï"ë[<ÖßôX÷ErιÞSî+Ûe}£Çú.õ¿yÒ>Ñc}}»¬ßå±þºÇúwëk<ÖÛe}¥Çúõ×=ÖS=Ö7µËúcëóXyÊ}»ÇzK»¬¯÷XÐc}Ÿ'íƒ=Öïj—õíë<Ö=i¿×c}[»¬?ë±~Ðc½»Çúõíí²^ã±Þâ±ÞêÉùXõ]í²Þâ±þ’ÇúaõJõÖvY_â±~§ÇúKë!õ=í²¾ËcýõO¹?ë±þf»¬_ë±¾Ýc}¿'íÃ=Ö÷·ËúfõVõƒë³<ÖÿÖ.ë›<ÖŸõXÿÄc½Äó>ÈÂàÑßñ™Gs‡ƒÇµ=¿Ð©8¨Sá÷.³ýT³ mëp»rl®'Ç6zrLïurì€gLÒ>ëÛ<Ö÷y¬=5e“Ç[¾k—õ=Ö÷{¬[ëwz¬»·Çúrõ»<Ö÷xr¾»ÇWWÅWõ»KΘV¿»dŒ{U*u*¢ùØOå«êÝ¥Ÿ£¼»ä‡›cu•öócŽýîÒ1]ƒ{êåÝ¥ÃÇ–9·Õ~wÉ{œ£']ÿ–× Fæj†Çëð,ÑíuHoIi—õ\ϯ÷Xßæ±¾ËÓÚôl—õþës<Ö¯õXâ±Þ·]Ö-õ,õ¹ë5žœïß.ë‰ë£=Ö+<Ö×{¬n—uŸÇúõë¹ë™í²ôXÏôXŸã±>Çc}t»¬öX¯ðXßè±>ÕãuÚeýtõëë=ÖOõXŸÜ.ëßyZ›ÁëÓ<Ö/ðäü´vYŸàIûõ-ëÛ<iŸÓ.ë£=Ö—{¬ßé±Þè±^Ñ.ëC<Ö+=Ö7y¬_æ±^Ó.ëÝ=Ös=Ö—x¬ïö”ûòvYïé±>Ùc}¹Çúõ5í²òXç±^é±þ õkÛe=Ëc}‰Çz‹ÇúJO¹ol—õqë+=ÖïòXoöXßÜ.ë‡=­MõÉëÝ=9g»¬gzÒ^ã±¾Ùc½Ê“ö-í²Þ×c}šÇúõ$õÛeý 'çO÷XÏõX÷yrþ±vYOñ¤}‚ÇzÇú=ë϶Ëz†Çú\õFõÑžœ©]ÖS=ÖK<ÖWz¬ã¤]ͤÆe&¥g‘N êY¤QÊ*¯ÿg‘>Ÿ£Ì"=^gz¦>ÍæGÌ"ÿ‹éü ®¶g‘×þˆYäd{ÙÑ#ƒßŠ“uºŽ¾Šå¤ý5ÔOùL|=ÅçÁã–ž>ÿ±ø¤UUWé]xúÌÞµádÝ3ê})¿>Ù'0çì,Ÿáýðƒæì=Ù™;0ç/ÄÑ+“j¢Ú°9"Õu3§ q>9ÁäôJåsóYÏô²ªRYO3cˆÓÚVºl]”Êßöq8³Sù­p‡³,•ãcÙœ«SùDhGæâÔ8Ö+.(¿l~å.—õí©|¯ê¥T>{Äá|Dœ Žžé¥UU ªçËZOb/Ý7iÎ)½œÒbÎyĹӥùBâàcU¥yú’Šé³e™w°eJ”§¼*Á ™œ«¥LªÁ¹£ûŽÃy²ŸóïXUivl}ЋÏÇvdü½©túšåÞ¥·î ìý‡½#õ éíœ>ÄœÂÞÎ{Ì™î µ°·³³HçÆ2_¢K¦®7AÁÑ|cog,ªÒÞÛ™‰3çÞÎ9Ïç;u sÞ%Îfw¹÷vV±à½•µ5³õSöïT*=ñ}øœmGÏqÄq׸ó‰³Ü%“Û'²Lçg“+ç—öásÑP‰“zº)sg>{ß‘¹Ÿ8¯»ô<Ý‡Ï tdÞîã¬Û0ç“>>ûŒ æ|ßG@tÚ»õå¯B8zNíëœRÈœ¡tuÐ%3¾/ÛKצêÒª•Û]¹:«o¤õª¾>ß —žŸc§}úô9Ó§Ë;NÞÒ×gp¦—-®šWSªvh[[úò.Gó“Ê(¯Wú:3Mæü£ody‰Sœ•7ù5™…ó|zí]Û:ŽdîtåÏéÄìäÆüÒù³+—ª]:Ô°SXÜÈÃSLB(ÊEµïB‡*?E?õМªSœsÌT»q Ÿoã´¢7'«Ÿ»Δw:œ9>Ëg–ÎíꀫåßFœV£e•?yúE“õ‰(‹²ŠÚê¹j‡’Ž!Þ£JM6kÓKê¨e½Œ_¸l±CMŸ>³¢âÿiìZ~#9ʸ…8!!nÜ—ØŽ£X^{wãå€üNf±×Ëά­H‘*=Ý53½ÛÓÝéÇØŽÄ•Krℸpà�PqD"â’ $Äå�‚?�„¾G=»gz&R’®_}õUÕWß«ªËÓYø¾åóWíoL}ÿú£Ä[÷{}}áqÆþ HâéØ7×ù«Kù6 o·×íÙ#‡ëMi<]çïX>ãuþ½*µî!ÍBÝßÐ#¼[çZûhݾaäg€üÉC~ȇ|¶Î–Ï_Öùo4,Í¿y×kõõ þ²†ãÇ6ø×q-òêF3Ê|íºe%Ó™z[ªçõý ›™3òΆ=3gd´Á_=üšÒ™Fʺ×)«"œêÍšæŒßÈ­nLYªïy4?Ühf)òo<?ÝÐ{|Ýê“ þ&Ž¥ùßzšðÇ ÒGÿÜhú±ÿm°bYšW69{³È·�ù»/g@>ö½M}߯x‰Mþ"žÒ¨`˜•¾Ÿ`$¿ÉßȰ|âMÌ\0Û´'ŠŒühS¿m×|~¼iOÚù _}ŵÜßmêý²nõ9"^ï_lÚ34F¾Üd;v¤¡þäÉ"›€|äIþ‘¢±œŸ8(ÅÈ{€üÕk•µ8ØB~Hîù@þeå zˆ¾å—ž|~ÿÿæ•mõäìÉçË×ôIŽnõ­¦]¼Ȼޘ¿·¥O`t««-¾Ûl[I@>÷þIŠ—üç~½eSæÕO¤Ìî'Þê|4ð8ÿYý±‹ÕŸ¿mÙ·oŒüg«i¯¼Î_f±|¾óº¾Í¢çµõ:XÔÒ¼ ȧ¹‚Ò¶œÀ¦wÿÛ‘ÖcA:–¥.æõÏÜ2ýܳ.Çé(3M‡ÃBÎt)‰SÃÔëÖÛЀoQª[Ái}GNŒâ¢¬ôWåD {CS8I’‹ N!(ö« ¨êüàÞîüq¸#\8&g ËfÜÎF¼Hð¯aÅéJÁM%&A%ýîm¨ @x³·+ø¯áq”i$ß,Óª¸Ã¨몳މxšA´–…È¥TH¿*ê°ª ‰IK†M Dp�CmÒáð{(qááÐn`ª p2¬ŽêÑH‡QTHØDS†Dž•qg)Û$(Ë£»JrŽ'Aáý xPé¥~ùv”p–dOr’ÕÃÄtÃT,ŠàN Ï g!z9aŠGuv¤n‹&ê¶p¦â6iÁn3Y·…3ÁN¸ƒ‘[eèrk¢n Wžn›6®[Õyf'`1ŒÖbƒ'AϤª=‡ÊS®£UãJZ/+v^dè×îW–Ê›cÐG²¥›ðL‹ «Ç×q$çU2qÔ@áqWbÈÍY U[ÛÙ©1Õ9ÏgûT~Ì‚kw×Nwª£k§'¤W¶ËôÚzS¯èe6 ê{iYi(íœæ“2ˆšX]Ê å*TOÇê;JïU附¥7FžÁ’°m7ihiy•]±Ìåå“¿«,Ž´Šx#ƒeY"ƒ´Iáø\- ‹¡zae‰¡iV7‰\ìOŽÛ||m®Iá`dàÍzdûkRx¨YÒ£»« ©ecm<Ó»ò\‚8õç:9((Š7?Ó.ÌP„ºœCÅÌ7þ3vI8w·« Ò ]�Eâ–y‚áuU¾žô ÒÇlšg)IŽp€ˆ0Kg²(!"‰QŒýÅb2×ÎlÃ!Ȧn+]á4½²#tP=L u˜šXél¢ÎµÓ©ÉB›å3×è¼è;פÎê4ÄXl|ú•8Hâä`ÂN€æ~XUA8¹Ì‘v!¿v[c´,4ÇݧŒã8ëá3þ«5‰“Ó¡N¸íªL‡˘[;Uú©ÉZcQ5i/;Ù O)-¦Tά¤¶¬•Û"¤µ¶È6cËÚ¬AÅjOЃ.!:ÁÕ³¼++9Åx™ƒì8Áó•J§²šd‘M1çöØ”Òåð„b¢uÊô­Ÿ ö¢?™s›e/¡?Œ`zSQÖ)%¾ª3®Wúx°{%aBc(÷w–ÑòÒ®À”–|:P…¨P=V Ã�×I‚õUR™ŽüÁÞîâø>¨A~ïábrP¨7J‹�j ;Yà^¥7fÉÁnÇrOâ±Ø;êõv÷W¡<^™òdeʳ•){+S>^™²¿„d)o/G@{Ô)MtÊ+ Ó.“¥!\&JC¸L’†p™ á29ÂebT©{§ÞªH'¸gû2fj³l6™vôì™–pe·´„}ÒrHKh(ˆ-%¢øÐ©œÀx{ѹF¥Z£%œh–ЬcJ#ñ%T*t÷HQ ›di(µÄ—0Òï`ŧòÝnC'A Ù^6ê ˜ÍôNʃ}•Kzù@§Ö\ƒcËnÞ¦\b‡úÈ^Ä[²‚ÿ-1@÷ÊcÇ0Q#øhAFD—ú¬Ó…³+m«n½}ª6A&MëÎx^ƒîVfÌOW¼ééb ´‡O{Ǿ,ë)Q>é‰Ëô< "³žœéM$þG=_PJÉ9 ?Ó¬ðstRímÊfí3 )ZÊù=÷õ<MTo‹FY§…Çå2o«‰¸³ƒýûA ½‡*Ú™+}×ì^º,‡”SäNû%r´=KÞ53»ñ‚VØSzj‘Ê‘©õ‘·îaœÍ'ï`݃#Jâ݃`³…4jcjRd7GwOðü[¥œŽYó�­e®N{›[�ØÃ…i¬ .=n‡æV!©&ßhrz¿‰Ó½ÝiNƒ\D¾_ƒKÀ3zM�*û­Rc¥OeZ‰I–ð±ªï'âKÌq*¬®ä­ª¯²œÛõÞ‡ßuzÝ·"„t (Æ¥Såæá$’š°ðôßé_Aw)n©l;Uiº#´nÔ`·XÍÑQGŽúã1˜IPIxíNí@Y”kÛ~ß·§ ¨ÜIµèp\mZÎá;89><?o°f¨í»)|+t—Š*)ET)7i!ƒ‚ª¤€$æ ‘ª� à–]` •£ë7("—ñÞþCQÔiOÑãàbspÛ{;vvç¼­ÊÄ8ɆAB#k@!Cv"útèè !v'^Ê;ÛÒÔ„¥2[3Ô"ˆµâæá}KD‘!i×0[p:.Õ‰ÁyßX û©ž7°—íÐêŸë^sžtÝÌB3·Výú³gx4’ÇÛ{o>|äVÐ¥0§|ƒ€zgà519ñf$2<^JÝ*èÜ-Þ6ÊÔ²¢ø–ÏèZu#>˜m ² ·0n!·I¤NuµìT )&ÿΪsºuCÖ þMª ¤(ƒº Œ~ûÞƒ‡ûøã˜%•óIÞ¹—.ðèÙÆŒ]Ÿ8Ù=ÄrÚÐ â ãóûuzÄ5ˆ)LÇ}¥µ\ëËÐ!+—AZç ªSn+Ä‘ÓvÆÚƒyÒ[_<‡Žg,ÑSÔP£›ÖI…НŠñîЭÑÏyvóÀ–ó0ÿûîcURNâ‘! §¹~ŒâÑH?wM‘íïÐòÂã1;N>P%ŒÙY] »Ôõ4‹¢x£xFWqQÕA¢RSJ iîÓ‰ ¾¨= ?{þdл8Oû§ÏO.ųÓóËcqÞë„X}7¡Dòf¤ïÁèƒ*€qà ø y‹�¶oÄúdpṵ̀â¦çà $´Üæ “+Þr;ßiÿ –ÐöÔ¥SõÊ“£ãþ9ìúq§xz‹©#× ’Ó™<!‰‡Ór–"Ð8ŽÌµµJF§·à0ÀK[.=ã†Q5c˜¨'¦8Èó2Šj^…€^ò’ü¡±æÇ„Ìɬ=î§+ ¡>y]Ž¡)N7]"!´†lŠ^¥]¡_º`Í4xî­¬‡êTX¿ñ0RÃH•H|BÚ[ iJ–Þ¡ÂxÙ˜@LqÌzÍÐKùC˜ìÜ%`¦q“éiZOABÈ)/²¨NdÉz�3YV/†|õ (ƒ–Ê(ˆ :6ÖLôÖ¨ÿ¡Ùß6õ©¥Iì@Ñí$U>¢…F!Dœ AËÉæ.`¤ÁX^Ó9…–-éP´Ø¾NŸœXz52 Òj½ŒäÎòzbF¹[9®‹2W%F2QÅé]KJ|cÞrªÓ¾Ã9¨׬¨ÅÈíœ* 2­têÀ<ËÂMŸxýßD."R×BUiž”u¯Z”C "åtNo¯µC°S†5ÏJOÙ'xè>1ƒŽÄ7@áØ>¾, i\�/Û4„¶xœÒ~A Xyù°â-Þ²]®¦«ìÌ´:2ìðÙH×ÚŒñ†ÚAkÆÇ®;öôHë°1.’thÊœG¸Ý1u }Ÿ)Ô½PÛ›J`æ®Úq;\´ã•^º¼ ò˜VÎó©`4Ö€ëÌR¾ãźÐm™·ÖKà_yùËêôæpm³¹^ž=:^9×ýªí òÀ¾6z+A+ˆÚ`ÄëGL\ Úýø³›iGÚæNQ—SÝV÷•Fá9 7Œ°TŽˆZÏQ€›p'¾G ÛV—Í·Àx¨¥D¥….EË�Ù7½¦ÀæºDÏÅ—u¦=|g<Wæýû¢¬€W–½ˆëjðÜJt© ôV»VaÒhôKȱe‚‡ ¥6»]j7£›-ʯÍå\× *_%ÕÅÙÈ_„%žÏ‘°iVòkfUÄkad&£C@·)©¿J1Úd}à ÑÕ“¬1`oÃïœé<ˆþ8MÙ[]â^{QÞcÌÁ K­ä£)çn«TóPK ����v©Æ@���������������com/sun/jna/aix-ppc/PK ��� ƒi@ò!±¾Ë²�ÕE �$���com/sun/jna/aix-ppc/libjnidispatch.aì½ tTå?þÜ™I2ab;n§n”H&-1lˆ•v'/àD¢vwÔ¤A¤&cH @ A½7s'4Qö& õ‡šÄ èbk{ð¬{­@Â6$tOºe9áE +­¨t‹k€ûÿ|ŸçÞ™Imw{~{~á<̽Ï}Þ¾/Ï÷åy•Þcqÿ°Äù‹ÛÕÃØüÍÌ“m’lLbRïNÆnÈfÌÜ¢áÏÎ$¯“™˜“}=1|7ៅ™Y³Ü›ô/«¡Ú߀_ÊKA”‰_ËÐ ,î#qo%§à³3«Ê×à£^¾‰ñz¥e,îú>*ÇäâŹ³Y·ŽñtÉÙ¨m“T«fÕ­ñUU¯‰škeÒPöødv–UUýÈúå1Q¦w˜)É5>srà®ð'[M¯tìÓFäÆé\Lê8©vlÕÎ…%fÝœ¦í”ï²ùÝMÌ麋yUÄ…ðM¶š_ !Oiÿ^eÎÐ^fÓÅXA·v‚žÁØÎÌœÇØœVÆ”™Åþ‚n–®&â]bº™»XÓZç„(›Kõâ½GE¾­ó™%›iZN†¦)N³Hd3›Ke–TmD:?•ËÓfĤÍ0û{PgN“Úæ#í4Ëü ÂXp%êÏXâ 8Ê—·‡Y(_i¦e~àéÅnw'³*Îb?µe{uúT³¤cH²3GíêÚ†Z_]íãÕå¾åË×V/÷5T§rÜÍiÎþü„;Àbêjg „G‡‡øÎqˆï±xä¸ëFcÖð^f ¿Ë’�·CxC½Û^añs62¶r».°‡Í-Ûs´±l=ßòžÒFÃ?g.P¼µ’p¡i¾ fvÙGL üˆåä(ÀÕJ³¿ ‰Yó~Ä$5•¹{ŠßrjZÛ]Ì…øÙÍ2h7SÐÎÝ­í*Ö.½C´ݪTxÄ7¢WÇ^fìÿÔïañÁ ´ýZðÉûïô£¬6*‹hîÞ;vVŒ*àU÷rü§ ^ŠÐÁgÐa)KZ¶¬¶¼~mu}yeí2¹¼|º_ç×^µ‡Mû–Þ7o¥A{¨ ‘r%K¤³1{´Üº5ëÖ¯­Ž–­¨ì¶Ž4íTGvšÓÔ§!úm‰îmAfA½=„ª;´°$2G(ƒÙlVjåï–`-ð×$ æmgÉDË0ðîîbÛËrXn–™éý޼Nm4/GkÉCã7æ3KO"‹ÏTÙW ºXMðð4³çÊ6–¥T,q(µ…àY„ü$á÷”þNøëAØŒ°S|ça·Ò\z¢OPŸ ß,úñBÞ»Ìbð‚»I“à Âaprš!àBQƒÎpA´žHg*m'ÙAÏ-x–YBÞ·Ðûñm="Ô2KÛ÷kS˜í°ò>®çÃsÕœX½÷á¸HºíÍaÖ!j÷LÒóéz¯œŽx,õYCTo&og<èaFÿ)Ì|tØö±y­í¬0pϯý*úcÞf.[Âr=Ìö:ñU`Ï¿;À£‡As è™Ò–Î’ñ>;o‰¦U.a HOòì çãÌ[†¬ÜäÐË©)g“Γ•j‚ÉìuìºOúÖ._—Êù‘ú• 9|çºÄŸÄ“$7ˆIÖ„Í€k³r^ìGî&¹!Ù! Îë�çi’Õ$gñœŒ>nG~sf"ǵà9”—¨iA™ó„DøÌY©i2{ÈO2DÉ09Êzì¹mà¦@?Ë¢ò!#¶o•…¼É±‘,^ÀÓRš`êºÅ£ÌÏJ˜e>øYß»P/Õ—Ý3YÐ.ëTäcæ3,ò¾Žº®•–äpûËàRÎöœ f¥|xîã²ÑÉy«"*g"ò~1K¯ò­}¬vu¹¯jÅúu Q‘_¾`]V½omCV¶.#¾<M¬MܺE{^§öô!º�/ö­H÷Œ Ï— Mçÿ mfM…61´Š¡Eý$´ð²4_­ÿª„¸#5ƆñO´_T³Ë€ÃTÐmÊǯÜ6B&? ¹6Ÿ%érö:w—TçêNÑà›häUj̯äA¯C&¯€L¶iÇ#Õ=6Á y9<ç)ž/¹êm+ùÀšŸãfaåEŽœÀt{K*Ͱç*µ÷;ˆvÐñ—o%û*tè Ú42û•l­ ¬X®¬Ðìn‡’(ô�LÞ @É“MÆûdßkôà<²‘J¬gÝïà|bò‰ËÆÌ%ìì^â-ÐÈÞH�?8� ÑÛþ}'ð [‚Ò"˜Ðçκ>`RæSÀÑž±SüûKc'H^Y”&x ³r8‡³ÈûM×gR’¦ACJÉéô|¥€ïÙ"°Qµt42N"ûÐ>cDcn;íK¦þ ÛÈA}G¶™ýõ´mde¯¢+<Y[g"+ÚÝ­ k£·R^UØ…ƒjelùl ÅSyÜžÝË(7ŸÊåev±lð—•—»rb¹rx^=mA—6D¶,좚ü^+ñžTñ6áñ’ ^4‰¾q©Ïz?¹Tƒç$ýy)ž­ú³·X;ÿÚZS¬]¬Å/»Ü–5'3G¬ U¾ÊWYSU]/dÙì8!¿ä§föTõp€¹Âï17èÜû2Ÿú”èG¬úàŸ¥†À³Û˜:1Infƒ.äQßcù9¦ wP·Ç*&ÚcF>#=åy,Ê•òPºÞ&œÖ“mLCZ›QÂØ·!•4iµKbnØYùãìú¨Ý7̬„‹J_]]*S-v‚ pë6»SÈ æu‘[wlX³Ù‘c·\¨s‰6:© m•h|‹ðûÌÚÛ bÉám,%¼|¾yNÙê2!/òù2L¯ }Ò³yλ_„½~ô×TÆ<§RÃÓ ,¼Ûç9ƒÏà×1Ïù÷ª/Ãâ¤òñž>±^àΰŸÙ`,í…[^·¦2ÆŽ^9ÝŸó$«!YjØÓ°g“  ƒ¬´ìB?ŸQšÁ¾­d°AYZ¸šdhNs4œM<Ž>ÉûúC/ÑŽøT1{²z¬»v³úŸ,™Ë–%ç:ä#v­_©…ÛþâÙ¥‡ìœëlÜsêiÈu+lÛ`ß%öÔ’ÙZBfQt•ÂòfåX™¹ÔÊrå—ïv.�Ÿ%F¦{a'¦~VîyÀÏíåæ!wÕNw«ºYÐæ4ñ$‡"~é$’߆ÿƒ²©>×Ë\^ÆÔ1v<ÚóÝ2áû!|—]qàA|“§-̺¼c;f1Ò˜¯’¦iæ6e“Œª“ûE[\oÃ6¹<íb¤µêiçöÁåå"Ï(cɃô>y>'äû Ê6`&I·ø¬,8'?¯^Ý(Ê…ütZQ~œÉAmÇ·ºHŸ5}Ö´…ÝÈû¬Î¾5ÕuõÕkË î/[ø÷©”B^6Ý/‡fúåwÂ<+ ê$§[/ÀÎâ©\S¿MˆOa «ªW­Zóh5c»Îðñ ›Nηùå´´ÕòÏ3ýò»wúåKy~Î é6ØnŸüŠä• _CýtªÂ¬S’“ó‰%«A–¢n…¼ÛÇÒyº“lVPb³!·²ƒl®ûIVØ—¯µô-ÕÂ}²¶¥¯Së¡t}»µ}'´}£Ú®ƒ)Z/ÅÌ×ú.ÕÊÚÐÁNm˜ÇíÖN<¾ÞI:¼žnDÿëdÖp7|‘àû~5åÊvÒ% Íç¶ØÐ ø4 1’Q¹"þ€+ ¸Â€+ ¸oaÀÕ¸Ú�WàÒåˆw(y† ˜wÈÇYÅÐVV3´Õdþ£P×GÓXËÑ>êc[ŽYÏÑ­l‡Rùßd.×VÖçÚÇ<ÐkrÜÁNé©dñ%•Îo£Ÿí’+oqȺ?Lß@0ºÒØNj7Ïçc®Ø¸Þ´ñï}Wxïƒlƒ~ïlñ±¼ØïÆ»\™Ð€ïr2$æûŽ ïë¿Ò;x%åÍžðžr-xˆOIwA?[Hf+Ñ·‘üù•‚ýİ;GaóÍUÐÉ 7fбݶ™Æ"cG‰°•*MŽ»™v‚ôUd¬ v;âúÀ#gH S¹xÞÕ;†ÍÁšÅ¸ÍZßcÜl‡®Eût]˨½ÔN]ß‘ž�¼®0dC°Xø)°Á5_Ú3IÐñ¹r³ÉAåpò# zHNù73ËÖ"ø4Ó´‹ºO“X°GÛ¼›¥p=3 ö¦û~‡²0¢g vñêaØÉðL~É,j ¯«þ ꣺©NØoáqx±@F#M8Nø—x7Ǽ{©ż»£ø2]-‰}ÍÀWÚòú†µ¾µk}Ru¹T –þ¦¼'ÓÏýžî™~CFãSºïÃù T©ÛÀÝl0ï m4Oåøõ¨¯ÂOÜö1Ÿ€]ú„„`·aÆÑŸðØ|ß»µ“I4FP‡ ßêÆ<à»ø¶Ã÷Žà#Ìy…ñª7¯4ÞƒoòYÈw¡1ôYäÛu£ºIÔ ¼·DüL ɳ«ŒGíˆ)xwcT1qé±cU1ñIF|4nl„lˆ˜÷Ó]¡c§ £A@"&hjÐm•ßýHŸ¸SгKØÝ±¾¬1~Ìýظªã~—™Í {$ÖÖÍó1w ú8¯¾Ìgéô=°ýc?ųYN�·l–©M„ÇIp/.b?¿ÁþŠàx«×Õ‘SΪX |/=ÌaÜ)`T¦G`4àCÿò ð•®´çN´ãóö¶·h|äâAâWÂA`ãûþÀÛ÷éBâÛ/ Ö¨l~@dLÇhï;Ðó¿ÍóÇÒÄ[p½tÁ/hóé[Ô†ÙfãeIÝ,½>ѵÆA"퉌q³:–±Ñy£bítc¬ÛðO ¹]•#ØRbìý˜²¢ö¾aãGôÅLfýV×~ò¹®È€®°ic¤Gt}Qµ±62’#¿6سôqTC7œ˜lü߬øÖ‹6ŸÒý·Ãðßá|XÁeâ¡}²%‚£¨>y]Op­ð=ê‹*•¿N™=bè’ØðçÔ)—ë6*ø(‚£qŠäÏ GN\YœûÇ/¡G6‘‰ÌwìLx„eO¦?€»ÍPʼn˜gÒ;cô Ÿï0¾ëiò§ Sä?A§ÌžD§$_A§°ItÊ™‰ó1ãæÁ¢zåµÚ^¦\¾œn¹øÉuËÁÿ1Ýò×/S0ÿúeÏåúå̇’~/«¸’ù‚:¦YÐèw¾?›Ž¹9¢¢ ‹Q4WÑ3žIõÌW,/5Ø9Gã"IàíE CbDFÂÿî1ÊH¦9aŽ‹gf <¬€ðNŽ yüížLjÏ !tš²¹ê4%+ñ 6(éÅ~šÃ§ñò•Ú‡üî&ÉhöB¶HsågØ\1žÏRÒlÌ©-Ζk ³euq¶2í¿\»8»ÄÖ<¿Äb™/ÿð¡gKg6ÏüæÁ#ò+™~%.íˆRîõ–©3”¢éþ@ •ÉÒ•"“Ãõ–¦T/ä|nŸ„¸1ú¦x'C”§T£,‚a#Ê*ÖËZXì4sý⤱õœ8*‹d?³ëñVÄ•ô"-3ûs€$š[P¦-ະ$¥ÑÚ†")¬,�ÜUð•ás_6ö¤×Mc‰”rµW�.là¡ç¥ÐåIæd7Ùë¤s) ÑÌiæsýI94N>óYÙŠ<-<®OQ½^W9¤§ë€¯»šç+ �c5h¶0Uèøºè§úK Ç×¶¾’(¾ Ë4W¹WÇÓ à©eðñ›+ãɵÿr<!n¬¤G!÷šiÎï„ük$ ºG±JaȬaO®aMÃ{ŸÌÜÙÎ>”W>—Ó]<ô,Á£Ü67[)rg+îl¹ÿé½àƒýhS Ú´-íH`lÄ[¦¡ž·×F>.–.¿ Ú™©MÙ‚D|âÆè›üV¶·´ð-|!#eAÚùP¶·äàÛ„²ž®<µÉÀQ…(KÄ'!nŒ¾•Z‘þé ¯¼cÁÙâ/Ëoƒ—Ÿ¶dË{,Ùʽ#/ƒ—ûAŸ÷,óï<ð¬\oÍHÃ2ý%q ÓŒûŽ}ê—ÏQ¬§½¼Lk¦?r‹6X¿éÈéFžt‹6ˆø$ÄÑ7Åìö’ è{àYåa”Eº` e¹uš›ý>òM‡«‘ÊÒûÈ‹zi¤>‚²¨_µ�Ÿ5àé>ç §³ÝrKÚ—•Û™v¼ó>«°~Á›—ÇEÍE]êS2gZn÷nmX|Öå“[5òÉâaÖòòªÚG«jïD"9C²r‡.{!W&“9®nGBÆ8tYÃ3dM!úZdÌ]ºÌYŽç{ 5R}V.oÀ?è/­ès\æ8•Ìb?øÑ©´.ÐæaÈÛîô+OV‰ Õ4ð^|ñ³%§À‹5 A—ûŽÈûÁo¯Í„Ìá<•Bü¤ó™]³rëCÚÀcO?«¤#!òÔóÿ"ü墴Éò”ß%úßÓôk¶È¹¼„;ùÃ¥Ù47qå+ž¸¥µF\Öèý 8ð“¼å²f¨Ø"òe3—/­œ—íŠÓe—ûî°+ß(­Á§æÑ|÷v±ÆˆÏgÁæ¿ø¬l²q¯É½€ðc°•¼þ-\xDn.8"÷ã[¿.“¹œgÉÄùÒÌåËl¥Ð+pÓÜÜÜÄQê²å;ÀM‹Kà6Š»g¥8ù´Ë‹¾6#OF#ò¤‡Ë“S2[œVLë›J8=åÚJmþ¨$›÷Ïó¾Iý2pJo Ù[R9®ý)1í·ëqV®K†·QÞÓÅÏÊU"¿²@‡åš'áæþ8M0ôò6V¢-N´« Á YÔ‚6×¢Íw€#ËLI…e~I'Úª!í&„ -$I12öÑS†Ì¸Éázý<¤Ë Ÿ„¸1ú¦$@ft‘Ì@ùóQÖ/PV7ÊÊ€ÌÈ@Y2>]É@YcT–G”%â“7Fß”t·¤|}2Ã:rC •¹ëù#àµYx&^u˧+슺Ð./ÍVžöf+-K³åÄÈ~+kn»l–ìòiÎCDcÈQ]¦l »µÔ·cÆ3ݪ%"O²Iž¬ZSÅå Ÿ3%Ù¡ ߉d ɲ ΞL&™,÷‚¯UÝq‚+X¶ y ¿Í²•Ûaƒ<§¼þñùÙÒ*Ðd¹t„ú‰’¥wÑ̲ú1òU·A'ä+Ù +EyrÊÌò?¡,·^VñCÐc‹…n]‰²¨¬Å‚D¼qcD£7x>òŽl ÑÿãìŒÉnò±)ì‰ò 9ÀÎr9ð¤“ìŒÔÏ) ávΓ1}ÿàÏÊ…ÜtwKäeè}H/ɡϖÖ'ôVàøTƹi*À ¬¼ÈD`€#à`B<E¿¦|9 \¨ Àã(ã)”±eXôʰDžëÔå¸@Çá@þ°xR<¤e‚¶ÐÁr?hÛyôKäü?–×ÐÆòžåt§~xûtéÀô^ΑÀ‡ýò‹¾#òÛgu¹?Ýè×°1nwäÜ…ölÔmŒfÝÆ¸‹lŒÛyÿ.-BÙ¿EÙÊ:uѯPY¿Ðu�l™À&ƒgnw¸^¤²tžÙ¤ëä‰gnwpøy½òSÓ¿ps¬$!>‹ÑÉ#Ä—œ'®ÀòÛžì´ZüV-Í&:ʧ@¶£Vz²•JèY›'›h[À7”/>ÿè¡:½%ÍêŒÀ0ø¼• ø‰¦ß‰ÐÔ¡Ç'!nŒ¾É‰ a¥ màT ¤ê´…œ <•,ʪ4ø#Y—UÉ¢¬t”…oÔ/äåÉ^ê“›x<êÀs/Éù€ê&{âó°%""s”øM,/_¯\>ѧßrmÄÿ„ŒÒe„Cîÿ?c7?àO+Ÿ†¬†Bº„ìaò[¸÷ Ø ‰<KvÜrdsÎ’B¦Â¦kñ½’ñMGàIÝfqVŠSfy¸-'·Â–#›¡ùo×ó;aÇ53a70ʯ÷§fÞŸ†åfæ¥ñ:¹yiö¤~ÇFîWœŽ‘'"r�åÈÓœvåÖí´6\¶˜'õ].“†m mæòá pЇö¿_Ìq |ºñо­ Y <e؃n<Åmƒtå;^ÿ¡—n…mc¸KT¦èðÛõ8+ç»Fzu”|JÞç{áS*qèóèß%¯ }=d‡£íOfs¿óð>t-·eºï8xïS¿2ºpôê&òÀ«­†?p»#táFÝñIy¤ ñMi­à¶MàÊe„^²ž¬e=ûFå¶ŒCþ�2d>•EïR’<ìò–ýi`‡ÊñÞHNÊoßGzt8 Ö°%)·9írEŽ]®O”N}¡yÁ¤´"ÞäþHµ%›è"ÿ¶ŠjárŽúçYЪd¾=¥Óiû,oÉËãè”C'»g庌hE}ü#”ûO"¿rN«‘þB}XïÛv=ÎJqԧч‡”Û³írœdWnãúÝÉñ³gìp3F8¢¹½¿;©^üž y › O&›aìÌå>†ù0ïóºQ@ºŸúÉĘ1·Ê"c&vv啯Ãä—Aòâiȉ]fT“çtyÑ,÷ ¿œl §•d-èäÎà¿ÂŽ#_¶´¢y~ið¶6Å×€ð*øó-臅¬•›?ôK.›—8rnŸ<ã<'â“7Fßä'\×PyÀ£#Ùè‹Èmùi¡kп"ºä ôC¯¿@[K”¢Âl“ ý�áÆÚ¾Ã,Wó)H–OBùÉÆ ²È' /g ù-TÎÕòGeÉ"dõe3Ù+’r/³Ëo´SټܽÚ¼]O]»]±òI©½óY¥&2N²S^™ì-­¸G¹RàýÌgKï§fDu}Ä~ùÔµ5K„®fœ¼í”-Là½"óYytxB ¡Œ8]ÇM‹ÕñK®•1ALJHÇ/‰êøVêcuÊJwvÚ,à)°(›Ú,¿ˆ:a£LÿïT‚¯fN÷—4’ý…ºŸo¥ûކÅxH ÷bŒ®FÝ"íèjÄÑ7CW†3Ÿ ¼ƒ‹1ººÙÐÕ.t]Ýœµ{ðÛ6UÐշѾ’±÷[:Ü{.5s¸÷\’ŘÜêËõ3?ÖeõÁQÈêÑlåV]Vï€¬Þ }ú=Ø)‰£B×fÒù%2à~štl§?ž<­÷ø@›Œ~ùz;ÉW½ßlÒûÍíÔo`£mrr¸•BØŸÓPÖ"Ыÿèl§îOînßBYOSYº?Õ­Á<Mþäþ Ýoý”…Ù7Ý/·ÞA²{1ž©oøõ¾–.€ï=­À®¤Ž@?A~§Â6´.ÍnC¿ëø€Yˆç¯Ù–AWßsŸærˆïÈokNGqZ0ŽÛŠ€ê$9y˜Æ(‘FúNÆØ³å0Kr‹Ê¹b®Žì*¯ÅŒ>XɤúiL-™ߨy²s2X‚²’ÆW´S™­,øHÆwZ‹jwÑš³DØ“™ùÛª&“·Æ~ VÂ’ÖVûªÊ×ÕU?rÇ7]|ÈY7®Ï{ÉÈ>¢è?OIEÍõ%sh>#x sM¾n™ÑžšA}=Ëš¦ ÇFs—ŒÏcòïÁ[Ñæjf™#üVgÙpü£ðíd_¯fª’ñGÁKÌJóh€­¢í6Æ&[WØ:V3kk5‹k½•Å—°Þ¯”®¬4ûK3ÜYøž d<ä—¹ÎÛ,oËäëªÂ[™'¼-BßQ‚iÌ¥â]åïZ=¥áþ\ź°øFê9+©L3§ ­cyUÀb¢õ…[£Ï³¨Ýx6S»K*Y.í“¢ùac];Áá³½™Ô>«N¿‚¨M-u²N¿êÕ•kªª«ÊõÕ­¯.¬¶¡¦üߺj1'9Ý\‡vÎ?¯ùîþÈž ÔÕ^ õµû´CíAm°}«v¸}Ÿ6Ô~R;Ñ!iÃÆ^/š_Ô‡ã-a¾²“íTãLÁ[M‹‚Ë,ÛKØWÇäD“ƒä¾mήòàäU0)Weq¹ÃÌ’ÛÃÌyŒ™r{Y|+àÎKñßÝÔ7ñ«â·¿½à·Ýàï9àµÅÚoÉגΰì,͈ßÛXù‚_É€]·²ÈÑÖhÙèärЩތöÌC{Ç·§ SÛ캋… çM€•pÝx›�oàm¼M€7xý\Wšç,½=~o¤~ 0¬X >0ú§à´÷µÒ…É.ÞÞ‡Ñ7VJG:V@‡6ÒzNmQðn–Oë‡åiž‰óå#´ÄÏm2©áã}×$#€}…>e¦í0øn>9Æ°Ô ôp™Ââð=®ùâÑ·ÐX<úbB6mþ7NØAý¹4ãæóü"GÇB>o[ÆÚ¹=Nk(€¿¶Z¦¢ÏX:jY3pFk ÎR¤{~§MŽ_JO~ç8Y9‘E"mæ3–D¥H:ÂÇqV^ÜWº2Ù¥±AeþÚaQ~t¹ôFK£eä¡FvdI—A΂îøÍ]ñ…b¿n|½K¥¹þx/øXB½&âM9‘xÒÄyåÔmßÌ“|óÅ·¸I¾]ÿøëÏÿ8ØGwM÷TÕ)«–Ê2è=Zÿ§–Ó~—3kª¸ ýß…·Ëð•hàëbËœçHWYÔÀ7ü¡K¸qA‡?pàS¿«‡hùïmhkñœ·/.nÂ3ÍÑ÷ÆÂÄýû*JÓñM‘¦ñAŠ»˜=Uüg¶px&Ú W€é7w4Zzе ‡¾H?£zô|›¿ž©>ä©ho´tù:fXz®U§¼Žl¨ S¦ÿe|¤ÓªýU±O¨±ñÁ¬Éu÷„z+©Þó;sÝêµOßxF׋´§æ 3Û-;ƒó-¯©©,Ÿú4µ%÷þ‹û¾{?µ¥÷3yFøæ¼wªpôu|öêÃÁ»X>Ñ‚êÏ üžg1iöi¢ßG†ÿGe*áýC£ŒT]†Ëi×Âå¹K×Â%•/p9bù“y¤õËðÈ盾P?PL5ȳX·uÜøõì=¿¿ÙîîÏj—iqc5à¹�ÿzÚ{ÚA†|Nv‘ÝÝýÇQÒ9s'v)~ã·„ÖÒ^¤ÍÒEwn#qÑð3|êW {1q”}­Ú_]¹¾¡º¼r™¯¼~íšåk}«ŒµC°q zi­·û_œé§ºÈö ¼Q¬Òm@¾Dìã {Øœ$»`w-"<´Í3Õ~į· ÛäVS… m,©üꘫ¾IÅM+ºDyäǧo(h2¥s™%ïyfAK­ÃÞpªe{Þ –(ÎQXt”ì¾––˜}‰²«•ü"‡«–%t›â­™´Æ²Ë¼iRŸi²þ°ì$[¿XûÃsÀ›Ëȇz_£òÄ>>Óæ)”ó­] Ÿ‹lÝ-©–Ÿ¶ˆ}TÃv¥µœÜö¥ó<ð¼qø½¿uœöÝíÁ}ÀÝ-¼°¼¹i‡ö':E:¡v2˜9à¼ü±?ðÛ‹~Zã(wÞÞ;wkK*l•l“§]HßÏ[èî29źSþkqï5Y]=ÌÞz—åg®›˜5¯—6fÜîÐ}¬Ùgq|ÏÑe°Þ°Þúƒ£%`ù©nåGm"“¥ KÒçñ’8Ÿî•¶ï¸öQ?þäÇÀq>á)|³É«ïóîSŸ\´N"®ç ®OÿŽÆ =ŒïÓˆƒ¯TÏý;ð!÷®b—0—°#߈‰Û¥Çõ´Ts_w‚mÇÃVõ³Xyû‘OÀpöà ©–z9‹Çám‚nä¼Ñ¢À­ýÔÿ3ð­³DÞK§åŸfú‰Æ€tÒRP^².„\ÐT.ö²E$ÐÇ8?ncÚðÍÀW§!M»{tTMd‰èסߨð}7ì¦ÓºlÈçþó^-»X;ÚJ}IìÏ¥þNûÏ.Ñe‰ƒÖwÐØ.~iïÙa½=³E{Ʋ…œÒv‹öŒ¥ ßtÌ%êsˆrÆ’ZÄ^X ð˜Œg§YÙÈh×R‚éfE|ÚÈ?û«õ•/ƒ\ª._×@ë—­YñcÉ¥y"ò_áÇò1 ã\><Öß½ÐÝiqç 3éð7p"¹R´K  [ü—· òq‹GXÝäû1Ì»£ãq§õ_øXq‡Ð{âÞ 4"XÒõpÒ´ 4 xP–þu±Qü£—Šý6i:ÿäyy¨BXüIÙö/-%øæµ‰c^ÿ/ü÷ÐÀ\ÈÏ\ˆË|‘ÝU°g4¹ìc{® :žúwžÆ¤¶b–è¦yß‹§æ¼HsÏÑx:?"­È¡d…®4‰ï¯§1à+­!nMdq­µ,¾%‘%”ZÅØbëB¾‡9QY¹À_ÂÜYt ÙÉ$»i_À8»2qü¸a S×A²ß>’ætZv’¬+è²TÌédm{..âc[°ú†inV¦¢½+‡¢|eÑ®ÿ„dªÓe­}Î3$/&ÓžÀâkyÆÎ |è³·  ÏjiÜzlˆÛúWi›aëëéw�f)'@k23È^·{÷…JÅb/á™Ö’Ó™„ï`1ÉÚ1?ð,¹÷\ØDãâ9HC¸¾½ÈZ%ée–Z\¨É!õ¸¥z\ Ñ'ǧçË—/EÿÍ~UÐôŠukäùÒh«x6)EtÖÒ¥3Æ;­åŸ?ü¦R½Ô6âe…h;ìËüL|ƒ=u ifâ}~7ãw~îîó»”"ïˆhÃ¥p t°+L§£v…Ù‚ü}B—š™°ûø;téy?éR3íž* æ<mÙYÐe*œó4ñÄyç‰[À67+u£íÞŸ( ç‰då{÷;Z¦™ê”ù…ÎsÃæ_œçû}D»G‡ˆçò:µp±öA+ñÅ»».‘Ï8„ø‚ÿFEê2ø/ã ü·÷Ò¨gd ñŸ^O}ÎRmP®Ú‘(hDü#ÎQ­ô©"Z/qZ€Œ´Á•,‰Òóuë{Góõ|sÌF[ëJÕÆÊ:ãçÏ€v¢i‰ÍžËytå|ÚkAß=Ÿ�ž†²N|KÎRfÅÎ]˜ç"2f‚nzÆ\5íóð|˜Æbõg;Ò›²Üzt¿©¡eÀ®1§à™äɽçóæœTðZ9úB@»”“¾¶¢}E+©w;”…÷ÃYâP­,Ií¼®~ Øû>B°Q¿º˜/àþ|.ÊÒPÖïF94– Ü©ÓDþ ò´M‹äe9E÷gwçuÒïÅÐ8¶^ ~o„Ÿ9¬ÕS>eÅý~Zw^¬]Ø9ÎÆïþãnn#¿ÊL°‘áKýæ-È— ï…fSÕŸýº:¤Û™ƒQž¸°´EÐÖ¤Ÿ=1œIg§tqØ‚wò+¬¨ËÛ2YZ§Aæ®Ðçr¬·EèÌì/]é†ýfÖeí+­Ý½j_²Få™Rt7õ­+¹»?;2aŸ? ßïSÍ~ÑÎóoðu*Ñ÷íJÑÒØ÷ÍJÑñïÍ„·˜÷¥h ^æR\iÛx~iàéÅÌH÷'†ïVÜì¶Ò"m ßܱßDüç?‹èSv²2í¡g#éj<"èuž)Å÷‰¦9«¬x ¦M#Óµ©¶ˆü“‘ãÚõÎé˜v츼]¿§ø°²ÂëŠ)_UŠŠ­¿~+ÇíHMPüVDìÿÈ:RÛ³c0ÿ+W–¯©ã4'åê13ù°ùÉÜ0ÝOsTô>6™«Š£ˆ9«bsÚ¨êò‘îÛ#ññ ÉÔÙèf4gÚ"™^Õõ¦‡t¥às‹¬ÞhªÐÓðE·ÜhZ¥~ÝÒÃÇ9rø9`éý<ðÑ¡ ¾¿KâgyîV×[:©Œ“˜à'ÊÍ:/†nq¸¶²|š{†|}Ó¿²z¾ñA“'ï3fÉ;jª Ìð7žøgÞÓXüÊ\X<j*cè3qð]›!ËüW˜‡…,’ZªgH1<³d5 ^Ö¾iWo_àÞ_ƒ–ø>|OáãíÕ’^ }ß cäÛÂÆ²º‡ø^=ÚW±¢åë–7 §„O辆*¦²0žÕ¯ _”â\if•ÏÍÎ|nî ÌÇQ[‚wñ}®hϘLíQr‹mó®Ô&~vLݤð_ƒ>þO¾á—4ëóc~¾‡‘ÚÑjÙIk¥ùúØÄ‹ûH)+LŽ›©~ãëþ:ùøcÆÅ}œmœÇ„Z$Ò Y[ÁÏ‚­Fz‹×9×9‹ê$øh½V0¸LÕF×ßîfòM&=Fß v¾oò2ø/¢5.¼lØUܦÊà{0{ŒvACç_ʦö•fÚs©^ÃŽ–oðüºØB6‡ mkÍ@›Å¾…táûž£³$£|2zê~¦×Xí¡@]§ƒß²l!\tIVQßh¯QñÿÄ:CżÎô‰uìevôËÍ%ÜÔëo-áø9]ÂXc,¿þÍ„ËÖGYÑ0˜#Q¹ -7š~,ÎEc5´¾`•S-À»é«ÞO¸åçŒ=—cZ¾ôýøV·ÑQ]hg˜ðNç(ìÁ &Oë·aOr;êâŽ`𩳠´"}·@fíG㽋#²ÔfŒ¥Ä¥³¯­¬|}} £T®YM'9—ßÁbÎ7¾|G/ôç‚ó-odNàá–ì ¼krÐVÀ Ù ² ópÚu1—šõOJäŸx~"§ ûP¹‹Ó«.Â#Ïð³6A¯·?#™)`×vÑy,´Ö•Fç”™M¶¶ƒ¾‘<¾Ò÷.L‘µ.ÃìúËpÙ3kèŠÉö‡B,isÚ…áP%³{[\ð<;L³ ëc§ùãöúî…Œ{—Ÿk;n¿/|‚Âñ3?7rú3í©cÐcëé—Þ‹µÓhH`ÇÇ~¤ß¨=î§³!£¦ÁÖ‹ãzHe7…jÊ$õʨwi´^£,~¶0Ê ²æ¤¢œn-?ov[Zœ“ðGãûSØÃ[ Æø”f±é奫«]]U~ïšµ•ÕUú‹·Æ·®ú›,:§;Ÿ=CíÂYà²&Ns´¯ò�3\úØ8}‘¯P÷�¾w&Ç+ò.Å/lDæg{²‚GàöÒÑçnÞeª½gò>J3¾:¦ÞLëÎzåðÞesËÞ=ÚXö.­ÕЙÕÀÏÏœ,ÖÌ£6DpcŽðÚiv³›û}µëªïñWV×7Ô®Ym`'‚›.}Ž…ømo¦Ÿì›ØþHéˆ÷ÂRœÕÃn¸™ì–|æfS0-ÎJ¾<ú@EÛ]¦EÅÚoç‰ó¨¥s䣵çŠï•†ÐWh~Œö˜X· AB0!˜,qñ ”&a‚ ! á:„¯ |ÁŽp=Â_!| Áðu„þ!áF„›¦#¤ ÜŒ0!õ¹…̉†p ÂL„t„[nCøÂ,„ „Û2f#ü B„l„;¾‰p'Â\„o!|áo\9¹yùóæ#Ü…6¸ îFX€Pˆp½ßAð ,D(B(~®–-Bø;„¿Gø/Â}÷#<€P‚PŠð]„2„Å",Axa)Â÷F(G¨@ð!<‚P‰P…P° a9B B- „•uhÇ*„Õkê¾O~“ð‘ÈŸÛ÷:_^èØÊ\ãlÖTÓ*Ò¡|/•ŸŸ+q¾‘,'s9*Y‹µs]ï™2a2žòºqî‚i—«t|Œ†w–†~&Ùì\¶^f—kaÕ±³í­é¦Uú~§ú`žX?ìÉû™ie`þ€?ïÓJÈ/¶éf&ÉdŸË/Œd‘^Ù”"æø9•â,ýd}NŽpÕR¬õnä²£y|š@s2ýÚ[ö’.Þùõµ9/šÜºý¬mVoB;a«¹›Ø":CœÿÂó/þ"óTaZgçÅ·Üx¹Í¡ítÅ”‰úvªÓa£ä0‰ô©:Û>öÖ'^ìuÛ@˦½ïª 6ðã&µ#˜gª‘mÌËÛ{r*gwä¤ó5Z›[dRð Èȧ©ì±ÃT7lŒ|²9žËã¼”žÎÓ6Dø!2#fÎpÞêêbŽ9Í–ü,<ÝÎàvE3ìÌfn;Ì‚üŒŒc•ð±%çO”J–\s8éìåbmïA¢Ùât¦A‹EŒ7A^“MÀÇl3uè}¥X:áœû‚˜5°Íì:CV§ºá;÷óµ·j%Çã„u³‘³vìì+1yÜ[ðE`“?L)Ä%Í€s¦3‹æ‡á—þŽ`SfÞ±“øÝ€ƒÒq8¶¥eÅÂ~õJðEμi‰Â÷@,|‹® _,NzEÀ@y(/­« ;ûpܺâvý¸ü W/[Ãˇ&«×4I½Dë­4y¯ÚÖÔ˜ºùV/_ï[^ý@}ueí²ÚÊBq'‰^ÎÒ«–sCL9÷W/‡º~ Á·¶èJë"gé.‰Ûçqù îl þoUa‡£|ïäôˆÔ“µ îÅ÷¬¦3AjW/¿wýêJ2Œ¶.žr[ Æû«ëî†e¡ç-™rÞtN=¯áð1–Æñk@¸Éáÿü¬ËGð_Ç×´‘-BïûLÍ® Ù–L[еwþE¿ã5UJÒÇFz gþ>'ÍœŸS.瘯e|ž?6õ¬6?÷ÉJëAïv³ÙÐof_™C°Óø8 5Q¥4a?œŸ§…:éºÍŠçÁ©’¿gð[_²ñIx?Çu_šÕ>¹?!âI§p[øšþ„D<>qšZìqº2'EûE!]ZãY³f%òœ³î••Ý–l#G÷Ù*Žž´Õ“lõÇÒlþc6õ˜ÏÖr,h ÛjÛrlŸ­çØIÛŽã’mçñ4Û®ã¶Þã>[ßñ mðøVÛÐñ}¶áã'm§Û¶ÚmûlÉm'm)í’ÍÙžfKo/°Íj÷Ùf·mÙí[msÛ÷Ù\í'mù’ÍÝ‘f+ì(°y:|¶EA›·c«­¤cŸmqÇIÛÒ°Ïf mÖðV[RxŸÍN磯Z©̲qŸØ.H²È;ÐW‚¶³Ä?mAÛ9—Ï »û³¯ok¯ò{Q@÷÷ó’7˜6Íð\Sô¥â…%¥ ¿]”z>ùú¥÷bíøäûäÀî![ÚE|ÿI÷H·¶iÎE¾ƒh+?S0¨€Si’œJPrª€—îôP³J0ŸAà3|6ŸMÀgðÙ|6ŸMÀgðÙ|†€Ïð>CÀgø Ÿ!à3|‚Æý q?hÜ÷ƒÆý q?hÜ÷ƒÆ ñ�h<�€Æ ñ�h<�€Æ®4à;í~ñø×Lû À÷[ׄó`š‰Æ=]$³;ÆX’Ì<Þð f/Öþé<ѯCk¡î3üTèýSüŒYN¿?~+„þMñªÏ¤ÒºVîW}tþœm„ÎPì}Ë‚ö¯rÆt *gÆû@›ÓFÇó{‚ëè¾øž£'ãw“âwK‹ßu¬ ¾÷˜/¾ïX0~ðØÖø¡cû⇌?}\Š?{<-~äxA;îK°&ØoMH>¾/Áyüd¬¶­ñ%mûâ·Œ_Ú.ÅW´§ÅWµÄ×´ûâëÚƒñõí[ãÚ÷ÅûÛOÆËR¼Ú‘ßÜQßÒá‹ßÔŒwlßܱ/~KÇÉøÎ°/¾0Œ÷„·Æ/ ï‹÷^Îï …ä³ÏnÚ‚ s¹?œ¹‚ °Uþp³Áó‚'_i%|wüœ%>iÇ{D‡ß+ćº/;Yå RYœ U´#ÊñÁñ‹Tjà Þ àm¼M€· ð6Þ&ÀÛx›�oà Þà Þà Þà Þà= úAƒ~Р4è úAƒ~Р4è @ƒÐ`�4� @ƒÐ`�4� \i Ù?FyèÓOB'ÁC:ºHó4ÿèÿz&ÁEç‰Fy(â[$3Çdc ¤ÏÿñÎ?Ä3ÄC_¿?Éy‡d—™ÛÿŽxGMòŽ>_‘\¬ý×s:ï8е~•dd±ö» ~qÿøþåø?_J¸'¿q^#¼F|^g-Ç{þoá¹IdQJÔ^¿¿zÝúUÕÿ›øG5C¦ëòG牡bíó Ýö¿G–pÛ""[Ïì3ô›­[ÿ†ë²ÈXßGzŸH‚/ÿ>'é ¿¯¾¼/üqaTš„‡œìÆñ<T¾f-žjÖ®yŒwÐPÃÿ£3cu›&Ý×4ìšË΂œà·Nâ3,bµëª¢Ê”‰qÖ£û´zâã£'5ÿ1IS WÔ–ciZ˱-|̧m9ÔzŽmÕvÛ§í<vRÛu\Òz§i}Ç ´Áã>mèxP>¾U;}|ŸvöøIm¤m«–Þ¶O›ÕvR›Ý.iÙíiÚÜöÍÕîÓòÛƒš»}«VؾOó´ŸÔuHš·#M+é(Ðwø´¥A­¢c«V¢FŸc¨<J÷Â}î6›ut:;0 öE±öÁª(=õ}²3deúzŸÓÅǾiŒw.ÍwÀpÉÏP,± o†‡2¸_†|ðŸwäõ03ìûa>νíc¿qg[ðŸÑ‚€“úC`m¬M€µ °6Ö&ÀÚX›�k°†�k°†�k°†�+|†ø uA‹ƒ C?èÐü÷ÿýÀ?ðßü÷ÿýÀÿ�ð?�ü�ÿÀÿ�ð?�ü�ÿÀÿxYñ;ND}ˆ»}•+Öú*«aKOFäî ‹_U½j]uƒ~îÝU` Þ ˆs“èÜšØó\tn—v.ê·DƲÙ×—UU—¯_ml4­\³ªÞ·¶šU—¼gÍ'¤µu4/?6ÝxÑÅ÷ Èçó¢ãúÞS>hÌÓG÷ô¸;™_ºÓŸ÷“ÊFí¹çÎ2ZSD÷¡Æ.úËn²çªÓk|ÿM¿Ü5˜U†zCŸúÕÝèટø!øú×C1s 7Žp?å'Ë7fóSæý*+œ‚þùœ¶™öâòù;j;ù|S˜·£s|Ëzí¹ÔN¹k8‹ÚøžÉ!ïaY4çNí¦vpþ|hœï‚vhsKiYËŽôõÄÈ}ŽnöáUV­yluMµ¯ÞðËù^4†÷¼>GEx}b:_ÿœ·>í[Ìbà¸ì½‘F«ø[Ÿ[Nëë•ÇýÂ_‹3\Áﻇ¾»UíÔߎá7ÈÜ4÷xçøûVõfðÊ"߯ùÚ¨‘—Ö'´Ílb.WâuµÃÆØÇµÆ#0GÎîeƒÌïº5kh ]ìxäh}IeB#âù½Ct&žé\mú¥±ÁYÅÚÁ1­m§9 f™8&ˆ8ë$q¦Iâ®0¶‘Ë›Ù×é˜íò5ËÆoÁŽì£¶_j¿oþ‡’¾oÞ}•}ó#â^þì¿|­+£}¶³ï|ß|\tß|ÉÊøGç<Cûæ%gýJÚ7Kì¾y7Ý‹dì7`¤=óSÙ/zèÞš-t^¤±¾‚œî¯ù¾›©¥îË÷Ãg>#Î)À÷è9¶ÅÙJm±ŸÎ²¤st寔7Î"Gyhb©5¾Q±å-àå)n:‹Js¼*à¦}õÖ­‘ç±Q×Ë$÷ß—Y.á…ðC0¼·³¼ïaŽÒ7²¿þ©ì¯7æë\•,Ñ8'ÁÃ’oå{–R™5§’I´?I ,qäd²Ä‚.ÉÕ`‰|œÿÚ'Ä’ {&|ëBÀ_Ë$eaB¿/˜Îïídé“ÅtÒyñÌJÏ‘ïEøþ4Ñ©¡q4ZcGý¥…ÆÎ2ñ=³ØOço‹s<µ34_èz•Iüœ˜øM©|Éýî½,u,rCW#ŽÎ‚—Ä^&î‹xçзníí­‰Ù;ã*©Œo,ÖF?¡<´†—Õ­ Ì”v"Üül„ŠËã[39Î41ç16ˆ÷ibžhì�ె¯Eh9_ 3ûòê†òÊÚjAÆÚÕËÅÞŸ òe.Úù(âiP¾._²ù]n!Z¯K>Õ+‘/Ý|¬÷2Ám¹ªIâªÇÇ¡ ÚÓ[¬½|~ÜrTç†Ù_•/[»fUùšGVTW6dÕ®[ëËú–nó‘~¥=g‘»6õñZ¾N?ÃîU¡5rDËËÖÇésp€_…Œpðszø~5Æ×–mc–ÜýÌ:8uto.I|x~ã Ûü._ú�;Ý j'½àú9³­Å}ãÄï…y9NMË$9ÝÅì%µÉ®@"­©Ò4Ús§d²Z?Dú8/‡îýóð{§Ã|- ¶È©žÕúüZì>‹>Ov*÷Ef)Û?¿Qíñ^è¡8¹1´ÖŸÓV™îç{Ô‘fûüԞƷþ™úy¢Š6Ó¼dI";hÝEc³èÝq˜`#xH/ç¡Ý ›Jûî¸m�|òµ=_ðœˆ]“"öÛvceoݺÚet²–¯y´zm9 ¬ub#Ù!ÿöÊÏ3CÏéÞ<²_Óüœ~±ë*~žéÄíçk!'ìÍTCû™½ì݇ç z\ºŽlg‚]àTb´'!·—Iü¾‚ÔÈ=³‚+î¹&yA÷—®°çÒœ´.¹ ïA\N£±4 Îcöõ™NšÓuå˜%P1ä tñò¹-"ÓÙùöLÔ~òÇÐ烆ռ|Ÿ VWv<~þ8«¤|#[ÅïçGIDx¬2ÙÕ¸?9ÛeÖ´ ÝSeóä¸_ÒæNl+hNó+bÝðÆiûEi:-r÷ÀvfõUU Ž;‡?ö¼cé ¢‡çE~Ov8yPÖ :…Þ§þSíz“ÚO8¡81æúùÕ�ú+°GࢤòaàëóP"0çÓÙRº}?[·ïżôUÖ÷è¶ÿP¶¸ˆÚͦ“íOSTuQñ�X„gsßv}Ý9B¶ÿO3ý¿y3ºoxÂZ €e*<{áÁ <ëŽáÙ|âYЖ֣Ó>”ü¶4òùпYŸ—û9ìð$|:úÅùtäFj/ÁåÓó{bøÔ?Oó~v^ßë"ÝW¶?a>íß׿Ý<Q>6ŒÈÇ|._Ò 9ëðïÀåÒûý_†w)/Íqk¾þ ¼†ýU]íêjßÚòuø¯²†÷‚qw@ëô¦ó…¸nšÂC1º©>´€Ùç<O¶ˆùLÙ&­ Ét.ï×ÌÄ}¾?ü+' ½LþPØÇ÷Ù¤ð{§¡Ãô4{Œ4ø^HóµôtXww*t‚%Ñœ“~¶÷,êwb/Ö…UÍ{Á/¹Ü8Bùs^àiì´&#ÐAñqçDYNænŠ 7$¦£Ìíy/]‡ `CPû»ãv6ÁœÓΟÃdç†LÌ›û¼6š »+w3ÊúA˜fì÷çgVZÛÝq˜¯l)oÏýîOïq᛺{é~ÝtšCS¾}Ö[šÁri½sè ðû¡øNv™’×ï » µ~Ï^á+s?YÁû÷Æû¦°÷Þh|õG~j µ‰ê§6n•Ÿ1ˆ65.g÷ n'éÕÊçl%å¡ûÔÏÂÓ˜õn7›«,?3½ˆöŸX=í%‰ôt²²$&=Ó `'me…mO î·×-Èkáç7q[Â\n§u’g×d…Mû€îsÀéÿáôxí»#öÜÐw™¥ñuäþ êÞs?×´§¾¥Ò:Á²‡Ã#‡4ïLt¤;Ëäû²À3»÷ø Ü:àoîÖFó×´ÈØÝd8ëÒvSýJîÏ´ž:/YU¾]äèÈ ñ*MU€åÛÌBí%XÂ7 }°U<,é>î’}˜Â’°¿ÛòʉÄK÷9GÙ#h§÷17ñ¨idcÝ'Äï¢ØË’=Ì\ßÜòÖݤ¯g2% òÙÉ9!øô°ÇU[<Ï™:Œ²í(/‰ÖÒþÿ€:BzbQh)³?ö°Jkš6éõì íFº®H=tszsðܬ¯•"üü†ÏáÄAn( }‹iŒŸi£þÎ×zÌÍåñ`ú$µ•hÊûÐ^­3w7³ðsÅ`+æždùy­G`N!'‹®=ZC^lÊçɦT÷;Êži,«µçm¨ŸÐ:fW#ø«‚Î@ýCm­XÒ?Ÿô”°'?=êÚˬ¿…ܯ2âÎÞJ2=›…dú6’ég¯'™Mu»÷h~â#ªý©…Ú»óÔc Séìóê¨X—üݯäå÷4(·›üžÈxåöb?âÑÞ¶ÒŒ‘FZ ¯,_â @×5"Ì`ISÙ‡••w;øóî±fºK :|§¢ñ…O9ýåu°ƒ÷ޱï֚ƈ§vŸÓ4F|Eò‹ú´ñ>§ƒh{éhº=>,[òð|W'p´2`ßóñËÐsÀ=à‘×CïuòûÓʾ›0Ÿ|† ´ÈÒâÒé<´èâgÊÞüOá¹lÏH#áî§ò•AÂw±ö_ûÇïÀqã~²Ýÿ‹Î‘pé8î ì&9 ½F~ lFU¥3L#|ùù*ÒÓtþdp'`’ÕÝâ»éçžCœ ßvйD{~í¬8îçw±WPyeCÖˆ;žÎ(¨c±÷yÓXÇòFwk[ƯÿýŠaãÇŸa×éÊPXø›ÖØEô"x>o7ßa¬97Öãÿœßë†ÿþô€?ÿ·C{Jž€Áí‡Oæ‘Aø"Þq¿k%ÛÆ0øú÷¾H>øB¦ú$ΰ¥y>÷»ûþmÈ3ì&²!WÕú««.3!éî&£F7îP'“ÏR‡ƒ·°ª”¶@vm‡^8€~KkDªXm‰xG¸õªg /bÓËË×V/¯]×@^f¬]½l Á¹ÎbêwÇÔ?7R¿[ÔÏ×É‚aG¯ÕÛP™]u¥6DÖýxÙ×'møñ£˜{™¾…Æ ãã±Â*jo™¯‹at':µ¿ö¶°èÎôJ´§VŒñMC‰Ð&zo`=³Ol­ë*‡{¾vøç¸?@{^˜“ã80hpü»XÚäøoð=‡BP!R_ ®yN‹^ga„ö“ÔÁwþ•hÎëdÆí€c÷fs…$Ÿ ß´Æ’î>8·òvÖ <8'|ž ç“®‹âºŠÝpY»x“ˆ'Þ? ~<û¨=¡iM²¡û™;ç.æi°C^}†¯P“ÉÖ:g>·È׳Q¹‰ãÇ P^MLy³hl/€Þ·‘mÀ×BWL(6Ê´óöìÖ†Œº “·Ä¦£uzÅZGdn3Ö߈åEjoê„s‘Þðm®Î·’“Ý\^^U}åžm¬ÅƒîÝå‘-’Ø__!¿Î“ôGÈý±=ÌôÜçm粿º¼cì.ü“0ÖÄs ë€ÐOÁc°æÀãþ¾NžÓ€Û§Ï»É.“’ù;¿/RJ í ºt?åVÈð‡ÞDû»¸ï<iü.¿æ÷ýáé¤çþð`0ù‹íçû“ĘÏÜà÷Ä‹§t-·ÿœüîá¡=õµŒö :Äø+Úù"ŸrtüˆûÂaøÔš?j›þ:ƒ?sߨÄüð|Ø@[Jäøù| 6¥°W?úþ8Äã~»‡ë$èÎ;´`†˜»¹Ö“#tÝÚGUðÒØ¹«µ ökýx½yšÖÏÐzáúqü;…µý>ˆ®‰†ŒYó[~oá=/M¬p*w�"gŠéJ¦’nò5ªzŸøZyùòÕ¾0í2ßúººøp¥.‹¯˜çëò¬_Ísñûfoa2å#Ùym¹‚¼¦u^¢ cß�•°Ž]åÎ�þg§þµ¼¡†OÒø*j­.çó¥Tod-F†~èUÖ_La®¯·Q¯kÕú†j¿ŽœIê³üYê»ab}±×ž[üÇ—u{Aýs‹ÿ÷Î-‚Î’1Ž–§23éeÆáhy‘5�¤ChœŒt÷Ái¬ç¶z=:Ê´Bî ÒÝuÅÚ¿=´Ðød¥¶›Æ4õ} váüÛ>úNcgyo2é™оN~~Þì25r`~A—9yÂ9€³Åɦläõ´Ð^ô.>o¨m‘Øbäy#rfcЏ‡øJg-*òG›y)¥§´(ãAbâVY¼è¹ô\©ïÝg‰½û)&Áœ•k¿ÔÏ`%yt¯d'UnêyÆÇœó‚8°,rvt|åãWR2÷û+Å=¸ÐUð#G×S>ÒÝ“ä=€¼Æš¼Ól‘~¦ÞiýŒ?:S%i답ÖßégóÑ÷ú÷Cú™{§Ä=à¿ü §“îïéíȇ¿ú» íHiG=ÚqjüÙ-Úæ °¸PÆ«Æ1ó¿g'ƒvóq¸.~®» ²’€Júuì€^^!qèÏ.ýŒÒ ÑæC6Sz=îûÑ8MŸ7üå=ØèLÈo—íºžÆcàˈŒ+tÑyîìÑ`F¹ù:Þêu¼¥ ¼½¦×÷ã˜ú’ô¸çbÚ5[Û—|yÞ±Mb¿$ÝqtÉ/¾¼ó}§žgCL\§Þ¶Y¢m£gõ¶é}ëÐõ1m³Š¸ûbòŸ£¾ ^K'{ˆž…=:êmež@ÏG_/xiÔ œ›iìƒò#MB±vŸ)-ΙæsòÄ=hĉqÔ~ž8î‹È¨¸x]FYê¹Î«¬,¯¯^»nÍj_]mÆòG³£ö±n—öÒ]�ê#¬Ym¢ý•ø•iÜs?‰OäC<):K„m,ÌâÔ+•Y¬Ë™bys™[Ûz‰—µ³‡—ib›Œ3Š“ÝVš¡Õf0©4#~þ´VŽÒLƒ[!æAÑ«§òñ­B:—7›Á§J¥3hžQ쿤<ÁÛuÝOÏ+èN1ó+ô¬Em€à]Ègb›Å8‹ö­ÝÐa¬¸ŒXJùwýÃÝ‹ÊËï]X^WûÈŠÕµUµëê} •5å¾ÔkÂ|‡SÓ"ð¡­üYo¿.«Ö-±°y©Ý4f߆ß/k'‡µó Áº;ÖÂkÃÚ+î»n ¼2›Ù"e.¼œWŒ²þå‹–U˜Êmûž)ÚìÛ§’N¦5ê!V!· 3ᣟÞßg#áÌŽxšË(EØ#¯æwlöñ3‰+Ä:øˆ×év§‹ÛÿXú`—þKŽÀ’TáÖëZÊâ×U7¬XUß¶9Úö³ä¶÷YJ»™9Ûg²ôölV{%›ÝbÙíÛØÜöýÌÕþ>Ëï03wÇLVر€y:*Ù¢بÛXIÇ~¶¸ã}¶t’z¼l*Yç{´zyýÚ£ûYÍÑ÷Yý13ó›ÉÔc X˱J>b[Žmc=Çö³ÇÞg;›Ù®ã3Yïñ¬ïx%<bCÇ·±áãûÙéãï³³“Ô³(RϲúµÀ÷Ž)á{Ít¿üÿÍä¸^WøSÞ¯XçW·Á¦': @ uŸ§Y…ð³aâÉ;̯ M…¼-è”C3éd:ËXšß ukV/Gç›×¦Ä7 M´ ‚6M MhÓÚ46M MhÓÚ46!Ð&Ú„@›hmB M´ ]6k«×5€6A›ƒ M?hÓÚôƒ6ý M?hÓÚôƒ6ý Í�h3�Ú €6 Í�h3�Ú €6W£ Õ#h³sŠ}æ)¦Û5Åt»§˜®wŠéL1]ßÓšbºÁ)ñ¶>þ$?7“¯%’–és7öþãR¤CB/±ÃtGÉD²àç%_ÍÇå~D“ä Î×í.–}Í<2Ï“By\{X!·Qša¾Ã*‚&÷UóVS^6Ìë£ûÇy ¯]{M¯/Œz¶ó:«Ž|ÍWÍ«ð¼õ|¼@1òU\-Ÿ~ÖÄ.#_0þVz—8?~q4»*.bÒ]~®t<•q­XX¹}~Û�Õq.ð_û¯VùÑ‚õ /"{û˜½Æ·ºŠÖ›YR£ç†\Ð÷ ¿süUúüXd­<Êï£R›YI8ÀZˆWѶª«â éˆfHÏÇé< :¯l*4twÇÑ™"ºOu6¨0 ­ýÂèìM—Ý^ÂØ·á–*iÒjW*kÉ!›)mÜÃ,›S˜Û5ƒ-.yÙžÛ–Ï,.™%…SÈÆ¯«YX°‘yø§Ý¦a·ã.¦ÃçÞ÷Ó|góº’ee>Ã]&ðn6ÄÏü\±8‹Î­ŒÞ!eZʘPšÑK!B’ˆ›J0ŸBèÅólN/ñ;[%¼!VíbŽçGØ ôµãönÿñð¦Þ ™´ÁXÇÅêò–h£´– |µ”Êkþ¾·ÿ„Ê÷°G¸ÿÄç.Óüò²é~Š'_ÐÖ´ëj4¢ºhL¶õvÊG¼ªîEB^õEæ[®Ú¿E}¢®k÷Ñ‚nsU4ýø3Á®^öÕû(oû[Ÿú©ÍÅÚ……¢.Kx"©'‘ÅëóÉê«Â—~÷Àg„;ôm1g‹~Âûöµö´Ìƒ2Ìy¯0t#K’_eY¼~Zçð Ëò°*î‡ýHžÌ[¤¼à¹uî]sÏÂ'®¡¼Æ7ʯn!ô›7Ææ¥< Í :ƒ‡X�Ó)ðA2­›@_Jâc¶4Åý#ób>‰«‘¥óyú'iÜ¡ª–Êû"4rw›dª/“Îóë2Y"}lïØP¤í;DkhmPÞ€¡“ÚÊ×-¼AéË*Ä]h³ÙÝ­ óaŒ­”g¿ ² óH¾Dq0» ´ožÒD¿ßñÜ„ï= Þ 7°¤^ò13mþ6ænÛÀÏÖ–© à®Êá4e}8¡¬’MtçªúKúBýIbnÊÛ¡ÓÛÃ܉ò¥p8zy?PÇ]ïŒO{é,üõÃtÖàÎVÿUò¸*Ùábí¸¼áY÷ÞKÞ/ÁŸI´ö•Öcp4 XR;J}_Šßo1³ÉÆÇ5íÒiý¾=ºw¯a„â¦h®Güšæâ™Î\ÍGHŸ²lxü Êô!þø7B6H¯“ Ý—RÆË†_%\VW"›ôÑ% ºžòI«óøÝA£›cèx§ £Ýtîu¡Ï/6sž}žp?» ?Î×àì¤>̿Ѽîóü\Z‹»KryXΫ<Ý/ð;Ýx(_ÒË©Ž)§&Z~Î'F?ïù?IçÖË`zi1ßÒ£yòrbâ“bâ_Æ_<S§Âë|^Ô‹:E¿lƸ+á2è§—wqÙUÍ×IF9‚7üLO³Yå÷3iÃdz'Çñ§'p<–ïa®õ¼}/ðü%ÊjìLLa_­ôÕÕ=BÇ$×®~tÍJcÌË8ÙØ/±¥hžyw¦?x'»ªâef‰ØSÆúë·™—l)éɨ-yuÛ½’öÏ¥ýy,ÐÃn+Û£¹»ÙZ[ŸÈ¤yN1ÙäË`¦¶»˜'œÊ!xÝMÒnÿœy¸Ÿ t³ýZ²œÆ7ÜÝÒ0xß«¯I$»Ä¥ë‚M×è74>m×ûØ¢kêöNЉëï¿è¼è"Ö‡ÒYÀ§øzË…øÎÇVç„´1´g _¹—Yóz8Oð1q½NïUëìâ:}v îWµãïû¦bÃÓšhº‹"†÷²tÞk(ÖÎþJ·×i­¯D|åÓ]lø-ÝÜè üìº-ôsqN6ž {Ø\ØÑÞ²=G‰¶+÷ÀgÙ;6kÚ¦L…¶Ýc³Ô&vX}mk–ò‹µ3G¿(¬QŸ#r½•]é/Æ0b긵êú™œúÄNÃ÷%ÿâšü¥ÐxÜÙäxRhnf¸V/Ôý¿ Z{ ÛÁ‹h=%í¢ýÄtΖbY4÷qÍzP•PÏ’-Õ‹z$½È—÷>ÓŸ=z°Þ;©Ç-Åóð½ÑüÌÙ÷ê¸ûw¯pßIŸ–˜3`¯[¶¶ººÜ@*Çe{šv@¹£Øo¬í—?Ï7ÎñéºÄy°±ã m'µÝí’Ö;ñ.â ¾ž3X)èÁyªØ\§N‹ÁÓ à)füY~túwSÜ ïOÔoÅØÀkW•g~>~ZS¬½ËÏŠÌsÁüG|º‡ýŸã ±²›í¹a™ñyªt»ªoA~"Ò—DÓ_½Oð;�e¶˜hÑ[¬½ó Å#.¿l¦=ï'õw7Ùó~Ðô_êqKßáûÚO@îÊè"ŽŸ3¶\è%Ó\Ä>ŸÏ*:~¾‡/†ÀÜ]–C¹*KÊ­÷8çºù8羟nûü6맺ÌÓä ê·’ ¼¼Ôø.'þê'ôÞW~uð'ÁQ½Vó”G^óz–\¿•öã{äÄdS_ü[è¼mü†ñû5ü¶à{¾’±3‹l<»ÅyrÒi¥¶'«ÇÆíœëÈÿ"Ÿƒï¡xžö>Hg"ô&Ÿí)û®=7¸ÐôÆÕxíôi}³Ø7’¥Ët´·þõ¬‚nÍJå{ ›€³kÑ›ãaÝëY”x”<ìcqF{YC$µ*s“NÉû9žß4Í-Ö>ø)ç}L v@)ç;’§/˜Ñ8 ùÐæ¸^·Å9$?Зø|ô§&ôIZ“ÿe×ãðzX¶˜Öñ#xà¶™À#Ds#ÎîÀ«œÖK›v³|â[öKQÖëY“Ñxqª&ÓRNWàè*'nÍ"Ï4.|= iRæ´q»÷Мq®¸*“zŒË¿v<ã½ï¿Ñßù¸bã©7ÑîÙŠNÛëÁût7ã,<ÏÑþ\âÁähÚ[ñqÁçÏùêžøù¶üw–Çøf³ >Ï=ÁÏê–ho#­ckØ‹8þˆÞ¤çt:îð°›o5øŒÖ ¨)†_,êý"_ïsõ~1;o‰¶7óy– û:›Ê'¾¶@FT¡~7¯6ŠKf騗Ïó¢®]F½–òz¤Î½¬x²6øgî_»^º8eÀVø®¤û ;ˆ~°Ÿ; ¸Ñ7Žê0ó;¤®ÀÃÅÚ©Ï„]q)ŸÒä½)ÑýÖÙQžá6E÷O.¹ýŽ~‹Ó+ß ßÑ[õwZgP§¾ÄëóÜ̵Q:ízÙ{ýu¢­££z=}xî ¼B²nìÉ0ºÓ~Ÿµl ù}ƒÖEId(œE8¡ic»ñÛG¿úó=Æû•ÒlŠyöOøVÅeh:m&:Ñ’1qçô¸Â˜¸]z\vLÜ —w@³ÆÄq—ÎÅÄÖã†câ^ÓãÅÄõèq»"zÿú]ï'†ß±¶š¤oh~Ýïˆ9Û9V¯“ eÌ!¸ºÌÒÝ×ò)ˆ/CMì×¹Šél¸Sœ·3v~U¹Ë÷úÉ‘uLéײo¨ºW7jçDÖâõ±c'žc|®Ú†ÔØóYBÿ œ놻…¢û'ÙSñMøÚ’ѵW8‡Ô§š"m«c)“¶bÖ¬«NÐEîû‘¯@›áˆ»‡Ú)m¾ª}T1sƒû%V3n¾åvjâ¸êdãÀ±óëáu_îßxiõ‹Ù¦ì?÷ᆵë+Ö¯­.ÙPoà"ö\¢ŸøJ6©qf̓ÆÓž�«:Xõq™E„cÝ Ê9s­¼°A¡KÌg9¾h’ÀñY”s`J8õœã6¡ð#§–ϲŸÏe~v“C·•‡¯ÚÏžû³i­ ÐAf¾p?¯Ÿ¯.öÊZ;âkÅs Ê_òѶ‚ö~}¡q;à=#S§@ƒÑ©ÑÀT7žû ÐàTÙëUÇ#tØ;Ö¢ÓÁBt ûÇãL²|yœIÖ©áLrMÀYÒ_�g§§Ž³K%:άg— 'àÌþp¦NÀ™c*8s¿$í¢öŸU°wôtǃb~öOëRòTÛMwOÈ›2u˜MoL€Ù9%>yI³Oà“ô¿�Ÿœ+ÛcÏŸ\œ­ó‰CðÉÅ” íŸõhÿ™©·ÿBÞþ$Ñþ K'ÐmöÔèviñ¸³ÿpŸ-Û“ìšÜçt¸íîóUx}.õ»kµCðúùEFä½l>ùÊr‘˜€·ü¿�ÞF¦Ž·‘|oÉo#³éâ=$ŸÀÿÛ+:/Q¾ÛëîõË–U¯-X»Ö·!•±èÚŽŸ‰ù:¯”l0šƒˆØaWƒ熎››ð|ѹ‰,3d\{Ô 2ô5;לo€lRHÿ…æ $šãò¨/°’©¬G¡¶Ðù%”‡¯Aš9Õµ(Œ·Îûåù‘Ü4_KóTݱÐQÀ\ÁVv.ðêÇ~âZ#)³e.7?â™ê\…ûÿgî}�£¬®Dñ;“!$!$“dòG eTlƒÄ„!u4JÔ¨ØdH&$’dÆḬ̀Ŗ¶Aq‹0ZìÒ•í£]|‹[ÔØRnñ-¾¥ûèkºKwé+ûwi—ÖyçÜ{¿ï;3óÍ¿Èö÷‹~|÷;sï¹çžsî¹ÿÏý~òëŠÿU¥ CŸ«zíX`“¡>l-¹·1xþ®×Ëég°™¯qâ}j|áÏåš[ÐFpÈuœÿß¿–ëõä÷ü~{:´Ê:ÐHUÖŒ`ŒÜëòÀУMìsPÆÈx~Gú ¶¥·òWöÛ²"uìGÏÊn245Ú×®ŒnøÞcrV,wQi©c>9®Ñó‘qLÄ:óÈK̦7f“{gb×7±7®Ú9ÜÇébÚœ›ºmŒ£K ®U¨~Š›Å}5¯Ü~—v6@õA‹¾Æä=ltì2ç¼ÚdhVÇǯñzsîË‚¶,‘=~`g·Ûw²Ýa{c¯O<ÁýMƒ?þ•šðÓïG O«²'q¨ëç>¼K/æšÄ#?ûnè¸<>ÿ±¾%rq!Θù£n½uñ)6f{•„þw¬ÃTY¦zýÎm«`pßÓ¿Ym[Ð?.ä;|‹¡EƒI2}ÞÈ}„}Ä:G†XëÈÛ¬}$Ⱥ¡¯~ºö‡ŸàÞìYÂÇÚßñ¹JÀ±¿¹…v–ð%¼a¢>žåõÛ€wbÓÝÁïg÷¸a™Àž¶B¹ÎA¹.6'ù¾%¤1®ž+/ÁüÜGy=¦ä…yKØ#’ïºóBÃGàmÖ‰ûqcà#¼¿4eA *G¨¿íº ´k´»#iþäu)‹&mþIõWÜÍÒ ­_ÕíðÌ™ŒSŸºÿ«ê“lG§SŸZ:Ò }øá>bÇN*6Iñ}<´ý¬\ß—è^ôí�õ»ý'Ò–ûwü¼tÚõ,ÆyNŒ#Ööß¹.Û¶Ñdò «“Ð?~GÊ< Ê\»ûóË‚ú÷@O§3¼ƘgT|»…¶=)hKBm#ôkÑOM¢ý_,ÿ&v‰ô›ãÎÛÉq¼mèñŸýýЛÿœ½£|ßÄ®­ÆDó´£ï„ôSi[ÂÒš!ïdÒ[ÂÒAú´ÄÓO K?/¹ü§ö‡¥¯J2ÿÔa¾.Ô.|Ù f˜×üÝÇBŸ§ÊT}VÛãV�6¬¾gÀÙ¡ŒZ:;œOBm ´)¼mÁvÚ™èmËûÏë·-ïG¶-““|Ûò»ÙñÛ–÷ó#Û–÷ Óh[ê±- ‘ ´5É·-¿}0vÛâ i[?Å Ü®üÙ"qì‹ÿKÂÞ¹\?†2þt/‚¸|íg”Ù™w¡}ôo–Ãï-ð>a½“§ïSl í Ðõ6®rûÇï^¾Ó(úš:÷¡œÛq¥k¸€ïo#ì*ßǧàAœ1îVuhw«² –ÞÕÕÓæp ÌWõPÚVÅ׸"o®‹CŒï¯çëH/1?èb�ÏqÂøÕ�Jªi Ó€¸CÔpäáo þ`Ë ÒãYkEÖ¢/ni£¹ÿ?vÓrß´Øçü6ØÊ ó+x@_bŸYÁ´ï‚m^ièåg06ÜPïÝ@[H¿1�4þ\ÇÎ˼ñ,‡–gì6ê¦ûWŠUoÞû0²Þ¼§ì›jÇý}Ñt$Ô¼÷œ¾ xoG¤ xwvLðÖT™¬O>­>ý{º¬OUèôñ§ª\sqüFdbŒ¹ÉyR§&ðŽúH=5îa³¡?°Î±Õ!»¨«%3¤oòQ55âçû¼Îî4´ó}]‡‰³T·¼»^c{võðùøX.>Žø Äém9ÒŽûn9ýa¾9±-Á6à3;ñˆÎ8ìÄÿŸÆaê˜YÎ+è­!4¯\žÞxíßöI½skí«:öžd¹Úxmµôÿ§¶­üÎQf›Ÿç¯?mÚŸp›àS䎶l…?¼_øŠîúqœµtuÍØÏûµ¨Ç„öký_¾u»B›ôM7.t–Ùùž†qN_@Òµ;ºp¯Ò¦øÑñwæTÚ¼*mÍ®ž~/ŸNmân¨gôŒᣤUi[@>K~¢^º÷ø7+|5œú÷&A³JbmÚË–åX¥í‘gî'^¾•¥òùÝvv»-=8ÕÂLè×(£î ·õx^ý¢z §!okX>ídNÌÎ ñÞ‡G½ÛÝòlJ½|�#~uî§ëþ¸Ÿ.bžl“a„Ì•µ‡·GQÆżn*¾x¢ÌS©uýU¾Ž°?QüÐW?–ö�”å¡$Òw†¥M"mqXÚ1È{eâé§.…¥?œDÚ£ai@Þ«’Hß–þhiËÂÒƒ¼7$žþú•°ôÇ“H{<,í È{mé½aéO&‘Ö–vò^—xúÉkaéO%‘ödXÚÓ÷š$ÒûÃÒŸI"­=,íYÈ»>‰ô,,ý¹ÄÓ~r*lñ<¤»6Kö à~£’þB¼´6ü«²Ž`Ðßq“Ùïg¶ÚQæöwyžÝÀƒÛÂÂÊ0‘HDÚ?„—ÿbi÷„¥õ'‘6|m×HZ…oí~v{cðij»§îGKmgéj{$ÆrQö›)íucð÷ðq#c& Íƒû«à};¶éÊ~ÆXmoX9v'RÀmˆh_’Ð?HŸѾ$ÁGHo”éLG†>WÚê‹ÓÑ=HŸ-Ó‡—ÿR‚éódúðò_N0½ÒÖ„—?!ý‡ô9Òf_´ñþøum?kŠ:ßÕËòøX®þ­ÎO«¿­«×±™¬OB~WÈ]¦dž>ö¾P½µK¾l Ï-ôb7;pgæýèüp>éφÔý³› WWyŸôÓÓ±‘emèñ:Õ]¢t¾ç• ¿kО çö“™—HŽv6øq—˜¶¨öËÁÈÔùÆ×Äš¼ÈßsÏ‚ê³j×­¾8y·2 ä½fÍZÌŸõ·ÛÙ9?B!ãôÌåa4qˆ#àÖdÆ:Ý|ï®à‡:ËÇa_‚q˜þGñuZ<‡‹ã>žÙdŒØ³¨;žQ}áùJÄø,þøq„åâZ˜ׄÍ!s^Æ¢ýô-O3n\žªôN=8Mzwóz¨j¿F°2.÷¿¶ÈçÿE%§ÇKH3Èr»œ·á¾åFæ³>ß8Äp'˰£ëaß°Û O†êj`~]Áù&<ë&ç(Ïà^œÛj ~P‡þHáw<£SFçéì‡Ø¸oê òaøNÖ°qFûóùFç±±y‘óf¿ø@Æ#¸ ó†Û˜Øóp;”iÕ°F ·?qì0–çUYDÿ…Î ¾¾]nðuGäÜà÷øzF´¹Aû[Aô{~ é¾ÅX4ü¨ ù²ë'ìê.(Ó.Οã玟•sjèçw ¿Šüáz÷$”í‹ZÙ�ïåйÑãïDÎÿ wœì±±Ê³ÆWáw§Â'<;¤õ—Ôý3nfÖæ묌Ûù^)¹Î¨Ü)¦úÃgvGvýH÷{"{½Ž)gÄq)‘ý^|þ÷`û¼vð}^6Þ¶'¸Ï ÷'sŽ<‰}YnîÓ Î3èÑ<>—9.ÎÀ‹úõ ?õŠóI8ÄÏá£íå4†µ“Ñl¯ð;¼@9[/ÏÊÄöÑó?wv…è×ZÔ/”™­ÃXÔü¾!™ò©:¨®s723ñéqôö<îl᮸µ3$\÷®×Šþ;ØHÅž£.†Ük¦Ìuó³Ë˜àí7”ì¦}dh† ÏØã:×üÒŒåþS•¾ýXoϰï›?£)T' }<ÔL=wǵl±VsŸ7‹1 õ?Õ‚÷õ´6Ûf±Ï"|ØÁX­Ú é÷Ó)ñѯ1ÂF>2]Fÿ0^v3³ï»ÓtÉ~Ð0Âï ÄûÉèúG3l ÿßãòækµþUúð,aLù‘é´EÅèÄ_ôAéP×¥èß©ö –64zšÕ>ÄÏ?Z‡GÜï î!ÓÖ.ŽÇÔ­ü ÂiL‹tà-ו$¼žüV°X'ýX¢éövä ¦ç~«6KGÈàSÄqÄ;ð@ò¨ì�åðyƒóhù3ìîT–R·Ó8‰k‘µ±Pø¾fÛ›){jß4ll ¾Æ×öž}›-ö#ÞÆr¿ öƒ¬yÊxŒÏ§a½®}“ðÜÒx‹™jÁ2U‘‘íœ}(p ï@GŸ²}-‚vf9ÆðµÔÿ-×?gXÓ¸³Ê_…|xæç`\s^G6Ç— kQdr±¹DÌ}GÑÛåÇž¡§A‡ž#Ó¤ÇN艻¯\ÒSJÏä zN$AO‘¬× ²^�N½ú0žD}°“26‘2îO°ŒÍŠÿ ­œŸœUÎ9cû34óG>Ûžž½ƒ÷kvêùº'ÐkÛâm ~ô&ÂEýž¼ÚßK¹ôÙÝï‚ÝÙ™bÚõCc ÚsÄ�›Œç2oƒ Zƒ…֮ܿCâL¤í ëcázØÕs˲‹hãÑgrÈó¡I_h?êµí‘ý¨×œ¢5Ù¤ÃûÓ Û2<Ç™þl²Ë$²k%²MPvÏ×RýüCÑ®†„½²˜í-cgcðÛï‰þÈëq·ímôUòwK>•ýÐèn'tHîγ!tÿ¾]‡»—Œ)"ÓŸOBG2}cðcî·³O`ŸlØÚSùH·ß÷A)ÆÌÚS?(µºv$¼}àçüß4­}Ó8Ñ|õÚF@9/ ÎsÝWuÞò[1F2vïQuøz&–Æz×xYV{¡m®^·(åAºx™~ôDû~ŸÒÊõoOóº+íþHˆ]ûÏ èÏ_°™øXžÆàî#c—ÜûŎe B¼7S |)ǃ‡ -ßdKèØëÏ®ë½þìW‘c¯Ãƒ±Ç^“iÜ'X„œî$ìï˜Æ¿ÿŽ/P¶Ânýî ¶Ã\LJ˜Æ¿ö– sM ³Vß·Ù\sô ×}Å^h­öÿÖfÿ¥†æá;M—ýc¤÷*,þ_×Xüß¹ÝbÛgûôCúßñò<Áuî$â/cÌ€}ÖÆàï6*e#{mBÖâ#ëoK¼8g¡ÕÁÎLuò½EG„nÿãzä»»ƒ¶èk>íµ¥ßja©¸N\}šýONüÀ÷ä?ýÀ'ëKçI›àË}]À“nàCô‡ÓÊš§—ï» ¾�þÑXµ ï�Xg­ÆrïhjnÞá´6£ß+àÕ¥kîµìèzÈâ¿üïù ôÉwëJGe¦‘Yȯß6 Œ†‡H?e êëoË ¾øôïá1.÷©j¸ [ñq|Ë0Õ0OùŽö�;ÎÇøm¿hƒ —-LaJòc²mx½ÿí%ÅVâ=ךœŒâÊïúI7NâýŠnpZýRb8R€—S Qp\IÞÓfŠ‚ãr‚82¡O7ÇÕq Ï×ÄãØë¶(8.&ˆý-^‚#æÙ~‚ƒûwTÇÈÊ!Y“,Mu¥¦ÍÊ9N¨óþ(ë1í…6·ùë¢íOx›åovzåœÏVf5CæÉþQ9ä&>ÿ ÍGØìØóÇ—ÞKn>VÝ3²Õyou’²7ìH ç_ç‡ÎÞ`é½w5ß3Êe�éø]š=ÒgZì}3V–éqz›TŒ¬UnðÍ­œ1‹²† ¼œ”ë6#¼í‹3§-Û¾u¯Z¢{íÅYBc{²F¾—Ï[È9 OÌõ5  }Ê$BÛ³¯r9%VŽÐ³ÈcI”¡ç´´ÆøëÜÂ/ÇY,ØÉayÇõã­ù à~hÚ#IÐÝÊǹr|›ï‘nû[Á¦cœîë|ÜãõÆ`pü€€ £çDô˜ùX—s¯_ Ã3ž8O ᲌˜ÿL"íÙ$ÒÎ K{>áñHä>DÇ2i8¾æþ˜¹ŸÎ”sÁçËùÜÞÝ}Sqw¶‹ûö…ö£ÇúDW›âåƒ::2Šþ‚?¹‚c6’¶=^ڳ˥o^£’t­ñÒéÊÙÕÆàEîÿ—ï3Vï&Í|S”é“V7ôOù’¨WŸØUûžªœg˜ac³>Ã+®ûû¤MÂÆ“=ýãʹÿ?×ú”óŠü¾ lC¿ŒëÒÆÝr¯f„ÿ½¶)�éÑ·;Ò�¼xÆà cµ­¡ságY^x_¼ôÂë‘ã¥o®Ž5^Ò?Ëò£:1Ö¤4[CçK^X9_òÂ’X{~!vÔK<ÏÒì}_æ¡x+è Ís h îÕÚjõlÊ–ëuiKöʪyè"Ócò|F7ÈÒ ²õâ¹Þ»‡…tÚÆ‘`è9l Û-eè…²t‚Øo¿¦ø?oN^ŽÏÏЗã”ãs;”c·&Çw¶Ç—ãáH9èSÚÛ(rtK9zƒ[f| 9޲4EŽó™mwðïÕý¯-òµì†qån¶–ÊƘóäÝ)!ûéåÙÞ–Ý8Ö³†û,†1ÙÙÿªGÜåjè„g¯ü>¯ýf\�O{|Ʊ¿þ¯¤_ä‘b <…²?+ÏL-þ’\ë4ƆW±éVæÐÇ+Î)ùßém`†÷ÄÝ÷ÄØO˜âñ:ø1_?u~ÅT_X`KÏòz·Éx�m(ÖG¨§6¨ƒËOãÜmĜ՟!ó\i8}ÔgÒÊF ý(Çý‘æï�êéòXöúÇ%í8GïúCû€1çήûdÚq™öpDÚß@9£§·ÞƒôGByǼG8ïX â’ñøß;°Òx´åÝŸ=‰ñ»ße%Mœõæˆz#îÉwÐB?áä‡gINމ÷ñ=RÎ>¢Öaƒê×û0÷ÐæÚô¨³Ã«îá¢c”ÉZÕ¯ŒâS†Ÿü–j{.ÿv»ÒÖãbKgꟈ>À'|¾_972>vf"¡vX-{BëЊb'Yž`P¬”V¾e·±4\çm Þ®œ­"~8÷ßü‰= úÇý ˜ð^Y~>pßÜ?úâÇ9 ;Þa<ò‘éb hº�òäóE#AÓè¾;Mü.Hô¹ø63ñý73†wÙfhëÆüž#³#Nô!ÓtrÿÃm¦‹JÐ SŒtØvÔC:qFeÈtqx3áZõ}·›kð~[IçPÓÙÑ¿"Þ‹Šk™@ÿÅÞ2× ëÜ•R¹ŸÈßU;^)}y!K\Yu‡Œ-‹,¿î »vûx”»²/0–ös&ôžfzá±0–z7<�ŸqhWf@Ïtž^‘S‰x§àÓ 6ô<èOµuøN([´ ÿ–‰Ò¡µWJƒg®c¹q/¶ñu‡2ކL° â~Vÿî v(<®Ùã|*¿ws¢tÇ–+¥|ÝêÊP Ì^7šq�y1<Äš ì þ×®”ú q�gcð·Oão¸Þ‹wÜfâ}I—ìu§N!m€׈sêeâšn>¼Ñ· ÞC×ÌÒ÷bü=éâŽd>ŽºE¬›¤ :o›.@](ãû>bMS“¡ÈwÏ,åne.u]Á~hÖùd×Óì‡ÒÎa™ ,v!×Ô+€ó„¾\SÏ16«g€Ü2ÎX"OÆaùöÁÓ*ßð¤Ey¢Žaý÷Æ^Óçóõ|ì ¼Úõ´â3h¶½ÄFÈÚFscð†­mØåÚÆˆ¶¶ñÕçB×6¦N£Ì•<ù~89Ò5Ôòçý«Ë|Ï Þ}"y&éš<…íM#ýpûÇõé –ú”¶ô–ÿ>‹·¥ s ¨sû=-òÄßìgžÁ¾¡BËü–_ ´?#�; Ò+ô•ùÿ©Ö‡:ÍûÆ_ÕÚ°½'£Ž‡±>aÿhzëÔy2¼ŒŸL@žóPÿpL„ù·¿0ïEP®uç m8Ö¼ËEÔ›«·)| øpmüL|žÿ´NŸçŸ€œ'÷ ÂÙL‹e¿l"ðwW³û3>æt ƒ}^tí¼)Eþt€C›<´y%æ•U~GiƒG¶£·KCéøúË6M“KW¡­öM—9 úe.™&®Ì0\—A¿'¦‰Ë† ÆSû§‰+- ×%¾î1=\, ê÷ddþkú¢©Ûi{v·ÖÇ¥<ýšþ¢^`:>öSõãÚh°=iPõê ¤kÀ:ƒyáÝ ßÓX÷Îòv|—Œƒ¿ øÿâëÈ‘úw ú ¿ß­ƒ¿)¿V¦¿ûv‡D¼È:÷¿žÖ¯sÿyò*¢eæ8VÇy;Š}دŠ:/ìÈ;å‘vä?Á\;¿ÞŸ}# ­~?Oó6ðËôÈøÕÇñB=¦õ¿ aäÛ¿ü;ôÓ_]-| Mµê­ëÉþÒrqW$´³˜Iö}Zñ޼_fÇBV ö9ú<¸®qê¾…�[ÔPº£ç!ËŽµÏsT<èï°Óò»)Îs4ò1xE;ÀÔöÚñfÔ9„Éo\—­çç§Û õÛîÿ +õ¿ÆJµ=À½õ)l[ãÎÓ+xa/öûðÎQ®—J>oÏó6â¢~)í Þk´]Å}Ýq÷ƒ¨²ã÷íŒFîwýäûÝŽñû=lwšÆ‰Þ­*ù„k;Aß21. .'ùòƒ‘ó$_^-âþgZè¼Î—ïП×ùr~ä¼Î®Wy}ŒºŸáw{ƒ¿›m7£öAÓã«ÐþýöŒÒ'Õëâ}hØ'µþÖmK{TiÿË}yn‡nÇô¢Í¼vÌk×é®ÿÁ2w}™5a?hןá8á÷•±èÇj¢þ¿+dù�ö•†ŽðñÚi¯á>~«ñ}É(/*s9æ—ûØgY*ö)@ÖYuoMŽ~!Ηy›Fƒ)Ç­]ÁØéNèïâo\>o‹;éžä¿·s¾AœÀ—¡~ÿËTð4?¾‡Ó|§it„ëÀdÇ…<E|.ìˆ;>›àÌäý¼×Æv@½ˆó?ëd?etd3Ÿ¾ˆøx=»Sàåøþ\Ÿ£áüä;&âjeyc˜ÄÙYÖ·~F~÷ä}wbŒf{û§±ò½B²þéíŠ?ßñ—8_e™Ã[ĸRÙ×:žäy¶Ð{yÿŒ¹¾}^èÚ(зzÞÇÝŒ÷e«cqõ±ìn–Õçôv»:‰[Of{5ø–ºöý¥[}-¯¦þ‰²¾¼è)VNçõÁFY›ŒUÑçÝgpŒìO/‹¢->ËÄž¯° |Ëð%x®(ßшsM Xèo†´xé?íy˜¡ìeÀ[¼ñ.—IxãXè*¼ñþ’Ëð.ÆûZà ýâ© ð{>uÞgá}Þçà}JΛåüËùDê²z.N®Õ«¾P¸ï“Q튺&o8Í2ñ¬¤æ<B™æûfßjIOý”-´«EŠ|¡ÿõ±h"ÏÿáÝz~6Ó»ÃÊÊVX FÇB¶Ë?«aYÝ+Á3<>”?ú9u¯€—Ÿät xé™Aÿ¯ ];UéÃýL;RWù?QHÛ2~Þæoë¸Ï¢—˜ ç±£ç¥ìñ` ì6<ˆÓÖáêkó ö·=Úïh“QòÚIÅ%Iâ4üÏ7Cü&Åοž-ˆÿÝræÊF©ošß+¾¯ÀÎi›)öd ˜7Ò¸£aƒi�Ú¬îÛÆ8a{5e²1xäõh²ÔhMShu³ÏǦ•6Õá×F}~½ÿ5äÎCß~Õ»7õ]&ùwFÉ6æ?žhþ·ÆÎM¯ËáÕ/±~þg¾–”¾Ü;ÿµý^=]mŽ’wÍûzºûxh}¹%vþM®P¿µ„†³ú4üD”?~Ùíñò¾ßÕÓ’÷áE>ÿ+}¸Ö¼c&ÔƒâFߎµ|´5¿²V,ïAdqêB3+‰F‹¸?J|hŽ€TšNU†å¿÷‘øuPi£Y»=Z¾x_ex®Œ¡/zàƒØßôø\ŸêkÏyAŸÏ«“9uáþ“1¹Çé4ŒÍ^Ç3Q8ÿŒ~æ±Ý¯{†Ù–Ø lgÚwã†'ÞQϺíd­ð»Y¬-ôòuì¡?÷™ÝiârÆ9@¾gAñA.÷¹säQÆßǼ LJ?Ó‰>mo˜ MÝÆ×Tñ>Ûó¦áçð[ò>è ÷.xã8‘øbìÌTÛA3›§.·³?tïîU| 9~"×WžX¢îóz ÇiO¬Wæ7Cè}éÝÊï¬ õËø-©‡¦¨>U}HQõaûLT=ìÅK²Ô» .B=ùÓµ#” õg¬ž5y(:�t½.ý –áYC1Ûþ{"o>nA7åã8=Y'2Vl >~St>)ã—ø•73Õµ¥KÑíEWOç†í}›\½~P7©~PáÜ”²·ÛY¹fêÛ*±ïNˆÜsàÛ§?6õm›гøIí9xYÙߣoväXzÛïcí9Æý/1uï,Œõýèß/ðQØ~©`äZq?¨„æ±{¤ŒˆTå¾=Cwôþ÷_ÑÖ¶®­råÚµóÛ‰í îùäýšÏ7úè¾ÏÆà+çd {J¬8‘<VA7N¶>­¯[‹Ô!y=8t=¾l©ƒOCl7F­ý´zPCeäý•¾Œ¼ïGÊè±G’—ÑÁãËÈ눔‘wý4dTucd4úê§•Ñš*#O¾Œ< #eäz#y½øz| ¼)£ïLCF%7FF/>­ŒÖÞP=vP_F=)£þüäeôBN|=vS¤Œ›1 •Ý=ÿȧ•Ѻ*#×ïõeäúe¤ŒzK^FúâËÈ5)#—c2*¾12úæŸVFˆŒðŽªeí¢=¬ùÒç{ÝPVûÚ˜}™=Dzö ±úeíÁ Àlè[d·dØ�ðåvô„gú Îâ¯0ôñ~¾mð½|ñW‚Aû!vdñWƒAàoCc£JÜò-̰ca?æƒøÃpx)ŽÅ³8žvû l#®%)qýé­ß…ï=8‡7h¾òäoH+Ò—H~öÑàþá¤|÷é¬ÿô­–º9?ÕÏP_޾ž÷þ>RÏ{žŽ©çoMù#})=÷”Ô™*MgÔý¶×bÍ u:}wwÖ¬œ#ǽ7G)ozdy»ŸO¾^ï.~½Þr0Rv[žžF½¶ß˜zýì/cÖë¨ó‡èGn°ÄÆ£ã2ÐñFü<p¸qõàÑ›ôõàQC¤tíK^žùZ|=èy.Rz†§¡­7FöýìSêAý•Q÷ ¾Œº‰”Qç¿&/£‘_Ç—Ñæ#e´ùÃiȨåÆÈhdɧ”Ñš+£®ôeÔõF¤Œ:Ö&/£o¬Ž/£®õ‘2êZ6 5Ý}}ß§”ÑÚ+#g”ýNýŽ˜û?ôeôµ—ã˨ó;‘2ê|n2j¾12Úû¯ŸRFën¬Œ:¢Ìuè̵Ocþë« ÌuèÌmšÎüWÑÑWcÏŗцéŒ3BîI‚1Å~¾ •ã×úòq|)Ÿ/ò}ù¸~CFîȾòW˜ÿrèÌ9”ù¯v~‹þ¹¿NÜë†x¿úûEÐÉ¡_ÌôïG"iyú}É{¯Æ{e Å`gó£ñïzÞîu†÷Ùàû1]¾·?¦Ï÷ö#ù¾ñ×Óãûž_Æç{Û¯#ùÞöÓdøþ-É÷¿¾Ï÷ëСÜæÓáùò˜<Ç{Ñ(ÏåúdÜ{Á>âg~ïjwJ¾ïÆ3Í_Ôæ?ЬÊ÷/êÌ<Ìç?”5ϼ÷E–ùËÎø¼ÿb_$ï¿ø Ê{y¯ _3£¾u‰ 6¾×s!Ð<Kì?Årû?“âY쎤ëKŠþw«²PýÑZ¢Ûž¶rÍãHè6Ž‹Èz>ñžò)ü¨«ký­lQ ý÷BÍwjºrõç¿ ^ƒ:zEÕhÇ€gõJv:ÝÄÏåðyЗóB_·ÖÓïé×Óߋԗëâ´_|Ó@dò“_›šƒO-‰¯/WDêËÆ…оLZ¯ÈºzEÖÕ«?¨õµ ôiÍ`&Ü#Šwã)ûªäuWG_v Õ—[ÃZÝm¾e#6àD•Güº&‹‡WèËâáÛ"eñÀëá²^i<¯ÈãGÿG•‰7²Ì»¾_}/RLH›„,ø™¢Û~”Á„ öêÐs“”[GMÌCrR²mÖˆn›õà›úüðÕHþß¿dzmÖÎ;âóþÁ%‘¼ðædÚ¬]²Íz6j›5¬¬é·YŸÁsÜxÅ´6KN íâs¿r~5²íj]¡ßvµêèËëÓo» èÿ:úÿÀÁdÚ®Xöh×Ûl9úoÙè̽”ûÛuڰΘý ¹)íÆôáîÿ ýúpÿs‘òØpÛôêÃÐgâËâþÛ"eqN2õᵸ}¸'Ÿ‹YbÙ  Ý®odŸ!tïÛW¸Lc÷ Ôý<¶˜í¹Ÿ3Ñ1ì>¡w:ã×–(í‹NûO¼ö_güºcE|ù¶¬Ž”oKy¬ñ+_'y ƪoãþ³Ï½'ó ãÒ‘HZüß“2&c#u\º<:Ï=NïzgŸk`ûü(õê©W*Ï™/’ßÔç÷†ºH~ßýAòü~âýøü¾÷§‘ü¾÷XüÞ3Ÿ¡_R~—ìYâ¯äó”ñ¿_㳺vTÝ~y’ƒrÛä¶+ŒÏ÷|¬Ïç{~Éç»zä3±'0þ¹Ggüs:þÑãóØ|¾vÙ€tÓç3¡aû/§Íçðqg|Þ7_ÏÍQ濚uæ¿Ö':ÿEÊèK`þ«Ygþëîcñ9@ø<—Ͼí‘|NÕøu磊Œ(“âµýüz¼¾û3ú¼¾{F$¯ŸKž×ÛöÅçõ]ÏGòú®§âðzù0?ÛÊìûAþ³ ?¤o«)-3"mµÉX$x£}ô„˲ÕgïÔ³Õë£ôÖëôÖÝ–¼­Þzs|~¯_Éïõù1ÛFb«£è6¡aPÿú)ŸÍŠnGí÷xâ½tù<¢Û&6­ÖçsÓ‘|nx3y>{߈ÏçÆw"ùÜø7ŽÏÞ%‘|&¶:êxË1ÞJÄ~ Ï9ìóEòzÝÏôy½îH^ážämˆg}|^¯»/’×ëên¼ x'Ò†¨v;Ƽ¬'Þ8JW·éêöÚ}~¯½'’ß«™¼n?öa|~7ü*’ß ÄÒí1¢ÛQú!”eþ[_·cÙ:vÂs k};>³Jø"žª¥¾ˆÇÔ;—@ÆÃ #ÊÜ+öMtýÄ;¤˜狼;dhÀ6e€:ÒÂÒø}M`ÿòá÷åÄÀá^bÇBåù…çõåù…áHyÖçÄ‘gK¤Îºg‡Ë÷¯Aç,ýUHÑ97#|À˜ÏŒLý•¢Ô§âHhù‚GBueÍ‘º²†Ï¡|‡o1Ž+<w‡íøp„Ó65®Ê>E­c§b¶ÓaãX<o:f®~ŸÎ£Ç>¯¦žŠ1wÕçèíuu„œ›‹Ìs˜æ÷ŒZÔóTxFMÞ¿ù3r}þ¶æ¯Êa ?W…~4ìY+ÄOAßÊÊy*y–ªµ1XÇûÔâ,ÈŒŸ êã2¶µƒ±ÎSNVw2xL‰ù›bŸ»RÏ(‹Ñ6õ<îtu…øê{ñVD‡–Ô¯xÈjx.¯.x xgÀ»±¼ÊÚ Ž1mMx×FQ¬5¬K»vv£n Ï^ôu¦Ý™ûî9LKÒ™”tÀ룶Mx¯ûîx類D]Î,&é¯FþM“à ¾ø8Œ&cÖŽ£óç¾D釲_#ehÖh˜á³}8cä*ü{#Ë0ãœ(ÃŒ‰q“e8®áHí8R݉á0LJv‚ã‚Äq)A:vK{#Ë2Ó› ŽÓGÆ%Äa;8󬼃1]<¯É#£}zº˜Aäf±}˜fMLÙEI·¤÷‰²§íN°ìf‰c?ÁqMàH7%F‡±Hâ°j8ÒÛ%ŽÞqXŽô&‚ãhr8 W%Žc‡¬›é‰ÖÍL‰ƒèuFKrõ›MH{„>eŒ%¡O4½H/JFŸDš´‹2Ï«Iä9¡å™vyyžyΪJ<Oã<’çÈ4òÜ(óL†·{IžUÓ««3'5½˜uÕöa&KÐÖí:‘6OKŸ9"ô*óp‚u5Mâ õlv‰À1{y‚8dû5óÁqRâ8› «ÄÑ-dµ' ø5^¦žN^î©ûež§Îs“á"Ïó ž¾7i¹‹tU´²}˜™ ]“rO•õ2»3 º/iùÏ83=ºg»ž}$qf¸"é®—t_I‚îË$ÿsÓ¤{T£Û\•„ýö ºM™´'ôÛ¼<ÁvÄ'pÌ`¢ìæ½IØ5b¿MG¦gc ¬•}è¾” ÝvYö"AwŽ qÔÎg{kïd­H͇9þšëé“1÷1±*(Çú¨Jù¿_¼qê;ܘm”U ÝúÆÁµx€óµ”Å/ò|ëýç'Jm0®xòÇÉý4«Äœ*Òó¶¸'úÉþìïí±èð;çnÇxu;SÎczÿó¥»Öñ.†ÓÁ>—Šøs˜¸âr¿ƒð;Ç÷8à{ˆðh”•¡¼º·Ø<Û¶Šq•©ôcwc°]úÿ3pßJÉÞW©ášcäöer~ä8Éã$Ào¾1y̼„{eÍ<õ�Àå'A›Ow\ÙÿAð¤Zþ5 O#e€ñ}Û €O]!ð€ËóŸSç5xȹmµ„:Ë�~‡„!ô`¾ù>Bà'ƒ_¼.á>‚ÿÀ%áí<càrÿßT[�þº„—x;Àåü÷T‘Ÿ• pyþmŠ8Æïðë—ó½OÂÏjð¬Q€¯ð>ðÛ$|Lƒgû>[Â÷hðL{cðéÿ㺛à~>ò¡„ýÝ ðK¸à9ð¿ðb n¾p¹ÿẙÀ|‡€O^#ð€+÷MxÀ×KøiR®�—óß“ÇäøÈg$ü�‡ñ9Gäþ—ÉÝZÜàýÆ%¼W§ÈPîŒ3»£Ïçâýïkë=ò6œCB_öxÖ-ú]jêü‡•ÇØ‹ñ@O§k[÷"XFüç ?¦Ð»Ôè¾Ñ±ìî§âö úg p.Šû]<;:öEÌ+~ÅŸ2l´½š³§1ø0ß¡ìË»µT~i÷Ô`e1÷Âʧ|שºŠ)î¢3ˆ»è¾ë.:¿¨{nq}tÀå…lðÖíŽ<C úPUpãü)âWæOuîË+a™=ž6·†KχðáeaocÍí©w–Å8ãê!dKž»‘ö‰ò$ªO-ΧÇ9°ÕIîï#÷v#þ==Ñî4&ÇfÝ<gü~B9O9¼ÉØ«ðeÍïÇxMêð­ònŒ…ƪéïVùãLˆÎ=†X‡ÇĽà|ŽqwÎQMÆÉÈxy̺-B÷;<=®þ¨y'yß–TÞŸ‹‘w]óÚUÝÎŽ-žÁ>u~ÚÿåcÌàß1ׇ¾Î–öW™ißËÁãH˾w‚'öý2xòCpü™›ƒ§ž© ž~Æ<óÌpðì3/Ïéͽâœ-Ÿ¯Uý¡Û;e{‰Ùâõq±nb½DiögØòáÐûÖoxZî{v!3Çë§ÚÍ=¾ë#fƒ±`½ÚÏ-¸–ØØÂHÆróÌIyš¹x_t´03á>ýJ£‡=ÎÓ’ÊS¤i’yÎK8Ï[Œb µ§/K*OžfΤÌÓ–pžF¿§lÄÇÓ'5Þ”iÈ<›Ísç?EžÛxúæ¤òiæÉ<7&œç™çVLSró]"Í ™§;á<_abÞ~§ON‡DE‡v'œçNƒ‰çéåé-IåÉÓMÈ<GÎs“ÁÌóôðôIe¯ÌóX¢yÿÐPÄóìçé“Z’iÒdž Ï) ÿ£AÌ_º0}áÙ¤òiFežæ­ÑPÅótóôIå)Ò”É<žÞy‹a„çùOŸÔ<­@M×:ü$cOÖ?ë³](Ê´,2q‹Œö‹Œõ±ðrDº1ÀÇ„<Þcïž0ZWösZ0Ï‚‹IñG¤9&øSdM‚?4þtO?eÑø3üZþ̎Ÿ>ˆ·>‚?£òO&ÏŸü=’? ÏóCžc$OwRüéSÓÙ?;o‰äOc$öáY$z~切´ ð>5þ£ïº°ºýŠá0§¹ó¶$Ç'‘FáS}ÂöäÞ'Ÿ¿³Ø“É“§íSÓšþȼ:ªñ*oOò¼Êk‘¼jM‚WÇ4^åe&É«c¯rÏþ‘yu\ãUnKò¼ÊµJ^u'Á«¯rÎ'É«¯rFÿȼ:©ñ*Çš<¯Ì—%¯üIðjœðjw’¼'¼jø#óê”Æ+srýv‘FöÛ‹^ç€òžÖxeNªßÎÓª¼2[þȼ:£ñ*û@ò¼ÊJ¸O å=«ñ*{^’¼:«ñ*kâÌ«s¯²’ëoŠ4ê¸ÕvÒttx“aÜvAî³újìûfåZÞaQîÙaxNØ.à^§„ñŒI<#axŽ%çˆÄcÃsðœHÏ~¹ÿê ÅxôêBÑx‚ëÊ#Çáû8ŽÓ â8*q´†•ç4à8›DyF%ž´0<gÏù$ð{¹NhxŠ&ð*Às5Á2y%Ž^ ÇMiŒÃqRâ°%‡-ÁµØf¹'êœ{‚8dY2ö­Gg‚8º%Žå!z"p$º¯JîLw:¼ç9Ž= òTî'H_@pŒ  ËVîïJ;Op |“Óq21LÁ±—àüi2²e—%bn:-q$J‡äÇÌ«!t<—Œl™Ü?9óHÏ%C‡AîMŸ¹1„ŽýǹqÈ}I3Í!tìOйï.õTÏrÄ!÷¦úBèx6):Š%Ž’:¾Îq\LG‰Ü?2BÇד¢£LâØBÇ78ŽË âû´f4„Ðñ¤èX ÷”L†Ð1Âq$Xo 6IDZ:F’¢c¹ÄÑ©áÈ>"Ú»›Ý$Ë2£(owç°qHÙšÎP¢/1ÇÄq$ÖÖIž˜üax /1'- <ÊžŸ’0<ÐGš“™ž'åBì³M&§IâÙ†úsÌIài—x2Ãð@ŸbŽ% <­QDZÎ)ªmçkU±×ºçwÀŒ)é†êÿh˜SùÛâí“ôoçi;!¯v\ÃôÁ/ð=àºí'>eß¿Û2¾NŽOœ99ÇgêpžÏ¹ï‘b¶Ž_0Ó³=¸þ_Â÷Üàþ)HSØdh�:FÅ=AÙüì¥ýû¬YY»>XbåÝÀØÀQo 0”¥4ðCCâ²uœn æÞ#×}ù¾\Uî>~9x|øà ¼ûhø—Á“; Áñ7Oí¬ žÞéžÙ9<»óåà9Ü•è(ÛÉ\¥<>Qž,î{¶1,³Ì9ï‹§Þà}Þçàí‡÷x_‚÷)ÛÉ<+¼OÂÛŽ{´à½ÞGá}ï„7Þ]=j;iÁ}bûá]ï½ðÆ=»á=†g£à}ÏÂû¼{m'óñÛNx7Á{#¼÷»Þ¿ Þ¿Þv²�é]o¤³ Þ˜ ¼áý±@§ Þó€¼ÏÒßÅð†ñ^®ï`„üðNÅIxwãŒÎŽw0~¼cÊŸïÃ;á]‚w0Bº¼ƒÞ§ñFxOÂËåšÂò·ÂË�ÞXþ“ðÆò§á]Ùð |Âò?§ üùxç#”¿�ñCùsGáåÇß¡ü(ÿ”¿`?¼±üfxCùó0”?ïóÆò#ýPþ|àß”ßüšÂòc~Pþ\Ìß"×Ò;yÊZúüL–±î®µmw÷7¹Ú½h ' Ÿc¸GY/Ç5o¾V:©®}ƒNí¿yêJ�÷8âº÷nÖmÃ3mSƒJýÄuw^×^b6\sk+ȺxÝ3ì|íÛÌV;o;c_>ÈLyâüß”‚{|ʰ¾ò»0¦ì…÷ÙPGžGõ'|?åBîkë=î™Aø2§‚oÈz­¶ñêµíNƒ™ÖW¥Þñ»Êôî;Vìgkýu‡¦®ÛQ†¶ÃÖ1çÔÛ›d½õ«òMUäk8É2…|ïëïåFùâ^ôµƒ2ô_€7žg{sîEeôÌ¿ç+lÏ÷O¥îÇ2^‰E7/çîòù·)¶Ùx4V|~¿÷°¡¸e{‘­6%Zú7–ÚeÀ·AojXƒ¼3»30G¹Û›µbûv5 `‹¹†¯Û<ÝZZû3,ÞÍãp9ÆÓÄ98w[Ÿ€qrM+çjb§Õ'æqVÕ8u}­ Û ¼B†¡]ç~|ð.3s?N‹a¬Y[„÷2L©kíÈß'õ¼ËSÔ4x¯Þ”öØ:-ÎÊb{Âu:6EÝ«“®œó4nŒîãc°À¹¹ÇC||à´7|ÿ ´i¨¸ÿfÇ’Fê ÿ÷µ>ÔÃsV…èîŽG6l§z‹ûzpOºŸ÷òÐ=<@¿Ü³cw¤ÂØím612Ÿ5C8m8ýýùPþùËΗ ß>Ÿµ|¥Í0¿nç 7ê¾à_l]F¹ì‚úêw Ý¿?Ï\cûö7ê~5´à'> 1¾×¶¶Œþþ忀5ÙÚYKãÔ'éªÌ ?¬›~¿Z®Ô΋mGŸ™Ï6â|çbÃÖŒs½´)òòðk>awb×_ÿÖ¹¾Ò9h‡M6¥¯¥ôÁ íåXi_ØÈš±Þ?Ùñ¢¯´÷·ÿû'ñ³ª'oý*èô¸Ž€ÑX0ÛÔ}xLèW ÷™ŽÌcõxG$¦oœú=¿×q1ŒƒóÙ5ˆ¿\Ʊs?lóX¶jùv+þÜ*Œ™�O“ý »úìk­©^úÁo|5ëƒ×jÀ¶Ô�þןðm±Ôx˜¡f?؇gÈÇ“ÃË™­¦œ¥ÕÜ 8s˜iÇÂW,#é`ƒ–³ª@.´é/—Ö}?xÆ_8V °åøa°YùÐþ¾�¶ìë¼OîçëÕÎyDÚ1¯áEÆãÿ‰M [þÖIÖª]çn¢ƒ@¯¿ÿåR¤òmÂûwAY-T0ï¯nIȆ,þÓH]âgH‹w‚Ò1�±[™1Ë õ÷‰_2d‚¬Ë¤=ÍŸIh[¡­Ä±ȹÏ_>bÍSÝÜWµíÅ9×p? §åÕß@?¯†ï”ý€èÁ~È`zñŸ|Ø÷@ß�SÿÉýB¸ Âû¤GÍ}vþÜ÷_Ùg‡2Uñ2¡Å·ÙF(O{ãÔæ}¢<sÍ¡åYöR}•MÜw½ ¥rŸL¶W>± ™~äúà bÛãÉ×Êoý“Ù9Ûæ.h –rÿo¨»È ls§~ÇË0h«~Çý!]Þ.ú#SgÑ`ý­™àñÃêÿïôë?à†¾Z}cpÒÂí'ôSj¬P×�^ƒtƒÝ\6†ã•å#ì¨íèò§4úÛs1a;kSXØòÁ?\n^šƒ&î‡ËßÏÏ 5@¾nÐ7qâmÆçzAG ¨胒ëÂ|Öú·¾íè#ðo;bî/i ^×úßÊÁ©£ïeUZÇõNo·«3Ü÷ÂÓ*ߎա÷ƒ>ø§Üëãï¥Uï(¶G÷3w«—w8z{…Ÿ lV©µ¿Ø¿}xƒh‡O.ò)wK EáþG§®rù/ågø^\¨giØNbZãøÍ?÷á8ºqªóM®o;AþrͶ{.èDõu„+ûó…n³Ì¡×~ãCöYBöû'pŸ¬Ê“™*O.D— ç‰{ÀéÎô„úoø¤6´?½ƒó¡›÷¡å½¹èÿFðâ?„¦â®d1wÒk;ý8ã�(ïq>ßÐùs%]L;È}­<ÉZDÝ7îußÊùþ.ØÔ)Hû$Kkœrå‡ð÷"sž/áw`á¹Ìë'ç9àåçA?8È䵞ÝÂsˆï)S‰íÕ¹0V¿Ý!u´qp¿€?UN3Tÿ8Ý1î¹&rêuyÄuóÚ“_Têž/ |‰µÄí³¯˜ŸâsQS+÷XÙõü©t«>Q™7Ýè�%’nà‡ûñ1šmBè·ª¾XFø8Ç·SŽíúu¨ò ÅÎKY¦vpY¸£ù@QË Ý)`wÄ:3Ôãèíy ÁËãÝî–>å7‡nÝ®ðí²b»pN®qê ÷ÏúÈüísñ,ˆÝŸ’¢ë£Vã«z. ™å`~’qmÂeîy–©ûiˆÌtm¤AÁ[ÆÌ¯ðOö—ÆLL9”a‚ÿŒ$œ¥ÆIc™$<‹ÄÉ át6Âi$¬:\…ÿRIx ó?ÊbÊbÊbåž÷÷õw87w{Wû:œno«_½‹/„0™N':kJ*ƒZ6þ à(ÖERÛô(hÕâ^GÿæÅ-Ý®mŽM½ÎÚ…÷3¶î®º«B”ÕµéQg‡×ÚíðX79ýÖÍŽMŽÍNk‡«·—Ÿ.âüäiœxþb-=ý›5N…˜HÕïí鄟3DšUŽþÏ{­¯×ÑÑmíç±z»ñ’«×e½½µ P*˜j¬·uÊtwz­®.kwy«¢A=txV|õZ»}Àßûú=ƒn·k�hÕhpzú­Xa~~tÇÚÓ-‚Æ+ÀTzºzœM=ý[xBÆDþ›^îG éê; @Œ,•‚YŸÇÀæÁ>g?²Âʯ'„¢²PÚ…¾¬w¸Ýº æÁˆD…s<jtËþm-®XH˸D»ÙÑ['QeÂ8¥§ôn®,„7LùKBƢΠõqd°¤å<½%´zïî”CdªæµÊêá¾ÀHqmÿV°Á’F«££Ãéñ°ˆôÛz: .Ûg‡ks?˜oîÚ»­›ø9FU'oóÔXÜX,^§\ý[A"ª,âɋ“;¼ÈãNgo:\ýÖb¢6�ux!¤³v9zz°âÛ:rQ®§žÛ峋Ïx‚g­„¯ãr©µZ æž‘¤^(•¿¨mžéGd-.—µÏÑ¿]Uyµ¸Ï¨WPRw•Út·Û9À1hÌÚ ò‡×¡žþ.—µßåÕr„ò„W¬lðBa5œâh*¡¬oÐã…´vò“«Vn/¡Ø«{ §¯‡³�K‹²v 8¶#“]ƒ½œ×&¯l”T4äIÅsÂòÞ¥Ù¥º‡êZ¤2¡aeßÖùÈmžÐ6J=ÖHtŠËn"$¶²ÄŠ׸¾W@=ñÙŠz߯Ò?U§¹w7åAi—‚e=”¼ëèux<Pò¾RˆV ÑJ•hl9RcÐâuØ ²°t®~Q`º0WQñ¸BëÂqàV(Hsm}<,ai7÷º6©p‚ýÎ(É•>Å »ôP•+^«S¦°H W8üƒ™±®WŸòULz«0 íVˆæ vaSd¯G´bµ¡ôx îôt¨d¨Ùð`t’„åháV3’ž…>„æ«¡‹“¯×¥ñ$´Õ"Y… ¬Í€.W"@±éR²‹CU8-kT6Æ¢&&Û4òBê—j/#êJm4*ƒ^rw-VªRûÃô‚$ˆ­£Z6ýÎmk¡Â: ¦èê„n9jõùOp%š=ô8E_"BJõ꣒(ál˜cÐ뺚E®ƒ€2o/FÅq=€·©‡ÓůXOQŸ,¸¾dÃp+mYl«B§WQžp\ÊI`ÓÕƒ+·ßïèLF¯Kµ$ºcÂÈö$—U/ý½ŠÝ_PÇ»Kwó¶Ø“(N5yihòØy%N3M‚õ[-‹Nû §-ԣƲ‡$›h&1’<¼ ÆÈXzµ¥2>~(ŠiŠCSúÄIcª7ás8„wÒO“&Š6q‚´Z ïzG¶sÚ\B:Ò´Ì_p ºãµËáä$ÁÙP=@ò7Žn+ÑtælDï%:½8J›jÔV>ΘÄKé[ ú°Ù9À;Cqh£QÙV={ÇñŒi½þw"´Ð¨Ì-+&Õ! Ñh&Ù/!Ç|²Q±U É24 Ô‘­®-Øï\·ŽBpˆi6Q´QÚ¥5kÖ"‹”7Øïˆß¬}=˜`@”Ø$¶Áàê.WÿrÈ)‡¤=> SÄŒU–V•–AºªŽêe›:ºªÊ6U–-µ-©(‡ÿË7-q,éttBÈ©Œ©õæŒ;Ìéâ \fÚoxiýæ­ס”*jÈÄJ\):ràìŽTOñBšFC8—S*bóI ¼îC¢!ô¨s.˜»’ ϳXåês»úa$ŒY«s¥ý=®RÁ-&µ\%PÃÍs$ñÕ[P^_äè𪩒-¯Ôø‚é0²LǹGË€¿sàJuŒÙ(Ä^*9ÊLV…æ…P0¬t¹zŽþÐò`dŒ$d">JùdÄÝ]]'—µ(K©×…YÖáoa|ãlÑèÄEâÒêdÆsYÆ5aúu¿«§SGH‘¡¢z%”å“N{ŠÅ÷B‚CrP‡˜ŸÐ+¥ñÊ"Uû-#*‚5œÖ…LW'"Ó£-UÕ.”Ðp^IJë‘Ò°z#´,È™ZΫhU$4OaI@§DÂÊ!k Nz¦Of‰Ó2®ÛåáSž‘2”q‚¸šÝ³WŠEHl¾ÒÕoÅ­Ÿúsâa=aò-íØbÍŽ…·|* êËbYS´oÍæ¨0bOTX˜ QáÄ.¨0bTXXVáaõW£]ÔÅ–›Wóº¦3£´úD`¼dª.þp$󰪞h0­ÍßšÍßš=Í“¦“)u“+¾×Èw½x‡·bYRE©#~—‰‹W`çá•¢¿ÙÂ_ˆáŸ«x\Ù¦D4šŸî<Xˆ¢3ì{ÉJÂÓ¬eJ£pm4®•ïuò½F¾q€/ `!02ˆ‘•ERªí¯³OÒÒ<àr;¼ÛuË­Ç5ìAAßÕ©¬©´¬Á¥nQ_ˆµÏÃ+¥˜ðpBOÖSc½Í“-Ž2¦ —HÝì—µ_ƒ—]¿>è¨ Cábv>ÖÛÓ¿שÄ “ý8¾Á¢£§ ¿#Ö¶•×4ˆË5›±N©Ñ½ ùJxºÕÈ|ãwyµøÆ7~WVˆo|ãwu•øÆ·üãëêfð×eòEð&x,ffÜ Ð�îZÈ<dL¯cš—Õ|0%§îÉQÓKü>ÓìùøJË€”ü÷Yøï)í“«’of&Ìg#‚-Yö— €…`ÈÌ”ëﯛ…þ…ÓgôÍnE(&.ÊjÇפ`æ-Ð>Nì·šYv cÆÓ¢döCFKÇb(S’Yf ÚrçéÐÓ è):ï žúÅ’_i£ü å´ƒ)’fVsÛ•âcUj7³›Ãûd(®Ì\Ä âô2»‚†ó舙Íi‡÷¨¤D¡I%†3$«pb$–yš,æÖG/ï|+¼/Þ˜òZ8þéËkEŒM¿¼7Ó)o±(ïÍçünLPœU†´p¼kFÕ,3ûœ…þ…ã+ø0ž©“–)øx‰ ó$F‹²ÉÅÌn‰¯^àÃ8&«Ðe+âÃà„ĕɫÁd”/5ŸR½òKüø[ÊUL`%åg§Õòã¿UôË0ΛŠÃ:øç üü·*®$Y¼iY*]çc”¿E¦Gù\ ‘Р •ŸøoIÈW=ý2ž å¼™-=«“Ÿ]äWò“¦è¬P¬â,÷!ã¨éÅ/¤‚²=?qwJÜÅ7šk°?ãUUÜ&š‘ZM÷UM7ge¤çvð­_f¶q?êJhþi"ª9k$Ò 8"3.ÂŒ—ÃsÖÌY'ÂhÑeÒ”šù#ùn‘»ò4›™s#„ÝZ}Z!龪̬ ôÆPÏq¨í‡DnÉ1ËTã!â0)8°íñ™Ùæ“õñË•B|ÿ’í©e»kvÕ¾tÛ¡Ïüühñ‹ _¸ýùEJ¾yÇŸ–>·xÙ³åÏTì«©úÆ’¯WméÛÆ·Lo¦žLûAÆ_g¾‘uÂüW¹ÿÝòzÁñ¢¿œóýyßûì1ë»å» Ò×dJbä¬Ê1>ªÛ-éJ›¥ÐcL3³G»^Ðs,ëIOZ(=»ŽfÖ{Þöÿ/è±e)|I±š™ ÚScó«J;ÊÐÏ”cföX€á1G®Ÿv™¨LÑ-Ä9q&fn Æn …@ßw(Õæ¥ÛD»7‘´bЛ%mJÈv*¦c_¤=ó†NÎSY«¿™®T#nR°­$ ܼ¬v4:/¤Pê®_ò¦l–R/Y¯™ ^€7·ãæRÑN*H_œ¡/ìf¦l8³¸`…h†ø»Ú𙿰vVP¹<k„Û^4~ÏÏ<öÍô?ÍxnÖþÌggögP7RŽJº]Ðß4˜ÌìX·Ò8¦çgJœæQ¤t¤ û4/Ëý’‘ãÇ5µ„øo“”°™í;ƒ¿VÁßgîÕ‘ŠŽ`ÄñLjù<{ÙʲÏÃs1F~²ý|îH’÷Å25ž‰2¥©fÑ€ŒSÄ ·@kŸ_p[™ˆK)³B[á1+›a†§žcV– áTˆ“ á™ðû¬€•eNXÙìfQ¨ìI+3§Z™ÙðÎxÆà9Ï1xNÂs ž3ðœƒç<á¹ ÏU³á¿wš ß2™ ¯ì…' ŸkðÀï¯LÀi^9 àyð½ržV6h{ž#V6Çï"xJà²Ì­‚ÇÏrxìðÔCœVx6Âe›Ó O7<½ð@ùæxáñÁã‡Ê8g7<{àÙ Ï<ûá<çŒÂ3žq+» Ê?g<@ÇÈgÎ xZài‚§Ye±èïe¯®È:òüóà„ÃÛ¾Ðt÷ʺ¦¶¶5kÛz{6=ÚßÓÙãq;¼Ýmòc}äbÌceù y{µpÑ)-<ߪ…‡Hœ§Ç´°wž~¦„„Ûµð‹EZø‚uK fjáïïÑÂïQÆÙW4ø»‡µðwIÚï-×½[ ÿÀ¯…ÿƬ…Ï]ÔÂ?%á8¡…¾‘„¯iá ãZøÃ2-üOÇ´ð?7ði-ü/M$|V _j!aBϯ ÏÿO³¾|I O¾ð‡¬ˆ„µrfjò5¤7žk¼2|& …ç÷ja«‰„5 7§‘°ÆOÃ-6&ð[«H˜Àø‡†ÛìZøsš¬ Ÿ'ðâN-|;¡çvB )ãǵðâV-\6©…+4YªÎiáj žÛßj4¹jIYVzî$ôØ ?ë «ˆ¼ê‹µðj·/huÁРé¤a¦{†FM‡ MšÖkõËp·V¿ Í^-|Ï„ޠ餡…Ðp¿fC ì×­¤,iuÊð0áÃÄ·Ojá>"Ó>Ížú Í.F¤¼.B›{ =Fdô‘õ�‰¿à|V³{†ç=£ZÝ4Ôì¡áf‹ cDvc?¡5³YEð�,t;{ÄÌ,%f–}ÉÌ šÍ¬ä–=*âÎ;lfŸÅ¶öˆ™Ù ^ }Ím8Žä�.²‹pÔ½lL{ ï"7­ÙÕ›Ù=-f†òšm7³ýP¦Ù 3E ÇEÐߘ}°¿³/ã›MãÚp(Ó·Áþ¼ç@žc“í1Ø›¹ÀϹŲmzæ¶È6êàܲ­n'í5´sAç‚.Ì…¶ln¯hÃçBœ ü;j6|çÜ îMØöƒ|o‚úo†ün}¾ êXèà\lËdÿ�êÔM&Ùn[e;n—m÷ê(ýÚ€twÎ1Ño˜:4lÃÐÿ9 »9GáÞÌÞÌ)‘ý¨sÎcŸÞd_óàý!x_ľ¼Açào‡…üç\Åþ¼¯` Ê åœ»@öæ™Ù'˜ö——H;ôÉÒ±¼› |›‹8oÀ¯9 ǹÀǹÀ³9g͆ìV³!åôÎÝm6¡L÷C»qQöß ö¦:³Xhÿl.”}.äm9›A_ƒ´fÐó7°/‹ÚÐWÇ#ŇàIaìs0ÈüÆ>_ÚÖ6àÅÕ7Ðé4f4ÌS;#cÐÿ´Ï”x”?èSóî– w ô»ÔŸyu¡ñçJxF¼8ÞÖãÚ¤ýlŸ¡ÀÅ­ì*\ߎ'×îñô:û#áŽM¸Š ßÖáÑßµMnÔ Çß³¹ßÑ ïìu¹õðtöz¶÷éä+ÿª#ãã9§'Ù¤Oèéwôô{»œŸ×ë¢`—«@`¸’›ÁBÿîøF¸àÝ:ða€{uà »æ€ü»�ß«ÿk€Ðÿ ÀëÀðc:p°¿æ“:ð)€ŸŽ„ ^›ÏéÀ¡Î›'tà5�¿¬e¾¦wXYŽIþÀÍ:p°‹9ótà/¼Xv!§Jþ.Àí:ðŸ¼I|Ëi„ÁvåtêÀ �îÖßp¿¼à{(\݆Çröëįø˜ü.€Õ? ð:ðG~Jþ8ÀÏêÀ_øø{�¿¤ÿ€_„§Ì±²\¦‡ö27Smfn‘üi€/ÐC=Ê-Óðå‘pSÀtà Þ¢_ ðvx?À{uà pŸü-€ïÖÿ3ÀG"á3²�>ª¯øø~\þ%€ëÀ¡íÍ=£ÿ;€Ÿ×ÿà#á©7üŠú-¹“:p—•å¥éÀoyø›�ױϩ¼$>Ó�p›úEyõ:ðûÞ¬‡z‘§cÿg¸ŽýŸ òÍÓ±ÿ3¡o•§cÿÓR�®cÿÓ>pûŸÖpûŸÖpûŸö €ëØÿ4è;åéØÿ4è»çéØÿ´O�®cÿÓ¡Ï›§cÿÓÿà:ö?ÝaeûŸþ”•/éGÀ¿ pûŸþc€ëØÿt¨GûŸýQ‹ŽýϨ¸ŽýÏ踎ýÏøÀuìôÕ-:ö?ã‡�×±ÿ|O$|ô¯-:öÖ�®cÿg �\ÇþÏ‚öÑ¢cÿgAßÙ¢cÿgýÀuìÿ,ÏYtìæ €ëØÿL°ŸûŸy/ŒUuìæÀuì&Ø¥üûýõ"xæ—ia#™ w“ð Ÿ áI-œRB $¼Ÿ„/haÓ<¶‘ðn>£…gd’p1 7“°„“ði-œJð¤<©½$¼‡„ žT‚g&¡&¡¦Ÿ„ ¯f^ÕÂiU$ÜJ£Z8ð3ý¤Ψ'ás$|M Ïr“ð¾¢…3 ™¤¼™g&Á9›”wör¶kú3»ž„‰þÌ>JÂÇHœã$|‚„O’ð8 Ÿ"áÓ$|†„Ï’ð9>OÂHx‚„/’ðe¾¢…Å’ ›H8„‰Že™IØB¤®eÞB7E / a¢«YD7²ÊH˜È4‹èd‘W–„‰.e‘:›ÕD¤~eµ0ÑÛ¬$ÜNÂ$Œú`”á^v“°—„}$ì'á� ï&á=$¼—„GHx?  áQ#áÃ$|„„’ð1>NÂ'Hø$ “ð)>MÂgHø, Ÿ#áó$|„'Hø" _"áË$|…„¯’ð5žÔÂÙŒ„M$Lìvö­¾d“¶#{?[—=FàGü('pb+²Ç œØ‡lÒvd›}žÀ‰ëº2|‰À‰Í̾Jà¤íãU\ÂÍĘ3 œÔ}3iͤ¾›8©ãfÒF›I½6/'pR—Í N꯹…ÀI5·8±Ûæ^÷¸À¾›À÷ø“öÈ<Jà¤5!ðcNÚ 3iÍãNÚh3i̤]3“¶À<AàÄþ›/8±ÿfÒ&š'5xi rÒœØÿ ›Ÿc%pbçsJœØöÛ œ´¿9Ćç48±Û9 œØêœn'}‰/“~NN€ÀI_"g/“~`Î#ðÃNú 9¤ÏCúT9¤ŸsŠÀIß ç,“þ@鋿Lø%'}€Ò˹¦Ás™Ï58éäš œ´û|±'m}n1“ö=·ŠÀI›žk'pÒŽç68i»s[ ¼À; œô‡sÝNúع~'ö?—Øÿ\bÿs‰ýÏ%ö?wŒÀ‰ýÏ%ö?—Øÿ\bÿsÇ œØÿ\bÿs‰ýÏ%ö?—Øÿ\bÿs‰ýÏ%ö?—Øÿ\bÿó˜Ï#ö?Øÿ<bÿóˆýϳ8±ÿyÄþçûŸGì±ÿyÄþçûŸGì±ÿyÄþçµ8±ÿyÄþçûŸGì^€À‰ýÏ#ö?Øÿ<bÿóˆýÏ#ö?Øÿ<bÿóˆýÏ#ö?oœÀ‰ýÏ#ö?Øÿ<bÿó&œØÿ<bÿóˆýÏ#ö?Ø ±ÿbÿ-Äþ[ˆý·ûo±8±ÿbÿ-Äþ[ˆý·Ø œØ ±ÿbÿ-Äþ[ˆý·ûo!ößBì¿…Ø ±ÿ–�ûo!ößBì¿…ØËûo!ößBì¿…Ø ±ÿbÿ-Äþ[ˆý·ûo!öß2AàÄþ[ˆý·ûo!ö?Ÿið|bÿó‰ýÏ'ö?ŸØÿ|bÿó‰ýÏ':Oì@>±ù¤-È'6!Ÿ´ ùD7ò‰}È'ö!õ$K†‰­È'íE>i/ò‰Ýà®p•0ÑŸ|ÔŸÙ2Œ6$C†I;’OÆzùhO2e˜Œõò‰^åÛ’OÆzùd¬—OÚš|2ÖË'6'Ÿ´;ùc'±?ùÄþ䓱^>ëå[”OÚ£|¢“ùã$ŒúY(èŸE2Œ6*M†QWgÊ0êê,F{•-è·fF½Í‘a´]¹2Œ:œ'èÃF;–/èÏ2LlZ>i× ˜VÞ2Ö+H#áL6“°…„‹Hx [Ix “p —‘p ÛHx9 ÛI¸ž„H¸‰„›I¸…„[Ix# ·“p' “6´�õ?]†É\GÑÿ2×Q@úT'sDÿ ˆþ¹Ž¢ÿd®£€ÌuŒ‘0™ë( sDÿ ˆþ¹Ž2×Q@æ: ÆI˜Ìu¹Ž2×Q@æ: È\G™ë( s$Læ: È\G™ë( sd®£€Ìu¹ŽB¢ÿ…Dÿ ‰þý/$ú_Hô¿è!ÑÿB¢ÿ…Dÿ ‰þý/$ú_Hô¿è!ÑÿB¢ÿ…Dÿ ‰þ’~B!ÑÿBbÿ IŸ¡èa;ý/$ú_Hæú ‰þ’þd!êª û_ qˆþ’>F!ÑÿB¢ÿ…Dÿ ‰þû_Hô¿è!ÑÿB¢ÿ…¤/ZHô¿è!ÑÿB¢ÿ…Dÿ ‰þý/$ú_Hô¿ŒY I¿¥è!ÑÿBÒ‡)$ú_HÆ2…¤?SHì!±ÿEDÿ‹HߦˆèÑÿ"¢ÿEDÿ‹ˆþ‘>O‘•„Iÿ§ˆŒ‹ÔþÏb—Û»7gms 8÷ölZ¼¹£c±ÛµÍ9àgSߎ_uiyiYiÙâªÒêÒŠšä“,.-ÿ×,ô `’üi€WG©ƒyºJ]⫼¦2ÚÚÚúœ}ÜÆ,îs¡SÇYžžÍè½¹f‡{ÀÕÑçðlÁŸ¼|óUÛ�4êÂÛ.q%”Å}ÜkßsèqÌ=É3öÝßÂ÷|ezÑOøÊíwqwËx.]îúÊêwnÃÃ芓ƒlùý@8@uºf–�ÍA^Ñf§·íQî°]%µ)î‹ 'õè…`˜;®ðô¸úÛºz›”8¢’®ø²ù&9âí/Kõ…&Ü䣿6á7 Å­WŽ­®³s€;sÎ#ÞæT æªbW¡9/”|s"]© ·Í땚ÚîŽeôª^\ÙlÐ Í©+Ërú¼è£Mºñ™Y·¹¤Ã‘YÔOëld›ÆÑ,ås@|G½…C¸°R¥v[ìxН“âØÑgâ Ts,ˆMøóˆ~ÿ™ˆ ÂØ­±ãO!q" ·&QoeUPú\UÕ{QÔ‹( ~†GþL´Èbçg£f-6gF¥¿«§¿sÃö¾M®Þ2給­m][åʵkŠ·*Áxõ Æ[“`¼µ Æ[—`¼ /†®u:}wwAÌ•1«·Æ¼øÑV%­>±hk‹¶6±hë‹ÆÙfMºµ‰½ÙÄȪ­\3—Œ•Ä@TÏÜHÑ­Æ–ö%êÍLØ@ã2?FnYbO˜•X¹7F1‰U„¨q qPœG%ÆWEQ­ˆ‡È"1q¹ë‰Ï]O<îzârן»»Q)QŽ�DµÍbÏô’pWõ1J‚„µõÐXC �+äÚ&î{‘\yÌ#+¯ñÛp÷¿äå—c@¡ÇÓæÖ>£[EIµMä¸çÀVâ”>zôͺÑcQN¢ÅbŸÝ/:Œ}>FԺ浫º[<ƒ}ЙҮnÖÐ{~cÔíÚÕ9…Þ=CGÕKâb ½4-V§#âÖ®x‘C®Êb¬4–GÜIÅXa[½³£êPeE·|ÅÀåµUµeµä‡êªoÿÁƸIùFH5ÞJþKy5ÿ) ëR›·Æ[µâŽr¨YÛ:Àb!`ÉŠ;–:{¶ÂWKõ í±A—·æŽò’²’ÊŠÚg†++ðͽKWTC¯µW&³©ÉªH²*-Y¦Œé]¶ÂZÜ«$,/[á)¯I+1Ëê*‘>ª«ð g+ѽåå+8õè‹W¬¸èÈæþ¸8 rÅ•PYÁX`$p\�®Hö Š¤zÅK Ê †e)gŠyPCc[qž3¨áYx*æ>‡Ae+Ê—ÈH ^&"¹½œß@rc¹ƒT)„bP Y=‚®Š*^¸|*ˆ«X‹¨…Õ¼ *XY±T70 ¹º`0û2^tè+Ë8 ÃðW– 6ä!¼KÁ^)äbQ€weåŠòJ•˜«V”C,ƒ¡–púòÃPT¯(_JÁÇÒå6‚YÒfãòÐ"Kð2.“2¨)>©WUe+ªÊUM¬(Ó±¢LÓÃ<€hxŠ N5£ «äTä¶µ¡9AYU·¥­m�—˘‚¬*!À<S—r¶çsE$`›ö`8|™ˆ.²RuuI'¢p0^.èÈĪ�ŸBj³y=€OQ—fó<àSÔ¤,ž>e=Êl“©•jÔ¦T¬%K9$›C8FW® àq„¾‰8˜Mu™¬imjå«.ç ó®®J—+!<R¥€å@¥êìéêâ0Á|ó¶>I‚%UV—ÛåáßÕ<F~K<²Âdy{úDÁël0ä[8`™� *&ti™,¹;MRÎK•Å5¿+d P� «|¶G1ÌK­Ù½Î­Î^X"²éttvŠlªWTxr8@pféRG€ýK…Òçu¨ÉÀä–­¸ýŽ .J‚lå+ª–r+éxl+–V€Àb«äudv‡ ßV%T¡ÓÉ«‹m‰HŸ2}µH�™~)OŸ,êçß‚‰fdªL"¹èîé€eeàUåj tYGŠ1$@PɱJH/=T7PT§eKV,«†Ò—cîmm x)0›E—Дe‚¾ÙÊç2¡ð)­}Y™P ³[ákyY9”µÅ¹]|W¬¨²IjdŒJ.±\‘ I™¨F¦ _©6÷£+K©íXŸ«Ó)�K9‡A£:Å·M|oV¾—‰ïù +~ç:½Ýýƒ}V¾b)jvŸ§bÅ2È6Ë­|Wò€æËï*N~¥§×±I�€Nh-åÐBâ?з(«\ªþÕÂwyy-ªZvT9‘È&¼MÂ]­€Iáw(äVáçlêÝ"U®šNÎ<�)u¤O„å‰h’£¼ù™Ëˆ TðYF•Œ†¶F‰ª@«94¯ËC³†Wr`WO¯ ´q`ö¶ 2[ZÉ*Ä4èqv¸ú;=(Ì—Å ­ôq³¢$¯JVîéÙ =tÞA‚æÑcƒ>$@jî°‰ÞUwò©v°rÕ$€JY ,_*`’!ØtB;™2™P²›Qh3=•jÊ¡¹tHñWÖ–/<Kªy )NS‹Ž—¨¾·wyxéZÊ¡Éôض:z!}¥š´Å&Z[Hn–q!1v @JÂé€ôK°…ª N�ˆ†³A8ÀU~º…ÛÙ6‡Wô·Ê¡ù\j4Wù¥¢ý¸Œÿ8Où±oÐëô©¿BSŠ¿~Fùu`ÊOû¹œÿüYåçM޼7Où½‚ÿž«üÎa•\? ˜bO°Á¸=„ñ Ú èª/©hkëó¶ms tÖ”Ck«ò¤¢–÷߫ʹTî¤å°‘�‘VUIÜ*TkR-/*—p}PIpõwˆŠMr9´Â°!m�U0hr­X*0Ø–qV*<îž~¥!-‡ö¹ZdOáq+H4Ù.8Ê—q*ê˜*P€¡…ålkÛ4  X¦¢dHý\*6Ñׇ^;´×žŠ2[À% h±U*Ê%ÕBKóqÁ©i´å(Ó›؉¶I5^*D˜'~R€¢2‡ÆWªîRaж=6èè_ºT©3Ю“:S®Ö™,RB¯v)6Ê[ón½¾¼­}â†P,ç,c)Ca‰ò•x ¸’· æ-ªKp˜²%ã’K°AÃh²æÙª…Ëp÷+ Ù­ÊÑŒ1ö 8È­ÚwìðV‘ ]‡n6J@p—R1 c€Vrt¨° ƒ„*D´f«{› «Ru« ÑÍÊÚêMôx#8 |/­º[©|¼«€w϶n‡W–ÉîœS°Š2Ñd;Ü^96o¶»S¶g¹=ò[t·Ý=]^ªâir<´„ƒ Tm}nGÀDïË<Ø/™VQ&ú…fËÖ¾~èR¨PÙo‘¥¯(—°- @Xqà˜"¤ŠrÑϽêkSjQ…ì;d;úœ]Pʼn‚„›Ür,,Xmpn€jÁ¶ò­}²WÕRQŽïà:o““.îF½fé2ÒÞ©I�mEŽ W::=n·P€Ì[µÏ@Ö,‘xÚŠ bè–Ôb.€vI9Ç›§á€¡z9 ^†þÃ׌– è…¨¨—V„¢®¤¨—VÌù à®Zˆ[Ô¶þÞ¶º––{ÛÐë?b_²Â %©óRSVrÏ€—:kÊK�GAH\@R H ‰D$Mu+W7©X–r,ëa[V²fÀ‰÷ä޵ÍM«VÖT”Ü»¾®©éîU5•%ÍëÛV×5×T•ÜÛR·A„—”´¬ZÝÖR·²iuMuÉÚ»Û64×­Z]³´¤á¾/¬nk®ûÂê’Sš+Ð’¨�I¼ÖãîíØÔÖçp·)²†žRtÀÖ÷ ´¡èj`¸&¬�P¹8@ ¶1Š×[sÇl×Klø }Pg/‡TV ˆPäš;––@w¬3ÂpqÛÃAÕh°kå”hgMôˆý¬(S["šÇ­}½îÍšE«€®(cU<O.rè™y` �mÛñØàÿcï]b$K²ì0Ï$U]ù©ütWu÷L³g8ŸšÌæ{öþžÉüDUödeædFU«‡ä<yø'Â3Ã?íÏ="³QD`V”(A�r'hô!µ‘ÒZ€6´¥�A€�‰Új#-¹Ñ=÷^³gÏý¹GdW7¤Å4Еá×®™]»víÚµßyC*.®§ºø>o{åAÀ1ÍE%ÔæHN… ÎåúY¯<WL‘…V­ºaåºå5 ïñEº×ô±,`˜ºÿôÕtŒÓÄÅ|üÕáAd,Í躡¯¿É«ÃQÉZñ1a©ŠŸ‰òc¥Šß)Ïñüä]ÕÓñF•¡ê3#„XtMR(8°ÉßÐ\Ø´¢¡ËB_%ÁnƒìXQevG¿c»îWaÝ7¹^S¤Î²$²kò7%b¥žJ.â~"Øó:(usê¿E!ÿP’ X순úépê;~ÓR¹›-„caHcKxÛ5^HUÀè 2]6’?qñi¸­xŠG{”¿ŽBB.€ª#ËæMÑ‘Î4tQ9ÎÝ‘˜0Š¡þ¤ÔÙê]$tsä<…\ß+ÈÑ ¥ŒNJVÆëúMªA[56{Ns‹RZ•#PGvãDÚ„_¿Daø᧨ ?cü$IÿÜvŠ„ä=RÌæ20-Ó%ùͪ^Ëì¼€x &cw[ ö_"Þ0+›tÝ4ºÉ_³•iÔvýt<;’Y0“Q¡–¥›w²Trß¾Ã}¡eÿ$<’¥>Ý(=çÝËš+½àÚž "8L?·]¨¡ @êfçº#f°­�s>4¿UùòŒBË~7Õ@?±톑©‡B¤y"ƒ\q+Œ×ryú±f"_D™xefs%b'^.édʵ‡\¿˜Ma^9Ζ¿('ã)-lªóaUŸ:yP-ÁÜ8|øÃ1ïšhû2TJýÁ§t<ûæ:„Çz¿‡)nû6 Jñ9=YîˆÖâGãeE|õŠ¢®Lzš$Q*ëRɹ4RnÕs5§æ GÛ=Åoÿ $å<¸ÉÃSJa¶gkôOæòiüQD­}4]ï£Üe”´¡ð·µŸ¦ëýôÇ¢hOØdCÙ…YWvaje?ðJ°b§›eltXáuØc¯ ÛöIÍ2b]þ{…Ä7èžÀç(å¨W‰*¸8(ƒX¡¶¸£òd||RÏD8ã·Óʧ͂xàQäOÅ‘‰,NǼËa§Ã3eLÕE „Óû«E—ôŒ ”Iï-S\§Ý�Ùž¹È¶q&%a|FAÔ,Ël”åYÎÇ ë¡öá!r) ã6 ¸W½ÒÔÅ{¥Å®;®|¹XU½ã!òQOff±*W<Dݸ#Jå(’HTТª0Ð)–&xGJKXRa,©REËJ"o1:åsFšÈ,­÷ÚÒr[Á´:ïÍeø¹ ¦GX®01±UàÂ…%殒긚@‹Ã´¦-úgL‹ëJx»‹EŒs[Ïô¬_ƒD¿%-Vkºò\Ô)½š­)4ÞPh¼©ÐpS¡á¦BÃM…†- [n*4lShئаE¡a‹BÃV…†› [z ýsQ¨X6ðü}M4• q\ÓØ³y:D[J«,-2¶«ÕH´`kym‰h²U¯‘´NK´Š5³õ8ÍòÙ¸«IUKÄ,ˆk"ëÄ´®É*—Èyd+Sí‚Vت¬z‰X¤_,}S`L!¡§àDݰ§áĹáZÇ‹ùºs´Zw¢c¦9Õ‰ŽA«5§:±VꘉNs¢cÃÑy°¦cPkÅY3ÕiNu b­7Õ1kÅYƒ¬ªs:- LÜT²Pó\µübéù\ø+¿ðÍüBç6e.{ËÙdÜç™P·´b"s$…foE¿èVÜ]êLÙÒa –¢Þ’öÿ \¤™™”íE!ÅT)å.!â̱V|Ó_2¥?p@+æXX…‘.{ˆ°!Èm…·JŠÔö*Zö–+¿‰vÔ“ùJû( QÛlÿ•$¡ÝqX–óï¹s÷ébîQSzüIv(‡c„ªWÒÚ‡Q˜’²¨¬U) E Âlï³(Ì÷F÷Bm=§×¥DaA&ð8z%ÞdPÀË+C²c¢8!íjð[1©–Uï ±‡¡`*)Ì|1<ëFÆ \-òÍëÉüh5"j´÷™‰ìR“ÔÕ3¿íæ*7oã®®Çïãù¿Br䪪3Þ.º†Xlß§ÈH{I;eø¶?çWÞ°÷Ý,²2©ø—vF_~H/œÊÑ~É¿tä¼Ê/2“Ÿó*K’€~é Í+)SÈh>\A~ŒŒœ~Á>åwο‰ÛÄevMLy¸ÍèõÈÄþ¶ 9Ln?5o8_Rj½wß§Ê’Pö(kH©©wHySvá÷g¥j.•/±IJ¿£T~÷Ç!R«Ì£EoÚ?-×¾·Lˆ´Ô3 Hö{ý“a‰R|¥Þ˜T¿G^Øp7ð÷çd«&÷wDÐe)v1È+SµH/;&Ôë&%5Ò¢k‰o½TCdïŒíLkŠEÒ4ÂbÃüŠì˜_íÓ·ZŸiXŸñ­ÏøÖgÖgÖg|ë3 ë3kÖgÞÛú² i|köæmÐÆ÷uN÷ímÍÄÌš‰™u3-&fÖMÌl˜˜i3±Èø&fš&¯™X5LÌlšXæ:6žP¢x/Šp šã¨}^OE•ìÛÅ=ývNR¤GEaÉTWÁεèb0ʨ ‘ã@êq[ñ¶¢z+Þ¯JMÐÕ%»ìRþŸqùºpн(¼v$Ívdæâ†Ô²_ùãG×Ëá[Y¶âªw¬ìØ4Æ+·ƒ‹Q€ŸøÁ[ =ýJ¬xjJ”ˆÁÀ¿Dˆdâ´ô7‘ØÿŒ¼!À„4÷Ì ”X ÌZµwÕ!`ñÈsýÎ0&Xb7º4FRBƒ²1”˜˜ÞX¢Ÿ¹ ¦Ú;Ĉ€%ly×¾ µ˜¸ê—³)w\¸ôíŒÌ;Ø.&ÒŠŸ-zaÑ´<j£„tq°%Ptp6y«ç>Qœ)ðΑ‰7ó 4¯onüœËJ"’}&¸ p†nn¸åˆw•ã HÅ\È?xGå"ñ.§Ÿi_ÅêQ Ç ©“äR J!RÂù>µ/·& ©5y§óc”ëK›²öm¬åà<„eZSÔ¢ªµ¢(xLL§óß•%‘úË·(%Ƙ,¨¹§0-p31p¤]“·ô[eŒ‹e¹z3|WñIFs´ÃÍ[$VÅâPaöT,J¼“f<“Ó”FŠw÷­ddÇã¾ô¾zÙû|ÉN“¹9)5'Æyf©S 5'ã ’~ׯÂÑZœÉÆê=ËÌó)ÎbÖÙ£Àc?F {ì)g¼A¾ÓŠ]\鱯Û}+*I*zÔŠnMüžJjZÖéüËUM=Œpv“¢Ç[ª_õ!èš̹.qN\cśД€Û€DDŒ›bÝ#ese4[Qg"gDQJÑ/õæOµõNLÂÄxÁ–%·›®·V¦Ó„„Ã¥;VHuð)ê„ÆÁÿ²ò餔ô’JI/¯”dS)4Ÿ´¨£hQqþ’Џ³Zk2Í™i*ëF©‘š‹ƒ„©Úk¸ÛŠýKÊÅW} ‡Š¼eš.ï@tKI»rq:[ŽGïüåcM-G«©.µ²€º? e©eו'®-pâB¼dZaìV›$æý™'g†½æjxf÷Õ]AÃØkã+j‰DLufY‚ö™¿dԢΚËÄæâðLq­ˆ³õhjý­¢Âtoø¢Üú´Ü}Q¾xò¨âßÏ郛 ö x”QGfÔ‘½òéC}û-ôœèØÖ2%O¡¥\.à{¥5Ü,‚¾§'Yìî}iƒ4'ÛÿŒ*ú³ŽïøÌ0ÊÉ—ä¡Ü¼;¡x“j¥Ø¡÷{ËãáÒ[ø2‰(7‚¯þl2ÇÓwoP–ƒaƒl§:2ÄŲ‘ÃÍxÃé ™`· ð©±EÀgÑEj§~ ÈŽëvô¸æÁm3Ô6²™,ÝhÛô´<íM%¢©Û79ZÎÎûÖMŽ*К­;ï/g“£Fˈ´f›&G8G÷ClçÔ÷'÷-²¾¢ÌûÞž•Š2Qt)DžÂ‰¢ë!jQtQD½TɲM·«X¡ènU­hݪª]ÇmÿY­hg¿9ùdœ¸F›j&Í­æóáÂ×1ÑN½ÑÐ2Ð äUDCÏãʧkr:5[9UËdÿz-çìôx,)Å8¦M6%­– pú‚éíh1iÈIJ³lž1TŽÏŠ8šNp>Þ’"PØwÃ�¼Ð¹Æ™ˆo‡ý†9 ï÷ö`õàlÁêA-nÞéÁ>‰rr+yÑÚ][F)bdϾ&ˆ8wDO5ç廫ó÷œ˜“ÙtH>‚ïBF9'œf›ü}D°¬%µõ×¾IëW ©þ±«ºš º1[ÿÀÙ¶Ùô[ªwU™5W¥uSU¿pU‘£?UÐ�*’÷iæbîÌë}šù︺Ogýž\¶‰ 8ƾ|í’.ï)À·þ½SÎuáÄ;ò<´œãe3öCåèûd¶ªzx—P çÝÜÎfNjي– Ç}Sœ/ÊŠ1 "‚8œößy *%ÙF¹Q« Œ´ºí¥ù¨-Ï)<ѧáÇS¦‹IO‡Ç½&]Æ6?ü[ôúå`|Œ{÷d»‹ÖÒëD,žiÙÜÃþp02_5œC¶òˆZ‡ë›Lçútƒ—éÓMvY©³p¤…j*Ä\˜Ä žGKŠHI¥Ë“a5®¸OôÌd||²‘¦îÝhæ£\“Ö劓TÓ6š§±KÚȆ 0[¤/½ì�j¦f„à—¼l4 ´~øywª’bÆ8(ÚìàRf£Q÷^$‘QùR$ö"R'7÷°un ¸/ ÂY¹á¦EcÐÅA¸Gt®üžLOÀAZöŽpÍ- ¢±Ó~ÐiÍD¢hâq‚­$ÉÓy]7VˆÏN|ulzÚw´8 EÙ={ΣÜ"±ÐÂÎDb·ƒDyYGµ“^&ÆA¼÷µÏd¨™ÁêNûŽÉÞg‰!¸ôÂÚ\ ½ù]EˆêRRé\¹ÎÏù¢Üx4î¯)‘ïå\Z‰}~ÖÆq†íÃÉ| ß:q½hic./5÷�oE$€Ã™A{ÇxªîUëÎ"X*飡ìµð¬LŠƒœú0³zÆU“ÅAAJÊ:Wþ{b¯§TUNq¿©…µø^ݦÞô½Òôv×[»åM×[»eßõª6]¯èaÓõ&é×käD`(G¾”ÏZ\ohZ]/{õ׫޽éeÙ­¯»^öé÷ùÍ%_úŒCò¸Èõ]Œbû{}Á7ëÏž‘ÞÜèéËF¸Åú5}ØãñEh:ÿÖG˹Þñ°ª¥¿¹´¶ÐZÐwTƒx”ZÊéÌ#ybNªc3!1ãΕߢj$<¶"b×èŠHÖ6ñ9°?U´ÓÓq-Ø t9Ôð{GƒÞ;Ïâõ—Úyïqnmßö—¸Ñɘ\1Ë"!éA¦mY;,qqØŸÇß { oêîMÊù„ÇNk˱vnFZK;?ÈZlÃjÝa êÒª(p ÷=\Ôšã.’%|’ óΕÏDÇrÓAk210±ÞqÐåËäù}ùÉ'¹üôÙåïò‰»—YmytÚMˆI-õfÒ“…$$Ö½³·•šaiÈzžF‡¤`‚ ë•OR¤dž«'S•”)‰›TÑʹ¤dH)¼ÅÔ5Ò+Ïä{¤¤Nçÿæ°¿Þ‰ i‡Åac5Ðr>n òûý y2kÈD`>)½É0æ;KM2͇±=íóMM†œ´“bÂG£Z8õ^7ŽH ËÞ=ä%ßId(3ŠöÞV%D–>ïÚsS€i.Oì•=8•U†#Ö÷Áý6ÏŽ^Ÿ $zˆbjz‚6Ò»ÎÛê!ø«ÐÐO’ý`t[â åøO±7b;°Bö8()µJõZ¶Û™ûÖ¿@¢¿øc³ÇòÌG:©Àú›É-s¯=\ßœ|k÷°9ûZgÑ6ûªçXŸ}Õl̾êG6f_o-ИhS] ¬“–Ù7Õð}öM5ôŸn°çm³oªqcöMõÀ´I”«�ƒáÑx隢ûdýÅpàSc s7×HºmÖºFұ噾WM3×QÕ4s½iвÀJyLµ/°(-ŽÛX””˜- ,JKƒ¶VʳeE ¼nă†Y{ã0¦qÛq¸Æ@ñOÛȇq\Ä#16Í‘¸9öê½ïŽ"½í8¦‰ ‡º±:A~@€+g›Qªøã1¢£®±1¬»´z_ÏïW·±ç÷«.뾬°¾ÚÈs'ª­™NZ{ÑB=¿UZ‚i,Î.¯´ÿ%Úª8!—”DØ?UÅê‘(ú¾/ü0|æ¢b¡ Äó:Ó™þVUø*«·¼}•ùÛœ–Xouþ؉êé+!3K¬™y‰¤¬/×I‡àtš‚y%—7¯+¬)»É'd[8>6Ö¸rlªQËë@PÕà¢>UƒÚ` ¿ËÞD"2Õ‚‡qRxÔÝ¢C4‘} _ âh1ö!Bðó¡eß×TŠVðA"À8õ_©rh*ǤÃEO"U‰{ègiCÈ0Ó‹aDs 3iO—ÖÛ3U–°Öño.6²GÜYaÞ6´h´¶Œ­°pƒëÇ®«<KIå©¥x‰ÖR|Ò!8­¥¤ç’÷SŠ;ò¬À5Ó2çÔa€>ȼ[g|Ú'4ïËÖíééPÅ¥Øç¶aÚb¶w»§ôóüxIñ1¿d~¤™)`äÌY°gŒ/Ö_:n®©¿ÔJº1c©šú 䢔juTI´ÆËx)·<æB°Ö¢\þ-M–IÞ‡^Ilô0&|à$ Ö!‚ã\òÈ÷û³r Ð Š%H B…'¹“C�¤ó>Gž OÁØuŨD’çÖD¶‘²É kK¾ÂÔ4ާ3DrYü’,h³<¼ ”Ä”8+À¹È—èsš:°˜ñ5„g®î6­Õ·86°Û^±¯6Ï\sÀPØ ²™n'‚5ê!ø­ÑæèÜ,½¼ÑòDΛmSYæ�XÀãL»Z¢ø|¼~×|å¯q>·/¤.~h©1-? €[¤.¢ÞëÊ> ¶Úh Á¿#Ñ£#boÌMç– WDQuœS4~/·^òç–h牟o±ü° Û)7 !P¢Q£¶‘º7‡%ä‘ûüžt'¤~Oj¸×ìI5#Ò`PkÂïGšÐ ;¡û©Ô‹Ï6h‡àµ}X`2Ï“ËOQÿ®¿ª:ÄB«¢ G§òT§ò)žŒfT¼wÇÔÜ×™ÝÔ8’­Êw{Š\n)Ú…1¯žHÜþçW'g±[ñ¶2sÜ;$9…hã>’ž/üJ×Ȇȸ±*×âNû<‡ÓdG¬b…ƇäÓÉœb©ËÈŠ¶î3E[ÿEA ã“Z]Ç0d•#בL`£u¢ö´³ a`Òo� 0~°±:”d0qØé<ß\<Æq= *ñå´ñn›ÔOïcÀ˹Úc#™ ò_mÁM“€Qì­Bìº O}”è6Ɉ'q$ßT‚; 6Œ$h³6óÖm#X7 &Ü—œ¾Št]_õ¶áI�uÊÝÑI‚`/ 4¡qtªÊAc {{óûey<]õK‡µ‘†2G…øÏž<} 0ƂƑ}ÁÒ—w?z³¯~9Q/)­FÃé`Î$=¦•çÄòJ„€¥À¿e¶ÄYù`<Ó»üöÈv:<ç¢ëECêtå›jâ¿XNº‡ ÝFér·ݳ剜ŽXAé7-šõ!œºãdöDàP²‹(çšÇ®ÄˆG)*QÆÕ òˆá}¿¾Øæ‡ñI°ùB¿7|{@¿ãÆÆ+I Dãë£wdŸô»¬ß×’„ ÿðÑkF I,RÑkI F®¾È‘$€‘k¯q$ ×òúë@ÕA�ÂÀW^Š$ ŒDœ ·»ˆí%€“üÑkvDʉDÑIbxj± Èt>ì½´jHx'þš'<PŒPn¼ÖMs"EBºõš¿ÓLl³ö ê¢$B¹£í[zº—�è;V.!“„[¬LGÍ-úqÔB¨·EK–B¾ u;bhK@§8ª±%°Z9òÕ­£ÇBÿí×£ñðtðähÔBìý%QÆ µ)Î>0î·°Q "è=½»¤Nçç¯ÅÀòÚ«üÝ$”ë£.™ÿ`(Ý-¯èÏXþÓŸ:º^ÓŸŠ<¢?uêП©POéÏܽ ¼®U.“˜4€Yäj½ŽðÍí òìÉ“)±Ž–Ü%ÚSxlK AùütvT“ H?%sj’£»ÍJO7þÓñ€þZMºaše™!IÀﮉC‚’îc2î/©h#Tu$QðÅ}Óˆ8ù%&>ES Œ¦/4ø²‹±ï¿þòOWÃz8&ó‹1ÉùäN§üüá+yËHza “!MONýýÙŠF6~j=‹á°ZÎ�!@LjßXΖ½SŸ¬/¸áO\Ë$!û"[H-}Œ;IBÆ›P\ž$ÑÞ›$‰!ŸÕ>´¬«Ÿ øúKd!`§"I2dÉ%Ëû±—å·ÿ~[I‡¨¤*"cïßîJ­¥„]·¡mI¦Ö¬%E]žå@ú|¸Tlþn’`$¤ÁÞ(©ÏA Gãéð!ûœ$ Á`ˆÁëìÆÓMŽk²xþƒÅlBærÊŸ+PÛHRÜ  %ôˆ Œ®Ä—/_bg‰ÃÙfq;c¹s»MCízµ¢%ˆxË$ƒä™J®+’'Õ~ÅÖH¾µ¡¡ÛÓòêS©2HŸ¥R.\áo) g¹hP—0œôlˆTÈ™«~uAó˜ßùúŸ÷ù0€ÊÏ!kβ:XÇöhXõã#²ÔÂÒŠŽ/jëšÇ±=<Å”œ`5$±óÅȧü¹¢¢´ª“œE¿XU'ì@­ ÖeI®-ÑEыټÁ‚æªv]kS;w‡]k?ž—C/ ‹!ZÌ‹ º?.LÖÇ.ÁŸT¯¨JùØÑÑòB»I×ãTq‘êÕ¹83NyØ›÷úãå;´Í<íƒÏ µÚ¯ú:€ŠuÉÐH‘k²Œ—ü5¥“:Ò@ÕQ˜¼‘¾ßM)ì¤t£é2xÈJ%]FPŠè2 t2‚žTOð`Ú>GŽTÚÎ`�RÌ—væJ)j"Ž\WȨÁ‡®¤&4i�qC—ñZؾî¦!ÄUìPŸB¬óQóBˆÆ–OøH(aë Ñ€Ð6 ”á³Á‡ŠÑŽ0WF¦­Œ¨-1eŒ]KðÙ[-„SƒfÐäM¾-Ød¢:qœšX¹¢ÐlrQ…8öMMj¹b×`æ¹ !¾añ)†²t“ Bö(P®8È7¹¨B¿¦‘±\±ž¿²ckŒ |Ä=@+†83-\Teé£TÙ’ naC?Ê-[ää§ ÉÕéc;a$i¾ÁCõÅ>6Ê”ÁÕCö8¶L‘“_²ÕÅ<fÉMw“ õAð8·\EºÉ… !z(—nƒ‹?dkÄùKš°ì‰Û_ã¢*HŸÄ–­0-lTgù“TÙtÓlòÙ&W)pR·¾Î†ZÑ™®S·µ¾ÎGÕbÖNyÖëÍvðá«z¶RÌÝiª³‰Ý{o2Q•˜¸Ó4µ\õà­¹P!*žv ,׳Ùôl¼X®zk¾xê&ð ÁíHÌçiflŽz8·ç ¡0·§™ú'~Ä»‘cÍSažO3õT&¬‡ù– -Ïr›%kiùº÷BPæê½Œ ZÚîû1i®~Ìnãmc'‰%¤ylù³¶fû¾ ±Aš«o³Ï—›ü¾—C˜æêåìóæmì­-ËŸ¶´¶áù&¤…z>û6ºÉßð ÒB} ?•ÞÅOaÒO‹ÔfH[ÚÛô‹Ò"·Š–{AA¨‡4‰ii¯ç+3ÄY`,{ÚÒ\Ïkf² ¶ìEKk=ÿ™!vÈõŸ&5-mõ=i†H" rËŸ´4Õ÷©BŠ, ,ÞÒXß»f-²P½+™fKk~6CŒ‘…±Í´´·áq3Y˜Ú yK‹¾7CБ…ê{MÞæºš^8Cô‘™Àæhó]Mœ!ÉŒ±9Ú|—ç™3Ä$™QÏlŠ6Çåûè ÑIfRËßæ¶|o!PÉŒõÖº©Nån™dˆQ2ŽQðY÷×]ä*‹¢ !J©C¶»íĤîÎr¡-‘]`éîûçòDËñ#u@vþsùB¢eÄ‘:HÞó‚Õò@h M¨„0µBÓðQÄ%™KhIh%†I[ˆ+Q R#+.™å¼±šXdR+¯tºe‚ıšUdÄi¼ZS!B’,Q£(_ñj]…I²ÄX.q¯*D8’%±e·ðª¡B„"Y¢FÅâ ^5Uˆ8$KrË“X¡k"ÉR+qœ[‰="øÈR+nb¬¸¾ {d6öˆ’ÄÊÛP!B̆Q’[=cƒfÜ÷—?w£´·’Ü7h%G,õ@ÝdáÕ^ØÛyxEw7Êêñ(<Í "C,”¥DY=[¸ið"ʲÀ²gév»ƒ2 ƒ€¶¬µÉ 2Ä?Y¦@”ÇÁ6V’‘O–Ù1˜gëMô† !OÆ!x‹`­^°!ÔÉrü£"J·±’ˆs²ÜXÞ4߯K" ÆÉ8Æ!^ŠÖšæâ›,×ù>"³•B myn™Óx+3¤@ëŠÀ2k­«ƒ‚ AMV»Kdò-œ$♬ˆÝ†R°…•êG$“I$:°’šÕÃd…NìäP³ò °¼Iºw¿›#|É%|o¾Ö0òÏ»äÎåqd‚­¼_sæÔ2'f+3¤ÈÀœ[æ|­u ?GÌ’‡:}ÇñºãhNö9"–<4–{݇4'úáJ.áJ*ˆ* no’Ϩä¡z·8Yw*þŸ#FÉ-\^œ¬»rÏä8Ïâù8¯éMñŒ¾ÓHôç¦1KntzS7½7Ü’åE³Nòqê&ùÚyXF4Úî£ÄYÐtéõl•#DÉí^JœEÍVxsVŽX%ì ÌÒf“ÜÌ•#\É#;ó Ùžzþʱ䑆�q5ãÍb9—<²6š§ÍÖøsYŽ�&4ˆs´(qLÛ,\PЦxD4yl,¯ 6¸&cËè„ Å#¸Éc»SÔa†âáäqn9“f“jÅ#ÈÉm“y³=žâçä6α,¯Úp'OÜFxÒlMCñ{rö$ºùølxþJŸ!äÉyëÅDwÝsüÜ~ªú©¼7Éöä²ñ’Wœ7¸ ¾ª›#ðÉøäoÈöÞÀzÝz|9ÄGP‡Í,Ά8‰î>:é¾:< ˜ZuëÑUM,µŒ”œ¥ëɶ&´�aFŽ‚&ø½7ÀoÔýdžlu&Dyfõ;Àç°¶^„¹lšnt²Þç#Û1CÎ1ƒIï&‰øÓãÓá(29‚†<×½¡D÷!_µó"hÈsk3‰;xàŸfçrL†‹‡öàâ;ÚÎ%ä%ЀKt;’Xºsî±Aα9Ï$uǯêCï!AÎ!A;3xbOÀs„9‡ Õlê³ {ž#È9ˆ“ØŠ{PŸç˜ûsžû#2 Ý€$žGÞAyŽY?çY?Šï&¹;:𵣪¤ÞƼ_`Þ/0«Ã•'º©ëµ&AÓ>ñGà‰Ÿ<z’»£8§7? ~¬`hèÖ¤]É­gÈ‘Ovhºþ þ=ÑmJYÖ­±cª.0U˜†áæ“Â-œz=þü øq@‹ãáÀÿZ×^† rdÀA9ý4p®ÀS|ƒæáÏ šIG^¡Á÷§AôöDù´°�©î\Ú›Yµ°ëÿT·-•½¥0·vùŸê–¥õ�-Ý€¶ˆÔ§¦á16{ólËû­mé̶Ed›ª»•ÊÞÖ˜s‹È¶Uw+•¿µ 0ó‘mmä" _ó/‡Ç8/0õvêM#K8[FÌ»…wÓØNÛŽͳón×Á„Ó³ãDÃìÄ›Æ.˜°v|hvÓØé;Ý:F4ÆÎ»©nD6,Ûrbâ-ìÄ›&î ÒÓ§cE{ìÌ›&.Žh׿Ý4uD‹)ÙE -š£ä:XhÑNšmŒ°©%JuqA‹n(9·¢µiänª[¯¶èb Çãj9\Èå ²8„E¢Ë²Twû¾š.6øÐ“‰=p×m¾/gÓñr¶xŒ; Ä‚>´÷3RÝÙ³,oÇKpÝÍúzí¤@(R¤ìfAÐŒWl#¦v& ¼_Ã�LjÙ0%«7ñ^,ðaj ëæá‚þî÷N‰-CÌ‚ÛóacŒnÍ‚–fj­™·Ã'á’ãCôR`ÿƒ&âK7ÃÇKÑÕÝ̸ɾqŠ BãywÄP…ºÓ'÷Ö9Ñú̶^÷ûê{'CܾD€SØs Ì¸‰ÿÑxAA ŒÿÁj4BŸ"¼)lx“En\*'sáû}�Y+߈o ~7´ðº[œ"וK¦»€.ȲW¸ „:‡:dOYì.þ™Üs*»‡E.òRˆÀª›ØwV{HÍ[G!•\Ce'÷å’�Ä_ù~òèOÂ2|B–»¿8®PEاßî×|1›Ë1ÅQŸ¹+c}èùÕlµè™[ºaʃéÀ_á®eâ _ÓPhÒ-’ÅxúŰ7¯É bÑ{Û$‹úHÀñèÝ—€áç#}Å‹Ëó‡¿î6ëÙhNö·$‹AŒWØíÝòpEhWvãÓ(Š�®–ˆèŠÂmwʳ+¾ªÄWE>(÷ÚÄkIÂ×ÃÅѬj’ne ÆÒö«wÓ¾MOu&ì]]3óðhu¼<Ô[sº ÁÔ* ýï"|w­—E˜Ô“Î]IÙ_’ÆO´‡qí<f Ì–Zöâ>‹´ÁM%{ð}þ–ø°çsùš@0ÊÏŒ¾rX¨t¹è=ÁSgŒ,¤_�¾Ãî¡”NG/ÖFHdÃ_ˆ^·Â©ä®¼«„3¥PêC”j1®Ž§³Åþ½?;ž’ù øâh¡ïk?]«ÅÈt:/$Í×%$Håk”›r5�,øUkWîbIã¯|Øv¹ð÷«_þá#|¾jöN'.cèÌ%ÃMÚð_·/ù“Éà,À®_0leÍìH$…ë–”/äÜö"‰ È–Šö«G½!®¨£Bï|)—èèÏSߣ¥—ñhë÷(M}’*ø?òõ—ß.‡Sô’¯pª?¨8|7ä¯ðÀÔh…ŠaQ[–ßËRÛd6p€IžK;›@)¸³–$V™/I°¥×3V‰|cÖѱÀ¡ÿf M2‹»ïJÉ93ê¶œ´’›D¸Ö"¸huómv#ücÉ{¦Å Øð¥õÑHûLŒõ‹„ U–˜ðç§Æ|Gc®/Ý<)ž¼|uXî?x‚oÓÒïý'ÿf7ä¿í¿üé“g]#?ìõT%ù龿cP×›¶hÔ–A®´Óù»L¿-É›Læ³ÓñtXÚ¿ŠáHøµù°þÜ ¾"àŒ¦â½'¼˜O7›J”ÏdPÃÅ*ê«>¸%»÷ø9P˜ÞÜ“LAN†v-´ #mG¢ó¢®ã–«‚†ÑRj§ÿ5Ò ‹UøüOTwöñ×wIYª$PœŽîp—è%hÊg…çOƒS¾ÚKù"ù_€·?1)#` ch ¶NkáJ•~±¨Ü6¹ˆ½Dd¿„ºà†€¬v\€Ú¼C:¯1ÿYôÎ!-àè?!¡‡´ »R8iý©Š—i`ÃHˆnŒúõÁ”‚ÒP¸* â˜ì÷ï³Ng¸sÊ 0v@åG)[U×ïmM$_TEP­Q"ÌÈ!0ÒЕ¬]äéH\‘jæüO´å ) ’"a ±ZеzÝCEoZñ+Tî&Tî^윌+Wl]©È˜°ÚU£9 kd¸ÿÜן/hu1„ý¯QЄM/#èKZ8(&åÉö†÷Ÿ>}øüÙ×åC=–¿âÐÄ ¾Î•ÀcÖkÇ_Z?DYðëÅó'Ï¿¤Á†_¯_>yö95ýñÕÃï^>îFÍß僟}½ÿ”|¨¾:8 ü ÿØùrÿg”|ˆ/mׄW_<yØÍ<ÊÃ/ö_vs@Rt ï÷Óç$Gx”ƒ§Ï÷»aè‘=ÿ õ_ùóçOï?ë†"´û)ÒBeöþ ¾ŠßZ¨ûlÿðÉ×Ë/÷_ê†"ïOU'¡H m}þX?Y¾…fDfü-%½ìþÚùO¸hÁ<|ø übÿõäðÉþÓ'Æê?üâåcru$,‰°x¸ÿ𠂆¤&<z ¨Q(õwÙŽ¶Ê ùü[ßÌØjކ'½³ñlQãŒëzEù(«áÑYø=¶kýZI! •Òùqu±î%m¸Î¼kI³ƒjŸJS¼šÛØ]d#r¼÷Ù=÷`a¿gª_Ø›ýú$ãËLAÝôÎ.šëéê}-âä½½?àõi×ÞÖÇ—HÖŸÍßrÚ¥a”"æÅW>/%¾³ñ74Ï¡ð@"<ûŠqÀ1d­mˆegÄOšå Ì÷€ãÿëø\¾á�"ÆoêAtà84Ê«ñ±Ç•ÌÕéü¾—Qú)X[dÞ;ìŒóßZÈŽ„8‰,Ú+ûiÉÏñJ7¡Së÷^žé¹—a`¨üx” 뢤:Ôÿ7Jéqc(*¡…Rì,I½¨˜am`ð¦H51[ÖíMÖ’6÷zjœ7­Ê}bÏZ”EbÒüöQŽqó—³RÖîl¬Ö"G‹ÙÄÒÙîÄ0ùmcIE,KÁâ–¯7²Âúޝ¨H†GǼDÉÀ{#Dε!9ø—Ä„sˆ'NÝyážd'8®Nç#î¡9~¦xþ‰'¸ü ``¾¦¤ÄNçCùîxw{øP:xÁo0o[­Øo`b¢êÐE/1ã1ÿ±+éƒ>·ì%Ëôšé£”²rø¶¿VÈ VÕƒwÏ0¤\/ó,þãðü¡®¢Gx+ûž’v_&†õ¡ÄY‹Ä@|Zñ¬(@Zð‹ÚcÙ ­¬¨ÐÌ\4s Ozñw±÷ H¬ûÌ9O‚^ /¥æÁÞ~ûàö™@£„¤Á«C'‘ŸÁù¿mÅà¤ï·o“qw]­Ö„tU“@Ät¼ÖýHÜ—p)À›TÇøŠb¥c’ä>^}ß$Â[I,âkêƒs’šZÈï~UBÊm¨HþN­åÇSZ98m_ÚŸÒ·´ðŸºÒa712I°œ÷r#׬/t "¬·•úÐúR!À4õ*É÷}�÷³ƒ.yt¹•¦pʰ¸êX%ñ»øXG¨ò^S>9æ-w@ãc\}ÍX©ntêùb¼ºa§Fˆ&ß<X4”B‹ Pðÿ/{óùpÐ=àá$Õ¢¥/¥¥®�Ûé̇!nßHø®?ö)wêµ"Êci¢“KÌïP'Éw Ô l—Ç„[™P1�SÓâíCÝÃb®õ¸ó8°O©çnp¸ Gõ)™eçFÜÁ‡ý#Ô#?>sýó*@G^lúCq¹sJ¼P[ © ÓÒïÆdÁÅŽGüV9‡¢}º6á„Êh Ü–4yBK 4žQÂuk0üN”+…ß-£�x(;šÑTxPV&ãÑt[<¹ª±÷ˆ[²f#ÂU;å-0çÁáðØå&}:T¾zÖÅ&—/†¿`ëú”,@G ¬«a·œ5Ù‘ÇΕ‡µö=fy=Éÿ1y—§> FÍ} L Äå‡XíGsr³¥-ô»ËY=ÔÜG^®ywÎÆ_OŽRÌU›Oë`SÕðLG¼ Û—P‚-‹'Œëô6sÂ%#ˆ”ׇâ!Tb±¸[:™6§•kŒÒAδˆö°ªdNbq¯ëz¬ˆ÷¨=ˆlB¤q^0Ež‚:öC u„ïÃÙé€+âR×Ló¼ô{ÖˆÖ¼/d†¸Çó”5ñ@žó“¡rË>õ~Â!99Ëj5-_O{¥(°óΪç‚Éß]ÿêÙW¯?¢ØÛêóª[C¾äqj§Ò$¦ÄÏv—ýÜïÔ{»y×}ì5ùöÃç,Úuɱ&ê"µýþn^>"+9´ßÝÍJóÃzòÆ–K§ó{»³áŒVUz'®;j“Í6Î>yäåP~ø“#•þm±pZ7æ½Eo¢Žú/åÝó©Ü¥×Ð¥¥×§ßÖŽŒë`›ÀX‚ÿѶ³9ÅG"èG§ã#ŽÝì$?TȶòßÚÚpþø“T²U£ñtðJðgµªÑjêUµu�ðÜ^–?)£Ožh-—`~ø>ÌÞ‡ùà}˜Ÿ¼óOÞ‡ù•cÞ1Ž÷ÏGÄþ@•®~wžt±cZäˆ4äàHlë8FرÖó>|ÞGïÁ{ð¼OÞƒ÷'ïÁ[÷ÃߨÆ{,WQ¨ ö]lXL «¢5—aeh·†;ŠÜ¸ ŽþÛz&ô’ Øê_íUõÙ¿³ƒÑwØ¿½ƒ¯öÖ»Tá9ç]•ò-ðÅ»[ÑXLme¬€qÀáø]ZÙ.½Mºt-N‹vxÊÊë]ζKZÛ9~ùóÝ=U¹žZË´«Û*í¶µ,´]úp-Ï®­\‡®eÚÕ»UÝ»M5£«?Åfù4ZZ„a€³¬0÷ÞÈrÏİîØÑòIðj‰74òò¶i¶N«üe6‘h»ZI€ÙHGƒ]BïÔ*–€OUZð÷rŠ‚YŠw¼•Ï}žR¿§Ûgaúiºbãœ]ƒåâ·½Rt}.Ù:ïR?¥ùevþ‡*è¦ Mq[EC™t§+ówWšníÛGâ±$lÚê1`´âzŽêåÖ\e¯ßòª1ÓÇcÀ›;N¦mŸ<«k¬[#?@Në××kWçš "l-㸵 γ«áëõ}€Scµ¡]}'YP&ñŠwwðï¿x·ΪÕD¹¯ÿäÙ“òùôéL¶!BÝ(|)…·ÔÜGœyeèvømäÕ»j9œèúù*®bIP(Ûƒv™ˆK…_MO¹Jnî§‹áˆM5®¿]Ü— Ó­våîEj)vTOëÕGùâg„E>/=ëµÉ Ku|ƇO´nÕ›­Fß_‰QëÞ(7éZS&„õÖW SvâvÁ�¾ÝøucQçS·ü‰Û÷Vâ}ol’n­íšÏ¥;Ü{…ëÎÆ¦¸ìGz›sYÎø›êsÙ-èËy /+D›Ð!u«\°Ð½h»¹ãö +UþMËí?èŸ-ùïhM Ù´Ú{±.öbSlÙ›Ø:é`b‰fïHˆž¶g"0§¬ã‘ÉeïLÆìž¶/ñ¼bôÈOŠÚ’—ƒÅŽvÍ3²ÕH‘[v+XÈkîËç¯pœøÚÙé€ÜÑqýñäWï·™~´ªšä´^ÉÔû‘uÊÝW¼Ñk]¦ßc¯dã›­S7,^±^g’ì- E˜t̽ҵ4H¼ˆÂw™à.{)sŠÞ-WÚ÷šÜÍD)›ƒÁÖ²½”["+„½>¼PÐä_O–JT$”Ošü$‘^·ÉÛ¥o&Šô§µJ賂 :¶ â'I'ht*¤O›Ü4 $Šó›òÓMØt#¤ÛNý-dè²A¾S+³A¿eõÖRTÐR·¶AÿŽ×æF‚4ÅÆ6~St—Ô¢Þbó»ÏQ¼““I¼)Í´=Bü~ƒørH‚ §ý¦y6ª}ù`SvŠý{‡/ º·áݨ‚N¿)Ú¿|ò¨äÁÊ«ÝÙd>›âN¥äÅNÿ'àx®XÊ3×û.I(vI]iÒ§-IFÓ¾ï¥-gõã:Iýž— ŸWyÅþÅ“ Ø—<ozÂþ¨™þûzCÕ…-ã&x`Cò)\!ÞQêÓo±40OŸú]Öšõ ~ ŽÖn®Nj£p6[ŸzT1ZŸÌÊ+.å»N³©¯ìzÄÉ Mûa[Ús9–ör×s3÷zZ#÷÷œ‚Z²ÿõÖÄFþOTñ-¹³%iSòÚlJ¾ž¶)¹ç46%ßHläÿ~ÝO-ü¨=µQÛ…ŽV¿«YvÏñÂzUáÉî–µîpg3qZŸ·Jâoy‰-'QÂô7Z‹ß`ûMªâ1àÀ—ñ[¦x+ –Êðƒ&êên7Îêd>amºMá]XMú]¶Ùug[z‡ÄÊøã­Œf×3øÿ`+ãQ˜ï¶3o{Šò½ëßT~ê¹E¥ÿè‡ÊûО—ov;+Ú÷ÿzŽ¢Éß9ð<Ô™Îg–ÖŸùÔÛÎ+úäOnÑOâ ¬ݨ€½ñFâ}òƒÚ5nÐí@š¥ÇˆþéAÃJ&ζ(íûÍ´¥g¹¸eâÉ 56ªûW'Îk;Á Q»»åñŠWX5}ÚÄ£²&}oàѾG¬ãчëxôf>ZÇ£Ç' ò&=�âùt¶žë>,„âãÑBZãmV‡Go„²GÄwÓ†GKÂ}b© <úT¨kxô ioÖðèÑÞ¬ãÑ3¢½ÙÄ£„¼G ½GO-Äãf€Õï£_ƒ­oÅ£rÁ£Wzàѧ>¨ñèC‡Gä>Èj<z{Ù]aì>iÁ£Hð-xôÁÿ¿ðèI÷¸‰ß†GLýèWƒGÔûd7}ZãÑ[õ+}`ß>4ðè©cÚðèñ†§ž1ñS>ÎP;ŒÀåÃËàÑ“NðˆÀòoþðÞûáч¿V<zEÊAemxôI~¢T´áÑ'" 5y=àÒHwáÑÙ"MŠc=äN3ElÅ£O!yª’oÇ£G²P±Õ¶âÑCú,RÂ&=ÎÑà&=äÌT¿Ûñèq©Øõü6z=„ÍÃÝxôA+=G»ñèÑ’\[²ÍÉUííxôéV<zÜ|¢ý.<ú<ß‚G–ÚM­xôén<ú¸j)´_[ðè¡‘"Ñä<z¨£PulâÑÊþ3€ÖKú<ú�Xò:‚Úñè 8"i{;} ŽD×<úl*î<zÀÀ�¼^øvàÑCô0´|ÛñèÑ€Ð6`=Ú&ʸ -á×•ÌØŽGç•)¿®¤É{;=aBåÚŠG&˜ÈrmÁ£‡ø&8€íxôÝdʵ Þ@xSX®mxô> õ•év<zHEʶòG‰ekÇ£‡ô‘0¶àÑã•!ë…i=dCË´’Ǭ÷l=˵ ¢Ç™rmã!{\®Ä<zHŸ„–m+=äO"eÛŽG$‰ ïí£Gxºf¾­xô˜µS™µ£íxôhB^„G¤ÑExôh�¦íKãÑ£%n¿ =æó4-lŽËàÑ£™ú§ËáÑ£±™zªKâÑ£åYb³\ mÏÔ{]ŒGà ÍÔ]ÍÎCË1=«o»=œ«—» =Z›g–ÿB<z„ �±þKàÑ£¹…úÀKáÑ£½Ed3\-.›á"<z´·Py!=pî?K‹Â²_ˆG�0>´ìâѰ«ÿ¼ } þÄò_ŒGŸ‚?³üâÑ#´Èõ®—£G{ÃÐf¸=ZF6Ã%ðèÑäP}ïåðèÑè0³9.GP$ ›ãB<z4Ú„—Ç£G›Mty<z4ÙXo݆GrŒ‚á´!JfÔ!oÇ£G["»ÀjÇ£‡ø‘: -xô8R§³ BG ¹ÑŠG¸$³qÉ<zˆ+Q À!Úñè!o¬&¶ÇjV[ñè!2B’xôI�]¿ b'áN<zHD»ñè!t’죇ĉ•¸ÁpëwâÑC^{lÅ£‡Ä6ôØ…G_ŒG_>½žVt—À£‡S.G¦™e¿aPÆaÇïÄ£‡’3 .£‡®3;/À£G%äI/£Gã2ü/À£Gœ“e…åÝG¦qŒÌè ðèÑ´\çû ñèѶ<±ÌàÑ£uyf™wàÑ#¨ÉòÂîíÄ£GÓŠÐm(í£GË8’aÖxôhW¡ûExôhV‘YÞ]xô€¸ÿ,“ð%»žáåt.¿Þ€9²ÌàÑÇ`N,óxô)˜uú¾K–û"<z4Q•è"<z40Œ.‡Gö…ÉåðèѼ0»>½�1Kêô~=šmt’߉GFÛ}”xôh±ÝKÙGö;·ãÑ#\É‚»ðèјHC€ÝxôhMdmt7=Úi8p=ÙÜGˆ�öãÑ£Qqx <z´)¶;Å;ñèѤ8¹í±AÎ.<zÄ9¹svãÑ£5‰Û߉Göذ§ ­à­ªo;=Ú /ñV<z>@®¹þ3ÀÓ¿õîÄ£‡t6ÄÙ‚GS+à.<út'=Z€0ØöŸžþ2xôhGfõ¼ mÑx;=ZÁ1Põ/À£‡¨™î ]€G �öàÑ£.=oÁ£G+8J0É6<z´c`êoÁ£‡ì0ò} =BœC�ß·ãÑC\�|¿òòÜàûmxô˜g}`ß_ˆGÙ1ïç|pÁøò;ðè×ñëÉ£_€GßįÇи�~ À>ÜG¿_O–}}¿ÇÃàѯØ› ñè×ì£KâÑ[ûKâÑC±vý <z4Ã.ÿ/ƒGŸ‚?»$=æY@Ø_MlS/G¶F¶­—Á£Gk#ÛÚ ðèÑR;õî£Ǽ[Øyw'=šgçÝÝxôh˜xwáÑ£=vÚ݉GÆØyw'=&ÞÂN¼àÑ£=væ½>݉Gå»ñèƒ ðèÍN<úx7}ºžFõxôÁ<zôd¢Ë²]xôèÉĸ·ãÑ£íýŒv<ú´ ¡H‘h°»iwavâÑ£E6L¹ =Z†˜å=ðèÑÒ4»>eü¯‚'‘ ñèÍ.<z4^N‡ò ðèÑú̶~=šlÏvãÑ£¥6¼¹žáÎß\ MÊuå²íáP'·áÑÇkxôqq)<úä=ðèÓ½½ÁåãÑó€Zñèƒ|=¥´âÑ×÷›xôAÞŠGämxô8rÙÀ£OóM<zÄx…ÝîiâÑ£‹ »ñÙÄ£G·n»³ ãx }}ûh¾¾yäð胼 >È[ðèƒ|7}¼G=¹>y/<útrt:ÔÄ£âý{ÁÑßiÂÑŸÊèt~¾F|‹ÞZ`Eï®zXôÜù(ú pPôŸ´@ч$M± ‰(ÇAÔ@¢wRµ"Ñ'#Ñ'¿R$ú˜‘èuÎÚ‰DÏSÁúÕÂVÖx‰>`”´À^!¹}º ‰>XG¢~½HôáEHôήü^nC¢w:¤XÜV#¿ÐŽDoA¢gPCþDf­É $z~ÎÍ_ñ®}Ü=-ešHô¾Hôa+=`\ñøTýEé¹öC²¯ÒØjàž5óÕäh¸(g# Â{Ç|~Ú[Žf‹I9Žz«Óe9tïE¡ÞéUët½ÔËU»j?“©oCдŖÖe€}÷¦þ>Jƒ§ÿs }\ý¨ W?ÞÄÕO~}¸ú€_5É…¸úÙ¯W?ÿ&¸úFè]ßäÛpõõ lTÅÕ7ß WÐçÀ hâêÇ›¸úÀFŽRW?½WŸ¡&]?\ÇÕÏ9\ý¨`Ð{W?nâêGßW*"Ê®>îG \ýx®>{\?màêó$`× ¿®>ТiaêÃÕÇkõn‡«O/WÏFh—´¿,®>ÔEK¾_« <÷ÚÓù÷ÂÕO/ÆÕXw’:\ýü¯põÿ¿ÇÕç¯üªqõƒ_WŸ¿@›&®~Ò†«Ÿ´ãêgÛqõ³-¸úF>ü¹Ž«]Œ«o6põ“\ýäR¸úúêàb\}|¼% Úqõ“_®>æ{ W5põl?¿�W?-ö³dW¡Z®ãêßè�ŸËnði=<wôNœ[@a~}ø2áoq~¡À' Âm >飓šOQ×_Ì®ÁŸ÷œqX›ÆŒ;Fö,ø3ïÇ(²;%rÙ¡›sm$žÑˆê,¸ßq²¬aBäWÒé\­]p1@EÚ½ Æ>ü¸Ç]icAÌÕ9�ØcÕËL|6á&CÖ8ë:¨UñRTqG@W=Áøõ�W“MÀÕhpñ Á{gšrW¿ä/­xЫhÄõ½Wá©Ñ„>TŠÎXÔ`À œ%ëðÁúô"‡5~ÀØSb·Ù5lTò›¯§cÛþ»êñ²{Àeü¨•”ŸõF ½“¥‚¶½ªÃxÍ.¼×of]«c{ù`Ö×.°ÿÕ~-ë#Õg{©Æ‚[âù–dÁ#†^@ßÏ׎åy2ZkŽdþçv]C8Ï¿Cmù9žù2Åð;è¿í¯"·°õ¾Ç�Á bÆú5ò9UG>Ž Õ`ÌÞ/ìÿ¸öS�Ib_÷Bþ‚þ·.à§E*–¢ì_ô-œ´ácÑaÚˆñ)¿t„?w>-iY¨Ñ½ÿɬ­ßË¢¥^oA 9ú? 'š4ÝÿîËBÕxa¿‹úo5£~Z¹zÞˆøiAF«1íG!WÔêãüÍ~˜G×öƒÒ›!¾áÿ‡>-µ°Ð²As6*Ú0GQ{¸ìGËXÙoPoFÊ^ üÛ#zZzÅÁ¯C0|È:Þú /O°«d9F-ÂÄ€~‡)bF �!2LHÆà:Ò˜ ©à\[¹B2†5¸¾ªKÉy@3E‹‘‘qcåÊ¡¥Ñ=¬˜Qò¤÷–IážD3“%™½$&²;¦DìN>^y$õ¥·ˆTâ°Fä¢Õ wÛQE¸$å&Öd‘)Ô8²Š™Kso¯Ö ,‡;«µÒSÁtðèR<Å·PÀwVkåS Êj¸ úÈ–žJ¿Ü²D-;Þ‹cª%'{On­š%¤,ßíÕZÙBªš¬eä{qᕬ²Ü5³)œCŸcê£Á˜×xÜeüñÕŸ¯f†Ž`1œàoÙCì€ Ï!GÄRs7:ZÌR|\–€oÉ2™o•åÊ#¦º1&œ*–tàMåTbÎj¿Í†è‘ éìÕ=„]ªr¶š‡,ÄwVët#r|C~J¯}Äã€~ÊXúˆë Ÿ2’®rnú©ãèêªÔÜv•v`å9S®1…K,ظn(<…bˆ0 Õ¡Ž´Ò ¾Â0IJFÝE$F÷±R˜)ÖñxÞçO€$ß…¼:¢0‡§¬†ø­™ôäêr<‘,‚zrüEH ˆ¦¯¯Üdܪ+ÆóR¨€¡Éî®6 Šî…^¯\ÈNèõÓáÙðTw¤²Ao0ÐÊÒ=@ ÌÑFˆ¡MÚ¹˜z¿ÎIË\õAPI}6“Ð…ì<gCžia!Ÿ“=ÔLË¢Øããj_Š\ e?„B.„~ÛBR-„H¶Œ ¹Fj› Añd i›Mu;”BñS–5%t<Z²Q—º¬)"0mI1ëãZY’…Y…MÈØì;J !²›i¤gD³WÝï\ †~Ûr 1˜ës§èHâëX4òoÒqÁòM†Êa¤™¢íŠ"éD¦iá‘}}JÑì¡$¨3A0ËÕ4™Ú@™Ž-!W !Üì­–'ÓÕ„‰üYRúD¹°Ù‰}ò«sK0Ü ú;âFü€æô#!Ðrž?'Ê¡PÊòeBƒO’e°}¬Ð9BËttYÙb5Š£Ó7ÖÚh¦eeÉškA»)lªÁD,ã–2Zª/*¬ªØD IJZj,ÔQåUŸð7SGãSGM…zíÜŽpn?Ò†Ð4‹ojݤx…‰ƒJˆ4ܪÔTlü&»ÍŽÙfVããjˆCšY«œÂ¤±ÉûPè4öƒo„ô. íQ¶N'šªû.Ø_¹Da©dTa3…&Ü*2UHénÈ›(F~œ4ë©Ï '–ʨþýQÅ£5 ¾™~Ö;톙‹œÉZr™dymS©h&öNç(,eçeKþ¸ÎÏÕÓ•C§Ê9špC,ôïÌ—| Zö–e…4ë’Ajéè‘:-CÚ÷lÚdµ¾­s$~ß&.ÎÑsujÔØÔ£Þb1.\2MÉð;6™I2Å9 ­çÈeÖø[ )$…:×NS–“ey>[ ºa^>sŸ÷Ìâ{ãü&J~êÉg”1ù¿3›ßufHþ(a+pÌ€ÇùɇØ I€ÙT (ê…P&ä °o ¨æã©Ii]@…DXM–”`Ëp]šHr'¦óÇkú”¨oi'ƒ,Ë£…-!³%¨j“šý%sÄÍä%Ñ¥N†P…HÅ2oá“ZCgB7:O„:Ä^P)–ktÆ¿)I–1ñSŸ_Ǫ Äûü^ùóUoÐ=4A¢£Ä©7JB7J®2#eÌö ౯Ml52MÝ:›èŽ$ qY7À&žÀ„âzîXFKuú~㊤° ‡s}Ó(“"ƒaØd¸àÂó©%%JrØ„,Þ˜[ŸnB ½®£°·BÊyÊ!‰G:$ …ð«Tã|ai$€FE¦\â:9w4c ;q$™s¯žÍ§ò;–)na'2sÏuØcãôêü¤·TZ¦Û?CuZƈï¿Þ›[–B'×ù@)‘(þê¼Òß”÷ñ¼:ÄøCéüm7 íæÙ¤œÌ«Jˆ±öÖjjiÜXÕ”T&dzɔBGÕù÷Õ@¤áî«‚Hœ7iÍu… Ð É0)í@2±9½Ép$#)gÿh.ÕÅ‘ÆÎ‹á±t¡kÎ&>šv_-Äòu_F¿V…7Ϲ<TNºg°úÚï ªy}Dþ-Í囹îõh¹qíé’ûFJMB.öf]•[P¹h<R"Cƒ—Û’©‘¢“Ð+‰uÑ·ýR–†"ƒÿå—/q.¥Ç\T|´7|Ðì““éw¿/–¸áØå³®; ^*Ûbd9+òtÿÁã§®”„Kù’Ö¬Á݃Å�¦TÆ“O>èš»/¿ÜúôùÃnt÷Å—å÷_tã»/÷_ÉßÉÝÇËÃ}¦wŸ</_½Øø¸›Ýýâ«Ï—/ö?ÜÍ!Îwšµ’<Ô õÄ?ÏOûGå¤7/mgS„d(,"w?Y”èº.­ÏÄ›·FAÕ5X–Ë7sü¦8sxÊ”(Š˜26LqŸæº‰;Ëx‘Hî³&)<7Êp5wezüÎÙät~ì¹5ŠÈ ¶ï#®’»\¾°Ðd7ïý|5ìšÔ¸¹.¾›ypèc3Q9ÑžÁw£¯ßÊøMcõA½òt\1%‘56˜Õ7b§ƒ?ÕÀk,oMšéúVV+BË•í«é˜¿D$õ37¿:<ˆŒù°ŠW}%À»Ã?­¦Ódêj®ò†*ýŽlÁ5Y,sG4?yWõtÐQ`ep=×d$]“¹QeÃÝ;—LÈBã)“�&ì&&—µ³l#à·[ì«0¹[9ŸSœÎ¢`g‚äÿ›­Rwå— o¿‡8ìi‘ºùõˆâý¡$g°Ú¹w|Øxh•Ü•''–r¬,²å ÊÛ®©C«¶Çg¢ÕŒ¤‚bk²•]¸ˆ$äø¸€K¸6ÒY‡‚/ƒo$üÁH?Åwh(À¢Ðˆª/"¦‡²C­1ÑÍ‘göz|Œ2GÃëBøNäè¤d]QD®€D ß¤›úârç9MA4:Jn³0EDv3=7Ç/ÓÍìõ)üŒðS”…Ÿ1~º›ƒ·]"!¹~,‚ÐtCófU/åL¡Ëò[°»ça§&âm²²IuÏRÐ襲ÈîÙœŽgGS¡è¸€TËÒÎ>Q ;Mw†ïp¾¸ìŸ„G’ìaö¨éFé)oZÖôXéïÖô\é9‡†·]¨… F\»£P7¡2ÄuËÁp Ç6EtQhøL6ååN§à©ÚIµ'?#Ü‚ôξ®~õäÙaŽBbû}eË~ô5¤c7/BT‡m¯WŽÉî;8°Å…²}ÉBÐ-pÀ­EÆn£¿r] ßà+%œË(ŒðïËv-i‘ó^­u<Ø«u0Ntg7¢ /¹îg§.sâ2Ç^æ¸Î|ýÔåN)7)áǧuöÌmß‹¼âúº%Ï u9Aj2ØêÓ¡"hüçË3Z ô»…º+ú‰m‚®†© ‘æ!-F8ƒ²¹â¤™‹~»L±f"mR&Ld©Í•Џ^.×í{Èõ‹ÙtˆLØzXþ¢œŒ§´­Î‡U­h"ª%˜úþÃ1ïdIû"^²ñ5¥Ð¨ÉRõØiƒâÔ•¼Á@Yi•›“{ ªòh¼ÄÁ‘[æÒš)“¹7ÅËWÙ:ŒËˆ"Þß{f5Wsj–µêyŠß~§œ§ÇÜÃoÍÖèžÌå“H1ŠŠÖ.š®wQî2Ê&GíÝ4]ï¦?=×ÂÆá†®£|]×Q^ëúW‚Š›Í26ú+òúë±W†mîCšF±Ý¢© ‰™`÷m>G)G½jhOî#Z!Ó²W–DGåÉøø¤žÈ‰p:;Ço§•O›ñ¸£…E„ã¦ë‹ÓñD(ºœK@su0Nï¯$½ÿʤ÷–)®Ón€,Á|‹cˤ$ž¸Šé—•o”åïÚ¬NÜž$æRÛ„{µ.w»àh\iIàºãÊ—‹U¸fÊgðph±*W<Bݰ#Jå(’HTТª0ôHžhcG’Sy–$G» UJÒC]6žò¡|lt¹‚×––Û ¦Õyo.ÃÏU0=ÂÊ’‰‰­ï -1w•TÇÕt�š>èڢƴ¸®„7$YD½VêY¿:I¯Ó4v4û’çÊsQ'÷*î­ú ‚u…FÁ¦BÃM…†› 7¶(4lQh¸©Ð°M¡a›BÃ…†- [n*4lQhè)ôÏE¡bÙ<Âó÷5ÑúÍB­ÒH¨´¥A[J«, ªÑU«‘hÁÖòÚÑd!ª^#i–hÉK¡:ÍràjRÕF‚è숬[Óº&«\"ç‘­Lµ Za«²ê%"Mdü–¾‚q•2Ž=“ Õ {6:7\ë˜âe_wŽVëNtÌ4§:Ñ1hµæTÇ ÖªS3ÑiNt Z­7«cPkÅY3ÕiNu b­7Õ1kÅYƒ¬ªs:Ÿ4”,Ô\áﯼXú~!ý+¿ðMýBç6e.{ËÙdÜ—™Pö'#"K ™ä|Œ~”Þµ-!`»ÀÛvøß))f&{.s§”¹„„3ÇJ„áb!½8.ùÛ¨÷ìÛ"̱ö®L‰°!Êlÿ…·´îåîÕÕÝG´£žÌVÚ{DÑ@(q/cK°¾W®ÝÇ/Cq:§ËíD-éÉg7#Þ¡J«^I‹S"D©!UQY+÷EÅÁ£ToŸZת¬K‰Ò©Çq_µÔ#“ªÞP¸ €FX1Qœv½þ­¿˜T˪w†È…Iaæ‹!<<D'Š”õæõdŽ›ÔQZì}f¤Àˆï^öߌÆoñ‘Vûdtºœ/Æ]Ý1¹[ªKüÂ+yòTÕoëv[yi/i§ ßöçrG²Uã>òÙ.™TòÓ>UÖ_Ò§úKº ¿”Ÿ:zÞõ§Œ›ÉÏå— ˜%¡Ÿ:VFóJKÖ2šWhFîÉÃPå·Ü›'v—QØ5CUýe·Åfî&¬¹oN‰në9Ƨ›y¬a—ˆrRAî[³Òê@/óÓáìÈ^äŸáŠ^(”ÔjòhÑ›öO˜¨÷ùYÌ·Bѧ¿dͼU ’>|èõO†|Õ•t{¦) gLŠ¿ß«º&.=ŸœÁýã9kæïYqDN™*Fzìïi*�@dž!ÔÙ7gImŠ¡IšVºšô`íÔ·ÛŸiÚŸiØŸiØŸiÚŸiÚŸiØŸiÚŸY·?óÞöb÷íoÝæRßæÌE6gÖmÎlØœi³9³asfÓæL«Íe¾Í™†ÍáypÓæò†Í™M›kXY§Ï&Ã3LF3LN3ŒÉq7bª´—¾ÕȪÚmŠ´âÚ£<¬¯RˆKÕEñŸs%º6Ì Uƒ§sTãŽPlEõŠ_“¤«JNG¤ü¿Ãåë:6©ü¤n†.®!¹¸%µôWþ¨´Ÿ�æ`;íí±¶óÌÁÜV,Æ~2Ò=¶zú;”ØñÔþ”¨ã‚‰ÉÒïå›ÄFÞH`Bš{Š"YÓÎýã ”ËÄç,ã‚v£ ‘Lc8ázc<11 ¼ñ'üVªá+,%õÎkš ,(Š1qÕ/gSî¸:éÛ)š½vø‰´’WuÔG4ûMfê vÀ¶äÝŲ<›¼Õóº¨ö¢‚ƒ "w£Âxö™×wmþ}ÎäÊ<Œp¥#ÁÅ’3ÒSá{i*—R‚"S )½ÞÌC©Œ ËÉgÚO©ÅqdšzK.Ä A^•Ë&Ÿ4’¦¤Ô”¨Óù1ÊmÈŠ“ ¬åà<„QZ3Ô¢ªµ¢r**ëtþÛ²$Rù¥àÑ-I^–î ƒFW˜!¸•3Ò®É[úÍÑZ‰„çe=1*™WKl¹˜‹S–î(3®/¨DÏŒ KÀ™Ý·‚‘5ûÒóêdïóÅcM¦ÖÄAH­Á¡QYêtx†gK¼ws†Â¡EQÄ=ËŒÉ5Ç:{–xìÇhÎã€üdp;-Ø™¾kìÝ·¢ÆDz¶¢[¯£b¾à°æ_®j*UNþ'E‡·T¿êCÐ5 ˜s]â´u‘I-%à®&³½ÏbÀ¶ê”„²¹2š(Ò“³¡ÔÉs³-r§8Y�Á0·û¯·V¦ãAFÚéܱBê›…0 : ƒÿuåÓc€p\J)ñ¥•›JÁ\²©øŸ }€õ—TÅÕZ£Í^ê"Rª¤ã¸h}žöZî¶eÿ’2ñ5-dŠqOŒ Þ¢M{ ºu¥]F‚8-Ç£wþZ²¦–!„ôE ¯8Ô…—]dzœ¸q6>Âá ñ?!Kí¤Nbvg¾œ8­¨†gvݶ1L½{4K,®q:Bž–ߨé 26Œ¦p毵¨³æš±¹R<“A\ëál=N Zë‡T¡ ÷†/Êý§O»ÁÝå‹'º!þýœþ×i㽺O./ÆKÓ£òéÃr¦_ý=":o!–<}–r!„ï~”ÖdpD5¶‰=¶)]€æ„û¨£?[à´u!µ31©Ü—<¡`3f8 vèýÞòx¸ô–ÁLÂ{¦a#òêÏ&s†)«×e96Èvª#C\,9ÜŒ7œš vÓ€Ÿwú믤®Œêfô¸šÃM˨id2YºÑ´éiyÚ›K4S7or´œ÷›U 5wÞ_Î&G† hÍ&MŽpëÁo ±Sߟ4ÚsÞw´Èzˆ2ï{ÛW*Ê\DÑÕù 'Š®ˆ@ªEÑUuR% 8ݹbI„¢Wµžu×Êé¹Ùþ²Ösm¾ä’åÍà†–Iq«ù|¸ðUL´ÓٹМ’ÉÌJyÌÒPó¸ò©Åš˜NËë/ ¯ükµ˜³S~˜A£E°)hµ\€Ó—“HoG‹ICLR™eóL¡r|VÂÑtÂÅ}!)ö„q7ŒaOëLãŒÄ·Ã~ȆWà k°jp– jP3 ÞúO웟8"Ÿ‚sç–ÎÚ2$H#{îAĹ#zš¨9/ÝYÛI9™M‡äÞ‰¤ä—}'ISÁ j«¯Ý’T¯òQõ'®úéj2\Èöl‘ëˆr¹À}¹Ú]M¦é¤ê¿ï\MäáÄCá}i¼O#sgZïÑÈ¿çª>Å1Åb8¿|å’™ádÞ«þoýÅ ‰qtžÆ47Œ'Øèž1‚Q¨@~˓٪êáñH5œ3ÙC¹â_cû§Å‹²z79šrŠHÉßRößù)ñq6-7«¶¸”¸)€ÝˢĆjÎsŠN�cÁч$ˆ]O‡Ç½µßü>sÑë—ƒñ1î|Ü“]/ZI¯±t¦…GU҄؆Bæ ¢sÈWQqé–é¼<Ÿn°ó"}ºÉ.ët–ŽTQM…˜ sƒ˜¡ÂÓáhI)�÷N†Õ¸’ÎÑ”ññÉf¢z!4w£˜“rMZ— ’´Æ� Ù&mdËL]¤/¿lj¦fB.èŒ×Ä6i<àÍ.­!~F>žªDÜÈ7¶û¸–ÙhÔ½IôE”Åp®‰¿ˆÔÇAÎ=^])Wü@Ð\JÔ¢eü¥T_Ò¹ò™LRc P½ü€å´_uZ3‘=ÐÕ ’ j©‡ 1Úù¯POûŽïùž…,PnÙ nagb;øåe%ÕÎ^x™ãëØq h… ò­z?²;í;bÌð[nC·`XŸ«¡7¢³¨V ÇYçÊ ~¬Ê·`¢Sc·D/­FÆÑpÃöâän†¯¡¸~´´1——Æš{€‡>ÆáaK×xÊîUë®2X*i¤¡îµ0¬LŠñUqZÔh7êØ,!“NLçÊÿHìõìjµ“ü Œ¬ÍëVu‹#–Öoñõ“nñõ“nøaÕF‹u´øá$Ý⇜¬å„`ñòQA‹M«fßâ‡ÕË7].ûøu?Ì#€„¾%÷÷ÿÔÅ-¶_±ó|Ã~ÝìŸ4nŸ£¥O›½â–ï×ônœä$mÖéüǰDZàõއU-qñ+Ø–ê ûŽêãI:ù4OÔIÅ»Ãq¨EçJHUIà¬bâ:ý¦b’ÙM|…Üoïôt�ïì 7(ëL:zGƒÞ;ØŸj÷½#FííÝýÌüš¼´%¡©DguY_,q¼1Ï¿Ãw彩½7)ç!ÄN‡K¹§f/Æû¿-ÈA´"‡qÔšÄ.Õ¥9�‰j4.<=réBhiD3©¨\îFتpÉ?…ÕõŽƒ.¿ÈïËO>³Èå§'É®ù ! L2í-N»1ÀëãÄŸjO’€=Ó$¶ÞŸ±$`s4I½ €Œ¤`Mê)—m‘S€XO öf²\IªLꌀ[9—ƒ”Ø[v]#]±ò�ï¹ÿ‡×Þö 0>p¾9–´>ÀæTúrpήmˆÞl)ßÃX£§L—ƒÓ>ßîdh.;o Ÿ–OHr“FA–M~ˆ¼À-á úÌò½·U ©¥Û»öxµ,|¦×hôtU%–V_ ÷=;z}2ð"£…Y üôn§sÞB=úÙstD¦Wî! ‘Ðy„ÿÄ{#»K‰!wœc—4OµFuZ´Û¾ûÖÿ†ÄÆ29JLÎ/¸¤“"ìYbǹmf¶gñ-Ssí'Zæfë5Zçfu!s³z’͹YÊæÜì-ÓpªK†ubÒ27§ºNXŸ›S]#L7Øó¶¹9ÕBcnNõÂÀ´I”{ƒáÑxY·EwÕú‹á AŽ5 nYPYËÖ•Ž2oØÖ0w\ s׋ -k1þ˜Ä–µ¥ÅqûZŒ’³e-FiiжK:sc-F ™"å§ ûöÆcNã±°ãq䟶‘㢑Fdž5Gäæ ¬wÊÿ+éo 4¥á�8Voeú„¿%”U\°¡¢ã¯¹•¬û¶z/ÐëX·èu¬Ž0ëǬ´žÞ à4%ª·f:©íE õüNk˜Ð sy­ýs”è¶· ¼úʱãª*+Ô7Q`6|;_4"õYMSµ\øþg:³Õ†¯´z›ÜSš¿7ª´zôÇNX_cÅ^XKóI]_®“Á©ºJðñ3jñ¥uuå¿Fqvg6 €õÃÖ¾QýQÕPGƒª?@´ñeo¢!š*BÂFòWÞÍ!õ»èMdÇÂ7‰8ŠLúü|Ò™Ç÷5•¢—˜¿H¢1aâÃ:HÐ*‡«ÃEOƒX „èwéÂÊP¿bÝäP?{×;]ZÏ/dbÂì…Çúq;Ð}êæ ó–A-£,,Ü0û±ë²Úb’�øO¹ZŒ—h-Æ'‚ÓY ¾¼Çè2(î|Èójƒ½$äçÛÔ¡©ï«ñ1¡¼ÃýCÙú==•—Û ¯pÞ¦ý!æÿzû•~ŸŽClÉýHsS)¹Êͯ_ûKÇÍUõ—Z ‘2ùÆ{ëÊ2T«£JÊÈ© ^ñ-¹¬Çº‰ ¼ûv\ª¼¾’ÙèANb½ÄƯ֡ˆ01޳r œ %b BÅ߯•#.@:ïK,jx:Æfз|RiØl²Œò¦Þ•³05qvPP˜Ö˜˜äA»åW $¦$†?~˜»p˜èsšI°‡˜ñ”µ…g°7ìÕ¼~pë ©c_qžÁêDcCÍfºÖ¨‡à·fkн&¼¼ÙJb‹n:Èøa¨[CHY|¸^?»ò×8£ÝÍJ"2w<òÌ5Ôåç4ðÔGÔ]Ý?Ä-,„‰"å{jnæ8·½eŠÊÀšÞs_Wú¹%ÚIãçãÁ[,Jì’wjÊapGj3©‡Êßö ͺïq¬^Wjð×èJµ"RaPkÂëÈ(&…ØÙÝO¥n|¶A;¯íD|â0‰‚ËÏVÿÀ_ibñUE…‘ŽˆC×§x«0šQñõÝTsßÎò& žÀÑop&øLFGr»Ñ®—yMEJ*ˆœñÅ2oÇßdåÛJæµ9iá®ÁØ¢Iz>e ”®aÅ4¸é*WêNû<XÒ 1ñí4ƒOûÍahŠ–Î3EKïEú½=>Nó‚™(°q;ѽ&°¡;‘û Zž¹ýt®�y«C¶�Š‹PøƒU#'Ç­ÓNçùæ¢ò«Mk €£M¢â=g"½«Rã,$11q¤ÙH&‹üW›ÄCpÓD BÄ{«»õ ÔïÎ#Ý^ñŽäÛJÐb(Á†•m&ÒfÁºqë–Á„ûòÓW‘®ë«Þ]ü#é ¹>ILŽ8æ/Ì4Î_U9hlá®~â•&@õŽX;»#|Ué°‹iq6gD„ƒ‘Ôšw‹ý%>ì)þ»1” (Õ †Q­ËP¸b.ç†ýõ2‰Éë@õû8÷Z90“ùªÑ®£}hÑõñ㛾œaXÔ¾À°ê—�8ø @Ôÿ2à7ç²ìÈ VÉÅß®YC\ÖÏ(èÄú¶">Hq;ÄéÞé©–óñhŠÊiŽô¸4Šœ<hØžhãÛýSd!Ïá³ØDêûЂùƒï“¶b¤ˆ; œéZg¿Ñ oÓ•tBî³·+é.½USßi¢]¯)%Á¹`¢ßít~äZ³Ž½¦™gxøˆÐolÍ!Ü·=ÈñR°¦ |ÁOÓ¤Çîø,|õEü¿~Ÿ(ø§ûŸ—/~õòÙ«’†Ùƒ'‡¯�=?üB>MÓ �ªAIcΞ7©òí|è…Éû/?/Ÿ=~üèUùðù‹Ÿ9Ë2c^ ý«/?£r¢(!giµqùù‹—^jšá¡eZ‹ðõþSúçàñËÇÏ>î†Ql k! Îpsó½á³¯¾´ÅïçO^>~ A?Ø Sìýôɳ?Ùÿü1%<ÞGNªþ ,ö†û¯¾ÔÖÔÙbpdøÒÀhà¾0€^­òæ'ôº†÷uàSHF€yS|ž0´%uâàètt "M I´·ÊG˜}¯›,*ÑN ƒA¼×$‰ H&ñ»§ƒÄIi–HOºÄ¥WõOQNìʹéÌM^ çÚ¿’$@„á¶\‰%J¾÷&Iø³ ÀºS§HÃÞxÆÅ•‚�·r4ÆŸáÞ€´Žæ‹P !p„˜×GsþtÍl5]Ê0®Nq{ßXP=T vÖ]°¥_=ž«ßZ°�WÊ–Rów🨒ÑéhUÀ~ú³AoqNÑnoðzEln&`WRý˜ª^þ8ÐQþ»è]Œ¡uœò ˆ…ýÄŸLhÓ? ç2l×Z&àÊË5Òr!–ÿ¾Îå2ò¤R®ò°^÷7x¼™èã͆ënÆ›ŽðœÓX_cO†§æÛm`ÆûÈÏXÂѦü ÅzžÐ¾¨¿ð°À ù®¤"õoÊh‰îSh}ÕB&·qÕ}(Cð‚¾[–ÇÓU¿t¨hÙ^.8Õ9R±—yÀÝú ¸;T ÿæàÉÓÇüŽñG8ÊÄ÷3îé-å²/Ï„õò/fÛLG«µfŠ^çè‘HžÇ§ô{üÿ²÷6?’,[žPÝ1RKï½îé¦14³@WuGáßYJ +3²2æå×ˈ¬Ì÷ "#<3ãU|ÝøÈªº€@b‰h ‰Õl#þXØ vhزŸ?`„œß±sÌÍÝÍëÖí==M×"+ìsw³c_¿cnþ;s~Ó%Œs8P7­ä³?=Úµ,>š'¨‡=çÇ_U©‰=RE°Ñ @—T‰^�º¤Jô¥K²,ça§½�tIÕèaX^Fõ衲¥—¡ hÁ;ÕèaÊÑ0œè`¿¬E/�I¶'z˜²=Ñ À„é‰^ÞèQè^�öl_ôD,ôE/ˆô‚(õE/ˆ2_ôkçè`×î6¢Äoôò(°½€ŒÊZôrÐõèä MÜ脘ãÔ’R1ÊNª0ñG/ °€RW¢ЊŠRT¢ ìE#zøjÑ „»½ ½Ñ ’Ƚ ‰½Ñ ’Ľ I+Ñ ’¬½ É+Ñ ’n%zAÚ©E/HƒzôD¶¨D/@P‹Zô‚4®E/H“Fôð;V¢XvG'z²;:Ñ RÃnCdZh‚̘ü64AJld‚,rE¡-Ïb)‹ráe2Gÿ†%È oû/lT‰Kñ ” ÃV%øÿ˘ây‡»ï/lD‚<8DÈ­ŸI,‚œ OÈs„"È£C„ û… D›žþ3!aÏ þÏ$ 9ê|½Bgæzƒ 7±|~®!ò®Ð]1© @Ð“ÚøÝÐæˆßô6ú@×”òÊà]3‘–±ºàQ§,eìnƶ°1ðž#ªÄ èDläJ|€ŽÜØFè˜*ÿ\ƒtr“ÖX�®I+Ý>­£Hÿu7�BB`•×@�´”‚rÑÆÀÖxäÄ Å5©Qöcë9¨Qö#JCƒ‡KLÜàá—Uæ*<ü´Ð€9ô*4ü´ÔdIIº­ÏزÝkš¦AÃV’ÖÇ'\1‡‰6Ì¿ç°ÌãP«°Ìǩ͘üòéëßjPÊ#kêðÆW8åiò„ò_÷“ÊÓ, íßna•§ùê£VAU±¼V ÊA–±qü÷lg—Ò\ô0é߬R‚2KFpÐÏã`]•ôIê’xƒ&>‰*$Þà‰G;¹$Þ ƒ‡¬$ñ5<.tI¼ÁÏOøªAþÎé¦3N—\ÜÓ%~a¹¸;¦{ÿ¡Ã´Ý‰È!Ú¦‘•™g—<Û½W©°3L«TØ2@J–ë@X®‡å:(Y®Éä–ëÜùþÐ%¹&àb’xë’\Éu’V˜¨ƒ2À%¨¨Ím},×*C~kQ¸üj"êP¦ã#¢¦‘ÿψš nlõß7‰¨JœŸÿ¼ˆ¨Cw>ù25Ízx÷ã’‰:ÌkLÔa· 5ÝJEMScˆ=1KEM�›‡eÉ;… Þé(jòNGê)”¼Ó‘Œ—v:Je)¢gÂБԒNÓ¼Œ‹~&œÓ‘,MJ9 –yêì±Ë8 ¸#¤öÃÁ8íDÙn£œŽ±Ä.åtœÖ)§†Æ¥œ2Ç’r:î6(§“Nr ?r§î åt\nîü‡–r:‰\Fh=$§ŒÓ¹r&)át¦¬ðM'¥a¼„ÓIÒv{Ã78]´pšùÜɯKÂiл3átRöÉ/1Nƒh=ÉëŒÓ8Aì0NÇq•q:‰Ú§Ó°dœN:.ãt¢b ãt¢çŠ ãtÒ)ßÂ×§™Â=¬2N§±‡q:M<ŒÓ†Ë½É8m Ý+ŒÓLéŽ~g§ÁèŽQQ%œ¥»—pÔîøFºN8 ‚÷0lNƒè.^p|ï@UÂéP}‡pð\ebf2ø¯cb_|ˆ0N–‰üð_ËÄ æødú%3¨ã 3¨åCÌ®%3Èå-ao¦_Ü)s–q™˜Á?o¯ ºµ«œÝ_—‰™9ê-ÅoØ©21·Ã` –w_ù~„Á˜™àÛ.«T«Î` êx_Õ–õªÕŒÁ&ï­Þ²^=—ú¶6©o;Q•ú¶Ö©o;¡m^‡ú<###5m‘’¿¢¾ý2õ­ËÔÚIªL­_iÑ¿bjýZ¦VÓ¸L­4EÔ™ZCy_^ej¥u¦ÁÔ Y©•e5¦VÈL­6˜ZYXcj ™lªÎÔ i“©•¥u¦VL­,l2µB\gj…¬ÉÔj¤~¦ÖNV›âF/ŽÿŠ©õ'2µvLà¨Ø2µvº¦Ö@}SUëÿáRµÈ ‰~'ªV{°R©Z;6̸RµòL&®ÖN÷k¸Zeüù¸Z9‡ÃÕD ®V¼³B„v®V¼HB„v®ÖˆiǾ‚«”A–«5èâ¾y«5ì ˜»Âè’*S;} Sf˜Ö¨ZƒèǨ2³ÄeʤTI”‰„åÉD¢¤ÉÌ’×K&R–$‰’#“RŠÌ,ùé ­Qæ2d†ë’Ö)ZÃÐjã7YR¥ËÄ!Ÿrc¤n.yf–T¹3³¤F A9“dUâLª¼™4i3ÃÒLQP—63¨Sµ†‰ëí¡Éª´™Î†‡‡«õÕ´dÑ ³CÄG)é'uÚöIKÕÚÆ=él†|‰F3¢I‡d'ͰëÐh~óïU‰.™CGˆ.£¨Rÿ¿pL—Qe¥NuI£öÿ ªË0ø©T—8ˆû#T—&ŽÉ×P]Fa…ê2®P]r¬˜ÌR]F¹Û‰®Ë?­s]âÅ‚åºtß+oÐ 5²Ë¸œ~¶Ë8ø¶Ë8¤Úgü?:•¼cö%ŠÊ˜—T.ÌÉøpo*¾Ê=–Ù*ßxˆ+ãÈ!®´ÛœQìÌ IÈša³D8ŸŸÂR‰8-8G–JŽÔR§äE«…vZ~Œv²\/¿D;In§DÔ—A¿*´“GŒü †E[‚eXŒ@˜]gXD˜˜o ¦I³fšE„‡ù1m¾–k1ÈòVÚI„F‰p¢-.™9ºGs.ÿªEYQ£ZìX®ÿß‘j‘¾Ed¯ ZD¼oƒ8wŽ(¿Y¹åìV©m®ÅØ©£åZä`×"Øû«\‹AìãZÔüd®E„øZ®EÄ ˆ°é9®r-"t�"üÎ\‹Îùÿ­Nµˆè;@©é©ÿbP-éë’kñÉǵˆ0z`¹ºý×âWs-©C¶øß6É%Á,Ÿcæ?O²Å uØÿÛ""" Ðe[tJú—‡m‘ìP~N\’cê9&Drà žæúó¤[t›Ëát‰$î�,à×õëø1mZ.™pñ hÀ.þøãý„‹åÖ=ê‡ã" ÁO¨æ×3.VªùŸz(mŒƒ¯úO¢\t ð{ÿ¥p."ðA“sQ#6V) Ô - œM*›HÒÆ·h¿zòP‰$_`[Lüd‹I ×bÒBµ˜üecZL¾@´˜ü ȳˆ T#—g‘cgüųˆ`ô€WßüÛ žEÂø3SàéXù1šEĵø 4‹”ý«iÈâ[ÄuøZšED½ø6ýj–EDÆ ½úæ¾À²ÈQ1þ"±,&®©¿L²Höø*’Eûh#YD „àøæò,">ÇïÜÃ<so Å¢VÝ?õ¶,V¦ewêõÓ+&-ìŠÉ_rEDO‰p6ø¿©‘+"HÊŸ™ªPÛ³Ñ2^jEmËJ{4ˆ¡… õêÕí#V=ÍïZÚ’WQ êÒ*j)VÅJ1•T±Ri^}ówꤊˆÉòg%UÔ" Ó‹–oªÉ£¢-˜eŒ±_ø“ bRcSL*dŠI•KѾ—*ŤΤ˜T‰“bâ£QìD&ÒËoü4Š8òg£Qtt[E+ûYñþþÏ‹Ef">B¡s°°("’ 5XA¤CCÖâð'ÉåŠZ²( k*£%:,ŠAhÖ4YÐ÷u‹b›À4ÿ´Î¢U™1GXý,ЉŸD1ñs(&?…B1ˆì‰àçA,•eO R}b~bÐ=D•þÄO^þİdk Ñ8ê£~…­®�ÄDy 4X¹e[A þâ?i(j%P o!PLZù“/Ð'&_`O´“F}áõs'&-Ô‰É_æÄÄKœ˜´ó&&_ ML¬‰51H=´‰Aú—7Q‚³üDÞĨ‰Fb˜UGbsìÕ�+¡4ÂC„QÞÄ(Ëý¼‰I mbÒdML¤‰4±Ü¶®¶ëÓ&F4›G?‘6±$WBÜ’oã(üz£ýà m¢ÚÄ(ïÔi“:kbR'MLjœ‰v¦qMÖ¤L Rgbe—ÓKš(qL¾Ž4‘#žˆ¥@<d˜4¿ÎRßüC—4áK8¤Hh;W\%MLjœ‰Ö ô0&&UÂÄ8vÏŒÈt«„‰¤¬&ÆÎûa0&&ÂDÒÖ ãÊwxŒJÂÄÄטøè/[bâ'KL|\‰I“*1ðR%šFÏØú²DD¶@܉¯#KDNÛS8¢ÄOSQ•,&5‚É¢<­ä°%¾©²%"¸‚(8l‰I…,Ñ0ÊùÉ`,ý%Ybœ4ÈÁÖÿ-=¦…,Ñðæ7ÈÁQ_–ŸË$d‰I,¤õqê%FYøcd‰(fƒ,1mr%¦ªÄÔÔ˜6‰Á�OÕþ¢D°»3úW%>¢Ä õ0%–à¶*ìñ`uÿ)T‰È¯–yßÓàë;mZ£JA<¨à•*Q!q)ñ¯U˜A2xeJŒøÃ‡)1­%¦UžDl‹UxYàð$2Ÿ|'(ÑåI ⯠Iý<™¨I“øhË÷£Õ†ü¢D¥²ÿZ¢DC{oš ôqÖùúê?¯%Qb”‡5¢Ä˜ƒxËʾ‘…½J”®ÅoaiQbà%J¤~Û$JŒ³¢ÄÐO”yˆ£&Q"MéM¢ÄÀK”ˆU³É”ˆ.ã£JL¼L‰IQbòžDæóZyAÝO¾c ObI}*¼þݯÏ~žD@,€¯çIŒsË“ç5žDû±äIŒsObÐäIŒÓ:O¢‰Zàé!¾îÑ©÷N½c4yÃObøc<‰ŽÀl•†'ÑÖWŒƒÊ*Oâ7ÿÇÝa/œy Ò-á âå ×ÓK;ó‘©¶¥Á0qt2=YEN– éÛIùˆm S%e‘žñ&IýäýnôÄ»ÕD’<ZÏÝïÌ!†ú°1›*³¤ç*-c (¦æÓ "2ùdnq˜Í—Ç®Èä“附ÂKq„¡ÎÒ,]M>f íÙ¾<nõ½ˆLÒFˆkeŠ­²@’é#ôëÝŽÞ½Œò—ÇPó7™àçœÿ4 É‚óeýEkBÓ†ùÿïÛ°Ûq>Ä–6¤ŠÙnt:Î×`ÿ—iÖOˆ/�žÙ¡™ËñtiWý@Émײ…ʆî·e³´Þ²¶Õ´eËFÛÈÃåã2·Ôòm™mÙn¢Ÿ–9-K³iis> I^iX\)ßš9mtóÁç ëXx¢WÊÇgNë6–‡¹2bIÔìök4·?$ú5šÓèú¼›:ý!$OÄZ›³@bÇúÖ°N(]ª ó†2ñ·k|²úÕ«ÿn·@ËãMqº[ð‡³zH˜’ü’WzRúyµß”ïÒ‘޶=AåÜ Ø-.Ää£\£»J”G$Ò H2ÛN·îGkTÒÃó™’có©ï }sWò‘®|~=Ó£±Øo°ÿ£iñ°™—'0ÇÓ(.¯F'½··ï:¯OÞÞËïàõàä­ü_ŸÜÝœ†’Œ^ß_žJ*~ýþb ¿þ}ty2ª\ÀìžsçáLjµÅãÓÃâ¤yJئ÷¾w>º¼ºì¡ŽìŸ=*N5èKP.Gø¾wóöвFxÚÊyš|™Œˆò°ÛèGON/í³DÒ¿¡_åÓŒ´Oõ4¬HÁnÚ£?óÀÿØ<Ð0“ºÂ!â‹È _ë4‰ÕJºŠ·Gγ]Åà×ûtW~tùkóôµÙì½Ì¶³‡Ù|¶ûŒ§w‹÷ýAÿmÿ¼?ü5µÅéÑíù”2¥ðúæjØ;öp.ÙŸõONz—ôLGÖ¿¤¦¸$³óßÎ6ãÑxþ´ÚÌvÏ m¤èߎÎß]Ýô‡g£ã·ô¬ªèú¦_¿6d6g¸Ç¦xâ¯ó£„o�Î׫ˑ1’#À!êÀ\ôï{rŒúßÔlÍvöl+÷ ‹Þýqo0 ÇöŽ©"tMi„†êôh0¤4äƒ!uè£yÒ·O›ñúy4Ý/ÖÂÆ9Dh”b¹±†îü2y2õûd¾°š<øÎŠ›`ÀÑM9:>ëÿ’®x×»ìÝô+R€Ã£! ßÞöχýËŠ2|}z{~îUqëü£ã9nCqõRlÉW6ßl2¥ á‰Ëè¡ÃÑœÓó«;±°Wu|uyÒ’9xصd¹¸>ºé®.© ÞýÁ1Í~ÝÑ»Ëþðö¤G“FÍ&›ñDÖÚ!"·§×(Êðæמž‚ÚÈ•\‚fÙp›¢Ý¾ùæ€,ñ€×VŸí„)ÅMïúêf¨SRïi\PgýMïdt~u|„ʃÅY´·—^}¨úã+zr¤©£óþ»KP,ƒeɈNn¨3ú窱ÈÞŒNin£^]ƒYäW·4ôD˜©pp~}ۻɭº¯/Žîe*Ä å©‘€™à÷þƒõf…Q²ÚØÎHóÍ�èðW7#›ƒ�ß)T$!¾X(%ƒ4>¢Z–’‹ëãã«L»¥ìúú8îDTך(¡ªVEq‡jYe)Õ±"J;Õ´&Ѝ®5QŒŠÖeˆáQ†Dߨʢ˜©+²,é`Ÿ¨*‹;¸ó5!rÖê‘'T· V“^Ôé“Es˜SUL“êÅ1X<<brCÂJÅ®îz7$ ë² ¨&K¸.Ë@SZÊŽ{4„µJ…¡[¥aŸ&ÇÃþþq½É.ßñƒh°Þê˜ÂÏÁ鈒Ñ[Z}M*Tf4êE¢ã“~ÿƒÍ6ít:È×ÅrZ,'8Ë¿Å3ZrøŒA±aË`9ù ŒÜyM“1dÈ6gQðz<ŸC¶u„ákZ¬ š%ùXüÊxöÕë¢×_Òrñ¾—â-Wk‚8ÍÇæˆ}TliêŸXζÏXèð’pT|¢9Í”³¢'Ìl^³oM‰+ÊåjY˜2ãE€¹>§c}-wæÝÿÎ܆¬Ž/Ëg«ýV¼(ý;R^°…q©8zÛ×fÂÏ£þ=åį÷Ôø‡VóÒr)×oyL&’€VµÁÉÑðHodøÃH 1ïé~&Ñ£Ûšûr?¦J|—+e@ËT)œcnè¼.eÆ(Ž�Ìá¡#xÜÏçæ1'ò˜ Ä› h59¾¸:!ä8¸0hIò¢wÒ¿½ Kúüèæ]ϬþK¹ “Õw¡ÕÃ,ZsIÑÂL?«ŒàýàxÊ$®ú7„]"M®{�Ò&q5<ëÝð28ø¯Vë.ÛMÖW×Ãý£Çð/pŠgb£ƒ(2©Õ~·Þ“#6y¤‰‘‘yÆ‹ƒ43©íçífµÚ°çÃÐ¥;sS¸š,ÔSSˆõÁÓà×[úó/ªe`qŒŸ)ÿ$DIÓ~шÌ#NðÁ#~½ã=�ü:ã-üêÓ/së¿O¿Ì­ÏÙ7į vùÝ:Ìäçè�~ÿÆÝÍs.èö‘yÐ]òó„Y—ä7])ƺ¸FdóóWü5(ÿ$4™'¬ˆÌ¯øãgþÂfJ™'^áƒ\óÄkþø¿è^±yܯ–ü½.ÿüÌÜjøyC¿Ì³ ;å_ôÐØ<jø°ÅMs› ÇæiCóEEÒ)S¤LÌS‡ü¡*¾µ)(ÍÓoé—yø_’Êoš ‚$“ÄtÊßÞI.Kп)vû μtE3Ÿ·|ì!íXÉN†/È ¦[¤Šç”m3)au3yiT*–cºšP˹XS)H ùÎDyXí—Ó-3~8Ò]±X¯6ãÍ §BS­Òså”aš[JÌ>4—¤Z¹ò‹Ô “ê=Ð$­ß´â¨6eϤŠ«Ol‰Ljù°ŸÍw³%âlVd9šÁfËóJm'#óÍä7‰­¬ó©”J}ñ ‘¡ˆÀFR)û~*ÃLji¯–¾6w„xY9ÙÌÖxçžuK­D :Þì—Ú¹Tt2_=<”7— ¢Qñn‡*XNgl cÄ|¢¬ã Ö4/óÄjì”’§u¾ƒÆ±‚<kh–˜Êƒ\k§†ÜúÑb¶5Ÿ@¹ÖkMÓWW«Lñg½™½ŒºRi1™“Lùal¦ŠÝP3­Gã%v·"+à~ ÃtãºläÜ”LÝ•JOg[œUŽVøTnöƒØº›ZõdµGupÓL…/8õC±Y‘Pª=õPAuY-Vr©uñøH}‡°¢TœFy9Â�Õ *ÂÕô3ɤ†„©f†\uKÒH¥û…醛‚¤±#uQØIÅöã v¤ZæƒÆ°“9I”PKÌM'¹,€V¶Åb6YÍ©za %¤©a÷L(扄*Üç#¾/øQ¤èT²ùtô<›b4†²D‘tIyyÊ aâ Ë…²fÝ‘›<Þ Va \mfÓ)veEƒˆzs²8iöXÇÑY ‰w*J®  û†AEE@f>£ˆ§†aEµ¨Ã~NFÅç6Œ+2t=½†IEÇ<@F@(  §ÕžÆ>XÙI'uU³Éšz‡éià‰†Û…—JÏëùl2#3ÈZk%åéŒÊÔ²ál](+rEZlqð0”ºÔ± ;EÌ”PVì»uà œ{ ßÙ„©*pF³˜?2Yˆæ4SZ«Fó•2zu­òƒµ“òÇGAmF%}.¨qÀ.V·¡0›Œ¶Ï´Ž}$MXÓà\¡u[¾min`ªÓÕ#øu«š5F• …;]C w¿ÅVMƒÛbk>½ @ÜÍ©a?ÜU›[ìáNÿ¡`ˆ;~Wk‘` ’>Í _­™VdË'CØ*²XРT¢©íêA†ÐT&$Å(6>x�b§®¥Då•é4T<¢Z3ÒËàs蜊LlÓûíZN.m§šc¶œÌ÷ÓbD½\wAU‹îϘ'Tô¢FÓ¯Ÿü*BAŒÕoV»•Ù" Ë,öÓÇ9ó÷©qȧ™1Cr¨0fYl±’ÐtQlPkÅ0K¦â‡K3>z½,½¡¢œºd*fAO*â“@Œ0*jòe¶ÙÑøæuò TüB^sCÅ+K˜vžRRÀ"Y¤:«9¾“ü</d()X)å• A‹«~äöd­Ô•\#þN&Tô‚õŸWæP‹ns’$(%f6´†Š]XG.=UFªÏ4šV·¡eN†“‚ò®B…1kò.1ì˜ôèa¶3R\¨@†œû)çÏ5MǶB{@}F!ÌúÓz¿|Ze5å·:F·x• IõtFc{žÄQUl«øE¥4òwÜtŠ]Ö„KàS´Býš�9áAÎꙇŠ\¸ãÓúM­íF¨X±�#*›‚zÞ÷#FŒcZ"+´ìÐbM"î3[æ›­)È——E)Rà²)V›)N…)f1Ã׌ÑHñ ­ ƃ¬liÑç½%sâ;RÔB~NóE©ŠÌê‘¢˜­¸; ”"3"µP7RcŽÈ¾D cäÄ ™PtÎΘw°©”XYîFã ötIœTÄ8×?{aJ­¡y`]¶HñMM1bF'Wƒ1N )¡¾e1SaEkÇ]¤€§þÞYÈ\35FŠwD¥-32¨>RÔ³Ý?=[g"7=”ymìÔ)jæYJDŠŒ¶ûµ¡‰m÷rŒ½ Ι´‘¢!‘“P-ðy9!—ˆ¦àH‰hŒFŠu8Y¸M«Hgû™Vê!M[èó²Qq·? •jjûI©N㤡+îq„•nªÐ‡ôäElA°¦Â¿âÚ2m™a»W{¬2‘4rJ­bà5(ÎT`–š5ËѨ JP~¥eRú˜b›ý²\÷Áz–«øÃrõCÌÌZ‘ì˜bYÀšø–„<jtÅ@û¥‚Û©AàóRÕvü’ŒÕºâº¢ÊÖk»|œ}ÂÅì<èD†<¶ªû- ™$n®Àž½Í(I+ ê™-Æ" Ê‘ys¼ÐüÎ_AFI^É0Ý/ŸÝ't+ê’–+íT3lžàØ^P›Ø‘B •$’4ªHy¿cË'á\±S™´jöÌAFVÍ ¡z£´Zw¸—ü­¢+²´[“ª íÑÛ[½Tú¤‚!Ê ÿ̬€‘!¾Çt6±5S@ô2óW&±üàÚ[,§t©¥®x!ËŸÇ®>#)0ú÷¡¤@@tý*pFmø“$áp§Mï…Rßçñg8þ†~™ºŒÉÓà3½œØ2Gz#8$àH‰‘àIsAÖ$5¥›Ð/ótÝn«ƒ¬×|ðZ~Ó/ȃº¹€ ¼"7œìƒH Òâ ,tÖCÐAdVbç'ÈoÌP|BM?/Ç üð@à™á|,˜áñºL‰X½ƒX ÀãÍp0¢”yü#áÝò†4)OÄæ…ôŠ?öye³3L bc[|éëUŒð¥¯«+‡ìaCˆ¬‘+• ›XÐ@EŠÌIELò¦ áˆC‹”¢ÅG%¹­$Öð9-:è¿<oùÄxjµ¶ JèÍy«õ 3›^¼á/BiMÇ)ÛÜjhvbðDãfÊ'y} *cdŸDË'ÿ%¶‰öËäû›ÏÀ·øPòìÇN�â(,¥ Þˆ#5$ >ë¿æbÉ*ûˆG[C””"~©„ÏçDD9PràÌZ‡RJ•V«½1äy)§6^ô›ƒXÜG³—-óQ,+6!l1¯à›´Š†f€‡1øÁvw,+°jå…Ž9±l–/œ|Ž~4SÈŽ™\»Íèá9É[jCþDÌhÌþ6>«F|¸š%ˆ¦;¡*ÚP–h#äÉN•2¸%”uËŸø)œinëDz>K~ýw»øtÊU,‹±,Ô"Ú>æ»Ä®æ –åùÑm:Y™'+S |¥’ÅvO >7¡Œ±ÖâÆ¹ÍÂÕ%]›†›0â£bøNÊ#1û †4ðiIVåõ>|²TJÍ"´]í7äÎà%UÑ`±ÛåqšX1Ÿ’”o}ã4­Ë™34N³R|#‹–éÊxLîèñá'š„m˜ÚêhîÈI~ÉãlL¦É:UõäߪƒªzÉóÍS‘3qœ©à¢ÚuxËß šP#…Ç™Z€õ-fY Ø8ÓúÓrø¸â¹O¦åL-ð »JØ­mö<õd]«á¹�àe¼|2ÇÿY31˜&ŸÙ¼Îe™®*ÒdÙ~äžMB–Y¼§Oˆ§lb4Dz=a¥$‘ŠMÑž²–?šþC¨À*Çe#U¦ªé~bv;ôž¹j̾ƒ¼wÈIÓ­j`Kl,’ªTUìÙCZ9e\¯¨¥ºZ“b>þ <kxÓãnlå<88žvsÔ¬ÐÕÚ²qóZW«9?ákÞf}ÆÂµJ(Ê“µäÁ¼‡ÚÕjó†2õ4pæÎ?“Bkm^äŒÌ"Ç­’t:mº)Õ&ø¨e3Z} Q芰gë¼RÁýÔ6…,|Þ’‰“äE8QäÂ"ó–ƒVp.Xæ×à>¹«’ά³ÿ”ô]Ÿ~ƒx„øÅ3¬~2ß<Ò]̰x¹bžûDÁ iÆÚbù ‚+ža¹gû…yߘ±WÁ\ËT-…6S‚Ôáhò8€$ŠnŠñfþyÄ;ýЉâš(§²k`ÞLâ^¹ÕñQšfÍ]§8†“ÈN‡£V÷ÊLÕ|è–ò)jÉgv:Ùy,€Ì´iôãùÇñgdø q"› u.‰>67ôæÎ›Èî‡'Ï´Ø ÂJ¾53mxv vQDW,c@!]C…;w+ºÙäQÀDQ­Ç+—$R‘·‚å«xw Q gv·Íc²ÕOÿâFZéO…ùÌ î2‰µšŸÖÅx¦æ×'ŠáăÀ%¹•àüÙ´:ZÀ™íz¼b¤æ9HÁ=†JHÑ9Ä7càDAšS®4´¢.Þ7xÙÌ@‰¢.³Õ@éÔMã-É2WÆïvdû›ty›ŽOQ%ŠÄÌ&Ÿz;HˆÑíG„ù×V„ƒxÇF#7îEýéD1e¢áµã� ©´†Îù^’jípz�™DZAš,¤~ ½XT­žB®†ÊÔNÔ@/¼a•(�3ïDhÊø­YjI£µ´G=0+ô²R»4'Š¿žLƸLÉÙ‚M„MªÄQqWQÀe$TMEZ,À̦Ðêi¹}(>#z"®ë–Ò 9ø2'Q<E¼;’׉Â(šªe!Q�E$Ó:`“o†ÆRܤéú;ê žžöy w9¤^ÏäJÔoÓ³‰B¦çÞv$Šmø ””ÛÏÜ÷$N=âäb2¼'æRu2“Bçõ²yë)oÄ¥[Ëb:lu”bçÅ:öå³zt‰¢«CîDQU•‹”ŸÙòyf‡~à7’†^íŒVžÜ!Eê(Œ‘8sÄæe.Ÿ$IâˆÂ¼Ï%q×3p;HØH/J;›vö7ÒNè׌د#¿’.ÊUr‰×œv’Š˜ü}îÍm q ÑNåiÇVË7º{UÞ®5Þ,LºŒxq»î×\ #_. ¬‰ô­ÿb5…O0â=…TÑl=MÖ”Ý4ut´T‘¤4åoh4â5ÖiâRCƒ‹‰T wc¤Ž`¿ÑW#©"È­ãKâ¼o7¸¾ëÌÒ/ÏW`Sù؇ê­PrÞç×&x—’*ˆ)¿íAþ¨nŸá¶ÂÕmç+rwR1Ž–œØ¹U'¥Ú|ÀQà–Zé—ÕšsIe»œQBªÈg#dƒ(U ò¡(Ö: L©òâAªhÄU;J‘ «å%Û:©ó|üIŽWÞʦŠS<›h©6£9‘*JaÝuGȃ¤™#5çŸÙËL¢˜¦ Éô-˜Apì2U,ÂZÞáÅGt™n&Íw+J„6aÃ/pOLJ2doE,Ð㜵9øìVébŸîa<§U&÷TÁKUϳlª@:*£)È@F)­¼²¦B“Ö“Î2"²à ’& ˜(v©náÓ³«ãW$}TêÍá2ãRX@ @RE0Л-¡—±‰jeê(÷4«Óî\+Õ] k4‹bóÄíè¼XJÎ¥£PDcö“D~¹*¨ÒÔòùŠkÌÜeßǤ jXN^>H8HܸґÙ$¦ñ¾Xa /U˜ƒäH:,¿G1ÅT{lù˜ËR†‰=%“*ðÁlC¢nE„IV1Ï' ¥Šf–³¹}NÏPH³äó»dVÇŸHÔïOF²£ Mª~ý¯„R¼ÕÃo'£Ê›ƒTá «ø9“OŸF“)»©î ±¶RÝb­[<ÝbÅÓ„‘# ŠøêŽ‹·¿ÿ¶z—ÄÕî˜ÌbBƒX±Õ ˲9#¨[e©â+r– jU8 2ÖC¾¸·V[¶Ê—aè©B*«ßÎ漋H&¢ëV•z™hM¼n}ãk0dªPKf�q©gÊ§ÖæÛêÑ躊¢XjFüAªÊâ™J7xf ÕREPëɤ\-ùœAª(jÍÇÖÛƒL1Ԛѱ¾±#¹Tt]Ðäj^ÏdŠ¢H¶~^ÍÁ²U%áA¦Bk|ü³åùLÔz†0ÓZ4Z š‰»îXË­çû'š)Çò¾3Å@"<È߬W @�<)§¨LÁ Õ«© ­n:c +¯É`ÂL\g$x†à¹˜Š¦ §´µ]¦¨‡¤»Ýg‹g  eŠ{D�ïÙòª”æàø{™" Õq'Êý¨ô©X¢»ÓÂÀ¯ÁEaUµß"Ô"k\•ò+l*ih«Ê‡~ o(äRQZö¦+À.Š{!n[铺å™ à{0ÜÌŸé¦ {°wsçf‹"SÀ#«Õº;ŸhAñˆr1Þ| A“"ò)rRÄV±ß˜~«ÐGv˜€QçÚ" +°:Ê2ÅA²Ú@+[`r6þ³ÈÖ˜O…)ßDw|ò™°Ä'”+¨æ*Ñ`Ûúó´¡]7¶µ§Î9až¦‰u ³Øš`½¢”­ý÷ûÙF¿­q>BÈbkZÕFå^F i­x ÆÊí.Éû9ŸÐà™%¶fØ®æ{³ž)RÚàÕ#æ`Ó슘6»Ýì S¬´ä/sÌ>U¦É,þr Øä—fÏÅ~3Ãæ:e‹ÝlæãI¼Êt³$nóa£«N]5¯F#g šŒf>fŽ–í²rqîf±.J¦ðÉÈiˆ|p¯R e´Ìµ‘)t*e‡)ˆr²ðŽO–F^ÅhŠ¡Iõ¥öI+v¢À^³ÙÆÏÒ¤U “R†ô‹¨©Ó¬=ÇA–VÌd=¬,uÍòœµÝ¯¥¨`™k(t?)púä´v)$3¯8]/(SX&;j#l'*e ɶ´NJ;ÎÖ…¬Å™¾’óiáÈ`Á}òzNgÜÈE˜N¸™3iLcmrñó¸ù´TAEnÛg>”Â/ÍHºR¤ò(t3B¦Ð'al…9èÊ‚f!Åk|t”7q2…j&”8S‡g”)LƒÜ9 M^ÑÈãº!îMY£áÐ#{º©nèf Óä3æžy2…dúö9Ó=¯ízŽ ª~ßµ_nÀ0JEPœfÔæUs¦0Í?΄£eÓŠ;Ä$íºRÞW:Èy9Â’:hª…D…”¡«,ÏÞæ Èj ö³s…fFiXgrÅf˜Ö·”´0A ±.çŠÒê'tóN·¢0]':©=›AEaO¤•ý Wä…C¦äŒò»…\Α$Q !Ñ~G /÷sEN;œ«µÇÄr…Nä-4×™7è¹b##峕àC'%c:)‘¹"¤Ý|Ë, hEGTR³7¡G¾sÅG|Té̦Ml ^árÅF¿PRk€WØ{LÖ¹Â"#•“-ìÈá­z®àˆÕæ‚ЕÐc Ájý™_ƒ<®É l!Eâ*¶|°#RGh¹’¿p¤.È»`y”»Ê-²»Õà SŠØ­’¸Ub@N.©='=(|Ž’Çak6€¦<Žêz»YO ÇíZóÖ H™ÈqÚ¾LÆK~fVWÙãNyìÖNKJž¸UwwËäD\ž¸–X?όŷâkX,q«ŠØ<q«G}gßHêÖg;)^F¹¥[î-ØãòÄmÀí|-û‰\•Ä­ vnóÔ­Šœ7wÞ—ä©[Bxòyê–9qØ^åIM/ÏNSŸ|SÂ…<u+ó²¡ê)°° =Oj +zf„O³ #g®pÂ,¼åeÖî\__‰F¥¡JÛiç 8$‹yZËÛ,õE3Wìau¼læºDbcq-P¦r÷\b®Èø‰Fêb6)÷•r…P>¬Šçöur…N>é²âÖ4Å*ÇÜl·v˜¯6W°,ÓÙ ÖNì8–o®°js¢xdœ}R‰%°o‰ô·–¢W*_þ˜ÃJ¹‚‘¶£ÝꉀeÌ<Í9þƒ\ŠØÆÉÞ1oVT�C®˜…».¨X̺p+lщ3!¹•’M_ â«|Ò›+@q²,¶úýV®øÄÑ’Pm²F©þÇbLÃW¡ÈÇâá «ä#önŒçÔUÜad4 <“£«ˆƒÁNõÌBWAÇÇÕ†Mf6 W›Ï¤’â}\)!ÅúÈëZW¡Æ›kb³r¾˜„0ÏDW¡†ù„SQWpÆý2…~øëÊÖÎÓ±»(žøÜ V°ÄS±”϶º‚%žž¦dÙ«!ØŠ;e6qE©\R+%.ã{’ ÁÍè~-L’ž({1OÕ‹j<Içë ¼xzYàRÈO¦‚(Lw4…|¦_¦„329»Þ]3ù +èaÆrÎgTEAòY)¥MùffœñÎRWÀÂ̼{§´)Ýìû=!JJšòÍ„¢…‰£´)áŒæŸg{¬.{(á\u|Èé§)îü +��q]y¬teý_`#›R¡¤fŸjû/]Yᡊƒ®,éH¦1%Iò!sJ§nºrè˜Ì'+÷¼?˜>ް¯‰Ït»òÖG4‹ÕËb¶5]YÒIlrЕ}aRÑÐF?M´>/«úù´ÀÉ!7¡‰¹+ ú‚ýYÞ U_Mwe_LkjXYÜ“õ:©§œËíÊNÅb:_ü€ÌR199ÉÇjH*•2§lžÖxµÔ•e}ñˆ—$4[R…d]'Ñf9¥d I<Yv´SJʪ#¹Xʉ18)æˆ}@}•ve_<ãXÎôqM’Ì‘H1eá^>+ñ—— Žº²|SkЬەE›S¤”•zÁ…KÕI–R@h’H¡ VY„)¹&›PZlKà(<\-Yv4Òž?R2³É-ª›,´‹eñ°ÙLŽEW–ØÍ+Ž…e‰…tÍaº²¸ZɹB+Û‚úB/ެx¿æï’»²z.ð)³ópY>i1™álcWËÅ—H Ö«õd¹£>#k »«.¡,q&I#OV·…-®xß*==~‹ª®¬q¥|mÄqEŒÑÛÕònfxÙo^ᘙµÜ1êÊš·à=cJfNÒáàìÊ«QPZê#Û†L¤f¶¨˜ÍÑëÙ|õdˆÐqɺbZF%·Tt˨½÷†§M„¼¡aú8‰“Šx \†Ï•H#ÕÛ®wev©ävÍYr›âÂkÅ$Ìd'ÐÚ”½†„Z——M\›TI)5bW—wÍQOY^%¥„R¡Ý~ÉO—v!}‘RšiIâ „™ð^ÂPRJ}^68˜A×àzYw/ÛOH6e˜‰FA˜C–ò- vÂz²Fº¨¡[ÐHÙ€LO–ä—ÊÄnh%H—8ºÚŒFZ©ì'Z~—Ÿ¤Íeá^|š[2¥Ž,Ý0¶¬Ú4~Ùƒ4›×¼f‚ì©#KøreNTm˜Ú‡y…:²š/W’ %)‡ÞÉÜ|ƒHÄR6;sV—¤±•N @’T$#ØYÖv–PÄTf…Ÿ¦ühS‡5^zÂ=à ðYÙ×¾‘Ÿá1²´«D)sHaŠ¿†•eYÇ{CJÄ’àÞ.k:"þ¡íeI§9‡¤fòe—åÜh¨ðxid3@Ÿ;zž¹4KênCm, ¼«Xm+—Êjorðk9 `>|¤ ¡“¸ÛZVy£Ù8ËjÈ@H»*Ac$Nšbý,{d¾F.1“Ùí‚ÀXçûý¬à»clðÓT|S FБå%¥;zà#˜Šð°ÄvçŠj2ÔKp�Ÿ¨Âãŵ7iiˆM1ù˜5_fŠ%l:”î:éò2Yù]ño!êbù|�º°®CÃ(¥ÔïÍHX€3 Ì‹Qꊺ˜q°(à<y)èv@¶ßq]+x iÑ€(ªˆpã¼|üc7 ±‚§%f™<uRÄ<« ¹yî ¹˜y×qA»AE„¢vC+šmW„º£€îëÊòÌ‹'&7ëÈ’k¶²¹ýdÑÝÂÑ‚÷[(ØÔIó$—/€„Q]hæÁ@Õ’€D¦8{ü4†1Ÿñ¬/øiÊçÐJšâ}¤Ÿ²hÂE¤'˜~ÂOSºð3z}9¢Ä�¿M1×½ãþÑùH>ÿ‡"©( äiE.¸ÌO¬‘ö*>rÊŽ{�6KÐØ+(AqóÔœˆ’Ê⟄Ão“08üðD„ã›'Wƒ#WÏK÷ºŒ#�O<ë2”À›Wßü¯¿ùý1=% ™:ü¦÷npÐy ²xó;0üáT^N†¯OÏ¯Ž†&½"R“Œ_¿Ü›ŸÉë÷7ƒ£÷r”Ç7æwöšê?::–kr$O­¶ûú¤èb!S€Îë‹_ÉÏàõyÿò—’_å2j4V€YØÊâ×Õû$œFª‘ÈÒ×Ç7ù½ÖRPc NM °‰:Tkµ tߣãó£Á 7�E25äÿºáv´6<dåè°¸°îøêr0¼9ê_â³öÎë¦ô|à é XÀÒcÐ7ÅSP‚7Å`oŠ· oˆ/Žî©Õ¨¿ü’û‚eŽên“Ð|/ !-plb¹*CZ¼Ø´é‰Kf@° -ew¬ˆlÔ“)ewb[˜£n«Õ‡YáÄ· %òeôd$1.hñ4œgN ~ÉÌoËè4ŒÀù½!œÃïlø7 «©¦ L‰·„ääêOŽo/nÏ0FG7Ô!vI˜’e^½‰±vØ*µíþvxl¸êm›CÂö.¢ëÛ›^ÙØœ^!8‡ŸÿT 8›ž’:"Kö~og a®å8?©æ9Ü{äÏ3ð6áÈ}ñAúýŒ:æ—nÂY’/Þ%Þ>Rýrm©ç~éáô‘¦ /Ý9º?r�§¹É “W=Ëû£““Û·àö«Î@/ìWÝ]`‚ó©N¯1Õù4Ç·w˜ñü¥à Ч¼å Ñ_@ž½WÉLé+;OœÞ«îxmª.OÀ3l¾©xÿîö-(ç}šÁ[PÏ{¯9½÷š3Ñ{¯¹;}6 kî" ŽOoï"ŽO÷ Ün*†ƒûÈ·=š[h<8¾¸~K]!ò˜€T½_‘y"Œîì ò˜Áè¨RC°Ïó˜‚tïz¤‹}Æ ÝÊûì5Vì3 _wvûŒÂבÎoºîî öÛ…®#ß.ƒÇ.½ûë!×Ïc—ó+SuY.Ì`M<V¡%—l’xlB²Hâ±®9;H<öÀ5¤ñX×Ü$[àÒx,A”Úc‡‹3ø‰Çg7ªõÙâ\æ»Ôg›wgoRŸ5Hsvú¬Aš»ƒÔg›wçt7Ÿ5Hs>{¯†îæ³Æ€�Má©Ï¤»€Îg\‡úúŒAºé2Ÿ-ø:‚Ñ>kðu¤óÙ£I=*óÙ£I=*óÙƒ®9;È|ö kHã³]swù¬Aׯg‹þ%õ¨Ìg‰Ûó;÷‚U£ÛKò!|¶ 5U+÷™Wžä>[°ÊÜÔgÜ”®ôYäöü %õ™„Uæ¦>»%õWÒóü–¹’’výÖ¹¢’v=–¹¤n‚•¡ë1ÍåÕÍA×cˆ=öôÚœœ"d_‹®ÐõXºx@:9Œîq&½ºü¬˜^]þªÏÁ%ÚJÃöÂÖD[‘$ƒÇ4òlÉ౑@rxÌuÝ»¹`{a3£MÛç~-l†íŽ6í)Ç~ôka7l‡øµÆrk‹eªå±Z4Éá±@rxŒg !Y|Öûåí§øÀ-éîXç³Ü/¯ï¸ÅŽ$âÑ ŒÎg1zt>|K×ÏP¨Á€#’xŸÇ:ŸqP»G-ñ>u«Ü�y>¨{sÚ¿à0'>Í%=ñi®9ŠOó‰ÒÔÐJø î -´êÞÐ:øîÍàW7�`ì²’këû.…oâRø&!.…o:ç ,ù‡hi*®Ï‡o9f‹WuÆA\¼ª;êâUõo9ÌK‹òŒ#¿´(ï8LSyó–cÃxg'Æ£¸ã1ÅÑ[ãÓœq$ŸæŽ£Êx4^Æ#¿âh3MÅí[ö©}¨:8Õ>\ ¼j²5+fàõ¤b¿Ú‡j¹(_ãÓ±g탵\̇´ñ^Ç:ŸMP…·ñ^Ç:¯Y.bSP¯].bSR¯aHÉÅñšæ"4ÏôÚæ‚u>{Ox#ð!ÜÁùÉU_+Ê5z^ê|Pרyõá]UŸrlœ†úöú—gx>Ø –ðåëÎ8fŽOwÎ÷ôX:ÜÓ‡~ùº3ŽžS×] ±ÙÎqtªSQ5ms2p€Nì=ÁzæÁ¼ç<zï9ZÖ= ‡eÇ£Àî§è²fÈ!w<*Ž·Ó||ïí=ÙñhÎî9ÚŽGswÏ1wššûs޵ãQpº|0dE³öçïÏï9ØŽOqÎÁvšš›{ŽºãSœsèÏãQýЃa¡:»ç0<>ÕÝ=ÇâñÕ眃ñx4ç÷‘Ç«9çè<שiÖœsŸF7»Y£kŽèãÓbó‰Õþ}$ìw±Ú¿•„-/Vûw“°ëÅê– ¥ž<»mOIÔmÛJS4ÿŽv²XíßtÆ«[ö݆¦Þ>¨*[h¬nÛ}3õö�Ö£·ƒÁè=¦ÒÐYópè­F˳xèÁ­¤Ö;7-&ÊS*äUš§6­ÅJyhÓXGƒ_":îèüêè„cµæ ‘6ôÁZúÝ¿ì¥ø>|ksp1}@׿0eõÁ]ç1dä¥ßƒž-‡§ãiS ߦ¦dRxz_ùŒSkäËл×BøÀ±fàBø ²f0…ð!åò§ô¨žáøêú׃þ»KÍászÇýk‚waËžpïWÐŒðÒè.½“Ñiÿ± [wŽù-¼ÇJ¿©Ó’Îo[ #¶äUžœp¼¥Ý1‡]jQ8þ’W‰÷0>ÐmtÇ—É«|ÿŽã2µ´Âis|&Ÿþ¸×?çðL^%- —©EÙûÇdjÕâ-‘‚[ýÇfj×ßql¦="»úÀ¸h‡ª©E{Þã¨MmÚ!Çnòj¥Ës<'o†á)GtòëÛɯ»åÐN>ÀXèƒäFÇZ¿™á…>DnÔCåï:×òìCäôûôüŠ‰Ð‡Éé÷ù ò«z ʯ;ç¨P^ÝÜ· ¥Áe¾i£4ómJ-ЙoOšµ€a¾]iQžs()¿`Ì·--ÊsŽ*å×Î9¸T‹ò†ƒLù”<9ùö¦E9àPS~í=Ç›òêz7ïzgxªU}Ρ§¼êþ%G¡òêÎM‰ýfºÀæ%‡£òjo¤B~C™w[‘oÏZ´g´ªM{Ç‘«Z´ço9tU›öŒãXµiï8 •WK¾;ǶjS8•WkœÓÈ·M õ-Íz‘Ï0Ê+‚åS^öŽnÞþº9ähXÞxïÁq±¼Zš)"üïáÄÇÅò©®ŽÉѱڔ×÷#«M=à@Y­Ú[Ž–åU£ÏE>ÔßÃÉ:—åWØáY^í9‡ÌòªØ¶¾íi(¯n D¾ jh±;Ëq²Zµ=“凘ç0Ë«;ç¸Y~Õ ÇÐòëÎ9Ž–_wÅá´¼ºëó£!GÖjÕŽAÊÁ¶¾# 9èÖrä}«=Ã-?¤Å^&?¤Åj&GλZ2 ßr¸®6íÇíjÓÞq/¯–;B ާ#òíx³êˆc{ùuçÚ˯»â8_^Ýã}ùU=úå×sÜ/¿Ž×ø¨“ËþKÔËe&jæf·%jAæ²ãµ€s³ëµ sÙy‰Z�:Ï›-àÛ߬E9àè`~åE8àa-ÚxÀ‘ÂZ´æSon/9p˜O{{‰™õŒ#ˆµëÏ9Ž˜O/Gì¢h®'æ¢p®gã¢l®§à¢|®G£Œ®‡ù¢œ®ç £¬®Çú¢´®‡£¸®ü¢Ä.çï¢È.Gí¢Ì®§êZp»û‹Z »œð‹Zлæ‹Zð»ÏkÁïrª¯¿—ÇãZ |y&¯Æ—óZ¼s:¯Åvz„-jôö¼]ÔêíÁº¨ØÛtQ ¸·Çý¢|oÏõE- ßà‹Z`¾œO‹Z`¾ ‹Z¾œ•‹Z ¾‹‹Z°¾œÛ‹Z°¾Ñ‹[À¾œÆ‹[о“Š[ྜãŠ[ð¾ÙŠ[�¿œÎŠ[¿‹[0¿œ‹[@¿ ‹[@¿œï‹[`¿ò‹[€¿œô‹[P¿÷‹[`¿œù‹[¿ü‹[п=á·x�ö(_ÜâØ3{q‹`Æ-Ž€=I·8öÈ`ÜâèY¹¸Å)Ðcoq‹g §ôâç@ÜÅ-‹[=Ì·xzö.nñô]Üâ*èYŸ¸Å]г9q‹Ë g~â·AÏèÄ-ŽƒžlŠ[Ü=7·8 zú)nñôüPÜâ1ð)¸ÅYà“:q‹£À§uâ?ÝÄ-n½‰[¼>~·x r¢&nñäTMÜâ,ÈÉš¸Å_0gWâÁœ_‰[œs†%nqø€MÜâ7ð!›¸Åkàƒ6q‹Ï 'SâŸAO™Ä-^ƒž&‰[Ü=5·8zØ&nñôLÜâ=蜸ÅГ0q‹¡‡vâ/BOÄÄ-~„=Ü·øö€KÜâNس3q‹?¡GCâ6BŽ•Äm…‰Û 9&·9rÄ$ns(ä(IÜâPà,NÜâLŸ|Íá}Ê뛫‹+r¤ãO‚�GïfÈá½÷Æ.Å€ã(ÖÕÎ{·ó£Áóà£RÕ÷à„Üq°Åšøò„-6¤Ç$Îkâ“þ{ôò%šò[޽X•÷~õž/V¥§l#æb]N÷á ‹u9­Vv±.‡Žà‹UùùÉÉ=‡a¬‹Ïî9c]|wÏñkâ³³ÛûžC06”WÕv뵆öÖjëµ?¿;ëÝsŒÆ†üjpÏ‘›ŠÛ{ÕØPا4lpwguuC˜—}ÏèÕœ_qdFŽ5ukˆ†îˆÐŒ5ÝYïÝàâôèˆ#3úu—¢Ñ§ëóuuƒ¨î’£56u·r]Ý&ª»äŽ …C8úGÁÑ«¡Ñ½ºË;ÝèÑõ9x£OqĽ<*ðXƒqÇq›ºÁ)Gpô)Ž8†£WÃòÙb µòYc EôYc`Šz¬q k„kܲ5B5nšǷbÐc[)bè1È­±i’+íÃaÓ$WڇæI®´‡M“\iŽš&¹Ò>5­r¥}8jÚåŠûpÔ4Ê•éÃQÓ$WÚ‡£¦I®´G“pŽ<ö0}8òXCûp䱆öáØc îñǦÇ>CHŽ}¶>û¬!$öYC:Hì±÷áØc Ó‡c5´ÇkhN<ÖÐ>œx ¢}8i˜„Ö ô¤aVqœH¦Ï¡"}Š#ŽÙÔ ­’†!XqÄa#¶]Ò°+Ž8hd]s®=)mØÀè`ƒ´iƒsmÞ´i†smÞ´iˆs.bÚ4űiˆsmÞ´i‹smÞ´ismÞ´ismÞ¬inݬiÓ¸YÓ2£eMKÈ|–5íÀý¡,Y~Äñ'} hš6ɯ1¥§qÀÊÆEè\ ”Érº 4Oõ\Éؼ¸ã¶n€M–qœLŸâˆCeú4—,³ª¹êN˜/ܓ؄°1¯nŽ9&fU퇤,,­W”\è‡Álˆo9úeM<¨NX’äg÷ý²!¿»çà—u9Ãߤ"¡¸ºç˜ Å]a6|E£¾äÿßqô˪n[Ú@Œ8N˜6°"^9™œ6°"tÑ5§““[½®^wèôºzõ/‡çƒ;ŽoÙTüæŽ[V½{¼—HXò3qY•“×…š60âéàø”†UÚÀˆ¬ès°Ë¦âö”£]z}ŽwÙP ùõj³¢Ïa/}Šßpà˦OoÀBVô9ô¥Oñ{Y×\²Qp½wò²6¤oŽ8ðemÜrÞz¥o.ÉáNȽS»Vú±RúHûA©«þX©WÖ-@=¯ÇQ1«rò”9f]zÆ1ëÒ;ŽˆY“ª›Ì11:ñ’Iés¡oEÙÀ|ð‘9ZfC|5àÀ™Mù-ÌlÈõ MßùNUõêÓ$Úç(™Y´Ï2óeÀ!3›ò[ÙœH9hfsåà™Íi”ãgzfQŽ›é™D9t¦g噞)”£e6:SShÇ™ÓÃiÅ™—œ'Ó£¸å8™ Å9®h 8VÜrŒÌÆ06Oo€7Ö¼r¤Læ|Èá1ëšá`ÈwkNo¤á»5ç7ÒðÝ­Ý;ié-ntlŸp³Jªq½‰’íÔ@pVyË¡7›Jk¯³ZÔ²ç¬5m`:£Û5pÕò}V²6lÀ;§}ÖéÑô9N§,^øJ=ïÌ ÜÔñÎ .M=(ïŒ'QÚéõGŽ8n§³^rØNªÏ*Ò½0Wy|ƒÓð“^rèNo /9NgCõÎ`á´YgLÕ@ƒFǵn@BèÄ"Í-FyÞ%Çõô?ï’#{úŸwÉ¡=ýÏ»ä Ÿ®îb80„‰æ³¢9-5U›¼½é_sÀÏë£þMïÄ*Núï߇ƒÓˆ#Öt  9hMG @ui]‡3~ªÌ7=9Ñæ oߪ®[×Ýô´0kº‹Ûs¹Žpc]‡]sÄ ­Ë±+H¡õŠÉ k\ÊMS”v 𶏽èpÄЦ<à˜¡Íª :0Ô£8fh]ýèN‡ƒ†zU õ©‚‡õªŽê1Š6ŒÀš€cˆÖk:¼ç(¢5ñù=G­gîqKÆL´¦£9ú¶ÃE=ЀЂ}´|À€|8D­Jߟ„÷£÷ aA0Q¿®ÏñDëºÁ=Ç­‰ïâ{óibŠúu}*êÓá»7Äõéø“7„u•dM®ȚةW´)ûZ´®,«GmÊ>Çõ*¹qâWšZÄUÓÜÃ;;¹æ £5ùÉ ËóšœÚøŠݦâФSW\Þ]²&¨kÞŸ\®9ø¨Gq2à�¤>ÕÝ€ƒŽ6U·æªÄ«â«ê8¦užKW·�)îÙIÃïoES7Íâ§u`T±¦nh.XS7ÂÅÑ=Ëë¸è³5Ózõ/®Ì³ëuÇ<jž]¯:4æÙõºÓ$ÇòzÍ/¥»¤õŠÓ2yV¯ø¥­yV¯ù¥­zV¯ú¥-sV¯ý¥-tV7ÀÍÉuŸ‰6Å´äfõÚC~Á¡E›òk$Ú”ÿ†‡Öä=”&¯×Ü|LÁªzÍ¡aE½ÞT5–W+mP7å?y½ÊCÓ‡õëÞqњΔÌ\X¯¿U¾ãˆ¢å{éÝNSŽvƒºÜL/ݰ)çüµf7 _û4|MÒÔ¼ãè¦> _“y4Ü6Ýܧákêu—²®9©·t¬«Û@ÊǺº¤„¬óØedÇ(%ëö~„à¤Íàš#”Ö4fŠÎ; sèà¥>ÕÝ€#˜6U<E#†©_%<‚ ƒè ŽÐ¦uÏàoÚT˜2 [°Šo×0©¤ŒMk ´M{˜ÕR=Ø0lZƒŠ€k¦1P¸kޤZטµqT½c¿°i‹[)EÓ·RЦ%n¥uC˜uVrÎ_·Á…ÚTàŠ¨n³úåQ½þ8ö}ÍAWkr^ó¨^oœ¦¾æ˜«5¹™kó¨^i(øŠzÍš˜Gõ:Ã!¸æø«U¹¬•y\¯ó¥™ó¸^g³ˆæq½Î$çüõ:_ªYãz­/Õ®q½Þ—Zñ:4¾¦^s^Xó:|oÖ¼Žß›…5¯Á÷faÍë0ð½YXó: |Ï k^G€$F!ëèïýÍ�…¬#?S!ë°å™µ)¿æð¬Mùo84k].Ë|^}¢Baë¨ï½ €¼úXÁWÔk-­VG}ï¥Íê¨ï} ò:î{_B€¼ŽüX7ëê6PÝ;îZÓ•Ð!¯£À÷tÈë8Ð(å‘u$X*ßq”׊ò^‰Mó:¼W:Ô¼Žï•ð4¯ãÁ{¥;Íëˆð^ÉNó:&¼·T§yÞ»<§yÞ»$§y Þ» §y%ÞWèMóX,ÉKó\,IOó`,)Mó\,éLóX,©LóRthLóN¬R˜æ5´X¥/Ík˜±J]š×c¶4¯È{èé¹\ý:†,•}úêU²êH²TâÊz¿%©Ž'UiìTÔÇW—ÇGCCè[ƒ”VÕç³® @æ’Ð#ïs€ÙŠœ¿>6WÄ^MŸƒÍVËÍù^à:Ž´:\W7¿#×Õm¡º>ǧ­õöó“;1S·Ž'EÉDÄu@):¾iMZÝ)¶õé¸òuDiu§î¶Ò{Êe´i«¶ÏqëZæo2—æmÊ>ÇÈ­+™t‰¯¬LGÙçð¹u¥9A«‡_Ð÷9°®W¦×Ç_Ð÷9øn]Ï ¤Ùðê†Mƒ‰Úܽj1æNÐ+sŸÊ\Õ´Îtwk0´ÁUÏÀb;º5­hö£»%Rž[¹¦i9œ8ïFM‹Éâ@À^ÝùnêÌtÂÑ›Jî¶Ø£»>žpÀ`¿îŽC×uç'H¸ÑQ‡Q¸Å¸ü‰�ë{Çë›ÅDwÂÞ¦4)n5 MêÍ\qãF}³jöI¢¸™îTÉ“¾¾¾ºëÝd6Ï[6%‡,Ö€?n×âˆÅ>Å),®k˜+™#û4½¸¦x;¸;ºaJKºîøt0¼é_¾ãÅuÝÕí壿yõ/¿ª†$!ž“‰ŒàD¹©?º¼Ž®®~uÛs!õG«õøû}AC ,­~Å©É*ÖæšÈw Œ» ³1TD¸:­IN²ª‹uMB†®HbfZ¯ŠP™ &:EÜWﵕòï—t_L¥¼AìWò…‰_ÇNý:f›¯Tðaµšspx„ü©Ë·Ï«Íê DÄ; 1X +Õ^Ï>s„ø©çUžÁ¦Â6åÌ*˜4å¨T˜6*7ªÔÖ0÷ÇèºõìFuZ¯‘ •Z÷/‡T¹¨Rë[FµŒTÈ(®g„0©eD_Oë!ÌjÉQ^Ïa¥r&q\©˜ÄÖ+•AÀs„äqD`劔ìÕ/~Õç n»$ì~—¾zõ·Vu¿K(Ë™‡ßEÈRJÂÃï2Ê2°‚èð»�YJI|ø]N’+Hè.”çoKQJ¢2 NU’Ñ}Hòǃc•ä‡IÔ=¼‰Þäo:opCÍS9*ןœhÞ88LâyƒÔdÚÌ(`L™‡6sL™dŽBÎüÇ“ÅZ"14OÊÕú“íóì¡u÷ËUe¬ú{'w&†3Íœq~¸ÍŸgOÏü¹G§›¯>rÂDÙ£‡¼>¹Û/ó’îÐ=L’Îá>ßòYüÎë4~3ŸóGü›rÿ|4šÎ^¦³èà”į^ýìãǃM’‡èêW¯~ŸÔ ¢ØüîÈoºl±š–—ýb4Ú—÷I ÌA‚?29FG[úBºÍÏ6ëƒ<í[Nþu“‡¯‹å:7KjËšòýµÿaúq¼yíÆOXWº‡ÅÉÝhxôn´Ó-hôu^‹`¼ÙŒ?Ë*3ñYª¬Xî6ŸGë&²¨îÅfŒÀ¤&{¬šÇÕã×ãÍxQìŠ -$¢™-Ö4GÒT0-è9æbZ0D;?ÐìØâÍ‹Oúa¾š|À!òE±x@ˆR[.Y±‘šØ§mŠÇbS,'…(R[ÇÕbÀÈÔ%v˜áElbˆJæÜ‘R?Ûoô.]«Ø?lV{ZÈEÚ"‰à«¡µ w>É•BÄý=ÎE]µÅl.^ƛًGâ”|A72 Óšx¶œÌ÷Ú5´•š-Ÿ‹Íl7&3`.·Òù SrYLæjßÕtOóZdb½#Û®Ä­,GTeQèZf¼|»D¶Âg»g²çb‡9\ûàdRP‡s;Dd«‹˜ðr“²®ãÝäY,e¥–[-G^€Q·ÞkWLæÚaÑL¶ÕzÜÌŠ%Ïä"@ÄËùl»ÃT^f»bxjv M>qÍÇ*ñ!Ô´«‹õ|¼37½�AÔêÚ—ñ|oÕ¶~»çÍ꣎>[EŒUcž¤SëI¸ÃÓ\1Ö°ÄVóeE´öHl]ÍœB˜S ˆ—f‡ÎD¬Ÿ”ƒcòq¬Ã/I]³mÉF¤Õgé|‰­¤;J̺¥7»M<ŽÓNã^F8ÝÁRFà3m›oœÚ*qc˜«ËÙÄ´AmRI›-9žÏÆ[D<Óil5Úo1_¥øšS„Ï3#L“¤ìîýëe_­9¯-ãÙµÌÔÓb;ÙÌÖè¼”¡Í”á-õÃ-uäb>e]ׯÃh…¾Û<YïtîMSgì—3»KÍXmk …«³%~wy;zÛ¿<>gqìŠ{Vœ¸ârèÓÙ‘1õf´3&7Θ¹ë«P™­ìZëI¥DqZöV:ÊÐUnŠùø“{©­þõ»þèÃxùÛ™ ÍN·ìœÐ•#ÃŒSÊÐE\äoþ3ÊPtZº bé>½º¹ÑÒ½1‹4'ùºÐ,Æ¥ 6k0 ¦ãÝ84+¯MÇi%d6-´óêºÕt ˳½E ë2 ç9L7÷DY§õ!k¸išÞ› e)itIUSWÈ*­éPÖgMDz,k:‡¿åÞÓ<(,‹Jn÷lƒ�â¡SÜb2’ ÈaYæâÓzCU—eÙÖ“¦Âb[ØÅÙ>‰|Ká›W?û§¦5Ç;2íÞÆÈ˜Mz4¤|sœ‹Diz„YýBpàòH«Í´àö銀š FÆ~>|¦q±ýPH£@4Ûi‚Б™\zûb^,¨.#^ßLsà´R‹(µ¥ü8ZO¤5( ØÍ‚\3к¿?)N"‰ µP 9QÁ¥)4iÖ7i ¾ÌÈ<³ùl÷YZ‚dfV—fàÒ1Z›˧ݳ´‰ ÙiZP’ѬéqG,hgç¥p7ž-- µ"„èÆû¹æ´>J úAz;Z­Ñ’ã¹@c7šÈ“™ ö!!-¦Óý„le¥Ä„ùž d!™¸F<*âÐVž–¾Ñv²Âô»íJV™0H¶_¯íƒc}Ìø²ib[mfO³¥`(Œ©é­"ž ÆS¾Àˆiõ}œMhü€2(bÛ v€EÇstuXö…z:y[k“s' 6¦þ¬8ÓŽˆ$µ-0A€óù~±è B 8Á *âVIºŽHñeZ銦‡§Z)ê3+ö’R­Rñ‰fk´fªzÄ’ÂUÌÀ2Æ‹©Ö„Za óPE&œQ+±O6+ê5+Á2Ö-Ü""ªö‡55EjÇ}ñ„‘Šà§"0àHL•eÙÍ&0Â<5BîÏ™ÖáÞ­3­‡BBÇ}Ë´N/³Ín?澑iu^vœ3ˆs7ÛÓæ,£þœi¨ ­¨Ï°¬ë6»½:¯Îg¦Gçe±3:AðÒ²"·ÃÓ1KË&[²#”k½Ø!Ù"8©Ø‚j·^q'Éí AÅÕþ•g®ûWž»"siÙ¿ 4Ã3»¶ü³å˜Êº¥ü‚Ú®82±Ò$›#¼h-×ìi‰è¢ÚcŸf²#‚°¢ÒKfÆ?•%¼k{ÚžÛqD¥ÎÏ›b<Ýš'OCT͵žÓ8Þ!r¨¬7¿¥ÅQkªÖŸ’#€8¡•Õc<çРR&øª“Ød¿ÙÎ^ ý©+àÓr¼3ù´* šy]G Á>.â®fê,Žešã{j9÷ ™¯ÖSfa ´6™¥5°Ë¦ô<°¡ð<H#Û©?rWìVd@죇3iئb­$óš>qõÅz6_=•Ê´qßýr³B¤ézÅEÌÜ,ÛÕãŽ@ÖÙº@¤~³¦å0lãš[¹5ÏÛÎæ&2ÂBÛÊ,¶«„¹(´íÍÚÉ|EO“$móòy¼•uq Z‹|F‘òÒñ7iöKrýh&ã.똘TÔÈJû­>‰ñö•’ÃWš`’Ú:ð%m¿0w›Œ°*Þ3óMh{©7㦳­wœtÇóðÙyh‘éiÝ'<òBŽTa剣—bƒ‰h£êU@”0U—ršˆ¦ô¨'\$¸+-8«eP –‡«ÁÔstBߎ\û†cÈjVt[º®Û\3Û3¨>™‡¡S›XÉ[*1j†Ne]èb“g</L+zF_å*†NuDAã7æ¡Å^ÛG³ã€#yvÝLÌœÇåÐÂle“o²"ô«K³š~Ö1[˜ÃR`€<¶ðŽèKa†nlQ¤„’ ÓÃÐ¤Ž†ºCU™5/ƒ8÷^M×Ñ`¼mivœÌ÷FkÑ´,Ǽ,ù6Å÷{ò‹8_àä/¹™)t2­¦gRÏc‹—*~»é3±Nï/£ÍnIæ­§ 5q^M­…åýÝ¥»Æ¾ÈæKÞu Ù’Á‚òØkë_æv°À-Ÿ3¢ â<ÎéVœ8·Ý‡sË´IrêBo^ýþÿlÜ=í ä÷­Ø‹Ù建6žmr…þL³ˆñǯ®ÍìûmÓ[ãîi:Ü‹«g[qôTïÅ˳‚­8y*È÷â[ÁV>ìÅÛÓôV¼=”y¿O‰ ö©´€«ëãQŠÉqñ(µý8^‹oG)rqÄ©£Ä'c†P‹DkޏrHÐX µ,x?jA³å~+.’«©¸iHìçâœQbY<ÉV5ôäH‹A#2ÒR¬i ˆ[&©Ñžë.[ѨÃó\ö 9¡>›IŒeÏÂ}µ(¤‰µ(Å÷²»L¿É?޵ Oº£L¿çêÍá÷Nö޹âÑáyfkqåk¶ëÈŽ°IâÍ™T(NœIEâ»™T,^›I%⯙T*šIeâ ™T.™IuÅ“§wÄ “d ˜$Cñ½$‰Û%ÉX|.I&âpI2_K’™¸Z’ÌÅÁ’dW|+±BG\*IâOI2oJ’‘xQ’ŒÅ’d"”$Sñœ$™‰³$É\ü$IvÅ1’ÖèˆS$É@ü!Œ‘â©#nIâÿ˜T(¾IEâ÷˜T,.I%âð˜T*®ŽIeâã˜T.îIuų‘§wħ‘d ÎŒ$Cqb$‰#ÉX¼I&â·H2wE’™8+’ÌÅG‘dW±BGýIê“H:TDÒ‘:$’ŽÕ‘t¢Žˆ¤Su?$©Ó!é\ý IwÕÕvé¨!iì•jù¸‰;[?p#v²~àfì\ýÀ Ø©ú›:°Sõ7v`§ênîÀÎÖÜàAÐuÒT~;c?p£¡[>jö ¬Jº%¤¦B·ˆÔøAè–‘š?ÝBRB·”Ô‚Ð-&u‚ tËIÝ ˆÜ‚RG"·¤è ‘[Rô…¨bK*iä–½!rKŠî¹%EˆÜ’¢CDnIÑ#"·¤è±[Rô‰Ø-):Eì–½"vzå']o’ÈÀÑêËüŸ8@½.³Å¤à¸ô²FfúF%•%V¥ZÚ%o»6¬÷Ûgõùv­ÀvGÈáçËtÌaçË4|"8/%_m£9aåÝ7·ÙA›<Ž9ؼԎ©‘]Rf ³3!›®]\¶»1ù‘*µ³¹xò¡í”êȇöù —Q_·xåP“ƒw¡mJ†ÊØ, Båp¬¥Ê–R÷KBÛÄì}X®>.gE÷¼“Rú8ߨvÚAãˆóƒÐ3†=f[‹ú:ÏsÝRAæÐ½F� ‹qŠGËý‚ì¾yõ/]Éù è!ΙW=s¶I܇^Ù´*À³blß`ôxÇ}^|’ÃS‘Šå,•&Íé°ƒ¤š6ÇìRê!²ƒ¬.1sÏã'³ífÓU…¼”—}5ûÒ¤7ⳉnŸ¶>Åt¶c7¡V²ÇÙ'ˆ£FIDak¦{x¦$­áíðÔ¾Ré•ï‡C[‰Z¯…ˆ»Ý&ï¸yÀ12k»õk‚ÐΠ¾‹Œ:¨_õ¼FgžëÌ*ªš}ñÀ(j×›{ÇN†÷G÷j(õÉGSwESo¹È­þ«eEçV^0v«^k+·Ú† kå«vh™·Ûô¨GŒƒ¿¯þPî–nùžaˆ3{a'ƒ²—s©H@㠙ؽZï—“g3¸H³ÛŒgóª*ª]´-°²““Yî5V“ x;S<»µ[)[Âeë]žèŒ™� x˜ÉëK$ÁîærfŒÓÒ« h‡ÄíÔôÔwòvÔœl˜-ÊwœCœPdŸùæ„ÜţŸ·äV™‰‹D“Õ\DnôïËÌA¢ÊÛÖÌL]ÇǽÁ€&ýš¥eº9£´æ‘ÁÊtöãð‘Í;s÷òdõ 9?á}À~ÿÜÜÉâ“9„bî É÷xu‚c,æÆ ½±¾S©Þ¸+7¾Þ÷‡¿¦›¼ÀÎëªP®ÖÇZ9¶ß­/£_=›ÇͦæuÔç8ñ„þ ¨KPßÃΉy‰÷k“5ôgX¾v.œ-ËK¹bÿØ<ÉóÖoˆ£¢xèñ1Ug³Ð‚SR7üÃתÆõ¦KS²Ú›HP›!I‚…xS,‹íxK>y™ò‡Õf>^Ò¸owŒ?fQžŠ‰,'oGý·£«u±<>×úŸˆÅx»Ò8±ÙÒé_žcOAÓ‰áH¨‚@æí#M,nö°¡U ›±+“ÞQÒèàì!Ôš¹âXŽìt^KÒ,øßÈ¥\nZÝð2uµÞâj3Óœ_Ì+2à‚|'«õgSj¤ÆÓĶrX—™wÞ*Å@~E»}S«²eñ4æz‹YØ5ãÃxKë¯9p’•eÁk¼­­Rž™Ý’uÝé‹Þ† :®J_Ⱦm¸ÊÙvŒežL÷Ïš¦û¤ÆKÄx=Üž®û~Ïï�äN=¾“”*ä}íÂX'rsJÃÒZɇUuv­v"ˆxûžFá”WeN†¯8§@(rªY­KE×Qh ™šIË„Z=ÏÊ{´aPÓÚ‡a³´ëÕ9cê“­ìðU³Øeŧ™ä¨åØPÙÙºÔV8û+›’Kl‰8²‘§¥[í7cdªÛ'³x¤„\5”QuAú½ÿ³œ·Ä?á·CGçIëô¨ìþT™aHjØä*Á™ÈÞlšˆ–:Ѐ9qàuóÆÁAè‘Êa¯šTN|•…°ãÚ ;§,¥&UÍ~i,*¨š †zÏÄ©ÌM>d¨n)‘38fPÈHsŸ%r[=zŽñöš¤¼yÕUvã½®À»^œSA ÛjºÂ´Å(£í£Œ$÷væþ‡³ÎV›ìbˆušBdWî”)´5·Nª}¦= jM`^À–íœËþ6tXÐ>Ζ´Sk½è cÕŒ7O[ãìÇ©+ç†#õ~gèü¦óË@²§¸†øà‚g½£Ëw£ã¼+³§d’Ãï£é8d~cÙEçõ¸”®Vó,–ÁJòD–HN 5ÑR–e²8²nGÖÈ®Çx‹CíZÑŽCø!úûã—±Îõü¼nWú »›HÏ«<)‘^guÞ—~ÆÉ«‡ßK³i§ºAYîÛëcž‘<Ñ)™oúy÷¼²S0$ïV:±"¥½$ ³´¼EãØ°©øl½miö}˜Ë%åƒni‘´[Y¢G‚‘1«Š±wV„®ÂX[4‘«é_à<ËcW^)NÆ€êOM[Œ'˜Xq&„ ¿n3ÄI_÷/O¯dª7½Me<]™N§"s®\\UŽ%¤$*%:]mÐõãýÍÑícitGE1áó©UŃFžtø³£¿¡òëÝÒ€sÿm•ö—椘ªCVÿ[ªî}š|BhtÌS(Gd¾ÄúßÿM²@Bàlt{s<º¼ÝôŽW—´^°àôê¦×w9êÝ÷®‡ý«ËÑñÑí»³!™Çè†Gç£ë³£A/õnn®nÈJuM šèµ<äæ‚TƒáÕ5‰EpÒ®NIttüK£,;;º<9ïÝPÂ>5Âþ%å9?ïÁ°w?¤‘Êb$û—·½Ñíå]Ÿrc3©4ÎM1Þ¢ê«)¾ãJ’Cªð«WŸqŠñ’œS|­•¤”/;ü6IòÃÇïjƒWÀvÝÃmörR1‰Ì7]®‚oÎKhÂ7âí¿ÒM]YÈ2¬ëæ3²¿¥Ï>2'GvIj:Æß±f·’’§Áa’†Tò4:|¤ú¾zõw5Ópƒ³ß&WL¹äJ%×·¶›-N‘Â54y3Ê›#oWòþÑh;/¨|£é3*KŒNºw…ÔGƒÎ«W“=nÅ› Ã$ ÷9ηÐ4ÆÌíÃï2ú¤oöñÁw¹HóêžÆ„[Ê™[ʘ[äì‡sÿîã´Ðóô”èp›ã„9¦œd1•=K?P;šËåô§ýNï›7f§‡Û0^O¯KAwæ}*—L5iÚrø"‡ûxKŽ'„f°VÖ=üäÃO[9]OÐÜŒ÷,„råhª¼#ÕÂIã$pµ|RøFºÆAèˆÿq¸·ÆË'Ã8oÁ.Ìž‹-Øe±{*‡$®¢×¹ƒ¯ ^‡=‘<;H´Ò—˜À’<EéRjŽôÎÿ ç¬óÌk@°s¤D‹™bÌ™µÔ[›ªZØ7Ïœ2œÈ^Ò3…Û”ÓÐ BÛnJ³IÈ1(`neÞíšáõó½¦ÍŒûó=ïÂSÚL±?ßj:’ü8f„tÌÕž ´Ÿa¥é&(œGà.Òq¿‡£Y¯b %çµ8. ,G¹²² Ñ¥¶¹ï6ÓbŽînïDþì‘g½.Íf9 Æ?ždßýCñ™Çi×|Øú'¥fÅA•³ê_+U‹=yxF×eÝß-uö¨e™+í3þ´Ì¤à°¸é½{ÃÑûÞ è(Å¿iö?ô°¬”"ú>›îÝ_ƒDÀ¦ßÓ""—Å51²&¯Í³Nz§ýËV²FÀå €S*]ÛyßÈkË�nÁÛWÿ·ñÈØ¯€Ï ¾YÚ¡9&ò/ÊÒN|8ÞAö¦ó&ˆ“7i'A ÁUë¤4d‘ó97”àx„Õâ4AuÄp9ü6íä4CÙ1Çÿ=¯>rÔiOv£8¦e¢xA�\±ˆGJéºàîÝ(KU¬%è"Ø·ÈäÁ$‹;*3Ï I–éÌøûº,œÂö¯viÐ9Lƒ�ý9HéæÛÿ‡¼·Ë²Jׇ×Âû†D¼UÞTT42ÆÈÐPQÉ!%£$%ÃbŠ *RRj˜†ÝfÓ0 ÛH©È¡†2*R2v›ÝŸÝfr˜"#‡¦¨cŠi¨¡Æ]ÿó8¸×YÎËÞ_žçùò|Y¿ƒóº®sk­óm½Ýð_ÝHK’E:Ú íþëhÂDµÿÉ ¦ˆ£ŽŒ ãÒâQÑÜ ·:2Ãb2…Û±µ–¦K;|ôµ#ùÚ§-:­¦xgáUE—6¥®ØyÕ÷®¼uüoñºò7Ž&ÞrªÅ‘âÔò¥ãŽH”á¸ë‹o–:r—.Z¼r§$ÊTƒE眢t:)Rë Î@—³1ã-ï :þ÷xÇŒû/ü¦dàË—®?‰6§¤Ž»*õ¤Ë÷õŒÔåãþòêïfÕKª¤g-–w–Âç/¥‹¹êqzé¢%ˆÓÿOGé_ÖP‰DÚ”E‹3n Úëãcû •>+%িn§Æ›»”ü:ðæÒEË´¥Ë–­·;KkÕ¤5‹ñ¯xDÜ¿Ò,>IoˆLr ñŸ„Ná¶Xž#‡[Ì&E}³IbËÅ,–³TÓ²’ÜKäÁb±—ŋĠyø·yl.ƒ‹za7•ž4[¥]+æ)1EÆ/mãÒÅÈ–‰ã¯¤í@ü™Ç÷ÝñøEQ^vfœÿ~’ÁF„¤0>Â%þ¯Y#ßÀßáãGYy§_BÂÖ2qé¥Ü.×uòñ\8âºm;·q¨6ŽËÈ¿ßÇÿžêÖ…¹$_Kñ“®Ý‰?—àO4ƒMvHž(õnÛú½+wìLÛ!>Mþâñb÷WìÍß»ò›>øZIñÆ“ÐI߬DrRDÙKKz²tñ2I½–Ë;þSÜÚíZ5¾“¿½D¸¥°Ö’o4}ÝœI<!Œ¾â«Óù)þÝ’jÆÒsVŒsÑÄ_;æ¸>߀8ñÚëvìto…ïÜŠÂ� J;]Z?¾ë–We¾0>S˜áº>`O,½±äÊ%ø_V2gø›Oê¼píö×l-tY‘èÅâ@×FÈÄݺŠ_žØ)Ó p©|>Ë=7óBÐè{j#ßN¼j|޲ã”ùÉÆ«®Û¹õë¹Ö)µM½‘Wë‹®ÚVˆy;?„ôèõ¿ivÄö›q©^c#ªž4þ÷ŽÀŸá;¥Š·—Ž÷Átr‘áâýš¯á×.$[ÙŒkO¸Lè¿»Uuɉ½nkéºikÇ~c|�7ž2!—$_¼¡ŸmÿŒ“×ù9ò&Æ5æâùÈ)ÆÄ^‡+×b-6%òœ“¼rÁY‹Ne>džð >ÙųM›Y™˜ü¬E§Ä}ã­<·¿ ë*ç©>HqÞ¶kŠ·c³6°h>Î!L£Ò©ÜPÇÆ­ÅkàSƹ}ói®ô÷_?ýc^»ôœEð RKÎJÆ¿u»~âm×>ÌÜzõÍßÍÚ¾ý†€Š„;MÚxJãOUA$º7¥´èº§Z‘_4ã¯ì*`Ç; 8Êš?ãïS€÷¸Õlü{VCÉ"¿^¢Ø)äÀÎ9•ŠÀ¶q+q!ÀvÖ×í.ÞZúÍñ{í–5â¤p‘fk້ãˆ<ÐpþñW}k|špŠ�{åø5®M’hš:³x¤]8v€K@Lõ­DjŒ &ÆŒÏæ®¹ææo.æVS.òdÉ‹¯Û¶õªü·—ã1®G^C‚Î[fàã›·¹È¨(Œ§”Á-#ŽHv-n=9®TØ).ÚzU‰óߟ@5®¢KÇ·Kô3|™÷øg“œDE$…]±ýÚSì¼+ñ÷Ìÿ-þ¦¢¦åÈë5ÚJê<m£¾‹½Ê뾑½²yŒc‘]#nâXXáÍ7Þˆ`³xוþñI÷Âëvî¸jáxS´úüMÏ]˜²<$°ÓGŽ“–‘$ ¸JuݵßwGïp”“ËÂBä¤I`⸑½!oSP‰®ãýã䥧O(vŒ'ãÖôõ«º 0þ»ªãõ°§'C= r„䔀V/¢h¯œ:øaeWR(ÁßÿÇŽ«Õ•;¥X2^ Ň ã{Ô|qZàÀzÅø;SJ 0S¬n@¥NQžTž(I‚®fÓ @’ÞÀ'~M™ä³ÅxÉxˆ Ü™Ü!ÎÇSeNmÛÜ+uWêqãž8Þ›—,Ão@/nÐtà ¿Æ3ÿ.Ÿ�‡è¿~ ‡8ƒÁ_ñ·ßçH<î7Æ_‰û›W¾ùtÞ•<<ú=îÀÿþ;ãOcÿö©¶ôqíʵ™çI¨Á°øÇyoD¶¾€-ÿv™ gró¶[·n hÈδM¤Ný{´€ýpÁ#횕׺Ģë )ºÄ² dz¾¾QvC@Ô™õèæmßx8#ðÐ%2¼ xæç{¡¹eê°À·— ¨,HÊnÙze ¾;eµg\HWôÅxc6ŠŠ§Œ vÊ‹_K†ø5.XéU;oç°i ìkjàuÐÿjË#eù’¿»å!Ó´¿³å‘‚eª¼å‘‚¥ªÿqË# Vºå‘œüÿ£-‰ÌÒàÿ}ËC¢÷’dL%Lÿ[XÜúÛ-é߸呼øo¶<dôÿfË´ÿiË#yé?Úò€¾¡vI26(’ýý- ¼KU—HTëoyHä\‚¨´D‚Âø»§nyH˜øÛ-EËþΖ‡„‰ÿW¶<ŽpKRñ~ãîPX¾r‰¸±‹SÆó{±šño³s¾&ŒçX¥ü©)Æ ÇÇ'7—¿sµ´x1W#kKd üf×"7Nã\¾¹Ê†ùÔ<˲óºÒ¿~–:¾œý¿,[HºN!¹Ìi Ö)ø7<â5¼K­£vK²d¹ÌðÜÚçi“Æ%o/ê#>è¯ÕVø/‡ÇßèIÔ‚aGœ8]×–`ÕbÉâ¥+Ëvþõ  „'whAc|Ûi›1çÿéK®’>¥*.ß>þFÅlaçø¶rßš(oÝ6þGþ(Ç~csDÍäo¢ µÒ- ˜á7žA· zO¿¾0â &Oð›àrcì[x/¸Ó¡‰Aòt1žN|YP Ф§-Ý0MP«p9ˆ÷&½¯´!¡5ƒ±Kh© E¼ìÐäW¥ƒËÔx©ã H05IhaÄ ª“Hô¾vR¸ü(2Xž¦á‹¨8˜'ñÅßCÑÒófÞ‹ÉsÒÇZhò‡™qh3 ÕÈ2£ÖõËÌL ;õàòwQ¯0¾ïÅáÛµà7«ÁÕ1ë„в@‹/quÄ"´õ¨#~ÌÕ1½–‰÷æö‰ŽüpžŸaÌìã@ j$×ýha‹x´t Åⶆ‰ä‹ùï�-ë‘A¸ãÿ{”Zæú õviG6Ú‘6èh+^ÚE ­®wý²ºÃ¡LèÕ;DÒ¶´t¢~ùv+PV¢ãr~²"ŒåµàwáG‚Š€r‚msèâU‚®º$ÉÕvI•ãrÉ1÷Þæ0AÅ@yJÛòªC—íRÔî¾ýN©¢A'ýSÝ{Æ=-xÇÑ®Jr´«rœ,W¿ìž^=äž^ó˜£]Óéh[K\¿\[âj+jv\®Oqè†F‡nph›Ïq¹É8ÚMqŽV 5ÈÃÊJåiÚ-¤ýè{Çå{-ŠŽ—Hÿý7‚?(tOÐæÐíb¿«6ݯh¯|»Vq¸äËѬ*ÀÓ;g -´]÷~ =( Òñ¸ë¸Ð®Æ{?¹LQ¿«ã'ï;ZõfE/9tw–{ïn}z·~±Kâ)$ªqµíêpòÝS¬è{¯&Ú}{/h‰Þ” ´žë2"Ñ´£èÓo ôÆ6<½oª¢Û Km+ð^m¸“¥µ•àéñç<ÚQÅsŽ�Ý/œÏ©%R½º_´îœW€êêÝ{u-Šä½sŽ=ïÞ{ EQ–>ÍWTî¾} FiíúE{º7RP%Q¢"ÉÆÏùÑÒŽhÇÞ»•U³œüö×§2ªióAÛ·Ph·€¶o£¢k•¡ïoªŸçžÖ/V´J¾-'Ú/è6 w ª ‚NÞNÛz}ÿ¬ç÷à!±û"ØÖÅD¹xÊ÷àëþ�ôðù¤\†ø€4é¿UÙøâáJ¡}H$^Ê~Lq'¿ÁS|ѧDÐÝ*<m€'¼ èèA5Ú¶‹½QCo¶›ýRKÜ´:YGtµ ½@v9¿ñ|l=hÝ&è!"Õàj<m w¾©I,>¸ŽHâ çI¢ý—Vzþ›Hü•‡QüqÑ5uã€Dbω†yW•*ºËIu` ^"ÑÝU¥|*^%8èçb«¶O›TÍÝ·ÍW¸o› Ý·ÍÁ¼ìñfÈü'"7ïçD}Næf±o2Ð÷ô sïL"±ïéDÙî‹'òõ‹RדOH&̸ÿÄ!'Õ/:©žèrR=Ñëdy}:‰Hú/Ø Ô²Ø}Û²Ê}Û’é¾m‘Î{)Ñ1}︾'Z¼jž>%¶›FTï|ØÓE~E Š’¥;ôLžóÏHŒŠe{ŸVšúûV¿"õü­)ŠÖH­‡|­2žã¤I¤ ‰zVêð 5»§Ï¾èÚö¬ŒÑª5 ¡K=³ˆÐ!Dåî‹C­Žß!é {¾=¬­<¯(ÅÕv¸À}{¸B¾]@Ô$™4ÇãðÓN‚â/ž[Ak‹wOÛR”&úgŸF´jËsY_[©“¯íNÇ¥­Þµ£í€ÒzœTmŠ>qOŸó¸ö>7Uiâm+БÇíÈqgµÏßåP»ô¸i ‚ûþízADhÛcDÈc›€^˜!è�æÍ@ÿŽØxÙ-ïßrïuÀ??MΔª>öÑ«î½ÿ€Ïi#·G€^DmíDˆÉ/Á+wÁ;¾Hô…ãòжN¢#ŽÖ¹^ÐKDˆ¡]DO»§ÿ‰>x™~­›ß%:áÞëŠÔC‹z•ègQ]CÎò~¡žð3Ü{¿˜ïÞûE’¾—¦O×êÓ,} ÎÇHCŽùh¿DõöË1g3Ý/9.¯@¾ãDðýеWÀïûxïø¿·ù´@iÒv¸Ú^—â,ëôÕ;¤íÕ:žÖ:^T~ÝÊï˜òp´£«—£9.GÁeèWÙ®=>§Ù=kÝ{=¹¾ÿõ<§“¿Þë´î×Ç^½í4çÕÍN_^ÝãôåÕcn|_ sœ_»Þµãµ½®¯^kt2¿vеíµv¥½äÚöÚQ×W¯õ¸¾zím×Ê×>suôêxôÎvüz¿ÞÅŽ_oºÒö:.½—×ç+Jwžáõ[•V¥´/]mÇ2\î} öñ>i°²! 7–º:ÞXë¾xc³“ï Õ7T7Þ(UÚC®¯ÞhRÚÓúm‡Ò^Öo_u}õF¯ë«7ÞQ ¾píè‹•o·ãißeîiÆíЛNKŽßâ¾8ŽÌ|˜=þÐoàÃFˆç|Ôý%‚þ}Fô¾ãòr1"Œï@o#Ï9IÏð%‘xkˆd6kƒˆDzëú­X² &ù¬H #OmÃdÌm‘øIë']³S‰ÄÓØH ßIžh£‰$s³±DâÇí "é?ôŽX…Mt½kï;x/žïÍzw¶ë¡w/sz08Ï}1xµã7¸_Ÿ¾ã¾xožã÷ÞK ’÷ì|"­ã}Ä·D"´cÐR›M"’~¶ ‰u¹¢4$Zl¹zôûh72¿I-ב~/Vf—Øãä;!úgS‰>rºûa¬Ëª>œç4çÃÅn|?\¯ïIY®}xÄiñ‡Nc?<ê´øÃ~¥½ï´øÃœ8â´øÃ/]mÙ«VÃ;—árÇeøNÇe¸Fi9ù†8Ï?Üâ<ÿGs®U}$Ør…ê£Ý®‡>úÒ}ûq˜ûöãH÷í¾XÚˆ´ÒrEé œ>ÿñz§Å„–p­êâ¹,W¨> vœ?‰s^å“·]Fö§`§Ù‹²\ãù“´×reçO{œ-ü ãÆ5€Q±#ËYïè|g£ÐÎGou0*Än&’·œùêwÔ§C?Mscù©ø+˹󧺪öén<>Ý£_Ð/ޏ‘ùTÇüÓ£nd>íSÚgÎ?[ê,ï3Ø*×>Ûï2ÆÏ¹:>ër}õg´²�´?£•œùÿù6ge~Áiñ˜zô± ÷íX¡kÇjãúÁØíÎzÆ$§±\ñÛíZ>vÌ̲ë*|û¹ŽàçñŽóçK¶žáZùùzןç)íjן_ï´ýób§íŸßêz÷shW¨>ÇIðh\iûËZGûËŠ`µ\_;©cyR-ôK‰y–«_mv:þ¼×2¾z?Ð^kf+ÚðÙÖ4ÛÚ´€Þ[{4 ã6(, ã6h}@wmÐ=µA‡zjƒ> è³ÐS;am@Oí„+zj'ì è†p, Ö350ªÖ³>0ÒÖ㢳õܰPë© Ì·¬§>ZÏ¡€Þ[Ï'ͱÞ]³Þ’€_³^<ÝAÚ°Á›šmƒ¯ Œ¯ . Œ¥ ¾50¾6¸Ji»ãkƒ÷Æ××Æ×Œ¯ ~ÑI|\ë8¡üF•ß—Ž_ˆÏÑBR\ÛB\ÖgC.r\B`Xϱ¾ÙŽæ[è¤÷¥:.¾5®6_ŽÒ.sµù®vÒû ô¾®6ß]Nzß~­ãiå×®ü^R~=J;é¤ vüB£—PØ VŠlØ;NàXe²#œ^M„FÜJ´ÖiÉDé[Nt»Ó¡‰’¡ØÛˆ^vZ7Q²%‹õ&ì´.œo'R-¿J¢ÎÂáO±Rd'Á+W9b#j]Û"ºšŒÙ6ÖŒ¬›k¥—³2sÀ#YÿKdýÇ\¿ø]eý_¸o§ˆuÛj¢#ŽË”¾€o²SÞq_L9ðMvªÄU‹*ƒ¶Õ}âÞ‹…ÆZ•¾&»íôîÀ,ÁN?îFuú Áé¹Q>æh3‚†Íð+m†ûvF¢Ò\>ng¤9 ›‘î4lÆz×ò™È‡j‰ºÞ;¨èHÀSÛ¸]Ìz:à í,ø&¬´ÙÙQ×Î>ˆ’vö èbg¿ˆvöÑ@·³b£ýv êÚÙп:"ôä^¢/9’ä|Óåíd‰ßëÐÜ®Çç¾ì$˜·ÞI0ïe÷íik§½ìêMù‚ÿz(a@ÐoNÇ<ê6¢v7Zóá#vMuµÍOpz0³‰»‰Úô=õCó{Ý{gˆ¯ºƒ¨Ùq>cØ}q†›yÙDŸû"q‡ÓÎÄýN›Ý˜'rzØ©4—ØÄcNûœ$¾ïô Ñåaö[ê#¾5ßñûV²ã÷­4Çï[™JÛï¸|ëiÇeÁ­ŽË‚NÒ­î‹/8Î º•æfÛvÁÛNÒNÒ¹:ÎtY=Sýî™ ¿3Õïž¹Æñ;3[iÎ<â¸$)—¤‹œ¤I.°IW;ÎI’nuœ“nw’&U:I“v»:’h/j¯*¿~å÷¾òq´³.r\κÂq9K¹œõ‰“ô¬“î‹…ÁŽóÂH¥ÍvœÎw’.Lt’.tsg»Ð­e؅׺:Þ¢ü*”ß]ʯViŸ8.g9.g+—³÷8IÏnp_œ}Àq>»Mi/:Îg¿ì$=»ÛIzöq­Cýir˜«#Yý_r‚ã—¼ÐñKVHÞã¸$?æ¸,R.‹Òœ¤‹T³]ä8/ÊWÚµŽó¢mNÒE%NÒE·¹:íÑ:jª÷‹º”ß«ÊO5bqšã²x½ã²øãrÎ÷ôœ—•¦ñãçhü8GãGÊ<×Ê”$¥-uߦhËS.rߦlv­LÉs­L¹ÖIr›“/å'Ë’©®Ž%qŽßõ%¸ ­ru,YëêX’åêX²ÙÕ±d›«cÉZÇå§zµä€òSO³tªã²tžã²ô„ã²l†“t™jÎ2õ%ËÒ•¶Öq^v‘“tYŽ“tÙ®Žen=Ç.Û¥u<¤üš”ßÓÊOs¸ÔŽKêÇ%U¹¤¾à$MUÍIU_’: ´Žsê'NÒÔQ'éò WÇr7w±Ë»:–k¦º\=æòÍŽßò¥½ \Ž:.iÊ%íj'iZ±û"M}IZ¥Òv9Îi{œ¤iuNÒ´Ç\i/hÇ´Ž÷•ŸzÌ´/¿G[qµã²b‡ã²B¹¤k.•®¹Tºú’tÍ¥Ò5—J×\*]s©tÍ¥Ò¯vu¤kôK¿Kù©ÇLHù©F¬Ô}e´ã²R¹¬ÔºR5{¥ú’•CWj ]©1t倓t¥ÆÐUCWiô[¥z¿J=æ*¡«T#Vi ]¥1tµrY½ÞIº:×}±ú ÇyµjÄê[çÕ·9IWW8IWk ]­1tµF¿Õª÷«Õc®ÖºZ5"#ÚqÉXê¸dìq\2^t’fhÿesœ3Ô‹f¨ÍøÌIš1æ$=7ÌÕq®ú—sÝÊŽ=×­LØsµ7νÂñ;·Hi»”‹¶wæak42­Qÿ¼F#ÓLk42­ÑÈ´F#Ó·¢d×<¤uhLYó’òÓäšãÊO{ãÛêO¿­}š¹ÆqÉ\«´ëÌ™n½ÝfÞæêȬVÚWGæCNæÌ'sæAW[æKÊùeEïk½Ÿ9ÎçÇù¼0Çù<͇ÎÓyüy×:.ç½ê¸œ÷™“y­rYæ8¯UÚ<Çyí'óÚ$'óZÝk1Ï«'ºÌÕ¶V3øµ·+gíµÚk”ö²ò;ḬsëÀvÝ-Næuî‹uêsÖÕ)m¿ã¼î€“y]³“yzôuÇ´Ž´Ž/¿,]ÉšêøeiŒÏZã¸diΚ¥½›¥½{¾öîùÚ»çk½{¾öîùÚ»çkçkŸž¯}z¾öéùÚ§çkŸž¯}zþQåâölìúË—õw:I×»5n»~¯ã¼^}özâë8I×·;I×këuua½ÛÕ²D8~hË/Ж_ ¹Ù— 4 Ê^å¸d«•e«•e«•ekdkd«•e«•e«•e«•e¿­u|¤ü4‹¼0Èñ»0\i©n%æB£ u.~¡Ûiµw2_¨^åB­ãBÏ\ìê¸(ÂÉ|‘ßÉ|ÑlWÇEê‡.Òµ¹‹4â\¤>ö¢ʯ\iše\¤±là ÇeÃ'é~ÜÞ¢ÝP¨´mŽó†[œ¤Êœ¤ÔïnP¿»Aýîõ»ÔïnP¿»A{('ÂqÉÑ8“s»ã’£ùAŽæ9šäh|ËÑü Góƒœ'iŽæk~p±Fö‹5?¸Xóƒ‹5?¸X{èâ[”‹®^^¬¾dc´“tc¼ûbãÇy£rÞ¨œ7®w’nÌv’nt»ªv£æuïÒ:ÔV76*¿ƒÊO眵½uõcÓŽË&õ›Ô lRÎ›Ô lR/°I½À&õ›Ô{oÒÃMºÆ˜«þ/WcT®ÎÙs5ÍU/«q+÷%åò‘“4W-ô’ ÇùÍm/™á8_2ÏIzI‚“ô’Å®ŽKtÎyÉÕ®ŽKv(?µžKîT~ÚC—èÜôl^è¸l¾ÌIºY­gó6Çy³rÞ¬œ7ïr’n®q’n~ÈÕ±ùÖqTëx[ù )¿O”Ÿú—Kµ½—®q\.ÕUöK_v’^Ú«_(çK‡•ö™ã|é—NR™&$ÍS«Ís»d6O½wžæpyºÞ”çö­lžæÊyºn‘§ÙáÕö-îÄ‡Ý¢Ú¾Åø°[t~¹E÷¶¸v‹;ña·¸v‹®;^æN|ØËâÝúäeî¼·½Lmð²ëõ=Ý©¹Lçy—é<ï2ÍF.{Ì­_îNÐÙË+]ùº¦šïV>ó¿t«ößÑU¦ï ÓâÕw #DX/Æ.¼½b©[žâV·Š}Å n/áJ¬&/$Zëö£®¼Ë­…_ù’[k.€nì'ºÓ­D`½;äöª·’Î , ÂÞñR"øqœF³Wcz6êˆ'B‹°/n¯m}FKÆé[ˆ³)D8ƒQB„Õø "wFÄnÅø<¢÷Ý~ÔµXÓO Â^e"Î)àŒƒý.v›·As†‰°t¶ž¦‡ßRz=o‹: º‘ÙæÎ}ÙmnÚn×Uµí/¸q+™¯èN·—P²ÛLÉ~7Z7¹½T{ÓA‡vê˜ïĨr<vJmg¨l)öR#¼'>ìÍØq*"‚Nò½›Ñ»8keo•=M„ˆˆ³Vö{ðzíDˆœ8keË ËËDȸKû}ôÐD°(îVÝŠü’£ÀÝ´`G'kl¹î¦•¯W ÃûOinìŸnQ„=Rœ¶±·áôwÓnÛïÐ?£½Dè?î«ýóQ‡*PÇT"ô=wç*¾pè_PG4εp?ïöùŠPÇ ¢O¿; Mܱ»#ÍIp´Ž;{wÜîd¾ûBÜÅ»ãe×Ê;P/w+qÊ‚{‹•ø‚;{•º]‰/¸{Xùޓ凨yä.㡱×á î°ýþ gíˆe'ˆ‰ ‰ïa' '=í`Qˆ¡Œ#D+jÎà½ù^,þ ¢P ¢NzÚÃ'"‚!‚î$‚Åãd¦½ Q¼‹ºÖAz‘39œÌ´?Al<J„}Nîùÿ{hÜéÿÉQ×»?·à®~u´ëÝjx.îùW_áF¦;žÜé¯~Ì`5¬›;óÕ¹¿:Ä=ú»ÝÉ{7t»õ»Ö:ÿ¼K"Ó”Õ°ó]ðòÜýß…¹wÿw©…î:¢O±÷„Ó;öž4G»'Ó}qïíΫÜ[ã|Éî©#‹h•«ww¶‹»uÖ»û3Ço÷IWG­îÉÕv)êU4àÐwæÓîéuçþFE­Š`3ÜG¼ÀÑö&NïØ½EŽßÞRE'ÝÓ}I³âvú´1y_fàt´Ý§_ì«PT­HeÙ§²ìSYöwÒ×Ǻ§õX¾èÁp··ý ry°ÇIõ ~ûP‹{úp¢“ôaXÅAHú°Jú°î=¼ß¡†’À¹zÛPîd~$ÒÕñÈl÷Þ#]®Žýsõñ&(¿11Ç*RT£HòñØ‹ˆN:4!IQ–Ô‘ET«¨ß!Oœ"™9D¶U*êvÈ®HZ>ãE¢E2W‹;JÔ¨HÚ1ûC àpEòíÜ)DÅŠªŒ9í\¢FEòmÂBâ‰|‰7•+jS4ê/EQž¢z‡B“µ;–©¨WјCKIö•ÂvLq(<EQ•¢^EÂ%­hRœ¢tE‚^" Ò^&’ñMûЍY‘hÝ´D¢Ù‘OIË£"’vÌøQ‡ôý‰™ÝO$=9×ÉXž¶ŠH4;á#"‘tþ QŸÈüC"Ü£*#ûMë!B®²’Hòv;.)N>vÁ¡O¼…ȣȧ(\‘_Q¤¢XEqŠâ%(JT”¤(Y~‘è<"Ñ’i‹ˆ¤Ÿ§M”¡HúyÚ·‰`Ôµñ“ÓVá64[‘+h‘èдåDù‚–ZB$s°içɸM '*VT¢¨TQ™¢rEŠ*U)ªVT£¨VQ¢zE Š5)jVÔ¢¨UQ›¢vEŠ:u)êVÔ£¨WQŸ¢~EŠ )V4¢hTј¢“M6Š<Š*q7œHzÒ,$’þ›ñ Q­Ò¤×"@Ô 4é«è"¢f¥µ:›žÜ¦´¥u*­[i=Jës?¹_iƒJR,ê¢Q¥á Z;O€æÛ²GˆÂ•&e;ˆb•³8D JëñD%+Ml&ø1¢t¥‰¥x¼DYJûðS>®Òòqʇ¨@iˆQ´(±ÒJ‘¡•)­÷”ˆ*•VûQD5J«Ã½@¢z¥á®à“DMJkÁ­A¢V¥µãþ Q‡ÒDw=¿&êVnÖ'êSÚ�n| *m7ðˆF”6†»}D'm n†ù”&CèeÌ›©4Þ$ŠWZ"n %) 7 “‰R•–{D™JËÆ-?¢¥åá.#Q¾Ò q[‘¨Hi%¸IHTª´rÜ3$ªPZîUU+­7‰ê”Ö€» DJkÆ]P¢¥µáQ»Ò:q³Ž¨KibQ!ô»Sz•&²†h@ibQ!ˆ†•&’O4æhX$ ¹ŽÈ£4±¨Æ”©~¥‰E…ÜA§4±¨¢D¥‰E…<H”¢4±¨ƒDJ‹ i#ÊVšXTs©yJ‹ ¡žN-TšXTímj‰ÒÄ¢B†‰Ê•&ÂñªþoªX”/˜HýßT±(ß4"õSÅ¢|ñDêÿ¦ŠEùÎ"Rÿ7U,Ê—F¤þoªX”v>UýßT±(ß%Dêÿ¦ŠEù®&Rÿ7U,Ê·HýßT±(_9Ð4õÓÄ¢|?&Rÿ7M,Ê·‡Hýß4±(ßO‰ÔÿM‹òq<¦©ÿ›&å{ŽHýß4±(_'‘ú¿ibQ¾_©ÿ›&å£ß˜¦þošX”±gšú¿ibQ¾"õÓÄ¢|!Rÿ7M,*”c4Mýß4±¨Púiêÿ¦‰E…Æ©ÿ›&z‘ú¿ibQ¡ÌP¦©ÿ›&ÊLpšú¿ibQ¡ç©ÿ›&šK¤þošXTè•Dêÿ¦‰E…^O¤þ/R,*ôf"õ‘bQ¡´£Hõ‘bQ¡w©ÿ‹‹ ½ŸHý_¤XT(Ç2Rý_¤XTèóDêÿ"Å¢BiG‘êÿ"Å¢B_%Rÿ)ú‘ú¿H±¨PŽ[¤ú¿H±¨PúÎHõ‘bQ¡_©ÿ‹‹ ó©ÿ‹‹ ;Hý_¤XTí(Rý_¤XT3ËHõ‘bQaÌK"ÕÿEŠE…ýˆHý_¤XTØ^"õ‘bQ"ÕÿEŠEM\K¤þ/JÀDŽo”ú¿(dëDêÿ¢£s|£ÔÿE%¸ùeT¢"sF‰mÍàhEɸÍ8L$ã6ƒs—¨t}/ÃÍ>£2‰½E>AïHÊq³Ï¨\E2ªÑlyT¾›¡E(*tyXÆw¬‘q& ¿ ²ƒHF:ùFì2–¨\i2æi¥Db¡iœmGUéÓjE5Z¯èA Ç(ªNi:ûŒH£W‰jt³»(±_KÂ*q/Q‹¢V}Ú¦¨]Q‡"x`Îó¢ /AD°n?|q4‡±; v>›^™þ> :4Ÿ@ÿ<ŸÚ”HÛgtŽ‚§N"½šr1ÑIüT'¶*§l%ÒÌ<Ú§(\‘_Q¤¢XEqŠâ%(JT”¤(YQŠ¢TEéŠ2e*ÊR”­(GQ®¢<EùŠ **r³ÔhèßT"FëÌ0Zg†Ñ¢ó¢+”¦3ÃhFëÌ0Zg†Ñ:3ŒÖ™a´Î £éÌ0Zg†Ñ:3ŒÖ™a´Î £uf­3ÃèE:3ŒÖ™a´Î £uf­3ÃhFëÌ0z@‘Î £uf­3ÃhFëÌ0Zg†Ñ:3Œ1ŠTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿbTÿb²ÝÚCŒê_L®[·Àï–¯=ĨþŸŒÕ¿˜"·¾£+1ª1¥n-#þoœ¦+1nå$Fõ/¦Ê­°Ä¨þŨþŨþŨþňþÑÓĨþŨþŨþŨþÅ´èª1ª1ª1ª1ª1ª1ª1ª1ª1}ZG¿"Õ¿Õ¿˜!}ªú3¢´Q·ò3æÖŒbN:‹Uý‹ÅÊWÕbUÿbUÿbUÿbUÿbUÿb±2ö>‘èßDFœØçccEÿ¦l"J§3áCüÎ_{œ±Rf˜ )³ˆ³‰KÌ)ËŒGÊf㕲߈T>ÅWÿ–ˆ7ÿ­éÚ¿•—×!%ø·zâFàjÍ)ëÍ™R6™$)[ÍYR¶™ERìþ=ÎL—’&öï‰Àñf¦”9&NÊ\â|àÿÈ5³¥¬4s¤¬"®!î2§KÙk¥ìçÃÍ\)ÓÍ<)3ˆ³L‚”exóÅrâJàÿS|ó?SÌR–˜D¬l ¥/5¢u/µŸ”­ÄíÄÄC襗†‰G@ÿ¯*e’ “²ÄÿªCèú¯N´î¿ºˆ»EgI™ ÙºÊ OW9äéj„ ]½ð®ôÆ/ ~lð±fš”©f¢”9æ ¬Ñ@Â_4¡ö_´aÔ~A©~ÑEÜQûÅ€™€¼ù²O_NÁ˜¾œJœN\`‚¥¬†ü/×Bþ—;M¸”#X¶õ| ßþ2ïÿ2íý%õä—àÿË"âbâðùe-qq§ ‘rœÅФOº=àßí#'öcÑ·;¿MՉ»žït ½Ý}FìÀ³’¼‚ 7óJ8Úø [ôJ,15ù•x– ¤PK_I%®ƒþ¼2„>?j áÑt#6ãÉÏ£Uxóh-Zw´Ž¸…ï´· EG‰‡€‰Öý*­ûU.Úò«|´âW¥Æ/%õÿWÍÇ_õaÅÁHëz<Њq$ÚØ“6öä±—AžžJÔÛÓÉ{¨u=ßž>HØ3€QëAí=£À¿Ž…<¿N N$Î3ñR˜Ó¤¬†l¿®G-¿n î6“üæUä|5’¼Gœ…ž5öûj>q4öÕ:hì«õÄ]ÐÉWÙ¢Wû_óÁ_ó£g^‹D¿‹›×žÑ–×ò!ÿkEíµbâRâ2âr–¤°Õ¯Õb¼z3Á­7-í­ãxÝ ž½ƒx³—Ö×Kë{Ýüz,úöõ8âÈóz2q tìõLŒÎëùhÝëeèù×Ëѯנg^o$½šÿz‹‰Äz"êzu½~|Žy€ùX†“âǸ‹$%Ž8žtÖ~,cq,æX,ýX6qq9|αJŒÑ±x€7$‹–ÒGNœÿùF9|æðŸoT×£–7Æð´/ØéK N$Î3 ¤õhE_ $ì£möQÃû:‰»Ð·oF¢ÞŒÏ7ãH‰'%½ôf9úçÍ*èÌ›ÕèŸ7kˆk‰ë ?ov¡çßìF¾9=9žþdzñæñôÿñ*,Ã{ž„<ÇiYÇ;ÐcÇ;‰ûˆ Ãñ!âaàßÄüVÕ|õêÒo H/$.Æ›¿a úMéåПߴ™¬êò«1>=‰7ûé=úcÑKýôýôý‰ø¶?ßögg—™P¬ƒO?½k?½k7qñ(Ûu5 ú[©hû[éÄYàöV6q>1%«ˆ˜ññ­6ÔõÖ(´åmd{ÛGœ‚±x»cñvFáíJâŒËÛÃèÿ·O¢ok€›„Þþmôü·e—ß–7Áº;ˆ1úí(Æè·cÀ °ñxªØø@)qFp ¾k`º40ü;Fêßå!"ÿ®¶ù»bbz×ßõ¼テ¿NIœ _ôN>ôübÄèwJˆë çïô"ê½ë‡¶¿›Ø÷nãïúóÝôÕ»½èçwûÑKïRCÞ¥~wœß!…M Ò'f¡³‰sÑWƒUè½ÁjâZôÒ`úmžp°ýó}ò{áÄ‘è™÷²ÐWïeç¢OÞ«B/½WM\‹Þx¯ ýó^7q/úá}zæýpâHIºŒçOhËûŒ›ïç -ïççW1^ÿ7ßï"½›ôââAôÀâo±€7? v}‡§ÛEÄô·”×Á޼Sø>¹}@‹û Ÿx˜¾ôƒà!?ߟ‰÷‡(íùч 1CŒþCä?T ©†)ÕéüŠžsˆ¶<DÎC'1R¿§ þÞúïéEŸn¿ÏƒÍz“ñíïøN#ßa†óû6~ÛNÜIz¿÷'|ü–‰¼s‚žá=Érâ Œã F™U¤TƒÃ‰.rè…Íž`”?Á(‚:vbZôaìëÃhȇµÐŠÛ@Ž…F —ó·‡ÛÌì¯@’á!Ô5LýþC$êýC,qèȇ?äA†?äW ®?4C3ÿÐBÜJ<Ýû(–øQ:ìî£BèðG¨÷£*âjâX÷G]hïG'!ÛÇá°ˆýÄqFFÕ[ ?¦†|\…z?®&nø¸•zéÛÁíãpû¸“Oéý>î! "…„Ÿa–;â¡]Œøaû#ÌFâIO„¯I!%•8v=’MœCÌÌ„YÍH>,}¤ˆ|Jͤ,Gd©6JÙj6HÙf.’²”âNÒ»øU?<Ïésþ‹¹ÉóL²”u˜;ü±ïü±‘¸—¸Ïœ#e¿Y,å�éƒÄC¤ó1Óä7ŸxL›”>ü<ä'±æ9þ.pq¢9"e’yCÊ Ó'eq6qq>qq¥yKÊó¶”f@Ê~øÏOÌ·¤„·üdˆ”aRFÍ¿ùSŠY*e±I•²Ä,“²””2ârÒ+Mš”&]Ê“f•ßÈ$u¥”PF}Äá¤Gšl)sÑ“£Eæb)›ÌF)L®”ƒf“”C¤Œ’~Ò¼à7ŸÆš—2Ž8ž8ÕtH™eþCÊ\ó¢”ù¦SÊBâ"â2ó’”åæ¿¤¬4¿–rÐlö›ÏÌåRš|)ëÍR6˜ïHÙHJq3é­°‘Ïzà“?D¿ý9Î\*e®Ù"ežÉ“2Ÿ”âBÒ‹M”Íf«”ÃæZ)OšïúLÚyˆÃM‘”~âHs”±¤Ä'˜¤L4×K™nŠùkMÀYÄÙæF)sÌ6)sÑÒ±bsTÊr[!e§¹JÊ^sµ”}æ)ûI e”aó ¿ù<S¹Ï㉉“Ì/¥LAß~^kvHÙkvJ9`J¥47K9DÊ0)#¤Œš[üæ cʤô™ïKnn•ÒOJ,)ñÄ ¤'‘’LœBœÊ§éĤg’’k~ ežùž”ùÄFÚøEIÐ;R6I¸ÿ¢‰¸™¸/hPÊ ÷¤6•RŽšJyÒÜå7 ‡ÿÅoî”2ÒÔJgꥌ7J™`’2‘”dRRˆS‰Óù4Óì•2‹8›ôó€”¹Äy¤˜}R’RDJ1¿*!½”<Ëø´œO+ á_*ÍO¥¬2RV›ýRÖRKJ=ä?kÊ¥Œ7ÿ$eq"q’¹MÊdó¯R¦š=R¦ggšû¥Ì2?‘²Úì’²ÇÔøÍGš{¤Ì3÷JYovK9`îö›/L5öðáÕ¿ä|öË:Äš/ë‰I¿d$ý²™˜+ÞÏñþW  •úWIĈ_esEè«Äʯª³¾ªCVÌ’¯˜?|Åüá+Æ»¯†Á9x’<µ‘ÎÌŒ¬‰'N N$Nâ›Ñ|3Ÿ”B>-"®#®—Xleò|¾”c"•µI’[›LœJœ!s"k‹ðŽ-Ïlm 1fôÖÖwHì¶vDb™µ£Ä'ƒ’$®Ù óm)Ó%—³AÈimP q™øvÔ$ñÂ5·JÖjƒàumÐ(Þ—†ÄHé“Hd'¤Bž éÄ™Ä9â™í„ ñÃvB%qµøa;¡Ý,—²ƒ¸Ë¬rL<°pØãl=éf”ef­”5f”MàãÄ·Þ8¼ïÍ5«¥,4R—›oK— ‡½ˆæÖÛŽ^õv÷²$³i/yˆ N$N"NGçJæ`%y’ˆ\+ø6ð AÎ`CjÀ!¤–¸‰e+)mà2>!ÃÄ#À>¬YfšÖ×G<BžÕàŠ<¥pÍ&Î#. .&.‘¨mCKÑ·¡ÈålX—h¦ ëÃ(„‰ç·aÈ@ìDäávb’dGvb™ÌìÄN‰û6Ü@±6hË  á5ÄÍàÞ9Ã{Hé‡ðl×AÂIXw²“°²d'¥SÚI°; kvR)qq[÷¿áûcvÒIàˆx™q؈bÚED)É蟈¼‘Jœ™#2Ðc™¨1‚}‘vEä’+£È#%Ÿ”ÉmD!yAó#ŠÑÞˆââzXADƒd¶6¢‘¸‹¸Wr91(óÏÊTWðäpôíäØÈdälvr‹äBvr¬or;qq'q74vrq/ñˆd#vòñI`¬ä$ÖŸþþló/RæK,°þBS%e±x?ë/¿gýÕâ÷¬¿Nbõ·ŠÏ´þNñÉÖßkñÛ)>ñ½vJ¬r Ö¬ì”6ñ¨vJ‡xT;¥K<¿2&¾¿*&xªxªOü¼•Dw¡”‘øjjq:1VlìÔ"3OÊjØþÔhÑÔ:âzâ¾ÙDÜLÜ!¹ÚIÜ <Íg.‘2œØOŒ­M;-Ö:uMË Î$Î".!®”,ÅN«"®!n!î”ÈkeJ"82–8•8[Ⲭ“ˆl#%Û¨d‰ø6ª\¢¿ê’LÀFGJ¾d£Í•R&Kþc£S%ë°ÑéÄÄ™’ÕØè,ðŒÎ%Î'.6HI٢ˈË%W´ÑÄ•ÄUÄ5Äõ’CÚè6¾ß oÝCÜGÜÏ=@<H<D<,¦—%eÞI59R²¯b2‰‹ˆËáÃc*ˆ«ˆ›ˆÛM¡”C’×Ù˜Éål̘äo6æ$p¬ŽõH>fc#ÍJ™ ù§­3oJÙnŽK9úôHÓ%e‚ä]vz")IÄ…Ä¥’gÚéæwRÖšw¥ì0¿’²×ôHÙÍœáÏ(DÔ˜A™gTBóg4À f47#Ìè7I9ìI”r„x”ø¤G¾éY eq"q¾Gtufq!q±çl)«L«”æY)›ÌÏ¥ì1ÍRöš“rL2s;ó¤ÌJl\¼y^Ê™­Ø¸l™Åظó””%æi)KÍA)kÌ“RÖšÛ¥l úHÊÉÊlܘ©öÛYqˆ³²‘‡„`oÂΪ€w’R¼Ð¬JbFŠYŒ³ê‰ˆ›‰[àigµÂŸÌº‡Ý ï7»~o6æ¡vv'üØìnx¹Ù½ðê³ûe®jgÂ_͆¿=оƒy¥“Lœ"3;‡žjN&6ÝB@Â9˜ÿÚ9e¨k¥SI\E\M\#sj;ëœvåœÓEŒµ#;§óýüÓGõa5 GÌHŸŒU‘5|‡œãkHgÛ㛉[ùμ3+Ìv.9ÌÅz²›Ÿ?7 Q`n 1#Â\Fç¹ÄYÄÙÄ9臹…ÈÓBòÉ“Ñn¹õ÷²ÞëðÎ<ì ÙyXÿ´órˆó‰ ùÎ-|‡|æ‘Ï<ò™G>óÆùÜwNÃn‘=-ôÓÁOË#.à;5|‡|N#ŸÓÈç4ò9mëN!âòIHGŸ'äA+JˆKM0ÎOòý.¾ÓC:s›„â!䙸 â·§g€~z&8œÎèyz9qúíôJhÚéÕÄuбӱcOoňŸÞ <½švz7¢á|tc~ù¿þó©çó©çó©ç󛈱ÆkçPZ¬'Û3(ÉX'·g°—ÎÀN‡=ƒyÎ…ǞϨà;•¤`ŸÎžQ±>£²Ñ‹8cØLĹOpNÄ ³Mdÿ$–—SÃ)a"³¦Dæo‰´ÄÄ“ð[Ù¨ë[…‹o5p,†ÁsuröÝì‚HâXbfõ ðþ‚$„3¥|Ÿ–µ€–µ œ˜–µ€ú¿€õ.`/-`/-À Åžé gæB†3‹ÁóÌ&Èà Ï$æ]IœM$±Þ¤Dâ$âd–)¤P““˜•%5gÒxž…}"{V6yNϳ°¿fÏÂN„=«“˜žç,êäYÔ¢³úHé'^˜ ž 97YØ ž GÈ3<ÏÎÛgs.s6GðlŽÅÙ‹³ËXrDÎæˆœÍ~8›¹qrx&Ó^’kÉó,ðLf®¸«¦vQ8q$1gX‹è+Å“ÂLrûavÓì"¬gÚEØÃ²‹SÉ3 <Óÿ,n"½…¸o.¦m.î fŸ,îâ;Ýම‡ô^RúHà;X鵋éðìhØs°—dÏI%.€ŸS>§†ô&ÒÛ¯¦øó´:ÅGNì'ŽÏ”rè¹û¶6…£“BO$æè¤0ÇNe9F {o µwI1$\‚]]»„­Xʵnß%๔£³”£³”–²”–²´†˜3Ê¥õÐsüª°`jÅR¶}é0ñ(ñ1ë]f@YÆè°Œ£¶ { vöàì2ú½ÔXʰ<S©©Œ>©¬7•sáÔzbŽZ*í%•³­TìÛå±à¹¼<—W€çòvò,Ï4jEZ2ÞL£_M£_M£JËdI{I£/JãÜ!­<W„ƒç ÚøŠòü1x® ¿]Ý»¢˜c±‚c±bˆ%GdG$ý^�žéØa·éÔ«•òÜž+é7VÒo¬¤ßXI¿±’~c%ýÆJÚËJú•ì‡Uô«è7VÑo¬÷?ÏÕô«±/lWÓo¬f\^M¿±š~cu )œÓ­æœnuq&éô‘ðó>Æš Ê“A}Ȩ#n n$æ¸d0²g`ßfÐÏdp¤2ÚA?·<Ï­&ÏçÀó\jËšH<]C‹^Ci×°ç×Ð×­¡Ìk8Žk(óšaðù¶Ÿ|:ÁçÛì¥o×’Nmùvq31{þÛ=˜“ú~…÷39ëÌdÜÏä¼23‹˜šÉyef.)ôl™ŒÂ™ŒçÅÛyé¬{ ö<zózø”Zqmä¼R¸Úpuã<êÆy# ¯¥„k»Èç=ðYÇQXGIÖQ’u”aeXÇõ¢u¤ÐÓ®£§]Çõu8¿d³° o³2Ès<³èå²8 Y\É¢œY”3‹rfQÎ,êpí:‹žO Ïï&·¿€ÛzJµž¾z=åYÏ>YOIÖS“×S’õ¥à³ž¾e=}ËzZè)àyAx†2Æ]@©.À.³½€1è‚~âA¾ƒý;›MͦýfÓ~³i¹Ù”'›™[v.)ì«löÏ…Ô« £CãÀçBêê…ÔÏ ©ŸruèBÊp!=ÿ…”äÂnbZúE•às}`èà³ñhÎØ ÔÛ ÔØ Ô« ÔØ ɤÐól •m`~¸ñ"ÇÃÖn9Œ’9ìŸzàœjbzÂZ}N)ô‡9 à–ÓÊŹàvq1¹­·‹›.¦¦]L/t1#ÂÅ´5ü ˆ”P62ÃÙè'f^º±m<|6±W7Ñ+nb†¶©ˆ˜£¼©„%GyGyg=¹lunùä‚O.­#·“Oé?s™¯æ2–åRs9â¹”<—x Û{I ù\ >›é6'áéfêÃföêfZÍfjÅfÚòff¹›©—zÀçÒ8ò¹|.e^Ê>¼”úp)ç8—ÒR.¥V\J{¹´˜’çqu+¯š|nŸ<öçöäöä®ôn¡l¡œ[˜•m¡ÞnÉAÜßÂñÚ’OÌÜ`K!rƒ-ÅÄ¥Äåȶ`Ó^‡|û²Ddã—e’Òˆ| ”óŽËs/g”¹œÑörŽæåÔÌË™]ž@ݸï_α»œ£v9GírÆšËé“Cïç;—Ë™^Nÿp9ýíåÔ¥|Ö•ïÇ 4?9L~:f ù8Ïfó©-ù…ÄŒJù%8+ʨ”Ïþ̧MåsÜóqFÅ~ÇàÍï°¿ÃŒå;ÌZ¿ÃÊw:Àÿ rþ+21¹g/íeÄÄ•Xûº¢ «sW´wwa늓&ÞoE‰_é#7gH™•«+3‰³‰s°ze%Ö±¯¬"®!®ÅÊ^A8V- ²±þ\Põê‚Jâjâs®”½Xù*«@Wåc­æª¬}]5hòüöê<“/e7Öš®ÉÀ*Í5mæ:)‡L‘”ÃÄ£Äcf»ß&™mR&§§c-«°kY…¥ÄåÄæR¶b5¬°¸ƒ¸ÓTøíÖT¬+nmÁºâÖs·ß^[„uÅk›°–øÝx¬ ~·À<(e£Ùï·EqX)ÊÇHQVEŠŠ±BRTBJ)Öаki‹ÊI©ã; ÄÄ­|¿Ãtð÷}€»‰{ùí0¾½.Þü—” æ—R&'—`mê:œ‘³×Õš£RråáºAäÏ×r}2VV¯Ï ÌõX+»¾Ìœ-%N,Øë›°Z{=w®ïÃêÓõ#X»Á‡¹â±ËpC*VoÈ1KY„Ñ*Ìw¤¬Ãà -X=»¡ «²7 ˜J9fvùm±ëÅØÏ²ÅXc,Î37IYbš¤¬2?•²Á<,%×`‹{Ì]RaýöFƒõ´ýXC»1Òü‡”‰æEÜׂuÜÈ9ìEæ6)é]o,3·áæŸ26ÝÈùÈŒ’73jßȇm8ej·…÷l£…n‹$Ž…nãúɶœj}<·Ñûmãlh3¥m-ÄôÛÚøæ[xs;£Ævžngn¹œ·ÓÏl#æºÍöÌ÷C™ÏlgtÞÎÜi{=ÌǤ3"lgäÝÎÈ»>v;£Ãvæ¡_àÍÆÖúÕúÕî …yø”³†z°f§%5X£+¡‡/ῤf§%ÍĽȓÃN‡›8G¸) þí&Fê›R‰SnÊ$ÎF‹Â8'½‰¹åMÌ‹nb¼¸‰óǛآ›8¼©‹ïã¿ÊÛ”ΩÚ”G 1ãÂŽ*bæ®;jˆÛ1F;:vàœ›Ýi<¯KYæ9&å°ç ¿--ðôIÙë9î·7gy~#e»§ßooIö¼%e£çm¿ý^¬ç·RV{ü¶Ìx~'e©gÐo¿_ëyÏoomõ¼ï·?è÷|à·ÿäó ùím)žßûí?xNømEµçC¿ý—6ϰßÞ>è‘»#ÉóG)K=£R¶{þì·•ÏRfyþ[Êj¯´´²×+£ÿÃ8¯h ¼b§?lôŠßþáWbÁIÞ©R–xÅcßÙê•rÌ+^úGé^ñÆ?ªðΓ²Ó›à·U>¯øáªlo¢”5ÞRözÅÿ8Ö»PÊ|o²” ^ñÆ?ô¦øí]‰^ñÉw{ÅÒïjñ¦I9êŸü“To†”åÞ5Rvx3ý¶ÚçÏ\ã_Q]ç]/å€7ÛoïNô^$e‰G¼ÓÝåž)+ˆ<b¡w7y^”²ß#éîAø®»Ç<â»vy€wùˆ“<b×»R‚ÄûíÊôn”’9ü®l¬žíâÙ®\ÒóH)Â~Ó®D®]¥ÄåX³ÝU¯µ«^nW;ìwWq7"Ú=)Ð{R[ïÉ@4¼'“8rOtãžhÅ=EÐ{J ÷”A7î©€nÜSݸ§ºqOtãžèÆ=MÞåR¶x—IÙæ]"e‡W¼ë=]ÞERöxůÞÓç•hxÏ€÷L)‡¼âQïñJܼgÌ{ºßÖ¯øáŸW"lß;KÊX¯Äßšx¯´¨&Ñ»AÊd¯øÞšTh`M´±& ZW“ ¬Éƒ6Ö@3kм=kJ¼çKYæ]'e…÷<)«¼âÃkj¼gkê¼+¥lðJä­iòJOÖ´xÅó×´yÅjj:¼âñjº¼bG5=^±Êš>¯xƒšÏWRyNJ9âù\Ê1Ïg~{¯ñüIJŸgDJ?4ÿÞXXÁ½ñž¸s [¾—Þì^f•÷r–q/3Þ{™±ßË5“{ëuÜÛ‹ñº—Ùì½Ü7¿wqêÞQì¤ß;F|xwžîNÄ.ÛîdÄ©Ýéˆk»³èöî\ììÎCÛ]½ÂÝÜ/ØSýö>Μ‡ý²ÝÇÇ}\»¾å>Îî£Ì÷q-î>Î2îã:Ã}Ì®ïc|ç÷uÁwÝÇÈr_/13´ûú‰qJÐÞ7ïtßt²–±¦6‘¥–±¦6’ôXâxÒ¹>_›?V[ ]­åÊmm'btml¡¶ú_;„ZjG‘Ñýk"Úø¯¹fnñ¢uÿÊØ·‡™çf¹{˜íïá|dO-ìn½ýžâFÔ¸§‰¸ƒ¸“¸‹¸›¸mÙÓOÊñ01înØ=cÀ÷3÷¾?2ß_‹ý”ûëˆë±Û~öVîo$nÁ¸ßÏÌóþâNâ.´åþnŒ×ý=|¿—¸xéýÃfŽ”#¤Œ’2F ³Ö:(u)ЙºTâtâÌpé«:æ?uÙĹÄ%ЫºRbÜý±uå¤T×"Ë­«CÆ[×}«k1âcëÚ‘ëÖuó«àbˆ#Ž'N6â ài“š ¥ @KÄ鎆‰G€÷FBo÷Æ'§@‡÷¦§çñº{ ‘ï-Á¾ðÞrdÈ{‡Qïó±î}>âpâd×ûRp2d_6r¹}¹F¼ú¾"#¾e_1²¸}¥ÄåȺ÷U‘R‡QÛÇñÚ×€±ÛÇñÚÇñÚÇñÚÇñÚÇñÚÇñÚ×±Ø×ƒ‘Ú×mÙ7€üsg¦û†I†ì®÷ái}81ÎcÛúxRˆ¡QõI¤¤àG}ÎçÔç‰Aõ<ÃS_LÌ3<õ¥ÄeÄÄ•ÄUf=î°Ã"깚Ts˶žóйNþ ×ëð?ÕsÎþ WdFô Wä݃<¯ò Ï«<È5í¹*û ³Ê¹šý NÛ‡ ZñÙàDÎèâ*åC\¡}ˆ+qEå¡6|õ󺇘 =4oðp¼ÁÃñÄ ÄÉè‡3ˆ3‰³akççóüÆÃEÄÅÄ%Ä¥ð—Ão<Ü�¿ôp1s§‡;ˆ;‰»ð s³¯† ´¨k¿ YĸÉeÊÀ§¡+„¹JÐ@ïÝÀ¼º3߆Qbθñ?•ÀGx‚讼=Â5ÿGÒ‰q¢Ì>’‰I"NŽíçI³ýX 6ûy’|1q N_ïç ²ý¼A¶¿§ÑdÒ*%ïŽý4çùй¶ùi NGÿ´ çŸÚ‚Ú?íljåŸâhc8Né7¦àÔtc*Næ7b&îσ$<iÿ¨õ>NÌÛLòþÅ£éęĥ¨ëQž¸~´'¢­"†5™GQ㣼mñh3Ÿ¶·BžGÛHiç;<Mýh'é<§ý(OS?Ú›ŽàìôcÅ8øX Î >VF\NÜ(³0óXq31ïˆ=ÖFÜnn—²ÏÜ!å�ñ ñŸÿÌü³8œ`üY.ÎUþ¬g,†±ùYŸù±”#¦Úoš Î6Ešû¤ÌDï5•B¦jœ nª!æ=ަâ6œnêïdšÄ#™¦1sžß<žˆ3اà êã8ƒúxΠ>^†3¨×âD÷ã­¸õxNz?Þ‹“á÷á<óãƒ8½üø(N/?>f–Kyg°Äã öœý>ˆ[Q’p–û@2))¤¤§ggçÉìØÈÇ ê•8A}  :v :v í=0ˆó–?÷�ÿ<òÿ¼Á¬•²Û¬“rTfÙ¦9Ü\ eÎH7Ûæœùl.Á¹âfœØ1ÍUâMs5ÎN7×××7·šB)»qö¸¹§‹›p®¸y犛Çpø ?N?;æ œØ1O$™~>‘aJ¤Ä)óD.N?QŽ“ÆOTà¼ô 8ýD£Y-e«xNóD¯ßzšÚ~0~w<òîÒAÞ]:˜3ŸyŸâ`.é<ÿy°}uÑßl€EDŽa6··Â.¶ñvŒéAÞÖ98B<J<ÍyÒƒ@Orìž,À9ü'‹1¾OVJ 5OÖcôŸlÀ¸?ÙÓéOöâtú“'Ñ¢–DhH G¼%vÔ’ ;jÁÌ´äà¾FK!Ÿ‘‚3K¦¥VÖRÏÐRŽã-¤TòÞôi©&½†¸–oÖÁ ZêÑŠ–âFâ&âfâ.´®¥›¸‡¸Þ2dVK¯cÞ<Å~ «æ)Þ{Šwvžâ”§êà ŸjDŸ?ÕÏóTúü)Þ×{Šþð©Qà§=ð‡O‡Ã>‹þ:ýÿtúÿéTôÿÓ™8£â?zŸæÙà§GHç”§Ç€ŸI@/=Ã[NÏà.Æ3…ÄEhû3eè‡gÊá9Ÿa/=SIJ3¼ë3-Ä­Äýè‡gˆùþ)ÃÄcàÜÊÛa­q°ñÖDôOkq2z©5÷2Z3 ­™¤ ¯3­Ù¤äñi쮵Ä,”²¶ÜZI\E\ ­h­æ´ÖS‹ZÛáCZ»q¢µÕÚ+n’¼Â´ÂÒŸõ –g#ážM N$ÎÂ=‹gsˆs‰‹qƒïÙzÔþlq#qôùÙfâVHõlq;qôüÙx°g{‰pêûÙS'åIœ9?d€ùpòüPY!e)ôÿP%ôÿPq5qd€æP-<í¡zâøÞCM¤t ]‡ºÑÆC=Ä}Äýhסø®CƒÄì‡CÃÄ#èáCcÄ'q³ã0N-šÃ±’šÃq¸‘q8w('™Ë¤L‡·9œAœIœÏs8÷2çÁ.ÂmˆÃÅÄ%Ä¥ð`‡«àÁ× "nAD8ÜJÜF܃û‡ûq¿àð nÆ)úãˆbmœoó#Nµ%âœ|[q2q&NË·à´|[!üm[1üg[%âT[­É²Ž¸1«­ þ³­cÑÖ‰QhùMÓÖ‡V·õ£ÕmChuÛâxïQ¶~.ø9?î_< ŸK€U>—9ŸkBä}ŽÑù¹DÞçmŸCä=Ž˜{$±ãH "ò‘,S‰ßH„Íáµ#˜™š#¼ygNÌóȸÌó¼qü|<óó™Èx§ðVûó¼[ý<oº=Ï»]Ï×óþïóõÄäù<oÀ=߃•º©_á[KŸoy+Íæ£Ë{…–w·m4Ùò¦¶m€²MðH¶>Ç⬔±mÄÌvl/|¯åÝ%;ŠøkLjOyà=‚âà%‚øCA‰ÄIð!AéðAðA™¸¹6 7 L[TƒÚƒj‰ C±!ˆ·ó‚Ú¡ÿAý$h�~2hycÐÆ%hÞ2h„OG1vÒuRï¼ÍÞOœKž1! v7!…8•887ª&dgçðÛ\¾“G\ˆ|cB54mB ,tB=$œÀv˜Nè&ÀØiØ¥5ÆhozxsÐÃ;Ë®ýFnÆ;Q¼Ë…~m «¸ø•-y3Šylå#Š÷a£xs0ªŒôrÔŲQÕÈù#¯"OÞËŽbÅ 9ª›˜·G£pr¿ÜOú Ëa´1jãJ4ï’Gã&~·” âJbz­èÈÍœ9ºãÝ £;0îÑÝfŽHµ REc~‡ßÑ‘ÖÅð¶u NAà·s@Éd™ ýŒ)€§)'…z3ÿ3LÊ(p, "6~8'ÏMl&¢[lä‰Íƒ<±ù$³-[ŠÞ‹­D_Åb ÁÄÖšùø}XÈË‹Åþ²™NÎÓ=ÄœYLçoVL§ÍN§µNÏeocÍÊL§ÞNÇŽƒ™Î|~:o=OgOï'ÃÓv7#g)#ï÷3h›3h›3ø» 3ª‰yëítN š|FôF3~06òr`¯ÎÄš’™É_9˜É›¹3#±Ãým¼3“·fâ&ˆ™™GLÏ0³€Þ·YÆ’·Ôgâ™ÙħüŒ™Ù™Ô¨™Ý|§þaf?k¹µÄQÃ¥”wâ¨Õqñļ£Ç{úqœ9Æ%“žÊ2—‰‰ËCžG©âJQK\7²‹¸R8³ˆë#B&GÛŸe í,œ=CŠ!ò\yf±Õ³JÑÒYìÛYåaV)Õ,k‘3ÌjB|™ÕŽ{¦³øë ³XËìtÔ2çúÌì,bæÒ³ YfWAòÙXƒ2³ëáf7CÂÙ-g67fvFmv4pv?|Ëìa³ÂðwÃüfódVbÍŽÈf•s ‰™UÎ)#®€ÖÍiÁÚE4ïÿòÄ20iQ<Å"«¦&ž¿ŸNœÇ§œ§Ç—!Fà7ÇSÛãûHç-°øbf•ñ¼—=—sö¹>È?—9ª¤s¢uÑ»Àa.óÞ¹”p.o"Ïå-ò¹åЇXÜ·pWîžIx„{Lp•æîž?²G¸ZûHd‹åÉÆýÜiÚÏSRûSá'§¿G_ÊuoÆyx3ÎÃß:ððWw<M¤73§õ´¢Þ=÷´“λ՞NdwžxO/¦fž>âø Ï FÊ3„èæÍ6íRæâ¬7_yKp[ÓÛˆÌÄÛŒ9¦·smo;òoîôy»0ëôvãÖ¤w�ùƒw÷"½Cȼ#ȼ£Èô¼c˜uJÃ{S‚ÃáQEà ¥ŒDF ŽÃ|*8w–ƒ‰“3–§"“ NGžœ¹Up&î3g! Î&ÎGKƒ‹M‹”¥æ))ˉ+‰q×�w¸Äó×ãdp³9(e ßiã;í¤tw’Þmž’ÑÁy*Ì_êñAgBõBøÛ&!ô !ñÄü…¥Ƹ$bì™Ü—1!Y°—lâŒ`HÔ;¤˜o–Ó;…Tó·qBp³Ï„´ × iÃ(‡´w¡-!=ÈóCú1Ç 9‰¨áÃ-?ãKD½¾TR¸®5ƒ¿®àcf⣿õñw |ÌŽ|ÍÄ­Äm|Ê_3ðuó÷š|ƒð‡¾!ÌÈ|ÃÀ¡øPöIh81ãi(»#4‘wsLh6Úš‹¬)4¸3ÖPþFGh"i(©#”3ÖÐNâJ¾’‡Ñ„%C‡Ã¸–JœqXt&Œ~5¬ V„l6¬„¸Œe92Û°jÜÚ«áÓüHNXµà‡>ÂZ͇R¶áçÂÚ‰;Ͱ”Ýæ){̨ÿeaLÊ>Rú‰ˆͧR2Ñ ®‰9¸W>1×<#en@O,Àíò‰%¸=±?05±Â²Êü\ÊÓìÇÿiMžXmœØ�=œØËšØœØ‚¹ÃÄv´hb¼ñÄ.´=<Ú$s“ðDôp8{;<Ÿ”2ŒZxyØ`xÆ.¼ ¾"¼•”.Rú1ß À|'|xó…pÞËç/E„bî>Æù‹“<À“ø›0žÄ¢'EÇ󤄠›ñ¿I0‚“—'ñw$&1+˜Ä¬`ýÿ¤rÒ+ ç¤JÈ6©òOj‡-Lâ ä¤à<v×Z#"Í$ü×ð )¥öþH=,‚ž<¢˜¸ Þ#¢~)¢óîœÂ$�¥Ž”ÌÄ#1h"n†ŠhAt‹h…Šhãû؉Àÿ è&¥‡”^Dùˆ>â~hrÄ ßb]\mˆa(ç6â$ðdÍŸìÃJ£¯XúäHâXXúä8³FZÊß}šÌß<™\€^šÌÖM.ÁˆO.%.C¿M®Eí“Ù‡“»'7aåjr3òÉÉ-ð®“Û0»ŸÜNÜ–ú° êOÄÚ ?‰8³EŽùg?~7_|¾¿�ïû ñ3,þ"âRXŠ¿Vã'7´Ô߉8âïâ›Ý¤`¶îÄŒÕ?Dú0é'1÷%ú3ÅCì}J8ÞåÆY 3%óâ)‰ÄŒS’ùN q&§d›Çýøy±»)¹°¯)y|šÏ7)ÿ”ÄŽ)e¸k?¥Ü<âÇ/Ä’”j̯§ÔðÍZ~ŵš)Mæa?~ï·ÂB§´±–ØÈ”ÄÐ)½ðÀSúÍ?ÇÙÊ\æÉs™qÍe62w<af;™í<žÎ£VÏóOKŸ9áJ)c'l•2nÂR&Lø®”‰|š4Aúv^ʉÅóR'ü«”DæyÙ¤órˆs'ÔK™7AdžwJušÌ̤ôóׄN㯠–<áM)3&ˆ¯;-s¤̞ðg)ÿ/uï—U}ö÷Ü)% Õ]‘’"¢¢¢¢¢R! ‚¢b!"b¡’’¢’a¢¢¢b¡¢Q±+Ö¨hQcÏÆÓèÛ\c{x6֍ܯŠ5¶±5¶~×uÝÿ>ç\ç>‡»ï³çõúݯWÇ«7ïs}>×çïõù{r,ŸÂ³ÀÒÏb ¤ê]¥h©î*³@uW…åëð¬±@k‰«E"2 ý¨H<&"ñD°ˆL±@™jù&<Ó,ÐFfZ:á™k–0²Àòx‘\ŒøÝÁVhFî±B ï·BÖßA²Ý ¥ýîH+´wÇ’g…øßOrá½Vèkîî³B]¸»ŸäkVèeî@dŠÀxN ¶NÂËU`´*n£[à¦à 5>!ý§G=O¨‰)T˦8…<À)t—ÎgM)Ç\˜Rù2¥ sjJ5æÔ”Ì£)µ–"xÖ§sjJ#æÔ”&Ì©)­˜GSÚ0¦t’ÜEr7æ×”«˜_S³,ïÂ3óej‘å×ð¤›Ê¦–QãPujæ×ÔF̯©Í˜_SÛ0¿¦vZ¡Ezsmj/æÚÔ~̵©×0צb®MÁÔˆÂSç"мŽ(ºe1ªó+ªó+ªó+ªó%ªó(ªó+ªävÄïIÂüº'óèžT̯{ÒHÎÄ|¹' óåž’sIÎ#¹€äbÌÁ{J0Gî)EdZ0æÚ´̯i¡$‡“i…ösZ´ÚÕiqVh3§%[¡M›–‰i5-m™–‡yj?…y:F—Ój0ýé —˜Ö€ù2­óeÝ51­™þÚ‚¹3­ sgZ;æÎ´NÌ‹iW1_¦õÜ‹ù2­Ú^5h:î3Óë0w¦7a.LoÆ\˜Þй0½saz7¦ÿôLÿé}˜þÓû1ý§`úO§ô®DmÑU$Óìzt¦gt#¦at3¦gt É­˜VÑm˜Jј†ÑWQžQŒ©4£SiF)ÉeXæg”RƒúgÐí‘3š0gg4cÎÎhÁœÑŠ98£ssFæìŒnDbJ0>1å˜/1˜ªw¶`ªÆÐü^L ¦[ ÍÄ´a™!_1¦S8¦S8†ædb®³S8¦S8¦S8fÓ6fÓ6få™å™VLç™6Lç™ÕXþgÖb›Ù„é<³Óyf+¦óÌvLç™]˜Î3¯biŸI­ëÌALó™Ã˜æ3G1Íc¦y¬ Ó<6튭ÅŽ­ÃTmÀŽ¥ÔŽmÂTmÆTm#¹Ó<¶ƒäNLØ.üë¬,LçY9˜Â³rIÎ#¹Ké¬R,¥³Ê­0ÞŸUeuÍ&ͪÃôŸUé?«ÓV#¦ö,ªY³Ú0ýgQšMe{v&†5»�Scv1Æ|v)æEd?æÅlš“ÁóqÚB¨—™MwÌΦ^fö�ÖîÙƒX»g2ŒíólËÌÈœcÅ1ל`ìýA�?gN8úHs"±‡Er4ö¹sbq7§}Â9ØßÍéBopN·¸ Ï«8>§W@®ÍÆõß9£èÇÑìq\(râÂñ¯qq8®Œ‹Çyï¸dìgãÒðÖš¸\ÚŽ+ föàq%įÆU€¸Z3ÆÕãh1ŽÖ%ãšp>?® ×çfáDssp-rn.®WÎÍÃ5ʹE¸j9·oIš[BH)!å(Ï‹@‹æ%ãˆo^ öãóÒÄëð$Ïa^–€23/§Íç\„£ÈyÅ$—àÝYóJñ¯toÁ3GÓóÃIŽ Oâ‹ù±x[ÔüxÑÏôüç§âÍHó³ÐÒù9hãüR)Ì/Ç‘øü ’«0eæ× uóëE»Óð·˜O3œó{ЛO·äÍïÃü ÝïùØ7ÍÂ|Ÿ?Lòá£Ø×Ç£+O·õÆGà¦-Añtû<‘C÷õÅ‘\ŒÞc<ÞŸ â+H&/7þ*¦[|Î,Å÷áÌR|?æH<ݵ?HÈÉÃèïÅ ??Šk å„P\­Nǧ„’“ÑëKHA*!=¨„\œ)M(ÂpJq"¡KNBŽƒªÐÃL¨&¹†pZ+I¨Ç›Ð€%6îIKhB7¡™Šw …ÞŠ)ÐFr;ɸ˜@7È%t~•bÛCrŸX iUˆi•HãýDº“6‘f·iU‰%$“žHc®Är\[I¬À”O¬"¹šäzôðÄ'ðlÄ+j›p<˜ØŒu!±oXJlÅ8$ÒÞŒÄß%Ò]މ=8.Hì# ×ÄŸá9ˆþâ01GP^€§0Ä‚` q¹Ð ÂIŽ 9Jüž%8.XPŠ5tAÖÖå˜Î *1=TaÚ.¨Át^PKráõ8ZYЀcÏ8–YÐDr3á-$·âHmÍJ%ÕâÌgRÆ-©žä&¼;.©ÇÝI-$·³ o“Kê ¤“ä.ñ xvÓ»=ÄéÃ&Ñþ‡¤ŒUÒÆ0i˜äÂGÅG!b¡@»ZÑ®…6|kaþua(2Fà[ í$G% gYãý…q$'aî,LÆqÍÂ’ÓHÎÄ«fáÚÂܱ0W –á¨ya®!.Ä›”ÄÂ*/¤qåÂzú+íXH£Ë…­8¢\؆«0 Û1z ;H¾Š%y!Ù»ì]Hö.${’½ ÉÞ…dïÂQW."{‘½‹ÈÞEdï"²wÙ»ˆì]Dö.ŠÂ¹EÑX—Å‘œ„³s‹RÑÆE™ú¢Ò“Güz·˜ô”\JxÖôEåØ,ª ¹ký"š7^Ô@šˆÙLoµ’†6’Û ïÀr¾¨W0u‘܇{Tõ“†Ò0DL²zÑ(jXLûã½%b1ž‹C‘¹8‚²t1­º.¦U×ű„ÇáMz‹ã1­'aZ-N¦·Ré¯iÄÌ¢·rHÎ%<÷·,.ÔY\»YWá]y‹kpŒ¼¸{ÃÅMXJ7c?¸¸W„·âŠðâ.A/¦2¼ø*Ž£÷Rlû)ÜÒ?Ha S¸#$"ž,°Î&[q¶*Ù†qNÆ8Cãï&‡'ùÉ‘ønrÉфǓVl“ãq%79 ÃMN¡wÓˆ“Iüz7—dÊëäœÅM.ÂõëäbâSŽ'—’j%’©•H¦V"™Z‰dj%’©•H®§göòÉ8cœÜ„#îäf²¢…âÖŠ­\rië ~'iè&mWIî!¼GåÉ}8BO¦R‘L¥"™JE2•Šd*K¨T,¡R±Ä†mé’`¼*Ê‘î’(Å/‰Æ˜,‰E=K≟D咽T’)•–dâ<á’,l¯–ä`èKòè­"úk11Ké­2’©ŸZRsKªÑ“YRƒ7p.©'¹g$–´àñKZñ–Å%4¯»¤ƒtvѻݤ§‡tö’ÜGx?Î.¹†Ë’!ôF–Œ ¯²då¥Vô^–ÚpËÁÒPôC–F¢²4 õ/%K—’¥KÉÒ¥déR²t)Yº”f,—faÊ,ÍÁ´ZšKoÐ_‹ˆYBo•’\Fx9Þ¹´ç6—V‘\#¾ Ïz¬/Kiýbi3éi%>Õý¥4G´”r|)Y½´½£¥½¸±´}¤¥$aϸtSl™µ- FmËBñ­eá¨a™µ-‹$9Šðhô¬–Åâ|ï²8’“°Ÿ]–Šv-K=ðÌDK—e‘6ªéËòHCi£œ]VBx)–eåz%ñ« ¯!NñëIn ¼ç“—5‰kðl0~_Ö‚}IJ.<¨eÝ ŒÑ–õâ*ð2ºµuÙ5úë öˆ•XxŽ*à¥XÅ#BDã~P‘B>FJ*ú~tþ]¤Ðlg Ív¦Ðlg ͦ”“\‰`JŽ>RjH¦Ýq)u$×cÛ’B{äRI¦=r)´;"¥ëKJz)Ý$ÓÜfJ-sJ¯~QÊ5ÚÒ”’I"y„äQ,ZN7Ò/§öj9µWË©½ZNíÕrj¯–S{µ<V¶tyœméò$FË“hK—§’œF2õGË©ZNýÑrê–Së´œú£åe ´ºËËh—W(0Y^©ÀHayÉÕ ´¥ËëÔS¯@}\Þ@r££ÔåÍ ´QË[h1–·+P—w§›8= Ôµå}xæò~ôd–’<Dò®Å,Å9ÌV­¬ˆBovE4Îß®ˆÅµ˜qÈYO8µÃ+¨Þ­ vxµÃ+¨^A6®(À²¢KÈŠR,!+j±„¬¨Ãò³ïæ+In"¹=Š­«mèQ¬è$¹‹ä«è¯èÅÑÁŠ>ìDVÚhŸW†¢+#”:xF‘‹§WÆ¡o³2 Ã]™…á®ÌÁ°V(_„gÉÅ$—Ð[e˜¶+©wXI½ÃJêVRï°’z‡•Ô;¬¬W ?ZÙ @M\ÙHr“RÏVLá•mèC®lÇÙ•´Æ±²‹âÐug倲žTÆR­8²Kµ)0úK V`Ü—¢@OŽu'ÕŽo¥FὯ©±˜#©ÉX³RS1R3 É!N jN-CW9µ‚Þ­"f #MmÂCš©-¸d<ã=¬‰©t³tê5¬M©t³è½x naä^ºyû^;Ö£{c•zxÒ:àª`ôp PðpV…*àåmÞ*VÅ#sU¦}ÓªRÜS½ªwÒ®ª!¹ý–U¤‡ÚÒUÔ–®¢¶tµ¥«¨-]Eméªn¥®êÁµÑU½$÷‘Ü«Ÿ«ð”«XE¥wÕöS«†qô½j„Q”Óâ顸2•+Sévó¦Gaÿ’)ŸN%$=žä$S§Ó×RÒS1†é™“ô,ŒUz.Æ0Z×têMÒ‹0•Ò‹±v§—àH9½oWN/'¹‚äJ;§×RKr=Ž£Ó°_H§Sz;ééÀÞ!½WÙÒ»±'J§ðôìÒ{Å7àÙ½a:‹ÓGÄ7á9Šr†ç42l8·Œ£W ´g„âÈ7#ç2"p÷]†G¬‘$Gáˆ5#½ý šÉ /:#G É8.ËHÁ0#/ôÎÈ!¤\V4£‚­T`ô”A»%3ª1%3j±4f´ciÌèÄ”ÌèÆ”Ì /(ƒ<Œ~LÉŒk$“÷˜1ˆ9›1Œ9›1Š9›)03­˜ƒ™6ÌÍÌ`Bèø@f(ávôI2#IŽÆ\ÎŒÃ\ΌǸeÒ¬HfŠxž©$§¡þÌL’³HÎE/13ä" ·˜B)!¥V!å$“G”YKaÕSX ­Le6“Ü‚£õÌVÌ‘Ì6¼åÌvÊfÞDÙ©g—R Ï«Øzdö(Ѓgöÿ¶!™„ b«’9Dòˆñ\-¨Ý«m ¸‚«ƒHóÕ!$‡â%<«ÃIŽP`д:’(’£IŽ%9ŽäxbÒvu ŽRW·Š_Á³ oó^ÝNH'!]„tr•Bz é#„öþ­¦›ÀWˆ÷á9(>€çú«‡ÑX=‚ÞËÞË+ú-kl$‡Jr8É$ãù±&’hü�ÍšXô^ÖÄa9Y“‚åpM–r#<sˆ™K2í€]CcØ5娶¯©Â^cM5É5Øw¬©UÂáIþÚzå&x6`k¹¦ [Ë5-ج¡ûù×´áeÝkÚÅgð¤±kƒ•àBèkñT¯µcé]K>ÛÚh,ÕkcI¦µñÊmðLR ý_›¬Øá™‚ÚÖ¦a[›©Xà™¥@‹º6ss-­,¯ÍS u][€~ÈÚ"‹ /A`m)^ˆ±¶ŒpZq^[WX¬­T ¥][E8}‰cm ö/kk±7Y[G8}õimö5k®m"¼™dúîÉÚV’Û°×XÛ®@{¸¶ƒ8ô×.BzŒ¯ò<ûè¯TÖ^#dP’¼vHjí0ýu„þ:ª@[”%è>²¬ˆgÑZaV0!¡J:<èàYôW*Y‘ ŒÊ³¢Hö¬hÂc S²à¾VVáÉ$§Ð_SIÎR¶Á“Ò6‹Ò6+br9«ûĬRú+}±(«œJ¼v «J–0«šþJžgV­N`VígV=áT÷³±üd5a©ËjVÀkÍjÁYå¬V%Pˆ™°·¥ÛfÝ6#²èkYôÅœlúZG6Ý ž‡íOv<zÚÙiø ’lÚc“Cx.ÎØd`;œ]ŒëìÙ%¸g#›vždW‰¯Á³Füž4k‘]ã‚ì)d7bYͦÝPÙ-X>³Û°¬fÓž¨l·fÓž«ì>œc̦//dcYÍ¡ña±.çÖ%¡æuɨyy€ëÈ\Gà:ò�ב¸Ž<ÀuØ/¬+ÿ„gê\W©@š¬£ëjI¦ëHs¶Éëh”½®…ä6åj]'ÎT¬¨g½÷ ¯·‘‚:ׇŽÚÖG¢¶õQ$Ç’âzŠùzŠùzŠùzŠùzŠùzŠùzŠùzŠùz<1*Ö—bú¯/Ãô__©±žâ¿¾{Ìõ5J <( ë›iÅo};½Õ¹¹¾óe=y,ëIÏ0éA=9ñ¼\äØð¯9ômˆúœGN(2sÈ®œÌñ;ÉdcÙ˜C6æÄc™É¡½d9Ô»åä’æ<ì¹r °çÊ)"¹{´êÝrʯÁ-§{´œzLÕœ&LÕœìasÚÐÍi'ͤ¹›4_% =¤­—ôôr’ß`ÅùÞ Á¸‡š5kC8†µÁ޹°! ów•ÿ ñøî†$z+5l ²ˆSŒi²¡ÓdC¦Æ†r,ÚHgéìÂxn¸jýž=8Û¼¡×ú{x^#ýVðm6P.l \Ø0"þ$Ä,:7´‘ö<o¤;ù7Ò·c6–¡¶hõÆ*Ô°±c¸±cµ±c¸±Žz’oÅøllÇZ¹±“4tcÜ6öà,ýÆ^\ûÞØGò0®‰oÅo¢Í¦1ò&Úº‰¾^´‰vmÂ{ÏÄ&Úi¶©ëû¦rœÞÔ€©º©‘d¼E_lj&¤…äVHÛMx£‘ØDßÛÔƒ£ãM½$ÓùÍM´æ²igö6 ‘L_°Ê G¹ôaàÜH’£1ôÜXBâp´˜/b ÎS0ι´BÛ†«¹´«6o‰›iÏÿf¼ «'Äms®õlŽÂÑúæèq0®Ü‹^âæ8Bâ1›“O&9“þš…³ô›étÒæRüêÓl<i+6Ózîf<+6ÓÄ6Ó®Íô Í´g~3­n¦¥ÍôµÄÍ#$"þ�ÞT/ YØ0­À»¾Åvÿ†ñà0–œ<a…r’gÃÜÏ Æò“Gu3/ËOžËa¥R^ÉÑÛ¼XBâHŽÇ•…¼$Ì»<úbW^*¦a^îoŸM»ÄóhÅ?v¨æub»‘׋5:öÿçõcyÎ#‹òü?‚ç …5Dò¦RÞ(Æ‹@y íµÞBûd¶Ð>™-ôø-ÑX&·Äa¶Ð÷1·µ…¾û³%dò„·c¶”b¶”a°…掶ÐÉÁ-t"uK+æõ–vbva+·¥›˜W‰ÙKzh¶aK?Ö‹-×°Žl yëH¾Àz‘oÃY‚ü`Ô ] Êá$Gl'9õç§aŽägb®åg‘L=c~æK~ÉE˜/ùÅX¯çcÚæÓZs>Þ7%ò›p&6¿•äv¬×ùÏü.Œg~7Æ-ŸZ¶|jÙò{Iî#¼Ÿâ<€i˜O£Îüal!óGPÞjűÆVjÙ¶FˆW!t:¹°•fº¶fŠ@* © ¤g·Vâ8qk Éu8*ÜÚ€ñÙÚˆcíM8¿ºµ™ä’[ÑÞÚ†¾ñÖ’;ÅÏáÙMíC¯~k?®Ìn½Fò�Ƀ$à ðÖQñKˆI-Ƥ€vûÄ*“�y†js p”WP¤@¹-(!¹L¹8_&¥gA“íLA³r7à¯N+ø}Êt@è«‚Ûh…q[¤2 ïBs}Ûòð(¶(sáYLr™Ïrãl« ¹RI€g^m:çô.ÕëmW•e€ü ‘Bš+ Vî„ÚðBÚU˜†ó‡…tÒª0—ä<,3…4»XH{) é„N!yÑ…ôWšo,¤ùÆBò¢ ë°ÒžÞÂ&’[IîÀ¶®°“ä.œá)Ä;FD!µð…½ô×~’¯‘Lç4 ‰9Dò0ÉtÆ­p{ÆígØ¶Ó ©íôÊí¸‘LlÇvo»ëÔö(âÐJ÷ö4üåúÖðv:yºoµÅ§púšðvÚÙRT‰Ã–¢*t*ŠjH®#¹»ã"¼Nµ£[Ô…ýlÑ öªEô%£¢a’GHÅU¡6Ü«°#™;"H¶“‰;vPo¾ƒfvćk7;âñDÞC6\]‚a´ö…âªÐCØÚ?d'9’ð(\czˆz‡‡bqÇæCqô×$úk*îÕ(äL’³PÿC¥¢Jˆ8¼^<D»ÁÆ»ãÄÃå8'ÿpÉÕ$×a]{˜êÔÃ-âGðlù—‡ÛIîÀšõp'Öš‡»Q.®Ã1i1Ô8пõÓ^”m¢ží8òÝÙAr'É]$w“܃#âØBîÄlç0É£(ï¸Î»‹>¸+×mwÅ‹ßC(Ô»í&od÷îÊÞ=Ši*(—ØH£ð ÅGÉ5ñ/x’ÇþˆM· ÅÑå#‘Š?<S”q “ö >B-Ï#5 xÔ+Pži@Äí£¿Ò~Î=!Xß÷„*жïi!¹CÑÁžNZã=] ôã{z({z(«{úHî'yå½6äï FþÞBBI'9BœÝkWæ1ïû¥t©”Î"•âí¦¢4 w0–Fc=-úYJ³:¥IØS—Ò‰€yoÒ»tn¨”¾LZJÖ•V’L5¢´†dòOJ;I¦óÝ¥tV±”¾ ZŠ7ðˆÒQDµ"ò(©y4ùÚIŽÂþôÑxB’Ð_z´OúÌëÂ8<J»•öÑ©´}ôæ}Ô.í£Ðûhçÿ¾p’íXs÷Ñlê>Úù¿/U|Ü—…=é>:Y°/ä’éË¡ûè„ø¾râWâQ²y´‡jí¥ÙG_JÝ×C2}År}cq?í ØOóºûCÑßÛOßfÝO;(öãW{ÄþX’“1m÷Ó¢÷ã=Àbnfž÷; e?y&ðDm”’ûéFŽýx¿Øß‚sŒûÛP.³âîš2ŽƒÊ‚qwMY(NÙ•…ãÚz™'|Ê" ‰"9Û¢²Xl…Êâq(W–Dr*î¨)+Á¹Ê²Rœ¥,£=áe•Øk—U‹àIß+#ß»ŒNÄ”5Þˆk”e­¸FYÖ£Ú²N܇SFßV.ëÁG­E–Ñ·½ÊpÜWFë×e´[6Œ-píl/£ï÷8wtÀŠ+l8×w€N5 -ghòpŒù’í˜SèÔÿÚ‹~ ­;GrîÆ™_æ¨ôÀ/#ˆTbÔ\‡¥è@=Éô]é$7“L7± ›O´‘ÜAñ¡YÖ½hã²ñÀ5’i–õ�Ý:r`˜äQ” ”Ú0UcêAóŽr(ÉäÿŒÄx0ŠdZç:Hë°iö Í“¤yòƒ¹èU,Æ4?H§–â>¨ƒe$Ó¹Ôƒ•ôW:—wN3¤ñËÁ&’›InE[Ò¨ó`;ÉWÑ<ØC2Õ܃tÊøà ÊàdM€ôLÂô<Dmæ!ú2õ!:esˆ¾8|¿I!ÑWãQïov‡hÿð¡r’«±Fª!™Jø!únõ¡â·‰ ”• µÿ‡èÄè!j3ËéLe9µcåÔŽ•Ó™ÊrÚ•]ž‰«fåYJ'<sñÜPyŽ ÊKq\PN£¹òìßËë0}Êë15Êé­Ü¥SÞŽ¥®¼Ï;”w’Ü©Q~Ë9í-§Ñ\ù®u–ÓNªò’GQ~ÌŠ9ø~[ 77¢Jr8Ö‹Ç¢1VÅ’Gr2z)ÑÔcYbØŽw‹Çhgìc½ÊixR»÷X?ÉôEÚÇð#ñÍq¦³´‡éäaúþõa;!ôõÃÑ$ÓW¼Ówä'a.NA¿èp&ê<œ¥@½>œ«€‡KÑÒô.|˜ÎS®Åü:Ü€6¦½+‡iTr˜F%‡ÛHn'¹“ä.’ñÆ<q˜îg8LçåÓ7 l%§‘Èã¡X¯·cˆSlÃóÛ1§3hÓéÅÇÉ¿z¼ƒdüvƒxœÎl>ÞG2}嶂ƘÔ/TØH¦oÓW„c®ˆÀ\«HÆ#Wóÿ õWÐÝ• è© 6¡¢šdj‡+èö’ :_YAq¨ /ÉVtc WôÒGáÒ·ì+®‘Œwt‹Š!´«ï|xç›8‚7‹#ô Ù#4N?Lmº;)p„ÎS‰ÅØIžë}íñH&ÖŽ#9˜ªGr±œ¡žëõ\GŠI¦s=Gèn“#•XžÔc ?BeûÝãq¤ÓÿHî7;Bõý~×L¹Š³ŽGú)&ƒØö>í𱸭(^`Z=Aiò}½ú‰?=1„—>1‚«“GC•Kð¤uá£q ŒûŽÆ[ ަX wŽfYÀ®£y$[ wŽ–øAXG+ý³ÆÂ:Zç=ÚÑz< v´Iiƒg³òðì g§c·£]$_UÀ·<Ú£€oy´Wßòháý ø–G‡ð?Ž"§29•!ˆTF †Ê4 ¥2“ä,|«25Tæ’\¦üžU x¡•Õ$×\¯€wZÙ¤À˜®²v’;HîVzáyUùxöøAOZ9à=iå°ß½ðõ/î˜ðƒvøX°Œ2Ž…ûÁÈâX„´<Çì~;Ç" ‰§g’´äÇRHNõƒZs,ä ?È»c•~PkŽÕ‘\ï9x¬Åï>x¶[ ´ë²@[z¬Ûmï±~?ð ø½ÏA¿_…ˆã¿OzÜJr°xËÇCñë¥Ç#ü`,Üî÷xF‘µþ8•ÃãtGÙq*ÇS±FÏıØqêwŽ—c>NåíøUì÷â.ýã}–×àÙoy&Dœ°â¾ú¡–—áNˆwªŸˆ²4À3Kø‰$žH±@ox"“ä,’Kpþ‰2’ËI®Ä½ú'j,0ö?QKÚê-àþ sú'šq(w¢Ïœ S'ºHî&ù*É=$÷á郖‡á9h)†ç'F,àðWY-0¶ª öû<Cý`ìPNrÉv¿Oá壃ªh’ãHŽ÷³*wJTeZÊá™e. *ÇržE¨ìUŨ5U%„”Rc¿¨ªÏ>T5Òl1WU‹ÆqU­$·‘ÜnQXUÉ$w“Üc9Ï>’ûI°€·P5Œ§*ªF±üœ¤ÝP'#üÀË:‰uðd¼Ôô“ÉXòOfù¯x²m9Yê}oÂXëOÒÉß“M˜G'i¬w²ÍïMxÒàÉ^Âû0¿NRëw*‘S¡ˆœŠÀ¶÷Þ(N¥br*ä,K§r±´œ¢“‰§*!(‘€7Õ‹SÔΟÂo‹ˆSÊ©N’{°žêÅ\>E­î©~Bh¦îÔ�¶ö§h~õÞe*NшãÔ!£Ø~žøí˼µ^œ¦{«à¹ ž´Wêt ÉYxÞðtN¢žÆ›ÞÅé<B Я>]Œ¾ôéúk)zԧˈSNHNiž®Ä91¦þâ4‰>Ý‹1<}˜¤v¿Ÿ"y˜ðÒ?Š!V[ñ¯Õ6 ¥:ÿZMS¦ÕУ~꿪iÖ¥šæèª³0m«éëöÕ¹„ÐüR5ÍiWÑ_éfÂjüb¦¨.%&¨­.§¿V`UWâx$æ|ª)ýá %­º×tª»p‡Iu7!´CµšFCÕ½$÷§gª¯Q¸ÄÄn®zˆþ:Œ+MÕ#”JÃÊò$ÏÐ- g(/Τ’L'IÏÐÃ3t‡Ï™\BòPç™ åLÆäL1qJ0”3¥„ÐMƒgÊ ¯ w+1Ü3UxOxâ­.YO('gèˆ3tVâÌ ¶{gñþOq6g‡Î†£÷r6’dº£ïlq’°\¥ÓúgsH¦›ÙΖb«x¶‚ìyÏÖÑ[õXVÏ6ÜŠ¾ßY¼­Tœí3 V4ÎưçÈ‹;G_¸HœI8åõ¹\‹p¯û9ÊÍs¥xƒJ"ùä稆žkÅzqŽÊÞ¹«$S£Ý\çÈ“<Gç}Α¥54¯ G¹&ã_…Þi yª5q$Ç“œDœ’SI¦¾ ¦„dwÔTb¿PS…)PSMH zz5µèQ×Ô‘\Orz 5èÔÐÉ‹:yQC'ŽkzÑ'©éCŸ¤f€äA”ÏãW´Äy¼ÅWœDmç£PÛùh’cIŽCmçãQÛù$’“INÁÖé|¶NçsHÝ­w>ï!I¤ó€OÒ<퓱X>Ÿ$„êÑ“„\!„ÆæOÒýOÖc.<Ù@2ÝCõ$}³)±‘˜4Oûd½ÛŒÈSt#ÄSøEñåõS$'Ñ[¯‡üÒ§*§[³ž¢ùƧš‘“ôqÈó‡'ôøOÑ(é):¹S+0¿jéFÇÚP’óp® ¶�ç j‹p® ¶Û–ÚRlyj˱ͩ­ ¤’ä*lsj«q][‹£éÚ:’q® –ØjiÕ¯¶G²µ8’­ÄùÚaôfkGHEü‚À¹p_„gÖˆ v\ˆD hœ{ƹh¿ñÚo|ö_ µã tÕ…,M\ÈÁ¹‚ ¹è£^ÈCõBz¶Š0¯/c^_(Á–ÿB)ÆüBÉ4«sjåºÓòB5Zw¡†ä:œ+HzÓöÕxBßtrðB?i ‘×E+âmxÞê"­Ó]ŒÃàb<Éøõmq1ëÅÅLŒÉÅb\5¾XŠeøb9NÉ^¤óqæÅ&Ü9s±-ºØJ2Ý>z±Óäb7Ú~‘ö·_ìÅ4¹ØGr?á×p³úEºñïâ®¶_¤û</Žb»Ý)h¸dCæ¥`|ëR(j¸D» /ÑÙ‡KvÜßx‰î¼Er4É´ûôRîR»”Bx*zV—21þ—Š0þ—Šq·Ï¥r,i—*H®$N5q‰ÓLr'ÔMò5<qiùuVÜ/Wg#9ß­ EN]¾[O8¶¨KC[êhö£Žúåº,½u”žu5įÃiÀºÜßUw•Þ¥Sfu#س_¶"ór†~99—‹ÑºËtwëåjBˆÓFœnÂû±Ï½<€û.âûå!’G°¾<Ч¯Xq7#8½àC^ %9õ\‰ÅÓW’EñÞ]%{vì*-ΟŸ"<¿ñŽ Õ]Žóm@¿qTœ càÜ$ÄK͉4âlÛ³g×n VqÔ¿X]ŽÚ®FœwìΗÉ*Ž¿ãŸ[Š‹woõÂqêÙ:NážmÛ¼†åäì-ÝS¼m—1gKþî=¥&zöoÝkª§pÿž¥ÛŒ9{wlßµ¥Ø˜SP¼»Ä,¬‚â½všÄÙù[i¬gkñHës íÞcÈÉÊXFÿÚñ^@ð†–8×Otü›3k¾–cur,nN|¼ƒeÄ™íàD{ã8kìt+¡!'ÏŽwzk^¬#¬xά9óˆ“dÀ™íÔ“lÀ™3×ÁI1‰s pR½q\å8icˆOæâ“eŸ£ÀÉ1JŸ‡žÜ1¤sž'ΟoW}N‘7Ž«ýN±‰]O§Ä„ópJ½q\m”ï¿4Öó];ÞŽiÌù p*L8Ÿ�§Ò˜£Xíx§1g’oè4(?óæ’]5&zìÀ©5*csæž:#NB,qêMš œ£r'HO£‰ždà4yãìΙ5{ÞÔÓì³u÷£»œu°Å$¬4à´šp6§Í„³ 8í&l7:¼Ö§ÿcÇ{X½•g§ÿcÇ[ZËá;Þáj̉µã ¯ÞÒÐSO{¼q\ý²o‡õgg¿lÇ»c½Ùîì—Óïãê—íxû¬WŽ£_΀‰í+íx­W=Ž~ÙŽ·Ûzå8úeà ¡^ŒÕeg:j¢ç°]L&œSÀ±šp¾�› çv¼Óטó}Z’5æô'Ô„óà„sü&'„s»o 6æÄÇ«ÿãäd�Ç«ÿãä€ß2ÙÄÿñÛÿÇïIàxõœœ/�Ç«ÿã伯þ“óŸÀñêÿ89?މÿã7¯þãg±Ç«ÿãäÜ ¯þ“38&þüÌÉ^ý'§�8^ý'çp¼ú?NNp¼ú?ŽÖ÷É^ý£ÅdÎÃrèÕÿypëî]¥ÛÊJcâÿXZìx¿¯7=%{v—nÛŠzLüK§oì6æüØŽ÷s>´ãmßÞâãÊ�ÇÄÿ± Ç«ÿãøYƒ€câÿXï°ã½ãÆœÀñêÿ89KcâÿX×Úñvsc¶&þõàxõœì ¼ú?NNƒoX7æ€ÿ<ÙÄÿ±þpLükïq7漯þ“ó7àxõ?8&þ¨ï’7æÜ¯þ“~Ôd¯þ“³8^ý'|ÈÉ^ý'g;p¼ú?NÎcÀñêÿ89'€câÿø_ŽWÿÇÉy8^ý'çMà˜ø?þØïxõœœŸÛñ¾cÎïì¸8iÌù 8&þϸë€câÿŒ_"ÌÄÿþs˜‰ÿ3n pLüŸqëcâÿŒ{8&þϸýÀ1ñÆމÿ3îeà˜ø?ã¾mÇ/3sìøÝcÎ?íøUCN�fâÿLމÿ�mx˜‰ÿmÇ/KsÊcâÿœµã·)Œ9ÏÇÄÿ ø*pLüŸ€và˜ø??�މÿðK;~IØó{à˜ø?68^ý'çzà˜ÌÿØ }3ñl³ãÕÿqͽÇÄÿ±aÙ0ñlø?¶­À1™ÿ±íމÿc{8&þí¢¿vbÌy8&þí›À1šÿ™=;ÒÐÄÿ±õÇÄÿ± ÇÄÿ „A\˜‰ÿþs˜Wÿç¬fÇQœMüŸÀtà˜ø?¹À1ñwÇÄÿ |8&þO ¶&þOàsÀ1ñ±Ý0ñ±Ý0ñ±Ýðêÿ`:Ï£¹Á0¯þrpQ8&þO ŒÂLüŸ@ðüú?4WéÈwÿg¼ÕŽ_ò®'n6ͯ†™ø?ã#íø}!c¶QÞç–áÄ:Îg†™ø?ã¡ 7ñÆCÿn6ÿc‡pÿg<ŒSÂMüŸñ;>áeÈŸ$ÜÄÿ_ ÿg<”çpÿg<”çp¯þ•CynâÿŒïŽÑú׬Xjýú?vHd‡¤¼ñÙ¿þnÙîÒü ?ûì_à ÜIq¥Ù¨Çbwê‘£#ÆÿAæ(mÔOÍù8Æœ €ñt8rü¼sîõÁ&œeÀ1ád†ˆgRL8›íâ:Oûœâ@ïðWÙýEˆ§¬;ÓGµÆ:þv1qÔ˜dÎÐX9çngœÕœ)ŸP=n鹪«ëN/îx"=tR ãß»÷mÛöHÌ^xûýÏþŒ¥%ëî’XZn°»R/gÄß=N1»÷Kæ(ÿí ÝÅyС8Ñ^9;·íÜ ™qP“({·•’לV8 <''ÜVD¨­p¦ÏA üÑù¼òÐÎxë™~÷[Î-®šS¼{×v …ˆç2ÝœGÕ§æÊñl–«¦ˆb]N è2áÔƒž7§H—Ó"rM8­BLx�ÚÅÕJ}[øßó ®€È{ˆìô¼å¿¤^Åq#*Nãôj9Kãµ"s²û´¢â|Â8Ÿ0ΧŒó©–³îe-'ç-'gãìcœ6Æicœ«ZÎÆh-gã8‡¥Ï&–>›g€q†gXËÉZŽQqÞfœ·ç]ÆyWËy FËq *N2ã$3Nã¤1Nãä88ž¾²=ÂU›~]åà©k“£ñÁ·ª]È- ‰cHCdÈn†Ô1äy†üˆ!¿Ô"J0CneÈ †,b³BÙÂ= ©dÈE†|…!ß`ÈÒÏa-âèUÈ ‰gH&C¶3äq†\bH Cþ“!¿`ȵˆ%€!·1d.CîgH>Cjòe†°¼°°Rgù5Cþ®E¬r7C2$‹!;rˆ!§r™!¬ÔYßdÈ÷ÒÇ?h‡‹©B&1„ÙîŸÀ4†<À] 9Âó ù"C^c+«þ?eÈo2¢EÆ2äf†°ög\2C²RÀGrœ!ÒÈ×ò6C~Ɔ°ZÀZ¤�ÖƒD3„µÆ¬^2dCŽ1„õ2/2ä-†t3¤Ÿ!¬5¶cHC¦2d>CV2dCfk%l§ÒÁ3„µu¶¿j‘@VæYŸ8›!© Ù̽ ©bÈ3 y™!ßdÈ» aå9ðŸZd|,CRòC¾Îwò-ÄúÁ V6‚X¯ÄZ‰ ÖB=Ço1ä=†üI‹³ÚÌÚðà3 aýéu¬¿¸nCžfÈ« ùC>`È¿´È„{Âú¦ ÷1„ù´˜78á$C˜^aë‰&ô2äc†0»&†0ä.†°5ñ^†°Z9±„! ©aë…'²z1ñ{ a¶OüCX¿ÌÛ2‡!˲ž!Ì 9Ìs a¶‡¼Á.†0o9ä÷Zäz;C؈ìú2†°Zy=k¯gé|Ãd†Ä0„Ún`mË$Ö[Mbã¦I¬žÄj÷$6ú›Ä¼ÓP6† ½‘!¬]À6 e-@(«¡lÜÊÊF(«ï¡¬Ì‡²¼ý‡¹1ˆ!¬¾q&CòÂú¯¥E&³qådÖ¿Ofš'3uò×´¹Hj„yMaÓ’ÌÖƒ„±±CØQ†0o9¬™! a£¤0æ †³)œµÆá¬5g5%¼!Ì3¿‰•ð›Ö2„µ�71¯à¦Ÿh‘›Y»™ù?7³öðæ-r‹•!Ì“¹…Öoa3 ·|‡!¬/ˆ`)ÁÚùˆ yJ‹ÜÊ|‰ÛYbgoÝÁâs³ô.6Sq§D–k‘»Ù ÃT6'só¦­fó@¦_§E¢YëÍæI¢ÙLÎ ÖªÏ`elóg|¤Eb¾Ä6wó-2“Õ‚™¬dÎde,ö�CX‹Ëff=Á+ ¹ªEfïgH«™3!,/æle¿Ç=«Eæ±ñÅ<ÖÏg^Jü yO‹$02õq‰+´HkÇ’Ø|ËÂ¥ aÚ"6ÞYô¤YÌæ@3K—dA/d›™\ÊfÕ–²þ"EaÈ aýW ó.–¿Äv-²‚µ¢+XÝY™§Eîc}Ó}lÿ>ÖßÏ8÷3ÿðþ¿i‘4æ-§1ïtó@Ò»2ªE2XÛ›QÁ6WiaÈ-²šùØ«Ù,ÄÁ6¿–•ðµ¥ aH6«qÙlµ"›YšÍf{Öùk‘õÌ^Ïb¸>O‹ä°>7‡õrØŒÇ6´á&†°öp­oHcH.CØ8n«MX»±õD؈uóÞ7°m›YÚÈæ@6²9ól½`#ór73„õMÙêÀFVV7²2¶‘F7þY‹lbùµ‰Õ”M¬ÛÄz«\6ûËZ’Üo1äšÙÌ<½ÍÌÓÛ\Íok‘'˜O°YÇJÖ W²‘T%kŸ+Yù9fcÈ ažÞ1¶¢qœÕå¬O9ÁæW«XúT±‘BÕÂVªØ çIÖWžd5÷$ó{O²6ü³â›I>•ÇÖŠžbuð4›W?ÍF¬§YoUÍæôΰ1ѶZqæ°9ÇÆDçØÊl óÐj˜guž γùó¬…<ÏæÏ³zqžõË燴ȓl¼ôŽs§ýaçf&%XøOÂB=€ßmT½Ÿí)w.Ÿ1ùŽ[j\;¶‘·d]$Øï)œ¸ ߪ»óJ\3wö°$f›KÏ7…¤g9×ô àDÉq,þQ1Àö7ˆsÚ¦3éq…õ„NœW{âœÿh¡+η:'g³Ç '‚íü-*KÔM¤¤tOâž„yߌÈQçœK³©‡G¸ñ¤ÏÛåRœ_ÕIŸÓ(§Å*üï v¨Aú|EˆñUÚô!=ΰôéðؾgÛÞ’Ä5d˜:}º]–º‘ÿaéókW:ÛvíKœ;}ÚTDŽPiÙãHù[´©ñN…é:©1 œ&95Dî:ƒÔ¸Õ 5`¼|]µ65H3,qHæïݱ=qÍŒY(ZµåùAé­Çy ƒÇ‡à‡ †~®: 1„þÒz÷ÖBY¿nXCÒã KÑ©;ryvê ÷Äyç^°"~>šáÌÓ­Å{!åéØþ­Ú”ÿ¾Ö»Â€§j[º…ÿ4;·ÀÀ.è‰&†kí"=ΰüjÇd×\]Nd‰Ë.WýªM÷ØU¼m–1´ÝUÆòó¡w×c莜ä Óú·¶¹ßêÓ{ ž4tpœv½ “†w…ˆUVA¾¦¥¤!ô•!×´iø©]õÓɯZœÏ¤}h®†°ç@rU®ð橜•-P!hG‘ñXúÃ")téÖ)·¥3€S¥²t,-Ö–BûwC±ÖRÒã ë*K.-{¡5^3w6ΪK ®ø¨K î9Ci¹*¤|¿Ù¦ß[Æ¥åj®Þ[øó¤¡ƒã´ëw:i¸8•rúõ ÿég];•¼¤á‡B„iÓô¸z™MÆi(õŒ¬S÷hØÇ9{½½;nKÜ3C7 \-¥Æ]î4ü›wªþ$ZO:U§¥é¤j6prµu0J¥õ ÷TÅj–¬MÕŸÈ9øK®ƒ{÷lM,‰‹Åv̤^ÀÕä1ÔÁžN)ô3:–>œkªžH€¥0þ´ž6°4œ”.­¥¤ÇÕ;¬ãaÉågw‰«Ûãä:x÷b¨ôââ^§´ü4NÊe÷ ¿õh³bãQµë-wÙø©\ã&Êqó¤ØO¥Svè¤ØCÀQ¥˜_³ðŸõ×Zob7±û©”b~ÿâaQÙ(sä)Ö¯G’•«JrŽªýÛz–·¯åIÃÞP)5¦¹ÓpTH)Ö›£ÇQ§Xo±Ç ‹ŠãL±À©QµQQbýb_3H±Sд”jSŒô¸Rì5–¯@å«_ÀY#ÿÿ?áçiRú¸O÷X&·ü?¯6/‡ŽÓÒ.T=œ6¹ŠFáS©ú–AªBšÝV¯MUÒã ëÏcIÕ 8Ÿàì=É_¤µ7G]–=Æ_È^Ü_u¬x8¹*+êÁ hˬÿi`Å{BÜÎZÚ_H-­%Æ‹ÎÞÊ9lîÖFqfÅUIsœŽ_Έʊ.°¢¬ø±Ã`E³bD +Ëμ(ÄFÁaˆÊ“¹ˆë·Jý²Ã®�­]}uRXëuìúp:Tv•ÿ™à¯[îÝ®‹aBصv‘WXU<¬‹XO-Ú‘Ô/%ÖrQ'†ÿ œX9†ã* †à)[ b¸Tˆ;Bµ1$=®°.ó°ä”ÇQÒi”T)µ ïÉ}Ó8×Û–T¹]ý°FâDÉ¡xlÿPjë,ßѱýà´¨z¢:á[¶ÿÙÀvL­ÕÚNz\a©î¾‹í¥J\þ˜E[Æ~#ÍYŽéXñ7पÊäରâŸVCw­µ‚ô¸ÂúO/VXØ|¿ÿ¦^Òó=®çº`àt©â\ q†Qª€AœO@œ›Yœ»¤°z½Ä9ÐÕÒ:c¬çßFJzútâœ4çoAœ¯7ˆó3BLÕÆ™ô¸ügÕ™V)ÎVZƱ87Kzl:qžœ«,Ο@œo1ˆ3Œ§·²8Km¸5È8ÎN=8ö`é< ÏNЉóàd©â\#ügßæ:uá%ÎУEÇkãLz\a¥z‰s yyh—ô¤éÄy-pT>­h‡8gAœc âüˆ3ói$ŸÖò+/qæsÂãXoeÕ¶$¿+•4ÿZÇŠíÀiPYÑVÔàMWÞ­¸4wÆh­ =®°~4f+¬æV|$×ÓŸêXQœLm«>»¬Xo`ô!1qZ+H+¬x–<¾(ØRº%±„|‰Ty4A¾D\/.% 7x ç–Øí£©³ºÂ´|Õg$¦iÇhË=c°[æÛ$N¨[ó‚òFsõâãå­×ã3X¯÷ÅÇÃéðÎ)Tqœyq¿`¿ëÎg@•ïÐÖÍq£?­•yÉ÷ÝBÄvkóô¸Âàa]‚žH=c WúÔ+úóóªqå%\?½E7]jžycwÊÿ¾BJ÷]–ÿzo¹Sõ÷Ò[שRÕ­ù“‰s‹[s®f³·Þu¿…¾C€6>ŸŒê½EñÇ•;H‘Zcuv:Lm�N±<^ž%aJí§öRÀæ9™Ú’ðil®ìì‡-€¼£¾>ºk«£ðO’[€K×�Iv [öÐÜ)¶êàÂ]¢ölÛ›éÔ“&ë©Ãý$7É¥¥÷ZTŽ-å2V— d²uG!”C¼_¤¨8Ø2†«ôìÀ€Šƒ«7þ*žÓÌUqpZ’ Á}•Î2¿eÏvW«>Y[¢†Jõf-0“æEÕ–º÷GÉel¨\ÒsBÎ))¬>ݰþ.ôbèÖüÇé­Yúšÿب«ù_&š{Æ ¹WO³Õb¢yPzk޾æ?É©ÑïÖl3Ö<<ª÷–Zó_Ê$N§[³»%ªC/;B«ù/•Ò[ª±ƒ¤ùšÄù±[ó Æqþk«Þ[jÍ‹ÐM0cÍ#Yæ©1"—̺5ß>έr 9Òéá(ª°ÆÂÉ7æX#M,ƒf¹ÔmukvÏ(©òÝì­h÷[Ø÷i½‹¿Ësß×O Ž;ô¿×Jœw躩á~ëÓé-O.æœ!¯[å–í²UH~¯[”>~*ï}LO|äšëžO°.ñµ¥ý‡ì“<«Ö?d߯ÑÖJ_à ×Ó£k4Vâv‡µÊ×°þU$鹤ŸÎFœBÇ饬ìwÝ‹À©ÒÎÜ&@­÷§=Gú^Êå!–²ÕÛI«·¯Ðó3mIq–çN»uâü!â³HUœÛ ΠeœÁ>¢ËP‡—Ù´qþLÇm4Žó^×z³fµëòRáž63~Ö*…•¯c×»ÀéSÙ•$üñ>`»ÒÁ®f—´?Áúu™/ÅgTâ´éÄç—!ŠWÅ'â“ñ™lŸ >£šø8ô¸ÂzKæoC_Ô±G;o¬ˆxé­v~ Õºƒ.Ä\Õq%1܆h×zœaùçð°øêÉeiü%í¡rŽ¿hu{ -3¨vL]Æ]07kf›1"Õå{]aZŸ’ÚE‘×ÄÝólÖ“R› (rû(Ç_Ò¯«çŒJOòôèr¬çUzä6j†JÏX8ž°ŠuÓç¹1pžçiU|Jô8š8ÿ¿q<ñéÐÏÕ1p~¨â I÷…SÖ/«â3,qnðŸQ]=/ÉzüdOf’¾?9Ý»bp¥XÒ#§Ï-^ô4éŽ8^Séi–8‘^ô´êêyC¥GNŸ{¼èÑåX¿!ë±s=*ŽÛvë·Uzlæéc‘ë²ÛKµ~W¥'ɼ~YRtõ|O¥'Ó\¿<IrëéVù?Z¯Rñ—GÉ*ÍŽì/óS¨â8ÛùÁ~×} œ6yÞFéþ ®AŸBþœ—>åKP5µ+ò=®þ+‚‡%ïâ‘S3žœrî¢>¥„úÕ®¡Ëèm9}i¤ õ@”€<)>ªšâøM˜�œj¹?UÚ…èWm`û/„¸¿Dk{€´óÁª¹Ïm{Ñl/a¶—ªlÇþ4ÀÜö@yÅç&ÛãZÁ´tí`÷¸:Û?b•vÓ¡ÇUÆT#ǯR]þ§Öö+Ø£1ÏA»ú¦K»”ýuöLNˆ Q®SíRýÂ!ŒƒÇ½àÝ®+àáejw);ô¸ÒPg÷‘®àª¸jŽšvø3¯‰ûHªyì+xªE»óA¹®[ŠO¢Ží±ÀRÍÁÖ€í*ƒõWV±º‡Ù>ä kœª5vÚÞ¡µâJ.Þõ&—UÚQ¯Ú³te'p®98žùyk<{yOO§©ÜÕ—ªBðd´j6õ ®ˆ$«ïàî6‚kb^gJœ÷á‘©B>šOãz¹³ÆI;p\uÐÝ;\/¯ÝLv¥œ¿{ç„r}—Þ8׊Kz}ÍœŒrCˆôÖÓBú‰ãý†LÝ8G«ôȳ²S¼èiÒÕ«ÒÓ<=ºz VI;¸X{ƒ4Ï&²¼hîÖÕœh¬y’]z+[_ódy¢ûSÊþ‹õfE<š'§ê½¥Ñœ¥çÍòÒ‡õ5‡µëj¾×Xs¸Uzk»¾æ›åu«·æUrI¸¹H£ÑS©Cwîú\O=¡Ë³ñªg)ty¦Ô}o—4åäºc(µê†ñ‘F7ã:Tñ ÇŸaÝøãþ¤ûXã|K•yœ 8îøDÈ톫#þ%càì’Êӧ^?>wX·Êk®ãˆ´ºíÑskšÇ–«?½5GêOÿ)ØoB2p*T¾è ð_º¿¤„ÿ§ßw?}»[ ´}÷­ÒY3Ý^öÅ©gù0Žþ®™À=ª\rou»<s›;}Ž ½·Ü)v{¼Þ[”bÍ)ºšO™hNõªÙ·ËþêŸxúLHN±Ê‡¬ƒ¼¼ ›9¼äxˆ[µkÙ=®°þÆÃò²ëÌê-/$+ú¥ cÅæÅnš}hËVàw· ¬¸¬ÐZAz\§ç[áÔ³_kÅÓx“¡{äâ^‘·©8¸ƒÕ}º´s/ AÇÒJà¨gõ+ÀRô{3 ,…Ö®@;«ïÐã +åÿÜRÉÃÐÙ;7áÅå›Ðì[vع–¾ –j-%=®°T>Òÿ®¥õ‚s¿–ÇÒ;ʤÐÝ=¤déO€ÓÆòô`ïðniý  “ÍKÜ!ÍK öû÷æéÒºCÀC:–þ=D¹3^kiÊ8×mÄ^,]$Da„ÖÒ;¥„�•ùaéÒ‰ï€2úÄÛ€£ÎÓr°ú·€£–B~²<½SÎÓ'ûý¯•Þ'u-•óô¸Ž¥KB”»â´moJ:°k ,m�'Z{¦Ø¡Ç–Ê7þßµT?Oï’óTçDóÄmÀáyºØÏXú_`)ËÓ»ä<}öÿÜRi6à :–ž Q"ÕyZ –^öË– QÄò4RÎÓ—|µTš?TÍ®<ƒ3 */î™yžÔp"÷jSãl}UžÂïôIò÷ÌŠwŸÖ‘ç£"¥³·-:)öpÔ'Ê ÅÞv›÷{椘öÄC+¬×û§˜g¹æ¾¡çC”»“U~x Äù#`¿mç¯ ±#RgÒã ëÛ^â|³·8ïÈ×=ñý Mç>Ó/Ü;ZñŒó¬Øô!Y Ÿ"í¬ø OˆœJ¡ÙQ¿<Â�ƒaÏü^ˆ‡´ç²z\aéœìàs§²íFVL –4¿¯c…8êŒ+ 7 ø•w+žÏáa–ƒSåüÈ‹^s°ðÑ]® p#[“)ªÈÏ& wžÙ%Íêü]Çöà¨loÛìAÛï¢8Uk;éq†eS¿@?yEƒÛŽmoÉÜÙØF9}¿Ý……€Ì’Îïrý?x<Zÿðɶc‹‡C”ijO¸@ø¯¸Ó•.^,¯d'ó„§Iž°Mu²Õñ3n[œ½Œ{ž¤’,]ã°ÌÙÒîrý¿»¥õX:M:ÁgÓY9 y8ÅÚ<]±Æ•.^,}^ˆ]l4:MÚT·78~cÉÓ9¸ò¹òtzúm,pòfˆÂ,…qšm’¥0ºÙÍÖn¢¥µ›Î};c²tÎçµt†4º±M×±´8­ÌÒOm7°ô3!©ÓZ:CÚgeÓY¥“¥³>¯¥3¥“’6Ó:×�'GkéJð„m3¼[ú\´{“´–ΔæÐlK>§¥³?¯¥±ÒY›ÎE×Ç�g”YúE`'XºAˆÒ~­¥±’—bKÿœ–Æ~^KgË-¿Î <×gGufÍ/,ý+° nûyÆIû´gÖz\aéÜß"Ÿöâ–:5ãíÓ§œ’mÛö$&Äb=P­>÷-¡Y|W'Y'.·éØ~8=Ú\Ný:°³ lÿ9Ѧµ=Nn½Ý²òoëaç%I¡?ªcipÊ´=ì½ãïÝÒè“æj-'·‡‡yXÿÞvž\Æ*t,}8}Ú<½j­m·¥Ð’ÔîDuèq…u’‡õïíaã¥SZ6›s®N³ô,°Ë ,}�g« ñÒ*ƒMç–ƒo› ´]á¡ßpCˆ’É,ýÀ•+^,=%Äaí®i‡WX_úœ–~îv´³ÈöeKãÓ¥µô¾y®\ñb)´X³Qöi”mÓÑÿ{{Ø…²'ܪcéÀažð}§\¹âÅRÈó#Ì^({Âßüœ–~îv‘Üòÿ‡Ž¥•!Êbµ'\–¾lƒ¹¦/L€P˜'¼Xö„uÎ)÷°{¶íMœK‹.¬¯L–æl?äšox8ªu¥TøßåÃö¦s!\¶.“,­ËØþûsYမK¤»³l¿Ð±z‡%j¿®¬�oÆÖi`E®ǘ_·Döë®ùjîÄp^�ĬX&ç…Îéé å_¦Úëˆ'_î‡\³ýÈÀŠ*!N°½ŽËäé<¬±ÌÓ~w^h{êeÒ= 6ÕÒI7…()Q,/@‹ÍànŸ/¼#D•öN]‡gXª-ÿÎßJÔ,Ê –Ë¥ž1ÐÊ5OJŽjÎ\©þii®/%z±âOBœdsæË¥9ó@Õyó±[1W׊•Òšo Î¼Ä$hW&ikwZ-X1äÝŠ/Þ ®º]kÅJÉ ô6/abÅ,]+R%Ï3PgÎaÒaà¨î#R ^¤A ²ýÃÀŠd!ªµ÷9ô¸Âò6ç`bE¬®÷…Jšuæ&=œ4m^¬‚âh0GôÅíí=9=®°¼Í'˜X§kÅýR¨3W0é?€ÃnîZuÊUþ¼XqNˆsÚ›»z\aéÌŒ¥’OºIVHëDË´zÁŠk!JZ,Ë è·# ¬h¢F{»—C+,;pŒ½ÇFÇGµÈ*Éÿ Ô™a �N£ÊŠ"°â¯®’íÅŠ÷…8¯½Û¡Ç–Îmc¶‚í"H—ü¨À :VD…(ÁZ+Ò{Žw+¾dâÉ!­ÒºC`¾+Lf*¾„ßÎs–¨­E.—„×”L¹=Ô¹Ù,ô^à”ªì*»ª½ÈÀ®$ˆeŽÖ®L醢@?¿O²ZÎwùÐÀéWYõ=zòÀ{ ¬�õ:µV¬–vCôÕ §æs.+ä2¶V:ËX®cE p2µe,ãN`gXQ/Ä%íÝG=®°tîKMùR§Ð©)Y²OrB«¬x8mÌŠ‡€ý ?¢Žù$Y²O¢³caLVüMÏŠliÔø¤Žï†(ëìÚ¶7ãU`?äÝŠFðÑ®XµVWXªÝò’&%ªûjV¢ÖKgo_žÖXñgàÔ²¼øØX=êÓeZ+ÖKç¿_ì7–¼hÌ:y‘#››¸æoŽzÜ4*ü3¡O 4ØíÓ¸&lÜ”#›_ñb…Y^àlˆúD3öïªÓ:8 :!Òˆëö¬eÛ({:ã÷㣾[ÊaæÛÀ>c`û÷…xV{·ªC+,ñ{#ŽÀưóa“´ç$Pçžs€£Þõ ¥.{üKÞãü<ÔçØìÊ&Ù»ÐÙ<æ8K3·:£þ÷G5/„ÿjAÌ=qf³ñ›d_â}–ñާfô¤œ;pwzë¯ãçJ»#ÿ c×Eà´Ëv…4]¥Àþ†]…hhÐÚ•+Ý£øc»¤È·x=rTî(�dŽû\›kÃóXeªåºóü³ÂyÞÓHç­<;½wʧƒÝ­ºíáKèßc¡ÿÔ0t÷Žñò=ïÈ)ãɯÒÝûã¿ÃÓðÆ7€£Úw!ò„Øh°kèù„x…í»Ø)­¾ÿ¹¼€§Ûåp¿ç¶U¶c|§Tê‚tñ}àtª¬è+ZÀŠw½[ñŒ^ibVH½CŽgÎk“l…yÐcÅ–‚×1$ím½Ê.©? Ú*Øoò$à¨Æ2"WøgCËøK» X¾ÊÆ2»$Ÿ6Hg/1oÙtìº,trg—œb;u¬HÁËðd+ÒÀаÂ`Öè…¯Úo¬8ô¸Â*‘ùžøì–Ê|Îúàä•ÀQßÍ cìW‹æ%>ßâ56ç°[šsÒÙ9lœªRœ¥ÝªA:£€ÉÅÀQïV-€8ƒWø©Aœ?83/w·äåé|c,%áË0Jà%aw¯¤YçKL“¯„(%Ì ðîÆ[½[ñehE[µ_Csèq…õ„`¿±¦|‰4b ªäz&wGµòˆ7ÿgÃXeü]qõ­lå±DËéŒeŒ[H§fìg\§ËÝ=Q‰|«âÒgÛ+Ÿö*‘n]Ò¿íP)‘OȺM²ç€*,é¤[êþù1q<a5ëÚut œ#ª°z¼Ú>&Ž;¥q\Ð)Á~“? QQßyU.ü׃Òb°kèËào¼ÎVo‘VoƒtÖÄK‹'ÎH·IéŒ=ÃÂÓÀâ =Öøùqþ!Ä™íŠyDò{ƒ.ü?ÄYÚ¤³J–¢ìQµ$·ÇBœáõñ‡ â<*Ä×YK²GnItFµMxï¸j´Õt³'ÎÞnëm qíW`”½²Çè¾ÏÜö]¹<Ÿ‘îÛñwÏpâj«)ǽW» ¿–{³6ô3R³ºG¸Š¼Óá¢ÿu{Kº?Óê¾™Óö wXø¥Îq²OÛ”çJYÏYé>"«û+¸¢jÊQÝÿs6Eƒ¿ÿŸs¤ þªÝkRúÈçèÝ_b²} œwTaI_ zÎK|¼sÜa“ï˜rß¶aûÉ8?–Ã:'« RnÇ–| „{G4®Ž›rþGë|”–êœËØ8…*Î68;~aiÀQ¯ÁÈe#x<ã©ÒoÇšÀËþ)óEÏ˾¨êî¾};×ÌŠ¥åÒÛ¼µcN›/ç<oÙ<v=%í<Òù†QØFàDËvá½ú¡ÏÅÀ.h}ÿ;Dk×S²?¯óU#>"kúžÇ.þý'×ÝUw×4áÿÂTíÎ:°Vô)ù7÷ùeøá;÷Ìp.j¿B¥ÔÊ¥NõE Oª^ÖƒU7¿9Sõ0pTãÜ€Há¿©|sá+à}üç^FmÁª/÷5UK©Ú/Ï^~%N›ª_Iî/ó:vöä+0ö7ª©òë+è!ލ¹íQòu¨?VòŽë«ÄnäW€„8­[][”pÆÊí2ò"ä³bW!áx>ÇlÙ³}«ó‹<!rùygÑ»yñ ¶Îoð9ïÚº»`›s Jå9¼8_h¾ö"žçVݸþbŽpß´¿Ã4ü&©8¸þv Ù#Ü÷;leTeþŧð©Bpöýò-xLT!¸'T[S.Ê÷¼¹ e๽(ÝúìåVÀ‹º#ŽÀ˜1pT÷ÿ\lôÖX8ž°ä{<Ü^\à¼1pâTaµHaÅz‰wN¾ÇïewX‹ÆÀIR…Õ¦ÇÑÄÇ;'ߘ¸b œUXícˆwN¾1w˜rÒTauŒ!>Þ9ž°:uËÆº1p²Tau¡üxçxÂêÖ ë1prUa]C|¼sÜa=-¿àVwX…càÈa=Ý/…µX?>OXƒºmË®1pŠUa ™·?OXúy±o Õý?O˜ç—'_#ÕÃcà”ËaÕ óúeÀÉ7æ§R–u ññÎÉ7æž§Z–m ññÎñ„,• ÷ÞËÀ‹càÔªÂ’î‹ žé%>Þ9ž°dߨ½K÷.ìLœçÜl”¤]ÿª—{ØûÔ¡ª8N/Wg^=ìਾ…'ý�ÿ&h¥ ®Gýâàöj¿åÐã ë0ËxíØñ{ GlÇÂ3Ò(2¸BÇŠw€“§EF€Ë ƒ</… ñ~ŠÖŠg¤ÙÂ`ùpãÕä-ù;\ç™T£€—ðÜ¡jðÎp8=ؽxUr­zlVšŸ>­cûS®²½Yø?~MPžíз}§µýYi&øœÛoõf»t»…s ÚÈ®ç¤ãÁ_àa…ߢ4¨vŒã×» v½T#Ä5¶c¼A.?cºçVWCƒô¥Îàuâ|/pTûâD§ðϳCœ nzé;g¶/®AÚüUÁ~ÆåPúv•jl%—Êc×ä2¯sGGxYˆòíh}þ»Åy{cÇnÖÏÁQœ÷õß¡ú^çøv»Iqq\ßÛ»~¼Ì±t'Ú˜œŠ±rü€älüÔÈšj=néyBWÏÃÀ©3ÑãæžãºzΧÑD›CzNéêy8-&zÜÒsVWÏËÀi7Ñãæž'uõ|8]&zÜÒsQWO/pzLô¸9¤çŠ®žÓo¢ÇÍ!=ÏêêΠ‰7‡ô|QOÏu×gÄX‡Cz^ÐÕs«]\o5ÑãæžuõD'ÄD›Cz^ÖÕ³8&zÜÒ󪮞,àD™èqsHÏ×uõ'ÎD›CzÞÒÕóp’Mô¸9¤ç[ºzÎ'ÍD›Cz¾£«ç%àä˜èqsHÏÛºz¾œ=néù/]=?N‰‰7‡ôüPWÏ{À)7Ñãæžwuõ §ÊD›Cz~¦ÖSº;}KéŽ}Û€SëM–Cz~®Ÿ 7§Á8>éyOWÏÝÀi6ÑãæžôôŒÿ*´-ñ&z–€ž¶±r(¬Ý8g�§ÓD›Czuõ<�œ«&zÜÒ3¤«çpúLô¸9¤gXWÖ¯=néÑÕÓœa=néÕÕó©?žc¬ÇÍA=®Ï«õL¼8ÁÆz<ÒcÕÕíü á&zÜÒcÓÕSœH=né ÖÕs 8±&zÜÒ¢«§ 8I&zÜÒª«çpRMô¸9¤'\OOˆp²Œõx8¤'BWÏÀÉ3Ñãæ»®žåÀ)6ÑãæžH]=ÅÀ)3Ñãæž(]=O§ÒD›Cz¢uõ¼œ=né‰ÕÕó>pêMô¸9¤'NOÏõ6à4ëñpHO¼®ž™Ài5Ñãæž$]=ë€Óa¢ÇÍ!=ɺz§ÛD›CzRtõ|8½&zÜÒ“ª«ç{À¹f¢ÇÍ!=iºz~œ!=néÉÔÓsÃ$àŒëñpHO–®ž»˜d3Ñãæž]='ÔD›Czruõ@Ÿ Ñ6Öãæž<]=/�Çd>ÁÃ!=ºz O'™øHé)ÒÕy:ÉdÄÃ!=Åzz&Ý œLc=é)ÑÕ³8¹&zÜÒSª«ó´ÈD›CzÊtõ<œR=né)×ÕÓ�œ =né©ÐÕãI&sDé©ÔÕókà˜Ìy8¤§JOO(´á“Læˆ<ÒS­«çà˜Ìy8¤§FWÏ}À1™#òpHO­®ž]À1™#òpHO®žóÀ1™#òpHO½®ž×€Óo¢ÇÍ!= ºz`œ;ÉdŽÈÃ!=ºz†c2Gäáž&==7N¶‹P“9"‡ô4ëê¾ ÔdŽÈÃ!=-ºz6�ÇdŽÈÃ!=­ºzöÇdŽÈÃ!=mºz.ÇdŽÈÃ!=íºzÀçM6Ñãæž]=�ÇdŽÈÃ!=zz&‡ÇdŽÈÃ!=]ºzc2Gäážn]=©À1™#òpHÏU]=;c2Gäáž]=OÇdŽÈÃ!=½ºzÞŽ×9"-‡ôôéêùpLô¸9¤§_OOØMÀ1™#òpHÏ5]=K€c2ÿãáÝùŸ°UÀ1™ÿñpHîüOØ&à˜Ìÿx8¤Gwþ' |‰P“ù‡ôèÎÿ„}8&ó?éÑÿ û/à˜Ìÿx8¤Gwþ'ìOvq£0Ñãæ ×&µžðÛ€c2ÿãáÝùŸpðn4™ÿñpHîüOøq»Å}ý¨ÇÏÙöNr^“¿×É»Žöo¸±_Â[)hé1³m¯G‚Ÿ­ó¼T&¿ÕŒ1ñù-»pî5~KÃf:käó[¸K#Øç·²!å"}~ Æ~>¿uÞjòù­ ðV³ÏoÇ£äúüÖ›@Èóù­Ÿ�¡Àç·~‹w‚øúÖW­xOÏo݈÷òøüÖL¼{Åç·–ãM—>¿#m¥Üç·ö¡Âç·Îãm}>¿õ<ª|~ë;@¨öù-,Q5>¿õ{ ÔúüÖg@¨óõ­—oÆ[ö|~k|~ë^שŸÞ¹$«ÏoaËáó[زÅúüÖx+Îç·^·â}~ëx+Éç·~o%ûüÖ_á­_ßúnJõù­©®Ó(>½µ�>¿•„(Ÿß–Íç¾òkG€æó[às~} FÑJ»Ïoý9>¿…½^¦Ïoa•åë[-aP¢|n£ZfÁ[>—¨–µðV‹Ïoí‚·|î‰ZNÂ[>÷-Ðùùܵ|wÐúüV?„Õæë[x)˜_«ÏoEaÔç· ,Ÿû”WVÃ[>ûó¯l‡·Æ0vм-€ŸÏþü+ÐøÙ|~ëeúùüV'¼åsûÊ/à­hŸßÂ>ÅçöðUÜÒàskój¼åskój&¼ås{øêxËçqÊ« øù<NyõUxËçqÊ«?€·|§¼ú ¼åó8å5ßûù<Ny-Þòyœòx•~>S^+†·|§¼v ÞòyœòÔJÅç±ùkoCX>{)¯ �¡Ç×·ZýÐåó[wB }§´®„·|§´B«øì÷¶‚ øÜFµ¶�ÁçQ@ë€àó( õw@ð¹¯|g|öÌ_ÇQ›Ï½ùë›à³?ÿ:Ô¥Ãç·Ú€pÕç·ðf>ŸûÊ×ÿ„n_ßúúMPz}ù~=}>¿^ŠÒïó[à¥(×|~ë0|~ë2}~ ú/eÈç·º€0ìó[x7ˆÏoý ½¾¾õÆD ø<vxc:|žxçR|žƒ}£c˜ƒUÏ~¿q ʼ×3Ç/ü‚×ïŽe»ñß~'¾UÌ"¦t[n: _¤»Ppæÿ&çjŽæ~øÀ·ª]È- ‰cHCdÈn†Ô1äy†üˆ!¿Ô"J0CneÈ †,b³BÙÂ= ©dÈE†|…!ß`ÈÒÏa-âXÛQ!w0$ž!™ ÙÎÇr‰!- ùO†ü‚!Ô"–�†Üƹ ¹Ÿ!ù ©eÈ—ÂòÂÂJå× ù»±N`ÈÝ YÈ,†ìdÈ!†œbÈe†°Rg}“!ßgHCþ E‡UÈ$†0Ûý’Ʋ‹!Grž!_dÈk aeÕÿ§ ù-CF´È¸@†ÜÌÖþŒKfH6C ò(CŽ3äCò:CÞfÈÏ2ÀV X‹Àz€h†°Ö8€Õ‹€B†ìcÈ1†°^&àE†¼Ån†ô3„µÆŽïR«0†LeÈ|†¬dÈ&†<ÌÖJØN3¤ƒ!?fkëlÕ"¬Ì²>7p6CR²™!{RÅgò2C¾ÉwÂÊsà?µÈøX†¤0ä1†|!ï0ä/Z$ˆõƒA¬l±^/ˆµA¬…tÜ¿¨B¾Å÷ò'-Ìjw0kÃÏ0„õ§×±þâºu yš!¯2ä ù€!ÿÒ"îaë›&ÜÇæÓN`Þà„“ aVLx…!¬'šÐËÂìš»ÂJÔÄ{ÂjåĆT0¤†!¬žÈêÅÄï1„Ù>ñw aýNH0CngȆ,gÈz†0,ä0CÎ1„ÙòCº¼åßk‘ëí a#²ëËÂjåõ¬=¼ž¥ó “Ã6j»µ-“Xo5‰›&±^x«Ý“ØèoóNCÙ6ôF†° tCØx0”µ�¡¬^„²qS(+¡¬¾‡²2Êò"ôZäÆ †°^øÆ™ Écë¿nü•™ÌÆ•“Yÿ>™ižÌüÕÉ_Ó"ä"©æ5…MgH2CXÆÆaG¼å°f†t2„’˜7ÎZ¤pÖ‡³Ö8œÕ”ð†0Ïü&VÂoZËÖÜļ‚›~¢EnfeìfæÿÜÌÚÛ;´È-V†0Oæ6Z¿…Í0Üò†°¾ ‚¥|kç#60ä)-r+ó%ng=ˆ½u‹Ï]ÌÒ»ØLÅ]lœY®Eîf3 SÙœÌ=ÌO˜¶š!Ì™~‰f­_4›'‰f393X«>ƒ•±Ìœñ‘‰ùCØÜEÌo´ÈLV f²’9“•±Ø a-v,›5šõC®0䪙½Ÿ!­ZdÎ4†°¼˜³•!lü÷¬™ÇÆóXk<Ÿy)ñ0ä=-’À|ÈÖÇ%®Ð"I¬Kbó- —2„yh‹ØxgÑ“Zd1›YÌ,]r!l½4’!lfr)›U[Êú‹…!70„õ_)Ì»XþCÚµÈ ÖŠ®`ugež¹õM÷±9üûXk|?ãÜÏüÃûÿ¦EÒ˜·œÆ¼ÓUÌIïfȨÉ`moFCØ\A¦…!K´Èjæc¯f³ŽÏ0¨6¿–•ðµ¥ aH6«qÙlµ"›YšÍf{Öùk‘õÌ^Ïb¸>O‹¼ÁæŸß`óÏo0ÿ 6+ò«)o 2„ÍÒ´MdÈ a}wkIÚŠÂÚ„6æGµ±ÞªÍ¯¶±±L«ƒm¬ô¾ÉÚ„7ÙÈåÍE as§o2¯òMæ ¿ÉòëMVZÞb¾ß[Ìë~‹Í¾õ™ùF:CX™ÿ›ÿµG0„õ•ýÌŠ÷ÙlüÖ"2èC6Bü•Ÿ™?ö!ëa¯±’y­ ]cȯëÂjÁo>Ô"¿e#©ß²‘Ôo™¿:Àl`mï�óNØ*ÌÀù+™¿{ˆ! ickù?b«'±ŸXü˜•–YId)?ø-†0Oæ“\†4i‘!æ ±ñû[õbÞÎóm†˜Þà\ý·?ìç@üê…Ôºº‡dÂ'*äßÐ5—énUç.Ç}§¢W°ßÎT¸¾¬š—Ú’‹¤º¿÷ÁBó?¢íÎïž8‘ÿ½w¬¢ºöÇ÷Lr’�"¢ò£"¢"bDEEE‹ŠˆòHrB@b0 Rª¨QQAQQAQQ©rmD´>¨åzmk½ÖRk­m¹^_µ¨¿™93{Ö^Ÿ=çìõöÛßœ?àäs>{­µß¯µ÷–÷�“W!Äz“¼|?ÅvF+þ©îºD‚‡ºÜk-ZÈPÁKôâÓ!:]÷ "¡ä«+ö‰òÅ+7¼ ±¼YZÜG^¼Ê;,ßGZ¸v"át‘þHÆK›†¡ÎKŸ†¡ÎMŸ†¡ÎNŸ†kWéB©éóCp*Ž_æ?ðéð„Ãy…Ö¯CœúåĪÀk-ôõëÓK‚Ó´~yr]š‹>uÛLÏGh~€¸;¡ÊmÆŸº/³øwM×–×ÕðžŠé¬pܵ ¿Ö•WU¥*ªrãñ§îM©§T‘;$°Ë.•>)äëu>ò‚oÑwŽºð|€¼ë!ð>D;åXR»|•V±9”¼Å@²–cÏù§ã•Irp_ÿqj—ò9`óG$T ’Û¼K+yFzÉd–üàB¹VJ–÷àúF˜¡äuäÆ~¡¼;iÄ‘Úב7ïÄmRû R»¶d>Dßõ8L†Z@µ?DÞ’'éµ?ÜpúJ9‹„¨©d–oJÉÖ…Rãþð¢HN¨ýmÜo‘qÿ§ë×#Ù:ɪöGúiµß-µkSþÚŠÊ7%íå2”[Z:q{Ö÷Ó…RSlýðHNRË9SjðŸN±L’ïWâµ`=yaSÈ2ËâÍI¦çØKíڼذ‰„’¯ÒØ)6C^l /ØŠóô6o %s¤Ê©P8©þËR^æM}:¼X ͧ}eVo‘(Ûí„õö"úJç·V;y_éÉ tiÞwðúÁŽÐª#ÏÝ‚½7ñ™;®TúÊÏÜ^ÎÏeÿeC·î*}ågîþΡ´Œ}掾 âîDøoº…ùµ‘–±žíö›â»‘óº”ãîð—PÄÆ](÷C$Ï!œNRòŽ ’—êBy’CíãW9A.?Þžäò«>~ípܱzøŠÇ0‘(¯uÂz{ úõY‰ë3ÁKÔãäÅñêúÌÝÓìÎnœ’à®,ùc-Ò’È6!üÄD"ùWšXüÉá,¢±ÈÝãĉÁ§§‰…Sü ÆñX<AÞ‚="báý—Gä8\–y·íVjÊgn{ªÖ wMïqgÙy âúY S¬ÖO1åÂϾ äÈçîí1þ«…3§9®×Y*5îóöB¶c>R(d9$/úœ²™õ¦¼uøùÉBÖ\÷mJgÔä"ñvõ):n餥íÌw5¡d jgæÿéÂYüž•ÐJÎJ¾Úûùù:{žnÒ…òì 9ïFrBí;u©‘Õ2}j<M{a’?ÒJn›A2 Ÿ!ù­äö$Óãô’Ÿ!/Ó‰™RrÇàÛçîJ)ŒÃ7Óٱߩ’õ×G>Í›« $Óþ)¹GúRJ¦¦¾Ôm^§ åÙc‘ÚÚ…pj¥öãÓ§óOéx^Îç˜äž„“/%Ë«—üì¸è‘^…Âñ[ã|:|épÒ–_ì‰Ê•N³éùÌê[þϧ9!Gó–ß“ô•ÛQmùC ŸFBý'†êx€ÃQÞÉ« ßr,¼8…‹Ã¹…Ï‘w²¬·",l’÷Ѭßj,<¡Àº¯ 8ß”uP U_Yj¹½Pd¸õ·ÃoN†}CŽ{ÓÚ—úqW9:œÂ r$Ç“3F+ç'ÃÍÀ!Ç“3^'§c;‡“áfàãÉ)ÕÊ9µPtø"¸ANø·Ìá r”ÓsmŸKßÝô§áX(wüy[‚oÓ‡èž;–§á$1Õ;^= g —¥Å à»8 ÷9øò}ûVŸÍ‘/F�R ÈÏ�]ò/`è ðLû;xýüIþ;YOƒ¿Ã¾ù—à3ð%ì*~ ±øG@À{d÷@À;b7xGìŸäÝài°ö‰v£öß�>K»a÷ö+Ø•þêp@À{ä+ðXø ¼Å¾Ÿ¥¯`÷í+Øoúª («_O××pªåkØuý|A¿†³_ƒ÷ì×p~çëF@`oýkØþÎq| yñ øÆÓ"@`ðØûf. àSú x†|;t߀×Ð7ï�¥îhIö今{àTËž3�½Ú=÷=à±rpìŸî=ñ=Ð&ìß¹=à¹NÐ| eõÛŽ€Àø·°ÿþ-äû·ÐB~ ¾|ß.d% à{ùí‹€€î·Ðª g²öæqßÛÓ�ÓÁ{¡ýÙ Þ{¯<Z÷®z½°G¿w' p.iôÔûÚÒA€€_ʾ@®�|A÷Wð>h÷Gë>ð†Ú÷[@øùSKð³x–àõÝüÔ†%x}·÷ °÷ͰÄ<@x[g >N°Ä €ðþÔ¼ ·÷´à¤¿eq_Ëâ#Ë‚XX¼Å¶,î jYü\‰eó2fÙÜȲO„R,›—ËæžÃ–ÍÏIY6o,8Goeñ>ÎÊâÞªV/óV?ýaeñZieíåH6”ŸlîQfeó³]V6?¿ceCyNpï#+Á}Þ¬?Ý`%xûl%xe%ø©y+ÁO’Z î¹g%  ˆEò"‡Ÿw°r¸/–•Ã=½­ÈÞY9S8nåðsˆVï…­Þ[9|Lbåð[)¬Þ [9ÜcÊÊå>¥V.?GfåòÑ—•ËG_V.o­\ˆ{.Ä=—÷DV.är.Y¹|båò¹••˽ӭ\î;gåA•×>׳òøÈÊÊãó&+ïyŽ´€2Ö‚ÏO-8smµ(ZãÜsÏj å§%Ô”–|tjµä>ŠV+¨Ý­ ឺV+~vÀju9 üœ¦Õ ZÚV¼_¶Zqït«Ä«?[aµâgñ¬|~6ÇÊççO­|>ϵò¡͇–-¿~ÃŒ•Ïç°VkhmZóOVkè[óY¿ÕZ‰ÖÜoÙj}n¨¹møÒjÃÇÏVîYmµÜiÃ}È­6|Ähµù�>´Úòñ¡Õ¶' ÜÛÙjËïa°ÚòóV[~NÊjËgvV[~Î×j» ¨;PÆàŒ³U�e¬à,@¸Ï¶Up üU�íOŸ£Y©õ3á'ý­øíÖÜ—Ø:€Ÿô·€¶?ÙjµãÞûV;>?µÚAËßÊóªBÝ9Ÿë·ägf­öÐǵ‡ÒÒžŸ0²ÚCoÕþ-@ ':¨¾2icуa¤×ÊXGÕ ìé1íÂçÔV~[‚u(Ì8…xòÖÖÜ×Ú:’{æ[GBî =õQüÎë(hmŽâ+0Vw~’ÂêÎo౺Ãè½;?ƒ` #´£ùIIëèí€@¿Óƒßþaõ€2 _y³ŽQÓ1Ðö ½Ã±ÐŸ ó¦ã&�³›ž0OéÉWA­žü|“ÕsGà´©Õ ZÑ Çï öô Œû¤/÷º·N„ÙVÿ&@ $ €~g�Œ N‚±ÍIs92¯�[ÁÂ"hŠæqäX=8VNá7WX§ð• ëÔ|@@ò©PVO…öð4(c§öÓ¡;f%ƒ«8rÖ0@ Dcã!üŽ)kôøgÃ(÷l·œ £ås E:ÊóP¾ön …i(´$CaÆz¿‹À:ï7YçCM9züÁ Ì`Œ4 F°Ã`œ9 Zþ ¡Ì_)!”ð ùš°uÔÜá°òßq•¿(=­A"1Åi§ôrò€àíWA>Öp9k£»Û©]Åræaâ~\ì}½ÉM°ÿ.}¬UÄ‹ [ª¸Ï÷7îëm°À) »„„:žê ÷ÍSßžK|:>ípß¹yNÜØ]ÒÄýáßMãîÉ tÓ�e•E#¼c%AÜK“•E#RÞ×Ù)Ä—ìžÐO(È*áû]Hd½óo®‚ü4LŸÊ¢À/ð S#‹xˆû4©±­@dç+~ãDbª3û=°GšÔxÉ}K§F6ñM@•5Ü=ëš•ŠI?5*fLw¯´¨©ñ¿aj„ñÊLt=FÕôšXZïVí”›¿kï¿!{xB‰û¯Ž»û`ðþ»ÊùØùåÜ}d·½ÓéÖšyÁNz°-¯ú]ä]p<É~IµUNÃiLÏÉÝâp–eÓ×á¬MÏét¾ÃÙ˜A×b‡³%=§¥«ë• r~çpÞÊ`³åpvf³ÁáìʯËÎî 6w+°îËÎ §Ò}×Éekñ—8¨KË9~Ùˆý%$ûKP$ö— Hì/A>s‰ý%T$ö— Hì/A‘Ø_‚"±¿Eb ŠÄþ‰ý%(ûKP$ö— Hì/A‘Ø_‚"±¿Ezòïé/!:mñ×¾Â5áu"1µIˆöÞšÓCS [–c�º&œZCó×i»Ñ�dØ_Nɹø€�Y J냭�Ñ*…„+À­"’û øtzÍálSÖùŠD•“¯í§GÇâb·5^±ØFtÅÂÝÛRÖù/võù+ÛäÔ|‹T¼êk¢czð¢}®&¦¿w8{”˜¾âÄôa'¦W¥‰©3O½�HcêÉ t͈i'ˆ—¿ÇQ[z¥ËÑßÐbڙǴÃ2¢ýIML¿p8Û辕Ø)ӜֿýuibêÔÁ,ÈÓ4O_§pïÆ—s½Po °.¾]¨÷¤v+üÛÂxul º4§M;ç9œµJÛâÄËIÿö·¥‰×}N[ÄãåÉ t5EÄKÅ3<^L»@žÂ.L'zwÄ6M¼v8%J¼V8ñrF*íW§‰—“‰!<^žœ@ž‡µ.vGÁ-|»K^GÔòKM wÆ¡ìµpÇ«Y¼¬vÚNìùH÷ÞG©•bhêy¯ö¢ã>¢w¨•h­ÜM”U¹õngʰ³æÅÝ`g­s¢ëkŒ—¨)mWD« wß²zKŽÿö¢ºûÖùœ€ãîÅ´÷÷ðÔݷ΄8xÕ>¶/ÓyˆÜ—©òµ«Æû2‰÷e(ïËP$Þ—¡H¼/C‘x_†"ñ¾ Eâ}ŠÄû2‰÷e(ïËP$Þ—¡H¼/C‘x_†"ñ¾ Eâ}ŠÄû2é È¿ç¾L|ŽUEâs¬‰Ï±*H|Ž•"ñ9VŠÄçX)Ÿc%H|Ž•"ÿúçX;?íï†{¬o‰Ät§>ìdÛoJlU®ÿÐ=ÖÔ®¢¿ïÙ‹h¦§ŠïÑAvT?!’ÏðéüjèÒAñßèéÄâ'cÓÄ•ôEº{«Ü ‹n.Gž*‡’w+ä…Å4¦t8J~YïŠDu''¦“ÓÄ´ÞExL¥ùµ4"¦é=Uú÷sg@ÿ”§JWš§ÖÄôÛqX¡â©òÓ-NLëÓÄÔ‰GÖ»<¦žœà&pÅÓé»÷T9ì-¢kÆ«ËÁ¢0[©qMN¼v;ñš—&^Î<7»‰ÇË“è:&"^J,Flç±�O•Tž‚WCá ¢K©Ý~¼Žq8Š·˜{ß{3?xqšxít*Ê<ˆñ,²†€*ëwý'½§J?o:øyªޛأ9ße¨Ã™¨Ä}˜÷™NÜû%íœd.äq?œ¼ùbM \O•þý½ Ìôž*©¸xª>è*Áx‰…¢íºÌž*Ù…é=Uº”×Så`ÿuÕS¥Ë<!:œµÏ;'.=U¬Õkå9qÿå ØSE"±§ EbOŠÄž*™ Hì©B‘ØS…"±§ EbOŠÄž*‰=U({ªP$öT¡Hì©B‘ØS…"±§ EbOŠÄž*é Hì©BhbO‰=U({ªP$öT!Hì©B‘ØSEAbO‚Äž* ò½yªtyËß ÷XD¢Æ‰oVzðêÈöXå®ÙcõwSûžö ®rvyш>îMññ¸ÄmëøèÖê/ˆœ >]þ\`­QïíØ¼ÃÝéLc³Ûç6p›×{`ì$ àÞ÷>Â+—Í¡…kÈ+íöD…NŸÛé¡}Þý�IÌèëÊ©dE†�gp†g8p’ÀIg p¶¦8áÙ“ƒâÖþn{¼S,‘x§˜"ñN1EâbŠð1[¼S¬"ñN1EâbŠÄ;ʼnwŠ)ïS$Þ)¦H¼SL‘x§˜"ñN1EâbŠÄ;ʼnwŠ)Òx§˜"ÐþÄ;Å ïS$Þ)¦H¼SLx§˜"ñN±‚Ä;ʼnwŠäûÚ)¶.]#ÇíÏÈ_räR¾k]Ú hÙ.åãqè(wRîÛKD¢æ#!:z=@‡ }Tu©l”¢'“SóSÿX—> ØÎõ¥nSn'¸Ô]èè" DŽv$â–ÕV ò5u™»#ÖYAܱðMí>¾Uþ›Ú>ÇE»*¡ä:Ûúvy…s‘ç²}Äí;¥ðDs=}¯yÓäÐ ‡³›Þ*`-‰Z§×ïä wõyqÙ47Ïx^xr‚½õÁ4@]ðj»<îÞà'Gð&»|É=·Æ•ã¿ÉÞB*ý… ï¶ç.$œBª‹ÈY¤•óŸŠœÆÌròzNO)çWTNÞ@Ç“#ó"¼kocY‡6:œF%/‰DÓŽwòú¨ˆ¼Xîçn</òÈ;òö4€I^ø’·Šðî‘05¶kSõ#‘™ó¡’bè8©3àH]-òµºþG¦…”Ü¢„:0Br7­äÏ2Hî)Y–„ï’ÜY,àsèc¢e{ÚBf ‰ú§$xyQœv,±ž—„–äû6 ê®Ë\Ž\¥$|Æ4…ŒrgR9Ðþ(-ä(·wÈãå§• é#_‘w¨¡dª¶Ú¢ 奪”œß…p‘’[pZÑR—OrÐ~HÑe u#º:J]pTt5]DØÍ uÍ!º ¤®C 8]¤]µ×DØÍ um"ºÚI]GpŽTt½Bt­°'š#uµ¦e#Gê:Þ€sÕÕš´ö2½=i8I-GÞúb÷7àœ¨èêI83"ìIéP8©–$ëŸC_r8£•»˜œi†3ŸéäÍçô­Ö(§·Ìû„·Zžœ@WÔ]Lʸn”»»¤Ü]ãÝä£ÜúâÝYäßúRî :ÑólôÇcaLÛ $Ú5¯†úK‡£ÄT쉙=œ˜Þ˜&¦ÃFbÚ†ÆôSPeš*2¾Ääõç‘÷Ûøq'wû„1mÛ#ÔžÝWÓ?9œ‘ÊXý‘¸Ò©‰V§‰©“ß-wñ˜zr]%š˜®~¢ä ÓšIµîu?'ó˜ŽÚÀc:Ê/·Tz¢×Ex¯ÚÌéÎxÇ[>ôÇØnªÖ’~P†r×±ó¡ƒí€y¤MXDÇ.§õë€%:Žû óâ€]aú$.ļ# EÛ…Q·qŽ{³P'_“ 7Ù«¤ÿV5õ†¢®’ãɹߗãsJ+z¥®}ê´qßÞ|zC‘h71ð;µzø*víp<Éû’UÎÑâÀ98_~êhW^ò:°Qj_¢Õ~NÀñ$?®“,Jœ4\’!%Ç“ó”/Gátå ±¾ø;H¢a·[~ª2ë™F‘Àœ)Ày�8Û8göå)N˜b‡¿¤Xöd]ŠýS~ÂÖ(Xÿ»T£a m4¬ÿŒ�ÈÙ€ÀJûhØ+’ÇÀÊäXÕ;}cJ�áSÖð~Dc¸Ç”5ÖÐÆÀÞߨ…+—ƒÇÔå…€Àžæåà‰q9ìÚ_>Ø«½œ{?Z—oö.‡ù±Ü×Ñ ë«c¡Ô=R@`Í|ì€@^Œ_‘±°;¼}ÆÁJé8XE^1ã`—|¬îŽƒµåq°ç2Žû(Zã ÔÛ Èk€À~î¸ÙÇ‘ñí�¸äàxð…kÂãau<÷VµÆ¯d# /�~2ã¹·ª5ž{«Z N€•Ò °r;|„&poyk¤Æ„J@ lL€}‡ °‡8V€'<¬ OØ ìegþ«¹à} þó¹|…\äB /†ÕïbÞƒˆÜíÀþ\~®Ä*†5óbXW/oÃbØy,†ðbî5mC9,æ;k"ç©ÈkCý $ÿ8SÓ|ì[ð3V ì”@‹]¾O%ò- }JÆ<«[ÀY¡pn¢÷9-á¤@KðŠ)ám¯hÉwÀEKðmn ;5%|ÿ]´‚^¸„Ÿþ°J ¥-m´$¥ª­ÞüÌó[2 ^žE>oDkî#ZƒON)ì µæ^1¢5ŒJá Të½iÃSC´ÇRÞú‰6àEP ½C)øÊ–‚{8ÑÎ[µsImþ’ÁC;i^^I~ ÀJ©Ÿ¶¼6YIå&'@(8kÖ–Ÿ5³’àK“„üj qoËÏZI®ËJòsRVê`o[DŸ)ˆ‚Q€ð6\Ì]0"JÂŽs’×&« üWË ¾—Aÿ^v3 ° \^e†K�á§ê¬rè+ËÁÿ|iʽp9œ’(‡±D9Œ¬ÊaD]#ê èe*ø9 «jAŒ3+ ¬V\x V@^Tlüi+x™·&ÁøpäÅ$HçIP'ÁÈ|2HžÜÑ€€?öä&@À/n2”ºÉ0 ¨OÝÊž€œÌè+a¬U þ•0r¨oçÊí€ìäÈèa§ÀêÁð<Ÿ#ó)p"c X8êòT8å×ûŠŽÂ猢#Œ<§ÂLs*øN…âT>>ôWó¤‰#yë':ÃÉßÎKá}®è¼•ÛS§�ªÀW¶ æDÓ`ž[Ý#5Ðò_AW€'O-Œrë §®ƒ5‡™P¢f‚ÿáL8Ò�3èè+ ý™þ½³àôÐlHÃÙ°Î6|\gCû3fWGëU»82ü¬æðµu5¬] ³þ«W-À\È÷¹°ú4RìÈÓùÐÒ·ã>¿°À¼{!´ ¡?½z´ë`L{¯MÖuЪ/‚sv‹ Ï½V3n€˜Þ­è0"jäów«Î.Ýuç&ðÆ\ «a‹Á+xq Ð®Þ '–€7øàÜ3Ö[`d)øU.…³f·óÛ¬Û‘cÈ;`6zÌ îþ;al³ÆHËÁOo9”ºå0ÿº‹Ï­»øyjë.Xy»›Ï€¬»ÁÓ{Å€@ÿ~H¾§‰#+Áûú^X{¿rð^(Ï« d®â÷ŠX«aÞ´,¼ÖÕރGëlë ¬®ƒ5½up à!ðrN"? ã–‡W¡S~@ïùŒÞ×óSáÖ(QšùÆ*@`Oáq8;ù8ô;OÀi' myfšO‚…OB™²‰!v.O;8pžÑ†“‰v+¾ûf·â%ÓnÅSÞ®?€# <î6Œ%ì¾Rj7ðÓLvïí^ÆìÞ/Û ¼^Ø ¼§¶ø)?»·?vßÿ²ø !ö¬|@Žä @øê®=«‘!¢ë|·TúBdO‰g„ÚÅ»å¢ÓV?¨ {–ÜKR^5z›øBô£fèbÊS×óaX(f†H^ )+ªíŸrúüô®tŠú'Q@|äµõ¼´ð5m¨Ña¨¬2ÔÊ]û¢þ~ö,wm9Û—#=ŽØCäÜKcjÄ‘ö)H,ÚJ{~nÀqúŸ+Ýê߇¦Ô~d^*ûL½…i8¡öÞ:ÏOë-ΛZ ÃPÄ{$ëijµ°ñËzU‹ýäH{º !±È ~·[prÓÇ´Ûp¢½IÆô÷ApÓR×øUfm‹ˆEá¼ uý©¹ºŽÊϬKϱ¾U8$å³¥¯£ôdNÙÓì9 ì!e,û {iu}Þl]oèÒr¬ÐRwÔ»„sp„œ„ÓAÊùF‘óŽãÉ‘œî¤•Èî¤×¥p¤×ׇÈÉÔ•ÔqÂÚäúQ9…6ï'§BáøýÎ9 Gú†% ¤ï“ïQÊ|ÃŽ›©¾¿^çú>u}oó}š½Â-?U¹ÊëѨÏÒU…ÀœzΙÓ+Å ýšº–~MÃS¼Ø¯I"±_E`æû5)Hì×DدIAb¿&ŠÄ~M‰ýš¨äد‰"±_ û5Q$ök"rb¿&ŠÄ~M û5Q$ök¢ÈNŽÄ~M û5Q$ök¢Hì×D‘د‰ ±_Eb¿&‰ýšû5Q$ök¢Hìפ ÿ·~M«üÝÒЯi¸HÌ.q~ñv´º¼áäcÏ’#"ê×”Úuõ÷—‡Ñ�ÍôkòîxQüšàÞ•îô®ªÐ¯Éé®èíë§µCë×ô¬Œ—»öEï:Ky>€×ÐÑ#3û5Mn2Ìú“‘ö½JwÕõªççéczôn­×ÐKÔ :ÿ5ûÉ‘öôXªõjgÀ)HÓ$}ˆ×Ð[Aðˆ<…[æz¬ÏìÉÓƒÜDG¼†Þi®®ceÖ¥çXû„˜–&5Ž!%“øöü1ƒ…,$€Q¾=ÇŒÓêúK³u•蚨Õõ¿ÍÖUe «žpB?½¿7W×±ù™ué9ÖW´~K|8£ü£Žm¯õúV‘Ó!³ѱÄ/»{„.Êéü.}€Œ[ÑcGëä0]£u-­çmÊ)ÉÜþè9vmm %פØ~r*Žß{ŽW8¡wV‰ôΚúyg=p\אַ~»„ÞY‡Ûû˜wÖœÁnÜ«�¡žWs†n¦š38K³ 8ç ιúLιúBà,LqBO°c—HO°Â/ö“Hì F˜ëÅž` {‚$öSØŒ"±'EbO0*9ö£Hì FCÅž`‰=ÁˆœØŒ"±'˜‚Äž`‰=Á(²“#±'˜‚Äž`‰=Á({‚Q$ö#Hì F‘ØLAbO0‚Äž`‰=Á({‚)Èÿ­'Øóþniè V(sú Q¸×ûýo~PA>öl9r ž`©]W/»š ;òôU,+øÝÚHön¦òßí";éÄG(Rò*­ä 8?Sn(Bíë"µ›pBíëµÚwpÞÐZ†"wé·í2U嚞⛑à1=n^f·ãÈ;¼Ù=›Í‘6·ŒÄ4KÚüÎïÓ§Æqz/¸ßR {.Ìì=Òs‘.Öï9™Sc?9¡=«t©áÖÛŒ‘>Åô¡¬½ŠäÍÄBùN¨õq Â+c¹PÆòyë¹-³TOâ±Iü¾¾h®®ã ü¾ôk7Íã©'O„/ÖñÔ,ôÅÚ£ÈiÐq<9!‡´ì®ª³4s‰Jà m^§--‡pM_¢Ž'¯¸†þ™vëà‹—ƒy™[¤ã©ïe„Ïäñäí鬭RWàÃg¬«WMf]zŽ}Mù^s2·-zŽúên¯%™KËþr*Žßw+¯‡?´Ò­$õóC{5à¸~h©ñƒÎíÈÁû˜ÚÕw¸yQˆâ÷µ8k€ó(p6§ 8{8gnçÌ휾ÀÙ œ78çÇ)Nè«Ökcà«–å·o±¯šDb_5ŠÀl4öUSØW ±¯š‚ľj‰}Õ(ûªQɱ¯Eb_5*öU£Hì«Fäľj‰}Õ$öU£Hì«F‘‰}Õ$öU£Hì«F‘ØW"±¯Ab_5ŠÄ¾j ûª$öU£Hì«F‘ØWMAþo}ÕÞówK¥¯ZÖ"qµ3Ê?ÒYîoœ2_5¹B}ÕR»®þ~w @öè7j}±žUn¦Êq%+^Äó!Ròf­ä 8/dо%R» 'Ôޤվ݀óZ _1°0šjß¡Õþßœßj-”¡N /?oºßø_¢^ÊËå1=øtEyÓ@¼t²Þl6'´™zéäJ›?0༟>¿N oùUFXÍ µïÒi·[pòÒçWor ñ÷ûjaoš†>9zŽ£Ü…Ú‰Ç&ñUû2î•–(-‚§soê_á«Ö›Ö‹ð޲=ÍÕÕgdf]zŽÛ¶9ô.¯¿¸>ô¾¸ð]ÅlENIf/¦>Ô§´û÷ m^¦-‡'pNJ_Vû¬ÕúÅék|—`ƒ{ ûPß¹Ð/.¸sÍXWßÁ™ué9v÷ôu§¯þN¹nŠ…å¹ïDÏBNRË Ë|OŃ–¿¯ß)å;îz+6×gnµô»¿6åCÉ 3׸ýåT(œr]ï } s 2øþ%฾…©ñžÎ·°Û'NL³&ÿº¾kÿ:Ὼû×I$ö¯£Ì cÿ:‰ýëû×)Hì_G‘Ø¿Ž"±•û×Q$ö¯£¡bÿ:ŠÄþuDNì_G‘Ø¿NAbÿ:ŠÄþuÙɑؿNAøø0ö¯SØ¿NAbÿ:‚Äþu û×Q$ö¯#Hì_§ ±Abÿ:‰ýë(òCú×íõwK¥è'?>Xˆn»Ü?Ž<Ã*ÈÇž}d ‡ú×¥v]S{¾öý‚ìÙîŒÕ»5h¡(íã> êmveûHo‰$RÈD ¤ö²ÒýåMD׃T Ù_®‘ûËS¿©ûˇµ8îþr*¾ºýåîîóî®!ûËoûËÙ»S¼xY"ñþ2E ‰÷—$Þ_&H¼¿¬ ñþ2EâýeŠÄûËTr¼¿L‘x™†Š÷—)ï/9ñþ2Eâýe‰÷—)ï/Sd'Gâýe‰÷—)ï/S$Þ_¦H¼¿Lx™"ñþ²‚Äûˉ÷—)ï/S$Þ_VÿÓýåúû»¥á[c»EâÇNßÜÝ©uûÔ*ÈÇž-ÓPÙ_~;ÜóÍ/øÇž=Vøç²ˆ›Îþ;5¾…úW±‚W„ä ë~ô&–ËÖcôtr[‚|Äû„rú—ÎD)ç©ÀfÏžD¤=¡®ÉDÎä]äQ!u=×l]õºÈ[6¢\êz¡¹ºtɬKϱ~®èŠÎÓP×`]”s–ÔåÌç¦õ‘'ëýÛ�ÈYûl^~[2Dx?É*6ãûD]Áfr;øi„Í4¿ž‘ºÔ»k tTY—žãÝÀÊ!w³ˆg#ät œ-RÎï9]tONÈ¡uðµ]4}ä]:ÖŸ ó4ÔµH'‡é¢oÞ-uýM±y…ŽÃä¬Ðµ-֧Ͷy}tV(ÿ®†^ 'ô¥ ߪIýÆ|iú×—&Õ¶s_šÃ† ѯqyê°Ù"qM׿*‚Ì÷b1"-Ó8½€³Há sm G" gpvp΂39'…(œ s!·yA·yÁÎYØžsöÎiÀ™ œµÀÙÈm^ø4·9…(œ7€ó×õ“l®ë'G�òë'£38«€³ 8ïsε9œsm7àôä×µ—g!pÞÎιÊüuspê³ 8Oç-༜9gÔÁE]€SœÀœ À™œ»€³8sÎõùœsý…À©NpÖ�g;pþÊ97t✠Oo€<M! ' œkù~äû o�G­§NͽA-Qä¼r üܰ 8Ÿ®Ý k/çÜ8sRˆÂ™œ) çA™£p$¢pú‹—‚®çyÜo|ÇýÆ@ÎÇ\N#”ÕÆv\Nco.§q�ÈYÂãÕxWãÀY(ó›Àžgm]#´u7A[wS'àtNwnóM¹Í7 Îà áixS’§áM³Ážy`Ï­ gÈYrV‚œ• g-ÈÙrž9oœw@Îg\ÎbG¥*gq—³ÚùŃAÎ03ä@_°x4È™r ¾/žœ™Àº¼økà|Í97çpN ¡6ßÜÛ|ó�ãÛa|x3Œo¾t= ºÞær–Lær–LÎlàÌN#pe~É2à@yN!4^K ¿XýÅè/R•s Œ3o9‚˹¥—sK pg1pîÎ]Ày�8ïÊá-Po=–sRåÜ6œsRMŸÛ¦óô¹ ÚºÛ®ε  òô6˜;Üö p^]0–¸m7—³ô`.'…PÎíåœÛÿ œ/ó%çÜÑ“sîœ!ÀœqÀ¨§wLçésÇlž>w¬9[@ÎVcþ;`¬ugK.çNHç;�Ô¯;{g0p /¸sp`ìw'ôwÖ�êòs€3‡§Ï‹yúܹäÀZÁ¿ÎûÀ±ß_pβnœ³¬7p`ì·lpF§8%À©Np¶sÎr›s–·N!p ~-‡ñØrhÇ–CÝYug9ô_˯Ôå[9ç.uüó[Q˳‹Ìärî‚2v×3ŸË¹»—sw.çî®\ÎÝÝ@”Ÿ»¡¿¸ûXÓä@»æw�9ƒ@”ûŸ9�çà|Â9+úrΊӀs>pÎÎeÀIçVàÀ˜¬­Ø œ³ 8{9çhWïh3ï6ó¥&D)‡cã{`üsÏ×\ÎJÁå¬ÌárVæs9+óAN;Óät9w€œ g ÈYrÖœGAÎ&åyåvó&ȱúÊ÷@Î ç¯ êÅJÛ¬üäìárî…6üÞ<.'…P9÷¶9íAN'3äœr`u/ÔÓ{/9ã@Ôå{¡¿¸w2—³j—³J]‡tZ¶Uꑃ¬†6|5´á«;p9«»‚œ® §ÈérŽ90/X c‰Õ½AÎ�sØÕ0ÞH!Šœ3ANOçÕµ<W7€œ¹ ê×j¨_«ß9ïq9kúr9kr9kNãrÖ 9ׂœFs+ÈYrÖ‚œ‡AÎFó4—s_—'<“~ÒŽàLú Þ)^|&]"ñ™tŠÌ$>“N‘øL:Aâ3é ŸI§H|&"ñ™t*9>“N‘øL: ŸI§H|&ȉϤS$>“® ñ™tŠÄgÒ)²“#ñ™t‰Ï¤S$>“N‘øL:Eâ3é‰Ï¤S$>“® ñ™t‚ÄgÒ)ŸI§Èwu&}6ŸýÙóyËf/ä}œ} س‚ç ½Š§½Š÷;öjÞFÙ«ùú½š[ì5j œm_Ã˘}¤óZ>«µ×òrû~>N°ä½¹ý0o7ì Îø ÑÞð ¼¶7ð™”ý(ŸçÚÇ‘ÇøˆÈ~Œ·$öc|ÅÃ~ ´?¶‹#!^ùÚ…½‘×8{c _ù~¥ý8ßųŸà;ªö|5Õ~ò@øüÂ~r, |çÈ~ò*@x»j? )¶)¨Ë›úÂÛ {oÙìM|ÍÁ~ŠïÙOñ¾Ò~š÷Dö3¼U·7Û—sä§`ÏOù^¶(¼À÷(œ\¾ÐàžÆâX驪Sý ‚|ìŸÊZ@ofHù¤N‹»h€š¢z{e.¸ùß—ãöŒÙ âp‘"9«¾¼¨öx™ò Èiå݈®G| Ç:œ¡4^Ö'^I'^iâå®Ü6ðxyr]Ïþ±ŸuGƒ‡ÑX<ÛUø'ëÒ¸'αB¾hà#EaÜë§Õõ?É›pxž*×Sá'Ó“ì3²®^¨Úþ'õ åd õc‘™sµL7wZE玔³ŒÈù%M¬ïŒÚ¼‰pÆH›¢ÈÙ¬ã09[´r®Wä¼)G–Ì“?K‹ÕVÀ§pV(ê@KfË"1ßi[{zl}É|v¨óÏP^2=9Á](“ÿØÏº­ºRãž­sþí¢ n›p¨‚Ü)dé Ó§ˆÞõQ Óg9MŸ¢y!'k¦’>¡z‚tNÊZmÀ¹WÑÕHt]©è2ᄺVhu=(Dåôºé~ ÷kS]X-ùµRê:%›Èù\¯ë”|m>fÀÙ@ãuJAd:›pB]´qƀ󔢫Kæ¼Hà uÑ»k¾ ~Oô5Ì‹P-«?‘6¿$2s^ô¿d쉤Z~–DÄ=š#íô.á-íù¥ç͵yÐ’7émNÑöœš§MÃßpä½4¦6ŸÚ#s:§á„öлb>‘ö|ìŒRÊŠú÷ïãý }î©ô&Ÿãe¨]"3篜 8*1¥-ím©ñ¯ÁIê8¢ZÆëKΊ®…:³'šꢵr€Ôµ×€³GѵŠÄ}E„=ÑœP—¶vgçprü/æµÉ HÑöœ¦­ÝÙí 8Aßflói-@NhÏhmv1àtj¶Í ™Ó9 '´g•ÖžîœnͶ¹ÉÀæhNhÏGZ{zp‚û§Œm>=?³Íi8ÒžÓÛkí9/°çY×»¸%Ÿ[E„:Õ@ò)ÍŽi?ƒ˜FsB{kk幜³›msIæškÄ!5E$#âE{ÿ•6_Øü¬ë™Ö,\§ Å$o$9¢Î¾DJnÒJÞNB͉¼S[6ÆpÆÚMób°È\~Òp¤=ƒ ´ec’'¸)ÒÜæÞ™ËFNhÏm^aÀ Ææ6O4HçhNhÏ­=Wpf5Ûæ6GsB{èzËÒžŸp4Ûæ·‰=ßFØÍ íÑ÷D7p‚T0¶ùŒ¼Ì霆#í9ƒ–ùë¥=Ë 8AŠ›Û<<sLà í)Ѧá}œÕͶyžA:GsB{š´öl0à<Òl›wØÍ‘öœÙ[kÏ3y/±©ÍgÏlsNhÏ8mñ‚çgͶyNæòœ†Ú³^›†¯p^m¶ÍÛ Ò9š#í9+_kϯ 8ÿÕ\›Ïê™Ùæ4œÐž‘Úvì÷œß5ÛæúÌe# '´g¡6 ÿbÀùs³m^kÎÑœÐ:ÊýSð{"'ýúXD¨lɶÇÀÂleÝæ,ºS3Eröp¾VRŒÔ&ë̈Tý!9oG:›8Aá„éóáô ~O—¡$èCc ùhÎQœ#κ•ÀDGÎ!JŠ‘Õ0«wDªFs’é9‰Ã 8‡Q]CÈ~“5ToÏ¿'™ž“R*bŽ?„®À\¡= §Báø7¥ð)\ìpÜ•¹Ã›½J$nl)Äñï¹Eìð¾!D~ ßáõäºÊÿØÏº¾|þîmE]ш}½Îá0¾{;„®c·”)æXR1±ÜõpÏÔtJ¥˜/ùÂß)^YÎP‘™sŽ’Îd­2{ºšÎœPéÑDk©k¸çBEYÿÉŽ*ÑœPÈÝÿÄhÎeŠ.Ò7e×EØÍ uÑÕ§ÎRW‰g‚¢kÑõã{¢9¡®·´º* 8“Ýó ú©ýì¼Ìî'GZx6]ײ¤…p>ÐÕ¸²JÐ×Ý_‚!g±®FmŠe 5Ï@ò\ÿ‹ñèôl2ÓTÛ1#NhÙ—±älâ΢fÛÜ “Ãl6á=;ëÉfs’é9‰; 8K]; ì‰æ„ºv‘²q”ÔµÒ€³BÑõ Éw9kcöDsB]dÔ$Ž‘º0ଥºÎ!{ˆÙ3©4©ëº’|œÔõ”•—^øþN¦POH~Üÿb\ÂÏ!ëÏÙ7GÄÔ„Cß¥Š˜MœCçËáXýM!ªê&–úó ðä‰õŠä— 8Û 8[•˜ÄìÛ#Rijj¿8¡Ítl#ý; 8o(ºèØFñÝm&g½N³™zlI{Þ1༭è";Ù+#ì‰æ„º¶]§J]ïpv*ºèxlu„=&2§7›#m>—ú] ~ϵ2srö5w”!§‹®NéG)ç’Qõ ÕÆÿb?çzïC»q.Ù·d:ç(ëQÛ€#2s{i~;ZÇq?ÍäLÜ/Nh3i±­52^ùœ–Š®*‡Ùc©‰ä„öÕxk¾´§ƒç`ÿ‹qÏxî f³ ‡´?Qc­4œ0^t­ò0¯Iœò õKê´ Å"ê—>Ô {Š 8 8]Ï”k¹9ý 8}•¼ ;¼Qk§ÿf™>CéªZ‰LŸs 8C¨®¡y:ŽjON¨‹xž[I©«ÌÿQ2#B•H.6àŒ7àŒ5àŒ1àŒRR¬PÇa©jÂéñÿ;NRÇ!-ÀœôsˆP³ $7pfpꕘÒ]û†ˆÔø×à$Ósrpæ+ºØÍIj9H]pÖ¥ïO#B=à‰jµô¡Öسƀ³Ê€³Ò€³Â€s—’CI­ŒØ5‹9ZN2='ç ÎFE×h{¢9I‡äû/3´¢úP¿0üºçUÎÏ=k¶´/p^TRŒ®�GŒ¾þÍ8aúÌ#éx²‹œ/2”}¨Ï $bÀùØ€³Ë€ó¾g§’bäÔjö½©ÍIê8áîvÎ_ 8)º–è8Ìž/NRË ÛÃo 8ߤßgJý–°ðàT(ßC`­€Oá‡ãÎ|¥7B¾»}¸Tˆ¼5e½7Âs]…8 €{#xr‚³íÿªìçܵÁô,ùsîW]•ÓåÄcÁ縷ë(7!<Wêü;$•ò>âžœhIýžsïcœâ¤; |Þ@’Ëòä]nñÏJ–¹sAod÷j¿€ì)X²¾çmþ§µgœ{Tfí¹GÒ2v=#œ&IÅ„ê"µIK]ÇpŽSt‘][îþ3{¢9¡.º!½,rO2àô÷¿ßwqõÜ{=ÂæhNhõ|xZÚsF`Ïs®×>¬½G„:]ˆšúÚ¢Ú>Þ6LK°™ì ص6GsBíïjµËSQ6ëC]¬µ9 EÏ_Ü'C]$2s.PâµKÇaq7à\HÖ[Äúfs¤ÍÕ–Ãñõü—AY½pr沚†“ÔqBÿçÜpuî¾Ðqïà‚rx!]ùŸ¡×u½[Cz”åN5àTÒx]DW_ÖÇ= 'ÔE½b†K]uœ+]ÄÛÐΰ'šê"wkˆaR×UœYŠ.RVí¼{¢9¡®nD×¹R×Î5Š.²fgEØÍ u‘óBŽso4à(÷ÿ\Dz|;;žhN¨‹ŽÞ»H]ëü/ösÏ ÍÊÒE¤ƒd(Å¿%‚³Ö€³Æ€³JáГäì8w¥Gñÿ¹ˆì­GÍFÿÍ8aúPß˰U€³ÑÿbÜò_Dz´¨–? '©ã–[`×ãCßj«Òã·‚PÔ_+ìÍ›DfÎó4^Nœ±77âÙDTŸ†“LÏÉ}M¦¡aï9œ®ÁÊ^éF8˜È}KêÒŽÐ"B½™~T9œœò³ƒÇ yúDsBí#µÚßË`³>Ô»lg`s4'Ô^B´ß µw EÙ¬õqú‘ððÉÚ2¯¬kEpþªÄ«Ê Ì›pVÔ‹hN2='7¸ ̼^¬3¨ÔǬ¯Ôµ×€£Üÿ3œúo|÷hN¨‹ø'XÒÇ5/Ç€“­è"§¢²‚»:¸=Ñ©ëbÒZ²dæcP㼸xNÓE9gH] Qß§¨¶?ï‚|¿G«ï+‘9Q"¹ }·N2ÓN8⩽ƒ£=Týéˆ~ºPªd…ók)ù™†Ú¾2"T¡¶¯ íDÚ¨[{L8IG¬‘Ú{*6C;êØômïˆ!ºPÌæhN¨ÎX’Úå½=úuD¨Ó $ŸjÀ9Å€s²Sâ½fÿgDj˜pí'´™Î¤×kÞ¹œ³]t¥ý«{öÚCzKÞa›œ‹* úPU’§p&p* 8ã 8—+)FÚUûW©ÍIê8ážKÞDNRѵQÇaöü{q’ZŽÜoÊ›mÀi0àÌTìÙ¤ã0›÷ÚCçø_K{®U8ô¤ö>ÉYhÀ™Oí¹„ÜJa?§·ÙˆCV笈UµKèÍ·òFß¼%œÅþã¹ù%d>h?as4'´‡ô•V¡´GñÛ¹d˜6ï2à,SìnÎ&2k³"Vðôœ¼`4`<IÛ¨z½®‘úvìiÎzÎÃ4î#ÉTVÄŠbN¨‹¶ Ám "o“ç E©ïöÖ{L8t¾Ó¢ùœ …ãßp®ì<¦>…›޲».w8ßû¸5/jw{»‡ÂîöH²»­Œ.R¡¼ëŽpÖþ eçÚ½·e²sí¶ˆ0Þ¸”Žð¥qyʹ­Îœí4/%íF¶lŸSéÜ,ÎðHNhõ¼ öØEÞNÎ{œw 8ï(6“u›„¼MÅË„Sµ_œÐfêQÖUÚü‘G¹ÿùRâ•N·p{¢9¡®EZ]7à|nÀùT™ýµà­ñ¥K b±œ¤Ž#dï™÷­çE}“âÃ{¢9ÉôœÎX¤ª|zp>8›Ï²/¥g9åíg-ZŠÌœ`¯Îø Ô¥ôÌcÄ-XFº×œÂ m¦7ðÈO‹NºÔ˜YZå žoa¤^N]+6“¡ìÝñÚ?NRÇ ÇZ-ŽÎPô¡Ž2¬øÿ\ºGÇa±Ø?Ž´ç2ºëq’´çôææòeC´%¼¿ÈÌ9Ñÿb\ .›˜¹„§á„öÐÓåò­–§pNQtѶNúP1{öÚ³”Ø#Os·¸ Ùù¥—ó#]ç)6“—‰àŽp¯ýã„ö¬ÒÚs©çE×f{¢9R×(º;)oûi1Þ€3–êEüdSõö¤á„ºèiîñRW¹§LÑEü[—FØÍ uuÐêšfÀ™ªèêb`O4'©ã„]M‹«š[w"äÌ2Ðu¥bs7‡Åkÿ8¡==´õâúfÇ]/ç:]Êû_£zg®ƒûÉ í¨µg]úÞ|”¾·Rîö‰à,õ¿÷h£ z´4œÐÚ[”ö<`ÀYkÀYcÀY¥ØLç;ÒÃÅË„³ð{ç$uñ©Œ×ÓÍ®)z9› t)ë?£ Z€ýã„öÐÙÖYÒžç 8[]d-!Ïæ0{¢9¡®MZ]op¶+º.÷…[¼fÀyE±¹IÇañ2áPèˆkN2=§Å{œw 8ïp”ûFí2ˆ— g·®^s’é9->4à|@u™ëWN2=§Å—œ/ 8Ÿp>QlÎ6ˆ×÷Ï m¦£Óà¾Ñb¯GñÿMö›êW&œPWNWËŽâÿ3ºƒ=Ñœ¤Ž–Õ–í›Û7EÈig «@±Y;ÊeñÚ?N2=§eN'EW¡=Ñœ¤Ž#>“ºŽmv^èåô0ÐÕ]±Ù`¦°ŸœÐí¿åÀfÇ]/g€®~ŠÍ3…ýä„öhg-pNSt 3°'š“ÔqDðFhy±ç¢ôžÞ£é»ÒË´å"3çGJ,JtS½Å:£5 '™žÓrŒG¹ÿg4õhxç( '™žÓ²Ô€S¬è¢¯0D¬ §á$ÓsZN1àLNï9z³…ÑœP;«.µ/4àÌ7àÌó¿¤ü*áÝöˆPs•¸w‚PÛµ5eŽÈÌ™­¤ÏƒšbÀCOç=øÆ‘qCÇQGʸßaÀYjÀ <k#ò4"Ômž†ñ"ýr"âdôÀIê8¤ÕZiÀQÎé¡ã0{¢9¡.ê !ïçoùœçqÎcÚ½ìL¡6H~D‰)éÍÝ{ñµ©a¹_œÐæ*­Í?5à<£èšc`ÏþqB{h«.÷AZ¾bÀyÙ€³-½WÃr_eÔË~r’:©_ïpÞ6àg¢Ú1}¨·2´c; êû÷ÏIj9kd,Þ7àìTt‘{²ÿaO4'ÔEïv–~Ý­lŽ*‡øÓfÿ´ùKÎœÏ 8ÊúϘt–>ß?'´™¬ fË3Y-÷p”õŸË³uÕž‚S¡pÊ5íFêSøªÃqçžÒgÒʉå Ñ×;]¬÷™Ü’+Ä%ÜgÒ“èRüiS¡<ŸÉÃÀgÒ¿(LçËéËÎò¼^«cEfNNwŽ|']ßÖE„:Bië„¢~qrlܪPdætUò½QÇq?Íäлb"üÌÿE8aúж®½LŸ¾œÞŠ®wufONPæÇ2€Oá»g R¿Æ‰Ä]½œúÕÝý+¢~:Sɼ~yr|]ê>¬Ê݃Êp¿Ö÷.ž.Ô7~‹ëïÓYAÜÑÍa)Ä©§ú¤Îz ¤žÌ[Ü×`çz,Ý êhužÈÌ9—¦üX²“å¾\~Ì8AŠ+!¹£pRŸÂ¯ÎR%wº8¹ãôö}KÜ¿"rÇ‘ÔmÏON;=h€ò†2·±sOŽ ¹¶‡útvO®Ã»0ãé HzË·Rüm"8eœR޲þ3žÞ—¼Úí}šÉ!« 9=šÍ m¦s+yKF«œ:ΜjÅæqÿþ9¡Íô|®|¯ÕÕœ«]ƒ ò+šê¢Ý¥.Çz£zꚣ““²§Bá”kìI}ïàp6Ò6ÁÞ)w_î´ «Ý¿"Ú„—„8j'o<9Aûó€à¥ð帷Z¨-öÇB¶ê$5”s%[¾šs%èz‹ô„o¥¼ÿULüŸꙬ¤–³JÊÙdÀQî®)!û§Ùr_¯Õ:Μµœ54î%ù:Ž÷’ÚLVÃñ|«œG]ÝtfÏ÷Ï m¦=H¡´y›g«§É€£ÜÿSBz¢ÄKñú8A+Q2™´Ê=K©ÏáÇ9œ%J‹Ô^$V8íÛ‰Uî_úé?Z9¿4ñÉ“èÚªìÿp[ÍN´ýùw’ÖYi‘ÜÖî -¡ÞGV °Õ‹Ìœßp~£¤*9i¢Æ¢™œw#9¡=ä–‘+íù£çŠ.²*’;éÌžhN¨ë#­®Ï 8Ÿpþ×€ó?ŠÍ_Äëûç„6¯ªÄ_¥ÍNi-“3óÀ >D`.SJ{Oy‚¯Õ!®,’×ÀÀÐ¥t”1¯LÃI¦çähÀ9À€ÓÖ€ÓÚ€ÓJ»âÆ”Œ»®ÿ"œ¤Ž®Bäw4,?¡.z"õð]Ô‡A®#åÖl]%º&ku`À9Þ€sœç%/è Ù¿+67Ó°_œÐfêWÐOÚ\ÔìvC/g`†vc‹.‹E4'©å"µŸiÀ¬è"þ�9ÊÀˆê¢kqGK]Åœñœ±œ1œQéWn#B]j Y9ÿUJÆ9í"RÕ€“ÌûãÙMÔ›¿Ém½PÎ%;d.áÉ.Z9“9…r†håTp¦)ºˆ‡[Ž|‘¥O4'©ã„ÞéùWpf(ºèÍ ;bi8¡.z¦¼Û9ÿfÎM gœNWþÎ5ŠÍ%ñŠæ„ºèÖrÆš£G¹ÿ9Ù ã0{~�N…Â)×”±Ôçð3Ž{@¡´ÕëDâžùÎ,ò*÷ˆYäYBûŸEzr‚µîå *µ®•¥Ì"G ¿¯\˜Öó3IïûVù+•ÔxEÇQS£¬±ð Mj w8ÔÔèç=W/N\˜&5& qÜžžœ@×óéSƒX8Ž„jÒXXâpæ)®w,tZà—¦±p¶cáF°pÑ¥ÜcúrH~¹~Ô#RC—ä ì–ÑsÐr5#_ñÿ)[«ã¸Ÿ0}&æ›§IŸj‡Órp…“>÷¤I§ýê9ާ''xËæ€ôéC,DB¨±p®Ã)Q,âXø¨cáƒi,\ëX¸,$;YV§ôúrÜ7y²Áf²úmª±ù‡³RõMÇæÇÓØüÇfh%&’VÂ:RðbsªŒõ—ÞûŠÍ›ˆœ£46¯r8o+6ulvæþ'>›Ææ]BŸ6¿Mt“ÞfbáG$ÔqJþB(/T,äX¸Ç±ðÅh Ÿwêúñù…žœ@—2ÖJga9ñͰúh,ü­Ã§XX#÷¶q,üE {;6‚…¤e³úÓ�5eE#ü!¶RVŸwOñÊ9‘”³ŒÈ$àsøn‡³§ê½ç;6ÿ:Í%ŽÍ{ÀæíD× **Uß%¡Î‚P∂Q‘Žs,Ls#Ùósvp ·°‚´‡Ö¹‚",¬è@B¡Ä=Îj¡å´Q÷Nw,ü[ ]ŸÝz°pH¨+ûY´ðySüžwï¸Wö¿žGÈ’îveAÙ˜DÚÃì?jâuŠÃq}iäš¹ÛŽ­rúïÿž&^Nž°™ÇË“¬™ÿ†(«( ´Ržær¡›D<¿ÕØ|¡ÃÙBóÂþıyŽcú™Ñ6ÿ¬­có.°y é=/¡ˆ7‚’?;>Lg,4y‘ûŒ•tŬ<ÐÓúXi¡û¦ÛaÔÃägÅB³{[Iw®_šP×zs‘Ôu‚ÐY(%O)Ð…b’)g¦”|ºçTÎ)åþç)äž“¨½þ€ÚLçÝó¥Íçp”ûŸ§¹y”Ï@N¨«›V×(Î¥œK 8+6÷0HçïŸÚLWì,m.6àŒWtøä¤á„ºèZÁB©+h/Œ[‰)õ‘­„li§ùŽÎÇ㈠‡³^ii^ou­ÓÒºVDµ´Ó„èû.oi=9AK«ìð’–Véã~æ¾Õ øxüìvѬÖ8…x~zYJŠSŒ¤áaA6Wm#6o¿Ðô™ãp>QÒg°“>Î, _IšôyHˆþ…<}<9®wÒç{{u>±GY#òãÞèpz+q.k:qŸ–&î?â$ðˆ®&£n¡ì;ìOÜMczÅp2ö“wü’˜>èpÔ†9NL˜ÎLÓBœ + Wgµ¦HLaDtu+·¤ú69œ=J^d;>ìX¸ …û„(ê î!ºüc7u,/šÜ¹¦:^%£Óâ•ÃC¹¯,ŽWëè¼òD¢WôšXZ_êü_(r ݈Lu8Ùþ­F‡'”Ôø¯€c[A 8ßUήBÑaˆÈJÉþÙÃ}¿Í^ÉààR¿ë\{nbZäþçÚüÇæºPœ:áÌV‹8kRˆ$ÖvsCU<‹tØl¹œr\äþó9çþáÀ™ œyÀiέÀÙœ§9çƒ9çàlÎà|¤p‰uI…#…SœzàÌÎ<ÊqSþ¡Á</:‡Ëyh—“BÎDàLÎóÀyžsn✢pÞÎÛ<^Lçñzä-༜³‹sÖwáœõG�g4p&�gp�ÎÀYœç³s6 åœ 7�gOç ÏòtN!Šœ&³‡smÃ9A>Ö8PÆ›ÀíI! g&pf‚®9 kÈYrœÅ g1çlÀ9)Dᜠœ…ÀYœ8çñqœóx=pæ§8Ë€ó0pÞÎ;Àùsž8˜sž€º“BÎÇÀù˜çצî<¿R•³©'—“B¦‰^õå îá³QïÍFÝ~ðÐe)[Ú÷† n(y4¤3 ýbú+ {82:.€ �äl@.d2GÆ€ä1=9 )ä @�r; ë�y_�ò{@>çÈå-�)ä$@.¤¹€ÜÈc€lä@>áÈØ@JÝØó)ä6@�òbì€|�ÈnŽŒk H7@r U€Ìäz@–¥nÜf@^ä¿ù}߈ûø€@Ž¿©€Ìd1 +ÙÈ €¼ Èù’# NèÈ1€ò#@ 5&TecÂ€Ü È@žä—€ìäSŽgÒž!"—·6"w& ?Jxqw@x"r·g(p¾�Î8@¦r5 7r å°ø?¹…y<OE^ êO ù€ð¸‹¼í-úòP%y€@‹]r" ò- }JÆç?�áõT´øo9Ò²7 C@;o{E˧y¿ƒœû9§ôÂ%¿ZÚÒV€@KR ©Úê=@>ãH~K@†Â˳Èçm‚h} —…|„&Z?ŒJ œ½iÃSC´9äðÖO´)ô¥?ä· ‡×eÑæ%@Þäo ù9’´�á­¨•ìïSD[^›¬$Œr“ Ô@Ö@¨é€@~µ…¸·ý „⺬ä}€@,àm‹(à3Q0 Þ†‹‚¹  FDÉßÂk“U– Ô÷2èßËnd- ¼U·Ê '–�2‹#åÐW–ŸÈ @½pù#€ÀX¢FVå0¢.‡uô2ZPãÌ («×�r+ ›�y^æ­I0>œy1 Òy”ÃI02Ÿ ’'wd4 ÒÈ[€@©› ³€ÊC�é Èi€ÀŒ¾ÆZ•|4hUÂÈ¡ò)@¶²“#S ‡«SxkM‘ù”� §@]žÊg¾¢Ã¯9Òñ @øœQt„‘çT˜iN­fˆSùøPtâ-¤èÔđμõy[':/„÷¹¢óVnO_}²ªN�æDÓ`ž[Ý#5Ðò_±?s¤F¹uÐS×ÁšÃL(Q3çò>G`Ý�}e´?³Æ�Rϑن³amv- Ðþ̆ÙßU¼ôZWíâÈœ#�á#jëjXºfýW¯Z€¹ïsaõi¤Ø5§ó¡¥#Æ|~a-€y÷Bh!Bzôh×Á˜ö:^›¬ë U_ô_€@Ÿ{=¬fÜ�1½ZÑaDÔÈçïVãu¹ êÎM½8²VÃO¤ hWoæ³?kI@€s ÌXou’¥ Ù‘ÛÛ‚CÞ³Ñ;`^pça€ÀØf9Œ‘–Ÿ”ºå0ÿº‹Ï­»†+owóu÷7Yq пß’ïiâÈÊŽÜ kï÷BÞ åy”ÌUsd5Ì›Vƒ…÷Áºúý0Ò{ð(@`m”Õu°¦·î+Ž<4¾*b= ã–‡W¡ôžÀè}ýO9²JÔ£°f¾± ØSx¼ èwžàëÖж<3Í'ÁÂ'¡Ì?ÙÄ;—§ œ|$c·à=‘ÝŠï¾Ù­xÉ´[ñ”·ëàÈl>û³çó–Í^Èû8û°gÏA{O{ïwìÕ¼²Wóõ{5·Øk Ô^¿ì5¼ŒÙ÷A:¯å³Z{-_!·ïçãûAÞ›ÛóvÃÞ�é¼Ïí ÏÂ{a{ŸIÙòy®ýèqyŒˆìÇxKb?ÆW<ìÇ@ûc»8²ⵑ¯]Øy³76�ùµ‘ïWÚó]<û ¾£j?ÁWSí'O„Ï/ì'ÇÂwŽì'¯„·«ö“b›òº¼©? ¼Ý°7ñ–Ín*„šÄ‘½ü]ûðÔÆ8‘¸ÏCðjg?ÿ–dÕ߯I¶™Ôß/µûï{Ü ¥ª“E#úÄSM;…ôQœU4b@ïÔÙ%ÿÌcèËWOÎ{Z#|Ž<Ûá4ÐXXXLpbñJšX¸åN&Ö“S½–ò¦‰Å!4/¸cZ徦Ü™ïo\U]4âøÔù¯œ2¹R"SHeQ­ˆ.²@ÔO«)ªõÓ#…„©1c3±ðKMj\êpÞUòô '5œà€Ñ©ñÂ±Ž´&ž3èy«½©ÑRC¹Ïê…Áaj”º±J½=í߉ê¾!ä‹ìwéÓ>³„ø¢wÆ_&´)&CMÔ…r?DrVòØ ’"%ËüšIüÕm…“úYîpv(ùÕäúp:ùåµåùu‘[éy~yrÿg<}f?ëÎàÌÚ•I¨1haáX‡£Ü‡f}äXØ$ÄI§±Ði!³àÌÚ•ä>4ñLz }¤«O²»­„·´¾.wÆáׯ0Oè››òžv{¶ÈÌi¾xïºú§æÉ»®-yIhh"r~I£dÄ íyKkÏõ~9L½·gÀ#B]g 9xË6*¦yÓ/ bj¡¯1*§•C›gÑsRÒ‹Û¾‰Ê™ÕAÇarè9)YÂí[9=tœ”ÍAéEN\Zm|ŽœãpÔžh³H<àô'y+¦5eSœ¡'šE{"ÅçÜõ3Áüç_p×Õá´à,šï}eð{iÜg“S~VÄ‹„”Þ†m?hÀ¹_ÑUH8Ê­FœP=K%o[µ3àlPt‘;gÔ“þFœ¤ŽCâþŒç)E×8‡Ù“†”Ÿ«èùe%OSŸ#;å%·ßyÐGžävŽ(«;…ÈmÏËêUôÜò²F*¶ê/¸û;ÙÙêzyÁØïª&"y/HG>èp>QjÜÛN,Þtb1#:[wb÷'\ENNÙci�2Ú風<X°:¸Õ™û·»—UϘ^ŸßtL!>§DÈ3,>R-ä[->âÎ&Ú+ˆ;›€Ú=g‡¶v+oÐDp”÷|çÛ´ì`zà}Œ8¡.r£fxƒ¥ý‰LyÓx}BäÈóM¶òžoGyÏwé‰ìà W4'ÔE{+y÷ˆý±g—¢kÑ8Xs{ 8WÓÓì± <_MîsÈî*àsäV‡óŠRw¶‰Äº7œºã­(GÔ»œÈz^w<9AÝYJº£œÁÜú°`3©­ƒ…®6uºÓÕ îtæc¤¹ô~u¥¬l/Ze:Ï¥-vÄMkzNVͯ¹Ã äP޼Q*+(ÙQ’’`Ý!YËÉ:È€s  rX-QFœ¤Žc­•º5àtVtÍÑq˜=ÑœP}FžÜÌ ò)ªD­"¡ÚÈPÊ›8å=ß¹äÅO{uD,¢9¡®wumBÖñ2Úòóc:ÖRÇÏ&œdzNÖIœþŠ.2·•»³Ì8A‹ô㪠­ßv‡Ó¨Œ>‰‡v8­ŸçÑú=çL’jxëçÉ ÆZ—Ñ�¤õó[¶†p¥«3¬}©-¤Û²©m»7ª¼D°Õ]#€Ó¯ó†{’š¸ÿÅáÌ¡qwo8¸·#J¤‰ûß…È/áqŸGn¸ÊRz¢È–p˜䆢ŽßmËï#7ò{±­ÐŒ7æ}¢íœQ ·úCÖè\xÞîÌõBÏÉRÞs¹¦ ³œkèö)g’§\ÑEîÖȺTÑe uõÖ¶™Wù_ö#/®J$ËYvV½ÈÌ©UbAúÓ,ån1#N¨‹®u‘ºf¥/×ì$¡NŠÐ¾K—_)NP¿æOÎÐŽ}ëp”չܑxdœ'OpÿÒ×åIm`un>YËÚLà ˆÔÜÃøÊÛ|z}0ÂÙηҲ²Ez»;5ƒSùNfIòv>©•ö;2åo™9ά¸®rv¹¿¼ ·š,¨ •JyŽÔ¾€Œý²ÂvãN™Î'jµ“{¶³r"$“é,ùTÖÿp‚[6ìÏr¦IBѱÄJYɉàÜ/h~µKIž®áûë‡SC$â¾I'™¥|4'´Þª-?YÏpžú§cJ&÷¦Z/HÉOÊ”wW1ßÉMªVÄØfÁöýâ„îÐÆ}»çµï }ô’_”éS£M2‚ÍRvˆäÚ”%½ä…Ã"%WiáBÒ§ØgJ ÿ`Àùýþ§|„ä·3¤ÏH](¯ÑÚ”ÿ]Éd­ÒÚ‘ªo¤|4'´®œÈý¯¬¿pœqte_™¬‡ÐqË‹îY¼Ž<^?Ù˜Ùf#ΖýâÈxý„ô¹¶¼÷)koj”Ò'1xÙðZABý^†ú‡âîžÎâq¿–¼„k+»äDr¾Vòž ’ÉÖ~_Ÿi8Éôœl[‘Cvä %×Í uÑu9Ên£¤<ôÂ×Òg9®Ë΢Bî7cmº–ŽE°9šj§¯'Èk²pNbfy™SUºc›ƒSVU˺ӑÇô:RZ²î‘r‚‘t†yޱýP%$Ueܯ#»Yêã~]¡ÖžàÆÛïžP¹«3k•Ô¥¼ŸÁémÀéeÀéIËÆuƒt/}šÇ²_œÐf:ÿ’;}ÙEœŠ®‘:³Ç„³”pÖGpVì'©å„åð|ÎP¥æBO•ëÖêä0›£9¡=ôµ¹W’]bÀ™`hs(‡øKdÉ{nݻڈÍdD±wsÝv­œÑŠœrÞÒÊGå,"½^ÖútÞON2='»Î€sEsgâ‹H]¶÷H9Êë-œiég⋆ëB±Ôøþ9IǺEƢрsƒRÂÕ=_w­ò (ó°§°h ɯk¤äà›²†ŸB¼¾iPTßDâNk÷Íq_¯ÕÜà¼?Úɨ;릈ÜÙ?N2='û!΃ͮz9÷è ^R‰X¥‰µÚ@ò½ÍË\Ÿ9åÿâÈô¹ž®‡Ë7²kÀùMsKB„œÍºžö¿¨%!Œ)]/"5¢9¡v²ž®fo7༦µ0 5˜´«ƒe¨WDfÎËû¿¢xýHd–>&òŠ¢=¼Ùœ0¦dçÚþZÆôWœÿò¿D•„9ºPÌ€S¡p¢÷&ºå;w<ô0é"âdo…<bob™ÓU.ä{žœ`O³‚À½ ²ÇÚöXU“ô;ªa~‘õpaš³?™9Ÿp>VRžôÂjL¿3NhÏÛ:{¶Ç@Nö^ÎÅæw âeÂù`¿8ÒæèK‹r§&Ñ΀S`ÀicÀɧ6ß@<¢­©úxqºì'¨•74fhz8å1Ïi¶9-€7þ‰h~*ÄÁ«x p}cåG Ê~Ñ]õô½LI› x™¾èÖnåìÀ‹î‰°à쀌×ô5´RM¼Îv8ƒ»Ë}ã±N¼ÒœœÚæÈ;¤ÇëFò®Yøž‹i¼Âòsã m=UÖn$½°ª‹È¢•ÓÏ€ÓWѵ0R—G¦)cB™›§>ÝÆ9œÍJ^,sòb‰“i<y¶9¥õät•PʼÊMzØï¾‘z½Êw9g‹Ìœ³hj4ÒóµúKÇ bÑH_c¬×¤Øt‡3\°×Ç6:=ÕÉ¿N“bg Ñ¡O±Fòê¸:"ž£ZÙH^†7 øt»Åá4ñx=îŒÓNþ"M¼œ|î°âE=Ïoü“!^a¾76ikŠâÿÓHÏh(ºˆ}/<É€£øÿ4~©Ë„ê¢>YRWµGyÏ·‘úGÝaOŽÌ/òÞ„ÊI}º=Z nRÞKœ²áÌ½Š¼sðec±3]†ó7Ñów þ‰h%n¢ûDRMbžÈÌ™KSã&úÒý2}Š¥á„ºè)饓h4àÜ`ÀY¤Ø3pyÿ8¡= µö,3àÜaÀYªØ³ÈÀæýäÈRGÞ³S9©O·—ŽÒúY#Eâ‰^N ÷n ‰(áë­ßM´õ{T)c’ÐÂÅ;I¨_k,üS¸¹½R9:£Á¢ÒX¸CˆN»¹…žœ@—â•Λ I¨w!”è¶×á¨=ã8ÇB‡Y”æß6gþÖzÆ›iϸ-¤=HXênž¬m3•÷|o&þcâ}*”Ät!Ñ®ìѧ>Gµs8ê›\y"á:0ÕDÇô%gäÒ^˽™œqÎRt/AåäÔK…BÎÍÞ³–ú / ;àÊ e/¹§—à ë%ôäæA2Å^Mí¿÷õnÎ’¡d.!+íQçOõœDp¢ÕÞZ"¤CÅŒéeE©uÁÇ~ õÌW}AC]´¯”¾ ‰_5[W´O©Ôu }_OzÏ&‚“{™t…rè,@ú-'þ"2sþlÀù“ç´^ÜB¼³²åyO/îÍãŒþþ9Aݹ%à Z|\(Ún^ªù)ð€– çn‡³)ç¿NS'|«Õ¼€cNý¦¾ŠuT·€ã¾ŠU俦©¾ŠuÔ E‡ÞòU¬ö)_Å*šíÆ=xë¥ÁînÓ½ŠÊ=o‘ÊçÈÛ«® ,œàÈyÓA²&‘Lnõ¼}¼¸7ùé£$á?õ‚‰ýÒ\Î9êF_—òê÷¦!ŽMÞM±EA3©è~IÞ«FÛÞ[É[êBy ;,c·$œã|ŽZåpJÀ'e‹îJcÛY@¯çÉ t)/_{ÈÙõ¼ƒoÏ3g©bÏǞɎ%kÓØãÞ±9ì¡£¦"Pe¿äÞY¤œM~ÉùŽoçÝJß<Ucó/ÎÛ†N~­¶ùe·þÁiÊ[Éëuâ €ØCÞ@š·°úC¸-ŸÏ…79éWôT{Ü^n'·ç6ú†£æ-ì—Oš±Ömtüs¾ÆÂ8œ9`á+Žmϧ±p˜S;á•öÛÈN„¸˜6éÞE#NÒZHG¹ÓP¤µÙF[›—å}°Ú(5ÔÓ…J}º·-­ËöÙ_!bmoc¡{ïyÚv>äxí³ïn«œÕçƒ r$Ç“óš*'ì/òwJ9%:›»p<9oørTŽ3¶>íô}ä­C÷u¿MÊûq.ò”ò™‡ÌÎÀYx[ð©çó.pÞ眧Ϥ'‰§ï YÃå<½ä< r6œí çMó Èùäìárž±¹œBå<“Çå¤ENÓä 938C@WtÕgpàmÊg¶�çCàìæœÍœ³ùàôâñÚÜÇ+…(rŠ@Έ'¥Ü¶+¥äø-düΚDâwÖ(ÂGžñ;k*¿³Fø5‰ßY£HüÎEâwÖ¨äø5ŠÄï¬ÑPñ;k‰ßY#râwÖ(¿³¦ ñ;k‰ßY£ÈNŽÄï¬)Æï¬)HüΚ‚Äï¬$~gMAâwÖ(¿³Fø5ásjûå;yx©êåwá)oÿ¼+ ü½6ûÞfÚ¯ðuZûÐþ ß­°_å£ûÕcákö«ð:Ò«|¬n¿ú!C¼wF¼½-å‘MNß3Èó@-Úçäc¿&gµŠ?À.²³oôÎÈknj„ïŒôï›ò]QN%¼æÎ€à‘¥;‰gš|YÃÚ!2sÞì1}gävz‚8âe4iÏí½µöü>xg¤;ó…wF"BýÎ@ògˆ)¼3rûpƒ˜špèMé~ž·Ó»˜¤?’¥øÞ^¯ã09ô= ù†ˆõgEÎB'esPzo_–^Ý;#Ý';œ·¨çLö ‘xÚ™áœæy|FÔ”÷ÝÅkŠ''¨)³@•ýšÃWkÊëù‚ßyÝá+5åuwµY9iûº;ÿò=~+§WTõï㎈¼ýkŽ{K†rgìëîœΛßAó½«4µ0øòú*!o&¡È‰æÐ ×õhÉÈÉ3àäp²iI¸ƒÜÈ!ªi¢ÿ œÐfr§¥8MÚÜÞ€Ó.øâÕîP»¯ÝwÐF¯œ¤áH{œN6ä\/íéjÀ‘÷EØ ãwf¶ÙˆÓÍ ^Ô§½»´ùNp¯„Z BíôMœ•FsBíÔ[ÞÑj$µ»½î`¥v»=¬rÖþuwv^åwÒ»¥ß }šÈÌdÀ)2à(w‹ÝIO[lH1}‰ ⥡4œÐæyD—<mŸcÀ¢èZh`O4'™žã¾Ê—‘3Ò€3€3\±y½A¼¾NRÇ Û^»Â€3Ñ€“4à”(6oÓqX¼¾NRË™"mžiÀ©7àÔpj›É}q"x Çëûç$Ósìk 8 8ó 8ó›¿0ˆ—gYvæú•†#m^F_&m^lÀiTtåØÍI¦çØ+ 8+ 8wp–)64ˆ— gè~q’é9öœµŠ®qöDsB]t¬.OØOp6pž0àlTl¦g_ˆ×÷ÏIj9a›ù²g›g«§I±yYæ~çà$Ósì_pÞ2à¼iÀÙ¡ØLÇ�3#âõýs’é9ê[~œ 8ïpv*67Äë_ƒ“LÏɲ2sì}ÁýªHD¨o $cÀùÊ€ó%5v¤˜ ‡œ§Žì¾N2='k`ÿü|y9#‰ ¤äƒEfN{N;NMåtø%}Šý�œ¤–#oCòÞàËÄéfÀ9€S¨Ø<ZÇañúþ9I-GÞ(•5À€ÓÏ€Ó×€Ó[±¹JÇañú×à$Ós²Î0àœî‰X¯[Þ``a4'©å„/:–ÚÿùöG/ù2í# 8# 8ÊúÏòU™{‡NRË ËÆœ`Ñ¿G„šf yª§Ò€3II)öÿ'Œ;½¿e‚Œ{°öÑüúj'·|GÝ‘x—·_®ȓڭ‡g8©=7à¸'µOóïŽ NjOªªœ>µ—{vý´!ûöæËGº¯,÷x÷äx’{¤Ð”WíBŽ'y°/9$Ÿ—’Ü«¢¦¶rz}…js”dÎñ$Ÿ)ùBÅæuâÞW2Ø,9žäó#%_¢HÞä„ê‘A²äx’/Š”<ÚOi¥UUÕeN.ßòndj0Ž'yd¤äñ¾ä² Ôªm‘’Ç“<&Rr2ÈÁÚr·¨ˆ[³£sPåx’'DJžäK®«¯­*ŸîÆtS¤dÆñ$—EJ®ò%—&«kë]{ß)™q<É“#%_áK¾²¬.eϪ]‘’Ç“<-RòÌ ¯¬­¬/wB­nÎ*Ç“\)ùª +'M/­rC ŠNg•ãI¾2Rò<_òĪê/¦«'FJfOòœHÉ?‘’ëfMssgucÉ Ç“|M¤ä”Úý‚*²ÔqŽ'ùÚHÉ7K›ËªªëÊPѵ›q<É7ê%ïs?äòÚÚêÚ±&º2Ž'y‰*Ù‰Ó/Š‹‹§•O«+¯½Â¯©oÓªg–¨÷Ý)eUå¥ÓgÔ¸_kj«Ë¦•ÖMNôT×ŠŠŠÊâšÚòšâÒÚIuÞ_nôfÔ–ŸqÉÞßõµ¥ÓëªJëËCʔҙ¥€O,/®«œ>©ª¼¸|zYõDçkqYõ´šÒÚr1­´vjym¯“z÷?QÔ—×N«œ^Z_íüݧÿÉ^¸i• å1˜ûÓŒéìü 3¦×•—O/®NN)/«¯Ê¯,­­(®-ŸTì¾sS\_š¬*Ãθð¬¡èÕ«·¨¨-ÀeŽ u½úô0°×ð~ä/1ÑÿzRòÕãT•ÖÕ]ä©çœ7¤ø’úÚeõnZÕϪ)?ozEµ¸@'•× ~ñŸ9«¾üÌåµ3üÓIÃÚÒYZ𢊠7‡½ŸRpMu]e}eõô”̳&—Ö™áŸD&}™^øK&;Í(@þ&�¥"ΛNÈ¿üàeXõôI>ÇûûœªêRˆüM´Jƒ©!¿„ºˆ(Rû‡TÏpJ±†DÂD«Wwœ:YZ6µ¸rúÌê©åAqp µSÜœ­Åå eÅ“K§»Õ]WWM,®+Ÿ4SE’3ê$@dÔW»yF˜))¸rze}qÒžÊQÖ)ïë£H¸TˆQ$Èðjg,çÄ/üÕGR%Éáˆr' œªéò�ô $§¤à˪+'úòj+§9xfyY]í¶Sìwu#À—›.¥eõAé’ †{¥˜Qæ©òI CAÝbÄ~!¯¸²_ –*>ìw Ê–ƒýyæ¬ËJ«f”«É›úëÂRWÈ¥55å•H¸ ’}pNªâ„♞\µÕJ Qˆtï÷Ô×âéÞž,YÂϪž>³¼Ö-6„p)M5åé Ð93¦—¹-ZPÒÔÖÕéͪkg1+§—_yÞôºúR·|©¿Ô–—NdÐŒºò RB¼hQ__Z6ù¢We]„Unñ¯,­rº‘‘“=‰®M~ô“ÍëÝTtú½úû¦N‘Ôý<0ü³†FÒ-|:~ŸÞï²ürÎŒãç§Ï8ÇoëtBôó~N9N˜˜*ˆçÍ.ÿ½Â+ÈÎX¡®¼Ö)N:Ö{£Z<fÔ8sÜòaÎog{?¥j.éÜ¿½&ÃéÍ«¾½ž—”‘4‰" LJiµ|ýõ~yt¥ø•"ü5HŽìmSMÝJrKÈO,ùw*mäŸAâKÀK{ùW*åŸAù‘€ovEmõ4bTjŒáêGpÊôR9:Ò#ñ•6Óm]•ã”Q/C”¾*õñ÷ŸÔ× Êë'W§’0õÕMûá¥î(ÊϪ:öãˆr§ÂM÷2uZ ú±`\)ÿúʉ•u5¥õe“Å õÓjN8¡¬lÒ€ú“Ϙ֫LôrGrޘ͗“?ŠÏvÑ™g +.>ç¼âªÊä”é•âRúët¿9þ1DK͵W“¿’N‚‰*'[§L«qFõ½œÿêJg–;Ãiï»S!êƒï.^Ap÷{ Û‰3�¯w2¶×dÑËI*Ù<y©å"©Š’êß{ñôê¥ zÕO®­¾òÌYzéá4†gùeÅ•ãã:U9±œþ2¤²Ö1À×3q¢c£Ã«“!Ür‘ªÓnàóa~6Ôpöÿ×Úõö´Íñ}!œ•ñL{ ”nІèxxÞYnâ¶’dqBË·îÎŽí4‰$$4âóÏgû|¾?¶5El ËoÊÖaá�ø“6& ¹¼ÑM¯BÆäMXg°:BÜóZÚ2òSÎe2k¬<‹%~{›Ãìì½øÅ§YÆ–è$ÒôØñÌê2²ð,šÏ¹e»ØÞÚJ\g9™<Vh‹Œf=–ÍÒhl& ³kLØÉ$é¶ÅŽ¥Rqv˜fqUgmŽ13:˜µË†™ühå 1 4áÑJY# Р1…r# Z11èA%MPdÇnS±t3Ü/J:J³dõö²Î÷ÃÚOœÿä_®–Ë)°ëi°ù4Øbl9 ösl‚Ìäñ÷€Wà Ü÷ãB³¨ Ì,*(2‹ JÌ¢‚³¨ ¼,*(.“dèg†|æ¬fqcî4%1!è(ÂöC»Ÿ`}Ør ?¡43ˆ0aîÁ$1!Hx ÔØ¨  ÆeªFeªFdªÆdªFeªÆejîj‚:Ðxþ|2 iµœ«àª?‚•É?ÈAÙ~ô6e•ît¼‰¡BªxáJƒVwã b¬³@/\ Lñèvà©¿þÕQbyy·¼ÞÉøYÕ/€ûµä¿³Ûc5ýýíui€CAÞ‘ª ¨„I—†µ b­æ=#:ŸÅi@µ|œNF°¨É°.¤…&ØŽBC›£éÔÈ–(5j9LØt ‰™Š¤Ó}g鉲nÞG·.e:†ò~çñƒº¿ )YŠÉèš ;*žjžmT$Žeò7ªAEU¤Ž›5”fm®¦³•ï Jœ"}¾A^ â‰TG´ °X8O|b»½hí% -©+™Åpù*o!?l„¸† †ÇõG™óö¹À׋vùÛÇu]ŠEв¡LŠh5]©Êy‰ŽÎyiú¦„5�ÐÄ“Ë&ÛZo&+5"™¼™ìz_Í©¼í˜>lÒt?v2sKsÓ·¤Ž ¨¦W}Womf'"qô^¹œVŸ §=ì“éM5ÒigܱO"üq›5?Ȳˆ#ì!å!͘/-O]ÁEòT«Êmd’b…(+®Z¤Ç dÖÞøkÄ»DfvíUê,Ž=ý‘ûB–|~yÿ¸üÕk)œ O~¹üÏ+™v8TÑ­Úà5/c‘þÙÆñÙûÊÎQdP04S:GQq°®¯IúÅ‹¥AíKžtj jë¾Æµk]÷5¯Ûí9(#üêòh* ŽZ]ta†>:é:%HÎ’Í{"ºxº7õÙ&yG ð/ñ§(/*§æ¤ÔàÔÄVªèêay;'ë:Ý'†“æ�M÷u"N;<]…´NíåzvþÍ”šËv:t⇴ÚñµÀƒy”qNy#0Ü–¢©ú d¹ªP÷7úØJž8/¥¯ð\ÿtŸõaÕhd|‘—±LLánÝŸ»Ê{‘*yÓ„¶ØÑ-h»‡/ˆ`xÔ†ÏwY}¿o¯—®¼êT/ïNŠt«ï78AÜ‚9ªaUV…„ÝŸÆs|üçî!j̳U%JòðÏM†›ò–æZ¬Õ ùÜËý-‹Gÿ3tôîÂ4й\×Ûyþ<$Ôþ•ðÐBiyZæy _t*í÷µ—Uç+P<Þlù›öÚp­¨¸ý(^{ï¦BCÚû€„iÑ@Ö•í¤(š2~+Ê'éíH¾ios2ºç-Ÿn }”­Ï^!Ë*ûÏî…RéæÍÐyþ 6F†Z$‰ù ½˜aû4“¢„|H€c3 LÁžó¿œõor£øÿ¼ÐVV•]òz>äe^Øt+-ns†Ç×[ÝWÑÞ8|1¿iÛûá^œz¡²LÓ°-¨÷Ù‚ íñ F´õ¾BÏýÜ!ÖY‹ÜØ#$Ò<‘!š]SòUø4úkYŽm‹jø•ÆCù?ñž±MÜ?±™\sˆ<TžA:S½ñ×Ï'të™ÊôC`zØ<<cþË3æ==ûPK ����v©Æ@���������������com/sun/jna/aix-ppc64/PK ���xƒi@’2 £Ñ�ÿ[ �&���com/sun/jna/aix-ppc64/libjnidispatch.aì½ TTWž7ºOUU@:•î"!ÑH¡˜‡ÓŽã⣈ÚàÌh&$±@A|%éC8‡*ÔtC'÷»$Y—$`0jÓN/{}?»[޶AÈÌgìÌ >’ÆÖNªÀoÚ$& œûûï}NUh°§gͺ³×¶ÎÙg¿þýì§t…EýõãÉ5Œþ¢;ç39ËÄØâ R“˜þgg&7ý:ÙíÓÅo† ?3ៅ™Y³<¼Œ…ÿ¤÷Ί‡™sÄo쀦ÿ±¯øûfM¹\3¡^ã7\®þ53^ü¾ô;üwŒ±ÄÏxó¨œ2OG/'Ünñj—ô¿Å{Ôæp L¡?ð:¾ùÔ–-Æ'»?/ò—àú*xðçãå¬ßä)+ß|ƒ$ +ÚóiÞ‹±óÿ¿YVþÔ3koÄôÙñ›™zãrœ7iê­ÿ)VÓ[£‡µKJý´ÚÑsZÐU¤ Äì£;50Y{ZY'»·2§k1kö#~ñŠÕüÖò !ý_ù˜s¨‹Ù3;ËÝ¡%Æ Î`lÿ f ,d,s/cê¬9wKñÛðÞÂì`K 4­)såa.ªïí;1«y³Ó4MM‹9Åðë4ËYÀ¬bf5T®\úþV¤­¡²)íÎ4f¥tF5Í,·£îì4& /blU¬eQ@e,P…v¤=î 8Ê›Óɬ”÷‘ Ë"ï‹ÜîVfU2µåëttz|f£I‰ÌQ¹±²¦Ò³¾ò¹òbÏÚµ›Ë×zjÊ“83·3IÇ£¬ãÑÄñèÑúG´ŸÂ'`N>Û9>ñãi·‘x.¥à³6NÂks³?bv¼O÷G \¢¬v½Åb3÷0VÕÉnóv²¹vžª¯ëdκΤ|÷å”kZ]]R^δ wíi€[º³”p¤iž4ãŠÓ4%ÎïšÜ«Àc•Yöêg„Ãì*&åneÖZ•9½{T.ÊœãW˜-0KÐ×½C;P ]ûÑ´­À7F߀ÇÖu±Dï‘Ïå<sG `˜ ÜÊ‘d”WIåo¸»Æ>ðn?páP®]ðÌØ¡¢¢ :),~ÍšÊâêÍåÕÅ¥•k}”µ‚.DСèÇbëmòñ6á;µ‰Ú*W²]e.³‡Ë]¿iË3›uÚmsv³,£ü‘sÚÁQIëMÖzFsµ!�Œ¨;—pCõ-avŸMßfc‰r\æù¡&fm¨dàÛî73+Êt-,+óàµí˩ҴG™«®´°üƒ#§ŽI9‰š Ú¦ätƒ§ÑjuÁ¥ôv‹]gY”ñûZn«<…~þѡęÞVKw¨•£…`\p8s#8Ä»d¥.ŒÐ‚ð"Ân‘†-ÐÃ~úUç_J§þ¼Wô¥œ˜ÕÛÕO¼à>» ŸƒÀ§1p1\ D÷ÉhNåuØ8Ïì§çvñ¼›ž}â¹%PÉì[ÌÈ`Œ`Ìô13x±£þ÷¿’•×.¥Ï¿Âb¼»†ä¿¹Â\yì¶?'Úz;é@Þ2½½&<Òs#ÊTXL ÞÝÆ;žçf¾Êâ Ø€»ø`"àK|Ùè‹€Õ›s€à%4Òvëiû#ÒöK;vaþM{ íC¥ç®0'ò¯ñ˜Í¤³˜i»-ÄcžÍk·$qþ¢¾R7M&Þ"9@¼”ãcVÎwÄw:oy‚fô=ðç¯^ôÏ.ðNËËcRÉW— }ÅÔqºIE„£˜ÙU$C©ó<!«i&Çpâ{Òs © ày=ä*áü6<_åò¾KÛÏËdÌœÙÄì6f&™‚ïÖÜ_híT‡êD]‹D]“Öº#ëÿ´î¬²)rIµ’L_"syŽô|Èüt 8¾XÅÀã>0o[€ú‰æ¶Ç¢àfª2}$†òÿ uN%=É2ÐsCà fÇïÕìf¥¼ çQ.SœŠÂr)¤?²”2ÏæïUn,ö”­{fKMX…oFÙòÍjÏæšofé2åßOs7h^¤–üQ´– Z£»€Yã0Ý šgW‚Ö]ÐÛõ,–è9eÆû+$Ý›ÿè>gªt ót®˜„ÎûX²§R¾)‘ïOа»Bzɰ¹|f¡�)w‡i~¥ÜÒ‰†E}õ°«ždvÀK¶MÆÙ×ÜmR‰ <‘íúi—ºš&’”—64ßá‚~€ü´µÇ!ŸùÑÛØú€‰Y¹n°B7°ª-¤ õàÒCáÂQ×1&e|ŸÝ&cNà×QëŽ9}ŸÏ¤Ú|²!\Dk9ó-Îg­e{LLÞÃñ{PÍÿ–ðX³3˜ÛÝÉì,Ot‘ÍV›†ïà Ø?Óa¯ÜN<ñÒ"–H6)ì�v#»°Zâ˜eUÜ¥.Ò¡¨/1;ü~TóŸ6¹¬,fó©î‡ôº®&|¡M…x^xÙ9Žb#¤Q—8’¦kÚt¤{]šKÏ7 ºŽ¥O¿þzÐÐlÈS)Þ7ƒYˆ§ˆ¾ˆƒl›Ný¶Z"õ!ÙÜ5ùÝd¹;ÇXÝÞ¤¥*/}ç,”Ñ„~ Û»@=Nù©,´û=id¬â©L*ý:‘ì@*ÛëÓËmcs)ÿäe×òüésÛ´‡~p­dA»••ìa&ð ì²k ø‘žé[5ž-úsžãõç"<[õç(ÿn´¹0DA®^¾Þþ6;™#Ò®+Þà)­(+¯òrÎl!•Ì’uÛÚA/«þ–ùÀƒÁw˜L}Kô'¶”úðêÛÆ}»ØtØeve;;áÿ-“]È—mºÜÇ¢޳[#lD#¥¥<”Wä±|z£<T‡n»^«Ñ'Y iãØ ™E_€é|BM–Ö¹$´;‰‘Ü-œÄ`VÂE©gýú$æ³Ø .À}X÷1¦ ùÁ.¡nòGB²læå~ŠkZÎì©©‰™jókªdf™m;Ödc…­”k_\v i¢ÔÍ2ÒE×VÅS«JåZëá ªõ écjó·]Pó—È2[vò¨%f™ô¾Y‘dž²ck«S™q¼ìå,žô�/»’ݦ—ýµZḵ&ômÕZ*Ï3;Ús!üRÙÁ]ÌY[µíBðKU«VêuA—8y]V’l ŠÕeºÜïI3½Õ|žÅ¿[{¡óÁ7 7nkFZ¼[:Õ úNåãݾÐÙð~„ª‹ê‰,SljO ù%‘¼(|âõ›Ju_£JÈôìï³ .×…žn:‰6à F²¼6-)²lFmÚÀ…Ú´û>VÓØ EZ¾zӞ˪ >à}ÚO²¡Ë±$+ˆ§ ûƒª9/Ý/vU äú¸ÍïóÿŽeq™cz[˜ïP!¹^&‚$#¬ÐŸSõ`ü%B·Èîïöû™½ÁŠv|û;ư¿¥¢lÈOåíåéX™KÙó ƒp�¸¬sá—ó6nÕJ`³sÛI]¶Ræ~C.KU kü},•ìzÿrfEû’ýˆ'ÙòÉHG’\7|CÂ/êríåS=¢Ž±«äoº¢À»±âûõíëGi¢o’¾¬¶ ibn’¦i²¼û²Ü™+”^‘ºo²´eHkÑÓ:{à·ó2‘ç*dû z?ÆíŠ.<ʶÂ7^[©ÖJ:i¬Õÿ¼È yy™Æ¨|Ä[BòÁdÈS »›Ë5+Ê×W—o.^šûð£Ëÿ*‰Eü)k¦ÉʶY²rôøÃ—…#ïtëŸí,ê©ÒMÕµlBüt³¡|ÆMÏ–3} Iã ^7ìeAœ¬$'¯SÞÉ••k92§{JäÚ%î{ûá'ù¿ ›F…yArržð°,sùw2·ÿ0Ëãéα€Ċ +K¹¬Âý}¶´g&÷¬Ö|=ŠÖØÓª5SºžCZKÏY­½çªÖq|º¶ŸâŽ/Ð_­uW´žã­Ú wHë7øÍ;piģߤëa›6œ—ýÓÁ{€‰óÞ[ÐÝÊý°mÝohýýOrz8±ˆÛƒ=†€ ™–â/¸‚€+¸‚€‹ð\Àkp ç yYqG2óõç²Æ~kîo`-ý;Y{ÿaÖÑŽí?%±ƒ§’Y÷©\ÖsÊÃNœj`ý§v²µô ÙÕÀª];%§ë0óA—>Ãq·•¥¶—²X¹ô¾ ~IbJéL‡¢Ðw#¬®dv‰ÚÍóyس‘qÝɬ:ò½çï=¹ ¼Äæ4zØ÷"¿ïJiL ¾Ç·O(¿c»ÏÃj"ß'¶ïÕ7{Ÿžqß9ŸB_6@'\” ]B>2ÙHŠ-æ@n++t¡/CX‚7MŽLŸðe¸MZ¥Õ4ó[H³òÍ–×iib“¯AÇ*¥&ǃL;K:&4¾‡ú×¾¹Èû³ú³v0¬oBCø-bLk³ç{Ü€Î73‹®óÁ@m×õ~7µ‹ì*àxA2ƒÆK&¶Gf1)Êv“ƒ`wÅâ=–ìKØ /S Á³3ûu£8¹¿eËíÔÀ_pÕ¦‘¿?Ñ_Ø®‡=p=£iZ <¯BH¥öP}®í°ÍQ7ÕKuRݰ'_ŒÄ}ˆÒ£„Oí²0 =SÑâ=:âÝ…÷˜ˆ÷Ô0ÍF·Laß0ðX³©¸ºf³gófOm’.ÃrµÊsÐÝäKtfÈÜ?Û1+ìŸéãxd[þ¹¿½M·×w°þœV-˜SÂñ¾Â?ŒÆ¢ü;¨í#{‰ÿ &§ƒ·ÓäðÝÛšÆóhÍ×ï¦1>Ða;òØvVÂïÓFõ1=¢ÃîÀSÌ}ƒ1½KýG™«kEh x`4:pß\¯/rÚaÔzlÙ°„’…7¿£±x£ G£òi¼-"ÎIq>Û¸tñ×1.n,Hqíãã.Pœ1fÿžgô/:D÷÷Ý ‚ñÁ‘tÿe†lÐXy4'Ù¯^à|�ý¥Ë îW@¶þšìvïÒßÈ.óxÛÝ»í¼œ³“U{÷ Ñ8ö^´1‹â¼eg¸íCñ!ûç#æšhÿLça8C~Á!öu‚`nܲžø ¢¡£uظn†¾ç0Êü®N“Cº06”ê20ÖV%¥ÿdà‚ÌîûØ€3ç0`ü5 >C¼NðyŸœÇ(î³ ÄóS[ Á‚£5,ï„cØ¡ö~ø€èŸçpºÐ8Š®;Ÿv½yù’ ÑgjÃø�î­,~tËíb®Ð\Ö ô{¨=ÃÞgëYbÈÞ犴ùyýkQ¿î{2,bŒ·$Âw›á;D”¥Ã1G¢Á,è¥8]'Åq´=¤“âtTTÅÇI‚$ H!]™1æùMöÍüÆÁ~IÕÇÎ tv²9|³ã[7ภû«Àþ¿8–p™ü^„Þj á-¬·ºÙë:ϳž°òº‰îêùè®íºëš®»âtÝUtÝ¥è:j½VSˆÐ]JDš¬¯ÐaÛÇéx d`X'âÝñîæem§±dmî$ú*Qð]ã”Öux\Âckv>ðÈ뀋*ûHóûš>æWR[õµâñ·Õ¸IÇüÚIgˆgθ~„$[y ÆpV€¶÷Å1Ö%|jÙI^_ž›`%{ ¸©‰Ä åñ#O0"ñÔ„x»`²:Èç£øŽëã·S|ûõñë)¾1Nè’ ß oâ]“Ðf}m®S,“é.¿Î> äWÛ,yÜøoX¯\»¡^ÿœÖ[~†æ ¢`dQœw÷Ð+ˆ7ô ×)gáCEèWàsƒ6ðB‹/8Á#ð0Wu²yÕ¶¨s·²¹ðɸޞªª¶™ÚsßÔ>ðÝ+ôð¯¿`ŸÝÂéuJìF:lœ½Ï)é0øK¤³€÷ —¤Ã Ÿ½ß§¸!a·]·”à ÆU̱q8¸‰Þ#¼¹ß„ ýãtäÕ ²•+Ê[Ó“_XÏ þ“éÉ{Cº-ܰey]i¬¹¯+ï¾ayI­¬øÝy±²/,ÇÈ»4ÊH$¸ ;Žd¢Î?Ëõ™–Ò¸1~§+ÝÈÊ ³deÝ4Ù½UrxßJt»Û¤¥J«Éìàö½S‰9len¥qé<ekžÒíš§Æ®D>×<ÙÊf*½K^–£˜³¶”Íô~œ{’øSù8í¤Z\XX›sLÍŸ&{\Hã@)j¾Éám/t»¾¯ià'‡÷1ú¦~§°Ê#®F£¬wQÖ[(+O/«à ðiSR«¨¬"·ëc^–]·â}LÞsL˜%sCê´×bæãÃŠÈØ~µŽ@¸o»2¦”$Â?.¼nÞsO¢›„ •]zPi^€K÷kÒ%Øvï[N·ê^)ã[+Ùb\_k¦y÷ÀÅfd¼¬Ä"ßÛ<ÎOuÞg4o n¼^àñbîIu{©¬¼—vRY‹4kÃ'£²÷-Á5]ù„àu¹]/rxã)>·ÍT¦|ì*TÖê83g4¶µ eø]…ò'(chœ™€³#:κg‘Œ=¡Ž�ƒ²ò"-/ÂS{êC°íߣ1ŽÃRv"ÛIsëa<âûnµ�<“žaEóÔü˜Jý*Î+Þ1Á+ÊWÍSJ‹æ)/àû’i²¼ðk¹'½ƒ£²òFÚIï»— ë>B{Ž‘œçcˆ)Ê1j{–;»Xç÷1ú¦Í*ô~ŒòëQ>•uey‡w³ åw —(k—K”eàò ½,÷1úF¼¦”¯?\Iø(I.,47[Xú,ó”=îyjy†¬¨–yòO�ׇO¼,¿ƒzKÝó¾3ÀdLÇîÉÇNzßû\VƆOªùþIúDºÃ»§Ð½c|ŸÀû}£>áíEùj=ÊzpPYUè|nÂ,{ß)eUQY ï˜^–ˆÇû}S½E…µ?lƒèƒY_÷þÄåVzï?éŠEzâ矸¨ÏO'ž¼|õ(øãtmQÝó¼Àû!m@ÿ¾[—_nŸÙ_– f-..«|¶¦ò!_ }Ey"ü-’Ev!‹¤‹\5AU‘,bWuY”ª¼‹¾ócôA&ä‘â9àüôøÁ‚yJðÝ�WšÜóäà¼/GÈ£<¯É>©ƒü(-Òq5 ý‰ãdºÑx?qVŠS×*/ä¼L¶›rù·#ÿZ=eø1Ò&Òœ¾·=ÏÍûânú5g)Ûó ÉGP¶/GýË›ˆ¾r¼™l­däÉÑYs´‰'YCu©÷9íjùÝv÷TS h®¬å:y‡¼?áq'¸¼‰ƒ¼‰†¼!ƒ~×/sÜüþ±“Êžû#d p³‡÷ƒD£pØöp9³Zù=É™'8^ÔRä=‚¼CºŒ �/oå ¼†ñb×㬧|?¯Æ4"eËx¹2ÆÆÉ÷ur¥_­€\©bnµýîÞó6å'^VšVÌ£þ§Îš&xðÊkñíy§÷B°°NÓeêžñ2UÈBg¥8ëÑçJÁz~å‚ëyôÝ=‰")åOÔó'ŠüˆSÊ©½c-$7Ô*j+ÂñÕlž:›äÆêyrÉ%/+?@|)›GåÊ{Ñf…äÞ·#ŒAŽýösYݱü¤ò‰_ÔÿÉyÙ»ÏcÔß]‹øx¼Ñ7墫Ðûî‡:#—Ë!uÑò“ªÕUHsª°ìu Ùa¥²Üa9$â…Â7Õæ.¬Cýêrdõ@vt乕Oî? ÞLÅ3ñº[y×iW*síJdûQÈÁR„íO€G¯^VÖ¸ìêìåv²ƒÀ³ýĈ?¡Ë–ÝÞÝ Ã!í¬j["GŒ»}–\q‘\Ù°©LÈ•ÙaûFñ‰ñJ¥÷²±~!G.ér¶úD».Cb!CÐ=¸¬®*œ§Cˆ#R8Oîf3Õ8óËò»!³A‹ é¤jY"«yÙr$Cö¾mÈÜo9H~¸u¼½­ËÜF’¹ßr¨ëŠ ©<.O¬(+v‰¬øQVI„LùqžÐÏiTVž;;O×Ï"ÞŠw®ŸÕ¥°i _øœÇì_ÃAöÊYe/`N#Â. ¸lA‰Òž ÙwIé{àe¥ÏÒ{ÊȆ^ÈŽwùZIÙµòåÚ9ÀIÔC'•ݲùHò¥|ÉJeoý‚Ìíz^·U:hŒÀ´B±²B¥}¥!ËPFúÂïQF,+$B±M‚‹¨ ¸ˆÒqa™%ßÎã í1ZK…P0ý.O××sCýNÞGãHøVŠoà•ZØ j~žèsGIÇz"úÜбãûœËè'ïè¶Ã½Ïi¢Ïq{ŒúÜ1꿞ñöؾ úú#£ÿê¼ó‘Ð×ÎõÐG=àg!××jÞC'9¯…ôµÄˆ?oÆ$/In*ÇVÈ¢2ÈÐUÙ½ºb5ÇbË›GtVK!‡¼:¢ÓØç,¤¶xaSë4N1hœ]®·]ÄÇã}Œ¾qZûVrÙã½€²Ãn”•¤ÓÚ’£)Jé·¸ï‘mÑËññx£oÔOH¦ªq$K®)à+^ž÷S»½{a3âë­OœÄ&™ÎlÅÅÏèFÉ8ÙÑ'ƸI†pÙQÉåF¿.7J¯°ÝyŸ"ȰmɆ­„œ;vy¬øŠ8NUÛJ.;`ƒÌT‚¬E_OÉ+¬M-Ó 7: =)ø›óv‡¡'!/Ró •¦•ÂÞ[ ¼mGþt=¿¶Þn&t´Žaðþu:C·?VÏ»‘|@ÿ÷+o¦t."m %¶Ålس ¸¿4Y!yAcÇ!/Ž£,¾^jçòâ8äE—L9_@¶ØLõ;¤¿à›¬Ã÷udÿföo"­ó¾U(àø1ýšær?ðíƒÙÐWQð–ë> Û|œ8dBÿï5Ò¶±`r,ô¯2ÀJúwi„þ]:O~‹äÀ/Ëo“Ý]û|ÿ¼í­úWÝq¿Ð¿‹@Ça»¿ˆú_$Y"úï‹ÔKÂúWÄsýKßÔ¦’Bï{¨ãÔñÊ:޲žDYß/eA—zßæòÃaÈ•ìEšÆùnÐUX÷1ÒÜ÷€¬~»èë„åØc¤oýx¦~ï [PY—mW*`÷¤úvÉä2 |›|8 ð¬Rßå8Ù/s=Hþú$çcÐO^‡o?Ðé¶;µPÞ3ŽnÓ#èf×ã¬Çi7X l§‹üê2vKÈï1l'ÑçE~ÃvíÖr[¯GÍȲCGØÕû¸›ÈñÔ9vøárôÐe@<ÕK~)ù„°!z”ÁòÛ.\/Ì~.t#ýý}>÷̲"ÆvÜ>‹±³Û„Ü(þ^ñüÙaŒ¿ùôñÌ㺠yA—!|<…]Öeˆ{®F_ûéTºA‡hc<<–Æôñ6¯Ö œÿÐL:VŒ§\N*ÏCÏY<ãÇS: ß1ßA²ÏµÛÁúxÊnòó4nÂ˃.R,—Áêv”åŽOÙG>©šÓ×y}C~ù\ho¡Z5w ¯}ÍKä` Ç× }ÔÜ>[|p-cÈÇóß(oØV1YÃ2ÉœG¶Šº†üÀ.í •5üÑ®•’U4Î’þÀËê}‚ˆÛ¯T&Ö¦³™„k”)yÏÃ.©þ“cïx"lØ{ ™C8gÆØA<ÅJô]ÉJ¡÷—@n—O“Õmž°m©÷© èý]ôþ.Òûù\ï+»gqZ€e¥OðŒš]C²¬~é<j»w úÛºTôݸ¢y‹Š>,—ëpøi<ÅsÒÛ­§ì¡ñ”T¡s÷PRÝÙIÆxJªÐ¹Iè_øF}Þûû•b<ÅÀ‰ð´C´ë8ÙeØãpâÐãÅx ¾‘¯¡4Âî-år¹Å€ŸëûÎkþÎk>Ò÷ô }¹fV€¹½š°á”¾«6ÜÕyÙ3U°7^öÁÜ¥ûN>Ø2dçÄAΖÁG~áSè`¿îïï4Þßq½`Ð@ø;xþN¼»P­ö‹Z€²Öƒ~}(+ßî‡]ÆpŒ²ºô1œô1œ' ¹ý¤nMËà;ÁGP-ÜwZ…gê#5zKQzÀë{ÅÁyJ7|Ú¤QYÍ_1oôf%~'þ¿i?Zã¶«åÙ]Ûá?Wp_«ƒú"ËÑéü®ò¶»IV~ Ð—¶=ìoãÚ–~/d—sù|Ûû¤×i<¹ÑŒßå,ÆUÊ$Ùzüˆl.Qmd_ȵËQÐVm £‰->åØ=Oû»hþ–ì55ã y¸l2¹kì bE,~s¹§¬xËúò§îÿPæ6ô/cÿ€¾>EñŠq}}ÁCЉ͊sàÑöšÿŒ'hþ÷ù<ÐLV-³;;›émÕ Ù˜7§9HÚûEû½è]Ó´È�ñ@C+´ÿöúÞHódåÌÚTΤ¦ÙÌ$³ÃÇj«¶kZÆÌ4‡ï–Ú´e°÷ž‘&JðKŒ´ÑHË× ÓÚ^=m Ò^ ´äÿˆ½ Ìùhgô³™Ðx¾X7püˆšö™#÷Mf¥yaàmÿð}7Þ@›Ð>>Ø0›·•ž;vŠgÚÖªìʉŽDONË‚Z²è’'Ô¦ÚAKÚ÷±7œ5åîd>Z{<ÌÁS¿$³j?âüü]sQ;©È#—Ƥm¥½€4Om&8B¼Û•A¼èÐù!7¬·¥‹l:ç‡ò¥›ÊÊËŠŸõ¬¦¼ø{•5ÅOy¶”‹y2ï.§›ÏµÎ ï!Šœ'Ó׳ŒäjÍ#­e¤AkÙ©uŒÖöO¶‘Ö$£mN¾¸•ðGYº³--oÛòdvû�­ó¾Íhm íCÌ<ßÇ¢æ0ËüvfÎaÌ4¿›Eç¸k*ŽgA=P ?Rý­Lôú;~}´O¿ÝàßîŒX&øøþøn¾.x†­¤¶*ºlrµJ‚üyÜ1ü¤-Ï{ˆëZ§ÿ^´i!Úôäø6å¶jÍ®Åìß“x y0æAÀL{§²O±vmt;Ýr^±á7ŽÅÐ<·Á+ùà•8Á+hs‘<tßuÆã5mFÛTýÿäcé4ç01ðÿ]eŠõñ‰kÐ¥F„'8ݙɊð"B?ì·„UøF¼aIExïQHµá(cшVZ‹A¹1†ZüD2ÁJkÈYmÕìµþqÇ �ë,²[ÇVƒí4G—½H²þ†+µ`v³þ¨’Öwðµ&—êK_§´Y“â8frSù¤ ŒþëÒû¯-"½õ YÒeñÛ–ýDÕ*äã(ù£‡kóA£E;T+;öXÕ>×:ëmÕ·´§qúªÈ!í޾˜ÛÝÊ÷Ñìˆî!’Ç¢;šÀÓ¨×D|ªØˆ?Mœ?iÌÎ4ÞÏÍ“|[Ú ¾EMò-õVxíO‰‡0®î¾•6P™ÔŽPyË©¼|Ç–Óz—«Eв^>ÎËŠ¿!™ýÍS×áÐfàp´$ó¤Ë,‡¼ß꓇²mÎú%?’ilÍÕNt>Ñ„jØÈ“ˆçü?š5ˆgZ@ßë—þq'Ê)Íè·DšúÇ(nÔz+øOeq¸Üº.N‡ëx@žÔÕÛÜÚHë­ò"Õ§ç-¹UÜS½È—:¼È梼?šasOµn¥òaØuÃo¥ÎëxhXÿ°ãåŸ0.«ÔE¥OjëQYl=¥ôqªýü’ˆú ®osý–°MAu»Þ ý^_ìÍø{ËþÀ"[‘?‰É$¨Mó·Œ~n ñÕk¼o*3^_ ONÿÓ2+¢ž\êûÅL&Q=Á„óàˆ4÷i"¾¿òŸ%“^ý)hÿãp{ù/¿ ·T‡Àí•}þyáåŸ+sn¹¿}×r¢@ûò²nO¹ñ[˜Û5\†ß,÷Ž/ùÛ,ûëË_—]¿eÕÚçÀA5h ùóe7ÒØ‘¦EøGÚY¤=H{ñÛÁ÷’J0H¤/wÎÏ¥¼£Ku»Ôãûša—Æ–±o”Ëå¥ÏÔ”—®ñWoÞ´v³gƒ¾n rÌg¬3æ~ ù,oÀw½Eu’é=?*O<+#Ýï!¹Û®…ð7¼Ðr‚æ®ðÛž»Ã´Â¯Zº]h£\zû€«>UÉ=Ž<ö—|=“òÜ´ÚÜ­¦ÔÀ|د1 Ê8èm( &ÙòrÎ2›8—ä¯w’mÙ8ÙÈ&ÊÕùW%³åî0ÛoÍ µ²mæË7ô÷&¡á°×V�<÷é)àN6ò¢î"*SìA5µO±¬ C!Ÿ±%Éö4Ù×3ø>Öç`'û doÓ>xÄ­@ÑüaüVsØ!)ø]éÞab¯{m[^œ8Ûùžû„^’.ø`zgõñµo®íÌ¢´Îtx?Eû?ÿyc’ÊѨ½«XL½¾/½ÕÝfRÄ:bS ~-î.íïµ7-¶mv݃~ÔžîÈlBÛº¹—Åq1»ðî¯÷#À[)àõþú3G£×ö´nw-Û]¦×÷[Äsží’.ÿ¸®/ׄ«à½–vq–)Å÷`£µ}l¯°}vŠÆ<óß÷Ÿ[B>�÷#oâ4Šõ¡уð ›uD\µ×ÚXÎýõ ö£4'·mö£”ÊÛ|€}ëÒl´¡šÃd+ÔËYuîb#p—Æ9稳wv¯<Bû;h?ö€ò‹ ™h 8A/-eÚu9qTÈ Már¢‹µ“œ@Ÿ¯æòz“ÐŽ ”±|Kû›*Ü;®^l2£ì³,Žïñ]ÂLµÖmÇÏÓþáÃÇàßYT<Öæ/;†tQ4O4Ñ4çW[ŸÀÌ÷ #] ò]ÐÓ^ t|ÿp’MFÿ»6Þv!Æhý¹=·‹öoößFýUì‘¿ö ÚyIȪ±n:ÚIûÍõ=è´W²F‡sŽ€óÚBjœ×z„¯}í¬¨ëÚAQÞµýb¯¸Õ½ƒ%âYôbñdãC^&\ÒX{ÈG1Æl,ìëÏ|¯x ä_yñ–ZúfÓæ_Nr°Rì· œ¯eÝ!öœˆõ@×¢qw«¥=g€I/ ’kºvmU\4íú7>§ÏÇtþåÓyõË,a’3 ̇Âc7–ôß„ƒûÚu¿±‘ÒŠ`kF€Ïhƒi[€à@A>+¤„Õm”_²ùú@ñ‘ß&M·jò¼<@ëÅdýç†èþÿä ÿ熨  qTÙ¾ñMüïð€ûTÈL±ANJ?üŒ>¶j¦±Õ•$S-2[vŒÎB@š(1®o–‘6šÎO ³ÄØ*O:7!ß,g¼ÁçvŽ­JJɆ¾Q+wähL.`‰4×àí¢ùøÑÆÌwHÎŽ¶f/çëÑ™jr¾Ãœ5º’M|L“Îk»Ñq’s|ït`‰Âscƒ€‹ž•½âÙLúfgø¹¬i9“ðlRóù]«Œw3ÓÛdÍ¢¨­™­,©n0)…ÎÌ ÙÈg‰¹;¢Vd ÞÝf¹Œï³rwD/Àï+ˆß_§{ÇHš_í»Üù*ô´%ÖÓQäïú#Ê.l)þý1ÒHú#YÞËñ‰uÑ´o$;vzU„[8ÏÛºR®Õít±GÛçm9P }Ò™ÙjÙOmÈm³¤¢]/åvŽd`ÓÖù’Rj×Ýu.ðP¿ËÒ•¸ŸæneoÛïM Úð×Ev¡Ÿ†¯ÒùH …ÆŽ�O ÷¹ô6¹¯o“j])˃z›¬Àa£e?lÜ–ÌFjÃð¾ÀBKw-CÒц™zlnÞõÞM©hCÓ¸6ÈÔµ’¯KÙM0hÕœé0ÒÚ?ÔQ„ôTGÁ)Ç‚Ÿ¾[˜^[¡ÃZÅô:LŽF+Ç5swi©€—ê Ú’’ˆr÷óry™¦ (3x™Ú-ÇE¶»(]MÓËTQfœ^&µ»‰Ê vS™‚®^Ì^­ 3€ô÷³fÁ“A­X§çül4øøŽúíÃ¥ô*–hä¡õc¹]Wèy÷e¾Á$wgpEN«öq¶W£~õ¨/z‘êó{ð5tö•ÇûZz¾C]÷¸Ãoc‰ôMÀh\þºÖ„µòwo«‘ó7–ÐÕüytõ•ˠסçÍûôgè~óQ1vl>«Çù®"OÂz„í-h¯ÉÝy¥•æFi}9Ú|ŸÍ•Ps ;™U×ZÑÖ|„*´×ö l‡~+KT+Ñî^–èb‰„Ï`K "Ä1ØŸ£íWø:vÂ…Q&á3Tím¦><þXQÎpl¨ŒÕt~ òj”ÏÛ^ÄçîC_^k òdáÆÌD½_ö�÷c„s·ëÀrQG62 èüe»Ag=^§ÿ—‘2‘Ëç¸üíÂÿ;* ŸDĽÈ}z¦÷Kÿ„~iuÏãgéøõ~ÉûÒHQ¶uô0áüS–Ó:zXÈŽÑ\ÓGŸ&þG³àù!ñ|u7äH4ìÝ üÞíÞñÅ€š7ö¬û0o­ƒ@™–q>ÔŽ/qd/3 äüN´¹Z´y¸_Íg'ôç÷ôñ€~}|àDc,cMë˜Ô”.æþ™™ë¨Xø�°åiîߣh½Òp )ŸÅÀÏ"Û:ŠÏû!=¥#ý¤×'è Ú ŸS:‘Ag6µ±ò7ñÞ#dÏ嫼±hÇ:½½hù$ƒº/RµŒÚÅýŽuhÇ1½ÝfYþDø$!_¤Jo ÚQ û]4빞fÊ' YO˜fjþ|ôõ ÍcIh[I—YÁf…Ö¶p¸‚Û‰§õg¤góôçõj¾Åx.Qó£çêÏ«~x΃Œ§<5µùïoÕãæzß2ì¯Ï<¨ºÙ}äkcˆŸNêiì´ÖE¶¨ëÌ/s™'ñ50Ú•Kªû!=í• Ð.ý¹ß([”{å^9ªïCD}WöOûŠk‰loe>“ôr};9^®È â·:äG…Ö9ßædvcL nTiUñ¦jc<)ªšæ+•¿ó›4ޤÔN“iþÒ›Ò'‡æ0#Ï_<Ÿ&yøg.õÏ_ÜÆÇ—¤(—l»‹Îª³7JQé:"tªX+mÙí¿ºE¤¡ý¬ånË)_‚ÍÍÇ¥²1ójýÜ-ðÀqß+-‘.ÔùŸ±¹øZ c ©R÷‰ðå•ía_^Ù6ÓáÚÉd:‹®@ûô׃ÿÀøùYšs¾`ÖœS–~ïÂ>¹þìßË9µQëràûϯú’C?‰ÊmcЗdvWã$sýëéxa¾M‚ö]Š9ÿD¿W þ1mô~û74Ö•Ü8]÷™»4Ëët&„ÊAÐÙ°°/íü¬ØVêÚêÆÛ!‡LÍlK·¾§ÝÂÏõ®} ºŸ¿Ã[3ºÏßÍiþöæl©MÅ|Ÿ4Ú5¶žÚ¥ÎÏw /¼QÛÆhÏ{Ù¿—8¿Ä_Þ»úÚ˜ÀÙšhËXf“e?7Í×ÙØF“ì&=¤®ã6Ê@&ãkRÛùÚ™4ÈèΣqœÿj¸¾)Éi…,/áç‰A×ãuÇRÝÚtª›`¥õ„g’$ûH¹Çä ³é;áŸo{.®A§Œñ²=¨öF ï]×6ØR°¿®Ù©µI)T·hããåμŸÂ&²¢®D#Ú×”†v û1EÈð«Ð^ÿ0ÿ\=ú:?[plµÐƒš?ðç¶y„·Ü6)^ÔwµÝ¨úÆÄ:‡ x)ëÌíbv‚-Õû8·Éèõ¯zœãÈÏq‡¦Eò ðà£ø¦gY.É€@¶‰Ê®i¼;ên1Å*hwƒÓŸ‹÷T¤/Dúæ,ÆRùù—à¡W³-§ƒw3û¿‡—…<ÚAk_h¥àéá Pkinš_†Û¶£¾@r”kt#z­[ ÛˆŽˆ£ó¡²B27λŠZÁ¾ñÌ÷ŠŸ©.£a«ÒMéDÿâûYÄYñ;f…Çð½|/olŠ E¶ŠŒ½!¾¶_Ëì¾ ÞLß .É.)üBtdyœŽmÌðÂßpFø<Åäóä »}1§áo¸õs^‹ ´£É×0´®dæ#ÙL8 &YZ&žiAûù(žÒMv¾lLFß ¸®ãÆZ‹u‰ÝqnBûÒ9NŒõ°ö÷m£=×_œ*åçn®Ê} $Ûºõ5N®qgâuñ³ZÇ ß4†piæ°7B6øèÌZsÚWÒ/½Ãçú[Úg´µÞÉÞŠ32•9 »7Šë.»‡ðèŸ>¡Nu®×IåÐìTå§r2“PÆmiÎ�lÅäX'áæm¦°w=×eàQr²iÅlü^寲âooÚ\Z^¦¿Vx¶”Ëൈsi"yx¤‹*"ð—Gø+½ÀLAq6‰¤l‡½Npu¢ýoï~.O†Sä]_ã-$»žÎ¯çóî¯×î?ÿ~áИ¥;°ÈÒL8©ÓnðßKc%y…JÒˆìÕXMöþÖ:í®2à§‚ÎÛ~ì7Z—V ^FmáÆâ1?»×ÀÍÞÊ-åËäÒòêšÊM ì„pÓ¦¯íòEôÍ® qÿ�ì#àé}±.0–å±;ï&;gs³@r¬8§ ÉÒM´^ŒþÀ}• ‰3ü¥Ëä;Ó¼£ð¿¥~ô)šÇ”Zl|\Á‚_z6!˜è= !!ÁŠ`CˆEˆCˆG¸ ák·#Øî@ø:Â7 w"Ü…ˆp7Â=Ó¦#Ü‹0! Á‰Œ0aB Âl„ûþB*BB:B„?Cø&B&BÂýßBx�a.Ÿ#ÌCø B6Â|„„ !,Fp#ä"<ˆ°äÕål)Â2„o#|!a9B>BÁ«•lÂ_"üÂ_#"<„ð0ÂJ„U ü £E!<ŽðÂj„ï"<‰PŒP‚àAx ¡¡ ¡a ÂZ„ „J„uUëÑÎ 6!T#<M¾•.Kã ´®Ÿó5và‰Ñ¬zœ›d9¥ŸGKç~KÍ’í ý%[ñ¥!þÁ{{v¹|ô·$_c³nÄûaž7ÎR1us¹J×kDhœqg²EœûÁ×%^gÏk¹~KÈ>·7¥XN }!Éèȉ!ð4͉æüÒò¯ÞE}rÎë–…c/Þ yñ\0Ÿ¯ôz0l’§‹ùZªßS’¨Ï¹rù[ u>ÏeÈîñi¼»é×ÞØEúyßϨÏÁž²f¾ci$ vï+þ{„]èÞÊVùïgêû1KneRö±™AȦf ±y_×ÚÒxàæöˆÖAs°T.•z;üÓ`¿d“ýz×9ÿ4nÙ›ê^Œ%:„Í ¥*qf™Îß <gi¦ör,'”8VÈÛ›s*g»d§ð³Ø^lzŒI:ÈÌRÙcïQݰ=d²E^Íá|%§ñ´ëC|šG“.1ç 8ë×G#™Û-ûùù™â,“—¸½v4ä~ævnS¤fÏ4Žš¡–Ò¦ó§À»C§cVö«N¢#Ùîà™~Ÿå6âmÀEkxi|Û8‹Þ«ÄZã ÷„äF¬×nd·òû;å5ßy˜¯÷—r\NXãqEÓ×"ò,ùvîTàó›¡�ñ’ùl•·'¥¨³œé´ @û凟:kfȆâ÷ôè°QZeWrz$ŒÁ½7‚Ñè¯ìÅ0Œ+#al¹)Œ‘xY^(ò�ÊCyiÍnó¸sSÇ­ƒŸÎî—ùÆ5›xÁm“Õkš¤Þ•ázK-í7mkRD]+<×>ãY[¾²º¼´rMeéRq7•^ÎÁ›–sgD9—¯…_YãÙ\ÃÇ0J5-túq˰‘¼Ðï½!y`õÃNGùÍ“Ó#TÏ‚°Íðmü·l#‘S¹qí·ŸÙXJ¦ƒÑÖýSn+Áøpùúaqèy;¦œw:©ž×«1la‚—ä ±þ'˜|é%>öè!øoãçÕ’2z8ÊJ6 Å¥¨,WCÔ@vƸchŸOºÝ©³´CÿüUvr”Ÿ¯-.6ý(Û+Îw ³óag§ljõÅ´ÖøA7›½g¦±šÖXÈxºŒ ýÁoÓ‚”&Hi«Ä™x~év+é7è=Øq·Û÷'™@‡Ûóðû2üN7âñ^ÄõbòíÕ“ùF<én'Oø¢¥)Â6¾£xâÒ±?ïÆ<îK邱¼M›ª'ð)áÿÔá_PºÓyê\Bãi)¡ùtrBËéÜ„öÓž„ŽÓ ûOïL8xúpB÷és =g¤„g’úÏä& œñ$øÏ4$\:³3!xæðì̹;­Ã‡—†Ï9‚#RIN°Žä&ØG< ‰# Α ©#‡²FÎ%¸F¥÷hrBÞhnB¡ZAþQBÑhCBÉè΄ŠÑà գçä Çq"Øàèît ;üªÝ×�ùº¡ÊŸ›Çýfk0÷N—Òj¸ÓNüóRÉ.ñÂåkÄÄÄ_Úže%‚æ…îפ¼@²£]÷ æNÁ·Š~•”ˆ¼ñ¢Ü¸ÂíÓ;è—Þ ´S u¿J&?ÊuiÀCÜ'ZÌõë‹D_ß÷× h'ÑÙöû§pú§p€Ã�p8‡ƒÀá p8‡ƒÀá p8‡CÀáð7ü CÀßðwt=šö‚¦½ i/hÚ šö‚¦½ i/hÚ šö¦} ihÚšö¦} ihÚšº’ï´«[%§’ü°Cmœª¯ÁI|ž¥¹Ø9~êÀq 9оINŽA±¼Âà –(ð³ëׄÿ°¿ôY®á³Bïûù¹Ôœn_þÙú6Åû=QŒÖ@ÃÏšM羓Î9?*Xzæ’—¿nÈÓÁ°|ï“?Zðx< Jñí§ÎÅ[OKñöÓÉñ‰§sã§=ñ©§â³NïŒw>ï>}.>ïŒ_x&9¾èLn|ÉO|Å™†øê3;ãå3‡ã}gÎÅ7Ž«>'Hq¾‘䏯‘ܸæO\ËHC\ûÈÎ¸Ž‘ÃqûGÎÅ•âºG“ãzFsãNëmˆƒ]»4vi\0è‰+ 6ÄwÆ•ÇULÎãñÉo#>'¼¼ÔßB¾áËÕß \Það¹Àõ›®Gß¾÷èoAŽó?Ôòs¾„_ûžøh ²8Æô£-~´Å¶øÑ–�à �ÎAÀ98ç àœƒ€spÎ!À98‡�ç`ŒC€q0ÆãÀýqà½xïÞ{÷^à½xïÞ{÷^à½xïÞû€÷>à½xïÞû€÷>àÝ•ß<†yçóO‡ÎwêuÞ¹@køFÞÞÏáóÆ·Ó\aÞ ùvæ˜l¬ô÷ÞÑù†x…xGàêrž!YÆåcûlâ’à©XJs¢ÿö·:Ï8 ´ýG2±@û?ÆÏ%áþ³g®ÇýðÂ;áŸøŒøøŒóÛÿOymÙãÛæ—oyfCù¾ñ›!Ãuy£óÂÚ•:쿆ìà¶CHŽ^<Îe¨Þ/ ´6~'¥Ñ ´Ál½Äh·‡ÏÝ…øÿ“ðÿ—²gìÄ$¼“ÈîÏ;Å›6㩦bó¦ï‰»bÊBçq¾Dç<ë6 ¿+•ßÅyžkäá|ÓI|‚ì®°M¸¾¼&¬4™/$¾ ºOÖªOÓäÓ’æ;¬5žÎÕšO{´–Ó ZûéZÇéÃÚþÓç´ƒg$­ûL²Ös¾ÿøýgà÷Ÿß6íسÇdžÏùG¤±K#Éc´·”x4ëHƒfÙ©%ŽÖœ#ç´ÔQIËMÖ\£¹š{Ô£å6h…£;µ"ø%ú~æ Á—àÓä?4’Mf÷hCQtwìú’퓧ôüßmdK(ê´ZzÏlãcÝ4®;2¼bï!?/´8Âö»—EqK/¾qGN;3»’í|~€ÆÔÄ3À�|ƒ€oð ¾AÀ7øß à|C€oð ¾!À7ø†�lÿØýÇïãÀw/ðÝ |÷ß½Àw/ðÝ |÷ß½ÀwðÝ|÷ß}ÀwðÝ|÷ß}À7ñûä²+äK|ö ô”VÕlö”–³È¿<F3,òn ‹ÞP¾aKy~&ˆä·0k`6³güÑyK‘g…¸J4 ºÂ¸³Ë1NÅÖ”•?³Ñج\ºiCµgs9?ûpÕGÖä òuÑ;\ŒÏÙÓÓ÷ôñ¾aýÌZã¼ßw2dšÓ÷~s€Ïë‡ïÕÍu·²÷”~ºÃ†UxßøÃûáçrÝ=I)þiàõ7N¤×Ÿÿ•ì?ÄØ£]Öļ^ðƒ÷à¨Cßz"bÞãîKdC1ªåÖ(I'ÒéîuñûéÁ铯²’œ.&y_õ³º³I)t—¡›î®˜Ac]tW\RŠòK§oT?Õ­¼1Ní ù!OŒó?è¬v7µ™ÎŒ7Æ�oåþÔºChÊ/ûp…,°…î«Z;&<ʲMßÛXQî©æc«(Ó¸Ÿjô%\×- >H¸­;ΜDƒœ€¦žáóˆs¢Ð¾f=Y{ˆî§T^›%ç<~¢ñsé]^WÌïñK…´¿Â[zFæk§–þFÜ s„ÖZÚ”#4þÆ(»Uë¯Kƒ¯z¨ׯ s~ËÌu¦?sxÓz ý÷¢ŽOFåÀ"m˜—‰²‡"eÓ½ÐT¾X'qm¯rw¸üÜ6­ÇØ{Oxäøü \†ðg õ=ÝÀán˦Í5´V4r|2»zUiL=âiL¢ÎGÁ3±Ÿ…_‹°­~üAow—vÙáèKÖqcŒ"Î2Iœi’8vð K #÷‹7­\@ø ‰ˆûàÿRJb¬²ÅÐ9×ú™oµñýO1_mÚjFþÛ´®/â¾:3‚ú>O÷»OvfÄ\q¿?C~nDPÓ2q^Ý W›¿íXS3Ó~6|ÓÏ‹X)#¾¦YœA{GÔª%²¸sާÕÏ‹X)ÓÙÙå!ýé\Åø^; %{é̈™‘gFøŒ3#h¼wÜý|hS¶8#â ¡?ïÛ)žiýq;ÇÏ‹€ÄlŠX_[›tüH-ýAëkÓÂëkéÌù½áü%tÖŸqÞDSžØÉïù*FþÈÿ‚8o"´'òqvHíwÄÙ!t¨Z€4€YãgDÆóÙÙ„“ª˜&ZSAãY'ÁÌm¢Ý´ßeìbˆ_Bçm›,S9["ò¼W)³ÑøeK|Œï£KbÖìR&Ñž9Õû¸#;ƒÙrÛ$W“—Ùø<Ç ´g þª>¦‰{º0»’Iêò˜§¦u¿)“Åç¶Ò]€ Ï¡ïùøþC’¥ÚEߣuƒ´ïНËÎÀ÷ŒZƒ\ÃçG†æ7]{¡öfŒ‹1‰ïszØÝŦÓ]>n؈“hß•ØWÇ\´<“¾íÐ.Ð/c—\]"ÖP^ÝIyh/k‡v‚`¦´×ݳ¡ã¸s0„ Ž3MÌËŒÀ{¬˜Ó; xâ@Ã}a†îŸmföµå5Å¥•傌•׊=häÕÜU¥è#Ûø}î ty•ÅïÅÜFëÜI^½¹3$¯v0÷d2‡Ç•Ÿ/àqåããP†=·‹îyó/ÇÍ{‡m…fvñXñšÍ›6ozj]yiÍ7+·lö|óÏu{•ôí}4ÖYÐý<ÆÝ—4–Lô›ôc  >_ø“!¦/fIt ݉š˜æ0iþ.f„ÙëcŽöÛkO:½>?«}™ÿ\Àÿ?‡0'¿Ÿáf%Z‹û‘G~&~‡txÆ.øm°*“RêyóèþÚ×§f°lZEöÝN6xÜJvEÃb>NmQ’ò6ês2l×h>7Ø¥]˜ÿ³ÔûÚ ½¿jgu]ÑNÚ³W¿m­SßÀé«N£6ºåúh~v@ýîŸÈõ¿þ{ã®'Í£Ò™ ~ßÛÞMc–)uÇ;þ‰à!øh¬/mý¸í;«O¦3Rø¥[<'%Dßø-ºŸÝ]ºÞ³eKåšZ¼Å›ž-ß\ q §/ÑKùô9^Ô‘cs½9ð‹øþà”¾ð]EDs؃|½gÔ‘‰û†ï:›è£'õûº¯EÑšH‚_àU²mGû6æw3 tΞ»5Saܣ̦ÃﲺâhMqR Éxo@JFÜ$´ÚI«ÀBæ¬ÍH¢¹hWŽ—Y¼%ý…ÞCý¼|²]À/¿ xÈŸ¤öCn8AÇïz•32o'ß»ª­¯;=ž×xÞà2ñ{E¥5“!^+¯Iœç2k?¤Äåe»ßÔæNl+è^Ÿà6&ðÇé{«t ÙFÌê)+d¼îŽŽÈ3‡ ½At<$‡r½Á¬CAu­ Õ6~Çc®_1îgóó13YÄ_yÈïE¿£¸Nf•KŸt 9{%›úù)€Y¦³×teŽî£ìÿª»5tÿ¥?[TH&bÓÈ¡©´õaqòbXˆoçŸ<a¾µr?怸¬ÿý¯Æó-dç[ËWñíÈ_NàÛ¥øvñm`1³ß¡~y8™|X›ï)äþZ+û`^½zk¼¼ÚJ4!X¯ÿl¯ÊxUy¿x•¯O¢ýë WÝ‘äýâyñírçu²òxHVÊ\V¾©-弬Ãa®Á»4ns«ükä5æãþü‰}}}åÆrÏæâ-ø¯´‚÷„Ð=,J˜æÄ#ð÷§zæVX_}:´:ô—dŸ˜/Ö=b‚hº˜óÏÌÄ}$~ŽÁí|-$e=̧Ô.0'’n…Ni¾öaDšFš_¦oþéÌ)ÖÍDù‡Î²Dš#ËÜ#d!õ=±—kăxÅC~\D\æžÆîw üŸS|TP”•tν5êü¦Ô¦ þ�åîÎyó çêÌüå‰Ú?8òögü¹™lá¡™¬}þqÈ×ÿ·‘ͯcÖúͯË|Î*–8z’ÙrÌNkÛQŸ/ç¸÷ð »zè.óš÷óöó³Sà‡Û½gXzý¨”Nßù ­m,þ×`—t…çéÞYºv¼/®¬?þ÷2Õk´)çq¾6¦‘lþú4ó[ÔfÔétÙ˜) Ó½?I)j”Ngtä$Šyfÿ7€¯vޝ³®ØˆtuH—¯§cÚÙ�èåßɇ“á'ÄX‹?Žî³19^-íüþÀÝ´¾óÒrà¾qð0ó ^ÿ—Ð=óÿ3 ý ³>÷sô¡o;ž &¥¿ ý¹6<ÿŠFç¬Î9­ãí_>sÐÜ8Ñ‚îmTfN¯õ>wFái‡6ìÝÇï“ÊyŽÎ*¼®Ú´Cê_œtP;Ÿwž”ž“¨·x#0Y©ÍOðNÐ6K‹¯å~%ù-ÓYb¶8ìD:K™x¨Öül"'­•­sošæÒýÙtŸ&­¹÷×›¼Ûê~C¬Ã2¥]ñÅ?gîÒ4²ÍÀÆ ÌdûùzG”GuкW:“ÂÛ$]±bh5dî“,ʽ•5ëuuBº¶P]„¯¼Ûôõ^‰œ¿çóOäbmîVí‚+Yâë=©¯ ¢Ÿs¹‚¾þÇž5Wçæ2ùÄ ú$µ—èËe@—Ö2ÿ³òsöZÑ/Î19G¢õð_¹¬Ìwxßat¦euNø÷'ÈÆôyŸëuÔÕtMÓéô/,ôvÕC7ð{%>›-×D;IOÑx ·¹}ùù¯]]àÇ2Èÿ¥Çýá°!×½ÑGäº]$×ÿðw$·©n÷!M&~¢ºÑŸ©ýtNqÝr!€ëÿUø ïÐØ"[®UÓM„k~o†š^ #®‡öNÖ¦„g¢=}Þw‹Òõà+êçïQù¨Uò;^r)tG ôù~ÿ6šÛúü8ñ‚ò äa×µËÏí5 yžÇj¦"ž FýÙxÏü9ÑøÚYÚÃW÷ø“Î!ðŒ 6ºëê[B{;>ûùЫÀ?`¢r3MÆ,uãä~ÄxzÈœÄk×rTÐãP?ß|Éû«ß9ê:A{uzœ‚ÌÎ Ÿ„oQ׿=fà™ãþÙóÿ¶Œl*ÓÝ©µ{‘\Ðö‘/²ÆßÉŸW<¤·a—A»�“â?$¾ ¹~¹ qsñö–Ðrõ/ìg„9©¼Ñõ†ì©žè>tu¬_3ýÓèZÇ|õ@öJí8ŸîvÃæ¾ÌnÓ•¢°ø¹½”Â,‘ëåIGú-,šô]øš¯*É+äzsÇ,1Ö[Ÿx<Ö ×ÁÎx^´IØŸÎ&{ƒø“ãÇýŸd²q\^ƒ·ÿ°œûEF>n_]ú?d«ð|!?îbà?О¼Èî!{rC¥\^v9I÷ј;ú¼Û'ÎáºK—a=\†í†¯h¡3å5-xò˜Ö¶”±D:Cñ©Á¦›žá]Ȧo._[¹¥†</>¤Y¹qÍ&>ºóE´aiD\ãÚ€úxÜâbDøHT·ux)ÿµ£-7hGh½Ò*–0i;®_¢qÉP;ºØRÐ +E'èçØöP»hmô<|êåâþ½løDA3d=ò —¢]¥hÓ´©RŒýM Ñ)|oi ³Ol­K+ã€ÖtØÍÏÝÎâ´8Êág:Ž8n@‹,yrZÔxž‚³!(2i:Þ©Ž€€)Kǽ5ÿ7À½ûF<Àëe“ŒñçD‡<fi ã½x€ÚdàðJ8çç\ÿ„wÂ3Ñ€ðIƒëlé0Þ+Ø×µ7‹øòºñê­ìh¤v ðþŽa0wöb–:¤ÁVyGœA™èmO$›ÌgšGõñuyT®mü˜Ê;Q^*¼° âÈvàë¼K&”?e:¹_uHë7ê"}™ŽìÁíÿ^¦ÏãúÆ­ŒàKjo“—Û:Ë#ÓOL{=.%'»·¸¸¬üƽÝXS½¾»,<wÏîºA~GŒ±Äˆþ ù<öX3µMqŽz.ûúõuDÎMÁ/8Azaèâ®Ò™úÝi´æ‹ÉWÒ÷�p »‹ì²Û¤éü½}€î°u‚‡zŠõxç÷ÉC°ƒ)þµ iSù]ÖÛÏÓºtè¿Ï²ÙÈ_ð±öO_Sø.¿ßœñ3;¹}èäï°c +°URi¨‹ß¢ñ3†²ÔÑŸp_¹>·•æ´†¡ßÎàÏÜo’Ù“NØF­Š9怬D;]èmÐiÜ®\8N_ñ¸ÅøG2ã{yȦDyö©¬Ñ7à„þ|„ö‹å¾9véfí‚»~¼Nýä :ÿÚ˜¯ ñðö.„x!¼ÖÿdnÄæâo/]ݲ•5NåŽO¤kžbºŽ©¤›|½­Þ/¾Q\¼v£§Œ»ÆóÌúºØ´Š±¬ÑÕó$LÈóÌFž‹ò@Î)ÉFÈmë äv"sèeû!¨„-ì&wwð?;õ±µ5|bÇSZSùly1Ÿÿ¥zCkOÒ?Õz“¯˜o\Êۨ׵ᙚrYGÎ$õYÿ$õÝ9±>Ø©Ïo6uŠùM‰ý÷üæÏo‚o$cì.ÇÇÌÜÓסüöð]ÞbÝɤoÐ6~¶<Éã›ù²O;ŸCû޶B.Ÿ§{yþ±(b´f)÷Mí£êû>ìÂùÇôÆê^hƒ¯ßɤÌV~–äœG}¡31—æ¶™ÎÄœ+Î$7¹·¹ú/§Ï]j-Û<‡Bg™NçÒ^ÿ?ª*;†=¬§uò1 åüŒlÐ&%œ¿¶ô­}+óæ),â<Ûé&ÁÜuh}§&à«#~¹÷Ý`jÁÃ~–ùº8'óÑð9™Á…ú9™‰ùv|­Çaò]¯ÞKù¸Mp}Þ£È;W?ÇÃ/ðÁVègAúõ3/÷ÓY^´'°@{þçú¹’ôý¬þý=ý¬È D—í½NN/è߈vМÅßNhGJD;ªÑŽ ãÏÛÑ^™� ©û?o³”ß+nÌE_5Ò ìä÷+(C ¬Ãë#½=vT/o©qF&ž]ú¾%¢Í}ß5ÚLéõ¸åá8Í%xí½{C°ÑýçÛx»šižŠÆƒ ´k‡Bct§»L4$˜QîoÕ:ÞRÞÆöéõ=Q_¼÷tD»æèq¯FÄ%^ŸwìEî÷H¬Îõß{Õˆïûõ<EĵêmKm»ê×Û¦÷±Þ@DÛ¬z\ZDþËÔgÁkÓÉ΢g1or5~h'kö¶&ä¾y•æÿÍ4Þ"ò÷ð½(âüv¾6�2¶ç~#NŒãç4@y1 }wHVE…¹.---®.ß¼eÓFÏúÊšÚâg³Â¶·nó6Ðþ§$««ûûÓ÷©\×b!/'ÙkäñI\º˜ v·0¹“nTföù‡S,o.‹ã–¼^âuí|š—i’¡ïet?yB|®:Ù}¤ic•i!iÑÎ,:”&.æ�Ok–_Z'æcÑËç &ñ±µ¥t~uCÙø¥gcß*å ¤ë¶=¯ƒL3¿EÏþü°XŒ|&)UŒñhh=‰kóWÀú›^üý`îŠââo//^_ùÔº•e•[ª=5¥Åž¤)ÁžÅï�Š€mæÏ€Ã§ã§=wV$ÜTÙOC6l©aüÞ"Ü.wë-ÁÝ ÷Ò¯†›óúêðøg‹ •½tùõü£—5¶÷VËZJë·Jî©ùRÞTÒ)>v_ó6©BiDg!ý9àý¼T\;~Ÿ·x¡SÙÈÏGy‰Ï–ˆuBðIo3l\Šóÿl–Ø2ÿÄÜ!XâKÜz]«Yô–òšuª‡w1×ðæ>ÏòF̬pd+YÂJFJYÅÈ6V=²‹É#G˜oä<k5³æÑY¬et k-e£ÛØþÑ]ìàèÖ=zžõLRO!‹E%[<Ï–¯­Þ|ê;qê<ë?mf§g1ÿé%ìÒéR<½Mb§wIÖÓG$ûéóRâ³ä<3KJ=³DÊ:S*¹Îl“ÜgvIygŽH…gÎKE“Ô³"TÏšêÍÀwá”ð½iš¬üO>üðºÁÀŸz¾NVn‘ý»àˆ … ² ïÏI/u˜ßòí½v58•m³èNBÚw/MŠï˜õ›6®E盢)ñ è�]A—AÐet]A—AÐet]†@—!Ðet]†@—!Ðetšœ._›Ë·Ô€.ÇA—ã K/èÒ ºô‚.½ K/èÒ ºô‚.½ KèÒºô.} KèÒºô.}7£ Õ#èR2ÅþR1ÅtÕSL'O1oŠé¦8~ Mqü@j™bºö)ñµ>Ö¥¼:‹¯Q~™!GÎýçs%42ô¦ÔA÷†‘<$[>_ÖÍ|i¥Žæ'¥ÂÀ"Ý&icY_™Gáy¦SW'Ë£|4‹Æ‹\²Š›æ-§¼l€×‡ôzÞÆ¯®íÓëkAûi·Ç¼eg䛿UyÞj>ªðñóào–O?³ã ‘/ãüªô.qÏBQ8tÑMòD¤»)ü\7,Öç–¦0~6VÉÏmöûú8ž¼ßù|£z"î”óøbB{$k˜½Â³±ŒÖ8›„’Â篌äˆýgÇÝ!§ß=h¬{2Îe¡qXnïlgA/»D¼ ø{nŠ'¤#z!}5ÙÝäÏ“oOtü*š¸wD­oœÁï#zTf¡uHðWhlÀ\[•”çŠc3d6pAf÷} —õ„š,­s%±KÙd7-ˆ“ƒÓÙÓ®¬Ù×É,.…%/àk2íòž¤”m ÷€ój1þbf¾{™~†ªérŽ“™iîµ®à®25­è›MÌ‘ÛfÚxJÚ_›vWãõcJ¦ ÆÌï!ìçî[òDÜT‚%ù.áf²©QÿÍòþºÑƧ¤ƒ„o‹¤E•ŒÛûþ“ë÷¾tn¦Ž;òo= X[”_²t*Siø)•ŸÇÊ…ó|ÌeÍ4™âh #ÕGþ?꫾­¨.¯oÀ]× G§Mkåù‰g»X3oóèÃi’ý¦}½^Ô+xê«û+hµ/œžÝ\ŽŒ+ûæý•·ýןËÔføÈ E]–}…ÌÞnãëÇi>$Ñ¿WøùÂ'ïý[Âúy÷­ìG Ô³Š:'h²6Ë�Ê›ÎÒëÜI)Ê ,]ÙËÒi½€ ÏZ~GXPfœ?sg’×wÂMëŒëgκlæt·±¥ÄÇtï)•Éá@y”–Òù[@ÿl²_ç>Y•Ce€nG}3˜,Îßµ\EY©´ÎƒÖñ1dšãþ”y»ëpÕ3—wÙodï÷i¼âI¾oýVèçÞaê úè\lw›)+Իƅú`ר~ZwAkšrêæ~w]`ík¡ôuU¢_f61³›Îõ™ÆÌÔ™%å© Xº¿‰ò.æcmT&Õ‰®GZ(Í7"q_¤=2HóÀ§Ò(twR]FRJPÒΩ«‹ÒéÌlúÖ ßwÁcéý?ÃÓí­Ñ´¥²E¹sïŸPn É7Ê£´ò~* 'kç(=­=åH¡ròØƒËÆç¿va²ú¨¿‡ë|ðŽ yöûŸ’è]Éÿ¦fà%ËUJ÷s~9›ËðÞww]³Þ2ŸÓ~®£Ÿ ¹R}e€Ö¹>züê73£‚“}«i×AfÎág�„}7•€´¯è¿p¤U èüÜ)˘çnQÆ ïóÅ)!c¤–q2fÇÕKãeÌ1Î'ÚÕ~—ÍxhMDô®ó<Òº~×ÕŠ t|ÀU*%h—¿+l„Ñœ^#XÅǨƒÏñ2·‡ãsŸ×ñøõ~¡¿%=Ϲˆ<…\F=)Í€HC´Ícß­·uN¸ÜE÷Gäu„ãH\v¼&øé~­ëäT² hÅ E™£ýmýµ!wô:šÂuŒî×ÛÎô¶ÏŽøöJ8ÏB[D¼"îCÓÎ |Ž&NŽÏÏoø+ Ù\Vc|϶‚Ý^êY¿þ):Öºrã³›ªŒ13ã,kn{½6+l{Ñü÷¡ nƒ-hg(•ìac'ík:Æ|d{¹ÞÛÃ_ië—°¹«Ø©zÅóóŠ{¡+:£iú•†:>®Ô\».æ~[€XÉSÅL/yY£~·ÕßazNªó«ä9ƒ¸wHÀÓ-úZJ¢}µ®¿¢?Й;v½ï´Ü´.±§öq¿×Êe:ý«É— u­tF³÷|­è½yìanßenÓÆÐžüjî.fÍ!~u2½Îö›ÖÙÉuþœ¼§ÞÔæ_,öÙLÅÞ§uÜÐK5øì›à³ÆíÒݶ÷5'‰u÷t>#hæ£{§€çù8®?‰=·í¦Ð¸"ž}ÞN66wË£§êƒI¬¹ªSÐ:5e}]S¥oÿV©ÃÿÚ¸]*,Ð.þÝ­ÂöQŒ¹;³•Ýê/ÆcÒ¸uö}1G÷´á+“?ò•<VÏìyìsÞ×ñ\T ýö}=¾B÷Ë`L×íåB¢ íåªk‡þÜ[lùc1‰4WwS[eP™^Ÿß]×ÉíªC¨«\¯«ÏéÏÕz½ñˆ[¦Çµà9›ïUçó ¿ý3¿bOå îâ áÔZs1‡Ý¶fsyy±XŽOÚ£ Þ_ û”+9ãÆ&B~à„3Ž çǵêásš<"i¾‰ûÆûˆc;¥‚.\VD÷ó³— |­¾"Ƭ•g§Õº·Fùyߢ>Ìy2ªè¦rMæc®û ´Oï9.f&>áëgø:ÂK{¹?³{H*bεî^n‘,º©Â÷+zßàéÇŸá|_Ñ]› k¦57ð+}hß Ž¸"﬘´w q õ¸²¼fÚ/ÿaš×Rg¾« ïü.ô—Ï2ª_?óùÃ(Þÿ`³ ½i©@|Åk Xûè/Ñ~ɬÌÝf˜ÛŽüÔùñ9;`ì =¸îФ>,å¯[ŒïŠíýŸfÉ÷-¶({Oü¿E³¬2¦;Uý3×Åÿsïeu& Ÿ™Ìä Ì� äž 7@šÔ¸P›ë„Ds3$!£Å6nÝÝØÕ6*`´ØÅVwÓ–~‹4¶¶Wº]»_ºÕšníîDÚockmÈüÏsÎyogÞwæÈú´ñ÷yÏyžç<·s?~÷¶}» ·ó�ž—àíM.?Y¸°ož‰mx^ÏAÌß—?R€íø]Ïï÷˜îk  §Ñ¶Í<軵Ó>ÌÐ8Ùôo$Ù}Ôò!Õÿž¤“hSUöV¼ÏyDÛ9¸÷ªf#û™ºÈÆp½6Û ã¤s4fu=]1kqcY!m+Ê�YEõc*›®o0œù?RËï8‡ñºŸ`ÛÌízÉZs¨ïwþ¯j¯€4>mˆÔq|îIë4ŽçT’L:‰}r×VK½´Æ` |Œ®}�?{ü ÷|’}tÍ9åír¤‡2R`yv1ƒ6 }G»@ýõ?GuNmãýÓ¤¶’´±;X{¾] §kMVÈj;%é·7å@ÊõŒ¶s(trt6“í²œ-ý»+ Ô‹í­§~òñ>¨ÀÖ὘¿S½zï%à»äEÆ7Y8ØKol íá^d´Å,%mÞ<¹Œ÷Ñ2ºNãÁõ‰Ð¾‹­Ó¸—މ]$µ›Þ&¸ÁrÐò“×åo?éPçt¾çIë“áµïH¶†k‚K¥þ³e÷ÍÜ?Öqÿ(*¿-ürá$ lÞ‰øÑ6C)– ˆ¸o¢žÒÎ…6W/qÝûiÿèIëÝÁh¶É4_&~Sơ׾p=óQ-öÅ´'nzµ•/GýA{ûQ©Ü•¤l1/3ιMvdvüÞ ’¦)É‚wg)v=Ó Ù0À73ýMôQ}m–ô7ÑÁß[ñàÐ3–˜—ÚýƒVâzÖ :X¸Œ÷f8èw^| ÏvǵÓ�ÿ[S+“Þ†}¾Ÿ ÷ÐE@Óð÷v8< ý72†Oþû$ü KïFæaÕï€ð­™ÆÐè)õ„÷f¨`!Û¡‚y9Ì©‚ sØRl€Ã’U°S véClœÃ&U°Z;«‚¹9ì”Ü&X”ÄÛIÓÐ/éöÐ ¤Vï—¨ÎÜV×ùØÆRÏI¸Ž&Äœ_@»<÷€å=ÿêè¹/! Ï!PÅG­éÆ^y½TN¬¶âÇ{­•6¼†pŒ\­žÄVõÉÚüÙÒ¹úRYÏý�ä¾<|?îCòò>ŒÓLÿ…®]¹VYKaplCÐ*óÖN–êò†.Ÿ'[ÖKï‘•(º•ÛÀgÏ„¨ý\kï~†´Jó7S–1¦íجÞx²v^BK‡ÒŽìÅÙf%“ô|‹=þîž&O·gï¯$ìWœ/×í/ëµU¥³!pN Ê8.ÉÊIÌ”“ÇÔ \¨|-Ø–Œ•Ú¥P$\¤²Âù»FË$—ï€)ù²<!¬«0mÿ˜Égûa ÿë?Ë„vó¨´6‰ÎI:ièg/²ýç¸&‘ÕA‰dæj(û÷ù¹÷/±sª/ܰÓü÷RÀyÛû™Ò|îc3=f²ç²œ6#K•ˆJ3æõ`õ+2µ’OYcj=|î ;› õU/ÏæºHF]@½àWäfMž»Ü¬æåfÙ¬’[Ö§,·ñ¹ÉíÒn.· &·K›UrsÄ'7TÉ-ϬÜÜÏXN#?Ø«xùâä…[•yß¹ù‰ÕßîcV¢Ê늯ÌÖQU™Ý¦må™p†ÊV*?e[ im%c“9[¹àà¶’Çl傺 5Ÿr&æV†÷ñ2d±2|¼[¥¿Zóú»T£*{ý§\öImÙ³\æÊ~¾ž—ÝÁÊ~¾Fe÷­RÙÑ£ñÂìþ¼Kn—6Z½lÍMì¶‹“dL%»À§,»é¹Én&‡ËÎÉd7“‰ß�îÄx¾ 7Ÿá%ó¡]¶­§¥ÅÓ]ÑÝÝp ›(çÌÒñÃ_+ßã#ÙJëI ®áœ¶ËpNÛjìü×ðÍsšÓhÂóÍ~q÷û”@+Òý¥oAñà|ÅpìõAîc–³ñÏ;Xp®Ëz‚ ˜Yû‚|ðyA/]·²ÒìºrVÎÓh b¿óöïÿe ÿµÑP,{‰g~Ãý¸æ6h&kT—Ig׆­¼²·:ü~Bmz³z¾3\£ï|®‚ „]ŽÀáñ¹‘Â÷Mð­c.¼rÛo &ÊûŠÞ‚>s{—º#ulU7ï3cÿVêo¸Â}i-³û(É’û‚ê}¿Ö¡êðÏéÜ»ìgÊé$Wi)„ä> ïëèô”¾Mäœó ÎëèôáØzœ~ÆÖÞÕKñŠÛ†÷µ˜YÇ×/žŠaKÃæúˆò™L„¤Ñ3¶·¸QÙ‹ >3˜ž‹$öñL ”U£uXî/£ÏÄX“„eÁX†ëYÄ!¯ûpâ‚fmdŒyŒƒKt_PÞqù£«eš {ÌòœÊOtÓµ™RŸ®å—t Þݨ;qÇ/þ^X/iJ8ßk_1âBœQçOPNÏ€?>HN¸ž%cÐçóöá!YŸ‰ò<Õó$µÓsïvèô·uî—êµÞÔcSS+¬'PtŸèì{cÇà»$4ÓOÆg^!“ƒa2 ±½=ˆk�Iãå£ ¡Ò`BÈ}Ô2N×M,W­›Xn¹×•?M¼åG󘇮q9JF§�Þ |…„PÖ¸f×.‚ïLö>›tôR5Ž£xœ?ØEx.T_UÂ7ú¿ ¶ÑöËÀT™8žF¬½©U%x†Û6åÛJ&Ž^ÕLÏSÊ&ÀO¨—ü!sžÈ[ﵤ~Oãï~Ä&\ó…wùû¯Øž›Š£áJÓʈï‘;r“Þé<ö ë)W=IÃõQÕ៵Q»[aX*‡}VãŸQö>–ÿà#ÜË›Æî{ùÙ2Žë¾Ós9�'Àí „±8ø/lOÔçÈ/Öé­à X϶ѵ_n´#¨ÇÆÁ–&Á–¦¡û·%'–Ë|ðõÇ¡Ÿœ'Û–¼ÏÊê"ÉÐnÙÞÚÐíËÖŒsƈãÿS1‚×íÃs&œ'ý@ŸOK±V:'ÛÕá‹gÌ®}¦c‚ç»ëûEÉœcGas™£ôÎt¾ÞáH<t´1÷®ÍLq;*z—cÊ™1åsmÍ1®DO•ÎæÓÖ©IfêH9æc›^ê”×kç Ûô,ß8ï 3}>N±¹ÿ¾_ü[ÿÁ¤“ýgþ=3®8.÷y,Ó|9ž“k5CïÃRõ™’¥ü&óÖ¨ò:T´m&óg¨ò;Uù“ÍåŸWåwÅOvP•¿uôkÐŽéX=Îa\³@ö#z‡/óÙ¢ÈzÒú<¹bÙŽ¶nO“Ôknîöø|ñÖ™XWb=@ëK¨?Uuf«Ny6Ž:ó4¯ó6%”ƒT×a=¶±ŠX¦ NcswI'äC¼I®,é«J:yêAz^bì:7êºJõå^úrw|õå#ëË7rçV_þóïõëËþyd}ùOm1êË‘õå_5¨/SêËu$Ù§©/K$'ÿŽõã¥{{¿ÈbèÌòKtÿ+Ä¢ÖÂéyô¾‹Ò/ë9Œ×A q±TÂ÷½ðsõcßïÇ7K1Ýu”àyÁ�û#µzŸùVë ÑýÂ*œƒ®~ÜÛÿc»‚ËÒêzaø ÅÅñ Î(w7(w“I’ÒÒÒVçéîîêÎÖøT´6(È›úøÂý¼àOEØž+¥ûJÈI°íÉRzFåᾕո—üiV‡$ÄöÃÁ¯£^^¤çŸ±s'­x× mC– ‡iÛðØÏbé(áÛÂñ̆ÃiÄÖ{m5¶!ч>¤>¶„ëL$ÿ6FÔùK\ÿAÏqÈF]œz‘Û^>1–éc eŸègg¾õó´>ñO¿Ð÷‰z5Ò'^ûl ŸFúÄô ’O�|¢:üì¼RHƒºdÆú¯ö¨{À¨žž™d ƒ @ —q|q$FßGÆcÞÔ6ëÝÕh„öÐ$ò¬©S¾ eþ¦N‚ÓÆýUо£·§Ôºá:æ·UrÂ6Èå¿drº4ÙOH'󠵫ážÞÄB)ÊewÉèÎÉJg³}d 4n<`i¦m­ã…XŸ†Pvç²dœ»‹®¡k_èxîRHÓ²„õ{=¿¤ý^Z÷’ ¬3Cûþÿê³Ï£Óg?õÿRŸ]®“ø8”ÞXuøÿžž[ßþ÷ Ü—¼J›EÞ{HÈ"¥o=?Sj¯àžõÝÁÚ³�>¦¶G×_6Z§Q÷èT?Û¬3b[+ô—ºëb¬Å×ôÒþÚòç´ý¨çÈc|Û9 ãÍt\µ1ÇV'§¶%D´ÿôxÂ8‹|õ&$ ΪlZd¾¼2_5]m~:ôÈêåãÜÏð¾&ΛÆßø¹Xï‚n6J²¤ñ¨1!£w?Ê3aÉ3!k#‰ì«DáPæß\½:LðrlWÖq>_}z%I¥s�õ¤À•ž ›£0HR+Ž’LˆS‚{¼¬™¡6ë| íèÔ«ÆOÝä*¼åNÿ/¿l«s?]FBTg?Ý¢ŽQXOªÛ6t]yc‚C5®:*Æaƒþ˜¼æ…žeP×)ý‡„<ZG›Ä}Ÿãª¼N)/”ç6“ù›Uù]qÒÎSåu«ho3—6¤Ê_íÙçUykT´·›Ìß®Ê_'m§*o½Šösù/M«ò·ÆGûÒIU^¯Šv•Éü~Uþ@œ´Uó¢ Aí]æò_œQåˆöÅÓª¼ƒ*Ú;MæïU劓¶[•wXE{‡ÉüD•ÿD|´/¼¦Ê;ÂçO4m‘ëVvczžÿ”™5�.þGgS8ÜÛtuFo/qA߇{Uxä5G ƒ‚ØãÔå5[–÷cuùÇâÌ; —™‰3ïnUÞI³y%¹Õ÷’h¯$+mdùŒôz’"×G¬?Éëi_ŸX_W‡ÏßAû®„Ø ãõyð,Àú]Z ­îUÉ/Ùl9p?™NýbÚþ Bdýb^ŽßÊóçÌE‡ÕïÍÅö ÿž_]þñ8ò_Áó«ë׉8ògòü9s±È¿Çì÷\¬¿¦¬…NÇÛÉôÎ{<ݾ¶®Îº–ö†ýÙÚ±ClßíIíüGô5ÅzóÜ:mÙ½”~´b÷{ºqeï-x@g¶ª=«â·]¼¢ë¾ã[s|ì#óïínó{äUƪ¶~@ôÜÜ–0ƒíe3ýq“|Ô“y ðÐÞ½r|Ðз­lQçqLë!èîÜY…Dñ×Óœ¡MÏŦº°%S{ˆ¡‹8û9­tÝ7“…<ª-÷¿T}DÚÏQõ¡Vƒ|áÞsìÇ Ž€Çˆõ[º}™æ%|K˜î‹à9v¿q,ÂùEÞ§Æã©,£ñ~Œìà2¾¡OK¦2¿³·Î‘ßÃÔeËW¦²þ?ûçWì,ÜÞ ëûªîb¾™Žwf“ µ~2Yñ€%ËÐ×r{`Êš0Bå¿Mk§¡}?Áñ*Ü;Éîæ³œÅ5)¡~<sâ§)x^.|�¸S=îè>m)9ÝìQ”ÃÔV2�° ®ªgÓqdÍøÖ1²”mM*c€o?ÇÓ©p[–NÕA_¾Ñ–‡g-ömßÃ×ɘXkåÀµF².Ls~çýqÎïH㟪qÎè :7çt¿v€<B”ÿ6çÔ2ÿÁsoXBç ç¨|þ»ñ„®U§á˜Ȇ¶U±»ÔCŸWʆç-kÇz¿£3ÿñ\žvTX‹åp5Y]Õáo¿#É ÷œ)m%y•—d(ã‰RdæãDD^[¨ºkO—gç#Ü|k MœCc‘Öâ¾43kéxöÑ8Ö�öñ5€X§›\ˆëÐãYÃïú½XgZƒ¹èùlÈÃ(Ø µŸ ôŒgð7Ž%á8=ãc/åQ¨#bo/];žCerø7\&Ñ÷…ñóˆ§ûªûªÿèS— õ«|² Êë¬ûHn kkho»Ï³—=ÏlÞCc Êqýûb¹öÌ4ˆ›h—wÿ©îý›YþñÍRþÊ-ß^‹ñt¦ßŽsF¬¿ö”=ÔûÚZÍ9o糓5öñ Á=ðGGñnÅ© zGÛµ»JŽã<m_~m ÎÕnÌ'×öUXî<02^Þ@¼}UwbÜÀ³©ñÛÌ»v÷¦_³ßx~Ñù­v§û¨eˆÞŸë ÷QâùK­¡wí+§VQ=Óºª÷‡ŒO¶Ü 6¾kw†¬¶šÁƒþª7 ú[Þ, ÷ã>>JïÞëߘt²ü I†XŸ3r sW¹o¨ç®¢ŸIØC÷¬¼‡y‘<Ù< îU4¹.áåpŽN~·ÙüÀ{/ʇî1o´ÕbùPN(U̵( .§z”ÇZV #”öoð< ß7HÂ…W쮊Òq>´ü]B÷ÔºÎ$L‚_{ ò<ci­Ÿ˜BÜ^!Á ÷òyúZ åÆy“ò³½_Ô¿…§¥³-B/‡gÊE²džQ&‘sí^¼?לãxæ¿g7‹ÖÇ'D÷ÿœ®ÿsmµSßœë½Ã ´AçÇ/)²Á}®:z«7¯7²W¥7¯JgY1ë|¦¯ÀÈA ?y:üÔÌ‘Ÿ ŠŸ˜{8?ƒ~.éðミ,î÷ƒÜïñ^¸dœÁ8|e³ªŒCª2æ™,ãðŒ¦ŒNJûõqü¡ÿz)à‚¶cuØÆÎzÀ:€{ç‘W×ᄱêð»ôìæ÷§m&Ì„²í®�ùóo¨ýÀvã}b2â ½qx¼ëÂôL¾sLj÷GB^¿ÐîÌÂù ˆË“‡§1ΟßJšyôcÛµm«ïD¶­N¼ÎÚVÝ:r4¿öÊbÓÉ?‡Þ2UzQéÍeRo§&ÊÕz;ñÜ@>ýÏ= º^¶Ž¸F-PÞãe¬ú:fw½‚ûÙÇÏ_–¢ð?ªâßi’ÿ±q-ÿ›u䙇>–êä‰Cχxþêð |ð7ú«ï¾Š|÷Þûf¦-Þß,p›9¬­7¬3(<¯³üL‚³:üÌ·Õõ”sL´ÿêpzë?ÙÆd[¾d£±Âš0-Ëv›mb*ßÖ ò>¡)ÓÄjÔ/;?­”ë¿i¿GŠýZ¿ÿói×Sh_Û»/ëtý¼¶l¶dZ6ˆ ®°%¯üŒ-£:üô›Úò]Ü­í—={«~¿ìÙŠÈ~ÙÓ?Þ/»HÏ¡‹Ô³m2ެÒóÏ·èÉuËîøèÖ˘ö~h_Þgà’¤“}w%DtÝÝþìœyðmÅù%Ç{ÿ ÚŠ¡Êš©­vw 5;'@�öMRÓûúšÌÞ©M™½Ïdº~L’]ÿ…wÀöÐóRû Ò{ˆeöë>*’Ê©Z‡2­íÆë–Žm(þhbÿ]3]w{’ÙùÐxhJ\°aýn<ëÍ•~yoJ"Þ•mžáC£ß¥g¹pßÉ¡òÉf2Bùô%ƒŒþÃÖÚWtËw %;§÷ÏoHYe‚¬Záïzø½£äx_¢»ÛPx'tßîášG‡Ç2töíü‹Ì¾–Û2ûÛÞ,8ôŠ‚™;IòLÊîOé¨;‘Ò†¹ íøÃÁ¦dñ ñ<KÚ/¦çYZBápx7<ã“ÿ†b[–JïF š‰(ßeu”¥uc~BTG¯oñºëÀ‡TðÓR =ô£ïªtfº^…êï­J±Ï@.(Ù Å¡´óÇÍáHÄ»7à˜4‰Ãëš pL˜Ä‘8Nà™Äñî’×�Ç´IN<—Ç�GÔ± ¼'d€#êX… ®Ù’î›l¦'ò~õ'I–õ#Ò¹+Â>6ïžÑŽw[£Æe<tªŠµ L¯÷ñ’Åû=~¾¦ÇÀ¥‘P͘óß±µ5ŸÂúŸÝtÌBs¶GÄòècΡÇâÕ×Ò ’òX¹¼XIZKqäÛJÜ¡6b\+Ã~”öŸÿzºÎób>Ìu–”?Ê:I÷yü»eD5·y â\yiÿ¢ÎÞE.ËI>ÏCÏ>ˆ5ÎëÄ"y]›é½ N¾¿;Ž3`ˆ—ŽyðñºFûëÇÏŽwàù)n¨³{麸}ž¥¶iŽWy{‚©3T|¶â8Ëk3µÿŸŸóò!=k)~þêãäoö©9^³ü¹_7P9Îfªh×ÄI;‹ö¥¡õÆÅwé5>Sü¨ðeÐþ0í _ú O0¾ó:,jyÆ™w©*ïpœy³TyGLéBʱ¾À|ßûáýhcѳcmPg}õn:ö…÷Ñáx­÷­ÝuÆE¨Grp<€ÛËg¨^/üûs<ߨ™|ã›i¾“ØåùLÉd‚åëU5H×'cœ§}ÛC¬<öJûô ½J÷=2~/È1>QÚ‡aßLæyþî†&?»Æ’­­ÎQæfùœ­‹fÞ%c½ÿYŽë¿Gf^!£ô¾¬G¿´ÊbOfsr Ófê§äÇ{è¹Û€7Ôg¬~œ­~ÕŽg1m_jhž~_êkŠìK=úP´¾ànçóx#Ê<Þè¯Y?TÍC¸V;¦òµ‡"ÇT¾Öm0ÐE›yŒU‡[_ä4”ý/‡…qœÙ÷ Ö?¬Ô×’.-'È"—2Í/ͶG®{à:½›ï)]Žƒn'hl§õ£=ƒötêÇ™°vŸ?ð=Îuˆg¥†ÂØ?›¹ƒëq0~=~õz}=~µ8R_yÕ¤G=¾rul=>öj¤“ΟÖ-ÝÏÀô8QnùàèñI–ôÈç%‡ÿ®þk'î B_3Hª$BŸs)¿ûgP\1µÂž8Œý½âY–yÐ/ÿŸúü¸Ü«»ìüý-å›u)üíˆÃŒòíÄÿ$ÿŒFBzèAÔý8ß{0;\ú >GjMpOm³ã9“ÒøžkzìñÇI~œ©$–€*} ¤ÏÒ¿A!][ú‘ÖøÓùWðC§GÁO½c[!îFŒeÍÚFíœÖ¶„Á‰lâ½è Ø_pQšïjÏ]7ŠËxG«Šg/ðìx¶…(ÏäOd<Û5g ŒÇÝ.ÕªhÔCÞ- 2t‚Ñp©Ò9!]­:´}kƒTÞdÝÞƒïÿ˜XPî1æ¤#|„Ýõü/}¼=p:¤ƒï·ž°}Çq_ù0{>:Àu¿•:V¾/:a„ž;Q×Õx§§É/ï¡å}im<Ç+ìgêýza@ê¿T‡ºQª÷͉t‚µ.¼Cã¼ïÂ^/ì»0U'›ÙS*—OÞËIN“TÜ!E,V>Ü/ˆeÜèaûmp/-«4G­Úç8³üã7i_<‘ÔÎ<eoºž1žï'Cxwü à=S»€ï°½uæ]{�Ljf¶Ðù:Û$Þ[ZqÌj ~ƒØèÜCülvŽË®s_—…xñŽˆ×ƒÕáÚŸŸª³¤|Áo[Œ¼XŸ @^Ú›ê·î/$¶©BdWºR…| –;K¡þàyƒà?ûʃÄZ>JËÀ38¦V“ÚÞRÂêho¯&•ßÉÎéË<OR!ÿ|(_=ÄüÅGÉEˆé­zwÒÜWJ’À_ !Ioßþ2 I| þàw"´ììoÁßaø[Gˆm˜åÁ?[-¢ÍÂ÷Èg= /µS[Ï'²sÀ^ðÞ×¡Þ»Æ ú·êðYº¶ × c âXÔƒ¶ÉÀáD="÷ Ó¯d<UN’éøë$¹/¼ ï®Pÿévº+ޤ%£L¦@ï oï·B½SpV‡ÿÔ†ßÊE,¸/5˜Dl¸Gµ?8í>ø+¼û`ö$ò õ+ž ¾°âXúñR”×±ô™RzNö<·vþ-5Ó¤ o‡dàØ޽ ¦0~C¯Ø[ÁOÜt}Ó»d¨z–ÝcŒ:HcùÜxö¥jnÂ},Ýcnm׫å%e– ÊÄuœÔ :öêë8 ÂbÚóð±&mü­c03iéì™ zNåOüËD½¢½A|å÷$_z4º¼/á}áƒ(³s±yIhG%»ž‚v–<Oòðªy’aÕ<‰›Í“I6O2ðŸ' \x—®ÛÂ3q†6ò…ôÑ>òØ_*`m²ßѳæñ>.?›ÂßÅãPÏà97C˜ó±<ÿJã<õ=¼£î I[KÐ4i´{¬{L Qú\øy íÏHzë€^0¶MR¾û߀›ôþ¦<€¶Žyi»úˬ] q:µ¾†mkà¿ÿ9ô·Ÿ›éÓ£}ÏKÇ9×�ö©¦ê¸ì I2åx¡²Ûbe„ؾ…÷'1ßúã¯%™xë¡>|Þœ.Þ:c¬‹ Y€§ø¤{–Ê e‚k*Q>(÷su$Ø›:U@ýÿ3ãtj¢³w�â@ ÀA–ý…h£l¼yBù!O(C*_±¯~�ÏÅ›*ˆäéãÃPoŽÏ‘§<žBPÿ·ÏŸCè%¼tŽø²tðA¿döìñ%ëàÃ{éæˆ/Cß8ž1G|Dß(îOˆôÿS<‡¨âË)À1ˆñžâO›bùMûû Ŀ󃈃úF÷wh¯H¾†tñNŠû!ôߟÒ}æP†!L‡ßügtÝ’¾þùm “§CgHŽRΟ.fu¥%Ë@ñ×7_7ö×?·Bþ¤$ 9–m³)M¬·¡ýM㘛F;ôcÓŸ—®Vs1ä§SÆ<}ôàqв¼Bi9ß[xöýÌPÚda~£LÿëýÌêð3ëÙyS³NaÞ‘Õ¿+!>Öb]m.0Mº‹ÕÒŠm3l—õ弇u´¹ÎB¬Œ°? ´™È8ßão$òÏr˜êtÛ}D®‡ñ~(´O„ñw7¾cû }…¦í9ü%´Á¾E ¤v>Êc!­[Ôã5¼NWËÖ}”ø± ñ&Ú/çÒ{9<.×i@OªÏèÚõ|KéÆ\Ó¡Ëð£!Ѓj ­ãBô|–è=)®­v§é{ŒUr µ¡ùX?3œ©×ùÂTä¸Î~ÎÒ~ÔŽC}áŒþ8Ôž‡ Ò~™ñÚŒ€ÎŸ•ÚÀzíßÐ㬠ì>ò'âJŒ< ˈ}£ 0?«¿«ÃŽ£|ÏõÛ[ÏýÈëK„ö ÏýÞƒò*Ýo‚}Eê§Ðo»±T‡ÿk“ Ï{m´Òþbð€g¾B‘®¯F¨u í=ý_KR±þ3¿âå‹'B¿bûd˜NmP/¾'µ?|î]èßm…v5~£ò™Ž­Ìâw@BYB&ô%Òú;’!á©OÓ³v¦¶ÚB3TÇÝÊñ}Ò?$µg^©RáÌ¢í"(wú–À+Kó“¬ÍøöÓqóiÄGýj+ÃKñ}SN¯Âyá5šv˜¥UÊrj£*ͰNY¯V}J÷?²¾¡ôõÇzô5º®Iò³9­kúcÊåNèÓÜÅúµìîjÅžšøúŽOÚæäýVÉÞÀÆ.Ò~Ù~\[‘Ç�ä{õL’ùkW³êèZ6vùløe:žñE¶uï0»,“÷'Zàc˜:FÞhkÕ3¶žÅq̱Ï;±sÇÃáðY¼OŸü7Þ¯ú°ônô‡cçªß¯ ß&cåÿ¤P†÷xÙëuËÎön9øüʈŸ”æÐ/Æiouø‰ Nï”=þm,Ê·š(߆£|Ó]{À¾Ù£å0·fÁZOÒqï©r‡z¯£ËÎÖi¡ÝáZ-Éî x+«s"÷5‚C[zI’‹Ãb…Žã††|ò…Þ´ÊÏ„ÏjÊ«»ïP^Ká§{0)_Ýòmw¤cc?âûàpnmyøn\†¼m¤kÝ~ò{:f)k¥KG‹ÛArqcS]SWG¯§³îÎΆ:60ǯ‡•Žw‰ƒ~G\ôs¢Ó¿‰‚ÒyÆ|½¸¨lø;ÇùCšÈ_ßj<Ïìì;(ÁézÖí–g¯7ҟ£t7i%«£ó¨Þ°CN§9½å„có—CN;ºzÛ¹ýF¡?¦ÐãGHÔ,ý•ÑéïlïjðÇ.ŠþõÜNî1EytúUþX6:¬Ðþç×ã²ÑÑiïîRËdHß®¦_¥7¯ CÛ‹ö-]mÍ1hßß—´=p 8~_Õð‡¿|$¶ý'K<T’"#ØmìE9@IÍ‹ÚW«Ã¯Ç¦+ÇÀu¤Àˆ.Þ+R%ï|�º4÷óªÏåó쎇 Ò¯èûÝZ‚ëÑ;ZIÅWȺÍAbÇ:°þ0ÞAH¡k䨙º;à{›Gi¥÷¤âYg®­,®ËçùG9ËðÑuÜçè~AÝïêzÉ6ÀΠôýD^{ò¤mA9L×äá|ö5od¿ë¹Ìš‚vYfÓd©‘̺¼žÎÈ5Žt¾H¾·ø?ëãÞGäõl/“Íðþœ4.+ó{Fæ—ž«­=3ó©ß2=Û ÏË”õ­œÅ5@–ÚY;^<Gíê5~ŸÇK…õÌÚû—/ðù¯al/ :}€áý ¬¯vOƒJŸ.¦Ë–;D]F›×ë°;%R‡ÇÞä:|T.»]jC›ÔÐÏ[Ú:›÷èhìj8OW9óó~Õ™Ÿ#8.ëmpýÍL˜ŒŸÏ¦k5„u=ë_ü:ë_îžÃú—£‘ë_"Îæôë¬ñG]ÿ24 ÿ?&Ÿ¿û Ç3 ézq7LF5ããš³%í:<sí–ud•Ö [ZÛJôÜŽºº]uk·UUÑu«ÿpÝ*m‹@V¯]Åx >r’Ó¨Z$$Ï í@ãòÙÏ®oÝ¿´ƒ®ûã·ƒ'ۺﴃîŽ9Ø÷òØÁ“¹ŸÔv\VÝÝ ¯£»«"uÔñNü:zb}lyuöÿy_ŸƒŽZ/Ž¿ÿ“êhçeÕQ—Áø_—Îø_{Ôñ?} =[G]:ç_tåÎAGµ—GG_{ç“ê¨ê²ê¨ój}uZ"utçcñëè«¿­£ŽÇ"uÔqÿtTytôÕŸTG».«ŽÚ{ôuÔ~G¤ŽZ?ˆ_GUÅÖÑ]Dêè®wæ £šË££Gû¤:Úc¬£Èóâß%!~^|ç¿\$Æó×AN™•„à<_À•B,ô\i^æq±3“Âa÷1‚çËñ[o ù{øÂ1±þ`%ž¥÷ÂÙyîáSÂYî'£ëåÎ[#õrçõòZ4©?/èFk_w.Ö·¯¶K‘öÕòHTûz9l‹<Ãý+Òý/.EWR;ßâ6ŽÒì ÜÔÚÚ–}9Ûùm¹åY^ÏÓñûÓ#çcûSëÓ‘zký«9øSðòøÓ#×Gõ'ñ6<wGnæsíü'9^¡›6ó/£ì7¸ÿj¿ÎýWMÑï¿Òµƒ¿ù ¶´<i-ÎÁF.üÍ–Oh;.¯Ž<ôuäiˆÔQßâ×Ñ_ß[GÍŠÔQó¯ç £—GGõø'ÔÑÎË«£¦Ÿëë¨éL¤ŽêoŽ_GGœÿ©£¦›#uÔ´e:º<:úòŸ>¡Žª.¯ŽËôuÔxu¤Ž>ÿBü:ú²=¶Ž^ˆÔQÃãsÐÑðåÑÑ_Þü u´ëòê¨þ¯ôuTß©£;Râ×ÑC ±uTŸ©£º?ÍAGƒ—GG/|B퉢£»U:å:ÇFê¶í‘q­~>opÿ×çuîÿºž¹ƒsQtäÜ[÷¥3±uôyO¤Ž>³zo]¿¹gpïB¼_~ƒá~ò]âíÿU’d?¨ÃK—½_‘½47aq“l#Ùãýãüã6»¡Üuå~‡Aÿ÷þï­ÌMî_¼>¶Ü÷ý>Rîû~Ü¿Àåþ· ÷ƒ¿²9ôåþà‹s–;Þ7¹ÏèÊýöKúr¿ý·‘r¯=07¹þIl¹ßÞ)÷ÛLÉ½ŽŒß´ƒ©Ävàÿ2µ1?žtî]쿇tx’ÆÚuä_isêÊ”“Jô×гSä»j’>ÁYíò<Ù^RÅø”àìJÅ6¦‘Ÿ_â™Ñ¯†CÒ¸ÇT£uàþ|~®Kšm÷]Iw5s™ÐÚÈ­÷¿Ýúj¤Üýþ·c¤uß4ãoLÙFªÃ<ÛNn½!ÒNnÝ(…\tÌHûŽCÜN¦ßÐÚʤ‘­¼¦¶r8rlãyÜVZ™:V4ܯØJ­ñ Ô [ MãP UœtQ{ƒ¾.j×Gêbïë¢.¦¶ÙOHúøáû²Nü‘e¾?rÿw„.>§³ÿûs/˜ÒE#ÓE0ªÀ:ø!ÓÁÑüù¹ Ò½fì&Ž(:à‹t.O=uËOôåË÷"å¿§jnñ²ÿhlÙßr}¤ìo)‹§ž:÷±JõÈüQ>s™tbäfrm™ãÂ$r¹ê¨Ïœÿ÷YóÿnþùÜd~(?¶Ì÷¾)ó½gþçê¨>iü[¿ŽŠÖFà‹³â•ÿˆ®ü÷Äÿ=:ñ¿æ³s“¯‰þÿþÿž-ñØü·xÛ컆6ß»<ªÍG‹3{Z»ºýÆm¶6ç!~ö²©zßµnQÝóoß4²_zóŸôu|ó;‘:¾ñîu¼N¿ô¾×cë÷æ»#õ{óÑú¥ô,•§È8;fÅÓœ†º¿9¨Ã‹4þ©j{ËçŠl6–¹Ïã¿ÁÓÑÕ} .™Ì#å]ó ¾¼k:"å}ƒ%~y¸5¶¼k,‘ò¾é÷Ñä=MÆÀ—F‘!¨»UrŠä!ðs.ç^EÎòœÓ8~ùæÔ·|—Æ.AÎ7Œÿߤ3þ_mvü_/î51þ£Îøÿªã–(g<Çcò1¨/g5s–óú’3ºr¾á{úr¾áh¤œw•Å/ç{Š-çÊ"å|òröß¿•Éú‘B ŸFlñCÅKÏ¥Èø!ËÛe¼¦Ñ§ê9Æ7×9±U/†ì®Ð—ùîüH™Wž‰?†ô,Ž-óê3‘2¯~.Z ™Ÿ™Ó8Cæ*^üEÊ<Áš%É<ZÌÖöÁLÉ{\WÞ»ÞÔ—÷®#åý™â—·ÏDÿw—Nÿwׯò–c¶A,Qó µzÕrÎb‰aûÇ«Ÿ¥+çAݺ±ªM_ÎU7GÊùú߯/çî-±å\ùÛH9W¾yùä|÷‹‘rVÅlþ•/VßʰÝ)çJ‹¾œ?3)ç}ñËÙûóØrþL_¤œ?Óvùㇷ*jÌŽVGFïKéÊ{DWÞ;Ñ—÷ΑòÞ>/~ywybË{ç¼Hy_>š¼‡Uv=Ó®;Õ®£Åuÿ ÷;àþ‰Kåš»œ¤sŠ¥ñU3›Ê·GÜ)†chš3OÏ€¬A~ƒýØv±ì¦wå€pŒi/ÉØÄÎË÷Ý øª=÷Ç5²<FF´úÜñª¾>w<©ÏŠ-1ô¹7rÌ«ã1QŸîc¤Ò�íuÏÊë¿ öšàyÁWØÝ|½Ï&¬žý‡¿âøÔç->¬-cø¸Ö^¶ë¬ÞÎï?Á³íAIŽì>²¾¿š¡üÍž–õŸ ÷­Æ¢ÖÓªþ,îωÜGµö¡Øcæª}T†1´£¡½½«IÞ?¦Oëú¸hî?Â=[ü.ω•Úò7 5÷Ûƒ\mê}HxvÞ éàw­´ÿ÷Ø¸ÎØNU‡ÿ]WÆöYZÙ>›»è\¤«>޶ÿp’ŠÓá) ÐN6¹Oi$JÔvŸ§«Esfß“+¥;6-ê3ì¤ýWò]l–ð¨tÖÝÌòÙŠ©FÛ¸äÛØ¿œÊ'N,¿äß®£¶q£3ÈÏ=@ÏMÃs|+Ž¥oVîß‹~½?£—± ùlR>×z¾k²ë{V¬üS+È õãtùÞ¿ÐÐÇ<‹Ñ: 8êcã°%3ig)Žæ_ÌòeŸV•½FU†ÐT~Â�Ðzv?§?¡_†ÄJ†#±66{=/ÃIÇ$Çõ.ˎíőTÏp$yMð!Éòaý²$Ç<Óp 2©!z'ÛÑäz~Ÿ£Ó„>F}¤ÖÏÍSó=&Ÿ`¶˜õ^Æ·eŒóЖ=%ƒ•=%æœ` ŽãQÇã#å„ NŽÃ¡Å‘šÌp¤šð+[Ѳ[ÀQoGBˆãpŒ0ßL5!S[Ç!ØuZ†yÿ¶ŒrÌžÒâ±§SŠ=¥dÅkOÇ’ßã4OÅAsT¡™ü»9Ð<ÅiΘ§is©hÎæ>F3½5Žrf©h®››¯&]T|5}”ùYzÔûs¸mf0›H^ªµ«y<ÖÍ ˜°M ‡àgó Ã1?f9 Öñú+é5ÇÇ1b‚7ÇÑÊt° Xg$ŽÅ¯÷ÄG9ͦi6&ŒQšiþö¸h”ôž¸NÑ{I LÔµv®³Dî—5qð=®ðm?;7¾í*|g ™a “œïœïÉ8øžPñýÖù>¢ð½0/ŽøÍÛ¶t¡}ÆÛx cò} i†ÃNXÙÍÛ·]Õ°˜[Œ±yUeŸà|G½_‹óäeÏb|/rJ|—g“™rxG3âÙôÎ"z—ΦK©§"Ö+ “ÖsÛì§ðœêþ„úwük zöiº¶å×ÏûjG,}‰ÒÝÑ;1Yà‚~ÞcÅÎ (cýJäú/ØÖÊ·ýçK´Íõž)Ï’˜¦â„ ÌßûødAùèÇTÙÆg^±ÔW‡ï¦{¯é]Ù×å^uÄ ß§îãçýݦ’ÓiÒZñ2Y }ÚZÖ¯² B_+TþüBÚ¯Új¥÷\B¿…Þsiò~Kž!vÇøØÈIÜð.d\GÉÏà×(ð¤,€Kã_.žŠ´¥õŸ*<‰¸öøjOVñzàŸV¥Ç5’|ÿ×섊Ÿq€óófGUü@vÿœ=¡ÀSÆ�þ4‡ªø{ÙÇÇÿgªô5�¿›ÃëxZà·rx¥ òSÁáNUzþ|ÏRÁ1ýB' <=¯:|ûy¿RÁ�ÎÇ¿.+ðÀÏí¯sø)Ê{û·9|Xg€­ÜÎ÷\Pàó2�Î÷_òªðdHk‘ñÎ�>ß p¾þý’[…ôx;_ÿx)O/{¿ÿ_ÊPÁ‡žÂàgTðÊêðmpø¤Šÿ€ÿ‚ÃÇTpàó¶W9|Dü·=ÇáCFÇ4nãë_.VÒ.ª¸t².&UºK.Ãk<–‹wÉWíðñûÙ\€„îa3¼_MûXJò¢¬Ãø\[gs×½•ôD?§jý‡0æÁÇ—îV­ ZoðsZräµ-_?Ðç{ÆÊläAK«ëÙE`ƒ·ÞÁ}wLgça⌺¶•¯&¥+IåÙJv'•ÝI÷\´;éd:ã5´8ÚÝå2xóŽw°MÁc¥ˆ_+ 4%MjiÑN’Þæ«ó*øÔãôÊx^ø(‹±ÑÆóäûQö«úT¬sÞ¡ÞÞ‡ÌÊÅð\)*ÏÓ}Gu—Ÿê.ÁqÄ?ÐFË¡CÃj‚Æ~]Ôf¿QÀsêÕ²C]ã9§Ô^ÙùŠôœMzž¢Êv£­cì4ÊþŽÈ; ñ,Q‰?¼¿ yt^4ÂtÜôë8t¼9ªO³_·xº}m]QiŸâ´=qÑ^…vEMÕöVOÓ]¾žiLuó0±à9_õϲ±bˆ rL9ÿjØ{þ×áÀÇ–pðãåá+ƒ7„‡>¾?<üñÓázc­ô\0—•Ï›ýµ—\Oo¬6-ú&úeo}ÒÉ(¡û+dóÔhsá9R,¿å«mê>¶d ×?Bÿo@nÛÉ0ן°*ý‰¥ŽxûÇ–Ð1  7lº¿ÍÎúŒ÷ÑüñO°<û8ÍÓ4WØYï�Í&Ë“ÎiŽ›¦iµNRšÌÍÛqÑdyNpš!³4ßÿ£y/Íߘ)ËSÄišozÿ&Û{0ÿÕã¢ÉòŒ1šWg˜¦ùŒ…Õ÷ÐüñÙËÃmèê<Ó4°²¹?ÍŸMš'kšÓt›¦ÙhuPš>šÿT\4YžÃœf­YšSÿauRš]4ÿî¸h²<Yœ¦×t9­V6féÅüWMÆE“åyžÓ0Ms…µ•Ò¼›æ÷ÇE“åqsš¦cßÔ˜>;iþá¸hBžÞnøªuê!‡vümÀõöÕ§¦ mA×é«G¦Þ° Äê÷‡æ³|3l €ŽQPúîFA>ÛèÝÎ3ÝÈë•ÆÅ+Ë3Êåc:fM§Šf ^š½(Ÿ+Ý(Ÿ¾×|ZùTëËç|´7Ô{máó¢|ð® Ì#§¿èîŠÐ©KÑéâ×âÕ)äär2ç§V$¸)M:&µ8.9ѼrÞÍŸ²¬*YeŽÆ/«Ì‡¹¬L×OPÞEV™;â”U"«ÌäOYVµŠ¬®x8~Y]QËduézÊ[¯Èꊌ8eU¯ÈjÑ[Ÿ²¬ZY-ª_V‹r¸¬qÈÊ«Èja\í;šW–ÕÂáOYVEV ãkÇÐ<¼s+YU²ˆSVA•¬vʲPd•1>YᲪŒCVƒŠ¬2öÆ)«AEVYŸ²¬†Y-8¿¬´rY™o«®HVdµ ®þ+Í+Ëjþ{Ÿ²¬N(²šß¿¬æ+sì À+Ÿƒ{Ûž%Ýùcî´’•{Þ{*<^ž“xø|õ¼GUxê<×xMâ©áxv¨ðÔ¨ðLâák]Ò?Táq«ðMâákwÒËs_ÛTð ˜ÀQËqÔªxTá4É‹‹ãIVáVá2‰‡¯%J“û‘¡'°L× s<fæ€ù: 4y®?ô$Å1Îq˜˜KµsŸKSÖZ Q3 Ç’dó±Ã|]Ô[8Lø¿•—%u@‹cIÇá2cœãجÅ!éf‰Û¾f0Ež›Æ;º€JŽÃÄš»„#GÀ0¯[;_—<!ãøÅ1ÀùŠÃÂeš¬¬ü*Å1l^·^–d·€ãD|ð²$Éq ô˜†ºµðõI'qða•Öí“q<ªáÄ^¬|KR†€#>øÚ»DemÒßjø5ƒÇÖÄ€€#>xœO,’qü†18x\µO 8âჯ±UÖð„5|˜XwfåuŸ½RÀ¼¶É㦡G4|Dž ‰ÃËùpÄÃÉöfm}Ç×5½½ÄD\·ñ²Ø³´u¯Œ#d®ž²qýÚÆ´m Ï´I<\.¶€¶M!ã1ÙÆ±IkòTx¼ ž¥Ä$¾:aB…'¨à±×›Ä3Äñ hÛ2?É&ñðµ´ B»BÆ“aÏÃc•Û®#K³°ý^^=ï}t~^ŵPt­äé¥N µîì=@ó6-z?"毆®½ ø�®[»ì(îÜ7¢¯‰j¦ø2)ÿ§—â^U;®ÑÂý9[ñNÇ~âmú±]hÃuùì¼cÍ“j´'ðÌŽêp:=ÀýR#Íç,¢žaUIHNˆÞt~`Å{‰K\MYµÕát_âumµ8¤ó9¤»‚¦^ {§~¼o ß_x¿"<ø~CxèýûÃÃï?>ë Ì¬’îr¾"—%ÈÊ’ö4Ò¯‡×¹N/š„ç{¦žoÃßß‚ç<ϺNgâ¯Á³ž§á‰kNÂsžÏÃï­>î:½ØÏ#ðD<Â3χá9ÏÃðœÁ½o®ÓWºq¯!<ñ¾Øvxóž#ðÜOL·×uú*¤»ž˜n<ṞŒï,ü^OÄ—üVâ}×Àò‘ ïøL‡ç�<m€Ê;{ø8Ïð?û;xB¾Y(ÿ•PîY(ÿ•@gË?‚÷]ÃsžX~Ìå‡òÍbù1–ïÅÆòãZ(,?ÈiËï€'”ÿJàsÊ%½ïè€'”ÿ ¤å¿ ñBùƒ¼g¡üY˜Ê%ÞcåÏùÌBù\f±üÈ”ÿ*üå_Œ|Cù3‘>”ÿŠ,xfò¹õ¦à"in={)IÝucUÝM»»ä;Â\Ã7ΟK{7¥598÷­™§û°f†°Óy}×a2îÂýl³›%ßĹx©sÝ8'Õ7…¹òН‰òWˆ·¼ß2Rî&äÜQ’ZJî9ìµ%àþ/'ú+]o‘ŸuÃ…}à£g&ð|ùÓ!º®rÕÊ'ø=Þñ‹1ð¥WÏ^ìá~-·]bù5øì´æn¯þ'ÇÏ8æÿ+ŽÍαÃéjZ:¾»…ûn¯¬ãDIÇ–Q’ÎtüÙÎvªeyÏ(®qÀû±ÞæûGÏÒý£ÒÚ iϼ&Bµbfy¸BY›eÚf e=ü›@o�c´½6ÖÜîótµlv5AîM:hÊ®\ñÃðL9ØÃ}ÎÎés}?³¿j¬`ê>â-ýÕYûý›ˆ ÏæƒßõXAœMÆïë³szÓHÒTÙ Õk,{AœåÁ~šp<Hï˜~t ô8¥£²èx OkÜyî#•åÃ3Õ³ç§ø{ ü~‡ÿ®…ßt}ÿv\S|þGô÷‹#`Û³ò8Ê|*?ÑÑoÿ!ÞUÕ¾ä<DZKŒÚGpm%sZ{+¯íI•ö8[Æçôtv{ö·ùøùôÞ>°[qÍNߺjjǽ3å´Ï¾«·Ô6ÝwÇžâºBÍ õšnÛ|]ëø—[-ÿ5óŠ%ø@åäûý–éŠìûÐìµÑä„ò?·Í:@×£¯Û÷Û~HÏô“AÖ–©x°| ±Âû«ž TÏþù'²N€Þ_‡>Fqð8ÑŸ•ténºHHû`gvÒ?¸ ;çB?ÖÄTï’@éIB6 StIº>tøëo¬ØÓÛ³$P²c±Å­œ7(w"–/?±™{ïYèû¡ìô#znVÉ‚ëô+û~sa¿¦6mµµ¬f&¾%dz¾ÝRÒŠëN¡È e“á@ÂUÍþÛ4F|—â¨Åt_ÙGê!­—¦…ú@._ó“±ÇYŸ¤xðÎÞ÷6Aý² plrÐø±tSøþ#d“$*K:¹é/ˆ ðŸžÚLjzôå?‘9“B2z3† �VÙ›òxAÅwÂg{¯,÷ÚÞ”GJðwi,†:çäÔÛ¯þž¶Ãýt^?÷ËÐS6\hoEzSç’2NÔØ°€kVÍëwAš›T¶|÷v>^@ùþþîÒ­þ¹¯;Ù¿3>”~OÏv ï³ßŠ™ô§ÐÛ|YtoCŒ|¡l ]O�2ýºyÌŽªÏÐERƒõ#è´õ 8BïZNúÍK`gŸ§çÏ»žX–ŒëûŸ=`¾·‘í8“t’×ÿãùýÇ~p³Ø 0X=û!=_~Áï[y}ïþŸn£CY*i]ÿ®eô ‡8ð®H(G1/‡C(ÇBu9@ß›±m>á¬$‰ì.êg>Ê;ôÞKP˜xû¿ˆ¸î¤ûx¹/Òøò™t}´“‹èùÏ!ðSÚ?˜ýc•A/Ö=ü,µ1ÊÏ«XÛcv}ý|KOÏMWüüçuýˆ\ØL ý|ÑIc%´K6N’ +7w`¬Ä8‰ï¬œ›o-'êXùÚoÁ8€qrâĦßÇFàÛ›öm. ‡A–ÉÕaË«4¦uÒýEмˆgXÐ8Ì÷×è}FØÌ&Ch`¯§�†wyÁñ¾£¨sÕáKg•¶¶tGnâˆñ:V©¦»Þw/žµ€2¡wõþ–ßÕ›¸Þ!S=»ÿéØëhåûdÜÆçIàâeM ííìÜ'°ÍÞ_@ý öÙûq9½Kµo ¯WOG®Õ=8øè›_ÐsSJOC9ÀFцÁ¾’&Žƒ¹`Ç;ÿ5пÿ—ì/WÏÞ~µ³@÷H{xY-Óó† É6ÑFÜŸÝ»JFhY‘Ç(íl ’dœ5Ö•·ÛãI´iÏgø%“¿ºütM1¶•ëÉ_sÎî“^Hëî-ì®`° Ê÷1ÐQŒö=”ñy:ŽÐÌd+ÖÑsT‘æãÖîãnlw„fÏé÷ŒêÙ–2Fه׽!cŒÒ9)Qî¶¥û(°\Ú]Ï.ÿÍ[Ìmq˜Ÿ•A¢å—u#ßokÙå.g•nÚ»|ìuåžâ—µg’Hý’Ѓd f;äSI,t_µ»Ùÿ{€·quÏ÷‘ù–Ï9%þ|ã¡&|£O ¹&7ômnORY°}…þ„:¤}kì¯ÎÖ–éëqíbQP®DÙWÌYr˜GÛ ÔÖÐÞv„–ÉÀËψCž­<ÀÏ/‘ö_ÜÏïÍάžý½…ÖÐÞé­_‚¶ìMHD—­¼ö¿†,Dz\xuì(Ý=+³¿{U£7ÝXh‘ð:I†/;wFç_2±‘¸ þgÕ'ÃÿæÀÓ àiøS à):p+ü/Ù�ž¤·Àÿ àv¸æI)I)I©t¿zÎg;›zö·ú¯4y¼þ¶®N¶Í«[HÏŒŠãáyu²Ð»ã"TNzÄ/Ož.¢òÝw‚Å—¶7tî/ÝÛÚÝuoCc»§<ÿBvÝX±É!1çèj¼ÓÓäw´6øO§cCwcÃ~£©«½în’uEóyp¤žÚ:÷+h<SE³ÓßÖÙŸS•|Û:Wû ~CS«£“JÉáoÅ‹[þ.Ç-78Z�­„m“#ÈÒ|7õø]-ŽzÜ®Œ}¥Áïqà£ÝÑÒÝÐ2ÿl§¯Çëíêž¾º=þžîN:µTºÈÑÖi”H‘`N}-mžæÝmwÑŒ„0ö{üô 3äJÐE†xÉ$Ì”ž._ Ýû{:<(½úŠÌùS§götCƒ×«›Y$0Žˆd8Å%'÷Añ¼â»#oM>£§”· T¾¿¡½‚£ Ó•`º’›¨ÍHòTòká»1§³˜yyºSUùã°ƒ¹Ú·ª=þ›ZØ Ï\çøO ¹Ýá£ç”©ò±ÄU÷@}ÒÌyu445y|¾Èü÷¶5«P½w{šºöwBUНºÉÑH÷^ʶ›ëÛäpcñ¨ÿuuÞš¢:2«G5 ÞÓäG™7{ZhUØÕéÈS™€™ÏçC>GKC[;0——ÛœQŸ·¡ÉÃÔˆLÑ s¨[ê${äR’ð·þ øß6 üí¿ÛொÃwQ=rŠŠ‡ÉkãR\ñEú"×ý Xn áÞ®.GGCçÙ|޼ŽpÐöæ|­¨<õ&¯§›bQ„½G–-õ϶Ζ.Gg—_¡ åŠpZ=Ü㇂+xÙ–\w=>?·£™îØuÐø Åopx»Û:Ú¨(°Ôh/ ÝÝ x9·wõ´7SŽºý ¹Á¢¤T9)5L£Û¤B"ì6Ò07I»=·ùŽ\Ÿ¶¾”·uªl”ê³GTŸ‹xÈ­þ‚A}±Å,Ù½(_ýø.ý“}„žjǰcQÔüóѪøÀeÙÔÞàó:J i $-‘“nFΠ?ž· kc1_W'+(…DC<^#¼KÒ§~t@Á@ÃU;baÒÉ¿¿½«Bj·”ïé”;…º½Óãm;•5JòªtÊ'$›ÃaØ iéîêPCòT-œí˜WÝäaUQyþîÈÖ«M˵|ùÀÇÚšdvdRôgtÖX´Ù+·0"ùÊ€DÈEK_Ai‚¾¿K+'m{p/ÿºeP·®”"@Ñù“HšàNäg§,ÚhE¡Â"ý§Æ.ÇÜØþ¨NJÿA‹%^„ñ­5î-‚ݨ2E·e…\§çÞ*pòð*C{Ñ-S¹ŽŸr½¨pšeƒôø<¬£«'©¸Ì‡#ýXÊlšièñwýT»²rÔ¼´^6’þC¼ŸÃ›Þõø¤WÀLJ°ƒ E_û~©ÎŒi5$üj#ñIãÀ¨k+9ÛÜÒÐÞ”(Y"üj»ªCŸœT/ÿ_HõINmªÝDë|_¤ŒñÊ(JQéÍ¿*.¨‹¯WïèYÊmùúýòhqUEÊ(´F²‰7›õt6¡45ºÁ¸Àma-_ óìù Þ È‹ å†<Ũ'Ôq0ÿ”â^ÚЯ?•ñ”8ìYS]1ìŸéîêñƪ÷E–â*š¿Ï©:\1ì^NjÔRÒSFu¾*KžA­wRKÂDCãVóYv²ßÓM_1øS'¥ÿîÑ‹+øjQéX®~ÔIé?/w^µ0ݰJ8Î~B½æÐb)¦™Ò«6 ÷Ÿ{ºîò`?fíÉhð°êö´iÔúõÙÎU(*é ñ>⛣£Í瓬ú €«Ó:Þñ»±«³˜w‘yºÍG{Šr'žå[[²®Ä ÏuM666µ¬s6®u^çZ¿¦ þ_Ö¸¾a}sC3ü’ë‡hãb N/4`ݾ¼=rƒJùŽK¨ßi­BØu5%’™6ƒZ‚75àH7_^¾ˆzm8¶UÂrHüÐkYtjþ”`ÿU2B¹A²Û»:¼]ЫG²Uާ³­«„I•pïQ(t(ªôòMµŒN+«É/çd\q±Ý¾M+7ÌTù©”Åòa:úAIˆdÕåSÉ]â€ÝNiÊx `*rd[WW»§¡S[NL‡T%dzd€:�sSK‹Ï#Û +g‰¿ ÉWà÷¼| ÿTl ÂH|‚\Ô $½ëôÂ˼SÇ>oéjkÖÁ‘ŽVÛ‘^©yyùÅá¾<öž¯ÂÃ%k€GÂ/«Cª àçÅRÌGø‡¨ÌEƒYä;?Ÿl?Š=ê0®˜­–qQŽœóçB<‰ÀY>”DzK)¿âvZú,z²‚ê”{¶žHú<=gññËo—7Gê§¿§Wâñ“¢¹qß®N. N5ž§ZóªwGKµqT¬5pð¿RîyÊ»6ÎÉp!~Ép!^Ép!þÈp!ÞÈp!nÈp!>(åa~¾÷Öšë©ßé ZiÒkýT§~¥Ê®ñ •ü¨«Æ»5ö¥À•ú‘½+õ{ׯ©Šåá›H¾/”â$Ïk6þ°‘D7o°q¼4P(énßFŸ:í;ü¢ª�¤ôÛé“×}¿‚W—QÃl_r‡“óUѧä¼bÕx÷ðgîâÏü‰ó8tÒÿmãÏ=ü¹?«øs'îˆÐïž>¿§C⋞LÙåõtû¹tä¥'qlQB_¨«YšOÛ»—m°8ÁD:|4° Šn´ð}›¹>Õ<ud:©?.§5wz:yäaÿ´èÌ0FµpefE ooë¼ ç-Ù„~Çö-]8ÔÔÖBå-ÎKsº÷P¯†ôÔkiæÞÒ^Õ¬òœè~ À|ü‰ïeØ;>ñ}íöŽO|߰޽ãSõ®±K ".®ØtNI8Ì’X†quTú1kJÁ_ŽùÖ£ GlOÚVzŠ^}l›—äT@B“¤á'”W™R÷3üöÛ‡i2ç-OY�£ _zº°àƒå Fá·Âÿ¾¨‹ñ\‹øwK fP~s”WÿfT÷p¬Ç1ÍRà÷˜UƘL1Zò$™â‚š ’¹N‡ß½ ¿KÜ<iˆ"|"‘Ë; šÂŽØ¸*Ž&дLªÕËC9´3¼Kó8l”âxÒÎñf±Ixº Šî˜U)Ù!!D|§9¾Çw„óÄñIX$éóµÒ!Kuõ¹ì¬êU”O³"ŸåC,‰e¯V>ÜU<$Kò”Ô“Cž©ðƒvßËè¬ø§“£•W&½DG$ÀKÐz@ºFo3ü96|yä—s1ŠüÖ)òËåþf`þPƒ„(MËÒÔù¢á+øóŸ7‡_Jg«eþÁñS›µNAÇ/Jlª×(þ,¥³mÖàÇ—Ì?þwFý&SÊ–AÖ¸£Ó£ß‘^r½³zë4ôNÑÛP©C/G¡G¿S™R‹ï¤šòAºÞ(òRÅo)í"µx>È{²>ð¿yš7·úÍ:©¯¹ Rˆ.¿Êã,¥Ê/ýh‚Ïä ™3XB÷}"ññ$$æåÄò$bªøÛ ÕG{eoTüž¹ D°Læã\nØ„9Îò7NðüK¹·I~'åÌ”9“”˜%ñx~Çð4}È@Ö€‡çM—ð0^¬½*ýu Ï]§8ÌËÉKĪô O /û0ç¿’S‘âŸR ÍŸ¾ŸcuÚÔ&ô(ä?Éðu.ååx¥,ÂçHÊÎô/æ>¸êðê/ä=uụ̋‹\ùäUOd=~õÐ5_[òÕ¥-{ôÚ¿ÍþŠã‘åƒ+þfå_ç¼byÙz&á´í%û÷_L:•ü)ÿ;õ{i'Ó¿;ï;ó¿½`$ãþý"IÝ wö/´D'Ù+ðeÝÍøº›ƒ¬›9_Wj|É¡Cæëa”ãË·™óåúÔù²äÍWä”ÀÛO=¼_l­ü”øq(rzM”ص-ñu/¿G”ô2»öòìNµÆ8ÎÒ\Ü®iœº<,ƒ$§xê V›—ÿ´’Ö[Aî‹Ñ>»û«%Håͼ<ëñ¤¯¥(Žø¤]ŠkžžH’‚Â8˜“ LÕ>ë•Êï¦HŽYO♲00²x–9”,…îäù#ô¼º÷yûì!ÞNJÊÚ “{þ ët‡’¿–òÕÔÇÒMÿÛy_™ÿÈF9AŠ»Óü–¥ ÿWßâv—Nå*s+µ½—O$JqpÔ" æ¢,Š\_C“ªWQEŠ>žàr“â§W®UTù1ýžþ„‰ô*>ž¤WÕ;ˆc˜?Oéð£ªïŽÖ°œ6Ú?HWäÎ[Ô\‘ó§-(|Ih\"9‘í¢§?dt8Ø3c=Žpø${.šfÏ+jø“Ïäù2y¾LžžnÓ€ç5Nö\2ÊžKkÔô¡¼!ö\®åËò/#üyŠ?GùsŒ?'øs’?Cü9ÍŸ3ìùSŽï§Éü™Áž?¨eÏ8¾ç9¾çGµÏ8/ ñg?[ù³†?]üéàONïy.·ò1þœàO^îõCì¹ñΰç¦dþäö±)?]üYÉŸµì¹çßpŠ?9½ œÞNoÇÇÇÇÇÇÇ]+øs€?9ÿ×qú×qúëùÓÅñ¬áéÖs~®“øãô\œþžn§³ÒáÿWL/õmÐ~ÆáQK þ]až½ouŸÙ}Ó¶ŠÝuu;«êÚÛïìlknóyüM­u ª;"?*ÿ¤q)t |ñóúpÖÿŽ„/Ò‡qP>8¢¿oŸ>üñZøÃúðgŽÀ+õáRá_7(×÷kôáoêÓµd¤ÿ—a}ø÷Æõá/}øZõá?¬×‡ÿ̦ÿUPþ®ü½·õá¿5HÿŸYðI}øÈí÷ò™6ÃtHþA»|Zþ¯>üÄ�~Zþ¡Ý?ëëÅ’<¡OñëÃS‹ àúò±¤êû»eá¤>ü ;¿J_>–<}ÿµèۥР\…ú~j)2H_¼Ô�nÀ‰Aúý8`)Õ·gKé£úp§Ã�n€¿Lß~,kúðµ½úðõt×Ö‡Kõ©wÕ‡o2ÐûæL}ø–Súð­ÍúðŠd¸¾Y¶gèÃwœÔ‡ï4°ÏÏ\Ô‡W ëë ìa·~=h¹Ñ@>7øcÍf}øÍ£úð=ëôá{ äp‹Þo1°ÏÚt}ø­þx›¿ïÓó–; ÊUgÀg½œ‰>¼É zŒàoéÃ[ ô²?G~¯ Ê0°Ãv{à„>ü>{»Ï žÜg ¯ƒA¸Ÿ½øŸ4ˆG âó°üÛ�n€ÿÄkúðoÄŸoé× $‘Ϥײç<³gØÓÁúkäJ>n·ÈÉžYìy SŽ<-ÞlÞwìæßÝì}3dzu‚=‰Î?HÏí¢, …—q»vðxíàý 2Þ..ãå¸ÑÁß9Ÿ·ðq«}._Êùy²ž¿²çþ} ·ãeï27ºÔïŸÖ?èÏN2~_àqOª?7ðx½ó+÷_¹œ6ñtyûQî×r¹mâqQîçr¿ÛÄíNî÷òþ¢Øÿ-çøÊ¥ü¼·I¢'ñÃõYÎñ•s<å§Xù¾íäEåýª5<¹Ôåü¬á|¯áü¬áô–Küðr¯çåú÷å\k8\îs<RyÇ#õ‹ÿŸë×sºrÿç×ï·C:žÞÅù’Æ\œ¾‹çwñü.‰o^žõRùxþõ.¸x¹\'˜_Þ¹^¥qiÆÅÇ{^èNðw]œžk†Ã5gM€gq|£ü]çáåÜÈå²Qz—Æu8?‰¼rT.É.¹¼\–î ÞNßÈùÜ(ù—ßF^Î\n%ÿãx6JòeñÇr5/÷Þ.*çr,gñʲ’׿å\^y§yùy¾Ÿr½”óò,ãò2×ÚÌõµ™ç£Gã“•|—·«–²÷W‘ üõbÝÏwà/y0É—ÙÓyW¯GÒ¸^³¿•X-zgzÌÃ#¡8~õ¿Å8 ©_ ay F¸“´pª¦ÿ §ÿn�¸]¾×€îðx‡üø!ø— àƒðÇ àÏÀ¿i�ÿGø«ð78\<æßD8ØÆóIb]¤9[àh¿Ð9«ìÚr­ìβ%¾ìÄR­w�üs:p°cË~8ø—% ‡x`yHø“:pðgË÷tàPÏY^Õƒ?[þ|à¿×C±ÌFÂWB»Ì:_~nÍÕ»qÎSñÉzƒÚ_Ö&8Ä;ë=:ða€ëÈm%ÄeëÓ:ðq€WñÍúJ$»)ÖÑCü³þJöiÒC|·~¤¯‡p˜ ‡xš°H¾›°BöŸ°K> ð»uà`ÿ :v•öŸðƒHx.ØBHî s·‘p°[™ìßv‹ìßö <ðïèÀÁþmoêÀÁþmèÀÁþíWêÀ'þ¿tà`ÿö–Hø*°û_ëÀÁþíÿ[¬ûOLÔƒý'–èÀÁþ÷êÀ‡~PöŸøŒì?ñu8Øât$|5ü7é*8Ø’ŽÜVƒý'ytà`ÿI_Ò×ü98ØÒ¸ì?I'.­ûOÖñ‹Õ£�¿^öŸ¬£ßÕ`ÿÉ‘ð<°ÿäoéÀ�ÿ©ì?ù:p°ÿ”Å:p°ÿ”ëtàA€ïÓƒý§uà`ÿ)_׃ý§¼¦ŸøêÀÁþSS"áù`ÿ©Å:p°ÿÔuàn€wèÀÁþSuü(ì?uDöŸúKø0´uê£|°ÿ´õ:p°ÿ´:p°ÿ´;#áxDQÚã:p°ÿ´18ØÚëÀÁþÓ¯Ñ×|»ì?½FöŸ^§ûO?¬ø :p°ÿtøY�öŸþq$¼ìÞj¸à:q);°•µÛ,ü}Hû¾ÀµUÓ®[0$¼ ï3Ú÷ŒJá}@x?¡}_H„÷á}XxÓ¾/r ïƒÂû¤öýŠdáÝ-¼· ïý+ú™Y» ¿L¡ü™Bù3yf ò\\+¼„wŸ+…ò])ÈóJAŸW ù¯ÊsÕˆö=KÐoÖ„öýj¯ð.Èïš<á]Àw˜~Zû¾D(Ï¿%?Ky,ô³TÒ?·ÿ¥•»`KO ï£Bú1á}\xŸÞ'…÷ð>-¼Ïhß—á=YxÏÞ³„w‡ðž'¼»„wA^Ë},ìuY½ð.Ès™`/Ëû^ÞZ&øû2Á¾— ö´Lð¿e‚ý,ô»lTxüe™à¿Ëû[&ÄŸe!á]°ïe‚ÿ_+ÄÇk{¾6Cxü÷ZI¿Vþž'¼;…w—ðîÞ+…÷á½Vx¯Þ[…w¯ðÞƒÂû€ð>(¼ ïÃÂû á}Dx?%¼ ïcÂû¸ð>!¼O ï!á}ZxŸÑ¾gá=YxÏÞ³„wAÿÙ‚þ³ýg úÏôŸ-è?[ж ÿlÁžB¼röëâ“C¨/Ná»P;„øíâ‘£^ø.ćÐ>rñÆ1(|â‹ã„ð]ˆ'¡¾pñÃ!Ô!^8„ú`¹– ñ¹– ñ~¹Ð~Z.ÄûåBý¼¼Fø.Ä÷å­Âw!ž/ ß…ø½\h.âõòở— õïr!/êÛåBü].Ô¯+„x»B¨OW8„ïBý¹Bho¬êÏBý¹¢Vø.Ô—+¼Âw¡~\1 |êÃÃÂw¡þ[qJø.Ôw+„öÌ ¡~[!´_Vþ¿Rðÿ•‚ÿ¯ü¥àÿ+ÿ_)øÿJÁÿW þ¿Rðÿ•‚ÿ¯ü¥àÿ+ÿ_)øÿJÁÿW þ¿Rðÿ•‚ÿ¯ü¥àÿ+ÿÏü?GðÿÁÿsÿÏü?GðÿÁÿsÿÏü?GðÿÁÿsÿÏü?GðÿÁÿsÿÏü?GðÿÁÿsÿÏü?Gðÿ\ÁÿsÿÏü?Wðÿ\ÁÿsÿÏü?Wðÿ\ÁÿsÿÏü?Wðÿ\ÁÿsÿÏü?Wðÿ\ÁÿsÿÏü?Wðÿ\ÁÿW þ¿JðÿU‚ÿ¯ü•àÿ«ÿ_%øÿ*ÁÿW þ¿JðÿU‚ÿ¯ü•àÿ«ÿ_%øÿ*ÁÿW þ¿JðÿU‚ÿ¯ü•àÿ«ÿ_-øÿjÁÿW þ¿ZðÿÕ‚ÿ¯üµàÿ«ÿ_-øÿjÁÿW þ¿ZðÿÕ‚ÿ¯üµàÿ«ÿ_-øÿjÁÿW þ¿ZðÿÕ‚ÿç þŸ'øžàÿy‚ÿç þŸ'øžàÿy‚ÿç þŸ'øžàÿy‚ÿç þŸ'øžàÿy‚ÿç þŸ'øžàÿy‚ÿç þŸ/ø¾àÿù‚ÿç þŸ/ø¾àÿù‚ÿç þŸ/ø¾àÿù‚ÿç þŸ/ø¾àÿù‚ÿç þŸ/ø¾àÿù‚ÿç þŸ/øàÿ‚ÿþ_ øàÿ‚ÿþ_ øàÿ‚ÿþ_ øàÿ‚ÿþ_ øàÿ‚ÿþ_ øàÿ‚ÿ þ_(ø¡àÿ…‚ÿ þ_(Øk¡ ßBA¾…B<(ä](ćBÁž ù ò/”ì{>ôQ(ÄB!~ ú)âI¡`ÿ…’¾æñwI_©ü]ˆ/…ÂxJ¡¤?i·0žR(øG¡ Ï"a<¥HO)âQ‘0žR$è»HˆOEB|*ô_$Äÿ"a<¥HO)ô_$į"Áߊ„ú¡HÒÿUü]Ò?_'V$é?‘¿Kú·ówIÿiü]Òÿþ.韯 .’ô¿¿Kú_Äß%ý_Áß%ýóuÞE’þówIÿWòw¡¾)âc‘0žV$Œ§ ú/ô_,Œ§ ú/ÆÓŠ…ñ´ba<­XO+ÆÓŠý ú/ÆÓŠ…ñÔba<µXO-ÆS‹…ñÔba<µXO-ÆS‹…ñÔba<µXO-ÆS‹…x],é?…¿ ã©Å‚ÿ ã©Å‚ÿ ú/ô_"´?Jý—ú/ô_"è¿DЉ ÿAÿ%‚þKý—ú/ô_"è¿DЉ ÿAÿ%‚þKý—ú/ô_"è¿DЉ ÿAÿ%Âxz‰0ž^"è¿DЉ ÿAÿ%‚þKý— þ_*è¿TЩ ÿRAÿ¥‚þKý— ú/ô_*è¿TЩ ÿRAÿ¥‚þKý— ú/ô_*´oKý— õ©ÐÞ-ô_*Ôÿ¥‚þKÿ/ô_*è¿Th¿•JúçëºKý— í¹RAÿN¡½ìôïôïôïôïê§ § § § §Ðt úw úw úw úw úw úw þïôïüß)èß)´ÿœ‚ÿ;ý;ý;ý;…þSпS¨ÿ‚þBûÏ)è¿Lðÿ2Aÿe‚þËý— ý§2¡½_&ô§Ê„þT™Üþ/íòúKñzÐ{º=¥ím¥û›šJ½]÷zº½MÅmÅ m %e%Îg麒 %kJ½Þ¦ ë6ÅŸqYJKJØÿ7•öøº1Ë&ü2¦’âkí®Û°®¤‹ÊÖ¸4°”ººn?$ -KOww'€Ò|mû›ðÀZ<`3^¼Ý]M ¾»påT]]‡§Ãç¡'YÎco]÷xðÕ^×Ö„“:è]wð£‰ÿ°c‰àÝçïn÷t¡†F<Ж$ÝÛ䣀¤–{ÙÅ4I@­³¡~4·wyiÚævßdÊßM/Ø«ë&$¹¹ï@ôÐ_vYНÓÛÝÖéoÁu‰Ò¯ vžêØüùô.^åýõ¸Ž d™î7„PÚé~¼/tÛéuŠ©xÎ(çe~§ç^<\T:�wÿœ/"Éà�啬ýÝô`~¼ N:¦(É_ 5Ž¿•‹ëZÚö3¨êò~]Í*dÕ8óå{AØÑ·‹ñþvG�þIwT,”“U47wÓK¯PÝÀ"‘ªŒ]†.Td!Ñ]y¥È•xš­rÓƒœ;C¼…¤¶Ë7§‘y`ˆÊEjd¾'àÇ”ëøQó)Ê•_iÀE]?Ï:M}EÚ<” "ÜùÒk7{7¼,œÝÜ +07z:é(í¼èÉTBŠP:Ë9'z2v4Y=Ø !+£§a‡KÇHÄNÆ6¼Dšy#¿ÖL¶ôBû³ÁUÅÄËŒ³pq­!i/ ùoiëlÞs £±«=ŠÎ©ÕÕíª[»­ªÊTºí&Óí0™n§ÉtU&Óí2™n¦‹bkÍžÀM-r[ÃEOW„;ÙvsÉv˜K¶Ó\²*sÉv™KFÅæ0JÆO>ž;Bª®L‰œ„EA´ƒÞé¢>dß0*`j_–GIÃKv”$4²D+ +Ѩ°Óï£2+Õ †i žîð3dÆS>I†QħÒE4fbJ×[º¾XÒõÅ”®/¶t}Št 9‘|†±™µÿŒKBoŽRl/Tí€æ@~#øD¡®{Ù5ðNBÊ¢z wê±ëp=jßOïÁ†C›¯Î«¼GEŸ*“aHq{|žî{TwÂ'߯›<çªdÑÄÇ~ÝÂÚ‘„¬Ž’´¢¦j{«§é._6ÌwÝXUwSçî.lÆÍc/Ÿ¥'úGñ¼¶x›JÝ’”DºVÄÐF±¹V†íó(Èhz @½À¸Ñ¡JÈîY•˜ö(”Ä%Ѭ˜µz!f”n,'Þº:¼<`k“¿l‹Û?Ê»ËÊkœ¦ÿ•;Ë®3ý^{q£DÓÑÓ‰½.Oó&ÿ‰øšrÀ¸6NŒWÕíð4µA0X»f“í–îâ²òu€Gó ºÅþuô“‹ºZúD‹¿ž~+ÛÀ?ÎÇÐPçßäß°¥ïR�ȽM‚tÝ–bº×}usÛ=ð¾×µÅ纻§Ë¿©¸¬ÈY´vMy·§¯]ƒ/]Kê߸…æ,jçYËœ[|eX浘{Ã:–^6¬Ã7Æ Oï/+ÛRFÏÅ(n—Q¬1…bA»Œcí–2V"¼G‚BÖm)Þ@Ñ›$(h=”[­]CA Š2eÐû$(D±–ÂôÈÈ\[Š×Ód= ¶[Н“a Ý'ãÂßš2IÚH££!@k¶”]''”€k·¬¡„¡Kç÷ú©^Ö¬“¸YÔ£‚®—pf°®ÝQ‰2ºf/õbθ]s/»ò³ìâ¸RþÀùÞ(Q^Ü£%²ÖÉ¥qe@em—‰ê #³v —ÌU=µk¥²\_Zd*’3%0§±~KÙz œSذ¥Œ¥ïÑ⹎s»¸G@äÚR¶QûcÚ¸eSCqºÎÉ•¦dàʸâœàzn’ëÖlY·V6ã5k+†ß*#Fe#*šg§@•-C×sô‹êêðþÆå:Ét¯¨«ëQ%O¾‚%æº$efòÔ¾QþbjÄÊ—õNÉ.zÄ/eœÂ•Œªléë×HD®ê?ÉJNGŸ€¤ÝyÔ¡� 9ç<J�RùæS,�s~OÇ¢øeä«ë7rØ CÜœÜ,r ‚’­²tHrÃÙ}ëdŸÞ°– ädÃ:©4‹8Œ&”r¸is[K …n¸Î¸·‰@!PRÒ‚o—Bd-DÇ(»àÛÿÇÞÛÄH–$gbS„DÎôt×us~ø£á.WìɦÞÿó¥™U]ÕSÃê®bUv¸Zð!222+ª2~&^Df!Ú…ºìaWÂ+`OV\huÐIº ºk¡›$žÝt”}öãÏ_üe›\ìLu†¹=wssss3óó—eäA´FÞ0,VNÝXzužxÆ4°I–j‡¯³ø’ù®a¼ä;q³±å¢ô]¸q6<ž1¬´6ûÇÇÒ¦ÛÏ„{ î••â Púål"ÝØÇ.ÞwÉþ§Ÿ%*S¦ûÏPRØS©Ïeûef ­Í¦ÇGé–+L”އ<]iõ@ëqV´žJë¹N\œðÊfsà&Ø/VžÙ³Ñ±‚-<( °¤ú*Óê¥ £œëWX¡¼¹Q×$FËʤåf] æv½™Š UÆé ˆ#/+·¤«[{à̘G~æ^3|' t?<mŠ–éßf˜.¡Q® ÔÚ±~Üœ ‹”Àlº1ždã@By,Ê § !ƒB!sƒÄ ¹Ý_.^M–c&û%W}lxé~%"?3H¦Ýº¾0H®ú¼9ë ˆh'3 ?iáÿÃj›þº¿Õ.¼ÏEé}‘Þ›cšæRŸ¿·$kƒ«ëPbRuëèìŠqLF‚²›€6c²zGPu`(ˆ‚WdƒÛè(º IÝà¥Âïœ4!)4L¹‚OFg\)øÆ…ª”8µÎ}¤]#› ——Íp076]{·éÂmæÜaýf•x3(%óšü0¶ÉhÒ¤ià˜õb2�ú6N4)VÜ$/d•½í?£ÊÊ}úÐR Ê*´ö7Žœ‚÷>sbú¾ÙÏÀúuò©² ¦Y5´üÔ$qHÌ-Ù˜‘/­¿?=i¸³‡1Y;ïŸQ%y[I|Ÿé1³á¦¢S $­φ0¦ˆ*)a{H-.¬…I_"|ñŸPEUÄÓå7f’V®î/ÄàŒÉˆiáÿô³˜‰ý±!`àZ¤H ÆËÅðm‹ÂoÂüÃÜbd†ñ[†qÔŸÏGÃy‹’ÊmCapaf‰AM•å¦?:TI9Y Xpëñ¢¾˜Î{1Ù žaå}ñ@òXþî‹A ™MæH?Ÿ[i[G®u¤NÏ2 dV“I“Ñ”IM@«#è82BJ•¢Ï­’f6š˜-“i“1Ñ$Ô¡ffUmE‰ÖWÚ¡‡+Œ'K4 tÕõÑ\«)™Î¬OI´RŒ¨ÔBã]Bš©–ù…Õ u|ß<­æcl: [Á#ãCGûRB©•ýÒ[»Rh`[~có¿´…þÓú—Ëþ1M2HtÆ‘θ8˜q×™|A’rÇkÈs[hœ×ZçãW4°Y+·€©jÆ™F¾g¸VP(öÍ7mÅå~.ŽÞ›nÍn?çµî6Pu;¿ÞÜšÏ& ¬Z+ UÿU`ø•¥òVÀMT*¤2;€ºpb3ŠlçãÙÜCs…ÒçVèç„ùêÂC˶ÒWhžèõó™,”dÒèb=HB&Z%3ÁIäýëÛÍÅ«þB¡‰Ao UQ&0idÝíÏ -S“áÆìØ@6<×gBÌž$^½,Xê—·š�èH½­Ç³¦¨Y47—ek{Ó²iafÜ=OÈ@òpo_¾QÎ$±·/ßkb3�ˆ§6¤ 7Â/’Ëqmó2‰mTnôÇÕ­¨ŽfÚ² ÉÍå|x* ï1&çc5 “„c>Í\æP&ëæ”Óºö\'fsÛµHÈêIÄÓ~Ø?nf}Ö…TY=Èað^žÊ ”ª“$ЪqyMQÝ^7ßi«¡ÚÉàN˜ÒÏÌóŒê'=[8©¿TZ­þ¢S?•Ò’ï«¿ÖC ­”°ñÓúËõÁáá‹yŒÑ„Û>èëE{ߌæ lÂ÷â=Ñqlªˆ’<É»DEO<zj5‘=…š¾Ž©¢ÇóáÅ{Ož?}ø —ì½øòàéÓg{éÞó/ëŸ=:xÞËö^¼”¿ó½Ã‡êÃOõн'Ïê—Ï>ê•{?ûú‹Gõóƒ/õœôI·ÝEBfµ¢9š ŽêqV›ñ–¡F‹Ìx^c8{bnÑ@�ÒÇ�v ,½ÏrX{¿ÉО1$-"òÞû¬Ü#[f/.�Ãñ‹†AEFê{ ™ƒ¡–Nöh™�†tå||6;mue‚ˆ‘cÙ.¹a²R'´ÚÎú¿\©Î20Øl±¥–u]ºeŸR}ŽzÏÁ œ‘iª›çýúlÔ�F6¡KDm,Ô+JÈFÔÕì»wˆ&$Y�šêç7¿žŒ°S˜©«Û_>NƒæÞ‡(„VQ~âþdúê#xÿ�´žœ@ÌšÏf¯Þ5}¤dù%°ü’&μ—äÁ¼oáaá‘~‡¯hò©ŠYÖJB<­1²`µiP¢º¼µFXJÂ<”¸ ¯Dè2÷'b‰Ó˜’ÝÖñB›ûG0t·Æu°¸‘W3„r}BkÆÙpâõ~&ÓžËÔ �àTŠÌ�o{Ih÷Å,›‰¸ ÑÁ‰4‘mk‚lé>ÕXH ÜJ´I¢¨Qï]ÏÈ<L$òg'"@H`õ§%* ¸qç$˜dÒg�;pÁWU2;O^ÕÌ:ò3„_�ÐobzîÍÈg´¸ÑDª¹‡0ãö^ÜËJé~%ø% ÄÏ?…}ø™á'±Ëä鞯’é$ó“*fñ—¥E ©Y-wšÖ‘MJï1Þ…Y|+á�—6ën‰ß!׳ORdÍz<Md±-mò€ÄEíW´Ò¯ŽßádÜbð*æ B‚ —ë–$Zk º-É´$ÑHt[â´Äö îù·¡É€ÎGõRÄh þ‡ ™›4F‹sš-´F2¸ôî8ÍžTæ~øYšàC1§ßÅ… ®ÿ®äL?#…FŸ¹ ½¸\i¯›ÛÇw6@ <òÅŸÕãÑ„¼¶æbØ´›R>n@^Ù›úƒÇ¢¤£Ë1 ïLó:ï u‚Gz¾AXŸ¼gÜ;9®E­TP$ç'ÇM}4Z4½¤JB3¡4±Èó´0÷\¾^$d@S žÍŒzKuf›™?Áï•m®’¿Ãa6^¹ÈÌÞþéʸµŸªíCöø¦±›¬óŸJD!©ÊÍã7Y¿‡Âþ€f·6U¾6UÞ‚A5F"º©^ ê)Bö= j‘®¤ÑºDTå:9e—œ/PÏQ¿®p=Ps ì”V$êW£ÓWm´ˆ�gÓ üôÃnU˜¤)¹,T! Ïül4˜ß7)�q£,:Œ2XÎ{„£[K€Œûon0¡@w¶àܰ*¥:Ìç4‚LõåºãÛÖ—‡‚qºwÇ]¦ÏI…˜éiäV(,×(, ¯õç˦:Ä—Þ,›/ë%Ïêv¢¨ñ Ruó¦á)C«·B–„‡¥Id°cc¯aÁ²Ì)ŒôÌÉ™ì;“áoÀþk,­•IsÑŸédõ­LŽàe1´Œ¬PòзԜ6“cº4ióÁ¹�«¶% ©UaMÎÍÃâ(±¶&£�êœgò3a28¶èË$ಠPËd €Å1ó’á9³m® cU‰‘3¦ c¯ —”»sÖ€2WFPk3Þ0·&<ke\µBå,k\´0f¬Œµ5b|%¨³v”­2þ yXægñµ?–²ÌǬ Üûò4¦®Š-X¸*¶x7ëâˆm+ߨ¶ÞŠm+ߡضŠm+âØ¶ˆm+à±mE¼Û@Â[©Uùf/B'«ÊÁ­+·A9Ñ·¦�[U [Q€­)�×”W”`ëÊÐuåÀÐUå�àšr`àºr�xU9�¶®(‡ç‹P;¤£¾½vøÎ=ú¼î/¦ãÑ€×J Ë©€-Ó4Î÷—®Ñzq®ñfÁîø×ÿ/}H šJuÅ~—عhF5(zä†ïÎ@n‚ ¦Çl%‹B€œº8S‡‹�K�¢ÊÆÕ.ú‹e8ãp(UE £¾êdŠ:h“¬&-¢ã£®gýcÂrí —J-éL}ë£é «°ÍÒôkr»@u©êe-^+I¼ÿiš$û'ŸÅÊM.okI“Y€q_ÙÞ¯q3‹Œh–oxËj‰NüPüÊ?7‹¦›áD—³ùðœê.P·F޼ώ–'-÷?MR§“†¸:xs2zÛs‰)íšzˆdWÝ}\ÏXàgEôsÞœsÔ‹~:ĹȪÒÁÓ©3|;˜ñ›`êŒúó^«äÆüõ6àª×Îø‡ê³ÁB~‰"{;ä_ªÂƿ쵚kA´ëdÖHª¬NfÃ%臚rô b‹ß}ì;Éê4î% uŒ>³($. .~‚þS÷†³•Vᔪ;ì@Ñ·q/M£Î0sW"ѵ2#N3&ž¦‚¿È"'€þü4HÉ= 6æýÉàɱóÝ|Ë"ŠäÃsž ’ñô¯†ý3š’DR°ŸT€â¸¤'ÆßÁKºÄžÞ錄7M:A¬[e)ÈÀ4 ¤Ý¨NœB5E‚Á’ùµJ&«ò”|…¼JX6+ØTV‰ñòX™ÒŠ#Ÿ—ƘGÏKc'­4¶ @GUq0ÿ3Sþ,Œ^xòC¤öL~ˆ¦gaÌlâ±0f¦Ý!Œ™)ÆÌô¹ cfºÜ„J<ÆÌ¹®0ÒÈgÜc•Žœ;lâ—æñãŽË™øíeÈ[Æs…/d¯¡L ù-Ò–ñô–™°QƒQä{ñ–©Öª¢F§Ž¤IËxê‡EÐJ V„õI­ì Z&Ò SÁm/)ˆ?^°¾óŠå„מÔí§8)‘E¶Î‚…«‘è¿9àôÛ«O•ÕùYw®Š xWûO¹-u2³x?Í’¶5¿×`­µ{ »Úë¶ðw¹u‹³”Z@PÖI 6U¬Ò¢¾aýEÖ­¾*|í×jX ߊCœÑAsœ9ŸÝ,çTÀeÌ üÄ^úú;Õ|f?E7crðïD´3‰üg4^©ˆ& ÿLƒ)Á€ÂÒH&òfRž•¡ F™ét• ÌUI:±`÷tfVž'Ý©ÅÀ" æýt2¹Zm‘aÝ1sç5®N‡\tdí$Y3¨§½ÖàèÂÍœ…{P/ùq°rLë£^ÊZ˜‘ D^×ç㷺ÕfÕ~šó¦&{iw¤Õüþð?ç}͇)6Jˆ!&~å]…MÓ·YDÖ K©<ëX?ŽK÷á\-S½‰*Ùê�Lµ'W“ å­ÝôƒYÜ©<§Nq´æ÷Qw‡fl‡åèl3¯/bÈé^°¬puÍJu%UÇû`ÿc]p°x‹šfjEUéLÑSTuuƒû‹É$ý¿¥ßõˈ4-ß ß5¼9“A]³ùF Z)bÆ$Qe¶˜æQP¤²ð´è‘þ"oñ¾‘Fr:ˆ4¨æŽÝõèSQŸ8BöÓºÖ…ò0ŦI&ýîPˆÏg†Îë.6‘ˆ„Î)öy²Ò"Ä#Xħíé¥Ò˜¥›¥ÛìÍ"^î»ãpàŠlŸP�ÿ‹e 'xcöô:ËÈ]¥ƒQ×(!ܱïv‘µ´PƒfrAc\‰Ú¹9ZZhÑeS òCGø%ðiýJE÷­*‰FÔêã$O R¶AË»Ëhý*ä ƒj—hÞ<EþÕ2,9L±V`†\ÆüŠÛòFܬ.kÄ×b™0ÖÐÒ“éêÞ2%ƒ•§Ë¹g aæq¢^cGœ'-;bˆkcÂ/»Ý.i‰-y§©Àn*·J]æk8ëK|Ðý ¦ýÏéK>h…/SÞ‰ pÕ·Ð;¬æŽ8™.F'ïZf…Ðúd9Qÿ­ÌHÊ\ü7u :¨8<6:Ân!“¤ÅydÞªl/MCRy[£ž[Ü¿í)`Bhœu¶Uð౓–ênÎÍ!-+xôç¡;ª•w]Юãy.3¼åÇykg¢¿óÛÔ¤‹ö‡Ï냧O{ÑÞóúù“Ï{1þûý‘ÈaŽÛ£c»i:Y9­vT?}¨¯6HIB%©œdt9ÁG-j“$¾€>Š[µÙz:Üö¿B+ƒé›’si‡´Ëåˆå+2_©a=~Xcãòt¸ãB ã÷‚Ȭ©ñ Xþm];`§ö ç|Ñù"õÓdrÜ-°Y‚ë„ÀïÄCÔIf³îuÛ¹Wý9NÄp ê IÖ¼ÕNÎê³þäT좠“ã£ÅôbÐéâø¨¬ÛÅ‹Áb:>êtP�ëvl|„£aí‚äàU§WS_IH™ ‚З’2„‘/R$ž|Ô’¢¡/ªF¼A5å˜ÐÐåva:Ûs[ƒ ,Jÿ¢åv+Τ¹±#œoà5qo9› çFð /þtXgIä6N‡Ù£&„ºb=¯=±i«Q¯}¯%vzv†gr@nEæ¶q¬w•Üf1j‡Z‚½=™;Äû /‹Æã'“1ŽtH%sâÞŒ9^²±ÛÀ·ÃAG0†Û¸É0fx©ðÌÐøÜŸ{fؽ¥´"mS¥›nÛ$!nœØæ¾ggð£Å|¯ûÎß÷´Ž§“!©ŽwB/i-ì´ãP÷ûÐ;žN:ä­ÒòD$íñ£3OÄd9Î5b\‘bÁ®;a_•ß^²ªÈ48ËíýǾ=Ò„¢Å*šYUõ~žÏ¼È½g‡ÿ'àl:èËA$òC÷3l©¿ ò9mz*~åñ‡‡YK'NŽåòt=Ã#=§a†Å«é²éã‚J3œ*Ëßé|º$‡÷´ÆÅŽ÷¼nøñ.»Ðádð.(Ññ ©×Úµ -•­µ®Â‹²]§fdÛè;§Àu^N†§ý.\,i¾É:ïêãÑ)ζÀyÊ8L¶ „³Nî žk†ÇCç.mõõÇc^ 0YC/¯¡sdnÆÄš‰� w€Ÿ;ž,Ȳ%–.^ ›QƒÙŽÚèôÕzYáÇg­ ˆ®9-Z¥Ë1+P¶ÖÇ ‘¢µÏ˜+ZeH½c¶èGfŒÞjg¡¤éÒt`_äOh fT\§C×LONȃ óáL!b²h€m&¤‰p¢Ìùó²w´r™‚9µ‰“m×>•u ï -úG8˜ñAw6lAi@d~œÂ°°Ö’™=é“^ö³†ðl… ‚tgÌ"òõPLźý||¾MÅeNµ‹›à20‹Üþ§ÔG¿óA4ü KØ5gÌ¢jÿÓÒæ ö0O—ÃÀz‘A£&˜1t‚"×nñe\>J8: º¬Äq¼¸2+|_’-ÍñT §ñãi°×Wdúõ1.‰Ý—î¹hóð¨ cö¯QUÑ‚ƒSLy1¶YLºÎ•AYŒ½egµ½åM¹,&ñæÛd×þ'ú¤]|C|oèÛ Û&},ÝÜ Uà6êã@W¯ëãVW‡úX{½¦UôÖõ±èé úÒ·¦s—mÒÇ9onÐǼ¸®#·YÇÅ}œdôqÚ*.fF–”HÌÇcÿ©7glT ˜3ßnT×FGÇtmdtDÃQ  zq8‹K¢—™ÿ’H¾aÿtØ´4»oO³UûŽš=Ð:™°±ãæTˆ­ˆXD(¯ý.5&öµÊ·p’oI)‰ß8äê±ÿ­žCO·ô×þ ý£ãþ»`è/ýþŒäVäí—(ÙñˆµKlÏyAì—÷\‡kú»aŽŸºñÜ׳1ÿ®æ-ä<e–Äû™œ¨þåéòá!- 9äueŽŽÉÊ í>œ· Ôá’à@†ëÚ| éÚO…Ýr€ÃšËÐõO£ŸÑw÷å'o™8ù³kIèÐ(+‚,~‹£³^–ä´öÅ‘­ÃTðj. R¿°2–’%¹_‹pO¤ i\úu€%QJhÅ$>û5y€³5\’Fø¦2¡ànΤ$Æ7ik!3h*&IšY›üì4´‘—,%æ¦|6‚/A6-H0îƤêL°UR,š…IL�¦u³0ÁðQU~ ÖO9o •ÓáÄÓÏÚV ™´+ÁÁÒ´ÜÛÔ YF¾çÏ\á¥ßÅ+æ'F°–ÁðRZp·"œ…}Ÿ½~u,öFêˆZ+ïá»?Û�?ÄM’ÑÏ^–aTRG‚,a8²ÿ¤û' ¥ðÝÃ÷Y–¡,×VUhåa4ðWþO”‡~u–ûßM/ ²*Ó›Ñk;­²¾X«¤l\¬[µ²¾X›’YY¬UÙ¬-ÖªtÖëÀŸè¬Ë…z«À,Z_¬ u!VëB݇ɺúTkèe²ºXÐE«‹5€hðxx4Zø®˜¸Ï‡ÇTÏnò³üqÂMŽ–Ix0ñîJ½Š÷šÔ˶ß_­ó;›5æÉfo eE´Å]ãB·É_CIYlrØPÂÏÍ¢#åÁäÌhrf69Wxv¾ØTpˆolz昞võʦçú|ìÄàÿ;Ô’²œ–=ìJ;§s®Ð›ûÛÛ‚áHëÌ[è¼Ü4Î:åZåfüËI¿ç™ò¯[Îìûã ðC|ṇ5Ïös®È½‰J-,–夯rè«(SÞ9ÕWd¿ ßÎæw&S(o…Ì—0 3µß“yÊæ©ZažêŸû²—oô†œ#ÉËMò‚BfÛW«ÀCàÏ H\þ^wí¿GëÍ 7yC—å † ½ycRä GåO`J*‡øwÝ‹Q§<û2 ïZ÷U]s3R*š‡۱əá½á_ð1> Z.çc�C2+:O°`¬q±‡?†ó¾Ìa ý¬ÍMSá `¾Ëi*VTÿlaK ™XP‚ÚÒ¹LD€ [53öÒ"ÛdR¤e¼É¤H˲è Z 8É{Qªà…­à„ÀCàzÁMX¼ßdã™1䥄›'ï;íqDÃQÆ÷F°7i7iïKüøìLîdg%‰%of†0|ð–~^œ.ˆ¸ ”Øçôs²>åóŒ>Ïùó…ÇçÖ m†ÈQW&ú÷QQ³<j¤r$Kö§\œ9ú°sl•)³gS®ùŽè–Í™ýÌÅM[EúêË`ZOð‚Êg¹†Ðà5ˆeÇ?èbÀ†li‡D&oiç „x• éaƒ ž›èx-Ó³ˆ`+Ñ‚nãx«¬†d.ji6à3ZgŠ,Åz€cßÖ,VöbìðT™¨ÝòvåXâ f‡ÁvÑ{ sÉ•"à7wÏɵWsE¤:Þë/^ûˆ?õ±1GÓ�×VãLW¾êMFcFÃIC¤[rðQä·ìÈÉ(BtªRe÷“jû¢õ¬ÂcNÎê/ hëÍ/GÇoáÞØJ3ánÁ–ÊÕÄÕ~ÒxW Wš%ެŠÀêÀª¹:°Á”ïD-C‚a­ð" a)ê³5è!°mH+®z¯…í†þÛ!\º¦TÏÇ&Å˜à®ÆÉ”ZÎv–¼"ñѧˆÆûW=­àTGX—bóÕ?#–99ö†=¯0&ΨÊÄÃ%¾Z¥rVH¡fGö |ˆJ ÔB"s(ÝË€€NO)·QiØËÚc`há9º®‰çlwF4¯lS÷[c¨ˆÌò'°7‰ŠÈÎût0'Ï â,8¹xœ,ðåѲ5_Th*e|Öëùº³z/Vå#`øT|ªiŸTÈ£x?•ÓN1õÚ¯®ƒO ‡P’î/„…'ØõQ;ýª……´Qz¢uщ6ÊM´Ih¢5‰Y}QLÿëÙg¡£€aXó@ÆO¡ù“Iy„Gyï½» ¬¬BÏQ­zâÇu}:YjÿöJá£ÒÞý¿?yúPGP<'àXB¡ÞDâp›êÁ¦£åÉpr<cŽ6Iåð‚ñÚP(?¨î­×ƒIçÄžÓ蜽hÇ£©†±£r{<åc8hµœ,›á1C$ª§¸—ÇÝ3óz«Ó?êñç‹1‰v–Òb1ækúþVúX6…ì>ý&oÞ8¬fÞ �9Ê [t`ó¶½ÓA ýFw‰€£݉'Ȩ9n½xyEO/åók9nv®ÞÇÏÖïãgÝûø·qLIͦ<¶çr®¿>zGBOÿøkyG&ýÛ½æÇfrÿþ÷­×úB'€öbôõ×òrMŽw°äQÖ×úpMÎï`tóõ rKàùc¼é%/þ¼>æœ�âQ¯T¿Åq9%û¹l7üäµNp¦û¤š÷?ͶZ¬@>¼öß�‡¤:ÑFyù¬4ح׺s@@gÀ»¯9¯V_‰©Ú úó9Ô"O#ƒ}¬X )1yêãŽ/J¬è.³ÛÃÓÞyxfð{ÂA_[Á ‰m=</Ûz˜í¾ÀYÁÇÊ{_RYÉßz}2ž?ùÏOS¯æÌ3ÖÒ¾x¿ûzÌI‘z„pg.áζ˜¿|-¢y˜gÅþÒýMQvRîÑ_ÿ5 ¿œl!4ôg%ŽèO}}ƒÔ½=éÁëcè1ý©žÔýiÇEeè¤Q¢¸‘± ýCºÃdüAnøó¯ž<™òèØàÑÁžb1H ÈgÓ£”�ô ¹.8Ýë6òh‚ ¿Ó_Ëq/.ʲ„iÎDþú AD,ƒ<\ûŒª—,’ŒÓpä1V⎡Ìp‘Á«Ý]9™<o…ž'œ6ä›/ÿx9œcÌsÏ<¥ñ ÁÀ«¿xøRî´æ&z5¤eÓÆ`º$½û—Ppþ­YLçxä[dÓEÿ,‡¤ÜöM$Ý9o]ÄÔçG8Õ‘ç$Ú9-Myîößä£ñü@š¹ù °ëÉGL>ÞB'þ×"¡ŠT>zbùÆ‚þÖ_lªí 51‹vo"jÏ9(~ƒ%!« ˜†÷¿.4ÏI//0QŠ|ÿÔ5ñóáÉh2|Èê*ǹÿ¼( !ñ§6&ÇVìP\i±;ϧc’¢3Ný¢“—dªåeLˆ™ß5ê >Æ|&¼x)ð"sˆ§ëÕnœ(ÇLS7‰úõrI¾”(ÚÎ^*åê5=iQR7h°ÐˆKs¥‚ö”*ê],Í©ƒu5ME Ø¥ÂAõµ¹è«!JA§Sþª»õˆ¯mûŸ xŸõƒVÇ´Væ‚y´Ï‡Í`>:"Áu –<M9G¯>™Ç{x†Õ>ǃ ê—=&ÓþŒ“ßôò ] R¾UGýù²yÅÊåñ³8‡c˜WÚsÛŸOgt©RÖ[ìŸ:ëuPü½ÝχgÃÅ0,CWÉ£:ô2¯`™š’¿Õû¤yIÍJA*�*.»ãKŸfþ¦¯è;.zØŸõ£Å;tÑ_û=Àƒ’Zs_‚ÜíV)ó‡jöå ÊS+—©äË¿!„ Æ—„B(€P*B%s‰„VdB8ïTD:¡âJfÔ“æ .pLÃg'½"¥q,<°«‡_ Å—.tŽñq*“Í.#"‰ 4¥“ˆ¼4 )ÅvIqzƒðX ·Ë‹«xÔÁ´Ç•â¥2™€÷@lk7Aë@*Ói NÐ$UD½Ô¸†H-Ã+’Ü+ߤ4óÍ¢ º³K­#¡Mt"©«H²u,j;ãE–ó]À Ö` òS&ßɵð5$j0íi®X..ֱРˆOKÃržxÎ`æ[ñ)_í‘ùmÀ¢&±ƒ\d± ¥Qä6 Q›ØP.²ÔÐJO?­SÖ",­"SÇ‘öU´â³R‘’(ZCBk =« ©ð´#‹›5‡Íµ"gÊs´ï"Q{9ÏSê’u,j0éy®XÕ§žó-‚öœ…?òAþ,4 êóÊÐ\¶ÚÄVMQÄŠ¦;@“”xÖh¬lÚU4jËwÁËwÒn¬â¡YtBVqöc}'±Ô7Š.ºº¤U;}[$j yQƆÕNÞ‹Ä2^`Âi†õÕtr>š/–ý]„½°ïÓîü�„ O¥j¥,n§óæ/@:Xª~âÛ¸k_¬h*¬û…SM•%í4ßò‘{ pª³pEö’Oˆ. …SíeAÆ•O=c¡pªÇìÎí6tP„n;Õh|#w>‘K¡¨T·áè:~¨å`3•j9Žyî@'r`?•ê;ܤ܅rÐÝJ5?±†ßÑ0ŠJu G[wáÓ+±ò—‘jìJ6ô·£K˜e”ÚÕ†·²„]PFª!9È»Ô@W]É×E·£ƒôÊÐ݆Þú³„íPƪ?9²¼¨%QƪIó4ÞÐÕP§–0)Ê87ürCgCíZ´(cÕ®ÔÞ…‚ÐßXõlžGúÛѸ%Œ2‰íƒbC;º·„ÑQ&ª{9¤¾ó¢ ÖG™äöÅ&ÝÕÕÇ%L‘2Q}LžÊ†nš¹„MR&ª™- ¿ ‚uR¦±áoR[¡¶.a¨”©jkÛ ‹ò±ÆXJØ(%Û(!Ý ð¦«8I%L”2U…l{„¤êΰЗT-xÛ(øB’Ï* Ì“2ST$‘¸P †Š3U:dÔÅ<Q D‹iRíijDÓô1 PlvI‘EF1DÚP@®X%&F. ™âÀ()s12>Œ^tCŹŠ¿v@H/WX“¤Ìõ¢tQˆ®x¹ÊB˜$e®.)ÜȰZÂ)óÊPD-¼ì°¦HY¨NTÁË. a‡”… FQÅFtËBX eaW¥Q°ÆGY(¹¤Ü…°=J³=Ê82z;,„éQšéAªÃøŒèÍhº?{4ý¼•⮹¥¨ë(ìÐa§=ÚŽÃ>ÝÉbÖÅé%l¡²T‹ ÌÚÙ¸›&/,¡²Tk Ìód:Í]˜A%›A@/â•>†C û§,Õ( mC%*`ù”Nç Ñ´ÚÅÀ`(aò”bò®‹V: %LÒéâ_º2Ù† Ð3§ Y¥Å6\€®‰ãðÓJ×B¡„}SVºÞ»¨È¶â°nÊJ×z'n+2QÓ¦¬rC®Vz0jÊJ5Kòb &(@תʘ¢-¨=KƉ%T·Ò³Àp°a\¤ »Ë2· õÂÍ€«‹ºËãd.H(€[n¹Ò±pñw°]\¤k¹+²h+.ËÅŪd]e[‘‰ ˜-ŽÍF.Vz×Yðlëòíܪâè.ö‹‹KÃ^Õ!Ý…ÞÁ\qb®ðã©+] yCÅ%ªÝªhU©„ ¼ƒâ’ÔpWUJ¸¸;X'.Q ]Å~qW¾µKü^•ø…=Tv¢}l—èò^%~yï¨%ÃE·]ä«Ô/ò­òPDX*Îâ(UuUz»Z9˜(Îb)UVv{¬Y¶ŠKuVyÚí’_¹Ì—ꬊ¨ÛŸvýr°X\ª&@UÝΫ˜ƒáâ2•ѪLº½ ×2ÆejT¥760vŒËl�7 61Ët­­*o¬1vË*CôÂãaܸÜâÆQÔÚ kœ‡‰ãòÔPã¸Û©–õ0sœ™9„Xv»ð¦Ž3Sïn‡BæÃäq¹ާQ·KîÃöq… ¿^¼Ô«\0|œ`N×Ð#%žÊ¥ëÇIü…ßZ¬ŠØØ:ÈÁÌqdæ¼Ç‘Æ _ ‘]yØù¶Ž+=…‡ô~}ø˜p@dé‰Ô(¤opZ:©¼Ì“Õrk Ý€Éá`M8²&ÞðÛ‘ŒìÐ|…ΔžáÎkÞȵ¦aH8Ù:A•“lãâ¼éë`B86!ð–vyE`=:ŽñƒáœEÚc L¾Ü‚Œ9“ 8ö[ r±3±Æ(æ7Ë džCŠv5DI8ýƹƒ½àØ^àç6S¿ð²ÝDw°Û 8®§~3áÉÄ£€l6ø>F–·;¶¹î`8¶ Ò’·tŒæÇíF{› b› u@rFôçÁ¦{s bs ­«ð› !—”§ ¡g@§TXïy/&Ö(¥ºr«”øÀრ€7¥ß¶óük?Àr_a¹¯°–ót‰5ninÞê)¾ÈðEŽ/Ð bŠÓ·Š_�¿>6•°áWÞ§ðl> ÊI£Òx±Xv‘#¯Zž·_`™®°LWrPçc"¯#‚>ÉñIO°åœÈéÙPMl¬¸•…âDƒ›öÉú€`á­,D@ø¢;Ãx`ý­,D'×4Ͱa8° W©ó+먒õÑÀj\¥¥Gwa—7 å*õýÕ¨¦âo ¬ÍUæ;¬aMý`ãP`‰®2ßåÂÛ἞b#½Â"]e¾»¥7;<ï=&zšùž:oxx®{Lô1ó}t­åáùm¨X¥+¿J'•7=ŒÓòktRù#žÇ=ò‹tªËޤ{TtɯÒiì·6¾z\tÊ/Óiì-Í\%”Ä›xIå©·36på­y±o¼SmVÅ:·pGÍ[x„òÂèÛÄÜUKÀü�F¥³ãtÔ,†s9 B";¥*Ô¡ÃÕ6&ôëÉ| ÃZØÆ}ª!Â/§“Ñb:„c%„ƒµ³„#SÞpÞŽ@¡‚6þ§§Y*0U¡¦rœEq×̱®Àº©,ˆƒ­ý¨ƒF–ƒaº©¼u“µQÀçs䤡>1ÎéïAÿŒ>@ÿJ½ÛgIgönýöÏfA”P,­ý+y) Ï4TØ5Ë ™,3d<ôöAçØUÈX¥C9ß°‚ ˨rž 7ls¼ÞznûI„åm…ÏGs2v0%,ON0À°‹*oe…Ÿ±ŠÊhHk‰·ô*F #aiÙNÚ�ÛŸ›¨` UN½ Bw6dÏ:ÇÆ*II­À™ .ˆÿ©©ª{‡¤vpÞ^ˆlzy^v6EŽ6ÅT{'I³t²4Í×þ—ŸþGq?!‘>˜Ÿ6h(Ã3vߛͧ³á|1ÂÜÁ-Zm�®¿œ.çƒaOÁðYÚ O´—xDó%΄r‘ª}M“d¥@ßLM~6ìÏ<XØŽûo»`9dK$ŽNÞ}‰œ Õ÷Ôq*dÖ‡!Íó1ÎOf$’ !X‰•…‘4Þ3ä™ Û°ªt.úgp–Œk"ú×öD}ùPŸBùâ!“ ÏJÁ7ÃùÑ´j‘¾ x<jPvм› ¬DžØ9tÉ­¦ãáÑòôàt¨ÇõôbCŸƒ.Ïüþ`e,Dr²OÊ7á¤ð`AŒ%CMÅé~ãøýÔc8qþ¸œ9_ûfûøŒÏµ‹²{6“Äæ¬mSþýÒ¿ð!wüÞ.æý'¸@Ò ©¢ìT‚ 4Àg“U›¶²I|oM8'ò}kœ*žž½×ŽÇNçC¬ƒéé„d꘴JâVÉ\¶Ò.¨ª@Ÿþc)í²…Œo¸ï›‰ë¼=Â׋{zÌØpíæ¦£Ž‡9?˜ü­Î3~ŽôkÓwº. ˜Ûº'ºQúòŸ XrŠs fŒºzÔq#ª¿UMê g@ Tð×Ý™¶¡¡ƒæóþÇðÑ`çuŒc!ºÆ¸Çœ\/Ð~E|í·á`gìävþïŸó壷‹áòŸšåLJ}oݦzxY!iå-v¹o0žûG±z2cÄ1u¡Ë\Þê—…³ñ¶sSéãS½'ŠþM:Â(þ<.§Œ”ù£C¾Ÿ‰ô“;Æ ]Zv€‡‹<¨7èu nËWrò˜€ÈГ“<0Ø'½h£àÔ'eÿF>¡ar †?©?yñò°>xð)Ÿé÷Á“ÿ ó_Ÿ¼øÅ“¯z‰üxôøàë§‚(ÅOô;}øŽUŽ&“}´�pŸÁ£·5éñlz6š kËiH8rÏËð³�à 1ÒX´²Ôp( —F“µ#Ùƒ­ðm#Tʇ­õ½`¹Ã´TùŒŸ‹ûòšM©O” Íu¢¯s @ÊÃv/`Ýõ­ %úÈ*=ûmnÖ GŸý‘²òÁÁçœášx§<$`ÙÇ<Lz€›*€nKùØúm`Kvì8"ÿO)þgÀŒNrÆwYÀtðÜ´Ü\^—é@¿˜dt-c‘ÌX¤>­w`ë+.Yp j ø·õÕ WDA˼Á´ä ¥àüR,Ó©Þe]ÊÏDåXRk)òHff€ÐÌÁ„ÙÉP%ëÚƒÁâÅ$Ëvÿ€ùr6Å™Y.#‘Q!s,uÔL˜T!Ñvp§ ܤRVÆí 2š€2»rMÞ¤Ó|IÚkó3£ÿµr$¤$gï ÈZJVš¶F aÒðaA´o.^_mЮÒY2N¶‰†ïÒªór²C,؆¼ÖĬ;‹äŠÄþvMž‹¾œJŸ¥ûÇOŸ>|öÕ7õCšf2 ¥fíXÀ2*`]ûOyb’ã8|øÍ¡é0ú ¿ž?{òÕá£43ñëåá‹'_}AS|ýððëzi÷wýàO¾9xÚËúàëÇéûœ¼xqð'T|øˆtN xù³g/{e�yø³ƒ=�ˆŠ^ü~úŒèˆ£�òøé³ƒÃ^ ÏŸ}ýàé£^œ„?{öôÑÁW½Xˆö?…Z°íÁÁÃ?"óo­TˆýêàðÉ7ê/ž“¶B¾]�¡<‰…Zpë‹G/D+ÅUÈC%B3þ–š^ô’XFèç2Ãúg/©²'‡Ož>ù»<�‡?{ñˆ4#‘KD<ü‘AsT >®õ±vÊ2qÄy¨VyàémGäXàŽ†¯úç£é|5ŽGŒ ¶H³Ê¿ÔÈR®z�wyšx¨Ý€ìêT»{Á¸«E`Ô.µS«ˆ#“Ýié˘º8Bdá³6O¥eú•´cf­ø»zgíWÝEõ×ÅpvÞîI é×B¿³'cv”ÍÞ–LUvÕ) n@ßõœ¦‰UbÉ‘äí_ÔbAš¹ˆ­ 8ÇbCÚ]Ðc¶RÛ» ]\œ¥nMh'ÝGurn<à Yc^—á)Ivßò«7£Óµ`T”ü’¼ÔaÖíÒ…Õ”VÍݹND‘”Õ~%™×¿ªùcíÍꯋ`eÈÍFiMÇæú0ºUÇÏø –Ñ­‡êð GZxY‹XÒTT[„ŽE.õWäNO/Øç+¥z^¡<ÿž± ½É¡ßÛŤ¤4¡]Lk ,°<›ÐžÌ§cƒó^{¡/<ͧ5U±¨å5x–»(ñZ}2ƒ8ï`?:’ON9èEö"ÂqšØ*(º1¤Uá!¦¸áÆæð÷p¥ý…Þ1½Ž.³¯;Ïc†»T™Æ’xÜ@ü_WI±J>âž¡´¨�¸…‹ÙüA+­à†´Â¹ŒÐ\Ë¿?=9AŠö¹Ü�þ¾ˆÍ\Û¸g|¶LX™.Œû ¬»ü‹oÄ¿°*iPƒæ¾?`æYé¯IaÌ¡1˜–!»‚Ú=ðÈš¾]ßÖÚý.M§Þ ½­Ì£÷àÝW˜º0žÈ/\)\7§íÁYƒêåódxñPC'rgú\{Ù¤ý¾›ó<er¾O]\’•8×ûÕ§oZŽ '3ëÊ ÜöƯ|ÿAUÂÆŸåbšßÀ3ÕÀ(öèE轂A~°ç•Ü¥þ@îdê(Ý%âqåÔw@›ø^§ÒÞ\ÛûÐ: ¸?>ÝÃ4Éû»)Úàõfµ»ßkæƒÞLëúÀ÷¿~udŸÝ€A'ë1Í,ÀéÌÈGêa¼€‡ÁÃEÕtÒòÁ&öc‰Bü¸Cø9 ™µÏ,Ú3­þƒ bñ[¾¥ýÇšDW|pœD‚ø.ÿÊ?iÅãÑ„¼À@L® `ý¸«Äü¢¥†…à¨Á<¨°˜WÑþ¦ä×X ]øåsu1íË_kgÓ=Eyhëá|p"Õë´bgé…J·LqEÄHËJ[³ô¾âcóò©)¯ëÝxâXÕ¿ŽbØ´Í—ñ§¼ÝƒÔPˆßð íǦâ.æ£ÅÐkL?c­ñ*í}/Û/¬Ok“?$0¹€áÿ_ög³áqï±i8!ì³z>öõ˜T2¨7N®–ý Tè«…·gôÛ8°z›)ÞM£t<¿#rDô®C¯‘�Ä;Õ&Þ·e·„¬ £þM?žYK7G¦Š}ÂRt ;9�²KXJíA´ñÿþà(T¬á“p¯»r ¯Ë÷ñ]ú¾,Ç/TK}–ú¡æ »–CbȤîŠք美Äù×Ù%KW=ƒBã9õÃ+gZÐÉÙtOJERéTLµ>˜ó&× –—,õ0~(�uÊz¤J&|¥(öJæžX::U¹ÿ@çø )ÐoKeÄiËD3¨æ°¨¸Ë¢ø´Ë?ê§®Ó¬µ+Ó×§‘$ªJ€À¯Êâ=/Þ¦p´@¦kQ~^aâÈ¥eª·I£? ²¼1¶‚q‡#ò{xÄP¾/?mª}——‰¢Ïg¤^¦‹¶Ù__LÛé¬ÓÖdìC®gæ%D*š)³½ךLZ•'„gš:~.ŠÞz!Lµ&LGÞ„ú³ZryMùœ#¹'lN{h#{WMÀUSä&=´êUÕþ›*ƒ®2DE„¡â÷‘,`–GoŠÇiúWæ· j·+î‡Ó³cnÖXWf: -©AøÊÄÌ82÷æ¥Ie§ã¢°¡ƒÅÂ&¶,_ÂØwQ~úsöNi­i–“úõ¤_ ×ëo`´«4tó믾~ùèó:\f¾{2ƒÉ 4>Òb:Ȭ±<cäOw·÷¬+$ŸíÆ^_¡>lõy.âɉâ`¿è äŒ(à o76y�2ùßÙLë-ÉlùÐÛ8 KÏ¿»ûS½ðƒp .Ž?{†$Ûp´^-†ò#0X¤…e"ù)}kÖŸ÷Ǫ²mA¹ÕYLÌX‡�Ô øP¦j¢m$áñ’U‚̆eJ©ûɶ ¦32 ­ß;yûý`wc®‹Ä‡ú6—M/6ámvýÎV¦qž?/lZÇ‹ËÆåd49~)Ou7ˆ Ÿu?ÿ‚›~Qàuݨûˆ~uÈß:mÙr«ëŸ×éƒ'OZW‹ópϬ!˜3õ–¾ÇÕ¯MüŸWhááZ /Â^¨;Ã-¼h[xqõ>Z0ª«+~ûx÷îŠß>ÙðíUiþù†oË+~ûr÷ù%jèxøöÙ }ýÀË–.4’DâxŒøްàdpdóŽŠ9‹ÒvíóvM®.Ç~ø^ØŸ¿öã÷Â~ò^Ø?/ìpÜþö6ìS9Á‡abV—XßÓ¿w}o±ðþ*>Ú½’Öqk“z™ø®Æúðw¼£±µcU¶¨›ä6Å·®u§vÎϯ¡ÒQùûww|Ö]Lõ+y mÇW᪪,-.cig9ÕòËÈãã“øª ¾ª.ãÅJØc+jƒlØvñæ»xQ¦µ·ö§1¹ºL�“¥Uÿ@Dëß&·²Ü¹Ž5\­Õ±[*šV*º_Î.ŒÆ ÆÚ‡»e£QÙXýìñhL<Ö¾Û-!—µw IÓ ÉšãÆóCÄ IAçìràPùûolåeo ¾öNnŒûxîÉ ý-5ä;Ak©êR3‹“¬­ÛÙOuMOüÔ´¨WÜF{ÍÜÚ:¯<ù¼i—IJlçyÆãþc~‘®ÐQÏôcݬ V:Å>êÁß÷’‚iø0¨‡ ý?Ç ÿ­ÁÓ 1æ"Ïà;ÞSÂFoÿÚ…7ª‹ < ø®*óD7[M[—LØ9¹ùxËÍsñîL@u0²3-vùíRxl…n·<šçkÈ[½ &Dm­‡2®Ï´È“²µžÓõèW»Ø°Þê81är×�Ë_öh iò½_<Âç—›åØããT`ýlòtÊaBØaya;,÷Tdìt[»Mcº›Â2õò]³Ž} ê{c™©rt:‡Á¶|¤DÎ 1_OΘ›_!5?šO0ÂG|³ò¾mam]$–þx¾¯×”Í$ |$Þû“ èì¼’õàÆ˜ºC­óQ çónÅnÞ[Óz«Øû‚hx¦ 뾓râFûf§nµ1s¦Nvü>¨÷‚¹¶W~ßš·_ûÅæ~4p™mE¹µ xweÛÔˆ¿nçü<æwßÎÇk»¤¶§Äéf濇;­3U%ˆ”LÐ>È^»(TÞÐót˜<&ž2p,–­pÃynßפy°“ÑøÑ¾cßtÁ Îø%JÛw‹{­÷wn{Haç,ö«ã^žïŽÔ D#†ÑN Þ?Õu'Ç~Éfªý’�¿þÚ*¢Üi¼µ3:iE'ð|ÐlbuñÏ{k¡{‹"´tOE‡ëÛc-z”Å(Ùý ÿàeÒG02Ò]kºl™P]¨Pâ‘ÚÓë÷îBH‹áÛEï%mHÑ‹>©§gǤÒOÏ}œ÷¥íüpÑѲY+ùHÅã¥ÙIÙR×{©b¶…ÒôÒ¤‰'¡'_êT¼É@ HLUû¼ô11�9,g _g¿¹Ó)»ÍezÌCÔýbµXÚ`?`K²»B;yý4µÁ£ûÍ:‚tZ ]ƒý ûQ·Pz£[Ûz³Z,½a3|Ko:eB”x [ˆêÊ0©CbÀv¿X)ÕEW õnç~±TõŸ xÏÐÆpz¥àã–Õ+%w§«k6VÅ<X)ù$àÄJ‘tÎlÏnçtk¦KPŽï³‡?x'ûÌV|GºnÁ^ÿ¸~1$¢†“Áª`w|uû£¯uË6V— Á¦÷JI°·Òk¾Á*™¿ýå“Ïkžþ¥™ŽgÓ nBÈ÷bˆý�8Ï4SÄÔËIP(0 5¾ð‡ _úã t1m/à[ù‚rhÜú¨Sùo¢X´*ÁKK‡ðŸt1ž#þ?Tî´õÜäñàVFwKî2]é.ü×™—¦mºeÜú¿¡UƒaaïÂï.¢Þ-àé¯%¥ætÒˆvÞÊ)>_úÛ›JŸÉA­N íßTÃjéJ ?òlÛXÅ¿³±x¥Žè l¬á·6nêE«[6õbµtS/%´©kÅ+uü¸ÕüdsùJ-,;:ß»¢Àý0Of†ÄÑ­çØé‡…ø}êMÅ“öˆŒÿNP¼aÓÝÐþöÆF6 þ–NuQ±˜¬‡vdq Ê/mȃÚå7º(Ëf¨ÑÈÎ mø´Ëª"Ó2ÞÏð…‡å{U™×Áéúû[Q«åß®FòÅO·~Ñ9uaè{›Ñ׎ñt.&~ĺ¢ô‹@Õú’ßEÉ¡â?´“O›D‚™®1º“ë>yèºs]C% v÷±iÇ.üÞc¯i»?xÜQµÝBnÈ4÷††X×ohH´m·àãÇ­ºÝPbÓw§¬ä‡;’4ö2(!¿né"s YÔƒÁ+ÍþFÐ,ÏZib„ÛðSü™Á—êë¶ zâµ=ÉZ‚žtS‚žl-AO¾ž §XKЃÄ>k zÛg%AO\íç ô O^jI’í zÄ'’F}‚žÌ`a‚žÜ€+ zжKÐÃ~4µM7A§ù3m5Agú[²› 'já=±ÁWô$VÐMГ¶õtôdm=Ý=¹¬%è)¬$LÐÙ~ð’Ž$7ß‘ g%“϶=©&èÉ|‚žØ'èÉÛ=E› 'mô$>A'ø‘=±kôD›ô7DãlHГÿ›– ‡ÆAîîmHÐé†Ü_Y‚$Ê£ËôÄA‚ž´› 'ݘ ÇhXIÐÓMÙÚIЃDAº‰ëô¤HÚƒë 9ôWJГð òì¼Aºž÷OÐSüëHУ9„NÀÁ zpo£Ài¾Wº– oa…ïHÐS๩ÂÉÏ;ôð½«2’GÇv$èÝe¢ÏnoNÐÊK¥|{‚t ,ôqÙm zJP_:i®› §Á.®'èNù»#Ahu™<ë²#AˆuÅ% zò zºàÜî=8žT>Lî¶=èR¥¬ß’ 'Þ‘ ]­²Kô¤Ûô€•׿=ñî=®Ú˜ 9ƒ>EþéÖZ‚dúY|´|C‚žÆ—M zR dа-AO$P›ôà±;$ò‘ä0›ôà<äñáɶ#A(Ž•â] z@x¬„ïJÐÚãBñ¶'è‰ÑØ:°#AžÄC:AÜ• =‘·²í zÐ yë!Þ‘ H ÅÚ– ¯ç!—bmNГ€ü4’7¶'èíi¢X[ô€ø43¬m z@|Zè›[ô¤ >uúêÅÖ=)èÏ"CÛœ Ôgªà¶&èñY¦HÛô€ö¬0¤Í z2Pž1åÉö=Ï#ÃÚ– ¤ç‰bmMÐÚóLÈÚ‘ Ôç…¡mKЃg�ÌGж&èÉÑ"’çˆw%èAdùv;ô ¼ŠÇn{‚t¡(.IЃ…¹|v'èÁ2Ž\>WOЃžØ‚~µ=èS©Zéj zÐÁRõÓ•ô`Ý/JÕTWKÐ{ pª³®˜ }wª½® wªÇ®’ ÝvªÑ® –²úþ¥ z`3•j¹«$èAo+ÕwWIЃîVªù®’ Ý­T^!A’ÿ|ZTª ¯ Ù>-£È>¸,AO lÕWHГ]uåôä@/ ý²=°ÊHõçôÀ’(cÕ¤WIÐƒÎÆ‰á_ž ½U»^)Aú«ž½J‚eììƒËôÀè(Õ½WKЃN'‰}q•=èv¢úø zÐ餸r‚X'ÈêsÕ=0TÕg{‚ôm<I´-A:•ªBÞž }IÕ‚ßœ æI™ªÚœ ¦I™©ÒÙ– Dgú�Øæ= Øì’- z@®X%hcc‚%e¦"¶-Al’2W±Úš $çÉe z@¶=y´%AÈ΋] z`Š”ö Ñ–=°CÉgG‚Pl/mIÐr‹lw‚Ðk¶Ç¶=0=J3=v$艪Ëô7w…=ñô¤—'è-T–j\!A8Zª5p…=à.›Aœugg‚0¹TCà’=°|ÊRçà% z`ò”bòÄ—%èAçœ.þ—%èAÏœ.ü—%èA×ÄÆÉ/KÐû¦tºÞ_– ÖMYéZi‚ô®J yW‚ô­RÍsI‚t­*|€i{‚düù´K&Ý >u‘.ì—%艫‹úe zRàf†»;A^Æt-¿,A,©’½,AÌ'fKyy‚t/Öåûò=è`œöe zÐE1WÜ% z`¨ ¡ÏUôÀFAFŸ+%èA÷’äÒ=ñe zÐñD—÷Ëôð›ÇºÈïJÐKÅYeW‚˜(Îb)»ô ¿©NÂ] zПT§àÎ=èLª&ÀÎ=0\\ª2º;A —©9pI‚t(³¼$A:•eWHЃNeÅå z`Ü ÏôÀÄABŸËô GfæìNЃþ˜©sI‚ô(÷Áñ z`û¸Üwj=A ǘ$ß• ‘øKµ#A:¹|>E¾ž7üÊù®=°u\á)ܘ †…+=‘»ôÄ»ô 09îçSäë¹R‚t¦ô ß’ †„C¢Úž &„c¹†.KÐrEÚ/KЃ9“ í zÐ6 ’j[‚Ž ‡4Ú– ö‚c{3 mIЃ°ÀÙ€6&èÙl p2 Í z@3[œ hc‚$ùùÔ±MÀÉ€6'èAšŸO+68Ð¥ zb 'È·“ú|;;ô¬dô‰/KгšÑ'¿<AÏJJŸâ²=+ }ÒKô¬dôI.MгšÒ§¼B‚žnN$&ºZ‚KìsÅ=Xx+ \!AÖßÊBWJÐIMÇ_ž £‘fýÒ=èoêû{y‚¬ÍHësõ=X¢‘×çj zÐÝÌwwW‚ô4ó=Ý• }Ì|w%èÁ*]ùUzG‚¬Ñ•_£w&èAü"½;AºäWéKô S~™¾,AO|I‚žô’=ù% z²rg‚¼:»3AO|Y‚žôÒ=ùŽ=°Sª\º] z`¯T…mÜoIЃµ³[ôÄ›ô`| 5•w$èÁÐZgg‚X7•·n® ¦Òú¼_‚t¸4 Þ• ýC …›KôåÎ=àì1¥—$èe„„>»ôÀ2ªl?é’=请‹.MЃNÃ0ºb‚tÌ©´#AŒ¤Š$dÏÚ– ‡Ì¤•=ÙÕôD;ôüÏë zân‚žtC‚ždk‚žt{‚žt[‚žts‚žts‚žtc‚žtS‚žtC‚Œ¡…‘º z0\U±!Aú|Š>»Òó¤ÛÓó¤[ÓóÈ£Åô<éÆô<é¦ô<i'=ϯ¯¦çA¶¡Jž…Üœ'zïì<x*[“[ÏΓüUdçÁÙŸ/Þ”'ë&ÀIײ󨙯 ;罈ҽÙy²Ë²ópÎ ¾ò¹1;O„··#·™¸Ùy¢½«dç‰þª³óDü–ll‹ÞÎì<|Ë=^=ç¸ •V€µì<ü hÜf—d牷gçÉW²óäî_KvžâÒì<&oá°oÌΓ†Ùy·D~µÙy°žnÊÎó£þœ¹¼µì<œH,‰ý¹!ßÏNvžd5;%õAvžbKväüKëÿÛq3˜×Á‚pÈ ~HXŸ9•þåøh8¯§'HŽ3ìƒ .³³þâd:×ÇÓþòlQÏŽ{Ÿ¥6ŠÓf®Ìâ¦}3±­ ¦QŽÓeÃ`=N<Ô> gñý‹óÒÍ0ÝÛ˜nˆ³­§Šþ:Ó AÛ¦¬m/I7”üu¤J¿Uº!ÉÆ#I‡:¹ÈVÒ AµÊ³šn¨ü¶é†@G¾è¤"ßtCº!äU#G´M7_žnH²ê¤’Oio%Ýïëû§Ê8óO›nÈò Yº!·#ÝPñ—M7„¥K^ZK7Ä™ˆªNº¡hgº!^2²h%ݳËÜþåÒ 3ä‡|¢•¦·gðI®˜nˆScYÎÜo‘nlË«¿^b ÖóEü—K7”\%Ý,±zÐÒ e“nè߈tCù_Kº¡â[¤âD]ÈìÜI7mL7mN7”lO7”lI7ä$ûJº!„=.I7dYZŠ ÝP´)ÝPtÕtCÙû¤Â’Sð¥ÍMé†â¿²tCH6#ïüwÒ q¢ìJé†ò}{éf=ݧ"*7¦º'OÝ ‹Äeðê9 ,³MÉ·¼äø¿ˆÌFÎö'$$÷Ïô霹>»ÞyðÀú_¾äÅŸHª˜qðNß½ÕKÍ«iXJy‡K2&”ì4•ü¥Ï\£ÏJY¤Žö¹O†g'öLè÷%s%#ðyTÚb͘#E4)À*;æÊŽO<ákY> iáÏÛå`5{޼ ¤Ùs¤¯·.úMÝg"—X_ÄúPo®OÂÞ3¶ìI.]»$}íG­ë]ªç™ü x²ú…5ÿ=cµ¬Düxð•X$áeû"¸Ü î¾®§­¼.IP3.Çó»hÑš\/|<3Ò|Z†›í{õ>MÄò>™=ª*†­-M:oxLÿ»ƒsÿz^bî$ ‡¥’w!'á¯{¬"7ò[¯'#“‹ðÞþhÑ{¬Õþd# á+â·Ú§©\bé2Vœ’Þq*ÌkºÙzM¥¹Lßx—ÉýûKy©Úçßí)­ûI1–‹ÐWÚì*u=¥Û¤æÉ½?aSÉ_¸¿g‰A©1ãÆnˆ3,S÷ãï…^Vî}¬8}o' +–Þ¼CÛÍÀ½"S”o#'°ª÷u¬n‡ŽUBË–LÖ©JbíÓ;ïR%É~’š;U]âKa¿»°8ÞxQIþ^>Ô?ð>TRì“£¯þS8OÅ6Ï)Á;­äÚÿaƒÖ“Ê«¬ÿïþ×*ô¬îv=«4Ú•=ïxUäÒÃY7*M¹ùMîè$ ])8Ó¶}»îFu½¨ÿbÍ‹"¿š<ci§­-IšovG:Þ6}mÏwÝé:"ÿt³×D8rþþ57ÝvŽ/#î:‰ŠÃ\%'\_Õ H\0ÈžÂ�(MTê»7 Td r^,}e•¾´qsék#?ZÞÚ`˜T—Ûdºµôõå‰×ª÷ß20ÝÏZDâÑŒ„“a¹Qs{@½¦¾KÀvB(¹¾Òë{®Ô:í{[ $WÊ}Ð]øÜÜ÷–ÝFÈen|¼\i…¼RáIP"Í+*œùd¹ÚNæcu(9ñ­Ø�Þ5°¶Qìg]¸¶PîK.˜»Ën=N©½·\©¨Ú—W;Û©‰¬{±µ| B)Ùõ2híZèÀE#Êã»ßd¤“‰NŽÀ/—Sò/R‹vñw`õc°Q“k <ØZhõ·ëŒ(•&ºwêz€2úŽ +…>òyW±î"cþ=â $6¹X®–$ÚÂÇÒª—t—Z#Ÿ,W‹ü ˆ9E�ÝxBÀ&çGܬ׹ø‰y}Yk-í¼¬m®V‘Ân0 uW±Šå-1žÉªà¡É*õÓ·ösº²E_Ú�%Un½¹­0Flͧ‹'°Ô¯oœyÆß[�é·~|n.FcMòê'àMÒ‹dâ1ІåæÒ[%Q¢=¾µä7k˜úîAX”ùîaxæ»r«ñ¦OTø–φçÃ3Í9k-#)‹ÀܾØÔ·& ÃËèšbŒ¡ÒC<ø¬³gà¿'Ë-Ʊ51§i §N÷Åÿ¡¹1ÕJcެªZ¹ÎŒëí£îèúñP¢`d^ie±Êœ¯Œ€VY¥•Ý ÎNÄ,ŒìÙ%ŒŠ~œø!˜Ž –lÑÂÒ�OÛHLÅÏ`Ö nÄ€…rëf]“Ò‡t’wnãW×´ù~£™š1kü¿aÔ‹Ò-idÇpæG$õ–ðuD D£ÑЦ¢e6ð Ó“u£ÏPkÇweB–¼ˆqZÚ«TcXò òcB{, ÊƒN$Á~Í èN¹x5YŽŒØ‰,÷cÃL÷c}ïzf L;w}a\»ö›d§ ¨Ø³Ò›yqªþßÍ1MsÁð“ú¶¾@%`?£6÷"utöƤ–ìe'ý4$ËA wU¹œ›\ÝUdƒg¶ º²?7ñ2tƒÛªr÷¤ IÁfB®ð“Ñ™‡;ƒß¸0…âm‡´sd3È›êwÈ@#Çü¸°™Ew›.ÜfÍVpV‰_F’ftÚ ÑÖaLfy$MƒW÷ÉmÊýh`+0ÑœÔâòø¯¨.¹‚e£¨òŠŒ…˜ ƒÆ‘Á9w„ 2BG¡Ÿs?øTùFfCLB@LÄäZnñ³;¢@Këí§' wõ0&¡qçý3ª#kÅëþg.|FK±©‚œZ– cz¨D} ©$Q!„ÔƒíŽ6^á‰ËtŸÌ¼…¨‘Ll2$~ð#+Âpùb²¤øÇV<^.†oÛòXËÃÊçÚ!Q„ß4„£þ|>Î[ŒT1nCmíúØ ¦ž`IpÉvH’2~²#°ÈÖãE}1÷bç<—8ÜIlÊcê?{-UCȤ w”î¹VQµ#žkv«%cŠ7 QY1™M™Ô´*‚ŒŒRÅæ¡ÕÑÌF¿ö“Ù“‘Ñ$Ô›ffõ´cžh5𺸚+<–Zhô+ìFu}4—Z’(òµdÖ¡$Z©E†•$d†$8,䨒ù…UÒzÂq¬µ™Ör©D‡^Ð2Fd˜ Cñj‘ô$²1½#….üÃðëITªBû½ú—Ëþqï0Áqž^ 'kñÓ+ôù¯3î"!ó$‘-7Îu½Hb[éïž5ÎàÄœ[@•’ø'?6d+h-—7mÕù¾$ǹû¦[w±/YBoU&lû Ü­ùlbÀÖŽôÊ>‰«vM·u$IZC•¾`¬ õáDçRBfŒèoj}6÷ÐT¡ô¹‡ÙºI˜¯.<4o+}å…ò糉@J[tçqfcÌtî&|XCˆi.^õM½Ãrc¨z1!CFéëÏ ­]ügdzº>k’©r{Ö¼ˆ¥¹}z»  …Aïœëñ¬i\ú±]NŒµ©3ïiaU+J2y ž™9pãq'‹=ÈØ“ÙÒA|õÚ™]Fk?5½Es³?ž(vp4Ó¦ ïJ̇§*»éùXmÂÃÆE3ç™d¡·©<}ÇÀ·ýgTYµŸˆá÷ ÜÌú¬ 2€HJÚ(§Ç_µî¼U«qy?‘w ½n¾ÓÖ²HÈ6JÄ{¤`žmT=)ÚÂIõ¸PÏÄZýYẊ1Ûúï…Q 9µ ¯×_¾Àf¬îîRÅþút@º¬í}3š/p¼¸§[¼w°©¢’*âåv‰Šž<xôÔ×並/‡ctçC<Lõ<yþôáƒ^²÷â˃§OŸ=ì¥{Ï¿¬öèày/Û{qxðRþÎ÷>ª°ÿ]ì=yV¿|~ððQ¯ÜûÙ×_<ªŸ|ñ¨ç„¤OºíM469Í;š ŽêqV›ˆ{ò£“ãyÍ 8ÅÂÂÆêœp1@†(‹Eï³\Ï_Òo2›‡g I ˆÄ#þ3ïq˜ÆK>PL Ÿž(!³0PÖÉ.Jƺ2~r>>› “¬ÅD³-¸]‹´¦Ñr;ëÿr9¤*{ÑV[¼—§Vš}IÕÔwž*ׇoeê¥WjýúlÔ0ÌÁqU…¥ŽNR˜í$>"IiÓmñÙÛ×·¾žŒ8y½Êºóõáã41pÚºEaA‘Æ—úÌoRz­u£å)Úå]á¤ô¶}:{õ®éëD%{0Áiâ`çÌ{‰kçá}Û¿Fégôr‰n±.k%  Ça0ÜÄåâP yPQÂ\:i.Èa²|�è'bÓ¸’éÖqBSûG°pSµ6u°Î“/3d„ŠwNhñ8NZÀh2%–Ù£ª#$)9/ÈÛ^˜1Kh…¤ƒi$ÙÚˆlZTÁ¶üI´I)•Ü8Ñ¥¬Dª  ½M LuóQZÐQ´R­[žwN‚IBFb" jØÐÖÄü<yU3ïÈÁÐ=Ðoâï^™9ùŒ–8šMµt°Â¢�Ã/î•ê=áW‚_Â?üLñS˜‡Ÿ~Víy÷{¾JЙ’Jót‘õE >Pq§iØ4ò¡Š»# i¥‘÷?kKÚP³$à°fM"o¦Gùéµ-kiä×ȇï°Á½¼Š¤ÈígU·$Ñ‹5·%™”ÄlnKœ–ÄjÏÞó%OZèåF»_ùåG¡‡Ç²›—’ý™Ê&¥nÅH ZgöËw_~§¼#ÙÝ)½þõ“¯j*íÁñ—q¶ËÄZS˜ x|i 6@=ÂV©ßvd,ù° (ÓÄwêæK˼‰Ë'·äÛÜž4ÿ= ¿˜<%DZwSaˆ½ûó72Ï4VŸ’EšÊ^ßgg¾ç£÷æËHiÇä¹yæ+©¨îÔïŸùZÈ`½J-·Î|5Ø_”ÃÂhPeT ßÌXœ“æƒ]£ïlÓï%ŠD'W–êwÄú1ý0ÓW»í;úpÚ#6ÓgYÐ`æºíeeØÜ>¾û³édˆÏr´ögõx4!/¼¹6-ÿ |Ü,€¼2 0âx¡ö´€N!ÉÊB`ij¡Œ‘%�"êãÕÉ1/ ô=ùûx„æä¸©F‹†p]`ö•æóyžb‘‰÷4„rú(IÍŒ:{˜fÑæO�XIÇâ´ïúveàJÿ­X³)vT7 Þd}ðÚf%.”’{°q�'ëøPø­A¯AwÇàAPÑŸƒˆnEjJõtNÁ= j±®¬‹D–®““vÉùõõ›¡IɽiâÄŽ 3êW£ÓWmÄ�gÓ üôÃnU<MÉ+JeÃñæül4˜÷Y ÀFh4gáa”ÁrNdê‰p†Œûojˆ’úýäRªã C&ÚúrÓn¾¾¼3¹n£@‰ÜëS'òTϳ.…~¯²­1ܱ¼ÖŸ/¼(O_bx³l¾¬—<­Û™J ÆƒlXHÕÍ›¦g·7Ð Ù…¦»þ€{˜lùÖL·üQáhÂGBR©[yí¥µ2i.ú3­¾•Ég†êáU‚⺲‡¾¥æ´™È==¯*Àùà\€UÛG}…T}ýàóAs!÷`ôè*€£�êÚûdÏ„É2â\æ€Ë6@-“m€Çv‹³åpl·7[ûÛ7-ý囀½±]Ø ¸ÛuÍ–¹2‚Z›ñ6Æ ô¬•qÕ •³1´maÌXkkÄøãITƒ eü4ò°ÌÏâk*,™gUàÞ—§í¦ŽØêá–ŽØâݬ‹w ¶­|bÛ x+¶­|‡bÛ x(¶­ˆbÛJx ¶­€wĶñVl o¥Vå›9|¼YìV•C¶®² Ê,ø5å�تr`ØŠr�lM9�¸¦¸¢Ò(Ý �]W ]U�®)®+€W•`ëÊA rx¾µCõ7ÚáÛk‡ïÜ£Ïëþb: x­,,2›Q˜¦EÌéGž‚`ÉÿCߦR[²ŸMRm5zd çsȺMN„ìïi`Èç™�K�Òʆ±ñÇ<Ûq$‡ïb¯¡rÔWm SŒ@j@^AÕDb{WD‡‚o™Vn8É TjHÿcêY_r)·rìš5ýšüg¤EAÜ£š—>nŠ['©=ˆa-/ë¶–WAR{Q]2½O’Ö¼! š¯ï!îA –æÄįüWãfÑôÏa°àj†Ë‹Ù|HÓ²LP·Vûæõx†{)^ÅH »Øà‰ƒ“ÑÛ^ܦÏÁ9¼ù·rÀUwç¯ø‰ç³H‘5çÄÜÃsqqŸl*;8÷ƒ™œîm'Ψ?ÇÕ1€q£?E½ ä—*¶3ù¥m°ÐŸ¢ËÞ姪±ñ/ù—ª¯ÁOÑ['³Fk¶ 8³á¶ÂEˆ/~ãA)'èIV§q/)IñÔå}–‰`÷šÃa¸Òs_®ŽPaì-¢1¹ÇáÓ˜Š‹p+?‘[8Ø`¨'v‡fõsŸ@eŠS¤±‚äjõhÞŸ ^ TBdÒß Hß<œ’Øóä`˜>ƒ«|z›èj7V &›³£ÐÜï7½—ü—Æ–íéŒD¹ta,øœú‰J¨u T`]œÊ«îŒÀbúJÅ”•:B±y•° Æ‰z~&£�¨¦ð♘ÃÒJ'¢ΘÇÐ '˳ g»tdSÕˆŒCæøhä§Jó@‰Ÿé/}Çc!?u6Šlf¦ðY63S/"›™)y“ÍÌ4¼Éf¦—´L6ñ²pG6iýÜ˸ï*›{9wÝä1ˆng¼ ÄO»­<Úã&™½Ûa˜Ù›^þ2žRÂ?/~žäõ}y+}Û _†'`™W칤#{OHâ·ˆ^Î/õañÑsiWô2‘gØ&z’¬ÅËÚwNYpxerÙ~ê ª#ÄÕgíªÖÔv©AUaÓªW•]\g ‹Üx7üO¹)u@N°»¶1¿±d­µK»šë¶ð÷¸u™]µŸr„ßiwlòøþDß[(²nÄo«ÿÚÔ–6žíZ¼­CÓžY_%I-‘ÀeLü´DE$þ­ÏƒžÙOÑÚ˜(ü[Ÿú$ÑÇ ‰Täó†¦ÁÔ`@áù$‰3Yv˜:Ë&äÅEu”N/ØDù…·¸;ŒEL° ¹®òÈô)¢×´¤wx˜‘)”dÍ žNxôZkh`ëzå×õA½”ËLíÂN0­:)& rvh ³®ÏÇou;3­r’>$E`Â.Cauá!©ÄßùЉLUâ1ÖNÕîIÊ‘š¸—‡hÔÌù\¸ü\,SŠ Ù"Lõ(×’ å­MõƒQèQÅÔ# 4£î€â ‡rèk€çõñE Ý V®®Y©.ÝÏd?ñ¨koQS†iZQUþÆN$tb áîbIÿÆoé·ÌS$!.›6¼g¹Z¸aBKEƘ¤ºöÛ¾YÔÚ E*¶�' H.á¾QFÒq:ˆ$„/'ÝõÜ¥‚ºÄ»7?­k]5©K%Ï\\ …' Dç3CÇ*œá�‘ÐùÀ!„‹.òÁ¶òi0�Õå’]\"ÙJ0Ñ»"ØwÇá¸áDµ\Øü‹e ?ÌâG `j¯±€ÜU:uµözÔXK à$/É‚ÎpGTDíÜP9é ì</Tž¢±ñ)RÖ•“¡xMœ8@Ê6˜ywÙa@¶ŸÉîúÇF¨^ë‰sb Ïÿc–sðÆ &ÈeÌaK<’7âuY#gX&Œ5Xv2]Ù®TÔ¥Üs¨œñ†»½Æ8O†D•Ow#‡M–+/‰%¼‘•cל›¥NË žÕõ=èìþçô!Ÿ¬Ã‡o¸x‰–ó”€Þ•5GÀÉt1:y×r+„ÖöfQ/ÃCGYû‡>ÛZ‡GGØG"d<ARVwŸþýi@jÂûÍðÜ6|G:㲳ݲ€gž,áxs®¾*)`xúç¡§ª•w½Ó®Oz.3¼eÇyka»Þ¿óÛh²Ø>¯ž>íE{ÏëçO>ïÅøïô‡ÝÝÛ5’,¡q• ÊAýôa-oH‰£’JN…2º´á35µÉNÙÀ?»ä ­ÚÛyá¦ïÿŽfÓ9vžyã;KIݤ±¦}E¶kÆÙàt@ÝéÓá" 1 ÷ù†AȆÔxÆ1¶®o];`§Æ ç|Ñù"õ³drÜ-°IÂ7¥Ã˜Å†K‚×Fmß^õç8úÄýK¨)ÞM\ïßä¬>ëONÅ& ú8>ZL/ŽÀº=¼,¦ã£Nï°n¿ÆG8.vˆÐ.H^u:u1ð0u—„”Ù ‰))³A#5âIш@-)£‘jÄ1T3Ž)ˆ>„Ø2»0mÌÖp ÒŸ·ÌöÒœ’âÆNq¾Õļål6œwøLÀ³é…�=§Iâj¹iÕáõ¨ ¡n…VÏjå¶Õ¦×¾ÛÒ:=ã[ªL-ØUÆùíUj›Å¨b ööd>îÐJÌ3¼@(gdžLÆü$DH)²õŽXÌ‘ŒÞ–k �ß± oèÈ…ñÂË„ñBCJ<nÿç…ÝHËRR5iµyضÍbƉmù{npæ;ZÌ÷¶ïü'žÔñt2$­ÁÉ’ØÏ°ýŽ£ûïC®>Ûâ©5Z}µá¶öOÃd9Î%ˆœe¤S°ÃöW%Á7—¬¨0 Ørsÿ‘oŽT è¯Œ&U–¿_wç3/nï×Ý¿ïÛ?C¢a‘Œ& öØß‡ùœŸµz_"~åŸésXŸPŽZGFcç§üÚš¥«_¼š.›>.5ÃY/öéÑù!O~5µÛÅ&ø¼nÞ¦gR$&g­ž Þ…E:$$1õzãË¥ÂuT~QØ¡Cש™6x‡ .й9žöW Ä–æ+Éóþ >âØ Ü§ŒCe«@¸êäÐ45-ŸƒáñPÀ9GÌ@_}D}ÄIh†LÖÐË„Ákè›1uÄŠf"@'È ‡èΆ' ²lñšè«a3j¸G¶Ù6:}µ¡°ðµÖ Dל­Ræ˜([ëŒc–HÑÚgÌ­2¤ß1cô£N³FΗ‰€Ǫ̀hf°?ò'´ P³0:q2Ä Ž¡s¦''d‘‰ÑFùp¦1Û4ÀÔgüV©ptÌùÓÑw´ržy¼Ÿñ¡Ãk{²š ˆüµC$îl؃Ò:ôeTß:@2ÉùѪv¢-”A”îlàžÈÿÌ¿R«øB¸ŸÛ‚Ï@·Y£¸Ìªv\fyFîCûH>høÍâ&0nΘá þ8³ù‚c?ÌÕå0°adب a!)4>ƒyí6ß¶æS£þIØ–™ˆ+Wf&?^#ŠçøZ‡Ûø5؈ë“gdèëc\ýë/Õçd6 ª3æb¿ñúUÕ.ˆ0(±ÅÔc›å¤+.P”åŽÆ²ì,¼·Z“.' ÏŒ¸ö¿Ò'í:¬,Â;È.ú–ò¶Q9ë{u”³JÝfåhî ʹÕÜå¬}_WÎ*ƒ”³hí Êb¸¦œ‘§fƒrÎyŸqƒræ Çuå¹ÍÊ9.6(ç$Û œÓV‡1‹ðz é“‚Ï¥þ3oâØèâ=œo;ºëc¤c»>>:²± ­£z <+R¢™C›ÿ%¤’¼Åþé°iéÎþ è¶ZC’ßQ#¢wBz'ÓØ!xÜœ Á9ŒÀ嵌ËÛˆE ;ù¶Ô’Ž;Ü=nJåÙÙ1”w@ãqÝ~¦s£tÜN ûé_Ýç'ÁÛ)àŠúš’%Fq¦³ú/pì¾³ä¿þÿì½K,K¶&tO uÝ[÷V[·Õ@!u«Å€–»šð·{œNDî|ì *_'#rgV EFDfÆ9ñ:ñȽwÁ/`€º'H ø€Ó# ˜0cÈ„9j!† Xß²µÌÍÝÍ÷£ªûöí«»¹ÃÖ2w7[öú–¹ùúð:.õmõh1\/Œ pL¹›IÛgdJÞ2ùOM/rôÑ[Jƒò®ØgÛs6A˜¹ÇÙtã˜SZO¶RšS,ó_ýûÆúæü‡</ãXð臣§N—?×È¿5I~©’›¤Sš-•BšÃ¬Ž»‡y7 ˆaÏâ‰1Ð6F-Û¤° ÏÕFƒÍÙÔ! "£ÁJ›ÚCY¦cMMfí1NæM =ÁÀÕ\MMQ Ûô5™‹-˜‘‹iÂ4þ¿ìa8û37çô8ݘ߶Qßøv¼ Ðösé-$tVÕÀžËrä gfù¬+ÇÑÓõÕUûS+vcpàÃ69¢…Roøx5¬šGï·CÛ4~×ÚçP i@RãÔX©$•7XNåWß?O ÉÉÇË<¦¡ïâºù�WlÃqã͒ɶ% E"´GžãOqðh?AÝpy\`›¶ä¡2«È½Ý-ÿõA_ñÀ rñå{¥¦ÉâŽ|*ï[Êõ°‹g)—¾â_ÊË Æ³”ëtS_ÊeÚi.å2ý4—rÇñ¨¬Ú©¸uaÜi.婸õ¥<?cÙÈ.îW#{Ö—ræúR!8™>Ìve]´Ïo¦W,ǽN™=–èõÊ´§;#Âéç•Þ/ý¼ÞûÍKB[—š@~¿Ê$ô;vХώ•¹ÏµK™rÊçÛAÃ�ÇhZéîÎ -h:Hkx”Þø\£Ã´À0-Âê0mÌÊŽýÿˆ»º›O-€xƒç2øB ëкUѺMáÛ¢p›Z†`µ¥“ÌÓÐ2îÊYNKì_ËuÄ€U=Ûï;|€+Ä| ˆ©ú_n>ÝLK:!3W'ãÅ2s´›¾_o\ë,WV"¶¡LÛݦ²o³²c×|b”ªùd"ªšO&¡oÍË-qi»¤SÁµó9J6Üe]8@^k5pTv¾¨Ó}õÏpGÝ N‚ïÌi–%€83W¢L±P (Å@.Ʊ`8ZÊ+ä™8!=ÒouâæG­™~ø¼CÑ$(gÑ·æü[Ç�Ñ›5h™·¾|E„OÚq^dºÉÀ1ö¡ôÐâÓHøO!,+ áéh¾Óe‚ÅBrj2;8[ˆM!w¥yy¼Š„·´Ú}¢,ð ŒHèGËÄçt €z~Ir”er…äÕ€»³óeÃ.ÂßMyQáÇ'ôxx:4I憥Áõ3ëlöŸçó©”:§Ëø-èx àPîþRúݼãL¬½þD®'DÊׇƒ$ä˜ãÍÏïä9Ý$ •›´,ÿn´Ý?lÍm"º ;•»'¾>º°r6Ðy˜GÐÖD^7%!se.ÎM%6Ðx5\"ÒίÙ= ÁÖЈ%f Ñ»±¶±d`°=ËH2ìrIF9I£BÉiN—! {Vãõ£<¨:Îå…Þ% ˜SA(ò5-;ØÄ”ÐýpX\´ð‡ae1 Ó‘#jÒHakU_®"5ù�WhwŽÐÜaúEÝ9ç›b›p¹0¥ a¾¥µйp¿2üêg|­î¨%| IJñ÷3˜Ð¨Ñ¨AÑDòJŽ‹Ì+=ó[{2»š·§,@Œç' ‡åBp™~T¡®=?Î&ïáôè²³Tê†_%z¥ªÔàúFdÝ)·i¥ÔZVðd­eÏ\þ¨SÄmWB±¢WË­zÕ[Û„µ „þ’5î¿r}ºܼmfšT`Á_€<®è å‘ÐÌ,L|nªC­‚w`¼çš˜Æ8N¨K-^•GŽÌá½™³—öíZå¼@Ö0çŒDª¨’*!'°D!p‰äÅ«ܲ"èâ€4Ÿ¼!¿†EÏI°ÈBìåÚr´—ëøuÛ3)tüRåT”vÔ y‰ÒŽž$ñxC^ ÕÇ¿éx¢èxõiÂiý½†j2$…ù¤ØuÓ‰À»µ (Ž¿hЇΑœ2êFŠsé¨5zêW») ?-¦$ÅÁ>Äv‘œ”oSíæeJJ™Ûƒ¼Ý§Óì;oÇéøzM§ÑešýE‚ÏÉÿÖ|º¥äØÏÝý<4m‚˜yö\S’ÐôðÛûê«d1jŽÛÚè$¿Ä+cZ_kôô´ÿ>í>^‰?—ÁMÈÁ+þ]s#,$ˆeNãšceœJf&‰8ÃOÿ†ƒzüÔÄÀ¿1_dÿÔĹ—{þéH(+„ìÁ½ß˜¬·|ˆÄ7—ýBÓ7 ¶LT‹?FÈó›ÈÄÙpù?[`Ä]ç3[§wú?UF“üÉh§õû¥fß­0" S‰RDü Qîo(ãs´Ì°€ÜDæ�áOvëÊmôzÜ WêmþDoƒ““Õ‚¼+½ÉŸJ̩ʟIÈ}Þ_Øx÷£ùÜÞý/a ð¶GÂÛÎÃÜšÔ‰AA¡¦ý³ñ—ñ×cŽU £[M¦È+ùSåB1—þÒwï²o˜ûò,T „ï6È߯hÚmoš½S½äãvNLL˜¿g³·ûߪFéo˜4Á«ÙDØË!ýwmµë±ývMð&5vzk\ÖÚ{‡Šõèjk=}‚‰×v*eSÞSþ”¿„Ô>R¹ˆÏ5i¥xTKƒþó@3)™vzz~øfxs2¸½¹ìiÆ~ÝôÁ“áŠ/¯g†ð¬">E¬µŠ$ùò¼*5Œh cñáÍ›áåÉÉqxtuý#βP•§×Ðß^œ\Ò}¢ˆ¬=lVÅÃ7×7Ž6Í€„Ò²oÏé¿Ó“›“Ë£“nÅaáƒ4™‰+L/o/ôF”ûM¯?8¹A‘!?mȃèÕyïòׇoNHqrˆ+éÉæ–ÿ€oLûR£òÂØäé‚£åqb¹YŽ‘ÖÈY^5yYLÔs5¦zòxL§øÚwò0œCLëg’ìóG‡ñ£û+eYá›üýù„ƒ¼Pn‚…iÇg:Ð?œOö â öA:èRFùæcŽ›¹1ÂþŽíÀ<”´k}=å!IyŽ61¡ÌÁi’E?$iŒS&*¥ÌüIšü`¦ÿ?5ëDæ!ëͱD IÁ\ca?®™mµ_ZN¡Ÿ/qÒÝðI™ tÆE{énd<üüi-“êFãës|Hr³dñùf2Ú¼#a4ù~¿Ý•+#ÏWÛDÝý£Ž5þöVW´ˆÖùØE½è—î"JÎãøy2]Û5Žº[1³xšSÑ’ú9êæ …¯ƒ“´œôÿ„ÛAZà—õEÕ¥œ…•? ìô¤™Ÿ§sò”©èÔÔùgîeüÄõÓ pýˆDÖaâ_w³­QÒâÈÂù'£—%šÆLQ(þÆàŠŸá3n¸I2a¾*éˆløýŸk°Osìç–ŒHã¾ÿr8|ZîÇCœAÓmH}+ ‚®’å!(9‚@c§}õg§½ó¦<Bì°<7€>å×_¢sô#Hå¨NkHˆŸ¾3¹àfÚÌ«´ .d‡g8^Vz"N]åÄëv7™­Ìd=ðj"ùD|؇Û÷KÂK–˜w¤" ºov”#¨ó Ž C/¬$‡UbD.«Ë rYX¦ þoù!ÊÀe%± â–Õ‰e/·N,S†þ/‰eJ’c‡X&DÐU›Q„ 2Êu0 …Lýß%–‰ll¹± ‚çz‰el Ý:±Œ%¨ËD¶{׉e¢¬X!uýÄ2–¸A,ÛPÁUb™8ðËㆹ|SÊì± 9~br6 ®ËÄඪ<AJšI£Õ‰eÈ4 çËÄx;bƒÊ)Á0bÊ…Õ@R… 9Ræ bÑ5t3.±Œå¨Ë$±X¦ä'ªË” Eub™DÃz6ˆe’¼…X&±Q­Ä2©md!–Iƒ±LªƒS‰eRZè\R;0-±Lš4‰eh±®ËÐ Õ$–Ió±LZxˆeh!©ËdX&+¤Jb™L¹j¾Ö·/Y9¿X¾˜Lá×–.&KmÕ,[L–Ù ­Âí`i„þÜrÅd6¸ÙŸ[ª˜Ü†ÿÚ2ÅXæ ¯-QL|÷ ‡'&×vø…C“ÇÒQ¿¶,19h¤9˜JNpQg`¡‡É³ FkÉar;? ™Ž Ô0EGïc™aŠ@ïcya $?WZ˜ÂÎí%+L¡ä_[R˜ÂÚÞr©“KnŸÉí¿¶Œ0…–üÏKB˜Bçu‡¦ÓñðÁt‚LGkZ>˜2DªÃÓ)k–|0;¾-L‡ÌŸÙâI¶L¸ÂÓÑ@è>˜Ž­JÉØ¡–&Цø¹ÒÁb‰²Á\‰Ò«^1’¿p¹`ÂdfH*L€ð® ¼, S/Kƒ—J\«’ †€XFŠ!šT0a¹°ºT06ºiIc1‚K† cI— AÆ{¨PÁØÐú5*˜ÐrƒU¨`BíX5*€3µW¨`6ÿL… &R²š’&ÒÊ) Þz˜;V_¢ØOc±B…¦ . ¾Ä*c@›ØžOÁ€JÈìU‰`À'„o„~" Å‘[˜8v Ö^"˜Xk[Á Â¥ Zmx†:%‚!X<—&.,LâôPS/ ¡‚À,nÁ6¸©üD0Œº…†Ð‚Ñ·Áp0Z‰`’TrTˆ`’¬ÆÂ !U‰`ÒÀZé3ˆ`G€š¨J“–-þD0„*ÀHT%‚IÿL"DŸO‹LV¶ùçÁ }V‰`ÀH¤wù"ð—¨B˜NÏ"‚i‘ifLP²ºD0`)j'‚I‘™Î”´D2¼Ld^"æ2ñþ- ³)ÕFù‚ÔC¼]òÀ€sÈäý»U˜Àb”’\D À* X‡ÌQ{‡4D P5h`À8Ô Qƒ4DfÑsh`@4dfo—ÔCFZÒÀ€}È\îÒÀ€bÈÞôÙ •ÅÒÀ~‘WyN¼´°40'ÚyIÓ)Ù™- N>äci`:vÝ·,0ËF£,0å¢û…ÃfCS‡†K¦µ-9`»H»–0–9È¡€ t‘¬QÀXâ ’&°hÒRÀºh¸0B±*ŒE2%³K`7 B‡Ù%(Jfêͯ\f—<qI—Ø…ðOhx¤+Ä.8>£Ä.©Ä¹â•ÐuÙ·¹µŸØ…@Ý âí A£/ ]!Ȝ٤] Ó5´+„ËB³5à¡]:3þÕЮD.høí @jF¨Ú©¯Ð®DåjòqÚ†¡lp*í Ž¿è\  +‘B‡a%Òõãà È$ÍÅ%ÁJdG¯Ë¯w¬›!,& ;Þ×–]… ž¹ôgB®[ÇC¹UbeĬP«`+' Bþ,zÓ—’Z¤•Z…‰“t§A •tšÔ*IP§VIµJòÛ¡V±T‘%µJ’Taõ+µJâ TgåYj|Y²ž(-ˆ2«äµP‰U,5ˆðª$y7TxUþ#ë’mO0´*©ƒ‚ÚiUù…fÝuhUðÓª¤N¯ý(­ ¡¿Ð|]V¥UI“ ­ŠßWZ•„Ï“øiUø#/¡UI„4ÆÐª$¶ÎЪ$ÊIchU’¸V F¡ùÊ¡U‡‘‡V,F>Z0ùiUÂrתB«F#é–VtFY®E,YUÂÌR³ÔYU@md^ÏÔYU@qdfÓ:« ˜ŽÌNkU…ÉŽL*¬*aæÐÍ•Æ(ù±•W$d,ù™¼"Ì’dÀ¤åKÒñŠ€B)4§›J^°(}¯H–B3e+?n“9,J˜diE¢Ä9ŸX¡a*&e¦£WZ‘0ráÒŠ0;“}^X{^¹shE˜³é¢gSC‚ê^ç€ÈO+„NZap²¯’>B+âÐ8Å%­ó8ùlïc)™A¸õM<ÖKkÍV'”¯é–ͦ«“Š0a”¯ù–Íæ«“Š0cT­Š´Ñ&ðæGIE::©H¨ßz8÷É\óyHE:ÍQäÍâäŸ"AÄÅa¤"ä×DBífIEJÖª’T¤“T);:Êêd); g™…ï HǾšqI@:YíŽi㎩sG‡¤S‹ó_Ž+ÞWCòù$ .cKºŒ‘Tóò•¶öÃXñ¥ŒA§Þ“‹fO.<=™ÀQ£'CVïÉ,«õdÈ=ÂFOfa­'CÖìÉ6{2Kë=ÂFOfa³'C\ïÉ5{²‘¶1VA…±â3{òß0V|‚±Ân0•ŒATe¬È5vç§+”6ðc¬²c…žˆ>ÁX‘cE`Ã}Œ±"HÝñ0VY•±"ÈŒŽ“…0V„r„a¬ˆÊÈÖ-Œÿ¤d¬@XSËXƸ·œ´ŒarðMéz_Rd_DXuj„tý'+²ÂFÊÙš”¥«È ‡­"+\² ¤J®Š¬xURUPÂaª@Ê%ª ôóTÄËS¦ÎîK§"ÌœWô¬*MEXyÑX§©@`Ú KU’ –Ô9* ¬RT@Rc¨`Q“ ",l‰R”·ÂOA¤ÊOáìîE²×è)¢ ²éÑ §ø£Ç’2 ˜VÒRè´"!öz·‡ðÜ7¥e ˆ’¦„üƒ@3å<¡Ñ?Êʈþî 忊ý£ÂéÂõˆþ˜#ÿDôÓ/èoì>÷Üð<~NDÿ¨ø¼Ñ?˜S"úǑۙ>ÑÌ–6¢œ¸mm(.݈þ žtVŽß3¢?ÝåSýÁLIEƒòŸ•!ø#ó1X{~ÜYCðï‘,ö¦r ÐJ ãhüßzó“¶ Ìo_DÎÉ433‰ÖŸ›0ž_„ì•‘ zèágúÊzþˆ‹j[»S´•Ÿê[ѧØ"t±LÂÅÔfd¾°©ÄÔg¦Ëω©_–ãÓ1õ£$nÆÔÙã7ô8OL}jêfL}0~ÊÏÏŽ©OÓW{L}°öE樑@ž9æl�ùr¶ÿd�y°¨ÕÈë÷p�yð±}fµÏ öµoð1y[�y›ÕÈkE�òqekÍg²3'€¼!;sÈ…_‹UˆøäAžöùäÁ²FW@úP Š50¬ýÁä³Ê7±øñà^åšo¢çÿë?>ÿtüxÇÍÆwë÷7ñã¿ ~|þ©øñ ”oœ Dîšú/7~|þéøñ`’=œï–ö¯SüøüñãAgˆí¾`„üˉŸFüxʼn͆3ÿÜâ~füøüUô©øñ vA›ÿ9Eh‰Ÿ"~<HàÀäöEÕý‚øñÕêúâÇ—ìp_P‚/‹_)Äßú§?>â·’øñòN©MÛ2$š»ÏR jh#4bf oÃiz"fþÀñYKÜø¬%l|ö×-j|ö‘ ñÙ¿–1ãÁbH¢2f<“þË‹>Èã¤|õM#f<Èï˜ñ6„ʧBƃðKBÆS~.Ÿ *Ào…ísCƃ7ð›ü "ƃ]ìþâ#ã ±à_¡ˆñv ûŒ€ñ6˜>#`<?0¾Óïþ_Àx¼žÿ}ÆkgóÍÇ-áâíLíÛƒÅWæjw>ö‡ŠÏZ"ÅgÅwÂj>ˆþ›z øNôû\×Vm´Ž?L¼mQ·U¼Aâ;1•—ÏWý×¾ ñä/s#Þ× oËêDˆ¯VÄ3³%ög¾ú÷êâùÇï ^K*¡&m1'6]‹oËgƒVÚ± ±á³Jhø¬>«†Ïªqá³WÕ°ðY=*|V ŸÕbÂgþð\3ÿHx>dñû…„¯œ7²ᙣ‹¸7"<3uþ%E„WgdOE9á )§hæD„G<OÍžˆð� ëwZ‰qÇâl'"<‚@€ü³¡ @0êJòùÿÔ#‡d\|u ‘2£ ­U‹ŸùÂgþxðÙ—„ƒGcÔ#Á#F(¸(?#|în™^$ø&­ð‹#Á‡eˆi„ý" ºîe |’߀Pò1Ð36l[„HðÑgD‚ÿ?‘à•\Q#Á“sýª%|Ö>ûHøì#aà3x;É4i øì¯OøÌ>k�Ÿ},þ{Ö ÿúÿ—»Þåÿú„rË/ÿ•c3ÂØŒÂêØlŽÆÊ†û_ÿe`lÔðïQ½ò†·ËvÛVƒg›ÁmgOì÷Üû݆™ýhì÷Li_û=.#¼‚ñ‘l—}‘íþ;7ö»%mÔØï‘ù2ÎýžÕC¿gõÈïY-ð{Öˆûzã¾ç¾¸ïù'ã¾+ûãçÆ}g¦Hµz[üE½í«ÿÁûÒGæ`Ô¸ï3Œ8qß³ZØ÷¬õ=ó}·3´A” Ñ+¼È<­!ßIYù'•H AVøNêzÄwæ•Á¡™+ß1TêñÞ3_¸÷Ìí=ó{Ï|±Þ³f¨÷Ðê=÷†zÏ?ê$€`èûÜPïÈ«}†É÷â/eµPïàãÇz‡žóá¹êýÛj¨wðÐÅI%Ô»C"½#Èu{¤w0ÑʬŒôŽüµHï 4û†T)þ?v#½f±F¤wðw•Õà‚Ù@ïE-Ð;½@RåÀ×ìÕ'½s�ïz ÷¼ç=÷„yÏ=QÞófwÐb}§Ÿä|WL õyAÞC÷/€Á ïàÔ×Õ—yÇÚ‘™+M¿¨#gµ ï ÎI– ò®ã¢ãý§•ïàÏS–ñeÉ«JŒ÷¼â=¯FxÇ^\%Â; œï̶UðNÂJ„w²ó§¼ƒšëªi#À{è ðž{¼çŸà]‰¾>?À;Ó‚)Á–ÿ,ù¢Å쿬x�ïÌâx¯BÍøîˆÿM”Þøî¡?¾{œù⻃«Ëß=j‰ïy⻃½¦ߦO|÷Ðß=÷ÆwÏýñÝ3ox÷¬-º{öÑàî18ÏÂw½Yµw/ ûYÑ`÷wkHÓ¾$¸{œÛàî Z«w/_u–ÁÝãÜÜ=ôwGßé4;Ž'¸{ä îÕƒ»¶£zgñw›ÁÝ£Ow§Ý7ÁÝËÊ‹©Ps7¸ûWÿ7¾@ þ·¶„©¦/ÝB£LìÌ÷åú¥F¬ö ‰8ÚÉ:ú€düÙ͆ë€$ö#óâƒ$O,‰ôÝ2I6òpýÖ~s¹<pH–í–Åê)¨h¸tÎR ¤šOæq™|rz†„cÍ'S¹ŠL>YîI(¡½ºA˜è¬ÎÒÕø‚ ØÃô9/[}y#+»â»úÂÚ³@’jÕäÛýšȞÝb;ßf² „yŽlëÑd·Îç¸8ˆ½Ú·Ù#ôÜäÿšG‹Á§öW¸Ñ©‚ÓꉸA[È·lÞ¸Ú¼ÿŸiÞ÷hß´Ò¾aµ}ó¿Ôö ÝöÕ‚ËþˆmÞ¢ð4o9ÍËÇÇÃHÁšmð¢ð48å+œŒãKêN^éEá锯ìteÄ’(mtŠÂ×)rO§£Z§  ¾Ö)¬Ö) ðu '°Yøm(ŸGçs}»Úž–Ä(Ý-øƒrûýô¼ÃÖ¯Ë(ý¼ÚoÊc±È�ŸßžŠ…Àœº’®AvÏËOËHòN®‘n€<"‘n@’Ùv²uO™à#òù8·û>dOš_Ž'Õ/ÇÿóÉôaÿ44ï`Gf,œ^^ O^ß¾év^¿¾—ßÁ«þñkù¾:¾;¼9 %½º?º:=•TüêíE_~'üûðòxX¹@âùÏÇs¨Ö- L{—§„©OÞžœ/¯.OPG„xÿ'T j6DHBÉáÛ“›×W”52Ï[9Ï“/ÅAª(»íƒtàøôÒ>M$½úU>ÏH{T'HÊœ'ô§¯ü/Ì#åsz >�‰£<Ó05 ×¼ZQWñúÐyº«èÿ¦oŸïÊ/£Ï_›÷×Ùvö0›Ïvðüø`ú¶×ï½î÷¿¡69=¼= ~U)¼¾¹œ Np‚ÛŸõŽO.驎¬wI rIÆ—g¾žmFÃÑüiµ™íž†ÏqÚ»9ž¿¹ºé Î.†G¯éiUÑõM¿~£±³Îp—Íô‰?ƒ$ß|W—Cc*G€#ç+¸èÝŸØCçÏøRik6âg[¹cv0=¹?:é÷éÑ'GTºª4ECuzØÐ#òþ€º÷á}Ö7O›Ñúy8Ù/Öw�þÉér5d Ýûeü¤µ|o¾`?Oñ› Á‚‡ Ýøè×㳓£_Ó5oN.OnzG) ÉÁင¯o{çƒÞeE¾:½=?÷ª¤þçw£9lD]sõ2Ý<’¿n¾™€™rJCÒcÃ+K§çWwbi¯êèêò¸7 £ð@lÉrq}xÓë_]R)¼9.zý#šCüºÃ7—½Áíñ M,&NÑ£ùbg3ËR<�Aæôô…ÜâêÓSDTs%— \qܶ¦ý¾úêYãoà>”S@3ÅÍÉõÕÍ@‡ª¤ÞÒ8¡®ûÛ“ãáùÕÑ! �FÑÞ^zõ¡ê®èÙ‘¦Ï{o.A·‚ðnFt|CÝrpØ;ïS­EöúðxxJ3Ýðêü("¿º¥¡(ÂL…ýóënnï&·*^]ÞˉAÌ&! 6ÂßúÏÖ›FÌj£2 9‹ætþ«›!Õ~€o<*’_{”’~R=KÉÅõÑÑÕ ¦ãRv}}w"ªmM”Pe«¢¸Cõ¬Š²”jY‘“Ou­‰"ªmM£ªu€\Mv”ÔdQ,5Y–tÀtX•Å&u!rÖê‘'T· V““¨Ó9 ±ŸÑGÝ V£Z¸/ŽºaÇ'&%¬Tìêîä†da]–�kÕd)bêÔdY7tëutBsBX«ÔaVŠ=š(»Êôöq½Ê–ã�$­4dou\ágÿtH‹Ë Ô[Z•M*Tæ6êG¢ã”�ý·›mÚét·¯§ËÉqnÇ«-?…–!>?1]±E± ^Ðé¼¢©2d›³(x5šÏ!Û:Âð-âSš/ùû•Ù?¨^½ú˜V ø£p¹Z!dj¾ôÃìtK Á„`Âr¶}Æâ‡·œÃé{šÝLI+z׿äÀÖ”¹¢\"H—Ú¼°0·"lH(v¤O¤%ÐhØ™‘íñQÿlµßŠ@–©ÿPÊ æ¹ˆm§‡¯{Ú\øyØ»§¼øõ–:ÿYl.å[Þ©ÓI…VºþñáàPoeøÃx 9oéŽ&qB7Ö;NåŽn÷aT®Ÿ!-]¥p2}ˆ®óª”ã8°uCGð¸ŸÏõAÇò 1pw†´Â]\Âì_˜Ý$yqrÜ»½ [KúüðæÍ‰"ƒ¥Ü ÌjƒûЊb­¿¤iɦŸ€^Fð¶Ìeׇ½Â6‘¦û×'�Ý&q58;¹‘å±ÿOVë.è!MàW׃!ý£ñ/uɱ‰a7ŠLjµß­÷俟‡Ý4122ÒhÑM3“Ú~ØnV«Ý^ @©4Ý™›RÇ3B=&o‚Cöpñë5ýŠùÕ308ÂÏ”¿hÌ#Ni.6·!w„f4ü:£_¦=úenýóî~óæ~Q£„æÎtë0“ŸCö9ù÷)û¤ü“n™]ÐuQ ?éB±Ö_)ƺ¸÷™ùùG:㟄#óD‚‘yà‡±ä_ <áos9…¯ŸÍéf±yàwüy:ÿ!²yØwÓÞð§ŸøEp#6ðWÔüëa»å8š ÇæióáIÒ)S¤LÌSü0Þ~Ù”æé·ôË<üŽ/Iå7M A’Ib2áOƒ$¥êÚLwû Õ%)D3Ÿ¶|Š#íXÉNÌ/ÈuÞ"ºŠç”m3+ay3‘iT*@ó7¯[ªå\¬©"\K ùÎP>¬öË ž‘ºÒÝt±^mF›ƦZ¥-¦éÊÑÊ4·*”˜Ýnކ¤Z¹òCß “ê=Д­ß ãô:^ªKVïÍy©åÃ~6ßÍ–à.Û¬Èr4Í–S‡n2Œ‡æÓÓn[Yç})•úâAC¡ûO¥ìÇ=§fRKóqÀj9æksGˆ7«ãÍlÍŠRAë·oöKmŠ\*:ž¯¦”7— ¢QñUs‡*XNfl cDÒÄVc<fŸæíežXRò´.Ãgæ8‘g Ízk­à¦ÃÅlk¾ r­×š¦¯B«Lðg½™½Œ� ©Îd:ž“LùaìNM‹P3­‡£%•µˆ¬€û- SÄuÙй)™ºJOf[Î WøÂpö;±u‘ZõxµGupÓL…/8Zö»éfEB©ö¡¡¦T—Õb%÷ZO©ïtC™·ïhï#1º:AE¸š| ™Ô0ÖìÑÄyÞ’4Ré~aºáfJÒØ‘:ƒ(ì$Žbûn††;R-ó-hØÉœ$J¨%æ¦ƒŽ“\N\¶ÓÅl¼šSõÂ@JþHSÃî™Í= U¸͇|_*º,KwT²ùdø<›`4†²D‘L<e0q„„íBY³îÈí†SVa \mf“ 6%eEƒˆz¥ 7Ížìˆ !:+¡óNEÉ4$aTTgæ3ê&¬yEµÀîOFŇ6Œ+2t=®†IEG­$¡€)µ8­ö4öÁ?D:©«šMÖÔ;( I€'ns^*=[¬ç³ñlÇaž*’rŠtÆe jÙpT0”¹"nq¢2”ºÔ±[;b(+öÝŒ:ð.?†ï CSUàêtþH¢LEsšƒ)­Õ×ÌJyEÍ„F…U>bð¯vRþ¸ã(¨Í¨¤ÏSj:«ÛP˜‡ÛgZÇÞ‘&¬ið‰®Ðº-_ȶ47på•6Ž«š5F• …;]C wßc£Š¦Áítk¾P @Ü͇˜Ãü®4· ‡;XÃþCÁwü¶Ø"Á$}šA¾Z“,RÙòÉÄY,h P*ÑÔöêA&*²LHŠ;Pl|*ñ�ܾå(Öye: ¨ÖŒô’¢S‘‰Ícz¿]ËÍ»šc¶Ï÷“éz]¯€EµèþŒyBE/ªa4=åõ“ß`(ˆ±úÍj·2ˆ¡b™Å~ò8G„EE2 òlfŽ=T³œn±’Ðt1Ý ÖŠa–˜ŸµÇK3>z½,½¡¢e*fAO*âãJŒ8BcP*_f›o^'IZ ¯¹¡â•%L ;O¨)`‘,RÕ_‘~˜Oe()X)å• A‹«~äöd­Ô•\CþB(Tô‚õŸWæP‹n€’$(%f6´_†Š]XG.>UFªOº¨Ômh™“ᤠ†¼«PaÌš|L {…0& ΦS ‚ È€üŒó皦ÎcÛN¡‹=�>£fýÓúÀ|vfE·Q ³^<KŒ„‹¤z:£°…°ÒU±y¬â•ÒLÈNpÓ)vY.Y ú›¢ê×È GrVÿ<TäŸÖotl$[8¡bpŽG T6Sêy?1Žh=ˆ¬Ð²C‹5‰¸ÏlIÖäÏË¢)pÙLW›ÉÔD/†¯£‘âZŒ)XÙÒ¢Ï{Mæ�{¤¨…ü=Œ:R™Õ#E1[qw(E fDj¡n¤8Æœü|ëF cäx£™PtÎΘ÷ °©”XY1öyIœTÄøbaöÂA‚´†æ=uÙ"Å75Å1*+ŒqZH õ-§sŽ:äjí¸‹ðÔßHpH"WãL‘âQiË ªõl÷OOÓ­3‘›ƒð•mìÔ)jæYJDŠŒ¶ûµ‰Þ)Úî7ä8{)&œ)2i;"EC"6æ(zFøa9&—h9áÐ[*¢1)ÖáäÔmZE:Û´2P™Ò´…>/w»ÑR©¦¶?l9ü™InFÆIC/VÜã+ÝT¡éÉ‹: ;)ì!›ñ 0ˆ¤6¼¬=V 98rJ­bàY7RpCC˜—‚édXŽF9P>r(:›”>¦Øf¿,×}²¥Âœýò‡åꆘ™µ"Ù1!År kâëòt¦ˆJ§·ƒ(º‘¢ ýr;zD4‘ÕºâºÑ}[¯íþñqö³ó Ù–C±I¦Ž’ÄMÃس·%iEA=“yÇ,Â@@6oŽšßù³Ï(É+&ûÅâƒû„¢¢vÂu*Š6Opr0¨ˆ P¤H¥�‰‚V‘ò~b£UjïV&­Ú=sDiH«f ‘Èã)­Önà;”¼Zc —5©HÑн ,[è¥Ò' QnøgfŒñ=&³±­™¢—ùˆRÍ—"[̧üÊ{‹å”®ã³×¬Æ«Zþ(xõ9’‘£wpÊЈî±_…°[¸©æ=Î$qÚTð4õúޝ ú-‰_#ò4(aª0Ú¿7çŒ#8$`õHðŒ¤¹ k’šÒé—yºn·t#A%Ød‰…ÐošâyP7°èæ!‚3 ˆ6M&}†G º€ ë!ÂddVbç'Âüzþ:œ|XŽ8u*àÀ3ÃùX0Ããuï¨ $ Ä %L!oýa7ðH8E·¼!ÍDʱyQM¸&0ðXÝ쌈XÇ)ÔôÁ¢+‡ìaCˆ¬‘+• ›XÐ@EŠÌIELò¦ !²j%ç½[|T|@må´†ÏiÑAÿå©xËÇØãP«µ]àÓ_M`«•ñjzEð†?y¥5Ç{s«¡Ù‰Á› ) ¯bÈÇECË'ÿ%¶‰öËw3ðhb|Sy#5åžqì°±Ga)]ðþ@©!iTð—k.–¬²x}´%1DI)âWKøŠRD”õ!άu(¥TùaµÚÙCž—ra£õP¿ˆˆeÁ}4{Ù2Ųâ>š€Îdˆý‡àžð!bEC3ÀÈa»;–XµòBÇœ“6ËÕ@Väe ù°¶ÉµÛ h@‘“¼¥6$ÅìoãS¿Š€*,‹ôã} lц²D!OÎüy•Á-¡¬[þ„ÎHùPMs[?–õY2ðë¼ëååŽb9}G¢Èm¦S|ÿÍw‰] eÒNÓÉÊü8^™bà›2•,°{bðùhòý~»ÃZ‹ç6‹ù$°°i¸ C>RÖÓŽG<4Ƥ |Z|EV•O©÷uã4*¥fÚ®örgH¥Õé¬Ý.ÓÄŠùp¥|ħi]ÎÑXã4+åÀ7²h™®ŒÇäŽ߯¢I؆©­>€æŽœä'ÄÊœ–üAEG=yGÅ·ê ª^ò<‚¬ªr&Vˆ35\T»ã+#5šP¼Ýnœ©hQßb–Š3­?-‡+žûdZÎÔﱫ„ÝêáfÏSOVX Ï�/£å¾9úM† ¦Ég6¯scY¦«Š…t Y¶¹gS‡eDïÇÉ™} cL‰e{ÂJùC#A{ÊZþhú¡«LIªLU“ýØìvè=sÕ˜}yï㓪¶ÄÆ"©Š ªbÏòÐÊ)ãzµÆ‰~ÍG€gMäú¸ˆ­| Ž§ÝœÅ0Ýâ\¸æ‘žÕ ­ælô„oƒx›õ ×JÂdáðpKÌ{¨…V›7”©§!ñü)´ÖæEÎÐ,rÜ*I§Ó¦’Rm‚j6ÃÕ$ ]ölW*¸ŸÚ† S¢…O#R2q’¼'Š\XdÞrÐ ÎËüÜ'wUÒ™uöŸ¾ðé7`ò ÄŸ(žaõ“ù@“îª`†ÅËó $ fXH3ÖÎS*¸âV{¶_˜÷I{Êšª¥ÐfBú1ŽgC�DÑÍt´™òN?t¢ø†&ʉì˜7“¸Wnu|¬†¦Ys× å$²Óá¨Õ½2S5Í¥| „Zò™ŽD6A§@ fÚ4úÑüÝè2„ |à8‘Mº—ÄŽF)››zsçMd÷Óg2Ý ÂJ¾53mxv vQD7]6Æ€Bº† w.*ºÙøQÀDQ­Ç+—$R‘·‚åkú<4î@¢@Îìn›Ç*d«ŸÆ´Òï§æ;7¸ËÝDá-PÓ%ðLͯOÉKr+Áy´Iu´*€3ÛõxÅHÍÓMÁ=†cJHÑqbÄ`àDAšS®4´¢.Þ7xÙÌ@‰¢.³Õ@éÔMã-É2WÆïvdû›ty›ŽÏR%ŠÄÌ&Ÿ‚ë& ÄèöCÂükˆ+ÂÁ<ƒc £‘÷¢þt¢˜‹2Ñp‚‡¿Zp€„TZCçÜ/Iµv8O€L"­ MR?…^,ªVO!WCej§ j Þ°J€™w"4e|o–ZÒh-íQLÆ ½¬Ô.͉â¯'“1.Sr¶`3Â&U⨸«(à2ª¦"-`fShõ´Ü˜~�9+®+Jé†|Г(ž"a^ åug¢0ЦjYH@‘D É´Øä›¡±7©DºþŽú£‚§§=F^ÃA©×3¹RCõÛôìC¢éy…·]ÝD± ?’rû™û~ƒÄ©G’\L†÷Ä|‚CªNfR(ã¼^6o=å²µ,¦ÓÈVG)v.Q¬c_>«G—(Ú±DûbÈ(걪r‘Rà3[>ÏLD¥ßñICH¯ö�F+Oî"u ÆHœ9bó2—O’$ qDaÞç’¸pÄ Üº©éEi'pÓÎþFÚ ýš!ûuäW’ÃE¹¢J.ñšÓNR“B Ï½¹­!4Ú©<íØ*b¹âFw¯Ê۵ƛ%0€I—/nW|Î:òå¢ÀšHßú/VøCÞSHÍÖ£áxMéÐMS§AGK AJSþ†F#^c=‘&.54¸x‘H�±p7ꦊ|X°ßè«‘T‘äÖñ%q^Š·\_8³ôËóØT>¢z+”œwÇùµ Þ¥¤ bʯ?*…Ûg¸­põ‡ÛùŠÜTAŒ£%'vnÕI©6ŸuLqK­ôËj͹¤²ß/g”*òÙÙ Jˆü0®u˜Pÿä-ÄnªhÄU;J‘ «å%Û:©ó|ô^WÞʦŠS<›h©6£9‘*JaÝuGȃ¤™#5çŸÙËL¢˜¦ Ég0ƒàØeªX„µ¼Ã‹/!é2ÝLšïV”m²XpOÇç3doE,Ðã´µ9øìVébŸîa4§U&÷TÁKUϳlª@:„›FS(¬›*t™Ã !k*T1i=é,#R! Π h’°€áLu“Ÿ˜]÷¸"é£Ro—A—Â�Z�’*‚Þl ½àÜ%¡›*–aåþfuZÂk¥º‹éš@Íbºyâvt^,¥ gŒÒQ(¢1 ûé"¿\HÔHÜØòùŠkÌÜeßǤ jXN^>Oè¦ n\éÐlÓx_¬°…—*ÌAr(–ߣ˜bª=¶|Ìe)ÃDž’Iøˆ`¶!QQa’U̳ÄIgEB©¢™åln_†Ó3Ò,ùü.™Õñ'R5¤Äû“¡ìhB“ª†_?Ák ¡oõðýxXys*¼a?güþýp<a·3Õ]!ÖÖaBª[C¬u‹§ÛB¬x“ rTQ¿SÝbñöûù÷Õ»$®vÇ!4Æ4ˆ[­°,›3‚ºU–*¾"`¹ †QP…“ #=ä‹{kµe+¡|†ŽÐMRYývö0ç]D2]¯°ªÔËDË>¯}ãk0dªPKf�q©gÊ§Öæ êÑ躊¢X*dÛ©"(Gˆd*Ýà™%TKA­Çãrµäs©¢¨5Xo»™b¨5£c}cGr©èzJ“«y=“)Š"ÙúyE+}¦ˆI%a7Ó ¡5>ÚòŽ|¦j=SB«3ƒFkA3qᎵLñÑz¾¢™r´!ï;S $Ân¦øf½BäÀ“rŠÊÜP½šºÐê&3ưòš &ÌðÀõ˜âŒÏ<SÑô”ö¢¶Ëõt·ûà`ñLá¤Lqà"[^•Ò¼/S¤:îD™¢•>M—èîô„0ðkpQXUí·È5„ÈW¥ü ›JÚªò¡r©(-{“¢À.Š{!n[铺å™ à{DÖ™?ÓMö`ïæÍÍE¦€GV«'tw>Ñ‚"*âåb´ùyLŠÈ§ÈI[Å~cú­BÙaFk‹(=cu”eŠƒdµ!VvŠiÈÙøÏ"[c>¦a*²¸ã“É„%>¡\A5W‰³ØÖŸ§ íº±­=uÎ1Ç‹[×0‹­ Ö+JÙÚÿ¸ŸmôÛç#„,¶6 UmXî%`´Öš—`¬Üî’¼Ÿó žYbk†íj¾7»á™"¥ ^=b6Í®ˆi³Ûͺ™b¥íè™cö©2…Hfñ“[À~$¿4{žî73l®S¶ØÍf>¦Ä«L7Kâf19ºêÔUójd0r¦ Éhæ#í²]V.ÎÝ,ÖEÉ>9 ‘Ü«C-‡çÈ:•²ÆÃD9YxÇ'K#¯b8ÁФúRû¤;Q`¯ÙlãgiÒª„I)CúÑ ÔÔiÖž£›¥3Y+K]3…<gm÷ki�*Xæ ÝO œ…>9­] ÉÌ+N× Ê–ÉŽÚÛI€J™B²-­“ÒŽ³õTÖâL_Éù´p d°à>y=§3nä"L§ ÜÌ™4ŽÓmrñó¸ù´TAEnÛg>”Â/ÍHºRDX¤È2q� c+|Çd4 DGR àÐoâd Õ Õ;S‡g”)LƒÜ9 M^ÑÈ㊊÷¦¬ŠÑp葽ÝT·Gt3…iòŽsϼ¤ +h=×slP½ðû®ýrƒ˜¨`¡Ž]µyÕœ)L3Âw3!%@Ù´âNÐ ’®”÷•º¹"/GX¢QRMµ„Z!eè*˳·¹²š‚ýì\¡™Qšø4¹b3Lë[JÚ ¦G¬Ë¹¢´ú ݼST¦ëäA§"µçoó ¨(쉴²䊼pÈ”œQ~·+àÂ9Ò1’$*áÃ!$"Úïäå~®Èi‡sµö˜X®Ð‰¼¥)Íuæ z®ØÈHùl¥øÐIÉ8 •ÀÈ\Òn¾åè hEGTR³7¡G¾sÅG|Té̦ #¯p¹b#ˆ_(©5À«콎Çën®°ÈHåd ;rx«ž+8bµ¹ t%ôEBF°Zà× $kr[H‘¸Š-ìÈ£ÔšGg®dÅ/© ò.Xå®r‹ìnõðBÆ”"vëGÀ€$n•“KjÏIc Ÿ£äqØš  )£ºÞnÖSƒÆq»Ö¼uÂ#’F&²Dœ6¤/ãÑ’Ÿ™ÕUö¸S»õŸÓ’’'nÕÝÝ29—'®%ÖÏ3c±Ä­øKܪb'6OÜêQÇÙ7’ºõÙŽ§/ñÜÒ-÷AçòÄmÀí|-û‰\•Ä­ vnóÔ­Šœ7wÞ—ä©[Bxòyê–#å°½Ê+’š^ž¦>ù¦„ yêVæeCÕS`azžÔV€tgˆO³L0Ð\á„YxËˬݹ¾¾JC•¶ÒÎpHó:µ–%¶Yê‹f®ØÃêxÙÌu/ˆÄÆâZ Låî¹Ä\‘ðÔÅl\î+å 7 |XMŸØ×Éf@:~?¢Ë¦ï±¦)¾P9æf»µÃÑssÈ2™½`íÄŽ#a!lùæ ; 6'ЇÆÙ'•Xû–˜AàÖRôáJåËsX)W0Ò–c¸[= Œ™'£9ÇßÍ èmœìñfE0äŠY¸ë"4‹Yº¹Âí8’+P)£§é‹A|µ€Ozs(N–ÅV¿ßÊŸ8ZªMÖÈ!Õ7ÑðU(ònúÐ-‚¼ÃÞñœ ÅFFÓÀbrŠ8ìTÏ, :Þ­6l2³a¸Ú| •ïÝzD )Ö;^× …¿›â±¹&6+ç‹I8CÄ™(j˜Oˆ1‚3žè—)ôÀ_![;Oc0C(žøÜ V°ÄÓt)Ÿm‚%žž&dÙ«!ØŠ;e6qE©\R+ iÆ÷$…)xãè~-L’ž({1OÕ‹j<Iç+^<½,p )ä{SA&‰;šB>Ó/S™œ]ïn!àa&ß‚fü!ç|FUä Ÿ•RÚ”ofÆï,fæÝ;¥Méf?î QRÒ”o&!ZHX!0J›Îhþy¶÷È겇é#œ«B@Á9ý4Åw �à¿å±RÈú¿ÀF6¥BIÍÞ×ö_ YᡊBJÆ6™Æ”L$ɇÌ)ºéÊ¡c2Ÿ¬Ü óþ`ò8ľ&>Ó-ä­h«—ÅlkB`²¤“ .ظ[ÈŠ¾0©hh£Ÿ&ZŸ—Õ ý|2ÅÉ nBs! ú‚ýYÞ V_M²À/Æ‹55¬,î‹ñzÔSÎå²S±˜Ì¿Cf©˜œœäc5$•J™S6Ok¼Z*dY_<â% Í–T!Y×I´YN(hO–†Å#­À”’2"à‘\,åÄOç j ¾ÊÇN YÆÏ8–3y\“$s$RLY¸&º•øËKG…,ßÔ4ë²hsŠ”²R/ø£p©:IÃR M)ôâqŒÁ*‹0%×dJ‹m©�L*ÄÕ’ewA#íù%3›ÜÑØ¢ºÉB»XNßá6›É±(d‰]мâXX–XH×LrPÈâj%kä ­l‹ÐzqdÅû5—\Èê¹À§ÌÎÃeù¤Åd†³…,–‹5.‘¬WëñrG}FÖ@ w¡.¡,q&I#OV·…-®xß*>=¾Ç‹ªBÖ¸R¾6â¸"Æè-´¼›^ö›W8ff-wŒ Yó¼gLÉÌI:1: yU" JK}dÛ«™-*Žæè¦ëÙ|õd¢9â2ZZèŠi•ÜRÑ-¢öÞ›¸m"ä ÓÇIœTÄkà2|®D©Þvõ¸+³K%·kÎ’Û^+&<›@kSöj]^6qmR%¥Ôˆ]]Þ5G=ey]”Í ” íöK~º¬° é‹AGVÙMŽ´$ñ„NÂÌ x/a()¥>/Ì€ p¸^ÖÝÅËö=RM™ÈDà Ì!Kù–;a=Ù#]ÔÐ-h¤l\O–ä‡4•‰Ý„• ]âèj3i¥²ïiù]¾—6—…{ñ~nƒ)ud醱eÕ¦ñˤټæ5Áž:²„/WæDÕ†Cûp\¡Ž¬æË•$CIÊ¡w27ß 1”ÍΜÕ%il¥‚$Év–µe�$1•Yá§©?ÚÔa—žpøÃ<FVöõßÈÏðYÚU¢!sHaŠ¿†•eYÇ{CJÄ’àÞ.k:( Ñö²¤ÓCR3¡{EÙe97*<^Ù Ð玞gnÍ’ºh¨dw«måRYíM~í"'̇”!t2�wÛBË*o4Û)βš` ¤Š]• 1'M±~–=4ß#—˜ÉìvA`¬óã~6å»clðÓT|3GБå%¥;|à#˜Šð°ÄvçŠj2ÔKp�Ÿ¨Âãŵ7iiˆM1ù˜5_fŠ%Ñt(]8éò2Yù]ñ÷u±|>�]Xסa ”Rê÷f$,ÀŽ‹QêŠ Ì8Xpž¼äGP@XÁcH‹DQE„çåã‹‚Ä ž–˜eòÔHó¬*äBæ¹+äbæ…+â‚AE„¢¡Ͷ+BÅ0 …»Ãº²<óé7ëÈ’k¶²¹ýdÑÝ!„£1ï·P*°©!’æIn¼€ µ/�_ ‹j€D¦8{ü4†1ŸñSÒ˜ä?Mùœ°’¦xïÀÁ`Šÿ<TA æ{ü4¥û~F¯.‡”èã·)Fÿúä¨wx>”Ïÿ¡H* Ç ò´"\æ§@ÖH{9eÇ=@4ËJP¸æ=‚sÆà’å? “ƒo’0=øáWÂ;Á·Ç‡B;A~=/Þk‡]�Bž{ÖÁ�G´þ ^‚ó[d<)ãàâ7'oúÝÎ+„–7¿aœJÍÉðÕéùÕáÀ$¢WÔ$ãWoû÷ægòêíMÿð­Ü#%ÅÑù½"+ äšÉS«-^]"tºØÉ óêâ;ù¼:ï]þZá«£\FMÇ Ä¶²øUõ> §‘‡j$²ôÕÑMG~g¯´Ô$(†SS‚-G‡bÕZ­]à÷ðèü°ß?é#x27çÿ¾áÖ´&6ñ:ÈÎùÁô¦Ïº£«Ëþàæ°w‰ÏÛ;¯šÒGD oH_#¼!}7B°ð¦x‚€áMñ#b†7Å[„ oˆ/ï©Ý¸Ïü¹üE¼9ªMq°MBóå€2bà�ÅrU2b¼Ø´!ÄXr,«¥$ƨˆ,mÊ„²;=×z[­~˜Mz Z,_†OöAB‘A˨‰~æpdðëf~YÎa,Îoá&–ÌH^N'ü”)¡K¼/„$·<yôË£Û‹ÛóCŒ„áá u‹]uÈ6PÅ`»1Ù+ lë¿™˜ö¶å!a«—ÍÑõíÍIÙäœ^¡‰CüüOõŠ»é9áÁTÇféß22 ãí šk9Ϋ9‚F÷ùó ?räˆ>z ýqFôc7á,ÉGïo©ß~¼6Ôƒ?vpòH“ÑÇîÅ'î õ‰›Ì0Õ³¼=<>¾}XÄ~Õ ûUw˜ê|ªÓkLz>ÍÑíæ>)x*ô©ú¯yjôçHïUg2gúÊÎS¨÷ª;žJ›ªËcÄ¢÷ɾ©xûæö5ÂÒû4ý×Oï½æ Qê½×œ!X½÷š»nè³]s×<&8:½½ïFö©ôïûÆíÑÜBã±ÀÑÅõkê ‘Ç¤:ùŽÌyŒ`tgÝÈc££Jy Á:<Ïc Ò½9!]ì3éT–Øg許bŸIøº³nì3 _G:¿Y躻nì· ]G:¿]Þ P]Nî¯\?]ίLÕ=f¹0ƒ5ñX…–^²Iâ± iÈ"‰Ç"¸æ¬›xìkHã±®¹ë&[àÒx,A”Úc‡‹3ø‰Çg7ªõÙâ\æ»Ôg›7g¯»©Ï¤9ë¦>kæ®›ú¬qóæœîæ³iÎßÞ«¡»ù¬Ñ'@Sxê³é. ó™ס¾>c®OºÌg ¾Ž�µÏ|é|öè]RÊ|öè]RÊ|ö kκ™Ït i|ö kî@mç½æô|> õ¨Ìg‰Ûó*vî3«†·—äMølAjªVî3®<ëæ>[°ÊÜÔgÜ”®ôYäöü %õ™„Uæ¦>»%õWÒóü–¹’’~ë\QI e.©›`e(<¦¹¼ºé»@ì±Gÿ„ Íñi·ðÄèzÝÂcèâ>é<æ0:º§ÇÐåg=pLxuAú]É&ÚJÃöÂVE[‘$ƒÇ4òlÉ౑@rxÌu}rsÁöÂæF›𘌵°¶?Ú´¸³Çl¬…ݰ=â×ËyP¬-–©–ÄjÑ$‡ÇvZ�Éá1ž-„dñYï×·g Sñ[ÒݱÎg¹__ßw¸Åû}fñèúFç³= :¾¥ëŒÎg(Ô Ï %Þç±ÎgÔ®Ï,&Þç±Îc• ÀuoN{L{âÓ\2 ŠOsÍœ(>Ío™"¥©¡•6ðAÝZhÔ½¡u6ð!Ý›þw7�`ì²’këû}.…oâRø&!.…o:g’üŠ)[šŠëóÁkæpñªÎ˜ÔÅ«ºc’¯ª×Í´/-Ê3f‚iQÞ19LSyóš¹b<Š3æñ(î˜BÆ£8|Ít2>Í3Ëø4wÌ2ãÑ0ÝŒG~Åì3MÅíkö©}¨:8Õ>\ ¼j²5+fàõ¤b¿Ú‡j¹(}¦³ñ騳öÁZ.fŸ)n¼×±ÎgT¡Ïd7ÞëXç5ËEl êµËElJê5 )¹8^Ó\„æ™^Û\°Îrï o>„Û??¾êÉbåC¹FÏKê5¯¢>¼«êSæÊi¨o¯}†çƒ½Ða™ñ_¾îŒ9t|ºs¾§ÇBÐáž>ôË×1›N]w1À¶;óê4T§¢jÚæ¸ßg*Oì=ÆzæÁ¼ç<zï9ZÖ=î̺ãQ`ÿÓtY3` Šùwš?y}Ϥ;ÍÙ=³ïx4w÷ÌÁÓÔÜŸ3÷ŽGÁ¤;uyÀŠfíÏßžß3ùŽOqÎä;MÍÍ=³ðøçLÅãy<ªz0,Tg÷LËãSÝÝ37¯>çLÎãÑœß3CWsÎl=שiÖœ3¥O£¿›Ý¬á53üø´Ø|bµ û]¬öo%aË‹ÕþÝ$ìz±ºeCéDžÝ¶§$ê¶m¥¾)šÇ ;Y¬öoºaÊÕ-ûnSoT•-4V·í¾™z{�ëáë~øSiè¬F‹y8ô€V£åY<ôàVRë›å)“ y•æ©Mk±RÚ4ÖÅaÿ×àО_3QkŽþ�¬µ¡ÖÒïÞeo Å÷á[›ƒ‹éº6‡)«î:!Cø /ýîŸØrx:žf0ÅðmjJ)…§÷•Ï8eš#_†“{-„k.„"kSR.ŸqÊ$Hõ GW׿é÷Þ\jŸóprÔ»&x¶ì Ÿ|‡1àÍ ¢Fw99žönÀݺsÌwhà'¬ô˜:-éü¶Å0bò%¯òø˜ù—ZtGLÃÔ¢ì3“W‰÷0>ÐmtGÌÓäU¾}Ã<M-íÚmækòéNzçL×äUÒÂÀ<M-ʓ©U‹·D>nõgÌÕÔ®¿c®¦=Ø^}`\´¦njÑžŸ0‹S›vÀ\N^­tyæwòfœ2Ó_×g®'¿î–©ž|:€±ÐÉŽµ~3  }ˆÜ¨L åï:× ö!rú}z~EŽDèÃäôûü˜I¡üªæ†òëΙ%Ê«»Bƒû¶£¡4¸Ì·#m´€f¾Mi£:óíI³0Ì·+-Ês¦–òkÆ|ÛÒ¢<g–)¿¶ÎdS-Ê&ò)/xròíM‹²ÏÔS~í=óOyu'7oNΘˆªU}ÎTT^uï’Y©¼ºsSb¿™.°yÉôT^íTÈo(ón+òíY‹öŒI¬Ú´wÌdÕ¢=ÍTVmÚ3æµjÓÞ1Á•WK¾;s]µ)ûÌxåÕç4òmSC}K³^äs�ŒòŠI±|Ê˓Û׿é]˜Ë›ï=˜'Ë«¥™"òÁÿœcž,Ÿêúðè×Ì–Õ¦¼¾gά6uŸ‰³Zµ·ÌžåU£ÏE>Ô‚3vLŸåWØa -¯öœ)´¼*¶­o{Ê«[B‘oƒZìÎ2oV«ö„i³üóœ ´¼ºsæÑò«Ž™S˯;g^-¿îŠéµ¼ºëóÃ3mµj‡ý eò­äˆB&áúHŽœÙ¸Ú3ÜòCZìerðCZ¬fräLàÕ’aðšé»Ú´gÌãÕ¦½cJ/¯–;B ާ#òíx³ê¹¾üºs¦úò뮘÷Ë«0ÿ—_uÂ$`~Ý9ó€ùu¼ÆG-˜\ö_¢X.{0Q 07»-Q 2——¨œ›]—¨ËÎKÔÐyÞlçØþf¾°eŸÙÂüÊ‹°Ï”a-Ú¸ÏÌa-Ú>ˆù”ƒ›ÛË#&óio/1³ž1£X»þœyÅ|z9bµ@s=1µ€s=µ`s=µàs=2µ`t=̵àt=Wµ`u=Öµ u=|µÀu=ൠv9µ@v9jµ`v=UׂÛåØ_ÔÝå„_Ô‚Þå0_Ô‚ßåx^ ~—S}-ø½<×áË3y-0¾<˜×‚äÓy-¶Ó#lQ  ·çí¢PoÖE-ÀÞž ‹ZÀ½=îµà{{®/jùö�_Ôóå|ZÔóå�]Ô‚ôå¬\ÔõåX\Ô‚õåÜ^Ô‚õåˆ^Üöå4^Ü‚öå˜TÜ÷åWÜ‚÷åÈVÜøåtVÜ‚øåøXÜ‚ùå¤XÜúåPXÜúå|_Üûå_Üüå¤_Ü‚úå¸_ÜûåÌ_Ü‚üåà_Ü‚þí ¿¸Å°GùâÀžÙ‹[Ü�{À0nqìI¸Å°Gã—@ÏÊÅ-N{‹[<=¥·8zà.nqô\Üâ èa¶¸ÅCгwq‹— ÇèâWAÏúÄ-͉[\=ó·¸ zF'nqôdSÜâ6蹡¸ÅiÐÓOq‹Ï ç‡âOéÄ-Οԉ[>­·ø |ì&nqøèMÜâ%ðñ›¸ÅK5q‹§ §jâgANÖÄ-þ‚9»·x æüJÜâ4˜3,q‹ÛÀlâ¿ÙÄ-^´‰[|9™·ø zÊ$nñô4IÜâ6è©‘¸ÅqÐÃ6q‹ç '`âïAäÄ-„ž„‰[|=´·xz"&nñ#ìᖸŗ°\âwž‰[ü =·ùr¬$ns(äøHÜæPÈ1‘¸Í¡#&q›C!GIâ‡gqâg‚üäk¦Oô)¯o®.®È‘Ž[< '7¦SôÞ»}æU¬«÷nç‡ýçÁ祪?rÇä‹5ñå1/6¤G$ÎkâãÞ[ôò%šò[æb¬ÊO¾{ËDŒUéi[Çà`¬Ëé>LÂX—ÓjÅ4Œu9¼p1VåçÇÇ÷LËXŸÝ3%c]|wÏ|Œ5ñÙÙ Ûûž)Ê«¾j‹z­¡½µÚzíÏïÎN±!¿êß3scSq{ÏÔ …}JÃwwVW7„y)ÐcºF¯æüŠ™=:ÖÔ­!º#¨kº³“7ý‹ÓÃCfjôë.™²Ñ§ëñuuƒ¨î’Ù›º[¹®nÕ]2§cCGEaJGŸâ½ZàÀæèÕ]Þ1•£G×c2GŸâ½<*ðXƒqǼŽM]ÿ”}ŠCætôjøQ>[ô¥V>kô¥ˆ>kôMC5naÐc[¶Fè±Æ­X#ôXãV¬z¬q+E =¹Õ"6Mr¥}8lšäJûpØ4É•öá°i’+íÃQÓ$WÚ‡£¦U®´GM»\qŽšF¹2}8jšäJûpÔ4É•öáÈcîÑǦGkhŽ<ÖÐ>{¬Á}8ö˜ÂôáØgéñÏÒ‡cŸ5¤ƒÄ>kH‰=Öà>{¬aúp챆öáØc íÉÇÚ‡A´' “ÐZ¾‘4ìÁŠCæôhzLéS2ƒdSƒ¶J†`Å!ÓH64l»¤aV2‰d]s®=)mØÀè`ƒ´iƒsmÞ´i†smÞ´iˆs.bÚ4űiˆsmÞ´i‹smÞ´ismÞ´ismÞ¬inݬiÓ¸YÓ2£eMKÈ|–5íÀý¡,Y~È|”>4MÈä×€˜ÒS™À²q:We²œ.h�M£€ÆSý>W²6/`“å‡Ì›éS2u¦OsÉä™UÍ%PwÒÀœxáž4À&„ ŒyusÄ™U)ö’²ì³´^Qr¡ûL‹Ùß2fM<�¨NX’äg÷̆ÙßÝ3f]Îð7i€H(®î™³¡¸;¹gr̦‚¯hÔ—üÿ;fìÊá¶¥ Ĉã„i+â•“Ài+B×]Ãq:>¾Õëêu‡N¯«WÿèrpÞ¿c¾Ë¦â·wLtYUœÜã½DÚÀŠŸ1åeUN^jÚÀˆ§ý£SVi#²¢Çä—MÅí)³_z=æ¿l(üŒzµYÑcLŸâ·L„ÙÔàé XÈŠSaú¿e̺æ’Ò€ƒ¤8yص!}sÈD˜µqËy땾¹$‡;m ?ôNíZ èÇJé# ì¥v®øc¥^Y·�õ¼Þ³dVåä)3?f]zÆ ™ué3dÖ¤ê&3GfC'^2)}.ô­(˜>2³g6ÄW}&ÒlÊo™@³!×'4}ç;UÕ«O“hY3³h‰3óe¯ÏšMù-Si6'R&ÑlΣL¦ÙœF™OÓ3‹2¦ge*MÏÊšž)”Ù3©‡)´ãÌéá´âÌKNæÍô(n™7³¡8Ç ÇŠ[æÌl cóôxcÍ›3gz4ç¦Ë¬kýß­9½‘†ïÖœßHÃwk´öÉùPK×hq£cû4€›URèM”l§‚³Ê[¦âl*­½@ÎjQËœ³ZÔ´éŒVl×ÀuVËwöYÉÚ°ïx<žö˜¼Ó£é1o§,^øJ=ïÌ ÜÔñÎ .M=(ïŒ'Q&ñôú#‡ÌãéŬ—LãéQõXåCºæ*ïcðqÚ�~RÂK¦òô–ð’y;ª7 § È:cª4:®uB'in1Êó.™çÓÿ¼Kfúô?ï’©>ýÏ»dÒOWw1è›Ð‰LûYÑœ–šªM^ßô.˜�ôú°wsrlǽ·oÃþiÄL 5èBf­é¨.­ëpÆO•Yã¦ÇÇúÀ¼qáíkÕuÝ͉†@cMwq{.×n¬ë°kþк»ò`­WL.hXãR®hš¢´SдÅíE‡D›ò€9D›Uéw˜@Ô£˜C´®À~t§Ã$¢^UÀ$¢>UÐa*Q¯*`>QÑP¼°aÖÌ)Z¯éàžYEkâó{f­g>á–Œ™\´¦£9ú¶Ã£EÀ£ˆCZ> O>˜E«Ò·Çáýð-‚°€\Ô¯ë1¿h]׿gŽÑšø.¾7Ÿ–cÔ¯ë1ɨO‡ïÞÀ7êÓñ'o u•dM®ȚةW´){L5ZW–Uˆ£6eùF½J®Dœø•¦qÕ4÷ðÎŽ¯™t´&?>fy^“S_±¢h*n¡H:uÅuÿÍ%k‚ºæíñuÿšÉH=Šûã>’úTw}&!mªnÍU‰WÅWÕ pDë<—®nRܳ ’† ÞÞŠ¦nšÅ!Oë6À¨<dMÝÐ\°¦n„‹Ã{–×-pÑck¦õê_\™g×ëŽyÔ<»^uh̳ëu§IŽåõš_JwIë§eò¬^ñK[ó¬^óK[õ¬^õK[æ¬^ûK[è¬n€›ãë‹6Å´äfõÚC~ÁT£Mù5‹6å¿e"Ñšü¥Éë57S°ª^shXQ¯7UåÕJÛ”ÇM9ÆO^¯ò€ûáðô„)Eýº7L+ZÓ™’™ ëõ·Ê7Ì0ZQ¾•~Qtšr”°êr3½aSÎùkÍn<@¾"öiøš¤©yà P¤> _“y4Ü6EîÓð5õºKÙ†×LNê-ëê6ò±®n)!ë<¶@Yç±Jɺ†=¤¬Ô£é_3ciMc¦è¼Ó0‡NÑ 3õ©îúÌhÚTñ NS¿Jâ6 ¢38¨Në*žÁAwÚT˜2 [°Šo×0©¤ŒMkôµM{˜ÕÄ© l6­AEÀ5aÓ(Ü53«Ö5fí¯ªWcì6mq+¥hšâVJÑ´Ä­”¢n³Îqµ!çüu\˜ñöÕ¦WDu ˜Õ/êõDZïk&a­ÉyUÌ£z½qšúš9Xkr3׿Q½ÒPðõ:›51êu†CpÍ|¬U¹¬•y\¯ó¥™ó¸^g³ˆæq½Î$çüõ:_ªYãz­/Õ®q½Þ—Zñ:4¾¦^s^Xó:|kÖ¼Žßš…5¯Á·faÍë0ð­YXó: |Ë k^G€$F!ëèïíM…¬#?S!ë°åÌÔÚ”_3]kSþ[¦j­Ëe™Ïë OT(lõ½×A+øŠz­¥Õê¨ï­´Yõ½-!@^Ç}oKבëúr]ݪ{Ãd¯5] ò: |ë@‡¼ŽRYG‚¥ò ³¾V”÷Ø4¯CÁ{ ‡š×Ñà½<Íëxð^ÃæuDx¯ÁNó:&¼·¡Nó:,¼wãœæutxï9Íë�ñÞpš×Qâ}%¼i^‹eðҼˠ§y 0–!Mó\,Ùæ5°X†2ÍkHÑ cš×pb5„i^C‹Õð¥y 3VC—æ5äX [š×ä=ôô\®~C–Ê“Àz•l„:’,•¸²ÞoDÉFªãIU;Õ0åÑÕåÑáÀô­AJ«ê1笫B sIè‘÷˜p¶"ç¯Í±WÓcòÙj¹ù#¸Ž#­×ÕÁßïÈuu[¨®Ç|µµÞ~~|'f*êxR”ˆ¸(EÇ7­£I«;e¢[ŸŽ+_G”VwÊô·•x¢‘ Œ6mÕö˜ ·®åøMæÒ¼MÙcÎܺ’ƒ.ñ•5€é({L§[Wš´zqø}‰v½ú3½>þˆ¾Çd¼u=/f뛵¹{Õb;A¯Ì}*sUÓZ8Ó]Ô`h#zT=_ˆíè"jZÑìG%Rœ[¹¦i9œ8/¢¦Åd11°Ww~Ç$ÁM™N˜-¸©änË|ÁÝõùà˜ „ýº;¦®ëΙX¸ÑQÌ0Üb\þD€õ'Gƒ«›Åð<aoSšH·š…&õf®¸q£žÙ?5û¤ ,nf ;Uò¤¯®¯îNn2›ç5›’)Œ•òÇíZÌ`ìSœ2q]ñ’™Éاa6ãšâuÿîðzˆ)- 뺣Óþà¦wù†)Œëº«Û˳ƒ”èßø£*)ѼN†-ôD‘CƒÔ^^ †WׇßÝž¸dH½áj=úq?¥Á„8­~ũˌdksMä»f]…ÙšN*"\Ö$§Ý¬*Ár]“©+’˜c­WE¨LP‚ùÇqäÚJù÷Ëíìi9HyƒØ¯ä ¿Žúuo¾RÁ‡ÕjÎtñ ý©Ë·Ï«Í”?u8²ÃNCÌ”«a¥ÚëÙûé$?õ¼i°©0á›rŽ+˜4å¨T˜6*7ªÔÖ0÷ÇèŠzv#:­×H†J­{—ª\T©õ­£ZF*d×3B˜Ô2¢¯§õŒfµŒd(¯g„°R9ÃMW*&l»q¥2 @)#B\®Ø“ýÑ×ßõ˜Îm—DñÁ¯Rˆþü¶”%¿J8Û™¥¿ŠL¶R–ü*ãl}+Ê~˜l¥¬8øUβcźçûÅm) HBøó•…œíëÛRp¦?„ª°ìßî©,9Hâôà&ú6ÿ¶ó­yºÍOå¸2¿<¶ùsÊ_ ê½ ¡â1_0Ð ’à IB\…rÁ/Ç‹µÐ;5W¤ýw¶Ï³GPöî—;«Uûßzhš‚“ä`¤Ï³§gþú€é$ç«wœ0l’ßÊ%û%5é>)•%;Øé–ÇwÀÑ÷í|Ο"˜_òóáp2{ÙÍ¢î)i ùɸ»¡ûâçOI hªcStáb5q/üãKí/Êß_‡ûò‘ŠÊ«YôwL®á»á–þƒ˜/þÙfݽI’üà½ó( þîØß?™ØŸanňíÜ;E¦Œ¶n?ùŸ&ïF›Çánô„/>˜ß ‡o†ëÑd6ÏÎ+Œ6›ÑÓXÊ e$ËB•M—»Í‡áz…6*…ûÅt3‡ªÉ«æqµÇýz´-¦»é†V8ÑÌkš¼iŽšLé9æbZÉD;=дØâͧï™Çúa¾ÿ€•Kä‹éâlª¶(\²éFjbŸ¶™>N7Óåx*ŠÔÖqµXƒÃ™ú×Kˆ Ù©dÎ)õÛýFïRXÅþa³ÚÂMh ˆ$xbCkAîÉ’-*… (ž=Î@ø®ÆÚb™‘ /£Íl„U-qJ¾ ‹„iM<[Žç{f¡ m¥fËçéf¶‘°ÈXé|†µ¢¬Vµïj²§ 7² ±Þ‘mWbwS‹ÈV–©_Yº–-ŸÄ.‘­ð»Ùî™ì¹ØaqÑ>8O©Ã¹"²Õ}½Ü¤¬ëh7~ DYiåVË‘W„0`TÔ{íjƒUF;,:ƒéöZ›ÙtÉKŒ@É9ŸmwXcj¢ál7]€êÍ´ñd\sÇıŠó‚ÝM»Êt±žvæñ¦€ß­®}Í÷Vmë·{Þ¬Þéè³UÄX5æI:µž„;ìÀéæŠ±¸&¶š/+z µGbëjæªÀx:¡Á�*7;Ì0pÆbý¤ì“#~IêšmK6š‚Á­>3HçKl%ÝQbnT”vÞìf4Að8N;{yàtÃù N6mÃÎ7Nm•¸1ÌÕålbÚ 6©¤Í–Íg£-ÈØt[ ÷[ÌW)>4áóÌÓ$)»ûEïºOÙWkÎkËxv-3õdºofkt^ÊPŽfÊðšúá–:òt>a]á×a´B_Žmž¬w:÷¦©3ö˱™Ý¥f¬¶5…ÂÕÙ¿¹¼¾î]³8vÅ'Vœ¸ârèÓÙ‘1õf´3&7Θ¹ë«P™­ìZëq¥DqZöV:ÊÐUn¦óÑ{÷R[ýë7½á£å÷3š¢ìœÐ•#ÃŒSÊPòæ¯þ73ÎPx,Þ /Þ§W7CZ¼7f™æ$_šå¸ÄffÁd´…fíµé¸›VÒy7³iáúΫwìÕt ´½E +3 ç:L/7÷DY©õ!kxšÞ› e)i|IUSWÈ:­éPVhMDz0k:‡+èÞÓ<(,‹:£É`®óÐ)ît<Þæ°,óôýzCU—…ÙÖ“&Ãévj—gû$rÃr,†hÏŸýsÓž£÷aOãd�Wz8 œs˜œ EizˆYC0Ëz$‰Õf2å*D@ ÍCcÁŽ>ÐØØÎ~7•fh¶ÓZ¡#3¹ôöÓùtAµògÏ ÕZD©-å»áz,íAi yäšÖþýèI±I±…ZÈÉŒ .¡I³ÆIkðeFæ™Íg»Ò$33»4—ŽÛ|º|Ú=K;Xàª)Íš·€Ä�yv^ w£ÙÒ¢ÁP+B¨n´ŸkþHëcà” ¤·ÃÕ-9š ü1v£Y褉àÒ‚:ÙÉ6QVJ ùD` ÉÄéâq‡¶ò´ü ·ã¦˜ØmW²Êd* ƒdûõÚ>8ÖÇŒ(Ûˆ&·Õfö4[ ~€‚™š>Ñ*âÙ�j<í „€˜VàÇÙ˜VaÁ(ƒ¢¶­àXt4GW‡e_¨—¡“'±µ6ùŒ`cêÏŠ5íˆHRÛc°°Ï÷‹¥ÀÅ nP·JR8"Ři¥+šžj¥¨Ï¬ØSJµJÓ÷4c£5S­Ð#–®ªà–1fLµ&Ô K˜‡*2æŒZ‰Åh¼YQ¯y\ N±nÁä„­ÚfÔDÔ©÷Ó'ŒTp³ŠÀ�$1U”d7Ã?€Õ¹?gZ pÒ[gZ……Ž —i^f›Ý~Ä}#Óê¼ì87fçn¶§ÍYFý9Ó:QZQŸaYá6»½:¯Îg¦Gçe±C:·jY‘ÛÁiJղɖì åZ/vJ¶àN[PíÖ+î$¹!¨¸Ú¿òÌrÿÊsWd.-û—gxfaË?[ލ¬[Ê?ªíŠ3 Û!M²9ØOk¹fOKŸj}šÉF XO¥—ÌŒ*‹xa{ÚžÛ4§RççÍt4Ùš'O@qªæZÏiï@l*ëÍÃ÷´<ªsÍœ¦jý 9è 1­¬£93—J™à'€§T'±ñ~³½L™™TWÀ§åhgòiU4óºÎ¸H.â®fê,Ž…šéGµœû…ÌW`•Y˜F­Mfi 첩 =lc(DÒÈv*Fè܋Р¨}ø0ÅLš¶©XE+ɼ¦O\ýt=›¯žJeÚ¸ï~¹Y¡Òt½â"fn–íêqG ƒFël=E¤~³¦å0lãš[¹5ÏÛÎæ&2ÂBÛÊ,¶«„¹(´íÍÚñ|EO“$móòy´•uq Z‹üÆ‘òFƱ7iöKrÿh&ã.똘TÔÈJû©[}ãí+%‡¿4Æ$µuàKÚ~aî6bU$Ägæ›ÐöRoFïLg[ï68¤èÜy ¯çáƒ.òÐ"-ÒÓºOxä…œ©< Âʇ/Ó f$Z ªWSÂTA\Êi"šÐ£žp‘à®<´à¬–AXZ¬SPÏÑ };tíŽ!«½# òZÑmé ·¹f¶g„QMÎè2C§6ï° ’ÇTbÔ< ʺ"ÐÅ>'Ï:x^˜VôŒ¾ÊU" *ê"ˆ‚ ÆwÌC‹½¶f×'í2º›%8-Ê¡…ÙÊ'ßxEèW— g5ù c6¶0‡¥À�ylá œÑ—©º±E9J&L{@“:êUeÖ¼ âÜ{ 4…£ÁxÛÒì8žïÖ¢hYŽyYòm¦?îÉ3â|“O<åf¦ÐÉ´šlœI=-^ªøî¦ÏÄ8½½è7»%™s¸ž(ÔÄq:µ–÷7—îû"0yá²%ƒ äµ×Ö¿ Ìí`k>gDÄyœ;Ò­.8qn»ç–i“äÔ…àðýô5ŸvQòüVìÉgìô]]ÏV¹B¦yÄøäW×f öÆ·é­qø4îÅÙ³‚­¸z*ˆ÷âçYÁVÜ<ä{ñÁ­`+.Ÿ öâïiz+þʼ_‹¯‡Ä»UZÀÕ‹õò(E˜äqò(µ}7Z‹wG)rrÄ­£Ä{c†P‹D«Ž8sHÐh µ,x›jA³å~+N’«‰8jHìçâžQb9}’ k$èÉ‘ƒÆd¤¥XÓ(ÇLRÃ=×]6¤Q‡ç¹ìDsB½6“ÉÎ3*„ûjQHkQ¦?Ê3ý&9Ö‚<é¾2ýž«?‡ß;ÙAæjˆO‡çý0[‹3‡\³]Gö…M*ΤBqãL*ïͤbñÛL*ͤRñÑL*ͤrqÉLª_LžÞ?L’ø`’ Åû’d$Ž—$cñº$™ˆË%ÉT¼-IfâlI2K’…xWb…Ž8U’ Ä£’d(þ”$#ñ£$‹%ÉD\(I¦â;I2wI’¹xJ’,Ä5’Öèˆ[$É@<"Œ‘éSG!“ Ä2©P¼“ŠÄó1©Xœ“JÄå1©Tœ“ÊÄË1©\“*Ä·‘§wÄ«‘d îŒ$Cqc$‰#ÉXüI&â¹H2‡E’™¸+’ÌÅK‘d!.ŠX¡£ž‰¤õJ$ªG"éH]IÇêŽH:QWDÒ©: ’ÎÔít®‡¤ u6¤]:êeHû¥Z¾nâÀÎÖÜȬ¸™;W?pCvª~à¦ìTýÀØ©ú›;°³õ7xNšÊogìnô tËGÍ„•R C·„ÔôAè‘?Ý2Ró¡[Hê�Aè–’º@ºÅ¤N„n9©‘[PêAä–]!rKоUlI%Ü’¢7DnIÑ"·¤è‘[RtˆÈ-)zDä–]"vKŠ>»%E§ˆÝ’¢WÄN¯|OI-ç#2PÚ-&ôZÊõl:±ëóÔìBÿŽqIe‰U©–v‰ÅÛ® ëýöY½~Å`]+°áA泫Òd,»^ð†–ñÀ.x‹1Ü‘VÞ-qs›=´ñ#^�X›“ë 5²KÊlaö&dÛ5°‹Ëv7"OR¥v6_>´R]ùÐ>Ÿ3ê민�jòbð0´MÉ`ùÿ'ïýúª²ýñs FJE††Šðæ÷DEù!&"!*ÉA@C³†Û8]§q§q ŒLÔëšy»Þ†Û×Ûõ6Þ®••™•™Ó8Žcæ˜9õÙëœuö~-kîýþñý>Ï÷y¾õ<¾×Z{Ÿ½×^¿öÚ?ζ«ÝÕù{±¦Hséí˜$h;k¢¥ Ëïn€>Ü‹·ël¨5õ-nÚ–ž Èþôí:yNÚãnl9qúÂ4S ìüma—@Y,퉻iqYÊeÎߥT¶÷,¾GBYt1]}r-rÜËW¼„È1Êõt®ºBŸbä8»îõÕ­|»+Ñ#óe/u¯¯¥'Kܽ˜â½[né©×RÜŠ~\·¬b±»õæv“æðá<ï­éƒ“œ2çf@Ý"o -^§ºª®ÅY*\ÃYM]+‘¿Ç è‘yûx.'ñz„sŠsõ±JŽ9'NЃ¸Æn‰ä‚jÆïÊÎN�Ù)M»ö™øCè!·8þÚ§jÉœë½ØÊE×È—:LLüûånÛIPaî”O ‰)?D/«ò"/—\«¹Dþêå ¢ ï)0 ‡~®pØßSdÂ5üIƒæÈý÷ÊiIîÝdëq×¥pcל6ÓUBò±ìÙÆÎ¾AyUr–X+Õºî¥JZš*êêeQâ55WÓŽH ßÑÂgtI²Ë`‹Ë ÞâEî’|w9ÙÞNŒˆPYÇǘ„¨<¶¥žï98[¶›º ÛéwŸ“º÷ê–™ÓÎbº8鬋²Õ²ñî²eKÔòÊ `Š´hy=“âݦJ¹)÷bž¼&%¸!,++gölþ+U¼æ°Å$µ,U³Ÿ{‚ Ö­$ñÝÒ;ÝöÍù¤ì#ÑécîôÙÎ@½Û>¡Õ­îµ·m¢üˆRèb‹×ô2¯iïŒE6ÄMÏ™’?½xžš¿ÕšÐ#‰ü´×±¦Óv¼.t§­Z·Ãº*÷€ª˜î•RÓ³‚2e‡´—âö¢È+ݪñŒWÑ $‡’Ö5˜Gyp¯¹}ýÀI`1]e¥n³²Ôš–yÌ+Ô;HˆñŠéy×À*-K®‰˜ŠBSsSuCusE³Z¥›šD¯\ÞT_Ñ ü¼¢¹ÅÉHêý)¼¤¤*ÙS˦OY6«±º!+ßë½,5gw[ªÈÝæ™^Oû Þ5;ž"z„xàžIªPƒÕ¾Wê•°(Ó8ŒÐÙ¥ògƒ»˜îøºÑ#‹¯óøbuÓ�×?l~Øá]Íyt̺¼±™žw£O~Ál÷ðŒò• :­qÑòÆU.ç„UT­¤K…´áœp-Í= ÷¨tCÐ9<KBŠw†ëѪW8G}ËZÜéÞ«XYѬfe÷2Jªá…|Têálx{T'^#giØw„àlÐÆû°È;FàÝøx,¬k® ÉßÞ×ß^+‹/ÙÇâˡԓ?Záœp[9N[:ÕôˆÎžwµ+ŸD¬ÉÊUs¨sYÑ+Ê+ô$¥’Ç9s•?V9ׯR¡‚“ØÒ¥FÞªóJ–7š‚4(ðt䎌u“໦†wßÕÙ¿Mˆ¿¦T8!áûÜ6.o¦Ë*”Ë/jæ½?YE_8«n­ó.y\S£IñîHWi‹îó6!×bYÒáoñyÜ-_ѴȲ[ÝÜrgPTëšì‹H×NR½¾1Œ×.ÎYA1]–wÂWî㊎5Šª¯ßø= Ýš\Nçžî® ‘<g#„JmÄÇ|¿áøô„ òe°k¨|#Ì0¡}Ûu=àÅ”¤x%+\™r¾í0FãvW-0˜Å*)Q¥ ßà KÕìmØÓõðT?îJ0áŠi<>ñÚ"½-mÓ-Ò±&SþŽPÊškØ—°9"ú¸s§L[ÉE_ ó}ŸH•â‘<%è‘ë¬îS_Õ"pgžý¼÷Me4µÝ]× ¦f¥­•Þd¯¤¢iq³»”‚tÇÇèØäZ;IJuÍ¿Ò5v&}Ë«˜Þq"ß”‚ieYþ4ŽxÆŽà)UþDŽqN™»Çîl»'êòÊåõ©I</hŠ?™'¢äª<JMi©©<�-ÍdzÑ +è”Guš¦I3ézqE‚þ‰t{ÅÊ /â;ý¥¥±zl§%³í‰ž’ÙîÜ®ò§³¥9è¬Ê%Yld‡áƾçfy!šÐl/,;®j©]®Ã0Q¦-÷‚+až$&¤¦˜&¾wµØx]csÙ”f+ëùÓÑ5Uê˯Å»6LS%™¦ã´x§  \isI"–LŸI÷zÒ;©œ\ýʵ²e‹(¸Ò­µð6!’Ýìz攬é¹³8à»öæÑœåšGroÊ™)Ö£ÓDbª²ÒªåMdþ&ãV6§<ªìNÅŒ÷f“ó¢Ðõ^É컹(Ù{éi¨WTØÒä¾ÅÄÏÜäLop¯•y5R½·x5rZU;7ŠÊ²œ‹,-ôÒWz?’$¢’¶²9EYe³ÊŠr¦ÌžU æ‡;«(gú´‚²œ’¬œÂâé³ Ê²¦Ì™–W¬Äå–O)ž’_V˜7evNBYNQѬ"%µkKâ¹$1†;)š©ŠfÏ*TBsH´œ›•«HS²f(¯uhyS ²ósŠ s ²•ã:ÄéªN~>}È¡8§¤Xù®C&tzÁœœ²9wNWµÝÍ'ëFOEÕÍ4üåUôJYŠo¢2Uðýˆª+Ô%—^K‰Ÿ˜œ’0qlrJâÄšññŽFîùÞ3J‚)I›ªu+|Ã1Ùレ†NóÎ_Aq_Tã•aY¼óç\hvb‚KTó»÷J›¶•)î¥Åd2¿Ox«6£–åÞ�RÔ�Ri�þ‰5jàT-Ê«VÜD×ÇÝzi“S}ª^j¼®7V›^uS3]C¥µ¤S;5AÕN¤ÚIºö²æúêJÅ~Y ÕÐ<¤lÒµý.ZA-:ÛÅÉ©©WøéžL< lEBúøTz-/eФôñDML˜°ÂŸ®žvk4«‰nfU#Þ­ÑL5âÝޏ2kªª½›ª?½6H—ÜÏÉ©ij~ßÄ¥J¹îSî¸Tx‹Ðžèîg«çýñ›“ü‹¼Ÿ¹mqüýüP•‡²2WÐ3QlVKVçz)‰ÎŸ4qi²?ybk3ßÖW¦é¶æì_П&¡Z¤;2™î-'ûé…½Tîy‚gÍôGHT•ôwG&6û›ÝmW\ñTÒ»ÌÝqHñŠ´ÌYçè­"«‚ ‚ãg8OÅÇ$Ä«.žëÒS=’Sh h—œOÇ+e%¬¥ FZˆi ?(DEpÖTžIqn ú[,,J"jùxc÷T&ù !«rä#‡7¦j;·´ÞJS~›¤õçêc>ÔŠ”é¾$ÚÌ:Íó½+<J ¿É{Ý g_QtD¾®Ù#ù=÷°‚.2)ß'ÎåBš«R|>2X¾›NÉűò¬é9tý‹þâ ))aß sþD‹âš_eM5–UL¦Öìÿ¡†ªªëÉt[Σýé¡–ŸŠ~‡«ð²²Å¼¿¿´zùrŠ/Ç~ƒ)[î¼ÔD…‰\8Ü.[¡ÖŒni—F™R}µëyâ} ’„‘2±º(g}ÎbÊÜœlúÚ‚ÂX͹³shZ2$Ñç4žSRHßTÐø\5 ñcIשjrŒÛWvNîô‚šÉÄ;œ¤N¬¦”Wq¢žöÅLµ5D FxÅßÇ]é9ëZ‹ðš/ŧâQBbj Î¥øÒ&V4%ø&¨ÿ”Ÿ¥Ä»º§5`|¼rmªD³c¤¼@pßdvf˜Fú»2ǦÄ'ªh–ìù¦óS»ünçOâ(Ó SDð§øýj–©^I]F=åó»4ǟ̪ˆÚ÷Ó7†™î1ï§;OLäΉè÷yD·Så¹~úÖ–­ó™\’Èlg×’Ÿ<1%>…\11AõÐìü!ÅešÏ§¸tÝ`'N˜ÙÈùYª ÕaϹléÌ).Ûit&à„‡ªjMw&SŸÃdSu ¹£ÞDrËLð1‘KTîïŸPß\UqmêÅ?¡¹âî²ÕLH"t.°ñŸéJŠþ *øðD§(0¯Ÿð“Å[+Å)ñ©Ê�TÇ:âýÂ:œ`E]Mp¼~vFåbI®°ˆ_weäÆ=ú RiôÕx'¸—2iôi©nˆÓÑÄ¡¹<šŸCõ»¡¶r¯âSâ¥ñŽOˆI£/*Åǧp0ª€ù>%ÁGóýÿó³ý1ð3êFMשII,!×O´ø]Eƒ­SSäÍh¢+ò[•Ós프=âøøä T‘& $5ní)jTÉtªØ¾ÆÚœÅ H-¯«èO ÉöTò¦ÛP[‹"OT㣂ö5/k¤"åM )œQÑüsFîÌO:~8Mr/çmé5)î"ú6¦18@iW! ”…¤:ó@ˆó‰†ô&wŽ v&¯&wâé´æn»oº:ok;ó©×zí=Eîc!Nq‘â{¤$Ç¥„º÷qÓ›ÔÒÆé³E ²Ie’„¬ÝPݪ¦ç2€Þó÷²öu Í ŽêõØ ¥e·«©úO£HšêN…ÍŠè#‚ÓO0=è :¸8è«BsâܺñØ m¨¾»¬©9½)ÅýDF¨sãÚàá+î.Ã)¢Fe«^bݹrÆÌ 9ÝÓ<ݤ2JBSÎД’¨2°”Ä„‰KUöàÔñÈ‹uš©`'80¿a"¬º´až@Ý+ËOÉÌ£%á(ÉþÎlÒ$?pƒÓý•+mä©©¼ÖŒ†¼Ÿ7ù¸R märO´î­ÿ&73">šâsìWS×ÔlÚ m®¦×µ5ÔpÀ‰îk¿A)ÅÝ÷¶»Õͳ§Î"ÂS ›J?e eÍô·É\W7i“a7ñ®ÿ^Óºa½jÏ]Þ´¨ºÊ[‚Õ*'IÐ61@-(É’he—äú¥Zó©öSœöF›t+œ—¼X<!ä™TÇi£¯2#¶è~îZ²éšudQE]sµY_ÃD°Ç„Óã2ç“ µ U´Ã-Ñ8É~@€–¯ ª)6¹ìõw)MšÚ¬¤Y¿¼Å“çp§5eIÎÛU¦Aײû«ô±‰FëùIh‹@µÁû,òÑG‹¤ÏÝé¬íÁÞ §U·L+JÏe2[Ñx^ÌšJ*ÅÓµôó݇yŒŽÎ‹~ ƒé…ô¨k ‚<òB¯(¼Ž^÷©¡n'ä(Y'Oër‚=Rk×ôè<MçóNël~Í/›–¿¹®" ^‘óÈlÚìÒ|ŒÔ&©þÉiXT¿œV²¹|ÒâµBÁ¢ÓŒk›§n‹ªë§RÈ÷šÇòb¥Óï—/6æ¦$ªÕ1ÏèF>«Úí€[³Î™€¯ñT-õìêÊ‹ó–/_ª 3L2Ô³ç¢k$%=C±êºÄÀ–Úºæk£B˜2Ìki¡âšØxuGl—Ã( èn\g/úaggV‡˜½¯fU -äfI§Œ¦¨ÚùˆnþF#œúê&·¡¥7Uuz·«Z?ÛÏEoŽÏ vÐï17Æ]™RFEYV™û~áì”ÄT™E:nIÉ8݆¡÷ÓœUaÊDZÑ¢…vå¨N†³1°hÑŠe+ê³ÎbZNÑò©®¡º¢Éù»­nþ×D/ï.rð¡¨áôüŠ/yáÞÒ¹õ å•jDinào¥YÎÉâiŠugÕUÑ‘»k«t¥¡¶º¢ÑL0 UÙçše…3eRcnL®_î…š¾µuèf<²}jÛÙòÚïï¬H Íi ‚žq¦ã@Ú³îH{yÌÒTÎãþ·.‘M¢å¥ëðļ“ypÖ¦–q^Ö6t1Ï×Á*Šeé$)E´Ú£K')!U+–-Ó™ÇÎëßΦ•»m[×ÜT›L ˆSaàôÙESb“â©%•*òâ¢ÓŒ³ [W³Ê»‘J·›h?¤9½Æ‘Ç�Ç_)‡òÔ7@=á0NÂs'nw{É É^y†s³Wo^wÛçûë}.¯ Ý7pY`Í]GvK PÎéKÓ&ïnj^k”}[5Ó!­eœê;Ø*ÄÂ](kVÿ¨ Ú홟#æÜk\¹¯cgn2Éð¶÷H°Ã,³ä,‘\É ¤G|NT¦q|ßè›Øè—ð#Kª²õJª@® BK¹·~¦€Ð>u üDˆj8AûD_·ogHn°5ë[£«¯¬¦O¹¹îÑG¹š[ãfbRî†^+ë[Êô 2Úó.¢û¹z.JNu„âî*zSbb#~°=ÝÒ°k‹uI÷á6sÛ³å¾I/™û;#p¯W)â{•dùÈ2çbøß„7Iþp-¯<üûå0rH5Êr³sÔTï*/Ìí£ˆ¾c9³ ¸ÜÄq9Ô± @.îÚ„›‡;[¸lâCV44WW7°]7§Ïæúƒ~˜ÊÃÙ’L_4±ÎÇ#øþž&l†6Ð…MóžéR=¼×®hÅùØKr×$ui˜S·HM|~}Â-qV£ù¢Œ~euY#=í±%Xö:vd篈Ǧ¨løtäéUwäÉ<·T4/uÊ6lèü¼W"ÏA}¾¿{ê‹ÿ;ç ´÷ü?ŸƒÒôÿzêKç ¾äÿ_ƒª<J ™*üoç *ÛJöÑ–ŒJªþÇsPÚ³þsP_Òß=õyGmpêóýÀ9(ÿÇsP•ýÝsP•c%Sú’O'–>¹<‚sP•Á$Sj’¬R¯Þß?U©G2ÍÚÉjÞójËsЄø>MHøÁsP5õý¿tzÀÙ¹VY£ ½n4VL$MLV³9Éï®÷”W¹Oä‚«œçkvÎFDÖNq±ó-½XBªsìÀ*ªà¯¾ñ‡S¼³9·Ü@§ó9ÿç:\s]˵e~ï,ëÙ\T]¸Ó£»—èÓ™\à&¨Éζ¢[ÈÛˆnbAw‘ó1­à•>µ¦á,ÝÛç|¼¿;:5%»®0ôZƒW$¹“M5Ëœ:'Kq'³QzO/™6“ã'¶6_{�àpé¨JiÊ™X½ëRúc=î)vr ÝC¦ì¥±‚6½Üq­ot¯®@æêø|ÿEÍt®ÙOhÇŠÑB=Ì ³zeª¬p—uÝa]JˆŸmîbjIÐX›Âæy5%Ög4EŸëz»Xßp˲?ðž ª~¼²þ5ê'Éî[¨~Rf f¬\µYé=wÝâl£*+÷ÊÂHP~¯là0ÄQ ¯‡!j|ö;ŸCòégS8cûÕhöÚ¡z˜¬1Qº×ÊõJC“½Vþ',¼IýLòž»aJâ†+ê'Ó+‹hWýòÊn¤1LõÆpcJþÆÃ*Næx=üOØM–59Ë{îæXõ“ëõI2Ëó°[K±÷¨Iêçv¯÷¨lì=ê[õ“í=7*JÍC«½þÆìVy÷z‹ P³ô>KP’¿õ[KÉSîò‚ÆÖ¨¬÷y›ÐjYÉqÿßÇ2bPº$Ï|O.“NaÙäWÕOW–u­ ë by= ëñ°éC,kÂ@) Nžäaù籇K`!ê§Æëoö'ê§ÖÊköbsnØ%õ³ÄÃd"Ÿ :°¿çð¹Ò‘ê§ÞÃÊB°¬|€ÀÞØUlsÑ~Ī¢Q.Uóñ¹š<¬¹8Ë·Š²CÝX¬Y—‰5—ô²%‘X¶ô0j¥þ0r¶<{h\‡Ø."Ö-°Bìáî ,»»ËV“z<$Ì õ"ØjåÅfè²ÿÛØ½»±‡ûʶDýz’h[‡6x)Ö¼b*ILŽÓXŒÀvªŸ¹^|yðuõSâõ°žôžæÕüÅÝR¯lév¡÷܆ÛÕO¹ÆÖbï¿úBýTzÏ=Ü °‹ÈËÃßbÙ¯›v±õøÜÆ•Xö›£y¬JcÂ’sÇ÷ÈF}‚Ï=: Ûl§²"ÛD^U¬1šÌçkLÙË„ížn¯Á^S? ^ÍÍ“öb)›š<Ä{î±LÃc$ùF¯æãÔ¾Ð+{ü„Â{ØeIS<ì á©O(y&­ö°ÎH|®3I`깤Ÿhl›xn¿À‹š=ˆ=ia›Oe™ØÊ“Å¢¦ÒQR¼ÆºF½{ØVÒJ“'—­d- S ĺæBQ³\•}£Ë^P?+uÙI]P?­öåR«5öÖ|ê5½%0òŽ5¶rÒû<l;EÅ6©Ÿû5¦¢©ý©gÛUÆbîq½bë5¶Wiz¬ÆÎRMï¹%TSc47®õzØA³ágºŒ<Ug%O«,ÈþƒÆ”½ØÒX¥úyÀkåiŠ»ðZyú<Õô°.š;Öy5»îW?jŒâÒz‘ÌÒek<ìšãÖùÃFQ yDc›ÕO»ÆhNÝìaÏVaœv‹úéðÊž¤~¶hLùJŸó^ÍßîÆ9gO¨*»ª1%ÝÀ`íÆ¼îyåGs5¦Ú xYcTÙ)=Þìa/¨¹*p£ÆJÖ„#zaµú9ª1ŠÉ3uM•õ¹ÏÃþIåös^ͽa8¢½éØæÞ©ØæÞ|lso¹jóN)û ÌטòÛ>SQªÏ';¢jj^öžA¹ì½¬°ŸyØ‹a¨££°æ‹I¢f!ö÷b­*kÑØƒ8¢Æ½Ø#zIõÐg…ÆTîÝg™Æ.`+/]ÁVö`+ûIc{4¶ŸÛ¿E<§¢éäH¯æ?«È®W|ÿ\޳Ó?Ø1ØeÄ^@ì_¢pý•é„kéþË~QvX`=;Ø�)ϱo÷Æw€l>I—)‹ì«5ö¯~U6Ucë±æ¿nE™ý+ųºLÍqÚW^Q¨×¢¯”c+¯´c¯¼¢8[èµùÊE”Ëï„”~Žœý.ÛüŠ}·iLq=øW{¹þÒmÀ3^Yw�Öìe*ƒ°÷x¹T·Xéw—àøº—`ÝkP.݉²½8¢îƒ{WÔ<ƒòì1òßH›/xØ«Â^Uò41òÿjBì eŽ£UÏVûwŠ×Û4F\ïÐÍ›]#Ûéa¯W?»=ì?h®#ØÐlÆîÇçQ,×c8D5÷jìõ³Oc/ãsÿI|î×Õ|ÙÃ^§ÊQ&÷ŠÆ( wkŒò‰W5v{ø=e±5¶ËS~Mc$ÝCÛ5ÿ‹Ö¾¯kŒjÖe%ohì|îåKG4F뀷4F³¯ŽRGN`t{ÓÂÙâÍøÜ›Ãð¹7#Ås±¢fЍ™!j’}¾«ËŽQV╽=WýóÊÞ‘èè+ØÃ;ÄËq{—ttBc¢ŒòÁ“SsÇä"¯ÍwIž=ºl(#}âùí»{EÙ+¢Íâì]ÑæIí»=”åéšçë÷úàøÞ#;;åaÇÂQÇŽ ¿¿?Ÿ{ZÁûÔƒöÆãä©ÚÇŽ“iÏ9NÖ£ýáÊÁ´|@ ö€(zk‹üà8ö~âv”ü‡ 8¾ïe¡?lG ~¸%øánQvµòá)QvN´yËNöÁ6O@­œ C­œ¼ ¹>™‚ã;ùªÀŽa„þh–}%Ê6ãØ?RV09O—‰5sO+ê¶ç~ä¥çal¥g+޶GH©g޶§[”C ö\Ʋ{a›‡‰²áØæÇ#Q‚G¡?N@®?¾åòq-îr|LY×i]F3Âûd!¶òI¶ò =÷…‡}º %xj3ÖüŒâàYQs#8¯1Ц<ì4­e.jŒfæKö¹ðÔÏÉ®/kŒü芇![ºª1Ñ·S|Ú–Æ”]Û½<ìÊ–ì�Í¥7b4¦¬ÀÒ˜Šåvˆ‡}¡ìÚÕ˜Šƒö�©ùÝÓ˜šìAöG5"{ˆÆr6LcÊíp)°‡kì[…ExØY…í›4ö�Êó,=©1zn¤‡ý)µò§•hç&a+çîÃþν"j~‹­üyö÷gz.Jc”+ŽÖ˜à弊»v´‡ýEY=Vc$‰©fÇzØå9¶>—¹ rE[ŸÄ|™€Öó¥­­Ïe¾üBa)vñßE•±Ø~ûŠ,Y{ÿWI¸vúJÌ_ }~U#ž£ñéÓ–¯h®Ò±à«ô÷¯h'CÇ‚¯Dœ¸DqBÇ‚KęޗB1\Žœ]¢±ë£Kb¸$æ€Kb¸$¢Û¥—q|—(&ë\ãÒaÌ5.“ÆôÑeµ:³õ©ÐåçP+_Ç6¿‰m~ƒm^!ÝNõÊ®¼¡0}fó ­§µ¿s?zø7¤M}FôÊÄm}Ftu4ö~5玿‰5×ßÒÑûÿ¦¢¢­OFþ¦â™­ÏBþö:ÆoÉ>õ^ó·jV³õŽç·ó1j|Kv­÷?¿}£Æ·*õçzØwŠC[ï.—‰k ïDÔÿn Úàw*2ØzOõ»NÜGùnÚËw¯‰VŽ‹V„}~w-ä»oÁzl+Êlk4Ä:Ûªèf[d!¥{ Ö”¶ux±­Ë #ÛV‘Ý.÷ÊlŠÐ•£]ÐØ%ðp»×Bl³W+¶Ù«äb÷"ÝVéš/C$²{))Ù5{¤k÷�Öc÷ö©¼}»w:öÞ»{ïM¹›ìÞ­(ÁÞ÷ƒäíÞDÙf”|oòâw5¶ "ƒÝûдÝ[åɶw*d G®(Fz§evàJ, |W`*·±½ó1»Ï_Ÿû R„ííÈÛÁ” \ÒÙ¼·o‡¤ <CFyˆ7÷ÛýhG÷ªÆ„8a÷ûbš±ÀÝ>m‡Ÿ¶û/¶û€Ûý—€ßÚý·€ßÚý_�¿µûŸC›0m~@ Zä€-h­ÞB™ ÿ;ª±ØÝ²\„u£}]Ä ûº©è×­F»Olí0Ê<¼½{;¬�­<ŒÖcÇ5ö2ÚgØA´Á°7Ð>ÃNˆ²ÓhŸaçÐ>ÃΣ}†}‹ö9³{{`,ò2pö70ûX„ý ,eëPfýmÅÑТ±&Qv?JbÐzìaÐ#ÈÙ m¢ì9älÐ>”Ä ý(‰A¯!gƒN¢$]B^÷ÁþãúÁŒë{p”(+DI žý ^"z 8á¶ØCSЫ†’Wµj¬=g(ùÊj=Œ0”2ñ5{ }e(õçàØÃ†£Ç £}ö6MBF–|¿ÆîCFyÁZQìÑQc˜²rÛ;m±¯'^Öiìô‡ðË(³’FÞï½ØÃ¿@ ŽÁ6"g™Sq–Q„zQ‰z±R´I9‘wºcGLÂ"JpΉ­D´áœs#EïTÈIQjƒÆ>Áçn¥9Ü;#²£R §µ£pwÄŽ:‰u­.êZdîØ£(§Õ^5jˆ(» Û#ÊR°ÍQ´WÊD¯U€ÒMùÙF BMßö°À¶àlÛ”uôC8FS™wZfy2N{ÌX[ØcžÃ9`ÌÌ{ÆÃlflä»öØÈ�í±K ÿ´ÇR–×®±­qÚã&ásãF®c lê}|:r6þeä,69‹Ý‡=Ä¥ /qÝ–5°ÁӃϯ°E£=GïÔËŽB[ŠWú›¼Tc˜áÚñ¥hÉñ= «ÑØyñÜ|.!ŸKPœM.ÕØQì=1�[IÄY;1[I¤u•öÍD1S&Š™2QÌ”‰b¦L3e¢˜)ÅL™(fÊ$1S&‰™2IÌ”Ib¦L3e’˜)“^=àn¶üöü*Ž6wfíäw±÷äS¢ìöž| G›|G›ÒyI¹ yqÞ/Ô¼¤äb)…Ø_Ê|ì/¥J”½*z9Xj:öZƒ£MmÄVRWcï©ëDÙÃØ{êfmjŽ6u'ò’úªàå¸àå ÑßEÑŸXùÅzÌ_ƒ=øWbþ“ØCšˆÉi"&§‰˜œ&bršˆÉi"&§eâhÓDLN«A^Òî¼<$úkýmýáÞ…‚=¤ã®ƒ~?öþŽ6}¿håUì=ýˆ(;޽§‚£M?…£M¿€¼LA^&ŒD^&$`2°¿ ¹Øßaåž=à Ž1{È(ÂÑfïϨÁÞ3ZDÙ}Ø{Æ8ÚŒu8ÚŒG—Œç/¯ ^DœÈèý}!úV>QäKE¦3ñuìa’°³I EYö>I¬¼'ݽO¹þ¤P‚“ˆ²×D›GEÙIÑæi”à¤3(ÁI—ëÉbš,òÁÉ[‘—ÉÂ&ïC^&‹ùhòÈËd<)´'C^&Ÿ¼|‹¼dâÙ 9ûËôa™éØ_f¶(ÛŠ=d¾€=L±` žÚSð¤Ðž‚'…ö1MÁ“B{ žÚSzp´Sð¤ÐžŠçöÔ›—©±ØßT?ö7u*ö75_”‰8?õeì!KÄ‚¬mV ¶’U‰½g‰ù(k5öžu?Ž6k-Ž6K¬„³v ^^¼¼%úùK–È_²ÎcY¶ð¿l± —-bAöm¶X#äˆÕnŽ˜rnÂÞsFãhs¢q´9)ÈKîÝÛ9¸woç¬ýµ‰þýme"ÎçöÂrÅê:÷mn'¶’»{ÏóQî«Ø{îë8ÚÜÃ8ÚÜã‚— ÈË4<á°§ Çþ¦‰ý‚i±Øß4aåÓDœŸ¶{È€=ä¥ãhó„÷ç`ïyb>Ê«ÁÞóp´y8Ú¼û—¼G/Ï ^DœÈ;$ú{Kô'¬|z:ö0]¬I¦¿€=L?Ž£.²ßéxrnOV~;žœÛ·ãɹ};žœÛ·D^nOG^n/B^nqâözÑßJÑ_›(Û!zÀÓ{ÆXìa†È'fýÍùÄ ‘OÌùÄ ‘OÌùÄŒÍÈË 1?̳ýŒã¢?!ùbÝ1OÙì|¼‰`çOÂòÅþ`þë8Ú|1¿ç‹ü³¢ìöžÿ-Žv¦…£9y™9y™9y™Y„ýÍ’Ÿ)<g¦˜f ¿)rÅõ Äú¯�ï3ØB‚³ð>ƒ=KìGÎ޼ÌÂû ö,¼Ï`ÏÂû ö,ác³D¼ž%¢é,±›µVôþè]¬f‰õ߬/°‡BáG…«qì…¢‡B±B)ìe;°÷Âçpì…{pì…¯ /…tÒ´Ycb'¸Pìß!æÆ;„äï’¿CDö;ÄÌ|GöpÇ[¢‡+8ö¢�l¥h�ö^!ÊFcïE±8ö"޽h*òR4y){ðEâD¬HäåEˆþ:E™ØY/{œ³s±‡Ùâ<n¶X̽Ïçq³7cï³ÅyÜlq7O^íÙo^N ^.aÅöW‚ý‹œ¨XÌUÅb}T,²®báÓŧ矞#,kް¬9§çŸž#²®9‹爬kŽÈºæ´‰þDÖ5Gd]sÄžÕœw±‡¹â,kî[8Ú¹b~Ÿ{{Ÿ+Ö›sÅ®Ê}p´wáh܉w5í;…Ý9û»³û»s‰èOÌ›wvâùÃb5q§XM”ˆõJÉT{‰X“”^JÄYIòR²Ç^ÒŠc/y�y)Ù"xÙ'x«ä±«R"vUJÄ>o�ö0o$ö0ïìažX)Î+Åy"œ'VŠóÄJqžX)ÎëÁÑÎ+Åùb¥8_H~¾X)Î9ô|±Rœ/´2¥èáAÑÃ9ìaÁ í…ˆUù‚$Q6 {_‹£]‡£]0yY f‹Bò ÄIè‚NÑßNÑŸÐÊ‚/DW°‡ÒJì¡ôm©ˆÂ¥›±÷Ò.Qöö^ú2޶ô�޶ôuä¥TÌ¥W—»B°¿»D¾K¬LïŠexçÒ¾KÈó.1ûÞu GK™VŠÞ†‹²‘Øû±8Ú…18Ú…b¶XX„¼,yÖB‘g-YÐB‘g-yÖB‘g-º,{([ˆ£-Ã;ävYö^Ö&ÊÄüPö0޶l#޶Lì6•‰ˆY&$_ö‰èOäóe"Ÿ/Z)‹=”‹ÕD¹XM”¿†£-q°\¬eÊÅyj¹8O-¿‚£¥7ÐÍh+Ä.x…ˆ˜Bò·cÅØ_ÅBì¯Bh¥b‹èAìöVÂ*Sp´•™ØJ¥è½RìuUVbï•Kp´•õ8ÚÊÕÈKå‘ T¾,ú{£•bo´ò–-ê…=,'p‹D^^ÕG[%²§*±ãR%"CUö^•Ž£­ÊÀÑVÝŽ¼T‰,¯jµàe=žoVuãM *몎‹çÄIS•ȉªDNT-Nšª‡á‚ê6ËÜ·«ñ-;»f5r]³OW×à‰Å¢æbò#}{ª–"íy)Iè;Áví<Ó¯ýÏôëFã­::UÕØsxCªîÞX‹§øKȲ¶jìž÷/¥Sü ­ÄûK• ê;ÏöRº·æÝO¶ë)/èÔÝj¹IcÄ‹wãÚ^FRš¤1ÒŸ¾_×@³öE©¹Q߇¶è¾¾{·\E}c×^Nþ~Nct7Æ»·m7à ©FºëàÝ÷¶D·Û£5Fw\FkŒî‚ë{€MäGg5F·:4F¾é½Qe7Ó-š-ÃïðØÍ1ËGëiÝŒö²ªÚõ*q´z8ÚÙêûvo}¬¾‚¶tO´¥{D´Y#ÎlîûÞ÷Ò- ­Ûû¨m=÷)+··iŒ²<ïÞ½ýc²k}sóÇ4‹êšm vkŒ2\ï#û(¢ì×íHxoÙ÷“´^ÑÍáÞGöOHfúÎåO(²{ï[Øki&Ñw©Ö/ÞûöOoÇ»b?}�±èöM/‘?è›c¼€Ø?’õÑØV¼×õ'[7ß2ofØëèV®¾9ö³/4FÚÔ÷ÈLñâ½µaÿ\Üdþy¥Àˆï{½ÊžÌ}·õ!ö âe¸Ææb¿ ?Ò·Ø~±¹þùŸ¾ýö‹Ó8ö‡Èvõ ·‡bQ‚Ÿú&ÞC+QÑí7}ƒï!ŠYú.Ü/{áø~IrÑ÷ò~y;Žè——ô}¾_Ò›'úöð²k}ÇÌùÎÉ1‘}¡1ʺ¼7ðì_ÑœzZc¤Û#¹ti ßô¦uŽ®ù0e ºæÃd»ÏiŒ²'ï-Wû×”•ìÕÍ2û4F~ä½­jo$8¨1Z-½¦1Ê?iŒ2ïÝUû7”½ª1ŠÉ‡5FQê ‘-é›Ì¿¹€{„lPßd~$­àº+­o2?r?ZÁ#t?Kßd~äu´¬GhÎÑ7™„ù(Ý«Ô7‹6ñ(Ùµ¾güèV´‰G鞸¾uܾ3öC–5ðß¼ÝNs•¾åÜN+}}Ëy“¸í³i4ÖÜD'Ë3uÍ¢l¶òØ9œ;»Š³EÇ6ÅËë{ùì8€9Êãb?òñJìïñzäÅù6Œ~î‰b• ¬±'Å'30_Ú:D`Ñ£ìBßàÛZ‚eÛ6À["ö6üî½ ¿{`o/ÁšÛ×Ã× ìítßt§—ÅnßïÔÛÛÅýÏí'†_²IE†³aŽcØoÚ;jEMZSz_°Ÿ®Âû´O_Åš]Ñ8¢®l³ë2Ö|¦Gû ;»½Ñ>³GûŒ8S|Fì ìÜ _œ°wâןì]ÅÈË.‘ ìÆïKÙ»Ís‘Öut[òÂd‹±v˜Qø×–'°uëR¼üÞÃZ+X§À”ßêaƒ|Û °Ä«ñX©±L)ÝÞøSu Lõwóã6$\`J.·ü§ÆÖ L/ª·ÆŽLIiÔK6´D`­–5f¬Æ!6,H`…k˜xîúpíA,<O`Ç»¡Q`ˆ ˜j%ù1u ì<b# ¶G`ª÷ ó<,"H`~)ýMÐzˆP½OxRcJ›ÞÕØ~u+[zVcdK©S:º~šÆ/#h¬GÙÄ©ˆróCSã»e¿Æ”n£,»Qý;j¯ÆÔˆnû'©Ø“¯1¥£ 35©°§5F‘v˜Æ”$ìCS’°_Ó˜’nh ÆJV.°Z5 ¬U`m['° kX§Àº¶G`JGƒÍˆHGFfJGƒ_ÐØ) 6cW:|PcJGƒ_Õ˜ÒÑàn) ~ÅÃhz|@cJGƒ_Ö˜ÒÑ`­Û›”ŽïÓ˜ÒQm»7E Ì'0¿À2–'°B•¬\`µkX«ÀÚ¶N`Ö.°Nu lÀö ¬[`‡vD`ÇÖ#°3;/°ËˆÝl ,H`a ˜ÐßÍB7 ýÝ,ôw³ÐßÍB7 ýÝ,ôwóeú®‡EÒO¬ÆŸ#k,\”)ΆÚó‰2ÅË0=§F扲Œ­‘墬Q”µŠ²u¢lƒ(ëÄ(Ù%Êö‹²nQ¦ônk‹<&Ê辩ö¸ÈóXv‹ú×ÖþwK(£ýíÅ·DŠ2½í¡1¿(SR è§±BQ¦¤Ô7Dcµ¢LI©÷Uµ‰2%¥S4Ö.ʺè]íeÝ´ ÑØ!QvŒVëeçé O»Œe#ƒèKT e‘ô:E‹2úzW°Æ2EY!}ÍNc%¢Œ¾Ìv³ÆE™’Rà8­eíô.uв=ô½.íe‡è«t;"Êzè{r;#Êè;tåv«…e·†Ñ_\јð¿[éÛo-þwk&}÷NcÂÿn¥ï|ýLc墬‘¾ù§1á·®£oÍiLøß­ô}> ÿ»u?}/OcÝ¢ì}÷NcÂÿn=C_ÈÓ˜ð¿(‹¾1¨1áQáô@EŠ2}_QcÂÿ¢òèûŠþUNßwÓ˜ð¿¨VúN›ÆÚD™’RßßjLø_”’RßÕ˜ð¿(%¥¾zm%ü/JI©ïqõˆ2%¥¾g4&üo”’RßKþ7JI)(@cÂÿF)) ÒX¦(SR ŠÔ˜ð¿QJJA> ÿ¥¤4AcÂÿF))M×X§(SR š£1ᣔ”‚ª4&üo”’RP“Æ„ÿRR jó0ÚF1e£•”‚tF=Zøßh%¥  ÿ­¤´ScÂÿF+)é¹ct¹(SR Ò™ãhᣕ”‚Þјð¿ÑJJAŸjLøßh%¥ ¿h¬[”))}§1ᣕ”‚ûkLøßmêßàþw›’RðEŠ2%¥`½²¹MøßmJJÁYþw›’Rð, ÿ»MI)X¯ˆnkeJJÁ:¹Møßm]ô`5&üï6%¥àkLøßmJJÁ?×X(SR ~TcÂÿ¢•”‚ŸÒ˜ð¿h%¥`'¢…ÿE+)ë%:S”))ëJ´ð¿h%¥`m/ÑÂÿ¢•”‚uŒŒþ­¤|Nc¢LI)øŠÆ„ÿE+)…ôјð¿h%¥C¢…ÿE+)…Üäac,,£¤2^cÂÿÆ()…ø5&üoŒ’Rˆ¶¥1ÂÿÆ()…h[S.Ê””B´½Œþ7fýa` ÿ£¤r¯Æ„ÿQR Y§±nQ¦¤²AcÂÿÆ()…lÖ˜ð?º>²]cÂÿÆ*)…h[)Ê””Bt|+üo¬’RÈ ÿ«¤¢çœ±ÂÿÆ*)õÓúÛ&Ê””úéݘ±ÂÿÆ*)õ›¨1ác•”úý»Æ„ÿURê÷¶ÆzD™’R?m×c…ÿSRê§ízœð¿qJJýôzeœð¿q~Ü—)0±“8NÉl„΂Æ);±TcJ‚#ª5V+žkÄÄq­Sòš¨1òÔ‰}Åqí봬뵌ëÂ]±qb§mÜ~\WS’·†kŒ$¯óëqÊZ'è¨q´+¦ýhé!\cgD™ÒÊ=£SZ™ãa1ÖŒ Xò£ì:YK"&R”‰]¿eåôˆbü¸Ó£ôgëݘºÁ÷–Æ V"j– ¬V`£¨¡wÚb”þ¬^#ýiŒbˆŽ˜1äz7-FéÏÒ^CEçY1ä+#5FQ8Jc¤¿Ñ#ýEkŒb²‘E›)ý Ô^£ô7@ûJŒÒßÀÏ5&v9Æ[»ãÅ.Çx±Ë1>R`b—c¼Øå/v9Æg LìrŒ»ãÅ.Çør‰]ªñb—j¼Ø¥ß&0±K5^ìR»Tã;&v©Æ‹]ªñb—j|7î@Ž'ýéys¼Ø¥/v©Æ÷LìR»Tã…þèâm¨Þuˆú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹ú‹í˜ØeŒú‹ú‹ú‹ú‹ú‹ú‹³&ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô'ô·÷Äã„þâÚqŸ=®÷Äã„þâöà®{œÐ}ÅìÁÇ ýÅ ýÅÃ=ø8¥?K¯ ã„þâÎãé@œÐ¥­æTÁ'ôçúó ýù„þ|J½tœð ýù„þ|B>¡?_¡hEèÏ'ôçúó ýù„þ|B>á>¡?Ÿð?_§à¥K`Âÿ|B¾nQSèÏwD”ÃÓ_Æ]ŸÐ}‡'´¯Æ„þèO&ô/ô/ô¯ôªw·â•þBõ'ý™A3ûÒßݨ×qôÝ·¬÷Ǿ~¿o·ø¿0ë ýØ ÓY/†c€î:½,Õ›áRõÀ0e% +iÒ+{ŸU¿Ý¦¯2mþ;­®†0\ôb SN4”á5@_kè¯Q± ·©Ÿq ¯W?1 ÓxÇ3Ü¡~â¦Ìæzþú¹á� ‡ú!âô Š`8èÙ†þŸJêÎÁ´’¾™á ¯ú^õ3Ša’[4à ÏÿI«“[\øu*Ép ÐéOGF1\eÚ|½è†þûzÃÃaŸió¿¨ÍÛ®uùé}U­åkNßè4º{ã°åèÝ�ý(Ðý”¥mé3@?kêÿw¤ú f˜ø a˜Æ5ˆa'fø ÑÝúaC'ÖœÃl‚3ÜŽÔùùi7r8Bm†3Lü³m¹¨~Üû&Ö›d3üEâ7‰ç~ g»í(¼¶‘á›fìoî¶´¯½¹è€Þmi_{ó¨+Ã�åýš6ß 0ϾEvÂ~úøõ[> “ßõa¸ÍÈÿ­uFþoÑš!”aû�õ¸ÒÏ€S¦ß·£Mûoû-­ß·3€^lø»èå@_køy{Ð×øéËðÃóÛä#l'o_5ü;âaúÑ� ¹cq`â™ÿÔQây õF§G7B;û~qå ävÄÈäèe3Æw†=¾ô G= àh¨z|'è™f\ï>ß©…úõ@oz‹û;mfìït[Ú×Þ9õ{ŒLÞ9uÈ8>¿Krf|—vOØߥñ²½cú}×ô$ Ó9®¾K¶Áþûn—û{¦Î{‘®æ)#.Þƒ¹æ=²=¶Ï÷ÊNs ÛÛ{m@_ tê—íð½Ý@ßô–¶Ïcä7lŸÇ"ŽŽE›1Ët~\¸ÑÈðX«‘ɱm–Ž3Ç;fäŒôÂú}Ÿúeû|Ÿl‰ì³N5edò>Èäý5FžïÃØß_ ômFnïÓØ9†¼ßmÆûþA Ÿ22yÿ<Ð/úqâ'’a²[.5²:ÞhÆr¼è?û3|ÌÈðøY#Ÿãç ý’%Ûíd{œW|à:튳Ý~@þÂvûA#Ði^àøÿÁn£—ö�øá¹þÒ Ûɧ€NrPö dýE£—àã'òŒÜNä½èÅ@/ò´å@o:É“mæÄ:#ç07èú6 wÙž 9‹íðĨsØÈöĨsèg€~è[N@lù0ÉèâÃL££I>ì/–Þ>,zÐI¿7>ÜkéØòá1#Ÿ“I¦ÎÉBWVÏ(•}l3ã=IþÂ>rrÐwdur?л-m''äÆóÔÉKÇ“4¿° 4Äèñ#+ûÈG‡|” õÉ®xNÿ(ߥ¼¬ÒÙ|3®`\µ~>Ûø¨è`u cÿhÐI[>ÚtÈy>ê†gù|D2áòÅ[ޱ‡qQÀñ¡'Àȼ‡b¯÷Wd’€N9�ÛXø{O Ð[Nòá5HͼÖè!Ýñ¤çÐÏš±|œgžý˜l’óð©_ÎK?nz+ÐIΜ«L²å˜ù1ù籟ÐG3ìzÐK ŸP¿”×)›¢{xîŠúúd¯‘ÿ' ¯ONxà¹àÓh3ÞOc€Nrfûü4è™@‡\åS²7¶áOw~ºÇèúÓ½@‡|õS²%ž >=ktý)Ù û©£ÓSõf,§M›§6ZÚO×Ñ=#5uí6²:uÂÈäT¥møÔ) _5ôÏŒ|> zÐI&toI©?ÓôõY›ió3ˆ9ŸAÌùl´¹èP¿ÓÒ±î³c®.•Íѽ-¯¯Ó¯ž†|õt$ÐI¿ìƒ§}@φúy¦ßÓ0†|ìt9Ð)æÓÝ+˹g¥ùéöÏ@ûg~ÞÐ?ÂúR¶ëÓÎçÿ|Nó&ëýsX›|^kxû¼èk€¾èë€N6Éþõ9Ì)gˆŽ]g¢ÜÎÄ�½ØÒ~qfƒ¥ýâ ù>û™. SœaùÙÛÿHGl·ˆ:Åy¶í?¬³ôœò² ö—?týóãüóÄÒ/È6ؾˆ�:écìm–ŽŸ_ò|ýE;л-íƒ_ð|ýGê“ó«?:ŠεþH±‘÷aþHëDžÇÿ¸èxÿ#圃%ùsŒ=ô$ S|Žd˜ôÎñö,µÏ{5g7Ý9cc˜úâý„?‘_pìýÙï-ü©Óm'pªåÜ pÿStØKùµÃöü'’Û½ÐêÙÛ9²#¶Ÿs¡†çsa@'ò<rŽÆÈ6vŽìœíð\=Ð!?<Göƶwn7Ð!öž#d{;wè-m?&™±ý9èÄÛÌŸ‰¶«?×øa;ù3ñöôçÝ@'~Ø6þLü°ýüù,Љ¶‡óÄÛÌù '¹2 T¾“dôrÖÔç[^ηÖMç»\εܳY¯Øû:êŸ7ô¿X†þ˜/þB¶HwA-çÔkó/£þÒϼð—õ@'[¥ùe‰åœ-êv€‡ ÀÃØË½�sÖÞS Tú‰~.À>Ã…¨ß ô6KÛóˆ¥€Ï ÌØ/tóØï³œ4¯¯/-óì—æÙ/ç/!ö~ óÚ—°þ%ä-_VÞ¾$þi~ü™eŵ@¿°Þù²Ú9tÈW¿< ô¨û¢‡˜~/Fq¿*Æø`ý~t}±Í´sdx±èÛÙ.vBmP§ x8<œ·ôüxñªËW–¡�ô KǯHV<ï|E~ʾÿåoìã_5õ/‘¯ql¹´èdŸ<]:궨ÚH€ýÏ¿Âþç_!þ5èp®ñ×$ û¡>äŸm1røk+Ð;̸þJó#Ǻ¿úC¿ ç —a¿ë2å<O]&=rü¼ÜaÆ{yл€N9'Ϲ—Oý~näöu’iÿkš79ÎéÒÿ˲’3 ¿†Xñõz3Þ¯7�½Ýôû5¬¿¦<‡çô¯!þºÚ9u`íÿ5äo_Ÿ‡±ÀÚÿkØSýúª¡_ý^ z„‘ù•H Gâÿ˜÷¯øžôL SžÃ9Õ< ç½è%@§<“s†+å@‡|ãJ-ÐëëÄ+“\!Ýý#ä/^‡^éT?3&[Å0ÙjÃG ÎQ ƒú' /Ò ç9ßÀzó²m>û†øô1LþÎçYß0í|Ó ô³@'Hd˜úJ`ø2Ô¹jž%Ыs5À<{5ÈÔ¿Jñ†ãÏUò‘.†#€ tß~†iŒ/1LöðÏ g=èd/3Lk÷Vz·—1Lú=Æp ÔizÐ×½è$Û&Ùžd˜tÚãÂ#¿à|õo$‡1 “8ý[Ô‰„:DKf˜xNa˜xð3L1*•á.¨³è{ >É5aÒéþ–ì%‹aòÇ© û¡NÐ3¡>É?Ÿaò¶ço)FÝÁ0Ùp‘ Gr(f˜ä0›á¨ ô(¨O¾õ ÃÔïïÎz!ÐInÝ “~ÿaÒé« S¼=ÈpÐ;N²}a’í0Lyï›l;óê\†‰ß Ó:¥”aòý… “ÜÅðQ¨s è' >Åpwþ²°{ÃÔoÃÔï Ó¸æ1Lq©„áP§èPŸìªœaê·Ú…{‘.j&;Y̰è@§X]ËpÐI_u B “<–2L¼,a˜|¶žáV ·ô»Œa²É†®í^4Æ7Vz´Û¦ Vá½iŽ«d˜|yÃC N8Ô‰€:$«ÿd˜ôò:Ã%@/:ÍA¿g˜ÆÑÍ0ÙL“ ?Í S¿- S¿+Ž„:QP'ê­d˜t×Êp¦úYÅ0én5ÃyP'ê½ê—@R —½ èµ@¯z#Ð[ ¯6 ¯…úë Ùö= “mßÍp'ÐÉÚV6Ð놕Í÷ú”á³@?oèJæ½N1¬äÜë3†)–®e˜dûS†I¶2LrûG†In0LrÛÈ0ɪƒa’Õã “¬¶0\u¡Éj3íPŸò–M ·Á³k¡þ:hgÔÙít�½êoƒ:]@ß tŠ™1¼ê€:ÝPç ôuê†úG@ÎKŸb˜bé6†)–ne¸꜂:gŽúœ×0L²½—á ·h÷1L²ý Ã$ÏG^ô @'Ù>Ê0ÙíÏ&žrᾑêgÃd¿d˜äü+†‰ÿ‡]8ˆìð ÏëéPËÊ6ëh;ÈܲƒÌþ­tèfÿܦ6{Óû¢–•cöìàS?¸ê¯:ñÄðF w@ýNË[ ÛÁÄ[†iÍL~Ϙ~C̹­’dÚ ñ=ƒyV±/7ž­…:õðl#Ð[€Þ tÞ‹î£ô»ÚÜ uö³û~è¤/w-o÷#ýNg8ÏȪßËÛs¶ûµ}ÐI¶ã¦¾bî¶¼üÜîwÈȶßaè‹ò|7·Ci¾Äp4Ð}@/·¼u¢J2Ìb¸Þ´Júåo…’ÿŽ`øÐº³\`˜ÆÉ0͹·2œiykÛ¹ÓåÑóNñóz†‰Ï†A>ý7½èäGÉ “í¥0|è+Ü\Ú@9@Ãá@'Þ'0Lq>ƒáb Óœ8‰aâg*Ã$“\†ÉN¦¹°³õÀüÐW•¼~¯k4í_GüOf˜l “á@ïúWw}T¼œaîPÙaf]o‡‘üÙVÆ�=`?Ôa_s`³‡c‡­zÐÍ9‹M_vâý{`•¡\ãÒû¨9x&øÚÀÃÃÀS†‡g€~ÕÀƒ‚LA¡†ŸA†ŸA™@Ïz‰ágP£áspŒ¡Îf>U•mølöjlúF”ÇÃàm@ßtˆƒ»N~4Žasöm>aê %â˜9”xc_J>Âë…¡…@';d¿J9À�†©}÷ÜÁF60šaÒ5ÇŸa¤ß[¦1r|vêŸ5|:]²<¯§6‡0<Ä<{}´iózsvc_ï7|^ŸéòßGévv¦‘óõ·¯‡9åúN wdκ¾þðvèæ ÑçýÕ¾!j9dú 7gšv¸9C´ÃK€^jô^tŠáa ¯º9G¶Ã7Bvh§úê4z '»b› ‡ñ†ï6: ß}Ñ<Êð~¨s�ží¶¼=U;ü ðCrã9"ü°Ñcø _4tÇyî¸äÊ6pCÐ}@'ßÊ0ÉmÃU@¯:ÍÑlÛ7 y®¹äàîéÙÃ#-oŸÊN>ÂóãpГ€îz¦¥ãððl çxKe¸è@']§3ÜeøNòü†Éöx1¼Gý¬c˜âçÃ)OãÜoÉs¿$?^GŒ qqÎ9‚øçœ™¾¨f=É0õÃ9pé…çåˆpÃ[é‚óÕ;ç«[xMAñ™×m@_t;çöÄ#ç*íÐWÐ;¾ è ·ˆÝ@?tŠO"ÈÝ;<öK8¶Üiêß ô C,º1 è4öb† ^ ô —½èõ@'9Ìa¸è@ßÏ’Íd2|èÇ€~è=@?ô3†N_3óè7‘|x_è¦( GdEß0Ót}ÍLÓAVôE2oýN_Óô@ïúA úa Ã¸n¢€÷ n&}ñþÀͰ'p3ÍS¼IvÎû$‘d«¼ {qÎצÊ&,waÇÔx_è–� b-ïkÝfø¤ïRizÐ#ò¿…øœÁp&гžt{>à ÿ[Š¡~ Ô)zÐ)ŽÍdx=´C¾É¹ë-à›·ì:Øí-4ïp~{ èý–P¿êúa “^ò\Øqong$ɳa'}ËJÓAžÎW­<:ØóHÐóN2áµÏHðå‘õ@oz Ð[¾èNr«b˜|™÷`é[UÞ^«ó¥*ÞSyèçNq÷<o%›üw†iŽxaŠÏï3L::ÎðYS?ŠìöÃ$CÞÃŒŠ‚:Ñ@/:½‡á6õó1Ã4^ÞO£o*YÿÍ0÷ä_ž§èIÞÜGß/òæÓQ4ñ<ë|­È£S]^—¢öw0¬Úˆfø(Ð]ñ0†aåãî½2{t€¡z¦‚y ô< +; ˆc˜ìd/ÃÄó‹ “Oíb˜Êw3L>ò¯ “L^a˜äêžyÙ·‘~ÿ…a’ÕK “ ïg˜bòó “ï¿À0ùþs “¾~Ë0Ùêý +Þzc˜äÆû–·?ë]دͣ£\zߟ)S6÷(ìhÈ£)Îpž]tØ“‰†ý¥èµ@'¹q^Mýó!zÐÍ] {ÌZC§ïÅx¹ô˜õ@ï´´Ž9 t˜CÇô�Îeè›0š~èNóçÃc®úXòGΫé1šNºãü|lÐ)ñzÍùŒG'¿a¸è4vÎ«Ç‚ÜÆÒxy3v›Kï«~k¶}5÷ ì±°5ö<Ð/öÇÂþ¡³|½Ža²O^#Œ‹€:~ ÓÇëú² Ñû*»\œix¶AßfñÖnã`Ңék¾è°þ·è°WIßpÑt’[ }#NñÓ ü\„ú—¡>ìE8ÛOL �zÐa†¾´¢é°Cß\ÑôHæç÷–Uiø‰ùÄ€|b@>1ë{2Î7OxÓôN Ãz3öpb(v±]9ß9 ¦ïâ)ÞÞÆGÆGÆG=è°ß;Þô$ ûžôL ç±¬”-×å?°W3¾êÃ^ }+DÓa¿w<ì'Œß ôý@?�ôn b~.)~@>± ŸXO,È'ä ò‰ùÄ‚|bA>± ŸXO,Ë'Héo ȇ¾Á¡ëw@ýN ozÐwöb÷ö¾b�ý ó£âæ’ƒ†úf†W?.ÂÔ§¯ghzÐaß2.è> 'Îâ`o3Ž÷ƒT?Ka¯/b}ï‹iq`Wôå M»Šƒ½š8°«8ØŒùÄÁþ }ƒæ2:óY öã98÷¹¾äàƒý[úú1h‚eÕgB; w_‡¥çMú „¦:ån|4wðÞ}ÅAÓiÞäy¾ÙàÅyçjÇÿxŠ<WÆÃža<ýòþ}qÁ›SâyŸ9hºe-ƒ½âxð»øÃf¼ñG€Þtš×8v%D³lÕÚ»!Ú´™�çA ÔŸã$´½ è»ÍØö™$plt`swÑN ~8H$^úÑ÷3-ky€á!bx¢¹»k'n�úF ƒÁ&;¡Ød"œa% 12Iª2ãJ‚½ý¤ýn &ËjÜoøLŽ4m&ƒ&ƒ&Ç�âU2Ä+çÎd/ú¨eý(Ú‡9=Æ› ãM†=áäN Ãx“Á“Á“Á×R"ŒRêRÖ>SºYYVS·á3Æ› ~— q85èÙ@Ï8êÀþsj9л Ÿþ0çæ ó©dÓReøô6múá|Áßt8sñÙ‹ÿ,Àp¦ì‡¼(-ÀÐÓª Ÿi{ Ÿi‡ ŸéAÌçNËZ {ïé°çŸÞfÚL{H_tÈñÒÁGÒÁfÒÁfÒÁ&>'”>'4>'ìf>•íß½Ûð™9[ØØCØÌGà `3`3`» ŸW Ÿ‡>'2Ÿjý¿ªÐð9æ—‰‡àY°‡‰G€~èp—`â ¨ÓôSÀ¬#&ž:ÄÞ‰—M;Îq)ÇÉI4oð\3‰äÉquR¦¥ãê$’ ŸeL"ýòZié—c줠ï1}MuŸ zDzî 5ò™\eÆ2îL†¹`ò ·lr2Øäd°ÉÉ£&_5rÈÌ7¼eVYev²?µ¬5†Ï)A¦Í)¡¦Í)a@tÈ3§Dšö§D»u‚þbY÷ÂÜ7¥êÃx§Àx§À:}Ê: Ãx§À9×ȦÀš}j€‘ÃÔb#‡©õ†Ï©],‡ï,ë¾.ÃgŒ7+Ò´™ùsV4Ð!ÎÌ‚»%Yfå½Ëð™v› k·ì|·N°²¿¶|Ãg6¬²š6³ò–lÈ[²Á³AX³gŸ5ôœ|ÃgN‡á3òðöÁà–õç Ÿ¹°o“[kÚÌ{Èm:¬=sÁGrÁfrÛ€ö q`Z¦ásZ‰ásÚFæsŒeýd£ás¬µóÀþóÀòÀþó`‘¾ uÀfòÀò6>óN>ó®>§û™ÏTËú©ßð9½Ó´9ÖÓÁ¦Ãúq:ä±Ó»>uÀf¦ƒÍL‡síÛ×>oçõZp–e=�{>3@&3 g˜>2Ö˜3`žš>2ÖM32žmx›‘oê8nÁ¼åóš.x–e­‹0¼åÃÜß ÏB.—ëô|Èåòaí™6Ÿs_>ä{ù Û™­†·™™·–õ3°½˜ë ÂMý°«‚H ƒ]Ä�âOAÐùŽAðbËz°ú…1tž ýWäWO ŽBcPâIä~gLY†ÏYÌCp³eý¼ÛðYyKa¡y¶°èàã…¥@/æÁÂZ Ã¾qa‹©sÈö¶·à[Ö/ ßÑmÚ¹ã< 1öÉ “;z�†<çÈï€8|ÇES§Ö‰EloÁ?·¬‡`¯c6ø×ìlóìlXÌ?š ë‚Ù ÛÙ ÛÙ ÛÙ冷ٵ¦Nq„á­˜×bÁZÖˆcůŠÁ_Š!ƒ½ƒ½ƒl‹A¶Å ÛâÃ[1ØÛ˜¿æpœ ~ʲ~kϹcÏ…{.ø×\ˆçs!vÍ…5׳™ßZÖÃ`3sa½0Ö sÁçÂÎ\Õ\ˆ9s!æÌí:Äð;a­}'¯Y‚Õs¿†øSûÆ%°¯^ tXƒ”€|J ¶—ø Ø^IÐ!oŸÇkº`¥óßÀÅ<ÈæÁyä„ó`ÿyÄöy΃½÷y°w1¯Óð3öç“Íó¼<ŸsÅ`•Ï?Rox›66Æ2Î5惟·sùc8¯úp°Ÿ¿ Èð¶ ê0¼-ày9øËzô¨á­ü·´Ü´S kRÈÁJ!+…uq)¬SJ!+m3ô»`¿‹sÚàs–µ ö~ïê6ÏÞužÿ½ òÏ»@¶wõ� sÇ]‚…†Ÿ…û™Ÿ+–µüº ì¶ ÖÅe0¿”eöRÊ f–ÁÚ¹ d^Vbèåp.SéÒCÔú³#ÒðSv[Þ ÏBNRñ¡âC9ä$å°çVñ¡¼ÛÐ+`Ï¡‚×’!ƒ,ëñu†ŸJË<[fž­„ü¤2èŸTF ±¢d^ ñ¡²ÇðSy™ù¹É²¶\6ü,‚9zÌÑ‹ÀVÁæ"ç"ˆ‹`_}ÈpÄØEä_¼?°ˆäƾ¶èÐ)®ò¾Á"²gÞ7Xt è=@'[å}ƒExß ŠdÎûÏUÄ'ï]Wí1uªIæýèo¥XÖ“CŒLªA&Õ “jIuÐù¼’Þoxt] >X >X >X ¹G5Ç´•ó?yÑ´S¾S“iê×€ïÔÀÞW ä5‹j(ÎðtÍKïÃ8gʼç_ÓeäVkÞšÃ@‡5KÍ1—¢Ö[žCþ¼æñÅ0/®:ä6‹!f.†uëbÈ“ÞÙj£ ÿµô,ïÏ×’òyD-ÉœÏ)j{€~è¤ ¾cYié{ªuQ@:ùÚ- ÓX"®z#ЉÿÛ&9óÝźÝ@ß tŠ3|O»Žx‹aø<Ð/æ>¾»„úâ»7K¨Í †i¼ü¾Ã’³@¿tjsŠ /¥r¾ßµ´ÍÒwÏ–’mðý«zòE¾YOq»„ánK¿?»Œl œa{• 7løÝÒj¯–áb —ÚXÎ0ù5¿CÚÐôN “mó½Á²O¾7Øpè§€Nòá÷—Sì廋Ë# t’}ÃÔïZn¤¾Ža’¿óÕH1ïÿˆì”ï�ÿˆêò½ÜQ<ä÷éšÈ®·2Lmó¨&’?ß j"ä»CM4F¾SÔtêôXúÝÕ¦S@§ñò}°¦³@'ã{hM ½9ÀôÕ ô0 Gš“L¿Í¦¯æbÓ~3ý?¦¹†ß·mÞ�ô@§qd˜øçy¤™ìŸßn¡~y^h!›ä=ê–r¨C6ÃwÅ[h^àûQ-$ÿ<†In|7¬…ü‚ç‘4^¾Ó¾‚ìï³­È´¼oØ+HïY ÃýÌ4^~ÏhE§¥ïß® ™ÝÁ0Ù ßÍ^AcäwÏë° “ñýÕ‹þaâß»\IãåwWRüç;«+I¶üŽäJòG¾s»’tõ#†É–º&{xÊ…ï&‚áHKß?¿›dÀïiÞM¾Ì÷Þï&ß=Ä0ñÀwï¦uÓ¿1Lü¼JG̲þɹKÈt8㸛dÂïrÞ sëÝ'\zH“zö„y¶ò¢VòS¶“VXc¶æ=è…@'Ýq~ÒJüó¼Ù ÷pZÉ~‚èï™YÖÞ=À¬GœëÎ<­‚õÈ*ÈcWÁ:qU·©ìåÅ0Óæ*Øï]U Ï–öaVA>³ªÞŒeä««Z€÷‘Vµ¹ô¥“Û€¸C² ÖÈ«xŸ!DÙî‹°O²òÛÕß®†üvu$Ð!XÍûœ!Û-kœw¬†uÓê6xÎtV{¹ÙoÕ³›­†l5¬}VÃÚg5Ü[­|ß»c¹tJS‚G¿‡ì*€þVœe½ä3}ÝÓõ‰7ÎÓîµö=€yõ=í@‡õõ=@ïb)ÿx ÎYî>k\ ØÞšP Ã>üš!@‡=ç5°ï´&Šû=®Â}”éw èeÍKûÑÚµ@9¬éú) Cœ¿—xc¿»7ÒÒk{É/8·¿WùEÀ; «Xð® ß§ü=à=†•Ýsá+}ðýꫜ!ànSc8Á°š›ÜoËØjN2¬|'à#†Ÿ=.|?ñð1Ãt·ù” ÿDÅÒ�þ.ÁO©ýÓ.ü€Êç>wáT¾pÆ…×)½üÁ…¦âhÀ.ü ²Ï€?ºðz%ú&Á´wÀ6ù eKaX#ࢠ?¤ôðW†UL¸Â°ÊþÆðeVµ‚™©`Ž ¿Tú äXñËÃî÷_ Þ ì'óð j~äõÂeÃ|_hƒâ!sï_)žù.î¯T¼ äûWʆ#Vc éÂ+F1¬dÈyòÃjÞ ŒfXÙ| ßñþ5ñ̹ñ¯•½ò}ï_+:߃PðFÅo çÌ Ýo£:°’[ çÏ•òüþeÿüŽØoÊÌùóo”Í8ß’ øŒû K‚!ž³V~ȹô#Ý æãQe¿·3\è~gÑIn +› èf˜ìçà ½]É?€çÓv²«W&{à|©]é%€s°v5/pÖÞô “íñÜÝ®ÆØ‹ó½ve'E ÃYX;ͬëön „ú‡ ù¿cØ®Æè­ÅÚOìšïx·SÂy×&ŠEl{›H?¼¶ÝD:a[ÚÔi|­Ø&7Ñš‹×‰›ö�ý€‰›šX±é°‰›Žš¸±é„‰›N™X±é¬‰›.šX±éª‰›L¬ØjbÅf%ƒ@~—|3Ù¿7±YÙ~ ¿ó¾YÙu ç®›•ÍÆ3¬ÆÈùêfew¼NÜœï~?ØÉ79GݬbT ¯=7“ŽbXÅ„@·7“¯ñzv³Šç72L>ÅkáÍ*6²^6“ïÌbXÙO ç½›·™¸·y·‰‡›÷šø¶ù€‰{›šx¸ù°‰“›•üyº™â ß`³j/ß?ÚLq#‡a%ÿ@ÎÃ7S|àµícJþ¦8Àëßdž¸ß™v`’?çü‘üy~yŒäÏyàc$žƒ#ùóüøÉŸó‡ÇÈ¿c˜|ð*Ã4G|Ͱ’À%†)&É0ùãy†×˜8ÿØZÿ[ïÎ ýn²¬÷h e1ö*ƒ<í±n ÃÔc°Çþä?4÷²®;2-í_°7ÛAk:¶Ÿš‹Ù~:(å¸ÝQ ôz o„g)çá˜ßÑiéõTù,Û|…cuÅ6ö‘Š-«;h,«´ô{(olÃSÞ{ý½]Ëz¿ÅÈíqȵï4rxöy>ò|òÉÇ!Ÿ|ä¼Î¶À‡-á@§8ƶ´Ö&[Ê-«o!Ùr.´…òyއ[(§âx¸öú¶lz;Ð;€Þ ôCÐ&Å<¶ÿ'ˆŽ™OdZ:–>AsÇí'ò€NvÂ~ô­Y8þ?Q õ[ÌxŸ ¼‘ó„'öÝ=qÂÕ]?å·ÀZ¯ì°îqu½¦N8÷ì„ýØ'̼ö$ÜKy2 è>3ö'“€î7ò2êä½è4^ŽOÖ}ƒ‘ç“GŒÜ¶R]Α¶†~ùÛ[I6üÞÐÖH “o±Ïn%þÙg·&Ýô #ç­™–ö‘­yÐ~¹¥÷6·VÆÉ0ëf†aÿsk <KëŽ[wöa¶’~9>l%ßáø°•öa8÷ÛJq”篧HV+ž"Ÿâ<ð)êŸçÁ§’LûOùžôL ·}-Ð×|Šçß§HwœOn#}q¼ÚF¶ÁñjÉ“çñm…@/:­¡8¦mkúZ “ÿr¬ÛÖôN ïú^ SìâÜrùïßn£}~Ÿ}[¥÷r·ÿœën/W?<on¯z-Ð[¾èÄ?Ï¿ÛInœ'o'à9z;ñÉ9óvâs‰í´ÄûcÛ�|™÷¢·Ÿ‚:4±ïl'›a¿ØN6Ã~äl§2}é‹ýb=Ç~±#è‘@'ÛdÙAëö…´æ`ßÙ±bɃ÷$wd½èå@‡Ø¾£ê´�½è$gŽ-;Ú€N2瘶ƒì–cΊ9œî ùsµƒl†×#;�½è~èG€~ è'€ÞãÒû©õÆ'[.ýi%/>? w<ž†sá§á\øiˆùOöÇž.z9Ðáìéi¾‡ÐïmËý{+?;= s÷Ó#=}Õл,=wwÁƒ.˜ë»xÈ“€î:‘çÁ.؇ìâ;fLcT}ôûIJN•ž»ÞºÎ�g~ÙП±L_Ï�=è°¶z†äƹÁ3µ@¯:Øä3뀾èäû<ß=Óô “²Ÿ>³è¤Îž!ûäüá™KçEÏ\6ô–‘ÛÎ�—Þïœe†û0;áþÆNسÚÙtسÚÙeä³s¯éwg·ÛW?•ÓŸî6íï‚}Î]‘¦]°Ï¹+èpçgW&ÐáÌwì]ï‚üpØÌ®RÃç®r W½Õð¿k ÐiìOvÑØUö’/ .HËz¦[÷k=sQ·o=cö-GþLߥ۷vÆè~­%ZGÖÎrݯµs£¥ÿ¶ËN“£Z;)æðßàØI1ÇÝw²vQ?üù]DãoÅ;rãïZï¢XÊßZßÕt’ÿ]Œ]–þ{»¨/þnü.šƒ"&S6¦Ö¨½Ì½;k·9·v›»pÖîN oú!3öÝG€nî<XÏ™q=;ÄÒß½–æ)þ>ü³@ϰ<ß±žÍ4c|6èùFÏB;%P¿ê”Còwþ†ü³$7þ›#Ï’ó÷äŸ#žïc˜tq/ÃC€tâóÎzÐ)ÞÏ0éî' ·} Ð×Á³ë¾èC~Ê0Ù°ûýë·d«ü ùß’|Ö1L}ýŒašC×3LúýÃ~Í0å$l?{"Œ¬öŽ¢Îz!Ћ^ôZ “&1Üféoƒï!ÛËf˜øÉa˜øáoÚ¬™aòY÷ÜzžúYÁ0ÍS+&na˜ä<’aê—¿Ïÿ<ÉÿFÀódçü½ôç)fò·ÐŸß ô=@ß ô@ï¶ô·ÓŸ?tò4†É7ùøÏ“oò7ðŸ§q¹ûÀîg5ø›ü/}òߤ{!ê„}ÐÃôH Sˆc˜ôËß“ü%šaŠ?ß^ Ûáøöé‹mæË†Ïú?EÝýÙc.Ã$·i S®•Ç0Ù³»Wfí¥Ø˜Ï0µ3•asnkí%ý–0L<Ïc˜xæïÆï%û/f˜x¨b¸ÓòΣ­½¤÷ “¾J&}-g˜øld¸ÇòΦ­½d“õ.ü"ÉœÿFÀ‹Äób†Iöµ gZÞ]ëE²÷ †©ÏJ†Ë-ï;QÖ‹dãe ÓX&3LcQ9ê@å?ÎwY-¦SLæ¸ý"ÙÇçaŽÛgú¾� “-õfxÐá>é„íaù>ωûJ€^j鸽ÆÂóÚ¾* “<8þï#ùñ|±¯êϲŸî#ŒdxÐ÷r-Ž9ûHü7¼ö‘M²½Du} “^Ø_2k^ë%âŸ}ö%âŸ}ö%²ÿ †i®ŸÈ𣗗(å˜ðÙûÝ~ÏMûI¶<í‡9q?Ù̆#ÿuïUqõßÍnÈ_ @”£ `ø#  @´°… ¡b›VÄ(« B”(¢¦-*Z¬Q£Ò–¶´/mi¥-µ©M•¶´¥–Vl©ýs÷ìÎgvîî Úïû>?žçmæýxîÜ9çÌ™Ù;@“ 49€3WòØe.;È>"ùÀAo†´ ¦ú)¼èg^}²|dŽ8èWº8¸ðJÀ딎ÖÞ�8çÕ«¶åÙk™Ž]?âöb“¯°Lâ¤x.à<~±çWx̒׽¶'öüŠZŸZ¯pÞ(6üJ#àM€œãžäx¯p-BòºW:¬°=¿Âãû…ã›Ø¼\1lós”Ãôú"ý%Šßoª5£õÍ6 oüàlob?¯²\Å6^Uk|ëU°™WÁf^e{½¿ öð*ØÃ«<É'_-rÑW«�Îó©ØÆ«­€ï‡>�ÍAÀÁø9‹ý¼Ê²•¹ûUŽuYÒ>§p[È®ÚXÎrßMËLbE[:ÐðØ/“v&ÐäÀ³ì_2OµZ¡ïFZm,™OÛJ/œíPâC›ß Ç™¶ À+g;”\¢e8FÚ,7‰Km,+™CÛXVÓ¥}pžd.në°B5(«­ø: ø)ÀÙ®$h;øY…¿Æ²•{g^K<ð<Àóg“;7_c™H¬~m9à%Ï×j�¯¼AÉùµFÀ›�o¶Âñÿ56É»^k·Bgì×XVÒfùì6˧)Ø~ýeœ´Y¿2/¼®êŠÖë•€¯œcÎ$i³ïH®ûz�pæQòá×ë€fŸÒéëªöe½~�ð6ÀÛ•N_?¤t÷úa á8&¹ÖëGžcšØùë'àÙ+|·Îël’_½Î~¬Ç¯Ï™+m¶yÒfÛX í,+œw½‘ xà»${ƒmf‘´Y®’G½Ác¿IÚ3/œu$¹Ü¬ÉåÞ`]ÈÚá ¶Y;¼Ñxà,çۥͲ]'m–çÒfVK›×Ųv{ƒíçAi³Éú«å³YÚi€§Î2Ù"m¶‹‡¤]¬ôÕÎ%olg;”uV;ÛR¡´Ù_dÍÕÎv%¹k;ó+ößÎqFl¾}¿Òo{›Òo{»Òoû¥ßv¶Y·³ÍHÎÓÞøiÀYÁ»i‚Ç+Enoò|+óé›iJ†oºøÍf+¼Ž~“õ"ëâ7Y²þ}“Ç ëâ7ù½²~‹sYG¼Å1_ÖÑo±½‘¼zÑ`2T}Õz‹å)sî[rNÏn×Þ ð·ùäoû�g¾$g~;'ˆ÷úM‡ê]oûUŸo³J>óvàk¯<�8Œómžs%ÿy[j€}îͳþ®ÎàYq…ªŸ8U¯³âJ�Ÿ©øŠ+|9Ð3/{ãj�gYIîÇö&9Uœª£ZöJb'q-€·ÎqLòá8¶=™SâØ¿dmw ðÓ€óœ%ù‰}“äöL!< pŽK’«x9þHNâåyXò¯œáéÃ>yNÉÓ[£äã­U¼{ë�9x›�?�ô<¿Kü÷²=KÎéí°ÂuEo'à'•ܼ,ñ#/ËAòR/ä¢^–‰ø£Ï§x÷ñ¸$ò©½f˾*„³|$&Û÷@INâc™ÈÜgßÂÙ~äþ8_ôSô~ YÏV�s«}¯S¨ö ©cøš­p¬³ïi’ùÔ·_ÉÓw xgyŠ]Ùç}B¸ä9}v“*ýÆ«zxðLŸÈ'>ðÀùýäé¿Ì‹KiTýô-Pô}™FôÞ·pµÏbõ©úì[ ôU€ ­ÎWX}ù½²éÛ ÏîWüöå8/vÕ·=H“Îg›j`Ìgalg¡¨3Øî$xÔì{$.e¤žôÐÎR:ÍȶÂ~Q4lKR›ÊPûòVø‘}7Aoœçk±‡ X_d°É:"ƒýHâC†ÚŸµ2Î)¹ÁÖEÌ—øÚEA™§“ÏyÔ9ë"Я}ï€è÷"ˆç”¬.ªšzhC®{ëTrÝ‹M‡â×¾@òç~êÜŽeßGÂ!Î÷ó[á<¹Û’ÔUúñ|'9a¿F%+û>‚,ióx$žôc™Küì×n…סýXþ²ÞìþدSÉÓ¾³@ì¶Ë\b—ݵØv¶²Û HvÞcJÎö"Ïþ0oö(û×�Þ�x#à°?ÒŸã‰Ìãýe{éÛ~ u‰Ì1J™àï™eªÿLØ‹Éô€gYþ2ŸÚ÷Ðx. [´ïw ½÷¤êÇ^6 ý�ðÁé€g�ž 8ûšø”}—Äû.™Sì» HîÆŸUc�s߀ZÅË�˜û@¾dßS@4þ$/î#سpèOXa¶ï áJ/öB3Pík[Ó¡¥øXh…ml ÄÞì›âïÕ™k h* ÎÓ$?(6v!ŸmU¼ < Æ<âÒÀƒ€C\Øø!%+û΂~èA›e%±n ËJ|v ÏûRÓ°ï5ú‹Ó/ó<"¾|ñZ ©<�8ëQêù7[aÿ½¸UÉöâýJ†3âË3K/f¥Æx±ìcòý?È©.†ùÅn‹ÞY¥!<Ëx"àêlƒ••x:ÐgB;Kñ˜Å6,wgAÈ‚õQV›’CÇ4Yg±^$Éb]È^FÛª¬÷³x~‘ÜÆ.EImÁ¾Bôhßï ó }¿ƒÄÕKò”^.ÉW2·ïk¸jß× þkßÅ@ñó­`þ)²½¤QÉä¿øË%Ç¿ö] !üà§?8ëHâ$߉à‘1\J2óÈØ.eÿ’øÒ ¡Xá‘Ú¦}¯Ñôí:ã/¸:+b]zNé×™à—eª±]–8Ô/+‚gYn/cYýEÚ»>‘v£âë²6 9Äû^©ÇÃl°·luvÂʆ\1»�ðBÀ‹�籉Ÿf—*fó<"19[ö_úަ¡ÔÃöCŸïÙ²Û•²i2~©­=Ït†é=ÏÀY£gà¬Ñ3gÂýxžQ¹¢§Õ§è[áÜNkFPn|æ>_½ Î'xZ×À³kƒý÷Ûšç-µs|@ñbßC*±(æxà=x·ï•œ$žå ¾fߢ? 8Ç^™#ìûCEGöý¡!öÉWí{B¥¶ϲ’¼Å¶iÛ&%±±ëSòœn°Ïkß*õŠn̯Ü[mßÚ.í:õ.ûnÐïK›Ç¿UÚ<fÙ«å;üBõ%ûÎPÙÏíÖAڤͺÞ+m¿Ô‹ºQxËà)ióø[¤x:à€gξy@Ú,¿ƒÒæX'wµ'°Èy†„B+\7K`‘:Oû‹Ôy8J¼M`ß‘8œP¸ß ¯Ø®ä.ò^oÊ!a à<WJÌO`½|NÚ<WÊ~bëH掄ZÀyìRóO€ýÄŽ-²æMh¼Ù Ï |¿BhÏѾ¿õqò…'ÈLm[œu'¶Àú’›pp¶=‰' 0·Ú¤‚'BÞ˜˜¦úI„œ!rÈÄLÀ9¾IœO„½§D·³¤]�ô…€^ x9à¬ñ‘D8Û“È2ßäo‡Ÿm|à-Ð'ì5'¶Îö/s±}×­¬}ì»nÅßí»nOb9‰ï'±Üd¿# Îc$1¿²æJ*|â+)�4r£ßÛV°&c ~LÉ?©Sé( ÖI§/I§?8¬ë“}ªŸäDE“ köä4ÀYf’'ÞurÐäž8ä-ÉÅ€ó¼&¹D2äØö½º’Ø÷ê†ðè§ð&À›¡ÿ}€œe(¶dß·+6“Ìr“|5YÙNÃ’»¦°¬$7Ka‘zB Û¼¬SŠ�¯�¼JôKó`ü¥ß˜§RX×2G¤üà<~Ùß·ï畘“Êc“œ6•õ(9L*ËRjéö½½!œõ%uõTž$®Ú÷óJ¼Mm¼ p–³ì_¤²ýHÚûá]Ì×ϤÝNÞ‘6ÏS¿•6ϳ'¥Í¼K®•zpæý÷ÒfìCi³ÍŸ‘öiÀÙÎÿ.möÙ³ÁvwÖÃÒ΢?/J;p–O»´Ù†ß”6ëô;Òæù刴Ë¿Ý9vI®Þ}¹ÒKw–ƒÄÉî4߯ÉÞSw–ƒØXw¨;±šC4=2­°/ðÝÁq2õ`݉ýÛ÷K.Äw‡iÀ§zðÜ${d=XײGfß/,{4=X×rî®ëZì¤ë:4ž}Н-@³ð€Ã~evÀÃ8ùíOëØ${¿+ˆ÷„¹©'Û¶äQ=!Fõd=JnÖ“õ(ñ¤gÐç*öÌS²êY¤dÞ“ý4KÚ<¿‹õ„ù¥'ó(9yÏÐ?óEëßþ#*TãOƒñÛmá= Ö­ià§i°ŽHË�œù’|ƾ[Yrƒ´\ÀYör®À¾s9„�ç ÒØ×ä¼AÚqÀÙ×$?Ië�œcެaÓNÎ>(yKÚi…÷b~e]Ü+Q§ó(ñ³ÇO™s{e�g3zeΘÄÒ^¹ðlà,9OÒ‹y—ùºW!àŸ{•ÀØØöd®ïÅ>.ó{¯2à¥<HߟtõÞ^°WØ‹mFl¬W3àœ{ˆ¿÷‚õZ/¶%±Ã^Ç€ß〟�œå/gÛz±ü¥~Û‹å/9goŸÒEoΣäl­}?µœ¥ì]8×ôÖJ›cÅmÒ®¼úg[’øÜ» pŽ ”6ÄóÞ86ŽÙ2§ôæ¸'9|ïsŠÆž¶¥ÿ>>ÀÙ~dO¼ËRö¬ûdM<› 8Û†œSíö!q©O!ÐÁ³Å€Ã9>~+¼çÞg9ÐTÀ³•€s<‘uP–óפÍr~^Ú,ç§¥]Ï6@Ÿ€³œefßþ¨´@Ÿ<ï<&í6 á5¯œèÃóØsÒ> ϲíÉ\ÐÎXöa=’ï ¸(Ïû]Ø/ûev[òAP'Ä~*kíAù€�=ËUâê ’¿WÎñ"?õÊ™ÛA¤o…´ÉO½’“ "ye;ˆtä•õï Ò‘÷ai“^¼"“Aä ^‘àjÀ€“¾¼r.ˆïŽô¿7e]ÎñGrøË‰/Ÿäö—_>YË_>h ì?Á6ÇÉK/'>¼?—6ù™½§ËmÏŸ¤ÍcøHÚdÞJ»™Ú²¦¾œþzÅ®.'{ð>#m²ï³Ò&ð¾,mÒ»7øý+ë ’U¨¾tÛ­ÌqW¬œdÊŸùžÇÐÚáŠzÀy<’G]Ay‚÷5i·�N1ÐÛ.mÛ!iæ•\ë Ê ¼oIûàG}Í'5g¾ÿÑ'ræ;}’óð>™ÇsÈw|“ùÎÇ0Ncó‰^rZ~%ÅŸ¬®$;ôIܾ²p²CŸ¬ ®,šJ%Û+I§>Ñõ•ÕAš‹stß¹’ç´Å®„9âJ¦ß¹² ðÃ@Ͼ,¾ÃwS†|çÊãÊwøžÊïð=•!ßá{*C¾så)å;|OeÈwø>Êð’5ä#W¥žxºò¾³2ä;Wæý±´9Ÿ”uÍUÌ»èñ*Ö³è‹ï— ùÈUÊGø®Éð]“!á;%C>2˜æŸä„ƒÓ”¿ ÎPþ28KùËàlå/ƒs•¿ð]“!†=ôÁ'ÅþÃÚ|ðIÀÏ*ûç{C~Á!#ä |Ïcˆ†ïy ã©ÊæùnÇ/ð}Ž!ÉÍšå¹­Ê/rÛ•_äRvž{DùEîqÀ;”_äv*Î=©h†”/ ©Qö?¤ð:ÀÙ%gÒlÏ—p›Ç,{X|g¢Or6¾'Ñ'9ÏCÊ~†@ìr,Øÿ%…ºO …Ü›Û!; ¹÷Ð å#p—¢Åw)†ü…ïR ù ߥî'Wù ߟò¾?1ä/C •_ -R~1t&ी—)Z®øå-ÿÐü2Œù’>ßoò ¾»0ä|waÈ/ø¾Â_ +T~1¬Dù°Rå ÃÊ•/ ó+_V¡|aØZ%ÿ¼D5¶<؃ã»CvÈw †ìï Ùa^²¥¼be‡y¥€—);Ì+üŒ²1¾0dcyçÎ)r(>ó]!šájÌÃ3Õ˜‡ç)ž¯ütøÀ ”o/R~7¼DùæðRå›ÃË€¦hü çô=$Ï©Ê×F¤Ç|Y¥nÛ#H![åvÈ&G^¢l{ÄLÀK•m(¼\Ùù?ô¹\ÙùˆJeç#Ö(;¹ßo²çu€×Þ ì|D£²ó«3Õ¼p5äWQv~u¡²ó«‹”_]¢ìüê2eçWûUü¿ºú¬Vöu²¾'1dÿ|ObÈþùnÄý_Ý¢ô•Ÿ£ì<?WÙy~¡²óü"e·ù3•çƒ=çû•óý‰!ȯ�šÓʶóÏ({Î? ø9…LTñvdšŠ·#3T¼™©âíÈ,%¾c1dó|ÇbÈF‚ýó}‹aš"À‹•mœ©l~d¹ò‹‘~ éTq~äÅï¨De£Ò”ÌGeyô¤î£x½&ö<Š×õä×W,ˆ óWv[ÖÑ£€CN;ªp^ɼ?ª ðf+œ'Ú}²}ÉÞÐ(Îåd=>Š×hRcŹ„œåÅò“ÚΨ#€œ×Ô…ÒæÜCö¶Fó:Têx£9‡—µÛhÎá¥v7š19ë>š×ôG¥] 8ËA|p4ó+¿}Í<J­x4ó"ë÷Ñ¡Ÿ6x–Ç)ûŒ£yœr¦}t‡^{Žf?Þ%m®<lñ©>Çð|-¾ißS)ýÛ÷T–H›×•²g7†×\²O7†×\ò[Ñ1å7cxý%¿å´ï \&m®K,•öYÀ¹.±<ض璉8y ë\bì5\ËZ!í  Éš,ÀÛ•î®á¼QÖÝ×pÞ(uékø½/ÛvéMöFíû.%Mœßõ ió»dul6аŽäjcy ²ÿ;öà‡g=~KÚ\ƒ’øXóai³ Å7Dz ¿lÛeAÑã8ŸÒÝ8¨Ãã±É÷¸ÀY§b'ãÆ(}+ ŽíªäõØç[Ʊ=‹?Žc{–Ú鸀^7ãÚ‹øõ8®½ˆ/«œmVâù8ö}YOkú}@Óbÿ ¶Ù$æcyJ Çr¢˜6˜Çõíq°Wk·CýÀ>]A†Â XnR´ï•ßïØw€†pæWj\̯ì_ÔÎz”ß‹tZázZÛžøKët–´Ï�ÍYÀÙ>å·Bö6¶Ôdz®å7#ãϲµÖñ¬w±ÿñ¬wÙ Ï|Ik<Û€Ô®Ç3?dz=HŒÏ>.õÏñ…V8ÞŽ‡Ú¾}Ÿ©œ%Ͼ/ûVãË�纮īñ~+¼Ï5¾è×� Û›ÄçñloŸÇ³ü ¥Íò—ºÙx¶UÉ9ÇóÜ!gzÇ·€|8®ŠÍØw­†ðv+üÛÏñlW2?Úw°†hŽ€Ì~<¨ÓÁW0_VŸÀý‰M�›�ë  ¬É&ÀY¦ •€¯öm'°|ä·<X>âj�¯œåógi³|~"íf+¼×6ýNæ‹ ,·íÒnU¼Û÷´Š}N8 ä9e({p¸?± ßäÛØ/¤þ?ýâoÒæyJrª g Ï³ ¿Ö§x·ï‡¯MS<^›x&à,çH›å)¹}‡¬Ì³ö²2ÿÚwÈŠÝÚwÈŠ}ÚwȆp–­Øª}‡¬Øóµ@ß4,gÙ¹–å,û˜×¶β•}–k� Ûj–´Û• ù]’gÂ^|!÷ù+is PæÙBžsÞ•ö! 9 8ëî—Ò> }‡÷v(9v*~ O~Jñ^xZɧ𜢷·|„f"ë÷ÒNTz±ïϽØ÷çJÿöý¹¡~2�Ï„>³Ô{'æ}ÐpÜ“5ÎDŽ{²ö™X8ç½â;9ÖÉžËÄRÀË�çXwZÚ~+|öl"ç8²6´ïð•ßWÚwõÊÞ±}?¯ìóN„} û~Þκ–¸=‘ýTö'²Þ%ï¸ðv+¼·8‘õ.û,Yïr>jâÀÙO%öNì°ÂzŸúx äyð3 Û³Jþö=ÂB]¢¢¹Žõ+{×±~Eï×±~Eï×~¯Ë„~²�φ>sà½ù@?h˜W™¯+´ÂsåuÅ€³®åÜÚuåJ¿×-W2±ïAa ¼« ðµðÞj[-Ð× ÿ•{ë]r†ëg]KnpÝ~ÏAxoôßø!x×aÏ1 ?4lÿ¯ë°Â¿]½®pÖu™´Ï©ñLò©÷NJTýOJ<M½kRºϤ, ýÚ÷D‡ú‡sŒöÝÐ!úÀ áÙ"è&З óá—6ë]lrcb““–CÿÐØÃ$Èg&UÁ{@_4lò›IÍVØÞì»­å›9“X×_”6ëZö—'±ÿÊštÄíI¬SY{NbÊo®'±Nå7דØ«¥ ó¯}öú`Ûv ‘yQ¢â±(UñRñ¹(]ñU”¡x/Êú aÊÜ]ÄñYÎüå+ùQò/ýBŸE€Cÿ%0†2 ‡¼´ô[Äú•sÂE¬_ùtQ%È|¼h-ô ùCQ�ú¯1Ô}аË~}çò›÷¢fèsôÓãÙ4 ˆ Emð®vèçШ3AÁ¾ß\j•EVx¯¼¨ôÅó…äöè!ÙÂ|QtÞ{ðsê½v–qN{› qc2Û›ì¿Of{“ºèäL%ŸÉÙªÿÉ9ÐO.àL·÷}аIÎlßí¾SÚ3_®d2™mIjn“+•L&¯1TÁ»Ö^ ï ÀØê€¾hØ®ä|àd¶+9hß;’É>è¿úi|?ôy�ÞÛô0§Læ9EÎcLfû‘úÒd¶© OîœmCÎfLfø´Ù‚wx—Ö2æ)¦€=Lø3âψ?S þLø3…m`¿´Ù¤^4…ãŒÔy¦°Þ¥v4¥pÖµÔ‘¦ð<rPÚ~+\ó™Âú•šÏ”*àbÅ”�Œ ÖS`­1ÖS`­1ÖSš­pg ëWlo Ë^ìm èwÊ~èç�à¡Ï6xïa ?47¤þ<…õ.ué)'�g½¿-mŽ2ÇM9«db—Td S!oœ zŸšªÞ;5Mm*ä‡S³€†õ.5´©¬_ùMÇTöq©§M-œõ+ë⩬S±É©å0NȦB0r€©L…`*èz*ä�SY×RÇ›Ê}ȹߩõ€³Þeý>ô;•õ{LÚ­J×Sa^˜ óÂT˜¦Â¼0µÆ óÂT˜¦ò{$¶L…uýÔNèó$<{ ðÓÐÏõ.yˆ¾Ø§hŠYïrž¹˜õÞ)mÖûï¤Íþ.ë©bòeÔÁŠI×Ù£,f_–ßw³/K«¸že_–µX1å ž<iû©],mÖõ­–•KvcŸ‹¶g]H­¦˜u!5ÒâVÀ~ð#€¼p–§ÔK‹Yž²—T|pö#9#Q|Náö'¹%¿šÆ²•úê´DÀSg?’½¶iì;2—Mã8)õ“iy€çNyšGÖÓHæ©•M#ùyüÒ.|&ी—ÎòM«Tò™9Ø4ÈÁ¦A\°Â¶4 r°iƒMƒlù—GòêiW=’WO#ýzd¯gé×#yõ´ý€�¼ÆpÞupˆŸÓŽÂØN�}Ðí{$oŸFöà‘|~ÙƒGö’¦‘=xd/fÙƒGréé¤w¼w:éÝ#óïôTÀɧ<²=æMäuÓ³¨-¹Ítš7=2¿Oσ~Æ@?…Ô–¹r:ûÍ>isŽ$5«é¥€—ÎqU~ï3}MΑNçX*ûPÓY_RûÎñ0GÚMðl3Ðð{äw@Ó[�‡Ø8bãtˆÓ!6NoW:š±q:ÄÆé%ÎLçœGâÌtŽ[bÃ3Ø%ÎÌHT1jÇ4©íÌH<pöM©çÌÈV2œÁ2zÎŒ<Àó'½„jé3X/²ç2ƒõR(mò#äù3j”~g<<ÒnœäìÙ#m~§Ô¸f´¿‡€ßÃÀÉÊ#¿œqð€wÀ»N*{žsÊŒÓJ3Î�~VéeÆ9¥»¨E”@.QÂö/k½¶™CKHæžZiç(Û.áx(uÚ–ù%Òμ@É¡¤TÍM%ßäxI¥Šu%Çd¯³„â˜GöFK€³Ü¤VPÂr“oI‡š§JNÂ{Y>k¥Í2?ºÞ§æ¸ëYâk×§M¶êçú5þëy•ß&\_¢Þ{})ôÉ<VJ›â¨Gj×We5”ìÆ>Ko såõì›2—]�pö)Ù¹þаÉ|w=Û†Ì_ןüœÂy‹Î#çdnHTøç¹OùnùçIž¡o¾Sá³8ÖÉùœY…*NÎ*Sã™åWýÏb=ʺoV³Ï,ŽERO›Å¼K=mó.sè,æ]ê~³8.IÝoÇ¥Ri·[á=ÇYìk²G?‹çš™Ò>¢ô;‹ã•ìoÎâxU"m–­èkËAö7g±dsËAt=‹mLìaË\ꨳØÞ¤Ž:‹ýQêä³Xþ’óÏf½ËÐl–Ù_¥8Û›ä³ •ÜfSßÙ+™Íc‘ÚõlæCâÏl–Øíìje·³k”®gsœX1òÙ €sÌ“ø0›õ)1d6¬—g· ëNÎÌf}É™Ùm€³¾$¾Í>øaÀY_K›u$¿=ŸÍ:’³³YG2ÏfIÌŸÝ 4'?¥èmÓ‘³X¥,ÿVi§ª1—rõli†’[i–’O)¬©Ks�ÏU2)ÍSr+-�úB )¼XÉ¡t¦’Ci™’Ci¹â±Ô|-Wü–V�M%àk€¾äPr¨‡1³=H^TÊö õÒfúó‚´÷Á˜[€¦höÍà« øj‡1C-¥ôŒÿà'aü§aü÷¤–XzN½×þIàs|€'ªñÌIS㙓®Æ3'C½wN¦Ïœ, œdNШqÎ)RãœÃº¿žS¢ré9¬wÉæ” ë]Î$Ìñ½Ää9¤wÔ«çT�Îsk@Ú¤wûR_nó|T#íj5×ÏáœGÖÔsêU>0§ðF•Ìišf á5BHμF¹rù“Gö¼æÞ=»æÞ=’›Í94‡æÐÎdÏnÎ  éü$à§�?­ú™Kò í Ïeß‘}á¹ì;¿–6ûN‡´«€f-ÐTM�hj¦hê¦hoúf Ùx‹²“¹l_¿‘6ûÝ{Òf¿“zÂ\ŽmRO˜ çëærL–Ë\öA©«Ì=øQÀ~ðNÀO~ ðÓ0æ3@ÃþûSi³ÿJ½ÅNäÌØ<ößoK; ðtÀÙgå\Ù¼ Éœã³œ7›Ç¹«ÔÁæ±ÝIl^©ç<öG©‰Ícl—6Ç^©ûÍ«z¶©Îc›yUÚl'RçÁæ±ÞåLì<Ö»ä½ó`}1¯pÖu…´Y×2O̓uÜ<Ø×8Ô®çÁ~è<Øû¡óN� ëWâgË\ò–2Š9GT–«ø*Ëœ÷³$?)ã(룲µ*c9ËÚ­¬p¿^Ç•qÜ“s2eð.Ž{r§lÊÏËÖªü¼, dXvžeåŒwYý ÞikÍç9Bêói žTi/W¼Ï‡úçü5J†ó¡V3öÄçÞø|ØŸ_4‡å7Åó9ËÚj>Çá,i7Á8¹V#ççs–ß Ì';ñHþ<¿âw>×m$—žÏñYròùm@ÓN¸äçó©:ÕüÃ@sDÕ^æs|.—ö1 áõš|÷u>ÇjY_Ìï�šNµœR­õ柚ÓjÝ7Ÿk>²Gl¿^høÔúk×ÄG¤Mà¼Î•o€/ ¹z$ÿ_ ô9Ê–p­UÖ ò€†bˆGÖ x­$kÉ@S8×eý²€í_Ö, f=¯+å,Ä®ÍÊúbA9ÐÌ<rfcûÅ­Ò¿XP 8ç�r®{û…œ%^P ôÀ¹"þ² ^­g@ìZÐx3µÅ¯°MJ-qÁ ç@Ö³ ØÆ¤Þ»€mLÖ’ 8H²¬<’ëŶlçùBÖÅ x¾›_pNá Ó¾0ðÀYŸ’ï-ä5”ÔÕ’<­¤Í>.qfa%гŸ–6ûµ¬ã’ÜBßXÈy–|?daàì³_—6Çö÷¥Íñ\ö²ŸJNµå&yÚBˆí !¶/lbûBˆí !¶/„ؾãžÔ²lå¼«íÆ¡6ûŽÄ™ò4Åo9ËYÎЖ³ŽÄgËKÕøËËÔøËËÕ8Ëýj<å°¯T^¡ÆV^©Æ_µîrØ.g™ËüXÎØ¿¥ ±±¼YL€Û<‡J]±¼ðƒ€·ÁøY†’Ó–³¬ÄÊ9·‘šù¢lõÞEœoH¬[” x¾Ï¢1@S Æ°¨HaQ1ी³ ¥Ö±È¯Æ¹h¹’ó¢ %ÏE•Jn‹Ö�^¥d¸h­’ó¢ ‡}ØEüœÌ‹X¶â ‹H¶!_XIJÛX²]Ä6,u’Eƒ~m·yèÐtXáuÑ)x¿Güt1Û¤øÎâ E³8Sgq¶Ïâ5žÅy@Ïz‘uÁâ1ðlà…€AŸ £ÅCÄßÏTúZ\ 4~ÀYGg³=KNµ˜e.kÒÅõ0NŽ·²Þ\̹¢¬17ÎñDÖ¡‹[�oz޲&]Ìy®¬Isn/¶º˜í\lu1ëAÎ0,f]H½wñióY5f{j—1/ñ©1,ITc[’ªÆ³$ h2€&ð, gÉùê%¬ù=Ú’"Å×’Å×¶Y±ù%,ñß%Ï—¬÷VÁ»Öª¯ƒ~ø9±%`ÿK8†‹=,aû—X±ä4Œó¬'-$O?ÉÇ'ß<ñ³|$§õS¼õÉ]–¿ŸìÜ'õFŽ?OÇŸÿÎ2Û^{‹~¶=™ýÌ£ø¦ŸÇ/gäüaœíVXïþÃ0ž#Jnþ£JnþcJÎ~¨éùO�Þôg”¬ü<¿Ë¼¹j8KY>éÒæ¿òÛÿ¥€gž8ËGl{)ËG~G¹t ÐÌT:ZZ¤Aü :¡d¸´FÉpi‹’áR¨í/e{œ|)ËPj¼KY’,efI›ç±ó¥§�gÝfKû Мœd/6#û¬YnLW|ÝÈò‘½ï3gû‘ßܘ 4,±Ç�^�Ï–«qÞè·ÂßÄ»q9àkï7V ûšì‡ÞȾ6”äü[J‰`¯äÆãJž7²=Ⱥró(¿ÅXÆãYV¤Þµ¬DaY©’Û2§ÌGËØälÀ2g7Ùc]Æã”õõ²µ@S­ä°,�ô5€7À³·¥†¿Œã¶üNvÙÁàØ®`Y9…Šßeë.g9Èù‡å©€g�ž x´s€&Oñ»œu*ùöò1€ódŸtyÐÀþérö ±«åeJËi^‹o¶W¨xµ‚žõI,ZÁÏJ¬XQªb× îGb× ÖKˆžçJ‰¥+Ö(®`]ÈoIV”~W°üŧVÔyøŠzÀYGb{+ƒz¹šÖfƒ³”.VœPò\™ªÆ¿’ãä$+ÙÿdN_ 2_™«âùÊ<ÀIþñò•c+ �gù‹ý¬,V2_Y8ËSìme)àeöŸ`›m[üt%ËMbéJÛJæ]æ÷•PW\Ù 8ÔV�99´ƒ8ßuÄJŽo²ÆYÉóB¾´Ù—Åíe¹ØR…psë_r¿Š,ÕgE¶zWEà¹jœpN ‚ß/sMË_棊ÀYÎ2U°le.¨àX'g*üŠ—Šå€W¾ð*À—få/<Fñ»ŠÀYþâ#”ïTœçñ ÖÙU>ÅÄ!v Cpž/<Жù´‚u!çŠW%*|ÛŽÌõ«2•<We)¹­ÊVr[•£ä¼*h@þ«@þ«Æ(9¯*´Âö¹ªhØÎe¾^U 8ëBö›V­Q:ZUÄù› CÎ)ÞWÁ¹µUÜÉ(8M9y@sÝ–3“«8HyÕIÀ9VK yÕ9%»Ü)õ䛨žå|ïM‰€§Îv.uï›XæR¿)p–ù/¤ϲ e/ã&–áQiÎr“sÑ7•ñü‰Ä{‡âý¦ZÅûM\ÿ¡y?:™o:дM»Ú³¾‰k8“o:ø± žOïV®ú±KxÒÏÍ\»“æf®ÝQLÎ_Lôõ@Ÿô\gL4´þÖ4Õ@ &é6Ÿì"/h ×£¤Žq3ó"¿A»ù0à<þ1Ò>®ö¶n>8ÉÐ#¿G»¹3¸‡•¿ŽÞ[©Þ[™£Þ[ɵDZóæS>‘w�h– ïëM#ÒCÞi i¶g©¡U¶�Î}J ©~+=Û—ÌG•ÇþàÄW(æWv~pŽ!2ï¬f;—yv5œ3Y x´³¬p.·:p–•Ô½W³oJ~¾šm@æ²ÕcàYÈEWÎö/v¸ºúœ x)àeð.¶UYc®ö[á3u«—Γ–ØíjŽ?’K¬æyVr¼Õ+<­®…~Zì?Ávkϧ9aD¾%ÿ?©t·š×qЦ5E>ÅèUŠþ‹~‘u'õÿ/²îäŒÍO~pŽ]Rç¿…e)µñ[X_R×½…y•õï-œŸÈÚö–³ìeßRøLÀYÎr†ó–§Ôóo©„>×�Î|I-ú–µ@Sc«Îñdoú޲g½†õ(çT×°î$‡_ú“óuk*U¿f àU@Ïc=÷5Õ@Ïú H»ž­‡g9ç—ý÷5Í€ï¼EÍáàøóÿbÙß½ é×è÷VöeÙß¼•ç9óëQÀO�sÖ­¬k™ƒì-ÿ‘6û¯ìu~)p¶S™¾Ä¶!óË—2‡ýß/û‚xþ'–5ºNÿËjü¶Ùž6ÓwH;ðTÀÓ�OœÇ {ßU+$W¬b?’üªª�ð"ÀÙn%ß®b?8)m¿þ}tUÀ²=’Ær Ì#_©R¼|•c‘|'ü«ü®I»ðbÀY§ç¤]ªöAn˲ÂwÇÜÆ¼H-î6>&ïºm¦Úã»Í¯öSn ûIù^Œó6˜¯oãyYê!·ñ\&ñù6Šá|‡ÎHZÔ³kǨg×vª9}íÙàük·‰ä·|å­GÖ×·“¼<÷nç}7‰™·§žx6à…ªÿÛ‹�/†wÍœe(þx;ï—ÑZ`´?/ùÝ1Š—u劗u¬_™ËÖqLy®ã˜à“6Ç%ÑË:ÖµÌë8ÈÜ·Žó Y¯c?¦uôhNlrà½0ÿ®kƒþ~ð#€Ÿòö;~GºÛ™j<wä©ñß‘øÀ �'™‡jew^ ï*¼úñÎò”5éUðìZ'Ç ©)U'ñÑ47xZ•ܪÁöª‹­p¢ºpö#±áêRÀË�½Wû‡\«ºp¨‹VC>YÍú_«®úFûO°Ý ø>ÀyÏ]ü½ú а H®R}h8Éú·šãìEVŸü <{h`MmÿLBð;aM}g"àéê½wf¨>ï乞ü}ôvJ;+½Ü¹VÉÄnKNr'ÈêN¨'ßY§ävg«’É�g¿Úã,±“;Î9ªÔ@î<8œ¸ó´Âí#qâ×wù‚8{,ø­>ÁÁÆì¶Œí.˜§î*œíMjŒwù­ð¹â»*�g¾äüðz¯œ+XÏ}Ⱦÿz–³ìû¯gŸ•½þõ,s9ÿ¼>hrg¿–üm=ûµälëyürfc}à܇œ^Ï>"{mëÙGä\ÁzöYû¬g¾d¯j=ûˆ¬ƒÖ3rFq=ûˆœK\Ïù’Ô^ÖW)9¬Xáý—õ°·¸¾p8÷²žÿÊ^äú& oÖìÍ­g[’sAë¹6R(í0†v+\ÇXÏv%õ‡õ‡çxû¤´9¯‘ZÇzØ›Xß4œçÈo4Ö³Êïy™ÝÐ~z€íM¾ËHTº° HÜ d�že…×/¶ñÁ@а ˆ~y€³ ýŒ¦¼Í¾GÌœõ"6àÁ‰O`Ï"P 8ËXbl ðFÀ›�ß8ëEü.Àz‘5T`?à÷ÄfG”N,s±‡À1ÀO�ÞøIÀO~ð³ ·ÀŠ®ïö)=Þ 5®»Sg½HònöM‰™wgž ýç¨÷Þx!àÅ€³fI›ýKlæî ÀÙ¿r¥½pŽ·’/Ý] Ï�gýJ.tw<Û ø>À[•^îf}I½÷¾$ïºû(à¬#™÷ïfI|¾»pÖå£ÿfï)³‚ø=P±Û«ïÉ| àÓ$߸‡ã•ØØ=ˇZ÷=Uj ÷¬¼Æ ç9÷Ôóø=M€·@ÿûƒïM9zb)ðëô{NØaÇ^òIÎy¯ðTÀÓ�8ó.ç!ï-œç&ù ཛྷ’vÐû­ð¾É½lKR£»—e’%m¶SÉ¥ïeß»º—e"6so3¼‹mF¾õt/ûµÌ ÷ò<ž'ívÀ9ÞŠÍÜ˶$qõ^¨AÝÛa…óÆ{Ù¯å÷¶÷² %fÚKeñÁØû«I< pÖ¡Ì59J&5¹€çβ•5N Ï›’#ÕÈ9œ1WYÁ»Ï,Á+}Õp=P¾aRÓx'à`'5Ì£äÆ5§¾Á§ð ‰€óXÄ®6d ó%ö¹!pÈÿ7€m€³p˜_ñë £¤¶¡Ty¯§dNÜà§¶ènÃZ¥Ç xÛ’øÝ¶ñµ û”N7´*n8 t·¡MénC;à‡�?®t´å,u¶ ¬±Iûs6b{÷Á\|_ªâ÷>–§ä-÷e+^îËU¼ÜÇr»†ô~«~GÏ}õJ/÷T2¿ÖÚ÷µÎv.¹î}Ç?ø)õÞûù¿Ë<{?ÿ•yöþTÀyü’÷ÞÏþ-±÷þ,+ìG÷Ë:nÌz+xß™Œÿþ€çý=2žûë�o�¼ð}jü÷CÍù~XÛÞß8Ïb{÷ï÷w�¿€ŸbéýÄnï?«tg—ùÄnk¹,i³œd.«Mš4 I–§ÔökÁjÁjÇ(9×Âþl-Ç ±íZ޽2wÔú­°ÝÖòü.ñ¤¶ð5€s€“X]˺x[Ëc‘˜\Ûx+à‡kYþ’ÏײïHì­=rc]Hn_{xg9Û0¿’c?PÄǼD.ͺ³Ûxà¤ÒãÔOœìU=À5¢†`{#ïõÈïI7’<òݤÇB¿¹ÞHïñÈ^äF’³Wô¾‘äìÙH²õŠ^6VNqÉ+þ²‘r¤8ák#ùlœü¾`#ÍïqÂ×F’gœ¬6¶ª»ä6’<=¥ÍûDò[G Í±]ê¨É=R_ÝxhH©©rHõHmöA®ImöÁLõìƒY@Ã2‘=ÇËTŸ–«÷>è¼è+P[~_ó ûµÔi$9x¤~û ÉÁ#ûƒ¶ ó{\ÚäËù~þƒô\œ¬m$½ÇK›õ>MÚ¤÷8©²9ÇIŽZG¼ÇÉ^I]´Iq²?RGrˆ;¯ËÝYKí )€6ÙLœäÌuÅ€SŽ'óBÝLÀÉ×âd.¨£ '¾SWxàÍÔ?ª£9+N~ P×Nö&q Žd啲Žd啵Žì$Nj×ud'qò;¯:²“8ùm×&ò‹¸Ni'NýÅI}{ËJ~Ûµ‰å#¿/ÞÄò‘3�›r�Ïj€› Žm‚à&ˆi›`lÇ4Ù›Ûyã&ŽW?7A¼Úõ™Mêî†Md'^ùžÀ&’ƒW¾³±9Mݰ™úöÊ=b›³&W}Ã3ñâm–6é={7«ýÙÍ+dM·¹ð2À«Õ} ›€×�^¯îqØLöà•3››alä;^ÙcÚÌkÉm6óœ(gÔ7·«{46Uwgl>ø À;�ïü”º_c3̓^ù-Ìfò5oe°Í?ßõÊï2¶-y×H›æ%¯ì£ma_“»P·°ýÈ>È–Àsg[’ßIm!û‰“½Œ-€Î>(uÅ-,ùÎÆ?A¾ß²…ÖnÞjiO^ùëŽáwK›u$sÇ–�ÐÔ� ëBêE[ö«{F¶�Ò…WöݶМè•}·-¬ Ù+ÜÂr–3ÿ[N~ p–¹¬ëë}ê~“úT[êyþ—õT=ËPjVõùjnªgùJ{¦ŠÕõ$“8©ûÕ¯U²­¯ ÆÛ±”7\û õlo2ÿÖs.-¾Pûõ‡‡ý…zâ=N¾ç_ p8çPçßêaýR ž= ôœ?ȼüP¦zïCÙŠæ!ŽÓ’»>Äk+ÉWâ8#녇ʇúüClã[b»–ûPƒýÇKöÕö}~í¶ä“üàP»xˆù_~èàSeÝúÐÀÏïlû’«ÛG‚¤Ÿ­œ‰ lMTô[S†çÉ!·ò_ÒùXâ»ïAÅ×Ö™jüv[~_¶öA¶–Î2”;.·² åw([×�Î5©-o] 8û¨ØäÖ€®Wo­~j¯z^+IízkÐs®·XÚMAù½‡RáBàt´•õ"9ÞÖÓ ÛsªOû32æm>5žm‰Š¯m©@ŸôéjÌÛ2�Ï< ú„Zý¶è3Wñµ×àdçci`Ç_Û`Én /ÛXbóÛ`Ïqì¹l«œu!¹ñ¶+lÃÛXâ#Ûê OÖ…äçÛàÙF%ÛmM@ßl…ýqÛ¾`Ÿci¾¨x週q[âê6Ö—üFiÛIÀOýiÀÙ$¶oc?’³ÛX¿Ãíi@úlð)~Õ³ ©êÙÖo@Úéð,ëWâyCf°Ÿ±¯_튯ð£†åJV €W^ý¯U|5`ü5@S«tÑPôõ0þà«ð&x¶h`mÞм·Â{÷ï‚ôc?´¬~eÀ;ÄùX7€îÎ)üa–­ä“³Í‹ý<̹e¶´ ­p}æáÀg^ý°Ì%>Ì~!ë净³¬d¯äa–‰ä¢·� ÇLY#?Üïâ9QbøÃG�çùNê³ Küðé ÍXÊoú7(Y={…0b“p.M¶Á÷Õô‡ÚË#àË€Ï>¼È}4(=>Â|‘oŽ#ßË,„~`¾~¤Ã ÏAt~pÐÝ#0g=ç ¶§)|;èt{à<©!o/Ròß{£ÛK• ·—î|9ôS 8Ô¨·³¬dݱ½pÖ¯¬%·³mgIö/¶�¶YÓm‡õövÀõêíP3Ù{Û~p¶©½4²¥ÆÒÈ1JòŸÆtÀ¡îÔȶ$ñ¶‘c¬ä<3Õ8KÕ8Ë�/ܯÆÓ¸\³±ðJÀYæ’§5®UyZc Œ­N麱>ˆ[šç}lûQØÒ£<²íq|VÏ4Pc|´^hxM9Û£í@Ãz‘û(œ“yôàPc|ôXf»¡Zõùì¹<–(ïåõÀ~ ó�Aü±BÀ‹¯wOž4èꢵ=äê\ê¢ãçæÅχÜ~G†¢·ÛR[Øç<wäÎú_ÛÁc–ó ;ŠçØ"çvpLÎŽor>aÇÉyvpœ—œ§‰e(9OS¢¢iJœÇ"¹VÛ¹ìS7ñøeÿº)p³Ôßš¸F!çšXR‹kb]ˆO51_r>¡‰ÿ»øQó(µš&ž_ÄΛxŒbçM¥JM~+¼?ÛÄ>"ëǦ ÀÙGdß¶‰}DÖzMÕ@�žÓe­×Ä}H±‰ÿ{¡´` ÍVxº‰çn©97µ�Îö#u‰&¶Ù‹ijúv a‘ÚEû‚œOhê°Âó`ϲ'ÒttÁ6 k¦³ ·Ë2Ÿîd8°Î6ìLUúÝ™8?Gö3þÞÐ9#Áaýb·e·ò®åj<;ý€€žc”Üé¶“e%{F;YV’3ì„=¦ìw2×ìdYÉü²âüN–‰ØØN–‰ØØN–‰|ÇfÛ³œ‹Þ•e…¿Sº‹ó©3ïbß{Ø•x>à…VØÞv±=‹]í*œíYllËJìpW9Ðû†íy¢´Ù†¥6»‹mX¾°« p¶gÉwà½5Ð?œÛÙU慎ñÀ¹]Í@Ãv*õÒ]lÛ²g·«ðý€³Ž¤–¸«Ý Ç]‡žõ%5±]Ç@/§@/\—]Û¥C‰‡»}€'ª~v§©~vç¨~vçÎ2[+홀sܨ’öZèŸåV"í:Àëá½ÐÏAxoÐQòÜ}Téb7œ™ÙÝa…cònÎñ$n³*Bý7§«þ›9Kݲ™e,߇i.Uïmfû‘ØÞÌv"õ„æZè³YñÞ|ú9­t×|N½kOš¢Ù“­úÙS úÙ3èÙžeM½‡ÇS*m¶g©‘Ç)ë÷=+|ç³ÔN÷4Á{Ùö¤Žº§Ý’ï‚Ê?;߈ó�bW®‚g¹å_÷.Ð\`ÐXMÿ.ôsylš“lÏ# šƒfBl6Ÿ ¿¸Ð”Ŧ±ÿ­0hâ š›cóeÿûRhnïÍ]] ÙК»@³µ 4v¦¹ 4OuæÙ.ÐìM{,š,k0ço‡9ýh.êiÐ Ó$ W£)´ø[…±i8&w¡áxò® Ûò)¿Å¿ÝˆMÃû°q.4»<I.4<ôt¡ Xü½Ø4÷6¸ÐÐâÉq¡¡yÊ3Ô…¦™hò]hx¯¹Ä…fäÌ{ô³\hÚ-ö÷Ø4<gÝìBÃ{ße.4¼oRåBÓaqŒŠMCù§'àBùM½ 嫞gbÓØÇ-ßt¡!ÔsÔ…†ætϯ\h(götºÐdÍÇ.49öÒ96 ïW&¸Ðð>fošB¢Ép¡á½¼lš™öù„Ø4eöƒØ4~{ß06 ÅŸ¸.4âæºÐpžës¡ XœÿĦáøs¹ ç°ý]h8Oþ’ M³Åórl^ƒlu¡áøó¨ Ï“Í.4íÏ˱i8þ<ëBÃ9ÿA®#µ»ÐttÁ/(þÄ-w¡á³+7¹ÐPü‰ûjl.»Çmr¡!4ÎE†CøœÌ7\høüŒK¾1$‹h>p¡É±Ï9Ħ¡øãMq¡¡øãÍr¡)$šq.4¼.óé>s£ M™}¾"6ß>ó›†âw Åïó.4|ä-š�Ѹä~C(þxϸÐPüñõp¡¡øã»Ì…¦™h&¸ÐPüñÍt¡¡øã[éBCñÇWãBÓîîïC(þøš\h(þøžt¡¡øãûº MѼíBCñÇçæ§|?q¡¡øãû]l.ƒúþáBCh¼×…†âO|?Š?ñy.4YDSàBCñ'þó.4â]ü}(ÅŸø[\h ‰Æ%Jñ'Þ%& ¥øïbC)þÄÛ…ÆO4.¹èPŠ?ñ¿u¡¡øÚ…†âO·n.4¢Is¡¡øÓÍ%n ¥øÓÍ%÷Jñ§Ûtšf¢™ãBCñ§Û*Š?Ý\æî¡ºÕ¹Ð´Í#.4º}Í…†âO·.4ºv¡é šc.4ºýÞ…†âO·³.4\æÞ¾MÈt¡!4ÁeÝ=ŒâOµ.4\æaYD³Â…†âOÂ:Š? ÷¹ÐŒ±›¦h^s¡¡ø“ðCŠ? ¹ÐPüILu¡ñÓpYË £ø“x ÅŸD—>ŒâO¢K>,@ÿÁe½3ŒâOâNŠ?‰.kaÛ\hšé?|ß…†âOâ Š?‰.µ¸a]jDü}°$—ø<ŒâO’K>ŒâO’K|Fñ'i¢ M‡Å{±i(þ$¹ÙÅŸ$—ù}ÅŸ$—õ2om%ÝãBChÒC.4’žp¡¡ø“ä²>ÍË"—Ü/âO’Ë-Kú³ ÅŸd—ù=¯h\ê6y’]æå<Š?É.¾œGñ'Ù¥™ç'—úaÅŸd—y9âO²KìÍ£ø“ìR?Ì KLÈ£ø“ü=Š?É?w¡¡ø“üšfû¨ZlŠ?).k¢<Š?).swÅŸ”K]hÚ‰f˜ ÅŸ”Q.4R\êuyR¹Ðt›QüIù² ÅŸ—ù=ï¬{ˆì¤¸Ä–ᄦ¸ØêpŠ?)-.4R¾åB“E4.ûDÃ)þ¤üÉ…†âOÊ¿]h(þ¤º¬+‡MÌù²</ñÿþç“=+-AŽÑS™ÿùÄŠ "iÖž3„ ±<Á~¼ùAº^–êGh®Ñx^þÇ8f/ÅºÔ .4MD3ƒiâ¢Ò<N¾œº*6Í>Š ©÷»Ðœ š†X4YVf;ѨzTmð?^4—Ðÿ¦ª}« ‘³¶§™E~‘z­ ùijIWiì1OƬÑ\BO¦Þ»Ec÷óC§~F­¦~O;Úýiú$)zò×Dóú>I [K:Ñ¥~'$ÕàY"9’–¼Móðäê¤ô¢y§ 4] 9‹†Ð÷ˆæ¯ÈÅ:á?Ã\dš\ÐS±¬îqøÔ4ëuO ?U¤Ó=åI²„îéaš¯8Ñ<qŠh„<%t¦D§yŠl±û•aš ÇwQVÐ}dlš§(wŸ‹Æ²zü‰h®W1ÁŠx!e„„ÿðs!dµ _¿Iž Ó($DóÝu‘4 Ól5h>Œ¤ùÞ ‘4 Ñœ¹0’F!aš¡ÍÐHš¿;’æãË#i>žkÐÌ5h~hÐüРù$’æÜË‘4ÿù0’æ?‘òéÆ',4@Â4—4—FÒønˆ¤QH˜f…A³Â ¹Ë ¹K§I³øˆ¥šw>è ê–iÿóñSu�Xk ä{ò»HÄcÈ�f  ¤Ò@î0g ä5ùs$ç1>r¹\c ׈ß@ª ¤Ö@vÈóòmù¥ü5ñ&È¥2Î@æÈ Äà»×@ ]xÈ©HÄgØ¡/Ó@FÈ ²Â@ÖÈ‹rØ@Þ53‘H|ŠrŽk ³ dµÜo †œã_0· Ä|üûr6é–j H¾L5rYc ÷H“ì7ƒÓnÇ äòI$’ÐË@Èh™a K äK²Á@3¯ˆÞ1?È¿#‘Äbè4q„L6yr³Üe È>ù†|ß@ ¯Lü0I2"IR_É5c&J2ü4i¥Üi Û ä)ù¦1ƒÓ¤Ó‘Hr7É0« ¤À@j ¤Ñ@ž37 ägbÄù¯ô7ÃS¦ÈR1ò–” Ĉ-)Æ\bX]Ê{‘HêEbä6©7È“òª¾œjD­îà Ĉ½Ý+ Ę»»Í@ Þ»yTÿz 4ebxAÃ6zsJO#÷ëù–œ0#‘´4j Æl•fä?iwˆá)iψÃÓ~a Fë•h FFÝËð”^†%ôZh Æ,ܢ…{sS/cîõ-1æ¦^¿7cnêmäá½<¼·‘‡÷.1Åòe1¼ ÷.12«Þ†{ñ¹·‘‡÷1<¥7úäÈ$)3c•ÔÇÈû4ÿD"éˆ!ÃôÛ Äˆcéí‘Ȇ¾.(D.4äs¡‘“\¸À@n1Ã+/|Ø@ž67 ÄÈ–/<i F4îkÄ–¾—ˆÙúN1#kêkä} É÷5r‰¾F.Ñ·Ã@ŒµLF?14˜±Û@: ä£H䢹Ö@Œ8Ñ ä1"I¿t12½~†äû-2[ d£l¿× ÄX•ô3â|cÙÿ1"@ÿ-òH$Ó˜A2•KæbÔ%2‰ 0l~€Ñó�c ;ÀÐé@C_ ] l1#4æÓ‹ž/¾Ò@–È:y9¹ÄÈW/5ò„ËŽF"ƒÆD"—×ÈbÌ2Wü$ÉY‰ 6rì!Fn3Ĩx 5V C¹rØ"‘<£Ž”gÔ¾†>܈ Ã8?ܘwF\m †Õ048¨^m¬’®6fá|c¦Î7²î|ãí# ¾F^g Õ‘È(#Ûe¬4Gý:m¬þF³Ñ÷D"cŒJÅ5†NÇ2gør¡‹#ßoHc¼±â˜`¬› ˆ‘UN4æÓ‰†.®»Ý@ ›´Ç@ Ÿl¬ñ'×G"S†H¡1jÊŸ"‘b£RQlxe±±‹Uí4c7͈$ÓñÌèm §7|Õ@ŒuÁç ËüœQÛùÜ_"‘™Ebd)Ÿ7<åóƪ¶¡‹Ù†ÍÏ6*í³uJ©a½¥FVPj¬æ«€¹Æ¼3×Èô澉Ì3ö\æ™gÙç#‘ùF¥bþ¹Hd±Š\`ðµÀX­/4$¶ð‘H¹±š(72´EFXdxÁbcþZ|ÚYlä«‹ÝÅÆNÍbc…¸Øˆu‹ ÿZlÔi9Òbcí°ØÐÅ£þ¼Ä°–%ã ĨÁ.1ªÍKŒ|u‰±B\bTŸ–üØ@Œz”ßÐŽß°(¿Q7ö«6¿±vð;KX·ÔÈ‘–Oݘe ÷È£bd§Ë Nï7jVµ†m4VR„FζÑàt£1ž†.¼Ñ@bXfë6‘d³áM› n6v46ï0#Bnéi FTßbTn·ë¦úd1r­z#ï­7<¥Þˆ«ùüC†7m5V@[ügÛ ÄÐò¶Ç#‘‡ˆ‘É<bøÎ#FlÙnT·ëíFF´Ý¨0l7VÛµF#F= ùXð4B÷-•usððPüCïY–'‘w‚ùæÍ¸ÞÁ§ô“Ev?ö~îfì§ŠO5|ÁcÿÞÿý[þ¤§ìŠPMè¿dÒ !^BìÜïÞPÏÔóÚpϓ͞ӬoÑx»W#[o°¬¤Fêî\t.˜ËG_D.ì~÷DåâûÚ˜òsƒ¯÷5¤?!iÈE–õXª†ô#äbÅ{°çÇr5$ƒq(Ÿ®Õäó‚“|~ØL4u(Ÿ†Kƒ_#÷¥D—O2=õØt”ÝOX> Gùï-‹äý±[#%öØzƒ÷ÍBr~l§ÆûBì5ãÝøÔA”ƪP W8IãGMD³¥Áç¾øŒ˜/=º4ºÓS} ¥a÷–†u‡£4|ôÔ/_ôTŽ0øÛÈþ˜žêþ 0.4ÂoZöð¾¬è#ìÁïú�Gh÷¡ÇÁw,'{æ~ÎEÚóŽäH îè‹’ÿ‰ùòÌrâë'9Ds%ÿÈûK–//:_=é©ÙÈ—ÝO˜¯¸ó]ÑøÚ1Úàkr¤Çí(E¾ŽQïÝñ»–³¾ú¨Åm 6<vŒh;ÑX–F–OÜ“Nò9v–hŽ |¶O!Ú2úüèòI£§v,CùØý(ù8è²ï¬Þñ~*æŽr¯†tòˆ†œ$ä 9MÈA a~[“*ébÇOPª?]£I5¤úDHb?­Ò¤ 4Jª? hýô„~¾¦9¢õÓÛ±ŸãZ?Ùâž ÓœÐúñ8öÓµŸçC4?ËÕúÉpêçgyQùz)L“ïÞÏ;YZ?™ÐÏ«áÙ¼ úz'O{ôïÊÓ,ü‹Xø;3‰æghá?Ú³oUt ïCOíø Z¸Ý²ð£æ»¬hàãÈÐÔ-24]€¼?¢É°Èðû!Þž£Éh”|l%Ÿß;ÉççÌ×û(ŸGß·ì»r|ÕÑås=Õ4åósM>¾…]”O¦‘É 0r’~FNBó{Ód”Ø/Ê5‰]{7lu]èç—ç4©B?Jª6Êît’ê ö¸OPª;”ý×¢K5ƒžjš…RµûQVw«£TãjÓr÷¸Útëg«'öi’OÉ–|½k½öT‹&gxJÉùÄ­ç4èù£=·¹ÇÞ‡¢ÆÌs1z>ìO‰Ö³×½ç_•»ÇÕ_ù£IÃÛ-FÏËÝ{~wf´ˆíM ÷lèý]¿{|¶iTü٤ш§¼ <23ÏCƒ‚ó}3º§ô£§š@Oá~ óœk¾ËrŠ?ݨŸ& I ¤eM¯¨U�½ë¨&1/H,3,1~êgÚSg5‰Á «$ökMbžUNû5K¬Jlq᥹Ðw6ºÄú³Ä~‹ûµ&±¸O%æ[þ©!XÖÎ !Þw^ ­›(’ì¼ íç×§5&‚ GhV÷m|ªÃ§=ÕžÊ ɰ#Õ=t¤w&K{üó`&£ó㩱1øÊÖÆ£ÿû×yÐäD}ûôcŽþTqŒ1GjJx<mÌ:Žù¿Dó›åÚx®‚ñÌÔ¸0Ö_;G#_ïéÑúQïzoMÔw-ˆ.çO•E—ó{u]OWi±ūÓHlyo?Å–K1oiN¶¿šhÅ'E-é©“0¶p?[^4ßeE©kE¬—ã©çÅ绂îÔår¾9$ÎýîÑØ¦Qó×a'‰uR¦ÜcFã=üÝzÊŠâcÔv²è©kPbÜHìo]”Åõ‘3ÚÎÝ(ß•i\üɋ߈‹){é©qqt..¡§v>\p?Š ïPG.’œfáoiˆYgûÝiÍzó¸xŸò›s‘‹½|— é-~pt..¥§vþ�¹à~€‹Ï9rᬋ÷#íyç™ÈìbWòõþA¯yŽ|u_+5¾vYö,Ÿƒ/zjWO¯¯ZG¾(›Ù5 ë‡di#|Äi„PÙ# V‘ÝÔ¸.ú/£§vMÀ~ ­"½vQò$ç]3qÌ'KµÐMuâÝòî?é G{Ò(ÞßrâýO”#õxyâ>â”}§$:ïWÒS»!ïÜðþ…OËûémÌœÆ|º‚ÆÜŒc~ŠzæÛÊâçF3Õv×í8fîÆü-Ç1{<wa¨Ÿ6mÌßq3­X{¼¤™«èÔE¼?Ƙé©]hc>©ù¸ã˜ÍEcÞõŽù¯EÚ˜O˜ýM%ù[Ú˜¿)c¾)ú˜‡ÐS»^Á1s?P]‰7ßå(çn‘¶ñ×#Zí"Ñì‡h(Òö8†cná¿jTÅ3=µë¨6æÓÚ˜õ5~LÛØõg󇚜}=œÆü!ÅÂÐÆÌ·”ÔSã®ècæ/îöà˜¹s±ã˜mC·çkc.q3Oñ=Îicæog'Ä?}ÌüµºÝýqÌÜØó¯Çì¸òØ õEÎM«×,üwŽ\Ðx{¦!_ãY?oùÅà‚žÚ=Zã¢MãâºÈ…Ï‹3…?uââ ùWÏAOJ ß‹<zjw)rqFóSïó]V°§¯©ÔÏ5$ÅȈ÷ÝÕÂÖ[†3úš:|ç›íé¡õÍú»÷âSoÕæÁTmÌ2þý˜Ös:ôÜ+»Ÿ×zŽþTZôñüã 6xJç'ºH£ô>C£½D2ê9õþ4Åç8ŽÏ?ˆ®÷«é©Ý¯£Þ?Òö=½'ÍwYöL½û¸¦Áþ„ü1RË®{ЙüÉóÈPsO´ùš¢ívùáúTê¬O“*TW”TÏ´žûCÏÃbôý©!á§(7nˆO}œ¡žRãé_‹ÆqXÂÇd‡=§áÊ÷IOÊG»Í‹n #驿ah ÜÔ!o1ßE]wDF€x²˜æ‰‘µ÷æÏEÚF³±n¾‘n4£7oÐú!ù4oÓ¬…æÜæ=ˆÄò‚fcduÍG5„9ý½†ô%äZ?y–µ'Y£i!d †§!k )Õ Y‰–ðÏÚhû;¾Âöã5vŽèßž*­ŸzÍZî·ðŸXË?ÏD}×ìð»ŒþKßwîÔó¿Ú£ö\£çS]èùtÔžÅèY¯H_íØó¹¨=ß½çs‰î=ŸócÏžÕÐóê=Gêæó±„sÚÎö#ìÍ¿Ó4ùtÀx¾‹ÿè;òØq¨çÿ´h=‚žo ÷L`Ï}ZÏ´ž¿ãг‡çKèùGÐsuÔ1{¬N­ç9õì)*»£÷Wç* Oœ¾øCèù.ÐܦÑvx=Çwu•æostN½Ùî=Û4Š‹eÐscT½Çzê‘ðùÃzªr-·R“ü÷ÇÓ}ß³ÞþJtiøt}¡ CgrbÑ<îÙ§Ÿ "{^ÄwÅkçIâôµÌ¿Îƒ¦PTW|ÏœGüñÄÕ¤ºËñ]§´wíƒw=>ïêV¡½ úQïâZ¼ë.x×7Îç]‰zö¾Ýé]]¦Q9ÛlæßBC«ìž³1{?È÷URîÛmiôœ­ˆžÚó&älv?½Ï|—Õ…µgš'©X[qqsRù&m̯[ö}ÝVGódzjOŽ™û1/èâ˜iÕ¿G?DY÷^_Ì´'Y;ié½Ñ‰¯ä\þFòõ ïƒÐ+¿¯)ôÔÞÞÈW²vâÅ÷²ã»J4Û8èHC6ßó>m<|we'FEh =µ7[Váô}ÓQÎä{Ç¢%$×j#ls!­»{6h#ü¶]ËäÛ¢žÚ;Ea+Ž0¾Ìq„Žu㽋"W§¦i5µ·RCú²9M)ÖâÆ4õjß¿C¾œRm¯ß÷·0M@‹ I!šÚ¨ý|¦©ïB?õZ?@ãûW˜F?í3±Ÿ®Ðœ‰&Ÿøî] I Ѥêó{/ ‰ÓègN5xRó¢ö“¦Ñç §ÓÅžT=+˜ô©i¢ò>ª 4ùaýLœ‡Œ¿ L£ëË©VàI=µŸ‹B4ÝÓ´~²œúé^mÝ? LS¤õ“íØOIÔ~. Ó”u¡Ÿ2­ ‰¦ñw¡Ýwr¡Ÿ+Ã4 Z?CûiŠÚÏ0M‹{?=õURô£Îùô¼…žÒ³¯BÇžOº[o—iÂsA|³F#sAZ*Ícõ©mcðâÝÞ‹>”ÐS{ñœÝÌV™æ»,»6Q6«èíÔ³~êž|gïKB¹ÄÞ7#3=Ãïu@›u‹Þ{uïßÀyð5þÍ™ÝNGçýzzjï1äûÞ!²ïí]àý¨Áûqƒ÷}tâ½OµÆûEN¼÷!/èù¦Æû/ø+©Ä{Œß‘}ŽžÚû>òÞGûePü>ó]V”½¶½ÿÔËz<12cÔgü¾ZöïpžhÖ_?@¾Þ¸Î²Wö ÉÑùšMO=ž|õÕ÷ãN}XQr›Çs5$^?Õ`9ýF,ÓÐ)iùñkQ§|Bt:Ö‰÷Œ™ÄûÏÑ—ß ¬1²•„ûz¥ôÔã7 ïÚYˆnýÍw Úoßâiî~|…f«ñ‘¿}‹_K4ëÆ×aì24ÍB‘ÿñ‘5áÇŸ‹¬î>þ}D|ýÿEd½÷ñ?DV›#*ÀÍDó‰†ÿìë¹Æ×Îy2 ´¹�N ÆoŠ^9ÉÔgXXÆ?~Šä³ï*¬œdêsÜ ÿýë<hNDóV\wïƒc¥õ|¹SÏr¢ö¼=FϹ]è9/jÏM1zÎïBÏ%Q{Þƒ³¹vÂÍ3`¦Ös©cÏ¥Q{~2FÏe]è¹<jÏÏÄè¹¢ =WFíyŒžõsMsœzÎÒ­® z~)Zµ'Ís‰ž÷V9õÌ7bDó7côœ©õ|³cÏYQ{~#FÏÙî=_ÚµçCÑ{¾ôŒÖóJ§žUh=×CÏß ÷Ü…È6¨]{ôïÒW.ð{Ïøc] ù1îÃâ¬ç8í÷§ÝÚÇÓšãQÇónhNœÏ˜/¯tOWh®¨Óƿٿ 4a]>MŽïê \„wÁ§âÿ=çè~QçØ³öûÊnÿÖh$ÛÉ¡Ùó÷˜é}ïÏÁ û¢g;Ëé©}S1Ûá~ ƒu8¥cE©gâ 7ËÎv"rZ²„}‹‘÷+õè7$vãjÄSšÄà)%±+;£õÜÍ£ç“]èùtÔž»ÅèùŒ{ÏWi•öø5ÑòU$±ž¡–ðŽE#§>º–WÐSûV¡–¯ÒN¹Çd¾ËŠv&0¦–Ó<ƒgj¶šàÄÅ`š±Òº!?à >`ßo•‹•ôÔ¾»‘‹ÁZí=ad׹ط?’‹}‡"íyßÑÈ5Hĩʖ÷½‡¹è`Mƒ ×8òN–6Dãý{Âû÷cðNOíû«Æû÷¢ÿkÞsÇh¼;œØ$Ö»¶OôCÞ/i!ú_D罂žzâbä=W×{©ù®ÿ§¼Ó ò Ý ’ñ£äjgÞʤ1„r­´jMü›�ÑŸŒ.UôÔ_Fip? ÿËÒ0,aˆ_ãý&GÞiMÛ‹¼ÿÏ}Âû?bðNO=±Gã]«·$è™ðÿïïkxÊzÿò~”3Ýj2,_tÞob½ÿyªëýó]ÿ¯yâcäï£Þïsäfª4í|øQÎ#Ä{ï¼ÓSO&i¼7j¼ëyøÿÞ‡êþîð]š4ß9Þ«òþ#Þ™e½Çø¶ÏÍôÔ“#‘÷aÚ¯¢vý_ó>Lû][ÂGÞøf÷o[v¶›ãû?7ÓSO.ÕxoÐxæ³ðQm&ÞŸÜÉû“OEFþ'Û"#ÿ“?ÒdB:q.¦ÛÆ~'ùð]Ͻü(Ÿó/çùÖóqÑåSIO=yåÃý€|^ú,òy*¹È+Õ¸ø†#䕽ê4.v¿^•8-ôÔSš…çéÞý†#™]à‚§jeÞOUhëÓ„Ü®!dáO=€>\;Eð'Þ‡·ïO#ï?YlÙ¿"Iœ÷ÕôߟÂ_EÙý�ï¿f²¢œqÐàk‘ž«ó5¢Hãë7N| ìõ]¯H§ô¾ÄåÑùú"=õþ–Óîøúƒ#_]ÑiêùãHÏmÑ¿§G:m¹HÓ)I¬åŠØZ¾ºP“ÆÇNÒ¸š¢u¯?¡4ŽQ ÷ì#i|)º4n¡§ZÆ 4¸%D‡“ðV”ýÁi´SÏ+5šÃ„èO#ä !«jÙ¢4O~'òž¿ÓT¼Ì²¬ÞéïïÒÿ.'ú@tÞo¥§ZCÞ¹à]ÿÅ÷yƨ–ï–ðNd oéÔŠá-ÅÈ6R;-–è°_I4DÜ{òþÓ ¢÷Í1x§§ZÎi¼×h¼÷3ße}½mðùê}Ô)÷N¼Î!ÞoÔx_xoŠÎû—驯i6?Zûv_¢Ã?ï+Ï—÷1Ú¯ê;ñ>æñþ�òþ³ÍÂûÓÑy¯¢§¾V…¼s?À»óþé§çýñóå}l¾Æ»Ãï㈆"dïýÈû;¡Xw0:ï_¥§¾¶y«í'Nü/óþëóå}œö›ÐD§ïÊz Ò‰÷w4ÞßÞ¿÷µôÔ×N!ïÜð~×÷§ûŸ/ïãµ³‰NßêôŒ?H¼‚¼§µœ§•è÷Ûé©§swîx_èÈ»ù]£®Äù¿Ð»æhOY®fsËΟ¾UCÈŸ¾gükµïò%V8IãZšßú\†Òøù“b ¿.;è©§Bip? ç/Èý/Îø…Ú/пâÄûDš+ûLCÞÁÌ3þ_£ó~'=õôÈûDíëʉw9òþ¿8ãOÔöS({qâ½’x¿Eãý9ÑûbðNO=ý'wí—ȉ8òþ¿8ã_§}6Ñé[pžIiÄûÃÈû/çyOê÷õôÔ3ø‹x»àÝá‹%Ÿ‰÷óžñ‹´/f'>æÄ{ÑâýU÷O„÷þÑyÐSÏLCÞ¹àýñÿ2ïç=ãOÉÔxʉ÷)eÄûo÷ë’®ŒÎû=ôÔ3·!ïS´=ÄD‡ŠÇgâý¼gü©íïœxŸzβÒ÷_Ý ¼ŽÎû½ôÔ3O ïÜðþÚ—÷óŸñ§-×xÓ‰÷iÍÄ»¶'õ«ïä-I“¢ó¾žzæ'È;÷¼;ÿ~§½ ¼5x§¹û™s8wOïÐø‚ß*¾fd_7 _ïn¾>¯ûé©ÖDäk†VqM|ç¿ËWëpä«D«¦&þÒ‰¯Z¹¤ksÓ¯ù[ôš¤…Ñùª¥§ZÇ!_ÜðÕù_ækòu½žu;|™!ÍseËéÚ¼ók¶¡F¾ç;:_é©Ö¯ _7èY÷_ùúÔùFëÓ˜KÜP¡ñå°“N4”;§¿…|u\Gú"é'}9_ôTë75¾´_é?©9¯ŒºÃQ_¿C}Íô!_I>'¾f_¿ÓøúƒØáúè|ÕÑS­Ú^6÷|u7ßõYøz¶?òõy­–›äXíù|»e] Í¿áPÑ?¯MôÔ³W _Üðå\íiÿ´þõìç¯YÚ×°“:ñ5›Vý\|½wið×ÏIÛ£óµ…žzv!òÅý�_ΕœOÏW òUª­X“«4¥4£_P¬ñõºèëñè|ÕÓSÏÖ#_¥ZΖä\¥ùô|}ùš£}G1ɱ3—Ö§h¿Qýí‚`_I1Nûl¥§žý6ò5W«¼%9W`>u<Ä_­Ò»ü_ú/qB|QD¿`“Æ×7E_m1ø¢§žÕâÆ\mŸ(Éáë^ÖgȵžËÔwôšCšgž¶’Jr¨í¤yÊR‰S­†Ö98¥9#éptNè©ç#§eÚ¾NùY8Õùš¯}·0i¾_óIWüHãëÂWŒÑÃôÔsø.»àëFG¾Ú?•Ç‘e>w§FC~ó\úà‚}§_z$ʨ/ø9ýÝWÅVcü"ìz깇‘ÓÚ‰»$‡;2>§oFrªg_åé§Õ'¢™iY^ˆœ¾ß;øÕõ¤?Gç´‘žz¿7h÷œ®säôÓ{å¿ÐVé³yµ_‹¨Ÿ ¯ÑøÚ+¶úÏè|=JO}=ùâ~€/‡ïy~¾¾ž|-ÖgýûHÂ׊ê.D¾>ä+¹[t¾vÐS_ÇoDÛý�_Îgi>=_7#_~mu“´Õ‰/?àBí ¼´Ãäg„šè©¯¯C¾üÚê&i‡ù®ÏÄ×ãÈ×ÒÃ_;øâàvaòur–èk`t¾vÑÿ}ýä‹û¾î“úL|@¾–i•Ò¤'¾–¾®ñõ^‹ä!ÑùÚMO}ýÈ×2í IÏ;òõéV” ï¿P£`|mc-Ñ R1Ó²£èþIE¿ Kñ–ò–†VCû=Ÿê¬$iŒ‰.=ôÔþÏ£4¾ Kù–B9ÛþÛÝs¶ýp¿õܤqáøu”/PnÓ׃\üa‚eß”—£"´‡žÚߤqqLãÂù×]äB?´"Qã¡&C4”Éô½Xã‚O%ž$.bÔöÒSûßB.VèëÓß8r1° ù3ÍÔûÿ9Sï?‹6¶BÛ‘Oú‹_+³‰¯k‘¯?Ž%œ¼39Fäqzêùxä‹û¾þÞE¾ú¿èo|÷‰ôõüP 9Kˆþ½AŠÏÏ/Œô¸çï@iܦÿn æ‹Äï†mÑÕ·¿¢}þQ—·ë¿ý®&ŸMX_Éo™2$ò±¾Ú龿rh*'ú§†^¤§žoA}ݦ"Hþ¹ù.+Ê:îù_DzÓóÚŽØmÚ·RníIó¬¥<¡ïF Zes :ùÎè\ §ž×ÎÀ¬Õò‡õ…Å›L.^…\¬Õ¾r²Ìì™hÎmÈŇ׉.jcpAO½ }íg­VçO‰zêÞŒl\T!·k;5)«Íž‰†ò¨¾ïi\ðîeÞÉõѹx‰žz¡¹¸]Û©IYãø.-ÇNqÜ)¾bOßÓ8ž¿qµ‹g½ÇbŒ‡žza¯6žm<çç»"UêG«6§8¬AÒ<ëhM”q6fþVùróÑÇü2=õÂÿà˜×iÕæ”;?ݘ-Û^ŒG.Öi§tRœnŒõ¬#eLÒ¸Ø%\Ĩ½LO½ØWãB;Ÿ™rù®®I~]£6æ³¢¡µpÆ­Ú˜aÙßÃKŽqºæezêÅëµ1kkê‡U’ÕµxHóò‹wh\œÕæ8ÕmRV(ò¯Ó¾N™¢ßâ!4wXZ?è'§ 4—‡i´/"Fpz>4Ešv6š#š�ig;jçÌËþBòÑèÚù=õ¢ö î´ãpÒÀê‚v¨íNŠÇ5㤋 íû-g^—1ÇøeÙ7è©_ÕÆ¬Gõ‡?혫µ_Ã¥8ž4¨fÏÕ~±û÷Q–uq!ÑÇXdÏÕÖMÕºç:¬=-{½óâ"Ç| {d.ñíš…\®ò¨4Ïú7àˆ¤Šmï@šx¨‹&­èÍòp^G3È‘øöFí›>X%©o _DOMמҾ8á;OùÃOQ80OËY>«´~öiý†~‡øŠAS¦Ñ¾ Œ4–õÿkík¢ñ³iiöãù¬éÍÃ4ÚäRv;¾« 46kïêïZÛš¯†iöiïÒs>4mÚ»àl|R  4w…hvh+—ýwRçK£â˜ã¯~vÞ3þŽqì×ë:‰þªèqìzêÀZŒc;´ó¢)Î_øäˆ´Ù%ŽQ<<°#re‡‘-ͳS; –âpËÑ4Q0ñ"_ÿþ'qAž2>:_?§§h»ö;µß¨¦8ÜûfEYï0n1‹XwÓJáÀ¿4„"äK©Z£(úR?Œc»Öhö¿ÐOÚkê—†hO×ÖÂN7{šµ]†Tý»—"ÕfZ£\¤íAÿçiû sVÊ·¢Kõ=õÞSc÷£¤šz™ù®®I•w¿_Ò«…$Õ—ª ©Þ£!4˼ô°†ï/=©¯—´Ûô<d;/ýßå!›é i§L×§!Ç^¾HCŽ2\C(~y’†|@Èç4„Öø/¯Ò’úËÕï¤÷—Òì§ž]š7Q†ÿrk¤½|PCH†/Kë§/!Ç4„äóò4¤—~o2ýK#š5äÊÛ5äBBŒ¯acHdÏßпKÓ‡ùhá{|Qsþï…,|v§- þ…hÒ¢öóã.Ð Ódvá]]¥ ¿+î9x×/º@s<L“ïBxWWiœßõ^h:Â49]x×ÿÇÞ›€gQdmÃÝ…$HXCH ì;" ¢"âŽŠŠŠ‘M6Y"KØ7DEe\TDÄ ÑADÔ—QÇm׿º“§žsê®~ºzäãßy;×%™¹s×9uj=UuªÚ”£×õ…çä´5ÐeÂéàëcgÀùVr:³¶ÑV«Ë€ó�וOtýlÀ9,9ݘ®µºL8=˜.-–ápârz3]Çhu™r´m##À“&9g· cŽ^WUN–äô1ÐeÊÑëÊ5àäHN]&œBß¶ÑÀ€#÷dÐ6L8üÕ;á–ÑTúcÂ_}z4õ`Ý�~â¡“ìqâþ˜vôÁ™9—øc©)£ËôU|ÓßûX¤zšyï®âMC]–ñIßÓ÷«»sOsŽðYŸÞÂ4ì¤;01Û‹´¶ ï«öXf»`‹9¾b‚ØžEª§ÿÎlg+—LÍž§ev>(f᪟¹±C„?¿±‰zú¶ñDÕóÜx>õmšÅJc®4ëîÚ+hi¤º7ýW‰ÒHÿsP¤ÚXHKÕCJã&mi䛕†Òê¨_úXSÀìºWg×Ñko£vy¯ˆ:«øo»>©6ÞFírå»Vkí2‰¢-aãvf;­È\£³âQµiZjšÛçÅÚ 3ÅߊÏÜõ.µâöjPæ£Z+Lj§.óÊ*ûVÝçtŒzd³KóI–ý¸ð9r뻜â¿ü¯ÊþXߎ[Q`¹ïÊ ŽSÆq²Ê9)”Sß±þã¸+6÷§vãˆY8ù§�ŽyµÖ”ã¸v•Ïã4#RåôÄrâON¶VNàT#9žœZ9ÂÓ«|\€ÉñäÔÖÊuQù´�9’ãÉÉ×Ê&8È‘ON}­œ"Á Gr<9µrζòr$Ç“Ó\+g½à, #9žœÖZ9¢gW¾7@ŽäxrÚi圧äHŽ'§£VÎaÁÙ Gr<9Çëä4½·ò[‰åÄ9žœ´rÄŠ¬ò—r$Ç“s’VNÁù-@ŽäxrNÕÊqß_Í#9žœÓµrÄ(Z¥I€Éñ䜭•3Wp:È‘OÎyZ9«§o€Éñä\¨•#æÛ*#äHŽ'ç­œ]‚3%@Žäxr.×Ê9$8säHŽ'çJwË¥Êí‰åÄ9žœÁZ9"e•r$Ç“sµVŽ˜O«¼ Gr<9£´rÄ\Yåõ�9’ãɹV'§~¡˜—÷Èœ*_˜r<]µy>@•Ãr$Ç“sƒVÎá™T#9žœéZ9 N›�9’ãÉ™©•S"8}0ÎñäÌÑÊ~fÖÔ�9’ãÉY •#üŸ¬ûäHŽ'ç&œÆÂÿÉÚžXNœãɹU+§@pæ‚8Ç“s‡VŽð²«È‘OÎZ9ÂÿÉ>6@ŽäxrîÑÊþOö•r$Ç“s¿VN‘à, #9žœµrÜ÷ý #9žœG´r„ÿ“½#@ŽäxrÓÊþOö×r$Ç“óW­œ–UµF€Éñä<­•#üŸªÝäHŽ'çYœ&Âÿ©zUb9qŽ'çy­÷å®ùr$Ç“ó¢VNÁy<@Žäxr^ÖÊþOÕwäHŽ'çU­÷eª?äHŽ'çoZ9Âÿ©Ö(@ŽäxrÞÔÊY%8gÈ‘OÎÛZ9Âÿ©6&@ŽäxrÞÓÊþOµ¥r$Ç“óVŽðªm#9žœurÜ ÂÕ>J,'Îñä|¦•#RVO #9žœ/µr„ÿS=`^Žs<9ßhåÿ§ú¹r$Ç“ó½VN¡àÔ{œãÉù—VŽðmªÔ{œãÉùE+Ç}_âÅ�9’ãÉùC+Gø6Õ? #9®œJIZ9%–U#¨Þ%Ç“SA+Gø65šÈ‘ONE­áÛÔ8=@Žäxrªèä4¾M€5QœãÉ©¦•S 8‹äHŽ'§–VŽðmjŒóqŽ'§ŽVŽðmjì#9žœzZ9·©ñ}€Éñä4ÔÊ)²¬šµäHŽ'§©VŽðmjv #9žœ–Z9·©Ù?@ŽäxrŽÑʾMÍ€µpœãÉ9V+ç€àÜ Gr<9Çiåߦæór$Ç“ÓU'§¹ðmjÌqŽ'çD­÷ÆŠ Gr<9§håôœ‚�9’ãÉ9M+Gø6µzÈ‘OΙZ9·©°.ˆs<9çjåߦւ�9’ãɹ@+g•à< Gr<9kåߦÖÇr$Ç“s™VŽðmjý3@Žäxr®ÐʾMNÍ�9’ãɹJ'§…›¸gb9qŽ'g¨VŽH™36@ŽäxrFhåß&gY€Éñä\£•#|›œ€½¦8Ç“3N+§PpöâOŽvÿ§…ðmrÎwâOÎ Z9·© Gr<9ÚýŸ·©àoÄ9žíþO‹Áé Gr<9ÚýŸ·©=.@Žäxr´û?-„oSûÎ�9’ãÉÑîÿ´¾Mí—ˉs<9ÚýŸ–‚à'Ä9žíþO{1ÖåãžáºrÒW•qª¥Ç9"å–õL–%þ.¶ â}-—> ñLK—{L!³¤ê!cC§^Ó3ç$N¥É¡Ë:•ž¹>lª ®]óæJwS=:•[›C§*È®°©’ òIèTnJ”Ʀê¡S‰ÒØÔ"t*Q›NJx›ú‡NU$RM JüiӒЩV‰T‡N%¼îM/…NU"R}:•ð@6…o,ëÙZ¡S‰ÑøÙÐmà °x¶WèTÂ3öÒЩ Dª‰¡S‰þõì°©’Ü® =º©ž=ºv½:•è_Ï~:U‘emN Jüis^èT«DªN¡S‰þµ9ôüU¡D¤:•è_›o Ý6D‹Ú¼2t;tsøDh]bÄÞürh]=DªBë: Rý:•¨¯çj„Îá*‘ªcè1Jô¯çÎ C1²=72´.ñßsÅ¡=‡‘jUèv(ZÔsá½1<w lª´‘ê·Ð©DnÉ ê€HÕ9t}‰2ÜÒ/tiˆ?m =;¤‰TËæJ]%R…çEëݲ7t*ñß–oCçPÔ×ói¡S©…N%FÑ细N%Æç/ ªD¤º EªÐ}¹‚[÷‡Nå–üÖÐ=Eü÷|èµCšhQ%¡g󴑪ièTbŒ* íé¥õ©F„N%f½’¹¡SŠT„N%FÑ’WB§*©þ:•øÓÖÌЩV‰TíB§ýkkø¹²‡Hz­ êkëÍ¡û²øoë“¡=1ÚlÝzœwu}:•è_/„žõ’V‰T§„NU(R J´Þæ…NU$R…^ݤ¸©¶…ÖU"R}Z—øÓ‹•CëmãÅ¡u‰žòâ5¡S‰qþÅ{B¯ DªBë: Rí Ýzň]šºWŠ–†žR]]'‡NÕC¤ ½*F›Òq¡SŠT¡÷ˆREÿ* =§¤‰T/†N%þTº–SÅUúSè%ìz)+tÿz©Mh]¢¾^ ½¢O)©<=ÜýN#ÀK‹§ÊþƲr» ŽSv„–e=\âݱ°Ëvõs{”óÊ~ýBy¯.,$€5 €|¢"¶H> Ç�r #¹G�y¯Uı©H@Žäl@ È\@îäq@¶ò ÿT‘¤t@�Ò  X‘t PI{�ùJE’¡&çÒs�Èt@þ Èv@öòƒŠ¤TÊ9¥ �2 9€@9§<È‹€@ɧ| ÈaIͤ �9 þ€Œ¤•€¬,M}/ù]E*dÒã�9+� Èl@V�ò 0Tx/�ùMEÒ*ušÖS¹€Läf@Vò4 ¯½2í;I‡‘$½ ­�™(úiúP@¦r+ ² €€¥éߪHF* 9€´�¤ ³�YÈ:@¶²ç+&Rh‡Ï�äJ@Ào©¸�[*Â\PZ]ÅT$³6 àÛdä@žúr&ŒZ•Úco¥a€ÀÜ]éa@ÀöJàGU†þU¹. W½ 2´*0§Tß¯Ê €ìäß*’•H@`¶Êÿ'ëF@ §d= ŒáYƱì4@À£Î†ž’ -!û2@`ΆY8æ¦l˜…³_æ¦ìϹ©*øáUÁ¯ ~xÕ³��È8@ T½ð¬ªB V…ñ¹*øáÕ §Tƒq£Z[@N¤ °Jªþaµ€ü¡"ÕkeX}" 0ŽU/Q‘P_5úªHM(Ÿšà“Ô¼k�^Yóv@Ö�ò< à-×<ŒÆµ`l©ÕÙjõ¼¦ZàKÔ‚’¯¾D-ð%j�Ö29¹€@ æÜÈA@þ¥"µ›r" 0Î×~Ï�‘$·: àéåBÉç^ȵ€Ì<ØÜ-€Àª$Æù:°Š¬SêÜÈË*’3H¬\òæûyo©H>´ù|œkØ|¨ÓºP_u¡.ê>ŒÆua>­’ë5d “yJEꃿÚ�ü„†»T¤qgi²¿�³LÓ7U¤ÙÕ*Ò|ìÖàÛ´†6°Rhså1¯©H[ØGj {_í …·ƒ1¡Œóí`Þi, ÐêÚC ¶‡ÝÂca•t,ÌÂ`¦î�^wÐÞìêx2 ST¤x;`¥Ùé9VÇÁŽÙq3T¤3ìTuÚʰ+ôånPÝÀß8JãXqt‡uSR@À«< æÓ“ .N¾èq§Ü ô÷SaêéÕ€ÀÕë*Òv*zC¯ì #voXÕžë¸Óa$9òsfU@ÀÒs&�ë‚s¡ež {;ç~£"}z^ÊyÐS΃UÿP@›¿�vÚ/€uJ_h½}Á+è +… apÌ;§wÑ3*r1œ¹\ žg¿óT䨩¸äW¹V‘—‚]—Âjý2(±Ë~R‘þ°šx |¤— ½ô Ð2_‚Ö² Ns¶ÁŒ¿ jpÛ €ôZø6èqÛÀGÚ-aÔà6èƒÛ`ïýeX ¿ÜX!¾ óû˰ò}vÃ^†Úy<Ø—á¼`;ø~Ûag{ûryƱWÀSÁãŽF€À ËX!î�K¿Y´±B¯ü'xÂßU�¤) °Zÿ<«ïáüô{ØUûêô˜—�+~„Ùó§J€ÀŠõ'{ÿ•¬Sþ#Û¿`%þ¯ŸUä0ì冹û0Œ?‡aŽ;ü¡ŠüNÖþý> °[ø3œþuú ìü}ùW˜Oƒ]µß`‡á8wøNXþ€QôXüñ‹‚ØV8R!{º@ F8”šÞÀ²œ•âoŸ¹o«Vþº,™Sn‘ãù´‹‰œøÛªÖ;ý‰¿l[Þ>$yã×¶F3$W “RG ^?•ß/ÈÈýq$ËZShù|×Ã9Næ9Eü}#Kµ’¥J'©:ÊT®®—Xª],ùˆs¬L•/þ¾—¦zd’E߬æßùås³æ‘žÉìbe˜ Õéþe˜ Õiþe˜ Õ©þe¸¶³¤ŠÛ~Ô9ò_K÷%kmѤ/£ý+£“hjã-+û$ÿþå¸ßÊþ˜ö/Wé_ú/é¸_¡ô<½±¿ˆgóWµE©–ÅäЗ¥W\«öA»%CÆÄ;½1†ˆ:µÏ¡µ³.Õi­xöœ1²NÝü\Îú²ìQJïæßß±Ö±¯˜Å³ $Ç¿˜–*äLa©æ²¤H‘5¸n¡¯ä«H^,ù1ö«˜HÍJc•üûÖ˜µH+Ù„si¿•hŸF{%¯ÁÇÙܬz$ÕĘäÇٗŬãtÚÿ•ÉiOäL‘Ú“E««Gµ?Ñ™In¯“üÄÎN_Ûgÿ¹vøÄWL;‘×þ×<_í7ù—ü_ç²T䛀Î"™*Wm-Øphªx~6t6äĵŸL´ßùçJ,äåþ½`C?–çùÚ<›rJuç/þuñ$¯ òç~ÿºp¿çKòsº.?Æ9§Øü˪åsÊ“ÂÉI甬ԲۛÙWùÏ)™Y"Ïô õžœøœbk¾Œ`•Õr¾2;¨ó…[†O«>›½!n-¿ÇZ‹;§|Îæ”tüÄ8b¶r*2Dx N†ÌH»¸œ,kc«ÁVq£œ—©'ó'ä”H9¢®œY*ÞãH*KÖòÆnLr.‘üRɼÍçj%󹩾–ókcüÛîåmìWÁt‹|s¡êG‚ÛG´±Ñþm¬J‘ç>´¹rˆßòê²¼öã U[‹sâ·(c˦VÌûzKgÅ&‘ßÜÅÔŠj»-+-GX±Õߊ¬%Bû j…+‡XÑLkE]°Âm½+UÏÊyXí)Î3 ©%×’&’mÙI–Ú“²X{r’ò’#vjL:‘õAQ¢Ig2¤ª@†ª½2iCj¤˜ÖÎæC¬…7‰—óµlᘊϰ}X9—·ÞÍßúJþ>ä˜äfZɇ}%ö—ü\I°O²¥'“¼2NHrü%'He±T·²Tì[«4ÉO©g»_i$UHç}Á5øü&y‘œI½î¤{©äö=2ëfäö­1ÊIÊöÏsIÉØäDr’ýSÕö¯Á’…,?$Ég«Å´%Ú üËyk“<E+™ÏŒ™DrcÉ/ôöÙ<N|T‘qÊGõD©æÞCGõ:®ý‡½W³}Gõœ‡D~è×i=9ÄÿÙ‰º,Õ³¬Û²Ùó ]_,9ÜÄr¸Cü»JäðMÿÖ^%rø>Í¡+‡äp6‡éÃ,‡ïjrh?zÀK‰Ý J:©ìüÛCõ—ˆªMJü¶X}á¯æ™rÜ÷Dzµï?×¥š{S€Éñä¼§•S"8«äHŽ'Gûþs}á#å> Gr<9Ú÷ŸÛ»mõÓØ»jð~ø»jÙŸÁmJQmÉ÷ß5©ÄÿKx­Úc"??þ!ïˆY©ug”#ÖH@F•‹uzÐ{dI=Ês`Q+¼ŸÿW÷Èìdõ<×NVÏÖíd5¶ÐNVãÕíd5ÞÏNQÏ’l¸­c§¨·½ìõ¶—¢F?ÚpïÆNUOOìÔ@Ô¨;U=õ°á6Š]á\@Ô¸\;Mª²ÓÔS;]½õc§«Ñ˜vºol§«§xvú%€¨§0vº±i§/DØ´Ó¡Ó¡œÓÕøy;ÃDà²3 ä3Ô›ev†“cg€w¢žÔØj¼¡ö;Cl´+Ö�D`·+ª±ßvE5òÁ†[vŵ€¨§®vEõ Ñ®¨Æ®Ø™ê`gB9gªQCv¦Åmgª7øìÌ"@ œ3Õ(/;s3 Pò™jT•©ÆoØ•R�QãÇìJ0FU‚1ª’z¿Ò®¤FîÙ•Ô“G»’lWRc€íJê-»Òß�ù5R×® #@e*CO©|: 0ÚTV#LìÊêɵ]Y©°+Cß©¬F-Ú•Õó\»2Ô`5Ò®½©ŠŸ`WéˆocW¶ZE³«,DÕ·«€¥UÔÓm» ´Õ*ê½¥F#ØYj\·¥FÜÙYjä•u êm&; zeŒáY0†g©&vÖ€¨Qåv´Õlõ>£­F#ØÙ0Úd«÷ílÕ³ÕXt;[½½hg«ñ$v¶+bgCÌVciìl5¦Ë® -³*Œ-UÁŠªÐ㪪w|ìª0[UUï.ÙUÕX>»ªUnW…¹©š³dWƒÙ³šCeWSïÝØÕÔ› v5õæ‚]]©°««Q^vu5>Ê®>52Ö®~? àGUWoßÛÕT¸ùb×P#~íà1Ö„ž[<Ïš0Ô„¹»¦Mgׄ2„;,v-5ÆÌ®¥FÊÙµÔH9»–zÇЮ¥ÆÊÚµî�fêZêmn»Ö»€¨÷íÕs`^ÎQoØ9ê=_;G½Ñlç¨ñ6vôërÔ{ vŽz7ÇÎQo@ØpƒÆ†4vm5~Þ®­Æ†ÙµÕv»6xæµÕ(A»¶Qf׆y¹6øZµÕ;³vmõF†]j°6Ô`.xV¹jÔ½ ^n®QoçªÑbv®z;ÆÎU#÷ì\õþ©]G}_®ãXháu …ç©’vô¯<5ÆÕ΃‘$fê|˜©óa¦Î‡Ö›^e>Œ$ùjd¾3u>”a¾½fç«ñÆv]˜©ëÂL]W½ßd×…5c]ð7êÞøÆu¡Ö…>Xü¨z0£Õ¯ ŒáõÔ<»�ƨðÇ    . Ô[Ïvz³Ì.P#?íú0§Ô‡’¯¯ÞK²ëüS¼ÜúPõÕÈF»þ@ äë«wúìúà]4€>Ø�v`¨¯mØ Ô˜d»x; `&j�m¾Án@À‹k�‘– Á'i¨Þ“²ªQÁvC(Õ†jT¹Ýü±†ê­^»Q'@Ôjv#õ¥»‘z3Ñn ãOcð¬ƒoÓX½d7þ^Eš€ä&jÔ½ÝÖïMa¾h >IS°´)xzMQ2¬AšÁZ¸™ú¶ÝL½+m7O¦¥z'Ân;0­ÁC;VÙÇ@¯<Zx[ÜÆÞcŸT‘N0&tRïIÙ`î>zegX%u†–y<ì^>ÿñêM» ì#uQ« ´±®à5u…ÝÝ®0Wv…qµ«úîŠÝ |›n°çph?v&»ÃŽYwðº»Ãy¢‹nŸ¨Þ˶OìDõ• »ôå“`·nÌÙ§@É÷Ÿ¤'ÌD§‚ŸyªzSÉîk´^`ûéê½ ûtðNOWãðí3ª¨È™ê tûÌóUä,XÝœ¾ß9°{y츞 'ç‚wq.ìóŸ«Þ‚´û€guØužzóÅ>F¿óA×ù ëؾ@½%a÷…ûâÁ€@˼¼Ü~ê]3»ìt]ãá%0 _ «¿KaϪ?ôþpZѼ‚þÐV/‡¹àrI.‡µù�Ø3¿B}m̾ö¯€½ÁBð Á¹Æç0Ë „3 «`¤½ ZÔUê-u{ñ%ÊN ë°{%y'»ƒ‚eU»Ù=Ïþ®,Þ+äùዉyE¢P,vF?Ø{Ã-vî,añ É$›ñÈÏ|‘ŠÄGeYI<¾®5Óõ áÄã.ÀüŽÈF+èéþ-Bó*aû=þ¶‹²±7§¶»rˆíWøÚÞ=n»g­=ø †¤äB†¤ ä*†Têt˧ˆ–ûMbûý:ÛS ÛGQÛë¹q'ý…íøÛîºSÛ]9ÄöGµ¶'‰T‚íÝÁöÍ`ûvÚZRx>†º ¬*®é2`cÙë§Ä­(çÌŽÅT‹}ÿpºCpÜ3ŽØ­öÚ|;[· Ê%“HŒ2Nú@—ãI^_.™sÞœœoÝ÷ôsMsßÁ ถþ;€ÓYèº+€ÓÇ{‹<1g˜óU�§È²òÒ8Ë„œÍæÕàl·¬Š—'â¸ï 9ÍH-[µºÑŠÔòpŒ(‚‚!QE¢ ŠD‰"((EPP$Š  HAA‘(‚‚"QE¢ ŠD‰"((EPP$Š  HAA(‚‚!QEˆW¶?–wÛ7Þ$þÝlYÕ½w¨ª½Z– Ï {#Àb"‡ì7²èÝ7æ»Ä.âÕ»¼ÑœnY+§3¤¢àüG„®=l߸#êœÃîëÊÔ®‚Ñ–w¾ú ìÚlÙCÒ™]‡™]] íJrš0$E Ç1$ƒß¬/³tÈ©ÔÒÚyÌÒi:Kk÷–ÞM-­ÿ„°T仆ío©“)t±\9ÄÒ¨ËòîÑ+–Ö»ò2Š!uÀÒ<ÕÒ:Õ™¥ÕYZ§‡HøWz¶ÕÐ}‹á°´’¿¥Én]L¦–ºrˆ¥¯j-ÍKë9K"JcÈ_ÔÒàgu~evio­æ5ê^¦5Øðño‰°«v»¾ÚŸ¤v¹rˆ]%Z»òµv½Ê<°4•שÐUÊì*ÕÚuÈ}½œÚÕȽ£¹RØÕÔß®”µBׇ̮CÌ.ͽڲ’ê¨}P±TØ5´ªÚVó÷R*N]uŒÚ†¶Õü¾ÌöC:ÛóEò¼o˜í®9¢Õ8ÖßöÔÞB× Ôö|~yXk»›ÃKlOÛá<.³ëgÔU`4s—‰ïçf-¡gvv9‡ŸÙ¹œü”Ø™]ò1œŸÙÕ:]pÜ“byg¶ÙºrDÞ™m¶)†ŒŠ!»³_å4¨ršN1pŠ9G”ØNzºT¡m1:]bHtºD‘èt‰"ÑéE¢Ó%ŠD§K‰N—(.Q$:]¢HtºD‘èt‰"ÑéE¢Ó%ŠD§K‰N—(.$:]bHtºD‘è~.E¢û¹‰îç2$ºŸK‘è~.E¢û¹‰îçR$ºŸK‘ÿe÷sÝÃüëØ¹ð^Ë;C®ù{.\£ü[-o3Ôó39ä\¸EÂÄÛT„x›t5Þ¦îBv.|*êœ ®¹Ô®Æ÷‰L÷v}•À®C®"j—+‡Øu†¡]ÉBÎ} ùâmêíd–Æof–ÖûUXzµ´É§ÂÒ•ÂÒû[ê´9ü+µÔ•C,½ÍÜRÅ.÷dÿ-†¤ƒ¥ª¥õ‡1K?ÓYZ™°t3·iæÞ%ïkYµ²ý-MÚ)òóµÔ•C^S×ÄkYºûÔùñ6yj CƒVìzj\‘ýVì.yƒþô¾¹ÍcºbœYLN‘ó¾ç=ɹéê¦ÕeÂÙÌÞ–çœòújð•(4oÓì'Ám$꫹}¥Xö°$Z_®R_ÍQ—¥«¯:BNÝ€Låm5Ëj8È÷Å€Älo¸Œ•O+]ù4Üì[ï?p¾—ö…›«a8‡X}iÆç,«Q–(¢C´¾šßmy_+¨uœ}¥öåÜÖ—+‡ÔWOÔeyõ5,(FHô¦aÃÕ Õh ³]ûVC£õ"K¨í-ˆÏ¶Ÿ’Àöƒ"‡˜íë™í´¶‹[`{ ØñQØì`¢®« ‡øK‡ÄñQÙëY|Tç2r9uÄâ£j•Ϲ<>*çkÁq}uù¦Á#i<’µ«ŒÅ#1$ŠG¢HD‘(‰"Q<E¢x$ŠDñH‰â‘(Å#Q$ŠG¢HD‘(‰"Q<E¢x$ŠDñH‰â‘Å#1$ŠG¢HD‘(‰ Q<C¢x$ŠDñH‰â‘(Å#Q$ŠG¢Èÿ®x$ïİî­ìv¸øw’eå|âžÃÖ*ÀÁx¤aÞ®Úb"'~ë\bÑzëùlô½”aäí‘,{mzÆê @9‚Ó_$|€åÙ¡êçˆúçYHö.ͳ+‡äy ož¿g9\Èr8H—ªÎå"‡®g%_®8vo9"_“ˆ#±'Žý8?«œUUN‘œNÀéœýÀù‚s²¬”"zš\yC1:MfHtšL‘è4™"Ñi2E¢ÓdŠD§É‰N“)&S$:M¦HtšL‘è4™"Ñi2E¢ÓdŠD§É‰N“)&$:MfHtšL‘è4™"Ñi2A¢Ód†D§É‰N“)&S$:M¦HtšL‘ÿU§ÉeÈ0ù¯†Qt8Œ?Ãa/n8èûlÃa\Aæî²Ìz5éIqËTËr -+÷/îIqο˒áéöo¶ZLä·FXô‡œðJžÞ2q%CrâÕ2}ƒbÄ"†¸œ• ©-¿2$_ /3$W ï2ĵâs†¸¯%ä3¤–àüG²¬ üí·u*¥Ú˜¾ Ѧªàþ 2ñ¢©ŠR·GV¤¥êÊ!çï=´¥ZG¤:ž•a>±$ËJëÁîõ§“Ø»ŸÖ“ÞÙ· ˜®§·¯œ]’Óß@ÎH_9{$ç!9k}å¼#9%ÁrÒÇ09­ˆœ}’SÄä´ÒÊYÆ"4½@pöˆ¶q&mLjš²жñ‰ÛHî+jùÚ6\9¤m\‹º,£¶‘ÑÊ· ¿ˆÙ•Ñ–Ù^Mg{F_9_KNo9ý|åülÀ9lÀùIrưüÉ g•]«}óó»äl –S±ˆµ±ÅŒSÞÆ* 9õFÓQ½Ý~1üž%Dcÿ6–ÚY´–Ñ´¹rH»uYºwN2„œlMÈ*†¤©ãj¥<V>ä;>Nå˜í•ú±òáßú‰qø&µˆœšœê’³êrÖê2àT¶˜®ÚDW]Nžää1]hu™p ˜®,¢«‰§‘ä´bºîÓê2áôgºª]m 8­$‡õSgµV— g»ß8N'NÉÙÅt-Óê2åH]y»ÆénÀé&9ï0]×iu™räø“4ŸqÊÇŸ*Ébü™EÇŸî;KâÔiï?þ¤‹#×ÑñÇ•’L_�« ä”0$Eû½6þRÖ‘j¯‹ÜÓ~Yªýâ^–kébjiǧ„¥Â½®s¢¿¥®¥SK³¸¥ÿô³t”¥úÏøJÏàͪ¥‰_(ʲ²QK“Ûë,Í^",]M-=î¾²>TçBK3³DžkPK]9qK“ µ– ŸÔqª¥J KGõV-u¡:Êæ· 'ª«€Qó"f´QK’)ûÏ_Õ6²1áñ¸xgt¬ïTÛÉæ/±dÿªžLë"å,Ÿ«ž;óíOü†Rœã¾$ê¨,/ŒSå�}gÉZ_Æáï,¹œzÏK9åqËü¥:¢ýÔ{çß3cï,YÕgÑÈX»YFÆ–qêíw9žäå’§ÆJÁù*G £ç7ª½ÆZ¦}‰N»Ë)¨(%FÉV;ó}¥–sv†žãɹËY8ÝÓ3ïíÜÕ)Gdtq×[bH,¹ëà<œOUN·*§Û*ç„Ýœ“e5nDK,y.–XùÏÿ³HæQpn> âÙFÁ¹ð(8§ìÛŒ†sóѰßrÍ€ÀÞ5p¦pÍ@`—ï8·ºv¤¯K¯}ã1p~:vùÆ@|Ô°tÌe€Œd °39Î|Ç€cà4ùZا½¶ mx-œ _ g[×B´Æµ«�ó¸ká´k,œ¬…sê±a2"LÆBÚX(ç±p¦0vùÆB¤÷X8s å<ÎSÆA4Ý8(ùqp¢1î @à¤oD´Ž+d9 p+aœìƒ¨q0ŒƒýÕñ7ö–ÇCdÚø3ó‹ñpv3,–ŽKÇÃÙèø½€À‰ÏxØ£¾®  uœ/_QC×A<öu°ÿ|D}\Ѽ×Ý D>\;ö×A$áu0úM€ö<¡ ·<ζ&Àí˜ Ñ1Î&,¢k&@t͈±Ÿ�§9à´t"Œó!l"ÄòM„1s"ÄL„ȉÐz'®N4&ÂÙDù'‚]“`¦ž·~&Áø3 nôL‚úš1o“æ-sœžLÚ¥"×§’ÄL^ 0{^ñc×ÃYÿdðd&·N]'ÃéÛdðR&ÃyÜdˆGš ј“!VöIn€HŒ w ÄNù} ´Ã)­1⑦@N…¸¯©p~:æî©p:bȧ‚¥S!Š{*ø~SUoÙž g‘Sá\xܘ›ñ Ó ÂZo@`îžcÂ4ˆ+˜·Q¦Á˜0 NɧA Ÿö% Т¦g7§CL×t˜»§C\Üt8£Ÿ£Ät¨Áépâ<Úótˆ'™£_Q 0’AÜEÄ]/Q‘ùEà Þ±O7‚·|#ÜÄ™w|f@œÞ ˜gÀ ¬0Îøhó3¡ÍÏßf&ôå™Ð—gB_ž ±3!ro&¬OgÂút&ø-3Áo™e~ø¬æ€À¬7 Ö;³ Âv6xD³Á#š}) à½Ï~TEŠaý^ >I1ø™ÅPÅ0þÚºâÕ‹á6J1D-ƒÿ\ Qs EͱeDÍ9›:bÈç€4Ö2s!?saTŸ 3ã\X›Ï…`ø-óàfâ<ðýæAÜò<˜açÊc>Ì2óg7øÀ8¿�ZæX /€uÓ¸»$/„H§…W©È"èÝ7AßY~ÝÍyu3¬Fo8Ï[Aò­°—²Ö)K!ûðŸ—]Ë`c9øáËaÖ[cø è§+`M´v*î„uÊ!¹ö¬V¾•ÓTä.ˆ*¿ æÊ»À³º¼Á»ó™©"÷À>À*ˆÔ½Êç>è÷Ãy?Ì2«!šn5ÌËöü Œ‡¡ .‚½Á‡a.x¬XûkÀ'Y{ àU> {2ÂêïQ…÷ƒÅÇ  ƒ;kÁ}™õ0¶¬‡9åqÇž€Õú0;<9èqO‚ÏöÌzO¿ñ4ìC>½_E6Âjk#Do‚³MÐO7Á-¿Mà×= 7ž…¥goW‘ÍPËÏÁÌøôÁç`¶Ú··€gþ<ôÁ’ç�9¥⨷º{+D¿�»»/Àh\ ~Â6¨÷ípÃúØÛyÚóˆ¬Þ#ÛŽŸTäUX%½ 1¥;¡Õíÿy'܆{ n¼óéëÐZ^‡:ýx§»au³æ‚7aÖ{æ”7¡%¼ ³ùXí}¿·à••·Ô§¹ºtš«»ŽN µ:-Õ‘Öi¥î™;­Ô=F§µºKãÜ’—«c‹³\½çâ¬PírV¨kg…º–qVtDm½Î u„tV¨{zÎ uËY¡Ž¢Î u¿ÅY¡îk9+Ôvg…ZËÎjßqîTÛ¼s§z ɹSÝysî$9,;u-8ƒÆTt¹¯ìô5²SQgBy²²_¿9Þ¸±˜È!1,ú‹©H©¼˜“XÄBcÍ›Obo‰’7žK9I+ g³ÌOšüu\²åÆf8wþJu5iE#’îa9ü%§-Ës’ŸmœRÉéLu%Ÿ¬ÕeÂÆt‘ˆM{·GFM7ÙÉÊù)Ây=ÆiZÈÊç]~Žg•_´ª“iÀÉœ Ì®b×¾0í§i Ës©6Ï, .i+ÑõQ]ÍÆëjÆ"Æ)ljsRNr6ÉÏ2?!?õ ?ìë~É5´ùaÑÅÉ$BÒþ6”®‡ t­eºHth,²ÚLWóžÁº<N©Žo΢ʓkjåœÅää92úºy&'G+g$ãäúr´ºœdÉ™d kkc¤Ÿ:i’³Ð@ΑäÄ£{1NydZÕoYDY¿²?òˆ2—SÐ71%fº2›XDY½%jÄT÷îjS÷¿«‘N'f¨œg‡Ê9©‰ Õ|-‹†*·"ІbH E‘(Š"Q4E¢h(ŠDÑP‰¢¡(ECQ$І¢H E‘(Š Q4C¢h(ŠDÑP‰¢¡(EC$ІbH E(Š!Q4A¢h(ŠDÑP‰¢¡(ECQ$І¢H E(Š#ÿ'¢¡ÜSׂa4ê„ýe§¯õºÑPù³Ë“•ý"ÑP+½ñp1‘C¢¡Î²èO,*C†j¾Ç7ê9©«‚ЕGSµÈñz–¦ Š9i±28ö©ÅjÆÙ­åìó{)ËÞaÀÙîoiËn¾J2òªåæàÈ¢#Æ9ì}TÉ€SÑßÒV™¾1Ko%ˆoIUë´Uõà8¢V˜®ˆ®÷BézÈ@×C~1K±XšòÒhÆ$—úF1LU•9ÜíÓêß(¦/Bé:` ëÓE£ò¾ £«uQ°.£búArfG1µfј,ŠI¾{Ùzap$OëRÆiâˉëª'8V˜‘­5¯w"‡èÚÉZ&ýâS­÷ ­ÙÎ"¦Òý[xëƒ%v$9ñªË§<†ªZŸà*—S06CU¯|Rå1Tõ7©1T'±O‰ÅG483Y圜¡rNn�œ€³Eåœr©Ê9e¬ÊééXJ¼V›d¯Õ­ŒÅk1$Š×¢H¯E‘(^‹"Q¼E¢x-ŠDñZ‰âµ(ÅkQ$Š×¢H¯E(^‹!Q¼E¢x-ŠDñZ‰âµÅk1$Š×"H¯Å(^‹ Q¼E¢x-ŠDñZ‰âµ(ÅkQ$Š×"H¯Å‘ÿñZî©kA1×:©}Ùéký§Üx­zw•'+ûEãµ¼uîb"‡Äk]cÑŸß'ÉcÇ ±8+ÁIcçï\NŒ“ã+çEÎVÉÉ3ÐeÂ)ðÕõ†ço’þÈâÐ^cï†ñh j´Æ1VpÚ1ÉÌ®Vÿ1‡—sÉóûœ÷$‡¿“C#ÓÞqÚ¦G´Í γ§À/ÏN’ÇæØH‹ÊcÑYŸ'ˆbÊTë½-CIÅÔ¶¯otÖ7¡tõ3ÐÅÚ3{cêÇPº~0ÐõƒotÖ¿%çpptVÛ_}£³~‹qÚYÁ1Bír‚ߘj—Ü8m}ÛacNCÉaïtјÉX4¦Us0þ´;+8ޱû²3™tª…ÒÕß@W¡ß›rNí0ºÜOjéò8ú7åò%'3xó8ú(¸ú’SÜ('ó¦ùRyUm=‹ySöG%æMp nŽÅ¼Õ/çyÌ[£Xl˜Œyë)_÷ H,ƬgwàtÎEÀ¹8£óÊ9Þ(;µp¨œ^CTN¯i*ç´\5.®}—VFŒââÅÅQ$Š‹£HG‘(.Ž"Q\E¢¸8ŠDqq‰ââ(ÅÅQ$Š‹#HÇ(.Ž"Q\E¢¸8ŠDqq‰ââÅÅ$Š‹cHG(.Ž"Q\E¢¸8ŠDqq‰ââ(ÅÅ$Š‹ãÈÿ‰¸8÷Ôµà׳rÙékÃßݸ¸úå'òš¸¸Ó\9‹‰w•E~#}lØ)9U}áª=ÿò—;µ?ËWòËœ—hïc Ý„Ó×Wû.Îë rØÏ@» §¿¯ö 8ï'Èá2ßx¿÷ÂÄ¥Ëb„ôñ~Ç6 ~wΈәYZäùçSÿÒ8v«‹«µÚM8Sü´;Õ 8U俽Fcÿ“Ü¡YplÇÑÇöd1íì…«ü+Š4JðgšJi-jkéÀ{¥6š®Ü#Q‚äÝ0]…ºØ—UénNj(]ßèúÖ/J0þµÓ<ÚP%ؽIȾYYr~ ŽªêXü†Ûãtðmó§pNòïÙ7ˆYÜ`=™*j0M­Áެeêãë:úÆ 6¥ë ®ƒ¾±|mýûiG‰JßÜk¦=wâ‘ðÚ7÷Œ9ú6ß)Ì,Ó)+¸yý€]$''x„ì”ã;Bžè_òš÷¸#ʉÇCò™¨<²zAp<¤Ë)x<)|Ë2+Y<dã§x<¤ÐÞ›ÆZåß bÅR$Š ¤HH‘(&"QL E¢˜@ŠD1‰b)ÅR$Š $HÈ(&"QL E¢˜@ŠD1‰bÅ$Š dHH(&"QL E¢˜@ŠD1‰b)Å$Š äÈÿ‰˜@÷Ôµ` <Í´è`YŸpc•¿ª¤‰ ô|ãÅDN<&Ðù‹Ebß6M©¼Ù|Vì/É™Á,fˆ{&¾"Ž]ýè9µó ê*°ª/açÔ½Ëþ¨œS NÁ[±sjao™a윺é“pN=…½]ók/:§fHtNM‘蜚"Ñ95E¢sjŠDçԉΩ)SS$:§¦HtNM‘蜚 Ñ95C¢sjŠDçԉΩ)S$:§fHtNM蜚!Ñ95A¢sjŠDçԉΩ)SS$:§¦HtNM蜚#ÿ7Ω§XVÁ×ìœúŒ²Óצ»çÔŸ.OVö‹žS{žðb"'~Nt¹EbçÔD*Ï‹›ÿ[à:ó].'µŸˆÝ°îÌÞsˆ}2Å"yβŽç_”DälLp·òs|&Ó5L«‹Ålѵ%”®<]L×`¢«4”®]%LáØ;|uu4ÐÅ^T°N!º¾º*е“éJSoúw±˜œ'‰œ= ʧ®šç.ìýë]ž»Tgºè«/¥+Ï@W#¦kÑu ”®Íº63]„c”œ&g“VN)“³™È‘/uÙÎälÖÉéj1Î-'é"/Ù߆i?]Û2]Ú—ŽºòWŒî$º~öå9ìÝ:Ž‘w{LòÜ×  û²W)Ú0N,Úç�‹ö)*û£í#8¿Æ¢}ĬáýðhŸŽ¢|êWÿƒ|¥ëÌñÖÔ#Ë‘³»ÇدÎ>8·�çn•sN/•sΖríqŽD$g/pöªœs7©œ8"9»³[åôqÔ<÷¨æ¹Ï§ªœ8ãœ×E•sÞ UÎy+€ó³Ê9¬Ê9¾Ê¹ ½Ê¹`2pnÎw*§o/•Ów1pöªœ 몜 ‡�gp¶©œ‹*«œ‹š�§Xå\ å|1”óŨœ~©*§ßÉÀrî·[å\’«r.¹8g4p^�Îw*çÒ.*çÒáÀœÀÙ«r.KU9—œM*§¿£rúçuà|ªr.‡1árè—¤r@} €ú�m,ŽHÎ~•sE{•sÔéwçuà€íWü]7âˆä@ùÄ‘§0CåÖNUWaUWá×ÀùZå\™ªrâˆä´ÈuŒ#É98'çRеtA„qc`®*gàEªœAÎó”*窪œ«†«vÅÉ™œÀ1êª'Ôü ‚~:úé ˜/A¿tpîÎ65ÏqDrög¿Ê u1ø µ Cÿ ó×àbS r`Ž >Éà»AÎS çUÎ UΚªœ!]T9C`|ãó |›!ð†B? ýthUUÎÐ §ÈérÎÎX•3 ê=ŽHN{à´W9WC9ÇÉŸäj˜/®†±îjë†Cÿýkø4UN‘œ¥*gDe•3¢»ªkø6# ­ŽÛGžœóml$´±‘P>#¡|FA¶_ óר6ª]c{©râˆä€ß2vÈy80fŽ»ÆA¿m~´ùq—ªºÆÁ˜9±²*gbUÎÄîÀ¹iâhàŒŒ‡oÎ5Ï“`›ÔE•3 êbŒc“v€œ@ÎϪœë«ªœ8"9-€uq=ø qDrÆÚêõÐOãˆä¬ø$×ú`2ø“ÏPåL†: u:êtò•sC†Ê¹|¿8"9Pï7 �ŒqDr«œ)P_S`¾˜kó)ûó“Ê™ ýkjwà\(ç©PÎS×�çàü®r¦Á86m‡Ê™m~:´ùé0þL‡2œî–áÆÙC$ç óÈùI•scMUÎÐ~n„µÃ]@Nås#Œ-7žrÎ9@´Õ‡ƒœªœCT93 ΄~:s p&g2pn´±Y*gôåY0¿Ïœ-Àv8ëg•3ûd•3ÆÃÙ÷öÙfC?-vwÞ¥œâÊ1DrÀ?,þäü®Ê™“¡Ê™þáð‘æ4�9m@Œ‡s¡ŸÎ­«Ê™ÛB•3úò\èËsO9瀘ãæÂü5w>ÈY rî90ÍÝr¶Ø;#’³ä|r~9Ð~æAû™íg´Ÿy0ÎÏ› rŠAôÓy0çλä¬9°WGbœP>  |Àø¼ðwu[ãÆ"7ÕTå,‚q~Œó‹ÀOXÔä´9ÐwÁZxQ/Ó äÀX·Ö2‹€œ'@εœíPËyÑ^UÎⱪœÅÓT9‹ç«r/UåÜÔ]•sÓªœ›.RåÜ4ä| r¾9¿«r–d¨r–4På,i£ÊYÒä@}-ùšËɲºN¢·Â»—ŸdE·ÂÝ §Ht+œ"Ñ­pŠD·Â)Ý §È@¢[á‰n…S$ºNèV8C¢[á‰n…S$ºNð£[á ‰n…S$ºN‘èV8E¢[á‰n…3$ºNèV8E¢[á‰n…3$ºNèV8E¢[á9š·ÂWªÞ©³ZmQÎpw{ƒZ_ÎóªOâ¼ î™;/VDí)΋ÃRÕrJÕöã¼¤ŽÆÎKרÈ6uçßÙ¦Ö ³í_*²]]i:;Ô½8ç5¸9þ7uÌtvµDõvœ]êÞ…³ îÈïºÕ‡tÞP÷mœ7TÒyã;Ù­ú�În¨Ýªßâì†W Ç9oÖêâMõÌÎyS݇tÞR=ç-uLpÞRG6g/ô¸½ê¹ž³WõE½=�Qwiœ½ê^¥³Zï^u<tÞVO4œ·¡|ÞVÏ/œ·Õsçmu¯ÉyG=×sÞ…–ùwu=è¼ï9¼§žï8ûÔg™SÊâ Ôwß=Qþî{#ÕšbY-÷݃æ-Ê“•ý"wÛ÷ycïb"'þîµÂ¢?ä}þ}Þ}Ÿß~†¤ ÄÛ˜Oïìû=Ž]»è]k꜄]Ç0»ö A®¦ ìšb9ïWdvýÀìzZkW=‘ª³¢®@NfH Øž*8}TÛßÈlw‘±Ôöú°›Ñµã™I»Ñœ€3’Þž*ç&±ÛÓc¶ÿ‚3‹å§ÉÏ8™Ÿ1ÂÒé®öcÚW2É=¨`)y=“| ‘<Qr629—hå”øÊ¹Ar¶È9@[¦]…qÊ[f÷ê¢ežM[æ9ç[VÆËjµÃ¿e:+Eù, -Ó•C^Šº,¯Ç½ÿkcyÙÄ|ì„ÖûQº&±òÉ"å³ f{÷)´|’&èʧ;o ÙDέœ›%g.Ó5Q«Ë„³ÌW×rÚSÞÿžõ”I–³ß¢mõÄdÖ6¾×é:1Ó· ï5àÜ#9YÁålÄÉñµýa΃’“\ÎFœ1L×OqBJÝPu1…É™Mòü´ÔåÏy2Ìyâ*VïK´vpzl¤ùIºŠäg«çy™gƒÙ³{ç„Ê!ù1á`eØ”äçUÎ+aÊù$Ö¿’éòcÄÉò­÷· 8o†Ês‡àz7â²ü|KòÿGÌžû+Ñ^pÒ –ª5I%_˜IÀùÔ€sЀó‘ä,dµs«ÖÒÿiœC¾­÷ÎwaZËÉ™Á­×ˆ“ãÛz7àü*ÏÝ‚[¯g¤_&§pRCåy‰Ašp6ú槪'+Tž÷äÇ€sJ²o~ò 8¹aò|J+ƒü˜p:øæ§£ÌOšýr˜vÿTÍ $7 ei?+L8ƒüzeò±œv¡ò<7¸WqV1Î@-ç!–çoHž»È<W5Ø„åp“üVò;L2ñr“Od’;1É?0ÉSt’{¦ù¶Ó 8§…©‹ž½ÀˆÓÍ·m\`À9/Tž ƒÛ†g¼o^nÀ¹,Tž—”¡ g½o~†p‡Êóƒü˜p¾õ­÷k 8ׄÉó©Õƒë݈ÓÈ· o0à\*ϽƒËЈ3Æ· gpf†YÇz›AšpVû–áMœE¡Êy»Apzù½Ë 8w„És/ƒ±×ˆ3Å7?÷pî •çUù1á¬õm«p •ç]ÁmÕ„sšå[†OpBíÿœÖ(¸ 8ƒ|ó³Õ€ó|¨<Ï5È g½o½¿jÀy%ÌXwÚƒz7áô-÷ 8¡öz§—¡‡¿¥ü ÉO|‡ö¤úÖ@òל¯ 8_HN+ÆN8‡ 8r?ª7{…Ø>Y[bG“Óíqø«ÈÇh9½Yù´‹Rj%h þ©jH®fÀÉ6àT‘œ~Œs á¤p’$§•j[m‰™rJuœ”JœŠ’3Œéê­ÕõßÌ)ÕqRòü×ø½ùÙñµZɦœø Þý§üÔµ·ð` ´È©ka/‘;1þ¶¹×ÿÔµR+‘ç^ôÔÕ•?uMŒº,ïDuÿ@v¢ZO ãè‰êéká¤Ä.–%–+RMg³Lž@Ò2L §C¬|pÚK;=I­+g#÷º+]Ý 8]$‡íu'kë݈ÃWˆäD>¥§çdÉa~ò8­.N[¦«Ñu–ç Éa'GÉÓµºL8}u]hÀ¹@¶U8‡=}¤ö#Åá{D6Éánή€w?ëqþr&èš” ÄüS4<<ŒG}:ÛéRƱ_BpJØÈOö`S&pƇÊ3ûZŠ­j0áœÁvrì¿þ)N©Ž“2ÀS$9gè2áð/5&ºæpæJ;­Hž§ÕeÂa^ÕœèºÅ€³DrØ.V²v•dÄá«Ñ–DW|w¥®è•ëi«KêÉw…iág°5lòMZ+L8ï¯&Îàë\ê«oacÔ³tŒJjƒä' 8ë 8ëbœ3Ù :ùv¥Fœ¬#ÄákáN$Ï› 8›$'‡éâñ´a8ü J´îÿt!ùÙfÀ)•œf,?wku™p¸ÿsÑõºg§äpÿç^­.› ¬ÿc¢ì'¤þhÀù>Œ—’@Nš®T60/åÌUlF{8ž*%þm&wb;KÅNlRÎ)r?*çkÎW¹u&;e°õõnÂÙs„8ì,Û¾äù°ç'Éaq ”Ct™pp¾bù™'¤&pœ03ãYlì¥râù1â°5šÞ2âð“ôzÄ®Ë 8—ø÷¯©Zø÷¯©š䧉§‘§äp?“ìå¦pêJNVÚ½ÓÿZŸíBR>í 8m%‡EæSÑeÂ)bºÈ—CS»p:K ‡è2á`ºH”rêÅ zŠª $_`À9Ï€s®çlΙ’sˆ•˜6fÛˆóíÿqŽ~Dã¿&Jj´ä‘œáœa’s˜ù¢“´–þOä”ê8©“ 8r¯éìä`]Æœ¸®³‰®» 8+ýç÷©VøZ R-3ÈÏRÎmœ[ 8K 8‹%‡íáÛÚºˆcÀÑžN¦>`ÀY-9 t™rôõþ¢ÿ(š ÕVÉÏpž3à<+9ÜË¥#í3kt6;Ù×{zÿµœ³Xùt%ås(AKðOõ©äƒœ 8 8{ 8{$‡}:ùm‰™r´§í©û 8û$‡¯A´'ûÿÕýxøOÎ7 æ÷YÌ»¨¯Õ~´9ñèˆÕŒSqöZËj0Á"ÑCç‹T¢äÚvðލÚAؾFG¸râÑös¨Ëòv{öÿ¤Þ7ÿ U½“®DP¤ NM†ÌHÓxÉ[ndƒóAO¶T$¾Œõu‹F°WÆ3\!»òI>oH\AyG,'Kr°öü*+Õ0~žrÑ•kÀɉqÎïLu9utºŒ8ì Ñ^ªå¬bµCÆÃ´¹®vÎç·‡Î"–Ê;b 8M%‡í¦:iZ+ 8ð“â<¢«]|pµâ>ït#©d|KÎÉœœî’Ã#؉GT¡›Gî#]ÀF6½wñ_ËYè;&ô1àœCûEоúëƒÇ #N ËÏS$?—Êüä‰Ö;Ži÷OÕõîYªRÖ›ÆjócÂÙî«}d‚<û§ž Ï{XªûI*¹o“€3DrøÉ>á» 8}ÙºÒZûs |ÛáÎø0mµoÏàvhÄáQÊ‹I~âsJšZï Rû×{ß~¾u:KæÇŸ#ãˆúö7¨S΃:5áðaO<ß"KÂ2ôOµ$A>Äú²6îÔˆ³ÞWûêyöOu_‚<oö­ÓU2?þ¹ÇØ·Ä NM8l÷ÛzäOqJuœ ëüÇÌ©Ö&(ÃÃíЄó+Ó>Ÿhߘ ÞýS=åŸç -Ö{éòcÄIöÕ¾Ã?Ï RmOç,ßv(ãpdüÏ…ÕƒÛ¡§wpsJuœ »X.bóŽ[>+YùôasÊuZ]l”°iÉï £ë¢B¦‹È‰ëò8q]']ï3]k™.w½‘!RÊ´³wŸ¨d¢Z‰ö˜ö]TòÅ«/’*.ùbþ^ õ¾2àÄ_CJ ö%.æ10Z?ÁˆÃ×Ë$î´ÂOþ¾D‚T?ø÷Ó‹Ùøìœ®Í)'®Äö¤Yþc¸ª ¿'Èó¯¬ÞµqDÆ}ž3üÇéÒüóÜ/-8?ƽöêþyNªj‚<W7È ‡Ýõ°kíñf`"Aª¦’ppêK‹–wÞÐZjÂéw„8l'™F«¦cÀi-9…lDú·Vבâ°w*lòŠlÚE Z‚ª¾’Ï7àô1àœcÀéiÀ‘ûcýØ=Dç-m‰™r´ç2igpäý¯~E¬µgÿÕí¹LÚ@N¡g€ä°½[{Þ}Ä8Øð3ÉŒ#º„ÏÎHŽŒ#º¤µÕguù1â bvej9üí>ò:nÚ$΄0~Ý%lïÂyL›ÎJÖ6 H~¦HÎZf{V{ÅN"rf„Y\Ê=óñ:]—rÏœÜóM+–œLNºVŽ)Gž*&]Æ8å§Š—ºo°ÏµÈ©â„MÞ H«ýÿSÅzS„íïÑSÅKÙìÉÿF]–ûʹzbXƒß¹¶¼3 ³S!1`¯%\Æ×Œ)C¹o“€s·g¥ä0$™ô÷x9qpxÿÊ!ùYoÀYgÀYkÀY#9chžSÈK×$Ï&œ¢#Äᯎ×%yÞhÀyJrØ»²)5µºL8+}u½dÀyÑ€³UŽ?éêjâ²Õ9<’œxÉhœöšG¾5t­OµºL9¥:NÚ>YbÉbÜÈc%Æ÷%ÈKGi—’ý9ïкšã.cûú—ŽŒ8ü%Ì–:Nþ"=ùIû¯4,ﻚÓ<'sÈ@—ŒûêÏï`ÖæùHrô~Ô¿ü[B‚T?Hþ^røjpH§ËÏqqBz•PµÜ߯…§Ë·}p’Âô‚þEÁ-܈³„å‡|G#½’G¾ÿÓŸ@¥È¢ëHqøEnõ¦ç‡ª/9u tÕ–ö•”6Ú<)Îfßü46àÈ}¤þû tp.ç±ÄäÕ—ôÖœ–’ÃnF§ŒÐê2áðWÔ.'º:pä[Ó—³®” µºL8Ý|u`Àé*9= t™râ+2BH?3LßI çt]§IX`Y>âœÞ¾ýâ’P¶û˹Ø@—¼gwy_ƒþu¤8ý}ó3ÉÏv‘Š¿OBg+““€3(ÌŒv9[èg+#÷ùû’üL0àŒ7àŒ5àŒ‘œU¬vH%ɳ gíQæÄíú'±«8TOñ—3Ë@—Œÿ¹|ƒAï>R¾N9…äg±g¡ä°;¼)]µºL8|uÝmÀ‘û-—³·ètú ޼gw9“Ñže›p°÷[ô«QcŽvÅš¾Î€³Ö€³Æ€óäääلüÚ/þ޾=eÀ‘ï hÜwŒ9z¯`›§Ô€ó‚§Dr:äùhrø{ƒÝIž_7àÈ÷°wwS´oqzøêÚkÀ‘÷¿ô6ÐeÊÑ·Õƒaæ¦r>2Ðu@rÎ2¨Ó#ÉÑ·ç¯ 8òýŸ} t™pø&Ô«ü9T]øË9l K¾ÿ3 0Ø>bœa~ùÉp 8q9S t™râº>#ºªp²e}UT÷Ãðýµ˜!ßµNÀ‘ûHø÷8´ÑFþmô£1§TÇɨcÀ‘û?ønóoý Ë)Õq2pdüÏ�ö ¨~ÏÓ˜£Ý§ÍheÀi‘ E}e Ý„ãòêí½ 8= 8'Ó5,UKª‡L•«Úîniúôy,GÞ#»"9¸§qØ.–õàÿÌö†Äö~œ‹ 8}ýë4AªóÔ)ß©£µëhsô£V¡GÆÿ\Q<Šqø™8y«<cªgœçZV§¼vüS]c y”äðù«µ–šp!Îjß<ß`À¹^r6è:R>f’³€Œùœ¹œbÿ–Ph±jOŽ(Gß¿VpVp–ùc R-õÇ î<uNÜ ³±Ú€sŸäðóî/µºL8üíb+›QjÀ‘û…”“üÂÙhÀyÊ€³Á€#ߣ.dïQ±ýhrØ~Tò×$Ï%œ-’Óƒé"¢ëhsdÜ 2n”Ç Š™¨Á]‰œ&‹vŽeµßé7Øè±¦îDã]9ñ¸Á”ñ¨Ë*û2 ¬«~«¥pkÏ$®2Cî9òýðj„ó…ççÓcªøŽÜ)ä7Ù©-÷päþO!‹ÕûØ&œ+ÙøciãrÿÇqø;äÕㄊ©œdŽcÀ1ÈOÆïþí'Aª_ýÛÏ•l/7e޶|Ž&‡¯­ªÿÇœ>¾å\`À©kÀÉ3àäprÔ©ªš ê´¯A9MNƒú2åÄ箫|Þ¹R ø ÖÓygú9bÞ¹lÿ±ÿ¼Ó$SÌ;çÑyÇ•Ÿw”3úؼ#f™Þ¼Êœ¹l߸Ž@–2DÌ_ø=_7¦}‹rw÷Ÿ_bµÌïE6Šg¯âq²Äü92Žh »™²PWòFœÕ¬v8§¼v¾#jç5Z;EO•½“tl¾í4-¶ï¡µãÊ!µÓ uY^]ø¼^;X΢.>L£å|_S“›å;Ò 8çpÎ5àÈw¤¯bksН #ÎxÊImösø(ÈmîŠWp®4à\aÀ‘ßš¿jŠíG“SÄòL¾”Wq„çjÉ™eP&îg6!ºâ»O&ý¢„ÙNä]%tLHÕôSÁqo}EÇ„#À!1&œã?&4›"òS—Ž W±F) .K·R£Ä‡Ç«£Ä‡gªwŽ”Òåóazçh?##·$*ÊøŸ!üœúY]‰ aQ”)«ˆœû 8«$‡yÉäÌ·âRÎmœ[ 8òé!üMTmŒÇQå°÷šè:·âݹ÷5„´úõòQål`³0ï¢¼Ç Ù'zÜ´ÇÍl/ú—˜…;Ôöïq-‰6?’ö8Wéq_¡.ËÛûp>ëMÂÿùðNµñÞ4”ï†Ù¤äå»Í 8Opž”¶BT¬ÃifÀá1ÛH~¶päûØCY„RÊwZ]&œ¾ºÞ0àüÍ€óšGÞÿÚà ÏG“ÃÎÑR¾ yŽß#K­÷æ½ _ýÃ'é\9”ûuäöbÅ¿Ó9÷ÃçY/àßMÖ®‰Œ9úUä? 8ßpþaÀùÒ€ó¹, K]Õ-bõ¥=Iÿɉ[JvŸ*þ+Tûa÷‰ôoDåÈd‡³âo¡t­7Ðå»›šYÉ€SÑ€“nÀ© 9üÎÑÚ<›pJ‡¯»;<焪 95ýÇa|Ï“¤ŠçИ×^‹h/0àÈ=´alUBîÑ]&>36%ºN0àt5àoÀ9΀ÓÑKêXÉí$‡Íï©Uµ%fÂéÿ?ŒSÈZ¦ö»·Ãùö‹“$g¤A ã+çTÉo g™¯œ³ 8òû_ÃV±ò!_å#ºL9q]$2?óBŽÜׯîþëO981]äíôÌQ¹O2Œ½ÐHue^iÀ‘ûHÃ6äÙ„ÃöRèŠ,ójÎPÉa'kú•ÝQçÄ÷mx+_E;lY k¸«H§|Yìþ^H‡fþ«ÈÖEb¶z…®"]9d/w9ê²¼¹RÙ·qçÊ øÌ«óX‹"ç•™òýŸ« XïÖži^]DKƒ¾/«E/h؈•†È‰ÛË;´ó/6­„_ÓÒpåÒØbR"[õ[%Ú¾#rØæpNñ¯è-NJÃBËù(•åï½o3É¡¥«¯!9Ÿ!ét5÷ÈnFæí±Úq?=@jPûÂçðA¬|ÞוÏð¹¢|N¡å3×}iÅ­Á3üËç˜,aE Z>®òý”l“ò©–ÑÚÕ0•àl9<ŸåÐýö€X£vè› ‡=EOb9ÜÌr˜k’CË«Áú³<¿Ãòœ¯Ë³Òp0ËóüòR Ïs…®I4Ï®’gјç›hžGd±<7ÖæYä®áx–çMâßÞ"ÏCäy³Ðµ’å¹Ëss“<‹T=Y[b*Á™"rx;Ëá~ËûšG‡¯rµuK£”åp Ëá1¨K›Ã…,‡í´9³^ÃGi繿…¿Õar‚ö9<Èr¸‘å°£_?ÎPÏõ>®A×D#~`yî†r²¬‘ÍDžw²<O./Õ™ ò¼^èjHóìÊ!y>ɬTGvf9<S ŽÇd9¼»<‡ äð+‘ÃSX±žf˜Ãñ,‡§c*ÁY%rø+Ëá eo@vXæŸÃvÍD¯b9\Es˜¼I›Ãº"ÕLVïõr;´„éÙMÊ~š“e*¡v%¬³kÔ!ËjTÅ"{æ ÖYeãØ}þvµwµ?Mírå=ó·ýJþãÝê8öñ~Z£X{NyW—çÑÂÿiÔ€ÖÅ1J8Ež¿IçÕBóI\9dÆ¿À¯.øi»[Î+«§fR¤bµ.®YÈfüÁqE•â¯=‹Z>xÛ9ÚžF=‡kø÷/´q)cøks‰®J1]˜Ã1ì¥>šŠHæëÓ Dr¾§Ž§¶§–ä cyÖžSUߟAòÜØ€#ï‘a'búón#Îx_] 8ÇpÚpä;Òc&”áÑäð—ÓIžO0àÈ·1EuaÂá{³â„J§„%Æl7%öX1 î…”F]èH»H¬Üw^þݤí(¼ÜƒÓ‘Ö•CFÚ—PW™]J\S¾sÃppfØÑØKÅK¬žZbã˜ÿlíÑ•Æ8aw£>´4O¶ ÿ¦c¦iW*t-§¥áÊ!¥±Ï¬4Œ,M‚ˆ;Û¯ëÃlç;Bå¶_'zw£BjûMgK…ÿÓ1ÁÙúñ¿ ]SÛ]9ÄöQ×l»¥¹‡Ÿª³t¢XE6ºYêž¡ŠQ cK»‰ÕèAË7‘¯F+¡.«,ú‘XšeMJc9ÌÂT‚#Z\£Ûh—´Ü_EIÑÃi]9$‡µPWY~’¥öÁOê%öE½EÖÑ;œÉ,=µX5 -«Ît×®»«ìõSâv•q=èrÛ+2Û§ðf•ˆÞáɱV•qª¥Óòé(rðL–›Ÿ›âùp>ñnmg³¬gZºœXàl×~B²û5§üáÅÔ[g”#ÖH@FÅ€ó€Ê¹­®Ê‰#’s²(S)géðã,�œ-ÀÙœ¯ó³Ê¹ãR•sÇàìW9ËnQ9˨œåçg†ÊY)šqýë('ŽHÎÀyBåÜÝ]-ç»ÏPËùî‹T9qDrŠS¬rîi râHŒso{•G$g pªœû&¨vݲj×ýç�çà\ œŸTÎjGå¬.Î-À¹8wçuàü]åüåg•óÀ4•óÀnàìWË0ŽHΪœªr,V9Q9C~Þœ/ÔüÄ‘g£râˆä´WuÅɰkÍdÐ5tík ÞùHåÄÉùYå¬]ªrÖîW9ŽV9놫œu0ö®[œÀ<?VUå<68ósp Þ»âˆäü¤rÖWV9Üò8Ì)`üÙ�ãφ6ªœ8RÆswËÊlí΃î|Z·Ü(?ˆGˆ)ÛMÅ®LLd Ÿ¨ˆm)ˆ=ꯀläï€|¥"£Ó©H@NQ‘k.�d0 ªíö5 �¹'�ÙXzÍ7*2& @Z–޹ ñ€,äA@J�+Æü "×V¤ ݹрÌd yCEƦ�’ H[@zr9 PÎcò@žä]@ œÇ©=×W(ùq9€\ H1 ËYÈ €¼Œ�ãþP‘ñUi Èñ€œ È€Œ,–ŽKÇ¿È^@>äg¹® u9“¹¡€Ü�Èb@îd /ò 0úM€ö<¡ -�9¾€ d2 K�y�M€¼È€üSE&Â8?±6 ­�1sb@Æ­wâJ@¤ù'‚]“`¦žT�Œ?“ÎêkÒõ€ÌZæ¤g�Ù¥"×§’ÈÅ€Ü̞׿Èç*2<™Éí�9 €€—2y- ¯rßUäIn˜ È+*2% ˜ß§@;œò$ »2œZ€ÀÜ=õ@Æ�–N½ðý¦ªÞ²=õ� ?ªÈ´ @êr, ½¹{Œ Óær 0&LÛ ´ði_-jz6 �éÌÝÓ ™�ŒÓ¡§?´çéû�ѯ( IŠÎd àK½xƒ7V¼åïS‘ç2˜g< ̃3>ÚüLhó3Á·™ }y&ôå™Ð—gd °> ëÓ™à·Ì¿e– øá³š³Þ,XïÌzSEfƒG4<¢Ù—ÞûìGU¤ÖïÅà“ƒŸY uQ ãO1¬©‹oä~@6þsñ·*2ZÔ[æœ�ì Ì)äV@ÀGšk™¹Ÿ¹0ªÏ…™q.¬ÍçÂ0ü–y§¾ß¼€À ;Vóa–™? WUdŒó  e.€µðX7-x[E‚ä…M¹JEAï¾ úÎðënþXÞrœŠÜ ’o…½”¥°NYºEEî�ÿyص ö7–ƒ¾f½å0†¯€~ºÖD+`§âNX§ÜÙSEVžÕÊS™¦"w5æÊ»À³º¼Á»ó™©"÷À>Àª*r/”Ï}Ð î‡ò~˜eV7�æå¿@{~ÆÃÿ¦"A]<{ƒÃ\ð0X±ö7Ö€O²ö:@À«|öd…Õߣ0þ<ªî-ÛÁŽâcP†ÝÈm*²Æ–õ0§<ãØ°Zf‡'=îIðÙž‚Yï)ð7ž†}ȧ÷«ÈFXmm¨"›`ÇlôÓMK¿îÙL@`géÙÛUd3Ôòs03>}ð9˜­¶<xæÏC,y˜SJ«ÈVXwo¡"/Àîî 0—‚Ÿ° ê}ûjyöv^ö¼£ 0²íøIE^…UÒ«_¨ÈNhu;ÁÞyHE^« ̧¯Ckyêôoàî†ÕÍn˜ Þ„YïM˜SÞ„–ð&Ìæ{`M´öýÞr�QwþæêzÐi®î::-Ôvè´TGZ§•ºgî´R÷Öê.s;H^©z§ÎjµE9¨ã˜³A­/çyÕ'q^P÷Ì«¢öçEÈa©êG9¥jûq^RGcç¥kTd›ºóïlSkÐÙö/Ù®®4ê^œóšÚ/œ¿©c¦³«% ª·ãìR÷.œ]ê§³ëN@TÒyCÝ·qÞP}HçïTd·ê8»¡vv«~‹³[›èqΛõºxS=³sÞT÷!·TÏÁyKœ·Ô‘ÍÙ =n¯z®çìU}Qgo@Ô]g¯ºWéì…Ö»W·Õ çm(Ÿ·Õó çÕÃw>!;9e'ûSè}%?‹zÜ7¶°ãåÉÊ~‘×�>ñÎe9$¶°·EÈ}O<ŒÞùäŸ I±œOS㈼Šž’g£°"ZqóXÁ-VLN`E¡+›YÁoiñ/åŬ¨VÔrš3¤ž@º3$U ç0¤¶@.aHŽe­œî"ô»œŸŠ#YÖ”ê¬4~ҕƔ΢4ZÑÒ¸eàö¥q‡iØû„®kiiLáñ¢¿kK£”F.”F¾@æ1$O +˜¥niü%Àvþ•BìO[¦’ŠYž¤²dlü”_}%_é/yª,yj²¯ä! $§HîK[‚Ã9å-aê$ѺіàFº÷ª[åß’ÜÚYG[‚+‡DqknÌY^ï~¿í¹Ó2Yœù%ºNm¬Á14‡·W.‹±?îµ9tÛêNšÃi¬­ZO›äÐòÆŸ÷O†ñçµç~–Êt‘j,­¯éE¬–ÉWœâX}%àÌ’-!¾Ì›¦~™w:CéoÌÒ_BpJ|ós›çÎiWE°+CµKO`ž8ü+K-µþÚi™Žü²jÿÚé%Z9¾räËŠEÍ ätc£zÆ)ï)EcD_¾€ö”;¾p碧|ïßSR«‹Ö›C{Š+‡Œê)¨Ëòæ¯ÏxO#oç6Ï_´“ÙÞžØ.¿Ðêrã¶ÛÚ/Hzœ¸òBµó„g½äðúâ閭á4bºÈ ¨Î&ŽübÈì•Tåu‚°½í/pJ$‡½&ªÿZ· ǽèGÚ*¯Óò¶:C´ùƃi[]á¾–P"ÚêïþmµÂNÑÆ.¢mu»nñ¯À$Õ?›¨ŽêŸ-¤sÓŒlnÒø6‚sHX1ZqçEÂâ–Õ¹…¿i …®»˜ì®´s™ÖŠ<ð£Ü>¸Eõ3?Û¥zMŸ}Äák}ö CrÝGˆ"Æ„C•"z÷¡Ú»gžÅZyåÒùXŽê&rv²^Êlÿ…pô#É—œÏ%‡}¿Û!‹¢Ë„sÀ×ö‡²ý+&‡Üsþ%uùs~”æÁÆ.)y6àÌâ½»Ž–ÃÞoI®Ë8åýbÖvÑ/–Ó~q—ûmv¾Ö¿_dŒåÓŽö Wé·¡.ËkÏJ¿ë¯C§ô”<è)u úE^§YÖì¬vÈk~Iòf=ÖûlöÝdýËo³ù·Œ ')[rúÈáß $/\%ÕLÃ~¬W.ÐJf¯FSNR]ŽüªÈlv/[iQa9q]«‰®&œF’SÄl'¢Ë„³•<¹[šÔY–<¶(þÀÊ$•¼YŸ€#oÖÏ^Ëúû½Úšp0]dLHêäß~ŠÙWìcu’9¥:NRŽüBkq+f)Ë+ gLðèW,¼¢Æ÷ÓÑïž^âßoÅè7ÝôËì Êù6®â‡_„º¬²Û¸°×th2ì®ðõi]Ù& dv¼­²:½•Ö霳˜_7Pgûœ"a{ µ}UjÙÞCç'ým¯ÔH躗Úîʉ۞Äg"ÿ‘ÇùÚðbÒùËK¬�Jì9Vb‡}g‡A²ïÏóÐëtpZ êGßæ‡INŽÞwº9c 8£%‡õ¤ µºL8}ÇÌY~c¦eTsù¦È :I~Ó6G~¡u.›“ø[ga8|vÈ#ºf$h‡X§•Ì÷3»ê8ó Æ±yÂÓk¼‡öåûÎήhU]jù÷å*ÝDžß¥}Ù•CúòFÔeù¬nhÏr~`%–’\M–X‘Ðþ-k 5ø HÈa{¹ÎßIÉß)ËÇŸ³\êrGÑߨäù=Ù BRÅKÞˆÃÚa7VQퟧ3íÌGJJÕJfo×$‘ï.%ýnÀ‰_OŒ½Ÿ×eÚÙë¯NO’JîÀ$ଓ’³aÅš.t«î ~Þigo•SÉÄv_E’Ÿ¤g 8ÏüI+Þa=—ÿü‚sÀ7‡» 8¯ÿI+úJ~™µÞSYªoYëåûó±<³ÀÞJ$ïd’PÉ øZæ5ä#Æéï›ÃO 8ÿ¹’O y_‚ò)dvíÔÚ5ÌWò‡ $d’µõ‚1GˆSäÛê¾5à|ý'K~Opë5âðy‡œ£%ýlÀ9,­¨%r8JÝ ü|"Íó Áù1â”!ÛérÈ+XÉ•X›M­XÄÎOHªT™*I¤ZÂR±óS‡Ÿã—çgQ–¯äŠ $0Éi%›rJuœd¹ÿ³ˆ}gÖùV+Ç„Ãviè?9ŸÍø¼äù×mˆ™\‡Õ×½,ÕB6Õ×æÇ„ÃgòútòÙœ3ekŠ®…žò ÍóbÖ’î"rZH9kKãÏ/f{ Iët–.nå«ýô?©½“¼ŠH>Qj÷çœ`ÀéjÀ‘ß"YÌöE)‡”† §Ïâ°½Ó$r˜|šG~Yc1_jO8«g­–óÐäè[ÝÅœ eˬ�ýË‚þžÄDåü˜pJX'gÉ£ 8#Båy³¼ß›,¿Ð±˜ùêIÚ3—Åû|å –œrúÊ‘_è¸)ɹV'çˆrJuœä"δ0+ú›Xt~%rä÷/p&û¯èo*d³'IE,=Úœ¸~3±b¹çiimhá5t{ž¼Íß´ŠÕ×Dòb¿³�«ln ˜‰nâ~&yp‘ؾÙWûíNû’L¦}‘Nûå”ê8ÉOp6„é ä<a k½ÿnO‚Të $¯ ³NYRÝ Tÿ7r˜_G¿}–ü©ç`¨–à/ç%]/&h l}‘´_k© ‡wÓ]Çäw 8o'È!ÛMuzToIÉþœ7ÿÜÎÀöŠ>•Ll7á°Ý]§ÏÌ™Å,ý™Xú‘ç@‚rf«6šŠh?Úœ€3Ž%«lü±EÎ8Þ-æLá—t)ô?ã¨ÙWØþ"=ãpå³Ú!¨ËÒqÔ…³ÚzpVkp2»„9ÔŽ«NþC–†?ç7Î/’þ.ªXz¤9Ûýò“RÅ€SÉ€SÑ€#¿Ðºd§Až&çß<×2àÔœ}ºL8ìþ…ó ÑUÏ€#¿²„`ê£}L87óï⑱”c 8­ 8- 8Í%‡íöØ#´y6á´:Bœ•Á#äÍ¢6þÃ"o˯q_¿ÃS—‘þ#d-1F}þ)!]9$R÷LÔey»©_TT#u•1Sxï_ä2$U ÍèIñ-yÔ.ëJ]·ˆÙ±Iµë‘–ëKY]f$°ë¡«µË•Cì"ëî0v•ËÑ÷ÓÓcõu ÷ãºbœ~¾rÎ3àœ+9+ t™pV³ºà{±ºØ%êâXZkÝP!½Ë­þu‘3^”ê…¬.v±ºàmžDL}q +y~·‹|5e ´ÂŸSãÜʾAlÕ•†‡}ϯ+±[GŠ;•–Ø£ÅeÜ.÷ø—Xí,aûTZb®RbSµ%–ÄKÌú{å­1»øš(f×a×0j׺óËíz.]³„®‡˜]˜]d$Œ]årô=Eî,Ýú«S®+ÆùÖWÎ,ÎŒç¶ä`]Fœ4¦+‰èZhÀ™/9l'кU«Ë„SÀÚç”·ÛÄè×d6m¹ß5¹M´ï´Ã¢–_£mÕCÚÆ¨ËÒŒ·ñ¯Y‘˜œ”»¤þYtÛ\VË´¥aÂáñc$*åa΃œ¿HçWhós¤8«|óó¤ç¯œÇ%gµA~Žç!ßül5à<oÀyNr6äçHq6³žË9±ž{HôÜ»hÏ]ßD p¢vùÅ¿çæ.}ð¬ç²{RÖÔe¯•e-e_P²öêr¸´›ÈáfšÃÇÝyG ]“ýsXç€å|Yæp)ÿ’îóÏao–CÍ÷¿g’Èá[,‡îE»þ"‡Ùþ9ÌË9<–åßÔ>à›C6ú-]â;ˆµ„¥·±Ö¢jXº–Yªy?ApöK?g– ÏÜÉ–žšÀÒIÂÒs™¥{¨¥Iú·ò >¼®sµ?ÿåT¶ËWG5XÞýå/çÑݰ;¸?_ƒ”ØwrÍMµœ¥b»"ÿúó/„Sªã¤Äã’ ¦]¬È¾\Mw–ñ7´±Ä˪3]]‰®ßCéâ±èDÑå}Z=”®1L‰{O­*uùs² 8• 8™’Ãï.‘»ÀÄvά£Èa·_u;VðRìGbßb‹½AQ;#.GpÄJÓîÀeh/IÌÉmK¿ûf—‹–÷Íå4ù%öÝ·®å; ÊwßÄHÛ´üî[2ÿî[×suß}ûÒûja‚ï¾a*±Úúò•Ä©<ÉÐÝÝ®‹vüC~?.ËZÞ~[Ç.)ãÅoë8_’·”ËòÓ”}é~Ã�ËûBt׫Ýñ¹kù ø"ÐWì-W™‰´ß_>ˆÍ­µ‘±¦“Y~f”ççÚùé òÓ†åg!ËökïËÙíWKóBÁ)ù¹™åÇæ«ú®7$ÈÏ0‘Ÿ³Y~JX~º .Ë¿¡®aÿ¿ö®=Fnã¼S+D°Ýüц ®*¸uF+®öt>ÉqbûZkÕ“îzw–Ü0Ã%gwy·K®Iî=œ¢5Ò"p× pI>N¬A€�yøõG8i‹hÓ‡hꢆÑ–_Ú4mâ~óàpf8ËÇÂÅÖ0tœo~ó›ß|óÍ 9Ëyåwűûsß—tþ¨Nçó0<ô¤¤ó™ ¯Dç/A](êŒyg´uI_(²æ´˜ã Ï³¢>_Ã1"Å”èó±W¤ç�ç対 =ÿý•o‹;ÿÛ’†§´Â<üÐ÷$ [LÃ?®áž%¨ë$ ¯Hžªá% _‘4üM]©;>¯‹cW÷ŒˆcúR(+uкén°Æ¿óÎN)•ãóÁÀ´FŒCb8ÛåU‘106úäŽ!<—u<ü¦8¦OÇtƘCÿÅy¾ÀxŒeÝLìK8ñók_VO«üÆ êi•ߨ_ÀÜ_À¬00ç ˜«˜§ö©˜§~GÅ<} » ŽyúÎLÂ1§Tž§Ïxšž?.ð\*ð¼\ày³ÀóS•ç™Tžg v~f¿Ê“K8ææÏFçSžOxÎ0ç ˜ uÚëÙ÷©˜gãæ3ÌTÌsóÜds¶€ ˜ÕûÊ%óéÏ×ež_´>ÿ~q&³­sǧJ’ñ)¢d|J (Ÿ(JƧŠ’ñ)¢d|J (Ÿ(JƧŠ’ñ)¢d|J  Ÿ(IƧŠ’ñ)¢d|J (Ÿ(HƧJ’ñ)‚d|J $Ÿ(HƧŠ’ñ)¢d|J (Ÿ(Jvñ”ÀÊUuõW¹zº Q#dåªÚ/*WÕÚ+WÕ‘±òª:.W^UßhT^UŸUV^+œtöZáº×Ôq§òÚ¿ª’×Õñ½òº:®¼>S¾AßÉgÙà7Åø,›Ò]lojqçÌĆ <?ÛY6o<*IÞ’ ’dHž”$׃Dú Ä…OH{Þ„ó\öü]¶¬ó=~_gÙ\wÂkÏs1Âì Õç_ 0ÿl€ù'qW稳l.¼h ³ æ% £=Ëæ‚ü»á|™=ü›„~$ñhÏ Á›1†ðð.^gÀ#ý:O–ÍÅ%Ëš~ßôÔgÀŸ°¬»nÞS 'UÞøŽØS.Ê¿ÎÛ.Öe‘žòÆK…žòCµ§¼ùIò+ 9 I€dZ’< 2‚d»ûð^â7»’o`É#¢o\ü®dç¹Â•›¹Ý¥¤ïª]”¿5-ìæ­ð¨%˜ 0×`öe˜K%Oˆ$Ëÿ<0wK:ß)è¼ß�“ï@¾¾Ð»-µw_’¾ç ?%Çó¤Ï4À(Ñù :?a  æŠæK’η:ÿšæÖá½à’ô•0ë’¶vŒü5�á[Á•ÿàµ?µ?#õî-ü•$éƒäs 0˽[Ø[X¹›×>s§æ#˜sŒôÝ]ë[Zk`·DŒþ¤*#Œô=Oñwâ•“˜:Ç\gP—)æy¦rŸæ¬fÕ�³Ì1G tÞmLÞBì­t 0-Œo€á¿Ô~\>[G;ì:&×yMÐyË�³a€I 01ÇHß½„ˆ¥Óy·1Ïë0•‡ 0Ÿ6Àü¾†ÿÂúñ-M0ôÌ#’Î ‚Ο5À<Ê1Ÿ5¨Ë£Q;˜K˜ ˜óóœÎ&˜ï^CŒ^ç+˜/rÌ‹u`v,É7„_ ä'Q–`¾n€ùª†opç½RßùK­Î»ÑÇÌ¿0À|Ç�󼆟Թ#-A;¦ì:F?æ~ß�ó‚æï 0Ë1òT6´:ï6æy¦òo˜— 0/`ø·ûvæ t~7b´÷µw¯fÏðõàðR•·F3W~b€ùÌs̪5L0]ƒÈ¿Û툶÷Ãïl½¼ó°dÕÓóûyíÃ1û 07`ø/Çwäob|[kÝÆä: _‹Ú;a€¹Å�ó!ÌÍ#}'PùzÕÏ “ë,|q+;…³3m€™2ÀLrŒü,Eû°w%F_s˜™’øü¿µ›b´Ïëö~ìÆŸáÌ÷Ô~Ö�³j€áÏ.K߇ÔïJŒÞ7ÌÃý§¤TdÀÜ3À¬`ø —}küÄHã»u¿pï¾³þuù!©ví7$/ã§ñùCy¾FrÓ’ôkî>Í”Í1Ï~Í}×£üšû—óÉ·~ú^þý/L€wŽ2OPÜ/í5Ì1„™½®ì“˜o.0c}þh3ÇæZæC*ó“Ÿ�ÉcåÌ9†0ÿª–ù×Uæ/~$O”3çÂ<¡e®©ÌŸ»$_)gÎ1„ù°–yZe¾rHž.gÎ1„yRË|GAgX‘MüÙ9†0Ó2ß­2?ö"Hþ¼œ9Çæh™O¨Ì‚Qfâ¯Ë™s ažÑ2Ÿ*Øù!¼0ÂÎC˜ëZæÅóü`3ÇæßÐ2¯˜ÿ$/`漤eþ­ó@òêfŽ!Ì÷j™ïW™ÿô Hþ³œ9Çæi™½óIüd3ÇfGËÜ)0CD¾õúÌC˜}™ù-ö_ù1(õ¾ÌC˜Uç[?˜„ý8Ó–ÕbQ‡‘•m¯‹ÜpÐÇ—ý8òzn²n9ŽÓC½¥ÓS–-&èu/Ú@yK9AÔ´zn·y–Gÿ´b„¬$»(´Üf§Ö¦—àDk3R„« Ý®åw£>ýn²ÝÃpP-Š^7Jü%kuqÎZ°íÚ1üïñÛÉ¿G­V+pú1ê;nÜNH —ÄÈ™Y91ÓGH©£SøÏ$I½�ÓØ “®›"Zg×Hö1’½æn¸LÔ<>r’ lw‘ƒB/òáÒñ¢^ß"^G±}ìȱ#VŠâ^ºiéÚ±iR®l!¿X g B"†ìL:„B'j®!/M,)•ž¾Íò7ݸåĨ =ˆœÔmv¹•£GÉl®™3s'—m›Úc šÇíeШi×&AW/„©å® Üf/R–Ï.O —ƒÕ˜´¼®›$‹D3«Þ˜wVÒxॸ-Òí>j„­È:-‰Û(]ÍrHáÙíÍZ-džóÇî¶V¸ØjwÒ,*îGIQH9ç:n,pæIS2NR~¥®+i¡ )¡HÀS¬8,Da›aHºÞ\±j-Hc(Õ9y]•*õŸà%‚6¢@à(Š…ZIß„ÈÐt½u'7¢uD¢†½„{x{˜¢­4óÜ-À=pc'ôð(Å^åÐPàD]ßIP{Ãé¸!ŽTÒ$¹�my<!p¦n뙼ݨ8ƒÔi’Êh£±N™I"<'”£%Î E–"²`‚<—I¨³1#,#0tvÒ2!óYBÅg£Àg|qÐß@¹h6Šp,Wò%)¾5Ÿ‹°]\/Í#9qt"ÈÀëP»À!I±§)ù¹ˆx´’+Ȩ‡)ù¢%9»}Öíl^š:ãb’Ón¿|é&pl’šê´oe�gƒðÊp;ɧ—NHþ.,]¨|. 7PŒÝF�Ôoê£2ªB½ÌÓä� uo+Z†h³&©‹ýKΉ‘ë+¢A‚NSr[3iêzÅ>®2¢vÿÀ툴Ú!ŒX'æÌldÆV„‘4=:Ià’ºìãy²/Þ$v>¾6Mò¹ÿª˜A^%kO†¨³p¨#…©NG:àSG¬g‘YÍoG†éJ‚bp°cÊ"šàƒ¾óÈ;A²hÏ0œ&!æÌÈJg5ÑaVEK õZ«¼˜)sKÌÂúFžKC'UÇøðLßiÉø\ÄLÇÓÔR<™5–à)ÚÚ<™y0í[qÔt»Ìþ¦&ið¦SОÝõZèò¹Å×s ¶ s@Á#7p^˜£RÒdÒ€—O¿j,áèåi”v"j]z‰›eÉÅÓ3ÖŠ‰’¹Œ K†¤Ù{T˜tÙä7K“ɟŇ]?HúnêuÈðÈíV5íõ«UÏ[YìÖk]Û³l2Ûx&I懖í#!áܳ°8;³à8õ†Ó ška1:®˜;_–;ßó0ôgõÇ›Žj‚ -¼¼@؉e7½¨¿mÙÑ»ph–eCló°€-F,º\ÁEè•ÕZëõ±’peÙLdÙðOân Xükèšivå-Aޝ³S±9‰Ý±lh(I«ØÍ–e£É¡]—Î8lµ}ližb§8ÚœÝ>CŒ áyŽy¨Ýl_“ðá.•Üs8ræƒbõù>èLÀþH£ .| –7YÙ¼¦Y7È ç NgÃK–æC)VÀY£a:Œ“õ !ƒ^zdœK`Ü€xèRÍ…ñ”Ó6AóÁÜæ‘j5#ª×øZè“9ˆß½0³eœ˜ s‹d5F/›÷L!Y‘Y §v—GqÜyL·¡—á¸È[l¹ŒèÆ|Јy`³•¾O¬+›§bšÄw‡—”N2ñXt£3qÞ¬å0¶¸ŒXd·(:ý!“Ì2ŠñÀRĦkÜñ‡¡ñóS,y2¼^ò dxEAè¯l÷šQw8†ô'Ç9åm4L`sf°y3XÝ Ö0ƒ2ƒ­”ÁÀfhk±ÀÙá ÜïG£JmÆQ¥&ã¨R‹qT©Á8ªÔ^Uj.¶ü-àqf8À©åQ³”† cù£ Rðˆ‚ŠòøA‡ŸÒüòÈDz�.E°a†¶¼*ƒ”7A2ª ƒHFÛ4iÓd„M“Q6MFÚ4mSö8º4@—T�#Ô* AÚ˜OJ[ýD™hó$ Œèâìͤ•–èüO‚${B†JF¯ Y "Ät}*LWJnñm å)P '½:Kg‰¥È™¥Æ\yëÉ ¸3 g1\ˆð\^ßvijà „ÅM”š:C°eÚpo‚¹V ¯´F@ÈÛŠš”¸–ˆ£ †XìÉØ|J Ep925ä«Bºþâ)²þÊ–ŽxmH'šxÅ‚ÈsXÎ(ÏsíâJÓ&ºñu°MçP‚@\& |°ž×ga žq [6f)¢vµ¿6pÂVRu·bÿê�\4©&[­&¤jUÒ¹<¸6ªbõ6ÝUÁNUúä­ ‹W0O5‰½*ih,|ÿ‚ùÜvüÂÅј¿"M𧲯ðÝÔµò¥±#ᯙž¸%PÒ¡qäȺ¦§ätmZN¿vªÄî¦ãöl1²þ€$Ž¥y*œ>,芘˜ƒ¥p�¨PyõÆKÓ.Æ­HDlUM¢½.Gµ?×éšÝ4y[¨Ü9—å·ÏE­!t/"e2Å$¹\k5[5ެö5³«-ɬ“)ÇiQDæk×…£M÷½*®ÁwãÍ ´EsP{Ò Çõ×IšwobƒÄî»qjÃ�î[¥š`âu|Ôgw'·R¡qø³!Ôí£Ø™ŸY>×8£DeÐÅ^É™™Æ}BŠ•Æ 3^ÞÛv•ÿßö¼ÃSö´=‰m &c©Il3‚ïF•â2ÈíE~!—É w +<ÈKtÅry¼þ9² †#¥¦Š0&7¹ïAáö7É­›è#üHðmUp¸å¿½B0yV5ê§¹ïO‡ñÏm£¤:{ocažp4A×gL”Šv>ʘ:øi/¬�“.jÖ&³T¶<Ÿr6ƒ´ã4]ü m!o€Çï–‹çœíØ…‰×`“> v’wˆ}ÂE ‚A;ê}·‘½ëuj!´à½D3§ÅòYb©ÕOæ™Ën ÙL„gy•+×à·\Ð)iÆsJïY–’sõ™<½RÈn,)I²{A,   F  UVúBBàÍãù€X† f®¤n,Þ<üs"Ä}–8Ù»=©æYFÝYÒ,‚|î0—&ƒÎ£æ }2ŠÖ‡UßÁ¬©i'ŠáŠ<Èëê¢TÇ< Ο„JƒÜ6uTÜÉÔîgèZ8Àj·ÖØÔ:°F ;Èígi|Õ'ÙNµäþÀžcùÆËƽƒ>ø¦OjaM»ömø‰¿›$Ak›Éhb;hcŽëûìªl/‘Ý B䯰zraÀcX‚?¾§\ [Á0_ÀaIoN†dÅÃXÈî¡R.¶Á¨˜ÉåäÓpý´Ùº ¹ï8õùrø^KîaØYŒ¬ N~¸ ó :4â+Pªåº)Ô× ÂA(‰³¨„…äV1!¾®GVk}QÖƒXº•ÓJRÆK¤D² ï]£›y/òØýyžó’$ a ”n;G”:ŠùmpÝõé)òâ-Ûhg[ma÷¡-n?´³ç6^65$ÏiØNB›n%´Ù^B›m&´ÙnB›m'´é~B[ØPhg; ílKáÿPK ����v©Æ@���������������com/sun/jna/linux-i386/PK ���OJ;@4¯ÌɆ��ˆ]�(���com/sun/jna/linux-i386/libjnidispatch.so¼½ |SE0~Ó†6@0ÁíBUÔªUAYm±« u-Øt[(H-Šò¨mèR-ÜD¸{‰ ŠˆŠ‚ŠZµÈû¥+-ŠZ *º¸TEMlÕŠ,T(ä;çÌÜG’›ðøþß¿ü.“™9çÌ™3gΜyܹ9²³L&“ üÅÂ?Œ•ÿMÒ Üßå¥ IB¡§ÐC¸HРÙ߈›LôtÀˆ] |J»ƒ=‚Y[!ŽçÇ@°$Í$,ÉdO=ÄñÁ|�rp´¦˜åqCš›çá3 òƒ?_„gOÛÏãð$À3'Ož—øï·t4VÃ3 ž×à�ÏHxž‡g<•ð¼Ìá–Ã3œÿ¾žµðlÇÏD¬<2<ƒ8Ì¿yˆõ(„'OWæ0> OxºÃ³ žûyú;ð@sÛáyž§àÙÆó6Ã3/<£áI2 üõ€g+<å<þçÃ3žgà™ O&<ãxÞ_yø&<½à)g<¥ðüž«áÙ¤+ã9x⿟€g <Ïòø+ðdñßëàY�ÏxnæixVÁ³Ç_åáƒê£ü=�O <·Ãs/</ÀSÏëð<Ïà3àÿÿñwÉÿnbHüJþ›.ýƸ¾ðôIK=CY×Ãs ÿ}<]á¹Ç“yx-3Bpoááx²áÏ­ºüx8ž¿Ãs<wëòïç>x.„§ <9!ô•?ž«tét¿Çë~ÿS÷{<Ãs)ÏåáuðÜÄ—Á3C ó$Á“O?ž× ž©ð,ÒÑÏü÷¿à¹ <Sày”§çò°˜‡i<¼ž¥ðô†çx.‚çExVÂ3žÅ.Ÿ‡O󰂇Ó๞nð¬çih÷ÆÂS ÏtÁø/»' c …hçY¼…³»˜x¼3…KÔ¸•B´Ç,Þ…Bgª¿€B²÷gÚ9VÛ),Pã]YùjüB ›²”x7 Ç RâÝ…L0Ý.~-0ŒÎc6ã¿@xËLW0þ°TÝÙDvã/š59 7ó¡Êo‘z?†åá—1ÝÅø“¬JÂdÓËFˆ_ôðüÑ-UÌ^c¼ D²áCÖþ ðªo4‘äºÂÿC nOÑâ½âá( N—šüsX^¼IèÍãn^þÐVþe>—Çú$æß ý¦„éÆwòú=$0øYÿVŸÃâWðx/žÿ{¸l~æñ8`ÔÆ4Ñï|­ü¯ Ôðò®äø7sø×@OŒc6 ó·BýÊaໞ×çpN œÎãmÀoáL-¾ø«ÇÆfŒ *ú ºóü×�>Ó¥Ô¯“~3t\‘ç—„ðóàO®Ðè/ú¹0¨®áñ‘ ¿/K˜-ø×ŒŸð·€0’Ã'ƒ¾,Óµg]ˆ>Ý"ï«­Z»úÛF¨é¿4~þô¿ÎSì^'á_ÀO÷RŸŽ¿HÇß·­_¸>¢q5(Ãu\ãyù·ñòën<Ùá¼¼—®-Ÿù7ÂïÐ^¹ÿfã)æ;¾83bY<ê3xø.œÞ4[tòð7®`öóÑ7ù*ƒ?ÄeeØØò"´Ï*0¨—pz&ÎO7®ïuX?pÂ.äõû ZSÏÆ6ŒÿÉLðA_„9™ùŸB|ƒ‹ÙCŒw¸™{€gï íÙC×oåå/äòøòëß`þÂÿôûôÌÇÂ8êá7²±ãŸŸùã™?‡ñϺëÓ ß®`<.çõ­øã˜Çø ÀÈÔŸá=Þßžçúx)oïµß§“‰ÆXÌüí×Éyˆ>*ãW/'ÈûÝÅš¼÷@{uZÁü8ŒÏ�ü”˜‹ñÀÿ„Ù8MöôàéRæ`<ôù½Yš>W?½ÁÆE²¿€ßÛÂü{ŒÿúëëålœÄøAÀ{ކÄ_œ¥å…èç‘ú)þû-<2”¿êóKgÎÇßËòoƒr¶ëäÕ ù#øøb~$ÃÏ£Àï|^þ  gÖéÇ)ŸÕ/¹œþ ^þ!(w}óo0 ´w 8I¼~‹ HóÃ0þ8È28Þ=xþ$ˆ�ÇÿGèpœ¾åðXge|¾Æç¾ÀØêo˜/Žù]!žõ ›7`TKxåFæcü�—Wçw3Z­“Çõhïk@‡yùOñúUsýKáñÞ+´g†¨³g6éè]ùÓÓòoú_VÅ|Œ›A>›=Z~1À×—kñû lÓÑ; ñ+ò”öîÄ=Ö_qÌY øæy~ è߸CÌ—£þÂáûrþqŽçÉÔþtðç¸5üÙPðsï Bç,žúç‡þáˆgñ|迟ŒÒø©ƒxO¾½ûºT¯±é ?½¸=ÝmåúÍõs È÷qèßpxˆ~.úÎ1Š?üqûRÇëÓð}³5øWAŸD˜¨Îçð/@9£tãË Èÿ8– <ÿÔkó2ÿ7÷Fpúe^¯k8hÏïÞ`sKêÏ¿nó=1þ(×—>_™Û]Éã  _Áün„¯�9ßòˆ&ÿxww'ó¡1žöë¯Ojý©Ä‚‰ï о@üïJy„Ã!öw*įo„}8¿Pß7d­¾?rþ*¹¾o„ú=¦ëÏW Ú_<×€ü.zJ“ß0—w[7ÀøÅ¼þ×óòÁ5Õ³ù;ÙWgÝÀÖ¨?�½?–hýßRžŸ?yÚŒéù¥Îñ%Îü|!Ò”éS qâìñøsüÔ) ùwÍÌ^8yJ©³°äŽ©ãKK K…©3¦O~`Z±à,*™1kàÃCÆO+&Mš’_XR2£D(^\2eºs’0¹Ð9d¼sÊÌÂ%…¥¥˜p·³Ä5Ñé*QÓJ§L†’„üÒB'’œ^8ë®ñ3Çؔ铅YK§NWGñTÍ¥–èPeaZᴉŠ¥Î`H˜4«dгPºcüÔ©ÆO|PcŽ…xþ·‹YMŠK ‹ó'N™D‘‰€C?0;¿J½©/ælô Ž¦Þ¬Å]!ø˜sˆ˜>#ꌉÀÖŒéÀ~ É3_GzÒÔãZ´`†kÂÔBb}âŒé3 KJ€Æëª“?©dÆ´|Óïv'–Œ€èÈñS]”Æe‰iJÃØ4¡";´Å9…´dDQIáøM¦#xQYYwâO|rÆ…³%ã':ógRÉ“¸¦@‹Aû Î +Æ„ 'Be}ž ÚçtŠaíÔ*©â˜9cJšUByØä ›iù¥®éùLŸÏHåO™>sƃ…ŠE‡Ê‰¤ *:\&k²è@YÔÌÑa Ï�‘ ½ô #Qb‘@&B›: YDí6,‘ºÅ#!O*) EŤ3#Î(†_0•Ì¢DäpêŒÒB€baDV¦L/¸ûáifLÐÒ‡§E$ÏÏ¿+ÿ¦wÞyPwœTæYAeÔgu×YAÝ $V8{è$€{ü…¥E“• MT*P4I©@Ñ¥E““ ML`k>쌬yh3‹ÆGÖßüTÍôF#’9¥,5Ð5iÒ`£4ÊQ­  ¢eGµ+8ÄÍ(‰J€¸‘ `$È)œ6£äáhQå^z¹—žYê¥g”dé™$Y]’¥gdé™$YzFINc;cbT™: ë3&iƒ+ú|ÜßreúGC=ðl"™)Î;3K£iÊ(0D3f ?F‚”è݈÷êgѸ¹%3œ�É”Rðö”HD^KsÏCT K Kff«žJ”Ê=0ªÊÙPd¿F2?0à€Ü;ï(*œø`© FÃ!wæž=ü·¦O)˜RZ<Þ9±H¨©™8PÞô©Á0%Ú(ìš^ÂgQl.È)tÍ(`®7ŒìàTå3EÖ<sžŒ‰Ϡ䀓J|T"›ŠÿÙ€±â£ƒ’»£‚ê+µßiN5@:asèÖ‹ ‹ÃÄ!;ÉÏŸærÎFá<’äšN‰¥—Â|a’0~Ú—`jîiB;Àl à§L§¹Ráô™Báô‚iЯÀ¦Bò_“•_0M)_ ¿g;…ñ'âaškú´ñÀ'øÆÓaV33ŒÂtÓ9©T˜D.^>ªcAáÔ)ÓpÂ3±&;Ì›S+7¾dr©: Ë¿ûž»Gjy%ãgé9þ˜†í=uÊ„‰7”θáf!¿°`¼s<”5¡´”Ív!izp#°{cÑäI¥7:gÆ™…3 ÁóœÕ¹qVéÐ*7NpM™Zpãtj›¢N÷¶0(ûÎwä÷¹!õ†›´ßê¯!ÊŸ‰ÿc¿bÔ¸IQÿÁh)1œRLHžžQzø/#ŠÁT¥P*á<ÆäšÏªVÁ¿Céh5 –#“p¬pèï BéåS:ânÁ6–ÖuÊ”.¸8”Çíîáḗˆ»šn.àáBVðp —ñpWòp5×𰊇Õ<¬áa-ëy¸—‡ <ÜÏÃ<<ÈÃ&桇-<låáQ¶ñ°‡B& Í<´ðÐÊC;x˜ÈÃ<Lâa2{ò°7Sx˜ÆÃ¾<Lça3y8˜‡Ù<ÌåáŽæáXâ/ÕŸ‡Â ^Zyhça"{ð0™‡=y˜ÂÃ4¦ó0ƒ‡ƒy˜ÍÃqÀ‡éiЇIˆƒ°�ã‰ÀÆ!,Âø<Apb©ÿ» c8Ò1¼è`>„³1î„9.„Ó!,ÇxÐÃ8„nŒÏxŒC¸�ã÷ƒ~bø2Ä1 Œ?åa />0¼èbøàa¸à1Üú !î®ÆúÄ ¡£TaØôÃAXaA،᥂°ÃËA¯1L½Æð Ðk ¯½Æð*Ðk ¯½ÆðÐk ¯½ÆðzhW o„vÅ0ôÛ@¯1L½ÆðfÐk o½ÆðVÐk q‘ô!þ Þzaè†@0za&膃A/0ü'è5†Ù ýÀðnÐk G‚ž`8 ôÃÑ ×Þ ú‚áÐ Ç‚^cxè †ù 7޽Æp"è5†E ×N½Ápè†Ó¡½0,†vǰÚ C´'†3A0œz€á£ />íŠa9è †sA0tC;cèvÆÐ ö Chÿe.û…ábh —Bûcø ´?†Ë¡ý1|ÚÃç¡ý1\ íá+Ðþ¾ íáh _ƒöǰÚõÐþ®ƒöÇp´?†›¡ý1Ü*yÒb‹Å׫4ý‹øß÷‡aÏûB`nÜVY¸]¬Â<e#ø›ð·ŠNÝ`¶¿âxZ¯ý5ÇSPEøŸ¿šâ¸ãP”„ñ•ÇŸE¸Ó8žV*ÂÑÜ_NqÌ*Âm1Åq«(ãã(Ž ExTÌŸKq<¥S„Ûþ Š#j¯ó§Pw”ŠÆa<‰âHª+ä·SOÑá‘¿@q$]„Çý­§1Ž;âEåTŠcQE ¨þÇ—¢ ª?űè¢eTŠã)Ÿ¢•TŠ#+Ek¨þÇÊ¢jª?Å‘µ¢ÍTŠãI¦¢ª?őբzª?Åñ”dQÕŸâÈzѪ?ÅñÄ`QÕŸâX•"ÕŸâxʱ¨•êOq¬ZQÕÿÆRû›°þ¯ öÇxÅ—Pûc¼†â˨ý1^MñÔþ_Iñ•Ôþ¯ øjjŒ—S| µ?Æ‹)^Eíñq¯¦öÇx.Å×Sûc<ƒâ›©ý1žBñíÔþO¢x µ?Æí¯¥öǸ@ñzjŒ·¶c|/µ?ÕŸâ ÔþTŠï§ö§úSü�µ?ÕŸâ©ý©þo¢ö§úSü0µ?ÕŸâ>jª?Å[¨ý©þo¥ö§úSü(µ?ÕŸâmÔþTŠ·SûSý)ŽMYä£úSÜŒñVª?űi‹Ú¨þ'1nÅ8ú¤þ&ŠcSáq.ÅqG­ÈŽñŠcÓ%b¼šâ¸c^”„ñ•GU(ê‰ñ ŠãN~Q ÆË)ŽªQÔãÅÇVEx¼Ç?Žâ¨*Eƒ1žKñ4Œçb<ƒâ¨:E£1žBqÜ¡,‡ñ$Š£*aÜNñLŒc\ 8ªVÑlŒ·žÝØÌ»±u„1sßï Õ.‹(1O3&Ð0zÌž÷+**Fl1û°WÍ}ÿpw“P¾m/ºÌ®¾²ØÂ /‘EÜù•ÌɾiÐåš;‹5fID4ÊÊvO£Ë¿[DL“Xksßû á뉰 „åJ쀞Fç_åʶ�ýêì1 (šÜ#Ù¢?F"Áçà¹ïF‹ØÖÙÙYl‹wƉµ € e&›½âÞXÆß›PA@5Rl±Ëe9^JOäfÊÝe‡!1:<QÚ µº\Ž—óìÒˆd‹äd¹}^ù=V¬µßŸOõ5òî¹-¨¾û‰“w»'@<Ï*;ìb›É9©d”Øcsãn(‰Z½â”/¡f™É–:(•Ø»á(HOJKöÝ‚uv&[¼âlƒEúR!û‘0· ƒ¥§Öè"Œlf}$²ØßÙ¤zì×§ÂÉ2J@5¯‡ Ž1Û}sOcå¾ý+TÎe‘s¬©5ž=΋än²ˆ½‹”²dZr`ñ òs?ɇ¡$ô—‚ѯ–ãäJ+¢ÐÐ+昈F|k¤F!-«ïØ)¤5hI»¥/e»,ZæèX>ë¸ÓÕ9,ÍÆQ3 ñÄZë­mÅ–>øC˜×"#¿Œ×e¨Yu /YHn3'';Zx‹Ñ KŽVÌΈ ÏÆñK:á®qÚä²VÓ—R›XÛƒJGѤÇ2ÑÈy-j‰ˆ�àñŸ¶J'š?D°¾Á\_a~Zd2]eG«©ÆTJ'üƒ#BJd„Î; [ctí¹ìÕ˜ß3rþ¯¸€wªC£gA£×9pdRý‡OóNKMqâ$6ަ¨ƒ¦r|2˜ŽŒRÒÕRlîw±­»ËâúÆIçz�U÷�Ø·1 ¬Ú�ŒÚp7憶°’kÚ-ÇC—#?SÎkU™hBø` Àœu“R.Ä"€UŠõ¿Tγ@z¤N²Ãj5h…_’Ýô»T¯ªB|¸*èk¸’×°*z &¶æ¼jè5ÇyÓãZfµtX±˜«P3 ¦f¥¦þ®,%LOuuðìF’ñẪ¯g.¯ÀŠèõìÎÁ–o=M5kÚ¢¯i¿Ó¬¦Vµ¦×b _CçòòÿóVðéy{îãÍ¢ò&cJŽÕ³ÇæYJÝã_Î`º»¾]vñv íçúvξ0j­EYðÿ MŽêëýr;«w‚ZïÊvÖ&KÎÐÿyÓUœ»|@œòتçÑÄy´«<þV 2ëÈ1ƒ–D˜õÔK½?gؽ^9Xùÿ“zµéëÕë$«W¢Z¯î'Év“Ÿ–q -÷Ê.àXÐÃ+gó‘_®Lbõ4£«·(-Çà„1ÚÜÛKÈY°ûëÖ�–ç˜Íúç°Þ惸í‰ß€ÅwñçfüÏT#Ö\&6µöªõÎI¾Jü©•x}tøŠrN‹ÜF~;xEVVyUÞ •O&Úþ%uT×¶É<T2ÿ[Üiê÷‰ë72½æjÓ”Ö^­’>k¿Ý>'»¬TÊcËÁ&#p'½âËac0�eùF±ÉT™=ÎÞ²ØÊýŸ:s2¾ ã;þä‹-Zj<¦~ ©Þ£l€pY¥V2³ÞuU¾Óèæ©Ø[�Nb4ö*JEššw<ãôçmKklk$b\aßæ¹ hû—éFâÞ¾â£Ø$ ±IZùP,®áðæ &i9›&™˜ÚïNBÁ¶1Ï$™G‰»Lý¾pù©½Z#·W&²kÜ+ÕöxîLíÑŠíQwä½j0jÉ¿µÇ°_CÛ#Á°=zýjÔù5¼=ý~†öx6È3j:‚í‘Ñ TÙl+¾-w»1£Ð"Ç‘V”¸“¾ØpðÕtyuÞ òq òF&< ¡°óÛ¡¬š=Г¨Zw›û AsÇBªÎµÕçxÅmjSìÓ¹·Cý›ëµ ~Ç æwT*è;— Ž1¬ ÙƒëƒXÉá¿(•ÌÇÉ�´ U Ô*Üê+AӦтÉQgÞJP‘k =ˆð›êÐ67†ÕþWÃÚhåÓ/ÞÝLTûñ“`Ûè°/pâµ>‡/OqKŒ«Zåe‡›ߘ‘ˬí[p50yëøgÞ‡àsÊ14íI€:Õ 8pÚ{¯œc‘Mh¼C™f¾ÐÑ*ÐÕF ®Ø eñ°2pˆÙ±|ÚPËÑ<RNVˆEUÞçtlu°¸f£Öú3Ž‘ ÚÞ$ÚðžõéÏF=kËÏá=ËókÄžåÞ ÊØ8XÐ8¼¨þÛÑ¡å5AÕ|¯¶�Än¯™HwÞiBøO2g]åœ&Ù¼H3d¿)R±¨Â'· fã+~Áš+¬�VhÙØ+ΩÕÎÏÅÈÏ‹A†ùË_QSn‰cS$8|n|'æIÕØCÔÍf+�E?AhËÕ ð)NÀØŸh™$¸?9/ÆõÕ)áJß[™®èÜêi»¹)iñ+½ìV„b¦ÄnJÒõ¦$Eh^Ìiû»0C_²¡ù€z Ka€9 ¾]¿ P®ì`¼rçÀ˜ûü2©±õ‰—(?¦­O ;&Ö›”î88—»19¥«:?XÑù4Ôym¡‚ž¡Dòm ±¿äá’G£³§lBçJ ½f¹rŽê%+Nt ÜbÕBþR|1Dl‚»¿+!µ†¦”6w³Ù.;쌹q*s=8s~Õb\e•GeÇQT®yˆ™1‹ï%2ð‡¥#n ˆ¯4¨6Å/à‡÷´ÂŸŒzÚПÂ{Ú%Í‘zš3ϰ—¯¾L²�Œ ×tà{M'¹r´²V¤UkÑQj¨£bm _v–S J0¯†þy‹-|1«†>¦Ïí×õ3Oi@+͹8ÜÍüÑȃTÿx²Уåê£:…Rú s޲F«ªW/üWS±ð̤NÄ:(ºO ÅêzZýôu ÓwÔŸP56‰ìáÜ÷scØŠW¯ðÅwyî¤pA›–U ÉU5Ä®S_R±6I·j—ä[He´˜”U;zÀ®íqö•»a‹âjf6Ò™¦Òƒl†]RƒV_λ– ™Ï )³Â8;ûÃ¥?à89—ȶâHÁÊýXèúRÉYNâ^C "’p‡’è´ßÒö»hŒÁ”‡bTræ„cã;Œþ[tس#b;ññHÿŸ§5ìâˆØSññÎ�ÿ6vQDì‚pl¼9Á_¦Ã{l86¾ûç¿]‡=:"öˆplÜ×ô›tعØre>aVts{öß0'1,gk)_ãxýT °_í§==N6 à H0ÊÙ…9£œ5˜3Ø(ç ÌÉg ý4dêAôðqOOÙbhWÕwòGì+cNš7€¤¥=´ÅRÉ›ŒËå߯JÞ¾”Šó.³i—GÚ%ã8D£~—oÂG³ÁJXi¯@´ÐvpÁ»w¥±÷¾tXÙ=ØÏwä Ä�ä=õƒ6õ‘<=!Õkþ�_¥e“¼l˜ä}Û*/Å S£¼ƒÜ η“ 0M­’§7Áˆ».¿kíµ_Mƽ/]4 ¢ž€íI\Ç’wô¥˜³˜ýB§Õ+Öî‰T‘¯¾Gж@-’Ë'Ó2É“ÉÛhÁ1/û¦Ã‡N†b²—RUÁª¿qX7Ë#¶˜iµ’i¥*b{ª&<§Aò&b"¸j·7)£Ýß„ŠpÝw¬Õ§cÙêÂÆ‡È”u§/FÚUþIúÂæ™Š {Qd{ËìÔ!<=Nr¢K9³7�Qéw÷›ç; T¾m5üS‚AfÝ(|!{-|8†Šóµ›ÌÎ!Õÿ6î5yqç²Î“ŽM‰ /,b„aR1ɤò²7‰ÉF[ÕúI,¸c’º?ƒ;×0ðÈ;° )æ(°¥X–ìeͱ¨]Þ@™óT»È¸=2ÂökLý)ÙæÆ-~yþ5),’« ë(´oP‡~í T»86õ:ˆ);¨ô¥¬ô­í¼iœ[ڛ˵j»£UÛÙG«rïÐ*»IɱÔPß½h:S¥ÒΨ¤§k¤{†’^¨’ž„²ÄTjByöÃÔ�¬h–™÷8Ò/fxÀõiëLèøï¶ö«›ÓèßÞ®gkNT¶þ®±•¶;‡¼I×Èû‘±n§õÔ§F£îš D×·a&½¾õ0ÚTøF™eñ(²BKé*RR(R…‰i¨ÿžSFkA—4ò£ã†ûÑûE^ ºÄîÿJóݘr_°ŒîF“*3Ù>RH6-zøúmgvòöˆíÑNúö(vÒ¿KøÏ€.­RûÆøG‡É©„ïBpäü/Ô7ÓØ³UûÄP!.S…øÕå²XBLÒ¯gK:!”ô •ô7@Ïù·q•ôÕ”¬šîžß  PZ-‚ÿÁ j=¢k ÞëDYJ¿ò=ŽB]Jc¤\¸§šÐÄ‘©Ù¦Æ›ð¤4Œ ‚É&n„˜éÉÜÒ.lGÕh?A Ý^©¡ëbC§h ýs›ªÝà¢d†¸(*ßxJ:!µúö7iìr“ÈøÜÀLâí2ØÒˆír ²UxçF­²«×ÒÁ÷Ø)ÓÈïµ.ðš/45ÈK)–9&z^óͦVˆàÌMÇÏßb1T¨ íbš·‚q—­~ó ­ ªo5G¾@®lÓÍ ÕEC˜ÒÔ8/P&*?ý'*îFç%å'²Ýlïî}ŶÄYÿÝbŒ©¦&™†©/¯pèDú CþÒ?Áž«ó$œd‡p`twãÅt¼ ©ȰÖeÒË®h™éä#¥ÏS¼”àœkVS6$°‹¸Ë$åµúoÓtR]R– Ò" SNP¦òvŒ³þá´þ´Oñ–žøüÓ¹Q^1—'¿uL|P)iÊfønµcÐ^8/¥;/>ϪlžéðìÞúhx¾<‹†7'Þá<³†— ¯-/QóFÃ;‚— áí?¯%Ϫá­8¥­Z}|³†«|†Í[Äw ¬ÿVº·²­Ô/¶]ç´¦6ê'ÇF3¡­&6Æ~i<ßÚ˲7G˜ßİìgŒ'F[/äs²™ü|Iø|k«ƒÜÍAÌá á )DIà ]8HøômkWò3®Ã~ÏV }g ÂÜ_µÒº›mSFœØfÕYvÏMƒ‰…©>дg’÷½¿9‹8i¾Ïë¼Fxó,W ½¿ùn¿C‰dñLWÖã¿ÂÖ›Œ%Ò*˜hV×i•Õ¦À"vpI·dh÷}z�ñnle­¡‚‡c¡þ°Õý_è[1"ÿ&"?ÿ&ð åÐ׈ÐC_¸üŒœäË!ä7Ud:ôu -KÙiYŠÖŽ²Ï¼vTû9r´nÔÛ÷Û—H}(R/³È³r½xÊJí$ʪ¬œoim®U¶ãð‡ ¯è¾ÏÆ1¨“<h„"ÅaR¦u5WçhØðJHPå²­¡ÜÙѤ¬‚«¾³h ;ßzh;Ñ"ÚDf¸ °iÙºm Q*ZÎi"©ãš.­[9šüEk‰w­%Z>dÎÅ–Ïqe€*£ãVѵg¿@™žüEÑ5>±‹UÖ’BYSµŽÜK¶ªľÊjÛáïỂçþ¢Ø(ÛFGâÇèÌâ ˜ØE—»"N‹þÌÜBc ×hÌtG àŒ8ó*/û¦Ì? yö1ÍK ¨£Û‰ÔB#ÍSÕÙg«9vÉäEp5xª4Æ·:ÎÜæ`æœÜ–Dœ5·n¼ÍçOT,Íá#MþˆÅÄe r#@ÚÞåh¯!Zž}ðúüiÆëÔì¤`fRLH&M‚ˆÔýHw¯ø0N¬ ±k"OØ4Ìì¿€“ÄU´ÜÊÍ'ö¯ýŸðE¾ñ#Nòìß\ì‚㇎} þµÊðúî§�˜“�•oXرZ>ƒÑä® n ¨ð,Í)•šWñ!_ìQú<~ûc\·JP¼|é,.åa‘_¶ë6Ö5boûËÏ(Vèj®D0ÌÙl#IÌЙv¶ËÈýnÛøîØÉy t€'Ç®U¶m¦}¢šiu÷¦Ak,–5Ù·«A¿óŒ ÛÞ¾ätt¯Ñ’Ó÷ªÅ…Ï­wî5š[¿±7|n=«!ònðí»Á¬Â+´#›Ûà{꘳?C1WùÐH•¤œVO£ó9ç¨ÜU¿n(N 7¯V>ª;…yìÔTN«&ô£ÈCÛ^C¡[‚…N'[r>=¡?ö‘‘Ч|EèÙ ýæÂ…óéù*|í§‘„·’/>E©ßüs \vÛ»«è»Nl»5±è{6…K­ÑÆ eÓx°Úì“ãåÁŶwãXãÔ«ãV'=9ðYóN¶‰ƒíUÁæ-—»÷ÍXÙ®‚ŒÐ /öÓd÷¯8X 66,›¤Í-–y™ûUÈ©:ÈF?Áµ‰ wt˜,… \ Vìÿ;Ç uyÓmPAúºø?R Avÿ õx­ ¾0|ÅimÝiü‡¸ptbé†O°žø†|ÛÆ²Dn!wc'ý“+700)Îà =¶¯õgxÿѶ4{è<³oËÇXèEP¨ï$$¿²œ²O†ìßêÑôZ$» Á\âYòåaÅ«õ²Ó A*©WÅa¾B@Szî³5Ìæßïlã]zÍçÝ%µ2,GÒt\ jÞÄÖÀšßf ŠÍ¯²¥ÈæزmóÓÐ1|’-7ÏW²={‘Û^À­íÝ<»w໸’|ŽþñI‡×”¬Úî:äÝ3V˜ÞU›ÛR…ûP¥Duß÷4¦Â q¡´´g¡ÐXK§Ò"»Er´ð­§:Ça0¯3Ç*y¸ˆî8È’évPù®tÜrq ùBn65š½/S¶‡®Å‡¦èìr; ôÜþ°ì8 ?˜Á{ÍKõÛO ¾ÉÓhóàñryÖB”!¶uµ¹Ñ0‰mµ¹'Çá³Í]§?y…LË£üÑ\pl3 °ÞgQè¥(†f1´fùc\ªß›ù<hew¾ /…ì5IW˜ìÌǃ¹Ð’ÛyK†ÛãíÙãW>·Ç%{"Ûã+ȳm¢ƒÊ6ÑÍOme|`4|\‡©úJ<Þ±=”’‘#ÑcL’ÐD0V–¤R)ðé²9El»pV6Ô9áGÞË@Ìæ‡°¿ãÙk x2×Ë9ÐòMržO…™S¨»™û°-Œ"¾…‘*Ö™ÄÝfVDÿGÌ%‹eÇA ÖC†æTw�½ElÝÖ÷Z½nÒœÐ[¡(F&ŽV:7/ºM|h’\ mÞ¥Ø_%¥"ØK±†‚ÃUˆü�n­óUÄ:ÏþKѯtÙ‹Ir‚O½‡*ÿ‰<(M6[&‰³ã.°¹ñÆuñèÎ*ÒÊqЉ”äYˆØÀÊR"“#{‰ð€$93…ŒC/ä­í:›gtG p»Ò6dÞŽX´ŸÝ6oQGÖ –QkŒ;˜S7Û™xt¾Æ‹óm8EQeM"±g–7Ó) å!Š™6àt}Õ3Ü\p½Ô.mõc I×$S?:…o)¾ê³¿¯l–ÈA›%Ët›%Ùµ‘6K2pRíZ†ãÑü´’ƒ’뀿?Ðó­Ý¥î”üŽù^ØÅöJ¨n?êý_wwÜÂy¦]©šüp¦üUÚ€¯âáåÙ’ÇM{ åL“|«> –­´ŠâT‡GMM¯ÄÂ6Fâ\"†Ëî“lƒ\‹"˜Ò.ÙƒåpǤC 6lqAÞàd2b]c• ÒšeúSIÚVu‰‡öÅY ³CÛ3ƒ9M$N‰bœÿ^c‡U‚úŽ/µ–�9@žUÑÌL¢CÐ*+WÕôê“ 3$)Ž2,S=CFÂFün'*q¢Úh…½²“ »Eå]=Î(Yß)ÆÕ¡•T)¥3J¿œÐŒ$À¨Çå5.ÿSÕ•Öæƒh~@Ã7°n‡Ý¨©|\h ÷¾§ïßi1#+N›¯ãÀÃû ³&i³„{ó]Íw°âû†V;kZ–Ù[:ç¤V5#›qG͘a¾Ø×l{ü¯¸!áfCÞ€ÄäHLJ‘f›iû“ ±LÌš;Éy‡y/ˆãëêVF£VÊÇÔP˜‚<L,¦%ú8Æ€dîî/<›òìç_^kHyÿ;¹Ù´®ôÓ¯zÚ^k‘sZÔÉ£N¢JfwcÕ¡Mzàì¬JgZ0üÄYˆ$ñüEÒ"’ïþdµd´/�X1i\»Ëª]£]>NëŽÖ,ðªO‰ígÿ ¯£Œ÷½cMkýB#ÁÎø3´Š&¬b× *ÒF¤ñ*š¢TÑRE_PoõÚ΢<áüËk )ïI,I[:^­ÿïØƒwáHõ -£ùTÞ£ñÃLêÌì\ !Kvÿí‘øG¿Él€wnE£Ø¢ÒF AV¤x;†·ø>~B±ëÙ¹¥%D(œMyæóo„Ã!åu:PÞàÉ.ehW»æ ”TçŸw,Äê.% k÷ïaê!º0j|×*%ôõ¯:a”#{Òkò%ÌÃ’§€Ì[“œãƒ&‰eŒ{XI‘)7–ù‹ÀX,ã<!YîÆµeˆ¢-ž#§´]âƒÊ.ñ—£Ú2@ºïÚwéüïç¨8 rq®·¬•mãêv\"Ð[B³Ü qõß_Î¥­žS‚áVd-H %\Üò)K8»]w³0t[oìѶU||[eÂö�hE· fr_èù>rAC<—EÙ0kÓ•…`æ@Nô�„$¶â‡SLj&ê�µ7ÒôKÜÊIøf9”ß[ƒøÃù}gÎëyá!7alæå€á¿MÔ–ˬ¨”G¶â.w@yÝ3Ù7l;¶êÚ}ìý„œDoÞQöš®Š°sð=Piò쾋?Gµ;šÚ(Ö&«K1É>Q˜q6ì' *>ß!üxRø£€o²8híÚÑ$AßÍK”òÊ9=dW’ìè *u­Wìñ‘²æ˜²GYslÁʯۊâb{e’Ë.˜nKÎAì$СøÛ)ÃöëêOe¯m ã¾)¼þ€öŽ{ÂïJø…ãׇá½/ø*ÿ³­ˆŸªÃocø‡Ãð—ãV¤£-áÿþYXùGÃðï%ü£r^‰!Dâ­p­a$ºïCùÕŸú Ï{:’¡)eG ²£[ûK$?†mx°—”¬¿n‚µ)Á·s Ò»‚˜Ñþ¶1t«.p-&ð# ´‡’Š·Ñ\ˆ¯Êxw£íM­Imôœ×P'Át€@‡^¡»r,˜òé&HiMmœ[‹Iu|o¬#µ‚ÉäºÉ$M<=GÑŒzZùp—º mnëIE!´óp%T±y)­IÎm±#ÇM›éýÏVA�ºžîçè»9r¡Úøð»kc äÒ^Ðàcn­j"£éTâ'ŸâB(t¬DÐÔ”J¤ÖÈK1¤—ž”ŠÐî¤ öŽÜ¥²ZÝ—°ü¶`a=úNd¾r¾;©|‡È´ WGÕ˜]›èý¯OÙ‚%V)ï¨zÔÚ÷n…¯ŸÍyÇhýlÒ;áëg}6F|Ñil” ¤1€íÝï“k‹h?¬Õ¶|èå#ö&œ—ÄVÌ{°;u¯±×é–Fa©Z‡Î€ìX¿EºyD6Ǩûü5º=®¢(ÙúC$«®KöʵF’µ® —ìõç%YÓ†@ ÚK¦/VIV®>wÉö=[ÉN_¯I–É®÷”ݽç&»ß6’]ÃÛá²[±î¼d÷Áºè²+|ÛHvCß>wÙ¥­ìú¬ •]ë:zÿí£s“ÝëoÉnÉ[á²÷ÎyÉNz'ºìz½e$»¿¼uî²ë}¶²;¶6TvëßAÙ%|";õÅcÙMÓHv£ß —]òÚó’]Z»Jb¢ÑHv¿TÉî˪s—]ÊÙÊnku¨ìf¯EÙ¨?7½ëSe$»Uá²ó½}^²ëŠöŒgF²[û†‘ìž}ãÜe×óle÷èÛÁ²Kñ¥W£ìVÔ‘S—¸Àñ=Nyòó÷×êGY€;šøúZ@÷b®Í³…ϨòŽzgweoh]Ñ Ö˜lî úm³ݶ™‘(^yÝH˜JDÕW‰±,ÉáÃ+ßè7”%6 ¼ÁNæë9açV)˶1ªw¸¯5Ƶ³y]¸F$½n¤^׈/ÞŒ¼7×ýº‚“€J7¿ªê»å-ÚÿÞ}nê[úš³÷½Îl¯ˆÌFUßáoF7›GÖµÙÁ5箾g«¾ïUÑúu³N²ùo¢dß« ‘,·g‘${Ñ#ÉÆ® —ì§oœ—dÛÞˆ.Ùe¯IvÞ«ç.ÙÑg+ÙÂ7BêUU(;ñýssÏ¿yÅHvu¯„Ë®òõó’Ýö×£»çc_1’]Ö+ç.»g+»^¯‡Êî§×Qvƒv[~ñe#ÙÉ/‡ËnÔkç%»ò×¢ë]ÒËF²ëôò¹Ë.ûle÷ËšPÙ½ñÊ®óÎs“]áj#Ù ].»KÖœ—ì2×D—ÝáUF²ûdÕ¹Ë.÷le·öÕPÙÍXƒ²ûô½s“]¯UF²û˪pÙzå¼dgy5ºìÖ¼d$»Å/»ìŸ­ì\¯„:B7½Š²«|W“®Iîv4'|³¡üü/Éoß‹áò{áåˆògÈ,».¥þåèNxÑ‹Fòþ"wW}§&ÛFaã¸ZdU”"ó ÈfYN·WP83w ‡{‡š~ËgÏJ#ù¬[.ŸÇVŸ—|Ö¬Žîh^i$Ÿ>+ñ¼îšš&™.ãQJ|3J‰7C‰Í Uñ_âµý âÙb<ÿ}Ápþû‚ÁüwUDñŒÂ¬´ê óß ç¿/¨[�Æò‰¦>ÝV)ê#¶$àq‚TÙ‘ˆ]–Þ²ò_<Rý¾C"Ö&ܯ»ZéÕU(Ïøm¡òÔÏI4±n2ëÝωõ¶çÃÅÚñ¥ˆbÍRÇ>/EkãsFbÝþî,´*BÍk’cðb¶f½h×D)vÛ‹ü,)Õœ—PT·«žêéËèÆçŒdtÑsá2úaåyÉè‚£Ëè­F2Z¶‚ïž+Þ[Q |ølöªÒ¹ÿEºÿ}³±tT_ÖX:]WIçä³áÒ©{ἤÓúBt_¶âY#é<ò,ßY;wé<öBt2VÒý¯›Î®›m5RËr#!}±<\H/=QH¢ðüÑóÑUèÁåFBÊ[®Lú£÷´—£”< Jnv«Òºð”VÉÆ`iMªs;ó¸û#)U?.¥9Ï×øòsÑ¥äxÆHJ7Bªí=Ç1‹°I˜$9Ž©e¾¥ÌT(³ùßÊnÞÏ¡xîÞÀ’¤°ÀÊgt»zL¢5„’´!Ä]e#-òØ6æ%-ÈûéLìäeFâ¶,\¼—®8/6kEtñþø´‘xžÆ*sÑâŽðÇ~blE ¥ÎË!Jù»Ðÿ/½«ï^²;¼N“[K\à8ãªqß§ÄuåÓáâúuù9Š‹iF÷g£;¬›ž2׋Oi¿ÃMt]-®:šÿ·_³PÕjʳ(š=ïwTpõ©çGW¥OÉ&î©pÙ4>s^²9ùLtgõÙ¥F²ñ,Õ©R.‚j¢Y¥4/”Ö\¡ŠfÈrÚÿ_,ù¬$³ÔH2-KÂ%³yÙyIæÛeÑ;ÙÜ%F’ypÉùI¦t™N2I¾´gP2KªÃúÝ*Þã,äó]¥‘|öV†ËgÙÓç%ŸOG—ÏøJ#ùü³R7ƻդ|ko ù¬l|ÎÓÜÆ3)]° ¥4ýís[P­_l$™w‡KæÑ§ÎK2¯>]2ƒI&u±ÎÞ´âJÂÙiN¿§‚4çÄSôý“·Îm¡tý"#™<¿(\&,=/™T.î\Þ´ÈH&—.:?™\½4H&?.E™d½yþ½iI…‘|­—Ï]KÎK>¥K¢ëÌ_*ŒäsêÉÿ›Þ¨ êM5KÈÿ© “Ò¤³Pé“FºïÉpõª</ ¯<Ãþφû?Oèq0Ç“¤³Ä¿_Ìq~RpU% 'ö M8ðt`Y¢m£«Gð]éøºfÊ'µâ=X×°Ñì+J>éž‚„ËqxÞ°…nÍW^ËÎ ¹V2ï�òr¼BóË\eÚ~'^°ÅºiÇÃç ¯QãÔxÃgá¢È;‰ã¢¿ê¾v‘n/Ö]`øÊ_Ž×¨‘nTÿMˆ@çꙄrV‘„ü_VŽLš}—-ÆFxô5õÛ/;ǧA÷ÏEtTT²QßÐå§á ¬Ïkt~o3Ýqt"væ@‰>m–ñÜ6Ôè ¤ñê¿¥lÆýèð³pø«o•·k ìñ]N]¦à×*W}²‹†'J»$1™Kq&¾ Gß¡S.=§û\vj#"Þ[Ù‚w"ï• Á¥™ì¥…_â¨À/ôI"^&Žhƒ8áîÐô‡�’.B’è(Ý^^w™Ê u1üØ ~"R¢O¹!þpÄÇKþ•[[¬Ê]3À[ç­ŸVˆå…ügœ¼ÂØâZ"<›¾±#; ’qïå8ë¾=À„®½‹¦\;Áy±EE¶|¬Ø¢Öǹ˜#ÿ¢!;dù8"{ñK«x©‚ŽD™EÇüTNb‹FBø„‘ˆÆvGûO¼†ö•6ôàò¼ÃIˆ+ÄÝí+¥ö7|"n¦¼èoãy;zÅŸ(8·~¥HL}÷\czÇü\ÃÌRKË7ÆdeŽå˜«âµX7å«�¿~pZ8¦ÆíhŽù˜R&Ù­´ç^U¸r"Â{û²Æüò0|¥Y™²[9‰+•båÔMè]ÏùÆ…3L Ç<§ ©‚·¬Ö¬‰¼Ñžç@&‰ßíÏšUÍ4NíFo)DÒ" HŠ@DÉŸ¢YÓJÄÊî@DÉÿ‡Fäý0Nì(Ù˜ÈN "F#“þ™¢:߆**S·\.ɯ:¨’œ_Ô6°‡+j6ÇÜÐAí] TLáëPÌà®9˜#¿¨![ö)Èö3 grä< ùН úõ`.KÃ~­æû̬WÿýëP™[8D“9’̈wͼ oå¯ÙP!/Í0$28ÿ—uPÛibXÑfNø×ˆE+{•¢å¥)e”ÚÒR4ë8f^´×¬ý¯°¢Nø¾ˆE+ýµ¢ûªE¿vÀ°hÝ«]ÉŠ¶ÊžÌ€ÒäƒÃÍS:Ç\«êhËþÐÞÞÎÄØHL+ã5^ûZÑ¹ê¯ íxšb¢´¢gT´g4ºme+¥iøc]Ë©@ }kÄþì—½;¦bÍv¤SÞ^¼ŽR‘â1x}�ú|&tøÅÏÏ`„ËÛ/-íM÷ ô„ÿÑu’½=é={,BÎì×΄68 yQIEXèѺ8úÐŽ¾Ü¢òLÜD÷Žf&Z1Çtá”gl€|S7^þ-Zç¼´'»N– îÔÎ[Æ—àæ-·c,£o‘Iš‡øBdµY-Ä»€] Ù|¹W,R_õQ_`GæêG':æn‹álñ<@6ï÷е{Ã�¨f¾²PRõœb™Ã±˜«9(+# ‰'˜ÿnrÚ׳±Ýâ„¢c!Ú¼Ú+¦|Ì“'Ròöñ¯x³=`œ? à c†ýœó)Gº¢›(~ÅŸwÑÏý üÒ%¯˜ÖÀáOÓIÄv̹šQÂ[Ç.¦ŸÕøs3Ô€õ›)5¥‘£¾O9>Ì™B?‹öñœ×(º^\Nרöæþ]'ä7êúÇÐÏ•Ð9ü¯ÒÏLü¹‚~Z?çè 9ö_Ϙ~ì?F/)–ãÏ©”:6QjþyZû C6¸`>—ýÏWÐå5ø–¢\ÙnbkRDZ7ÁäQmÎ@£»Æy[ÐîD ï ¾)dsWšhZȵ¬1U8'à+yMÀïú9X3Œ/G¿Ž +—¸±ŸÖWîÙËÉ”&/ÅøÜZdš½º%Œ´Òmöî¡8o%ëRF¿$BÑH`~Y»èH;a&0#WÞ†òŠß »ÀÝßæâ¿¾ŽÄ†`Þ6ß×Ôaé3<÷a>OáA=ð-xT1Šáoý8Φi óº{9|Ÿ‡Áñé\Ë\e:"·ïç)r££ÆTiüãªhþ¾¸ž]¦&j/±§æáìÓ;¨ét0%óŒÊ¿ŠðRÿïÖHx…(«±eQdÅeà+îŽë¿uµqªo·å¿– [×ÙÒIGœuÓÅU^1[­’úÝpüÈ\|Ã+µ±ùK¼wÈåx)ní3´g—æâ2M}i”ó:ä¤Öˆ[0¯ Ó¦ó×/ÿ†|ß{åtA¦:—÷µ]†¸ÓŸ¡©þ‚¹«U¢‡(#ŠÓ’ùeÇæ"žÏüö7ßµ(¤é~[›.6öO‘µÏ’¾FàŸÑ>ŒæsþÔYeºÍDBrÒþez¤â/‚º–¿W(¶dûn#´ç–áÕ1ìóxêkÎN¶º¤Íù §Æu9 £¡áco:?u!V±‰»ÿêD­ä.Òµêy*™.džgAú qº’Ù…£;ÃЗqˆ^FèJ¦•£{Ãг9Äÿº +™º2ôçÃÐ39ÄN#t%s5G+ =ƒCxЕÌb޾+ =CÜc„®d¦sô/ÂÐûrˆ«Ð•L GoCOã¿Û ЕÌýv†~: =…Cì0BW2Wrô †¢÷V¦«FèJæTŽ~mzO‘g„®döåè·…¡/áåèºìñvæ`â<Î+¯Å¥«œ´d g#Íœýtæ‚®\‡*¥Ä/b§h3âWÉ<bcü ãw!‡øÀfȯ’ýŠMãWfœÊy\Êãey× 5óÎãÄ0Ý¢›1Jöñ ÎGw(åœÊÚ xT2—\Àx, ãq‡˜l€®Ë|.<Î åq¶²ºaÄ£’ùCÆããa<:9Äæ.†<*ÙÏt9¡<s*w¢fÞÌy|*ŒÇ©¢£1JöÏÖsàqj(EŠýµð¨dη2ׄñXÀ!î5@×e÷? By§Œ? xT2tf<n ãq,‡xÃ�]—½°ó9ð86”ÇÑœJ–Jf/Îc]#8D{'C•ìo:#ByÌåTž6(DÍ|¤ã±å âËÒ‡£!¼qøª^dë­<p­ü³#ÃWö 䜶˜?5s?aþÎ1»rÌ+L¥Lg8favù†a~Éâ3BËœ޹—0¯å˜ÏqÌ{BËt‡cÖ¦ƒcN䘮Ð2†cÖæý³Ç|2´Ì±á˜5„ùÇü-ža¾ZæˆpÌ턹„cnà˜o#¦c/kØ÷¿Q–ÙV„ã·þ~Žïâø¿¨;K1ÿUW†#%äÎÿeÈ}9Þ[y)¢äÕÓºàº+ÿËæ‚êWIôk¢­„=Ço‹ã.+’ïòd‡ÛBh#8ÚN†¶n*+ x~HåÙ�ÙGÈeÙÍË|þ¿!¢6À<L˜‹9æ]sõQmQ‹]Ž|?âÈ]YVZèÇh˜M„y”cîë@ÅÊqƒ1§–í¹t;¤àg†ão&|Û!†_ÙL où\1Ω¡:Wf„·ÐzB¾œ#äȵ ù.BÞ¹šS8òE„Lß/Ï«a7Ò¤‡ãTÎ Žóƒ™ dõ”ó¶GD[Ch£9Ú[|mÆ!¥UY É“ÏjÂtrL§YWCY©[ßðWÚãí6^àóJŽÖiá® ÌU3Ö°À´ð—Ú:ކG °À=¡¦„¸„0?ᘋc L /°‚Ðq´1¼Àc¡ö/p!ažä˜WØ;¼ÀÌØ71´ŸcXW7…Ø3¼@7a^Ç1«cŒ ì^`9¡ÝÆÑæŽäz×ÑvŠ3ÌØ³¦q9#'Ù.w8ZoB›ÄÑ:p´’Cz4g8ZOB{ˆ£}bâúTÚœp´dfì8ÚÓ­KPiSÃÑ’ÍËÑÆq´½Aháh=íYŽÖ‹£ý„66-‘Ð^áhG†Ö³I6"-ÐÞáhÛ8Ú{MA>-øÎ7±©ö‹�ß­jP6Êœ\!h¯¸³¹ï1é×—jô|ëäîL›PtºÀq/5‘#׿;]Žk°t¡%ûðPÑÔ@à]\sòÔ8û°…=;[f®ôsfÀS³tß:¿a'çô4º:³3Q¸b•€×Õg³­ŠÊ@¡y{ô å–|ÁV‡oœ†ôôؾåϽ•WáÏbú™†? ég&þ¼—~ŽÆŸCéçTü™I?çâÏ~ôs þ¤MŒÊ5øó*ú¹âwÔSkü?œ&BßAJeÆŸ')µßwüâpÝð'ƒíŽ?¿¦Ÿ±øóSúyiÖÒÏïðçú¹¾¥Û:Ø‹ç„n†K€Ë¡Û{wàQ<+I,½Okí{Ú÷,–=é|ÏìMZÎà»yÒiù²Í³Ç™Hª‹BÛQG7vŠ VÜmwà¯^®¹vÜŠšÅ—ì=è}Ú"à’Rhšëi‹”  h¹ó»”}:(·óÜÚ½Êö•NQBSƒ–5ø²¦›6£Þ¹Ø÷K¦@¿B™ÍK´ïIü÷A”í?DA"Ï>šÃïѽºx_ñÕ¥Ó`Ÿ Î"~ ú‰M„åóZ?“rðŸÜW4jš*5§óìquh¾Rä„jO£ó¯ÁÛ½(d�w›ú}Ròµ<*›®Ž •!cPÖm1t6yP_ü9Ÿ~¦Óû:ðõà ¡8­Õ$Nk±°¯^ÛOâ§•š³[«s|4äÐ'4”à xª}ê‡oJñÓ 8±Õ}-ƒtv!êìã \œ‹h½å¼ɱRÊ[-»Òä¼ 9'SÊ[&嬰mÌÉ^ó´ìHÆF|1–7‚œ“$dɱ_rT±Ã’€1< 8fΪù r9[_gN…o)Äh…X“(96K® wÔÖ9ê©‚Xš3)¤¹“Cš»¾Î±—rÊ’<0è¤è½uŽÊqõð ƒî¡BÓ=±ŽÛFÇÓr^Å‚¹²c¿©Æ›&àöHl!î€WHÒdÇü4ÞoØ<ÒÀ¾ü K»” ¯¨€_üRkÀå‡ÎcAü÷ØtÙÑS˜Q7p0‡É¡êü:e@fËŽ*ÔáFì2Ží„uL.k¨s`[ Í‹Ží&ѱÐ"Ö™ñ9ÛûåTÙÜ«Hšrn_9g!Ld«IÊîc¡¤4JªÇ$k˜<Ò0#¼hsñö¿º^f僱éÿ r1µJŽ5ŒG6ÛÇ›6™û“—«×Åõlñ@Û•óÖH1†,zuê¡ôkC´¼žQNÓò²½Ðà¡Ð{tR´«<šÍÂèýÚJÚ½ d å¦ðs’é’þœ$µ]¦<°¯„WUòYƒ£ ¦×Ê&‰¾ãš   ul(žò]Ѓg ð»ðβ«jÒ‹¤Lö¡„)˜<€ìBÆ€øyO!¥«=› Ö㱆Øè6Ê…Š®sT‘Iwìg}‡Å?¹SN#ÞÉÙ…Ï~I÷]{$X<^ãú1…kv„⇉ìköY8ª:ð¼3Æ[l}?O@Ånaw+T0¨GævÈ9›ùXåû~l®|¬ÞC¼^Â/z´Ð`,t§.]èïeÞOˆ!KYA›žÈ1aÉÙœ]¢ô«ÁþÑ´Á_ù]„×�é_xZÛ(,/D+Ø4›­'s½’XበN#oºí×Û—E˜¯¦ö¶¡ìJR„òù8´q‡¥#²Ã·…ÃeUŒÅo¥ $ížS:6“w˜ÞçÍW8¯þ.ˆóÉðÔtêw«À¦î‘¤uzä\U WväªÌ¢%�¸*B®nG®vƒ»:2W®°5`go¯¸CœþPŠ<‘•­/—U—™ÌAÖ~Rªÿ”e‘»Qeqá¤òÁë¸Jäù¨l¨VøÐúbŒW++KGŒëÚÃW;ëšü/6(¹ìrY‚äòIe-r'9K½z¿Y$Äy¨§e>,"žšT‘b†¯„(6>"Ð1ôÝÊ­ÌŠÃ"èPR ˜q„öÚ4«�ˆ 4ˆµô:ƒ5ùýŠ ?m‡ûØ,{$–]fe#9»ÍZé�Ì`÷×W‡Ûég&4ŸMe'þ6z_ƒR3ðçeôs°âÒÙ(:šÇ3¿£}Š‚}8õXs,cæÏ\­š?,¼Ÿþ³ì%Mß¼<È—¸•x_þ0ÆRÛÇ|²·^ºnE5°|«Áãî º !nD…¢¦ËîÃγPod<÷c/_( ´b§¬<K½bxJŽ0 æ® Ó1Ó1‹o¹ âBi@º7ËJöú>N>G\°*UÆ"|¼?¸n@6)$ËA2¥?Ü5Nv,”s3Ø'€Mi YŠ©s,c~G…ç˜íI<·^^6_°yð ÂðŠ…?œOÔëC€_$Aû‰Ž…uäI04öÍÒDM¬¾ ÐbìÛ6÷Õ&ü[ÇÈI$«Â”ñb”&:Î$%î²Jy ЀCûT- 5ºa ­Ñ„1Šv?å Á¾ºwc-J6}@ëÎBðÛZ_Лûcúñ›»ûL~§Ì6?¥Üb›¥(g¾hü+˜eª¤ûryõ•Ü2† Êéê¼�(:;"}Næ‚ëú.öH´Èz|ÿ}Š<ÿÁyÜ6<õâ]7ú'Ö¨vñX¼£œ*�/£ñ|¡nxe^ó‚Êé¨$„ %ÝM®ml4ëÅšdê÷*"}Ùd0K"uï¡ïcñu`Œž/7^;DŸê™£6É÷©Á×9–0O`%yïÀ ]cî…’¥]¤A—Æðö+ç,Y`’ËV±ØÙ²kelqºœ1Öö.}ë�l…޽)÷�‰8i•©´œ³R6[HêMxWÑ„ ×üt†WëÇðõœ¼•Pzì° ~ã~›»/$‹Ž%à9®„–ôsTØÜ[Ûù”˜4˜ ‘ûéX,#ïŃå¼:U˜N¬b_Àoo)@™œÞ Õ ÑÝ›Ïÿ!ÂImäXî))„=cT1^*Fa€JK™¬Ò'T.Š`©R{2‘v|† Êó%²VŠß9oá2¯3[ìÔÌŒáz„â–ÈêߪSúÙ>\!?ió¡Ã=/ü™1” š€í)þQ;š–}ÛU1÷^¥ ÿ*j¥…ô]%;{Eë“¥|#!g)Ï@*l/ÔÔÿ@K'86EùJµsAú -½JŸîÖÒ—èÓ§jé›õé#´ô£úôt-ݧOOÖÒêÓ­Zz­>ýèI5½AŸ~Ò—01ØÁìøßÓ}5³·¯í^AG—€Îí–À=ÇØîçü¤žÍÃÕF|ñÝ4#wƒÆóe’yó⼕U?)K‘ùøR¤ò-bÚÙÝåÁNÇeЇÊñ÷{ÂA•ÂaŸâµµ×–¦,p;Zé­ÎðOvŒcßf_Œ†n´5Ò.äÈÈàafj#ºF½•Ï|çf3»­5ˆ=úÆëÚFv߃»æ!ZX•`&“º]2øûuÎß°|[æ‚>!9ãpüŠÁŽêÚ_þè8[?!ÃÄ`ùWÞ‹Ì|ÜÕ g™U—²šD)èÎL3s4D±øÑlѪÂÓ‡–wãÝvZ³C¡ ­ÿ-àòÔáé:á™øM¨Øï¾/OÑr ¿>º„vSКÕÇ–ÐÞú5Œìiƒ<Ðì5{®”bf'_%~Û*åìEÖä&˜¥V±f´4ÀNö.+«*­ådê?ðî»z$ªð~Zp^€`ø1G›g0´_|$<€hÅb¿< üW¤d…yH»mþó´>E’t² é>2æå¡‰ ‘g] Q—qäO°=N‚A#VÆDê1p{oR?ߘ·WÎ|GùóÒoŽv·AF+x_”î#‹éÕ±®_š?>»ÁäQý”Á8Ÿùd ÞËÓ¾$©~`°ø'ºr,tЩ2×ånÝ·'söJ9ù¤=5 ›ûB­gÝÎÒX?w–Üb5ÿÉ>ˆx´«ì:(´~óM¢”…ä\³è8`•ÇAs¿¼6ñ^r”°ÖͯɎƒr±•}™3±l86$£æ÷€:ý¡=Õú´[_f¾N&‹(®e¨Ašç”5B+Á?­úðBØ"œöõa:Ø>zXÖ/Q2fôˆ¿ëÿ¤ñ%ƒ’r‚¤~„.uP*Ö>È+/)/o•O•ñ¿sZ)_)7>¬\i¨®Ü¹§uŠ©W›N°EþïɃnðO ¹/;ý‹V AOüÂ)\Òa•Íï¨M‚ÎYŠŽ«?†Ì2ñ.åu÷E¸i]T¥±¹¤ÚŸÓúÝ9 ©ŸZýCø77yôvñâuÔ;`‹Çû§+ßÙhÿ]Ë_:Œò‡„â½9$$aj.^ è N¢œ”Ñ?—{¿šóµ� ó2„ÂÀ¡Dù£“º]¢ñHä}Ña!ÙéQ!¡å ÁBžÔµÞsž晅AІ¡ŠYÀõE)-~bŸ}2|ïÃØpÁÂ=Bæ`.M8$“?æ¤þ[µÃiýãœu'ȳrј;öj_ÙK¨ ØÚö†Ž}®‘uŽÃ1�£~̯WŽ 2›lžÍÌÁpfÀ^ÕÉ êR¿iñìÁoç„nDEøFŽÝ0Ç"|7äðkÕìì¶+~,jw—Ôøž‚_¢=ÍܺþGAGWPèæ–—íœaŸÜ⦳ÎqÐ$¨§æ¯îàZ‡€M‚w8¾–:ö¡ˆqùñ�½ƒMdTñJ.s–3”™›n5>[ûôÖnÇ>Á§ÁŽ—ñ7¸ÚAKý@À3ù»t/u|›‹M< H`/uèÞ HSÎü¡‹‚("o޹JüØ%d¤6zj\q|¸¿ý¡¬}º‰øáÉ4vÉ•D¨ÑyQÈÖUÂ?¶j°~E_HŠ—3,PXÇÍX9j úlऺ,ô¡vÉ‚÷p—œ£d;’.w6ɃÚm›2Ì¢ÏÔq—¸Ë$uƒ„X;ò³œažÔ¯¾4FªŸ$e™åxÍ€~Mswß|Ú ­ð:¯œ©¼æ'ˆ mS–ŽZj º'0Áø'´Þcõ=>”ö¿&aí­Þ­14A5Íì„‚²òÙŽE²Ðò®—v¼¥Å‘�3ªº dðR<Çœyèð—XèZ@t‰¯M 4wÆ]Ø:S¿²Vg"x¶Ï×/>ÜX†üÃä7/´ºl„÷*.§ú²|Ï3„Á_ÐÖ­ë‡ ÷=' ü rv¶”ÌoEÕ»mÓÿ©¬]óöêЀCŽ¥ßg³âc›û}XÂ[®E]µû¦ù=…�XYGzÑ1µ†ëO¸ ’è+q5rhÉJø…ÛÐÞuSÍlulý]ØƱÅeZ/ËÆÏI&ã'Ú}#ïÂ# H\—ÄÑF­g—s¹ï²».b†?¥Ï{Å‚8êù¾çïDª+ oüßü kS߆äù¯…4…úÙïìsü�ÒN¯µ“t¢×.iH¢4Ò Yõ&p{ë2»š{’›µ²ØbpjMêžæOµUæû‰ì»$éö-:e }yä­ãŸy ˆ;{‚;Ýk/Œ’—À(©|>MÍÿ "Þ´[êânœ4i ô¬ôËèÑ»³:Ñ×Å®Êæ«RìÓbæÁ¸™£¸‚Ê»TÿͦýïÚÞ¥ñs)EÉañ­*?ž8 [LDå%+Z™M7Ö·’°…2 ýÖ­a:ÓdqµYÛ䟆 A9 Ë}¼â§TãÛ‰,´‚ãJæ¦ yfr)‡Í—8݃Þ?ÅÛãX"y,)4GdÇZ àwREÞöŠŸÿ¬9R]õ^ñ€.u4K}õ!†‡‘,\Ï*>æi<NÙ?6 Ý@³âŸ(ƒö ¨Ì+ñ-:y*¾´h¾ÐÔ`ªç °®+Ñ`P&hù‰x—ô™WªÚÄÄÊǯ@Â�F¥y¯fKn¥–O ÕûRÚå«ðºš'Û©‚˹V¯¹[¿ÝN»òŽdGÆ·•Ïn}fiKðÚ¢À÷w!Ù;¬£…¯ TŽÆ±››Ö)Ö­0³S æ ”ÿ1ÓÛ{ØNøz›_‹…–oÄ‹ ¤Ÿ ‹£Ùu>væÙ8,6ëÍd<ØöŒ2`?çÀ!³•Æâ_Ô×Å\(B˜8ù›ž¿±É‚yšÙ[ØÉå다[Gëe¸´Ò }‚V¼ärD|»IÄziÌkÍá§²³T:L5rN“<b�ù¶ Ñ%5àxëØ?xÄ< ²8éw÷§Í÷ Í«]qxá“k­tBÚF\C¦Í½É¿ ­ï1×5¼:ÀrL&(5i”ÓDoYûž*þx¶kÖ CƒRøÁÙ�“¨œÓоªÕ }qNØu§ŠhWåð`lþÌq´šIç1–ˆÄcÀY&Ë( pÂnR’® zYÎYB{Ù+|ož¢uÊ6Ý"žw››è®tÆ ñ\ €Ò¡éðÅm#p@šs—1f* làÑÔj‰¢SŽe¬tßÚ¼é]¯úî¿=$xmnô´Å¶.®ÅþKqpQ.na>\ðЇ~f^Óã±ùf_Hcûܲª…9ò»sUæë9ôè,Tâ•©5²k%Þ¨WA$[$ÇBòšàî¤u"ó²±»�‘›PQ3pIÇáVnF ýdÊqŽÎ¡O~%gz‹M[\»w›ùã×Ӳà Hð+ñç7™ÜA‘Ĭ#-©ñ¿LsH,Ž#ŒÒª2qjnP”¯ómÊBeé~?óó¬øÚúGd“iü†V½Œc4dÆØ<ËQh§Ñ/·=¾ŸÓïÙ/À£‰Ž*T[Y^*òaÿœ*Û<¼LLj¸ÞQ%¯«1£“d³=~”ìN•7á©ñ2 DÁ¡!k'1§*V.«’5‹»-J¹îýAcùwPÿNdÞq”˜-sÝ" @dÓdãðXáÜÚªžîd›?4–XF/j ÛªÈpì󭉵¹‡AîqpLÞ4“wÉ Èó@‹äÁ#ˆàôôÛes‚´þâvøß6¯[g@…|4×ÿ#¿l—øa¬¼Áa‚gj›M,ª«“o.@ÂE é·ý•øÕåÙ@è8ʼn æ|wgñÇ6ÉU%o«FnËÊqOç︨xüø!©u§Ïb󌰨{6 µîÄ ›8çÅ"bn£X¶òr't¶•Ÿù¶^„¦t—쪒>Û÷cpjŽ1“—RݚŦvhÐ~u3»ÐK3<ÎÓ*ÕÉ^Ê–€\\.gε³2ÁnÆ÷ÛåŒÅ#íZ\fô\­I,6÷q|Kc)CqYšÿÁ…M¹r–VlÉ…Åqv`féúœ—}g‚¤”P/£Cms_Œ+ÓÈzB·‰ãq–¼ÛMr­èçjµÍM·µÆ‚¡p¬îçhµ‰Y˜c L”òÖ€ñ•‡Yû}fs¿oÎ °ôÛm›Ÿ ÐÜäܯ¡6hvÛ*èáu‹(òþ›û¢¼FQ“ß,Ó°Ýæî‰oÕ¹ÖH#@Bwõ¯dÓbk¤‰D jç¼PÁ^‹¢h—rAjrV‚>o)å%@ž°Ùæ—¡,óª¼æîLaøf6Õ“ºZAmžY§‚ûªH"Þ×ßQe{ü HL¢¹²–÷ÚôNÒ¶ZÞ[™Å=±Ò]fo‰Åßóª6×Né÷þØ åy©Wbqýò€úO'ùË*èáyæð“ª4[Ù÷©q_“ôÙñ¯:~xEƒ´ëø×W4ض·zEì„’g6ör+´ˆEÊsCŸì·Óæþ£ƒÖe¨:®H¶îü1özè)Ž*±ì‰Ø•NÄæUI­ÜÒ¤õ‘Éð€³e™LQÎi'²¾ƒJIÿT*;,S¯|8ûÉSgó$BÛ$JÆOÝç,ËKC±D,÷a«´Ž¤JÆMv¹AÚþN¸ÔE…Q¯kø/œPÄœ<lí ’Á«QÒïûj®¨“HŠþ—ñCêO ˜í€!o7-IUõ_‡S*Û<ÚÛ‘q:$µšê½Å—KõÇ\±»Ÿ ZöEA;8Žƒ MÀÌÉW¢é‹E.ޤָ¶ç#«sL|lžÕÐ|¤îá "Ò‘^»ÄŸí¦?qÔ„|Çð‰P<QíJÔûoÃF(“(”ZcÅÑ4ÝÂÿû‹cáÿ™q…Ò'¤…¿ã'è‰m:×ËTü«+ê%Gm?ÐAñ¥Ø30¾ AÆõäe>D+Iµ‘‹•IyÑh^oÁSúsn©­G'f<ä³´®Iª7µöwÔÛÜÇðÔÉpp�]ÿ‘õýHÏÞvR€úXG­Ì) .^§@ÿ&à&½¦r›¥œ½rŠéwDÿ²c¯è»L0L6ãQËÅïâ½i]½ƒìýv—ÆH§½YÉÑ çÔŠþžr^=§t×ï ,ó%4>óYY,£­º _C«$. V<)Ï!×J r„©]ª¤ìzS°ß?·Ýæ¹›6£ê¡ÀºÌNí4)s4HDΛգŽfè ·ý{géwÚ9²×iɵßk¾Eg>-?4I£ÌR õ|ñÏËmó-è?¯ÿˆFNâwnÏ.›' dß±u!©UzÔ"ͶJƒ,Ò,«Î�½l`€\U^kIV Ð�³øI¬TBèÕ640ÕfvïÎMxÛIô€]¶í»df%G…<Û,þÔ~}Y•œ‹‹I—至R^¸­ ”åúAÎÃìú¯2R,ì窰ͻô«‚F :Q@×â1L)–á9I6RÜ£Ž'bԑµ G{6RT9Dy™bÅ> s�4RÃ6s- )VÅ!#Åüm¤¨)Œ )¾¤†®2µ±¢¬šÑÔÆŠ ê¢C ùÿ nëffÞ®Å倴y®,=™¾Ïo¥p_!õ˜ÿr¼ ˆ†n˜ú¥üC»ØvÔ{:Š;㡤}>ôªÌ1àSãV¡ À㦂`Û˜³ì>ô„Ž0ç*‡–sÿrÜ ‰¶¿ñ8YÈ—qè8¾ÿ¿jþÏí¥€v…Y~<Þi† a½ }ÀT/½Ÿj÷3™_JFßë&eÅEÝP}­ê é+ÞÙ ³Ñ‡,R±:ßÿiïë㣪ÎüÏ ‚3jjQFMT& ñ…$$˜H  *Øq23I’™0s‡ÖTÊ€xÓUAíººk[>-«üjZ¢òSª)~ÖÒŸ¬oÅŠÖÕÄ`+bÀÀÝïsιsï¼äÅîoÿûåÃýÏ=Ïy{ÎóvÎ=玺 S}¥ƒËæ•Æ¿$ëF¤R*ë®ÚJ͸q¶zJ×:Ú6†¬iÖhÅVuî`VeöÌêã{r#»m|¿P2IĈ8´»-hk+ÊŠ¬Íb˜³îŽ^ÐûW()·¨_ùˆ°!(ˆì΢PBdŒ¬eü¾ÈXPñPÐÑÅ|<Üã€Ç/¨x*x©xÐs‚âÖŠO õRÔ"Ѳü3‰‡ydúHyr­½mCÑ¥ºæñEÑ®y|Q3åÁ-7ǯZ„h¼ûÖ§E ¸ñ+HËy/Á¡ºƒ“ÏRçUïËTïËèy‘´ßFk¸oRØî,˜À}*<j¤ËÇbýI…&QtÐó`&mt¯Ø›¯dÀDž…äÿ–7bÓßè¾Ïzÿç§HÜÉ_Xö“ý}…¬•å”Z±W˜,i·í…ÌN¤×9E™ù…(ë(å ïW§E[÷ZÖ¦çu†~Lžà_xL+lÇÓƒæ§m7"ÚlçÖ§C=}mU;"²œƒjÕNÄaÑp»º ½€û7ëYÑW:ù}+ ؼôm#ÂË`ìÔ’]\ܹò/.͈ÎˤÍZ²õqÌ?¨dê–&|Éß\Zœ³|kZ™‘Öº+ZµËRµÅGŽžE„-ÙÉ­Š ïTgäá®Aä½7^Þ[;âœB¸"£/¿¤Õ.H{,º 5 R4¼U­žÐU*d©t@Y:jÈ’Ý’J–¢­[ÍâtàI6Ü(w™†÷ª;„3Í39SD äKϥغsøÒs…/åëÒjä .Ä‚6Ì wɘâõGödD:3òÿþ2ÑãßÑCÇÍHá§æˆœž¨œóÇÏ=.çÐ)XËݽ#Ô}bv”_rغá*p?&`–ζ%¥6ËèLŽä‹¡ÜD¯\ÕÙj³Þ³¶>iÖ­÷ÓÌ—c†J÷=wö'UŒˆ>¾Þ.ë†ÛRVû.AßàÆRWUË2Õ²ŒžðW±š_M¨ù(¯9›¤¬ÒÖ“qJ\ßþMc¶¥‹ÏuÓ`üÅ:=¹/‹Zu¤gñ×|Õ 3´¾ Û2-Î ænȲ‡Vø:Õªw{š¾+þAÊóû<~Œ‚Ö¿<Mg i ªmênn\èÉ™/MÓlÇ\îÑ>ÛDŒÆ9‘ÿ7ë¦^dÊ?d]ÿ\IÂ!ç\È»æåfôôIs†·­ë_–~C. ‚™"O6ñ¦d'W¿HÕÈÓt­€2Þ>¡«PhE¡Ð a2_ˆi…zÊÐ Ç�:ÑaÒ ¨dO= Ó¡4ŸÉ( 4¦Ù!÷ó¦¥q+z8w»{dؘ~.‘Ž%RTPÒ]š™ÿŽuÃ"š¼Ïˈ͹$ˆ)7½ÞÐJc6/EͲn°ð=Q‘ã>ä(hAEö"îT…§+A>]4—“.é¤0j^\ ULµ‰j^ÿªå’‰WñD ¢zúhXfÑâ?×öq9‡Èðí~ëÓÞq|)*1wžÍr’^_Z7½Rßáö»3|”cýŽuý›}b¬÷‘˜UuPo—Ò DumfWkbÔžÖƒ±Õ†Þ¼Æµ&ã¬î×øVè… Ní6älk¶µÍËŠ.嬸'jξ¶"[[¹îö÷Gm½Ç¢å¶Â_ìý‰öˆ·²=ëÎÆ)ÒÎ8§gÓƒKŒzîÀ¬à‡cÕÕ™êj.6y§ã4ÍFšævÈI/µ”p?¥qYÊ{B«c=çQÇn·õÜhÚü¸¼ûÛ<Z»\VÆ_qaôæ´5õéë–ÊX¹Î«+S(»ûÔT·à/|•õCcµ°ÈÁŸ¼Ù»\¼C-…¿Tþ؅Ș8´;òQ¿ZÑNs·ŽmëF±[r›EɇۋVµ£ÿkm9ûÔÓêñî‰ŸŠ·9¯ÐôS¶Aù…Q×US5­÷ñœ}–’v2>t¯`‡pÕ|þÞµû?Ó"æ ä_` dæcZCÞy×·Ü;÷)´¹‰Ž3çŸRFåTOZ7ÙIWyŒ˜î°ÞOÛ jÑ¥é'÷`j<(_ÅPmaÝØÍWˆ: 6‹xa-ß}×QíO‡6Žö£…»»ÓÔ’­‘>4§ï9ìˆÞš­ä“•qWÙ¶A4j7B©xQȰÇ!6òÑËTË)~!ãPŠH¡]MŸªw|Ÿµ‹P¡yqÛ®TaBøÚXˆÐ§6ëø3{ž ™|±bë[Ÿ}óÎå|ÐûDÌp„w§_GJÞÎíãÏX|è°KÄ IqWµdÛÎXÐptcɶðÕç•l£:‘|CL²{.¢.rûÆ•0z‹Úº-Þ*´¶Ã*|­Ú&fù%íÊŠg¸…(²Y4¾Áá µj[ïûB½}Énq¾fèW.±S£Ûèí´hE<¶Få4fâg„Qá1/xÎq¶u• ïqç™8nOtaïñùLGtLó‰œâÒZ#Ø_ÿ³þ˜u®8B§2.äû;ÌÓ\¥5ZrDϰŠ,ìÀWqE[¨Â 9cØæ…ÜwáSÜŽ„)îôþ˜y†Œ ýŒÉö\Ô³ÐêAô>C†1ü3¢O´‹µgxù¬¨ƒfE¤74Ý^J¦q­0SÔ¾(<?Œ#bp“?Ó>á̪á̦öœK _kÛ¨…ß̱)BÃÄDéÈYnÚ(†?Óó&Ëמ¶™»QñÿRJ±Ù 4]¨H8ËX54V 6ï§Òïœ<!ÏE ÆKŠœÎ¶nªI‹ýFùòm²nðòðòE¤Ö½]%bÍçý¢çþhÕ±è¢tµêhűhY†ZqœÎï7OFà¶Í˜E››÷D>îW«¸Yù’T*ëbµ/–·²a¤Öÿ™Ú)Õ|™kÅáhu&‚qv#æ Öß}Ú¢Ã(k£¥h•  ƒk©uÿs“sX¬3Z×O}A3ü#¼í‡UZ´Qæêtþi¾O--“ž¯ ç¿:r$æØi7pÉþîÛÅg<6þ;-·´¾aiív¿qÞ‹9ßvœ;§·‚{i÷yÕ±žd–ÚzùkÉÛ]i<pÙøÙ)Z¦¡ã/zߥJ·ð’¿»™ wât”Êiû¡­­4‹OוÛÔ.“מ(¼v*?µÃÝùm'Éó/?ôl9ËZˆb{ÏÔæSoy·Sµ=Úº]XÒžçOñX„­´H_šuãÓô¤¢]åo"5égÕýBTÕÌó{FSb)Î'¿õ¦øái 1ù«öé*Zµ“´}1'Žˆ©Š‚ˆB‹JP4Ù”e V˳Hd¯¤€Nú˜<Š.Zwª-‰ƒ;–j)´©·ŠÁµnø¢Ï° ßÒºQa{Þæ­±õþB¥MVÛ’Xþb_Œå xítf­­Å¦¶v·µdEZ² y4ñé‹fô~}…ïEZ2ûõPÞÿýæ›¶JÍúüiµuWìÅÞËô©ë}ÑÊtDz›V’‰`m7ÚÚø|2w0#ÿ”u= SÆ7}]‹™õ!L¯»ßß@Û8‹›Ã]ü£üÉ®œêôœÍ\£¤í˜š×èÕüÍCäã³|n8_ø¢oèyXXrˆûÅnî»J>!±È£•œ¶‡Fs×XñîÞ2Ã}£‹|cë6•¿›Œ–|¢ ­¥ßµªÖÔ!jy L£2>ZÒݳ‡ÄoMí7>C{'>&oµ‡â™Ê“´¬È´rë9pF¬/é­Jô®‡¼ë¡áyWôØŸé»/bߺé_'yWr*qÞ!û³_ ïú³¯S{׿ó®‡â¼+xôNÏ#è¶qžwB÷UWSP{ÿÍbweUft„ú‡Èš¬1ò΢ þ*á“èØžnÉÚÈXêãò]í7]ôfD9¯mžÙŸÖ¶ÆB¦¼¢;šþHtjälZóëKsΉtŒôe4/º!Âzåñ�´¢~ÿsùÏcYº"%Ç,‘’ã´é&¿ä“ð/õ3ÏüÅd¿¾‹š¶Ÿ•L¢íg½oÐ>FK[KztDO5?ˆI%Z_E‘½Ï‰\Öõò{ÿE<‰UØÛ&é_ÓŸô¬áû§y¾—bݦ½˜ݧ'óß?¼‰ï±‹eZ_ª<'rzLóèè†Í“b=Ëè*ÊXȈ®Aô vƒ0Ì—o¹£M¶¨ÕúRa:í$|[Ækçíi Òov©”µDº-ÄÌôsè—ÞöñM6~Fâ8ˆŽç— ¿J›3ôÙ >Zïý1Šèš“E¿¿™cKç3¢R+³øÇŒÄ>³Ý3x/¶Ü(GÜwK»2KúÐÈbµêD$ߢŒŒÞÝàu±K&‹~ö6ËŠ­›…|ëf›87ÒÄÖ•›&¢6Ê[5&üR´03Ê"r¯þÜIWãF-9ÑûSÎÎÈ1;xš“I?ÿGênKŒö[ê§&òÓ?ÇH KN<*¾hʼnè*„ôÝ0¿9ˆò´“{Ò zE\ô*-Ñs0¼Dv§[öè»Z»1ã¥q ¯8&Nafð³~é(OÍœŽj•¶hÕ Pµyû8KGðmìtÔ“Áï]Ï÷œv÷]ÉÏ¿ £Ì±üuBäf‹’.>YxŸ¦íbÙW4²'g¤{”ZtŽä_Q(Êâ»smyƸuÿœšV …¶…Žƒ\Mçº-khwk§%ÿPøT[dN*²À%Yö¢¢>ª¨ðu¯¨WTÈ+r{©gñª~2›¤33j£ÙU‘-öm±qó{üçgtà¹jfÿ?‘˜¶Èòß ‰}©”yªdLxGJ‘y†Û«h…-çÈ'ÇÕ£Z8³R«Ê€hU¢YÕ‘Ö±,<&r !!í4ùàÝÞûº!Þl- ¨Ü ð�èP|àvÀ<™N°R‚žçi3~¬}j}µ³×a}­³7Çúbgï$ëK½1æïë½—Æõ^ŒËŸò{­t¸Û§×;’ßý@=YXÍÐîȱ춪np©ï€No«:AÛ¿æ’‘>‹ru¤ot+LäÈÖK#}#” {”K"}éÊøH_¦2.Ò7F¹„Ê‹ZzûÞ³ôžøÀÒ{üO9˜XèÔ iO:SO’„FŽM¢@•YüL$¯0£M~Æ‚ ؤP•“bUާ*ÇS•ãcUŽì$~Õ²÷ë÷ðßñð_{??"Ùûg±GÂû©ÿþé, öÒiÝÊþèœ~z³oUßÖEKû#M§-t_é{úùIŽe?zý"ä©ì¾ùï\vàuÆ&‡fÛËü«] >Ýíjhðùëìׯø~ÆV¸V»r\þºÜ²†o«¡0XnDjÉ··IИË)Á°[ ½v·Öç÷…=ÛïR|«½v¥¥É‹Çvo0æ°b—ÿjÅŽJn—ⵇš\n¯½6 †(«°—£ öÅJMc¬|Aál{­Ë×àõPš;èåYõZiC©ÍUþP¸©)T¼ž…MÞ ‹Zc4|q¬­Ô0»Ï_°ûŠ7½Y‹:\h ì±}²'?+h€QfQ¸¶ÖŒå ÙÃ!Å^vø‚^·bGïê]`…ËÞô5ú8[j\î•ÄwW0èjAû‹áoI FqùüöFoc ØÂYcÊÅk¢þVùQt ÎÏœ<ˆ³©áî@cn(ìÏ]áwåÎw…”Ór¾ÊQ—»Þ®Y=XÌy]]!P‹õ”·8wi�xYas£‚^°ÙÏÙ|=„GhÒ¨ïRgC DãTïò{¼ÁQ²Ž…a°¨V²'V”.Oti°×]^Ikz f F½+„¡ñúíu®`«Î ¦aPÝ.G‹D«CC‹I‚Ì¡¡ZŸ×3ßç_Éͤ¬Ëñt7¸B!TÖèÀ80$ŽÊ€Ï¯x“è~Q9X Ø0¼|M^ˆ �)*›;T΄¼u °-è…pyýºB&e_ÀG*!o¸ÉÆÇÄ £Ô<C”ÀXö|ƒ{ÅDW`z°UAÎ|³‹¼®&Ø“‚ø6„ >w¬ê` QóÛ›‘܆œ¤'I¼Š¯Kï’‘¡ê2÷eI@<.æFC’²÷Io‹V¯YB[Jcì¬5ƒ²ÊhÞP23¿©e ~ÄcĉòêUˆåe¤•ƒÊ›Q]*¾ù½ÍeP6Iü0‹Iý·B˜éáfÏÏ-Þ°³Æçmú”á7;>¯î[·×­èÌ~ Õ GQKµ«!Ìë4ËB±4ã‹t5©»…Ü—…†*_Ï>\ºX5ŽøjRÙ-s3¥…-¸+^’õb ¾sýCØh‚že µO.2;uÛÚ¬we¨úKÃ~71GÒ¿q¼”&äïlƒ°_"^üoó!åÓä.Î .á!̼` Ü4”ß0â‘é;pmH]ZªÇÍCЙj‚~ëždÎ<UþdÚ2 c7HV;¡]«IÛ‡2$qÙ‡W âäbRm­ÏéNm†Œ½¾o㫊kKÌàM*--£¦ê×PBÙ>ÿêÀJ¯9¶nuLd �Ÿi„›ìå…K—è%Ñn²n7¡BvOÐÕLtºkiR†Pz%$bbÆüSä4GNƒ|!)Ç&aŒÍpÏ̯q×ΘZ3}ê ³®Ÿ–‡y5×»®÷¸<¸ã2=¼y&‡ EêfÛ!Æ$Nå#Yne0PÓàm ñÌb¢„Ⱦl.gÁ¨ht»O7œ6†ý¢•“Cú´5{²'Ç4_„¸9Ü‚²…rzÇÝ+Ó'×ÒW»ÜnoH®Øt§˜&*$&CVÖŠ¨DÔ’¥Ùç1å«ZR:‹- Œrù[L3ØìF×ûä4ZZâ†=„ÉQ[6Ùs7$'6ûGó1!ãó$L:W|V‹þ+¬!à¯óÂ`+ C%g±ÿŸîófr¦Oã.„ÍœÁ1\šä|%…K`7’ ½™Ñ´‡%ÌA˜1`F€Ît«–ZYö�®Çl2SĘà=tòœj‹ Qd5s…•À"H¿YJACÊô(j�» B©¼Ñà¡3E,ÙC³‡ÉQ\éºÅh*F|Kž ˜©ÍÆ;ËøóTê\ͲËËËq&%™L{£/¢¶‘Ý#n:!– ¾š®fÅ 02wÎy^©¤–Ü l×L)s‘â"é5Ý‘—gojp)ðÂNwÌpLe‰` Q¢ Ç,›Ör+¸f‰ýü¾@®°§&¼Eñ&>+®wŸ-®‡ÝM|ˆAH|4ê™ø¬”7ñá\®Ãæ§¼½Õ¤éKî¬,1=+ ¼.¿ù Zmæìr+qñ›WÙ„À˜žPSM(o¥  äôÀAôÂ@d“� ²h™V—Æ›!SÇ¥Ä-S‹ãR©‡2¡(>A0C¦Ý—Æ» SJãRDgdÒ\Ò‚«¤áƦ€Öš+îH|œŸ[w‹J@ ›V‡ŽsúeET<õ*ÄÄc‡ŽSz MEŽDBóâŠQq°/uJBÓ#*Ê>/+â^–²×•.ZÛ“f#Ä„9’P&ž‰Å<΃ÐxÈ{[&—oMO,çÆôÌ!Vg,¬­…'d†îIºÄ’]YÌLJ)©“žäe,¦®’87HË™¡Æ’6ñA\ÊLú-©“žäs™Yó%}ò#™á.&åÝÁCzÎ5yË»*ïcº$q©‘£¶Ë[Þ0y/*•ˆIþZBŠ·‘‹E0Ðä *-Œ¢aøÀ€G7¥B©LVYªtÌ* \Ze‡É*K«`XeŒ¡Ãdo•ù³«ÌŸ%Zeþ0Þ*óG V™?K´Êüa’UíåV9É%˜«T¶-Ñ&[Í›hÍ’l‹ŸE6"^ VÆ?1eü³Ÿ%-Í‹à™»÷¦ ·ÉéöÕš|1GT]yN¦¦}˜<FÓî Я€ÇpßšiàÁ ƒÐ|˜kp`ƒÕÀ[  àÜ-Ÿ/|3íú„sÇišP5Nà÷Ž3Ò~Žû7Æ ]ÆéAh&ÿŸm?ÁbÔñú%šv'`3`>àŸå•àç€çLø@°ÃtÿJBÚÃÈÿßÀ6AÀ‰K’a çß…ŽÒÖ\¦i+®Ô´BÀhÀá+ijáÀ/$í<\7^!®?º\Ó¾ºLÀ?LԴ܉âþ7CÀW)€ò?j×´§'Cþ¯Ò´hãâlMKl¾ZÓ®ŒÍÑ´mÀ§€æ·“4mÎU"ÁEòþ ÒÚq?éc�§Qn’ê›Þûi*Ÿö˜Þ3ùØ[]½Éóx×ð×»q´æ©MŠŒ¦—ÚI¯‰S×iz :Кö¤*¿Û®«7Þ¯ß*ÞyÒ.Šï”¡`À5͸UÊ�ŸÖÐ K¬iðwóâEkÜʇW/þ:ZS|~LÏë¾Ó;\YÒlþîW¬GÑzY*æÄ Ú³§å¤˜‚ƨ’8NäxÙþ¦íd·iÚ¿á:G^ îÀ}“ úL÷¶ú£€¯Ñ´ Õ´'�¯úφÅ›5í]\§!OèQq%8ô°¦•oÑ´ÀzÀs[ijáÀ’väcš6P h�Ð>¶t&6gðû4~ŸÎa„éþ|M¦é~´‰f”éþ<~?‚C†éžoLfç˜îGšîÅß’ŠÊ¹e‹XîjW0Wilb¹ïêÜP}#»uaE Ëõ*îÜFÅUÃr›‚ÜÂ~%Ä‚æx×xÝ,7ä…‡×0y­ Ù}rïþ‡òoÛ£i7"�?ànÀ±ÝšvRÝW½®iûöˆë@@é3%^°\’V°uX©#ÿKÓè?í¸Ò>ý¦_i}ÙìÝç!G¸nn‡-D§Ÿúµ¦ÑÏ,þ¦ÑŸv ¦ÀõÞ5m ®éïjZí¨Ç•¾Õ8ó}M;Žk'®™`îš?A¦qýWúh{–ä£eí"f¹7ÃrIfzú ŸÀ,ßÛJ…çglQ4fdÑýé›ÒDíæ:†ôNszid$QÐÎ×倇ПÍés#i”w õ }\Ÿ"/¥Ó§BèûySz™Q6}«k9xrOвé;Qß ­ß’ºlúrÕ[àß”ÉeS:m= ¾¾–•ºîH¿õmMÛ”¢îJ¤í@ÚKæ´Á3ÊÛ„ô›16$䥴‡¶i/˜Ó˜È·ê|þ<Eý@èOÖhN+¼Ÿ+).0_ûûòR ;I?z™…õãz.®c�Æ.dr37]f‰Ë?£Ì¦®½ÕÂÞ¼ØØ èìdƒnÀ˜� ÈØ�™€ @:€úAß888è|Ø è�´¶¶ž<ø `Ë­–z=øåúf,›ŠJVm~6c´ŸwÍ ·c$o4Û/`ÜZBVÎ z²|´_žcK‰…Û·uRï@ξxc²¸ã Æ.d‚ø"&lçYM <t¿�µå8®¶Ë}Ü"Pòwõè»ým™kðm-úý àIÀ³€—ï>œŒÊal<àÀl@9à.À ÀZÀƒ€'Ï^¼øp0êä\˜ (ÜXX xð$àYÀË€ƒ€÷ŸNF]‹ü€k�³倻�+�kž< xpð>àsÀiÀ¨ëp `6 p``-àAÀ“€g/Þ|8 5ù×�fÊwV�Ž–ZØ=Å®‹©ô±ü#èÐTÁïýG»Åt_`ºŸ+BÞGp¿÷y¿\Þ³yÅųíÙóTåØiùy¦=»ªN0,°)yaŽ!b2SÎpLuLQrL§¼þÿS~wÊ!8ïµð ÅR‚âZ¯ßÕ»BõÌáiñƒD\• sÔùù6q"-èm :qÓÔ 0-•2‡â]ƒÿiÏ-’—âbo½“ïtÖ{‚ÆôV;„êÄe…‰”‰à‰Êw5úܨ; ðÿDE¢Ðšò þ¦Éΰí }Ίl(ÙÇÃ2F2]êñ"c0}ܦÒs²³‡F û:FÒ½Wù3¢#»Là¶õéV- ÛJtdW ÖZŒzGH¸™ [L÷d æÈ:,²^ú«�œ'ó=' "s?È®W›é& ¿0B>×露|ò+äw¾oâ›^ï]&:O¶�ÛÈx:¯‰nÝ}t6y]i¢#?G0>E½«$µ{ h–_™L×b¢;ÛD°!Ey›LtJž…ÃQK2Ý#’Žø¿6uÉt‹œAÄÓýÄD·tË ûWtžé–Xš™n«‰nèÖ.ËÔ>»¼þš Þq¿ºAã~‰ëåQ;^6·ñÁòë-lVB½¯™è(ŽØºù–dºý&:ò7Gg‚‡¦òôö½)ë'º{æ7 ^KO¦{‹ºBD·Õô@¿½ÀÔ7úëÃ$åK¦ÓÇVÿ›q³…=<Fè3ô÷¼„òöÞ†Øñ‚äòRý–×4Iud¤Ž‹š·_ ã¢Òy n \d|#†‹¹(éªÀÅ<•tRàB²<1\ÌoIÇ.澤Kóâ-1\Ì™OÕñ1üJ: p1¿&y¸0hËc¸Ð^O ]Ã/õÍÕñ‹Dþ³Á-1ü{üJr$ð‹ù•äEàfKDøØüŠüÊ|RÜ8¦³/µÌ¼t<1ÿí x4<ÿi¾=ïHÀ_KÀ—€Dƒê pô£Yb¼i(ï¨b¬DâÄÊŠÅ©'«·¶°$î1uˆF“¾þ«mŒ½>B¤?üUâ´á¯Hú:‹˜;ü׫PÞ2ý}ªo»ð„Ÿ‹r^8ȘOâôáàö\ —¼ ð?­ ئøbà'îgìR‹ ‘ ‹9žÄ$•yáQÿ¸>þÞ$Ëÿð;ƒŒýPâoJúULÐ(ñÙÿRâ—K<]¯$~±Ä?—x6ð´,QþtàÏü™±NYßI?SÒ/þã{—LJw_÷ c×êýþíÆn”ø/{Wøï·#ÿó:¿·1¿L?|nXïß(v }‰¤1¹L¿%=¾= ×=l”¿ xåÄ|Ÿð{¿±á?MÈÿ[à7 ˜¨–ôoÿ‰i<?6Ùt’§ÏÓãùÝŸnد a¿FÀä…þÁhO6ð÷«ÄI= €?d´Ç |œ©}¤­ýBÊããÀ'C®‘ò»U¦ß$ëßܵã"ë{x‚žfYÞG#ãÛKßù­ÄxM•éa’GÞÁX MàS€ßº‡±*Y^%ðc&~ЧxrŸbÜQz+ð÷戈„?$L<÷d=ÿøÏl\"Ë{U¦_,åýÀ'â^öï(ðmp³dy#„‹`¿#è¿ü؇2}ð /í¯¾ú�Ú,ñuÀ'˜ôñeyI~l¾ÿ9Æc3¢?�üìsÂþðÿÈeì6‰[࢜.qÒ’pkF¼<M~ŒÇDÙß àÈûDI¿xÃgˆG¤¾ùdþKåxß|Ú(Ä’~ ðÃ&þo5_$ÏIÜ#ëø«üþ3ðQ¯É²¼3À§þÆþ·ÄGÃÅÖÀž-–øLàOоX‰ß üµfCžï¾ üY*ÓÛ_—!b3ÂüÙuŒ]&ñÏ?¯‘ßþÓf#}æ¨xù¼mT|ÿn—ø 2Ý |7úóÅh‘?"Ó÷¥‰ô'ï2ñë9àK¤¿µÁ¿¼üÚ»IÖß <Ý$‡€ÿ³É¿•å?%ëÿxìåõ2}Ú1U…¼Èþ_°]Ì‹¯^÷OA™¾ x!‚ê«tûüwÆ>’ô¿mÄ7"¾y øÖÄü‰Ò/þ‰ÿø/ Ÿ·H\!+•í½�ÛjâÇå¸éé„ Ëú2`h—ò7Gâ×ÉüUÀçDŒñ[ ü¨©¼qã¿ÏHøeèÿ8Ùž#Àwn4Ò-Ðãýë ü*à}¦òf¿¼JïQ¬|Œ¡¯äsj§¯7òGßó¡XK'ü)I?K¶¿øÆ9,¦O¯¿wƒ‘ÿàO¿Ž9ñH©{ %çJÿüÍ¥F{¦Ï6ÉËÍÀßþÚ\ƒüäH{ºò|)ßR>[ßý^!é??^>Ÿ®,Óãø™¿KöçEà³æô¯�<‰yŸ¤ÿ�øR“Ñ€OyB¬É~BÞñ%jŽß$çôKdù ï7‡øÇω÷Dß�|ˆýß óO“ù“øß|”' 黀߰ÖàÿÀ¿{^)ÓÇCé¿÷†>�ÿ¬1‡Ä—¿þa½>ø9áÖíïÓÀ¯íǼk”Hÿðç¢Fÿ é7Kyÿø}&}eš#Ù—÷¸Á¿[€·A %}…¤¿VÖð£0†ËôMÀ›¡ß $¾øW[ ý!¡>¾A˜™n–[ö5xÄÿžÜº_{Ê4ÇtÇLñhŠôzs}ÓgÍœBI¹îP8×T|ŽÅ —®­ý» ñ£/ÔäRÜõ(Dõ‹[}snéUrˆRÅéaº­­å oô+¹õuµ¡\¥9sçzW{M´ •Ûâï”Å[ì\Ô‰,¹¡ ;wͬ™¹¡–ÐjÔL[œî@£3ö;Aío µh«<>iÚrmÚÛ\èñië¾ßÛLeĨôÃÎÅw.®fôÒš¶®T Ê\êó{Íâ÷Ô Åfi}«wH ºëƒsæÌ›_VTìœæ˜Ê« ºšyÕƒÕV‰F{ƒ«M»ê®Qß>îÌ£ÒóøëüØžlq †Š3�ƒTÌ÷§Å¶|¹‚u!æ¤çoƒ¯ÑÜ~ÒØ¿?8C :’ ¯¹:™UŠãQS­¬5?B¼þÕqÙ|u~WƒùIù‚2çBÿü€+aËZØOÚâòz•¡ùŠ*å>º(8œÎrçôÒ²²aP ¥:õÁ0K(&Á’p6ÂtwÍÂZÔZÄ]Mql¸RynªâÇõ‡UɧÊã8ù_Ào®-épCLMÇS< ¢;â”9;—îa°¶\°V×n]éo¾Ø698ܹÚÈð˜[&í‰ÜŸÚ$öÞ8Êw ot‚ô:l@%]uæC2aÿ0Ljd’Ÿö2m{õ®Qh©SìÎ('}¢Î"‘Š€<û3˜iÕ»i4Æz˜âÄM‰«&TR3nËÁ·“ð`_ípª 4yý—½XÜÒXØôòJ¹éÈ“>,ž_³ÈxÀ—Áo “ •Á€Â?p1°¤… ŒpŒ3ºë6#ÈF¼¨Ñ1ÁaJm5f Æ)-r€¢‹N.Ýä­¸t&y,aK%1‘Æñdæ h0g<éôŠÆášÞ Ò…Æ1BãPå £ÆIc£6¸Zó ÔC›•¹ƒØugž¡\u1;tÃ]ïñ6 X JªªxX.dñ°¨¨¬ZH‘ŸŽšÃÓ@ï<›Ý¡¯8ƒD†¬l.—@!óÉ¢Á¥@?)ètÖ„BNxô  °âe%ofÒù·zW…š†¥PÈ\XYV\ïu¯ …œ¡|è°Â�‰ýøƒ•§àDéäo„Wø}z¸`'=!O—2‘ûºwtíoô6º›ZR²]ºOøTñS¯¬6!ÀÊc^¿gø+&e$Cæ¨qOËT4ˆx†¾KP+q0ÍMl«2ÓÉGq–Óét¯qÑèð€ÄÄ#Çô¡+)••˜m! ù“cUéàô“›C\>h…Ó–çbÄnkÃÒéRq“@žq‚ŸÜòã*ä_VÄ6LJ9±óI‹É8½|kBìT/Yß¸È b7¨R ÎÑ<Œà`e –,ÆŸ´„)µGžrö…ÐA“û¬pR|fDêµäÞÌJEÕ×ÓñÚÃ0Ûeƒ‚ù„™î }ÜfÕI›5X©[:Yo˜£0R¤ˆ·ky‡‡iëÅ]µÜ—ã,_í\$5‚Ÿ óâ‡ôúb¥ðƒˆ+éÌÚ°ÆÍ b©ÐÆý¢–´›gJcs¢ºF„úÜ:ì¿�PK ����v©Æ@���������������com/sun/jna/linux-amd64/PK ���‰I;@ÊqK¥��ª’�)���com/sun/jna/linux-amd64/libjnidispatch.so̽y@Tå÷?>,£¸ààB‘¹UVÚ %¥Êè`P”¢¾+CEÜ \*“„)®·)Z,[ܳÌÒ\RQSGQSCÜPSÑÔîˆZ!®ó;ç<Ͻ÷™ôûùëG]ç>ç9ç<çuž}¹÷N5Ç÷õ÷ó3¨†§ z†…£9=<Uç‰6ô4á߆ۈ×hhøoßK~n¿Cý‹¡&pUqzÕK­Ý~+š2îúƒ›œ?—ë6”ÉuÚÚí·ˆ'£þqéÄÓÖ‘ðûooáùû^ Áí—ÿ^�¹&àóüSÓˇk¿_ ׸&À•W&§ÿ×<~¿TÐ1®l¸æÂõ\áZ×2¸>€ëkÎ÷ ÚÇïÀµ®µpõ†k\EpIp™9O!ÿ}®p ÒLä¿ïÁu?\íàú ®W8}%\÷Àõ3\‹áú®õ<îY¸ÞK†k\_z»…þ¢áZ×[<<…ÿÀõ:\3á W \ƒ<d€ëN¸ÆÁõ9\¯ÁÕ ®Îp­ø¾€k,¿·Âµ®4þ®Áüþ¸lp­‚ëNû®ùpóð·ü7½<êß(¸ºÁ5 ®ÿÁõ\ßÃõ\“àêwùÿ?ü…z„;÷]áj—©Ùöpu„ëQzwƒZkÿ{ŒÿÞ W”Akz qO'þÛ®'<äï…«¿®þpEzð¼W8\¸îèCàz.l=‚}ØöœGx¼GøUþ+Єû¸rùýp¸úzÈ¿W2\cx8®žpõ€ë¸Þ…+^àO‚ë!~ŸW\wÁ5®ÛázÓ†<knƒëaN ƒëC¸>†ën¸ZÁõ<\³áš ×ûœï¸†òûÇáz®ÑpÙáÊ€k\máú ®ép½ × ¸Âõ¤[Ä¿�h-‹{ù¢7× ½…¡['?ô–†hŸô`­}w§·2äõóE7iý;=Ä ø¤·6Ôû¤CmyÙýCõp_ô[ Žîôg±Î57,{›…Õ²iògôÚ‘,ü §ÿkàü¼QOáôE¾ëàûÍø,\èÏ~Ϫú§²p7Î?¦£'®p·§ ˜Ñ»T°°Zö—ùajipD3?‡qz’Jq§¿Òе1êßÓ¼ÿoÊôSa…¿¿yü{w~µß½›Ûß%•…ïäôÈ�n×Ïéáéå¿J3wz%/.Á-™ž î‡ûÛ±_,~Hwüοª~à~ çþmœ¾YÅõ ×qúkFFŸkcáËœîâå!/Ÿ…/ªz8ÞD®¹j/¼3þMµ‡ëÉšÆÂj9iÖ„§+±pŽ÷®§v& ;9ÿ<žnô8Äùc<ü©Ž9†rÿå±pwNÿÓàΟÍõÌòÐÁËçk«Õ?5ßûq{–ñ|çÅËpsVÞò<ÊaU°»þ[øïîêË”ïÞíÕ5¿Þea5_z¨ùÂíQÇió=ðªþœÆýãÈcaÕ?!œÞ…ÓÕúø‰ç ¯pí¸~åå0œgìÝÜßy¤«öÕ?p;çòŽm§gzø_M÷§VŒE¦ø ®/ÏÕ³°êgÙCÏ‹ÜÎ7TÿŒbáô�öû ¯_áe,¬–C+o7†y´’¿»~µßú»/çóYXm¯Ž{øAn2ðòÉ3ª§/âå¿ús®âôÁzxµ1ìæz*¸žÃÜþ;<üÀ›7Ã>Ž7z7 «å0ȃ8÷[¯¿Þ/,âñ8½š×ë¯8½#oŸk¹þ?8ý^/ª=ü©ð~ÁÀÛ7uÌ×^mÇ8]­#¯q¼ÃbXXíóãù’ÅÂjµéepÇ¥ŽÛJy¹ŠcöøóòÈËU-oÿÿâükÔvž·{jþ~Ãý9÷·‡;´Xm·oeú‡sþW¹ºxøa^í@/Ž/X˜7÷† ^Þ†y”· j{ËûÇ8ÝÂó¥šwj},Së;Ï_µÞ=«¶3,¬¶3kx~9xïök+¸~G†+”—Ÿy;_-³°Úý®ê/t×7§ç¸ó_jÀ?é ´ãÕrµ€…ïoÉ~cy½®öhŸÏ·Býç]‹<ÆE÷óö­ºã?Íq=Çóq®G>rþ.YXm‡±{íÜÉÏðr¾Œ—󶜞¥¶o±ð?œŽÅëN]7[†™\Ï0®'œÓ?õðÏ|Þ>|èÑŽ]à¿v^®Bæ²ð!NÇéásXXm— ¼]fá}œBtïñó3¼^,;ÉÂ÷pz§'rº:Æõ‡óúÞ•Ó7ñ|œë‘Ý(pý†R~˜›Õʃ_m7ór«6ð—8½O7Ú#ßTËùtVËó³úy53<Íó+šç/ž†ê8­È]ÏDÕž÷Üé#¹=yö\áå°‚ÛÕzô•ªÇÎÂêx¯”×ߢS,¬¶'¸Dù ²:M:£êùÀÝž©ÜÏYå,¼“/l=ÉÛ¥.{XxïÒx»ÎûåÛ¹žÝþ —¡·;®]-=ò—·ÛI äûãtUO:>碖«vÜoy¼þ^Wç) ´3¹ýŽÑ,ü/Wo5`Ï5®§bŒ»ž²@wþ×øïR^ïjç²ðH®ÿ?w~UÛ7Wm ßËéÏq=‰³Yx?§òþ…£àg¿ákÞ?ÆÞÎÏÓ-àåÍàQÞjßórÂ˹Êó;÷ó0^>Õþqª‡ÿmÉÛó<Þß½Î韩ýÈ;,¬ÖÓVêü…çïœÞG_|ÆÂµœnóȯæü÷.•Ÿûç<§TÇi|ž¢õMzÚðùZŽÚÿú3¿­hÁè =ò±3÷óïj;0Ã=Ý ÜŸEý®©t1xÿ=¨ŽKg±ðANÂË­×;µ|Nö°ÿ~þ[Çëõ0>®ãÓEZ;¥v†7 êZk žnªýˆ_v’“GËŸœcžmMN6$§eŒÏ�bʤáx;|lÆë©†äþ’_L•‘cMÍî3vxNNjŽalæøQ£Çe¬éÙ™{O~nø¸TCZZFrjvvf¶!g|VvÆxkšaTªõ¹áÖŒ ©1#Gf§æä a€5;7Åš›­Ñr2FAJ†äœT+ªŸ:±ÿð Ã-cü(CŽ5{lêxÃÄ”üáqƒy$&fBb©Ù‚$KÀ0.u\JÖdÐ1ì2¤MÌপ\}†;bxÊÝF®ÂÉ£­“³ ¬ìԬ䔌4 ¤€ Ý`tr¤úè#z8‹›áÆÐÓ=ñ˜ÎõÇøÇz€÷ÁØñ™Éc3SÀ¬Ìñèrk² :mlæp«™™;bl*™ž’9~BjvÓpNrZvæ¸dUéq`î¨Ôì4|l.Ѹ/‘¦æ¹Mw*0©¾S9 ÐX3¨° LÏN>R÷é@žTß¾qx‹WÂð¬¬Ô‘†ÔIÖìá)Öä ”r/0cP ÖL&Ž™•œ9btj €ó9 ¡ÕL% Ñi4wLÈÌ©EeSf9øf\rNîøäÑã‡'3UÉã'dŽIUKPã\Ï3CgÒÕ8_,˲ƙúR67ιxŽx¨¬7`„kˆ%òÔšÊZµaDªnH8-;ÕSI7ÌÌ‚ ?r,ÿa KƒŽÍÌI.öÛ )ãG˜<nDæX`Í™<®AF*ñÉÉý“íw\}nŠ+ö¦¸úÞWÜMqõ¿)®pÇR'=Ÿ|½äÁCgiLùJcjÌUScžÒ˜s”ÆÔ˜Ÿ4¦ÆÜmMïÉÖ†K¶™éÃ.¿ÉzÓÛ˜’ØŒlh0©Þ¹ii7àm¼ÁF¹ÑÖ…uE7Ú®`OŸž™Ý¨Þã6Ä=ABê¸ÌìÉq4ê÷œø=çÆ^Ϲ¡'snäÉœÆ=™sOæÜÈ“97ôä8hl3Sm£ÖÝzfšÞ¹âÐ7܆2=݃ž#Ù4ÜÈdXãbs+)ƒ¡!Êœh>z‚ðÆ«¯#T?n"s³3­À#’Œí©mÍI¼!iMÍIÍž¯TwóÌXTnF#»ÄÆ1Æ$ÆõIOM““ ½ásqÉÏÏ„ñÛèñ#3r²†[SÒµŽš²‰3%Û Rë…sÇgóÉD#m.cHHµ¦gŽdCoèÙaP•Ì ²>2çd$¦Ü ƒLàå µ8â¿6–|ã¬4ÜÑXE0Ö;}P *PלªuJúÈÔ,/w²¬4O—kM„ÎãAÊOÄœÉ90_H3 í‹; e÷¸10)þŒñ4WJ?Á:~ä8¨WЦAäwéÃsà.3Ëjž’§z, æ-ãrÇvÂØx<Ìj`f†A˜uZÓr i4ÄKÆâ82ulÆ8œð¤¤Ãd‡æÔ‰Øœ1 ZÑ ’ÀúñPÀÆfŒHy('ó¡Ç É©#‡[‡C*#rrØtHãGN•–ó°u"$òplê„TsŽ»ž˜ó0äÇÃ#r3ÆŽ|päc=O9ó0hJ>è7ô‹ëÝ'ù‘‡yÈóDŠøçGÿ©¿ì?~á¿þn1Þ\þ§A {ëñEñwãöó’÷¶L¤ûûRmòm«/ýž|þ>ì÷7øJÇ;]Ý^5^õpu+}](·}F3´Þ¢npñóÚ9¾ äA¿ÓC<è=8=̃Îé]<èOrz7z §‡{ÐûqzOúsœíAOât‹ýNOô §púúhNæAÓó<è–4.ô Gsz‘½'§Ïô ÏMeá¹ô"N_äA/äôeô<N/ö Oât‡=‹ÓË<èéœ^áAÆéUô!œ^íY8^ŃÞÓk=è]8½ÞƒÆé†WÜé!œäAâôºÓÃ<èõÜþ.ôZNïæAW8=܃^Íé==èUœíA¯àt‹½ŒÓ=èNâA/æôaôeœžîA_ÄéYôDNŸäA·pzž}&§zУ9½ÈƒÞ“Ógzл©å߃ÞE-ÿô0µü{–µü{–µ|÷È/V<èµê9·åÿ ×çAS÷‰<è§9ú]œ?¼þžôΜßÒ�¢½çÖ€ýéôÎ?ɃžÇùó<èqþ¢øgzУ9¿ƒç †øÑÆ—ÌèM<èe¿xf·øU_¤WüSz­À/Ò«þp^-Ð_èŠ@¿E ;ý"½VàÏèõ}º@7~p£Õùûä*!]‘$ðˆé ü"=DàE ‡ ô¯E~ÁN‘ÞEà+Ы…tEz™@H Wô§º"Э¢‚=«D{úÏ=šÓq¸•@·tñüw¢@Ï è· ôaý6ž.ÐÛ ô,.žEŸ$Ð; ô<ÞY  ô.½H wè3zw>W ‹çÇ ôú2þ @/è t‡@èeý^!ÐÅ™E•@L W tñ£"ÐÅsõµ]<Ç]/Пè†a:=J  ôh"Ðcz˜@ÏÔwèâún]|Î"\ ‹Ïôèýz´@V [ºx®?Q èCz’@&ÐÅg[Òú`ž%Ðÿ'Ð' ô—zž@Y  ô¡½H è3zª@Ÿ+Ð3ú">Z /èâ3Å=K ;úk½L ‹í[…@Ÿ ЫúD^-Ð_èŠ@Ÿ¨èSz½@K †ëô<$Ч ôþŽ@è²@ï"ÐßèÝú‡=\ "Ð{ ô=Z *Ð-}¦@Oè_ô!]|l˜@Ÿ%ÐÓúž%ÐôI}¡@ÏèßôBþ­@/è‹úL¾D ÏèËú"¾\ /è+z±@Ÿ[sôb^&Ð× tKþ¹ å#àÅøÏݧ´5¨ÄùÒÝ­ ®»_‹àÏu7†é 6gµ þî~Ã8qVP¸?†qáÈé po ã).ç2 ?a<5æœKáG0Œ|:‹(|†q8áÌ£ðFsYnaö:‡Q¸ †›a8‘ÂÍ1ŒÇ{œÑöÇ0žÆq†SørWãq<g _À0‡s†Pø,†qÈâ4Pø$†q¨â¬½ŽáÃ!ü®ÄpkÂOánCø)¼Ãm ?…×a¸á§ð ‡~ /Æð-„ŸÂó1|+á§ð#üþ÷~ OÇp{ÂOái¾ðSøu w üÎÆpGÂOáÑîDø)<à ÿ5 ¿„á.„ŸÂ/bøÂOáþ¾“ðS¸7†ï"ü~Ã] ?…ÁðÝ„ŸÂ÷aøÂOá;1|/á§p{ w#ünƒáÂÍ1|á§°?†ï'ü¾|„ ü¾€á ?…Ïbø!ÂOá“~˜ð_¥üÇp8á§p%†#?…w`øÂOá-~”ðSx†{~ ¯Àp$á§ðb ?Fø)<Ã~ áž„ŸÂa¸á§ðt ?Aø)< ÃO~ ¿Žá§?…³1Eø)<ÃO~ Àð3„ÿ å?†£ ?…_Äp á§p ÷&üîá>„ŸÂO`8–ðSø › ?…ïÃp_ÂOá;1ÜðS¸=†-„ŸÂm0Gø)ÜÃý ?…ý1ü,á§ðå;!Oø)|à „ŸÂg1üá§ðI ?Oø/Sþc8‘ðS¸Ã/~c;j‘¡]Z,6‡ÕßUAÍh‘ú—õ9ØcH²H§sŸ´äG­ì !ëí9j>èQ–@‘« ¶ä;‚,’q&P\• í¸±È![eî©íÆ! À¯¼$-­þOSo ÐÚx?îÌRne±G‚ˆ8©¤<Æõ{97!ßÕ"ÉôÉ–|WSÓ' Ú’ë_^"]îy÷€¯@ž§‘jv¡¡i¨/¥ÖÆIWã¥qõ�±‹Enj‘þ‰“ö»Šð€œØÕ"‹—þp%†Cê©Ä©㥳®Ä,=l‘A¡œZ/«—gbÿ„6bzb`¤ô!íz°¡Öòö¹èhò/û%ÖÔìù—ýM8*°Øgaã#UXì¡‹ÿDoœplÄY²E:®Ü i±GÎ¥ˆÃ`€bnH ’I­/ÆÎKõ§JŒp ÂL3ª=JjAép(”ßöÈŒ£šÚ¿®y©ÕÜÁÔlp÷¯­³îVô¨zúŽÖðžz&•G8J<óc€EºBåÌVg½ƒÉ-9WQ½?ëµÁF‹´ýårçF„ÇË  šŸµäÓüs!”‘Wâ¤AÉR‚icja‘;t—Gv r‹xé ª•CÏ"ׇ˜9�Âw¢ÉIÌLJCÀÓ«†ôHÊ$«–¡8é?W>’ ÆI[­Ï@B òá;Qå_ 2Âáú°’Š‘¶³ÔºÞ¥F¹Š0 ñÒIk縔íñö®!ñRµfu¼<²µkÚ²ÕwzÊZƒÌ~u  fãXíÅ‘{ŒÅ|â-Û,fóžÆX2¼YZK�°8_qaÁ@®'½“XÃbnóŽq@±¹|Ëwäïg©,›#ê”BÈÕˆJgèTëøwœBõs V•*p,¸¹Üm‘’*,RB¸;A*…tNP6³L%T}[vp@ÔÒ.dÖc–Ø-ÛM+ü°äk,jÞ`„.äåW‚•>¢2^ªCEç%Ø\i‹µ5r®ÅIM¡m³ÞiÉŸRf°By6—Y¤U°PE¼nI)±ømÊU•«Ú"U@ -E.Ôµ)ÐR0m¡×t�Â"áÏ}#¬ðÓ~عA„ŸzEÙTxöÔºŠÊ4”¦‚®IiCêl­‘Bûz›k[§¶§rhgh÷Ðìî俎fA§Ñ”yE¹¡¡#à Q•€èÅë¢0Žè™ëj§Q¤™ÖǧiÖ1ܬ ›ѸYܤ Á¤M×4“B¸I?� ¢M¶ l«+%KèæNÞNÿQwúÞшNïÕÑ'²9²6D6Ø+ª!‡+ºW5tÝ8ºÝDâö¬íà»\»Î˜€åý ÚTâåfÓ6ݦjÁ¦ݦ.ܦ'®b«WJþ8h ¡1½:úº� êvß�žÒ,¹½A�þ7  V�ðÞ @80ù ¶§ñrS©ÔÞ;޹"­ ]Ú l„#Až‰óVu¤¢Ž¿¤Ó1P ­ú< µH¹ÐÙM©’NÅJæj[¥É†ÏuÊæêõ ô‹ëðŸ(üÇôþ3ˆ×Û¦“%¿ºÖÒ}+ðÄÛGv5Å埪µ9Þk‘ªä¤S رåVXä)UÙO:ä*²€Õ¶#oÝQQcZo‘“×v)pºÅµ¹×?¹5ÆFÓ8Í"™Ù| ü"™OA Ý«À*ø•“ª{•À¯éùÃ6ÇÔOdã§€ÖÕ“‚fYŽ ¾­µaN¢•+°'‹ü7 ˜ÕÊY$ØSk_ŽyG;‘_üçr1Ö Š‰”€¬0¤ÝbJ1Ñ£": §Œ‹‘túo— žá0­vHFÄÈÆf̘‚m4VàöЧ¨/<ÞT¦ýër9C[*­¶¤ìu®ÆFÈ=?r«äÈMRÂ9ÈŽjÌ ÈÅV§æ‡rù¡¸åGD¥íH¼tâ­»xÍç cªìþ®-–™rà;i’Ÿ%³¯½¹§ ïÀÅçô¼«æyWØ?GùqRÀü€ R Ç0?-?šÜ.äGð¿í!TNù1åV5?Æœ§üy«š/wÏæÿªùM1‘—þÑó#Ä”nDš×žò£=’’þócj{ÌÉíÝò£òã“”ýàG¹pAËEȇ6WK#ÀÜz ÒÐ5m9�Ö°º'mÃÑŽº4¥1P_«Ã¢!F n±¿ÙÕÏ’âRŽý ¦°æÙ¤ÚXéÖ}Æ5;ekÌöÀ&A Î7â±’û)Šl€qÉ Â¸ñ›K´�˜5›õú/àã#\Ä7®VÇ·ùVÄwJÅ·ÝßJ²NÀ·-Æ>áY6ÿá¯\9Ç!nµŽˆ(‡a½Y‚ÉØ4çÇʉKÙSH;‚^†Ø÷ØZI°N)©\;=d�þ‰j‚¿©šÁo e¥æ'ö/‰Õ¸n±²11 Ñqæ–&•ZJÍÕh‹K1ÞÑÏT€¯7È›Rj0Ù¨ÂÉSÊ”J*'U�ÒX˜NÈþé «(ÏHýN(‡`l<Œ !a BP§ü#*© ê0C04þLÿÔˆ6·âT ¥Žè½È#86W“4Ó_L-Ýì[Y=Õj9ê= ¹¨ž…¶SëYÀ_TÏ®¶UëÙ,{ÐxÂüªÔµ˜¡4ÃW}^«V¸_ÿ¤ WX«W¸­ ¯¬!zTø­Tá¾CÒ©³b…ku+V¸ fUA«p8·m~œrãÀ1Ðþ6´™Îç ÂÁH¸,^ú[qÔ�_J=ôŠ~.¥ä¬VnD”c³õÖ-`_Œ;ÃàÚœ#´¸¶ôú%÷Or+zœü)v…±¸ü®R¾ܲqÊ-Ì”ã` ­Ãr{ví&{æ¡=ç“Ö8=æ«{ØEÁZSVWøÐV 3SÁÊœ¦•i74y&ÖåWÀ@µq3äì´vØÏc'¿ ì»iDÁ ¾µ#°QFc\äÃê&•Øß ¶ä#§Ö–<LИH‡¨-!^lK ;¥¶¤æ#_ªiýÈÓ ðÑØ£ä£)GÉ©ÊÈ|ç«.tÍÇí¡_ÒÂÀérç¯ÎCÿ­ó£‚£Mú•uNÌqœ0†´Áò¾‰ìÔïŽÓ…Õ²­A/ã@…¯/ÔâŒK/iRs¡ài"lXã½Þ@íÛ�é¬ß¤ :b z3é © Ž²Í „K ¸¼5˜žhëVïŠp9A2CfÀ…E(™*<ÌM¡KWÆA.8‚ÍTÐ"båÐV€Å C¦‚yä¾ÈþªÕ)ï,U§^êÕ)7êN:Ñ£N´¥êô’fü!V§Mm±:­mKÙ>–Øt„ÔËð£<mO¬üq[Lº<7…@‘¡¿‡ ª ‚ãúP!ã Vz\Ÿ‚ÊS­¢±Èí•« Ö ôO©E:¬Ø¬=ÏPʹï@á, ë,ðT±Im\¾qRã2Ǥ6.3€àœ@õ,©Š°½‚Š(}Y*hß³!<;§TaÕpÆñ©@¶G¥þÃ\¬ßHÅHRZ€gœ½xñó±„Y|%NÚg}R©þKZ[œgšp!Æê‘±­±žW{bÖ¢îù Ìê$­z¨ËhbQK-‚¸ˆõH<v”‘ @‰5­ÆÊž`"TX‚>¤$®j)ŠÛª–{}¡ñ_œôo¼tr�[ÜÍŸ¶Æ%‡-ʬà sIle(r<¡:IýãÊ=Ìbâ½b¬-Qtn–kò”¿â%ß|­Ž¿¡. EVxñäþÆb–ú’¦µô(MzZƒÒÃ|IãûÜœ®ëªt¥Ûø’¦uöÍšôSCÒ[½b@šVåó5é™ JOð%M{fMºƒÒÝ|IÓŽCSMúj«¤C_o¥®gº‡€è}Œü²;y¾xÕd[}ÍåZG›M?\SWøâÜ9s3rr#ÿìN^ÎÈñÜ_0r”‡¸Ci²½ÿ:₩ÍÇ bc²·ÏuÃ.nÆo—]®ü“¥fz-EZ…[Ð×ÂðÌÁ(¢ØqM;^²•ÑÝ?ÒŒ g–š‹Ù ²)ë7£ ´,[úV‚Å> wN¡I=x�š<¾5–VÈqÀøýïè¦Þym;­•+ÛÎÐ$¥ŠN1¯Æ$këï3-ö–4g·l®6Zì/t²t/³ä;`>v²–ÍÉöYìÖ®&˜›Á< ÷Yòϰ(ÉVKɨd"Õk$›qúoÃÓ¯Q³Ñ™m§¬ã8ÙNxí¡q¥ rò$Qߨ†’€4´X6ï„NæƒMþÔ)8¨{»‡5ÚÅ*f9ס<í…Öb7ÅËÌ8َ.oô§¼€žÃV ÅtBôݧû!+·(ëÀ¡P ò厧ZB·rÄd›Ž dŠ 3Ù²Y~¢$ÿw¿ˆx0 ³hž0ÃAi±¬—7P­P0. yQ€>(ÓMqH,·¤ÞòèDÒ÷aOi|¤)€Ü\+wLk‰=ÙP¥Š<¤öfU'‰°'HíÍJÀ7€yb†q¥¶–`fvÍëiêþ‹¹-´{0¨ûY¤_õ•#ÚîQ}ú_g‚RiÀ üjŒl»B8&»¬& RªÁEÉÄA—¿®mA±On@’©à*ÕÚLÛªŠËº¿eyò¸ŠÒµ)ˆÉ6”õ®u ¼4úñDj>§ñœª mR¡Íõ5wë“:ȃÜ@„˜B²?'@³ßù¤K[_Õ¡Ok.@gŠßl õMM1õ0PžÆ½“$h? qDÕŸR³LµÌµÓj¢«ÄÒ½ÂÀ*˜œ¸+1*@¯’7q^¿JŘ[VßÌGÎY{è¦ßÛT7_HhZ£æÚ Ý.ÖÔû4¡™îÎÜ—tÅÏ5óô‰©àE²ùTóAuÃ+Ž%ÿœüök"¦±MÜ0uÁŒb2þ‚ÌGר¦~vR²î:N„wNêCÖ-FŸ®"zÔCd¯ñ[$üM²¶l†CÖ&ÍØ¼t"ø×yü£ì¬Æ‘j7³íOSÁ`ô:4ZPJ×á Æc¥¿ãí ©-–Ž)œ ö*{F8ã¤*jËh¥Ür ã‚?Ø‚%8`ðWìl†êÇzö-gɬ¯ÊËÕZÕ£0Ôv¿"zfGS_¹ý¨ž)wõL ǦJ6¾ohmø'ÆÔ:°«ê¹!WÌìÿ5êÎcz›ÝîÉTÕÜT_»"ÔêÐï›xU›{ÔXðÚ{ǰq¢ŒmmÐ3ví•ÍëßD0/KPuþ7U•Fzë$’šà$1N2âòŸ†övè–Ô÷´d}ϵMn}OÚo”_,¿°ïqN½„ù£kãÌT°Th,!ùÌ“¸®\\þS]ÁŒ˜Ã¹;¡JËI;AˆjzJuÈÿ±fàŒÑ’Rié¾[ì¢ãí±‡`ºÐMÛã›YìûYÜI£¥û‹ßen€òÂï|‚ØÕT0Ú—@wº’y§óåËBû¦;™­ÐÔ‹¶ãþ\ N[-²‰ïHÛ!³ˆ–jBéG±‡´ÞñT¼µ£i£#_é_6±;¸µ® ºP‡%%pšÅ¯Â7ã×Ñ´ÂR^(¨³ØÊM‹)icñ´TyÀltŠÞ6bË(Ï I°MT½çÅ%Å÷ Bï¦lqmqç€ó$kÏ)i§ó¹K¾‹.t_l¦­ÿeõ@s&°Øm»°§NÞ¨ŽQŠƒ/RSSò3uâW7è¸ó©‹ 'åð‘îÕ:O6Ð,£Pµ!Ür.iD¨Â‡nã9'5"TæC7X}ªõ!„»?ÎV)>„pÎyèZÃBU>„p·Ôùõ5ïó54_Åù°²ô0ŒÓësÖáñHkÓüúû¬¦ˆJ>SUëNÎø¹Opž{Å­óˆÃ£—¹ëYÜu8oæÎaq‡=âðcn>‹äg¢ÉOŠv¨áø¶ßG‹ÿËàߎâïÒâyÄ·¦ø�œÖ\u[+J2­é×|–_ßrb[(ñ…ûÝjtQy1ÎÆJÊ‹û²Ÿþì'Žý¼Ä~°Ÿ>ì§7ûÁ bZ‘÷z-šEB€Ë˜ÂÑ}½A[Ê V`õqçÑ*ZrÎûàÞÀ`ŽpÄËŸã²;§%$Î×K<VKz°Õ’u®¶ðÚ ”ÄÐq#ßë%Å] ¯—àšëimM÷/ØJ-¤/›G#Ü:‹=·J]rMì‚C’Ô6N©ÚˆGaÍR¿3“-òÊBu²X¦Ò¤L‹uP|ZAËVuDšk`û´”D·xI±È`” ÖÈãjãåÜz¶¾.‡®Å¼@¹òx©Nyy¿‹viy*4øZõ ‘_Q†W©Ážg›õ}äÅ5e{6¥ '¾Ðf ªÁ5³Ï× ZëЖÍöíÅ ±O›Üý‰‹¬´|ø*šùèu–M|¹È­øPù2Ñ&ø“Wy û¼‹¶É¬Ÿ×ªÂ3>ìü”‚{hk ®Ø…C‘ˆ“öÇI¿šqÏü=ævP8ŸÊ!—„©gÜOͨíIðòú§ŽªÓXÌS^1Ö©,&ûŠƒósAɆ¯ƒŽ)4j>PûóR¶là3y5ý‡]¦ËÚÆÁ8H¢Mš×Á}¤Ödä_ó2ÙÌb&{ÅXG³˜/ë1!Üä|*& ”Ôu}(ô=5Œ[ÔÞbÚÄø–" -(0[(²î3M|ÿU/a1w Š»pʼn–™¤i0ÈÓ\îŸÕtMöÖÕÅüzIa³lC!ᦙ¦á^o w°› Çæ8(ÑÝÚbÖoWí-ˆâØûýXð}ˆ…•{°6]Ž—þ ´VZÙpØ£ê¹tÙKO;®çX%h!ÕŽ”ª±ï¿Xƒ#ï]sxö„¹Îå _iEÔéI ©Ô»jÏc¥¨Yal»šß$0-¡ç4ØfÏ«Ç6F=™PM-pd:¥6Ö´ºD™\AÍmíÈÆ§Á 6Gî ¶S0RVþ؃»Ý)ÂdÃÇldã­²nÇ}™‹!|%å}rBä»H •”){tݬ~Ù#»ïSg›É{h¶¶OŸmQ¥Ñ£—±‰1öB’u·8Ûœ 1²qæe2¨àE¶¿W Ýë[h“$ ~”Ö{ÑêLÎÔ;çç]n³Þ_±Í=p_„©Ç0©Öú´Ç¬¡þé"µŽ¡—B„NKn9©–9³Þ ™÷à¯.Wy‰l¼t‰œøùL'ί@sN]bN|œXI!ë.Ðürêĸ rbï:Õ‰=+P©æ?g¥ê¿Î俪JÝí@JiJô¨¼Kä¿Kÿ©Ç.ѯ^Bÿ a¶ fþ«ÿ½´™üwü({(׿L¸Nèèå¿hÓÆº¤8i½ïåÄMljìtåà&]œQî|z7¦Ô4Æ´±ø4ŒùT‚òÏ6õÔÝàXƒ×ëˆc°7lij/òèJ‹1µñƒ ù8´/ùqžÐ™L[ùEÔ&p5‘1±y,zÇEÄØ6¤\Ç{ê/ >Nn&lC’½›PÅ ¦â3Oå1ù½x?מñ,ÐyÊM3hçèw ìv¶ß¥M.õý.ÏóÈqÒN,²Ð…œ3NúOù ^Œiõn¶G`ÚX«¬øwÃðxrÐ!|7 í‹—œânöòŸciv†Ýú{é´Ò”ËìÈ9µ7|g·rS c{·%Úxw# šÎÛ{oFùõobñÇ/nÅ‚Ÿ7þïï¾\¡>ª©%ŸGÎùù<´´“êÞ¬8R<Fíñ^”ùnÎü7+á¦#Rã|oæÁM[¼™ 7mðæ¸ÙŒÌïxŒ‡Àð¤XÓÆ-O…ýï€N³ŒŸ½Õßåüý¬­@ò Hòóéå±Òçã;´,óÜÏ(¤ý‹!¸L"mèv…­Âäo ±à–sR™¶£¡îW”š—±{4ÉØz’ŒmÛÔXÄ™æ2&›…NÌOb0à¤åaxºùd€ÅÞ»“e³³õ@më6ÌàÐ)¥íNZÃD"ê@ìBÅ T¬û…f¡< Ÿ^ÜÖ‰Ö¼…lPÌËL¶å´Û0ƒdz˯om*Ø ù˜_j*ÑoMïâ ³ÐÈf<|ø!Õ×U(’G«J0¹ºŸö6Sd<>ƒ<"OŒVêw‡ˆ½˜³Ï¢‚q ®�ll3þeM¯LMï`š§§%÷†V½K3�{¨ß÷Å{dæ.µå]¸ƒZÞWwé-ïµÐÌÚ‰Õä_jy§"i]©Øòžú[Þcÿ°FõV\C3îüGÁË×c²®§E³EPëçÒùj#Ÿ¬Uû‡î7òÎZµ#‚á(õ¼†ê²ü©C ÖvÜKGäè@ÿ&7/Âç' 2i.°³ò–»HÙƒ™í±çU„gÝûËe–üž"š‹ûç?Uf°Þ/c1/ƒÌqp-ýåvÀÛ£<¢¼«]™¶@9•<™˜SÀËE €ê=¤·X˜_ßfâpþoe+_fKh# e&š®ÍÞÐlEäV^™~ðÒ‚”²?u†2 ý®B ÷TÞ"Ï©\¦ic±“«q¨9Â¥ÚK~…Ñ™Yºn‘û‘[áRÛò\_K¼ýZ>Œ—j@i·´üIÍ[™ Zàc?— (mbEé%DS&¢)‹—–$^Ѫ`‚<Òhˆ±÷õ‡Z�Õ:œ¢b`ܼŠÝ>®l.ÇÅ–ûL¶â œyöèªU;kWúAƒ¾±àóWr P›Zz…˜¦Í ê´;Äý|–æ@”˜…='v·ÈíÚâ,òXæŸ8W)Øç÷d?pÓlùI¨Ò¶q=Qz›AM–óZ(7C‚XhL¶ßAºæ;eE™¾3Ìv†¹ï ã;aå´X8d-Ϊb%çÆhmgàê6ŠË]L+ˆ‹ ›œël;Ê´m VéSFP¬®l«ÄŠ7¸–UÿÐPëU +xm—'B‡˜™/š/]T¢uÐÁ®e+q[ÉHf½lí_Øm%HN<xÂøãå6PFGý‰ÝØGE´±•ÿ–”WèÜ9t*3°%áC–'Òvå(èvö¼ªµº×C¼øäœt‹< DÉ#CVQ— 5qú]Õ¨ý‡*®|½™œ¿]˜VEûà”Žü[·0þ®hm-ð#ãZ7SÓ•ÉÌÌ çëb ä^ܰãi NlŠÞÊÒ°\ñ4<¢R`Ë(alwx± Lû8ÓÕËZƒê@½.mÎÓai¯Â:ù’àé>!5PUXzãäX#€„òj`Å™’âôpåÔfµ˜sú+Fƒ ®|Ì ZrõŒý73ÆOFx«^N¥í1öAþ¼æwÓêq£@…ëdš¶‹zN5)Üyiö Œû4՞߀˜†.ÃL@sL¦;âd«Ñ�M.®!KÏJ±wœÛ¯Ü�æ^îYbÉK*3ãx/¡L³¤oPœ<0š;k°{ &3Ö‹7J£3we¯+ ¶ë€ý´g+<�óþÆ4ô5w­ˆ{÷e¡¢pèô³Ø1™T£´™8NÝmW’+™½ÒeM:^ 14ˆïùýŒÿ—o6CCþ_2´Böߥ›õ¡áÿàC‡˜ÔôKnù ]«†x(6[x¶a žø1–¸ÒXönˆ2*0Rˆ³Wö›šQ`ˆÒq#r-óŠØ· [÷ØŽç¹ú›uvØÿ‹³«D6œ†‡³ƒþÎ.“êxÓIuù?$U-&µâ¢ØârøI¥åjƜᗿ¥Ój§¡ž ÂÓS±‘†{©Ÿ³í¾Àø<â¤Fƒ³5¿‚Ñ ËT`<Õ{l¤!Îl ¤qö¡TªFmÓJ•m+‰ŽKƒñ)óôÁ¸sÙ¿.—øüj¼=µ·qeOKC«`”h‘rAzís¶g׺@ƒ­ ²çÃmŽÓ'ÛͶºÜ[€†ç¶ÙvmV`{´Y¡íT@Lá'¯òܱÀ'˜aÇbLW«Ó>Áÿæ ëÂÅM‚×c¦õ ÁQ+ Ì\°Áö5Smîæ{UœÄ½ó®-\Š—Ša.à�;p£èVïàX¼æu<m|™ó¸vÝ¢9]ÖSåó™„ñv&5¥¥Î®Ó¦h¾‚ÓÛˆ:å‘ xÞÀy¿û’U’TF‹{¤-lÙFxƃÇÀt{B­2þ µ<µú6‰OþoUþ§}ð*Iä4Ä„@±Ê½½F¨mçÑÊZË90 ¸Q[/(—.À 1^:à¼p€ÎCúJ¯ŸšÞnä1µÑôÍü(çŸ üÿ˜BnÄÿ7ã¯WÆ¿l®Wô—ªÿé7å¯9*«l%Óm?ù€EÚ2 0A’N›V›Ãbå„ÉRî4ýìý|ñŸ.w–¬ãû“êùPOin½}·¥èyëÖ6 ¤Ü¹[ð­00·²Þ½ÑÏ=Þ0fËÅ÷ÚîĘ $¦ýÜÀîLRð-"ʶ54í(ÿÁÀ6ËŽoÄi ÌGÆ®¥…¤9öŽ…‹ÊtÐU³P\OSß—¼}.„Vˆ*#ŠqÀú8¾€“Á’HcI~zdBiÆ’˜ö½A}ý?SÅ¡×8xŸ_ßí¶„¸W{à‹¹ü£4-\Ó‰ïUL[«9¦#&òâúødùj2xÕ÷ž>RLÇÌÖ|2tÕ,ÖÞ×óF9rûI\3Á×àŠz‚+™OÙêbq#ãuþg½ºöÒá?¹^_{éƒÒšèQÓI1�IO¯×^ÒOâÚˈ“´æ~_ñN_Lõ²ü(Ç¡,ÈÁQÈPžÛ“ï÷%ÕCsVKû&¹ ÉþrÒ)ö„llKêr—ËÁF’³Bgùþ1u±æMÄ.GæSkÆë‹ù ?û±è°-fªåÎÁ[ÝÖÑ?èXÙ8ï„§“ªÍ€f@سNõPSæp<n­zè*TAåo*RQé¤ÄxIV‰ê{=}‚<Ô•{¨ïwä¡6ð£l�ËQ·#ƒ+÷±ò’Æü“Eþù§šü³RŽ:w.ë|~á¨êŸWÖ’Žªþyv§~9taµw1Û*©„LFük5ükþµþßÿj†¿šáGR‡ŸÜðW~2Ùz·ŠÿñÓƒ7í¼y»¦N¸pœ°Ä~?NbV˜ÿFŽùMuÁÿV“ ^üMueµî­~¿9î£~8´ú±·XEߌPFn)ÖÑ_?Pϯ"ô£Iñw$uZ)¢·Gô½»ÕË·„¾ü(›Ö úŽÆ¬!åÿ–ÿÇXý�¹SÇHŽjÈØ#*ü—VüGTøq«ôNBĬqük4ü«þ5þÈÿ'†ÿäN+Üð#üÇÜñÃðƒøWãªQG†è†íÃ0†ÿ(oŒ§éŽÐ?¬•ÿŸXù?¬•ÿŸ|–À´qü«5ü?1ü«ü‡ÿJ†ÿ(äNËÝð%üGÝñ/dø"þU„ÿèÍáOdøÓðÿ¦ã?¤á_ÉðÒð¯ô…?7µåÐù ÂRj®g›Sª ŽC°Ÿ*Á}ƒؼ¯º­IÍè±7—Ã:Z6fþÆÖúKiå(³—=S¥Ú±oÙ±«Jµ£d…:´!’¹–/!àÂãˆõjJsxJ¹ŸENPL«“Ž÷lç—[Yƒ´÷Z¥æLæ Ê™n«ôœ *ƒ‰Uq„r&I?Š9³ôæÌ¢#l¹ò6¶_‹´/^@¹3~”; ß]ÞçõXÿšv¤ÑþõãŸT+Ë—³ñÅOº•‚Iˉu³r’Ž-­ "+ý¸•Ÿ fá>˜|(SWbÿzôðÍõ¯yT~VVû×…‡µþõ¡ƒj¾uXNùzPÍ·ËoÜ¿¾»±¡þ5æpÃýëk+U}»Œ<4l¥î¡¯€;> zT)1¾¤Ÿ—ˆ:s=T}È­=Ãf+àGºû×õ‡n¦]FþùøÚ¿Úiýk«ª®þHþùo¿êŸšê_͇ï__[¡áÿ‘á_!àßødø1üHúù7üU„¿Ê­=3—់ø—Sÿº®êfú×E䀪´þ5¿Jï_[ìW]pi)¹àÂ>ÕÊR_ýkߪFÛלå*úï–úËuô³A·ò!Ñ£š“c>’6~/¢W"ú“Ýê‡2‡Ðÿ4ÏÃ,£þõçƒ7Õ¿ÎdùPë_ êýkË}*üËKþ?{UøÎ%>û׾ǿLÿ„á_&àß‹ø—0ü~$m\ì†ÿ�á?àŽ6Ã?ñÿˆýˆ7׿Ìeø¨ý‹í€Ö¿´Ú«•ÿXù¯ÔÊÿ ô¯}4ŽÿG ÿ ÿþJÄÿÀáGÒÆïÜðï'üûÝñÏbøg!þ¥„ÿÍá/bø÷kø÷ëø+5üß3ü{4üßûĨ«åÐÞûÉÊv³ 7â æÒöeäÄ¥*ö%ßö´¥:öù Z™Aô¨V$o|I[‰ØÏíCìgö‘‰ñx® °ŸûŠÔÃ’²„æó²qã>­{î·G…É`<¤Áèú=."š]Ô@T›VûšÁuï0Ùu\ÿ{LÿÔ�úkæ ýá } +"/5_Å!…­ÎT0‹A.X¢B^·˜ ç,Ñ!ÿXøæ=êvRcüI¿~+B¾´!c½“â¸I—¾$õÛàGÉú»ÃÝ{CÔ·ä ®PñšÞ§+T¼=ãQKóU†Wö—ÌWeãgL÷z®{ÓºÛîšùbûnLÝ«VQøž2–_–zéw9ïòÏ¿¾‰Õç!é·oDÈM²ÁrfVåxð{ìߎT†¨/,ù« ùÅïò³¿ªcˆ`®géñs½lœ_IÚ×r틾 íVÔÞ´×ÌõXÿÀó͸ÄÄÃ7õ<Ϭ÷•¾ü£×‚×¾×ú¿E¬ÿû^èÿvcÿ·ˆõ•¬ÿCÒÏ Ýú¿=Ôÿíq«g>gýßçØÿ-fýßÖ‘¡‡zïV=¾ˆ<Ô}·ê¡Î‹Ør §‹¦²Vól,0«àÔšYi¾Ú¿=ÞõÁ¡·.VÁ/þ–À§,ÖÁÏÙH?"zT Rc,@Ò¦¯EðÎ ÿ{…xçLR¿j&¾Šþ;êþ6T°n Á›w©àý–À?°Kç·lmÓ½J0õÅ\ýt¦~ª÷Cès|¼ã¯ðß:¤'}§b_ú aõŽ}ÁN�ú)Ñ£L¤ÂXˆ¤’"ö?EìüJÆÝÏûó32n-ü(#aÓ¿…rohоc‘[dã—L¾5¨ëNÕAm¾!µØ©:Èï\̽êµÞÊñúÕ»¸ ­Aþ"ôÚ…:{‘zé/€pÑ£Ú“ãÇHÚ=_]¿A_ØíÖÔJê·~Šó¡oô®ÝZƒ?èMìBBõ‹ŠæÑ…>[ƒOv»µ_0íi¨½õ·zkà•߬¿{q7óAZ©ùºz¼Ù ½í[ýú¯ ½õ[ý²�uÑ£:ã $UÌÑ_Þ…èÿÝEöõçö]žAê·Ãòäôw»wiðïÐÚÿ¯Yû¿Ckÿ`Úd¾Îû»5þi’ùºlœ±Ë þ—Lý¨¼<ÔÌÅõrÓê=4í ŸÏö¯xÿ·Ëc¼ShþÃVg} äßýFE¿a¡ŸðŽ~E9@]Hô¨N¤Ãø’*çŠè¯îDôu;ɼûøXçê'd^ü(9 ±óÛ³“æ‚iãQÓÆÜ?ÔCÓJæ,ËIIîz9xq[+±(W]Õ~¹ªm¹êªf |Œo±ÿÛI€é||aÂUœÖMÄ÷ÿ-ÔÞÿ7ŸÀJ …÷ÿ•²b¢GE¼q1’NÏq{ÿmÆÀÞÅ¢›Ø#ƒæw¾ÆnïÔ/4­ëR^bZm¾Š( ýecå/¯\ŽÚJñV(Ü‘ e*¼'ç¼e*¼ûæ‹{(I¦5åz6í½NK±’òsÍ×*Ä?çÄo¾Ö!ž,<û‰õÉK‘äïñ_â=¿hù‰Åí býG yÁê¹ ¿°™Û=¼ý’ hµ@;h pÞÁú·Ã€lD© 5qA+U¡>3OÈIÏöúÓBû%%Ôƒ]8KýeŠÕa]·@ÇZ·€áw�ë’7AÒ-³E¬Ñ;ë;´Y*bþˆíß!Ö5ó©£j·ƒÍÓîâkDØ.!PºXΗ³¾ì(àš¼]:j.¶]:h® TÇ·º\/®L­Íõ¼¼ž¯ìB@"Í׆"š ðmR`¼¼ H‘³D€Éåðånå5ùCx?ü(æaÅëA ¹whð‚w#—ÉÆ¶LÃq|~m› nú—¿M7yŽÏùæ2ß?€ï*Ç×\Ã=‡ð]š'¼ÿÁt#zÔ<R`l¤¤¯ÜÞÿX†ø&—¹á›ZDøž…¥n.âH ¹ËKX)U]FÜ!#™ü �³q«Šî‡Ù„náVÝ—³šO)rè…R†ê"TED÷À<݈لîvÝ`Ъô'zT ‰ŸBÒÄ/EtóJÝ—¥nèæ}@èÆ}€ç Ý„RèRKUtƒK5tÕ%*ºÝ³]i‰ŠnÃ,÷ [aþtK©žTÿêà³sU€oÏ"€OÌÕN�ÅJÑ£Nn'€/#éÓ/D€ŽípÝv7€Ž÷ à{ð£<>ûŽÛ©ïð,žÓ¶óâl%*ž×¶¨k¾"ˆ§¶¨åÖžêýEäv`š¤wsT„s¾"„Cæè?½Ê»D20„“‘ôÓç"ÂcÛaÕ67„Çì„p1ü(I³±ÃX¹Mí0x¦!ÀyÛÔããmZ‡ÑAØœ Ð�^úÒ½þíæpïäÏuaïÈKª S]ŒƒËQpD2Ÿ3ÜBo=8ÇžA=c™iÙ\ cÑsÊjœŽã‹)ñ}’�¡7ô=M¶aSW™ûŒœtŽ É­¶¤T*O|¥ñÜV2úuhün¥Æ_=æ?¥Z¹ˆ&çoí¢¾¶élÕÃQ_’‡ëfé~d3¸ón¢GÍÞJ¾I/Î=üæVôð„­lÅ<ƒ­˜ãkô&½G^Ž…åﯨϊçd²£öÕ—¿ ‡êË%@pZˆ-t¹ƒ=× žQ¶-aqžõ~ŸäA|ýþ™z^…¾ba|@Äàñш@üh„?~4"ê-sþå�k,? íƒÁt¾µŽûô—g’‹>~C;ª‹].ñL€­rÒÏÚ‡ðcDnëŸÚóºÿèÏë²Wó¿}.ŠÀvcW¨¥~8•¶GöäÕ¯flç7‡‚f*è¤~Uã�½K2„}$O9JÆ?6#K•ÅüµÅ¡-ߣ3®¢ ?Îmë@Ö¦*«ùýfz8B¡î7ô„Ì%jýU‰›P¨¥Ì;ýdJÿrS-ýÔZ†ÎTµ¨ÚN‘¶?šjéßÅÒßùILð–XLãt‰ó’¸s*I<ï-‘Eût‰MLb“èæ-ñIlÐ%ìLb#“¨žî%qu#Jl¢BŽ<Ây„œ‹&_G4m$ç:K+Ýs‹§SŠë§{åÜ4J±¬‰fã‰Mdãbfã o-$ᯣú‘IL`iz§ÑŠ$jô4¦0‰QLb¤·Ä¾ (ñ·.ñ,“ˆdOzK|AUºÄmLb…Dm§{•ÀªŸQb³.ñÇF’èÁJ¹"yIÌ&‰mzîì’¼rç ÂÚ¾I¹c|â/5ßzëxŽxz7¨ã!Š¿S·ãÁ</—É=Ôq˜âÛé:z¸ÛÅh#ñ´h¢¿1Á½˜Í¦ø KpT¹®©äUhþY¬GŒš³Gl gïe' ½$Ö®ws”Î$º¿M[¼%¦’Äu]âúÏ$1•IÌõ–0“Är=3ì…^NèN Ÿ2úr‚±%EvÖ óVN¥æŸ 0ÞBñuš# ÉÞƒïxrû:d5é™ÖÙ;¹ó„©¢ÁäöPü2=Óbi�zßû^åý›µÈ:ZGWö®Wr¯“ºûLî%Šo¡'wÇ;”Ü÷6¯ä¦äÒ´ü[ºžòoô»¬)²yù#—$>Ö\Yð¶—{Èg2p9Å÷ÐÝßš%×ú]¯äꊑõ¯�-¹…,9|­<¤hßMïGŠ|uYˆa~¼0N*UÎ!mC})2`RݼŒ>aÃø™ &êþö@ö,yƒƒ}ÑixlcoØØKºß3bJå§"þ} {pyÊÌ@™xé:YžGg)ñ, ´×šâ\Û-òÌ z¾„+üɈö)IE(|Ö"mæ*öÆå_6ÙÒ!µ|xà†=8ÂLý¸Á£¿öÐDÆëÇy{oM%å&Ý™Ëèw }>(Æø÷pz3¤Ïz£¯åôó}:º…Ñçqú! ;3Ù ª:N¦¨wxÔÆøYcˆš?‰¢Æð¨ñ÷cŸÏ KäQ…Õ‰EmeQ‘<jF°¨óLa{õ"F),ã:13\Võ8FíbQ}^§¨S<ªF­`QݘT9òèÏXT‹ZÊ£Î|Qo²¨lõ1ú£†³¨gXZ“xÔ2ŒêË¢ÞdRCyÔ ŒºŸEE0©Þ<êuŒ aQŸ²¨{yT2FýGðCW²¨–<*£³¨eoRÔùÕ£6±¨ ,ê�j…Q_³¨“Lázõ”P§Ä¢š2©¹<ª £Æ³¨o°õ`µ£°i±J9šGÍǨžLêî|’zžG½‹QYÔïS(ª‹Qþ,*EÝÆ£^À¨?èÉÈÐPu=›E=†Q;YÔbfüï<êvŒZ΢žgQe<Ê€QŸ²¨@µ„Gþ�¢Þ`Qß1Èñ¨5ìªöþwñë{Õ£×0QH¤L߯ė \+5/Ã'è³NZSÏ]/ýíª(pX_¦Aúß®¤bþšÒ‚ ô@T©+1$¿Úæ“ìËa®$ØsÃþÑ[hÏßÊ`ÓRëómKø~ŒÐÓÔKGV¾Füø²¦ ŠრÂØ‡L÷ÒSÃÌê^x@\OÀMá ¬ Ð…ø»á¿IŸ*aVšð6>ž€ï=oõž‹=Xb|o¶øôµ–ÈÇß&¸Jg-:£m©DÍZv̵/ÄÏOÆ•nljœ’$QÖüg!n‡Á¯NLjàÓlô³û zD–âÙó±¿Q|ä¶,$œDÿ]¶C~þu]}^ºd#™¢ùë ²üªèf;p<óþÏužw'¬ \zSœ±‡Xì—Lðˆ›‡±«8FܤBÖ„Lñ×™”ŧ¶+Q¨"âºÛûo¸¿ÒLû@Ÿµ©úV hÇ™©ãÇ’j¥î=ìôjŽãúùêJ)´ÿOô2«òü~@~Ô¤°i†øuöþ1¾ãnPPnZí(I* m¦ ³õøÕÆú•Áí}õÒ^|¦ÖÊjæòǤ0üh…öÕBgø§¸?cœ2?F«S Å—>h_×â‡zÅÓƒFÚ¤~œb±å)¥VC&ð·pEÓÇ ¯AEt{éÖ8ÙLª©~²g‘·ÁzZKÏo ¯<Óg†•X¶ïmÓÐ ¡šÊ~Ó6Úhgå[^³ŒŸˆç¡u|Bñ­tS½u¤Ï ê°P|{]Ç@oï-§9iƒ:ÆRüÉÖšŽ;¼uô$ž­ÒFñ‹uNñÒqnò|Ô ŽŸ­ëØà­c.ñ¼Ô Ž<ŠB×a÷Ö1xîlPGŠ÷×u¼ê®ÆAÄs6ÄçħæGŒÜ¢)÷V°‰xVùV0Ÿ"gê ¼L ž<ß þG‘)º‚ƒoz)xxžõ­ „"Òüà­`+ñLñ©�㿦ø¡šŽàß¨× Íbª`˜]Í"‰Ë‘¹?b³…FÖšŽ!¤ã6ßF>I‘WMš‘·{i"ž¦†Œü{)Æÿ é~œùË>t,õaäÒ‘î3 ã;ù¬ndá^F¾D<·7h䥕fäG4Ô ½Ç·‘-|ù×Ô±¦•O#÷PäZ‘^÷2r!ñŒö©�ãmß_7òOê Bß~ݧ‘ƒ–ø0²éhâÛÈé ÖŒîmdíȳ6¸!#+(þsMG°…ùÛdŸFÎûÁ‡‘¤#ÉgÆQÙS7rçd/#Ÿ$ž ¼â•–š‘ hœú¸o#Ï}ïÃÈ_¿G_·ôiä Š|WK ²—·‘ïÏ`Ÿ 0>â×¼NÓ¥Ð&ù4òq_F¶'ÿ´ðiäµÅy …fä‚IÞëÄóO´~Añïh:‚ÿÇŒüs¢O#§-öad*éèíÛH EÞ­ÙÊÛÈÄó_󆌼úÆïo®ùÍšC-¾Üõ#—‘Ž}¦aü˜"­Z¡£rHÓ‚ïh )o¢×bÎk‹0&ôEˆ1tk¦ >Ä_g‚)Þ‚áLðü[Û ý/›_`‚±Þ‚K‚¡$¸Zü™ vc‚ݽ—2Áº (8EÌg‚.2(¸™·à8&x„cuÁþLð�<7ÁKð&¸ž[è‚­™`Üå-Xó Î&ÁJm"´ê5ü—ôÿè-¸˜ N%ÁÏuÁYHðY–âÞ‚N&ø Ó0ÁöL0Ï[p, Á{uÁéL°3u¢·à(&8†ÿÒcC_`‚&øª·à=Lðe\¥ Žb‚¥<Ð[ðôB|–ßÔ§e‘àûL0Á[ðk&x/ öÑ£XŠ2ÁöÞ‚/‹‚ÍuÁ&ø<gõ¼ ÞJ‚{´eßÐnLp/E›¼Sœ÷5 ^ÊEÁ™º`ÈDüˆ *Þ)c‚$˜¬ že)þ ÎÊõìÌ‘à=ºàl&Æ·y§xl+r$ø§¶r:‰ '½Á÷{§8‹ ¾J‚?é‚0ÁÙLp·à+L0†ßЛ0ÁT&¸ÖÛÔÛ™`{ì­ ®Ë%Á®L°­wЇç“àu+ 6Ó?a‚5¤7¸ŸwŠŸ3Áý$X¡­•‡Z˜àb&¸Ó[p\C‚Ÿé‚w2Áטàþ/Á[™à$øª.xÀJ‚áLð]ïÌ#Ál¼[\Æ/Îg]€wŠ3˜à³$xÎ_ÉW3ÁXï2Á‡Ip¥.ø4œÂÏg{ ¶e‚ÍHðu]ÐÊ[2ŒjŠú¦ÊsI°&ctÁ'™` Kñ“×¼/Í!A é‚þLp4\’í%XÌ?"Á_ý4ÁrÖ±Æ0Á×¼SœÈSIðS]°ˆ *ä‚àÞ‚1Á§Hp¨.8˜ Îa‚¯x›úßllM‚]uÁNLpLôü‰ âê­© Æ  žfýc0ìë-˜ÃW“à M0rE¶Û˜ªÄT0O3'øÓt,ÍAåü D¡ú®ùRSÁD¶<ù^6®/áÚïõ\|5!Îû7„ƒÞKý‡ú&VOAYàoÒ8!Ó!› 0îÓY!†õ¸¥ü6‘–8­‚ÞtL]ÚRÓ LˆÌ¦¤D’l|úóCÄþþë\ü¾Y]ns\‹¢e¨ní ±ý!Ï÷±ÛC¯@>Ö¬Ô¾Çb<?=õú(@qÄý]1­.W#ʘ¾õ Ï9ŒíÉ”á=ßðÙ…÷v¿ïŸb÷‡ð>œÝŸÀû®ìþ,Þ‡ñ½¼oÉî/á½Ýûƒœÿâ­N¾I7I'Øýx¼ßÏîà}9»Gï97°ûÛð~ùu]õBv ï?g÷¼·³û…xÿöum=ß9é“úhZ‚?3ÈQ“„kƒö Eøjö,ö¡U¸À+Úc­¸Oh6ðeF¶N¨¯ ²¯ F‰—¿>ŠÒôšJ[5”}e¡©«( ÇGüSdW‰%ÿJ·Ü=ômÕy¹X~¢F| ÙgsYoWs€PjLøß‚Ü¥Æþê­õ(g3*k'P±†XÞÞŠ6ò÷•â§sÕä+Æ}™³€ê2þô)žÓ©žïJÊÏÆg´ósôŠ_ðû’tŸ“ð½uMÕïÊ@Âd,r.~ì˜}„Ðæˆµg¹Lï/¥¬¼Å¥lO»çÀ®Y[¥õñ9¾k`Ä[yn³šû #$p]-èÇ6*&ò#ñºJzšX'[»NBŽ`7$§û"¿ÙÕãÚ­LÉÅ<z³k¢’Ãï†(¹¸[ò‡2~ñ•ŸøÉÀ$ËvŽÚˆrW½ŒQ+*ÏtàW‡ñ}ïÚ;c…g­´ïÇð÷#'H[X¤_ hÑw8ò§„A‰Ú0„Þú9#ÑE_t´Ð÷¢§ ‹5­—XjébßßS߀\jþ¿×€§´›‚aüÉÄ'ÏÀ-3-’ynþ)é‹ÌµSz¼‹ÙMAzß íL+€º©mãW£•³ðd"f·tÁo¡—š )}É> âñÛ3H%¤­j>-$™ A:Í-B1kG|§‹½Ð27Dýç-²äo@¼È>“Ø»jêW±ò)¦ÀÞgž •g®¹pišµù�er&°¤Lù!Nêk!Bz&W<ÿ1îÌõM´H!)1(¦̈́ºØ7Ò F=ô ÷ž™ø: ¾CdsˆÍÂØ©ï0|=Ëz G¯Ãeæãä9Vž…Þ±Iø‘Å÷ñs+æˆ?Í®R3}ݽ¦3µ7.s~Ð0.¿4PN(èeG!þÔœ�¥}Pb9°{)ö>ðNB!,Œx™‹ïÿM˜ ä!ñ~—d¿ÞRàù½BsÅÉ}³hÛ.‚½nM|ÎÞµ¸yw›š.=?[€ßìnŠ+éìÓG¬`lÇuv+uël#\T ZvãLÅ ·ÈI…¬:�_ñÙYTRËhÎé§V¦« Ï#‡At&TVúR<×eŽ]=ÁÅÑ¢çdÈR9¡}ŒS¦B>‰åDºòÖ8]¥=2f,~3FڪܓE_{Ál;/ýcí'¿e‰8¢d¾†µü­!JÚk˜Á•W_#yª/²{ª}ñþí­Tm%ñÑ`ŒŒ gÆPD=ucE;~£Ú1;í0Ù¬‘[¶ ›\¥x<Öyz»à$ÖM~ƒÁH¥ñÒyåûñxÌ7w¤E¦* Ì¢DþÑÜ´*Qp¤„o¤Uø>>{Ò2öús?zg­«¨¿pÕ ž»1ˆÎ÷³²Ÿå,¾&˜>††+­†áóp s߃dçywÇãS9mÉïãýýt¾·ËÙñÚi§ö9iJŦ.TÅv'HÛ”õc�ßò_‚Tm2ÿ/9c¥ €Ï�-ÃÇXöÈu£éóª/CrÒ|kêЪ‚JõüLœ)¶TÚ¸Ÿßç/£C¾ì{›–Q |Ú¸qÒ.%Ó‘øÇçÿ°ØWæ‘ÃðT$ æÂq6š<2öE6øûšÚõ+Ú•úÇ,R…ûGìi©M:f‘ò±%e9¾},ûF¼=ô+|‰‚t(^ª£ïÊö QÖ¥BóKÂŤ2ÆaûYà`Ÿ00Åî¥ÄXâøõ‹öñ±4í{W{µï]­($$Ø Zä7B¨<ño ¼\î7Þs?_H+íá_º¥sóû"*i»ÙÙ�SnMÞGâîrÙXf`n`ŒÜ®+l—*èh{QëÿÔZÕ¬ÕfµC³ä7†(G¨ ¿‘Žc–rŸPöŸT¶3â0e»Éb#¡)´«¥²`œŽ ëg¹óC ™#NÕ¢>ì·c¥“1®­0¶8¨ bŸLé}ð±z']Ç.ГÙPWñãløÇAwÂúiɆ§Éb —<Cuª©Z`L¶hQ¤ oå I`5;Ö[+[ :SiˆÃŒß®¬5±_Šá@í¢úý«w i¶Óúm¤&B‹ønu æ2¸¥~0…z£x{×£vtCII'uCdJ-ß`I©—l4´Ç>26ËÝqàfÙü»¿ÒC ¿KÞ¼‚1¥ý&Ⱦ~Y¨±u:u«_¼‡Ýj?ph¿aæÂçRAI¤.zZy&WjË.0Dþ ZŸ¼ è#ƒlÇÓ'[lå¦OA[L¶ÛµH“m#}efU4oFŸƒo€wá¤îkƒcÚƒ¯‹ñö»»ŒÅç3\üµ}ñvã[ÏâìðlKd€Níq‚}T³ðçŽyi_¿±¬~]Tšfr%6Jòó“ò/‚ù—Z“…¹¹ù—ÚY³ò/µµŽ±÷Ù˜_fz÷@šVbúnŸ÷€þŒ¸Ô²ßýÒhLï:’nŽ!k=aœ€å$¿½µ˜l«¿?-GÈ!¬ åáû“!g1\Ãä˜qö©= ”R˜Fˆž£"z<ƒщP¨ÏPœÙü=I¥Ô‹°!% ùøí$õVšS;çi±lÅ.*«2XNl1œ¤‡=‹FÈTZOa;?5,/¤~Lž…? £†Å$1mlç§\KÅb'Åv ƒ”Å»3¹eù¼ÞŽ—‹Ãü¤@[SÁ*ýý`T3ƒi ôÇ÷·ú› bÙ—ƒ©÷<Ì"÷Ø`�1.Ö_º%ï<ó gP/†º†šÈ3ÒYñꪎ‘È4üè•tŠ—eóIðëe÷¢,åx*öÍR€©€æ¬ /ĤƒG¨Â^T›ÿ�V#°v~GMÉ­Og°t¥íq0:HLל¼Š–Þeßë³³ÿ s²Š D…3s,R¼˜»€æsi$ó¹/Ú~¾®ª ÝSˆÏ÷Ð8ß" ôSó—úãDT�1›4ú‡9§“´Ú»y$Âi-mŽ‘úM¼­oËjT3Ìe[»«nÜRD ªóÒ­WŠ“§f)¥£ÐÈ©éq¥ýht®¬FBi?È;7Ò;³Ob›�Íh‹tªù¨QöÐÎiÔ‡/ *]„:¨Õ�·l§cš‡kò Gß``Ç´&Ħ֨ºÖÊßœ·Ò' ¦†$àãB¡5A¨ê>$U;ÏÁ^EëœÎGˆ« ~H΋Ö+4½^‹"D´#º õW„ˆ,!⸡Ûň !b‰Q,D|"F oˆs…|;}Z‘›ßL©øêë¾Ç‡ãØ÷Øë¤ãð Mj5 ñ¤ÖE¾§ª^Ñ9ìo©hàºZå„·•}#âB“)‹ƒ#S1Ø7éøan‹´ÅŠMTÉ…êï­Öç‰üÈ=˜J-û#<u_ {ŸøÚjõuÓýø»¢‹º4Å0}èÕÚ^%æq³(9bãg¾¥ ÷Ÿx¹E‚TKåæ,K"†}Ó¥f¶×÷Û`|'Œ” mÇ‘ÕlI¦ûjpB±8¼âoÊfûÀæ L_{U¶É6™˜¼ ´°`²­à¼87 ½|Œ<1ø‡±cœ®²Áô°o cŒ“gQ½9ë!øòë¦1ÐÞù©I–°ç+›²O]?êÏé@Lœ$r°÷zçV0Úa¿Û‹3Ûž´w¢½­{¿~ÿÕã=E1r¿@<ãø¦‘o3±iL‡ì0’üá§ç1ù¥02èg·ÑxJê#¦/C)ÖaXÌ“hÊ„ž0GÔÁXƆ8ŒÉŸ2)ÐT°ö=ÌÝ ?Áé!hªåÉÓ»£ˆ¾j›ã/‹³Oé‰o~ï=œ¦2Ñ+¶o0 dó¤üúv¦wž¢…kJÓTЪK÷.3â@SªŽ•~ÏjZ€µ“½_œkk¯í¦wÎ^g–ê±¹'«½O�K.p ¾óýÑW™¾šmž `߯‰Ù'ÿD- øÇô’ÿSk˜n!w2BJÛ1¥@—šÒy³ô¯8¬žx ªË)žŸí:–Äz.+>ðªø ö‚©Ë˜‚þ‹¨S2‡aÓá–ÁäU*ÇðÍüê‡`‚”•¯zñ‚%fOKT¨ä‰à‰G•ï^¥þáÑë#½˜°š<ª\ª³Õ]÷ÁyŸŒ¹]YD¹’#h-åìè`&bEõòïàe˜‡1iÔÔ®Œ¦çJ„ Ávƒ•®¯èÌC|1#„“u¦Gݘ6Ÿ Fúú*ÆÏ§´ b ô€qü!å.þÅkzÑm¼îv¼ÌêîÏ~bÝ-¸‡`FC6PgÛkø].èßm8fÁba‹Òn·ic pç©—y’Òf< }ÆÅÒ˦?pU꜌ß=À¸±oÛÄÛGv5Å埪uæÑÀÂxx*A9Šà£büT;ÐÎuÑÅ⮲q _¹‹©ØÕ9öÿkïÛ㣪®…g’L<AÆŠu´¡Mó‡Ä$šIf™0‘—ˆh’ “LÌÌð™9ÓÖZÛÒÊm½^Ú¶Øk-½&€$ ­á!â£6*âŒ`Ð 1ç[kïuΜ3É„ØïñÇ÷ûòã°f¯µß{½öãì£Áæ ‡î'ü\mì·j?]DQò´Q^\È”3„L7."æGíüNظ?§ý¥&Ѭû(«S_FS+"0oÏ'%œ‰ý¹W“™¡ðJE¶ió Þ/óo\H ‘'µ„=÷RtÖ±AÓOeý7ãØþ«…¦Dnˆ®©ß‡°ã*·çœ0³ïCì[†i£(ùש߇€Ù÷y¾Ÿý8x†ÂÎ6¶¯òšÿî[kÒi]XYý¶œ·f¤…–—s?¯•-œ(Ÿ‘97ne�аNuý?.1 šNà‚{Y7@lç:§„7+lD÷íû½ìM±÷¡ÛÇ'"2ðöEn(;µŸhþÌw¥ÏF(ûmþôÉÃÛ‹FÞ¸]ʤ=û^|Aßù¢?,'ٶ—­å€EøÑk6ô`lÏÍìí)}ã§¡È!¾¦UÁ7ý}m{fV÷Ÿf-¨V¶¼Ï"9|+è×,Uѹ;c¿8± ¦�‘kÙ²Õògæþ_Öî§íÅÖŠ´ï$hNTÿ¡9í$²7þÎgD bùy� úÑm;Ï@ŸºìºO{ž_€8Ìm„QÑÓÜf¾;ÆšèOûþ¾} âK‚_âàLgߦH#éQ^BØ+vàE†šŽé踇Cõ§áêÒl1p—Áç³­À×/§ˆòA¹S|F¾G •ƒs ²#9=Qý,N©ÉïJöÔH¥ O 3ìØ¨Ù¿ã4hÜ‘ÂK¥)bèn™òõ+ïñË‘0ÑÅe÷þ¾]F-ÊúWh™h¿¬ôw¨Lf"‡¯#Ýßm—¾R_Imè5¾ŠK+ÒÕJ½š‚¿$ù NÉ–b‘æ?çL³tØÆ3 ä|÷Ù‚þ${ðþÜ^¼—ÎRq½,°7Å–Êw­,ÀNñŸ?ÝǾÝðá«Ø™§™¤sþ§Z®¦ÔÈ7 -y#\É“ý„%‹Ü‹3mp`{ù÷-îŽ_è ¶Ù.}\£|ß$ÿÜÊábbEŠ˜¼ ²˜ ä)Î%b ¨k°Ž»/ ?¼ ,|z¦}@Àé<ülÇ8³¡Ãô*übW[™‡ è{HñMƒT7BªðD¨Õ«èF†›PfBPq“g»v£AËßOÂÏÏ—•ï%†ŠÎ¶¤@t˜ ¾�þÂÃà ÷BP2½Þ‚dÕúï=v©+ï@Þ‘@÷0¬t*ŠÉ…wBè°=«K ¥^åÀO¤�Ã:Á›®½%¥¼ÃjLïš.´`}¦çâû;œßÜ=ü{¹ŒÂÐ:GL±Þ·‡lYøjjŒ™DuƒÛœ3BIÅWrjjj±ê¸ø-¥Õt§,X°¿x„!¼}-Áñ…\(À7Áð-°¾¿ñqø­9lMÎ7ü€(u°q>ÀîçØKó—¹LU<~&\C1¿(óqз‹Á¢o<®ìqO¹sxW”®ù8žOe“¿Ð”7E6/ Ç$Ó'ëpÅ¢¨{~nåßÛÞ`Ï6”¶Yü>Ç÷aÊ5+÷ñÔ«æ³êù¿Í¬[Bûã|¶Ë21ám}¬Íþ6¦Š‡ì_6ä²Êp+õÀlÞº-奅<Ë ”e×é#ÌXǤ,’õßã NÉZÇ^—«ö‹¡1¯ñE·v1dzh àÛ?0‰Y‡ƒ…FhÄ·åýym¾aÒ”óàË�wCÿu˜N=FZo‡ßç ™² ]ž,1§üh§ô»ÿPчҗvé Qê¦=†ÞèÇpð2oM:~d8qUº˜ÿ¥/Íßú¹¬KÆòé¨úýä‘Ú¹š­±ï‹Ñüô‚e¾Eú+ÿ*0;/ðHW°È»$bÞ¡�®½½ËXùú1PòÐä‚ðÞÙL7\kïØÇ†.°/Ý!ukWdæ²NýÍ4M„l,¿óÀ_y&#©/ðáWƒIá>&Ó½2[0às÷wý7‹U¦?2g†z»½úäl¬¼*Í` &¥á­°-øî8dþà (;óH»xîáçÕ|÷ñ—#íY¯Í”ê¦õ:ŒE³1k<�ÕÖÖºõûS‡éöGùpÎôYó»¾EPŸ ýëS\›™R"É„r,Ò>ˆd€HÂS{û×{:«wjš¯¢Ÿðý:ü›4û¶6¿k•>±JRV¡Øü 89ò›>&Kþ¬`ѹ•()·^�¾†1òš›e_EY4r±ob:°yô}§t&|3X«H½òrü?ÃËgc„m<B8©³út÷Ë+þ 3êZÐö³S5=¸®[´ ’ã¢sÙÎí‹ô«Ô2O< :¹o-êäïŠ, щ3\ÜÄ!ùáWlÇîô$`æÿX‹÷÷U° Ì~—³—‹èÝH{Ã3˜êˆw&ª5Pt`¾¶:—-pÞdU£Ì![`5§ô?xÚþÞιÿùð!ìØa¡yî»ôŽò?’Å–ÄþÈ/9(êšÏœ¾NòlÛØŠ¦±ÿª±³«6Ék/2GEhþ×|ï–"(zÒ†;g³¥Òi¸‘Þ&þ<mž4³ë¥ØG ?……ÛÀ-fVóÐ4¾UŒ„WF0Ñû3R‹f1ñ_µ¿Hw%€~­àJ¬ñ*#Æ÷ªD 5¡&×@M^Æœ.þ¼ÿ˜èÆÙ¬°¤ð¼™²ÌÐÒ&™Vù‘Û"ÛÙôþNŒüÅÝÑÈ™3ÙÛ©ÇbÏËC™ŽÌº„æ£ÌQ)ú;Š”Ðü+7-‡@‚ÐÂö‡ÿ¹KØð*<†‹ü.±©¢Ó %I·8òÁÈ]Öÿ˜ƒ¹ë¨S@tduÃ0Þ l8Íî’s#˜¨’¥{àP"³Å¡{ÓDiì.†-Ÿ¯ð‡äJ9oúø¬DóÇlŠº!þú+r {îÞ |NŒÛôè/´#y®“#q¿!ï]ÔúƒbàËq–ÝØóìÂ,á‰oã7½‹®‡¢aF¼ß"õZކ­R[ ;³ùwœ]@ÝP°•c·ÑjÜJš*µöØã–¤ÙÑoÊß#4ŸÄ#¦Ó D…õøÆ®{‰REzh »>ÃZ&uIG‰R›]*N)8RŒçMÖÈ–ëd¦N„æéœ¿·šk‚öíä ð<øôkDÉØ¿Y`OǺ =ÿ˜°a~xUïÍ)ø5tÛjþ5ôƒì ˆ©‚ŽünaÃN¶r8¶|5#É”‰%Âp”‡ÆßĆFºþVV ô­¦9¤%)'™]G¨J^ÿ)hCpzz±ÚxÒàÐÖ0÷±_x¦"íá¡åCúÿSF>œf°å½ Sâã8áðå°¾ºÁ—j•Â/çC÷gµƒ'ä”:!–xô[Þðì²#83Åøà²3ëg~ç Á‰¬EØîçìSo0‹˜Ÿ EÝ%}û͸Æ÷£vpm…µ5w:LˆÎû_râ"ÇÒ qxÐüö–Îy¡%ˆuÆÊâÌü­UØÜ^è{~»¸…-¯¥c+ó¿Ö?•j0Ì ù¶3ÿ¨àorÍIwKsí€hžÈ^Ù)n螨MeÛ·¸šæñ~Ô,f%íw¤ Íã€ê(0½ºyåN8 ˜é=? &º-G|N±`쳌þ-VP©Ù!•àìÏ7¸¬…Q’X!%f((bd*Ô¥Œzj$£f2jàâ Âk Ýà—Þˆžqà`¢]š]ëHL1:$•½iBKÃWê÷‹FC.‘ÌËšñ.:¿‚7šdèøöp"­Äx!‡Èt~@t˜:W¨‡.ßT~‚p‰·¤ˆÇècäj?~¦üé˼ÿW$ÃÛÁ… –™Ñ|_ ®ÈŒXñZ˜>—åó¦`N°ÿÀRÁ„ÛÖ`£?`:x,ʳ=´hÌíX‹ 'Ä÷ÚóßqM!ï�žA–-Ì•}ù&@½’ˆúä'½ŠÁÙ/4ÿÄ€“é¼0™`³*ß5šéÄ[0£oÀâ„h<‹¾ƒ”ÏúôS3–èL`më°ŽNÉÚnÍšÒwÈöWÌß/ %ûÅı=ŒëL/ )hB™…úðþ¦ïÁ¡Á÷îÐõh9ÄV‘ÆBÛ¡º1v¨“ýÆ7ùg„À[ ¬uý›æþþÓÅÕêšËDß´rtÃÀ1~^…�+†áûw^Ÿíç/2`Ťq„rÞT ŽƒM ’òù½B³•-͆)äÿGõI_°ZjíÂôÈòˆó2•B^54ÿ<È_¬R¦ž§Aë=åãZÏq÷ô‹S‰0 öPq»=$¾�ƒá®Žƒ#4y´*5ÛCx&«h¡¿jëßÁž¿ß!”|î Í¥ål ÿý,nk–¥ÜÂÇ m%;À³hg_fAüĽi'ÿ™?ÿ“ÿg¹¨½;éÖæôÚCc°0ŒÇËÊ ¬7÷ -+ñ¥´PYÂgí£jV¦)ýÕa`¬hK±ç·­ì±ç¿ïkÄ-ꬿC€Ý‰ž8¥O”¦‰\¸DPf;Ÿ¸O£¶‡’p4^ð¦áZ|hòž É ¿ÙªäÑ[ö€®…ÞÞ%™fz±aâú�0΂t<ÿ$J^0!&«—´ÿo¸ 8¼Šö¿Ù«hÿÔ›PÝs 0ê–•%…iÿYÁý�”¥³GÛnìŠ>nq~¹Ï9Xþxà¶£.Ér¨×Ú?M |˜">ÚÚhèp$-ðAÒÑnãA·Q8Ãía!« f‚!q¬xôÓÀ‡Fk¨®d¼Uú4òêE4åáƒ`Ò; ©¸ÂÑ ’*u½Ì|ŽÐ”`à/œ¸q°{`m—ÛäNg0i¿=¸ ê nÜ' N>9}­,Qt (hÔ-]ìd¸)h5P½0ÍëÓ5ÚæFXÿoɨòÇ›ù'„ÀSìœr¨|Шùà8ý„m¥§¡µxbE²VåA•_L*ßIF-5öÐC¨¥f$£Ê/•ÿ4r4[UùÛýVP1¨üÙªÊÿ£ŒNÖªübUåûõsST勆" <„’ U•_*߀*/WùƒbˆàˆYØv‰tt¹U:x´[ ]_¹ýÐxD:|áíáoì’ö\xçÆ®W°ã…Ým‚uÜf Ë¥À˜È‘©{ÅàÝ <à÷@‡4?bdÑÛXµ¬j Z’˜^Jb3/|䯎¬Sb ï†• ˜YX˜¡È Ãûçqóy¤F|®(Àäàh7VØüóæÉQ^Ð3xû#ãzq~Õ=3”t3;Ì»ì±K“s‘ÕÑ ™ÈlLc÷¼L¥v¦ìÁ±×I % ?ú‰Ô怶M<àIu-¯Ã ï•LŸxP1t:¥»AÏT¤Ûqà¥òtl…(M9̨¦DÆgõÀ:ØÁ‚D³¤Ô¼%dâÎD}~’*ê×ï÷xKmG?‰‡:x± ê5É\Ô«<LÔÒ|µv©)=’Õ‹SªvËÑ,[o|C*ÊÇŠ<Þ§©´^êµK…¼"¥Ø×yxgÌd‹'×[•`g€`Œ0öɇLA}>jàõ1R}Ž6P}P÷Šé¨MíÒC Eí Ø×y”š“igú{“»«#ÛLüpêK´û£ÅÀšLè‡&³*ÉûXcQÚËfSgO¿µ·åµ™>®¦o)¡ „ŠwÞ°Òñ/é PÅdE´Ç ¾IÈ$OX¥ÒibGi&÷iJÍŒÏ{tMïÕë”ñ뉼G^«GKr­Cš‘+J÷AOÜ—iyó/ ëñ8±"ÇþM{IúŒ|° BËNôGòßžø-ü°çÿ]Øðü(%eƒµ««çs%Gâ­áÏà”õõؽŸ†Ï} ò|õ%ìß è_úÕÄ¡y»©­˜ Ö¿KJÿÅt1¿¼·×Ë+™(]t—¿NËk |ÀŒq'¸WlÅõðÌÐÄ~hG()mFÈ7fíά8ñhVœ’;Üß®:å¦Fü‰¦³óÅ)ÛüÙ¥½öÀG—ÑiW…ãF_³.14ZþBsØ<nŸ©ã†1 ¾QbAјéz<» 1£ ó:ã Y0«ÓœÆNãÛX(˜‡¾™RêU((’l•¸L/x8A1Ä™T^!â÷Ô=¾SÁÆþA6¡°‡î°žF(³ à£é‘„s8e>ÐdQÆ¿í(nZ¤a_þ̘Æì-X[Ó#WdÒÈ#Céq t–pÃ]JJ¡å¥‹˜NŒ¦³aº\ÄÑëÌ“óŽàæ4ŠËB..Å/¸€Ì¬,ÿMöAU\BkKÆK—,ØÞÉÇ-ÒšiöŽ&)�™¤ÇîZžfø“‘)òö£µZ®‚Ððw¤±¿XŽü;”W®SšOZ¤ús¬uÑ©(…)[‡7èý0 “ö9´Yõ¿@™%£2 ]ÿ¤Ç×´L Ë,”t5n.™Çì¥b™â4ÿ)r'G²2û¿ú7Ïã»à�{0Ï—b9~!ßšÕŽnØI©DÍìçÍ72êØÔ44¦¦Áôrß+¿¿¹Ð†ë6Ò7Á3‹*†{YI`ºÅàT_òᾟu°1ès°5ÓøÅT²bÞÿ ;ñj]ÇŽ<‡vµÂù>ºÆA òSÆä\Ä÷—Š<Ûq_PÙíjF~ Ér!ÓÞfT"¸²0´MdÜY5ê'Ë0êEŒz¬ÏíÜËZ$v±5ò[Ö»{*¯2ÜŒž˜Œómð«Ö³‰7s\àŽ+:°¥¹LL¦259v½šò¸°þÒeJy– ¬HG{ˆvÙÃŽžrìÙÿ›B�c0‘ô(Œ22eL €÷¡1`1~³T§5îÄÓ *Ps<¹­ÆmÐ`ŽÐ+Ò"]Ÿ±ú‹‰¦Gʔʔï-eS·—r÷5Vä?ÔˆŽhÄ{YDÜ:¾ýJJfSi~Jdåg8 "9¶Bó“FîØÂ`~s)2ÑOpÚ ö]8'“×Ðc VLƒVæÖ¢”%u1?w’‘ù¹'k0U¤â~îïØ‰Ær0D„f ­_Á¨6ç.v}Ý»U_÷yF™ÀÊ´�›‰Š¯+4;YZS3‹‘ÈcdbŒÈ¤K$:öàÕQÑyC<ú ÊÍAe�rã›’k”¹ÌX÷€Ì¤@ÅüoCÆ·°Œƒö“uô¾ffžAÞÃi¦Œ¸ "†ÿ™ØQ=näÈ6±]¿ŒYx˜ATªY¡îo8ŸÛ€Ž2Ε—NãúeËQJZ>î‹ÑÞO@¦‘Ùø€9/s¾ {Ñ?ò´ƒÍ^RpXvº*tw¦? A³Á àýnlÍ{hXø~|ä®>e w³ÏÀì/ö²ì͘äüBä›xöM*7Gœ}úû:O({Ê»°EöÍEÏ.ÃëC•=¾Ï›n†@‚?ºåíj\ÐxÝ[îg„öÛX9®ø{¡"#¤?u;CwƹÄ7X ®o'ªÁÀ©ËÛŒàxMZö1mŸ¸Íès  w„ª“ÍbÖ^Ü^“N…ï>É6܆9²&l�^>ÏD+Á÷s¥(ñº’"kï€.ƒ,Œû™ fÁ5Ù‚ëÇU³»£“ØiHò²ñŽL\ö > .‰CŽ'*.‰©ZqI`ÖïîÀyâ£Úe8:€O|ºzU:îKÛ¡¨ ÇٹûSÎï1 Íì=m©k[‚ÐÒŽç( Fý¬ «°/‰6°ÀÜ×GÕU«a¼<%»â)áÉI^­†ªXOi2æ´~!«÷”nb‘U€{JlEaò·¸–ÉBÁõɬü[˜­ëQÊ^Y£8Hê¢+Z¢Quþ<TqÛ– ¸JY¨gÑÇ!Úé�™–Mðþ%üÈT¬VÏÿ~M.JçÔäȇû¹þL“£ïßíÒ6/›÷W¿ÿ1fئìRWJI_Vô•(]�}¥¨*1?Œ›8 ®„æ:F:xú dgк°]èpq“þ€ ǰù²Vwã™a¦dîFÝí«RtwŠo-¸áYŠÙ—Qo‹f_•]¹Î–¿b:;•Ñù¡{ èl‹ª³Ï-FJúe­Î¶p½—¥5u±çµ:û×8”DóéŸqèßb Ú£Êï{"ÒÂæŠvP~P~wcu|5·:Ów;ÛHÁK!N!G%šæ.FÞºÞ½X±y¥ìWR:·ysR"_°“Ѝ(_óÇNƺ=±q LQarh7crPŠ›#íz¶ü´êVÎ;"%ЬŸ(´ä³öOy³Ê]¸˜×Ãåeò$”™È£lùöátGpN®˜ÿ¥Ð|é®R¶bòÄ;h4@ˆ  ˜•ý¢çà@½îâ%¤ã@ TbgþKÁB6Pk gd¡ù%,¡ ¨ŠÑ™¥e{÷¨eg+j:4oU®È÷„æ‡YZÓwY '‘‰1".´ÒÒ6P€æß¢š|ø�ôżi™b<ÃP)¯Í$¦›Ë48BPVèžL¡yt"mN; &團8ö÷°Ájc�Dpì/Pœ¦fž… œØaúÁên‚GùY0¶~ 2XS§<ŒIñd÷;‡euIž8Ê–Ç?s'¼}él9)4?È.*õ{îMÁÁÙ…GËòÚœAGFŠ3èË�…t)ð!›ÑvÑD74~š!¼–!ðÉeÔÌ#lc³ <dvá^àzv”±àúOîGõèe{K3`ÈKaÈ_šo7Ò˜þŒ1ÖÔ# ø¦;öc•ê° Íc;¦,ÑïÐh³m£-(ð\Nq qÔ2FßÜ« }TFg2Н7*£¢fÛ(—Qç÷FåÓo$;;Ò¼¶ðˆ¿¡u!œ×&´8Ù‚­Ë‘5*„Çi<èÁçÞçl9ïsæîrJ_°½iðH%{;ïÈŒu,»»…Õ¢ÃôÆ"~˜,Raò‚ð ž–¿ÖC$~•~pF&®ù±æZØ~ S1çÇ÷mîŽ@ÞU‹Tv)¦ŸüÌ�Š=îèôœ>9šQ¾_ެïcÌ3Ä„ù÷ú™8õé`‰p¾RÍ©©‹˜þCšã5'ªÚŸcS¥Q§ïSœÀÃÂ#.h¼ LhpipŽ~B³œmÍ<Uú–Cj‹|ÄÎ7?̶’ð½?C¸5òÙ—Ì4 Oì<¯Ö+r‚‰\Qc’¹w¼Ä®bÆ×Q.\�¯FĪ„*d‡ðûOÃ_¾ÅÎuˆÁâ{þkÂçX)#q?%$4ÙnkÓpœ‡­?Îh|3'âÈ ³wx áaï²Íõ–hZ>šwàl+ëÁ(ìÝ…¸‘pkfû·…lMê ùiø0À¦A~ا¸véî¦ÿŒW•K½yçóŽäÉÌœßKæ|'.æ½tÃJû¿81‹.æ…|`ÎÏá+P¦Ga%oÿ…¼‹µ éš…Zc>¹a!s`ëÒèˆ_à xð40àÿ�ާšÄ£§¢sßxl³ñ|ŒñöÝ…†û-`˜WîÕÌÞþ‚¦ë‹Aæ§—/®»yñù/úóâêËìÐg«îüîá?ÑE^|…ðÒôÞ«Vºñž„I±oàâkÞxñ…C:k>bÀ“¹È›x²Á¸œ lvƒ:Ž \LôgNJfÝç¿VÞcÉ?&4ÏA]Qµ'PHöaó [ÞI«Ô=°—ø`˜Ðü6¾„¯;:qótWp˜±3Л$lþ-`½)“]Ñ8ÉìÄìv1ø 1T2 ´½ýL".ï¬üQ^Ô ˹ǾéÏR²dý¸F=CØ*·q³/ö$Yò÷ùÿézÒXKÑŠ]ËÏýàIñ]<¾ðÒ=Àõ¼°e—|P¬:"ïr„騋pˆu4XðÓѸ[²ùO}ì2Š]Néó<ÙÞ~:ñôé¬ 6¤-É" ·ä_šŸ(%¡¤,À!Ï€[°óòÿê{¸åèÔ#‹#ðuϾüuÏ‹ ,œ„gªºøñý@_‚Exªƒ½kÝfmîZð"%¨(z"é=à›Œx¼†ðZ#Ò/z}ÏüÚ@_ÊËØK¬„á<Q³·•ñpR¤_£ÚÀº'–eIxïEä»èZÒ˜1NƒÀèS‘ˆGp"_€½ÌHŸ±^Kžü!²ånä¹³¾Â“-'…Ímð#8R: £-pi„(½í¿%² ü_‘¼ß’ÿ…Ðü3t;«öãÇÅPjbp4Äš×c¬KÉþ7ƒs/ãGñA¨Õ·¿ÃÈØì¿k/kõý)r/ÖËhà[‘»YÝÒ„'øJm9¯µëÖ¯x“ÝÁ…­G9Ln‘Žç±K‡ð–÷eÆ'R»oÒ£øÍ9¶}3š`r wøÊ;ðʸ\(â~‡l‘ 3l’-zÜ^êÔFÇZ„ïCC×a‘ÛÅÀþ$kþÿöÓylÍç…µŸÞo‘ÛNǽOÉ.]ÆÃ¥ ÄàciÂK•#jZÞµ /½î/�ûm ®ÍHÊk³T<‰·íÈ6É‘1>Ð9Ln³àÛçb`ß‚¼6ÍÑ$e~o;äÙ:­’s#ßO‡Bøú ó¼¦áuv†Ýtnš§á+‰/Gù7;‘DäÍæ<|©d Qxâ.øÙ|Dhq ü?~ÀÀ¦ºÖŒ4ôႆöîTËðöÈl:«Çn:Ÿ÷n‡m»—»+£–/ÙZOgµZ¥#‰ósmÛm iR+±È¯á)}ɶݖÿO‹tÄ·ËI9ÞŠEA̓þV1X˜ŽÍ²JóÖ…“ÔfXƒóÖá}BÿöÉÉàätQx© Ô†ˆ'1†Ùñ•aÖa*ˆå8B¾4£ÿéà¼íV©'¨äNl(ˆô4ëñxÝma½ |Ä86h{‘…áDœžlÀ»Þ";á?è¾füÒæ ìÛù’ˆßÿGŸþ|5?–•#„tٖį֑è~þ–2½ 6Jh>ÊM °ë~ÀîÆ NOï°=ÅÃôCÌ›¥>|³ôTdÌ®Î{|?¾ÓÓ|_tò¥+w[QJƒ|°yø6{ÙN²mÌkk9/ù· ›Ç3ç~^§ò‘.è°¡¯lP^Rnà7ýH-xhðôB:ÿ”Æi XEÊ.6|¡rµKÃðÈ¿0«]”_‡ïU.’ÙZ“ñtyprOøû0³û×ɶ-vyж%ß¶Uy•zÞFG°…_/â× ±›ÊCãÏ!C8‚9áö/‘…,x$zúÁ¶§m‡½É“KYðΓ<vÉ(Åòàøs)ÃÛs¼…þmPè¶`B ×$l*\2½On{:-ݼg¼Ej‡Jñû B¼{ÿ¬éû°ë»rì¡ëÏ$¡å[è+ãÛb&ÖJgGRñ¸‹ò¡æavs„ò}¦goÑ¿YZõÈSÐ,ßøjø+hTìBÉ›bGRkQ¨°ÏMÄËmOɶçPvìÏÀß²mK²¸ŸZ†cþ`ªœÏñ­Ì$Ê'yÖ<p”ææM“mÏœ~cˆ<Êéwuï$[^¦Ëzdènã Ó«Ô #nú  &Ãì‚ÉqBMÇôöWÒôÔ`xz+ÁÿºÇŒï–L`ÂÚk¼¶Í–{ ÆtÑ^#M±ÇÉA>ÚãýùŠ ¿<Ή/ùz%#!ëà€ÍN4Mêà½ltá¡:|åãá¥Îš–6ɹÝ_dË“¥#lpÞV{° Xçß7ÚžAnÆîè3å 3(¿˜±v0Í=oo<å:.òC·èÙ>Š]J‘À½’f´‘ †¸ Úv —ÁµÑçH�ðqçv› ËØÄz f7rv²€R·'1¿nCârÚ`¢Ê? Wcɵ-{± ´,Ó¶AÛ:Ô´¢Ü‰ZVn›!Y?†ö_#&&€ÊXgç/c9òÝ ¨3Ö- &âTL6 V‰n¬c— †¯íe·”´[é"» C Í«Ùaö^Eh—³`§"´÷áéõ‹F¡­ÁƒóÕñ×Åç¢t|ØÓr`†4÷cƒ°›“‡¥žÎfç/¹¦±Wµ£I‡á¶“€AäðXSÐÆç_äGãÁu`Ý»âöŸ½ |€ˆŒïçŸ> ýOTIaÃêlŒl|ÃJâ9ɺ¶Ñò*¹Ïµ¡­l”žG7LÉrånÔoÑ‚Ä'äòŒ~Æ!AÓW¨|Ÿãh££>U+´qLpõíìbA”ù2v¤E”^³K—œÒ^èE{ð{°)Å“S{°<Ý,1;ƒå™8)EKbýU–E> LŒ'¦ñ…Ò1XŸfvv„®GßuVÕ˜:ïXä‹ ä†ÇƒsxÖÀs<Ó1<÷ÀSIq¤ç‚­{[Ea'ôê°½¢ð__BøWYXâ¡g žÏ…ÑÃö¶žÃÿß½k/šº35váÑ=øßëÚ±CDÁÖel{ „·Ûø•Ä~íç¨(%ã†]{$õóçöÖXOI.þ_^ŒÿÏ®Àÿ.Æÿ—4âÿ¿Y‡î+üøC+ýxe+úªÇØ®Ïe°·À™%_ô÷\\ïrdÀ,ךavÀ¯Laýa&õo‰¡3ØÝnÒ‡bhS®1‡§Ý€Fb¾pÝÛÕ¯{ !TÏ|xÀòṓ+=åôTÐSGpo«°ÓšÞ+¼„ÿñ?°É>ÒXèÁwë8Í‘ƒs3z>udôüšzó`(4´ÿÚÀ« > öìm­³fäÂÿŽŒbønFü¿(c1ü_ÑÿoÏ`='<öbF+ÿ±;ƒõ~²Lyv+×t¢—{eß7ÅÐõ¯²#28s¥àîŸF%$û{èb­˜$üO³ÌîTÃLô7Á{‡ÙÞ°ÂUW[m®rÕÕÕ6,5WyV¸|µžƒa¹k…+§ÎÕ°4Ç^Wç^ꪳ4-õ×Õ¶ªÊÝÈãÄæãõ5ù«|þ&·¹Ú]SÛP‹±Ì™ ._í ·Ù·ºÑ h³»©ÉÓ”e(u5|×g†‚=U.ŸÛìmtU¹Í5ž&^‰&/&õyÌåP ó_T—W>Ór‡¹ÆU[ç®FzU“›%WJ¡´/Zÿy ^c£§É箞Õènra­¢˜£Ö+h®m¨ñ˜<>3«>´j ”りRëͪcó§þ™ãƒŠDó-ñ×Ô¸›Ôt^s½ßë3/®©mrWùÌÐÒe.è—¹±©¶¾–uÑWÕƒ8®¦&×jjG©Ç_WÍjäYâsÕ6˜ëÝõž¦Õ¬«4)Yi^dïYÚÀ*Þ`ïPPå©Ïñúr–7¸r.¯Ï†Ã¢[ÖÏ4H>Ÿ«j™YÇeÐå¬ïç;ù€A!Xs…¢öëie@X~~måšÜÐí ¬Û'𽀂ªø:åÖy¼8nË\ Õurfù¡»j¨«Ôì^CPg®irÕ»)¾ÚÏ’å88Ë\^*wƒy©«i‰k©:¹ 8Hå¯Ù¼öÞ+³Ï�ü-òÖÔº«µ ²Ž7<ÐWÕ¹¼^(´>†(†(»ÂSÛàs÷‹çià€îà]2´tn`M¨�p–Ýz¥”ćÚôKë<K  ›ÜÀtîEpûe1“Ü�éýÕ0*ÛÁ@ù–yª‡‹Áéˆöd)Æ-Ð f±á+Èrh¹›§wºAÿèëá™­­R‹oòÔóÈìçàUé_¬~˜~íÖ—Ç{.òÍPÊÓ¶i®‡“J™²„a°ú¡¯Ï3äÖÇÖ§LíºÁj4h—E«ØŸ_ú×EUÛñùBÏj‚‚øÙíÃa°£ôÊ‹ÑbêÇ÷J;¤ %bˆÙÄÈ…×íäê}¨Éõ陆rÒþý°²©Ö7ôª÷O¯Ø¦Á5‹. ]zŸ2CÏa¨|’]²z¾«Î¯¶[Ë¥¤þg+ú,ÃÂLÏ,f ½CáG%‹¡ÆS‹ÊŽ)j�ý¦­*iå‚…zîV²-øÚåÈz½�Ò¡$‹§úg›9pýbê­4çJå—ùª°ƒ(þЫ¡ëOR1qëpå~à:Žû£_£ýô¥Úbãð¡N]ÎeîÐô&¿ñJv&ê‹ðD_«âWà§{ßü ñ´F¸?«–gŽ�åq…òì0¤KÝM¨Õר ®¤dôY Í?‹–×ßɨ©©­¬XO\Ñûzý¬/ª_}T¥˜QVfÇ*+Гmà σn­4ä"¹cWçñ°¿Ñ\n¹g®’N'hõ*ˆ–¹ºÉµã‚S_ƒ“ApÓŒŽ¾¶Á0ÓÓp+M©hÊUëe¸:éãíŸ\55IUÍäÜ%“roŸ6å¶<ø—·dŠkJµ«~©zphó˜œÖy\8Ðê>F:1%å[ÑäYRç®÷²Ä|b3»•uLjhùýã)úW;äzúxM'x•isæ„ê,Í|„O@tóÆ™öY4¥d&Ù Lðɾ»ªªÜ^oì¾'CÈ£…ë÷ͪá /%&ÉÊÚjMºys˦æzÀs5¬ÖÌœ3ë]«Ìê Ò¤%°7Ì^˜€5î›P}?p’ºÕ‡‰›‹ÁDw…§¶ÚPí÷ê< K«=~èVƒDušÁËþÇßySY��†&ÝÆB�04u2 h¤ùÐ�¦ÃPˆ*öNN« 1sCtVaˆ:üEã ,—†Ì8&Jã¨4ÞfŒuQ¢gÍ7¨%d9ßàòû<³;Ø{ÐÙ3ð!5(žW=¡8OY«Á])ƒÆ{0ô·ä†£jˆ5VºÜ-oz‡ýÖF¡­UêtÃ$Òó ™ååå�¸J4ôS¡æúZ¯ë†z{³ز®vÉr×J_¶×c@õW9Ýí@R€bÉ´b|§¡XâBî¢äå™ë\>°õ\NÊžœkˆíCŒG© à ×5rœL/bÛÍ µž®[5áÕ>w,®t™«)7gèàX$ B,Êâ‹+CÁEZ™ k±¬¾óQÒçÞ[aÓàJ<ž:·«A‹Zk{*ìªòé2bÖ®îq†Ñ`°ªš «¥&Ì+ÈÙˆÈÆø\Ad"E ”5Ñì:«Qæè(j¥‰Zª£b ‰P¢'ðÎ ÚB5(e: o ‘¬(¬r „{ê=  ­™�1|v,:3KŸZ1>1çÖl%ÌâßW‚Ùc«¼ŽÎVÂH@R!ElDfÙ•Bq؃¶ÄJ5(È”#©Í÷•°Fß7`«+\¸†HjÃkàB”Ý�yŽ/²þ‰FŒ"Ykí´lœÝèñÖòedUβùªp,bVM XBCTö(^,‚âASæ4BI±ûa¢ÑíU\)rL8µÜcŠ‹ˆF.3hä›b÷ÃD£[ ZɧøýQ”`¡ø=›¹ú¬×è'k*ýVe‰Â$‘ºÓOV1úÍ ¥€†ÿV{}îzÆMžFw“oµ½c°žjE•r¡ÒheiU+ó0iålV&­ÕÊÙ  ³5j‡õZ™áb´2ÃÅje†Ôke†ŠÑÊ «•²ŸVæõeZ¹ŸŠQWé¶XMØ_kÆ(ØXmÖO‡ô3Ìzð°+õXîSêquµ â�wž™yolr7VVÕÖhl1Û§šï4®Í”å]ð|ÏÄïÊòMYâsüž™ Ç{ƒÄù<Í7ó'Ïá?Âó;xþž­ðü„ð?€'’,Où?ø”ÀS Ï o×ÐŽÀóùò39>mê ´ÿ]ÏýPÆÖɼ¬åðdÂã%ˆÏZxZ4áxÏfÍïÇЦ!ýÿês<mSeùÙY^O<c 8n(ÏÉ;8ÜðÕ;8Äçù²Ü;•?«n—åœÛùï]Wxzx0ýÓdù™BY~³H–ïƒúM¸K–Ý)˵ð˜à9á&x,Ų<âM(âið¹\Èa;ÀVÀ¿é_Àçvþý£~¢©Ù-nˆN#ÕÍ.qt¶­îúºpG¯Ú½JÝþÕÅ×NAH¬Ù�ï¿•<`¹š­ÑÁÖ«3æ5T¹üK—E÷äE¾ ñ\XŒ›  îú¤nÅÑæ 8âël/ŸoÀêV)ÜJöqiËWÛ�Sé¥#¾ÆúŠv_ù¶/ÌדpÍk NR‘\ƒëƒæÌÛ²˜2ª±úõž?¶— Þ£ž‘åïÀó=xLðüŠ >»áySŽ÷,Ðü~(†v<‹¶Èò7¿á©çG?縡<oAÜ€Y¿å¤_pˆÏªŸÉòy€©ÏÊr<ӟ帡<k)î�þž.x"ÏrùJ#¾LÃÓBü¥¨0q@z’ ®þª+ÐS¯@y…òG\>|@z‚ S®@6 ݨÂä+ÐMW ëÿæ:+¬öÙ†œ®¦_}£!§Ú½"Ç»¬Þ ÎrÚ 9n_UN½ÏµÄÓØäŸƒÏkhò<îUî*CŽ× ¢ì_e Xã5”;Å,€üwÏ1xžú+ð<§ þáð£7dùz¾Ô<ˆ?«yþrT–ëŽñç=x^!h¼.±�¿ö„'¤¶_ƒßïÄ—Ó}Õ'ŸXØ×'§B—Õœp;@ÀñrŸŒ¯/¯5@½2ܼ ð=€›†MÁxÃey< Ée€V„# �Ç’åM� ¯’å.x à€_�|àêÝi`«�N-ËsnèC8ÊXh†rÖÝå|àe€—ŽVšx“,\pÀÝ�×LÍåç�܇t€'0X4ýL€ðõ�·ßõ8q"” °à‹�<pü­Po "P¶³ ÚU\מ6µà"€ÏÜpÀC�Ob<€I ZÁNg�\Ð pÀj€»6<ð9€kÁŽxàŒ6} ˆî& ôÀí�ç",þBX õFh…z#´A½–A½ž™éG!=ÀT;¤GXé΀ô¡Ò#œ é^8TË™Ya¤ˆ÷`ûΆôç@z€—C8Òcüy^€0ÀÉ�Ï̇ô�Ýéî^�én¹Ò\»Ò\t¤X¸Òƒ?¤xà\€u•/®øÀM�_˜ºÒ,¬~x¨ú}4à=Ðï�ǬXˆ_&è�øÀE�÷¬xà€IWCý�f�|ÓLm~XØå\»øôj¼iÒ¬˜t ÔàD€g6A}N A}®ø†7C:€›~é�zøs ÔìÓd„`?Ü p-À3�·�<´ÒÜòoàv€Ic¡~�3�nú%ÔÃ�«Öý äñ�·#|ø àe€gþïPî8€ÿåÜþk(à™W¡0ü:¤8ôÍ1€‡�^Fúa(ïPè@Ç›Ð/�+N@û�þ×ÛPO€cý»f¶Á¸*Íx]ê°”VPÆøö!ŽÞ>9¡õª´ ŽQŠ–GžSMú£ˆ° ½tT²¸))`šR¬%æþá2ˆwLoV iS"§ã[Û™©F/SËA:¾’1 èÚô–@¢BÇá €Þ¨¥;6%’x=O³ è•F]=MäY›CIj{RÞ}©OžeÒÆ ™6%Ïßœöd"•7Ùˆöü(µ¼ºúÎz:Ð7kË›m¯ðÓ±øjNoøBjþ6¬GÔw“is¢Z_|pG"øEºx¬]¡$ЇåÄò’@N4åÙ¦MI¡DÌß4ؘ,Ëשõ)cåm2Aÿ’Õ|&B¼u`_F«å•ëÚí ú]*]ÔSз}²J·ëÒozÐßQëaӥߎùƒ=«`œ‘~è]@¿;Nùg€žv°OÛ%›¡ëùøCÌžT®çUzE`ئäisÒ“j>“áתt˜ÇõùX!6þ@ßx­,»ÔzL×µÓô@¿V¥;uô§ˆ~k~~…~Sy9ôÞkµýP¦¦Çv~¿¦}æ£Ú~q¨gá «ùŒ‡~]|ÌÅÕ|êÊ)z#ЯSé³túaÑ—%´r—6ÿÉÄÍI!åÓ ñv}K–oÓö§¦½è¸AK×·ý¦^ðGæ³7øô|ÅäèÓÀÿXe0ÄÈÿlUþ1ô·Z!Þ(m<M9臭»I;núz:ˆþ‚JwèùèæoË2^ÛÎõ©¾žØõÅ@ïŸÿ0Þ‹¯7æ2:}†zÊý:#0Œ—‡þ`ëmÚñÓóú‰[¡£ÿ¸èwªôúñ‡@гâ¤_îAèk!ÐôïÆ¡oÁÀ¤øõÛ 4 gÄ¡¿3ÐoTé³ôí‡@î$íx—éÆcü0ž~›Žo6'n2ÍFe¬æƒwxo›£—Ĩ>¯z/зhËáü§ÓÓ› ^ãdY¾‡ÿ¶½bŠ–ÿfë臈/ý çN•åKqè©à‡§Ý.ËãÐ'½çöøù;€Þ5->½è;òAÿ¦oÂôw Ò~L_0Hû‰·ý@o-¤ý0h,¤ý@¯¸sö=÷®AÚô4˜/ü3}лþxíú˜_|‡~è­0ïHWé}û‰~>NúT˜5Â|ål¼ö½æ1'ãµèf[üôu¸DóžžxízÐÓTºÞ?Øô6 Gâµè­0Ÿ:‡~ëó¬TºÞޤ‚Ý7Ãüë½xíz7Ðߎ×~ o-×úm1íúb˜Ï½¯ýX¾#~ý·cùŽøåã¼t+Ì«µvRã—à|µgæÀú޵æ£ÝƒÐ'½ èkTº¨úñ¨ç@Ï…yè-©šú1{¯jï1΋§Áüq£ÖcþílT®jy[ ÞÖûµö§LgOw½ è‚J×óû{@ßô«ãÐ/=Êo6½ýÅËOUéz~,zëýÚþÒûË€°è¹Úñ£þêZJMœúm„Îü¢ß2F3à†96%ƒð$ËIˆ÷ÌC²œ£òØÁ’͉¶P’3Ì;œù¿WA{›´úú»Œûåè†P<\gØå…yj’~œyL+º¼ýoñ*YgД«©ÿZ¢·èûøežŽ_žƒxï}<°ŸsuþÛ!ˆ×ºZ–'iãiý ï�ºS¥ÏdòQ¦øÿÀHÝ@ß®M_:L•4õÆu’ekÀߦ‰gŶOÇN`Àü_ˆ·õñ¿–Ï æ§a‡b¼fˆ·k=_—‹–ûd¢ms’-dâ=ñ¶C<³$Ë_öóûX~ÅïÃõ™ÞïAý´|©é\·™¤ù«Ø_àzŽôwµý ™Oã:OÊ&Y~C¥£?H%ÍÒÉ/^¶cS|}‚_UÛ ô‰qäg ¥Ç-ìýë¹èÅ!Yž¥¦¯Ðñ×{@oz@Ï7ýÖ'’`B{â}KOCÏ úké¥Ñù<®[íÚ zW¥ëõ®g¥<)ËKôõPû×¹�ý¢n>ÉÆßÁ½o;ÄËý¾,êõ¦2þ3¾;ñÄÊrÁ8M<{ÿyÄeˆ·ø×²üí|w=”B%®¯õ¾(Ë5ê<®ŒätŽNN¯xÌŸõíì·Îâƒxo¬®JíDV0ÆÛñÚ^‘åôýr·Òêß}o㟵óí:>Àu¿@OÐÎC˜W¥‚¢}±M–p¦&›7$ŒH(I–²?ᘲûÂæÿïR»,ßÁÞË8"ÁvUJÂïµ;42ýÅ +røÎÇÍ'Ì%X@ÐBp:Á™ç\D°Šàr‚åÄî¬èÿĶšc(&8àV7‡­7\GpÁF‚Ë.&¸€`.å—IÐL0`Á‚‚½”¾‡`˜`7Á»vl#¸‹à‚ÛV >C°˜à4‚™ÍÓ ¦L!øëOÅnÚFTv•ÝÃ]ùŽ¢pæ·xJÅ~- Œ”ݸôtyYL?”ÝĹ”¿ÂÛ]Ý Í¬äý ìî mBe]YÙ5üÁ”Ûôø”,‚V¶)•ÝJ¥¼>YÆÛ& áL–)¬ôK…ë¾ËÃ)l3ü¿ñ—yÿÀüvŠÆû<Áä;8Gð;§,#8Ÿ` Á7|šàówÜOð8ÁSÏL¦÷ÏÆüÁ©ËÎ'XCpÁ Ÿ&ø<Á÷<NðÁó“ ©|‚ß!8•`Áùk® ¸àÓŸ'¸“à~‚Ç ž"xž`r•Oð;§,#8Ÿ` Á7|šàówÜOð8ÁSÏL¾“Ê'ø‚S –œO°†à ‚>Mðy‚; î'xœà)‚ç Æþu»H/ä0Ö.>Åaz‰ÞNu}Ìá·ãàoˆƒÏŒÁ+ùçÅà×~*á1øâ¼aziéæÌé3çe™ñõ„©æÌyKü >?Ýšçg¡Û²t1'gçfORc²sÊÿùõc^¡ç³½«Ù¡l¯¯‰Ãeʯe.ï2CvõêˆÂ¡¯É½´ÁŸM·´è•@kr×¹0"ýj¬ó²ñ0½!Ûç^ÿãÍ0@óT»|.C¶{Y%»‰£rYuS4dÈÆw!½P Ë«€ˆ 0OHËj⪯­‚Ò=>ö/ˆgºÄ iª<õxÌ®¿hýK¸Wƒ¶_±×Š? Àr ºb—ÿ×ÒGj芿 À]d¦Ð?¥I¯Øñ,¢)éÿCŠ¿¡üÅz¯ynû•ôнWࣾþ±ÕTŸ&½âO(ÐlˆÖߨ©¿ò‡kIšüTÿ… â¸ÄöŸÒþ{bÓߦ‡Šÿ”@YŦwQ½Mñ÷¨ÄSþbOµ-ŠIß•¯‡¹1Ç´”ôÊ9¹¥1éÙ½_˜ùw}ú4}ÐP“^ñO{J.¶þ^J¯ô_1¥Sà‹ƒ—ÿpLú/HÏ+pSL±åK1é×M7êàÜkôñcù燔^ñŸO]<±Ð8`}cÓÿ4&}˜Ò‡‡˜þßbÒ÷Rú^J?6ü|LzÃ}$ð÷Ñ8ƃ4ǤÿO{uþ@~mæý<}+ɯBWÊWÚµ;¦ü]Ð|ïž°1NýØ“^™¿dVòôÛbê›þ`LzÅ¿évñô1å›cÂ]”—’¾m ÍO—ÐøÅÓŒMÿ¦¡¿NÔ¦.†w´¦lퟣšÇ|?›^ËûÚ¿Ü:sMüÐΠJŒÕ_Ã㔿qOÓÕƒ—?Ø_"Ä^0@‚D¨ñâäð‰ªÝÑã“T{¢Ç›T;¡Ç'Gõ¿?LÕëz|Šª¯õøáªÖãGDõ¬?RÕ›z|ªªõøQªžÓã¯Rõ—/¨úIOSõŽ?ZÕ'züÕQ=¡Ã_3à¼6Ñ0F•k=~¬*¯zü8Uõøo¨ò¥Ç0è Sü·ãà3úá°“ gû­ó¥ÆÁOÿšøxùÏŽƒÅÁ?ÿ«8øßÅÁoƒ?ß‚¹ø|Çã<¬¬_M%|½°r+áJü9<\EøEÆþ6ÿª)~îïyx#)±’ÿc<¬ˆ÷¾âúútÞÜÅÃNÂ_•€º5ÕÐVÌåHá°ë¼EdztZUw'ù}¶ž¿áû<üO¢»câ+zu Å7Ó6 K„¯XGùþ…˜|þFðµüRoS>]Ô·~?Iø¶xø Jw–ð¹¾–ð}J»žâáó„7'rüÖ¾Dø2¯ ððÂ×¾‚òð« ¿ãg<üá·¾q=+|òŠR®ÄÓ©½ï¾çŽ(’ÄñÅõ<8ŸâLÒ÷ÛF‚7QüÖu<¬˜‡é1ñ›(ŸûbðyÄŸž$n³•?eÜ”ÿwb/ÃSIœßÖÅðᶘüÇìLX^ºXüþvê8•»õ VÆå2á+¨>w^0éËUúóÛ&âŸu<¬öáÍ„W䱎ð=$pc¨Ö>—võÃæ˜r'ü%ÅßJ÷¾®"üocâ+åî£øÏ3¾ƒò‹ðÝ?äau]<YŸÏlªçuÉÔ?tÿë2r‚²ŸKŽ£Â‡Žd>Ž‹côÆ¢˜ü7|ˆòiü%+újsLü¥(~+ Ôd¿@øîŸðð ÂŽÉ‡ÄÆð Åï¢|Þ¡ú÷ÆÄWÖûG #9ú++|˜3LßEýVBñÛÈ.l#z%á»I®·~=á{(ÿOÿÛa¼?»cús7Å7~S–> |.á1¥püb +=^Kø4š€(bóÝ}»J Rüât^Ÿâ‡y„ï!ýÿŠï'¼™ôž2¾~ëªuè÷ ßõ ž¿‹â?ŸÂûÁÓ;SÖ¦|Ú~Êäî Sú!†ß.¾‘ìãvÂNãE†B‘Ç)„7Óø*r·€ð[›yXÑ3¾¿b×~¯à¯æíKü³O)7ÈÊ=Š(ùoÔç/Œ »Ó¬?eÄÀýS0b`½1‹òaüÝBtžć1úyËÿ¬¼-Æ_ÝDùtáñ?¦výˆòÙ3ŽÿNñÍsyXÑÃŒ©ç!‚(þâseù¢G)÷<ü9áÇŽ4è¼de¾›7’øòÉ%üü‘úrIúay þÁG)Ÿ´­<ü6áŸ%|î³<¬è¥W _QÌÃÇ”v1|ÿùÑLJ<üŸQò!ü|Â'§R¹$ïŠÇÿTêÿ˜qünêÀüP@ùøè ʶ¼_ч˔ø¤à/>DåÇŒûÏ)~÷÷xXáç=1ù“˜Þ¦øÅ4^Êþqò(*·UŸÏ- ~“_1Šü˜ú,¦ø]ä·+räSò ñ°âïý„ð­'yXÑ'¿!|1²2 ~YÉçI}}Þ'|ã>D x¦«H.óð|2Hc ŸKvù:Êçæ«x» %úvM»*f|Io;bñJ?Äà•|VR¹4 _ý”ðëH~ûHÞñ Ü@úáPü¶å<|ñÕ‹qêÓIùt=¨Ïç/1ñ"ØMù÷låáje}]ÐÇWr3 +Ë(%„¯ø¿Ixá Oó0 ¿aá­×ñ¨ÜVÆ%†ß¶ ËÑÊ'ø\‰óá*öñ“˜|Ú^¤øëÈÞ­!|zÙ‘ ðw~ï„ð=?æáÂoJÓ—«œ¿xV‰Oýs–𯾛æ)Šmú &Ÿ«i¾v^‰ŸÀûít cØh}ü¨ŸoMãò#}¹Å„o±µ£û¯Uâß#¿ñç<üá¡äOr§ðçö˜úÜBð¿(þbòëhºhø˜ð]¤%üY¥žä*väš«®'»°·ô²« 9KüµuÕüÿꜥuµKªn½-{RöTŽºÕ×ävç¸ê«§N¾i9U^NU“¯6{Ž€×篩ù×si€\ªk½._Õ2ÈE¹Ü~*×õÀO¼,‹Tþí�üYSS;u2[ßàËY¶´Æ›ã[ is¬îî:O#n!æ¬ô²{#øM9P*$Êñ6U嬚65ÇßP» r˜cÀ‹F*«<õ•^C%įä÷LTÖ@…értÍEˆš-ÕÕMx¡fƒ{%æ¡ÆÂË(ð ÉxC.÷Ô6T{Vò‹+rãFä—*W.’]ÔÃÝ´Bs}eü,•{+óðN¤<v‡zù!¿tS òË6¡�èdèÐʦââé{IiåmÙ·eO¬F솨J¯Û·¼¾QŸˆ]V½2sð¶GãAžî†ú¬êÙ¤1uši¯œÕàð¸bî|‚ª\¹w ºv*^ VûÊÊòÊIevûb•@,ȵRéÒ¸I¼*kÅ‚Ü?X‡U»WͪRK+„®ž­ä_ÍÀžáÃ;´ÁåCÝTÃZªOXÃê©Çy}Õ z\¿+CU¶Ó\úÚÈï—©¬÷ûÜ«*ñna}&Õu¼Wca<Êùx(²ªˆ°××Tçnø:À¯+<ÝGúnCnˆ~,$ε԰²ÊÛ¯xÍ´5  xónL ¼º™kÄJ®Íelv۲檹JÄV»ëjëõÙºWùð6·J~S^¼ñ›0Kk½È¯ê¸Ñ=¼5ÐP_ÍÐzµ,Þf[Õ¸ZŸ€zíJBá%]ãm¨Ôhד©¯ûkbÝPA¢E2¸–xš†(žFwCüAJ笮_⩼P¦¶òhpõ<mPÒäñ±ÞÄçLo4Œ¿Ú3Š$(ʪ‰i+”Ä~¼?ß¿7 n¡ðŠ@C]ôveML‰k”5¡¤Úp`ïêºA{×KåÄ£×SQÀrÑ;ÏU‡Á¥Ýgxe dÄnTæE• ©ÂýZSͨZVínŒ› S˜XTéLÔœ!żªëð4Y½¿¡Þ5D B­l·z£©\Ç]Y¹Äë­ôú\MÜÝ`R¦ý.™öÖ.mpÕ Ù±TØK—¹«ôúëïñ µRU«\(£Ì* ­_f¤jYŒ™Ýq†œ¿7s°Ò<t[5w2+Ù¹¼å µŠ­rv%bÐ6HdöRí`EúÕPî\oÀ{4ò°æ±ûécì3f:´–ª<j¾Òù›`@®˜½ªe./¸-žÆ¡wíнdµ ÁdCdmg]Íåó5¥jFe”‘V*&L¹CýŠ™”Zen²é†Z~¯bTâ=ïüZt|ènvµeìVþª\nþ]'¢¡úë;•ìÞY4@Ë™ñ©t³c¡êûøy”š˜r*¡ Ã�ç`Æ;²¨ÁSɾi�žØ×©( í`% F挡ê<ÅqÓñ~¶@'Wø1ˆXÏ·Ö |¢±ä¼Ó*ÑmŒÎ6Ð~ ±cØ'†`ìƒðg¨˜Ió =Á•] õ!kõAÍìRux½«½àe×Ä÷\˜ã ›©Ñ¯1³ÜñW5ê|:f]Y¾¢r6‰»0ÚíŒuxîj.ìÞñú!›[­ïs^^ýY²z&žÌ¤LšËÁ| &$Ì&WVþ'PK ����v©Æ@���������������com/sun/jna/linux-arm/PK ���уg?ØîÞ+«��7œ�'���com/sun/jna/linux-arm/libjnidispatch.soì½|TÅÕ?~7Ù„%DÜHÐHR] jªQ‰6ÕhŒd‘¨TÑ$@Ð�+ •Ú@‚F Hø!¤fµ´Ò–ÖÔR‹–Ö¨ØÒ–jTú<¨¨› ׄ@-j´h)ûy¿çÎfgov7Q¿}Ãë03;gΜ9sfæÌÜùñë¦]m±X´à_¼–¥1´ó"MË»ün‹§i 9šCKDÜ™Zã=û<-Yø`Õ Q3þ¬K-¨1âÅoŸà@¢Ã5Ð7⑟VQiÑ*>5à„D-”GŸ™‚q3õªR-Ÿš>U”UÓ®¹îíÌSŽ?[¶üÙöËžöÍÝéûNîwO_‹¸vÀ÷U�ˆC; °�p¤÷ ÀõÒ?AÉçlÀ¯nÀµ€bÀjÀ9€ç�«$Þd@®ô»�Ë�g^�¼˜ X xHâl–îÀ�[•<=�'à;€Û�‹�Kd| p`6``ŒŒKüp Ixøû6à@‰ ?%]/à‡€©€Ç?L1¥ÍÌü0°0 ð àW Þ€‡¥" °C†'ÖJÿY€Û­š¡§ü»ÀŠÿ® ? ¸pg”òÿž�ܸиp pÅ écý%Eø-?Âo6ÅÿKÍÐÏS€§ß’qÔË3¤¿HºvÀï”ô7Kw:à'€6ÀrùÛ˜ò¾ÐxpàAM4ð¿k¤û[ÀH@ ÿ�%ýã�c[�?WÒ¶�ž¤ËðvÀùÒÏ6ø3ÀÀùÛýÚÀ¿áÒ½Òôû&Å2àrÀïëßì<¯àüFº7�š•ßoŒTÊðc€s¥ÿt�û®‚|]e ;Láu€Û”°UñÏŒVÂä»p©òûRÍèß‚+MyÜ£ø“Mqó§H½)®Añ³o¼p± o�Ü8P X¡ºÇx‡fèuªfCWî˜|çÜQV›AÍçÅ6ëÊãÑ·Y ¬éùšÕ’|Ñ}KžÕÆ× GÑ“ÒÆ8,§^}Ý ®QŽ«-­Æa³&uÔÝ>J³®ÈÊÎxê×$>©­¨ÕÆZjm¤^S|Qjâé#\™»/xS÷:œÚ”[vÕ$$Ø5óßÅ~1úÃàûÊ•ý õ„íš} ûa¶Iê8ë˜}û2öOlCìïgHìÓYo³d¸Pº7H—í‡ý+Ç ¶Ã[”ü©ßA](R~/–.û{Ö×JûðÅÒÏ>žºp— ³Ýëžýûo¶G¶Ÿ-Tgl«Á1‡m›ý+ë~µüíQ%?ê=û¶ÇµÊïMÒe›aÛÚ(ÃlÛì?[e˜cûöì{~*g¦ö ¿”.ûÿg¥Ÿms»ô³ŸaŸìûÕ6Ìñ‘mœm›ã ÇÊ—7_žÿãŸüêÊ¥ÿ:õWoMwßöüêG^?ì¦?;'gNOûÁÉÓׯXûåÊWÅ8ü‘K¾HœzÕ¥w¯¼þ'ÍÜþÔk)?›¸å?÷ýeMÇg¼tÿÍ—ÝÒ8ùš×^ýó5/åWýäãŠ{Æßèž|mwɰ›³Þþûé›^6³«}áÄw^Ñö÷û,ÕT^^üåâƒ?8kÞü3Ÿ{füº{ÆYž´~Çóçÿ"åò‡&äÞøÑgÌ+žÕ;£æ£ ?Ú¹ø¦<ýøÅ“R~rJKáwþÚÜyÅ/ŸutØ/S~œwÿe‡Ç÷<þùôŽŒìÛ¶M,ùpRÓåùGøiGÙå«§ýdù)ÏåT‘˜`»9«=cö[oì½â _Ú÷ÎxêÆžÊ½àî/n:ýõ£ŠŸjÿšyoß9;ÃrÉÄ[î.Ìk^z|ÿ¤Ö+ßzãðõÄ©ÓöΫ]ýÙÔ篾èÉ‹þñ™~ÏÈ;WM¾uÖ5‹‡>øÄëçö´í/¹¯5_öËß­þó%—žzsûð__žÿúSN¸ð¾õ£Þ~ûÈórÅWm¹<ÿÝŸùa+äñû®µËgÿkÃ=]ãW>ºrÃåù‡Þú×ȾÄ^•wZóÓÝú½ß6½{y¾öLNÏ­¿©¸hÄC ·OÚu`x¹òªõög {ðxâUç½WxÃØŽÂÎ' ï_òö§òþ2²«³úó³ ÛŸ{þºüEo¼tߌ¿5<íÞ[÷X~ñÜø»ÿøÎMé?Ù6eócÿ¾ñáE#~;ã¤ÏÇçž“7å·—ç?¿ûÞÿü勱§NüÕÃ?üÏù/¿[4eìö³î™ÞôóÊéß.ÚsIÑÊmª*õÿU\`¸ñrÄNþÄ"éZîÈûÒ›Máƒ'…‡;MáñáaÕ>vh†-£†/<9æ˜ú€)¿)¦ðݦðºäðð|Sü/Lü™âK‡‡Âìe~Èß馰ËDo²‰^±‰Ÿá¦ø“Mùí1Éï‰þó¦ø&ú)&ú¯ÚÂÙâÛLñÙ¦ø™ò><<¼ÅÄßÏF„‡dŠO3ѯ0…ݦð8S¸O ;�Ï*ôi»Ç™ðš¾ßŸc’ÿ&üSCA¡máø¯cd߇Îm!~²¶éwÝnÌ%N…<öÃ�œ-ãK!ÏÜ%a«¤Àš¼í¤Pû;íoô‹óJƒþÇg@¯áÃaú‘ÿ¶Õ†MËð3˜0VO3æ? Ó&³ås†Ë`Xy–ãÃ÷Ÿ NÓ$?÷#}ö4c.Æðø}ïåÆxËð,t 04 $>ç^îË [áÉ(È–ƒ°‡†aø=Z’ç76¿øÛ1€í’ñWÀ¸¬.ÆW†§ÀÝR`ŒáBžp3aÜØ­Føz¤?‚ ¦7Î7?oK¨<•§[‘ïÈËÛcØ3Œ“†æÃ†-Ãð~ÐÙªÈïaà·=bØ6 ‚n政«¡/Í0\2¼ ú·u†¡‹ §1¿‡Cé;€7Gág ÂmJøUèãn§EÌY‰ÿ<èíŸiÌ+>å.¸A‘'äe+ …9gÌq×,Ãipë/•×úÛϳh)/+ËsyH?®F}ÜnØg /c> {Œ0ÝÙóCùí…¼ó[Còn‚¼_†Ê?ñÍBò¹ú–óãPú•pó•ò üFoˆÞéÈ/«(„ÿ1ðÚòŒu†w@¾0àJƒò@ù2¾m¬Ã0| ò«Û¢W þj”üF±=ÐX”ñŸBÞY7„øŸ‹üËç†òôÚa°åKùýõ_¸>T¾¤/€¿dÉâ÷6…â¿tǦ†ôk)Úwæç¡ö} Þ©Æh¦xÎñv_j¬‘0Þ‰òôýÁ°³njƒR¾ÑðlûµaÓ3þ5Ô¯õÞPy‡ƒß}ÓŒy�ÃÀ¸Ç°5¾ôS{ ™á'Ùú>¸Åy¡öóâ{®5æê ÿòʃ2×ÉüÞDý»ÐÒÏyHÝDÃ.g¸üT]ê/ïDºJ~« ÿ¹ßÕÇß@ߊI}ƒ ßyûÖ¶³è_áîÏ õ7õ¨ÏóBézõ7‡ÂeЧâGBå)ÁïåJþס>vþ'TMáï±?ÿ;àt#}èÛŠBúÄ1Âí¶Èþý$í]”7ÿ\‹æ‘ùgA?j¦†úóP_>´Çm#ðe ·ñÆ¿«Mùo†<20ÁhŒ—øG&5G¥¾þtÊÑ¿KúmHZ¥”} ym]Êo;Úçá…Ư+à¶c9 éÿ é6^ÒÏ+ؾì-çTÙ#}éfè„LÿÒ•N3ÖxDyÀ_ÕKÿøt9ê£ïá>„|÷ý!¤û¨Ï÷…â} ›­”G‡gëBí-ü¤þ'¤ÿ &ùýøÕ þm ¿åöPýí‡|Êÿ5È7u]¨};Q¾½Cãc1ê«ç¦üú¿[oÜW ³j“á-(ßÞó-BÄøzLÅÄ»X¦¿†öæÒ¿ H_üP¨ám…Æ›ÓÀ_Ö£Æú­¿àV䄯s­¨hÁ¢%‹‹*«J–ViEó.^ˆçU—Ð[R±ðÞ2­èÚ»Š®/[°°²ªlé•%••e•ZUùÒ%wO¾çº’EeÚâ²»¯-¹«Ä½dáb`ƒW–TTÌ-™w‡¶ ¬ªèöª{<eEó—.YT4þÂ"†B¿ ß¼%‹ï*[Z¹Ì̯(YÀߦ€Ü‚²¥…@¹±¤bY™v÷Ò…Ue³ª–.›Wµl©Hv]IÕÂ»Ê %‘«¯žB/az‰ÇSVB™TZº´¬²’?S~ê§©ü&‹Cbʯ5à/\¼€%-Z2÷ö²yU‹fĉÒô2è)Zj/ …=RZý?T"œ –.Y6·BI?¿bIIU8¶JŽáKrÂÃ. …ïZ²°´Ÿ³¥‚5Vľ¨¨rÙâ¢Û—ü+áRjò=Ue“—ÍŸ.£áMa Ì DKP Á.]R|ÔÎÂÊ"O ZŠ…•îAqղʲ¥w•M+©¬ºjéÒ%ÑYXðU)® ó—–•M˜W±¤’jÇŸ¤¿ˆ1QÓ.[¼T¶šèäc¥_E]2O›/Ô^[T¶ÅÔ*«JË–Âï<$4ؤ§rá4dBº}‘'jJ‘KQѵE¯œ2%–¤§—-Z²ôž¨KËJJ™Y1Èô#];¤)CAºz(HùCAŠUþ~¤É1..-«ž1x“‡ îXr aÅT+–¤BX±DŠ%«V,9@YB-¬¨z»xIT±Kkƒ6>ŒEUSò+µÊÅô·Uó£g„ajÉ|¡òeƒR\(F5´j6•þNÚ³´ÌS4oaô<ºÀŠØȶÚOIþ̇B1FÆ ߵ䎲ÙÇF™¶cRl ©ƒ`\-Æ™Ø8ùÆØ)4NÇÆ›a  ±‘‚ÃJŠ(:±ë¦ÍX|Ãb0[ÊžqžçíöÅ KVzJªæ•‹Žc|YRY¦•V°F¢óbÄ–VÄVŸ%ž²Å@NŒáå&t�Kî.(Y zNàWÞ³(º�wÖ=‹æ.©ˆÕŒf•/Y]Flg±ê¾RØR±¢c×:©òÊ~{)Ê•å%1ãipÄ’kl),D bKaÁ`RX0¨„54˜ "‡ReÕUKKæUÝ%Lßù²¬Z"­Lie‡ZY¨k+,ç(Óæ0¼ý&º0p‚m&zÙ*sRãÇPâ_´ïžWY¶RYµ´"F“ šIÓ˪ʗ”jóŒ¾4Vs0¨Çá$÷”+ËËæÝQ¹,zÓë7ço4&1U¯ßâ'ÒMAD74PÄÞ‡u>È ª&ú² ¯!a£lÂyå¥eÍS%*¹hѲª²jŽ2w˜~Z¶XüXyO%fVóµ’¹l:á(‚»EË/*ñh‹î@ cE²…‹ïÐÊ—.‚£¹@ÚÒ· è+Zª•—T¿ÄS¥•Ì›'§Fe‹ïÒæCc|‡¹Jª˜VVÍ¯Ôæ‹³ˆr,-«X¸ˆú0¯|)Åóé|£÷U‡Ó¯v‡…g}Ö~@ Ë)EJÄ@ !»’¥ *µ¥% ‘1»"_Äê’?Ì….ù²’ÅË<ò7AhÙâ²j1ÿ@/+ºañÝè´‹®æeÕ K«iÛÐ-‚ØúýbJ­U,œ;ï‚Ê%\¢••–T• zne¥ŒTüœ{‹H¿á^X¾dQÙ…UwƒÛ¡wB)/œ»laEé…‹…j^úŠæ!'íšiS&_YtÑÁ­@Z\ÿ¿ðPè7M ™ã#ý3ãs9åÿÿ7ðŸ!—’“oCüK]¸ð$Êì-ùÝ{þ¸Í&]»tS¥›&Ý é:¤›)Ý,éfK×)ÝéæJ7Oº.éæK·@ºÓ¤ë–n¡tgKwŽt‹¥[*ÝréVH×#Ý*éVKw¹tk¤['Ýzé6H·QºÍÒÝ(Ýéz¥»Eº[¥»MºmÒÝ.ÝÒÝ)Ývéî’înéî‘n‡t÷JwŸt÷K×'ÝÒí‘îéöI÷˜tµOeýJ7Yº©ÒM“®Cº™ÒÍ–®3ˆ÷¹¬O„ã~‰ü?³h£áæ1\ þ†ëbx6êWº}ünù¨gºåšv”¿ó»Ãס¾éÞýC>£áÎfx-ÊE<¸söB_·˜á%к?€ˆ·œá‡!O†ávеAžt‡Cžt“ Gº©šv˜îï!/ºÜ8Cú?ƒ¼èžyѽò¢;ôû,Ú© Ð7ºùÐ7ºW@ßèÆCßèZ¡ot ot¡otoƒ¾Ñ}£ ¾éAßèÞ}£ þZ莀¾Ñå7Mºh¹[鮀¾Ñ] }£ËojtŸ‡¾Ñåš2]4úvº)Ð7º§@ßè^ùÐ}ò¡; ò¡û,äC·õIw*äDw4ê›î©3](Âaº•¨ºi¨gº§£^éÞyÒå¦VÈõÔQoto…\é¦C®t3Pßt¿ùÒ=£{úºgB/é>½¤;ý ÝÐOºõÐOºÑßÐ]ý¤ë€^Ò }¤;úHw<ôn&ôîYÐ?ºBÿè.ƒÞÑ=úF÷‡Ð3ºç@Ïèf¡þénAýÓý6êŸî¹¨º?EýÓ=õO÷*Ô?Ý娺“Pÿt³QÿtW¡þéÎBýÓ½�õO·õO÷Ô?ÝPÿt/DýÓu¢þéN@ýÓýêŸîE¨ºÐÓt1·Ó½õO÷û¨º3 w×ùS,Ú!»ë=g.ì´úÎ×­¾V}¤ï1ŸíÈõŽW1züé1Æ_üŸíÔƒþÝŠ¿]ñïPümŠ«â÷*þŠ¿Qñ×+þÅ_­ø=Š¿\ñ+þÙŠß­ø ¿Kñç*~§âÏRüÅŸ¦øíŠß¦ø5Å´/ä?¢ø{¿OñïSüŠ·âoWü;›âߪø½Š£âoTüõŠ¿FñW+~â/WüÅŠ¶âw+þÅïRü¹Šß©ø³¿Cñ§)~»â·)~MñýT‘¿âïQü>Å¿Oñw(þÝŠ¿]ñïPümŠ«â÷*þŠ¿Qñ×+þÅ_­ø=Š¿\ñ+þÙŠß­ø ¿Kñç*~§âÏRüÅŸ¦øíðg8[õ G«ï|Ìçhêfÿ¢i×wÙô`Óø’E«âÄåºZõ\w«nu=æKpîíÖ´Y]Çd9I£Y„ÎÖÑñZóiû@Ãâôv%8wu{$­bÐÚZ;�;9H—£ùkJþšVëÃpœDÞâœM‚îÉ>oʨÀ…)A¾ì Õöß@ tŽ ËÛiq{;5g£žÚ¶ÚWìíÌooÒ‘®3ø vÆ5ëVÍÛ™¦ü®¹Ý~ ÕIÁøD‡·“8¹ ÃÁø9Êï©SýG $?àÁòØ·Ö>ÓŸÜ1Ý/ʾ’5¯æARËcoêŽóÕú4ÈÆêxB×ÚWëVçj´= mhÛ%íÜv‹¶—u€<æ@þvæá4òHpy°¼ñȇe©=8”Ë:q¯ò±’ët›}>\[Ýò$:Q–öf}÷K-ýùÙ‘ŸGæ·÷E#?kÛùúsŽŽN'hÅ9&ûã²¼ã�I¨#Q6ðq®æ­Mµ·®³8kã,M´ŽGüšÍ 3çEn\ ŽL=ÿà±+[õcµ†îXÀ«ÍSçËåhÔíö Bîê¯X­ÍÝó,éFÍÛ•AYO5èjÎuzÊ‘*Ó™Ó|Œ â¤EÁgI7ƒnO ÐÄ͈‚Û§ÐsÄ 7ôö*ô2‡@/+½+A¯]¡—=zÎôfÞ6ÐËN0z¹1èM½ yCàσ޵ W§Ð˽‚ô®=BoÚè¹cÐ˽9 ½Â!ÐKD¿1Û±AÏ6áŒ`Ÿ#ãç8Œú0ãTjÞQ.ÐB뺂ù:”zC:3>óµ¹ê|*N±£y�Ý^¥p:M8åpÐÇ ÆW >R0‰ x#Á¦ìWØ_³f{g_°õ¿CžI­z%ðÙ? Šƒ|Εíu—Ú×¼Ô*òJv4wóOF¹Íy[ ¿#êb`9ÐoŽúЄW/xû‡ —S€÷¦ /R=œ ¼Ý&<O¼QÀû£ ¯*^*ð¶+uR¥NR€÷³@ Ðký½Q'ê$»½UÏF_ŸÜþ˜/ÞÙà«v4éVÙG7¿¤Œ‡¨ MÖEÍó¡ôß4mfÓÄ9óa ”ÙκjD]­ô-w¬ÕûÚ#ÓÉ“tœ¤ƒ9MŽË˜×X]r ëLÍ:–§½Q¯±‡]É ã”tŽîˆ^žz¥<ö—#óѶ#¤£UH_å6Ò'¸|©2mÆSao@wU¹Xì<º0†²o$Ž´7êM¶ãd¼š^ÕqRŽ<Ê6e »¡“e M’o“o3ä­žz~gУRìvŠ´!]”g$èØ[uK»·Ëb_éÛèX»a°Q(wUæšÝ°“‚vJþ½ñJXØŽ¿ Õ©›epe`]2?öyŠ~´€ÿå ÿÁ<‚õ{žÌC­ïL%\ÐÊUêÛ‹úN–uÆübÑNíöçB2šÓ.#Ò$“Ø9Vë.Úrí ¾-ŽȨePù;6P>2¿#ÓÏ?˜¡ðiñx;7¾âµz&dTì¶Þ(ä_±¢Uú¶šU¾­Žýz¶KmçøMóz;‡I>¶¯ÆÔ$?`gÿšHÛ‡º7=h_ô˜¶-B:Ž…?à;"ÆÂîö¸ÆXÂÙ…^Rɾý&z;£å Ü7L¸íp"oÆíRd†ãqûǺt@ïç€Íl_åûÞ±À¡ahß¿C>=¿5ê)ƒ6íÙçÜ¡·ê;n@:Ôû)ÃÔ×HÛºQß&ûˆø£Ÿ¨iWtÃëö_@=’<±?Ø üzo›³¿'*sµŽ·2¯‘>¢8¼]IîŸ*'ŸÚï96ëb¾ål­Ml­9Wަܬḇ»áïwiž‡F'h5>udF¸Ž$ Y7@öYÚ=ËTÆŽeœ œ½1ʨµ{rrn&lžp²ÛÓõ$Øaɦ²0•u燲¼VYÞ—=J:l* êêlS9öE(‡8ûO ®²(ƒ‰ÿžüÛ$ÿkMüïˆR'›Êà‹P†É»¬NêÄ,Ôyíèaš7eÛ/쯚òÙAN×óJ§= /KL¼‰À ú‚¤¾ÿ+½h—kòLog_”v*嚇òøó?%ÿd™†IÞ=dÔþ?Ö¹#f›Ò9»äí³ãá¼íŠPÏw‚7çhþççØÿ1ÿGcðŸ&ùü»·‡ÖT:^6ÖT؇ŸkŸÔ?ÖsMe ìYËÇ5N×´^7`*àZÀ@àÀÕ€«�ù€+hG½“�.À÷�W�.ä.\ ø. ðÀ%€‹e<iÍ�L�Ì\ÀXÑ[ X8p.àÛ�ô¯½ç�Ð?õžÈ mõŽŒ8�gÎ�| à¤Р{O¤Nœ @Gß› €}Ñ{ ` ßè…]Ø »§7�›­ƒCïp€ 0 �;´7€qSË@oê!¸žäûº‚þC]_Òìkõ`xâ S`ß8aw6ø<[tûؽÃù?Âva}ŽFº‚vÖບõxধ 8YÀ÷´7÷×{ÛðŠµµ=ijÏ œbmuy sØÞ½NÎ[Ö šÖ’½ÀÝ¢o½ŒÇfW÷׃ëjÚ¿ íFgˆŸ ä£9×ë¹&~^žÇâGž+ ?˜¿õæÛÃåánÒAY_à<ZÆ3¯ Np’Eþ³ü˜/j^ͯÂçÛ6è·ÐkÍX+‡i-~W?óKØ:(gÀ®ù¹6Èòfã·\ŸaOÓ¾‰w>êsŒmÂÜ« ²Z­'H<ó:bØæ!Û¼söÉŽÉ~ÚãÁö5í2mëÙ.”íé"Ù¶œò·Ùæ²Ñ¶¿ñRÿóMúO]Ïø†õŸ¿o庫ÖÒ­ÎQ6Êß¾@4Jÿçð×ÃîäÜ–óˆÙ¨¿LÖŸ›óˆuQç)³1(”4tИ&ý~êô�žôӦΑþ·áÏ–þÿßúá™Ož#k°®6j�[-Æù8t“Z=çD¶óNnŸìçwÀ•ìOÓ Å<_|;дMœkÚmèÙ‰öœ¼úåòvMƒ› p¸¸î›ìÓó¤Tδ¦î`ʨðg­V‹³l•s^±†`7lédmóÁæWB:¦Îk©[sH9¯TçN¢m»õj§7ñ£hßZíb½<l.>¨Å< ÖwùCHÓÅõú!³m`°s€9�®‹o(vãñ¹&ž5v…®®ƒOÛZOÏ~fÿ7—Z_–e­Î4àÑÏõ*5]†’Nàȸ›!ãhyg}),C4¿÷H|Ë›aøÞ§Cm6Î’kúìE®6­YO…l(«ß RåÈù‘CާYŽz<eMÛÕA9s\ÕÎsÕF£sU5®ÆíÇXøŽÅ[àøÉ‡|šg“^íÜ lÔÖ#×DÑpíB+}´AÆ'¦4u[Šðs=*A±7Õ20m¢v}ç'ÀÃ˹&\17€Ý ¼Ñüæ¼”ªq¥Úý>”¿î=p+àÞ ×RSà‡ýÜY?çR6ïµþ׿Ë8ÜaôÔ+»ªÛ¿ ¤ »²Xá5lîźьu 9 ¯¶®%«iÕ±®Ùç×<泉oQõ>'úï üÃl1wȾj|%ÜFƒLG¡_£~? «ØghþM,g±Û?JÔ׋ \¦ãØ’„~Ρ”3Éó§îhßÕÌëA™™ñ¨Ëýíµm Þ7||„vºý'†·£n¨2[B^íÞ®0¹I͆¬j\MzV ô™º‹1R”±1T~ç6c[uõ;âöôån×´'jÚ ]‰Å;äfSä–�9ØMvò âûiºn?ñ¢ŒMú4lʈS= ┪v0â8Nh£q¾K´»ýÇØÁ7ñ¾t=®ø_ÎØõ‚æ0»‘Fó®Öm«u»oµžÊo“€d@ßÀüúóq‡ñ{CÊjA™RÛ›ð»¿[TýåØ(¾û®ïvŠ>6Ũ?o×ÿF_âܤ?çxKØí°å’†3G“Èsâß—ãž[Ò`ZÒ`ú @¯8÷ø]ÚçRà|�އVÍ¢åm1te ÿÖ@ °Oꌇ¨7öÍz2橘ÿþߦ6ÝÙ ûf÷$c=ˆöM&ôÈêjð91/Ìz©Yϲ@çte;Gè9˜çB·®‘óíœâtýË“guY]›ÿñƒ+¼¯Ô9½oŒtÖú‡9_éŠï˜î³¹Wûs3ÃûŠVãíJ€>Zïȹ:êõÖ[6æCZûzíg8Û Ú\¦Òæ8þÂ^ydžßk¡‹ÏÚ׉9T|ÊÊn ò³j³:b¾ÏúæÄØ^³áwÖùÈ7ó‹w–ú¬ÎG ¿VØù^ ðøÖß,wSÞ¤ç\éÛ}Q“à s’1mÿ˜ SYšÅïì“sSšõ˜ü<p(~ô—]¯ê éq žx î•>öï‡n®¥N¤y›éáO8©Î êÆå4ò Ž©\ ÞW«yz:åD¹8ÖsÌÏÑÒuŽŸ'!ý›(‹ë)C'¶n8.Öüª¡ì[+fÂÀ¾uϸ(}kq”¾Õëf_1êtù½ bßê1úVÍâíœ ¾¸W!cžì7e¿ªyVë`ïPÄ|Ð ³¬6œº¦…æÒj?š`îoþdš„q6OâWè?F:L{g{ ù5Þjô¿A†¶Z²}óAkû#ývÇÀ§½á„\hCÉÒ®Lm¨Ög‡îQoÒ#Dýq^¿ßùn§í(¿¥"ó‚$;t¥Ï¤+×›tå:EW~;t…éBº²2LWF“.tÅ‹rzi*ºòÒ׸ŒôV×ʈº}‘¢Û~n'e ]è¥b¼Ÿ¸É‹Ú~>„®Ú]Uó_ÿ¹Ñ{b ®Ež©š!OÊ5(?ëÌpùÅ™äwü³ünA\Çå×þÛÀªÂÿ|¤o¤ü4ʯ&¢üüŸ…ä÷ÁgáòK]ÒÓœ«õ‘˜#p®P ôw‘äé7ÉSåg2øa?¶íGßœ<õY¸<®ÈsØ Êó”!ÈóQEž AžîçGŸòœÓòÍÉóf“<oTäù›ÏNLžülpy^ªÈó;Cç´ç&)ÏäÍßœ<MòŒSäYt‚ò¼}òü°/$Ï®¾ÁåYxò¼ZÊsçãßœ<Ÿí —ç¯úBòq‚ò<uò\«Èó±!Èsö Èó“>Cž#ÛNí°‘Û'¶md‡´‘³¤ìpJruÂ^΄\¯ ~“òx»bÙÈÄ¡}L[YÈ ÆÛ™íÅ<¶õ™´iû:½/Ñþu`¼¥ýK¾-)ëûmßgí«:ÿÍï]5Ü“‰ùª°§7éI÷ò¥ëùŠlÒ.vD±‹Áç«AûüÌûigE±³bØÆã”ú;~ÕNËŠ`ŸjÒ·S}[ÒgØÆY1lã£~³”úÍVê÷Þ>Ã6ÎØ0°~cÙw?¢}7”öôþ§áå{çÓPù¾Ówbíirßàí韆êã÷ŸÞž\'ОÎí)cÝ@y‹ïVgÇ$ãÛå™)åé”òÄü¢‹kbœoîÕÞíÌpÐÓŠ!c·¿¨ƒøÍ߸NÇv7 :™ášêOÓš{Øf £/dʰE«ä\sŒE«ê:ƒ.æÒ\oày0ÅÙä‹smñe€‡4ìs…VЉÖI’Ö® ÜAÜxg¯ÃùV'u<vâ÷8ünËtŽHp¸F$ðûξsÍ,Ðiç\3Fý]§ÔßTøƒü׃ÕïT‘æžW˜ôïREÿ6|*çž1ôïésϧþ5”mOÓ774ñŸ¤ð¿àÓk?K?¼ý|ôIHþ‡>¼ýd@ûa]³ýì_óÍÉó…OÂåùÜ'!yŽ:Ayž1ynRä¹aòtž€<¿üÄgCã7'Ïù&yÎUäùâ''&Ï¿~2¸<¯Väyåä™}òü©”gÞcßœ<O1És¤"Ï;NPžw AžŸ|’ç¿>\ž9' Ïë¥<¯þæäùÇÃåùÂÇ!yžv‚ò7y>¡Èsóä™{ò<þ±!Ï–†oNž·›ä9_‘ç+Ÿ˜<_ûxpy^«Èóš!ÈÓqòü¹”ç´G"Ë“rŒã·8—·3á¹ÿÂnì¿ÐÜ5 ó¨¯cÜã[’Óø~Xc_£ç¼y/ü‘‡BßrƒßÆÙ5W®â¿sô0NkÔÍß6r¦ÃNùqv‹ß7x¦¬u=¬¸ÎgÁ<+ã[˜'LøøãÙ“FÚág츭Õu_§%‰ïTòC½y8—§cìÚ¼VñM‹s,­ÝÀOrÔùêÔ}´b¯êº i¶´´ê[²¹O~<âköõúp¤¯~)<M3Òd#-/5À™œBÐ,”¼®ÊÏ.ÝŽ¸ôk&«s,¤±Õ„ã×€›c`:‡åý<¤;z^«~ô²VݜәÓÔ?;Òì>qÚÐæÚ|ézûe† ˜žéF‚–9íÞ8#¿À%?ƒåµøñÀ¯H7ÎݰžÅÙ:¥Ž© Ä ¾º,±ë™8ï�Çò:.‹,[s=7"Í¿‘¦ïÜV½ïÒ2šïLÈ5|ïFšÝQÒ˜óÙ²B>-·ê-çuÈtj~?3§ó!Ýo®‚iœFybå³åÙ|òq®·Fõ¶i|H“?’ž7�ÿ]à÷œ½Þˆ7x¯Ä¨³`ÝïyÊfzÅÀÛ5ˆç÷À)„ÖÈuÇ ´ˆó p’¡E6äû Ò‹B+ˆópÚ³ZõöÜzcÖ³dà? üÀßßA77"Í Ô¤©bš-HóSê Ò¸†õ2”t=¦@Iö´ÁåSŽ<n!ÆÏEüîÓ§U ÜÛ‘5qn�N÷°ßBy<ß1ú4–ËÜn£,0—“ÜCÈßÚW ’?qò€c=§U´!µÝšÛS5pïîþSÏ»*FÙ‡yŒödà=¨¯áì} û3^àû¥ø’V=ÉT÷åö ú™#qêƒ~ÎxîîøAäEœ1ìSÎjÕ{.¨·æüs,êÃØ–FGϫުW{§úGúê|IíÆ>–“µ?öœ¬më1»šó }9ð·5ÂFq6èL3¼=6¾¦ÝÔEÜT𑞷׌ã#ÇL®å×Lõ':7u;ìÞ®6Øs6ð÷ø¿‡86ŠsMR7É·K±µ4‡ÛÏ߾Р™%Úï.¹Iß{wgûýɇ¬Üƒæ4èwèïlOÙÚ]<ÇÇï¸Û2Q®TC÷)WÊê[ÿ ßÑœzN*p>CújÄU˜âNú—±ªqnS\"âºW€¸S\�¼û¸n‹¸4SÜÑŒ½3iˆ;6*<îãŒ5b q>S\/âþÆóTã[õ]¦¸.ÄíB\â¶šâö#îˆÛ¸SÜ?÷⼈ó˜â^CÜ3Üg‹¸BSÜŸ·•û²—kŠkGÜ“ÜÀ¸ SÜóˆÛÄý!ˆÓLqÏ"®‰ûŽwà”ð¸_ ®qvÄí6ÅmAÜ*ćú7Åýq÷#·¸FSÜzÄÝÍ}7ˆ«2Å=†8ž}oCÜlS\=ârï:âòLq+7—{Çç0ÅÝ‹¸[WŽ8«)®q…ˆs#®'%<®qÓ—‹¸=¦¸2Ä]ͽȈk3Å݆¸ï!Ά¸fSÜMˆËEÜ‘±è;LqnÄMà1âæ˜â¦ î\ÄqÿžË7qã·q™¦¸ËÇ39É)†ÍÌß.Áo§á·åÀﳇã_ˆ8;e%ç`m˜ã%ßX+B¸Ó�� »(Èììä.À@VI P¨t�öܨ 9€]€�ë<ÐLl¥¿4h�L+ fz�Ç�uóf@êÐø�å@)`'À¥rAAê� €ý�çíàPØþ_üvüÁ�åÚ˜r¤\àï˾ªá6ã·@#~¯Gyªñ»üÙষ øw�JáßH}à'|ìlD¸ávð› p!Ïcàg `6`9p4¸m·ßDŠ]Æ÷Ë»ÓýVîÍÒ®w pNgœYhJÖ´M“Oõçb~n-¾Ú/ê,ÝÛÅ}s騷LY—¢D]ïü'çž°3,m÷Z´}·ûÀy¾b?òå ò›üÁt1ù{;S; &ž¬i—Æ!þ „¹VqÕ§ú-r®`Éðvq¿°æ^­‰üšÁC¢äûg7ã7‹é„ª(4z;Ç”þüÆ2q,Ègº¢ðtŒÛÿO£ìÁtܯú™/Ç‚\”¹àã ¬ýYöÛòó[SðÞ\ž3ñû]yž„߉ò5Œ×([1èeCVÙvc=A“m‰|? xtsœLÑ´õ jYÓî±h³ï0òê?Ë‚¼6ròº—u$ö1ûkì«üŠý²Íeœ`žgŠsŠ÷uªv×¢ê´Uºt«Hã>ål¬·¬6Î]#žr›(å,éÝ t¶’Û;Š{§ñ‰rìWË“šYRÖÁ>cœQñÍŽûî¶T[´Š[ŽXîò;Œv¦î­§Œà3¾4¼<˳YOvoÖ¿#x¸¹+ÉîMìã‡ËzÐj ý Fßé2Ö–ÌupVÿFo<ðGÁ5\”Ù¶½ùnY3ÄÝK]äi*ô>Ë5B? »*#e„ž ¿Í2BwÂw.Œ¡ïœÿ°=]ï¤îuìeI6ÎiôÐ.C™î´h­qÔøâÆ¢>S8·YáéXé×&€ßŽéÐë—»‚{�l>è÷ÊÚWûÅžY‡tâlE×1ß=<õ!ߣ\Ÿ„ß~œFès5ígz«îüàû~ðþ Œcà3ç%ø;+×*Àq�ç˜']Oò®ð%q_8è¤N2p­ÀµBh({IºnE½ ƒÜyÆÈØc½^?:îqCG•ûŽù`cŽ…\xfT{´;ÎÙÒ݇ü­ÎÝG¹nZ³²û\‹{lÎë;mÞ‡ý¶šz¿ÕU'ÎGõ™²e»Û¶ÔíŸ%lT‡|¤9pfóÄYýŽ#ÎTÐ –ã˜,GZÖ-Êb}‰åx©»<';Ÿè&ßVçÊgòn­©åaX–ãïÀ_y~Þþ×T3üÈ‹òØþmØ’®©þô#qŽÞÓùkè°÷»FÅ;WˆýÉÔ3±7]›Åó ïˆ>iò¸2A_‘¨5÷°PÏmÊù<¦1ä:‹÷ÈøûÀG¶¤“mOm³¤v:ÒG_z8®¶cà?ɳ²›<¦—>yié¶:í>ʺ«YѽFœã ÉÔA¦vE¦ÚxC¦ dš ¹¼ ­è¤<)× <)Ë»Mùd ’èûW½’óHCÌy$i+äs«)ŸÜù 7åcC>¦|4äsÀDËÖuRo“Aƒ8Äm6Ñʤ¾hõ=}’oµÍÐtt¦ë칇cQ1Ç"ßjý‡‡ØïŸìÃk}¨kýÀ!£D÷6&óè˜iï«<GšV팮ÇÁߘÃÜ{Ís 5bÿ·sB:ú?èc³Î¼ž9dìÍfÚŸ!mÇ%!½ ~7F]/ûÎn}�¿/›gPœ bOÇÆ«tV׊~^&ýß2N;ú–‘ ÿe&ùDïÙÊXHwœ d¤*uÐbªƒœuÐq6üÙFf¹Ä=¨Kž·ëEÖÊ{x4ìj ¿• }£.жxPì²]lŒÐ.²E_³¢;R»øõñphÊw…I³#àÜ&uš8_ô±í3œ¤­ìæoŸ`ÜìcžZm÷1”ç(øJ’}ѯ÷®>ÃzÖÎ —í Êä>Ê#ÇÐÖ{°_ÛrP§¨[A½:¸uù!Èž°WèÌä¤eˆ³u¦‹q·cïMzB)äÞö½è)c–aÒsR–µÝA~äëæ¾÷zàMËØ÷ÐF, ÿ¾·´7üûÞâÞÐ÷½¿Á¿k¢QÖhûѾ‹68Þ¤ÇiãÖÏYJýœ žãihs,%Ÿ}â;ÝšnU7’ /bßÐqãÌKôˆgï’Þ®,¤_û’× škÏÈö52ÎgkoÀVãæC{ð¾ ]÷é@.b<]Âó3)ÇC:Ì4ËÃîèY/¾}dHÝ;7bwû爻yŠ!§€Óð4ð[ w²óJüñ?ÖX Û¢p+àzyÀ}ò¾€ ÀbÀ ÀB‰·�0P&+ï$(n“ôæ�n‘g©·ÙãBý“û¢ðþéLÙÖ¸',XWû_/ï1ž“¬PN #ä™»h?¡mJés¡raGeA›gûòaWl÷v6½tìä~ž¥mé½ZEÛç¸ÛÇ}”Άþ±—õ™‹öïièÎOaÛ\¶ó¨¾í¿ùì1ñiߤï Ä;jÂKÞ_#à0áY×ÏgÂÓ€÷œ¸ëÏËûæ:!ÍaSšd¤ùY¼>^*ðZ#à1áÙ·Žët£ó¸ÿs…O´uÔ7ëz dhÅ A93¾Wžo–çÆy¦Ü[aÌyƼ¡"Ðÿ½{9×Îi�G‡1Ü…x~O«0Î Í©0Îàñ^/~ŸßÛå]\ñ¾–nqîã8Ï%û¦úmß­¿},ü|žïv‹8ÃNZ.I+oˆ´þZÉú\ê”B³ùv£ìâ~6I37M›Bó× iÓÂé¹@oëW §òø#ÉcØwû…Q'_§Ì‘ž}ª?^[Ó£Òô‚&ëÚc3ö¤¶oÖ§¹[ÅžmÞMÆù'i$»6ë\Ó&}qFû#ºÍõˆØsà±ý¡çÎcÞ4Ì=Óxÿ}•wnãJz§!êÄYÿ_Uß _¹ñ^îבÇU’žùÌp£B“wäŽ{Ü£”{’T|»C~ëvÖúNªõ_tca¹¼£óh×LcM’siÊsx£|vcüåZGú>/Ú­8#î™êO:H»24.ÚLg’-?æÑy‡×9´á<Þ®ab>ÚÜmõ<è×ìŹöD9fÏó6„S¬×½ÚM\³ÍÓªsŸî0×*_òvÄ7ëš¼£³è agN¬Ó€aîU¾¬øu"þ7=tò(ίz.X'i¯›ÏJ¿»†÷Wd‹3½ë0v¯ê?ÓËsĽ=ÆÙÃc ,×(y¶7x®×¾ÈXT×Åš òr‚÷iÊúïø[Zê¡\+“ë„),¿iðY¤Q×ëæ[´üEòθ)çÜ‚<ÒÔüí×ûÅú‹GÜÇbá½#ç<¹n„ßãmòžØ äõeqÆÞ&Öoàš]ÊÊ–õ*m’4ÊsŠ‘)ËðèT?ïåL®)+×s|ˆçùÍa² \×^Ñc|J^$צŸpëäV}ëRãüù^ùþt?¿efNÜ 7ŸÒ*쵞svëQÓ$ï9èKX£d=^Âûþ䚘SùƲݎ8[ûúkö.qFVã=Zë½4îŽÓœ›D¾åB¿›Œo}5^Îk’xnî¤!TS8[É„¸Ë%ü7~' Ÿ;SŒo”S.ë2g>ÜCDù©ç2šÇü^qž’†k{Õ¾[Œï5,µ´ê¥¼I«óý Ç˜‹:âÔsQ&wI«žX }C¾»»åZê5ñ?FíÆo+ïŸêOö\ãÏB6¸j½“×É¿šê/Ýdïµ~ö¼›‚ñb­8”ÏÏÎEÇy—ÙÌÓãC9|]¼×ˆq»¡_´YvÁm6}ùgw¨ÜOpM“ö„ 瀂Ãõ’瞌¶—ŒvWÊ»J#´¿ àÍijo*¼ñ^ê9eòîôm²Öõy‘awÀ¥žÂ¥®š�ëb£Ýªm–ëÅ\ãf›ÿ/ãü/Û«\ëmv˜¼S“óž¢n£Í“mÁ#Ë©½VwíÕ¥½ö!>ÍÔ^×á7Þa>m®¥¿­r¿Ϩ;”½™vóYj¹73Õýìt|wøÜ-ðahî6§û«ŸNSöB.èüìtׇ¡õ ߇á{3ãÐßso¥Õþˆ¿ {hg§Õü¯é6ÎN·•DÞ‹ù!¿ß|.¿gù%Ÿ üN‚üšù5šägSäwôï.¿hÈÏSüÍÉožI~EŠüv~xbòûó‡ƒËïJE~.“ü¬Šü¶~ ù=-åç,úæä7Ê$¿“ùUœ üî‚ü>ÕCò;¢G—ßì¯!¿›¤üŽÜúÍÉï=\~/ê!ùeœ ü΂ü~¬ÈÏ«Go¿¶¯!¿aR~ms"Ë÷,fEØÛŸ&åçòãš:ïà°Ë;8Ò"ÈñN“)rü«nÈ1m9¦E¹ƒãº!Ç´rœ©Èñºz˜¡ñþ¥©þçtCŽiä˜åŽßê²¼å«É1 üP–ÛïvrFùï3‰&Ç3MrLWäX}‚r¬‚Br<v ºK¿†çI9fÝY޼sç+¶ç#&ù½~ \~;’_òß9ÄöÜþ[Líi"Ò/¤=?£Èo›Y~ÎwõTæí|YÐàz‚*»îm9yóÎGßìȲˆì²c´å y¯×©ƒtLíÌ–ºr´_†µB†÷›dx¯"Ãÿ…× :xª¤»åØbÒ?Ò× ¢ƒ·)2¼þãÚ-f»0Þ®Ô¦ú_>YõzøÒ¼ÇyÊM‘Ï\çB–¹Ê™ë )K‡éÌ5ó ž·æ×¼Ëà|”ƒw 3éå·M2=K‘éJøyw€#†LO#]”g+ʳÕa¬Ñ˳éë]Fúh2®È4~¶ã4ÈP]ËàxÃ;2 × )W‡I® W‡"W•;w²Í¾ñëéhp¼÷%:~2[ö“GMòüGW¸<ßè Éó‚ÔÑïAGÛ’ç³ðóüÙ€¾’òå7¼¯¡£iRGµ¾yY®4Éò~E–ïv˜,?ì\–sY "Ë?u}uY¾ÚeÈrû¬ÿïÚûù&™~[‘é]'ÖÞ×t ÞÞORdšÔÅ5@c­?Z{÷t}õö¾¤Ëhïå×µ³£±tô|©£fy¾í—ç?ü!y^$åKGO“:”§ª—KyÆÒÑçý!y>ç7t”ß ¢éiF==COÓ¥<í3¿ù6¿Ê$ÏŠ<ß÷ŸX›?è¼Í—)òœ ›%|ŒWeùÿWoó»ýF›oŸE–)!YZìAY{nÒì!9žäœÚo³S–!½\!ä8Ñ$Ç 96ø‡¨—C/Y¾É´ÛÅýAYšö©²<E‘åÉ,·É^Êpc½æxKÜõ~’cjؾ$a³ƒ‡L…‡ …‡eR–U×E¸óiÆù«®”w¢Šï-3Å:)÷EX½ïêâ­®¥ºW&ð›Æ´Zã-(¾ iwl°ÇƒßÓÂß 1¾%ˆýŒñOUöõ$ˆýŒqú×jÛ îÔW÷3rm@ÓüZ²7Å•Ì}Éað—¶Å`º£2]MXº‡ô·”}+ bïßÀüSL<fEÀ9MáÑ:Þȫޔ×S^ðÔ=„Z®W”e<Üø\oÊ•�ÍùÐè6ç_,?3¥ÍÀƒMáÁ&yh4ñ°ÁDÇŽÓTÞ‚(8n‡‘G/ÏÈÎv4 âFÅs4wG¥åqû/߀Øk7Þ±åÚz–h—MúûCõàžÊt½ á[•ºO–²P÷$Ækõ*8v‰³Ñ$¯©<Ûâ2ö/u|³Àk1ÑšÄï,ò{ÁÍÈ_ð†0ùzáï*yù;ùMè!=¿eN}ÿt,6ú6ÏKF¿ÆoÞì×ÄÚCûq·*ïlâ ¼Ã{ßÙ°¯{{öŽ“ï>´7ŠûÍÂÞ9k÷Šsì#¸•!ÎÊ­Ößì Ÿß‹07æûP=¦±çC_xŸé÷…úÌïævÎÑÛ£ï%çèüÕ,öÈ92x»é«\Fzõ¾µ½ÊÊ?ùBýå+ðÿ÷o9Œ{Z9¦ëøÍa­ïe¦E ýÐæ÷Ô–)‘ÇšRYiÁ7æ"Ô‡Õõ®x¿)o¬Ñ?rÞáqkÃÞÓI“:‘¼³ÂµZ¿ókÖÁ*S¬Pêà}߉ÕÁAßàuP¦ÔÁÜuP÷5ëÀVÙÆÏïÿPöÁý nŒ¨‡L¾c†1å´\Íñcå‘I|—ÚÛ™é0Æ]­}­>ÛÞ¬gŽ  …kRirù<ŽåH›‰2,èb½AÜsÈýÍA>'H»¾yføšËXS}|K©{á/æZwŒúH—ó±·DŽá¹<ã&ÇpÊ“kk“Üx×›˜‡L0p)kúa/@Õ;áY7êü!XÖ @;[îñãî·"Üæ4ò|Jyö`£k ï+zñÁ!½Øÿÿ²“ùhÎ5"ý×ÃF+N×û\MúKÀ1î»6Î"ü¦‚¿ÝÜ޼Bo·tkZCÿ~lÌÁ+ËEÜš«êûÕåqªx;À¸ÿÄRºÓÛù×ÖÔý,®´eŒ=|'àJÊÙý �ÆÕÙ×E¼ûŸqân·ñÎBÄ»ÿnÿÉR·¢ÝÛ¼ß;U½ÓÃô}º‘ß⹩øßqë… ø*dìð¥ë=óîî3g¥´Õb¿qØXò¾'çŒOƒ|„3÷x;÷Ê»û³´w÷O|`|Cç6©ù¡1®b±|çÁn¼ópÇáû�ƒ÷ÜpíØæÛŒ~â]û½’ágšx‡qF€{¾lþü`à;쳫/jÕ«'‡æZŽÁæZò.ÓïònEaW\϶•”)û†™Ü+Y,ùÀ8ÃVÿŸèw¹ý÷…öúP_IÔ´w2|Æý¥ñ)Oˆ»Kã5wç³ö:qw¿Cžÿa{@_ÐÉ{ Âöww¹“¯éÈ£m÷Œ…·Å³ÀçÆI±çÇÞµÅ/Þ¸.Cš³ûû±Ñýëýð~ìÐû¡~¬�¼dN0ò õc+Âú±g8AÙ­èǬcƒsºG1¶<ê›õ±w†k——Q')gå°V“rhWåàvû_=Ž—͸ã‰öèÓÒ~Uîz"_uì·|Æžwêyöø&½võœ:ïQî>`þvêò÷¨G½;Ÿs-³Üù.ÁŸx®¾=]œ Ìcgƒã=yƒrñ½ÇÂÉû"î—Ý±ØØ§•3ÓØ£E=³‰wI÷¶ø®,÷^¥¥v÷:k×ÝgÿT_ܤ³Ž†É>“ûK‚ï¯ïô¿…½1PãæYÄQ<÷íó©¨ox7EwûÅ{»žÐ{2LG¾¼'SógñîU0¼‡únñÞë+(÷?ø=ô{áïÆp»u´1‡-û1óÙÑ_ä¢M—sï;äšaß ½ÙŒ±„÷ýÞÆ³“V÷fØ#ôRàÌA[+¨Ù¬»0ç=[ܯѨ´‡—;—zM»Šwûðìð£â mü Ø!õôeB§¤í_‹ß* +nügoðסM Ï߽Ͻ˜úz%®<ªû¼™®�ºïtÔùó‘~ø,F>É%èWîļÆg”Åå‘0\ì³{\ò’ Z¤k¦çâ™GÆÁŽz[¦!Ó0­ºo½Æ»KŒ“œ·<~K5Ñ'tÖß‹¶“—Óª—ã·xçÚnñ¦ ÏV ðvU9ëtæ‡>Ñ|¿˜yÜó¾! ¤Ku1»Ë\¦©†kþ0ÉDZ _êøS!ì¨9]ËAãž!,öv¹jÒõ|OºžèåžE­×â}ØŸ¬ÝÜÉ»=x~ÑæyО¦ý¼œíñQOtÖúlŽZø7è¥âÜ× ]‰ŽÚî#hß|/Ô±®›áDçºîÑh÷½è·«åù°jèÊsäÑ÷¸x;´¾ë |æùØ—<®çC¾äY=ß™§ÌùÛ)ÜKF;|&jµÝ,y%ÏA~ß ^È÷¥‹:¦L¨#9n´õl~—Ê%úå™—õïbžQpY^âQL4“¼9 Áß‹uz6õåSë¢m¾ õP�¼ÙÂnõvMÓŒùåkïAO¸vÅ}§nãìi1¿¾÷Œ}‘#䨉øølŒi…HÛˆ87ÜGß3Þ.ëBÊšR)ò,F~¥2?ãÃÕú³ÌòÚ–ÌïÑþüöÉüºøvl=ž×%5cû¶igO³c ém‘i®ä˜ ºsä]ÃEŸô¨ôŸxí¢YŸ÷ßkÖK$ÏEïÉ7éÜÞ.¾Aɶl;Þ µ¥ß¾ÇwÉêt¶§ÃolîÍ¢=Û÷·W‰v±Y´''Ïo9~h‡‹{GQ~Ø5ìwÊcw°"ŸóŽS–#tö;›—½x³îôÕðð.ö=Ó ³ìŠ‹Gè‰èÓòáöôZÅÌt¡×6¤«jÍhÓ.Žovã\|s?±8_æ0ò,·z—ã÷tÍQõòà=*¥WX´Ã‹3'}p“— ÿËñ€c‰±4x¢±®»YÏÐŒóéV±FžÜïêJûª{qoèâ¸ÄþŸsr®Ï@Ý¥JyÅO�.ÊÄ·hútÚCÜcØwçÉõðœI<o_6÷¦]ᘾnü“ýávÒ“ûCvÒqø²n|Š\7Þ£¬Ùron†œïQæ[#¬ó7ÚqÞ¾n`·6÷'üqN#­ºNì‡îF Ù«Ìß–ìÙŒðóoòÉw … QŽ,ØTäsÏ þ’õ™ù¾yõýCM¬Õ×úăòÞùû½—X4×’¡á·ß üÒ%á÷Ù[ïößeÿô~ã.{ Ò‰;5Ñ<…²ä ì9ò>ãLoç‘qkÄþdÞ!Qn_£«ö¡*ßüKBg@hglá<úU]²9רk¶lW…ï»6é?j5îkwç.s\Æ} 5ï µj3;ëÑörñ{Ï CŽyð7¢½ÝJÍÑ"äÍ{F:W†Þs®ö³­ñÞõóP¦:©ÛVÌÛ#áòïZw—cŒ³E'žxŽ—»xßU np¯@Þ¾Ë[õzôw6Œ«|S¶Ñ®7xÓõf~€}šˆys…sn…bÐÔl?¥ü6vm÷sŽ7;G®ý~³8¯ô§Ê3|óµÉ¢õ®,Ì´o·^w$àŸ|ï5Nëý7ü¿¬·„Þ|=`1Þ|}îßánüPÃ7fÅyXÞŸ^`ë7}`ôÛgÅiI\×HôÔv;¥K\§ÕBët‹«N¼+aŘ‹~úá:èºóðœ¯%qìO›yV×pwMƒl´ð»jëâ´1”ms¿óÈýûCçÍë€_ºÅöFÑg¦².ø›³Càóls=~/Ÿfô=Á3½ Ë{MëźNM;æY z´á¼ìV=//tîïÕåqy4­!J=¨µN>†<_…Ìw~ xp¿ôz�Ý€˜°Šº@GÛÛ\Lz€{?�rè­Üàúw¢v¡c­˜K/Gßc–÷"Èû)ô…¬Ÿ/£/bÙÊÏkÕ—‹³×+ÄyfÆ£o¯ð2£lì‡rF›ù-ÇDèekÓÖÂnkêæýno¾8Ä»ÜÎ÷â­MàÙ¾Ÿ<ÌÉ3áÞÎýèþ:Ýæx\è…<ó­Cð¯]ß y\Z­­ï‚$¶7v³LIÒÞõ%ÆzS™rQ¦E\qãcP/‹× ÝLežÚtï\¤+k*áº\üXü-ÔÍ‚@`“8Ó šÄ#Íx­¶‡ù0S@ÿI¿m;ÑÞÔ3þ ]œÍA¿Ç<Õµêà{•‚—â¦î¬‘ù>v<ð0ïoë‡É»}m7×¹HWðc¯EY6élƒÖQkt¦ão9>cÍq›¶ÇšnÑVÁyÚ~P=Ÿ»CØü ú6à'j+ºƒåÚüS¾Bþ65¹æÉ;#åß!ÿ¶væßПÿNäÿññ¡ç¯©ù#ïíö‚‘7xPó÷EÈ;õP[ÙŸ òÜ?t„ùRGD^˜ÇËöô;C¬Ûl…×(ÿ/ñûâ`.¤½ ´_íþ>úúØ;Æ·¯HºßüNH‡Hè?uÜÐI¦i¦líFûÊß"ô’º÷ì"ëÞ­H¿öxô¶f‹ÒÖf ]ÝW¨³´:ÛEgF¨³“Î^ˆüK¿Bþ©tÖEgû"äï5éìÉÈ?_ÊÍ&îÄDŸ ¹‰o–ì[(÷`?çY­ï|g`?wü¿‡'Æý‡oG–ý¤;ë+”ÝAö[¢ÈþH„²o1ɾù'|…ü“#È~kÙŽÿV“ìÛ¿þ_CçÙE?Ì5âSîí ¾DÇJ×'‚ãÌßä<ôõÿÆÐ(ÌlÕ s8î×øhÇ}Žïß9Ž^+mH޽×p= Ê°#h£óÔøxßUc1ì:Oº¸ ‹vœõ%ž{¾Òo—¶ÁxØV“�ç�€d�Ï_bÂÞ;\Þ71LœÇÔz¹ær€whm¶Q€ Ò>ã\×:¶©[|w6çþ|‡'™cÂ\pÂÎ̆Œsø»}M7ã’Û7u÷q®ï×§MÛTðë²ÿyH{”oØ‹·v@gm·oÜÆ°oa*blÌ•óÀ\{úè-}tž#}ôÓo‡ß%Årô›ôÐïwiwÈyÓØ·Có¦3àÿ1ê˜vLÛõáóÆÓÞŸ7Žz;4oôÀ_z‘aßD›7Ö@ævÚB ê/Ùõ„¾Üý„X'­ò<¡¯ ž³¤Ã¹7Ê¢?…ßHÓvJ«Þà3æšû0ïáú8ß&`¬ÐV÷˜¿=çÜ ;~VW9d)Ö•Á·¥ÝÛµ´äÏK ]Ìõ íYù;ÚÿÑþëôï#޶.ÓUÁþMv4†ðþÀÛ(Ö­Vè×E ÃùMæ6Çöô®"=ÚÚv§ÊA{ó<…N^ :´É-’·‹"àq®yšŒ?7Bü:Éï»ߨ0oªAÛ;z,лé(ßÁîóþ_£ ÉNc®öŽ´¤w�ôÔß…mëX-Æ[Ñgs¼EÚþwhxÓ¥Î$;ŸÐ;yøbc>œ«ïöà= iòŽà\—o–e¹y—s£X×U|cPï1hŸÑª[<~c˜ÈóÝœcÛúߤ—÷8Þpe‚6& ó˺3£ß=]ñ˜qîßæ­óåÆ?¬&ľkž8S“ƒ&ñò€ç„qÎΑ3bÓro ´ˆ“œ­ƒÐªÐÖ3¡EœtàB+ yŽ„q€“ƒeOÜmÈW„qþmÕ’ö|+6omÀ;bM‹8ÝÀ©„Övàù¡EœÿNÞ ´v�¯cZÄù3pú2¿³}'p_´F¿³ñ¿Cü¶´‚ò'½vàÿ*=ÆÿñÅCàmpŸŒA‹ñ#>m´ìÀ}$-®g%uóû=é­:m¾#Êõ7'ú§8gï¡äû[tÞiš[´Ô;݇ßB;i71N½·‚|¤"…1ø`| âÝ1ÞJ ÒJ“:«LÄy 8ä?×nÜCÏ;ïI—üçqo’“kn^Á;éä?Õ¢G*S°<cLuž<~ƒÆÿñ5c/“¸­ƒ”‰8뉃²äAÎýõ±ß£N\Qê$SÖ{´</âw á},àV Â?qæIþÉ3ùg9¾®NeƒÞŒy2þÄÛbð¯Ö§ø¹ƒ”8FÑ+êËAbÙ¨_¬— ~}•ºÉA>£cðÂø“ÈÇÞ&Én >v¹ˆóyüÿ]Ýäž?FžŒñUCxÄÜ=ƒðOœWâCõ¬'×óe_v¢í=yü<Œ ñ[†ðfHp›)q‘u’«´wÎϾNL½»cäÉø%ˆï‰ñæH–›ýõ üg¶äß)u*Wê”3ÿÎ(ü‚^~Œ<Ÿ‡øl½UϾ¸U|ûgýl kÅeߤw |sE³<¬ß‰ô}9¡·ÀˆŸ?øs¿Û„ŸüYÀo1ágEÁOµåõ˜ðQðíÀ¿øù&ü´(øÉÀ?øi&ü‚(øVàŸüÃ[E¼*S7Ò˜ñmÀ·¿}T«Þ>¾U|“æz‘úVOÞ¨M˜k |¯(öïçqZRß.™8ð½œÜQóËA𑦸ÅM­¢<üÖFÞJ[¥|Nðûß¡‹P¾Šås"¿]À?vQ«~lœ‘¿¨å‹”nOÜÃúo.ýÔn¤µ˜Ê)M6òú1Ò´ Ÿ–‹ÊÃAùHÓ„49cœ¢W:.t‡T¤4HS‹4.äáâ;'(O|cøûJiH7Ü9P~[ÑþÆ#m²¿U¬Ódÿ‹:çL»kxàÇœv7Ò¦ íÞ±­‚vŽ#<M\„7 ÷ %ÎX¯kE¿-‚ü:€û©2÷qï²1>P/X6§Â_›Ô‘Ê·4>à;bvÞ'Ü*úGŽ-j=lGúÜåÛ‡´·}ao¸ì뼥ǹ pÔ´^ÞÁå;Û¢µ, ¸6° î@†'È”�Û�Éw9€R@3`'`; p¶4pŠõ€­€ÝKCo¬ð]•,¸Ó�õ€FÀvÀ>€½*8×w P ðö:�¶eÀdf�5€ÂŠ@`Ç]àþl¸s�€]w旅k‡ë�8ùwt­ÔßmÜKh­*(‹%Æ>r~oƒ_CÙ N@5À Ø8 p ÜÓ�[�ËÒøßÀ6ÀF@5 È¤4€üîlx€¤ß è�p¿L5ÜrÀl@  Hì‡lw�6ª�n@6À8�^ÊA/p ²;�Øh�”2>È{'`# Šoà�œ€dÀÐ(œpfXß½#Šþù Sç(ú·#†þ�îéÀÝf¢½3 íà')´wÆ }¸Ç¸~Q8íö(´�ÿŸÀÒ æÑ¡÷±}whçŠïÑÊÛƒðrMøìØ/ˆþ@ös#%_‘ÒcûDºöµgêíÈk¸É¾Ž·æ7莢ßÿÒ1•Ýÿð ׌‰gF´/ÌøÇLÐ/0ágEÁ·¿øU&üÜ(ø6àï¾×„Ÿ ü¿� ¿ ¾ø/�ÿ(ð#ÙfüTàÿ‚çRÏŒn¯zÝþâ줷‹ïò¬}ÖÄzýâ·‡øÝœü3¾…9gꥠ<ÇÈï ¿|ËØ/Èo'Uˆ¯±ñ¼_2x®â‡·kð†¹Œ=Óê·‰m™gŠq« iÛL´×KÚ\—=Œ žÅTÏ‘ðNÖ’Æ?á?,ýü½Oúy>ë˜ôó]ëY†ŸëÉÉÒÏûR¥ŸßG2¤ÏI?÷pgK?ߡˑ~~ÇÉ“þ=|Iú¹ršôsï}¡ô¿Âû#¥Ÿ÷v—J?ß¡«þxîYúÇwÀ¤;¿#Hÿ¯áoþ_Áß,ý¿à™@éç;t[¤ÿ'ðo“~î_Ù.ý¼·{§ô·pþ]Ÿ9À~⽪aï6ºýèž“>S|êÖgo%É}þÔÑ`ú°ó:v·.Òfž}f¿­õá§bïQ'ÏÀæžm䡬×%ýz ¤Ýz¯[ú—ðîéÇÚ[,ý|‡®\úçó-éŸÇ;R¥_|’~æ[/ý7Ãß(ý7Á¿QúyöÖ+ý3áß*ýüÔ&ý<{»CúùnA»ôóÐnéŸ ‡ôóº}ÒÏo@>éçÙÛé¿„÷ÑIÿDøJ?÷ÕjçþóyQúù È.ýçp/¤ôgòlšôå¾é?ƒô§Ÿp¹<ßañz;y¾ÃÃ{d݉s b¯Úp\©ÿð¸ýßeŸ*Î~4‹³VsSìö;y‘{–'Ð6_ÝMåì…ùýËLî¿WÎrä¿N/ƒ{þœ­ºÏÁy…q×·8_"i†In·ß.öç‡ò£Î›æí£œFÓÜÙ¢yã“ä9â¸"œqË£çíRÏ¥h5«uñ^—Ã8cä³Oá“çLöœdÑê¡ø;�{�=€ì{`—Þc¼Qæq÷Ü÷ƒòͲHo£»‹ïO\HÔêºGÈ{b9˜×~Gq ú–˜]3î þ‚ý]²acg&híIîUo}¹ ^ìÍÝÉò¬k‚fœE »ïãK‚³®[ÝŸºu„E#=–Ëâ&oâ­»:ûñ­NMoÓ`s¼Éýêëôß¾8T¥w€§ˆ3ÁëÄwfq¦GŒoMiì?Ÿ¯Ò²+û;ú‡‰ëMÜ'Þà¾qƒöæ7é ð?þ†ñÆÝ¸ª|‚ß—£­°]$s?o{£ÎóP<oÁ=û ãÞê e?¹÷~‹t‹3~àÝfáø»N¯¶ë:êù*ûl½]ûÞãa§±îíêzƒwý×éâ|ï©Cœú}†¢Ur¼åN¤‘{úkœuú‹”«Ã8çïXé³ €ÿç…lêô*ØÏÁß ÓüFÈÊð//®ÓŸAx÷ÅÔÏt}9ʵ\ìÝlÖ“kº«àç™ ñMZ›ÓÕ¨í¸Q«ó<‹PïßÞZ§iïÔnÕkŠGèu˜;&×x¹OWÜͲN|ƒïzÄÝÔW¡¾Í¢|‰Î—»é÷ˆs0ov·¢±Yìÿý%Ò$iu7‹½–µÝ<“älêN{Óg⾓QÜ·BÆ=ç|«3¿á7Ò´ðLŽ£Iì>I»¾ËÓžž°Üžž`q´ C†Q6bÏæî,AX—ÜӔػivãžgþ^ímÖ3Þ0öÊ$ZîN|ÒÛùŠØ»°N¾í[…r¨uË=≖‡ºƒñ5¨£v„äò<ì%×C3[õrñÐ0Íx«{R«‘ŸU¦ÝtÄÍH Þy]+ÞuMžÚ½]õ<#ƽئûô‰»¼Cœ—íï‘<,c½@fî{‰^醜º)Gñž€£¶›ñ¢ÞouWxÐwŠ:z°Û,Ø'c¸ƒ:åç Öts_F Ïx8Œº´ó¾)Ô+ë€õÄ:`]ü(ø+÷\XœwòÎë‰ ㌬ÅYÏþb¿õkÆ[¿ï)¨ƒQš{Ïh}}!—Ë~“8ÍA£oz†ûrš¸·›ñ)oç IBßž\Sçg[¢ ̽»6ÍØõhl?ÅxÌC~í5Ânz¯ÃÐRð´\ŒA³Ð·ÍÝãå¢Ü›ŒþÛ±VovÔA¶Íz£Ì#O«ó“þ²> O ½=Fú¯JúÓH´øf.Ç îU¶iÍ¢¯ž!jî Úʲlq<ôˆ¾“v÷| êUƒ/øîõʼrÚÒàý è0Ûìã¤ÉÅWûÓøõÇ5ò  ïf¦Ë7€®–¿Í\ÈÜ�(”x|{h&À ˜¸p.�eé àý)�´ßÞk�§ªQ¦óXæboWüÕèŸÎA¸{jàODß_Þørì’œþ;ók}”Ù !ý¿"`ÜGÏ6søuÊ¿£_æ;Á¬Öo³¬“Ç•ßúäoÍÊo;äo)¿uÈßV~Û%[¥ü¶_þV£üÖ#»Oùm›üí.å7¯ü 6{/ô•ó=è«¡?Ã<+}_‡ÝÍp;à ¾Ïö$ïðm–ú{Œ}6÷Âxãíüíãnx~;vOèQÞwáäù'އÿ8¦ó¬ßTµÖlÖK»‡ë¼ÛN“vY¤»:~jŒ™IÞÇ|Ù/]ÿ@[.캈6™sƒîlŸêç9{ž—£îMj\íç“zç*´‡„žg7Y{ÈoeØùþê´Ú±Nô õ’û‡U´A1æÇØÎwyNÄâ˜ì¼8è2ßÀ⹎]€çÛb¼UbzëOR¿ÿ.ÿà¯�Ê¢ç¬VãN”?α©›íÒjß ·)6²à±˜÷!粸WsìÆn«ý­nž¡½õÆë|Ë@øÅù:‹ ¹FØ¡;L´‚öÐaØCÜ×Yïà>,Ø4b}„ëJ-z½©ÎhëÊl/s݈8Áø<¥.ÍiU»™|’æÉ÷–HËϽ àñ9Ç[¬#žÍÍÑŒ{?x©¯‡¿eŸìÜ(Þßá:»vˆ{ËXÆxmeO²£>lo/ìâMÕŽõúƒ\poçŽ4û:½Y´óUº*â ù$BŸ—{c»Ž‰3xë`ãlЃg0nuŽtÖ®„ÖBkr\éël®Õ+ó´UþfÐf¹†ÿ=Ö"#³ ëÅX°Nœ}%÷·Þyð @àwQúÇéJÿx­ìwÈþñ÷€Ý€Ë�/(«*¾Mæ^íO¶oÔ“Ýõ:÷½Rܧè-$‹g¤œˆÏxâQ^Ô·} ÞOLòd»þH‰ÿ‘)¾B‰[%­ Îj®‘ ÞlÜ+ \k ÆFô×A¾ƒï*_¯Ô#ûMÖeͺ{u¯Ù›EϾÆûþXµ=Á¾`!Ƈ`_À·9·ˆß  ùKMÕòüu„o‚ÿöJ²óqY®ÿ×Þ›ÇIUÝyÃçVUÓEÓÈímZ&ÞŒ­¦;† É‹†ÍØ, *™î¢hmºË^ÏÇê…5MR(ˆEâdœŒ3Ó&fWS.£>˜0™<ÆdªnyÓ(É IL& Òï÷{Ωª[E³dž÷ýïí‡[¿³ŸßùßrÖ}ÎÖ×¹ýa‡o³”ÏyÔ)÷Ð^=4Âw…$½ˆÝ Êï7ÏMãËPç èG}‡q%.ô~¶mÚÆs­ÿ·ãlôÏÝ gœAÞ猳ù¨#ç£x‡W½¡ÚÁú§çú®E›Eœ2ÖI¶¿süuµ÷ôï]~¤…{´ÿ—\þ³à—þ}ÿ¼+üJúéþªÁoÙ—RfìsXÏõðûœ+þo^SyìN÷;Ëf]OÆIãñÉו¾“N÷î~—uîeºN[Òûhƒ{ìñœÿ <êÅØø¸iä=xºÞ¤3½Ï¾™i½ÊæZÍbÈÑæû²2´NÞû³Ï©Ž,·—ðNÈNSs FTͧRžÏ{ŸëåУ·a©óʳwòú2ÈaoxмŠë¾†(MÅ–g3¹ž¸a<ÿìwÍ»äì}¥=º�¿©œiªsû°#'{ù®z`ßdØØ“·qKh_žëÞ!ÒÚ„øõ¨o×KâêÞÎ=iÞÈ~9ùþèÉO�_êܨ»Á³™øÍvè³^cÞ9$ïuuÚ?¹<ž­SÎÜ3úì‰1îâ|õ¢NœOfŸ+Ýv¹Íuë ÏîO×#œû’æ×_îð®-Q‹mð]vîÅç¹Ä{‚c'—SÇÀåòÞ¢Gå=˜¬‹an—óß?FœgæšžBƃ/i¤4rð>u‘/ý^½Øã|ùµÜ;‰8§Â{~8ŸžU ÙŸrÏ]øyÆòVõž|ýàä§ê_,ùêR ñ±ÚžÂ¹Ž{%o+ßU<úA%ã<E!ÇÅBÛ,°½þ$ï A§Àdøqu6%0~Hã«ßf?»Žg�=XÕS“6Çðó”8ž¤œ3DõÈ»Z8ÖÐßøÆ®å<û©å³Þ1â l9G)ÖË»�¨KzDlÀ^.çÛ–ëù6sPÒG9¾>cðbÞ×Á½ò Ù+ѶoÔÿv<è´$q€v²ÍÔOi3ðÜÈSÖ1U'´Ñ#&'úJ=õÛJ¼Ðà¶R¶×¯µ½õÛ¼c¼¼3äàHú\èW‡5ÞÆÉ·ÆTßU2ë%M$_=yŒ´†þy_Òóçtš?JYÚ1ö¿CZ`Ÿ >êkTsnãê<£#ç�©È{)rûY©âÀ6ÇsïŒÚF[ U=¤¿}¯ñ3®×¬Tw"?†ýuú�òÙ÷*í¾mG_Uuò‰¼·¥ÒíqA®Õ”%9Eñ‘/Y‹Ð¥É·@KO¾¿áºJ@ÙX'j#oOBÙå¨ûÛµÛœm°?Q >¸œ[¼qï·Ô™zÚdrn1(ß³KzföÛ¼ÿÈ+Ö€–Ä<Ÿù ¤3Â~À~}Vçw<¿+Ô]³_˽{ X<(ç6}¢>)éq>øšj#duÑo)çþ[Õ‰x/×åm™Â·zµ}!ï-Òû²– o& '蚣'ö$&€—<†ï}UÍ#L¬W÷Ô¯ä+¯Ž^.ß‘sõ]ô÷B4~ZÝW¼@½ñçïNÞ¼ÉV÷….Á»JM¾}ÛÈ» ê“á¼…G½…ûú«|ñÖ”ýjnØæ”o؆Ö$ä;b/¢~ã„zïëG¨O¹g›LÃ~Dœ”û~6ïíË탠¹ƒ°§Öß`soÅñ¾ ÀuÁå¶OÏm{çÅR4­¤ïIâ] ÿýåW¨ý wWþFûñ^Ô©’òqNëÕ𣣠Wo¢/Ë=åÛîlïÀ ü^õOFm;ðþt–/žNç-²oe‘× m9|²vý uçoÒxý)×4Öƒ^cêÓëœ üv-ì2Q–zóGÀC0B=ô ûuÉ?B÷@¶þ^ʫҔ—wÄóyg¬yxZI •Þ@(Á4¿DšÅâ!Œ~Œ§ÕIÞp´$–¼ãò·àkÅà[aÈ'„õ#Ì/XŸuÉb3rÀ/ú¼…ô\'!çªS#ûœµÛô¡‘"¤ù{îWQ§ôñâšBê´IÓÜQPnî˜ÌñϵÙÖñ^B¼ŠpŸc EK<‡l|ˆ2#—yô9fÄ)×áéô:N9ê…ð_G\¯XŸüÚᇾ»8L>kšö6â4´¤ñaÐâú$ÚZ´v.x{ò-CyE<ÓxxLâá°SÑxX†W³M‘‡ø0ÚE\ì8à÷xDââV…‹(qqØYûÀ.àâaàb}òSLæ»Øk“Ÿe;Â!ýzàb;p±=‹‹0û•uY—$.‹ÆÃRfJ\„–yü/æÑH\lwÊã‡åܶÅÀEâ”Çgºo?xBÊgðΔ‘Œ¿Åb¯ì¹V_컢/ê6߀6Oó ƒ еWôyŒHÌ˼ =ç&æ1í–+úlO_,uø,ퟩb�õpL8Ö¡N¶¯"ɹí+ÐO±x¬dJ¼½”²¹ôUÎ݉wŸ˜Ä1 yÚ;"îîK°;8§Wke>Ô}žšü-å?÷Æú­½’V@×»É{æIÜCvú1Ð^¡æ÷‹ÑÖ*Ú¥gÔ˜œE~ :½oRÄnÒãt&õ —~F(Ó=™¸Jã„8û6lpêðSŸ#ÏLP—5 bj²¯„úÓýÐߌyŸsŸÃ:±¬OAcx¿ÃI”{ߤAûÃ?Pï†w&ëÂÛa'°¿?€úÃÑ~K·™xxgtôš—_LMøÑï¤ß‡@³k";ì¾’ Òèûõ®[“¿Ç·¸qñ¶;œoôc,Ëq³6y”i ;°Þà2M}2Å4bè;âÔP?0ŸK‘?˜á~ð™\ë¬Óø|«ö¡ox£xëîí/1îýAûª­° Â…¶YgÐþà&ôcã8Û¼ð¿.°g¾ y/¶K¦Æ’—„7Ú…­‰qñöÄш…¢ÏžCÙ „Ý'" Òh/´+"Y%}r]Òdm!ðñ]ô‰¨íqãì1Ú7à5K;Ý‹þÔ|kôžg1üIŸ¤û°óç‚-†£nûÿÐ’0ùÈÚÔ>ôÏ|„Wƒÿî6Ä!?x­§}»íç]hÀÁÕb‘]7Aœ¤ÞxPr?%×ü¼À¥Ô ,Ä—º‚ýŠ?)Ý�a´‘9_Óm€vy×7ƽçþ¶ÇTi€ƒyì—» ¤ ?,ëÄû09Gæ±ÚÝõxã}Õ!ô›< >0Ežg~HÊÆõYÛ% o•ìÎJmÃlOøEcé[%NaX”Î7•_(yD#ÜÆÓñ¾ÔÐ-ð—˜üúâýN5ô ŸqLä·‚4xþ.õNí*òΨšKõ<1` §Ê{}êNŸ¹õž‰~«öù¶FQ˜ë‘„7²)ÁyØ·úv9þúÝònªéÈ‹o; Ža<|¢0hÂîm.- úJyŸŒv„¿þaàøR:^¥·ñû©jk‰ÍsÞã@7±’sŠó[�Ïk’s» ÃÕȹ†ý°[÷K=î�ïu³ö;A“ñV&¯=I>Âûî€ËiŸŠðe\—¬Ú•¨£Î‡t òbä] º©DÓ:,Ë1Cå¡Éé{£‡´U‚8äÖ~©#J^®Ë,’éœ ”[>F¸áå:œ´·F÷9m¸ÎÏŠy+©•Ãþ¦S¸ÂŠ  »© íª Úë†]j�—×íiÕœ—õئðGƒ6ïÐ÷¼¶éŘ4C°;6$<¦D¬krûºiˆ‡øÒôø²Ò˜¼'}ª5ž?ž?èÔØÎ’$ïXæ{à _Ô ‡Þ@Ÿäó ø ì-ôƒG:þà S ×Ͻ7¿‹1^ -§ær¼jY5,ïX;ìxÑ/x±ÿÌhå\д”ëyüšñCü+€Ã/š±Oí®ì1ï3TºúÓ¥%JöMp­y߈ç¹y–äjà°Oï!¢.%õ'ðQÊö…Ÿ~è/œÝÍ~á}"Rî =þ y<Ú{m»‰ú¢¾¶`éýìm×–›¥ì_Þ¹íöûÊ15lFxÉïYvâôëß$#,ÏêsÖQ&˜¤½•ÉO³. F*Y|’o—ón-àª<2Tº‘e‡÷Ê1Zé“e“»ýdÙ¾‚{"}6ãüŽúnúlÓKK^MÌœAþʦ-o(]&×,÷J}¬¼Qå¯Îfýdþæ~ è.¶HçOÝ/K]VGþÛ(ò/ˆÇÇ9~q{r!ÛÞé|ù}¾ °.ùq‰÷íNÃûÔ«W§ê0.f ŽSÒ¡”Ñs^H’ÖæHÝ4S²cäïÞGà¥.þH¦=uæ.ç›ï§ãl¹CÆÙçÔ5Fu¾8ç.Ä© ^v¹Ü'ÏùKïÍå=ÂhjV ~XÀû2hÿS|¿MM&h�2¸òúiÜ™ÀÝ0üf½O?Õ_&ú ²§’åU‡'8)YÿuÉ)gF½òòèIêœåøýÒ˼8â= üž‡õÃbÈ3ïzðOÞS¶ ,yÛŠ…ÊæX²áQ§\Û㢰ÅÉgŽòþÆiã3W=êð]öÃÔþCò®B¿¹#Ñø¯i{}KÈ·µsî}à=ñý#ÓÏs½[Ú]°ÏRÌ‹sk¾ÈçÀËz=;ʽƒ‰;äüÁ:ùvHÅ´CÎSµ:ýàÜ(yˆI™»Wâ‡ïvpMÛß—¹ç¢bÎ!®«²¼RŒ]oŜݲr®N¨·©½œ÷Öå÷¦ËŤ~Zˆ$ÆG¾¨WêhSç@6„w$¦v :ÜCÉû Ù§”uÓO9láŠÒygmRâ¨ÏìKµ;a×Ã^zî`¶n¥‡ä]v} Y¦+ƒ~èõ;!ƒAcÀ9ðYV(íßzi&ÆÚZQºÛ¹Dâfß­©9Äý ò}ÃXÊ›WÎL¹&ŸMËý«ò^5¤—8Aú½ðÛ¥hŠ}XÉ9¿×H7¶éóŠ Û'åŒÿñÀׂ‰CÒÀŸá7NìaøÏð›zeó !↢³gÔ<²Ðtv¸[¯é¬€{tIgÝ—ÜK‡¾æÞî©ÓÔÜ ç² ¬ý#†è’w·q® ~Tï÷È£5/újÚ'¹FKvÊzÅ’7è¸ôûøQNÛÑ“9÷�þA#„ÿƒk+\ãH$R\o¤ÍQˆþ%ýóuèåÝ—4­ Oèé•?zYÝ¿6‘ô&V—ýøÉ÷ ÞO\&ÚÍå6ÇîL8SŽÙzÞ³ûÓGv. Ä ð/ï<!uN˜jÏçEÞIíé`=§#üïŽ-·9¸okft¹Í3yE–ÚãÁ1xÏËj\Z Sèß©ŽpÏQ,ùÓ—²þœÛ¤NòcíÇý̃þäÉûdÛêí´‰i{xWÇK¬,ù¾=Ü»…6Rÿö…ûå]–érþSÇûöKê.{ê ï!ü´“¥­Ÿ½ÿƒørîdÿ§­4ßœžY™™!l@V4äÎ…Ü’7²4üôêB×)ÂwBãöþÂÆï`\~¹$Öˆ„NÛ‡1×7oyIl¯ÙÛçEÚ›å<‰×6„I¬ ì´#Ð8§çî“÷ÈQGÿ®œÇ@>èÇb·´-!s ×¤|1†¹ õÛëø£»lò<l8Ú™o×Feao–~÷%Ήݖ¢þIãY1hsþÙ+nÓöÂ~m/Ü :p¿Ügð;=ë @~ôyÏЖÔßÀF'æR–4&R´÷6мh~@òÃd9ì–%ÑCHÛšðZÛS†9׸+±¤q·3?°út(k—¬7çyÿ¤èâË.;ùM+˱Uwo»Çà\ó Ã²qM“ëqK™ÐK¨K è“f$Y6Ç€¬lâš@âããa7EƒöšøN{—A{a(áÀÆ5÷Iýu¥—s,åÈ)ðm††Þ#lE¹O&’ò6nOùa“ Lˆ~9Ÿ1·q¯´±¸/áË!ôc,–ªür,ˆìu¶?‡2DeÒÙm,F¼@¤¯�ö“¼o;ˆp?ä¯rô¸¶ÚSÜÛv³8’s%(®è®l®Ù÷u±}èÍæ;T 9)¿æÊ6Üš<̹«UÚQ“À׃žþ8òšèأ쇹 £Åltµ}Ägª¾àÞòîNŠH«}©œw¹-uE46c÷£x©mùi7 { ƾQ¹Hê÷>èÆeµ€¯^$õ{ôõ2Ðû%¢zV(AþKéO¢þ×=‚xÑñö@t—¬ßøÚucößo Î×Ü–ú{èÎi½™²†z3ï©ç]î\‹æzueê°ò^ýtãÈË)¤¶×:î×®_C9³ãÇ‹t»jßHy`Ÿ[(³ <Ù ï‡òŠë+bµ÷Ï-¡>ÅÒ–Õº8ðé§}‰ÕROFžµLG=™ã¤\óˆßè|nÔ¼Bö‰N[?å<ÈmÉÛ@[ãøFŠXŸ¼Õ'®áC1Ø€ïõÒ.¤Mw[cý�Æü›|œgº]®ãLvÈ—¡¿Æïí‡]øˆœ¼´k=衬ˆoZ‡ÇzØ…»aîžœ~wYênÖ^) >‚x1Ú†‡]¶ß^iûdiÛP…¤Óë8W!NÚ>œ|ïð ]óàEÖ|“8„m•âžWô°ýã3\OS¸Y%÷kqM³/µá¤KÉÓÄ.'Ÿ¯½ ýõ¾?Žž|»öÚ[–š-íiÅof‚ûñûŠÈ€=.²3!¬&Ûñ'¨³_Ú§œøï÷•<­’zlú@SiaÀ[jÆ–Ø^ØîF| ¬(Ö„/t¤xd¤ÈÍB–xb»S¾X¿ã‹öÙ¬õò^¶!(Ç\}2(>Lø-cŸ“(-D8¶.™èØæ>h¿Õ7àÔíöË=3Màûâ]Ö™¿¯6·Û¼¶p‚í¡ÎÊyÖŸ{$Ôý´ä;€ƒºÀAIk_<¾¨ùšß/N èuëjy÷ö>¹7ÂËù3|‹û7\tL>™T…8µ¼“9m÷‘–ßsÑò¯GU}ƒ1·'Ÿð¢o£ÐWÑÞ¿÷’f£NE$êðÍ”g8GoQæÜž¡Yh:»9%ÍÆ¢Šfc»í¢£ Ùø!Œƒú¤¡i–ãc¿—ô¦dØ šÝ™¥Ù8iö°¤Ù݈W!ÍF³4?,i}Àë¦Ùh–f™^ÇÁ8qÑìÊ”ÇÞHE™jE-WG$jÜPwùb•|‹(UѾœ¬‰ØE¢"Ùþ^Ùi,FÜ@d �ý¥y;äx€¼}uòS^®Ñ·S~Ç9WÜ/égq4ÏùÃhÔ6ÓãB¯‰.×ôJ¾w¿\§}Uíƒ>v¹¤1µ[öe¢j`Û[Uj>l‰¶ïî!›¶ÏñÉu±~Ég<óJ‹Ê¯€D„û¶S†}™¶îwîç©–{»ƒ¶?2”¨‹LpÑúú$òýp0>ðaÚ8^¾©>ðŒæw(Ãö0KÉÓo»òý£¦§%(›ñ„\3Z§×3%üûŸ9ï¨Ó¨9Kü¾1scä]\ç¸*°À. ”eÆñlkÆq´Ã}I” µòNCsªM»Åy⇠àý÷…¦QZm-²=äS ?Å·a–@·Nâ} Žój9Î#Ž »~&p¾tq¹ôÛíðþCÏü]ö$±ïÄ$±ûñÅ{bÅóà[ž]ò ­â9»0æöŽPÖüÌÃ}&j¢@ßs–yúgj`·=7Æ9PÎo¼íÅ: >SžX—ü+¹/aSÂcÂÆ‹ïu¾HYUÊò+€#_¼}¹ô°säkœw£äÕë惠Ó~ð39P;DýŘý°Išʹ%öÕSàãª8'°þ;e¿Ï—ïB®Jþvþ’(é:K—X=b¾¥t‰´ñ©÷Gßõp¾îÎiµÙãÉ¢@Y"ââY?'N2`u�ØCø›œç¯$hÓbæZ!~‹A’&ÅáÒeøis­”¹’î“kFGçQàÜ.aØo×p¾ÄAŸFôA{8ïÌqT(ÍÐar_ZS‚4Äùg÷üsÐßÐúë(ûø,–t½2¹HÓ®,':Õ.B¾J¶•’ö £ãKE|ÀJ°Ä)ëBRf–˜QGÖ‡º‡Ù–ü8Ÿ~»8•üp¢‡ûw”ú>ßð^H}©kGëäÜdzdcõ’ûÖ'?„økb{gßnè%ë“m’Gð-pÍf@Αsý‚ù ³/q”ó·È—xášš4샎j˜ÛGþ…4Çy¯8ßn"Í7Z ¹©ÉHçT‡@sž4_ä¿åÚîe¤¹Æ~I3Å £b zo`»óÚið@ä÷úZù0õiï©‚_M¿ÂŒMŸLôUµ®ãçºÎ|Ô±h‘ÔQ!ì2ê¨S”ŽêÑ:ê%ñv»F õÒ/}¤/QhØshw!|€ó(ÔÛÆÔY6s§S4…uqên•É;åºâÊ”º= ]uŠPó»¥BÍïÆ® NÜgÏí1Žr~w}ên¤á[dÅr_3d1e³{mQCÎñþÐrŽ÷×gøö5Æ#Çe…©Æ$ÇfŨºÓ|ØñM�ß�¤ŽË};‡GŠ=ðÃñDŽø)Ÿ Qé1‡ä¿·Ì°ëAÏk6׬üÊÙäSœ+- L•ã-=.ª­ ø$ÆE|(¡øÁ�ßH’k[KXÎÃNI‹SǨ{ÉßcÇv:ÿ„þ<–R¶øãUÔM(ow¦8Wî3‘–o¨D"#÷‘–´ŒÍÐRŒ´9[Åñ¹óœ´4H¹!À»éˆo²Dœ  ŸeVŸžË+ŽïNÖ¡o¸¿¦õ®n%^#À+Üó¨÷8èaJÈûüÝúkù™ì õìè#£sàÙ<žä¹Ș5øVqD¤i:–ÚµÒ´»0C»¥cÑn¾=•¦Ý´=…1V«×G$�ß÷¤×G¸6¿Ý1á$íJžh&ÿIÒ.êÚý¢+)1cŠØ é·$M¿i[ ôëéSôû®›~3ë+Ǥßiš~­4ýêñío{ü¨[1Æøñ?ƒ†g¸Çö*5¶Óøyb¡}ÍãÀO¬È.wöfìN»Èmo?sÝøáØþ¢<OüDœr|Nârm<Æö<ðÏÌúßAйèvRè—{@ _@gç•Ó\kQ{ßÊã‘ÒWÜk-qµŽâ^[áZ™ ½‘¶ë{ræ°^§*ç­Óä§å»“¦™M«Öv9»OVNE>r}m¼ö¹$×%¡n|§êYîQ€îyùûÜsw[ò;îsî<­ÖªÒ´0ÅTtP~è‹å­U-�-ì%-¬I}J¯Uùrha¦…Ý’Šäì•IÒÄ„¢‡ÃBÓpè è‡bŒÓ­§ÓíÙíx¹œÖAfI^pí«²=er,gû}íî»�Õ5ö+kÁ]ÿgî'vŠz‡aµ´¢(Ÿs¾ëAÏÜO4p W([%ýd*Ê¥ü]²‹Ÿ‹ ß˱¾E¨=Eä§mr?ÐA¹/Âm«Ψù+–AYÛ ´­—å)[yÐöYÇ<â´C† g”ëSÓk[…õ»™ûhˆ+àh y¹[ÚÞÄïxàôÄ­Aþ“¥É+O3ž²Ñ9¯ôqÆ Œ?,nÞûߨŠ:‹Áç(û`ÿÌeŸ ž/ dûÉ?q¯wÔYâŠ3ß'õ'ÒðƒšþûK«åzœÚEúiÖ~%šþ…¦aSc'€x]§éwX¯[ •΃_üÏ\ŒÊses_MÐÖ»ÖéHã²ÿ†ä ‰¡ìÒWÞ=tùó£'Ó8ðŸd¹êrûKˆ2ÈŒ§!ç³\ÏCxl[©×\û2è/â|e¦÷©sýêcI®µyLØQÎwA'øõç¢5ú>ö ܇ô¹½«y¿º¾£ýƒp°‰ßÇ»Ûi/q¯ÐL ûëyïÀ9ëkŒ×ê3Þøä¾ø™±å6ß÷á~]Î9r¿Ÿ´ÅÄ­ÉI\çï ÷y®áÝí¯ø_wªÕ½é|+5½> ĹÖÂýç/ëý·ïJ]z¸kž5 ïÍQv‹|ëá_ª¥^¿Ëa2Ü“>]‡ÿR‡ÓO†‹]ï'Á]Ò’þ Üm.™õŽŸï©·¦¹Ü.·–Ëý§GG³û¿!Ž''êß~ÔÑÔ{Ëe^ê]U¾»åAœI»¦ˆ•}C¼HÞôî°qG’¿Oé}Å<ƽµ;Mî]XŸ’ï‘é=¯àšg¸NäA˜gŒ»Ö•ö]}@½É=ßKð»ÌÄØt&Í€m1(ßy7õúüÃ^q²¢qÐ)¯t¦†åZÚ;R¨¼Nâ÷ññ˜!Ä$:ŠGÎPºé®<år²ÜÜïø¢û‰òLè0Bž|Ð'ä½Û ƤɈ8…ÜËÆ½»â¶ÔDî} ìwªå:j,Åþ¿„ï˜ûFžoÈ=ì^¹5¶ÏÁ~Ýˈ,)…~VbDv–ÑÛì¢ÈŽüNøb»å<1÷Ńǧ~ú9Sr<I~Z:T$öÐöæù€íº¹VȾGŸyÀ§Ž•¾!ÏA0_£$’(2‘7ø<ó>¡÷3 ó!ÚÇÉ©NEü@æLA¹ØŸsN;½o™ïÑ•ÇÕýì[ö'÷¼_ºN ¯€>¹–ëjF8¶cˆïÃÏNp‚B½sXX^j&Læ|¾<{ÚºLÒ‚|ש’z߼枞1œÁw©ã{œß?§Þ#Ÿ,×7×r=¬2go:üªÝTêì‘ù[ ÒùÈõ–±ó½Iç Z‘{ˆäÝ@ß_nß0Gž›Êúóíâ[àÏû5x—?Ûüú&Ý힨Þs”gdÜ8xõ˜œ=­×ƺûuw¾5šå‡³òäérŸÃ>']ú‡ ÐƃO¨ß%¾0tÑÈ!¹ßƒk]ˉãúXªº~‚£æ„VÉ÷ü ¨3Ô¿i×”'àâROp°„経à`)ï%€ŸüÎ ØȵµÝˆï×gVÉ'o€ûk¸Â]›÷d2Hz¿¥Ön ̃#ã€;ðÛÏ:pÏ?ÏqX’eêÈwÖÏÔþHî; í9‚>2òªÞ£ø:úŠïUò¶¥ò“õ&½sŸó0Œ¡ߪç9ºéó',ÿqõ>ËçXàz?ëðs¹®~È©€<uÓ}Aààë{*ð#ɇŽÉy å7¬ý~àò;¦ý¾ïò9ô{NžÁ¹5ÿBô=ès°€çâ2¸žž«sqL[,ö€Þq⇖çŠj‡F^×wì˜úBڛܓ :2Qå'ϵLTï ’ã€û÷¨K€_ÞÌ1º¨�¯|vå‹É§á,žù7)ñn‰óœ!ïTX\,éEY=§¸¯Î{÷ƒ#\Þ¤ûƒøýFàßÁKv&É÷ßV—UA×=]{œï9{=rM¶Lîåðpz¥‰~ù+ÈÐÂØ@‚ûöŠb} öµ¼º×WÖ—‘¿”¤&—Æ‘žçK×{ég¦æ¢þ“Ëúä!¦ùÃèèwžEY ƒ¤v^Ð?˜¨0+wNÇe¼•B¥}¶öÅäéó%ò~‡Õò. ÈéÝ|ÓÅ »ga5ç-`cOãyŠRù.K¹|£—¼X½ñ'§¨4u‰æiŒS!i. ùµ*5ÑŠ �ïÛþ e–gÁúí•sXŸ²ÔŸAwOW½˜|“ãdBi |ªŒo´îLùQÛ( lLðM`Gâ룣oþïQµ‰ôT¨û Xì'¿+(ƒ“yVí8òàäIàÛ‡yïÂòÏ‹<ó½Ñ““Ä‘Ë"ù)þ&e&bÓ/9¶'‘æýŽA>[G WHûhœì?Õ¯Bô°?‹å¡Õe^Ñ¾ã'η&¡kXúýúc{ í±/s¾½^îñ¨ý0E^×·@æ'Ïi™ÇGìÈ !Ž8–ºãÇgqþÓ–›AäÏö¡Ëìü^朇Ì/oŸpøgæ„襁®¸è¶^¢ÛÊvNÈ´³ßï×2JõO,åà÷„þ‰ÄiýSæŸÐ_œÁÅ„Y¿ª×øX|·/AƒÀI8ùñ÷NL“"]w8ùF9ñ1Aæ­¾¦þ.Iã§èȯ$~&ùÕ5žþ[®o9Áýw¾ G~IÝÌï¯%Í­´Q/ã:¹¹>9 |Á#J’Ü‹r‰§$ÕW3ü˜×à^ÏJ´e­Q«õΉÕB¯Ÿç1”ìÓÚ€ò“wµq­I-¯Õ_u¿â÷—ð»Ê¨OVAkzüñû(~ ¿ÙOÓç —T•ÆJ0*ùæ¼Ü£/(+ceò·eªª"V^4e\k.Uù­þi&dÒVÁvžæÃo”ÛÔê<*b–3Éò^ âÈÛjOѬw {OŽ >sâAáMxÍ 9fKªlÞïä©n§¼t ž÷ÏÔAÏ "¾¿Ä?Pòâ+§Jþn¸zlY,ù··÷$D}ËW1.Þ”û>,+á TAœ–0Ìéø:èà3;QVŸÇ(IÜ/ÿýXªêÒ!îG—‡Ás“¶Ç(Ky¯‹%§_§ý­.Èü”Íz´‘ƒ‹m+Ñks¿Qéë±äœ‚hB˜Gí²K¢‰Õ“Ÿ”ëVý ˆóYè_«Re¯s/ÝÐôÕˆ÷ÂO&<+ ®Ý.â÷£½§d™Æ¸•œ÷˜çÁéç5‡“b8ÙQþÛƒ×à[ЕºKˆk<ÆúT,>Œª ۾ʣ‰ðY´KÒùP|º×0Ú}4aŒ+¡Î`«µúÚT9h©Žû²LjÚ -ð3ÎEM«FßM3Šñ{ú¿)¦MC¿â·O®™.šöÅÀ¢Àk%,kµ ^ñ•bëA²àMØÃv±µÁ.)Ž%kKå¼Þ»åà©V|ÃK�¦ö½azboxƒk¾Ê¾ê{õ}õ=ê &’Wר“˜_p?ø¿x¥íµ¬f;à‹%K<‘dmù¶LûŠƒÍ* ôUâ‹Èóé°Z_,¢.e\7Ý6.­òPgâOž!ý$I?\oJÓ–Ükߟô7F:¯¨å<ZÇü*àWòØ+ÃlceDp_8ú³¤oz:¾qÃ~”×7ð\ÀµìëÊ·¥&]Ú—âHÇc¼Ü;-b¤eã¼c«Ä¸1šôÞú r Ïe}eÙ¶xß›—<ƒcÃXKÍệÐ({¶Kž3vÏJtÖ¸ñxî=b 'Ÿ‡>âÞ’KbÉѺÒÒQßµe—”U•²}BÌINçsÉMÐUH³%Ï¥Ú%ïŠ&Óa,Ÿôîöó¬,ášÿíéñ”®GÕ†“‹!«¹Oô‰ç†“ख़oU l°ÍÐì[oDÒtàÄGP¦ùúùô³£'='¾ð,y߀?Ì}}â¿"BË~X”òNU¤‘çfh¿úŒ8i‰FÛÔé§r¿ž¿>eùAÿæÐ?~û@ÿøMþny@ÿøí·>‡ô?áþ5Y®×ã1ð'2rÌVr’«÷ Í·ÇIYKÉ4œ3‰ß{Ó~ƒsîQ}¿ÃygšêëÿY~r™yÒ®õ™™¯|+º(¿’CG%^8Gz(á›n<C_†~Oã…¶ŽÞ»‚ójûaóÇ÷¾Íû ¼a‹ðj›û¢ÆG?— ­9ÃÜjÓC[ñEÞ[KY°ãª¯9W‡_sê¥þµ.5×Ü_`Êl9–Ì=“«å½´{œuÖ =²: dþô£Ú¿nɶ®…]µ·à«7Jüõ}žañF‰¯¾¯t lð õcÄ_ëÙ´ÁA>¹>OÑþÐ~Ü“8`¸ÿPØž=_š…¼+ô¾)Äô”R×ø;ê¡æ6;]‡Ÿë»i-è`Z¯C½-¥›€½Ô Nð¾xÔÇ Ûg¾µÝ ¶Ë·ô<rÎKݸºBÚŽò^Ãu)Ïs/ð½å" mšËóL¨÷&¡öeAÏ?ê lûªÁùÙ’y_,ÓL“6÷*¹_ħï óˆîhéí·1¾ü îÿ‰¾)ÒyyÍèe~ð¶/ÎV¶õ«Ÿ<£ç)Ì#“gŠ=J®L­“gY·Ù¼Ÿcæ\ð௘gÊ,Ý·ÖkŽ·¤/q5ú˜|Ç‚ 2WžËZÅ9§Êj¹çò¾cX)×ÑvÃ(MyªúË<UÏ{ù† /¨ðômÙ¦UòeŸÆ«ëËžÕûËêÙÆØ6ÛŒîqÊ#{œŠú=ÎÔFè±Ð £hhо>ÂsêïN·&9Ÿ‚>>4óC°Iå¼ Ç‹yè2¡÷óÎD^uûámö)¹ï$–y;> ÛyVÔAYG3м‡9y÷_Éûû’ë’¤í[’<K@Þêãšy`›]tfÂÞ X<³;Å: ÷Ì€Œ£úw¸?|®º¿2U »ÏB¼HûpÏUÚ"|Àa†qYjn@Ý3à¹{G™çî—¼o>­Ïa×o3ƒõÛ'ïe!Ô­ÁUË™‰¾g™3ê=DI_µFŠŸÛ–0 ûxjÊ<µ/xåž¶`–žynô»B ‚P8úžÞÃðžÇÀ—k¡äæWuLŒ‰ ߃Þõù‘ryÅêù„'¸Àîê®ÕbŒ«]çâØ‘ÉržNÜžºœ:Vl™]Q÷5pŸÝ—þz ñúg *:2PI{KmÏÝ §»AOâö¤ˆ«³ïFý€½í¹èȶ•ˆcLÅø*ó<÷ª²ß.ŽoK†û…bMãöÎ#‘E¥¿ûŽÈ=+f Ÿç0Rå®±ZŒøÐõ6ïf\S¨xj}ê¶ mû€[Þ»ÇýÜ×gè}]lÿ•ÐÝøe>rýÜä=±T=Ê^Á½¶yf˜¼&'>×%|Ò.Šñì’óoº.ézƒ¤82}~,9I”Ù“¥í¤ÎѦù´“w/Ès ß‘y™ÃÎ :YW=Þ‚¿œóµHÿŸIÈr‡ûoê{qÜy|•¼‚~±ÏTó+>?RkL¼rýÛãìDxû7…¸´³iÉèhn+\”Z,%©–K´ÁåÝàAÜç!m,±GiCÈ»V”ŸßK¤V–š~É §Ê#&¤åãÿ—+>×뎉Aû7Úiɧ.¿dÐþµöãüý©³Þ™xÐùòÀ.ä¾D¿í£ÍÈs;Äé³'&‰'NœýýÜÐ ÞÇ1þ¦=¶¼û!@»òˆŸMߢïÒäÞ•ºà9·{Yú>z}Çuʘúçdæ~ÇK›¼¾L`<pÿ.ó�¾Or¯ç—ÿtº@µ{|$Æ=mr .ýùM¶ãîÀ_ï±)W‹¨K¼OÍùÇØ–§Qç:‘ÿE°CŸà™°âè~§bx¿3I|ö„¼7¹ôˆ3>óÅbeõý{/êpemêXÒ ï±!SßÝ9ôÛÚ?í÷ï9Áû¹öÁ;©¸ŽÊ6MÑm¶¤»…gᤜ•ç_fÅRÉï²mëR—RërOœ‘çdRrÎi ç*£(‹8å\Óqì‡ßå]£êÞõ¾çþ —pì�‡¬Û¿s®epî`<tXât?ƼñS÷¸?âÜ ú(†.UaEîƒÎž òÜ*x=Ï@‡©KÕÝ%¬kÐ<à<£ç€xFw¼‘ŸWë>v×ÉÏw { yŸ3êrÍwå½,ö\”{*HzÜëì•ôØÇ³2¶/1Ê9þà¹éqü§öØnš ιÒQu;â<äªë3qŒúÞa3q>§‚v:ô°´³—ô–Ö¿õyžg§XïAe¼ùñ½?ÁxŸspôê¨Ú;@\åy}5OJ'ï|!ŸB|žAz÷;¼*– W…éõ0ýýï(\ý z}ú.˜Cȃã˜ùWr èµÊb½.y½¾ƒTõv´W­¥UzÖÅ’WÂMÛÄórÛäüÞ9'KUþ<;ÆL­“ÿAÎOƒg—ü#ÒOù›=¶¼Gz¬}7—kVš7Ëý*/”rOc¹÷¹m—qþÓ—Þ'*øæÄ ók¡æHRz}6Á¹˜j†EîýÈäQÏ<M™GýÐdÖ“uñ•ÚȧàGz.ô }ïÐÿ–ëÄ«åÛÍjÝpuÙ¿2þº˜Ô…Óm6a7ZÐÍÇYÛ¤î'çÝ"H‰y'ñ>©0ôéplÀ/.K~KêgkSáXI“”okSrNAˆRÒÏ\2M‘܃±_ÞGV(ímž!|>Åù÷û8Çf=Ïû¼’K¬ÈÛƒkÙ·o¦yw›äëRíÒº5µAžYËyôJÒÇܸ:Ëͳed:؃õ{ì~ÀÛWnKðÞÐbsŸ\g㯹mÄÜ÷ƒçÖrÞåM“wØÖì][R³Ç6¡/0¯m+û;âòl#ÒFe>ÅgåÃ=˜ëRÖuœËs×bσ«„ø<óM¾YrÉ{•îã;å½XQ)‡ VFÁ;É»Îöl«–û8ǹ=´#¡Þ,¦\@;Ì•¶Wl—es¿AAí¼ÿ™ºàÇuþ<k@:ýÂèè!3¼Ð~0Ë¯Óø5­ÖçÑ?¯ü“ÜðB*0'ò6éöŽ#Ð,ß9ðø6òö<øýX®“DlÝ.òyY÷oF|Dž|Aöå±@äí)ð‹êpÈ铼ÊÃó ìk„C'99À»¡Læ»÷ûrª˜õÎè¤è >r‚ã½jBô—߈üZ¶2ð1ýÿRÙÓ§~é)«‚«…[�·.w7\Ü^¸£pOÂ=÷\î×§t äWW ·�n%\în¸>¸½pGáž„{î ¸$ܯ2£oø´±î(ܓç•_ ~pGឌI?‘@{¯�¯’};ëôá>ðç d5ç•Ïç."Ρ Åáü÷…âX{ìo¿Ÿ‡:Zß·GOòû�¾¾Ä,utb®iÅ÷ðþyÇß·þÌ3GœØ€Öî¿ò‹›ºµOÏç§gZæÁôëôKÄ>džú´)¸F9…> Ý ãhôYŒÿÀq©?ÃV«¯樓 ,²yp|àEèæ¾íák ʵÀéëaçF lo],9Ìyø r/ 9Ùþ�¾§¬;‚1Å5Ü(¾<“Ãã£h_Úìq|wáû‡@}ŒëC(¿°¾À6¬m¶çªþ”º“‹gœc©[´&¿Î·7xÏšßzQ®oñâ„i'ò|ux³xñ÷ñÝÏÉ)àg2\ÁhÄoó[od“é ˆâÈžÉpÄm:­ì¤oEú¯sýmš(÷Û­*KóùŸÊùî›Pe2þ,~Ô‘ýVz•ǾcùÓäÔhÇ6®î/K—Ã2> Ûvy½$Ó—<zª«?†ëÕÜÉùúÄ;ƒküªoük¶%=ãoI} y>5CŸ_Å9‰‚Dº\¡Ëý yòøÉ)#+û´œ¯\(ñÉ·ƒžDß®C¹_Å·߯áË9ýÓè§§ Wz-¿§Ðò—‚Ï{àJ=‹ÆUý^ô[ú°ì_´½Çù`žó.æ=¡¦|[55>3èöü“:+ͰI:Œþ…À%Ïwò>Žï£lÒÖKø>AºAâ¨Ã øý2üþ¾ÿŠïžÖ8|ÑÃéðÛóÒõL”o¢`Û‘¶O¡ïèïù`_òvWß|Ý•ï:ä;ñBãe*ÛeÈ5{™¯iÈ|ös^;õiWÞ]®¼]¨¾ÈwÔ*‘yþâŸ2Kì¼sÓ•ç®<ŸöW_ŽÉ›õnþXÙ~ôÇË=›\e¶_¨¼bè ('"LÉKF…�;ÂýÜò¼9óäÝBð“{ÊÐÛœÎóK+SÅÅÜCPÊòÌãòœÏeæè(ÏD9fT•'x÷å SU¬Ê2Êcò~L–7ÞUÎ/¤>¸ÀöómÞŸË}b´5ƽ3}³ûmY‡úùx(/&.Öw&;žö¹úãC.ܼræ/ëãgÿT Ä~úŒšo…¨ïßù ¾q†¼Ê¯úaEÕý©”«;\c|¡œÏZ!Ï}ûã/:‡]a+u³Ý1—ÿ›®v”¹Ú±Æz´`ï…ÚÂñ%<6Ç•i*Ú2¬H†¶ú\õäúï)tŒ!&À ʾ|‡çñåžrÈÏœg|Í¡>F™É½Q<³@™î‡?ÑzÍùø¾U“•‡”Åé¹aØ,rr[º…p‹àV ¹ #–jèÏâÎЖÐìöPÇÆÙ7tuuv‰[:ºZš:7v´Ý×Òl}réÍÖ†ÞÖÖ–.«çÞp‹È‰¿´½½ec¨}AׯÞÍ-=7lmj ÷´uvñÉÎŽYÍmȧÇZ¨R·u[=Vwo8ÜÙÕÓÒìÎçæÞž›[ojÙÜÙu¯ª‚Xê¸ªÇ µ·w6…zZ¬EVwOW[ÇF±ì“ æY:°§'Ô´Éêõ´mi±z6uµ„š­žNëÖ›¬ÖÎ.«©½³»·«ÅÚêhnoé‚"Ó¢(«³ÕÚ, Ëd•.‡Ÿv«µ+´¹EÇܾ!Ôt—Õ¹áN¶fS¨ÛÚÐÒÒam umml±š:„&4¨ˆ¸YÚ±%ÔÞÖ¬ó·BMM-ÝÝâ¬öÜÓÖìjþš:7Ïîîí˜}gGhv}g[GOK— ?·tt£•Ý­m-Í+Ú:îÊÁQ熞P[šêîFe6× ›dS“ÎFÌoëhëùx~üΔÞÛÔT)t3}¸…ÿ+ZÉɃ*ÕÞl-]|¡\òÒnlïÜ�Twµ€6Z:šZΑü“²w…è 7o+BÝ=ªñÕK¯¾õ¬<ó"¡z6u6_ g!Z»:7§¡êY¬/bšë]7K¸þêîÞR)o …Ã-Í×çÖ§»aM™jdŠ‘?Ï]%EÑkäh;»>WŸåsrËÍfwr{:³8©Îi㲨³cK oטX9ËëüõJwZå×åÆ ÏW›ó¢-[½œq·ZŽðŒüzŸ]7wT±±¥‡Ø]ÚÑÚ)ªs‰CS]¸œŸF³Åt´Ü³6„‘2&MŒÙŽëÇÆ¿+¯‹-^ôv·(þ|V¤›8ÖxL'ºèbD¨·§s¹¤Ad™“Ÿäð•ÌkmW[ÏYýy=/>£Íºác÷l^Þšñ_€+æÔ³'M<ùy¥þ‚ÜÆ¤ƒ ï½5ÔÞû—ÐuM6INž‹´8<«®g畉:VúUi¾?cã7K½¡ûbóÌ$¯ÉM~þ².¾Îî$ß™¶Œ!Æ¢ŠÛsGd:ùy僫˜s±Ä³«‡D7öv4±õI[=vÙ·Ÿƒ5] NyÙ_|ÕX7ŨW+5§úlü?¬“;Û‹¯Õ 6hg÷µ¬‘ÊâÙr.ƒ«1}ÏA³9¢Eåü‰®ÎÞð…är~uþÌæÒùZß Ðv:ZNZ·örc:'zõ9dj.KKù‹PÆt¾îú-=llé’ÊÐêæŽšw 9Ù…˜hNò±ôñ‹©ƒ;ªëé¦{ÕJXþ…úøyúoFkk[CÚR¹¸>ÌM‚±±¥ó.ê›Ë–-cåròPn=öb³=‡<ºñÆ¥DQú ¾}V˜µ¹­»[GÌ’¢°áÊîŒ}Õa3ŽÞÖ• Ü+»çYé0OÆÐDý°CÙ£îðîtYVsK«…VuÚŽDŽð¶ZhA\tVk¨­†põ•ÍW«º„»Z Mm­YZK²–¡a{ÂoM'ÔºP¬?mw[Õ›C[­+Û™ƒÈµëÒæðÍá–®k’5¢38QÍl£&@:Ë8šagJ‘x8Ë6_ žÓ’ÍSãÙšmîíîAkic”·)4„¬pWÛæ6‰v%ñêê Qÿ[ÔÙ‹Äšäé<HìJ%Kb{5`·XÝ-=½aøåÌ1œÝaóØ7ídŒ:×Ã*äÕÕáêÎs¢I\ä¼è ·£½3¤ly›hoÛpgèžžšnhgˬ]Óð‰–|„20Û;;%fzÃÓ#„ÄveZ—av2ÔE+Í]¡{˜®»·«5äÒKÆã”Ä]ç o“šc6|1ÂCÚ[Ü~ëjk­p{¨=´Y%øèuwB—Â&q8S/tA‘ЧG�pÔdAuΠ\âé/˜“Ië7:$ÓE’ò‹Îž'é‡ÒSÒØëæPE~r°¥ùá-knœë¢3Ý ›!LÿÈõW˜ë껋s9ª¡Ì«®éºnhj­ l¸6𑹞S‹µ>úps¨¿¤ ¾¶¦®&pÑíGÅ5eå Í4O¨¡_M®ŸKÓtÅÉÂ*¼«¥•sP³o’ÍÉÍO‡Õ䆹¥Qnü\¿Ùm³ÓPq�׸`~oOËYqrýd¼E›B]ùñòüd¼Õ›ÀføG&îþ2>DwÚ77ÏOÆ[ÑÙ±1¿ü<?ïFtÔXåá/ã/îìÅ€Ëdâá¯ð~kg[³ç ^s[ý Ìw¬ ž¬×ÂÎÎö–PGNŸåúéx÷ÒðuÅÉÀ:k >Ô”žgÔt•ã§i„HwÓGÖrFéM9õÉõSñˆhwœ,¬Â%r]áYX…+dæÒêX~ÌWÍxV/SsgÕKs ±zu~Ù>CV/Ê“Á“ާ"äâZ-Ti«oÏÏC÷™ŽÆàêóãHè*°zq~…I†ÒìÜîì€,Õêl®®®©J~jòcWK¥§3ËrÒj–þŠSÕ¤£Ê„ÈïVéîಃPÁ5é o,˜±óã*øjÔ‡8_ 5ä»È•¯+HÕ]ùkŒÜ¡:�Ö‡8­¯µónU¿1çR§¬9+ s‡§R;4b³q³ÞŒîìnSZ ËYªõ®šŒw–=Ö¤Õª³ünnm…°‚L̰ÈlÜ|¿t\´iµÄA–Ufå§! —ƒD†eêypºÆ_&ùT†ufë•ï窗¤„‹…få—­—°„›¦í—©ž\ßÒ£«f‹žÄZ¨ñ›öXƇöX¤q*Gµö[ªpBޑɇíK7¦ë®=gêš‰îæ£÷v÷´lš»:¡ï÷Ü;æ8k´Ñ‚íß٬ǤÔ9 ÛÜ- ¥@C ^ºXªŸE犓ÖÁ2ñ¶HéÛFr\è¼hc³æ¬½0a©\uë/áÚëÌ/ákç(˜_Â×Õ)˜ß±æQfÜÒÑêݸ)»^¸D­Ñ‰±l[øétcDcNÔœ³€ëEo~ˆ8÷PάN§Tª¯ÏÚÆ.›Ï¥wdmi»,¼,æ3[ˆVJsËViºåÄuOˆŒ‘Ðe¬žejÛ„¶:K·Y|¦±\تžsµ»Þ¹Ù(ú’v²ZdÍ¡®–4‚gRuïië�el,úØ ó¤£éƒvêwë÷8ùÛ#]¡ë·_þöI7Þõ[š-2Ô/&¸~»â\âúíqÅñÊß³[[ÛÖÉ?!º`ut¶lmi‚w ðлUÆ wu6ÍÞÜÙ ;›°jí¶ ëÝT¿xé*ÄÙêšÝ³9,ã7·l™Ý½Iòˆ%7ß$uÁÙ-=È¢'´¿ƒŸ7ú†‡#ƒ’'Êߟ“¿o”¿¿w–ÿ ò÷£ò÷ù»ï¬8gçóò÷“üœ${>¯<í“ç_bô•iÿǾʴÿ,/Ù5úÀšÑÑñ]‡ïÌÝ£¬ÇwÆgF`„ýCÊ¿xò|Vù¯Ã÷Ÿ?ñùȉoŒ>PÿßTù¼÷Må_þmåÿw¼ÿiÄüԳʿâ{*~ø{*ÿò¸Êÿp\å_üÒèÃÃGªùýçâÈâ(ÿ5? ì‹ ¾¦ÂÅë£<qåW#q|«‘ÞLÅ›zŒá_‰„ñýó«‘ò7”ÿÌ7”ß'þúªHÝ¿)ÿ_þxô1苬ûw•ÅÏ5üsοù× qbŽú½³ÝQ¸Ãpáƒ{î)¸§á^„{î8Ü[p‰ObÓ Ñ†;ü{C,Á÷-|poÀû%Ü)¸÷à‚p‹n\=ܸupëáášá6ÁµÃ…ázà¶ÂÝ„ À Ã„Û 77®åïÇ7÷ýF…8Ý©Z;ô>ÆO8ûÛçúmêߌ?Uÿ.>#Ä ü6ôïD§‘áéÓ…@µÕ1|9ÒÛk„˜ˆo}½!¦àëû!.jÌWÂùOi D2ùÝ™ÑΙ`7ä>ï]*$çyO®?C¾ä&&¹Äú÷ É9^Æåø–À]Ʊò!õ»§Zn’D.Š¿œí\SÍ7„F%ÖNáûà ñ'|oü ÚÛþÛ,¦î¯ùÿÝÿ×îÿÍ¿ Œ›Qý·`6<B-¡ m¸? ¬ºé£kß´TŒóøñF‘§ÔWnL6.óV•žx>±hÑ<«zqˆ¶P‡UWSWóáYs¯V?„¨é¾WŠ’šîž.õÝ”þÕÑ ÝxcGo͆޶öæYP%´)Ô½IÔ4ßÛ”êÛÓ¥Bôd\Ѐ°®–vÆS?Âí=¢†v˜¨éiÙŠÿ9ÝŽ ÎæPOHÔ -5ðfùêg[óVQÓ²©AmÀ’)”Â/S¦ßÙ„‚˜…,â—%†6·5¡6=*HÔlèîVùBÑèjÛÐK«´š¥‹î òjÇïi7ÃîÓ_²ð|‚®¼ žæ!$•ßc,3ùÝ˾³ãQמ.·H92-–áÕqÈg‚–ñ.Uîg“²õKs%BñÆ#?¡‹Ùr=ÚÝ"âoò!:K·Ípµ—zæx†|ŒŽ|ÌÝò³Í®xä{tä‡>ݾt¼-:òSòe:s <ß튗{A_n<ºû]ñžž£Üþ¿2Îʯßr€®xŒrwêxlÇzÄ¡;~ÅÙñ¢®xÈ.ºÇÇÈïó*^˜åÿqèª ‘);ïË®üD—!oŒü†…’m’6 ·|çˆ÷´òV Æ£|óŸ#Þ˺\Æ+F¼âsÄ{ÍÏD<ñÖgÇ{S(3eS{7t!´Ÿ¥ƒ”+?ÊÏõ=Ð}òò£;áŠG~Y±eì~ûO/ãU#Þ ÄÛ_ç×ßߪ8á´?ã\ù¥«Àƒmò˜²†×lQ:‚;]‰.3ýfýŒÜxé¯ÇoÍýÀ!:òBñ™ô8Ÿ—_ónCü¨äìüÆú{/]w«ÂHêä™ãÒ°*á½¢4¬°Hž¢`…8ò«„ä V¶WOVØå˜W°²ÁÚkÒ°*èþ ¬l±§ç¤aÕ£“ ž(¿ë3°²Ñá4¬ßO2°æ ]iX!ŒãCÁ¥òëÏÀeò[œËå×ÌÀ“Uý›†bH§ ¾L~I ®ßê <E¸ÿ¼RËt×çô£Oüz´8¾,ž–Ïʃ?œóàüò¨cú†ùº6uÓb±pýõJ~¦á¿ÿað\¾°¿AÉÂO®Sòðw]ùÛÇòàDük¸÷vÇ:ý”s°Síô÷5€wÞŽ:éòo6rÓo`øÇ„X¡Óoå=²ñ¿ŸwüäŽlø¿á{|_¶}¿sÅg}ž\¸ðS2„åQñ?ˆïð|Œ]þG'¾Ž< ÜèJϾ�}RHÂð\ö×kø‡®ø¬ïÛžìx(Åxx7/ü4àf—ùºþS½¹á5€¿9Û_7þ%˜é·4|àrÀCîÍK¿ðñYUº½‡óÂÿ1þàýfñ™�ü˜«?ÿ ðºÖ,þ'ûrÓ_ø[MÙð›\áÝ«û]ô¹p{bä„»\ñÙæí€·®Pü›á_ü“g²øÿ7Àáû•'|:/=| ô¥ë3ðcwfñù×€g|,Kÿ« cš> pùMB4êô—,b{£€gºðóÀo̶ÿE¦?Å'­û[²áãÇåæwy< pü…X¬ûï〇\å}2/~ÈßmÔWç)½“éÿàš¿™àŸÉKÿ€OÏ62ãùÛ€—€¸šu}¿¸î£  ¿™—þW„]ãaDLã®ìøžS˜¿>Þ@¸+[¿À§0Þbºý÷çÅÿ,à'žTgg$ÿÊ  ðã.úùàò? )€ BäÿX–þ.¼É…ß«�¯YŽ>NÓàaWxàð§³ù}p;ÃaÿK€¯…Óð?~úÏÙö}pÝ—²ôðàÅ`ša ;yñ xêÕJ÷d¸|n{§�ž{[6¿†Ò7wŠ‚—æÅ_8±<Q×ïÀo­„ ÔðѼøßʃ øàõYú: ø‰=JŸ—ô]Ä5–,¾*ŠrÓ_ 8ËŽÏeyáë¿U—å=€‡?ƒ>Ñð!ÀkÊŽ¯ïÞ0D£•ð(ø{±‚øôò,=x Âœø¬Ðôõ!ÀOA¾˜æ_rÇËm€D}tøýrëû9À¾Ogåá3„ïËÒÇK€·>“-ÿ?W£ü¸†'çæ÷WyðÀñ”\aüçÒÇÀKnÉöÿyé#€c‡³ø> ¸ú–,?| ð[GÁãuø;€›W¨ù0Â'ææ7 ð/dã/6 Qw©‚×äÅoüä7_ç·ðcï ÿB?ø=àgÿàžZ#Ãß_�<s…²· ›˜Ûþs•Ç6ýð6eñQ 4²<KŸ¼øÑ,>ÖnüL–õîùdVÞ|ð\=ÿ#Ã]ð3€/O\¢é ß˲ôpò’lý¨Ý¾¸Þ•~"TâK³ôWéš þfnœŸ­ßÇ�¿xg¶~Ÿ�<ÿ u×áú¼ôÍ€­?eû»pl·k<ÞŠÁsJ§vR.ýÿK^~?™”Õßþ Àë]íù=àk³ø¿B)â ÿkÒ€?çà¿©‡²ãkàê†lú{�/A{×ëúfü¿ñ*ø+€ým¶?_1sëû <MwÂнh³Îï ÀOìÊâcaIný–•dí¡R^ ø10§ˆŽà—ÁžÐð&À»Æû^ÀoͶç€c'²òQ4uõt÷ô¶¶Ö4‰æ¶îp¨§i~¦×ð3½›?¹°×ÍPut’?[[ÛðÿY;ç3{ðå6.ƒ64unnèîíh¸³#Ԡº[zôsÅ`f Ë®]´t)Ï81^úæP÷掞ÎpO0ø‰K.j˜SS'6o…ÝphCgWN„s•¤ö0gOÊd6§wi_°Š7¢ŠÝ÷v7uv´º äj~v×÷‚ææ.žø<Wnòè‘Ìn5³kÛØj¿˜êgj±É΃o¹«ª¡aãæÎކîžPWOCƒhX¶¥aUËÆ¶nÔQîriéÎîcumìvŸp¹PºtFz;!Šêèm¸¥ãž¶Žæ†ùŸœÅ½˜&µvµ´èƒ|éÕòówžÚÖJêlqÐ g•ц–Ž-Sp·Ú\$º{ºš6u]LŠÍrCª`³›ÛïìhK¨Œgƒœìn’ÑÝ»·#·¦MÍ-aÑÚnéÈ!î–ͨÛE6AíÃ˱ڼqa²“TÜêií¾˜r²é–!]s;—¤_ÎÉD¦ÎôaCCÓÖPÃIGC÷àô†Ï׌zP[K××ÑÞsEFë=iç¤( £V¢¹–ÛóUMØ ½-[Ãò”öù²ç ÁA”©‹*Sµ4=2à×pgÏùø÷U«Q"I&ŸdÉM7Fû†ûÒŽ‹éó¥ÈDDwW垦îö\úÓ]9“ÔÛy6ßÅ=L9 ˜Ûj4¦\'hÔÙÁ†† ÝÝ6V[<6÷ö´lmà>û¦»òQ#;kõm«oË>¹´áæŽ[än|‰òìpÐzLÍ¿3·.çgçÕŠ!·áæV3¿’àyHkP¯¡¥£Ù…@’)šØÖq×ÅTPmÊc†àMá{/&ùƱ†QÎI•ü£x™ŽÕû·Î“óby0ĵ;“¶ùü²Ý=•B᪓ò zE7W-¼÷“\òk A+çʳ¡6+n3£éL¥”ž:wº¶q»Bm¹Bã‚‚–ƒFá ’ 7Å]$¹A5¯¾wó†ÎöóãFÖí–ÓÒÑÓ#‰r©R%OçÐǤèô±¹såeîPž+΢ç¡�-w%?É«…b„ <Ö.წþúVw¦À¼žÌñ _=¿Xéì¹ ¿_‹Žê¼Gí œWÖªQãâgç-;MŸa¤êi½8•V°j%mHDòWË…dâ‚ú¥‹6µ4ÝÕÝ»ùœÑ¨hˆÍgi%1Šöò„@ƒºëD¸†Õyš«ïh`eš[ÚÛ6_$Y)æÂÒkÑyDàÆ ZmÝYRh…`ë๢‹¬¡T¦ÝT§ÔÀîô½(fÀÞ”Šc† *nŸU$µÔXëâÍéo½±^´uƒg¦› ÅŒRläÈÛLõó"¸ð<H”LU³yÉâ/4D7ž_ñPã\Ÿc:ŸöŸ+†ææò¨óH%Ž–¥‹•&Æ4]r´tƒúººÆT¶ô®åt|}soGž-™’iM¯=#L3W£twœ=¨ó¥Ús!‚å†à›[Ñk ÑÛr+ ”ŽŽ¦\Ù“Ö:3ÊAzAáFm>;×Q““lâ<#]êÇÌû|¤–£\·líá~ûµYþ<Ä&­µ²ÂŽÎy2Z`.68 w% ½(Jž¾@OÉñßÜ®¨2í\weôUÒóÔ^ýºUï—º ú8/0¦iIVäÿPK ����v©Æ@���������������com/sun/jna/linux-ia64/PK ���5‰g?Šanê��ëQ�(���com/sun/jna/linux-ia64/libjnidispatch.soì} |\U™ø¹wf’™$MoJÁ ›Ö›6H”) N¡…›4”¡”v £‹fJ›6Õ>Ƥ-ÁÜ”P‚uÔêÆµÀÈCºˆW\Ãê®—ú ï°ºÿÅ?>éKñ1P•òhóÿ¾ó¸÷Ü›y¶I»¬ÿò»LÎwÏ=ç;ßë|ßyÞtéÒŪ¢ñÏGÎ#4µœ¥ ïmòÑ_¢¤þ?‡¼™æ üÿFÂ>×/a¥ÓÿW¸Þû]¿½U,wøTâúNåߞξ=ÝïúÍò¦ˆß ÿÚÏŸ‡{›ˆû×ÏWìÛ²¦¢@û¼ÿD}PlËÈV1<½¿£ÓˆëWÆs&A>rÙ²w“ðoÛžøãŸ¾xgz‡R}áv¾õÕÕ÷nÅ|7Ãó=þM/<Ÿ„§žÁs5‡ÿ3<ïáŸ'áøVx¾Ï;cóxÞÏ\x¾Ï�Ïw< ùß—Âs<ï‚ç;ð̇g-<Ýðüϳ‹ÿ6Àó<÷Iun' Ïxþžûᧇ¿_O˜¬½žÀóNxæñwçÂs/<7Âs>qxäý÷x>O'O™ÿÞ Ïvxâðü<·Ã³Œ¿{?ÿ½ žuðü<+á9žð| ž¤:nƒgÿ= ž³áù6O/†g'ÿ{:<›à™O” ž‹y:Å·æiχùïð$àAQ¹žÏÃs<—Àó^xZó|¬ÿ´°Ë¥¿Oƒçôßžùß_ô¼»Œ0ùÞ�Ï•ð|ž:éýWá¹ ž<IxÞ O;<ð÷ßå¿…ç³ð|žk Óù_ <KùßÿÂoå¿&<¬|žðÜ ÏnOiéïoÂs”>žÏñ¿WÁÓGrÿ‹yÒ_ò¤[à‚çáiã°•Þóß÷Ás¿Žÿ^Ïgàéà鿃G‡ç)o-ÿ]Âë¥w³ùïàY-ÁI᥿·IßâÉ7žE9¾ÿ <oÊÿ”ô÷)ðl†çBžþ<o†çcðôsòmÊ,v3IHª«‰ÄŒ@ý”¬F] ÎTû‚ªZš©úë–ž÷ѧþ…ÌM‡üZUÄÎЕÓZkN5Ný`kc3ˆG³¿ºQ7Wßähn^x³ßOâiż¹QWÒÄ4ÔV¢Ïm«ÐþNk­»×ÔçëÿîGA£- Q\ä’‰ÿH£-@=6“Ôe´#¨'¨s(è'h¯Ð~¡MB[…6~%/ue£§ßËßÇQ?„C9IHõ£ìÊ|ÿÖð_´ñÈ+äÇ¥÷Ýüí:òÿ#<zùq)Úl“0ý@^!ßPßPE?ƒ6m*òþÓ–’Ê@ùF]G}Û)Á?ÏQvQ„­AûŽ6óNžÆ~�m ê8ê𽎺/ëüWù/Úüoð¿Qÿ¾ÉÿF;‚vMØ{YO¿Ã±OÃ>íÔ¥÷¿Ü³çK Ûî¾÷닺ÿtÚ×zeü}òÅÚŸDZ›® |ú•_møì«Û_Ž>zÅ“[|ûCÿðÎW*®¸ôÂë·_µãû-+¼ëɺ>ÿž×>úÈgÆ.žýðï»èïS­—=ù£Ÿ\öpÛ–{_ܰ÷ñßúþ~ü@ë’«*ß×üó'êÿé©ÊÏ[ëÏÿ¿?Z>üÄG•?š= ¯núíý§ŽŸ±zí›ÿõs‡Zÿ{ïço˜£<ô¶¯Ö-¼m^ôš?^<{uâêß-7ÿ8oÁ¾£ïh©»wÆ®•ïztçsÏÞsÆáʯÕÝ½àÆ‹~?÷àÿzåXÃ9xàüUû[>·°íðŽ¯Œu.üäÒS;?·ò‚ÞW*Á÷5[ í?}úgÏ΄/™}×5¿¾+úöë_yOýS{OI<t×㿹lõÏ?ÜÞ ¼óü¿ï>ú‹–;ýôé�>wœ¶ôg«oþä_®è{Û'Ïûòyÿõ—}7Ô~ø–Ö÷_}ÙFý÷ëo½ã©³ßtß΋¾62¿÷'ï¼ð´÷Y¡o.l{ꮟÎýèNùùϳ}ñ¥‹g_z϶gÿùô›î|÷ùÏ~ìù¹Û?µý¶½ðÓ?Õþ¹â¦K¼iç}î¾äÛŸ{vaÛû¿µá¼êÛ©k¡îE—þ×ïOùùų+o=ZqéÙ¿\ùîÆ±•ÏݱòÆ/Lð®…Ô>¿Òúׇ–µm|úá.lð¾øÏ:ÕO·%®ó]ï—éÓ/_ó‰cÕß^>í¯sÛN½vÁåß^ØöÐèG^{䕯ÓÎÿúÞßßôÚÛö<xÆ W~îþž+ÏêxüÛxˆäù‡ýˆªÖÄ£*M›Þ9}Ýb]ÊàiöC®äp£’Ü}‡›1úsV-/ç×¹³~:‡“X™·0x•€seæùLãðsYþ/uŸâ'.ünò8Bôc§q¸ˆ/’þ³Zæ+yÿýcøå¡ÜpÅŸ~W¾ü„õ—ÞKóÀùr—ÓZ“nåÁÿpø‘Pnÿë-Ósç¿?O9»ó”sIeîüoÏS>úÿsrÀ®ÈÿÑ<ø|&\ÍSo&OþÛòÀ+ó”say˜™'ÿ‹yÊsžüoŸ–ÌSomžrÊSïòÈÛ+yäí›yÊù]ø–<ð'òÈϪ<ù?G¿ZòÐáUŸÛï·ÿå¡çö<ø¼^åÔ’æ¿2ûæð3XN ëá_„?ügƒ›¿`éÏÿµjž5³oϯU²òõ¿¸Ë™âYÅœ7âØŠX%/‡©èÜ>"ÄàÖy åù>ÿ´»Œn\ÅÒ‡ß;㿇á“åxõVr{ÎóϬáù70¸Åû‘Ÿ©<?¯×àùûßD\åü¾ŠÃWðòy9§ðüæ:–ýγ¼^k&Ç“!Û8ýÉ3,-øâçåXkY:ÍËyÇ4N‡{Y½I>0³“ó×úKñrîø ¸ñÿONO“;Í©öû‚Àó“œ_• ~—ëw{ßÌå!NÎ_R§ºðÿP—s6+Çäðó8ßÁtŸËË1ßîοh×m,-øøÎób7Ÿåå›C,Í»gòMÏ9îòOSY»º<íú8—·ä9yùÿ­°üšGï¦ûyùw¸ëÝÌáÂáãåø¹<O²´TO _ÅÒ)Î÷Mœ@íøXÀ7ðò9|—(ÿYwþJ!WCœ¿\Þ*ìÃÓn|6 ûð~®Ð~Q-‡_È”àåß'ø²„ËÏÿ/ÇXë¶3£œžQýE½ w½›¹ü›–ÏË>ÄÒ^Î !Wçºëýç/yó—vþ™×k®w×{.—ÿažpûcùY~ó÷r!|0ÐàùG„þþÄÿÕÂ^õ±rÒŸ‚ž—¹õñ\ÎË ®s:ðüF‹›þßàvÏ⠎ϧcÿòâxd®ùeÿ®âùO²´Éáh¿)ݸ½vï!W_eõ y«àú5ê¡Û×kDùn¿z§§¡2¸§îíåŠòÓÂŽqû#ôëyŽ9Ó‡—s€ËÛa>ïvæƒîòÛøàg†Ã—ˆz?ÎùÅéüiN‹�!¾ÿ!ðçÁ»°ßáød<ø<ËåÇäA¸“5‚nî~Á'ú©…Š«ÞãøˆAwüJø ¹Ë'B>tÃrûI<öùÎ_Óƒÿr¹{›[®žá|!?åþÀ»8=y½†ÅÒϯQü§‘ uŸ'ìêwyüÈû¯…œs¹þÆ/DÿûKŸË~?#Êùª»½:oæý Ÿ—ù·'Æ;¸œózÿÊù8ä¡Ã÷óèWœãcýÌ]ï•¢ÿ}¯Û^íöøj<>mð.–úøßB¶0|"|@®FØÕ•\n¹¾<'äœûEB>ÿç»ñ6žŸÃïã|´îW\ù?,ü1n- —“ ‡>7 »á)çNQÎB–_ؽ÷ˆzÿÙÿAÁ_»Uèé ;ÿ:aWÛÜvõ·®tÛÏårb|¥¿>)èãñ+~+ú…aV°°'Ky¿iü ÷—¸ž~IøEŸáåsq9Cø-oåúÎˉ‰z=þÕ°ð·Ïçp^>Î;Ñòù$™Ð ÒѱnãæM=[Vuoéè k×oZO(þز~Õ†õéì†\k6w¬Û°ùºU:VoÙÜÝÓ±jk/@W÷®Â/h.Ò±d[ÇUëÖ÷léì^´aUOOgëÃ5Ò‡�ýà¶Žnž»c5Ͼ¥«{óõ­7,[µ±“lê¼~ɪm«â›×o‚,"¹hÕ† ×­Zý!²®sKÇ·ÜììXÛ½ycÇÚµë;0åÀé_«7oÚÖÙݳÚ³vêu»Š[×Ù½²\³jÃÖNr}÷ú-WoéÞºzËÖnúÙ²U[Öoë\É Y¼ørüŸ+W%“kœ,-kÖtwöô @ &ì2%o&AYiý¦uØÒŽÍ×}°sõ‚McïhkìFŠ?:’œ:6 ÒQ'¹fóÖë6Hù×nؼj‹;÷ùç¹Óï¼Àž÷N'½móú5Nj+ûZ`ÖMQC&Ù7vôlÝÔñÁM«:þ«m}74ªõ†-­[×®¬óåí˜çä¦9…äû Û½y äî¬ïéHÚ‰|_¬ï‰ÍCKíìéìÞÖ¹tUÏ–K»»7çGa]9™‘\óÖvwvvÌ[½asŠ‚øßø&ï·[7 µÉ_|¡ï7‚ n^Mz¶¬éìÁ¡ÒO6vn„ÖÆw5|ÈÐÄ?zÖ¯½'H¤nLæý’ßѱ¤ãüE—_^ˆÒWvnÜÜ}CÞÝ«ÖÐb®.PŒiI)™./%ÓâR2µ•’©PûíL­2­ß´¦³wùZÈ×Z¹ ÑÉÉUˆPN®B”rr"•“«­œ\…è�ÂâhXGHñ¦Í Òðvó&²¡¨òaÇvy[cÇ6lÞ´Îk(·ºíèV!ÜÊ eϦ$˜ì-kóã ãæµô»Î¢H±Þ j›mç“ÝÉŽÕëó×A³Ó\9 Sw§$F`)%¨xý¦m›?Ôy ö …³,ɽr‘‹i×U8OãbáLNW_8ßrÖÎ$z&”!j—]Þ±|Ó»7²kЪ®NÞ@>¸iýšõ=ÉU[VwQÛ‰nBB6÷t’5#ùqao×l(,>›“› ý)ÐC½lÈæëc«6AyÈßsÃÆüB�y¯¾aãu›7ÒÄ«»6w秪j!Þ÷Pw¬ÐëÂ\‡ EXÞc»\…²,êZUð=ú,…èZ˜ ëŠPa]a*¬+F…uE©@ªbtXW„ë8:{·t¯Z½¥cõž×r ¸e3wT¹£îh™cÚVvaGWÐmaÚ^>õ‘„Îä×(v‹÷St>vðB'ûúÕ=@Wz¶to( 2ÂÓº²sK׿5d5³¥…Ô•^€„-ñËuu®þPÏÖüªgG×°Ø¥ èÙAfz@€š¡¥h„rZä éèì]ÝÑEA7ý‚Ho¤Mv^Ëv ˚У¸:+ð2Ww­éL’äÊóŽ[·töb§ó!hë& 칡bµµdÕu¨Iî,Ù[7m\•$?ü�; Ÿ­ßô!Ò¹iÍFjÐ >ÿkø«£›t­ê¿7'·U«Wó`«sÓ6²¤sx àùb©þnY µŸHÖ5ÖoDñXÝÕ?›VC¥k™1Æ\ˆ°s5Ê Xh.øšõÝ9ပ«—Þºi}ï�´|îþ,Ó†õ×­~{Ïæ·¿óíó€ƒkVmY_×ÓÃbx�mZCÎíÚ¼±óÜ-×ÃG炸œ{ÝÖõÖœ»‰ ͹P€$P¹léå­‹:Î{»¼ZÖýOµÿ“SDúéÿ¹KSþÿü¿oMwæzg¬_OWžTp@†¯ßÌß'øxâA¼‹Ã³x’Ã{àÁ&>ÐT9<èë®yàÍöÀ#®{àQoöÀ xà1zàq7<ð^ÞÞ˜nrxÜàðv<Åá |ˆÃ»<ð4‡'=ðÝÞës¸épø€nqxÊåð!|ŒÃÓø3¾ÛÏpø°~ÃG<ð,‡[øaõÀ çטÞÎáÏxà Ïxà‡ôÀ»8<ë'9ü°W„üŸî‘!ÿxJÈ¿>$äßO 9÷ÀM>lxëåðvo½žô–Ïáø0‡§=ðñÀÞàÉÏÓ³rß™ãÁŸÏ÷d<ða?˜'ÖO¼•óËOžÃù57wùA¼‹Ãµ¹¹ë ÏÍ]¯ž§|±O×)WçŸ%ÁC|T‚+|L‚WIpýt.Ï™yàQ î“àñ<ð®<ð! ®Jðá<ðÜÿ¸ŽsP‚“YN~ySP‚Ëëœ4 þ~ žå”¿C‚ëü«¼Y‚›<"Áå5êQ©ÞYÜòËk®cü<.Á7Hðv ¾P‚'$ø¼K‚_-Á“üZ Þ+Á{$¸)Áß)ÁSR{å}kC\Þï•–à•|·JðaÇõîò^K‚Ë{iÆ$¸¼'#Ák$xV‚O“àd¶—×ók¼V‚ë|ºHpy½!ÁeyŽKðÍ<!ÁåýI ~Š7%øL ž’à§Jð´ï—àÃ\Þ‹bIpyÝþ˜?M‚g$¸¼O%+Á—Jpòfþ] ¬Ið°×%ø7$xD‚¯‘à†Kð¸—÷õ$$øõ<)Áåý¦—÷v¥$øßIð´¿Q‚Kðë$¸%Á$ø˜_!Á3|¶ÏJð¯Ip¢;ðË$°&Áß,g—àºHðoKpC‚B‚Ç%x£OHpy\R‚ËëªM >W‚§$x“OKpy¯Ì°?C‚[ü >&Áß"Á3|»ÏJð3%8itàwK`M‚7Kp]‚Ÿ%Á#ü­ÜàgKð¸—÷¼%$ø½<)Á—HpS‚ß$ÁS|±OKð >,ÁÛ%¸%Áß&ÁÇ$ø­<#Á/’àY ~›'søÛ%°&ÁÏ•àºHðˆŸ'Á .ŽÄ%øù<!Áo—àI ~7%ø;$xJ‚¿K‚§%xT‚KðåÜ’à+%8ѦU˜¢FH+Hö‰’°ê/A¤NžÐ ØšònÈZE*±ï£þ«JÌ ô<Õ˜³Æ ø�1š¿ÚÔÂ3µ–h¤™¦ë‚¤!¬%ü¦Š2InÖnbåÇêzývڢ頦é;¦iÍN§hz¦6i:l§“4Ý`§4­Ûé8M7Ùiƒ¦›ít„¦Ï±Ó:MGì´FÓØiBÓQ;=Šév:CÓ†Ó~šnsÚOÓ1§ý4½Ôi?MÇöÓôJ§ý4Ýî´Ÿ¦¯uÚOÓ §ý4½Æi?Mw9í§é Nûi:é´Ÿ¦·8í§é^§ýG0ý1§ý4m:í§é~§ý4=à´Ÿ¦öÓtÊi?MïtÚOÓCNûiz—Ó~šN;í§é{œöÓôn§ý4ý€Ó~švÚOÓ:í§é§ý¯cú{NûiÚrÚOÓ?tÚOÓ£Nûiúq§ý4=æ´Ÿ¦æ´Ÿ¦ŸqÚOÓ¿pÚOÓ§ý4½×i?MtÚOÓ¿wÚOÓY§ý4ýg§ý4}Øiÿk˜~Ýi?Mƒùí§i¿¶h:h§‡iºÆN§iZ³Ó)šži§MšÛé$M7ØéMëv:NÓMvÚ éf;¡ésì´NÓ;­ÑôvšÐtÔNg_¥úo§34m8í§é6§ý&žÕ½†Oi"- ’gY \°§³ÁžšÂžV÷šEÞ[EÞU-øÞ(òÞ,òÞ*òžøŠÔ_ä½Yä½Uä=Q}EÚ_ø½Yä½Uä=ñ©¿È{³È{«È{¢ú‹´¿ð{³È{«È{â+R‘÷f‘÷V‘÷D iá÷f‘÷V‘÷ÄW¤þ"ïÍ"ï­"ï‰ZQ¤ý…ß›EÞ[EÞ_‘ú‹¼7‹¼·Š¼'je‘ö~oyoyO|Eê/òÞ,òÞ*òž¨Á"í/üÞ,òÞ*òžøŠÔ_ä½Yä½Uä=QCEÚ_ø½Yä½Uä=ñ©¿È{³È{«È{¢Viá÷f‘÷V‘÷ÄW¤þ"ïÍ"ï­"ï‰Z]¤ý…ß›EÞ[EÞ_‘ú‹¼7 ¾/ö/Äø?C¿cü_­)*ÆóшNhü! •8a¢D¾-RüO†þ:Þ ’6:ðÇÇ ö>ˆãô{ž?`úèx¶(¸{EÅ_È>5hÈöÊÓbdû4’ª6‰֌ր ®°Ù éJ,<ÖØbDšéøDñ{®Ž‘ôô<aúºˆ©i~‰!²ÀªÓð{ŠÄòÙxæúaÜ0@ŒL_/éWhý$ø’ˆz?ŽÑë#Jfü¦&ŠP*Ë ë*Ìÿ›ý/ÿz³’L«¤)rz€”ž¡$ŽdÉ ¥g8£= dÙu3–WÕ‹ã%´<+$•õ”þ¥?âG8} ðñŽ¿›ß¦!ÿ̈nÒú"J˜4&U?iž*ýÕI½A‘ø9¡>:>ÃÚƒõ¾@”Úoo5FÓù>©kâû û^“¿FˆV9_#»H«0H}XÑHcZ ­¾4!‹|´|S5I«¥\Oú« ¬?cèœ?„¶/‘Dþ`Ú[~Èù^„¿LŽöQú±öIü¢øÉ2Y^ÙÊ#ÒŒòjT‰ü"­9ùíM‡ÃgÕ˜$鯡'B銎ô×UrÉôM¦�mŒS~ÐöDƒ4"}÷�)È"Öž¤º ¢gújÚR¡¤9Ki41Mó{éÃê׈íËänß’í cût©})Þ¾°þŽZþ͈ñ·üáÏÊ •þú(âÏô ù)ÛƒÝ/‚=hG~SyÔIDéÑü”ÿ橲‡’FC%+/£T-H…hûh{!KUÇ”Ùe¶wiŽöêØÞˆÔÞyø"gñö€Â&@A-“½èÆÅ’=ùšÃóG›v¼ƒãŸl#$3‡¾r&£Ç—U°_˜ß|±7:Ç ùŸ6ÈlM³ Þ<ò>£†ëo¦/tA*ÔŒúoàx¬µ÷“äáGõ;Šôqøá•:ª/L> 0j:ÑAÊû™}L½æ%`¸(h´;ã9ä%ì‘— •WÁ?À7"äá¢ø&óÊoéøuû’ããKäúú¦´¾—°[¤]HÒÚ_=µí7½íŸÒúDûƒ¥·¿æ·Jëí¯)½ýÓNpû§´>Ñþ¥¥·¿ö·Jëío*½ýÓOpû§´>ÑþæÒÛŒžØöOi}¢ýáÒÛ:ÁíŸÒúDûµÒÛ_u¢ù¿@®/ã§õ%f‘þriõ%ˆ^Ûí&¤ÕÍßɯ/äÔGìøØêò‘Q_ê¯ãW¨,‰þ_L =òÉßTÑŸÕ§yêSª§¶¾ôQo}SKÏÈQ¯?;ùõÕIõYGÆ—4^eüLRr<X,ÞÍ¿Ðx…Å'4~I`|NÛ#LJ]ûÇÛ}ÑÇëvÌ× ÛãkxP¿/Ò¢»µóM�ìaöȃï‰Ö¯¾jL_ £ã“)°OyêcýÙ Oyê×hûO€þä©?EÛdêíSžú{iû½õO½ÊSÿmÿëúל°úÓ´ývýY30;FÇ+ª¢J+iÌXòñ®jÇ+J?,Vÿ‹F_Obú²¯_QÇóãêq\_R®ý ;ã/ЕތzJ¿KãøW0™n¨ÆcB´}ùéIHáñK•ÏM™ý­‡?*çg-SŒŽ/2þÐñÇÜø'Ó³|埖5¾XÚø©L¢NwÑÇ Ùø¨RÛ– ÑñÕÿEùvÆK’¯É¦ÿDù1|3\ø› ÁÿÉò£ŸUËñOÐþ[{ä?ò’ö'„ü'‘Þl¼1¦´•£ÏÁ(Ñ* ¿‚ï õ~âÈ“IûwÂå'ÀÛNØò%õïY«rN,Cí‹Aí‹õsû+Ͼùx»»=c~°WeóÇŸKoÎ-?¹ç”ª¥yæ’tþ aϸôøå¥ÇäÍLÄÿmùç?ªb2þŽÿ×Ǩ¿{|)7þl>ÀÑ÷câG0Šó4Mûz)> >Gç+'Ù~åç__.þEö›9ù—™(g¾­@{ÙÉooqûG|îþÛCíÍ/Ù?j'“¿Í¿o/×>†õyùæ¿ÀŸòKí¡ö¢ŽÎg «º)ÉoÊ#¿Çn߃†Ô¾ˆ<ßFHìW”ŸÇ5ß5¡ýÎü1k??6hû‘¡¤ÕPÛhÚýmÙí¡óÁºªÃ÷$ í1Äüg«o ã;î?Vè1«/YÓ_/Óž‡¥ùF(_uÊ·”d°¿ ä±¶É)•_¢ÿ åÕQüi}>ç¯éûˆ:x1šNWLÕ óóöü±ˆO‡\ñiÖ¨hˆÑùô*jÿÀßýW™í-„ö§ÕáOM§é„?ü^æ¯}Æ'qzsüc×üÄk†-_:¿kPÓÇ/�>>sõªA¥B±æ½ÌEH/Í–?Ó7æ_§�y|Ï ò6Hêt‡_ŒŸÀäg7@ùéå§Wí‰ÁoÆ1â×7¥ø½4_ Ž·Pyfò‰ò=™õ_‰)‡®eãWFà‰žÛUü‚Ù#fo${ü?ãí!Jºþ„ÄÕû¹}Zû”°í'£Ÿ1ÿ4nÿ ~†öÄqü*…ýU"Àæß¡=¬½Ô?î²çßͲçßãn~kªoÎZ`ÿhyUíÈ?É¿í*Óòõ7»ÑÇõ7 äÏ0ÒË—®°í-©ö¶ìòm{Kó‡‚d%È#ÚÛã¿ÞàköÅàë;ÆJŽÏ‚ÁÆ¥Dgñ–çK(Пãiî`Ï·œSþz¡(‰U0ûHéÿ¶?õ‰†¹?‚ß›ÏÙí%ÑØPÓ?2ûª©ýçPûß"Æ#ùxíO-Ō̵ˆ6ko•¡·BË›o‹Ì%Rÿl >'¥þáž\ú‹ò×në¯éèïØ±é“GÞ†[Þ à?¥oUÂO¯¼Qü©<ø"~§£È›*É[Ê[ðèOKÇ7!±JIÞ^Àö=g¸äÍrä-+Éîo›Ð?Ÿy°N„<@T`Ûÿœ7ž<([ArÛ3Ÿý“ä÷·Mð¦P,Ïüg_°-G}FYõ±x#Îü¨¯žHþôž¦Äø.„9CO÷œÿ{u|‰ Fê/É~RÿlÊèó’/áóÐgJëëö¥ü޽Ì™zý4Ž_!ìuàª~z~{½ õ³üûãÖOc~ ú™}YègÖô»ùcD+Ž•?'‘~['~V)ömø¯6ý,ÿ ïôk¶|“7¢|+\¾g?H)ò­ýÅãLŸZ{§¿jÏ'Z'ÂÞ¥^qä¡â(×Oš<˜ùäái9:”c~µyžf´ÙqãbÓ³ÿ$á·ÆoˆŸt–Ÿó;¶£ö¯jU B ŸU'‡¾(­_ÿ«aУ_ЛæÏçƒÉqâÁÈÃW/ ó=¬¼Ã/^Vpü.BßÊèÅÖ_;ú@Øø�î×àã¿ÕFêwíP~ê›ê�I¯´¯Ç÷hº„ù˜‚ûSr/rüp}´Áǧ5N ¿¾*!é’åÑ“§˜žñCºQÔÉQ_M¾ú&±ýÒ¦…³†Ÿ¯ª~`/ýýÒü ¥ >ôû´*ä[õ´?ÄÛ«ó÷Óü,ñ§êÚëÉ+yèã_¥ë•}ôô ?²žx‘®.Àåê+K?¦Ñ*I?4ÔwÁ†øÁûÞÊÎK õ¦ÕiMÔþY$<kˆí"©­ F*èù>lØûÙ| °/¾$iãûÅ2$Þ?Ö dï7Ñ~²óRø|Á�¤~¯¸=Àò4©¼§¥òð¼”²çß#óg˜“Í·³ý=aŽé×[äý~äàÃãíÁ8ö7Iã| Ðú³ÅØß¤ þ=ð¾kBÇò�_;~³÷_Èò�ükKU·»çGˆdOs¯©´|AzÅ¥ù§]¥Î?éo«5úK·ÑýV÷Ÿ±þCýaùyÿ1pô`ñ[CFBµÏoQïöžêoDâßN»?I¿t‡ÜŸh·ÑþdÀÝŸhùú“aùÊ©oÂúÀ¸=^ž¡ëAœùBàGŒó#U²~†‚LÄú F/ºžÂTpý©VŽËßü+Ð'#]ïüaÈȤïÄüDUÊʯ—‰Qfù¤ÌòͲò³õ3qz~Z@[þJ¤õ2!ðŠHS&{ï‹ô|5ú}–D›¡J\ÞV´öC’|gÍ©ïQù–õ?Èü×z¯¼(=éä<ßõ³ktÓîÏXùI² æ%ßðøø’ã?ž°ÿ8ÊÖÿFøþáP2ÝPËì™A÷ë–¿¾Æe'ìÏõ®_¢ô³ú5…Ï_;úv û]úU% à?ƒ~ÝìÓq½òùyøÐµg¦öìýî2ZŸ—^²¿Óþ]:_Eäò‰£¿„ÏW¹×CyÊ#Ž}´Z¦GR¬|Uvš®^GRuù÷Ÿ†’c¾&ïbÿkƒ¦%I´¦Çá·ÉÖ;Ãwý‚þvyÁº °/”þa¤?+×¹ä÷ß©üBÅÏø9pŒëOBÉŒ l¯Y ½E¿ù …öëzÒaý !”ÞJ¯Þ¦HýW0™ ë'ÀžÓõtÕt=G9ûñ q¾]RUì÷áo¯£ö Iý—à÷&Ú‡búN\9Û…ö¿Ãÿ�[¿8«ºQalÍÐó�Lô7ʈuÛ‘¾ÏXÎäñ|±ñãØ‘:~¥N’¸†h;æÑ4ŽóõiõžsˆqïœËMoûTCY‰ë_ÿeP¤.ŽøìâøËç[dq=aöyˆûë¼ÿ' j{èz• òÓ»lP¼j§ Orõ'<çgø¢àŠ&p}õ2V´·Ÿaˆó2Å~÷ëo¯•ã#î¯8øFw6츀µGÇýùI²Lðã÷¤1½ç«7.¦øŠïO¾Éô áÿœ”úY}Æ]|< ®×ѧѰÇIc…­o oòûÝô}"ïûªP_VÿœôB:ÈåçžÚšŸõשWï»ñÇòþß øºÒïëEû ûèøNw¤ßÛ^ÂÊë•ú«çwd©ÿQ¡¡<’YÓÊÐW•Ñ+öcX²æ±šÞöÚ;j?¤þ7ã×<û¾S¾}*jiü䨝P¯é›Þ”Äúi<¤óUivž ®g£ñ ‡  fùþ€´ÞŒé»‰ö½dú²òMÿ¤þkŠO]žo{Ô*ä?Ÿ½,Šo, áz>:ÿf~¤Ð^’è7@>5l£Ň®×Æxr/ƒë³iý†š:‡$H/_^n5›æ(f¹ò ü‹™Òú ±~RÔï=Ï…ÛcÃsžp¯èÄzÔ·„ã?5KëÉ—Éþ“ ç|5·=™P>‹o©¾Óñ¥¸rèýg«¾»Œxý¹o:ëòáSïÁ§ýøÚTèOËL3T…¿¢ç?gÈ Šã “`»² å'Šþ è“Ï@ü¬9‰Ú—ÐÓœOŽM>ùz@EŸÓL°ùçOË÷Ñú-6ÞÝ=Q_½þ‚!Ú­ó‘þ{|Y]Ÿ ýßðv»D8€ëKdÿ«ü3¥¬õÖ:Ù¡þÕ'y?Cü‘-X´Ëgø^¨}ÄúÖ]8þo@ûPس(‘åô1Oùç_ ÷üK‚®1!¾N·øw&ß!ßj²¶+x>˜Âæ{~´ü.§ÿ‹Ýý#ê•f/z›Y²€GCÿ>›¯õW±_†ãOÞöÌÏÓÑß?ŽËýýÜn§ø=hÏFû@~În…òâ{Ýå3¡¼ô¨»<3åÒ\å.ÇïËþ~€HãÓöø—ˆÇw9ã?Ëèú¾¦Xí[¨‹úÿØeh|JåÅ’Öÿ³ö^T‘Û>³¯>&ï¨/.k/FÏbò'ƛўáùäL>˜~ÞòØGogòÁìÙ>ÏwÊ/Í»ÇÛÏB~PùØCý>»;Ý@Võ»í½áðg^à_øÝLùQÇù…ø˜^|žºÞ…Ͻ¾Âø Ý7Þî£òÂøYн&ìïé~€úvÅ>« ìóTÒÏ'Ÿ`Ï£óÁ~§øþ>ˆ¿ Œßèy[TÞêJ ç ¡ Í_¥ýÇU€oå5!õécí?zßßñ‹Ý˜X¿ežwãb½úpÒ\uÉܬù>¾"~ý™:íaH[ŠoÃÈ©„,ŒÌÍàøE 6 ×½ÐuëáGYÿO´KùzJ¤à=Æc‡|i°ÁÞa(Ÿ´øjcVEî¿PÀÿ»ë»¿ ³‡5Ú¤Qo$ͶšU´¼=ÞÓöü@‰¼—„š\yÉ\MÒWëH?’‡H¿UÑ ýój›Jö6p~§g6Cÿ”´f3ÞÏ1̇“3M_¼%Ìß'q¥?Øk­ÂþtW¢‘É÷@Òqßð«".Ƈtÿ½õÈ`=Ä?·Î‹øÚ"i_\þ¤Ïs4|iþ=ÈŸ¿0\ªÿ¢'Ðþ>€ýgBÁó!U._Aü»ªäQÚ’¼üàìàLÒ%†Õò«‘6=U—lž_“ôeÆÇ—T÷š´= Dþdô*Œ„$ç |âÃZ&"ÆÏ«I4>\ç´ò/ @þ]‰¹Ž¾ç=ŸLÕ¬¢ëÍùø¨ú ¯Pþ¤Ô¡sˆ®FZ~ìkþ<Äß©%@Êö=彡:iùjš ¿w•Ÿ QCAÿzŒ½÷ÄûøÞús{ÿi½ïEýÉHöÞãÍ>aþÏ»cø¨;E|;¤Øû±¢`">ÐOc䯦üÉXáÇGoóïAÈ_MókÄ/æ/SŠ:ð¯äùyÿò0—ô~U”O¿Ï@ϼ?„å§^Äñ¼çq<чôF=üpÓ ÇAß^~$Qž#¾ý-3+½éöPJqÛWÉ>ô~e"¾^z;üè¡çq@|mÇ!ý€^vùÁû\í‘ée·_ÎÿŒ»þÌ›o¾©ÚË?aÿ¬I‹?­ÿ5ñgD™üøS!NüIù»÷ã°ñ•4Ä£4þ¤ûÅÒèÔÇz~¤Žçe‚>2ÿük1~CýaŸ’W„ÿÉæ;Ûf\jzé#ÆJõë$z¤À^Ž‚››*#~-a?½Ë¿TÙ~²¼ó©u1ÞëØŸ‘ðW\í=¦ø™ñ›ùxŸ;Ÿ5ôµúzüÆŸJŒ÷ìûþÄø¸ë®òãã¢ú¤ô ôüh¿/ŒþŽþŽ šÒ_§…íùÏ®—ÇcÓÄ÷„7Cø/èg€ø7ÞZ¡ÆL¿¾ô7³ür7ø+ü|›% ßAcx®þm°?Ab¢}Örÿ„ͧd Èoèùƒ³‡ê¨½Ë(—ÿevªŽÙ;èß]ó{ÍwQûGyüõ­EúƒÃ_¦ö-îêbùûƒ®{EùdRúƒο·÷½ÔqfZ…þ Óícë3HÎþ +=ßüýÁ!\.Îÿ±ýUÑß Ç“ùü@FY|dÉǰJ×û\&üGg>½â-ã7’ƒ~…üýŸýWbÜÕQùÀúìø8í¢¿Ì_§ÿ’ò‡1?ÓO#ÍíÊü·Ió߃ÆÂ™ŽÿüŽÙþ»Õ_ãO{ÓW£¿wß‹3|~u)—ƒÉ?óÏÑW–#½Q2ihO"±ç?Ãø¾o¹à‡C¿Õ‡lÊ·H²a¿FvÎæöu‚~ï¤ô» í?øÏF1ýxæÊß«\úϯ±»Dù“¨Ièg©`Ç{nÅóûK±Û'â[H?à¡y÷Gˆù¬ûøyÆÕ4ž�ÿ×û ?ÊlMcôW>|çט ñW¦oÙ‘A¿²õ)˜¿ÝÖïŒïCt}cþþab{òëWæhaýÒÝúE寮ݡçÁ].þ9õKÊ?²Ëѯ{ÜúeÓ³ÞCÏ7²þŒ~©<ýúRyúãèçÉÑŸà?dýé~cëÏî/–§?½_üÛÒŸ¡òô§}¨<ýqôóäèÏèN®þô½ÁûŸ®/”§?Ñ/üméOìóåéOøóå食'G>w’õç Þÿ4®<ý9üÙ¿-ý ~¶Ìøç3eÆ?Ÿ?ÉñOê$ûo=olý9øé2ãŸO¿þ„Û/?Ý5ÿ—pæÿÔà£$Ük½õç[®ù?†ÿ#æl.¯™¾u¨vç‘_šF×_rykkª3ÂógëÛ׊ñóƒa:?ˆß›|~0õ^>?˜væs¾Eõä™ÎJóÍÒ|`ó{IuŒ,øæsI¢œ8ÿ—1/jocø$" =C†8?>æöÛôciüÓ—Æù¼˜âïmî۹̯S|÷*1¥þ‡íñO¿IÈ¢¹} ²÷PÙýè_=`¾—Îß%)=BfT'º¦´Et«.IúÙúX¢çÀ'·µ™!‘æòU§³N¥å¥ñþõ åMë*-Ïž¯Ò+ ¥ŸÍGfUZß:ÔeŽÐ'ù—Ô¦Éû‹3üiùfBØË ò3Ê�ð‡Éo³4Ÿ8罫“Ñ%8Ÿ8”°ç—?“;9?Û¬@Úɯ!?ç¡}zÞ—¢úVÞü üÞ~ú†H;èK;]²Ç³ãÒú³vy<[šL;×ü`Ò™ R}nÇõ";Ikº˜½?øÔ~¶»ì}W~{ßþiQþäØ{6?ˆçueÑÞë*ÊCÊGn{ßþ‰‰ø–1?Héôrìß'\íIç´RþÈ_%Õ?¬ÞOª?éû\ü¬÷ð³œù‡ƒ¯æ˜xù·•ÿÞVfü;x’ãß[O²ÿ‘|cû]·–ÿÞ: þ{÷ŸIúC÷ÏZä<Üo^þ�úYóNá˜éå Oæ7.NÓùyæ„[t¶þ¨Ê4á½I¢É³¹| èCl=yéÀø€ZÃ_”¢ësŒå¼ÿ²úbIfþ@ÿ[1He„éSÖÞ_ôåëg˜ü%_T³-çþ’jËU€üêÅ &ÞOš1£Ðn¿û[ܯäôç>Q~3ÉdÌðo^À}ø½@¢³GêXyLü¸ž)i$Ýú$Þ×E¢6þ ±þ‡ËÇá‹j·Ñù¯ãíOyùÖ\_Tƒ4�=ó®¯ évJœþÔPS¢?¡=æúÝOås®wÇýÒEÇ¿û©=Xæ²_FñïQþ䨝á#t½Ír\ï´ÿ¡eF�×ÛÆ¿ožˆoý)¥ÐËYy³«=$§>Kù»Üù™>Pþ1|&e¾ß›~õŸ±íeŽm/süë–“<þežäþskä Ý6›eŽÝô76þuS™ã_7–9þµý$}ü$ë϶7¶þüX™ã_ûÛÒŸÑ–9ÿÿÑ2çÿo:¹úüÈIžyƒ÷?»o(sþÿ†¿-ýè-sü£·Ìñžäñm'YÞàýO×¶2Ç?¶ýméOlk™ñÏÖ2ãŸÞ“ÿôœdÿíú7¶þ4÷”ÿtÿméO°»ÌøçÃeÆ?[Orü“<Éú³å­?7—ÿlžý¡ç7ÒtÂ9¦ÓÞó¿voÄóp´T=ï,CÏc‹ãüY†Ý¿ƒç#&Ó³‚â<Äim|ÿ¶³£Ø~¦²ÏÇÐN­âøëÝÆ?áÅÿगIñÇñcÝÏ J㙋ËÛ¿Túùi]èþ) þÜÿ¦9õ©þ¶²ê§ûÇMܯh*‰Ãƒì<´á=û¿¿L§çéÇ/ž›…Ïð~_ÀO{ÎWËåî?$ ̺Œ}Þ(ÈwCY烽m¨.çOè$F÷Ók¸ÿ ï+J†íóŸLûü§rÏÏ åk(¿hÏö:åïñiaqŸµgßÃËÒÇo¾=˜ÔÅ}O¬>Z~éí ñûN¡>ƒŸÿí«k²Ô½?X–¾eôÖÛƒ’}–ì/¡÷¡Úø8ç÷=˼Ò®?Aï§Š+ô¼¹À[b†ôËéL_òMSK߬Cßí{ô}ô(}+'•¾Òwû,ôÓé;œ“¾ˆÏñÔŸÀú[÷>ü}äsëO½i*ê7YýaýÿX°þäiSQÚ¦ÿ-~©`ýcSÒ~ ênÝûÔ2£ä›Û7êÏxë×NsäýXÏ+*d¯¬™S«O‰™Ž>í äýÉ;§À^eŸ{ö= ü|üîÂútêTð“¨3À^îÿ)´ï‰ûŽIžÕãª_Çú·ïû/ì¾Z°þxžö_ýÖߺ÷¿‹×Ÿ1õ'°þ=û±þ'¿Q°þôÌãï¯rØõ”¿Ÿï/µœô?Þú-¨ìÉ³Ë ¨°=ÉÌœZ{’9e2ì à 2—•OÛËì‰yŠC¿ÐÞþúß–8ßfÒÛC&Å>æoOZjϾ_B{ª¦¶=æóGwÚ³}ï¯A§¸=dRÚ“¿ÿJÛöÊj=ðkÐï§¾?þ Éìçóèoþ¤°ý>eJü!Zÿ¾}оÇ+X¿5%õ[¬ÿ<�õ?ñÔ1õÇWêy}äµ:ZÐ~ê3¦Ö~šÚë§æèçþ–%üÕ“¢Ÿ‹óê礴ËÏ£ŸÓýÄx"ðè³Sà_ÈÇžX–è¯.Ü¿F&G>òÓsúÓ³Ö¦§º÷ÜåÌŸ¡þÉdÒ3ƒþŠº÷E¿š"ô¬›ZzF¦X>Ç€_Ò}*éþÇŸ¿=ÔÕwèCì>—DË‘Áô5ñ<žc‘WêâøÐÏŽo«¦$A}Q÷¦µSkO‡§M­ŸæŒÇ8„ã9/LºýQ“ ¤çöýçS9ãafÍ$ÐÏ©¾õfâÏÍàx±î7HÈ$/}ô̱=þ²lî? Ùücí ×£·ì}yYFi{e°š|»aèTç}ˆ½ïÃ÷}ô=ýÞ\€óOþéö*–îâ|¤«“‰Ï•¿_�íÍšû^^f)‹_tå ó§så'û1ÿežücþLÎòi~ãˆ;ÿ˜__2!ÆL߸8ÓWë·wÒúàùM­8_FË3*IÌêƒt¿aå)ÓH â½8žÏåÌÐüͿȓ:æ'+rä7ü*äoóäù!¿‘+?¡å_êÉÄüf®ü-±;_ Å?w~,ÿ²#ƒÕN~K©ëÛ3¶ük$ºXÈo¦ô'¡¼ô±3ÓÛÓ[œ¨$嚟¾ZÜG¡ç¢~‡- ŸT¿â>ÏŸ¿Áø?äܧV’¾=úVß;ØFt½u�ô›T;öѹßʘjŒÛ÷ñ>Æíõ3ËÅ}f~Õ %Žú¸ÓžÿÉ\Qštþ7=ß6aüCm·/ v’ÓK¿Œ^Òù¿™£ã±PûÇ:δ¶ï}xÅ×;?"Ÿ_·éÇÎϵ߫ì}þóÉDù?(^~ìê‰çóæ)¯u ø¾ªäòÔ}O•Ðþ2ÊÛÿHñòÚK/¯uÿ//X~?)^ÞÈŠ’ËÛ¾¿þv•^^ë¾//\zy{”ÀßÑxéô;ðXñòzK/oûŸ/¯¹ôòöìýïâå=³¼ ú=]¼¼ÒËkÝ÷lñò¢åàWB{.+Cß~^¼¼¡ÒËÛs „öÆJ/oûþ_–`ÿ®,½¼}¿.Áþ•^^ë¾J°eàw „ò‚e”·÷O%Ø¿¥¥ówßs%Ø¿rÊûm ö¯ôòZì+Áþ]Q†ü=_‚ý+½¼Ö½)Áþ•^žºÿP öoIô{±ûWzy{ö–P^´Œòöý¡ûwyô+A?†J.æ7-¯ Êþx{øx¹ýÏQ^±óaÙý»qéþŽÎMhµ= \ï3j€³Ü”Àû%X}F°í¶yiM9m•/mß™ÔòÒª9åû†q?s¬âš6è}'†B‚lýPÆÖs.z\Œëר}†&Þ§ ï™ÿnn?ð‹×åüᘈgØý™/¿“ûãyïG•ð $ÇǯP ï 9æû”Âú¼ ô»xn"(è¤ëáL20£¿ëæP Ti¤ô F‚müþlV~Lyéãg²óâi<ôR›’Ûwb<‡÷¹aýægZ÷½Âïçñàs }¯Ä |2t©—ž¯zâ~â ô]ì¦'¼K£g0Öxècß� ¦ª¢-Käñ�×úÕüøääoåÁÀûsý~ÔòùgÖ_2—Ê;ã_ôá+.™Ëïsú×x¿ ’¥•nó´Çã9æö½¯MlÏnhÂÞCHLè}žü~ o£ùkÀÏ—ûÞ_R{éý4æ÷¿ˆõƒº~0ö0QøþîX ð×¹Óþþjz¾~ƺðôcï¢÷m‰ûè{z~ù ÄûeÆ “zLaíÑñ<þPÜÞϯ|$%¯wŽ¡üÆìûÑ-¥‘æoÃûoR8þ«+xÿž`ŸÖµ+רûŸQ~ŠûÒ?Ó9ùùL›‡ŸˆЃß'J÷»$Fžë#(/¹Î/Pãh߇Õ}¯M</?ºÈ¹xÒÎË÷¦–Ó¸üd¨üÄmùÑñ<üºö>qŸ ^ô }“d ¦G'qzŸÞçŠç)dp|}4¡ðõ§æÍ§‚ý`ã»Ü¾€ü…ºvÓûÁ„ü[9éÛ»ÈC߸›¾&£ïRAß’Ö+{õ¿ë??à:¿,©Òõÿçd±BýïMߎëÿ/¼i±¡^tŸ_ÖéLˆöÇýô¼²ùöùÿdØ¿�ÞŸP öžÕ—%æ°ÎÞƒüŸJ´åö‚~À÷Ñä\Kùð«ƒ*ÙÙdŸ‡öñf´÷ #u&YPâxw0Îô›øcº™Tû«LðO&õšU–b´£~ñûLŸ†ó)½ÑMÏ$I“‘¹%DR¡èo^úò™F[%‰éÕ$¤çe{á½y+Ž?&UiJøà}¢"äOéýGÙì¼ot:~÷ó·NY·T_ ÒóѲæ%ímÆŽi¤B»}ÓšÌE•§Ç·N‡÷Q¼/ÉJ,¨22Ê6 ïáFµ¶)òCC̘MS}­2ŒÛp}xì7ðÞ쯘K„–¦‚£-â=ô>¨¯1<¯EýØ· ¾7ñ{SÝKZÓ·—òñÓ´Ïÿ–XfÇôXŠ·~mž®LÒ_Ÿ~Ø7£I[TÙóEðü¸ ºÓ×¢ãõ]}ðÞòeÅxmBik ß3"=¤¬…ü>K¤{ Пʃ^öžçfý œûîè}Nãý*|¼Õˆ*­R}Ã~Ê’ô‹þ\ì?ø-!ˆžVz¿ÌåõE²ë*1~¬Ù÷MiÄüU¯:åI>Ï­œù,I ¯¶û;¼Ÿ±Uèè©jWû«Ô÷}œúrÔ_qŒ÷¹ >ùþ†]\ÿªÈ0Ž7GïåôFKEÿÙ÷…ö} ·Äìû#ÿ\»Ê¡û5 jœý<ø^Ÿm7\íMÉg„ ½êJÒ¨À^&ªFÁŸ0+À¾fýøb²{Ý¿± ïëzèÈ?ºÿåÅÕ^rËþñ‰ö2v±Û^b{Ù}_ zVäüΙØç<!þ^7j›qþÕµ陋D ·\tÓbKAûØkÂ÷T3·=cøëÐT›P¾œñ~¢‰ût°ý)u)¿ŸCÂ?IbÓz|Ãtþ:ÿþ³¦pûâ8?Bûœ랯£÷­1ù· ^y„ÚKZÿó¨ïD]žÿ˜ÐŸ$ —¬û±Ô-Ð~óº¥á™z�ïŸ÷ejIž›ŠÜ×¶Kô—jòU<ßIÖýåí‰ÎùKÑý@ú.îo³ú¹>ÙçKÙ÷m_Â9_*�ïiÃóûc¼üºµaDìºÐÙ$ìKй?ŽHó]*ÄVãß@~©ÿué÷µÅô{²ìËÆWÌþÒüXh@½NØó™ìq´&·=öÚ‰_VâªY{lûBÄþ8˞ϺŰãw´7Ô¾ðûÀ[ïÁõž¯Œ/‘âS2üš¸ïÑE¿5'™~]%ÐÏ:núi‚~—L¿ôa7ý’¯æ¤ßO2ý6•@?³TúQÿf/«"C+o8ö"Dôe`_‡±¿/F¿ÄËnúE^ÉI¿äI¦ß–èGæ¯üéBþ†K–?ý¯núe_ÎI¿m'Š~I%'ý>RŠý;nú‰xˆdK·öØ¿¿ŽÇB‘ç Ó.=�þNñóõOYÿoòï>OøO`¿?ñôŸlÿkÖãK|½L¯ˆ·ªÉPž‡(üjô¾©´�¿hÿ?æôÿ>Ýc_R‡sò÷ÆÃß—|YEøÏD‘ê߯ó úu¬ß˜ù2}9åë¥ô¶|¿¿6|ÏåK[Qª|™/¹ùÿ3ðÊMëŸxìåÛÕ”JÔ& j,ŠÈ÷»ƒñøç8~—Û?ÿÑþã]ó&úS|¿u~ÿi"}ûKé?Ž›¾º ¯¿dúê/zìßK¨¿öùœ™‡ìó9êð7MUÄ[¤yž / àþðŠÝ¡çìroЫæ¿ÿð»|ŠÏñžoy‚xùеl~ÃêK šØþ1®ç©Ë’}ß_Æícð¼YïyN¼ÒãKòØ1rèû`9ú^n¼¬Iñã°/§½Ùybêïö~ÒòèCØWûüƒ³¿&ñ'ÓOùíx.|¥Ý_]b|ŽçOóüÕxŸ{IÞqËCÚ¡<€¿ò§ñ%Á¨cOªF_C{‚òÍìIÔmOŸ·¿²öÉ×_í*¹¿Jzô1ó"Ø;3-ègÑñ¶”%Ò,^O©¸>. øñ`“Mßs&ê[5=/�ä—xéñ’/‘_âšÿ€öSªÆãûŒ‚ñªïbš?9åS~ñûìó!HÀ}>„_;b"þùÆz|ÖŸÜôÓ_{朿AíØ/çü§sÜöå†\çoHù£˜ŸŽG{¼ÚÓ$Çû…ø›ú“›¾$ øññÆs”VÚ?Èú|» ¿<þÚ÷Jö×"/¸éCþ`ûk· ü¢¿ÆðcþZð,wý}’¶WYžSÞCÔ?ãò*ÛËüÒþ˜/þÓe{õÙ°¦+1G¾šPÿcS`¯L¥°½ÂõSUyÄx èoÏ‚<,kלñIÑþ]‘9ìOø4Qž˜?Jó›êH£Ùš&­tü’'éƒÓ ©4{Ð7ÙNªüUCï#¡Ô’{5Ñÿ¹¤a††–ݧéúçÕ3 ýwd ¦ûf°÷Æ'Rо¡Ö¯}¹ øíŒOgà·w<ºžµ—Ž¿>º>xö°UCÿOÇ_Ùùä¶¼)f³û]ˆoø ›ßæï@þRnzW§Ž·ùcŽÔ„þÄEê1Ž·³ú:à‡ö ä)UØß7Êò·ƒÎü³²ü„‡rýWÈ"—~Ÿ!Æ3DoæãÝŽ~ˆöØßÇñûHdÖ­ó3Ùù7.Æóى®£_V´öO ù; ôT²`_(ÿ²8`ÚöŒÚŸà�Ê›Ã?à¦m{C¨½}<‡èê@;÷ר¼":È3Êçgbº¡Ï¸ äÎW$[wqyføe‰Aï“'zhÈþù ùœ¡€|ö.˜ òªàøúvÓžë…÷\Ÿ–6<µ/úÕk�ÿˆað4Ê·‡>c¿[Þ´ƒ?Ðù‚ö;òÚO1¿ÂÚŽ6âùóÔ~f÷¹Ë³ŒÇª“ÑOÍгÞûÛ“ ×ÙSÖŸ%Àî„z-ð?ÿÁÞo‘Äý¯Ýò·K^QMýÑÒü‡ÈoÝø¥Kû—CïÏSŸòèøqÕgyô9‚õ™v¦W¼±û²~ÃÑgÆ“§›>Kú%ôÍíðxõ"^¾–töChGJÆÏ¼pjñ3£á‰ø%_/?rÑÔâGæçÀoìµÒù;Åô³rÑ/R:~ÆÓÏÈE¿Ô«¥ËßãgæÂ/ûJéô›bþ¹ø/?2Åø_„ß°=¾èØÓ éÈÂf ÉÎo[÷:Þ·å#M&®_ÓézѤ² ×c Ö4]ßÓŽëuÆÔùz(×ú¼_Òõ£8ßmß·"ÖÓˆõJÅëo<æú~A×;-Cú”¼ÞibûUåXÛÏë׎«þçÔcnÿ³¼þðñÔÿåcç?¯?\NýÆüÓÌ@ ëÏ8ëÇ4¬®ïâßãy’ô~´ú(YZ1?¢¦p} SWЊBù&Þ¨i ö}i½ÿw<¦&ž{éÆ3Ƕïãñ¡ký•ו试|^eQù]uÌüýùdÈï±×;–úµYUPÔ¬éÑiù4DçáVGÏ1~üÊ Õß<ú<¯Ó©ÏôEO¯ÊÒÒ_“è?X2ýß!ÚOÓ¡„^87­þùÅ儞?Iók­w|d±h¿¢òó'1>¬‹)­"Þìš=Þ.@éAÎò…s‡çh$f)=¤¿è|]ÏʾÇõœ„|ý£¹ýUî“ÁÚ_ã]iÅøðÓûƒb=c†Ø÷O¿[9?­Fq~•Ðõ°³ñ<Ó¨Ó¿4Pÿ£ANç¿ôKáy$†àGR»¥&é3ézíeø÷×F\÷9œò‘$­‚>ô{ŠÅÇg�~®õ—~Zq¿>®§í«Yšâþ>‰Úã£?sµ—•ñ”×õwtÿ¬ëþSˆïàñ#æçò”÷¾/å/]ŸkõuOä'“÷üý±·ó–ŸG~ –’£>¯üÔQù¡ôòeŒ/ &MèxyOúîÅ…<‘®_Iý=-/!o=£xœgÿ†½~½.*Öã²è/ðâö—Ó;ïþ™ͧÿâöz^º~ÝmYkªD‹é·<Ry‡kÆ©y÷säÜ;¶·¬ó{Ã.û!Úë“»©½8†Ü¦³…®OÑó&¼ò>ºÊ¯Bþ%ð¼Z}ðÿ=>óÿÔ¦”ŠçïÕ‹ý†ªCûPŸ¾à4Ö¼„ûϽ£> æÐ‡öðD}âzq)íÕ/ÿ]þÃ)P^Bâà㋵,‹‚=þ$¼üé=ųÞ}¾àOzûïçØ¿0ä‡ÙcÚ¿DÆÇcáèBq~° ý¹s#­oaç{çÎ5É`D·Zt1ÿ—²ùã+©:N/:þ‡ãc|¿ÿ¥ÿ>XLŸÙxÉç§qývêãçßjb|Š/Z¼¨üý††÷¹ê⼺¿* i(Ï_~yü=ÑkYþúd"†å¡?d*Ç€Ÿ=þFØy-¶~š¾¤ß¹¯QwÙgšßGð>lÛK™CéµXŒçÅ}Ò}•öüªÞ Æ/³Aa?5úžöŸÉ´_ýoÆÛÓ+µ§Á/%Mà_þïƒN}´~×þR¤‡÷|ƒžÇ­éö’L¨oâ|¹]>;O¦ï;¶öì LôO{ë¨?¶Ç÷ñ<˜¤ãE¤û¡ã.LÚ/xË“•w¨ôüæö?ÊCù¡xßK}ü¼¤SIÐu>ÈèLÇþÑó;pýq2-æ»Ç*ïðžD÷³*CŠ©“ÑiÂcŸ—¶ía·/ò:ijÍJ<Êøkú‡ëÃG^?“×_+Îûpäúß%xߨ/Ók¿FjÅyô~lˆ7ôT5Ýd©û}ËÓ _wáâO-µWñóE¨?­:ôŸ÷ô瑯`;Æ/ÖžýåT^ª¤óM¼åǦ£fÆp¿ ˜_–üÊ*þyõÇ5‚ô¦òÉåé©Âò„ô/_^¥ò«.X~–?ÅöÁ«¯¢¿¢ç1Ùô°¶àüP)?rã;2mrù‘}u<V§+ñÈÜÈ«‡Æ—“gªsÊ{,Ÿ¼VO”WfŸ‡söׇkœþÛŸ82q<)ìœÿ�]áþ#¹ŽÐóÿëh¼Ië#»Û_jhÈèÑÎösQ{ýí‹€yÿ“ðø;»Câ{ç|¬úfv¾öqOþètg?*æWš‘_ô}ÞýÍ.ÿ¦*}[Àþä¦oW•‡¾-ÂÊMß!àGÏ÷A ÔŠ´‰þ*IŒ—2~—óþj¼Ƥ÷Ï$ñ>jzŸ1‹‰sŸ5Þÿ$ƒ ì¾vÿ1¿ïx¡3Ü�B¨µØóYÌÿÀùrïý2»«Ë»¹·º¼û‘ÖNîýÈ)z¿ ÞœÆûetz¿Lû‘†Ê¹ùÝ¿^,e÷‰³ö(ì>nœÏ - Íþš%ì>q\¯Gð</2·Èz†C¾Èxáû`’ã…ïo~&TÞýͨŸb}â.÷}0ùǃ'A~‡&I~˓ߑ`yòÛ\srå·¹²<ùÕ”ã—ßê Ñ`ž:uéãѱ"÷%iEôc¤¢<ý¨8ý8ö?5Yö?P¦ý”iÿƒ'ÙþûÊÓŸ±I°ÿS)ÿZ‘þ!^Lþ}eÊ¿o ä¿ù¬Z—üGlùOpù¿ò’¹u0¢›’ü_ùWß…ò¯7¤ê©ügDù7zÿV‹Gþ–"ÿ*¥ÇR/$­™-Åä_¥ò´Ô%ÿÑòåOªü}R(ÿ„Ê´€ü“‰ø–(ÿÍÈ_C’ÿ!Gþ¯´åÿÿ£åŸÊGdFˆ›[rÉ¿”€8ò?ä–›>õúœùš,ù?ZžüCþ²ä_=Éòd"¾%úOS&ÿo ÿi‚þô9Z–þÄ0ÿdëϱùOúOê%“t½~´,ÿ)úúѲü'®Ÿ“ì?eJöŸv¿:ßã?¬¿Ùø#úêѲü¯à«G§2þ ç……F=èKk…KøÃ—’ •Üñ‡Jã3—þhý ‹óŽ éOó+åéÏáÃå食'Gº^.OJ‹?ŽONfü}¹LùyJå¿@ÿAå=ÍÙýßDºÿ;<Ÿˆýœæì¡:*¿™>Æ’ý¯æ¿–)ÿ)Sþ_™ ù/=þîúsyòŸ*Aþ«{‘?²€è”Þü¾„ä‚ä7^D~SEä·÷ÏåÉoìÏS ¿%ÅY â“ß/½˜Ë/õçDyQM–_æÿ+ï*,¿ ¯ÿs¨¼ø!z¨¼øa÷_NjüFùõ½Ôq&]”éÃöFíûÝ'ÈïîË‹,⬟ø¿^5HCP+OÐx†Õ§?ø~%ºÞã \Ÿ>¬%ˆÁ÷sUøg“™Ú€°ßâü'ÐŽÞÏMè|œ^DÞEüýËó÷ÛÝù)þ!Ú~F¿¸3¿¬KëSEþ+ÿ‘Iÿl™òŸ-Sþdùÿcyò¯+¹å?\šüë9å€ÌAùoòoRùwü—)NÒ/«H<@ŠèWóËÓ¯ÃÈ­_árôk²ôG?~ýiþCyúsø÷å食'Gº^(O2yúí$êO!ù'Eú£˜ü¿P¦üÿ.·ük“/ÿ„ü¦1§ü‹õ̃â{ GªŽê‹¤”T420ï×Ë)ÿ¿+Sþ[¦üÿá$ËÿÁI’ÿðÿRù?X¦üÈ#ÿá)ÿ—J—ÿ¡c•ÿeÊÿþ2åÿw'Yþ÷M’ÿtåÿ²ÿtpoyú3²7ÿ4ùúsBâƒÏ—§?#Ï—§?ÍN®þ4ÿæÿÇS©?»Ÿ+OzŸûßìΔ9ÿ)sþûù“«?59þW‰ãWo8ÿk÷¯Ê”ÿ_MÂøÖ±­ÿ°œõÚ?qýG)ó×äÿ—eÊÿ/Ë”ÿÌÈ2SúúgË“ÿ´-ÿYYþq~¬´ñ[”w¶?ÏGdóÖH¯A¼ü8Då³üšÅüŸgËô<ù¹üBûJ—ßpdî¿%ý5xÑ:Çý#™¾Ð©ƒ„´¡=çÔÅq¿¾¡¦t“îõÞ}<çïá~W!¸Ÿ4RÂü‘´ß;ÓWuÎ_öþ$~¶Ú(áWdÿwn|ôüTÏk–뫞Êúøùß´¾¬QÙ£ô¯¢ô—î7hÇþ¡w±ó鈨¯ˆ¨ÏÚÏô›íFþ›Þ¦O1âäQ¢·™O+Æ�è~úg‰?rÜøé ¿jº¿9CÄ~%J/ë9¾_Ø=S›}:î×|Å}$TßË‘Ÿi'X~j¦V~âŽüXþ˜ÇÍŸ9Eø3g"Œ×ñ|8‡?É£ã1ŽŸéÁÏ:nüÞZŠ|¯¤ò=PT¾9¿j§–_8/×§à}Wê3*ʨŒhŒg±Ç‡÷‡e¡½‚ïºù‘yÝæ‡UyºGŸýÇÆÍi/¥¿t^mO=/3køFé}]Åú“IªÏÂúŒWŸáŸ=ꋟÀöUBû“PqÕG×;Äq+Þ§TùSõ´¾ôö½•O zºý7yÚ“Gùy)<‰¾¯çûSqÿîÁŸp^JžþÆð¿ÅÝÎ?F{¢úÿ¼¥°÷¼%Y?C¹ôÓrëg±óPÿê­þ¾œõgÊ®¨@ýÞó¦êh}ì¾Øh.~Òó+†Ùù*?›ÈÏ çŸHå5ÿ×׿‡ŸpäÒú/ÐgýWšž_â¬ÿ²HpÖ^Yü¾[ç>æù‘¹]ïEgÕe‰v,ÁÎçñ’/kû»)¤/ôx~¹ÑP øç³Ç ÿçÐûN³äNŸ„’¸‰®/ÂcÔ8~&­O¹äÈ`Òª}ÿZ»3^üéÄõIyîûÊ¿1ˆ÷=HëAƒx>–ÏÐóºX<3ìÿÊYÿÖ_Êú·'Ë\ÿöŸ“»þ-KãO³ôõoO”³þ­Ç—>ZxýÙá'Ê[6úDñõgøI×÷e /Ý‘'I~ G~ºrð£Ð}}ÅÖO§xηϱŸ¾Dý5Üú›Ê©¿¨!:?•%M1Ó¯·�ÿmýÍØú+ék¹ç{ÒW])K_ÛŸ:¹úÚûXyú{¬<}yòäêëÈ#“«¯±GËÓ×ð£S«¯#žx}ŽâùmLtÿ©¿d.O2ñ<‘`¯¹â’¹ãnÐ_{üóé>ø¶5á·@y¦8Ÿ=£,>2XOúD|B|d[Ìîé£÷‡Øý-ÄG±›¯fiø¾:¦Ì.ï|_c…}¾/ÞÿMôÙ#uò}L8÷qÚçoÅñü­¸2{Dsð¯‹9üéÝOåãcÛO³ö3ú¹îOpü <߬Ð~ä?lßǤÛž¿§ãË ¿8?Æú‡~"êÇûÜ[õbúÝõ*Oq—~Çòë÷3Šò'G¿#x?AÏ÷Fý¦ô Åòχ<󣉸\Ï«:ãÁwsû-Ë—r)Î7¥gùP¾’¬~ê?3O ÅR,¾(äO3}Oç±ç¼ýæ]¼þ ÈïÈD{Ÿ÷<*•ÊkÆÆ¿žÊ_ŠÚ‡Ý{ÀOZ+Ð~À{*ÏU1vŸ¸°Õ¼?ý_]»4þ<š‹žùíI¢˜=9ê¾ÀPˆQˆþ}Hï8¥/ÒÇêÛVÃí_ÜæG5=ïŽûß ÿuº#ÿã(??‘ÒÇÀûì«Úq?SJ•ú÷Åîþ=sYýi²=0ÝöÀsÞwˆžÿÊõ} äEº„dޏî+¢úúíŒÿÿÀ¥zÎþHÊ?€ùÿµàyVÒùª·<6Ý}¾ªZø|Õ¡Çø’ÙW¡Oì~“ÞŸl½^¼ÿ 7£ÿ™Î¿´êQ¿LµÕ¤þg Ï·ÖQ~2~P@O"ÎGë'þjÏ#Í[_ÌRæ}1·ÎÏqŸ¼Gl;ØOzž&”ßàÒßd¢A)£| ßRzÞ®ŸzËÓg•S^0ý|u<?­šÒÃôž‡L.TyºœñШ{<t üuÇéÓT¢üùyÊ'¤påµ|àxEÎó‡Eÿ¬GtÖ~I^øù”¶ÿÇü_z~`ÞùÄ`2­NçãøŒ¡,åø$п“õ3öïŽýBú"=@¿ NßL_uŒë÷ª„?”:öñ:~'É_ëO©:iõ¥ŽŽ_1Q^Œ†Úrä/,É úÊ3Ë’çbü+ÖÞ:Î_ôÇãG]ö‡žû³íñyû#õ—ᇎÚç-ÒòŽe?Œ~F-ÚqÞs¨ã_zÞ2¡çßJùw?t´ì󕋦cW†ÍÀ0=Ï7ÀÎÿÇøëOSÿ=‰þd–îg¬€þ´~€ÚC¶wä?(>×�>1ÄÇ”ðÙ]*>Ñù3\ë' Z?øßðk-ÿ †ÆU4þçñ>öO¤ß‰.;2h¯'â÷sV“Ù©ºÜþ O¯°÷ß^vdÐ{^®ìï+Žÿ¸ÀSÞðwÆ|öýZ†ðo\ãßèŸ4;çŒv"õߦ߾,µDÑ ÒþÈ/ß'Fûkß„“Ÿá— Ðóª—Ü:/þê?Þøcz#ëÏãoU4#–¤ßgjâ>¶ôÊím&¿ï´1ªsû¬³NM6ϯIú²¸^ú ÃÈ#÷Õr™ú‰†ªã°?,þ¢þŽåçˆfóûÉÁï9ñ´ð'Bÿº¿ þgk¡øÀtøkûªA0¾ËþpñXðßʋǞy¨¼x ûɌDz¯ãxò?ëÓGoº=Ë?Þ)'›0Þ2Á¿}f¤<ÿv·;?ó_)ÿ>%¯\@ƒ(q6þw7®gDÿg‚>†êãî©|³òÂì|U/2}§÷øô#Ð?{ã'¨Zã˜*ãÃýuZ_±ón™¾ÓúMÔÇ‚ñ-ßÿ‘ÇcUQ>Ãü“¾é x|PŽ=°ã“Üö&&Ň=þþjj2žõFï-1¾íñe^óÜŠñX\9ôþ3‡·ï -/:>ýv9ñt/ùš{ü-UJüb´œ†ë·øú-Wê?ëL_ú7{ýÐì˜ç‡¯Œ¡ü$Øý48þ­#¿x? £Ÿ¡Šó”ë"Rüø-_@y¤ógôûê.ÿCyù›¹Ò_73âö=øåø“¬|/˜ß3û7Lý%l¿¼ÞÇ‹«iû3Ö…3-^µ�ß„J lX}àˆ¿¿ôÐÙ|'=o¸™Þ¤UÌϨx?žŸChý uè’¦þo/kÿ…áV«í<Å,÷ülàÿRSZßú¸íÁ ¯¿:™ºt}Ñû*4ô× æµícT²WÉö‘Åkì¾&|¯8ùùýO5öýOÑ£Gc‚þv¼Vü|þ•¼>*jõmL=ð]v~³=óMêĿރÿý Þ¿ «ß9?ÛÄósüd-Iñþ¢¤Ñö¾Ji…oÁ?Že•óã~¼¿&ˆ÷Û ÚLg=shH¡ñiH~ô‚¾™Îý }3ÔþPZº¯áHï–ÞÏ„tVzªÚ_7b§}Z½è¸ä™Í_i¤FáþS³ ñeÂë…+ÑÞu¨ÿÌÄ)÷€¼¿ì‹™LÞMß3 ï»_oöÚß[ ûý>ú½ù¥ÉÈ <yŠû~ÌŸ”Îï«ýõÍsˆˆ·z�ýi·íA:Ð ôâø@|êþŠ9«ºŒõ¤|¾ÄGõê ÒûÊh}é Ú·ˆj¯—Åò_Ôû¼ó8±é›÷>ÍÜõñûc°>ð¿+ç'ü;9þõ´=éÖ¯C=\)æ#Û!mìø�êkÂÕ¾¢ã/6=-¥=Ðú²CøóÁQ´Oà Â>„¿ ô%úJîO¦ƒØ''?9|?Ús"ø™!õ{¡?]ŒûÿüdÔGýEÚÿš½ÑÈÙrÿÓ+î}\#æk"„´0þÇ_Wædè~º>d‡<^ñÜ{lÿt¤~o(±J¤Ó•Øÿ¤RuÄŽÜMë‡ò>"ÊOØó· ÿ/ > Òþ#×xDÁñ7>QrÕŽyëÀ¯Š¨”gaÿW)æßLEùáœnÞ^g¾†°û’¯«›/•ç±/Ì>“Æ/#óåEŠJFã  Š!§�>fÔYï¿R¾šË{2M &t&-?Èã»vÌÿ ]”í†ò{£ºÛ¿ï¹¿›®¿GÿÀçÓï;Öu þ,ý]º·v]ßñå'‰ð/¸¾ŽÙöÀÏîÛŠËý/.þ@}±\õáýâ’ý™´úš»¡üøÿÉž Юîyê²j%Øû=£}ŽôŸÝ åÇÿìî¯5v=¿Jô×fÊŸüyÚ³1!¯ägxÞ1%þ—ãÿ€>¯ÁõQ¨¯ÁaìÔµP~ D_é`Þ§§.¶Ç§Pÿ ·ýeôJúu쿞rÛëQ°®ñXÈÏÞ³õøô¾ñ(‰ï€þï{`ÏpüäEC¹FÐ/öµ£xÐ3îÁõa_£ýs\Ø—åý7Uóù­ ŠþCO>^ôŒò_ ½bý£GÝW¾­bþð³_&–oØ'ìuÖ¸h&ðÿÃÑ~Œ÷šå+y|2¢bÿcd#Í”?uÁÌ õ§yí;1~÷á }/¢/š¤/Úëîøa ýù¨Š÷Kø- ?´=USAßãþ€^]» ók`ôMîûÙîŸÈ/ÙÿÅù¹ºæ=„4%ú«oZLÚö8kbš÷ÿèßú„úCíОèoT¥)å·ç£ê§ìñiÀOÜ?üôep>:¢ºö̤_õrJ?¯ý“õ­y7ÆçýnÆö3{¤ýÓWáŸ@ÿÙnÏïÝïÈÒë) òHFïwËßÈýÇ Irßï…þù–káÐ_}ôzíœþuänWÿÊýÕ–!xýƒío)özì_c–bBÿJ ìom{¯`ßOuèæö~ú‡1¼O˜ùgè¯1f¬T“y3pG·û+Fï ¦ëcØŸ¥}t}í‹ÂóÊWÂçÝyý·‘ ûo#w ~ Ûþ£ç˜Úõ¥þ:þÞå·~äà}Gíû¥þ™›¨Äû©¢H/îOgézz±¾5ñì/{Ó³ª„¿3ú žÂ6nO ½ß*ºç</íóvMEâ[“ÙW¤÷€þ©ð̬µðôVhÇø™| çû s{NÚô¡:³w>÷—ªÉðÞG攄¯,χïsË3Ò¯˜<‹ñw[ž=ú8!þ‰Î›áZgØëï,¶þŽ®'°î¦ë‘þuïÞÆuˆÞ™I€ é,% ÓCŠ–)GN`Çé5L<¤– QU—M]ïXqS5›—ޤm ¹©3¤iJÕ-”ºúÕq¡Úï+“—÷>´õûVùšnG²·+7NËl³û”÷œf(ë§ål!ÙN$[ß9çν3�’r¨|ÿejwÎ=÷Üóûêgè'ÿ%å×%¨hXŸ+ôçõ-ì“yú³Ãû!ý]A½‚𗤙Ñ[ óc°>¢·ÊñU‰¡}UôêüE _±”ú»R–¹©ܧÁËóýÇA~†õ„#à¯P~…Y—ïCùuÿs&æ×%›ùƒ ê¡?x“Ô¿Ÿ ýÁO,Á|ø™+ó›±¼þàÖŸó±Eü_~eù9vˆß:ÿ«È9õlËó“ßîÃóÃxϸŒUT8o/Ì«ªbÿQÏáÿýÊòcüEòc°Ÿ_T?°áÜN=ç#þ€k²åºøãks"ÿAâ«' gèÙZ$¤¸ÿÊüëyx_%ü…øçß+#þXþ™ùçÍ÷ z¯F~¤÷ubüóDr³ôgU1¿"Kó²å|d®üÅ©?ñ;žWÝüû#'—ðýÒÓWáûƒØ¿ÖÁø«Kß§úq—ÏÛÕ©Þ6ŒñïßÍ7¸â~ë§×váüaø¾ìŸüÇ“ iÿ–Œ‡³ÒWA~祿Æ`èoÌb~‚Ï>¤ÿN ÿ¿Ãýÿ]ùrÒº2ÿ?ìùc÷gDöWnµ¿êî¬èÝô¿þÈj÷пü}4MþíßFyþC_ÿ ã»(¿,õ‡_ØX‰ýÝ…½i6™9bé’üù—ýí¬ÖÇáýJ;Ê?ª)£þDþÕÄvšw?çã /Z9犜/˜eF`¯²‰¿…ïqÿ‰¾Ï‘ýòÏòþ…¿‹V(?‘ß99Œo xt&ˆzãðw*ü[s0_tFÙ¢ë•V¬à÷Å'ì¥qçµ-I‡×žmvŠçœ/sÊÁ÷Sž’üu;ÐW‚ÖÓ­—¨?&fN³F”ß-mý'Âõ7Ð üþ"ý:L¬ïϲ¼› ù®wнèoüÛm‡ÿèiµô€ví@mä!ÿãþ¶&øžÍ´¾+òRÌøA{µ“ð_ùÐó£¨/Ðï Æ|†/ÂßÙ?aq×ó!“ÎT&e4ç×>ʿ쓀›ï‡F™êïÉâ Á?¤,”Ž3„ö3c‚õ¦ÍîKõžˆ?ø¾ó q~Eò_8CÒ߯l°ó’® ôYcV Ÿ4~_æ'd£ßÇü„sÔyžj°mÞßg Ã_æ_ ®ïþ-­†ò«èÒý ë%Ü)Gø›Ì\æ± Ùþûç•¿ ò'¼³ò<΂ýr ðß‘ÕÜ ï+ÊyuL×ô̬kÉóðÁ†3_ŽAúÇ{Êæù½óáe½aïïÔ�Ãuõ‡§Bùõrƒ~aš‹ëÓû>íá×+åGþ§ >áïGX‡+è­ªÉüñÑþºO¢}(×3ÜÀ_h½è}cF+øk ðÇþ#Š®ëáz\þÚì5ì§Dø[ ŸW]8?ý…IoÄéö!½7¼?=ô­k¸žwîÙÈ<Þ¤3”ÁûÓŸjMïŽÏáiv~sõñÒøWÄþõƒÿ‚ùψÏ믋_™ÈÇc%œŒÏËÅ—¯Áz¾Ê q°——Z? úàç–ß­6äC×Pß3¤½Àþ£´h^ƒ 9OþðWH_ƒï•5ÌÇúÝEì…©¯¾ZŸêµ¶â*ÖgËb/¸˜?Rdb¾°gõü‡øC=AòówXßý ZÛ[î¥úzÄ_ô~Mý‡ºýDñê·‘÷·ÿ‡¥ÜÇzzOIz?ƒùóøkœø‘>Œü®í¿ò4èW ó{Aÿð'‘_|ª‚¿­6̧æûõ@^ùÃR^5ÏG›Y5? ž÷>.òÕày’èß>´ôÙ\‰ÍóÏ%‹e”?E³ßŠœ'Sð]¶à>Û+ñ>[…<¯»¿l‡U]qžû³?¯ï~ã9§Ïþ-ïª$¿|ç¥à÷=ªß&ÿÎ'@ß4§õÇÖ?öÆßª3ÊvÖWÕD~Â…ÃÊv~>’/ü{eMô£èñ"ý9ÿ¤Ž ô?5Úp¬‡ðVV¢¾g¶Ýƒòî€Ì1G‰©Ìþñú§x>òKâùÒwÃüVÓü|ûgk¯±Cš ç}ºèݸîè,ÉoÔ ^ï =Ôé=?›Uܤ§c3¡ü|ýߕ߇ûk¤¹<åïYÿ™Yù½ÓGxüÙï¼öCS¿>>¯h˜ï{äÐ\€’ÿü¼¹¼àòÿÇ ù/çÁž$ý£Ð‰ñMÔ`ÿÆø^Ý~@?ƒïyo�|AQÍ’ø#þ©N…ü-Ì/ùÃË!<þ\øg¦@.ìÌüWnÏûñk\:0㿳ò¥¿[ÿK©!FÞÂO�ÞéKÈ~žÀï�}…üñ¹_xÌ2Пû釀Έj2~øsÝ¡ËÃóè ø‹ôOùK’èo§hvm§¿‹6 úÐþþå‹ÕÒ÷fBz8S}zV‘ñVx~èKŽ8Ï3ˆÏÈù'M#Óþf é÷ÔGNã~˳²~ítv½ ßçÁú§1¿ž‹ó8òcŸbÉxdÀž­çD3oÔÓ‡ÿ¦´ïIþ"½ƒ=±-­Gè…ø©÷¿ñ>q}©ÈŸ7èîPЄûËKöÿÆKÿ5ég ×®çïN˜ÂÒk4ÿéÔé%ÓçᤤÏS[Ñgõ ÀÑ'ç/d/ú(B|ô…ø"új¼OqâŸLøýÊ€¾½Ømðþ‘ð¾ô-ýuç÷]^øþŸk¸ÿoˆûTwÿûðó4ºÿ¹%ÜÿÁ/-¿ƒ!~Ë-ñ[xðërü–ø:g¿d?y¿ùX+ŠûÄïgÄ~ ø ì/Ô‰¿Vý½Þ®Àø1âWÞW®ZÚsâ}—óËrð7È›~áßÃß«®…üúÏŸd–¯|qæ#À÷²ò¼“Dÿ­ýé ú^‚Ÿ}ñáWliö_­áüÏý,ðY¹jøxq|2¶°ª¿é~½Ë? ü¸WÞ®2~*—~ø±¯ýüþÕÅ{ñgãêáç W?öÛ? üXWï~]eú±Þ õ‰r  üýA-1c,ȯ9Øuø™¿_°7Büh´?’ï¾RÌNFÞ÷H¿*Öãðqc²ìÅàïN‡åAÿ&ý„ôÂÇWîðw~ÓïÂGöè+EÄ ýñ¤ï…úÓBú?åD믤?qû»‹ò`?¾ÔÇÁ~'ú’ôĤ>Äù+ý3œ~êõ)ô÷Ðír=ªeKÒÇ­Óõòºðj==ðï¡þÛZ¿ èaÑûSn±¿:ÿË8à/´¿¸¾=¥ô×ÝÂ?ŸwÇB}q¨Éy_º~Ø.õÃø[­ôÃìé:~Ð{sü\)‘þùè«èrÓG¢öH£=ÚG\ŸæõíK²§§Ü%ã«Ò&ñõ­ ­ðÅN |U´oe ²§;{XìW%ú%ûßïì‡ëì©…àÍgn©ðŽ…ð–[Â[8Iõ½Ï;(ìo„e‚û{ÅöJqrÉøÕCxl oå„ôWÙ€ecüM#…ÎñYd®¨oæôxþúÜð§/ä?,óüá-ÈÅ~ÞzÑž|Jà§Äî=|OÝÍÜÎbžGü㇉_,ÚÿAÄSë5F[ñ÷D?sŒõ]¿EýöRœÿÑûöÌg`÷Ð ?$ÿ“o‹zݹ¹»…ÿöç®4”IÕÅß3îÀýÌ([É‘5h=oYî÷¿ñ³xý §±./p?§)^Ñ$?üswFåSÀo"ùG½b?vÄ¿‰ù*ãÒŸñWÌ„ù2nÏßòeªÔ*æË¼,æ³ü1o̬0ü0¾ñ¯æÇçO|Ï�û(x.ãZ)Ô—”=a‡†zZØ_»a]?Å›=Œ¿%‹o^R¼ öJ½|,ã}›9xîá›­‰—A>uÄX9àã+é/Ôù‹)~Áã5÷˜²™ò^Æ+øy”e¼~áùµ»´Ê±ÿÀ“,Q¼@‘üǵú1¿çcØÿáØdÂÀûVPiʇlèÏé0°þ,<ÿâï_ãþ/_Ê_uFþWMb>&é—t~\нÄ}ªo å5é UmŠT:ç׃r}!¨Ï”ý¹‚xÚµ9Q*ïÓÒüu~ðÈCýuÈÿÜw3Þ!ù~*kÊûüóîÄ åoiå> é¿ûâÂôi#=P¾ýÍùø óåC~òûÔFÆ›H äAõ·€¿Ãúãr?DÿUÍ’öGe5ö³ÉÁþéû3¸~Ý÷Âþo¸Çkôgf¢ç£Gâîq¹?…òaäþÂx®ˆ_Ã~ýñÇà>’¾SiªùÝ…÷«çúwp~6í—çÃH~¥:•1ý_DþÚaìgU!ÿ®YDý±a ñ\Ø_¿Øܧ|¼aGÐþ±T)ïÿóRöw`‘ýeI|âþ,Ê—iA/V“õq ­?óJ=úã ÐcÓóÙµðúÎ+B߈¬OúFE«×Ï«hÏYúy§¯Œú€ýQ_÷Ï’~×ìûÅ?‡ó£zØðü¢ö“þ:Ƈ½ºøpgÈOýñï¼ýÕ¤cHùÈÏ[ä“€Ð:*äIS{ˆÕŽ/üÜ9µðóêéEž¿¾ðsï…ŸÞœÿ<y‰§„ñɆ¿Ó3{†Ü¶ª6¯èçì%?¯|ùÉzÊçz%|Ìod:Ë{X´H~6˜êú¾õ:h$#q»¾?=ý^£õYP5¿Oãz¢~¤ÆiåÛAÿŒ+Ÿyª¿f³‹ßQŽäxq²‡±Ÿ)ÂÏɕջR?iûI ñMöœ'ù¢M²}€ôeZß̼ÓõyþD ëƒ|Yÿ ëÒ*·“~ ë×9qÍf{÷çx™è}|ûÙ8•Âz¬ÆýÒª·-ß~ö6ÙOá¶p?'¯ÙìÆ˲Ÿ|«ý”—q?å&ûÉFösüÚÍ•Iì_ñÓïgS«ý°eÜÏÍè-+÷£¼v³7™\–ýŒ¶Úž]¾ýL5ÙOõýá~Ž¥6û±®eÙÏ=­öã¾ùöóT“ýïÐ[j3{työShµŸ™÷-ß~*Möã¼/Bo©ÍFÛ5˲Ÿ-­öc,ã~žm²ïÖð|Ž]·Ùztyö³µÕ~œ[—o?ÓMö£‡ûÑN\·Ùkï^–ý|¶Õ~¼uË·Ÿ#Möc¯‹ÈŸë6W[žý|¦%[Æý|¯{ox>ÇWm¶Û¯]–ý|º¥~°Œûy©™~9Ÿ«6».~0Ö’ÞÞ»ûÁõI¿õA³=ÖCýHjšôö^áoóÙdÖ0JÿôÞ§ìÐë_ºÙSۮϓ~/²L"ð;“óò[)¾7£.9_‰ñzÕ粃~ǘ2ÙYÃþýÑ÷ý_‹öSZ?•[–KMÎÛº%Â/ß½Ùz¬sYÎûÁVû©-Ë~xÿ„fç]¾¥åyOü<œ÷ÌÚ«{ÞÎÚˆ<y÷fã±ä²œ÷¶Vûñ—å¼·µ:ow°õýnÿ98oïæ«{ÞöÍýîÝ›ýG»–å¼ïoI¿k–å¼ïouÞΚÖ÷ûçἫ7]Ýó.Üáç=›Ùc×,ËyÛ-éw`YÎÛn)¿Zßƒó®¬¾Êò{uÄíÙì=Ú½,ç}_Kúí_–ó¾¯Õyú[ßó.÷]ÝóÎöEøyÏæÊ£×.Ëy²%ýËrÞŸluÞ–Ñú~Ç2WrÞÖÒë ‚óži}Þ¿µÔóvo¼ºçmÜñŸ½ì±ø²Ø—­é·÷êÚcÙÞVçm=reçí\ñy¿Üú¼»á¼··ô÷ܰ|ç=ÓÌßsCÄŸðžÍö£‰e9ï{[ÒofYÎûÞºó¦õ-:o#Óê¼­íŠÎÛ½âóö³ƒ´~çy¬7¬;ï çý¹Vø±ß³|çýr“ófï¡~ˆ°>{áøÁ{ŒøK|àý$®/æ57âãJ¿Ïã·t>„„c©ú1eòÄ{6ñøƒ¯Æ#ùô{ù=W³Þ]w~¬ôíG¾ªžWîñÃq'>Éóg®`žÈ<xl‚ç$Âó⇞^žê»šÂc//<.Âó±6¥o/ Ñžû—ž Â;ðÄÿqaxÊ«šÂ³myáñžÜIÄÏá;„‡5‡çÁå…Ç'ú9v#àçÛ Ãã¬l ÏØòÂÃ4¤Ÿã�Oé; Ãã_מ{—á‰Dx^ܰ <…xâ‡7< ðl\^x,íZ8¯ã}HÏ Ãã­h€ç;On9á‰;6ÂóÂI„ç>öt§¾_íóõ£ðdáyñcϦeæ?Oîøj€ç¥-ˆŸJª)<ËŠà?„ŸÏỄGo„矮<áçØMx^ ÃãêMï×2Ããóóº ïûÂðÔ®mÀÏKw#~òËKÏ—§7mö•ÄåÎø¶så››Ó³ÝÏ‹›žÞeæ‡$¿Žß øyió‚ø™én</‚gt¹ùé?7óû¾ þÓ�Oü ž{–›ÿ<½Ϋ˄óêý[œWõšFþüKWá¼\Âϱµ‹ßw£ŽŸÂróÄÏñµ‹ÓO¹«)~–äé „ç;¿´°þÓÇϖ奟Ë÷µ@?×,L?NògA?LM¡<EúyñWÖ:›âgëróC„çØ{7ûã‹à§ÐÏUÁ…ð䎿åûÂøñMñóÙeæ?Ï ÀÒ½0~²ð\þƒðÄN~Jÿ´0~*ñ¦øùÌòâ§Bð»ègüèð\üxœžoE{ç×Ö:šâçÓËm!<Ço]œ?×Ú›âg™áaZ å;âçŸÆ=žótÔ>U~wå²ÈÓÈ?°þ‚úO[Sx¶-/<„gòØûQÿY«9<öòÂã!<¹ãïGËÂðTcMá¹yáñžØ ÄÏw†ÇhÏ}Ë S¯Cû ñsøñýue­)<Ÿ\^x „'wáù§…ñÚÃ3¶¼ðXOìØm¨o|jaÿÚž{—›Î àñÿ‡…áñ•пܪ>¢ù¼*C¥y ¾Ìk°i^ˆøNXŒEãhït øÚk¢¾'9,ê{~·ØäÙ8|q€ÃW úbœ¯\{äø°¿6›lœ§|Ÿw–3¾¸ÿÁˆÇY1úýÕ4ç˜Fówv²É÷Žàücr·uÂþ ÂþÁy;×%°‘ì+)žðÃGä|„¬o-Gê[_cbÞÂç?r2Éë9ç©ßõ¿SX¾)¼©¸ö_¿“æ1Œ`ÿ‰¥~/ðÇÇN^ÿ×XÏ“8ßwîI^Å¿ßð¼ñý$‡?¬®EêË�>^OÄÏÇ£úۣ绹¶áXpëEçµTçD}›<víæàüÃçväùÉ&Ï­ðyîD“çFøü…c©ùÏYô÷Mžû—ÃߟlòÜ ŸçŽ_7ÿy%òûMž»‘ß[Õdÿ4?œÇÛ°ßdü¥O=­ÆG^Ÿº™ê‹@ÉÌ;¿¥ÝϬ¼Ÿ=ò~îÔì§z~V¬´P7üÔ8߇âWÈŸü]™Ô˜º1¨wò”™+ž—Ì£%ú¡ç‘ú)ÿí¹»~N&æã§øsõô÷¶Ägìd|ê—Âó8Þäyíbäù{æ?Ÿ¹¡×&Ï«‘ç'{æ?/_ŒÐ[“çNø<v¼ÉóB¾“ïžÿ<ùþ‰&ÏõÈ÷7y^ü±,þíÐß <Ogq~ºô£dF¢°ÿÜ_Üìó h~‹ŒÿÒü×p=šçòÎèæŠçÖWÆyš òQ¥úÀÊ¡›7{Ê.vÅò¯Oôoµ<‰®o¨bþ_uâäæÍnÛ‹Ï< ÷OÄùýzGß«†û¡y ü{UE΃´Ìï0~îÇ`Üç‚’ëcú`?ê‰OlvýîÇȨ?õ~ø<J<_¾>/ÚÌ·üEõíG à1 N?)Yß úlˆ/¶¾E¼»‘ßE×Cüpú¤xzHÿÞ8°ºÃZEö¿ñ£Ñ÷ŸžâÄæÍÿ öÓržnô÷ãM~ߨß#ð3œo‚÷8÷Yꟙ7ó”Ô/è>à¼$çw'lÌ™QKÁù$©¿H´_ìÂó¹ëö»Ó\´Þ¶¿©¿ÚÌå¹M§¬˜œï1_Í-¡þ8Ý×å?Š9úÜÔmÌÙ8?ùÍ1b׌–4/$BßWž/2"ëyiÞ²Bó™ñ>UBú~Çùé`êr“úpVý:ìߤï›Ý;#óIè9è�b^A8¯$aù€èï#úÏRæ›,ö7á_Ÿh_“·Âù-„o/œçÍí:_êã¨&òŸ_žÄ×’èóQ�ÿý/}^KãßÙ[…üòÇãm“ ëÓI~|>¹éî£yò$ÏéýXŸuô¤Ü+¾ï`ψü-~ò‘þÓ&öÓ°7´z¾ýñ9Îs¥ûÏù_¤ßòsµÈòÁü3'zŠ%7ŒN¸ï4<áÊúyÿ¬“wá{F°¿%á¾/ÖÞgû%5ãq‚·¦+ºÅç·ém9ØŸ"úW'¨Ÿ‡w|Ü ìž Ñ’áÑ%<§iÞH0ÿ¶¿”‘þXz=Í£,ÔÙ›½>.æDðŠÎÇ«;Ÿæý­h}œG¼!ÊϦ͖ßó&Õùß+š4ôÔ··›4 /ù½ ÷§ÞáýMŸzrè?Ú¿!ôo€w_bq]ý}øÛûÅëG\êßpï‡CþË3Þ_‰}Äî�Ä.E?Àþ …¾££}€ëÒ²I¡¿9Ïã|ÊñßœLP>ë/]ÿ¨FæEžÇyÓ‡˜È—RVÌ7uÔÕ(‡A-?B~ ðq ؆Žö‹§YǬVá÷¥ J_ö‘g�ÞÁ_ y›÷ós­8úKâ*È“™mÊ€e<ú1òç�¿(oxåõôÛ`žlE±â`?%i}f eW{ã»æ÷·Xt=—ÁùÕÜJ@_p`?:Î[%ü$˜Ñ‹ó?e˜p¾3{?Îw¥zµØô:ŽÏT¼] î+3ßOø¸GÎ%|«c‚¾Óüù¶úçÅRÁïÏ¿æqn)§üoG{ëGgP_dGè¹±sŠ?÷”‘¶ÉÎ,ûìã·±ìzÆã@/÷�¿ØÐÁòLÝýÛ Î=%ùÇ˸ŸÃÙgžOá|ör¯˜çmEüipžnÛö_þ®~2 o+NóåüOö÷jνòó¤üaõ%œWÆñçià<]VDz¤ûP{è ³8¼qö°ü¾£L^ÇŒûËßUU·m|êæÅÔcB|Òfb”¹ èåú*¼ÿ‚ üœÖ·´§ªý>ØŸSe–ÿ&<ON+Ža?Ï� ú=¼Ìåï[Ïü-³¼Y[ô#,h‡ƒç©´šazáÿØðø�Íoÿ4®÷=ÙóÃóyòétÊw¼`(_tÝ.™Ý»~YÈü1O$†óG3ú<(ø¼cg)ü}ð_ýØ$ÿÕùy þovÓÜ6¶A#ú˜éz÷ì_xô÷Iêçål7}ÿ,{ù~Я¶¿û­vinЛóø7gQÿõí¤ËF÷­¾v þÑçß³cæ¿ß¥9ð½Tp¾Ø¿mê}bÞ¬]2ºw÷ø¯õl0Ï)•ÕÅþÞ�þŠÑõ:­ç÷Ÿ[8o¼Çy@ʱ[‘~.¦'z©½ª«ù€~4/ð¤UÁÙ©Ûpž ÷—Âû.¾ôþY€ç˺è?=m<÷õÀÿ‡ôÓè? 噫•áþ¤Uobƒ°ß}/¸ÏRÿ3&Oüò|‘÷7Ì>µç8>’4¯Í6$½øD|WÛØÒÏãëgÎýßoÔ*ˆÿìhFÌŸ)K|ÚÞsÝg4ÞO¹=½_yi+“E«Wí‹öO%ødÿa~~…ØSYÃŽðþ-øbæ­4OZÜWñì7v²É~O~ôp¿\ÞÒï­X%¸ï|ÿÙÉ65¯¹1ìO¤Þ›Ö³±®Ûþ<¨¤õÖÐÿ øö¯ çñžBýÀ‹øÏ×Ùƒ!Ixèo®©Ç¿$ùEëûÿù¡»®œßôý-ÔÏ#üçOßúŒä_ ÿ¿=·)ž>zîk½•Þ¸´W5ÿ1Uôä÷AÙ/éOGõ'ÐOäüP¤·ž¸"í¥óƒ çc‚½ ý¾"ëó¿ãëæ¯¯~k‡þ®Íh_öÏü6±›}!xŸð›ÒÕû‚ï±k.ó«#úN„ÿO­Åû(ñÁ}è>¡•¹ø³¯®ôé˜c¨y þ$þæ;ß øc'É;Ÿ½ õyåÁtÊÛ üß;¾BqŒ±à¼Î#ýP=[¿~‡üv§Vy‹ômÁoï··þ~ìúRÕðfþê†CIvê~„ï¥@žüe—:{Í.sx4ç|}J÷­¹<ðWo'þ¿ÿt~x¾3z?à…þ÷à÷Ñõl\/ä—,~ЧžWû™ÇûAÊæNþÒfõUW6 £~âÇâêd”~¶ÖÑ·ýÏ×̧GÁO~óžyü¢¸6ŒŸ <.Î/�úý7œ~íÒá¿xZ¥õ žyôl®]œ?ûAò‡¨¼±êïÓôMWÎo8¿µ^ }ô·ñ[+¦ü,°ï$ÿÕ#ú'âøÑ¹oðýVþÎïâô·}ýçU6òûd±RÇï£ß3ð{óß¿Qkñ~íB³÷½[­ï5}ßímµ~¹éûFË÷í¦ïû-ßÏ6}ßj¹_Öô}»å~gÎ7{Ÿµ|¿ïÇÍÙ׿~³«ÔñKN߃«Åý ù}"äçtŸ¢÷Ç\Sh>Aä9É⯀žö¯«|ú"öŸ´ÆÀþ{Ühc|Òµø@R‚ý[–þÃJÌ¥ü+ÊFæy7(}W`¯NñyÆö«}üÒÞd…îÚ{V»ö#~§þrþœô Oél ø•;áö~JÎpµjgd¾<‹òw>ïÒU€‰fS9Ö—¥þÄjîsÉ{àÎÕå ô—|÷ÇÕõêü]yì×DEñï6û‰ã¼Yëç•ΙtŠòy³y¾kBÌ£Cök…ï[õþfy™©U¬æ³Y½rþ$—ÿÛ°ßú³¡¦·c}åЉ_ÛLú5‡7¤‡”Üï.­€ÏM¥.a~üç³ÿéÂË11À_…æÆØܯ­8¿°Þ¿Åv[õüË•ó fc¿çö5y/‡øœFxh¸ñ(à?^FüZ!ýM)°ŸÈ¼7Çûõ òA,NSl $îÇÇLSÿíñ[iGû¨Œõ$µ¢‰ö£q—Ä“µ¡€žz*‘ùñ /§ù÷ž�û»î¯6¥€>çt>ç¡÷C+—掜'ÁàßÄù6ö?w˜¨w1T¸oj|xDé+h•à{8íáI‰?þ}QÏxÅõ‹Y¹?þ{:¯ºó};<Ÿàþ--žîâóVÎÇÄýZÔ¾Eÿú‡ÞaÿÒÅá72JäïÅüµqâáù$ʲþÈmϽþù÷ÌΡ=ôØzâWšMý_1ßÄÐŽÿÊfû]Ào:+ßû%ü¾Ú@Ÿ=Óÿí{?ˆÜ_}8‡û)‰ý3ÑùÙ¿8¿Újg‚õ‹¨ïø‘x›WïVtÎß%v/Îçx.kxx?⮕ügþY‡¹ '—ÃÔ~6ö ÏÊzáÍ#-hU8¿ü€„ëíùûÁ|P¸¯lUèÿ6Ñ_ÆxP‘‰|}>¿È©`<�¿Çç½Xmpß|§<å_žì?ç1&é¡ì…1©o—Bÿ÷TÀïRòûÿÝšþr_I‘}QÐuœ§½ËïT&ÏØnϯìü=ê_°ù|ˆLt¾IãßÉìP¦ã¹ÙšÉ½Kð¸³î‡ÆrôüyšÏNß%ö�Æ+pßéoë±$ɧ{„<o7ˆþÂü¥ÞËc-îSËøòb÷©ñþFå¯ åo¥A~˜Ýg4øsçeˆø ÆŸ¢ñç’ȇ©…çAóxk&УV€ç8ÿ::_>8¯4ÎK¶Õùü=J_ô¾ùTæ±;øzŒÖCxç÷6Î?ÀõwiÞ\Ãüiì‰üšp?šÓ_éÝhÏñþ ¨¿`¾N¼ˆ¿gÊ7äOõ Ôõ׈®7v}ýzæ¿„ð¸´Þðœoø½+Ïù×'MyߕȈüÁÐz׆øðX6VðŸÊ~€ž§£öÉźý²€/y¿Óézxghžò'}àáþ8ØW‰iôEøQq÷÷“¯?Eõ�ö‡þ“ŠÈóxOýúæ[9]@?uç‹óløM$~¹GÞêï/û—øpÿñ÷¾š˜jùn»Æç3ÔÅû¢ü•ð¿ôƒö�ñÿÈ~AB}j/ì÷]rz(¢ë¥{)‡üXÊaOKyPÆü:é)jŸß3ÿ÷òçÃÉù‡ÿîéÀþ-šK´—]0¯²>@ü8<¨>0ýý¨>pHs0`Éò{§6óè œÇ©žzz«¼%èô€>Þ¬?UXÞŠ¥”ùù¹ú(^wyL#ÿ­çïw„¸9|Ù·êù£-ó_B|ôLÉúMvdÝ·{Ä}C„зŸï‡úU@ÿØ¿=àwø~DÿBz®_|WýúèˆÇ÷ ý÷òG”·Õ~Ü—É&÷%~}ýý³.,¼¿©†ýéõð“ ?Dߟ¾ŽüoÛøæí?¾’âU#â÷þ¹1¸_v»ò4ÂÏÆà-Ÿ_ÞAùýæÏK×ÕïÇ:_¢ÇÈûçW6ì‘ïoO#ý†ïû?Y<*>öñŒÛæ©Ò¾¶ÍoØ«ŽT0_3^þÕ;W[(ÿ,ò¸˜_ïLüõýÉßÄùW ÍÏ*ËûùVW–/øøzßÚ+¾ïî‡ïÕàÆä ¶]™|qPðw÷ÒwL¶©}}¡ó‰û-šÏ%â·BßH°!ò?È'øÔ(Ï—šÖ*<3ürâ¯�~̇Cû‹]ý¥ù Ïaÿ/ øOë»mö­n?<S´~õÎWf¥~zÆþ8ÎG?ƒùD‘¿Ê|;÷yÌ·#þï¥àyEƒï­HàzÁ|”6…•ã¼Nµ‘Ÿ›ïÐ?7 öX6¸w„¿ÃÕªjCþŠôMðàþGü6¼ÿ.úÃtÌǶÚuÐgÈ| ×æ0å“2þü'™=Ÿzüo>!ýÕ‘õ5ÀÈ•â™ô÷3+)~Ù?Ï·j¶¶¾ÅþçÁc³'º_§üœÀžCù[‰Èßj 5¢_-c~f“ù"‹áWåûo¹~Üz�ö7èý?ÿÆ¥½ò£‚ZAübþ3·”S.{ÂxoÝý}Bú»<a±Á.Ê—Ÿа:‹?Eÿ¡ O ¿ÑªA<)Ά€_lédìð³x¿Žâ}ž=‹÷ƒž_ ýÃûÙ~mæâÜ]gÿŠîwJÞ—ÍÇú^sèÈSä·óä§òßó]¤-Œ¿Pôý6èƒE©²j}°Ñ/“À_%=ìÒØå¹MQz×/ÏÏwoü;>øAÝms0ßÑ¥üÀ,æ?º˜j´aýmAÉ ù‚÷¥ÇóO‹×ÒþÐþÄýYýqj©ù\F_·ÛV@þmã÷{²˜Ÿš_÷9¤ïaÕ¢üZ×ÏFð÷D°~Ò±,šG)ýñ} ë_⯡š½`ügjÇ7Óo8&ò&õ€ßêÿ+³_Ý|IÍ•õ4}Úd™vÊ=Äó×Ìðy¯È°`?ÑóIÏÍé>8…ùùs4Ÿ1=ožä3‘o¨Yo\÷Ïì³_¼¸W ¾7´Žy³Eç-çÆt?.ù 3-Ì*ñJ•ϦýÓ<Ì"ÉëÌq…?Æ1ré•5ÀG?ú0éǾ—4‡2=òAÌcæHIÄ“. y¿7˜¥~]êSa¾äÅ_ ütžZüª“ös2Ç:þ}<ð6ÙxþAü1´oƒó/<+\^\`ù¦].ºYm6è/cDñI¸OÄ_­8Ò_éÏÐß´ ïA¤/߇ûâç0=; »ñ? ø]‚•7!3ƒ|Ì'¥(ýd€ï$+ÝS~·Wä'=1.¿žÒk‰Ÿ–€½žøÜ+Î#G&¾~ìæÐÀ$ó9½¡ÿ ìià…·¾ø>w­˜wé³jýâyü=ϵûÆ`?¦yŸåN¢~‘e›__cUñ=:?¶ÛÊXþÙøm˜_Ñ»g÷ŠNKÒé›ëU/È_µ¿p)ß—Mf‚ø8‹ÿ¸žKüö4à·.ÿäí9Êÿy,˜‡\u²Ç½¸!¯SÄ~‰üpŸ„ÿÀ}ÔV&þÇ¢÷'éd2ï3–¶ïr½}ƒùý)[êß,ÿvhß}î3Úw‰4êW†úT Ïâtž– ú/grP!û¯Õy†ßs5«áûU ï€?¶Äßbü?mÜü—MÄîÈ¿S‘ÿgñ>x±¸¨?ò-·1¢Žß: óÕø}a¨µä×q÷â…¹›kž\ÏqQÿÇ|„®Ë1ÿb 'iÚ‘|&oÑ·9"èǼ†üÉ£Qþ(øqû퇩ž&°‡Gð{‡»(ÞwÎaþÓX‚ôéÎb=Än)Í„ÌãõY%bo»t_^´¤=ìçbJÌÛ‹)T¯éV(økpß\À7Ö üÖëƒ5â|;Ci+‚oÏÄúËèý8ÜùI!üÅxè˜BÉ_KÏQmÓAŸ(Œá ÞxÛóÛâ&É{”Öä»/í­Ë/ÒB|O†1‰ñkSÑz˜Sù€‘÷Ç’õø>Õ±0¾þè¼x|>âW«6è7(/ãAýL,­ß ç¤ßFýiž˜üOú0âóo~?ܽéy$Ió2kÅ«Ç?>wYèï¶ôô—ªœWLõ5~,®ÔûÏbdooŠÆs}^Öƒˆû#ëaÀþñˆŸïÇíá?˺™Ó¦ü}à¿wGñ]ø§¨Ï‰¡¾ea<v²ÉûG´~žèg¾þ¶¶NKþÀÇêó◉Ш~~Òæ‡=å÷ ¾‰ùâá|âýëa|ÅÄú/£ów‹x?Ýoaþ_V òí�?˜ß„ôNø ~ï–—ÇÉøPÎ]¥áo_;Ø£üo'|n.øw°žüö—s…ýóY§d>ÏןlROQl§ó­;‹å¢üSðó'ZèÓÚàm ¿ÛTï£ÿâÓi ØèXÃùy"¾_»n ¢ß³ý5kZ&¿_Y5¤çúz'·i=ˆÉˆÜ…?žeVÇúò!¬ÿÅó±› î»vöäÜŸ}î”øÛnXï”*ø­Ëë=Í0ßx2ô‡Ðýª`<õ åɼû#íq¦êôEߪ s¨¦?™a†o½~çfûÁÌÇ÷ŸÝ\AýËncî¾TßîÇ¥Kcñ(¿2Ñÿ€ïóýÁ}¹;‘ý¯ø~Öî÷€zyß»\Ëïoùûb³ß£…鑲dÿP6Ý÷9°gŸÂz•†çù‹s2?ÿ)Ì¿¾ üÀRBù÷?Iô䪓 ü®‚ù5 ü¹²ý[×{:Ý6õ N¯iäßÒk¥‘”`¿é ÿ»,òÕËèÊ/»´Ÿdñ6Ws°Wu=†Š'·çßF}‰èEGúÐ#÷kï;­œþ­¬Ûf þä¶£ýZEøÑ~ÝIùÿܾq4¬'×ç×[Eõ'쯓Œ+Úõ‘zÒ¤ãnhï›7]' ,/Ú²ŸºÒúýìp ر®íåDEÚ{â¾vÜÁé³–dÃn8Öp3…žb­o]?>Ïõ’ÙË ‡Y]ƒ¨O$\)O-õú0¿ÆxªIôOšF›Q+ZÀŸ·¹µ-Ñï¹°^`¯)yÉ_òÁôwÉ|÷óV÷g4;ß+[ ï²ÿTÔD¶œ(³´žUF¥¾7:Ðnrzg×c} Åt£ lþ\ø;n°ñ­A¼)•µ:‚÷gn€ýñï¹£Ý5- Ãï×{ebÆ;F_•­º|ú>ËF?ðw'×µ]«tŠý±p?ƒèçÁ÷ð©áþ*PÚïÈW=^-E×ß6]Ûä…ë™…~±_Äw²ì¼ÍŸc¿å(>g°~ l÷‹üCu æGÎÃÌËß×ÒŸˆPzáüÇ™n1uÞ|ö |V3!>ó€O#Cøþ£‰=˜˜_ø, |&ŠC·€¼âúW'Ü©Xïûø÷Œ£EæþàýbNÖçpüÖë[ŽkvÕ4§SÞÿÛ†Šk£÷…ö_Ãü?N¿½‚ÏÑq?†å+H_¡„©Š\Äê]aXÊGÞhÝ¾ÏØœÊ tÁ}íâõ§K¹nÿ,Pû¡O©ãl²ÆrƒÜ>qQ>$Šöâ>âsø^/æŒÖÃ_ÌuÒì8Ž—P¿³± õÑNf"¾œB°ÿN6„øp>’ñqâ“óøÃ }_u µ+ÂOz¼È|ö¹K¨|®‰þ9Mò3’_}¤‰=~älhフócÞžÛ”¤ïé¡?ÆC}M?4¹s#+á÷~gtí2ØeG°’rxnî&fQ<ÿ<ðŸ,Òƒ»¢xÀí$þÃÜ<ìw[±¶Ee…5"Ÿµ6žù—ïHÏhY?œß²ñ¡Ï­£0}ì´k«]ÇðZ^­Š~@žÒ@?ü¼² êû•¹›œ’Õµ=¼ïž2Ü–>¼úœÂÊmô¾;ÿ=‚"e¡õ|lQfÆGV[ðZÞR¿<´%Y6Öú> ?ëhù —ëçÊCL-¹{ø•áÞµ€ª«éÈ¿Œ}V§eé) ?—]t}@€õܸëÑ}ì_¬gè°žå§,xf‘¡¯þŒd)'ò;Œ»Ùîd)Ö¿Ï› þJÏå~T7oùîÐ|Êö‡5‚úõtEúë&v~é|¶Šû:Å :+ ÿ×ôè`—‘} ËUéo¥Ÿ y¸µk—fc¾ÜÔè`7ÿ~EÅõK¡ý£ì þ<�÷Í‘ü„Y·à}ÉI}Ñ”ù´>æ·˜_èl}¸?ú¾›J冾Ò‡›zñ£›,z®Eðu®ûo€W)ÃÅëËžý[k¬P,—Ë'~ßG¦°>·vS OíÛÇ‘_æ{…¼óe¾”ʳ,ùPØÿ/¸oJãùh¿»ðoã`ôщ'q}üžMûA~ë÷‹õ½ë¨^Þ¨“—Yk”ÿ>]± ^ö)äÝ)–œú®âŽ E–­x‚}­Ùo>[ñQ~Hü%§Ø ®gŸ´êòaœˆ|¾OÊ“ È£¤kô}huøûTvÌ�x”_ÅüHMÈÏl¸¾9Úß~@þUô–3Ç0®*쇙 �Ï”Ô\õežÈ~ó´þGq^½†þ–1”ç‘ï3£#ôÏo�z]#ü…ê—Ÿˆ¯ìø0êÃ*ðgf‘> ð“(Uú¥|ƒ÷ß"ߨHP~œ\ß@xKò¹všqùy‡NëÛ|}¼?e _ù_‚ø_D>.Y:»Pï/,hÌú‘÷wbGýì ú‘úXõZAŸQ|YRŸ«*‹ÓO¼âËþIÈ_z8=ñóû1à# Ï‚!èiøM!B?[3R¿»naúý´ýdZÑOáÇK§Ÿõxþì§£—5¡WòOée>¾Ãõ+oÎݤõQ_åòéè”g€Ê?|WÀßÏãúßU`ýg4çÍúz±é×/%I¾¡~~Ò]éŠøÉá_9,ã}ÿÖBùþ©Ø =“g°ÅLßÎRàŸ©±ÒFÁ_]Ì×íƒôŠîÓé?¹ž:î( ¾ƒùíw¥²Cð·1Žø.Àß fÖñóBüIú‚ý ÈúžŸ`½~Ÿ~oÐ÷ha¾m*;ÚÛqGÍEFí2­÷ô‹ˆ~/¿—ŽÈÊÁþuX}&ü ¦Œ„ùŽÎQŸ!{Âõ õc“É ÆÓ y¾Å¡ÐoŠ ¿,Òw€Œy úžÏv¤ý¸ûÙ˜¥„ßSñ‹öý}jÊXÍ_;ÍX´ßeä±ã }⿉ñ˜Š!Η·à·B}-Ëò‚ÞªoŠ|\Ðeëù‹u´ë3Z¶ô[¾·$ô‹4ê—îóÈ,£ìï”÷GøÃã;X!a¡ÜI+‡n#ý/ëΦx:³A¾bVß«Ï/ÍœJ¸=l6êoçßg{ëýÆúîí¤ÿ&‹ÆFÖçjõõ*¾ã×ûsé}ô—$ÓÃ1þQ¿žÃöb}(Ƴ‚ófÂßѧ#ëya=½ò±ŒôÇ]Óþ£ˆ}X4á}øç‘à÷E þÆ~É¢ÕËêê͸=‰ùü37kùáF‡‹ø^/×?jNú{P?wÑ_îÛˆþ ¯{§¡&0ÿ ûùÐýÇ|$°×^ëQìWqMÈ3 Ö7?ïdÖFŒ' x:Ë,“Nû<à;‹Õ€ŸèŠ¸ß “íëYÙú_1¾Ê}Ñ1Œ›üY†ù3 Ͼíd2ë ^o“Ú±lŒ!àÏú0ð^_@÷ÓH˜÷½™”ý ,|>áy¤wYïÞèߌ3ÓˆÞ‡¸3>ÒÑ7ÓýhE~‹ü}öë¿Ùqñ+ñ4öÖ7ü³Ñ¢üYgü×ÍÕÞÄc]#æ³�>7v¬Ïj˜âu…þÒ¶t@f==àùK}µëÕû2é•VØH¿ÁYgäýKš,×þ;‘R ~[¿!·Î0í èì«öëjðéS]ƒØ?p$‘=ûñ@ßôÆß{ioó]8=ø}·+® /¤·‚Jô)úýûã)eR5Í\»é»B\�Uä {äÉ ÆWÉ>zÈÖC�¿Î }û�ý`>½äGñ¬iúñã陿ï\˜» ÎÏOêï)×Çý±|÷NÍæýð ]÷ ¿(ãcæÈ CWÂüŠ\Nú+áy„õr ÏÜá³»Pÿ»€üžË×ßV®ýÀ¬uêEÔ$ÿ¦~ØoýFÿúC(χh=¼/Õósw%IŸhìŸÄB} ø^%ô·#>H¾9çqÿ&ѯ´_œŠ¿ÆÄClž¿ðJÖ×›¬ò]Æ›Ž óO^æñê‘x‘…ý¡×+“ÚØÿ¹‰Yõ)6RycpÜÇ'0_ËX/ä%É ‡úóÅ iω¹}TßèØ_‰úƒrûÔw§Ð>wÐ>1v,¯zEa"þÆÏ“)B¾8Åõ=cÒþÖÐþ¾PúüþìyÌ™!~Ï2 ò픇À>.½`ßîFûøhQØÛ¾ö±¯8ÂÞ®±÷®õ‡‹ho‘¿pìm_{Û‘ö¶§ì„õœ8ò+Öó”ÂÞ®éC°Þìn ë?A_¯™2ž[ö£6«ìNc½øû!ƒßgµ$Ÿ³þ?0v'óù‡÷ }wéƒDyIì“`OG…=]6.Ã|/lOçÁž¶ÐÿU\õRVƲUX_-Û9¬GÅ~Š+vÜåv2ó“`Oë ¾§²Üš(ÿL³ê[:ÖO`~Y‚ôÒoØÖ;–P? 㥢Ïè¨ß¨l¨a½œ!ø·õ#Я\ô§†úQ ŸöäǬè~ÒyÇåWE|ôýªaOb>'·—C÷YÞ8ÂÆ@^¡=ÐY/{vsP_ö~¿lJ}rHêOÕ׿òªù…^¸ŸØÈF^é+·œ.2QOKøz±x¬÷&×ú¬9ï2v+.ê{úÙ;ÙZôºÜÞá¿)¡½(ýí°ùSG?µ•ð~‡úrhxŠðï¢6ÃýÉòˆ~íVz‘Ÿb>œ×$^baü¡Ð¬?(Õoè ®åG ú Ãþ>è#I×íE}´Y<Ü õ'ò«\FØ“öëswéQýõ-ÜÛr?þ¡Ÿ6‡§ÐP¯Eñë“ýg¨ßÖ”•“ö0ʳ"žíäPÿ:ü³b yåÕJ™™ž’'ÞתçPž”ÐчCû„üµùÐ>‘ûñyø¾1Žò•¾—Í‘=‚ý m¾^ô÷ÁyÈõáþI{ûK$ÉÿfÈxGjª2 äg%"FÎÛêâñSôO²>°·‘ß|y¾ý«Ë÷ƒórÑŸß°Æ7øù¦²[¥ |–Îóyž5„×m ¯ÿ¯²vÓïÛ çéÊ|„,úWÉ_Ÿ,³¢þöqô—^컵Ÿí³dþŒµQäN`ýBØ¿Ÿ�þ¥Gü3?Ýg•‡,âo|½=°^Ÿ°Ö”SYä7W쟱O6äsüT*Äÿ Mðã-ržMï/¯õŽÏWÜ_q¾I—ìûˆ¢ ô;­ò¯tÞƒ²ÆÜÛr?Õ3 Ÿ7Þßh¼ï/×Ï6ÏÁ¾Þ÷ õCöRà? äqùà Ï~ñ¹Òüü)óÓâüÑ_ S< å#à÷ È“)¤ƒüQ#¼¶7Iñ)àÁúœŒ'óå!òÏ%Ëoºµ_ß“O[DqgÁúÑøU`ßb><É—¦ôP8Þ?ÿ×zøìÁ~ÉßJñ_÷ϸ¿ùIŒO]¼ùÖ~cߨÜo^ÀÃìW€ÿ½ÒÇOõ“¢Ø÷¶_˜u} ó5jÑþ&Çá¾dGÅýfê“ ÏWlßÓ*?B}“àŸ*H~%ýmÌ?Šþké¢xà|ùÛÆóèÿ דþ²@_àôû÷%¿1~DôÇx<×Ѭ£õþ¯ÊÇ){ Æ[Cÿ^NúÃý×Âßçá÷…Wæÿ^øóuñ¶Â“À·uRöŸ—ò:r¾¬ð?ÂüñçŒèý5v *üx õN § ¿ÞmÓGÚÓA¾¡¼ýÖÍÕ ÿï ìÙ]÷0ŸÂàõâü+±îÑ ¿Ô9ÿG]?AÿäHÂx@»ŽóÃJ›¨GgYUÔG¥œñ¿pàåKÒ~¨b> Ú“ñKõù8Ôo‰hžG$ça¾Å¦rÊÁ|Ž6€'ÐÏ™‰ùàŽåb½í Ч×3ËÞ ÷‰êWf¤ý¼x}æcùì ì‚ýòÒ²¿*S1öbQð—ì͵ ݰUÌoWûîr“%Ë@ý''üÓYÖÿø7DZâjÀÏÏÏ }ù\ nÐHŠò×ËlJð ü÷gb}:Ú'‘û3PâøN?j¬^ßùÿ/ŠïÌÿ¬—ŒY€¡_V0¿sÑ~)ÔÏ€Ö3”lŸÈ/—õ) ÿ.âÇqÇð¾€ý#é}k'àÃôœñoï’àgú[¯Ïmf óôßùèº~éÕ£~ÊÅU€]›4 ‹ê1õ¶\ÿ'øSnèÎÿàõ›kçõUtž=ƒâ<]Ô‡GRÙqÌw#â›÷S©±¿¹™Öc!}ñóä;f`¡ýôæÖ÷‹.XwßQÿÜEöò×Gû»`>zXïà;NúD’™£åï*lJÖ õið}¬OK¯¡zÕ½Ø/³a~ÉàË?5Ë…ûò‰°Oãl ûO4{eü‹÷~ýÙûfe½#w*ªÈan^ÆG.ÂùmG¯Rþô÷/ñõ|´÷½ñ®lžg„¾5}ä’쯆ý>*ªì7˜ýÓ6åûãûüþ›lcPCü¨‡øWð}&ò5fTÑ¿!š?{þ¿_ÂüÃ1ªç6 óýùù¸*æ?â¼”øvEàÛ£ú¥:ÿÿ,oO#¼ü×^é¶¹j¤ÿtÏGV?ö(ßÒ¡ü:o¬GüöCÀo ôo8¶à·V›ŸV׃I»Ì÷H™oxìCœþ õü…¿07&â;”O™zãù¥&òc;Â+uù‘•‘ŽÛó,”O|½ÃpÿRq¶ý¥HS°¾¦ó~ƒÏ‚|y´ûÁhý ûá7<gNˆßl„ Qü&õÛp^+‰ýÁýÚ³ÞqX×,¿Ï;$¿~!ôñþNXŸ/ ~SEøÜßAÿíö=Ø_ùßx_Îù(!~ÒÁù¬îú¼æ7ä3žzCБ_Â~|îkpžÛͽ¯S}Æ¢øM+…;WW&NþÆf76|i/àgcPfc=/|OÆÓŽHøl‹u?@ðQþ‰uannî‹ÞÁòÜo«åÜ_ß\=$æíDç)d%½Ôä}àu†ºÎjŽRO/x¿ƒþÉAÿRf€}áËü©þãöZÀïŒZùÎÜ_ù›BøMà[Y°÷ƒz†¢Ÿªª°Ã”oÃá ýß©BÄ?yDä×rú¡ßÃ÷*ŸPÿt³÷ >þ¾/ñQ8ö›óûÛnÿ^(_ŸÅ|g´?"ðLKüpzÇóŠò£Ò£|ª-uúµÔ/˜§Ð¼°¡œ´?êü#»4v±^ž[—B}Qà/1X_ˆÂ_¾Sßkú —¢úš¾¦ùþ›·½Ëm+`þ«Õÿ–°xþëd÷Nâw�ó{5Ÿú!›¢ñÊhþkã]Ƽü×»¢ù¯œP~›§tÄÊuó_c"_ÆvÜîíX/?’ÈŽYÔ1MÇWº¶—§?l±Yvœ÷R`Ò?ùT ôão§g?øã>µ-}vË·]†þMʯ í?ß5Ý!Ž¿rWfÁù—ê„ü(ç øÊ÷[ v° äÁm;˜›t)ßaúÆÔ(‹aþL²‚ñâÐ^NêÃd‡õ¦ÖïÐû0ú§¨_WpßÃx·…þ%ÏÂøÇÿ"óv ÿú6ý±£Ø?ßÊc†þ“¼š‰ƒý'ÊØïPæ3y)x?Į̂ÿ6iŒÓù”ë잟ìEæO¢<ÄzÛ(š‘ý±ZÓWc=­Èoü°ˆï’•5ø‹yþqãûYš×bešÓï&©¾¦±ÿ‹¯ ýˆÏ¯ áÓ"ü¾Ò¹¸| Ö;ÿú%Î’¦iµ¿1k•Ù¡O½ööY‰W³IÕóÂ^~õƒ öŸ|Þ‘·'oüË’ðÿ@?7:¤Wæ#4â7À‡³·ûÆ6$t¼o¼åíéoº‚ßú‰»2ågw¦gzJæÆ Þ¬Y¼?Ú% {Eé€ÛIôݹ—Y>úßU}8ì úW'Û‹öû”ð·'Y¾ãg‚~¢ù«3—ü· ˜êô‹ÿæŸ>‹ó(ÿÔS»"ù cØßÃ:TÞ¹‘Å‘?‡õ£œ„üð!ðƒüÃn°¯HŸ,Êý3µô©æƒXêwøµÝéï>(¾KVé.±‘çñ¾½X2úÙ~ØHö™çû®WÜÃèÞ§ ¿÷ùñ"›<]6rlö¶CÌ{ýI‡±>mFqàýÕ%xž¾fãûüO ¿‘ú÷.Æzßç+Òßø*Û:ëÉxø«äüV8>ΰ½ßýóðm_”þyWijá|Ÿ¾$ûÉ |¥y|zèOBýNå÷¡ÐÖBÿþß½_ý:{»¶S}ŸàŸàŸ•v6¢TQ~Zû­ò7žÌ–“Ó!=o#ÿ.æ§ûÌ ò‘ð~w~Å ü_د¬Íðáÿåý»Ø…=¼‘êËñýkùyÚª“ßõí)øÿ¾ì…ͼM–Ga}cŸàÿÀoYaXèŸë¦ucBæ×Rý¾ã仸=|Æ âAîóì0vŸq.ÞpëÑÔßY–ë-§jÞæO³°@È¿Í^៲ncÃÀïñ>É|ð8ÝŸÞPž€ý:%à γ¸·³Žž7»?£YïÁ~”V/3ôžÑû,6á²É€Ÿ#ÿ0V ?è]æëS¿®T-Žþn~‡2­î¯ÞЪz û•´= —©ççpTNÚÃyÙÿøñ4æ¢}$ô+–Ø>¯FÒÜ›i£o¢µ×A’•¢üÝ;ËÌNúÿ6öÛÒØ¨ßÖÆ®óBØCn]½­“ú´Pß û_¤Ïüdoóû0ÑØo‘ïŸ-² #ų7hNF_gÔØ#o¤ø÷󎑹ÉO¡?÷ùNÕMè}À/R˜ïâuV"þ1œùÃûuY¹Þ©Uz Ÿ—Ñšçïÿ%Ú;ÖP&°'|å¦9ÿ ãÇÀŸô°¯idÚïð½/w°Ø—˜{ŠÈŸÃüæÂúŽëñ÷sÉÿ^y‰ýœ¿õ¤jx>¾ð'ݾ]“EéÑxÈlÀïž®SZ äWŠèÏP1_ ø`?:Y¡?}$™©—[Ð[å' ú3ðk¡¯íýÜZ_I¯rµ°¿Â3jË|vA»|w@_Ð×,æ<ƒùÿÑ÷)ßÀ•ùA>¾a¬3¨¾Ä8ê†ùyÌïwZå÷ë{¬ó ¸¾–’ù`ÃzGÇ)ß�ôµÂúzUákâþkÁß{ݡʊiøÞ´y‹Îù÷ ¼ßÎà ¨?a½ILö‹lîðlƒj~á–ÇÖƒåÆF6ôO·œ)óý}/Þ%òåËyÿ¶nÿ2ì‡ú—Dúk7®ë¯I~”Ìo`ýÌHä<9~ ôŸêØŸÇ{kqQߣŸ½›­e;±@¯ËGÇ~´¢_%Ö$KHNÉÚë€=pìEÌŽãþÂç•©:@¬ëÇþ�?6êó=.#y•Ã~g?pt.¿üäø1ÆÇ–,o<å ùã}Yïô— óŠè õ±„‹ñÓòg+æP¾¹Q×_–çÛÑþ~±Zú.íñíQ½ú¼þß ús’ç#Œ‹óé4­Þöà|TÀ÷:Èûø½oÎ69?Fõï ãÓVêá ŒwÏóoÉûHýuîý;ñhÊ/»ãã¥[¤ü»… §ÌÒ`{ðwöÊW¤üñ0Í’ùhð‰ ó¾—5åzµµløtYÔ“¸«1ö³¦¾«ÈüßxÉú¯Ç¦2ÇzÂü_”'eÖí¿ž,Þüz_}ÐB¤¾n«ÌWËö£=RooàüÌ/û8ƿ֊|v=’¯–£|µc¾š‹ùj–ÌW«¡þP²…½`©„ù‘|k™¿VÄ|öÑó×ÂzKÀü7“ê0¿¬:ˆö¤YWV—¿Ö·°?³qý‰†õ MÖg¿Ð öüpAøwÜB/Öo—±Ÿæ»0ÿó# ={A?¦ú ¶UÔSX*ÖÃR~ix^Íëqy ”A~MÖ¤zBf�½ }óûƒüâÏp \Þ/¢@“¾Êû3L›Vë 9?Î~m´<TQŸÌúÕ;Ñ÷OO[kº}E1±žpÖn¨«',Ëx«­Êúyñs7òT¿ðGõžöA ëg÷`=!ås•Æd=!¯—’ùe¤±aì§ô‹|Û00Í ¡ü¯Q½_~¾=ö%èógëåñÌ9ÐÿÆÂxΑSs"ž3ŽúGè¬>rHcççîž§¯°z{<Áó‡dþè‹”¯_³ö!ütÞ„oùûèÏ OF1?f%úà>DåÉ‹Z©¾­S4°¾´ÿlð'¤HÞýY:(¶¾ÿ7ý¾æŠõu¹¾CùjÙÿè«kò÷‰2õçÂzTÉ?ü­îšVèý» øŸBõÓçü½¡ŸmƒÄ™Âú�ïÇÝè„?þ~É‚óÍ‚ª9R‰Ý"ëS\©OðÉ|o'Ó úÏŠaä÷@?IäïßœµL‘ÏÉ÷îÛ¹H¾÷ê¥ç{‡ý[ëó½ÿÀ¯}ôo¡#ÄWÜ­Ò¿ñPz¦§8$ýî›swǹ=ßãõÜ¿±äÕó«ü¾Ðï#þòôoäZÚGµ×êïG¹Fö´¬—"}:Ërë]f®Ø¹ùÇéüsŸŠõ¬1•Éü‰ñ\C õ¬?ÞŠú„ŽþÎÙiªåúIÆŠàÏÚo6üío°ï7Ѷ¤{Ã;²îO«Ïˆ|)û²Ð\¦zCàÃ@ýƒËû°¾@#}¥ FäN¦ö¬üÔ ì•ÓˆÏYÒ_^PJQ}ŸÅªäï«dšø#ÈHýpˆ¿!¿;§oS¿x„w‡ÐRæúÈAÌß«¾FüJèvhOFøÊûæúÝGúÍ¡ßú„~c5Õo é…õ½ï õ›Êòê7Õt3ý¦@úÇ'0ßÜh­ß|bôÐãA¿)¼cýfæÆ««ß8MÖþ™¡îWµÊ5˜¯MùÕO‚¼p'h~Nׯ÷ùÍk[y4]È®öj;2ß è­®'õ­¶BÄïtÔO\êàâüißÁþ¨¯$¢ý!™áj”a`ÿcdðŠÕý ²ÿÕý°ÿ%×Wr-úð|Û ÿACþïÐ ø ~ªëxb7ê7T?7U߃ô€þ¨?G1Ì·5E¾­æ4äWÚ?™ËsùœGç—Hzb€ßyò!i`ÿ(–™}æyÔ'(_Ä›uÍnÎÏ^-Iþåéã?˜O¥´§ó{Å7úŠoÌÓ"ôcGü ñ ào¯ÿâþ@yÿý‡¡¾�ïk/ª¾&åu±¾?LóøK¨?„ëñ—å·‡òŸò¨>þBï–‰ô—ñÆQ_ŽÈ 7îÄúî\­ï~x£;qÝ¥½ õ{ôës¤_Ô?ýI[ÝJÿÐ÷ þ±Ãªbc+°?Ë¥ûôíÏ;O¸·ì{ÿ¬iŽYÆ# /’ÓèÏØÂùúÊ‘Q¬§rœìjwëû½Xæ}†7¬‚|U¦Á8büÒë˜ÔOl O{¼Ü=bOôÝ�üÂù…©”¥ìúÑà÷É#ÔÏøÈvÐ/þZEýfûíS©²2&èÏz03µªŒú½=Ñäø»0ÿyð½‘_Zc¡¿o`LgúBÁxèû£ÙÕvÿæ ý8̇ÍÈþ8×#b˜„õ'Ï+ ú#(Èo6ü•œ/á–ͬì‡Fú?žw óÅ0¿Ë+cÅî•nÏ”Öaýµ3Ô¼'‘{m[©¶%"ï½Ú¾Ì÷’Ó(ÿ gEÖklE~V ësR>èíØ/‚üOûD|5AùÝÿ%{,ªo¨ õwÉ`½ù/>¼Ñê²Uôoz$èïrKsì~âý]¨_¡ž¾äonõwûê¡©®XôÒX?`ÉúëMŒ?1¡Ïý&Ë(\k(û^ò'kÈ_ËDïÖÀ—0æXêÚwéÙÛáVÝW)ÞO÷Sÿ_àûé} bL¾H÷ƒä‡örðÃŒà>¦̯~‘7:-ßOû©ôÇ7 ÿ¶õ.¬WåùÞÒÞ2ó†ÐfVásP„#Ïüèù®AÊÏHN‡ý2°ÿ‘‡£ØjÊKèëÖâ;¨GŒô/ê•ý&Þ ß›Âøõ$öû™@{œêç³ûGe?¨ÑÞŽP_ý‹¦°QõÝ Ÿ”"ý‹vËþE2Þ¦ºÖ-ÌÈ^ÿËïtE¿ÔpBÞî Â|‚DùüM²¿ Ø#"¿yå÷¸”ßúÞ+噯ë_äý‹@~×\¤wÜ_¤¾¤2ÐwqÈXad• oìß’¬pú–ò»Œô­(CìÐju7Òw~M}»vŸ˜o¢’<¦õçå7£¿¡ò.ò7Pÿ"áLLQý?ê›VlXøK¢ýŒ¨ß…”ßùQ{Éûaƒ<Ç|ví ªÿ ü‡À?þ$úÇÏ=þ\)QÊ]F˜ï§x›õ{>ÆOóˆ?bÿìû„ü óŒâý³0wögßödíëñ½´>ÚÚ³ö9[òrù™¯ þLý#œÑ:ûåw]<ì·¨ï"ñùmVÐÏJ%}ÜÆ~&®Šýf¦¨¿Y(/*ø»°ë�§Àß´´<äöGüMòü+ù5Ý~Ÿ²ýM_Ãó/ô×Iö ³ñ¹èÖào*#«¬$þf ¿)𣿉Î× ýMyÜÿ´Ô§Kˆ?¦ìÃþ”kz$}„ú6ÒwèŠÒCõ ýMfÃxOîs_$ÿ/Pÿ'­1ÞƒùcI—ú=¹f`/s¾�òb^§ ¢Þ¿1þ£Qü‡×c„ñŸì8õwŠÄFŒÿðþN‹ÅúZƘŒÿýî¸|ÛÃÀÞø{+‡ª#Ÿ»#;°ŸÖÓ}èï›ø=8¯inÿ…ôi­‹æ«ýœØ}B¯R?§ï'\•þV"çÁù1[÷XÐÏ|F¥ø‘ñØzòhýnqËi—Éz„£Óy÷E÷˜õ”¢>L‹ìgÅxÓþKØðì¶–©‹õ_¢úʰ?RMö;mÞÉQÃúJÙ©8$óñÉsJXï›ùÜ¿LñsŸé=Ç‚ùV2^­/ŸùòK^¥~!èôо¨%nûöŠý퉸Ÿ¥úÛ‰’Ðß|o½à·Ef€¾gÉúÁ²Að‰ó™g¾¡ïúºÚ«ž¾ì™’¯v �'ûÎÓÉ^‡ïú¾ú߬LÔÿ¦9C aÿA~™ðÿäÄ÷È~ô›æöús+8¿!NñrãМïÖ¯LjeÔ÷I¿'}‡ë£ú½k…þEããØϊü9a¤¿~|e5žWŽÎ çÙOÑ"Ý’¿…ý_¼3ÆK³O2–§Â}Q¹>7û#úíäýÈŠ¢ÿ™Ê¶®«¯w’õøÎª‡‚ýÔ_€ìÝ�Ÿa=Ô(ÕCaëZĽ~(üóa¿š0¾¡#¼®´o ²¿+VoTŸi”穬Iýx0_p¦› óþ<ŸæuÓ÷o©¯o´êä™éÇS~UðãÆß?îm¿jÊ?þ»cEÉW/Æ}ï]<_+R<¾èîU"ü˜óOßwñýa?½bûÔ´§£ýú°ß‰‹õÞµÒP'¼/åm„?ƒþ…üy¨îQÞ-ùsQ>{ ø]ã½Ù>‘_ÍûíÙá¾S<KÜçÐ~ˆÔÛâß¶øµöß+ôwV_Gk~}‹à×åvÁ¯k…Ìç×Ö¹²à× 56á×·O»•Ú{FÈŸ#þ°Ì_)ÕVüÙêßü9·Qð犜ç­GúŲ~ÙO­SäûÖ(>üYÈÿ¡:ù¿8½5‘ÿK¥·+ÊÿX"½9@o^Ho>[ß»ÖëÃü Òwý’ÙŠÞÒv/¨+P˜¦~²!½U+ð·sðã ô¶5¤·:þ–˜fƒHo÷ z«nz+\hkAoÂÿ3ÖµKs4ÌGCþj`>—±eé&ªúÿÐè+ÔïÐXa°RÁXMò5¨¯-Õóé0^9*ø·fÿsƒ¾öC²LþܾJâúw%Ò/AòGÆ',¬ŸwÕ³çOÌ÷GGý[T_]r¨ÿ-ð—Èþ ŸHÿ}!Aö¨ä§î^·%¼öÃ~;߬~þêýÇë÷oŸ\xÿÖÉÅ÷ŸjØ¿Žñ¯7¯žœÛ$üE9Ãð†>ïVÄûûÑÿ]º¸úÖþh¼Í’ól §@ž—˜Ö5—$ÿ/ÅsÅ}i៭iΫ ó-þG=|Ùùéà›ùêgÒJÿ¶Ž–Cý»??ç?Ï,™Ÿ“¾-ýZV[˜ŸWä¼>_òóxqðF°¯÷çPßÂþ¯ÓR_$B¢<Dü@ôÃIp{½‘8[%?ãÃX_Ÿ({˜åYH¿{„?‰úi[h¿¦0?Mô·ŠQ<l ${Bô?hcç§D~†Kþ”S¨¯}W‘ñ˜xÙþ+o+…ñ6ïGØRæ—pú™¼õÍúx›é9&ýkY¬¯!{= _ãM ÆÛX[Ø]<O¢î@jøaÿ+éoÊ"<e©?Ú¨ïÎ?¿!Zû[Vb¸¾ìÏl«…ïçšœ·ÅBû+\/Gú&ök-ÄÐÿœ«ó%è¾Ëh-²¾ƒñXnŽoe–}Ç×±ßj¸~úÍÊõɧ—ÃxHá5êg™òHýS¼oØ? šŸ§þ-£¸-ð—ƒüÁõP>©a<* ?ê745”]­Í¼Ô`ÿ½,¾ÚC<ÇÅ|'çÇÏx<‘÷Ï44Ô×é<Æ1LÀ³5+äe½| úi6ëW£eà3>¬_è#ï÷¤‹ŽôçéLÿ!æc9Xÿ‘J¤þÞ†ý“}’Æþaâ·d ÿ­—í-ºßœf¯—ù>Nlãû –þ¨„7¼Ï3ÒÞÉÊþŸ¾ùý%¨ÿ'õ‹%~Qmà¹&ü‘ëëãBŸçýMi>AÀí©!iO¹JhOQü—¿¡¿)ÙSÅföTíUì_ôöO•Ã~He>ò/'äu‘a< ûI[Ñû3ÿý@^—±ŸSãs›åD?ŠÇGïߨ䇘¿¡ýÀÉ}[öëA¼Eþë"åÈûè¿ûFËCÖ¹¯<Wûi Ë�{Ó˜?«§°?Tý÷øÊ¹îSÊã¨<~ ëW†Öˆþ1Ù·Ã~/¢ŸGO,áòZôàÃþ/‹Â§#|SnËóbo }¥9þëÜË´ßþ>$ñmÌÉþ%SÔ¿äd}ÿÿr«þ%6õ/A~'ö[Ÿ7†1_mæGâ¹¥Œ¿çø-ìG}Ã;+žd?ç@¿û>×ߘ?4=¸VwÛü‚+êçMš¿ˆõônt^‘©‹ýveø{YÎw³Óf÷ÍÆú÷¬.ŸWÂz®í˜Ÿí2ªw«ë£òz¥–ó‚‚õu³ûuoè²{W¸?¯oíKOê—†Š®û*õ‹õ"å×§+­»²¿¬Ðq~Î š7_$΃=uyn,>fä¾d–YIçßï¡ùK630_k. '«ë·#燿ÃOö—¥õÚæ[S?Xw^¿Û ÏÚ´~ZàãKˆ°ž‹ã£"éoÞû_Æ÷ÃùAü}g óƒxƒe©Í +§²1yþ°£àü=ø]Ðo¦è©lÀgHoªŠóÓ ì¯qÿžÄ¿#ù ž×”Ñ›^ÉžñíFý=Áý¹Æ]’O¢¼K‹~›¬ò#òýøÓúªN·Í.¹„f¤hþÍC"zye‰ýèoËŠî/ÊßåÒ˜j¡Äç‰óóTËZä¹Q÷|ÑùHüþâ<G~_]ø�¿¿>ÎßKÑ>Ñ/(jП>/î_ëßJ3Ú\ ýÍÿëÉÊß= gÃý-§ñï”/ùQ²8ˆù&êäwŒ|dÃþhGÅ|É›´Ê©È<b=¬glÃúŽ|ÈJoÏ1êäë?ø¿Ö2eÅMßë‰ÌËBü›‰ã¼Ð£’_5ì'ÀO|ì‘fóSõ«¡ß§á>§ó¼ÆSbž•…ùÑÆ üþ‘U¬ÎzÇÃçß;ß³1˜Aß›·ßEîOzê·ÖçGë+¤?Ð$·±J;,§~Æ3#'s³Ûo ú%£3gÄÂ~qGþc} ósMÌ¥û­Šú•òËè{ÞjÞM â|õòvKßå±ì Ç4§˜aôó~2Îkè¿b×áü¿”3,æ9R¿K­ªl‚ß«àýýÕHþ þ}åA¿cÓO\ƒõ?¹Â¨5Ôwלú|o»ˆý{là §2Ô±Uæ#›[êü]…¶­Â¿þhX¿Xx²Ë¦yÐÉÁ¾Mp4OH:ƒŸÎSÖ%¼ÿvžÆæó ?+ò‹Ûe¾omÄóá]%èùðy%ÙßÊgã='@>|&°GÑžþÁ¶=øw=ÿrg­¿ ìËà ûÓŠ7Ü/ÖûâÙ-qkÿý"þÕlÿ)¹_Ó1ÿ§XAù¦´ð'*@/ކõšÍçyXnèÏ^³äþâ?ãçAý“,¤'}ë5Yo_ŽÔÛoÖÛs#Ê×AcÇXß §­¶ë; ×à0õIš™Bû_žÅ¿©ÿöê2¨ž+„'‚Ÿ™?Ôo'á|cä:Ž{Õ˜Ìwžò Ë2GÒÑû—θ=áß_îŠ ìÇ»„þ™Ì½í·•ÍŠ¾í•æ{S¾N¯ˆŸÍŸ» õí�·Ö[<:À3ÀcýTß÷›i¸/z„žhþÎ;€¯ÀWß§ªÊ6Y?‚ôî!½×jßþãŒõ¼È³ûãë Ê�²i¸¯EïÆu®Hÿ?õzð(,0â*{>x"ng{£õz +·mÏúJù…wíòÿ²óãn¢*ù滟H±#[0>xûµ¢þ™½ø¹Žô õMçoú@Ýó@^¬ úQKÌÝßûëm¤‡r„cm÷¿ðþ¶{»7k1—úÓ;‹ðÒþ^teÿ#¾Ÿâæ·Ðïwn@ÿë}|}̓ïS(|ž¬_q5¬§h{…Ë§ÚØ£k¼òŸFø¢ôáÈø Ý'zþ Ý'›½Ð½ëùñmw¹Ñûýêüí¨ÿ5Y_ÜùW¯Ÿôø8¥!þâÕï;Ìpo6=ÄŸœOý�ø÷ö?¾4ÄoÿVq-p¿FþžÛo7Ñ'Ý__ã¶éê+Ø/1]FûƒçûëØß¶°Þåãöß(Œ¿þÜÍe”x¢>ëA=ü»“ä›_“þK1¢þ¼ >e™Ÿ_ë1ë†cóäÊ;Ð7hS(ï<õ)äÝÔÂò.Ëæ÷gª“HòoëÉ?3“o¿~oìÈ÷½@ëY;Ã~Wtߟ½ç:XìcU^Àöm6Kë18ß#Qú.â| ªÇqW /8æ=ìçÈá ë{²mb>.õ÷îWv?ÈW¬àò5•¯Ÿn%_Yƒÿ百ãýÍ÷÷ç­Ì*“ªÑ†þ¨Ôofͻܤ9UhφýS2ÄÕ,9/:ÊvûÑnÇy¸Y?úÐwU±}åK ÿÇÙƒðÜEý½Ñï_öZþ“ÿ¾‚ûÆnôöEÿÈëO ùê#ÕPÙê#f ø³ÑGþÿæ¾=¼Í«ÌóH¶çÒV)¡¸¥/‰“Úib+iÔ¦—ÏŽëØ™´1Iš¦eö‘>Ër¬Ô–„$'NÙQ 3˜Ù¶£20c %m·VᲘa�•Û„¶€¹Ì®£ì†nØ}œåò(ÍžËïý.G’í0óÇèIôóûž÷¼ï{î—ïè;q9 ùHªÎî/ÙÔò_ŠùHçŠ ßŸRûŸýâ÷×ï[´9¸–÷ç%ß[o{Y½¿´0>uM&·ú­¼ÿ±ƒÝø½ó‰ _Σß>¿“šýúòŸÕÅýôò~“@ïëMyžü^ßGX)Pgúr'—_qÖ÷¦S¾k<õ·§aŸx¾ÁÌòEÞ?ûƒ¡ƒ‹·æCOúôû,ñþv>ÿ:n,5]ëÓÔlÇò#bý¿»qÒÇûÿâ-¡ß·Nœd†Y'ÖçãRÿVÜòÑ¿å©3×|”·_9¾Ö*_C+ß•å[˜+¾Ýž\÷Í%Ï4{-•ö‚ ˆï–g¯8ãOžÆÙ¾R¡§–÷o5.£ý²íwâ1>Qù-ó²C¼æjñ<¼ñçý7gÄþ„Ody©öÙ-Ö7‡VqÚ‰¿B–Ÿ,/væa_ÖOU^Áß;û!&üñiõÍÝçÿà’Î/ùCe~ý«Ì/´ÇÔµ/»äëòÚûç‚ò}b=ïü^C­¿ú°Œç‹{Îÿ¹XÏ;—ÛaŸ×û;Ï;ãSûeb¿¦8{©‡·ÉûãÅýÏ~ï/åýñ|bbŸ9óìõyÖQ¼YÑ}‚.:tDÐÓôLÉ$:Ôa6­Ì½ÝDÿ´¤GЇ>$è’C‰ý¹²}ÿÔQ4«YǸ³é÷¯Ñzįîo÷=ÌÅM¾_–üòSx¿hï—åÍfq_MOЈ¬¼öÇû–Žw5cýT ,ëuWyññÖE«ýIç˜l¾2ÿ zÿ˜7¼ežð5ó„o˜'¼yžðÕëјï:ásÑ|êwík#æÃ¬¿Ì®õ}y©éf›Y‡¼ïÞâó­|DÌ—xsPó…k6&V¤üT.°–5+ü‡Äó­†{hEîcb¡wýáSùmˆñR”W@•§“¾\ “Ïo‡åå¼0¹ÿOœè1å\“ð'ØÍ.žè{ÈØ¡ÚcÇF#¿*Ÿãòf÷·¥#Èåß>ÄåK9á(ÕÍNûR7Iù‹¼gvç‡?g{¦mÓOøzÍà'ý}o~({'6«òìnïÄuÜÞ‰ —÷ǹ|‰½^Ø++{AÇÞ„Çž¸ÿ+’³í±Õ¹'|»MÆí]'ì•„½}1Øóm2rÜޣܞo·çÛÌåýG¸|‰½AØ+í’ö˜cÏSžfGS®¡Ä¶<Ø-ßOîëóõ¬©—ï'7xg‘÷52¿x^ôóòí¯çýõjãì*¶»«¸#"Þ'¹„µ ú~N£Ù,è#}3§sÇm:—3ξ–-¹³xc¡Áâúrゾ–Ó¥Küž8/èVN—L±š0ή`¡;‹kYÓ 9Ÿý»·?ÓÿÍ~PÕwfŠú"÷·-ÞlúëÉ-c“=êÕóœ?ÚçWíý¯¯ÕÛ{„]ëwÕ·¦x~Èó§¾Ó¦M^ºI£™‹–/ÎåõÇí¥ƒ‰ú`›s9êß^è1×4}÷¤óüø>ñ|Õ‰ïˆø\‰]ßÀã™]F¨œ3…¾`€]<išFHÕ‡ûŒ5ëó9CÔwn¯Åòßò¥œ°JØésö|ê¢xÞ×ä¶gû {¦Ñôøc¹®`ÈñÿâIî?Ùãþóñ`µ¨ïžÿ¶/±Â^YÙ :öš<öLa/çØã>þ»“q{Â^IØã¦É^n ãöÖˆú~§°'ä¿-äK¬[Ø+­ö˜cÏ]|übçšò¾/n.76úÎ/§þÒß!å¯tž5ûŸè‚Ù,O Ö3)Ÿ;ÝØ¼ÒräsÏ3ce‡+ü[lã5îðÈÆ&wøÉ¾+Ýá§#[µð­žðçÍækÜöN ºÃY¡mWþ­0[è>Æœ|½ºö|¯A<ßøŠ8Nùç¬þ7—’ËØ ¾Ò ~ñ[6âyA øÊ.¶Ãí_Ì×:–ŽÝM+b¾Všiäí¥é\ǵרö"ÆÛ�{eÞÿ.žO÷ðKôçbÿÏàã¯>~²À‹r LÞ‡ ìùäxÂìùSã»ý¼CÄ:jµ[‰Ãíw¦ÓÉ4»'‘ŽE“‡ñcÆÝ½{þÑÁÁXÚÈOÅD4G¾wx8vØîH‰%²wŽEc©l<™`òsw2±i ÎueN¥!ž1ɬ‘M¥’éll@É9úöŽf÷ÞI¦+Wäg§•¸!kXÃÃɨ•;L6Of»ïî¸Å@`6kE‡Œ„•Ù¡tÌ0²Iãà]Æ`2mD‡“™Ñt̲ñôR¥WÆç&ä 1"ÚêÈ–€ac0mÄ Ïéá~+ú€‘ì?"R6deŒþX,a¶ÒýÖá˜MòL‰òÄÁ c½‰£Öp|�6 +e2•é:p%ޢɑöÌh¢ýHÂjïKÆÙXZ˯{žâÌ`<6°'žx�y¦ô&û³V<Á“ne2Ü©‘6®ª«j³UÝOij·3Ö²»õ “Ï/™àžŒF³<ûTÖÔ“Š)Çv3wyA4¸ƒÃFo×|šªÄ?<œìçEŽñúKDc5TÜ-K^ÅM ðüÜce²*CZz«¤Oâ…“J,@;cƒé䈛ӲÇ)‘"îc¯¬&;Z÷¸KSžËJ¥b;¼~e²<,j»c›’Î횪ýÐJ«ùÕZÁ©È¯}Gåìg“Þ|jñ¤ù�Bw&Gc¼ñ§«æRknÿÈä¼Óýé¶³v.æÌBÇE¦·×ý²íð>§¢]Uøè•ŸÃ±¬ÈñÞÄ`Rùí­<h-Z½qEš».;æ±c½¼‘[¼UÕ¬/UÓ´£J;E¹¸t.Ô 6š‰©>¿j9QrU®lÇyÁæ˜5šMîã„]O¡Ú£WŽ V)?Bï½éx6VÍÏc"àòŽ Sª—¾¦ƒË<=­ÇDÖ]Ét}x«Ö•æÎã­áÑËimN”ŠvµÃo…¿•úlÑjñ÷ÑxÒÜ!§{å¼%S™µõÚ*Ú4sÚû#ô»úwò«;ÕjÊýÞVL*æw\¦ju­•nòHÝ£‰¨ÈψÞRÝþý5ºµyüÒL,Ü=áŸêð÷«)VKeW^Ó§yÆIMõe8Å?bç3Äcäĵúøiç[Unúì®”ö]éähj¾q_wé²äöïÞýötv¾zo‹Öš)Uí§jù®(-5ÆloG3‰L¡Ûíg/¯'‡ci9ùšÇ?·hezŽŠžo¾N׫¢J]ˆnQùI¡ÑºÛ…*5ø^æú`ŽòlŒ‡i%µ°òôFA»9š| &Ö/»å Æ£GºçÑ V]}ëîîYEÈûù£É¸¬`ƒÃI+Ëq 9Ú?ÌýN&Óßâ3ʳUì´d€‚Þ¼]Ñ}ÓE ôö­ŠXáƒ1Ïd¨UqûÜ}1\‡×©ñ‹Ö›Þ|ùÒV븧ë2·&’+:ª¨˜û&Ԛݞ!›Æ@lPöÉ„ÑBëk®‘³˜X9µòxÆ Ž -ëZ•?©t,ŽÆúÍ»Hc7¯W|M¿$ùTÕJð1ö2Fˈ5f¬/¢õ¬siË`o*–¶„7Îfƒ?*©q1kÛ N'6`XYÛŠÌ ¯~ìcìç}`ÌÑ‹M Ç»‘ÑL–/ö ljðZ?dñì°ŒT:>—Y#ªÈW+¶h>»39ʳðH›«q®˜ÒšGG$ŠÈÈIJ£)èñìËTà-”0wýµ¸®½™}\g:á*âšÙ¦ì.lŸ…×Ño ξª#Ž÷±ŽeÛ2˜iîî¸÷@xW,ËAÒj>œLÊœMB€Z®¨”ëÜó2aBJ¸êÕ@Ú:&âfFÓƒVÔ;ÿ®.¶u˜+<.gÅNx·ìöìðmÞl¤†­,/ÉD¢üuVZå¸Èç²Yò¢ZªäÐrxFyfò%‚],v>^Æ>ÍÙb¥l1K=ùbï;eRm«È…pF4u®S6V×ðÍî9ÐrÕOÐ •ˆ—ïÔ\/Ÿ×ÚÄ>™J¸ßÝ~stpk°ÿ¦à›BÛ¶læÿ6÷o³¶ Xü/{þ{SÛÖ¶àåÔGÔF}ð'}‚ß&øm•|׬Ú%çÐ*<{}íwɤUêEx[e¸{t­ŒWÉoOÄ“íªCRrœnsÑ*üx6Öiw#.¹J¾”ß9d¥«ÉWáKùýC¼ vlù*|)ϧ).5Ž|¾”ßÃÎjþTáKùn1ðVñ§ _ÊwÉAÙ°å«ðUù”#¼S.Š>p_ßBoÕÍ4OüÎdr8f%*Ê·’ùãbóÀ%gÓ0à Ɗº÷Q/+ø¨_¢`Üõʦ1î©yd…•|%/ Â-çÐ*\f¼+Ü¡Ux—kVäÖS‹/ô«]jÚ£®ê'D°ÍËZö«?´tCj¿­¨e§.gç#d•@K§.'Ê"Ž_-÷WÈ©r†èýä_·.'ó RÝŽ]ºœÊ'v‘ X¬'GRÉŸ8³ymÝ㪡òÏ6=VK+ÅË&½ýG‚¯zÍ6·pÍ"ƒ2Nü·È^(±6 ¶å«­7D ]^Ñ­Ü?QNö ‹ëßéÒï V&ZÞ¢ I…#÷Þâ7Ðg‰G<X e¿«f êÍÛ*¢Q‚x€šb¡0y‡-dSÉLÜyZ&ìõJTQ›LsI§osO/UøÞÁA>âªøš9ݺ7¾Î'yžþý2_îݱ‚ˆ<žLˆÝÍ#–FÛsyùèÈéæ½þé|—âìÌÕÝ{"VðÿÄÁ-æîöÝ+ù®ÌåŸûhÝmG±YÙI|žÿ6Sä“;¿\;]ù/{„õ‚ÞÍŽ!òÁ­·ÛNW@s§Ã pµ²ã™ll„Ò#*r:É×UY´§*í±Z VбD49€¾BÎ˸&nt$#'_jÂW½]r¯f 5ähîj˶¼ÿâ¡ú¥ÒûN‹ÀI`8ÌÇ9à0F€‡€}À  ƒÀ l4ÿL¤c83øPX}X�¿ñq¤ü)`8IòÀ `84¡g t 8Œ�û(ñLÐ!`üÀ&`�ØdÀÙ¿Cú€eàYà4ð °œ“Àp˜ŽÒÑòAÐØ]xþ΃>„ôSà#ÀCÀ>`І€ô´ý„W�t#‘>„Ï =€eàYà4ð Õà°œ€À<p˜£úL=î­à!`°‡ê0¤ù7të«Ê¿ ›€¹m ‡nQ˜"X¼Ya#°ü  ƒÀ l":} pv«Âà`Øó&Ð%…SßG|à™ï),ü…ÿŒô}Wá¡ïÀ¿oÃþ· ï›Ð÷ …‘¯+ƒpâ%„ƒ>û"ì½€ô~ òg ÿU…ù¯À¿sˆ¼P†<prà!`°hCÀ °h�'ÿEa�tèâO~Ð ØôcÐ?@y _ó?D8øeð/œ < œž–€SÀ"p‚ôÇ9à0�ûÝk7à'Æôý˜#ô‚Oçbª=·i¾'µF9g§zÔ%Vmßz·J5_º,C;ب=µž?yž(%寨gÿݵÏëÚÕJ8{šö¾kW×ýí]ZKìDÄÆìíZ¼ûAL•È®êÊ­_µß(ž5P<×þœ+i ËK-[Z+ý©¡Ê™ïȽru�Í3Û‰Q¦oÛoÙxb”»æI—µïw‹ÜW /©öÒ®°œlQ˜=: Ï€cà§€CÀð°Ø4ãm ƒ [(ü&Ð`#pf“Â3ÛáïÍ¿EaôÐÈM�óÀqЦ€CÀðЄ޳°3½Í+_= „àŸ ù;`8s;ü}“Â2ð,pxXN‹ÀI`8½yР§oS8Œ�}¨„L£}¬¡‘‡ï·qq ~£‡_oã’ü¥~£Ëjð—×à_YC¿¿†|‡°±ÞÃOã¹E"‹EkÏÄx£ðöT:mIŽ&²rë�ÁƒC…¸«¯«w—ËŽÈ ñö£Všþ–ô@ìh{fÈ^õì½Kî/¶Ç²\mÖê¿éó¨oÀÐó¨%…³_PDx Ð�êñ 8û9…)Ð@¾Œú,~õtß× 8}òÀÔW!x&øg8¹è³/*̽¤pXNK/zåg€7€!`°˜zÑž^ ù¯Ã0øMÈM`°Ø � 8û ø ¼�,Ï�§¿áOö&Iÿ´BÕ^}¬Û¨™÷oöƒ®{™^sÎW4Îû‹'×R;­Í_ÂùkÏ7O†T½#þ"Î_}¾¡XØâåïjnöÎÕY·‰_¶;ü5/³suçëo-ù<òk)¾uCIývOñ;üuçü[J¾%v|îɹúó¡%Ÿ<ùvñ}êâûɇÄ÷S!¾ŸþKñýáwŠï¿ß“ïâß=ÏüÿN?[òÕÙúx¾œó÷oú¾ä;É´tÏן[|“æ¯L·]ç/•òK6W‘wé±í‹tó|jàéñ[Í'K¾*öE¾Ü\;ȯ5Õó÷Žöu=k½zêíò£ò¸Ud̑ѷË|¾õÉ’ß%wÊÃJ ¹W|²D~ê?…|\¤×‡M\,ähS¥Ö#Km“(µõö¦]=êÑzé_£^_Ú¹¾mB_ˆëcu­B_«P×*´µŠ*Ð*j@«¨�­ÏPþ:ù¿D–cã®§ÝÎþŠz\_Qnàoçñ¶œ,ÑhâÔÛ†sõÍ%Ÿ§·–|¶^åÞªôzäÖjñÖºâ y96†´r¼‘ÊëK>âÈ ?}çê7hþ¯SåÖ`ExÅÛa' V<»¼×-ùu¡áô3¼Ì'|¢ÐïåÙïòÆ_r»Q³û±åÂÏ ç—Yïô©ª%$*ý›È1ÒÛá÷Ÿó5—ìÙ‡oý2ÏÏ}êñn¢’´ÃoX&ó§ÑÚ+Âïø¾á¾ìg¯ýy½õŸè î剟7Ýóé]·\鵨¢B~þ‡I÷î7D;Ú¡µ‡ÕàoT ®hwà7Ùõý€õ™%²'à=J»è¡ÚE‡Ò.:”vÑ¡´‹¥]t(í¢CiJû³ÿ•=÷0ÿúÈ#üë£ò¯ý5ÿúoyþU|Œ~7ÿúøßð¯O¼‡}ò½üëSË¿þûßñ¯OOð¯©÷ñ¯Ï¼ŸýÃø×gç_ÿøÿúÜù×ç?Ä¿¾0cðïYcfuÉ·qvõÌA­ñ´Ÿµj\j°Öñd´Úš¾á­<¼Z»_¤ÂoW!N=B¾Yw«ŽRw…7{Ç‘=Keù5Xgý¢�ÿ±NåE§´øëÿµr$~»VŽÿF¾S¿0þßx‚+Çß˵³ã¦jý­¨_9`ˆLX}Ê5¡|ߍХbZGåÚ"лÞ.ÿ¸ŒÈ{â-¼Úú­;åÀºW\¿õæ§]ý²²So­–òŽKÑÎv«‰UE?â ïœ'|{Exóe…ϧ¿F¸¿¶þuÔÈñaC¥} ߃ñÃ?Wøó„ßüo ŸOÿaŸòï.ϸjϧÖѼ\÷ÈúÙ,ghƒ²"=ç]`ˆwþïý!Þúe¸ñ¡J/§kM |;NU ßR=|=•ãäìà}˜‘;éX‚úÁQ}~°k­wÞaó[ÀoÓÚåzÊ—í©Œõ¶S®þa æU|>â_σYÝŽSâwû7¥?êO™ëøŸ²“¹÷NoÞväØ.r˜yý¦~cƒ>ŽÕà7×HçðoÓÒ¹íj™¯+¬éeÂþ¿rÀNÇÍ4¯ûOÇ»_C³jë%ß){~ö´LG3ÒᬧÔ:£ÁjóŒ?ŽXèëu´Žø3`÷o·’_%öK¿jü¢òÙóõ¶§ìt´Q?ûa)û¤_¤b£¬É/ùžtÒ»äñÙò­BlË“UóY¯7k1nˆîØŠˆèëO¹Ãe}óSáPïžÇ»Ö/Vò¤“?‹°Þ¹Aöó~ÝžÕ!ÑÛC£TýÓÊcµšÇ‹u«[_9ØÚ{?•åyj©V/ZUyË~¡ä“YÏ.ZïØr˜?5ÜŽõßÚ 3ý„ïI¯×÷3ÿÇŸåi_l=&ç¹yµh,½ÆÝÈõ'·RÝ9ØrŠië�•EÞüéù*²c½*Ïü¨YåoýZ‡nó†o¡uÔCÒ·/éýO;•ûd~ýáä—¿ÌØËìtóy_ñië³>ì†úuº¥ì[yÞ_|æuß º?ÿZî»û¿˜~lsýwéû,kÎ7Ojû,Lî×°¢qžO®¯Ígw¨xpvÖï¡M`i)ö?÷*H/@>:ا°å·Šo‚.Mð‰6ö)<Dò é L |¢g€ãÄ?�9`ü耥°HúAOÓ—ßAÏ ‚<Ð�¿:‚n!ðs C ûÀ7AO‡HzŽ(Ì‘ÐAüN¥@z@à §ˆ:€ài⃞^ »x@W¦u¿Ÿ~ɉ“JMÄ|‡B|tß8Ò~tä]ðŸø Í‡á?ém<û¯€º4À'ºø¨ÂñAÏ�ÏþvC¤t8Fz@—)œôäá°@z@Ï�‹$ÿ˜‚à{ž!yÐy`™ì‚ÎM(œŸèi`Ó ÿ}°÷AØ¿:ð!äø&èÀS SÄ]ø{øEz@³IØ!» §ŸEºˆÿüø˜Â ¤tê´ÂÆW‘.ÐÅO*l¿zæÓ {ÀgSÐÿY…Cà› Í’ÂqâƒÎ'É.èâ—–È.èü?),ôÌ Ù%øó"üù†B|tê[ð üèâwFÀ/žùü%ýg¡ÿ‡ ¤ô °Dò?‚^àY² º œ!ùƒfj ·ŸgaGÑð+œß$ºÎ‡x¯Wt ÀJ•èéE [Àg‹]ö¿QÑ… #¤tðF…ãà› KÀɃ67*œ"yÐ%à4Ƀ67)¼@ò Ë›hb ?Ûk£|ªóЬ]aˆäAG€}$º´EáøDoBz‰Úܦp|¢gn…>²{ìÞŽp² Úì@¹×ÕyèHÊ üèÈ.”ñAGvÃâƒ.ÜÿÁ/.÷ÁðÙ›Á߯ð ñ@ÏA” é]¸þ׃šý'ø>ѼB)ÿ‰šEá?ñA›ƒð|¢sqø>ѹaøO|Ðfþ“ÐìAøßPç¡‹ÿõüèÈ …)ðs q…yðMÐҤtàÔ’]�^ yÐå÷ÀßEðó½h°¾IôÓH7ñAŸA>ƒ_xùLò óÀɃ<‡vMò gž£þ~~þé"þw ¿û‹Á_¦èÈU ƒàç@Oö‘ü E€)ðK ó+Žtq-âŸè­°C|Ðå›Î’Ý[à'Ðh„Ÿ Ë·* 5R?�þ ßÄøÓ©p |¢ƒ]Hø&èâ 'It¤úÈÐ^Ø'= óqŒCK t0‰t-¡ö:«°‡ø S‚Ÿ](*œ ý S§N‘<èàÇ‘ÿ¤tù gÀgŸýH­ƒ>‹ñö‹Ð>Ñ©¯¢þ€Otäk ‡À¼�»ÀIð‰f/Âîo½´<Kþ,ðY«VÜCÀ°±YaÐ�¶�ƒÀÐö�û€cЗŽóÀ `8 ,§€%àà4ð,° ¼�œÎü9Œ�À!` ˜ŽóÀ `¡Ù{Î'÷?«ö¯ÁÏ×àjð‹5øSàû4~Dã~â*…tÚé{ íSQXß_’êݵd¯I¥ûõ ›€×§~=h:/E¹5G~ýê%¹â,áA%k\¥ž_áò à î"ûü#ÏD•¯W–¨M¹Wı³• éÚë€}oòò‹mÞtðF#û¯^RéÉCþhÊ÷ÐM›ý[Ðö Àþ?SH÷%|ˆ¯Óµ>å_W¯oô™Ex#ð °E«§9ðà‡@À `pXÔìSzæû¤P©Üs×è‚F5º¤ÑÓ]Öèf¦—h´¡ÑA65ºO£#ÒèœFç5º ÑE.iô´F—5zF£Y‡–~64:¨Ñ¦F÷itD£SÓè¼F4º¨Ñ%žÖè²FÏh4ý°ÏN¿FÔhS£û4:¢Ñ)Îit^£ ]Ôè’FOktY£g4š~¨h§_£ j´©Ñ}Ñè”Fç4:¯Ñ.jtI£§5º¬Ñ3M?¨´Ó¯Ñ†F5ÚÔè>ŽhtJ£s=‹þ|Ò $:²z6zÇjÈQ8+»vî¼ÅhéŠõÇ­„±µmkÛ¶M¡Võcm™ãòHr[&›V8D%’ÙXÛáÄh[ÿh|x`S|€IjÈÊ ±¶ã Sa6­Bð²æa騰%ñWoGxûÖ¶Ôp698ÈÚÄÏ·™ X[66Æ¿Å뉸drÀÊZ £‰cñÄ@X¾’ÅÃRñÃê×¾2*ý}$Ê- Â(W']µFâQÅemâUWN(8œä¦3*$ÓŸáùMŽˆš°˜Ÿ‰9 ¢yŸ›5y}t\ñiÞFXßà§ÏóÄ4è7|ŽCñižG8Y_=>}Äñáe.û4$,¬P(ÜXîŠOó1“©9 ŧy%!Í#é£ÏŸÅíš—\þÓ¼pŒòá~ Åëï_uŧy!¡Áÿ}¬2ýÐmÏ[Û¼HóP=ÿ(ý ->Ík i,d®«ÿü²½q‡MÍ_-;Ù;µø¦öü°p•WÞÐâ?¬ÛßëÅòÛ¼óCÝŸwkñËÚºz>ÿ'Ÿ¬Ú:ûÌ&¯¼^~->=%l¼zîøŸPñSo=ƒu=á,Ж3oý§ÏóL­ñhDë¶©&ÅiÒäõø_çÿ¯rŧuÝ4âëò:ý}å¿ÿ,âŸ]`üŸhñˈ_Fü ZƒÕãÿœ©²§øg®«"ß^w©üJ³Oë©ëæÿï´ø%šO ¾^ßôøâ1Œ;þ4Í?¿Ñ‰/Eíù âOÏ?üñ‹«0Þ¯Rñ ïϽ*ü¹B³?½þ¯Á>×<öWúâ±\«âOjÛ\züëœøòS^ï?ãýYZ…ÿo„ÿAOñ¯×øú>Ë —m÷gâ·hõWÜ}‡û3»QÅŸ}¢WóÿëXeÿ½¤†ýSq?Ïø7×§ŽKç«D¨ãOUå×Ùã¾—_oç^~ƒ=N{ù‹ìñ×Ë_l«^~£=^zùKìqÐË_êŒsþ2{Üòò—;ã’‡…³_ëá_i^þUö¸àåìþÞË_a÷ç^þÕv?íå¿Æî½ü•v¿êå¿Öî/½ükªîÃÔ±×Ùý›—ßd÷[^þµÎúÈÿÎîg¼ü×Ûý‡—½Ý/xù«ìöîå¿¡‚'ªk=»X±¶¼ÿu5økjð7Ôào«ÁûWsëô/…dìŸÎḉ|W€ä㹫êòŸIOIÑ&äOçªÌŠÄÇóîøâ ^oi?ú—…<×Âî•>Á¯ü¬«ÁßéƒÝÇñ¹¯†ü0äé\Ù}7øö9ð?]CÏ×jð@þtxóów¾Ê9ªø,ñWç¯òCÎm•Á‚OÏ…f ÿv’ÇsÔ26äcþê~ƒ<=×Åñ9vª†ü§H?Î;äÀÿ¾ä;ý¥å_jèùôØÏçÑm­¬S|z®5þ:ðéùÙ>=/†ü>’ÇsW`ÃàÓs¹ø'êªûù·¤çè ê§ëT}žÕêóWIçt¨þü|û¼øW×WΤ¾z¤ ç àß�¾}nü7‘<ΛàÔ×(w’Ö[îï#ýt¾üøtN&e’§sCÿ?àÓ¹Bš'ú ç(ó¨ŸËÀ·Ï»¡¿jk¨î¿Ù ò¿QËÿ»¡‡ÎCQ>gÉ.Ρ”QŽ>·"ùÏü :? ø/ÀîÍîÈz® ù‹ ÕË÷·5ÒuÅ"è¡s£à¯ŸÎû`úÅÚ)èy ùÓ y:—šG~ŸÎ7å ?F|œ“¢|`Qu??ºHUúçŸHÎ}Øøgòüäý‹«ëÃbä—ÿOˆOçPއˆOçR!?´XåON+¯±ÅäóäÃShùùÉ~~ zìskðçW5ä!s4N]G|4D:V´|:7Dåþ'Õõ¿òö9äÿ@ ùã§s”Tÿ?Hzèœ æ¹ÏŸÎ…ÁÏ—Èœ¥þäÿÖ°+~`/Ó…óÌTϯYR]¾òt®ú½î%ª¼BZùÞyÛA¤ë(ñé|7äŸÎßåÀÿd ¾Jò»¼ýÉÿ¦tá|JŸ¡ø³ä?ÎÓzpÕRÈãüí§u/­n÷OIþiï¼îÏ–Š~æâ%}ûW§ss4ù(øö¹KÈÿôãœ&­Ï~JzžñŽË—s…9È·.«îÿÈÓ¹ï�ÖÿÝ5äÕàû1òÿaòç ·\>J|œG6¡ç;¤'êm_¿&?1žRz¯]^ÝŸkðo­Á¿{9ü¹Ïk7>K Þ¢ø9âÓùpèyŽôà\Ø d|–äqžä_�ß>Ïþ÷–«ú3½É[~DòtŽ~ú¯ ~^ÑÔoß>/ ù}Ä×Ú×1ðé<;åó{®PíÚÐÚõ“àOhüO~:¼VáwH?~P†ü9èéÓô\„<ý΀æ{¯^Q½¯¼òø½åó‰¯Í‹ZÁ§ó˜äÏ–+«ëïªÁ¿·?ýöù>ð¬!ÿ~òG›Ïø_Rô4üü_WVïg~v¥ÊÏ!-?=t~ŸêϯRòEM¾ýªêó¥íW!]oö¶ëƒàÓy|Òÿ6ðéœ;•Káªêùðy’ǹÚ ½Dú‡½óÿ_ÔÐój þŠ€ÚËÓ?kÕå·^:Gæt�|S›¿ TÏ·?—òÎ~”þ_@ýΦ ~žôkë‚Ï?ôû äóOˆOçXÉð@<“²²Ñ¡¶¨ýî:þ']ÉÂÿo§ËˆPu/ªøsp0ο+®¢²/¶’ï²ïõ G“#áÌh"|$a…UX8Ëâîµ$„²pxwø¦½½ââA!G—„ÕŠ£®ðq.˜³ï¹IÍÑ6ÖÍñŒpéD6œ6Í]{z;w†·´ma)õ½ðÈh66év‡ŠW";×#u ¤Å•­âbŸ##)·`-äuÒ‡ý܇yíâBƒ2Ž[¹(žØB¬ñü—¯â‡$áLÖJgÃaÞ}4¼/v8žáé¯/eÜ—ÈQ²èú£ðh">6‡¯J÷‡,ĹÁt,†‹6é탩4ÏÜÁ…ÄV•@]$–úc‰U  Ìy‰‚L£ªís'S¥‹ §;¡Ž$âÔ¦l¦¬3ž»ŽxÜèÐ@,Åy•Mˆ»U衺–Á®Þê>©ùkV÷5Ë‘ÚÍ¥†ÅÙ Éó\ÿ)bÛ…2‡ƒ}¼žÄÒG]wêf²éDtaM×8Ü'P³†ð”o–¹¸Y\5‡"ñªt&j»íˆÒ®’DšóÂG²suXâvUÙÈhbÄò$Ä.yÁ×ü9Ü5G9p_{ )Ì^§_¬Òãá*0½ó:˜àI¯q•…’¨lW‡¥t[œºWs÷ݽὉ{ämMŽ ×µe"Ë€x…´jžj s%RIÈ»S+º´p8:f‰†-Gwˆ“sÕõ~Y­ ‡y†$’ayÃOÁ«ie5÷\e¦ßiç.p›Cs—¼1Ìu9ž¡QjA=ª«½¨ÚÀ¬~Þ…è•XyŽú"zµ¬x_oçñ»Åuæµ”‡7;ƒ©Ò4DȆ¬ ÷7™Z¿ö0'*¹ª|{DÕ«™¾xb`ÿñ‘þäðÜY =ÉÏðªª×yÜ”'þã¶FqÁå`f!îÊ:êm{¡¹ W Nv§— †)ú<­‚2:?Ì[ÀÇ eÐ݈ájî<™•—ÒÏ•Ž{y¶'©×BÅ\"–ˆ ÇG:Š©V÷g2j 2§GóNþh&Þ, [f¦øKõus¤££¯wçP,ú@ft¤¦X2Kˆ"‹%Žj}N8©.ã#\tÈ3eŒ.xÊÃÓ‡­çå¨?ÿȰsŽáåð¼ïxÆ)û‘Ø÷ÌÓžE>¸£ q?ÛûK9çtWC5wš³Íʉ•Ým©ÞÛ™h¡Ï§«CãÞ‡‘÷a>ñ[@vuΑ]²oC_,ûáùjáṇoÕ†q{Ü\ó_ïXòv.s ¢ôv©ˆ“¶[­lóv;TrsZg¨”Ù«{t£;oÙ±hfØ[F7Äxú~+åÑçc´E•¸2º º¤òq¾š,Þ¿wr'¯ òä­'yFÌ;�‰É°'÷ø¤j`Xvs´e9ëœW÷\Ï3eõæ’žG±±¬¸ (¬®í™£bÊu oÒÑÔqÏ„ŠÛÒ©Þ§Ó^NZ+ðšžËKdç)9Ù « #>¢vÍ‘õ×AœŸ7‹ÅZºêZLp„íÿPK ����v©Æ@���������������com/sun/jna/linux-ppc/PK ���2ii?}ì8±c¤��®�'���com/sun/jna/linux-ppc/libjnidispatch.soì½\UUº¼稠X4CEI…WºƒIHy‚v(¬cPQaRQ@€‚""`ó�çi÷"Xƒ…ˆ¥¥eM6£“ݬÑÉfìf36™ƒ“ÍP9sû±ÉJ=ï÷YûÙgï³á zß¿^ü÷Úk?ûYÏzÖw=ëY?öZ³sn0E˜DàÏ,bñ?"f´âš&L«bd|šH£E’¸LÄ+Ïu¦‡öÊŸ£ø‰IÞGðOœsPˆóíˆÍÏ#„ieˆàŸø7? ÿNê~‰ü“ÏJë>ÍÿèÀOˆÝüSŸß&ÿÓó§|šo¼e®øzzCÑšÖû?}úž¶Î—6œ7îoî·ðÌ‹ßTüðkб×%üHX~|ÿRSLBdÂUÈ„%2FLŠwGšÄu"k‚å§Ñ&‹øÅ’–(ߤÌT(!á⸹·ÜpÁ ?¶¹D‚i¬%éßÊ]‰Ýñã÷â““ÀÙâ¶<=Ú4i™„§Èu]BÌèØŸîM´ëþôRæˆÔ[äž{D̨Q,‹þ/uüýTNÃïü¦á÷øYñKÇ/¿Yøeâw~×ã7¿ð»?*§9Ìã&ü²ñ»™ïoáë­|%=ßÎá;ðËÓ¥?¿yƒÈ˜Ï×»ñ»¿Ãó2¾–ã·¿*¾_‚ßR] ~uø-ÃïAüø=„ß üœLÓ€Ÿ[(eéã¸&üVrøaüšñkÁïïU|]ߣøýß·ã·¿Çø¾¿uø=ß“øurüzü6èø=Å×Mø=ÃágñÛÊáçðÛ†ß |ÿ"_Žßv¿„ßËøí¨ýê¹ÙY3Ÿüïë_Üüô}¹7ÿÏ‹üoí9ÿ|tWâu©¿9·>îæÊ¨ÇoŽú«¹î+Ï7m£¿ž¶êÁ³³ŸÚq›íõ•·{/u uߊo7]½õ¼ß§ßÝðÆ×µÞsãoÞúÝæº¬7ö«ü§ÿóg»éº¿%Ý=¦ø‰‹Þýð“ÛÆüþÝí·îùÓÕo¸þaZñMÑÌÚù%—Ÿ=ÿ¹Ï–Lúù//3ýÛòÇÎ{þÊ]Ö«Zf~~GÑý—ÎúǼ«þáºõš³Ÿfühóy™¿ý¼ÎŒ§ÏÛv鬣kÇœºüÍû’ãdÍl·ýµøêm7ÿk]ÿ¤ãéžYy*çÑ™eIwGZFíH[;yå•—Î:xàýüø7.½6®ï©?ßùÔ±ß_tçצLjWÑKnüËþøü¥Þsõ4Ó{ï_ß•yøl-dyó`Î]_>ê-yôJwö?ÝøÓs–úå½×5-MX|ãÏ^8åw]Wü}ÚoÖßY÷Eî|!ý±Þ»/˜‘5ó¥¨Q¿yê÷?~|ÅÔ]'>ü0é÷—wé¬ÿ[—5sÓìÆ‹·~=lp¶}òêz&}rÎÿ¾ÿyÖÌŸyŸ]{aFÖÌÚ^¹vËog7Žþò§•/ßÛ:ªeÜñf_9 ŽíÉ\té¬<úìÃcò>ž2;ïÀĹ y]GŸÚqî㟜³oæ®_öæ½·8ë–wn]ñÆÁÜgWgýWDÙƒæû‹žœ³mó#w~õ_¦[7%¿2îÀâIÿkFÁùYY3_™óíC{w]0ñë}ǽxõŸÎýí¾ÿ¾òÛÆ›kŸk»yùå¯LÛ_8y×6O¡ôoÕw˜u;œ¢ÆþHþoy\½WŒü9µÁÏÅtõþ<ùÿø«‚Ÿ›ÚƒïϹÍpÿ„ßý†û[ ô×î'ß?lxî2äçÍàüˆ ÔûS.6Ãûfÿ¿žÇßG}l?Ð&(ôUÁÏÏYe _|ñ_Á÷ãèWø=dx~£áy©ÿøex¿-øÞ²^½g<Œ7<ÿGðóñŸø¿kà¹zŸ ð3¤wN¶¾ÂÞkÁ÷цôJ…éA²¹ÑÀ'ü€Ñ•0Á[P,Yx>ùG»°¤Ïs”ûñar$hôc~ sÜz~>îˆZ_~$ëËX”ï.<[”ç–ÍB´RÖW+÷cOñÛÿh¤ý9µýÊ=Ùï¼c¸:•û1ÐÇS§‘^*¿ÿ:ÞÃó|å>ùýùnòXþ?Ï×rúð…Öø´üYà|ƒûÑåʽé'BÜK~’zÞ»µû¨§…øÚS‡r?ê, ‰öÞt‚Ó{^ˆå6Ðïfùÿ*ÄЩ%‘ï‘V.žGøX^øÏŸÂûɬ߇¡ßHM¿ãþ)L&*Ÿ:å¹m䣔_§÷g4µD_Êú¿Dˆw,š>#ö y¦ü²>£<Bü²UÓ¾Çjò'Šø9ü¶]ä¿äqþ?>NkòD\Œû-Ú}4ü€ÏÓtúG}{>…)‰ó‹ò½›Ò;ÀúBÙ>æÔá ö®j§–?êÿ}ä¯6s~à?½“«á%öóüšQûY_»…ɲMËï¨ù(j«ÆÌ×pê4ýšöC¿±:¼Ã¬=©§?¨"?&ß×Ú\ =¤ÅK§µü™î¼E¾Æ«}Tž|?þgÐ×!]}Ÿ¸)YÃï9¿-äYðå:êb˜ê°¼Ñ¿â ’ßÎù¯‹& åY}FÄrz— Ó帮fy‘¿—µòŽBûñJ©¦±h_öí×ôwnäéTì“ÿŸ>Ý3ݸåôÂyd#]œ>d) üðý·Èï~-¿´ÿ™«Õò?oÍ×êwô7Bß­•¿e‘_^ëó·Çó팗qà¿Cão>îÆç7âgpg‘¾‰ññÜRÔ?ÑÉùGùìêÔò{NŸ0Ý�Ô”ÆòÝ©¸ñjý5=ôzux½ÛªÓ?ôWLø>Èò¿ûÖ©³_¿K‹–ÿsÞ¦« ãyÔ/„x¼Wã7z \ßãÚ½ý–U•Zý4}yNhòœ›&"¤=åò{¸åËöѦ¼¶ÇãÀí]§SÃ×X´û¨þ²=ƒàÆZúcñ¿qݬO»0ͤþæ^¾Gß x0bzض·¡³…ñ41O&Ô×iòŸóaºýÖ®£ð~ÛI-ýHÔ‰ç€9Û—„DÉtœõþÙ½«5û/LçºÐW>Íï£=~±SÃÿX`ã·ÕžÍW@¿û5ûyìÙÿ\®Oèûýú‰­œþ/a?’uö}ªwkôW“™ê7×q¨Ûû“µçãPÿª£5{yÎFèg­Vÿ-ÈÿêÚÃ(øs;²4ù#á?m?¬ÝþU²N‡´×«wjx‰^(L³¨þgp~ž¦é 73>Ïñ Óµ¨¿–^¦?Gˆ¿@Ÿ¦ÃÌí飭š~Í}¸?©ÕwËrtíÈE3ÞzÐm%{°IÞ.X¼¤ª°¶®¸¦®°Pί¨ª@d‰£˜‚Å••‰Â›(¼½lAEm]YÍõ•ŵµeµ¢¶®´¬¦F̰¦¢®L,.[\[V'*—T-X¸¸ZÔ•×,yðºå·/.µ ÀFâ9=š?¿¢/.©µUÕ5UuóEUÙƒ7?P|GnˆKj+˪À¿†.ül?äÛÜ%x±¬F½½¾¸²òþâ’EbAY]áºåÕe…ók–,.¤´èN‹—¡’%U”ÕÔV Ûó+‹Pܰ[PV“’;‹+—• ™-$º¬¤nY|í–⺊Êò˜É 7Ì¡ ýn.®®.+ÕH2KKkÊjk)BLà©Æi¹ç´ Ë’êååPW!õŸW^SV\*ÔÜVs¿ú§Á÷ÖàÛ«¦i÷Ë!Ÿ–¦Ýϯ\R\§Ý–.Yve™¼¯®)«.,©˜€  «–V.)Až—Tɧ%È-e‘K‡t£Ë¸¢.G€¨n¶DA™£®¦¸¤®ð©öù ‰º% 9i§pÉý ËJ Š”c²=°¤Bj>¸¨é¦FÞ‘fQÞ‹ k—U.¬*.TVT=°dQ™ £ðT·*ɇ'ÒŠ/<]–¢ÌðD7ÈOÄA‘ƒÊ8ɤ¾P$%@]]™r¨aJ¤,jyêåù5eÆW)jè—T£Ö—VòE±!%¬\R[*åR”ŠªÒ;–/¾I%Hk—/I(ë|aáM…W_7gÎ0¨®UÖ°¨nÕœaQÝ4,ª;ÂPAceŽ[çƒîº4d“†TV€(œ®DáT §©�Q8EˆÂé)@NM°5×-¯ <jÊ‹Cã·ð*ÍZ†c’UQ DI]·lþü!hÃjºÂZ¥1 ÷8¬]¡®|IMXÜ„¢€£psÙâ%5ËÃQ„Õ{íz¯ZëµCj²v(MÖ†×d횬J“µCjr1Œí’’°6:4w8K懱us²jÃó<X‘%Ú‹«`ÆSÃ׸÷0J&·fIèátUÔÂ'QoBÊZ›;$äZV[Vó@YNÀ7 “¹áS9‡£ºSqLÃfæÎ¹¾¼¬dQí24e·Ì)¼µ*g ÜÃ…U¥µÕÅu%åVVÍ­ª IF1ášÐeU5ìþ‡1˜ ÁÍeuåKg•šexD… 5ÿ‘£)²d„⫤[–B²½ŠüÒá)ɇ'•¾J€TŸ™°•FóÙÁ‚øH'4à­R÷HçMœqkð­ÑWgß\u¾Q­KÊK˪hT,^Vµ¸¸Z,^„‚@WkYUeEÕ"QVUº5 ¦PäÐ5TX#Ê‹k^R]'ŠKJØY/«z@T×É~Gáâeue*­E†(ð§ÈÚåµèQÍÅ÷“µ &‘ø›߸ ýHâ¡«Y7¿VÌ—.^!!º´¬²b1õøJÊkèRUBEÅ«»eîÍ…7äÞ^˜yû…·Ï¾qÎy³o¿CÆÞ8 6 âšµ…wÜuÇ~‰îŽ r@DyYeuY_YqIJí’”i¢°¬´¸®rÞ_[«ô’UU*¦–/Y\6µîAðž L½YEeéÔ*‰ƒ©x]WÏÀHܘ3çºë š’]•ru |µ>>âÉïùSÿ™ñ³îh5B„îN¹Ž–?³á‰ÐÑ(Lÿÿ?Ý¿QBœ{¡àiŸñ4=q4GUqY­Ô¶8·F¹¿ˆæå-¸§ù÷ÐÓô¡÷ð}Œ¢õs×*¥è¢1ð „)rÂÛ•°ÃðN%<ºáÝïCx.~/Ç»> ‹?Èñ«>¬ãßÇñ÷+á1içøn„OêâOq|.¤0]#Ü%Üzw´.>†ã7!«„]Žã0ÑÇsx ‰L¿á$%­y‡NUÂù±§q8 a+‡¡w‡×"l“a1»á,Žß†°Ã»ÎáðA„s9|á<%|¥•ÏaäÝ]Àá|„‹8ÜŠp)‡)/å,3奒ã‰5‡O!\§„ï&ù.E¸“Ã(#w7‡{ÞÄá>„·(á<’qrO"ÂŒ“{òÞÁár„393fî!þ½&0~òH'ŒŸ¹ÇÞ¯„ïÌ@˜±t'éö‡IÆÒ=”GÆR•û1“Wq3®æý Ž·"Ì+ÈE˜1V@:9ÍaÈìQðfªn=އ<žHïG8šÃàï‰á0ðãQ°gºòxâ8Œ´<ñFñ$pzó$rúñ0ï݉p2‡QŽÆä}Ї1yŸaÆä}Õ3&ï#™mF™z“…‘3& QFÆd!ðãaL=c²ð8ÂŒÉB’1Yƒ0c²ˆòŘ,B=õ0&‹šfL@˜1YtaÆdñdL£\<N«‡)>Ãþxš9ŒºàYÍa*‹VSY¬å0Éß¡„ïOF˜1?¥Å˜¿uÙØ¿vÉØ¿Ÿx2æï>=Œù’h„ó%ć1_B|ó%ć1_B|ó%„ Æ|)ðéaÌ—ó¥N„Ù~–ÿeh=Œÿ2Âã¿Ì†0㿌ðÉø/£2eüϧrgüÏ'ì1þç×!ÌøŸÛåaüÏö¼ŒÿùÈ»—ñ?ÿ4ÂŒÿÀ¡—ñ¿�vÀËø¯Dyyÿ Ð.xÿ €+/ãôàeü—#_^Æ9tëeü—ÃÎxÿåÀ•—ñ_ýxÿå°Û^Æ9ÉÌø/§´ÿÈ»WÁD3ðãeüW¤"Ìø¯@]ð2þ+`뼌ÿ Ø/ã¿õËËø¯€ž½Œÿ ´/^Æ¥Ëø¯€½ò2þÂFyÿ Qv^ÆÿB’ñ¿tÈø_Hùeü/$2þRºŒÿ…”wÆÿBJ—ñ¿uÍËø_H20þRÙ1þ{^Æÿ"Ò3ãðìeü/Bô2þ3^Æÿ"ÒãÉÆø_„úèeü/"0þÁyÿ‹ö"Ìø_;àeü/êG˜ñ_IºbüWÿ•¨¿^Æ%0ïeüWÂþxÿ•T^ŒÿJ’ñ_‰úèeüW’Þÿ•{füW’<ŒÿJ´Ë>ÆÿbØ:ãqÂŒÿÅh£}ŒÿÅÀ¼ñ¿ºò1þÃ&øÿ‹Ñ¦øÿ‹Ñvûÿ‹[ã1l—ñ¿õÎÇø_Lò0þ« 㿊äaüW‘<Œÿ*’‡ñ_Eò°ý¯BÝñ1þ«HÆÉÃø¯B»ìcüW‘<Œÿ*”‘ñ_,ùÿKH?Œÿ%(#ã ìƒñ¿uÊÇø_üøÿK(]ÆÿàÄÇø_‚rñ1þ—�Ã>ÆÿÒã¿vÃÇø¯ž}Œÿj’ñ_ ûÿÕ$㿺aÆ5l‚ñ_ ècüWÃöúÿÕ$ã¿ö1þ«ã¿mñ¿”–é0þ—’~ÿKQ×|Œÿ¥T^Œÿ¥$'ã)ÉÉø_JeÇø_ŒvÄÇø_ŠöÂÇø_Júdü/%¼1þ—ÿ>ÆÿRØ ã)Úã)å‹ñ¿”ø3þ—Â>71þ—BÿMŒÿ¥h¯›ÿ5ÐãìOã¿ølbü×�ŸMŒÿØ–&Æ ðÙÄø¯Ýkbüß0ã¿xkbü×@'MŒÿ褉ñ_=41þk ‡&Æ ôÐÄø¯šÿ5(ë&Æ ü“&Æ êBã¿v¬‰ñ_½51þk`Cšÿ5ÀIã¿ö¤‰ñ_ƒºÓÄø¯Aýmbü×�·MŒÿÔ&Æ-é‡ñ_K:aü×¢ž61þkIÿb9½Ëø¯E961þkÉ&Æ-•‚q=pÕÄø_Fzfü/#=0þ—Q¾ÿË€‡&Æÿ2’Ÿñ¿ŒÊñOý &Æ|É̘ aÆ|éGÁ|Ä£hšó ]kbÌSߪ‰qþ�å‘qþ�ìaãœ|ø&Æù”/Æùƒ„=ÆùƒhC›ÛR¹3¶ëN ÌØ~ÊZÁ¶øÊb%c›úY+ÛÔήŒÔìÒJîW>ŠtW2¶ÀðJÆv%Å3¶ÐÉJƶ:Y©`;¢¸ZÉ}Lô³#V2¶h³V2¶©o»’±í€]ZÉv=cغ°’1¼œèÃËaOV2n—C‡+·Ë¡ó•ùœn§ô{lÀF5«øš?€cD³Š`¬™íg|§fÆOìX3ã§åÒÌø©†›?uÀ@3ã§åÒÌø©ƒÞš?uh»›Ù~’ï×|œõŒºÓ|‚ð'Í'9Œ¶ Y)LjÕ(ÇæÓ;Ð"XÏÀL‹:&€ºÐÉa´_-\Ž-°c-êø�ôÐÂã-Àv <yZx|`5ôßÂå¸u¼…Ëq5êHK‡)ÝdCþ–TCþ–4Cþ+‡I¶ ÃÞ¶Ø8Œòjau5Élç00Ö’Ãa´5-¹¦¼äqõ´…Ëz5pÒRÀaÊc‡QZJ9 <´”³Q[*9øi©æ0°ÑRÇaÒƒÃðëZœü.Ê´ÅÅaà§ÅÇaÒa3‡7Êâ<þòÈ }NVÃÐÛ(7íÐÂnðw«ñ´ps¿v#ÝÇÕøÃZØ îSã÷ka7ä¹M?¨…ÝÀÿ#jü!-ìF]¨Sãia7êÅCjün-ìF¹]•ù¸nîÕÂn¤û”¿S ¯NžUÃÀÉ5Œrÿ‡†®.ŒåwaW§ª|`Wª†¡+»î¢L}x‹8ŸÃÀÛ jxÛ¤†7³FºMjx³¨aàmŽÞF«aàmŒ.b®Þ"Õ0𥆷±jx§†·h5 ¼WÃÀÃ9jØ%Ĺje£†Q¾5jezžF9îPÃ(»©a”×Õ0lZ¬îbž†M;_ æ] †aÓ®Rði+Ô00|¡FùÎVÃÀÉÍj¸WˆgÔ0Ê1N £KÔ0p{‘F™¶«aàçb5 ¬NPÃÀÿµj¸Oˆx5 Ü.Rè;—¨aàóR5 {{™†½u¨aØÛ5 {;‘ÃT­;Õ0ìír5Œ:þojö6] ÃÞNRÃÀïjö6Q ÃÞ^®†„˜®†aoÿ] ÃÞþD £.ä«aØÛ$5ŒzÔ¦†QljøoTÃ6!²Ô0ðïRÃÀ­þ+Õ0ðÿ€þ_TÃÀ¿S ÿ3Õp‘W¨aàÿ5 üOQÃÀ½þ“Õ0ð¥þ3Õ0ðïUÃ.!RdxíDÿ gdÄ3­&Ûþ†ˆ¼nƒÕ¸7â™kˆˆô#9}®Ký§§‚:³KDÚM?úÐ.LQvÓ¹k\ wFšŸñâëDs‹‹?QxöOâžqÄüÛŠxð5·‹èô#o—·»EŒ÷¬K´o±Í—Á![—(w]ê²pšÑ®Ò=o©·ÁëírÅx{\ÑOöåºg¢4:/¨Nïôû[úl¶N‘4}²0ÍMˆ™±ÏœDŠ3ÊüÌ-.ËDI šÌN-´ï@Z?Ñ 9dHÈ€¼Äy!#ò“@ùÑë†òKù$zý3Îc$ç1–ò—~ä-äŽs™•<!qÈS¼š'U.â©êÏÈ7Dšš^Iö''9H‡¤?gý‡ªkÒ³óÚqÒ9˓ꞄÒïr W”i-ͨ28‹&-·=%lRG¥9ŸþÂçW”h&šœ·¯òtˆgDCï•ÇôF‘àôNr¸Æ ‹w‘ÿ¸Ôé*ÿ ïóþ“Þwü§¼_øO×ßãà<PóÐvyø*ÝÑ>òxµ£=SÄ9=żÅsÞ·DÎ[ç-™ó–ÖÝI<™DLú‘=•”?Ê'ä‹„ü.}ùOõ*ògv™dÞ8Ñú<:˜°<Ó±Ó ‹Ðï  <w:&ÐóXI7MÞ—ÖnÐv\Ì4}ªLS†2ÀûÀC Ѻ¢Ì¢õBÈÙ!1’0uRçÚ/BÜJ¸íOà÷—Xè;µÑü¦Ô·çÿ!Ò·ç ÿaï8ŸwŠÿ˜7ÛßOåª H7œÿ¸Áu$¶èt?PG¦µ%ü€::6¯32c€žzÎö³ž,¤§lÿÙCº|$#Ió!öò‘üæ£?D>8‘œ]>RCä£N—4c>lO™lÄŸ> Ïì9s¬|@Œd &“­Ë”«£±‡¨ktrç ÐÿSþƒþs@ýŸœ×3c þOw³þcýŸn5È`ýe8B†x–!šeˆÖéÉ6POg 2fý€2ž˜×g(ã·–1F‘ñÛrµ¼ gH#/ðŽ—ìÓ·•†6"ߨ†pk6ä¥àÌË©ÁóòÍ6ÎKœ’—o:éâw¶+ú?ˆv©Wm—ê]âßÑ&%:wMqdt Ú¥¤¢ÍÂâüÓÕÔ6%;WL ¶)ÕÙ9‰Ú%+·KÜ.eY7È69Ú¦5hÃÒ¼vÐûŒÈUõƒ¼쮳D¤å•|Ø_ÁÖ&’TâAymŒ^ϨwE®F<s¡Ý˜$Ÿç•C§)‘ž»žyü¼T}NüœA§xÞ˜õ¾ƒŸi¾�l‰6“Ÿ[ûÐSnp‰Ær”Õ#ŸHzè%y¤öõätx|®ç•÷ÕraÙóÎUyÛ~!âtùÕù6²QîUäÛÀo`ÛTïr'„³OîY®H¤cBžRákŸöQ¯uL‹;]DwG‰ÑНå¯smEšÞO$“ݵ.Ûï/Ò½›‚w£ùÝ›ñ¬@÷lžYøÙUßUNÕŸCÙŒÓûŠíäû(xaŸñU²óI Cíÿt*Cò§ÀO¦§ãé,E¢\¬%¢ÚnõêSI¿éÕ)Á~“Î_"ŠË%«uƒˆ ò©\6ÈrÕ°é”õÕ*Ÿû>‘õµ±ì#*?«ñ]zŽø]¹¦ŽÔ$?ò@ò§£¼Âù~ä+*zûuç©e'ýZ×Öˆ^½ úªàÎÜÈÛr‰õü [±-ðõú5A¿›%Eh‡Í ª“¶ç†÷âPßkÌxïÅ ;hÐ aQêø‘zº.På ôº\нëqÅÙM‘›Û-zû°I°Aª]"]‘m"û•~dW,Ù/ô¥áÀuIÄžÖ?‹hª3­"¶õu×ê1°56×e"fz˜éÝæþ©.s¿m½©Ù%.uO„¿Q$7‹™†Žåú‰¦ªt·¸p^§˜FïȾÜzÑí¿Æg¾:r½.¢ÝSÐk-¹‡°]ü¶©ë%}²sBît§ÿ,Å7V|äpŠHêÓQßnó8Q?Ö”rðw_+"çuSÛÛzòpê“øœÂÑ”¾ó‘‹p …‰÷kþJïëʽ´ ëýå„”ùîwv’>Hòyˆw­Øk-ã즈b„÷#<Ṫ- ÛèÄÓûT7ðÞ%tm­—<‰Ïx„÷[ë©9Jí�ÉCmA9pG¶¼µ"â�üEÊ;eMv=Öný Ê;Ÿò2okdüÇ:c¿ú„\Bö Q"•úó«¸AêÏÁ¡êÒ‰ê¡êO^IÌ ÒןÕV±öâÝßwdûÏÌÖÚ-dj="oÞÝ(gç{)aêUB ^¡>É2 S§ˆ6]¶" ºÜœ¯®ž%ÁÌmG½f?•ú§²Ÿ û–~ä•md»›; ¶¶@/‹÷>—HO&ò}\¿VÚÌA}¤_£MQdwù]"«Ñºï¿ ²=ã‚Û°°í¬^ç¿q'Â˜Ž­Ëßi K„Æa I/Ë t¹º ÐEB—d ³‡à' tyƒóCõŒèÒɯéq%+1ÏFÛ=2ûúòŽ€}ý3ÛWÔ]i_á×±}†}µ’Ý2ØW×0ìk4Û×¶›iF» šHØÎ§¸)–lgž@+RvÀ†Â~3ØåHƒ]Î Ôða»ªØXÅ®’f»Z[­Ê»Z=l»*ÎDìª8}æ;ÙUqú­ »*N¿¢ÙÕˆ–0vµ9Ø®šîe»ÍvÕ¹vâiW§6 •»óÙ)çJòù#{º1B±¯G#â5 D$¦yi7ê^é¼™à“ ëÔ‡Qoz\©ˆÏÅ5Ãê2D"’ê³]œ}OÅžõugïá=Óôƈ©è@êÃèô¸¬ “ʧ|Ò>&âÓB<ˆW8>#ÃööCl7+„í BŽmÒä$°@X¶-h£ã§¶H;´XL˜ú…MöúIÙ(·³¯å­ˆÌ€Ýs4–|Då8º¤±¥cS;ÑÀ»Ó+ü§©|'¦¯ðŸ·âTCú aj¬0?ãC~œ—d§�Û„ç“à׸ xnB?å `ùú;¨=Z­ëÛH›J²ÙÅ7ç0ڣߔiýæ.‰Q”ÛÈqúMpû/¾ÑÚá×þïÖá4z£ö?áxèì"Ê7éPͳâ‡Gôò]/ÇYbUýÈ>¥_Ä7®ÔëÉl øóD»@²¸òó–ˆCÈo5ü‚ÃAmótV }¦]s­ÔM  Õëyé„ìiÔ®"?6%?g>eF¶«É£„`*í¨ÚÆrû*ÛQ]Ûš~äÅ\Ùo@›Jí0ð#ûžÎMSd^©ÿêmÍÞìˆ>ènŽl3ç -°“Ô&Ì~_ú?XA|½L×_Øôÿu!ÐïâøPí<ÑgûÿÏ7dÿBœFûçwè}L+‰¨>¥êc²oɶ6pÛ¼û¡»e@~å|Ö)á¨%âû™ýªÞɇÂFÉ~´³yÒr㜠Ùµ?­ø´ÛR¡ûdw lRŸ’>§{œÓ=“‚] ¾¶Ólv„êo«yŸê `30§%Ó†¿íÜ=EÁ¦NéGžßA2Áþe ¾_Iº z7]“ÔÉU'¦½í$ËvÊ [B@¶U¤ÈfœäSçòôã $ÛÆIb4dŠ…ý¹Âå?›',³¦¸ÄØÌõ"òdI¡B¤„{êk ‘nwêç¢È¯z>ê¹mÚ|”(ruFœ6ø­iÒ§¿þýaûô:Dãæ q-ö·h,ýîAèš t‘ƒÐ”h¢ÁëºAè2 t1ƒÐÄhbÁëútgOèâ¡Ùo ‰¯;¡Ûd K„Æe I¯9ƒÐåè’¡I3Ð$ƒ×MƒÐEèRÒœé7Ð¤× ƒÐõ謃Ðth2À+kº:mšÜàqm3ñI'Ã3un„žÑxŸ¬ó»Ív9>+ü¿J-ñû%Å8Öô»Em¶ÿô)×nÅnA¶+´ôN÷·›sôé1ÍnMî 4Á4Ç¡©3ÐÒÓ¨2=$®€¼öÁæÿ6@ÚÌÎIŽô#Ï4ììˆ]üßc°©¤[Ks£¸^A6áÔ [8„綾ô6H;“ìzUÑ£¢kÅÇBýÛ4†ùjp¹ÎÌt¾`º`€.‚é tÇ t?bº4Cº9ºs™.Ú@—g û1×cº|],Óí6ÐåèÎc:ÚÄæ'W¶Öaàøõ¦Ê˜†i­6Žd.à¶=\{rÂж†âŸOãâ<VuR—F‘·2Ù“}¸Pé<}’üÅg vÙ\Êé”40i¬Vò£ËG¥‘?­©‰Î÷…žNÔôT6ÕÒÇpM¶ÿ½-Ôÿáú˾¹¬çéGžÚ!ç̤éSõsÌu?§Tñƒìâ|žÐþ`xŸlp™ ‹dÖ…S§ G°?:9}gê«R?:]nœ”󅇯VüãË O·\ç‚þôÆNêkS_ú:(û.£Íêº'C_ëa^3;ë'ký®ÖF9©Ìq™êä¼ ÞÏöt”úÎxŽ|šýY­ƒ`»øßOÑ·NÀ}<ÍÙųƒúh]"6¸¿yb¶¾o ^ýnê÷oÐ×G_ /…¾ õ—©¿?òþòçî/þ±Ö_>ñR¨þ²­ÇŸ…þJ,˯Êè]ovy¡7ïVq8ÛHã²OBíh·júÅû Zÿÿs]ÿÿó»ðÌ¢›#Lp5¸ìrŽP|1žò‹ç1ú1bêóÉuMO(ãÄÔWlŒØ }R_—ûè?mhö>4glœ¦ò!9bhœÐ8,Ç(:CÌ;åp$é×8Ìþwó0b7•Æp†ë«S{,ç—{Á›ä|ÕÜ ý½àz2â$õ¨_Äñl'M{ƒÖ¬cRF*?KoôÅÑG9Øtå4å”CzRÊê³â‘ʼvâÙ>­e’6¦í¨8Ýî6Å8O§£ÏcŠu~|5ÊÖÄþŽIÚœ5oˆCkŽŠÃT®m&Ñ—~dý޶‰âX[¦èo+ÇÛÜâDÛFq²í qªÝdíM–öL°`ŠnÝ ö;÷\McVr®ŸÆ]Ý“#¶¡~ï${ÐúºØ½æ2q€Æ´”²3 烙ëGeåUÄYó%LSÇ’©\õcÉy13ê+òs­ãÄ¥ï."=S\HÏçÝ-¦Õ›ªè]õéÅ…77ILCš½3ÞPîïüƒ˜f5‰ ó¢­Øc[oJó^*ç-äøuû±·>#Ûáéqå7žû¦Ã}ÄåóÌrU{îsUz\ž.W¹ç2±Ó.DìËi½Ýin¥üI_ê*h}˜½`_JcÁ¾ÿƒ_ÙøäN‘>]X€Õ^Úe¡ÁìuÈñ?9?±C¿¦!Ócj%âMtàoqO.èí š='Á®$Ø‘:¢ay¢¥¾ñl0ZØÚ,Èî#Z’ôkU¾$'òPÔ¡Ïä'ÙáÛÙ½Ðøì®-Ì™žˆ¬ô×á§ö¸Z[Õ3e\·ÿ%ðH£òo]Ú~u~Õæ}]ôÒølÛN×%ÐK‰2¯æZýwB–×àGPßá5Íãù1®>§?³'›¿AD»ˆ?‹Ïo›Þh’ã1îwü;©n¸¿ðïöŒó÷z¦ø÷x²ý{=‹üû=«ü<ÏûÒú²‘¬-k½XNòU}ÀÇLÖé:>¼®Ñ~O6­fÚNJ·ëtœí?sHÇ3vžIàÙÌ´*OÂ]Ãã~Í:~‰Ãà×Ê´ÝAüð³êø%Áï$ø­eÚMz~­OKÃ*ÏÓÇ€Kç¯:ÈŽ6üæe‡•æ6Ä_^B›¾ÚY&í*êZÿ{² é9³3¸mØâ—F¢^y"`'ÿº‹ìœëÏbO«º…?Eü€•ýÀÉ»øÛ>×Çu¥|7k€q©+AÎ]êJ$4ÆÞu&Ckc?1imì_¾„ntºI §ø#>mÆztBL»-P÷/uÕš?!€o;Qs‘×dª‡Ö^“Pêá§“IVª£]®<ä2tœ™:˜ç•çðk˜à—£Éq\/#ÉÆséÇ‚lÔÏÍYF¥ÒÂÞä¨t(£ó%®è<§ølÿ{:3™wÔ5gü¾[×7{Ù¶8TÛÒ8ÿ#¶/ÇnC9Uûy»©ŒeY£œ©ü‘÷©üš/1ïTÊï,pul›lÆ›ðT_p½¹HƒÏº[ßf£L‘Œ$ÊØ ç“ܚοIbùÖÛ¾O^lšlg:ƒýÄ>Ãü_Ÿ6ÿ'þrþØ/¯XƒÎ·‡Ö¹èVé²ý>c”ß=CÁÍwÙº¾.€/£æú‹A½Š™;%fÆÜÉ Óò~3Ãù¼=4½sÇÆÌÈ{;aÚÜÈ„iÎߤæÖßdËuîûi¬ó³Ûc[¯ˆµ¾-,Ö¿Q¾쓲?$Ë?ì8mO¾A¶ÿÔq}>§±m“kÆ~nÞÁÏ›õý ‰›Re¾BÁÔÑó(®ºDø׊tsÿæé“ü=s'ž5¥EÄ¡íi¦þÍoªú¡ùfà&˜ˆ™kG^oB^›àgØss‘ÿÞ¹ 7?aåaú"aÊó%L#Ý97'ä:ÿ–š»ï¹Ð.l¹"ÖÙ?#¶vFÞ–ÒZ&,k~B:8e¡¶£á—*f¾:9w2ø§ØSê'×Är~Ô¶W*Kzï�±×Ìq»õ:“sE“#õFz@üiø“ñƒÐ÷Noÿ³Ç¡?‚>†Ö B<}4Ë B2},è ¡?‚>N¶Éé… · .„¾/½�ýþ qÂ!ç ÚŠé·÷÷ÛLÆ<RÐXÍéoì`M?êBZðØP`,cÏÀ±Œ×` acFkòíâϓձ!ä—¿°c¯œ?ðŒ†Î> ÛŸbµuOÔí”cJ?Ö„¾é©tåû?)cµ¨²/ÛºcÀZÔ‰"V~Aó@Òo.×®ó@ÖÀ|æwY³Dã›»ÌAk‚‚¿a>¹¦]¡‹Œ2læ|LøæIùNÇ׺‚ÆHýíMÊ7#•¼ÕôsÍćøŽœW /ñ!ò’„ôV3M‡>/èïX·Ë¼œmÕñ‰ ç™i:UEgs;”«]Ç'Ô÷9”ÿV¦é6ð‰Ô½Ÿ¢üvêhÒBÐìÐÑ„ø–…Ê8@“¥Ç = ÆSÄñkÇÄk µð ;]‚ýÅ?­“ý1øKôÌ.>ºv/•Æa9­Mú<·>)ÈŽÆày?ߢ>?.¿S8‰gü,°öàòìºFÛ.›(=i‹äÚš?ãùN5vñ¡IÑñéMuú µ®Âwqþ5]×O“k³¬>DõšÆ![_ѨǥdOrèÇÍaƒÆmhý/ÍÇK_|iü²\¨7aæ·ƒûbK°ø‡7‚}À?¼¤ù€‡›B®­¡q.m¬²|tWp:þr­ò‡{µ>ÊnŽÌàMe|Ç Ÿ—­9êñ7§u–|ŒµjyèÆµŒòüY‚b[WÙÕr ²¢< \ÏkÞßê±úrЗËO}Ä?¾ðýËMIP9|0Y+‡?~9|ø^èr8øV÷¼þøØ0Ëá„ÕçÿƒÚæåùFÏrnâ˜âsB·Ñ-½´V|âpsDðq»ó|èŒKH©/º;¶¾‚ýÖ&›<4 éï¡>5­]r_¹W™£]cEµëys­Ÿ£1LçÛcêìBÄëhb™æX÷>¡¯wae¹^IG¾»Êœ÷Ñ»{'“ ûéu<žf KÝA¢ë ¦‹ÖÉ“Èò'yñ¬ÚÀ#ÏH|~†ø(aÑúŠgùg^ýÀŠm ò'Çè÷i4ηEZÞÛ6€ÖB´åo èƒ×)Z †:û•[¤,g5³é;ÒA}&ö‘œOOdmÓÇhí íü6Ý¡ÎgÛÅ{ed{óJGÏ¢þµb?ø§ºögຟÀüâ‰k¡†5.íÏ%Ù§_®|»¿j­s¹ô£·0F·1Fw´m>9ý†hFâÚŽŠÕðÝZ‘—ä£yèF¾7µnåk®ñ®Ñ"Ã}¹ò »\ÛùgQ >¿X9&=Y®%$Újê[ÁÆÔQß ]ëzýVš?˜(DÞæ˜ÖQÚX2?Ë1cÜ?ežéý3èWÊzºUX†ñ^¬\·+Þ ýÎJ¿F¹[ò¢bf¼Ó'‹KçNÓê'ã½â9ú÷ÈFž”cÔE"RŽÞçÚÔxá›ô[+ç­™ážBß¼ö§°ÛŽû´úÉý)'‹ÑèߟƒüµNq‰ó3׋µrÍÔÅ"Å)þGõ•Ð×­¤õ«îr‘а¯?ŹèPŠ]üþfZË@v7³k,­}¬&›Bcµ o½ Œ¼ÿ.埞»+D¤{ºˆ¬_Ô~í¡×;Òž‹FWª-³sl2t^ YãÝ?vçóàÿY ñCŸözFã¾é+ÐG#,®>[æzÉäŠ6äé¼Ì®qSIþ®q‡qµdvE[ƒÇ£ÒHÍãD.ë�vó^0Ë2¯„--w=,RŸ¤S;ù_£4š#•wÔußêx‘­kœm$ヶ®Èµ21¦èzL,Þß"õ%Rên ô÷I×T>„Geüåléàz8›@ö�uô(Y‘AÛR™þº\KMãN6mÜé½™ÁãNþ,ҹʓÒ$Sº ï@ÿ-d»?§õÖO Í3Û¦OO¯Ð¾?^–ãr”õO¨¬¥Ègã¨ßLem§²®œÒ¢¤EÏlëÇô·V(ãú$C+žÒ²!­8Ô‡j½œêÚ\æßLéK›³Ó|J7W/}ÈØ¸µ?%ÛÿÎ ­õƼœîƒº“Ê™ü8JiÅËt’è§þ¦~% `“l4ñ~5ðZ ^•CëôwŸÔéé|ò£iý13H99äû¨7àKºôÿ:ö³Y_`kd[Ñ ÞcÍqÎ(ÄÃWj 9£"N|ñe yà³”à4¿=y÷÷L3ÖæÉ!Ò¬¦õTß3ÍCš'§ù üŸo~Ï4£ i"Íf¤Yô=Ó´Ò<6Dš±ÙþoŽÏ4# iö‡Oóën¤éøži Cš}C¤™Œ4-Fû“íß7¾¯Ëô˜¨Ý‹#ÛNi;Ç}–"ù_«ÕKâGï‘íÑøž:˜íÿz½¨¿°x/Žl�¥…öXH~-ýÔþ]‰<ÉçôL‰{÷®òžr‚oÞ |ã|5[ú»÷¤žµû¿h?NÅ¡­<1ÀöüÊ|Rò¤4ðnãód_1Ð~uïoÚví[70í¯:ñn¤º¢ô¨¬7Ký0i›ßþ<VêèoŸ£;ßS¾ƒ<[m×&¾BÆÔU²== AÝ7©ý²méÀ­µ˜·•úö´£?‚ÏR*ÛÐxòYþ¢ù,d»ÙeûÃ<2è1zï“ÃXòWä7'‘vÂfŒ”<œØSœÏÛSdÿ’ÚÞ×àË<÷ÛÐ'Êw/ô•k€gÿh«‰–âÝ7"ïð'œ« ÇXäçU³/hü  Ç~òi¶2]­Ì_¿³>ôv¹÷Éóþƒrï“ÎQK}LTŽo…ìÉZôíÛ´>èÛ³³ý_å÷ƒß¾*¸üöD­üΗ¡ç¤þu"Ûÿ¯f´ëB_÷4ì|Y„çV*w×ã(‡0~¢­óËÃÖÑÊÚ¹¾>²êCöTXÖõ+Àc²ÒVgû¿ÜDiz E¹÷Yí] rä·¨‹,»ØÓB}`ÀDý¥]ßw>pQM}µoC§IÔ›¯w46âã@ÔÏÙj>­/ËîKÄhò7P~çdöœ±Ò<2­ä2Dßö7—Ð<°~&ä-Wë$Êb.ÕG<Stógé‡f¹ž•cK&–i)dªk-—c\‡%Å'/wmm‹ê“¼5]áuz'ù»”/à¨Üõ<häóß\ä¢>ÍI³–øHüƒéSòyN¥}ûïÌ«RÒu+tšÜûÞåç9ó´÷<~–ê2 ±ÖäïÓõWʳý'K!Ë•ä×ɹVõ»­s­';Ø6D»)}0uÏ ý(âo‘넊+ä ]±F'qò'äŸ'Rð-£Q7:©mÝìï Œ—l=‹ÖÙNiW…ùV(BéO;Za#’d¿·Á•nÛAýBgTRP€ß- ûnðºÞ,îw;ýnu'èªÀ7S¿prZuáÒbšêaÐ ƒ¦r4¥Ã Ù> šòáÎUÉõÆ£ü=yQ£g©cb47,ÇÅ*&8Ô2N?òÀ6*_å»Ïÿþ ÜxÔô"arŠ1u¤—ú"‘:³ÈQ¼Hxcó§g>åïî·IÎOÑŽì¹ÚQ?úúsŠÓÑvò\Éã:ëBâRþD$ßý°i,Û¦¤ñÇ×dê÷£C­×/¢­‹WuLmóÍáo¥b¤,c”yF’‰x“\õö;*†üþÑêêr9ÑßÝ {^ñÃë³®y­Ëöÿá=ÊëÞkÃæõ»ð/ô‹þ°”ø÷þðüø€?}# ]í&ŸB›Óé}keøÒ­mFº•ÙþÖéù“_3Ô<ïÒHDÛ‘Æ51Æ¡¾•Ø4Åá|ëjÍÕž’²ßNÔϹÃaG5{íâõ—ß0À–R}2=W‡ú&ƒxç™ðXý60?MûwAîåíoX”¹“‰–X9&zÔ’¬µ;–´öLKxǶ[âÛÝ–„ö–Ä5nKL¦GìÈöÿª‚Æɇ¦½¹hMqf›HVæóL&»xu±ó!¹f&ñÑÊ<Û«»ïU¾'žÞh |»KëRGºoòû.êh¯Õe‘ßÎj{ôl&÷4NzGÙ+@ôÒþ?®[„²Þ–ö·‚.tãÙn{-Êxö›W“tí¯íïRðyõc9çÞ#R‘öm!Òž´OèŒ|±ÞB+ç±tG L¸,Ô²!Y¨<hL=ýHµS-eÈØMßVXÒ²ý¯¼'×{D}Jë¿!×Q.‹**»øõ•ƒ•Ãp¿£Ï›×Ák—ëÿõË¡óÕAë#Ô9»rÏ[æÔùÓ%kyÿuï¥8¹7ˆ_D¯¹LĬA?|àÜÚ®ƒû»ÞÕú¯oþÜZϲÐ{ìZ®õkv-ÎÜš›ò°A·¯í±eN€_.û–ðŸãäøbÐ>�~W°L¯QÿªRÚØ²g²íúIvÀ¾PÝWÓbG¦ïªÄ‘ë{çâ`}ï¼WÓwÏUÃ×÷k£Bë{§nÿ“_ùú¶ŽLß»—²¾G Ãʾ‘ëðW“ƒuø«‹4îþûðuøê¡uøË·4þr×÷ÐaÚÈtøê•#×á¢Î‘ëð•OƒuøÊš_Ý:|þº>´_qk:|eÙ÷ÐaòÈt¸ë³‘ëpaþÈu¸cc°w¬Ñtøë²áëp×ôÐ:Ü1SÓᎫ¾‡SG¦Ã›G®ÃŠØ‘ëðå{ƒuøòÍšwM¾õehþâkM‡¿øû÷ÐaÒÈtø«bÖ¡ö-§ ¾}¿4ø~Hr?$·HM?²à�ågM¡ˆ™nƒ#†|­i±Ó‹ü~J ñ‘ô=&tOzÆ3Ú÷+™ÖJÓøªÜ©Sì¤}q=õ¿ŸÖ“M]å÷C7v»*íU‹„©þ&ÓZJ‹Ò0ð¨Óó˜:Nò)²=!h-zœJëŒÊQßS×è²Ó÷å›Ôg$+É7œôlþµÃßC饉Z¹¾t¾~¥@ÿfXß„nÿ0ƒÛ÷iüåú0{(­Õ•7|»W¨ýËY)ûíßvCû·]×þ½2‚öoG˜öïçºöïçß§ý³¬Îì¸rd>ÛöÞ‘é»´zäú~qW°¾_ܪé{Gýðõýòm¡õýâ\Mß/ÎùúΙ¾ñÚÈí|IòÈuøÂ²`¾P¦éðåéÃ×á/ƇÖá çi:|aÔ÷ÐaÞÈtøÒò‘ë°¸ä:Üf˜ÿÙ¦›ÿy)äüÏ@nßZ‡Ï¿«éðù7¾‡sF¦Ãí׌\‡E›F®Ãç ßÿ?§ûþ{ÈïÿêðçM¡uø\‹¦Ãç꿇sG¦Ãÿ9r\‡[·ëpë:M‡?_<|¾˜Z‡[gk:Ü:ý{èÐ>2¾ðÂÈux_<ë0^¯CZûÐ!×-ŠíÁúÛR¬¿-wiú{ñJ“ £ÃR¼ñ°gä~gñ:vª:ÜbB˜uøì—roe¯¼ðz„_·j«2§õäë"²ñµ1u:}­5¤_Oã„#ÓWÁñPújT_ÏÖ?«[ÿ½íÌÈôõü»¡õõÌ{š¾žyk$úò²¾hMß¼×,³Bëëù›Y_÷F-Ƀ÷îÙMýZs!u6aPíÖÙ3£¤ÎV¨:Ûü%Âû­+HgÏïSÇ Ãè­2Xîç“û]MÐéíIUo›×!ÌzÛü(ùáAc£Cí©ý‘<´Æ€d¡ü8/1;Ü“¡Ã©Ãfƒ,´¿v]`ïFŸ¨ãçý;ôçI)ãüœ¶îü+}Y¤2v©,Òôe‘~äîÔŽ +Ê$Iî£3˵—ðÛ¥¬m¡²¡r9¤”ÇÎàòØdð6éüŸçÂù?Õ2]þßüŒæö·†ÿÛ¤ÿ{Zî|:áT+㸟q|åpº8ùÎþSú² u8´æGîÕ h]�­¢5B´ë;~ä¯Çï .“-Ëh-þ`º”8†>åX<ë4ýHþ± ]6¸öºgFì‘ë„Z4]¶ªË§ ýŸ§uýŸ-áú?!tùì®Ðº|ê5M—O½4"]ÒÚ£)Ê·ÊþµŠMxSÑ_k°þž%ÿÇ92;:oÓÈìèÆëmãQMoϾ22;úLSh;º±E³£ë¿«ÕÙÐCÚTÿ}#W‘ûbÝéÐí3¤7uœb -í6ôÿºuý¿gêGnK7Ï mK»oÖliwæw±¥CÕ_Ú‹Ü»AbÐ,צ— ×ò‘apnB( nƒ ótó›¯7™BcpÃ( ƒ]_ƒÏ3_‹Á§›ƒAsÍÔ…žo¾ã$d¾dˆµ#ÐýjýÕŸµÂ~{ä@¿}½¡þ¯×Õÿ§_¦ß ;ØÚ^®T³—ëÝÃýŽNî#'¿£ë¾ÙàŸwÒÏzžnw²žbôzêð¦£ÎwƒuÔùš¦£§Ö SGÈÇÆ²Ð:ê¬ÐtÔyïptÔ¬¬Ëî`I§ŸÎàt»ÿý4L?· V‡[ÕÏ“/ëçÉõš~6.¦~Pºg‡ÖÏ“s4ý<9sXRô×^?Hwû#×OnÆ íÅeƒéç‰GƒõóD½¦ŸîÛ†¯Ÿ —‡ÖÏ“5ý<qÉ«Ÿ®F®Ÿ[ã vHî#vhP´Î`ÿ×éìÿ†áÎÃt…ñ×éüߎaÍÓšoÚg‘Öž¯ÏÖ:Ni?Côãþú¦‘÷{o>9˜:0¨ž:fë©ãMO]Q÷C‡ÖÓÏŽjzúÙ#ÀQô8¢tËFŽ£œƒƒégp;ô³Ëƒõó³ó5ýt~6|ý<ùZhý<þ†¦ŸÇ_ùaõóäì‘ë'{Ç`õŒô[=PG›‚uôØ?5=ùÖðëÚëBëè±õšŽ[óÿN]{⢑׵›šÃÒöA±´Ö0ÿ·V7ÿ÷Dèù¿XZ·4´žÖ.Óô´¶b8zêf,mK_±$×ÄAõñ×+gœœItX©3ú&êÈœòVúŽJÙ?Z;­SÐy&Îì2ž©BßVÓ÷>ÊžWk>lmÔÕÔ%|†ý:‚õÝnÿl×®»2Œ¾sƒ÷î0ö¦½[£í¢­eX8T¾í‰W¾íi3îßÐaØ+b‹V–m[µ²l[Oëôè]U/®­æ“Ùþu-¤Ú¯+¨ðf¸õ¨7€.ñ×ð<v±fùk\åºÎ)MÂ,ù=C{W¯ï¸¶óÆdÛz‘ƒ²ØOkµuŒkïÕΊ"½>~/­å´¡‡ýF€h„eˆoRugäíæ³Azï=¼Ç×^ÖÁ~ÖÁÁ¶7Ä’¹í¨Ø–~䆿v“ØÞ>QìhÏ;!çÀÙîQ"nz‚0­Ë0ïÔïÁCeÐêŒ8žÙ5æ´î,Áæ4‡Tï}.ÚÇß'÷î9â:¬§í‰Ì®ÈÀþûÞY®~W”ÉþêñƇÎÇ9Lo®ï½Ág &ƒ¹|µ½{\´¯Ï¤ÊÀo7ègˆxÜBÒušKå>·GÜ=ä8€è$};´1Úrжx_êŽ ðõ&ñŽ7ÐïÕëÏÁPøq'hOƒ6p‚«Ë¤?WV§‡¬Ì®Ñ§Â——¤9¨éÊMg‚øfZpš´Á˜D]Þ³8ï µäs‚ÖL ž=n;hs ´.¤RWù’ïÐ7ox'Ï@ï}N@Oó?Òó/2Ðv›Žw¥‚¯�ïr½ô;¼ ƒJ>‰¶Î ØÂÑݺ2p†(;èrC•?OÕññ…à“º¬0|èy¢ŽÏê|šA†ž:¡ã³vp>'Awx[@4;uØêd<wìÀ)Ðõix‰8ÉØÚÚmùê‚äÓhw€v§vh·BÛ Ú=ÚÕ ­Öå{ˆ|Ë첄¬SüüŽÏÁå º=CÔM¢ `ŒíÆaºAöýݦKóXˆ4óÀ¯tˆ4‰ÆTzÜÇ©Ò,�]Ÿ.Í“!ÒÌ]ëiMePîqŸ$ÍÞÌ.sr€ŽÚ”Nyþ ì•GÊ¡<-š|žÈÁå³ ›÷†—OÒlÒpì‰á4c 6öÆ\®Ò¥_& n|úµ"È>{»< Ön‘FgÚ5šßä=ãZ.±vŠ4ùݞɳQEKÔÔ'„ðœõ$o‡óP_Ší?í?­ù«¾¤ôÈÇ =ÿÕ=‘à¿l²³lÂr¹ Ç,í‡s]_Šg_Dyëë¢Ã.VIïÑ7ðÿäùÁ û>ojz'ì~}!ç¹öp{zÐ.%ÿì€üüÿ!ù ø¾ÿÖñ:^™à¿Cׇ¸+þîdäñ¨<ÓQ‹C_j5ÙtqˆÛ%¿C×âà·­¦õ¿q:ÙN n S¡‹;¸zø¸'tïZW¸Ãº¸ÄÍEÜ^]\.âÿ³ºü»Kw%âºuqYˆ»ˆö¥ÕŹ7 q]ú«þ‰¸R]ðµêcÚ;R׌¸}ˆËÐŵ"îÄ%é‑U«‹ƒ¾êQÄ ]Ü!Ä-ÏöŸ9®‹ëG\í¯‹Û„¸Û·G·q3·]·q“i?j]ÜvÄO{ÅkqôV™W§‹Cßç‘¿#®HgA܇ˆËÑñ;…¸·h¯x]°÷ÈKˆKÔÅ¡ÌY/÷ŽDœâÏ?‚üŸÑåß“ˆ8äÿ4ú9_mWýó™ð¥‹' Kû‘vå[¹È4ç.ô+2#­rxcd–Ò¯ˆÌI?2£_ñ×#óÚ‹#3ÚÝ‘6:£ZΙ^lá½'LÛdŸbƒˆ ÞËÕ”*Ï &»˜øNÀn*çdFW¼F_]$L6èF\4Í©h—˳N¸§+sÏ™]ÂKû!Ùpÿ¤L#ïÄRßÁ}¬ž‡<ÿ@w?½12W=g|öá~¦ò¡´é»»�þ>ú¶‘}üÎü¹Üˆ?xY) »hÞõ=ø9Y.;ø,¥}ï\ ždå[FÏFÙ><d¡1—½°ÿ©ŠÍ¸E¶1Ehc²Xl]g“wỸÅίÞiOûÜYý"Áо¢ò¬éŸj~¬¯[Òm~:ú™àõäè œ»­ä5CÉëÃïÒ>€†gYüì%¶úg9üì1Ú‹ò<,g@–2^“¢}?»iÊPgV¢þÇŒg¶Ó\¶ÔÏ¢ ò\ŠúÒ{êwµ´?CÈs›¹ß ™a[W~ªÎ‰C®Þ¡ö‚¦ñ–'G®ÁÙ*×b|§³Èt¼Ô3R-®Šˆ=ªÌ4¾Cüh|‡xÒÏ0÷«Ïi[ì·ïûœ•¦É7­ˆäëØjÞùðŠuñ8¦šÏæ óÎî¡!÷{tÒÚ×ì•Øyö˜ô¶Èñ¥g¾pÐ~Úr=ÀVón7úbÊ>(i†}9†ûÒçKž%Rß× ô侇«óðüÒÐNzòÁïü!øÖvdt }{]´íI±H¦o¦ç Ñújڎʳ®"ÚM4œÖÙ>Q$¶gŠ$ZóAkBÖ¸Eœ:Þ°ÿÀ#a‘¾Ç¶µ‰$w:tLßcÓž Ý’ÖpFÖyÍ^ZÐåÕŸ£¥ós]BçCGgvw(t¿I>×úŠ]Þ¼þz èªÃð¡ç¹:>ù!øÄÑ äCÏuýroAˆþà!ÐÅ…éâyÌ)Ÿ¢ý¤í  Ùáç[t|JçCãÓ1!û[ü\×Ïõ–‡àº0ú‘Ïõú© Á'taÆèù¹ºñou>© ;†=ß®ãS‚OèÂ釞ëõãÁǺpú¡çzý„èoŸN?x~Ž^?®8< >å!û±Ž�2†dySÙƒç>¯úŠÞYÞf¹§9Þñ|‘Eý~z—è>3?Cg½³ð Ï9³ôü NöPcRÐm /»¤QÎL†lR~È«öuIfõ!dþϱ†‘™žÇédn !stÝà2ƒfüÉ@fØ´ñÛÂÈLÏWëd1fg†M_0„ÌDcýd†}ˆ>Ffz~@'sG™ãA×=„ÌDãüdN�Ÿ´02ÓóXÌ!d†Mwl™‰¦÷9 |ad¦çù:™»CÈœŒ¼E!sr _ßOæTÚ#9ŒÌôÜ©“yS™Ó@—3„ÌD“ôÈlÍìÛFfzÞ«“yK™Ñ¾Œ =êÐ(ã¡$«*7Ëì,sãœ2ÛÀ'1ŒÌô<0FªÎÕÑùñƒÏ×™dvEí5'*c¡æÖŽƒ¶Ã@¯Ðzcúý -5ÐÆ2íví1Цh£™v‡¶/³+ò´Ö´ƹ‚àÝk McÚÝÚƒ ]k Í`Ú^-ü¿È"mлWѱ%¦qô›è£zó;@Ÿ¤ÍYX”¼õx÷ƒ÷í©Ì®1ö×sÖ{í¸,C#vuF"ìüÑ^¼ð³U:–Ý8? ýñéø+cí oøë«${Ÿz“¬—�ï~-ô=ºC—ÏXÎçqО¦µ ­­áƒô¦ÑŸ4ÐÆëçÓàßœfLoo¼á}’Ù€Sú®oTÀ?'}y/õ åôÝF½iЭ/¨çÄÓ;Ëg Ð_nÔ7í‡?ª9h§SÄIý ˜Ç}T^€öòw$­”{ -Ú¢Q ¾\}‘Z´–㾉!øÂÆ[vøòœ°/Ú@ Ûjñø¦†à ûhÉ5ðå:ã3Ì Ø%K¼oF¾Y™]æ~_;ó5ÎÏä€v‡¯=ß\к |ó˜oœ–æ¯r |óBð¥y³8 ŸÊÜÙ%ÅæúŒëZ!Gß@{g"ÚíZÐnhs%­q Ahm®¤M2Ðv‚6Ë@›È´ÉÚnÐÆh“Z—Á˜7evEhs%ßTíÐåÍ`ZÃZó¶L:/U‡£ñË.Ÿ5h¬CŽu _"Ísú2”ùiŸ6wæt¹x?‹Æ@ˆÖ.j‹å{xGÚ‡ù|ÞˆñmåÌÇXðµÓól]=Íùµ˜O^Û-÷Ò,yMXì¢ú(ÍÑ6žöD¤o?^®¶'â’—l¿Vu<F6¸]È=[Õs¸S\ Þù¾<‡{ñ‡ò»’ÆäVÏ£É=ò†3—Öã)×É•£“‹Ö#–ãy®}¸Öáz×f\àêÄu/®kiì×n\wâº×í¸îÀu ®»qíÆu/®¸Àµ׃´ž×>Z÷ˆk?íqëIúÆ×SJú^ íˇ+#‘k,®¹¸Ò¼¥×D9××ãMÆÕŠ«×T\m¸&áj—çÝôxªå\_§“Ιǵ×H\é¼s?Aç[àzXÎõõxÓpíÇ5A޳÷xc丗ÎÀ¡ü»ä\_g5®”ÿ\)ÿ›p¥üÓÙ”ÿr®¯ÇÓ‹+å¿<'®Ç³WÊÿq\‘o´œëëñ¦âJù÷áJùÇù÷•òŸ%çúz<§å9q=ží¸"ÿž"\)ÿ¸Rþ“ä8ç®”ÿV:ß×R\cÖNô'èÆ2»iì±ý qˆÇËý7Šƒ|nq4¯;=–~$!a[ôºº< íÅ«O”·gнíÅb»›Î5^xe.åÜõIcš¡Æ"3ÛÄþtФŠ-é B<ìíå[S:3³Kî Éó«UíT_:Ð<”oÚ+æÈ卵–HeÎŒÖÔ.œ-¿Œ·Dâgq9Í'ù™¤G½Óõñµz‡zÕÇujDûNª¼B©fvùãtõ9ÕÕàËSêòÂñÆ}é;«¶7D¤< ò£«å:Wçî)òlP¹ÖU™o°Öµí¨RVt®dú‘KWÓY‘t¶$¯y ø^RÖÒÎTûðÛåžùnq8Ïgñ‰ßßxÁǦÇ!¯Vèdº°+ó‡°›öÌÏ"; {E{ªÛçeÇÌ žÎ–|:¯ÝDç±FêZ_†ô~£ë�íG¿ßµNäèé¸O£Ñ®YaŸ/iv±`®V\ãškÍgâzïÔŸ 1ã+Ú×É¢üÓùAòÌ÷(sLnžæ3 £ÌE:\¤~×5 (Ûjý«(OW}Áth3—áj.õ®ËÄ6¹žÓ$rÓÄooËùmÅ¢ Í-ŠêÓ²(ãJ”kóëéÜPGýÅ×ÓYZNy–ÖFø$ÀÆš7„}ÍQ‘Ó6Qäµm¥ÀR90Qº›ÕúºHóøÅ¦ÖËDª§QìöŠý\&~“é±TN”|kx€Ïl—óÎU V¨]LO¡sķȵš¸§}ºèÙÜ÷bf ~鼩13Gç¹åAÿë¥NÁ—ðKsŒ¹é4»°æÏ"™¾?šºeÕ-Ë:пmð=íPꔆaç² Ž”‹É†°/—õ¾CmÇõç¦<1AäÎ&­r¤\@8]0¾u…H«ÿdaÃä¢ó¨W+Ï„Ÿ…l­In�ÿ³MDÓ{v1ÿém‚Îàí�m?·Iû2ƃEÀ§mÈjîÿ'hVˆÛzq‚ö›þ¥ÿÔ Ô—6¿Hš›×8ï}G[£ˆ›å?ÕØþ‰Ã[(ö6î&_DÄθJ˜fÜŽz²¦šôÑ ?Á:ýEäŒé:WXê'·Å¶F¡Þ¥‹4WŒ°8£Ö¤dþ¿ßyAk â2èžÂ¨§çû;¼¯¢þ®—óÿ}î !._åk‹¼Ÿ8(-èm‡{²"ûšk…UÖ\å¾î ©ŸÌcÇY¿&…dßÊÆõŸ8¾yÕ\%58Ÿtxè»pä•mEbPz]ÄëÜÔÇ–wÃæçÔM”‡Æ rëêo ÿÒ¶‚¿ƒÝ ¶³Í ”…ë[a…ýµ2É¢¾­Ó²‹û÷ÁÿɧyIÉ»‰æðË?åö¢•¾°u‰“õÉ=Š•½ÐJFᚈwéû«VÞÅ;ÈF¸ßñïtáßMm‡gœ¿×3ſǓíßëYäßïYå?@glŒô| ä!MæüE:÷ýÏ´ïðýQ¿À ÿåÊ\ž_γ°‘ó3á³ɱ%Z‹ç§¶°øÎç1m †¯u£ÉµBY¯Fþ!Ò8÷°«÷Wöl¾ŸÖ'¢úEudF/Ñ”¼¤Õ­’Íê}¿~€szÙjç¦÷¡ýO².À–Lï¥ùüŠ+¶qi6¥dÕ=:GìIºYî£Lgïeû¿íE¹$“Ž ƒóÈB»\ãs™ÂËúºkÓéÜ *Sún\–ëeègotV ÑFʳò¢Ì*~èl]ãw õ–ëõ³ïð}íÆ~Slú‘ ŽÙEYØýûýöGw´ôÅŠDï1‘¬ì1qÁjy†ô!´hëë/º^ž%]÷Òg Ÿ�åt ­ñ!}>ÈÿQu+ÇoTê¿]¾ •«ã7°¾BÁ‘ˆn\õ‰ƒâx½Fj`½ÆÎZt�¯ @þÊžqqþ&yfä7éÊ™‘äm$ÿ®x6Ù0Ò Éo]Ÿ$Ê\<-:ˆFµqz[@õ´µ^ä)õ¢d20_-õò ðx­§ºqßš€N¤ŽJߣs‰7aYê|(<’³'åš’§ÇW žô}+}"בÿaž_|üÇW‡Yƒ[ }À•4Ρó›dK>VÊû>moûÐß§Ö,ž¨½¥óìŒkh÷"µ½e~ÜOkœ‡¯FZ÷ÞŒµ’å:FºœRŸ¶a¬ñÐê ðï\7I9Ký×S+ñá|eªŠn.ÜïÒ}#ð/”½Òuë½]Áý¹þNì jÛz\´Ÿ‚ÍuÄh¸ÿߪú@ìŸdøT«>¹b]¼ÙkÇ®OûEÁý áÒùÈ­úgЯ ÏSuý›ÑïE\@6ƵòÙ0×Ëéå£o]ä:ó(´7*sÐÚY;þ”_`=%Ê&‡Ê¦ÊèÒ·µrÏ`˜å\?‰ÊÒÎ{îäÁ¯M‚ýK•6q"Úª#D£\mô½ŸË'ò¸LKylTΣoýȧ¡=Ôhÿ£m]–ÄæKD÷o:Ý+ÐÞ†Ðy@sÅ̰ŽWèÏš~-ø^"òÓMâB׫Â2/NL[ƒ ñ‘V‡ˆFØBgï´ÆQ}»ûŒ»~˜<{,"~š…×dFzg¡ý½øMécÌsĄ̊_$R¦¬±™ëMÛ(Ýúåù)õ‹~«{Lù!ÿçIôÍ”>Þvô1å¸k«²ŽO€Ý_.Ï‹¢¼ä¥ßí?MüQNô®ï×xÞÒ÷¢ò½á}KÉwþþ|y¦¥<KŽyæUˆ'Ÿº›hd¿&ò%ís%šƒÆËê^Üo Æ«9^{&çÌ¿£aR¦·Yúw‘ð#LÌã�ôÝ%Fów©±”ÿ†O^•gÈÛE­§Ëý®ý7ðŽ$;‡rËòü mÐVAgÝÅ5¼ý²Ã¹J¤@o“é¼:*KyþØÝÐOKß–Fe ¿!ÞSŸÉo('¹îzI}è¡Ïƒ®KЖgËÊqò¸†Ó¯:€¿hyfð8Ô­ºˆ^×zQj­‰²¼F“{oY(Ûº"’ˆï—ˆµ­°ð×söx�=gû”oøüÀ8ÎÃkg÷=áÑ9NP_ Ÿ²Ïm•ß–Æ)çpÝ·œøÐ˜ åSÚ¥@ï=£æ‘ΦçÚ³‚Lݳ½t†…é/÷Ý"zz±¸pî"1­w¬kчÆÉ3¨»‰t“E|dYH^ùéx9åÙ]t.Ù"ú1´•D;ã q!½wçÄ´6.GÔÇ{uïÉò•g• à÷5::Ïׯr\qÊXNþUzÚzÎ8¿'î¤!çò¨®Ó9m2»&/¾³\Iƒ<h=t†©‡ÀžRO-çµêΠzØu¦#¸Þ• ?ܧ?3M=/-Ý-.œ×)¦áy’N·WC·‰Jžïü�õ]Ú W'ÝÏEßêžÖuȳ°i=zœ[lëMÑ(³÷Z„½ÿ•Ù‰;+ÏË÷ï™­¼ú¤Æóž—´ð]Ÿòóƒüž`šñ¿[£½óCŽÛ¢“o.Ç­Õñ¿®Š­½«‰ä¦vˆôCz²‹¼£ /•õž¾RÎå“ï*:¸{)žg©gÝi:ûöäà:›KßwT#­}ÐÉÔæ¢í¤u Ôv²ïdå³Ùe›Kc¹ÔžÊu©Üîªí4µ¿Ô¯¤vYñ£Îéð>#]/ªŽùF]âßçuúÏÂÏ«v],,ÕQÂDßRP®x²ˆXs­ìËÅã—`󘤯‚þZœ:¶jW%ÒÖeªÔ1ÂÁÆŒ)ú‰a}«šÙ)¶Yåø¹n¢“ýµ´à#ô=¿º eóéT¯ÿ,ø;põÛzäè&æ«ú¹¾² 5Æ,ëîHúMd›a_×êÊ9圦”óô}S+}ßMß8PºŠ¦þ+ô»ô5}CâÝ ì§plc'ú¹ð÷çu~Ø@ú^›Oû½¢ïx£¾3»ÎJ9à§jsFy|—|õc¨GkŸ©?Ø9iÐþ õu¼E+eì3ôiœäcÝù5®vqÛm¸f±_l§o\زҘͻÍ뎙Aç¨:½"E™ƒÓñ=½Ïç¦?îmv‘KüíÚ|Bîg¸/Àõ¨ÒOÌý0Зb?;ä¿HÍÿš7D~}J¶£m£¨ƒìt~•NuÓ§|WŸêOr. Uêf£XË}›ÖQ'ëh•Ûš£¢€Ç‘‹ÒŒ‹l›(JÛ2Ey[±¨ls‹jøÎÔt¾šÎwtÓúÎÎe–Û<–ƒê¸ªqŽ€ÆS©¯}¼”~0S™ñ^èKß!÷»ó0üݨK<Ô÷N”ãoyÚ3 crÜe…È'=¶N¹HãfsgŽ™p&Â6ògÞ|z¾í*Ü(:¿íò¶WEõðýùSÑn{»\Vë[÷‘(j_{µ¶lÚæõ–øE"zìÍ=Mï£z"ºÕñG¼ Õsú†íšX)PŸ9£ö½H÷ž{ ËÖ½/zÒ%Ö.w.Ýšâ¬YGc¯vgÔò•Îû¥¸ÆâZ€ëqÍ£±ÙúÉ[R¨D؆øÄo©¯èHé'ÛÎñèœJŸ.L3jᇭÛ.¿ ”ÇåïÈ2™wMÌ ³UuyìTÎ:µ_}ËK²ÎÖlMÉìò˵G$¿\ósÈBÞMúr‘y­ònn ÿð÷,è“æÐ\ lìb:ì‹Rïo›©ö[vÃ&”«cÊwEwÐ÷žÝ²nѼÚ;þrlô ÿîjLTÍ“]ÜîVó®”á­Kue¸‡ÊL¥å2´5¶Èò*ð<‰6e‰wîuÖmM¬Œl]g‹Ü ®<Y.ЕK}ź*#*óú϶¦€¦€Æü‘žcj«ψw­ óHrÞj›�´‚ÎÎÙ…p–”óØ«óæ(”Óy4O€÷h“pBãL´7ù)î–2?Éq¼Ójü¼Ù£g!ïoñw~i®oEÆŒoeYYhŒœÊ‹| »È~哯»ÜöO'Û‰>÷;Äi·â¤‚[q\Á­8†¾cÏ”'Dú5ýÄ“ën3êi)Ò´Éqi´›Ö4.ž]†´Šti5É´zDtIr+߃»è]à$›¾ÿ<Då«ò\Áá Î'  >{´¹xæDÎÀuµ†»[£”3üàoJ]gßµæ#U×Ù7#üÿ´÷õqqUwÞg††—tÇŠŠu¬Ø‚¡d¢i:˜ ÆáE¥@ŒÉ†€¼–”Äf1!/»…$¶1‚­úÄš>›ÝMwS7‰Ñ¦–ÚìgÙ]w7íF›v³Û´fŸ'ϳQQû|çœ;÷Îe ¸Ýýçù”x¼÷ÜóöûýÎïí¼­›Ÿ�<œæ”u!_å0Ê4¢>7žEtþŽÎ “N ó³°që×®#?y<ÆOV÷œ÷ý„;Ö}»*¾M_Ñ};.¿×}—߯tßÎÈoÇtßÎËo‡tß.Éo{tߎÈo½ºo‡ä·Æ; ûzís{–Cܾ>‰9·Zå©ûPé÷u‡F¸ Ôö5Î<çÀçõ(Ï|ÎmQ´×$î<1`ž9?œpeàT$ãa&ý¿,£ïÇ×énÓÍÑÎ>¿ÉÛ”´ÙLóe1¾Ñ °¸·Î—Qfå~àsÊÛ* Goƒ®(çpéw}iÝ+"ï׆Ò¾•ô+–Rû|ß Ê=³8˜¢¸r‘ÁžÐë€þ &À‡CùÖ´o†êåysuysz¡MW.3 £jR-Ëé\ý¾u(w¾Q9º€Ê­^dYÞUëv0kÈ^ÞKðÒžý\+ÍŸ:-Š‚qV‚þž2¹F]Ÿàór4Ëï+3¿ ½‘O¼†_Äé…>š‚?p’Y†Þ?{³E’­@Ÿ3ßÙ[ùcð]?K¾ìÚ‘Œ¤ 7)“Cü<7s?ß@ø+ Æ Î4¢ t÷nærAŸÖ„Þâ­ðåwÃÒÝ_#e¶/Ù…ß/»•åm{RÌ­QÁ‡žxGêÌfèî÷Ó§}£,C袊“óÅ‘ÖPß©s³îSÓ#À3œ#'9Ý킦ÏƬ;mºuŽû°L5vÿæ{–‘ÿµ…Y1KVë¤u5µ^Y'Ñ"Æê5)É}Áme¶àË9•tf³þfàcïŒPöݶ¡ï°ôáFлžå ·à 8†`çñ›¿ªœ«²F~®8K/tUá»,¨Ê#µíÊ5]"Ù 27Æp¶~´dël2îîÏ…ü‰~;¯ÎI{XÙ祇Z€oè[xàÝGwF”-ý?´?¦x«rlžgPçu¶ð]ðÄÑÃh§¹+¾®”Àriþ…Ï“ÀŽªóÍ…ÏcÜ’-üV2™ŽXB÷ýÊÆýédðäåMÌ:˜ÌRC·ý2¸”Yþ´…Y¤°TÔ¿ûb-0]ÖA÷GÛÕ5ì¹qúÝý÷±îÓïpP}ûžw,ë“yfÕQRøîôÙ}^Gž¸kÎ4 ™&ÃÂfäiã'O éÉ}£¦ ò7`s¬Â‡¿ÿú!äçò}F÷òµì×øï›¥;O;Ø@_~û;©x>~CJ~$믥6ÕrTùÑçwsý ½v¸ƒ â·À¯%»ä{8ùÞ³’ r}$"üc3Õ/¿ FøySE}iºÛ𾾓Ô&µ=ßvçw†|ªp%Jüíz]û›æSõ”½»nžòÏ >ž</ú9º7,O¿¶9_ GÍd#ÓaŽ8/â=v ™ô¾‡­¸Ÿl›¬WŽùܯ£îÏ"íæ¡ß_@ï ¤~΋зE¡µÜ`J¦ v›ÎxYœ) wšÇ9nD_…D¹â@Ÿ¼]Äjk!Óx==#Âf#m3Ú¶ Ýf:k¼‡Rî6Ö·ðù »Ô[üîÎÂw?‹®;šàCо<ØÅá~–>æ¿)9¤»8Üg΢ñÝõ<øp/)•}y‚ß󮎥v+ç9=å8‰ïQ¼ÆX‰ìêÀòHæÐmæÌš”Œ‚f_La•!—Ù¼™åGH‡F×Í™4¶Ï WÛ3 V/²/ Õ×V†6¯£»�¬òÞGñ^Sæ>À?î•ÃR*"“Ñ=Z‰|ßEíÍ[q—‹^>>«]ü ï·Š`ƒ~ϬI“𥾳 >‹U¬›±š»vêî!X)̘’ 7Ôç¦6)l7Ak·øeÞî“b…ö²Bwþ-œpºr_Š2©Ò8¼ÎlÛ·ÁœõŸ¥5­×Ì #Ë(Þ;ÜÇ牧ÏPßÐ;¥¹ÿlŠÃQ(e“àùÏÀ@4¥õtÐc²’׉¶Ôÿh¯iŠýO+o¦<4/Ú¼Cwó쬴·¼ÑɯܯÑ%ô= Èý(AÉßÛ ß8þn„,4ƒÇÛÀÛ6!·œ·É;H³¤WêÒJH¦Ë•É©_€{Dåóù®=¾l¶ {¶üfY×#ê~C+zú?SgT—°Zèó5L™–÷¥:9o‰uødðÖçœ`C鋯”ªüE{N¡§ž‰$pÝe‚/AÏøN{˯b¼“Å÷~ ~Òí|Î6¥¨EÎKÓï5d±‚õLÙÞ)~w‚æyi]ei/ òågÉFšI½A¹9•ÆÙN~oì}wÐ=c_aé²þz±GvùOè;ù6t.í3À·ïK¹IÕ}w@fo“2ý‚([øŽØVäSaØ‚ÖÈÎËô“2ý~‚`”é'ôûI¸¥»/ž½sst? í!>3üÎÉ^nk>ˆì…Ú ›DwbÐ}Ñ»0Ä=éÅ[Mõ±öÈ´>ÈÙ®$fvo5-]4®šJðLu&d` ”¶v„9×î¼4ZTÞÛ¿e†Üë]É,uMÖX“§[¬Ì²–Ù\÷±¢Õ,ÓÊ5ÛJ™µ lñÎZº;r?Êå„wW½î |`ëð"–^ŸÂ† ˜%T`¶®cVÚ» ßÕ2 áÉáJæpåïeE…'áÛÞFë%´GZØ­ +³…rß6cà$tá¨ðm‘/=x‹Ù†±>ß7£Þs}q–¯ Êy5¾ñSέ•+býÔôÙE+8)lÑôÚ÷Më°Ý¯Á§#8{1~ÉRé¸x;_{¦y{xÄã.>ÉšƒYžÊ ,cÇ…´¯¯û,b °³öPAl,ËÞÂÏ ÄÐF¥Ѩxëô‹óuðÍSÛ ¡C¨ ÷Iåè‘MûÜ£&ÚWãf±ÊúTÔ…ï¡–_Ù Ñ_ #èsPÃå̲o§Ö¨?"ë÷¢þ€ ?ê.|d'[dµŽàOj«s­E¡øæ)å½®ÛXQÈΜãê3È<Î`2[jÿ5»½æx&ÇÈ3‰Ϥ©<Cp‡ ~e«Oc&‚wßAfæöٔ߂n¶°duë¥iâUZ/ þ0ý[¥kuS¦yòBÖ{lî­éyÁßV÷?§®ìnFýaœVŽgK'Úý!—yDÚ׉ñ›òj.Œ° èÑ ÊÇi‡¼*,ʬ/WÞÿNC+×gˆ/A;+ß+r’/Ìe·Ãn‡]«¸ ãÏð=\”+=C:øìq5@o/¾ÖÂ’¹®Ûб`Æž,¹rd ÕU†Wšm¡;Mù®rå ùˆ^3‡ÖUÙ†oŒmYgJ{¹‡Ò” À‘`|N¾`Nÿ}&Àf » OÏ0÷>ćÏ1•ÏÜ{§èü­•àuYnhQ™-´ 0ƒöi$–üˆøm¤ÓþVA·©Ókw¾ñôÛ8­»·šG\w1ÏêÜŒ‚Ð}e4>Í}¶ hñSìvÚKIc°šïNôÝQÆnïªtÐ>yž"Kð[/ûùÜê2ðÙÊiÏ’eu.+y*i?ð²É½Ö‡iÎ&´…ïk²­ö,œvYÅþŽÕVÚOä·¾8üM!ÍsFŸ ´Ç“c3ø6…-½#—mÒãÐ_N4~´wu`X_Y‰¶2ä¦ã$·¯ï†#•[z05¡7T�¸ SÉǵ<ÚK}CuˆréÙ4v_ÝŠú‚µ•áݧôPý£¼Ò2$ Ò©,Õo6ú†ñÔºK³øÔ'tï€=ôˆò'?¹ ]”ÞNú3¥(t xnpü–âè/`é‘1–9èa|=†äp{-峑ܑü-Þ!ö~#-ËÕŠ>¤ýW¡u6àž>X‰´”3¯œð”±žïßÄ2©¿RƒÃ_ˆöG%—ë$×ÖãC¿a™¨OÅÅFólÅ£ÖCh~'€Òl2-SÓÖÒ ®L¡3ÙÕ ƒ,êì‰^Wjüû í‹€þþxÐeW¦7丗‡R+¤ kE;<z{ñIŒSðÇØ2·ÃUz…­.Û±%”[éyªl®–Ólu뎡ÜZGè±u¶Õ¹»a=•Ηٮ\v]ø©<®ÿj^Lž¾Xf£}Ö ¯ÍµNÐÀ}ŠÖÕR÷»J™ú.ÚoÏ)W´~µzž!ûöBðÇNG¨ÌÙ+`ýx²&åÒtÃ.–°h'»tÐð:èÁfè«)¿x4iø7úÊäÚ�ÝÛê<бM̺» ºÏz.TŸÀyº+s8Eè<®søýõã$+Ö©g’­ÁÁ3;ˆg’&‰gúoÏì'x/h<“t!Ê3ƒñy¶öˆ—›äºhlxÒáˆ<ÿ§d–(Ïð<;yžm€7–g"¼Í¶x<ÃËíæå*¢<³‹-t&_!íu3éê¡dðÍ_Í'þmBG]šætdäßxÎw@Ë4æZ½(“ÎLå…šÖ-sB ´l~TÐr‘ !§i ž˜}hŠåÁg¨¤õÙrå?~ÄýàÍgúx:÷ tïXð3fàáqc<Íõ.žcð \ŽŸK<Ü¿ÁtÉuù?¦Ë¡”•½ÁT3éiâ¥,3]�ßpß)˜&¾C†Ïêæú.ü…2Ù÷ƒïÚ8=žzß},'´ùÏlTäŽêÂøTÎFHnMçuuðvù™¶R¤¬äó‹h3œú|°ÓÏÒÏ/¤˜Â™f›Ë©ü¬`BùÛ¯}ãFó…Ð)Â~õ}h¶9oA¿SÀEç FÁW{õ |§×ùʹrå·^”»uè&î“\%zô‡L—A»+ÃkAë'@ë/c¼p³Áf]ÚûÒ¾?æ>ÓÕàžü0‡ÉâáþöMÈ4Øšd×—Æý¦+„Éš :—æ4¾<@þ›r9|ð8ñC[ßÇÇ+×\€\ðª zçvÐÁ²ñ=f‚Î/¢2ä£õ}üÈÁ¾o‘O*cŽò‹=µ.òÝ[΄Êí…¯•I2¸~ϾrÀù&=,}µâÑjÁ7G¨ï@ÇôÐæÃùÔ‡a®+¦ÔçЯîâÓyDçP+úþ ­SLg?9Cü·Ÿ,‘“,;2 Ÿï/î†.ú c>ÔßÃû&]Ö•ã¼™÷«…ìlWK¦ºûÞú3Û¾\Æöþ„Y¢sÝ·$÷¹ÀãáGþÎö5û̾lØÉÒÃÁ[j¹¯?˜t¾º›ä} øå……Ž!Ÿ\Œ=XMM+¨9}išh y>ן˜@—ð /wÄ9ÅŠˆŽÁdà9Hp[O‡wñçáaè—Š>l½íÝÃïe\»ëÒ4ìnäýö†AØ|&~ÜùKôQb‘*Û®úÈZéX]†>J†Žl­B%Z¹\?¦“ë»ÑF©ëråjdÍoÞø _Ös4“ý(b:øY­«T!»á$»áj¹B6c‹j3œ)§Y #‚~Ìrb÷T‚ÿnt¦°ëúÞ[ž±­nMž&zR;ä[ƒŽYÄÃà•¬`R¥ t&Zg`,v :ïvô ÷­¸/A¾I.Ý9ÇÒW×Oô¡ØÍ}h¼ÊrjÒNSýÐÉ®“KkóC-ël‹wqÝq>TZÝ«úáoÖŠsÅÐøfz˜M ½Ê®ò9ôñ±ÊÃC·šrÖ¤½ñòW¹_G4Ú­(÷Ù#${‚Få’F­ Ñ"Ð(4Úä­Ãxr“°³ä¿Íj~#ì,ì"·³ÁÔngW—F”í­#S ú<‘ú¼Ó»fCÈÀ8²Â™Ê<¡ÜÂ^Úë =+‚Ï Ÿ tÈ&'ô'³läͶ¢¯­´óÃ3ýfE9á]NÈÖœó°{6TÛ♤5ÊˤOŸQ&Iÿ„6›m¤ƒB.–O:68Ÿ|Wøfü|ào•&¼~Ä„tæ$=1œ‹6¡?›uS®’ü’úï_1îúäõoU÷Rƒ/³BøàègÀOôÏý3÷­Ãû:‚ý“ãáíÐÓ'•JŒmà‹àŽÚÃJúŒ¡¯ôùIwƒ×³ÝÏ}°žÛl·2 x³6”×.µxœD3Ô³|r»ënÁkDë^+‡ïÞÊn¦Õr^[ýØÂéÕ­àµ2ðSYFAð7•ù¡Ò*›ô'Éç&ÝC¾3üãX^‹ÐyzHÀ<=H¼Æ}²¯Ò_²¯}Û'æÑý�ð;1V{0t÷¯l^;t§o‰Kc¸tŒ—‹­¦³‡iouŽžÞ?Íý·-zÿÍl‡¬ÒþE¾Q¬U+WhÍOÎãU öerÿt™êç�¶ó­?ÌFýàY…ü¬ -žå4Þ þÿÕpŸêäÔž!¾×ûƒ£€ 8}p‰|÷sW³È— Ý•ã¾äpû}ž“å:œ¡Œ]rk¡ƒ cô•°Óÿ1"|âÿ� ðQ3Ä÷÷Û ã2 Ü»î'·eín©÷t2Íõž*ÓiÒwn…ï\.}çVá;;Ó¤Þk…Þƒ³º2]_[é*‡ÞKƒï¼«Fè=.ÓªÞ«tôß&eº”ôžÛÅ}¹Ò{æ΋ ß’ë=“´J^t ½ŽH½—*õžäÅPx±¼ÈýTÓ±PÚÊ^Ëq^Ññb9ñ"õä…hQ|â“I΋dÈ6H»�~/þ¯FNË-*-§.Î=¯’=/d [ÈÎGÞ“ôÝÁë<<ü�êø­ô›O}´éYÎ_3Oͯ!7S¶ô“°¬7ê7Ÿø¤‹ûÍü»)zOèñSWÕ¾Cž ðu&ÚÎ>9üM7ðùd)¾SJt85qBW· é6J‡/òÕ Á³Êaà’åb’gS+—‡Ü*ÏN‘fs¾'àòÂÎßB^0î¤êä%5*/ÙHÏ t./å,å3xyô¿ƒä$`zÊ ˜?ŽÀ_gü÷¢rûqú‚¡2U=Z®\>¸éž’«ÄA¶²—x„ëÍ ël¡òê^ðA6É úO_î…ˆœû ˜m ŒFáÇKßéÛÈGCºC—ž-tÉåfšw"ºl`l9x—hÚ}جÉч5ožà6elʇEˆ‰Ù‡NûÍŸ¼us¼=æ|ë̶·˜~Ãí¶½^–µ·ŸÙ÷>ϲéü£}ì´‡ö¯Ó~uÚ“Yéû5Whs¬æ ÈXŽ«P™ï_Œ¤xV™ô¿´#þÝÌ´v„ÁOø|#v%r«2r&÷VËiò[ƒÐÓ£iÛh~h"æi¬55LøsîQK=ôszä'4^¤´”s‘ß@¦€?ÊU oßCh~”SÀ[VÈ4ÍÑܶÂþÎóqYª°aÛÉ÷H£½/òûàpStÜW©ûR¯ ý¼bàûÑÔs°#dëûS$ß«ãE>M=Žtò2û<羚Zà›Š·¦ŽàIóÒ½ ™#¼Ë²+Ç}á–\T“ _ö‚Š{B^ÿcÀý-ª‹Óe[ªfÐVÚINÓáŸ4kø§ üÅ÷H|üSþ©FüS4üˤ¾Pñßň ÃoÈ pWùŸûïÂî!¹À˜q¨\¹ðïbN|ꨘ'¾óe1'>u$z.g‘Âïƒÿ]÷¶’Oß�~¤ùLšo%˜ÖFÔqóù£1»w¥Uº¸?~òÞ¦XǾ=tÕéÿÇ냃¯ÆíùDÂέ³Í°qÉšƒ¼y §„þ*“ú+Te£± ù­€=[¯÷¸L§ð9ûAÑöïÑù¯ÿ:;É&¯i'G“{}“»f·“Jm(UÎÁ?Fó\Ó½$$+áA¾p•æ¯ø˜/Uó%‰1krº*3ü·O KH¿„wðr4WÃË%¡ÜÊŸ4!Ê%]TuM¹2™C¼Dú*Xï©,,m£5ÒŸ÷‹}¹ÓÇi~†Æ¯ûFMgøýUª½M:0ÓÞLÎfT™)~.‰Þ—ÒÚ{tº:Ã!ÏVPY:Ó>Îq²1ð±ÕEc‰ÂòÞp¤ÊV8¢üSx¤úx*ᑯþ#Ý %Ç`¶þGi-¬Ê&ãOB—<+æaøYÝê“ÄýT&ä^g“c¸t1›ÔÍÛ\âóÍ C°~­\ùYXÛL¬û6™ƒÎºÒ؄泇nåó1g¢s ø<‘SIóXåʹï l0‰¹èç,W Þ1]*��Çð÷ßë-Àx­`û2ÙÖf²×—•s¡'ß°…î,°'#ü¬S^øYš“6Ÿ¥stÀ3SÆÇ€—5r£ªç­Ñ9Ç]R‡>gΑÿ¶KÂ9^Žó§eœ|ñݲ}gÎÑr„üÔg˜s´ìÎ9>oÎÑPçiï6è‘+÷áÔÍúü¯Úú>d•{§Lgö-Ü|Þ.ál¸à§½‘-â·`¶~Lûu˜u €Yao“i®%\ÏÆ×>™Ünø¹m�ô÷3Èú·§O‡×ü=ùN'Ö¦ÄÌK4æ%j~—y‰pZì¼DŸ½–ú(k´¦z\{Ÿ…_ò,×YƒDû +>×,‹à UÁïêYgƒ^IßGº ý¹ïI¼?Iò£°¹ý®OrÈ×°äSâÌíD3¥S>è š«(‚½²hù?ž˜1'ò¥™s"a6Ÿ9v$fNä@íÌqê¨y›èws¸¥¾ûhݸ ƒwAÚ?’Zä!Øé÷£ÀÓ6דúq¥iâãÊŒ˜q%÷!ˆÍV1>œ²EßLW#8оŽÎS@/›+U]/âíîwÁß ¦ñ=wZ»iÑv#HÏ tÞnHïãÖJ÷ƒs´–áüML½•4˜£Þ"òócêu£^·¾Þ#Â÷'¿ÅdÇxŸÆÁç„ïhÊc÷Ç´ñô¤tÍ‚ŒgõåzAæé%þƒ¾\°Èó>Í™kã—´Úå¡zuüòA†–ïƒã ŸáùÊcò]‚¾Àøâ“qø^4&ß&žïIÏ…÷ýÊÆïI:5ÑE{ùPf)ÆFÃðÐÿMˆ3ŒŸ¼®'Õñâ4ÍÅ¡Ý WªÐ9ø†ñøûi}ôU¿Řr\›ó˜¸âa·¿Nüàý( ïßçã‹Ñ¬x™¯Ÿ ÒúãÄiÄŸÑ7ø‚ö«÷ÜÚoÌAc >îàûÖß.Š;ÎÀ˜Eklí‹ÞÂøšV„%?C¿Õo­flMU¦³¯ŠoŽX)fj„/òeÆîÈU&IVÃåe¶¾u{mÄ»´½o;Þ@ª•½ø=£7òu= 7 ¦•æÓ\ÿÀ÷ùÙLË@‹Ø‡Gû†`G{>±f3ká“Ê$Ùž§ ™…Î0ÁÎ^}XÂÏ}g²ü {C=j÷Gø{ô~š³Ã»™Ö>H ¯ãs°|m‘öÈždu¼{«r(êóÂ×å>ïïàïÒ\=Mp¦)“€Ùä5½@g£é|‡3‘-¤½Ð}§VŠó7 ÷ýäÏ{#7Ñ}ÜÆ»8ì¦Blõ X§é~Ñ¢}8ò/½®ò-øÚÏnûNNO` ¨Ñ‡€›A ÷®RÛüì‹ØoÎÜ'”Èp„aßäþ’g<‹OAþ,³Ÿ¹O§ð}XGÇoÖà.Ø/Î"`wÒ¢Tîå\¾ï&ÝPož¾Þ1Qo­Ž6¶˜~F;çÐ&µ%pQø5;si.-9€¶Ž¨qõìÑóâ}ÿ¹D¹‚*^®ò™H¿‰ÞïíÓ  gb`Ø3•C~+âV|Ϙ‰ÿ”“ty¨¨¼—ö8Ð=¨4ÏDwé ßË϶¿@ý Êœ)ʵGû/ù7Ú»CçYPvHÐÙ"Ï®YWß­C´.$ÞÓñžNpÐz¶ÄWƒñfît‡ÍÐ-†únÑÕw‹®¾[ø>ÚKs:FÉyŒ‚3rî"ž>òлĄ>ú›£quô8ÇËl[ac)VÖwñU[Á_*ã…ßT®ô½üJ/×% Ìßóí¡›”q™ß2°RÓ!­\4,¤|¨4Ó9éBò%ž¼ÄϦJ=âàzäfÒ#oEõHhË¥|¢9|æÊ%?Ü#æîÔ¾¬G_†¹Î8Õ­±{Ô>­¾ 6Ú„Í"½@{µ)ý÷”6zÄ{+ëÕ8o¢÷Óx…}|ñ–|ÓÏÝDwˆL~&Ëy<ý´Ç]àcÉkü—¦×2 úv~ÛFyÃ#‡ˆ×Ç=Å’éŒèy<xðR¾‡ÝðK¤1‚î^éÛùâã‹´Zw)ŸtÑX*cçRX´m7ù°‹Ôt¥[é¾/è�K3Àf&ù$\ÆÒD=:š8%M”Ÿê¤2”Fu«õÒþ ™ï{"³[ÌtçrIßÎ •„áÓ÷­½áÆPá¥|ÒÃôttpÛ·mÁc9•výGkZ2¡¥žÊÐ2Ú2Íú~*ÚŽ{Ïþ^®;Ć}|¾ïì1õüí¥'~…M'}ÃÏå–òy½‹ÓX‚3š‹bl¸<b¡;ij¶ÁÞÆ÷Ì, Bû±H_4'è—<ïMÈîÿlÄ’‡:‰]eÊdd:b£;` [•ÉÃhó³VxŸ2U8ª(pžiïSð½¥<,ˆÊC2ÉCAT=Ëí—»þ‹3]Ø®à›f?#,¥sh“äAÎ5X_4³¼Ìâ~ŠÞ-,æÈ¶Ó»‰`gý}ü÷ž±4Œù·Á—è‹XÈf†w’/Ä2hÝü :§Eó_Ð}Aæ®äsÛÌ6ÐeáÐwøoû9j¶¦3÷؆~Hç@¤>˜çÞèðÎ ޝ‰=z´–gB�*ÙÅ/)]Vfâ{®N*¶ð(­m)"ï·Ú/8YüÒôå!;Š~ˈw'MñvS—È£#úÐÓ𭨳ȕ«L¨xòß÷éW¿ãƒúSß_òŒs ݃~£}¨fÚ?G°¯`ÊyêwÚóImÊotwùe¹gû¼ðMX2ÍõÒþ¤Óçg‘ßL¸#~€Æ¥ü|Z=Ÿ3aÄ·°#ëÁƒf±62$~”v3‹îW‘ßéÒèLÒeù­ötªè•æJÌÈkæ{"ON9‘æ•NP~™—öš(¯šÏýÜTÆ¢]b¿ÊXñn¡µðI…ý»ÌMåj^tç#¿¶éRxçé.{øå÷zIw ÎZ¢ Ñæk ÆðÎKnø9)Ú?zìºzŠQÏ!õÜP^"cüÌâŽø÷yð=ݰ …ïŽ ý‚Ö¸XíÐk,öË/"¿†ÎŒÒZ·9ºRØ2ºK?2�ÿ�üKç¯"¿`éÁmt÷ø OGÏäÅ9ÇDzCÞKDýxˆö|ð;¢ÒØgõw¹9Mì&ŠÓ^|òÝbÎ]¶ÞÚÛŸ [Cg&SøùW“ñ –85f¡»¿À§~Îm'ú´žîxˆ=‹e„±ˆ­Cï±t¢%²¿DcÜ"æÁ;[@û9ŠX¨Y~Ï ¼õ,ÞmEZ<3‹à9#¯ïYHO‘é¨÷¡H=³Øñžùl$OÚ_›HòL†¬aü›x$È*ÈõøËÎ_³/€6_²¯à;C°©`¦¼¡Ý,‡ßü Æõ?eŽkœ3Õ~+¶åVq†ÚÀt·'Ýû¢…|“ÿ.(Ýq4ð–ŽÏO¸ZÙgé.°P+šé¾²ˆöç§;ŸG½È–JeI»¸/¼�ã.ºsÃ"Î'Ð<¦¥TÕ lÛ@_$‹ëz·ØkJ¯Ì‡È¨¾M%×C·’.ÿyT—÷ß^±Š1IñIvú:‘ê^|%Á†».ð}CVÐÍüׯæ£^zÏ üi^Y§ëé7ÜòøYùŸ°èoaÐ^øŠétF~vìÚ Á?泋ÑWÅÍd{?Í=£‹Xrñ¨é�?‡Ô�}Ö Ø¿š&îÉüJù¾š¯–‰Í;M¿ÝàFÞ$ä­§ßœ&>Z¿VÖŒ¸ÕP–t£E¶½»rQ˜f¶ÕLûûU¸ß|ü4FzAž¤1ÑzôO2l#¦P–úïø¦&øï«GËOÓ|.ÚfK©ÌXøeÚÿÑWóÕr“ŒeRÙq¤9ßäã”cô› º6 0Ní翯žŒü¿ž­?¦º gÏÇ…¹žÇpÓýGœÉ çìufÑï«#yö<“¤ßy[À‘p%œ yÐÿSnI#:‡t"²[œ_§þ ûÎÀ·Ð±St®Æê Òžå)ºÙÃmï©©"¢-òSºã`!Òòd}HŸ¬¡tòCQ—gq pª‡Ÿûê”ýù6ê6#žâ~nò*ÝݦƒÕ X3 °æpx¸­Ï–|iœ4kæðAG¸O~B¿CR¤£/¥{Œú˵5ÇÕaR_ Ýÿ†u.½E:K½×t–¼ë‡¯SÓ¹©£t¶KÕQ¡äý$æpH?ÝÕO+·—$'G`>¬ÁD³-˜öÕ|*Ûo%ÝE>ËÄ–à‹ø>Sþ©:)ä{4F/‘¾!½D:çP Kšíü.ï‡W@¿ü®”ÐX‚Ò)òÒé7\¶9YOO›U?Õ#Oòìy¦ÏS:“`÷ÐzÛü)‡ÇJ|'ÿšC ïqPÞ ™×Bü'/ýÞË™×Nz€ò’ì“Üó²oò¹1•ä—Éüý”')¡w¾L§¼]ôÎßå)Qýþz0'­7xÇ¡ð× reßaåê¾1ebß%er8¸¿¶¨7øî½½Á©ÂÞPÖÊÞ`v|°÷î _4ÁgKx)TÆ##ÊÛ‘ʹÈyå<ÆY`q™T.ôg)û‹”Kýë•ËôͽåŒ)Ccë•cAåÐØˆr˜¾PŽŽWŽM*§’¥Œñ;».\o/þÆXϱlð {¨æºXæÐs_^¸Ãî~–¥bÖ¡wYzìÓ$x#¼r|rü2¢\‰ÜAwÒ2;üíÌsË1^áå”·Ïý!§ÇÙñå|,~fí¿ýÉ)J[û¯oNÛßÄXèÊÑ`CŽ#ø:ü•ú÷ïvTßü¿¶àö»¾Kwâ1ù§þÎýŽÆ—e õ3ÛÐó€ñu–5ôKf†ìßÁr†‹YÞ¹;˜ã\1sžó2÷¹~æ9÷<«<÷:«=÷KVÿ3k¦»ý¬ þE†óuæpþ’Áv}¸$8h~©¦Á¾ ã,Èüþ`ÃçlAy/ñŠó6D8rš{Y—þÛé;bãcqâcŬ—¾=½Ì¯Oçñ†ä€L?rÈ˺õé‡ qcûñâ‘~¦Ö·Þ¯¸>jþÈóŒöÊ¿8hH?'MÃËEýÓ—g«Æ5¨o©.N{ª²uqº×Å ïôˆýGÐCÛòÁ&‡#Ø€ç™;{cêõñxw)o¿OþŽNá»'*¦1Þ¾]™”g”³ aòUݸM~çk<(7ð:éð<Ê£ }T/å¡ï”‡Iúúê]ÎD_Ä—â‡cãÛ³ éG évC<ÛÏ1”/1¤á©0ÄOòá5Ö_iˆŸ1”wÒÇ ñZC|½¡¼Ç7ÂÛlˆ·â]†xÀP_½!ýŠ!½Ñ4äŸ0¤ÛŸ4¤áÙcˆÅÆ·Z 僆ü ù3 ù·Òm†x¦!¿f,oä¿ý†t#?á3òÏqCº?·¿07n5ð×vƒ¼ùmûQCy·!ý˜!n€¯ÿmCy?n7Âgíùè¿`Èo‡íg éFù7Ôwùü|ÎPŸA~¶Ÿ7Ä ðÍàçK†¸¡ý­~Ýn„çêÜü»Õ oÛ íï`†üý¹ÕÀŸ; òdä×­#†üFxÍ-Ÿ[ ú|‡î0ÈÓVî0ÂgàÏyÚjà×y†tƒ>Ýjà×}¿ÕÀ¯[k qî0È“‘_·äg‡A~Œü¸ÕÀ;Œí_4¤Ç´Gg Î#œCxaá,„Ó'Ž#C8Špá0 ‡FОÚаaaB!ˆ�ÿÈ@èBhChFhD¨GXP‹PCûG*<%n„"'ÂRBBB6íG¥3‘|_íAIG€d² Ð9òI„ „«töï2Â%„‹ÀŸüð§yTüðggho(ðgÀŸüðgÀŸ½@÷å"�üðgÀŸüðgÀŸîü𧻃ðgÀŸîMfÀŸ~׉îûeÀŸî¡»;é®eº[—~×îôâ{—òhÿùƒÔ‡s…ûZѸôï\­¦Ë!øîüî«¶ÚÊûö sÒïõôï¹—¯ÓÖÙBõël*oPÐûnäR=ä#Â74¾{Ïdùô¿}•|:îó!]õùåF7+J&â7OþÞ…àÖâ³…›3æH¿ (w¦+ÊçÞþï wf"�Î;#2~X—¼î¼|í:²3çH+úï…Ÿ·Ñ¨(ŸÏQ”/îG¨Q”¼+€×¢³µŠr÷EôIî$ ‚M”¡sN>÷ ä)ÊN#lS”»Ž ¾в$€€z— m‰U|›Op\”O ‡ä“Úþâ¤ùh+?SÆ^#LÎ TþžlE¹·Ï!Àw ÏcâÉßÛ=“Z|¶p¯]÷^bHk¾vùß5ÜQg«Jî–aÂJ„j„u¥ò{£ÿ=T\`_éíøBÀî ¼ Íöo åqŸ=ÐÜíó6Úö5Ú›:»í mþžnŸ½ÙÛÑØæëN•eî Ø;›ìí¾öÎîÍѪÚÚ:¼Ÿmö¦no»OæG¼m£·¡ÕÞ¹ñ1_C�Õùí}¾û&o÷Fï&Ÿ½¡³­ ¾ÆT‚ï1ïãÞÅmÞŽM‹ÑÐÃMòfîïîîìR±m­´ûÝ-›teDÆÒŽÇ½m-F»·¡Áç÷³åŸhiÔU‚¿Õݾ†ÎM-[|ö‡J¶oìijòuÛ›»|”~—¿À®ÖMh¡ ïxÜ×héìÐÃ^ ”6yÛŠ»7õ´#õþÞ_—È£¯-÷4ˆÆ¾¦–ŽÊaÏQûâ³ÝG(墜½ÉÛÒÀrîjÌ‹¿ËÛàÝF�uû©*ôe@²WK yÈZÖ€§¢*©Ÿ’?ÔŽ•)àµÅKxPe5övoz@ÒÁoÏi÷öÚïj# ôôZÝáïéêêì<Üåëæ5hD«ŽÒ‰ˆ‚†š:í­Eàã D[±ßÕ§/ª@V«s…èZ ²ö�Œiolé&6‚Í^ íµwu·´·p¶ÔçÞînïfâ§Îž¶FIçÆ€·¥Ce8¢Ž®o‰ð}HP’g÷û=]Fž›ÉT„LCgûbOÇâÇ:¼‹+¼þ�çrOñ>ëÑWÔÚ»;8¹òì~|B5©¬J|õ_›œŒµt<ÞÙê+íØ½wõ¦ þ7ôðñ7µø+Z:Z¥ŒFyÕ@Mð¨?ô8Tv¶t@Áf’v m^¿˜·ç#[>²å«ÙXA¶œòwvD@l¡®f-ßåô/c†:(ÁЋ¥%תÅPvS[çF¨ºn:×ס ߌâ¢ÏëéjÿE{唿®™Q§!ø)ÐÜÙxškêîlWc9Z­¤2…ºs\˜[¡ï QòAoôMa,<~ÈLKCŒh3üuv„ƨáZs&<¹3¾Ì Cl»Zu×h7ЩÑ$'Ç™²’+Gtg\ªÌø47\js×€Ê˪(ç‚fN²iàÅÈUTOáž ›>+Ûä uK¡XN,sHî7ð…®ÀÜ<ª5Óá{¢ë…¤Ä剸xƧ¿®®ù6Ïzü>áKÌèÅxò¨šw3ÌÛ謂9ä<ˆ*cêãvrÞUñºÖv·fôçôqþµKÄã÷¬¡nՆͭcà ¨Ìc¬KMøµÅåƒì›×xÛz> _çkEbê\)½–°Î¬+š5^ù*Uïgs7éan‹ýó­3Z<?¶øÜmÍf}’ï(.qìC<®X+‘jñ9탮™ÙTâLðPhUOGaµ´9ñÛ^7‹jºL†êçÁ&µt sfªàÿ$Lújçæ Õp—{¦‹Ò*î×Yx6Æ´ˆšèîì麖]6‚ó)(Ëçk%}¯ÁÛj¶˜²zïe2“=g›«ÃT+?gLÖ«‡´o“¯›;C×€MŸ56ïã¤É®¥DcŠÇó·çƒ>+ë’©ç{ÑÂX~J|ŽþËnjj©S”óëÃØ"r¼y(++#àbê‰z?v¾ÕÎbV­*%©Oèíiöö¿_L,P•d ë0€jkÙø˜÷‰@¾Ö²¬xmMݾ�L8üm|„ÙÓe§Dbš4¸Kµ-m5J•q¨{c·÷ *çïénòêìDœ4š¢i+½…[r-½éÞm>ý·Ú%Kì]mÞ�:º]x¨³ã‹rè,‡Ö-~>ªŒì»7i¾°-mXæÚØÐ´Ô±ñ^Ç—_ºg þ[²ñKÞ/5zñæSçò‰ãò…`Æ~«nF•bX˜SÍyQK«è$¢kqnåXIgpÈ\õ ä”Í2•^š­’LïÏÉÕ—ÅèŽæ¦òEn*· äÈjtðDç¨uµÞ25±²³½«³#zj"':÷‘ßÑÒ™/¨Å¤ôFÔêæ-êòoød™•Í�½!-% dyt…F*G™e9N==”Î?®ˆÎUP3*0:Zª-ÊFVƶ$ÔVtv¶ù¼±øP"Ì$úDDòù¤ÊÃMM~ïkK~ “š,¦4Ý8Y4xg֥˫OTû0N³šKWøkMgKcœ:bòˆU~ˆ‡¡ÄÌA”öçˆx®®IÁ8uP½QR§+z% ‚ i3ê`:ˆ©Õk.‹Ë3Ë“в], FZIHKRƒœÏ¨#¢Ž,]&ðÔD$¶M¡IÀS!Rã”gñòÉ&iGæíêôó)Ü™}(ó®C^MïˆÙ8U#ÌoÞµ³ƒ«ÿÔ˜yòý¨óìuq©ÇkzÌhÑ£ß /‹¥¤hqMçD¿éôIô›A‡D¿ëôBô›ND¿d:úÝ ¿ìBk©¼ŸËZœ‰%í“&Oºo\tÅ¢¼¬£çAÝ|r”O´ošÍqMg‹¸¦¤¹bRu2Îô\%Ÿ%âi´b>a&†*3ÏH—Žš¬‹ ®Èóè 1n˜éÓQŠNáRÞ•²|¬‘õÄÇ‹alF>¤ ^¦”ÊUK8Jå³L>WÉ'­eñ…+”2”Y]èÒA¾Ùðµ«0vwvùº›ãâ2äõa<ÒÙ¨®ÿÔ¬¢«A„,¢6tY»Ÿ œ˜”ïöÁûæ®XêlyÔñol¾ùÈuO‡”ìè:tþÚ1¨�Æ~+±ßÚZ:ZiMM,ò0é{vuûºêZš´± ùp|ih̓øö8—"äå\ËX¸¹Qrl†�D¿|R|É2§'Åï½GÄéIñeKEœžŠÒ—®(á<zž¥ÓïâÛ|B¸ Á£(¡ ˆg‹'_¯(ý/ ìAhC8+¾Í'ô—ÏI„ÀÖŸ©(»/!¼pZQvUàyD<ùû„mZ|¶°»K÷¾Þæ‰7&Ï^ÝÑàíÙÔ¬­zÄz+‹7NÂ7Y.NvwÐ?k…¬Ç˜ÂfŸOˆ™!èä>äüÍ×(ÅBqŒøÔŠóhxhé�wlš¯Lè× øú•n•PkC·ÔE4ã(7ÆÏ0ƒÿu+¡ÚúýÅm/ºÊ襳F_/_ÞŠÉ«Ÿ ˆSP·À;cÙ2Ú¿êalÜžs÷‚YþY˜ø³â_‚|·àŸY÷~.Oºî=M—'U÷ž"ßÍøgÕ½'Ëwþ%éÞuïüo1N-ÿc¬ãÈŽN_¯¯ßý>ðFO/ÏÓÕÝÙ°¸½³§#Àm’LjòÛaˬ,)­BžÇ½Ý‹í]<£ïñÅþf®ç=?Èmýb_�U¼é˜Wå¯ £uBQN؇,Ÿ 4"D¬2B…¯NŠø)¤ŸjV”× _^»‚púà5”9…<§Î‹t}x­F<_¯á ÈúN¼iߌá,^/Q”ÆÓCxÚPî‡"/ü°M†^Eys\„1è¢1Ôó&àÿñEÆl"ü8(Ãa*Dü§9"œ­GXŠ`Aü‚ 'd‘!(Â[“ô4ÝšPˆþ=RFQ-x ;íÌdvÂøíºŒ'àÚÝ‹ç1EÙSƒç„¢üQ3™¡cÿø0žè“¯gâyTQ†òðD?íC<ô{:€'¾¿ÒÅLèþ¿¼€'h|¢ Oèäך™)øý°O´wåÑÞÐN"Òß²1S££!xÂfüõ<Û¸¬ƒn©b–`±éÖtΨ°L,S™Þ‘‡”,V|}åhBJ1[ù-Ó‚ÛKž3X®/?šžÆŠ˜~ñî;ç‹¥ÀÐ~›Jez0ƒ~å+Pj2¯|μb4—¿Äòl¢GWøŸ‹ÓžàÑ>ÿλ¿X`’•¡m6¢Lï$—¦ueP]TU²u®R«`Ñüg‘¿Ï=j~Ù6oxMl~7Â$òŸÀó<áÈó#/Çn¥„vM –SD@Éë ŸRv+Ó»jðþ¶®ãªÑ^U“V•>¦�Ê\•4:]¯±”|Ë4š�=›ˆr]±4Z¯#ÕCW×P¦÷Þ]Q|uôᴂʧh…þïMï­L6=î¢ýUî²™ }cv(ÓT„çeµAß”Ué+&­¢Ò#–·{W¥<›øLò+ªY+«IZÅùùÏΧ™áÏýì硤UÔëuq¾?¡­”é¯Ãs=ÚŒ˜fÐëg&=“¬kL6UœVÍ?È:£¼íkþÃèû'Q§›ãñl"áÁ+æD|0qՂѪù€õ›)ßH}:m:Ú0?4£úÇx[•ÔP£LÿõŒ¥S¿TrpŸMT»¦’h[é¾{¯^HUÿI,ü¼â¿ÿ»¿UëŸALÿ9úÁ²G­{åÓiûÄÔ.È!êߟ¾÷:=б5ËŠAc‹U™þË@—Éh½ÜJ’vAÑ­^‚µnŽW©¬<bY¯L¿Z˜/Eu³B“€ûCc· y¶@þOBNØ×A:ù_…RÕÑB<?äÁù?µ<]¤ÊC‰Ô=]þnzŠ1Ášü_CYËiU èPšõ¨àšŒTíèD2}>„9 –¿_+ÿh­ (Õ»&MïÚ…Þ}“d{)+Y`]¾ezÎLíW.È¢<™¯2ýcô‹°’ë†L„¤t4üéŸÿ…¦YÞÿ÷û¿ßÿýþï÷ÿuЯ oÂãÓ…t}ìJl[ÀL5öNeú}d!;€ñåõd0æL<‹'ƪ ŸÃ“ Ù—fÞAå0æM8‡§ ƪ¨{Y#<šw8Ä·Ïmc4öe7Ñ8†Æ¹éŠ2ïÙ–˜ð¼õ*ž!:ø©02o_¯aWEã¤*+B:BÆ'U³Te!`ìT•€±QÆAU„¥N„"7B ‚¡cž*ŒsªjÖ#Ô#4"`LT…ñQÆLU?UaìUDˆ lCÀX¨jÂÂ~„#³U½€€1YÕ„£ÇŽ#œ@8€ñSÕÂY„q„·Î!œGÀجê"Â%Œûª® \E˜@˜d¦jà_ ü«5ð¯þÕÀ¿øWÿjà_ ü«5ð¯þÕÀ¿øWÿjà_]ò»0ØÿOŠücœ™õ•+ ì9<´:×¾4Yþû=Ž%K.‡ÓžSåk´{¼ñý‹®\7ï‡ã^cÞ%Ž\VŒÚnêèáîO²ÅÄòý›ùK¾?Ð-žÍê[GgÀ—¼ù{ZÚ¿ØÒÈx¬Ùëofù›;PR<Ý"EžJˆ‰Ô!­Û׿¥Œò­«-Àòi½Œå|½ø?Œ@Zg£7àeù¾æ:~º¤®¹±[‹±|ÚwâGƒâñX©�Õ‰²h´3p‡ÇÛÞÒÀ£"P~”kèl§©8£7ènÙØ#Næÿw+:ˆæá®Ê­KWçíò¡g>ùH×Q°` ¨›Ë¦ÝÓD>ÒÒ*ešY>IºeY³Ð™¸®TÿTŸÖ#õ™t¦ÇtíšeX-ô#½.[~ôÏ"ëRñhÔ`!Ká.ð ¹Ç-ß²F¸Î¦|ŸÑåë•õ' ›ÀíBEúõéò ñ`ºyH—/C>·EóÁ¶ð hj¬o—ÌgFžZØ]ÇâäÛ«åÛ’Îc'âä;$óÁFmE`‰ãqò}WË~F`ѹW}¾?ÕåƒþŽš%ßq]>èw-MŸï¤.l�ÛÏÈ÷––¯:%êó©|E¸%‰|°…&nÇuõX$¼?ÓêÛÝOspѾRóQðëòÁ&7yâÀ÷®–o6™ŒõÑßE-ßNØÂûg¡ß{ò;åwŽ�­.]>µî+²>‡ åcèBá#ÈÑûÚ×ïðUéw=Ód_wÁ÷1¥ÄÉ'x/úõë—à6EðöY„»˜¦RbëmFëâÔït–y¸/FqÙ.÷Å(.ëç>E$ I§ð¸\຃ârí€ûb—ë Ëe\ö—yŠKü †d\¬U¦xšŒ×ʸXã2Iñ<Nò'âb=ÄÌeŒâ eüŒgÈøa¿^ÆÈø "Þo‘ñ?qáû%ÿ“q¾ñe\Åç3">¨â“ˆs^¤x¦Œ«ðÜÌtˆßbˆ“­‰ö£…)ÿ'N<Ý¿Éÿœ!þECüK†¸Û_eˆ×¨qA,¿tÉešÏ»Aôßfê%ú¤_/`{š™ž8¬‹¿ÈL˳iŽKæ•™hž>"ã?†(7ÊøÿRÛ“BùúË£È@¤›€ïeÒÏ_ÁØ÷Áo¦<ßÈØÚ7•ÈøÆÖPÿ6Ëø·bñ1Aï¦u…z™þŒ QÓ?ü`ÓˆÖžùzµ¼d¦;¿MÃ×|cg‰•2ÿ#±í™[ ñ'[y0Ëü_7¤ü3"èAýa† ÛKü/ñ5ÿÜ}ó[Z©é ‘Ýg5|–1öÓó| uŒEÜd—ñ­Ìd!ù¨ñ}±õ' ?¿ROóŽ2ý¤äwËõœßþô°jôHøU¾oàòðƾ@{9¢¼%-¶~ òþä¬Ö¾å~C:lùµú-à·Žëò? Tkµþ´@wýé›F‡±@DzÃ2>ÉØ~§Æ/‰Ÿ‹m/Ñ xˆž’ÿ¿fH`ìùãºòà½÷?Œÿ½!?ð?`Ñò'-TÓ¿'¿kHJùHú<èi×ðMú3=0Në2=*¯ð$ðÿK‡ð"ÛOúc¸~è/ö‚ŒG}HOÒQCüG ï1]{'>qNÇ8ñ‰³º8lËð6 Ÿdè´?&VÊ[òhšä]›¼)¶½ä cÿsDã¿d¼wPJyKþ3Cþ3/”±eúß0öÓºößÍo…Mê<­ñ‡`ï9®ñ‡úpÙþ.=Esòçe¼øžÖðµn ?¥’éÁOäÇÔÈø{VÇOÖwUx잇~!y¾$ÒSÐík ,Ù2úqŸGMÿÚ¿¢µŸòeÄ'uqз‹ô¬”÷ƒ¾N/ýv®½ÝŒý!ñÃ~7ðC øáëÄŸé2ý¯Q?÷íeüŸ ùÏF4ú¦¦B=–ú h¿y3©é+b˧®†üviüšº™±’äŸÔ†ühëX‰Æ/©ÅØ3 ¿éŠŒÿýIøJyOÖ_Ì6OKŒ­/ 6ù0­/ºez!½Üß�ú‡Éü­†ô3sû!õuÚS†ôç™ia„Öñdù?GUÐÙ쨌ÿµ!?tÝ¡€ÆOéð¡Þ„š`‘qØ›½WuéÐ}OuqŒ«ÞÊÔø#}wlý飌=ž1]–é‡Õt¡ÒÁß#OÚ‹ô¿ƒ=±jôMŸ`ìONhý± 3¶þwÃÕ^-x8Öž-x¢Cú\ÊóŒ]jÑøiÁ7cáY0j¨úáÅõ¾ þ üMþî Œ¿gÈ?…!Ú!ä—ô¸î6fºü½1‡¾}‚à“òuøaá/ùñ:3­ÚOë]2¾†±Ín¤Ÿq/ì µ¿^ÆÁ+ÿNcŠ!ê3;ÿÿuoJ—K-ú¾Ðü™ë@£õõZ} Á¿:©ÙÏ…ÐwiíVÆ¡{&û'å}!äíϳµþYØÀØ+Zý 1ÆrhÍSÆŸa¬ç,Í—Êø·Ñ¤Ï¥ÿ°þÚ*/õÇ·/õy¯ŒÀØ·ZdÀ§í$HæÏXÛËÁZ Ÿy›L¯0¤CÞv‘6"Ó!o¬³÷ß0äEoÜÀÇjH¤<¡õgÆß2öC}2þ/c£—5ø¯‡=öRúÛ2žÃØ·'5{v½'¦þ†î€?ÐÓÔ”ßÀ[ü]Þ@C3^Õm_xUwoâ•¶Œù)U\ÍB¯MM-”§«¡N~̯f]b]]{OÀ×[GKZÝî*JW¬¬»'ß¡í õvoò×U?R½F;¢Ä÷‚Fcr¨¶‰T´Q‡cw–Òfºº†Îö:OGÝcÞ:±®®Û·©ÅðuËçm]¾ŽYsû}¹›y¶Ôè’¦nŸ¯n‰„e®Êø¶ìÙA‰êÊêî-)-¥íÃuq”étXJ�F'›üzâÍV8c&7ãÏ–‰`ö±Ç:ZÔ®Ž5«£/”è4úº»ÝÅA{pg«(W–®lö5´ú{ÚI_K»¯½¡k³þ á¬ÝS×ÔæÝd`š†Õ—¨óñ¹ÒÙ àÇÒ9IW‚¤MY­ø’¹;•q âjÇÄç x[#'›ç`õ¸±‘¹™¿eS‡·ÍØ*ë7ûÚº|ÝBjZý`ݪʪºâªêªî ´ºæþªj­¿ä^é:>ƒ=$òbÚvýX{—Öð½ùKçD »3À/NbþÍ~tdÓ¬Lsï=s×ãóûº×]N2w7Èà sgšSôÄå0óà£Uà£X–4ê²Ùê¨[^(Ᏽ»¡[:æUETATŠ9y—³OT!Õ6úÚZÚçÓŠ†,5ÓØ&TD‡ï * ™a)g ?rF¬«kèõŸñãØ:™Ë¿w.Y‘ôÑN÷Èf£·4qèôh4{ýíή@\f'b ]¶4–—ÍÎÓ)VÌAq±Y[Þ ¢:’tž…Ͷ¯#0Ÿ.ñ‹ó2üL‰vº¸±±›îÃW_Çã1õ<TZ÷pGE§·q¨•µv~o—¾Š¶yÊž8XUs*L³¢K¡„Ö:A=Õ={C-Ò’ÙëÀ_[j¨! í0-°¢ÝººÙ…¨¥£±zsûÆÎ6Í󷨎a)¨³–‚Ž%c%a{»·k>ý¬—]ø0ºû+TºúÝ ÍÝó©‹û5¾Þ�«‡^Q¸Í×1/@`«¹1y`†1™Kz¥sÖÐ)ÿ٬ů٠!FÚQ÷öV:J5/ºØ¥³;9üTÄÃM`‚üzqž‚¼AjÍœû;æÕ(´ ‘\ï^ÀÃd1z‰Å(†Mh¯ƒnË‹ui:Á¦p¹::ëøezpf³ý‚óãºo|Åz“ªeêæÅ3QQ)›Cݡε hçâØÎ| IÉÕi¹J‘«Å×Gåˆ'ü~EGÕ‰kšK=mº¦ë.Xå5´_*lqiã§¢âJ®p6úýBߨlv­7Åó€ÛhÿxcþVƒ{jc‚‚½UeÒÞÓ1OÅ´I]Ô»(4u§;š55ª‚¤¬r8§Ý7wwð´sYqÁ Æ\¤+¹*®+{¼®JZ~îÓçg:¶bóC´c¤Hkˆ‡zS‰¸£if(ÑF ªrmIææH¿ª(æbY~¶W?’…ykhnôui—ÉÍ5¾Ô¬6wVó3œ× ù\ª3ÊË¥bLsýìIGC¬†Ö4)eðn6Æ!×Í;n.ëcp§¹C¡ã £:ãåÚC3¿JíkâNNÿŒ«ŽæèDî«5Ìp©þPK ����v©Æ@���������������com/sun/jna/linux-ppc64/PK ���„@g?Qdx¯Ø«��E �)���com/sun/jna/linux-ppc64/libjnidispatch.soì½{`TÅÕ�>wwó"¨AAcA²Ú$ „Š~²A¨€AˆÍ†$ð\H Ù° >Ú࣠1 Ø(Ô/Zü¾Xy„‚«¶Ay„‡*Ö ±ýÒ¯ú¹@Èý3wîîÜ»÷î#R¿?~¶—½9÷Ì™3çœ9sÎÌܹ®ÉÓ~n0Ä÷Ÿ‘ €%€áƒZé¿Âg5ì/ ‰†“ 7ŠûOh)Sþúþfp‡TŸê—\×"ýÞË•3øËý9W‚¨~‰P¯ü%XÞÄ.„×hÿ’aª_SaÙW9¾rÂûMš¿„ìUýb¹>>ÃÿOæ³?\Æ{î}€ÜTe}wßM®G'LþçŸÿéíïÙû,e¸ëàJƒ+®>óµ$3Êõ»ùƨkm6CÿØóx2Ädˆ5&üÙ´~&bŠƒÿ×%oþ¥B0_kMë“0qÁ�뀾¯ïcJ6¸IòjÓ°ÛóÝæ!9ÄdZ3.9Y6d[Øm& Ö5¤¾eXÂÀ„èICK¬7›Ý;[¯& Q“¬±ïd¼ðÿÖhßíÜýpÝ ×Àe+®qp‡k\ẮIpý®{àÊ‚k \Sl¸¦Áu/ûû>ö›Ã~ï‡k&»Ÿ×\ý•«ÁãÃì÷¸òà*T=ŸÇ~KàZ—¬;\Ë9¼R¸VÀõ\hÏåpUÀå„«’á¸àZC$]V1Øz¸6°ûpUõ ®Ç9ÚO°ß'áz ®_±¿ŸëY¸žcÿ®Z¸ž‡ë¸^dð-pÕqô¶±ß—áÚÉî_«Ý¿ ×.¸v³¿Ç~_‡ë?Ù}#\oÂõûÒï_›<iü‹¼ûõ/ÿ"gú'¯_ÿ?¥Wÿó©·‡ML{ÿšÊÄé‹â~==îoƲï×^|:úÂO¬;9{ûžÖî_7ø®Öš*.í¸ýÕ~ɘ½úà=k¹çýwÿ¼³lÒÁÎ-ú§øˆ±ãë©¿Nž“ÿŸürFÌ_>n¼ïð©ÛKºÿ!T\´/WpKÏ€×:–ýÏÿJn.®ß®ÛÞ¶ŒÞ4þüLÛÜÁwýã¡ÑÿpßwGÏWã®ÝÙ/óOÿ1k˸—ûí|×Ùgc¼·úňA­“Æ?cý[þí»§ÿßóž¡ßd¸Z_ÙàöÔø¢äÙ±¦è Ž1Ϧl¸mð]G[?Ítpð„Äöí_<¸ýÜ_n|ðÂÊ‘éÛß¶]WpÏ_?”»ìä#·ß)ùôîºÌ3=¥ÀË¡£Ó®¯ûî©uOݶ&ûØÏ¶ýìêò¯¾{tâúeæÅ÷Ìüí Ãÿ\·±äïw¾ï°4ý.ã¹æÙ×4þ¸¨÷·ÿåº_WŒz»ëäÉä¿$õ|×ÿ>?iüŽÉ®Ÿ¼zdð’óé/ßùåÚ¡_^ý?ŸžŸ4þ7ë~ûì ã&?ýô[þ4ÙýÝÏýþÑš¨Mñß›<xxtð]'¯‹îÙ3ë³á“gµy`õ¬º³Û÷\óë/¯þ`üÛÿÕ<ëÈâI÷~x_ÅÁ£9¿}rÒ¯ E+sm/NÙ½óñ¿ÿ•pߎoÅ·.úWÝ7n΀I“Æ¿5åÒª–·¯ráƒoν~û©kþôÁo»äš^úÚÓÓËoyëÎòRÞÞ½6„øÏHHœt÷€C¿šÞøÆüïê%×jR¹ú m8y^núu‚&üª£Úø†ÿІ_õ½N½÷èðY¨¿Jþ‚N½_êÀ?Öáç^øÓ:ò9¤-Ÿ«uð¯ú»ÿÙ:ô÷éð#˜5Á}]:ôGêйþfM°áW:tJuèTèà¯Ò‘ÃhönÕ‘ç@m|ázgèð9W§ÞèÔ{·ÿgtèßbÖ¦ãÖÁOï§ ·ê´Ë¨ ûL¿¿N½%:í*~úÒ[á]&Bˆ¶ð¯§Ž×K`Æ\ÃJ?ü¨ZŽÃOç½.?˜ÿòãŸ`±�~+þs?ÜOÃ}?¼MŽWö€¾vúáÇÚêïÓÅá738ÔÓ·Á?)Óoƒë?ýðOÝ ±GÌ-\{ÿ”ës€£_ïooì>®] ø5Ìçä#Ó‡¸Âô-GŸøåiJãødíŠ5óŸr|Êò\ðDÞî‡Ç½ÌñifôÏA ÒÃÕkgpˆ »8>eú˜Wü£ÏÚ±wgz1&¯N2ý9`9{hfôÁâÿéÇ?ÅÀ_äô+Ë bSÃ<Í¿‰ko+ƒÃsÃaŸuqÄ~qk9|G}ïçðm~úqïrør½V(sšk—B» ?áàÄï»Ó¯lÏ›ÇVpr“ó-áìùS&7C1èñINV†ýq§G&ò–pp™CüT®]í~xßÑŸŒ~ôÐã^N_œ¢æqüÈýø‰¹Àµ+Çß.á#N>m~ùDßÌÑqstÚ8:r{ÁÎcŠ8¸¬Çb •88³òáNfV/ôÇøs\{\è†öþ†ãÓæç3f2GŸÑ?\ýßý¤¼ýXö�úKÔO¸vÉCÍ ý®^¹¿Ào×Þã6¿În29 'ºŠóW§X{û~ ràäy¢Ñ=ÄÍåô.óòéó—ý9èóš¸"þS‚Ÿaõâ1sôey"¿õœÝÊvˆü›8¸ÜO!w.qrÎòËÙdäú#ó?4G]ÆÑ‘ùöö½ÈÉÓÍàðkZÈÁe{€|:þO\ö{à' ñ?‰~~®¿•õþÊð.Ó? áø—åúŠãì­MæäÆÁvFøvqüÈýû 7žofø(gNGY¿3@1}Èñ#· àÏœ<ëüÒÕsvÅø1A\õ&W¯Ü¯¡ÞèͼÑ7FqõÊí= \»d{ƒv]3ÀooÿpßñÜø{’É? â’ÀÉÍîǾ‡ãG¦ý®O '™>øÎoo÷óßÇÌáÛ$x?xnÈòÃOËö�ã€a '&‡¨a@ÇÊÑa|ÁN®1h÷/a/'9¿ùDqtŽ›ý|ÆrñÀ Æ'úCײv›áÙ\®úcß\NïÍøß·rôë}П¸vÉýtÔ‘Êé]ï ±stÜ > üØœ~e}Á¯á¿ürx?Ö/‡ø¥\½²=ƒÿ0põžâä_ËÑ'~üxÎeí¢ö¿óç²~ÁŸ›8ùcòÁþw7×.9n„vÇ~ÆÁ>úÛèÓœe¿ý…‹«Éö ý¥ïÎÞÌ~y^õ[ÎÀQ#€ÿvÎüFÔ  s5'ÇqÄÈűÇdùC½FŽÎ1Y>Ð.S9—ý9øa²Ÿó'2Ý$/oþâ¥KòJËò——å呼y%KJ�XàÈÇÛüE%«ŠHÞÔÇòî/š_RZV´üîEù¥¥E¥¤´¬°hùr2oåò’²"²¸hqiQY´tÉü‹í¤¬xùÒ•ËïÍ_\DJKæ’ÏñѼy%yPpérRºÄ¾¼dIÙ<²¤håÔüÇòg–ÁŸóÉÊ‚ÒEEK€þrüaÏbÙŸ9K¡`ÑrùÏ»ó-š›_°Ì/*Ë[PVn/Ê›·|éâ<¬ ÿòÃé]ÁÒ%-/-fÏ[”?aS€Üü¢å³�åÁüE+ŠmTº¢ lÅrZìÞü²’ÇŠf1"?ÿù¼Åkz¾Ý^TèGÉ,,\^TZŠ�™1ä£)Ãü­gu, ìåôPVBå?«xyQ~!‘[“gg­÷JáïÛ¦üÛ¢üsôþ¿Wh ß9Æÿ÷¼EKóËü.]1wQýÛ¾¼ÈžWP2 t¸diÞ¢¥Ðæ¥KèÓh-6‘ieÃ5\Ó#QÙdjE޲åùeyQ±Ïc&Q¶TBGéä-» ¨�•2¨·Ç––PÉ+U,§¡dAß‹óJW,É[°$?O"W²ä±¥ ‹d3 ŽuŸT}p$¿ú‚ãM’„éçTÁqÀbC`LƒÎåAŸJX]Y‘ô‡¯‡I@ªjú·^áyË‹ÔEºàR;ôúÂEìGòº.ZZZXÒ¯.+%K g–/ž»t ––/ÖE¤}>/ojÞí§L ëî°°&……õó°°¦„…55,¬™A°@bEŽûæÞD]ôI!…åC &+R0Qù‚IʇLP>¤`rò!øš‰åeú–‡#@q¾¾ýæö{Ë`D&•,„UM\1o^Üà‡® ÞE ƒ=êWp„+^º<(6èa@ 0½hñÒååÁ0‚ʽ4„ÜKCK½4¤$KCI²4¸$KCH²4”$KCJr18Û¥A}´>u<–Î â!JʦL* ¦æ‡À‹,]™•¿ÜxZð>À œwšÉY¾´ ð!è*)…˜DþC—×Òœ8”jQiÑòÇŠ¦ùb“ õEéîA)0 †˜™3åî⢂…¥+`(»wJÞ}K¦-…ðpÁ’’Â’R{~YA±o”¥jbH,Y¤‹†`CèŠ%ËYøÄaJÓ‹ÊŠ—JÁ*ËåIVè!,ÊŒ¦aeP Jv4Æ¥á IÕG¥±Š•oLÐNãÙÒ¡A¨/ZÅôˆ‹¦}Á¸Eù§:Vg±¹|C·.(.,²H”,^±dq¾,^Š€TkÅ’E%K’¢%…‹¡G+Sdwó廼å¤8¿î—ÚËH~A Ö‹–<Fìe4ïÈ[¼¢¬ÈÚZ¨}––—BF5äÏEo¥D¡ö7bã%'�ƒÈ¤šeóJÉ<âå¡E-*YŒ_AñrüYR€‰¢ÕÝûÀô¼ŸçÜŸ—yÿ=y÷O¾gÊÌY“ïŸI¡÷@}É_>¿4oæÃ3T¨®¸h‘½h¹_T2·`déÒ‘w’¼¢Âü²|`dni©”hI!U¼tqѨ²•`}£@Å£æ®(YTx[ácF-¡Ê$¸ÎÄÈ=Ó¦L¼;ïg#ÇøînçïåÉ b ÿ“ùÿpùÎÄ®ÀgDUZøÿÿGÿ‡{îp†—HûÌÈU%%ý¥y¢k¦#,®äFÜÓ8×<„Ãóë¨4É5~=},¯ÉóVnoTÀ…÷í ެ·1x‹ Ÿíü¸M¿]…/×Û¥ƒïUáKó;†?Ǫð?'¨ðëܬÂgíús² ¿Á-:øV~ƒç(ñÙ߆¿¨ø?$Íïäé+ÜÌà±*x2ƒ«Úu(ÁUp+ƒ›Uð,Wµ÷ã³5Mgû7[-*¸ÁUr8TÌàY*8“[kŽ Îì¤5W «•Á•veèð2x«~ž0¸ÊÞÎÇ2x»þ5³ŸVÞaep•žK`p¯ Î䄨àLÎGTöyÎÆà *8“ÑDœñyĬ‚3û<¢Ô£áë6OSÁÛ\eÏ_{ܪ‚w1x– ÎäDeçLŒ†#¹*8“ÿ› Îäy¤Xgýâˆ]73¸Cgýâˆ[—õR­‚[¼F·2x­ .ë±^gýåˆÊzX9Ò¨‚Û¼I ÿº‰Á›Uø²~Uöÿµ WÙ¿‡õ»#*û÷°~w¤]E§…ÁUöï‘õ®²›ÁUöï‘äkø„¨àÌn?QÙó—Œþ'*»•ýä'*»ý’ÙÛ'*»ý¸–Á­*¸\¯Ên?®gp•}~,óoSÀÉ2¥}‘ëUÚ§°CÆwè໕ôç0ûÿ¤Z¿F…ŽÁkuðë•øNbð|¥}’_°~ñI“~³¾•Á[tð•öIÈøm:øí*ü- îÑÁWÚ'YÈúÅ'^müO‰1ë/ŸÆêà+ý3Yº‡ÁuðÍJüeõ ž¬ƒ¯´sR6Á-:øV%þcÏ2x–¾Ò?“•ÌÎ?ÍÕÁ·)ñW52¸Žýª´â<Ìà:öïÛçÆð+åöêØÿ§*û¯dþêSûÿ´^‰¿ºŒÁuìÿS•ý¯nfpû—÷AÉøUr{uìÿS•ýoÁà:öïÛG'ã3ò©Žýª²ÿÝ ®cÿ¾uv†_Íìÿ¨ŽýUÙõG ®cÿ¾}M ó?Guìÿ¨Êþ7µ3¸Žýµ*ñgú=ªcÿGUöÿ$GŽêØÿQ›ÿW… ®cÿGUö¿õ ƒëØÿQ·¿nƒëØÿQ•ý×ÉüëØ¿oÿ�Éùó£:öTeÿ/U1¸ŽýmVâï`þö¨ŽýUÙÿ+²¾tì_Þ"ã7ÈüëØÿQ•ýï’å£cÿLjw3ƒëØÿ1•ýïeöLÇþ}û¬þƒëØÿ1•ý`ãû1û÷í_bøÍL>Çtìÿ˜Êþ›Ÿdpû?fSâ41¸ŽýSÙÿA÷Ó±ß~Z7ƒëØÿ1•ýÿ‘0¸Žýûö‡Èø³\Çþ©ìÿ;\Çþ}ûmd|foÇtìÿ˜ÊþÉúÒ±ß~r†˜Çtìÿ˜Êþïepû?N”øï²øá¸ŽýWÙÿûƒ\Çþåý`>|Ï×±ÿã*ûoayëqû—÷Ëø²1¸ŽýWÙÿ‡,Ï=®cÿò~Wÿc–7×±ÿã*ûÿ3³‡ã:ö/ï+–ñ[™¿=®cÿÇUöÿI_×±ùý¾•Áuìÿ¸ÊþYc8®cÿò>Oÿ˜¬_û?®²ÿã²~uì_Þ÷(ã³ýކã:ö\eÿ'eýêØ¿¼_ZÆ?Íòú:öBeÿgØxwBÇþO˜•øŸ³üýŸPÙÿçÌÞNèØ¿¼ÿ\Æÿ‚µ÷„ŽýŸPÙÿYæOèØ¿¼oSÆÿ+‹ÿOèØÿ •ýŸKdpû÷í·dø_±xà„ŽýŸPÙÿWM ®cÿò¾Pÿk¶ò„ŽýŸPÙ¿‡Å{'tì_Þ?)ãw°|ð„ŽýŸPÙ'ë¿'tìÿD»ÿ™û?¡²ÿ¿ËüèØ¿\­Œÿ?l|oÓ±ÿ6•ýw±xµMÇþå÷}dü²ñ½MÇþÛTöÿ/–ï´éØ¿o¿ºŒÏæ‡Ûtì¿Meÿß²þئcÿòþj>ËïÚtì¿MiÿB4‹Útìß·ß›á÷aùW›Žý·)í_ˆ—õ¥cÿ¾÷Âdü1 ®cÿm*|ÖÛtìß÷^›ŒÏüC›Žý·µªðÛ\Çþ}ï1ü¾²¾tì¿­K…/Ë_Çþ}û®eüo\ÇþO*í_¸ŠÉó¤ŽýŸ4+ñG0ÿ|RÇþO*í_Éâÿ“:ö/¿%ãfösRÇþO*í_°°|ü¤ŽýûÞO”ñY<©cÿ'UöŸ.ó£cÿ¾÷bþXœÔ±ÿ“*ûÏ`ùÅIû?Y¯Â—å¯cÿ'Uö³ÿ“:ö²Y‰›o9©cÿ'UöŸmfpû—ß»”ñ§ÉíÕ±ÿ“*ûŸå`pû÷½çÈð`þü”ŽýŸRÙÿG\Çþå÷düY<|JÇþO©ì?×Íà:öʪÂofpû?¥²ÿ‡ÙzÐ)ûgëÌ>üÙ„Áuìÿ”ÊþgËòѱ¶.íßÃúË)û?¥²ÿ9l<:¥cÿ§ê•ø²u–S:öJeÿù²|tì_~GÆ/`ãï)û?¥²ÿU²=èØ¿üž—Œ¿‘ùçS:öJeÿOÊò×±ùý¿†å_§uìÿ´ÊþŸcãÝiû÷½ÃðÍúïiû?­²ÿ_3yžÖ±ÿÓV%þ‹L>§uìÿ´Êþë˜ýœÖ±ÿÓJ;^’ù±ë໕øÛ˜?9­cϧUvx?ËN7ªàLn­6"}s+áÿ3ÐiV x+ò_?Ý¥ oÅvÍÔÀ÷jÃÏ$kÃ[±¿§iÀÝD:SI GùäiÔÛª oE?0EŽr½_ƒN›6¼µžHç©ñ۵᭨¯_kà·hÃ[Qó4àèOVhÐñhÃ[›‰tî’_~Ƭ ÿá?Ñ€c¿HÖ _ÿÞ GþS5êMÔ£-¼¢Ç~]¬G{7Â5@GûÜ­Gû1iÀ‘ÿ—5àÈ”í-ZŽzYÿ ÿÑ€£b5à(‡©pôWqpüÛGŽýëA 8ö‹x 8Úm_ x3ü{•ûËÕpô×Â?Ƕ&hÀÍðo? 8êýZ 8Úa©mä÷p7ü{ý@ 8öÇ\ x ƒ©áíðïÏ4àèð/P×kÀ±oÝ ·é¬85y¼WŽöŸ¨Gû¿Q^O¤óÑÔp´ÿB 8Úÿ3p´ÿŸÂÛñßpäqíߪGû_¤·Á¿7iÀÑþkÀÑþË5àhÿ/jÀ›áßUp´ÿ$ 8Úÿ¸@øY´ÿç5àfø×¬Gû¢Gû¿YŽö?Tî†ÇjÀÑþ‡iÀÑþoÑ€£ý?¬o‡ŸÖ€£ýgÂÿŠö«íÿ§p+ü;YŽöïÖ€£ý§hÀÑþË4àõðïb 8Úÿp 8ÚÿJ 8Úÿˆ@ø—øïï4àhÿ•p´ÿ»4àhÿ÷hÀmðïmp´ÿ‘p´ÿÕp´ÿQðf"[©†£ý¯ãáÞL’Ü1w]Me‰ñÏÄuµ÷ºMfdÎkxÅ+ïq³ÇàŽU†QGúf‹â”É"1w ^–½-CˆÝøÎXã+÷º‰Y¢[Õ.Ñ­ò ] ¶Æ,ÁÖ$ûaU9 /×[ß*ÁÖ·ùaÞFoñ^ÌÕá•`k ‡G^,Gñ²‘祅ñÒꇹ›%˜»…ãÏÊøËòÃÖV³zk8X2ƒ¥quXXVŽgÖ¶5|ÛÒÏŽçbƳ£ÇÚQŵc+»+»®‹Ñórx ¶ƒ­mb<7seXÙF®,ƒmà`™ì«9ÙW1ž«8ž«^"'f/k8{ÙØÈÚÛÄÕë`õº9zLÕœ>6´1¼vÆt¹ÓåfCxr3^ª9°z×qõ®¯gvÐÀɵm-×¶Lç9¯µ3<W/³¿5œýU1™®çdºžÕ±ž«£šÙZ5gk™L7r2­ŠeúHàè1^Ös¼¬e2]ËÛ8ë—k¹~¹.‘ÉÅÌÉ”õ· \[Çô±ŽÓÇÆËžÖŽõ\;ܬ¬›/Ëdº†ïGÌ^Öpö²‘ér#§Ë5Ì&×p6¹‘ñ²‘çÅÃxéâÚÁlhgC™Þ6rz[Ëlc-g˜¯ÝPËÑËbôr81ž«8ž«XÛªø¾Àd¿“ýzæ‡Öó~ˆµc-׎5µLõ\;l#[›ËÊÚ8^^‡·žùõœßXÏÊ®çÊn`íÝÀµ×Íü›ú!.»:ÿèNÀqèÒYÑÛÙã&—òŶjˆà.­ÛG¹áw›è1*ßZGÜžÁ¾q*Á³Ú-^؉cUçjwrgÛܹßèz±=§c<!®-ƒË2¶ˆ¢ëÕv«u —žB„rsÒ° ð¬2ƒ¤z⤱Ñg|¥£èM!hÇC-å2·ˆAÀ'Ô&Ññ¯Ç¿NàÇÀNà¯xëÄ6�ï8&ªÛ<¦ïk<3¾GtÜå¶Œz•Þ›ƒñ�õ¹i}@Os)}¨eãÜ2Ô²™°d„8«:P†s«‹Ù¸žïÙ^]ÜѧsB¼£#ج¾€gÝN ; ÊqU}éð¼Cb]‡þå„„ ²B|À£e°¾õX&„–# ]w”§\Äf§7Ãq)SlužºÝ2p¾3Ü¡%‡ àå€ò@9\<+6]"¶€<2ý²H�ûÛ5êe©m™uÂ$”m#WÊ$ªÛç\9°<s­ñ°OŽûˆ™É±:¤%üdgù@ŠåB•Y}î‡ëå?õïH‰jF| Ø–qí‹Ù£WnDÑXö#¤á‰‹&^ˆ|G½-Ùå¨?ÐߨK¡ŒzGº¿x h¦ŸïúkFý¹†~è8²¥:ýt‚Œ;A?¨W-¢ÌÕòcrOÓ—;éÒ‘»E[î†iœ­?’Ü[x¹?´/v–Cù•ýþžg™ìcQöÙbOUeõBF9Ú2ø6çþH2jíŒ.Û™Œ$]¶‘‘-ˆŒœ:2*Ö’‘u»Ð€<d€'.Á ðo°{‡¿Ö:á\r5úí4Ôê´³VÓ¶‹ƒ8ÝÖÿH¶àQÚBÂØðláR!³…dÉ.Íâx±ÿH¼·õŽ÷‹ Œ÷D‰÷‹zzrkëérÇ{õÔÖve[-áµõÂ^ÖV³ÔÖ »e[Æþ¤joC�—q¼¹°…+Ô1”kÔöm¤™kwÓ$§®ÞÉÉ+ûÿ4INÞ*|ðzô7`ß5¾8(NŠƒ*ÝäVE ôâP)r¤1sßpŽ«'Æ>¶Ä„1Ñæ5$âŸï7Æ?–—ˆ]–oG Œ¿AÚê, cfœ\í´Åì ú³>MÆÉe)b–Û®o€NÁ§µ{\ÄnqCÌq”« ZfÈb‹P…e</jV¦%hl |8W t`פO¬LshÞH+–?—ô¹RÌŒå]¨íy\óO;\ ʾº lME‹µ°zëƒÕk}sŸBÅÌ8ö¬=ÒqGg¼©t¯1ã˜3þf¨ÿf·yÜ\±ÇR0¥ÙóŒ_n"€|Ò²Åo:¨ # õq¤æX™ub–rWÁiòŸ-þÏÈ“-Ši Z@ƒÒª'#n ÐÚÊðƨêÌáê x+zÓfÿFw´dÿ`ñ4¶/”b{̭Оi̺ŽÄÀo"Ô!åMˆÇò¦q W èÕR@Z³Å” /¾¾%°ûÑÞá´ñù”œKùsàO¼Ð´r"Ô?‚÷¯Jê·l´ÌŽN³»k½ÆØ¯•wAÀo +çÂ6¢Üõò-“$ ìèg°/ \P&õÊ<“æì ;¥lT~z—ѦÅ7ú«Ž”s(? }¹Üºú>óÑ’l¢rƒ–©è�= ΂˜= ë)¾:AÎ4FZKj:R¢­HÏ—+Í;í >û·-š®GO¾®“·ëåNÚ8•¹n|ã”VœÇÑ·Vì·]ì¡>·jŠ×²sjb¶Ø9€õ½g}úcýõ4á /œø£4 ÏÈó)îÁÆbÔØnú€š/ˆÇë"­Þ¤½F$]௹“HWº9¦5£Ùèå6z¬[…£–82¸c˜}ÎIHkåaA† båmÄž±UìÁ2HϺü#Ðs½÷/‡û�ñ l;†RYðö‹vçΘ= ‡¬ ¾¹ŽÒIKÿ…(–O%æô©D¨œb|ÅõØBÉiGGiÛO Î>SF¦§ÓD"vuL mÍ7:ã ý½I¤ øñ8Éÿö¯_äÍ9˜äÀ}Þ»þ>1gÝìïÏcöP¸UÌB–~su±;Îè¦í©#M(S”'ÊŽâí'Ã<uF»ÅFâ³Åó§c@ÑÇÀߟ…;ß1a»(fì[iÁ¾Ìhm£v{WSúHï)„ÁßËjôcê{‘OyŒ.»Çqù®)1º!Ç´¢íÀ¸ÔŠöc©šš¶c…²'eÿŽmÇvW|pÍÈA‡ùl)ŠóÀ3õ½÷Õ?İ1º¾·~ÀQpC!Èo<ço÷ʾý¬¥èŸ-v¿ë“c‚ÎÏÑù2ðÛ`k•'GöÚ?± |ž-~EÇN”;èkK$õhøÜlñoŸ1Ý;æÙ¸ù´�Cž‹cÓõ ¿®cÂߨŸ�¶„ó“4Æ–æ“eÝgX!f £cÜ eh>†Í@<`u=|ä¸kvÌ×¾OúËñ•;´'X B}3?·ùIÃzOCh[ŸT•ÍÒœs ,7KUÎÆê4…Q6QUÖÁÊÆ†.ÛsFU¶&üz{êUe"¨·Pi£Ô?ÍŸK§6o» vß3Îg«1‘q8¶¡¿¦ãŒwÜW¬1ƵF0ÆíecÔ¸ŠfbÆvÊcŽ;éSˆÐcP¥yjÿÊ”˜=å)à+b³FVN‰Ù³Æ-¯Î„1FÍ_ÃßfiŒoÓ"ßÎ]¥ß¾¼Üûñí˃ãÛ—¿SŽog÷…1¾MÒßÎ^Ðß’Õ>oÔz"P_÷[)w–× œ†:¼C.ËùEápÀ>!'%V(c�ØZ'$–Ù‰Èý²Å/¶É~×gÏ~–æP^°L0(ÖudùiMm·¸¤õ€‚ÖÁôš€ÖM’Ü%:H3­ZÁ_› AúŽVlr¥ýú n|Â~1•úÍŒ³FIëX{À†ÛGíÂ{¡¦rh6ôßÏI¾?fŽñ/£ìÿJõ òÅyzǨ­Rl‡kt4fÛBÌ[ˆàÚd|%½DìN‡¬¤*ž˜œƒ³1¶Ã¾ò-í+‡ÁÏìô÷ˆƒŽ?¶4^@¾¯Ù¦"³ý³ý”¶VðÙ¾KÚ£—[Ú~û»¶ßþ†Òö??†í»µmÿó˲íó¶lñÌb:†êC–›”7F¹ƒÊuµ½ç¨,wv�þØG㤔–?øžX]Œ¼bYôw»´+bÄÇ5´ìkÆZuy¬lÑî×yð¸G­_Õ0èûG•òÂx(äôÙÇ’¼.£ÎƒGÜ"­?;·BL¤Ž‡X,„ýû õk…âŽBäú;Ÿœ;†Ó<³³ÄXuIv4HÊõ)ÿóÒœÓUùŒb°Uó<sþßÊ™¿ ‘37ý)gö9lŽG+†ËÿÇÒûÜúôÛ¬-RçÖÐ~A¶yß‚<åF˲óÍ%‚£ŽÕzó<al€¼(`Û7¥òéjãS¹l‹Ô7ËsakÉ8ŒÍùúUõ6A½s—è'±n§ÑèPÏŸ©çÊFí`ýbßp‡\¿¢ —±qsøŸ­21Ö8ç¤þƒÚúgÀI‡:-Àc«5>‘”‘Ö\–Q߀—ƒÛ†’>tÞÛFR-qbƒ˜ÌÃݤOæVÒüÍbOzʳƒ@lVbx è[U´ˆoŽl âÀƒí;À|’Æh¸c«±›; 9{D|}¡àtÐüÐ7Ï¸ÕØŽõ†7×-îàÊyärЮÙa”-æÊvEPgWÎËÕ91Œá[Y ¿Îž½\¹X®Î»Ã(ëæÊ&DPgW.‘«sfeûreÍá×yù(W.™«sJek¹²iÔiãÊY¸:§†QvWÖ~Ýßp岸:FÙF®lNu:¸r¹\“Â(;Ž+k‹ N?v{ö™Ð‡¿e|,¸üXþdŒ´fj²Œ5Düï´Qtܘଠ–Œ Rš-^ªòì36ASŸ– àñ’M‘óï39BñÈÊ%«Ê¹Ã+w±KYÎØf¹&U¹šPådyØ*Hj¶xÑáOä92Ò±ŸóX¸LŠ>yC’/1AÌ^Šû¬à7ÇI¸OVŒa:y/[ 9ÉZ:†çÊó•ž½ÆàqðÊ|®®– mGPÖÈÊÚ”eCË ÊXÙaª²!ue¯e>Уâ9¤]AÙkXÙݪ²Õa”½Ž•µ«Ê†ÓÞþ¬ìUÙ6 eû1_èE›€Ü£ËggüØBº?ç½½œž]¦€ý=Zë šqœSžë£Œª®úÞÔ¥n4¿¢hS2N5A|lÒ#¨‹îÁÝejò×e ¾÷6BÙ)âï:bQÊ/@W?(ö¦q?—ÐØ›ËA þÏ¡õ³œåji'‚*×nñ¼fj à£PÊS²Å6iò"'¡2 Æ[ÉRÊÆàUÉ&0æ÷ñôáw½áɹKZÓw~v»—»¥¼‰ÎwAY\Ë s]IÄMõè"íÇ'"ŸòœʰrÞ#Êü­ÎTìÙojSó‹óž$b—ö{ á�Є1aò¨}”~5ÀÇÉóLtï‰ç#:?Õ1TÌ{2²ÅÖMšsuÄÌæ3Ú•ó?Ñùõ|–µNÔ‘GÚQöX•*!•wÏthôqº]Ø&\? 5nÎw}|,p¾ëãƒÊù®„šï²îûg‹=mØ”Ì{'´¥óUÁÓù¹ ä÷gšÓ±þ& y7£x÷_¸'¨½£’Å3úÛ4¿ñÏó}œ¢œçûøF†Ó¢Üß’mãö·˜³Åž“e¸gÔ¾çL|ûµÐ+¹y$Î;iîÏ ê«ØÞªÝòÞª ·oÿQÐýUtÞrkûª*Ù¾*œ7 s_U&¾³Áž¨H÷C¹Ö}4?Æø ëÇ Æå.WáiºÖ }DÒ×c ÎAà\•Ç^“WâS9ø$'Ý;KeHç¶h™˜ ïIàüØÆ·*;ªæìhJ¶øþt¾}¨ËPmTǧé6°5Alöù_ì§Ýþýè/?¿Úß…ƒ¢ýÂYÑqQ݇ˆÕ3Åš‹ùbíÅ5býÅmbÃŃb£üþ„í·§Ÿ<ŽnÆ9Ù”¨\ô^—Á ¿’|Éì<|»ƒ—Å…$ƒbû¨uq::2·F7:J’†ud‚âÈ`Ç‘;pÝ­2%7×ÞÒSÈàÊLaAy ÂHkF>±WfNY@}ƒ€÷‚ I‚eìYé>}±_˜ X­[…úK„,º†Rê<`0ß>ñ.°‰t°»¶Ãº¸ ÆÉ»À'V¦e;²™º'•ç*|¾ÞóZÑì?wZMÈá†#©.û‘ÔŒt"¸žo&³I¬+=fäR±àçé»D/w:pdUìºåÃàó·KéúÁr8~cY\Oåy¢>ì5#õÑò„E/íï�8–¿™¦SÞNyhÄÑÆX,mTÒˆŠ ˜÷€l,µø¾HÌŸ|@.(ˆ—»:A/ûˆ¹ôb¥A{¿Íø‚4øæaÝ0ïœë|ç£O—Æè/qS›ÐÖ.}Ap}ÕÎÖ`çt€þqμó%ƒ9£Ez§û§NìÂþN÷ìÞ÷‹ÞŒÏIbµT»Ëí¡ïÎdn÷‚?w³÷¶éx 6î9@ ¿û¼ÔO….ì§аv@_:}é<ô¥óЗÎC_:}é<ô¥óЗð½pÞ�ù%_¸ä·-ÈvhëДž‰t˜X>*Aî­Šº“uꎠîºÞ‚º³juwïÖ©;9̺AÝfU†ñº[§O˜,aö‰q×k´)Yݦñ‡B»r¼Šv]: 6n¬ˆ"fÜc¼úëwˆ ÁÖÈkÎ"“ fKÕ´®lñÐ6ßX¿ÿrl`lgtwLÒÊS¾ÿúR#àËy&ñ|!XkŒP7ÄäðËXç€!+[|îñõ|yû+”F_sö»ñsqŽâËø©Ö {£ŸX/­ë¶ûã¨Cg•qÔ¡#RÕ=MGîYaù²:²W§|n˜ö�å ½¼OÆôfkËàõvqµwf·¨Ÿj ž8ðª¬7IgÙ^ËÒ-\&µûRÔ›¦QošN½¹­Šz/èôc“N<8ÛŽ´Ò 4‡§cP@ùSÀûORÜ&Ô ®´ÿì�•ðo´a ön¹så‘TÄõ >ޤZë.@>ÍìºõCÀß¿ÉïçåÊ>ä9mm m’Ú&Ø+ÚØ ]ƒ«•#Ù×åÂl±YÚÏ‹~ø1ø×ƒ!Sæf žýQ¹ê¶/3ËrÈ÷íd>'YÙ7½vä�þ»þ6LOó·¡û£Àœ§ùl`ÎÓü±2çÙÿY蜧» è'‡§Ë(«N ÞàosÛMêö¢þpŒ³Ö}o¥ãஸñ×L v'‰ÙS¹.Ü¿”œ4¬<eä§'+ÇÜòyð7Æk}’†9ßËÍqxöÉq¶ü¬¿Ó3¶¿³!µ¿å=kùš$€oKG~°n°¯ÝXWž\Hs¦ÿ;Œm”mâoÐ<A):Gðr§ZYs ·{wH6¼îgrDX È}Zæ~oµÅ(îŸ}×ðM$b‹ªÊæRGå¡Rë (7äg(”Ky1Ä®ÝÉ•·Æì¡r­Y=t$å‚òu¬Oæx?iXeq.•™£ äõÞÈ•ž‡S;Í9ίÓrPN(/o‰Å÷Ô½1$aó”Ëw»ÇÄËéÉö:È€gœá5i/Î ™ ª5@tk€,ShE±ŽÄ_vŸWùo½ †ˆ¶ ϶pþ¿É?Í!ûЙÛÿlŠž5sÏ å¸u qoS廥ÁßgqZ”r¤ZUÙÖðÊFƒõÌQ•m³,Ä=fUÙ¶0Ë&‚_9§*ë ³,ú¤UÙ®0˦AÙEª²-a–…²iª²Ía–…ñ®û[_Þy-7ç 1,Ý'Îí£SÎñªçû¢ÞñÏ÷5EE¼‚íã“×ß‘ÕÞˆiê¹ÑÀyѨÀw‚}<ý÷²ÞÌAªçسŷ×ðmóƒfyyßî~“·Óe:Ï݄Ë´~UŽ9½óB†ôáéÛ¾÷Ÿ4ö9És¤ ¿v:/ ò˘(Ež?Û˜J“ç4"Úwë)€<7ÄÞiÕ»Þ‹0‡—ÊFùÊuh­e¾Mç_wÔ8éÞzÛ¥ô=ïðùıXâ1øx¥ä¯óiÆ_ð=ÒÒy ßz½åËto¤Š/Ìi_A粨Üö‹‹©Üz’¹ú‚¶GUß0Ì Y}>>^n-¥{¹•£t>PE—æ|Œ®™£K8z–péA,ÝŸ+ô}%U¹¾\¹ë÷\9W®8TߢøÊþÒÞ»Ä?‰{Œ‹böxJÀΪ¦C¬»ç6¿®û’¾Š{³Å·&³΂¹/“oP½x©»c1÷aøAßiGß{Æ\ÉÂðƒê©MÂoß›Ìߤï¶"¿Ôgÿà³§°Ì-Ë([|ãUŽ¿ŸdïŒæ_Û“ß…‘×ö½_6çß2p?j³÷�iŸX¦Zï«qnªÚë‡k™Qû/<PÞÒu¯ ëšÀÛAæÓkôö¸æÞD#Ùóh`²gŠ2yóáP9ж³u¦f̉°ny­IÊÓÞ¤ï¥ùy³üó�¿?©œøý¡Ú†ûsÑæ@6m@Û·.¤Ø¯½_´Ê Ú#Vûti \§åt™/ïo§{Ú¿ l§jÝ[õ[”õç•ïúŸíLäÝ#bÜþŸÊw#z­Ã7ŸÔá›ë•:l|.¶jë°ñ}¾9@©Ã7£Bµ ×Ú˜=`ÏWýPZªÄã4¦Ü%Åv³ÜŸ¹É¹OBž6ßÍðÙ·~9Êåô”iÝŽ*ÌŸnVåOB2ä9ý».üð\Ř沿úŸÁ5)4 ƒ;ȳ†'ÿRƾžõè'±µÐ-8_¨Œ£I;ÄÎ^ˆë!Göz>½½S†ó÷YDphãC¬ðZTà7×H¢/Gîžwõö.Ã8º+ªú«½òãÀùÒ%Ñ@ZH£-‰Ø»Íø>Ђr*zY@/àÝTˆ=štxÆùã&àÙä¡<“-8<}”±@™€s] ŽY:u$~#ôÙÚ©‹Ž¼=€W ãx®›Êúþ{Ð÷ß#Ê=Äúi@?Á÷²Å×ÿÉÆø&Mè›»'Œ•wÀs¾Z/ý>[Ít®è¿Ü¾iÌ1äõnßz¤ê½ çËòoÎ_ûXË…Ñtž\ãw‡3…ÚOν{nzs¾“DÏE(’ò0<kÄwÆœ 6ÓuRîDi¬é9ˆíñD“\ïK‚£c²4ßvÁEj/€àªÆy“Ž©`+¢�ñ…€;`œñ\È#ípoϬ3ôu¿BLtßKÜ™4ÌE;Hû9¹õoò;ŠؽI [©ÉwÑq¡#O¨–˹_#¦e«=tb×ÃÒ<«P½f81uäRžš”eé£*—/,µ T* ãŒP˜á&†Œf|×W¼<áfÚ¦j|¿ã§$×GFJ~^gU|–4¬2Å“º-…ô:WC;à×dn%Ýé÷7:È Õs^"¦XÕcs ‰9°¾4|U4ÜG $ê \O¢©A*ƒ—ÉÆ~cÁ¯Ás#ú`üÖJnÇà³6iØ î¿©u.lN]ÝáIÍ&KëÕ$Çù̺ø18¾9ª¢Í¸¶¸ú«wÐV·É²îKb¯c‰­LiN­\è¡gMbrÕ§Y3·ÄB™t€þAvç.Oªê‘öÿ|·Ÿe|N|âQ¾çrwY+>'æÌ­={Ow±ûeÖõm…òª»*~Mð›¸6Ôg–©Ž#òšNÿlñUA^µ!äÛs@px'VÜS~²6[Ü.Û@u<aïÉ ýeúò¼¼µ®oÀY :ëFô¼@þâlØfh«MÒ}L®4fÏÒÑ °ú‡Òñ³à!Á¹ð»søÛ§ ®&ö‹W_Ô9Ú"øV2êUº7Ó\—q¼÷Û·h|tHZGƒײë¹õ«´~°s½jýÀqé º_è0ÚÖƒ<`]h XòãŠÛØ„>·¡œÆ}©O!?~^º·ÀxâÁòXËIe^¡z¦}Ð ¶½ŠÄ‚ÍÅV.$©'žîɻ̻ìÓEëgíÅçȃ·D³¾P¾ØNùvI|mâü2Ã6ei ýØFyu±*–ØçGû\!݇ÓîzûݾõÞJ­º/5ÂøÔv‰¹SG“ÝpKy�^¨ì¦z¤6><AÃ}6RûíY&*º¹@wGxºØ±X_—€Î૆¾+ ôPN(ÜׇòA¹w‚¿töéH¥~àÀC¿Ý':ÍY þ à ËÕhQ]tÿÂçÒùÈÊÊWo¯„¶::Ryºè„ü·¥—<%kðäÁwãzIϬAô"ê%½D z;ö|ÔKz±ôZ€ž³—ô4èAžÓ3¢—ôˆ½fègûÿÞÛðœ’̵BШAOéÇwHåÃîïÞqÙâ³jhßÈcý⹯a½ž>`H{öß—g MBjŸKðmt<Õ¶ÓïB=fzjµêñ·sûWÒXEëòÉÀß_ëè÷×ïmÙ¢w7–KbIÖÿ!¶n¡ra¾ŒÖûž m8ô•¶oúÆV¯-<òÒ-ú<ýßG@'‘¶å�©¡íülaçùþ`'ÕXÞùÜ£L¿>u¾ò™tMOšÆzwÑ_‚Ìű â/§;ž˜X,f§ñÚvˆ×Ö‚±Ze 9|¹â°Ò+çXÊSn¨®>õµÊ’Ùýñ o‡5J—�qieO q5^“T¿Í ßø 1DÚ*ÂØßVüc.ì7×2yâ²]þó¼é>_¹~y,çålÝJÊ0.ßÓ—Ú2ãÖ·_l寷yls>ARuÞ‹Ü3 WñYŒûÜž2:Þyè/Mf{ʬ˜—DtÆ,'?œÃÍ·fJyœØ×?S?]9S?^Âù¿ªÀy§ú!óNõW)çên ½á»nˆi«ä¸X+&öüFŠ‹­[¾ýÆxî ¶ ó ê¯R±¼4–g‹ßîEùvºGçûÇm$4?ìü-)ηlæsG:ÕLàù‚§Kûàå=ð4w¤g=k0þ 8ëQÊ!öú×&}0f~tuæþîzÏç µKý]šûzñ;–OWu~¹ßAÒ7íÿ[¤xn5ƒ.Ķx�dz‘{~Kd:2ÈÁ㥺í¶PZ(S¤÷ào’c›ºLŽf‘ ÝÈ;=»H±„óÒó’m½ùtþ܃ôh¿š Ñ¥ô^óá—øi^ÚKqë%\®-éNm`[ëžçžãš)ñ âY)o ú×4 ±˜ñÕN÷ö°þþÞž9©L„Ž…R¾k‰Ç|ÕoKþúþ÷ =ã'_²5{²|V¶5°¯oiž6÷®ˆòw}g±ZvŠûéÜÅé]ÀYõtNr´o}võ@››&¯ób_ñÌu'«çK¥|'¿Þ±ç(SÕù.~è?š*Šâa|EÙý¸öÊë]€s†»ïV>#-¡ÊÿÐ êŒUÉ K-Õs‹ú¹zEÕ?‹ÁÿôO™&öQ¤ƒþA¦ 6¹GU§-O¹!ž§…xžâ¹5ÄóbݳTû ,QÒ&´CÜÇÄìÐ þèÅž nþ/݆EbÊÆ›C¥Ü™ŸBÖ9ã³Ò3·‹i¶Cg¯âй±lÆ»·;è<ýß ¦ý+Ø>ð’N÷|ýå‹ÚC¾Ï>M!\š#R4Þ}\ËÙ ÔEËC]Hëªü)žmôç-ò™!–ªi¹–Óö›Mzr°-6[þ|¶ç¹ Í\?ÍÏ"ÍæN3£ù“ùc?fG3ý ÙF±ŸæG_iÍ{«é!KLï”®õ‚}TÆÜí(O&f¤W9e&è¼ö³pu.ÓTîúu\8åñÌo(_î;ŸÑÉíÍasÌÒùÞ—–1Þ-6Œ9TÚóMÏ,{š¤IsQÄ@¿¦kßì\F+<KæÕŸ¡ñî³L0Õ(ÎiÖ9ŸìžžM‰}ßµ³¸MúCƒtÚÓtþ™î-À}UÓ¥³Ðž}”Ŧ՘wàÇ2³¥½†øŸQ~g.`]€Êî’,»õ¾½Eû‰þþ{>®òñ¡8“í™c’üMáí/;¬úî êÊðº�þØÚÅ+ûðì,*ûµdžã‚ïÿ�Ÿ£9Ù[$¹o~Ž“{È5  !è·uóm™?ÝÁdî?S>Ê/gyNÛó²ôýX¨—žý&ï{`ûpÿƒb´ ItÍ\cÍú™¯s‡gŽ(s‡Íg#Ýwà¿õišw?¯í™|e®óÌŒPû Çk†|¬ÍwþâÈ#S¢­t]_½–&’VÅžý€óÆÄBmÞ7ç3=LòéÁÈù¯!âA:ހϡﲳˆÐ÷€MÉgúÏ#ºâº{:%PwOPê®æ–Þë®&?´î6ïTêNê½ÐýÊêîW;µt÷ïÑCͶ@=Ô¨ÎþeèóŸuõðË“¡õPs“R5WõRÅWV¿¼éÇÓïn Ôzxj@ïõðÔŒÐzøåsJ=ür}/õ{eõðäs?žžÚ¨‡§*•zxâ©Þëá‰Bëá)Õù·O^î¥lWVO\õãéáɸ@=<ñRGõ^g†ÖÃë•zxbE/õseõ°iýÐà \›aç8,DñЇô‚ïÔØAý³iê˜�Ïãˆ@ßµ—ç’·‹tæˆ(Zñ“˜SÀ3gy׻ÜYx6Óa„KgáŠMçàî .ûÇ㔲ßtɾї«iíu °£Míh“êüçaœÿ,í3P6Êç?ù÷÷‰êïÛR}VÙ– ÿì}ŸØx[è>Q½L©—êü^ö ÷•í–õ>ÞÝ>èßïnLÔÝÆ[”º[Gïu·~YhÝmxK©» ;{©»Æ+«»ª·~¼qeýzX¿U©‡u¿ë½Öu„ÖÃúÛ”zXS/õÐpeõ°î¶OUù_•*ÿ[ûò¿µaäëTùßºÞæµWVk~Äüo­Fþ·V•ÿ¹@þç#ÿ[«ÊÿÖö6ÿ«¿²zpÿˆùßüo*ÿsý€üÏFþçVåîÞæ5WV«CæŠïKè}[xÃØ·cÜ:r4P®çuàZ£ÔAåæP:À¹f _¦ýÍ„J)ÿÛzØm¿‰ÓÃVY.)ÿÛ*ëaµâº÷@C¼<!§ß´âB^ü‚Ø]ŸÇ”|ŸÔáé&ßEWB¾5ºò]}G |WQÊ×9ú‡É×YZ¾•¿SÊ·rkoå»äû È·âs“Y_¾Ç®¤|½ºòujœèT¸*äù‡Áå[Z¾ÎéJù:ÇG"ß5%L¾O@<GLkR¨M•á9_`~*Vió¶j1“s¡ZÎêµ6åY1¡Î� o­õ èѺNC9”è•~¿ÃcpÛïJú<¨ÏЍ@}®ú§RŸåBc‚âû-ˆ‹2ûð¯n»$·ò;B «Ê•cêY§Ýf¯<>´3z@V]F ×EªWïÌ8dÿo“ÄnÖøûOH™³sïaÖèʼ\cþ¯\5ÿ·2äü>;ôW7¾#îÐnÓʃ¡e^¥”¹ã»HdŽßJ¢rO1ºÝñ„àû3ØîC’¬«uøJa².Öõ ñ¿Cÿ?2þî«{4´¯Z¹Mé«VnîíXÐù1<#ù§m~V°ïÿˆöï8ðØÇ²}ìm¥lË>øa²]!„–íc“•²}ìŽg(+Òz#çF]9¯( ”óŠJ9—æÿ09—>ZÎeŸ)å\öqomxÄ3¿jÃ¥qz6¬5ÆJ{–ËyDÈq´·y•vNUªñý³RÕ÷Ï–ì}NµLóük¥/ÍTúðÒÑ¡r*únÿKt_oÈ.]'WzR‡§’P±f¸²­ÙjËuùâ@¹.X)W{IïåjßZ®Ë:”r]v2”\«“H[‘‡Úxô+>y>«Ã ;—[,ë…<•1ÇÔhÈsÙMò\§”çÒ#§ªÏ.} ´<íÏ+åißÄû-yâ¹z|œQ£-O/K>¸RòôêÊs©Æø·T5þ-9þéËsIThy.¢”çÒô0äY†gý¡<7—¾í25éô{O‹—]©~ß6A¯ß/Y(×%ùJ¹.ZÖû~¿èw¡åºøŸJ¹.>ªßƒ\Ï€\۰߇!W5O7^)¹¶êÊu±Fÿ_¬êÿ #éÿª6, £ÿ/RõÿE›Â«ÂŸêô/ ~HÿWȳFw|Z¨ÑÿªúI$ý_݆0úÿBUÿ_˜þï‘gIù•’§WWž *å¹ D)ÏâòÞ˳ø­Ðò,¹ ”gIÇ¿·ß¹Rý¾QW®%·ʵ¤ŸR®ó‡ô^®ó -×âmJ¹o%×z•6†e§óŽèÙ)ÝïŒõ\ÎP|3‚åùò<Žû)þsöä¹:ŒgÎí#¿1[ãÂø@È¥g ¦ßeiEÆJç�WnzîÉFs²šu6Y Î$9ûuV´8 Í ~NVÕ ÷ížE€ ¹TÁ‘Pq¾ëé> }ç¿ë:}C+‡°îÝm·ømcžjÿÓ¼L‡ú~Ó¿çÕ¨iÏùÆt/å±gw€Ž_Öz/aþ€póBíòsß ·<~GÚÖ+Êo¯C»Lò^ÐÉ$ëV’ ¸F¸Ï•¾mN÷¼·J{ÞçÒõqn“´ç½@𗳉¢Þ I2÷Šòs¨76Ô{�ã'Š=ò·|Ø7V|�߯þB¿ 4,O÷ÈmRœDÏSíɬ½)*‡û.lÐsé¹Ù•Æ&éü™¾µþïäÿ挪ý¦ßà?cö½xfgì¸?J2£g&Mþ”ž™dyïÞ´ ´šý¶ô„›Nú½x¦o—|Ö Ò±ì¼×N¿¹të‡ô»Ðž­ÚgÔ{+MHkmKáiGøí15°ö´bY¬ÛÑ‘b¬¦úßy=ûÒòÞ} ÁéDå úÍÀ s¡-÷Ù†B&E§™Lî³Km15èж˜uøñJüäÄúÞWÔ¥cj̬‹§ú`�žs<Eb<åT‡à ül_úMY¹<å Údyo†â\Ø 4`ŒŒ§ß´Åþe©šAm5Ô7ÈY[Š%=õiˆÌ^}åìJy ±’<g4KòœÑœ~§Ï7Ú:¹?Y¢q¿…“æ·€”Á0m^îodtšÃ ÓülñÛÛÌ4Ú}g¦™öy§!hy2ëâjdž$Ù ±PÞeUºÕ£QüŒàøi ìË3[Âä§hY•üº,;gQ??s«‹Æ ¿­ÍŠÄÖì’ÍÄm‰ÐÖ Äsu¶FP§ƒÕYaXf–¿Î"¨³‹Õ9&Â:»  áêt‡_§±Eª3¶=ÂqG.× Ýh“úΞtÀ¶bÏiÓxP²Ó÷¬ñÛ—1à»–L-@§[›ÎC‰‡’CÓ‰;}V‡óµ„ÁèŒðë$7°10fQ„:i€2®ÎælÏÍêLŒÐöX¹èvåX>Æ2Ë{‡A# ëm÷óýpq|W³úÝò-—Ë ôk¿hWû5µÀ77~>Üß5¬þ–ù–ËÕò=Û¢tämˬ3Õè bDË{³­JnÔ‰ŒËD}ãoûìšðÛÕ.µ!êÉÈÚî+7K{¼ží‘úçì®±Äs¦.?ïXeÞ1÷¤ç±í+³ÑóØ’ˆéd@n®¦…ù(¾'l©'Å.ã!ÈfÓw\C?tX¶’b„Œ¾=j/æyQ³îú+ŒÅûŽýþ‘jg›'Õ9ž1)å:Ðu+ä súíÉ“uJ®Î¢åô|üµ¦6,ï|Þ“š±ò)¦VïÁ–-æJï1à·SBüßNEºð¼c;Si¶"§/Æy È=så3¤lz#ä$[|p™” Í‘ži¤AÏ ôäùŸFçx.\¹úfÔ€¼ß`õYŸç¤ÁóÍì¹Fù™ÀÏòü? |~_#<Ÿ!=ïi×hO3<Íž7k”O„çýØóz þ=Ùâ,vžo[ƒÿVxÎæzlÏïo‡ç¬ý=íŸY ÏYû{4Ú?Øö,ÖþöϪ…ç¬ý—5Ú?ùgí¿¬ÑþY ßY¬ý—5Ú? ÊÏdí¿¬Ñþ¬ðœµÿ²Fû¨‡ç¬ý—5ÚŸ üÏdí¿¬Ñþ\ßLÖþËíìy&k·FûøLÖþnöçâsÖþnö?”›-Þ/Ÿç¬ÑþÁ¾î—¿Ë§ÑþÙ^xÎÚß­ÑþÙ`¿÷³öwk´6ÖÏÚß­ÑþÙ±ðœµÿ’Fûøý¬ý—4Úÿ0ÖÏÚ©^òÓòy3Øû_—ªË=v9ã${^ì›ß‰ãΪ~G:ob?·¾¿#Œs:6BLtn竆|?ºc\”çqps=«Î0†?‡¿ç¬ÿm¡ßé£ß¦( >6KçFÜãIбÐa‹9k]KÎ�^¢ú»h4+(ÍœŽ ùÜ=Cqç&¸§ã‘0 h˜ñüŠß…JU¾“ Ç2!¶5œï:Ë´ðûÈÁè)uó¸s²Åû>‹¤. û~}ßó<p¾Ó¬¤ƒçÍÉ:²ÿß"}ú‘Z錖Q¾XĹñBÜÑ“±ï‘éû%3:¸çgÉ@‡µî2ŒW3èÙgƒ€/›%?“à4áyé‘Ô[\&x>í+å<*ö%„ßK×3Ü¢ˆgªÊô‘.ÀÙ$áÐï5hÈÆÊ{‹ü8—[5p _Þ›ÉáÀø2c€v{(¿òþø_Žâö<±³qøoYá·¿8èœ+;£e˜¼æ‚ö¦5¯¯jG´øž.¯ÿæçÜ‘œ£Ç÷éšÃ«ÒÞ6ù½Gn¾™¾ó¨Æw—øñq ËÈ뎂˜vuyíoåÈß:?¯®»öÕ¨œHxEÜê’¨=|ß·È*ÙwÍPWûÙz[«Kß§™K~ß Ï~£ß‡ÄïÖ½÷¯À³¥sÎ ¶n¦^A}ÒïÆUÍi¤óûõ„øe—=#Ù©è4iÐ ëì'J§žx”m'Œ9êóŸ¸5úÝÅyPb¦lë8þ„Zc×Äœ¶˜=# ëÓd\Ç8ɯ±òflCèïJ ¬Ç} Òx<×77†{%‘\ #óJyÜÀsæ^(ÓèÏÛæÖ‡ŸsFVç¤Èꌆqr`WgcuƲ:c#¬36³î'G¹:#˜·4²ùŸìŽpî¨Ê8¸:=äóVVçˆõiͬ»ÑËÕé ¿NéÜG(ßY$Êlñ×YÁü,IcuGXg”ÃÕ™AY¬NaY™u‰quZ#¨Ó&Õ™áZ$±A™B®ÎÜêdóí‰iÖ enèæê´GP'ë+7D8G ¯Üð,Wguu²¹ÝlÖÙ�e¸ùä‚|Ÿ±Õy&BŸÐæ,§åvt¬–Ö‘p}¹c¸ÉQQÐØoªÎ˜]ÐDs¡ŸKëË®aÒº0®C"MÊfv"à­·h „‹ü8 þÜ€úÙ|ß Ã"ä»=³îzn~¯ ÿé‘ê¼~G„uz$Y]¿e…òÀv¢¬P(«PõG(¶V4 ;B>!ßÐÂÉ&?odãö€ÃÖé•d3àÉG6&6ÖáÜ:Œõý=œl"Ll¬ïée¬$›þ…?’lظyÝ·ò ãæuÍ~ÙF²®™Èê,‹°ÎDI6×û‘dc–ø¼ö£ù4C™ZN6æd“ÌêœaÉ’l®íÿ#ɆÅ>ý"ô‹&ˆ}úÙ9ÙX"…É&R»±0ÙüXvÃâß~‘Ú Ä¿ýx»ÉŠ@6,.ì©ÝdI²é÷cÙMŽÄgB¤v“ex»‰ –4å²:#Œ •Í5ßhÊ&; ÙüDG6«�ov@},ξÆ!Ÿg_“åßÛso²g«É°çÏ·¿-Øž¿(wfÝÕ]üZ0Тû¥½d÷…“Ç5zD?B{4FŽŠF,G#œ<b䫉’†ÑËÑp‡A£>³îªF G£: µ@æjKG£& Õ@#AE#—£Q ÈSú6û÷s6y¶F“À}i…õ4vg{:uhA>Ù·˜_w÷lòú×Þ [¥µ÷Âö<µþòHÌ'@¾5H'#±Ð›1»(–_[Á=$؃ĖÐâ›Túªñï?) '>Çû÷eYdJ9ÑýC®èCÁø½Å'hòCõVd ƒ°Ÿ>D)ëèX¿¬‹²$YÙBÌ#AÌ·›ß'"ÓŽðö¸D'9~_ST z·jȦšÊ†í%ÔÉa“–ÉoEõž­$An—ë‰9— BxŠmôïw.jPЙü©DgÞéPt n‹#û^ ‘†<P: ã0rr ”OP”‡ºiù°öáÿc+m…H~ø�ý6…AÆÉ˜2އ¬yÈ…òÉ*lÍaÐ(ά‹nãx°EȃÊ?©âÁÁñРǢÇq<8"äúm”j$5­aЀ~UÏñP!õ¾½M~8Ú ãhtÇCC„<ÀXmñ÷÷_´K㟡K;¶(jâëqÏS{àxlèb¾0ð¼²@±@£&0¶ðÑè ƒøÁ(k`lá£á ƒF¢oϘ"N’iÌ ¹v4ÀߘÔq’ÅOãÞpbð¡&uœ”Åñ ÈÉŒM±…F81ø>£ooµ»’4KÕ¼Ä [ðu$ç*ºöÚ¨µÇÖ5éS6žÌK“ìkžU“F9¥±HÊ æá^;jïÙâ(zNµ …ëÊø=)~|Ñå«ÒLô­÷kñ'ô»µö ÍD¸TÕRð91e‹£é»Á¸ïבé»y7»ÍŒŸüNž1’-Ž<ˆxÖ7Á—°õ6S¨³¯šå3D|ßh ±ÇMþf‹ºß`n.~KÏâ&Ègd¶8‚¾£‹|ý:èƒ&«Úìö·ù6ù̾îüþëQV®î[Ø=Â÷²ûZºïÞÿ¿É¶ƒÝçÀ}-»}ˆO²ûf¸w³{ü^m™tŸgÁs”Ø=Ö5‡Ý»q/»oÁs Ù½î-Ò½Í ÷#Ø}.Ý÷'Ý×Ã}vß÷±ìÊötK÷ùÀsO»‡²=çØ} Ü·±{úÝZé~nÝ÷'ÝC½={˜²è ©]ºwD‚ã} »¯†û*Æî/q°¶4Áý"VW3Ý÷'Ý#ÍY dÞ“Åî‘·qŒ&´«'ÝwÁý0ÆÒOd÷ ϦÓ_ÔÐ}Ò=´ëò·ŒÜÛáa÷9øÝZvo¦ûB¤z¡í—³{ÐÅå&ÆgÜïf<4Ó}÷Ø=ËèÔâÞ&7¤édtìpogü€¾.2Û]Îet ]—§±{;Ý÷'Ñ™\ÃÊ"ÿÉŒ‡D¸—Ï©6ºc}kÿ"®Ù[ªÊltí¿Š´Òwë«üN ®ÑûÖí¡Içõô£ëõô}Í‘›e?…{�|þó%bǽ�Aý”j-?óiÒq€Ø3\Bc†•έ$Î\ã;¶N“ß±LCÿ…û:RbƒÑ¾4|Ô¾6<K~/õyì $èÓ”_OeT—´¯€lFH{­@8Þ›¢ºàòÀóö¼o¶˜ü<ó}ܺp(ß't)¾MÂÉt#ÙÃY×sTá'Á?R?Yò/˜×š-þT> ¸Ì§ûhî½iÜ—{5ΰw¨÷ wHúggŸ³÷\/黡Ü~ß~;¯´mU_:œârCÎ}­#ĺUg)Eçʘ=Ž‚¤¬›‰Þ °‘Uß' «´ü¡¿kJKjÇ*bµ‹êhÑš±Ä„gÔÁ½ Çí,Bbñy…-i˜3ž¤bœQ=†²!¤™Q6½©ÍZQA¿‹Ûèy8Fí õð{‚Ò2Å—YE²2ÒEèó·ÞÄþtk?v~ùVé|Ç»?…>x ÝGêz»ì½Ç·EÚwm¦óÍÁíÉWfG'– š+Écîy° ´mÜ¿ÎÞußž¡>ì{q`‡‡ˆÕ3Åš‹ùbíÅ5b}å˜lÚ™ó$Ø$ЪüÉÝÒw̺3¨V>:³\½_îÂAÑ~á¬è¸(ˆî‹ÛÄŸ½¢}/Ò>£ž#ô€‚}mž°ã¼Kh|‚p"sm”íÛs&hŸ‰ó‚Ú']†XŠÎéÐX-«eMßyu‘+ÉÈ çJúm;ç’‹|rfï[`¿¡}žõuWz̬3c1c½K’†aÝ£’†]Âo¯ó¾Ó>Ð1j!cë©]Ydý­®z™Ò å?œ+:FŽEŸk²È1&ý^nJT[¨þùÂ8âv>6ÐQy¨ÚùS§Ž¼“à<²*¿¬@˜ï<Á ð¯žI,õ±ƒH1Èý}O©uo(„2ô¼¿Q¿§trïé9ĸvŠ ¾ßׯÂCÏ/¿Héà÷DÏ…±d,Ðk¦~aÐØÑЧ7·‘±¥$võè˜=cï'& ¿âÚç€ÚÔÊ”ú{ãH‚3¡6`YθçS3ß?r^_3þÎuÆm‰÷£Üd�Œ/{:΀zy4Öà|ûê[žpx^¨;†Gc}1 XOÐ> {VÈÍ·/œû8Û¾KžO¥|ÿPÿ´QJ`}ؼ:j]È~?ê--û²;w…, úó×9 sóèÄÕïþ>d9O’ÐLmì%¡ ôke¾8¨>=Ý$Æ7#è´¸âbö|!4Hï* £vƒ±;îÑtíì¤}ÒR5?6[Jý¥k_Ì6–?‰ç{¸ê¾tXëü.=Ä~fú®Óê/ßZæ7ØØ¦Î):ÀÇt€9>æ<ø¬óà³ÎƒÏ:>ë<øô…áøAy¼‡öda{ EÄŒùð[®ÓˆožÌ·ônÁXúF<“Êá;_"±»Làïw–ã·Âæt¼ ô ÜÏ<âñ!ÒwY ÿJyOÒ«ìog’èùÖIò{4-è°ÿ¥øCé¾jÿêëÿ‡ø�ýîw¢4/3ϱæëÙâÝwP?Šs2>?z÷ôvè‹>?:ô&9VCêÿ‘a$fˆ éÀ ŽýàÖ€þÅs ßZ1ØÁÍôLtçÀwâ{ÝsÀïPúì5é{Fø r<ï¥ŽŽ›MEúM£j°Ó ë>÷ûâ«k•gŽ`Ûè÷Y¿Ê¾Ï}·CÚ[;t KCŸkɇ΋ôûž•Ù¸¸7p9ÈÉÌöRÏê"íÃß ô*q:=£$¶¢Š˜±/ÑõÁù§¸gúÀw>,ˆ¶—+é!é]ÙŽPV(/} ”4Rþ‘Ç q®¢]1Ü;,§%ùðmAij#lŸ5û^é)V/}3tÔFy¨#!×ÀßFº»Pjg(ƒgÒxW“j©%Ñ9OçÑFáY‚eçÊdÜß Ï;dFe9xs€Ì°ßÉçï„ó¶¾Ï1r—›>`6SÏÎ!ÁhøäÍ}CÕ·w]u޹g=©“®ÆoÌ'åSùP›¨´ágÒ Ý¡~-íPö ¦g™'¶÷|Ê”å—ÐWiËö¦;Ô²þ¢}öÆúñàhŸza(íS–'æ{¨¾#åA|Œ)ŸóîY8à›Ê3× …óŠÅAe,½Ï!ŒPÎ)šÜ–ž=ôü(:§Xœ£ICšËóȱ¦T·)èz/{dOÀûÈè}kcÅnVo­~›‰]‡æðrY©n–œÓ¥Æ xΗõ^P9 -ùú'ÚÚŽ?S¶Sð¸ÖI×x©Žæë¯ÙA>žè³E–ó¾}"Ÿ;ë†úìPqFÓúM€Ñ´ÿV‘j¯K ÒÜ ºÎÔx´¯AÿÂX¿½‚ß`A]…ö£QeÕƒI=;ã¬{M%1ÑØo!é ‰±|aR–%ž vösrëßœ„´VX’’>ê9Ø ²Ì2˜Ô¸÷“ÅI/ŒƒülIpìL–-Êdúbï(cÝ7AnOë3tÃ8oĹ!ÌÕ*²o(¬LÉ9|韹ÕPî87®<å†êÊáS_«,™Ý¿²ÇSô{Ã"š[áj Äô$\Ó$X8—i”ûîÇÀõ$ûã‚©ÏVð9w Ü=Û >4ÊL†ØÜ›ÃägÇwÈ ­µî¤aÎ?T¤åÜÔþ:ÒÏé{±®õ1{pþatÔ•±rŠ”¨ q Öår·[+¶Ð¹‹-´<®•ï'5”_:×$Ñz|vº_‚¾öúñIðu íàý’òþ.æ 8&\œ,Õe:Œï˜ÖÇ‘>ìlÂDØ ¾ c ÈïùÜÌÖHß³‡<®¸Â ù÷xˆWa¼ZÝMR+¬ £'HªóU’ºúoï8$  c w%!(ïŒÙ[º[­éD¨˜ ñU:1CŠïêà<mÒÄrHqÏóBh]ÅÓB:HtwØ=˜ Í@ž…çš$»VvtTâ^Ðc<´ùUbÍÜj¬õÔ‘ËjbÁµR)þ¿ñFY~áêÐZgØ‹õ w“þÖ­«¯/îïiòõÅý=»Ù<["#sKsdÖ}=ø>½b¡Ô7Gm"FÈUÚ-!×$)«rIõlBý 8&Û)®Ÿð²¡cÆo§ÿz^&x†�'á\V…ã}“ŠáIü‚x¶rNnjúbÇgÍ8÷4îáÔ6õÑ3‹êø.RìÓ/èiKt<¯¢; ý–qn¡}U¸0D<‹øcÏÒqè>:À×1eùËç´ê£ù²¯Îë·©Ê4¢Ÿé¸Ùí–Ö¦4ska›ö;Èbb_?°îïîî•Í é¬K®‹ÓzÚ‰�uëm?³Ø“žÒqÔAn¨vÆ^«4Oí_™"ùeQ¼ÜÀü)Œ‡B\{θ[د®9pM‚kDؾgU„¾÷âPßóݾ÷áyßS×}Fé{®Ý'õ݇-qdpG>Û>GÇÃ|aAF&ÍCg)t럿¿ÝRPÜ’-^ó•DãÒ j/ Ýëhö®¢9t±ž@ëóVPxŽGãéYÿ7¸2c¨ÿ@ý"N»„ƒõíGÏ ÝÙK^?üÚÙÜ6‘ü`Â�6nÓù+YP7;[áÒ^Ù~ŸÉr•þ¾æ$WdzŒw¹½ßqÏþ2W?ÅÁmØß±¯Kr½Ø\®RY</ćÅq¹6ŒÏ|^@ã1.?Åu.z¾ÂNb’ß †8lHç{Äí{ÕoÏldÌ,rrµ3.æ­â›`ÌØm¶®%Ï®© ±&šò1gá·v<Ä»ù ‰a³‹T[× õlÞ¢šÕaÆ:CùuÌŸ¬uÂ7`ǵòzD¸ëiˆrL`ý%øžÍuô[ÏG¥yž„åò4÷¾åC#»6âØô]ØÂ&©ŒØüÔ¯hÝOb3Ðv¥:å>Z´Î}tü`}Ž´‡;o¼\CÓ±©‘à“k²Å¾é,¶Çó5Ô€]îÜ „9³Å«ÎJóŸdÕºĉëQpïví#c ¯}hßÉÕÞ$R³pŸd0ÆRéÙ®ž3ëzùø¥ûJîk�>/DÚþ€yùýnUnÂrdßÙ˜“„7§pÍUì>7[Œ¿Ý³¼°b‘A,^ÎA]àþ“Šz;בTiJLbÈ,h, 4¦Ëí±Vì£1ÕÞl±ÏAV— îßb÷vÿºoŸm V ÷Ïùç=ú(Ï·_þ£¥'7“$7\ëªí×§œßgH~æÕyßÖ¡óŠ5.n½ ×»”ù`϶Žî}úìè6ON. @.S9ÿóØÀrëÚhº—Õ¾mTnPŸå s±`Ït~+ÃBŒhè÷¥ù²x:Fç)œÒ<zÅM4î 9Ϲ$”©–çfÑæƒ¯%;à×àœän°ïËl.:×5”®CNÙ·ƒÁl8îKë}1Xm]êKç7Ÿ>Cè±4ÇÔ—Ž›Ø^9o-!ÛÝÕ¸qé`ï ±pëVS7΃c^ŠkMˆ?–ˆ÷Ò5•»€VÜdæçD§ û§v\+?wÆy=ý8Iì�~�nsîj}îsÁ4_p³úö“rwÒâ\øFjÀ%Ëp}Êǵ¨~@süö‡ß-ð{ü>‹ëU•)©§ ]€�þme‰=µ>žÆ.WAÎæ yKm+{œÄZ· ô]v×̘=hS¢ŠW½(Í-U̺¡PÏžp-Œ®_­ÈÖFcßðù¥%ÛF‚_¢{±Í€[,¿+r Ú©Œ–¼‘*ÑŒ¹ÀËÒZ×#í»qŒ½¬–%«ç½xÑô» ˆCžÇ=¤Ëó‰l ¤ ×Y€Ozî:æã– ‚M>s×}èúÏXûAŸ€í`r¡úD9ùa1C”6b@_W+?GAº^¥z¯=¿—ä:_m}ÚôDëT-}ƒ|ú{ îYÇÿOsoUuî®ùHfòÄž©æ­(£bM€&A©Ä æ5°)¢É $ä³É€2“ –¡zz‚õôÒ6`lÕôÞKÏ¡-m¦ÖöÄÛ¼ÛØrmZéu"žóæ¼EMfßÿ³ÖÚ3kïÌ„Ø{ßßï·kÖÚk=ëYÏzÖó±¾v(ã ‹ˆÞÔ×Ä?=è—ƒ±œÂïr»öLá¿òùÔ…Q¾V’Æ×>Ü ½¢{�ÓþIÆyßõœ£u³ôW¥Þ¿ª/DçØ-EøMüGiÄn%ïñ6vóqàÕßuûÓá§ß%ísší‹^bÕË/±\š;îNkb­Îyq^x»”ÍŽë”9ÿ‰Þùºßl©’c¤Lޝ#ž’níÔâo²\ÚsBð‰?£–È“AÔ_Ëë¾6Vˆy�ÿuS½QoúsñzO± _‹ƒìCyFmó<ד'üq‡ô .÷S?ÂÖÞKïŒ4.»h­ ¶éˆ‰§ÇOÛþªó4`,÷-5zèn·§oÔy™öƒ‰~´q¼?\¡÷£íç2N{áê§ßÕYšöùœKïʺ +/£;™h? É;Ô1,ü¼Ntw“Ÿ÷ƒŸ Èàß±³š¸ß˜Bùûž=žêAž'”ßAÓ;>Ï.æ^-yÔo´7PI‹Ê´eJZ»Ls+i2-[IÛ+Ò.O*iÇeÚ9%mD¦(iÕ2í„’æ•iƒqûàcÊ]<´>²gË^Uÿ“>™~/Ô,îâßòy7¬:O>‹sy('÷\¸gº“H'æ%6]iÍ…ÚÄçL^ô]…gÅ>3õÞ'¾ÏcþßGbž³\{’ôì’ë£Õ^¥zásÍØÖó§X÷1igyÝ×3vôzÈÏ;Ä}tá›* ÷…„3Ùõ=kà ì“¶¤‡êD|à™•ÌEÙâ|M ç;Ž1„n[°¸†l,@pƒuÿýQò76å}&Ÿ9)Ÿ^ã*8€º‹óá_ _7dÚWއA³&à‘¿ÉEiT–îM¥²ÛWF¾\íõö3gØ]$¼¿Ì¼¦óß j×´ÅÐüÛ4´6V‹ßñýŸb=Ö{Pù–È ëü[®‡@³sö‚Ïü&Ëé#{<]Њ¾ùíça‹<Ç|Ÿ9ä‘/Dvz÷É éÄ%~Mëî^PVò˜6Ùö_óLÑ@Ó|ùÌáÉ‚>ÉŠz–d¶aЩÉŒœeD£â&f)}”9»ÂЇßqqûŸ|ßË ^£þ£}<“oH½Ñ€wÜïú¿zŠår¹Óôû°®ØæÐÏn#ù¼O_«òžŠFÑî(hÐ{ŠÏw枈 ÅûA¿nÛµI¾Wb™Õw0=vð5Þ3>~Ç™[È ´?›æž^Kc0t8¯ªxËWL~›å„öT¸¢YÌ]ûÆÅY^xá"×T=h[Ëʦ™“îF›´±¢T÷£<â‹â¿r€óÐÁ› ßI)η é㌯bÞÒ~æÂ˜v†Ø&—Œ¯€LË‘}qV_W«Ô¬:ÍSó²‘å†z>áŠ|{‚¾!4,ÊYø¡KЇ¥jÇ>ÊÝjœÇõ»ÞtL¿ë-v‘ëMš¦¹ÒPØqÌsËÏíÝ`ÙNödÉOáCp]lß´ƒŒ,^Ü´wwžrf§wÏ ý{a§…W¾ã _?^°éßEÚ¤×þ·“Â\†çxì.•©îûK6¶©ŸJ^BŸ£\ôzÛ˜¾×ãËaá?þ¦4Úc‹JYì‰û±X—>ÏrñÝKcqršïOð€.ü{T“(ËyòоKË÷(ý„ð 1Àà~Ù”çÎGÉžh:PÒß4À¿‘Ä¿¹ÄVTÆ><¨¬ o |LpžÑýK^ÊgNSž;‹†B6[p&¿œì$¾· öá§¶"­q[ËZ»ôs%}øvÒåñ¸¿q y}ß‹ê=¢³©×<0mŒÇ.½¡´;×<Öé,€žFß šqÏêæã’îƒ÷9üUú¶9d®\;þ‚ìç8?ñ=%)ð§¶B?å–²M¬`Ìß|ßä»ò<< ¾ónýÊ)×Ûù¾±Ø‡säÚúõøýŠà¾Œâ?Tìõ¢(¥ÛPOÝã[ ½Lg¬ž ’ïeÅËÕ•±©ž~‡åi¸>Õµx<=¶° ðyøØärþ'¯ïz[ŸáÛÃv!·è®tš‡ùâC;Ûõ}É…ß@èÙä8¦.ÓBB¦MùœK|±ØkEßæ:ßcwÓ¾ì7pîÿƒ«2öÁ{J¿žÑ÷¨Ðýêñó³Ø3HzôÎ-šÖw½=²™•twät5.XØU[ðl(ƒU…KØ¢p·Õmb3ž_iÿ̓t¹QvqÁ³áÚêªp÷&WoË)�íÅ\›¥¶ôI˲¯‚&IÚs³2>–E/ïìàcj€¥ÑšX¤áèó>+ò Ì.îç… Ìû¸>z½ýð`Ïtä³, 2àŸü­®ÝMÐs:­ç³EÄ3îÑ_iÏõd0 pv]PÏf‡ÊIœl œÞ?ljßaÉßôðAûFª.–Ã6DßDúACSÿ\<dwϦhÅ•p&ú“]R¸9öÿðbÇ1’ï¥OÆ8½é|õ5ý¦6yÿõòQg¨'Âm¶|Ct†wvÐø"þAßp:Œ÷Ø?A<Dþ ÷é,AìCñ-Oä§¹ŸÊØ{r^ûòº¯öØsÛ³rüØâóÁü›†³ü{†;ÛiÜ”ÚÙ^Ø‹1D{iÜÐ^/Ãù‘ØOåx9Nc‹l‡h†Ý-åÆ:‚CåñÛ+æâ.}ž£àN‹7~òlæî_±‹yØû|ÍIšC°Dú{úÝÿ nï|¹ÞÏj¡÷?¹A§qܵeœ÷HŸ À.b7zÆÀëŒdÑ…;âü¢y+mðâó¤§ÏRz*væäðŽhå¾£o6 ò=ªOó=©Ð]“~e­…ß5½‚M…j™öDdé_ÐÔÍ÷H½L0ø@]Óq¡«ÿçHÊB÷j“û°_ÜAÐ4Ìuí�ˤsÖüŒWì=Ž7}ãáüŒë\Ô ¯êïÈÖ¢o'dôä÷ôù\žxòôûSŠüxJÀù×Å|Í0ö·Ïê>µaÿ ¾öþ¬Ì7GÉÇ×& j‹ÌŠËwG✠_³è¿©+¾¿TßOe>/¢ÞS›¸£v©°¡-A݆"]*úÛ²¦z%˜¦½å}Ô²fñI6QzÐRðjïAë»ðÁæuŸt,ì>½‘Æùî‡YQØ»¹(˜îx&è„ò¨Õµ9b Ô¬ÂÌpæA68‹{XQWþ‚…áÅ®óãÝàÍÃÕ¨/½}üaVy¡ 6ó¼fØç9S‹Yî˜ µÌÖ•ÏÜS%Ì„~—@Že0g Ê”ðyiË$âßtŽïÂÒ'ÙŠ’7mCägFF½¥…¾%\"?ævyÎyø-…§„]Ž|¹¡k­®Éù|Ÿœ¸›}¡ËÉ¿g/|¥öÁg9OŠ'Ý~]šÐ—±£´Ïzû)Øت„c¨÷M¶L§máóbÏ ðöDN–yKO²æÐueUç{XÕâ¶�ý ^ò-f6Ø,<Е®©n®'*]tZ}Jøü×=r½˜î²õ” hÕã= ;ðzȧ¨×{ÐBsKÕ¡ëXUm&à!½}n|ÇEð¦zXuÝIf›ª„ v8Ѩ£WÔ11.ëh×Ûø+ ù¾·~ÈK8£ºhc¾sEhœ„Æ+ƒ!V]v3ž= ±2À2 ‰:/•\/yÉcæ%û¹/ÍqÇy)+×ÓÕ4ôK[á«e6â©Ék™“h§ÒÎÀ±cÚK¨?/Ï—¼ã"Z×fñ9Š*Þvò÷2,‹.þ˜—yªR{ïó‚Ž?C.¼L¼…¶ç=чÇKÊÙg`׌D6½ãŠlM~DtfÏŠ3oSûHŽÒ™Žâ:–ÓµÍá#YAý X9°'F‚ ¾.lƒpíHUø&Ë¢HÕU\©ùʘù²¦¨-ÞïšÌDû‡ÏëdiÀ£,òmÆçK`‡–¯´õÂ_ÚÙ„1ñ#æ½ø°­x8îÕùÅûdŒÎ'å¾ÅaVNã6|;pþ>Úr0 2w‚Ë7âeÔ“Meúl|ît²û…—là›.ÇhîèQë�ú­‚÷ÙÊ ò“‹Ä9ǹ ¿Ä{¹ŽÌÙpd²ç† V®p‹©ºö©É=YóEÿ²Íò|äXWżg÷TÒ>OgW¾£>|GYíC]bŸ%;JsCán¾4¯«lÞX±:ﺜÀ£É»ãÿÅÐx ]‡°«— 9xirf°e7ä³$“Ìí¯$š?ìª�.UU¡N·Ü﹎x }WOc)2PE8f‡2mÁp ð[\Iõž 7>H~#¢ÜFó ]M€®F¹jâ•ìp탼Þ¹%-²©,Á@Z¥ÁŸ› ©ôÔEºcʃ6/€6'/; ^xžäb& _ >ÜŒ ã%,7z‘E~\F:`3SÏW#_Æ(!K…ßáõíÝÝÍ<ÅMhÚ߳Ʌ¶£\ãßYE¾ƒrýãwßâéYÍS·�7Ñ/eèKzß;9º=ˆváé®ÈžÞ0U™¿ˆ¿Ë3½[‡wyôNÊA{ˆ½ã"=A:B×ú8NðòEšO÷Tj+vk±‡òÙÊð¢*Ïþ‡imwêÀâ`ϸÙÁ<y¹º« ý[©±y UEÅylcþc»Âβ¢â†!¶±é±a'dUÆÄöM®Ç‹˪<‡ÙÇ#Ï-¹¼1—äÚ†ç±ÐsÐÏAö^LvÚ%Ð9¯øAA ï)ZSÍê+F›¨ãýwH›Hôo†ïݲÇÂNèÛ oPà~1/˜}iña¶ î³qÙô sN5î²,*=è𯂧‹ Ï,ᦲ¢°ŸyºòyKÒhÎ6!K#¹.ï£ýáZ[¾oQ$ãgÁ©\ÈÏ ô}˹è¤så±£$©’$÷tˆ~÷H~¬êjŠÞHv—ñD'G•§« uÂ~ñœgNä­ ¾Mu.§:]áÌõ¼=‘LÔDÛQg%êÌb¹$sIÞ¢Ÿ–Ñ8t»t4ÁÇOè"øø;ÄÇŽ%ÄÇã×ÖáQ¢ÝŠ;râ|<˜œ£Y(7ÀËUs™9HöBúèxøøž^Z(|ÌߊP>]4Ö>€ºÜ¨“Þå™ÞíÁ»<z·ø[à=èÜ@ôôy™…h:é�/ßýÙEij²® ]ùÑy?rš¢³d?Òü0ú�y«<o¢/©-[7®ÎÞ0}nx0ˆºr€{.pÍAݹSÃnˆ]?8Ûó³oý-#îÛžúp!ôC/ôÄÞÐÕVWd Ì;y½Ð÷©æòåPÚËãÛ†ŠW’½eeZIŸ_Ï/c–wÁÃä7:CY"reT™=·üûÚdÏŽ¸8­$ôÓEèïð®¥ñÎ.`¹�ëŸg `‰*0Þ¥<ül|9Þÿ¸ŠÏÞ_GýöÞ0Åç«ó°çóN‘bèÎbí§]Ÿtø–Oiß•z6›ôéÙž¬.ϵè÷¼"ü1Hc…öZð³Åß׎Wjã~”[<y ­ØF‰ã=¶aÐmdês ó# óg`c\Ãò [‡>|ï> Ûi¥m4ôñ²EŽ“=Èõä5ÈwÐ…Y ñHñgXµ‹Æ=õ/äžÓ“<~Hö¢önäÇlj/‚=Ãöû‘k×Xô%ÐÀIr:èÙ-ç! '›°çâ! ¬9dïì^©ËðŶ2O0SŽËLÒQ4&­àôQepÿJ!.bL^$ž)Ax-ô„´K¾7þ™¤~MsûQF¸ ‹-¡þücïDŒöh;ÃMàƒÓºM,‡øŠd´~AôóFÁÖüþ"W¥½ŠÖ•Æwð~Ê•°æ{>;tf:|=¿úWÒ¹ìÃW ¿ ì/–˾£õ€kµÅàõ.·ÃÙõ—¹_ëK]\B×VÓ}MÕ‘ÊÉ5ØâUã4¯Hú’Ñ÷O!œÂbµ°ëêƒC·\÷8ªÇ³§)ô_ndôœs"ÈœyEè7OŒ=C´yÚà¼9ÂÃsSe e&lé¦[Iæ8#'ouu\ÈàølÝ ôÒ§ˆO£ïÒ†»*¥‡ŒW@¦®eB¦¦ós*ÎâëåØo ±Ÿ¶'ì•c»û‹Pg¹ûëÑ]c/Ùàÿ] Û¿ô íx0:o»FºÍCð‹IWQVäÉbt^ô]+ÚXþxŒÆtØ]VÞü¸'ƒÍí9¿ùöMŽØ{ï‚Y€û•dO8ŸA,£¾Ø™¾¹ªvwø‹–KÅy¬¢ðq²«¤=Eçʘ° 7ÖNö`x‡˜ãXÏ»c"/˜[®‰Ùy{Ë«lÁÆM.a+X¢áòõAÝvŠü šÆýÉš·2Ûbr˜]¢z.6±Ü‹ä+@W€7rWÙüÁ N£ã‹¬g¹ÝW.í‚[`Ü"hzvÁ6a„ÞNØÆ…]@4 emvhD¶ÙÓ]‹�ÆâÝëƒáƲ"òitѨ¨z<~mµÇÁ*ˆŸH…{@ûüÛ‚!ŒUŒÑeSÐ[“° ¦ £ñä`L:‰ö4GSÌ}ÍØžqŸ//6ò¥¢ÊØ_øœÉ[ÏÕÚ$ɧð.«‹dTx9[D2 öÂ"²»aOž¿ñ+ŠÉϰ=O0#»,‹è憮Fá—é>­ONåÈ]²¥V1mÌ(7Ï}ŠöO{œ¢M¢=ëãíAQ\ìF¿tS{.öEž‡l?©­ƒm€ö¼ämMŒ4ò3ô›š¿–ûNc\žòoc‘ü'} ßÓN6*`„Šoß}ÙÈwÔ6Îw•ðEšà‹49Ž…²ª9ßumßU€ï*0®Ç«u5ïÊïwI»¸Ÿ|’ÁäÀÎ7òÝ�ùñ±'8߈í!¾C¿ }Ü t3õ!ðZ7^ YGí;âE_½Ow>m0ößû´ÇÅ{‡»—qž„o ¼À¾ì‡ õÂð¢w\>7³Ujÿå´"úŲ!wêRxåÂYèE¾à\r;µ[µSÙ%ð1ÍÿÒšgžØ Ñú§§¸‘ßóÁíT’º åíÿ``ö%ú€ Ó»Jíý!n›¼€¼C?½ß�Ü€ÿûbnýo^²×Ã7ç…ž/ò¬+ýÖ¾þ$ü²ü"O¸‘ÂjäKr¿Œ¾{T«¯‡ÿÏcÂøÏ£Üîój1èÂeUV¯ÄØóßA.¾ˆv»‹¾›÷Ð…v¤eó´n=M£yâÁ»K�§V‹‘ÏøPcÙJò!sçý%Ð ¢ýG¨ýö¢oúº¹<™X<HòÄr¦Ë ¸eRž8!O&H†ì"›Ùã”2—Ûû›\d[mÌ‚<©­®*®„ŸqdÃí°æ’¼ÝX鈑>ëªå¼`á¾eVuÑnàBc /òÄï-†¿•‡‡æèöoï÷‚fº×Äu‹ðÃxß+ýð*!s3¼Ϊ2·¼/æsÂY«ƒäsÞ?©ð>è xcø÷rÞ'}DzIê$Øàýÿ¨"]:Nvø Uü<fô¼¤ãwxÙuSw#ï;Ò†?59Ô{žy<oCŽÀŸÅ–»¢*ËÛq n߸èŠ:Ɇçúaô¯ð+OMöê}Tzâà Œ"Ôí–þì^/xóÃQ¤»Q†ÞåÉzËä»x—Gï*µÉewÔb‘ïˆ1„6. ï¿’|'ºR›i.ŒêEŠ@Ÿ½»+Á?LòOfÕʰW矢 C|%ާEžw0þзÑLeüeÆÇßB¼wÓ{>þ`/ <÷­©/hÿì,Ð1Új‡AøœˆJÿ*rÒꪣs §àH»£R;Oó¨Þ)²‘ï½bŽìü³Âǃ?ÍØÊܲ7€û’ßKþu4Fýà¡8êœÚ z¹‰6°9¹ü'[)ü0t@Óê ÙKû{ ÓàãOèzõ-KôÕÔ¼gàõ½+¾Ž“eØ×ߟ7í3O9O.30bžÜÚ>}žÜº"1ïl]Aòºx…69 ¾èÏ^qþ„6Ùý͇fu8þÜ}ÒQ.¶‹Fçk4G;‘ „n8˜Íýb¢³˜ër2akzÚŸ‚ɾB~-½Ë\ǸMKfLmØÀ8¡ycî×b¼C·òy®ëWïE¸™ÙÏiø<Ù>YÆÓiÿ—ÒMmû¨eÂ6Ê Nþ <Ÿ¡ŒîgÕBo¹¡³è]žéݼ#›¥(r¤ þxÖ²(ùNšÍÊÛå8 [¦´ª†n£ùÚzÞæ#Ô&ÇÞ öõ˜ÞfÛæñíhó/ §ÇäxfB§”ögŽ¢ž\Ô—hwf’vs›0ãx¢Ý™¼Ý¼þŒ— í&{â`f/ow¦IÌlˆ·»B™—"½z0sÝÆ+l“¢ÈÉå|œècˆûB‡î¥±_ö@¥ö§¥b}àò˜+ÿãU‰õËÇõ³,´/›¯Aü{³¹/A{ 2Å< áÓ= û/¬šûƒñyø+Yμ0ÆñZŸËþ ­ÛnÞ:¡ê´ÒþŒj³_AçºHDZáWX«¸焎kòWN¾Ûnª*òdé~ê(»_è¸&¡ãPÿÇ#öÛaKÂfv® 5%Ú[ÅýÝf®e\Ç‘ÍF:`9ó„Ò½Å|n¥‘ìf ½PLkT=Ë]!ùòÎrÉX²ChþQ—|,;ùúÅÑ'£ùrý"Àõ(hDõèðI¯R\¶ËºK¿¥=Eö¤®‹Æ·)~Œ7áÇDØ,tª%oFÊyÕqFô…ãtjªåéókä ¢=ëhq¿u;ù­| nÍès�D/>—ÃýVGŸðÁ›õñF¾ÉõD¾C0ÒÓ#]À ¹¨P:`|‡—õJKtYEßž&þ$yWrz«rïsÝ-öœÇÚiŠ|ò‹§l½üÎTÖ˨΄³'ø1}xºNˆ-I¥¿ôq ¼Ïáw-­Ãy^¦ovU‹³á—iÝÄKç³0^ʼÙ0dUNÉ!ÛPd`pÖ~9ùà“µÈÉÏ¿F÷z ß’¹ÆCràa¹Þˆ‡î*¢ù'z9I}—¤24önâsl“Ù‘Ã4‡•¶Y™³ŠÒü¨y8}~ï“÷VxÄþ]V8|…Ö&çó¹¨Þøœ ÒB'óªhþ®RûÝ÷Î÷ØFÄÜ}dyÌ6´¼WܹùÝ߂˃ÎQ@Few;ê‹=Úñpè—®ðM®È eä7ÓyØªÈ Z#°ŽŒ¯€­¶2OÄOÓù€«uý‘æ‰Ï·‘ò9”d¾•ïÚ6ÁËñ~LcàKwä_Ïs¡OMó­ö1ðwê2Í·Ú‡ãó­!ó|«ýE}¾•û³j‰V=O½äŠä‚Ïðûâç€/Ÿ«´‹<øÛ`´z4:-íãB_>¾ÏpøhN)ÒËFºûµ‘/þÅu~s~Îí8Yú­Ø0ÍŠóÿÜÞgBZÞÕKÉÛ Í¡Ð<‹>ÇY¼ùvšg1ϱИîñBÂÙ"ùWVüá –‡‡Ÿ»…|©àòe'ä+« †×ßï껚-#/>ޱ‚þ»Ø¿¹ŸÆ‹æœÙüð,×çÌ1�Èæ=49ŸÞS>ôÍ·< èTò¿Èå¦WŸ‡Ð6/RæznO=×qÏf®‡ æz~˜l®ÇzTð€uíÍåíÞù^ÿ‚N‘±œÉ«1v®&ÿðòø:¯8¤ú¾–3Wð}ª¾¯.§HF ¿<VÏýùÈ6´º…¯{é].»nÙÀnÀIéøáÛK¦¾®Çqi/†Ê.Y .Yq\zñÞMï —Ý=ª^-mô÷ëi­É3n€k'c¸+ÈŸ0À…ýW…ûûÏ‘½dñL:¹O_/ìTKžð9Þ̧۬qZp<ȶBÚ¨˜x/¤¤ Êù‚ZÈø'`·!|¯J„ï{IŽE޽ã"™è=5¹Lîí¬…_–7­ÿ劳Ï^B»‹CºO[˜›iÍ 4q‹5¸Ø¥JíÂQ’EÅ™ñ´>ÔùÔîÌÓ‹1ÔS^VbeAO(?¸{»˜WàþÍ+ÔêþÜ{ïînT|½,õÝ…Q¼ý“•±ßˆûÈi½wò h=Á}§ƒ“t¯ºØß0Hkȓʹæß4WžÉ%v"Ëû=t'­î÷¤òyäYé÷\¥ŸË¡{€{na/KÃo~ûU½è;ÞÊØ®¥ ö,e#<ý/½ÁŒ;ß ×oríþ c7äk—ºò¯©<^áêYñeWäkoѾ}gÏ탙…oQ_ îŠ~œ9Kúù^ K(«¼€ÖHΜå ^ûùrø[Ó™TönÉbM£{e莙’zæì.^°îw¤»PW>ªÅÂË'ø9Á Yö•‹gs¡O_,|ª7ÈþaòòH¸V½C‰õlËðlÀ3Œø „ÐF~ž'ä{<,*Ÿ*ŠÓÚÄî&²‘µK‹{™Å{ÐZD÷C„L,ò¤±y´w¾gèh_W‹½ÆïÚõ s÷^í¥uvz íwE›O2 ÝYò&dæ©as+`ÿó»¶él&·ÿÿ_œÏ¤~è�7ÂNáûP-‘}´†?@>¨¥>ì™XÄ÷w^Ëqrê¸�7+ᾤ½á„?å¥6ìÎGŸœÔVì_Áïn¢óê7ââ•ÒSÚ:&òð!½”úí¹–î2G›Ñ~ÈøfÊsðhþ²ò,ÅŸÉçó#ô{_>räûz­G›êt©uR›À7ë&¯,M´ky'³ôtR».Ó¼ÈS݇ù~œx_ =qšÌçí£ý=ï-#ûxÿåN+ƒ¶èô£ï"ŒÊ:é»jªüœÆí¡+õÑñìaC=G’×#ñsÒþ%Úç ´Í*ÚvéÝãé¹cê:Ç1ÒG¨›ÖØþ[ÅÝÁ»ëþ¸ þð†ƒ‹ˆ·É?Œô–‰3Vw8Žíïu‚Öö¾k}ðÛÙG{ŽÄïlüÎ&\ЧeDó8ŽW1'hAöÅfØ( 8ó8ó8ó§Ÿ·ÙNmžçârnž”srþfùˬ]ß“Ëe.çRÉ89¯£ß)Ð›Åæ?zžå3 ßê9÷#×òh%?ÐÆz¿ä²ÉƜşÑ'¯Ñ¨ÌîJȤ:؈_†ŒzœÎ–³‰’ È¥MK›˜eùš§{“Kt¿;ÝëMð³ét®mñ—¹\:Z\}k.—K™KÞû]áò¸\zŠ1ëByÐQ<Uò. ’EyxÀ‘–‘‡“Ï £ÐQ‘ï•y—û™cWgô%âƒ]Èç}ßrqßâä�Ùð“‹Ÿc~¾iî4ø*VW蛋*c¯Š;!N°žWþö2;{ö};(xkDÜÑ«ÏM€Î\6ý2)Ü ¾€ ;‘ïA‰œ,ãçUôz¨nȜӴG’òör{I;C¿Åïaú= ~Ÿ ß{iÎ9´‡KÓ:røÁ‰E´~Ÿ‹ñ{#2¸ \Wøu–¾{™¿_,p Û–l3´û4Þg}&!ó– ËŽÝc¥{Í…Í»¬-(àÊCgQ¹-/¿Kß¡òC(?b,‚—?©¦r£×ˆ»Ô Ú4$ë´ÒY;‚¯ãOm¡<z;H—-QfYϾhõ1õ{Ï—¾ ½ô+~'rxÅÄ"ÒSôŽpãzlÏ·\¡cyUÈÃÏè=Иë!œùYÍeeUd·ÿ%ôm‘ž_üØ1YO¶ ×ÄÇëykf{cÿîÕòðM|l¥ñóþÜAÜV _ tÑï€ؽ±Oä,/~‚åv=ÁÜ‹›ãýºµˆÎÈðý߇¬ïî®cöqŒßB¢Å×í T6úRï�ÿÒ{ 0“¥}ñyèñ_iZð ʾ¸ cö!írÉA1ž#?t£»‹´ÉÐð­.ÚËHó>w¾¤ÅJ2µKã•,c ãÚi_™·—Í)ýk/¾þÓ|\;1®Ù*­—Êq½A<–=â~/¾$°!ùCÛ>ùÖOoÞØ_²¼8Þõ8Ù<¼¼=4°ˆï)£3Ô'¡Š˜÷µ@á-h÷7Xvý+ îaî%û˜Ýû%ŠÛEü9¼ÿGŠ[xã žÓsKïc(›C{eÃoqMå»Ò»¤"ûøD­ioÌÏYÞ'òß­z=|ï%\Ð*Ú»îïÐ?óú¾Èæo|x¿¸ÈÕ÷m~Œß?YÚ7Æqîrs=˜­·¡•Bðš½ôGÚYÏÛÐszCocl>›,ó–>‹òrߊѷuΔþ(6Âó½Måb§ÅÿÏ Ù5õæ»Ü@ÿv߃mƲÀó"Å“=xwZ>£¥ß°œKÔ¡5S€ D‹Â}Ð'cÅùÚ$§I>è´/A§¿—FœÏWë|nÿ§i]ÎeÅÛµÉpæ*ñ,í¡Å±z°j†‘Å^ês¯ÎÇ…ŒÙ_‡|vïµ3…4¦¾¡¯@ß#¯'“9hM"œOëõ ó7´a÷§.ï¥6\j·ì—g‘f¥~RÒúäø=@sûË¡OWs;ÄMçźê%ºGžÑØ.îÚ=ÌN¼*ß-¡tºuGeÚ|O¦v™Ú¶¢×ÉßÊωüðÝ)˜‹:S~‘÷â»(oQóy¿qq”Öìg1gé/žFhߨ”ëñ~mpÃÚ°Ç[€¼¾_ìrƒ/{G¼¥{ØÂHþ¯‚h£]´ïRP—…7sÜzCdï?«gÏ-á•*ð6KYÉôï-¹YžÍ{즠<OÉϧÓý¶“b½k&‚òŒ þ»»÷‹,·÷Ûl>þ‰C{ØHs¾BôŽŸïVÎW’œ•wýÍáß|€¬¥3Àâ®ÛÄÙ_u. ó¢ÚSq™n®tæ³1~?»•ð óÙò T±Žß™¸JÓŠ·GFzñì}aC9!o²ûÅÙ²ý°Á Î>Ø~“ãhï>–;y˜¹'_áç Çé#Ï‚†š”{¬§ëßùŸ±\~6ùw¯dvškB]ß•?v®+ÿ“ÿw8Ÿ„,åÛiO.üéÏ3¬øÿ‹Gø(º?è8ݱ"3øìöñCúº;M¼Kt¤½tæ×{ÐÞήZ4àº`ñ^nãõÏ/¹”BÄCwEZ.Hßó)ù¼,Ÿ%ò©»DñÝד~géIv¬p/ùÀ÷Ç,Ý{ÐöDp샷ÈÇÆØs†þúÙEÀ‘~»‰nÅ̹Œiáî}Tã{ÔÍßF‰þŠå%¹kŒîôÍ¡ó\ðË2¡[A–ÙCF`œZ/„or›Í=…ßføžü¾=:?E6O(í³‹7Â1_Œ¾çV+ó—ñûÖ‘?|3òæ‹vˆ2<ï²q7ykƒµBWÄዲVY6ÇT–¾Á6)ëÙàÉ`6‡ÊršÑÙ%Ýb´zXÇ ù­3ç¿EþDCé£nF_9¼-E†¾"¾Fá· ¿ËtKu¢|Œöö£n¶l8û›¼ÞÐÏ Á$¹zÝ#éô-bÈÊG/׆K©ÃD¿ËkÈ–‹:øZVŠþ¸Ì¿gÆcÄ™ïKôM·CÒSæ7ÒæÒiúNòXgÈCwËTS:o+ÚIm2ÕEû‹–IÑÁ!º—þ€ö‚_!ç.¯[|¸‡Ù¼PãXéÁËëè(¿OéÔeº?) e(Ýo2ïŠ$L¼¿x‰ÞCVÒüc!øy²Jt9›æ:�ߊxüîIÔ7)ñµJ|M´¹xyö€‡ìèÇ…Ä—à{1Ÿß¸TÍq¬¥ù•‹{i^…ÓWŽc¼÷ðsŽüüüÅ'â29M‘s%ϳ.Éörñ ³•‹4Ö‹K!3ôûÔ¬›IÒYv)Oð{8€Ã\þY˶2!ÿqù·Çó®WÈ?‡õHhl¹+4— â±óWÊ89×F¾-=ì]ùœ–ù¸§£N¾Gco€À«ð]3ËQýãNn#ÃFq, =guéãQ‘y›u™¾kýåÑa ‘eÎt×§ù‘òEžç$OæÇåÊ?ã—Æì„½ŸŽ÷qÛ‘'}†<4÷¶y3ä!ùÇ×^Å:&Ûú%Ï›C¼—$ÿ’y‘“E”wÌë¤q•$/­Ÿ–yÝ$s(/É’/¼ì/¸_:¬Ó|r™?gœò¤Û‚SÄŸâ=͵ÓoZ‹Ž~IÀã²"Î×—ÅùÚ–ØË£ëx]÷ÓÝû¡nØúÉâ`èÍÛ‚¡Ë%â é~™ÿø¿¢×ÁÏ{ˆ9é^ñÈ9 ÿY4¤ DûµÁè íhô¬vþ‘+zIŸ¯ ¯ÐFÆ7k£Þdžášgx³æieÃýZÕð ­zø¬V;|Ikxe¾ÖþÊ -øÊf­÷•¶÷•~­ï•ÚøcWÑ}<äÿÓ½,²û-ZëMyü¦ÜQºÏjì·÷›Ú™Ñ‡9 O¬äs/sJd‹û&}¬hr7óL>ü“?e\/Nþ™UMYXõÔ ¬vªTŒ{šÃ½õŽ–²½£>Ö7º›}† Œþ” Žþ™}ÃÂŽ¿qz£” ¿ác#oìf£o<ÃÆèÛ žÝð=ž±¸=?¥û'/þÖçƒuŸ<çÙÃæG-ª»Ñ’óèÔÏ l‚ÚÁýjÛ©¦ ÝÀÚÕøpŠøp) ÂGY²×ÇQßóx#@ixŸ=`‚?hŠ÷úX@›ñA¼}¦x’öÞÇû‚‰»Hˆþç_êÍÕeíùX/ãß¡¹S›,<ÌÏs•hڌ߱FÞ)ÊKûPOêû¿,ËÂè‹UO³´pƒõyá½¶çÃnr7×ut^ð›Yõ á¯?bÿ¡ö21Ÿ­!´ÑœB;÷Ù`ÏÑQÓÅeæ 9l„Ä“´o9aa&ÝéEçNŽ1t¿¬VpÍó"œ‹pÂy\~1>î× ¤ó+~Œl6„ÿ@ßöEèB¸áÇÎGx5}Ûá54¯„|9;ÂÿFßöEø šCx-ÍÍ#œOßöEx­! ¼ž|~¹“öÇÐü7µÿZC@x#Í[!¼ !µ!ÉGx3}Ûá'i®á-©ý$©ýùt&!É/jÿbú¶/Bš¤ö !µ¿€öm#,$9‡Ö™©ýKéÛ¾o¥ûûÞFûT.£ûø~!µÿv²Ù~†îïCHߦöC>Rû—“͆°!µÝ߇ðú¶/•©ýw"¤ö{éÛ¾Ké~ „«HÎ"\MßöEH6 µÿ.ºËáZú¶/»Rûa'_¦ö—s›± „ÔþJ„Ôþuü>ÆîAHí¿!µÿ>²±Ò7‡×py%äÓ~âËÊØ¾Àùr€ø^¹ÿg-šG\$ŸUxVãYgžr™^AC£âÞÒåîÕ¾Ö[n_ à«kp·ú;ýî@C‡ßWï´¹¸Ç½µ­Ã]×ÜÖ¹£ÃïnðµÖ7û;2…¢áåïÛp·mu·ø[Ú:ºâàš›Ûê|¿›‚f÷Ö_‹_æG¼y‹¯®Éݶe»¿.�î-~«{›¯c‹o›ß]×ÖÜŒþúL݇ÝîÛé+löµn+De÷m½‡WuWGG[‡xoªsµ»3ÐÑØºM)'2—·îô57ÖK\ݾº:gçôò4Ö+@ð·±µÃ_×¶­µq—¿Þ}où}î-;¶nõw¸]í~zsçr·›š‡bhEëNk ±­UÅ¿MÛæk.íØ¶£oï ÖùÛE3Ô¾£.@4¯÷omlm¤\î<½P1’Ý~jV>ʹ·ú›\ÞÍõùÓÚÓÙî«ó‹n$¤:: ú¶h¹×Ç[)ûGBÂû:0×1‰÷ÇGཱིåÞ´o—Ú£ÜÐÖænñµ¢G$]:Ýy-¾ ûæfjJ¿­;ÚÛÛ:À÷µû;8”×ÇiFBe[ÛÜ­mD­h—/¯Å}s½™¿dÿ¬ á ¸«Dw'°kÙÑ�Óºë;ˆ…ÑКïs·w4¶4rRP«‰|¾.ÙÎÕm;šë9Fm[¾ÆV‰RJI^å¿WP•ˆèîôvè—Vøq:Ã-×V×ÖRع£µp{«¯p¯3ÀGÂs¼w¨À:PKG+'ßw'’�*“Ý/R;¯L^ÍtE¿¡-[ýõë[›äØó:ŠB„p¨„ºŠwU[ck@Còµ¤]]³¯³­n)@Öd-ˆg]AÁÌ«È`z¹¶VÑ]ˆ¶”pÚý¢â ½ÿT8ôÒ¡GË×\ R’òÛšÛ¶@4vøÑÙþV}€N!x@”ßÑ^¾Œ÷$Ë+OÒ>S&ðX ¡­~ÐÛÚÑÖ¢¦ä­Kôàj*[¢$ÜÇÅwIþ:µ·Dé{|íO%F¼:1¦ëâèÄ«â?gFMH— RÚ&Ã+ZÊ4ºëO€œEý6#ò mÞ ß®æÂÝ”JÓ’fÆO¯rØ™ñY'íLÍHŠÓäH\Æ^y<ªYùß6€(^Á!ð62-&¾Q ÍÌˉêZý”cû0ªRòKÒ6•$§²_˜³Eƒíèô [%i?éÍcxú8Ö Ïº:æÛh»j6Χ´.×óÉÿîç:þdx>B/>ÀI”ä½o‚¯ëÈ™%­¡Š€ÊdfxúË�1)¯,\Õõ€¯yÇG‰"ÓÆÕji)MÃw:¼xÖdåï×õÉÂRnšÝÇu}çt:¤†Q`1c}|E.¨ÍO¦w’qÊ&ã(ÖĄw”ªR‰Öéh¢ÐÚ­uDƒFÏK^ÿ¦bí x™ª˜=z„ŸøÒ˜Ï›.ÊSât=iýÂ_Â0ÜÀÍÿäú3N·¤©)øÙ ®ô»;Úv´_Iï›QúH Rñûœâ8]ïãYSYJIåT*¯ÉK¡³òN·$faJØ*žåà“mþn|]?5ëôöì$Éw%¡k‘„cgƒ‡š•ÿµËA«Ž Ñ'Bù~Dÿ`†þ\¸ukcîôή?Eä¸ÙÙÖä'ÿ¥‚{08â¥jGÏtr=¶vm9‘J!ç§½s·4vvƧ %5Z#½æÆ-Û} :¥Æ­(ý܆š»ý<.‘æ¶6îïhwS½4éq³ªŸšÛ0,)‡ŒC¹ë;|PÙÎ[}uF;$É{švjšé}#·ï×à½oK³ß ·zéRw{³/�Fh‘…ðwo[매ë/§;¹'Ÿœpn+XVP„pYÝíÅ[ê¶.+Úr[Ñg<Ÿ¾u)þ[ºåÓ¾O×ûêñ+®‰; ˆ; Ä`žž¾¾à…{›·>n8&Þ¯k£NJĹödkÚv m;uÛãNù4°ÊG3sr°tæå›áÁ;¥¹¸QBÇg-º(�§â§Ì&Á¾6J6Ʊ¡jW·µ´·µú[¹e¯×*á´6¶ª2) â õp ”ü]ÿªø´ [Ý€fÕâ%V’l®2ÒÊS¥<§²¹}”¿Hd¤jÕö)t×1•®æuÆá aJulU[[³ß×jl'å#*E?Š„>±tßÖ­þ8ŸˆvÚ¨úRzŸ—oÀŸ“-p:<]Ô z¿'©ÞÔ/²Ík“ðçmõIàLË'*Vù(Y«e{ÁPÔy"ž¯À‘”MG‡ïQ8€/›•`Óûip˜Â.Èf¼ó§Á‹óO‚“ ž`[#âf:JÌ×蘛äÉ48ÓÛG”“P*íO ;cýBzEC“´OŽìp¦×/óKä´—œ—-hoëäÓãÓû]æß$ó'䞘ÑÔ¥Ïìæ³ÛZ¹jÊ4¬GL“ӊעĥ-4ÊQ³5bHÇø+”#/7ʹxºI~ÅÓMò*žn’?ñt“¼‰§›äF<Ý$íã|Ãç«îâã.Ùäœ!¿qœ*é|\)Å ãB¡çceßÀ_‰ô„~ñ„q£ÜÑU¬ßLû:Cé"NYf͇“|z’é$i>i J¸\P$ò=¸Š‡IìYz£(�=ÿjJÝ7Mñ'à&EÔ0`ÙÓrÀÅË•óP¼fÕ¸ëeX.à ®•!?û°IÂ[%Ãõ2\-Ãr®•¡~`B¡BWgÀߢãEíìhk÷w$’Ð+ÅÉ‚†Ï×V¯¯ÿmXK[„œ�D°HK'bá¥Ã†›±bE+E>}Þ!žwÖrgG«”<âÏèеÀiˆÓ+FÆôæÆÖ&Zg }ôžìùö{M]ãVNï¸ýË—¸GÖ»“jä磆±z9Zš1ªê•‘³î¬S†_z»ˆSHñÛnq )~û2§0Ù<À­u¾Ûk¦eb-š%óÃ:Å“áI'RVTÂv˜ßèüŸb>Ã0CÑÆ îpò~çk·bAÝÀ~ørE­ Î6…¯>Òºër¾~§¬š&êQ–útz?|Õ䙦ñ…²JÜšX«Œ;²I믾úhå°ÞŒ/Ãò«I + áÓ—tãÙå0ÆÝy·Æ=þ—ƒv6ýω¶$évü³¦È?7EzvŠô¬ð3S¤g$I·âŸ3Eº#IºÿÒS¤§¥H7üB`Tó?ï~k›?è¯ÃûN?xzG0ž¿½£­®°¥mGk€«ùzk§[¼ßpOÕšòû‘/ÐÂVáN_‡þ›Çëý; ;âr½ì¾{¸]Qè�lÀ·EGôZ[ p=ŸWkÚWæ3‹¥HÓþÙ‹°VÓú†дýN„CƒÇ4íÉ2f±æhÚW‘ßêÑ´§Ž"l×´ÉFˆü_ó Ö´¯#¿-WÓúsVkÚ¿®m@Ó†–!ŒjÚÏQÞŽú†O# jÚ«“jÚ¯ŽjÚoPÎ>©ig?-OÓ~øi¨géidrx;vÝÏìÁR˵Ù=-W»üYu)ù–h—ÿ)±t¯L£w^-öBë+Í(Í^“^ª¼3Ã¥?º¶j¾#Ø–…lõ!kF)»knŸå íªÒž§-¥s¬ÊDîµ:ŸÒ¹þuZìŸFùd‘²Þ=w�eúíßLK*ËÚ!”¡s·{´Ø?çލ¡×ò´Åuu¶Ê]óñ¼ˆ|Ô¦v¶f®÷i˪9 ªâyßÓx߇p„­=d=h“ëøÞ'ð­E¨ÞKZ¬¦aΰ»(?2®B~ž¹NL{¨"ßY~žD´k5å<h[3wÈ×oçx´Šö<@™ÎoÖbO’šÛ |íº‹ò®É*yéL Úÿd3?Ÿ²šc½ê•H·ÈüوƴÏ[‹}µ1›—‹UßH¿ûµßŽrk³Ëæµ\µ6òÍ´¯;ôvWPÙ{²¬›(¼/ësÜ/+Àv-ö5ŒXë€Òo«úíkž¶ðFZ¿™öô¯;�ê*[™U)^¦–u;pœ±kòØ ¶nî¡rH?àüß2¾–ù/Y:ZVN‡Ïꈜ'´Øÿ ~ϾÀVs8÷ͳPÇ<•ýÕ9O΃9h�ÃáœÑb'Ú£lÍ¿d=9—Cšàˆ°ÒþyúÐú0Aº?+ÇþÿÛŽ²5T1àÜÃ1R¡ =œBUŠðÿØêµØËè·´%¬|n» Qó.*U‘Î?¶~-ö ñû [‹Q¤óÏ}‚ãÂö2ò áwù¼ ~Y•%øËvN‹ý2õÏGýq:pxTûCqæùí.-öè=ë([Ãù‘ñ5žÿuàÇå‡òÃeàÈú‹ýwyÖ ï¬óçÌçÔHÈ|kD¾×¤^°ô2ët22ãŸ&ÿRÅ Ö?ô pÿÞnJ¯•éÁé½)ÒûRÀ0¥·ËôAÑèiéUÆô_WÉ_2mK›aÞ'd\Ø Ö×je|ŽŒs¸mZì= F?ã'uö5ú·|¥nOoqiKÌÓõ§ÎS¹26Žõ¬WÇT]'é:G·%®Á=côÿ8Ñ-ÏDùòtü¥mrs¯Œë6L®h“ùÝ2®÷³EÆ'Dô“ß—ñ)ùú ìÅŸåg¹2tË0O†E2ôʰL†U2¬–a­ d(ùág‚-ßù_‚wª?ë¸à3ë;2þŽèëÛ}ò½W„çrdX$Ã2ÖÊ0(CYîÜ „3*Ã1Fe8!CY¿dkTÖ•õEseè–až %Q %žQ‰W´J†Õ2”x¾}\Æuüdma»L–q‰g´W†{eاÈ'ÓùØú–äGù§kë[²¯¡¤Ó«Æü¯ øl£xo{Pä·<kŒ³Í9¦ø9cü¡5ÆøÃc|›×ßnŽ÷ãMƒÆxKƒ1ÞvÌÿ€1XgŒï|Êd¯1¾ë¨1zÙ›à…‡ñž€)>dŒï1Á{l‰)n¢÷c—Œñ½¦öï=mŒï3Ñߘ1þe~OLãÿ\oŒ<kŒÚlŠ›Ê?mêï§÷ãÏšúóy>ƒ¦ò/˜à¿8dŒŸ0µÿ'µ¦¸‰?‡Lð‡ž0Æj7Å«Mññ—˜)¾ÁÖ7µçg&|^6ñÃË'ŒñŸ›ø÷ßç›âUÆøð¨1þK}~5Æ_m7ÆmÂwÄÔŸ¯e›â^cü·¦ññº ¿ß™ð5á÷† ¿?–ãgMüô¦I>½ijÏŸLðþl꯿˜Æÿ¹\cü¯&~ýëqcüm—15§q“¼:o¢ß»¦üÿaÊÿ?Lü9aÿeâÏ¿™äÉßFŒñ &z]0Ê'Kº‘-™Fùcɲ›âËLñ€)~Ô3ƳsLqSýÙïãsLõ-1ö¯¥À8þ-KMø{Ö˜â&ýWlÊ¿Üȯ–’jSÜTÿJSûï6ÊsK¥Û_g‚·!hŒoÜlŠŸ1Æ0ŽWKu¯)>dŒ¾È—«‰¸ þf=6›øå¡ cÜg‚_gä?Ë.¾™ú÷ Sý}Fùcù?}ÍD¿¯™êû¦ þ!“>––§G’§?³9iú´øýRnÜ_Íþòw÷êÕËÝywß»1ß½¬àö‚¥î[‹–.-*.ò¸óî÷×»Ë|‘þ©âüäy—Ýfλ´(Ÿ±‚Î.>ßYÐèaƒþ«µ-à/ØÖº£`ËŽÆæúO5Ö3kðu6°‚ú®V”a C¼‘'Á ‘¼ëð7û(£üÕÞ`´ŽÎ þ þO§Ñð®­Þð±C ?ÝWÓPß‘ˆ±Ú?׉ E°½/©�ÁDYމ¯¥±N¤t[ÝRVÐÖNX·¡’-(U×ÖB“úÿ_tɵ,á¿JÿÖÒg ù…ú§® Àï|ßTþ¸1´“>P× Ô9y²=t¿[–×ýs=Ì“uê0ôòÄß^%]–×ý}=Œû÷úŸ:¯Z¦øÕ²¼ð§¡¥Á„¿U 7&üt=]øë‰ãhWêUÛ_ÏôÔË×C>?Œ~ÔþÖéåÅüC"äó6–˜×P˼äüÈÆ 1dfù¡âß“¤üˆ!´Ü;d*Ÿ£üÞ3­¼îïé!1Cý_VÊ[e¹ã†Ýd*n(ÿäôòÿ~Ü2vÂT^åÿ¥¼œ/t³È¥™×ÐÔúL/ÿj¯(/C#½Ìå¿›¤¼¤ß«:ýf*<IùY~ I~süd’òƒ²ü L?:Cù_²8ßèå-øÍ"Cf!<Têå©]¿^ÿo„>·ÈYÝ3ÔÿÆôò¯‰qbyM/eÌø§–ÿS’òµ²¼Y†g†ò禗ÿ£´ þ¨ëóAfüs*a§7ÎWZôyKG*úSûƧ——ó8ñò™Æy_#?ý§‚‘!W¼<û¼©¼E ¯b,éZ2•ÿºüõAŠòúïdkÎxóW‰ç?\ÇzLmFêú§d¾«î˜¡þ”¶x>KŸ)ݤ'ãéøü´ž.yÿš2SºœßÖõT<]ާ¸þÑÓÍóÏzºl§®/âér>:®ôt9o®ËùxºÐã ¹­§gËts{Åü}BÎêébÞ_—£‰ôy"=.õt9_úª¹Þ«dú€)ýc2}Дþ"ý×Õ¦táOër%‘þq‘—zúÕ2½Ö”.Ö¬ñq­§‹ò‰u=ý¿‰tã¼*Ò?Á¦ÿYìLû¯ž"ýšé7¦HÿTŠôO§H÷¦H_›"½úcqzþaL$ÙÉ¿mº*ÎWÿ®Žç®%‚¿}‰ü¿`Jþ¯'àÿÑ-Ó‰ïþp†|d~ºsŒ½š"ÿŸùßðФtZKO7+e kN"ÿ¨Ÿö *pzeæ^rŽ•ü:Œjrjé¿Hàiy:‘þ[™Î×ÔH]âÉï>›¼]–ÿ¡ä“‰Ö9Éó[óºé‰ ³u­Ò/zÐ/Ö)à4§HïQè£'B^ZŸSÚ;$óB¾Z˜£ó›aÜY‡RÀ]¡§Þu˹˜<¿MéÇ7ôõMÐÙ¦ôãëz:è`ûœÒúxþ¶ÞðŸLäÿ½lõ»í{ u}ŒöÙ~¡ð¹LO£µó7¯ŠË·×ôvAߨ.$¯×ž‘"ýZ¥%>Ôöʼnz_SÖ¡íKüus3Ò×§€ß ÐG—óh‡ýŸ”zu}AõTò)ù©Ð¿V¦ƒÏí*?÷%ÒÓ>–Ÿ´… |½!}µÂ'’Î4îÒ•ü½2?Ò¿¡äSòÿDáI7 ´w”üµ‰üé9ÉåIz®Âÿ:ý)ÿ- Ýt~ÝÒoWúÅ-ÓQOúŽNøõé œ\În%½VIߟœžéÏ*ùË”üÇRäE¡Sè ö£n÷¢ó•véù—c…’> ÓÑŸŽÍ‰ô3:Ýz‘Þ‘Ç•þ’]±�üàøŠ‚gUOÇ1¾NO/ÒO)øëõþQòë] ~wüM©Wò›>µój…žº�=·(|X+’¯CúÝÉÛåTôï¨"Oœ(íêM´ËÙïNç¥]:¿AÞ;­ð•ì¯tÈ稒_¶×úÒÿª´K§µë=%}0‘ž‘–¼]פHÿŒB癹±F¡smÿŒ&%¿ÞtŒë E½®ÛÉþM%¿loü’ŒÃ ½2?ÂŒ_(òYßoúdœ½19þï+ü¬ËðIfnòöf.N‘~‡‚>ŽV ݧÐaL$;ÑîÌ/(õÊþ¥q”Ù§ð‰.Çž@ºÊÿz¥õHWÆõIâçÌ?¤Àó?•ze~K3\‘yÉóg¹üe¿Ð·² öÀö*ùoSôéH»&éw)pt~†–U“¢Þ6…z½/YO)t–ô±½Œô“J~·’ÿ-%Ý›HÏÎPà ÈJñ>ûj…žc>Ú‘}Cr}‘Ÿÿì»:÷ÊÄ5HWôÚëº|€¼ÊþçpžQàH¾">ÉþiŠüŠýü;}¼ÃîšcOn¿ÍÉQ䉎gé ’ÃŸ£Ø!£ú8E;æ<œœ>sêüešž³O¡³NÈ«9ŠòûZ%¿Ò¿Ô«‚Üšó‹„û¹¾o rlΔvéü>ŸMÑ®vé\Å.=£ÃFºb'¼> ÓÑ/sK>×épn£;9ü°bïÉö¢=súü^׃°÷æ¦ÀSу¿Õûòaîk }Áî{᪤pæ¥+ãWæÏ†ž—BþÏSü”ßze"åWäÀïõv¡ó6(éº|C»æíHÿËJ¿Ë´´%HWü²3z» æ *ü)ë%¾š÷ªÒ/n™ÿÒ>y½WæÇxœwIGC2?äFÎ J½z~è¯e\üVö— r/çÎäíÊùlŠô:‰'Íùç(úâu‰É«œþp¾«ðU­’®ø­¿“‰9Š¿3ªS´#çm…ºü®ÊVè Ûkœ«®Sòëz­–Õu:;¶n-¨cõí¾@]~êÇiðS?5†Ÿt§“ÞŠ«BéçÖ­ø??ëqû²ù¢`}â¼™¯c[gÍúϯ q¥?cÖÒâk÷zï^W¾juÍ­·%ÞÊSf‰kfMs[ë tÔ5t˜‹Åϵáu³¿U}M‡˜jêÚZj:w´ÖloõÕˆ3G5þm‡<¬[ßÜÖîoM™»Ó§8Så 4–níðûk–J|gƨ¦FÍW_SSQsÛšòr:ÞX³“…n©Ñ‰”²°¸9DYni¢“’í³¡!íg55þŽŽÖ¶~©hc›’Û[uþˆß7RC)T0Þyâ8 °ni ø[5†¾jñ· ñ³A�J«ÊW7øëš:w´p*$î5­ÙÚìÛÆjü|Uyk ƒŽìü³Ëï1ã´] ÚŠ»agSŽ÷.5ú tOÜ'6CÃįäÊüVNùÙ Òé7S™ÇOÐÇIƒ¿¹Ýß!†Ü½ï©Y[uMéýw×Ü×Ýåë7Üuÿzæo­Ýc蜭©F¤<ÛYÃwÌ€ž¼ŽŠn7°]Á²[ÕÑà—Ùè¶[g.äïôwìTnÊœ¹kä©ï™3ñqùH]ç,…ˆ¸ÓtܶÜ–*WÍRðÄ~ÓŒ8d¯wòåÃú rŽå,rEy”@“�Ö7 QÐê„ è÷ xÁ3{˦$n1â·¿ÅA‰#ÆÈ·/#ÞZ×’œåááN]¼|´1¿jz‰®òJMÓZI‡ïgbO~©Á¶øˆ¥Cõ¥õõtµoK[‡ó{Ëkîk]׿«ŸE#*ЈæYò¼¸ù .rtR¢[‚…VLP¼ý ÌH›†Ê×t²YK±~eõ•™›:«¦f[ Ä}gÀר©IÍë­õë»Z¶´5'Ô%ÿ×–ƒ\10$[GJE¿]·®iÙðkèl~]“úž”ø{A„°N”{ ¯ÔÜñtF¸2(e%0:¸œ¿{šœ¯©© úˆ<ü2¼Yê])(·¶cܶ& ÑÈô¶ÎJ_6v&dºY‰+Êf4å­­Þ¼o+xc¿«Q\ZF¼ÐNŠ™„ îlÞ„Ú-ËV¢¼AöMG¾ªÓ l7'µø~:*>;{Tò}Å B X~th{DÜVP4 q±fp‰\åÈÕØ “Bï4³FÜÝÏèZbrãˆ0‡P¾‚å,D•®ô`"ÖµwÍÒâB؈ý#Ñ{5—3[:;…˜ÑÙHǧ³qFÏ,[:Ý&©o¦-™3ª~½&A)Á»WÛ¤ ¡ßý—hÊ5q¹ÏotéìꄽUm ¤Zck“‘Ùã—ƒÏÜ]üt‚¸.‰@.ƒk*vÖÜ/õ ¿vÆßÉteƪ®{i7é m%¹PC?êýÍ-*Ââ¥~ä3 Êθlë Ži^¤>¤¹@Ýʇ9‹Fqß© ‘™¿Ä¨Á× ¦­=`®@÷d¡&ëêýí‰kÉgò&Šˆ[ùM2WèÁ™dm|p” ÔpñlBÎR¬ešæD~ëNS W…3z®Dv“ýE›æH¹Ê»çÊîV§Nñ+6•LôiwÙÎБÜÀû�PK ����v©Æ@���������������com/sun/jna/sunos-x86/PK ���,Zg? ØIÐW �� ™�'���com/sun/jna/sunos-x86/libjnidispatch.soì½m`SEÖ�|Ó†6` Á­Z]V«[]Ömµ« U 6-` Ð,H[‹èB‡K±xáz‰Ú*" (***ʧ‚ØB—‚²ZÝâV·êåÑŠ*_yÏ93÷æ&¹ àó>¿ÞMgÎ9gΜ™9sæû!G^ŽÉdJ0 ì_<ü§ú3à·´ÖÄý)‚Yè+üA装«ÿZo0ѯA& gßâèËÜAXÙCy8~ÝÀøÏÎ &r¸à›ââFøÖÈ¿¯æ?3ÿáç{ ‚q¹'¸iàbXG>ªÎHR¸¶· @òB3?ãïëT`Ú[5‚p:˜¶ö‚�i÷zp‘¿þð' \@.‚Ÿçjæ/¾R€´p$ö;p-ð»”ó}ðѽX& ‘߃O24Yü_ü3ý>þ–…@ùYû5È ü‹þ+màî×’'ôkƒ2w9¸àf~#ËÁ î^p} rw›"ƒÿ'¬õCžÁÍ;å�îï@ç<“î6pïw\O“à· ܵà*àwe/“Ð ®Õfì7Å ¯o¯ò²CI¨emánw•–QO]ÞlÜÅrë­ûŽe€ùÅ2º0LÉð»„ûÏÝ>ðûü.ãp ü®€_*ü H…«à×—‡]s–r¿~ýàwüÒù·¸‹mç¯ð»~7Á¯?ünßmðÈã ‚Ÿ#ý\ø æþ!ð ¿;9œ¿áð+àðøäþBîŽæîøÝ¿»9ü7]c¹;~ãáw‡‹¸;~Åð+…ß}ð+ƒßdøÝ¿rg*w§éèÎà®~³à7~ÿ€ß\øÍãaó¹û0üDøyàç…ßþ]ÒÑ{”»ë¾-†ßøASjußëtþ'à÷÷/ãîÓð{~ÏÂoüVÁïyö‚w ü^ÑÁØŽQÇ ~IŒÿY¹Û+J8þ»€»q÷bƒ8—êü—s÷aq®ƒÿÄÝ?s÷/ðKãþëcð3�~7s&ünåþÛá—¥‹wü²¹?' ­<î3Å]g ^ÂÿMàî½ð›Äý%Qâ>ÀÝ)ºoðû;ü¦sØÅÝ™ÜÍÝu8•Ü}~UQÒz„» ußdø-‚Ÿ~ñoÕQðñß“ð[ömy¼’»Ïqw5w_„ßKÜÿrŒ4PßξÀDõ²7ÔJ½M˜î˲;´/ê§m\öŒzc°ÛÿJèž;ÅÇv]ñÓu?§£>ç­ úŠ<ü-pÓtôÀ-�¡åá_Û û÷&›L¡ô :–ã_pÁ¦ pýÐS¿Ü•_±:Aü"<”éd„Cz¯³¾á­&fK$ÜShK¿ `û×Lcü6L”è£ñ ˜˜Mò · Ý ³›�Š-‰ós-ÀËuù�p3ðߢòð³¬\vcgù/Ö ü$†¿Îú<„®x(Hÿû¸P~`|P°òð Ï5ºôS®žÉÚ3†g¶+»0à´lÖ`øDŒßÝ$ärzÓ�^÷ºÀó{žPšþ2€+Ʋ~ÃW#=hÌŸsü7Ââo¸”î‰8þ€ @¡ÿÂñ�Û¥`~SA!·ÞÁôÂ7�\�Šþ0‡Gbø™^CxÀë ¾/äp…9h›õŽï-<°}6Ósþ À)3Y߇ðÄ÷Ó?„éA#Žãp�àd|×-4—�ÜñÓGÿN„k˜~FxJXü�×7Bœ~Àž æoÀ¯³¾á/®Ël„Û»íìÞ  :ÎÔñ× :²æGtõ;¶Ê`ý+Håg ÀiÐÙöæñçèÂQ·<p=tÒV¾mÿlÖ'‘ü�®ºËZÙ»�@'ÝÉÜõtPž§~üÏëÃy‰¡üœpÚ ¦«1<=,<ÃÁðXÇÃï Ÿ °:«1<½‡�.�E~ŒÇ_ÿ%€ËuòÛ pkéiŒ¿ñ¡¸€Óû àæì ~8ðº™]„ðÅ`�7[ƒú÷:€ ž ÊãH˜þq ¡ðK(ü7€+šÀ†êÆðç#üëã~ à´‚õùß�Wyƒå†xë‡Áöô§î¡ôÓ�^§Ó÷c�î�xOƒï ‹?é ê—®ÖÕ×�×ÿ-˜Þ3HŒÜ^Þ¯‡ÑÛù²¿ÁlZ ßá+ç0[áÖ°øß‡ÁG�N›ÅlOŒ :Ö}!_'ðúpuU°=à ä{°!Çóp žªÿz„ÒŸ‰ðƒú\x‚NŸ¼�pë¿‚ú¬`®>µ\¨ƒ;Âèw!þÌ ½?@‘¥Uù½àŽA¸ àuPþÝ9ü(À`üþƒÃ‡Ãú‹µç…Â6‹`üˆ?/HÿÀÕ:~­à™ ƒ§†ÑïƒsØño¸ê6AxÀCƒáã�®ŸÌï|€×A{ÏâöÇ*k(ý5�§=äï_>4ˆßpÇç‚°…Ãßcz˃öÄy0ølýG?à |nósÀÍÕÁö9àê;ÙØ á¿l¿=¨Ÿÿp&Œ…7rXÂÁí fË#ü"À©:ymꚟíHo,³ƒ1þ‡�§ÝÉÆjd/¼n~ßÄ^¡øv€«nb67†÷Ñ…§Àïf€íÅlü…á€ë‡í£»Ãâ»Îúcaøò°ôÞx ZÇs~þ ð¸›U}‘°ÓÛz‹‡ ¥¥“‹\s*JŠfLžêJ¿Q(:³hdÉ}“g¸J¦ßQ>qÆŒ’§bzIEÑô‰³Š&•O›áž^RT>m’PT0røŽlçHGQÞawÌu”ç( %{cÁ*féô’aèÄ™‹&M›R4Ã=µèþ©‹†MtMžYRt_‰‹ùF—LŸ1yÚT¡tÖôÉ®F`byyriù´‰®Xtî(›8=VxÞ´©÷ î©å“§>+Ú¨²iÓ£§3izÉDW î�þî8éÁ5}îŸ:¹xòŒŠ‰®IeÄ=†Mž:Ù%�᜜!…üåO¬¨()ŽÅEÁô’%Óg–äMœárLŸ>mºPT „„âòs¦§ƒ_¦Í(Jf»¦Oœä*š9±Ü]"T¸Ê€åâ¢)nWÉlÆK´d)^ÑТ ”»J´Tœi%S£NÖ ÚD Æ$Ò©£Å˜a$ˆh‘©âÿC€ÿâr¬Ñ+Þä©Å%³‡—BüAÁ¼Îœ69z©Lž:sÚ%Ó@%P% O—0µdÆ×ª‚Öv&N¿oFtR3 ¦Os•LrA%€<Þ?¥B(×ò$‹¦Ý{?„ SJ¦@p¬z2ò1mÖà‰S‹ËKÒbÉqÐWtah%?$dz¤f4Í!S]Á óæNHq`Áåæ 40¯hxNÎ(G¡ª-bK8‡:ä*Èt,˜33ž ŒÇà4{šûÞò’`A—ƒ.(fß ¡’©3£"¥C„ìÉÓ¡8¹KKK¦ŸI.¨B\Ó'ƒ®™x/i’aCІOuN…\E¯[ÀbNLýF-A­21"éôEöÝÃæ¹#˜éŠ3p® 3'†0ƒ­ìˆ5Ã5½<†�†òK¦L›>G(*š<íÞX2 –úýLÝO‡h*ëÐÚ¦ÆÌvLÝ?ƒë~^±yéLŠ­œfL~°dZi˜ú„è}èçâQs¦Ü;­\˜1ù¾©ËÔ¶“'–¥B¢+ÿÔ>K+€5Wi¨ò½áz]#›\ ZuRYqIEì«ÖRM Ñ{4R“á͸(¦l‚ô‹ŠŠJ¦OŸ:-F¥bÆE~‰«lZL ðFr` -†šPÅZ”N}ÈÙ©ˆ;bÔj"ƒ† tH\M™21ºŒÝCî(+™ôÀ ÷ÞÆó°…Ïš4[„f× ,.†l†0yÈWmÀÁBk@¬ŸîI.b¡dʤŠ9Ñ™~Ã,‘RÀ: ,5ö+±um0-fØhÖÄY´ÿ¡ÜJPk kOªÞ¦2).'û@_}ÏÀv)1 ‰è¤ë;­¡€9ƒ:cÎŒIÓ¦ž)Ñᬷ=s–cõ2Z=#Ò}ZW£mq5åh7g,AÒlgæ<GoÒÍ “.%;Ñhñ:†s¨UòÂX áŸ!ÛØ¦‡dϦ¸§ÆjwšGA~(wÁLÍ÷ŒõwÆ™,ü ÌÐ .*Ÿ|oÑL>6‰QªXoYÏϸQÛ4 ¹„úcuDS4êîQ£c¶é`Épl;"¡É;(µå«é9W(¹SyG½Û´Yƒæ ›8úØ¢âU[ð J¥tZѽiPý1å°ì–ÜÇ´×hfEG=›ö<9¼«…v¶¨N ó€AàÄûÀ(žèš•¬{ªÊŠ�…7éºÓ®KF9‡)J¿îúëUÿ”vÝ�Õs“€%3pнAøÿÿýýî°Á}/v™³/wSüÊ¿-¾úÖfÜÁccðS}6 V?Ip7á¯ß{S7³ \j ¦Àöï°ÿ |쿸0Øøk‚!îÙÄHˆøŠ9*æ¢[Ýø0¸[^\D|Õß-Ê÷h¹P)±S&þ×Ì¿ BÊ€8Úç’ÊݾÜíÇÝ4îfp·?w3¹›ÅÝlîænw ¸[ÈÝ»¸;Ž»¸[ÌÝ2î–s·‚».îÎæî\îVq×ÃÝ…Ü]ÄÝjîÖrw)w—sw%wWsw w×rww7pw w·q7åf.?îöãnwûs7“»ÙÜÌÝîr÷ð qBÂs‚Ð‰î ‚p]œ¢Ì�7äŠîBÀCwà¡;äŠ.è8tGƒ\ÑíÆöS%< òC·ä Ý+ؾª„þ‚ÐŒî%‚°ÝÂAtç²}V W B¤Ÿüt¡ ü˜!<ÑÂöm%þI¬èþÚ,º¸ö‰î*AHFw¤ ôA·/È Ý{@N讄z†nÈ ]È Ý|¶,ñ/ 7t﹡;êºO‚üÐü¡[õÝÙPoÐ] õÝîPOÐ-‚z‚®ê ºA=AwÔt¯…z‚îPOÐ] õ]P@KÑ}–íCK\ õ]ê ºÏC=A÷J¨è΄úî ¨è.úî,A¨Gw¹ 4¢›"»Ñ½@Zн_ZÑí-mè^( ºn¨èŽù£; êº l\â3P/ÐÍ‚zå”x ” º£ \Ð]å‚î"(tÿ å‚î(t‡rA×å‚.Îó£;�ÊÝ(t_‚rA·ÊÝL(tÇC¹ [å‚n ” º"ÔOtŸ„Àz\•<¯.žG­Ž;4k�þ=»Ê0ØßL0îv+ÃHþz‚qÇ[Fò¯#w³•¥ ¼’`Ü=T†‘üÕã®¶2üè¯"w%”áÁ¸Ê^†óO ½e¸…Í_@0®Â”áV ÁT†[Lüiã*`Ù„Sƨe˜!¿`ÜÙV†[züÁˆZ†[xü§Æ]leU”‚‘TÙBÊ?Á¸‹¡¬šòO0’.[Jù'w •­¤üŒ¨ek(ÿãîž²u”‚µl åŸ`ܽSVOù'I•í¦üŒ»wÊš)ÿ#鲃”‚×"ÜJù'“*S(ÿãJ~YåŸ`Lº¬‹ò ámTþ&Ì?ÁõTþ7ÜHåp=Á»©ü^Gp3•?Â+ ÞOåp5Á©ü®"¸…Êá ‚[©üž@p•?Â+Tþg|’Êá4‚‘õ² §lF·òøícVÊ*Æ]e³î8‰0f­¬ŠòO0®Š•-¤üŒY-«¦üÜᥔ‚1ëe+)ÿ§"¼†òO0Š¢låŸà~o¡üŒ¢)«§üœðnÊ?Á(ª²fÊ?Á™¤üŒ¢+k¥üœ°Bù'EYÖAù'8á.Êÿ j/ãþZ+Á…[n&E]fG¸žàq'#¼Ž`}.½ûW\Œp_„« .G8 á*€QÑ8¥oFRþ ¾ù;Ÿè'ˆ‡íU[«pÓ†{¼,zÀã[Ÿ‚kúÀßk>k8'þ`Ð1#Å· ñÅz“Dѵ"Æ`8:¦+‹¸&ê­wõò‰Xä’9U¹RÝ%"y“Øh{Ïøþ(e%%Ÿ$˸'ػǕêBL);ÕÌ)ŒKµ6e§’ºDj-ǹ&âx÷¹.Å>,½ó|ôMJJ 4‹IcÇï”û¤î¬fÿ =ñp_i|§2~%&úœ9NÜ]Ò.©ò¤t@¤¹©f)#U®±$‰ÂTK wÐwßá¾^®±Â79Ga¨aXä°�Ä’ÇwÊî.(¿“É Ò#-í¥´¬@.7c®ûéRF‡Ñ@Mè©—lžw)™>žº·^j°yÆÅ!rM FÉo ‰ÕHÈ'1CŽ6`>òpVÙ&ç·šš (/Õ¬Iƒ[‘,Û!;ì Úž„Û.WáVZ)¤~‰ì¶zºº#](;,ü©—鏨ØOj{OäÜu¤og>˜»Ä`jôÜÍ3±Üõý_çÎÉ_åÏîÝC™´y®VÂóh¦<ú/‚/¾™XÆgâ¿_tþ· Œÿ´ÿ·ùWBÊÇæ™|ZãßÂøsšóoÆ¿ë¹&#*ï^ÜÉUZ ôÓ÷a Wb n»ÔÜD;Ç ”©Œå{TùxoÇâ«IŠ.Àí!;®ù(¼Á¿Exù­ñŽ6_&nK êwg˜ü®9¥É/‰—ÿ)&?` ÿÿ‡ÃXsRcÀÊx¾€ük2£W¯"Vê5Yÿ ÿ…ÁŽ0ƒ Úƒ¿€Rþ’Š7}Ÿp~:A8rFqjüåi ÔÙJÅ<3Qð‰®°L :¡e"™e" ¿TÚ½›÷R(qÿm�£ÞB}=z”xxœ\™¤\» 5ö´Ó&Áçl“g'fÉÙŒ‰á|&V,;›!‘ÚzÛ¦zɱßÕGNИ)dL ^´dÈ‚ÍvÖ©FæAÐBÜ^†Aà e;æ†BŠ¥&5:£6—LðÔ»§BP©mªÃ.Ë*ÛKmŸe§öÁOV”4I’è´"ö’NAdž ²Q ;ö{ëÝÈ#2œ$6ŽcÊ|ÃU$d1þ"'iÌES¬è®­Ð]}:€]ôŸA¬N‹<ÈZ*ÎN\OÊPØV-!@‡nZ¹bù稞kt¯3¢›Åéþt0þ†ÊZ‹³Q|3ß�ñµPGìÛv5ïG¡Ñá°(yK±tŸ„Ò-8*7ÍL`Ö3@ê˜ÑñäV1‚øâád° °×«<É¥• •ËieÕ× 5jw“ë2üh××ë‚þšÑ(§3ÂèF{åµ'‘½#'Lš½"«õI– ±Ñhí éC¨®†Ê¾jǘ`BÕk¡1H;|Y&ÉÙ†9úô',£6©AN�«k±]+†æ·†Ô5ÄN‰ÇöŠ]Ò.solkZSÅ,ä·‚Í#íf%$Ø'ýÊHúÄ^äÃerßÚ¾³´šµ7¥þ Ìl7Ȭx8Sr´3²¸-=h7?ʰoÁ3Ûàgþqük{l¸ ‹þR±µãšFßÜÔ?Šßu@ƒöÜAí šÜ¼ª¡XIÊ*&ðf—\Y++ªjÜOï=:ïâôzVMl›­¹(ëÃ`éèpÿ-Åg^‡jÈa5uH+P¾\;¸vÙ†»í”ÔCOÈâJÎo-µƽ«Ÿ,®_“9õ<ÖÌŠ«ëñöµ‡úõøê+édUvý„x— ÔÃS² ”ÄAD•kñ+Q VØ«àpQ‡êê!qeólC³@¼¦S-ŽÛyqÈ k@úøGh_ƒÔÕûÌ7ú_¤èÃ;ÃKG£ŸÑ™}Ýëë©Z,ÂA¿êëkz@«±ã"j,jvVe¡ÂNÄ:– ¶ Vg­>³×ÌÿË~ÄlA³šˆ<׫("ˆ!u(oPoNÕ–“í ‘›Ìt@-$‚qRWˆµ.e¦¦”ã?íŸa»m Æô‰%rÀZü FÞÒÞ³ZÿO“GPÊkP2}a܇mŒw ÕKã©;or e=ÊœEf‘Ô^†ÁG+ë…Xûމl*W²í g«é@X¦ÈD;<’6!Dz{§,[X$g ï§åü6ì†X<ÙÙ±ža&eRf"eóº°‘m%¢…G¼“"&™â1ùÌ¥:û0”PzX%ÇÒúñp ¾Ï¿#±á=®oQÿd-Á"òåú§ ¬– þi¦òŒõO–¦ʪÓë½GAÍ»DíGVÓ9iq©””Ë4ÌÿŠj ª(TO¨¦T•8¶òyêì•ϱv#åóU{,å³³ÝHù¼Ö©|^n7R>ïEU>Ô!¶‡*Ÿ—)ú åCѯlçʇÛkóãáoìj¡îç[W5–åÛ¤ˆ ›†®Ú¡«'ß„éyâñ8›úÄM@Ø÷®ɤÑî²b‚¤Zpú!3Uùõ{dE«GsÁ~ºô<|ÅY¤Ð5òƒD|¢ò5uDô4•5D°Í$Æ+çHø ´Í³€Ñ[ ñÛ_Ú§Jõã˜ÇOÆúšgÛä°79pîNXè8 ¯§Â?jó6“¥l…N~FUå(—&X¤(5ùÌTj;ý¤[wÙ<oC ¨²é×y—J•Ír¥U­·I  Êæ`•mp‘!¬¼8ÊT œ{©^§êuå-?V2µJ>Kzl-›·1¨’Å~£*9 ¿:ÚÄÆ<½Y9¯òUN»?²rZýF•󋨕ӂH›T­ ¿šüdç‚ÕGææ¿>Ž:HÑ`Vp {®n E5 1#‹l+ŒW£$P°•”@Íá6và *Ðýeœ*l0½TñzVAü7#‡bÊ#Û åâ§ï Îý‘Û—Ø9aôÛV¥G‚æ‚SbH± 3z¡¼d!7µyGKv¬³MI‘¨f-Xݲ8ÁÂ:â�ë6ÏÊ%;bèw(¨VnŠ–MÑV)¿%Š)Ú*ç·¨¦h‹Þm 3E©=â·ø�Ÿá›ÿ™N`]ÍD­ÿMÔ–öWú÷di|T¥Ç"ÝðÔ4¶rœñVe¥´#‘ 51­NàöЏ†ÏsFVÈ@Êä×™^O#”T¨:Ê+2ÒÿñG4c ´¾ÔIX嚎*ÚXÀú ¨4¼Ò@uSÇ2ýAôp@›‚ï&‚Ï"A7¼[#ÊâH&™¸uÔ¡oaŒÛUÆ“9ãKp–\lLÁ9]}*cÓýáQH¦á&Hƶ¹ a žõ¿+÷˜ÕöyŸìY,´žüŒöIÐ~|‘vÆý¨ªm´*­8CÐ/‘*E3É&¼3š™=½±x›ƒEãÉYNÃøP9J¡€Í�•ñ#¤—IïoÌZ‡èý££#ùšÌHôAܙ¦嬨$lÞ!,NMv$™;8™«8™Á1ÈÄq2y‘dFq2ÿaÓDrA 2[ùTRa$™!œÌRNæ®d\œÌ¸H2C9™œÌ„dþÌÉG’Éádzp2e1È|ËfõjÊ#Éds2;Y¹"™œÌɄЙ®‚~4úG¹Æ•’iºxŸøõfqÇ ZRã­2$Ê;×rnçôûDPÑ¢ôálöÕØÔfÒ úq6»Ø ¼F—RH„,ëU"¹}ÿZÿÆ“jÿ\/ùólÓÛCÛ#â %íë@€Lt±k†+1mŸØu­ëwa­‘ôo‚¡ذÅý¡±x߉gÁos­Ž[¨5®‰ >?´5Éׯ£ åQR"£üŽG¹šGIŒ’Ä£˜y”¾†ÕáTˆÞ´ÀÍôÝ(eŸ—Ö¿¾7 l*J¶Cg(˳Ùl6ªˆKjIÇÒœÔÑù'´B ßpZ”Dj¯Ÿº °ø/–{Èë=\Kƒ¥�ƒ%°˜¦ü´Ž&lü3JDDø5~ò­4 i•åÚD²ˆ-y)cPÓþÈYdÿ8J9âAz7ùÃò·<<¸²ž?ì¯0‡NÊa¾Eyˆ}¯°îƒšäõkù䪕å/¿i9œ)èéû?¢õ¬¢ö7ÑX:Ô…‰¬CU{?êP“x"jÌ.ëÍJÎpj‘u|3ÃøUºDLàVEµ +±mV©üªÓàsu;¿Dë_ãºaü§!ÿOù/¿ƒŽu“;É»ÏæùV`&d>Y›|ÐêbÆ€…7¯`.²£¼%4èÄ™ÐИqÐÛ“÷¸þûÒp,OH" ±F!Ë#e(F=Ÿ®V†}´ù]—ۨKaD5OÆdÀ$ËæÄ á›—Ê¬d6_Ý)6¦‘Ì£.=d4°Š˜ŒHø{ãœé-'µ‚8y&Ù†l*×"ëÎN*I²ŒÕ2¢¬>ó§óqiä(g^‹Ñ(§ ¾úO¢nŸgçnèüváÙüöWÿ´ï˜=Oëyr¥Eyp>V÷¾ ®W/D|'.D°ˆô@È„c?~Î߯[†P'€vµÑJ‚¶„'uÑà çù±&fåƒ ê°à >¥…&¥å¬†í¹ŸI¸:Òö\Œ§Îlžy$ÊvumdÉB\ßiò—hå€rL§UÔ!FÍ%úøجU†õúä;ÿ0î<Á ïÅNRno’vÈ‚’X…"Ïk3 MŽU:MŽVÕ>,g#Ÿ[iVÁ› ¸¶ÇïèëÐï3Š·G±Y$óHŸÙËAœR‚OÙ4±°äMÆd½VBª "åiñ¾ÂxÞ}’¯ÅL¤+ÀQ—¯Šj#±ïí ›¼¶'DzwsµßÏÖí׆ubÕ¸?ZŠL±¤DYÄ{Äd¢'™dÒ’ˆDÍ$SîD/Fµˆ^Œc)9ó�Šaóì`kûï"„%ƒÂÂf7Ä©‹÷Žélu£Uæ41˜-‚«X®YèÁÄ)#, !;¸?-˜ÿõl]Š"5Ñm",ª,ºB#vÐ:h^Tn‘k6„. c :;”0îmÓÆÆë÷[†ѵm®GÊsC)Öf?5‹ßhyþ#а-<mÄú…¯;,bk¼Íƒ‰ÒÆlšØÐÆ´>¶³*Ö™*hs^ŽVÉÝ&;Z¼õón‘7buNgúVÕ rŽYl2QƒO õÀ´ìÑ 1ZÄ]Ö]í“­ÐÉôA… ÙDšU‰? ›ÍäzUšå÷&ÚŽP¡6L¦(PÊÓ³N¦q§¨¾8ì25:UÁö¢Ö@³Ø:‘¹`Žõ‹ÿTûEñ›! Jœ“ ÎãÍVqÔa1Û¼ïãD§[5“+“d¦ˆÈ>³R3=¯˜JÕÖN¬»6/î*кåóygìhåý3¨N™’Ùcó”`¢ÝVÓ§FÝÖ›ðÕŸAõ´8z }•_]ðl…cwx„åa‚‡^�"ºL'õE|½^­w07©TX•Ôª ’ñ)Õ¿ÿ†H-øÁýÁª"×4†s²4‚4ˆ>×swÂh˜Ç3ió>ÏYø(GàÁí‹<@9¾"={\2IÊ­ŸÃ‡w)eW¿ïbº`+©UÒ„=z%n#•êCF™XBâÜTÔµÊÕûi¢°MmŒþ©´ßDå  9àšÞBŽ0´6~bdh­ø$ÒÐzú#Ck6î·è”0±º$ê’pÒ¡‘ Ë*vUê¿@\åfDÔ;z½cù%Rïd~‚!ƒY턬*G?Aab{Ë4{FvF—fg,Õæ¸2pçä{êèÙ¸Û<«0¹ãPXB›VL&Ö[h½¤—U¡¾ "Ã÷ý.4]Zdg‹œßâ3gI>âPïž7Ÿì_™GÊuèËžo¦Ý æ‘ÒqŸy>[õ|ꀚ,ÈØÄ xP•õ&ÉÑâŸÔÅ÷3isžse¹ó²ƒ6ÎX´I¶¿Èæ‘…à æ}¨\ÿÈÌsÍ•A™É¶Gð&™&¯…Ûéûpò"å•òH}¾Ô[–©·´„åò˳”%eÎGð]  çƒ-ØâKÊBÆç.¶ ó/Ó$:€QdA²ñÔ¨1H6%DDÅ#®l90 ´jŽOÌ¢Ðæ÷üŠõ¡—ª�=üó”_±€#f›[☌䪓è£ûmøŠ—vᱸÿ=m·C‹kÆÝl÷°×Œ+ÇíÔâ&ãÇíÒâ&ãÞ`·M‹kÆM4ŽÛªÅ‚qÿM5tþ‡Êÿr¡å|þçêPÛØÜÄàðÛie£VýHùýBFÊóÛsˆæöƒ4¾·mÊ·ÃøûFèh6ía£âd6a?ÃÒGÚŸæ3® ²÷Äù ³²™ÄFóØñ;Ù|G…çòðºØÚE ϳùnšK³mr&ãLÛΈnto€Íµ-t~gó<Å{Ž~ÆŠÇ•ºÖÏíœX6³{b,{ÙÅ^®ªüN°yh<ßnÛ:¦Ý[ ^¼Z3l ÷-Žs`†²Lãèöó QìgÆÐ+Œ¡¥’Éÿ³nüÃn¾%šÝ ̹lò¿ª£“o_hò/;{Ùu][h�Q´¹KQ^¥� !“)º)êÝIiŰ©vÁ¦â¢™Þݼ’D5X>ÄÊ*vr”Id0Ñ#á ºõƽ¬ÃVý4¥‡SOÊß>€ö^BóÞÆÖ4JDaö¤8ÆæV¾ýó‹é<I4Ë0ß~èbश©ìü�[\RDZ€•µâw‘™ƒºh‹0•Éý|7Í®)·U`#÷C#Ü'9:]÷H ´ßήŸU+¶¨³jKCmá°h+r¸„™Ç%ŽNÙªP£¤WË´ 0Þ°id¼oddÝü~¤‘uãû‘F–k‚ñüñòÍÝü›§4`dõ¼ÇÈêߺGË…–c3ϱ…Ê~Ýv@ŸÐ)¨úíuNU[;¬ÊSQäöOh63†'#äüoÑ®�ÓL^Éí0œ[c†`` Û=L%íâkÅš Éžïÿ¾¦ºÙ<Þä¬ì6’ó¾Ý‘rþp÷ÙË™X©Ü­Ïþ}†rž¼ÛHΣvk™`™ —ó‘¦èr>Mr®æzŒõ7ÐÓXqwŒÃ¢Œž‚Búc5Fw—øýµULJ@‡43)½ôTØþ1Û¦}rmùU_¾†ðÞªA~)0ÙÞ,CÐ]~°‚­´èW 2ÕÕ‚í/WC¼ÍÁx­úxÙÁxó«™dy?Gû•Yœr5ÎÈ ÿÝ8ïò9ÄkÆ£HÅUŠ—Nó3v¹»<’'«è“-TIîó'‚z?]žc–å½DZÛŠEãBÉïczÝn{/˜¯6}yZ¾ØvJÖî»Ëvl×5�š%„vP–D›ìÔYö5»@© ~×ÖÏe‡]yðšÿýH·dgÛT™ŒxiM¤"玱6aŽU.¥)¸UÁΦfqÇ‚Á h·ñA§°ùÒ¹lœòÝýÈR?`I¶‹õ`Ô779ð”¢Ðä8Hò8ŸÏ›Bš|ڔϙJÎ*ì€ØŽÂé™Þ£6Ï $:ÒÜðuœoßu—z5ÎŒâ,õ~Ìn&ZÙÛˆ{ü“ä1†eÇÁpŒeˆ‘k–‡›ƒ³°Šw IlžK‰Ê¤·ÑòM?*å{˜žÉÍôY7žE•Íë°1œɓÞ*LèeóÇã’é¶ÔÑ{Ä~_²áŠÌ°Ͳ:S›%–sS�DÆþþOÐ]×Ú<Û@u@™É¾T6¾eS¥Íª3-©þSãåTsq_!¶ÈƒÀë‘ÍöÛÃöB±7ËuIbëü¹ƒå$šþg¨ÔÙÕQˆ¹‡\×—Í6AšÊN6®w™D‡Ç<`–Ù¶ ë4“wn–ØÕÛæØ“eÕ3”<f›ç{ðÈ[IQlÞ§˜ «*lÞý=P±7cÍÞ–,à6M]pJk$›£jF.‚ÈÚ׎Á .›wÕfÎÖR-Lgg]`{ä ä‚L²âHy°'¾šYI6a9Š™oï3SAΚÅÊÏó%S~|]¨YNŠ“{x÷¸²eçAðÃãfôц%VusiØ~Ùj¢6 ùU´¬E! ÁIqü<å.Ènf<&³]Äq:`ÍoegâÊËùRÝ6²ùê¥ Òd† AZ&R³62>i†ôbÚ¹_¥N’rè­A7çù1ÇÉTûOÂwÿ*×Èžr{ƒQO¹¦!²§\Ý`´Ï®·6§9 ‰»ÐpúqpƒQy=~å庠/"feAcjæ‰äåÔC>ŠÚ»¬=}=ų½þQê<v3J™µ“$ o€P„–`ûÂ<ÙwàT}ŒÓáÙ(6ÿ«o¿‚ñ»µJ45Qª£Ù'l‡Ø$9 êˆ}@Û÷õ &)þÊÛŸ\G8 ì¶GðL³L¸<7Rãã¡ÀÇÃìÜ«5ÀG¹f¡j®Ö1•d×sXLÆcmã\ò.§\T)©ÍÈ6BRPpa:—ðtHnùͪыùòUuƒ•ôFŸŒD�ª£ ÖÄ/ê!/*õ·õm|^÷lËeM½A¹LCJÓO0>÷syèØ. sÉ?(ÇqɰisoÙÑbj–h¡ Q¬vŒ}ËùÚLG]=•Š*O%_(S%tþŒ}…<dB¤øñ¯ÀÝþã<ŸgÇ_R™1øk å¯Ñ¿Îþ2CùKGþ2Î?{Ù1øÛÊßnCþ:BøËåo1ô<þÚ_ω?ky1øÛÊß^Cþ‡ð—ÊßOÇ€¿Î®sâÏÂ_a þšCùk6ä¯-„¿ÂPþF …çÆŸ%„¿q1øÛÊß~Cþ”þÆ…ò÷æQàoñsâOá¯8Cù;hÈ_kÅ¡üõFþ’Žiz*È™5çñh"~‡Íƒw aêÞd¶03÷ æ j8¤MJžX÷ $°þ(ëŸXþC{È?5[tzpÝV ÷RDÞß”“Âb>ñ  <TcÞŠIf%«Å8½ˆŽí6–â™Uô•ï¨è½àG¿Ç]T$¼¨¡Ü˜]®¹ {°¤T’,+&åæ1ƒ–Ð2©DrU¿GgÕoT½mÀÔMHåæ_°ßP>z[]jZñ _jrW‡®3e liÿ–°u¦)Á|) €[Úò÷AþhËšthKûÇvòýFJ¦WY%_$ ìÌHŒ5©àÞÿÞXn/ev7ضóåYV9+wj2ÁˆŽ¶(×aÎ*\%FC•Ö¨“RµñOžä‹ÀlMb Í4ñR"d‹‘ –Šóppól'­Ó.:ù]¸]¨Êœ-NÓá‰Ú-Ø=C\Ùmƒ?Ä"è‚0)Á_ˆ¡hôÈàµÊƒƒ²UŒÏ¶à:‹­Xöé¤a' ù‘M—©YÙÂèwþÌâ9¬ª,xd\÷ʤ‘±âÛŒÅE„ãü;f2v[µ À•z°bÙL Ôü¬fOU=¨Šu8óñ&Š5E‹Bc' ¡âámÄjõ¿w„Mt‚øÚÁ³`ðÊ#¬4\š¾Oò­#@v¿6Ú±ŸT£?6´šƒÈÐÂsÙÑ_ù‹žÈ"’lD$9‘~bòhõ$[÷ ®õåï—¼v¦›%Zd¤L";CMﳡ’•ùyëWÞz‡ëUïŽãºý$Áå½”#êyÔd<Ed­JéßpâÕíê2®b¦Ï´óõYm-‰-öý@à~µ¹!ál›NÐí³TW“B·ZBdi‡ÔÁü3Þfúœ¶2èéÖ†ÁåapU<; Þ«ùÇÓp]Êä»1ó¯¿K™÷ Ý^õkòÌ+¡¶Ù¶ïÓvͰÉÊuº•µÒêíÔ&ˆ™O¾ÿ­(Ä™\»‚ôëy£”8J&÷]\ÿJ•vŸ£ƒ)0—À˜£K+™f,Ê ±tˆ©éûƆì×EzoÝ…ôŽm;{zNÞØˆýÍâáþr~2ŸþC¤—iœ×ÇY²>R>Œ‘ûÒ’\ 믔òíxüµ××$hœìðP¾Kñp}í«Iùæ4M€¯í4;ç r\øÊQ7;f´?zßÚÿ™¿êhùsCº‡¢äo”ò0ÑkÚA¯6½+ÞçQé]Oôféèu2zåÑè}úÊ¿3 ½¯G#½«#ù«ŠFÏKô:d''Zß–½ïDÐÛÞ_?ÃõTãýê@Fªr ‘­²€œ,;p‡h#eGR˜„ƒMLÑÉ(µíxÞ"âT“µû%ìÊa'’KgäÔ¸e7i¬î~¨ù‡û›Õó«ÊfB:ù6Æ“+-Þ}PI]#¤.åëwñ�³´ O»®§mflÉZI}‹iF×õ’$Õá]bø½ß:TÓ5§ï =«š~´}/õb¸òÓ› |Ê šK’|m'ØF6|F‰m“Mâvòݨ¶1‚OÌû†˜#ËÀ¢Èo¨ ú§tçs“‘ ™³+ 1}Þ&„Yð&2ï¤LE6véSfçt‰ßÿ†þ8.Š”oãêýF lc@°¾0ùbûw&+c(ýå[L¸ƒA¢”íŠw›N¸—b~\ÐKŸXÇï¸ÅyèuNW˜ •ÍÇWG’ƒ¾½.†ä^{]•™LÑÔ,², ƒàö'ÙýloŽB.nÆ ž†•ÍÑ\àyS0ZȺìu£é¹î¯GNÏu{Ý`!«(ÆBֆׂëKFçwW¿f4G÷ØkÁ%Ï™ü˜+m‡AY ›äíÃ6z$뎺²Cžê~E‰íãÖŸ/ÌKí##2V;\<ÉKM¦9V0XqÚÚÿêÖðûZ@žG¢<_Ütòüh­‘<ß^)ÏkÏQžÅkc˳p­‘<o[û›äÙ?¶<™|Ž@ù<²ñäóÆ«FòyêÕHùÔ½zŽòÉ~5¶|2^5’Ï¥¯þ&ùdœ|./@ù”nÐËGÛ7k,ŸÇ_1’ÏœW"åã~åå“ú .D?OŸôŠ‘|N¿ü›äÓïläcŽòÉYõgÚËFò¹ûåHù8_>Gù˜_h»§äÓ¹ÆH>_®ùMòI;ù|•ò¹ê­sÏð5Fò°&R>]sŽòi{)vûÚÿ’‘|¶¿ô›äÓ÷Œò¡óK™`%+o硜N¯3©ËÃ|qx¡ã︠[’›mèàš¯vŠìŸljMv¶x÷Íž$;ZegÛåÍbWÀ=Zvì‡áÐ'í×±u1÷A¹²×O€rPªla_¼Ùýdw«\ÙvùiñtÀ}ûÜ?)ͽ—y%w«TÙÖ¾]Ý¿£®8®„˜®<ò¢‘4+ð+±)6fê¯)‹¨9/U„´#+B¿Öݾ‰}¿ÅÀZËÔJ…Õ×ô;±f¾¡¯¯\áD«¯+^0bSz!’Mï çX_3^ˆ]_S_0’p¯~S}ÍúßÚ› 쯆Pû]/Ou‘:Zû_mØþW´ÿÕçÚþŸyñî˰ý?oØþŸÿMò¼ëlôcÃ`”OÂkç ÿò¼‘|.~>R><Žòi|.v}Ûðœ‘|ž}î7ɧðlä³*åóí«ç ÛsFòùuU¤|~YuŽòY½*¶|jWÉgÞªß$Ÿ¼³‘Ïü”Ï®WÎA>?®4’Ïç+#åóéÊs”gelù¸VÉçž•¿I>g#Ÿ‰ÿ¼|öëG+ Ç?+ Æ?+Îuü³"¶ýZ¸Âpü³â7ÉgðY²iü³F•Ï.Ç1ºyV«C eôê³F2ª{6RF‹Ÿå2J?ª—ÒÄRºíÙØµèºg¤”ü,IIêÐäÔfÛ'×`r Ǥ] ò—ÛêçÓ0ÿwPþ_RóßäÀÝD¡"0Þ?»æ#,y&R=c(‚{bˆà–gb‹àOωà‚gt"À P<ûPé)û¯µ/ ™of÷­âäÔ̓P ½È®°¬< &&ߌÃNC1¬Zn$yy¤,׋!=  bJ A\¿<¶ ®Xn$+&ß!7<'âÜŸ㨄1yÙݦn%n¯‹¸ï–Í•á†aœ O–+ûð;\t13»W¸[Zx:ÜþÉ"ûçµ~}þáñ!‡O·ŠuèÓFbÍx:R¬iO(¡û?ÿ!ŠP?ˆX_,Ãiõƒš2ºž‰BõÒm\f$Ý×®¶ogstȃÌÔù`ú‡(S¼è¸}yˆ<>¾åqáêXí­ÑP"ý—IäŠe‘¹tY¤D>ÿÁ55ŠD>gÙý. Ô†Ï\"Ÿ‡Jä­§Œ$²œá†œs8tK†Ëäs¼|ĵÒ?38?Œößmdÿ=îú§ïSFò8ÿ©HyôzêœõÏÖ¥±›Ý«KÄP·T¯ i,ë’³þFûcëSªþyçVC`UˆþiÅhjȸ½\²ÔH楑Ò–FQC¥1äñꓱåñô“Fòð>i †pŸk^f;È Ãî ª5äzÚ?+À×Ù¸ý›IöïJµ~”69޲ê¡J× ¤û“Fùù‰Hüø„aõˆeÇ,"¶8=a$Ž™OèªG5ÔÛf^AJ%÷Q ´¶]]'ÑìÈÓ· ZW˜„È5¨ª'´u¥ðó†ü¼]Kø+•#™5rÉq¨§x‚y`ól0ãŽ:#1®­‹ãš:=|o !Ž©‹=Í­3bz]ð&~/ça$oÛ6ï@âïׂw=åw£&ŽXÿÓäsËÍ´þ÷¬*Ÿ…úQ„qýz¹ÖH05µ‘‚y¼Ö@01í¿Ú3ص†ö_mèù'(ÚNÙÙ±0Nëí†ÏO°F¥Ü2€òÿ æj¥#9dÅþ«1´ÿj 쿚s<?ukMlü¹ÆH�Õ‡ ´?¯o‹f­¿pû_ÍÊÿrÊ¿–ý(å¿Ä°ü—”ÿ’s-ÿ%g(ÿ%†å¿DË~µaþQ»DæŸÕÿ›(ÿOcþ£~™~€¶Ð¿tF=¿ØH ¾Å‘RHaR )ô_{¬Øw±‘Î_TÜÞÅ͇A0€.G•ÆêvÑ@_*é7Òüç2ìoRÎn>aEµáügµÁügõ¹ÎVÇ^ÏH­6œÿ¬ÖßS‡Å £å`=x½Ýì_þ•ÆOC~ßxÜpýïqƒõ¿ÇÏuýïñ3¬ÿ=n¸þ÷øYç—×÷;30×xè˜ÕwVÓCòn\ß×?f”÷g‹ÌûSc}üXì¼÷Ì(ïW<ö¿«ïo ò2XþdP»+ Eÿû õ¿Ï@ÿûÎUÿûΠÿ}†úßö›©ÿ+•B;À™¡çÅÙx“Óƒ[%ãzÆœ'ðXre\fҮݮ®ŽcOà9Ú¤.¼ûåìIéHC+¥å{uƒa‹°lÚݧMõ?f(ÃâEF2¶(R†CÈ03† •ƒÖ•;‹¿Þ‘´E”ÉÏ©WhEŽˆx9¼¸(h6¹WiÖY}d¼,•A |m_ÂÏ_Óî#|Våó$ÿ—§ƒöý(eGJ<±ŽßŠ·…*‘É eÛCì•QÊŠ Vÿv9tc•~ÿî?’iG¨2†P—×Òþ#±+~æ0¶çH¢½:ì)I½±ð”£j»MC÷=ó(í9Ú‚‹‘Ú¾Iü€ÇIÕ;½Õó°´KÓmQü…ö?AútK¢¯æ¢_Nv‰x–ÑD[Géñ og°y¦u$ÚA·«ÐÕ†ÁÀ‘è“o¾­Ù¸ËH½LEÀçQ¡ƒFtÇg‡ADHºŽ…B¦k ~aùê,³Ù%‹"^µjóX»ÓµÁGŽªUn–Rx ïå«)gÄ€ìß9Y•›Åœ`!ülÁнðÛÓl¸•m)÷Éó͇ÏÏÒqVv#K`‰Š¼š_$dëw*²O\ÄwYñéáhµ„VDh³¿ OóIJ3/ZšxëÈ›Í "ËŒA¿Ö5­ŒÐNàIò_ÍÊ_N«áð2>I¥²3ѰŒYà‹‰Lª›4©~.UµŒ­”Ã0?â^%(˜/4ÁœL0L¥³˜Ð¾ý%ˆ0FSS(µ\Bë~Ðz#ûjÎ?ÊØ•ê²ùD&Veú„$ÌDb?é· ÃH² <„ðoBH9ÊYUÓ”·è놼D­ÅU„ü!_}”•a¡z*QË#;Þ C³ZM+ô£ae¸C]c,Å^¥ÅK‘ÞÆÑ‡ ÷¡·£³À޾ÿûHôE£»1: <Ü¡O2H=‰b|ÚÍ] ÞÔ*¡èj?ÍÕäü£¬r¨(ZÓ¶›ݨ¥Ö&ßQµ6%µÎ…„ö4¡-Ò¡­Œ–Lhwšõ‚hëb£UÚE\, bYCbÙPª,p­™¡j€Þ—¨3DoPƒgš±¥üp”´1ÝxÇõîò%^ñWÂzžbü±ÈS(ÆuÆ ³ÀÞHZŸO4J £g ,V+ýlBÆÓ»*<=1•©ST\,ð®PkÔ¢ÐK6òmÇôª6ØÐæÚµñX–9Ç‚ š_ïn‰Ð@«©0Ud²ïáL–“öoÈ$ ¤­ ]ÇSµë¤cQ´k!˜yj!©ñ’sV³ƒ›>ñ~¤â@þiä/&ÿò—‘ßMþròW‘¿‚üßEþEäŸMþòÏ%ÿ2ô;ª|â*òx|âò,ô‰ogß ï¨åG�KùÙÇrY<Lž•²ØAžÕü\ÇY¤»®kù´ŽuüYǾõß±Eñùrñ]Ì)ÀÛø~G=?œáhm‡ ù¼_è!.=ìT‡}ù»eÏ»Ø h_¥‚ÀÅmU›ñ x¼µ'¦¦Á°:²Ø Á;ØSExÓ\vˆêÍ~·ï­ã×_š•YDVí‡xb²—ë~s€ O ôt]îMÒ-ÿFµ¥µíí©Êºª@`~#J•Ìæjèd!´ý3ŸX‹î‡>qtºí»|b˜íÛ¡Týànò‰»¿¥·›D º/úÄltŸ…ÒÅøO€mñó‰YÿŸ¸ \ÿ?ˆ•.ˆâgõb6(¶x-va„¿‘·?~NÞµèewa  ¨ýÈ›…ÞëÈÛÒÿGòFo2yïú¼=É»ãÆ1`„c´ql ècÿaòf ÷+òÎ…àÿŒ¼ƒÑûy;X=yÑ»‘¼Éè}…¼ô®8MEÌå›|+ï¡@Àÿ$E܉ûg²ûÍç¦Ó‡ã;•e©h<ÿg¡z8Brw59šña-¨}6b¿ª…ÏÙLÍ335ÐŒ£´ÿÌþgg�Îýì™&…½R(ÀжÆdó.€xÑVõ6â…’ž/±ŽÕ¡_ŽCâGÒ÷Iwo°yV"Y†JÞzü[·íÕíÀh»L~™}ׇ‚Ê„õqÂï]‚`ŸhïÖá›dWÎ ÒëÃÎ!øÄæcúzÛþ1ɭǘ”}2SÒó’Çî¯Ô¸ ï~|òB&|í´ {6=Äì·Ù¤»£¸×Ÿ>ÆF´¾‹‡cؼ‡2TjÂ3Íê L#Ê ©RejéDÔêÛØ|, º%¤BºÂQ[r ÇenðUÜvø¿8­ËG.½0ý¯³÷8|bš¡ëç2!ûoWã¥vÅ[ªÆû/´}ežai>ë Ë‹s˜.€¯4’|ÿøStãc:¿f¡ó+v¶.˜MŠïrl û¼&:;›Ï_DÄW%åü6¬à®ßE¬º•‚Þ*€ü¦e9ÁQm:ãÚªì­ÓþAèür&Óð€gCðáù8<¥ÄQø.Ïç³fuõaçµ¾N¡ó_^~&–»s¦ ìÀ°†óÈ/œ#aÝy@å9 o÷`ú<0l|Ïß«©¤DvxBÞ«±+‰sq™NR÷g/IóËû´;°@jt¨:ä9Ðþ¯ Ò÷yh¾A=Ï Êµ+d®åÔƒÚ DØ|Å×—QþÃñ]!øïFÁ¾ï°ŒèüG4 >w›œ¨½Bíbº® 9}>DÝ åã+Ô‰!ÏÎeª?<Ü›v{øjrº¸í©³šæR£þOoC«‰îèÍŒ 1]á&W…ßkŒÌù¾d…O1Ff¹*ù0ƒYÆÈ,ðyq$2³XíÆÈø¥!¯Š@ˆ³‘Yà›yc²™ÂŸ2Ffs9òžd …#³À\Žüe²•ÂÿbŒÌÏçÈ?E Û)ü¸Í™²1än¿†#'Qøncdø*G¾$9™ÂŸ0Ffs8rZr?*Ê¿ç™&S ühvœ =x˜¿×'Dbù»ÙfÍlü’§êxnöS/EpîEšiD³Ñ˜[øçö®n3(|®12 ,²q+3>eC3B9ìÏ†ÂÆ‰°À㽇÷Gp˜ÉÆ•ÆÈ,°³×9s˜ÊaÑYÛË0ø8çpN‡Ù^lŒÌsÏÃìP@OÃDXà×=‡¾ó(|³12 \Öóœ9Ì å°€èŒ4N„þ•søL‡…ÞÙþõœ9, åð.6yi5L„.´2߈àp…/6æºÎ]†ãB9œ@tú'Âm\†Û#8,¦ð6ãì±À¦s—aq(‡eDç!ãDXà½\†FpXNá7#³ÀKÎÃòP+ˆÎ¾ó aÏÃY±ÛQgÅ|5´°Aã9 ¿8[5ÚÔ¹™½„Y˜b9Ma#74{T>7¯â(„“N8¿†àäEÃi#œ#=çüã!8ÙÑpZÙÔáü)'3N «²„“Š“ ç kÅ„31§8N3áÅU!qn(Ny4œýL_ÎüãÁ9x´î|òíÔ3î ™€×Ö s:a.9œ¬}é¸:Y;×b°Ð°:õ´î¬â®CD™^„tîfõcãq>eçÜÆ'OYÍÛHCþ7´v³ý8_e€ò–µyEm¦o!¼LŸêrx˜V¾ÂÔò s8Žd„y$Tž‡¢ÕC²MÝ´ŽÒ¥“JϪT<FRYDIÝ€«Cν´È„qÙ u)Ïî~Ô5“jÂ餌ºŒÑØ­'T£&2c'©èÞÀEçnŸ|ç jÔðéN'¹F°´Ë>Ì¢'´fŸ<–£í=Z ¡ý‰ÐåT0|°MbõÉ’ B*!üØÒA„½g@èKëiÝྫྷ v³‚²X¢T?Âq%b1L$úS"塉X£%’A8¿š ±&ÒŸÞÅ¥ñÎ_B±GK$“pæ%b7L$‹Q"ÍIR´D² 'Þ(‘$ÃDBÍÿÏ M$9Z"y„£%’l˜HE]ºñÕüãD°¡%µÉÐ÷1êòÄZj÷‘ ÖŸA«8ë§/áÜ"v£âl ?‰¡í }ŒŠ³…p^!œÛOÄîÿTœm„ó�á í3ÇEég&á¸NÄîcTœFÂyÄ9'b÷1*ÎnÂ)c*¿ò„ÞVÁ œü§K´zùÄÝêšäc'BÖ$¡;`Ë’„áhÆ­–ËHÇÕ‡½h¯8&ãeÛ$Çnm6Drì•tî%Vȱ…ß|ž_ï­w%ùj^BbI©x×òRU 5ÈŽ¶í4#Ó–sªòïûén›Ùd[ÒO§ícBÞùL?ŠÌ\'¼K¹s4ŽÝÇîÙnÌÃãÁsââÚê|d‡:Äiòg:þ0™nôÖ»wȉ4ÿ“¤£"n;ÁÖ)þu‚­S|{‚­S=ÁÖ)O²uŠßŸdë9ÉÖ)²O²uŠ1'µuŠNjëU'µuŠ'Nj믟ÔÖ)êOjëNjë#NhëOhëÊImB8¥­SœJ[§¸æ”¶Nqó)mbÄ)mâÏ'´uŠ{OiësNiëß×Ö)jOië«N©ëâkè­%ï{è•Èû!zç‘·½3ØúzËÈk> ÞñäýzG’÷Rôæ’·z3É›‰Þtòþ×=®bë*øõ÷ä^;y§¡·yFïq¼ÆZ|½?’÷ ô¶‘·½Ÿ“÷ô~HÞRÌñNòæ¡w yÓÐûy/Bïsäý :ÿ“äýz‘× Ã?Ÿ¼BïLòÆ$ ï)ôN oÏ�x§´yxçÛ}±éᤦòAzÿcšIðÑ•ÁóãeR“Do†ùÖgAçùó tÒÓçl“ê2ø* |Ѧ>“H+ìÃiÏ]–mó>íƒÞ…+­–o“¡ &³·eÙþ¶ÀâE ÄØÛžâ.ó-‘°ûSþí÷á–¶Ù´œäêCçåK&ú‚ÿšheƒµá»X,š¾Ý^௟A‹;¸&î’æ7¶¨Ë†Z^‚¹ M”Ä'g=\ÃÔž�N3R ™?1Åðå¤@ ýyu~¸¾ åLRfuG™n›bâ;´$Ô§Çäü$ܘ ËkiEÕö> $*&éˆähÃ÷,ÛäüV9£AÎ 9¼Iföîq¥¥Ú-l__Î`Ù¼B.`Oö:[˜’•-b“iÀ<ó¬Ï圼¨qäœìaø ²ydSÎéÞqäÍDïˆI슳!ø ’›Òf+Û ìi¡÷3ܩ֊9å/BªëÌôȬ=žme³k½ü|”¡±QäùÚ¿·Ë;¿ŸìH•ÝJo Jyt¹ 0ò7ÈùYÐ˸WKùël›òóæ7Û<šiÇŸœŸª Û·{ &ré8õ3»qWä}b.’éÃ6z¤åA¼08µ­¹+¼oÓ´ø*’¥&Ù$¹ëew*[B׿÷ù»rÔoI Ò±!®áë8éCɱ¦áÛ8ɹF-²#{C×ì±íüý‹Î¸ÄdUŸ§kÀÿP§RiÐIë>RþrýkÒêùjöDm“£yan>Âá\ŠT1Ê!7˨d"n_ÇÝM¹Ù„žÛ_J“*€ÄR99î-¾dµcß„±)@Ê͔݅²£oSî`†–E׳¥²ÇâÜ…˜DƒÜãrºå0_ÊÍI`½~C`ït¬m·±÷*nqï¶yž %áµÞ}òðþ²¹CÊ~Ó";öªOxä7ËÃ3ð³ùM׿dë[rn¦d]'f¾u{»œ;EOIYâÍrw ²_+Kôö4¤íJµ@:H%¿ ([9¿6°x5Aõ)†�Ø­ùû!É<Û¼÷cóÏ_+»÷B>oàùÜËò©Ud Žå¯Ë¶]Ë6´Ñ« =Ê•¨^Fª®þCÔ}®Ë©~ö¡‡üB Qµ²Êîu²3Sš•&ÓUª$$g÷ å×K¹Ù2”(¾•£=2aWÕ–2fÕ]öÙ"íò‰}Ì\Qò&Ðã’™ ¾J]6/Þø#»Á[Ž9†ºð@ânÁµÉ èu`ç7.RÕ)îóÏÍÝ«eçÝ»»v—ÌTå@Qxú׫éï-ÂôÝU>PüÙ´ý:9vP l“Ýõ´FX˜j÷ÕÔ§ñY¾È>‘6b{.%“l°œ@Äê0pýà=êmÀ'€X™p?<x‘ÁGîá+È&<óˆ Iv„Z1¹rõ=ZCêƒÈ5ú†d— Ìéû =¥×Cùûß?¼OÛaaÚ†ôŒÒmf˜†ét‡h˜†ñx¯RcÒVßçYÜÝÌÙKV'j Ð?ÛQ ø×âË·ùkX3ã®gÌóUáÁPRþ|°Òñãs;[äÞÚÍÓPŽìq­>ÇA)xž1°d¶¢.HÇ)”Œ¿÷  = ZAY‡JøãR{g‚®ë´°{:Í|Søññê’jÈ{XtÞÖ™¢ % —ò%c:Ý^™,9[ÕªðÊ8[+TO^ðaGÛ̂ͱ‹¥Ô&ñËÇx˜YÙ4žn—lH¯÷ìqwŒðÍaçívåò÷c‰z·¼8Û€¹2d®'0'í’}£ ÔÝ.Õå4›jóVÒ X2ÙýÁíÚüfMÊ)TÆÝCÛó\j%¯2ñÙ#0Pr´‡wsX= ͘R=N—%³Ô~¿¨O¬ÐZ„îšP¥l,íuúêÆ«4óŸŒW5;,X"ã»”ÕJÁ_ŒOHÉà %þ×2ˆOeÇxfÜ<6üP~ÿ ‘x±à³€¬ÏѪ¾[¢ëD­š’+ÆF)Ì'ÆbÎAoÄÉŠYR3ó¶¦î”û¤òýþÒ®Qʪó1É(ÉLº…V4�j*~bÃY6ïîh³yvl ûp lR¾‰¿«{¡O\ù“:Ì­ãRUŸ\]Â6Ĺ‚‚ÄOÙ4¥X’ÍöÉ5áyîæŸ‚‹¯s|¬¶aó|;\7C¿ál Õµ`XX¨§i¢1P]5iµÏ>W2×oVÅÝìôúÐ{Ͱ#¡ÝD5ZR~+둬ìé¢öU|¿KIð3håãä‘…xüFP {Óùï{Ù^_1DÄ7ûe»ÞŒÁý\#'à;–W€Bªš7Aà“þ@|ÕëZA}ÕknêÅï:|Ðw'bköåÜ%åZäf©K¬·J•{QÔ$ìÔ&ß&(�‡™ê—‡ÐR¸S TíOé`}Jï—V‹™[’|Öw‘-Û‚YÕ¼ŸûjeŒW#ãòR`Ú4å@{ÁKnÖªø:nÓåš¡ˆ Óé=-²l~‡J2gœm»¹?«¡û±†"“Îý`XÏC¤ÉÄÔã^~÷|`ñfƒ ˜cžµù¨Ü+ÛQÁÉÃî’ø+tùmŒ|"t J™îøÀ²Çu=±í©_GfâÙ¡]|¼“MGŠÈ–ûñ$ë%WpKb µTœ ¸.–!ÛVíáµlÖSÿaL@{–ªZÎ±Š‡SÀ>öeC1µvH9V|;N½¬P6½“¶›póXÎõtŸÝ씚ѠªGû %�£<ÈíÌ›åAƒý÷ž¦qá0+ûV æ7êäçº I4 àÙYrÚ%}&ÎË3Û<™fVd9Éò+eã–8Û‚I¢{i£2®†å#9_N*Ýï9†æaB%èýž$›——d{äW¬¬Nᙘè3ZÅ?âÝûG F}Ønc*Û#ÿƃCõŠw·C’röÛøv€³^ÇÿjLÐYßþæ?Ǫ=KŠÛ·Àº`ïMÑ› &ÈPt¯N‚~|^ŒÆ{ÂDJ,n´*1@Á”¯A¼ïºxy<öÄòç¦CÉPÐl†„Û1TîjyÓD£$úÍh¿V•ßí¡|@öïaïvÅl GØ`ûï…¬`x ¶yo>¬¬”{þ•½$a…x2Û¶î"lå¬.sê°˜^ÌɵPÕª½;lT0ÖÓ�µQA3Yö+kT &Vs9KœÈ™Ú°À"kø—±÷δW#˜öóçœâòÒäpƒ‘2¸&#©t4ëu%ðs!=¾q2“,ö7ëCmä)å?(ÁJ«”(1û{ž¢’—žR÷ÑDéÂSø¸0ìCó(ŠØx’Âì¶o ‰ñ,‹ñ\8©™ŒÙGÿO-$îÁÆH ­_%£¨9!ólLAPJÂqR÷à àæc±çñ=æzâ?aì‹N²÷<¸è?8¡Ÿë‰)ö##ðíyÜJ»™É·J&ÿzßLÿ'W&)¦ µ÷ÆšT—Y|>Û¹A›.cœp¾Œ"µÕ:¶¸GÍ5›ÀÆðæÕf!8 °y0ûD[·c6m%¼‘N¿î‡¾éüÈù5œA¢a YT‘yÞúÙ³ÀþDºÊ_PÜ`¥gÖˆ5ha¢òË1ÈkŸB|ôò´ëÛ&ç±+ÀÌîìæ…Î-qA%^PU¹EpY¸ý‘$6Ž£V¶Ë±EhÿXßÎ gÛWrAK@ûFÜ~ê8¼‚ÖDN+oSdÇVS¥Ø@¶xëeG«­Î±…ÔëAlŽZ5ˆY–eGô÷|Ú¾tŽY÷Áòí‡j»Òª ¥™§–»ÉNư£MgÁYå­‘ú˜Lô^]€Þl ø3äáv|â"ý¨œô¾“dá µ¹7GÛ€Yv@˜u±qÒ^ÎÍ¢ùFòV§;?"pÝ—9œd· GÎûñÚx‹æÏÖ L[Á¨kA"fábÌ´´Ú…uxÙŸ Ä,ëFÓ•lCS8qƇ?’£J9o8 bi<Cƒ1EÅb…á `©ôh¶ ¸œÂàÇ‚>r~UÃWqRÎ]h çл¸†¡lÙÉÉ”Ub}<;ƒ§»!£OcÈ)' ßéaOhaIñÇÉ„úÖšrò¨ºä ~ßm›òk› ËtZ&Ê¥Š=ÆøP‹4Ô,õ°=öNOÓaVñ àÿüþa‘ª^†Í#€Ëa×ÑPhœ ´“,bC<ø¥ü…=ÙY-å{â‡eÅΔ|e4½T 梅³ÏÆuH)¾ŠeJr~µ'×!¸8×k»’läwm$Ïïlž­¤úÀ¬–Ívì‰âlžH/xâAPv|÷¶§Í3ÍG­ ò;ÀQkóä`”DÙY‹³´ìÁ%VÄ4Aæ¬E»ÛY?z°ÄxÓøU®*P™‘AbßcKKÄôóÅÎFb,nž:ìe±™@ixG+>ü~Mž‡X5f'´èËùš„OÆé$Ì–%;JæÅ˜ ªŽ– ²µ³z¤Ê6H. ŒÕ,JM™÷_˜ WþB¹Ç+(+ÇI <”C“Ù‚ê”KÑûbQ,T,K!jջؚÀ© ’ÕÌuPeù”“góÌŽã…8<P¬¥q!‘Gåiùî2ì0 À„‹Ñ¶ £2h°y~ ¼h©Õâü<î!¡lêçä°É‘Zl3ót“ õê¤Â-yZý#|´“G’uœGüþ(ð‡„¿8ã‡Ôjˆ•øý ñx²mÁ‡(!OÍ~u¬EåLóf’Ý?‹¿ûê¬õ‰Y?óƒÎÕt0…çL{ÜM9>Te ¹ô£ÁZ¸RcÕ'nøY›ßÀz¬x†âY1Ôxêb+Úáƒiþ æd7å æ—²�¾ðBkÊ¡Y=›ÍÐÁè·HQ@ý®¢¡XN1V’ºÙü˜³^À8ÚàQÿª“îGªt %‰zRLl&²ƒó£w†3ËÖ—kšàJ£zÉ«œË‹/£v ž$I Ê@ñ 'Yó5.n¡7µZ6ë€#Ù/ž¢\UË2è¿$ÀàN÷àðg ¾ª™Ã\T5rø ·pø}+~‡Ãk9ü ‡WrxuÝÐÒif %+¦­æ³.8ÔdSN¹ù/Ô0 9ÂN à‘5Þ?ÉŽ¬±+røŽzßV\ƒç;0q.Àu™ÁÆH ØBõÂÑ%u…Ÿ£ó%¥DšF°û0œx [˜¼8|Í“ð›¥)«ðõ.ñ°³º K­<é¶‘Õ¥»¸o¶…¾x®ï›TiSŠq™u Ø_pwW6³EЂT/´mÎ2‹ß›DÅÔ½oNI/Šƒ¿‰P: Ç\j»#Ç,6™K¥sûÛˆ£G�V˜­J—:qòB`‡¢ Ÿ 'Ð$éûê¹ßЀø«‰fK“fÚÃå…õïBižµ)'‰õÏ4ïœc&öÿ&ç£yt#´jéàÏÚ{ñwº›Ìâ&W2dÉîÝçîP>9M{;¾#a´/•ÝmM9l˜ö×°}ÕR—² í>6o†¯Rß‹Õê’#YÎ1K{ûÃê}(ô¨XóTª? gOÑÇvZÝ0³çæœVš’zq°ú´–&^úlݾé©OªÒëJ$²ˆÛ… Þ®Dé§cŸ‹_%ÊP¹MïKÉ×|(UX¥ŸÄMÒtsSv3.ÐC'„/QãSŸT© ØhÙ©žG# ÎõtsÄp¬u}|b €¾²ølzb|œßËYP“܇ùD¢²–8íÃV³¬¦Ž®ÿR Þ6˜äù‰A?_ߟæÃ-Ê@vþs˜v?&.·).-ØHM¾¬kØàÖ•ú{ñÛþæ$;¿‹qÄý_¡™›,DOömfÛf±¼¥à·Ø6‰_‚aöm=£×`Ö 6¹pFU±ÞüÔûk^þ#½7ŒßGCúáÇ\ÊMrX¨­ù¯§ýà§c~cÇï žïKÆiîb"7Œ=TXãéÕÄe“Çwád¹Z}ë[»a;^ÁH§IÄ‚„øV ¾D^«r1¿U=H Ü_æå’±L7=ôK"¦¤T`|–h½ûZß €·YªY »·L1|/ØSÐB•«©=•bZ.Vn<ÿ|WäS¾ þéÆhJ?â ¹”ÄBøKû„ ×Û•1W 0~É£Ùsœç]ßÙO\×É[—Ò#é8½E`ê¢îäËf½CÚ-“w=MâòLã¥×û¶fáîWs–ϼ@. ­h—vÉHÛj|IYrá“Wo|O‰ÒlœÊ Ú|h#±«›Dñ]×yö¸7´ÿ¨-ÌoåƒÚ@ÒF ¬üV}l÷> d…Q¶yÓtåb¬ÔAíBÄ ¨¹Iê\'ŽY»iFTOв|¡ÙiËI%»³Xú˜Gˆ¹•[@£]ÃÖ´ˆG;‹";9‡¶ì&Æ$¿¯‰Òw¶êYP“NâY™k,̦¶×Š\ìrÝz͈f±€¤Â„“Áb¨Þ®c´Y)`}È£6‰Iûï#Ó M}õál á’ õ @\ÿ•§ê [/SRSèüçP¦°ï·"…€k«2Ðc]®~JÆ·;øÓއÇû½§Ôw·žÄTÁй-¯­UpGZ,Ž6%3›- j³N Pÿ�²Iâ®=ÀL_kœ‰•^"–:õ‘͋ͽÉ– °¿ÉæqbÛ3û÷t?á¿#``_¨÷•Y•t:úÑn4¡9ATÝ[©غÇQji0À@`þi´©l ^Â&rº‡í‘y8�Øužøm—t¤áûxécyëJÜ»Øõ‰²&Æ&\+1‰&ŸÕîó|Јëy´e@³Íó6ê–šu¸§öáë{VEdmø*^Ú->Õ7Ï"æõ2›ü_ ÿü=äóN°f:‰]?ЃU…±¯c-"šCÕñô{³ÆÊâj‹Ê(®š´yé*Žš5ðyÀg³†�ãâð_ó“ø«Í¶à=´p?÷Ä7‰k´][¸Olóìf“o¶¥=»šÑ 8à³™›eq-’ì°=ò<Ž;¶nA¢}w›|I7‰ÆK5øYd–fábá"võ fç|ÌNÀÕ—õŠÔ®ìŽÏ¢~ñÀôDÞ/B(»­ ¾°¡t±~‘çÉöH_>ªûÊèl…Ÿ|mÚÝݱ/Îs¶}ÒzùéãcŸ_þ±¯KŶ­Ú¬/i®ª² ÆÌ² �½0´ï-"+$ßyȵE*°ÊŽéˆ\Ù&¾ßÐM‡é´4Æ,íß7I£¡¹%0.@ É:[üñòV”Ÿ/é}HF®¡Jâlály-6ï¥@ü<g‹Da´ûªUšc‘´Jc,Òp+$&QaH둈Í߈cð·Ñœ{ÿlGëÉÿ>-}äKJ?2‘¸C’ ûA³dí!U˜ý·²sùÊÖ®@€•€‰é~™º-©ùØÁë±—´-x«ßÜ /»¼iÀ1Û#dæw)ÿOqïeqïÿ?›Ü Q#¢,ŠJCv +Ø’„„$%.oí²I6M²q/! (² a]csÔöØÖsJÕÓÚslÅ[µÕj@ Èk¸T±bmbPƒR¨Éïó™gŸïn.pÎùý_t²Ï{æ;÷™ï\vžÙ׿ÐÜ´™\K1k:¹cÌ®ÜNÊFí}b’ÜCEex&e %|4ði"òì'Ò±³=1Á%q!úÊt"]gE¿¶€Gº!EºwtP|ƾ¼I™÷r)%M<7ŒJzean~‘Aá”·.¿0eÚû—ˆËÜòSº…nÁ Ö]}£Ñ}Ÿqž2Ø‹žÛê¯èˆ\ß´µŠ/¤†šB{ÇÊ{1n¡³Ð §úƆÊ>sñ¾OSöOY÷øGÐ8دö7{â¥>3 ¡Ÿ|ô͵E‡çõžGßq>JPDG6‚§BùIÛ¿˜ŠYq(î%ûvß(¸çîC£ÞòVW7‰‰nÓÝT0E˜NwÒü;;.ÐùMJ¯}·7.¸=øn(;”%˜:|¬ÆŸ'âe‚ïø†f§þ:m‡ù•ý¡û¢Œ„²âP%ä?´.‘TÜ5”ÏJhÊqûv¹;x&xºç£¶PvRó$%wC'¢&á½´á.Zv°#¥w^îQóæxúàeª!ºqÃ!û.sK•lÀ¦^ô%è¹Í+ˆ³Bë“©C½O´4Þ^vÈÜ2_¨(>töõt^rÉ”ÀŽüy÷?ô”xÓ`êXªsÙùe¨óV&š›^K§Ù@_n(DÎöÝæM_ÐyøÆd¤5´K ßyö}æ–¿’eib0[Ú'Áþ|]ç¾*Ü’àØpZï÷šÜtß@IG_ŽEŸ,g‚hcËè‡Ywý†ÖâÚ§õÔa»sa}í7¢ MƒÄÑÈÌìö¶Ž9Ð…a 5®4˜{°û?¾»˜‡ÅBæ@穎p¿$ÕŽ•͘~!qPÆÑyôuSé½6UC£J}XTj÷»ßŠ9D°ÃýÐZ6ª¶ .§k¶CýNO Æ1·\GS1F©è[£RsŸ¹ù/(±*- Ðúø`cBpQ|pe‚8%Z—„vÐü¾Õ+Ýþnpz˜¦…ómD‡Ì›«(Žõ ¡ÂdXuš›ò¨åÆÛ‹ Z@s—u Á’xT7fVw† §¶çÛÉ*”d‹:ç…dM†¸} ªœÆd¡‘_žªõ·Ñ<Í›NŽ’N%É¡BZ~ší°m¡i~°$1¸¶I°=_r—pH‚C ÏlÞ¼¹&%¹«·ÿPàMS°X¨p¡/áq‘¡½ŸeEKÛçD!ù;ÅÏ|ø¤Ùßinþ ¬Æû;é»áŒ%mðîx™ÙàZhðCJƒËÀÖÇwÇœF¥vt5žT+%sÓrêb¹‡cBÝ„‚ (u'(zI$KìÊyH ‘b  =Do=Ò÷tŽu¯iý]ÿ¤îM´ï(ÆNsÓ/i\VãgîáÖ…c¾Á|@v2([Ù•Ëèd™éŒ}Ú«B÷ûðbGt„½§~RFúȾ«Õü8|$§P¼ÿ¸5kŸu?ÒšõÞ“c°fFË ‹ufQG =Ѿƒ¾óÉÙ}CÇPºf`äo­¯«¡WLâveÇ]BRd#o( ò]h˜‹ß%’è»é—E Ý¾ì‡Bc‚Â}š~óÛâ[Œ£¢‡ 숴'Ë#ìÁ‡(°ž1B;ZKãv-£Q =Þ¾}íg¨P{Q§ïû×Ñ¥´PbT3í¬Šë'ýˆ‘?^v1sËG¦áû×›±‘ý«“÷¯ðÄ©eZC÷bjT‰+÷R%…–&ØÅÌ­”:bÙÞPŒå·)›hi\°ì i‡½¬Ó° çᢽ±eôÝ °b¾+b0Êøü/°ØB_ÀAEZvÈŽiîýòìKîaSvÎã‚×·®ŒÇÐŽJÄÀèc꣚ílÍŠg5kÚCúÿÝkIw¦œ¢þ´)&xóDäæ&ºµož˜‰˜›N‘†(Œ;¹=Æ¿ CO°èRQ¤!^%´4ˆý´¹i"½£é?ÜúüWTŒöãkÍͯQXP<Ít]§ì`±e‡P­&ÄZˆÑ¬?XG‹�L>Jé!¸OE”hny%V¯#5ÿRýºùŒ¹y4"{FN‚_Ò)>1<ëä Ð]òOÑ2­òËÔíŸÆcD¹öÞ3¤ùi“qcÕkSµµ“éW)þ¸?¬øç— éà餧×OÙ•-;ÙbÇ+ô³±$ˆ““Œ©¡¦—IµûFïû♋SNSü‡é™ÜgÚ¿½{j`{\(cKëü-t+òn:åÔ+çÐ}þ÷¥z¤™Di< 4©±ï1o—èR»K6ÆÓ¼rQ®EôíŸÖ}étÌcv¨_J¡K†}§þ·y=<|^›×Cll‡x×!?(f-ÔZ›^¦VÚ*?04—&—Æw¯ë£ïÔ0?ˆW£wàð ,öÆÍ Ÿ.:,•—Ô­ ÍßCB $: )Ñÿö.±‚‘û:AtoRœTõ­mšI®3‚ ‚ â©çwø¿¢‰PLô s–Ò¤';^- 6`÷êfîÙIX<m§ùÓyÝ'be¸­ùIè󥘀øO„Ji~2¦5?1ØÑÓc/ÂLˆ6·v™DM?)²:¶#\.ziÜ/?²’ƒ Á‚øîÝb)l’³¥fá±`Š>3^»§ŠHëU(Çî?AbÈV†5†ÿÐôL©j ž1 lÿtj`G\(¡­5£æ¯7Ѳpw¨4.pä{kg‡T;«AÀÔÎ*I‰Ðüap;Ûv’N7'v/À'¦YÛ±ÈD_ ,ŽÁ2·‰“#Z¢yÓe"”Îàü-ÁEÉÁ{‚÷Äwÿå ÎQëIÑu·wÓæÌu÷ž¦&ùÑ$_E“œ‚‘×üÇ}Ô$?|djÍX"äƒÇ …¨\‚õSv-írh—Í{ÌM§ÅëŽþ©¡‡ŸSåò9Êå8•KîaU.§Q.²ÿÅÝךqÍ´{©\vÈ™¹=—Êå99#>L³ž/Í›–£sÙ›7-¦½¬ZCæÓ2r×Y([é„öq”£]ÎQZ®øVàü6#ë ¤yh*cn:Ÿ¾.\ŸD?§kVë´7‹N‹)ÆgšÞû.lO¶=](’Pq¼šœtKÝ«ßÐù± Gí4…{†NâÈáðín'µ¤á®öOzwÛ7bÞÛ<|ENÕš×’Tá”×D{Û5íeÑÞž–b²¿‡æ†MbÀ?ï99û* UIqBðŽxÔ1)¯ªQÏ”ÀÎüy?'ç€&Ñt/Â˼˜<i’Ã{‰(„qg³ÐÓæMÇ¿–úëŽä'±ù$fÿ§Ì-‡©\ïH Ã2‰"{-Eö5á˜Çî×`=v’Ú©U&$Ø$?J“¥Rè>MƒòºÄîõÿÔCA›75P@q÷rûN‹ù8–f¤ÄIŸÒ}%ôi¬\B‰ Úe1¥E~†§î‡O+!w@씈g9˜uûNËåë¢ØwNSÍîoI?Òì×Üt­Ç×'¢qˆ71U¼_–kóŸûE……}Š {½O~Ÿ[˜„Õü¦|Ïl¡¯Ö%¡EÞRƒ¾ºƒJtLëºÄàñž}AvϽňw/;bE�{º/§/jŠ»WžèžtBïÖæ@ éZ+é-q6Ydœ1Þoä÷µýËxÚŸ›5Ç$¶ãn¥µj@n±››®a›s!ÑZ[õÙ;°’7oºŠîI)ù“ sÓdÚ¹Ùµ%”A¢¹™¾÷•ôÜn27Ó;)»ÑqÍ-t9‰iϼ€œ¦ÐׯÄdó¯iÞŸjL¶ïM;‘Ö*ñv¬[^¢¥$!¸.^ü¶ïžPã(ðyRu«u͆ÃzáiDZ„6}x¬Ñ¦}…z{Få™7¹Ôl+˜FêÍfQ E„šÀj§‘ÖBêAÎIp ú³ï4o¦ÇQ&›’ñLEÑ|:ѼçäœÌKÏ2³·cN&æbj.»9H…ZœŒ¼‡ŠìÆüŸæbwÇëþild–ï Ý-²ü¼Êò[ñYþ1-æ×"Ëų,çEdÙ36œå»,ß:6œå4H,åTÎ!Ñ…c1­]It"YþŲcuo¡ó“>–Û»¿åû¦?¹NÚ¾ö-í›.VG¯P~4‡Ä:ÐSŸB®½a˜¹£T[a­¹‰îØçÈnº�´-¸ÏŠñ#^¬é»äØõjx:u‰ùǃï¶9ðEð4µÖùw¼ªæSŸ¨¡«1zèBÕÐû*rH>bH¾{à´ßÔ‡.Ñs)úyp»©]_I´Î€6˜2iüz'´"z\ÇbgE<*~ÓûcäüñÀ¹_ƒ\‰,©ñ«çšª6TüØ?·>,g(B³¨©\X-ÊyšÞe›èçõ0mfTù“ðîÅ9ç»ãC PêMûé;ûE©Ûß67ŸÒög¨�JÛÚ¬¸…Ú5­2M%b‡àpØuçæ‹ÇK,íÌÍ£Õµæîv:gúQ«o\bÊîà—]ùGD#i^�½™ò­Æ»gëç:ìúæ—¹åñµÌÁz} /2"ô¸TöOIe¯k™Ñ£iaô =o¦ŸõÐWøèoDbÕ¶+÷ 1È÷Í0o~œT×s´ÓiÝߺ0íÝS']&óÓ½r è#¸“ôù…ö}X™¯ˆ£PC<ôø1è{Ñæ–ߌoÅÌGë÷kãí}æM?¥¯í? î¾6÷ fæot$s~'¹nxƒ¶äñ‘rwœL–\X¦ˆýØ”>c–üb' ÍêkÓ(}¢UVin‡z+.XÖÞBozÏà0Z_°ìéKÚˆ_ˆV³›2M††zF裄ЊdûŸÅ ¹¯˜›þ(ìôyÍ&›¸_æ•à’„ VE©Å/ÈÓ&Ó¼çÕ&Ó‡â}Šƒ­/é7H±}E›ýw$"sSzœ .I¢›öûJiù‘p±½è•`Ñ^ó¦ÝÒ™"+L«}§½æ–§éÒÚÂÄà’äÐ Ìö‘Pß´¡•t±yÓCÂ’—DžÊw}þ¡8èq3tž,EÓ†Ýbƒ¢#¸H½'ž{Tü˜)¥‹Û•ñÖ“¡1ôŠKÙÑÖRZ—£-cþΩ¿>cÚ‰N|rz×ohÞÐÑÓŽeýKìø­\Ä¥³Eh¿ríÔPî!¾Ž£ÍÊNúVmdÈ…r¢N~¨ÓÍÁâ)ææfM.æÚåŠ%¸;О|^4’Ö…› 1’ƒ Á†x¤©;…úÎÈ“ w¾¯ëбª…Þ57§ÐôË TÅõ+ñ_}m¼ï€Z’›ö´Yß)6ë;åf=Š"ˆå­£ƒûz,2üàÓ›j“ÑÖ:¿f‰íâ{}s(¬7tª%õvs ­¹I%¶ÓÚÛ“€|ëãƒ+âƒy »òä¬>‹ô–ñ½™ñ]Z·‰½ŸQœD›yhpWФ~E}[²§'>°C>‰?æóbgs\`G¢tïB>íªá¾bn¹žÎõÊåãhy>+Ì õt›7%‹{åÚ(z]to%=Y,&Óô2DëÝbE1AŽC­'<Æz‚¦ á™¿˜‚ÞwJœ·ËáµPx¯“‡×^¶¯“‹œÎ^×®¬O䢥̤_:Ñ:e¸wŠ9B¦áxçI/Ð1åžróþÖ ¢ HÑÏ¥¥{ñ”?ˆØvLmºÛóœ&¿ÌÖ5ùQëš§NFÈäáDN¥×Ò5ÛKÅ‹ Á’ÄîKOÉÏËOÒËÁBã…rwÇæ¾A*õ«>:|Bj_\X}Tàmå£ÓâÍbñü$-yH׈3…Zý Ù º~«œÌMuˆ®õn±�ø ùÂÖ…EKk#l;»ûë À›ˆ·ëõébËöÍ7ÿ=ŸQ™nþ=ѧîÿÚGõÛ³?òÌ[ø}â„®÷âh¶~ï5âhý‚øÐÒðï«êï÷N _Ð<è÷Ãf†ÊºþU„ðÝkÄ÷ñ¡˜@cÒ,viZ•ZŠÑüh(?^hDÿ¥=u¢]½F§c轜q¦~Ìjè& ß²S;霅olàsú”ê›B™Xs ó0¿¸`t kTU /~íkæÛCMÎô÷ cDˆަHZp4BEþßèóõ~+å̯A¤gS•îÏü"¸Ûm;Ðí_%û•ƒíGößåöTŸE‡ÙïPueXäz²èp÷áó¤ÙqÁÑôú]L÷‡ýüýî•+BtÚ¤4ÁübáèªÀé ‹1]gãBõôM[F¨41tžùÅê¸Àg¦æO}ŸšÆ~Øõ%ìCcl Ûx36ð…©uù8²ö¿ýk;ÍÍÔ´÷ÔŽo'R›_|3˜ŸÔóDÛÉÿ¤ŸÄûDlô%kO›H'h•ç‚Ä8qÙ‘l/¡²Dz!£(©ëqñù÷f`·?˜{,Xt—,ë dšüVyDp¯xw¼4Iœâ[(ŽÌЬÅDdºW¿ør:3øÍ›jþ޶ãÅ:?°ã²À‘Þ”ƒ­Ë§_Þó þ>üÊâMÉc¡²chÆ¡¥ ¡B ¯½­I­—QʕשÂ+ý^`}Òßj<4¿Zï›aÝ,êz2­­ËØh7…2. Í ÆM¤vÙ¨í2ù? ða›M'LåK¢½â=§u™E(÷Ø�* ¬"­®>ùþJ(1Ð.H…q=-‘çYÇâʼnÎ]é?ezˆÎŽÓ©Cßô)]ùâdgïU&-p½É7—$ÒQDºÇ~Üûý)½Á¬Ñh/É;M/ï0íÌÕÌJ‚EM¡²­íÌ“,×â£×}î}}ã0ñ5üâÔ^¼"’K®ú¶LDÿ"_UºÌÂÛ Ö Uu£'øŸ M¢Ál%­‡´„ŠSöŽö;ü ¥eñHè@Ù$lE`C²æŸ85v@£nï¹§k>ÖÃøaVÀ|¦†ÞmɆY³H¹“¹]ÝÏb3øÄüj{OªùµöžóïÚ{¦CcõL %¿wqÏ¥øøàâž ññ¾½ÇŒ )¸$®g”xúqÆ9plFkYÚok™8ÒÙZv‚^Oê§á°èD Ïä»:Ð7~è@ߨ —úb|S}±¾K}q¾É¾ßž ¾KÄùeSOß{¦ž˜zzßOé9ÍŠ?†ÓŽ8M¼oµ‚ÎßÖ'¨â—ÆSìáwßByIåôp”“)ÊÉåäp”“oL?�ßÕóÏ÷ð§÷üéy¿¶«çØ¡3õüMÿýé®TÑ(FO§2I¢Œ¾ßÅ­ÏÑ£¾?úyÜ@Gh4ü'Ä…¨OÑÿCÇ–uÑÃ-÷¾¾ë “Öõ³Ë0 þ’¾ùz”û¯ÈýëKu§!ÿ™†sPÿþëªþ{`–ÁürzÿÀÕê“Ì×Wö¼}¥ÁÙÇGY ·­©ýwýh¶Á‚ÑfIž1Ëp+ÅóÆYçÆ2çàÿk’Ó[ÿÀ‚úúæ#Í0‰0Ûæá¦þúþ{ÿ@L[fÿ@ï\äm®ôC&^=?9aÀ¼4~`ú¬M—¦>e”!Ÿ·žÅôaÈ¿Ÿ_~·`/Ì£0·WÚ‹ÉQ²ãñùÙwä'™L<?‚ÕɘN˜50½ê“Œ–2È6x8ca2iQò[Îâ÷ÿ…y’Òõ/†ÞºFÒ²90Ë`n†)Pö…0¥?ïؽe“ÿ Ô)¸ôM"ä{á¯fÍÏå'™Sx¾áñþt˜q0µK»s1eJöY|~úXÿ@;Ì“0k±š_ ³æÂ§ú©O2sñ|5ãáÌELfL”ü)š²‰ÜÁ¿xñ+žã„‰aÏç1™ö<žÉŒcÏcÅsŒ0ñìyLX_š´Ñìy{–ÿfUUUß(þiÚ“ï ?Â4¬Y Lï_¤¡çÂâ¬ë-9κ«}§Ïç¬Xc©súª\ßËYiñ¹-+Š,Un¥¢Æíõ{\–5κÊ—É~Kü>‹»ÊRëªu{Ö…ƒª©qW8}. }ÔXª<ÎZ—’×”;+n·¸ËosUøœ×RîrÕYV;=åÎÕ.K…»¦®JQ0Wz¯·Ô58kª+…×êºÕ¨kpÕùªÝušv›³Á9«ÆY·zV¼­vÖdyVûkášÛX᪗2< ¯Ïã¯ðQ>*]UÕuÕ$a™¡çy]½ Ö—Çãö¤hQyñÖ;+\²((/yEù" –e>’¦òX嬮q‰²«@!’W=V‘§ÿF™ë…¥\D‚‘õNJ7f俬Î믯w{Pr%õ.0 aY8ß”ITå¶Ô¹}FˆH¯éQ¥g¹²rˆ²]æCfŒ0³ýUU.OØ×Rë÷úP™–ÊjU-2°Æ‰l9-õžêÚj‘EÊ Õ¡Óãq®#}ãö×TŠ”¸Ë}Îê:ÕDî™/•]Y‚v¯® Ü ®§„W¸kgyýu³n«sÎZìôúr©:Y{åïçy\(š:Q43-^X!˜qÚRië={ÑEÕÊÞ[Uíª\\]w»ˆ\SmIå±¢Æéõ"Õµ©Hf*’™Zê®®ó¹ɹëdä( ÙÏÍ_½ Õ‚ d žÍg”ßÕ5îrtY îªÓü ïÅ¢ÅFùõ×W¢„‹ué[ã®<Kš6c±Qz9$7Y”51/e1¯Ué·ÈYþ:/2 ^´ÑêŠpÔw­Ã'cpRÙ *«È¸d'^Nmälqñ¼,wKë¡TP%Cæ~ÕÈiñ¹Ï)×ZTZòÂÅ5RjF,*#ygkóa•4¸ º|TŒÔÓFlCFCÕÏYXrÞÐåVçZ[€Î椎QFµ}¯«Hª®sõé_è÷söéw­§ÚwîÉŽô«ëÛ‘µÅðqûôŠ:÷ε]¤f¯[á¬ñŽs°Ÿ5PFè~Ýr©®Ë¦g‰á¶DŒÞs 3ì=5ÒûÛÿ0ºŽ'Yiåy7G¶^=¨uz.7\·œ¤CÇ¿ž¬uâÏó×UPá(ùsOFD¹(ò?LƒÔ_r>ö¿.‡!m‡iOjp¹˜°-ò¸ýõg7Œy„ôôß(µ³¶Á•ú¼ô,r|@êßúHrƒùPþË W»<¤á£ÒÕ@½ýlŠ$Âû9Ϋ†ˆëìs«pÙ žyLÇ*Ë¡OÎÏ­l#½Dú©®kpßîâs–s &"maÅ9=/¯€²¬BWÉ YÛ-fßþzKaÖÊåzŒ´@ºRׇèB–Js-ÉaÁWE‹¬€ÂiÊæÄšVì®»NMýÕÒ Ú+fÊá…‰¦eṮ—WTe¤•§§ÍÍœm³âkùlçìJg%ž\ç¾>Ââ«Æí¤U"ײÎ"º¢²Rá–zÜå5®Z¯ð,˜Ù,E0n8]¯FÊKýu*•ÚcÓô²º §õc½š/××Rù ëaÞ°:,B+¹×:‘·y)+þgëqáG¬åF@Dé¸ô$ͤîå«®óÃù\ËŠ¯w¯ë¯ÿîþ[ibK;C_RËvÄk¬ÒUªÂ½ëlkº!ã ¯ ´B¬t5Š¥t„,W–Cxd›ƒ–äç´ß±¼¨taÁRmVƒÓ3ËW[¯Íªt5Ìò®©ÕòKŠr£w5rh}KjŽ¿~_I•œÌÊœê›'j®è¬¨py½Ìƒ”’‹­Xãô8Ñf<ÞèèÖVW²8#VóÅ%j¡/kKú”s[à•^}eÆ••)Z±Qi/êº^[îÆªÁY·ŽmL̨u¢j ÞB”hV¢Q°¤UéÙ58ĤCä5•òšºØ]·z> Ùß™QˆN$µ)·j´ìf’j RÂŽZkÆêT3š>â­ÛÕÐÓ"¶hÒØj&J+èâ”}Å¢9ý>÷RÔÂ^!`%º&«[ÓgøÃŒ5ú$}(Ý3òt\c3VmðìQ‹šÌiÑ¥ˆÐõÑz¸m*·Á«T.Í'Ùë|®Ô†AùâÓ‰z•Ê¡FŸÚŒÂBjrd×ÜÕhc|Z ÚO¥Û¬A;ú25¯øKÏÖ9ðA”n„¢9‚ð1h”·ÔV{½”uÙah¸£;Óu 1^“Ú×jªËos®õ¥z݉8¹|‘’Q#;s¹ÑjµÔ×8}ÐÓµrÀOOÍHMÓ¢ U‹Zà0ö¸ªh(™U$¦Zt¹I‹ºj÷,9}`Œêˆ¶ËJˆ¶[¶6ÚõmE8Ú.ª'Úr¡¨)n+Ò»‚êsÆ;DËo*ÍerÙnwËYÇm^Bº^ã…A™àŠ^¶KfCÉg(RÎX&ši"¢ÒCËÐ* —püÊ5'•«²#d¾”ÛÍn"5Ê%/ÂE¦K9-„õwm½»JZô©CÍÛ|nÕ< d³É%Ë;d¿ƒ»ÏMyÊÛËð£I7ë-”X®9„[ê¤y„êß^²‘;ÀR#ˆ6ZïöŠï HnY©r;;ŠKªª0"QtËèOýÉ£? µ›y}¯óú\µ"w½Ëã[§Ñ|Ò])Éåy™¼ÊÂJ2ëuÒBõ²TÖËRÑ–SY+VÍÂèuR ²— »¨^&ì¢{™°ŒìeÂ*ª— »è^&,#z“ˆ(ÆAöš~„`´µÆÒ!8È^3r!m­%!m­É*HhI3R˜ud“ƒ“, 5ôˆ,«çAÙu4hâ­�†êöQ]”ŠP…¢(ìC±Q=«)â•Q:ƒTßLe}E ,õŽ>Cò`ûM6ýT]AÀFtŽÔh½‡h-­°¢fxZä¦G-¦Õƒlåt3Ò ¢ÛiÕ$§¨b^YïqÕ;*ª«¯=†˜¬húz`ÐzÐ½î ‹ª„D¢e†-E­úiWb¨åÐóQçö§ÈÏ#)†Ý—x޹FÓ·¹Æ“ ³¦Æ³ æA˜Ç`ž…Ù³æÌ—01×jÃþ;n—ÃØ˜L6ž—”Ãx`6Á<ó̳0;`öÃù&f¦ Ÿ—ÃØ`²a–”ÃxfŽwì„y æY˜0ûaŽÀ| sƒ¹Æ“ ³¦Æ³ æA˜Ç`ž…Ù³æÌ—01©ðsyªŒÏ†Ï$·iýRÍtW¼)-!.ŽÞ·¢“oS`,O÷ÐE¢ZÖyñÍ1‹&Œ*Œ*Þ·¸%¶tÜ„¸];_Ï:/îO ÇÇøÇ½¾sׄ¸¬?—M' ,0·’¿mýóÃþ³'ŒZˆõ¾ž…Xþ¤Ñ aæC&N¥c¸ôdªôl…l5/«%6gs\N`Ôúq¯gíÌÚ%‚Îÿ} i3 ~#Lâ3ýr¿yQË[b7ÇÅlg…ª…~fdçEĈ­'¾h|Ê&¦6Û yzAŒ…W¼9v9 I/#qj°“Â|®`Z “Eê7ÇµÄÆäEd!æ?ǽž£gx`œ‘'QÆT&iˆ3í…þ#QùBå¶ÄVuT8¾Þ€Eã©Njà×ò»þŸLŠô+ÒÒÉ"Ïs,"i²}<ÿíoö<6~°ÿ؆æ#o|ìZK~Þxª{ú™ò¿œ½î§ëuÿnÿ@KT[йeøzÞxYtv(íPÿÀ³\.Wäè¡qzrrÆ‹0ëa:ÞëØ2B˜”ÎG¨ýüõìéLVé¼²/ð05éFõ•ô~ÿÀmC¸Ñ‘m„[mD›Û;XKŒüïÖù1Ú*˜J˜½×ÃÌ‹Ñ:aŽÂtÁƒé…9Ó“�“““ 3Æ3fÌL˜4˜ ˜L˜ù0 `ÂäÃ,†)…9ˆ¸ñ¹Ÿ‡a–Sø¬Ág<Œ7/F¥òÆ¢èt¨ËÞGr3d†ÿ×ö±¦½tµ<yÔ{TÓ^gϯ±çÝê™ä÷]=ô©On§qëãS¬r}ä³ÌÛšeÕuŒÑY¶žçë,ÏI=u…Îò UÂt¥ÇÊ+u–g¯š®ÖYžËz%EgyfkAªÎò<Wb¢I±<ë•æ âsU˜å¹°£èlŸ/^­³x]LëLQ<Q|–ÎÖYeÌ®ó$ñy"̲×|æ ÄgÜõ:_(>-a¾H¦/ÌÉÿ«]Å“£ø’(6ýÿÌS´Tdqãwd~LÈOeYåÏs–El×y’V|¡áß„ÿV€L鶘˜ñZ-øÄy&í6ÅwƒW™MZ­â¹,~j¿ß‰âÂ(.ÓŒú2¡¾*4£¾L¨¯;4£¾M¨o'õú3¡þþE3Ú_ Ú]³IõgRù¥¾*ÁqŠé¥¨v&Oš¯{²r?•¾à3m–<]OòtÏ` Y׎1¦WS·Aþ©QRžÞ1#]¨»Óûß0þ>%t¾Áôƒì‡™ûý࣌Ç1ùgÀñŒ÷Ò{ëLþpsÿ œÈØŒ¢<Æä/'1w¸—¹gƒO0^žÎä«Á37ƒ“ÿ<…1ýô„…ñÛà>þGà™Ì½œÆø¨¼U³5í€I–÷•àRæ~xålU…à­¨¯zúÁpÿ\¹ÿ�œÁü{ÀD_Sáß ¾ŸÕw x#ãÀmŒ·‚füŒpdz ¯<ŸÅ÷.¥“À'”û生 sŸïfáMÀÃ^ÆãaA¶¦Y•üLb~š˜<ÝUº…±Œëñpñ<ô2þ9N0þ:'™´™‚ÏÓv€X}îÅC"ãñÄø’Ç Maœ�¶0ž žÎ8<ƒqx&ã%à4Æ.pc8“ñð|Æ‚0þðBÆÛÀùŒß�/fÜ.eü1ø0+¿àåÌ}†èo˜{28ž¹_CC8ãyà8Æ‹Á72.W2n�û¸¾72~|ã߃·0Þ^Ãø+ð*Ætó!–þËÁGÏw2.×0ÿ«ÀõŒÛˆK4mÑxÙ¾¾Ÿ¹ÿ¸ñÀóô‚aü7ð£Œ{ÇPÿ3x�|pž1žL”èIæN?ƒöã ð!Æ%ཌowðòßÊøp+×À¥äYõgzÛw!ëÏûÁyÿ¿Âø ¸ñLážg|)xã™àÝŒo�?Åø{à7ß ~‰ñíà&Æ÷‚·VjÚ=‰2ýmà5,ýO€3ùxÎg¼¼˜ñQðrÆÇÁ72þv¬\è<SÔUŒ“À•Œ-à>^€ë¯ KÚ«ôÁxíVpÇjƒËÁße¼\z¡>3kµ` cz_@Ÿï¯¿^ $jtíÜçŒïaðfðTÆ!ð,Æô˹Œ.gü3ð=Œþ1ã_w³ôþüãçÀMŒOK€‹ ~ œÁøOô›Œÿøádƒ÷ÿƒÅÿ.øEÆPüÌÿßÁ{wSü,¼/À]Ìÿ?Á/1÷¯©>› G*b<–4< |‚…1x&sŸ >ÈܯŸb|-xjŠÁVðtÆsÁ©Œo�§1Î/f\�^¸¼f²‘ž2°“¹ß>ÆÜàæ¾|ã%¬ý‚×2w/ø(s_ÞÈÜïÿ˜q3ø·ŒC০þ麮?0÷ïdüoà?îfüëñ´7kðÓà“Œ_ ú»Æà—Á0÷íà1Ì}í¿0~‹êƒñðó—éœÃÜ?/Ÿj¸LåÏÜ{¨ü˜ûqp-s?Eõa1Ü¿¥òeî±XRcîcÁmÌÝ þã À/0žþ3ã+Àg| -Ù¯eí<šq&ø2Æß_Å8…‘¾"ð|æ¾œÃøðL¾\ÈÜ«Á%ŒëÁ71^ .c|7xãfpãûÁ5Œ{ÿ¼žñ/À÷2~¼…ñÓà2þ¸‡õ÷?‚3ýð:ø)Æo‚}Œß/`ýã/à$ÖßÞ§ü]jðGà'Yþ¸‹…÷x:sÿŠòÏÂ? Þʧ—ú,,üQ0?b<|ˆùŸ¾‘õ‡‹ÈÉ_ NfîW€ïgœîd< laýc6ø‰iÏïeœ μÜàðÃÌ x1“/?ÂúÏ-à$ÆNð­¬=®Ï`ìw°ø(}Œ7€ûX|MàJæÿ˜µ—Á½,üŸ€·1÷­àvÆ¿`ü[ðß¿@åÃÂ{ü)sœ8Ýpߎ›i¸ï§1÷CTÌýCðræþ ø*æþx.ãàEŒ¿¯bsž¦ÝÁx,ø^Ɖà1¾ükÆ—_e|%xã™à§ƒÏ0¾œxÁYàiŒóÁéŒKÀ™ŒW€ |ãJp-ãÛÁ{À?d¼üïŒ7‚˸|ñçÑ÷lÿ<>ÕàGÁW3~œÅø?Á·2~ìdüx* ÿ5°‡¹ï·0~ ÜÆø Õã÷)Œ€_`Ü Þθ¼Ÿñ)ð!ÆýàÏ2kÚ�ãpÒ,ƒ'/e<|-ãiàyŒg€1ž^Ìxùçó5ðæ¾¼‚ñ÷À·0^ ~ˆ¥÷&p!s_~ƒ¹¯OeîuàåŒýà›ßþ>ã�øIÖï×1÷¯gü¸…ñÏÁ'˜ÿ_‚ÿ|?“ÜÁÜ_?ÊÜw€ÃøMðNÆàw¿ þã¿¿fü1Øœfð1ðtÆ_ç2>þcÖ©«ÓO¿606ƒ`|!x+ãKÁ/2ž¶²ô] žÆØÞÍäíàNÆ À_2^k5¸<™qÅÇøVð_8›¹ß¾‰ñଽ5‚}ÌÖñ÷2Þ neÜ þ9ã‡Á¿eü3p;ãÇÀŒ >Êxø+Æ/‚ûÓ/%Ø þx ã½à¯ùü<ƒ¹ÿ<‡ñGàÆ]àrÆ_€×2> þ!ãoÁ¿`7QÓžc<œÇÊ÷|ðLƃ[À§˜þ½üãTðkŒgƒg3}4<ž¯Á{Yz Á0^þŒñà)Wý×þ†¹WÏO7¸|c8‡ñàRÆ›À¡›jãµ-`ãÀéŒo¾ÑàŸ‚Ç0÷­à÷o2ø—à§?0~¼’ñKàå¿ ¾•ñà6Æ{À/1î�w2~_aðapã#àUŒ»(|ÆŸƒÛŸ�÷1>¶Üa0}%z¨ÞàÑàUŒÀ½nƒÏ·1N&÷:ƒ/—2žÞVkð5àDÆiàúƒç€;n7x>8q6¸ë6ƒóÁ[ƒ-Œ—ƒ©6øfJãU”—ÁU侯àp'Û¯ó€72nï÷|7ø)ÆMà»ßþã6ðŒ îñü(ø-Æ“? nôü[ðÆÏƒÛÿœÏ¸Ê›ñNÊ/ã½T¿Œ÷ÓWè ¢údü7ª&ô|-ü¾ý¼õmÌ™Lr÷:ú>­CÓRÇH¦ýØF¶ŸÚûÄCX³(yúþÌÇÜé+ü´F|£Àï2>ù¡Á‰àR¦Ÿhµœë#ðFæNû³~æNû¯Û˜;í¿>ÀÜiµ“¹Ó~lÒUÓ~ëFÆ´?›Ï˜öG·²ðhÕrµáNû£3¦ýÓ§™<íßncáÑ~íb&Oû±™;í×jÌö{w°ðh?•¯×h¿•ÏÇ.@y>yÐ(ßÉàFÆSÁ3þbðtp×~ƒSÀ½L>ÜÁ8ê—q&8ŸÕï àKß58<‰ñ"ð§ï¼\ÄÜ—€g3^~ñ-à=Ìÿ*ðVÆ.ðÆ·W~dp=xãð\Æw¯f¼ |ã-à1Œ�Ÿê4øað?ÿ”Ú?ã­“´ð?êÿ^uyÐÔ÷ÉäþwC~'¸“ñ^ð”£ï ï¯Qü1Øù[Tø_Q|ß1ÎK| Þ÷jå—Fî4©ï_’äÙ§G•û4ðó˜ÄI!ú~øº$yjžrÿœÚËÇÆ÷ã¹po·ëñMÒV€;?Á:Fº¯²ëúc’v7¸ñ;L߀ïbü˜ÎûÌ|‘öGpb·¦Qñw€³ø>M’ùÓËë¸ÊÎ}*ý:$Éôë<ê™^.ñé<‰Ž$}jðdðÖo žÎk óŒ "ë'\ ÷9*ýÔÿ:˜ü¸ogðBp=ã|ðŒ‹ÀùŒIXS{Ú8Þ॔Ÿƒoo›`ðÀ™ç\ >Ƹ6ŠÀÿ`¼¼›ñfðÛŒÛÀï0þwð«Œ~žñ6ðëŒÿ�þ=ã7À0~+*}ï‚3¦þ6ÿ¼Èò9q^dùÐù(£Ï?} ÷>vþé¸7Ó8ï4êÂÈóNÁ*<ªï«/Œ ï!p#úmãODÿzœ¹[`^/@ýÐì|jïçù&jã´OÁùÌÿ™ e{|l¼”ÚÏÓò< qÆEFø45˜w‘l-Ê}!8…· \z?Æpåîwü‹¦Ñ4ñCQáýÜû !ÿ2ù_£øïôýö*Ì‘”ÿ.¸o…~­Uî1èÈ[Qès_îý­˜fÈô%Ëó‰É‚'h…É‘ñ—‚¡¾£äkÁíP†÷ÅJ>øé¼ã¥Â=A»0GPéÙ ~”åÿipÒX…·—øß5íJÅ=ÿ[òÜ.ñ„‹áŽô^¨øzpý=Fø«.ŽLï’ß„ô)÷ûÁO²ø nkw’{ûÅt¾Ò¤ê?QÛN[(oœ%÷wIúd‘ ïcð6¤çRå>vrdü‰àz”ÏTå>œ†õÐ{Êÿ•Qò3Á½ÿ‰5zŒt/—þHÓþ©üûÁ‰A#¿?wæÈÛp‰Mò?EU¼{²ïæ+~o²ÔG[2Ù8O:1v¢ö …ß(Ïp“»åô”ÏjÅ©àmÍFüËÀ¥ ï)öƒ“Yù/‰Ì_¸÷7šæQám'~HÓlzû’ï·¿8Tø'Á«~bäï¢)(_”ÿEŠ—ƒÛPÞ—+vN1Î O„Ƹ <Ÿ¥ïNpÇfÖ>éðø£ý휙ž·Àiï’ÿ‚¹Ói܂ۨi ÊýB4Œ­(ÌëTxCŸéçO'Òù,¸—þ:R¹ƒüÌ(Ï:jX ð¿jw]™ž�8íçšvòÿX”û ä~'êL¹Šro õG1æt*¾)=!ÌÁ”ü@”ü˜Ë4­†•ßEàNŒ‡[•üLpi«¦] Â[îXhè‡[ÀÛÖË›™‰×‘{‚¡ ÿÿj”Ç3—EÆ?:J¾åþ¸~—¦µŒRúˆøM³«ð,èˆioí¹¼±Ù¨o/¸óÏFú÷©‘á?ÞÆôýp/øÍ$ɉ’?Láúå3pk¯gÀí7ñ%ZÞ÷ÐçT}O³D†7í+ñ­Tò3à¾u¼UôÇ(ùïG± œ¶VÓîUò­àmÅ|o´ä·m4úÃëäó1z?Eäl¡ÊSîG¢Âï&þ»¡ÏO‚W1}’< åñ–¡ÏrÀM|</g\1-2üÛɃ^8mƒ‘Þß{[ ~¼ õ?Vñ·àÒÓ´;—GS/ä«ÁÛå ã$_.½Û¿ÜÆÒ{xã£Qãç)|ÔW– ïYðÆ¹žúÂ/4Üß·¯5ò{‚Òƒþ¾@Í?¾~Ò(ûŒôÙé݆BÃ>¸÷=ù»5Ä+À¥ó‰µàÎ; ÿÿv…|?!Nåç?ÁmFÿ|܆öºTqúyâw ýüÁr>ø‚âJÒ…âøéš6•WòôÈüЂ$¢pV)y ÜÓ¾'o'No»×HïMQþËÁ1Ù)SòuÌšjÃX„Á= Üï‡{{1?Ú%ÿ xÚï”ûߣâë¯Ê×Ä9vrO€b¹õz]ß_¤M'îÔ´%Ê]›åj¤ë_f­q׺fùÖ:kjÄ[ŸåþêšÊYòöYt;C]ueµ·Þé«XC—4Tx|Õ©^M¼Yë½îFgjYy}þª*<:9ËK–:,[îp€FPn¾#oiVQ®#;wQA±°*Ìaî•n‡¼ìÓQés{¼§¿‘^H­¯qù\•©™i6Õ•Dš¸fÇQ鯭]§ÉÛ‚`oÍР® —ØÃ÷Sˆ×æó :Â6ŽðU"EÖüêz½W¼",_á÷`±ÓÉ£õšõ–ê¥cá$­Ã¯î‹0Ù‹í$c 3ÚR…)üó—àEn f! ²åA¯Ì“h˜”w!Â^¡Ì_Ÿ'OŒY¬ƒlYaDEÃ\Œ¸XPÑ–<ý¯í“0·`a ¶‡"®”v¨+¥…0¿ßÌ¡®Oq¸+ÂBºZÊÕè£KnrÔª9áѾ‚^Gýy5G½Çí£+(hqu•æp×T:¼®Õ F¨dSî÷†-úÊêJ×1ð&ÉBqã™j³••ºÉ«ÇͼÑŽr‘4Ù€dÀF.rõ †ÈMX®dAÉ@V²ä©Ëi˜«~‘ŽhËÑ·ï8øí;²K º 'gÙÊå9ù©Ö´¹V)"naë—3VúÅ‘î¶â’(÷°•q+A¤D”½¼Ì R„Ùé·D DØŠ7ü#Ý +y@¤+³S×DºsK㦦H ߓċZRÄ=E<B…òª ëT&à÷.DªTqm–°“;ØS,N!1èb.œÇZ–¸àjØÆ}}S¤êW—IE¦’ßSé"îwŠ´2¼ŒuèT ¾1J×,ú˜%R©š¨*È<5š0Í‹q¸E2;U#̆ڔÂz¨—Õ€”û”Ê «FæG´4V£ªö¢o“‰ _ÄŽA÷é°Ê-¥þl !â¾-¿~'…¢Ú©á*-Â2ááXj¢¢ˆò [©œ‡Yf<Œzi†-Da†IVHõ [¨d³ ÖäýC¼>ľ_ó1‡‹+áA¡°ì‹ˆ­´z~¥]³¥b“²êžÙýÅ>âQÝÒCÑÉGÇà›ˆ"Ù¥D©‹ÓçÚñ7Ã&þ¦ÍÅßœtúc£?Vú“AfÓášIæÐ»I…¤UHY…«Å*ƒâVáÝ*üÛ„MÊ‹pl"›Ç&|Ù„¼MÊË4‰ERsÒ…|ºOòéÂ5]„™.3"üfÉ ™!“!d2„L†™-df ™Ù"®Ù2Û"³…älï!9GÄ8GHΡ͒s¤Œs®m®g®œ+dæ ×LN¦'SÈd ×LYÈ"»± »Á.â²ËâÏ”å/+ M–tš,ü4YâiRD¯#YIªfTe©º±*IY§²2¬²6¬²:¬6U£2Uw²"­²®¬²²¬²†¬²Š¬ªv„`†|î³eÉ ›¹²Ž„}†ˆ'C–§¬YÂ"޹²äe© ù¹²mÊrò™"„LYVª•ʬ¤ »L³]ø°‹ìªhd6T V7M… ³/»ˆU6<«laV™9k†Ê°”LWý@Tºò'“­Ñ*‹Ã*sl•Ó&ëÎ&#²É*´ÉDجª—Èn"h“j“©¶ÉzµÉzµÉÙT¯S]Mõ5½³ÉPTS}LV¯MõSÕU_”Õk“Õk“%a“µl“y·É¼Ûd¹ØdØdØÒU–¡È2³É±e¨~.S"»«M¶›ì¶¶ å]&û¯Mv`›lS6Ù‘m²'Ûd#²ÍVšG†2[y—ɾm“Î&û¸Mvr›lq6ÙÙm²·Ûd“´Éþn“Ø–¦´ RtªÂ¥ål¥'e{‘ ´ÊØ­s”®”ÍFmUÞe«·Jeb•-Þ*‰Uj«lôV©K¬™JÙJÕ¼¥&±Ú•ÚU L¥ZæVéYžÖ9ª™ÊDÈbµfª´ÈTÿ’ÝÈjWiQùS©¬¨0e:eAZeÑYeÑY¥š´JÍiÍT*)9[%^Iª¾¢ò®ÂT½Q¹©¼«1HeE¦Ú’ŒÏ6W ;²ÂeYÛdYÛDD²IÈ!„MD)[…l²MÈ&!+?]Tsº/}¶ü;[ü#þÎ3Å_!/ *]”}ºˆ1]”Dz¦|¡‰jN±§‹ÒN5ž.*<]Yº¨ŒtQ÷é¢êÓE©¥‹ºJ­ ]Kºh 预3D·Î½:Ctê «œ!{¡32„ÊÈ#C(Œ ¡/2„ºÈ¥‘!r—!r—!r!ò˜!ò˜!ò˜!ò˜!ò˜RÕ'B©šœø8ÂBCB¨9¡‚Ô˜G„Ò*O(<¡î„²‰iI:KdMäLdLèªtY!¢¬E5‰ð­â¯MüME<¢kËÁUr„¥!B¿¤ i7 çêÕ×j̼!•—mmâo†¸×Kûqrë|b:Y/jëiçÕ*kjýuµÎzͱºÖ¡¹ë]uŸ«¶…ãªpTU£˜*«=š“ùêÆÔ ª¢¡d\u Z­suu…ÜH¨õû\4vÐõì®ÊÔj¯Ç™jK7Û×{Üõ©éù¯ªtãñ!»ë}^ÚäÖQEö)+Ó$Jg¶øc(M|Ì‘%ˆ&'i):2ܬ´Gƒ‚ _›çô¬ö:<εp¦š²‹Ú¢?².ÒÅßLñ×®Õ¹=.–Nš«¾ºÆ½+(0<ô!/ßŃº{Oì&^ºO\˯þ ®äUVòI^ÁKVbYª¥ê �Dz›–­@%U꛲¹Å Å.«Ü‚Õ‰6`õgcÿµÂØõøêR½šÑ7tÃò^·¾ p,[žµt¹C”“¾‰&P]W‡ÅM´u„JÓ°w^«ì9 K]««½Xwˆ Vአ_zhnš£tiINî²¥¹ŽÅÅßËZ”ëXž•½8×, ‹Ç]åq¹4ºüÐQá®uxýuZs©eVxù¹Bþ¶šV%71ô"6B–•9B8´ÞÉ]ìöøëè²Æ‘Ää®ÒpòÖxµu¾3ÞØ¿çÛ˜X–Ò‰¼°™­"Gˆ¼Tm‹~ý`:£w]íÐñÛëBw÷Ñr[m# mˆ E:Ò³ Œ²•ý`8?Ôí‡u¬·¼ëLQXEE'á*×à ‹Æ!Ò_€ôWÖP>³â*þ’*Ègy×rïƒöžõÍ,‡Kßs&ùpuG(ªáƒò–Ê-hÔ8òx[m½V΂tÈoSèÂuÚ¡Q¬D>Ükå• i#•£ØU=kÍ¢äT–ôŒŽfA]h*»´°Ê*Ö±hqIvÖbGI^Þ²ÜåºF¹„åÞ*ò&ˆGÜLzÖ„/DÂGH©Ú W4Sõˆˆ†Òá<;¬Q[úg+ùí9Ë…¶(.p”Ô•‰_ý)‰y#ê0Ñô&3‚S o*Î**È12]–”‡ 3o„Â4zY¤0êÕŒ � µCëpT»ËG*3£Öo“*Ý1=é#E0¢j÷*Õ®Ú´þ“�#ë%ùSZ½ü59‹Sªá5zá²uµåîÍ[½ºÎ9¼FSßßhUõH‰¯*RÍbËï†þ¬XS骹m²GE7üø$bt‡uŒXFø‘Å!F0‡ƒv®ëÜ#´(9{P{š#ŠêaÆ÷ºÃëˆðdÜ*sÓ9#4i ÍD0)%FÓÛ‘’›UZ³ÆUq»×_«:øbêÞk+¼ÔÂýë¿j¯þ#z¯Qg4îë"F U_A÷WÔ¯>ÑHoÔT£ ~ëèGUÄoJР2²¢e_L‰™‹±q}öÎ_¨¦z#’=JWÚ¢N*kÄä âÆë‘DãɈ•$±ò‘&Üw0_`ú}Œu^¬PΩچ?{–GbÂí¬`¡Õáqh„î¦TTííô�g«A¡ÛΞò<>Ÿ“Ë™„Þ9ñ•®j¡ú7ÌáF¾|¤(ýŸ%ÛÔ§ ?] —î2dDdËhº¢ßžµázÏ6w7 ‹&¿Žšêr‡úE瑪“,?á¢wæˆEàˆ]ب5¡/£høf_Æë¡ž=íT>Þ:5´øè·½²×ÓOaa±èÒ•±CÛ¡_lq”;½òdûúX~+íp /.}´:z�¥ß=«?ëã0~.+7çjÌ¥+>ç°ÁÒ/3ˤhÚ‚W¤,óו,³ÌNµ¦Y¹°¼­®°:ëüNÏ:‹--möÿC¡šÊë-ËÜU¾µNKJ8Åùªk0 ¹¼–ëà^ãôT{-ô;`–ÜÊjZº_/¼ΚjMQi©•ëꜵÕZªk*Î5•-uÓ»F8Ò N|ú`»¬¬xe¸õD"<.Òµ©øH­¯ñi©b!’*FäT±"Lõ¸EªÏT„jM͈D«–ºÚí3„è…±õ�·Ux¤ƒˆÍãN¿¯¦¥–£9¦"E>g¹Fá‰OTýº<¸Êý«ÃÁ ¿¤žååWƒN"/ÊÁã¬[íò†]ªëÂþEÁÈG]È»FE|Îÿèt?]I§òžW†®`¥Üé¬#Ýô<NÉ’Ý1Mæ Nœú¤#ž£”Ý=-Ì(Ã=N:ª<^ÉÑÔd¶“~MJ†nx¾JÓÄÍÕtTtãùÒ\312ýô®íípSxt‡5™UZdxôIgÿ”ÝmMf…)2}äV�3V=Ó×dè2—£r)SrÙš¼ ›L¦’ÇänbeMïÔÙ¨Æòû&Gw“Ùû‘!—¨>+™½c¢¿g-w“£wYô÷Y¢åê5£~éŒ+™¦Q†œE}6*9zGî*$s€Õ‡^Î-LŽîì&“’`È™”ù±ú¤:¦w/è}“zSdxd~ÆäèÎçÒÙ&í‰ØH9ú÷LNÜ]m7‰ó¹Ñùø/&GwLŸ€\<“ÓÃ{–ÉÑÝÓß #÷{&GïdÅ]oÒâ†{•ÉÑ»†ÈÍ"}û4ÙnHŽî¬^9KÜ`9z5ưÖn…\/ ^¥iŽ1ÚÕßô8•ÜâZ“6çJß^䟰gú·éA“¸';Z®Ì^æC&Ía,÷PK ����v©Æ@���������������com/sun/jna/sunos-amd64/PK ���4Zg?ëm½õ��ØË�)���com/sun/jna/sunos-amd64/libjnidispatch.soì½{XTU0~Áë`IQY‘YYY/”ôªI2:äP”Rv14„´¼ ^* (Æã(Z–y)R+¼ã Éëx×^323òRh–3IIVЦò­Ë>göÌ0XÏóûç÷|>ã>{ÝöZkï½öÚûœ9óºÙÚ;Ä`hfPø/Ty@Ñ®ñ/Q”3¶$XW% þ¿^iO´F%øß3O|JEHÇÿ›Ág€z:㝫újkŇ/DðÅq16%ÆWŒþ—öÓ!a¨{¨°!ÔOH©ŒðiUQ<m¸l%ê®xÆhêU‰º`WrÚs©ùfW—-D=º-—ÍD½ú&.[jxQÖ_l…å¡u\¿ÔÀõšž\ouMÏs¢~¨kîp‰†Ú‰z¸(¯¥û!_xq2—šb„¡Íýô»Añýû[´¯ý]]++r¨? ðZ7ø÷ÿÿ_þ ×…ÞcF…ëš~ŠR�×iýe1”UO*Êa( ž†q¡ÊÎPÆ<óÆ@ð3”®g¥Ë  ‡2v0ÐCÕ=§(] te‚ ¬Êz(k²e=”1Ï=”iPÖ!”í¡ë Œ…2æEE�å á ”P¾e9”ePVA¹顬BúŠré l_e(Ë¡G@Y—£(…P&¾|Pæ@yé  ƒQeG ¯*J2”¯=”åðAü ‡2Ѳ tA ¢jª¢X tM½ ¬ƒr=”1o=”iP^€²ÊH˜ .(c¡¬ƒ2 ʘw@(Ó ,²�Êr(K¡Üe9”ÕPšzÂvAÙ Êœw¥?–ïÊP–@Y‚å E©Äò}ÐË™0`ü—Ì‚v±œ þ€Ò5‡ã“ëE™e ”•X~,KâFÍGÀ‡å\àÃrè¥k>ðAYú1Ä(K>Q”Z(Ë¡TL€ÿô„²¼ lƒ²d¢Ø ÌYñÊ´Eà'(AY…õÅ/ Œ]>‰_-¿BYe”UË€Êòr eÉr˜÷Pæ¬�ZˆGi+!îBY¾ dBé‚Òe”ň_ 8(K¶@¿@¹ì»h¡ì e”‰P@ÙÊ(‡BY å«P–CY¥²CQVA å.(c <ŒtaÜ] íR”P¦~(]Ç@o(k \ eÎПˆ?ý ­ÊŽPÖ@9ʘŸe]-È‚²ê7h3 ô:þ„²ä ô”ÊYÐ ÊX(+±^2 ¬‚2ò*¸†2ÊØs`”¥P`ý<ŒS(A¹Ê´¿¡ß Ì¹ãjì%ÿX£ Š Ê´e”‘·”?¡¬‚2hI'ƒ’†´³A)€r”ï ü?¥ÊK±%âŽ3ZÜ6¼ü˜bi¸®UxD ýN�Ã¥fÀ#Ð>$µ‰|3¤WëfM µ‡=ÛÈ’[ކÿ±øXà3hÇ%Ú¾“šÙÃN æ4†Dž‡Z ú±ð©…x¶À Ó;“š=eG޹ ¡ Ueøã|­$#}ïÖÍz!IRK”·Kà_S|Ú³u††ôæ¶SYßNÜ%Êz ÿJ§7#½ÑÞìÉɡΰ7X´ÒÐAÇœ'¥BÖu¨=lRhÈdZ±ùÏ 2ßAâž-Ù~ = ´9õEoõò"’[@6êPtí!ί ÔᔬCàWÁ:P%û!Ù2ˆ¥…$aÙ§%ʼ�´`­0…H´/ÌšócÑ~À'ÂÒÚWW°ëi²ëcV ?æ–bÜ�OûAŠò ®‡…ô4‘h?à{�þN1þP÷I��°!ãíUÝ»e_î?ä¯ÚÙ@+ÒòÑ€UìÖP¿·O;CŸB)d÷Ÿ!¼öÅËö<‚í a"ÑF{S Ê\)Ú@X,ÀެƒKÆö` ‘`�Ö*‹Âƒ°h€Ý,é\�°.�{Ô¯¿ÆrwYÐbê tI~tYH×—é°ý* {è¾Ómƒ1Ø úÿ'|èç?Cymï!Óôu†M2ö›joò–èÈ.€Ž<cÈPEéÓBj7äq{ø¤fNãä°)¡¡]C¹sBOÄ@¤þ¾Wså.?} Ú3ð“ríÍ3•ìA9¥@·-—–-¯®½y ÞøU¹”ÿùØû(ô�PÀ¿ªøÍ³'9.½Äó!ÔÂV‡LÔ†1Ɉgæ+ÊíòX Ie¦ùzŒ¢ù´õ£ýhq¬WËÌFãh/ŒQ”Ç Þ±0 `–qŠ’ïÃ`žõ¥y6�¸1v¬Âœèš·‘uzEê[¨f„å8м¥(¿ér{“^³:Y¬]*Øêl¼À^ ñ9bd±¢Ü&Æ5õ?À:ìV½Í¾­›=éí·±€ï2g�Þ ø{%™‹6VkGãyÊ+sà‹›À×~ào‘ñ¼øV£–5¡sgÀW‹¹¯áÓ½:[¿ ð7Js~Àö{÷;8Nl˜#l¡O?ÛÎдIa!ÉúøÃù\ ´ý”ÒûŽ©þÞµlД�Í\¹a>Û!x'‡¾Æó“ÇXж›H)Š4vÞ‹Ãã$õކ²èÚJ±ª3Àêv·÷]ÂŽDºþ˜»«þtZ^ºW¡,›ä¯Ë4Ž�B¤›eóòt{ 0¹i:ôéŸPÆN~ö™Ó+µe”dEÃ>¢GÉåý”tí¦úú©?Àb¦^ÞO¯]}�] Ÿæ]Õ´`?Е½}yº?›ó^ær~j1�ìiÈt0öfñZ‚4‰@Óö;?È4C43Ñ Á½Мi²€æ^nÑÅ@3öIWH~œ °2€…ùôÓ\¡g?Ýžm-xoUçcÏLV2äÝžZ´ö^Gdºç4]û®¸l7ÓÏžL ™íµ§ ÐTùÓ ÖäXÈž@3i–÷Üõ•b€õѳ„׋nÏ;¸ç„½àa:§Xszétë.öЇäy¢ŠqÖS§;Þ’÷”_ûÈ›(üã¥k{Ò±øÓ©</$º.@û¡œ;Ët‰:Ý3@ç sˆÅÐKWt¥¥²H7IÌo»‹nÀG~ý2YØ›¬ÓíºH†Ï$¿˜'…&ÛÃxÁûèÐYæyc3Â"a,Á(ÿX"À^ÑÛå\6¤«øüOë?ÐÍ�ºÄÖ’~´~gâújnÎãiÇÍbØ/ç…øÐB®šû•R=¯Àñ†g�ÊJÞcÉë÷�±R Þ»€¦óJï9 ¬ÓJïÙ"Âê�Öq¥÷<aaGÄøÁ¢Ö~¥tNŸÎ�‹˜U׃ö77ÕãaÐ$MIÖ¨$J0Úÿ@¥À^o'ù”÷˜—ÙÃCSÚjÉ8Ò—ý¤°¶ë}Ú¿u³NwR&7ÂyϢ탪‘6…/Ëc@Ð÷!úБO…ArY³ô”÷®‚ÖÌ´=B¥qÓèc¾ôÆ|lϰÎ�sÈc“òÌbÿ;˜|Dë?ž¹�í>_Z'ý)Ï+ýJù/Ð&W‰<HZÿ³ûÄø!€ï«ã¦±n±‡ÄƒíïÑnÊÀ÷~¿=8䯓šÑü¬¾&b.ý^ “ùÍ‚úéO‰Ð‰!ì°áb$c{“p\U+Ê÷{þ¾ØÚ#ЇõäD|Ãqý¢,“m7;¡- 5†8Ñ>”{ÇÝAE —èúÐÈpA±! Xh(+†>kíÇB~N—ûØgrAš®@óêQ‘ Z„L‘Kâ_0¿v~ÍÞí²Þ$?M÷'êGáÑÇd:˜}ìÆþÎÐIaÚcÏlÏŸÊcÞ9Ù”‘B@ßS§¸ŸßŒ¶«^Û96Å–¬-ŸñÆoÔïGE™"ÛÑ•ïa_ ¯ áSð£œwc¿¢´G[âVM;¿™4¿ MŠÖ—l#ê…ç7Ï@G^íņ¦¯=üeH´ÅþþYQÖÈí=ŽþÌ3g”÷\ïãÚ]W5:f†¡Qa'S]Q.Ð^ŸÉ1Ü*m­^aá¡ÏÛÃ-›îí­ …AY°}‚cÃÓ^?`Œoׯ TÄÞ>!&Hg1Ê€v­¡1ŸeÀtúM_G~>ŽëHƒ2LÖƒæÉp60´LÛÂÓøÙñáJƒÒ_؆cÇ Ÿm�‹•e†<‹bB4hŽ0OÊ®2(#Qÿ§Z5‹y3¤-÷;BökyS{1O~»Ú t7Ð\¬2´1·‰Y¦Ñàß…÷SH¢ÝËÃ?mMÃ±Š·p}l%ñikž/DJpŒí˜‹âÙO”âû‡G°âøT¹V”× ]µûV¸ij)<ëA[qÏy«ÀiûäËýáx¸Cá¾û€ÅŠoûá½8\ðœ&>8~ïW¼÷ÀðìM›ûý¡_Íâº7|ú("Ç€?¼m‡ñ>UÔ†Ï#âúQQö%Žtø<.êOˆr�|ž×OÁçi…c þ e†ÂqÏü0&eÃçyø …Ï ‚f¸(GHº¿$Ê<…ÏKÆÀg|^Vè6‘ò:| Íø¼;|Šàó–€;$yE9E‚ÁÖLm—[*å >]º~Wº~_”°5P }W åV VJ±7WxjŸÂgTÇ)…÷yƒÝ÷ÖÆ{ë xüÓæÛU¢Œn„æzé:F”¡Óþn¥vV‹gzw7A÷Áç¿âº|º‹kÌ#$ºDEœiÂ_¯ ²RDÙ·\š(»Œ>þÏŠr|‹ëÌ ´ÃDù¢ ¼!#ê¹¢´‰r´(ÇJ<¯ˆr¼"îS4òW(Ê7EY,áð^0æPNøL°’ rð¶ÎÊ ©>Ëÿ¡(?å<Q~ ŸOÄuYòå?Œ»m!Š–ÞÏšÚ¸k+à1~pŒ1ȉ'ú–JÙ£¾pŒ=W@äNŒf9=Å$¤Áws]‹çÅ{– ÿ•úª;YÎ0ñ ÀûBÏšî¾z– zÍ?ø.MÎb_¸;H»§}ù_zXŽ îzëÚÙa‚€×Ù¹~»8'î-àUç¸þœ $à‘b@jkV¾€×ì⺖;Î54®gÁÛèÏh1g©“ø:×_~[¯ÁÏs][ã¾pmpð–!o­?碭¹mCØÿ|SÂëÿ˜áៅš4ø_»žpåg!WÀ_ð*á-/X¤Ágûêù•“äVáÿ¿B÷ÛEAû®¯nãßÏ®8‚·•ž_â¿®¡,§D,~CÜ"à.?ý‡‡6®Ož ýeðñSÄuíþCq9S4}–qý°€/ðªí¢=ñ˜Mµ€×­äúW‚þ¬€vikI³0¡ÏÇ‚NÀïðÈ \×ú·ÀÛ†^ð\Ïc‚^ãD˱Fkðw}á…Z»baÓIY à±+¸þµ€ÿOÀÓ„ÿµ<éBXã~ 5ŠxèO®6 .åº6¿º—ó  ¯9"ôýeðH??ðR¡§v´PÀcÄx¸NÀמ†€çÝ\‚¾|&×µyô³°Kñ‹çg5ùbÑüÙ¾™ÐGØ{TÀhÖ¸½æf޶俾B޲ˆëu>\ƒ‹ÅY»\Hr¢•ÃC}ljCÐã³'øwHÀxN¹/|·&ÿy®›„¸oƒèÿ½ ¬äº¶æ_ B.ä‹äQËïð:‘Xˆð¡$‡7.§¯ ù”ëüÅ ôyáÜ~ýhr"ï`øZÑî ¯ëà A¿AÀc…ÿoëÂN/ ˆ–ïÿ$àU÷°à‘Z\ðÄÿ0@Ûs¶ˆón9×µùx)Èú…{ŒÆàw 9%5\"’ìþÓôZ‡hcò ¯‰»Æ»Tƒ Çh¹êá òjúzí>g«æÂ^¿|æ^$ோDÿæAæ‘ ù€öÌÁã^.⃶Îð‘˜Þ%àã‚È/ô‘b#öèÇi"Ô ùVA?UÐ׈ø¬ÅóõAäïô%"nkþù-ýA_5‡ëÚ:‚÷°hÝ<ÍõP±pvðH±™ûMÐwð´Kl¤ú xâß\Ëž2 EãúdúXaï->AÀËE~²OÀ·àùèò›ë<Ѿ'H»û…ü*·µ8|^ÀcæûúçÆ–b\ùå-É^*æc¬˜×¼NŒÿ0Ñï¯ xIןð³Aò¥Z6_ ä<Ð…ˆå“îu‘žïùêùƒ€WÍòµ×ØJäó~~k'àƒüàéAôìØJøMä- ø½^"â|¢Øôð*¿|c¬€×‰§˜cüMMްKÛ‡/ÐÚ1­¿ö¶j\Ïo½k”¯œã)Æ¿Hã”Ó^¾šë"½P®l-àïû¶Û­µð§ßóè½}Ý"_9Y›Íõ*%à.—´½¿]ÀáO->”¶²Oú¸üò«JMÇ´mä.”ã ?DþqA_õ¯Z´qL hûø«Ú4¾Ÿí"è Ć>_ÀðRÑ_Ú3fý<FÄíŒêå òßmÓ¸þ‹„œA#¹.ºMÙÔývµ2co^´CÐGŠéËZ>˜=,Ã6.'+#oØH[Ü}¾õûº(Îx,ëùay¶¬Ü^Ãçåeå)i=ÒËœœþ˜9Úòpߤ>æŒþI=­æ âÎ>*/?7+#;7+KyhðèÁ™£FdäåÌxaäàŒ‡Û†ÎÊx>ËÆWgåæ 5RÉ“;Ì–ÅîÕ#{ø¨Á¶¦äô:8·)¼uÔÈç•ü‘Ç|±)²~CGåo'37k°-‹+½@¿çg¾¨ØF1@yaä°!ÃòrÛ2‡’öˆÍ6r˜MÁ½{§ôCð“:8''kHSZ¤åfåeåŽÎ²γ™ssGå*C†ç¡ddƒ8%k¬-wp¦-côàáùAZEȨ¼àŽ;†dd<”qoÏ”%Ç6”ê#òmYcYaÝïùÐÿ]ƒŠ•“52(r(3*3(›ˆ£NF‘ט#‚ÓÀ!“RÀ¤`TÃFÉûH6Ðõ—â î£a#Gz1+mx +×ë‘Ñ£† Q2²ÀU6edÖäÖ‡BPQyi¹£lY™6èv°ê…9ÊpÝ"’1ê¹�«ŒÈè¦Òsœ­ÉéôX8jŒeðÈ!ób›”2RW_3ñ²ƒå!ð,ú!'7+G›àJFë#=“¬ôîÝÏÜ_ M;µ7ÍfP—FA.úõò­'7ѯ`Pò¨ü熓Í%Ë7÷ÌÏÎÎÊÕ¬pÖÈÑÞþbHÓ²0NØr‡A@ü…‹‡S2™>¬ >­AÅÞM1îÚ(i‚È;2’Ÿ|8)5¥×?˜½Dב‰9ÿ´Ã{7íòÔ¬£rÇ)y¶Üá22†z®)Ÿy{ýŽé¹@¦)SjdÓfc�oO €Ñ¢w2›Ž@yÃ^Εíþ€!ø<†ð;¤ß¸Ï®`˜6x8ˆèOìJÞ°çGÌžs7;T³eûFØ{ï‘æÕ°l™C‡då4=˜¥Ì ¾lQœÓÕŒü‘ÃÆÂšî?“3šô•·½à£†óƒÔ,ÛÐQ"32²rsGŽºü(ëÕÄ(óNZpOÜ&b‹Ö%q´ºSFŒÜ‘D†) ,E"ž5¡IRZJ¯¡Y™/æåsÞŠ3~LfŽ=™I2–­<eXøW›ÐÞÃ@#€Ÿ™3N¶ù™¶¦”@UýÒl`™9¸‰ec®W8F8\‚.50àsΣ§zÔ&çÊ™O6yÃÛd†ÀŠáJÔdÿ8½|&Žœªæsªš7./sÔÈì`]S Š>8›Î&PÿGxí–]Á3 ð#›®Ï_vÍÊÓ×™/ÂÌ‚. lä2Ê55½®è-/y”óùT!@‰1ú„Ш©I˜’œwµ„áú”éßTøb–Ç1ïºlwõCFäÄ9OÖyG=ÍòÌËejy—Ûx}†©sÆðaÏeŒ›—&:Ç8:4‹ÖmÎX/-45ãa1£Þ'Cüü':÷AÂBMnÞH±à\Þ ô¬…£Æô÷ðà°"g ÉÒ‚:è èŠìQÏ Æí®ÞbãÃf=ϱíqÚ–ü“ "##x ¾ 3ìÝF‰ðó[øö…ƒŸ‡ÔaÈ`Ûà MæÔÚP »2ïÎuwœÒ/ýá'2âî¾çíJ€bïî¦]üWùÿwÿ”0zwƒvÿúô¸}¡øäÙ6õ¶„aJ¨rQÔßï„õ0¥™‰ëïQݨ˜L^‰ñ¿à†€+üâS †16ÂiøGx£ÔHvjWa>p­ õ«ûózuõoͨ‚â‚Ûêã;EhÌXÏò9—vžX6„ëÅ~ðr/ñƒW ø ?xñ`®—úÃ3¹^æ/_ÀËýå x¥Ü%à.?ø.ßå¯ð*?xµ€WûÁk¼Æñ×Ý~ðH¯óƒG x½<FÀ• _x'ðƒÇ x¤¼«€GûÁ<ÆnðN~ð4õƒð®~ðAžè*à?xŽ€§ùÁÇ ø�?x€òƒ øP?x‰€çøÁgˆq8Ö^#è üà‘‚¾Ø^&à%þò…œ~ðjmüûÁw ú2?¸"àå~ð:!§Ò^/à1ƒüü|‘ËN~ð.ð|ïê¯Ï$úÁôiAèøÁïôCƒÐçøÁ“}Aý‹ýà©‚~¬x®Ÿ_ÖžË#z þ±Ÿ!ÁåW•Jðd ûŒ.?[ØU‚¿%Á%x¢·HpùÑ‘4 ÞO‚àò‡±\~>±@‚7—àõ¼ƒè…ËÙY' ~•àã$xW þ² Ö /”àÂÏx[Yî¯ .÷W™—àåüv ^)Ás$¸K‚ËÏ1ï’ॼJ‚§Iðj ~«¯‘àÏHp·ÿ@‚×Iðl ^/Á}žï}Æ HGHð»$x¤\‚GKpùY× þ‚ï$ÁåçyIð| >T‚¿'Ás$¸ü}œb þ¼/‘àÏJð\~†¹T‚Ëß?(“à½%x¹ï$Á+%ø0 î’àó$ø. n’àU|ޝ–àS%xŸ(ÁÝ|®¯“à%¸2Ð -#$øL )Ác$x´¿R‚'Jpùû ~•O“à6 >@‚?)ÁIpùùõ¡\~Þ?G‚çIð±<W‚Hði¼X‚•à%|¶Ÿ!ÁåçóK%¸ü½•2 þˆ/—àŸJðJ î”à. ~ß%ÁûKð* .?û^-Áí¼F‚ËßûqKðû$xO•àõü ®<ë…¿"#$x7 )ÁŸ–àÑ|¤‘àÏIðNü +ÁHðD þºoˆ_Û<Di¸e=ü_ ·l+1žšøã:.ñž*ªŽuæqyñ˜zxÊ©Žò†âã!žRª¯À:qO Õb—QOÕçbÕòäP}&Ö1¤zQ}Ö1ð¤Q}"Öñ±VO"ÕßÀ:>F뉥úËXÇ®öÄP=ëøtŽ'’ê/`«ó(Tëê<u—°þÖ#É~ª?†õ¶d?ÕÂúdÿ%]þ•dÿ%]~;²ÿ’.?Šì÷â¯"û½ø«É~oûÑd¿·ýkÈ~oû×’ýTï‰õëÈ~ªwÇz{²Ÿê÷`ýz²Ÿêw`ý²Ÿê°~#Ùë×b=†ì§úX¿‰ì§z ¬w û©‚õ›É~o½#ÙOõóP¿…ì§úi¬ßJöSý¬ßFö{ù;‘ý^þÛÉ~/ÿd¿—ÿN²Ÿêǰޙì§úA¬ßEöS}Öï&û©þ9ÖÿCö_ ñŒõX²Ÿêk±GöS}Öï!û©¾ë÷’ýTŸ‹õ.d?Õgb=žì§ú4¬ßGöS}"ÖÿKöSý ¬w%û©þ2Ö»‘ýTÏÅzw²Ÿê/`ý~²ŸêÏa½ÙOõ§°ž@öSý1¬?@öSý!¬?HöÿMã ë‰d?Õ»c=‰ì§ú=XïIöSý¬÷"û©ÞëÉd?կź™ì§úXïMö{ë}È~¨kq)½ a@ ˆ2ÇOIýûå²8¯ÿ †ÕIñâ{Özû–͇Zì' –nÕy7˜nT,v—Áâ0Þؤ†vÛ€ðZ¸lîO{‘0ikeq&„-ohp› ÁÆ:hx:é™[³³ï6ÝÈÙ}ºÅñ´ÝÏb¯�ÆUÄeqÆÿˆÍ;¶A°‹Ûç>r¾¡з4GôuÐÚl ´¹l&‹}[еÌÓj°úôÀ­%ÚÈÿÉâXgudÕC‘5Üâø;Åñ§E*�J:ALEI­-*©Yõ»·¢}jK«ãg‹ã „tSñD5êt !‘Ù,jŠÙÝP#Ú«†öj !«c„Ûêȯƒ­Žñ¨A"iîJq|au˜w8OKTww….Sáÿl¦eœ 0á­´†DóEÀ¶ÓTøDˆ¢)¨Fýì@çj˜ZL*¶Lrì`Ô[¾(³³c3° nŸÕq e–à—,öñ»”Ç[[2.4*ÀÝ—Å’ZtÆv}jѯ¶°TG>º[8¸¬Žî©™ÇS ?[`:«fÕXU°TÍŽfkýËkÓR7D7t¦dèÝ-7t¤A2´mpC?hÑ”¡fÃñ†’]’­¦Â›`Fxí`{=-jº±À«÷³-šÖûÍë]¡HzkT_Vï*½‡]’õŽz÷»ä§÷ÒæþzÛzk:kÞ¨ÎEøàjv‰9î µf*Œ¡¦`„¤U-»„å? ßTÔAŸ‰ØÂÙ¯× ÕÀV¨{ìùæA=¶*¢)%m>ÒPâöqÙ}e—u.Ã5Ëtc¢·—¾ŽÚfâ?h³Ú§Í-ä6£E›‹ Ê>ÑøÈy•¼.tz$¸N‹Ã/£ShCINwûè#tо ù{nr¸¦ŒoçEIŠÞhçåèJ¯ ªôÙf—WºÎGéqËJÇ ¥3J£´è)èGOþßúøÇø‹±¯9î8Æ^ ¼)Ž‹•!þ¦YÔ÷‘2Åq6ÅqÀâH­\‹÷1,Îü]ؘ•[äJ2½³Ív5)GÇÁ(oO&ßµ1’&[x¾KLñ>…1 k%ÆxužûAàðÇV´ô{ÀYË!ú*è]«:°<ÿI$Huüžê¨·:<à©ÕÍÐËßÇú꤆inÑ*LNl²8ÍEÇó›kwj< Ó:fwÍKùAQÖb¾µ‘^ïà,ÆÞu¿óGCCŠjŽÌ¶m¡ØfYÔȸ3VGƒ{ÀONÂùnìÆüÓ¾þ»ýùûm(ôøclÎô¾»?}G¤ØKïìs&Ý^©ÄO£Cn…qàL¢¸ÛÿÁãR¬§°ˆfÕñzŠ‹h ¸–ÔÇ7 %ÅõĘL‹¥¹ÍvŸ³:~iHËñVÕ,\´ÀM J-ÀçàaÝö®WˆÉªC,,c%é0èIQYnÖ�FT¾ÐÂ}õ¤"æ¸_y:@Ó¢SJb±ó{{•+�ƒfC’ó5ƒ%³ÁÝç48ß±p•MJ†y&ÕáÆq ‰0â{íHŽ—M¤¯BXnPú; uóA]™À⌺çñ½°püàöü]º3›þäü ?²Æ‘^¥FmUpŽæƒ©ã«©Ç“暢}¼:¨é5•øžñunÈu&œÇÿM“ûàд»n°Økê,·o³:‡t4¥Ø×¥7âÛ}Tàz-W©ÕjúñTGEͯ‚1]mQ ŽïJ,`iѡ׮‹s™n„¬6É´¦U2L%·£Õ˜kvÛúz-¬FnKæ×gX‰Åav«æã†j‡ù84p;”5PvÛjzd` µøútÕøCƒ§f´«FÕl,jüÏp¹ ÷²îãu`â �7»î¯à̪£D¼÷4&ýŒ0ñ!�I!ýý�ØÜ<a\èm}� hxºËTá*r9ŒÐT¸žb;XìŒúüWêšï p? Äž0% ?-™[-—g‘—rS¾†”·!å-@)õßåÇgRŽÏãÚøÜî7>ÐÈú­‘ñiÙ|"Ä=ï¨ÂSjPÜn‹c‡ÙòÁå+«$,p¯a«mRþ&ØšÀ[buœ�ÝÜã¡•“{ý8ÀúïkÉú‚Z·"Y…ÿøm4ßþKäÛ±1ñuú 3¬Hà•Œ°¿Ñr”D!Š¥$ÇN^ Ó ôê³iÙYœ¢†?ÙšFc®”‡]¥ËHªÑÄäÆHC@ù2æ -LG¾Áȃ2›~ Ö2Ú¨Íq›Šu‚²Æ%܈Ψâ“äòN’Ëͱg°–ü #ïŒJŸx‘_£Æ¿}Á 8Rk1\`�xá.:c*ª£xáŒæ¦âÅ}/Ü/r„ã\nõö·ša µÆ°Å’¹Om75;ÉÑ.Yćã[jc þè‚j®ÅhQ â1Z@ôpS´pS{¯¿§—ÿ-E‹Öë°¶›¢EåßZ´¨¨¥h±ìo-Z|\ë-î:§E •0ñíÏy£E°¹óžPø7E‹,åÔúF‹Nc´Øè³=Ô%‹ pß\«G ·ˆK¼”n¦LCÊs'µhá7_úÁD±L¨…äR±Ÿ7Øž¡¯‚å&Ûχ˜ ñý;IŽ*‹sžÒÄ%(Ññ{œk#­ßÇO"t:#ã¯ñ𼜠ÐJZæ'lC±°׿3ªù·XlÀS/IÐ,!è" Úp€™Ê2Hʦõ�8AÔh #v!ïd¥w<Æ&«ÆfçÑ«ã«`hV§iÙiÆ÷„@®ݺE¡Ä²Z±½T0þKL§ªM…×£*1Øhf},݆÷Š_(àY[M…S 8!Õzí:yýj7!ÛÑ®»¤nÛh]‚qÌ‚¹]„ű³aj´‘ÌÁUÊ=䩯þçxpáûdÔ„g±Ö@ƒëésÚàzò\Ó—…�°æÙ·ÅÒ�Ã|οûŒ6Ænû…œWyÆ;ÆZ·ÛDð„˜s4Æ jõ‹ïÛVc¬–GtwT×4rú@áÞä‘ó8Žœ÷•¤Dz„ÎdLâC,ŽoJ ŒOÉI7ÔÔ*H•!ÑWÍÕ(Ѫ6OvMj˜Z€9;8ÏyÔ¨/ Y°q/V”Û Á·¾+Žm ¿È‹ÜH%-Û×Ö“ç w€®žGt…ÙO gý ·A…ßí¼§ÑÕ°hžû8˜ ?áäò,µ WEw ìi3(Ms×…†oô|3¼ÒÙÜrJ÷Ôÿ')?Ðóü_B[ïBÙû'òÆ?ñ<ºx&ÇR/éôbmÒÛk#ܹхWB/T1ð(ÝŒûŒ7¸¢V ”NÑ’jJ,Egl7Ó‘Õá,ußg±û j(>'aò åVš§çmEgè¼m¿-Áí>¾q‘ÙGAZ$Kk{–ÔÐÎÒ’>C­jII(ŠßÔpŒä{çäxfZóD‹J<¥¶ÿ’m?ßbLk‹ÓØg/"ö©a%l ÌwO7µ¥�}–ƒ’6<N›7Þ£Ù®I¦¾‘vÒù–¦¾oggj,ù_蘗“´ú)Y®#Ÿñc{WÇ<¤±õÔ¦¢|^Ù ΗÕTô„Œ½Ncïå%¸O&ñg–±µ ö~^‚³—$‚¯ÿòcÿNÆnÐØS¼•2Á|ö™2v²Æþ—`œLïÏþ´ŒÍÐØ{{ ºË½ýÙÛËØX=ÙKð÷E‰àjö#ÚST»? ÚñHÚ€Bëå£~•Á= æk~M²ÿ´&ɾ=ÏóñFýïƤ 8¨MEv]òy_SÑPuÖ•ª£jOû¡îÑQ)êôÅž¶}öþó™ÏÝwoh°×ç­Åû¶ðØ}öú;lí mŽÐåóßß|µ ˜„âŽùáðgþ*Æ}â‡ÃÛ¡ù3w…î ò×X=ËýÜo"ü�¿Â%á»êø™~øv„Öñoœöqß5ûÎî³këÁI"Å: Š:s#Ý#h¨ñ‰G_¯c"»)çh8Êy`>ò("êøÃ§)s¬„PÌ¢ö²(üQÕh{›e¥4<Äñ€ómé@ÞÿZaOéø=‰³=5¾ÿï($*daûSQº%ƒëÊŽ§zòŽáAÁâ\˜Îž?Èòocݾ± %õ!~Âna" &öÃýÿ]iñÁ}I~•©ðsZUÀ{_Ú#êHíg¡€üƒ”ÆÜ“-Ì5²é¼-ßI¢û³Ÿ ~—©váJ²õÁ)$•ûLEãÉ x‡‰-j´û¦£8D`xÃNÒqÐÝá(å§èðájmÿ„÷ÿGpÿ_ÓЬ¶~ˆdî6mf™Õ,3jß)T5‚r¥†©|H9aW¼ÏB>«ÕòÁ«k(,«õæƒ~ƒä¯áÊ#OQ>ø;‚.üà›® ©èYݞΠädm¼¥Wón s2‹:pßPC&º9_êDޝý¾öôlàþVã'ü¦e¶¯ÿ@™íØß´Ìv$�<8•ð|ñ;7>ö?EY£øþù=PJ÷eÒ1ËÄ^7¦v)Ž œìå‹­þ‡H›‹3¿Z;¥sÛýdËøj:·5‹sÔ˜¢®êÈLjáúym5d~œ´!Päqd¦÷V?åa~¹Ÿ«Gݤ¹g7lºÝ7ÿÀ›5H„ ¢#õ"ˆoþNe“6‰ð´b‡m2ÒZÜ$¾Ä €žÑ>ç­ïŸ ÿu‡¾6Eóš(\¸ü¸ðéïyjÒI'¥ã®iBm'Oá;Í{Hšcõ.\ wš+¹º×â  c:>Äaq8ë/aêYTE…ÓÅÄè…SálÒQ†ÍMùâ%s:V’Õ%at¾öÍ Ô½íƒ_óI€Å~¬ŽN,·ï·8ûc¹ ,N+][œÉP‘Å~ò¶éøÔ”U€sѯ ž›€EÕxCl2>� i;Ô#0óüÒìøÍ¢ÎA{R?ì'úgó¯z /Ág{aF$9ÌèÙ/6€¿!‘�«£S’}‡!ž="ž3Âb'hDòý«ÉZÚ,€H†S]]OЀġFýä)ŽÁµR{töpSQ.­,QýÃÚá8’ôŒ�=“ŠIÉ{4%§³’wÐÎÍ;× ÁN~1®f»=wÉã(êÜÉÆZ¾0Ë$¢¡åláŸ?‰¦,ñvÏ!º™LòÞo\ß R+R¨HôøÍãã5‰ºŽŽK"žD kLž©p k¤c Èôt¥û•ª®ý(þNãnN$sõƒy¾±€V<Ü0µ:DÐø8êÓ+!ª¸Tóž"×kñ0bp¨YÕ%8pÌqÇ…}ý"âv[Ô{­ŽSIöm††~‘@o±oNîVó:ìnÌ•øPA;¼]nQù0Eͯt9HFÕ°ŸO˜ôlRÆÀ­º¿~iÔ¾kiXEõ’¬Ç%;…Öÿ¸ïðÉ߬jÍ ÕYEG6ãKTš1•r‚Úíœñ_} Sw‹û#�b¦x êõ+!:2õ†3EèÌ0SÑtz�ÁYM’§»„dè5ŒîPpÌ%åÛÜħ»iù» 6g?  ë.:ñ/9Ò]ÉêõV7®~‡L…o’Ìød·¶xôüŽ�÷»µÅ#�žŒKÚ}˨—<Î'¾O•Ù(¶_“È#ñ¼GŒD|%(x´Ãåt’©­Ùo âS<Ù°Þnh5ðJ£È½»Zræt¦Šÿsh<ª: Ó¡áq’f±ÜöÒcåoÛú´*¤Û QŸžð·ÅT¤êIöûØÂ)EÀÜ«éÐ€Æ T<L}Ç‘1<EEhHŠýA貫°5}¢<$;~—æJÍ·ˆh=nö`*ÍÏ—â>²hl|5Š¥:ó¨_õTœ'׎kyK Œ¯ûÑ›·$CÐr÷!x€Ÿ)o醠äo}ó– ?aÞò1Þ~þƒn¸‚º!ÈØägÜ{¿%ÒP¨s>uQ6¹å7>±¡î�Ù›"Û{;7ö1j¨®Æ>W#h1o‰Ù «Ú n÷”c⣚ž?´{ðQ{é¬Eï¬[N\îrc¿r#ŒÄûxœX9ç Mó5u0¨é{€œÖ–ÌÝ–Ì}°Ò&Zœ­Rx¥µî°:“wDBí�4áN?$îê˜S!¾Ë $Ù]‡y'úœ?¤3ªzª¶„L÷¹' ìý)�Sá꺨ëCl¹=¬¶‘vwÏѦ7ÉŸû€I?"Y­_ûÁ éžâ0y=–€ VGǽޚő¼7cmæ.|  µÅÑo¤g`=zÍØ ¤¦:²üF]b=õâÿ*i…·d†M°vÁ†ÉiLtQDÚ½Ù‘< çhH†ïC”ñ ¡òevyŽŸ£NôžèUâ'jÓêáÊs]ƒ¾JÇã;ž¿/ùákt<>•Ak¨¾ZÇã“$žuþx·ŽÇ§[<³üñu: ð¼âߥãñA(ÏÓþx—ŽÇÃ<÷_’ö>û7Úº]q¼Ñ­Û‹ûiol?™lªØÂ›Ú6?R&Üp,Ønò–ýZ~ÑäþÏÝèªÊÖm?ÈgŸg¾†T6Z<#(ŸW^/´Lq|“âØkvì´e'3èìÙÚ}.Y Í#5– ݘä}“Os!d’¥`ŒâÌ89WÈÙIÏ•Ÿ|Û›¯Ýިлñ<è˜o¦HBñÞÓF’êIiðæi†Æää?˜o6ºvpûI|‘Z+ø,H稑ã¸Fs2í©ö®×ƒùf“Àg‹±˜61Ó2Î÷0¹õÌó>×F)§gÚ%}ÿ¯F=Ù˜v¶{c:æ› ’f'QëÔF_”Ë9§çAíQí5µh›Ú¦qìÆ*MbCjm—¾hïÿ![Åyûæïp_z®¿§-ÎJ…N†ÜI_a@; ‰§<ðâÎèCÏóå— dÜs|¶~40wôôfÜ5¾Y§žNŒ="a1?âÄÈÓâ¢vÂ}oUcÚÐ M˜*:«Š4Ï® Òó=tsæ ¬­øÔI½Õ±Bp­„†ÜWGM9Ja¡î9,¨@­¦×™qÕ…™êØáî µy§ÏáûF”:ãß>¤e¿ï¥ÐþÆ!o¶q�hÝß<á—#”müAöúf“h³R{Ψf{Hpnç^<äyK49†V€ø‚#ZûÚ^Œ9¢å°#ö¢ª´¯Iǃ£º¸3îkHÆ$#–jì&¤ÝÒN–KÏ·õKvlŸuâã-ðY:„®Ôjúø¼Çwߣ›´–S«¡‰±r"íwÛ/qǼ­ÏÕ¸ï073—ýôâAÍO»¿ ?=uÐë§¥@ë^Nð„-‡ÉOó´ô _?eÐÖŸtF}ÿ9 þ wÖhc†hr4ûiÀaÍOA€Gk~2áÕUÌ{<_þòìAY"½­› ©øu”“eÂ_ößîH,¸”2:œnP%âb~×rEI4Uœ±dv)·êðìàŽ µ~'ñ yÿnÚØtZw˜N ÿ<â{ë ñk¿ˆñ‡ñ§M‘!@p æ¡£Y$ 3Ô†£Â®ð?˜n.Óåé)—/)>®bsÏ0Õx¿æJh}B‚¾Lб}7!¾%ã»âq[¿þWCÐnvîôÍDpÀÿ~kŠã 3d‚ô”È·xÃ`1tV’©b/?:dÚXç^¸Ç÷nà¤ëEwÅÝÐbœíx¨†7)óëw'ùœgûŸg¹©ÏB,ö-‘û¸x¢EGþŽôRËNsw)¬¦fzÿ¼~Ð¥Ngª ô”Œ8Ѳ8œi-²P1½+#é•Û¦B¼‹¡¦„­‹äÄöŒeóÑP‹3éËÙ?,ŽÏ-›k¼§V5u:–ÁÀ-sßó9mQ2r©„]–±ËÝçvÓÆ†Õ¤0õ¥0Hò·Ópú±al0Wš ñGø¬ÎøAË%iæt‚¡AÉŽ-jÒ�5lΜ/ÉZ¬ÎGØ-ífÛ_nÑÆT8:Á^ez ß?œ’ö¦gŽGs©eÆ% ÌΤN©= "OÇC0÷ŠÏñVÑàb¬š _nŽ‚7 KaÄív„ÁM{[å>P“ Ÿ «Æ“oµOÄCjX½¥[„霖Ä͔ЧjŸÔR´‡{Á9Ÿók’ŠG4Iöí†änƒÃLo5\Ò=©& ²×·56´`û EàEÄÃ| hÕ·ø©h¾A—UðES~{N×%µTDê˜D÷o_é俇+JA±ïèX¤¥ø–Ÿ ™C)Ê‚µ}¦¢¥Þ¶ìãE™Þ¬ò {X€ˆ¾'ƒ�» «ÕyË7K`qÏQmïñÆcÒÔvo¤d¶{Çj8†·Ñ1ÿB§µÛb±wWl÷@­Übï±E±ÝfUq˜á£Ë©e›D§vÙs®K‹¿¬×VC·'ÃF§ :˜“¢Ü2–tDçN/2Z“ºláï³€Ú- 2ƬrÍ.íÀl—.!ßå¾wg#fša _Ww"x2Å}gü‚¯µ,œxãg|í]Áj¿åêÔZÁ”´‚EPíßlþ7ø|L ~ªý6ÚÞI� §#}—5óê­ÂÇøÀêío´ElêLüF[Ä vèƒúó-ü¡úè 9b1ž·+±ªÄ:ÄýsûÇT4²$O·‹>ãç›ì=ª¹ŠÓ$Ö=l'åxš¡×èÝ©«#&[2ž‹? 푞L­`3v…¥[[Ó÷Ò:‰ï0°žB&Šº¯c7Š‡ÎŽõ&ézÛ8¿Ût§~ƒ×¡ž¬ úþcµ6—)““´}X(ƒb[}r-js÷|ð¥ç¶ Ò¹uP8v°?Îýíë2Y/já ‹ãÖÕšÓøk1WZ ÈèØ“E§—q3°Êv9·OÛ\Ó)3ž78 Ç¿m¯S@{ÁÛã;3ÂÛÞ5ÿ¶½˜€ö"ƒ·Ç‡ÎHo{ ÎÿËö¢Ú‹Þv8£½íuû·íE´¼=>üpÆxÛÛ}î_¶Ð^§àíña³“·½'ÿm{J@{±ÁÛãÃg¬·=O}ÓñaÁ>:4S8®ï ;È´°Zx¯õ˜0£^Ηƒë˜.ÕnÃVMY"I1ìÔQ?oæ)Û»Þ»ÏoBž>Õ—nñé ¦±§ÎúŨë¿6(A˜V|ËLkÏúž‡7¢ÇYÉ¿°_Ÿ°Y;enIGsñ)óÇec¢¢2OtÑbõã:jÐÀ]žƒg5?€œn›µókÏ›g½þ<)óZïƒÕRþH½xbÏò÷÷îñL;ãí/{ÔE š$ΰû|E©xAîÂÅáµj,ÙˬÇ@’ç':kÃx¬FtQ¬ûîÍæûœ%,kèD"íÁAšZç u·ÙL‡õ”_ÀŠ<+Éöù6ñèPlæ„‹›©9#®ö#¤Ç Ý‹7ù´°ÔËaÕ8žŒ´ªƒ‡ZÕac±;þÞ¤K4•ŸItœvÿéÃæ%ïàó7¶þ¨"ŸY¿DŸáw¶pÛ’m“çDj­¥hß×°!/�™žög.o3îÌbÝG7²Õ¿üåßqû¤>xAm “ˆÒÑH„»5ÿQsþODl¾æ Þx¿}¦ž—4ì ”®{Ë–('ÆûõÚý|¼v´ûγ˜º‚L­¤øâS;ðΡ(«á<j¸h G£¢‰tWÊøvæbQízs1OÍŸÞçCì¿ô”Ž˜ Îdž”ÒµS{¨U;GpSæá)ë>)ý¢ùd<·ÃK‹Ï§|%×­ÚóW7ï¡þϤJŽªßØÌóÍ—ê»ýêÃüêüêƒýêÉ–çC ÎEŽ0mÜç)Þ¨=…ÿuÍ»b°yðù�`ß*Ÿ×p|NGlxîCÞxn; Ygj»æEѺÝ[}ö×þô»5úòFè5r«šÜ1"ú6Çy½qF¥, ó îБžôuôõ„&Û›ªµ—‚N›"©Á&è‡iô‘@ÿÇåé=Ûˆ¾Þ]sï¨ÑÆMést›fÿáä¯G5} Óù¤½¦ŠäŽ‘ü"*2¥:v%«ÖŽpžg÷z|>ÊqÀSµÞ¯ùû­#êñæÇEz†yB->è»B[ »S0¿wlÅ;m‡l·lÄØÂ€öùºí‹\w GªÛ¸fküŸmN„äÉíXºç!›sC ;¨‹kPØæ =Yè>ðB§32¾ôSŒ‘§Ü�=9;[¿¿B“Ññ-~åØÜƒ×áVËÖÕm!ÑQ P¦s¾Cý ‹®cÑålÄ-° åД¿W?‚ÂÂ.âñF±[RÀ#ÿÝ!yä‹ã{«ã¬û—µº?: èó•š+ãJÆï«¬•ýðW…öVÊ~˜ñ ûa,@OÎ|Þm²Ã|<Y5¦ï¤/¬Ôã7ž';RkÌàœ± æè6mû ‰¯ÚæÝÇvF÷mOèJBŒ× ¨c¥ï>ö0Ì~èo>±î¿šf^&îCk`°'|³¿ˆ’ßCúÞZz½Æ´šZ#ži¬·¨!júqþâ‚jTwÐyë5á}âµmƒ(øîmóûÎÚü:wh›_ûq(¯ÇêÉŽÍfÇ.ÏëÛyö7æ5ªÛߘ›‹ö¡‹È?›·jþ¹q ’­^ÿ„³;œà Ñ$ˆâœ;l¯Öo—üÓeù§îuôTæªíôT&{(¹Iÿä^ÜÎþÞ±ÌK²m×<”[AB:öÐà ¾P Û¯»o4¿ÈEÖ¿ JnÝ¢YSY¿b‹×úfhjs‚'\KrŒCÔr7«ðµ~#�m…õ÷­$ëÍP¸7¬F믯ØFOåä?ðOÆÇP¶Ù¿¸Ç2·m Ú¿M·5Û¿M·uãöokÚþͺý«ÙþÍ’ýhlóÕlÿ6¶+Ú¿ÚÏþ­²ý+Øþhÿ*¼·°f+Ùò€ÏûL‚Ú?€ígžEªq]‘õ£·jÖÛV‘õ#¶jÖg®ò™’ý[›¶ß¥Û¿ŠíwIö£±ÍW±ý[Ùþ-hÿ*?û·Èö/gû—£ý+Éþ-ÿÆþAlÿÝþ-^û·èö¯dû·èö¯lÔ~´Zºo‹æ¶­‡eŽìߤۿ’íß$ÙÆ6_Éöoaû7£ý+ýìß,Û_Îö—£ý+ÈþÍÿÆþ4¶³nÿf¯ý›uûW°ý›uûW4f¬ßH,;Íu5ÇW§ž�§¸‹Î�ö^ ¶«=øcwú>¹ŽßÉ%^¹ÆÞ õ{}CþMRSݦŠô]Ûðyn3ÞY¨?¹C»_ºÂÅgѨ‡Úz½Kÿúëg.Mù5ËIùr—¦ü'À” ·Që‘)Ë©Gb6z{Ä<î1O(vQ Cm¹otvá¹kwڞГòøƒÆ kÙRü¾#ÓÛê‚®§FWðõtêM¿Så¤_Á¯~û72ß<Á½‰ôÛ ýå¾úMÚ$­a¬Û•¨›ZŽëiѦ¾žÐx¹“¶ž¦lÒ×ÓÞ›4—'—“Ë6i.¿·<øz𱥩õÔ³±éõô¥õš¾\Fþ´ÞëŸUÀì^Cð„]$ȸ�A«–ùúgøFÉ?Ç“þ€Âýâ2ZO‡lüÇëi9ùçºúzÚi£w=í¸QóP‡eä!¤cµ]Öèzòˆ&ãiÞ:Íú¯–’õÏ­óZ_¼îÏžð9É1.BPÅR_ëGnâÉÏ‹Èú¿áýþ¥´žfoøëiÛ¿A_O;mð®§7èö/eû7èö/mÜþõMÛ¿V· Û¿V²=Ú¿„í_Ïö#¨b‰Ÿýëeû²ý Ñþ%OŸ_ÿoâé ¶½Oo_¯ÇÓ[×kÖw\BÖ_¿^³þÊ%AÖÓ_Ö5mÿgºý‹ÙþÏ$ûסý‹Ùþul?‚*ûÙ¿N¶Ÿ÷¹-@û“ýëþý¥lÿ:Ýþu^û×éö/fû×éö/nb=ýymÐõ4¯R·Û_)Ù¿í_Äö¯eûT±ÈÏþµ²ýelÚ¿ˆì_ûoì/aû×êö¯õÚ¿V·Û¿V·Q€ýøv€Nj¼ç3íå�éÕ;̧”¢3¦B|Å2è8zfüþ…d|Ö¯ñŸŸ{=Á¾ Æ¥úl¡0>î0€m©âûé'>%Ñg>Åï;©Ú"Åššõ™f@æB2à™Ï4C€}['G5}¿K5W›*¢Uã•Èì*xÊVqrŽÿ÷çÀ>Xü"$wško>(,e#§Vèëß^ÿ*¤õ¯׿¼þUòú‡ ý üœ`[š02„l‹F:à h¯Ô_mPP©?ÿ´€ŒS©?ÿ´@2RÜ÷¬†~êŒìûæZÇVÛŠ“ï—HöEuªôÚç}µÃ>Sá§l`ùjÍÀ6là‡«½þ±¬9SF†³nýQæoà�ÛžÞô ‰Ž…Â]VFKØGkxBç¬ÑLœUF&¾³F3qb›h6ê÷ðK‚ª±×ìO<Ʊ-:9Mšἤã73õ¯‹¬ç±ªñÚ5äL 1%D,ööDvƼUš3 dtü´U^gü\–{>%gœ¯ gBÐÏŸ¢3²MOHsz& l]ñ™+pÇ“ð› 8mjk®r¿ó)-lN £¹*\Ýâ|ç·›¾;h®Wªz˜ÐKë‰ ^оÇõ«žd¹ÖOÉæ Í÷ʇU¾ßÿ÷ú£E…×;ÍØ¶×ñ<f¥æŠ3Ÿ+ÞZéuÅ¡Õ`÷÷O8µš\Q… CŸÐ¸=Qp|¾–<1Ÿ<q5Æ?ÁW4xæX&Þ²çoº[ q˜/¨ÆGê ÖªÆ§VS8;V>±ZØÎ’Ò>!ÛSVk¶?ø d‚íÞxf¼fuÀt—žã›·Bï6zÚ ©ÿWaÿÌý¿ŠûA?ì?fØö˜˜ íæ‘è›çáxÿ;J§Ö´UÚT(ù˜ôw¬Òôýci¶ëû•jœ=H€c`-Îvýùp|¸¬R£âW ÅtÇÙþ¸e¹f` ¿|¹×@#ZÁ^Þ_ c€ë�l{Txï\Ý ÷ÚùhકºËWj.›O–­Ô ü`¾ï\OôÎó§Wêó|ùÉ%ÒzôäJoÿeï4ÿ¦ÐŠ4­;^®Y×s>Y÷u¹×ºÛД;žp?I1¶GÐmóý­;²¬ë'¬KÿˆDÂ}x®HVè+Ò×+4뾚GÖ}¾B³Î5Ï'XhqÌ´ì{E¸²³-;ù.žW›*¾òü:O<ß¡ï?ŠÍ§`‰ÊZ¡ORLBêpofÃ$a™fn¿ydîñe^s»“û~‚'XI€ñ.uç›}Ô-coósh);¦×»¹¸2y–ÓÞìzîÖ´1ÿ>>Œ ‡ûCÄ—/§Lc“š°‰(mð9Èåš[ÖÎ%·¬\®¹eÁ\ý8¿ÑýÖ¸åd+>Ÿ[WœzV*Û8Ш…nêйdê¹¥^SSÇFð„ÁÄo쉠Թ¾¦ÑÔ;Ŷã?$S'Aá›KkÔÅrÞfuÒž‡6U˜/ ½Å!ªqW9ºè¾a:ŒÕø¯Ê5S¿üˆLE:6uãG¼¥HTÒMkvë)õ{åÞëH½ öÙ·/Õ |ë#20Z2p8°¸G<¡€Øƒ4ü#_;–‹}$öåìÈÀEàû¯>¢§}9ï¤nûH•Ìs;À<Ï22o+PYÆ ÌA°æô2ͼºR2騼JÙ¼dy¿°v™´¾:RaͲå‚}—hÖ-(%ëº/ñZ7xÜS ž0—øvM.õµ®7�m…uçu_ÌÁçKq$>Èt õÃ\Ok(ZfbøNÕx=]Ù¾3®ÕíŠf»Úêv…—j#4`|ªÆƒK½Ã×\4ð%Ðã…Åš»>$Ÿ\ì5p 0¹Ë ž°™ç"hɇ¾fßDãóÈl2° ÷ÑÀg—’!7íÞ*ì«#ûþÃà]ª1®l?à÷ç–jöuýì‹[ªÙwÛ‡AíãýΙ%ZS/ ¡0ÿ4ûÜ}ãyíûÜUO8º„ìÛ‚ />ðµ¯h‰dßÅYd_+´¯ð´ïµ%dÈ»·ò¸¬Cëeàçª1ƒ®lG1ï_¢Y÷Ôd]¿%šu)4_ ß—xû'ß[(±x¡f] ¶nÖB¯u§ƒ)§çu¡lÝqšãkݧ�´Ý"¬»‘­»{ž÷ÍÁU£t1®Ø{Ù%’}/-½×úu" Þ{u±fßËsȾ¼Åš}Ãæ4?Ý{ Ä ¨ãí^ ¿ÿŒL‰¯\ ½ÿ í1±‰1$Àh@P+?·-ñMì1“Lì …{ël\+6,¢µ¢“tN#Ó·q�U¦…Ïi‹ôüg6ç?‹ôügv@|õáÉE^û²¼@ }ž2;>³É¾ê2¯}Éý‚'$’�ãMê<Û×¾ã %ûžyŸì{ ÷³h8¼P_ x}XÙö}ºP_ V-ô.å 5 —Î" ‘Ž-œ3K|ŸEÜMêovlëg¡×°ÄО†_ÙPcT»Ë`.<›<Sa+zãc-ö:¾ß\©r­û6ÜÔ;Ì5àôz _Ρ^ßi!}jxÿïSýþß,¾ÿ÷©tÿo!Þÿ›Å÷ÿòý¿xÿo–ßý?�Úº‹³æ.3øþß ¼ÿ7“Ö™U xIÄ`í>0“2¡ñ6¾dñ>JÈ®ÔüKæ>wü,:çX@þ[�ü6æ·¹À]9 4ÿœIþC:ößÀ™xËœßsàXÌï ¸ïZΞðü®}Îñ­'r¦ô¼Ô¥zzeAõ—<†áKCl¼–j?j³:¾Ònµ;£"gàÛùv«™òíö»‹øv{Åû •x¯A¾ç^À‰øØUX‰?"ã÷}Ê€÷ïM¨å7¤8þÜa<=bÀ”ý=ØáÒ+›ñ¼¡ þ[Ò×®äï7L‹ä·z!¡k#ðË ¡gë9ïÑÓ$³° ⤆’ƒFm#ê6$ ©_cêqX¨ñÏ–‰ïü×…h + ÃzÜYÿʳ·u¡Ÿm®µneyÉ­‡jÔÍ"ušÞúÞ·ˆº3·ÞihÝËp‰ÿ!Bc¸¥˜š1ÃÙ²�†ÃdßHa3Ô¼K ;ú’Ju†­Ìð>3Ldè@ V!~ò»4&ôîºãSÄï Ò]- ½$\sXÖ»ÔÞèwí®‹ŸÐm]»¿X»þ¬Ý¦O´›O ¯ë ÷±x 3LÖ¼-؉!ZgèÄ ·0ÃÀ@†§ˆá¶páìtb¸8:2ÜN¥3ä3Ãføûã€!WG-ü®3|‡3Ãç _~Œ ‡4†xuºo—¼E‹‚uÉPBgëì/ø±Ï ñïcGè±:û0‡/{7ŸŒýZB÷×Ù ~­· ñc?9ѱúxzÛAÎúîF'àj¢©»ö/¦^ó¹öÃùãÉF­OÖ¬yz0ÃÈ@†Lj¡§Î0Æ2CB CjáL3ÍþŒw|íï9ñ/4 bÿ̈́wñcÿƒð½ƒ±ï'ômÍ4÷µá±V>­Ñéø.®÷Õ_oû66š¤ýi ÒØ„þƨ5f˜Fíy[jÌ;ª¿#êÕºe‹ükKøŒ`–ž‹èDݲKS©±ñ7f#a3´n{öm¢NŸJݶñ£€Y=ï#dØaÔ´ûq¢¯v¡Ô­j0W'O×]Æí…½Ý¨ß7õÍzcW©Ô¾½=-_+ë4~¾LÅw]Ñ °œÆíÔ…ü–§q3×ø%4Æe\«çÚb®Ñ—YœÆù\‹àÚl®ñ PŒÓ¹͵É\‹áÚ[\ëĵ®Årm׺ríüTªñ×ùŒÍFߊgÁÛfQG6.åÚ ®Y˜r(×zr-‡kÉLɯy4ÞM8s\ÞOs1\våK|V¶5_΀ËL[ —72´ ./̤ËrSáó0&R'ÝÂnj1;j¼kcŒB ÒÉl:%ÝÝøþEw{A˜è¾-¦Â$dÕ-l%¾.ž-ª–W!‘c:¾Á]C¢èÚâØ,ÞÄÞp Å~a¨íÏXŸç@“a¯)t(ùwâ|HQ§Ÿé¯ô§§à«õ%|ãOgü¾)üª`ß|á÷ ü ÄO–ðË_'ü ŸxÏ‹ü•†3Ìúž@ATš¿=æÏD!Ñ Ú÷ÁœQ×°~ ü=Sô7�2¾ãïø+’;íµB]-Pg' ß{åÓþnÿô«Â~$ZÄüg˜ÿ[Zƒ¨F=ͦ­¨ÙˆÊgÔXæúH & ê)FýÄ\o T6¢dÔCŒzQ ú"ª#£F1*M bÕœQ{/PQˆú^^Õü B]+Pç€úšQw3ªáFý€¨JFÍ+$ÔqÚ†¨9Œ*gÔnZ€¨7e,"ÔRšŒ¨çÕÛz[ òeeÔvB¨ˆŠcT+F ¨D]Ũa,°§@u@Ôßµ”Õ¸M ÂUè§ÕJ j'j;£Ül×ï/3ê+D-dÔF¨ DMaÔ>Öp@ÍB”Q·1W©@ êIBa°ˆ/ð¡Ob–ÇÄ2B Ruëþ}ÿ—SO¨Di«ñÓðuIûwšËé'‹pgü'éürŒDUø)XX´7v¤WòóͦÂ×èð i‘e«ãD~RRCÿŽÑ$@¥mY$}§øCü®¸z¾¹|#Q c٠ʆt×É›Åþ¯-‹ÚÁ†_áËÔ¯îBŸ¢xY6­È“ˆøåq:ñ@|ò+m^`|‡±„‡ _v‚å½*†;ºö mð~Ë™6 ßu‰o¸<ÞãÔ9ÐPú…€øî“Èj·âÔÐÙˆ.ÚƒAø“¨:Sb¸¡Ü÷áóKÁ gA­O«÷ìL šèÏ¿O¤]3‚°ÿYó”ÛqËìHú­S~ïDÔÿÓ[^û‘b¯|šÊmƒ—î?H—Ç¿¿æŒr°œ9oÉ~B²ˆ‰^W% 'T²ÑR¬+.Ùx- }°¡‘ï_x¿?-ÿxŠ­¥Å¾-Z»¿ñ†õ Éz˜Ýî7¨Ù}òï÷ö9¢FÍ௠Ðs†ÏÍà7Iðû8â\Ã$éû!ÅQ[fÊßW1U—# à÷nøÔŽ¿“ûñ{îë&â÷˜cèœø: ûLú~Îú÷µ×IÔóË© ††‰×Êó÷­ñéñý žîöoü“oPýª§ó¯ƒyTŒ_9[/‰Ÿ‹—D¥á—3—öÛaÛmÑ)jXGþñ0œ9üÚ"q® ÜüŒãNû½1‘Ϥ ¿­Òx&¹–ÐÛ*"7\í·ýšKø=‘AØí„^©±¿éÇþ"ác·z£Î>Èý»ˆ¯Æ~„Ðïéì÷ø±o˜ø¡ÁØ? tŠÎêÇþ á“‚±¿Eè:{u±/û(· ÆžFèZ“ƾĽ+á¿1a¿†Ð:»ÝÝ@ø¹Áؽ‡h»Îþ„û焌}¡ÓtöÎ~ìo¾G0v¡¯×ÙCüØŸ!|ó`ì „þ¥ÆþÝ[¾ìæéˆ_ŒýVBÏ2i»§E|ŽVAR8U-/†ùœ$²p5þÏwp¶nጚ…xÞA!kãsBÿW·ðY?W¾e0ö· ]ÛFS1‚U¼2¸ŠƒU|œ„|Ú&Hÿ%t±îÅ/ÞôUñFÂÏÆ®ºHW1ëMR1çÍ *î};@Å­oÓ6;Xó }“®b7?'þ¯ÖAØGú@kMÅ-”½E}QTÅûUŒ%!SƒµÑ–й­5ùªxn c?HèÛtodï®bÙ´�çc­‚´ñ¡·¶ÒT¼ÚOÅl¿Œ½¡Ç¶ÒT|•Ò³¨· ƒª¨b ro°6NNEt;]Å7 }Uüšð]ƒyq¡¯Ñ½ø%QÇíAU|cj€ŠcHÈþ`* ôj]ÅÓv_ ïÆ~¡_нx«˜\ÅS%*/A!·kc;¡›é*fø©¸„ð[aŸLèu-õÃPÚ"E}ðFPŸ TñQ2:X÷ú‰–BÅÖwѶ;êÎ×H^픀“²ÏˆáZávf8?ž2¸ˆáH ¡€­ØÍ /2¼E ¥:Ãf˜Á ·2<L ™:C:3<Ï îÉ QÄp«ÎÈ ÷1ç ‡§ ƒG¿Ós;3™á@ Ã,bX¬3\É û_%†Õ ¹Ä0Jgø˜N¢j&Ãâ’�†qÄpΰ“Ž ¢¾a†¼@†±dô_ú­‘Ṵ‚:8~ *u†©Ì0~ŸÀ°ƒ^ÑÆ0C>3¸œÄð€ÎÉ tÞ ÆO d°M¢ P?ŽïÊ ½¹…ÉCC¥Ât†‡éT(ê6fÈð1ìÔŽ©[·äLÌp!ÐKÍ'#ÃDa wÜïÄðG C41¤ê ˜¡Š¶2œr"C¤Îà S˜áý@†Äðµv¾Üº3Œd†¡ sˆá]!îb†lÄð¤Îð»µ3´ dèK íu†?˜á¯ÃO=Ý~ÐOðḭ́V2œ£¡1WgÈG ØA d¨"†,á&nÁ O2|J t†.ÌÎ q ¯ÃÉPa‡ÊŸ9¶* éİTgø–>f†oÕ�†ÎÄð»®Ò@f(d†OB‰a•Îð23D¿L K7#'ND†1:ÄñÄpb1L dL Ýu†pn¡‚^ dèJ u£“¹…"fèÈ`"—Îð3ôg†Û~vÐÞJg0†nf†ˆ@† ÄÐ[gHåN%†Å ïÃæ},1ÃffØÈð"1j ñòq.þ»ømë¶,+"—dýø*žø]²H·m}Å)6þZÍTºŸ‹¯ºI­Ä„o:\¢·YŸkï.®( ±Ô½Ä½|2¾äfþð¸Õ[‡‡!n×8qž¶E¼óv$/)ÊÉÁü{ãÍ'ðx®ûKòï…ÇÒ~ަÃù¼ËA¯ æWûþŒ®~vÓ«ÔDÏ—Ð%ÆE¦½ ù^°Ë\ôk~¸ÐQK§“ }ÎÓ~‡EãäFïÆúR¸Á'?ÖÞŠÕwá¢%^8áâ*¼(*ÐLøñz8_ÇáµxS7¼îÏ×=ñÚÂ×V¼îÁ×ðšï•E=‹×ùz^óݶ¨oaÆyZñ56{ø~]ÔãHó'ßx¯ùŽ_ÔP¼>Ì×yx]%îJàõ6¾¶ãu%_?2óõ«/åë·ðú¾vÃðóõD„¿Ê×*^çH×Cøz*^àëwðÚÊ×3ñ:‘¯?Àë.|ý ^wâëxÝž¯Ëð:’¯âu_ã ôÔóí€ ùp]Ë×s^Ã×óñz?_/Çë]|]‰×ëùz3^—óõx=Ÿ¯¿Æë|ý^OâëA0M=â¾^Ûøú ¼Ê׿BÌð<Ã×[ñ:¯çàu2_‡çáÒðõW0k=ùï yb.zýÙŽ¯§àuÄEú=Sgü òŸu‚Á«Eò oÍq¿&õ·:?.�õ£{',jÀ´øÃò1<âÔWCõ3ISÑû #Õ¹¾c"¾‚”çCªÚ¥cŠz¾Š¿Nt:ðÆß™ÂL û¶ˆÔnò¿E¤û@>žï&Üö&Þ}h°]瞎€Æ+Þ4ðlÛil(—j‚«ˆÉŒîòx’6×0Ë2ajêó‹(¡|^Jx^Z(îW¾Jeêð@¬â÷a_ç64œ\.ßo¯î¡Ç7qû’ú§8öˆóìNd¾ÒâšÏ¸Bƒ“Ã@¢3Mǯ£ HãluгÝ|Õn=½°+~ÂûýÝ]ü«°¶5-ß“¤ZŠvÛº›ãN¶ÔéÒr’ç,j—Ï…¯¯¤ûâž+š’Psy’}§!¹Ûذ1û,jÎØ døÆ´&c-tß™KGñHsßà­ p·Õ*Žn#^ÛG슰àûW»™w™Šîà[×ì«ãtÜ‘b"à7‹ZàÂ:¾–[ニïû~Øù©Ží^×O¨-£ÃüÔ4‹}|4ŒazU®Õ‘ZRœZj*¼Š’‚p‹šÍsà–ÑÚ,‡y¶»c1MŒÈÑÚ}wñ€»% 6Ñ¡üŒ€ût®ZÛ,ÎÄ|Z~4¯€8D×ïTͳ-™©³í®|6`†Å‘ZlÙ\J/bv8éÁ~FÍ>>R¥‘†§šIFBöñ%°Z7ðjMøÂã[,ÁÚR‹ºa(½*n{Rñ¤Ž8Üå 7 "prG‹;‰�Q¹/ðÁ„¥¥ìLî8”ï„öÒ"L…òÐxPóDˆÛ”üTs ’æ i�Á¢n?H±EÒ¯ ä"epO¯æÏFޱEgL“¿ÅõxwŠã`²š:ÛâøßÉ«¥õÒ¾ÕÔí;Óäe8ùUÔEíò=èå8hu†ñ[¡¶êHDY�®íRmÛ¤Úæù�G—IöWç(Qù«°žC7+ÀAVƒËŠ¿ ˜?�½HÁwtãÛ‹Q»d‡3‘ƃxA_j)ÿpšU{cß`mÀ·@¦–Y†d•ÉÅûô@Ý)ûשn¨«v[çEçóðM…£aNëýȘ_uÌ/zFóïV' +�Ðy‰ÿ§½wªºþ†ÏäÁhlA‡ Z ™@€hn3aB"„‹ !™hn&3\T™8Gó«—ÒÖöG[ý•ZkñŽX1%#hÅj©ÕP h!\ó¬µö:3gv&¾Ïó~Þç7pæœý=kßÖ^{íµ÷Ù!Lo2“BÇhùæl„¢Yƒ$Ùµô-ð“åx–·ho ké4œÉ Õ&:‡Ã÷ ﺒ’Û»ðùþ76Ó÷Ûƒòù`£ bLãmøª†âZgŒëŸ5—çÔ¤¯£¶ XêUtFå¨B{~‡œúkÿuu4ºÑEßK“f-'C+ï²8¯TCûc÷Ý"ú)<âÓüÀûÕèû+«å‹Ànx ¦w‹ï›aÇvIvWµ$ìÅ$5­À/ÙQ$ÝÒüѹÈýâ†×“"�m¸ªQÌÍ©—µ@|£¤¾®BJ›5AUeܦ½Íº1†š)š\$¢-Xê½ÂÛÇñìÖ¸Ÿéê Û¨LšÄ†éFIÕ~F^àÛ‘½:–È|’ߣH<Kg?ñÌ¥G1‘¯ˆ} yƒúÏÄFïPNØÂøçl¡í Qâ¶â©;»éÀ©`üYú`kØoïChñÚ‡¶;x²*úþ=Ï·yÝLY¢—YÙÚÙÚÞ­ ôq{à¹;„¸€°’¬e«Ö;´Sx‰Öì ÜI¬ÍÈv³ÔêªpëI<õdÑ»š÷¾°ä1¾£A¿Æšn×ÎêóÃ_ßBºçk³8j'pÃÈ’lÓÛüÏ£gP«ý°öÔ üšûb޶ö)ɺMÃkJà&”Rì#%}ŠöIK›MËM ¤WSò×6`ò±={'_ÍÛo£3Œ£ãéOZÔ@ÕcÛ|äÜW_ÞŽÞŽ¼õ<Ûý¡óžÛ@)íó݉ýÈ=±OäÂEÁ¦Ûåïï¸ ¶öqžÖQà÷fÿê ÚàPàØí”Öz=·‹ xbÀ ð¾aÖ:h}B3þ…Ë¿ZÌLJò€b6“DС©ýíÚIœÏ[¦OƇ–Mm|NÑ» Ëë•ðà4Í'yL7¸¬Œêhy}hÖ‰>Îí¶ù.! …¤û¿6÷Û[@öÅ Þ³8}<O>͈<ƒJ¡³–ŠØ€l6÷ÇÜÓn—ûÜ×Ö›‹÷k ÏXt~|”Sš¾K 4¨§è ›/i|œv¨?òht{æ}(‰ïèpamõVÜ“Y+€Zû•òÏøìX4Û±ë 6¦Úý_"íjN¼Xóê&L¦Xç› J=DÓÛ:É¡5:ÊÁºÕáwRíÞ/;íZ?p·Ûý÷&Ûµi 6¿O‹ƒÇÄl_ñ(ðr(6°µUËì:šË³ß„á;FÅáÆÃ»èˆ© ñ<`;įl¿õœwÊÚaêú¸‡N ø¹Ãtºû¾N{õV 6ï‡/©æ.œT&ôaGQ ¨í`'éô><U7ÏíY½Õî+~SQ·7Yv¶QêVo%E‚x˜ªØ^¸B³fB+Zˆ{¾‡ÅQx`.{ÚñØ$;Ê#owRPpÂÓñÆ,Zi¤GúB‡‚°¿:ÏŠænM¬0m˽+*îKqú“oõˉÀcåXµ¤Ã„ýN{Lû�ãηØ'°ãî·¾t‡l¯Ç%¬Þê�ó&0eiÑÍvM!¾�ŸÏãÙ™À±å™XLAì¸åá\)dÔ0•tPý›Ç–!£æ€¡ákÅõjÅ{÷Ê8µñ @å¦�G%‚@oÁ½Ÿ'Ǩ뛈Ïo¦–óîIíØ4ÏÊë|kŠRm˜ºî q6W˜jç Q$R(DâÄÓoŽ Ë ¤ÆšY5*N]·Ÿ>È…nÍaï”æXÏÚX‘™¸]¸uoò2œy*ä"M«øºÀ7èo˜ðÚð|+ò Z/h|6â‰mÀ6Sp7„ßÌü±ì³ûA R=ä˜Éä3”(|\å ñ1¸Z?—ÓÍ õèpcß@ÜÃø;5Ѥ.h9®Q|fèœ1J7†;èo¢Œ-T̘ŠÊòp*F·ì.ðŵÏ,¶#øå9ãyÍÁjêÎþÔ­;W†––.›jÓ’sQ$2å—6d×>Åê›J'Ò]Q|(¾Ù+mþøgf(м߱±æàs¹æ �½tJÔœf“±æ€^¡SÂôÚ3¿ÌœçÏñyï¡Èþ*Ôaÿê¤ãÈÂ|ø¼Šø°m1ðaœÌ‡³K!¬yg‘Jdà§e|tØ-ç hcò,µI†Ûh×ï—\ìg%47I«tBdYaîtmŒ¬@ì #:½L„¢ߤÀ¸Rª[[¨r:0y𣧡eG|)‡û€Ä²/0XO¨Ûèu¢N½øløÜû¯Ía†!›2þçw'ëEãäs%¢éç û1„§²½¶þ†VXÐOþ5žÚê `ˆ!'•Ñ'«<"VOâ ’gVö‰¼Q ¦6ks R‚…MÅÛfí\ÓÝT˜Ó1¸ÓtiÏ&Ê1ô„:§=tH{SËÊå`þŸ$^ÏÙ€Z'µÚ_ní¢£X—â9CçÝVo—Ø;OÍCÐn8¯ÝÚøÛ0Õǹ¨õvˆñìÌÛ‰»ko3ŽgëÜ-^‚‡Ç›=[À&ãó¥Ú¬/˜7蘩À\H£Ïó‰‹úKÖ­M'²ÕGßlÂEž¡ÑGoF0kV<|v”/þéJM]ˆQécàc±ˆS»i¾"Ú+sìÚ9´U¾Ÿ2C_ ›¡Ø‘K ÌXªwéLKÄSŽÕM°BõŽK­¯Ã³„<Xû;õÔÇèоAdgÞ6j8ùYñi Õ)ü/ ¼ÌOubÔo39ÿ: K0ð+§°{9¤™5'o ûÊŽæ §ÆìÈèÍcFy¾Õ‰´€Ø/FŒè,®¼ ¯MÇ9¶Ü!P›Ö£=îMQÇ�Ç‘¨_¡5•a/í£ÅÐG½„¸íþ•%Ø5ÈMìÙ™ N„€Ò'…IŽÒ˨—FföÒòýÃþ-:`Þ5z?Œ:;S¸ó}-Úv¥]¼î¥õ«ØÀ¬Å˜¾“@±Ôm œùþ)/rXÄ«±€µå®´Ëú(ȇ¤ÅØ»|”ÒâKº¹‡ƒs+ò6UimÚg׿$•�—¢>ø—X䶦bîí¿Å:‰ò-2p-ÆJÓ­6.¡Úø0Fo÷ÄÎôOùFQt2`>-¿iý<6gCÜm>J¦]kçÁ2Glî´ÖbgÌÏQ·Kqð Ž tbÑ‹#H ­„^ñ$qÈÁJ Ó×àí¦6f˜ðô­!jãõôp™Úx0Næçûâ&a«£6>JX1C„‡” VÿƒÖxÊן‘CêƒÏ˜D9R+®KFžöç%$<8”ïôgÁgPß_A¿Ã®½ëˆ½»ÒN /8±åËÖú«h„ø{w…,7ðL4%øM Pü"Áß&üÈâóó§¢Ïæcí]½Æá¯^Ã'€|·!w>ï–Ÿ.¦.ÞØ[1ÔÃXP×ßÂM†zا\žINÊ¢ ó±+°þ6.ݹ1¡Ò¾×^”fÒIšõÀImX;®±8õrLYl,ÇÈBiºCXƒK8|,ù™K#Ì_Œ„äÖµÅ% ²W“´ò|~$³b¯·¾]CLRÚ_´‰2 'Á½LË]©6>¯ ÍSô�bô[Sñ} ‰ÙÚ›jãäð+u{¾=¶¨»[ïôÞF+  îRsøïNÆó1ÄîÀŠ…T,O”ê=ïå q°Mm¢ÃØrWB"Ü˽]qî;!î*H_³»<ÛûMN¶÷\òŠR0ÀîÏ‘+¶ÙpÞWÒª2¬Å‰ÁüÞcóÝ]¨]€œ¹»ÂÖ–KC»Å�0'�«=H<g:äõ_†Â:p õ•4/%B™øvP‡€rxze%°TÏÊŸPV‹È+Ëá\ `“q<Îá¿ása„a¼!™ž<ÐëÄç«¿Ì¡‘’sh¤©¹—$ࡱþ6@”Á¡d… h1 ?©ö`.²‘ÎFp|wàa¸Î�÷ Û ð·á°7àax«n Ãø©0<ß�?†³ 0}9õÝ(†£VŸàá¨ó§€áÁ1g{¬¿ðg칕Æ~B‹¼_‹ðŒÃ?zÔ"ÃŽ Ÿe‚›wE™½÷¼"›Ã¡ZŠc :q„|þÎR&ß”t#¬-þžÛŧ<J2Ž'…†¦ËÊÝ=‡oY"AM_¸¶¶þNyÛôÔÇSF¡}0Ëæ½Yñ <ÕÖ³]ðî2õ嬛÷°É0Ù6ÚtˆM­c¡c7ÊCOq£¨<37¡\ÍÍMÀO”åZn‘'Õ—s²wäH¬°ZƒóiÅK˜ß[y¤ ]ANü÷vãÒ+Zw¦¶·Û„çÇ3 ûPç€(µÙq6mFb[îp1F©¸7ísß (ÐÞ(ðWu$¹Tô×è;ê×î«)û˜óo[ÎÓA_£p÷; kËMÍ.Yé1C¬ÁÛnÕ÷ÿx7°’üyÚ¡j'øfÇñòc׺‚EG½¼lþ™‰––­ýÁî…öúäïÁþ¶1;l¦]8Ô©­�å>/ÑæmOÞÅWa§¬-ïò¥bzÚNy|ÓŸPeÒÇ&¿Ç¯{B%Ú2÷×°Å%àÃÕ6_G‹G|EüG$ƒ˜îÿiŸÙbW&Û2?sŠFüó‰ûhä²çøuÀX(`Ý£$û¨ºñcڡйW·áQCÿìÄ‘éÿ™Cý#õfŠ0_‹»F}9~�¸Ês´¼kÔ—âÏߤ(9ò®øÁ|n$ÞG£ ;O>…ä? Î§Ã=t‘Mk ÞÚmд[Ê"Ãþ—Þ© ôÎ(î––ðùCSMK‘ÕñG¡–¡'x<Bά´Åá ëLà…yh6O}ßàþcSß*Áó–çé[ß¼ ?AßDßÏ£´á>2àôŒµùs †&5b'¦©Ãƒ‡!ÕQ‡¦Èv¨•زß;Oï_ŠPc ÚŽ#;š1@}¨¶øëáa>üœóp—;8µø¡C,ëÖýÏã%+: ¾©hï™nw¦ÃwÃÐÛg¶¶˜éþñ×Ìó„íïÄ2—Æ/þ K ž£3õF¸µÅ¨ºÉÒÕÜ—e÷ã˜ÓÊ„îD_R ¼ÓvB|ØAµ•î°ùeeû½ëÚ´Û}©6b»Ázúp…ûX ÷瘼]ñZü†Jܽúˆž?ù(4_(.ËÒÝ=l—/©Þë´·Á³ç}ˆ£-¾€“¢˜¾—Ò<‘=šë#Æ…|ñcà­¥»@ Ò â¶x2*¾ ¤ÏòÌv­Mm|4æ7»­4þ4÷þ¤KL”ü>íÚåzü8Nau‹ñÅï:Prªl5o—¿Üáúñ>댈7–ã݆uzCîû˜6‡iô}àu¦v¹+¸R/ÀÔŒõM­v"ï3ž¡ÛÔ—áøé‚bð\užxGébÜ>ë&¤ÓަMðúó†ú!·Þ©C€Ûw1ÈùÙe(ç?ðN=³P÷[xéÞîà&².¦áUàñbúfu¦~°¿'¾x´X?ć"«e·M;ŸMÕÒÒýJ ¿*Ö«Å"ûàËóØßßÕ’IJî·È=ØólÐÕ-¥_Ÿ\îcãQìjã{¢¾'bÄ>¬'â_-7)`£Ã~ÿ9´hÕõØ.Û½ç.W×}‰ýÿ"ÔW]`ØZƒ±Ú»¾¤ëJ©“o†›Ö±?°9Vm,CÓö ÉÛi2uùÁ[÷Þ,‡¯&±ÀgMtdBÍ™ˆ=ÉñqàG]û®˜Ò¦AW°.äFÛáÝKÝB¯#>Á¶ybð)ê *…gÄÐÞ-dÆMé Ü<[`ùâknðm%4_2q}ÚŒÿ)©/ó—û¦®�§ž•&èð|‡Ÿü3wªë¶‘a_Šä™Ôu{Èľát9�jËŽOÐÙýÅ#àá­XPWˆi9Ð]”€É=_=‰÷ HâðxzVw7~¸íÑJ�Âö °ãð j‡ì™@ƒj‡|YÉSÀ®™á}ú¨ƒ† Û·P_PÏ@Ê׋-Ø—JDb½]W¯(�”áú’Д§GôGo;pÙš0#Ìå<½þ¿¯ãÏ«f4 ¯`»]dZk¥|{wC3zXC·|P…q1ÈÜ£!‹vøÝUX bî?hÕZ³÷þ –‚Õ´;O{÷ägyÞÙ¦u<0²Kýk‹jm÷Ç °ùóv¼T$Úq8%³Kmܳl&O}UÖZ?.×ÑV&BƒŸ |m"¿êú+úè톜 ü$%˜‚#»Cíxà‡§º»OÄ¿bug‡(ë+ÊÄb““·eŽ uý‹ÔwpÇN´ü›=óSuÝÄÜ“#gD=T›HÝ%‚ÄÑÛ‰ýÝCð{ðÉd{ÿÕ?áz÷šð;ݘÃvÓ›6þA»æ®ïŽ¡´µå%°õÂqjÊäøƒri�Ó9þkÔH±# ŽÉWáMËPá¶θ°¼Ô¦‡©c?§TÏ&ÚΉ^ÿ`<¶8¸Öúé<Œså’ˆù ˆôßAé´À7~âÀñ)‰=Ó‰íÆ½Ebº¯ÚØ#ïÎ>»‡BQ¾¦‰’ý^0ÛÛÚ/o NÑýÑÙ÷:’ ô±iŸÍg ùó³¶1Ù2ßkPmØW.À%+Ú»öf¶(Rì­Gn¶{wÆå㩌™­îþ@}ÝX¶AÍQׯ¢zßíQdíÇV;žåè=x ³£¡ð¡@{P3úýRðá 䜨3«#C[„u]ìYTI+�-È<¬®¿áÔs÷Œ€zõ­ºîjtù¬É¾‚4{&ôɯ   Ñž¹G]oÂDZ6t¼m‰<÷òl_Á$›·5ß1ùú½jc=­/<~ænuí>ÈxÁäJ3h°A˜|Jœ;Û–Ù®®}i ʘ- ”¤Ï"bGt¡Y`Ffá›”ð›uâM ¾±yOƒì?‹eú;V®Ü¢ a)?CšpÍ"hÏ=ዯY$NIM8ƒC¶'ÿ>r5>ê_÷©ÖÀ"ß#»¨ø}¨Ý>`F1‡¾‹ÜÌ?ÔÈü»öíŸüßíùRðá`ð勘ÁoP„*å§®Û>€øI vdÕÆÏ‘5Póí™§Õõ›peV ^¥3x50¸-ß‘yýyµq -Ý›úêmÈàSêÚ;¼ l¾lÇØ‘Áðn٠îR×ÞLh60Øfd0¼-Þ¤àR.ëž$¹ ©?TtºÒƒR¾ö6´Zw»ó±eÌó ¾ݾ©×Á-´Z¾ß½+KhÂlP…MíjS*äÔ: Ý¦˃þñB¬Å÷‚Z[¹-„ûŠd\ßApÒçp Î=Ýݽý<NåiÝ¿käß´Œ?!ÚŽ‡ÛNÅ1Óg¯ÎÞrç_ƒ†zvk 1Û{0!|ôoQ:»»[&z¿Œƒ(M»ìÚ@7(-€½ŸÇì?h:ÖPÇ´ûmIöýði²ú󲆃÷àá3؈íV›<<NÏî¦@Ao7>y“tò£‘­,=»Uk+hlŽ  bš ÛÙ';²Gv@n-€”ßtí¢º´ Á…Ve:=jî�Å+ô:n¦µRãP¼+Ë—ñ,øçV¥©Smz úê:µø_.@nß»€ÐcÓ¦Ã}^rð~{Òþî0±±u Ó»"›å åW¾@”ß4Q~ñ3ÑíKZ7ÿˆ÷žóY¤ê÷™×´Kmª…(ó@Ïô”–q E }Síˆ3îwaÙ #xÄhÌë/»h$=°ùߺ)&ô¢ÓvÚbã-P9  HÇ›Í"Ôõ¸~hžÐ{Ñ[iº¥Úø-=Š·ˆÌí뉺Õ;øÇ‹°¼ë£ì‹¶Ø|0-ÞUS5ž…{§x¾*ôçMìrLžê½[‡6õ.x˜á6Wë Øfvx. ZbxüÝ–9Dm¼†°'bu>Šãf·™BvVæ›Ø™‰‹Ív €6]+N–¶=›ubuíCcã=ÔßfzÏîŸc+éŸóMbUˆµÅ¡Y·BKa÷ÏÝjWs@™´¾ø–>^·ÛÚ²~BùJ²`*µ[ ²¤Tñ˜ ?fxL)<w\›jºÑª“Ö»Ê()ÕÆäa 3iï‰vqfµÍKŠMËøª×‘ÁÆùkÇmþú[[Nõb½;À<éX°g¾ã®ÁCQ¡­÷ç%Ý{dy—$œÇÖÝ S8kÐ{ „9 YéÿfHÖ°¾Œ/æaqwd$ú±8!ÑíóH¢Ú퉭"ê ~3ÌÆoŸÇFê(Z¾vÍÓÔ'æõ4R7Ï ©ÁÁ'º»I@?'Û3Ô¦oÉ”9Q×þ“ž!Ÿ¦cèöâ¾LÓ Ü¿Ÿ©OÀY©±ISçÑ®%3áÈ>Š›*Ñ ù-"°ô#üÀퟞè0Ý™�ºÀæâÐ27÷w„fŸ­$°Š Lçz ù˜v�ÑÒüÐ1æ[Û˜Ï0 ¤l''}>W}‚k-''½MŽÿ¡]VQÎ2~@ö‰–Ï-vhªð`[d;ñüZÚŒqV¢r\�Mwã6ðfÚmó;öfaSÞÔ„í4ÒM·c¢ˆ—ôÃajÃÐ!@ÏS`¡(ËÓ¤> lœ jÓ¦LR×·@™aN½l.èßt…Ñt^mZ �A¼§e|?‡Ê"Q· „äà*<‰š%Œ¤õ($÷ŸB½Ž}°5Û¯F£Vkíþ­¨Þ[¿Mô~• ’u» âù°wÕnÛÿ­÷+“ÿžéÃIà@ƒßÐ7¸’•­­ždo³¦Pµn³¢ù¯@SÕX%DöF ±ìöv_ †a'*v÷¥Úy2ß;êÝÕoÌ1Ü‘gÿ—G®zC;f¹i Þ &QœÍ7¥Ýák·A«±mL­+dfîñ¼:øx1ŠTQ"YDOàÈ4ÍÚ_a/L»=*@¢øhhM†ÄáçQ%x'!h a@®‚ÿŽdâç¦H&foEf¿Bl\ªµç‹¨™ B3ù94“»÷Ħ³3ÔtÆáqu¦v»öžÌÏ<Sk¸­+øsh+Ù‚¥UkG¦‚åi…“lmÓSDlºÎÔ¦F Ðn÷¶äkñ_Ì6)ùþ¸]ùÚhS{8iÁ›NàÈêkmêþÙ¨Å¦ŽŸGÊ,ƒê*2ª— È<n+0_üXR*À Îî‹yvH_ûã†zw˜¼§ÍÔÓ²iY[V¢'€JjùŠå| ú0Ôé‡Ïð9Ý––îà‡¤@r'ÇOÇð¼egpþr.÷Ñ~ò[Éâ<§Ÿ÷èðg¥`íK[Nx@óÐ=�^˜áÅ‘dîX±••oðy¨ `yñïÌÂ\ÄIº¾Óæë,Ъ¡nV£¤¿?ÍɳbWgwЇ£;þøŸÍ2‰©™ n¾ÁÏ¡®Ç¾ÇÉb“ÈvÙß22þN€ƒ­€b§æÔÕù¾F²UÜjž¶7ŠL_Ó)Ï_Èó ö4 ÆP÷[–çÍ2Èóûøuåù-ÚLMìÅΣž,È…ç™ß‡XüÅ-ÈⱸEÖÆóô½ ÌÂɃß!ø„ïØ :æàSßÓaßR‚É ÔŽÉñ¡ïµ6»¾™¨34ø‡S˜®S¡ÚóRXm‹R{öäù*מCyþµÑjÏ©ý‡€8\{aí9¬ŽÙãÏ7Ôž[†o3ÔžC¾Á{‹°öØ ödqíɵøôÑÅ’{¤·’;%7&4Ž/•ß(¿=T~WSùí€öËï],¿Œ¥EX~`ÐeþM]OëÝ2©k/ÁÒݨÅþ†”‰ò{ö(²ypJ²ùs²;§´cMÖku°ê8~|+àª}Úà“diA=!N$]x7Ö“Ufüú4^@=9uä_6ßL”€· QöBIï3ÌÿÁÿE!±vmèÖ©¿Gº¦óÇ…à¿N‚ß6rêJ€ƒcÀwô×NEÊUïÇQøò“¡¥LCQmüˆ¾@ƒQ79c.¶V¥Í„œÜ 20ë2®E¼q_~ÏŸ@R!P´æçO}àe4„68¹{ ü{AcØ'Ï‚.Z(jÔAT†F ö‘ŸÁ~ªvK$ºiûÜCÈ×FB7-ß@7mŸÚ¸¸ ³�oïoSðmÐè6ƒJx‰tV>òíá™È·û»p’&C³›ƒm³‡”p’s&*áa xâvéj 7-øÉ×T¨³ÑÿÚA´ÿ^Ü|£S·}… =K¿ÕL£(—U0ðmÄ~y±SM3ÉÀJ·Ó6mjgÚjØVDðίq¦ÈÔƒDxŒnS÷Õ»@µ2!XÙ¯`afïoÍ9øIxœ…æ5íÎ×ín?U€ g¡HŒ´«‘öÚ“ü}zç¿è¡<² Ùá‡þ2Ôß8FŒòw§?´^/@þŽùˆ-ç ¥b\å±;èêÔ¿§�‡,ñÚçP¤h ‘›Œ|3DDc¿ ¾‰–¡–oÆž'®žs`©hÐΟ=…HÒBjùïS†ó ßkB‹$Ñš(‹¡ÁìÜ"ÌΈÁÁ죅4˜‡/þlGîîtd¶©k÷ÐV63ºN´Æ¨¯¡'0A ÙüÃú«Mbþƌέ&µé®ó.Ž ¬oFbS»mòÔ"Lbã4DusÝ4Ó¹jÏ<¨=Ðéj ½YV$Ú3;ÕõÅQ¨æ¨«QÕù¦c•94«L T™)»ÔÆi4l”ñ/3«k€#"“ï‚*³ø’P•ÁQ½êúó¸¯U—ébTØ×. jÀ›”𛽗F52O«ë^£Èê sfRÿ.xÆ]m"»næ3?Ž Éñ$~q÷~yE%”÷5Ô=4hŽ—éøœDhr†£·µÅ”vXqÙ"ÿëb0ÿù“@mÊ*cÆäa`lŽHeö¯|T; ÿè“TƲ•1Fí[Õµ§ê<È7ã ¨ @¿ Ô<°§AŸ4…V %?æ.ËÁn¨Œ³&ñ}áÌ!ñ-a»øPósà³öò9š˜b\ìýò<v‡Bc óp,¡¿-ÜïXá�ã%>Ÿûygí=úy“HÁRsÿo;²éJ˜4~±èË›EÀ¢mÆAccÛLÝÑ@¯X„MóZyüçÿ¹ã?-r³ „P«¬ÿé­rkž¶ [åIvl•§÷´i¡EÓ`üñC0«`Á Ø£% Àgm oFÓ¶ùD¼Ý•´ñ‰ó‚·xN aïÌ4ŒWov(èõà œ‡ýãÍ6êÓ|7(ièvˆÍoþÁà½i·ÝïØ…Ã¦M‡D§¢ w²CÃíÀFÜÔ)—áä©56Ô¡cÉV‹·ƒ#èèÖç/æ[ñSWP<If=ž=YÔ‹¼ƒâÙ«6US<wB<3ƒ?=+ퟜnâ1u›ï‘ãˆÉ~/ ÆÖ½-ýòÆ´â|™ý_¢ú²cöÚ ëÚzì˜]hÇ;æ8ÖñÕdÀDbõÌìô|�6èo¦›„„aKù.Ö]4FwÑäQ15!f!´ô<Ï‹ÿü¼Í[‚Ôðòô§TšpÖ“mÌˆÕ ×ÁIç öèÿé(Õø”ãhØì¹4´NÁošNíÚÐéâÓ…ß9Mo¡�&O=6 5ÍSýèÃÈ÷èX·—’ÁŸM# ‡+©/þåi4“»…ãí²©ëĦŒ›ò©çãõz:i/ÛNž°á\õ/PÜž–fœ½œÙŠCW¹ âÁ·0ÑÖô1~›HA‹º>‡·ò÷Ço§Ú;~ ¡ü ×2�ج®NTâã]Ðö“èxZlþ)+ÿ!xvG˜¼«[èÈak‹m DéŸzØJcŒßÑ-#³2&~4F4fGà›OP¤GϵãàßКˆµæì”aZôl2¬²{¿:›]Å­ùþA“m¨ͤÄGüÞŠÜ, νSmNÀ ´ŒV!ÅÚgãŸ3ZÜ÷ø&á^6VQ™m“‡ƒÀÏ¥Õ݃§[±uIº§3OžÚùå8õ,ý£F«º—^9ð…¯,‰!h?¯Ï§hêÀ¤˜†oRÂoÒÄ›|ƒ‹î´9[>AÚ|s¶z;LžvǘŒXvoOÔ÷…À¡ÑEq8ló5NY²|\àsJ(ðUJ´ù§'n¦Ñ¥¶“{ÿÙßf:ãðÝ}¢ Ïê:òÏ3÷}R¤‘ƒm¨Y·‡†_¶Ðð ªÒíØþƒþfR­‡½‡â´÷LÇ8¢" ¿¼~9êýÒäwOãá—cÚq)8”kR4àw[.+ÕÜÐ@Áó ؼ; Güj.õˆ·àMÞJµÃ¦-„"[˜ˆ‹´Ü´à×ú>αñÿEdS—«×³5¹†zŠ.¸â´aK~‚÷sl¸bÕ¦Ïhæø"¢_šp^RÁ›ÃþÄøÀ>ˉà„îHý? —ŒÎã€Q©·ï}öfê9¨w†Š-…õ‚¶‡ôÚaÐk;âl¾ñ»)áé ¸N¯ˆ¾8Ù3?¤þõŽCÿ¿â“’;ŒÚàÉ|Ðk³á¡Þ´å õö6ϵµÅ7儾Œ/åÇ ÷œ¾?.Âñ7ðx†5Fϰù뢎g¨ðBŒg0]fÆÈü |+&HìQ|âl˜ß“§šðýÚLJ0&”¢(Ïà5b}Ò6ê¸í™ôj6hÜÂSº¥oAûwÌø=â©'BúÕWv}!%n(ͤÑÇa¢Û÷µ!û^÷î –Føwh­_6”¸%›lNýà ݿ#sð˜lÌ7Î5ÐûN„ò]�¥µÞjx÷Í÷¢½Á¹q¾Á‡³°í}ºP¾á›@ƒÿ:CÚ v°[‰ðÄ¿©£âÓÏ‘ðº.½G´ tëF"Ü ù '…Wƒ„ÿþû#Yæ`Þî˜üú{~xÇY×Òr÷X«øFž}’&ÁÍÍóá‚áž§Ëø18ù…A TðúÓ4Ü b%º‰hV›OGô‘Ô¦_@ƒÒHP! …ÍE´Çw&'µßŒ]¤É¸ë|uvƒÛÕu8$t¤;ë~ ÍG¾�ãû¿‰p�¹ßoŽø‹\oÿ¾Þ^_j_î½oà¥Ëdµ|!¦öºþH}¹p Ôõ’Ë!㥃Àâj󎇠/5d»-'“dÿ•æŽ>j¯ÚúŇá_•7ûú›:¼]qêƒ^\îÙ• >¸‡©­ÅPú!aÛ´8ZéÿWü .5Îwrøn†ÄÚ½Ýfu½æõ·õ··~kmê¶jgÕÆgpĨ%¬ŸÚâ¼Âjh1eg¾qä©çœ(Ô§Ä 5,¯êËÓþzèxZh®®}À.;.ÖŽC/žÇɘ8÷f9´c`v` t7è/ŸoÐÏ}îG[2¹�uírl\¾ƒ@½§‚U�¯à¡zÚ ŸƒÎ3evÜ׫˜ìÞ7ñ# %&ç-«Mhñf{ÏÆ¨âiïdPÑKqù«÷ÛKK¾ØkÈbQÞ{bãú q¼Åá×¼¯}—Øü¹q`ÅéC#GÆ6{oJP qíuæûêƒ8ËÇ—€Ö&úñßeÂ6ž§Î:h¦ÓÞSý<ïäøÞ³Ð>¸w‚Ó½½­ÎÐ;òºˆ·­_Q@ áj=¶µcÁaÆòòž(ÖðzOÅy~ì=ãý y:*ìÏ¶ØØ|W†×czOÅd«´xÏÇyâ­-èíÌFœ2©ëZé5td±Ñ¶ârÜÆ{´?„®®û/Š«¿Ú¸¨÷Ѱa9<¼‚Ñï Ù©mýbÂñwÅRNTºÂB¨P¢? ­{ÌñÞã= ±<‰õõµ°¾ÌámÜw I–cðÑ0Í} 1j3ØÇòžóïç~§ïO‘35¾6ã‘· ´]$J¹âN»÷*<n(µÕÝ Þ)Xž¸áznï´jq£²µ›Äø¼»Ÿ‹0þa[éøgm¦½Ý_"]6Î[Òû4È‚až&oÂÙ å™­¾ô¦q¿»(óom6ß}‰mÖ'¸Ú ljѬ«MÇÞ€Àjù÷¾¢b˜,ã—EÜ›(p¼ŠÓ§øiÑâæ#7ðyËOÄZÛ½]ýî›ì›³)vÎÛÞÓ ÷¥Ù¼;¡§±h³6g“f}Â=¯¼W CE·¾[9yâ›’xLÌ`ßFom¾aÉêËùpjógCõ:•ð ½Mu´Ú½ß˜0PÇ€�†ëðW%š<òY7AÙÿþ‚Ç•ìÓ}cÊö¾Ó—¦û¬[µªQɸÒpèþÚ,-ò<b‘LU[‘ª@œHQ2ïì"ùøXŸë]ýø`qrˆ¯`‹eŸ7ëíŠqO]sÚte ÔÝå×Và]<N  EùÌoÈSë¡AÚœÇX>£•sHŠåȇ>ë Þ�„«>¶ãÈ‹‘ó§íÚ^1$wÿ×b16v'ç¬ÁÏN«›mÚ‹tJ”w%®zÓh¤nõЖÍ4bTÐ̹ðA·#þ(z7î*«Þq®<–ë#÷aÁº‡‚Ï5¸¼|‹eZÁÕìËîðÒ‘;cXDôU¾,&¸˜UŸ·à›2ykó´ãZX(vߨÀÿœ3¼ÛÔÆ3˜.ëãBxÔ‡¾&çÆXëÛØ8=„LAÓRà‹»J}'â¦LOŠk/Š´Þ¶kŸú 6æig ´o ´7>ëÐOn‰W×�gSŒóñJ[³}so+ˆQK,òÀajÁYmY ”^ï8™+Áîý6!/óŸ÷½è€Jï1ú(F­`#‹õ&µiDx>7ÀÀi‡¾£Ïê ¾;“}· '&Þ¿ ùX‘Òc)¾Ý7<ûõþÄ©ñW‰Å.] 4ßgÝìP Nã&T0Ú>  Ú[h» Lß>…VÊ¢M`KlV›p¹QpyhÞ}Ž/ïÉ-¸û"îÀ_·–˜aþí}å?x†Ç%J­C+Ÿ€Ÿð»:íCZY1¥ÅÞGV–Fë¼âĶÀLHФD¬þ Äõ,kìòØTÂ޸ϓ‡r¤ËdN4™t L~ÞI±ÝßJ›i›ƒ÷œÃ È»ƒ?£ET$Ú†P¼ýÅèóP'‡ U¤oYGI^n÷|ý€ÏÞ.Óò~´h'îKjRÊÁÊyÕ¦M)o›>о͡Mä›6\Ì›v}aÊhT=àñÊ–òøSörmú0 ½¼ûŸ––r{¢Ïë³÷ë ¨ÌÌ£$7Úê P›Qܘ?xp/+9ÚÆwW¢!W¨Ãþ{p €¯:Q™7ªM4ý%¢²6ÆÐ¨!ÔBhItB”uÓ«díqå£~¾ç“.ÜÅB×Ñ'§ºÄèì1…6‹Óî= 5õ97 U¯>´_¡ì«¸° ã?�#ÄãžõÒÄŎϺÔ?ÁŠ¼Ï·úñ<¨ê±ò“ç3ØK;±IÍÝ.Ð/ã bXlšÅæmMÈì¸wt¶oücÞ£±Þó±Ë÷ÙJOrDÇBD÷}L{ÉAÝÐ \Iß4ª[ÿcÝ@[Á½(å ]Ìî"³ÏÚØË¾ŸtQµÄ* Us+7EØ9ÆøíZñg]D‰û(Ŧ豫MA=âÃy s¶xÇzOÇ.¿Á7ç~ü‘­tÎã¼òÕd}Üæöud»a½Ä¾àãú‰P¸+ž‡*»ÑkݪضØ&±¢:<UêãªXò †öîí^rÙIZpu3g©wuã`Ïm¡öÈgÝM’x^~ ´q,›W¶¤µÄãðÈ\ÐŽÎKò‘·YE³)ZhÉ5Ú⌅Ãtè{«Í·1ÀÑ ¶)-ƒ1ÄÉ8) ×ùùã&Òvp60+ø)X{Ó®.Ðvæiívßv_}‚Ý7 zùɾsô®±ç‡:ÀîÿVôìîýP¾É Ú¸ŸÕ‰yêKï9üø%D),öpéïÙ݇¦‚ËW-\¸î‚ GЧ#\óàr2Í|­æ{óÎf›ú¨Óþ;mêv¾¿ŒwÀ_'·&\é\ß©Cúïl>Ž¿ß¼ÓæwúºÜ®Þ»Þ:ÙŠ:ØTk‡]µî�Zªu<ÅÑÓ¸öÛ´~6mnBkpÐwOì,ÏVksÒð7? gáï‚%ø»´ŸB=‡Ï5óÃ64ê‚4uÂÿÄ(±RÊ(›æ[LÛŦ9F%;À‚2;à)E]ûÉ͇6ÿ £„=dó?0ª›¸'FÒºtìÍ{;z°w ¸òáš ×bŒ®›øÊã+Ÿ¯"¾ªø¾³Y})oT`§ú2þúðä oß©‰ä:Î÷«Ä;Ǩ€ ¬¯ÎïÔ{£:ÿÈÜÜSnSkZÿ¨Žz€vìl.WïË•¿ŽQYð[<ª~oµ~ËFÕÁïÓ£ˆsê}/ŒjEœÃ³Øý}Þe#¾âÇ8®×Õí¾Òæ±ðÇ87ÜGº;pT^xŽáÚ¶…‹v*¡?ZOxÒ mêsP»w’{™é½Iéûož­»{"\WÀõÌ´îî“Óůáùeƒ»·ëç}Ь€w_Ïìî~óÿÅë,\à »»§ wYaøÝðütá…Ãx»š¯/Âÿÿîõ\Ü÷[!½pÃ× ¸Š®÷çA¾àú~.XDp ƒë‰9Â^n~÷¯‹á®2¸¾/Ì×°Ùà‡Ÿ¸ˆr¡ÿW Ž »»ß…ë×p}´@`sý‘iëá^°@ÜñºÂ9p[ww;\É‹º»‡{ ßñš„,ܽ]ó 4}ûüþŸ¸·‰]RŒÓXGý˜¯¸ráš ×lmGý5ªð=ÏCÙÃ÷Bw÷'Ï‚\?wq×ûàç ¸†Ã5þ9qÇ«ž¿‚ë¸_ØÅ\5L;îip]— ®¯wwçÂu=\¿ÖÝÄw¼Îoïîþj{ØÝÛÕa yE¢ÿÝö0C÷¸¨ú+!tú>.t¹€ÿK/ð~ÐÞ_rø^àý€¨ïcB÷„ ¼ïõ½)tïw÷ñxù7¶¼¼r>ý)J}ê1\OÃõ6\y ö} ?,î·}õ–¯:Ã…øÃu{gw÷¦câZ‰W§¸çÏ̾ќ[RsÛ\âv—”V˜kJÜ•Ë]fwE½«¤Ìì®5Ï-0—×Ö›K«j<õ.sEIMY•«ž‹ƒüzÜæÚrsµ«º¶~U(¸ªªÚÒ·ËŒ·*sy}Iµ‹éÁ]µ´¤ôsíÒÛ]¥n²Á¼Ôåª1/+©_Z²Ìe.­­ª‚®²P¡n¸Ñl¯Y^RUYFÞ+k–UÍrW»²¶FQn/Y^2¶ª¤fÙX;x]VR•]¿ÌS o­+K]u‚F§Á]ï)ucžÊ\å•5•HeNÑó¿ªÎ°ÙU__[?F‘òÔPWRêlÁDÔ7 WàU>$Ã<Û]É3𧼤²ÊE¼,¦¢w=æPþþƒrЙÇo(៻J0ýz€a~Ì©iðÔÕÕÖ7 ë\õDfÊì0ÃXy­¹¦ÖÒ]ibnšG—Éá3¿g»!cáps<åå®ú¿sµ§Á …l.«¬Ç"‡ŒT”@öJÌuõ•Õ•”UÌ–kI}}É*ÎGn­§ªŒRT»Ô]RYÃBFœ0ø¤Ø~N _»¬†ÞSPnÔ3PZ[=¶ÁS3ööš’±Ž’·‹Ù +Tc`õ.`S ±ézs@Ô@e–@.ÌÆ(åeÑP^é*sTÖÜA PXÎ8¯¥U% úêTHj*$5µ¨¶²ÆíêAW[#�LUõâüÕ¹ ˆ Àa{Þ…|ryý/«ª] U»ÞÌwÕè¢G3I’£ø÷ԕЄØe뮨-»ˆP%Åæd.ÒN6�…¤V&qKYø/(©ƒz=92 »•¥¡èëk«1=ö”žéÓé‘ïÈøDE/F¹¹˜øŒy*®¯rI A1EåB¨ïô¸k/:÷rz¦…X×WŠúdY8‰=å¥gZBê+º|ºÜÈV;ÖÆ>e+L´ò4æ0D99J|ÌÇ× ;TȬ­T/\BÍ]¬÷HÿÔ.\´×žùXQ_é¾ø¤÷ô¯ëè¾5KDþÝz¡]|+'©9«æ–Ty¢ç»§¿\nl#Ú œ¥ë½QÙÔdRÛÑ🄠"U â?õ߇^4&µùä‘R­×§^„Ú¤Óõ¦z&+%z<Rüz²úÔKÿ4OM)2ˆé/>|aÓk. ¯!BÇ ;ï?HD}âGT´ùŠP—ÅdN¯¯õÔ]¨ Û"ÂÓ”ð Èã<Ýæ½�±îÉçPËs†�‡qøìP¤Ë\õØ D)×å¨ .¤d"ƒ¸8û¬—ø.l§…øØÓr=F§nü_Ÿ#½ôLOeÍòÚ;\F»ç¢ƒŠÚ&Žš6ÍŽY×ï Ç„aWU[K–½§ÎœŸ=¯X;c£úª–¹¬¾dÒBg³;YÐ㺣¯÷hk+ÊÌÚš¸kÁ]ʲÀC‘ÿñ¥2—––O[:.m⤌t ü·,Í(É(+)ƒ§½¸þtúªjK°—jÔ/¢ #ª)CnQ}íÒ*Wuyè9ØóˆÃñ÷¤Óõoˆö¢Óé©á”Š¿húfÔœšÒϲŠpŸÙ&úú@UAõêar¯z.BsÕ®(üM3÷ÿù¸�ù£>º˜ˆà’KOÖõXõÜ•5x=ð?(_cûFêïý§ã†Þw8]†®d¤‚òMCwx´€SQ‘/ÔŒo¨_‚=Ó2×ÊPw>‚Þ¨`£x6 ô¸˜q˜â‚¢<û,eìò’ú±îê:el™kù؆ŠjÅVX`•GZr±o*ÑÐ~{Ü…åÂ@¹ÕtØî,)-u54<*0•niEI} ÈQ}ƒÝŠÊ2Cœ£ 3í…<Ð JNø¶²!ÀÑ úxOÊè²1ÊÌpá™ Üë”âZè”Ô¬2 ޤT—@YTyhl ¢#&cQûâ³Ôe£…òšŠyMuÔÖ,›‚MýM)ùP±Ä£’2fa¾‚Ý}%7lLlb©?®„{ÂJ¸“ªè­tô6�¢ŠnV:cŠ¡‡$i S£÷‚ ȹJ‰Ç]; ÊŽæ!ÓQÜŠÞ[è¥MÒ þhú¨o³^1X¼JOëS‘ AE6°"B×[öÞ†$o={ÁFj£!’³ÊíJ]Þ#_Fó£ŽS­eš«¤äç£x @Y^[ 2 „[©ù)«õ€^V@[º') ô‹Ï– 䀺ƥ“ nèš0ž\pëa ˜«+0ë¢Â`3DV€AçåA›ŽMRU¹ôö’îÔ†ZIœÓ]îHJ©õ7¼™o±˜ëªJÜ ³«…Q0.u|jš"3U‘:Hw½«›—±dž(2ßPSY;V˜7‡Œå‚J±Ù aeÊU†°ËØ4,Ì£’2¢”Þ¹Xž)QF¢Šo-²èrjk«\%5Frbä®×ŒÌÀL½K‚É78)å·H´AQˆ¬‡fƒT†ß„âç·¹o1±ü"'ò…È¿[ñŽRÃo¦E¼éâWyð ëmu]m (iªS)Ñl:w-‹(ÙôƒÉjˆZïà=å‚ÌX6s‹wëBÌô,9·¨m ®ß ˆˆÑgNšÝSëjè[†’¬T1¤.¹ ËË¡EÂèfã¦áOž²ÀXÞ«Ü®jJ@}m«Þ½JÁ~hÈÚ2j$‹§M2Y¨R ·¡Ö €kYª¡–¥‚,§¤˜Å"\ëAd-#Lªe„ɵŒÀÈZFTË“kµÌ@ÁƸ?‚P†Cz#{àJ8·„2¬„9A'Ê(‚ÔIJc€#E^ VpÓCYæç•Ï€:êaxÈ Zµ—*;ñŽ#äPØòÁn ‘Ÿ9Aì2fwz¾àº™j¨®ÀõŠž"Ü=ñ1ŠýT]A�B•#UÖ+ðBÖB²Â’,<%r ¤LˆÊ¨07#1è Ý=)a¢’]YWïªs–V–÷ìƒD1V½?Уè‘û‚!RNH¤Óœ’>†Gp£·®‘ù°¶ßéÞ{KtºO?Ê÷ó|¿t–¸_Å÷T¾ßÄ÷™|_Ì÷¾ßË÷ùþ¾?Ã÷í|ßË÷OgõÞþ޲¿ó|¿tvtº«OåûM|ŸÉ÷Å|¯áû½|ï¿áû3|ßÎ÷½|ÿ”ïG¥øÏ³ûÒbNßS‹{Ï“ñï&¦›É÷Å|¯áû½|ï¿áû3|ßÎ÷½|ÿ”ïGù~žï—Îáôñ=•ï7ñ}&ßó½†ï÷òýA¾ÿ†ïÏð};ß÷òýS¾åûy¾_:—㟫Dü¥Jîÿÿïÿ‹¿ù2‚+`/ôÞH#»ÿoýCu§‹šÎXxM߯*1JV”éa±ðos”iS±CÝðhx¼r`d4¼ŸÒn‰†÷WæDÔ´Ühø�eC^4| Ȇ_¢lŽÒ^Ä*ƒ”¬(õ2V¬´dôdP¬r©ÒW•ƒQñD¥eJ4|ˆ2®0>TY²°çü»Xå2eÉâhø0¥Ë OR–DÃ/W’£âW(æ¨x²²)*~eLà?èÿa/xôFÿÿ>¼§O½ ëX²òIE$rIR%¾Þ³Q8=ãÅ|ÑêoN/øÌ^ð[ ¿§œ,#¼§œxïYß7ÞS~UpNeÏzô'EðG—=‡Ûß̸>[³Ãi‘ÂyŸé[˜>ñƒ½ä÷‡cž'ÜuŒŸÕÃ)‰Œ÷2“ÀÛ%OMÅpš9œU<ís Óo)¤w0ðÆ;%üÆ7Kx3ã[%|ã]þ"ãJY$ÞÁx‹Dÿ9ã ýIÆ%ü²æÎO–è'0Þ!ÑOcü€„Ïc|’N5ãY®1n–ðß3ž"á[O“ðýŒ”Òó/ÆmýYÆ‹$üÊX!'‰‹„û,ècb}³DŸÍô-‹…[×,3™>‹å0KçÓ/q ÷{Œ/eúùRøË™>k‰p¿Äé¹é“9|}>µqEÂƸYªO0ž"á/r¼ÊRáþžñ7˜¾BJçÇ:}©p×q:3^Ǭ^`ü ‡³FŠwHœÀ7Høˆ8.—JáÌá¤1}”+ã‰R8³OðRÆ›%ÜÍøF ¿Ÿñ ÿ=ã$|᪲$S$üzÆwééY*Õw=ýþOÆ“%¼“q³„ã¶&T¾žÈxš„_Ãø$ Og<KÂó·IøÆ‹$¼’ñù¾œñ%þ�ãþãuþGÆWJø Œ¯‘ðvÆ7HøûŒ7KxñÍRùv1¾Q¢¿´·#ýpƉ>•ñN‰þfÆ»$¼ˆñMR8åŒo•ð»? áÍŒ”ðÇõôKøkzú%}»—ñ‰þ$ã[$|PN¿”¯kß*ᙌo‘ð[o—Â/c¼CÂé/ôIš[¸§óÌ„_3}‚”¯¿0ž(áÛO–ùÀ¸YÂ1ž"áß3ž&á± ,W¥‘v׌gIô)ŒO’ðIŒo’ðÙŒop'ãÍ~ã›%~þŒñƒR¹ìLàöq­pá leú•’Þþã²~c¼BJÏyÆë$|È�Ö3>’ñ" Oc|„g3n“ðBÆWJøbÆ—Hx-ã]R¾ð¨j7× ÷�^ŽõÓ·HüÙ¬‡/á/êé—ðß áÆ7Jø¿ß$á¦\î>˜ñ-žÌøV Åx»lG1Þ!á ¨^&*iS#ûTNÆ /c\î§W>D™%õÓk¿]Âo’ð•ŒÿJÂW3þ' _Ëx«„¯güS ÷3~FÂf|hQ$þ Îï‰ÿÍø ’ñ ÿã –qóM‘øËŒÛ$ü5Æ›oŽÄwrúGJéßÍø8 —ÃÙ(…ÿ^¾þ1ã]þ9‡_&…ÿÓo•Òù ãÉþãK$üã$¼[—ϬH<þ‘ž:)=—\"èÓ$ú!L_$Ñ_Áxƒ„`üW~ ã›$| ãOIøXÆŸ–ð ÆÛ%|2ãïHx6çkev$>éÿ!Ñ0½’‰Ïfú€D+Ó×IôN¦ÿN¢wéáçFâw0ýY‰þNƇÞ‰¯`üǾšÃïÂ÷2ý‰^c<GÂb|¡„?ʸKÂÅx¥„ÿ–ñz ÿãÃ$üÏŒß#áÏ3î•ðW@Â_×åJÂßdþ¬‘ø[LÿºDÿÓ7O‹Ä?ÔåG¢ÿÓ›§Gâÿdú DdúÍý·º<HôÿÖë©-?ÃôƒfEâ¦A!áý·Hø¥ŒçKxãN Îø=~ ãk%üÇŒÿRÂ-ŒÿNÂ'ž¨ÌÏÄofú­ý4Æ_—ð§K §˜éwIô K—2þ„W2þž„×1þ‰„¯`ü„ßËx@›ÿVÂ`ü„„?Ìø9 ÿ%ãq³¥zÊø%¾™ñe’^ú ó³Hj/^d|‹„¿ÊøJ oe|‰¤¯ÚO‘ôê;ŒoÈ‹Ä÷3¾UÒó1Þ)Åûãiý¿o–ÒsD_JÏ1ÆmRzN2Þ.áçW$<v0çKÒKOp•q›„'1Þ"á?d<YÒc#Ÿ/á×1ôL*ã‰v©]f|„Oa¼C ?‡ñf)|ã$}8“ñ" /f|‹T¯2¾R—2n–ðJÆ;¥ô×1ž%á+ôx%ü^¡½–êQ“^^3¤úËô×Kô3>EÂÉx¡\_ ×_=¿R¼aúr‰þ%¦¯è_cú5ýºÜJôo1ýãý>oýGLÿ;‰þ ãÏKøWŒï”ð£Œ"áÿfü ?Ëø9 ½TàCŠ#ñŒ–ð!Œß(áÉŒHøÕŒ—HøµŒ/—ð@ÂÇ3þk ŸÌø_$<‡ñ$ÜÎø. /bü ŸÇxPÂ3~JÂ]Œ_2'¯büÞÀø%ü.Æ‹$ü~Æk$|ãë%ü!Æ/á1þš„ÿšñþã¿’òû'Æÿ!Ñ?ÇøQ …ñع‘x ãƒ$¼M—O ß«óSÂ?`Ü"áŸ0>AÂÿÉøL ?Ìx±„c¼R»¿[»×$¼Ÿ*ðG$|0ãðaŒ¿,á?d|§„ÿˆñv £‡/Ù9iŒ¿#ÑOd|Ÿ„ßÄø‡ne\•pã»$|6ãã%|ã> /a¼CÂ+ÿ@ÂkÿH—ö†#_Íô‡%úFÆOH¸ñØy‘øÏ8üùRø¿`ü€Ô.lâp¤pþÀôiR8Ï0}²Dÿ"ãc$ü¯ŒçHøNÆgIøÆK%ü=ÆWHø½¼$ü3Æ7Iø—Œ?/áß0Þ&áß3þ‰„Ÿaü¸„Ç$ |à|ÉÎd|¸„'2>N¯`üÏR¹_Åø¯%|4ãyR8×3~›„c¼FÂod¼I³LÂmz:%¼q¿”ιŒo—è1þ„—1ž#…sã}=ãç$|ãnÄ×0þC _Ïø%üAƧHø£Œ;$üqÆ—Høï¯•ð§¿KŸe|ƒ„oe|£„¿Îø ©º‹ñ'$úwNÂßgü- ÿ˜ñO%üãßIxñKDâŒð“Œ•ðóŒïÚ÷ø!ß,áƒ@Â/c¼AÂÀø Éød OaüYy|•ñõ>q›”¯©Œß*áyŒWJø ¡?(éçYL¿R¢¿•ñ%| ã¿“ðeŒ¿.á5Œ¿-áž!Ñçw­Âó»Â=™½Yôy2Aá~‹·ñÓ¿C”¾ùôï¹Âý)‡£Ï‡9P9濘?[%þ”éße¤ôëß›ý¦?&ÑWêß $þ0ýÀ…Rû®O‘è_#ákï”ð•úw ׿mœ‰ëßq¥ïMât—Òù,ã?‘ðõúw")ü‡o—pý;”MÂõïV+%\ÿ.v“ﻌËýVý»’l‡<Ö‹þ–å°î´p¿ÉøK½Ðoïß­ËóYáÎaüÃ!ü]•ç—êó?Óãez}c�Ó+.A¿‰çqÃô™þ!¦*æMâïžú®œC†røç„û:Æ÷q8›?Ìø ç2¥…ç—&3~#‡Ó|^¸¯¯•…LÀY¿–å﹑õôÂÃó“W2ÞÈô*"Ãù-Ç»…g>¼Ìø ïA){‡F/—¿éé-Âyžù`¼ˆ'&¼Äô½Í¿=EñÅ÷£y¿±J…4µxá=çÙš/ñ®‘æ™O¸,z¼®Ë¨÷¢lb©ç×øY›�7ËÀß/ˆ¾çüùçŠó “E8b|PQÞÐñÝ­ïû á=ÿ¾gúŽŸˆp*™Ïç8oŒLç a‚^Ù,ÜŸ1~=ãOGâŽaÑãÍô[þIïf¼åçÂ}5ã_%¼Ó+Üú¼”ßêñžnžþ¨¼Êx"O@Nb¼ƒñƒí­Ëù¹^Ò›„xxÝ^O$1Ÿïî{˜oWè8ë¥k™~<ãÊo…û$ã]”¯ðz}á[“ÿ•)‘ü¯àp:™?O1Þ¤ã/Gæë =Þ/…{ã»ï`>èÓÍ¿ÓñÇ#ÓyååŒÿ7ç‹ù?õòè|³2}Úc‘|¸ãr–)_«‡~Ô^¬ápšon}•fÆ[¤ô¿ÐKz^eúD./•ñ777 ·Þ-éè%œõôüE¸?aü¤ÎnˆX}˜¯àry^¸õùÃ73¾„óõÆg2n~’Ãe¼ñDnGôòm|HlÏu+¿dz…åDoG¶ëøc‘øÛz¼¿n®NÊ1ÆÓžîýŒ_–,ð"æ¿ñ¼äè|›‘ÌúPÒ'N§ùáÖë×ý½„³žéþƒÓÏåõ㉟_d|§SßÐè8ãf–}…L7¥³çú¬„+Y_ýR¸õz”z%×SIŸßÌôæß ·ÎÏ2Æ7q~?g|Ý•Ñóë¿2—ñïa‡zÀ›ÑÊ :Îûdüm §çz™÷˜¾ãáþ˜ñcŒ×m‰Ä‡ü€Ãç}—UîêDOÿµLŸ¸U¸ïfÜÚ ý|=ü_7‹£rW®G¼C®>]ìî^ÂYËá˜ÿ Ücüw½Ð?õQŽk¤rܦ§ÿÇ߯ñ¾ËøAn¿búïOcþ_ËíÂyÆ7mn^®  ÿ!ó?]\Ãá_ÏxÖXèË|³/zV¸õúXöÃèùz½—v‡Ó|ýóúšßôΘ^/]&ßÖÓÿGáÖývé83fã×þO†sz˜~ãs/’ì™ÕŒ/aü¾a⾡—ðbú4¶®a|ã[X?è»çÿ™ñ5÷ ÷ Œïì%ü=LŸx§pÄåøwÖ3¾ƒé?fúƒ¬ŸCóÿGD?qó‡õ¶ÎŸI½Ðg1}ǯ…[oGÊo9.ܱÜp.g<ñQá>Êô^ÆëL"Cí— üQƳηÞÿú]/éù#Ó§q~G3þã[Ø>ÙÇø‰¢>¶Hõ±ßUÏ’ð˯ŠïUW1Xoëz8qó‘ü©d|‰d·<Èø&®i\¯ÿÀx'Ë—ûnÆ›¹ƒrã7÷bWé%ýßq87pó©ô»šÓùóÈtŽa¼ãW‘ù-¼šíy‰o‹_"á¿NŠžžßÌvËÍŒ¯f¼™õ|÷G~­§G²7v0ÞY+ÜfÆ;ôp8_?Òù Çû?­—×çC¢§3ÙÌr^ÎuŒ'²ü³§d0¾åEáfóB™«ã¿ˆŒ÷3ó“ív½ÿ«1}çŸ"ÃÙËò_Îùd|³žNÖKÙŒÿ•q…ù©ë‡ÌÑóû9§§E²¯6s¿i ë1½yŒÃ_R‰ÇŒþà‘\Ž¿‰äÃ8Æòz:ŽF¹ydôþì]X¯®nã‹8œM\^—1~'ãfÖã{ ÿ…^Ò¿›ÃYÂçSèÝøŒD¾]¡l”úû_0}"wLïÒíÁ±®•¸}ãØŠÚj×X÷Š’ª*Úµe©§²ªì†² ãÇŠüÆâk5•e• u%îÒ Üh­´Þ]™Ú Ðî8 7Ì/I-E¨Áí)/‡G§3·¸p–ÓaŸ]ìt‚+/Âeµ9§ÍÊ.°:s¬Óí3 ÊÏ5¼/«uŠCœeîÚúg‰g%n*SWår»ÊRÇOJËPð…³²l%º&*´m¦³ÌS]½J»€n™¨„\ŠQ…ö˜£­¯¦Ùóœ!ÄÚ° 6n?ˆ[ôÐ6?b.ÚóÖ°¹z ;¼UR7¢Wm½Ea6§Bš°Ó¦ r˜ä߸‘å&ì6„Ð5Þö IC.öN$†m°ÈmÜ =܆X{ fHÑÞ„ã2%ƒÆôGl½…ÄFÀFO8 [ãäckˆØ¸—±“·@tºV–†ˆœ¸]¬k¥7ªÌåmÈXœàÑÚã 7r‚òkPœuõµnÜæ ƒ¦íhgmU™³Áµly8TD–zB�„>¯²Ì%ãqIílÌ2[VVÛ›6èq¼á&rÎ¥”4!@"àp.¬ú¦mQr“.üÌ3¤fž!\xƒI¹¾&ɲ¼ƒ¦Ó¸ƒ¦¨=6ÙÌ=¯8×–jIËœ$Hh·0¶~ÀKÒ7‹|ÒFaÒûÞY,’BÂņd‘$LßÁ,’ ¥]º"߇!±¥Wä[Æ[€E¾7‚áÝV#¡½N¬®ˆ½F™ j,ÊN58ÅÞi‘*•¶¾%Lî4lkˆ“(zœTb$žf,Ú¤¶Wa’·`Tý¼!ld*{ÍF¾¡=Z#¡ðž³”ÈC¢§ªç®¯ºfÑÛ,J%‹(3r·&'‰—Á’HÆ%b@P¦ØYjÁUÅ�)w³šR?$i†åÒ“wÿ•ÉÐ|¡ÛÙcOLCᑤÔ]H!Dì™kˆ_ßWCa9 ¿@ˆ&Ô‡US˜_!ˆsr‹Œ‡œ:7C�13ärê8Ù†M’Å{MØÐÚ#l2§Ë¨„{„bÈ>•@Dk¥Ô·ÅÅ­r96AË{mŠêOûpÒ#ﴉщGgÏÝD#_6MuäŽÃŸtü±àÏxüÉÀŸ‰ø3 &àO&‘¤Ñ/QZˆÊB,ŠECäòn!ÿ鄤 z 'ÂI§pÒÉW:ѧ z‘& 9èÇý8¢Gôã(´qò8ò5Ž(Ç‹\Íx¢O4ã‰f<ÑdMÑdˆ e¥!Cä™bœ@”(=ˆr…6('Ph‰r"…6‘h& þÍD¢™DáL¢p&ÑÛI‚½W&…I4™D“Iád Æe&ó_°>Mð;M°4Mð:Mpépapùp¡p©ˆB°ˆR°ˆb°ˆr°¤sQ \h¢-¢,¢”,¢h,¢l,¢p,ãÈû8"OÏãé}†à<!ÿ OñŒü%"8LqLœ|¦·éí$¢ŸD!L)L‰IyI#pE)XMAe2oD>XY†YÄÓDþÓ‰9Ë8α`ÃxÎ1×�ŠH‹I‹È»Eˆ£EðÃ"²lÒ™. /]D”.Ê0ÝÂõCTQ”é"é¢DÓEªÓEÁ¦‹‚MçúÆ•Œk™^ÍD(\¹¸v‰òMçÊ•k¡(ßtQ¾é¢|Ó'ÒE1§‹¼§‹¼§‹¼§ ö¤ãº,BœH<K IÏ]„ õ5]ˆDº¨·é¢â¦ ™I8]Ôàt!Té¢&§‹ªœžÁ:G„"êtºÂtQ·ÓEåNR—.*yº¨åéBäÒEmOÕ=}k3Šât!cNàD.pŠZD","»EÄgYDÐö.ÄÞ"ôˆe"+T!|B•X„Ô[„2±=b’nañªÄ"´ˆ%“ŒS-rË DðÓ2“+!Øj™Äiaïâ]&ërN çi™Àar¦E"ë,‚u¡--BuZ&qª9A™Á‰gJ®+œw“k#¿ãP¸ùᬈÀX–&rƒ#X ‘.x.xé­rj©"ZagèÄ$ISô­u l&:K–-«w-[‹Ž.hÀñÑIm`:0êÄCu¶Ò JYUµ§¦º¤Nq.«v*µu®§ÛU]ñ¹Jå•sYe½Ò�ÆZåÊÔñã¡è¢Ñ¸j–+Õ%Ë*KEG±Ú}\ vâ1;®²Ôʆú’ÔôT:©ú´u©ãô_^F7<•"jȵuî†^_àèGc83*½HdcÂxâ X/•嫜úQŠ’êÊWàñ)ëÌ<p£Qº Ç¢ôçðPTix(ªÞ]“ ½w¤ÑǶBô µºmåœ]œ=«Ø>…ƒAˆpƒ;ó—;g¹–U6€UEæØU΢Y…¹Ö¼9³¬N‡}æŒìéVgqvŽÃ*BÓG'Êë].w]v–ÖV;<5N4Ù6 ÙÌsÅA³J¹èyé†a8â‡>ÂA#¹¯÷ÔEõÔà.Ñ}‘‰®poâØîï醫aÐÑ8ö¶4vëÄIÓ·È‹¸Ÿî©”U5¬ªVœx†/ˆ»ܽ‰+"µ ½ó{q ;Îq9v{˜·â4ŒÞü ,÷ú²šŽ—éõ5Fa¡‚ì¢!Z®{#&á ôÛ!ý½QÑù?…å@—üC9ì!bˆLïs‡8B§†8]ú@úw¯A5‰‘2(cÈÕíÕuJU(GˆS úâÙ.8z×ChL§1™9¬]!v'Oë+ èêÉ׳xAÉÈgÉ ^TbÅ9ÝQ˜“ípN›6ÛZ¬Wó¾™*F} ¹$õ´ïùcÏë£\!C<RÔ…Ó"(γÁ"T¾†£`úÒ¢c[²”TÂL»³°f5ÖW§õ©¨HÜu)éƒ(\œy·ÎÌ.°ç^D¥È5V꺋-ði}³œÜõU œÎÊÚ¥}ñ,\ê· ½ ½§ž(¨R5}g•tïIɳDë§õ­ÄiSJ8üL4Âdô®³ vÍ^U½´¶Ji¨\VSÒ»îâáe¥¼Râ.T¨ÐU0A�š²´¢ÌU×·ìäUð¯÷–ˆÔšÞN:=5•+¡™–+®³OÖ„ã‹dµ]½Ë°xäÅéÄñ¸šÚ ËYnr®¶áoU½k—¹i¡ö…ø„&XßÍ"бFë#%ÙEöÜ Wé žj®õ¬ó+J°„LýkEeƒþ‰ªt¸ ±Å×I@å—Ö­2.q÷š^Hªdd”ƒ§<ÇíZ78ê8l„.¬7På Ë&<§ëmb®ÑžÕ-ât²ª>m‘0l‹ú, .T>uÉhêGr­j�ƒ»ü"r×W“ξí L?@Y!*¼¯éC\—]°Õjµ4ÕwàyDuQ"¹@âúÔ›aVL36âô³'è,–QýSY_•О×pdqÆCU¦¸/&¼Ð÷° ×lÈe+,îT½K/d¤5\ÈÂ3 MdgUåRçrî›ôQÂ(ÜÆ÷zÅï«‚+8’NgêEPb—^íC_Dk¸‰¹p²‘5n<€4gÕL<§º{.]g;Å<Bι´¤A|æ5| ŸØ.¦–B­w:{6²=ÁZY¥”ÒG2d'ôèJ–APVâ.é5JO‡¢d¥\3f¶§¦p¶9#Õ’fžî‚ZWYjÎ/©ñ”Ô¯2§§¥eü$ª*»Ñ<»¶Ü½¢¤Þ%(JpðÃ<Ç]Yí«Á|¼¯*©¯l0ãá¤fkY%v¾o¤0o°¤Z20*%µlU ôëK•TW󩢬>ìRR+J*ˆ {ztwÃûÙsfÎ I^Ø…$ä¨w¡N…[Ij]•[I¥¾J*5Ý©Ô]L­¯%Þò=‚µ¤NŠtZ”ÔeµàAÒè8ÄíöÒzñ#_©tú«’ºä1â.Yª`(t‡²£1‹Ô2×RÏ2ý¹déÒz×rÝE©æõ%5Ë\ ¡7•5.ý™X u¢† Žë÷§áRI}ö‘>ï<t缜2¼4ߊ0êtÿúù!ú½LŸ˜&ù×ÿ’%ÿú¹úýa}ûÇyuñ†ôëç‘èw}^q¼ä_OVŠ"æüëþõsKô{'/Ôè'ÒÞ¯”ï¸./V ÏïÓÏ7ÑïÉÒäyiZ¿2 ®óÝݵzüú9(ú½H‰¿Î4…Τ ù×ÏKÑïú6'rþuz<Ö)ÎàÖÏUÑïKúE÷Z?Áþõu‹úù+ú½Åàÿ’(þË”° ⟾ÎL¿7÷"?ºüUJþõs(ôûòÏ”ˆ¿ÄH§R+ù××AÉë¡zóï‘üëë´äõZ½ù¿›ý‡ÊŸý…îý"éÍ’/ûO×ÓÏ ô{ƒ4Q–ŸŸIþõsiôû˜AJÄŸIºÿ–Ÿõcƒôu@KxU rZc(~ýþ?²>eÉbá£TaÌ‘NåiÉ¿~ÎI—3zùÉúãyÉ¿~JÂ’‹ó¿Mò¯¯ÛLfÿ2½ì~]ò¯¯s5³ÿ•½Yrïã0uÿúy+›Øÿ)BÙÿ‡JXöŒÙ'O°Æ“}L1=ëïg·î†MÂÿEâ²þû* †íˆ\_Û›ÜÎ-ZüÖ‚RZæÝÃÿÿPK ����v©Æ@���������������com/sun/jna/sunos-sparc/PK ���"‹g?%ßü%©��¨Ð�)���com/sun/jna/sunos-sparc/libjnidispatch.soÔ½ |TÕµ?~æ‘$É ëH¨PAK+U®ŒŠ5Õ¨¨9yA€¼ ÃK¨LWÓ[Z#FŒ”Ú‘¦J[.—RÚRõ:i¤÷rom›Æ\nšÒvDjs•¶ÔRK™ÿ÷»ÏÚ™“afÐûûÜ>Ÿ“Y眽×^{­ï^{íÇ9gÓ-…·:œ£ÿÏe8ñ__˜i8^mÊ‹{SŒqÆXÛ}ëÏQ½A†‘"w8ßh8q¨¿‘ã #·DšÜwŽU5†‡õ·[~Óä÷»8hî*½–nàŸ[•i©’7]®Ñ#SÎGÈïH98rqŒŠá—/¿cp°žãåüR‰Ië•ßËåwŽ+pL–óSp|TΧâøŽ«ä|†×Õ8®1¨åŸ”ßkåw–ü~ ÇõBÿŽ98|r~Ž›qÌ5Îÿ»ÅFßj£oÃqŽÏภGŽ{m÷ï“ßp”àXŒ£ Gy ÿ UB/µ]¯–ße8– ½í_‡£Ç*~¹·Çëq<$׈/âéa›p|G#ŽÄñ¨¤ù'_úK8¾Œ£Ç8Zp<‰c;ާp<m“oޝàø*ŽgäZÐvÿYùÝe»öœaáö[rþm{„þg{…þÃÂóPþ†Û謘{Ù6:O~Gã¸$&ݸ˜óËbÎ'☔ ü+åwšíÚô˜4—ßOÄÉüΖßl÷nLPæ§qÌúvù-”ß;méîÆqÐóå÷~ù-Ʊ�ÇBʵEò[ŠÃŒ)³R~—$‰µ6š>ð³8VÆI·Ǻ8×?g£6z3Ž-B?"¿M8¾`K³ÕF?&¿ãØ–@ÖÖ˜ó6úk1÷¾Ž£ Ç7äüyùýfÞüÛ‡ã;8ö'IÓß'x¬Ÿô˜søàÇ(§/ÇòÍWŽ›M¹Ïó ÇRÊ™mùmØú0~×Izøšå‡QÌ!9‡ðÑÍ“s´}?páh•s´ù¯¾ŠóJ9?#Ï £ë~óåþïcîÿj=Íï@±}†í>¸}6û69_00¿u«c=Jäþª˜ûÅœI÷—Ùªí;¾n8×¶Ê9ú)üLí¡h}?ŒÉÿ£˜ó‡;B»r4Hú7‘KTÿ޳p{Пã¤uîô¢Š°ó%Säüšüœèo6”DõåD_ò5›>œðëÁÛ9pûxŸíü{†ñ.û¼b9Có÷ZüÿSÞ9@ö3D~|c(ÈÛrŽþõqöqmr~ûÀü.àço°AjPî?åGëï‚.¾¿áh–óçbòÇÿEË˱üò5^é^]ðé¯QbWa¼pØÆïô@~n\ûô«Áƒ>öë%Qý¸ïŽI<53þxIîïˆêÃþog¡-?ä/.ŽâÍý}¸ y¶óC1üÿͺÝ&ÏÛ8ß#õÅyJ–ᘅ~Â!xHù öåÜ"çè^£=ô}øâ/±Í•s`¹1œ»SÎáëjçFõŸ²CbFÃjï)èc‹à—œûä>úÍ6›|)?7™¾¨ýSÂñ“ò.üÇq£ß¤"&¬CÜᨱҧ^b8뛥<ø¢TÄ\¯­‹ÊŸŠ¾ã1ØÐ˜)ç:¦}¥"ž5EñŸº'æ>ð¢ŸÜ*÷»t|›­âÎÔoĤÿUÌùI؛勼iÑGÿQ$ç— LŸ6Ùpøˆßj¹þuû]Ñwâ¹>õ‡iˆ·¾t6ªÿ´fÃq#õ»Sοüüœ=rÞ }žŒê3 ºÝ€~É!xLû3ŠöFïÓÝÿ°8êoÒ!oý×49Ÿ;Pþt`÷@Ÿ-=bÀýÀ»c¾œÃü±çå}Ïkh_cL9ÿ×~?BÓ< Bð™þ+Ã1õ3¤þég¦†þê»è×Ò¿ »õÝ­Ï0ôOK_µC¿OÙüÛ0Ä· ¬ÿl9Þ¿‰xß!þfØ3ÈßnË<¿–ÅÛ°£†sCXð8nØëHÖ–þÏÒ%KúáƒÔ0.8(þøüŽè0Þþ Ãé/üPÅá7 ¬ïpÄø§Z~5|EÌýU†ãÆén«üáÈÓ•g8ÚÎcÅQü ÿ®áŽ6àh‘sÔõµ™6yÑ~ÙÖßgà§‹>h†œÍÔ‡´ç øïoÑä|…á˜Ky‚–¿ÍxxKÊ“ñOåÏ€mØ_º?ˆ{¾Üc+ÿœ³}ˆ?Éx;&ÿ;†ãâOú“Œ÷ ã_Ù~¤=eBÿÏÿ~9‡~ïCûvêsŒ—¾º/ªßLø–' mçÀCu^TþLô/OTGñ”ùšá¸¾'Š÷L´ØÚGlüâk·œ3 %ìgä¼?¦÷Z?¿X¿¬Ù1ç#v±?ÿ‘å7ÃLœÊùã1é1fš¿;ª¿,ô7'Q?·NÿS ‰ÆÛäýê;-Zß,èóÇô¢¯òñјó«cÎ1ö½Ï•wÆ ÏÚâ³ô“†ãJè/KäÛ¼K-öxë]æ4þ¸ÅŽø †¡ÝÑúø%º^ú3ñ7.ÜßÀñ©øÿgÊÇ©…¿r¼Ôº?rªáøŽS˹íé@¿>jW×Õ–5eåõ+ýÆíekÊJ+êkKW­®+]^WVzg™ÙšªÒeukêWTÝ_¿¬Ò(]²dYiEYMMéšYiÿú†ªÒšúº¥•õ«ËkªŒºªµds3’”—U¬0–VùÁ²nMÕÊUËêëJ—Ô”-åµ{ý+WWøW¯¬º±²reÕªU†¿Þ*,¡+«Ê*KKo/ýøÜÛn3VÕ5¬\Vç_’XäUE+ëýUþªJ£²¦¢¦~UbÎkW.óW)Ö÷‚õà©nK’jYÝ2ÿmsWµUµ«ªü¬©Ö„®è*ÿÊšª:£Á_­jT»Ú_µNe3J?]x×M7–Þuë­÷Þ2¿tþ7ÞRj”VU–ùË U|¿ÖŒ%RÿÒÒeõå ¥Añ·Õ))Šê‘¼jå|XK ’¸ •UëîZ‚ªÞ¤,Lå¡ÈRÔ£¾–V­\YWoÔ”­òß²reýÊÁ-vk…õ'J¦Õ¨îoD÷ËÊj–=TUz5¿šØ4Jo_SzOÕÒe«Pý›!ôª*eІõLP¿ö¦õw–Õ&Ç’•UInBW÷®¯-¯¯1jW €Ú£‚¬ÿÉÛ­“(ð3×jS°_U݃Å-K–¼pP“0Új ú`×Ä‚²±V'óýd멬©J*@}°¿de}íP[; ¬ËÀÿ²:å?”Z© \K–¤ÂŸ©ª­_¹>iý,¶dÙ°²ª¡´låÒUÚ¿Ù.|*‹n»¹ºªbŪյƒØ\š¢ªÅrU«™V-µÚçýe5«[Ø|_»S‘0Iù7­÷WE–6‹ÖºR  æaI}Ôo¯‚$¿Æˆ6œù FiÑ=wÝ|ËÜûî¹¥´ð¶;ï¸ñÓ·ho”D‹ºßrô ZíÃ:éï$*kV­O¬¶ZË󬮫YV—§« ~=âÜwÞø™ÛnŽ82'¹ÕAôW¢ßW/[ZWV3À ®j([Y5ÖA¥‘äˆ_*M.9(î*_ŽÎ*qII±÷�ÜPýÚyeuh}3¢Ö]mYW·µ7g%k6ƒ¸¦h÷ gZ[Ÿ¤)¯êï}’%QøMtu†±qû·•ÞUWX”.Y²ÊX^·¬r4寨î÷¹VoÛèåè7©g‡D7W—­ŒêK›Mç“3‡ÐcÝn9°[o½›ÇgÊ ·$SXÃàú+`«V®©Šö¥Uu•å½ú“I ]¶¥nZ½d‰ÝA•ÒaG1TZZ¹ª¾´ZÁ MW¹ô„=$ûaÆ`Œ¸diͲòR Â1‰:­F¢ÚQԮˇKã)$9sÑù²U஡,(ˆqaI$¹µ¦¾l`Ó¶ÇΫ-Ã,AhWW×H—_Zo¹ƒä[|“§‰FŒk+V1îÔñ{2, ÒÛ”^ (m””3h~ó=¦�ÞÄÎ*ši®dÒø@×QQ]Y•4<-*·rz 8t¼+Ë*ü¥k¬ž¸´²*^/ZauKIµ$mJµ'­±¸.‹WØ]µºvuVêƒBKõ™*u}R‡2З­áH.i‹U ¼š- b«]µ~FqKŒ%JÙZÀÁüz¿ÍéÙh‹Uñ*éÕ:ˆÜî·ÚƒDºØ¤èVÑìU«+—­’D ^Zó©Òš²õõ«ý¥«Þ£*]¢P4}õª•Ó+*VM‡»ÎúÇ+ýĪ%ky‚Ñ·³:`p¾Í¹:ô‹^»t“Э · ] ºYèù [,zÝЭ½ž×w*Úq+ó…ÞºMèNл-úÓ,wÐÐû„.}@èÐ…îý’EÏó‚nši í}XÒS†W…žºSèvÐÝBÝ#y‚>jÑ·Q¶°ÐÕ  ½tŸÐ3@ŸºôI‹¾u9%4ùœšéÏ y¸@Ñy™[hèyYºÐ¨×²L¡Q—e¡ƒ ó„†<Ëò…ƒ/4tµÌkÑw Üe“äúIÐSä:ùO:�z†ÐM g {-›%4ù϶èBÊà¶X6Wh–5OhÖ¥PhÊS$ôiÐó-ú3¬{±Ð,·Dh¦7-úN`uY¥Ðä_-4óÖͲ„¦®üBS?넆–m´è»(s@hòÜ"4°·¬IhòÙ*t3èf¡É³EhÊÙ*4ë%ø/¢}ÿE”Sð_D9ÿEä/ø¿›uü‘¿à¿ø\&ø¿›|ÿw“àÿnÚNð7ÚÚ2ÁÿÝ´µàÿ`r™àÿžy ÿ÷àÿÊ#ø¿‡õüßKÙÿ÷RW‚ÿ{i/Áÿ½Ì+ø¿—2 þçSÏ‚ÿùÔ‰à~«Z&µhȹÜ-þ¤tºÐðEË3…†<Ë=Bo'>õZž/×§/×aÇå^¹žz’ø.Øhù¹žzšÐë@Ïú�è™BCæå³„Î-ø¿íz¹àÿ>èd¹àÿ>–+ø¿vY.ø¿õüß.üßǺ þ– þïŸZð?üÏòJ‘a&hÁÿ¦ü/ ÿ `ßå‚ÿÌ+ø_� /ü/ <‚ÿ…¬£à|ãrÁÿÊ)ø_H þ‹™^ð_ L.üKËÿÅ”!(¶ žÿÅÔ³à¿øY.ø/¦}ÿÅ”Mð_ ,-ü£],ü?�_´\ðÿ�ó þ >ÿÐÖ‚ÿbÚKð_L] þ‹Y/Á1õ/ø/¦nÿÅ´¯àÿ~ÚTð?ÚÚrÁÿýè;– þï‡Ï\.ø€å þ OÁÿ¦á\!ø�6Z!þÿøäâÿ�ÏâÿAÎâÿAžâÿAÿ+Äÿ/öVˆÿ_Dž^¡!ó ñÿ‹€âÿÁ‡¯ÿ¿uY!þ0°Bü lºBü eü—à¿öZ!ø/aY‚ÿò,ÜRÁ lºBð_;®(œ ½¯ü—ÀŽ+ÿ‹àVˆÿ|ÿ²,Áÿƒ”Aðÿ Ëü?Ȳÿ²,ÁÿƒÀÆ Á ð°Bð_­ü—P·ÿ¼.ø_Ⱥ þ²,ÿ´€ü/V…>Wþd]ÿ²\ÁÿƒÀÞ Áÿƒh_+ÿÒ¦E‡Àÿ Áÿ"´Í‚ÿEÔ•à1y þ'‚ÿÅä)ø_L› þ‡‚ÿÅÔ§à1Úæ Áÿb–+ø_Ìú þ³Ž‚ÿÅ”G𿘺ü—²¾‚ÿRÊ ø/E}kÿ¥¨cà¿«ü—BžíÿÝ ÿe°ià¿ 6­ü—AÏ5‚¸ÎÁ9ÚN࿜å þË!O࿜2þË¡ŸÁ9åü—¡-×þËÈGð_=×þËà÷jÿeÐmøÿ2ø½Áë%ø/nkÄÿ—A·5‚ÿ2´ýÁ¿Iž‚“<ÿ&y þM`²Fðo’àßD[®ü›Ô•àß„½jÿ&ðS#ø/£Þÿ¥”Mð_Jž‚ÿRòü—ÒâÿKÉ_ð_ ÔþMê_ðoŸ5‚mªFðo¢­Õþ¢×Xøwú©gñÿÀaà¿‚e þ+XÖ!ÁÉlЂÿJ–%ø¯¤ÿ•,Kð_A› þ+(§à¿‚å þ+˜Wð_AÌþ+hÁ1#ø¯ fÿ´µàõ,ø/‡½jÿåÐs­à¿u¬ü—CçµâÿË¡óZÁ9t^+ø/‡kÿåÐI­Ä?0¹³VãåÖjüCWµ:þa¹:þj%þY?´Žj@ëø§ôl¡)OÒc X;W®Ÿ=O®“¿øÿµL_$i悞/i ŸZñÿëN€.‘ë°Q­)×Y/ÿ°¾ÕB]#é7‚nëm ýB‚üßBÝ þo¯¨üß{Õ þo¡nÿ·°,Áÿ­ÀR­àÿVà¡Vð+°Z+ø¿>¤VY¯ ÈFݶ‰<{@ïšiöˆ~sÖî“ë´»àÝqÐÚÿcµ/ ÿ\Û.üÕZÁÿzÖý°Ð𙵯JúI ;…F»«í–r_Ý#éý  Xº6,éY¯ãr±wmŸ\gúr~©ö¤\G\Q{J®SW§å:é³riê ‘¾®NâÿõÀFÄÿëÑFêÄÿ¯=Zâÿõ% %þ_ùë$þ_ö[§ñ<Ôy…f^ ÿÆaß:Áÿz`»NãŸ2hü£íÔ þ×Cou‚ÿõˆÿëÿëÑöëÿëHkü·uÿÐs…ãÖEã>hÁÿ:àªNÇ?Àjà0Y§ñö['ø_ÿV§ñÿ*hÁÿ:ôÅu‚ÿõðQu‚ÿõÀIÝ:Iƒ¶_gáßøpR4ÀUžÿAû­“ùŸõkÔÉüÏ:Ø·NæÖóu2ÿ³žrJü³ñOÝN¹Žq\àüIÆ?°Z§ñOþ×ѦÿGA þ×¢×iüÓv‚ÿõèûê,ü;ªÐ–ë$þYBÛ‰ÿ_²Äÿ/†ëÄÿ/¡üâÿ—ÀGÕ‰ÿ_BŠÿ_Š6[š6ÿ¿”õÿ¿”ºÕø§Ìâÿ«(ƒøÿ*Ê þ¿’zÿ_‰¼õ†¤Ïzñÿ•ð“õâÿ« s½øÿJø„zñÿ•¨o½øÿ*à¶^ü*pR/ø÷ÃwÕ þ7@¶zÿ®ê%þ©f¹‚ÿ L/ø_ [Ô þýä)ø÷S6øäzÁ¿˜©Î'YÏb8륧5 œÏÓ4ÚÅMWßÕ40¿XÓÀ磚F›JÑ4ðü°¦Ñ.LMÃOhm¤VÓÀð.M£}ݦi´‘ÕšF;ªÓ4ÚÑJM£½<¢i´Ù„ö‹>Ù·úEŸŒKýº?füÚŸ ûõ|ôé×ý)Ú‘_÷§h#~ÝŸ“~Ñ'û\¿ö'°‘_ü ý­_§€¿ö'ðK~›?ñëþãn¿ö'ð~3Úýº?>ýÚŸÀ×ùµ?ïòëþ}_÷§ðá~íOàü…†Ïñ„†ò‹?¡?ô‹?a¿ï×þ>Ó/þ„}´_Ï'ó~íOàÃýâOØWúƒB£ùµ?Aìá×þmʯý |‹_ûø%¿îOÑÞýÚŸ`¼é×ý)ú£Õ# C½Ú²÷u:WKLž‰º¬–x yWK;z^-þgb˜Õa‹ÉëÒÞG"öX#íÔƒëkd|‘9×ȸ m|ÄóW¢=®‘X‥5ß�Û­ßåƒֈϹms­…OîÍw®•qÊÜj5w¯ê¸Ú­Úv¿œk-<8ªa»µÕѺ¬ªQßµ~Ûu‰£ªa»µÛu‰ª!ÏÚ­6þ/UC†úvø„QÒ¦à“çi¾w’¦ác7jõÊÐ4t8YèøÆû5vùŒ¦Q÷46ئi´µ{56µBÓÐy®¦¡“­š†nÑ4êž®iÔ÷ M£ŽÏiX}^ÓÀd@ÓÀÞMc_Ò4ú¦hv¼IÓÀL³¦Ñ§äk}G¶ÐŸEѪiôk5 ŸÿMÕj»VÓ>Ãøš¦áCR5 l\¢iø„M£íÓ4ÚøÍšF[§i´Ù+54Œoimð!M·ŸÐ4ÚÈM£/¾SÓès7k}ë2M£ýªÐ+ñïRM£O\®i´©¯hí÷›š†ïMÓ4ÚÂ?k¾ô:MÃg~JÓ¦aü‹¦ÿË4 Ìß®iàüIMÛ×hít¾¦á¾¡iø§¦Û #OÓÀÿG5 üiøÿ˜¦‡¦ÿ&¡Wÿ×køÏÒ´×0ª5 üïÓ4ð_¡i࿦ÿéšþ‹ýÕ7BAŸaÌæl>ã&õ Ûçÿ=œþù¾þ{iöküûü+áô½®öKp¿™TN7ŒÚ‰¼·7åyffoJ»£'Ãg¾î5r†—.7zLO®÷¢”^ï™Æ„€‘åâCÃÑáæ­¦ûk¯øŒX•çLËt9¡È{¼Ç´žŠtòçugÀHÞnŸ±õs¸Îò{ð»ÃãuôNP—f;M^"÷O÷¦[×è)ïáƒÏ| ”ü[s½n]Æöœp:x£3غ´+¥=CÊÌ;‰tãÀ§#^ ¨Ò¦ïÈ-ïD=³äÜméuk ËÑm¿<v}6¦Îy7ªÏ×ÃàãÈ©Û<F¾UÿòYàwëËrôæ\ê°éyŽ|cp-__Ûï1Òq-tà9%cF‹Ç+yg1ùÊy Óñù ,ïe¥Ã`ÚË'w!Èü3È<­ÑSæÞ湜åyÏÛ‘åäïóx ž©7æß‘kžEút\w"ͨVÈÒº1y ®¹äôøÍFÚLØÀ™=¸7<3Ü`¾uóétžPäoHËü.\ϲtWF™²™÷ÿL±˜Øº4Gë³coJ¸ù¶²þ¢¯tÖCa8îq•µ|íÃèFIû<ư~ºm4O°;~ÛvÅc˜²y@Ïà±Qóð_üõ}F:ù<Þçµcõ à³mìU`ïÕ°äí@ùÝh+ÊNí– ÃtÔc˜Ô¹OÉþ„‘…¶8‚éTûTy½MÌÓzh^¼Ïôê:øî:=ÞgdiŒ‰,÷B_¯QÑjÇHp›—õ[eµ‘²f]Ë@y‡4ïæ(ž==R—6ÈÝá2Ý®õ ü“7B½ï‚Þqо9ÐÓöm´sÀ(ºt)¯©§_/ ùаÿŸÀˉò;ËsM(rœi-y̓C,ƒ;‚|nȾ€<ÿ<éÈS3¹^&©û>{z¦a­kI¿Ÿ¼YÎ8 ™;rË<¼Ï6Áv½^„ž!ÇnêYlïˆÃsÝhçsi;Ú—˜è1(£U±l«×µm¾>Céî¨MƦØ:í²°óÁíñA°3�_èCÒ㥣lÄG¯Qlg«-ôEÏ«òR«0z àËÛ048<6®Á(äøävú-E›€Ssµ¹ž ÀÜUL+õj‚$íÙC¬¿U¿òã±:À=ÒC(òŠÖl‚.ÜCiR7‡Ø÷ðêö)©Û¹n}Iê¶ÕV·&Ô-}ˆí8'Z·²ÂÁêÿzÓ²L`åßÛ%ôM?JûGÞ…¶²mûm½` z*Ÿ4¨ÞC‘_ ï²ÞOÅêýÙi^§èý*›Þ¯„ÞÑ–MúË?‘¤ü¶ò‡£üLð~iºj9Z?dYOÆ‘ÕeÉzç~ Y=±>=>&mïÀêµP00÷C®×é$õúg[½¾‰zåKŸ’£û”qV¿òè¦Èû7 ?²§¡_zõú£é_ºíªL”5³‹ýÊÙ~^LÚs‘×Tð ‚×îw ‚óX—w ‚E¨Çé‚à|Æç' ‚sÑîî>U,ìH㵊>wÀ¸=è™Ø×ß1ÇòÆôM§wN 8vmšÐõJÊk¥5 ¤ý(Ï{Ó‚sÉòxP¿I8ïJ v§‹(g«§¤m‡^zó|ާ¯ ;ÁcÞvgØÑëõevµ{RC‘3Îö ðš·øm4|Y”<v¸|ž^ÊŽr½>®Ïï¥ mF–;ù5uƹ–+×(“ØÚ²£Ë1öŒéÿÇ+y‘Æg|áz‰•iÿÝ1ùf«ø$`Lƽ-,mê õ$åt’ç1޽rq|ˆðœÞìñrÌä蜤øÎ`ì*÷¦àÞðqrc'Œm¾0c›,^ 9ž*knôy9&Ëê˜;Ë-/ýœi)k—‹ñóruy,Ëv=‹eÁ—ºç·-îi:K=€ß橳ÛÇjÝÄñL݃©yI0Õƒ©Ñópâ׋€…ŒnâbŠél%Fœ(ìÌìÇÎß4¿¹vü“ƒÂ‹ø)"~vLT¸²pÓf8€¥ßÆÁRßöþøíB°Ô´û±ôÂàXjÚ™KMOž¥¦'ˆ¥Q‚¥–œg– ×ðŽ zÔXÚKMøXjz(–¼6,-K‚%úºG€%üë0SûY+f÷õËôx]bÓf=†§ßgZÒÀÚhŽ‹ÄïÑãwúlÈq³ÓëØeùUzúæÖ5Ilo¯} ×>ͧ®[þÖ,Ñ×a¯‘?ë¹]–`À°ô`zõxåPЕ!>?} Þë@{âø_2ìJ¬ƒlÑÁ¥ûèàWz#¯ÙtË܇ʃöàÁø?[ÆôùÄ8ËÆØ¦^bËC9Û©ŸÉ¾œ8ú¡ç\ÅŸñè{û­1Ôi¦D_“¢úzôWqôÕ$þç¡/ÿlOÚz»½ tr.•~ižÌa]Õ12ôv Édn²æ¤ý/`t|Ä\¥_$‚îÚ!Ëp]Ë_fó->`&»ùˆïmâ[Z¢þ¡›í}6ù¢M´²'oößÐAûnæ•þÛÓ y£üÍͳó¢ú¯G¿˜Äç< >ÇGŸƒ´œ·È²ÚÉ£[›FºŒOn&.¾•~—ýr3EŸÛ3ÓçÐýõWÐGe/ßÊù$]7êuïu2õ³ä¾íѪľíÑÅçû¶GK†èÛÄöEQöèmñ}Û£7‹oˈÁ锨{ôjñoO3ÍÉ´Ð »Û¶=(x=óŸ{S^O|y5ˆu ýx\ïè¿XcAà!ýbü }`´ÍFÛÿ>˶£b|àX›ÌÞ®ò´kóê:¯Åø@úlø‰/±¾VÞ¤¾p¬…ÍGŽ'ð…ÔÕèjÚöIÄñÓN!ý¦Hd¯ž3f:I£Ú?å@Èó­è<ZÙlÝö€ïoØî í2×˶×íjWq3Û ]Ê$üƒ ó×eНe7`5øLîë[£ó Ã9Ê:§#;`Ñ.åCÌ–Á¹Cê߆‹&ú))g.ÊqÓg ¯òg͇©ú è"_i¤Y>¤ì0åß.¶"ÙÎ:€A™sWó V :²öQ‡/‡9V*AÚ¾xþúó1ôúó÷¦…úºU›xd;â©yCõGÑvòÈRñcñÊ»Ý6ÞoWã–i÷æ)æãŽò° °lgh—·a¼ÿ*1]Í€žôðMÞm&ê€ ïŒP䯉}Í#·'ö5Ü|¾¯yäÆ!Æäß‘8êåqJ—ÁyÈû›/oÒõ$Æt]»¥O°×<^”<[¢¾é‘ññ}Ö#£)3òì·vEçó»¿?‡xÃ>—Ÿ·Où¢G&é¶ ÝNgá: ²q>½ÃM|·aàíŸmIa+œÐ{ Ö›Yÿ\+Îa¹jþºœ¶-§Ü½}»i´Ž”MÚÐ0¶W« •ÖmÈgüã/㬰½nß•hG/qNÃ6kvÙæa[¢óKz ¤Ç6O`vÙæ Z<1säÇ%ÐÏýȳg°<Â߈™ßLÆŸ}qüÆÌ_%ãOûL¹�þ¿ÿ› SÑÐù—ͺ~ŒŸƒÿÍœS»�þ/€ÿÀÿ^®á\€þ^€~¾þð'æ¼ ÿððüoÿî àèø×ƒÿ­àúøW^€þ�ÿ¹ýý•gZ‚y¸l›?÷ÙÓXScÞáÞýh£oNd_ú¦ œØ—ê]…|ÁÁʱٹe2i=]z\‰zì,Ï5c,Óê˜béGØÒ÷ÛYÅcÈc¯·’?ùõ>Ïœˆm¹‡c…Œð;OÖ7EN_‡1òQ™÷Û#1Ø8úZί0~8S,ìºÜÈ gÚF]ˆã _úW®ôºv¸ÌjΙìœÈâ¼ËÓ ¶º¼iœ9à šœƒÙáòVìÈØtz»užµ_~_`ŒúHAhnày_úè€quOAÈg‹÷Èhœ52.•ñÃOhŽúý&.êù[5>°ô—-6ú¼ÆÍ•§ÜÂúšW×ñ²mëxìWˆô%G7O7]“Œù²žšn:{&û2P÷±=i!_ÇD#ã…4ÓÙó1Vé¦Ü\×~~¿#-ÔÙ[š ™1î5Æ·ªµvÆ[ª8&¢ü¸Ç5’"Ê‹ëåŒiÄÞ7¼`÷âÅ4‘zn¾&ì 6™Žœ€cûÖδÐì.k¼Y ~wŒ6æq ¿ž>p߈ã®hŸ?æb¼yËÆÃ>…Ü{Ñ %Ž|!:VµÅ[RãÇ[ Æ/¨y8Ä—ÏûÀÇš[ðÐóÜF—áé6Lw—ÛÈDì›Ûz ê_çƒ þt{3q^hÅ©Îuäƒxn›îÿlñ_\¬@¿“+†îsËÃñlÔ/•ûˆKâ‘y¸Ô¾ _íýcî¯�WH{ªó“ WÔ!°µÃea‹õê¾o-'¾ KI ß-‰ùV¸‘ïíΫY¶²N¶O>¿¬]–×DzîbYVÝÍ™Á´P÷{po°ð›¿°Ýs¹àÒœØüÇå3Îñ:‚ž?nÓsßPò`È]Õ³Ù£æ$z>Šº=v,~Ý,ÔuCÛÌiVû\6ßÃ: >Õþ$´ßÂ0p޼ ‰OÄÎ…jÌB̽ù6 [ŽßZ~È<©eOi²ûæyüJ¯‡íèñk¼OèÜÛð-áÄc‡Í×';lžyþØaó5C™§˜:wŠ~õQ¼‘ïkpîö Œƒ7šçGœèGv…™þ½¨Ìùv< mªè‹÷ŠêëÜÄF󆈅ØìÚ=D,L±Å§c°Ðš¤m@ÿ‘I?PÖ¡˜|-±ù˜‡y·§2}ù äÕy];$k<ëPu7”?Â8ar®¢î{¥§‡X{LS#ÏÎ$˜î£Ý~tå²bš!ø)®µŽwÆð;ÙÇ?tkÿ0!�ÿpîÓôVjÚúÐþuÓŒ˜ò”]¥¼¯°¼g­þÔ³MÍï¼»‹×ÐÇ:;'ûô{½©üž£'Õç`rMèÜGY¾àl¶­Ý^já°±Rã¶ÉEê2cˆú¸šú¶ì÷î.^§¼ÏFõ¡öÞ< ùz'û2݆ÒÚT&åd›‚|ÊÇq5û¡^Û Ÿ£ÕåËÚîlǯ‘Õâô Ãõ¹-ÎÀ°§® ;Ÿžêuâ^ï=yeÐÉr[¬¹O7b§tÈrŽ2v©þt=å oÄWª,ô½ÀáûÇi kNÙܨׅ9·L]X6ªP±%÷"ŠšìqŽðOk´æåÓ¡ËÛÅWåo¾Î›ßܬö* m!Òf5;ç¼ :Ÿ}s~ÀóœÕ×obLÕzE»1¡³õú|»/SÇ_ÀÏ­Ç€1êœrSV®kÛ×´¨{íj]áM£"Ÿñ ’N'Ÿß@Ðÿ‰ÎÀð12¼ºZs'œV‡‡'2€ÌÇa¯îɾtÚªº‡®Ç™`Ì:¬ÕÕ>qÑ\è~äW¦šNÆ®´ÇØ£öÚæ4Ç&´g†'šî7½fº²­¥ï&ê;Föp"ÙÅ.ȯæ(9×aÅOð‡¶ø <Ùb%C§ã¾J¤Û£â`5ßÊø<Ó^–Ž‘¥¬¼i”Ÿµ¥?>HúoC6wL]ú’ÔåkHŸ“þd’ôO ½Ç&ωAäyé3méO ’~ÒçéñM$r®&‰¤G"ïïÃqôaëWÑ­8ª£ç‰ŽÈ¬Ä÷ÎõÈüð¦M‘SÜ›òú=ÞôŸÿ\ó仼« š%§¹A_i–qþ«Üé0ÎóÄŒ;ïç^[ñuùCH;Ò»Œ “§¿a_ÿKw&O Òç  'O?éGJú­CHÏqp®ÈãBzŽƒó¤ÝcOoŨÇÁÎPäY¤Ï¶ÏžíM…†7îMy>ÔÌ„ã~͇á/k‡½ÿÑ‘f\ 6ÞaÍQdsü͵YÎ…à7‹søÍàÜ~‡qŽŠûO8—„ßιá׏ÎÏD±9 ÇK8ÙÎ_ÅуX<Žã„\?e›¤ÜwBîiŒiíó�¬;÷ëúë¹Ò'úŒa¶õ`æ×û:¿^·îàiÉÑ\ çDõÚˆ-ÿUÈ¿ùÚæxÓšìµ6Oé½Ö±{‡dÎV×ex™Öœ­‰1²×±ÝÚ»=„9Û@MÌœmÓ±ToçÑz„¿Zÿ§ÿØÆyo«Ìf{•½ñ¨»×ƒò¹ß»éncŸœùØf;Ù§;Õºr¡^{²]«Të<nïü6ö_7¡—œk¶åãmóEYÛò.²æ\0þµdÏÓr?“´^­}ë*æ£ìjÿyn™ÚŸc­ã”Oã=Ž~ã´öru©¶ñúaÚ”ãœmÖþ´cûÈ[Ѥë¿ú+Þ#?>ûÀõ”ùñmj?øùû Æõcâ/ßÜ›n•=eëâï)³Ö”ôž²8{Èç)ð)B]òÎq•hÛ’w˜ødæûòég-´ÿ±ïAL´ô„ËjßÜ?¾¿}#CØ8Â6Þ·Í[–bç!‡{#àÛÕ¾1sð‰dº4vþ!YzÄg—Ùçl²l•e3Æ ÇÔüàÑͳLäÚÓùIÊUVbË·%N¾|É÷3äËG¾g:¯RùІ +eñÙ'må4Å“oó %×#–¾ÌÉ'âŸeŸ°ñoŽå¾ Šï€¹ø¡ðµÍ—Ç<¿añ]ØyƒâÛ}a|Ëlsêe­qøÎý‘%ïé!òÕ±dµïÎ8|¯ú‘%oþøªµmŸ±i*¯Á?ŒÝ¯Æ ›®|²Ï'åcùÄöÛ‚Oµ¦ÚiÅÂf³ÓËý\Y-cø]MÞ„~>Ý*÷áwȇv‹Ýã5ÎêG>·)òŽjÿ£þg7Ÿÿá<7ós›í•ûã=“³MÆïˆm áìQ­#½‘ãÆé— Ž58ŽÜžózgkîå[Ôór£€s4Üÿ‡ü…ÈçÞ–d_ôY¬2îÏ1¬5ïûðºböÓ°LæEy[$?÷pf@ž ¤?Ày×yó¨ï•=%—Cº°z–îáoê1íùóHï:éáà× 9oR„þ‚ö<oﯲl³¶™€>x£Õ7”g¶ä<»»5Æ>´ êèá<¹Š!Øï³ÏçØÝ}ùáĶzø[ˆ ±Õð¡Ûêá§/ÒV…h«ÛØêæ8¶º!‰­®c«O qoR"[5}÷*3ÿ»7˜ù;R7±lõç uª}?_7oC\< m},dÚ€¶{Pï1f¶ïáKÖ26x'5ÀzM%oÑÏk¯IùÌ. Ô>ȪüÄvÈ _ÑÐåô)_Ñ y¡Jñ“dŸJŸZùr8¼H²€1Å‘;ƒ.ÔçÃlÐx£OGYjLsDöå0ÞÐ{⎠|Æ=©A÷ k_ˆê_­rÊNÆ”3[•sЉrzt9ìïÔž ˽¤â¤?2Z¥ûYŒ<§ÁwÖ噬ç>£k-æqïH«ó>3…ù1'Ô¾,Ôgß{ >²O¦埈Åôþ@æ¡ È“väþ�c¿g(ÿ‘;ýÒ:FÃøwŽñÿ‹ÇïnÓ¬óHeô^d~wGw’{3þ—åç1EâÑ ›"'·7åõ&Ÿñ¹äÞ´'?±{â:3;ú\o´í)ã^îe"M½§Œñ}—5×¢bg½¯,6v¯…jß’sà8 ×ç«ë^ÿÞUÁüïÝD›½,²¾€6íýj´÷á›<„±ðÙxí]æ4¦p>Oè+@‡…žÀyi¡/ã:Tð3¹f8ÛÕ³Óѱë ?4žc;ჱME¦Ðùf&tçã…æstG-}VxøÌ¸m¼íÇQbÍEŠd\]$GXÆÜEƒ‡môK1÷J†ÿƒÚn3lÇø1’£ÑØôò —Á†Á†·íMy#?4ó9—ñâtlè X{ ÍÍÇ|ôîgrõߤ^ £‘6\Lá:?}4÷H ¦Ÿ²ö ôá8Ž#ÜQjáó4 ‚ÞúnA¨tOGjx¶È0ý| ×]¯ ow§†Ý½i¡føx®eÊ­;]¡VУ@?†_·3àXÔ“j¶÷§¶unõ¼Àò."_\û®ÃZ÷Ÿ‡ñks—Ó;ãˆÚK¸q;¯“WG†W§ý¾ô%±kÞÏ%YóΰÅбëÞGeÞ¶'¼b¾È�ëC{[\&õ[K~ñÚS‡5§Ý‡z„·xþx«ÛÈdœÐâ6†owÐíÔ·Åš›pü8v¾XÍÛäSzpygË^Í骗×Íóô€1õûÖZ;圽ùª°qßõCyDå-N$ïSÌcø2tݹF ìÙy]Þ;‘ÉkÀÀqÈÓ‡{ánÜ »|hë¬÷õmWsÇ¡æï§†Î0dôËEz;,~Ge?(ëø_Wå8ÙŒñv¸ÌåÜW¢öž¸‚&ŸåŠgÄ@ R¯${Ã7ü&ñšë†ÿ>?þÚð_C‰•1f®µc²ÜíPÏl8Á_®»žç ÇbñŒ|wºÔší†ß \£Ü/Pk´­Ñ5Ú _Eœës \£=FÐÇßÈ|\jtûŸ—Ú="Mß6kŽª<~¦e醠ç•ÇëшÜh˜#bòG¾ãÈ×¾MÍÛ‡š{\¦÷JvLôØ<Õzî.N¾0mEl¦‡Î½#vžM¬7]j_MåèÑx<¨Ç_‰Ð3&sÍTðÁµGä,½kkƒû¬5ªà�Lܧ²¡$þ>• ÷Ëþ×aÄf°I £íöàÞ­²à]µhk°Û–|¿¡<¼ÿÔ©:@¾Û;]Æ�üÓ.¬÷E?t._¦Ø– ÃíGÔúf¨E­o"-Æ™ìOõÜ+e‹á;•|ñ{ t¥Úæ»hGÇœáÂ0ðwÜÆøfÂfÆ)V¼âQñŠÚ³´á~ò³Í;ª>¸SÍ;–M£,·…ë3Ü¿Û lr/ŸcÙÁ¹ÐC–<£Ã©¨Ëgè\ÇrÐÝÖ#©íêʸ–ó¿žÐ¹®«í7±ÚEK—aÎí6LŒ‘6\Ç4}+Òý{ç„v>G7ýR—šÝð ¦Ç½v¶S}uå})gz7êŠ4?`âS•Á}S.“:Èd;¬2övz}ý|zl|И֡žc k³—-ë¡_µX÷wÆÞǽ°Èú$ïá¼/ºŸ Ô‚(Gö¨gj¸®ü¬…äߣtjá{®ï„ßQ±$®;°S1mv¸'êÄ…CÙhSä/õVù‘±|è¨Oñ~ÄoÏ„v>·ÚÀCoØË”kßàµÀwÄ'üÑC¯I~}¾]óãžèÚ[ÏM‘?¾¤û>àæÒõµ[i·Oôyìm úú{çD«>=ý¼øÌ0|2d9Âò8b¿dT=È}“’™íùŽA¶#®ò0ú)ê§:ÚÕ\~ç$e×;'<‰ø÷¨v`hþéšÿ­7[¨sÊé"/÷ ¼þ ¼Ž‘Wæ ¼:ÀëÄyyáõ/àurˆ¼òáµ¼N ‘Wþ ¼¾�^§ã´« ž¯¤ßøŠà¹ÇNGûÉ‚¿ÎbûQíɩָ²¤­OhµÚúž¨Ÿž"¾ñÄ׉O—/[ûlŽw:¸¼¬=b!î=J'ß°áËFûLüÃ-lŸöv%ín˜öñ©ÞôF7|dì1Ìô#¨ õp¼áó3ÆŒa”©ÑÐÏ+~^s2^L×mý–qâïYþåÜÄÎë}ÆùúZÿ–¥¯slÿÍ*žt†½; ÚCôë_ét™é\G>ý צ¾Fhù]|דWË?2VþÞÄò”qFz§á)qü^±|ÂÈnðÛ9óKKŸï¿Ã2Ù·YÏÒv럱üx䨶Uç÷ßì‚OŽ%ÿÖ‰œÙˆ£ ÇÑHä=/ŽJ{"‘³™8ŠqàÞÙ“‘Èû³q4Yyx¼ß-¿¸wŽóeÎ<þjXÇß06=gѧö$?tûÁü§Àóï;q€ÿßçã˜a]Êñ÷Lë÷4Æ»§OÊ/Îm¬;ž.êpŒQè®0æ[»)râÛ{SÂA¾û¬%'èn–uÏmê91#ý9sN$ÐfÜ–¡ÖÝj¯ß…”£×cÕ;‚ԸƸ#v½•k³¼‡aé]ç­»¦†çÿüu×qý<e_¡ŠÙ‘†í±ËiÍK²".Ûs}ÐdÚß ¾31X´®ïpš§ÈKž?R{÷äÇ[³•ú}\æBG³Ž\O–˜»F¯¥¢œÖzêºßr=µ;Õ›Ù‘d<ÄþmòæsÍœEš?o³ðø»N…Åþ5áŒòík)ûUžÿ|ÚºCy×͈>Ï£ÓVÇIû"Ò^ÅçòYûü°ËZ—];_±ýAÎùÈZÒ8´ñ܃Çñ'b‰¾ð«'qìÇ}ûš¼‡<üIxü·æaŸ¯’¼ÙÈ; ¼«©ÛËp@~`àRîù{&7ÜpÐcdɳØî3iA<;Èçð?•Ò>u-±=ïi“íí¿ïMy½‡sW dÛGÙ~tÙ�Ùìùÿù[9÷” PåOK˜ÿ—È_Ì9¬ù›‘ÿDÌ3ÀöüÿüùœK? t›Xþ¢÷pž-AþæÙËÑÔ12X¡ÖçÑv¶©g~qò¬Ã¥ÐõRتdŸìx¢¾r]%èq– ^ÔÏ1S†VÈ0?}†9í�ònŠüi+y°žû¸|¿Ú«x9q{çV÷YׇïO{ñ$ßFÛóºÏXû–A<òÖ3ˆëî¹õÌ.p³®[ñ'áÞÚ.¶?i·~ŽŸ­½íkÎg–Ð>Þ"ÿhý_$6WCîå{SÞØ Þ×rì${*³@\›®ËcY,ÓVÞ÷›­µ5×ÔÜÿ~‚µßå»"êúEîm/å©ý?ÐË­ÍÖ3ÛùùÜõ6µ^:e“ºŽ&ÑOê÷•<k÷"Íxêc¿Z[]—–D_&?›>ƈ|_¤ý!N[7DÆ+Ð^›ô< רô:YKγ\+ã[¿WáÔœPßï9ÇóY£c)íÃÃjýlíúÄkbkWž?'³¶aHs2#G½ƒ$úÎoš‡ cÌY‚±àüÁÞxpíÍCX#̈®®õÅ_#\{뻊>~ï4äÜùÃWŸê3.µù3Ñÿ[?†?Hÿô?&‰þsâèßs‘úŸõáéͦÿ5áøú_ÓGÿZÇ~è¸í°íƒëxM{b¯ùáù:^óƒ‹ÔñÌQÇÿt:~4Ž??ˆŽsáG|‚Ž—&ѱGÇ‹/RÇÓ>D_}:¾*ާ$×ñÿ¨õ¿AÇ#’è8-ŽŽS.RÇ3><¯î¹0¯þ¯ø:^ýó$:^—ÁW¬ûà:^ý½Ä:^½÷|¯Þs‘:žò!êxÓêxc¯‰£ãuÖXÎzV¶z}§Ào¨þã1s"‘q88ïך;1²#·"‚´aý>¹Ó¡£ÐÁõï„zzÓBG7OU{SŸšeæ÷ fÞ±Ùt´ÞìUü9÷È|“Ž‘õ¾>Yot÷Ï—XÏÿ&ŽAGJºãr®ÖÈ—k·²8Ç‘м©ÖYp²!¾:–è™ÜøïeZ}E’÷2©çgP§‰\ëH¾¶z|âu°Õ£â`,wˆû¾MÝ"Žþ…šÏŽÚÕˆ¿Þâ?ò0f ÇoS}[á·&}ð6å'nSþ_ž__ÿ_d›ò}xmÊÿ­ kSþçã·)ÿ³ƒÇ˜o?'Æ\ ýOCûâ ‰þ×ÇÑÿÚ‹Ôñ‡¨ÿÛ/PÿŸN ÿ“ôÐñï_Cÿ!Ä?þ‚$:öÆÑñG.RÇó?<¯úó…éxÕãëxU_:~}ó‡ÿ¬úYb¯ú÷óu¼êÇ©ãÂQÇ­¨ã'èø±At|¹õý¬ãÕIt\GÇ5©ã¢QÇs.Pdzèø“ÉuüæaøŠ¼AÇã“è8Nÿ¿*÷"u<ïÃÓñÊ7/LÇ+߈¯ã•¿Ž£ã€è¸:®‡¯Øg¯´žé˜kíÓC¿W'Æz½†ó—½H“¤~{‡ºï1ÞGXǨ=WîNlϕϞoÏ•_£=Lj=[s/WöìIiÏì˜hÀž†Žç~ªbÊPDíYF¾/'ÐiËèúýw¦˜ùß¹Á›Ÿ@·ùðó‡¢[¾Ëº C·WPÏÐc¸5í˜:±Ýcdõf˜èS½ƒ¢wxØ©ô>=ôGõŒ .v »d`8òü)‰Þ‹>€ÞoO¢÷8ïÿ[yãÐÞáù7Öצ÷ ô~¥ŠŸ/ ý1¾¾÷âçãÿ·ôýÙS¯ïÏþ1±¾?ûûóõýÙß]œ¾?Û_ߟý™Ò7|Z̼xsô=Æ-zmEör_jÛË­l#Ï4ÕÃ>óákøž|¦©ÿ9ŒaùÎCÊý $Þ–SÖ Ù•­øL�Ç¢b«ÅتÏf«ÿMœ¸öìåÂùÛ8wÓž8O±Ù25Ž-Ó†hËÏ_„-‡ÁfÑwC[úãØråmùrŒ-ËØòA–³ÞcŸã'ÐßbücÚÇcŸëý©ÿÚÒå@ÿã’è?/Žþs.²-9âë¿á=¥ÿëÚwQ÷_‚ïšòËw5üøâ}WC(±¾ž¯ï†ï_œ¾¾‘@ß_Ó¾+޾ë oYÿ·æÓ¸g‰Ïçzþí™Ü×;u¥wΓq¿îÿ ?ÔPzvÉ÷ ï‚$v¹'Ž]Š.Î5ÌI`—OÉØy~ȲË?ò©\ú£|‹ÅK»t£|K·íFÝ:†¨Ûô€ygbÝÖ¿w¾nëÿ>ÄyÆŸtÐm}_|ÝÖSº}`@|Ô|@=ŸUÿ‡è…Ð:Ñw-ô0þïÀ&á8 úæ8«£ t4ZÿúŽ$õ1Ný8ÄqÖHkœ:úÄT3¿;-tœ{Ýÿ¨Þuac«úÇ.llU¿5¾I2¶¢n‡ÿÛÇV§ BÇ9ŽbyGP×#S}ÆÖï‹ÇpýÝIlx{Îë”gÊiCðW6„OÍè˜Ðnpªø‡Ÿ²N6 _›@§W'×é±WàúëõÃé^ÇÞú9é^ëõèç¤Y¿Ó‰â“‘¡ÐkïÈÐضz¯ ;píW†”ôŸˆGœ»øžTîÝ”g’è½îï¡wyVºî/‰õ^wâ|½×½uqýeÝ/ãë½îµ$z¯Þ«à'šÃòêõü¤ï¯^<ÎëžJ¢ïæ8úþòEêûs ô½v}ƒïÈLß§—Dõý1›¾ûDßÿcÓ÷[Ô7®½s—²É•a7m‚ó±GjŒ=Ò.ÀŸú�öøx{L‹c+/..©“À¹Iæ"a׿ø|Pü«ozYöè³á¿¯cdp¾eà}Ýœ#SöÞÅð Çí¯/^ßµÿXßµçë»ög€›Ÿ¯ý×øú®ýA}¯€¾Õó¿ÿÇô}1ã­ï$ãÿÚ8ãÿÚ¡Žÿcõ`ü_û`}‡ÿ€~u÷ÿ¿üMí¥À—$±‡'Ž=F\œ¿©y?¾=jþ–ÿaýþŸäö¸v�þ/Å¿`ý>ê–í�úu ]¿5`ü_“dü_gü_3ÔñÿÄ{M‚ñÍ×âáú‘ï,‡Žg =Ðk”77G¿c7žß}â÷~™ê}ºÄ:÷Äð½ªê9bcü¦Èñ’8ßZhוí9zõ^g¾Û•Ïw«gk£l‰Æ.¼OL™hOLŽ|S¹S=o]SMþ3äûDH«ž•¦ì|ïЦÈïÔ÷Fg„" ÷ÍÔÜ™xßLͧãØð–!¾kéeòä7Ë¥Ž¶g•k®Ž¿w¦f*˃¼ÿ1ð ¼ní¸'¿fÊã¶=ùÛ­÷på[vÿí~´­—»Â§õ¾N7iwÀÈï×Fá×…ß\KÏæ–`Ú‹'£ûÈWœå^z‘Q}ë˲͊¿óù+ë]™s"|–='ù³k�_>Í|"G3ä˜É÷9¨wx¼ŽÞ©ý{ë›øN¡.e_S}“Çö~• m%ûò‡óNbz›ú6’õîÃfëý­ÄD&ïëy^î{ß‘knÕ{ó‰%Èý +®g®0úËoaù­yAîÓO»Ì-G¬ô™|öÙ&ËÁ^‘…ϲóy]{Y°ñ›(¯•× ¬k;-¾á`ØU¦öæw¹}/ÆòÝÚ­cùÊwÜšÔ{ß^ñÙÓN;•á¼úB†C†Ý1×Úq­ç7ò9¢=ê5¹Þ}ª¾FÙ!õ¤óåzuú~ ¼ˆö!­<§ô’õ¥²CœSWúG½å[Kòþ×2ÓVç|yöYËwHä;l—Mù-ÈG^Âc]¯]çË×^¯Æ\û®µOÚ¾[Ùh”Ù Òv?¿¬òÌAt±|{¤þGíõ'Ö;×§úëŠôh~àNÐÔûþ. -¤ÅÁÆ |/•MÏ}¢çÌ- pÐ7Hݧ€×IßSÂ÷trû¡®Éùæ€×Ù(ß2Ãâ{¹Û.k\4%ç{î,x¥K¿â!oúú^‹wf‡KñÎìÇm»õNáÝ>ïßwžðΗ÷én‘wÞ{šå›c6YÂïìyí£O¯…×Ë'iŸGHW1~¾/‚ï‘sÚÐü”ùª­ÍŽb¶lfÿí À¬1ˆÿ6bå|œïa‹¹¶×fHÙ³·—²– (;-NÙ+QŽOxÍM\Çòñ°œb”£ß}S˜¤œi¬ýŸCûWïÀÓíi¾´§â$ý�e ‚%´ÿ²_SøV&iÿÌ;FÑþ˪m|k„oCr¿RîNÎ÷}¶¿ï:á»QóMÐO· ÂýY@t¶%ž'’O^Ù솗÷_E9íRΡÄx1÷|Àrö£œÃVŸVöj‡ô“º¯`ß'FjDG_¯N‘½;IÛÝúeߌr¤?.Sý1ý›à|]|œ—â‹ß¯¯°Èw<‰Ïó@ÙçcŒQ-2ÕðùçÝð÷äÑW*±ø”ñ›É|‚ŠQW¢qŹï²Äcîíˆo-}”7È»/3´ÕûûرÜ;ˆ.¦‚_d\—ƒ§> .²QÎÆh[-Xmu–Ab–=Éå?ûx5‰ü[¥ŸVã;­ôå>¤Ëò/ÁñnŠz= ãábÈ1o{αB¾/—ïÅï€(7£%gWѶœòùÙ†õ¬;ô?2kΙPüLî±’Ëq ¾€6àìÈ`ÞsW^yå2vÀØÒó›”öì¶¼]yÏåˇsàûs?…º…ç¨÷ùµ÷„¼dÂqÜãûí)1ÖUcéî‚PgWA¨;\jæ{wäV0~pL•µ£K¬1pÖîQ3¿5ª‚ßïùMAè(ês õ ÓfCšë k/ ÷ °ndz7SÚGI¢Ž§÷ŒÚÕ÷Â%å‡QWõ=d]WŒF¿•Ò~)ê|u>©ëúnAèà©‚ÐKï„úŽÏ ú愚Â|?aAhgGZ¨øXA¨u ¢Nm¨ÓnÔeÌ9LFöQGЋ÷­´P ¿“ý\ZÈÜ:y$eÎám¯åäøŒjŒç\ùÞµí$2ê¹ |÷Y¾Q“·Ëm:oyWÊŽ¡å[ÚåÕ-ýq›ß+´™™é¡Èïv§xùî×Ì\U+ã¥óë±'®—S†èØø»ë.·ÆÆK6E~e=ÿŸwù<öïÈëëusÔtó}€œGhó(z®ÿ»‚åS:Õ8¼z3Ÿ§—¶Öi‹ÏˆÆgåÓÞ±Ò~^½ÓÛº_Éø€eêy›nKC™òÍmÖ3î¶ñtùÌè³îKO³íí³¾Øo½—ð¨þyÐzv}*eëp{Óõw‹Á›ï³¼”ÏXáz¦¤÷È·!ˬô>5g7!`,Qç†Oé Ðà<ÆÏyOäͧù¯¦÷yŒȶ`?t¯¯IQ2rΠ㌟òÝÃV9U>ËVÇUGÅçÓä=~Ô?ª÷Ó^ì‰Î™,ý)g�ˆ£«çD"¸ÿ­ãwßè‰ü¶À5ûGWàZužæ©Ÿå¦}×ÎsG”gùŒ]i?è¿Çw°¶ª÷.}¼Ùš‡ŒÁ^ä¹.vôñɶ:>Â:"Ý?S§Q=”°¥ùœ¤yžºÖipιìSöy*™»h¶«€íœ½)ÇfÛæÇ6Ê·ÓLj 8®CìušöÞgá< zæ{š³Pn)c ^ß/ó^¶w•V½òþ²Ýg ëÁÈ©_¢´êû?:íé4âÌ¿Ï77Êý!í±o¸7ÃÇ!|Ž‚O%ç³ÈçÔôóÊÓéºn¶Nwr¤/¶œ…|G÷© †±)òë´ÏÞ&óö¦„OCæ³V;^ú‰'û¼ñÒ©÷£,CÒMµ¥c_çkƒOéMíO¯Þÿƒñ®zgÇ6ù¶8°’ûþŸc|&RÙÕôô§}eÀûæ³ö§ê­h¿1ñ@6ûŸÆQÞ`8Ú/Õý=çWÔ{v,0KbõÎsùÞÀF~o�uº„¸h“w÷À?¸)—'úÊÝ}×N>®á5ú’˜x#[æ]ŽÙÊ÷Pæ˜ [á<·¢Ù&ó8‡óò|gŒMVÆ'î#ª­{¯ÒïéñKºø®O å¯-êÝK¯à¼u‹õ=—ŸÇÖ|ÿ õ™Dî×£rWä%Ûјkî³Éb“û9äËOÂÿo6þÞÄü˺ðÿ2òM"ÿ)‰ù—ÏHÀ òM"ÿIø{ð_Œ|Éôoç?+1nÊšð¿•ï*ÿò‰ù—w&àßÓ4Dþ§’àç`þ£‘ïôùŸM‚Ÿp|þçÎA¯ÆõïN¿&ÿ7‘/}ˆü3“Ø7Aû:÷3~oaˆúy)ž¯´î•½”€ÿ÷‘ïðù·SÖ·”-?t‚ëSýùT\< ñÃh¿Ã𫿽¹êr]ÆT=ýc¹Pä7j,—X†#6^M¢Ã= êø9ä묎Yê¶ÍV—ãƒÔ娭.=Iüɤu¹ïØÿ_ª ûŽ)Y—ãIÚNÑ{] |º.— _sÒºø†\—fùFdWZÿ|øü‹¬KKŒí<= .Ú.ïG¾Öäuº]¢ué_ëÞx‘uÙ™¤.}ïÚë2¹]×å;ÈLZ¤½àº¤¶ë9þƒY—¶$ý˼SêÐuyùv'¯Kà"êÐv9|‘uÙ“¤.Ýï ¨‹n/ïß|û’×å"ÚKj{9t‘u9 ßQß¾é°×ÅÛ_Œ—+|ñøS{jÌÓÜÿ eâüC¯¤ípÙêÕ­ëd/“õä—˜~§òâú³™ýž¬É¶0OžïLUßfÉ–5éQâGg÷Zó/e·øýê<WxìÈ£=²qxü'x̳xTà‘fÄãq2‚G‘ð˜?‡/ó¥8<vƒG±î«zúuSQB~ö¹`ÅïÐ�~}qøµ€Ÿ©ûq¿ÊÁù•ÏÔüöàšç&ð¬>5õ·žÇãÈU :¯Kæ{¬o:Uø‰3bŒ<Ég öËfÅá·üÖé±à¹k1[ç­¹7ªwKG׳eíx”¤‡¯›q} ÇÅGDÖ<wES‡Ås+yÆ™çÖòNžOÄÿ¦úæÎ£É{dMÎí8ú¬ñtÞ‰1fæ7#³WXû¬g[…ÿÌ8üGs/‚´ùñv1¿¶ùIßq ððŠ\Sƈ\–½“ì:TñŽ¥G½Æ±ï|~ïý|¦‰L3àÆª×è;·žo‹÷°Åq=v>1Ð}bá{®»RtßbÆiÿï±ýŸLŸÒ²©uFäÛ~Íö8<ØþOëq±`iþý –|g`Pæ­ýw1ºÛŹf /åj>˜Îµô—üˆ ekOu*2#ï— ïK‚ûö36>ÌO>ÈiÌÞŠ8mù½ ÈH§ß(O^|yÊÚâðY>‡5ÞÈðÊ�Îê¼ù~ñhþÈÿªwGû•ŠN;Öúå±|”ÖÏá8òÜ~ÝCç@œ¼ŸDÞ=F·ÉrtpYÊ ãð›�~a»,”C›ÎÖf&ÅÉ›‹¼-âkÂáþýš­+©½týíx ,qú¨3äÝÙ/‹ÛˆÕ‹¬­Äk¿gN"oÐ>ö½´Åø¨Ñçû¨ò8u;süv" ë±3NÞŸ"ïû<ȲoYˆ—86?ÃöÀ.‹Øh|ŒæÆÉû-ä=Èk°ÑÑßQ–O쌑õøÝ�}×–û}›šãíï³óÏ÷gžàžÑ˜˜‚>¸ÉЬïIÇÊJâðCÿonÕã6¿æÁù•ûâð[ÁýzLkã×:8?ód~p_§‹Ùøµ _O~èÿÍÝz<¡ÌàøŠÃo:¿¥Ç$6ùJbù©Øg`,q:¿qàg ¿Ó6~•CàWm[ƒO‘õH‡'t&<«%M±¬‘g 0Øp|‰K5Wå s¬¥×³wË{ÄmyüȳNd9Û/cŒ'ëy G[óïæïx_­ñZãˆ<~Ã°Åæïø>î ê]ä|‡ù/ËkWðû1ïi¶Þíÿ~sô›êÿúÆËêÖ醱xS¤û¾½)oÌCgYë~Þ ÊüYsZ¨ˆküþBoô› ùòÍ…B®¹´©5— ˾’iƒi¡“ü>â.· 3`dÝ^w‡{ŽÉol{B=#ðÞ®-›"È/߃ߪ¿Ïtí»ñþøPä/Ìë3ÊøÝ‘BÁš?îtz Ç«mB»uº|úzZ·mSßðúi~C2f=¨Ð¾„qØk”Ýe[Û€­fŸÿýó‹ü¶ÂŒ€q è#²Jæi,¶OäZø@™Àÿ×í“øÝ%s™M¶TUoK®þu ^;}²ü¤õ.oÿw+y}·5ÎÞc[¯×,¸ÆMmÎ9‘ÍÓ‚®F×÷œaºÃnøJ·wž|/o|—Û[Øóù~Çq—µ.>š×_7²§Œ\Ò̳yš©¾ÛÇ=ÛôwB‘¿i9ìv´xÙ³`CæUüv©ï4ª´8Ÿ+çžC'“:ÝÞ™ø€ßYøõâw6~?þžÆ– ÑæEòB‘ÎçÜ‘Ó1Ó^&e˜ŠüÉ~Îo±Iç¡^š­ýG2^ðê=ë¦}­7F÷Eü^ô×þƒfOZpcoZ0`µ™×šö¦„óøý�®ŸI –ô]¬>~Y°¡gN¨ýÄÈà¢S±_¿ºÀkß¡™6EzN±Ž¡—¬gWzNÊùAŸQú+´Ùùo„ÀΣP¶¹ù¨ûo9½üfÁ0î¯èp[ø`>òÖ¸~ŽiŸ·ž#j‹I·Mb¦kcºç‚*Ýþhº’n¬N·é~œ¸Ê[ÙàþøÎÉ´[éwšÓ¼éÒvNâš“öN ¶A{MwûdØü9S¾—@ŸVÚ&ùgC·•“½>¤á÷Êšeï ßá,²T4¡^Íb[¿Ùx®={4×c'{ Ÿ¾*ìb9;ÜÌ^vq™îÆ4ÃqÄk¦·ºÛ3{]ðA©Þ™Rþ°^¯o<Îg÷¦úÆ“îÁ/ÚÀ(´‰%¸>ß°#ÿÔPämòæº-mIÝŠ<îiÿ»]­í*ýѦ—ÚÒä Hc•Ï4c%M&÷ÃÙÓô¤…õLöΰÕ{®7ê:—cú¡·éìIe[)­m–þõk†¯¹ÿ[¥ËsÉÞ˜;ˆî«8’:@ÈOÛ-j–=h’®-ÿ[þë™~óU\¶‘X²œíf”ìÏǽ¯£¯¾°Mùá+zÓB/u¤…ö„:; BÝ(ûÕžËBG B}GøíPî ³êZ"ø-lá~KøÙ©Ûø …‰K­y8…Ïü.Òë6ïáõ˜96íÙ Ãl¥]]†ýÙ-³0Z×ÅVß ·ÚËú”‘EéJ‡µ†øô?éþÞ©ô¶øoø…ZüWêoŸÂôâwÐWñûqh»‹ÿû^.´Ýq§€¡wÓ‚Õà›òDô=·ü¶GɦH×*ôÃíÇùBÇùl›üÖ6¿[ÂúB§.Žk­¾hñ÷Ëwå[,Û5+,~[}_ܲ››²YûQŒœ˜oݰÌûQf!ò´£¼jµ×Ñé- Zz¬±=Ë£žWd_&Ï)Ío¶¾›B<mû«.Ýõ±ÊûÕ7í¼ó·©½?¥££*Ûmí Rßsñô¸Ù'”f#mzœºHP7OlŸÙl½m«þ~x:;SÃéàÑô¸í}+¢‡¼½)ÇØ¾Æ©öž­–MŸDÔGŽm69\”ádjû€~›}ƒ`{kWª9_cyé&åX‚_ø!s®¼ÿ&Ó‘jzˆwôÉ#ö[2”�ksñ»¿>ó$ÎQ{°p}>®•à÷–½ù*ÓÙ…¾šzìuMìoûSMÚxûÆ oþ[¯ÅóòFÿù? o~>áwƒÞßóùWÂé{]í— ^>êÖÒYä½½)¯{€×bÅîi“=²ð)Ùßò¨÷bíÎ 8îÆo›î·É÷³˜}Ê»ŒÜgõ<ÿ1‚v‡¸7û|Y®Ï·®›ãu?%׋ásö<;-ìD¿‚¾jñ0ñŸl¯©Íno&hh®ç‚f]µ‡~íaQÿs„;Ч»ÊkÒÿB³e¡ß0Я¥³oCYmÐi.ú£ÇØßsßÐÄHhWï5ßxÊÿ‘Ö^Æ9|n•ÏNÐ%S|Ùü ƒo÷<_¶¤åwPư‚ïL xÐ2éËŽ¾±;Ɔ³{gµ"önÎíØÁ8ü # ˜¸-°+œ‹¾lžæØ{¬|§Ö‰²KXðt 6Ëìàõ ™…ôÎ(ƒñ] Í—ûÉ€±(8ÒŒXú-?`ÅžÖøš2î·ö¢RÖRμï!6X÷åŽg3¤›Ë{VÞEÿ©u’Šü+p"s„å‹ï“>%Ûj3‹‘o ó~€sâèOv‡§mª=.zKñO í–¾"×RÎCݰìÁo.³¹Sx/Ø_EÏÓ/‰}¾Oº—ö™ªìãk„¾NxÍL^ë¾Ã—ôû¤>ÊF±zF}vë|ÀKp’‰þc,¯‡:î˜hŒ¥žcìf½©kØÑ£ì4F5*{ªï¨?hT{¿VdÝHŸ+²ÞÎXòÉ+½üŽÖø'¯ R¿Pu³Æ‘ÓŸíNõ2ÖÈUþUõ[‹J›e~Úòkê9kò.Ñ~íšPäÛÔmwª·°qlÐMœY˜ fÛ1‡2>E¾2§r’c&´ƒ+›Ý›^ògá>±€²r_õMdï€o"£Üg(ãXov´on ¶Sïgô¢¼Ìƒ¼ÛHŒÚr¦ÖÒ½@ýDûýò�÷Ñ?êÙg`}{â“6äsÒ·¶«~¾$B¿ˆ{»Tìº+˜\Æo.xþ„u㳫ãÔ\”òAM½“½•-î°Kb+ø—E©Í©ÞI6·ˆ|®þ¾#¹g[ô†Û«güS½éV<µè®žT5~®ã4©÷6«g*¬¾·äGj¿+b®f‰ t}­glË·Zs‘+qm eæ~@ÈÝ"ô/@Ÿúg  ýÐB}Hèƒ>*tè>¡C ÷ýè ~çh$9#Œ£;9׃ßÃÖ¯¢_±/zžèˆ´ÙèÖ˜{[m{YnŠœ»}z÷LÄ{ÞCã÷çڔϸEÅ ’–ûLõpú‰1¢SöM|Ž:-”½ø×êq0ç–¸&NÇõ în±ú3ø¥iÑ1sù\=fÖü)xA&ÆIÙÂÿ2-×K®-”‹üeéQóVò¢üMSó'Ö¬tj‰û¯Ç"m®mü®ÇWùL‹{9ü.µÅÇgŸsÀùÜèžUâZÇyü¸Ul¯öߊíÜÏk{¿lóþ ŒŸ‰¿J[l”Ïw6lSßÙDZg´¶ol®EÇuŒ0MÑgÕ—Ý|D£aæêo¶vãÚ>WÐä· [SÍâFðDuwL6 Œ±TÜÊoË£íä¢Â·›zn¤1X!óó½’écÅA”ÿÔt/ç@2&>홃iª¿*Füjù´©fÌwÞËXõžC'¿¿[¾¯ºÀ×ZG¢Þ #Çߨ ÊÅo‚~e|cªYÒ5ÏçéN3²¶«°$›¼¶ÐMùA=Ht{•‹xòC‘“äcÓcS7t°=ú\ȉà�^–ý5V ƒûÀ+Ó–¶«¼nÕ X6b}ß68qáeä/Ïm<Eû@—ýßuÍüÞ©“ß}{ù-û Öœ›ú–)Ûdñùß2}põ-Ó¼ˆw~gþäG»áºdæNWè4Îó{˜ÞðYïú²Ò9·jNVÒ4V¾œð°XÉqÊú*u•H?×ÀW(°< Sˆ å3ãáAÚß8Äp†õ—··ZcÜ7íùaƒñ…9·“ûA®›p±H¼vÆOf=Qç–É:r~rð;ʯrŸåWï@AO¯q üåиæœßA¥{ãÍùÛ™†yžžÎ>Ú— }º üi]S™™Ö{?ÜÀ2 ÇŸÉóÂqrîINžˆÅÉ„Pä5ÊÁ1¿`åÓòÚŸòztž¶¼äüyÚ/n>Ž´äi×/pÞmø2ºDÏÛU|ùàë{Ú‘çÔûT$½ØázÚ÷¾¦Þ§‚{¨Oæ/ 1Öì<3å³|FÇD“Ϙl§>˜Ž÷»6ˆ4aÜë }~û²m³Oþ‚.“ò±\.5~Ð%ß‹Þ+›uá¯[¬ûkÏ—}á1‘½÷¤Ü1Ä®=Ä=vŠ…™[[,yüú:®9yG8•ïõ\øc^ßfÅZˆƒê¶øWÓ&Ræ_™^èJY¥1eM™DËZø„¤¯öþ¡<ž«9«þ6³ðx«%c¡­¼£’ïVæKŽ‹…_\,%.pþš`øÚ®IöooŸ}ñDŽb³¬ogóÚPŽÈnùÝ‚¸1ÏÙ=V|rÖ‡ßjópx­osóÚŽñHxéý8öÈïFô«Õaõlõ|&¿“]¼)rvÆåë¸f£æpÓB¯ª>4Ïd Ÿ®ÆAõîÖQmù¦£1Ïtö¦†ùì^8ðEÓ1úqÜ›Žß>kþÂôpNNÏS’_‹5¿wC¿G¶Kñº»×y$ø¦ö lßÐ÷>ƒq<™¯cħ;ÕÜâÂo+»kÎ94ùéŒá96mƒíl¼£ßÃÞd¬2KÖxFS^·„º!'Žžfå϶BîNŒ»ÕøÂ§žýã÷×:ù>)¤?.k‡Ú<Ff‡…éW8·`[›ñÀW^j_#²žõ)7­g¿òýT^¤ÉCÞÿ✻ ‹q¾›½à´| »“õˆ]Ãõ½¬sœç´ØyoçÞ”7ZÀ¿:Éûvð™ck?C¾^·áºSï$½nU^½ ;ɸÒ6W·/º.¦öz'adð°k?êãô7±Óâ­ µÇð <çå:ã¥tÈqÊup= 6¿–t;bµöOzÝ|�_ú‰ÉèW2w¨=šÖ3—¨}QâŽvÏæ§LJ†\ӽõ)Òhàðø²Ð÷;ò>Ÿ%m4|™*.$Þs#Žæ–ú1²¸9Ã×^\ãyäa>Öýb˜r¨2xüU9¶2$Nå¡C¿ K׿1uÛ¢“eì‡>y1ðë|ùN#F=…xõÝÉÞLŒAó:àã„×µn˜ãÕs7Ÿfù\ù†Ï{¥©¶03<™s}êþÄã"Г™yÝ; kà‹>®¥ò~£{Ž’;H¹y®terýÐÃk ±­]Š8ÀÍ2„'uð{ÊýoÕú‡M¦5ª6•cÝ×)ƒäó fJSëéêÜ…þþ(åâ5–…ô¿gY¬ÚÊ·©3”±ÊVF®*C}ÛÞ›z’r^>•jãËg= Zk ÞY¡È÷HãÚêÍ6NiFY+Ÿî³ÖaÕ¼}êågÈëyßÈœ~åt€cšÏÁ¹Ô|YÇåu…çPäÝ3©QœÛæN›'Zmç~kîôîÆ¼òv¶½ÞTâMaùºç¢í÷¥ž9‘ã¼ ¸Œt¾û>­úÀyä\Æù5‘ªžåÓs_ô/è1TZó ªøÞ¶ÎÉ\O_P®Þkmé/G]w‡Ý”ƒã”ÎIáô^—7½Ñ¥°Áù'õÍn®¡ékàŸmÍÙ,¸G~?#þê$ù¸F¶öãÿ¸G³ûòƒÐýâ¯:&]~À¾."úøÆê‡ø<~ã—Í´grµ¿àQíÝÕè5/¤›i&.ÝŸê»ôÉÞ|´‹ÌÆÑA´/mÒ;å`ªolG×}&¥}dGšéŽÆÇ¬u°ŒgP†é3Š¿HßÔèôæèø?h£¹>œ¾{skÂyô5|WÓG×ùËÕ{z2|FÏÇ Oc bÙ ÓÝ“a?’a:º3LgW†éúM†™Î0Se˜iÇ3Ìô73ÌaSn ÒMA:ŒU»¦ ݤ›‚tSn ÒM1‡A_ôyj\hÍñqßì]‰Ý€kï«õ'k®O}õ<¤p§t<'bóÙÔÁ"Ä%|Î>›þn‡Ë°öÞ"Öç>ûÿ¯½¯²:ó=ïÌ$™|™"y A1d†oµ5ò%(" Zuf’L 6ɤ™ BuKí®]µE¤ƒµ±¥-í²­µ¶·½I³½Ûv·÷.e)Ms­R·u]Û¥V-mÜÿsÎóÎûÌdðÞ½{÷÷3:ÌyÞsÞç<çù>ç¼ï™'1o¡5(䑈}~ÖË|ŠÛ½^òÏáôU¬ýkµö5…ä×=]…”ãÓuƒ×´…=œ¦v™¾_дäË9ÉïÓ;$À3(®öñŽÆìÌá‹h~[nx‚ºe€ðáû¿:~1ëÏŸø·nvÏrûšŠ¾îê®8ù’sŽ@×sšÇ%=¸fÞù­ÒÏë&Qw2g è¨æ:[>¿ÇgÎ"@ßï†^`Ü׿xѹ³¬úÎ~fh’¹öBÏ„ªƒ½ÀûìÎJ óaÏE]¯£µŒË4¾.Õí¥xQWLk½ª®lÈW§ß×8ê û2ëñ]¦åâì,¡õ±Û?ò¯¤Ïk·àÉ¿PŸÎ¸“3MÜã±ßFã‘c'^dŒ¿YŒ µ§±¿81ií½¨ÿ,=D2¢ý³!Ó>Üë®§ÏÕϘµ¬K4¿€ÿ$ç%°¹ÉfßMMâ{o÷Îì©îòw{€´ Ù»èy×ÂA³'ÑÙ•´æÙmÖWµ NPg PË�s-ÈàzÆŠyWèè4âM’è ýcÿõ2ÁL§ž³óØçì1gGÒ¸ÍþÁ–*ò­¸çÜf}Ó~4>O¦Ý» óÝÀöÐgUšîxLïû€W“÷Nìó0û¡—‚SAÏŒžÔó›õZFîy¯'ûølÒâ½À=˜CvKv¨*èü‡@ç‹´–øÜ.íK^>Œù²›¤>vóOv›3C虳 ôÜí» M}N=¢÷â6ÿˆó_Ä“ÍÿÊ9:éðE\¿Iç$&¶[зŸ˜|ó:qtóG„o&Þ,†]~WÛ¼~N"gbØ,£+†_a³ŽÎ•龤ŠÏX¼ÐõˆíAîC¿ifA~o!&ß�}ïïö!Ö´ ü]꿯§{Èï=¾Èöž„zjA÷RŽðm:ó¶TÔí[»=Šö(¼¸¿z/í¥zúü'»tzº<×s ŸÞK-¬ì²ÞÖë*UÉ¢U}þî aïà äWð§ƒô,J)í÷„}}WÑÿQ½84‡=àßRº÷È^=®T=æC'ɲϫ¼œô<ç/Œ<û[Ç®¡Ãý/Ìù*›w{¦¿¤í´ä-ǃ~´3¼¸®¼°)®¹´`\ÏÕUNî².Ú7£N÷‰qU¢Ý·ºíºÊ!]9XeíóªÊUv¥OUÒ×;Ñöcœ4.Iƒ36ÐöChZF4ÑþÁÔNŸ© z½ülGÿÈÏŽî:½‘í§ùÙ'í¥ö!%.ï²Ô3(?xe’ì·ü¸ŠÌ¥õ+²‰®ç’ÓðK|Ù?? Û°'æâ'jhïÏVÞ>߃Áð´ÞÂ]g´oÅø€£�ã-L{„žºü¾'tÎT {+8áé«8®hÿ¶Ž9bïäígQîz¸®:F±¹úG\{ðSÎе§«Œò)ºNùûƒ³ëJzl²Õº¢ad´Ý×Ñ>YÕï¶µ]\¶»�4z®y|Ží¾•÷LçqY9´–?ÜÈOÎ (æ] zq­6b­TÖëd  eÊ­~Z´ ÷ÒlzXC:NºN2žÜ?ÒEíyÎõw¸îû,tûÁ¹~ðÉê5c(~üJ̱@/`Øúíÿ¾»°ÎO´?É6Òã°^…`¬Ð%Úôl&l#Ű‘¹úÙÀá"茿º³»j r°ºÜéªì.·­^Ð1X¹Eµò¿BÏÅz“~:§—ö)ž &½OÍIz»½Éê¯Òçñ>yeôg€äRxÌ›¬ÀØ‹ +ŸÓºXŠºêú:VÕW4T.:^ö;>#9#\qtFØÞýàÛFºvUIÏSæ@Sž{5ù½W¨ÂEd#Èÿj„‚˜ ]‚nÙ˜#ú늻ê=¥ ØÍ•ä·xƒöf¼ûJ§{z`sðsôê¹µŽmÓ»ÿÚÎ#ŸLk”çö Ïí®†R»NíóÐ<7ì&[›`Óót´ŽI¿•7DÏÔÑó>èêsu~Ðå9Z¼×9L9Ò°/r„׺½ƒÕ‘ÃøééNú©/ÄÊ¢Òþs¯ÑXôü|èε¯À˜,9øÑó¼Þm/›= ̵ªô7½[ýß dTBüC_EÇ=vE·§‹ü„>£¢·<iÑ3=÷ »®p¨ªs®sý}ð§¨ÇÇøË*Z/í*–þí¾Fí/®ë.m¡ùxeÿ¹ø‚i=Þ]ê©ùviO¡íß%òVo»z°6\ºÏö.ô^‡¹Æ])äëºNðþùáøÜ¾øÁEÊÚç _ý-$¿>ýŒ—ÎȦüàÜ7H_Œ½ÞÜí7:ÏöZzö:èIj}†ä ;E¼,Ä\©”sfÏ*xMäp­Ð]».¸ºämºGÿN™s/®‘-C7ߤûqïÆý<$äÄþ`v—Z×÷„­Nøà ”AV·îñ¡î`]>|ÛÝ·OëmìÑÜ®ô!÷90`¡íjMË ý KûÓÄŠ´™Â¹Rù0ùZsÿûÉG R_ ¹’×ÜŒª+ƒïðB¶¥øTpŒ¾ÔàÒõEh?—é)užu:>qÅÔÃ?¨jö•ðÐ߰ó|‡ÚMí*¨×~Dþ¤ÛGçc…½½>U6XMë=u•RŸz0vâWyÿ9›ä Y/£xú‡s ~ÄÀê=¯¸m¿»g#í÷ì£)ûí¹Fó gº¶¹ôÌHÎtmw”Μ~˜ìï›”+Ìœ~äõ»²ëy­Û—3ýhr&|ôû¦ÝKôzúÏ€nj÷2è.òî:?jA^AÓªúÏОúàLÛîα«!3|ü½êgåÏ™}`ß.Zï¬Ø5rfŽ¡cyÿH©Žå 8L,Ÿ™Ë»ÉÏ 5‘³?!»†O…^!¿éùöÏ@%éâÅ7µ=IÒ+äBÖïz’•ÄC´…}Ž|%]¯l£WúJÐÆÑ«Ö+Ê¥ª´^Q_}u~ÜÀè•É­Œ^ÙÐ+ÛÕ+ÂeÚ¡ý>¦'¥WGI¯L}1ê?‘¦Wž¤«W.j÷Pš^¡1kšÖ+ø3­W˜ßj^eèUüjyÿÙGŒNýéâ¿¿{n5X­ßC€üã ñ—û›ÜîνFÏ,Ö¡£ÃäK0Ç#Û†,ÁõkÒåÁ‹<ȳ”~óÓi ÿðjOФSZŸ€ì>%xvH<€§Žš¿P;m/¶õÔätU:Ïž†ñ—$«êJ¼Ò¦5Àò}*\Mq—ü-é"ân1âíÉçQŽ0XÝ¥ŽU%ýˆ¹Èƒõ>X5â.桞GÏ#ôÑ^àAZgðõÿ´çápÞaÐÑëëÿ‘‰K:Rþ[Ý9aÇæ½˜]ŒO1ë÷Dàì }Ç=—V'}Ã9Ï®Nú‡rž=¾×‡oÄ“Áœg‡W`ÛϾ<Œ ësa+ù¾g¦ò÷ù’•{´ÝíÒvG678óÙ—…Ýéµ¶ª]gŽÏ|v¨›x×ù£ì+è9³bßÁ‹áfí©¸¸;§.y¹æ9óÊÃÖ“Á°wh&øy ~ªwŽÍ¬Ëy ñ<H1‰ø:˜Ãüì?÷³?gv©×ÁÏ׉Ÿ35?‹KêÕß½ÚuföØ]I{µ¼ðHÝTÐuÀÈYëJýÞéo²j@u?ªóŽòY”wÏÀ‡u?Ö×)9×÷AÏÈ´ŽT €gvå33t "ûÿüŸž3T{µ¾„²ŸúDEs²©ˆuçö Þí/`{Ó–tY_%zññ×~DÀ,|ðJ½Öìï?àŠ(¯ÇwûØú¹ý»<‹èdL1¤hŸg ÿï@>ÇâsÚF'$­§æ'½ô¼ëòµ¯\®JX—ýiºLsS£Ë™â=rœSàý)âýåv=/ÒøQÚ†žë“ç˜ó\æØh×óuÖœþ‘ɧl×ï/¦kݤs õ8|ô^O=çˆþÜžcä£i å}Ö0x~¼< \Øäú'g$+Óx÷Ï{i/ô©+µþ–ôy¡G&.¢|qæ´~s WS.l%a›Ga—„‡Îk?>ö¢ã-ùWkN/ʃ”³ðs”ˆ ½&ùó¢w™õ¾å(î¦òøÏs ÌÊÙßÒØþ¶Ÿýìe=æy47‡ë¢=¿ÿÀnAzGóÈÙ½-‡ÿ™2PFž.u}fZÞjË^Ï/æßÕç;Võ‹¹˜#T«"9Ÿ}ßEðëG‡ìÈ\ʉzõºfÑfõ^©à{Ïþb÷A¯ÿóƬk¬á¦ ÌÄXgŠ±æ¤ÆzKj¬3û|¯ÓXg†+_Ÿ©Çú|“=::€\<ù.u€bèÙ¯8ç%8ŽþÈöh-ž±Ø?ßNÍÏKËžœ«c¢3‡ý+Êñ(&>LN£yµ3gïáù:ì¼Ö4z½”öѼ§�}îCê7sƒ³OPÿÇw`·ùGØ\Ç¡³Ý¿ÎQ†9Dü½Ýäy·N§{ ç7´œ‘£h9{“RÎg‡gÀNÓÇ“?j<sêäxîïÇO¾ÏQÊ?(O½ÏEã m¯c<¯Óx «ã:œ}2®¤9¼›OŒä›õð¿¡{öÏOþçéäü±tÒ¿9…ö.ñquy�ëd{J'«lß)ÒÉËíÊSðµøÐõyò¹&?;;…×h+‘_”éy„ÎåOÀ_•Â/ÿˆæ0ܶŒÛ®Fì,å5´ZâœíTüëå”çž{ŒðsÿÍWKõž-í·QýǨ^ë‰A¤7÷½Ù¨u ±‹ò´!ŽUÂÇû9V†3§t^:ÃöÓšцùäfŠU"ö»y°Ú¬ó`s?ôàLR̯ 3çWÀµÂàrbã™ã™y°ž_¹ôü0[ <A‡Ú :í0þ9” iœš×gàÀë³:¯Ï°‡’Qöp倴‡ÉÔ‡c4~² ²² ð Ä±‡=dD¶‡3ϸöpFÏÉNÁNÑüzrÜì­“YWòÑîq–+âw)p~=Ͷ¡¶7 Ýs}Ÿšôn}Ÿà]œûý˜yئy@1å¼Ýè} ¼Âåçȯ ?G>̺[0»ÿì)¯©S·È=¬|^'_¿kätë—s~ñ4í3{º¼kÇX¯?’{ÝéÅT7ñdæy=ÍOèºÙÿ£õ½Îó°Ö7Ú‡FÞ@ÎFë`°Óª=¹ªŒž3¡çƒ'­Ø}9íÿÑ»¨Ê{wðÁ†}:ô#~| þs=õ8l§èrÂã´®|`€r½)´f¸Ÿò=¯ÞC'ý§w† «IÛx~vPnHkê´¶ÞõYEë÷hýô`¬Ø´Fw' ßóiLÝ…×!ÿ£¹LXÛðÉËUÙ“ @‹É¿oÙ¿�öŽv´¿©×=}©õDòmƒ“‡ïÓk‰êý‹ÍZb1ýÎt >"I¹ å„YŒèõnŒë5³~Ë_>þZä%ÈÊ·˜ß™C^øŒ#³NˇSëô]žaº=ø4É„×ã*)F Ÿ^¸H9 ñs9ë·ã™ýµ†¿ÓGÀ{넇â7å!ä¬Iݾ>Í_šG‰\ºP¬Çèù{°Ëª¦kCÈua³‡N ßÕóT'GþnŸöw·l6þÎ<Û“Sü]ê¿SLqÆGºÿ©ÙuEÒÿ?‰˜Ð«×Ì´ÿ/Øã °’y\`bÀ@ñ1ŽàÇ!Š´¾‰1í{Å£ý‘Ö^Þ'מKÒÂG¶Ð¹)ôümçÙ¹æV¡†iþË>µ8ôo«9käcOÌH–âS¡çÉý#QÒ9WÎz­U¯‡_k§|í+UÉÊAvÖ¾Fõ'È×V%KiMT¯¯õlpí‘p‘ù‚n[ÕGאָ†žW_دn¢}¸ ¿ºþIÌÿ†=z]æöuÔÖëhZþfMäSÄ›!âÍ ÛYƒ ¸¼ gò†ô*"xãwÖCØ·åkÞTÙ¥'hƬ!\Ö Ë›í†7î:ßÑtÞ´¥ó&\ÁkùYxÓàð†øBýõRl×úýo˜ì¢¾ÔOv^zãqçç‰;e2î¸ñwdíù1?¯×q¼¤Ø3Äq})Š=4ß§¹Ü:[ârzí?ØÃçû!fžÖ<òM? ù• VO?ŒXõ&éüÜü§.c {dù´š·$+ëÐ^”)Æ91Í£÷KtL£gk)&91 å,²pžIl�SS±žM¸’Ö#ôû>=4.àXpb†Îó8Þºs¦a³þÿ j‡9㿱|aëç>®}<í§öŸûíãÉ'õ<¨}’‡ÖÌáóúÂ^ðtÙåÉjZgóœßøü“Uª ¸z)¦ïQueǪûŠNU÷Uv—S^GÏíQþTGï-m±~w«o é7òºÇ—ªò“äwé=ÐCÏ…ï…gÃ_þœâ졜gžÌ:Hgÿ«‰ôü<Øñô7óê¦Ðþÿ«¾°ïuÊ);N­;ë(}È÷hâ+üÞ†ÏÒ€G•w«]C½>5üèœ.uÓ!zÇ=WÇÒRøÿ*òŸˆÓ´NR YÚ'|vå°O?ƒÆ6uî©ô\SÛ§>÷ý9¹f%çš„cŽÎ5syÿc5¶®ý`©É5“œœh2çÎ:Ì_˜µ|×ÿ \Ó¥§3}-?µÿáx¨Ýåšë®‘wþ¼‡Îò7ϘlØ¿�ùt®¶›òÁ˧¦½µ—òTùë3m?ñˆÞÍ;ð©°29ß¹-l£¥ÇSñã}¹ºªXWõ:ÅoHþ'<)öé½( ñ;6ÂöQÁöq†® {9Ý^œõsw’½è}è#t±b°ºÏŸ>Ï8û[c7ç¾dì&<–Ý è|âsÔïÙ“:ß¡çúϾ‚9ý”j½¼ßÿ‰k/cÎý*‡ªû«û«ÅÜ/75÷»!5÷’˜û‘LWÛ•IØ5>ä_ç|ͳXa?í¹™³ÖoÜí¹î'¦¼îã´'ÇÏï•íy[¿cIïÅ¥<ã€*-è© »Õ¿Qã{öh·N¯,¡ç^õ³@:Ÿ(¤66rý#µÙ]¥Jw£=ts“y?XçíŠÚ~s¯ž‡#0»‡žN¥5Òáœgƒ?Sçô Ï|öðÞª®©fÍúíÿv,‡âü¹Stÿ =ÇÑ¿CÛ�Ý£yÕQÊc2æUF¯›¤Í«êÈžÆYg˜¶nB¶gÖMë&'Þ'‘G%iü‡Š 7¢õ$ÈbüÈ—Ú‡™Õ‹ò»š'¥ìþl+ÛÆ š' \AóáÜÄ)âÃ9v¾jÖÞ8ŽäÊyÛ¹·õÚ®ÁwǦö¤^°ùÕOA#ðû6áO?"ì£3g†sëtËw$×^q:ÆïY¼™ö×"ÿ§çW–Ú¨Wå_Á=}é}û% ïãЧå}¹Ý““Åî;ËIŸ|/Ø?rj}¼JóŠƒ}ÞJ½•Ì8í ùmãÜŠ.u¾óð¡g%M}Ñ=Ø£Ù7ÆôKê›ÎŸòèçM’~Ü3œºgƒ~í-ºO¿œKϦÝü?÷šgÕŽ¦Ú­Öí^§v.Õð¯ÎöÜò jãIêó0½i×ÈÈ¿œóK’}ñaø˜Cøȵ»(g}¼û‚~¼x¾ÎÂœí£¿ÒkoŠÏðävÑïëç÷}HrÓsÿô|X— B¥øäWSñíÖïÔ‡‹a¯¥‡rô;—7ÑžÙÞ±£})ò×Ðç®NNº2ít__©íÇ\©ô ¯Ï÷…zÆÝÐÿ0üY_’xºÙC}>¦ç•ùôžè¥ç“ü˜ßäÒûN„ü÷óol•·CŸ¸ŽÌ™tmä5êo6x}X¯w-E䇉º—p hûó|F0á�®íTºTÇï8Øþþ‘/O‡ )|ï·»Àó‡™÷¡ž§ö‚®{'½›­ñ Lï«ý¨šž«ù¶stŒß{û:ÑGùÜ`a‚ÿ¨Ü¿$\ùÔ’ð4Ô¡:ÝÞèÉÛtn;A·-E[û;ÔžÚ ½Ç=wnâÈȹæ3²C”ŧß§÷¦^Åõ°¨“òï�‘îÝùåœdt‚žÛ«9 ¿ò ½0!™Ë:4¹ùTú’‚ü>g_JïÖ>8/ì£r ùÚÔoæíéÎÓï<L5ïjØ¥"Ÿ ÓžãÄnsÅtfôKеÿ2º–§¦B×ôZ$r¿)‚ ²;eðÐoꨩ¨«�Mó*ͳôf-ø4Lc ÷†Nk­ß‡x»ÛÈö÷zlf¼ù´ÇCﻢŽÞµ¡gºþžËÁµí6vDúò+ª§ÜÑyÖ|| Ÿ.þv>‡3>]ü9Οâ}2×ö¿¢ßyѹÒ\}NÍ„¤yß%¾îaS{{'ÔYG¼?èÝÛUɶž¿'w€Ê>¨†~F˜žùeY]Ô£÷žÞ ¼9dl˜Þª#¿§ß½qú…Ÿûê“~Ã:^‚ö·Ósÿw—Û4÷{ûˆÇ¶õMï ïÉ3g0±ßßñάþ×NÍêO¾5«ÿÕ!­36ù³wœûotþ¿/“Ÿ¢øýZÿºƒÞA¿ÅÌÛ?=ííÐïÛ ÿ£vò]â^o_˜žÅF_!=ïJý0îÒYý/›ü^¿SD0?×{œÖNN ŽÞ‰ë¥çóú“ç´ãЉZ}Î�ý¦ÔÚïáúËÃøð;k¿ó^ùË4ž‡¾tÞCúwíz'„KhÚ«ÔS‹ÂÔ¾g‚²h]àÒY§*»/jÐïû áú æX“(f£<d‡)O(ÖÏ'‡?Û¹„¯Wxí&<tÖò òv}fúãk—ôL¨:ŽëS0Ž"g4¢tÿ†Úá»ï(¾1’Ù ±"=6ë³j'}¥d½e±|î†ÍôZuê‹ízO¤÷e]3Ý7û™ 'Ï|ebÝÔs>V=y:g äEÜOc¿Æn¯¤{^Bû'L=7lÞ«ò¿iÖòJߢ9æ }²„®tH¯ß¦½t2Wï;Tt_j÷…uœß?§Ï³±¾×Îë¹”òU„ü‹ô X¿g¹gÀú|>ÙÆAèÅ›0†>ýL®*@\¼†Þü ½ïï(9QÎí5¹D±Ì%ÏÞÉIïîɘ?¡(6íŸ_”ÛEÏ\”ìñ„-ýî?ÑtPQ<û ᄯ©† `wª€Ï•ñ‹seÊœCçõ$iíµ¤ë¯Ãâs‰~Î{¿÷¶û{öxê,ê¯×[7™ò~Èì_†Ñ/ÝÝû}¯¢óoºàóÔ0½¯pyÒ÷ê_Ì9M~ ôyˆè=_®ãÌý­ˆpÑ)Œ[Êÿ5âsŽí£y„´«á3|ù6½—tü¤ç›†KM~�[ZKü=¨Ï>¤w/)(ŸWsÈçæ&=Gsêʆé}´ç´ï¡g½ŠÒú¼'}Ìô.=ÕôuÑ3Su© ƒ¸¼9GñŽæDäÛ“Óu>góRäèþÁ™]eÈ',Ðy <~I}C³ˆûçöyt|tùùö0îqÞ‹ƒ½åŸg˜ÎÂuÔÿù‚Õ]ÔG±àgDÓ³¨ïó©ÉO¡_̹èÌ2ûa[t¾Gi÷ ƒ.óžÄ®´±ÇO Å|z¯6C6ÎïxTo¥lèòéƒ,&Óûìú}à¿×ÏÈM:ê³WóÙ«ûìµ'|öäà›0ïßò™ïÕ©§ÑN¯ïVSœµ„®Ï&-Èëú½”=ô\ÃwfщIŠq¸œKåÇç@‡?›Ì¡6\¦µÚ‹Q.E9å‰\.28Ýw,ÿn–ísÊ_mWʺǃðÕviF}|Là„c®HgŽ_·ÑϧÙeÑ'oÎê[M熾3«oÃéY}›þ4«o xå<½7¯¯®NÝôå}yý§x]–Ö;ȦKO¡eâEñwL™xQðiºì3Ϭ٤·˜+Ø4Vï)ÔÑ}©œý{É¢ÌÜß³ZÍï†gô9Ìô KWŸ~'Ä߇2½»Õç»î Å_ºNk™ï› ] *53~µ½¦m{¤¥¹ÑŽ'::Q»1ÚÔÜÖœhŽµÙ³Û"‰æíQ;±³=ŠËv´£#ÖqEÚ} ‘––æ¶­vC¬m{´î"¼vS¤¹%ÚhÏžÙx…R÷F¶GæµDÚ¶Î[I¼¦¥%º5Òr}ÇÖÎVܽrGC´Ýà ¿ÛÚ:¢ ±­mͪukÖÛõMMÑMÕ»xÖw&Ö7Ýmuì4]è?‡ÆV]aG¢ñ8®/´ÍJØÑÈßéˆ4$¢îõû:šÑ´ s5DPµœ˜…1g^¿¯¹QTªQ÷ÅÛ# Q»)Öa˜Õ''bö†}kê.Œ{Óª%ô}ãºë¯v8‰v  ˜Ð8’*Pü·ÎH‰DaÇ£‰Îv¾¾)³[#m9ó7nÏnì°g¶T$ÿnk‹w¶·Ç:ÑÆõíÑŽÉÀFšF üj{f£î¿TC¬u^¼³mÞ½m‘yk#ñ„‰í­)]#IÚÍmM1»-–°µÚAbÔM$‘¢ÚA?ZonM€!.âå±Î–F)VŸˆ4·9‚'–·w4·6k6%"|ËŒ:¹ÜiíŒ'ìz˜@3 ,ö¶îˆÈ»# ¤áG::";™ÏZN,ìD"Ò°Ívìf›V2Èîö›àÁ<ÂàÔ¤F¬9^ ñuJ¦w@ªmš]sí8.'¦] e¬þ^"z[$Ž!DÛì­‘ŽúÈV¨E Ì‚&7¦ôå]ÐÛ‹“œ¶EÚ[¢â~˜œkb§Ð9ºN_-vSG¤–ºÑP?¿Ø þ™ñLyC?Aa¼©9Ú¸¶¹íƒÂÆå_Ê—HÓ½îH}ÜknKñ³}²®4´Dâq0«µMkд&ÕôZ¢ðýJ;ñŠÛGßk3» ËÆÄÓ5ßÈô¦áÙÚ«Ë:¢PÉh›ã(F!3¶žå~BnƒA°€5+ÎG‰ùëlo„¬Rªf¯É2¾ŒFxb[¬ñ¨Sª©#Ö*¯Ì^ëJt9Ý{¸°^«ï5W¬•Ò2wßiGü¹F-ÝÄÞ?¾+F]5ž8|Fsƒ3 ¥MÔŽ?¤qð¸C½�<‰X:Ÿf§y×.×AâÊÊ¥Q—ƧÏéò¨Ë¤gUjhãQ4®(\GŇT,8¿=ʦúok4A’[GbèNWÖök2ì̽i|]v»k‹Þ·FUŽ©wYÇtM;e¹œJ†êŒGMŽ“UNÎp ¶cçæ îü4¨"‰ØF†¬xuĸ`”)|›)ËʆϤ_ï a+3%»ô3ð;9Áøž2­‹„T²±ô·zÙÎÛ#-ïFkÜ[FóÁéô]P*éZÎiÁ(¼£1¤šf»£ª¯×©ÂzjÅGÓ;6ÞŠš ãö÷¿_ø9ülq'›¦Ü™nÅŠkÆó«¢«±\ëh2qӪζâBZDž½ÿ;Çpkç¡+£‹ 'è3Ÿ'%³G»ò1i:OœÌ@ý.ˆÂŸ›(nÒ‰jöø™â[Ö«cèsZ¸2Øoèˆu¶Ÿ/È$é] HÒ·YL�Ï£÷©¦ceJYýÉX1_Ü2{Œ˜î—œLâAÆ-é\=ÙíÐIÜyè“MõßölþU_<Ÿ“LG•Es/„ÙTÿµ³ñJû0²1Aø]ÎÆ‘kuSSsÈ™é]˜\ÓoaûÙû`”æ17ê™LS)óÙ F=®Zµ†Xå|Ãßo5kEkj‰EønŒuÖ·€ž–XÛV§L`+-€Äù›àÀ"Ó7Áóƒ¦o‚-00}y>C<‘E“ÝÚ§–[TKsý½‘û5qŽð7^¿ySè†h_ÂŽ@t£MU ÕÙ‘ûÈa’ÜD‹;˜ö~P]Ïý™‰TK,¦W:Û5V‡ó´¸6SÆW³x")þŒÝ³ÎRÜú-€ÝÞI@áZ¹ëbmWñ/4ÇõL<µdú™_³ ¦ß -­ohZP[?¿vñ’…Á�þÔ/Œ,lŒ4¢”Š»™ÎC^¯¡ë5é×õ:†ŽÖ×ü5 KQX²fCA^½>_ØzF¬MwQ0f¿NOõíö‹Y9­¡Ì»YËhô¸¸¾ft½ô £ï}}^[slž‘Ši¸FÀ¦~g"º,µŒ%Ú¾®Û/ßéÈÖ>ËuÝþÖmз"Õ>ËuÝîU qÛg¹®Û¯…Ág£'ËuÝ~9Œ,ôd¹®Û¯ÐÎ$U‘jŸåº‘ÏíÚ3¹r1ð¦;6¬$¼YÒî_‹µD#m£ä;ú:·ßI“Ñ.s7Îês¾Ñ×Y¿H0R¯R0¯—šø7оÑ×M{„lç¦^3^Ô»°©_!¼¹Ä3ÖuÂoVÙœ5¶¬tr^¦R³o5…Œqs«[Sˆf/Ïl—â#·5 f/ËlGrá&Ë\ºfß9ª‘37½Ó¡oUf;Í'nµÊ¥oEf;Ã'n¸Âi8;ûò–žïÄZÛcmѶ„› P“šÌÚÙW8õ‰XºÿIÃω ]7^»ÆižB�ÌÄ ¸{ÿ–Ñ·iVãT§ÚgË“èŽÌö¾‚ð-7t’¼®O­¸s{qÙt1ûFH¦ž¹÷Wpè`C„–¤9‹‹»tge¬ñæ5£ns„ ³´ÍLwÛ»—©íì+Öèöí±x³»»åì?Ô¤.;{ ®¯‘Û ¦~}SS<jBhf;§Êuëé÷g^wÚcü·j¾ºî=íÆQ×ùFܧ–ró|Wœ¢íõÒ·ëæÓé˼.è[EõÂݧÝ8êºKß ª—n_Þ8úº`.þîtäd¬»Æ™,s®ƒÿ©‹Ä'É/Q±\ð_{®[Ã×Ù»¥î >H¼«ä8EÅ %ÇáVP¦Ž9f¬‘m[XÝÎx"Úꌻ#ÖíH†d³Ïlá‚ó­Î6θÌ_úı“CC`úuw3ýzKsÛi?ËlˆR=åùíÑöPCs“æo*ïÕ[ª·ßÌýŠÝM7ËKí.Fhª1º#µÍxÁûÓ)\LrÖ~ÄÖÇôûìÙÁ+2û;ß5j|bW¸ÍÝNMøÌ<ƒæˆÙ‰‘ôÊ {&‰FoÍf[g©¾­­!Ò¹u›Ë³ÕfQe›ß¦ó%Ë-ú/ë‚Θ]£:3kÏXëEi+@1=áÒùÿ£}VýÌ„Ù@M›eD¢æÒÒ@¢¹ ³Ø­î}ïj?ùjw?ø]ì˾ËùZ©ò+—ýøÏËeþóˆòE¢M‘(Š6¢œÏeþó‹r—-ü—+Ê9¢¬ÿ6ݼaÅšJÍK´jÇ1o{¤Ã)k¸1º}^|[Êï­^³ÎëçÁlÑÔc¼E½÷÷Þß{ïý½÷÷Þß{ïú AmmÿÈïù÷®Eù—W£ü.oBùu.‡Qþ5—[P~•Ë;Pþ—Bù.ïFùrùi”‡¸|åsù”qy�åâòQþ!—‡Pþ—_Eùï¹| å¿ãò”LÙò×ò¹(WÔò9š(Û(ËsQ~ËKPþ[.Óï3™ËPþ"—ïBù—·¡ü<—µ|Ž&e‹òg¸ü(ÊOs¹姸|°–ÏÑDùÊOpù[(ïæò÷Q~ŒËÇQþk.'Qþ+.¿QËçF |嚲LJòG¸\Šòý\ž‚ò.ÏF¹“Ë PîàrÊ1.¯E¹…Ë[Pnær#ÊM\n¯ås#P~�å0—Fùn.ïEùN.÷¡¼™Ë‡QÞÄå—P¾…ËßEy—¢|—_Fy5—_Cy%—ßBy™){Ê×q¹åk¹\‰òR.W£¼ˆË¤ÿó¹Lú_ËeÒÿ«¸Lú?‡Ë¤ÿ³¸Lú_ÍeÒÿé\&ý¿ŒË¤ÿ—r™ô¿’ˤÿs™ô¿œË¤ÿ%\&ý×ïYrÚŒÜü}”ÏãÛÆ÷T¥JÖ㻄¿©n/>È·Ëvà9{É}øF.žKÛr”Ï—ÆFΞVê±ï*ÊÙA¾+ð¡Üý¥–ÞeÊö)Es�5…ê(ß/Â}g˜”ˈ˜ØÈÈŸSnÄÛ=ʹüówó– ½>‚¿lߊæZø³>¼QY”XŠô”ª’?J³¤ã+¾ìªü’i|}×íÍR·!…7½î|ýTŒÝ§ŸÙYêœùU6ú準,uM•ç,uL§¥îeSwI6ZN›ºâµïŽêûãðýàØü°¶Íëéw)ÿ82éÓu•©ºÿ[–Sð©‘ßq‘|…þýjA0¯ hB0¯hŸ@�¯),²æù½¾`žû/b˜×´o!˜× ´/#˜×Þçà㵆÷9ô˜ukå6†Í…gÛ Øø Àf=ƒ×¼Ú?œ¨d¸”áÓ —¸Ó¡¿œa?à |ß&†+ÞÂðÅ ÷0<Q‰?¯ö«ipe<)¶þÓ1Ä·•çþ$Á–O…ãë ¦öµÌú´8^îðC~¬uø¯ ÿ7;òTFžM÷o‡üëöù?ø´€?ø ÚÁoü_täkù~ѯeäû=G¾–‘ï£J½ÿoô?ïJ3~õKÅð¿;ò·Œüï臥õÃe©U¨<;"ÐÜoMVž›¼\Y«~Ö€øÀG¼EY7ø\*à&ÀÜ ¸]À À¯ ø~e­¶Ü X¶ÿ8à:ïÆý’Þ'/ð3€ü<àï ø0ð½$à¯*kÏ7 ý°¾ø€¿¸VÀÿxHÀ'ÀBÉŸ–ø_,ïÿ7À’þ7Ëñþðöˆ<y€?<îðÅ€ýž)€“žXÈÃ3GY7 ú<ÔŸðÔËþ¯Ü%àU€ðÍ€…þx6–ý@Yk%½õ€m7–ô´–ü¸°¤ÿ#€O øcʺYðÓó(`I߀åý½ÊZ§ü,`Ùÿ!Àßß�–ô¼XÊãÛ€%ÿ¿ Xè‹ç•µ^Ž÷`ÙßO {ò$Kü¿¼[À¿,û{°ßÀ‚?^šj ýó�ãó–ãóVý{§‘‹ðå¨ý{¯,ìÏ»�í%þk�KüË�w x `á¼� }ôŸoöæ½ðjGKü-€%ýqÀ‚?Þ+ËÙÕðG yx,ôÇûIÀß>Àr¼ŸVÖ&[ÀK~°¼ÿ+€åø?vÐÖûkï�?-`Ä‹¯ ø·€ðøûRö]‚ú¹®Bý¯@}¥€ï„‰pê+Üx‡€»¿ à'/ð—� øëʺMøG_?`Áoß÷� ý÷ÁßÞ&ôÃ÷À‚¾Ÿöåƒ}Ü&øéûweÝ.äáûàÙ,üNè]àÒ›S¡¬;DûøÓ;„þåTú—3°Äý¿CØkε€ý9+”u§Ðœµ¨0üçb|9w¡}¥€¢åýÐ÷-uFüÝ"ì!çÀ’Þ‡ B¾9ð‡[vˆú€…þç|°°Ÿèï9ÄÓ-Âr ÿóÊÚœ0üßf‰o°_ÎIÀÂå¼|Br?·Hyü °àO®°-`ø»-Bž¹å€…þåN†>t ¸ °ðG¹ˆ—·‹|"7XĻܫA¯ /þm³ìùÖæ°€1™Û,ô%÷NÀ"Þæ">n–ýÝ«¬»åø:�KúáÏî®0ò«»…=åþ5`ÙßÀr¼ÿÝB_s!ÿ»û ùß-ùùß= `Èÿ.¡o¹ÿ]r<ÿ]?ä—П\Èÿ.Iä—ìïuØ'ÑÏúšûgÔ }Ë£åÓ¤€‘ïþ3o:ê…~å]ú•€‘ÿ|@ð'o)`AOÞõ€½yˆWôå!þ€¤çÀ’ž`¡ÏyÈwîú›÷!ÀB_òv®0âÓ²ùò‚Ÿy–ô<…ñïãÿ2ê…¾æ!¹SØ_Þ}’XÒ÷?�ÛFþûáò^,ô-ïW×!·¿~ æÏ,äáGþqèÏ? °Ðo?äwèÏùÝ#ôÍùÝ0äwO—€!¿{„?óC~÷ÿä‡üîüóC~÷~ù!¿{$ýß=BžþÑIÀ_HÒ ù…ÿü_HðËù…„½øŸ,éüô ~b~úæpRÀß,äëG>Z/éû!`áOüÇKú^,éÇ|¦^Òû`Éÿ·�Kú‘F„?Ì÷øó‹� ùæ#ÞFD|ÊG¼„Œxñ)ñ6"ø“xö™xòÌG¼šx–ý!Þ†eˆ·aÙâmXØ[>âmXâG¼ ñ6œðC€…þäc~úŸ¿ô)? ùËñ Ÿ Éþ ÿìò }ȇüC²È?$ô5ò ùçCþaaùXø›|È?\'à7”ª£uØGÍzFþYøá? r”'a3 ¼ÅÊjöV€ùjƒ ¯`*`A_ÁLèûµ*¥ïó”Õ(è+X XèOÁuJ]Ø:cè)¸õ‚ÞÌ„>4ã/hl x;`‰óÍa XèOÁ'� {(Àü AØCÁ3€»üYäwBþðÏõB¿ àŸë…ü àŸë%ÿàŸë…>À?×þ¹^èCüs½Ð¿‚_–ü§ù„üF>U/è+D>U/äSX€ö=nûÂ)€ýžø3EÀס¾EÀë�0”ÿÖMß\#¿ÂêDýcÀ'Ö· ŸFýõ’ÌŸî“÷ÿíW8‰ö]~õo¸pæ;ë<õI×ðûWÀÀý8†úƒ~D©»1žâf¼E{Q¿VÔ÷)k¥wôe¥ðçE°ÿ•B¿Š­ò.z °wôg•°Ÿ"øƒUB¿‹Ÿ­ö[t°ð÷EЧU]~ãò.zôvábÒÁß≇˜/ÏBý_øUߤÔ&º¶áO1æ§;¶ˆú­J-Ÿ‹<p×oGýâþQÐg øó€ÿ‹_,âiñ?àþjÿðlÿôKÀçpÂ…/*<WÀ3p¿˜o_´õ x êåýßΰ€Iÿûü1Ô× x?ê%ü7¨—í¿§Ôzà·j .:ú_õ¯¡½°ÚÂXs”ÖíLû’|ÔïõSqÿ[®Fý]†¼Ö‚žµ|ÿ&Ð'øUr/Ú‹õ†’¿D$ß Ü¾í…½”<Xö÷M¥~[§¸=äµSø“’“h/ÆWòGÔ+.½°oé個 ýÛ¹ZÀäŸ$\XøƒÒN´úQ ^½Ÿøu­¡¯ôÓh/ÖßK¡;ÄúLé·p¿°÷Ò£¨o0üÓŽSþ3Ú‹xX†ñìø¡€1žG¼íÅzLÙm€…}”EÑ^¬÷”Aû¸¨ÚÐ_öI´öVö5È_ïÏ3 ÿºS¬—”ýø„þ–ýõb¾V^ XŒ¯|Ú‹ùIùû”Z‡zËgú/_öb=¬ü´ñ¤œÖ§„}—“ý }-G¼Ø!ô³ó¡/ ø ùëZ†¿Šúï ú›:ß }¦ç; G¬•ÿˆ}¡³ÿ²\YQ¹ßq#m‘ó‹&[À[�Ëý“`¹ü¤Iî´–û3È7·Êý†û× óÿ­û+[Åü‡âýNɔϑx{8!þ¤^OàøôSüiùÅŸFŸâOTÆ'ÄŸÆ#þDe|Aüiù ÅŸFï¢"/ÂøraÖìŽ)5~åmaþj§ˆÏ$¼“.¿òÓÊBN­,ÞŸ›Pœ^?áâ ØVÖE´~6ÄíáßÔíçÁÎ'lTV õ×ÂõÈîüž°CYE4~ŽÿVV1¿‘áÇA/ìÓJr{ä“ÛˆŸ…–ÿ„Ïàþ§éy nøÅx'| ý}{¹þŸ•U¾›ž‹`˜öÉ>®5û‹~úCôÿ ð€ÏAГT©ýÊŠBe•VÐ3_æëQþÄñº"ίŠ:e­pòªß�x­€Ã€§8øZ#_q—€w>-à!ßQÆ|jÅ\÷ö»pæ~pÅg”§S?/`éýÛŠ¿>M/‡ò­»ü=ðç ·§ý_Ú~‹aÜWq<}¿¶þü¾ O�üÛ }ÚäÀ†I{3à|¥>Iþ ®\÷1æ¿ËÃ\OðRØ7­w•iý¸øf¥ôzën·R÷Rüü.Ã*Ïj_ôA¥”÷pýÇ0$Ä«‘á/dÐóU¥ŽÑx+¹þ§õ?Wªç´{ÿÄ¥öÕ øJ¥ ß’) ß”~ÿÄJµQ|¾‹ë£õ-ð.çy2ý¼ÆÄO9Ï”iýžÞ´~×ïÄ/fÜÿÕ øJFü·Ú¹=l«õ!—ÿ!¿Þ»(1ð%åÊšp„ž-b¸:ß%W)uÿ].¿.Y¬Ôg?.A]ß^T©Ç_ðç”z‡ò½- ÿØÁoüÿšÑlùý”0ý—@Wûû¨ÚÀtÔ÷ã”ÿdøÚôû+Q÷èWn×C7>Y鎿r»R/n¡}z†{2îÿ ?oSQ®Ÿ·¹ø*£¯–ßèkåóJý3ñƒå_ùJ}óûßëøÎ*õ7°‹õaÒeJ=w—ËŸIËÓÛO‚>í&ò-ÖoÐþá7\~LB¬}z­{èÑû%þÇ­vá ?ÎÀ¿_©#‚žo>Ìã%øçÊZ°X&RÖRðÐóéØä&ypýdä’Q|[Áð:Ѓù’ï(à­u…ËÿÉ-ˆ÷³~ÃN&Ç‘J#óázèÏAAßäg/ê\ùOþJºþLþ>üͿؔ\ û¬n?ä<ÿT¦Ÿšü&èßáÒé¥ÐŠ? vÖF˜_—b<ý»úikF=æ*ýÿåú'œç«ÊôääÒ¦ŒöOgÀ ySÿLï¥ÿV‘ÿØÀp2£ý¯•UGú»ÍÔ—LVê:²æ÷¸ù/½äúÃ)UÏ—ÿSnPÖõÄß§®WꟀÏ3Äð#àç)—ŸS /÷cþg±>V ~›-ê‘+|c‹ëo¦`þvŒlj.Ão¤Ó?¾÷…×ÜöS/Qê+ÐwkÃð/¿E¾h=ÀðìkR˜á :µ¦Iù ëçÔ•uƧxüSf´ÿ¼R_¥|ãKÅbŒ÷;ž©?�üC#><)üÛÔ˜"ÿZ_†ùyØ€Åþæ²E¸À½ÿ²@‘«o—%œçÇÊL>‚|yëÑ“¿Ï+ÑöÝBóï— \´„Ÿ$ÿƒØzÙ·¿§0> ñ²HïeÈ÷ßêQ)uÙï3êÿÌÏ›ùLÿÓü gÈ¥güÏ'·¸ú3íeP¾¹—aØË?/péÖ©Ô'D¼ŸÖ }Ðó†v?Øž§!ÿýùÏ.†|‡èé3þ¶úºÍïÒc{Òé·q⥗õdž>}bÈíß¾0Ùû»!ãþ{•õ~Ò?Ž'6üÑ·É~ØžìO)õ9ÒÿØÏßÖBI2üÈ7ޏü¥gAŸXë›A…KÿtÈü‰m®>M¿VY× ¹ú>ý¥¾.ìcz‡R¿!ý:Äð.èâ‡÷4Ã9ã±õ¿ÿ$}|ÓŸÏ€!»cÙLÿæaÀkþSz{¸µéË¿*ø´SŸÛW-Rê³S\z«nÇxçºã­Â\ø-Z?c~U=’OÜ›? ~û\z«ÀëgE~6µGYs†èùg®G¼{‡ü5˯ꗀé~öG3öŠüpt7vÜÿŒe½äÏØßT">ÞOë]ìÿgdèÏŒf¥Þ¦õ”—¹ùûû('þ>߇=½âǼm±Öè¼Ä}‘–ýÊv}gsKã<óRô<:½«­¹±9ÞI4l£C¼:Í5qs\\üª-‘šµ¬>¯ÁG­hŒ$"5ôZÑÓ€ù¢»â‰Î¦&´¦×ªC¡Pc,dNe5&bñP¤s‡©ÒïO‡;[[wª¦XGC4”ˆ…4’Phù¦õCk×ܺ)´" Z¹:´jãõ7¯ -[yÚu¸dÞ%¯ Ò+ñí-ÑD´±& ÚkjÑöÆå©Scet8‰{äçÍkV„F-Êr¾dÍڵ˵æ+`¾‚æk¾ùZ`¾š¯Eæk±ùZb¾–ꯠÁ4X‚KÐ` ,Aƒ%h° – Á4Xæ,ó –ùË|ƒe¾Á2ß`™o°Ì7Xæ,ó –˃eÁ²À`Y`°,0X, –˃e¡Á²Ð`Yh°,4X, –…ËBƒe¡Á²Ð`Yd°,2X,‹ –EË"ƒe‘Á²È`Yd°,2X,‹ –ÅËbƒe±Á²Ø`Yl°,6X,‹ –%˃e‰Á²Ä`Yb°,1X–,K –%˃e©Á²Ô`Yj°,5X–,K –¥ËRƒe©Á²t©ÑVqȘ†åYŒ¤¿¦.”:ÇÜäœcA-ÜsaBò”c ú¤«1íÀ=Û–opŽñwÎå µwÄt®v®j í¬0ÂoŽ? 9§3P:Gié«úü0csltµluµlvµlwµlxµlyµlzµl{µl|µKÓ¸ÇFËÈÙ�lŽQ3r6‚�[A€Í Àv`C°UØ,ó1_s@œ³cÁµÜ l€-6À&`› °Ñ؃‚Ž#â^؉Ø‹ØØØ‘Ø“Ø•R~ñ9žÍqmŽosœ›ãÝ÷æø7vp¶ž�›O€í'À` ° ؆KwÉøØŒlÓ6ê�[u€Í:Àv€a»*ÍŠg*w¼X­‚„…Ì™?¦–»e`‡``—`Ÿ`§Xäø|î…ýR€S€=S€Ýd€ýd�Žr•1 ŒB| ·©äN:q…;a``€Õ6Lã:ÏL7aw``‡``—`Ÿ`§`¯Xê„1'Žq cË ²eÙ2ƒl™A¶Ì [f-3È–t,ÓœE©/±]Ù.ƒl—ÁN˜dÔl—A¶Ë Ûeí2Èvd» ²]ç;Q—ñ±ÙƒlA¶À [`-0èÄðTg|Nwâ¸ÈHî„r'–³NNÀøØƒlA¶À [`-0Èd Â!û”F¥dd ² Ù„‚lBA˜–>§Ð\á®`;@¹BŸvjlêúÆÆŽ¨#)ŸA6– K%ÈÆdc .rRÁ"ç~K%ÈÆä¬"Èv\èdJŒí"»ÐáƒwÌ5ƽ-ýleºÂÇŽ’ɰÅNÚ‰"–¥N" Ñ!yôËMx[éF­ ÚPú1w«8 ³ëÑ‘ÑuXÜeºcHŸO§ì†Ìoɪ̹‘© ¬îévŒôæ4¤òGRÁÉ Ç¹Û…Ü‘d�¸*¦sMY(íŒc§rRq™#hé°s2’ÒÿrªÿåÄOÿË©–þ×x½”lÒÒª1jJÔ(ä¯B±–ÆP}gœš5I*‘)iý#SŸsî4Ýd,ÄU›¿Ÿ*È®cLr»<99ËÈÜ„ÐbšžA¹×1Ç ¥°[åHÓU Ãø}™tL,—Uîà…d塱.2cN¡ÔO³„âÄoѯøšô~M2JÝŠ;CÎ1áàýææÆ(óßÈ/ݺ=S€¬àî¯}P<oâsHÿ˜)©>`lŒ xÔ‰ùq•PjªO©Ë¨,…k~(„'¸OÉ£”[É·þý¨´Jà ñ‘ä‚yØgVç"ÌÃáEÊÎ3g ,Õ,H™ñ@é×tòÎü¤PtGCjHŽ™¤Ì3…FOÒ Ð?nâ†*¡…™ÞNŒÒ½]þàÍÍÂi¦Æ¯™å‚£ì„îæÀ±*å3]»Ê”a`LbÖÉVÚF´#ÈF;¶§)Œ É14A‘™ƒ¹GXSëQ?†$O{3g“žþ+#®ÇÓ7 ¥w{Ìœ¦.þÝs †è(¿èWÍåU)Ñ0™Œµ¦A¦†R§*ç@KÔ¹ÖŠlÝÚÝ -Ô?F×+AÚ´¨k¶…ÑÖv ‰6„šš°±¹#ëõÖNЧ[Z;ÛZ#íY›ÄÚñPsã8‹­tÈch<ñO;ŠD¬]…¶¶†T<hÞQSk.·¶S`+BC¿¿66æ6â¯{¦©QµF¶67•"÷…èœ}hL¶Û£mÛÉ-‚-©ŸàßHÁü{)ØüÎ@ˆjúUú¨FLDúß`:Ž%ú¢³Ê·rÝŠPhôÒaCjé×M;³Àç”ͪ cèH´ÕÄ•†TèÖM×oÜR‘zŠ,tì'T¬5ïl Qdc2^Tû›”{ mC¿°�{ 4©…ØmÈý=ÊPSKdkZî¤ËcõM>ƒ Í_±fŠ·µÃºMcS #Ó+.^c épt̶ÚYjÔ·õù[­§©Ðšqú©(òhbeÈ_<ÑÑ…ù™36º±Qß°vý²ë׆֯ZuëÊM¡M×/[»’‰ê_ݽ» ÛÄã…šcõcRƒîáºõù»nžp>FëCj×7a¨Ë´`تÏöD‡äcÛbª%¦Î+±Uã0,Õh<®º¼_vÞ³ á}ülèÆí¡Ñ­Íq _O£Z: í;U‚N_]¶sR:Ò¦Žè8•àÕ­;[ëc-ªõƒtâq»2Çäºýø¦DÒ«ô‡óKȼ u×<^óµç 5"W“®}ú×DÆÑ!“nžW‚d=¸Ç#€<éù1‘”É_;´Ç'¯ãÑoÂrêçHÇÖ¸ã¶Ä…¥ãá¸~ÚåÛ¢ Œw¶žG¦N¢J¸WoÌÐYK3ó›PTÇoö’Lá8ýëTÁ=ˆÚØ$¬±ƒU›÷Šôo¯¤E˜ùAÚ°qýò•+nÛ¸2´vͺ›®¿a¥ã\ÆéДn7îz̆FÝ9wrt¾±%¾sl.µGÒÙFm-XýÛª*´âŽu×ß¼fy†ÆÒßãè¬ñ÷©A¤\oóÖ¶HKšSÃ,»ƒÍÊ—ŒÇm7ãKžSÆÄ?®‚m†/‰ÝgΔ®Mÿ¡c ç3†ó87(¶ƶ‹ËQäqšh­«ž~YØ(§ºqÝšÐú¶µ1L‹ê+±*§h͉QN¾)¡ãúcžu¦À¢Wn4‡·ILƑ؊Œt¯ýüüÚÀ“9Çk¿³¢èÎVRnEO·\¥@.9‹^-ã:Z‰ñ:ù¼a+ÔÒ\âÜ,]ùÆaŸÑzmiGä3œÑ 9ó¸9îì #R O4%fB.U‹"¬6:ÀœsÈ욟‡Bƒgü6nÞv_Cœ²?'yOWÎB7­a„û9oˆ]~AéÖ…µ¢Œ\*�\|öÆhûx£c¶i¿p›þùÌÅ´.ÇsWès4[pk0ác\¶Èex‡EY}]¡PQÓÙÚÙâpqóy8ïPÅ«áãèXº3Ò?K7®IjÈ„À!2ËøÎ8æLMªÉ,è0çsÌ)!“W#«8:ª»×MNnq~¹¯2NÓ]`WuYÓÙØ¼ý‚˜ÀTÐ¥íKC-‘±ÎDÈü²¡ËRþ…¿ºÙ3®¸µ³mý­öš@­}Cá¶¹Áç/Z°øªÚö­Ñv;X[»ț7FÚ:#;©ÑÂw…/¿ÚM%‹–Ì¿ªv‘½4Í®]<¯60/X¨-P7,_~µ=û†u·]aÓ/ÞÍ·gÃ\µµWÍ-¸ª¾#ÒÖ°íJ\ u@Q·]ñú÷Ðúÿ�ÏÒ´¥Äš÷E:¢¦]„VíÛÍ-˜IGãöU¨o‰t4Çmúu{ec3-2]­1_¨YX[«TͶH|›ªiÜÙFI¿þNt¨šŽhK¤fk,Á%½†aŠÑmÆ‹*ÕèŸ á‹í-¸Q'„5zÒ[£'KÎtÔI¤µ¹AÕèE/ÀæëÞtLßÕ€”D¤߉ý _C+Œh­ïÜŠÔ×wD·;+\Æœ$ê”™J´wÖ£Ïh<…Ýê‚z̦èTÄ·qïïæÏRæ\RzŽv¯ùäT©³M͉¬ÊœMZh®Óù§ô)¬SæÌS‹ÛÐ#%|­Êœ‹J}¶²ìþ.á{–™gdésq-÷[Ìuô\ðt·£J+ÉíJE»¹n;z]Tq0Äí&ˆv׈vC|öêCÜîbÑnƒÛŽÎc¥>šÚ]$ÚÝÉcÊ5Ïa›g±Åxß• ‹v¶óNI–vM¢Ýý±üµ¢óîW ·óèçµõ3Û3&di7…Ûyõ{E½[”—Ù/}nãvyúý‹Þ‘ð¾œÑŽÆ›0´¾x‹²ô» ‡Têì[ÝŽú¾Üí7Q©,}æh2 }!ÚF»Ó¢Nòå!·]'JjŒvÑÎv~z—;K»GÜv÷mR–~ÇÍ—¥]h·í¶ðµÌv»™¯~_Ë¢gÐ}«E;‡ÖO)×¶”i7·Ké³n=Žþ‘^ZžÚ¦ÝAÔÏxÉE—²£çE¥ïÅmŒngMJÇ÷ôÎÐŒÌvÿ PK ����v©Æ@���������������com/sun/jna/sunos-sparcv9/PK ���‹g?“äÃÙ±���+���com/sun/jna/sunos-sparcv9/libjnidispatch.soÔ½ |TÕµ|æ‘ÏÀÇ ¨T¨QáøV…¶ÖA2!B |f >">BD[G&*`|´¥­Õq´ušê5¥)¦\+SµšV[s©UD%ßúï³væÌÎ9C@¾ï»7¿ßÉ™Y³÷Úk¯õßk¯ý8ûT_vÕån·Këþóh§Ó Aüâ¿{h!˧t†6BÎiÓ´T®ÕÿNºûÌote0ý€¸ûùÞýwr«yÏ=Û’ÏÈ·%ÓÌÇ÷D3l¤xž®çdæƒ?MZ/]é\ŽäEW_ºúñ÷|È’ ¢k0]C^Ù|Fôu?‘®“”´:ßsø>’®Sé:¿¦+—®1ü¶:“®qt§+ÏÂk]é:[)cßÏå{>ßÏ£ë|þ|!]S5iwM»˜®KèºTëùw™åóå–ÏÓèú]WÓ5.Øäû–ßÀ÷Ò5›®9tÓ5Wá_BW)^`¡—ñ}!]?âÏ7Òµˆ®%t•ÓUI×2þm9]+躙®[˜v+]·Ñu]ÕtÝIW ]wÓu§YM×ýüùAº¢«Ž®õtÕÓµ®º6Òõ°E¾Mt=B×éú Ó³üâûãÚVºÂt=Éߟ¢ëiþ¼®üy']Ïj=ÿìÚaËçþÊo~Ëç!|?Ž®ã•t#”ï'+ßGÑuŠMÙøË÷3~ÇßY|?Çæ·É|¿€ïS,¿]äÀï º øó•|¿Šï×XÒ}—®ïñçkù~ßáëfÒu=]³˜vß‹è (eÎãû|™ð·Øò¹‚®›èZj“®Š®•6ôÛ-Ÿƒ–Ï«èºËòý^ºjéºÏB{Àòy ß×ÒµÎAÖFåû£é6Óõ][øû6¾79¤Çß3t5§øÝúçòÙÓ3è™~šþžßÎ4ƒ® ~é»]÷IÝÂ7_æ“é]¥V–ßKðYÙÁ4ø’¹ úý…,"ê¾"A/~ƒÓƒ«ôú8}ˆþ­OÐ_‰1>m—C½^u ÿ.Áçs‰qÔëŸ ú¯} þ®Á ú/ ý<{þ.#‘þ–�ñwô‡¤-É»*øÜâ@Ð/ûLÿS1 ½Þ/ýUR\àzÊRî"&R¿äú…ÿWè¯'ø´™Föu}há/}–Aô¯ô—?.²³[僚&q_{¼Nô‰‰ô?0Ò›û{yÜ—%Ò¿Êý8ìâþQ‚þZ£…¾&QîñÒ÷sÿ:‘þ§ŸXÒ¿¯Û—û…ƒ<‡|²ß'}{úXð }2±ðŒ¶”+}/µ Ï• 9³ëMrõžëíËõXÚ×Þ×X~ð¹ËÒ®-vñlsà³cPwß7ç&¢»Ü¦½F韷´#‰gŠÕ<í ú¾yØ—ëí›HÿÇZN úéÏN¤cØË;3A!—S=¼?óh'Ó©/ñÞ™ G.µðÙ– ‡¤Tï úͳ™Nwïk zÛ×ÌôäÿØ’^úIÒsZÿ„ݯçØE¸N;)AŸÅút®ÓÆ'ø<ÛÎ| ¢ÿÀb—ϘNúH»Í"ÿ.–…ÚAÚ= úƒR?„Ó´M ÿÏãñ—öx"ýMÌÇMzN{&Aµ&ôöVBþqmÌ„T“×mõ“ö¹¥\9– }¤§'ø¯á1p’~¼_Ž-ü›,|Ò'Xô ûÒCúõ–vÁí±Vú½ööJÐÒ~e;¢¤éÛÒ[ÚŲ!{¥ïö›ceü•þß|¶:Ð;-ýf‰TN†Ë‚ÿw˜Nþ'ãäý99¶2ˆ~©=ÿŒïXü˜l×dÏ ®þÀ¸r‘ÊXž°o1ãÐE±UF]"ý~nîÑ‘H_ÂíÈEøÉh³Ø]çrIÞŒ,ô …þoKQÇüƒ~&èa= ŸÊ´èá?²$<d:è!óªDú?³<ðc™å ù}<Vr‘ž3×%Òï‘qõ/™Ï%è_ðXöø‰þk‡r_Ið?]ƈ4æÊüo‹žL§~5ók{>Y.KüÆúÞ²ü yÞæxúɲÆ{í–ô%Ò×q{‡Ⱥ.AÿÇ®ðŸY·YøD,|,íèMK˜eñŸË~™ì’õŽß[Q{yÞ2Êú«r,ŽøêÞAæœýÍ‘±8é;ëü`¢ÿê“e‘GŽù©ŸísŽ_Μø·åö¹Ø^Ï}¦%ì2Tú*·Ïé+ò/0˜è%zТ7釩^}íÈu¦ìO©}õù±Åɏ޸õ‰Xô)Ç0¨—%¾ý\g:éµÏA‹°Äÿ}-íåé7 $âí¿dGòô½1'Ì—þpÔ×!Ží{—ÅŽrþ‰Êé[ŸàßÈöõîú>f‘Sêr:Äù}[,éoˆ÷úîOè3PÁtÒw߯éâz‘ë—ˆWµ÷¤þɯô»Ø2‘z#»÷[ ÿüZ Ý‚ÿì7Ütï÷‹ä é¡ß –í«ßòßÀñƒ‹ôÓ¯#‘þÿþœ×±ßöP}ûŸà3@úm³ö?E·Õ§æ0.èůÊ1/ùõþ‹Ò/K”{†,Šâßþk|–¿háJ¤÷ÇM²—ôÕß2i9ÃRßÊý—EÏÒ?žû;øÏÞD¹ÇI<ˆ~ºCzËøe…l/$f¢¿v]Íxè¡ô¿ú[Nû˜o§HKqÓ�‹ÞŽ—þŠâÖv±«ÝÂç7»Kÿ@q€~´¸ìÇINÏï-ý©e¼?à³D¹E!¦S<00Ë¢‰[гžmñcMÝ|/_²¸¸B+ž[¾t™veñŠâ¢’òÅE•Ë—ýhIqÑ5ÅË®(-Z¸dEù¥×•/œ§ÍŸ¿°hÙÍ¥E‹Ê—,˜W¾|î¢R­Ä’âE‹ŠVœ§-)­›KèëÜâ’µ¥Ëˆå’¥K+–/)š¿¨xhß_¶tyɲåKK/š7oiie¥¶¬Ü,ÌQˆ¥¥ÅóŠŠ®,:ëÒiÓ´Ê%K.Y6_›·¨dQy¥s®…•3––/+-YV:Ï1MÕÒ…ËJëïëçšF©—.®,M¥±…˦]Z‰šJMÈŠV.[º¨t‰V±¬LÔhñòe¥+Ez­¨t^ñ²b­èŠ«¦_|ÑUEÓ/¿üû—][tíE_uY‘&ŠïÖš6Ÿë_T´°|®£Tü´%Ë ÅŒrJ^ºôZ2ÄYöy¥+§Ï§ª^,Ì S‘ETòmQqå²Ë–.-_JE•.]º¤üð»<…Z»MK‘êYX¼há-¥E Ò@°–º˜x]¹¢è{¥ VRõ/!é+K+a½’Š›52AyÕÅ7_S¼Ø™Ùü¥¥)~$]}ÿæÅsËi%T*µDâ~ñTàâ Ǭ„žKÍæ“ºÕ±ýJ—¬ÐPÜÂT ¯ê¶MªDÔpô‘]oL…¡ï—Á;Ì[Tš’w·%Sµ¢ò Âþü¥å‹{ÛÚaBø˜}©0;}3QkXHuIQ׫K—/½Y[ —ª~&#Q±´´¢¨xé‚Ê¢ù*á¼Ã˜ŠÛXª’.š1í’²Ò’+—/µú‘¬™¹tÙ>¯+^´œœ*Ù|å2éNSUbpñÍËJs›¥6º”¡O@&˜“¤óËN¼’>k¢–h^× XhE3¾7ý’Ë.ýÁ÷.+ºjÚ5ß¹èŠË¤J!ùé:ÓÑ;&4Û‰ù¥»“˜·¨òæÅŽY›žgù’E —8ã´’Ä/'‡xéÌk.ºzÚ% Ä)s¹Ù™ÉçÓ«{OŸû#êX´Ê… –/JMÝ@pã³:ÕÊŠâ¥%Äo Yˆ—"ëÉ×”W/¡v˜çÌ㼄u—›ÖMúžßݘRºÐJ³ãHñs¢[%gº¸<ES®ìî}“,_"ašŠ‹€ø•×L+š¾äªòbÄ"•Ú–,œ·°¬¤¬Û÷š½©Úàå’Ô&<{·ræ¥vÆTü%eÅK“›Î¤³¡ÍË/Ÿ†f‹ëê⊊^W’WëfUqx¿q˜®BØ£´²téŠÒDPTºd^²¼&%œNœðဲ.%CCù/Ÿ?¿ÿ¼Êò¢2DjºÂ¥;öè­a ã¨C,Z´pn‡ƒÉØ$u&Œ¶pþaZŸhJ)Äî©…•ÄûpÁ Éü0út–B’Ë•/K‚Z)…rKJŠ—•&ë€LD.¿¨Üt-©…4ùV•T.Ja�3©%päXÝ1CÑ„‡o°•Ýæa;ðKzS#U¢¯³ÈÚ ^gç˜àr)7+ !ê:JÊæ•¦ŒÂL-R_»´¸dYÑ ÑùšNæÔg 3,šWj׋¦T 7Ñ`¤2KÌ~ÌÖe‚‡uðÃÃj×”çêÒeeåÎ`^À^‹ª‰¦×­ÏÊUÞ\I3çf&š®Ðä´ÆùBÍRÀäÛüÂÓQsIDÆ)ËI'Èðåð¿Ü Múµ‡éÒÌ8å¬8¯hQñÍåË—U ´õJ ,T·ç äŒ'6~ÒÙ“Ç/¯\šô¹r~çѶ_n¿‘ó§aÞ÷uf2ý©V¦ûzŒéÙ ½€éºB7˜ž›LßÞÈô¼dúÎ|¦ç'Ñ]eL7z-Ó zÓg(ô8Ó zÓÉô…¹L/Sè²¾ =Âô• ÷åÝL¦ÿHò©UèÙL¯K¦/”zhTè</}H¡K=7)ôÓ›“é?ªcú.…gz$™~£Áô˜B2½U¡K¼µ+ôf¦Çº´K‡B—òt&ÓIþ:ë§{뎤³žÈTèyL÷)t¶×Ù íò€®ÐW2=W¡™ž§Ðw1]Áÿ¢Ó …Îíñÿ‹Y(ø_,åWð¿¸é…`º‚ÿÅܾPð¿Xʯà¿\ê!¨¤çvñ€‚ÿ%R~ÿK¤]ü/‘üC ýà þ—°{@Á¹Äƒ‚ÿòÓ# ½é þ+¤ü þ+$ÿ²^q….q¥à¿"ÈtÿÜŽPð_Áz~PK¦ßÄr>¨àÿ&ö{*ø¿‰åyPÁÿMeL×:Ëù ‚ÿ›"LWðÛåAÿK¹ý>h(tÖçƒ þ—²>Tð¿Tʯà)ÛñÁ€B—ò+ø_bº‚ÿJ)‚ÿJ)OP¡s»{PÁ%ãöAÿ•ÜÞTð_É~òÁP2}™Îtÿˤ< þ—Iý(ø_azD¡K½)ø_.q¥ày€é þ—Ërã =ÄtÿË%žü¯8Qð¿‚ù?¤)tÖçCJü³íòÿl—镸g×ë!]IÏ~ò!%þÙÁíô!%þyšíøP2þý;Ù^JúÓ ú]LŸ¡ÐÙ??”Œÿö Ó õüP™BטžŒÿÓ²¾ þWÄ™L¦W1NRð_Åþç!ÿUÒ. þ«¤}C ]êYÁ•´£‚ÿ*Y_ÿ+e}# Ý`z2þýOK> þoÕ™®àÿ– ÓãJzY®‚ÿ[e¹ þoe?ü‚ÿ[Y?r»²¤ßÆíeâÿoå¸qâÿoe}®Qüÿ­Lד鼟̿Fñÿ·°Ÿ\£øÿ[çkÿ‹Ît#‰îß!ù+þÿ–LWüÿ-¦+þÿöck ]êMñÿ·Èrÿ‹Ô›‚ÿ›#L*tÆáÿ7s?¸FÁÿÍRNÿ7³ßXRèÒ. þof|®QðÏsø×(ø¿…Û隈B—øQüÿJn§kü¯ä~m‚ÿ•<îXWèìOÖ(ø_gº‚ÿ›¥ü þoæq¥Tg7Ý`º‚ÿ›9N®Sð3ËY§àÿ¶c®ÐYÎ:ÿw°]êüW³üu þ«¹½Ô ÛK‚ÿjY/ÿÕÜ.êüW˜PèŒÿ:ÿw0êüß¡3]ÁÿRž B—zVð‡¬—‚ÿ;¤œÉø÷o—ô’žÇu þïvLÆ¿§´£‚ÿ;BL(tö‡u þo—öR𻬗‚ÿÛ¥â ]ÖKÁÿí²^ þo—õRð;·£µšB0]ÁÿíìgÖ*ø¿q»VÁÿmÓu…n0]Áÿm\¯µêüûÕµ þo 2ÝPèìo×*ø¿í²VÁÿm²¾ þoc»6 ÐÙ¯®U𻬯ÿl3]Á5û½µA…bº‚ÿj)§‚ÿjÉ_ñÿÕRÎP2=Èít­‚ÿ ûíµŠÿ2>×*ø²ÿ\Qèìç×*ø˜®à?dº‚ÿ ÔO\¡KÜ*øJû*øJ;*ø²~ºÑbúÓüß©3]Áÿ¬ŸuJüÿ4ãpžœ~·Óu þWq;]§øÿU’‚ÿU\ßu†BgµNÁÿ*ÖÃ:ÿ«¤üß%õP茟u þïÒ™®øÿn¿ëü×Hy‚ =Îtÿ5Ÿ¬Sð¿Šq»NÁÿ*/¬ )ti/ÿ«Øß®Sð¿*Àtÿ«8®^I¦ß)åTð_#åTð_#åTð_c0=®Ð¥< þk¸_^§à¿†ÛÑ:ÿ5Œ«õšB1]Á ãm½ŠÖÛzÅÿßÉr®×:˹^ÁÿÜ.Ö+ø¿“ÛÅzÿw²Xo(tÆÿzÿwF˜®àÿNî×Ö+ø¿“ûµõdúÝìßÖ+ãß­Œ‡õ þï2]ÁÿÝÜ.Ö:·‹õÉø÷oçv·^ÁÿÝÜ~×+ø¿G¦)tŽCÖ+ø¿KÊ£àÿ.©OÿwI9# =ÎtÿwI9üßÅíe½‚ÿ»¥üq…®3]ÁÿÝܮ׫ã_)¿‚ÿ» “ÎÍ%èŒçzÿw±Ýëüß`º‚ÿ»çõºBgœ×+ø¿‹Ûi½‚ÿ»ÿõÊüÏÓ’n(éCLWð·—zeþg{„éÊüÏÓ’O ™¾ƒõY¯Îÿ0þë•øgÇ õ+:ûÃú`2}'û¥zÿ;¤~ê”ôR~%þšq^Rä”ôdüûwJ}*ñÿY¯]Jz)DIÏã©zeþg‡ÁôV….åIÆ¿§¬o\‘ŸýU}‡Bç~¤^Áÿ‚ Óü/0åöoÐ:ヂÿ!¦+ø_ÀýÂÿ ¸½oГéeÜ®7(ø/3˜®à¿ŒëµAñÿeÜ7ÉzØ!åTæ?·KyTü³Ý7¨ø×˜PÒs¿°AÁÿ)ÿËz)øß.å *|دnPð¿“í¾AÁÿn/TüËô!…gº‚ÿ§ÏÔñ/û« þwH}F”z±Þ à'ûŸ þ·Kþ*þ¹mˆ+é¥~üïvIÆ¿ÿiIOÆ¿'óiДôŒÏ†L%=·ÓŸBgü7¨ñ麒žÛWC2þý;d¹ÊøwG„éŠÿßÎþ¡ÁPÒ³>’ñ/„ð7(øßÉþ­AÁÿ™> Ð¹½4¨øg¼5¨þŸíÞ à§Îô R/IWãÆgƒŠÿ8Ó“ñ¯¤>C îTüK;ªøgÿÓ à;÷ï EIWñ/åQý¿ä¯à·£†¸ÂGcº‚ÿ!¦+øßdz2þµPÀ¤oÔ>Óü?ÍxÛ¨àÇoüoçö»QWøËrüïd½mTð¿ãÒ þw²¿Ýh(rr|²QñÿOs{ߨú¶ûFÿÛ¹n (õ•tÿOëLWñ/é*þƒL&Ñ]÷F˜®ŒïåöµQ‰ÿï•úWâÿZÆÕÆB7˜®ÄÿµRÿJü_Ëíw£ÿׯ™I¦ßÇ~r£ÿß'õ¦Äÿ÷˜®àÿ™>žœþY_%þ¿W–«Ä?÷p;Ú¨Ä?÷p¹RmÝ|¸ÜF%þ¹‡ñߨÄ?÷D˜®Ä?÷0®u…gºÿÜË~µQ‰ưþüo“åÉôçÙî þŸ 2]ÿ®æ~¼QÁÿó:ÓÉô§¸]7*øßÆølTð¿MʯàÿÉ'¨¤çþº†Á¹Wòì,óÏÿ0ìòpOz#Ê)°¡C¯ÃløÀ^ÏÚ¤ÎçØÐŸZ:h^:ôz‡ =¢™g\©òàÿz›ôhG‹lÒ‡›mÒwÓlèqÍ<§M¥£=.±¡Ãž7ÙÐaŸ{lè!Í<×N¡oUý$ã|«‚9_ºUÁ‰‡n &Óe?¾U¸ýnUâÙmU׉¸ÙRè¦+q‚çnUâlUãŽÇ¶FzéJœ ÷KlUâŸlUü¤Ü¿±5®Ð#LWâÙ?nUãdî·*q²GÈ£ÇÔqÄ65N`²Í§Ð¹]oSãdöçÛôdºÜO²M‰ä<Ø65Nàøp›'s¿°ÍPè:ÓÕq"ûÏmjœÀýÈ65Nà~a[@¡3η©ó$ÓÕq"ã›Ã8q[P¡3¶©øg<lSñaºŠÆÃ¶Bg<„•~ê ÆXéGø¼ X™çäs|üa%~ÓãÉô<ÖC“ÒÿNàvÔ¤ôBLWæo'²h *ô-LWâŸs†0]‰Ox§¼¿I‰øù|ÿ“ʼ?÷î2Lçó#üO*qŸƒãR‰»ø¼ÿ“J<ÃçjùŸRâ >GCî»ï¶cXcºb/©Ï§½­f\=•ŒÛn»<¥Ìo¯f¼=tH¯Ôwu„ééCJzÆáSŠ~ºåWpµ:nÒ7AtªÉgÆú7 rbC¿ÛzÒqü¨8Ó@¥ëšy¦¬J74ó\V•ÐÌó0U:ä܆ù¯µ¡Cþmè¿çÙ¹~qüÆý6tÈ™ ògÚÐ!ÿ©6tȿՆù·ÙÐ!µ òÏìIÇѯâÌX•ùO¶¡Cþ‹mè ògÛÐ!¿Ï†ù7ÚÐ!UOº8¶ö>:äØÐ!ÿ¹6t¤ý‰ ò§ÛÐ!ÿñ6tÈ?Ȇù³zÒÅq»—ØÐ!ÿ:äcC‡üOÚÐ!ÿ-6tȶ òo²¡CþkzÒÁFœG¬Ò!ÿB:äÿ± òŸhC‡ü?²¡£àGmè?lC‡ü=éâhãí6tÈž òO¶¡Cþ6tÈ’ ò_iC‡ülèbO:†.âÌj•ùŸ°¡C~· ò±¡Cþ±6tÈ?ÆùÏ´¡C~WO:Dg«tÈ ò÷³¡Cþ2:ä/±¡Cþglè¼ òß`¥ÿøý—g0ù¨”¡]ìÇýÎ×ã™wvtÿ–n¥Ýùj<s§'r<ýfŒ…x¿SÝÕõÕδø 5CÙ±"®òA‚Ò C;‡¾×øz”è±BC‹‘÷ùŠÖèžOsEg£á2{EÓ¢Ëè¢ïH×çË©_ƒŽt5¾¹>ðu¿¢¥1¿LÃ{Í­DC™µ>ÝÕþc íηâ™.³^†õ3‰ä!Y¯$Y[w¦½_ÎŽ©ÖŸÓ¼ÚÈ$ù¼îˆ‘¶ipÀG|Óˆÿ‹»oˆôGy}… ÜN”1‡€÷;-cµ¿8DyÒ(WèÎ{Í.èGèÊF«ÞjÒ§~βÞÞû  ¸² ´tÔuýõ« ~ÛQ?•_tô¨…¾ê|Z6¾¯óiÈ–'iÏù´L¢eÖû´áîß}e:JS4à…ï mhà‘\ÖK†/flût3¥cyß"yÏ&{ÿä÷·…\Í>]sÚ°âï} ´Q¤o7ò“~À~DwSšãžÒÚ¨^H TÏ[Àiéî§´Ù$·tï¢ßn%Þ._™6u\޼2­¯¼ësJúB¢{-<4ú­|¬ºòP]êõydgÚ_çÕøJ²E~k@oÃV §-3Gå>öŠ¡µ›IŸ /óXM<t*3y‰G–Ð7c¥eæÈ’É9}}‡É§ŽÊ“21Ÿ•Ô†Úk|ÅÈ÷å² à×6ÛЋ8Y6² ÉÝŠ4}þK�жGí':³Ø‡´mT‡Ølagø ­ ÏîYà帎ä˜abcAžÄ†;ª- ™åjÊÖ÷ÎÌ©ma9Ûf’|¯S›©nÀòJ¶/ô@é(½†tHƒ´Ì7<)«æª U~@#{7äÕ\\Žù_èËá¼°¹‹l½Ÿò»ÍôÅ!§ôcNí@:èŽÓV†·›xÿ7åñ²,‘^¤‹Ògšéç¶§å7àkÑg@¦UõIi žàŸ%ì>7»Ö?RÇoh'À¾á½º9Dz§ò›¡Gøëµš1ß7­ºëÐ>j¿Ë¬6ÙBy¢V»D¸>-Ú9Í¢«]$ŸÏ¡îOnfLr]bNØ�ÏÆðëN#p[‹ò¥¿ ¹ Ó…¥|§4~`±íôu- Ø2}Ú|-Fµûµ;¯–ÇþWÄýÁŠ݃î÷VµUNÊ\—lñ§íLàÉR‚u^¤Ôù@ª:‡D[(ÑQw§:×Q}¹Þ™TïXz1õuYo²ó«T÷ÌÃažëdÅ|]Š:«Ô©ìשզNë,uzˆêä;lÛlÑüÉušÛèT§>-Úå¨ÓØ·´h„pB¾%âJ´9ÇöLù •2Û2ÉSÅm…1pÆÍþ©ï*:Î?V:~|,ùÀÂÒŸgÑñ9¤ã\K½ Žqùí6å³”?”ÊÏæº¯L¡§úÍÉýMÞ1”ÓCrÆ{Ê9åÓ„œ‡>&9uÕ7ÛØþ KÛ*KQŸ@²ÝçÖãútÚÔçW–úüœê“Çý KìQPÝõÍLêóg“L…Y"fÕ(úkêWqâ¼²Ý$cÛ,’“ø7ôˆ?ù]Füú‘ü3öW5Ý ù?Ý´1J ŒnzŒâ]­³ª©šÆßû¬ªiM4£é±oTG0TÒ£ÏÏ zá·k4ÃU£•½Y“YÝù訠«ÖÓôÒæ¬©î׌› ;ò—×ÐÆ ½5U7Rý£3…Ì^ªoÁ5mW4m„ÌE_ e6’nö5Ý[Kü=4vbxWåV¸[F®àš/}z×—«Î×µû(}”ÒÝ7&Ï]뢶Mu¡27’\¾(Õ²ÖæPEu²l¡4~Ð+e2½ëÈÅñš.b4—9>¢x8KéÓó[)­û~çEŽi¿Iù ´É"Ö(ÓFQÙ7Á£$D¶ËDì§¼Àñù¥LÓºLà.�zÜ,ãyú­ÆFø­Ö¯Ó¸ã;?£q‡ü‚!-“øé5† z£9„E!akz×zŽwvsÙ¢/§ü?Ù=ÓŒÀå1½t’¹/ðÂuÎ¥:ç%b˜ïT£þÄógÈoƒ­+ª»¾ÞIñv3tL70lSÐ90GØúîQb«BÁÖ|ßD˜ >aøŽ3´þ«1ÂA;aƒøôG̺ÉpCHó&áè‹Õ®€À%°\¢~„ËZ+.™ßâê/›Ì8ûHquÚ1ÂÕ))pu¢ ®ô^âªÞW™¸r;àÊHàêÊ¿§À|àå„«ìiïRÞ2Ÿë“±1bìçºcϺsS¼p¼‡ÏAúS ¥®që®'&ë4ö»²c-á$dŽÍ‡á7øhò£~÷kÚÀFŸéËM_;ßã®È†Ïn¬Ž1qç‘tÇëY¶ÐnÈ1•ß]`ÜÉ}@frý¿j§±A!úº£¬ÿ võGÝëHkM„׎Ís'ë�ód÷×´ãшî£qÊbQ_Ák´.yõ‰j}>£Â9Eëç “ÁÐ •³w²÷÷0:*°è¨ÈFG:ûèh ùž³Ñnàk #øøžÏƇßT…?BÛ$ßS&|ùwÄ~ØT ]lÎc\¹œðx/ùjè':KÔ¥õáA®‹Ÿbqs|Di¸Oƒ¿!leèIöó–vڞ軩}å‰þ{–¨{\öÝ(‡ûo_”ù`¾­•dÎÔwA×­GÕ_]éKáWf²_¹>ƒÒ^Hr¸P?ÆEÅ2™ˆG}à×(í%ȃ:ßG~þµÅ£yá·[ÎF?møNýt@ƒŽ6$ù×Òl©GÔ‹øûy¾èÔ>mÚ§Î>mZGOŸ6íãÞø´‘Ó»ª*ÿ9O’O›öG{Ÿ6íMŧI¬ÎHø´i/°OÛˆüEá¬~ðzYu×Á÷©MߌP=†‡xîËýÛàŒû‰Çû܀ě¶¾O¶¿œýÞpg¿7í)ø½-f›žì÷‚ØA7ÈÆï '¿7(ÊsW–6>œt["±ó­½÷ÓÊüŸÆºj¤¶Mþ/Pø¥¯îêzFÎçZô)Ú?tCu�Ïõ¯£LÌS¯D™ÖØze?%Ú äD;´Ê�üŒ¸èž›DYË(†Ž¦ŒMƒK*̹Å>˜§$þ^âß?Jí<Á[òUæõò৘_€ø­„¿j0ç…ëÁ‹çi1¡º Ÿ!æÒÑ®‰7ÚøÆ KêP0'|áÍ3/x„ ïTÆ(7¸-_1‡Òí³óäυ·tx%ò¿}ÝK߯Á\ioýO¢=t²ßêYž¡]…|-ì×bT/Jä9ÉäÃø ò` †: =Ö¾“hÅdÛÀʃ6ßàþ+áQ``,aA§vøY"þ°ó-íξ¥à=}KAÊé˾eµ¿Dø’9-šCöp2^z†ã¥³„N…û£NJ}}À¢¬3ê+í&ëL<~<3ᣠž²÷Q[„*ïÚŽ<VßJàøÒê®/£äƒ4žÒ,üNÁ߸­zLÝ s‰½Fø™Ù ÜÁ�‹À-0HýƒK¿†îF˜óœ(s+µÓH…›2Ñv„ºäZHwû¡öi¶Û(d›o3G¯1_Ùÿ¯”ó§b®r¦m›|âŠSúYæwš÷Òï2ínç×'æŒáç‘^®ãô…N陯–˜›LÉ×C|;ˆïõ‰9©”|=œnÆaø¢ßM|/æyŒÐaøÊùަ^èáEâ{Ib^¬W|ô‚ï6âû}æÛÚKýVôBuÄwë­®wò–8¦³ð½…ø^ÉsJŽzSøÖö‚ï<â{9óíì¥~c½Ðï5Ä÷RÖC¥/t˜+ Ì©¹=ºG¬O½jˆµâWI|óŸ½å”±\?ɯb·\ÇòzIü™oq¡Sú±-XkÓÇZä]™"mÿä´%MNuëspj¼Ù7µË²Ö:#2:ü1újCœK¾æ’ꮢÿ§ïã-ë¼#(¶-„¯últøôÏ2§Ã¢O?XÕnÎцÚ¿· yî§Øë—ä¿Sá©qi?¤Ø9­ÖSöÒªÜ OmÖÔNÌ<:*èÅH£§é%ü&æBè·Õô}“Oóâ÷ÕYÕ˜y¡ïTÌ•ôo© _CߟÊ ÙÇÚøhUx ú‚ǧ(nq`É[¹Í±Šæ#DçsߤqßT(×e_Ìzõf‚HÛn¦÷ÄfIŒ’ke~ËÜi¿ûÏÈóˆ˜ä´`Ö¬ÐWmrÃc+Œ4Š1î­0Ò~™pAæôé]{À;¸%„a÷êŒðË&!VÖ=TÇ ê7N ¾h˜á½â"Ôòn˜(ÖçC“è— þ ÛOù¥9³‹=Þ˜ [­š »CµѾÝ[‹Âè¯ÛŠšÂÄ+ÉÜ¢#Ï—sÏ/y?…ëãö&6’q(Õåܽ„#‡±Ù@–ÝÜï_¾Ý¾ß¿|èÈÍLzt‘íÝG6o)"\¸4/tCXób\¬Aßµ.ÃE¾ÃE}%¯Më™13¿—ì°ùÉ>çºcÿÐeú”j#˜ 8a„Æ 'Zâ;‰©|§ô#[´ÉœÖ\ï pe¦%Úµ˜GW15)˜FñÎ"ÂU‡™oî.K¾LÇ|ãD¾9Ø7ï÷xÄ#+‚"ÞÙäÒ†·¬0¼ÐS;a€ÒÎÜ[iúÖPFx*ÉW†¨Mà]’ Îlðå¼µšòWÔ‹yËǾ2wºÄ‰©‹¹¡º;ÃFw­)ÚãK{t›í±¸™d>‘t [dnÜx¹×Ü“¹í*hbw®cC1Âfœ° ,%ÀŤçŠr‹Âã ïeM<¸þ€»©÷â¸Eï>G½Ÿ&ôù"ÎqùeûãòË>î—_ö/ÄåÃ8.olŽùÛoˆô‹Ž"ýyºç1ÓÐæ©üÀ›ê_À1¶'2s@—_*|€›Òosý¸o”úkÚH><¡¿ËšYh ÍПԯüÐ7ÝýªÙVêzÙV8}qSжršLk꾤Ӣû<'Ý¿"tèqÒ§Î6«µäËu´Ù ‘o=¥o§:í¾XÓ(ö¾Öœï8M®kqg ÙýɲÏmµÈï(û!ÃRâÝj?)Ú„¯ÅœKž®ÃïhËàu?·uÔËÚ™íüPµó´D» ä¥(ãDYF˜|¥Ùÿ\vëãgtBg>Ы»>ÿDŒÏW`Î8èCÑJÍ›cøÆ~9åR–Iâk%õoƒ-øÊ–ø"ŒƒO´øÞ@ ¹Æc®`Ëô8ù{Èߨž>ÔµW.0K²A®–JMÈ8öÀ'A®–+Â#O”îõ¯ÄèÞ€õ9êOÜ4&ôh^êޝwÓ6Žíô<|zž‡é® c›=Dw5¸ã™ÝWÝõÅ%…bÛQ>| ʨÑ®èhø™Cnôÿëx^5V8 ç6/.Äz¨;Ç™XוõfÞ5æ¼Z&én&hkÇêÙ«&ëÙ«ëÚÈmÐý¤êc¯¢¾yÀjWõKOÒ÷ììMÿ‰ès½ÆýÐoÙý§m:•ô3ËH»ó­?püCˆáH‡ï1†€ŸàˆôˆzùÊ¿y3z“–&ña‰½:å1ð¹àw çç‘ìãÄü éx#•±—îäÏ<m¤'èvÕäÖ88'¾šôOú뻚úîZÑ?J>®Ö¥õ®0únrY°c;2œ0Öw“ÇÈ‚}î“'ä}ätŠ1ÉË´zwÈõ^…Ñ¿õ&=óÝ›¾6aƒoBÂî=eލ2ÛHnë<hÛ¿¿ü21®ñÀ/(mm»,#1ϾK–ìFÍyŒ_½[Xz Ö-\C.É´-"­!öX‘<™6|[møn}{ZzòÛðÝDiu´Ûèd ¥Í¶‘¡ÃF†»(m®oà¯Küª5ï]Ùæí›6¾ÍôXò÷nú£|¯°ÿýHï]—]¾CŸðÜìÔê®ÿäîL{¿‘÷@PÌ~é2ÌCQßZ‰¹H›yéóXú,Ç€31…ý‘ÊØÒ—4¶lѦcªðѳ{Œo}Ê8ôRJë}†É·)EÚóšå^ÄÙ=Æ–jÚq”vPB†¤1®šv¥hI›"-Æ·ƒ-2t¤H‹ñíDÚ¤q³Ï:¾uœú$¥õ[ççZŽÓû˜vúì¥ií$?Ø/[ŒY¼SMX\¿ñKøÀ"áOäq€ãj¬wbî‚îãR¬`ŠîY˜3¢{æx螆9/º{07ƒy”ž¸ïjVî»øqøÛAW+ßÛùç{‡’¾Ó²Gu¾“Æò_‹ñ¨eÞp¯Ü“Ëz³Y—EÞ¥”7BvÔd^¹vÅÞ∙×ð^’yK©oKþ¹”?Hùw™k?FÆ:¹ÇYìOÎéû“]Ê~žSÕ˜Ï â“ÏsªùÔ&xM¢sª—ø•9Õ¼ø=H|§ßÉÔnã˜ï>«0 ¯ËTÇë›[Ì5 ´)Œ|µþœFjƒ»S)ý4C»kOz#tÁãjI‹¶{Ž^»Yì‡×ÅoÑëEŸ‘k„èc¡â›O}cÿNÇ<BæžXÓWÐ÷\^ãòÆNÁZtÙ*Ö2.)‚Í}^sÌ!æ¶sñû»wCOÄw÷¬ˆ°[té)"æ¶ýX#sïŸúbŠDù£}²^Ø‹Œ4{ˆž À•Ùëëú¤ß¿·3í½ ÜÛe® ìLÞÛUR§îíRxy˜ïÿä&ãøÝ Çœïjó_VXæéV¦Ø{ù Ï+Š}טǦ±2ÏÛçiî õXæ±»çˆdŸ å †ÅZ¼QÓGïÂEü[£§5ËÞM'¹Î‘c4ËÞM'yò9­”'ˆ¹;yVÍËc™F¬Ê¯È#y¶·N’ò`¾ÚqïáyÉó©Å̓;Õ™bÒk£B§ë£+dswaþÂA¶|!Ûô®ÏH¶|Êwwë¸î|ÎûŽNýƒGÎA˜²Å1Vw’mbVÞŠýÚcuߪ¼�ÚÖ›Ô¥ÐAEµð«våÏëZ»ëŒyõÞò,éÄX×çE¯tˉ9õ”<-ú˜ÛŠ1¨Ï3h ª%pìøÌX[6¼}‰ïÍ>møsb<rÑ7:´ù ÉCéÓ$ÖàÄúf~e7ÖÉÝ9Ú†)qò/Oß|Úú|îú]ã2èSÝO•eöVwí•ü…—ê'(öÌ`Oò^Õ´‘üÁ@±g~qNòs+ðmEMk0®ÚSÔ´‘ÊÖжz5ÊÂóZX3ÇúhL3´MƒKB«ý%™¼Ï&s:”#å_Cy³Ö¥Ø+—xVid6žãiþãë‹fíæ=-æú|É ðUæOÇPÙx~¬?¥/ÚÍk×És¦Íäý'Aö÷(m\<cvÑ÷¹ÿ±™{ºèªžsOMïåºWQwê/~Ÿe³ß <û<=õëôTF6lû.gÀV°æÁÉVØ«aÆ w¡Ÿ/Äþ�³ÿ²·ÙEÏ7š{y¤ÍÒ†ñ^جqð¨tgo3àëhlfüëÈlfì··™ñ‰b³~q3ý‡Î63öõ´™ñÞ·´YÞêôê=d« ª»þçSjO/À>Ti?÷¡ö>œbè x¡Ðnêpâƒýs‚Ôž/JǾS‘ŒeèW±‡û60L¶ëÛÑô F2»v_Oñ‰ýUz¼íú XçÞ89¾rŽžcŠÏZÁ¼À“t]5ûðS[® eHüÁ§SÙ+­{eä^‡ì!y)ß¹ ¹·‚bÐQq¥Œ.£_Ëu¡tË:HˆäYiMKu;i[Ž ¹£Ø“Cuë®ÓyâÙ³óåܹɣ¸“0VÑ 9ÏTò5QÙ1k¾?>­{ñ6„†åþijø#ÁÃÜorx¬N [ ÎÏÏ7Ò$b·Qíà·'ÝÐìda»ôáx1FõL§ûo[® âûk-׈ûýQ*CÆi‰qÑ7ðýÿ›÷Cðý ‡ù’ï*°Ï×õ�ç+;Fr„Ž2ç±)ÿÿ÷{ÇþçWwuŠý?äóÉo _Oñd=Åë'Wä7PNôÉ–=tn³±¶›FÞC× žs6Ç*vûè ïÔOû΋“DZDÿ» L¦¯Nù%–ñaŸÌ&Ÿ:!»@ëƒrȧžÏ>u¥Oõ˜ûkF#®åϧ`‰?çðÚ>ŸÄë~ì¿yØk×ÓŸ”ÔÉ6Œº’L_bÌÌ<úcM—?÷Áú#ÎÀ: öb~Á\7ž«ãYu›ye|ŸÍwÆ{× ¾w(ßå]¶“V‡ßôþš}—]Þgæ÷ÿ+÷ Ä_­˜[4‚÷ñ‘Ÿ¬"<N®îúô?ÔfÞk†æ­ÍÒ\}^1÷t–W ÿ†Ý­6þñû/5cçÿå Jýöˆ¾ò}†=›UáªHUxßçUárº¿M×[Ø—L÷×é·t9:'^86bü«-=î…Ë£.= üs=ü¨ççyôy(}®£»×m¸~ÔNiö=4ŠñxdéAÞÿSÈJqν­Dß}½nìó&S¾½E¤ È´»ö@¼bD_ORÜcÄë{©Žò~e®†Ë[<TçÇCó 4ݬŸw’é Jšì|Õñ#ªOU[Qx_t¦æ£:½…˜’ò¾]ïÕú4zµ~ÔžÒàÕúöiqýyÕ¤xÒ:Ö(TQnt–^èŽy"vöè^è%ÓÐÎøöU½”&6+P(ž]si.’yÉ|r‹)ó¹ì¿ ;™ï£ü”Çäy¢3~xÀý;¬cR¹$7}ï7òwZÿŸ¥Ç]T¿·Q?úmßÞ™Z¿(êGyÚ( Æ’ b #\Ž´QW€ä4FdOïõ¤´¯»cG8}¦ÛÎ3⹃ZWî›Øo$ö!yš^Â~¢(áØÎF#/‰™xK±‡ÿÂCÎëù~Ñ3>¿ð`+%ŸÓ¸@Äçb.sùe¹ÏvÐ]7Z±L²LG]»ßÃkú6¸¾DÅ5å»ÊlÇSÔõÿ«-ëÿÕ=×ÿ/Äú¿¡¬ÿ¿'¬P<7y1^ÂZ(ðQãnzsU^žkôÊçÛBž¦g ëÐ)Ú�Ê»dÝGr\,eDþ6²x„ÜA_È«Ÿê'lã«áü°ò“¿�&Þ¦ü]b_a‘0ºj‚.ô…}?xž­F œd}ÆåSÙoe^qèï„òG#¿®:½ÂÝ ?!ö?e@×{%¦</öWWXW¤qHa§‹_ïC¸@í^§|C sÿÙ+±–اÕi‡ýä}N.µßçtá"só¡oP·Ý„cnã/Óo?0ñìq6ö y(½ûËê6È@òþ2­âv}ˆ¶4Ó�ïÅ­3.Q·sP7N»ïaJKéú¹¿œzÒ.ð|¹›Ú^÷¾õ|ͬb±›äìg>·®B¾'ƒ/ÝO#_âƒ\qÂèç³¾÷fV~0+PF²ì®C<fÆe>—‰}í¶_bœ=ª±Yâù€p•اmy>€÷hû7ù´Aæzz¸*z\¼á~€ûŠCo˜óá{æDÄoTÆÓXG ÿVø]ºð¿íž(k#±Gìg¿ðÆØJûËÖJM‹›m­*Ê¿ÃßÒoÏ£]ã7Ó™iˆêá4O! °'ÊÁþ»Bñ{ê3Ëx¼õ&“Ï^+Ò×nqŽË…¥ â¬CeyR–z±¿ýÂþ\Öõwúí4þ­¿ÑwÑþÌ=. ýv%tþ8Úaçñ3ò<"ÿLø ^ó.&æðŒÛ¹þæ~J{óÔï5#ÍÞB#­l¿>’¶{Æ(gÿ³œ®\È�Ì¡,”#ùryZô&Ã̲/¬BÙ²¿cyöJy€yäí?ò;Î#Üúð܇‰… bÄÇÇ|Ü­†o•¬?µY^u׿~€òÜû§Œâ¶·/R!x¾¹IóYÛÚéòSèGÊe}Ze‚Í ¥©{>ˆWtæi‚F˜«ú€ú;¬¯·}–F ÿ{3 ú>Ô¡u™°çŸZ—}½)g¯Ù—¾M>I£r| ™eˆ²Qÿ*§µ7¼¢’—˜‹;-“õkÊlúðk#~±£ÐA¶ˆ_„øµE]u›ºî ^ñ£¨k®C]&~GQ×<‡ºÞMü:Úâ¯,Ø? eÀƒÀòÇfé™{ ‡í35øåöÌÂÚŸ˜çw1îŸk4}Éå{9¶AYäkg <j».nGUÔ¿øöò3_äû}hËÒï÷BŽ÷H†ê®ÿéÃ~$Ø´kGT&=\•Ðb3™»}ŽžÙ2KsíEû¤²ö’¼Ôô^~èýOÙ›¨G>ý&Ùv½=8èëÞ„¾íF:”!… Ø?$u·w–žŸix)ߘÖY† û7h§¡¯jmưȿ‘òìå:´Ì2뛽k¤w£/°ÖNº¢:ôNãgøþO®S=úF;ˆüV@œ£- T¯¾í…F_²[ß=įºëà™¦Ž¿iGÙ²ïd›¦™ýCן!cÏqøg˜÷ƒò^È÷z¾ó<ÜWüûW+ùÎû ¾fú×·ñ绾‘ûxÊ’Ë‘÷ov)ß™ß!ï<?pHÎðüÙg¹É÷/ ¾/J¦ÿ»ýÛÝÕrœî²üý,ÿí|çy¼󸮤;V÷'$ÿ‚÷MÐ’¿wßÏÆ<þ{sâÁ¡šáŠÎ6°ö2©ºëcqþ߆‰Í˜ßòË3ü°ÿc‘-©]âÙ„­Ø·0[ì;ðË=rŸWŸ¨v™º{%0®H7´+{×þÌy?Ĉ>-b>NìcÂïX@~Ä}ñ™9HÓfÆzÙXzì5<“§ ·Û3ÁòŸÃã¿ãbTËzc;ž§$9òä3•†wòìgh›£û¢ƒBˆ16ú¼ëðLòõXÃ8è:³}ýMÈŸØ“á§2†[Ö$d9±žÏmN~‘ÊLéؤoµIßLéÇQ™o¢NÖõÞ{Þ»3í¯C0'ÈkºÃ1׉õ3øñê®§o"»)Ïj[óWíL‹·9çßÿ¹M~k@ý§Sââj²í k>Ã<ÅþªSpµ?ï1Òwù4×~ªÛgb ròHÌ'ï}–ç­2}ò˜Ïtéé•“ët1åG¦;äEäϰÍ埸3íýæ6ò?+trEøc‡ü#)óeùOaäDù Ì%9ä_‹üÊÞ©<Ø$–Ñô<ÖÍ£›^6©jz†t"éüÍG:4ob~zòÖw˜¸ýñû¿2Ìrÿñù†Øf.“ŸÛÏÀyïFZœŸà~M;¹NìCÎF_Ç\Á O|нÍæï^sþà¼VnƒƒÍõûɯÉu5óÙÿ@ç`2›ûž‡ó3†{ÄXçi:p–(Ñ7ŠçKÊ»>õíž{ï–ùu’yHˆÊ'þX§޳ÚÌy’ÉM)Ê»¹.qh¶<”è·¢¼dÝþÊ€ÿáòž¦ö¶Åœ/ÿ׃æÙ˜:ðŸ)Êþmg’Œ ²F^ÖKz³Ø#qÞ÷B,_0…|ßµèÃgÑÇ÷YÿTíï1××!c É(æ²Äº7¥Çžœ"÷,`>BîÀz;|ÞÑáÝÚ GSL×½_à¼+œ×žÏ3zÎmwIoÖžÚ ,Í<?«Úbä=W þ™o´5­¡1òF¹?þÈ×ãÏëÓ‹õø¾‰õøóدǟ'ÎÿQô,|ÚÚWu×'ûî0÷Zlp6Ù ‡ÚðmÇÆù)ÖÿómÖÿó{µþŸÂßÞùO™ òwØÛ ?lc‹žÿ.Úÿ1Òó)ô|·žk¿¥žËŽž§¡ž¿ï ç«£çÅÔ§n?Fzž˜BϧÛèyÜ·Ôsá·×ó¹ÿ92=Ÿû¥½žÏýw =ŸEzL~ãÒc£çsÛõ|®Íùç¶}K=Žž9B=?æ ç‡Së¹ãÔG¾sŒô|G =WÙèù–o©çÇ@Ï¡ž/sÐóT=û,z."¿± üqV™ØOä›ÚuxwŸ<?ñóñáw©¾ç~†uaŠ‹Ó#†Ø·…¹Ö`¨ºËІáL3§zªë¯‰3ÂÎÍHqFØåÈGñØdÔ}Õ=û¹îÏdCñìÉŒ9¥ôÿ™ú¾UžA ºöñ÷ö…_}Çp×ÒMïÚ/×g(ï>ŠÓþ%׎×'ýËyýpÒG=ñ3é½Üß·SÔ¯¼ëWÉçNÚm¿þ4é Ði\²Ï¦ÍL¬îúèêk›ŽM›™ô¬s›™ô”Mw|Ë6üömfÒ²#k3“VÚ·™IKWþ#nWÂ÷“ß:åÙ`F L³±ÁUßÒÍÇÀƒÐÇ;ØÀŸ¢˜@zž@ýð‹ÇFÏç|î¬çs>í©çsöK=7}{=Ÿó‹#Óó9¿¶×ó9?O­çß&Ÿrí1Òó£)ô¼ÁFÏßRÏÇ@ÏEG¨ç=ßp=ßNýð'ÇHϧÐóù6zžò-õ:zN;B=g9èÙ“BÏy¤çQä7î:6z>»ÃYÏgÿµ§žÏþà[ê¹îÛëùì§LÏg?c¯ç³ŸL­ç¿ýŽüÆ ½Õ3ïï;)2>ü/ª÷”Ë1¡­÷õ«ìEýrQ?ìÃE-ö\–ž‹mìYÑ˳^ßD°æŽ´”ïz^ F†ÿõln ûÙ)z6t‹ópÝÊçÅaÈ+Ÿ{RõkÑíiÐu”äh¡+–IñýøðǦž.Öó'ˆÿû# òÑon¢ÿ3šðÞûDÉ)t?ä[è~h ݰѽ¿—1öo¢Iº?ë+{ÝŸõ[þ§ žÇ“·’߸JŽ“z©óq6:ÿHÑy‡ƒÎÿ“¾ÿAß½°}O£ïŸÐ÷t¶IÆámrÖ3Go“³šmrÖ“=mrÖö^Úä׊MlRÇ㤵-ùŒ2Ö@b… rù¹–}ÿÂnYݶû`Åäu5¾À<s(÷ÕÛy ¼üÝý‡a†Ö0èýPãàQ†˜çvÖÿKìxáQر/Ùªå’ÂŽçÚØqòQÚñ;ž;Ú¬J#]Hí‹âŸbÍÑF.ÅFäû,6úÿÀ¿M|çèm0ñ/Î6˜¸§§ &þùèüÛÄWím0ñ×Â=ÖDYÿïÿ…úëEÿûú”‰ÕGï¿&Sèüfßv”:Ÿï ó€ð_=ס¥ÎƒÔø¿×§Lþ-l2"…M†ØØäø£óE]ö6™ð•MŸ"mr&Ùdù¡yÂ&ç†ÿ™Ê&rn”l’û´tÇ­âl8qæ‚Щ°S@ØCì÷'ýÆRêwBôèõ;áUgýNx±§~'¼ÜKý¶´$éw“ú}Bè÷‡Iq”˜;£ßÞÀ=uþž8ÿçHÇd =Zê~WŠºßnS÷`/Çc>1£¸Ïc¯£gãyôõ“ÙÄã ºÅ3é¦Ù8lÂÕú¾"Å8 º]@>¦þHÇaŸc-�{·Ó-cx,Öûúù¿†¥°c_;è%†ß”{ŸM ç°×iÞþ:=ƒtÊûð¾©#ì+’¯%îH¾wŒî¢ïÿD»‚<Ñ1÷Þ1âø/ÆHKé<Ýgû§ÔyÞ G¯ó¼Ÿ:ë<ogOç=Ûë¾’êlÑù#:ßZçý)ùˆ7„Î{ßWNdw@-ñÎÿ¾Wêüô€»åtSç{Mÿ£[ç°|wnÀ+íFô4¢ÿ“éé‚Fv"z†|.ì06úη°ÑU)lt™ zi£_@GMt°Ñvë"]E¾&uzsªù¶ÞÛ¨©Ñ´QÓFÓFM;Û¨iSÂFM$lÔôhÂFM?N¶QÓOzo£ñï½Æ¿ïl£ñïô´Ñø}½·QScÂFãgo£ñ¯¦°ÑéÕ]qñþÔé­Ñ½¶ÑiVíµØÈÙwYlÔ+ß5þîo¡ó{Rè¼ÚFç5½÷]I:_ä óù‡Ñù=ä»|5–÷þ/Ñyî·Ðù˜:i£óSRçtž•BçcIçc¨~Ôì/þ/û¢q/½ÆEœm4î=m4îÅ£óEã¶ÙÛh\(µöE¨]ˆñïççöÚFºxÏlC2À.}M»¸ð>¤ÙgJá,©^è¶ì[èva ÝεÑmi/× ~ºYt;ÝA·Óìâ%ß9Ýú-£þ¸Ñ<ϱT—ÏràÝ‹8_¯utø}ªßDñNÔ+Âè·FàÜ�ñî-úÆÂ·ƒ¯åÝ^7@W=ÎI(0ßµÛF<ZÅúžsWŒ¼Nã"Ðí÷ó8í"Ûç }›üsÀå@~â×,Þ÷wöápI›yÖ±ØççÌÏ÷÷œi³þf¯Öÿ©¼žTþëYšú.3ÿl¿ÇçÌ?ò;´^WÛ ýöÎÚîgÄ÷øZË3›Í÷k©îz÷ÔLJ‚ÛB.ÒõIÍ>=Ÿ½†–Mw7ц5ãìãm!Ì¿‡ß¡oìѯñgnœb¾£{ÝÍçÎlÀ»iLY±÷®fîÅ?³Ñ|·„–ˆ΄üÔ“$÷Khï–ë5’ ûè1žÖ̳«r¨_ÔÅù‚Ñ1æùU-â™§î÷^ÃflánѲÄsÚæ{o2c… |ÝïŒjýñ›œSÆ~|â-žÏH¼îÌE&f<E¸ŸjâÏ÷ˆ³KWyg–â\wœ§Š<iü�)GlÔ§EËÀóÃÖ²È^Ryb¼2Êä™oò,6öÎÙ }îž­i{zòÌO•ŸûàÔÝį€ë:ÌZWn;šy.RÎ èyQÊ÷ò'x#x«xÖžÒâ¬Ø½3Kj9äh¦üxw©xŽg2£^RÚÕþâ•Ñ™£jQ'¡çÙš|ßÙ`“çÜHÌ´[~2ßâZâY™¬ò€‡x~z¶ôs;dºgþžž°_“ÐõÐâÐ^Sa;…ÎqÒuñkÏìÜÕÂ|v‹3ˆµÁ ;Rà`ñ ZxEZfêâ}Öâ Ï^`:CÅ4ñ¿ gÈYtÙJßÛ{Ú·8S±o«CÝó)ÜÂí¦óð¶)Ž8ð;…òHÐp–ÍÈÌd~Å™=ù•øøù(¿/¿¹zs³£ÔVÛü5tã¯$hÏsÊWÄ37a£¹y-&œ_-ž…‚­,²íràówô] 1e3LŽ,m~OOâ=ùüþDüfXä*<¼ÿ(nä66\òã¼e6¸C› öwýzâîÐNœy¨Ðž$ZÀRæJ{¬—äÖ­%þ–¶4·Ö†?ÚuþQò¿™ø×Yø7:ð/<JþÅÄ?di M8ƒã0¾XœÍ뀫)ÿ. ¿}¦­"¯ÞΣü­~íô=~~ÐG¦¿S)¿¥ï™‹s2Xù‡Mß“ëÀÏO±•Ó%™ª}F#ÿ÷V}¾ù’ø[ðUb‡/´±£´ÿ7%þuøŽ<ô¹}”ˆ1 ‹Å™í=cŒ¹q{]\ø_”?d‘µÉ¡­e¥¬?#þÍYwm"߃¼-…sá¼$oKY.ù.=YŸ ü‹¬1_Ôq„²ºXÖûñ<6dàó°ñMæ~âõ3ò½-ä/‘ï£Dy$÷Æ™xÇáê-È Ü€§‡êq”¿‰bÀv¶8“u~U³úµmº¸Îó‰G‡E¦N,m¯!þèW]& û$r2{Ñï:È;±wBÞâlU^ʸåð¼x-õÁ£»RºTÞj nÓà@ã0¢O—{“Î롆Añ&¼|(Ñ›zî·÷†HßúA¡æuƒ»üD;™®ö"}Hö¬ºAzä'ƒã±Qæ3¶™$ËÀÐ=wó@ÅêžÑƒÝñ½{CÄ¿eÈæìmCÞÓ©ßõPè¡ñÜ`Òë ¦¡9­O -®;“hÜBqÂÐhUxM¬*¼‘Ʊomúx¬¥*üØÞªp¸½*üÌžªðÏÛªÂ/ï® ¿þnUøí÷ªÂÇP>ùêðh*븃UáêUá{÷W…÷Å«Âx‡ŠGŒ«Âã ¿4ö‰¸D¼g˜âáŽMƒ‹Åyé$#Þç!x:ÉÙÕ0诤éÓÐÇãíëÇIÎ+HÎéRcLý ›Qyç’|SIö’ìE$û’½¼i¨n<EãÞÍ>]Û’±íÓmáO…}?1JöÊß3»«nO×IŸC ïXñnŠÍÞ€ ¸£ö1(V8/HeaïÝp‰?~†Üo>>¶z÷ì.}›7Žw&ó»ÞçµbügÆIæ;؃[ýr§w}ˆòp©«;6ÏEò•ý8°•>ï#;ã}§Vwý÷”iïwÖ ™ _)Æz$»76:ü2ÞÒ'f ÞfžûíÂ3íx±á=ûÿ\ܦB¢]Gµï1Α¦ð1ž>ýd’oý6GŒí© 1B¿a_­8“"#ü2ÅòýÖñóܲàl9~4¼cþHu[‰çÉq†¡|¯=ñE¯×»ßË\­»•i#p.bt¶ž-ß96‹´$xÄø.|æ‡æü|nÜýZdÚåÑåùô¹ûý"ÍŸ‚§å}#×¢<ùdž.ùÌ<þ€3µ%ÝœoÑ+’ÇÅc¶/Þ[�^dsL¿a>@œ‹ÙóÙ÷1ë¸,Í,GØØÇzZ¹â5Žy=còú•/œ-ïÛœñ2~Gü=ï–käw["¥YQgεu—‡s>êyþclœGg±×MÂ^å]Ï@—JºvKºœ® z¶Î·ÄÌy4ÂçÞ†i9:O`^œc¢ ¯5ßé‚ùJ–k®¶Ú_ÒÙçwâ</óŸŽ>˜±™ïêiñÏä\×g9¶s¥H·œÒ½‘4'ÒÒÄûs çƒÆŠùg'÷(ËÃ<æZy~êgcz”%Óñùÿf:¤Áaãfcy~JHûl$Îyç3Ëù VY¯¤ü:Éz`¿©ƒk6tèÖtxÒ)”Πv€ÊÑ8Ý¥–tyÐ?¥5p>§?›ÒǶø^ôU|FŽ«Éóq¶‰óqââ|œ÷ðŒ_÷ûlzœ3¿³¯ðá Ø |5ðß24Gø#¼‹ûd¤Ï6y^Ùƒ~ã0ë{2¨. ·ˆ³nâÚHC¾³&m׳„¬ï Ê&Úó‚6ÛPã�?}ÿ&Ë„|T®®ôÑ~Ѧý£t–qLOxnÇX™û+Ÿa“©žaƒók o.Þ5ùê {Y>N–en®,îN€eI³ÈòSŒ±øPøö|G5Ùð}ŒÒô’ï ¾ž¿n'ï}{÷’oÀ¯n÷ãò^ò­°á«‘|6|oÀ<Pïø·Ûc('dÃ÷ œ;ÔK¾x¨°á;ïPè%ßxˆØðÅü¿~UýªcIæ;²Ý†o¥÷õ’¯GK»ÙàìæÛ+z©unÅÏ㽕6|ÿLéëÇ—ýÅõ9µÿ Ÿ`¾;ϼ õ¯C©?>NæÅoõæoµb®é<Îh㚃Sãb\c_öŸ•:©ó-RW…6uÚ‚yÞTu:J١ÖýòïSdovh÷6²/Ç|éÿ ²c<:ã(dWÇãû¡ƒRö*CÊ~9¥×e§t½•ÝìW5mw‘!ç÷›ŽBv»~˜É?Ð-{·Þ§ôÎýNUïõž½{mâÀQÈžï {ëçRö–ý›Jo8ÊNéŽXö9“•T…ìö˜Ñë>ë–=(eÿ¥Ÿá,{ð(dʹøº£½Ð¡ojÚß-»Äû7«)}ÀYö£Àû‰÷’Ú£]ÇgKý§”½¢[öY˜ß·ã›%ö?ä`=ÍKeüãä§‹Î<%Qî1gr=¬eŠ:v·áØø}¹†¨£ì¸XÃ)ÎÅìËk%~^Cô±[3ÇùbË}ð‘X�/ÖO …×ko»s~è¼]ÉOã˹u–þº±;‘f—?®äï²äoJä7lò+ò_°{P7¤­õ ¶ÅÜó€ñù.ð•kÐæyÒR×Å­ ¯}XÈ2ÇŸ~ô›ÌKôíX€=öZæ3Áï©„nË~¿'~­–ºµ'tk[·˜’ÿWbþz¶‰GäÅ/°·GÌéät"/0(yÉ=ˆw^MX?@=úògµî½”FòǙɘÓÅøX¸¥2y ÐÇúTõ¿ë«i\ñK3ã _­TvËl#±V3‚Å|ª5Ι;ƒù¹™ßzxoD­¯¤ Ÿ;(?Ækõ1.4ëIõ?ïJ=(柺e=‘y—)¼`ßyü—/í>Ý:¼A啨ùˆùjï©B¬ åYíXÖNýàÁ²Ǹ(¼.ÅœšE–D»»AÌ%MöO%yŠÞÏÆ:�™©üœ�Ö¤;Xÿ˜“±êž×ˆýÿ7t¯9+mù‚“Ñ>x-éë¸ÈÓ Þœä3ƒJþ”ó˜Ó«ý%VŒ¹MŒRa¬û¹ˆ„®Îÿkdhc°-µq]ÊÀ¸ó ÿ`9%?èåÜ•,çùCœo©gõ<ÈùÁ›òžˆº6$ü–Ò6ÏoCÿ“eÎÁ‡q$ò_‡˜÷  ¼b•£¤@áñ*úßèlÓ&±BÓFfÙ¦¾w%Ê? ä}{L<æÜ$µ“âÊéèö{%!Ȳ‡e±àú¨Sx=Žõ¯Tr° Š;=Æ)‰¶Z\¡È°ËY†¹ ¯ ¥Xe@ù üŽj‹¿Pu¸ë ñ„ yA¶-e‚âËÎ/"Z¾a¶­N`|*mñüïb™ýÑÐZòóuæ~7)Cƒ ˜ûRë3s’‡‘A¼£QÉ—‡µ)ÈgÊPPd8ë¡Xµë‰°µU¶Å Š-j•|hÿ—¸I†BŠG„/Ý+ÚiÉ.´ÿÓ'¥%ÎÚžÛÉÏœá~4/¹ýOþûè”>~÷ñßgöñ#;{öñsw)¼>ÄÚœ‡ûx’1_á¥;ð‚\A…×1~J`onHá•ç,Wq\áÁxÆ{´2/Çšfì1¼¼ ¯f…׌rê,r!Æ3RÈU«ðÚ„w!e™Ø‚<r黜å*éTxÝK¼"^ ¯˜3¯¹­ ¯eÄ ±•Ü÷ÚÜ×äÑŽ3¸÷r æ`fï§ÉXÜÕrC¼Ö`ækS²¿Ä~Š=2¦zUÄßXgÑDyèóqæûgF-Æïb ÏŒÑ}x?êÕ§GµˆÝ«»þü2¾·.Ç{mN)Â~[÷þÉ×Ô'½Ã¾¸ÑW~ðR¬‘ùÊ»°Ö²–ðëNÈ:^óž\ÝÕvÅδø’™|mŽa®‰Œº¾.cÛ3˜›ÇÜ~¬0`™ËÇY÷ÛÂÖ³îGÚh¤ e„?Å^ÓÍ>}ô滪»úÞGEõ™ï*ûrêW$¿·µh[Ø<s~t¶\‹Qä‰-N˜ÞõòQÝò–q$æ?ñ SÄ68Å{™ˆvŸ[¦¹‡T{Fk ÛÂrmÆ;oCV«®°×óüøQ:ι'^ÙæZÙ¨OxÝÔ1÷!Ë~Ï ¹­òPïBÃ;rŸXo4eKwñž ëhêùØ++,C^¹î ú61v+-¬K¬íŽÀ;¼†6e‹{jת Bâ=&ñÙˆ‘ôŠ‘¿ÓNØ=[_Ùâսأ] enk Úq 7ü¹†6Ÿ‘~ÕÙQx`:6Ss‘ݾ€Ý̵å¢\Èjò4üùÓ»þ¼à16j/Þ9[/£Ïñ™tð“ÖÙzÝ¥û ºo¢{!ݱ÷ÝWSÒ†LïÚ½Åû2Ö%û[Ëk0å]BoÉ´¿£Œ„®æ#†Ë6÷‚jâýv‰µ¥ùò= ÓuýRü`US˜ÚÁIÕ]¼{gÚ_ûm¡ôØo¿¿¨é™ƒEM?5u¹_72·'ñ+-d~'n7Ûðˆê®ö½ôÛ §ð~†ªðcëÄ„í¾o¤ºþ€Ú`m[×›±~ÿº‘…}ÑÙAð[¯ã>6 i~kd5¡Üä4ÃdøXQSØýŠ–µ99]ûn¢Ëtb={`Óçá CZtœ$Y&®=£ÓóBZÈݺB/X;Eàô̵cóðþÑf ?Yí{æ]±¥Ø£ÏˆU2ݯi‘z¥Ï!îÕØ´B/„‰f4½=M¸_Ó2(Mä1ýX÷\à œ…OüW·hÇgh¹°ÃÏ/ rˆÿ/Æuzî×éŽÞ P›Ax»I‡ÿpÑoьܟUÿ;z“vBÌk¤‘<…1ÝHk¡Ïxïã é]ÿOj·™ô[…;bÜZ‡µx’¡ÞlKe¤“/¤,›udòGúô®wZ+¨¼ß°í‰H |!ªã÷¤_îßÛ³ü>$é÷× Ä9~ü{PšlëïÑ¢¦}¢Ü:õŸAøC±?Ò¾M:*Û0)?äÙSÔ„÷Ø­!÷k'=Áß„Ìuú òo8ýÇÈþðÝ}Z´ó‚[tmÃ9ÝyߪûDr¾¡¼–¼µ‰¼9‡8ïXä]—¿?“MÅ;vSÙcáÝ!枀‘{Û‹ÂáÐ:ñìý(}Ž…‹U…ŸÁ™­t½Lß7FMÚÏéû[t;Ïù»¨Žkê3Âo±.\–w€Xž;Ñá™}RN©xß9Éç.вO7ýòÈ—Ä÷ò®»Io”¶¦uüMNîägrî¨ã÷ Ðç[I—Ñýfºo´ú]j—ß#¬¼@m—~ecâüP¼+áÄê®Ý·Q_ÙA¿áù!½¦!$ÞçðÓyŒ#Oh$¬¬Ÿ„¶”³y丨ˆ‰:Ƨ¡ºîý 9O@Žgoñú¬e3±þOi®$þÍx7ö׬sëÁfÕùíŒU1®…žÌgjدFúB,åKìÉÉ6×µaf«uc éHWµë6FÎa[»Ä>‰’ísÌ=¡ëĹ#çPZŸRñ¯@YÏÞ2¤GÿeúIÓ_m6ùÌnB¾á–} :Õ|åîSw¦½ÿ�0 ŸEr o4í5½Žpâ Ë¨ßÈÍÝyyÈk•ºÛ='P ýÕ™>HÈBr #9&·Î úºî(sÿÞy£éðõ“ýŸå럆Èßéÿ¤{€® Š5Ž3q§wÐ÷Fºˆú¬š:ï¢؆Æ&rNØ÷\zÀM׉·1ŒúËÏ÷š<ÛÁ}ókãûnæÛ*õtç«ñÌžÈñè³øù1ÒYÞET\ ßB¡¾‡®‰X-îu'f¿jö]ø½‰û`ÊÚñø…í‰3ò<ÍfŸFñWN;pQ“ð?7:¼80ý]Âo2ކ›ô@¾…^ßP'žáÒ7Ÿ ­°õŠðzú¾ý }Ð燨óýkÏÈC¿T9M_Ißï•kið‰µÔ/·‡Rö3ñ<×8=D~;«04êŸ|Ô§4 Ÿ:ŸÒÃ~¿<¾¸•ð?ûÓ ñûo<;Ü<µËmhÓjÅž9ÓÏÄráø ßãij­À Ìæd„Ò úꆵ.Ó³ÑO´kÆàÚºõ}ÅÂÞMˆ…CZZ îë ùÅ+ƒOèƒQó$;Ãøý§xÆ+�¹W§S|=3=èOô§Qצ4ݵÍ÷·jC©NÅ„ŸLè÷§ê°aìYŸØ8h³ùþª_Îý\^âB÷ô®W[uc0÷w~«Ý/.J^~ÔtS:tCýé¯xÿ”§6#ü Îfãç/ð^/ÞkkxO>€}›æX¶Ïuâ¼ïÇLŸ’“%ú6òµÔ§ÍeÊwò¿ÛÈï’MÞ¯ïÀ6ræ›±¬ƒ¾qÛè—ø ›ˆ}d#<#lŽ9�è¯" úÕ˜Ùfi›FXd{©z§zmá¼'"/ôŒüÐ寨ñ¾s´ÁÐ7tO±À°CœðFöõÕšv>NØy­;™ñ½è §A®ÃEë̹±u³ès‹5®“ØÛ»al^>Òl˜ÜœO:8³ÎÜ9zØ–®go8' úpÄ1 Âßêèÿ1'ã1}a±Îe €?{pêÐýÞt}e͈€·vDÀO²ºV›2{M™ õŸç¡½™réciÜÇÞ¨ÕÞê—ðþò|úxñhÞü>êL»wòн'7ˆwéü z“˜cuå½ÏÙøM´QÂ¥¹5&â`ϱžP3\÷××ÛÉGzLk¥¶OøBöÚ‰:®}²þNØ«ÚÕ´Kø§“ká#¢Ï?ùnö¥?A||"0Ä}EWŒîƒ‰ÇïQ7úí7ÏôSÅ¾Ø ½ ãí¶Œðz¡çtìpòó¤ã€EǹŒÛŸŠéàÔ+×ñ^sºÆ •ëã¶A¸××£ï$|Ÿ^ÇéL˜s‰¾ëä ¼_5§ÎÜ;+ÚÉ8sS{ÄHÎ\þü'úÜÁŸwÓç þü}ñç7ès-þ}ÞÅŸKŸ[ùó«ô¹?Ó¸¼Øç2šï½ëú„ïüž¼.~^÷ûcÉß»é/ò½Ùþ÷#½wmq 7&ß–½’ë»á]dg‹¹3ŒKo0÷Ê=ô›ÙÇ!¦x}Šˆ[8]éx~m²/Û}#öÕ#>¥±Î96ÇÖœá_±Q#Þ×'úRùÀÑ2ÖlåZòõˆýéÞ!ŸH|GHYöXÖ¢Àw3ó¬7Çþ_ó}€àkþ.擆‘OÔˆ³8żÃh½{_t öòê.žJ:¯Mì„ïÒyÞ"§£›^ÞõOÐÄWòïòùvSïß´ÑxúE1@qÉ“qÉ3æ2wWáÄMÕÖwòÞÏuÐGt@ Äçˆ],¾§‡¼«ÓÅÜ£Kø-àÁ{ÏñQóÝåêûÓk2šª›³¦vRÛú¨±Ë *kæ–NzœÚ{vËœ@ÅØß÷´oŠýVRyÞ‘†ö÷G=/wFçr[(^âýÉ3ÅûT½™¤‹ºô@>ʲ¾‡ôgTèTצzñþ²“ž…?Ϻ_ŸÐ;l36ªý ô”÷x`ùÛÈAþ6Ûô·Åìü-ò>âò½Ž{gžï§ØãIÌ-b| =Ao™âÙq-sÕÙ°] ñ~ÒÑîiš«­Hï:%cà ¸á]ð‡o3íQZ{`ümò3|ÙÓ»>/¤k›¨Cºçû̽7á€g»:SŒ>%JºLü¦¯´¶- ½VÒ± :ŹýÀ›p|pЛð{&Ý®±ux·åõxÇäI·‰wL–¹Nu™ã#Ïí5÷|gåIcÌwV&ñ:¼ðÎðÃ{í3¯èºuñÜx®®­Ûénqá=%Âf'ïÊÐ] ›Í ÙÙŒÛáˆ=…†fžiqÒ¬Fs¼ûµèC3t° GÀ1ÞiÛ2Gs“8SuChXîŸÎn5Ê >¡{E×uÀ+ɺØaÞböæþñ>Ê�Ô…Æ4Y»Ìñà_@—¼'óy”gê™[ᎉz꞉ӻ äD²­Q»§>ì¤�Ÿ…ð/”qäv=ÔÀvÝ©Úuäô®?@ÄþlÛ4~èïA¯!]ÑçS~FmÐbç]=çpO|†ì~¥]þ¨Çýc;…ÏÀ¸Ï¬K(3NõÁx³m¦†w®§±>}ˆ»°7†òoFU=ÄM= Àº§k”éd91.¼‘žygYòÜ<µŒ…û&èZˆê‡÷*‡úMíÜ=Gó‚W|…†vN1{ Ìäqbs[¡‘Æ<néM¹(“òmæwÿ[<sc©üo¢^'Xdœë\ÿçXÒ"pHôN¤Åþg¼ÓZ”/è'ÝR6PÞõC`ÕB_VoÊõ]I7¼'|Šßð\½ØSG>ßÑ·%ÚÔ‰ÿÕhò»4ÑN|…óMIð:ñÐpʼn}ÏfŸ—ÙfêåàòL´ð ‹6e~îÇ<Ç€çapçgÜ]Üõ|Ÿô×pÜÂqÓ¡:¾Ïæ{vrºcuÿ†ßSýu?þÎï£þæEå;ß¿’òùÎï©îâzz#9ݱºÚ¥|ç÷lšÁw®ÏWÛ)î {Ê÷4‹¹‘ìꮯ^ øãËÚ•çC~޵+1Ö‰3k†nÎ` Lj¥Ç1/êÞGmltø­HUøíAm\sšÝóH]Î[‚W½9:•üؾ>Qã­EáŸãrx¯³ˆcQûb Ÿ ñÅLÍÈ‹ßÿŸöÞ<ªòZÞ{f’L& ™„[A61 †Ì ¨5ÃE.TQ™!™\4É„™I�«d TCôœF‹ˆhÛ©X«”Z«¿¶ÆØÖÈiOS#§—=-½|-µ^P!ó¯õ^ö^3™ð?ý¾çùž‡iqg¯ý¾ë]k½ëöÞöfcÊÕÃ0VœƒcFÈIL0VS nã{Íxb#]-æDÇãæu}x>ï²C¯ýøÙ‰¨ÿx–®?ëÐkpÅ±× ðÖãâçå`œö–|Çäh’gÐáã¼Y³Âsmš˜¯5;è“gl¾çAKzñ¬ ”+æg‰ë¤|’o<—,ßlþò•¸Nð§QIÎE‡>Ÿ¹þr\ÇM–ë³1ŸûŽâ}”ÏoZl¯*™|ÿ#ކ0ß=97_‡²óyC¶nÈö‚§h»èpÆÉŸQÜØ¦7”¬dkg} xfþÏ i(×¹= ¨7¯}¯öÍÓ¬lM±_™ 1ÕÂæ¬pͳϕ…s11¾>f;ªLÇ:Øî@ÆÎ‚v«Õ±.¥3¶k¦œ§ï.Ë^ñÛl|Nø(àêR\ú=Ö…áý®<\‡Ýásåö ølæ'ÕïbÛãFâǶ?â6 ¸Ìˆ¯G¬;ÈgWfg ulÏ„¹j‘KYºjzº¢|0]³œÜü#ÈÓû¦k%ýàkaÜt ›—-äóc`Kú3å£LÍ~2ª8—+ÝLïÑéZ‘(3sS‘ËOÃòPßÚ t†w)öÒåJ&>ï²�í@oe±ŽåCVäå…{c�÷dLì`ñ9¶efžé<…´€Ì¿ÊdÞ¯ÌìbviÐX¸:ö.¶-h°W5*Yl<“Ý›K—Å~ƒô Û‚òÀ¶‘~ÐÿÿàßöŽ}Iô)£ ²o«ëø_=³‘às)¬-üN]ß%ñhUþØKø7À^DY‘1š Ú¸ýÑS|Ý™­{dN £½„÷+ªP÷±-Àߥ**ÎWyøû£>s¸úc9拟—~Å5•Û¸N6/ýn_×o˜Û‘fµ ¸ªžÔmåîÝ ã°"<Ümײ̊U˜®•õwhзç\øX›¯»‚LÆw)Á³Çø|LÉ~|_× Ë•|ÿÆóulœ´ü8´=в½¶·zì] §ÝÙŠ ¶ÎÎ>÷ßêaúе·øÞuÉnqíA|¸îŒxÌãÆ‹ò}k{-SáùC¨Ûý™Z”ùUÿ´©._~γ{”' Çô]šbòx|ÑžoØöÀ­ [Pö¹L—úüô°ú~‡Vöè|׸ˆúr¦«¸¿Ãc=Êæ´åý[<V _YU$¬!Þý8ÿï²ÿ;úœýW²˜RÔeÑð{çvŸâY_9F}Ʀ÷á<Äh¨óŽ#õX”Ñ Ûµl½÷v€à=«Çvb–2ß‹~Âê1 Y=æãVå˜Õ“ñ¦Õ“ùà!jõXOZ=ÙïÍðØf@¹Pn”›åf@¹Pn”›áÁ>Zur†'Ç£bÔÂé(Yt|÷-á]j%_ìåg·-ÈðúÎQ%î•0:·âýJ<<ñ3êS‡€Ÿ£EÙµ�Æhª¾Éey ÇXfði*Äeždcßu«ód�myGÐNc~%‡'_¯äa B8â“eq|„åú¦¥¤«êpFôyÜ?…}ƒñ`èÇ‘·,¶®²N+B_ƒclÈ7P.' €ëxž °¸ü“UÂùáËŒ¶&A[˻ƼÃöjcÁ¾ýéRFá;Þ=ÖëÁwëáÞ­Á%.%z+î!*É|È¢L�_sß>~6¼ú…QÃçļ>ÛOØ7%.Ö7âþ±À‡¸p0îûy­ÏVÇ@ž“ KºÌÙ¸VqÝÀ×Äo…xÏ÷U)VZ®ŠÇŽóÁb^8OŸ·?aûŸb]È;žÓÀö°­è¥Š’Àóä%=ßß+°,ò¼{Ô+ç^UwF,ߺ&ª`ƒ}9Ú˜¡%¬ÎkûäÚ„G¹ü îÓø™2Þ,ÚAÅv¾&‚s#c°®¨÷}RoZгoZØÚmR&°¹œÍÕgú¦}˜Ÿ(® Ïiý›]2WZŽºŒûž™>/v\]³…Ìr™Ìö³÷SF‘Ñ‘/ý ïM?Vr™oä´ô<Äs¸äû!¶&:ñn1·þ‰(ó ^á~'ò&æˆóPߦp} ã÷{À憺ÇLiÛ=.bAß>þ¨Âó-ÍÄÞ߃|²x›„ò|´tb4ÊqOä<zövvsW]yûF{]€3|Æh|_ès@ß÷¯ö(/€\Ãû¹y p¯Õh+Qöþ‰9½üÝ£ñ½_Íö-Œ2óp}þ[7˜h9ìhîï'^eغW£2Š—™ð‹&cUÓ²Ø[<wŸðçü—L>ñ¾es%Øâ˜ÿôÚWÑßã»PºÆN s5+ðèš°qïˆwLõ„wyL@ƒë;›®.jT–‚Žw‚OχûqÖF5ó Öyt^ós'ÁÝ_éÊ…ºæÒF5¿Äî*%âŒÚcÑ\{Í ø%çpN¡[U2ŽßªÙO¬‹‚Î(y&é˘cà{0ÀåÁpèïl¿@ÐStb³fßq¹fî±{Øœ›ëR\9GQ+Œ ¬Ããú™éAß6`B4“©Q©Œ˜¢ö^ÆËâà SûÌŸ)N,go̸» äãÖNé§Õ}RÈ÷ [)~ëÚŽf°,ÊÂêõq9TwZ•éôv×a¾ª{ÔÛ÷MÁ9Lôí®"×ø.ÍUttä;OuheËÕþ Ð?N3löX#ªÇ.i |8b¿D:LTÿò€ÏÑv€öç°œôÝ8/Õßv Ï»Fþ}›‡¿õÖ‘ìê3`¸7R‰@Ú»k^…Õö†z æ7;œrþÚUxbí¦Ók®Ò®Ñ®‚ðÏdÐñLÐÛ¢]NüvŒ¶�x±<ZnǹLe`½ÇºcNEuwvõYæ_§àXRÃþÉüäa/¿ ýŸ‹ãÓ«JØaÔ+;ãØG¶Uª]”¼û·†ÔpO_†í³Î??\ï²€Ú°}ÈÙì›™ú-Ë®Jvä3ÝJF÷.׳{qÛ¡¥ø+½6ˆY¦ê؃NMÁ5ý^“Rxb])òÁöéô‹œÅ~Àw%Ð0 hR‘ü»tÀe‚k™íLç‡Ð¾jú ó~”eÿ†ˆUÖ+óÇîEØÐ†HQø ¥�ôtÅn <;è²A¿/å{\<EÀwÉcÎ8¾ÁSÑ¿ü⊠e¯é±ó=e¸Þkêï›:5¹ßâ©’²�Ù‹ºÌwöc;WÔÍî×E5¸/(õ(fÐ þ• 3<¼l”†eoöT�-Zé«J6\ËÄs </as­›µ Ð; Ÿáþ=‚ËŒ&eÊðYQ£iAèj—E)‚|­�ô×ܺÜbù!·CÔc‹Çn;[]ÃìÌ» uTØ{ßú ê/ ýã¯@ŸöRì]åq¯(Œ“~ÖoÑ,Á7Ü?Õ»cV8ä¤vk çõd»TÐ ÐQ ¨ û®.“b•óƒ{Á»¾¹} ëô5€ÇjøeˆÍQ£¯ ‚¯™©™ï‡º{§@¿>¡Mö~¯ÉU„¼÷€ÏÑãg  Y‡JîoÐo®ZMo¸Š"&ý~G›yŸ9êÂøº«" »,NFÍ}Ên°ðŠåøú¨ú`+\nº™ÙýЩ}Êñ@Ä}Wt,-‚þeöŽxÑ?c¹ÁÍšf™ mÖ 9ûîÇí·¼ë wCŒhT®êÿŽ>ž­}¯eºX>|^w¦¶îípÍXUŠ@†8NžH>¨€ŸR.ŸÁÎ#hbÍ@3ï±O1íe¹ïðÔ}hÏ<w˜ÒmÓ²plc‰}‡ã³®iJÞ^äJJg´g´ÆTÓÑ�ØzbúÏôµyP÷­ÂF¬0Ö3 †<Eýjk6Ô®9(®q™ûCµ5ðom—YõK È Ïkÿ+Ølòˆü¼ÇÎ\Dì×Y€5ã+äúÔ×ËÖ—ÆŸÁ¾E_¾+·¿ó‘á§Ê2ü„g2´•;´NÓ€FôËVðÑZ7Ðtö@ŸŸhså â8vx_héƒçðÏÚU�1É }Ï.m�Êõ¢tÜvT´U�Ÿj´ ~vrZñËÞ“í±¢Nt© è‡dˆþ-Ï\ ã=ó‚]å ¸ áx±3ü+ÔÑÆîSµQ;æÙ!¦ºò0Æb›±±2®[ ߆:v1lÂò^«ËšàíCàû×kVðs¹è÷€@ø†bBß þ`í>ôU#¿™s¥ÆP⯶Oª?ÚÃú%öÖÿip{˜)nTÙËÆE¿5Ö¡¨Nû]§b{à› ïÖ  lxw-â`ïÌ ½[ƒúðæ¡wמÚ-³ý\Ù¾Ïòn8ŠECï†O†ÞíÞ >©}õ™n‹‚åöBŽQpôÖê3áý.µ°!ö[àgt_rÜ a¥ÇS¡hæýlÝK;û„üu2Æ.° W?èªÆVp-b´[:_œl®Œûæ(ïþX‹ï@×>æ£ö¡èˆøÞñú)uïúžáÜ¢†Ø¿ö‚Þ_²7Ä^`ñc½†¶‘k‚üVôäìaÊŽ®YáÑÉýfMÆ»ˆP×4™ÓÈ|\6ÔýŒ¹<~x ~xxü@<¼l”esobüX¯ñø±rþÜψ‹ðŒÅ–Ù?àYQ£:•ÅS_Q©/W#íg3Ê ísG™+·k– Ï­‡û§!?ï†1çÀ}· wy¥¯¹&B?õ¯w±\Çp§þا'@A Ž®ÿzÐUÄü±²kë’6NY®äʺáo*êÌF<÷-Òñ�Ìæ1}t¤�áˆýêÓ5 e‰´€nå@®ÑÃæ/Ö³uú\ áìùh€Ç<£»rµ,lòõüCÖòú âÇà\æ‡T—õ°;‡û^œÃaþÆ^ÌŽA¶ÝS]Ù£vóü³ ãèÂø#¿àû>¯Ôcú"ümÅœb†ÉÙ¨Lþ:ØùŽ9§ÍàC0Ö"Í@æç€ãI´eŒ½Æ^—u/Ø è®bB&\30ýoÆ{§Ç´aПvÐ…¢}fWþnS8_äcÃÝ…f#Ç:ÛçѶúî�Ÿ®â|.ŽvÌp©8n8jÂyÅÞåR÷ªš b§ƒñ^àRÑ·Ÿ†ø}âøP‡Vt¢C³÷˜ÐæÎÍB›ú–ôc<E9:wn*ê\÷ô1ÓVV˜+§G9 „픞û=ý–0ÒSîveìcô¨W‚œò 7ÉèŸVŽ=öcA %)‚6^‚ØÞûôÇÐc^†~?Šý°NQÂ8ºÛòÊ«=–W¾ÛmÄV9O×­ºÔgð|Y††ûí&¡ŒQžõÙ-±Ï,蛞Έà{ò/ß—)ëoÚA>æ® ´GìýîÚîþvà×]Û;ÐîÁû½p_ÝR.\6¼÷ä–Úxÿ¹¥6Á¥ã7øÏ?n©íí6CB¿u™=8æ)´®cÎþfxŒ©aøå=—†V%§§0Š9ˆõ1çiÜS0|¼]ÉÁ¸Šqäºëh;ô³™¤\ó˜\³\(×Iº\Wß®Ëõ2—r äz åÚ®\‡¿>Ô®ñ±÷½ÌW¬‹ØÖGy,òŸûv‹EãÙU7Ð…~s¸ã%sOÈÿÇ †4 b¶éÁþfý tõãüÐ…ô�]ö4m7ëoåoáX…õ·¢@?ÛO w|hD|ÇC˜;œ»}ÜQ–k/œ¾Ï±òVø»ñyÊ0ÖB =‹ó�×` ràæ«E Ã|åì}à vUV@®©\ÂÆY4ÇhÄ9¢pŽQiä{T̹¡ŸœQåþ2WÎñÒHʤ/)ƒÚŽÓPF —›ö©¥5\.˱ͥ5 ›Šc àã \‹lŸÜuìhÛ¦ÿt³9M6ÆS»g¸@g߀z»œ§¯»“ðºûÍ«sPçu·Îkyµ»Tã¼¶keðyԟ呼®Òy½Ìc9…¼¶k§@'¡ß?†kÑQ#]Ÿí3kÖïÂÐïçžãý>œ‰ý>tki ÆQçà<ôƒ>ÇôØ,»Š8Jû\÷â8ýõŽ9mÕŸ­r|Þ±Çäà‡ó ý±±­óAÎ�íía¹Ê·á[[:Ö†º¬¯ŒØ‡˜;» úº‚åyb¬Xì~çyã|l½è“»ÿÂúòÞ¯é×sÃÓ4 ç%"yÉ9Çó2sPòâé‡|2 /9”—AÌ=0^jyÞÄxñ ]§€—SÈ ôËóçz¡/+˜Ÿy Œ=1Æ"/N¬³Ëy„é_Oýëù‡ë_¯®=Rÿå±2WÆÑéBÿ:@ÿ:˜þYûMê_èß–½\ÿòúK5ËiÐ?ˆ§!þÁ?ô.Œƒ<.Ÿc牎AÞ6dØã´GðMÏoÿ/ðM0¾üŒíÇuìL~XÇÐë…¨×8Çuç²—­‡ÅTÍŽy/´9µ¿ƒ¯ßBnðåãë1—„çz~p–­#ȼ ôýsŸanÐoÖçB¬"—ÅúÍ<wÕø\HÃÙ?“¹<:Rú·»ÿÀ𬓹ìÙwô¹õb.Ç)oÇç²Àñ…!‚Ë Ê2À÷ æ³À³Fx¶vÛÙZ6ÆžÿÆÜìdÙ™ ö²<ÇÎê°Üs7¼ cû_ *@=X'õïÜà>¦cbsh'û=ÒNl  vÒ+í„­áP;ûøÎ ;±I;Ùƒ6¿N6öQÃæÏîA~Nƒ`ýgbúsîè\…ý?‹9ýÆÓ›ÙX"£Ì®ûhöß¹ŸÛ¿ðë¦8û€ŽecL×ø½æø8¸7…?}ô¶ˆöC}A¾ÇdÀ„ç`œ0ƒÉ¿óÀ£llÚùGÀò5É5;¹þ<[±äwÆÎg¼ó¸é§æ™—²ù—ýt^¼ñHfõé9l>Ö77ùrŸÓò²÷ê ¡ïx‚Í9|…éôF½£BE;¾z@à~žÂFåÒ=™Sá:÷.ZèQïž®dÀó£O(6¨ßyúßE ¼Ú`ü×»ÃÑÆrbw<2°l|Õ‰uè¯Y^?n·ÈÉq΋äå8‚œ]±`x›„-çA›m˜S@îf?±|å“JN±Gaßá:º.jEº Þmȧ©Q™ƒ<veCž>ô;µžìP P¿«l3‹nÍ®Ê6ãB9Ð/<O éè1E\{>†>¯ßV sÀgYÀgU3Ù‚Öô—†•Ó(äíZÑqз!¶í­aóWJ­˜Ë7½Ê×IÆüàÁSµÐwX¿ôÓêGeñ9û1Ïð9{Ó÷}ÿ(öß ã!ˆ¥uÈØ.äœr~x<»«‚ÿ§íÐ'ê‰uQ7®O ¬ÕÔM}¾�åÍ×-||ãðUúÚ£ Íå9=ê„¡¼Á^ ÌÁoÙ±/E¼éç±s̶žø†’•˜×Y•Ÿb^‡ü‡8€ç<0ì€< cî©2ÎÙg:PÓoŠXöB|r^dÈ9±Á•'cÈæÆô À_Ï›ë",²¹ê†Ø¯öѹ†÷;;˜˜"UCÐ>Æ¢siMÿ­J×Ueϳëó ?#ó 6ôÑk'p¾áïu¨wzó9JÈwb¯°y‡ øêu櫇ÖÁXf­GŽ)_Äç'ÐW!Á3ÓßqžYØç­È úˆØ³¬\ýulùPÐS6t+ȃçN5ÀWABîôOͪQ ¿Q?€—®c뢬ßÖ±9œÙ‹ò8ŠòØŒs/áÙ\Ñdò@º‰ÈÃõ»¹<"Lç¸<" àá5פ.°¯$ò`ûoŽ1yD…<¢TÁxyD—’DMR( l íã3³g<u+³…"ˆ9hÛ 7íäf›òØò1Ð.¶ÐØÒÏuÛý§£ë5)¿ålÿÎf[Žê±%–‰±¥|L—ê*èE¨x´ÎØÇƒxÅöé51&— Sk˜k®È->éM­ùÇŽsjÈ\jl:ŽuN0y.ܵ×ÅyM£ÌFŒ:q+[Wâ1 âL¹çˆXÕ®Ç* UŒ¯b_Ãq£í³Î/œØ¬d€íò˜i6b&–ž÷ Lՠχ?b¾:Èæd>×0†OðX·ð3Và㮱܇6wƢ̗ó“pÎÁ _Çwç¢_?ÒÊR\o†ÀbNƒ{î@7ö+E`OÏag×X.]Ü0üÈ3xÖüªéÓí»_¿ãbðžåàÿ1ž>ñDø)»2îäm»éÇ.÷K·½�<~üÛà—òÞ×NoðØé¼Vr@6ÿ‹ëBÝlMzå“8mR »-Êè.¥ó%ô3•eOãyÓÛ˜ÛÁ¿[qŒvtë;Øb&ÄèŠôù·Éœnø Í?Án.göiÑçRLjüë³s¢.ôëðöE{¡½ ãM eAï‡{XÇÖâÄ\*È‹ÐÑ¿ÇæR­–ÙFçRÁ6²q_îÙUyÚÚÛÔšve,ØFáKY0oŸZsºÝcG™Ø†«ŸÂo>¾œå*±Å¶Ïbñ÷×ø‡qŽHätÃ^1μs:¢§6COÏ}Äæ7Ö1սב6‘%mBØ®ŸžC˜°O“ÛÆðÑæíhØÇ {˜S0 ÅåÔçP7mŸm‰ÛH4•Eé aλ®8v+ÙÃס‹ÿÏÌ ÆÍ`L>>ë´_þq ÒóXy_N†>^ûB·º‰×.Õ,ÑÍ›jBZE4ÄbòWáŠg‰}ƒfÜÏÚùbW#»Æ:c>`¹Ë‚û~qÏÁègì2´5¶¶a™†vq-ÕycæÀ­˜ß³xq –Á{Û'ÕŸâóÞR¥�ýlgì£P/Ô±}Z=˘ÎvŽ~™½7$‚9ó8ô»ý¡ˆuà¶'jŽÝ±ƒ v—† ÚŸ¨zÞ{ó¶îCÌĺ #ø³m #8öyŸc6ѹ‹Ñ#ç.ôñþ•¨÷iÆû£ãæ.n“ã˜sGÈÜ‹ÿQÈ[¢8޹…X¬¹ús‚ ðÖ£ÌÖ\Ó÷Y.lNFØ#ÎË<,t÷ï8/qøÝ¾âòû©)|nK«€~ßQãîï °ÏŸš­‘Dq\môÿhì  Ç/0gk½oÓ=‡Š"ßqá±F2µ%ýÓ5×ÀF—rZÕ,ïo„qþ“¾ç)ç$Œc@¶.¬sЮYÐ~#¥Ï•¹—í]ZpÀ‚ãQà¡0bëšèÉ랈ûEFž‡v®Žî‡6ÞÃ3i#fðï%àç3‡†¸ñï1Ë•|¸fÁ?Ü“ø{l ë Xá_BÛ¸?Û–ïµ<…PÏ õÞÖëÕ°½_Ǻì"qO˜} ÿVPìz¹å¬Ü°»·³ûÿ˜–|ð”ñ_ŽØ#ü8蘽[©@³áû(djañ„ÞÛˆ~ž¯ Ó¥»”Žß­GÝéƒòÜ mÊ\®X£ìl¦Ç2m¹’çøE}gø èäA- |Ï$¸Zbýž¼*žû7â¼e8ÙÙ=vN\žB.éÆ:¸§Æ'öƒ–ˆõé ð‘‚6ÛêØá7˜@¦×÷ âõÄð?k¸' ÷⽸žaµ.W2ñŒâÂ>ßi*xsžqË? pܺø{l³¬!6ÈÞÓ�ϱN?Ђm³þóÇ¢C⽿o²÷á^èü?a9xö>ýú¸Üê}‡ÉôdÜïâxŠŸ™ú–“t@ýÿz“Ÿ ÿ «ÿdðËi´Ë> C½Âýæ(vÏõàEýžëÁ÷ô{®OßÑï]ìþÛxòy>jxoü5ö@ xD\ŹóXŸ¸ŠrÝâ>š¢~õòD¦"õò¦Ãï Má¾úÙG3µ¢ßà~øÑQù~ŽŠn´g€÷(|§ÚDèÓq;f€Ÿ§p ƪ1v6Bœ‡�?Qtq€þ-WÆcœý³qý«@ý{õÏÍôo’Ø7Qr!ø¬ì›Î[?W: ž•�|6´ƒcSkùÝ·‘ün9òÄø[û°‹ëÀßœçìþõìŒÛ_àYÂÀ_ü ÷o wqûB}ú>Çsrï»§8· 'Ü'^Ÿ9Ï5œp=–píÃ~‹nÄu¶_”ïÑ;ëÎìdgR ÞO0¡ õ9US¼ÆÃüÆC&~þ$Ó¥ØzMJ1ú‹K™¸ë<ÿ¨ïáçS°¯GA,,9Æö ÛÍ}žãqá¹hës™.ËQv&¦ó=ld?Jìµ°@ù·ñÌ –Ãý½…ZÈïÃçLšúÊqÂýŸzU¥å÷à•ö.¤¶úí[½uß™¼á9ô ›¶ÎØðáÃ'{ ¼O=”uè;£éĽ$äyˆ?—íåû[ñl›_éÀ3>æ§~Þ¯aß}±vBž¥åãÛð+ï˳ìXÿƳýxìX&žI±>ž©•tó}<Ê®íÏ«;îªTÃ|`îþ~û€ÝƒûjñÝ×Åì\Ú~ ó-Ë^ žé°³¸¸¿?“ïqFºí`;ñÅøn1<ÄÞ3êÜS”­ Ö°w3O‡~˜®Y²µ¶3úçé0Vž®åâ¸ö}×~Сiuhe÷üxÎÔ¬÷ ¹ÝtòäF­è„Åc°xJvÌñ(=´<¶ßá£xò1㻺'hì}Ù9 €39>9 È õëw0†zc˜½Tò N¹ð,ÛƒWƒ¬ _™Ya~×U7ôåœ�AœÏëá±Þòõ‰s7}wЃ\¦{'zrû7†AWÁÖCávèükþ„¸¡/;¾÷(ùG®þ^šåJ!î7ûÍý°ï¸ßMÕgeg½À—ðóM×[2¹T¤Sì .Æúÿ½PØŠõÂ=ÕäýmÈh²�M¦þŽ>\#.þhÃ}Jý8޽õBfì4Òº‹ßjŸ°[¼ï®—å³vvd€}p úà(äg›—àZ îŸBþxä ›»0&¨@¹[PæÀSÖ3 sÈ™®¡~¿ªAÙã·¼žäú 1¿/ú`åË0&{ìê¨òÄåmæÁÛ ‡.Ôß±4}�þgĺLÕqò€ç—´„UÀ3iÜreôcP¿K­ÞQæ±ã˜}øÖQò]›0nš¹´µ¿=\�9€ÊÆû°ŸNbûÐO3Q&X~à6W?;sW õ2˜ÿmçÉóbVb“¸!Ö«Ãè£ÿ8�ø»r#ø÷ ¤ïÑ«Ù;þ”G/¯0GÌJñ^3;O "ÿe´É(”@¾Y[ñ}8Ž#ŒÑ¸¿3¡Ïä÷2Æ ¬IŸ…¡/Š#æ§Ø7¿ñSæë&à{D7háWK´ðOJ´î7J´ÞŸ—h{Y¢Eþ³D{êííȯK´—Þ)Ñúþ»DøC‰6ø¿K´¡¿•hÑK´SŸ”h§Ï•hgÞÜ uÛ õß íÚ ENlОúÆë.åñi—ÅL¯ó³]á'£j)äoƒ[4üÛýŸûêeìo»^­f3ŸWœƒµ`=„ÿ¤|ÁëQ+žÀúonѬǶhöã[´"x®Áó²7ªµŠŸWkU²Ï÷tßkòþ–§îÃwƒ~´å©GÎlyêøîÐW«uþ¤úÐ}oTúÊÏ«=òËêCßøÏêC‡Þ®>ô_WzñêC¯üwõ¡×ÿP}èÿ»úÐ[«>ôÛ«ýþ“êC=W}ècôGàËÝ»³ð|yþ†=Y‡:ÙYžG€üÑ…ï*ÓOû«PþF>ó^Ìþ¶0þáï÷݇>pòä>´ï2ùPÓù>ùЖÓîCnÉÂåß4/畲;Y^µÈ÷6ã¾)vžÆ£X#jõ<±à>>~ÎÆ‚g}:ÉåØåÒà•ÚŠÖosS ÚkCíŸVç«ojm 5ù[µ²Vo¨©Ã§…¶µù�¬ù`F\½ZossSkƒVëoíðµb-Ä«Õ{›š}uZÙ¥u3åvo‡wv³·µaöµˆ€Ü¯hnö5x›Ú[ öµ[k}mþnj øjý ­MwªëW¬Ö6µ××ûŒ|nàYÝZ]¿Ê×âlãM°Ÿ¤±…=мµµ¾`à‹½­—…´€Ï ä7zÞÚ/`À·šB¾¸Œúk½ðh1 xN„oiª#•õ‚mÞZŸVïpa‚(â_»ØÐnÔkßk–Váõºë^)% åjbD#{Ê&ÒråzÞKØZÐjoð5~¿ÖâmÎ…|ƒZY‹w«vi3ö •ßM­Áö¶6 ä«[Ýæ x±ŒÎˆë‡‘~¥vikO\”ZËì`{ëìÛ[½³Wzƒ!Ö%¤ooÔu {Rkj­÷k­þÆÔz ›ñ†tª%ú‘zscb ^ìoo®c˜ü›BÞ¦VÙñ(ò¶@SKW"Ä·ˆ«“!–ö`HÛ&Ð –€Öè…^ZÛ[{²ï ¼Û„œY?‰Î…¼µš´›F¦dÐw7¯âÂC ò‰Î1“¸âk§B@¯Z™¸fiA�Ll¼œŽÒ¿év$ºÑ|­Zƒ7°ÉÛ�jáa&×éúò9èmö±Ÿ½­u;€Ô“ÓüõBÄ::©ëxiÖêÞ°Ô8õÁów;Çi0±¿A?Â`}“¯neSëÄÆéO÷e€ Hc­ îP}¬ñ7µ†x÷ ûºRÛì AX-åP´Š–ëE¯F ¯Q”²ëfÜ<²ž¿•3ââ"K‰§ÍǾNЇjÀ hðŠ%çÔ¤~C³ˆ<à•öµJG3÷¼~{[ô•n¡JÙŠ$ü%‚5úë.�»¢Ôü-R¶ÒèÑÅX÷*XÍÔ÷ª+ioñÚ«¼m®R¸–®Þ?¾# #ø ‚Ïhª•l(5¤6=K!<eq´®O3çÝ””» ôôÉ&/€ºDz–êÒOGQZ$ŽÔ·x:®¾�J)•z,8¿=Ò¢ì×à aÏ­�GÂùWa-W%؉Q)½.͵ú¶¬�#÷‚U¥Ô»¤<]•Znç…’¡´}<ÇIÚß’]nÃ#íXV¾àæ@ž•·=ä¿CR¼,b\0Jß-˜e%ÃÇӯυ°E%yï'à—9AzO×Dˆ*Y*ý¶hÛÍÞæöÏ£ÇåF•‘r~J)]‹EZ0ïH zÑdõoñdÚB–*¬f©Vp$½©ñê(ÊP¤mïÿ~â(ûÉâN2MYoÅÅUéüi*•ãI&TZÚÞZ‹Rˆ‹èeÉÛ_—­‡®„&.œ<¤{k1()RÒtž8‘€ús?#Q\ÃÕäqX—[Rh }Ž {û²€¿½í|ùC"IŸ‹!Jß-d�x½×‹¦Ê”’ú“T™T)Kûãý’ÌH. ¸)+@O|–ć>Z”ý:’ùW<Ÿ“ŒG•Ds/„Z”ýÚ„ñRûà}Ãðç'¤é×iõõMn9Ò»°~¯"ì§Ã‡Ç1×±‘LþæsŒ:y<\ºtŠJ^Áß·ƒ¸æUB?ú›˜ÂÕ7û½!¸ÖùÛ75]ÍþÖù7þ°<N„Åïóø=^ñ~Ž“ßË+âGò0|ºÅ@rMZKS0¨O·(ÍM›n÷n •E„¿ná-kÜË|!¸;bë4|$îÁ•iKÞ-^^^pto0x®ÇIߑ湠ƒ°šý~6ÃÐÞÆZ“=‚“n—ÚYëphmÍÞ(T PŸ‡^äzëbŠCL4ÙH\ŸâôÌ)¯,¯€keí¼›jë++6Í©˜_5×é€ÿ;6ÍõέóÖÁ_zÜMt^Žðòx8›G¨ øA€-A&_>±ð%3vm xYô&øøý…Ígø[Y6ŠFË872{“ñHzÅóò‘Ï©µ¬7>»µÉ?›K›—ƒûrrÏŸo ùéÓS¤ÜH8+¿¸ÑHV> œ•¿±z×x —OgåÁm4Fù$pV~%p2z’ÀYù¥è�’ГÎÊ/aÎA —Oçýs3ó4F¿ðû5·Ö\‹x“NÄÕ_ä÷7û¼­#úw$\”߆ƒRN¿ÍÈYå|#áB¿°c¨^é÷b”ǵô„óòØ´œqÏŸ3Á“çÆ=¾„xgŠ'ñóÙ39w–”NQDL?)e7ò?ø¥nÔ•-N,§ËQ”åÊ%–Ã~Et•­QŽ÷³(ºNÒ·4±““(µÔ oIb9.'Qp‰,ˆƒK›¿Õ×2²‹„|Œh(û³<±VÙ Y/ä÷?qxD‚‚pîËeq`Fúëá•+—õòÉò¬‘XžßÏ@|‹9Ø_ õ™tQž€yeëy'ñçBz뎃j¼8Õ,²³ AwRro^>¢šdð)kÑFyŒeÛüÁ&cµ Û[Á®Ü•ëåZáÆËéò¾º¾>èã¡1±œ|d¸õøú‰pYø¿‘ÉÕpïqGÀEE¨ÇÑݼ¨•p¯S åÙ”¶áæãéK„ú–âsâîã*Ž€ô-ÁçÔíÓŠ#áD¸ð['®ÂºËå¸b‘„ƒüu ʉʋ<XLäϼŽx¶BÀ…wÓk (Þ¥”Oò`‰Bù0`cG°mbuÛ‚!_‹ä;àoóB\ Éì3™GH‘Éù ='»à|«½Ud\ü?plÁ!g$n¬`ÆÃ››ZïÀõ,¾ ŠÏ1Ïo øÚܵMõ¬ô¼—-©Þ¼J´KV7 nôÕE/®@Õù¶êËŒŸc}: ^²T'ùÖŸ Öxžc·ä•âËkeΉt%_Á?Y5n5Öˆõ!¥ŸØ“‰l¹4›lžeÚM­µÞö†FCfËù¢’l|/§$UØ/é„Nʆ®RÚŸ<©æ‹âf€ül`Çòÿ£uV¶g‚/ ÆY“O5 §BM­0šm°õ>×zò•ÆzðçX—ý¼ã5ñ³Ãÿ,ÊÈŸþgN·ÀÿL)ÊJÏMÏIß–žn‚ÿYSÀ³’ÀUø_f xF xÜoͪš%+nP”Ù¡æØfwxòov_çë˜lÔý÷òÕ«Øød68¸µìGLFûÅßÅßÅßÅßÅßÅßÅßÅßÅßÅßÅßÅßÅßÅßÅß?øgV”p…?ö{yn2ñ<ÀóߦyÞÏßJó< Ï‘ú¹ªÀó×Ó<×àù+iž»àù‹iž{àùwÒ<Gþ¥yŽü#Ísäÿ‘4Ï‘ÿ¯¤~nBþïKóùïLóùß’æ9òïOóùoHóùw§yŽüß’æ9ò¿:õs3ò¿,Ísä¿:Ísä~šçÈÿì4Ï‘ÿËÒ<Gþ'§yŽüOóùÏOýÜ‚üg¥yüÇÒ<þ‡?NóøþkšçÀÿpû·�ÿÃiìßü§± ð?œÆþ3€ÿá4öŸü§±ÿ ä?ýg ÿiì?ùOcÿÈûÏ@þÓØòŸÆþ3‘ÿ4öŸ‰ü§±ÿLä?ýg"ÿiì?ùOcÿ™ÈûÏDþÓØ&òŸÆþ³ÿ4öŸ…ü§±ÿ,ä?ýg!ÿiì? ùOcÿYÈûÏBþÓØòŸÆþ­Èû·ÿçÒØ¿ø?—Æþ­Àÿ¹4öoþÏ¥±+ð.ý[ÿsiìß üŸKcÿÙÀÿ¹4öŸü§±ÿlä?ýg#ÿiì?ùOcÿÙÈûÏFþÓØ6òŸÒþ~b­hã^qŸ'îŸ÷“ø%_.×ç'Ü˵,qª\®éŒ¯÷bm)_î§kA™Ýâ^®Iáü±sgøí?Éõ_±ö•¥‰û1â*ךÆóË-[ãáÓ#â^¬÷Ìxõ5-ÙÞYq/Es‰B~þXì3%þÇ×è ö)ÿ7~«Ö^(_ž“xOa‰÷ŠÐ%õÎuk¾Z“;*&”{ü+I@Šiº%ï’+²ó'+F¹%ñÅÔ)Ê5'à³')÷9èQ­F½@zÊR”ÛPnoŠr?H(ל¢Ü{ñÅLmÉË© kت–¢\e|¹ñ©Êmˆ/—Û÷?“¿2xò?}aòO©7 òWŸÿÒýÁÒ=”¤Üÿ+?Õ¢Äþ–ìÙðó§à"ÎèqIÂÅž =žI¸ˆ{zœ‘pÿ¾˜Ø®ˆ{kúà"^ÝÜ–�qKk.â—¯%\ÄY=nK¸ˆ¯àܾÔWœÇÿ‚ž3 p¾×$!î™e>°¯ÂŒþÁ¿¹6^ÀáO&Ê¿PÀÛÍá‡ñó< àéDúÇ øòø8O”Ãx%ÉÏ,òƒ$ð )àIR«ÿ§àÅ#¡cþUÕåö½Y޹q2<•)ìîªðeJò~©Q’ëÏz%¹ÞÞžÿ&Cÿ üaÀÛ œÚã~59=˜ã'ÓçªÉõùßÔäúÜKÚý€ÀcÈÿiI?)ú½:RnØøÝádúcµ$ö¢Z’ËMÍ7ð?#ý Ы^bÀW8ö£è—F"7õz'~U½‰ÀŸ"ð %ðZ'rVo7àMe¾™À‰Ô­ÞGàNúK½×€ßNñü3?¡î&x¨#p; pâÏÕox„Àv{ üEø ø.ÿ ‡ ügNôSý?Bà'œöË;Né9eÀ›)þ¿8•ÏGNå|ŽÀ+ ¸ÉBिL9NúÅTHà$Κ&x˜À5‰À/#ð>/'pâßLs x ‘ƒéj§ô/&pâL׸‡À¿HàľLk œÒï6à~*‡zRžØ…©Å€·RúCNûåKNñw8ñ¦]Nü¶©—ÐIôÁô÷ø× |ˆÀ¿iÀÛ(ýÏ8Åÿ=§|½LàT¯^%ð08±#Ó/œÊù¸ßLéü5¿gzÀ)="ðFŸÀ)ŸxŸg©°„“~1gð�±_ó('ò4%p"Os úÍS œô£y&úÍx•Rzª œÒ³”À‰Ý™W8Ñ[ó'ön&yKøIó&Ò¼‰À)=mNå³…Àû|;S¹}Ù€·½2?@àÿ*Óv÷x„À÷8ÑgóS¼ƒêÉwœâÀ©<HòÙ/À‡ùç>@à$zÖCàï“òOp‹BÊ!ð‰üÛ§|:É‹*|9)ßGà |§€Cì²4øK¤|ˆÐ&ð{ ü ?Fà —à§üþ/"ç¨.fÅòšßBôÄòoNü…ä [ˆ½Xþ‹ÀIÿZÞ%pâ-Ä_m!ýnù;½²|fÀép9ÃLà.Ï!r rË(2ÊI#å5~g˜ÀËHyÚnÓv‰ÿùñÃ.'òÉXnÀï"ö’QCÊ“¼1ƒÄ÷/yfxü47ðm$È ~æNâ'3Hþ|'ñKa×¼›ØÑç —ï¬!åŸ$p&pâÇ2ˆ¸“Êä½wR¾~JàTnÿnÀ·õ8‰¿Ûˆfü†ÀIÌøS:ÿBàÄÿgüví—a'zž™IèWœÄÙ;‰fŽ#p¢?™—ð­ÄN3§8‰k™$ÝJÆ™óœä™™×x”ÀI<ݦ8'n#ól™7¸‹À78É“3I¾ºÒÙjÀ·“~Ìì pJ'‰›ÛI¿d’ñ`'¥ÿ_œØKæ'ö’Iô¿“òEô¿“ØE&ÑÿNýï$úŸIô;Õ¢ÿÛ5'ú¿ÒCô;•3Ñÿí”/¢ÿÛ)!ñH®Ì)OwÉ'·“~Ì"ó‡I|Ϻ””òW8ñ‡Ydüu7鯬/8á+ëZ'rÈ"yàÝ„ß,2¿q7åë6§|ùœØQgÝÝGàíNüLÖÝNô6‹ä{tº+‹ÌcÜå"ð‡ œòõ5"âϳž#åä<ÝEümV?Ó~9Jà”ß7 œøÛ,2ÿpñ«Y' øÝ”ß?=Œô[3ŒòÄïYó<BàdüÒIè´N"ð(ýì$tZ‰~†‰Z‰~†‰ß¶ý ý´ý ÿi%ú&~ÞJô3ì!p¢Ÿá0ý Sùý ½µý “þµý “~´ý Sùý¼G!p¢Ÿ÷hþ5§òù&É÷È|¦•̧í vj}‰À‰Zûœè³•ŒëwP~ œÄëS9D œÊÌ›íTü4Sý9CàšgKGÞEò¢l+z²í%p’'w‘ü$[#ô½Í&yò2^È&yòÒ_Ù$OÞAüm¶‹À=Nòä$¯Î&yò=”N’'wQ:=Né$yr—‹ÀIžÜEé!yr‰ËÙa'v”ÝMàD¯²É|WW„À÷8Ñ·ìá—ÊŒ»ï¡tý¿‡ÒIôÿbÙ}Nì"›èÿ=Ädý¿‡è6Ñÿ{ú<Jà$®eý¿‡ÄµìÓÆºÉ&¹/diSò_&~φ ×qˆžØ Hù0“yà/{±M!pb/¶$¾H{„+~òR/OìÚv•¿—دm1áëq~Uðu¤<É[ldi'¥“¬ì$ò·œÒ'G œÌÓî¤ô÷8±/ÛC„_Ê™/ú²FàdäËÄØž6à_¢ô“|c§‹ÀI¾±“è¿ä;©>|c§‡ÀI¾±“Ø…ä;‰]ØH¾±“صíNìÅFæ¾-á¨'dÜ·3b”Ï!ã¾Ä¾rF}ë3ðäh„Àg‘ñ8ÉŸs<~)OÖûr|†~†šùU™³•”·“ò’¼‘Äßœ)ßMà/ò”/2¿÷í"›ÐOáï<DÎ9‘v ¿¹d^è0¡?÷RR¾†À+ ÜEà‹œÐ“ë&ø ¿¹ABÿÅóv±'ïj€ï%åËHùƒŠ®' aEÿå;jè#pâÿˆ½ä’qeC„À@à$îäûj þ!—Ä…FârÉx³ÑEàÇœÄß\bwÄ~s£DÎ’N”çD>dž6Ú Ñ‡¼b¢W _NÊŸ&ðjÒ.™Î[môW»Ü·|ä‘ùÞg\¤üFù:áO2A_òî$å _y½¤ÝA–èÑŸ¼—IybwyÿNø¥åMàQÿÀ‰]Œ2‘v·xi—ÈÔe„/2O>êj‚‡ÌKZEÊSüDž‡‰=Ž"þç"ŸQ»=¤G}ƒðEáß#ø)ž3ú«Mô¯: à'÷Gý™à!~ ßbà‘ëõ¦ �Ï#åɺCþ‚?,à€—‘òËIy¢‡-W ü §ùk‰ù­ÞGà=„Ni/ ïüljœ‰ËÿÁCú+ÿGÉ– z—Oôð0ñÃù¿#x?GàÄ~íœè³}&½²û=¬8‰wÏPx=ý·o#ø£¾Ûà×#å ý`ßOðxj'öûmÒïö>B'ñóöcO“x÷,…ÇÒïTn•±Ó‚Bà·x„À›ž0ï4äñðk>Ô+ø*Áã"åÉ>´oK}íW$x–¬ãü‰È‡ø‡‚Ï~/,!p"·Âr‚‡Ìw.4è÷K?�ÿ W<$.ÞFè'yW!]ï#~¦ú¢…$z†Ø{!™—{–Âüš€ß…/’òšQ¾`Èàk˜hPŸ‡ò?!|… þÿL±Œì»¯ÀÉ<ÿ}d|G÷ƒÝG÷_‘ý`Ý$ ûÁº]NÆ;ÝtÿÙÖMògº¬;Jàd?Ø.ºïˆÌÿï"ùݶËcÀižÿÚ/Q]Îêä— Zhþv/‘Íßî#ôÐüí^2n¢ùÛ½„š¿ÝGó=’¿ÝKÆ/4»·ÀIþv/ÍIþvo”ÀIþvåÞkÈa†èõ,ø‚ã|Šà˺5>~Ç%Xâõü°lø.ü[rý,<kàŸ%ãä)£ ’—=!|š§BæWÐþhÌgÄ<Ó}ý6úf£¼Sú?&ã‚ïI=¾Fße”/—ç&€ïÑvåù‡:€?Läæ`sàûT]þ=2ƒú£Ÿ0ðÌžÅÁ*èçè#„M”Gø¿å¾G¿eÀçŠyêÆ}r?ªôÃУ?&ò‘~�ù&pi§(·§_èŘ|"σl»3‰Ð)ó똹ÉûqÌUŠ^¾þ/3à¾_x )?@àk üg¾‘à!øSí[S«êû‡Q¸±?Y­'ã¬1m¼œ‡s?IŽçI2oF÷3?%åq\™dŸð˜ƒª¾oùi²¾0æ¹v±Ö>ˆå_Oÿy¡w¾÷Œ�BûcN~Uö#èÃX‹Ý'A9¶ÀÀ³UÊ'ð)ü~—UЗ±s ¸WöøÅQ~¾[Øò+ ø«¢ßUðc[“ó5v[ x§ç£•„¯' øíÿüeÿ5þŒòï úGþqã ø¿H¿ 1vÜŒäxÆU¤€/.0ìšèÿ¸ë ’žç!íŠù"<o4Οÿ–ð» <ƒa~U¡Ç=Nð ?†ú3î9þ#¡?¸ÇyÜq®Îö2^ø)£ü×<Êíýô|f”MÄ)ì—ñšÿñ^¿Öhw¼|]ò€òßÿ3)ÿ¨–´ÝñÏ&§güó<ÏòßGôAŒÑþÇÿš´+óƒÈ²Ag‘Èo³`œ[dOÞnQ‘çÄýˆçJbפ_Š6¤ÀS[8òø£±Iìý(ßDìHês ÀÿÅ€ÿ¶†Ðs8E»¯åÿCä?ªà'S”ÿ£Qþu‘?`MÈ7àÏË8|Œ*1àK? qjÂ,Þ·„àù¢H€‚oðmò<#\'¸ ø1qžsçÑo'§Â=¤¼ô“ ç †ÿQ׉<S½ž`蕺^ÈÓz=á'žïÊ8îøŸH¿Èó, âBB¿È{-`ÅD>ÿ,åzZ\aøÿž¿ôÏ7ÊoxL çbþ”ACÅ>ƒþrA4ÅZRù?@Ú•ãeGþ5þ¯ä ü(ÿxAÒóqÅ/9Èø‚rø-± aï ØÇÄÜäý5‘Èí=iGPt¢#Eù*R^Æ诉·$=¯7Ñ“ÏÌðv7ÃíL¼—è¿Ì—ÀÿL<hÀŸ“ãÀßHŽ?ÿâǤ]CN4à¿z…gn&ž6ú×+ôÊKrŒòï »0E�^i”¯v„ãÜ’Õ¤ß5Ñ.Ð[â&ð0·’x!Æ5&x^ò%þk!ŒS%û ø‡2>‚>”¼’\%¯åßô +1üjïWAΓòŒòÇežñeÒüc±Ž0Æ‚“ª’·;éjÿå"¯VîøÍD·¸:)E¼žtÉßä¸ä6郞·D¾‡ò™ô )?DÊÿÀ(ß+çaÀ?Lú¥ÿ_rüþsÒÇOŸç«Qþç$¼d&±S—¡_.A{‘ã iwð»dÑ™þ\”[âù\÷%Íþäû&@/ÙIè‘y;ÄÙKŽëÔÒî“Ëù’ý2Vúl÷w)ÊÿÅ _?G ã¯K>#r“~øšlØ‘z…Œ§`_“IÜÿžÌë�ß䫈<0øšÜHàš€ƒ\'òÉÿ'{y^ú `i2‰;ÿ.ãÒó;#O¨—þ ôhò‡Éå09FúQŽ+¡Äñ½¢- wZ1¡SÊèÔ®IŽ_#ñè99ö¨µòôÈy*·Öa”ÿ·ˆà ü›ö˜?)å~Eû!H¹A¿k' ø‹k ø¢ÿÂoà™­)ˆä|ÈaJÁ#èDûšr½Aÿm"PA>Sjò¿’yâ'zÞ+ü¶øòg”ôÛüZRyŽMág¦¼HüªØ/yÑ”S)ÊÿÕhw–l òßÒ\O»|Ÿà)h”/ˆr°äUºÈ(TΟ�¿¥›’·[ÚDä,ýȹ4_¥Æ8Q'õÙð'S”–ô¯´ ÒžUBrû <ñ«?űKß!ãéo!o*%r/ýä‡S‰]¼4dà™ê"ý.ýä S ýQ¢2ŽE$/ýïO5Æãª;"àLÝIä#õò¬©ß!~̘GšÝèoñÍmñ67³O—ljoj®»¢n^ålþÙø%ËÖ¦º¦`›7TÛˆ´¬ „šÊƒüÓ©Á+ÖzËk•E›‚Árø§,©ó†¼åøeIÀÏnøkCíõõP?1âv»ëüî†fÿ&o³».äÝÞö­üû–ˆ»®½¥e›RïÔúÜ!¿›!q»¯Y}ƒ{åŠ׸Ýp·$îîÚåî¥7,\u­{ѵËV\ þ]•r'~¦­ÙòÕ•;”¶ò ({Ýb½¢ÎP†ô2>½jÅ÷ˆÏ캓|k¹|åÊÅŽ ~qð‹“_æðK%¿Ìå—yü2Ÿ_ªøe»89'ÇâäXœ‹“cqr,NŽÅɱ89'Ç2‡c™Ã±ÌáXæp,s8–9ËŽeÇ2‡c™Ã±Tr,•K%ÇRɱTr,•K%ÇRɱTr,•Ë\Že.Ç2—c™Ë±ÌåXær,s9–¹Ë\Že.Ç2c™Ç±ÌãXæq,ó8–yË<ŽeÇ2c™Ç±ÌçXæs,ó9–ùË|Že>Ç2Ÿc™Ï±ÌçXæs,UKÇRűTq,UKÇRűTq,UKDz€cYÀ±,àXp, 8–ËŽeDz€cY°€k+ù0'»§ß%FýåÏÜú—ãx%ùÍ&,a|KÍM¿lÆm}2¥ßyÇÂ5¦–&tò[–î¶€?„_ûa%”ÝÄ}_ñóO†¹å—ýœ (åç'”}s“Ûœ0º auÂì*„ÝUë–W!L¯BØ^…0¾ŠqÒF+ p pTJ£È…8„8„8„8„!8„U:„Y:æH1‡I€|›Žw„x*Zëë&ë6ëFëÄᔎH´"œˆCx‡p#áG‘8„'qWâÐýšÀ'=›tmÒ·Iç&½›toÒ¿ çÖãæãöãää&ä6䨒îRàfä6íFíVífíví�Ã6TZ( Ýñ|e)t1 7ÿN*š>À!’Cx$‡pIá“Â)9æIŸ/Z~É!“Cx&‡p“á'à(—r³àÁÈÍ?¿%ŠFæÊ¸"Ô!\¨CøP8QfÃÈ¿{Ä·AYáŽÂ9„Crä.É!|’C8%‡ðJŽ2ŒÉ8&™°L§°L§°L§°L§°L§°L§°L§°L§´Lþ]fvévév鬔aR vévévévévévévéœ#£®À',Ð),Ð),Ð),Ð),Ð),Ð)c¸Ä>Æe—\FrÊe,ètÈœ@àèèèèèèè „¾×5Jï{§0!§0!§0!§0!'˜ëöm_Mí�Ê%ìËßܦÖÕ|²§Døt cq cq cq cq cqΓ©à`ž¬/8ÆâÆâÆâY…SØ…s®Ì”>aN° ƈø‡ Üs…¢ÑÏrˆøT7Z [ÂI˸Ab™þõN7~XViõmYÌÃÛµòS›XŠ£uÇv©ÈÂõ°Èh8,Ñd¼cÀ/(®À¯°ãçÝì³€>NÿÖ2qj«ñEXtUR yEk0ä…@o'ÎŽ¬mÜœ$|4wiœ0xãŒ27~hSÿr¤üÃ-Sqš#°ÞΉ÷û¯HCÙEâÇþ+R-ö_îõô¾‰Koð W·þqcÒÿŠÛß\çÞÔMš¤•Hìé›Ö,­âønȱ·Cmty/Úv³Þ3#uLÜvÝÂ[Ö¸—ùBpIÆ™‘òÇgPÆyný£¯KeoªÁ%�Ã0‹F<&Ñ/K æIÏÒ­ȸ9¤ãòÑ“žªŠvùÀ“AãÛåÉ(6KjBfËÇ` û[šê|Bþ¼ÿ‚¾†ŽÄ Þô­âã!È&H1»Û|P•”}l3EŒ}ÞämnºÓ·†}FS JTDY°{“®.#²<ѹím0Âõ¹áh“ÊHw+ ýí"/”CjXÐv‡ÿ¥†~ ;©s!æ!e¡ÛyâH!A¤Lñ]Æ=P<ŒEÒßœó•À8ÿè¬Û·µVgIš‰nž:6ˆ'`K )ä3BÑÂDoG¸4ªƒ"¬‘¾tqš:ÿLXÆí;ÁÚ"p,Õ}¦aW‰ý'ˆà#ŽëĤƒ-Þ†EY_ #NaMÒÐE| ¦Ë•^#Š-ö·vøØ×Äø˜gàc6jà Ù÷cW³Í‰Çco!Jo´˜8<ÔA#øuHÇ B7~ÖÖ·ÕPF^ªw S`-¯U¸PÝú—qùqgxfX¯ÛÛÐð5€*Lƒl&ˆ™þéoóµºC¾–6@ã«u×7º¦@RxK;ЧÔ5·´·¶xÛ’ñ·…‚­à,ðƒÇîFðT(?æ(Bþ6ÅÝÐâV‚ ƒ¦­åÜÒ†5¨ š Pœs+ÊH×<CS_§´xšj¹ƒÒ ‡zîfí 1ɪûZ;Ð-‚XšAþuÌý;”:Y‘Ò$ÆÏu0ºp°ÿÊYºk¯_âvœú«Õ§þÄÜ/Ç'èäß|VO`„Z˃ »SÜ7®Yx÷âÝ„‘?Q *Òâ¶·º1È cà^ù ôŽØÅnƒzEw™îŽ¨ÏˆFŸØ�»C­ë‚ú¸ë›½ qI´LwSµ>˜qÏY²b…lmëÕƒœQ¿|©)ca3'Ð ©Ê0gÇPߨÏ_j”‚1$zœÔ‚j ­XTÈÌŽäú·ÙæÃ¿ÍÕE¥OÍ.[¹zÑ•îÕK—Þxí÷š…‹V^ëVXóÆ„j½àßínòoJI4®—}sÞˆóç4ûàúêz`uëeÙwª•f#º¸Ñ[¶úÏßcKÓˆU/´"M!â`H’ƒ}Œýü=µpº¯ëpßàkh ûlè éô^mÛ6%„__´ízüàv*dõ_š‡ «·µlò7+üSîF&ÞrG >eUБý¥76Ñè-°¹¦tÈŸ®& ñÚ‡^*ñtcj:ÜzO¦³"ô„çÇ€ý†þû:¶¯!‘|¦£Ÿ‡UÄÆ¢–7ÐtwT%œ§+d™¦¥…5+7újï¶·0ngÄs3”sa||âö±ø+¼¤ 0 fêu‚…M‚ „j‹µž&L¹tß‚ç8wÍ «_»ä¦®u¯\qý?-\v­ô*iäÝÌÝuÊ‚\íEî#u¿®9¸­%e•îHÚ[››ZS«Æf?ø·%·^¿pÕŠÅ  •ý<¦ðâçóiç ¬{ÅTF°©¡ÕÛœN2Ì‚¨g„¡v�£+Í«ÒU½†Ër–KU¤Æ±ÀèÂvÞ…q÷UºqœÏ6Ð{¤ylÄFðˆ-þŽ´¸Î§þí­RÓaaz|Ýõ+Ü«[Wú½˜G²,JG\]UœØ˜{Ö…S—Þ£ŠAe¼}Ì«Di.]ºm“dùç.×—ÒQµß9œÇß³þàc:„k­‹§×1Ïð,lÄt~E0&A ¤ŽA¿œãªkNë—YÈÅŽa©êíÕÜÍM›Ü"•‹×M§ÑiMõç±9fJiÈ)¦ \ KÓGùy䪓à¸ÒPÂÇßTõÐJ!k­õ†|ñ2€.¿îæ‹æç!’ãÝRlNÓ¼(ÉþDž²‚ÛÑp~ƒ ê‘ñ¼yñ%ÆXÊh„V]yS;GËaVR… `Ô6ÖùÒ¦R\ŠPqbN ^¹“¹ "¸Pw_²P™V€t6^ ³–G¯¤. !g„ n9¯t9=bV<ò1¯l¢ééòìÀaXp[U©ÍŒ™.“¤­±žOìϧ z÷3Oæb¤·iÛcE2G9/åéž1Ñ’VeY2Šít,p7{·ùÛCn1f¾!¢t)Ö3ÍQ\eSgÜØÞºúFmn¹£B[æƒ8ÜT«9œsæUο¢¢R»Ñצ9+*¤,z·µÝ؆…æ~.|Þà•)êpTÍ«šsEÅ<­µK«˜?»Â1ÛYᨰ)Ë/¾R+[výM3´9å•ås´2ðWýÍNGÅsÜ•Wl x[k/ˆ;�ÊÙ8ã"ú‹èý?@Á“m®üõ¡-Þ€—óâÜ vS¨©Fì¾ v<oöš‚ÚJÈöµkëšpšêJ†ù GùÜŠ E)oô•òºm­8l`×P@)øš½å þø‹Íð?}Ü…“'øW98fÈÜ9°­*²²œ ›ËÙpKî¡ÃF¼-MµJ9›6ƒ{~¹½Æíwå@JÈ» ®¡�»‚[Á9F(éÛÔÞàönÚðuÈ;JÄß0ªñÉ¿•ü¦­}´é ê( CŒ[Æ3ÿS>6ŠÖÿ1?|‰€ø~”ü•þ*qÍxœ<ßÜŠß¾À÷¦æÄ×—ß³’W;ž§0‹¶h}Ü/š¯èß»RJùE~÷J^õï8Ršåo¼b|r¿ÈïcÉëL -/IûSFÖ—ßÑ’WsŸ¨OÞáן5²þš¾ø+û† Ö¤þU#ëËïrÉ+žfõÇ&©_3²¾ü~—¼²ïXbýQIêãûݤ<Å÷Âä9zž îg!{’Ô>þª?3Yýú$õ⮪•¼÷,Ž~ü5“ú\ÿäùýœÁÔ數_Bê‹ïlŠs¤ª<O:м"®>^o"õùwÕäy,UžË²Q~IqESØYaÉ·l_œ“Råy)µF1l„¶ú8}$ýâ{Qªün”JÞWw¬/¾+¥êß—JÜúMëïLR_ÈOÿUºúŽ‘õÅwªTù½*sÂ÷ãêß?²¾8G®êçÉiùÄ{W’úËE}y>5œ¦~/¹—õ…ÞÊsÊÖ:%þGëã{eMJ²Ÿ^žó[5$ÔGûýšbè†_ÿÏBþS”ˆ™üýÍ„{ãg²¿Àÿ0ß•ð„”W'¤lß4Æ)ÊLMUÿÿPK ����v©Æ@���������������com/sun/jna/freebsd-i386/PK ���uVc?[¤w7Ù��E9�*���com/sun/jna/freebsd-i386/libjnidispatch.so̽|Åù8~—œpèÙ;lÄ´M5Öô]Tl‰RM*Ús!`¢—KP©!!D 1¹#`‰‚{'¬ËÙ(`Qi‹5P~›`šDK5 bl£F¥°×¤züûyžgf÷vï.õûÏ7¼–¹Ùyæ™ç×Ì<óìììƒÎü\³Ù<ÌÄÿáŸÒÆ«L¦1¦%›éþS*”Œ4]bJ2™MÆ¿‡/4Óuf&Vî0k—kˆÉÔ¾É4„—' ¾$³v­<^XnK€«ãÞ†´ðß>¸Á5®7áZÏïoÔѲ˜× ®"¸Êáj…ë6¸VÃõ*‡{®{øïÑpm‚«®R¸jáú\OÀu‡YÁÓ«áòÀ5C׿4ž> W\WÂõ \søýÍpeÁõ+¸Þ€ë/p½ÃËVÂu\«àzÙÿo'\þ{OƒK„ë¯pÍƒË ×8^ö<Ý�×p=�×sp=— ®àz[‡ÿy¸øï ¸®k-Ï¿×dþû¸‡k\ø½±p½×y¾‰§÷Ÿ…§peÃõ\Up­ƒ«®×áz®;ÏRÿÿ/?‹È§œþÚ÷®È_‘¥ûýK¸FÂõs¸.‹€ËÓý¾ ®[uù[ÎB—ú÷k¸ŠáròüžfÂU§Þt¸~WîÞ]<½®ñpUGÔ©„k6\ipýTw?®2¸fñ| \“tå5x&ê~{áòóßct÷k!\—ó|=\·Ã•×oàZ×Ý:ø ¸n†k \KẎߟ—Ä?ËÓ:]½\žþ®ßÂu)\/Âõ(\Oó²¹ô?×ïáJ‡ëpm…k;\[àú\Sá‚a™ÆÊXê•ÈGèMç©ùJ†«ùDJÛRÕ¼…ÒõW¨yV±ÿçj~ûñj~(¥8G°¼•R9¨æÙtrTËŸÏ~8Ì<¥S†«y¥8°ü…”îÕò? ´GËÛ)ݯå¬}-Ï:BÍ_dÒÿ%š~‘OŠÈ_‘‘¿$"Ÿ‘ÿQDþÇùŸÝ©H} ¨`ò Öç/yŽ•º•Ù$æ¯VVe}óïEÔ_"8h2uñòXŒå?ÿ1ß *q€QO`ðv0‰¤{ظù�~áAf˜o•¥À ŸÌó+#ÚTfYÏæ,ŸÑjBÕïpÓ!åoðs”ÍLšö€ü2]ÞQÿóˆöÜ`‚Ga¢þ/o¯à¿2™¾ãù‰ÀWF>ëû˜¿øóÝÃæÌÿŸÙˆïZh¸øûÙpV~qDùLÈ7ngó Õ×Ñ“ ×G ››ÅX~�èë‡AèÏ?]j tAžOÃ|E8áùÆö�ý£Ül¾¦rèRëarþ”ç?ýŽ,gcæ ]t? °w›Yþeè’òýaü퀯Ïßòò‡@¶élÎÁüôyÏøM ¿ó8¼ èKÒégNü‘{»&¢¼ÐO.‚ñäKàßV¦ïhov>ó'Èþ€¾}¿†|þ=áünH—‚³•ÓwÒ10üœçw¾ÅlîÁüa›‘ž- Ÿ]^6ß`ù€O'ñ Þ^M0Â/†úY Œk9þW ­ÔÉ# äÓèc¶€åßBºú7Ì·¡þúØûû0¿¿Ž° àëýM˜Ÿ€Ø÷›0ÿ"Èwá æ×bþ;ȯ*g>-泡ÿ9^Ûç  g•޾ Á>ׂ³ø^þ[È÷>æëuŽbsæÍ ï½n6_c~{Dð‚üöÄwˆ—¿c2–÷€üBÅÌÂò¿!§�hEË ùµ˜/yèuéèm°§Û"ìé¿ Ï–…ay~ ýc¶ùa˜êÃxôÏÏù´¼Ì|cÌÿøkšÎüÌ×€=æÍ ãëz&?ÆüÌ õ—í7™Nó|=ô·ÑsÃú±B~@gÏ_êÌpù¥çé_øÓÀA cå«A¾:þ·¿.>9`¼ôàøøoæO#|Ô_¤ƒ3Â^oxy,[÷ ü�¸âq¶6¡ñô× ™Ãª~ÁÆ<ÎüRÌ¿t¥<æÇ!ÿÇ¡| ,¶þÍËÑê¿:<^¡M«þÀEàÌ€ö·mcþ'–ßy×6æÛaý¤£˜…ùý@ªŽ¿Rø±­;yÿ8€ò¯ ÓW ÷§èà‹ <gN¸|:ºÀßy~4ðo­ —çFð·êOž.o�üþT÷®[Ù Ë[±½ê0|?ØKÖ¶^$z!ú›ð|~´Ÿr_~- öÀì–Ÿ i,¨†áò…öŽ„Û;ùµ:z�ß?=,¿´'pþ×ññ첈þû(ú ØšË7DØÛv¨g…Îy9—7NO²nþî�øþ‡á?{°ÂÂ{:‡ÿ;ô¯…ÓÃóïé í×|ñ¢ð|v%ôçnpŠB<¿ðäèø»ô×¢¯¬@ß”[ÙZóé@`“­Í1¿ û߬°|¯ù¤ÎÏ•ÐÞ®áñ+òàÔ'rúE_îgk,ªa«€ŸÆnøq§çÛyaú¿„û•ºþ6=\úFðïy0ÌADù#ƿ෥€­ þ'Ðþú—X‚ì'B¾¸Æ_wu¸¿†ö–=²œ;nW0áSÙøüÛ³Çå—Ýž›;ÉY\V<.;ßYf*«˜5w–©¬lúüiøsÚìY÷—CvFymùÌYužòÚ²ŠÚisÊËfÍ­¨†û1ïNœWVÄïß2{Z]]yivõÜ™UsjLžÊÚêúì·°©¢bVYymmu­©nnMí¬¹ž ÓÌrÏmÓ<³æ•›1£¶¼®oLòÔz§{¼µÚ½ºY3.S]¹1Î-¯Ÿ8mÞ4€š5w¦©~zÝìò¹êÍR~—g]ÕÐHy­® C |Ôy€ÃÚSE}í,O¹iNùœé5 TÀ[¦Íž}Ï´é÷†Éãx!_VåYPÃxÁeuÐÄu×R¾¦¶¼¦lú¬ ÊLF¨ôëùŒp¶†ªÝð2¬e\^ÚýŠÙÕÓ<F4× çgT{ï™]NdN¯ž;¯¼¶nVõ\¨4MG:h®zN™ZïO€¶g–×C¶dÚl/Ýã¢Ã{ªHRa*',É3‹,¨¸²¶|ÚŒ°üŠyS¹¹ð'^ÓjjÊg˜Êç{j§M÷”Í£–+¸]€>@Ý&O5«ŽŠ)«¾§ª|:0äó»`k' ¹ÓXÒÄ1¯zÖ ­¨–ÊP½ ›9euÞ¹eUs§•1T`Èóªï-W &>ÔíŒø@aAŇËa*‹”KjZ< D>ôɳ€” Ä™:õ”³ŒÖEØM2wÊV¹¢¶<²*Þ:{Åêèß3fó„õ‡A)œ]]WP,””YsgLZ0çžêÙ�Z·`Π€dñeeˮ˞0á n9'¨œs‚Ê='¨ ç5ñœ &ʼn•Ï¿½à²…ÁVai@ñd¥Å•ORP<Ai@ñä¤ÅŒ5Ù <ƒ[Ž™•Ó·ß²ôðÐIάZ°©loEÅY`ã08(Ç]د8zeum\|v f‚‚ò9Õµ âAÄ•{ÝYä^wv©×U’ug“d]|IÖE’ug“dÝY%9ÛêéqÇèÁ±Ã´^]ž\ÑÃãþ†ÁmÉ0fõ^—¹+ƒ2³<rêâYJ) DÕõyÓæÂL0:~7â}„úÇ9(×U[íxðHfÕ'§f¥µÎuVÂZ^W^;¯<_óTâ0wîÀh*ç‚‘ý*a~`<Àq® ·T–O¿·Î ³ámÊnŸ›_ þ[ÕÜY3fÕÕLóL¯Ô&jRrÏ=(Þ‰7 {çªk‰8c.((÷TVÏ`n5ÌìàT•1C{Ýü6Þœ~#‡:éäÇ… ´éèÍŸ k>>(¹;¨ž™¸ý.ìT Ä£ù¸êzºõôÊå5Qâ0ÕxÈ/›ãõ”ÏGáÜqË;—nÖ-¨ƒõB…iÚ=8¾AHÝsî=Àâ àgÍ¥uQùÜy¦9Þ¹s¦A³àêÎ> —Q”U©˜V;³N[ •MºcRI¸¬vZ½Aiä¡ã=TLYùŒiži°ú¹§®VjÓ€¦²ò¹3è¹>²ðÔÊSO<Mâi2OSxšÊÓ4žŽäé(žŽæéžf𴆧žÎçéBž.â©§KyºŒ§<]ÁÓU<]ÍÓ5<]ËÓu<]ÏÓ&žnàé&žnãi OÛxÚÉÓ]<íâé^žv󴇧½<ÝÏS™§ý< ñô(OxzЧ¦á\/<µòÔÆÓTž¦ñtOGó4ƒ§cyšÃÓ<žºxZ¬ÂA»æ5&SÖ³i¦˜zÇ<¤9˜ôˆyHó0?ÞdÊÇôÀ‡é5`Xi1æ™L“1ý#àÇûNÁüd°ÌC:󽘇tæç˜L•˜¾ íâ}Hgc¾øÃ<¤)˜¦ƒ0½ä€iÈÓ)@¦[>L·ƒœ“̦¡) gL/9cŠœ.†4äŒéå gLrÆô °LG‚0½ øÃô—Ð>¦¿‚ö1½ÚÇt ð…é @¦@ÏhL3An˜Þ|bz3ÈÓ[@Θ:OLsnLó€nL'€\1òÄ´�䉩 äˆi!ÈÓ"¦“@n˜ºA^˜–@?ÃôNèg˜‚\æcú;èg˜–C?ôú¦3¡Ÿa: ú¦UÐÏ0½ú¦³¡Ÿa:ú¦ÕÐÏ0­…~†i=ô3Lý Ó¡Ÿaºú¦A?Ãt ô3LEèg˜JÐÏ0]ý Ó�ô3LWB?Ãtô3LŸ†~†éŸ¡ŸaúWèg˜> ý Ó硟aú2ô3L_ƒ~†ièÓ·@ÿ˜nýcŠ›À>†‚}X0ÝúÇômÐ?¦­ LwšLB¿MN9ð¿ýdÚløÌFiÆ@£ò¬•Ç•g1âŠ;2LÁ^þžÅh"FKMÁ.Êã³òJ¼l¡<>®Äÿ‚M”Ç'>• ®¡<þ¬Ä0j°‘ò¸Ë£·\Q‹*ñ±s°†ò¸Ë¥2 óS) •¸¥%è¢<F”+ñ1s0‹òXµÃØÁÑ”ÇH•S1ŸJyDU‰ ”ÇZ•¸u$h¢<¢®ÄíÁÐÌãŸÊEÄ?屩ʥÄ?åqWLe#ñOylºrñOyÜ)S¹†ø§<’R¹Žø§<Fl+›ˆÊ#i•›ˆÊãN˜ÊâŸòHje'ñOyŒpWvÿ”GÒ+»‰Êãî–Ê^âŸòÈJ¥LüSwÑU†ˆÊ#k•ÄÿiÌ/#ý›‘Ê7’þ1ßEù&6²ÿ”_EúÇ|åW“þ1¿†òkHÿ˜o¤üZÒ?æQ~éó5”_OúÇüTÊ7‘þ1ï¢üÒ?æ³(¿‰ôùÑ”ßFúÇ|*å[Hÿ˜wP¾ôyå;Iÿ˜Âü.Ò?ñOù.Ò?ñOù½¤âŸòݤâŸò=¤âŸò½¤âŸòûIÿÄ?åeÒ?ñOù~Ò?ñOùéŸø§üQÒ?ñOùÒ?ñOùS¤âŸò¨ÊJ™ø§<>i® ÿ”GÕVÿßaÞ†yÜ1ì¥<ªº·»(*˜o¡<ª¾2óM”OÁ<> ®¡<šBåHÌ7R> ó£1¿ˆòh•˜¯¼[ü÷$¡ß‚B1ݵøüÿÀð¾µcò– [ÇâÏpÈ”æd€–¯„6‡$¤@N2ù[<—ˆÇÊY”®ðmoŸ°5‡*_•‚ê¹dûiªÝ.`féw§Þ}'¢ý]A)·Î š?ú­a!üv©Μ!ÂV”¤És©°u*^LàM—üƒ Ì3Q؆Kr[ÙÇÔvf`;æyç 4TÐ&\ò½'êBÒ©Á ˆ¥¡¥Š+gòÒPÅkƒŸÉ€Ýß"¶{.qá-G Ü˸ói(þ˜‚M—Ü]-ŽI‹ûQÃ’Ó¡¶-ËÔ6 —Ü6ÏmµYÂ@‚§rw~<ÐpØ('%òÄ“ÈOJI@˜…e¥r&Ü”½ën$Ø Q&½~‰Ç�ŶÞHC … P…²¾­ÀÀ%¿÷1^Q>6•Àñð#½Å¿G*°z†»$³ÒìK4™Š •ÆÈ Iê@’Ié-j­áðêx­ž»¤!JóR¬S¢4¢ï¼ÃU"O;…tÜu7ÒÉd#ôkRÙsÐdÛ%˜ —QEñcÖœ¸$:¬Õs>qI<úÑ�U èq8ÞðÖ2~ «‘X…Fµ­$Ä_¤68 ¤VV%8¨Ôò五fÜKÊ¢4¶1äJs1Þý%Nª…‹¨Ðákñ\ wQ’³ßþüt†$gHG¯Ò<YC®5 já.‚¾ÎB¥yJ4€÷ó"¥yê Ñ‹èû`žÀ�äAÜ‹=Bi®Œ|+ÜŸã~t¶hhÜÞ€cMòÎï¨_/œ1ôwÑЕ;ÔáÄñÔ$v &QjE¥:ìvÎ.EÐmš{¡Lñ†JU ’üe»ï¸ýªy¿Àµ�ÚÊ”c¸m†ÛUpK†[ˆ´z.”†J‹p÷¥ävˆ  ?°™º£ Š­p¬K¬,+Ã0!&˜½€¢_³"g²Ö®ŽÜÕN¥ò Üà¶è?n–!Pc s´ßÈ‘Ýwˆ¹²�WÁŸ“. Ó”Áè#i‡¡ïO*G¿'}r$}<Ãè³"}þ3H505š>ÏMT2›6?Nô€43 ’“‰u1U/%XyF ¶ýfUꕱ„2S-E£ö,£B[âÿ ÚùÊÑH¡<tš %)‘»]ÔÄ©ÿEæMôG6qoé^² SLí¾ç@1d–ÿ…Í¿iEÒðô)FƒiOq6gÇRœ˜°Ô1…‰g8 Iÿ(DÓ‚®X÷�¥v߉ï…ÉHáAîÿjsBÁ¸BKÁ›=4Y•Çpk°ê«†¼Ë�Z’úÁœüïŠÎ£0úy\è³4/ÌU] Wóp«Ùä;KªÎ—Ë(¾+ó6DKš|õaVA|+[ìo¶ˆÎ$ýnÔm@} ç¢K…ÞЕmOš]*8 ž‚p $™KŠÀQ¼V˜—Ó€âô=B[jß`>·¿e)‘ B¢å±ìÝŽ…!LÌá ÝØ®xÊ}ßr®Ÿµ8”OÑÀˆÒŒ¤/Q”»'+ÍH9°±êó°ts¹A.C{#åâ]`à#?Í.|JßÓgk”¼!ÉòD…h)d´¢¿å>*%‚cÅØ+RsU‘�‹ƒðqESÃ|„òÙa£BÞ?­§Bqb÷]Cnp!ü§ÐÇ ª?¬PcÔ\yà3“I(ÉåY›À|%ìà…ˆcÒpRnLT'e'6æ ÑT½{]G ËŒÄnW‰ëö\~º·¿­?$ |©‰4b¶[2Pr ¸¦èÆQýmìDÂz&Ú6šþå÷@Ûé-}»cÌù—Fò±þ,|äEóañ£+ƒ¼úFÇ˯]Œ 1$¹ûå‡p`qöwXpñ‰¸Î••Ï ¬(‡+ª? #4œ—¢OU£}¦œw—:¿áŽÝ¿Ò¤: îE ߘì¾óÍÜû”{¾ jä˜.EªŒÖ}Qj°I–?’}g^x¨ü )¡¤Œzž!²Yµ2³. ÛµU¾•Ù’ÚŸ¼3t®¥Á¶BFÛ>üM´mïøo\ÛF×.8?¶_<“ïÔlûéoP! ,>âüæÔfŒî~õøn¨Œ‹­Š”P |¶$¨Ãz»Ý7½7­ªp5Ue‚Q¬1 ÍÖZm©°ˆRš›phoÎCçTqÕ€ŒpíE‹«àû0+hýÉ^¦Óª*ð±!ä‚Ò-»#'Ò EU&X4¥}MœƒÓ»N8ž`VÚ/ ǦEö6\Ùõ—r+mç½íöÿj*{ÆÓÒæ™4T„e½Mn·Œæ½#A•UiMÐOq ìÝà—ß‘¬5¸¨îW*ß"›máë4•Ûgÿ‰&èH§�¬P¢×¥ÍQ…Uf.,ÅJTp€Ë©@¾4W#4Áåk%JQ~XÓÝôOuMw)Š»+}ôƒ"@LjAa 05<âL’¼Zçúö“i©ó,*yr© àðÞ<†ÐÌÇACiÄ Ì’ó(‰ØûsIÀ(•2º*µÊ2¾ÀÅ}S#�ñq²�xå¢,j8b¢I#BZ™‡òu¥¾ôZ?­ƒaDQ®¹ä›û@"|bq ÒßûÚÐíþWh8°–HÀÁXR9aWÇHQðn…↮9³ßØ5Kû£»æÿ‰Û5¯Ç®Æ íK£‰…—ûTÃÎÐÑòÄQ6N'Eôæž>Coþ«¥°~©ÿnUÿ¿ÀI‰|Àßy‡4¢Êì" æ}d£usžT`ÀuècàzÄí@t6û›{¤!E°öÍ×!!3Ú’a´Nï/�8¦QÑ>õ1Í&ªíšÊzðÅ…Ò¸iÑßô8¶à ¬óuẮˆ€¬Æš.ºi3Ô´S¥-øn,Õd0ŽX“ ‡nÁ÷Qú$V–«BŠ¡Âù[pq°×íTœ‹¯´ˆ:ø’fð ]z,«QuðÅŸà7gÔvFǪ3&¢¾ðܪÕɈUglD|13¸D«“‹Ÿœˆ:øÈ 8‰–(CØøP Å£Xq*…ênŽ5ÿhðå[Âκ६!66…+qJó"#¦ó·à •Áÿœæu–ë|A7ó7wÑÍ<ÃÍ-8óyß>sšÖóè«èÇn]UèMìpî'7iåH¸ÕáƒñKÌû”ÍóÒJª‚s£jà›÷Òð¸žM£7™q@Ùÿ€‹7)€¿–à¯Ì±L�¯ŠgqÍ�·rÀ'é ‰þ‚ „a‹Õât—!¸ù¸Ÿåˆ+“ñ6À7uÅcER€(4—‰°òØ ŸÝÇÓ’Ieõ¬"ò/@koŽVïÀ@ `Áð? zkÖrFðƒÄÚ·l‘ID÷Ѳ¿q;ñ¿¬ÑlD úÙý%0¯lc2éálD(˜;œ›D¿bÃwÿ›b©0¼›÷áð¾ ‘W¤A#Ï覻ÿ½é[å±]8`¶û/‘jcáOR6¢.•ˆZNb8ŽÓ CLj?øŽ‰áQܓ̦‘ÔϦ‘T¯®Z<iú±zôE) ¯*.?@MˆÇÒÁ¦ögv4\•~¬± ¤„JU<#,ʘŒ#¥x„u<À KÚnËìXø‰DMÂDâÖÌÇ@æ‹èÔåˆT—÷6]ij´2[LeÎæÊ\†ª#MV*‹ÍüY™¶’Ãú4Ø¥J©ÎCÝì°œ¡^!ä fÁo Ó±`·ðCÔi`cÒÅù_©ï)_„õ=p€ÐA…a“£8ô?m2ôÈh ÚÃù¢çU×àó.2Ï€àÃö‰*F`‰\û%ÞÇ€<*•ô#nDc¦Pl„ ÃŧR~ó¾°dÄ…®á ãƒ3û[úžDö^?En@Ø%Ùû•Ñ%iû*Ú%iü"®Kò%à ^<ÊõP™…炟ÁMyæWì)Ýg>M¾lξHî=_ ÷뾈æþ}Ši2e•t¥‘¥žûtÚXÈ…²J³2I¿?I9üËwƒœÏAWh $¼)(Šâ3zV&ÿT&9$ÖåàM/?…Q*œ¬(r®ÖéhocµŽœ|øý)ãøÎÀuM¢ P_ 7ƒKP¾Fä ‰Æ;ÖEDËi%ë' £?8id2·Vëc  i%Ãz’1)–'À¶XÏÛÌ©ÉP+ØiDÆ¥Ú* Q†.~Žª>÷ƒfñ`"W$Evž#æBw@OS@Ïéæ�ÜÑÈ0ÂN %='kç~62,Yœ‡¬xƒý´¨?-¹´†FTEÊf3Ë »o2=(ØŸ%:÷—ž`+5®!Ðk!.%'ÃaÌÆ¹¯PyœG1Ó÷Àh~1ÔP»n3ðoßÑ…LîYÚåùÅ¢“ùžŸÙw´rvúa ¹Ã¿ÿ óê+$Ÿp–Ùæn¥WòócØè§2Ï ú™vÿ=€'-¬ñy#QÜ)™Ù£‘¢¨g GŸ4 ù ½Dèm›¸<iêØNàg€ËS³˜‰P,^lÁ_IYÒFÄš%z³¢¸î@·é&»”/ÕÉ.8ö„:ò©…ùPØ÷E¡‘øžâ1\¼YÑõ7G±ÝÑpû#àè Ê×g¢àä8z’Ñ Š€£¨÷Ÿ£áú#à(B?/îh=,¸íŒêS ýI°ð)…õêþå=œãîüœ=¨Û‚‘4ÏPaà*-}Ox¡Sío¡'ü=®hÿz žUâÝËÍŇ•Þu1¼â-xÞ‡÷Ñ>ö;yA5 w±¥?¤Ò"µt…±4‰JÑB £Ýó-Ã)ƃĨ‹€·f+‹-ïXÜÚð.>j·Õ_(ùOAêTzÅc›Ðí‡õߦ\žæð4›§·ðtOïäéžNd)Û»`ÕÅ .„ö0,ÐHÏ,l.åqí‰9£¯dè4©Vèé¤gÉÍ+Xþ¸œ¯?5® Ïèq {BŸDOè³øz¾îWcAëù9êzþç½Re.-Ç^~¿Ñ£5¯‹—:pÛB½‹G}¥!w¨*üLýÂNZÈñçA~<<’U—R”?ùNÊ(õÅ›!vàJ¥¦R¸Á€WhuLp,C“¦¸’tq^ñX¼”"y6Å’ÿöOŒ+†¤óÁo¥Kc©Hy¬S}n€qåµ*nÅÛêYÊ‚~PÒ÷ˆoš€ñ¦Æ/qÁpawi{ÁbôE ‚ÿ¨ƒQÑœ~lÄb!:½GØÉ¿Û™LÑìÄ‘`€gñMèïjæv L'ûPÞRçÏÔaL¬Kè&ôËt¾½ävt`Ÿ5úûCØÂÌX-îÏà(±€Ô< q]VàÍ}ÏQ/tDÖõfPÝÔ(œwÐýÙç2=ÎU€±o=K•¼Ü§+¿æE„xÛÆ'÷ÁÿÃqÒíXšLQ‹ä¨G³•¿º1D‡0]âGðz†¦0îU##£ð\M÷=ºòP­ÄLÃ;â"¢;ÃG¦ÑQ(.g‘ ݆…Xh NVÛΈªx)‹nðŠ«ØÎ!(ú$9ø3µVÖ µñZä_ÒSÏOìW£ó£‰£,-‘Ÿü˜I°¢Å˜ž|Õ>l>m ¥è*ÚYÅÆn{¿ÀÅ Ô =ØcÔN$'jX1<î»Ú)§Õ±ÆÂðxöá;82Ùßô&{Àž ¬8¦ó1âô‡ìoî“ÒpÝíuP`ö1"45<Fi1ß*—Öì²Ïá•» ^Ï+úEøËû ÓÕûŒÏ> ‹ª÷UÕû¢U7î»¨ÊÆùl²–‘îqÛk;QF¢ó¨ç&ŒÆ·J0ºav¤6ðª8qOOéÙèÌô’j4ÈɆVyƾ³Éé¨Üò±QN¯éå$|d“ç£8r*ýÈ(§ñEËé‡þ/rbã¥}‡Ó&ÈW©òz°gÿ 1.p  , 1¯Z<–Þ¢f˜^"Oß Øw˜¥›]Jó.zÅâ¼¥5Zñ[¬¸“Š“yq_/M©Ê}%]iî¦2í¬ ·•ô‰YòiJšÒ¼— x µ¦¯ªB½ºË#¸5Ö£ÝFÅ£ÕJ/Øßf¤´ÑýTNJ=ªô!Ó£ò.*·ªå×ZkaТ螧%c‡á²»¥#r Éènæ¡zN@ÿsK‚¡œƒò=¨$|š„S_ªfÚc€ðÞ;í™Õ7oƒ…Ê?ÄàÀw±½ŸZÿ7_þòCz<(:&ßÅê–L:`¤Â½ü6ê4uê&¿NØpHÕ)[©†(ðÑwˆ-±û‚,¸Ô÷…4¤HÿàIsyÈTÅbèîwƒe4hàò¯ïu¿ê{‘Åúža¡É¾'Y$¡ï1uÿ¤}‡ÛÈÞ¡Ò™Ht&×_�.<Ì(tñ ò%»ùÓ:¤Ù¥z Ú~QÜ‘ £¾äZ¬ÒE°œêpö"ÚgKºÉ÷*°‰nŸäNêpâi“è^$pcB‡³‹Ê³ÇªÄÌÛÁ: $*M´´~‘Ç·bðÀ°³7}ÔÐ+vj &yYjX$Ý®Gjè»PÁ"©Ð°¬ G“Tú1á¤Itv÷ åTæ}–y¿B”ãÇJIM°ìü4~ŒdiªæùÝçGùŸ¼¸~¯ä[ÞËEÕHàYÂÀp»o Æu.¶ûé‡Åîû2A¥}ذhу.X-|ËÖì°Ä·ÁµH©í’磸݋иaLú�­Í¢ Ð^Ĵν<:ëìRC³€š„¾Hd]\_ŠÍ8¨-Äű­)D'ãJΞ€%ZïfI¯yg‘4\rúäŽ}jxa§Ýÿ£!8ÜÑ*›Ã%06GbRî‘Æ[ÒÁ;vP&L¸¨þfh·‘·‹s´li’p1Ÿ Ão¼áW¸�Ýʘ7$w/ü¯x}JR´ dÖ[ê_¦º{ý-Þ Z_å¸ÈTr`ZVœQŸ7jÃüêåî2m˜|Àfu\ü7¯]pí…1Á'/ßmUâø¿ÇÿRÿ?ÿ¥8þcüÿG¼ñßO‡T¥û.€Œ|ø}5¥ÿ ú÷á{ò;ï«1Ê3ºeUV ai©¼o—>:éÜ+6t?ÆÍëý©ZO°œ&¼R¶M2 -¬£ô}}Ú¹ÇlK¸A »˜€|H¯³WÛäWì¾yä9ø0pÄ!ƒ’š Ù#dw#8Úš­ïY4‰ó!q;¾‘#úóÖn‘M\™§ ¶· §HþÓG•YÙˆP8X£2ÞÜEm±AXgm¤{šGéܶ^ÙžGñ¯Ô áAì˜5ı Ý l\HahÀõcRLj°H-ÕDmSjr¢{í2\à'D95Øh dSF#ˆ¤/EN/`…àó-RîËÑ�,G€¾O}Ã"¥ ¨õnôÁw¢êU¦ˆúvQý?!ê*“vû§ìöÃäTÐíàl Ô½´‹‚¼Ð]{a —2°c¸IÆc›¤ìTÅÓd•¯{Ÿ‡]®²ûWàbÇ•ñx+j«¸‰~a_ÊL¨»Xµ n:’{¿hyÃE–“j—²­d+Ð äÄ÷°ió1f6ÛORëH+‚ ­ÿ|µ¿_JzƒIà©hKþï<d,5d,Vl¤€Q6ŽQµ%ŽF!2,ö‡ß' ¥±a%¡ ê<‹è¦`ÔWjðI—(ÍkT8et)‹„"¡cßw"OÛ?Qu~ðÒÔ«Ž7Ø ˜¦Yr÷°¨QÐÍŠEÿ Zñ ¯ ÷Šä­Äûb‚hy Q®ˆƒÒ…²2 %ýVVª(û^ ™) ZM‘…]WÎV‘ïR˜ á@’¸÷|“€oç :,M|æÐ~nGpÛ@$_d·ØjzN'zÝáü?˜q eXÞ>8ÀÚÛ„¶Ë4ÓŒ¯J126ü“¬¾êL”½ùXþOG‰Ôê Šw”iwnƒæƒeÑõ4é%GI¯8JÏ]Y¬Iï§gÇ—…or>Š«®œ¬áûGúQø¦D᣸ïÊ)¾†8ølQø¦Fá£øðÊ©¾køÀÊœ ýØ v.µð.†Ÿ>¥½‘ —à'0nU™JÕ`C; o¥©å;ÍØ›Ek°ô˜aÆ‘$ÍwICÞP¥Åd6iÛÈ®Ù@{òØPö81Óf8ÜÈ1´Ê„{ôÔØßÁ¨Ø+Xúu¸  Ir(÷±P ³? ¤Øz Ñv BàŸ‚Kœ^äÂ6–Xq/ÚTŠ¡õª{žFãîDÀ¯F2[Ñ:Ã~Žè>ËWèKëää  Ñ15yÞ*\FËã;h4î{×±ì}°$o£áû mT#|{;ÂËI­ü—Xî¡ò ¨£©Úú£ûwH ¤¶½A[ ±š7Ãn‡âM–ÿü)m¾ÞÃïÎê-‹¨• ª\Áêñ5‡ªçX<�Œc´; ‚Å‚ŒÝ#Ul7¿[òzý êðç%oªç¢Û~¦»[tÛD¨ìM"ÛWC³Áïzˆ§€°ö+>8EOÞ½ýòâƒGÈbÚôïzv&FÒÿ< ýTOd½ToTd½¹PïC½€{À¥Õº›j¹"keô ´“ñHª5%R7 =LÆî£Æ¸¯¡nÖÙbû¿ø‚P}¦`Ó™BK(�aA/Ç:īȿÁ"A&6Øb6±M‡›Ö—)Šª€“0]ÄE¡¾üv±#}Oz‹_ñ\ ¤TâÛ4¥òWï¨k—vO ÿd+•ÿô7戅øó&òôò¼Vr©_ê¦7¡¹€_ÆÜ¢?>G¯o¥m8ÊFÌ„®¯x ®Œaë[Ã÷‘.îÇÕ™JgÓk& é]¹“Sâ=?û£0™®ÕÀÆïpcˆ‘ m5¼B‰Mm‹z‡A“Úào_C9%£T2ÁÇÕ˜ØAQS ”×I$™¤ñ‡6ÍÂÅÐÝé{¸4*[âJcVKli2l}kI*Q/½^ûF¼nbÚi «…Z£—UÛÞŽ÷ºÉHzý‚oÏU­¯ˆïlÖ¿€#Šú×wŒÕ‡écÚúqJ«!LXЪWƒöÞ­;Uò:p¤q§À0Ãt º­âaa¼»XÌ*,“'^T&ý-F™ô´DËdÝŽï#“Œ³¾”b•Ýi|)%¶LòZ od¶DËD§ÿõƒë?‚×ÐÛ1ô¿ýûð:æxýkë¹ð:åm¯oÇãuÇ˃òšô¶‘WK ^»¶}^G¯o¶œ ¯5; ¼NÛ×=/ ÊëÈF^“wDóºë÷áuô9ðúÞÛç«o»Wïöx¼xqP^Çn7ò:j{4¯[¾¯#Ï×Ïvœ ¯«¶x·ÅâUèOUÙ=¾.üjN÷þwù«EK;œGYð¶·Ã)+½íÇÄî_FË@ô'ü{ægЫÉý—wey¯›D?ÃÝ›asx&£ÃÛë£Âzë _‰`õtÆy÷Ï*³ÝÈ®—µ¢='",àÞ‹îþ¾§ jZ¹Õ¨&ÿÖh5nŽû7g*Æ×nÚjí/·¢O…|˜,îhûÿ_Ô–B[ŒDön‰&rý¦ïcKYç`Ko;[Êßb`ø¦-ÿû|ØÐ9>üü 2éÙl”É®ÍÑ2YõÖ÷‘ÉäsÉ[ÏE&› 2¹rs¼±ä©ç×ÿ¦ýoŠ¡ÿ7¿¯Åç¢ÿ-ç¤ÿMFýoŠÇë«kåÕÁëÑ·¢ymÙø}xÍ?^Ÿß|.¼N}ËÀká[ñxÝùì ¼&¿eäՃ׽¾¯®sàu˦sáÕó¦×ò7ãñºï¯ƒò:êM#¯)oFó*7^óÎ×÷ß:^—n4ðº`cìùP[p|Ædjwï×È–œŽG`6<w¶AL¥}‡ó¸:W„v|ƒQh7D ­ù¸³SÎNõJôó¯ ^ð!‹óx\~¯Á‡¶4Ïk,ß‚‘zß1Ë·½idy–ý-íÍLj=‰<iŽæùí×ãò< yžÍóºfÏO6ã(zMspž¿7ÏwnŒà™¿ì™¾'JÏöf#ÏÊÑ<ÿ½é{ñ¼á ÏÏ½ŽˆÊ2‘B~Ó¡Æi¸å3w‡ÆÉ7F™„Ø3|£hòÀ-”Ày“²-gP憈~ð)¾Ë&¾yÝ(ŸÏ_–Ï˯ŕόAúÁŠ× òñ¿Nº°IDò~äOˆµç>ä>ª±=îœ,"¯ÙÈðÌO´N`´¥ÉÈï7MÑün}5.¿xb½ÁïÚ&¿+›0°xtp~ÿô½íÿØö¯Æ¹ôöÁ¯òZ ûåûÙÿkFûMoÿ1øýáêAm;ÿ°íß½nd~6[ƒ Æÿkü¿ƒÿõqùÇ|°.ÿ¯ùU î †s= 2°?m2Ut8éxÏ•WH¦ Ñy,.ßåM‘sÛÛÎc1mü'¯y¾ Ï¿—çÊAútË+ž_‡,’9Îóøt²KlNzŠ›·¯×ŸC ïÿO†×À©x¼ÆñGç–WŒ|]÷J4_ÊKñK dœŒ‰°å¯DlhŒµþ}Õ(uC<Y7ÿ­7Îëño$g²Ê®åIv˜jYèá¸.uĵ‚²z*±ûKÖe5t}´¬>|1® àÃéà‚hØö²×W^„Wp�4f/Yedö·ô騟ÙÚWŒÌ–Ǟî~ÙÈëO^Žæõຸ¼ÎA^ïæµë%¯­/Åóѯý£®[ßD&Ÿ…ÃÆõF§Ç:’èÖ—ŒüÝøR4Cãó‡¯AÓ‘Dü…^4ð÷Å‹ñø+zBÇŸS¢@Œ—"f[ârùÒËç¤Çé/ùœôb4Ÿ—½—Ï™ƒèÑjäóĺx|V­üŸõØùÒ9èQXgä¯n]4¿}þ{éqä:?ŠËß#+þgþäÏ¿g_0ò·ü…hþÊžû^üå½`|þñBÜçË¿§&¼xNvºõy#Ÿ/?ÍgýÚïe§SŸ7Æ?ž?û<²íñèy†ÖгhtĺsâõƒçŒ¼¾ý\4¯Ò³gŸGbðêyÎÿx.6¯É8S08áM^êüFJÐ6š´=F‡^àŽß,Ü·Ùð F¶í>? Š·ö¢?)ÆG³ÍlMá< Â(Ó$õ� *ÄM×ÙýìÙ½/ž7Íî_A;Ulx¦Ž¼Á ,Ú¶R1"fmä[E’3!ç™kr.]-ç+þWοD9–³m­AΧ™¶‚N¾¯_ï‚Õ4²ççâ_£ö h0ùæoÏéý¸Åý&ã–Ÿ7â–mó&zá4û‰ÂÉöûzád¢'½( ŒÞ§=ì¦'ñ%òCÏ iê!Ä$ñjhe>íÛ…OßÛê³x<1E{ÿí¨xk»€Û¶Í’# Œßǰ(Eü@梀¯*y.ÃE9¾ADïi‡û¶²bïÏBè+E)réµXkØšBç¶YËKpKÇÐ@ó]ÐTQIa©‚_œæèÜÊæ½°$ï';"d¥}Íxö™týçø^oa`ù=X•½C ‰tX‹Ò8߬¡Á£•=W!É V~´‘hÜ ë½pA;}OÀeð#xí)´¢$j{ˆ€™(B:‰ª-Œ³ôÚ ¸k å»AËmÿÊR=Ô*dÛ€‰À"P‡ UEè=h”Ð9é¸ýé”ZNS+nŠÿ<£µ|еˆhyddË# A@È9�öÌç-/_Šg -R6©¼a}½Q¼\œv†ÁÓO²z"†ÕÌo5XŒèöC»EÁ¿æX¿  ä¼ëŒc´Ê»ã ðþÙiMW®ƒÌX˜Ðæ3Þ b£V]ˆUK°j4×ƒÍ ‡Û߆«ÚÌP+¶aÅŸr:—+ûH\âJü|C`9¾—WÄß4ØóXµºE†êW³v%3 ÔP6bí"µY}½,µ^Ö{þoöUµY:3`ùC³:û_ª±‹Õ7œÒšý)5›fhVWûöä±~þïcRF©^­xD”çÇt¼^‘¡SQ‰79 l“ѰŽ|G* aû¿ÓKÎÇÑÆ‚ï·{~ÛX¨Met-ï)~º89MàÒA \Êñø‚hrE¢îˆA”/¾ûTË�|ÁÄ︹×Ì-Xg†9ª^2þƒGÒ|§YðÒÿ¸"ƒÞ±Ž´à<­ãcÕ'¹;ú¸?®Zh¨˜¯V  Á»NªCM§óùpE#±.µb+VŸÔËë­òʧÄ|UZyý€ì£a%þ=†•ᩞ”Jlee¨Æ@T”Nh#à~ê t~ ØúþTÌG¯Õýhœ%¼yà‰I€…úæ5Í7Ä¢ ‘SÐÓ9L¥€õ«Àò+`&.bèºV#§Àú_´¾½a †wGS°">+4iây9K4 ظpew, 0„~4 ËÓ±YsQ ;wÂ8üLVUïAü¿kký£µµ*޶V©”îBLŸ×,~<m(nëÖæW]ûSÕöM_C­އ…•÷µ&,Ñ?¸Ý‚ß 8×;Í©¸Ù^DwyïÇ~KÌ|A~}% <Mè]ýÑqtTŽ#ð®â ¬c§ÍT¥*tR|á*êCàX¼Ù]e#6�ìg%¨'#øñ¨O’N H7(ˆ¸Ryâ“DŒK¡ã|ÔºOÐÑê …Âvüa³?ŒÎop#n'^šKõNÖâ§¡ìÉcøº8‘Ñ÷•¶ç|qÿX[Ø×ë�/_ZŽŸeðïéð·aüvøÛÔ_ÎNÜ•N§ ÓîԀк‘û¤º#SXkF ºÄL{Í[÷1òÝ�ÚÙ ÄÙIƒÄ6§áŸu‡ý:¤¿õ)|©6¼Ñã²ôLÕª¸w êx[4¸—î­3{§A®D)±1ˆ»dož]¿éïŠdmØnq%2k÷}Åp”*%<ú¢$ ÿwwI$ ieÛƒ·ÚH ¸»`œ¡£Jm|QÌUˆ¯»ëœ „¾ftú?X¹1uc'ùêûVªf5îßy’ûêtÚað8ž!(–éP@í#ÚžÛå„"©¤Æï¯uø#þyPØ÷g¨gý„ªó=âVùwLAAüÐ Üßðµ‘ô›¡8ˆgÝ„Ÿ@ÍB^Ö§Åâ­øbð]`ú÷½ã«}ãÃ3L{€•÷2ü´ )‘{WÑQ»Ÿ.û›ü{-w,nÍ�âb\bÌ_½û3?’"þ[ØŒ%ø¢µWÛVÂ÷OþQQî„Ý]j¹Fý)@,]"Ô´S ÚÂׯyIÔYÕÏC©äÅ£XäS Iq4(ý.ñ0;SøªgE]×iŽx Fô“>á’LZÉxᘾ ›Ça¾VãC+ °DKŒâÎ~ì‡0¡E¾'rÞC¸|ñžH2;�›<Ò^Ùfbo„4„ÂMŸDÚGA Þ’ÀòÛ Ì¿Gì²û^…¸€KùFÑ9Ѹ‚¶Ï™Øà,yeÿ1φÓÁÙFˆ"öúFß~~–n¤÷…íyè©‹Pƒœ$§(°¼Êè•AãûYßÐû+õǰx7$¬ÔFv|ÇÇ€ÞÀöÁE‘‚myV)Æ å’rA`ùªOøI±p ç_„ãõ8ð'ıqüiÄq|‡‹ãø qÜ1WÇ‘8Š9Ž¡ÿ‚£8Œã'ÿŒÆ1™ãøâØ}:6ŽÉa·ÄÀ1…㘌8ƒà˜Æ1'Ž©Ç"Ä1aSÃ8–ÇÀ1ƒãxqœ7Ža[cà¨ä8ºGë©Ø8*Ã8þÇlŽã;ÄÑ0ŽÙaÃÿÃÄq¤BY0s&ŽÃXþ|_éÑÝÝìWPGtï*ŒO0‡%ÅÅO>RžÈIP«C+xܳç©X¨©d‘7&yVN^.’§®Ú"qXUòÀ7òv)Þ]F‚¬1ÂS¤=ëb!£’G‰ É1rp‚f"AÃ!ÈŸ G ‚’%()Lм%s‚A‚柌MPr|‚’c”2(A)a‚þƒ TNЫHÐÁ± JOPj ‚Ò%(-LÐŽT»T2A#ã42A£%hT˜ /c4št úÛ@l‚FÇ'ht ‚Æ JИ0A¦žh‚28A( Ž„ ŒøeÄ hì  ”ƒ ,NP:ôìñØeÅ'(+A9ƒD%ªeÜ0²,º»‹TQëW¶xÖO`yv®Î;Ž‹åZVMñv«¶¢‡o!ø»þ#\‚-_¦ƒO‚o#øz‚Žà_ÒÁ'GÁwücï%øwtðŽ(ø]ÿÁ; þs¼5 ¾‹à; þb‚?¡ƒÏˆ‚ßKðû þßß"|Ò§aø¬(øn‚?Cð þG/ºÛ@×|JFt¸ŒÕVSµ›?Åj`5¡ôSšý)Å£ŽR}P` ÕñR‰ÔÔ’OÙ£„eŸ¢Â[0–‘EáZª¶’ªýª½À8Š ½Ž wô¿tó>ÖÈòAZR«Îzªs꼎u–[? Ë,¾‰àOüb„~ðgþŠÏˆùSQÌo¢:¿þ ëe¹ §xÛŠTx}þn‚ÿ)ÑäýŒé¥ô²è3Ò‹+Š´ª¶€ª8šyæsl¦Et÷ño6ªôR‘ª4ó*¨J¯èîŒYe?UyŠª,¢*%Š·‡ÐÑ�)äëYB€VñvÆ‚í'Ø{ÕdùU =ü[øÁFð'ëqGÃ%ØÁâ÷­@ÅŸ‡qO‚ ø ?Gø?pGÞ"ØŸl-ᾎᖼ=%ü[”† xZF`yUg@ k!Ø {1!Ÿú¹jý•QÐV‚.'胇ô˜£am[O°[!æ?èD2; ÞAð+^2àŽ†M"ضŒp·|ÎwŒ®Šî½EÚ`§‹pΠj;©ÚHªöˆ:xíÕS|%Áwü@á{> çFÁÏ&ø|'Á¢ÃŸ_Cð ~Áׯ5»·D›mt<Tá8U˜I®×´5: z>AŸ×‹Ð™=]ƒÎˆ‚^HЂ¾€ ÿ<L|Vü"‚O!ø}Cóx@¸²7bÆo÷L…•ò7\9¿ÁÐw‰áÁ¡s¿çæBŒ|äb<¨T lCÀ%.Å»@ïÔöŒ€y~@¾÷ |’Ã=ÜÞó E¾ ¿KÞ¢{~}¤ÆDÏ”…¥øZúŒêR<×U$o~ˆ¢v0ÑMI‰>eÃOà<÷§ë˜gÊÎü¢þzÉ·‘€²úßõü—‚·ˆArº”Çñ/ÏÅí™_°Ó±æ½GÉ@@ùUF/Öªlyo•ˆøu¯Dí«õjøÄqćï>}ìƒÎÒh¡ØG7`í{ƒ rÃC²Fm ƒÚ¢.…_ñϳ’ 'ù’Úd¤ˆ±NÚЈő¯v–rc5ía_¥Äo†Ñ)¢Þ¤Bþ/<aÄ¡à7` ˜èÛ¢ýxfð˜Rn0š!ÝfIW„¦úóûFÏ<1ïp_JÚäßã9¤T„‚°€-óDý¿ÄcRI¾÷A©$’é(Ýycd„P¦ÌËï‡w(óÆÊ÷ùð ÷4y–™•³ŸóúPž¾x8i,8 U|Π;LÍ@ ³–Ƹœ¥(ºoõNyÄ™¿ÔùŽäLSEö»¹¨l©¡¸Ã¹t‘,ÐÇÛðe¡´"UnÛZ:*D^°7Ûlâ§"Ü»ûšMtn¨*–¯]„EMBK²è\/9G‰ÎµÐ’T0†u®‚uÎ5¬ —Ælš¦¯5$‘T0çj ¹A¨ÔBj5ÅõRüÇ:œ«T¡†+¥Hý*ê*€z¤�ølZšç’¼Û€ÄÖ %rãƒ(ç&1{L ׊{nÄMÒr:õ~\†èà?—¥cžn’܈ ¿„M§]”È·?È4‡8ÆJΑbv“Ñ‘GÄüPUAiø"Áæ§+76lkÈÃ}%Ð\a†T°Z²œs¶XH©`ä#Ñ$ç*°ÄÑÝU’k¬dÙ w…±›/¶û�‘ãÏ­’s ~y]c} “¤ƒ7é\M¥ªøV3 ª¥ «üŠ®î*&9›Z7 (Ǧô‘Œ-bÌÐýœ»•û2/hv¬7\ø¹õ²õzZàÛÂ$uoÏ-‹HVø•dzÄb¦”š1ŠwâÝVevÉ»ÂÕf©Õláj_˜ø“™6WU²| ÈþNp!oS`ù˜qèz׻䱤’Tzöâ_NJÛ$9Û¤‚ ¢³Kt¢_‡ïÊì‚U–XÐ#9ð{@Ý.þÜæñ)ŒmŠÎn’?X„]Õ»An_„ÿšX�w;±IÊÂQ¢äŠGñ§—*^W¡òû�R2ûê—“T˜‡ZksŒ§]+à,7ôdx›â1ÿ1ïXÞz¡Úz^dë …* HM~´,kfçk–ƒêµcä‹ V4®Ü7V¿ïIɧ™Åv8éT±`’:L*FÒÛ€ÚVC˜ùhë×Ä”ðy1†Ç¿ÐÄ„…¤‰T±]râÉ7þ²¼ MfW‰|ú÷L§á)U£¿!ëp¤ïI××ⵈ‡Óߥ£V»îß ?ÛÐÈxo4R‰6¾�\bûä;%.¶ž­=Äùy@x¡—=ÿaÛ®ä´q*FÔ쫈[`_VLækp1··ß%Ë QÐ×—€ó+Ù‘Ã%æZåC ´—Ê‹ô”?'îûKø[òØÐAꋵ÷?fÑ„ƒïÝJ½Uý�)Ô!×?> *¼÷qžæpU™ªÌМÝ*šÎJ< ·ùþ(I†rz€ši—bX†ø}6x‡iú}¥FÓHÓêT^ýXù+4¥Ò”çpÆ;ðxï-hLõ9ò߸}R>KÞˆÐãóØÔ<nN‘×ÀMqlš¼ªAw֠䣠2æÂ\m.ʬwÔɱÏdØûþJ3ÿ,Ç‘ÞÊ‘ÞÒ)3ò©Ðó†y v®^ê|¶ÃÙÈ /cÉR¸îµÚ™Kóg²-*aû±ûYØ#§HaE‡%­ݶ @wwÃÀÓ‰Üq?v´Ub¶U›’m|ˆ§ÛÅe“Ã3µ+…îM–œkÅl—Ô°¢õ`2tÑœlV‰ãÆÆÛpö1/àX³ «®ÏçåSzÿ|^˜×‘Oûáæ*HÎ#Àü¸©Raÿ L¶U̶ˆ ’{E:Ê2Ó½Ìø}œB6!1q¬ÂU‚Öô3FŒUl‚¶l±ûÿš@b¡ò„ß3µ (xPazŽl«¤qS–&eHã²D§ÏûK†‹Ào™ÏÑÅiÌíƒÉÍ&Ë“,›Ép»ÏÓÀ@’Ý—O?~h÷Ýb&Òá¶›ðеX‚â='Ì}Y`À›Ú}CØÔ·Â¾ w7,jxÖä*9ÁIõV)µyòðßãh6ž¬Ü—#ŸAÅ9Wudåð‡‚XÕîÿŠ=ó§76­òî×í°û.GtÙwìtñrq¾ºi Ýî{&ÍeØäX€X!ì´U™‹D÷"Ù\¯öØÞ#êóêÚlöC3`¾0�ƒ Ý÷IäÒ®Êe»*— ¸§aüa Ù¾äm¬çRñï1}ƒhPÁ><wWÕ˜é~6`2},Ыïƒú4³4F-6Ì3h‘«F`Gâ%Øý÷àfÊì,i9êP,Þd#¤y–MStHtñƒ…gèsPS*ìTÝu0žzµïþ8—‰ K-ÛC±ŠÎªÇ­*ËPÒŠç" f©â]†'†ÛÙg”5ÏÆ‘ hV›¿ž=0ã*¼~õ&f‰ày~�rö C©û ·Í烌/ÉüÞ@½Î@üŸã $ä" ,ú,Kƒø]ð·ñÃ2Lq̧´?<î ßÐ(-¢óP±÷Î gh•R“%9W -‰.ÈU™…–4—¼Í£Ñ7ª˜§ÑûôX”¬ïÚ¢€°â9Y¤ÔZåëæ‘©?>&/!LÞ Ú4Ñt4bOεà_Ã4â–w:tžçé“¶ãÃyüþ6=¬æy<i<ø‹p¾ ó—„óx`9ÛtÌò8&Óޞǣ΃Ãy\! `:=:ÓqÖ©\?ÁílÞSGúG§áI:!É.eÚ·¸©C[ý㇟奊¨K¤Z)tü‡QwË‹¿Å½É˯€¢RúNyV8´ï)wñ›áx‡ÇXîcµò’;ÑO á‘SöŒ‚ižjª9ÍÀ…áãèôI&ÏÚ“@E0²/ßäè?æD΢•Ö‰2£…{™Ú÷Š*4:HÆ£û¥™}ïæ>Í'˜9?Q£¹‰ŸÍ©2¹Š”©ÜÌši¬’-˜jò<@§bZ?·þ çh¥ Wº_gÁczZ&‹ã¬ì€y©¡Wè°µNI^Ç‘ç$ ';ƒ©X¥B\kŸ„²'·c8Ê@o\Òˆ;ß1õx~"hs?ut«rŸÅ¥<Žaiúø·”gIÇTñøt3;0ÉQ ´+÷Mæ_ø1ÑÇéñ>LºÊãš¼í¾ >–cÈc¬¬(Gr÷Kô{yåimÏÆbz O ¥#Bí><ð];&4µÎ¸/ ˜îÏ·Ø}}4Ï’‰s›dº1Á¾d» ý’^üR·[»”Ú4¼W«(±Èõ¨XòIRÿ1‘¤º…Ï‚q®i~q®©Ý«v­ð˜xfBÁhîSÁc£É0\%¥EÊcÒÏÅ-µm©™5–ú÷h8iŒ8:Dg¼í•<›ð|måzÄõ$0$Áe¬/ -â±ñ™'íNÍ šØ¨Ve¦|2b»ŸÍ’Ÿ®¥U¿ü¡3 ú¤ ~TŽp²…±›½}’»ç ˜å®ßVSUFGß®sÐý“ßiº/O`†;NÕý,ÚIºß/§ÝÇ6®ÓFžTÞEÄ[ðGaî°2Rb+AjvWgšç줈€ä�#üÏíVÆl"ŽràVZXÂÄjx~¿UrY‚¯$¡rªT¼ÙÆ•ñI§ÉbÎf[ß?ñxVÎó*š°<þ2‡yùCú6 Íž4;â9B<¢g³ª¬T„ðeÕ*¼+¹ŠY$¡-è• Aetß^^X¥9›x[ÏÎ…™æ[~T2У™Ç° ‡ïoQµþíl´‰ (S“j*Éty óÞ4Ø+ÑV öÓ§•÷ý"é6%쯚ãꣳ³ÕÛ sÕÛOÃ=$‡nÿdZ°\&Õ$CédüP|Hž7—B´Êõ –­LlÁJN´Š±w6;Œ›Ó5Nm€&Ì~ =Åäȃ±1ÜÉ(m¢Q^|ZR½»ñœÆd?“|ð÷ƒ”—óò²ïŒr¼°šË¦ï™p0KjH’ê]øñ¶6Š6‹îmÚjiâômcðÙ¾ÂùŽ÷–gw@kŸ!éÅäØx‹Ùî_ ¿\°†¥ÂlƒYWÝÛ×OÃ}/ŽÏרzk‹8š&0<ðZþì^ w¶Q€g:ñ¤œv‚˜“G£ÜÎØ}oA…,»ï(­Û„ðaÏäá¿cÂ÷Ã{tÎÎg'#y—™ú@ ¬ðwÜ‹¾h¯äì$R$ç.ûÊø FI»„/Í¢³ÇöÑê˜K ^ªßm“†”#Ô’¿ÕïJ<yox´µ;ß1©ïñÅ:z5ðüµöž K棂ñëë€ÃLTÂ߈ÒüŸÉLÂ^+ßH9Bç�íœC½Ãål/%‹лŠÒ¢-f‰ß.eÀžµ CÄÞÖ+&ãw—÷'òÏz‡°˜âÒBЄ„/½´˜’¿¥´Dl÷Ã7M4òeYý-žPÇ„¬èȵÐä>îeã‡M¼Èíô(Âûi¨4ÂþV–%oâ°Ùâih¢ÿ¯²,R‰¥"³«.Aìªs-B‡1xw`…\VÁ¿GAzDý÷üø·ö¼¶@®¢²—SŠK1C’è«—õ·cq4¾Jí¢-ÆN´!ÞfëÈM¢€Cºÿ˜f'ñc±Ýó ÈŒnó¦> .L%òß«ðûžahÑn? °ï+±Sþˆî°¯t~Hã¼xìû“úN'Ür±S¡u_ŠÿC Ž®ªÒÅïÎÛ†Nƒ5swýÐÄ< &$loÔ9ŠE%¸éÏ[öïᛤ[èp[ y€t™e¸÷»CHÆw=M©pKJ|%Þ‹#“Õ¢†èª(b~M~Ä;íWR¹o&¾õŠ-¨wÊfªO{®fM²—¸Ü?½?”6ÏO ›nå›*Ùdú+ù Ķ£HïaxÔBçûDÂdø“ŸAA…8g ýPLçM²ž¸o’Ø¡2ïvãfnáÀPü æP1t¼[èze«y§Ø)tšÅÒdñv›˜méÈ9ß2ÕM�ŽO)ÓÎÕ²Ò[¤ë©Í(¤Ã¯ß¸OZG‚på.œ•ÕÆäbò£i=xêžœR\z»ä×+p:e>¾m.Ð|ยy®â¹µwàP~í,m 6Æ÷ÿ®’ÍȇÃõNïübv6Ëå\Ú<¸M$ˆ¾mê×Î:×¾ÿULýÛ‚J±¤ŒSŠÒÚ› ,´ZMÞl³0pžç*Q@�¨œÞÂÞC�×%ÉÊ«e›‰Ü󼟀w$þ5™{ÉÕwO¢Y@l‡N´3�3=ÈoNSÇø±”e3‡2Cžá êR—<¾\µ›óúáõåú16j¿ü<D½uä)&pÑÝïÉšñ}Xx÷§+é{Ì¡á'ñ["?©b' yj }5ÌG`+ÊD˜ˆ²ð!So‚âɰ(IàMª÷¥œ%V&õó„3Š÷òÔµ½˜ó´ŠÔd.µd÷£uö5..·Æ‡Ø6³«´„…‹p¿{O <LæÊ‹Ê‘\Á,O…xrØa³HN‹Ä~´‘à b›�ï’ BW⊰×ÅæSfÞË ß#&Ô/²�Œd/È_€"úÖˆÃBž«5¢äêêë ÝgxZ”å¦Ó4Þ·Þ’'ÐbÖÕéýaU?£´ó éñ,­Y­ø¤± YS×~ Yw°Ç7ž…Ñ Ò¼–Cx®”Ü=">ìÆo× ¤U¤%•î£ÿ9ÍÞÉ·¤Sø$ `o¡\TrêQ¼Ý%¸Þ x¹•Öz0®î…Á¡Fµ*|¯쪴°ÈU‚ßÚ ÈøMƒ�qšJ¡!ï‡ò\LêvŸ«©”»Ð»•ÞÄ ï  ï»…?}ÙíÂÁfѫݷ‡††aÍñ>:X4r%Øý»!³XA§Ç¾$ˆsªr~‡3ä |9óÞPë׉ÒVì9â€xèCy]¢ÝW %Ç»„^³¹=°âÈì®ÙVö™˜ 2wÚ}MV“)SÈÃAvÉÜ!,˜ì²,ßRØy§´a…÷Å‹?Úbæ7Ú­*e¾æÀñ!XΘÁ:Ð1»_ÔhCá¾)/9æo÷ý\õq´j­3ðŒ}Ö¯wqFÆX’ ¼mø-30FƒÕò, öFwÈ.<C‡Xæ`Ö²?¼³Ë]½VsÞHEÛµ0Š ï'Љã,b­%P«ÑÏv:³û&¡šó±g¾ªˆãN؇Gz;.BByh ¦¸ýøMX}æD¦È'èЇ½Lðä„|Ø»»WÜyüŸÃº.gÚ ÿw_A©}[( L!âCÎýXAôãw»¤B+ ` ÃÌV@;,¬ºfbÍ*æá¢60–´wuH¡_èLDoe ÑÂÙÌ+cnì͒ź’Slm &JËóÑ8èã`¢»7ôþ"ó€$!‹þ÷Å.»ÿ)°a‡Dºn½8ÛÃV Á’ÅfB-¶[Å­ˆ=ØŠd&Icq*ÉÁbÀ—Aoó‡TBvüóVÙ*´u8[˜§NûìþkQ£Îm’»MÚð~ßõô÷óÐ;» Æ2c·¼ùG�ye+ ù¶v81Fi곈Çv!#Ø{ l!s n¸g^Œ×]*•$‰­òeÐå<;<C3{E÷&ÿ1p0½›°²ó(&¥«}žØµ¥\4ÎD)7IlðÏ$'.Ž|?E"½mR®•€íãÓtižãFo‹}ÉOè)QK¢;”énÝìÂ0 eµH’¡¾XÐ$¹lØ‹¡b—l·/9ˆñ-¸°¸lBqÑc/¨2.Ú XÎ\:Š^p<†u¼MRA[æ}û’×0FálÉÌuØ}Ÿ cÏì “À¡Õ2K�"@M"†¢�ÎKv_‚Ç(ˆÙÑ•,!ŸIø5DD;Ê&SY” ŠÝ¾¤m 6Ë À†/acƒ³Eh±‚p`é÷'žƒ®SøRëÕ!á„]"ó°?ük¨ $ aîž(¡½àS,ÛùBA(Qr‡pÀi·Wš¶‘uJѹVÎ…Õë¹0În§¡4”¹<‰úÀ«4z¢ûð8.;Þ}y»T°6sŸX°Úþðãê*þ)• 0nV%¦·ˆ‡å9'Âc¸§Z< Žà—CÁ ì"/P<|å΀m¨ð’j»„ÎA:r†[Ðñ…ÔJ+�çêÌùŽy‰â|‡ä^-=´) ÝøP>'¡Ü¼!"Ëu‚„�}{p!¾TOÔ‚%<æ+‘àÌ÷íKþ‹ÎñÏîÆïÚLôša{ðv|;Oì nKDÈç¿pÁ“?ÂbFqUœT”›ðEYèC—Ñä° t`ßÞ…"Ÿ8ôCæ‚@’ ÖüÁgÑ¡n‰6öòI|DÀÄÜ#v‡ù´çòVû¶VÞó¤,ÂÁSâ!T oâÊ,QBëwÛ}ø±U);Ü_>÷L‹Ÿõ}’Þ”1r‘k……×¼q¾Ã.ÌÀq¶j sé£ôòÑqU/vß×0\ˆÞ5Ð a@[›ø0 ôƇIœsáRNr®•²’q·ÜTò¨ÎPÌt­x¿CÜBK¡‚5â#-ø#Ç~J|ˆ>U›443çüSv?m§ð®éȹáiÔ½–­gÄìT|KÚ”¦˜e³¬ Á‰ ŽJÎ5‚<Ò%ž„VåŽ(Êd MʵÑT4(Qˆ¹6©${ MB� Þ– öC&ÅÐu`ºmÖà«ù=ÒGæ!1€ïÚ—|A'—„ÀómÝva7æK’E÷^©Ä–yØîÛ�d 3Á’( 6bàW==¿—rS�_À²8³‰ ÷c¯uî…›Ô§ñƒ”™50N<CHÁ^6NäHΞL—c[¹(�aNLƱ€Õà -|œP VphƒÄpöB80Ðô£…ï`&JßTtƒËc÷¿ F÷HÁ6 0ÃŒÈæCð9 Ú®(´Ø}«N¢ÑC·è²ok‘6,FN‚ÜÜtg©C¶\Þ!Ò ÌÇ÷õ³­7²ÙP(yÊö;Q£k·“ês-0ˆ‡c®Ï&YiçC¶%1!sÀÂÏx¬É»°ûnJ¦¤õh°šÃ‹½o(«””™àýá½�Àw¡"@÷ì+n!q#š¸¼0„[3Áç0ƒg-îä_°2 Á_¥ï¡à‰¬54\hqHñwf—¸?ÎV?DdŸ\¤ï8âÐJŸ [u&‰`å4ܪLà“;²-?AP1qŒ¦Aß$”·PÖ"-nã÷wÒ›¸!)ì ´Ãì´f¢HÀv¿›ÎbXƒÝFZ‰H#:ϯYy”Sça-u"m±íK÷uäÒ˜ȵÞèGAJ,µA)@ Ó·èì¾-§Tv®|_ì‹ñsHxÏ,Lµœ -â!pV„—‰£íK~Œ³5B4<i~‰F÷ïÛýËÀÎÀœÌ\/%V±Ô&ÞokmÁ¢Sì)KM`àž/vÑ ˆ\$û’3,ƽLœÐ»7p Û,ßܽÿІýÜ@+èC}!ÕÖ º±å±™øAµ< eI®$ "Ó™&º»¨QhйKtvâà`öï„!Í¢¶ #ú.ŒGt‚û…®X$ŒÜóa Õ˜+)8¾�Ù‚s½+Ih….n÷D7ìþ$X 'y†�7޾CÂýÈ:¼‡pö©‚þÕAÛtƒ@§¹ÈÖ'.ZLžËù̲ÇÂú¬"&•õ謗à‡sCÀö¨ /ºR:²(RÖ‘Eq0ñ}bî‘‚õ~ ]gYÜNÞÝN'ÇY@®Ì)±L¬M‹lb‘5øÄqzœîÚý—g<æ¥Ð¨�cB®%8 oÒ H=}C”¸š1@7ˆ‡ãÌH€z>èã¢R€þ§Î)mÔ¬LÌ‹`&0öQà‚uµqÿÎÀ[Œ°«5F…¸„¸¨IfsHðòK@%ے쾿$ÆÑ Ÿº4ÍŒÀ‘±ÄÄ}!lA&½‰è…†õ—‰ÞuâϨÝsÖ“k|5ù@ßOÅcbœÞ@ÒCR¶c÷K`"ŒþïѶ§u’{›½a$ìÆ èÆ…BÇîuàÿA»à¡.qÑé-`ž‘ Ö‰°™ˆNGv2‘d:¦ÎðÎ6šÞ÷ãÂ_*¦uSX]O!Û·©ºB#Û|}°í¤émr6¨ß{®Ýý Þd|:ÀÇyY¢çåzöTx<òq#©xájqdH¥Vðãàj_âGû ¡z¬Xo°AîÈe¬àÛ‰´H˳ûÓð4¡ó͵ñ7â�8èºW‰ +<瓃h+À©ƒÛï{¬t+0Æ nRÁ èî°.iX}^r¯ pÆ— VeÞ—l÷ÝB¾øªÌPÿúDš¡Ö%x«zZá(ä\f_òQÍòìS©8ówÙ}c,T5ß™&ÑýÌVÖÓ<¸#‡æãgA™ôÙS»bÐ'Ui¤œË½ËÌÎFZâœIr6¢;:]£¸›·žt ±YXð¼Ÿ&z}âauQX° †£v»%y†£Ÿ¸À*ÞoK­âí0­ …@ð¡°(_DûIÈd¾ÙCOcn¢ÅÜÉo °¤£À“˳™ß—\É%Á?Ãø‰èr'£ÞÜ»0†AcâÀªpT$ÿ¬k·eÞ¼¿ð-ÎFĉ úÂ}Éâ›8Á¼Ÿo•\¡õÿµ÷ýñQU×¾{’#|yJ¹\Z ¤Ê$@¤Éo‰ÅÓÉÌ™dt~9s&K´:áÇtŒÕz}}Ü[ë¯ÇkQ¹­Oy½Š”í¥¯¨\\ñ+Ú¤‰Ï¨<Œ‚Îû®½÷™sæd2‰íç}Þ?͇Ŝµ÷Ú¿Ö^{íµÖì}¦ŒÓŸÓ}ﵪ|Šú¨(ß’P:¾Y+z) w[!-Ö‚­ëɵ¹+ôþ…¯@賸^ùÅ%Ý3¸¥ÝoP&4Oeý}÷ðÕßø½ø†Ù‡ê„ú¨ãêcǡ¸–ãŒ}Œ/e:TeÝÊS£×œ't vŠøÊˆ~/Þ=3~G~üøFÜDìÏ­€žOˆ×Yo§äh/IÍ~4¿]ìzúÕó/àÝ÷Ó\~‰Ðñ­ôBVõŠ—ÿ,Üè—)FAIC—&ø'-3ë¶—£gp �—zIGÃÂçòòšþ諼˅å#‰6,²ù§Ä"£c6$·çÅ»I.Õ‰åõq~'Q;3^³CÚ²\Îjó4é}ïÝžÝA>v=÷`Ó>h°i÷óÝy‡VhÏ'Ò¦}˜»Ë;¸M[Ð{>ýރܮíûDÚµ;Rv­(«~’f×ô¾Á-‘¹áͶåoQ ¾·>H|§s—ú¢ø~(ñ Ö¸õœA‹Þܶí¤w4ñÓgåÐQÛ§SM› ˹½^8HŠ]×mÉtÝø 鯦³ú>¡–Œ·GP‹$¿»?Níû¿Ôe6zhð—_J»YÔœ3xúŒ‹½îsÈ€dÝ•"hÕKûÉ@½¹?‘¬¶"’u4g¿$ƒ{yt¤`ûw¶è5ÿ»r zÿH"òRì³ñ×-¿ße‰7GÉœ]–‚­sù)š‘âý–ša8ç­ñV”þèËy(�Ž ¹]Rl/ÈáAËÄ:¨¾xëC½ÇáÊôÝý¯+Iå•ÂÆ‡ ¶î8Gxy",uº¼g¤ ¶:VgEÛüì)õŽÎÓæƒÂ«&[ š™?cÃZÓsA®i¼ûÈŒë{ž7»õµøhÁÖ‘qîÇqžAñŠ yBd!ó’Gj•!ÀikÇžKè;¿;NŠ8èS_šã ÿ³)uæä'$%§µèiùsÍœ;¯B›—?'8U<•Æ!6 °“wšÌmlv‰n¾°®¢‹C½‚YGÁ¬ûh¾ºëÊG ÿ(Ú¦O\…"BÛÓp<šœŠ­Rxlf^W—¨K¹XÓè»´Ÿ¯ë¡f˜›‰»¸•õŸ¥™¸‹ ¶¹èÚ(Ý}ÙEln2é?iæ&b<±¿ïé„{ µL§ `>ñäø ]çýT¸Åðó#ôÁ5ùå¿-è}“&¡‚ CG2¥<ORhãôòÉì¾ÞÕ^ž'Ìä—'Düt÷«h½÷Κ³¤þ؋҂mÛó9ÕI?ñ³ ´ìˆîS×"[¨¬Ì?šò ‹öìº$±".è=†¡¾ìß%͵]¯¾?È5Û†üòÏݰ–sÎÓ*Úžrö)X¹’øQ—ÙÒ‚^âXù/-©ýòÛ1¦µÄÆ6¡¿Ô´œ—¦á™úPæêC©ããxË0Ž<}ÜŸÉûZ®Á«޾•àía<uØ ~! Êøy Ën;èÖäsô–˜_ïï[“WH‡:kFâ¿…8ø._8[Ìß4:\üÜIúb÷n-Æ5œæŽìÜNMl íÙ3ëaÐökfKI˜ÿEJ…îÔuæ×yhlð•³\aÃäz~ÀàwpQŒS¼’<76š[°u3wæpœÇíbÖ/Ç.¶ÁßñˆÈpî}ô}Eü(– ŒrŒ¯z('mÉS·ü¾¾tFÙsü[ûnª•WÒ·—ûØçóã=B¡æŠðV͉xÍÑC5G¸GÖz8Ñ�4Þx`¿—®æ�êI,·Â³Êƒ¿ û¶ñyøš‰»»?¶½?žÝ•oÝ[°õ¢î+º°¯ºà Ìî]ïðÝù(·oߤ&)võ´ÜïßÊ•ûýQ¾Ã>}F_²OÃñ«£†½þsþMÀÓZع×_Âã§O§âWGù>ï>#÷ù§Sû¼(·ú̘ø•ÈXtF“êDë¾øèÀMï9«%fôì³ôAÝÅ{wóM¦çóñÖdJ÷HÔ<ï9¯9@äø{Ž[`£Ö¡„ydïÄ¡ þÆÁwj‚ö"Qéug“æþê3³Ä•&C#ºÿE÷§“ãðè'š4ÿ™\ù›tWþcÞÖp.—BX…=R2ç‚%­—ªÏ4ùþwba“I¸_ýŒ|ˆÂÁ“Òÿ|â ÑâÐzlzp#VÇòèCÛ‚°ê¶Ör»s/ß#ø]4õR¹Gìánx_¼{Óï0<ѳ§ïʽôEÏÎxílyÞFž¦ù~ãmóh<J1„CZ áLs½kvƹ�ÅoŸïÊwå 6öÚqÛ—Ô¹ ô9ñ­Á²×é·Ì?ý4~0þ[¬½Ç“ɵñšcO}ŠIKôœ¸ó­¤òh!Ýõ,}»ñy¢çX✾+óÊvç•¿^°ý§¤™(Pý ø#ËáøÇüAÈ™ƒ[OǣDZ1ð�Ô±â•";­'Š¿Ï%«çD¢.¿øpbÃÌfÈື“Ék¯9›î¶äb�ƒ—’TôìÞr¬gÏ%ñžÝ‰ž½ê‚—ý{ä>²çÕ? ]L¾ò¨î÷ÿIêçßÓ’¯áqšJ¡£÷F_' Œ”Žÿî‚íKÇÿBŠh…àZîŽßš_›¯M¹ÊgѹT8„2ø¼Á`-èÝÎÆlÐj%£õ™OSrúIÝ2Z› ƒ×‡vÑgFyÖÙtù(ÙÈõ|38ZŽ2Û½´ b0ñ¶™ƒçHCûgŒÿ™¯ìßüDk~"G³€k¢Ï›Š¦'¢°~MjlÿÖµÙJ;ÑJL-ê¿ ¼ýô pSÏí«MÂËï Yè ANbi,™Û}IÁ³+§Æ¦ÄFó^¤eÑ}q¢7vêÓÔyíæ t¼´màÙJq´§fØjù2V3`M6ŽD3œ#F•,>5`èmž^ð"2†ÞÏÏÒó›âùzþx~†ž_¢ßßššÈ‰´òsÚòÜÒ5üxY~Á³•Sµá×–Ò÷«Ó»ÏGgŽêçËkò0Ó&ÏÀ[·îKÜFÞzU¢¡ò/B%Ö¾PrëGja_EòÜzþ* ¹á%rVƒª-È@M^|Uaìço6Ê—$Šêé ¯¡ŸPoèŽ_eÑÐ}èñ¨E]ÍQ—ÇF­êLðS}….(WZUÑ[ué÷é•”•¼ƒ×§ÐB×p«ª'½Áߟzïe‘áw KÄïúV«q€Æv>æ=VnQçÉ󛋦G‡ù 6æøûðÐkâ>S>ƒ¹µ(yka_Í@sò—eèÓÀº•â {êw6´Å†é#PüP﫬}Ö¾‹™vÇ¢•þ‘Î*Q̘D±5_]ÿ_´ô S´øô?½?ÂWÉ…¯Ù’° [/Yã·–ýk£ï$ZO%ÃEè] CÊÉu…ÜE+êSFùÙËäÄ-ÔÈ~+ú0´Sž Lèç ¿mG§VXhÒgòoj¢Å}±¯&“ŇcÓæì7XVNŒWN…;îaÞèÌNeáÂ}ÆßûH4¦ê|wê,·ˆßþu–Bƒ÷¯ÚüE_ì;ÇPõˆ¬:œªº!CÕòÝ¥|õ&C…tJ¶pçqìmãÕ†ó¦ù¾qÁDó-ûOdœðË0ß}±ÎWè(¯s…>áÔv¢±°øHœî{4'!¼ò’­0qŠÚb=3Y´0ö꟢[o¿1øc| Tã?:VýÀM�à´��ךd>AD‚Væv�Ýiº)·ìžäû/ìZYð⾡åÏìZRð쾡…˜Í7¿6TŠ·¿6TŒ·Ê‡.ÅG¼‚ů±]„Çl¨�Ÿ¤ My7ŇoüÿíËù}仟êÃBm™cík=/š3p)÷µNÉ5{^Ôß”žoÒêsXKKxNl4_ýFl´€ÐBúoº:—ªMX†Fß´ zÛ24òVñÐ0=y¯àËЉ‚-Coh¿•  ÏI`Sj.Bëù¢yÔ#ô…ß›*„…Gݘ“êÆ,êÆ,êÆ,êÆ,êÆ¬Ø97¡åš¡ÿó&þyÿ ½åz_üúÐô÷§ÿ¾ö¼ËèÓt©/t6¶élR–¼0ö¿­±Éèiþ“}trà!¾âó5§6¬(•ë«—Ù"aW‰ÏÛ^âŠDK¼‹—.Y ø<%®°êµ¯¿¼Ë¶Ð¾Ä¾Ô¾Ð¶¨´ôŠü[´ÄVZ¾lñâe‹–ÚÜž°­fSÈ6‡ÍØ<N¯OqÛæÏu³›]Ο3ÐQRì5V\ÁŽ€÷6P¬­o²µG=%lS7‡Vèrú¼n›_ñÛmN—K‰DX•3ðMÕæôù‚.§ªØªl5ì tªnŠªMžF^H´b*Òíuʵ¶Ô.ekª·K±¹@c‹(j4ÄZ‚A›ß@³áލ_ ¨Û|¿s“m®ÃÐzA¿ƒÞ[Û\7sý%‘h ä怳¤9è ¨J˜]é xÕ,¤à9r£ý5Έ*úå ý¢eà-4l5(ӌխWÃQ— +l~ñš í,gŠJÅëž (Ýõˆê ¸@Ÿ‘¼¸E#Š`ªlcΨ\§8ÝüaCØ«*Lð©Z­û3§rs›ÓMïmøØîtÝ’1q‚V¨kèrŠϵрKõë$8±^ÌqÒ ¾ß¦´t†©«ÆÚ7H"cš(Ýè …7ñÍ "í7+.uyu=íPÂ|2°Œ§‡d׿ê Gç744àÃè Þ¢°® ×Í<¾ Se¾` ÃŒ¶ûEKY„ÿOÏ —p„-^Ä1|¶¤Œcøðx¼šÇÜÓY_[[O½²ù½‘‹õfg·jYCņG¢âCŠ?:â¶5è8¸ks‡Ý(h‹DÃ'&DÏ©FŽ“ú«']»p¡-äsªž`Øoó’@,¶—ÙK™™«<ÁN vC‚ ¨ò9±„uùaÅãC‰’FEí º „2ÃnÈ0rß@iH( xƒ%•\£àv.ò7«Êšô4NWÕé ›éLiœn}g0¬š ͉œg¦KOâTk ;f2S§«%I3š9e5—D3é˜TÁß6’b»m¹®¹Æ@R úgÀ@eL‘4਑@C¥X€N—ªuGˆFZšœlb£q®S¸È—KØ@aL4Ä;A ¹œa†lù‚K‚1 T¡PúóIŒéŠÌ«OËã‘9ëÓrRl¹Ui¹ÄG™Q™ž!f@æmLËãc’9µi9b02«š4šQiòÅÊ7šª ? `SäJ‘§ÛÍÉó‹ÓKK5‰íM®P¡ìÎ鯯¤êiT\Ï!ٮᔟA뢄™P༺*4G¬‡›5BC*‰rÌ×WòA_ŸqÔ͘ ¿"·‚ªÈ>&u"m¬ˆp€óG'Ôùhë™XnöP0â¥éŠÇîä]6'4y<°O˜®x$9AÒa(ë™AùHê1):y=Ké IlÂuÒ¦ë!IkNЉk™AIê1):y53ê#I?6IØÈ¤¼Û»¸-B\“|¨ò9µ–$.W¤Ä¨ïò‘wL>‹F%b¿ÍUñs±CJXÝÌ`,Øa×Ý$ÝÒ¢ôó¹‡% M–fzšÏ¸ÅÙ¡H«™oü¡°r¸¼Ã^Ü€~ØÚÉŸYfKÙ£ši`s+n.¶ù¶äV*’m ÙžFS¼ÞçS:œ¾ ièÖlr)!.iu“mL¶‚+èÏoX[±L³ïÕ Í« öuªÓ¨z;­H4Ât(î&pÌIu襹c6°Sf´¡¿)ÓH˜äd¤ØAÕ¦[Žn›SM™ôTåX>¬W1½Òª`ÔçæµÛU§7 ¹#0ƒl¡°×ïåÌ~ Wˆ§ÁmðG#ª­SâÅ�QKØÖéD §±4,`®sÉW鵨ªÓÕiÓæ¾Äë¶FÞ—´ŸµœÔH9w§iuEÌ s-ÄYt9waÀ‡i‚.U]+gô4‚®+[‡3ÜNbé úÈSÜ|n¿B?}ÁÍK§3àö)ái²,\5(ÁÒTUš£F>›‡”+cBwD|Ü)6Ézñx÷¬,áwÉ…$çÓE óÛ!ZvZÄšb¢ DãŽÒäÊuø‚íFXs{Æ),¾ô²ä6ÚÐyH`}õD­šÊš¼MMÁdo¥m²bïë7¥» F7kyzàƒª^Wªé”¿ËÇïÆØ>d°CÌãMoKw¨'lË8–é|W‘â “§œiôcɬ}Ñ<ú zÂL}©M±+[o²²JïÞD2¯Ì´†ÀBv2Ä(2t*cô`yf¾â“mÒ$ûZlcÒÅÓËsí5é¢ée»)xò¶«í-ÙWüømg¡µks-¾óZ ÍÉ…æTð-£‰o³‘ÉÖ™*nO/þ•Ë£ë2Ë6¦K¯VUV]gˆm·ìÇvi~æv6Žò› }S<mòÝHã‹T!aŒ¼¿šSÇ‘§45(â…uá`44Ѿa2~®M(ƒZŒr"º´¸eúZ ¦{ Ù6óLåÇÒ£žé´Üã™H)¤Ÿ¤]•¡­‰m«ïÆZsÈIÒ,×Éñ6½Hz¾5Ú,“­fœ¹ ÓjŸ)]•!K&/*›/ä^A4Äã¶ZÈ)›ÉRͲµÁÀérH—Äá–rÊ c¬Ìµ¤¼Ýå)+m_\úí¥W,Zˆ Û¯p^ávºñ¤¤l|ø¸p‚ýEŽ,ðújÞ•i“ôà‹RñiãÖ©éËT½™>BJ“o?=`¬{q2Ù[–LÞ�xÏs®Ÿg‘v¬LÇǃÝYhÊ·s‰€G»—èøÓ€ç�G�ÇdúqÀÏV$“ý?„œ’ø…Wëy‹ð¼úê‰ëèÈBs×$Êÿµð0Ú¸we2y' PïÈO‚7/ðñà7†ç—LywN¢ü_ ÿ0¿B@Q/ý«ÐQ^^e2ùT=x8X˜xiúø:àÆdò'€&@²^”!ð¯ŸWãs¸.™ �V~W›L.­B|î�ø�«dÚd`¤}¿&™,¬Ÿ«V 8[1H¼sx0PyZë)Í""né¨mþ¢b©qiÇÐò Q’ {NªŽT h•ˆl0]ÎhG§1ŒÆ ‘/½TÊIÑ ·²‰‡£2Ó;ÃŒû‡ ¢oý«l¾+ë3:bŽ “e´åç´š«q±ŒÆÚ¸–kó¥YqAþ¥$}×*t>eŠ RÚ¢hÕ_N¦…ê D‘=Ù}ÊW[Æcc¥ÿ”LÚ�…€ü„LÉO‚À‰Ôññà¨áyŸ)o`öÃÉä¬G“É/A}€=ˆ´ÉÀ÷A,œù©ø$hFÞÅa­P÷Š´ÉÀ’¶p=èÈ›•‰ƒ³yüY¼5ÏÊ!Çð|¾&ßð|žfšáù\þœÃ!Ïð|¶p˜jxžbx-ÍÕõëXI—3\¢úC¬Ä­t•D:ýlUSc +µv-ÿ#Ú¿O&·�B�7`„2 ô|õQìÍòs< |‚Å_ ˜ø M@ž¡¹bghþö÷·¿ÿO…v¢È®ú€±JÀÀ*ÀzÀu�ÀðTÀwwf|ð—5wüB´8 (½È–®¬TVÖ�š-€Ð F�§�£€³�†<+ (ffl€9€ù€Ë×n�”¾p|€@ll$åÅ|íϽ±=ùBû{‘±ç ÏÏž÷Ég¢ÿ|.’uXn[Ç,[ò,—oµî@Öl& ÷çÉ$ýð«8?okNåô)ÕÛ¬Û¡jI\ 8…ü§Œùµ±)D‘Óa/l±‹:hßÞ²;™¼,­žXîF»Ñ«!éEM»ÿ©1¿2fÝ–›Sg/ÌñØYƒÎ Û�tâÀÉä=ÚìÑÐͰÒ'a×™Úk²®·[EŸFÕ ñi*b¹9öBªƒ~é~ä·ó«D;a´SJãÍaÐÐOµ™ûEÇv–³ÊnËñÛY €ÆmÙždòt&žu¢Ðuv[øF¿_²“êýçdòþ"m½¤}Ë^x£mD‰ØYî`.Úù<X,xxÒnͽ߂Ö?Äìvâ`õUÿ²sÞØ¶sÛ-öÂFQᓨçÆsìVâýJbþ+Éäm&^bVlÄ'zÓ?Ýý±1¿†÷0Dcæóš½¯&“ß5×Ña·Ñ…ˆ^äƒmb̧9�ЯÂFÞíòèÇ~/ÿ7ónã†�íôÃs£ƒîƒbo§j ²£3ßÁ÷¡ÿLb É€®É‘c#Öƒœ]Øý ñ|�¿€èÿŸ˜°'>‡éSÁ&ÿ§­aãZ.+²¤ò/œÇØ<À@-  àt¶�<xpðà}ÀiÀÔo¢<``  Ðð�º�Û��<8x ð>à4`*õBÀ<À@-  àt¶�<xpðà}ÀiÀÔb”Ì,ÔÚ�@`àÀã€g�¯ÞœLýÊæ–�jm�  °í[_Ñ“ù««ªZf›_·¶µØF‡ Ù®—–Ód••~{ÑÒ¿ÈÙìWíøTÃâ³S{êtF:™Ý½9�ñ©†™=¬øèY<„|*³S`›ÙUeþ§³!È ºªeøÿJ§C|ÓNåœ~¯‹Ù).>nv¡ÞŽ Š·G€Â[#çpÒSMox"@ëû~ s¦0é1h>�c_gB'P:é ‚•ùB?Ì–4¤/ ®<èH¯9_oO[Ñ çJ:Ò¤?Œí’Yf #}C°RÒc «’u“#=G0Ã0Ny¥­0бyú­étõºþo ¸öűõ5èH¯ägh·UÒQ¿O øá7ÆÒÝ(é¸Ò†Gpƒ®ù~N.C}7̰px#G§Óêó˾Q}¤O Òê7ð¯ËÐî1ÐäeGî8hŽC×k ; kº¸Žö‚NK:Á=²ŸD÷Ø’w*ÔørÓeŒIÚù†íq†ô·väN6–Nã±ö÷üßYX?6Ñ ™aMîÏ5Õ·¦ØÂ>+[_¦¿ûåg®¤Ú;EÃEË£34\´@kEà‚»¿¡á¢ ɾÀ…_ÎåœãÂgïÿ–†‹™#™¸ðõO¥pàrÈq# Y¸tmŸÎåÖ…‹…<… †œLá…¢ý.Js&ð ˜ñ/—[F|¶ ÿ{~qß­ì£ä¥&|¡ 7Ó_9^÷ë7Ówšðˆ ¿Ý„ÇMø½&|§ Ø„¿iÂÿ4A¾¹ü™ ò'¿™þ\ÃIH´®…ƒD"}ä“|г«Å>@ø:à;1 «%Þn*ï>ŠA•ùݦü;LøvR±?À~Ÿ#èé EíŒ-•å_¾õ}Wâô³³(ìm sÒëûpënÆše~)m޾^f`üÈf‡ï™ÏñiŒ~Oên5•ßbÂcÀO…û@¶÷ðþw;#ñ¾ÎS™Ä÷ïm·ú0Õ÷!ð}ߥ3Dþç¦|+TPÿ¿0ö”,?àôlÀó±Ù]"ó�Žb%Þ¼¬“Þô&ð(áÿ¯¹éí= ürÔ÷˜Ì ønlBoKüàóÆ~òÆn²œÞé;p›^ÿà-ë»ZæW�ÏÇfºDæ{¬é퇀ïÿ¦Hzúõ®"ÃüüÈDÿcþ° ßeÕõóÐÏORû7ëý;Ü·FØ„ÿ xµ¡çAůi×ñRà;¾ÍØs²+€—506Wâë€ßßÂØõ’þÆ)éý¡×½Áüü½Ìï>g-cïÈõðO&úÇÉîÃd,’õÿx§oÒËQz…,Pþià;±x+%>[Ò±ïêã;5]~®~b™>žµÀ{ÆØ7%}ø臟I¼øƒ˜ÿß¼ðçº|¾Eù†þýø£÷ »—ò °%ž¸Wÿbà‡/v"_¿ÀAþê$ÞzN:?n~Jû#™ßcÊï>Ò"ldÊ¿øÙ_aNŠþðGßgì™ÿ?€7úûkS}GLø«À÷mÑùù pø©Ä/Àß}ôŽÄmÀ÷?_ |ä«\âà«:ôúî~í½"þBø¯ß}’±/$þïÀKúü$äy.L [‡ž¿úÜôþ_|ÎVП+òoÅçRÃøïÞ,íBèË{€ßýc·Éúþø÷ ôÿÍTÿ“À°_¬•ô/�¿ÿ‡ÂO!ü=àoÀ]úXâÓ`ò”!‘ÄK€Ï¾KOó´ôúé4}ÙŒ½'ó·¾L×W?˜¦ÛWÀ¾úðçŸþ åÿx3p¯Ä�¿ò·Xâo·Æ÷ð“0¶Q®Ï—†ôþ]í} ðj¿žßB&Æ7KâwÏ ëùŸgâðkzþ«ÀUCýï?‚ÍxÌÿ”Ú êô³A¸òÆ.“øàßY¦ïçWŸ}«Nï®>ÎØùVï�¾ô ´5Uà?Õ¨·÷ðG ýyÃ.ÿA寢÷µÉý.?}|Åú»VÒçMO—·àyXœ_—üž9]·Wiÿ¶ž¥Ó/&úü3Iß |‹Kß»§§·ÿ}à-ßÓ÷³ÿünè ‰^mßÀ÷ôÕ{Ào�ÿƒÿø¬Uã?Lp›Wço%án}ÿ?²Y×_ýÀo‚í,û¿ø¾ÛD¬–ï·ç§÷ÿ=àýo0àúø@—Þÿ‹`òwÖÛJƒOd´�WïÐÇ5å' 8µw/ð}Â÷"úŸ�ßý3S'ü ‰þ_ïºL_o¯¿ûv}½²’h$\’åN=»’B*Î�¿¤¬`W¶G½>u7°‚ ¢F=»+{-ÔâöFBNÕÕ ZíKl<jwˆðH_ïG(W\É GÇ«UÝô+%j7J–tGøWñâ+rjd¼éMK—”xŠÒq£AoÀ勺úvÕÞiÄTg¸CQ‘f'Ì x¼Àè2ôÍþ££  Ù‰hÈ!N'8¨fyìÝp9Üpv°ÂíÓ›�J7Õ‘¢Š(*Õ«És¬¿n}£Ãtç#²9B=·U4±Áp»Å„Òq Åñ@íÒ5vvó³ÕÝŒ+á.ÃåÿH Fã÷H?‰êXHÍ,ä§?üÑ€ßbŽêëÖV4ÖWé7ÃÅMó*n˜£e%Ðź]ŸÈÖ?~!7uÙ aü»~è;;?t:¿âw…6³†µõަÀš ÓÍŽˆêÆ,„Òo³9®MN‡Çàc¤ÙÓ™“¥§òÂíx¼×Gƒcqm}ý$¨*Aåç7ŽˆYã–Œ¤Dq\ZXÙøåV65yÐxe¶Fä« ãqk‚ lñ`tyjìÛR¢axŸdÉí=v¶Ómj.w“`\g8ä®-Êì½ש³ÓÈ—ÈÕ-ýÒÜðË€áˆ7€°;;Œ¯Ïp‰Ì&6ü(¬áb¿²I¥Ë q±Ó3Áb¤ tÞˆ¶ä9å ²é1m‰LD®@‡ E6$Âñ¯|¡‡dÆ«‘+ЬÊ(Ê¢ °Ýë7ûÛƒã—ç-ðE¼Pî éYšu鄃*¿>8¾ DtL[jÄš|i‹4ÌW©Ã¡L¤/èÒ/‹x; f˜Oõ m¢û!;ºn’ÉZbjdKʰT8û¢º¦›m ¼Õñò¥¾!ÑOgå7'Mñ;ûúáo4˜xýVgQŽ…úŠá«Å¸Ýi Ãpuº•ñ7rÒ:¼©ªIiâõ“¢¢ºÜ>ú®+Û¾Y_ÑçN{i‹Ãщ`/r†Õ”‘>à —ÐöE7ÊÍïžÉ"`ÍõUŠë–Htü>q†NB L¼‚H¼õ"$Î,:üQUÙä ƒíY„ˆª&± ÑŠãàßÞðjöbJ”BûAÆL¾;¤¸£­Êå¾"vÿV~Ú~bù¨Çœ¦.Y´jªPeq‰d2¸&¬‘Ð ]럄LW›û Ù[ ÷aÄm» +ª•u¦ï7L }©J²0CÛOä«sÄ1a]ïÐE1q1…@^JŒ¿Ä(»¾á¯„!µ|3WɾVõ!qëØ R ÿ¾9õ–+GÝš¦ÊŠ5ަÚÚõ5-Ž–ŠÊ55PÆo7Â÷…lŒ=Ãd˜–ÉD‹„JdYê”6騼ŒÓ°c 68È8™„5É_ 6 µWŸe‚ÅŒš\¦,£ú# AÖ¤#ÅütÖrÅ`ÜV¹ý“¥–ì¶`JÅ´ “9ºë¤(ó»šÊøÆfD«ÝáÈdM s7ÕBK¶2š;pZø T:ˆ^¹y-–ÈÒi’ÿ_PK ����v©Æ@���������������com/sun/jna/freebsd-amd64/PK ���—Vc?™R‹ä—��µw�+���com/sun/jna/freebsd-amd64/libjnidispatch.soĽ xEÓ�¼9¢ŽhôEНx¾‰%š…,Îò&… ‚BBHåˆd—€E7«Œã¾ÆÅ‚ŠFÎporpFP‰¨AdÖ�ÄÎý«ª{f{¯à÷?ÿóüyvªº«ºªºº«»§§çiKúÐȈˆ ü/Êp—!~Ë28•ã—dDhY�7�ò¥.1ô¢¼FCø¿ï“"ü~ †Xú¡Np5s|sRw¿ßÖKYîò« ~t‘œnâ=Œnâ=Ýý~Sy1Úo §vÀUÃï×Ãõ’€®<¸VÂõ)ǯt˜ ׳h¸îƒ+®J¸þ ×Ûp}Áó=W6¿ÿ® ¸ÜpeÁõ\ïÀ5®yžWùïõpÂ5^(sÿ]�—®ëà*ƒk2ǯ+®›áú ®wáªæi¯ÁU�×p}bèø¯ ®ù½ÂKáš×ûpÙá×]tËá�W1\‹àz ® ¸úõIÈ·®9ü>®á*âðR¸òùý‡p½ ×F¸†rºàgp½Îá/ùï¬óè4®»áZ×$¸>‚k\åp9áuúÿ¿ÿ® €{w÷?!pýà«à„û›àú7\}àº2 ŸE¸¿.I€S;)ðïV¸€k0‡GóßÛàº7Dþ‘Â}\cà&àâ¿Á•×Ô�úGáz®kàº\À'Àõ\9<®û…ôi|îîmp•ðû$/Ã5[€gÀ•—®Ûáz®‡…ô pÝ ×sp=W"Ç¿À?à¿Óš!ü÷¸Â5®+àú.\oñô)ÍÿàÊ„ëM¸žä¸káZ׸ֵ®±põ…ë=Ãùÿ¢ ×VCâ# ±Bᣠñ¡ðцÔëBáõæPøN†¦þ¡ð ‡Câcôøå¿À v‰¿Ð`è ‘üñ] ýBá»êqÈßͰD …7*Bâc õ!ñÝ !ñ= Íÿ …ï„cø^aðqað‡Á_¢Ò iüeaðÿ Âaìˆ ‘·(Ú�Vˆ1$ðàЕãïŒdøØ± ÖÜ«ÃSÐ2øú¡-aøS·Ðøµ2þå‹|c$û½8Šá³yìËó¿qÃ|ä/ÿü0ò”weùK«ÌÕ0䆑½»»á˜7Ð?·E þBCé˜ÿk5÷š†OôÇ?†ÿÏaä|¸3“³ñykýÇý¼^Ry½h1*‡ÛÇÍís;ÇßšÿÀ.,+·[MöÛ;Lþ©¼Þç~Ë`m|„10!Dþy½¸e°æÁG¸^nÞq{8~¡‘ûU1ƒOpüž‹ß9¾×…¡å<ÂíÓÌËýJËÃ'¾ÂàŸ49¹ÿ7sÿ‚ã“;qûÏep_ÞÍ-‹ávàá&ž;/W˜]Åó˼^hpe`cVü{,Œ?r>¼~S8Ÿ¢ ™_eø›- ŸSaÚ]ÿ0ùG‘?Ç ·ëS ÖìŸÍå,àrjã¥\ßÄ�}q|i@ûýÚÀðe|Ðð#×÷4÷·T>ÈñM¼ÜD^¿¼ƒÜág\Ä2ÞÁóŸìZßJî‡eÜ5{~Ìùgg3øá(ö»224Ÿy¼%¬ó—sïúñþ­yƒµþê ·MŽû|Ãý-»”Ášß✠!„<q»%r>;µðÌû½r7µÈTÎý?‘ûÿŽäõžÍA 9ÞÊùdW2øÇ+Ü?ËôíÅûÞϘ8~(Çgr¼fÛ/¹šïd0ÿ1Äpÿ)ãƒPmüW¦¿z‡ûCvg&ÏÇ<ŽÌÑúŸy ÖúÕjCh>¸?”sÓêk¯÷Ô¹]¸AÕúÕN¬ÜžÿSnŸÖ€ö»=L;Í ÓNOpH}™Á¿püÞ¿5øÕZÿÆãÝnŽ·ñzÌäxmîsBk§¼ai~XÄý!–OJköÑüùCkóÝ}œc$Ó÷ î‡Oñ~µõYkqÁÄñÍO3Xëgî×úŸÙþù¯5†¶Ïƒ\ž‚7<ðBö»ˆ·Çò�û¯';ó6ŒgóvTÍòrÿy‹×cA€ŸoÓ?¤q>™| ¡õ“=¸Ÿ4rÿ×Fœïpÿ,—¬ùg o/sy~Íÿ×ðz/å“}­¸$Œÿ¼Éó—Ï÷Ïkdç;¼œã<þœË?÷k~’Æñ±Ï› ­³P¹¼Akã–^/õ2–óq¯aðH^üî'´ aðùáXÎ'6 ^ÒòsÓüê1nÏlnÏ.;·ósþù3ÂØsçëð—§„Ë“ Ïu¼?i}„Áš?×kròɲ6þù“·£Äוּë#n>Á¿›ã{pù[þòÆí™½žÁ÷F³ßGyÿàæ‹`|>úŠ6ÎÌf°6£8Éôj Ðëï7úpüîÿåÜÿ#y<ýw˜~{e˜öXÃåqç2ørîؼq|8¿«ç|JGøã÷rÿ/xƒÁs>»´ù—_›fG‡©÷'µþö5sw7$òþ¹Œ/þÊñ³x<й8•—û÷“Æ�ÿÈý3‘û'žLÜs¹ÿhk2x¿×ÊûÇWkãg>~Óüó^_­\_mílš6¾ýƒµþç>Ž/uùãK9¾€‡5[ÝÆn¿BãßÓæeÇ|'o&οœ/²jó·Oy@ÿ‰î‘‚ÿC¼¾Zùâ±Öïe„É"L\.ÑæÅÙ Öú½«µxçf0o~†Ïº†æƒaŽìÆû[^=†Û¹œ±rÆ¥=t¯9Ã:Ä0îžôû›ÓÇÝ7tèpˈq#̃Ó-ã ãò'Md7.wfÞæLžôd€ò¦ç=:©Ð–7}\þôœ)yã&MÍŸøØa3Æ=ÀñC&çæ&O›úècS ¶‰Ó§ žu/d6äçO—7}ú´é†Â©Ó'Mµåͳݛc›4#Ï<aÂô¼ÂBD ·M·çÚìÓu\á¤GA.Caž 9NÍ+–3#rMšú¨¡(·prÞTC¡m:þð´y"3§AYyÓJÆÔ)´¢Ó ùEÓ'Ùò Sò¦äÌÒ2É™<y|Nîã>)9_€Ç=f›UÀT›q…PD¿[ .˜žW0.wR>¹ÀÃ?WÒmþð�XÀÕvÆu7›ŽÏŸ<-ÇæÏæ¶þ>xÂ4ûøÉy$fî´©3ò¦Nš6ˆrÑ¡§M§‘ Þ e?š7}€#s&Û ÇM‡8­6ÈR>B&ÍNZp(Û$r¤§çåLðÙo/jèP+Þâ•‘SP7Á7Ó6='×6n•œÏÝêjÝ`›ÆÈ±bÆMÿX^.(âs,¸œÍBBít•ts̘6i‚ž4Ò°zÁ6SÆÚ§Ž{ljÎ8Æ üyÆ´Çó4‡é8×}LŽ3ù Õq¾4VegJÕÞq¨ÅóäH‡¦yž,#Ñbá²äBÚò 7†$w'8qþô¼@RDŸpZ´ï “ùka%œ<­0r±ß°¢Lš:aø¬)ã§M†¬…³¦„ÍH?nܰqý[­ÿ ×”+íåúrYÿQ®aÿ(×ðrÅòfÞ—ù‡Íƒ-þ¼ÆÒ3ud+=SG¦Ò3ud)=SG†Ò3ud'=SGf‚¾fð,[xÏÃ>sbNxÿ—äëz;b’6i:ô@XÔ`{~þyòvÜÁ`§ÜaïÂDGÉö+×'N›Þ!]Ãå€H‘7eÚôYåèÐî…ç±{áù­^x^KžÏ’…[²ð<–,<Ÿ% ÏkÉ)ÐÙNËí°ÏÂú´üzˆI6kZ¡/øâ@Gü†5üAqTdgÙ<åA舦I9S!$vÜŒx¡öñ*7sú4ä‡ɤBÉi@Xu 3Ï›‡¸ææMŸ‘—®T:PîŸgFWù'ÙÝH6ì(£9Ó:db^îã…vˆ†÷ZÇÝ75}Œß›:i¤‚[îD=PSMóLYS'‡Í†˜Ž¢°}ª6¥è Ïe2òl§M`Ãjˆì0¨ÇÙ7êæhDæžÇÉ&‰Fçæ ¶I8šÿ'ÙXñg¥áŽžUT¦ÃvçT ä£qµù4ë܉ò ‚Ìa(°Ñ8|Ü»-o&çñ�”}*! gÂ|!ß3ûÿ,TÝS‡z€9äŸ4•æEySg¦Ø§NÉba¨;57§QòÙ0Ÿ4“ÏN4Mâ ˜©à ãò&äØr`Þ3¾°æh9 ͸¼©¸•Cß í3Ó`5�ïæë:­øÆt·à›8Þpk�Ž ÀÏÌ`pl�~ÇÇàK9>!�¿€ãûàË8>1�¿„ãàË9>5�_ÁñR�ÞÍñ™x•ë;*�ßÊñÙøvŽŸ€7pþøŽŸ€åø¹øxŽŸ€OàøÒ�|_Ž_€Oäø²�ü�Ž_€Oåøò�¼ÄñøLŽwàGq|}�>›ãð9¾)�_ÀñÍøzŽWð߀ŸËñíø&Ž7ô ðŽ À·jþ€o×ü?�o¸—û�>†ãûàc9>5�ŸÊœKx•¯+ À—ía¿Ùø?yþ‚0ùgàÿàùç…É_hž¿,ŒüKð|_î�¾~Ëéâ¾ÁT£€—¼¸1SÀ' øQ>MÀg øL?QÀ‹û üh?SÀg ø¹~²€Ÿ'àçøRÿ´€_ à]¾LÀ— ø%þu_.àŽxþ ïðë|½€ß àU:ü÷“¶ ø+|»€÷ÛjõátŒ€÷¯Æ xqj¼€ÿ·€Oð}|_“€OðâÖÁ^Ü#›*à“¼$àÅ­#™ÞÏoüm>[Àß!à' ø;|€Oð3ü`?WÀðó¼¸o·TÀ𠼸ϵLÀ‹{n—ø _.àŽ¼þ>ïð÷ øz?\À7 ø¾IÀ‹û¹›¼Ø¨~Œ€oð¹¾]ÀOð†a>|ž€Žðù>VÀOðñ~’€Oð ø¾þqŸ(àÅý¿¼¸?:UÀ?!à%?CÀg ø'ü(ÿ”€ÏðsüDÿŒ€/ðÏ ø™~ž€Ÿ+à_ðó¼"àKü‹~€MÀ— ø7üÿ–€/ð |…€_À»ü¾^À/ð^|—£IÀ.à›üR¯ ø/|«€_%àÛüjoø¯_! c¼b¼âüFŸ àÅ÷Hú øJŸ(ૼä8£^iÅ·ìA¨&¦ÚàMþ­gwƒ÷Zþ/…?ïµS«ô4{áïÚ}ãÀÇÓHðw㎛àã“O9ÁµãOÁÆ×¡<¥¯BC±g.ÁKFQ=„0¹<Ù¿‹0¾‹åÉ$øu„qk€'•àÿ!|‰?‡0>aö$ü¸µÂKð „»!l x*¸uÍÓzá|„cI‚Ç"Üô'x$Â=H‚ïE¸'éOðP„{‘þß…péOðm_Lú| —þÿáxÒŸà+¾”ô'øb„/#ý î†ð¿H‚;!Ü›ô'ø\€/'ý nCø ÒŸà£_IúŸEø7„H‚÷!|éOðw_Mú¼ákH‚kîCú¼ákI‚W!üoÒŸà¥_GúüÂ}I‚ßEøzÒŸà×¾ô'øßHúüÂ7‘þ?…ðͤ?Á3¾…ô'x*Âÿ!ýÏPý#œHú<á$ÒŸà‘ßJú|/ÂýH‚‡"ÜŸô'ø.„“I‚oCø6ÒŸà[¾ô'øß ý ¾á¤?Á#|éOp7„ï$ý î„ð ÒŸàsÝN!ý nCø.ÒŸà£ßMúŸ¦úG8•ô'xÂfÒŸàïLú¼á!¤?Áµ§‘þoDØBú¼ ᡤ?ÁK¾‡ô'ø#„%ÒŸàw¶’þ�cÇ47e èhÈ’äƒöû%GÊÛÃ`û—¤¤\ÝkP/�µZºJwŒ$ããÝ%)FÜHJ¬äÜeÿhž'š.’+¥ö$Ðü4µÆý€Œh¨†dww^€­§äJnr¾ƒ9o™ Ã{Ýþ¸s§® A¤ç´ª†êRáÏuO[–£=¸Íˆl¨–ÏAΓ@¤–žòzóóóK±I~DM—óZ­ò™tyJ;tÕ ’ÒY’ÿ´ÊßzK1Ú[•è>’¼/]>äÍL”œnI®¶%ûéòïÞÌ,)ÀPÉkMW¦´§+ 0.5˜½°<1©¡š•e·ƒ ­Ò3‡c¡³wœŠ�IlN·:NEÚž’\ ±s4Ë’+îß» xÈÇ’Üñ IþY½3õ¦„½P¸jÜP ‘×^Á¬ÊÁ$7Ü3~ÀlÀOÄLc•äV÷ž$fRÂÞ–¯@^=÷c,wK#ª¡ÿË’œmXUqh1%îæ^Ý  qß(¦qC’»še÷å——äÓÔv£;‹°´=’…FF’kÇ4xî8ƒ^“äÆZ}µ’͇c©¢N[åÀÄML°l¨"ÛE’Ò»Oº2¡O êQºü²Uâöƒÿz_FCKJ”Ž-ñÑúÈ|þÑå5CAàà*vp•ér1JxK;S¡Vy³Í…ÃÝàeŒ{’Û’äõ¾<J4˵P(¤~«¥zKÑü%nÛ¿Í«#,¦/ÿ¶æÖZ#6§Ë*TZTZ3¸ ¸Œ*°¸UAú)Áô1æM82hYÇr ÊaßÇRÌÁ´™7Ѩa60ÊÔ'd&<¨gê\Æ*–ò—)(¥|è¹`&Wò4ø±$A÷]�ýŠÇèe1_oÍhhjÅØ4š¬ò_hv°wª$g5JrF}ºlAÃ2ÿÉ2aõ±’L% Ôd»ôIrCÒ$äQl»¬Ö±šjM%ƒ#Y&Èr»ž%ÉÍ* d°u\— ¤ˆjlèI»Òå6äYê6¢Ó» V¹ÎÖƒ:e.®wHJt‘N¯íjÈPo°¤Y ÷�5diL—¥ÜjkD]ºÜºÂǸÙÝt5t=_õ|ÒZÏѺžãLaõì”䊎Ҕtõ¦Ð[Z/èj*¹ªëÓ ·êpÜ·]ƒ¥}Y”öÇn¡¥ýÜ K[Ù-¬´3‚’BIÛè'­íœ.-¾êyàüR.ïÍAòÚRuYSCËêT œ¿©ä.ês-n©ŽFûž[ôÂâöv 2…³w)J\uprIi„`©ý]C•n{Ž[ik×°VÊ Jò³m¥d–Rý,5ì¬n©¾h©~Si)áKÛß埖ÖäWÚgôÒâ±´ÍgXi¤ü·]B»‰óœ.ÒÚ ru‘îëX¤Í>‘šýD²úDJ@‘nÕá¸ÞW—Y”xH‰wûŒØ7¼Ä /ú§·úIüÆi]âÄN|xÇâeºÒYnE?Î’’<ùÂî¹:Ù$w†²�çŠÚÈDŒÏŽÃ}¡›mVâ’.�*¹¸U’íªœñcšlirî²€>|Q›×» §~êü?iÐð "pn#¿§b`ˆULø”b…1QÃŽy½N·i¾Û´Ê-¥·ÀÿN·}´”ki‚þdµ7KJq«’Ñ$¹Ò WHŽæVéúI‰T²~”\÷1IŽ€›¼¥¨šÔÐ+å~ Tª·Ò´:z¸ýúMsZcq†"pñSŸãõ óUy½Šq8+ºt¼Y“k;w70¥ÜDZA'TJý’˜/`ü£ÛË®*É1š½²š5{µÙŸFÝ’DuÒ]i}LV¸oéŽCBÐÀ[©D¿šo–£3QüßÑhn ¦>ŒFAeÅøzg?ÍÒëI³xÔìž Y×Y˜¡ÁžË5s%Ÿ8¡U×oǨºœðU×ÎNP7;ŸòC'ª®ˆzû±º®êŒÕõ/*ÚTr=tö|¼üR'Ílò1BÌ褙mæ1ŸÙÈ^ÐHhD…¾•FTövø*u›ÀS¯Z’ÚXóŽ(p‘ЙÆCiØv”P£Œ8¦ª1»æô‰r½ê§ 0ï1[†Üš&ÿ‚͇eOãYsk×F'.8>OÇFÂ~éÊä>±éŠ­O< ·Äl`ì¿jÉØêØø{øE’»¥*ÿë÷Pçõ³†×Oª<©ÖÕu´À(Ù"7'5Àe¹;¢Üs]4-]þ¿VÑÂT¼¦–©xâ˜OEôwóôø„4ÅØÃHî#c{Ó¼¬=R…Þ_°€ƒ8ßbѱÉ`3·xÄÈ8r7©‹x½r´À¿EH.6íßZ¡=Ã@HorôÈ'ç†ö� !R>,š½¥ñØýÉU1¾­·…f=u3ÉÞ~Ô;Ap%e fðÚƒø¬J®¼Ö1懩IürLk»R“h<ækë£Àÿ×>¥>ŠšÄûˆšwXlÝ¢±IÄD³&Ñ ›„5‰9QZ“˜q”¹QZ“È#„( åÊVj(ÍV¹& ÎÜ'æ)‚À|ÓTÒ8gÁðÐÒn SMRY‰LÚiÀi0.‚*å{(óºhœP—ä6C#Ÿ�3‚݈:R’÷°J÷¬8§Ï‡u¯ÕxJ+¸,ù°sL~L%ø~HÒ.p@u3˜$å­„ÂmѾiùo‡Zt÷÷ÿUFzl§iàø—V“óÜPÍÿôQrüO„)läBÉ<;Ïió_úÚr }’«~ÔÂÌ£ÄUDcÅ€•’_ìÎf܃©V`Ž;1õ8£>†“êøtšÜ¢)tD¼ôY5ëo†Ë[)!e1TÄ y-‹¼L>‰ó±(b1#ÒÏ!J£¨Ùà‚@x«¤«½±´Þ²Ý`X‹+‘I%Fív„Üæ`5}ÇèQâ«j.øoÉÀ­ †t@’¦Ä0@GçÜe/Ô3Ý€• M_±ðÀ«®ö`$ÞlÐ1´‹¸Ù•T÷TbûrÓ”®³“sÉŠ)qÿ!­w ©ä}Yi=#½Ôœ#ŽjíûÏß ÑzÄ×¾5ÕŸ¢¢Ê²±QKT±}ßbÀöÝ×ÀÚw2¶ïÑÔ )(ƒ>È�k”ÄYÉÄ,LòÜ˺„IÀŸÉ“û;!2A]€ð\Ù´õó«ü º-YÝìAêÉÖ)Ò¡Œ1@[2YewºüK†òÖ£y Vú0úö+¸ö‚í¹Uþ:`ÑåÖt DÉsGši6[#9d%Êü2y…¸ ³âw¿UçžH—÷g‹1Àù Ð fÆjí—Ñ’@rÀA&jn,ÅÖc-n™@YåÆjÍŸ0ïÑsy5.?¥Øº ƒµøp¸°\ÃÒLáZZQµkËÉ¥a©Ÿ EM믷ëÔãÃRg†¢Æ‡Ëž“ç4êþa©¯EMk³ëtêNa©Oœ AÔ=Å:uSPº65­#ß­S–zA(jZ¥ŽÐ¨ã~¤<´`”9 2\ÍÐÛEô ˆKw9ö¯vTzêÏj„¯úÚ/aèwø]¸–[”†¥û™¡ÏœñG×3ôOþèµøôÊþ¹Ç|V �z{xÐ<Òœ…£¤gãù&ò|\Bu쪳l£q™¼Iò©Îïe ë’¼!†.2§ËÎfúq5Á<=ªÎ«eÆ5GŒœ)8•ß@²é¥l¸¹àêš«×æ+(¬E§ÂÅîûa¨õ ޏÒi¸…î4üýÆ #¯tÂ&ž.ÉÎzZø¥yÊ¡VÄ¥Ýù2‡$|üfr-"Q\(wŒ@••*Õg„$ïô–N4òÉo©d .ì*ó1#ZQaªÂøC‰‹:+¬ÜmˆÐ9Zsê‘35€ŠÐ3Ã, †/Æi'c¡Œ†ª›Y]€•£÷š½›¹¤¦AÙ”Ñ~ç¯&gw´‘+« AgÈí#Éúóñ ¢2ŸôS6  Þt€îcX8y`XºJ-:q†·x ‡ry÷S±ç&§Ø*´Rî8mr¾DëÈ.71›ßD?¬Î9ëc¿â(íQøLe;(b09ÇFãän � {K®Ë‡®Ei“ñG^OþAN¦\þ:d³8À–’Üí¤Zb~¥ÐòW»Zþ%Ë6póÀÍÅwXsa0梚U² ÷`J‚ݤÊfp÷11’Ò?&©Á;">ˆrN­¤¯J8¶Áútå5AÏk~Ä<Ö<N¸XÝÆÝqÒ¿æØ*pË3 Ô2D ËĦ’cÝWÀÝår—x°Z>w‚ékNàÒœ�‘£¿÷´QkÇÊJ¬ð�ó<âän‹5´¬� Yë¢kY3‹;8`g•{Á¨e!+ W­$÷ªö,�îø|$ü#.m –æð(Ì Ä8€|¡™\§¨†ž§ðÒÕÅXGþÖóY¥g{(«8?!q´LÝBf*™Ñ‘éž8…ú& ¾Ìr‰ ›§’|€”P\ëÙ£ž?ª¹*L‹šŸk4Ó°—9¼½Ð³ˆ8–ÊX‰)žK¼ÙüàI”![ñj­?ñˆq•œœñ«6f»ûB úÕ7fë òª×>¥Ùº"êÄ>qÌö$ £­-–Æl”س þSw@]ðÕÙ„7¬ÄÀˆ²D>£›�”ÿbµŸòuûÛ5š”w‰Ê?p†¯¥-D•ØÇø2<*ƒ©äYCˆ®‘ú>´×v|„ZšH݉1þD¬áO³©{ô>ÏEzöKD…³¬ÌÄcÒ³µ®ÿYïÓ”DuŒÇ=/œõ8ðW1m·…ñV*l�ñÛ?c Ç™ˆýÌs8$Û«}Ì Qý/T —x0ò$à³M½CYóWˆÅ6ßEý>Å.¯Úp€P?[DS¤À(au úúÜ'ÅÊOGšìëÿGªÿ‡+õú÷œóÓl׉õú6Õ«(ÚWtíÞ=2pHû@¤A'±ƒ·lS2¶’µ º^Öc»Œ\·”Û åîº"{+Dú$Wz}̽®4w,@êÊ_ù,:zŸ©ÏLÁ^Ü»Ùë–-Û<?·ëÎC‚³e€v¡‡ÁÇ”q/ƒb¸¤}êÅÖ²åZšžÚ¥S›6º¡®”n» n“’Úê`G{|ѽ`Ì×v@ÄY£Ÿ•"½ÍбfÈ}j=ò/^s\\Æ UmMˆJ{•ã‚¶„í”™4.ç/w3õÊ]jCôÌ +ð'‹-Àð–.÷©B’œ^C6eQ0·Ê[Åð¶ÊX9k›§òïðâ¹Cˆ‡ú<3÷9¶ ãb×û6ш‚u.ê,f¡Í]—ŸÝ@}ì¦À€îվ؊ŶÎsð\x¢úDô˜®¼¢ÆDô´lvDÍ!ˆè¹ÉЈšBÑóŸî©!ˆèÕgµçûhs€j†Vìh/\‹O¹mí7Ø.NÚ¥/ ÉÂø‡ûwóîãÔÚÞÈÒn HÃíwö,mU«-í XÚi¸Í䜩?Lÿ= ÝDé£Â¦÷¤ôzú®€ô^”¯§–ÛêL%m`mÏŸç|ÏßM«ï¹lèhïRÔ<ºÿV¿6\ÚPó¨ê† œýUçœÆ`œC=Ü aœØ<šÁV F”Úú{~ßõ8†óƒÂÖa}A_º ¬¿‹ µUÄ´—<@Ûr [k;†}Xºò&.ÄÐŽ”àý(­«#ýÙêÈø?bÙj]\3XÎ\Á—WB¬¼ö}ë#¸FŠ;T\C¥ vÜÑÐÌ¶Ž¼‡Æ*·Ñ|ÍÞăHf²·ØzùÆXmÆ·€c¬ÊŠ>ó4d½†”kp™ô7Bòiv&$µªŒò1þ}Ùö’ætäR¦´¦+vþ` ljGc‰!>VhS݃„ìñéæÁ?XLHN;ŠÞô ˜J³Ûýò?Ì‹«ªXdlgZýõÌ '4Nl¡¥ÅO`6C úÒâò&¶öâ`mù¢ç—=Àìõ¡$v„U ‚Yêç.äO&z;üîQo&è9KÿÑ~g›UþÖ*ï´Èµ¶Dæs÷öMRmƒBïÓàýG]$›9„ÉÂÚb·Ãz[Ô¸öe)ŸR@7KIÛ9c[«l1´<Ë×KZ9Ъ:>‡J±Mb)Ùïx7nN—L›ˆ¹çj½·¸IÈÃòÚ.• ÆS>ÜgGÓp¸?ЛӿY´`„Ä÷‹ÄRñ¥Är¶£a*@\c7D<ßÒYõ fuïÏV š6¡ ˆÛH”q5“q˜Îï×߃ø]ÉRF üØÐÙ¢#L^LxK¹^ Ä‘:Ñô;}©©4,i›à8‚>ÎHßÖI7“ÞÂ`ußn §Òå?DgƒI÷7Ϙ]Ÿ)A|ì)Ð"¿;„-2ùÑÕ8ê=%)’ÜêÐ|“Ú|lÓ¾a¡„xóÓ‚Èêß~Êáir5´¤xö8Ÿ…ƒXͶ~¬Oþ¤ûm ^F³¤`z->ËN3­ªVWî¢î4;ÓRŸTÅ™œ=Ÿ=�,IÔêÝøÌàeJЂßó =3è ?êào1ƒeX½Æ‘CÚÉ!²Iò7‡´’ï„âp¿cr÷ÚtÝ.šƒV|뛃.:uáSÊÑTFTA£8m;„sÐÖCl:Çw#½<>Q¼aOÒÀ< zq{:vhY­¶Û ?eíM¿So·A‚޹q‡«Ý•%^’Œ”C¶{€‘ÞÝ…6˜|H7R;éê%d¤£ƒv—ƒîcÖBaõ¿iFªúšŒôåo𑾢r˜}>ùF³ÏÛ_“}|ã³O ¨§”þFö™„¨Ì¢}¾û íÓø³Ï=hŸ1Ì>©¦mYVùk×Ðïhb-êbï¯ò…࣠«’Ôàù¬n6m¼€='KUÉf•À–?WKrcÇo6­æ9.f9P¯–™7^ÃP‡•o6õˆ€€”lã,CkÁ²b}BÖ¯Fg³r`—1ìlÄÎcżqˆGÉ€´*pi˜,›¸,Ï2º~Å“Ê'5˜ ,í)–vè`¬Á4¿Òs …wлÏ_üÖB섆UÞ†.! ævVù/µìk´ÔªlÞ´±U}w>V­ªèÇäuÉ÷þ†Ý„rиÈeßDïžžúÅ[ù Z•«ôý²Ãù“χ-dzg›Õúør# ˆŽ¹oF]Žö¤¢‹¡š¯ñ¯f61>q –? ­h@-00>†7‡àæ¼ÙG†KžY>.ú®Û•ÕÖ說ˆi>…›þxó>Ü܉70T7^†7/ÁÍ'ûáæ¹�{ƒøYLxœåörëÉS¤ÝÍŸ8 ‘öN žäíTÄï@ÞPÍžXe¯ï‘Àj)ød!RrTÅJø´0Ë-ÕYèý%ø-ç¿KØsW*›&Jœ-õRÆs,`9Ø,À1k ødk€H£ Îv´w‡ù?ÁÑg*Qé&ÚT2šãF€¦Î]&§J«ó+‘d.­|�›TêoœT´’áÆ«’R�³ä:5yMÐ(ûZžývZÆît€–±¿5â2öñýÔ±@½^þô"êu>Äù‘2P~ÄJ79/‹ô8®èqp…f·Ÿ+HнäX’îŠþ‰¯²*h¿¥²Íbuugn²ºzÅZ#À;Õå»i†Š&§Ü‰fÝs—ÒCþ_¾¤§Þ FÖ𯅤£c6¯›U†D[’Ú@òYÌÿÈ.iòqI:J‰.u´÷(º¸¼( Åm‘ S¢ë%Ç�ƒí&@”s ª7Ø®J‡[�—$5pq‡)½Àš–ŠaJÿú!^¼'õÍÞÚ´Ó£‹H+£uök#‚ÖÙ•û£“Úp/Fåþ(É5ø ©R–*›£õG9ôìÆÖ)®PßÚF hÈ<© (ÏCR—«yDDfQ†GãküáQ"Ç)4å’–nÜWH…‚hÓs³hãg».¥ÎÛ¸• ªó3/ìf*¹ _x8W´ LZº LZ/4ÿR´rR³–nÜJa)¾Y K—³‰ŠÊZÀ ®R¦š·-±óñ¹+Ù³]‹_?l¡øÕ´Ý¿j~†`UMø”ÆŸ)~}‚¨Òz1~]ÜŒñ+¶™{°OW­ý”àûIê˜-ú’s9½É�A_0w½g&æJÞ¢-D×±…èL¶˜Ôç}˜"¥ê ‘wÕÓBäìÏQÎ ðÞGx^§•HlÑèvl—³ç³szµKŠ™z‡[Ûgå™Eè,wšÜ Õ¤U0ðS½[õVNhz>´òL'Õ,ir›äÔ d†wFq_ü‚Ýl­núÌ,)ý[µÍ~,¿ÄÅø+Ïêî%)Cb=c½‚°Ô§+O@”™43]É™˜.ÿ­ÚB ŸÄ^Þ ‹ö9¨ÍuÄ…? ˜¢~á1÷¨® ˜™Ê¶iÂì}¥”9žB‡»ˆ6ë‚¿ÎÇN“Mëq@ö´œÖ+:œzžAÓo$sÆDõ8•N$9Ò³ú4“zg-k¢úy&GyèÉ\K!×ý1çÕµ,çÔÓ‚mìõküÊ>QÝKå®dåvòÜÝ!cìnÕëëž‚Œ¸ô¥KÙ‰g8~*¤÷ðäݬ¾P§W$•Aëéyй`šü§NÝ„”ÅõÌÏ$¥×^´Ò÷§µœÇ ¾rŒõ©Ö»ž{q=Îüž¹„XGO쑯Ê…®_+mÜf­)ø¡+À²|ý! £ ¢“z)ã⇳×r= ¿§×épêîÚªœ’Œ“œM]t Wзê÷Ôô¨ÝÉ1žêSáes1þYåøÌƒ¿T 0[‚UˆÊ®dYH’t¥ÐvãM°Éû2.W[åè>V9í'ƒç¡^Rì?* ‹éÆJÂuã–×þ!÷˜ÿ+w\Êöôë  ˆÿ¿€‹Ñž]çþq ÿ×p‰ÜóÄ?/ ïÿµ�\§—Œÿa‰ÿ×ðÉŒç˳Ἷ¼ ÚY·3z÷EœrX°W‚ x#ï%4önH[S´ëè± —wñ^¡“ˆfΨ¯à4Xhð½›Ô§vñ~ôïprÆÔøºw¹ÒªÜcvŒ¡a`£Yl`dao ™ØìµíÉzàÖúÈ 82lYƒÜ ×©SZ¿äÒC½¡0s_Vð F5ºNÜ`Ÿ Ó1”ˆ›ÓÓä¿™lhÀèö Û)}Ô^P )©@Ö¥ËOW¢ÏÁ¸x—Tòˆ˜Ú¦W;ÄWò^u®k¥grÀ”Óÿý¼m¸vŠ §ô6ß#Mìý<œÚ¼É¶ÏYå=’ËÞ¸6Ú¡¯>':ÝfÓk›-Î#´[ ÜrøÖ½O²íFÿuoH™Çw7†Zÿn ±þm¿pýö{œ„x}º| 2á*ó%02P9GaIJ=ÔÁú­ñýoh{ôbƒam¢¸Œ}¸‡Ccqhk°-Á7ž•âú¤6µ'$´,àãY¾ºäofüoEþwˆü_¬¤ò¥PãEŒjRÝ"R¬ä{KÙú·\O‹MßÉUl [FЦ»2ÚÕ»¾'gmö‡Êÿónž¿[ˆü;¬ò."9)±ø&n·ù8íUýÎóS“×Û�cÛÍlFØéèTæW2ÙØú@¨òžÖÊ{ÈóM=|†ÎÿÇ.ž*äÿÓ{¾ü×iüïjÂÍRúb~¸ü&]ÿ¦d¯=š<ûö°e4¿öô$oš ˜úÈM«,ñiJF¬l‰mðÌÞj½ç;Ú{þôFm=‘?]²C#üUñä™ÃøØƒ6럳õ�"õÙmØ¢k’ÜÎ_m×nŒˆÀó‡ÔK6z½üÙÍÕ˜ƒÈã›pÁ)Ä#"˜àÉêËëhŠñ†=Œùy£!'*êJHŽûÀÀ^Gÿ[]¼Z> è?è}w«|_yG9å=ê½ëq†e尿«ÎŸG¹âÑj+?Çã_àu¬ÀÞ¯ô÷ß7Q3˜øÛo‡ß’ÙnmÉŒ› ì™:b+7Kí_|zpóÝR—c9Û7žÇH“×’Ì?Δ¹’[Ët#½ ¼Z>ÕÖó`ž¨$¯úgŸøú3®øf¨i²åWg[.¤ãûw×lÒf±që‰[M¾Yì¹F˜²ž%ˤ\H|Œµe8‹Íüg±Ã¾¦¥ãë!ž´CÙJ+òv•Œñ¥³_ÙKŠñª¯ý–’÷¼LMxü¨»6àKgј¡ÁI¯ÒÍnÔfíöu4kߨÍÚ'¬ã‹ÉL_pûïÒÐ>Uj ž¢:¿÷Áée#Ð> úÞÆ@£d4[@(´H÷šE¢˜O6ú,rx'¨ßBvOißIùQ¢EîjD‹ h<¯E È"¦F?‹l.%‹¼\Šï®ÇWŽCN/·È¤úþÿµd‘ÌúþÿµZ¿ ë«Ä­Üì�ç.ݮڠéÛ“ùS쟾gv€r§×¾1Lß_U¿ZÔ÷Þ¨¯´ó¼úN$}¯Üé§ï·/‘¾ïÃÚ¸ŽÞ҈؉›Rì1úz‹’üÄMïÇ×Þ£vhz^¢?dþo\µ#„ÿ»}þ¿^÷ÿ5Ìÿ× þ¿ý¿‚ùÿæÿˆÚ²ÊÏÿwÿï8¯ö£˜ÿïð÷ÿÿ1ÿÿúÿZÚ’µƒ¶äòúžµ]Ó»°‚ôÎÞ®é=¾"°¾cíöõí·NÓ÷ZÒ+ùêu>}»¡r]™¾—ãŸÛ�µw¥¨/–¯Go?¯¾Ù¤oÒv?}¹Hßåð£î_ƒŽz1NL[e›¦­s5i;}›¦­mµ_{çú~·­C}‡¬ÕôM^MúÞºÖ§ï¨Üå„O¹ø£õû Qß‚m¨ïcÛΫo&ékÞæ§ïß/’¾Õð£¯@}ûlô}{«¦ïüU¤ï³[5}KVé›Z6)qû!TgigËöÅ꼌C�¶ÂàÌËÞ“²´C„™y ½gåuÛ¯%Œ’ÑdZ•uh@¯<\^·$#žrÏÚ¥ŸÛª‹Ž¯O b¢Ç è·“è“(ƒý dŒâ¥limù ×C¿¯Ð,½u%Yº¾Âgé[À¬Ë ŸâÞB–~Qs—‹–î´-mØë{Oê 6âÄö¿Eoÿ+Yûߢ·B HAí?yÕ–ŽãŸ.u“º‡ õ¹lÿ+XûgR@Ô–e~í µÿ-çõ¹¬ýoñoÿ/°öÿ¶ÿUÿ¶ˆñ¯A+XükÐãߊóÄ¿+ÃÆ¿†âßj=þ­`ñoµÿê1þ-gñ¯žÅ?DU|åÿ(þ5œ×"å,þ5øÇ?™Å?ãßJŠõbü«×ãßrÿêõø·<Tü«?Oü[¥Ç¿å,þ­â_Æ¿e,þ1}AT}¹_ü«§øW^}—°øWïÿæ±ø7ãß ÿêCÄ¿:Ýÿ—1ÿ¯ÓýYñ¯®ãø·R÷ÿeÌÿW þ_‹þÿóÿ:æÿˆÚò¥Ÿÿבÿ×WûÌÿëüýÿyæÿÏ£ÿ/gñ¯Î/þÕêñï+ÿjõø÷UˆøWÛqü[¡Ç¿¯Xü[!Ä?T®+Ó÷²Zÿ6cü[êÿj)þÕžWß2ÿjýãßs,þ=‡ñoÅ¿Z1þmÖã_9‹›õøW2þmî8þ-×ã_9‹Ë…ø‡Ê]^Îâßfÿõû~ño3Å¿ÍçÕ·”Å¿ÍþñÏÉâŸãßWÿ6‹ñ¯F_²øW£Ç¿/ƒõE-k-”¸}5Loxƒ±ï7­2̳ü¡«jüŠŸÉŠOsrÿQŒRûÓF˾ýº®äK—i–ºèK²TÌ2Ÿ¥NTƒYþ\J–2PÉÆïåþ\´ÔдTj ‹_ù^þ?k¿_Ukš~¶”4]P­iúÖR\2ó‰ãÓ·ÎB_„T’ÿ¨öU³³Í~7ª¬dËaÅØXí§ñs%¤ñð£>SŽey5”ÇM«-‡ùùgáú¯4}ÿEz%ÇåÓ7•‹búÆRÑFO ?õYúfV3}'¡¾Eº¾•Uš¾ë¾ }—Tiú~úî©cÂøë«ý�1êl•OcˆMöA4šÁuh<®á‡*?¥_vÒÀúâ—36VQÌÈÇ—ªéœ†åš¾7~Aúö-÷éÛ •ëIø”*Øx²PÍŸŠúæW¡¾ÙUL_\Ó¤£K˜¾_Wjúnýœô­¨Ôô]K”Ão¿2-ÙðíÊöŸ²ó?Èã-*D±?*Ekìb [c{¥Ÿ5Ï’5îƒõ©¥Q¾¬d%×½˜Aú~©ä²ÏÉ —|é3HjIø•mTÝ€Úù‰h¬J4È}•‚àcaVÿn½þ?cõïÖëÿ3¶þ¦úÖÓØ¦¦2yõ9·àðnû]‚ÃÿìöÓvþ3¤mü¨¥_PÁ²1‚LÄU»Ã¸ õ¿T¯ÿÏXý/êuëù«7«ÿMXÿKüêßMõïfêâ&QÏL_ýoÒëÿSVÿ›ôúÿ”­öéо&uóu[7ù©ûÑ\Rw:ü¨ïŽ=ÚnÊ`¿HÑš7ÉcÖ幓ÉÓW—ç†Oq}ò0yÁÕ_hféõ)™¥û>³œÝ68ó ™å‚Md–ýˆjøX4Ë}›Ð,ÖMÂ6ÅÌëýbûVŒWl ®_ÝŸ±\AååO“ÊOúô3T¹™2Ø'òV…õûÐçš"ÿ%“¥Ï}ŠôG©û1ER7’"—"Êà§ÈsQ‘¹…ú}R¯ßc4{¶,!{6mÐì¹wIpûöÅ«ü:ËQÜ'uÓF=b¥RÄZmÈ—-G¡îýµÝüÿ>…ã_¬)ãñ ¤íc¦M–£Úzfò¤Ï4}Ç,!}G}æÓ7 (Ô!„OÉÜ@ú^¨ØD}_߀ú–n`úNô÷ç]ßÓ“¾êzMßßá“E[6­úš¦g  ßüë¶ ã“y–CÎ6›|ü©¦Á›“¯êÓàY(M}†ð)ÿ[O<Ѝ{?5øf=j°c=Ùç_¦öCÚÖUõàÇhºåëýl;©˜l›\Œó§O08¾¾^˜mu]¯éÜ™é|b¦sÛGâ[ÓOÎ8 ¡êõþ¡*Ç´Ê‚' µÎ‹„ñŸ¿‡æ°ñßÿ-ÁHÕk½0»QÖéã¿ØøO—Àö‘o<„õÿ‰^ÿ±úÿD¨ÿuXÿ±ú_ÇêQ±‹ýêÕÿ:aÿ¬¶¿Ø´ºAT1nÚ:ÿø3ž:¨¬Ãr,ôÿëü442 ¿ž ¢–PôIZ<Ÿy­¦éÛ’¦óÖjš¾ð!j <œK4‹>$mK|:fC~uáS&¯%-ˆºi‘¨ã²µ¨ãçk™ŽCQÇaö¿R{Í€îçÙµ~}ÓxÖ7áörÐ6g­Ÿ¶—Ì&m< Böø˜¢ÏµâüeÍMÏå‹Iϲ5šž,Ö| 8``ù”Ťë¼}ºÚ€F-$|ÊÜ5¤ëCˆJý@Ôµv êê^#èêÛ/Nú7uÇþõý5~ æHtò 6Ppعkü¼íIR0LúÛTîa�¿·BSï›E¤^e…¦^õ"ÍaQ¿5iú}¶ˆô[ò‘O¿×€H}•ð)e¤ß,De¿/ê÷[ê×\!øëƒþúý úAȬ¬ð×Oa͵û ÂO»Q³H»>ð£ÿµsVÚY­i×öiw`µ¦ÝÁ|­}¨¯}ÀÖ¿>Ö¿Vãú×lýk5[ÿBÔÜ2¿õ/ÒÏ ê—å¯* R‰ûyµLF M±fµŸvOÎ$í†ÎÄõ£Å¨ÝÇ«í®Ðµ‹gÚuí:À›`¤®Ö‰÷I­c‹}jý´ tø‘ð)žU¤V¢>yOTë?«Q­ëW3µð#zÑ5äø�Û_räj¿ÉŽûûe•ŸŠo‘Š¹ð£¾²{ôšUB~ç*MÅä÷IÅ«Wi*öy߯?½t±>ÿcŠÆŠžX‰ó¿26ÿcŠ~(÷»~ó¿U4ÿ[¾ý±xqõª€xÁk0õ‹ð×oõ ÒÏ?ê²0^üºRˆcVjúe•‘~ƒWjú¥•ùé7p‘>þ-cãßEÂø•éÉôKXÉÆ¿+pü»Ðoü»’Æ¿+CÄ ö<pÚÆßÛº,n’ð”eÜ‘`4¼î’d˯¦’ÿáÐò+{Çï7iTÙ j'œxã©rØ ‚ÀÓ¶"–Nÿ²+Y¿²mvUÊÝ¥Ö.Dwþf…n¬&0Ö v2ÖH;ÆÏ÷)ô¬"ú“ ¥Ó¶ÆêuÔ$Сyúûºq—®`ï+â;ö)_á¦UÚ?›ñ~þ×»ìyëûÂù_Ëñü/§¨ËÙù_ˆZòŽßù_+Ðn}W0»á4ÏioMò;˵ê{ý]B8–kÕç|—gãíc ®L 5{3ø¥2WİùÖÀ|=�Ÿ;NEجO wœŠ¬ 7–SÊc§¢l·ñ§áò×ø®¹ ¿T­m¾òÒñ§õçà)PB>Zž©Á|d6ç®–Š öþ}Èg§F µÆ›@»œúº’K¡\íü*0[°Ží Ú×ò{¯…NOÍ'€Ð~¾ÛÁ¦¶gpÎâ–½~/Kd¿Ë†Ù»Þú$e÷–ÆDp¦ÆãXÝ®¸ÓÀȶ”eŽÇÌ0ÁztuÝ7‡Ó´Fj4¯¯$ï3¸5›íÒ_CÅÍ q?‹3XF€×’4îYœó¼(ód&͵DЛ@ÌDIJǹ ƒè©d!¹6–`/$¶/#º±ÏôdM¦‚–ŸÓ$“Ý|F÷z0ݦQ ÑÍÖOëšÀþbžD·+® ¤Ýê0<ƒµ“›yVîh´à…Ÿ€ˆ ¶§qçÒ¹œ9«‰]> ºrzPõaòb~SÉV`ÑtÒsåWÌ®Ž`û0ºÉD÷ªNWÄèŠ]apyÈqsˆnŒN7žÑYÝ#ÁtË] Ñ]£ÓÝÎèº2ºkƒéŒîE¢k9£Ñ]Àè¾-'º÷žráÝkD·üŒV‹Áï4ŠÛL÷VÎVw+žª»É@o‹ª;ãÀÛãe.VÜz1!î%¼¿]“9ÙÌîÚ0캖s?@½='Nk,>t±8þeh{¿ä,ÞG_é,zKññ—aòEËÛÈåqKÜïä‚k/Y8î�þ`sUØ /Éþq%AtŸ2ºK.R§+ctÑŒ./˜îIF7›è¶œÒèf2ºmK‰ne0Ý0Fwšè^Ñè’ï/2OüÒÐ6,å¶19Á6÷é,®f‘µ4¬…ïиI<è ¼;•,|tZP“X@¶ ü¦’¦“Z±ßO *ö·/»õ ^ìïXì+—¸ 4ÌìÚ0#¨e¼ÉŠýŠÍÑ‹}2¸Ø¢ðÅ>¬{�‹ý—Æ¥ëOS¨AZ¦{רó/Õ[þˆ)T±ŽÏ©b{›çªò¸e$çšvMν%Arîù<¬œë?çrV£œ…—¸*V+ôã_ìe¬Ø­Tì`½Ø('åĬ3ñh_—ñEâ÷üd8H²3›`ÆW‰©s"ƒ^bPƒŠ4“AOd™ ·vv;n Øm)ž€ÇnÀíDv[·Øí¸Íf·åpû0»­€ÛÚ)të–ãðOpç¤Ù(ÆíKjgЕ,Ðgüš¥Å0è�ƒØgÆ… b§ªfPƒ< êË ·”È Í b§µß`Û¦m¬dÛ9nt0ˆ½ældÇ-âIE¦’ m^¯7„o`÷ÅÍõ†)/žE{oL0ÐÞÂK^Ñ\pôãèXt’vÌñ\ÚJ»û SVœ%kà+Rµê¸W½üLty¿–¿ò›çºÌ˜ç¹6‰7Oÿ6ÿý úiç»õÓΟ9œÙ‹7Çg±Êgë,å´ æM©xªödܤ †E“Ï\‚m£ ½õ˜¼l2V^ºROþû3L¾™’íåéòÞÆ7ÅÉDPV„®JçŽãg˜þðfUhgg¿OÈ:ofì’{.ê6<^;œÒJ{ÀQ.SÉÕ¸+VIž™Œç;:u›©äb¶_Ö;<¨ûÂÿho–›ÎÚ ™²Ü|œ |æLÄãqòKA,uñkx¬t¨}£qG§Ü÷NÄÂþPŸ„œ-ïÁ¨?ï¥ö.¥ZóÈ•4ö—è³M×`‚¼Î`„f ”êè#-¯#ýŸà®Ëÿ6ß¾Dl,Al~d “Hâ®ýÇͰM”NߣP׿„ž´¿å È4– úᣬ¼¯¡Ž<?1¯¼ŸiüOZ…Iõç|ç3}ç¤þñ$[gí-oWÜÍŒÇ6~R§¿JqÿŒë›«vÉqo}„ä`C5>os¤¼� Á0o <߇žÇ©/C«h€)]uÖ¼¸˜æ?¦UÆLÂïü(y7î߆i™ú XÞ'ÇÓ·½´ÏCyÊÞÁõrWòÔÇØQè0«[ó’Þ2´öáJž7WO3(½ðÑ#{+ÎÉ,IGø¡4©4õ²7š7bn¿7(âÙ׈0•>OF¯Pl¤yÅ#PøžIß‹ú¤-ïCx–y&;<ù¹ØzÖ¥qÃ"jµìDþ/áá©Gl—Òkká¾QD¥÷mù!ÔLÐø×bˆL¥,‚<:É/p¡8û ÙžâŒ4zçd¸ˆÞhxëéÀ7JJ}sG:.ÈoöXܨþùï1ªL%CØb@ pñ\ÃçgAâEñæ2ñÔ‰Aâݳ˜σæÙ~. —äò=ãò? Áoj\Ëó?vdñ#c±<˜E¾Æâcd‘’Å`Ei0‹5ó& 0û³è,°°³h]ÄYLAφb±m‘Åè`+5ÑÅì,^X˜ƒY<©±€,„d1R`Ñ'˜ÅÝ‹ÞÈâ¯3¡XüK`qA0‹hE$²X’Eó>­±hø@¹BšgZH ,¾f±ô}Îb²¸) Ìöüûºƒv]:‰mŸrflh€Ç$ç¼Ýab_ž-g"ù[Á @ü³L¸qÁÂ]¥ —…½t:Œp§Ê|ÂugÂu 0ÂU—…î³2Ÿp¿å ÷Zî{Hãï@‡nŠ œm" W$Ü=á„»-ŒpWÂ=,\gM¸q(\V8ášßó ·çQnw^pkß #Ü'ï…îå÷|ÂÏ nïqá~ƒ4OÅÉ0 „»ƒ w[°p7‡®wá¢áìÁÂy— ÷xž0- ®ö]Ÿp óI¸7' Wöná^z7´pEïú„;3!H¸lM¸? ÍóT{á ˜pgrƒ„ëN¸È0Âý¾Ð'\q°p»ráž@á~ù;ŒpŸ/ô —›G î¹…a„³/ -Ü#‚p†`á†jÂý•‹ñ/œpW ÂÕN áªÆ wò0©ï„®ñ]¸¸w1кë;4kW4Õ}ÓçWÒôù‘\š“´ióí|F—Áè¦Óå2º»ˆn¼Nw £32ºûƒéú1º«‰î®ía¢Ûð6ÑÝLÅ袉îø_ÝFFggtñÁt»ßa«'ã‘n­N÷£»™ÑD·ˆÑm%ºguº{]£ÛLWÈè¾$ºa:ÝÅ~åUÓ at/]îñD—÷[o ¦{•8ƽIt{Oht0ºkÝÂ`º\F·€è>ÐéÞbtóÝü`º~Œn.Ñ=¦Ógt0:[0]£{‚ènÕéfùÉYL·›Rˆ¿©äÔŸ]íh¢;ò&[ž QŒîn¢«Òé̬¼Ë%˜n6£K%ºtºÿ2ºƒ ˆî’œ º;]Ñ= Ó atY¬¼‚ËÎèzÝe:ÝM¹Ì.Œ®kpy×0ºÃ9H·ÿ¸F×™Ñar® ¦û“8Æm&ºOuºõLÎ Œîì ºjF·˜è¦ëto0:'£\Þ«Œ®˜èîÐéÒ¹]ÝŒ`º\F—Ct‘:Ýu¼þÝÞ`9û1º;‰nË1îÇf—7ˆ.9¸¼(F—@t¯èt«ÝJF÷Xpy»I’¸öl¤­ÓMftNF×L·ˆÑý@tWët÷0º Fwj\pÿÂèVÝï­Ý™lfF÷EpyCÝ¢[¦Óíat_'º‚ËëÉè¦Ý“:Ý‹Œn%£›\Þ’$îA¢¬Ó=ÊèŠÝeÁå-gt·Ý…:Ý»œn>Ñݬ•ç[®þ“3Žèvý¡Ç1F—Åèê ¢ka嵌£ø§ÓÝÂèRÝ÷cƒèÖ1ºD7^§kGt½FtŸ—§0ºWˆîn#£û‰ÑE—7†Ñ=FtÇêqŒÑ­dtJ0ÝŒ.•èÖêt÷2:£›Lwšj(®'Ñ=«Ó]Ìèò]Q0ÝVFwh,Å?.ùױ ¿@=• {Ö³5ùïÆâ"Ü9â¥?*²ÔÛ$ ­Ë–¥~V£Šµ/øA^\™Ìª§UáÒR¤Öv1f<Wêõ"‰ñÃWb žš#´*wÔ?+¹ïÌXÃÚT:¡å y¤%©Íœe•ÏJ® exT0˜‰ë¾ëgãy[s­\@+ÕhË[µg›­8釖ÎÞjÇé늚å‡)%ËhÍÏi¶áñðHå­¦…ÄøÎ¸Îr�ÿóŒ:žKÝZŒ”)­/ƒ)^ûX¸ÿ‘ßÏž’ž©AÉø©q¸®eA ¢%hn%Ÿ ”¨pÞÿ PNËruÎl*nQgœñr,‰·7Îç·N¯íyÕù.¯µÙŸÅÒ¨¾røW¾ƒbÊ*ŸcGJâQ»ü£è`íD~p«Ò K—ÿÖ¾³¸9MVÍÞí Äÿæð<vüÚÒ,4¼@rî²¥*ÿ¶$ýê8i(êÞÒÊ3{kÒþ2£/fH“›%¥^ÖÀR™Õ ²}_*)…3%úä­R8o&JÊlI}Ÿ,?;S]ÀnF©/Ó£õùb<‡ õÒ/º„&uº|*©Á[Ч-1d#"•n<¼ìÕO÷3{0‰˜8-/ˆ§Àë÷¼B0^†\3\’·‚GÒÙêŽâxpÚ¬¹é² ŸL¥™VMɬsÚ¼ì«[¶žt¸=žYS¦zÔð·ú«‹ÎD†[þÄKݘMtJ { ¦v¹±«\#¹R¤:zÊÃÞŸ ƒþm—‚Øx“=ž„§+ðgAŽbô"zJD²èYcŬ¸¹7 ü[<ϵAÊØÄ\Ã2ïó>s±äÜùL‹=’L'õI•‡¢Ôñ3ñ¬´>Ðp0ÕQɾ>¥J3±þâ~rᑱi}2%9ÖªÐof -âµÖ¥áJH Ôœc¤Ë9ŽR, ˜¥�³˜‘ ý£8²³*Î&)ÙÉñi}fZ’ޤÝù Q<ŠÇIYYs²*#údB’ý½$§í…â\™ä>#úH ½×v ‚£ÌJô{rô[fÇ ÷âL%øYmö0ä;çs°U³iƒ¤Ì'‹kµ@‡}9èx$! {\Ç­Y@fî†t<JÛgñxöiÖT�P¥T¦ç�Ÿ’ªúYSÉ€·]ÂQ©.žAKØ×<„ .rµzl&û Y·F g$Æ ÷Mad‘ÙZN¶kÞeŸ·)‘ö‡a÷o™BÓç&\qCñ… ¹.]þE=T„âäj“óEêÝØã\e~6SŒÎö¹ô¨ÙÈY àª\YåÐ'•³S éÀU‹”÷–V€EZFÓó «cø‚íIÀÓ'ä죨[ÙÌ¿'×j ™ŒuY@Çð¥²GMwŒB=’¯ÌÀ(÷·ú_haÎì`Œ Ôoħl Öw%Þ?SƒÍ<]nöûôyéF܇§^?ƒ¹içqr¥9}$õàLv7Jýa&Y:Hµn[ÞÐöóíHG 7Ð#ž‹‹ÄG<âþ::k†¶×eàÉYüsrqã¦j ;2äÍê!êÀ|An6YþL—=xhôærú¦ 5*Æ;c¤ïщå*zœØT²K{4c5¥Õ‰çV…</š5±Ão6•S÷w8Á*g•·«9Xެ=ב\˱¯€Î£ÄeÃ磞‘dú›F³c„‹P0þh,i—ï°h ?®ÂøàgíùzмO’Ø92œng˜—ã«Üò÷ér~'V«æÛÉ'dn|HýËŽƒ7;¢Ú”¶› c…ãéÔ’þ1Ñ|ýû!»õï‡,›Gš`\€8«®ÆVÂNˆÓà99#ðù_'Í?Ⱦ˚´‹_º Àbª±Üš…bHÁ´G›•^}pǪÜH›/KKYüÁánùO]¬Ub%ÌàqòQv3Ç =”!½ÁùðJõ¾ü}ŸÙÙê–§�2Ã$e:¹z‹ z{ƒ§7g…ãŒ4y?oç{pÄAå¦ Ìí]ô3fPƒ-‹Ñê!4?gùƒ=eƒ=èy§ßy×ì+¶b_1uLƒ>z±´ù£Žú6.¢0ÉoGñÛ´y+‹¨é¬y8É&ZGN.O›~5;ŒËÀÓËB3¹3Ûk¢¾þÄaô2ܬ"ת 铱Hƒ±Û«uÄN°ƽÞÌé¹"H)¥®Ï2o˜«÷A±rRð„ʾØ+X>Ë(G]”)¯x‚: Àf«=à~˜kЇœ–Œy®€@^`®³ÌÄ$õ÷Š®O?‡ÑÕ‚•m™·²ŒŠÊŒÅ ñ¶Î9™G! lreá\A¦ØåYa ¯ñà~ƒ6PF5âƒ^| zQjéd!&ž×+#¬Ê#ÙƒM{%¨Øée&Gï&§€Èf)¬t)sœìnz­Êq²—é5÷K"LÎ[è瞦’k‰³eÔ0%ºÏÀ‹4•8••=LIÛ¸®¦’7AN/ɬÌ'ùç3ùçSÞÎ Ö( @$€Ê›P-“©sÎ*pºËјJ.&¸3ÓG‹YŒ*07£“ƒo›®™ÂùE‘GJ×d÷^Ïó;ryëTÂJaiW“¼pIw­z«ËAñÅ�$e>ûZÓJ½êºé´Ñã™ô,!äðJ™3鉋ÎÕê»OèU´=R¯"梬Š,Ù|h2LÑÏ3°êªœê«.ÿ*pâ÷}mÜí%±N •Y]†ÓàªðËôâã@=w6A‹ôPùνî³!O69¿¦VÂ>^u{)dÆ=6—DèI¦˜úŸiü}µ©¤šF1–QLˆ —=7¦DXTÿp õ¯oå±�àUOLc£˜8Tì3!^8ÎE£Wžë®IVr„„éÚßk0¯ÇoV™]i}6:ÚãMÏS’+®!/àkì}'³0ø«g$;Ø[SèðT¨»\i–-3±¸öž¼8ûØ œÞs~ùß¡Î?ñ¥cWMÓÍâôÝGI¹údÜ }Ƀ™­JqšMí±x¢µÎBCgõ>DÔYh¨í™…Þs½L%âñ\Oò)§ª¿3ê£C²0¸ƒÕU<€L ý~hÀQ¥Þ9™,}÷ÍÒ/Le–îÂĈBôž¬~ª~ ‚ç¹ä,ir9Ó$v‚®‰'ÉÐÈäñ~,ÿq¢<p¯äõ3šÌøùdÏ=^ ¤Ï©ÐÁ¯×Á2/ÓAæÓ'3ˆy{ÎèÖhDðˆŽBpŸâê…gç9þ>,ߦ°ïk°óNa˜DoLÐG¼:[aìJ>™£½‘ÆTò19 ®Ñ$äú­ÑÔð.ÑxçLsu=’cпÏ3W{UeË—|ï)ÌóíÝ»ÐGî`ŽA{ #®÷á…7Hx´ªFí6ÐǃîÑžj%tF˜>lg»LCÎåbQqZâÛläFÿm6éÊE2Xâdºü;+ÂÌÎ{oy7ô÷°Á`8ìù‹­WôÅQ/ŒÜé¸OæâH;ЕúEvX¹~¢«É‰‹,æ¹0&L%ø1³24ÚᎠù™K#}Ó?`m•;\ou=o•º(ªÃm¬Y Á¦¿Ž ^7‰… ¿ŒQŽ…9ŠgF›JzEQpëk¥3à Ò‘3â…Sõ%×<‚w?~í&9+é# ÜB33©†ÇùÀÞ©êÌt™‰Ò[f:Ú{™ž¯¡mqoä|W¢¸Ñ¬ÌˆÆ9gܣЙ%ý@,²q9ðàÎ6“s3·™U™_JÝæ/¸„‚›£:›!Fh­Ë/Y¤á™9SÌÁN×Ń|·WŽ10ìü·¤?(¦U¯·Ç kû «^¬¾ÕC ‡à‚ФUÀ·ŽcK2VÎZ`c©¿gBõªç1δ?F­~”ÛP®Ä7œ~ã«~hŠc£‹ª=')"ÜË—°LÏá41M>¤D¯ÝÎ××óbPˆÂ0¨Ú‰’Ò\ܨ–æc'UŠÜؘé¹ãg™k´|ŽYÖ2™qsq¢jž„¢áñ‘šó”½¢0Eqž(神c¼ÒäfÇ g£ì‡HV…ÑuÈòùŒ¼eš“é½ËÐ>I£*h@ãƒ@±øý§‰¡ÿA]_pš^+¹…F!ZM–ô¡0†‚”÷TàîéN SþµpÓçqI˜:1ÓâÁÚ½ÚÉÄX·ý™‰¿`&ö¼yŽçgÚãbd?µ`"KœC‰éHå@#ûìĆö§i´á¸©{¤UîµíäÉ:Ǥ!^lÑÁõ0æï_'9@û×j™W±‹—©V~RC(Oȇl-{t“vi"nÏcôߟ !,g¾žŸ˜ÎLŸ¨Î#Æ…‘HÏ®€´.À¦«Î2'OÚåyï,w*J¿U½éQŒä„–¾Ô1pOÀCÝc4Wœ¹ŠXwÏÃOl ošÀøÃ¨¿¯VÆ<TJ鉵Œ“¥KQ¤èNäúË5"Oÿ(²±dµìâ1½ƒº âùâ¬øö°Bœ'nÅÍ•tžx„ŠRý<ñ¿ù³öQU™&ƒ0Ó*7Ûk‡1Qy$æñ}OÉMkmi}b`ÄúžzÁhÈFÖŽ7RghwKØŸŽf=ã‚à3Èÿ)I^5‘lP bd•j‘ð7µÛIð«¯NÀ!Þ9SÉ/Р)]Îىnj—±Ïó¤Ë^3Gþ™§:Ëâ¦}Jí”K_‡.SгW,K,øÆj•_Á­0›æovì‡ð%ÚÛ‹‚¢<=´-°áÏ —« ×a=&yÇMÀÿ!…Cå·$ñ\éåû»ô"MYvOà¾ÝmèLÑ^Ípüç5l0“§é\°¸’{¥ÐÚ‹¸g\ØœûÀzS —%\q§ÁD‹¯�åkßó6êÂ`‚ž¦]D¶­l-£qŒxÞ·¤$æÐ~p|zAïjsA¥:ãþÙìÁ -åÐ_Ð÷†±NÛcyËÔöŠ—{è,ôCR]gä_‡ƒhˆÞVeP\ÛÉ”wì]Õϼi7!Þ´?ÐSònñÖK ¤ ª$/hÜWÓ¤¨X~gZ=<Æ´:5&`£ip•TÊ—‡Ä´|lZ=ÄŸ˜‰œ»€ÔT‚ÞIróƒtý]C½ÚÕ&œ°[Œ£=¢h‚Y¶2dKLºì6ËY0öȈ5×YzS3s 4Ø2ñ¸ñy·­i Ù®F¼[íG!c<ËXôhëG9^¯ZqU¶}»)‚§Ëí,¹Z’wªoQ*¥ÙqncV²¢[ækë™V¹–]ïNT®ç ¢È_¸?ÆJ] Eeư%+Âþ?¿Ã‘’ù$T*Ö£-?,ˆã’'é‰]üàpkÆ<†Cñn¸ÃT¯á&åBL±Mª_ ëS%cÓ0ý©±üÜí(&MK3^{?|~BÝä`0uÜ^¼u6â§ŒµxÕå4®Ÿ…¯ºàŒIµŒÓXÞˆ䪖ÍPÚ0„‚î¤Îøäêö|±~€²1R=8]½Ž%¹¿tFý.ÀÃFÿnr4w–®¯’"`x ¿ãÆJŽúI†@]—vqŒD¾kÌDiä{ž?¢¿«àïàóSÚÙšñY5;fš×¢½¬ ¯÷û4ž+p Õ–¡¿Z¤º¡pÑó!š¸XåèÞ¦ÕÆ®áwšä´Þ1¦UF|naž—ÖûB5-[›1ÛpéQµÃQÝ~è Êk¥¹ ?³Ÿ}°ß#diÛÐÔy¬ R½_ éJÜý3YóÎeß}wuÙL~é26ŽŽ5(ÑPE—ºG8Ú¶$Ùx äF·ÐÏ 1¾ Ù`RÛ+B‰ë‰,«ý[–^g<ZÄûˆòˆê€ï‘Ó·È%ùT_/ú õ¿g™¬]$ef¼ñ³4ðgûelJ¥ƒ±ÁsÿüÅ(“ö0µ—½B÷8ß’wú?VRv?„ZÛå¬m¯×v»¤\žŠµbi”\ý£Ô÷ˆ©TW…†2•lbM—ïÅo¨~ݸ~é®k_z¿kpóøi¹ˆ•ï8íµÿWÊ5žæ­lަÌIˆìí•�nkÜÅSÌJúf.%U _G±¹Õ1¬3X¯ó“–/Cé5Ù&P¹ü™‚øÍcDñf\ÒÊÚf~æÐÙ?0\¿…™ífÇΈ$¯Uþ>M>éØÖ±%ZÝ<r;~Á6]rþ`Ë€ü‹!?0 '\öF–_}[ÏYk˵‚sCéò„NíR„ñO{,,]>î3"Pùg;íÝ(Å4WzU9wÙÞR  ê[^ÅoURúa¢¼O½d ½dåyšÖ’è¥uøh<>¤;Ïñ@-¯))�«}ÇâYËçüžokã© ßxʬ§)uB¿†3pO™åí¦’ç©²SþcÇ®¬Ÿ#å#Ì`»!CnN£‡O¸ƒ„Æz!¿çZùIi±4>¯"¥:‰eØbCñ«Õ?Œ1b ý¾‚Ò433ä¶«^ q € `½¥jg»Ú×3}=i^¿!{ß WØã‡3l¹?µko´ÝZæØð#•%ÏA@i3gŽ´Ý€òáÜZ•XÛ¿cÕô ú~ õ™3­8#}¾Ç£Ž3›7$<W×RRløÕ£RåÑÿ§½oªºž$3É&Z°ñ«ÔQƒ†«†„—š×$g„H� “dB¢ÉLœ/E(“�磩VKöRk[úºEë½>o›�’`}°-Vk‘ZÍ�×D´r¾µö^çÌ9“™ Þ_ïßï÷ñãd½ÖÚïµÖ^{Ÿ½÷¤ˆŸŠ#Gw§˜:Wéì�îjàXRÒ~6LBC÷Úƒ%Y6‘,{M?Ý`1\\¦mS2Ñ2–à¢vhâ?Ø—ƒÐò,q@<èO{lb‘±2`O6&1܈µ0u=…ÝØõ®©ó¶Ð6— Ì Ûa^” ¾ê€t§“e ê³ Mvß«Lq¶òŸ´¢Ú-'L ¶]ÚàgáAÓV»>BŸ:ˆû§0<†r WdÙo¤ØÅ)+½|ƒ�Œõ÷Ï:×` ™ËÛ¡Á¯C²¾\7‚mÎÛ×ÔÕ'Läó‚U¡¶Spþß=\àï#ж0Þ³ÖemÛõ4S“Iox¨¼+S˜›Þ¹˜Ç#~Z,ö–Çräƒ#Ç’zĽgÿ<n€SÅ×¾ÿìѲëúí¡Ì½„é•SÙ^©'Ô!±.²…2z…à"˜ôBGXzM-ãY¹wÛ ˆÅÐEBvHϺ¯ñ~Ó>õ!péZÓÖ“i¸3¸0¤ÿh"pDK`ü‚‰èÌ|l1ìAhÚ+=Yìôc… ö2/ÇœtâzÃ)!ý¸#3?¾Æôž*(L×[v±ßÔu6]§wNÌ|ó~ü]£EYvK•X™ ˆ³^¾%Ñð:�6‰&ý( Nz#a³bƒZq¥b<Ыp=wÐZð®íì;ï ½ƒF0ð.`5uí†6Ú‚Ïí¯aÛÑÎøœ ׂç› }ÑE>48µW8ò1LøO¦Iû)­À±‹ !vK¿w<[‘üÌ&ö‚B÷—æBúwAß3®”<Þ Jk9mz¼§«×n²â#ÿ‹v\ý¶;êoxýPñ#Çx߉{Ë®;쳋g^Ç;Rî6©_.äÉ[ÿ_S”T(„ ¨«ÜŒ®˜^rqrüCªÎ)\¾;P¾OØ-¯™¶Ø6�ªÊrÔ´-jZšxs•åˆ)ðÐ8\$Y’m zóí–?›:ÓÆ#¢4Ëfé1m[5Ž}¹ËfŠŒ§ˆ·°õÊÒB[H¿ßnÙ‰E5u^Í„jÖÕ(T·B»å³©óV&^ÐV0XT –IçÜHÏd–šуs ß$Ç¿2ÊgFf1€R“¹2µ—QÿÀ©¹Œ8w­iÛïA•„ôSQ4m0e€÷«Á¢ä“l¡Ò,ôÙpZüƒ  Ž'.È?Ñ$ˆ‡`†´ÆÍ0­w‚ÐV†&æ¡sº‡y’h1ó67 I(ûévÑe W^PäqðÏ%錡 >ÉÖ×ÇxÜžb·ÜÚï‚¿ÆÑ߸Ðטò€ÊÐÊÜÛÀ®Ûg·œ4mÅy±¶ìýÉ*޼˜ þ¥t˜?Ëb·€ãu€7½ žgøxzÂä ã¹éŒTtˆÑœ€öKBj ?”â+ŒãÙ¼ì æù@W¹ k 6÷7 ;AKЮÎ5<q?Z½ÎœÈ}ß–³¹H¿èÉòßÌäÑn¦ÐJƒ-R…m7â~È[jñçb3èËȾð xà JÒg8Ñ®ì<+¿¢AÁæF§=`¿Ê˜þ 9ÍÇOÿ(I¡=8÷ö~’øÈ6óxh‹nX’z³ÇôboÒ†é!L2bÀ 9Þ{Â4õt¨r’pd¬ÐÊškÂç/àm«ÚñŽ6Sç_!/±ŸiŸš\XÐÆ^Ó+½¤ˆBð¹}ÌZ‚~ÿT| *8„õ_„:š·(—ÍD™:6è”ÈŠ*XF¼ÂY;òŠ}Èþ‡“oÁTµå ÜÝv°@*fî|¤£q”c¿Ã^¿Éñ‚†Knì÷yçœcbh8` t»!ŒBœòlèuׇ¦.ü~nç?Ú††sþß*ÎúÀ‚`Ùm#¶ÐDŒ„áJKÙôSW�†˜}@Òq Mjc{wç�`{d[á¥;¤¿RÚ8-÷e½E‚ðX”µæc[hé v6|ƒµl'¾!³ÆÙ!¶æ6iR›<Î~vg+CÿBê5‚(ÑPI³á¾/pa�ð6˰iÛ á C×äÙ-ï›o³‘{i¶=¸0­m§Ÿí5)ç–ê?õKU…ü ¨T°¼üÕ}‚e7T[q<³dv܇zÙ‰?–e¹ U-[ÉX¤ªFobù•›íb¥b¨f3J¥>b¨˜&j6£Þ¢*! $£ý¯ é§ñ1Ä=¤¿*¶ƒf_V¸îÿ).²d0FVƒ­áƒl a¼Iá€ÄN$ã(Òë‚“ºïÍâ‹SbÏ‘l¡÷u— =kM…áõ´8É}/ÊÀ2exµ‰²Ã­Ä Ô©çº>q¾ ¸Âwž·5rEt5»€ý ÐÉ$vFÍð$(ˆbx¸‰È6�'Á©°Á pÐW,¡S’ÀPùOÂ{ö ó]`r$õÓòæ^¼²'ÉfÙŒBÒ�¼›¶ìdãÅü÷ZQªAnÀ XòÛ =Í<·«Õ¹™:¡VZ’üŸ@*Um¬¬L¿`Ê0bOº|”+ÀNÚE‹=4ùîVl‘ ¾tn<+CײE„?B@FÂ| ZêSðzn¶M}§jj˜»„÷ÁpZ[qYcœ‘YÑ|¸ÖÈäĸ;Ùß`÷ÚÇ/ÂØé°“Œ¾0 e”Úöâ×PgèAôÏÊö –~_¦`É|§Åç¿Ñ–XL]SÓQŽDWfÙ“þX•tˆ|K F”#” òÃ4"²!U‘ö­ˆì“Eä#{ 6HˆHˆHTfñ0|†çžgC&I%[qÄ&Λ˳ĵ9ÃW[¸ëg×/8) B´¢®xY€�°øÈ±â®±Ç²ï+bîß)P‘²q{ÅIo®Žáÿ‰@uî2‡ÿz•¢7°7ç[Ƈ†â=×®µÿÏÆ…þèq¡,TVs ¸ÖV±¤ÐÖW’Ë')óöÃ;NŠð74 €ìCY™Ø{ä¸z ÈW×�Ç à`½]ü6bmàÐT‰‹³áÉ o8‹ú…5?;P†Jöa3(Ù·?ÇoàpÏ5œ‚°) ûL¦Øñn“É{›±'>²‹µ‹wïüì$ÚBèÙnG¼{-Tô ´sà6ú¿&œý380ËÂå*s•¥›ÿKã’]´öƒ a:vSùa4ÕU¡J`\‘këÀlWõ þè¦_÷ÔÕx3�ö´âhž^Ðð+Ô“`¸½ÅØÑ?aü%vÃøk6‰u¶Ô_%¾ŒK–UâoÖ1WÓ ]|#ô2îØÄ�Þ q·4(ãN•Ü $p»Å#(wBh ¸,¥Ìc±[z<ƒU–ã¦Î§Î¡ƒ?P5õ˜hpðu^_g•ª FÃY›cä p IQ€6gx2™+À'6ÙjÁUÙ8á x0';œ»jI®-´�Çfxþ;[…IR`C.®æú‡àÕŒ¯fÿ‡6K?ø¿8¡ëÃuáƒxœ}„n6þtý?‰.(dæ÷HÏ †ÃMYl¹><OvŠKÍá7‘j!WÃlØ›¾éŇÙ4¥ g)¼&BÒ6 M?†¢P%NL¿dÇV�WÁ½K¿ÿΚpù¨&çMÛØŠLØ·}Ç[P=#pl`cD_I>+üóì,f ëM(¬ÏžÃ¹® ±àÔ‡²vþ=ó’Æh¶½†Zåîm«ü+„ƒ #ò_0[,Õ‡‡Î/('xÆjýæ¬ÿ=£õߎúßoí9…¸«_¶�û0@& ¯Q1U&ÀïGõ/aü)×~_¨SN6þÉ ŸAn‘9üÓ³ŠÎ4`3fàUH‚ü½É’ù¢·ýƒ¹�‹ÍÐùáç.(m¿EZí7x±Eã\D4 h„I4Ž%…Ñ¢ÑÃD£¡A%î $ͬ8«²Q5˜FpÝ€fáÒ‘u†é‡ôƒÍ»Â`­ÃG?%“}XÓeÏþ3Mv-vÙ€U¬+RuXõ×C¥¿iL6~”ûlóíÁÞØÄè¶"èµ¢¬p;f»šl!¯~9ÿ UÓ’9ÇÝ´ð<Ö°ÁŒm’Ïï_ûª‡±Ò\{R?ŒŒYÊ⎣ŸqÇøÔ*Æbæ,0–zV4e6±Æ>x»÷<tïó¼{ßòÍ(>t‚º÷päã“7tãúC’ÄûW@Ö@aVØÎRÖ¿TV Hõ·¬Êbn2úÌÛ9Gý»õÖ£úh¨;µE6´#ï๧q˯~::èö”Y3Y*³¬«ÐZž"ÆáúU( /àÍ\wYYûŸ÷wÑ'$é(oÃ…zl>æ¯,(À\çzˆnºMí¦ÿŠMRçd1þ‚Ö²Üô›(-Ëe•=7X&ý˜ñðKËÁ!ß ¸êAFù7¤h]uS翳¸†VƱ‘s0w=ü-Fɬf”FfŠÌá·¿=æsà1®jø^ä—™Ã.IkÍVKZk6tšx0g…gøïÓDl縓³Ž'P’WïÁ’¼s6b=\ÒȨø¹K¬)¾‹¬[_CâHiôývGå5uyA]œªaðT[;^Aª6uºÙÊò£¶&‹˜’Í¿§dGMÛ±IW)x»¦ÎWÓøàÀ‡wünæ ¦®|wn­u§ñOcIû…©}6\ĸ`ÚöhëÚ3w£ç¼OÏv»‘×z3[¹ªcS6 ð–s0˜¥1?YMúûݸ‚"¿½åqlL)giÁP^ÊšÖƒPAlËg¦À-iä’'™:q¯¶ë_ùèd9Ö³€›Ú1� ƒ%UJ³*EöÚ×°Ò|jê\¯”f™¦4è»OÆ”¶=‡'¥`âÚõ|ï‡-ÇâØB1çþ4ß2yde–η]œò'x ãmƒ³Žó=KlCíü¯ƒN n,㸗âí\`ß]bɳùÀ19|¥º¬­b®{×J•/$„æí NªZ‰Sð‘®?š:¯dºÊTº°¦-±Lõ ŸË³ñ$ 8æÙ'2y䦮Mü»ãrŽ2qo™XZ®[._è)eæ24s h¼‹¬ò‘ãh‹Ÿe¶X4º óÿº zóñ.Èæ®¬3†¯Ö³Åv£©7þ`=º¤ê/7ß/‘׋`R`چצÿÏF™c1'áë )ts¡áÓ—d¿ìvðÒ}–(Ÿlª¼^ü-MŽ1û||…jüõ0ÁûÜÊV°áëYóè+å¶ù&ËÀ¯ÎÇhEzê;ñ[ ¹9z5äï0:—Bgýb%ûˆ7 X M÷Ø‚Hý ”Þ3¸CÔ²,í6<É ÚrÙ|ûç™MœËx3µ¨0ÛE›bi¯c”ØBa1XZ,m·´.× Ý‰OpŽ\ä×^Œ*ÞNQ†Šö©÷i[SŠzùŒÕ¬ÆR¬Æ‡wÉÕÈ7uþžµÚü,›i‰ Ù€QÍ«áçÕð1žñ<3(ìz¥+åÓdu5hÀ°ðj2Ž·’UÕ¸–U£Ü8†º–bêzùMS¾ÀofœŸ0½I+Œá¯³u1Ã_Ø·ÐÉŸ�°'ã2œ>…ÍmÄ%ÆðiÜF.ÚÌa»æ´Úž.±Jüz9¤œdõüðoYA&…xº?`égK“Ö/ç+od}6`äÎY+MÚˆ€ˆ‘û¬|!pÖ*Œ€«ý÷óD YçÓ¶yØÐ¡ÌmwáZvfÖr¼dñ0žÛ¶§lA-©eg`æåTÛrŒUÁs²Ða/h–W®)ÄÄ&°>ºˆ¶ù«|~Fe¨lÚ (þ[Øf6Ëä7—aYŸa½µd¬~£XËĽeåiäØ67)ê;[ýcŸ)zù3E}‹1Že€Ÿ)þ¦×~¦XÌèaÖÚÏ…Œò–N½úùLñõ]dõÏÎ6ˆã|û“ÁçÞGcŽSjœt›ºØõAÃdЪ¤ö©ƒ6ñ5löì„ù4´Ù·!|‘-ðþñ§Ô T¡kú‡ólcÏ KùŸð«ÆÁ Ôd6 SñWÜk1åkêú ú­S§ü D8ü]dµÌj]ŠCÎêžš‹ šB÷ê ßþa ¿ï7fât{=ú'ÅÌ?YþI1›Ù ¬Ý/ô%3›ôøÙI:„NÒçu‘›ùeàì„–²dLJù°?IhÿÃuØŽ­lßO:é¢&éï`Ò?aÎÙð?oqh8ÞâzXƒ™F¯<´ ëb.ùÀYF±^ÿÍ2Tc[˜­tD= ݬ–=Åë ¯ÖB½æ0T]P»’מF†_1_а§Vù¢¢Wvo¢féàÌ—Z:øVôüp˜O%µªñé°¼tp_âO¸Ç·¯˜5@#C~ÂŒÉü_,AÃmŸó<Ž~áìY!´\L¿þ+ ñ¿Úàù£È'ñ–Ë;¦-;.àLsb}Ó2Œ;3J°vœ`9@óUÚ¦¾ƒ[ÌCóÒ­ïr±¯Fz×»à˜:K0¥©+s`b6¿‹4< : Ül€z.f˜YKØW–NF¨|àP.cäûap ˆ3_š€û7ØÁãÄN¼n Jô·/Fœøå´“†.ìiÕºÐK‹qÒP¬¿Èæã™¿]Œ)Ôa ²ÁÿHª¡û–™q€ëZŽp×8À.åØÇÛ3—°¼“X‹-š¸[Ñzô,‚ÞÜmÚú,Lí¿yÛØõû*ŸE~_åmùj_Óóé‘ ¦m‹ø Ó5'ìÁµY¶àíløÆûLì¡=¦ç—qÕ4ø’ôšÐxX7`­J²‹ÇÇÓ·³-¤Åb¯©ó öi´§ØÒ³üØã¶Þ)¥!}–Ð8bkì f¼ôuõXÞ_ó-ì:ã«ì:(žö•.¥³žélSÖKø›6»“ü·.%º 'pNPÓ«çs‰c¸ ¼ÄvJ=âÄ—­–þ»mÁT[à€ê`O1c5ºæãÞ‹ÀÉÝqç¦øïæ«ÞVËàÆI0Ö Sý‚èÑòÁ!¨@S…­q ¨,0’îË Œ¤ò¦yºßo>v1ÍÔٜĿ6wóƒñ£]ÿâÀ‡i¶ÀŤ«{Î@6b mØÅËH}©àëéNþ…mFèKÅ- }åzTÍ“O Á¯‚°¦ÙÄW zl½ÿ ÈyóÚm¼ÔP%q*ÓG_my•>±vý·©Ë òÊǘº“›ƒieIÇ#ú‡&FŒ›Ì”1ÛídÞT,AxžôÉ0=ü"„#Y¦Gð /]¹>9ˆGÞBåÀ›dzz5I>²¸ºÏ畟 Œ$3JšLùZàœ‘IÀ8.)LL7AJL“/Éú 5 ¯‚P°ì±‹àÀuÃôÏ"bÏ4ÃKŸþ1Œ^Áâ ‚þ´c"|³÷ryY ©Ôaû¥÷'°û ŠQiø}`üØÆÓ7@§ªÄƒ¨¦boAO×Aßì Z¾àŒå­× ‚ú@`dÜZ¼\z8oÔÏn«hÕ‰ýÅâíÊfÊÈÕ¸ßQê“XÞ<ùœt�j /¿@º&ÇÚù!ñA Óã=Ä?ø1ÏÃãn4:ó8ž"=]Ÿõ9î0nÊ*3=_Î~ì }Hkp#²œ±Z.šBßc³D+^Ü´ûäLÜýûͨ`ÆQÁô|1®Ù§ ¦…{m¡¥x¼÷Û+“ ²=äKNÚ¸ãØß‚þÝöõ!Tõžª›w´L¬Ú9ø¶ã²,XµÓ÷TÐú\Šõè¾MßÖíI©{%pÞ¸i¤*ZŸëö@’> „2±óëÆ®Ãe&k1—U¬º&П&ѱâV|¡ G}ì /çKã}ÕN<]r-Ëf¾·ßd-8MÂDã<›,žË4±ßkZ{ƒ)8=xº8p>Ù7½ó ïúΞâ3Ý— PÏNçáɼÞcÅãzÃÏ_£«¾@Ãý@ǃÖýr"='{bôïœu‚X·Kñô‹7xæ;±¡*²èèzú#»ÀžÀ–í«ö‹³û¬Ûyî`ýiøD§ÓžBôáé¨†Ùæ_vØöý(‡}„ òK…È‘³<U{¬Rp±ÞR·]¬ë‚6pøªºM¡6BøAþñž\ˆãi<МAýŸ…àÌw@"jŒü7ÅçQ,Ø@!À0c„xšW%}>8?£õ•ÍÝØi ýƒení¶5þU²Ôµ’bÞ6Øq‘+¯úÄ u—Ó#øC»Aëöë+`³LÜÇVü;­LQÞG ø]kʺz#†µW‚v߬ÑÃÁ&(ˆÐXµÏjô~ §³Òèß”€ëW[P(Y·³ñ`ŸÑøÄxòçÈÍ™O7î‚씵-pi× ñ}›©äBŸžoÎÍ)£k™*¦Á_ëŽ2~‘ÍÉÝPé= ÛíA+¼Wí‚ìê¤`Gv°æá°ŸK¦:K·yÓ5øÍœKC ìÄeUæUè4C#gãv…_û"Ú>Azér©ŒôÃAÐ.<$¹¨,K×Ü—f{&¾ÉÍ}é¶—ìbEF°üA\›‹§89Ÿ¢3¢ý‡Ìš°ÙpÎÖ,VL¿A:ê»_äûLªLÒ\ê["_·ËÜ”]\¦ï³~‹9 `Ÿ`<út-:.v”m˜†ƒ[»M]3ñËáá‚w˼¨*¸Úh D Øö“ólAý{±¤Ô* i¨%©J¼å}ä³§ä Ò>aîÝ;M¡gYYæ¡ÛŽr‰òYp¸ŒßÔ5øö9fJzË‚,ìûTž,ÙÃAë·¸tn àø×™Æ.¬H6uV°µÛSæ‚3Êñ]›8ñ}Ë›E9Pd™ÉX{­Ðhý–ܸK:À*,+ÁL–�Øk<ù&’l½8Hô x~SLf„ÿÀ ‘ö€Ú1Ó‹WZ¹R9ˆ¿i£=™ªSL$U Úo;Ö•YÌÁÏG¸‘æµëlìÎd7$+»CnVÌ$˜:ÐekrÙ…= Ö0íUÝ:<Ó Ý®EÓZ×*ù«x¯bÝ®¤ªo±:†Ê²òm¢þýð¶Z¶qW1?S¼£ÏÚÍ´Jܸ›%4^iëϙɖ­íÖïAðäw©…Š“‚$õ±t¯¿Ä/pC°\ÉT.<2Ùmg3ým‚ˆÛkÁþµ°M‚øªM<_%îƒîµçØ‚p,ƒƒ\™]s±ª`e.N[>asͧö0Oô´u6¨6žÀ3 Ávmÿó=4=™êƉU%ý¹X:>BNxÜðøáÙ�ÏðT <Ëà©'žûèÙH°{_·� ˆiûÓo >ð¿ca‘‡v2<Ÿ™®HÛ×}ÿ¾{Ç>"N5ÛLíÅ?¿?Û‹$˜¬6“u/„ð©ÉzÞôìm/< `©0)1ö†3>{z_s±É]’+‹ðïâü»bþmèÀ¿?߬ÃÓ5&÷³ÝôòÒ.kßfKæOç°ß&çå¢fxb-.=Ùs²íbYŽÙo¹¦-xÁ‡]ü“z.‡MÛpÏÄŽtY7ß,áõZxvšw`Tó΃P%<Ká¹ó‡çvzÊ詤§†ž6‚ûºMÿY–3¸Ïô<þ âÐ+Ÿ™²Xè4ÁwÛ8Íž3(ks†?3=dÏþµækÍ‚ÉÕû3¹P{÷u7›6•åäÃ_{Nü­Í©¿+sVÁߦœøË/ʃ—çrºùË+9¬å>¥û!–¡É§æ²ó<¸Ùヹ¸ßJ^Ïœ4J8²F$ÿ§ô 7ÝÝÈ;øë[$vŸÒ>ò/§Üãt–,)›cöz§µµ6Lkôú§9Ú›fÏœÖèñµæ-¹e¹ ï6óôüü™ÓògL›^`Î/˜3Ã2'¿ÀÜáô9=fëºsŽnŠ×Üìhms6™s§4MÕÝëXã˜Öæp­žfõxÜ]Ëãlt¯vµn�Ž…¶jsƒ¿¹"ûÖw8u6×G[k“¹ÝÙîö¬7;^¯®ÔáºÉgv´µ¹>§¹ÔìõyZ]«UIWû}ÕÍU,Ï%*ÊÚÖ&U¼ºÚòBÝB‡¯uÓÜ<f¯ÓçïÐÕºÝæv‡ ²õ¬ö·;]>¯9·Ý±Î<¥ ª!—Ì åv7³Òš§4éÝíÓ¼~×´{]Ži5îV4ƒn^««Õw»®Ã ïþŽ&Èßîðúx¹š=îvž³ÎÅ@-VÛç&œ:¹%>¿Ñç÷8u«>d³¹šÝºÜ©öyÎÕ¹œkm.¯Ïájtêü^'o ]nLæ©Ku¿Ï½Øéh‚ôx`™§ÕçÔñv×ùäÜb–'§dýRG›_[ÚRhÇGã}1‘‹ÐÃN,TEá„÷r¿«Ñ×êvÉM(ÞKxOaK¶B»opÖ¶x°¸êÔ—“ÇcW9::œX7»JDîu6úæj¸méj§‡õAŒ†bø*Z®*-ž1¶c«kû>§.·²²BkÜ­MºH;•—Û0 s{«×‹mns;|º6·ku“ÛßÐIê¼ì/¾Ìf�š1…�`höLÐÜÜZ=T?Å«=½×±Ö—çuë*‹—ÕÖW8}�Hü!»&se$ ­k.ó8Ö:0ïª PP<³×ïiv4ª)Ë @µÛ¾f·§ÝÜŠ1#of^¾.ºU"y*ç(ms€ G"aN÷8›Û Æ´*§¯Åݤb$BžŠ n}§ 1ÍÕêžVÂ, g€pž*Ìéë}ÎQ<Zã+mqx¢ù¢pŒoI‹Ûã‹fŒF2N¸h>-ŠqÙAB¢Ù¢pŒ¯å)š1É8˘¼E³ŽÂòö]ŠRi]¬½³Æªb)q»Ûœ—ŠK!hQ5ƒ$±€vt4úäâpÑÐਲ਼±Õ}­„9TXÅ¡Æpl;ƒäTÖ`*r$Ì鼕T £˜ 7ú¹hF…h6 U„(K4¥ˆZª¡b;¡DKà=@´«QÊ5^"• -´»½Ãí‚@eÚ;WÇ@^4kîTÉH5±È^ê¸qÈ“™€’À yÿ®GçÉH Ç²º1y³ÇÆ+öxë1ÅR™Q…‡D9’ê|W ¯t 4z»“¬>+ÏèjsS”7Š›Wd1xk³c‰µ™jÓquËëp{[qäÓE Ožƒ•;QÝÜ þ‰.bxˆ/A|Pð%:•ñ!îQ˜»M§Ø bŽ GX+u;D¼Ñˆs¹Ne‹ˆ{&Â^¦SÛ#â¢+t$ïyk˜/‚­F¯¬ªô®è…I#)„e§WV0zç™R@%ë½>g;“»Ãéñ­×³~» Ež<ÊvÖ÷àIvtŒÆrOS‹kkuÝçXí$¯™ ïgG}ck³j,®„r˜—V{=Ǭ8£²«anr63_Éí2çrÇ’¹¨€6;ÑñTûá¶¶6çjG[1y¹ÖuÎ&ŠìŸ:}tŽÑ3ht»Ö�'ã©\X<Gvð}ns#¸eà`+%I—ç‘üê\^Gô‰³©šÍéD2ÕÌFg8½lü§öÏZUvÅíâ¾9z+f—ÛgޏMf‡OñíådG·ËT(’p©ÛßÖÄRr7ø­.yn>‘¹ÃÓÚÞÊ›Ob0=.«ª9D»ßë37@µBE!¹Å1êØàcµ¹Ìó¬iãó9[Ìr¿2OÛ~i+E#9Ô2E©1kétuz~uc{˜ibÍu ›×@›¤s>%I7³ØPb/TÁé2¯vxPVÝmèœ9›”þþåms{±ŸZ®¦6§']æqf˜]ñ&V’“gqÚÌÍhyu:nXU*·ÛcÈ#”ÌÛÜêl²ƒêñ‰iõq#Ú{¨d{ˆ\j¹<C‰âs»x Z¼j—ç†f(�H–­l¬˜üŸ&þê6w4…GžWÅI‚f•£ãGMKeK4v*:͈̇ÅÑ“,íU='›#~ ÿ"º¼0±õµ6ÊŌ̜Í8ŸN\dí¸–fÚ¥h`<8ŽUúѳƄ呧ï—Qšèò”+ë‰J”°É"EÝÏÚrF.£¤cèCdu"¦|FV,ËV$™XrsùanüzªA.7Û(½WN.;º6>³t—ut=Öâ*Ì—K@_›[M—ÛÏyòªO¬|å£/‘óù^ÞzRN1jªÙPí½¹•D^T_6~»kím…Vªåäæ&Ò[Õ¢Y<Å]¬ÜØù¬ˆ³~8FþQ u—_ M»©Š[†1ìWÔêà—(Ä(»«´GLlùÒ˜]¾ Yáqû;Ư¢W1¿TÁÇGyt,>ÍÂèhýuk§$‰JcŒüÔK«£ócS«±Œ…6‰ËóÏâä7¶Ÿ¦´ãhÏ%gd²G|yí¬2º<|ÙX=._vR±—äi©Y†Š‹±¾‹n4$Ä¿6·›Í<ül¡X.N§¨íi¬tØ‚°n¡Ûu+MmhêÓêe¸2éãõŸÙ8ÛÒÐØ<3¿aFþm…³¦Àÿ‚†YŽYMŽ&xSì(›Àfàí^¶„Í'„àáÛÊX±Ò¿Ä|æÃ˜DzÜte»ª¤kÅ)ÆRÖåçïwQ tºë§KÒ›ð $é7ð<5ƒC|Úá½PŽ÷¤$àù¤}t&þ ÏàÌHxžsðèfIÒ8x²fqüÕ�o/ISÿŸÙð,‡ÇMáÇU´_Á³ï2Òø0íÒÿrùñ™|»$eÀsÞS�žØDŸ»à}±*ﱩxŠ¢øÏ÷ŸñL‚<ñž¶GË$é~x¬ð\UÆq—óœ*åð;�ß(åŸtxž¹ƒ?7”HRu ÷ñ<ãÁø¹E’4ÊU ÏfxÂ3Ñ*INxÞ‚çÖrIzž!xj* ^ƒÏ•‡xX»ž—à¹Ñu…gÔŠšjËù&¬nÕêUÄZ(«H\ihr®S–“bó«–Zt1øÔkŒLT z£—°bù/9u®F‡uKdmOàk-ÀÓá‰an\¿Iã ¹Ù7Eü Êí"[sä <+딓¿‡d_«Ëä/cÏÕë]sØz•’6_ZÕ͹ӧÒH‡£u¬î=Þ+iŒj=t+A¹mß—¤9ðÜ ÏäïIÒ5ñÉ„÷Kß‹„ã=Cª÷cQ´Cðܸ tå)Ij…ç"<ûŸâ¸Ëy~M¼"Àt€…�káyúß$©ï‡’´žmðTÿã.çÙOðÿ ÒK#ˆNõ/Kz]¬F¦Ä¤ë˜<Fü cÐ3Æ #ÿô1èãbÒ“hƒž“ž¤ÀÔ1è†1èÚµU5e¶Åºikži¾öÝ´&çšiÞ–vP]eÕMos9û‡¼óÞ’$|jáÙ Ožc<?$XrH’zŠT⩞ÞûLÏfxþàû‘\´iö˜û‘èß±‚+ì8°sx”à1‚ëª8ÜN°›àN‚»î&¸‡à {RzÃGêˆn$˜E0› ™`.Á|‚…‹ k.'¸Š` Á‚ýn&x”à1‚ÃGêRy fij‹:Õ¿¢£æk%nן8ü,~(~$ /§ŸT ÅO$˜´a±.i]VÒ×2ÒŒÝÀ‚—ÃãÓo’V!ƒ0!kkri¦¬MHýÆÔ! ÷�êl£é+á):»h¾ éå ëA¢?„ˆbŠ/ìÐ B)ªtž†§ø~£æ«èw¤pú~xòÓ†¤ëz¹¦bý>W¿8"ÓõPßU@oWÓí;Rz^Πïzk’¦œ©´¥¤<¬¨>v ‡¤5_Ȱ#uéÃú@Ú#)”ŸøÌ†¤•ü–kÊ‹§±ò~ƒ:?k¤¼xÖ¢È4$ýK :–ãmÀ×d Iï(é[±ú@jõÃÃ)Jû_>ãCÒ{ ø0¿0¶=ȧί‚·?¦SôÍ_’&)ôrlC uAH¯J6iÝÄ!é %¿JM½wý….húé—@ß ô\…¾@ÿu ïú[J9¬šø§€¾üª!i¡ZNJv¤”AG#=8·}Qœüo·~ '§¨ÚâCSðþü WIŒWÑki;RC†‡õ(éøà-kÊdQ·'Éë çß8$µ(å¨ÐÔó9 oúÕ ½JC›è·Æ‘÷Ïá­ûÆøú2ʱëFu;”+ñ±ž3~ èóÕí r³ÃPHUõ÷Jà¹IÝ_+4ù<tcîô5…^­±ß'úJu{GéÓà+ºyHš¬nOU}?Dú-jº¶¾zH¨èÖ!é:všé6M9r€^“7$­ÓiÊò½øá”^IÇ|»/Sͧʧ è;óÔý¦-ç¢?£ÐíZùzá´!‰6ŽaO_Ç‹K€¾?C-Ÿ¤,’Eø>¾á¹CÒË{ÆìZ)ÆÇì?$¾}¾ºÿ´r6;ÐWB`7ÐoSèZ}}ý@Ÿ'þ÷!p4ý ý¦8ô÷ 0’ |!`¼}HʉC¿\Ël _§Ð«µõzîíêþÖöÇJŠ¿[#7§ì0,F#«¤Ó |»€ï~ _ľ>.îCÒ“ê|bÈßëÀ·øNÄ‘¿Sx=P‘ZþkèFN?'þ-@Ï.’>C·}¤8~ü6 ÷—Ä/ß ï,Oÿ%Ð[Ê ?“bÓ_ú:k‚úcýÊÔ§Ç­?г+Ôè# êô~!Aý¾Ó– þ@o©’ãÐ_zá‚øôS@7ÚãÓ3ÒÑŸ’²º][¢‡ºU[ÿtôëÕtA[ ¯[8$}¯þxókuüòýèÙ5 êôA g)t›¶þX~ W·w.’þ¯ÿ¾|ñôB׎#v ç.‰¿ èƒ è;€¾»6>ý—@o©’ÞŽWÌiüø§0ÿô 7v/ƒñV=Nªü£[€>¸<¶½cõúÑô6 ÷ݧЖþòŸv�=÷Nð‹ÒUåcãý?û9à+j’nNVó¡I]¬ñ[ß>Á9$Ý®.j<½ô ›ºVÞ¯Š,ú•qèóˆ‘7­>¬Ìäùg(t­<>ôB§º½´þð÷ë¤ñ>ÒŠŸø ÅÿJœò½—€Îü¢ï¿RÕÜx$%¦ø 9Àß[;$-UäÆÁ’‡S¬!}m u‡ANÏ|ÙëÕ~-´wz¾vîŸøò7€®WÕ‹ñU WY ê|Ë’®Ò©òU•ÿ¢wiÛä¥N#/_ð}¬åƒñ³V3~f€ lß4$ÍPó©ý ïz•B_Èä·\öÿ~èGÔñKB@7ìЫÊí¾îÍCÒ@ªšï‘÷ÒXý™ÿ |…Û†¤éQ~Z mÖoÁŽTηøj¶ƒœªÓcíi *±é9ß)à>­Þ@óôävŸ]Ô=$­VË¥ªf½»[íWiåïåÙôêvPͧñ‡|j¾9$ýL¡—³vªVµëà;úÍøöä ÷ý–8åxâCDŒõ‰‹@ßùèT®Ä×ú§×ÀDµèßS×£&”‚+Lÿ.<6$}]MWÅ_It—BÒ ï|LÝÎýfýôA ŸS÷?ȉõa½¢P¬ÿ¯çñ!éšt´ýÊúó{bH²]%óY™¾ðÙ©€lŒo"؃ž_IsÕóˆå\¥P»d9ž|G_’f'˜Ÿ!_ð ¿µ~Ãôo‘¢ÌþßöW†¤o«ë[ÎKGbÌí?Ú«ßI^µWžg¿|Ë{†$GŒþeöè»þS%ò˜ùå€ÍÝýˆåZ‘‘jÞšœž\’‘f<ü¶¼úÍÆ?àsí’æ°óÖ’Ò“­ŒÉ¿–y² F¯µÉå1ÃÓò*´'"pQJ‡eÒc}2SY8"¯ÆÉa&…3xB²þ ”W·;(s¹þYô"¯Î¿AŸ¢Ë*¯Ã к¯¼î>LñåõCyþ«rþ·jñE7q(¯æï¦øò꿜ß9I“Ü:ªÞ?ýŸDÿâ…å-WÄŒŸ<‹Ã+ ^Op:Á‚‹ 6ôÜBðQ‚O|–à^‚‡ 'xš`òlÊŸàõ§,!¸˜`AÁ-%øÁg î%x˜àq‚§ &ßFù¼žàt‚%l è!¸…ࣟ"ø,Á½<Nð4ÁäBÊŸàõ§,!¸˜`AÁ-%øÁg î%x˜àq‚§ &[(‚לN°„àb‚ =·|”àÿ3ÿ*JKç˜s+ÖM5ã)ÖÙæ»è ÓÝø ivþŒüYÿŸ ˜ò¼ëÛ}Ž€>‡-ò[‹ÃÛ¢ËkZï}]žÇÙæÀ�½u´ùty¸åQ—çs®ƒ¿x2 hî&‡Ï±Ø_gK=?Çí­º<Üœç…0÷6BʫݽÁ ÁFw;nyø'ˆ®Õâ˜"ƒQ0‡™.Û{y\µ´ñ*º<ÉpÕ•ÊßJäøòø0•hr|y\“áQŸ³£¿ßPYäøò¸$Cy\Š.¿œÿܨøò8'ÃaUü´ñ­T&y–Çq~%ª¼Ñ_ÿ‹¢âŸš©…{¢"ÈAy?Á‚¨ø»,Z(ÑÆÏÒu‹¢âË~‡ £wD—ÅWv%d&ià;9Zþèüë)¾ì—ÈߥÑwÍ£QnŽŠïŒÊÿ…ôýy¸'ª¢w s’ªÊ_öZ*xü"Ê_¦Ëñåz¬‹*ÿnò«v I¾xùoŠŠÿÅá2ãoŠßOñû)~4t8_ö „¨ŠGÅ—a7½+ý·€úoµT†æ¨ü×ÖiuüÜ(b4ÈËWÅ9…ޝ‘]Õ¿–%œs99Ä8ÅB[­ÿãâäß³šÇÿ‡)qþ‰þ¥�÷`L|²b_µøÅnjñzÅjñÅÎiñ©ŠýÒâÓ»¤Å{£ÅS숟±üxEÿµø E¯µøÌ˜~} X-Yµx“¢_Z|–¢7ZüŠ>hñW*r®ÅG[{Müä8ø¯ÂakéuŸŽš×\Ÿ/Ë—Ä—ý“Ê/†8øö8øuqð8ø`üqð?ˆƒ;þø—ä—þ™/ÉÿeÛ?^:¸<=öà?Tç+AßÍt0[^¯¸ðY÷ð°lVª¯«âá…„¿;NúMqðmqð>JÏxøf2æO~ÕZÎ%þ}„ïø‰¶üÃqÒ?GüÝ{©>„¿)96^2ŽIŸJÑvé6†O×uOãvO6Óå2>_‹¯‹“þÊ8øÆd^Îm<,ÂQ¿\OøG ßCíCÓqvkt¬ô{‰˜Úm?ùõ¯Çáÿ#ñoþÿŒðKíà¿ÓryVóðÕ„—dü¿òp˜ð×¥\mäáÏ _!ãàá„oO‰#?ÄŒò}†ðß |þ£<üÂ?-ó“üßOø½„ØÌòßòá7?ÂômOgÔS9›©>Äÿ¿Ž‡§¡>vùKˆ€úw>¥³DÏåª&JÞVÇI§-Þ¿å;ÚOØLå~ˆ‡åöÿ á;¨œ‹ÿ"áó£êûÂwGéo²ãwUðð{Tß,­äa‹¼Kø|ê_ÊF7ðkÆsÆ9Ä_bˆ]_›œ/ɡܞ~¯ZÅÃ+É ÄI'Hüæ—µåüwï¯ü(;ð[â?¶“‡e{õ.áÙPÕ>'äòtó°,·é©±õîúTjJç-*Ï Âïéá᫈¿Næ'ùßMxáWmàá' ÿ¸Œïåá ß“Êë»'ª¾o¿™ìŒì>NøÂËm›FíCçeåƒÿ·~W ç~NZ="þUi¼<?¥qä.Â÷lçaÙ®>'-Ä¿‡äM/z‡mÔ ? üp*Ï×Aü¿Iãí3¥¿ûâäûZü!9ßoòð„ÿ”ðQr•e¤ö¤ñN^>ø:ák?ƒð6Âw“bÉrØFø¬yøá•Óÿ1ß.׋ðɼ¾ß&9<Jøá-<, _ÈélâaÙÎÜ0ŽøÐòÛÆÅnŸjâïø[èŠ{ÉgTûofé|* Dùÿ;ätôœßKòó8¥Ó%ç?ŽSž_R:5ähÈvòÂüË3¿~Èò|ÓiÜ!~Yþo%|÷<,Û‡…éqü âßó¸–?@øá9<ü‡~ô¼ï1¹<oó°,'{ŸExRÝkr¾¤Ð²ßòn:µgT¿œ$þžyx)e?Bx]Ër8y<O'+ª_nOü$o²\-&ü*jOù»Ý&ÂÛªåz|œþ%þ¬€¶<‡¨<5Qåù€ø‡ïæaYžÿ!—³“‡eÿçÿd}þ#Ëz}3á{*yøÂÏ!üp—¶üõ„_õ /¤…¿­r:tWî�­C<Aø¢U<ü5Jç¼^QõzQN‡ì†¼Løá÷ü'ÓxúQFìöüïŒØúø¹œ~#O&yø"ƒûçÑóú¯dòtºkµøìLÒëoóðJJgáWQùåå••™±Ëé&þoñ0‰»îaÂïÚÁÃþW„7¦ó ]”ïeR{FÉÿÄŸOòIî¹îÏ„ßLòCáî4á‡ÉþËß®˜@rEþ›,Ÿ·~˜ê{᛿ùa–íOá»CZü ßAþ°ÜV}b·ÛŸãà?Ó?ÍÃsI!O~O‡åùÂWL„²ŸóL±ý¢â~Œ‡e»wþm¦Øå Q:Y«xX¶{ß#ü@“úéöÆI§ø;ÈÞR÷èŽÈéG•S7ÍïõL‹wwµn~Ar¸Øm{ÎÛuóü­m¾[[]·ë€Áëó77ç5&HÂI4µz;¾Æ`”OˆÂ«|U¼âyR/RùgøÚÜÜ:{¦œr‹»Ý9Í·âN[ëeNùWÌYÎë gOó»Z×A¬%ºVWc›¿É‰‡òòZÔ!ŸÃ³Úé\†Ý®æÖÕ«…ïmïÐáÉæz¨p½×慎|êùÁÖúfÓI·M©.aV]QÜÔäÁ·]ε˜†Âåuú0]<‹—¥y×{1ϸù@¢ËZ]Mîµüàl~\F~†|ÓY‚äj XNÏÕMÚ^W‡¢Å/DäÖ•ú¼C£€÷m÷»Úºú²;WÙJ#—)óË™• ¿”rvºÖèÖ6zÛœ®Dåc·Û²Û›"W%®t„¯ÝÙÞØ±^W¹ÐV_í²»Mºúz¯¯ šºC{d}}ã:G}s«‹Õ ;%Ò ÊF÷ÕÆã`¥®¯¯¬ŸQn³]W pµ³;ù°yêå‹Ó«HX\T˜DíÕä\WÝ ™—$Ê„®<Ñ­ç·ŽÑÔ,ÍÐ.¼^pôeãŠ0¨®ìnGéijã%v4àeÄLÒ.£á*¡áTW9ɺ–¸”ü6âÄ<tç6)-]dƒ}îÑôx[Ý.oÇjõíóìVn§ê¹½aF :5‘$±{^TWe;×ùðzÕz~Ujó‰·O®nõ¢$(MK'²XrÅÆ’"/©a}=èeÄwÔ³«j"í3†‚2k­—ÃÝ‘À€‚6-YßÞàŽŸåÀôº€:AÛ"… µÉãö±»7ã‹…7ÂݦÔX9Yo=Lqëë¹U-Ðn®Ävï×Õy[WƒIÒµE~å@•ªˆ#´ŒTª ØÔÆÚÕïÂqµ�Ë5lJNäÞ¤„ÁX•ŽH¬kìòð±u½,)­/ˆ¨S#Y°ÕÆªÑØÒäŒ?–£…bY•^–Õ^rY\˜VSîîI4ªÚʼ‘¾“¡¾¾Áë…qËáá£(ÓMÌÆ¡/oŽþ™‡V\c+mq6ÞçõÇ/kÐ˰c–ÀÄ/˜ïà·ŽÔ·û}Îuõx…S!¤Q,°¶\•êÙž§{]­²Ï¨Hb=bpìˆId#‰Ò:²º*­Hc÷êØ½Rcˇ úTãÖ$0·J¤’ââ厙" `û}xƒöeÈtYt™Av YT7Âñ»(ÇL¨œRÛy X6†ô)‰T&h y ¡_©à Eì^ȯdÇ®ÎSjÆ~/$±½a¿¾€öú^f«›™®êêlò»1õöê’b{}uyùkm}mq‰ÝZ\®øÉ{Ù¸¨þ¼�ÐæQÚ0–.`ŒdÙ@È®‡¦¯!Üê…S`¼öõè¿\†ÃÉ~¢ç2¬-A¿òŽŒš,%¨7 «•ÎÐ65³^ŸgêA•¹E Kì5*f)wuõ•kê“ ³»KñÝR¯œz}},'‹;ÆJµ‰:JíÁô†Uʇ—I•¬_ˆ{Cœ÷ÿ PK ����v©Æ@���������������com/sun/jna/win32-amd64/PK ���˜su?úÅõì6���'���com/sun/jna/win32-amd64/jnidispatch.dllä½y|SUú|“64l&,Ū ªF@ µ(hŠMµ.hGD«(A)bU`Ši¥1T«€ÔÅQgG±âVDmq›â6Œ3jÝo&Žƒ{uÔ¼çyžsÎ=÷榭ó{çýçí‡p·sžï³œå9{Ù9ÍZަi¹ì—NkZ«FA­÷¿=ì·×èm{iõmL«#òÚ˜3«æ-ñU/^xÉâ .ó͹àòËF}^ì[\s¹oÞå¾ÒSÏð]¶ð¢‹<x@!§ñÝí[NÚòËûˆß#ÌÚ§…]o>æÞ}Ãwïïó&¾?iŸ§ðzî>/ò°›ñ¹Œ_OÁëéóæTÁ·l<—‡4í¢kûi ïùCޱ¾ÎþšvºSÓºèÝä:‡¦yñîèž}vÁšqÕêrHyì/GKæ3Ýnþ“S{j»ÞçÔ¦ÂËÊí^ ÌÑÚ†* Wçhç:ú` ë_µSÛìÌþùÐèŵQv}ï4'1²çšÃøW‡.¾è‚èš6ÿd¢©EØõ,3á ûw(Ó¼5ì¿6ö´{wF¸¶C«) ÊÈdÕ†³ëz‹—,žÃîQ'•\ǹ9™ô_¼`! èƒïLWZ?vug„›ž]ÿÿþ‹ÄK Âñ3 ½áx¤ÐŽ}QN”à -œˆÓÍ,”ñêÌÂ0½/„YT_8¶¬Ð§E!"ìZÁ|<Xµ—¿l†R&{¡xǫֿR ?+¿¨p�¾ëŒOZ¸è…p¼¾0ÌÞ$ê «Ù…=Ù%ö4^4OýÛš|rDOâwΨ3ue$±º°Š=Dš\Ç=âÔ¶i@·‰±£Ÿyžƒ½.›òKÙ§0 YɾêüšNov$c '#ö sìJì¹jì»0v*,XeÚR¨Ì1SéœmP©P©Ì"*ÎÃÛàˆ.zU›¯|tcÖ¡Ù”%›†Éô h&ž\ý €ùUxAue3Pi£5½T…ÛK FXVXËlиòl­/¬cïÚ<õ` ŠÖêw0‘ÅEmð~#¼Ÿ),ïúPå|2*A­f_¦££e 5åW=J|Uë§ü’N§.exaÀK dr„cm D*žÂ®ËGGæ°Kx޾ÔN÷<‘œÏ=-õ*<³D©•B ÐNi§› \¤ž�ië'f×ÔØŒÐµÖÐÉ?€Œ§rMÊ i‚}‹ˆØ,^„Ç«ãñÊAÎr-zy„}¬.K E>Ñ<¥,d$TÔpå­$}S]?ʆÕÜnåÌnÏýˆv+ÿ×·É@A ”ëRû€^· ^Ã[„^¿ÿO:Ý QÌèU;²¦'Ó'LÖ¢Ô”fåSJºÿ'sJ‰Î—‘¦J! fBa�¨`ii/‰]ÎA*Ȧn™ÄÅ.O77ç˜@ qmEcð"§\/L!üž­¥…U mÑ•Ÿ»5,¸üe¤°Šp¢CþikÒfÔÖôÍNïø‰i ’pÍS©I<<¼ïdY5’è0ØuõC œt³n’ÆõÀO™ŸSú È ŒNl{%Ã}—•úJÈï [ÊÉÒEì…°PbVÕEOU8;’q†É[9KlÌŽ~/Ø© Êý€Ê=÷.-Kz ]+nöò@à‡<[E!m<B ¢õ{„øñû û€ç�ñ<vªµª’…ÛN*b¡>ZR3 ü䍨‹ñž†óF5ÆxÈívšj³ ÏVfÐè€VÐJr²9p{¶Ààg$óÌŸÿ!K`pr’ïj¦Àß}Ÿ%0¸GÉ?š·d£ìÀuæÀÇØóœ×: J³”7Úó× ®Yj¢ò{òZ‡hÒVÅÖúˆLU€©×óDp@<·0‘[Óì&6µpÀ§†ç䆛/ú<NÔ¯ì*`rälOwµžTlß-uXëpñ·—1­3à²oëIpÖz\µ–ÂÅÕ:ž*¯‚žÑÚ‹ŒÚ«ì÷&ÿÝÍ~›Ùïìwû=Áß? álþLéÍOÙ…¼–øòû}KI8nn÷ÔQs 5T¥M(fµ³Àj;sD&4ÂoîÕ“¾·¥ JK.±¡ú|Ÿ¨.ûÆ–*Ø 9ʆê¹ßõ…êõ6¡U°e²Ý™Iõõ¯ûBµÎ^¯P’‹l¨¶õ‰êÑö¼B‚KîgCuºÆlôjƒÍ¨BúM¾èȤzûW}¡z¨½µ ;$ÚPÝ¿O¨µ§ ¹+¹‰ªëßP‰PLáE‰p”dú×¶A´.ÁôÏþà ›|N3QžcÍÓ°Ú¬Þ6XÍ<%ÈÁ¶<Öœ ¹ü+Û ã” )Û ¬ìO…Nšý±BÇ¿£oå‡ë››è5ûs°Ðês| ?Íݘ4;ñIÿ,×–XfýInÜJ?©Œ'ºäZ‰ö›¾*7[qoå—Ñ/Ö¢£U»î1ó.‘Áª­+Q*¼p?Ø#Oâ$–ÿÈ—=z»{ð_,Mòšëê¸Ó‡·XSjVpË›8eVúX­Ïš©:l¿»Ÿ9lg8´¢¶¤CÉ.Û¼¼}À¤nQR1pÊ@oj4>wpü°Ã[áŒÌ{e¨h'SÄžšjÉþ÷Kþ^9ø{Þ¸(—~xêÇ`Œüè¿x^ç~„)æf¥r(ìåÍøÇ¢7ø—¦ŽT&ØbµM‘-=³V cê%4dþª/eÔlÉÙóÓætå³Ï‘xOíhXñΧüË©>4“ÜÁ“4!ª¹.« Ô¬TRÛOàÄo«H5•z{oJ\/6%î¼W4%–)š¯Û¥ÎøEÂw|ŸNƒ-JÁ×®FLÏV®ƒr½K4Q…×oöö!}—B…ø—ÙÛ·çŦü0ÊÛ¾0—~Ù$âšã•°‡¥Ìa½fº<+©Ì&¤my3FIOý3³œå…Mñø*PIçqf)?Ë=#\Ïé«fHSK;ö½%È55[“æ§9DåëJþ”c ÚôÏlA#J¨K¾°„bD Ýbfa=l|ÄRÜd<ÆÝÉF3b,+b™ê½”9”›ÇDoØól¤pŒ‰é¾Žit$óÍz6ÈS•PZB0"s™ Ìç"……s0a>Á‚Ì;’&ÐDV9OQB}g1•—ƒú Õ>)ôÛ`ús¢󠬂ž¬„ºÒÊ—ƒ-Þ‰úÒ&JÈÉž&2À§Ì×g<L UlQdHb7"Dâɹf¢Éϲ=D µãss¨�·yå7ž!Zr˜™äåŸ÷…ä&‹4Å‚ä×@²M3‘üOVÑÇ+&o/;LX™*K÷JýáSYQ^­Ð{*+‹#z3ÿ]CRu©CõƒÊ¤ÏPŠMùµ2ÇaýÉÍTy\wº£‡òØè?kT‹ϳ¥…PdB‰ÓÇ‹ž‘žUWAÚ›iÓà_ÿšN{¶Ö;î=ê+kÇó¦7p^Iœÿ'•N'ÃNêLcï†ÒëOàõ$S2fÌ–%ú3•Ô~Š•ÊÁŸI¯ :“9<tå\Ïåžsþ.bÎáYñ7íŠv÷'æh¯B¹R1«z DœÁ¨ýü>5ê/Œ´­@=}ƒ1VòJ!ä ùÀR¨Eˆ¾°”î4%¹@G¬Ÿ¡üYÊ/øÔÌÙá È^H×ó,æH(ÖŒ'•ÚDFmê%ÿp µO´ jO@ü€ FO*5–:]³ˆ··,ÔnΤöÄŸ,¨Ñ“Jm2£ö8Q;åcµÔÉ6º,VuY¬OîU"Tf!$¼ðæñLÑÁƒ5ízö»ŸÝ/d¿ƒXò+a×°wg×ìW Í«ög΀Ü\ýÄ‹<º¸³„Ðß.ÿdŒAÞ)‡²œ‰äéÓá–%ÿ$Ϫ?k”wü,ï<ÿåßDÌ;ßNy§ÉuË/Éë!l“+u"»¿†î`÷©+ÙM?€•"M®Càævs&ÜœÇn®†›™ìæ:¸‰°›+!–â>‘ÿ~χ¨à‰ŸÈžv¥\¶ÔÊX9–ÊcD…4 ã­ÇX½”ýÖ³ßõì·ŒýØo ûmd¿ÓÙïì÷)ûi瀜r]B‡ýHÍ}Õ±~¯cê/¤Ž•GÓ——ãWì —Q/\Έö‡Ë9Qgª?c½?ÈŸ_ I°\éðþ+6¸- ÐÒlþWw}™é\霚tƒ«KEÏK¼}dÛ̦ÔEÿÎ2H93¼ò‹jðµ›@þtmꀡÊÃñ†�{ÛÂ2%ÑPÉb]9ñiÑxQ{ˆ·/Ê4ÖUá]¼H7W°ÿã : ³òø*˜G•<©Ñ8â1kœÚ6l¯6•UêñF A3tú§ ¢øˆ•Ö1$Þ& ÞþaN8±=kó„Ù~Å€pëž4þy±®=ú±¨º¸Îº5#âü=D\aŠòõ .lŽì[¬¡f&a2J{Ç!Sý¡� † TÜ”PÀÓtõ UË¥,ˆUÓøoÀAÙØ3pÑjf„ç”±€¤ƒöÜ´Õœ™ìUY¥¤ÇK!%åîRC5 ïà{°?¼#ùÓ@›åÞ×Þã¬C(ox Laïê² ›Clx¶vpØø N…¯·ï_ZùÚÐ#_—›Â~õAfXð÷YNÝ­à–6ŽìP8ûûô>qÖ1ÀÂÙSÿ艳…¦°§Ú„-@ΞP9›[7±VÝ7Ö±²VÑ“Ò<õ£ØmQ[¬ÛaárË{™Ñ¼½sùhIŸ¸¼[T¦šy&ЩY@11=aŸ˜ªû„˜**y†¸X™ôïëÕ2黩P&ú‰½ŒÄg—‡¡xЇГ+˜“]n‹2÷û{Ov¾Ìö›°Ð**M˜ YÛ‚}Ò߉VÆÎù Œýj“�EëÉ€˜;­oŒý)ÏÂØ¢¿õÄØ|SØÐ»™a±f`²vX®ÊÖ?ë[{[Ùzô7°õ™ [Åf¶¾¶°5¿olÑ´ j  ”ÁÖ,ó'}ÿ¨Ý–ÕhÛ'6~tY؈öƧþAt L¬L×mY1šý…(„‘9¬¦C†›ö£qOJä¶|³:76µC«™Tš( ° ¼4¡fàÉS0ß’°dàg˜n“£­’ýô×%£ÑrӻݽäŒS§õEÉêWÆÓ� DÈžÓA~ sHšÎt{ͯ"M·^µ„ç·DàMN8w> ›%ìÙ4£ÖìǾþ:`8ÄTåzˆn‹“7¸ôKüI='C §±Ôšèy64õ{îcÝOýbÞÞàå3AÙ­›ßBxƒˆãÿÐ`¡›gàÿÏªÅØàÇw¬Ù2°ƒš-›§a³eõ4Ùä6ı³ýZÉ3¢~ZÃQ­ë¦÷nÕ55Û’.§ĆT[R0qÓLj›£RYH¹3H-ìTgRÞ R…½‘Ú…TA©¿i½êÊBÊ—AêúÞHéYHù­¤R'ôHgO:+æAcjÅüðå*™ry~ø½SãžtS7qO,ÍeΟ9¥/93tgxþ,C—±lK.|<wHÆ>†3ÄñÅÍ¿xeMhfOËdokqŸJÿ˾I§Øì‹7bÖ„ÿµè@ºqD¦†¶‡ÂØ 5MÜL.M4u‘?•¾V-Žï+Ââxè*Kq|(Ú„åÔ²m‚&i)5€3Y¸LÓ}X€ž&öá$˜ÿõµF;qöià8{—ÒfÍe5£»hg:ÿý"†IõH$ÑP‹Ö_W £^¹ÍeN:ÿ>!ÞP�Ó°žÿµh¤¤ær‡›ÖÁHn¢ Þê'ð¡5_)½`2/4ɼåp”yîµf™S£=[]ÓÞtj mx:ÿ(†Cà$ÌvˆɬT~­L}3é' Ö|bÓ^ ùYcqù±hA¨  ò„jÙ¼Œ%m ùÊ¥nw81“%œáNŽuÉôô™^oj »-K,l1 V.XºþtšÝC·@¸è5Ýä 7Ýd³wÌm>³XÓîd¿aGkÚ+S4íö«c¿²wýÙïwGcŒ;ÇíÎu¹ù_?ºäÉþÑ5Á{>*Âbº¬œ§+»9›`„ÔÎÄ‚^Ê¢7žÔ†±lž?øp'tëq.îŠèÕ –Ķ;ŒñpA F̱[{Q(é1²ŒRò ôÔ$§–rã×—41oÇ&Âéü›'AÚƒˆ±*l;¸m‡{E'ãØVÑ)‡‹«¬£¾Ù¦¶+4J{~eòÀשS߯P*Ó݇(]lïþ-ÆéÙ¶óÙ{Çä™h{ê_0†ÞÚi mtаÚY̓®zÍÔðk‹Nîd™™úê×{Œ‚s÷5G¹«gŒò¹™±é1 ÎÕ|ÌåÏ=3æ†(+ÍQê^Íe€@I¦„÷eåj€€H‘SžY™4º‡ï‹xŸ«þìë¬`‡!¸G ýÌé êœíÙ'<ÛŸÁ’FPâH»ÊiõÆ`Óý¯dlòÔw™CÖ¿šeò¤²�KeMæðO¿f©ŒÆ«x€a¯g`íìÄŽ’œ"÷”3U<X›KIÝÕZto¾WK¿Ï‘Î ¢984BŽëH§ÓÉû™ø}+»&>2KöÓ+½hâsø¯_éEýmT%4±««]tô¦ wﺺ8ÆeâØûr_uñÒË¿MŸíìE¿Ø(«OºèìMÞ>êâ0óЇ³ëâc³lG÷–.šÍá¯y¹]|e@èb{—³eìîM½+c,(c¬yÅÂ}UÆÅ¿MÓzKŸØ0”‘Óƒ2ºzS†¯ÊÈ7O²¸ð¥¾*cÓoTF°£eüÕ&@ßR†Þ›2ü}T†Ûabù€Ž¬Êxß,ÜS/þ6eÄÚ{QÆË6ú–2öô¦Œ@ß”Á¸©p­Ò¢87§Šjë¸^Vò—•äp²—sødžŒÊ=8ë¿üýøžÆoù YƳÒOÔÖi&Óວãè²|çãðúÀ—˜»Š³gj¥×ÈÎÌ q”UòÑ'ùþrpOò=ùQ:ýµÇk/`ŸèŸÝ#ýß1úßüŸè¯÷÷DÿÇÑé³5Oßè_Ò#ý[>üoì¯Ò?ñ žèOùÐlRÓ_–ù‹°ž&-72–Ô ÓSê©/läÉΫ[ë4ZJWËg5à &£ÆgS£ïmjêeBù!fæ1¯X£e̯ÌXŒ€«èN¹>8—¼gŸp£ÉŸŽ=]X m€èÁáÄZ¼ÅE¶ìª¿¤ij¬&¯¨-‰å•£JZ¡1@3²|éüôXÐo}aæÁ{¡cäSå 诨 ˜9¢Õ–ο•}LÀD‡^¾:ëÛ`Ý0öÁpfæ=/‹¨¨/,¤Õ~\ Š^ÕßCK€sMK€ÍVËl®ã”*ˆÇ<GqZÉ(Ù‹EþÞ—'ꆆ× rRm%Ÿ£Àl öµ9 %´ëC½{œÃTM¶‹ÚJM«AK±œ:`)•S•úœíétQ[êj É»(øú9¯ÚÄ™×h>¥ ÉÀƒ>å…eÁÁ¢˜½æÆ•gãH俌N »ü7“©ºRLþxè~>ù#ä§A߉‘ÄÌ Î] …yWYei"TACeÁ’ôMaI 5e¨r ¬èßòÁMª{ù!_<ãaÓl³"~v™æR°T°Ü­ÕŒd^¼5ƒ CÕmÌ-¸CCLfÛë×`άº«š «í!¯è7ób¿™7Ò¦„¼Ë÷‹Ì 1Ïô²Àa b¬»~ y4å2=„X†\‘³=ª`LiñP%»™ÒVÌZòB&Vª€ñþåbä=½˜ó~Âs¸^¹Ì½h;÷‡^´‹³í ´Ìò"LåEÎ}²¼(fo ûF³WSê1šÊéü‚ÑÐÁX3¸½¾0�/€«üþXÁ‚·Q–ËÛ_Íå–Ë/ýBäò×P./‡\{JùÕÙ9gÌÎasv¶/e¡wì½RQòTç 'Àè Z[X«–vº¥7kv$Ágd˜¨–Q€ým”µ�»!%D{{¥R€mzDƒ‚këh¥à rI÷eWpÙÛKf-Æž«ü‘µ˜p!rê˜Ùæ³…*ÀœÉ2sYE:¿föójÉ75°àyôè4•±‚¤ ò2¯Þ¹Ÿìº?K`ß-À6â2L`åú§OC铼² dÍo«2¿1ï1T�¥0И˜Y€ ó Ä™º¥=乯¹ÆÍºYØ¢7ž„/ᾊӶ¤2 Â( ÚžÎß4’„ay™%2gÊ™z†e$`4€é?LÿAÛôŸYD}Pl©OÙ×ìõÁý–ú µ>ÐS£êâ"se0W«¢5ZŠÊଧ°28æWòyÀÅr¯„ ÚN¨deT•Q‘9ç’ÃQ<ý¥Ü[.EA_¬ôÙÖo˜f KäÚRl`¥•sl\ëóÑ â›Âó¾qZÇ+ ^1øl¥òg ”S·ã?ÚM}ü‡ÿjÁÿIÄÿ,Éÿ®'ûÎßñªž“ð†I¼ þ'xoÛrÄ{e¹ÀûªõW)ð^Lâ­üŸà• ¼ñ„wŒÄb‡—uÿ ã7yÒ\æš M6–ËݤîtMøTÖçl÷<ë†YÔUÇtÔ à k^Žø`¿ä?ÕŠl>´ Ù|z™`óÒ'”½Hìçßþ_ôS'ôsÏ—À_mý+îMÛüÙ«ÿ8ÿvé?’Çr‹ºÎêNº³¹“àNìÕdžZ™·gwòÿqdq'cóP}kçq—ìÏ3wr½áNÂ:íöÐpQã Ço8s§„†/›(ŽÎ_dNs‡3 nÖp_³Ók#fib6s>+`‚û‘^áGz¹éMÎ~d?bzoÁô¯)~äÊá6~äÿ9½mémèUˆ=î*™ÿû_äÿMïå+ïïWÊüÿ?Ákx×Þ‰wןÿxÞÑ„w²Ä›ô?Ákx_×"^ŽÄÛþ¨-^FQñ”Ÿ%¸± •MàÏÙ‡¯ ~žè¾Y+ç=*Ö¿|]Æ¢ ý¢6Xyè+Á f‡@h$„[$Bž0×2Hi·œ¨�¨DA¸³€#œBçK„û¶HÛaÐÞìcÚi%lô¥›ýy¦.!~çzÎnûÚ3UbaøË½9Ã/^ ¿s…`øÛÍ’aì›ÌƱ ƒû†Ãåšá~/àVÜ ®Á€û¦'8;<Ã8¯€;ŠàÂn„÷±÷##8À¯Ô[ã‘�?Òg{÷9ý–´VBÛ¹T øHÖô›ÅŸñãò!%!éE[l÷ëÈVß{ž=SÖóù¿IÿxbîLÉ܃̽b£…,üóò¡I®=>Ÿ €ý$ÀŒ?ºÎ�˜›¥ƒ¸ÖW¨Šn’ÛŸíΡÚkjw€zÿ=@õ¨¯0ª*ndBnž€«#¸%ÜÂ^á^eûÜ, t›è0%sº„q÷�óMvú¹fúwãôûý}%ýMg§ŸÍ<ää߈"ù£Òþ=Ïj õ|<”Ó¿•è?$é'úoÒ—‘ª8À5àX,{�Ⱦ߅êusºÙG½ … XË‹»@Ê Æa±ÐÆ9>#’þvûSûE®¥)]åúù²Ó Û1ŒN Ð'UL«ÔÂåÚBªÏ/Áæ…\;á…_bPÆÒm›¡›N¾¬nôÆè“¿dî5LÄâ}|h岇喃¼gþ¦ÿe/΃úÞ‹ã8õ3ô<Ñ‹Óü‡ÿ¯zqzÒ7ÍéÓg tØï“%|j€møÌýjyÿò=×›ú—Ô­§^cÙcìãwŽè(~çŒîËïr¢{§Fb¶M†¥Cê$Ìc©ãéåÔRÞáÊt]¶v“Å®Y¨Ü®É£ÝdLÿ'-RzoJ§i«*¥ë¹1϶ëÿL¢Õ⌠WG“­’½yɵí§æÀh �Vÿ¹šPá îìº FbÓ®cÍSS÷bÓ–ÁSÍ^ávÚÕ[‹ç©”ùçܯPâÛ¹BDlš½\D÷0¼Ÿ†ãÊÔ)ŽW¾s0Œ"V)nA5&Ò&…]]¿Ÿåµ—ó)*g$Ò48ΰ×aÐü÷©lÑìq׈­½Ø'‰º\#°QojðY!–p06*$ÓˆËU6.�6ŽÏ`ãò¹ÈÆbcª‰=¸Y—«ýdã¹{[ï!6*%• •’ÄÆ©*ïÞÇØxz¸‰ Rhtj™vyí^D~ñ^'§XHg«â¥Ã­‚ýŽô{ »W 7£‰»ú݇ä•ä¹ÞV¨äò3È_CäŸ"ò…vä?¿Ép· ‘ÿ@%ÿò½ŒüÃìäÿNä)Ýç¿x yÌÒu ÿ‘÷-RÈÏòeä !ù‹1hþ\;ò7’îãR9å‹È¿RÉò_µ’_Gä¿$òyväϹ ÉŸ~— ¿‹È]¬ÿã=Œü fò,Ì^J* ¢¤‹÷¨¥Áy÷Èì\•¥4˜jb§ºƒˆØh#zµZ¸yê¯æ¥A5/ ªxiP%Óÿ éUcïÞ éˆUc÷Æ#Ýw·MiÞˆl7ŠlxÔFb¤VfÃZ%ÖJ6® 66«lœl‘ÁÆÃÄÆÛÄÆþwÛîÊ;‘Ew õùUòßÞÅȿ㵒ÿŽÈ“ýòŸ»ËFʱ$å¾RÊ!\Ê:)e"e”r'±áTÓÏeÀÆ©l¼|²ñIYlÇÆ,JžgÜ%Ø8é.b£Q²Ñ¨°Ñ(Ùx”ØH©Úøl#c£Ãcec ±ÑEllÙhÃF=){Ù‚%wÍ’f…fÉÆmÄÆ[*ç%lt’QB8<–?Þįî»ÙØp‡`cíÄF‹d£Ea£E²±šØxQec÷Œm{YÙx»ÙØFÚ¸çN6®%6VH6j8%66J6jˆ?©lœl™Ù`aTÂPÙÒ¹Q;6¨e˶ ²pØ”¥liÙ`S¶ÄˆØÕFôÍjÙrÑh3/[6ó²e/[6I‘Æ’Hg¨"­¸ƒ‰T9Ø*Ò©"m .Ö™DZeðÔšE¤‹íD*'ba#z›*Ò$.R©‹ÔÊEj•"=GõW—Z=>´‰Ô4È*Ò?2üÁÞŽ\|t»*Ò[·Kž:²ˆôèí6"m bëŒèªH+n'‘:¹H\¤.R‡é"iƒ*ÒîÛ!ý´ŠtK†HÝJõЭªHCn•<íÎ"Ò¿o±éÝ[Ø·Èè]ªHOÝB"uq‘º¸H»¹H»¥HCH¤éªH.éóÖ,},91WSab‹×&sˆ­Y·ˆÚä"JþþÛùÆ òwù"ÿc‹ ùWI…/Ü*È?Eä©ÙæD py7•‹“¨@ºÕŽü´$D‹ ?‘rår•¼~+øÿý­äWùˆûcìÈFÊy_*çDþW•|ÈÏïoMN?edúÝÄlg‹šœžo‘éAÏ’œ6¶Ø$§‹Ñ÷¨ÉiA %§=<9íáÉIçÉI—ÉéR)¡Š´õ&R‹Û*ÒªÌrŒ´îS9f$ñîlå˜])'ba#º8”‰Ê1žC4'ñ…×8a€HÝR¤4¥1¿*ÒÇ-L¤ó²øáGP{ݤiî8Ö#[?Þ,*½=7#n§¨ôÜN£ÒÃ{dc±q°ÊƹÀƱyVٓ͡ɾǛˆµ½N{ͳK,¿’߯—Ñ TÍ~°ž*àš-àš É+EZG"=«ñõþûY5û>¹¦ìµl½;ñÜZd뉵B³ZKŒø¤f}Šf}’¹ÄÆ*N`ã—U³·e”ê‹Ö!ê¼uªfg­“ªñgÑì”u6š-$b#èU³®u$P€k6À5ëçšõK‘’H“U‘»™‰4ÄeÕl€ŠÝÒìÝkm4»ØZ·Nhv5g¤Xj¶XÑl±dã[šªuˆÊƦuŒër­lLlMlügMÖ¹c ²qóÁFÓb#(Ù*l%ÿ"6öWÙlääZ ¼o†½„ê^£ø§›¤…ÂY ¼û&ï¸ ‰=eD/W |ïM$P97p97p˜8,Eº‰DÚ±P锵L$Ú1UÕìs Q³mÔ (¹ÉF³“Œ÷JÍÞÆ5[!5[¡h¶B²q5±ñ´ÊFûÆÆœVͶ.´h–¹Á@¯Éõ:|ÁNè&×ü¾˜Ý¿Ãïa/…ø}öâ÷°dÿK~_Áî¿æ÷•ìþ{~_Åîâ÷°¥â¯ü¾–Ý;«IÈ:b#‡?6Ò£›?6ÓãïäÊÓã×ü±•ÿÃÛèñ þØAÿúŽ;éñ{þ¸›Óü±‹ãò¸ºw=îËi3 ×8þHG…¹&òG7=Á½¢¹¡ »‘Ý"ªÝ<71»}¥ác&I8AËÇÇNh$µ{°'öº¦á{ß)‰È«°ÐŒÊ62´ב뾯¬‰p(&Âzü ýƒhz™ ër)oä l#2º¿ŒÐ”ÿÏËN/¾1ND‹ì×¾µY!¥_ºŽyà N7µ3ެàÝÝ•ºèUý¦èðÊ 3»÷ùÇg„W~±Z3|zÖ«0 ‹{;6ÃÛön­Ð*ÈÕ`|`Ú~7B!Ò°“Ìs$È•Ã9lMùA”°©EÔעêǖÃ1W dìC)|LbÖ}¢ìf÷UúÅpßTšã`_+ٛʞ§m}ÿ¶iÚVy8{3”U0{~†óœqŽq¹N™º¿Û,/×¢û ^ÊÊÓùž=ÊáD&L¹þþßòE8A—¸kR317CÉ75ÿHnö90Ñ¡Ï~FÌ_r™¡Œ+n`joþ“?:p@#ŠŸóß»Áõá.ºsm»a½`.JçO7Á¸æû§i”e„ÁG_†…ãñ—É’&HŒúy_P&L–Ovôë v.ECÂ\nü–Î?†áÒÑ cÀ¼ sÙ±Jì¯Ç©ã“0¬ëDQÜ™>c ÝhÙO‘K”È—±Èɯ1 ãŒ8wºÜi‰ër4´Eýð>=ÓÏbïK±‹”؇ôVSü˜øÎ@½4ooE›¨Dënb™írÚ åx¼¡xŽ=-:‰_À§P˜ ÓÊŸhÊ|ŽBè ßö"ƒ”okš –y‰)ŸÁŸ g;&š0[½¦ó(þ¾MìqÖì¬óŸp¸X¬«’㜱«‰<œJ/úI£íŠ•íKŠMëmÎ3CÏMù§À¶š8øl:þÒS/ªœ¯qeíèã1ÍÁóÝù½|QEtòäÌm¼žR­ÜL™‹Òãˆ\V�ƒÞ•,q 1Ëêßîd¦¦¼Àc¿2A1uÂÙ˜Aâ™ÎKÐ#š§Éô4_HÝÝ„fÂfìn‚IÐ7ŸÒý|RfP¿"Ã.›©C{€¨q× §MÙ ¬üI€‹“Kgf~Óãy/~ý5ë©=vx®w¯Ë†g½Îõ˜M(ëø0Lç¯ù§C3f#å?i;ÉÊ/ž>“?ñRR\@¿|µ5š]x· lÏáqVd<¬‡2w‡%{VðÁî(S¡ñŒ;¦OåÙhjœï“­Õ£.àV"Ói9‡| IEœ–³fžSÓïþ¦7'×a"‚¹_÷iæ4~þÿd¬H‚F¶!ê%_ ;ƒó&RS)öúyæØèOfÆ^ ±Vbï^uQ9ãÙo-!¬öá£þÜ)/ÏÞðâãÈŸ7ÔΨmÄš«FN£ ÙÝ\¸«(3Pêt•D"ÿÄF…„h`NTÆqq•ìëîÑèT'°(ùnó.ºujÉ9~+£?keôƒkâ¯× F_»V‚ìýÓµ6ŒÞBTn4âKF—_+bˆX$ Œ^Œn;ÈÊhu£{D?ÉèÏ ,hÇè{ 6Œv4 •6#nX2úpƒh•$øfYA~@-0úë4Æè Fó2ý#AÜ× ½Í�+·ct™£—•ó¸’Ñ“D3I̼ã'Ì"£MÀhÕVF¯Ÿfetj=BL®ŒŽ¯—`•vŒ¬·aôûRù2&ãVIF߉v[‚WÙ•´ç2:}î�+£® Fo»!Ö\#_#Áªí½ôFg•2#n­dtÊ5¢!)6K¨æ³€ÑÄTÆèˆ FWOµ2:žt16¦ö. Éî:Mí]0øýÚŽß÷ˆß·¯‘ÑEtè]xö¥+—æóö/ ¾b« ØÿùÆþÙ…Vö9ÆÊþŸW"âC+Uö7¬”øÍYØ¿z¥ ûs‰ØyFô•ýWû-œýÎ~3g¿Y²°ß2ÎÊþåìßN [{Êþµ†ú6faÿ";íŸJÄN4¢oRÙpíoâìoÒìÚ¼ÀþûG3öÿ6ÖÊþGg”¤°ûLÚ_o¨osökì´_IÄ*Œè­*û%\û­œýVÎþfÎþfÉþ©Àþ¾ì—g°ÿå Dül…Êþî¿- û[Wذ»ÅˆÞ¡²Í §Ñ#“àWÑaÃØo“ì·MaìÏceû+ûé:Dü¡Ne?Y'ñ;³°ß^gÃþ£DìA#ún•ý5uN£Ë(Á¯¢G‰±ß)ÙŸìßî³²xûU˱r9²¿�»ˆ¦¾\âwea?°Ü†ýýˆØ0#º®²ÿó2§Ñ§•àWÑåÅØG,Oý皉mÖTJcS©¥Ûû/S¡¡m½&în2iÄ~%×/˻–ú|™äuOQ_\f#êf"¶ÉˆÞ­Šz#µ›ËÒÍEÝÃEÝ#-õ—£˜¥¾Þßj©Ž£¬–z–·.S-¥àk{ö¯µc›gD{E#û§söÝ|¤ÏÍGú4Þ…ˆXv–:ø(´Ô‘È}þÀÞ-¥_mc©5”ŽËUK-7R•7‹¨³ìå DìX#z*êAËIÔ.jUô–ÒÅXŸÉ,•QŸiµÔ²«1zµj©¹WK|_öK¯¶aÿ0"vݯ²ï½šØ÷söýœ}gß—ÍRíG ¥Þ:-µóª^-uÿU6–Zt%¥£+Mã^WWYDr¥¨…Dl¤½XÕu%‰ZÌE-梸¨i©L¨äûY-•{„ÕRÏ_…ˆÛ®R-õðU?˜…ý뮲a¿†ˆ]fD«ìŸu±æì‡9ûAÎ~0›¥.žŒ–Z<-åéÝR_\ic©Ãˆ»ƒ®R-µÁkyQ°³ÔGd©w KU¨¢îà–ªà¢VpQ˹¨åÒR(b–úv«¥,²Zªk)"î^ªZê奿2 û›–Ú°¿†ˆ%ŒèU*ûÑ¥Ä~g¿Š³_ÉÙ¯Ìf©œ"´”¹Ï?di¯–°ÔÆR¯]ÜÁòCÃRO\!y­Î"êº+lD­#bµFôZUÔ ® Qk¹¨µ\Ôj.jµ´Ô‰‡3KT`µTðp«¥VÔ âÒÕRU5F[& û'ÔØ°?‰ˆlDoTÙZÃÛ2œýFÎ~g¿.›¥îš„–úî-ÿJ´WKmŠÚXªˆ”;Ád©Q†ª›³ˆú»Dù%Ê÷DÙ¢ŠÚÎe µ…‹ÚÌEm––ú4À,µq„ÕRŸ¬–z8Šˆ÷FUKÝ5Ú=YØFmØ?ŸˆmDߤ²Œòvgg#gc6K@KEûü—ôj©w–ØXjÌänŸ%ª¥.1ÚHYDM.¶õ­ÅHìÕÅFIõñżÄEmå¢næ¢n––ŠÆ,õýp«¥–fµÎ?fí‚Eª¥n_d´‘²°Õ"ö/&b³è*û',âm$Î~g¿³ß–ÍRoJsßEK¥«{µÔ?ªm,õ{Rî‹UKÍ3TÝ™EÔí,u8oDß­Š:Œ[j7u7µ“‹Ú)-µ*fµÔÐC3ê©jª§ªMõTµÑžÊVOUÛ°¿†ˆ%ŒèºÊ~”ÏMÐ9û:g¿‹³ß•ÍRKAKÕBõTï–`g©JG—-R-u¾‘ªöduš]¢ô1Ÿ½[µ?O”Ý\Ôn.ê.êi©'&2KÝ=Ä*òcQäÇ'¢È.TYàÓ_>½Yxïr1ýå­Ë T“Ó_ÄÜAl9ä€<,rA.&ÈK.·|y!B>¿P@>¹7¼$dæ4@€< ŸöZ!%ÈÃòáËl  )ÿ-¥üŒKé•^Ò+!?›À gd@¦& är‘bÿqB¾u™€|å2Þæ’ d„¼ ßñX!&È?ä^vŠ=˜;F*¶€+¶§Y€�Y s2 /&ȹYd§Øw äë ä‹ xÓLBúH¿„{ö²BŽ%Èq¹y¾ äy²„œÎ!2 @$ä»ãäò ÈÆÓÚ•ñ™¿À²lÙ*mù·eO“�r=@È€ÜH[ò:;Èg.EÈÇ/_J=MøÈ(@®l…\N×dž-Ÿ'Èmr ‡ KȰ–3�²0ò‚<™ _°³åóòÎùòæùY.!ËÈr Ù · ²BædAžfyIy®”ò .eOóý�ò̓ä±oLëÌFÈ÷/µÜU…;«d[AVJÈJ²RB6ä+­7äM9ßòY‚Ü*!ÿÈ!«$d•Y%!C�Y‘yAžM›çÙ@&æ!$lEWÍ#Èj Y­@VKÈ}òÓVÈq9™ §ÙAv“”{¤”Ÿs)k%d­Y+!ÿÍ ¢äâ È·F"dÒ'UÙ@@Ég”L>Ãyò©“u d„¼ Óý­üyA~0×òKòåKäöKxPB6*ò4€\ùåhÊ—¹À²ûœTìV®Øf Ù¬@6ù ÷É€Ì#)Ý9éÈ$åERÊY\Ê Ù¢@¶Hȧb÷»­š|ŸƒòY;)Çä>ÒË!7JÈ äF Y E‹"߇ ×\lyÞ\„<k®€,›Ë›ˆr“¹IB�myVÈÃÉ–£ ÒgyÌÅYt±€1An–›ÈÍF}y ƒ,Ë€üûùr¦b»IÊ=RÊϹ”­²Ul•7�ä»ý¬í+ ò#;)!)¿“R~Á¥l“m d›„œ —d@N#)%Èãì Oº!§_$ §\Ä‘²Cì_�ý.+äÍ£2u�B¾z‘ äùW ù:‡ì” d§„¼ Wf@>p�Bn"Èß]hùA>,!ïá»%änr·„œ{e@úIÊ™ùÙ6ý Ò!!»çd—„ìR »$¤ o˵B!)‡äñvŠýò„vò½ x3SBê ¤.!_,dþ Ès¨&y®!;çØ@.™ƒóçÈ ¹”{$är„¼ ϱBFŠ­%È“bùb–j‚¬’çsÈn Ù­@vKÈÃ�rzä·$傼ÊòÓ ©}y¡€|ëB޾̑íËïò­q ò/N+ä•Tà=?!Ÿ;ßòÒóòÂóäïÎçíK éV ÝòJ€<7²Õ$—d½”éJ„ü¡R@~YÉÛ—Ò«@z%äþ�™tX!!H/Aޱ“²ž¤\&¥\Â¥, d„Ü1–A.Í€ óúr,B^kYB™dŠÌ$žI|Ò§@ú$d@æd@@gäŽÙ6:!ï?O@n8·/%¤_ôKÈÁ�I'ð¨N‚üÏ„ôŸg¹„;_*öB®Ø€„ (x_ÓŸA>ʈ¦|4³t냲¾ý œŸ?–¥ öqå 0%²çý„°KŸµË™V]tÝÃê �¿‡ÕA ø=¬j™M÷°:hí¹t«ƒÚø{Xô3«ƒZx\Xt.¿‡ÕAåçrŸVC6†pðÕAãùW¾:è6þØBƒfsŠŸáöâëÎâ_ùR¢ƒx\¾”ÈÏ·Ñã"þÈ—½1‹×u—“âK‰~æ_»è±‰ÇåK‰¶r ¾”(?ò¥Da—/%úŠåK‰œüÑKK8n=¾Å¿úèñSþHkw\‡òÇ�=®çq‹é1È¿éÑÁÙÓãGük9=^ÍãVÐc”?Vr\W ßá‰ežòj|KS•ó15èBÄ3Ä�[‚nYJ˜[V8Q«9N!gL[´`æò(}Évø,f.ÿnšSÓõç4-ù½fÛˆKªü´¤Ê³pcÝþ%AšÄ_úB +Ö¢‡Ñvrg¾Å÷þӡt¸LsAN§æ¡éìªÔC4±ùr8­;6í%(½£å°gÁÓLî)®ÇÙ›èþáÄà‡ÙM:îÓqbEø'VD¹ôáv×ïÏuŠÛ‹Ä-k— åAŒÐ,v§ÿ N‹fꡨÇP‹ŸesÈÄêúŒž÷ïã+Ä~â°7¿Ýþ~|ÿ9ˆŽëÔt\§Ö°Ós½ 8™#wOäv(k?bµÕZt¶œo )&ƒàtã@"˜;…Ý/9–WðfƒÕxz{´ÞᲉӫ ®†¿Ì™ó¤þ(€jµèH•uˆU«„áªlÂU™ÂEàÝŒ°{Úx*×k”§ ýñ¤ÏfwÉO~¥½ØI¯;åB¹?¢rd¦qþSº¹ Þg™Ñžå¼‘n^ à¤èxH šH kù¬ÕøŠ› ;Ltb’!ŸRˆ:‹T DÒk^£¥/øâS¿ÌÂ/TH? E³~×SšQ0'ÄìÐ6VÌêg:D}èÐx¹/§dà\~!Æ¡Š˜.­’P꤂š7®¸,‹o¬JÄ;Š[Š’b‡BQLYlõ|6¢~ÑÝœh§$Ú©íälV)S!0k°$ƒ+±d*ÞþaŽ~ì6(Ú-´‰ã¬ò`¬Í G;Aˆ‘!ÊQH¸¬¨¾õ)X#è°¼­Ç·%ni¸ö`•=x_àÁ©o{R“o€ifîi’%â]‚ª™xI¥‚P-¼œ3`I¾u&mÒ Õ°¼fƒÛE¸µrþ>”2åTÊ”ƒ®X Sîié€=,éU9ª¯Ó¤û¤Dn[8ž» ƒÂUª2±ì¬AHQžúg4NÙMU5=‘[ϽÔ‰‡\UŦ6æ{V™ò +'köGoƒåžzb‡Õ¨ÕŠªJ©(YË'”}UaÛ·Ó¤SU—@¸Y6X‰ìH¾ò+sV‹'²)I|'?@Κ±<ƒN1Pxt¢5Ž˜7nÍs “— NUf~ÂÓV[£ï/ Z’MEF²1ë® ,«¢—ËsùŠY‰|&”Èa}ýt,1¡t+M4ŸÒ†Åà'g¦Ót°™+¹¿C1b"6õ$ºÄ¾>Õ–ô—“Òq…ôüΪŠ'¡îÔ iš\TR)ùCY» ùàçgWè+“PÐ?‹FÅ‚Ý`Mùª˜ g0äw|©¦X½-N°3è’;цêKžt ¼±æÛk2Dk?ÿ5é=®–U¨#”zxPV4PMûÍcXÓ?{LÑüÊ VìÃþá3YZ ¹„¯!o„¹Î¸†Üy§¡ÜÅ•GH±X¢7Ð2úkóâPí ’?~ [˜,ÅB†)^½å› ‡ø)`D.V ëÍÿ2@¾I„s¡Dïa5ù¦õêjò†fƒZ)s¥flïÊ…âM¨Ä<Ù`|,IœÜåSL”aýqÙ2¯Þø›hþt³ùÈ’ö èþ‰ît�gs‘àÌ ù<±ýù!_\ßÁ VVj5ÎÔ릷að2=Oĺ÷ª†Ÿà .æy2ž…Þh¦‘ÈmŒ7Àþ ü|WºyþîYu" ÌÁwá¦ÁÑ`:¨_Ùø{ÊÙžî¢#ÄÀñ|›eú–Ùà 5@!‡¢8a&Ǻ ¦ÓÝÒÄycIjŠs HÖD_f¸áX±pÂAtdh°u× `ÇåŒï°¡+v/ÝiÃÙ~@´Öì8Rù¶¯“§¹‘{«lù³a_€ÝŠV#ñ‚d¡›[†+:Þ°É$¼È úg§Óñ(bÏÀÿãk†1`*J Wì†Ó:Ç`±o=[×Q Â🋺Ý=œt‰7lþŸkú”µ¶š&"ëˆÈéÕì)B$ËÉ2F’…eþÀóX —%úGX¼2äaÝf!ÑœA¬…ðq?” 3¶b<žÄD„LòÆyétòî~¶æº¶ÒÎ\5ýLæJÌôñRõŸ»Ë†Ðk³ÒéÒÄl|Rbø»âýÌYv�Ãû°Ž^�#´@à{Â`ï¿=G@d«Ë$k$á:» ª,âèø»í8ºÆeÑß° û¹'ɨböZXµ®U˜¨ü:;’‡YIþÇçØvÚ¾öjLÎmXà?Ó†Uá8J³äçb³ƒˆyÅcjiâ¹=äŽ4 ê0"Fš˜Öű“„0Ç.ÏÀ¬Ùyü/†.›tîý›nýÉ'ó8Û²|hãåäܞMÌ5Œïa3‰: Ïüf¶7•]’Lh5‰tñ2Ù¤ÍÅý5 ËtI$Ýü‡£CÃçÃN ës~#g^[Îô^9딜éVÎt[Î~qþFÎܶœí镳ÉÙ+g{l9›ó[9+°å¬»WÎvKκ­œuÛrôü&Î|¶œi®Þ8ë’œAXgšË޳Ã~+g~[Îܽr¦KÎÜVÎܶœ­Ó~#g[μ½r¶GræµræµålÌ÷Іõq¬¶rHÍþ‰ÜÂVpޱren<«‚ãMà'¯7¼W4öªµµ·‡šúÞÕ&H¥N0èäÖLés3ép*ž­uà·³f˜:v—Üï`:(c?H¸A¤5Ñp‹=[c<òœt~€E7t #„{4"7H‚Þ D¼Õw-á›$•Ò ˜kåvjÛ‚€ÓTÔ§Þ/GÅÙËH|6+ÀCA(½S#=[]!89:$ÿü} šÿ¬ 85ÊZu#YA¿Ô-Ëü«éÖç¢|"]ïÊxÃHHø?:Çxó üÏœc\1ß1çøì#Ð9þ‡pŽaK"MÇ ;\ _Ç›¡ºZä²?l v�ÏÇÁ‡1¹5P´ ×_…á¯< k^ µîOT‘qªýÞ ‹@aJ²É·5~X‹)ÜÓ'XÃý1“ØQ‘ØNƒË*K”Ǭø©Š’-»ð,ðª°E¢Ô„ì”oa¡lnI‚oÓk+%Jsóȇ&‡ÿѹt“-¤›\Ïõ›5ñyЊséfhôXãPfòe°Ûþ@£@aÎש¸ï’µ¸cŒ¦*M^G=Ÿ%ñÔÉ–Æ\ôhá!í‰ä/—²BMrs_[u4!øôôðÎäØ)Ï’>ä˜%ЙTäÓáK¶ù‹^Ó‡Þ iû¾�{œ½£yÜ󬨡iå캅ýv°ûµ/hÚ¡/jZû]ø’¦ýÌ~7·kZ€åÖùïætýh»¦Ïî[žcÙ9Sw±ß”W4í<ö{ˆýþÎ~E¯öíçæ×Ÿ_Ö´k_¡+üg¿>ÀxOlYa…=K=g±Âè¨bFú¨§oÃý¦"tÖbeê�¶I `•ÜÀ‹ocuû™¬ˆ9;î w�Žš÷F2Uü�c€ùóà‚G—ˆ™^ÿMãp(œ©ËÒ>Œ`V*-¬ölåcUê± >baœf$ú¯øpV@ŠË¤pí‡{Âä?|‰[©ˆÄ^Þµ-äÅÃÇV™ØÍƒïU©ßrnâ”yÖQòø^E„zpy]P6„1ï:…Ç{enZ~r¸~àÝÜ@¸aKt�‡=N¥ hÜ…ï•btHV1À©Ô™ö§ChûµRÜõ§)ÿO>n|=7(:!sÅN`¿éüß'¡°Ð¯½Kãƒ<¸ƒÙÊçë8KƆNÍ»ûa= ðçÞÉrÉÅ—Çâz-¦XÆ€šìãe‘x‹°K1u– ¨_+¼´µQB3z|a­S¦Æ^}ÆxTJ ”oLõáhÁοÍPŠu+ª,ú¨ ‹tƒó$H–:‘<‹kƸ9f¯bc/\ó‹fÅ-† ÷BJ¥­¦ˆø| \D¦Qò2H$Æþ^šSÓŸßȪÜñÒ*ë¹ 3:ѳ—å£ôᦈ·BÄ8D|W3"ò½ånj4âwp©Â" cóïm?é,z¼¾å.â€8p#Q¯fÕ’2! X€¹œ²„FåL­îx�vƒì°ËÒm*î$˜`™]W¤'ŠŒµޝù¶cÅ<•ÃÈzì…ª¾?JÖ¥sîŒ ø|Š’‚Æ ³“Ü µüI~ÅÙ÷÷«R%-—{• åLÓÛ0׃­wŒ ¹¢Ëú¦|˜ (ÕúSÕ„œ‡'4;³Œ?Óf®S`¾<}ï°ËÀóY~ {1ŸÜÈË8W/Á§·À?c¯X/¢R¹0–eÒU#±\Ø<Ò)¿ëoEÃï˜É(ƒG^cô›™™ ¤ïn׌¹=,,\©´øx‚/Bç Ý]e|‰Â·4j¬øÔ(åÅæðÍ\\±AmO$G~8®[z»•­ H´±¶1ò;6#D%F×û[?´´(¤zò6kÄjqßÄm· 6žÉ[%X‡1ë ŽYo4 4¦}°‡—½r4µ4LAÀ¿çÃÏY¾»ÃqK0“Ñ£…QÝÉ4ª;ô0…]=M·å˜4é}rfJ¨âÃò»¹íw+ÉMŸ|ôKâÈèd…™êÒøÈÆŒ—0-µØZ%giU£VAÏ&Ï;>F2‰Oà› ¥kŽ„½Ÿ'³|ð—}ds‰ë±=˜ÜÚƒçâž«8(XœÅËœ3[(3ÞÂDsXX«ƒÏ¥ž­¹î žªN§‡E– %/#üe4$Ð@9|`]ØñdìjÈÝe§Ê!ÑÃí^îg÷z˜§!m§¸¢i^JÿØ¥~|鸴ú·“VßY‡Zh£Uè‘îE«öⳜ°ß­ÍFfµ›˜þª<»Ú3^ƒaΚ²2ñÒ-¶L«Ûs½Fi¾F©¤ÒaÎhp͘¬@‡p däg¨i¨„`¥ù�¦~¬j¦+1ªõQ”·ã‹íÇÓ#@•þóͦ�8GE¡;_f€Ú›s ehÎzi¼¢VÜÇjk‡Àô/ù4,º—ò4¼fTµ·K)uÑKËǪi˜º6RêÚˆ©kSžÆÙA{óy´ÉE9hpc¸ÜDëÜrÂA X¤‚Êj_+Ôö’ÔÆå(ŠŠm/0¥ 1?�ˆ 7 ýÉ18/`³FShNlþßG(•k¿ÉÜ?ð4<Ï'¦‹Ýçt^[qeåÒä3[µ7©uX;Œóáë½öùvÌ݆iBp˧Ê&èÓ§ó|¯Ï*œh:G̨üÚXì‰Úô&z¢”2Y$Ÿ:Z¬¿*ؤØ(3”_ 56k¨ êg-[¨j%Ô;YCU(¡6g Õ¨„º.k¨F¨Ô%öAZ• ¥öA:• cìƒx ˜¦J �0R`±©°)ÌÍ’z»èSFÛ¥¿ùÕôwí$žþjN^Z8ö¼’¸5I™÷”~â0^=6ÒH§©#%ð’\ÆÒèÞ‘o¡†åçš�x—ðˆ3-‹^Õ¿¹Aƒ)&àøîðŒÖïiÚ_ÙoÞûš¶šýNàWøýÂÞSž³ý¶¼gÜ÷žùÛÅïeöíÀ_fS#ÈaûFµñé|ã©BäF(LYÝ6#vu•=Ûöø Ü`7É\,ß’©©‚(§W°4 —Ã¥¶ ˆ*=r£X©«�VkP,Зj½Ð‡U©>L¼ÐûÃ]ŸÎ#_ùÅ&¥T²mbÏ–&vИ—Þ¨)óÒáA£¢n¡¬Å¶£r5ÿˆ!WóhP¤Ã}#Sõ4Ìv`›( aÅ6¸Ð‚wb_¹/µÂ‚n<YÀS®å‘–¡»¨ìO "ÐÔf½}*õ}UÑì½Á|Ò”FÌPâ3¥` . ¹ŒB®UC®å!A˜µ\µ½Y‰îF}B›p¨u…ªÌb(šÚ?ëw|gD¢Zt¬þg´5¶þ¡(‰Â¾qCš>Žq™ü”çPœ…ªô6Tñ¶º1Û¶ûb_}¦Aþx#ü2ËÄ©¥E;õßêv,ÀË“ÕdD”HbÏü€~%$Ë(y-TÁå×è ~—ŽöO z ·)\¶ý °u=tB{܃9û|í÷3·¿‡ ÐÚšÀÖ>„O` ±Mk€ƒ- ya™XG„Mð^tÔ@?»ˆ=53mšèª‰èýšUu>æaê¼úzxÕ@ú’5##ôJé®ižôræY'‡á*m>á§n#ËS_š')%‚¹¬FItô2­ìžjëTµ2o¸‰YÎðâS››Pú•Û©ÅÍÂÆÃ^Ž&¦¶aôŸÖa%u&–X "–Yžú]N<Wó}ÿD¨�V ¦§„ <±‡q€½@Îs VÃÀJ¨*ª‚A5öÿòªažUxPΜPó`÷KML4c”sJŒŒ¡H7Ž¥p+ž^Åûã˼§&í¢Ñö@«é¿;†f¦ !8˜”CÓS`Sþ í•sT(æ‡dùº#3&¦yñ„¬‘o´‰¬ÄÜ=>k̳qð½€ÏƒÑ°L&vÊYRDnJv)Ü#ò=K<>Óð4ÌýršBÁd»ÊDÉGÌÿ¦!wDÝ11+*”ä= a‘à‰¬¤fؤ—ù­Açz.×<ç0ú!ÿמ!BæN~à‹›fÅ¿à =ðŽ|Í~¶ƒô¸läl‘‡pnŸÌð8ÇαtŠékÖ¡ö¯˜†ÚiÚEdn#¤æHBÌÉâÅ\ô[p‹£,ãe“/—Mžz>Ö¢¼‡¼3c6b9ŸdQ§—º  ª)ÐG'ÔbÎ5skãvÅÜ>™Å\™[_�U×`§,ãÁÚxhA"´�³ÿ‚Øò,ûÿ 1¡,ûÙ¿Rd£äðãlαr‚𑾋ø�pÈ )—boSˆä ô­Pçî,襉é ?Ž{ÑDDwfôãL§“…¶”»²R††½[‚–M°ÝÅ®p<WFбAï×ìÐA²¶¾]{¸%vj~f ¿Q7fù3œ‚Trpy3öa,­¤Æek·‚ «âJ¸³z©E–a®AbSsjF÷P~0¸ÔŸ“O¢ùpmYb(»ŽÀ5òD¼<—G(Iß„Óßã¡p{¨X°‚KòBÅ*+ÅKf%f'ÊŠ%7Ø]EÚ'·Ó³u¸[pòëÑàˆÂùf"Ó±l]^š�²3Ã2VÃl&S °,ë(7+^µ8CÁÿº¨È^T¨…Àq^ì»J-ŠóX!pCÃoòu.bMÔx–›èë ¯‘›x73(`+ŽÀ£¸ªaÂê•àÖÝÜÍš½cnó…ŸiÚíì7ìsM»ý.ÕûöÛÍÂŽaW‹û·Ïè ¿Gÿ©iï²_ ¥ióÙï>þþ̯u,® ¦°kÅ?íæØŒWá`4òïÈå#Pü¼.~ÈŒ§¾ˆÆñp÷[ÂóUý@%ì]’Kû]äÒà^1Åá‡Ê£a<õoiJ4r¿Ð<t˜ú<õ÷k&•ò  ðh´ŸF©8Ö%ºDDQ‡;«º§&T[Æ*q„²Ö< ZŸ¬æ-…j%±×äá&¨)¿%@–2f7xùª8/IíJhÊ×¹öƒ´ÍæW£qg‚ähXŠ<Ý}�wê1I£I“¿c´ˆ,¶›ÜBñÿ`Äòc,8ÓeÝh§1bk: ¶6¸‚1ÂÏH½�rì‹`4óOàà¢yØî Œk©iZ…‘4ÏfT˜Ê?9\£¿V�ø—vWÊV:'Á|¤‘ƒk.OèþÄŠÑ™–7Îèñ–7¹pfŸé;:š/"Æ×8ÄÅgX´¹UPãÊè¬P=3œ†.Ní£,Çæ³ˆP1?ïc9ÚëʃVèþ¤3w­#wHÜb±ôId ÷“Ä{Ñó,~pDÄ«3z(^s£R„èþxâ“[ã­r¾*g”Oà¬äœ5#e¦ôó…÷ýó¤~Õ¨ èöÄþ®Qg 'kP€¼ U1ݧΗ°Û³êA5ûËß?½¬Òo¥—Åùñ<ëæSƒÜ©eâ­g«ñò#èsÆÛÓdÐ'Œ—SeÐ9ÆËƒÄË„ñn˜ HºÁõ9{Û¨à/× ~aN?æ,V-1‹}ØMs¶£å’?þ‚ÓÊÏß±ìM›Ë~°ßì·‹ýžf¿nþûô£Ü–YÁ‡YÁ™ø"~^ç1(/S°w/Ϫ¿q³†öàÐ@Tð‚ïó8áøLnç† ´GÂa–¼Ó›…ìT³—øî"â õ£Óø­”Ô9êÇ\ããø8Mýè6>lÊ'ž üD¸€iÕÍ9-Hík ?H< PøEƒP È—¹…©½Y 2a.&G·0š;6µP[¥w˜bÝí@µãJ X^œh°tXƒµ„‚â¬A<ö½?üƒà±yœ‡ÖhsËpÉ…iÚ€Ëæ°8õÏ|‹v–nþNÓà ¿FåÞúû_¿æ÷=öO-zd©Š¥«ñÖt%zê]ÓÞwU)¼Q1“”ú°6Ñ-“W‘iž†# Êž†'”H<!yêïÎxéôÔ7e¼Ì.3¿pÃ`¢ñ¦Å´a–] ñÈU8Û¸¡pF#wÐyæ“~#MÂ÷-+×BC2‚ÓV™yS…gJ‹-qżÆÖØò GÔ™:F®ÔüòòiE°4)oÁú3Ý,'î÷c:­^Ê/ëd•ybÌ[ë/5‚De•àex|¿¸öüÙÙÏãD+Š^F*Í´"Á»B¿p+е®8K-Ü‚Ón‹ ¿ÙGý!¨ÊÂÐíF:eµû‘N«2×?ž+u ñ«™NÁm‡ÙàÕú«P§Õ ÓjH24IŸ%¦™Å¿}የE ŸË„OçÞ†dXÊ -Æw<T & Ñ´ÁP-»e¿n¦ZÆM¾T[¡ÏÇæB;CC陂_Dܶ3ëØ.¿@ñKÙdPfñ$ß Ê]š³Ë9¼âD|D¯äõ‹#z²úèŒNUs£‡˜ÈÀ&5šªç¥šˆnyÙå•“Ù°¤« ’NPÀ –r¯@Ä-€rÏSßÅËL¯ÉõðZ]/Å…ìÆ„›!ž„Ãq†òÙ )OÎèQÊS.ì)`Šî‡^ªpDÜŠœ©Q2 NTBÄæ"œ¦ê)õ®—×»Þd·Mx!p~Ÿçÿöno,Á:¹§ŽÕæjK'ã--oŽS4?óeÎõÉù¢>M3È8–©Ä`^§3Âæ-*t B…*!§™×–W%»!¿J(ÇL¨À–PJh¢[š„˜Î Áý¸} #Í~yB ’åš\›R ê•N=(¹_ÒJèt<…¶gé8^âäïú…ùM^«L4ž€¯½3øÍmjÂ)Ê_ï+ˆï—æ7#?Jåᦣ©‰†¥Y Ÿ|ª`OùÄò]‰«k#qEä|ð>&'‘°m»".Îeä‡<[ÀnX˜ä9p%‰IÊã%¹&i�‹yCÂŒA^˜h5ûð€3¼ú 'æð–êU"œ[6h‚Ôš)ÄÆK!5^ ÍÞ.c–yN~é.:@~X+2É„Pjûâ%4¡~ÿï¬y³/; ¢ƒˆåX­W«€ÉSB`Ú¬h'>͇݆”6,¶7æ<õ;5ñÈEÚOÇê<˜%Eñ>æ¾dç²Äî–DEœ·ú)È|q·J㘎þ!è}§¼êgÙÞð<Áîæò”ïÖñ%ýèašù öÄ\ÅíOËå-{sÁù¨}¶k*®þÉ”úLóK L>™}Äý0nÜèa4«»ip lxiø_°€ŒšÓÁs¹PžE^3?^ä'7“ÅvUá(ßà§…§0°Å#ÅSbPa8‡™ Þ.éoSûÐ7H—ì 52ÊäL=ÃXñfÖYœ••_Ðî.Œ›`¦ÓGçõMûÆ9júëµý±Ûtß„ë¼!N-Öåä*öð˜¬ÁÛ4øDÝ-çx‰ƒ= ·³—á•tú¼ò¬ú7X¡7{/‘'>ú0ŸËYIä–wgý(­°]Ϧ14|<[;®óÃoê|îðäXWôƒ 0ô‡]±4g»ˆ´«+ÑûˆÂ2ù&ÚÒN,Õ Œ4eû0™ö²« Í;Lèo2"?ìJ¸>äÔÆm»Þe×ÔáÌgN²^¼?_|BÕWzdÌ-)HõKço]í€%æ1C¯ôNhö9y»ŗ6¾(éL's\^v(à™n‘ Û¨”J:óX)ÃõØ®“97~ϵ‹r±º˜Ìʪ£sq­ ¨烖0iØã6šðÒñæG»>RÝÕõÃîÄË;Jmã@‰Qö-úÃßXŒ(>ײ»Z*é—±ÛeÛõA±./ßLŽ÷‰²§zã ÌÆ¢ÑxW¬Ë-¢¬1! ³[oq±®Üÿ.î.¥ëÿ>ªã¿ˆa?®´°š}ªæ)òFŠS( VÞ}Ürî4쀆é²Ú”.O@ér:»BƒÓuŸB‘â #cõ¤ÎÕœ.y(˜@ó¯ÃZMÉpïË­*j‹u;¢§á4ß’"„) Wð$\-“p…š„«e®À$œ¼ÉAµ¢?ƒ÷ à‰œóJxS…áÈ«¡ú¯Šzx©h®BÇÇé¯ì.+޶öÔ¦u.'ôW6›ûMAéBÓVùìUnœ)ý…q2žÞLi�BK#$\ín0+5}™ßbß2óÜî©I |¢ézrO)ñ.!¸ÕŒ3Gâøn²àfó»x'¤Û¬„k1 PRÀUæ è¤T—ŸßW% ž+016mOtÑ£nͦÁïÃHZòsEêJÂɘ©³E‰_·˜ p—Ô’ÌâÃiëÍ„€p`¢•ðˆw-ÌeÛª2õ$\ðzÂv9ÝšÕ_yÊNÀ|öµÏs͵šŒÄ¿Aúr tÂupž²_°œ)íú©Mh¥™ÜÅôDH…’÷ƒAZ1–Ô-v¶ »SKÜÝý”ýˆÛ]7ô“ûÆÞ.nÕNj!!w ä³qÃ9¸‡Ël2š*d¹[>J㾢ɮ[×È~Bµ±; -¹\ý¸j]ÿq¡’¾u)Û&'\¿¸”m“…f»¤fß§Hu)»)‹Î&®J] dQV—"i—Êwj„H-g3ºúG|üΗp=àæn sú›o`Oz=/á|襮|¼»~úÏiEܵíñlÂò9¸``W‰‹/rh©÷©CÖ®û€Ó§††w•;#T|µ:ûj> |lÚñ¹Nð±þB)d:{‚lÅnƒÌ¥jwMcwyø‰[Áþ˜žü䃱iÝ9àæ%4îú'{lwEó(­%\N=îº2χaˆ×Ë&ó›æ8´ÒĹ…S#ñQO²ˆ¼ÄØš{’3EL'ÿ!Æ¡ùàºnÇmƒ7ɾ¬2¾,“_¢¬d»?æ€QÍDw8ñj¸)ÿ‡*‡¦¹„:áV¾�ê8_ŽþõÊ/‚©-·Úi5íN * ñn vÅmûô5¿ÒŒQV’ˆU‚#Îêu`hœûá¤J¦ô/ž¨nG ôiš2Å1‘âä!ƒ4Ôƒ§þáB"zBœß[ë·žM?hãÇÄé~IÿñohÙˆù´zð'ņ¨|p»a/.fÁ®�iOý·  LÌí›Ï£õ$}ûÐÛŠ=i,cC_žÌ¿«åéÃøÜq0÷ÕµËI¢ÔÁÌ7øE·ä»ÀŠä¶ ~@¼Åʃ¬ žñ Æ­ÀrÑU‡ÁX›ò‡Îð¸Žð„y9å¼–/·Roqú÷èã,9XQ$f/ƒ5ù›ÀGH±OÓûkf ®÷.¸(’È3p#*I ~GsŠ26•ºg°×Ïi™Z°ð~„‰÷0o͇E³ŽË´È^ÚÓ0×A†2›Ž‚{3uO &-dÊ»§wy÷X MëoJ38áGlÙÝ;án ánIxšØu %ÇàbÒ]¥YºÊ%‡âTz‘®«ÒÑár(_¦•TCÁ¨(êh´*45ZäÁD#dõØÎ\ñ"Ž/гõÔÿ…ç,P¾}Z¹õ=«@s˜U@ç€ â8—cðª_XUƒ‰Áµ¿ZUQ+“SDíÒ©´@¾‹ïœŒÕ춬Á\õѦÙ'@WÊû¡¤^©<Zf›Pܽ ã¶C§P‰ðŒñáÏ¿˜…à ŤôÆû ë&rº©l&·UÜÛ¾ç.<Ëré[èñMq|§PlO¬‰§Âfõ+¹†mÍêí]^‹&¼Ü¬a€ªK÷•m 2_¨äèúþýÔr&ŽGŠäp‰öSo,ÏQƒð׉º x=e%|]º¿ÍwŒ_Y‘£¼…úÆÓp¦S%ˆµ>5”+«rÌXƒoeuõ„¡ÕWÎO_Mµ 4~;.ŸO’,*°›ªrL,U›i:»a9|Nö9Íu¦ñj3=ü:¥t@w4?¿X0aÌÀðHªH‰iÇI{© 0<a†ÛħpÈØ®w~ÂeîŲ>>‹ˆ¿dï#SÈi¨¹(3ù{OE~n5?µÂxª@dBßS‚ã­cÉ$¹Þ\'u3r?K™X´G´H3 ôÎH@8ðErÚ"Œöm@pŒ;9 o?Â6o—8ýònLÞÒ„Óß�;:Y6>iÁ!ÞòŠÏa|Uš¹ÛetOýKš,—ŸÒDª”½°2rk5w{hß^žX= ×™tg°1¥Ä¿x‘Ñ`î/¿åP ÃO û—DŒ@3Ü2‹RS:ìŽð 0O4’ƒ|ÙxqЦ™²)p ð†ØdÈžµ°—Š;‹&_T4¹ÁÐäM½i²f‰ÐbÍ<Usq£‰+¦ùøøù=¨‰ŠËˆl«‰&-ã^o©‘°v? Ø0‚ŒjRžW¸äžX›ÅÀÂq›Ða”¥a¯)²è 0ÞùÕ†¾ðð•<'ã·FéäèHŽ·`;¹Ð²ŽP±jGÞ³F,o‰M-Ñ> e”`~I« …¡áùóL‡©¯d·ø>ýe÷ ŽSÆ,ú>zÞ®W¿£*ÿ(Vå·}gõ[|Ô)Üsmïšñég;”ZÐÏjÁÃe%yšG@SIÃXjíÏ{¶³æ™K.Ù/Oóù„6~×}¼O_ÎQ$71HoxÇ! _A½Úë.û샩=è¨ÿêK¯Æ“~¥ÈÊþ%{I¯Ôæ#••JÕîãÝj¼^—sQÍ5zé‹©.ç]ü|x­ÜÜeð›Ò‹×H/¯žñߥ›¡<_3 ‹3Lì6L\œÍÄ4(O#cƒ ‰™²X¼¢?'mˇWüò)(†^‚ÂîÅ`fS]‰ü MSûf©(qšˆ{>,Ç'‹e_‚¹^úý…@fÇO†Ûsq&Fª?L»iƒi7±©¬†˜Ì± ÓTÚ”3W„vJ LO^›A@ûñ×>ôœˆòP±žÙÅ)4Ê9;ÖËÝzü*¹œíÒ7®´#0‘¯ì¼Øf#ÀÉWäýrˆÕÏI�BVÉêV&%ãeÀHNü¥2áe·¬ çלeê1ÈÁÄ‘­ê)¥ÝD•/ÓjØ”:“CÅ™ÙÑG@ó ·HeÌÍ»bÌ@>ôQø dA7)‡¹ÜkH瀑Šu9ðeÈ–\óÛŸ•©ízs|žØç†_AâV#çq£áÁ© Ì«‰Êš Ð6ÆxC6ÔÝã^–mtwòrïhÄÌp‚Y®�2ÑaV«rqYÖàU™Á«hж’‡PΪâE7ËYýÒùŽ…-¹2GˆIÍtü/Írh¨ÓÛ)%ÑËD�¯q ½(P‡ó F(ÐâñjÛ•Ôu¢‘I|Í»œñÕ-iÇÚüêÐV‹á¾5r®ZM‘îëKpOýÍ*;d#‡W˜ 2V á³ Ë;D³8+jj½U+`ñ”ŸÚÇ„_p™Cö…0&?⩾6.6Ì^ßQî$@ÓøÍ·¨­‰¨ƒj¤m‡¡§‰D/Xl›HùJ* ç¦fY9Æ'N¸IÃþ‰WÒľÔp›·~³~C5ÆO6){W®¡Í”RGD"áù2['X)m–iPPáCˆ>u¨‘³�ö>ïIÛŸ±ßQ&BkB+V±Yb ®WϧÝg+TÿÎ/ßUÏÅeìÃ_yŽ%'/˜QÕ‚0È'‹²Íí~·i8F —ÌòEiPtðd-ÞX#Á2ÑHñÁœž‘ÈŒrp RÂÖ7ƃ†JŸ1}¡ò¦“ç‘ð¥£]ë¯1:Èç¥pWW´cÈw2vÿa!’5¹2¼𠥏e—²˜TãUúÝ“'�Ÿ+éS:ÍÜ]ñu4|wAódÞo|ôœn×½4Cägúð3)ˆy³ËÅË£äòžØ\Åcâ N ^÷D)È)9ÿkJ´ÝÐö¤§u/v Dä„àÂ’P'¿µå12¤Õ7u\±ú!KŒÏ‡1ŽN$uÕøJÙÖÅsþnâ ‚a7U{ÏÓ•Ï>Í ˜™.ƒ¹˜0—sR6 3œ;P-û�»KE Î1„Õ% ¢Õ–a-ªÚ,ï¼É<§‘*Ȉpn:¤ò‹…¿¸?çs—kë—èh=NÓDo1÷‹ÉJ\½]FŸ®èåæÞÍ10í±þ¯\d{ò82…è�/íðÉÓ{œÎ.Iü5^¾|2„€•Pæ,F‡RÐüh{À(ʳq|6›%›Ë B ŠÈ*A£Æj‚ìÌÊFcEEEFR<ڦɬĊ0›~YÆ­´ÚÚöôëùÙÖ» ¨˜HÂÀ#ˆG𨳬hM•ù=Ïó¾3;›ë×ïÿ·%;óÎ{Ïý<ï(sÈ|Tÿsˆ:Œx¦‘[^4‹xØþ.$8ǺtxXð{݇§bκ»°)cdóîä´{—<Ž‘i|8y¶=IÊÙ¸w¹?Yß»•ßÂê!^a¸¼Ä? s28‹Mçf„l N#ïg£%Ññ³hXó«~é›,SÂX™ð J/‰À·#ø‹=waÚåÊÛœeºý} cÃßÀí}–†”χ‡)EΣ°-Ïæô|òηí&u^eÚR,¶äU¼‰Z¦ŸB"×£j¤ ÙgŽéä‚fï„ñ{2 WÁ3—-·p>ަý7Ÿ3ä;÷Å ½¢¯ð£õ[·¦”“’‰Äá?ÊÑø×¥ù‹Í4ëêÑ0k$²ÂTr˜É«(™é9€´¤:”ÓŒÜñÕ p‡I‘ÇèrÛØvZn³NKsÁ^-]{MÖuŒV"¡ë`_-]¯/¡ë˜häþèN«7r„ š™¶ƒ—­/vmG%_%>È¿®7Ñ]äyš8oÓÀLÂÖY`ƒ)ãô$J)L~Ç"÷Ý7‹p/&"£7ÄSàcÛ¶ê1ìã—&{çÕppý x“Œóœm nâaþkÒv&?ÁD¯TŒ‹dWYé‰jYu]VRÄa=ZßD¾?ÌTÎ9pÆÁ¤ùí4ŸI3J™Ñ³œ­H­É4ß_G ÖFÍ×h£ùÌGÐ|vÑ?…÷šo~£ùž±O"¿k’ÓòìÌæ[¾UICú/œj/˜¦óir[d!àh2p!*°›X€SøÉ¢ñ+Q…Œ"œ˜á¸?™ üˆ}øH0)®^S\IEìT nâ%‰œâ5éÿ¦&Ÿ üÏjbd‘ ´ ꉂœ Ô…È'v$!Èù Ó¸¯q!ØÅwz+«³qBÌFòw¨päfµ‚Ý£ïV“lL‚]öcîÎNÙÓZm[qXZ¿d jyÐæaˆhÄôQASs¿À$Íîµ;0Ó“dYä 岑ƒÜp†‘ƒo€2íJªUH"ÍkŽùP9˜8Ù,#Í:G™É¾ÿíLšäàe ‡“ƒ|Ð9 “äþàwFÚSÝ9èvŒl¾oXóö.¹#Óú¬²Ö䑃+n!rðf$›6"ywûm#m,“¥ÜU&¸€m;Xë9áOf×nôpª¿T³"Ï„²Í«bŸb©&´8BHFéÆ04 ’jĽüù¨â 2·�Øu‰< p…1CƒtŸyÜÈÿi” °ô J�ýh3]™NP‡®N7Az8ÈÁ™Â€Z¶ÐØØBþŒq—I ?²y'¡À`”Ãhn–‡‘µÝË(U+žªfÆgÀóXÈs™$*IâkëÏd¹LdmBÛˆWϬ£À"v$Î0 “Òå±F³X0é¬8ƒÓGD/ãXCYœ*…,q~óÒ.ÖëØÕÜ f¤ù�y ØÒüžƒ¶EÉ0O› ÜîÚ?°WÛÙfnͩ�+†ç §¦r¾i˜Ú–ìcƒ.8–ã þXŽ8-B"ä- gËߞ¼{ôáe¹GN C ¼ ,KÈçVÂ]sþ/•˜>:ÿ×:ÿq›UñÜçkÏAóÉ÷lBP`Šúë'&ª¤{­Ø“Úà”qfu„cˆøkÛÑ&‡n*„0xÌ÷ù6ÄXA—ó+Uj9Éß`Ê:à2KÿܤÜDE\1˜n/`ê͆Ií)«º=ÝÌ•!«eïI(C<v3!Ï.šOtÂ.ˆ6騄ßJBdoú· Ý»ðz›2Äcê)LŠ—ý&4@Bà/W†xL¥Å¿ËžP†°dÖH¶µ”!<‡g´LÂÐãI[eÊû¬ð|¬•5¡ ¹}>S¬f4•!® ©•NLÐU¸”LÒeS†°yº€Õ7RRë°)Cj6eˆ½çØ1¦ Ym-‘¥ Y¼Ä\2,•”!K®sØÃT†Ø[ÂO eˆ¹SV &ÁÄ6žÇ²åáÄ$WUØkê¶ö Y {ì6¼{¤ªèù¶ÃÚÚ#x¢‘-ôŒh Ð ìÂ2{ ÷›)C¾‡-!–óÔßf!P&¨êE8ʤ8a/îš7¢8+†z»lp`d­¦¿Ž ¸R¥ÀbÆYe\¯R`³Ç[½a<v¯`ñ]&]kç±MÉ·ÎOLŸ`qWIlKm2ß“¹–Z]kã±Í«còØì ]}ü¸õU2Û,e÷ÇëKNKòÇë³åûEÒ€ÇnHµ4ù6GnK¾Ì]ëƒmZq[jüd1ØøÕ®fÉa <ö†?É öìÃ%bbf“ŠØl”ír‘0/IÈ•×äù75ùl öV“™Ûl« 'Q3Øø˜P³ŒÂ]»Y•&G3jšÖý\U„Ù8wMKeã®ù»G¬ GnSwFÙ§&w]›à®“øLÆ&ñ™œ'´w„oÂai 5‹%Å;ŒvÎ(Ü5¦·×.Ò.0I®f)HðՔůWðƒo‰µ1‘2íJª’>Y|uIò _m‡µ˜ñÕ%'ŸÃ‚ÿíš|õ{Áá|5qŽ]nï~g|µ=s2¾ÚçÙ|ArZ޽Kì:›ä4>œ<Ûž´ñÕçGªY0ýÔ ùä5mD&zT{ѦOöþúR¶=ìüu/o€ºœ:°×zÁ-TêLhm&‹€Q^|œiñçcd?çtýŒ‘õ‘=Ïæb¼4Ú cVÆ ž,s cª¿IfÆo|ó¼Žo’—GãIf¢ùÜX̧’MÀcgfð›f® ŽÀ"`ZcEq$[ $Ì<gŸ´úJ‹ã­¬ÏHV£ä[·”{2²ÎsëWq…Sº…¨˜‰ëš‡1Ée~$þ€»òså2 ’lÇj‡ÛŽ%·ZËŒµjGkÕš|Àü�œÄ­V—ˆDOl ¬ZâõCçšY<'Ë"†›xM‹Mb¿‘O0ý"±?Åüìí3»G«‘Ïurñ?_CÝ)\í€.ÚÿȌҫ‚Î<g’Mœ®§¢6 ešÔ°_æÎ/%â<P6ú XM#)z~nE/Hцwµª®ÈfþŒ´|N’'~JÒ;Ñï/ÍeƒÁ™ýØ&,c<qÛÀ6<ÓýÁ—P™a_r܈.Ÿ—·ž„Rƒu†héóç:¬­™°œãf£Ôý‹äº9— víu︒ÑéW²q:]ç£+F§˜tºy˜Š8^D§'gÅNÿ›ÃvGÖj³€_`éE ·\<8l£u[||‚¥<Ç‘´Z;ãNY!9gÌMBžmRNÈ'±ºœ1æL~…D{0AÈã÷d‰2pBžö('äÍ gòìK—çÜrFÈ›¥þjC¬­ÉiI‚‡V[¾¿& ‰åëµV‘‚m%°ÝÈ ÛþÑϱ-¢T›÷7²²ÇØÂãh¡FİP5Ý'‡U,^Ì;g)ž¡¹ ÷ŽeCaɺmVg»ƒâC-x £ša|¨+:¶øP-&‘ˆñ¡Î…OúSëé–¥FOÒw±¹‰ªw­ƒÚ¸ ©r¬`1Ì–ÙqxưÔã–¥_ABe¡(–ÇAËi.«¤` òÄÝûo5߆,g‘–õ;H¸„‰¯ÐΟþaÅš`FÄ(îpðˆçÁSìQø{$•ËÖ¸Í?¡ÄŽ©zÇû®ç6°yð‰+®b¡›þºÁœ™ð\%_’`š· ‰iI„©ò˜È²ÒÊnâàJ JE‘‚$E ò@;)(~ã nKñf.v�#Þ`A¼Ý͕ͻ$×]iË7Å–¯’ò}ÜÆòUÖ%âVššº&›ú9 ¦ípŠ9˜„M §Û<bB¸Îüšc¡Ð*sÐ@ ¸îáM7°¦Lr Ï"̦/ǦßãóX5*J׸ŠîG M73ŽDîIۉakÍZ“±E(Ôšu ÿ¬$,¨OÒR|5 ö¡V‡`Yµ²$è…ù¾Šý1§ª^ˆº0WÍ’G›¿3É÷Úñd-'`Î2+IÚEï½ÆvÑ›ð‹×Pºö¿Æb1# ›KÈI‹Nã¤õN¤Á³Y¿q.ÙE›®ŸðújÅë9â³(¿"sxFbnŒ}uýklžH»Ö†´=v¤]k"íÓf;³Ú‚8ªZϪ]\íj!A ¬^í†+-°ï0å÷´ð`;CWsTíúÉzÇî6ëå¢a²6Kú–äÉɤl±1Y²tG2MôÄô1ëÝ,Ã{†¥2ŸsÈLÇ5m=›û"±ùQ>÷üú_uðÈ>ŸâœûW 19ëœ+öª-\Ur§]›^å �j§ëùWæ€ÇUæ£æzöU6ÉXþþSŠwRù$éÀèbS¡×±åç] ¼²"ô“a¬°:XÆ:8“wÐæºêò½j³‘Iî ™q˜‹,Ìí“ÐðWòœ·{Ç®’¡‹,°`¸Ó‘(osKe yvÎ,gÊ©Mž®”• Mn¶Grî+ìHΆ_v[7‰àñ_1Á6«D�â*|ÅaÕZwKrËmºÌG_f0I¯[íä$P²¿k¼Ð­‡œm4,Ì × õòéLú†ˆà{¶œ–<Ö––—»®çì³:ifÖm4¸}¶NŽÃN±×Ìz’ð‡‰št ÈÞ=t™­„ç›”Ã?±wŠ·cÊøLæNÒmõÏÁÔIL�xÒVQäºb›–A } &£ÜLÃÒËú×íHÌC"—¾’@"¦¤ÔÍ…o\¥òÛ CØ|;Ó”o«0i÷FײÝÛ ¿ñ› ·«Ö²ÞbgêæŒ03·H½BÒÒrÒ°TÂ2—•:ìÓÿÛ,¼²ãZñ&ßææèeå‹l;Í3B¯HPOïV¦°MgÖ‘ãHàœ¤‰a8â·%ûN.°LÌ¡þ<«þF^Þˆúó ”7JýW”0tY‰Cø†ÈÚË[cë±&Ž:øœŽß¹ØÅÿTÅ~-¸ø¥:ÈKß#(úûK‰}—Jƒ‹„¹òÀpqÆKŽD0Æaß΄oŒqü²`Œ¬N×dlÆâíB\l§ððe y8,„ÂÁ"[lJ®¹SÇ.”]›jå7å×6mO%_}²à%miXci{ð“¥íÁ¯vmùf0™<Í'k{² ¦f³f%©ˆ]ÛSŦ¹*Q’Ô&¼¦ªS“ϦíùÏjb^Ç6mU°*Qk{ðñëµ=2«’¬‘¢®[mÛËÇÅ)%¬VÌÁ=´J6E÷à;¯Ë¶É^}oÀº‘ß~ÿÛ€¿|í&SÜÏ öv=—ìÙÓJ,Éþˆ´œØ ½ C�‹#N›„)vV™<û™|ˆ£(vVñŠV9F(v¸ ÷¢NRììæð“²Ø;”ÕTì4ðò”iWR• &V˵Þƒˆ×!Ý÷<ƒƒøétN>}‹ÿ·Ó·V`:ŒbSçÄþ[H,f é:øë ~ª AÂ;<¿3MNã(.Ö¨½#£¤-¶ÊZ›Î¦É©º„49÷ B¨káUsýúŸ[,ÌŸÀ›Þð2¡D‰Ðˆø“É¡jIbtÛ"I4|*è— Ê-ßT¼¨KZƒý»w ä/6rï ‡ ƒ"‚Åþ‹CúÅL:=Íz. Ö„‘ ℾ(¹?ŠÆ¯‘ÖÐ.»ŒjN‡šé=úz±¤a1§¿ó^U¡œ,6 ØÂNöN€Éy̓ë”Õµ£Çýæóc—ò¹iÕŸ§Øæ¢s…Õ”a~5™–éã×¥¨²‹ó«d Cf‰†¹ÔË6jÖ3,îGÁˆ\fìüÕ0¿ âpÝ!‹Os÷"rg¡ìß¶ªÐÓ~Ïœ¥JxŒ!@FtIá C©g!£¿c Á€•Üu7ò y/:H¸&Yr·"Ó@ hYt¯!‡?˜Ÿ_¤_s‚‘œêÀX¶aE¼S–%G,ª/¯áëÔh´&?.ÙŒtRÂݼJÌ Ur¨„âmšD­›•†r&f¡SKý©–û±ÌûlêÛ¾k"‰Ä&¸Äó7 &îçñbXð"vª(`LÐ’þ@.Š6ż¦F¬Aõ:ÜË8ד­´Ï¬ ‡Êœí¶Ž…r2#_‘aY *x! Bv! ÉŠ™^kôãðŸßwÀ½êJSóëδÞ,;H®çœôƃ—G»ñÀ’w3òzãy:ÉÑnúM ¼s Ÿ?&i´ ;§rÇ"»TÙº9Îgãçað“𦛕)á ØÇ»ÅœO'§³O0+²¹�VŽÈQ™$ÄYàþ?ñ¤:çc¬ÍîäÈ7¦mÚFûÝÛ-®8˜ÆJTràd:•²&•¡iV§“lï¤ÔˆMtšÀì$à_/�¶ZÌû†Rà‹ ±hÐÿ/¥ÀQ^ëÿ¿R`ûˆ¿F8?! 6UÇU\uLØTǣʃ¯<‰<øVÓHÕ1±m¦ê_,Õ±}‰þ#ùðÒóòaSul¯´6¡:þFBbßðiüßHŠ?/øZIñˆºW ß\\ühcÕ5jc>W#é û å]oHO"¤’é”MÀÈŽO˜ï5@–Àä<Pidb¤šGžsžkøXkçù 牟,οÚ9OüIŠ•‘Ìynd6 &—‡YˆËK*bç<qe¹L‘—$Ž×ÔõojòÙ8Ïÿ¬&&â´qžVÁ®DAÎyâc‚ó\0 çùŒ`)½G;ŠåÛ¸JcY‰÷Ľf1žì… éhÊs:…ܼð^m4í9?§™ösšÐe“Ý…ƒá„ý± *'¢Ýñ0I±qFå¯&¨brdjfʰÊgò^ÂsÚe›‰9ìµ ‹™<<Ás.Û†-)‹ç¤¬&ÏÙËËS¦]IUöšð7×zK "¾høyÆ u~»b?iƺ¿ÉŒ½ 06óù€þ‚çç7Éã˱[>Û['Ãô{‡§bκëí6$U&·ÙmKȱzÉåƒ9ûgn0‡94•8ÌeÜV°È¢–X4ÆØ®~Óˆ°ic±—4BiD˜ÅÇÔˆãüùnHMá©@>Rj0?RS)•â9Pêüü<HuóT #)u!’±ŸàÒ^—Õ EòšÆAfÁèÆ›Ákdñ{­í£ÞeK¡ñT­¼È¼ V?Gì7’Â,]_€—‰×X]$Ôˆ§.ÌÏ« ˆËöÂ#T…ÝðBS˶Àcj>>µÐÀC%c‰Ó*|Y†BBúŒUøj åY–"²,-ŽØj–àBôWá“Å[QÏ´’¥Ž e³Ôõ˜_IifÒK”t$¹Í¤×(éZHJgõk”àƒ„ – ÿ.‚„dœÊ}‡Å±B| ¼;Ù;Ì/¬6¼g…Ò©�d€ž•£‹rnm7¦mq:„mÛ|סMJ¹»äí ¶YR?”ú…Ô ¶K'Á¡¬¨®Lè :wÉÕ]Á©»ÕíWiãa€e9°>Îyî~É)*Yâ‹’¯âýEÐÙ,ÞÐ6£ Ë-±ëaôÛp+4{ða—.…�œy÷{P{§"R–W™‡Û¡$-ó”oDÊt}úTÊfÞ2_S#N Ì‘½ÙwÿÈ!„ÆÊmm95m†J9Ü.·mΉ=0ÛÔ6Ê‘€�æ©…ÍP™s£4_º^ºABÇW¡=à®ÐÎ h?(©(ü¤_J­©Ð¾µA-&6¿ ',w#¾îÒ+ÄÑåèÑÛ²´²Á ·W\\+;ŽÏê¯q…LåêÕëPNÓœr”°òg§Ñ 4| v{Y½ÔôP]ÿÝÞð㳞ÞµÍþ3<é§M£{O!Øж¹@²6/Ub³…•íÔ‹óñã•>íºÔŠê ÆçÀ{AÀùV…¶IÖ¶aän|®_™NYmÏñ^QOì ’“ÄkÚžâƒB£ôÂûçðr´y_½ûë®`ë¿Lö©Ë’½ƒõiRyª't‘ê”t•wP™ŸˆtLŒìYG 2ÆóPá씌ncµtaÝ3Ôá`ta¾/ ÝRÐî…ßz9 }(Ý.Ý&Ý*-<É­*¶õÕÚä½òÿ g£ˆ5«VæÈFkx·rû_ÓB‡kŒ!úoYàeÁAÿ5ÉZN"æVwйEØ(Þ˜ºGl]9å® ÷+så¶9ð\ Ïñç³ày"{Ì÷‡Ö’ȤÿÑßô÷8ý=FÒß#ôw°]nÞ¬\Ng(¨á�ÿâŠw¹ó‡H2Q8Ócän8[¦Kÿ¼Gœt:Ú‰Ï]ˆ1?•ün¶WVô.Yú-yófY,?Õ~÷/(uÛòèŸ'k¡A_t.<¡ˆF?†§DË–ÿ‚÷wbóïžÀ”>ä/(u=ý¤C@³ž­úŸÎ£=–ÿ›~TÒ)` Yý:ëQÖ:â§êË!—~…•ˆ¢µ…¸›äo™i¬ø^GœRÓ¼;ô%bû]ƒ²ÑEc³W™C£#¾¼`Dö\wœ><ܲv¤xŸvXk—½[ÅŸµzÛCçê¯OÆ–&º”�g¿¼¸µxwñfýÀp ºŒX.8PdYä÷'-ò/òíÀ»òkõ#PxUÿÜ<ŸØü3âN}ÆtÈ4h(竃CÊ4¹øâ-rÛW0êÚó”CP?˜"ß»{AÀ9¾ çþšeG³{èÞ®]é)ÈÔ?§dyhÖr:Ö’ �7mÛ KÝÅ7Cñ¬Ö~©¤Q9[?tÆÉºëÆîî0¼‡CçcÇJ$Ôz*´må8éó‘iÚ©o!±OÖÞ‚ü2¬ÂÄçqJ:6Âiºí¥¨kQ“Chg0]sÉ­áe MRU/óryÛÅ`;tuââˆkdÑ»é~.Wú3L²›Å€¸[#“ÝøñyöñÓ§ÙÇ´5§#ÑP¼ï÷iD!äx¡nµ (¸¸CdãZ¹ã㢹ŸA¦ò¨ì h]Áh®ò€¯V>M-uNUPï¨G %þ)® ØSc:S€<1F+VPcŽÐ+9™â]»Hg­þ#‚Q—ž;NÁì)`–ØÇ ƒã7<VÑl»ëÝ[7&j›ŠÏ=K.”µ=òš¿=Iÿ/î’ÈmG´!b³£Á±9ºïb ]×Ý›'Æ3äè¼"#÷¿ŽVÂŽ°úÕ#ã>$ªm9ú©öÜWAY¿aR^]ŽÍo©»º"QWº–Šë~'Ê…[lýˆ ïÌó0f¨YmƒšÓŽZHë…^<$Šëêò¨+YFêÂ{GØvtx„*Ü%®ø-Ìe@t×ÝÑß–¦\ÖßæP2W¦fŒ®‡l´õ·¥�4Ö AÕSjP‚ЖÒß–ªd³¤TJI@h‹¹Bײ'^ø’šA‡6wÀØê ¨½i°Ü­½]œâ  j�ø‰5ì?³ð¸ þh:"�£T?+ jó3`ϵ¡†¶7¹`½?£S¡HìƒÄÁÐëÐØ”Ø`/Ùë[%7E¦´îŸkóóR3Ä<†OnjkDú³+ô©ÜÃk|4?Y¬½ Øžÿ®uµ`F&åÁk Óò0̓ió܇齘®c:ë˜Ñ…©}˜:z2Фc ¦AüUìd_cV¾[;DÝ6~ø·*àì¾@²±Ù\#Z Y¥Ø‰ÒR­E2¨2±@jÌsJçH,P[ ÁÖÄ7\Æ Ž¸j&:¬E¦ÂÄgÍH€Ɔޠå©7†—Ö§Ï0–°õµh´/[Ÿ6Ûúy{8¿ƒ8¿}˜¦c.B@Ç´^LëÁ4à/b´B¸|׿`:õŒ-.óد[Ÿu°>ÿ†~¡UºN|qw`­ƒÿŸÞɾº3¸Q÷§)c‚È&çÈÎ.\ µw6¬Æ˜P){I£¥‹S»JŽŒ5Ï—xuËX~ZúéüØæ0J}æ\ ”L:lEøí½4ïpP"×öÉx6zâA—ýÐ_¯ŒÛÿ°"ðЃ¨îbüÓ'ÓÎíf³0ßÀY�vao4{Ì B7†QûîêÂÄH¶„o×U,ñe–8Ÿ%Îb‰s(Ñu9{+(¥·RövûVÌÞ:šÎÞÒÛ¹ð 6‘Ií4H-Ðÿ "><Úµ@€ªÐz%-EÖbÀÈNÀ³;Ú‘@tá`{Úõ‡ ïMòk×§JZ;0D)DOÝ 5~Pãý=åÍû”qAmg4£½ ·þÈaÃк9‘t*Ý-ÍG@~ia;§óîCöUÉÏCb¯ªx_…¶+ Ø:à7èÝ „Màëy@Ô#ô÷Æ1Âæ5ÜíÞvÅä²õ^sˆ®˜Ow çÁf…i˜}?j€JPmw—ESTt¹ð3hJ†‘I h»%c6({+5Þö¥×©0G‹œÒ’Ç䀄)JÊTÝr<•QP¤ð-bER[òNböñ ©Zg\,]˜_RYø½!ñ7íѲ!ä ª€3ÈÎ 8p@T2úGd) ®BæÖ)š ÛBκÙ�ŽJn3CŽƒïÊ÷ÅçÏ÷éÇʈæFzejœ•\½v’Ó­J¦^3ŽB°øðîˆv /³:ZzÔZ®mÑwNdtSqkl¬¿^?—½ë)y€Wcú£€$rsˆ6`ÇŸRÚ>ž/ù9<?4s?¦ûàlÅ·êk¯Â÷†)ú3TMØ¡ó¨ÆLÃ5ð3‡û.ò·Î†ì[€ú)Õ\û†úpñÇ?Dµ^Wĵ ÞKs_ú5p½.½ïtϾPƒžEô½0b}1t9þõhJè}#$¬ë’o-n^ýàÙæŒˆáó¡:-ûCZ·‘»û ü´æfBñn}MëELS繈3>Aσíû'þ¨&d³~î•\Dɦ/ÁÃÈ‘¥ùytÖ`[õ¡Da?僉¥¹90�šPt}€yV\Ø×¢ –}À T�'•&»}…ÎŒ jÚž€¶M¿Zf‰bøQÚ½üƒr5¬ä ~R9Bß‚¹¼¾ïÆo ®¦Œìe Ý9¹¯[Ù§dš`ßëK=æ ]nÕzè„a ôLÚ<u§v· ºü¹þÙ϶OŽ™Íèf-ÃîNaü‘O½Œ§¾oÚHœy“· hƒÚÛ²öî0—þZv°¨OmÇ0ö*ö7†\Fžâ}�-�t•ž¾¬LÖŽÉÚ•n¹:«¤iéyÇ…ÆÊÕþ’TÙÙÖ4³�ÐÅônŧ͟áÖ÷UÂþïÌñž¹´u8Ì"îWpáfämÏ–›w+“ã�$8ò ÀÖ-‡Ÿ hB rmV "¥Và1‚ìí£ôå-üTÉÚQØ Ò«¤Ú…ç,=ÙÌâ~Y›‹(º²ô¾³Ž‡NÞaï¯,½Àƒ]¿ÑÍïó/qëìú&YíÈñ^´´£ØÀó ëw³¬ùRN<í_ÅeiaèTé½ãCYð7/4þž¡° Ø~­È}}M%îø2ø~_ÞñäO¿¡O·¤;ñp=Ï×õ>\W…ÖÕÃ¥ Çä˜ò%`u9ÿ¾ýmàµyyru_ÛŠÐDýÐ)Èÿ¥ã¶Ÿ—çA(~)‹þ6Ÿ’­¿IßÎF®êƒbé¹Ó"ºûÛrÄp„‰²4ÈŒ8#óò´²<ø¦ö~®v¥DÊòþ' j ]¤;¡Þ¿µåý«/è­ ÞB™Ú½t¡ x;CÈÓé{o„²<4æeÚM­PÖ®Ë)¼7GŽÎq@#ZY¾Ñåý<Ræ^vš_ÛÅënÆS¿QÝìÄfôP‡ eª:4„ưy;€¡yâwÀ6ß |2t²3kC< €ö�NYr{”‰Å»µþèœ=r-ŽAC Nòçy9+Ó8âÈg€dÕ6é7aÓÞbó&È)¾HÚ®…¸î3›é)gñyRœÔÝb3^4¡ŸåC˜\k¼ª•°$ì©æÚ7&$ûÈ{íR&h¨èiv¹õk0‹ýÃN––¹•I$ôl¡lÙÓ0[ºÞq+UïÆOýTCJ(W¥<)eyޏû°øý”Ãâr‡×à9É»þ†„<²éÓnv!öÏ�o$Ç&xÙ‹âîÅp~Y7´î -†ªy¿†!áæûµ>4S@»´&ÐL—pó¾’ôñÛ} rÂ&‚ÐÉ„’'ˆ P,û,u5výnÐi‚ ©;þ²<_0š}ü‡C†Ø¼Ž6äVýè$\§ÜÎ%Aòv*(�™¬n…Œ{ #°úõ(Q©¸·uA…3¼¹õl'p£€Ë!Ë3Eê÷•,‡¥ÏÄÒ§‘fJ^h ¡ôå±6,Õ?§¤Q‘ô§3O>–öŒ“EÒ:c§!Q“rTyÉ·ò"I;äGr ÿø%mw0ò‘EÒŒbø�9Ì<Aš/éešÈF§ä=$G˜îK|¸ rDË{g.Xrª,®ß4G\ÿ°»õþØþbÜ)�¨Þ Wo–£ã»àÓc½ð©íh*´µPÛ"yw‹á ;jÌ!†[Üüá"ó!n~zÛ|Xm><ͪGbóLxðAÃêà]â#Ëè$7¦ ±%‰a´UËÄ0iëËÅð…”2W O£‡ë”廙 ÄðGôv³2Q¼C çÒ[µ&Å•œŠ"ær! nr¼”‹dm<¼­œ#‰ë>Ê_êOý%ÚSáq-ܰk0v*”ÀÙ¬Œ¹Iý<ùu@Ñ|á„)ØXƒ ¹ è§™íV¡Ü; ik‘2ýmØäÉqã0NGáƒ÷m³jYôO¾¯É”­±Cˆº%• ÊÅ—*å(¿;NŠ¿HùkjhÝ%Ãd4û";¤qÒÊòAxóã[ ÷h‰>A¿± ']-¶«‚¯q“GR·ùÊañùwI;,F§ÃdKëÇ`ö{iÞ‰çâ]´ƒßY¶S¼›ù!¨ƒßÃÿ¤‡ï‹á_ÑC­²H¬Ã_!-5ÃÛ(¹Al~ÏÅî]gtÕøKÇ*é(Zž‡>— ·Ê»ŽÄÏ‚™ÁùöhFâÀ©úKÆ*n`1Û…[Pøcõ.; ³$’Ù i-¦•‡?z *õÚ~°HŠ^{¨ÅŸ_¿N"düæÇBéñYí!Z~ý£;Í)Ã Ç 8·=Š«&ry<%r¹9§±ûp¥aª¤µˆSæç˸Sĺ4üɪϨà»)~!­Ö1þú”´ ñµòÁøDX?øÉ¡´1ÕðœZ®•Ò{v…wÃCîÀ@öÍÍ*ΠŠëœåZ+,ÏCni=­m¦¤~4Pî=\.^sØdIŠaí^ÃcLDݘ$YÒvHÆÉ»e¹ êV<H ¶àÌ|ÔZ£sŠÔÁÒeNÉÙÙâ”­ñn9¦*¤×/l—vl?ㆈêiÐ4n±ôFo‘ÎDM‘±Õ€O΢^:Å×xâ;!‰w²”wPjÂKo—'E×ߊ¥ªwê{'0£2Q¢+ ˆvm >FZÿJ@Zžƒf€À ¹ºß42л.g{¿Á¯íD¬Q„*@°Fø‹šæ�{ü@œyŒnI;^ÜÚô JU”É~o_¼ìw`^/gX¸¶™Ñ]ÜJ½?Ý;ˆÞ9ÙÏcŽÛ’r4z§‡Ü´801ˆfÿ‹N¿t~þ±yÎ.æÅ8qîú4ÜÓa½ú7:”|)$Pžr|,¤¦(”Å׉Vòç#Ò_\¨äû à{Vhl†ê òæ³½ëN÷£- ü bê[º»šu+áojR"ýq(�Uû‹[Éé9ä\öY‹ûHpËD™A´O;Єê¦ýèæ‰]¢=ƾ{w+ç`¸Öż Sqý¿ÌÃs4ôê~|*ý�NBKÃøiVÞÂi[À²£ÝÔ¾ôtìµ2>i˜ûYã¦EBg hºôÐÇ4ã&&“5†¬¦‡Ó¸®·.á3Ä€€éšó†¿ä1 m2;dõ÷„6¨C»œ„âmú?@Bí‰|Z5mDjÅÒäÝvk;l€2þ.V áï}J6ü]ú9Áéø#³ï e …׬x#�ŠœØ8Hðà‰}V¦¬šyYh|ãrÇ¥À¨)”»>Ï,¦ôK¬ô/ÅØå˜~`Nü{.þ ‚¿�Y'âoƒñð]ü"†ZHÛÖ'¾¸ð•®�WÜïíPÎ"u É|+·ëEU4QÐkߌÏÄp YfØ3ˆU´±_,‡Ñž:­\ ѱ€$FG:Lˆ=¿eѬaû£ŠÜ{ˆ¾Š…Ì‚“%aÑ‘¤£&'u '.Ân˜ÍjÄòQ¿Üb3XQ"Áß©ÊÅð·P9þNWÆÁß"±9ÕàŽ‰¼ªØöàˆ�þç6-vø%¥Äœ`“S‹àŸùøG.×>€10ÄÞyôóS’¥SŠ%BÂÃWòm¦Ø’~οfÏjfú¨¦O{]°·îʯ%¥À¡Xó–{ý�°æÑ…ùµcÉÖ7Ê\ê‚@Ù‘5J¥‘ûýýŒ&s§0°´ÿVèƒ>¹ƒAI¯ 5—póº(š¿ ôçA­K6áU t8rõÅ›õ½›�{ÈtÆÅ .«?ñþWn¼„ïHLLZáW!â„t¹k{a mFï_z�–ÜÇzI¸“\¤;*§è=·bÖ¡÷aÖgÖÈ]ÜK§ gæ–Ñøgvh €n¿ „¶¶7€‡ôÞ‹Ù°O€ØDæ%‘AX*�íñikÑ,G¯¾¼px$™ë5œ»e÷ƒ´8±Ø´]þ¨ÏíÍP Ö€³[œ^w(*TÇ.³&šhojó£9îõÈ)’xek»ü Ió·¥¶3ݤK¨³¡t9êïqñTfB½± ¾»8ñýðÝüûxúÎõ˜{õ·lyvZy"yúú‹êç¾t7)'•Ô`´2�©K$ÅÒ~CÙkÐ@ :šìõN$غ˜·KBöjŠS‰–•´Ö ·Í’¬"!Îm#‰¬)f5eµ(еD®—{Û—NcòÓt.rMQþ9ŠLˆz Q[^h|­ •æ vÍ^ÙÌE Δ·Ìú¹ê a„=Chú€³Ã éP¨HŸ \,‘©_B/ÄpÁIóØ`€>÷M n6×x‘iÓ§dóÍþød#÷éwqNîfwßR Ïg±Ãè7nf èkgG;.~?p”GZƒìZÕÄ|_<A F4íÛ« 7.¨+n3ºv-D?j»Ü$âŠ5Q$9+Hñ|£#a)q½�;]+!1»+ OÌúÆuä÷‘Ò\_ñçZ#wÝ;¨Hÿò1Qëg7!80rõÍ€óC<øhR¤ÎÚ¦m¡ –¯ã&üðé>€é&D¸á‚ØÙQ dlú¥¦O{€Õª(n­À­z3+ЇZ©üfÚL1ý­ ìÆ<ŒÈ—èló¸=-îæyV%2³úGø“imk+n„šR·â,†C^-Ï”]@A–V–à²Öe¦à×kóÄÇZ·o VK3a—»v¶IâK¾ñ@â/sU4(ä¡åLíÚ¬ ww]†&¹‚Þî:Q2Ú€ç)(÷n­û�[ÐÚl@XÔÚ�4Õ݃†Íú=îXåìŠÂ¶€Ö)-èþI¢æËoÓ§ t«½~Û2i¾i  Ø~ëPŸ‡«”¿–=;Ì ÈTÄïE¹¾ ™Õ[±(…Àü¡·Bo»P~¤âþþämLÖ©:,5»»>è i ÙlgO^Ô³çn¸C�J6z·%òÃŽù¢›{y/ò§ÉÑ܈(ptðÄT$»‡é. ­—P²¿ÕȽúéÄÕÅVäà {¾_ÄgÙ»¥~:E.³<Šb&͉5§`½ínÙû~ÝA&f¶DvJQ¨e"�e ?3Τ»Ì’1{Ém¶²²:ëÛï#]ºWɃ‘Î…gýl‚<Š;~&|<™„K÷ì£óS?ú2ZëÐÍð~Ý¥ð>Tß&O¤,³òà} F@55ŠÆ!S àéð¤ÿ“<Í¡×f)Z™bä¦D–…“m¬$·Ç³’—d7X®õjoûµ#4Ÿú90Ÿ«ÔYç�(qˆaäõ:]§®`Ãß2- ›÷‰Í; €d·< =˜z>³Ì"¨†*ß±Õøq }äÀ­û�»^g^Jêçn”Ì{dïP]–¾ó<ÈZšJ‰ÿ Êš¨wžGZýyü¤{ ÞqXïSì¼×àõwç!;r½º ÆÃv¦ß"«AÞ‘x3¬Æñ½´ïÂþ L"$aI0é iUyóGJ›˜l à5tN§kL› ° [ÿ2 ¥(;õ,¤=µFîóo‘µ&R€@„4ÀøìF‚°P°LŽÒ+ˆoüÑ9ŽØ-L?Âö½¾©€N|} “Oþë.öûïÊÙªïàïëùï„;Ùï—°ßwÐPm¦¯€ BýÓj‡ï‡¥ùyA "<Ò ˆbƒÑo-€,hÎÐ:¼n%µ*­9걉u_ÀQ}F½ÖItØbóÀYj%U.&ú0[5ö‡´y .¡É K1ÿ":xVÏr ú^ÞƒQ×Ë`Ww´7ôÕß!#¾Ü¸øÑ/iaÖb¹5©„p®§é¹ÕoRƒCU > Ñþ²­;†|ê5'«pU˜½rê÷àMzhŒ58óþ+ñ bâõoR‹ˆK¡âcû‰þ-yO¢AÊ…»ô3jNRõ'‡‘ðwmÜb7ТmÑ¢“d}³V·³lÎÖk4‡[¿ìdÙ£‡YŸö#ãžãqÅßx€ñòµ_¢�'÷š7Ø1ƒâ— &B¹@®Þ';a!ƒï¹õ¦7 @0óßs³š‘ÿ–ƒ@Ø­ÙTr!Óîéq¨ì»Í´íb«[WC ®LäëO“š¬ÎBÏvGhüZ\gýp˜ç¯ðDâ�žéH£eÃGp9[‡=>k Iä‚¢!æV™b³‰3÷i3w€Òze*Y~kö@ ?N<×[–ºˆ\0ÇéÞJ¨Œ–P½†ë†%T‘Š©:V‡„Rõ`@y\®EªŸ’âHêq$Π—yH×q®A”)Ë”¹ã§´¬ãeùX ³Ì?rdäÊlš——2–#US‹Z„[Â&`N±ÑTˆëîÌË’µ·ëϵ-ú^ÀªMÕyÆg@|VçÃA ÿªslvv¦ H-´Ô�·Êc)[LÓ‘»h�ðux«?zgªÛÉY·§¼Ø Ò ©þɓǵ6ŒLضHö*$\B&Ñžú²ò–Ý5Ó:ÊÒ3Ê#fAB ¼”fÀŸ˜R )Á€Údª«Þ^¸®]Ëž 5þÒ¥ùwŠá�÷ûZ m‹a/ò«ÑÅ©ruQÀùÕË@ÖÒ<1˜G’2ïV1Š <€C¨Æ€ýw²þCÊݰ4Cšk?2ÎW<¢k{o½4¢ãRcuIãè=h©C‰noÒ\Z-À½„ã¢6y)¼ÆÓ 3«jHÞ÷—-“Õ+ å^ø;¤,’úˇrþ¸C€¬O1r{w ¤> «? Àzµ º$õ£)÷~´ Â™ß!kc`�ùÒÊrAjAgX²œu«åê§ÜÎJ¦I†??+N«ôîÚÎ�)ˆÝÞcKW”kP+€ÿ÷å{÷-“ Á¯z % þ)cç¹ãË;ÊÜM}¤óWÛ¾ZÓ\ ÷ÑôÖ9»²FqNi9‘açòŽ_.GŠÅ[ÕÁ”Öæ‹§c�¸TxtËÑy¼¯wÞÆ2»Œ²¬xô4~!æ•;hÞ*e´Ð7œâÃ×’aÆ%+¤|¥Õq'äF™j ˜=`òê ÚƒEZu‰eN?ŒÆŠNq¤šWn6 ‹ÑÿÂU‹OV—g J†½&ÇȽ¿Çå”ÕÎlQeï`Ý{@ÈéìBH0«6ã )Õ³ôÙ$úítš7‡¦é§÷aîdøËYæ‹>'¾0îÔ¤3¸¡Î¨ö ZDl‡¿;ô¾nÄÖd¶SPê:åýº=;ðxþN‡í÷N>ú n°câÜÞ¡.»½U»­“'µÃÑ€BR·ddM\›§XCç—º‡&î?›¾5©éå¬iIœ{\òµ3ö²÷�¡×ÉûÐO$4õAFÙïv}U•Õ‹pÝ cY/v¹omÇ^ü–÷âȱ‡¡_JÞ/CëÿM/ì>Ì4§¸?zµC¿`÷ªèmNsx'ŸñS‡ðÐÝÁêD °}®Rgö JåUZVo¿”§*9úÕw0ƒ®1FT•¯¿ôbȼÈ;ÌÅ,’'ï bv=Kܯ¿»ƒ,ož‚÷yjþ‡‚ÑŽÍÅa,ð')dÞ0žÕnF“òpÂöæ;g¦„fHd½ \¦•>Ól,›®ß‡ØS».ǯuªÛPJæ¢V–n}@T{ODÊòâidx±ƒÌ Ôj75‹ö6‘Ìd^%waƒ§éßBÔtÉkºpCŸ)®«Î;¥>éÆCÇáÄvéƒÅðÑjCÆØ}†‹ƒÀœçY D‘_û¢¼ø#¶8þUÅõ¨j÷Îúù*‡ F„`½ús˜Üûi%tA¹ç*m¼Þ?V¢ ¨™Á)]ßoh¤yÖi‰¾%©C@[è¥V�àLØâ‡Æóã“Ê›ûC S ¬ãéì³$ú½µÜªeü¨øŸP±Ñ‰"Á<[&”íbÝPäK6o•¸Óµ»ÐÖŒ!ÊKa6¡£Ø„y€ŠøòFà¶ú¹j墨¡iruÁI£CŽf5Ø9»%6Y]°4ú977’ÝDÐ(€%À£vîeÙMA|¦©eu§ìl ¨ù‚îî�”¼ûFlhúcÅhIu¹ÜܯLR—»è6 Ç®r+6±CÏÛ†Pî\¦d¯sŽ7‹F€q§1#ôMö¾»ôÃQMª¶‘E€«ÿ g¹YÖ`xÆ4y 3`°ÏLÉÕ{×3Iìúz¤¿&ê«“‰^=Y¯=lÒŒcöB¿€ ¦ÿæT .=#Uëˆ?¸]‹uí¨PEý{P†Çüyâ7“¢VºTXÅÊ….¢eÀ]ýÌÌw ŸÏØ ,Qq¢h<'YxBˆ§ãס-´³×߀8«a³yœ‡/Ê­í6üT½x›O¯Ç™Ø'«3ßBcõ¯H€¢ò±zÛgØë¬÷æj<ÔMgÓG¾@¼éé>S¿©Š”ðŠ(íâ‹ð~uÿ ü6‹¾5„RayØ]G+ш{s¬t·dB¡•Ζq+»W:á1 åJ€÷U²˜]+³!A¦ÔôõH´Ùgþ•ÏÆSÞ‡v‚M¬âœ•)þ=h—ßà[¨×®ÌŽgÎ\¤dÏ,WÒ©ehðstA1¶6z‹”A« ÷JG‰3„êç•)¨U56´`ÞC@ £önÛ¶CbŽ£F¼ÈõÌë‚P7†NH{q«)FU¸övz©uÃæÏá¿©<ÁȤø©˜æÓ/…Ió#PöG&™r]Àï±Ð}p7)®¶ÁïAüÇ´Ã¥þ虫He툕»Êµ#êýê·QÒ~0�¼Tã�HÑ['$Hˆc´õø¿PCy_¾Œ†ž™ºÉv I+Tx?^ò-ýq(ý2R#‹ÿÝ*¾‚)ðùŠ¿&i›$ï»â5íjkºü‹ ·w‰¨_ƒõ ¸;¦P¡¿Íå/ù;‰ø•ËÔ‡„éí+ŸøX‡ìhÞ'‰¿ÛTÞ|P9!ÄÕÀ2VC+o£>‡ìÜ¡ÿ}K†¥u¤»ÑÍ.›´rŽ£%…ÛUoHÚf{m;0ÇŸïñ±6Ùù¶ìÜ+ ¨ÇQœˆŽŸý¯ë߈¤å&e|bìlä+?Bx^J¹÷„âìpÕZžÓXR¤Üè×ÊS%Uw<& ôt”9¦Kêà]K'­™Ò¿VÐÛŽº´ múg·p]ʽ»Ÿð;7@† ÈðššÊbepÞ»»2ø…²Ô~—v?0¶ÊøÆÒ¢(G¯uHkˆóýÓVÛ &– ¸Õ•Œ¢½(ÚzØžrŠöV¤Z|Ìc+4×Ò«¸eňIg&¥’ö…ôÊb‚P€šÁ `.@ZªÙ�úô‡¤OýþÓPú´1F•7ïSÞ”Õ-†ì}[9½¸/ˆ…5Û#>Ö*v·6÷‹¿cOôŽҟŠ7c:,M'Sù;»tÁÅÕ‘&Ä/1ø¹¿¾�cB‘V¿;;üùL¡¿x·†)Þ¦ŸÕNaݰo̵/éŒ%ñ²pƈ’|Œ›õÇðŒIèØÐ#½f QÎ+èõ¯¿÷&îÆ~Éû™$? x‡B3ôÛp`þ•|pèNý‡¸jÊ(çîA'¼çnÊG£ž;,~©U|ع‹–gú[.GGIï^8rï|h¹ÐÑO+Ö~¨M¿TžÚ&{ÛÄæ_Óâ– Éê1ØÐ7¦øµíx4D;6U@¦,z7ʽʨœÚÃ{H˜èzl$4.O™ƒ'ÑèÔOÕ‘V´n ¤ÛzNßܸ¿·±­¡åpI)zìFk‡’3°ªmqû[jÔF¬ ›°B‘šË…×íçav¤—)xÞË,|™D{›¿eÊ@üšm ô˜Uô-Ÿ´ZV?èUìÅÚBgú[nO!“µÓ)Om—z*&RÓ'CN ëÕ17åbü‚fÚ�)¹Ê[&ÄÇóV À"µœh¾UD|$o:ä=°¬[Vn LmÅ6öè5’³½F,½ßYãÜP#©·¤ÚôThÁØ^Sîýà¡.ü»TQPt߉k4–Ô(®Æ’+C0]y²Ñÿ çqüJwã‘ÒPZã†-9ñô¡¶wÜ9ä3°gû$¯ÄS¡½«·nAªÈy X½¹¨¼d�½!i`ïé­ù½©µÐ+­£ƒ\Ú´·M pgCFÇ\G!‰Ä3ái:>¡yÓ¤"0ýK‹å5;®ûðO¿Þèaî•iè¥Ó¦Ï–ÝR4uœ|÷î<8Ž-iÈ—;·¼ƒ¡1²z䮥Êk2ßúCí¬Œ·þ€» ì˜aef/˜±´P&ð‰ÿ±SF”ê7KI)[ý%óóåÑßr£ãß@ÍÑä�'á9+%í¦Tâ|ãQª÷Thû­’¦ë¯ìFÝ™*ѯ셧³€ˆÞž„’õ[ß7 _ÉÒQ ÂE¸Ï€Å(ÖÓ©0ÊèmÉ…=TaÂÎ$˜ÐÖøJ~#îFe²÷ÝÐiÅ­’:3º\½Á¯³³#ÕQÔáw�Ã‹Éæá•�§ Ë=(ØÝó¢:8²Ú[ˆ{õâM†§rÅÝj~ªìl>¸ì‡WŽ.qèÀüÈsï ì©›A]ˆúyÕ¢¼ZÒ$HJmÞ÷@ÙÀ>³ƒ¡t­]{׫‹=€1í߈uUoµ¡ýn$Qõ::øã+ 1 æð•Dâ¶ŠŒÁT®ERu•6ܧ~Ú�ˆO-³_ÕèˆO&kD|Å71Äw¶ÞôÞèˆï—ïâ‹ß‹Xo¯‰õr·%°Þ„v)h¸àwvÐG"Dvê“nL`9ëÚ[–‘¡¸ »yý»'b«BÄ&¿BRîÓaÝwÝõÉ72$ÿøœaHÍ]�½œŒãƒÀyÈ Ø–›ªÿüÝ‘C… ÷4p°5ÛÃJ˜U?l/c¡q'Ž>zF‘ì=ªŒÁ-QØM¸Üa4¶ê—ÜÀ:Fˆ/ƒùð]~o¯øD›:x|©×»é¡ËÃÀš\Ã@ )Žó­ZØlà^ÃÙpè·\4Ød' xö# ~ý¥4ó2‘œ4¸õÔC—Hê‘EÊ=ð·\¹ þÖ„Èw¯ÖOóN•< ËÇow(ùðWbQ/9 PæL9QÏŸq²¯uðõŒ“}•ûi}K¸.„郕)x¶ZÊ (•åö$™å$ ð^Ë }©ÈÈý¥êçŽP<IHÃd4ýsò|pPšûÃ4 7„øýø d#5Þ¶KÍ4â)†m£y÷²àh²�ôþDo5ñ¥³’_ÚnlÕ†ž.³ àÀòOFçêG—-Hý>ì«TÖbJV@m .:ÆÆgB/JÐÈ”5íL§ùØ”m…N×;5RKÎ_;›H:̤g üißò®“ÈFú“´²ˆ®œmù“<“zR’#°,І×Vúé—ÍIªÐ‚Wÿý6›KItïdn3÷Æ×¹”\–ÕQe_|é\Ó £O¸e¸Š”…>”.X>kQ±!­EÃUTêòGoê!+P摬¥øð†t´vicTØ ë¾$WŽf¾ÆÒÄõAwïH× 9êJ‡âúñ)Ì9ã.îœ1ÅtŘ”Îv˜)Ë͇»Í‡Ó̇´4ϻ²ö.ÔÑ´‰ô‘ƒ†ÓÉ4¶›wÐxÙtÐxŽ9hˆa”ÿ‘“Æm)v'·ÜI Îɇ2 ¥ÔNòѸÂôÑø–¸îãÌצÃ×úÓÄ×`ξóú/Q èGG ¿Öû-‚ñµ¢rír€‰± —“úȯ@_±†™k}ñû6‘Ã…SÊÔÚI=ÈãÄæº1ˆMKa] ×3· XIôOx·NAÀ”^Và/™ L_<…Œnu÷%–;@ÍÉ耿~ïÀÒÜÌ ©ãh3^%6ßï_ÿ&ŸÆû´`XnQEG“šÈÒü…¦µx0v1$Ùú “O“"½‚9jÐ1І=U¼-~´#°äo™Ÿ}°º1ËÁÀuöp>‰¢Ù´þÛùfO±tUÜ]c^:}‚~˵/cW£(•$u5åÎ^dFjc¥¦KÈ(?ýÛ?žˆž¸w³—p¿ÀÝC_û}žjºˆÜI."Q€ÃkCüž µ ì¥ùuø•žŽ`ÊÙ qÖýpÒiœ²ÖÏ2vÖøKsЇ¤5­Ï?— Ûe¶ü¿B;àWÑD\wÐrQ˜{ˆ/|ð»jÔÖkküp¢¢‡Îàm¦‹~÷C¹ôøm6N³Þ^”pyïlèεÍG¤ŠäÂÿÀí»=á"„ëÆÀ߬úô ¶“ãÓù¦RÆãÊ“{È\æ?9”6¦¢z.¹‡Ì¤÷,tI ô@¿ÜTg:ÖÉ}C<ä–È­0.Rõã¿W÷‹×è°çîòt’{HPÒ¶KÆvË=dʨî!)Îæ²‹ q4LµøÈä }H‡ùÕ|âN7(éþ’IbxD@rÃÆo™s£á5Òñ ú¡ý“ý6÷z8t@ˆ_:¥ÆïݚLJtE¹7V.^“šN {5¾ìB)úªéNBr ‘î$™Ò«V~€!²–½}ÊaêÅõÇí.%!×0—Ó$ˆ‚^r%)Àj<F·vÂîN¢“;É¡ZýbW²; ’Æaú”ÜŽÙ2’² ð•xCi¸¬Ü¯ä“ç¦3AL.Úßï„=ûÀ¡ˆk ù_{%¦(ã(x%Fö#¼ˆ§©¤d? ÒJD¤‹Ë ?ó~%²B§õ†æ±’ö†Þ5€f*ܯd!z¿¥»‘$[š¿ ¸•Ið75”K™Ö1œ%<€ÁÖ€d Ýã÷ö-[$kÇ$­Ÿ ‡8hžÎ@P}ƽ ’ŽR°º°à²sÈß@| sQT×�ÃÃÎã€ü–¿H=ˆçӺÀ•ðÅOçÎ"Ð7qÂncEpΠÌÒ³±«Ê¬«žDW?ìgó YqNd‰…\I8™ˆáKI Ò¥g=cgˆ×æpÂJÄ&¤0ì§” :¸X™½ö>[ÊDÂãêà±yL ÁžÌØ ZŒŠô%”X’³(ö¨º='öª2õØ.ƒU5êìËBãkÔå)—RîTLŒa,øR̾\b~ùJŒý¾¬A4\Sš&†5|»Û|{ß¾o¾}ߨFå)¡¢’‡žod½ÃW;˜“ã] Gë6Ó•P_ô•5‹±O1|¤??TP®]„'‹F çšuNm»A¯±Ãð­,üMž?zŸÜÍÒß$Ds€œ1b›±»T'OiVÌk¤RŒ>E°#u _)%ÆñŽf5b¥Üw¤2¦ l‰$o«r™:8U)\[È–hòÚéìAäõ¶‡nS·»ã7A¥ñkÔíŽøU0ãiq¿º=%>›užûŒT‘þàɆH´E<ÏÜ+0ew1*ŽIŨ‰¿v.Q|Íçõÿ[_$$`‘JyŠù"å¸ÈÒ)]»/’Ý¢ˆq˨«»ï›Ó \övÖçëU;G³\ hx¡%ñVY|úS36€bQ·kô.A¸ölŠh¥Ž‘[÷sN˜®ÎºòÔLŸ†b‰™o“Bk+òÙÏu:±âø“ø2†½0¥7ó¸i¡’mÅ›áéçÈvžöSZ+ó£Ð²¯šå�ÓP²Ü·à‹š] d5›ôa"^„ݤ7êúç:$Λp:¥3p0M8§øÔÑ„Ž,eMø™óf¡Q{Q%èÜ¡ÏýˆÔ{*¦ô©‚ƒ¥^ ­­¥™:—¾G^ne“„f=L^Q)IÓðG΄U¬ÄW0·‹—KôÙÈ]ýwØ'ÙwÎtÚ ‘{à–E„•ÌDŸöž©ÑTеìËgò&§P^3† úÀ¹‡þ.X1LÒôùO!ß‚&s0=ÉV„Û‘þÀ°´¥î¨œ£—¢HÌtÛ‡=$†) (t8«Q  %ŒâÒ¿ü…2ÙÛ¿ØÛöÖÃÞ³·-ì­–½½ÂÞØÛSô¦‘8„~ÃVáéúd ZêtAz3¤³ÕøÃ´F j€ NîSFh"ñ{ÍK(¡Ü½Ê­bïÙo.c«Â\F¥7ð‡ùØÖ¬$‡ œÍCöÀ;¨œ¢Î0 ìNýg[Ëzæ~fgûÙßÙò–Œ´§Ð²#3ØÎÇТ‘›NË]7Ôi=´RþÓ?¯"ç‹××Ǿnú_ß'+Œ*àî¾KKü'Ñõâb¬wÐPd[¼›û¬}r±–-Îàûä_O’LçL4íߪo;Ž•lyÒÚ'Ì1M׎Ó}2Ãbt&ùe©³ÚnCͼK_v±O^@Åmòº¬gÎRRŒNlé¿¡r¡ÓA ßO¢sQ®îÒ¯£žÊ×ÿ\Ç|€IŸ|°Â!4ze–î{Ò0½³¢µ¥L®þl3}Ø®çaÑ^E¡Ô¡_ÚÇvé+¼'±‹ uá´vºåêÙÙµ‡þZR+ÛWŒ]T§ÕWùB§kÓ­ÌpÛ o“ì(`pàÛ@·}’ ø6r¥ãktÂ7°0©÷åWUh_´AI]„$Ȱ;¤Nƒ´»•´`$% ntlôž[ÜÚ|Ð÷âÙ(¯3Ú5=^*å´KÓV:Ih.¾XŒ_vèûeø¸áà«/ܯLj>ú‡¯qÈ£Œ¿é¡ßcžô޹†G:¾¸fD ã=^âjô¦‡\P4þ¥o0vÐGŠ;™™Ý“Œ1´Å›ã˜Íhkô.Tú±Ë—…·„&7÷+Àõ–9KΆj´žx&®Bs?ŒCëÛ‹56cûc:Ò¢@ºÜ¼9Çí¡\ÓÜrÏô(sg¦+e0!—‹ë»ôT™ÍI!˜„Ž46,ȇÏ$ßhÇ4š£=v¹ÄP;Ïi8W@Íχœc†//ÚöÙÆmLKò%röä[¿ã¶õÕ3ÉN}õ {s d;\?½‘LÎûþB'ãnAŒ0ë@¥ƒt­.ÜyN4ËDÙ5Ñ¢´Ca2aå«È"U#Sµ–ßü3Eö~Q¿P®Æ‹¨‚Ô_+GÇßU'£-ðŸÞb'†_Œ¦ö%ê%a*¯õ;Äjá@#³.‚®H°ä­þÈä)ðLf–8 Ä ,0!O¦KñiÙÿ„!—ÐÞ‘¢J>ðØÛB—¹·Ó€{ÞJV§+z;?ñ³ŒÜMBX·ü…{ðïˆd×ã3JŒÖ‰ýN(×Èb•§0Yãˆ38¬üTåTÙ9ç†̇Ïа¬ÿÉ…Çä]€� ß£×8PÔ:úÝ…&Îwþ .y?ÅK'·ë÷£É¾Ö‡¼8Q–£ Š7 +àA¤ÏùÒ1âø}ì_PIû%²g¹ßý3v­#^ïÝ¡¤ÃèÅæ YÖWüÙž.×%#7Ãohÿö¶­ruÖ{ƒÖG| Uœ Ä•2¤HèÒïø!#WÇ‘ @” Mïaµªˆ"·‘…íÃì`ÏÝDûÓ³édþA6{Q{9c#•ÜøïËm@ï•ÑìË´CÚýLâ¿öÑŒ´‹á•´ -ˆÍƒÕ®H1….¡ŒC9„º!GnëÅàW²·».FArÍ×úTÈå h{Þ=b¸Œü˜ç¸+°Î˰ÕÊFnvKQßñØ4–ä`Öë©Ñ¿±kžÇíªTõˆ[lÖ©¹_áýrÙ‡/A+ñ-Ú¿w×CçÚö§å‡]•Œ µ-§"²Ð1¨ewÒmt›à¯³ r.}»iÆ¡U{…¡¬N‘Ɉßß4ÈùV3­ Óîlšñ[Z˜¤M3µ¥5cÚŒ¦Q[ÆrŠŸÓ4ã[JIã§6Íø)¦e°4 ¶¯=ŠO$®’Œîõ+žF“K¡‰ˆ}8ÄnŒU¨ôöŒÒîí ¥Ã<u"6?BfYÅã8ñÕví;×!0e‡üÚ%ÿýþô_bhJo§’%@«®ð9~?y¹7þÛ.¹þ? ¢'P¼ÏÈõýwâ9ÿLðRb&dþj•a?ñ+Ñ ej¼¦˜7ô¾ý=ÁÈä«¡µÀÀx b·8¯AºÍå�LÝžøÝXÐás ¾.GÞÒ-GÙ]XEE=Tæd¬m÷]Ÿ‘ûGèc\ÀP\hù¹`XùV>Rùz0âë1«)A—cßȺ «‹ë‰Rhútõ)¤ÂPϱT« ¥B‹ªu?*²��Ö/Mí™H”žA £¢€û*«;�îï¨Ð6ï^;Axé÷‚0 YÑú"æQ UgàÔÛrq�¸é÷¸ /ÐwŒþ(G¿õÁs°»p z+ó\< èÑ<‹«³úÿ�²Ó'÷$žòŒð-ÍÍûÄ0zü)F.re˜ç,òöÞœáP9zÍ1?9j3±ÓcùdiBHlÂf¯t±Ÿ.ŠºrŸÃO [©J3³¤íH�ñ=±—ñè³ lƒÂ€km^ŸA®eaVFÝlð±“Y0]Eõij8cgþ»ŠåÓ˜ËSü¾½Þ bX=ÑYÒ³Öt¾ù[œfè:Værã¤AG¡ó³Šµ=ÐuØÃ;­îKÆëÉÆ¹—¬Æ Ó.@9ÀÑß°Û/áÕâþoi÷¡ö«ˆi¿ä|këx0@•–{Î…(æì‚²·ÜÛ-†1Eã AY‚±º€k>ô”<°KVÛ.›ŽPfÉ­lÛ¢ñîþ§À¤bêÖ ããèÆ¾4ßE=UŒñ‘é›8„†šÉßähî*nÏ‚þ2ÙÎgQz†þR’¶;�„x?Ù‚=L]ÆqžÊƉc"ÿ~ûi%Øîº}jBÍ)Úô+§£‹À Ýé±_=aˆá›igeŸ å4~K" 1<RsßzV`þÅ5¿!O§Ï+ˆTÃÙž‘6¦6/ 2ÿ W-ZïAgr Çbù´òæ~›Ò³½ôãúô‚ ”p©qöÖgZ`Õ"¿{7�E# j·d5žï~?§Âûn…8'^Ñ|m¿•üÅ’øâW@‰O“]…_k7rÐË–…3+ɦÃðž¨㟹Øl¡Ã?³RèHm€»~K;¬Æ½—÷ õÀõŒÀ¯ „½»øªè鄜£ü<زѵ‘UH ¶âi\0t×Þ@‡«+±õ™Çõºó×´^¸4¶}™dÿŒKƒsƒ¶Öx¢{ Õ·¿‚VÖétX»Œøx³&öd&ÊÞqu*ÃbG`öqFî~Müb¨ÝP4¾i]`£¹"ð¢æE<Â~ã]n÷ ~9æpßlF¶Ð…‹6Wç¢r­7¨€îªÇ†B“õ«¡b#R¢OKêgl/2A†÷}ñá?¡øpr,‹¨þ’xÔÃAíp mÈŒÎz’ÕM‘ïí_0OK׋Üê~Gèý«— ¬vÇËVµè`c\?Z‡&LÉ`œ0a«°†~‘Û¡œZ®m hoÀ‡_QD“Áø=lÕ¤ÿ’ê–Õä¨Côcü|}kò–“6ùÞ$N·Ð±üXüOÔxÛtîkŠ&As^.ÐýCškÛ”ì/Å‹rMGf·z/�7_9-üÉâzCêŸA“g•èë¸ùÕªwײ $/ŒA\s ݶ£®|Øž8Ã’úÉ”«´¬><N•÷ ¤•ŠXâKé÷u»›BL¯#”.i›ºÿêPVIý~ÑíQ²‹wk[),çÍ'0x€žéfž¢_P,ׯyè’ÚÙÓÜbøm”Âmý…i¨¬¼¥• §~9KohY¢øѧ~Å*û#”´•|¸~<aŸ`˜A`¡PG[Þ|P /A r`¯¢„ 37SH¼¼ref9°èG2äèô¢yt•Þ‚$>Ö!©K'V ŠGZéŸl æßÊ€i‹?¡¤ÞŸ°› ãqýf}ç8:1—´Mra{À¹¯¶¼ÓÁh-?+Šq`Ç–³“¾¿íñ3ÌKxöPN½ïržázG2r¥dáÔ.Ä£ë—HUŒ¡_ \UeÞÏó8�'gwú¬dô ÀÊkÐØYËjCܤþãŒúµMFîÿ`3:áü1©ð¸ oé}ùAñaQÐÄ•7Ã,?-$Õêk¹c_epÂ;è–‰mXÕ¹E¼î!KãXú ¸ÚèŒ/ÂÍC[(%4Q|É KQ£f ^Í‹€Þ¶,—¡ÖʯÌF3Œ‘׌q›ÕÎè™rš¶½N€_O7?‡ŒNlHk— 7JÞMâ œe8yÁ Ê#0Øk³!6£E9"¥¢"ù˜Kl^úã/Y‡ýïðÉ‹õ�]餢FçUZð+tƒWi~xêô§QlI¬=þ'#÷‚Ÿã…+ñŸÓûïaï‘É¥ªÎlø-‡u_@û”¶c7M/lÕã½.ÛÏ”¿/ä{Ò(Ô&6‚úsV¢üeÓÌŒPfËfè�ˆqÿ|l:bÀV¬ë–ÏÂ+Ѐ¬Ââ~­Ã_ˆ¸©RF]¾‚Š<ùŸ#e‡yÌÓeäª?§½ü-'ºüñäÂ>¹ ½âÃ2WÛ´7„9­h¯û‰N/ýŽ;¶š"‰ ¥ˆÍ¯›#;FF¥N6¶‹Å—Ð&yS nX™ ©)2ýk£5‘1ôÚtd‡¹iôaÞ=÷ 󉟱=ytú¤C}ûĈ¡b˜âØH§1¡êN¢€Uxzk晃¹0i0Y5~v§ pãô_q"7ý+@ݺ®d2ÚŸ_[Ø…ç¼Ï÷õP¿ÖF”ï/ 7ïF{‹.«¿ìÔ¿ñ(N@ލ:WÜo?øÕ;`üY˜»c*gËW&À€ì/ì`3²ò1ZøL§‰Þþå¯â…~‹Åå„Wñ‘.ôI<ÁF ðAòžWŒÁÙxú8Êo²DÂ.Tg¹¶) ½nä^ΫD÷Ñâ}±GH.~*U[qœÐBí Pò#(Æv=Snà >%Sjœ™º¾¾êîð8âMêúdxT=BL†ôÂ(·y˜p!I³~Ê”x-“H‰‡È3Y‰÷Mé’3ôŸçtIôy –|Ý"K~OdÉ>?’%?N–|ä,)Ôo{žh„ËŸ?Pñópþ.LÏwK“¨’±Uòú�Lß`<Wÿü9 N¥RÍŒ&y Ir}‡æºž;Ysï=ËmŒ"1u%†ë‰oá¾ÙËB4ü×T”áµÑJ¶:/ÏÍC1ÄÇÁK*Æl€B{&»Œ¼ceë†2´/U …²õ ­žÅ¿Ý¼¹áBovK™C¨?‡ êrŸ@µŸÖf”§¸Þïv«½¾øÙúçÏŽ¦peóñ,W·”ü?ö¾0Š"k¸{ŽdrÑH á0ƒDh``M‡œ‘ D.QA’Œ‰†$;醠ÁI4C3º®««»²êîºßº»êº€ I $€$€ ÞQtí0 ñâ†ùß«êžLAwùŽýþÿôTwÕ«ªW¯®WU¯Þkì宿íƒírÇ6–L”{ˆö~å©_Ñ­5ß 3ï›Ás›ïB<&xb]ßþ –žÓPûGÚù5†”Õi´{¾d¥oì>ßHi nή1L$f‡ìž£ÒË~P¨-ââ²ÚHÖ\ăWA|¾–œLsƒ‰ÛÆ0[1yìçSXr„!uº`½=Ø`mt¿FHׇW‰ó×rocnc½`mXÚŸOksÈõ·øœ† ÔwóŠEùó“äìF·&Žåi±d"CîÓÈÓ ‚¼GŲ̂ЫbQ3nœÌ"º³€“çÅå>0¨ðÇ®í6TÀº*£…ÈrÖO¹t]Ï(<«Rc­™‚‹§§ vߌ!øzVŒ­Ž2¯©IÀüPǯ^'ö­Ž² D­[” D6­¦ŠÖpšlpàuMO€•¶n“ûC€Ïð¾,‹çTòR¤ç˶byüêiù²Û{¤l€”¬ÝdØ!5ÃN“/HÔ)`:ä;Ãm„Žu•ãä0ó”¦ê},®Å~vÏfäs4oöžµÊè1ê°äðôzþФ± …(nÔ îôèƒR´{3$ì Ö¨ìX‡™ßŠg#“Ȳq×PªU.ÛIc¤ylÇaD“ät‹òìßè±t×1+½—ª^K…®Ñ¹FŠác‚r40Dße¼ú'ô(æýu CïÎ&ب"›çìy©ï.ã©)¤ oyΞ“È}"Ë.ãj˜œel½ÄETrVžE׺š<2½1k|£oH²-ððŒ÷åSÖF´xºO1Â,Ó‘¤ÃÖ3z?tZ\É€M§Ž3¹škp ×ïuè¡êÏi£»cuP9µ¸9±:ØAÞÑž=·©5˜8ûõ¹š/*VF4IŒP=›ˆçŒÁ¨†G%çq}W¼wÅ aŒâ"B±Ð½Fd.H!ŽÎœÏŠ#¹™ÜF;k=½41­Ò¼q Tmpèßr¤í•¾GiËÌ$ VÓ®zín-™L°È aò ÃêŽç¡[ˆ#=q°§#N4óÖ½¢An Ül0àˆ¼Å[±› &4Õ·9¾¼Ób¯ËÐ{ï2Ù}+Í0̽†²€0¾u´#i†Ü2&¶ÚMª†Êwz–¬ò8g—.Ê)ÇE©þ8¿w/r|Âq3jêÏLÇfÞΊ£¸ñÜFžµ¶-EÅÄã†ðkô¼þ-ÞÄ¢oŽ~¯µmÉ1ä†2“¥V‡§Þ”£ÿ 'í-i+nõWX6ÄTLõ|e“<_ʼn±¨$càVZ¶­´l[iÙ¶’²Ýk÷Í3;|ËH¡¸M‘8ȹ¼„÷BºÇƱü ݬGC ЉÙ5”w¨&;;²BLÃ%uþ Ç ŸFi%%t*f²Ãöìtr«²í ¶[§¸šu@±×,¸ÄÍÝJö•3‚vÔijœÌ<ÓnÚÌo& -ë¡ÍXÉžå^…Jr*�\›J€¡B3lP¨~8}Ã7&Ãj*½²Cq;iøsÉØçC¡õ쉇ˆñ®6 R9a|ý*TV‹ê¢èPðj= 7òTƒ×èšÝdªÉkÌ'/±^ã|9„*`>ãôä AÏãŠd{Ánn“0Ýìâ½5öN‹FF |JAWŸÁf²b�€­’g×Õ°]E&-«ƒØíW ´´®©Iƹ¡Òõ�ETE>‚þ0l¥x»wa0-§Qù�+Ë;äø`–â«mPø4ÜÆøm %/¼ÒøGMÝhÀ˜dýŠÇ+9ÝLì î ªzk!ÑÅè™2'Z)áµó¨ÄíkøÝe4%$Á@t6ö´µªÞµy£eÀp?Œì‚Õ/.¶Ë3Ýúó=™Œ´À.ËÄ8wm‹Øß35èI+”1å¶ÉDØ)©;Ä'Þ.ˆãV‰*-aþowOºšÙ͘Y6f¢ÇêŽ d‹ÂâÙNÚ|Z«RKýp)‹ŸKè'&£ÜKßç“M ãø«ˆqIq ç5"† xI . LA`ßó‰9�Ÿ;­ÜÜ)öµË–%ËbTγê΄òLSXÈÙ:™XoõÙ‚ìnÌè½:Nƒž�3Újª:í¶ôøNêñÞíÛÇϧªØáüÓ Ä4·§ 3Ê�ˆkU€#L)¹¦0¿z‹Ã£°R $ˆ¥îôT#-X²ÿ‰r]D‚ÕÓìبnH ¬©’ô;©Çwz·ïp$#.FR`£/B’øõDR`ëòHóô‡ËQ½‡1x¤ÿd§ï´¯GSòn²ÿ™)øNÔ«OBûØ>ãú,ã´¶Ao<CF¾Æ0=5ª|ÕéÃd¸ÓxžiCL ñ<}”wIøøH£2_×½ŽŒˆøÙåDŠªÌ£§q jk×rì?„é´òh<‹ò“'T{óÀ¿o—ëPãL6·Épûp¦ZãWbÃù•΋ž¼Ïw¿ü×R‚A+9š(õ´°ö‰q“a Å=VOÇ —€:“£!š£Ó{‰DêÛR?§uç’>v뛢Î{mF}ÇóHøÒ{¢‚Ôߪ%©Ê×Ï_â¶¼èjˆx/*ÊCš2ˆŽQ¿¤÷R³ø‰÷%pµo’Ù±FU7ÍëmKœvë÷ÐQ­ç—ijÏ8WÖ�‡«xžÇñó:¬„*LÉÊ,MOË«•»kÍÃÙÓš i-¬Ï«÷ò/Fööçð/$ÆÕ؃ôb¢– QÉ_a·~ˆ2®8Ö¨€_\ U’«²‰ï ‡ª“Ùû"qxyº2‡ô<–TÛ=…If$‘? &>ö pp;Åþi<®ücžzõG‚‰ Ä}Ùû¹Ú“˜ÉÅô)\ ô)x®×V=*à==ñžGäeÒe¬òÉyõº£gø24çÕØû½ÄžlS° z¼®Îdê¸þ€ 7¤7þœè÷– QúâžbÖõžbÖ¼§Xû_rOñFÀ"cMØ5Å«ÐÁxKq€2ôYzK1ž¿|“¼@®(¾y™5ô¥m^=ñÏÛ¼:ÕÍæóXØŇÛûŧ6\þ‚â�Îû4›W&s¸Í«ˆ—‰Zу±x½­©›Í«¹´yõ:fóªéGÚ¼Úº¡÷²´>Ó{Yx¹©cú?aóÊ@ÎÞ/góê­ž6¯6®5eÕ†Û¼òÛ}±õx¿Rß ¡EåA¨ÿ(1{å9c¢†:Ô+–k4“VOj×'³´—sZPÈÖËÚËïþWÙ¿Š¼Œý« ¼¼ëGÛ¿Š¼ûWgIǸbûWg.¶µýÎþUÄÛ¿ÚþŸgÿê¤FB®¦ôßÔþÕ™&žÜ‡Õì_µq3ÏâñM¯ö¯Zù`ëÛ¿jêiÿjÛ•Ù¿¢HNTäW5ûWÛCö¯þtÉ ‹üöpûWѽؿB=}?ÆþÕòKØ¿:Eí_E¡ý+’Noö¯â®nÛ¿zà¿ßþUø=EÍþÕ)Ò^{±…ÿ’ý«ZøaûWZç¹ÈþUÓ%í_©·/gÿŠf~±ý«ûÐþ•©§ý«¤Óx1÷€å9ÉÕ®;õ£­aU/ þ 5¬)ÄÖ˜0kXuÔÖj´†§YÃê(§v°Ì.Í ÖÂËÄúI/±F¨±®R bõW bõQ bE¨±.\ ±N\¸´A¬‚oðXòbƒX)ßc|9ƒX¾#UÐQfk…jë ç{ˆ˜½ºÐ±’ÄÚN b-··u”’3…½Ärü€A¬kÐ V¤fëÂyÕ Ö÷çUƒXÇB±>Õ b½{9ƒXèL¦$”ä_v7ÔeëúÇq!¡ĺƒÄ¢ëróÇwK’rû°»gvá°g¼ç:b’fþuDüN2ú¦&"PóE:Þ»ôªvÝ«ùZ‰yšªãL”-»ˆ$,|šðóO»ÈÒ%ó¤]ä”#íœÓWnrxΛ¸kª?ü¦ë1¬ÓaÝY9‡XI±[ì\v›]߆réñ3ìDþÇÚ( èð nžÅn}Ç= Ã"±TÖFq¢Ý ßàSØ=Òò=|- ܰ‹!Ÿ:ݤ›HP! ú“SrŸ p„íø…ßH¡2Ú ±ŒŠwO6«úóÑ? o:Éum Ý3÷å/c¦ Æî‹CU·J5Í~îpVµëâ»ß½q³¨3›:sÀ¡úµâxxíX@â-7u­™‘ß%*}+2NÈû=Gu\ nå’—gõ¥Z{YO^ô\ÍÃäÅ æxŽFˆ£Ð8úÝÊë…¬.¡J­°³à?§»ýx5Úéþt•ã6€£Í"ÿÁ°§hØcÝ¡aKiXExX@RŠ›È>9¹‰¹ÜGúúz²9!ïv[u(êeÀS1¿9tQ¡Ánmv+Ý>+¦CMRù¦yTj ²íF²í–­Cq)ÐY"‡J2rjKѼ/G¯|MbomP4Aá_þ#´rÏY“Øßs6FŒ÷œ5ˆ±N´@5)ŒuÊÛˆ†/1$ð{|g�\ê'ÈqRØõþ®EVÄH¢`àD9î8Ë2ò~Â7?gñ~^„°ì&{"Å÷6›èÞxº÷:�*¶Á *ªÇ+g€^ß‚ÁíØÇ­Kqòãdvëí€QÞŽõc1ñ6ÞR_§—à  ‘‚—`G•¼Á²üÎL‡\™RñCS|÷j”Û÷Æ}ÒÍõË &º*É bµeà”x3"†ð¶ÁÄé?Åõàû¨×Ó¸Ìîû!±bhÂ<Š*Å!¥¸‘tuù”}âê«ñ¤‰ô*_\ÚØêßÝd´ 2Âk©0±îaU˜8ù§ªðp%i?ÍJB#YAïˆÈðB¯»þjTüu þ¾ÿ*œD‚¨ÁïÐà;CÁ, þ›¼©ÛÏað?=”Þ£ Љ:˜¸V[OÄ!ŒoLÀl—;ðï-o…§<äuŒlÝ%8¡¢îÂnŒ!Ç=‡r§RÐ@‡ÕÚâM˜Ë¤à;vª¢šr <Ùü$f¤ˆ¡AXe|gFÁ·=!¤f²R·åxbà'ê’]«8N‰Ál�dd°M½¸zÚ‹RÃû*? 7–“óùÊû5À,Èoæ @Ï;B3¨Q5«£r(tŸ*Ý)·àà‡ʦè룄:V±¯W7*/¡RmÁ:Ü©¤«'âQxHp¢ö�¬JëâÛù5Œ#XOd*jêÅÀ`‚•£ëÔ·À†ŒúÀßµýÛôùpNm °3ôç€dà›½ˆ^_ 4 uQ%„è3ëzGôot!ê”÷;å‡çtPJâ×è°t©Ó9‚ €Ý(²á~4ÚÃ?ýҧƒ•>ìõ¶ºè¼ªLI*F¨⃼/+7ðDTB¼V°ŽÅÁ.ÛDé— ðôŽ÷Y_8Þ-”–|ë#æQ$O åoÑhùw Ön:'fÖ¶TY<§u+ã<§õRÙ¸!Ú÷XÙø¸Þ¸Gñk˜RðÈ¥dqÛRåÓÌùe^|^öjzDãyC6°®{›HåD¼/°{δ/ ír{V5•Vû¾o$áð½Y¦RùDã�<fÈEÖ?îIr•&Íî9Û¾O‘”³OÀì¼Å×¥³¼Ç¹R£òkáh‚‰Å¥ÄÆ ¾½úÀÛdœ̤£H�^WܦÂÓTÕüSfa R¼ˆÅkƒÁ˜]iDÃþ$%ÿrQ>¥I·¿zš˜�xé”öYf„T°"™>’‘).FJŒd<I ½º¨i}É9U„�ˆ ­má~QJU¶¬íÕîb« ùî Œ�IJÄO~HRúü”0€{”DÜm;…oòO3¶‡îµâ^,f«ª™L·œö/Hý”[Ö†‹Ð¡d?àR‡ŠE¬S~5èž%LÀÇvù¤Ð ¼=GRÐÆŠC6|Çms&ºáÊy¹ëž@äûÊTtî~¹U×uÐCÔB’‰ÓÀë©xu ù})¹vL¬¹—Èé{Xû¾˜¨øeõU~ʳ'鋪|¡…¯î=ïÇׄäè¨ñ÷Bw€58nñ“ê¥ö`‹Ã ¬cf¼ô]f¼wêäzMA–gÊ·ÉŽÅ»N"å f“a¾§Ý˜§DÝ»B=‰òlF?ìÊþ8)XˆìÔKë2Uã$T»–°ð<Ÿ÷1\B°†×$k°îJiw¬€Jìr2¢Gô•‹ˆ¨¼g Í0‰Üc!’`@›»F¨’ô8Ý=ˆYÊUžv–•zÏ—¹†Ì¼ H«HäiÒ=»:>aT™r­H7È_‘7ÿy® •Ÿ‘nœ’U÷b*ò¸Yu¯§®·©&¯p2[PÐ0`Q>õƒ/zJ…`Õ ¡‰œTî.XW²d*]KeÞÀoÆRj²hÈ;£yFbj åÑPbA¾5R%ØŸŠßÃÓ‘S,·‘Fã¸kSoþvSŒ?TN'”S;{Qµº£ânU¶Àˆ{\çã·£À™Þã·Èþ@$Tòbªª‚ÊPñ@‚¿qïqFxªÂQÆNÜÇpÈmÊ­P“ÇYÄ*DÍ$òÄä΢zÀ™ZL¬g6*[ÇÒ Ýb»|VŠÏ*b‰kÞ“¬x3n�a>ÓýlOnÌF£DCa]§XäæR`ü‰FVúäD£NL¤0{ÃaÛ.‘JàTáÈ*Ý�!,1UGzw×}Ø šõJƒœûÿlJïNƒëþ hð@Q¯4 ÃyŽ|«Y`F*_?Ôû¬o|ˆhª%Ú®U8˜Qo«­¦‚çfÖëíOÀ²{i¬òׇ´éþõ&6ÝîãÙÀ`ï.Fñܶh`\F¢} ëù`3¤±êã&†©J¨ ˹€¾‰I·›«3'Šß_`ª­c¥>Èɰ g•µX¥¾Ù,Ê×¢^äc÷i¤»ÏWÔ¦ V³"ùÃf<UwÙä—0¬m;ªávñ~ÅÀ¯>‚ ÏaP•£!dÞ@E5T0«‰€»ŽÙÌLFõOúúÀp~spÚ§²Õ¾/ű¢ø¡?4*0o3«e¼/ûyÿ‘ÿ'1~%‘÷Fñ11Ü–öÞËÜ×òr/ßjÐý~%%¦Ígˆâ½ìf¼ Í{o5`Ü–OS*¹l1mX&î(˜]6ï­&2¦Ï kµ}2aJˆÏ¨÷U3ÎÐxßuÀ*àë t«9 1Ìܬ ×mÐú^=·‰7ñ¾ÜxrÙÛÅ{§š”on K‚9vù ÷f·MHËo$·—÷fƒ²÷{2r^¬µeûƒ¸`Yj&ŠT5ÀÁEAM 0jR&ƾ;Öx5»;ô([ÜQúò¦ŽP‘ÒñxŠÅmúš«%—›€•K{ K3úÔWdW`"Õ§3ïS›Ãáó‚8?ô'·Ú ÛýgÊw?G™·Ã0i¥äÀôŸ£üA@ï”?ä×LÍL]5YÇÕD’ Z•Ýôœ›&ùÆÏÃ’|éçÝ’üíå“d¹šWšäR5ɱøq¯šd*~,ìžä ø”ÏçÀ¢ŸïÐúà„ÍT-®Ä*M%s54æ}üf¼¥\ÕŽ«]k£IO j"ð„+o²ûJSS=7›‚Uýí2·ÆÆÀúFŽä6bW³{#}¸m9€Ô!å•È ¢’£íh³e§¢/orç9¢_¨Ñ]Þ±XòÅA".oºÝ©CHPQFg1ª¤0TuB>Õ¨¶Œ#üÊ%ÎósQ2¥S3Îû¶Ø×iÝ)Æee^'¥ʧôÔ;îÙ‘ “5P¾_Ù(ªÚÐQêDNd9¸€¤>ëb#T[Ò_¸éʵƒÖ\ÍÓê ž.È^dxÏive:oýzyšì0È;!UàoaM°ù3"´Ž›ÁÄù‹ÔˆÖR_{Áj¢ÑÚ陕YΊWËă ÂÕŒW¥OB†ánÞšÕšàË%ðE–ö<¹EåJ(°>G=nTU7Y´.²ÝLlÉGQ®užßKmàÔ\ û~]ë¢,\í'-‰—Oó08TTRÎo$Ê ñ5ËõC¤”ÚhT ïÔ‡É$ kÂU:*'´¼”tR¸Rš,…åýÔ851`Üᦶ¶.©§fõ1*>fä?g4 <VÎNœò¥wr'áq_f‹™5ÁïOò6»¸Ú*²¡çÑwO]¹¡8ìBºÓƒ* ›~ýÝør˜„ñ4¬FR7^ú‘0…„ §aóµ°“wáMÖvE€µCM4�¤—H!í»üh¹CUe€XR›W&B½ãÕ܉›D‡ð½À¸ Šä$:‹ÓüžSq܃3¨Ü1n Šsžº^“# ´4· $ŒOÁ·HFÜEöîr­N÷e_¨^nÈZ Fû²ƒM¼"‚»q§Ð)}ßA•¯1DʻьFƒÝŒ©b³VÈ$L\|7žÞ’4,¤«Ýµ¤¶p 0/¤·°>òf‡|Ÿzsᑸ?ˆâÄRu7Ý�d3Úa‡S>Ê{'º¼“ßRÖ‘ö¥à»9]=¼|í?$>±T8Ùv½ŸåšT"ã(?–Jd!eÊCyïFÛzâ! AŸÍÔ$¼NÚ9ñþÉ+ëí·L¶ëw«&á_†•€'!í¨|RþhkJ¬»– ÃKê±Õ¼µ´%ÖÝGþžO[bñËä6ñúxëa÷‡ðÃՔ♘þ£¬Ì)|–‘Òì§Œêƒ-w6vvãy²•7°Ù±ãjðdL¨›Ð¿Z"Eý&àK¼®Rm¥Þ´]×àâ˜h_¥Ó,Îð™ä^ 6œt;•6sbW]KÊŸq ؆ñ>¼@Çœù0ßÍSþþ»±,W;Tµ43›N±Óg|…l‰²)7c1*@1I^s”YAnâ6TóÂæš›…ÀÍ,¶Û©V…9xÂ×ã¦kÖ$�xRȲ[[©RS;¬Á�$׌ñ„‚}¥ž" £å„?A}# ³m§âkS;ªýÃD‹˜›D2ÃRIC µ6J‰@°î[‹A}³õkw$É"P7ñu’Ws»ÙÓ4ÃÍ]J»s´ön÷Ñ?]€½ªuýM¾³kXØÊhHS35$!§¯vDX`j5…–ƒ³SQL{•b&1îb^Þ'L|•Ä[’d÷ÙLÖt·™×ï³{êMÖµÄß} ¿>žT! Å; j…¬MÅ%f™4Å�UÂ{ ©„hX3ëÛ)ñyÎv!˜8ˆc'žAC›Z|‡†:6 Zsr|ðÑjF«tøšU¡¶xÍ6nõ¢Ø¹K9D·}x²×VÛRUi»&¼ÊûˆÁ¼ÑÖXb¦·ZüA#1i+ çºir³Ý39)(fy¦4ž‡u¹tmm‹˜äé`ÅX˜{¥AÄìu öµïÑ1¾v‚Øq &æ‘:€DìžX=ª<í¯8îÝR¼ë~r}SÝ*QEú¨®,õN`¢2‰Â(}CÑ õ±Ö¸Ò@ýZ{LŒqÕ1ˆz¸œ›]6øOfÀ<^ÍQJç2šRó²^¯ ~X¥êd {“½Üõ@õNùˆÃÎèôŽˆ mNM—ÅÏéq“gyF,#eøfF)®ux”D¾aäÌPMöþõA4|`Ø™ KM%Z=(š§ËÎÏq1šl&®šÏ¨›zañf‚]µzõJ(°2úR€TÌf{pxÚ2ñƒ¶büŇ!N=×3å®/T^ ÍKtÁDýD'Âác•'o$S÷CÔ'ÈÕ¼Z[â#‘ƒª,&®¿*òbá§D}Iî—Ô‘ Ñ·Qûˆ+<ÿ>N|î¼é®Ï YÕ§‡R=µÐKÍÌm™Ó_QdÄôˆØ¹>˜xf>UÏèB–,¿Ë¸â8ÕC8N˜m²x'öÏæ‘=•âÆ¬¹aǪyȄñ0×î•æÛ½+€‹5º>@rAŸµ% r¢†ƒm:2íåÈï:½9fÁkƒÙìï\Xð½+øÊ‚‚õ4ékã%ë7ÉPX„‡FáDq>™ábmòÛ‚wô>XÀ#æõAÛ#‰þ>Þ¼‰/Gâ‰ÑB³ çTÖÖ•x¹Å: ÞÌÊÌ£úÖVékOK—ýi~>m'oõ¯ˆ|vVðæT`ýÜìµAÌw`ØFÅÖC\Íl*‰~›«­&ª0~‡µ$Ÿ¬_H0æê9Ïpò׸QùÈ{aîù¦Î·ƒ‰ ·¡†›S‚ÚÄdœy¶vªæÇømBãâß@µF+ï¤ê'OÚ=§tnçtŸó&³ÃúæÒ1vÏÙ­þ¥z"­ßÞ©˜Ä&>œj!ÙÏf»"÷îÅËœÊäÛBÊ#\Ëì2 Tƒ„qÊ63Ùét…0:—ÎC£q’ç2DH g]9Çd“[‰jå/æQÞ« “·vr5CIšsQ°ïÓèJ«\‹£,o­§ëL€X‡’ñŸ2ó¶*š/Pê/!½Ž÷I¤Î•yšFè„·Bo¼àº‹HÐ:Q%ÌZ‹)»ô¤øý”)ó‚ÔŠôa‡U£@£‘ èèyX, ¡ªµÈj'Z¥€x- ã%úÞöá,‰»V¿"K N+Õ‹%ï &><ŠÛi÷Mï&Ž•ŒJù\dlfª_ùð%7u¼IX‚“Nù Áz•¤A3ªH¼’I~›WmP‚AX/W`U6g JGvp= È+ª°qªžŒýå}§/§Bn“[¡ ´4ÊÛêê QlÁj8åzÔC¶ Tàø¹Ø4›¡ñë)R>ÛܼӦ¹r‘:çtûÒÛQ®@ÓÀ5Þî»CƒÑ]mÙî9 ÝFå†L¬˜#¤bðºípµbÌÊósB €»j%Ç€b5†Œ÷žœ?U<{î8,4¨XCeáùHT?IѾc,Rm†Úzˆ Œ¡•0@5»F‡xùM6°(­ã >V@@{F½ÓÚÎ §´º©ž50”ÆVóÂØ¨ïŒöˆo°2O¡XÍ·xÈ»Où-ÄpZ üç9Ñ„²5ëfÓ³œœ${Æ›Êé\,"Œ‡2Œ‡2Œ‡¾"‹&+°°±›Ýor`€WVÈ:5×!£ *¥¹’«IÝL0z:$:œ‹ÈÂ]v™š<!\™\£Ú}ØC>ÕO4¹ý¹c>ó ±tÖs7÷Ç*袥2‰^µÞKoÓ„‹ÌàØŒ÷Bß%`}W@™ŒkÞÑÆv“ ÇÙuª°HÆû9r§ ŸqʰœÁ†4*ý¹÷P|Zƒ‰Ûf!ó*š¼SÌ/pC0QÈÅÀ8V§àûoÜ·, ÍoW3‚¶C®fyiåjQǦ,[¿•"QêT®0œ\Xõ6?Gnt€¶ksIÑ &>}+6îvÁz„ŽFü¤IÛ7«mv𙽯ÙÔPËž  4Ñ!(ŽŒ7wO ¹évëY*Gr[n¥C.0VðŽÄ!7 GKÐ)“Éa¶íÖ}½"ÞŽÍ› M‚öݰê(ü\B§D§œ“' }`hË@¾U·âaI®…‘dÀ­d‡ºŽTxp³g¡œ’ SRBw:ámX÷õD;ô˜úÀ\k“˜ U„(À Ñ5"(ˆ¨)È!'î\ÙòTTyDd¯:jwä’QÎz»E«²&‡5è&ìßåN�ü74ޝÈÞ º÷¼PÈâq#÷”²#Ÿô jxAíYØ2‰j*iJ_z†$  h÷T¹Â˜ó¤á÷<P7íD²i—wwè®a;ÔBÈÆ¼ì0)[f‰Uzåeú’¤¼@_,Ê3ôe¤ò8}IW|ä%F©¥YÊrúbW*éK®r/}™¯äÓ—<e>})VfÒ—Le}©P¦Ð—*e<y!šç•‘ô«[L߱؊™¾#9}Gº)§fw"ƒ}Œ¾+JíôÇå-úŽcŒÒLß‘LÊëô×·ÊKômã)ÏÓw\z)OÐw<—TÖÒw\*ÕôwŠHß‘ŸSŠé;.•ô\¼Í¥ï¸µ¡ô¯v*™3º.䎚Ñu“ÖBßñ.®’@ßqëN1Í]ÈUÎå„.â*9ÁÞz<þÏîªç(»5îÚÏõÿ“ÓMà“B}6S3ø$RŸß‡R"m;uX›IÀïŽÀ4ª|ŽRŸû¨ÏHði§>·SIâÞ¡>7SŸLðÙK}&PŸ,ði >è|6SŸø°â©.—‚‚£8\íwÊ»Oø#ÅAkØì5 Q“¤yTè6ûI?ÞÂþ±Þ±•aÉßê[| Q®ÕŸ9×ê3§¸¥{`ý|‹œ�dj¶;¶/¿•Ný»§rõ› ŽÚFqW€³ª·{Ñ´¹ËåZ_s@¼¦æ„8Ìî?j†÷Að>@}çà=†¾jNH2ÄÐ"_‡ç”É}5!Áµ~ ;É-‚8‚Íà'}™#7;üz)%x ކÇÒpék;$=¨^@:ñFÜ=°Äv™‡ª¢M’^Íøj–¦Àk¾&Ic pG°EzÓáiOQ3ŸÒâ"e‡à/!82àÁÏÐ#ÇÆÛ¹f;דÍ}o÷¿®ò!Ò­¤®JÀ²¦Ðê‚‚@Ý…g@Ý¢ÕÙ'=*jXEí$¸ySW…-‰Çú‚jS«Ëˆµ‡QýÇ×UFëÍž­*¦˜ëT‡s´+ÿIެ('îÄ;ÐÇECŽW‚"Ÿ®S––ô*ë´¶X5ÎÚÑÁ¢ò³ÏÄXÞsN·|ü¦®ÚɯƒwÒ3‚ß],¯É< $²}(î@$¹•#rEàèë ¢Æm2ÀTnB½¬5i¨Ap›L…ÁQ*]2zöé(¦.EÐpÈž«›MŽKαÔ< ¢Á­»‰â&DAvÑÄ¿ŽÛÌjk•Wm>ˆG`ÿ:ÞpB¥8çÌRBK}ÊP`q‹júräõYdÃÏÓäO~‹PD×øÞŸ¨7ˆQYܶ7=éüÉz¼Æ+ŽòÙžAÏÉ(ùÐañø[xëÞÊþž}&ÞzÚ}µd°î^2سπҺíÁ=Y'šLR2|98ÛGÀ2ßÎs=§?­’ágo 30iü²Ò$¹ÉÓ®M«qñV½H&]c=¿d˜òÆ=¸©„GÞ'Ѫ¸¬5M©º‡O~¬?OªÚá]ˆ�:Qwòã©Ý0óÄ»W…šDÈ|ñ2E¯ ›ã~WF½ m‚5µÈòþ‰ÆoO I`»/îÙPÅsF‹r«v˜Ü®m‘F(g\½6>ÎEw³}²ß.ÇÉ_‘]ØÄ~v"z‚¹öÕÇ^0¢»>‚†'FÓ¨²ò`P‘öS¡Ïè×Hbnø<a¼ÀtâÝ(‰ÚFörnœF¹™tÔC¦í"2m+oÔ´Ce‘s”£IB«jŸkH„Ñ&î·ÑT€Û¯¯¡ä…RXFÄÃõíœî¬=€Å:oþᓨ*w‚¬•U³Y)ÚzZr"+(/8=ŒC¹Ž�½:p¤óD€\Ž&ÆÜ¬‰€­40ß{O*?Æs^ÿi#žà `ÀIL(v7g7 É&T: ‹¼WЍ¼™N# éü§v-B:æ¨4œªYnA‘ªY.£(\,®…Ñ4ËáI’uÈêxŽ´†âÛ®½Ç5àÒ,'|΄©–Ó]oö¦ë훊o>àÞ(±ž¤@uËÅ€^¦‘Oú(O†’ êä|"x­²¡d±¬×,Ö„‰Á¡ <ªLÅÌÕ{ì|’ø ñjx1‰ƒï0+}lÁàꂤ“g‰pˆW:ã¼SMš®¸nÇÑyØ Ž†$~îœ ßÝ,¿ú,2iKÉih–z £Ï²ŽgÈ^Ù¹ô_@y²}Ê;o¨rb(U÷*ÌBi1ÜÊlªIE¢3‚ï6Ð}먯—øÊ»É0à%Z”éõh™O ,=o hiÇ|”‘ßÿ-1ÙÕ?S ºÂÅ~¹M�8lç%8}?òºÎ䎨²2sRͳçu»Âmâ£3Þç·§ *§ˆB _%ë­I­&¸E¬þ%a™×­þ*xùHå6±þv3"»:ÈÖƒÒhQ'.¨±H}ìú&èoúå_wd¢’3\Xá k+‘ n$«ÞëßS{° { ‹Ê¼¯“ÉÈNé+ì#¸àçeÿ“ü°CúAnã‡íâýgŒi 9ßi‡³eç«{î]ªÛ™—÷ðòΜ‚†ƒí'Û²k@%9Ûgˆ°?ˆ"ˆÅ„ ”ò>> K-ézH:­÷Ÿ2Êï¤5ç4gËÂZËÁO}±fk³¦“J½ ÿCHkÊö:×ZmvëÁå×À’S¼Z³�®‚/ –þõn£`m¢À^°§½³ì]ŠØÁöì¡Â«Þ—Íâ%{Ïý ÜàÌ£YƉú9þv2çh–²NANÔ¿ õÐf ÂÄzjEÿ5à” {!¶õ ·n!|¥ÑË‚dü´ÉÚe˜‰-tyH72í&¹áIÜÀsÊaTÅ¢Ÿ1ÊÍò)þà!­?y0[žSc‘[冃GaÝËÇìç‡5zéb3Û AN}Z Çoþà'$ë^ =ö•‘x1—Á[78¶ëìimkór“¬óQ@˜}vÏQƒÝºgÅh–øùýEXe&•}–Tö·€’VÙŸpž§èÞ-©ìÆQÙeje ò¾‹ëk»-+Š!ïÉö%˜¬mn#oÝ_©“÷‘Ÿó˜Vã©Ö=¢aQAuX}#¤VÝPÔBÕ=õª»Èã£qØ!(“VWÁz¨ž'/Q?H–ƒŸ“ z›T4­ðÚù5´G«!¨z+êGTN£üqÇ=¸÷d\ùÃP´¬S\ï †WˆâvÄ=­´OnKiª%EnF¤±­"Îòa(  %K;¥átèË­€½WÛÉXÛ­¡Æå(8€m\v¹"ßl·žäY¯ð:Aþu@þ‚ÝÓÈŸ]qDŽÛþ6&iÜ Žü‘ ÷wüšäaÜú6Zð„y6…65A¿‹’I}Ö(ȱ78Å0z¯…¡¢L(~ „ócÎkç}üÁOžÓçƒÿ4Î(È@¶µÒ¦ô$ù5­<´qªgPýpäH…˜zÙøý[�ƒÂܧ²¤X˜Ro¶Á« ˜<§,’^ðꨈv×=`2þÍSßúÿs@/ƒB®6 8Hç€~ÿ¯žÆøvÈÏÆ9àÆýÿ æ€GÛºæ€ûÛþWÌ[ÉßÞÚchmý_0\Ýz™9 ªõ_˜œòï=4_½D3¾°5$˜¹[¨ü|êHr¾*ƒ`ßtÇ»S>“ã›ý„ *v²|ÃÆ r�U ò·r�ž=Nëi„²v^we BÊž‹ ¶Ž)&<:̯a&[ÄØÉQ¢ir´h˜'sôþ€Â¯1ƒë9mäÖ ×źD¡ÆPV”ˆ“iÍÔúyù‡æ³_¬Í\-Šk:åf›Ü!3ì³ÒØ@¤Ì§} ‰WŸI¿ ¤TŸ™¸*ÅL®äª¦óúÓ|]ßý~�¡(�ÔŠªÏ¤‰×WŸ-^W}f*÷ÈLȲúŒmeßê3Ü#<ù*âÖáæ<ÑKcä¤ù_‹Á±Ä XóK0(w,¡ìâ®k åÐóz5¿Im+>pÈ(òUG5‚²“Ì\푵%Öâ|Ù­“MKû‘Ý*™u´\@úÝcJæ×Œ! Ž?�”‡« Óüx¹¥mÜ‚*` G’„ºšeAL «I!/£©j$䦎O5ĸ‡ ‚cÂÕ¸idq r�âQS…B•¡ IB²E³KHŒÜH‡€ÅFÒà:&` GÓIeVŸ‰â> A”,gQ…ˆÜÂ{:"¸lÁZÝø#†I¼ç´Ž«EÝZŸNÅ#®æeÌß7Ÿ ×KUûKÜÄ€¶|-’t†æŽÒó„F6ùë@D¨aˆßW[}ÆÄ=<Å­w¨~¢©ÇÑñº"¹�ß±˜Â@ž&›–Æ| _épWž¨f„¼yNG‹‹;&¡FM¹1P?€G ‘ÚE+Žÿ[:âu6­Ðr;·­í¶¯>Ÿ›À0«â(¾ü¤ƒ+> ôãVð p�Ë­{‘„u,¥™¨5⬮¦Ø€{fIK,kjR‘.“Ë£y¿Ž~å¤íßž„dÕm=Ë=t½xñ¸?ÅëƒàŸ™Åop«:Ï®] ;¤4�¤Ž_¡;ù¡þKOmõ/WðM—"Ó^$‚¿´ܺíz|) rkŸ#*Ý'|¶·'¿ÌC,ÑËŠ8ð¼~/Üü½!™LɈ-‡@̈í8€ò¯![Ëo%GÐ;힦9þózO{$îeyKS3Q+÷Õ˜Åü6l†v_l–Ý—ðˆK˜8Î=Â.ëT]VÎ6]ú^Þ„[4v¿c Jb¸¹M·D˸Qs¯ÀmêpñÃP+}®KÈPàÝïs\¶a_ >ƒ Þç»x«Ÿó<¦Ã;KÖγ;è–óÛñB“µ{ø~ò½ý&ú½4 âw,Apk£4µ¹lúFnKg |÷‡’JføÍ“¢±ü÷vÌ& {¤$@9 2jâÉ0q ø×m¤»ÈG°hsß<ƒ`=8ÕgHEý2ãÍ+çòò¹>16Ïé»ß4ìÏmаq›8A~_àþÚ,óç²}£LV™§d?ò–œ¬ï‹z;mÝŽwxštÏW:!í#ðÇ3íñ«^ÒÞAÝj‚þß*YQÅ îgmÖÕÓûVüë(ç¬+m(l•âÎÄäæðò~›þ€ÿhŠMßÌ„¿#ÐeÛÓ ÄìÇD?#Uš)®ò£ß*7‚,+Å `®“‡¹MmICV˜š <÷GjÒ üþL´=…ßð’"Äæ÷bZ€ÇCÌç¥fœøpŠ„<ðÔÅÚ(öƒ„ý®˜&4G”KJ0ºˆ]ÔÿDÛi…PÏMKÍüêO‚A–áWŸfªYFZ³°óÐÖÉ[wI<x̓|fáÇlPwÀ×<šÂE1bêX¡¾.^߈¹Þˆ„7x™G¨àè×,·h“‡N»�4%å0Lr–˳‹áÂòP­‘ ¥øï…gQ ÙÅŸüpPý°]F¦:¸ ¼Š±OÁSýªö54Ê‚ r˃/’—§º #xù  øÇ³ò’o`¬¼Ç%$7ìÈâ…!0È p]ï[ =ʲ»€Í™ìâ½YПÀ1ó^»Ž÷VE D<ô(¦yÿ(zws5‘æn&\TÞÇ|IÞçÅWºñ>ܦe°þŒ§uåßd‡ÛÔ Ývß4õ‰½ðó$þü žÂŸ§ñç×øóüy6àÏoñçÙ³ðóÜMj•úÊ‚°,H±e|æv`"o”‹Äv¢tmº/7Î…ºøu³u}ÝT 5×µ¡]c”еYßâj?cÈ@±š¦Ò¡®† uw"´PW`ì½ý<Wû:N„¤µ ÞAÀIFˆ.š¡}r•;Êé3ŽvBŸH†°¢––>�#M‡@Æc´)è• ^ǧãp8ݰ…°rϽZËvˆ ]œ1ô¬<~K0Hn±ÕMÕÆA¼?w‹w_oñî‚xMSõ6ùƒŽÛ¢ÈEô]À¡`o[âïHâåzl¤n¿brúâ>ߢM˜‹­¤ÞœÃ0¾»ÃÓ‘çò.O½ß…¢E çöm8ŒÞw 'Û·V{üõcÛqõŸ6 ÜvÔ°3ž?ùµfN!} ác;Q¿å{˜ ø‹žZ ¡…Wð¨ÀÃ?V78g8 wŒÊLy43Ml<ó…Ã7Ê‹góaÒw¦ÕúÖª›¬lƒ™ãEòE&5� I«“HM꣤1æDc½ŸßއÛtÚ!¬C>peœráÒŠ™/ð¥Š_¬ i0ªuçù3TZ9Ïs:2îã~Þà³ui4F«êxšî óI *{p–@Dü˜yh1„®÷w¬¢°{¥ÁP’pÐXab‘AŠ€_F|—¿ÃY¿—ÏC#önä“v˜K`ˆ¿àô-†J,Œ­° ;›qœGÓWð›*ÈoÃ|ÂËYçèbÜ�kq½M>È{sa>y[4¸ll’-í[‡gL'Çu‚õûU~bºŸvÖá©×ñúo!?:cÍåiÓI31HF¾½+³ét2Æ+À°M›MêåV˜L|†› X­t.h蟅XÏÅØSóVí@¿U.Y–G†|m.Ù-ÔÀà(Cÿ>Mæ’lOÙÿÌ$1§ÈDâw¤„**$©I{Iʼõ1ÌK| ÉóêtþȣÑ6k´ÓYã™5æ@ø"OKÐê—²á•TÑ"ø¸†²ð…=|—:]Ü‹ÕÕ5]Ü«M.B-íkÑ„I"Ý‹t ­¹¼éƒøŒãå·lò(6¦/¢~:u}ªMU˜^VàNDbp fWq ‘A"‰„z•Kˆi&šÍï4»N~L2ë™Í1”/åÄpd@t£WSsÁg| *”.¶d˜ì°Ã+fÂd=x‚4]ýþ5$¨M>‡c�·i-9À#Ý F¯]ÃÞ$= ¶N×°¤cÃö5é\֜焣z“ó¬ õªuÝ;ÔCÿD‡ÊÓ:ÔHU}r8x‚0ñùT”‘¢ñ hñiàÇù,„–šñ¾Mþ˜uì[0ë…†òÈ]B_)ö¯�v.nSA>ˆ¬Zö9ä:Õí+?é\‚<­¡s ì@[ÚT® ,4t®#«è\v[Z�úšBáõgmÖcdd]åABcEwõ°mÝ:X{¨ƒ€®E:ØPôû°ƒù»:¦•ÛÕÁ�¤—fÓŸ¥ ×CêÞÁè´¡6‡v0HÙf=G:XCX›ê`¤ùðo¨½l'ö²Oi/;CzÙì®^Ƈ÷²a½ÌÚD:Yéå:™ÖÉè Emu&ë#hßÐÊ{6ü€“ô!àÑp£1¬Ñƒ½)ÔàLçÒ]‡ Ç/*¸¼z¤¤S¼ê }'oýnU˜ªJSÓ7ãF˜ÏÇ þŽgNâªzNjÕæDl’¤©"S[eíôU}ËÍìäýGNG/Ü„; Ír0ãXÍJ Ô°S³þ€#í¬ô)ôRÄ]¤Š,!…Vœw¹ dåõ™—ýûBÝAEB5ÚÒIÄì€Uwì+cí¾ ¼õ˜S>Ä=‚Ò˜3]–ù¨h¹2!ó&HÚ;Í„¨©xéPÌ`d–›Á ÀC›ƒaèÀ5…àK5`ý»I{åÞggQl3.qöTê½díl‰{`Ò¶ð¾ñx™f¬ÎJôôk©ZsyÇT_B*æ”ñ™€³`L½¼V+QŸÀRÅøi¡®üJ’PW˜ú€!¯ÓåèB!WF¢w$‘m¥ƒüüNž°¼,ÇWOš(ÅòMºtðrZU~‰?‘ºò 3P¸î _}Tïà¶5u³õt’uø„qÏ“‡Rî™\™¼˜{†,Ò%“šü7øó§°–Ò™®6–%߃]w“TAþÄŽòï)¹³`¥0;É^Ðy¢áA)Y)Î ŒLO²‹W¸‡v¢!KŒSnÉÖD®íÁ ¬˜ì}Àt¢ÁÌÕPù–©fϧì^½wv’<5 Â<í_yšuÞ©IDé8ézeép�ªO·[‹}”ú•_RŒ¼Oùóº`fEbê'ÞONM2‘ý TÇ(§ÙåÙæ´ûÌv_¶2‘§¦zõ+ïTÓÊdAÞ ñ?÷Á—Û<»-€BÇdTîs!(ó\¸€úòöÙÑpf"wŠf‰PÛˆ´ûâü Ê›,‌ò>bõø\ •WâjŽ‘Û½SÍÐã,Áo d¼¼†ÖÆw’nhW\${äò·á6·i‡ª¤´TÕOJu3xŽÙí]êlèùdŸ•E,N A,ʶO¸9Pã¶ÍDèË®ê&•ãþ¼Y½K‹þĈ`¢þjrÉníEG‡2]zMç5Þú»Ð‘DåO6H·Ìh=uLüýP"TeD£hj‚å)õšòHl¿¹JeÛ›5wv·å›:Åa™ÊóÚ t§üwlÂ6yhFк OzW0äž|TOÛ½ MÖÝ\M?¢TznR0ñi Þ³ãjPèdz"‰åj‰iNãå7ƒ‰+Ôàj5Xº—\ä=ç‚bÌõAî‘?b”‚Ö®;vk3÷ðÏIBΨÐ=‹ÒÞîYœÔwÝëÂëC“ÕëC3°yÞŸ úªØÀã6V²ê%¶Ö°;l/¤à.Þ¬D-Îõ7¢øu–ÐÍ£>»¼‚—ˆž\Èâ]g±D²/gº÷m&È¡{òäâ‘#›K5(s–c=)ZåZbJ u›âO:V²€·ËaœÄ{HÄJJÙ²‘ÁÕ ¢+‡|¢E¢„‰+iTWÔ°häfžziÜ,Äo³"Û’Ê_ˆÄ{§<³/™�E›4éoÓMb×ïëpWú$J âeß³—ÅKQ«'"e$£²›¹Ï‘Ž÷9Rã¶"{Ø! ÙKÈ8�Ý5ºã§ï„w¨³”^ú•w>jõfÞc†’gÀ”ÿ0Ê wlŸý¯ö�W#û9TêP/Èw²06ˆSM6ýû°6”cä©à:£äý‚/Ö¡ÈDÁ\)ÄàaJ™%Ês˜—wÓÉ "Ùp‚{W†¥TLL7ÑõÞ~‚w¶AðÞ \_ƒÊõñ20µzTMЇÒ-<† b=’pª¡¶EŒÄóKð�;$MÅßaAXÀIP°¶ ìÛ‚wº-G ì}&à-`¾ ……h³M8ÁÉõ0Á¬ª¤Äì‡Ñ÷Î6Ù€†uÕð¸›Ê?j ìèÛ^½ R„ •'ˆù{"^ýqOS«m@˜`ÄbA9åÙïÿÔŒëhxüŸ˜cÚ ¯!s?ÕäÒŸRxclPòýÜ–ç¾Ažnõr°²ß«ÇyùhJLR÷5œ“ O#žÖ×ó1õÀÒrNån—÷h¤Ì8HN¦ë2üª7®aø oÈ0Áá× ÂßQð{)yxh5f§7!U™rðJãœÔ$gZ[—}°È)n&=î`eð¤LgŽï)ƾI¾R§S†¸D“”g‡uHQÑQù¨gJÞÅ£vÙ¨½HxÁe±W·°§Œ>ýËRÛ1Ä_¯ÅO×â§ÓøDlõrñiþ¹ZüL-~&ŸùCù§Û}÷¦{¦p$Ø]µxÉ›¤MßI2äí¢”Âã·nVHÕH¥¤öŽ@þë7_¦ü?€?)ÿÆ7_aü¶MWýÆÏý—ãÓögÖâ_ªýüˆö×¶ñ_ÇŸ´ŸuZÿZûɽ‚üIù¯0~Û߯°þ¯0~î¿_­ÿ¿_†ü—­¿íÕ+‹¿^‹?R‹?’Æùãâç^a|óâßö·+,ÿß®°üW˜¿Y‹oÑâ[h|Ë,ÿ+Wý+W†î+WF?óâßöò–ÿ ãç^a|óÆo{é2ãÏÌtü' �ÿ‘Æ$…ø¤Ëð¤üZþ½ð?!PXsä)÷õ'K]ÍßBüY»¼_6)ß.¦7^6î ÌhYr¶õÜc~þQˆ.¼QÙ‰ÑDîDÑOÓÒAÐ5ÕQÛEö‚püv9qÅ\œ$ìVRû‡P¸˜Ö4¡ß^"!9.í×xìsŽÐª[îÄs©­Gº]áÄx«³{¢H.¤ÖèMQ‹¦7øb$hBú‹”ò %îYQÁó…KÕ‡-ãýÕ™hÏ‚çk¤ê°-Ýk2KùmB·˜å\åþ£L.Ò•Ìÿþ“ޏõ/P·MuÛU÷˜êêþHÝÕ©º“T×®º T·Lu—¨îCªû¸ê>¯º¯ªnƒêîSÝ÷T÷ÕþêZTwŒêf©îÕ­PÝGT÷YÕݨºmªû™êžQ]“J‡þª›ªºéª›¥ºóT·Xu—¨nênPÝ—4ºªî>ÕýLu/hù½¨æ£º·¨n¡êŠª»FuŸWݪ»CußSݨî Õ5ý™ºIª{êÞ ºYªëTÝ»T÷!Õ}Lu_PÝ-ªÛ¦ºíªÛ©ºQË¥º#T÷Õµ«îª[ªºUªûêj^èþÝÛß‹"[£ÆÓ_PW8CÝžŹ:â¿^ºtxÏ?¶‡{±¿±ÿˆnþºÙ‹¿©›¿!äFõâÝÍßrczñíÅ¿O/éëz×wó/*(q¡{÷Ý©|iiyA~n~ajXx±×.Rºã”…2~Yê÷õŸVŠ<5?wQ¥T*·âRxW©ø2…Ÿ¯~Ÿ ÿ;ÿ›ðÿgÿ´Ú1«øW.«‹ß]Ræ*'þˆ?²-íÒ¿†¿§S¿8”2ý©¦ŸµäÊÒÏUã?ÿ�í§I{»»]ù•1S ÿ®¥ßeRiéµ]áø"˜}¡3l6›ÿ&¤3¹$‚%3w~¤‰¹1=ý¶\è› ©~S^qüAƒ¨¨‚?@@Ï5™LXý&&RëÕ z‚%‘ºSËݶª1×]^PTY ôa*™¦œq3Eðo SHp…ö'•‰%‹‹,Enw¹ÛÂ0qjwžãœ9{6õ%^³3nûd„™9¼cF—ϬÒ3ÆÇEóe–üŠŠÒ’‚|±¤¼ÌRœ_iYœ_XdÉ@…hË-¥åù…±¸È2Õ¢å_Z²Èï^f))+(w»‹ ÄÒec¢sK‹ò+‹,åeb~Hb„¥~M¥¥Rª¨(wCHQþb‹ °¸Ü]dÁ¦à^L`ÆhåAü2â¢G[ø.4$H;g¶Ã �Š.wùbÈ¢¤Ò’_YY´xQé2K¡ä.)»ÇRI-)¢P%e%bI~iÉý$ùè9_RVˆ(ALË"éø¶,+—ÜḎ±8D €..¯¡¬÷AêX: XÊ]–‚üÒRÌ (…8.(_\QRZThy}A©ûZ‹K*+ 4%xæw!UV)º¥ ÿI PZš“_BËŽå‡å.+-EeåÒ=Å–ÊŠü‚"B.pJ»,œ^c{Ð+Tö"Zy³æPz‹Å€tyYAјOð|Ò1L qìʤPÅ\&†Keù‹�ÕîhåWДîÆË”siIyºëŠwC¯ñ*ÅÂËÄ›€ñ*$ À’·(å—vUÖ&D¸ñ½¦wyYôÒëXàXD,b(ý±»—»¼¢¨ŒÔw9|-))(Š M¾¯Ò©¨ªú´¤NxŸÕànì·š`‰Xì.‚^ -â¾ð8ß;}ÔH…ùb~\w> Ž6…žƒ»è§RQ%fŠ-h–Úù¡pb‘†p ƒ¥Û�v©LªD¢.ÍÿO  <é{-OQÔdyÙâ¢2Q-Âgö Ÿï¾GBàÊPéžô7 r"vèŠò’21„&ƒÃmÝ “SRà.¯,w‰–y%¤´SÓÒBtqÒAá¢z̘1Ìä wù=n(bY>�He÷••/-û ä«F±aÕ ŽÎ¥`“,=æµ¢´ôtœq}Ρ oCØ÷ã=¾}ðÎ>Òã{]ïõ=¾ÖãûÑß?ïñ=ªù> o¹TVh¹;Ÿ°Fœ¤ h=°NÀ‘˲$ß]Bº å…–EE.Zçáݵ[z‹‹ U,ÓÒÁЩù•dl«\ yA‹^V­,_ªÄª+-¯¬Ä±Ûw·tHtXBêß +)¿P¥ºyª›®ºfÕKk.TÕS‹‹ ]R{Ñq=ñíA†¥Ð‘B¤ Ð ÃÓ±¼‹"Ô*"ñ ^/ÎçR «<…R*?D+$ %vm$d—ßjc,‡‹Îª4wz +¿tÎ –Êâr©F üÊûHŸ®„yúV¹‹|‘,—ÂФõwwy‰E%"Æä§a·­Ê_\QZ4Éb‰Ž‹*(Î‡Š±L±Œ,±Œ°¤WM›v­5:n*ÌQ÷`’téL®á/)-%]3ßå*R‡—ŸBo,—i¸Ð™S(‡ _Œ$錡tš KòK%‚»mv.).¦ˆ8¹aº¯Ì_‚äÁîŽc·©ÊAgN¬3ÈÀó/ÅtÅ*,*(ÍwC‚„<0„€`ˆ\B‰LÃÂ"iͶ{ä|Ka Û ‘.‘ˆZ¾újÚn7¬RÛ·êf©®Yu;Õö¾^ ºõk‘Mm°×økº7Â1Zû›Óô¤Õ¢"Ä•4;,I¹$ª^a­\MúÙè9]mZ~I)NÜÆZF[†W†úcNy¡9̀ѕÄÓü§/V¦ybzÀí—1£™9L ðÖEŒ…™ kÌ"à±ïƒ÷iL>ø—Œ›„ c†Çmx ¼U†â‹—ŒmƒXn«ÕuøÌØ|f¤ˆ°"„Y ½H©üò™eð= p™ nÀT¿|æÂé‡[!ºZÊŠ —¨ÓGˆ°�7|̸ù–°z*Êï9ú¨c´Î"7¶èEËh? µ²’ðAD£×ðJúßÑó'–0ÿ x7Œ/§‚0ѳ¡òÂf¯ÿ$|˜h¾°@+'ÁÈ@ò¡«nX&-^½›’68h‰„0‰`T›d™¬áµ´F¤2ÑÅ«ßÐÝãÆ––2ÝþXÞ`Œˆ4EEÇÄÆõáÌñ}û%$ö”<pÐà!W¥X†^=,uøˆkF^{]Ú¨Ñc®O;.cü„nÌœ8É:yÊOnÊâ³§ ¶i7Û·LwæÌ˜™{ë¬ÙsæÎ»mþíwܹ`á]wçå/*(,rÝS\rï}¥‹ËÊ+~ꮥ%K«–ÝÿÀò+W1ÕÕU¯®†Žª¨ú¡«ŒFXÖÖÖ#ž^[]«É‘×dæÞV ëKX„ææ¢(šedu]5Äb`Z\\l2EVUT,­¨¸hýÉ óø\”\p:ñûæ"míy,Ê—Bý‘iüç‰f.ºÆYGw¦ÑÂ0êó¸ºÉ-¯*BþÔ&Çä@Êù÷e—WјsgÛfuå|Õì\!;3=äaëâì„\_–S¾jöÌisnãgÙ„±”›-JÀû/˜8&}Áì"QªX0o6é÷E÷L… ®hzÑ2Fý¾U*r/›‡c¿­J+úÏ~À¨§[õ¬ÐÀK`~,šÀüÿ?òg¹èßÈп‹Ã.þggÌ—ýWs‰Ý!V‡ýcä¿ðø†ýÓýÈZ\Ëÿ4ùÿ þ.®ÓâÿîÀvýû±ÝZ�ÛõO÷#ÿuµ€+ý÷CåÇ ð/ü+<ìGY-w÷ï[Þ®r÷ôa÷S™êÕzjjz¸Î»F^ë{dÝúŸ=úóÇ~ñø/ŸxòWO=ýëß<³á·Ï>÷üï~ÿ‡þøzñÏùëK/¿ò·Wÿ¾qÓæ×¶l}ýmÛëý ;v6íjnÙ½gï›ûZÛö8øÖÛï:üî{ïðáG·òé‘Ï>ÿÇJÇÑÀ±ã_~Õùõ7ß~÷ý‰“§NŸ9{îü…àOؽOÈ?jÂþŸ.ÿÿtþÿå Sïáÿô·Û'-^<©²Rë…ð7Ê’|YÄ?'çúÂÂëéGnða9^(*(BnV‹?£|I·ï™Àáçì"`‹Õ�^ºGªæ©”¤w‹TFÖ;<°¸ÈÎæä» ŠÕøÓй%uoŠÀæ—‘OÈóÂô1mLÓô þ2L ÓÁø‡™/JîÂ|’Î4w‰ú†ë=É]úb˜ÛŠ ËŠÂ<æHô+§¼ŒxΖT7_Ät0>ÆA8„Ápfì°[gð3p3¦1Ôˆ;›øãßÔ™3fΓŠð bC"$‰)“rêP(”Š¥ƒBBY¡ÈPr$�ü‰×ý¸s'¶Ëô[Ä0É#uL6™\Î2¨øëõÿ( o`˜—Ê©ùHg_†ù¦Ì–ótÄe#tLÄ,]n>‰e’ì‚?“€gBÀ¿<º~Àcºªù?Ø•Å2QW«ßÙ½¸‹z|kñ.†ï‰Ïü¡ s<�tCø«p¥o`º&–1¡«Á `™vÑ/äªáÉã †f fÿÂ$æ¾ø�Í{¬Ú>§òF7ß–_–éû,|DZL\Ïr¢›¬c’݉é¡üÿ÷YÏl­¾eZß5O¥ŸpUFò+‰¹ £á½`&Ã<ß–ÛtĽé›¦ã%édB:É£uÌ 5Ý,ü¾F?<yXÂ+fˆ‡~xÖ¨åÕ³nFÎ…~Oäƒn·º¹DÛR�fÀ<¨K€GWƒ¿¨Î!ìaxÚîáËÀ5CØIx,óuÄí .ó6†¹ž<€C·7¸ ì<�îÐeàÆÏg˜•ð´ºZ¹?; åvq{ÒçRu÷'¨¬À€+ÄA7¼î.*×P.x6�,º½– ÂÁÓp‡.—´�ÖÍðXîз7¸:ÛOÀmº Ü)»f!àpè†àÔ¾§ÁCØ*xÚnÕÂ0zö€Û aŸÀc¹SGÜÞàFÝã2<y�‡nh Cü²Õ12 þC€ùž �ÿÝ]½§ë¼›a ài8t{ƒÛ aà±,з7¸Áy “OÀ¡ÛÜû-<�î·y—(ÚžBý2Ÿa~O;À£Û­}†ÁÕAØ3ðXꈂKf™ä0¸o!,¾ó�ÝÞÒ[ïËàÙ�pËõž^ ¼·ãpí—»fÆ,x,wéˆÛk9 ìxò�î™ËÀ)„gÀ zÏ×QiÁÓpèöÖ¿-ÀídÂc¹[GÜnseØØÝ} {‡é˜xv&ùFÝÉcŒ†d$±O±ñéÚø[6'„ϽZº‹z|÷6c¼XË–0l<æ;¶+ÝA8¤„æÀÃæ@O ðH»PFto›{Òcã>¨[x6�,ºÝÆ»gÕr„ÏÉánC¹¹A››†:œÒæ¡E¾‰LrºqÂÕCØLÿôD•^˜®ø„7ÑxÌèÏ=SÁÝ£ÇET$;iüD¦…¯¿'یÓ†õƒtc+bÂÓsú¬ÄŠ;VÇ$ÍJ éÍŠèòÇ|fÅP·/¸×ê™8ÖÂDT@jÞlK¿‡è˜¨otì=Ó—ÉÄh0˜^8¾ø ôHg¦ß}á;SÏ †8‰g¢ÂŽÆ’äÑ´FÑ´ÈûÀ®t1o‚Ïxšäq�òÐÜn8_M`ß¹€eæ# ¯þ`v⬠ ˱ ‡ôµÄ2–ÂxÆ2ÇÌXÆ÷a,îÁÛ“Úú×m¢IžéñƒÌàEIõˆ_BzW›©¯dŸÈ2{î¦ojJ'|ð®Q‰&&QŒf £˜Äñð>+¾^‹{ë*h7À5¸ƒÁeLLB!<s"™„ñ”VFc„ôÙ¤h&©ž9QL¤“4.¡­o=„01 ísÀÉ ׯ"ùÚx/d°w0æÎ>í±mÑõ¦ŠÈ¼µ-4ë°Mé™(öuFe¡ùš!_òíÃ�÷TAûZ<ÃFê˜Hö)&JåK5ÜþËl;>º!~•30à΋©ˆÍ .ä˜Áæ`wRgÿvг–Æ 'X&õI–š§ÒiõV ¥¥U;kŽ`Ìb$c†4ÍãbëÙ(hoãŒéQ–8Ʋ#™±ˆIPw Þú3¶/“Ü9 =±­ ϶_³Ì¿¦ïUÀMŽf’Å(&èœ tN†t“Ç%ÖÇc>@G3äkžª£CϲÌÌçXfªŠc<Æß iÄ@ÑØ_:ûµÇ·!<Büüy–yž*üð~žµáeá)Œ„2F0‰ãâëã/ökÓòÿ÷,3è,ó»¼°öå†öEǦtvH,3DŒg†@ý ú´ò(P¡-±>ypôx&¢ÞP¡ÏÓ¥cÝcw qv@±G ¥eëô‰ú@Y…´’Lô§ZJ´åáX3$‚éóMd½±Â§Ï ËÛ ®KH[|Úâ—l‹/Á<m`¢¡ÜØ·5:ži`c#ËøÕr„wžO´zèg`úA{ï7Ž ÅùÍ–Ù»£+Î'ðž´³+mçÑÐΣ Ãû,hç„~1@¿h EÐÂÄ —ÜŽôc9hÇ…1О£éxHh4)šÌúX#Õ³Á?*’e"Ÿíj'öÁ˜ßJßÑíž ~w¨áè&§FÁx÷ÓÕÖEUulïô„ýö!FWAý,8“8™Ì¥â¼„kë0úÆŠfAÝÇ š5 3±½_[|=ꓼ ôƒçŒJ³äë6㪺ô—ª?Ë ¨‹H3õ#–™ûQWš¤màú!5’é[ø*Ówüß�ÿ—ÈOÚQk‘¡6é\õ)Ë,…'>_Å-úêx3cvwaEx“P?…:‚±$úu2ÔôÕP¿òýƒenþ¢+­xuÜÖÂXf4<שálßÀÓÌô}¥OE\^,ð0Ã’cHÐÆã ’gx~ã(%´4ÿã ËÜwÆ5Ížcêæ?KßÑ c»úÇ p÷ëŒoçÚâêcBôXže†]`™ÛÂÓ ãŸ‡°¿ÁS áè²ý£™þÐßûCïïîÛnnëSBmç‘¡q;ÔžqÎVy x2®G‘~ ¥ÿ'ƒŽù;<¿ÐhÁÄÃx? ±ŒÎÓàî†9ì9xþ¬ÑÓŸÏD€ôU$60q㘸o"*°«48æ¿ñÐ/˜˜oºcÉCML_(O_(O_7- †¥‡ú:Ìe0†%�N B_ïÏä11Ì`q,ÌQéÌà9×Ã<5ø‘QLb½ºw€}§ð¼ý®=®-¦>ªÂ”™s&¦5'Œf*ïLÛÔ?ŒçÉ…¡ù¥k< cγCkÃÀëºö*’GÇÙRÄèOý Þû2ý¦öy%ºÓÔÑf¨×©}eãiYâ!3á{’ÇÆMãy Ì·ƒ€2€"±íÑm¦z­¯f†âq¯ýx÷Ñ1}ÆE÷6Ðqð�‡öˆ¼FàË ˜Õ½¯¥cžVÇ$À Ò"ù`}¸» éÉi8†Z˜d oßNs{Ÿ¶Øú芨<SÏJaR. ÃöÅv í�Üqÿ§½+ªHóÕMd¸# ”ÑF „p)š$!HÂ!‡I'éÒénû€à¢De58 0"&!I7!ºŠŒËhÔ0 FÅ5˨DÅGe™\# ìÿ«ª×ýúî�ÎÎþ†_þ_U}õÕ}½®ª'êÏ×B!C{ã^Û{òÀ´~ />®ê'•<õÔ§žŠž6¥>ÑÕ Õªó0;Á×XµìxƒÇnŸ±x̾υó^/œ÷ø~Þ+ŠM69*ä–±IÌÆXNnJNßcò¥Fî\á[8ëì–ëe>­å;V§X ¶*jNy±!­LoÍ3O§M…©Kí–©™Â7ÃÈ-sPGH§kªÞTl$»¿B¾Xn ž\ ›RªlÞF~ÒVÚͧþ¸Z+wãȽOŒŒÊ5 6\rùÑÚ‹Ã £f‹ì|WcÅÌã}»bâ;^0¿ì6ÓnœeZRn*ž\ɵS z duœJþ„œ³¬iÖr{9r ×P$öËÓfô‹ ~ö;T)ÈãûËéýMŠfŠÑ†,åûlØ\Æ\QlÉ*7RØÄ¨<e¹ûÌKVÔ,SÏ8įÈÀ÷éN)7ÚéÇ®!QPÔ¹M›aK7:JK ÖÚYf²³§(µ2D¢Û@vYfó"‡eŠÜ)†ÄRÞ¬%—4½Åî°ÒÌÈ€J;{šç ÂUí{BËâ¶iH(*kà¦tƒÍn5/eÝ5êC<ODÍAfh+"»‰—œ½XT ƾóÔr§ý‹)Œ Z(DÒÌ$‚ý'É‘DÞR‹MZôH”…"•ÂZXºÁh°ûˇ(EÚîš#¶²ßÓ/}*s2•ÏÍJù$‘‰W…›ˆ“µ)‹×TÕN¬\;¯±¥°W)VþlzsØžÀ>Ðr»ñê™c°òL6‰¤#Ž·SjóÊ‹ɼhòo+¨j¹ž6AFSl<·À±yÝfêËmweAoÈx-IEá-ÂxÙÍÝšóÌJgoFQ˜Fmq;®n½0CåCú•ld÷‰šÍy¤a,~\eFñ¢¤r›Þ^óˆO!¹~ëìÉÓ‰?„ÚË7˜¥Q¡/a/i3Ü;b}J6ÅTœk)7‰,ÙÊkßLƒ(°4”ÃVFñKuÐa´©k´ž}$‹ß¿æ¡zQûRÕÏjQweJRتte;ì‡bþ;/©9¬C›•6M†.òë3µ¡$‹úK&ªÏHó¶ÌW¼gæDgÛD;D^0–Îøv>Í;™h‡”6mòÌ“³íæ¹ñüWãuÏjùœoФvŒ%ÇwhYسû17hÆ8»’±èÛ0n!•ÑYÇÞÁXý>xz7ª¹|)cUävF–…°UÁO (1ŸÞÃByîÉ ø¶Ú{ÍøQ]øƒÍD~ƒŽƒr¦2 7ú€Í0G\:JÀ#¬fþ{ý? ¶�zÐo е¼~A͘;F£ [è½<ÂnFÃ>™ìa®ÎDôˆÕ d¤ƒ.‰‰ž…øƒÚ@ɳ¡ƒ~ÛC‰Tƒê‘7;@»Aß‚ú>§e‰ … {@OƒÞ]õoZ6 T z´ô6è$hÐóZv#hh¨ôèo þ/hÙM BÐ P3èР+v Tú èYÐAP'hÀ‹Z6t+ènÐP+è[Pß]Zv=è6ÐÝ Í Ч úÐKh2¨ô èEÐG AW½„0AzРg@ïƒ~�]õïðÇ?!@ïnºa¾qëN'=YOÖ‹õf}ØÅ +̽/eX±~¬?»Œ `1l »œ]Á±_±+ÙULdz«ÙËâØ5ìZÌõ‡²_³al8ÁØH¬G±Ñl ËÆ±ëØõXÝÀ&°‰l»‘ÝÄ’X2Ka©, íb2»ÝT^\n³èíEe¼ dÎÈÈÏ6Q&ÙYèëó‹Ìù6‡)ÿv“>?›˜Ÿ?ªÔ wÛŠþCéåBz'TDí5¸+o¿Â¦7 õE‹‚ —””ç¢#¡%J0Aáb0-j±,-/‰H,œFÌ1r—VšCÄÆØPŽæò3Y~6Âæ¨%&æªÁÝù˜Â]Öhu"¤¬ÙA§BHL¡Sv¡2L!³Ì¦ÒPî‚›¡Æû‚9òügÊB8·Ìl -1€…’ÛàŰ•TB†Êì’üüÌÔ2åöŒt[Hw1s@K ÊK'¾BH/6/2„)8!ºì„L¨â!KPˆˆÃa„Âu;B*—ŸµÕh…Ülsyð~±Ü† ”Ÿ½ *SÁO·u¦³ÀAiÍ„"Ï“š‘^(-¡ôH„¦D"”‰Pf$B¹!…JË1ƒ·N7ØËÌÁsÙ¦?³…éÏlaû([¸>ʺ²©ß"„ Õ à>Ÿ’ %®غÒÉqápuÜ®'´…í mè–ÌÁ[‡I©AE–Ђ#lkñH…j.©PíÅ#ªÁx¤BµT¨&ã‘¢6óùÌò\G ®"1˜«8B_¡¼ocìv$q¤Qo*)*`œm‚.ô˜V»¯ªå3ŒFC©Þ˜"ì«Vñô¨õØ”N^Wl(áß٤‹—nÐèkË¡]Я+Ñ‹û<⊇ú§#ÏlÖUèMK=W èâ+ô•º8cñPïtÏ2É» ÅÙžCf“'0•«n …η먑È`#ÖãèÄoºGÁ÷zÞ.®X¤Ó/?èàŸê•I¤ù%&‡ªÜ¨pØèt¯®˜Ï鮓2=ŠC¯³XË+ÊyÑÐ —“¶ZÝûÄ=áe;ìÙ%¢”ïdéIãçÏ)eæB»¾Ü¤'¥ÛTšÅT•×So.5‰óß~neDüÐŒˆÓ9C^ãM:3„Ýü¸â…ò,Ck‰Ö<­y¤»ÛUéð­GJVn(¯BYšÞt­]Çoë Z¡GK£×9aó'˜>ßp…~ÞÑxp~õ‹#¿vº8È&†PúgåM¯ô/gã?€¿%åÅ*Ï‘Ô/oÿž«CxáË ºTzn]®[ëùË·Hå2g¤LPz(ÄG,£==¡÷å.x¸¼LœÝ®/*S.$’WÂ@Çìé"¡rí©¸øÜ Ä~žôxµÓÙ² óÞ,Ð8â1‹W¿J:ã³T.¼|&õ·bŒ/é¦ûRŸö/Wªþ(ìüí…|šQyä<fán5”áq¤œÚúé•î þîê(ûûó·i*7”«t.s‚Ê,Ü=+y%ÝBÎßžËÓ|:|�{.Ïgˆ©îþ×-ÀžËc2í±Uɰçò4qŸ�ö\žÏæÄ'€=—ë·ƒ[>€½(¾tdY>UŒåÝš3Ù¿Ü„¼G<Õl6ôÊ…4‘ëñö§ÒG ¤®èáò*qe4`óº¿TïúË— ‘ëòªñ«œR÷A¸Èõxûóxã+¯.èòq±<ŒÜ¿÷ˆËh—ÓãñŸ9t6á$êšo _á¢ÈÅg„’—ù$½h|n(y^>RZƧ…’w× éGǧzûñ®RTÊÄÏ ¥_¶é…Dã§„’çå!¥…`|z(y‘ïÒƒ”„Ÿ¡þ%GN”òß¹-fý.Ç.z¸H‚¯k¼ûÆÉø€£cv³w?/F¡Åš+ˆ:?Uq—o|ÝçP…X‚bí0Ð8IZÂùæ¡fšˆ•o ŸÁ§yäUÖ"Èøù¢0•<r2W¥›È‡ùš¯Q* ¨H”sJŨ™àç¬$4P9e}31}·šd!yôx¬I‡Ål+Wß&€·µ’÷ÊÆ3Œ&¨—5Â=»¤k–@N:¹óUñìfƒë›@N¥/W­O5 {)T{J$'Ý /C­Ï=LKm¾éôqW¢Ez2Õz<ÃwðtÆ£–øÊ©Ò9E­O5¼M'éó“ó¤3]­O=ü«ú¦×_ÎdÆïSê‘èµ;©^(–jy^ÒAmïî]}Üd¯î§Ÿò/~žô‹ô¸Õ0¬k ´³DöI|]áPOÞ­¼ýð‰ûpñ*#®¸WÄëkïvvnï~ŽuyÖQâ×Bزd• 'îKDº&Nèv«SÖœâbÀ+¿½ŒË»—dfÞûóëû “®To-Ô—ÒyF#ç¹7T”‚-ük!!ù{ Y&Rl+)7ÐV®Er|}Xþ|¯/È]» wAîENüÐ%zÝ8¯õ~èñO=N¹ácÊ{Iùλˆæ±èô+ š@C²{?b¸~×wíè­Wu9¹ÂÎ:‹A,½½ô“¥‚Æb]Fz¸|Ç¥Àáxé/5š 1tZ üvTåÅ­_â~dú•µ³ÃB×ÿy~Hõ ×Ç•„„>|å‰÷[ƨ,Ä¢pâÐ,uíáL×[,†â‰Œ.šW‡ì]öX䎶[T\O2 aóÏ/ÞdxÄ,ʳb?èyˆŸ'Õy—g–û°øKTÇ€¹ígÅh=ì]¾Ê"=Š—Rs¶é™â.ÒP) YTž$òçÿª>©SåÙܶT‹FVo½“ì•&Ò{ªy|7· ¤_%º/ðDëlÚQÀ¼%d2,É@gNÛ郖J&ÒhFÏ@Ùæ~½C÷íNWe›O?ª8F°ñÑ;ìæ™X˜l‡|ÅqPÕ ï‰žø¹µK† /p-õ Wù<ôÈët¸píJ#”Nű !žU»M÷øv¡ý'x¼t¹¿qïÊ ž[ôlôÏTæ/±)üC6ŸÚüó9x¸n >*Î)>g~Äý­ÿü&PÍžçÝë)Áñ~Y]<¡Æ#•h°É?]ë÷Üñšd¡ø*'Ð|f¼aÇS¯‘'£Kå ‡ü i Ç½çZþLáy±$ÎF–µŠ.$&‚z¥<AÊ' möç5©»ÙjvX&†IŸo–t)]i¯sT?…i§nÑ®èW¯(–k°™ÊKó®€sNßyµ÷x£Ì #Xxɸt%Ýò]:_œ„I¯Z4¢ô.4~‰“„<½ƒêJzd Ž$=jÑsšÈO™üìó„@¿kªwìâûŽsh'±tøBysY;ñöY{ÉÌäotÄ1† ã‡pT¯Ç"ŽÂÙÍ˦LÉ *£ æ‹ù®òÏm©°XînPÐh6•«vJ¨ÍT%Ú(¨ ÍÉ~Ôu´ù ÙíA›’ýuc=hóA¦ªÏ ú¥]WQn³y~¸—”íÔòåVÁÚQØßa˜ž-avÕ#=á'”™ÝÄÉ Y93û ¯Þˆ/ßDÿÌ”9yùt¸yN—÷5‹cF³™ÿBç°èH@©‘´™[¦;²÷}üƒ”¤!ByL1tÅVý Ûæ°–è‹ü×Wê÷zäù—öº"/>p\~êºÌ:Üïnù%cFë,F½NÉëÜ_OõÕ?Ãl!"•?¡–‹ïZ¹7û¦;øûè1 ce>-ºî†Â¢’±‰…c¯?nô(üU8N?®X_ ÎÝóDöû¨ÙÄ‹°—”ϱšÑ°+lÜRlж0ðjÑË£×_N™Ç»e#ßa’1÷¢“Ñv½í•ß¾öFT8úVlÈ T.Ô¾ù)RùÝcO¹fBqêB?¡ÖãköîÿUúU»„•O‘ªšpáªÏ)¸sÝ}š@O¿(*ÝÇ ºü{¿Z¿z¢ 0ÕÁÿ# ‘†©\Àt«¶8ø÷¡õ‰~°ÜL™ÛZV5o³.~´Ü×hÝ;ËT¤w”–yÂçM­,ÐüDè ¸� ªh¢;>‚àO°u–×Êɼ„¾^Æ'fßÔýÈYì{àþĉ$¾ñ«—0( ç&-7a„/íuîþº´¯~ßÓÕ} ¾‹"u½ ´?Î"‰oxë iãžîF]ܨD£CÉ(/7z|ÜÅÛØàî£ëØ8Œ±·êÞ+9xæ ‹ëæýýiöúJL4 ûˆ"´Ìó­ö Ï…çÂsvcú„ sÛbñ=”–Åžïƒ^Æ<í­›Dúæp ãß'fôáÑî žŒ¾yÏXFØSb/éÖ[åv™ôKx‰Ô-í.õ•Döƒ¥ý8iN‘æ4iΑ~fJû\‰s¤þ[eØFiO‡t šÕ bâ»=e:H¾JÆy¹ÄÒý^é¾RêY ¢¡y³4×ÊøÔËðœÒþE¿·¥û‡Ò=æ"/”<SºÊÊžÿJ׺L”‡òÇ ÒÜSšË¤Yé§ÛþU~ïU‘—æKyiî+ÍÉÒü/Ò-Í1ÒÜ~§0”æfi¾\‰¯4_¡è“æAÒ#ÍWJsçRa¾Jš«¥Y'Ís¥y°4T óÕÒܲD~ßV‰4+¿/ÖHóŠ~i>-Í«¤ùŒÅ,Ÿ*¿“tç÷ñëÂ}ç[¾«þný]õÿÏúlê#êéùBªëÔ§Sß\Ã<ý‡&�ýüOtÐG¸ë‚>—èýìOð ÷àÃû¿÷ù|þÙë€Ïóÿ¾JÈõ 6ŠO]êÑÙ¬XºB× ¬m¸ü©>D'ß]ÅÑùW·Ú¢ ¢}ùO“%¾¡|ŠÄ§îHÝå싺£uóƒë¿»–s·`Â{Ë¡ÛéXþ¡Ûö—¿ý“Žu·ðõ ègB?Kæ4ù¿×8—|‘Ï:ýÌ=ÜcéÄÿ*‘¿Õkä|÷aÇ» ù®â7Çé~¿Ökž­èÓÊr+¨¸@â\‰ys$fIœ*1Yâx‰‰u£%FIì|Dà ‰Ç%~#ñ¨Ä#;$~$±]âA‰mHl•Ø"±Yb½ÄM>ú×I\#q¥ÄJ‰‰s$N•˜.q¼DÄh‰=£¼Ë[)¯Ÿâ-åÞ6øüà=òQꓦ—nJÀ¿ÓgÎÐz“ÖiÉ š(U—£-Óºˆ:4éd¢}Ó£ ¢¦Ê"tÿ‹°ËÉÍcçò䤟›ÿäž"ÝUÕqeÌî§F›ìæ[/òð'{¸ùäM}Ü|ΞKÜ|쉾 _õ؆Ë^·ûå‚zeß%¾öÛo_ñÚû3’uÇ7¿`æzæõ'Ë“Ûþ÷ÅågüúÞä;ûp}Ú//zhèŽÍÉ?¼R“þíò¾,_ñûäW&}ÐøÎ™Œ«*_{?ÙüFÇš¯ªöÝüÒo])»ï[4ëÚúÞÒ±,u÷ÇòÓ§¾¾ûé[Òjÿkñçþ:täÊåפÇGxºs{ôç¯ï=™þµóàß¾ÿúÓÌ;׿3Ùpà͵¿lÜ^3ðó-S.6 èãX~¿cñ¾©Óç¦qþö†‰ãv=÷mîtç o|}áÄÕ›wfaãñZ]K^Úëõïþ¸zÞêö¸…¿ùáO¥O¯ïüÛµ¯Ôµ.ü¸Ê”µï‰ÇujgìsPÒß:ðÖñœJºãÆ^n±ÐŸÎ¤yÅg?ô¸©ï½ß%íy¸aàgs¼øî_’Ú.²òæ+nnzÝy,)¹qg¹qÇE¸þþ¯“ÆÜ¸;õÎ/>X÷Ê|™ôÚ?¾­_yé’ÛŽ$Ìšÿéô½/¯[ÿ_û’Ý›;Ñ‘þV¿—·#é“Ò—ÖÝ0ù™+Ÿ}g]Rì7>ÛýÒÄQúÕw&égÝúêÝÏæ.ëûïùIùP÷—½¯ÿ©ô¤Ø¤†¯nh¸öÏ÷¸~tWSí‘ÝÅ˦´}<릢í~`HÞ’{Ϭ]~ãÚ“ÞY—6ª¥iÒàß¼;~ÅÍïb?~8ñ£Us{\ñìÑÏïoì6ñ•“S±s£ãú?%LsÇ ÷†üiô±Œêy74>ðrËœC +ë^ýYÙs—:g>•~ë‹ÑÃã2ŽŒ¸nlvîö·Å=rkT}Ë'¯ÝuêèWnþrÕ¼9k'½’sïÕ13¦ ¼÷‘Ý×Ýu鑟<õö{­Q7¶÷Rê3½s ßÐiFDï ½�:úèR4„D ÝÐÏX…ÆÓÈß&Þ-=zô>诠hÈŽMÿY¬˜…ßê¬Ö²jàz¡´VËÚ€º¾pŸìß±ð: ” ¾ øûþ#À·�G]†q|2Ð ªß ÌEçZ¾�Øg †dÒœù+Œód|´| ð0¨üq`* Í>ø ¨|5ð/ ]àé…S¹ã x p+¨•Â~j#=ÀG0Am'ýÀ+è¥Ó£HðEPð-ÀŸ@Ñࣇ@-(†d€7ƒtàs€Û®e,žäÇ~zž CZ†cl_�ÜJOwü!=À}7¢%{àš›'àk€í Uà;€ÙIÈÒ¤{Õ7€·�Ó2'`-Ȟ<!ýÀ ]àéòÏ@-àé.òäiÈò ¬µ‘_àiP;¥7 íÔžîAw‚Ž’ ðéë(ÎÀÁÙÈ·ß",à[9È7ðt¯úú[vðt—ú=3‘^ðÕÀ·r1¿"`¯YhSàénrÈžî(´ <ÝU>n6æ/àéÎòÐðtwyÿ9˜G§;Ìï Z§åw™JOw°×‚Œàé^õþhàëÀÓýê·vƒ·�ûc9°—ì÷Nàw Aëµüv(<ÝÅþÈžîdŸ¼:ÁÓýðNÐnðtO|¯Û0ÇOwÌA}Óò»ã_OwÈ_™ÏØ$ðt—üC bðt§üIÐðt·üÐóàénù?‚>OwÌkõN×ò;ß A}ÀÓÝð{@cÁÓñ …ðžî„´<ÝUÄXùƒ’_àÐiò RŒñ|ƒ–ß!¿’î?OwÉ—PFàéÇì¶”xº»^[Š2Owæ5§{ç£Ëðtþ™»‘Ï$ƒ‰”sÂß ìy/æ¿düÐ ðôâ}(è4Åø8(j£–ß•ßã>¤<Ý{_ êžîà4<Ý×ÿ=(v£¸ïØŒ Ÿ ,hFþ€§o�ÜŒN~xºÿG éàéŽýÔ? ½dL|k�ò ü´’t¾ƒr5QXÀîmH#ùÖ‚vƒ§oì|éßÜŒÁä�Ù{b@:HòÀå ÀWût Î€×û}Š| xêsäÉA›>†|øtçGzÁ[€—ÿé¯. _�,;ô’ Ðõ?H/øfàH/ø*àXÇ.�OëÙݧP¾àÛ€±²]F:s1[\I:ßa¦»<ýø;´Žì_€6?´h5¬žô7÷Ö°& x ´ |t  jŸ̼XÃZÁç�ã/×°6²n}¾˜7HÃŽPXÀ} o(žÀ¿Q|®Ô0è4… œ;L➀<0œ†Eƒ·�÷¥kØ ðmÀë³ød  4ö êë4¬ö6 ›¾ør¾†M%yà[…–ó}WYà ‹é,Šð„Ë@ð„m& «ßœhÖ°vðôÍ®[A=¶hÙ`h%…¼åä-ø«†Í¹ÀÏþÒû:-¬µkX3움;„Îjà‹¡spÀbaœ,ù©ÀÍ‹…L ðMÉÓ·ˆ“—!žOj9Ý…°6iY1p#èt–m~|—ÐÓtà Çb…q<áU"žvà7 ~ÐC8x9ò|,ð’–>øä½È[ð5ÀŒ•BgpÔ#¶�ö“€újäÅøÈþÂZ‹ú°VÄŸÐ.yÂ—Ö =»ÎGEZ\Àk~‹tnÖ²x u†ÕÀ¾ø<Ⱦj½†½ j¿ýÊâ LxLèO.|Lè/�n”ü&às’?�<C<ü²Ç5ìy8 øµä¿.Þˆú ™uÀS…ßÓô½ÙMÈ7”E,°t“׬Û$dêŽ'‘Ÿ©>¾ød ҾػV„Õ˜Z+Ê1Ø\+Ê…pL(÷±ÀßK~7иEêÁ~±E”éQàÈz‡D`lÚ2d/:É/0©QèŸ LvjXä9áJ§ˆa7â û(`­äwr‰2ŠÙªaæm¨à-ÀeMV ž°¡fð.à¢ç4lx#pæ¿!màó€žGžpÄ "í‰À<PÉ�|AÄg pÏ "]{ýw¢_ªE€OìiÙÜú*ê9ü6翆¾y² Xûš(£zàì=H/êç\àG{„~B¶Wè'÷:Ò™ñÀů ûJà’? ö¦¨ççZ5løÓÀ¿îC¿ þ°q?ê.x°c¿‹0æ€àÿü–lƒÀžo‹xö¾ :�¿íÀ˜w.xÂbP'xB×»pƒÂÞC_L>¨aYà ?8(Âj^ò¨?ࣦá¼Xû Â_üþ‘ÆN`éaQ.eÀ“‡E<OŠ~ùßÜô™†éÀÿ\ÔÂ-_hP—¡¸éKô½$Üû¥H#áM_‰z[õg ûðÏ"Üvàô¯Q·!Ÿ\ÿµÐ¹xXòÀǨh9:&â \tLè1ÿpLÔ“àMÿ-⟠L=)ú®tàý'…ü*àC§4ì ä«ÎS"ž.à~Pây�xTÚ÷;­a÷ý„6ûà <ðíÀ ýÉgi´¼½ÌŽÐ >X+ùzà ÅŸðÍnZž'­À[¢´¼çÿj&ÀÃiÙAð=ºkÙý Ê“UÀÎ_@O@]-'áÕ=Qæàc×ô|<PÓ[èÅÂþ#²ëƒ8RÚüù¾¨‰FŸ ¾ðü&àæ¾è3hÌ.¼ þÀ�û @ÞoX=@¤‘ðºäÉ�íÏzÏ^‰:þy`æUÈðYÀƒQÀï¦\¶>˜‹tƒOV‚h,&¼8NØW^«e'‡"Lð§Ã-â@h•¼Øú ÈÄ�‚¨½~3q©8d„–דXàˆÔ} 8f,æSàǯŸ øñÀQiÈרŸ˜ü¿ 8,i?X ¢¾® ¸%G”W=ð{Pd:w墂_|zÆ¢ZsiÙ•³áñѧÆÓz¸TFëeà~P'ø`ÿ9ÈGi-†ðA‰X—�WƒÖѸ´—Ö}ÀÞs‘nZ»§úaÍU�\ š¾¸T ¾ØûVÄ‘¯Ý zžäÉô É÷ƒúѺ Ø{ô€×§ŠÁ�WƒÖ€¯î=OòÀÞóQ7I8 …uVp5h8­Ë€ûA Àw�{/@¯N5‘<p5è É÷ƒN“<°÷BèÁÚJœÊ_�\ Z¾¸T¾Øû6´E’N yàjÐ ¬‰j€ûAé´þöÎG½¢uphøàzÐ2Z÷ƒê7 üäªxÔ¶…Ö›Zþ­{â Ó$O¸@ò„+$Oߌo<á«’'<$yú¶ùIÉÒ÷¬)\Búv5Ù¦IžpŽä mRžp½´'|FÚî•<áÇRæÂsîÏ"ù==úeƒ~”?‹xÙóß¼ØÓ34· ËêæÑ™ÕXÍf¹,'³™à2X6›sþNaÙ\îQÿýé‰bÝøo5 &I=´/Ñ·”ŧns™YùÙ§Rh+gFf€f+at¢f—ItÿË ù÷¸ç±_!´4ÈT0 ÓC~)b£‡Il¤Ï¿ÏòØt6”鸫úCBÇRXþ˜ ÿtÊ„{ ›ùg°n%.é\ªˆÇÑÂ5˜™I†A~t0©5ÁBZ»Þ†³î*}³AVhôèѬ'—uð³_:?i¬¡Œáþdz>ðCyfçÒ£*7n‡M9+Ùx^Ù‘š2n?þ² SÊ}PnZgJi)$h󺿎mæ¥1žàÜ(Ÿ<ÈT@Þ¹¥0ÏáZ²nE#¬l©§\ÆQÉS˜¸zÎÂÍ`}ÙÜŠpŠ‘KE8Ûr§'žiýôùç9ãw°R; Rõ¯¥ŒÅòžÇËÝ¿FªRÆŒ5Gh²çï)Ðð½ªeÏ0þÝðI€Öº¶ºöºãuueõ•õGëcâÆ7´7t6ŒoœÛ¸¡±¦ÑÕØÜ˜ìœêlÝÞ¶½}{Çö£ÛYÆdø­©uÕ6×îªm©m­eÕZ®»­†Ñ'XýÒݨkìØztkÙ¶Êm«¶mØæÚ¶k[ë¶ömG·unëÑÓß4¾ijÓܦ²¦Ê¦UMš\M»šZ›Ú›ªv¬ÚQ½cÃŽš®Í;víhÙѺƒ~Ý%Õ‰õ‰ »xübÇ7V6V»ãØÒØÚØÖØÑx´ñxcg#söpF;cœ:g¼3Ñ9ÞI)ÈqÎu8Ëœg¥³Ê¹ÊYíÜà¬qºœÍÎ]Îg«³ÍÙîìpuwv:™«‡+ÚãÒ¹â]‰®ñ®d×\—ÅUéªr­rU»6¸j\.W³k—«ÕÕ²µuëÏZjž Ï…çÂsÎÏÿPK ����v©Æ@���������������com/sun/jna/w32ce-arm/PK ���kSo?îBha¾e��Ð �%���com/sun/jna/w32ce-arm/jnidispatch.dllì}\\Õ•ÿf ˜$C¶/J*ºa 2IÐCêhP©M+$€%0 Ån~í$!SÒb š*–q7ýmvé–v³Ý¬·¨i›µi7Õh³šv‡^Á[ZÓ6m£ü¾çÞûfÞ Ãýí~~¿ßç³èÉ{sî¹çž{î½çžû÷mx ›Yc6ÀÄcÇ™øs³™ÿü€ù}~>;6÷GKŽ[*´¤ª¡±Mõ¶¶lk­Ý®n©mnnñ©›ëÕÖÍjc³Z~×Fu{K]ý²yóRs$ÏíŒ Z’Ù›/}‚ýœ±UÆ‚,eKš%©˜õ L*ä/zOr# σ±päêV ›ËªˆGÿ(QV?c쟌y/0æ$&㌉01ÿYXÍœY(#î¯|Œ)‡¦ _æ«o÷áyø)åÕK£2­fY]­¯ï>Ê$åyfX:7Ó—µ ÂÃË€h€¿œDç^ÖÚÖº…ɼŽË4_Mᾩ„"ïHÜŽÇ“èÖL“ÅÿþûÿàÏÂî{Ý·¼mq†XM`(ÉJc,µv)c¬*Ì𥟚ÌC^ÿ £q¼'œ Ü_áÝÀ_âQ=Sÿà×ç€N ¥�gSCwJZ¢˜Xü~_œ˜¸à­¸åm9,À3­[Wö‡’ Ó‚``¨|¼GOV߫ۘÀeÇ<ž·ó»–‚tŒðb„Ó»F¸MÀ~”QZÚÝ!ÒpœÙbƒ!+ XR‘¯<µ_gÁ@˜)ôzWà÷~ öé-M·Ó8/ïxŸn;³>ÄÀ¯a€)gšäë�_;ñu oÒkpbb,OO¥GÏ¥§Ú£[´@x.Û?ÊeÕ ë õÔ¯‘øk࿼ٷ˜b¸EÿŽvfˆ‘n´µ!kq`è@*ÛVÔþƒ7³À«åÀSìÌ—BÌþÂèüqâ'ä ôë¤[’Ò§2!=“Œ”^ éåBå} „)Ý #tGºàò©±´—P~Ý3п$¿†Y𫘿5’Ÿ: ~lüÖJ~箟™ßñhˆßFɯ{ü¼³à·Nò«˜¿ÜYð»Sòc³à\23¿;$¿ã3пC³àW.ùygÁÏcШ"íhò$ µ;-¿Ÿ‡Q{3Ûƒ¦"½Ì›eº¶xRºÃêô4c 99M4‡g ùwÐthÓ˯Éúo #Ù3a+²*  È#ÛpŠ.€P¨�T<€*@5`À ðȶµ:�u€i縑öúïOŒµAÈh… ö›e92©O× ú´HýgÎ@g•t—fÐY’¤;3ÝU’îè t $]ç t %]Ý t’Î=C¹¦ƒî©šìc¼Rÿu²L:]²l¨OË@ºÔ§q»Ž´‰Wï¯Á ñ+¿%hú|NƒrR¸œQÚKã‚öŠä‡¾i(4ÙŠäK}¡òúÂîHEôAÐ7M#ƒòÁ72P?_íéçý» 9Èû ‹ìëI^‹Ò=„~:Ó ý¤(B?\OR?²oauOq©¯@:¥<L½éÀ/ >ýp4ß=“täF<EöávÐÛd>uÙ…8–Aá8 ¿`p¬.áØÑo+/Œþ~Õø-ûq[\ÄîujN\Yì$™$?âõCÉ‹ø”K9Ðçý˜ûòø¸'ד oƒI<} …R‹)ý·¿N7»,_K6ÒE«ñÌ@:7+«#¾Ð:Ô?˯ûŸÚÀØ…“YL9þ_g,¶0[ü®ËކÃû¹ì™y(×M¦MöŸG�Iн={¡÷»Þ›¸@v•ÿ¦>ef„U lg>•_7÷1}x÷à~æíõˆoì€_øK”_µ‰Öc¢�í»”ì—†ö…~WUÓôlø¿yùÒÍþ+µ­l–{Eõ%ꟓü:x؈|XM~…|^Ož}=Æ�àIöÅà›­fq^LÝiÛÜG•þ0ñ{üNÁ^.Ã1Íá`„æ8hÜ®à4‡L4$cKÓ3‘·'&ÞKc-ª-ªqè㛤/S¸ÇÎykCÈϯƒŽúÜ“²Ï”}êÙÇž–}ïqÙ÷“}ò ÙgŸ2õ¯ÔF¿£¾:ÄÛ[Ÿú²“¿>m©Ré?h%¥?ñ”M{KG»{âf%°íî Ê_V‚ñ逯æü›Ç4^ [~~@Ø‹¤¼ÀÐuMÄ^Q¹å³{ÂIhÇ.´ãò‡�T\€MòIPh2ýž |¦÷qa³ˆÿ É@¶o°›ÛÁ&iãíÙ¾ ïÞ2ôaÔ¯Êg…|–˧[>]òY,ŸNùÔä3O>©o§úq}; 0 88tj�}çÅX0R6ÚØrk(“m ó±!cOcÀýd§](3ô]kñÌ.ÀØy°_w Zƒ ôðeSÁ<fÜÃ’,”&õŸ6£$½ mÎgϼM:Qߎ§£Ë™%]ñ,霳¤Ë›%6Kº\b§~4 ü(7Ùâ !>7àNƒ†™;ÎÀo¿îõ(—ÞQšOI eo Y{>Câ-¬-œ‚þ×øMs$Ô¯RZŠ, ó…ÿÞ¢ÀÿPaƒ§Uë žÑ^blãÞ¢õ“܇ƒ†Lf†'&bÒ_ð‡¯²xuÅ 5ä›úiô➥þÊgIW=KºªYÒUÎ’Î3Kº µAê‹©Í™ÛO žÊÌ<Þ™ÒŸö¨»ByʮЪ?MŒqz¢•6˜ó˜Žï,ñ Ù}î#¾ÓÆí"H8{mR8·ÿn1OD¶¿Cö°ëÝÝÎzõžkÙ˜a}”]¿ë–·U©·ôëVªË4Ÿ‡:köOÇi<@ã£n162|†£„÷{BË{Ó¨í{îñ ó>­·kO!ÃTÇm*½ß&ŸœÕ€/ú OIï±Ô<âã 3Í’¼/…ÝC~; ñ¸2õ.M:hÒA³È7¾Ž=¥ÿì#l¬Ï·ðlÂóßñ´ø+BsÆ¡9Íê°=pgèÕ‰‰WŒ<Í•þ Ù >¢º|Ž’³Ô@6õiü+¢>%Q¿l*Çï~z]wËÛ§QN™FŒó÷i<Äõé‰ê²|™×‚Ž^ÅØâøùS¡k²-þÐHþOHá:úqgò`×D²ùvï÷GÍ«Òün&åãñFü›ÃEŒu³Qç÷üqbì7°+}4Ç€ºNù»2›üÕÄåuy^¸ùKI”?¯ÈŸ¸;(O(‹òèٯϑ>ų›üLê¯Ì|x:ð·¾è Xþ‘íµÌAZ‘±§)Éæüãýn¤gGy2>œÐÇ*èƒüñÃRd#¨È$¹¥]0Ú— ùÝ=zŽ}Èês÷–�|Äî¨ìH³sI¿nÌ•[<^ÒUc÷†)v“¾Œy5EÎ;T"<GË‚Œšs¢±2·¥2>ßóÞöb¯C8áÉW"ÝS»7‡óy» '”Mü³ô‹(sã{š_OA™Ì!]öëŽîý|ÞÝP�y5iz®œ{¿]¦ÃßÃå»—æ¡,È[2p–Œñžd£}A§á3×±1Û#è ñ›àûœΡ=©G¥¾ôžð|¢TC.мŠðʇââ÷È¿ |È;\P†7…— <áÀýŒÆB̲1Fv¾Û 8e²óTÞç•·†lê3;|ì:vÁnSžÑÏТ4Ÿº–]ÐäÚ‰CÒÑ8û‘¦jÌ/ÁÿÏ4ÆM4×$Û³ó7}:ùüäÜçüOSúîðuÂΟ*éש.ZÇ.Þf_Öi‡ÚíÀâþHÛ¥ºwø~9O¹Wÿ­í/®­v¾Û áí7÷ó:d×v‡ÌcÊ”{M‰Ák§­â´õxÚvøì.íÏ@o#(Ýl“l¿"ÓVâø¿Ë·Eù]íÅ›„N zãP¢=k¢ý)hOMC;`¢} ´‡§¡í2ѾÚ†›Dþ—M–·ÎDûK´Oô\µ ŸŒæÿk3”Í“ϾIèÇ‘ ll¦ôþ´—s§ÎG°8JûmО‚–øž0Ñ> Úih™h} í’´nŒ‘âi}&Ú.Ò¥¤µÇñ¥ºä1Ñvƒ¶¼XèÝÐAqn„V3Ñî3é=;3ª÷Œê¨Þ™Aï-&Ák¢<ÎÝå±cÛM<›xô™x4ÎÀ£áÞkúcúGâQcð ¸ Ûd‰õ¿"<dxµcËШÙ+¢ºû µGI—‘ ].ŠÒn¦öø1A«$j&Ú{¨=~L”_F‚v>`¢]CíñêÉyî½/6Ï9Såa×QÝJÀ£z«™Çczæ´z{LWÞ¹TäqOYToT—“ÌÅ ³%HïR}lz–Òû|¥M…ý‘:~|i´ŽFÓ[B:Z”@GqéÍ›!½d„×™ÒÛdJ¯Ê”Þ?@®ªQ~ÿT6Ùå™hᘧª dSî-¿ólú:{–Öïsb탑Þig4½ïn0crz>øjG‡ûuò É_[Àþetûúhüsþ úy­oøH�cLm¯žê!Ú’&öÉ4øªì¾0ѹ½ë¯"_ø´_C;4¢ÂqC^Ÿ<ŸÅÆÈç~¨³?fžÖ €©žŸcRÉÊÓ# zô£Ãýýï'Ælž½| ÅðcªY¼\æbœèäþK‚sv¡ð7Tø´ûÏð»NH|ðÀ_퉫î À‘ŸÜ+qC·î }î÷Â'öKÜ~àv�÷6÷£C ÀÓ~/pÃ47+éZ@÷Yàþ¸r‰;‡4¶÷&pšÄ­]-p4G-q¿npg€³KÜßw?p?”éŽ_Õ¯_º¾_¿¸ïKÜyà†Û�Ü‹w ¸³ÀU�wBâŽw¸µÀý#ùûW‰4>µlW¨ ¸oKº.à » ¸oH\;p´ŸÀ ܉«ÎÜ­Àý•Äy€£=7÷5‰sWÜRà¾*q¹Ày€S땸 àÜÀe÷‰cÀiÀ] Ü>‰»˜ŽxÀ¥·WâΧ�ç�ÎÜ©t‘·/"os€û I7�üEŒK’€{Xâú€;Ü{¿›ûsàºdÜï"îeàš$xÚcð.pÛ€«“tvË®Ð/Û,é*ïÝÛÀ}8ªûÅÀéøýIIS¬~9”\¸**oZë~Q¬wŸ|Qìçq¼ÄZ$ÿ¸û%á#_|IÌï|™)´îy Ïa<G NŠ5Ú¼ï‰õÇîï‰9]åû £5[</<iÕñ¦ÐZ^x6áÖ-ÿ@¬»;N‰5ÞJ<ý´^€'­±9þUì:ò¯b-±ü±îvOžŠõáó?ë p;O‹µâA<iØþ#±nœ´*ñNkƒ€Ãxïþ2ž4Nñü2�®�vB'»pp y;‡øí€’pp é^ô"­jÈXw?É'ÁWœ:]ã°¼%ÖX­r=ÁÎØ…cEà8"Ÿ4®2Ö'Pvá?¡¼,ŸZrxn%Ñ8.+¦ujwa±˜“¢1­cì†ßÑÜZ0L¶Äþo(›A¹F‰ôøålñ9.ØÙt±g¡`c%I¿¿Ó›×Ӻέý[²ašç`îýzýb±ÿöœy‘Î'ŽÅ´.gÄ«6Å«6Å)GœÕˆC²þ²¥BÐk’ž×cIo}è e:èÛ‘—N€[æIý7‘/>Mã@äÇIã²?YŽwÝ ý ä7“Ѻ>­Ã‘lÿ4A²xȾC “|’|cn|AÚ›÷ëkÙ•­‘œ~¥#DýB“ì;³h?  £±«Ñ¯ÒwëÐUðÙžÔñ½oûùz¿a•Ð÷®fcRFâÑ=1!öº ©¯X¹&C^>ƒ8™ˆÿöo'H>ÚkHënd å‡êò<Ç7Üòv¶i_£|ùQàYƒ±¶çýøµrnO ¤CÖTÒ—&ç7<ûõç Ç\)#ɱÍѰ‚f!ž i®…úálê÷ iÜëCR¯ÃàãWEÙ«Þõ¡~J[êæEN©¦ùœ/•mõÿ|NËÏÜsü½#6ïCÈ)ŸË¤½šª–3OCó¡,ЩûÙýáßLL,VÂ6ÖDsL”Ýsײ,âÌçRFæRŒ<ÌQ¢åNõÙ<ôÖÄD*µÙ×FçBhÎEaýúvàÞ¡u\è€æCl´éLìZÓ1í-Ô—õxÖñù ÄÝDsÍqóóv¾î²>dS¾²²'F­lÍñ YÅœ^ø®qÄ<Ê—Bhá“~§sÇ…Í”–3AZ”›öŒNy±iKøÜ.ñ¾i Z#ßß6åû#&Zc.&ÂW[ø|Í—ôø5Îm×m õr~[«á<ç‚çïØ§†"ëY §ù};èí’žÚ ÅÉdêÛ”¿+¨ç ê‰CÉÒ=S¥cÓTòù+ı#Τ¼NAŽ£Ÿ\o\Е‰!—h)ŠÏÓq‹tþqÈ9ØÌy1tvq¾cÒ™¹,˜öªnAYüáær ymæ¾;Äçµ½oé|/°_øÿ̽;yüñìy7ÄîûñË6„°Ì ö$3öO›i®†}½týŒ4©g—÷ó0¾Š·Û'Åí‚ ˜`,Ät·ƒlÿÞEŒ}d LÏÏãíÕ‰Æ?Cú±fšÅÑ=EZRÆö= éËH7c ú.¢/¦1ÛýaÚm-¤¯`Œ´h@ã<FÏ/™^Úu"?1ÿnSÞûfàs ò¾s4›fAãšMöt4^O(4d—P;ƒÍGCïFÿÂqë^½:“]8§%Î?핳²½£”ÿãSÐ2éèÐ4}&>„'šbø7|-Fý=ÉòókØ#/©ÿ<úŽL¹W†öÙäÀOU¿%öÐÐ~›<¼ç~Kì±Ñ¾%öØxä¯ÜÏêEã¤8�`\î‹Ì >€|Q¹¦bó¼¥§{ãLm”ï?”rÒ:—Ïó%}¼â…Q‹X¢}9ôûeȯixîe˜ü Ú0Qc Q{#Í/Ƚ:çå^‡»G'“ï]Êâk¡9 Ï„-&_áoÈÏ¡y�È} ÷¹zõ>ò‡u§šz¦7K¿äéÑ¿ûîĘè»E¿mÌ$Ã^5™ö£fšÖ(i_h&leõý´S¦{éòu@õ)Ýw ããuª_\í÷UØÎLôû-Àêû½.4&¢ñС3B?²Ÿõ»»ùZ¦Å‹Ì„§õ¤ˆŸQã ¹×Ì)Ö©2ùúÔÁIëS™Æú”GäÒú”æ Ñ~ÈÌ)Ö—ÌëxrÝÓÐKtÎu‹zByÝ_ÐGëšÁ,^®Ü¿üZÌ>¬ùRÿ|ò„ïDi“lN›ÏG¡Nž“:þˆIÇëä:ÓÕ×Ð:n€öŒ_ 1#/¢î8#÷ )±kË=óXdoƒ±ŸÑ•Á·øº›ïDOõ˜û´ÿ|`žX4úAÞÇ¡¯¤>Žy{¡=}þ¤ÐõÐ¥•UÝ€²"øçWs#åš"mÔàoÄyž«¹/‹´¤ŸYk–í€ö›4˜ö�Ö˜÷Pkѽ‡Ùh+‰Ö¡i¯¢±NKò¼�yD™ÅÎ{wý¦‡ó ujÚs}d@¬SÓú`¦© Óš—™É–rá}BèeÒ‰ööÔÙÞæÄµ·éÖìçÐ^6ó:ûTkúû®kúŽY®é‡É®_ØÜÐï'.Ì´†à7coÑ^Kc _®M{ c½ÚÿýdªÏè*êOêß[ÑM¶ò݃ðM¨#UÁ4ÝíF/¼¥§òùa±ç˜òÂû ØÈ“9ýú\¤[7˜¦? }ÃïJY¾„?\Ý`–^\’Òê@ûp+iú:ÒöU¾IåA´Š‡ÒŠãF[ÓÔØ’4½2U# G-êýŸÃ?ä2&\LK¶ó¹Ñ¯Fäp€a ^ÅàaWD>¿/鉆èçÊ2ºZà$/w*K2Öa¼Kuñ:¼Ÿ/è×ðÛ¦ùQŸü|ï­iû´žõVðN‘r(ø&Äáë«zAKñÚñ´ƒ>EÊBãP³,dS(.í܉ø…Œ½Iö£ØŸ¥»Ð_¤¨f,}¨G`LüXÈîï Ù1¾¼™±¿màgé)Úž ]Ý£S?PG8È›¢î‡-kâçòŽÐïíàÈ"ô]0ælW{ù>ž&èü/¥|4¶î, ™Òt§F{龪» OCÞcÐSöÛÀ%ñ~+K'SØžÊÉIò²ž˜xÞE6eIº :0ŸÚû (Oóþœbê÷P~DGe\Lé% ãy •wr^ÐT!.ájÔ=—êòcè½íÑ»Aã!ùaSËi5ll͉ ~œMõžøßý'1¯Êç¨î¥9 +|€Å•ˆóEl¬Ïc‹äÜôc̽×!j¤±I¦ÁÛ4Òh“iøLiË4~òÉAKó3É £½åÐK%üKcžæÎ«Ä9ÎRÈCñàãDxׯÑY¯þÄ"Ú'Õ«EÊ´‹ÌÅü„ú'´£?¸(Ú6á|Os°£^Sý·ÃnÈ60´†æq‘Ï#ès½/üòyÈ.<=À‘= [ðqðTjžÑ5Å¢±ÙƒrØ»v‚Ú|Mšž;ãÂÓ"ø†«¡O’­”ç#Mog¢ýÑ\q%tYú¤¡ŽÒ>”<ذ\M¤[£ˆú¢©|>×…MT_ü‰Øã«à™ ø´˜Çsý$Ö²¹Ÿáý åÑŽ±#••q¶û7bLDvžú(š·ÌÖ„^öhÂW¦þ÷üe³Ï¨HŸ1…Ûð,}Î"á“qÿ¾jÎ"vΔ$³¨¿¾úýXðñþŽÍØC¨{ £{/¿7qaÓO¦G¸ÿ'¦ý/ÔߨoEæ1FNãxÚ_bì;¿71¦r_œ|ï|ŽOôýtó^~ÏÓ8Íiýù79îõ!C§ê²~®ÓÈ|š× ÿ…æÓîçû£l¦ù-ê/Íó[·XÙâw–¼nò:ß™íÆZàcºË:ÝzúcºÓ]sì».:·×»ßàÑ©_7-N}‘‰G‰Gõ7 ûôìiyìÓ3L<rL<²WF×5LÁãòEƒæËzЉÏpv”Ïù<ƒf·þ§¤X>yjZÄ&æ‘­%ºn¿ïÖljå@üòP_w‹¸´̼~9Wúo¹ZVo¥~¡ÏH÷qýgIÓéàqýõ¤¨ìÅ&Ù5SþÎLÁÃáê7ɽGÿ¾”{ôÏ>œÜƒÏDåþûäþ_&¹»²¢rwŒEå><šÒX¹Ÿ–rkRî Cn ‡l%M±¦Ž°/ ltñäösǗõGð8V+w³”›ö}¹žŽ•ûiäÞˆ0W¹óâäÞš€‡=NîOK¹oúprŸøj¬Ü릑{ º¯,·ÿíX¹Ý xlº-VîRnû‡”[‰“û†iäÎFØpæd¹ÏŽÆÊ­$à1P+÷)·'÷ÃÉ]w(Vîw§Üóó8ßXœ@îÜ8¹ƒ xØâä¦ýƒ$÷¡?œÜÇŸŠ•ûåiä>A{æìñÚ9+÷@Õ+båþk)wðcNnGœÜ=ÓÈMû÷(¿ˆîoá{B•¯Eöû4í‘:qGòÇç(âì,ŠÆóo»CFœC¦8݈S‚8nS~ Guë¤ã5Åi@œ[Hî¸4œ ÷EãT˜â¸GEœA=G“é\Y£šâd"ÎBÄñƦsšö‹™Ò¹lÚ5~{r âôšÒ ŠtzMéœ1Å9…8´/¬).:ÐÓž0#ÎSœ�â\Dœ¾¥Ñ8|ßr(Bßa¢ß ú èÛMô|¿©òd„¾ÆD_ zÚŸUú†áø8Ñò/6Åq×õëyˆGãôlS:¹qé8Lql žÆ_ÓÈ5jÚ ,ïOþ;&Ú�Ñž¥0ÐçDõtÒDô4>ž¯§§#ôý!Ð? ú^Ð7DçÄ(Þ¢²5‘:ÜnŠãEœÇ§:Ž~h6å˜öÛ™âT"ÎNºG#•Ëåõ$×'Ï'qZè\[\: h.Ýc3ʲ¶?y3­W…¢é\,é šâЉç,âÜK:[›Î hºLqŽ™âEœ Äé6¥Ó'Ó©6Åé2Åé@ë6Ä¥ãMŽ)N)Î&ÄYFk[¦t*e:ã×Gã¸Lqœˆ³Ä´1waÔ§£ù¼B>¿ÓÚ`Íhð©çÐØÚª\ÝŸœ‰¸4?üd:ÆÝªØ¯Fï4öÆ8‘Ï%=ö Ù¤~ý‹ÀÍq‹9¿È¾½5ý´fÞ#y €×É‹ÆIïb,ÈÇŠòü5͇Ó@âãÙ‰'ýîÀ“îxòãyÏxÒYÃv<§OÚÃÇïŒΠ;¤¦=¹tÖgpb"•ö·1¹GâÁ?FçV3<Fß ÎìzB8|μ¿ z¢¥³Ü ÒüwÄó{4.ÇóeûãI{øžß•g6ŽcœêÀx2ˆ§B÷J¼Š±%Û~UÜÇQƒ'­Iù_w¯ô½½åä«Ñ;X†_ÞÃâx-zKÞkÑûXª^‹ÞÉâ{-z/KïkÑ»Yh?ñš8G~ÏK¯‰3äWðÌ<+ΫxºÏгèxÖçË›ðì<+΢wãyô¬8Ÿ~Œž€^Z—Á3ãuXhÊ'ž•�:¯Uƒ'Íùváy˜€ö¦áyæu1G~Ï‹€¼g¾¼ŽRÞðÜô†ØƒFëu5x÷¾!Öï|xúßëvxv¿!Öñzñ<ü†XÇ;ò†Ø'GëvÇñ~ò ±~w ϳoˆu¼sx޾As ÈëO‘GšS¸ñ~Ïlèß ¨ �Î.®P ¡¹™lèï PGåòZÌ}7aºÏÂ1È×¢†lî<~§ÖBê#Í_ȹ1›{¿nü³"ï:sïu;iÔü!Ë­‚±në×Ueÿ(Ñ:�ßVÄ Šƒ÷,j[ƾ··ßc̘˸ãçø|ÆÎŒ4ÎWÚµÃ$[Gðöètö2צç4:“ í´‘ou÷¿ý¶4½ÏGøyʃ|>$i‰ð—¬g6hûCs´—Ã4§• y(Ôþ§¦Ë£{!@×)é’ò‰fO Ý3DóeМ©²ÂfØj¡³üžšWò�_ùméOŒ|GùÉÐ|vwø2pmm(áÖ_÷?õqØ/Ï”+ÀÛ€:8û êãVÔmÀ®�N!ÜU‡2®á9õâ¹ ÏÀˀijW YrPv­g$E=0òíµ¡øûÕžObû6a|mÍbäfüþÆt®~m¶ØÛE6ù»¿„} ’-¢óHOëÄ‹x²îü¾™¹lãÐ?$±J«ßGñ™p ¥Ys`äy›`ÜÍ~ØÏïà<UذÁ§øzœaÓhÍ’‰u‘ð\M¦:·…öi#]‹»#Dº¶ù;C_Ÿ˜ØçE:éw/øÓÜû‹|]icø»xúµ×ùœÞ?Ñ>…1Hó™5(‡ï¥±1#<€ð&»j¯E良¹lnCÙä4¢½�j�€âÚÙÁaI{Ï‹âIp¸vÒ}P'Ù¾£åy/—S×ö¤;/¬éO<Eó­tݱ³æ îóÔ@™¡ñ+é}6W{øYC‹e?_4tKiÌÏÙÿ’žéÞ?Ò7ô<znâe·1¼F¶ZÇX…wÏãý:ßs„:m{QȪHýÜ�ýx7£žàXíôÀ6OŠOåC²ØÒQ¯hmHyB·¢]Ó>…<…ög=1B¿iý0¯&KÏUh}Kà¨|mbüÉs]Q{šòDûÞs þ²]þýg.¿›å)ð80Bë=Æž�3ôóûœrŽØ©d-ôb5kQçØD–áOì‚oþøa+è\$‡N|ün)ºˆî$0ÕãwߟØW º½ ÄšæƒéãY²+«CO½?1ÖNóºl¿n¬»áÛœÂXy'èObLJkIdïîžwçx§õ¿·¥GO%[e¶W;izh@ÛpctιýFY—TÑFIt޲„òA6]êÏh×Tç6½Cë*éñíóäëjЇ8WýÝwu`$Òö¡”šž‘šË‰ãñSd™Q|ž./#Q/ˆ6EÙžÒóhŸÄÂ'tŠG8mq wŠ›ÒýÄHŠBõé)½ãdZd}”ÆÕ'i½ ‹×J·i¾ýþìÓÌø�i^š"Í6¤yú¤™ùÒ¼<Eš÷!Ío¾?u™^º˜¸L*ï9ÏŽúErR¼¾nùïøÝc°+†Mùùï&Ûî"ðùÊû¢n8’©^@nº{ˆîB�£îušÎ\sQ_nO,Ûðüœäiä‡òÒ‚¼¬v}pvCFÔq+âÂõ(¡>ñö‚æ‹€§lÊž½³DZ{Íé%#½ÏL£ÃM¡Ãß¿7±ÏóÊ›}€òNQÞo"Í‚¦ý¤9:Eš/ Í«?@š¶æðiæÞ›}šŽæÅ)Ò܃4Ïǧ‰:Fiñ;‰¿ºg„êˆQ'_››ªÞ,3ÕâÿYð?9 þ\nÈ|‚xw‹|ˆü÷Œ\ ž)ìñhûÏoÒ}jåYúN§ìÿ‘ö¯Þ¦´;ô÷„‘Ÿÿÿm‚p¿«0þÓoÍEÐng† !ÿåÂÈß õ¯á)x KH§>øjj?°':Ø”wñÙ¥¿š€Ï__<~ˆ0K˜û½vºc.x@¯Ä3¦oT÷‡xy¨/ñýܾQyÁVüî}¢}|fÚî½ |çªbÿË|Ø­Ï;�¿ÛNÞ?Fgªìò\íìÂø¬÷§Æ=b\ÊÏ»(÷„ãQM`è*ø=È÷xh¼f¥}s_ÿ [ææyòÌ « ;ä^ ø½éõóX­ÑÇŸ'±0qÇé2ÍklÁûjøíW&Ʀ34P.|~ãÅÉç›Èï ýßóh¿­‡î€â÷j¯ž'ƈt_ª1Ö[œ%k}ÈÿæAS$—iÐYÚ30¾^œ!Y‹ñ ? <Ã÷˜“þœtà†èJwÀê÷±æòôî¥}¢©â\N`è&ÂùIÆjîûR:K×1í?`òìÊ—àß~ÍXùvštà º‰ñíó´Ë;<ó‘æåûSIs8Z¿žíYr:—ò=\ëçSúº·Œï¼¿ß’û;œ·Zæ‰ðdÙÇÁ']\>_èÞ-uNwùәʑÆÉŸ€^r<izuú2ÆÊ¹xÏó¤ñ}A´ñœ´È=´ßÌâk)öŠcX¿êÒ8–ƼT§íêî“cÛ4¶?”\š²ŸsÓTù¤vÄÃs”ÃT÷©M1öÉð0ÆÜ— Ã(Ò¼yn§½VÐy2ÆæÉ(¯…8‹ý£±ûLi¿ý0äÍDx*Û52Jc5m÷ÈE´çdÿ~ïQ ûä=°ïÏ¢óº·ãÎ�Ä5ÎèûØcHóŸ§]çéöÅœAxº=:Møt­H,w‡”Û»‚ä~‘ËM²²“Ì$¿YîŸLL¼2Žþê9BøŸÇØä<êŠ8_†ú ÿû1ËOšŸ òÌ‘ãŒÞ¼T•EûdQŽò;Ú¤Ÿd?Èé„®69êÎ+¹2.ÆIüΧ<ŒY>5³ü©Þ]#Ç‹úõx½è ï÷§×Ï|è§£(±~º¤~þ<Û5Ï¿“îmm þ’Æü½àå{Óó§3&çg ¹ÎK&–Aœè…} IÖv\$9ü»G<4W<E<:g±ᣨ ›PþÔ_Ð>dš·N–}ÜK©l̳(j;-ö¯}õv<ƒæ»ÒÃ~ÈÏó‹ðËÀÙÕgô/ÏacÙ…ÑùŒfؤ_¦±1ÇÒè8Ï&×xŒ;œqÞÈd®t'€Åc�9wz&£_edbl;ƒ>9³~Ì@“ ýv:ë©Ï¤ßg¬~/£O®ž"Þ!Y7h'‘M¡1^Î rÑžn[NÌ]×Dg¦YÙçÈ>u}Ã0Ê“~§’Üç€#Ù“!Ã0d¸ˆðTíÀH²Öx‚ÏA|ÿý‰Å¤sßz§ït@ç´FcØ+ÙHë ”µ]ð]€rhKç\Œ:@ùt xÓÄœÂ;ïO¤nøˆè×vÿqbŒÒ3æŒ4igèUSšÿ!Ó4¯K祿óužm’ÂO(Gö³O¡žLhJHer>P‰<S™¯ÑÖ†2åüÑ.ô½;àó�*ǹòI`}ÑßSÁ‰ihœ£ò9:áíáåbªOuÈ«(kaW²>/ïó»r½(óûè>kŒ°¿=G0'˜¥_ÁÆ—À¿çÇË‚=/æ-Ú®•QtÉë Þébž¦¬‡—ð¼ÂÛò—u‹}…Ú¥¯™BŽQ)‡ý†ýüDbãîA4¯MAsQÒ8@óÊ4—$Mh^œ‚沤ÉÍñ)h†% 4ßœ‚&hÜ—šÿIwgh¢þÆ“æÞË ZS¡;[Íç¢èYÿ9q>œöœÒz­±Ð=²ÆY)ºgÖ-ï"¦µºg–Ö[Ÿþ·q7Ø1øÞg=KEë-t?-­ÁеÆýôW@£‹úë½ò|€çœ8ã›;(;Sù9 Z/A{¤5ÚçkÃïýïMDμÖÈ{hµYÄûâñ3Jô é ŒŸ'ã;ââÛMñ[ß|þôH\ÜéÒ®‘iqÏ}�¹ïN 7éV³‹s¬ƒ ó<Ã÷éS‡ûütâGýmðKºÝý%~–U³¿0º’ÆtðÉ;Õ]!÷±_L\ÐÎÍ>?yqù9ýòsŒk>'DñéüŒqvÆBã øáj:Ÿ3JòF÷‘=^fZgé¦5pŒëè¬Ü¸*úçlyñã>›Ðø&Î.—Ë3F4ö9¦Êo ˜öYû%(ýù¦ôÅ7±—ÛøöHzÜ}AÆ=‘/ZíÒ¶6ßwn¬œó„s<tÞ‰Ò ݮŮcÓ]‡´6^ñïhÏ€.À¹ãËJ”±ÃøŽ‚;ª¿byßÔ^}Nœ<滩˜ü…õó»�³ßdJÚ´ö¦üÆ€[~c@éåsóJç v㌿G€Ö ´¾çºÊ¸ÿ߈|éƒæôkä™*~oìA=ÙF{êêsÙ˜ä²ÐÞtE®«ð±ª[ÜíJá¶ôî:7šÂ¢|ˆVÌn¤ñà>¢óŸÏV<Û%ß–¹,« ïÛçÊïUU¼0úÐÜè]µuT!ŸƒöžvëT/›h_¿‚ñ½šÙ#ÞD{ø1nr[ÄMŠð)±cKÓ¡LóæÊùèžÎOüƒ•δwèâ¾Íƒ‘8TtFJ>å× ç½’jAG4ä@Ç‹À»Ag}¬4/c¯Yð|w@ô¹xï’qær=ˆ÷Þ=™âƒ·WgoÚñNgPºÕ“ºJþ®ºG§s<6€ÏFߦzí1Œ©¼Þ4Ý1ýM(~ßdnÔ'Ęõu5Õ^±W^{i„Þ)Ÿª8mSw¯î‡œNŒ¥²ÇGü4ÊöŒ´ƒ&Uë¡õÉ ¶q!ñYˆwZKk’a|-¸*àˆ§ÅûÅ]^}uhòÐÓŸ ™ç^hžoêü7©|hß¾Y·|쫈ùµzõõ¿c†9–}#sž }cÆçí¯;Úfh~nŽåñ#Ì‹<ÝhÒà wÔÕ¬g‚iê4|Æ7í⾊SDçUñ~la¿>8<AßBûP×|(?cÜi¤ÑGòÕÈï!àwšL¯‹t}ÐXsØË#¤q—ížÂíOÕž¡¦ø1\ïÄëôoi iS\/í�/¡_Q6 ÂRñnÖ팙½¨7­•îH(hç{öï [´=Vj»Ûé®a&î{¹YÓ}dzΗÑÚ~WGæžžÑ:,õ‡<~Ú}žÀÓ½öDSÙ©ÝuoîO¦oá4ÑþŽÕc ’®ír<X¾Iìâqm¿eû�­os[½v{ãB:#­Ñ>.6ã–^µC÷‚ïNÉÓÅ:BÄïfðSMüÆßüî =X´ÿ™õòvMû>®ÐÙ{ê “Ñþ`¯i]\‡¼TŸÃx®©¹¾ü}áøòhGÊ.øJ~¦Œrð~V> ñ~Ôô{*8d¢éˆ£÷â7é‘×™_ÐÝ>_ÒŸ£;³âêgä^ŽúyíÇB~n°‹3Ä>¼·×tèÅïv”¿¯†ìÈFºƒ'õ1Ê_º8'Dw½-æûªÂ>´‡ 3æÿâý(þãäÛBß”v¯Ôùs&Ü%‰ë3áŽKÜS&܉;`”¸ý&Üy‰ë4áF%n wTâ>oÂ$îQº; ¿}´ÆZÓ¯ÏÓü˜nôMo ÿ׆gÀ ¨îÄø a倊·bÏå¢àý úêÉß±vCOþgäÙô>¾‘CtÒߘt6~IúŸn7í˨ çS¿®=¥çÂ÷¢sÚÖ|òcľ›´ÀþМÀ+áNm/êò>=4|ïí«cûB6àìÚ>ý1Ôñv¹‡Á/ùQ;þtÆýX*úCÛ’#uMh¡¼£k+êïñ½¨�/À8·gv Úcxw@?òI@y?†>ˆçyP¬=R»²)OñüÀ¯ã²ÕO³‘û_]´‡hÉ¡‘³JÏP.ß ÿ6'Wß’¡=‹Îï Ý8g~\ò1|‡wà;äb¬Ú©äc}ºšßÏ>Ø'Çë¢\ÄeÑò0ü½Ó#7î~%íóO§\ÞA²R%ï {òsšû‡LÆýÓVy¦Ò¸z9Ù!Ù/8´C:ùÞ´‡âN´Í´”sÍ÷Ô'+{lÔQÓÉÙì4Æž¦ò¥¶á¨9Ä÷sÑä#×ÐLºà{-è~|Ò+t|™ßƒ@ëKOE¾—…~c(UÛ³Ûª½¼õÌŸæÞ¿{®;ðÄ÷É=.¶7ÔK{¸ÇTèòX‹×Mä>un“Å¢…rŽã~Ô«Ü/¢>ª�>À¥½³ƒÃ’ö8žã€³x?O<ø^¬§tÊ»-ýÀ­»Û–ôŒRä~½ëÕÅ7¥ž|hˆ–ô5'¥¡18µiÒ=Ñ‘þÊ!toðæ{‘~óÕ)øRýý/£t_‰+'²_7ñùb\øCÑ0ºw1Sîi¡òë¥ïÀÀo'Ú‡a£­ìñQ£­?Dûbe[ßF>›ì »ÀëA`_hóį7¼í£~Ðý|¿‡¿àО–i÷è'ñøµ¼“âєػÕG-ýúŸ§ÐÝêѺ]I{wøÞ›§Ä÷Ô +ׇb|/a¿>€~vÜ×òaÚUÝÑ®*ãÚÕw‘¶¹]m‚lÏKÙïL‰î‰. {Ý!ûà2Ižß^ž_Z3ý† GåsDâÿÚ„¿8ã}· ï7…gN–ÍçðÎËMÎ’Œ*Òî1ÑWK_6ʘÒFþI‡Dcèný/ÅœF<ŸŒç7áÈçû ‰ÿœQ—ݰ#î“áyR)qúP1>|¾ }'Ë{ˆ·‰ÇõFàhì×>朗ߋ|§À‹þ‰ì¶_ŒA-ñ­cŽ+`þNæå~%ÕKºãý¥•æÈmê“|®Ë® û–ˆù/ >k ­šÆùtîšxñ³ºÞ�¿êNÐäÊsýW-²Ò‡ZÏ¢òûÓ}ë`1cÏÓX}ŨØkN{îíÒ¾±nŒÛáã",‰|XÈJë¸äÏ’Ü3|£�2ôr2Æ{™¿Qð×ɱûó;¡û~àhí™ÊŽ|7Ƨ´¯ÍE{Û ‡e(wÒ+ödéÜ×ðܲЉ´Ψƒîê"]e›tuLÞÝ÷X²¸3˜ÎnÓûr/|@ƒßsù­è}v~Ç݉ëiyNal Í©mMf|ï=ù)œcLþÔztã{Gñ÷áOŠûùäÉwà­Ñ¤^ãî[£ù¥Léoå)¢ŽÐ݈¹î,±Wée�æAFÕ+Î ÃþÐrûÂÖñý=žž½Í}·×ô臑'ã7¥è\†û—ü×èÞ±¡µÅ´^½qhÙaíIžÏï& =ð5GÚ£© [JºVéÎ9uWЪíÒÚ”±@~íá$<ë§1ñ3Æq6yÏ&ÙŽl©ÛÑ ±Jšlî¡?g=£¶k4…høž”'¹Üt7Žqç#{ö†¶I>ÇÀGEÚ*?£ÇË÷iè.là,êÚéÒª%™ÜCK¿ ÏÃiß®¶{ÈBþ)ÍHÙÌå hö„Í4$W޼gòŸw¥›Ãï<’Ï.}ˆ7s?ÆV]ðÁñ̃�ß«OyW_|Gô–K} )Œp7˜pê«CI¹ ÏôÙqa„ƒ½º:N·ÔÞ ÝþͧýÒR—FþEš¢ò²á~˜¸c’ö5=c£=Œö°QÙp¸;‚ô"ÿöQu¸}´Q3eÛŠÌ3šï²d¢­* ™}ÏQ_óß²]:€7Êu{Ù« CóTkˆúÒºKîfñ>Ÿ¾¿SJïùÝ 4Odæa´ù ð^j„ÑUø½Dþ6ÚÑ<Ä7â’ìïZD{±Ó·Ì¤m¡ûÆí´§Yò$},0¥iø?ðgö9xÝi¥Hšyòw’üm„›å ûEsØ_í5œÈFåA*£¹Â4=v&O¶²/Ñ«qº$¾MÌm_‡)Î!sªñ[I@o—ôô K¾§—ÎhH½¤Ëô û6Ï”>“åLó†7ä ›BãDß÷µ'ôWø½‡ù}" ;žÄïY"|Ùr‰§öËï!ßB~<—CÈʤ>’øÙ4¾O#54*¯o=‘ügÀ>ÑxßõÆöóóm÷XÙØÜYÒ>j%_SÈg—z4òž¾.×dßN6Õ”š#Ï =ئ8fúÖÉw¶fÓ]W(ÿ ”¦üŽ©"å£r%YlTói^tfÝSÙÒ=+ü[¨ê~^ÿ)ŸF]G#þ™ƒÔ‡eq{O¿³)=)+ÑŠ=þ²ÍÅõ¯æ>’ær!;ÝÁAgs¨Ÿ¤{M4SŠÚ&é{}¤GÐQßHOêWCŸš© Ñw¹èw|˜aêÿÄ¢ý!­¹P?¨)TS÷…´Þ$ð{u²ƒÔª¦~ŠOVöÙ_8sßeà¦ë»²§è»(Ô÷1·AM`‹ÿ¶8Þö’OL÷édCïôí[Ñø÷"³cÛÊbº‰ðt'z¶I¯6(è­%i4@¿'íA¯Ãi:í—±Ë:Ù«Šù}…×É=¡NÓoÚ϶ÓôÛßv5+ò[Áo‡ümÔÏ ùNýR¶|Wñž‰÷ƒï‰s–Ü?“̶߬N¶£›¾Æ”º~¦,`t¯Ò˜ôDw©ÐSŽ*tcœËš/ûú*óƒðC OÎ ~I—‹gÀàiå¾—È_Žìèì®GÚž$‰3Ê·cUœ—2ho—´Ö8Ú¿J@["í¹"Ëß!ËßN>&Þ—Êð žiôð3`Ë2a“-&Ÿb`b"ÒŽÒøZš°kÔŸdpÛ™Å÷òS{Δö„ÚloR‹f†¬C™¼4™6Õ›H?L6q_§»Â$-åM•iù4ú­Dý§CÚO;òCwk+&{»Ó”Ÿkd~Œ>T‘>µ“dú–å…΄åÒw»@§¾<4GÚv£/_ eQHç*ñziÈà™HFcœ‘mj¯¤“&[fn³T-ZO¾<Roãúò$YæܾXCšðŸ>Ãç,„ÜŸ½ßÃûߤœ6ʂʌߑ…ßäŸÙD™ Ùdfª=¼¼î) ÿ:šŽMöwF= þk¤Î,²œ)\áõywëõ'<1ñŠÁ×&†Äoa¸­'ÿØý þ„’Ê$Û@ùî &©þ S^âieÆã-yyÈà¯çxÙ?"eWL²'ɺkèÊà•‘ ï—è3Sáé!ý¹Ò·6|lsä‹õOL¼i„%ºoݨϼíѽ·Ò~Ò;åÏx?!ßUÒ·‘Á0­µ¾.¯é¼®Rßß#ʈlïó„} qqž©o¥È=$wïºà~ˆÌ÷EP†£% ïÒgÑñ;ø.D]µÊþ·7»lóæ0>ß™7>£´}Æû1´E#®Ù‡7óX!y¬7ü1éÅÓ‘]3ÂIoÿfÊ?žVúþºY¦¦±õåF\ò"}ºJ:2ÍGÈöbÈ@ußÄW_ãy:ÆÇÖ÷ O ,1êÇ#Ïf{iÔ³Ý<ÉçR£²ü¦”KÆ‹$²«d³øwîa³²Õ4á§È¾Æ¸Ç/IÞq,íÒÓd6Ë6”)í7½ó=Rò=›Ï“ˆwêëòÉñ“ð;¢eg—öËËm1•—QF·Ë4ãÇH6)o†©îdÈ:LuzY‚ºb—}Q?>ž ½›Lñ"úVE¿aøvÙßLYgP®Š¹¤2ä|ö„ {aÐ8¤ÿ߇íìGÒQŸDcï ºñJÔŠ/Û Ó\›¢¦ ÿ˱ŸÛú>ñ"žµïMæ³�|æ Ào8³>8øü?MŒ-`ÏŽÎ ®²Ág0æüLX n˜fÐG¤¡{|Ÿ…ÌÏêrìŸ*|Ôho»“©.9x·q¡•í¶/ o0 î1Ï-oÛU±WÌ~ò)ûCß¼Bëýžð\>ôðq—ÕôLæ¼<¼ïT΀GàÙQÆžÕÿÄÏî>«Û”guú~X4‚n𧼸áG¼÷G1÷o•üÜtWCZßÏ”´¾Ó"Ÿ•³Îç|™OÊcZ$»ív9Že©»ç%ótðž¶{éa÷O6.´§ívp=¤ÝòŽGꢺ°cÌWyÝЇúø¬Ô‡"õ‘*å,œ+u‘ÆùЧ"Ÿ†nRŸ}‡ë&íÙwþ uô{<­ÈëoiÎ6íÙ‹ôm5;ž´NÃÉÏîÓÁo–ïÇMïÓ{—éýˆé½ÏôÞazï5½W˜ÞwšÞkLïGMï›L÷*Óû!Ó{µé½Éôî1½wšÞËñ®¸oy[sÓ8‡ÖNðö§ø_°Ûè\Ú¡÷Ž™ÃiÍ 4£Œ¾'€øU´·Î}¾§OÞÅ[Åë¬íî½Ï/6xXÜU¡?"n.í«s¿0ºÁ{-÷´ÑÒ¢u„¬KŽP=#:º§÷1e<À” yÿ?¯ƒãtc<òsÐT‹)IéKB܇̿!D2í¿=ڣ℟©YØ`ozÒàÁô“¯Œ§ðä;|g`èëø‚ÌSÿm´ß7“1VMRUŒ¯—Àæ]´(7D~±{Úÿ…Î$KzØr5dý~ ¼äê®! ϯ7Ä”¡P’eaØZ„>¯HâUÚ‘ uÔnÜå!5¸¿ÙóWý80´<¹~ßs¡…ó»ƒ}+HsqªçvÐ|9DßâZøcúB× A÷òG¿LºÛBçÁ`ƒ;‘×qž¦%ånj“%I)ü~æ’甡60Ô4HúîuÜŒgr+õc7'Y6…ƒm d[ü\0YÙ5„<v  ?@~Ÿ ZR?ÈI÷1–±V`¡“| %€xð'hÿ€ýžðõ¹%жöz‹ï9%v¼+ùúëKlx·Ñ¾<ÿR[«ýeþÚ ªn Ážý­C=‚ŸýfÆànŒŸ7‡Ò°·WѺú<Ô9upó«éø­eïú‰’ø‰Õ]´-Þ€¬ÏAÖ#6wp裨Góˆ—»—tùJò©ªu!ÍJOòågìåùr¸ë> x›ŸïY%|¾-yg̶ݲ\½$ úJ²xn¿Š¾ý‡z2Dõ$“ïËuÈ*î-£¹gK X(¾•åÓx~!ß3ê!óð+tŽq±e`QPfé»n :Ëí½Hc×r¼S9~!coxÁջ´ŽFá”ø¦ƒ/ÕO úåtøé–;º‡¬÷@Úù–¤kv-2k´ÕŠv`¡ºnY/§s½q¶°›Û¼Ä4Iw+¨;5kˆŽö,ùÕ¡—ÒCµÐGÒüÀЄóª«&l ç/\rµ3Æ–‡ùoÉgþ†@˜×ÃôÃóµ·î!#ŒÒOác¯(.éîtÔeÔYÙF 9–üp`>y*Í }:Sw_y.ÙTµÍ!åe”•ÕÏëHÚ瓽>m²e§LïgLï'MïgÇhƒö÷è4FÙ»qlÿnÛ¯WÒX}é” ½¿´ÆÝâ%Æ· ¨lù}&ÜØçåqPî¾gáïù^Ia'/Qþ´šàÚZúnÍÝþÆKsþÜ8;´_¿É"ïCB;Jk‘^ŒÙ^=eµ„ywó}Î6úÆ€Ú¯ÓùO’¹` Þ¢Ó÷ï2€Ýø¾ Z©ÚÆŸ6è¨ ªÉ¾³ÞÈžâJ҇ƪ·c¬åB߽ϻc“{iýÕÊËžß[?dCòus­Ü»F{›y½… ¿‚ò6Ë¢oæŸþ‹êõ2É'õú;&ôJ´~ü^ žªfºOpüY·õxiùiN;lΓ—ÊÄ}ŸãûªkhO[¬¢<¼(y Ýå =D\/×ðùžgù<§~­Ðz1?×LëÖÁŽ~'U…ObS tòýÇàõ éKÐo7Íßæðcü\o¿^\Žâ=Ü8í퇜gòûuþýÙwQ¿`—õ†¾Ûiy�ãÁûõc«©LvÓ^¸0óìѤ÷ëV<œO{Yiß:ôá9 SýI¾"?ËjŒEmòê§]¥oHwðu(ô«¾¿ÀSÏýøÜªr×öÓy ›çyÉöÒ÷{{‡™zp„tÇÇÔ }7ß·²_ÿKÚŸ‹8¨Ï%ÿFe'Ó‚ž0¶èåsNÿJc7´kâ=\Ãï»ÙæÝOg.¨¿™òçu”¾º™ÏØPÿÑšÞ?&ÃéŒñ¡ôއ];Ë}›ÒË×~ЇX [’º7ˆö²ø[ÚÁ¡¤¹èw‹ƒé7�¬Úæ cpOÎ\P£9ÊÖ ñ\‹öúˆŸÖZ×dZò)è[…?ç´G¬*…“>D8­û7¡ÖÑù¢Ÿ  ÿŒº‰ú؈q†ÅãíE=kzOÞÉçgSʘ8Iu•Ÿû�ž¾±ø« ±¯™ßñHï±'õ“ïño6òý¶÷ñýqÁ›˜?„ö3t/~WzÅy‘9ò¼}ó…ù÷ë7Êtry;„Ë•/†ø7š}¥HÙˆïCï‰ïn6pþlÌ<Q{¥=se›5ìè€É¦v›Þý¦w¯é½Ýôî3½6½Ÿ0½7˜Þëð>?ÈÄŒçå_�þæuÿý÷ÿî߀rÀÍÖ�Ö62îİu'à¡Ú‡komªmÞv뺦¦úmµM«[·íØ^ß컽}K½×רÒÌØ'Zšo©kl­ßâS×ìØºµ¾UmlS›[|jÛ¯·¥ÕW_gæs×ß][7Ôooi}ôöÖÖ–V¤]Û|£O­mjjÙRë«Wתm¾ÖÆæmñøGëL$ß'V—¨’Èç«ÝÒ 6×ú®W} ­õµuª¯E½oƒºµ¥UÝÒÔÒ¶£µ^m¨m®kªoM•q!ŠÚ²UÝÎ…‰°2Ò£G“ºµµv{½¤Çï¦Íµ[>«¶l~ˆrÛPÛ¦n®¯oV·Õ¶n®ÝV¯ni’¶ é$ß––í·¶íh¾õ¡æÚ[=-;úV“îmnƒ´m[ëë*›?£‹–Í¾ÚÆfˆ]ÛÖ¦Û—Í2°Yf°a®ÆæFŒlî7ÝGiÅÆki†–vlñ!ë"ûSòñÖÓ¿¢¬cxP€ ášêÔuå3q‰‹»­©e3T×ZºPß¼¥~ŠèŸà¥ÅØoô]YÛæJÈ]— OqD(4_CKÝ œÛÚÚ²Ýø•[ÕþZŠSjBÜÅ‹´ô¦Js©‰˜j½ÞúºÒXyÚ|Û#’ Z$QC«õ&”ç¦I˜IzˆM7Ên†t}-QäÆä±J†¬mi~¸µ5¡V&¡¦—ËHn©âe¹#¢Æé¤™VmQñbÚßFÞ`&µ•I²™IÙ¶ziw]óÖ–í«¦Ó×Ñh2Íõ¬Cƒ­EKIX'æ£4±þM¼f›<ÛÑV/ìñ¤ò0²˜¨=‘f «Ýák¹†™×A°ŒáÇ-ö¬Yq^÷·6ú&•ç#„œ=£í2ã‰K6Ž· žÉ*ÆÈé3*O</#àpKXrÖ<z_mÓŽR¯—E£Äð\+»·ÉýÉ$^ÒDñï1ì~ÎjÞ-ßÅý„¶ÙòŒD_}ú´f/³9 µïH^ô‰jű-Òˆ>mÿ`Jf*“8Y<DºcGóÊ}¤§ÍMœöS˜¦dŠc?{ÑH6a¨7 w,w² þ2™ÙÎ^ r‡k›?W_Å¿Éý\DW ±SÔÙ˜®EpþxkËïLýr¼8@³±õüþf_xʺmÅÄ5{/³hÓ1ä¹Sô©±6ÌèågáŒI¾fùÖ¡>l«oåÎÐ ²™IÙÉìGÎdLcØ$òÏg#‹™”yeÃ4×Q¢Óü€~ù4嘳ukãƒÆdvemäá–Ï’ßyç|ÔÃCšýÙÙ²¢_ºãŽu¤"ã û=|Ž)—�Wèž™gŠÈ¨€\ºßP p*ĺ†îT–Ðt^ÜoC÷×Ð}6}çÅý7Ç΋;lèœî$ÔímmbÐH¢S×ûàÒ6¶®•¦±NmC3Å0ÐêR´”¥m%ªFÙ$C´…üÑf1Þ5‡·i©uõ[y«oiVsq(8­ÖÓˆå&ÄS·Ö66Õש¹Kën2‰Å¸‡dó¶Ö{ÜÒ¸5ÚÖ`Õ;A‰±,pô= ú}‡‚¾AAߟȈ~wÂøæ„ñ½ ã[Æw&ŒoLß—0¾-Ñ ïü©“÷ùøäw¤é{Ò€Nº‹0»17t°£¹©¥VŒƒyœÍ­õ[Z¶5C×upö 2áåqõCï©‚Nj|¶€!\¹ˆ†£¼f7æ7ú[1ɵTFÄìÞª;Љg¬ÛC%êJ,¾ 2{ãp¨Ÿ¥q¿šx9·ݶyËV§¶¹@[Q\¸<ÿço.¬-¬«­Ã·ï˜˴Y纔šŒ7ÈLÎÏ,#ܲXœÉ‹1ÑD‹pŒÐi¾âÖ <;±üdزØ0³…‹¥Å ºûZëÌ4âwÕ'=·]¢Ánµ¦¥¥©¾¶9&Xœ¤{”&šÈoɺ¡¶µv‹1÷"õƒ“yjhiõ™óù-ç¿Dß#O,NÐU¶¢4Ñß"ü”¦9èo^Þ²csS}¬nሯ˜2æ€&É%ƒi:%wc< ÏŸ¤¹kãi"z’t‚ wM<é\’ȰÜ&ш²“dœ{G< ×…¤¹åñ4B’ˆ‡’sݲÝÛÒ -»öXÿEÖ.þXO{§µDëoL\Ù™^´°e)n”ù6ïS4•ÊDð2#ˆÓ%òˆ:žVü¾ òîW·¶Ö>J|ךøš‚„칟JáR3ÆžZšÂ”K›3á<“héË&E¡T€¼‡m©à(mMt4,Kz™á§‘R(w‚æ™yí“Ê\¤¦IÍAËZáC5ÞøAsؼ¶HÍg‹š!7·íGÛ|õÛåxÏÓÚâ­oõ=š°L•<y>ðÍ[êÌóÎà‚Ķ·qã,<8ëÊá,mKŠÆè“"tŸZZ÷é¥qcÛÈ$§i޽ª¥=Só£j­œoSs·×¶«K›ênŠÍë½Í‘i÷»OÞÛEE|#áî4Ò MÕG,uèŒ"©phÒÀFŒuê£<×¶ì€ÓM\âæIÐ {[·7òNm3_ Yï5»“¬JTö³*{^ÆDM3¹\SfcÞÓÀßãÖ•±:iA›P‡Œ÷pÝÉh“Oú_$~Ó“~,¿éI¿‹œâ7=#sîmÞR»c[Ct}¤B¬9°D>=p2^r–p :e¥lG|È4cà˜QmË#µP°|pcºu “ïjò蚣eËG¦m!‘XK%[WßΫb ­y`™ ¢É ŸT¥™XD ±“Ï䓲h¹•/ö·š»ü&–XîX6³Z?û0kK<WˆE­ëRoðΣfåklFÍÝ–ú!|ç’¨_þÖÊ,L�cÆ{²é=…¿'q˜cz·ów‡¹¦wž]jgi¦w‡‰f¾é=ÉDcåï÷5¶úvÔ6ݽ£F)·¶®®5OýØæ<9îËÝ|ÊòÖ†–íõ·úAŽnÝR¿mË–[ÛZ·Üºz{äVo[ýŽº–[Zë‘Ëe[[ÍÚð_=ke>ÖÈZX3SÙVV‹÷&`ëX ~/Ežµ ¿T‰çf ýW”ž¨â9Æÿ½ÙñÒ'èÙz¼C¹Ÿ16þsäøáJÆvÞ_æà]ŒmÂà܆Žñ0ò§æ€a¹0tƒÀw/eì":ÞrÄq­Ä¸àF¼�âyn€P‡p†¸çÐvà½4ÅK�73vfc5 0ú0cG�—𮵃ž^ü>úC7"͇öõŒy�í€�à` šJdf€Ò�ä�쀋% ìÇñìÀ³¸ hû�]Àu¯G~a§=OU«�x‡�ƒ€Q€YC7ßœ�¼¸j2> øào?\|4}�¶¾ ø;Àd‡[¦ÛX‘.àë€Wï 3VØ Ø �¼ø€vü—¶ètk cßœ\\ó ŒŸ|ðW€S€w�W0¶ðÀç_ †�l”±¥€ÛÛ�û�ÿ8xðÑ·‘& ðEÀ7�¯þ�øèÄ» ZdÚˆ í3mÑŽv—Š6æ`óТ0…¥³«ØB–Á±«Ù5,“]˳,–Í®caE½]®g7 z,ec7²\v»™ýêý-l»•i,Ÿ-gÌÉ Y[ÁŠQKX)s±•¬Œ­bn´¤5ðþÊÙíð±ëÛ¼µ¾- Ëêšš`sÖ=xWs%ü&ùz/ŸO`Ô›<ˆ.íAti¢K{Pt>˜¾¦œoB{Œói#ÌDDûê§åÝ™\¯4:³©ˆir'Ÿìêô[[ëëA'ö+LOʧŠòi®h6d3qD»ñÑí›[¦‘¢M8KM@ï«=ëÖ6ÔoùlÛŽíÓ‘ñ¡ü4á4h˜.\ÖpubZZáNC!êÓ`¼2]0Ÿ˜&\¼ÝWßÚF^Á4„žÖú¶úÖ‡M;&¦!ÓÓˆqï4÷£n´<"ÜLmJBî¤ÝµõÁï\3 M£CŸiÃ… –@•—»QSSÓ”õ 'ˆ¦/;A3]ñ ŠiKPHyz¢™ÌŽ Š®üNOÇ'Õ¦$ià ÔÇýÆ)i¶s7nÊ`Œ–§®Žä8¢È,X³nÝÌDkgCT>¢;fC´n6DwΆhã´DÛÛPœr¢t*²¶ìYÛ ö¬mFÕ6“j›ÞF!xf“Ò6ƒ!C¸ÜL2 ÅL  íƒ9N<So›É¶Íh ÅpaÊ`ZàaJ¾7eÆÖ¥š®¹D©¦k/QªéL”jº¥š®ÉD©¨Íð¿£¢ãß/1æc,o,Šëî¤é·ùï<ð—�)Ë�ä�Šw6�º�}€ÀK€W£€ßRÞ/ È|  Šk�•€jÀf@àa€°ï‘þ“x�NÞ�üâÿ·åª´ÜÑ´£­a\h†'±ýzv·åŽ–Öíµh›mmµÛêïgl£åøl•›[kÑXÿÎòqsëgÇé÷†–ºMõ¢ãGŒ3„CSÛ²ChðYÍ~”Dºäq{;hÞ�^.±f’djkZ”d½k‡Ï»ÃW^¿yÇ6ÑÔ@ÿ3ëÆ˜tëmUõ­vñ¦”ã0›ÁËfÎØWlæ1=F“¶ûëêÉ|VµlØÑäkä–ö¶·rž¼n–’L¾L›¯nkcS=F¨7&?ØÖì…¾­l{òѲ¿HÞº•ÈØcÉ[ehW2w}YOòÖGÄ–³ádÙg2=yûæ6_Ë#[ çhòöúí[¼ÒuùxƒEÁ¸0¥µ^~,EÒ„®”=”rþöhÊ#m"¨‚­%Y«ZZš꛼Ë76×zÛZ|¥­å#A¿•75m¨mlÆøMWÁò;[Û|Í¿?QßÎçì¼þïÁڻ¼²’í:ð;ªîº«²âöJ'øï¿ÿò?c>‰æ�è0H&ÌK<žæ™´xú«�T3æ±Fyz¬Nü{ÛÈÄ¿·³{ð¶ŽÝÅ>ßëðïx§¿l¿zŸøØ˜•u&±Ès•äCóxIqòpÄFæc­¬‘5³mà&æÏÖá×VF,»8ùÏÉ6“üìvµ!Ðlg^V úG!M-~ñ)Zv'Þä²*¶ÝÄTJ3qƒBe«ÙüWÏçèTP݉ðÕŒ/»àm!x²”sª-\FoÌ\žˆ£â—™sÞ6ƒºð(§Ëc)&~÷ñ9Á6Ÿ¶ ùZÆóUÄÒ8íÐ6[<u”VE*ExçÛð¯ñHo>ƒdj2iä!`Y ëˇ5pü ˆW šm<iÔ ¹)·Û@AFf2NeXÎK¤˜ÝÂßò¹LU ÙzèÅïû9×&Y¿¤u—äÓ(e4ôÒ<ƒ¬Ë€œ>Á®ø´�·¡¾]öô—ËoÂå7Yïâ| µ*ÙÉ5•¦T©–Wñ²oFÜ&™jtîwÀ–°áþgü¡ÁA3®È¯Ì?’48ÿlá¹Âó…ÁÂáÂÑ‹…ã…— /^)dE¶"{‘£H)Ê(Ê,Ê.R‹rŠr‹òŠ´"gÑÑ+Ž­8¾âÄŠÁ'WœZqqEfqEqe±·ø\ñùbå6ïm¾ÛŽßvñ¶ÊO‰¿¤£¤³¤«¤»¤·äPI_I äpÉ‘’£%%ÇJŽ—œ(,9YrªätÉ™’³%çJΗK†KFK.–Œ—\*¹\r¥„•ÚJí¥ŽR¥4£4³4»T-Í)Í-Í+ÕJ¥Å¥®RwiyiEie©§´ª´ºtSiMi]iCiS©·ÔWÚ^º³Ô_ÚQÚYÚUÚ]Ú[z¨´¯4Pz¸´z妕5+ëV6¬lZé]é[Ù¾rçJÿÊŽ•+»Vv¯ì]™Q–Y–]VQVYæ)«*«.ÛTVSVWÖPÖTæ-ó•µ—í,ó—u”u–u•u—õ–*ë+ ”.;Rv´l ìXÙñ²eƒe'ËN•.;Sv¶ì\Ùù²`ÙpÙhÙŲñ²Ke—Ë®”±U¶UöUŽU]«ºWõ®:´ªoU`ÕáUGV]5°êتãd U>õβµCW ²þ¢Ž¢î½+|·µß¶ó6ÿmöG ‚QÐ8 •ÂŒÂÌÂìBµ0§0·0¯P+tº Ý…å……•…žÂªÂêÂM…5…u… …M…ÞB_a{áÎBaGagaWawaoá¡Â¾Â@ááÂ#…G  /<Q8Xx²ðTáéÂ3…¼–¹ŠÜEåEE•Ež¢ª¢ê¢ME5EuE EMEÞ"_Q{Ñ¡¢¾¢@Ñá¢ñ¢KEF]:½"§x¼øRñåâ+ÅÈ)¯=ÌÍøÊÉqí„6¨ÔNi§µ3ÚYíœv^ jÃÚ¨vQ×.i—µ+0/¶ü†üöå;—û—w,ï\~¾ X0\0Z@Z!Ybk…ÛUãò»Î»‚.4kæD»ø?­[GJ–”+=^z¢t°ôdé©ÒÓ¥gJÏ–ž+=_,.-½X:^z©ôré•Ræ²¹ì.‡Kqe¸2]Ù.Õ•ãÊuå¹4—ÓUìr¹Ü®rW…«ÒåqU¹ª]› k«ÁÕäòº|®v×NHÞáêtu¹º]½®C®>WÀuØuÄuÔ5à:æ:î:áttrvquã¹vº.ºÆ]—\—]W\l¥m¥}¥c¥²2ceæÊì•êÊœ•³¯¿(øwxx´*­ZÛ¤ÕhuZƒÖ¤y5ŸÖ®íÔüZ‡Ö©uiÝZ¯vHëÓš7ß—ßž¿3ߟߑߙߕßß›(¿/?˜[£üÄV¢Ú<í¢Ä‚d Ž/8Q0Xp²àTÁé‚3g Î%}±`¼àRÁe´æ´9íN‡Sqþç×àÃEÃEêŠM+úVŒ¯È+n*>zÛÀmÇ`õ2J2K²a÷v®ò¯êXÕ9e ?±jpÕÉU§V^ufÕÙUçV'}úáÇàáÌ/Îwå»óËóÉJ{ò«ò«ó7å»–»——/¯X^¹Ü³¼jyõòMËk–×-oX޴ܻܷ<Zã{‡œ}΀ó°óˆó¨sÀyÌyÜyÂ9è<é<å<í<ã<ë<ç<ï :+‹=ÅUÅÅ%®’ÉuBt“?‘œ“b;GãÎKÎËÎ+Î+«X€1 ª˜]ËÐT-O+ÖÊ5êCêåH;¬  ížB› ¢­æñr?–6æ$Jæ<ʃìÀ©¢ÓEÎÅÐi`EEqnI^‰Vâ,! í°úZdZb Z`;Z^w)`b%˜¢A^Ù‡±Î“Ë"¸jxÕ誋«ÆW]ZuyÕ*›3ÔÿSZ6Í®94yÍÔ²‘ß-yÖ4'òý_Ökÿ§ý-£0~öûŒm³`е¥Õ—O‹Û¦?ñ #J²Äs‰òy(ÊÇX‹at5uz*>ôw=à²AÃ1ù�ÅŒY ˆÁ|0ƒ¡„‹S̘G�§ç˜1_ŒÇ`ždÛ͘#”z æ@0óc@Æ\3†®lÁüp"“yÇc0YÀT¥š1æÐ¼m�£Î7cÊ€9±ÀŒ©$ÅŒi%//so æ_(­Ì00ƒ1˜T<Fc0JºsÅ1˜Ïá‘{•Ó‡'söÌÛxb0ó1Æ<ƒ)�f<S LæB3æ�ãŽÁü 0Õ1˜WñÅ`¬¨ô}1˜\`b0…ÀœŠÁ|˜` æ·sþ±U^e¿8]˜6ik[ç6\X‚Ê`#P*¢Á ¥àÈ*Z âéí}oÛËnÛkß[éÍ¡U—" bFl’a` *ÆjÐ`Ä Q²lÎDLöÇtS˜?gâ÷ü|Ï÷ÜÛ¾·êJ²¬ïç>çœçü~ï=çû|QNžfŸœ™Mä× ëˆÌxÝŸÜ røVŸ|dì6Ÿq"»@.9Òp;ù2‡È÷@–y dçû|ò&È_‰ÜŒÙvùý>i™;—<”—~î¦öÙIä+ {æùä,È9"W@^%rdæ|Ÿ4cfÏ!²d‘Í cD9MäÈU"ß™yO^�YNäo %"·cÍxšÈ2«D² u÷R‚,#r¤Û+½š¹È·y6E" {Z}òsWSŸ‚¼µÍ'wc}j'òAŽ%>ù$ö_<&S-óÉ7@""—Aƈüé}ëÈ›X ¯©™x<ÙºÅ'—6D¶É¨u½>Ù r’ÈiWˆüdNŸOþ ÒMäŽwÀg"+@néYØo‰Ý+廲Þ+í;Þ+ŸDªóÚ‰@foóÉ@ºˆ<òæ¶°¬aW–<F© k³=ñGßa³Œ :¹|ÇÔ{÷°9°#,ýQ—soo!x³ÿnÆkÛå~-šA^Ä+2[úYêwØ,Ýå“å Åχ¥ΕnUìÂYún?ŸS{Ã|ö»|Š…ž¾\®5¨I,}Þ7uû|6 ÷ûe9Lä8ȹý“ç#D<Òõ.žp©„ÈFÈÊçä3~€Vu‹D~"}&ò+éáAŸü¤ý`Ø_s­‘-çG å°[ÿˆT¿98E-rB&˼îrÖϘ;Tzï> O9›¡AeuÀ]:æÆS~ª:¤ºœ¤Êi›e‡,1Ee:ù©nEªþCaM'ã'¸ý©ÿ½©:‡©¾^5Õ½ÅB\^`mN$mÇùajC¡Q¦ôÕ0çè&—ªgˆe‘õ?:õ8¼>_¡vî�¹±×'‚œ&›¤¸Ï'€\"òH‘# ŽØó® Ùœmh}ÎØ³³9ÎÆž±ÕQª3Hu‘<ü)Èò£>yd'‘×@Úˆüä¬ËÙžúÍ%›·ã{‘Û@¶>é<4gÙ̃͘ËÙž,¾;)Ëœ+ÞOõúR-sÄžêSMWÁæ<‘.Ç]éö¬qùÓ ›èË>)Ûçµ¼:«%vÁæ*‘Ý ×ˆé&ò,HÉ•nÏK’Í÷e-\ÚSÑäá%ØìKZL¿b¾S>Wasò=uË[“¿è‹àêɼâÒ1å%- ýóýT @®ù(H‘M {ˆäëõ[eB>S¯þ× #¸iò%Óäá!«³|rdœ<<2J6?Ô9g’œ_¤Á'×M’Ï[T”·ÙžÍ¬kB–6è7᤬@ÎÙÒœÉiRh°»Œ%ŸnûâHCèá·ð´Žr¾�r˜Ès ÍóÉK ‘Í@.8"DT, ´O]cØbw”(çEa-o }Þr‰J@NùÈI"6†­q DïÔ–|äõò·í.lÉÖ¤¼ÀOõŠÌ‡JCF5¥u¾+—gøùÔƒ©ô;A&ÈÃù —)çû@Úyl€œ¥|'› %"ûßz(¿ñßO6ÏJ‰Èß�.Ò¨»²“džô‡È?@ž&2³Iÿ™ôòMa½îiÒßòaKTÓ@ƈliÒý•ä\j²ïQ–ìl W¤'@Æ©Oƒ\£œOJŸ14“œ�r •õÈIJõ2ȪÅïAö‘Í?A:Ȧ¾£…ÈnJÕ 2F³iÈ(¥ÚÜθ"ÈE²Ù ò8‘£Íºm’ziVÿóȈRýä‘ß‚LÏ×+ò©oAkPªY-¡M[‹}[v«ÈVJµ¡%œïÛAPŸîy‘Ré&rÊ”žqä»ägäå òZ™ñ®4V»ˆ‘ŒÌºšØ,®Hµ²‚¬'Òƒ'ó6³b}Wg9;,ue©:üØÃk¤`l0¿]Þ&´—BÍ£ÓôåËb›ŠÙ!£’Á#áê/¥UWçEo1Û—Ñ÷ñm´ïJ½$תÝû.“‰ "ÿ3ájœ‰¹òæÇ‹òËÓc^˜RÐ$ÔTh¹Ÿ§PÖ†*)†§¼ ÿ¿w±ô?Q!üÿÎútÅ1µ)@¦­Á¢›Û…X”R¯q×"g˜ÖåñiÉi¦¡Ðªá6ø”zªMFú-ût½]÷§‡bjK¤ yÌ]yéD>5£Ú?Ó´¹ZD„µˆÒÄ)‹TFºÆ£†Y•&$IÖ¥ŠÆÒ´l)R¸T)]J+Ô$Þ¬A ¨Ú!?Z–¡G„ŽbÞ>’·„ŠÈ‚ÿ dUåhRòT*”ÚÕFé¾e˜5 “×idcÙùRÞGb6µ·áj-2¹Zõ}é"ÁZ¤†5ikÕÿMKrèé¬]?µWÿDÙújÓs;ôðMžÕQ^…Þ·|¢ö9Û.;ÜS¤;lu¹þ(_¢4›;7­Á¼Ï|fów¡òB ù“ÐÐAÈETމä éËåäO¬BdóÙž‚ˆ†ã‘<©1#ƒq!êµOæo¬øN÷F½­‰]äÙ™¿õñ†¦Ôa¨ß§Ì †þ­Êüi¬Tã‹R¾°xé1<2X. ÈY®k`ã$‡=Sí3¹*$‘à¨'P�‘ð†ô4“ Q*¤9U=žä&T *ž°¿3–ˆä:¬mñuE~6W­ËTè–™OuiT5éP"ËqM.óöSBˆõÖt­þÄJ±®så†öµbýÊŽµ+DÇêÎ.|mFWgËYËï»êQ´w­]Ÿ|Z(‰jͧ> Nר²Z ›ÓgC .ÄêÙLÙXΉ<9äaêIB`4–ÍW~÷™ŽŒ¤GmÎFèÂöSÈŪò5O õÊL±X`ñ(èʪ̒º³yßVŸ d· ˳ÍX…èö;¼m"%}#DO{Åèy<Rh¦î•kÚÉ­þ|¶ä{oÚ2™©…̶£n)ãd2æ5˜l¡¾…AÔw(æŠC¥ {Mô Û´›y5Z±´úeì W°=%ÒOæÇ=Èã´´Aj–¾l%½nâ^¹ö,ªö¤Z =2Þ™Y¬`òWÓFúÄGˆ4Úe?ckÄŒ-ã^{ýÁ¿Ê,Ÿ’UŸ¹”×’6[s+ÈÓ‹–SðÚ¨a'í©]òãj ‡þÄœi앃8ìDÙª ®/ {Šçªgƒ S}-qÓ'NÚOÝkÁ¢ãu¢ËÌ„C ¦ƒ?I“é¬ j”¨n7+,}ª<TK˦$U¶•L…m“Drˆ•Í YN¦w)gæF¢}“í‹f¶ºýÀ¶Ÿžo~ʯ‰ùaõKbìúÅßuÌR¤Ïµ©Öjêi>ù²fŽ›­a¥º{ÝX ÷ÆV7jÀd’êç/_Éë¦F¹è¯¨ø¦R£Å R¨Bl–ú¹TaQ ,rÏa  QŽq»]ý PK ����v©Æ@������������ ���������íA����META-INF/þÊ��PK ���u©Æ@º8ˆ”��N�������������¤+���META-INF/MANIFEST.MFPK ����'©Æ@����������������������íAñ��com/PK ����'©Æ@����������������������íA��com/sun/PK ����C©Æ@������������ ����������íA9��com/sun/jna/PK ����C©Æ@����������������������íAc��com/sun/jna/darwin/PK ����(©Æ@����������������������íA”��com/sun/jna/ptr/PK ����(©Æ@����������������������íAÂ��com/sun/jna/win32/PK ���'©Æ@%{ªNm���…���&�����������¤ò��com/sun/jna/AltCallingConvention.classPK ���'©Æ@0™T¿���'��3�����������¤£��com/sun/jna/Callback$UncaughtExceptionHandler.classPK ���'©Æ@2z=ªž��a�������������¤³��com/sun/jna/Callback.classPK ���(©Æ@Ã*n5â��¯��*�����������¤‰��com/sun/jna/CallbackParameterContext.classPK ���'©Æ@š‘S¥Æ���1�������������¤³ ��com/sun/jna/CallbackProxy.classPK ���(©Æ@«FŒº+��é��1�����������¤¶ ��com/sun/jna/CallbackReference$AttachOptions.classPK ���(©Æ@áð ��ú��8�����������¤0 ��com/sun/jna/CallbackReference$DefaultCallbackProxy.classPK ���(©Æ@›B¸B£��l ��9�����������¤¦��com/sun/jna/CallbackReference$NativeFunctionHandler.classPK ���(©Æ@_5¸¶��9��#�����������¤  ��com/sun/jna/CallbackReference.classPK ���(©Æ@Îûñ•D��+��'�����������¤—;��com/sun/jna/CallbackResultContext.classPK ���(©Æ@) Á‚��Ø��+�����������¤ =��com/sun/jna/CallbackThreadInitializer.classPK ���(©Æ@é¶ÑÌQ��5��)�����������¤ë?��com/sun/jna/DefaultTypeMapper$Entry.classPK ���(©Æ@#óÆ÷I��+��#�����������¤ƒA��com/sun/jna/DefaultTypeMapper.classPK ���'©Æ@s?g=8��õ��#�����������¤ I��com/sun/jna/FromNativeContext.classPK ���(©Æ@ƒ€T¬©��� ��%�����������¤†J��com/sun/jna/FromNativeConverter.classPK ���(©Æ@F9Ž5��á��,�����������¤rK��com/sun/jna/Function$NativeMappedArray.classPK ���(©Æ@•×Ûü��V��'�����������¤ÌM��com/sun/jna/Function$PointerArray.classPK ���(©Æ@‹Œ¢���×���'�����������¤ P��com/sun/jna/Function$PostCallRead.classPK ���(©Æ@‘Ñ¥Y!��sJ�������������¤ôP��com/sun/jna/Function.classPK ���(©Æ@ÎϫЦ���ß��� �����������¤…r��com/sun/jna/FunctionMapper.classPK ���(©Æ@šcЬ��b��*�����������¤is��com/sun/jna/FunctionParameterContext.classPK ���(©Æ@óNñ‰‘����'�����������¤]u��com/sun/jna/FunctionResultContext.classPK ���(©Æ@.|Vü¨��ë �������������¤3w��com/sun/jna/IntegerType.classPK ���(©Æ@â^uï§���û���"�����������¤~��com/sun/jna/InvocationMapper.classPK ���(©Æ@™Æ~ÈE��@��$�����������¤ý~��com/sun/jna/LastErrorException.classPK ���(©Æ@<KCÇ���������������¤„‚��com/sun/jna/Library$1.classPK ���(©Æ@b|A±����.�����������¤„„��com/sun/jna/Library$Handler$FunctionInfo.classPK ���(©Æ@doÆR��‚��1�����������¤†��com/sun/jna/Library$Handler$FunctionNameMap.classPK ���(©Æ@BÀÌä ��E��!�����������¤"‰��com/sun/jna/Library$Handler.classPK ���(©Æ@ð6,NŠ��z�������������¤E“��com/sun/jna/Library.classPK ���(©Æ@ÀýŽHS��ÿ��%�����������¤•��com/sun/jna/Memory$SharedMemory.classPK ���(©Æ@Çežì¤ ��)!�������������¤œ—��com/sun/jna/Memory.classPK ���(©Æ@Ú|ðF��æ��(�����������¤v¥��com/sun/jna/MethodParameterContext.classPK ���(©Æ@­šÁ…����%�����������¤=§��com/sun/jna/MethodResultContext.classPK ���(©Æ@•4wÜP��Q�������������¤©��com/sun/jna/Native$1.classPK ���(©Æ@âmÀ1 ��«�������������¤«��com/sun/jna/Native$2.classPK ���(©Æ@3¤wÞ0��Å�������������¤å¬��com/sun/jna/Native$3.classPK ���(©Æ@Î/F��B�������������¤M®��com/sun/jna/Native$4.classPK ���(©Æ@¦ 8ÎX���������������¤Ë°��com/sun/jna/Native$5.classPK ���(©Æ@çéaY˜��–�������������¤[³��com/sun/jna/Native$6.classPK ���(©Æ@®ü t��ª�������������¤+µ��com/sun/jna/Native$7.classPK ���(©Æ@b+éµx�� �������������¤z¶��com/sun/jna/Native$8.classPK ���(©Æ@º±)^i��ú�������������¤*¹��com/sun/jna/Native$AWT.classPK ���(©Æ@²†��Ž�� �����������¤Í¼��com/sun/jna/Native$Buffers.classPK ���(©Æ@8Y§¥���Ö���%�����������¤š¾��com/sun/jna/Native$ffi_callback.classPK ���(©Æ@!p|„bB��-�������������¤‚¿��com/sun/jna/Native.classPK ���(©Æ@ `ë§ï��¬��!�����������¤�com/sun/jna/NativeLibrary$1.classPK ���(©Æ@ç›@s-��Ù��!�����������¤H�com/sun/jna/NativeLibrary$2.classPK ���(©Æ@ë$N²��p6�������������¤´�com/sun/jna/NativeLibrary.classPK ���(©Æ@,üá[���������������¤£#�com/sun/jna/NativeLong.classPK ���(©Æ@P8ÿµ���'�������������¤8%�com/sun/jna/NativeMapped.classPK ���(©Æ@'Ø^“��"��'�����������¤)&�com/sun/jna/NativeMappedConverter.classPK ���(©Æ@­,‰x��× �������������¤-�com/sun/jna/NativeString.classPK ���(©Æ@jTyy��+�������������¤C2�com/sun/jna/Platform.classPK ���(©Æ@8ÈÖ“���º��������������¤ô9�com/sun/jna/Pointer$1.classPK ���(©Æ@MTw4��&�� �����������¤À:�com/sun/jna/Pointer$Opaque.classPK ���(©Æ@Ꜭeõ"�� Q�������������¤2@�com/sun/jna/Pointer.classPK ���(©Æ@bÞü ��é �������������¤^c�com/sun/jna/PointerType.classPK ���(©Æ@ëB›(Ò��Ÿ �������������¤¤h�com/sun/jna/StringArray.classPK ���(©Æ@àºÀ..��Á�������������¤±m�com/sun/jna/Structure$1.classPK ���(©Æ@²T¥š��w��*�����������¤o�com/sun/jna/Structure$2$StructureSet.classPK ���(©Æ@ÓEC^F���������������¤üs�com/sun/jna/Structure$2.classPK ���(©Æ@ÁY��&��)�����������¤}u�com/sun/jna/Structure$AutoAllocated.classPK ���(©Æ@qùˆÐ’���Ã���'�����������¤w�com/sun/jna/Structure$ByReference.classPK ���(©Æ@€æ×:Ž���»���#�����������¤ôw�com/sun/jna/Structure$ByValue.classPK ���(©Æ@Dùð]G�� ��,�����������¤Ãx�com/sun/jna/Structure$FFIType$FFITypes.classPK ���(©Æ@x ±#o��W��*�����������¤T{�com/sun/jna/Structure$FFIType$size_t.classPK ���(©Æ@,@XV��c��#�����������¤ }�com/sun/jna/Structure$FFIType.classPK ���(©Æ@ îza��^��&�����������¤]‹�com/sun/jna/Structure$LayoutInfo.classPK ���(©Æ@­õç`¾��Ø��'�����������¤Ž�com/sun/jna/Structure$MemberOrder.classPK ���(©Æ@ð3FÍž����'�����������¤�com/sun/jna/Structure$StructField.classPK ���(©Æ@ŽÓ`м6��¦q�������������¤è’�com/sun/jna/Structure.classPK ���(©Æ@݃笞��C��&�����������¤ÝÉ�com/sun/jna/StructureReadContext.classPK ���(©Æ@ñzŒq��Ü��'�����������¤¿Ë�com/sun/jna/StructureWriteContext.classPK ���(©Æ@ …Û���*��!�����������¤uÍ�com/sun/jna/ToNativeContext.classPK ���(©Æ@x-Ƨ�����#�����������¤Î�com/sun/jna/ToNativeConverter.classPK ���(©Æ@±|B?‡���Ã��������������¤wÏ�com/sun/jna/TypeConverter.classPK ���(©Æ@\Fƒû­����������������¤;Ð�com/sun/jna/TypeMapper.classPK ���(©Æ@ñ”ëÅ ��ˆ�������������¤"Ñ�com/sun/jna/Union.classPK ���(©Æ@ÞÑ¥Wô��æ�������������¤Û�com/sun/jna/WString.classPK ���(©Æ@´‚»ù��ù��'�����������¤GÞ�com/sun/jna/WeakIdentityHashMap$1.classPK ���(©Æ@¯€`U�� ��;�����������¤…à�com/sun/jna/WeakIdentityHashMap$IdentityWeakReference.classPK ���(©Æ@Æa“:€�� ��%�����������¤3ã�com/sun/jna/WeakIdentityHashMap.classPK ���C©Æ@Ö¤ÝÂ]�Œ�(�����������¤öé�com/sun/jna/darwin/libjnidispatch.jnilibPK ���(©Æ@¿“ž/��¹��!�����������¤þG�com/sun/jna/ptr/ByReference.classPK ���(©Æ@†³¤��ï��%�����������¤lI�com/sun/jna/ptr/ByteByReference.classPK ���(©Æ@ùù¦��ú��'�����������¤SK�com/sun/jna/ptr/DoubleByReference.classPK ���(©Æ@!±²¤��ô��&�����������¤>M�com/sun/jna/ptr/FloatByReference.classPK ���(©Æ@þ†«��Þ��$�����������¤&O�com/sun/jna/ptr/IntByReference.classPK ���(©Æ@àæ gª��ð��%�����������¤Q�com/sun/jna/ptr/LongByReference.classPK ���(©Æ@r—ƒâ��Ë��+�����������¤òR�com/sun/jna/ptr/NativeLongByReference.classPK ���(©Æ@Áüöe¯��R��(�����������¤U�com/sun/jna/ptr/PointerByReference.classPK ���(©Æ@ Åšâ¤��ô��&�����������¤W�com/sun/jna/ptr/ShortByReference.classPK ���(©Æ@|³ƒ���™��������������¤úX�com/sun/jna/win32/StdCall.classPK ���(©Æ@Åð˜”��û ��-�����������¤ºY�com/sun/jna/win32/StdCallFunctionMapper.classPK ���(©Æ@–™ΰ���#��6�����������¤™_�com/sun/jna/win32/StdCallLibrary$StdCallCallback.classPK ���(©Æ@'ÄÊi��>��&�����������¤`�com/sun/jna/win32/StdCallLibrary.classPK ���(©Æ@ÂäÐ÷ÿ��À��,�����������¤Jb�com/sun/jna/win32/W32APIFunctionMapper.classPK ���(©Æ@z¸Áö‰����'�����������¤“e�com/sun/jna/win32/W32APIOptions$1.classPK ���(©Æ@sþVƒ��Ž��'�����������¤ag�com/sun/jna/win32/W32APIOptions$2.classPK ���(©Æ@`\½l¬��‡��%�����������¤)i�com/sun/jna/win32/W32APIOptions.classPK ���(©Æ@þóuͽ��Ú��*�����������¤k�com/sun/jna/win32/W32APITypeMapper$1.classPK ���(©Æ@³Œº³¾��²��*�����������¤n�com/sun/jna/win32/W32APITypeMapper$2.classPK ���(©Æ@‘s!…��'��(�����������¤#q�com/sun/jna/win32/W32APITypeMapper.classPK ����v©Æ@����������������������íAît�com/sun/jna/win32-x86/PK ���£Xo? )Š]ð>�„»�%�����������¤"u�com/sun/jna/win32-x86/jnidispatch.dllPK ����v©Æ@����������������������íAU´�com/sun/jna/aix-ppc/PK ��� ƒi@ò!±¾Ë²�ÕE �$�����������¤‡´�com/sun/jna/aix-ppc/libjnidispatch.aPK ����v©Æ@����������������������íA”g�com/sun/jna/aix-ppc64/PK ���xƒi@’2 £Ñ�ÿ[ �&�����������¤Èg�com/sun/jna/aix-ppc64/libjnidispatch.aPK ����v©Æ@����������������������íA¯9 �com/sun/jna/linux-i386/PK ���OJ;@4¯ÌɆ��ˆ]�(�����������¤ä9 �com/sun/jna/linux-i386/libjnidispatch.soPK ����v©Æ@����������������������íAóÀ �com/sun/jna/linux-amd64/PK ���‰I;@ÊqK¥��ª’�)�����������¤)Á �com/sun/jna/linux-amd64/libjnidispatch.soPK ����v©Æ@����������������������íA»f �com/sun/jna/linux-arm/PK ���уg?ØîÞ+«��7œ�'�����������¤ïf �com/sun/jna/linux-arm/libjnidispatch.soPK ����v©Æ@����������������������íA_ �com/sun/jna/linux-ia64/PK ���5‰g?Šanê��ëQ�(�����������¤” �com/sun/jna/linux-ia64/libjnidispatch.soPK ����v©Æ@����������������������íAHý �com/sun/jna/linux-ppc/PK ���2ii?}ì8±c¤��®�'�����������¤|ý �com/sun/jna/linux-ppc/libjnidispatch.soPK ����v©Æ@����������������������íA$¢�com/sun/jna/linux-ppc64/PK ���„@g?Qdx¯Ø«��E �)�����������¤Z¢�com/sun/jna/linux-ppc64/libjnidispatch.soPK ����v©Æ@����������������������íAyN�com/sun/jna/sunos-x86/PK ���,Zg? ØIÐW �� ™�'�����������¤­N�com/sun/jna/sunos-x86/libjnidispatch.soPK ����v©Æ@����������������������íAIï�com/sun/jna/sunos-amd64/PK ���4Zg?ëm½õ��ØË�)�����������¤ï�com/sun/jna/sunos-amd64/libjnidispatch.soPK ����v©Æ@����������������������íA‰¥�com/sun/jna/sunos-sparc/PK ���"‹g?%ßü%©��¨Ð�)�����������¤¿¥�com/sun/jna/sunos-sparc/libjnidispatch.soPK ����v©Æ@����������������������íA+O�com/sun/jna/sunos-sparcv9/PK ���‹g?“äÃÙ±���+�����������¤cO�com/sun/jna/sunos-sparcv9/libjnidispatch.soPK ����v©Æ@����������������������íA…�com/sun/jna/freebsd-i386/PK ���uVc?[¤w7Ù��E9�*�����������¤¼�com/sun/jna/freebsd-i386/libjnidispatch.soPK ����v©Æ@����������������������íAÝ�com/sun/jna/freebsd-amd64/PK ���—Vc?™R‹ä—��µw�+�����������¤‚�com/sun/jna/freebsd-amd64/libjnidispatch.soPK ����v©Æ@����������������������íAB�com/sun/jna/win32-amd64/PK ���˜su?úÅõì6���'�����������¤x�com/sun/jna/win32-amd64/jnidispatch.dllPK ����v©Æ@����������������������íA©Q�com/sun/jna/w32ce-arm/PK ���kSo?îBha¾e��Ð �%�����������¤ÝQ�com/sun/jna/w32ce-arm/jnidispatch.dllPK����’�’�Y-��Þ·��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016305�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_decoder.cpp������������������������������������������������������0000664�0000000�0000000�00000004424�12710376503�0021316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "lzw/lzw_decoder.h" #include "lzw/lzw_dictionary.h" #include "base_cpp/scanner.h" using namespace indigo; IMPL_ERROR(LzwDecoder, "LZW decoder"); CP_DEF(LzwDecoder); LzwDecoder::LzwDecoder( LzwDict &NewDict, Scanner &NewIn ) : _dict(NewDict), _bitin(_dict.getBitCodeSize(), NewIn), CP_INIT, TL_CP_GET(_symbolsBuf) { } bool LzwDecoder::isEOF( void ) { if (_bitin.isEOF()) { if (_symbolsBuf.size()) return false; else return true; } return false; } int LzwDecoder::get( void ) { if (_symbolsBuf.size()) return _symbolsBuf.pop(); int NextCode; if (_bitin.isEOF()) throw Error("end of stream"); _bitin.readBits(NextCode); while (NextCode > _dict.getAlphabetSize()) { _symbolsBuf.push(_dict.getChar(NextCode)); NextCode = _dict.getPrefix(NextCode); } return NextCode; } // // LzwScanner // IMPL_ERROR(LzwScanner, "LZW scanner"); LzwScanner::LzwScanner (LzwDecoder &decoder) : _decoder(decoder) { } void LzwScanner::read (int length, void *res) { char *data = (char *)res; for (int i = 0; i < length; i++) data[i] = _decoder.get(); } void LzwScanner::skip (int n) { throw Error("skip is not implemented"); } bool LzwScanner::isEOF () { return _decoder.isEOF(); } int LzwScanner::lookNext () { throw Error("lookNext is not implemented"); } void LzwScanner::seek (int pos, int from) { throw Error("seek is not implemented"); } int LzwScanner::length () { throw Error("length is not implemented"); } int LzwScanner::tell () { throw Error("tell is not implemented"); } byte LzwScanner::readByte () { return _decoder.get(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_decoder.h��������������������������������������������������������0000664�0000000�0000000�00000003164�12710376503�0020763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __lzw_decoder_h__ #define __lzw_decoder_h__ #include "base_cpp/bitinworker.h" #include "base_cpp/scanner.h" #include "lzw/lzw_dictionary.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT LzwDecoder { public: DECL_ERROR; LzwDecoder( LzwDict &NewDict, Scanner &NewIn ); bool isEOF( void ); int get( void ); private: LzwDict &_dict; BitInWorker _bitin; CP_DECL; TL_CP_DECL(Array<byte>, _symbolsBuf); // no implicit copy LzwDecoder( const LzwDecoder & ); }; class LzwScanner : public Scanner { public: LzwScanner (LzwDecoder &decoder); virtual void read (int length, void *res); virtual void skip (int n); virtual bool isEOF (); virtual int lookNext (); virtual void seek (int pos, int from); virtual int length (); virtual int tell (); virtual byte readByte (); DECL_ERROR; private: LzwDecoder &_decoder; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_dictionary.cpp���������������������������������������������������0000664�0000000�0000000�00000015427�12710376503�0022063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "lzw/lzw_dictionary.h" #include "base_cpp/bitoutworker.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "base_cpp/list.h" #include "base_cpp/pool.h" using namespace indigo; IMPL_ERROR(LzwDict, "LZW dictionary"); CP_DEF(LzwDict); LzwDict::LzwDict( void ) : CP_INIT, TL_CP_GET(_storage), TL_CP_GET(_nextPointers), TL_CP_GET(_hashKeys) { reset(); } void LzwDict::reset () { _alphabetSize = -1; _modified = false; _hashingShift = 8; _bitcodeSize = BITCODE_MAX; _maxCode = (1 << _bitcodeSize) - 1; } LzwDict::LzwDict( int NewAlphabetSize, int NewBitCodeSize ) : _modified(false), CP_INIT, TL_CP_GET(_storage), TL_CP_GET(_nextPointers), TL_CP_GET(_hashKeys) { init(NewAlphabetSize, NewBitCodeSize); } void LzwDict::init( int NewAlphabetSize, int NewBitCodeSize ) { if (NewBitCodeSize > BITCODE_MAX || NewBitCodeSize < BITCODE_MIN) throw Error("unexpected bit code size"); _bitcodeSize = NewBitCodeSize; _hashingShift = 8; _alphabetSize = NewAlphabetSize; _nextCode = _alphabetSize + 1; _freePtr = 0; _maxCode = (1 << _bitcodeSize) - 1; _storage.clear(); _hashKeys.resize(SIZE); _nextPointers.resize(SIZE); for (int i = 0; i < SIZE; i++) { _nextPointers[i] = -1; _hashKeys[i] = -1; } _modified = true; } /* Prefix++Char hashing function */ int LzwDict::hashFunction( const int Prefix, const byte Char ) const { int Code; Code = (Char << _hashingShift) ^ Prefix; return Code; } int LzwDict::getBitCodeSize( void ) { return _bitcodeSize; } /* Add dictionary element function */ bool LzwDict::addElem( const int NewPrefix, const byte NewChar, int HashIndex ) { int j; _DictElement D(NewPrefix, NewChar); if (_nextCode <= _maxCode) { if (_hashKeys[HashIndex] == -1) _hashKeys[HashIndex] = _freePtr; else { j = _hashKeys[HashIndex]; while (true) { if (_nextPointers[j] == -1) { _nextPointers[j] = _freePtr; break; } j = _nextPointers[j]; } } _storage.push(D); _freePtr++; _nextCode++; _modified = true; return true; } return false; } /* Dictionary search function */ int LzwDict::dictSearch( const int SearchPrefix, const byte SearchChar, int HashIndex ) const { int j; if (_hashKeys[HashIndex] == -1) return -1; j = _hashKeys[HashIndex]; _DictElement D = _storage[j]; while (true) { if (D.AppendChar == SearchChar && D.Prefix == SearchPrefix) return j + _alphabetSize + 1; if (_nextPointers[j] == -1) return -1; j = _nextPointers[j]; D = _storage[j]; } return 1; } int LzwDict::getAlphabetSize( void ) { return _alphabetSize; } /* Get indexed prefix function */ int LzwDict::getPrefix( const int Code ) const { if (!isInitialized()) throw Error("getPrefix(): not initialized"); return _storage[Code - _alphabetSize - 1].Prefix; } /* Get indexed appended char function */ byte LzwDict::getChar( const int Code ) const { if (!isInitialized()) throw Error("getChar(): not initialized"); return _storage[Code - _alphabetSize - 1].AppendChar; } /* Get dictionary size function */ int LzwDict::getSize( void ) const { return _storage.size(); } bool LzwDict::isInitialized( void ) const { return _alphabetSize != -1 ? true : false; } /* Write lzw dictionary function */ void LzwDict::save( Output &_output ) { int n = _storage.size(); _modified = false; _output.writeBinaryInt(_alphabetSize); _output.writeBinaryInt(_nextCode); _output.writeBinaryInt(n); _output.writeBinaryInt(_bitcodeSize); for (int i = 0; i < n; i++) { _output.writeBinaryInt(_storage[i].Prefix); _output.writeByte(_storage[i].AppendChar); } _output.writeBinaryInt(_freePtr); } /* Write full (with hash chains, for future modifying) * * lzw dictionary function */ void LzwDict::saveFull( Output &_output ) { int i, n = _storage.size(), code, HashCode; QS_DEF(Array<int>, MarkedHashCodes); MarkedHashCodes.resize(SIZE); for (i = 0; i < SIZE; i++) MarkedHashCodes[i] = 0; /* Save main dictionary part */ save(_output); for (i = 0; i < n; i++) { code = hashFunction(_storage[i].Prefix, _storage[i].AppendChar); HashCode = code; if (!MarkedHashCodes[HashCode]) { _output.writeBinaryInt(code); /* List head */ code = _hashKeys[code]; _output.writeBinaryInt(code); /* Other elements */ while (_nextPointers[code] != -1) { code = _nextPointers[code]; _output.writeBinaryInt(code); } _output.writeBinaryInt(-1); MarkedHashCodes[HashCode] = 1; } } } void LzwDict::resetModified( void ) { _modified = false; } bool LzwDict::isModified( void ) { return _modified; } /* Read full (with hash chains, for future modifying) * * lzw dictionary function */ void LzwDict::load( Scanner &_scanner ) { int i, j, HashCode, n, k; _modified = false; _alphabetSize = _scanner.readBinaryInt(); _nextCode = _scanner.readBinaryInt(); n = _scanner.readBinaryInt(); _bitcodeSize = _scanner.readBinaryInt(); _maxCode = (1 << _bitcodeSize) - 1; _storage.clear(); _storage.resize(n); for (i = 0; i < n; i++) { k = _scanner.readBinaryDword(); _storage[i].Prefix = k; _storage[i].AppendChar = _scanner.readByte(); } _freePtr = _scanner.readBinaryInt(); _hashKeys.clear_resize(SIZE); _nextPointers.clear_resize(SIZE); for (i = 0; i < SIZE; i++) { _nextPointers[i] = -1; _hashKeys[i] = -1; } while (!_scanner.isEOF()) { HashCode = _scanner.readBinaryInt(); j = _scanner.readBinaryInt(); _hashKeys[HashCode] = j; i = j; j = _scanner.readBinaryInt(); while (j != -1) { _nextPointers[i] = j; i = j; j = _scanner.readBinaryInt(); } } } LzwDict::~LzwDict( void ) { } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_dictionary.h�����������������������������������������������������0000664�0000000�0000000�00000005343�12710376503�0021524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __lzw_dictionary_h__ #define __lzw_dictionary_h__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/tlscont.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Output; class Scanner; class DLLEXPORT LzwDict { public: DECL_ERROR; /* Dictionary constants */ enum { BITCODE_MIN = 8, BITCODE_MAX = 16, SIZE = 65536 }; LzwDict( void ); LzwDict( int NewAlphabetSize, int NewBitCodeSize ); void init( int NewAlphabetSize, int NewBitCodeSize ); /* Prefix++Char hashing function */ int hashFunction( const int SearchPrefix, const byte SearchChar ) const; int getBitCodeSize( void ); bool addElem( const int NewPrefix, const byte NewChar, int HashIndex ); int dictSearch( const int SearchPrefix, const byte SearchChar, int HashIndex ) const; int getAlphabetSize( void ); int getCode( const int Index ) const; int getPrefix( const int Index ) const; byte getChar( const int Index ) const; int getSize( void ) const; bool isInitialized( void ) const; void save( Output &_output ); void saveFull( Output &_output ); bool isModified( void ); void resetModified( void ); void load( Scanner &_scanner ); ~LzwDict( void ); void reset (); private: /* Dictionary element representation type */ struct _DictElement { int Prefix; /* Coded string prefix */ byte AppendChar; /* String appended char */ _DictElement( int NewPrefix, byte NewChar ) : Prefix(NewPrefix), AppendChar(NewChar) { } }; int _hashingShift, /* Hashing function shift */ _bitcodeSize, _alphabetSize, _maxCode, _nextCode, _freePtr; /* Free hash table elements list pointer */ bool _modified; CP_DECL; TL_CP_DECL(Array<_DictElement>, _storage); /* Dictionary */ TL_CP_DECL(Array<int>, _nextPointers); TL_CP_DECL(Array<int>, _hashKeys); LzwDict( const LzwDict & ); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_encoder.cpp������������������������������������������������������0000664�0000000�0000000�00000004333�12710376503�0021327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "lzw/lzw_encoder.h" #include "base_cpp/bitoutworker.h" #include "base_cpp/output.h" using namespace indigo; IMPL_ERROR(LzwEncoder, "LZW encoder"); LzwEncoder::LzwEncoder( LzwDict &NewDict, Output &NewOut ) : _dict(NewDict), _bitout(_dict.getBitCodeSize(), NewOut) { _string = -1; _char = 0; _isFinished = false; } void LzwEncoder::start( void ) { _isFinished = false; } void LzwEncoder::send( int NextSymbol ) { int Index, HashIndex; if (_string < 0) { _string = NextSymbol; return; } _char = NextSymbol; HashIndex = _dict.hashFunction(_string, _char); /* 'string++char' is in dictionary */ if ((Index = _dict.dictSearch(_string, _char, HashIndex)) != -1) { _string = Index; return; } _dict.addElem(_string, _char, HashIndex); _bitout.writeBits(_string); _string = _char; } void LzwEncoder::finish( void ) { if (!_isFinished) { _bitout.writeBits(_string); _bitout.close(); _string = -1; _isFinished = true; } } LzwEncoder::~LzwEncoder( void ) { finish(); } // // LzwOutput // LzwOutput::LzwOutput (LzwEncoder &encoder) : _encoder(encoder) { } void LzwOutput::writeByte (byte value) { _encoder.send(value); } void LzwOutput::write (const void *data_, int size) { const char *data = (const char *)data_; for (int i = 0; i < size; i++) _encoder.send(data[i]); } void LzwOutput::seek (int offset, int from) { throw Error("can not 'seek' in LZW-output"); } int LzwOutput::tell () { throw Error("can not 'tell' in LZW-output"); } void LzwOutput::flush () { } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/lzw/lzw_encoder.h��������������������������������������������������������0000664�0000000�0000000�00000003167�12710376503�0021000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __lzw_encoder_h__ #define __lzw_encoder_h__ #include "base_cpp/bitoutworker.h" #include "base_cpp/output.h" #include "lzw/lzw_dictionary.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT LzwEncoder { public: DECL_ERROR; LzwEncoder( LzwDict &NewDict, Output &NewOut ); void start( void ); void send( int NextSymbol ); void finish( void ); ~LzwEncoder( void ); private: LzwDict &_dict; BitOutWorker _bitout; int _string; byte _char; bool _isFinished; // no implicit copy LzwEncoder( const LzwEncoder & ); }; class LzwOutput : public Output { public: LzwOutput (LzwEncoder &encoder); virtual void write (const void *data, int size); virtual void writeByte (byte value); virtual void seek (int offset, int from); virtual int tell (); virtual void flush (); private: LzwEncoder &_encoder; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016422�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/algebra.h�����������������������������������������������������������0000664�0000000�0000000�00000026343�12710376503�0020200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _ALGEBRA_H_ #define _ALGEBRA_H_ #include <math.h> #include "base_c/defs.h" #include "base_cpp/exception.h" #define SQR(x) ((x) * (x)) #define DEG2RAD(x) ((x) * PI / 180) #define RAD2DEG(x) ((x) * 180 / PI) #ifdef INFINITY #undef INFINITY #endif #ifdef PI #undef PI #endif namespace indigo { const float EPSILON = 0.000001f; const float PI = 3.14159265358979323846f; const float INFINITY = 1000000.f; struct Transform3f; struct Vec3f; struct Vec2f { DECL_ERROR; Vec2f () : x(0), y(0) {} Vec2f (const Vec2f &a) : x(a.x), y(a.y) {} Vec2f (float xx, float yy) : x(xx), y(yy) {} float x, y; inline void set (float xx, float yy) { x = xx; y = yy; } inline void copy (const Vec2f &a) { x = a.x; y = a.y; } inline void zero () { x = 0; y = 0; } inline void negate () {x = -x; y = -y;} inline void negation (const Vec2f &v) { x = -v.x; y = -v.y; } inline void add (const Vec2f &v) { x += v.x; y += v.y; } inline void sum (const Vec2f &a, const Vec2f &b) { x = a.x + b.x; y = a.y + b.y; } inline void sub (const Vec2f &v) { x -= v.x; y -= v.y; } inline void diff (const Vec2f &a, const Vec2f &b) { x = a.x - b.x; y = a.y - b.y; } inline void min (const Vec2f &a) { x = __min(x, a.x); y = __min(y, a.y); } inline void max (const Vec2f &a) { x = __max(x, a.x); y = __max(y, a.y); } inline float lengthSqr () const { return x * x + y * y; } inline float length () const { return (float)sqrt(lengthSqr()); } // OPERATORS: inline Vec2f operator+(const Vec2f& a) const { return Vec2f(x + a.x, y + a.y); } inline Vec2f operator-(const Vec2f& a) const { return Vec2f(x - a.x, y - a.y); } inline Vec2f operator*(float t) const { return Vec2f(x * t, y * t); } inline Vec2f operator/(float t) const { return Vec2f(x / t, y / t); } inline Vec2f operator+=(const Vec2f& a) { x += a.x; y += a.y; return *this; } inline Vec2f operator-=(const Vec2f& a) { x -= a.x; y -= a.y; return *this; } inline Vec2f operator*=(float t) { x *= t; y *= t; return *this; } inline Vec2f operator/=(float t) { x /= t; y /= t; return *this; } DLLEXPORT bool normalize (); DLLEXPORT bool normalization (const Vec2f &v); DLLEXPORT float tiltAngle (); DLLEXPORT float tiltAngle2 (); DLLEXPORT float calc_angle(Vec2f a, Vec2f b); DLLEXPORT float calc_angle_pos(Vec2f a, Vec2f b); inline void scale(float s) { x *= s; y *= s; } inline void scaled (const Vec2f &v, float s) { x = v.x * s; y = v.y * s; } inline void addScaled (const Vec2f &v, float s) { x += v.x * s; y += v.y * s; } inline void lineCombin (const Vec2f &a, const Vec2f &b, float t) { x = a.x + b.x * t; y = a.y + b.y * t; } inline void lineCombin2 (const Vec2f &a, float ta, const Vec2f &b, float tb) { x = a.x * ta + b.x * tb; y = a.y * ta + b.y * tb; } DLLEXPORT void rotate (float angle); DLLEXPORT void rotate (float si, float co); DLLEXPORT void rotate (Vec2f vec); DLLEXPORT void rotateL (float angle); DLLEXPORT void rotateL (float si, float co); DLLEXPORT void rotateL (Vec2f vec); DLLEXPORT void rotateAroundSegmentEnd(const Vec2f &a, const Vec2f &b, float angle); DLLEXPORT static float distSqr (const Vec2f &a, const Vec2f &b); DLLEXPORT static float dist (const Vec2f &a, const Vec2f &b); DLLEXPORT static float dot (const Vec2f &a, const Vec2f &b); DLLEXPORT static float cross (const Vec2f &a, const Vec2f &b); DLLEXPORT static void projectZ (Vec2f& v2, const Vec3f& v3); DLLEXPORT static bool intersection (const Vec2f &v1_1, const Vec2f &v1_2, const Vec2f &v2_1, const Vec2f &v2_2, Vec2f &p); DLLEXPORT static float triangleArea (const Vec2f &a, const Vec2f &b, const Vec2f &c); DLLEXPORT static bool segmentsIntersect (const Vec2f &a0, const Vec2f &a1, const Vec2f &b0, const Vec2f &b1); DLLEXPORT static bool segmentsIntersectInternal (const Vec2f &a0, const Vec2f &a1, const Vec2f &b0, const Vec2f &b1); DLLEXPORT static double distPointSegment(Vec2f p, Vec2f q, Vec2f r); DLLEXPORT static double distSegmentSegment(Vec2f p, Vec2f q, Vec2f r, Vec2f s); DLLEXPORT static Vec2f get_circle_center(Vec2f p, Vec2f q, double angle); DLLEXPORT static Vec2f get_circle_center(Vec2f a, Vec2f b, Vec2f c); }; struct Vec3f { Vec3f () : x(0), y(0), z(0) {} Vec3f (float xx, float yy, float zz) : x(xx), y(yy), z(zz) {} Vec3f (Vec2f &v) : x(v.x), y(v.y), z(0) {} float x, y, z; inline void set (float xx, float yy, float zz) { x = xx; y = yy; z = zz; } inline void copy (const Vec3f &a) { x = a.x; y = a.y; z = a.z; } inline void zero () { x = 0; y = 0; z = 0; } inline void negate () {x = -x; y = -y; z = -z;} inline void negation (const Vec3f &v) { x = -v.x; y = -v.y; z = -v.z; } inline void add (const Vec3f &v) { x += v.x; y += v.y; z += v.z; } inline void sum (const Vec3f &a, const Vec3f &b) { x = a.x + b.x; y = a.y + b.y; z = a.z + b.z; } inline void sub (const Vec3f &v) { x -= v.x; y -= v.y; z -= v.z; } inline void diff (const Vec3f &a, const Vec3f &b) { x = a.x - b.x; y = a.y - b.y; z = a.z - b.z; } inline void min (const Vec3f &a) { x = __min(x, a.x); y = __min(y, a.y); z = __min(z, a.z); } inline void max (const Vec3f &a) { x = __max(x, a.x); y = __max(y, a.y); z = __max(z, a.z); } inline void cross (const Vec3f &a, const Vec3f &b) { x = a.y * b.z - a.z * b.y; y = a.z * b.x - a.x * b.z; z = a.x * b.y - a.y * b.x; } inline float lengthSqr () const { return x * x + y * y + z * z; } DLLEXPORT float length () const; DLLEXPORT bool normalize (); DLLEXPORT bool normalization (const Vec3f &v); inline void scale (float s) { x *= s; y *= s; z *= s; } inline void scaled (const Vec3f &v, float s) { x = v.x * s; y = v.y * s; z = v.z * s; } inline void addScaled (const Vec3f &v, float s) { x += v.x * s; y += v.y * s; z += v.z * s; } inline void lineCombin (const Vec3f &a, const Vec3f &b, float t) { x = a.x + b.x * t; y = a.y + b.y * t; z = a.z + b.z * t; } inline void lineCombin2 (const Vec3f &a, float ta, const Vec3f &b, float tb) { x = a.x * ta + b.x * tb; y = a.y * ta + b.y * tb; z = a.z * ta + b.z * tb; } inline Vec2f projectZ () const { return Vec2f(x, y); } DLLEXPORT void rotateX (float angle); DLLEXPORT void rotateY (float angle); DLLEXPORT void rotateZ (float angle); DLLEXPORT void rotate (const Vec3f &around, float angle); DLLEXPORT void transformPoint (const Transform3f &matr); DLLEXPORT void transformVector (const Transform3f &matr); DLLEXPORT void invTransformVector (const Transform3f &matr); DLLEXPORT void pointTransformation (const Vec3f &v, const Transform3f &matr); DLLEXPORT void vectorTransformation (const Vec3f &v, const Transform3f &matr); DLLEXPORT void invVectorTransformation (const Vec3f &v, const Transform3f &matr); // returns value in range 0..pi DLLEXPORT static bool angle (const Vec3f &a, const Vec3f &b, float &res); DLLEXPORT static float dot (const Vec3f &a, const Vec3f &b); DLLEXPORT static float dist (const Vec3f &a, const Vec3f &b); DLLEXPORT static float distSqr (const Vec3f &a, const Vec3f &b); }; const Vec3f VZero3f (0.f, 0.f, 0.f); struct Transform3f { DECL_ERROR; float elements[16]; void rotation (float x, float y, float z, float angle); void rotationX (float angle); void rotationY (float angle); void rotationZ (float angle); bool rotationVecVec (const Vec3f &v1, const Vec3f &v2); bool rotationQuat (float quat[4]); bool inversion (const Transform3f &matr); void copy (const Transform3f &matr); void identity (void); void getOrigin (Vec3f &origin); void composition (const Transform3f &matr, const Transform3f &transform); void transform (const Transform3f &transform); void transformLocal (const Transform3f &transform); void setOrigin (float x, float y, float z); void setOrigin (const Vec3f &origin); void translate (const Vec3f &translation); void translateLocal (float x, float y, float z); void translateLocal (const Vec3f &translation); void translateLocalInv (const Vec3f &translation); void translateInv (const Vec3f &translation); void rotateX (float angle); void rotateY (float angle); void rotateZ (float angle); void rotateXLocal (float angle); void rotateYLocal (float angle); void rotateZLocal (float angle); bool bestFit (int npoints, const Vec3f points[], const Vec3f goals[], float *sqsum_out); }; struct Matr3x3d { double elements[9]; DECL_ERROR; Matr3x3d (); void copy (const Matr3x3d &matr); void transpose (); void getTransposed (Matr3x3d &matr_out) const; void identity (); void matrixMatrixMultiply (const Matr3x3d &m, Matr3x3d &matrix_out) const; void matrixVectorMultiply (const Vec3f &a, Vec3f &b) const; void eigenSystem (Matr3x3d &evec_out); protected: void _qrStep (int n, double gc[], double gs[]); void _givensRotation (double x0, double x1, double &c, double &s); }; struct LSeg3f { LSeg3f (const Vec3f &beg, const Vec3f &end); float distToPoint (const Vec3f &point, Vec3f *closest) const; protected: Vec3f _beg; Vec3f _end; Vec3f _diff; float _length_sqr; bool _is_degenerate; }; struct Line3f { Vec3f org; Vec3f dir; explicit Line3f (); void copy (Line3f &other); float distFromPoint (const Vec3f &point) const; bool bestFit (int npoints, const Vec3f points[], float *sqsum_out); }; struct Plane3f { explicit Plane3f (); void copy (const Plane3f &other); inline const Vec3f &getNorm () const { return _norm; } inline const float &getD () const { return _d; } void projection (const Vec3f &point, Vec3f &proj_out) const; bool byPointAndLine (const Vec3f &point, const Line3f &line); float distFromPoint (const Vec3f &point) const; bool bestFit (int npoints, const Vec3f points[], float *sqsum_out); protected: Vec3f _norm; float _d; }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/best_fit.cpp��������������������������������������������������������0000664�0000000�0000000�00000017236�12710376503�0020736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" using namespace indigo; bool Transform3f::bestFit (int npoints, const Vec3f points[], const Vec3f goals[], float *sqsum_out) { QS_DEF(Array<double>, X); //set of points QS_DEF(Array<double>, Y); //set of goals Matr3x3d R, RT, RTR, evectors_matrix; // Matr3x3d rotation; double scale; Vec3f translation; // bool res = 1; Vec3f vec, tmp; double cpoints[3] = {0.0}, cgoals[3] = {0.0}; // centroid of points, of goals int i, j, k; for (i = 0; i < npoints; i++) { cpoints[0] += points[i].x; cpoints[1] += points[i].y; cpoints[2] += points[i].z; cgoals[0] += goals[i].x; cgoals[1] += goals[i].y; cgoals[2] += goals[i].z; } for (i = 0; i < 3; i++) { cpoints[i] /= npoints; cgoals[i] /= npoints; } X.resize(npoints * 3); Y.resize(npoints * 3); //move each set to origin for (i = 0; i < npoints; i++) { X[i * 3 + 0] = points[i].x - cpoints[0]; X[i * 3 + 1] = points[i].y - cpoints[1]; X[i * 3 + 2] = points[i].z - cpoints[2]; Y[i * 3 + 0] = goals[i].x - cgoals[0]; Y[i * 3 + 1] = goals[i].y - cgoals[1]; Y[i * 3 + 2] = goals[i].z - cgoals[2]; } if (npoints > 1) { /* compute R */ for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { R.elements[i * 3 + j] = 0.0; for (k = 0; k < npoints; k++) { R.elements[i * 3 + j] += Y[k * 3 + i] * X[k * 3 + j]; } } } //Compute R^T * R R.getTransposed(RT); RT.matrixMatrixMultiply(R, RTR); RTR.eigenSystem(evectors_matrix); if (RTR.elements[0] > 2 * EPSILON) { float norm_b0,norm_b1,norm_b2; Vec3f a0, a1, a2; Vec3f b0, b1, b2; a0.set((float)evectors_matrix.elements[0], (float)evectors_matrix.elements[3], (float)evectors_matrix.elements[6]); a1.set((float)evectors_matrix.elements[1], (float)evectors_matrix.elements[4], (float)evectors_matrix.elements[7]); a2.cross(a0, a1); R.matrixVectorMultiply(a0, b0); R.matrixVectorMultiply(a1, b1); norm_b0 = b0.length(); norm_b1 = b1.length(); Line3f l1, l2; float sqs1, sqs2; l1.bestFit(npoints, points, &sqs1); l2.bestFit(npoints, goals, &sqs2); if( sqs1 < 2 * EPSILON && sqs2 < 2 * EPSILON) { Transform3f temp; temp.rotationVecVec(l1.dir, l2.dir); for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) rotation.elements[i * 3 + j] = temp.elements[j * 4 + i]; } else { b0.normalize(); b1.normalize(); b2.cross(b0, b1); norm_b2 = b2.length(); evectors_matrix.elements[2] = a2.x; evectors_matrix.elements[5] = a2.y; evectors_matrix.elements[8] = a2.z; evectors_matrix.transpose(); RTR.elements[0] = b0.x; RTR.elements[1] = b1.x; RTR.elements[2] = b2.x; RTR.elements[3] = b0.y; RTR.elements[4] = b1.y; RTR.elements[5] = b2.y; RTR.elements[6] = b0.z; RTR.elements[7] = b1.z; RTR.elements[8] = b2.z; RTR.matrixMatrixMultiply(evectors_matrix, rotation); } } else { res = 0; } } else { res = 0; } if (!res) { rotation.identity(); } //Calc scale scale = 1.0; if (res && npoints > 1) { float l1 = 0.0; float l2 = 0.0; Vec3f vx, vy; for (i = 0; i < npoints; i++) { Vec3f vx((float)X[i * 3 + 0], (float)X[i * 3 + 1], (float)X[i * 3 + 2]); Vec3f vy((float)Y[i * 3 + 0], (float)Y[i * 3 + 1], (float)Y[i * 3 + 2]); rotation.matrixVectorMultiply(vx, vec); l1 += Vec3f::dot(vy, vec); l2 += Vec3f::dot(vec, vec); } scale = l1 / l2; } X.clear(); Y.clear(); //Calc translation translation.set((float)cgoals[0], (float)cgoals[1], (float)cgoals[2]); tmp = Vec3f((float)cpoints[0], (float)cpoints[1], (float)cpoints[2]); rotation.matrixVectorMultiply(tmp, vec); vec.scale((float)scale); translation.sub(vec); identity(); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { elements[i * 4 + j] = (float)rotation.elements[j * 3 + i]; } } elements[15] = 1.0f; translate(translation); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { elements[i * 4 + j] *= (float)scale; } } //Deviation if (sqsum_out) { *sqsum_out = 0; float d = .0f; for (i = 0; i < npoints; i++) { vec.pointTransformation(points[i], *this); d = Vec3f::dist(vec, goals[i]); *sqsum_out += d * d; } } return true; } bool Plane3f::bestFit (int npoints, const Vec3f points[], float *sqsum_out) { QS_DEF(Array<double>, m); m.clear_resize(npoints * 3); int i, j, k; Matr3x3d A, evec; Vec3f c; for (i = 0; i < npoints; i++) { c.add(points[i]); } c.scale(1.0f/npoints); for (i = 0; i < npoints; i ++) { m[3 * i + 0] = points[i].x - c.x; m[3 * i + 1] = points[i].y - c.y; m[3 * i + 2] = points[i].z - c.z; } for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { A.elements[i * 3 + j] = 0; for (k = 0; k < npoints; k++) { A.elements[i * 3 + j] += m[k * 3 + i] * m[k * 3 + j]; } } } A.eigenSystem(evec); _norm.x = (float)evec.elements[2]; _norm.y = (float)evec.elements[5]; _norm.z = (float)evec.elements[8]; _d = - Vec3f::dot(_norm, c); if (sqsum_out != 0) { *sqsum_out = 0; for (i = 0; i < npoints; i++) { float d = distFromPoint(points[i]); *sqsum_out += d * d; } } return true; } bool Line3f::bestFit (int npoints,const Vec3f points[], float *sqsum_out) { QS_DEF(Array<double>, m); Matr3x3d A; Matr3x3d evec; int i, j, k; m.clear_resize(npoints * 3); org = Vec3f(0, 0, 0); for (i = 0; i < npoints; i++) { org.add(points[i]); } org.scale(1.0f/npoints); for (i = 0; i < npoints; i ++) { m[3*i + 0] = points[i].x - org.x; m[3*i + 1] = points[i].y - org.y; m[3*i + 2] = points[i].z - org.z; } for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { A.elements[i * 3 + j] = 0; for (k = 0; k < npoints; k++) { A.elements[i * 3 + j] += m[k * 3 + i] * m[k * 3 + j]; } } } A.eigenSystem(evec); dir.x = (float)evec.elements[0]; dir.y = (float)evec.elements[3]; dir.z = (float)evec.elements[6]; dir.normalize(); if (sqsum_out != 0) { *sqsum_out = 0; for (int i = 0; i < npoints; i++) { float d = distFromPoint(points[i]); *sqsum_out += d*d; } } return true; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/line3f.cpp����������������������������������������������������������0000664�0000000�0000000�00000002021�12710376503�0020301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" using namespace indigo; Line3f::Line3f () { org.zero(); dir.set(0, 0, 1); } void Line3f::copy (Line3f &other) { org.copy(other.org); dir.copy(other.dir); } float Line3f::distFromPoint (const Vec3f &point) const { Vec3f diff; diff.diff(point, org); float prod = Vec3f::dot(dir, diff); diff.addScaled(dir, -prod); return diff.length(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/lseg3f.cpp����������������������������������������������������������0000664�0000000�0000000�00000002540�12710376503�0020312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" using namespace indigo; LSeg3f::LSeg3f(const Vec3f &beg, const Vec3f &end) : _beg(beg), _end(end) { _diff.diff(_end, _beg); _length_sqr = _diff.lengthSqr(); _is_degenerate = (_length_sqr < EPSILON); } float LSeg3f::distToPoint(const Vec3f &point, Vec3f *closest) const { if (_is_degenerate) { if (closest != 0) closest->copy(_beg); return Vec3f::dist(point, _beg); } Vec3f p; float t; p.diff(point, _beg); t = Vec3f::dot(p, _diff) / _length_sqr; if (t < 0.f) p.copy(_beg); else if (t > 1.f) p.copy(_end); else p.lineCombin(_beg, _diff, t); if (closest != 0) closest->copy(p); return Vec3f::dist(point, p); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/matr3x3d.cpp��������������������������������������������������������0000664�0000000�0000000�00000014362�12710376503�0020601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <string.h> #include "math/algebra.h" using namespace indigo; IMPL_ERROR(Matr3x3d, "Matr3x3d"); Matr3x3d::Matr3x3d () { memset(elements, 0, 9 * sizeof(double)); } void Matr3x3d::copy (const Matr3x3d &matr) { memcpy(elements, matr.elements, 9 * sizeof(double)); } void Matr3x3d::identity () { memset(elements, 0, 9 * sizeof(double)); elements[0] = elements[4] = elements[8] = 1.0; } void Matr3x3d::_givensRotation (double x0, double x1, double &c, double &s) { if (fabs(x1) < 3e-16) { c = 1; s = 0; } else if (fabs (x1) > fabs (x0)) { double t = - x0 / x1; s = 1.0 / sqrt(1 + t * t); c = s * t; } else { double t = - x1 / x0; c = 1.0 / sqrt(1 + t * t); s = c * t; } } void Matr3x3d::_qrStep (int n, double gc[], double gs[]) { double x, z; double mu; double c, s; Matr3x3d rot, tmp; int k; double d1 = elements[(n - 1) * 3 + (n - 1)]; double d2 = elements[n * 3 + n]; double sd = elements[n * 3 + (n - 1)]; double dt = (d1 - d2) / 2.0; if (dt > 0) { mu = d2 - sd * (sd / (dt + sqrt(dt * dt + sd * sd))); } else if (fabs(dt) < 3e-15) { mu = d2 - fabs(sd); } else { mu = d2 + sd * (sd / ((-dt) + sqrt(dt * dt + sd * sd))); } x = elements[0] - mu; z = elements[3]; if (n == 1) { _givensRotation(x, z, c, s); gc[0] = c; gs[0] = s; double e0 = elements[0], e3 = elements[3], e4 = elements[4]; elements[0] = c * (c * e0 - s * e3) + s * (s * e4 - c * e3); elements[3] = c * (s * e0 + c * e3) - s * (s * e3 + c * e4); elements[4] = s * (s * e0 + c * e3) + c * (s * e3 + c * e4); return; } k = 0; if(fabs(elements[3]) < 3e-15*(fabs(elements[0])+fabs(elements[4]))) { k = 1; gc[0] = 1.0; gs[0] = 0.0; x = elements[4] - mu; z = elements[7]; } for (; k < n; k++) { _givensRotation(x, z, c, s); gc[k] = c; gs[k] = s; rot.identity(); rot.elements[k * 3 + k] = c; rot.elements[(k + 1) * 3 + (k + 1)] = c; rot.elements[(k + 1) * 3 + k] = -s; rot.elements[k * 3 + (k + 1)] = s; rot.transpose(); rot.matrixMatrixMultiply(*this, tmp); rot.transpose(); tmp.matrixMatrixMultiply(rot, *this); x = elements[(k + 1) * 3 + k]; z = elements[(k + 2) * 3 + k]; } } void Matr3x3d::getTransposed (Matr3x3d &matr_out) const { matr_out.copy(*this); matr_out.transpose(); } void Matr3x3d::matrixVectorMultiply (const Vec3f &a, Vec3f &b) const { b.x = (float)(elements[0] * a.x + elements[1] * a.y + elements[2] * a.z); b.y = (float)(elements[3] * a.x + elements[4] * a.y + elements[5] * a.z); b.z = (float)(elements[6] * a.x + elements[7] * a.y + elements[8] * a.z); } void Matr3x3d::transpose () { double tmp; __swap(elements[3], elements[1], tmp); __swap(elements[6], elements[2], tmp); __swap(elements[7], elements[5], tmp); } void Matr3x3d::matrixMatrixMultiply (const Matr3x3d &m, Matr3x3d &matrix_out) const { int i, j, k; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { matrix_out.elements[i * 3 + j] = 0; for (k = 0; k < 3; k++) { matrix_out.elements[i * 3 + j] += elements[i * 3 + k] * m.elements[k * 3 + j]; } } } } void Matr3x3d::eigenSystem (Matr3x3d &evec_out) { Matr3x3d Q; int i, j, k, s; //Householder reduction to tridiagonal matrix double x0, x1, nrm; double alpha, beta, tau; x0 = elements[3]; x1 = elements[6]; nrm = fabs(x1); if (nrm < 3e-16) { tau = 0; } else { alpha = x0; beta = - (alpha >= 0.0 ? +1.0 : -1.0) * sqrt(alpha * alpha + nrm * nrm) ; tau = (beta - alpha) / beta ; x1 = x1 / (alpha - beta); x0 = beta; } evec_out.identity(); evec_out.elements[4] -= tau; evec_out.elements[5] -= tau * x1; evec_out.elements[7] -= tau * x1; evec_out.elements[8] -= tau * x1 * x1; evec_out.matrixMatrixMultiply(*this, Q); Q.matrixMatrixMultiply(evec_out, *this); ////////////////////////////////////////////////////////////////////////// //QR Iterations i = 2; s = 0; while (i > 0 && s++ < 100) { if (fabs(elements[i * 3 + i - 1]) < 3e-15*(fabs(elements[(i-1)*3 + (i - 1)]) + fabs(elements[i * 3 + i]))) { i--; s = 0; continue; } double gc[2]; double gs[2]; _qrStep(i, gc, gs); //Apply Givens rotation to transformation matrix for (j = 0; j < i; j++) { double c = gc[j], s = gs[j]; for (k = 0; k < 3; k++) { x0 = evec_out.elements[k * 3 + j]; x1 = evec_out.elements[k * 3 + j + 1]; evec_out.elements[k * 3 + j] = x0 * c - x1 * s; evec_out.elements[k * 3 + j + 1] = x0 * s + x1 * c; } } } ////////////////////////////////////////////////////////////////////////// //Sort eigenvalues in descending order for (i = 0; i < 2; i++) { for (j = 0; j < 2 - i; j++) { if (elements[j * 3 + j] < elements[(j + 1) * 3 + (j + 1)]) { __swap(elements[j * 3 + j], elements[(j + 1) * 3 + (j + 1)], nrm); __swap(evec_out.elements[0 * 3 + j], evec_out.elements[0 * 3 + (j + 1)], nrm); __swap(evec_out.elements[1 * 3 + j], evec_out.elements[1 * 3 + (j + 1)], nrm); __swap(evec_out.elements[2 * 3 + j], evec_out.elements[2 * 3 + (j + 1)], nrm); } } } ////////////////////////////////////////////////////////////////////////// } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/plane3f.cpp���������������������������������������������������������0000664�0000000�0000000�00000002727�12710376503�0020466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" using namespace indigo; Plane3f::Plane3f () { _norm.set(0, 0, 1); _d = 0; } void Plane3f::copy (const Plane3f &other) { _norm.copy(other._norm); _d = other._d; } float Plane3f::distFromPoint (const Vec3f &point) const { return (float)fabs(Vec3f::dot(point, _norm) + _d); } void Plane3f::projection (const Vec3f &point, Vec3f &proj_out) const { Vec3f org, diff, proj; org.scaled(_norm, _d); diff.diff(point, org); proj.scaled(_norm, Vec3f::dot(_norm, diff)); diff.sub(proj); proj_out.sum(org, diff); } bool Plane3f::byPointAndLine (const Vec3f &point, const Line3f &line) { Vec3f diff, cross; diff.diff(point, line.org); cross.cross(diff, line.dir); if (!cross.normalize()) return false; _norm.copy(cross); _d = -Vec3f::dot(_norm, line.org); return true; } �����������������������������������������Indigo-indigo-1.2.3/common/math/random.cpp����������������������������������������������������������0000664�0000000�0000000�00000002510�12710376503�0020404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <math.h> #include "base_c/defs.h" #include "math/random.h" #include <stdlib.h> #include <stdio.h> using namespace indigo; Random::Random() { randSeed = rand(); } Random::Random(int seed) { randSeed = seed; } void Random::setSeed(long long x) { randSeed = x; } unsigned int Random::next() { // printf("%lld %lld \n", randSeed, 6364136223846793005LL); randSeed = 6364136223846793005LL * randSeed + 1; // printf("%lld %lld \n", randSeed, 6364136223846793005LL); return (unsigned int) (randSeed >> 16); } unsigned int Random::next(int mod) { if (mod > 0) return next() % mod; if (mod < 0) return - (next() % - mod); return 0; } unsigned int Random::nextBounded(int l, int r) { return __min(l, r) + next(abs((long)(r - l))); } unsigned int Random::nextLarge(int mod) { int x = next(); if ((1LL << 32) - x > mod) return x % mod; int max = (1LL << 32) - (1LL << 32) % mod; if (x < max) return x % mod; return nextLarge(mod); } long long Random::nextLong() { return ((long long)next() << 32) + next(); } long long Random::nextLong(long long mod) { return nextLong() % mod; } double Random::nextDouble() { return 1.0 * next() + 1.0 * next() / (1LL << 32); } double Random::nextDoubleBounded(double l, double r) { return __min(l, r) + nextDouble() * abs((long)(r - l)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/random.h������������������������������������������������������������0000664�0000000�0000000�00000000727�12710376503�0020061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef _RANDOM_H_ #define _RANDOM_H_ namespace indigo { class Random { private: long long randSeed; public: Random(); Random(int seed); void setSeed(long long x); unsigned int next(); unsigned int next(int mod); unsigned int nextBounded(int l, int r); unsigned int nextLarge(int mod); long long nextLong(); long long nextLong(long long mod); double nextDouble(); double nextDoubleBounded(double l, double r); }; } #endif�����������������������������������������Indigo-indigo-1.2.3/common/math/statistics.cpp������������������������������������������������������0000664�0000000�0000000�00000002500�12710376503�0021315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/statistics.h" #include <math.h> using namespace indigo; // // MeanEstimator // MeanEstimator::MeanEstimator () : _count(0), _sum(0), _sum_sq(0) { } void MeanEstimator::addValue (float value) { _sum += value; _sum_sq += value * value; _count++; } int MeanEstimator::getCount () const { return _count; } void MeanEstimator::setCount (int count) { _count = count; } float MeanEstimator::mean () const { if (_count == 0) return 0; return _sum / _count; } float MeanEstimator::meanEsimationError () const { if (_count == 0) return 0; float sigma = sqrt(_sum_sq / _count - pow(_sum / _count, 2)); return 2 * sigma / sqrt((float)_count); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/statistics.h��������������������������������������������������������0000664�0000000�0000000�00000002110�12710376503�0020757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _statistics_h_ #define _statistics_h_ #include "base_c/defs.h" namespace indigo { class DLLEXPORT MeanEstimator { public: MeanEstimator (); void addValue (float value); int getCount () const; // Manual change of the count parameter to avoid a lot addValue(0) void setCount (int count); float mean () const; float meanEsimationError () const; private: int _count; float _sum, _sum_sq; }; } #endif // _statistics_h_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/transform3f.cpp�����������������������������������������������������0000664�0000000�0000000�00000020462�12710376503�0021376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <string.h> #include "math/algebra.h" using namespace indigo; IMPL_ERROR(Transform3f, "transform3f"); void Transform3f::copy (const Transform3f &other) { memcpy(elements, other.elements, 16 * sizeof(float)); } void Transform3f::getOrigin (Vec3f &origin) { origin.set(elements[12], elements[13], elements[14]); } bool Transform3f::inversion (const Transform3f &matr) { if (&matr == this) throw Error("can not do inversion() of self"); if ((float)fabs(matr.elements[3]) > EPSILON || (float)fabs(matr.elements[7]) > EPSILON || (float)fabs(matr.elements[11]) > EPSILON) return false; elements[0] = matr.elements[0]; elements[1] = matr.elements[4]; elements[2] = matr.elements[8]; elements[3] = 0; elements[4] = matr.elements[1]; elements[5] = matr.elements[5]; elements[6] = matr.elements[9]; elements[7] = 0; elements[8] = matr.elements[2]; elements[9] = matr.elements[6]; elements[10] = matr.elements[10]; elements[11] = 0; elements[12] = - matr.elements[0] * matr.elements[12] - matr.elements[1] * matr.elements[13] - matr.elements[2] * matr.elements[14]; elements[13] = - matr.elements[4] * matr.elements[12] - matr.elements[5] * matr.elements[13] - matr.elements[6] * matr.elements[14]; elements[14] = - matr.elements[8] * matr.elements[12] - matr.elements[9] * matr.elements[13] - matr.elements[10]* matr.elements[14]; elements[15] = 1.f; return true; } void Transform3f::rotation (float x, float y, float z, float angle) { float len = (float)sqrt(x * x + y * y + z * z); float Sin = (float)sin(angle), Cos = (float)cos(angle), Vers = 1 - Cos; if (len > EPSILON) { x /= len; y /= len; z /= len; } elements[0] = x * x + Cos * (1 - x * x); elements[1] = x * Vers * y - z * Sin; elements[2] = x * Vers * z + y * Sin; elements[3] = 0; elements[4] = x * Vers * y + z * Sin; elements[5] = y * y + Cos * (1 - y * y); elements[6] = y * Vers * z - x * Sin; elements[7] = 0; elements[8] = x * Vers * z - y * Sin; elements[9] = y * Vers * z + x * Sin; elements[10]= z * z + Cos * (1 - z * z); elements[11] = 0; elements[12] = 0; elements[13] = 0; elements[14] = 0; elements[15] = 1; } void Transform3f::transform (const Transform3f &transform) { Transform3f tmp; tmp.composition(*this, transform); copy(tmp); } void Transform3f::transformLocal (const Transform3f &transform) { Transform3f tmp; tmp.composition(transform, *this); copy(tmp); } void Transform3f::rotateX (float angle) { Transform3f rot; rot.rotationX(angle); transform(rot); } void Transform3f::rotateY (float angle) { Transform3f rot; rot.rotationY(angle); transform(rot); } void Transform3f::rotateZ (float angle) { Transform3f rot; rot.rotationZ(angle); transform(rot); } void Transform3f::rotateXLocal (float angle) { Transform3f rot; rot.rotationX(angle); transformLocal(rot); } void Transform3f::rotateYLocal (float angle) { Transform3f rot; rot.rotationY(angle); transformLocal(rot); } void Transform3f::rotateZLocal (float angle) { Transform3f rot; rot.rotationZ(angle); transformLocal(rot); } void Transform3f::translate (const Vec3f &translation) { elements[12] += translation.x; elements[13] += translation.y; elements[14] += translation.z; } void Transform3f::translateInv (const Vec3f &translation) { elements[12] -= translation.x; elements[13] -= translation.y; elements[14] -= translation.z; } void Transform3f::rotationX (float angle) { float sine = (float)sin(angle), cosine = (float)cos(angle); memset(elements, 0, 16 * sizeof(float)); elements[0] = 1.f; elements[5] = cosine; elements[6] = sine; elements[9] = -sine; elements[10] = cosine; elements[15] = 1.f; } void Transform3f::rotationY (float angle) { float sine = (float)sin(angle), cosine = (float)cos(angle); memset(elements, 0, 16 * sizeof(float)); elements[0] = cosine; elements[2] = -sine; elements[8] = sine; elements[10] = cosine; elements[5] = 1.f; elements[15] = 1.f; } void Transform3f::rotationZ (float angle) { float sine = (float)sin(angle), cosine = (float)cos(angle); memset(elements, 0, 16 * sizeof(float)); elements[0] = cosine; elements[1] = sine; elements[4] = -sine; elements[5] = cosine; elements[10] = 1.f; elements[15] = 1.f; } void Transform3f::composition (const Transform3f &a, const Transform3f &b) { elements[0] = a.elements[0] * b.elements[0] + a.elements[1] * b.elements[4] + a.elements[2] * b.elements[8]; elements[1] = a.elements[0] * b.elements[1] + a.elements[1] * b.elements[5] + a.elements[2] * b.elements[9]; elements[2] = a.elements[0] * b.elements[2] + a.elements[1] * b.elements[6] + a.elements[2] * b.elements[10]; elements[3] = 0; elements[4] = a.elements[4] * b.elements[0] + a.elements[5] * b.elements[4] + a.elements[6] * b.elements[8]; elements[5] = a.elements[4] * b.elements[1] + a.elements[5] * b.elements[5] + a.elements[6] * b.elements[9]; elements[6] = a.elements[4] * b.elements[2] + a.elements[5] * b.elements[6] + a.elements[6] * b.elements[10]; elements[7] = 0; elements[8] = a.elements[8] * b.elements[0] + a.elements[9] * b.elements[4] + a.elements[10] * b.elements[8]; elements[9] = a.elements[8] * b.elements[1] + a.elements[9] * b.elements[5] + a.elements[10] * b.elements[9]; elements[10] = a.elements[8] * b.elements[2] + a.elements[9] * b.elements[6] + a.elements[10] *b.elements[10]; elements[11] = 0; elements[12] = a.elements[12] * b.elements[0] + a.elements[13] * b.elements[4] + a.elements[14] * b.elements[8] + b.elements[12]; elements[13] = a.elements[12] * b.elements[1] + a.elements[13] * b.elements[5] + a.elements[14] * b.elements[9] + b.elements[13]; elements[14] = a.elements[12] * b.elements[2] + a.elements[13] * b.elements[6] + a.elements[14] * b.elements[10] + b.elements[14]; elements[15] = 1; } void Transform3f::identity (void) { memset(elements, 0, 16 * sizeof(float)); elements[0] = 1.f; elements[5] = 1.f; elements[10] = 1.f; elements[15] = 1.f; } void Transform3f::translateLocal (float x, float y, float z) { elements[12] += elements[0] * x + elements[4] * y + elements[8] * z; elements[13] += elements[1] * x + elements[5] * y + elements[9] * z; elements[14] += elements[2] * x + elements[6] * y + elements[10]* z; } void Transform3f::translateLocal (const Vec3f &translation) { translateLocal(translation.x, translation.y, translation.z); } void Transform3f::translateLocalInv (const Vec3f &translation) { translateLocal(-translation.x, -translation.y, -translation.z); } void Transform3f::setOrigin (float x, float y, float z) { elements[12] = x; elements[13] = y; elements[14] = z; } void Transform3f::setOrigin (const Vec3f &origin) { setOrigin(origin.x, origin.y, origin.z); } bool Transform3f::rotationVecVec (const Vec3f &v1, const Vec3f &v2) { Vec3f v1_norm, v2_norm; if (!v1_norm.normalization(v1) || !v2_norm.normalization(v2)) return false; Vec3f cross; cross.cross(v1_norm, v2_norm); if (!cross.normalize()) { // cross product have zero length -> v1 & v2 are codirectional identity(); return true; } float dot = Vec3f::dot(v1_norm, v2_norm); float ang; if (dot > 1 - EPSILON) ang = 0; else if (dot < -1 + EPSILON) ang = -PI; else ang = -(float)acos(dot); rotation(cross.x, cross.y, cross.z, ang); return true; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/vec2f.cpp�����������������������������������������������������������0000664�0000000�0000000�00000016005�12710376503�0020135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" using namespace indigo; IMPL_ERROR(Vec2f, "Vec2f"); bool Vec2f::normalize () { float l = lengthSqr(); if (l < EPSILON * EPSILON) { return false; } l = (float)sqrt(l); x /= l; y /= l; return true; } bool Vec2f::normalization (const Vec2f &v) { float l = v.lengthSqr(); if (l < EPSILON * EPSILON) return false; l = (float)sqrt(l); x = v.x / l; y = v.y / l; return true; } void Vec2f::rotate (float angle) { rotate(sin(angle), cos(angle)); } void Vec2f::rotate (float si, float co) { Vec2f a(*this); x = co * a.x - si * a.y; y = si * a.x + co * a.y; } void Vec2f::rotate(Vec2f vec) { rotate(vec.y, vec.x); } void Vec2f::rotateL(Vec2f vec) { rotateL(vec.y, vec.x); } void Vec2f::rotateL(float angle) { rotateL(sin(angle), cos(angle)); } void Vec2f::rotateL (float si, float co) { rotate(-si, co); } void Vec2f::rotateAroundSegmentEnd (const Vec2f &a, const Vec2f &b, float angle) { Vec2f c; c.diff(a, b); c.rotate(angle); sum(b, c); } float Vec2f::tiltAngle () { float l = length(); if (l < EPSILON) throw Error("zero length"); if (y >= 0) return acos(x / l); return -acos(x / l); } float Vec2f::tiltAngle2 () { float l = length(); if (l < EPSILON) throw Error("zero length"); if (y >= 0) return acos(x / l); return 2 * PI - acos(x / l); } float Vec2f::calc_angle(Vec2f a, Vec2f b) { a -= *this; b -= *this; double cos = Vec2f::dot(a, b) / sqrt(a.lengthSqr() * b.lengthSqr()); if (cos > 1) cos = 1; if (cos < -1) cos = -1; float angle = acos(cos); if (Vec2f::cross(a, b) < 0) angle = -angle; return angle; } float Vec2f::calc_angle_pos(Vec2f a, Vec2f b) { float angle = this->calc_angle(a, b); if (angle < 0) angle += 2 * PI; return angle; } float Vec2f::distSqr(const Vec2f &a, const Vec2f &b) { float dx = b.x - a.x; float dy = b.y - a.y; return dx * dx + dy * dy; } float Vec2f::dist (const Vec2f &a, const Vec2f &b) { return (float)sqrt(distSqr(a, b)); } float Vec2f::dot (const Vec2f &a, const Vec2f &b) { return a.x * b.x + a.y * b.y; } float Vec2f::cross (const Vec2f &a, const Vec2f &b) { return a.x * b.y - a.y * b.x; } void Vec2f::projectZ (Vec2f& v2, const Vec3f& v3) { v2.x = v3.x; v2.y = v3.y; } // two edges: // x = x1_1 + (x1_2 - x1_1)t1; // y = y1_1 + (y1_2 - y1_1)t1; // and // x = x2_1 + (x2_2 - x2_1)t2; // y = y2_1 + (y2_2 - y2_1)t2; // then // (x2_2 - x2_1)(y2_1 - y1_1) - (x2_1 - x1_1)(y2_2 - y2_1) a2 * b12 - a12 * b2 // t1 = ------------------------------------------------------- = ------------------- // (x2_2 - x2_1)(y1_2 - y1_1) - (x1_2 - x1_1)(y2_2 - y2_1) a2 * b1 - a1 * b2 // // (x1_2 - x1_1)(y2_1 - y1_1) - (x2_1 - x1_1)(y1_2 - y1_1) a1 * b12 - a12 * b1 // t2 = ------------------------------------------------------- = ------------------- // (x2_2 - x2_1)(y1_2 - y1_1) - (x1_2 - x1_1)(y2_2 - y2_1) a2 * b1 - a1 * b2 bool Vec2f::intersection (const Vec2f &v1_1, const Vec2f &v1_2, const Vec2f &v2_1, const Vec2f &v2_2, Vec2f &p) { float a1, a12, b12, a2, b1, b2; float delta, delta1, delta2, t1, t2; a1 = v1_2.x - v1_1.x; b1 = v1_2.y - v1_1.y; a12 = v2_1.x - v1_1.x; b12 = v2_1.y - v1_1.y; a2 = v2_2.x - v2_1.x; b2 = v2_2.y - v2_1.y; delta = a2 * b1 - a1 * b2; delta1 = a2 * b12 - a12 * b2; delta2 = a1 * b12 - a12 * b1; if (fabs(delta) < EPSILON) return false; t1 = delta1 / delta; t2 = delta2 / delta; if (fabs(t1) < EPSILON || fabs(t1 - 1.f) < EPSILON || fabs(t2) < EPSILON || fabs(t2 - 1.f) < EPSILON) return false; if (t1 < 0.f || t1 > 1.f || t2 < 0.f || t2 > 1.f) return false; p.x = v1_1.x + (v1_2.x - v1_1.x) * t1; p.y = v1_1.y + (v1_2.y - v1_1.y) * t1; return true; } float Vec2f::triangleArea (const Vec2f &a, const Vec2f &b, const Vec2f &c) { return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y); } bool Vec2f::segmentsIntersect (const Vec2f &a0, const Vec2f &a1, const Vec2f &b0, const Vec2f &b1) { float maxax = __max(a0.x, a1.x); float maxay = __max(a0.y, a1.y); float maxbx = __max(b0.x, b1.x); float maxby = __max(b0.y, b1.y); float minax = __min(a0.x, a1.x); float minay = __min(a0.y, a1.y); float minbx = __min(b0.x, b1.x); float minby = __min(b0.y, b1.y); float big_eps = 0.001; if (maxax + big_eps < minbx || maxbx + big_eps < minax || maxay + big_eps < minby || maxby + big_eps < minay) return false; // regular check return triangleArea(a0, a1, b0) * triangleArea(a0, a1, b1) < EPSILON && triangleArea(b0, b1, a0) * triangleArea(b0, b1, a1) < EPSILON; } bool Vec2f::segmentsIntersectInternal (const Vec2f &a0, const Vec2f &a1, const Vec2f &b0, const Vec2f &b1) { float maxax = __max(a0.x, a1.x); float maxay = __max(a0.y, a1.y); float maxbx = __max(b0.x, b1.x); float maxby = __max(b0.y, b1.y); float minax = __min(a0.x, a1.x); float minay = __min(a0.y, a1.y); float minbx = __min(b0.x, b1.x); float minby = __min(b0.y, b1.y); float big_eps = 0.001; if (maxax < minbx + big_eps || maxbx < minax + big_eps || maxay < minby + big_eps || maxby < minay + big_eps) return false; // regular check return triangleArea(a0, a1, b0) * triangleArea(a0, a1, b1) < - EPSILON && triangleArea(b0, b1, a0) * triangleArea(b0, b1, a1) < - EPSILON; } double Vec2f::distPointSegment(Vec2f p, Vec2f q, Vec2f r) { if (dot(p - q, r - q) <= 0) return dist(p, q); if (dot(p - r, q - r) <= 0) return dist(p, r); Vec2f normal = r - q; normal.rotate(PI/2); double c = cross(q, r); double s = normal.length(); double t = -c - dot(normal, p); return fabs(t / s); } double Vec2f::distSegmentSegment(Vec2f p, Vec2f q, Vec2f r, Vec2f s) { if (Vec2f::segmentsIntersect(p, q, r, s)) return 0; return __min( __min(distPointSegment(p, r, s), distPointSegment(q, r, s)), __min(distPointSegment(r, p, q), distPointSegment(s, p, q))); } Vec2f Vec2f::get_circle_center(Vec2f p, Vec2f q, double angle) { Vec2f vec(q - p); return (p + q) / 2 + vec / tan((PI - angle)/2); } Vec2f Vec2f::get_circle_center(Vec2f a, Vec2f p, Vec2f q) { p -= a; q -= a; float cross = Vec2f::cross(p, q); if (fabs(cross) < EPSILON) return (p + q) / 2 + a; float c1 = -p.lengthSqr() / 2; float c2 = -q.lengthSqr() / 2; Vec2f center(c1*q.x - c2*p.x, p.y * c2 - q.y * c1); return center / cross + a; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/math/vec3f.cpp�����������������������������������������������������������0000664�0000000�0000000�00000010001�12710376503�0020124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" using namespace indigo; float Vec3f::length () const { return (float)sqrt(lengthSqr()); } void Vec3f::transformPoint (const Transform3f &matr) { Vec3f v; v.pointTransformation(*this, matr); copy(v); } void Vec3f::transformVector (const Transform3f &matr) { Vec3f v; v.vectorTransformation(*this, matr); copy(v); } void Vec3f::invTransformVector (const Transform3f &matr) { Vec3f v; v.invVectorTransformation(*this, matr); copy(v); } void Vec3f::pointTransformation (const Vec3f &v, const Transform3f &matr) { if (&v == this) { transformPoint(matr); return; } x = matr.elements[0] * v.x + matr.elements[4] * v.y + matr.elements[8] * v.z + matr.elements[12]; y = matr.elements[1] * v.x + matr.elements[5] * v.y + matr.elements[9] * v.z + matr.elements[13]; z = matr.elements[2] * v.x + matr.elements[6] * v.y + matr.elements[10]* v.z + matr.elements[14]; } void Vec3f::vectorTransformation (const Vec3f &v, const Transform3f &matr) { if (&v == this) { transformVector(matr); return; } x = matr.elements[0] * v.x + matr.elements[4] * v.y + matr.elements[8] * v.z; y = matr.elements[1] * v.x + matr.elements[5] * v.y + matr.elements[9] * v.z; z = matr.elements[2] * v.x + matr.elements[6] * v.y + matr.elements[10]* v.z; } void Vec3f::invVectorTransformation (const Vec3f &v, const Transform3f &matr) { if (&v == this) { invTransformVector(matr); return; } x = matr.elements[0] * v.x + matr.elements[1] * v.y + matr.elements[2] * v.z; y = matr.elements[4] * v.x + matr.elements[5] * v.y + matr.elements[6] * v.z; z = matr.elements[8] * v.x + matr.elements[9] * v.y + matr.elements[10]* v.z; } void Vec3f::rotateX (float angle) { float sine = (float)sin(angle); float cosine = (float)cos(angle); float yy = y * cosine - z * sine; z = y * sine + z * cosine; y = yy; } void Vec3f::rotateY (float angle) { float sine = (float)sin(angle); float cosine = (float)cos(angle); float xx = x * cosine + z * sine; z = -x * sine + z * cosine; x = xx; } void Vec3f::rotateZ (float angle) { float sine = (float)sin(angle); float cosine = (float)cos(angle); float xx = x * cosine - y * sine; y = x * sine + y * cosine; x = xx; } void Vec3f::rotate (const Vec3f &around, float angle) { Transform3f matr; matr.rotation(around.x, around.y, around.z, angle); transformVector(matr); } bool Vec3f::normalize () { float l = lengthSqr(); if (l < EPSILON * EPSILON) return false; l = (float)sqrt(l); x /= l; y /= l; z /= l; return true; } bool Vec3f::normalization (const Vec3f &v) { float l = v.lengthSqr(); if (l < EPSILON * EPSILON) return false; l = (float)sqrt(l); x = v.x / l; y = v.y / l; z = v.z / l; return true; } bool Vec3f::angle (const Vec3f &a, const Vec3f &b, float &res) { float a_len = a.length(); float b_len = b.length(); if (a_len < EPSILON || b_len < EPSILON) return false; res = acos(dot(a, b) / (a_len * b_len)); return true; } float Vec3f::dot (const Vec3f &a, const Vec3f &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } float Vec3f::distSqr (const Vec3f &a, const Vec3f &b) { float dx = b.x - a.x; float dy = b.y - a.y; float dz = b.z - a.z; return dx * dx + dy * dy + dz * dz; } float Vec3f::dist (const Vec3f &a, const Vec3f &b) { return (float)sqrt(distSqr(a, b)); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/oracle/������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016736�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/oracle/CMakeLists.txt����������������������������������������������������0000664�0000000�0000000�00000000664�12710376503�0021504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������cmake_minimum_required(VERSION 2.6) project(CommonOracle C CXX) include(ConfigureCommon) include_directories(${OCI_INCLUDE_DIRS} ${Common_SOURCE_DIR}) file (GLOB CommonOracle_src *.cpp) file (GLOB CommonOracle_headers *.h) add_library(common-oracle ${CommonOracle_src} ${CommonOracle_headers}) set_target_properties(common-oracle PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if (NOT NO_STATIC) pack_static(common-oracle) endif()����������������������������������������������������������������������������Indigo-indigo-1.2.3/common/oracle/ora_error.cpp�����������������������������������������������������0000664�0000000�0000000�00000004731�12710376503�0021441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <stdio.h> #include <stdarg.h> #include <string.h> #include <oci.h> #include <nzt.h> // oci.h on Solaris has typedef-ed dword. // We define it in order to avoid conflict with base_c/defs.h #define dword unsigned int #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "base_c/defs.h" using namespace indigo; OracleError::OracleError (OCIError *errhp, int oracle_rc, const char *message, int my_rc) { if (oracle_rc == OCI_NO_DATA) snprintf(_message, sizeof(_message), "%s: no data", message); else if (oracle_rc == OCI_NEED_DATA) snprintf(_message, sizeof(_message), "%s: need data", message); else if (oracle_rc == OCI_INVALID_HANDLE) snprintf(_message, sizeof(_message), "%s: invalid handle", message); else if (oracle_rc == OCI_ERROR) { if (errhp != 0) { text errbuf[512]; OCIErrorGet(errhp, 1, NULL, &oracle_rc, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); snprintf(_message, sizeof(_message), "%s: %.*s\n", message, 512, errbuf); } else snprintf(_message, sizeof(_message), "(can not get the error message because errhp is null)\n"); } _code = my_rc; } OracleError::OracleError (int my_rc, const char *format, ...) { va_list args; int n; va_start(args, format); n = vsnprintf(_message, sizeof(_message), format, args); va_end(args); _code = my_rc; } void OracleError::raise (OracleLogger &logger, OCIExtProcContext *ctx) { logger.dbgPrintf("%s\n", _message); if (_code == -1) { if (OCIExtProcRaiseExcpWithMsg(ctx, 20352, (text *)_message, 0) == OCIEXTPROC_ERROR) logger.dbgPrintf("Error raising OCI exception\n"); } else { if (OCIExtProcRaiseExcpWithMsg(ctx, 20355, (text *)_message, 0) == OCIEXTPROC_ERROR) logger.dbgPrintf("Error raising OCI exception\n"); } } ���������������������������������������Indigo-indigo-1.2.3/common/oracle/ora_logger.cpp����������������������������������������������������0000664�0000000�0000000�00000003740�12710376503�0021566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "oracle/ora_logger.h" #include "base_c/defs.h" using namespace indigo; int OracleLogger::dbgPrintfV (const char *format, va_list args) { if (_file == NULL) return 0; int res = vfprintf(_file, format, args); fflush(_file); return res; } int OracleLogger::dbgPrintf (const char *format, ...) { va_list args; int n; va_start(args, format); n = dbgPrintfV(format, args); va_end(args); return n; } OracleLogger::OracleLogger () { _file = NULL; } OracleLogger::~OracleLogger () { close(); } void OracleLogger::close () { if (_file != NULL) { fclose(_file); _file = NULL; } } bool OracleLogger::init (const char *filename) { char full_name[1024]; #ifdef _WIN32 char *tmp_dir = getenv("TEMP"); char path[1024]; if (tmp_dir == NULL) strcpy(path, "C:\\"); else snprintf(path, sizeof(path), "%s\\", tmp_dir); #else char path[] = "/tmp/"; #endif close(); snprintf(full_name, sizeof(full_name), "%s%s", path, filename); _file = fopen(full_name, "a+t"); if (_file == NULL) return false; return true; } bool OracleLogger::isInited () { return _file != NULL; } bool OracleLogger::initIfClosed (const char *filename) { if (isInited()) return true; return init(filename); } ��������������������������������Indigo-indigo-1.2.3/common/oracle/ora_logger.h������������������������������������������������������0000664�0000000�0000000�00000002113�12710376503�0021224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ora_logger__ #define __ora_logger__ #include <stdarg.h> #include <stdio.h> namespace indigo { class OracleLogger { public: explicit OracleLogger (); virtual ~OracleLogger (); bool init (const char *filename); bool initIfClosed (const char *filename); void close (); bool isInited (); int dbgPrintf (const char *format, ...); int dbgPrintfV (const char *format, va_list args); private: FILE *_file; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/oracle/ora_wrap.cpp������������������������������������������������������0000664�0000000�0000000�00000051541�12710376503�0021262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <stdio.h> #include <oci.h> #include <string.h> #include <time.h> // oci.h on Solaris has typedef-ed dword. // We define it in order to avoid conflict with base_c/defs.h #define dword unsigned int #include "oracle/ora_wrap.h" #include "oracle/ora_logger.h" #include "base_c/defs.h" using namespace indigo; OracleStatement::OracleStatement (OracleEnv &env) : _env(env) { dvoid *res = 0; _statement = 0; // can throw here... env.callOCI(OCIHandleAlloc(env.envhp(), &res, OCI_HTYPE_STMT, 0, 0)); // OCI call OK, save result _statement = (OCIStmt *)res; _query_len = 0; } OracleStatement::~OracleStatement () { if (_statement != 0) OCIHandleFree(_statement, OCI_HTYPE_STMT); } OracleLOB::OracleLOB (OracleEnv &env) : _env(env) { dvoid *res = 0; _lob = 0; _delete_tmp = false; _delete_desc = false; _is_buffered = false; _opened = false; // can throw here... env.callOCI(OCIDescriptorAlloc(env.envhp(), &res, (ub4)OCI_DTYPE_LOB, 0, 0)); // OCI call OK, save variables _lob = (OCILobLocator *)res; _delete_desc = true; } OracleLOB::OracleLOB (OracleEnv &env, OCILobLocator *lob) : _env(env) { _lob = lob; _delete_tmp = false; _delete_desc = false; _is_buffered = false; _flushed = true; _opened = false; _buffered_read_start_pos = -1; } void OracleLOB::createTemporaryBLOB () { // can throw here... _env.callOCI(OCILobCreateTemporary(_env.svchp(), _env.errhp(), _lob, (ub2)OCI_DEFAULT, (ub1)OCI_DEFAULT, OCI_TEMP_BLOB, TRUE, OCI_DURATION_SESSION)); // not thrown => temporary LOB created, so set flag that we have to destroy him later _delete_tmp = true; } void OracleLOB::createTemporaryCLOB () { // can throw here... _env.callOCI(OCILobCreateTemporary(_env.svchp(), _env.errhp(), _lob, (ub2)OCI_DEFAULT, (ub1)OCI_DEFAULT, OCI_TEMP_CLOB, TRUE, OCI_DURATION_SESSION)); // not thrown => temporary LOB created, so set flag that we have to destroy him later _delete_tmp = true; } void OracleLOB::trim (int length) { _env.callOCI(OCILobTrim(_env.svchp(), _env.errhp(), _lob, length)); } OracleLOB::~OracleLOB () { if (_delete_desc && _lob != 0) { if (_is_buffered) flush(); if (_opened) _env.callOCI(OCILobClose(_env.svchp(), _env.errhp(), _lob)); if (_delete_tmp) OCILobFreeTemporary(_env.svchp(), _env.errhp(), _lob); OCIDescriptorFree(_lob, OCI_DTYPE_LOB); } } int OracleLOB::getLength () { ub4 total_length = 0; _env.callOCI(OCILobGetLength(_env.svchp(), _env.errhp(), _lob, &total_length)); return total_length; } void OracleLOB::enableBuffering () { if (!_is_buffered) { _env.callOCI(OCILobEnableBuffering(_env.svchp(), _env.errhp(), _lob)); _is_buffered = true; _flushed = true; } } void OracleLOB::disableBuffering () { if (_is_buffered) { flush(); _env.callOCI(OCILobDisableBuffering(_env.svchp(), _env.errhp(), _lob)); _is_buffered = false; } } void OracleLOB::readAll (Array<char> &arr, bool add_zero) { int len = getLength(); arr.clear_resize(len); read(0, arr.ptr(), len); if (add_zero) arr.push(0); } void OracleLOB::read (int start, char *buffer, int buffer_size) { ub4 offset = start; while (buffer_size > 0) { ub4 size = buffer_size; // ub4 bufl = buffer_size; ub4 bufl = buffer_size + 4; // to avoid bad behavior on Oracle 11 sword rc = OCILobRead(_env.svchp(), _env.errhp(), _lob, &size, offset + 1, &buffer[offset - start], bufl, 0, 0, 0, SQLCS_IMPLICIT); if (rc != OCI_SUCCESS && rc != OCI_NEED_DATA) throw OracleError(_env.errhp(), rc, "OCI lob read error", -1); if (size == 0) throw OracleError(-1, "0 bytes read from LOB"); offset += size; buffer_size -= size; if (rc == OCI_SUCCESS) { if (buffer_size != 0) throw OracleError(-1, "read(): got OCI_SUCCESS but size != 0"); break; } } } void OracleLOB::flush () { if (!_flushed) { OCILobFlushBuffer(_env.svchp(), _env.errhp(), _lob, OCI_LOB_BUFFER_NOFREE); //_env.callOCI(OCILobFlushBuffer(_env.svchp(), _env.errhp(), _lob, OCI_LOB_BUFFER_NOFREE)); _flushed = true; } } void OracleLOB::write (int start, const Array<char> &data) { write(start, data.ptr(), data.size()); } void OracleLOB::write (int start, const char *buffer, int bytes) { bool was_buffered = _is_buffered; while (bytes > 0) { ub4 n = bytes; int tries = 5; sword rc = OCI_ERROR; while (tries-- > 0) { rc = OCILobWrite(_env.svchp(), _env.errhp(), _lob, &n, start + 1, (void *)buffer, bytes, OCI_ONE_PIECE, 0, 0, 0, SQLCS_IMPLICIT); if (rc == OCI_ERROR) { int oracle_rc = 0; text errbuf[512]; OCIErrorGet(_env.errhp(), 1, NULL, &oracle_rc, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); if (oracle_rc == 22280 && _is_buffered) { disableBuffering(); continue; } else { _env.callOCI(rc); break; } } if (rc == OCI_NEED_DATA || rc == OCI_SUCCESS) break; _env.callOCI(rc); } if (rc == OCI_ERROR) _env.callOCI(rc); if (n == 0) throw OracleError(-1, "0 bytes written to LOB"); bytes -= n; buffer += n; start += n; } if (was_buffered) enableBuffering(); if (_is_buffered) _flushed = false; } void OracleLOB::openReadonly () { _env.callOCI(OCILobOpen(_env.svchp(), _env.errhp(), _lob, OCI_LOB_READONLY)); _opened = true; } void OracleLOB::openReadWrite () { _env.callOCI(OCILobOpen(_env.svchp(), _env.errhp(), _lob, OCI_LOB_READWRITE)); _opened = true; } OracleEnv::OracleEnv (OCIExtProcContext *ctx, OracleLogger &logger) : _logger(logger) { _envhp = 0; _svchp = 0; _errhp = 0; _ctx = 0; // Get the OCI environment, service context, and error handles int rc = OCIExtProcGetEnv(ctx, &_envhp, &_svchp, &_errhp); if (rc != OCI_SUCCESS) throw OracleError((OCIError *)0, rc, "Error getting OCI environment", -1); _ctx = ctx; } OracleEnv::OracleEnv (const char* name, const char* password, const char* base, OracleLogger &logger, bool object_mode) : _logger(logger) { _envhp = 0; _svchp = 0; _errhp = 0; _ctx = 0; ub4 mode = object_mode ? OCI_OBJECT : OCI_DEFAULT; callOCI(OCIEnvCreate( &_envhp, mode, (dvoid *)0, 0, 0, 0, (size_t)0, (dvoid **)0)); dvoid *errhp = 0; callOCI(OCIHandleAlloc((CONST dvoid *)_envhp, &errhp, OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0)); _errhp = (OCIError *)errhp; callOCI(OCILogon(_envhp, _errhp, &_svchp, (const OraText*)name, strlen(name), (const OraText*)password, strlen(password), (const OraText*)base, strlen(base))); } OracleEnv::~OracleEnv () { } int OracleEnv::ociMajorVersion () { #ifdef OCI_MAJOR_VERSION return OCI_MAJOR_VERSION; #else #if OCI_HTYPE_LAST>=29 return 10; // Oracle 10g2 #elif OCI_HTYPE_LAST>=27 return 9; // Oracle 9i2 #else return 8; #endif #endif } int OracleEnv::serverMajorVersion () { try { OracleStatement statement(*this); int version; statement.append("BEGIN :version := dbms_db_version.version; END;"); statement.prepare(); statement.bindIntByName(":version", &version); statement.execute(); return version; } catch (OracleError &) { return 0; } } void OracleEnv::callOCI (int rc) { if (rc == OCI_SUCCESS || rc == OCI_SUCCESS_WITH_INFO) return; throw OracleError(_errhp, rc, "OCI call error", -1); } void OracleEnv::dbgPrintf (const char *format, ... ) { va_list args; va_start(args, format); _logger.dbgPrintfV(format, args); va_end(args); } void OracleEnv::dbgPrintfTS (const char *format, ... ) { va_list args; time_t tm = time(NULL); const struct tm *lt = localtime(&tm); _logger.dbgPrintf("[%02d.%02d.%4d %02d:%02d:%02d] ", lt->tm_mday, lt->tm_mon + 1, lt->tm_year + 1900, lt->tm_hour, lt->tm_min, lt->tm_sec); va_start(args, format); _logger.dbgPrintfV(format, args); va_end(args); } void OracleStatement::prepare () { //_env.dbgPrintf("preparing %.*s\n", _query_len, _query); _env.callOCI(OCIStmtPrepare(_statement, _env.errhp(), (text *)_query, (ub4)_query_len, (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT)); } void OracleStatement::defineIntByPos (int pos, int *value) { OCIDefine *defnp = (OCIDefine *) 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, value, (sword)sizeof(int), SQLT_INT, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::defineFloatByPos (int pos, float *value) { OCIDefine *defnp = (OCIDefine *) 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, value, (sword)sizeof(float), SQLT_FLT, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::defineStringByPos (int pos, char *string, int max_len) { OCIDefine *defnp = (OCIDefine *) 0; _indicators[pos] = 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, (dvoid *)string, max_len, SQLT_STR, &_indicators[pos], 0, 0, OCI_DEFAULT)); } void OracleStatement::defineRowidByPos (int pos, OracleRowID &rowid) { OCIDefine *defnp = (OCIDefine *) 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, rowid.getRef(), (sword)sizeof(OCIRowid *), SQLT_RDD, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::defineRawByPos (int pos, OracleRaw &raw) { OCIDefine *defnp = (OCIDefine *) 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, raw.get(), (sword)sizeof(OCIRaw *), SQLT_LVB, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::defineBlobByPos (int pos, OracleLOB &lob) { OCIDefine *defnp = (OCIDefine *) 0; if (pos >= NELEM(_indicators)) throw OracleError(-1, "pos too big"); _indicators[pos] = 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, lob.getRef(), (sword)sizeof(OCILobLocator *), SQLT_BLOB, &_indicators[pos], 0, 0, OCI_DEFAULT)); } void OracleStatement::defineClobByPos (int pos, OracleLOB &lob) { OCIDefine *defnp = (OCIDefine *) 0; if (pos >= NELEM(_indicators)) throw OracleError(-1, "pos too big"); _indicators[pos] = 0; _env.callOCI(OCIDefineByPos(_statement, &defnp, _env.errhp(), pos, lob.getRef(), (sword)sizeof(OCILobLocator *), SQLT_CLOB, &_indicators[pos], 0, 0, OCI_DEFAULT)); } void OracleStatement::execute () { sword rc = OCIStmtExecute(_env.svchp(), _statement, _env.errhp(), 1, 0, NULL, NULL, OCI_DEFAULT); _env.callOCI(rc); } void OracleStatement::executeMultiple (int iter) { sword rc = OCIStmtExecute(_env.svchp(), _statement, _env.errhp(), iter, 0, NULL, NULL, OCI_DEFAULT); _env.callOCI(rc); } bool OracleStatement::executeAllowNoData () { sword rc = OCIStmtExecute(_env.svchp(), _statement, _env.errhp(), 1, 0, NULL, NULL, OCI_DEFAULT); if (rc != OCI_NO_DATA) { if (rc == OCI_ERROR) { text errbuf[512]; int code; OCIErrorGet(_env.errhp(), 1, NULL, &code, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); if (code == 1405) // ORA-01405: fetched column value is NULL return false; } _env.callOCI(rc); } return (rc == OCI_SUCCESS); } bool OracleStatement::fetch () { sword rc = OCIStmtFetch2(_statement, _env.errhp(), 1, OCI_FETCH_NEXT, 1, OCI_DEFAULT); return (rc == OCI_SUCCESS); } bool OracleStatement::gotNull (int pos) { return _indicators[pos] == OCI_IND_NULL; } void OracleStatement::getRowID (OracleRowID &rowid) { _env.callOCI(OCIAttrGet(_statement, OCI_HTYPE_STMT, rowid.get(), 0, OCI_ATTR_ROWID, _env.errhp())); } void OracleStatement::bindIntByName (const char *name, int *value) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)value, sizeof(int), SQLT_INT, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindFloatByName (const char *name, float *value) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)value, sizeof(float), SQLT_FLT, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindClobByName (const char *name, OracleLOB &lob) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)lob.getRef(), sizeof(OCILobLocator *), SQLT_CLOB, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindBlobByName (const char *name, OracleLOB &lob) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)lob.getRef(), sizeof(OCILobLocator *), SQLT_BLOB, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindBlobPtrByName (const char *name, OCILobLocator **lob, short *indicators) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, lob, sizeof(OCILobLocator *), SQLT_BLOB, indicators, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindRawByName (const char *name, OracleRaw &raw) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)raw.get(), raw.getSize() + sizeof(int), SQLT_LVB, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindRawPtrByName (const char *name, OCIRaw *raw, int size, short *indicators) { OCIBind *bndp = (OCIBind *)0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)raw, size + sizeof(int), SQLT_LVB, indicators, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::bindStringByName (const char *name, const char *string, int max_len) { OCIBind *bndp = (OCIBind *) 0; _env.callOCI(OCIBindByName(_statement, &bndp, _env.errhp(), (text *)name, -1, (dvoid *)string, max_len, SQLT_STR, 0, 0, 0, 0, 0, OCI_DEFAULT)); } void OracleStatement::append_v (const char *format, va_list args) { _query_len += vsnprintf(&_query[_query_len], sizeof(_query) - _query_len, format, args); if (_query_len >= (int)sizeof(_query)) throw OracleError(-2, "query too long"); } void OracleStatement::append (const char *format, ...) { va_list args; va_start(args, format); append_v(format, args); va_end(args); } void OracleStatement::executeSingle (OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; va_start(args, format); statement.append_v(format, args); statement.prepare(); statement.execute(); va_end(args); } void OracleStatement::executeSingle_BindString (OracleEnv &env, const char *bind, const char *value, const char *format, ...) { OracleStatement statement(env); va_list args; va_start(args, format); statement.append_v(format, args); statement.prepare(); statement.bindStringByName(bind, value, strlen(value) + 1); statement.execute(); va_end(args); } bool OracleStatement::executeSingleInt (int &result, OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; result = 0; // to emphasize that the result can change even if we return 'false' va_start(args, format); statement.append_v(format, args); statement.prepare(); statement.defineIntByPos(1, &result); if (!statement.executeAllowNoData()) return false; va_end(args); return true; } bool OracleStatement::executeSingleFloat (float &result, OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; result = 0; // to emphasize that the result can change even if we return 'false' va_start(args, format); statement.append_v(format, args); statement.prepare(); statement.defineFloatByPos(1, &result); if (!statement.executeAllowNoData()) return false; va_end(args); return true; } bool OracleStatement::executeSingleString (Array<char> &result, OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; // clear to emphasize that the result can change even if we return 'false' result.clear_resize(4001); // maximum size of Oracle's VARCHAR2 va_start(args, format); statement.append_v(format, args); statement.prepare(); statement.defineStringByPos(1, result.ptr(), result.size()); if (!statement.executeAllowNoData()) { result.clear(); return false; } va_end(args); result.resize(strlen(result.ptr()) + 1); return true; } bool OracleStatement::executeSingleBlob (Array<char> &result, OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; result.clear(); // to emphasize that the result can change even if we return 'false' va_start(args, format); statement.append_v(format, args); statement.prepare(); OracleLOB lob(env); statement.defineBlobByPos(1, lob); if (!statement.executeAllowNoData()) return false; va_end(args); if (statement.gotNull(1)) // null LOB? return false; lob.readAll(result, false); return true; } bool OracleStatement::executeSingleClob (Array<char> &result, OracleEnv &env, const char *format, ...) { OracleStatement statement(env); va_list args; result.clear(); // to emphasize that the result can change even if we return 'false' va_start(args, format); statement.append_v(format, args); statement.prepare(); OracleLOB lob(env); statement.defineClobByPos(1, lob); if (!statement.executeAllowNoData()) return false; if (statement.gotNull(1)) // null LOB? return false; va_end(args); lob.readAll(result, false); return true; } OracleRowID::OracleRowID (OracleEnv &env) : _env(env) { _rowid = 0; dvoid *rowid = 0; env.callOCI(OCIDescriptorAlloc(env.envhp(), &rowid, OCI_DTYPE_ROWID, 0, (dvoid **)0)); _rowid = (OCIRowid *)rowid; } OracleRowID::~OracleRowID () { OCIDescriptorFree(_rowid, OCI_DTYPE_ROWID); } OracleRaw::OracleRaw (OracleEnv &env) : _env(env) { _raw = 0; _size = 0; } OracleRaw::~OracleRaw () { } void OracleRaw::assignBytes (const char *buffer, int length) { _env.callOCI(OCIRawAssignBytes(_env.envhp(), _env.errhp(), (ub1 *)buffer, (ub4)length, &_raw)); _size = length; } char *OracleRaw::getDataPtr () { return (char *)OCIRawPtr(_env.envhp(), _raw); } void OracleRaw::resize (int new_size) { _env.callOCI(OCIRawResize(_env.envhp(), _env.errhp(), (ub2)new_size, &_raw)); _size = new_size; } int OracleRaw::getAllocSize () { int size; _env.callOCI(OCIRawAllocSize(_env.envhp(), _env.errhp(), _raw, (ub4 *)(&size))); return size; } int OracleRaw::getSize () { return _size; } OraRowidText::OraRowidText () { memset(t, 0, sizeof(t)); } int OraRowidText::length () { return 18; } OCINumber * OracleExtproc::createInt (OracleEnv &env, int value) { OCINumber *result = (OCINumber *)OCIExtProcAllocCallMemory(env.ctx(), sizeof(OCINumber)); if (result == NULL) throw OracleError(-1, "can't allocate memory for number"); env.callOCI(OCINumberFromInt(env.errhp(), &value, sizeof(int), OCI_NUMBER_SIGNED, result)); return result; } OCINumber * OracleExtproc::createDouble (OracleEnv &env, double value) { OCINumber *result = (OCINumber *)OCIExtProcAllocCallMemory(env.ctx(), sizeof(OCINumber)); if (result == NULL) throw OracleError(-1, "can't allocate memory for number"); env.callOCI(OCINumberFromReal(env.errhp(), &value, sizeof(value), result)); return result; } int OracleUtil::numberToInt (OracleEnv &env, OCINumber *number) { int res; env.callOCI(OCINumberToInt(env.errhp(), number, sizeof(res), OCI_NUMBER_SIGNED, &res)); return res; } float OracleUtil::numberToFloat (OracleEnv &env, OCINumber *number) { double res; env.callOCI(OCINumberToReal(env.errhp(), number, sizeof(res), &res)); return (float)res; } double OracleUtil::numberToDouble (OracleEnv &env, OCINumber *number) { double res; env.callOCI(OCINumberToReal(env.errhp(), number, sizeof(res), &res)); return res; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/oracle/ora_wrap.h��������������������������������������������������������0000664�0000000�0000000�00000014634�12710376503�0020731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __ora_wrap_h__ #define __ora_wrap_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" // forward declaration struct OCIStmt; struct OCIError; struct OCIExtProcContext; struct OCIEnv; struct OCISvcCtx; struct OCILobLocator; struct OCIRowid; struct OCIRaw; struct OCINumber; namespace indigo { class OracleLogger; class OracleLOB; class OracleRowID; class OracleRaw; class OracleError : public Exception { public: explicit OracleError (OCIError *errhp, int oracle_rc, const char *message, int my_rc); explicit OracleError (int my_rc, const char *format, ...); void raise (OracleLogger &logger, OCIExtProcContext *ctx); }; class OracleLogger; class OracleEnv { public: explicit OracleEnv (OCIExtProcContext *ctx, OracleLogger &logger); explicit OracleEnv (const char *name, const char *password, const char *base, OracleLogger &logger, bool object_mode = false); virtual ~OracleEnv (); inline OCIEnv * envhp () {return _envhp;} inline OCISvcCtx * svchp () {return _svchp;} inline OCIError * errhp () {return _errhp;} inline OCIExtProcContext * ctx () {return _ctx;} inline OracleLogger & logger () {return _logger;} void callOCI (int rc); void dbgPrintf (const char *format, ...); void dbgPrintfTS (const char *format, ...); static int ociMajorVersion (); int serverMajorVersion (); private: OCIEnv *_envhp; OCISvcCtx *_svchp; OCIError *_errhp; OCIExtProcContext *_ctx; OracleLogger &_logger; }; class OracleStatement { public: explicit OracleStatement (OracleEnv &env); virtual ~OracleStatement (); void prepare (); void defineIntByPos (int pos, int *value); void defineFloatByPos (int pos, float *value); void defineBlobByPos (int pos, OracleLOB &lob); void defineClobByPos (int pos, OracleLOB &lob); void defineRowidByPos (int pos, OracleRowID &rowid); void defineRawByPos (int pos, OracleRaw &raw); void defineStringByPos (int pos, char *string, int max_len); void bindIntByName (const char *name, int *value); void bindFloatByName (const char *name, float *value); void bindBlobByName (const char *name, OracleLOB &lob); void bindBlobPtrByName (const char *name, OCILobLocator **lob, short *indicators); void bindClobByName (const char *name, OracleLOB &lob); void bindRawByName (const char *name, OracleRaw &raw); void bindRawPtrByName (const char *name, OCIRaw *raw, int maxsize, short *indicators); void bindStringByName (const char *name, const char *string, int max_len); void execute (); void executeMultiple (int iter); bool executeAllowNoData (); bool gotNull (int pos); bool fetch (); void getRowID (OracleRowID &rowid); void append (const char *format, ...); void append_v (const char *format, va_list args); OCIStmt * get () {return _statement;} const char* getString () const {return _query;} static void executeSingle (OracleEnv &env, const char *format, ...); static void executeSingle_BindString (OracleEnv &env, const char *bind, const char *value, const char *format, ...); static bool executeSingleInt (int &result, OracleEnv &env, const char *format, ...); static bool executeSingleFloat (float &result, OracleEnv &env, const char *format, ...); static bool executeSingleString (Array<char> &result, OracleEnv &env, const char *format, ...); static bool executeSingleBlob (Array<char> &result, OracleEnv &env, const char *format, ...); static bool executeSingleClob (Array<char> &result, OracleEnv &env, const char *format, ...); private: OracleEnv &_env; OCIStmt *_statement; char _query[10240]; int _query_len; short _indicators[64]; }; class OracleLOB { public: explicit OracleLOB (OracleEnv &env); explicit OracleLOB (OracleEnv &env, OCILobLocator *lob); virtual ~OracleLOB (); void createTemporaryBLOB (); void createTemporaryCLOB (); inline OCILobLocator * get () {return _lob; } inline OCILobLocator ** getRef () {return &_lob; } void enableBuffering (); void disableBuffering (); void flush (); void openReadonly (); void openReadWrite (); int getLength (); void readAll (Array<char> &arr, bool add_zero); void read (int start, char *buffer, int buffer_size); void write (int start, const char *buffer, int bytes); void write (int start, const Array<char> &data); void trim (int length); inline void doNotDelete () {_delete_desc = false;} private: OCILobLocator *_lob; bool _delete_desc; bool _delete_tmp; bool _is_buffered; bool _flushed; bool _opened; int _buffered_read_start_pos; OracleEnv &_env; }; class OracleRowID { public: explicit OracleRowID (OracleEnv &env); virtual ~OracleRowID (); inline OCIRowid *get() {return _rowid;} inline OCIRowid **getRef () {return &_rowid;} protected: OracleEnv &_env; OCIRowid *_rowid; }; class OracleRaw { public: explicit OracleRaw (OracleEnv &env); virtual ~OracleRaw (); inline OCIRaw *get () {return _raw;} inline OCIRaw **getRef () {return &_raw;} char *getDataPtr (); int getAllocSize (); int getSize (); void assignBytes (const char *buffer, int length); void resize (int new_size); protected: OracleEnv &_env; int _size; OCIRaw *_raw; }; class OraRowidText { public: OraRowidText (); char * ptr () {return t;} int length (); private: char t[19]; }; class OracleExtproc { public: static OCINumber * createInt (OracleEnv &env, int value); static OCINumber * createDouble (OracleEnv &env, double value); }; class OracleUtil { public: static int numberToInt (OracleEnv &env, OCINumber *number); static float numberToFloat (OracleEnv &env, OCINumber *number); static double numberToDouble (OracleEnv &env, OCINumber *number); }; } #endif ����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/���������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0017507�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/cpp_unit.h�����������������������������������������������������0000664�0000000�0000000�00000000233�12710376503�0021477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef TESTHARNESS_H__ #define TESTHARNESS_H__ #include "test.h" #include "test_result.h" #include "test_failure.h" #include "test_registry.h" #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test.cpp�������������������������������������������������������0000664�0000000�0000000�00000007434�12710376503�0021202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <math.h> #include "test.h" #include "test_registry.h" #include "test_result.h" #include "test_failure.h" #include "base_cpp/output.h" using namespace indigo; Test::Test (const char * testName, const char * filename, int linenumber) : m_name (testName) , m_filename (filename) , m_linenumber (linenumber) { } Test::~Test() { } CurrentTest::CurrentTest(const char* test, const char* message, const TestResult& test_result) : _failure(0), _testResult(test_result) { _message.appendString("- ", true); _message.appendString(message, true); bprintf(_testName, "%s %s", test, message); } CurrentTest::~CurrentTest() { if (_failure) { bprintf(_message, "%s (FAILED - %d)", _message.ptr(), _failure); _testResult.appendErrorLine(_message.ptr()); } else { _testResult.appendSuccessLine(_message.ptr()); } } void CurrentTest::assertEquals(const char* expected, const char* actual, const char* file, unsigned int line) { if((expected == 0 || actual == 0) && !(expected == 0 && actual == 0)) { Array<char> error_message; bprintf(error_message, "one of the strings was null"); _throwAssertion(error_message, file, line); } if(strcmp(expected, actual) != 0) { Array<char> error_message; bprintf(error_message, "expected: '%s' but was: '%s'", expected, actual); _throwAssertion(error_message, file, line); } } void CurrentTest::assertEquals(int expected, int actual, const char* file, unsigned int line) { if(expected != actual) { Array<char> error_message; bprintf(error_message, "expected: '%d' but was: '%d'", expected, actual); _throwAssertion(error_message, file, line); } } void CurrentTest::assertEquals(double expected, double actual, const char* file, unsigned int line) { /* * Cut off decimicals */ expected *= pow(10.0,SIGNIFICANT_DOUBLE-1); expected = (int)float(expected); expected /= pow(10.0, SIGNIFICANT_DOUBLE-1); actual *= pow(10.0,SIGNIFICANT_DOUBLE-1); actual = (int)float(actual); actual /= pow(10.0,SIGNIFICANT_DOUBLE-1); if (expected != actual) { Array<char> error_message; bprintf(error_message, "expected: '%.8f' but was: '%.8f'", expected, actual); _throwAssertion(error_message, file, line); } } void CurrentTest::assertTrue(bool condition, const char* file, unsigned int line) { if(!condition) { Array<char> error_message; bprintf(error_message, "expected: 'true' but was: 'false'"); _throwAssertion(error_message, file, line); } } void CurrentTest::assertNull(int expected, const char* file, unsigned int line) { if(expected != 0) { Array<char> error_message; bprintf(error_message, "expected null but was not null"); _throwAssertion(error_message, file, line); } } void CurrentTest::assertNull(const char* expected, const char* file, unsigned int line) { if(expected != 0) { Array<char> error_message; bprintf(error_message, "expected null but was not null"); _throwAssertion(error_message, file, line); } } void CurrentTest::assertNotNull(const char* expected, const char* file, unsigned int line) { if(expected == 0) { Array<char> error_message; bprintf(error_message, "expected not null but was null"); _throwAssertion(error_message, file, line); } } void CurrentTest::assertNotNull(int expected, const char* file, unsigned int line) { if(expected == 0) { Array<char> error_message; bprintf(error_message, "expected not null but was null"); _throwAssertion(error_message, file, line); } } void CurrentTest::_throwAssertion(Array<char>& error, const char* file, unsigned int line) { throw AssertException("Assert error", new Failure("AssertException", error.ptr(), _testName.ptr(), file, line)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test.h���������������������������������������������������������0000664�0000000�0000000�00000021410�12710376503�0020635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef TEST_H #define TEST_H #include "test_result.h" #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "base_cpp/output.h" namespace indigo { class AssertException: public Exception { public: AssertException(const char* message, Failure* f): Exception(message), _failure(f) { } Failure* getFailure() const { return _failure; } private: Failure* _failure; }; class Test { public: Test (const char * testName, const char * filename, int linenumber); virtual ~Test(); virtual void run (TestResult& result) = 0; protected: virtual void runTest (TestResult& result) = 0; const char * m_name; const char * m_filename; const int m_linenumber; private: Test(const Test &); Test& operator=(const Test &); }; class CurrentTest { enum { /* * Cut off number for double values */ SIGNIFICANT_DOUBLE = 6 }; public: CurrentTest(const char* test, const char* message, const TestResult& test_result); ~CurrentTest(); void setFailureNum(int val) { _failure = val; } int getFailureNum() const { return _failure; } const char* getTestName() const {return _testName.ptr(); } void assertEquals(int expected, int actual, const char* file, unsigned int line); void assertEquals(double expected, double actual, const char* file, unsigned int line); void assertEquals(const char* expected, const char* actual, const char* file, unsigned int line); void assertTrue(bool condition, const char* file, unsigned int line); void assertNull(int expected, const char* file, unsigned int line); void assertNull(const char* expected, const char* file, unsigned int line); void assertNotNull(int expected, const char* file, unsigned int line); void assertNotNull(const char* expected, const char* file, unsigned int line); void _throwAssertion(Array<char>& error, const char* file, unsigned int line); private: Array<char> _message; Array<char> _testName; int _failure; const TestResult& _testResult; }; #define BEGIN_TEST(description) \ { \ CurrentTest current_test(_currentDescription.ptr(), description, test_result);\ Failure* failure = 0; \ test_result.testWasRun(); \ try { \ #define END_TEST \ } catch(AssertException& e ) { \ failure = e.getFailure(); \ } catch(Exception& e) { \ failure = new Failure("Exception", e.message(), current_test.getTestName(),\ __FILE__, __LINE__); \ } catch(...) { \ failure = new Failure("Unknown exception", "unknown error", \ current_test.getTestName(), __FILE__, __LINE__); \ } \ if(failure) { \ int fail_num = test_result.addFailure(failure); \ current_test.setFailureNum(fail_num); \ } \ } \ #define DESCRIBE(description) \ bprintf(_currentDescription, "%s%s",m_name, description); \ test_result.appendInfoLine(_currentDescription.ptr()); \ #define ITEST(test_name, class_name) \ class test_name : public Test \ { \ public: \ test_name() : Test (#class_name, __FILE__, __LINE__){} \ protected: \ virtual void runTest (TestResult& test_result); \ virtual void run (TestResult& test_result); \ private: \ Array<char> _currentDescription; \ }; \ #define TEST(test_name) \ void test_name::run(TestResult& test_result) { \ Failure* failure = 0; \ try { \ runTest(test_result); \ } catch(Exception& e) { \ failure = new Failure("Exception while initializing: ", e.message(), \ #test_name, __FILE__, __LINE__); \ } catch(...) { \ failure = new Failure("Exception while initializing:", "unknown error", \ #test_name, __FILE__, __LINE__); \ } \ if(failure) { \ test_result.addFailure(failure); \ test_result.appendErrorLine(failure->getMessage()); \ } \ } \ void test_name::runTest (TestResult& test_result) #define ASSERT_TRUE(condition) \ do { \ current_test.assertTrue(condition, __FILE__, __LINE__); \ } while(0); \ #define ASSERT_EQUALS(expected,actual) \ do { \ current_test.assertEquals(expected, actual, __FILE__, __LINE__); \ } while(0); \ #define ASSERT_NOT_NULL(condition) \ do { \ current_test.assertNotNull(condition, __FILE__, __LINE__); \ } while(0); \ #define ASSERT_NULL(condition) \ do { \ current_test.assertNull(condition, __FILE__, __LINE__); \ } while(0); \ #define ASSERT_THROW(expression) \ do { \ bool throw_error = false; \ try { \ expression; \ throw_error = true; \ } catch(Exception&) { \ } \ if(throw_error) { \ Array<char> error_message; \ bprintf(error_message, "expected exception but there wasn't any"); \ current_test._throwAssertion(error_message, __FILE__, __LINE__); \ } \ } while(0); \ } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_failure.cpp�����������������������������������������������0000664�0000000�0000000�00000001352�12710376503�0022702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test_failure.h" using namespace indigo; Failure::Failure(const char* exception, const char* message, const char* test_name, const char* file_name, int line_number) : _lineNumber(line_number) { _message.appendString(message, true); _fileName.appendString(file_name, true); _testName.appendString(test_name, true); _exception.appendString(exception, true); } int Failure::getLineNumber() const { return _lineNumber; } const char* Failure::getMessage() const { return _message.ptr(); } const char* Failure::getException() const { return _exception.ptr(); } const char* Failure::getTestName() const { return _testName.ptr(); } const char* Failure::getFileName() const { return _fileName.ptr(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_failure.h�������������������������������������������������0000664�0000000�0000000�00000001137�12710376503�0022350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef TEST_FAILURE_H #define TEST_FAILURE_H #include "base_cpp/array.h" namespace indigo { class Failure { public: Failure (const char* exception, const char* message, const char* testName, const char* fileName, int lineNumber); int getLineNumber() const; const char* getMessage() const; const char* getTestName() const; const char* getFileName() const; const char* getException() const; ~Failure(){} private: Array<char> _message; Array<char> _testName; Array<char> _fileName; Array<char> _exception; int _lineNumber; }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_registry.cpp����������������������������������������������0000664�0000000�0000000�00000000554�12710376503�0023126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test_registry.h" #include "test.h" #include "test_result.h" using namespace indigo; TestRegistry::TestRegistry() { } void TestRegistry::add (Test* test) { _tests.add(test); } void TestRegistry::run (TestResult& result) { result.startTests(); for(int i = 0; i < _tests.size(); ++i) _tests[i]->run (result); result.endTests(); } ����������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_registry.h������������������������������������������������0000664�0000000�0000000�00000000465�12710376503�0022574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef TESTREGISTRY_H #define TESTREGISTRY_H #include "base_cpp/ptr_array.h" namespace indigo { class Test; class TestResult; class TestRegistry { public: TestRegistry(); void add (Test* test); void run (TestResult& result); private: PtrArray<Test> _tests; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_result.cpp������������������������������������������������0000664�0000000�0000000�00000001217�12710376503�0022571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test_result.h" #include "test_failure.h" #include "base_c/nano.h" using namespace indigo; TestResult::TestResult() : _testCount(0) , _secondsElapsed(0.0f) { _startTime = nanoClock(); } TestResult::~TestResult() { } void TestResult::testWasRun() { _testCount++; } void TestResult::startTests() { } int TestResult::addFailure(Failure* failure) { _failures.add(failure); return _failures.size(); } void TestResult::endTests() { _secondsElapsed = nanoHowManySeconds((nanoClock() - _startTime)); } int TestResult::failureCount() const { return _failures.size(); } int TestResult::testCount() const { return _testCount; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_result.h��������������������������������������������������0000664�0000000�0000000�00000001325�12710376503�0022236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef TEST_RESULT_H #define TEST_RESULT_H #include "base_cpp/ptr_array.h" #include "test_failure.h" namespace indigo { class TestResult { public: TestResult (); virtual ~TestResult(); virtual void testWasRun (); virtual void startTests (); virtual void endTests (); virtual int addFailure (Failure* failure); virtual void appendInfoLine(const char* message) const = 0; virtual void appendErrorLine(const char* message) const = 0; virtual void appendSuccessLine(const char* message) const = 0; int failureCount() const; int testCount() const; protected: qword _startTime; int _testCount; float _secondsElapsed; PtrArray<Failure> _failures; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_result_stderr.cpp�����������������������������������������0000664�0000000�0000000�00000004162�12710376503�0024156�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "test_result_stderr.h" #include "test_failure.h" #include "base_cpp/array.h" #include "base_cpp/output.h" #ifdef WIN32 #include <windows.h> #endif #include <iostream> using namespace indigo; void TestResultStdErr::appendInfoLine(const char* message) const { printf("%s\n", message); } void TestResultStdErr::appendErrorLine(const char* message) const { #ifdef WIN32 HANDLE hConsole; hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, TEST_WIN_RED); #else printf("%c[%d;%d;%dm", 0x1B, 0, TEST_RED, TEST_BG_BLACK); #endif printf("%s\n", message); #ifdef WIN32 SetConsoleTextAttribute(hConsole, TEST_WIN_WHITE); #else printf("%c[%d;%d;%dm\n", 0x1B, 0, TEST_WHITE, TEST_BG_BLACK); #endif } void TestResultStdErr::appendSuccessLine(const char* message) const { #ifdef WIN32 HANDLE hConsole; hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, TEST_WIN_GREEN); #else printf("%c[%d;%d;%dm", 0x1B, 0, TEST_GREEN, TEST_BG_BLACK); #endif printf("%s\n", message); #ifdef WIN32 SetConsoleTextAttribute(hConsole, TEST_WIN_WHITE); #else printf("%c[%d;%d;%dm", 0x1B, 0, TEST_WHITE, TEST_BG_BLACK); #endif } void TestResultStdErr::endTests () { TestResult::endTests(); appendInfoLine(""); Array<char> message; bprintf(message, "Finished in %f seconds", _secondsElapsed); appendInfoLine(message.ptr()); appendInfoLine(""); int failure_count = failureCount(); bprintf(message, "%d examples, %d failures", _testCount, failure_count); if(failure_count > 0) { appendErrorLine("ERROR\n"); appendErrorLine(message.ptr()); } else appendSuccessLine(message.ptr()); for (int i = 0; i < _failures.size(); ++i) { appendInfoLine(""); bprintf(message, "%d) %s(%d):", i+1, _failures[i]->getFileName(), _failures[i]->getLineNumber()); appendInfoLine(message.ptr()); bprintf(message, "%s in %s\n%s", _failures[i]->getException(), _failures[i]->getTestName(), _failures[i]->getMessage()); appendErrorLine(message.ptr()); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/common/unit_test/test_result_stderr.h�������������������������������������������0000664�0000000�0000000�00000001071�12710376503�0023617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef TESTRESULTSTDERR_H #define TESTRESULTSTDERR_H #include "test_result.h" namespace indigo { class TestResultStdErr : public TestResult { enum { TEST_WIN_RED = 4, TEST_WIN_GREEN = 2, TEST_WIN_WHITE = 15, TEST_RED = 31, TEST_GREEN = 32, TEST_WHITE = 38, TEST_BG_BLACK = 38 }; public: virtual void appendInfoLine(const char* message) const; virtual void appendErrorLine(const char* message) const; virtual void appendSuccessLine(const char* message) const; virtual void endTests(); }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/��������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0015302�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/CMakeLists.txt������������������������������������������������������������0000664�0000000�0000000�00000000474�12710376503�0020047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROJECT(Graph) file (GLOB Graph_src src/*.c src/*.cpp) file (GLOB Graph_headers *.h *.hpp src/*.h src/*.hpp src/*.inc) include_directories(${Common_SOURCE_DIR} ${Graph_SOURCE_DIR}/..) add_library(graph OBJECT ${Graph_src} ${Graph_headers}) set_target_properties(graph PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}")����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/automorphism_search.h�����������������������������������������������������0000664�0000000�0000000�00000012010�12710376503�0021521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __automorphism_search__ #define __automorphism_search__ #include "base_cpp/reusable_obj_array.h" #include "base_cpp/tlscont.h" #include "graph/graph.h" namespace indigo { class AutomorphismSearch { public: explicit AutomorphismSearch (); virtual ~AutomorphismSearch (); // obtain return canonical ordering bool getcanon; // vertex compare method compares vertices degree first by default // if compare_vertex_degree_first is false then vertex degree is // compared in vertex compare method after cb_vertex_cmp. bool compare_vertex_degree_first; // Reverse degree order in refine the refine method // By default nontrivial cell refines cell by degree from lowest to highest. // If reverse_degree_in_refine is true then this degree is reversed. // Trival cell always refines cell by degree from highest to lowest, but if // nesessary for this flag can be added. bool refine_reverse_degree; // With this flag during refinement cells are refined and sorted by // sorted neighbourhood of cell indices. // By default this flag is disabled. bool refine_by_sorted_neighbourhood; int worksize; void *context; const int *ignored_vertices; int (*cb_vertex_cmp) (Graph &graph, int idx1, int idx2, const void *context); int (*cb_vertex_rank) (Graph &graph, int vertex_idx, const void *context); int (*cb_edge_rank) (Graph &graph, int edge_idx, const void *context); bool (*cb_check_automorphism) (Graph &graph, const Array<int> &mapping, const void *context); int (*cb_compare_mapped) (Graph &graph, const Array<int> &mapping1, const Array<int> &mapping2, const void *context); void *context_automorphism; void (*cb_automorphism) (const int *automorphism, void *context); void process (Graph &graph); void getCanonicalNumbering (Array<int> &numbering); void getOrbits (Array<int> &orbits) const; void getCanonicallyOrderedOrbits (Array<int> &orbits) const; enum { INFINITY = 0x7FFF }; DECL_ERROR; protected: enum { _INITIAL = 1, _FIRST_LOOP, _OTHER_LOOP, _FIRST_TO_FIRST, _FIRST_TO_OTHER, _OTHER_TO_OTHER }; struct _Call { int level; int numcells; int k; int tc; int tv1; int place; // _INITIAL, _FIRST_TO_FIRST, etc. }; CP_DECL; TL_CP_DECL(Array<_Call>, _call_stack); TL_CP_DECL(Array<int>, _lab); TL_CP_DECL(Array<int>, _ptn); TL_CP_DECL(Graph, _graph); TL_CP_DECL(Array<int>, _mapping); TL_CP_DECL(Array<int>, _inv_mapping); TL_CP_DECL(Array<int>, _degree); TL_CP_DECL(ReusableObjArray< Array<int> >, _tcells); TL_CP_DECL(ReusableObjArray< Array<int> >, _fix); TL_CP_DECL(ReusableObjArray< Array<int> >, _mcr); TL_CP_DECL(Array<int>, _active); TL_CP_DECL(Array<int>, _workperm); TL_CP_DECL(Array<int>, _workperm2); TL_CP_DECL(Array<int>, _bucket); TL_CP_DECL(Array<int>, _count); TL_CP_DECL(Array<int>, _firstlab); TL_CP_DECL(Array<int>, _canonlab); TL_CP_DECL(Array<int>, _orbits); TL_CP_DECL(Array<int>, _fixedpts); TL_CP_DECL(Array<int[2]>, _work_active_cells); TL_CP_DECL(Array<int>, _edge_ranks_in_refine); int _n; Graph *_given_graph; int _gca_first; int _canonlevel, _gca_canon; int _cosetindex; bool _needshortprune; int _orbits_num; void _prepareGraph (Graph &graph); int _firstNode (int level, int numcells); int _otherNode (int level, int numcells); void _refine (int level, int &numcells); void _refineOriginal (int level, int &numcells); void _refineBySortingNeighbourhood (int level, int &numcells); void _refineByCell (int split1, int split2, int level, int &numcells, int &hint, int target_edge_rank); int _targetcell (int level, Array<int> &cell); void _breakout (int level, int tc, int tv); int _shortPrune (Array<int> &tcell, Array<int> &mcr, int idx); int _longPrune (Array<int> &tcell, Array<int> &fix, int idx); void _recover (int level); int _processNode (int level, int numcells); bool _isAutomorphism (Array<int> &perm); int _compareCanon (); void _buildFixMcr (const Array<int> &perm, Array<int> &fix, Array<int> &mcr); void _joinOrbits (const Array<int> &perm); void _handleAutomorphism (const Array<int> &perm); bool _hasEdgeWithRank (int from, int to, int target_edge_rank); static int _cmp_vertices (int idx1, int idx2, void *context); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/aux_path_finder.h���������������������������������������������������������0000664�0000000�0000000�00000002130�12710376503�0020607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _AUX_PATH_FINDER_H__ #define _AUX_PATH_FINDER_H__ #include "base_cpp/array.h" #include "base_cpp/queue.h" namespace indigo { class AuxiliaryGraph; class AuxPathFinder { public: AuxPathFinder (AuxiliaryGraph &graph, int max_size); bool find (Array<int>& vertices, Array<int>& edges, int u, int v); private: Queue<int> _queue; Array<int> _prev; AuxiliaryGraph &_graph; AuxPathFinder(const AuxPathFinder&); }; } #endif /* _AUX_PATH_FINDER_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/biconnected_decomposer.h��������������������������������������������������0000664�0000000�0000000�00000003700�12710376503�0022150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __biconnected_decomposer_h__ #define __biconnected_decomposer_h__ #include "base_cpp/tlscont.h" #include "base_cpp/ptr_array.h" #include "graph/graph.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT BiconnectedDecomposer { public: explicit BiconnectedDecomposer (const Graph &graph); virtual ~BiconnectedDecomposer (); // returns the amount of biconnected components int decompose (); int componentsCount (); bool isArticulationPoint (int idx) const; void getComponent (int idx, Filter &filter) const; const Array<int> & getIncomingComponents (int idx) const; int getIncomingCount (int idx) const; void getVertexComponents (int idx, Array<int> &components) const; DECL_ERROR; protected: void _biconnect (int v, int u); bool _pushToStack (Array<int> &dfs_stack, int v); void _processIfNotPushed (Array<int> &dfs_stack, int w); const Graph &_graph; CP_DECL; TL_CP_DECL(PtrArray<Array<int> >, _components); TL_CP_DECL(Array<int>, _dfs_order); TL_CP_DECL(Array<int>, _lowest_order); TL_CP_DECL(PtrArray<Array<int> >, _component_lists); TL_CP_DECL(Array<Array<int> *>, _component_ids); TL_CP_DECL(Array<Edge>, _edges_stack); int _cur_order; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������Indigo-indigo-1.2.3/graph/cycle_basis.h�������������������������������������������������������������0000664�0000000�0000000�00000002302�12710376503�0017730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _CYCLE_BASIS_H_ #define _CYCLE_BASIS_H_ #include "base_cpp/obj_array.h" #include "base_cpp/red_black.h" namespace indigo { class Graph; class CycleBasis { public: CycleBasis() {} void create(const Graph& graph); int getCyclesCount() const { return _cycles.size(); } const Array<int>& getCycle(int num) const {return _cycles[num]; } bool containsVertex(int vertex) const; private: CycleBasis(const CycleBasis&);// no implicit copy ObjArray< Array<int> > _cycles; RedBlackSet<int> _cycleVertices; }; } #endif /* _CYCLE_BASIS_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/cycle_enumerator.h��������������������������������������������������������0000664�0000000�0000000�00000002557�12710376503�0021024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cycle_enumerator_h__ #define __cycle_enumerator_h__ #include "base_cpp/array.h" namespace indigo { class Graph; class SpanningTree; class Filter; class CycleEnumerator { public: explicit CycleEnumerator (Graph &graph); ~CycleEnumerator (); int min_length; int max_length; void *context; Filter *vfilter; bool (*cb_check_vertex)(Graph &graph, int v_idx, void *context); bool (*cb_handle_cycle)(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); bool process (); protected: bool _pathFinder (const SpanningTree &spt, int ext_v1, int ext_v2, int ext_e); Graph &_graph; private: CycleEnumerator (const CycleEnumerator &); // no implicit copy }; } #endif �������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/dfs_walk.h����������������������������������������������������������������0000664�0000000�0000000�00000005302�12710376503�0017245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __dfs_walk_h__ #define __dfs_walk_h__ #include "base_cpp/exception.h" #include "base_cpp/tlscont.h" #include "graph.h" namespace indigo { class Graph; class DfsWalk { public: struct SeqElem { int idx; // index of vertex in _graph int parent_vertex; // parent vertex in DFS tree int parent_edge; // edge to parent vertex }; explicit DfsWalk (const Graph &graph); virtual ~DfsWalk (); void walk (); const Array<SeqElem> & getSequence () const; bool isClosure (int e_idx) const; int numBranches (int v_idx) const; int numOpenings (int v_idx) const; // mapping[i] = index of appearance of graph's i-th vertex in the vertex sequence void calcMapping (Array<int> &mapping) const; int *ignored_vertices; int *vertex_ranks; int *vertex_classes; // -1 = unclassified, >=0 = class number // walking from internal vertex of a class is prohibited void mustBeRootVertex (int v_idx); void getNeighborsClosing (int v_idx, Array<int> &res); DECL_ERROR; protected: struct _VertexDesc { int dfs_state; // 0 -- not on stack // 1 -- on stack // 2 -- removed from stack int parent_vertex; // parent vertex in DFS tree int parent_edge; // edge to parent vertex int branches; // how many DFS branches go out from this vertex int openings; // how many cycles are starting in this vertex }; struct _EdgeDesc { int closing_cycle; // 1 if this edge closes a cycle }; static int _cmp_ranks (VertexEdge &ve1, VertexEdge &ve2, void *context); static int _cmp_classes (VertexEdge &ve1, VertexEdge &ve2, void *context); int _current_class; const Graph &_graph; CP_DECL; TL_CP_DECL(Array<_VertexDesc>, _vertices); TL_CP_DECL(Array<_EdgeDesc>, _edges); TL_CP_DECL(Array<SeqElem>, _v_seq); TL_CP_DECL(Array<int>, _root_vertices); TL_CP_DECL(Array<Edge>, _closures); TL_CP_DECL(Array<int>, _class_dist_from_exit); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/edge_rotation_matcher.h���������������������������������������������������0000664�0000000�0000000�00000002576�12710376503�0022013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __edge_rotation_matcher__ #define __edge_rotation_matcher__ #include "base_cpp/exception.h" namespace indigo { class Graph; struct Vec3f; class EdgeRotationMatcher { public: // takes mapping from subgraph to supergraph EdgeRotationMatcher (Graph &subgraph, Graph &supergraph, const int *mapping); void (*cb_get_xyz) (Graph &graph, int vertex_idx, Vec3f &pos); bool (*cb_can_rotate) (Graph &graph, int edge_idx); bool equalize_edges; bool match (float rsm_threshold, float eps); DECL_ERROR; protected: struct _DirEdge { int idx, beg, end; }; Graph &_subgraph; Graph &_supergraph; const int *_mapping; private: EdgeRotationMatcher (const EdgeRotationMatcher &); // no implicit copy }; } #endif ����������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/edge_subgraph_enumerator.h������������������������������������������������0000664�0000000�0000000�00000004030�12710376503�0022510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __edge_subgraph_enumerator__ #define __edge_subgraph_enumerator__ #include "graph/graph.h" #include "base_cpp/tlscont.h" namespace indigo { class EdgeSubgraphEnumerator { public: explicit EdgeSubgraphEnumerator (Graph &graph); int min_edges; int max_edges; void process (); void (*cb_subgraph) (Graph &graph, const int *vertices, const int *edges, void *userdata); void *userdata; DECL_ERROR; protected: int _fCIS (); Graph &_graph; CP_DECL; TL_CP_DECL(Graph, _subgraph); TL_CP_DECL(Array<int>, _mapping); // subgraph -> graph TL_CP_DECL(Array<int>, _inv_mapping); // graph -> subgraph TL_CP_DECL(Array<int>, _edge_mapping); // subgraph -> graph TL_CP_DECL(Array<int>, _inv_edge_mapping); // graph -> subgraph TL_CP_DECL(Pool<List<int>::Elem>, _pool); TL_CP_DECL(Array<int>, _adjacent_edges); class _Enumerator { public: _Enumerator (EdgeSubgraphEnumerator &context); _Enumerator (const _Enumerator &other); void process (); protected: EdgeSubgraphEnumerator &_context; Graph &_graph; Graph &_subgraph; void _addEdgeToSubgraph (int edge_idx); void _removeAddedEdge (); void _addAdjacentEdge (int edge_idx); void _removeAdjacentEdges (); int _added_vertex; int _added_edge; List<int> _adjacent_edges_added; }; }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/embedding_enumerator.h����������������������������������������������������0000664�0000000�0000000�00000012352�12710376503�0021635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __embedding_enumerator__ #define __embedding_enumerator__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "base_cpp/red_black.h" #include "base_cpp/list.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "graph/graph_fast_access.h" namespace indigo { class Graph; class GraphVertexEquivalence; class CancellationHandler; class DLLEXPORT EmbeddingEnumerator { public: enum { UNMAPPED = -1, TERM_OUT = -2, IGNORE = -3 }; bool allow_many_to_one; EmbeddingEnumerator (Graph &supergraph); ~EmbeddingEnumerator (); // when cb_embedding returns zero, enumeration stops int (*cb_embedding) (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); bool (*cb_match_vertex) (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); bool (*cb_match_edge) (Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata); void (*cb_vertex_remove) (Graph &subgraph, int sub_idx, void *userdata); void (*cb_edge_add) (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); void (*cb_vertex_add) (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); bool (*cb_allow_many_to_one) (Graph &subgraph, int sub_idx, void *userdata); void *userdata; void setSubgraph (Graph &subgraph); void ignoreSubgraphVertex (int idx); void ignoreSupergraphVertex (int idx); int countUnmappedSubgraphVertices (); int countUnmappedSupergraphVertices (); int countUnmappedSubgraphEdges (); int countUnmappedSupergraphEdges (); void setEquivalenceHandler (GraphVertexEquivalence *equivalence_handler); bool fix (int node1, int node2); bool unsafeFix (int node1, int node2); // returns 0 if cb_embedding returned 0, 1 otherwise int process (); void processStart (); bool processNext (); const int * getSubgraphMapping (); const int * getSupergraphMapping (); // Update internal structures to fit all target vertices that might be added void validate (); DECL_ERROR; DECL_TIMEOUT_EXCEPTION; protected: // digit 1 relates to the subgraph, 2 relates to the supergraph. Graph *_g1; Graph *_g2; GraphVertexEquivalence *_equivalence_handler; CP_DECL; TL_CP_DECL(Array<int>, _core_1); TL_CP_DECL(Array<int>, _core_2); TL_CP_DECL(Array<int>, _term2); TL_CP_DECL(Array<int>, _unterm2); TL_CP_DECL(Pool<RedBlackSet<int>::Node>, _s_pool); TL_CP_DECL(GraphFastAccess, _g1_fast); TL_CP_DECL(GraphFastAccess, _g2_fast); void _terminatePreviousMatch (); // // Query nodes sequence calculation // struct _QuertMatchState { _QuertMatchState (int atom_index, int parent_index, int t1_len) : atom_index(atom_index), parent_index(parent_index), t1_len(t1_len) {} int atom_index, parent_index, t1_len; }; TL_CP_DECL(Array<_QuertMatchState>, _query_match_state); void _fixNode1 (int node1, int node2); int _getNextNode1 (); int _t1_len_pre; enum { _RETURN0 = 0, _NOWAY = 1, _ADD_PAIR = 2 }; class _Enumerator { public: _Enumerator (EmbeddingEnumerator &context); _Enumerator (const _Enumerator &other); bool fix (int node1, int node2, bool safe); void setUseEquivalence (bool use); void reset (); int nextPair (); void addPair (int node1, int node2); void restore (); void initForFirstSearch (int t1_len); int _current_node1, _current_node2; protected: EmbeddingEnumerator &_context; bool _use_equivalence; RedBlackSet<int> _mapped_orbit_ids; int _term2_begin; int _unterm2_begin; int _core_len; int _t1_len, _t2_len; int _selected_node1, _selected_node2; int _node1_prev_value, _node2_prev_value; int _current_node1_idx, _current_node2_idx; int _current_node2_parent; int _current_node2_nei_index; bool _checkNode1 (int node1); bool _checkNode2 (int node2, int for_node1); bool _checkPair (int node1, int node2); void _initState (); void _addPairNode2 (int node1, int node2); void _fixPair (int node1, int node2); }; TL_CP_DECL(ObjArray<_Enumerator>, _enumerators); int _cancellation_check_number; CancellationHandler* _cancellation_handler; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/embeddings_storage.h������������������������������������������������������0000664�0000000�0000000�00000004441�12710376503�0021303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __embeddings_storage__ #define __embeddings_storage__ #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/red_black.h" #include "base_cpp/exception.h" #include "graph/graph_fast_access.h" namespace indigo { class Graph; class GraphEmbeddingsStorage { public: GraphEmbeddingsStorage (); // True if to take into account edges with checking for unique embedding bool unique_by_edges; bool check_uniquencess; bool save_edges, save_mapping; // Add embedding to the storage and returns true if such embedding // hasn't already been in the storage. // Embeddings with the same set of vertices and edges are equivalent. bool addEmbedding (const Graph &super, const Graph &sub, int *core_sub); void clear(); bool isEmpty () const; int count () const; const int* getVertices (int emb_idx, int &count) const; const int* getEdges (int emb_idx, int &count) const; const int* getMappingSub (int emb_idx, int &count) const; DECL_ERROR; private: Array<int> _all_vertices, _all_edges, _all_mappings; struct _EmbeddingData { bool sorted; int vertex_begin, vertex_count; int edge_begin, edge_count; int sub_mapping_begin, sub_mapping_count; int next; // Index to the next }; Array<_EmbeddingData> _embedding_data; RedBlackMap<dword, int> _map_hash_to_id; static dword _calcSetHash (const Array<int> &set, int offset, int size); bool _compareEmbedding(int id, int id2); void _prepareForCompare (int id); class IntCmpFunctor { public: int operator() (int v1, int v2) { return v1 - v2; } }; }; } #endif // __embeddings_storage__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/filter.h������������������������������������������������������������������0000664�0000000�0000000�00000003121�12710376503�0016735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __filter_h__ #define __filter_h__ #include "base_cpp/array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Graph; class DLLEXPORT Filter { public: enum { EQ = 1, NEQ = 2, LESS = 3, MORE = 4 }; Filter (); Filter (const int *filter, int type, int value); void init (const int *filter, int type, int value); void initAll (int size); void initNone (int size); void hide (int idx); void unhide (int idx); bool valid (int idx) const; void collectGraphVertices (const Graph &graph, Array<int> &indices) const; void collectGraphEdges (const Graph &graph, Array<int> &indices) const; int count (const Graph &graph) const; DECL_ERROR; protected: const int *_filter; Array<int> _own; int _value; int _type; private: Filter (const Filter &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph.h�������������������������������������������������������������������0000664�0000000�0000000�00000014505�12710376503�0016561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_h__ #define __graph_h__ #include "base_cpp/array.h" #include "base_cpp/list.h" #include "base_cpp/non_copyable.h" #include "base_cpp/obj_pool.h" #include "base_cpp/obj_array.h" #include "graph/filter.h" #include "graph/graph_iterators.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { enum { FILTER_EQ, FILTER_NEQ, FILTER_MORE }; enum { TOPOLOGY_RING = 1, TOPOLOGY_CHAIN = 2 }; struct VertexEdge { VertexEdge () {} VertexEdge (int vertex, int edge) : v(vertex), e(edge) {} int v; int e; }; class DLLEXPORT Vertex { public: Vertex (Pool<List<VertexEdge>::Elem> &pool) : neighbors_list(pool) {} ~Vertex () {} List<VertexEdge> neighbors_list; NeighborsAuto neighbors() const; int neiBegin () const { return neighbors_list.begin(); } int neiEnd () const { return neighbors_list.end(); } int neiNext (int i) const { return neighbors_list.next(i); } int neiVertex (int i) const { return neighbors_list[i].v; } int neiEdge (int i) const { return neighbors_list[i].e; } int findNeiVertex (int idx) const; int findNeiEdge (int idx) const; int degree () const {return neighbors_list.size();} private: Vertex (const Vertex &); // no implicit copy }; struct Edge { int beg; int end; int findOtherEnd (int i) const { if (i == beg) return end; if (i == end) return beg; return -1; } }; class CycleBasis; class DLLEXPORT Graph : public NonCopyable { public: DECL_ERROR; explicit Graph (); virtual ~Graph (); VerticesAuto vertices (); EdgesAuto edges (); virtual void clear (); const Vertex & getVertex (int idx) const; const Edge & getEdge (int idx) const; int vertexBegin () const {return _vertices->begin();} int vertexEnd () const {return _vertices->end();} int vertexNext (int i) const {return _vertices->next(i); } int vertexCount () const {return _vertices->size(); } int edgeBegin () const {return _edges.begin();} int edgeEnd () const {return _edges.end();} int edgeNext (int i) const {return _edges.next(i); } int edgeCount () const {return _edges.size(); } int addVertex (); int addEdge (int beg, int end); int findEdgeIndex (int beg, int end) const; bool haveEdge (int beg, int end) const; bool hasEdge (int idx) const; bool hasVertex(int idx) const; int getEdgeEnd (int beg, int edge) const; void swapEdgeEnds (int edge_idx); void removeEdge (int idx); void removeVertex (int idx); void removeAllEdges (); bool findPath (int from, int where, Array<int> &path_out) const; void makeSubgraph (const Graph &other, const Array<int> &vertices, Array<int> *vertex_mapping); void makeSubgraph (const Graph &other, const Array<int> &vertices, Array<int> *vertex_mapping, const Array<int> *edges, Array<int> *edge_mapping); void makeSubgraph (const Graph &other, const Filter &filter, Array<int> *mapping_out, Array<int> *inv_mapping); void cloneGraph (const Graph &other, Array<int> *mapping); void buildEdgeMapping (const Graph &other, Array<int> *mapping, Array<int> *edge_mapping); void mergeWith (const Graph &other, Array<int> *mapping); void makeEdgeSubgraph (const Graph &other, const Array<int> &vertices, const Array<int> &edges, Array<int> *v_mapping, Array<int> *e_mapping); int getEdgeTopology (int idx); void setEdgeTopology (int idx, int topology); void validateEdgeTopologies (); static bool isConnected (Graph &graph); static bool isChain_AssumingConnected (const Graph &graph); static bool isTree (Graph &graph); static void filterVertices (const Graph &graph, const int *filter, int filter_type, int filter_value, Array<int> &result); static void filterEdges (const Graph &graph, const int *filter, int filter_type, int filter_value, Array<int> &result); static int findMappedEdge (const Graph &graph, const Graph &mapped_graph, int edge_idx, const int *mapping); int vertexCountSSSR (int idx); int vertexSmallestRingSize (int idx); bool vertexInRing(int idx); int edgeSmallestRingSize (int idx); List<int> & sssrEdges (int idx); List<int> & sssrVertices (int idx); int sssrCount (); int vertexComponent (int v_idx); int countComponents (); int countComponentVertices (int comp_idx); int countComponentEdges (int comp_idx); const Array<int> & getDecomposition (); protected: void _mergeWithSubgraph (const Graph &other, const Array<int> &vertices, const Array<int> *edges, Array<int> *mapping, Array<int> *edge_mapping); Pool<List<VertexEdge>::Elem> *_neighbors_pool; ObjPool<Vertex> *_vertices; Pool<Edge> _edges; Array<int> _topology; // for each edge: TOPOLOGY_RING, TOPOLOGY_CHAIN, or -1 (not calculated) bool _topology_valid; Array<int> _v_smallest_ring_size, _e_smallest_ring_size; Array<int> _v_sssr_count; Pool<List<int>::Elem> *_sssr_pool; ObjArray< List<int> > _sssr_vertices; ObjArray< List<int> > _sssr_edges; bool _sssr_valid; Array<int> _component_numbers; Array<int> _component_vcount; Array<int> _component_ecount; bool _components_valid; int _components_count; void _calculateTopology (); void _calculateSSSR (); void _calculateSSSRInit (); void _calculateSSSRByCycleBasis (CycleBasis &basis); void _calculateSSSRAddEdgesAndVertices (const Array<int> &cycle, List<int> &edges, List<int> &vertices); void _calculateComponents (); // This is a bad hack for those who are too lazy to handle the mappings. // NEVER USE IT. void _cloneGraph_KeepIndices (const Graph &other); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_affine_matcher.h����������������������������������������������������0000664�0000000�0000000�00000002436�12710376503�0021574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_affine_matcher__ #define __graph_affine_matcher__ #include "base_cpp/array.h" namespace indigo { class Graph; struct Vec3f; class GraphAffineMatcher { public: // takes mapping from subgraph to supergraph GraphAffineMatcher (Graph &subgraph, Graph &supergraph, const int *mapping); bool match (float rms_threshold); void (*cb_get_xyz) (Graph &graph, int vertex_idx, Vec3f &pos); const Array<int> *fixed_vertices; DECL_ERROR; protected: Graph &_subgraph; Graph &_supergraph; const int *_mapping; private: GraphAffineMatcher (const GraphAffineMatcher &); // guess what? tip: look at any other class }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_constrained_bmatching_finder.h��������������������������������������0000664�0000000�0000000�00000007373�12710376503�0024522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_constrained_bmatching_finder_h__ #define __graph_constrained_bmatching_finder_h__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" #include "graph/skew_symmetric_network.h" namespace indigo { class Graph; /* A b-matching M of G=(V,E) is defined as a subset of E with * condition f(v) <= c(v), where c(v) is the node capacity and * f(v) is the number of incident to node v edges from M. * * B-matching M is call maximum if |M| >= |M'| for * all b-matchings M' * * The algorithm provides possibility to find B-matching with * specified cardinality. * In addition, it allows to find b-matching * with constraints for capacities for vertices sets with hierarchy. * * Maximum edges multiplicities (when edge can be used multiple * times) can be specified too. By default they are set to 1. * * Algorithm is based on finding maximum integer skew-symmetric * flow in skew-symmetric network. */ class GraphConstrainedBMatchingFinder { public: /* Parameter per_node_set_id[i] defines node set id from 0 to N, * but if per_node_set_id[i] = -1 then node doesn't belong to any * constrained set. Vertices with same node set is are in the same * constrained set * Initially all arc capacities are set to zero. */ GraphConstrainedBMatchingFinder (const Graph &g, const ObjArray< Array<int> > &nodes_per_set, const Array<int> *per_set_set_id); void setNodeCapacity (int node, int capacity, int set_id); int getNodeCapacity (int node, int set_id) const; void setNodeSetCapacity (int set_id, int capacity); int getNodeSetCapacity (int set_id) const; void setMaxEdgeMultiplicity (int edge, int capacity); int getMaxEdgeMultiplicity (int edge) const; /* This method tries to find b-matching with specified cardinality. * If return value if true then matching was found, but if * return value is false then maximum b-matching was found * although it is has cardinality lower than specified. */ bool findMatching (int cardinality); int getEdgeMultiplicity (int edge) const; int getNodeIncidentEdgesCount (int node) const; DECL_ERROR; private: struct ConstraintSet { int node; int in_arc; }; void _createSets (int n, int root, const Array<int> *per_set_set_id); void _createSet (int idx, int root, const Array<int> *per_set_set_id); void _createVertices (); void _createEdges (); void _connectVerticesWithSets (const ObjArray< Array<int> > &nodes_per_set); const Graph &_g; CP_DECL; TL_CP_DECL(SkewSymmetricNetwork, _network); // Edges mapping between graph and network TL_CP_DECL(Array<int>, _edges_graph_to_net); TL_CP_DECL(Array<int>, _vertices_graph_to_net); TL_CP_DECL(ReusableObjArray< Array<int> >, _vertices_capacity_arc_per_set); TL_CP_DECL(Array<ConstraintSet>, _constraint_sets); TL_CP_DECL(Array<int>, _edge_matching_multiplicity); TL_CP_DECL(Array<int>, _node_incident_edges_count); int _source_edge; }; } #endif // __graph_constrained_bmatching_h__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_decomposer.h��������������������������������������������������������0000664�0000000�0000000�00000003323�12710376503�0020775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_decomposer__ #define __graph_decomposer__ #include "base_cpp/exception.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Graph; class Filter; class DLLEXPORT GraphDecomposer { public: GraphDecomposer (const Graph &graph); ~GraphDecomposer (); // returns the amount of connected components int decompose (const Filter *filter = NULL, const Filter *edge_filter = NULL); const Array<int> & getDecomposition () const; int getComponent (int vertex) const; int getComponentsCount () const; int getComponentVerticesCount (int component) const; int getComponentEdgesCount (int component) const; DECL_ERROR; protected: const Graph &_graph; int n_comp; CP_DECL; TL_CP_DECL(Array<int>, _component_ids); TL_CP_DECL(Array<int>, _component_vertices_count); TL_CP_DECL(Array<int>, _component_edges_count); private: GraphDecomposer (const GraphDecomposer &); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_fast_access.h�������������������������������������������������������0000664�0000000�0000000�00000004153�12710376503�0021115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_fast_access_h__ #define __graph_fast_access_h__ #include "base_cpp/array.h" #include "graph/graph.h" namespace indigo { class Graph; class GraphFastAccess { public: void setGraph (Graph &g); int* prepareVertices (int &count); int getVertex (int idx); // unsafe int vertexCount (); // Prepare both nei vertices and edges list. Runs faster then // preparing them one by one. void prepareVertexNeiVerticesAndEdges (int v); // Returns nei vertices and nei edges for specified vertex // Numeration is coherent // Note: pointer might become invalid after preparing calls for // different vertices. In this case use prepareVertexNeiVertices/getVertexNeiVertiex. int* getVertexNeiVertices (int v, int &count); int* getVertexNeiEdges (int v, int &count); // Returns vertex identifier that can be used in getVertexNeiVertiex int prepareVertexNeiVertices (int v, int &count); // Returns neighbor vertex for the specified // vertex id (returned by prepareVertexNeiVertices) int getVertexNeiVertiex (int v_id, int index); int findEdgeIndex (int v1, int v2); void prepareEdges (); const Edge& getEdge (int e); const Edge* getEdges (); private: Graph *_g; Array<int> _vertices; struct VertexNeiBlock { int v_begin, v_count; int e_begin, e_count; }; Array<VertexNeiBlock> _vertices_nei; Array<int> _nei_vertices_data, _nei_edges_data; Array<Edge> _edges; }; } #endif // __graph_fast_access_h__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_iterators.h���������������������������������������������������������0000664�0000000�0000000�00000002342�12710376503�0020651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef __graph_iterators_h__ #define __graph_iterators_h__ #include "base_cpp/auto_iter.h" namespace indigo { class Graph; class Vertex; struct Edge; class VertexIter : public AutoIterator { public: VertexIter (Graph &owner, int idx); VertexIter & operator++ (); private: Graph &_owner; }; class VerticesAuto { public: VerticesAuto (Graph &owner); VertexIter begin (); VertexIter end (); private: Graph &_owner; }; class EdgeIter : public AutoIterator { public: EdgeIter (Graph &owner, int idx); EdgeIter & operator++ (); private: Graph &_owner; }; class EdgesAuto { public: EdgesAuto (Graph &owner); EdgeIter begin (); EdgeIter end (); private: Graph &_owner; }; class NeighborIter : public AutoIterator { public: NeighborIter(const Vertex &owner, int idx); NeighborIter & operator++ (); private: const Vertex &_owner; }; class NeighborsAuto { public: NeighborsAuto ( const Vertex &owner); NeighborIter begin (); NeighborIter end (); private: const Vertex &_owner; }; }; #endif //__graph_iterators_h__����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_perfect_matching.h��������������������������������������������������0000664�0000000�0000000�00000006351�12710376503�0022143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Solve graph perfect matching problem module: // To find maximum independent edge set // #ifndef __graph_perfect_matching_h__ #define __graph_perfect_matching_h__ #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" namespace indigo { class Graph; // Graph matching problem solver class GraphPerfectMatching { public: enum { USE_EXTERNAL_EDGES_PTR = 0x01, USE_EDGES_MAPPING = 0x02, USE_VERTICES_SET = 0x04}; public: GraphPerfectMatching (const Graph &graph, int params); virtual ~GraphPerfectMatching (); void reset (void); void setEdgeMatching (int edge_idx, bool matching); int isEdgeMatching (int edge_idx); const byte* getEdgesState (void); int isVertexInMatching (int v_idx); void removeVertexFromMatching (int v_idx); void setAllVerticesInMatching (void); void setMatchingEdgesPtr (byte *matchingEdges); void setEdgesMappingPtr (int *edgesMap); void setVerticesSetPtr (int *verticesSet, int count); bool findMatching (void); bool findAlternatingPath (void); bool findAlternatingPath (int v1, int v2, bool isFirstEdgeMatching, bool isLastEdgeMatching); void processPath (void); int* getPath (void); int getPathSize (void); void setPath (int *path, int length); void clearPath (void); virtual bool checkVertex (int v_idx) { return true; } // e_idx - edge index in graph (not mapping) virtual bool checkEdge (int e_idx) { return true; } DECL_ERROR; protected: bool _PathFinder (int v_idx, int needMatchingEdge); protected: struct VertexExtInfo { int inPathMark; int isInMatching; }; enum { FIND_ANY_ALTERNATING_PATH, FIND_ALTERNATING_CIRCLE }; protected: const Graph &_graph; CP_DECL; TL_CP_DECL(Array<byte>, _matchingEdgesLocal); TL_CP_DECL(Array<VertexExtInfo>, _verticesInfo); // Path has the following format: (v0, localEdge0, localEdge1, ...) TL_CP_DECL(Array<int>, _path); TL_CP_DECL(Array<int>, _edgesMappingLocal); TL_CP_DECL(Array<int>, _verticesUsedLocal); byte *_matchingEdges; int *_edgesMapping; int *_verticesUsed; int _verticesUsedCount; int _pathFinderState; int _pathFinderStopVertex; int _pathFinderIsLastMatching; int _leftExposedVertices; int _pathFinderUsedMark; }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_subchain_enumerator.h�����������������������������������������������0000664�0000000�0000000�00000002717�12710376503�0022700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __chain_enumerator_h__ #define __chain_enumerator_h__ #include "base_cpp/tlscont.h" #include "graph/graph.h" namespace indigo { class GraphSubchainEnumerator { public: enum { MODE_NO_DUPLICATE_VERTICES = 0, MODE_NO_BACKTURNS = 1, MODE_NO_CONSTRAINTS = 2 }; explicit GraphSubchainEnumerator (Graph &graph, int min_edges, int max_edges, int mode); virtual ~GraphSubchainEnumerator (); void * context; void (*cb_handle_chain) (Graph &graph, int size, const int *vertices, const int *edges, void *context); void processChains (); protected: void _DFS (int from); Graph &_graph; int _max_edges; int _min_edges; int _mode; CP_DECL; TL_CP_DECL(Array<int>, _vertex_states); TL_CP_DECL(Array<int>, _chain_vertices); TL_CP_DECL(Array<int>, _chain_edges); }; } #endif �������������������������������������������������Indigo-indigo-1.2.3/graph/graph_subtree_enumerator.h������������������������������������������������0000664�0000000�0000000�00000004504�12710376503�0022551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_subtree_enumerator__ #define __graph_subtree_enumerator__ #include "base_cpp/tlscont.h" #include "graph/graph.h" #include "base_cpp/list.h" #include "base_cpp/obj_array.h" namespace indigo { class Filter; class GraphSubtreeEnumerator { public: explicit GraphSubtreeEnumerator (Graph &graph); ~GraphSubtreeEnumerator (); Filter *vfilter; void (*callback)(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); // Callback function that returns some value for subgraph. // Graph is treated to be maximal by this criteria if one of its supergraph // has different value from value for current subgraph. If there is no // supergraphs for some graphs (cutted by max_vertices constraint) // then such graph is treated to be maximal too. int (*maximal_critera_value_callback)(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); // Call main callback function only for maximal subgraphs (by maximal // criteria callback or by size). bool handle_maximal; int min_vertices; int max_vertices; void *context; void process (); protected: Graph &_graph; struct VertexEdgeParent { int v; int e; int parent; void reset () { v = e = parent = -1; } }; CP_DECL; TL_CP_DECL(Array<VertexEdgeParent>, _front); // array with current front TL_CP_DECL(Array<int>, _vertices); // array with subgraph vertices TL_CP_DECL(Array<int>, _edges); // array with subgraph edges TL_CP_DECL(Array<int>, _v_processed); // from _graph to _subtree void _reverseSearch (int front_idx, int cur_maximal_criteria_value); VertexEdge _m1, _m2; }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/graph_vertex_equivalence.h������������������������������������������������0000664�0000000�0000000�00000003070�12710376503�0022532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __graph_vertex_equivalence__ #define __graph_vertex_equivalence__ namespace indigo { class Graph; class Output; class Scanner; // Find equivalence classes for vertices. // Used to check if vertices are equivalent during SSS class GraphVertexEquivalence { public: virtual ~GraphVertexEquivalence () {} virtual void construct (const Graph &g) {} virtual void save (Output &output) {} virtual void load (Scanner &input, const Graph &g) {} virtual void prepareForQueries () {} virtual int getVertexEquivalenceClassId (int vertex_idx) { return -1; } virtual void fixVertex (int vertex_idx) {} virtual void unfixVertex (int vertex_idx) {} virtual bool useHeuristicFurther () { return false; } virtual bool isVertexInTransversal (int vertex_idx) { return true; } // This method shouldn't be here... virtual void setNeighbourhoodRadius (int radius) {} }; } #endif // __graph_vertex_equivalence__ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/max_common_subgraph.h�����������������������������������������������������0000664�0000000�0000000�00000055505�12710376503�0021515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _max_common_subgraph #define _max_common_subgraph #include "graph/graph.h" #include "base_cpp/d_bitset.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "graph/embedding_enumerator.h" #include "base_cpp/red_black.h" #include "math.h" #include "base_cpp/obj_list.h" #include "base_cpp/cancellation_handler.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT MaxCommonSubgraph{ public: DECL_ERROR; MaxCommonSubgraph(Graph& subgraph, Graph& supergraph); ~MaxCommonSubgraph(); void setGraphs(Graph& subgraph, Graph& supergraph); //two main methods for maximum common subgraph search //exact searching mcs method. Hanser's algorithm used void findExactMCS(); //approximate method for searching mcs. 2DOM algorithm used void findApproximateMCS(); //parameters for exact method struct ParametersForExact{ //boolean true if method reached max iteration number bool isStopped; //max iteration number int maxIteration; //number of solutions that are finded by exact algorithm int numberOfSolutions; }; //parameters for approximate algorithm struct ParametersForApproximate { //error is the number of unmatched edges in solution int error; //max iteration number. int maxIteration; //number of solutions (connected graphs in solution given by algorithm 2DOM) int numberOfSolutions; //if this parameter set to false then only max solution would be writen. true - then all solution and at first place max solution bool randomize; bool standardRandom; }; //two callbacks for edge and vertices matching bool (*conditionEdgeWeight) (Graph &graph1, Graph &graph2, int i, int j, void *userdata); bool (*conditionVerticesColor) (Graph &graph1, Graph &graph2, const int *core_sub, int i, int j, void *userdata); //parameters for mcs methods ParametersForExact parametersForExact; ParametersForApproximate parametersForApproximate; //array for accept input mapping and working with it Array<int> incomingMap; //this method sorts solutions and maximizes number of the rings in graph static int ringsSolutionTerm(Array<int>&, Array<int>&, void*); //returns all maps-solutions-mcs void getSolutionMaps(ObjArray< Array<int> >* v_maps, ObjArray< Array<int> >* e_maps) const; //returns first element in sorted solution array void getMaxSolutionMap(Array<int>* v_map, Array<int>* e_map) const; //callback for sorting solutions (see _vertEdgeSolMap) int (*cbSolutionTerm) (Array<int>& array1, Array<int>& array2, void* userdata); //context for all callbacks (edge and vertices matching and sort solutions void *userdata; //callback for managing/ if return 0 then algorithm will end its work int (*cbEmbedding) (const int *sub_vert_map, const int *sub_edge_map, const void* info, void* userdata); void *embeddingUserdata; //Exact method: Hanser's algorithm //------------------------------------------------------------------------------------------------------------------- //class represent node of the resolution graph (ReGraph) An RePoint represents an association //betwwen two edges of the source graphs G1 and G2 that are compared class RePoint { public: //creates RePoint for input edge ids RePoint(int n1, int n2); //gets edge id in first graph int getid1() const { return _id1; }; //gets edge id in second graph int getid2() const { return _id2; }; // set of neighbour nodes in the ReGraph Dbitset extension; // set of incompatible nodes in the ReGraph Dbitset forbidden; Dbitset allowed_g1; Dbitset allowed_g2; // sets sizes for all containers void setSizes(int size, int size_g1, int size_g2); private: //number of the edge in the graph 1 int _id1; //number of the edge in the graph 2 int _id2; RePoint(const RePoint&);//no implicit copy }; //solution structure for Resolution graph class Solution { public: Solution() {}; int numBits; Dbitset reSolution; Dbitset solutionProj1; Dbitset solutionProj2; private: Solution(const Solution&);//no implicit copy }; //This class implements the Resolution Graph (ReGraph). //The ReGraph is a graph based representation of the search problem. //An ReGraph is constructred from the two compared graphs (G1 and G2). //Each vertex (node) in the ReGraph represents a possible association //from an edge in G1 with an edge in G2. Thus two compatible edges //in two graphs are represented by a vertex in the ReGraph. //Each edge in the ReGraph corresponds to a common adjacency relationship //between the 2 couple of compatible edges associated to the 2 ReGraph nodes //forming this edge. //Resolution Graph class ReGraph { public: ReGraph(); ReGraph(MaxCommonSubgraph& context); //clears resolution graph void clear(); //sets maximum iterations number void setMaxIteration(int m) {_maxIteration = m; }; //set sizes for util variables void setSizes(int n1, int n2); //adds new RePoint to nodes set void addPoint(int id1, int id2) { _graph.add(new RePoint(id1, id2)); }; //main method to perform a query // Parsing of the ReGraph. This is the recursive method // to perform a query. The method will recursively // parse the RGraph thru connected nodes and visiting the // RGraph using allowed adjacency relationship. void parse(bool findAllStructure); //retruns index of RePoint which corespondes to input edges ids int getPointIndex(int i, int j) const; //returns number of nodes (RePoints) in resolution graph int size() const {return _graph.size(); }; //returns true if algorithm has reached maximum iteration bool stopped() { return _stop; }; //gets RePoint with index i RePoint *getPoint(int i) { return _graph[i]; }; //solution getters //begin solution index int solBegin() const { return _solutionObjList.begin(); }; //next solution index int solNext(int index) const { return _solutionObjList.next(index); }; //return false then it is no more solutions bool solIsNotEnd(int index) const { return (index != _solutionObjList.end()); }; //solutions store capacity int solutionSize() const {return _solutionObjList.size(); }; //returns solution list const Dbitset& getSolBitset(int index) const { return _solutionObjList[index].reSolution; }; //retruns project of solution list to graph 1 const Dbitset& getProj1Bitset(int index) const { return _solutionObjList[index].solutionProj1; }; //retruns project of solution list to graph 2 const Dbitset& getProj2Bitset(int index) const { return _solutionObjList[index].solutionProj2; }; void insertSolution(int ins_index, bool ins_after, const Dbitset& sol, const Dbitset& sol_g1, const Dbitset& sol_g2, int num_bits); //callback for managing/ if return 0 then algorithm will end its work int (*cbEmbedding) (const int *sub_vert_map, const int *sub_edge_map, const void* info, void* userdata); void *userdata; CancellationHandler* cancellation_handler; protected: //list of ReGraph nodes each node keeping track of its neighbours PtrArray<RePoint> _graph; //size of ReGRaph int _size; // current number of iterations int _nbIteration; //maximal number of iterations before search break int _maxIteration; // dimensions of the compared graphs int _firstGraphSize; int _secondGraphSize; // flag to define if we want to get all possible 'structures' bool _findAllStructure; // flag to define if search was breaking bool _stop; // Checks if a potantial solution is a real one // (not included in a previous solution) // and add this solution to the solution list // in case of success. void _solution(const Dbitset& traversed, Dbitset& trav_g1, Dbitset& trav_g2); //Determine if there are potential soltution remaining. bool _mustContinue (const Dbitset& pnode_g1, const Dbitset& pnode_g2) const; //solution bitset store's parameters Pool< ObjList<Solution>::Elem > _pool; ObjList<Solution> _solutionObjList; private: ReGraph(const ReGraph&);//no implicit copy }; //Create Resolution Graph for MCSS class ReCreation { public: ReCreation(ReGraph& rgr, MaxCommonSubgraph& context); //creates resolution graph void createRegraph(); //method change input array to map which corresponds to list of ReGraph nodes (they are in bitset) void setCorrespondence(const Dbitset& b, Array<int>& map) const; /* * Inserts solution from the given mapping */ bool insertSolution(const Array<int>& mapping); //sets input mapping to algorithm using bool setMapping(); //creates all solutions int createSolutionMaps(); //retruns all solutions edge and vertices lists void getSolutionListsSub(ObjArray< Array<int> >& v_lists, ObjArray< Array<int> >& e_lists) const; void getSolutionListsSuper(ObjArray< Array<int> >& v_lists, ObjArray< Array<int> >& e_lists) const; protected: // resolution graph to work with ReGraph &_regraph; // max common subgraph as context MaxCommonSubgraph& _context; //creates nodes of resolution graph void _nodeConstructor(); //creates edges of resolution graph void _edgesConstructor(); //returns common vertex id of two edges in graph. returns -1 if edges hasn't it int _getCommonVertex(int e1, int e2, Graph& graph) const; //returns true if two edges in graph has common vertex bool _hasCommonVertex(int e1, int e2, Graph& graph) const; //returns true if common vertices are matched bool _hasCommonSymbol(int e11, int e12, int e21, int e22) const; //returns edge and vertices list void _createList(const Dbitset& proj_bitset, Graph& graph, Array<int>& v_list, Array<int>& e_list) const; private: ReCreation(const ReCreation&);//no implicit copy }; // Approximate algorithm: two stage optimization method (2DOM) //------------------------------------------------------------------------------------------------------------------- // this class is main util for keeping adjacancy matrix and other parameters for fast work of 2DOM algorithm //Adjacency matrix class AdjMatricesStore { public: AdjMatricesStore(MaxCommonSubgraph& context,int maxsize); //creates utilite store void create(Graph& g1,Graph& g2); //creates all solutions int createSolutionMaps(); //retruns size of first graph to compared int getFirstSize() { return _size1; } //returns size of second graph to campare int getSecondSize() { return _size2; } //returns element with input indexes of adjacency matrix of second graph bool getSecondElement(int i, int j) { return _aj2[i]->at(j); } //retruns elements of utilites matrices which are stored matched edges int getFirstIdxEdge(int i, int j) { return _ajEdge1[i]->at(j); } int getSecondIdxEdge(int i, int j) { return _ajEdge2[i]->at(j); } //retruns degree of vertex in first graph adj matrix int getFirstVDegree(int i) { return _degreeVec1[i]; } //retruns degree of vertex in first graph adj matrix int getSecondVDegree(int i) { return _degreeVec2[i]; } //returns number of unmatched edges int countErrorAtEdges(int i, int j); //returns color and weight conditions if it is presented in max common subgraph context //methods takes account to corresponding between input graph ids and its equals //this for two graphs bool getEdgeWeightCondition(int i, int j); //this for two graphs bool getVerticesColorCondition(int i, int j); //this for one graph (first) bool getVColorOneCondition(int i, int j); //returns dbitset represent row in adjacency matrix of first graph Dbitset* getFirstRow(int i) { return _daj1[i]; } //returns dbitset represent row in adjacency matrix of first graph Dbitset* getSecondRow(int i) { return _daj2[i]; } // retruns solution correspondings between two graphs int* getX() { return _x.ptr(); } int* getY() { return _y.ptr(); } void getSolutions(ObjArray< Array<int> >& v_maps); //returns correspondence parameters between each vertex and vertex in other graph with the same label int getFLSize(int i) { return _mLabel1[i]->size(); } int getFLV(int i, int j) { return _mLabel1[i]->at(j); } //context includes input parameters and output solution MaxCommonSubgraph& _context; protected: //sizes of graphs to compared int _size1; int _size2; //adjacency matrix //PtrArray< Array<bool> > _aj1; PtrArray< Array<bool> > _aj2; //indexes of edges PtrArray< Array<int> > _ajEdge1; PtrArray< Array<int> > _ajEdge2; //correspondence between each vertex and vertex in other graph with the same label PtrArray< Array<int> > _mLabel1; //adjacency matrix in bitset view PtrArray<Dbitset> _daj1; PtrArray<Dbitset> _daj2; //correspondence between two graphs Array<int> _x; Array<int> _y; //correspondence between real graph and matrix Array<int> _cr1; Array<int> _cr2; //degree vectors Array<int> _degreeVec1; Array<int> _degreeVec2; // matrix with not corresponding edges PtrArray< Array<int> > _errorEdgesMatrix; //maps Array<int> _map; Array<int> _invmap; //max size to reserve space in arrays int _maxsize; // true if two input graphs was swapped then initializes bool _swap; // sets adjacency matrix elements for two graphs void _setFirstElement(int i, int j, int value); void _setSecondElement(int i, int j, int value); // returns reverse correspondence between input graphs and its adj matrices int _getFirstC(int x); int _getSecondC(int x); //creation of matrices void _createCorrespondence(); void _createLabelMatrices(); void _createAdjacencyMatrices(); void _createErrorEdgesMatrix(); void _createMaps(); //creation of graph for solution void _createConnectedGraph(Graph& graph, Array<int>& map_gr); //two graphs to compare Graph *_graph1; Graph *_graph2; //retruns false if there is no need to swap graphs bool _checkSize(Graph& g1,Graph& g2); //makes invert map void _makeInvertMap(Array<int>& map, Array<int>& invmap); private: AdjMatricesStore(const AdjMatricesStore &);//no implicit copy }; //Construction stage: greedy method class Greedy { public: Greedy(AdjMatricesStore &aj); //main method in greedy stage of 2DOM algorithm void greedyMethod(); private: //creates lists if vertices void _createLgLh(); //returns number of matched edges int _matchedEdges(); //keeping util class for 2DOM method AdjMatricesStore &_adjMstore; //list of unsigned vertices in first graph Array<int> _unsignVert1; //list of unsigned vertices in second graph PtrArray< Array<int> > _unsignVert2; //adjancy status whether vertex in 2 is adjaent to assigned Array<int> _adjStatus; //assign vertex from 1 graph to 2 int* _x; //assign vertex from 2 graph to 1 int* _y; //size of first graph int _n; //size of second graph int _m; protected: //callbacks for sorting list of vertices static int _compareFirstDegree(int &i1, int &i2, void* context); static int _compareSecondDegree(int &i1, int &i2, void* context); private: Greedy(const Greedy &);//no implicit copy }; //Refinement stage: random discrete descent method class RandomDisDec { public: enum { MAX_ITERATION = 1000 }; RandomDisDec(AdjMatricesStore &aj); //main method for refinement stage void refinementStage(); // returns number of unmatched edges int getError() { return _errorNumber; } //sets maximum iteration number limit void setIterationNumber(int max); CancellationHandler* cancellation_handler; private: //returns number of unmatched edges int _goalFunction(); //returns true if there is advantage (minimum error) to do move bool _acceptanceMove(int x); //returns true if there is advantage to do swap operation bool _acceptanceSwap(int x, int y); //creates list of error vertices void _makeLe(); //keeping util for 2DOM AdjMatricesStore& _adjMstore; //assign vertex from 1 graph to 2 int* _x; //assign vertex from 2 graph to 1 int* _y; //error list CP_DECL; TL_CP_DECL(Array<int>, _errorList); //list of error vertrces TL_CP_DECL(Array<int>, _listErrVertices); //size of first graph int _n; //size of second graph int _m; //sum of all errors int _errorNumber; //new state`s sum of all errors int _newErrorNumber; //flag for keeping breaks bool _stop; //max iteration number. Algortihm breaks its work then reached it int _maxIteration; // for stucking override Array<int> _stateArray; //error number in previous stuck state int _errorNumberStuck; // number of iterations before algorithm consider current state as stuck state int _stuckCount; //save current state in case of stuck void _saveState(); //load state void _loadState(); private: RandomDisDec(const RandomDisDec &); //no implicit copy }; //Randomizator for approximate algorithm class DLLEXPORT RandomHandler{ public: enum { DEFSEED=54217137, BIG_PRIME=899999963 }; RandomHandler(int ijkl): strand(false) { ranmarin(ijkl % BIG_PRIME); } RandomHandler(long ijkl): strand (false) { ranmarin(ijkl % BIG_PRIME); } RandomHandler(): strand(false) { ranmarin(DEFSEED); }; void ranmarin(int ijkl) { int ij,kl; int i,ii,j,jj,k,l,m ; double s,t ; u.resize(97); uvec.resize(97); ij=ijkl/30082; kl=ijkl-30082*ij; i = ((ij/177) % 177) + 2 ; j = (ij % 177) + 2 ; k = ((kl/169) % 178) + 1 ; l = kl % 169 ; for (ii = 0; ii < 97; ++ii) { s = 0.0 ; t = 0.5 ; for (jj = 0; jj < 24; ++jj) { m = (((i*j) % 179) * k) % 179 ; i = j ; j = k ; k = m ; l = (53*l + 1) % 169 ; if ( ((l*m) % 64) >= 32) s += t ; t *= 0.5 ; } u[ii] = s ; } c = 362436.0 / 16777216.0 ; cd = 7654321.0 / 16777216.0 ; cm =16777213.0 / 16777216.0 ; i97 = 96 ; j97 = 32 ; } inline double next() { double uni; uni = u[i97]-u[j97]; if (uni<0.0) uni+=1.0; u[i97] = uni; if (--i97<0) i97=96; if (--j97<0) j97=96; c -= cd; if (c < 0.0) c += cm; uni -= c; if (uni < 0.0) uni += 1.0; return uni; } inline int next(int max) { if (strand) return (rand() % max); else return (int)(max*next()); } void next(Array<double>& d) { double uni; int n = d.size(); for(int i = 0; i < n; ++i) { uni=u[i97]-u[j97]; if (uni<0.0) uni+=1.0; u[i97]=uni; if (--i97<0) i97=96; if (--j97<0) j97=96; c -= cd; if (c < 0.0) c += cm; uni -= c; if (uni < 0.0) uni += 1.0; d[i] = uni; } } double c,cd,cm; Array<double> u; Array<double> uvec; int i97,j97; bool strand; }; protected: //keeping graphs Graph* _subgraph; Graph* _supergraph; bool _findTrivialMcs(); void _clearSolutionMaps(); void _addSolutionMap(Array<int>& v_map, Array<int>& e_map); //method returns true if edges with input number completely matched bool _getEdgeColorCondition(Graph &graph1, Graph &graph2, int i, int j) const; //returns all solutions void _getSolutionMaps(int count, ObjArray< Array<int> >& v_maps, ObjArray< Array<int> >& e_maps) const ; //array for keeping all solutions. In each subarray element[0] = vertex size, [1] = edge size, and //next '[0]' elements for vertex map, next '[1]' for edge map (in sum 2+vertexEnd()+edgeEnd() elements) ObjArray< Array<int> > _vertEdgeSolMap; RandomHandler _random; private: MaxCommonSubgraph(const MaxCommonSubgraph&); //no implicit copy }; //for searching substructure with map class SubstructureMcs { public: enum { UNMAPPED = -1 }; //constuctors SubstructureMcs(); SubstructureMcs(Graph& sub, Graph& super); virtual ~SubstructureMcs(){} //sets graphs for substructure search considering their size void setGraphs(Graph& sub, Graph& super); //searches substructure for graphs and maps vertices virtual bool searchSubstructure(Array<int>* map); //condition for edge match bool (*cbMatchEdge) (Graph &graph1, Graph &graph2, int i, int j, void *userdata); //condition for vertex match bool (*cbMatchVertex) (Graph &graph1, Graph &graph2, const int *core_sub, int i, int j, void *userdata); void *userdata; //returns true if graphs was swapped then initializing bool isInverted() {return _invert; }; //function for substructure search always return 0 static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); protected: //ptrs for input graphs Graph *_sub; Graph *_super; //variable for considering swap input graphs bool _invert; private: SubstructureMcs (const SubstructureMcs &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/morgan_code.h�������������������������������������������������������������0000664�0000000�0000000�00000001640�12710376503�0017731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __morgan_code_h__ #define __morgan_code_h__ #include "graph/graph.h" namespace indigo { class MorganCode { public: explicit MorganCode (const Graph &g); void calculate (Array<long> &codes, int coeff, int iteration_count); protected: const Graph &_g; }; } #endif ������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/path_enumerator.h���������������������������������������������������������0000664�0000000�0000000�00000002616�12710376503�0020655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __path_enumerator_h__ #define __path_enumerator_h__ namespace indigo { #include "base_cpp/array.h" #include "base_cpp/exception.h" class Graph; class PathEnumerator { public: explicit PathEnumerator (Graph &graph, int begin, int end); ~PathEnumerator (); int max_length; void *context; bool (*cb_check_vertex)(Graph &graph, int v_idx, void *context); bool(*cb_check_edge)(Graph &graph, int e_idx, void *context); bool(*cb_handle_path)(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); void process(); DECL_TIMEOUT_EXCEPTION; protected: bool _pathFinder (); Graph &_graph; int _begin; int _end; private: PathEnumerator (const PathEnumerator &); // no implicit copy }; } #endif ������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/scaffold_detection.h������������������������������������������������������0000664�0000000�0000000�00000011046�12710376503�0021274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __scaffold_detection_h_ #define __scaffold_detection_h_ #include "graph/max_common_subgraph.h" namespace indigo { class ScaffoldDetection { public: ScaffoldDetection (ObjArray<Graph>* graph_set); //two main methods for extracting scaffolds //extracting exact scaffold from graphs set void extractExactScaffold (Graph& scaffold) {_searchScaffold(scaffold, false); } //extracting approximate scaffold from graphs set void extractApproximateScaffold(Graph& scaffold){_searchScaffold(scaffold, true); } //array for keeping graphs for searching scaffold //PtrArray<Graph> graphSet; //callbacks for edge and vertices comparsions for input graphs bool (*cbEdgeWeight) (Graph &graph1, Graph &graph2, int i, int j, void *userdata); bool (*cbVerticesColor) (Graph &graph1, Graph &graph2, const int *core_sub, int i, int j, void *userdata); int (*cbSortSolutions) (Graph &graph1, Graph &graph2, const void *userdata); void *userdata; int (*cbEmbedding) (const int *sub_vert_map, const int *sub_edge_map, const void* info, void* userdata); void *embeddingUserdata; ObjArray<Graph>* searchStructures; ObjArray<Graph>* basketStructures; int maxIterations; DECL_ERROR; public: //class for keeping graphs class GraphBasket { public: //max number of graphs to keep enum { MAX_MOLECULES_NUMBER = 100, NEXT_SOLUTION_SIZE_SUM = 100 }; GraphBasket(); virtual ~GraphBasket(); //initializes graphs basket void initBasket(ObjArray<Graph>* graph_set, ObjArray<Graph>* basket_set, int max_number); //returns index of first graph in basket int graphBegin(); //returns next index of graph after input index int graphNext(int i); //checks if queue's graphs already exsist in basket. add this graphs in case false result void checkAddedGraphs(); //remove graph form basket virtual void removeGraph(int index); //returns ptr of graph in basket with index virtual Graph& getGraph(int index) const; //adds new graph to queue and returns ptr of that virtual Graph& pickOutNextGraph(); //returns index of graph in basket that has maximum number of edges virtual int getMaxGraphIndex(); //this method adds graph from set (defines with edges and vertices lists) to basket queue virtual void addToNextEmptySpot(Graph& graph, Array<int> &v_list, Array<int> &e_list); virtual Graph& getGraphFromSet(int idx) {return _searchStructures->at(_orderArray[idx]); } virtual int getGraphSetSize() const {return _graphSetSize; } int (*cbSortSolutions) (Graph &graph1, Graph &graph2, void *userdata); bool (*cbMatchEdges) (Graph &graph1, Graph &graph2, int i, int j, void *userdata); bool (*cbMatchVertices) (Graph &graph1, Graph &graph2, const int *core_sub, int i, int j, void *userdata); void *userdata; DECL_ERROR; protected: //iterator for looking next empty graphs in set Dbitset _directIterator; //reverse iterator for looking next not empty graphs in set Dbitset _reverseIterator; ObjArray<Graph>* _searchStructures; ObjArray<Graph>* _basketStructures; virtual void _sortGraphsInSet(); //array for keeping indexes of sorted graphs in set Array<int> _orderArray; int _graphSetSize; //function for sortMoleculesInSet() method static int _compareEdgeCount(int i1, int i2, void* context); static int _copmpareRingsCount(Graph& g1, Graph& g2, void* context); private: GraphBasket(const GraphBasket&); //no implicit copy }; protected: //method for extracting exact scaffold from graph set void _searchExactScaffold(GraphBasket& basket); //method for extracting approximate scaffold from graph set void _searchApproximateScaffold(GraphBasket& basket); private: void _searchScaffold(Graph& scaffold, bool approximate); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/shortest_path_finder.h����������������������������������������������������0000775�0000000�0000000�00000002356�12710376503�0021702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __shortest_path_finder_h__ #define __shortest_path_finder_h__ #include "base_cpp/array.h" #include "base_cpp/queue.h" namespace indigo { class Graph; class ShortestPathFinder { public: explicit ShortestPathFinder (const Graph &graph); void *check_vertex_context; bool (*cb_check_vertex)(const Graph &graph, int v_idx, void *context); void *check_edge_context; bool (*cb_check_edge)(const Graph &graph, int e_idx, void *context); bool find (Array<int>& vertices, Array<int>& edges, int u, int v); private: Queue<int> queue; Array<int> prev; const Graph &_graph; }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/simple_cycle_basis.h������������������������������������������������������0000664�0000000�0000000�00000004722�12710376503�0021311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _SIMPLE_CYCLE_BASIS_H #define _SIMPLE_CYCLE_BASIS_H #include "base_cpp/obj_array.h" #include "base_cpp/red_black.h" #include "base_cpp/array.h" #include "graph/graph.h" namespace indigo { class SimpleCycleBasis { public: SimpleCycleBasis(const Graph& graph); void create(); int getCyclesCount() const { return _cycles.size(); } const Array<int>& getCycle(int num) const {return _cycles[num]; } ObjArray< Array<int> > _cycles; private: static void constructKernelVector(Array<bool>& u, ObjArray< Array<bool> >& a, int i); SimpleCycleBasis(SimpleCycleBasis&);// no implicit copy bool _getParentVertex(const Graph& graph, int vertex, int& parent_vertex); void _minimize(int startIndex); void _getCycleEdgeIncidenceMatrix(ObjArray< Array<bool> >& a); void _createEdgeIndexMap(); int _getEdgeIndex(int edge) const; void _prepareSubgraph (Graph &subgraph); RedBlackMap<int, int> vertices_spanning_tree; RedBlackMap<int, int> spanning_tree_vertices; RedBlackMap<int, int> _edgeIndexMap; const Graph& _graph; Array<int> _edgeList; bool _isMinimized; }; class AuxiliaryGraph: public Graph { // graph to aux. graph RedBlackMap<int, int> _vertexMap0; RedBlackMap<int, int> _vertexMap1; RedBlackMap<int, int> _auxVertexMap; // aux. edge to edge RedBlackMap<int, int> _auxEdgeMap; const Graph& _graph; Array<bool>& _u; RedBlackMap<int, int>& _edgeIndexMap; public: AuxiliaryGraph(const Graph& graph, Array<bool>& u, RedBlackMap<int, int>& edgeIndexMap) : _graph(graph), _u(u), _edgeIndexMap(edgeIndexMap) {} int auxVertex0(int vertex); int auxVertex1(int vertex); const Vertex& getVertexAndBuild(int vertex) ; int edge(int auxEdge); }; } #endif /* _SIMPLE_CYCLE_BASIS_H */ ����������������������������������������������Indigo-indigo-1.2.3/graph/skew_symmetric_flow_finder.h����������������������������������������������0000664�0000000�0000000�00000003406�12710376503�0023101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __skew_symmetric_flow_finder_h__ #define __skew_symmetric_flow_finder_h__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" namespace indigo { class SkewSymmetricNetwork; /* Find maximum integer skew-symmetric flow in * skew-symmetric network. */ class SkewSymmetricFlowFinder { public: SkewSymmetricFlowFinder (const SkewSymmetricNetwork &network); void process (); int getArcValue (int arc) const; DECL_ERROR; private: void _init (); bool _findAugmentatingPath (Array<int> &vertices); bool _findAugmentatingPathRec (Array<int> &vertices); void _increaseFlowByPath (Array<int> &vertices); int _getResidualCapacity (int edge, int from); bool _isEdgeAugmentating (int edge, int from, int sym_used_dir); void _dbgCheckConsistency (); CP_DECL; TL_CP_DECL(Array<int>, _arc_values); TL_CP_DECL(Array<int>, _arc_sym); // Variables for path finding TL_CP_DECL(Array<int>, _edge_used_dir); TL_CP_DECL(Array<int>, _vertex_is_used); int _network_sink; const SkewSymmetricNetwork &_network; }; } #endif // __skew_symmetric_flow_finder_h__ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/skew_symmetric_network.h��������������������������������������������������0000664�0000000�0000000�00000005605�12710376503�0022277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __skew_symmetric_network_h__ #define __skew_symmetric_network_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "graph/graph.h" namespace indigo { enum { ARC_IN, // Incoming arc ARC_OUT // Outgoing arc }; /* This data structure defines skew-symmetric integer network * used to find maximum skew-symmetric flow. * Skew-symmetric network is quadruple (g, symmetry, capacity, source), * where: * g - skew-symmetric graph with 'symmetry' symmetry * capacity - bonds capacities * source - network source * * SkewSymmetricNetwork is based on Graph class, that * represents undirected graphs. That's why for simplicity * bonds in both directions are not supported, but this can * be implemented in the future if necessary. * * Term symmetry is used instead of skew-symmetry for brevity. * * For more information about skew-symmetric networks and flows * look at these articles: * - Goldberg A.V., Karzanov A.V.: Maximum skew-symmetric * flows and matchings. * - Goldberg A.V., Karzanov A.V.: Path problems in skew-symmetric * graphs. */ class SkewSymmetricNetwork { public: // Return graph for network structure querying (vertex neighbours) const Graph& g() const; void clear (); // Set/get network source. // Symmetric vertex for source is the sink of network void setSource (int source); int getSource () const; // Add vertex and symmetric vertex function. // Return created vertex index and optionally symmetric vertex int addVertex (int *symmetry_vertex = NULL); // Remove vertex and symmetric vertex. void removeVertex (int vertex); // Add arc and symmetric arc with specified capacity int addArc (int from, int to, int capacity); // Remove arc and symmetric arc void removeArc (int from, int to); int getSymmetricVertex (int vertex) const; int getSymmetricArc (int edge) const; int getArcType (int edge, int vertex) const; int getArcCapacity (int edge) const; void setArcCapacity (int edge, int capacity); DECL_ERROR; private: Graph _g; Array<int> _symmetry; int _source; struct Arc { int from, to; int capacity; }; Array<Arc> _arcs; }; } #endif // __skew_symmetric_network_h__ ���������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/spanning_tree.h�����������������������������������������������������������0000664�0000000�0000000�00000004453�12710376503�0020315�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __spanning_tree_h__ #define __spanning_tree_h__ #include "base_cpp/array.h" #include "graph/graph.h" #include "base_cpp/tlscont.h" namespace indigo { class Filter; class SpanningTree { public: struct ExtEdge { int beg_idx; int end_idx; int ext_beg_idx; int ext_end_idx; int ext_edge_idx; }; explicit SpanningTree (Graph &graph, const Filter *vertex_filter, const Filter *edge_filter = 0); inline int getEdgesNum () { return _edges_list.size(); } inline const ExtEdge & getExtEdge (int i) { return _edges_list[i]; } void addEdge (int beg, int end, int ext_index); inline const Vertex &getVertexFromExtIdx (int ext_idx) const { return _tree.getVertex(_inv_mapping[ext_idx]); } inline int getExtVertexIndex (int v_idx) const { return _mapping[v_idx]; } inline int getExtEdgeIndex (int e_idx) const { return _edge_mapping[e_idx]; } void markAllEdgesInCycles (int *marks_out, int value); DECL_ERROR; protected: struct StackElem { const Vertex *vertex; int vertex_idx; int nei_idx; int parent_idx; }; void _build (); const Graph &_graph; const Filter *_vertex_filter; const Filter *_edge_filter; // these members made static for saving time of memory allocations CP_DECL; TL_CP_DECL(Array<ExtEdge>, _edges_list); TL_CP_DECL(Array<int>, _depth_counters); TL_CP_DECL(Graph, _tree); TL_CP_DECL(Array<int>, _mapping); TL_CP_DECL(Array<int>, _inv_mapping); TL_CP_DECL(Array<int>, _edge_mapping); TL_CP_DECL(Array<StackElem>, _stack); int _current_depth; }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016071�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/automorphism_search.cpp�����������������������������������������������0000664�0000000�0000000�00000064327�12710376503�0022665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/automorphism_search.h" using namespace indigo; IMPL_ERROR(AutomorphismSearch, "automorphism search"); CP_DEF(AutomorphismSearch); AutomorphismSearch::AutomorphismSearch () : CP_INIT, TL_CP_GET(_call_stack), TL_CP_GET(_lab), TL_CP_GET(_ptn), TL_CP_GET(_graph), TL_CP_GET(_mapping), TL_CP_GET(_inv_mapping), TL_CP_GET(_degree), TL_CP_GET(_tcells), TL_CP_GET(_fix), TL_CP_GET(_mcr), TL_CP_GET(_active), TL_CP_GET(_workperm), TL_CP_GET(_workperm2), TL_CP_GET(_bucket), TL_CP_GET(_count), TL_CP_GET(_firstlab), TL_CP_GET(_canonlab), TL_CP_GET(_orbits), TL_CP_GET(_fixedpts), TL_CP_GET(_work_active_cells), TL_CP_GET(_edge_ranks_in_refine) { getcanon = true; compare_vertex_degree_first = true; refine_reverse_degree = false; refine_by_sorted_neighbourhood = false; worksize = 100; context = 0; cb_vertex_rank = 0; cb_vertex_cmp = 0; cb_check_automorphism = 0; cb_compare_mapped = 0; cb_automorphism = 0; cb_edge_rank = 0; context_automorphism = 0; _given_graph = 0; ignored_vertices = 0; _call_stack.clear(); } AutomorphismSearch::~AutomorphismSearch () { } void AutomorphismSearch::_prepareGraph (Graph &graph) { QS_DEF(Array<int>, buckets); QS_DEF(Array<int>, ranks); int i; ranks.clear(); buckets.clear(); _graph.clear(); _mapping.clear(); _degree.clear(); _ptn.clear(); _inv_mapping.clear_resize(graph.vertexEnd()); _degree.clear_resize(graph.vertexEnd()); _degree.zerofill(); if (cb_vertex_rank != 0 && cb_vertex_cmp != 0) throw Error("both vertex rank and vertex compare callbacks specified"); _given_graph = &graph; if (cb_vertex_cmp != 0) { for (i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i)) if (ignored_vertices == 0 || !ignored_vertices[i]) _mapping.push(i); for (i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i)) { const Edge &edge = graph.getEdge(i); if (ignored_vertices == 0 || (!ignored_vertices[edge.beg] && !ignored_vertices[edge.end])) { _degree[edge.beg]++; _degree[edge.end]++; } } _mapping.qsort(_cmp_vertices, this); int rank = 0; for (i = 0; i < _mapping.size(); i++) { if (i > 0 && _cmp_vertices(_mapping[i], _mapping[i - 1], this) != 0) rank++; ranks.push(rank); } } else { for (i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i)) { if (ignored_vertices != 0 && ignored_vertices[i]) continue; int rank = 0; if (cb_vertex_rank != 0) rank = cb_vertex_rank(graph, i, context); ranks.push(rank); _mapping.push(i); } } for (i = 0; i < _mapping.size(); i++) { _graph.addVertex(); _inv_mapping[_mapping[i]] = i; _ptn.push(INFINITY); while (buckets.size() <= ranks[i]) buckets.push(0); buckets[ranks[i]]++; } for (i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i)) { const Edge &edge = graph.getEdge(i); if (ignored_vertices != 0 && (ignored_vertices[edge.beg] || ignored_vertices[edge.end])) continue; int beg = _inv_mapping[edge.beg]; int end = _inv_mapping[edge.end]; _graph.addEdge(beg, end); } int start = 0; for (i = 0; i < buckets.size(); i++) { if (buckets[i] == 0) continue; int end = start + buckets[i]; buckets[i] = start; _ptn[end - 1] = 0; start = end; } _n = _graph.vertexCount(); _lab.clear_resize(_n); for (i = 0; i < _n; i++) _lab[buckets[ranks[i]]++] = i; } int AutomorphismSearch::_cmp_vertices (int idx1, int idx2, void *context) { const AutomorphismSearch *self = (const AutomorphismSearch *)context; int degree_diff = self->_degree[idx1] - self->_degree[idx2]; if (self->compare_vertex_degree_first) if (degree_diff != 0) return degree_diff; if (self->cb_vertex_cmp == 0) return degree_diff; int ret_cb_vertex_cmp = self->cb_vertex_cmp(*self->_given_graph, idx1, idx2, self->context); if (ret_cb_vertex_cmp != 0) return ret_cb_vertex_cmp; if (!self->compare_vertex_degree_first) return degree_diff; return 0; } void AutomorphismSearch::getCanonicalNumbering (Array<int> &numbering) { int i; numbering.clear(); for (i = 0; i < _mapping.size(); i++) numbering.push(_mapping[_canonlab[i]]); } void AutomorphismSearch::getOrbits (Array<int> &orbits) const { orbits.clear_resize(_given_graph->vertexEnd()); orbits.fffill(); for (int i = 0; i < _mapping.size(); i++) orbits[_mapping[i]] = _orbits[i]; } void AutomorphismSearch::getCanonicallyOrderedOrbits (Array<int> &orbits) const { // Each vertex in the orbit has its canonical number. // Canonical orbit index is the minimal canonical index of the vertices // from this orbit QS_DEF(Array<int>, min_vertex_in_orbit); min_vertex_in_orbit.clear_resize(_given_graph->vertexEnd()); min_vertex_in_orbit.fffill(); for (int i = 0; i < _mapping.size(); i++) { int vertex = _canonlab[i]; int orbit = _orbits[vertex]; if (min_vertex_in_orbit[orbit] == -1 || min_vertex_in_orbit[orbit] > i) min_vertex_in_orbit[orbit] = i; } orbits.clear_resize(_given_graph->vertexEnd()); orbits.fffill(); for (int i = 0; i < _mapping.size(); i++) orbits[_mapping[i]] = min_vertex_in_orbit[_orbits[i]]; } void AutomorphismSearch::process (Graph &graph) { _prepareGraph(graph); _active.clear_resize(_n); _workperm.clear_resize(_n); _workperm2.clear_resize(_n); _firstlab.clear_resize(_n); _canonlab.clear_resize(_n); _fixedpts.clear_resize(_n); _count.clear_resize(_n); _orbits.clear_resize(_n); _fix.clear(); _mcr.clear(); if (_n == 0) return; _fixedpts.zerofill(); _needshortprune = false; _orbits_num = _n; { int i, numcells = 0; _ptn[_n - 1] = 0; for (i = 0; i < _n; i++) if (_ptn[i] != 0) _ptn[i] = INFINITY; else numcells++; _active.zerofill(); for (i = 0; i < _n; i++) { _active[i] = 1; while (_ptn[i]) i++; } for (i = 0; i < _n; ++i) _orbits[i] = i; _Call &call = _call_stack.push(); call.level = 1; call.numcells = numcells; call.place = _INITIAL; } int retval = -1; while (_call_stack.size() > 0) { _Call call = _call_stack.top(); if (call.place == _INITIAL) { retval = _firstNode(call.level, call.numcells); if (retval >= 0) _call_stack.pop(); } else if (call.place == _FIRST_LOOP) { int tv = -1; if (retval != -1) { // handle the value returned from _FIRST_TO_FIRST or _FIRST_TO_OTHER tv = _tcells[call.level][call.k]; if (tv == call.tv1) _gca_first = call.level; _fixedpts[tv] = 0; if (retval < call.level) { _call_stack.pop(); continue; // break the _FIRST_LOOP and keep the retval; } if (_needshortprune) { _needshortprune = false; call.k = _shortPrune(_tcells[call.level], _mcr.top(), call.k); } _recover(call.level); // advance the _FIRST_LOOP counter call.k++; } for (; call.k < _tcells[call.level].size(); call.k++) { tv = _tcells[call.level][call.k]; if (_orbits[tv] == tv) // not equivalent to the previous child? break; } if (call.k == _tcells[call.level].size()) { // return from _FIRST_LOOP retval = call.level - 1; _call_stack.pop(); continue; } _call_stack.top() = call; _breakout(call.level + 1, call.tc, tv); _cosetindex = tv; _fixedpts[tv] = 1; _Call &newcall = _call_stack.push(); newcall.level = call.level + 1; newcall.numcells = call.numcells + 1; if (tv == call.tv1) newcall.place = _FIRST_TO_FIRST; else newcall.place = _FIRST_TO_OTHER; // discard the old return value retval = -1; } else if (call.place == _FIRST_TO_FIRST) { retval = _firstNode(call.level, call.numcells); if (retval >= 0) // _FIRST_LOOP did not happen; pass the return value to the caller _call_stack.pop(); } else if (call.place == _FIRST_TO_OTHER || call.place == _OTHER_TO_OTHER) { retval = _otherNode(call.level, call.numcells); if (retval >= 0) // _OTHER_LOOP did not happen; pass the return value to the caller _call_stack.pop(); } else if (call.place == _OTHER_LOOP) { int tv; if (retval != -1) { // handle the value returned from _OTHER_TO_OTHER tv = _tcells[call.level][call.k]; _fixedpts[tv] = 0; if (retval < call.level) { _call_stack.pop(); continue; // break the _OTHER_LOOP and keep the retval; } // use stored automorphism data to prune target cell if (_needshortprune) { _needshortprune = false; call.k = _shortPrune(_tcells[call.level], _mcr.top(), call.k); } if (tv == call.tv1) call.k = _longPrune(_tcells[call.level], _fixedpts, call.k); _recover(call.level); // advance the _OTHER_LOOP counter call.k++; } if (call.k == _tcells[call.level].size()) { // return from _OTHER_LOOP retval = call.level - 1; _call_stack.pop(); continue; } _call_stack.top() = call; tv = _tcells[call.level][call.k]; _breakout(call.level + 1, call.tc, tv); _fixedpts[tv] = 1; _Call &newcall = _call_stack.push(); newcall.level = call.level + 1; newcall.numcells = call.numcells + 1; newcall.place = _OTHER_TO_OTHER; // discard the old return value retval = -1; } else throw Error("internal: bad command %d", call.place); } } int AutomorphismSearch::_firstNode (int level, int numcells) { _refine(level, numcells); _tcells.resize(level + 1); if (numcells == _n) // found first leaf? { _gca_first = level; _firstlab.copy(_lab); if (getcanon) { _canonlevel = _gca_canon = level; _canonlab.copy(_lab); } return level - 1; } // locate new target cell int tc = _targetcell(level, _tcells[level]); int tv1 = _tcells[level][0]; _call_stack.pop(); // use the elements of the target cell to produce the children _Call &call = _call_stack.push(); call.level = level; call.k = 0; call.tc = tc; call.tv1 = tv1; call.numcells = numcells; call.place = _FIRST_LOOP; return -1; } int AutomorphismSearch::_otherNode (int level, int numcells) { _refine(level, numcells); _tcells.resize(level + 1); int rtnlevel = _processNode(level, numcells); if (rtnlevel < level) // keep returning if necessary return rtnlevel; int tc = _targetcell(level, _tcells[level]); if (_needshortprune) { _needshortprune = false; _shortPrune(_tcells[level], _mcr.top(), 0); } int tv1 = _tcells[level][0]; _call_stack.pop(); // use the elements of the target cell to produce the children _Call &call = _call_stack.push(); call.level = level; call.k = 0; call.tc = tc; call.tv1 = tv1; call.numcells = numcells; call.place = _OTHER_LOOP; return -1; } void AutomorphismSearch::_recover (int level) { int i; for (i = 0; i < _n; ++i) if (_ptn[i] > level) _ptn[i] = INFINITY; if (getcanon) { if (level < _gca_canon) _gca_canon = level; if (level < _gca_first) throw Error("internal error?"); } } void AutomorphismSearch::_breakout (int level, int tc, int tv) { _active.zerofill(); _active[tc] = 1; int i = tc; int prev = tv; do { int next = _lab[i]; _lab[i++] = prev; prev = next; } while (prev != tv); _ptn[tc] = level; } int AutomorphismSearch::_shortPrune (Array<int> &tcell, Array<int> &mcr, int idx) { int i, j; int ret = idx; for (i = j = 0; i < tcell.size(); i++) if (mcr[tcell[i]]) tcell[j++] = tcell[i]; else if (idx >= i) ret--; tcell.resize(j); return ret; } int AutomorphismSearch::_longPrune (Array<int> &tcell, Array<int> &fixed, int idx) { int i, j, k; int ret = idx; for (k = 0; k < _fix.size(); k++) { for (j = 0; j < _n; j++) if (_fix[k][j] == 0 && fixed[j] == 1) break; if (j != _n) continue; for (i = j = 0; i < tcell.size(); i++) if (_mcr[k][tcell[i]]) tcell[j++] = tcell[i]; else if (idx >= i) ret--; tcell.resize(j); idx = ret; } return ret; } int AutomorphismSearch::_processNode (int level, int numcells) { int i; // no idea what this nauty's if() means. //if (_eqlev_first != level && (!getcanon || _comp_canon < 0)) // code = 4; if (numcells != _n) // discrete partition? return level; for (i = 0; i < _n; i++) _workperm[_firstlab[i]] = _lab[i]; if (_isAutomorphism(_workperm)) { // _lab is equivalent to firstlab if (_fix.size() == worksize) { _fix.pop(); _mcr.pop(); } _buildFixMcr(_workperm, _fix.push(), _mcr.push()); _joinOrbits(_workperm); _handleAutomorphism(_workperm); return _gca_first; } if (getcanon) { /*if (_comp_canon == 0) { // again, strange nauty's if() if (level < _canonlevel) _comp_canon = 1; else _comp_canon = _compareCanon(); }*/ int comp_canon = _compareCanon(); if (comp_canon == 0) { // _lab is equivalent to canonlab for (i = 0; i < _n; i++) _workperm[_canonlab[i]] = _lab[i]; if (_fix.size() == worksize) { _fix.pop(); _mcr.pop(); } _buildFixMcr(_workperm, _fix.push(), _mcr.push()); int norb = _orbits_num; _joinOrbits(_workperm); if (norb != _orbits_num) { _handleAutomorphism(_workperm); if (_orbits[_cosetindex] < _cosetindex) return _gca_first; } if (_gca_canon != _gca_first) _needshortprune = true; return _gca_canon; } else if (comp_canon > 0) { // _lab is better than canonlab _canonlab.copy(_lab); _canonlevel = _gca_canon = level; } } return level - 1; } void AutomorphismSearch::_joinOrbits (const Array<int> &perm) { int i, j1, j2; for (i = 0; i < _n; i++) { j1 = _orbits[i]; while (_orbits[j1] != j1) j1 = _orbits[j1]; j2 = _orbits[perm[i]]; while (_orbits[j2] != j2) j2 = _orbits[j2]; if (j1 < j2) _orbits[j2] = j1; else if (j1 > j2) _orbits[j1] = j2; } _orbits_num = 0; for (i = 0; i < _n; i++) { _orbits[i] = _orbits[_orbits[i]]; if (_orbits[i] == i) _orbits_num++; } } bool AutomorphismSearch::_isAutomorphism (Array<int> &perm) { for (int i = _graph.edgeBegin(); i != _graph.edgeEnd(); i = _graph.edgeNext(i)) { const Edge &edge = _graph.getEdge(i); if (!_graph.haveEdge(perm[edge.beg], perm[edge.end])) return false; } if (cb_check_automorphism != 0) { QS_DEF(Array<int>, perm_mapping); perm_mapping.clear_resize(_given_graph->vertexEnd()); perm_mapping.fffill(); for (int i = 0; i < _n; i++) perm_mapping[_mapping[i]] = _mapping[perm[i]]; return cb_check_automorphism(*_given_graph, perm_mapping, context); } return true; } // lab vs. canonlab int AutomorphismSearch::_compareCanon () { int i; QS_DEF(Array<int>, map); QS_DEF(Array<int>, canon_map); map.clear_resize(_n); canon_map.clear_resize(_n); for (i = 0; i < _n; i++) { map[i] = _mapping[_lab[i]]; canon_map[i] = _mapping[_canonlab[i]]; } if (cb_compare_mapped == 0) throw Error("cb_compare_mapped = 0"); return cb_compare_mapped(*_given_graph, map, canon_map, context); } void AutomorphismSearch::_buildFixMcr (const Array<int> &perm, Array<int> &fix, Array<int> &mcr) { int i; fix.clear_resize(_n); mcr.clear_resize(_n); fix.zerofill(); mcr.zerofill(); _workperm2.zerofill(); for (i = 0; i < _n; ++i) { if (perm[i] == i) { fix[i] = 1; mcr[i] = 1; } else if (_workperm2[i] == 0) { int l = i; do { _workperm2[l] = 1; l = perm[l]; } while (l != i); mcr[i] = 1; } } } int AutomorphismSearch::_targetcell (int level, Array<int> &cell) { int i = 0, j, k; int ibest = -1, jbest = -1, bestdegree = -1; while (i < _n) { for (; i < _n && _ptn[i] <= level; ++i) ; if (i == _n) break; else for (j = i + 1; _ptn[j] > level; j++) ; int degree = _degree[_mapping[_lab[i]]]; // Choose cell with single vertices first and then biggest cell if (ibest == -1 || (degree == 0 && bestdegree != 0) || (bestdegree != 0 && j - i > jbest - ibest)) { jbest = j; ibest = i; bestdegree = degree; } i = j + 1; } if (ibest == -1) throw Error("(intenal error) target cell cannot be found"); i = ibest; j = jbest; cell.clear(); int imin = 0; for (k = i; k <= j; k++) { cell.push(_lab[k]); if (cell.size() > 0 && cell[cell.size() - 1] < cell[imin]) imin = cell.size() - 1; } if (imin > 0) cell.swap(0, imin); return i; } void AutomorphismSearch::_refine (int level, int &numcells) { if (refine_by_sorted_neighbourhood) _refineBySortingNeighbourhood(level, numcells); else _refineOriginal(level, numcells); } void AutomorphismSearch::_refineOriginal (int level, int &numcells) { int hint = 0; int split1 = -1; while (numcells < _n) { int split2; if (_active[hint]) split1 = hint; else { int i; for (i = 0; i < _n; i++) { split1 = (split1 + 1) % _n; if (_active[split1]) break; } if (i == _n) break; } _active[split1] = 0; for (split2 = split1; _ptn[split2] > level; split2++) ; _edge_ranks_in_refine.clear(); _refineByCell(split1, split2, level, numcells, hint, -1); // Check if there are exists edge with different ranks // Last element in _edge_ranks_in_refine array contains positive value that // means that such edge rank exists. But it is nessesary to refine by all ranks // except one because cells have already been refined by edges without ranks. for (int i = 0; i < _edge_ranks_in_refine.size() - 1; i++) if (_edge_ranks_in_refine[i] != 0) _refineByCell(split1, split2, level, numcells, hint, i); } } void AutomorphismSearch::_refineBySortingNeighbourhood (int level, int &numcells) { // This refine procedure works like refining by sorting neighbourhood ranks while (true) { // Collect active cells _work_active_cells.clear(); for (int i = 0; i < _n; i++) { int split1; if (_active[i]) { split1 = i; int split2; for (split2 = split1; _ptn[split2] > level; split2++) ; int (&split_cell)[2] = _work_active_cells.push(); split_cell[0] = split1; split_cell[1] = split2; _active[i] = 0; } } if (_work_active_cells.size() == 0) break; // Refine all cells by collected active cells for (int i = 0; i < _work_active_cells.size(); i++) { int (&split_cell)[2] = _work_active_cells[i]; int split1 = split_cell[0], split2 = split_cell[1]; int dummy_hint; _refineByCell(split1, split2, level, numcells, dummy_hint, -1); if (numcells == _n) return; } } } bool AutomorphismSearch::_hasEdgeWithRank (int from, int to, int target_edge_rank) { int edge_index = _graph.findEdgeIndex(from, to); if (edge_index == -1) return false; if (cb_edge_rank == 0) return true; int mapped_v1 = _mapping[from]; int mapped_v2 = _mapping[to]; int edge_index_mapped = _given_graph->findEdgeIndex(mapped_v1, mapped_v2); if (edge_index_mapped == -1) throw Error("Internal error: edge must exists"); int edge_rank = cb_edge_rank(*_given_graph, edge_index_mapped, context); if (target_edge_rank == -1) { // Just update information about ranks while (_edge_ranks_in_refine.size() <= edge_rank) _edge_ranks_in_refine.push(0); _edge_ranks_in_refine[edge_rank]++; return true; } return target_edge_rank == edge_rank; } void AutomorphismSearch::_refineByCell (int split1, int split2, int level, int &numcells, int &hint, int target_edge_rank) { int i, j, tmp; if (split1 == split2) // trivial splitting cell { int cell1, cell2; for (cell1 = 0; cell1 < _n; cell1 = cell2 + 1) { for (cell2 = cell1; _ptn[cell2] > level; cell2++) ; if (cell1 == cell2) continue; int c1 = cell1, c2 = cell2; while (c1 <= c2) { if (_hasEdgeWithRank(_lab[split1], _lab[c1], target_edge_rank)) c1++; else { __swap(_lab[c1], _lab[c2], tmp); c2--; } } if (c2 >= cell1 && c1 <= cell2) { _ptn[c2] = level; numcells++; if (_active[cell1] || (c2 - cell1 >= cell2 - c1 && !refine_by_sorted_neighbourhood)) { _active[c1] = 1; if (c1 == cell2) hint = c1; } else { _active[cell1] = 1; if (c2 == cell1) hint = cell1; } } } } else // nontrivial splitting cell { int cell1, cell2; for (cell1 = 0; cell1 < _n; cell1 = cell2 + 1) { for (cell2 = cell1; _ptn[cell2] > level; ++cell2) ; if (cell1 == cell2) continue; int bmin = _n; _bucket.clear(); for (i = cell1; i <= cell2; i++) { int cnt = 0; for (j = split1; j <= split2; j++) if (_hasEdgeWithRank(_lab[i], _lab[j], target_edge_rank)) cnt++; while (_bucket.size() <= cnt) _bucket.push(0); _bucket[cnt]++; if (cnt < bmin) bmin = cnt; _count[i] = cnt; } if (bmin == _bucket.size() - 1) continue; if (refine_reverse_degree) { // Reverse degree locally to avoid code changing below for (i = cell1; i <= cell2; i++) { _count[i] = _bucket.size() - _count[i] - 1; } for (i = _bucket.size() - 1; i >= bmin; i--) { int dest = _bucket.size() - i - 1; if (dest < i) __swap(_bucket[i], _bucket[dest], tmp); } _bucket.resize(_bucket.size() - bmin); bmin = 0; } int c1 = cell1, c2; int maxcell = -1, maxpos = -1; int last_c1 = -1; for (i = bmin; i < _bucket.size(); i++) { if (_bucket[i] == 0) continue; c2 = c1 + _bucket[i]; _bucket[i] = c1; last_c1 = c1; if (c2 - c1 > maxcell) { maxcell = c2 - c1; maxpos = c1; } if (c1 != cell1) { _active[c1] = 1; if (c2 - c1 == 1) hint = c1; numcells++; } if (c2 <= cell2) _ptn[c2 - 1] = level; c1 = c2; } for (i = cell1; i <= cell2; i++) _workperm2[_bucket[_count[i]]++] = _lab[i]; for (i = cell1; i <= cell2; i++) _lab[i] = _workperm2[i]; if (_active[cell1] == 0) { _active[cell1] = 1; // When sorting by neighbourhood is is allowed only to exclude // the last created subcell. For ordinary refine greatest cell is excluded. if (!refine_by_sorted_neighbourhood) _active[maxpos] = 0; else _active[last_c1] = 0; } } } } void AutomorphismSearch::_handleAutomorphism (const Array<int> &perm) { if (cb_automorphism != 0) { QS_DEF(Array<int>, perm2); int i; perm2.clear_resize(_given_graph->vertexEnd()); perm2.fffill(); for (i = 0; i < _n; i++) perm2[_mapping[i]] = _mapping[perm[i]]; cb_automorphism(perm2.ptr(), context_automorphism); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/aux_path_finder.cpp���������������������������������������������������0000664�0000000�0000000�00000003542�12710376503�0021741�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/aux_path_finder.h" #include "graph/simple_cycle_basis.h" using namespace indigo; AuxPathFinder::AuxPathFinder (AuxiliaryGraph &graph, int max_size) : _graph(graph) { _queue.setLength(max_size); _prev.clear_resize(max_size); } bool AuxPathFinder::find (Array<int>& vertices, Array<int>& edges, int u, int v) { // init _queue.clear(); _prev.fffill(); vertices.clear(); edges.clear(); // push initial vertex _queue.push(v); _prev[v] = u; while (!_queue.isEmpty()) { // pop vertex int w = _queue.pop(); const Vertex& vert = _graph.getVertexAndBuild(w); for (int i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { int n = vert.neiVertex(i); if (_prev[n] >= 0) continue; // vertex is already done if (n == u) { // shortest path found. mark and return _prev[u] = w; for (int j = u; j != v; j = _prev[j]) { vertices.push(j); edges.push(_graph.findEdgeIndex(j, _prev[j])); } vertices.push(v); return true; } _queue.push(n); _prev[n] = w; } } return false; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/biconnected_decomposer.cpp��������������������������������������������0000664�0000000�0000000�00000012307�12710376503�0023275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" #include "graph/filter.h" #include "graph/biconnected_decomposer.h" using namespace indigo; IMPL_ERROR(BiconnectedDecomposer, "biconnected_decomposer"); CP_DEF(BiconnectedDecomposer); BiconnectedDecomposer::BiconnectedDecomposer (const Graph &graph) : _graph(graph), CP_INIT, TL_CP_GET(_components), TL_CP_GET(_dfs_order), TL_CP_GET(_lowest_order), TL_CP_GET(_component_lists), TL_CP_GET(_component_ids), TL_CP_GET(_edges_stack), _cur_order(0) { _components.clear(); _component_lists.clear(); _dfs_order.clear_resize(graph.vertexEnd()); _dfs_order.zerofill(); _lowest_order.clear_resize(graph.vertexEnd()); _component_ids.clear_resize(graph.vertexEnd()); _component_ids.zerofill(); } BiconnectedDecomposer::~BiconnectedDecomposer () { } int BiconnectedDecomposer::decompose () {// recursion? no, not heard... QS_DEF(Array<int>, dfs_stack); int i, v; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) if (_dfs_order[i] == 0) { dfs_stack.clear(); dfs_stack.push(i); _cur_order++; _dfs_order[i] = _lowest_order[i] = _cur_order; // Start DFS while (dfs_stack.size() > 0) { v = dfs_stack.top(); bool pushed = _pushToStack(dfs_stack, v); if (!pushed) { dfs_stack.pop(); if (dfs_stack.size() == 0) continue; _processIfNotPushed(dfs_stack, v); } } } return componentsCount(); } int BiconnectedDecomposer::componentsCount () { return _components.size(); } void BiconnectedDecomposer::getComponent (int idx, Filter &filter) const { filter.init(_components[idx]->ptr(), Filter::EQ, 1); } bool BiconnectedDecomposer::isArticulationPoint (int idx) const { return _component_ids[idx] != 0; } const Array<int> & BiconnectedDecomposer::getIncomingComponents (int idx) const { if (!isArticulationPoint(idx)) throw Error("vertex %d is not articulation point"); return *_component_ids[idx]; } void BiconnectedDecomposer::getVertexComponents (int idx, Array<int> &components) const { if (!isArticulationPoint(idx)) { int i; components.clear(); for (i = 0; i < _components.size(); i++) if (_components[i]->at(idx) == 1) { components.push(i); break; } return; } components.copy(getIncomingComponents(idx)); } int BiconnectedDecomposer::getIncomingCount(int idx) const { if (!isArticulationPoint(idx)) return 0; return _component_ids[idx]->size(); } bool BiconnectedDecomposer::_pushToStack (Array<int> &dfs_stack, int v) { Edge new_edge; const Vertex &v_vert = _graph.getVertex(v); int u; if (dfs_stack.size() > 1) u = dfs_stack[dfs_stack.size() - 2]; else u = -1; for (int j = v_vert.neiBegin(); j < v_vert.neiEnd(); j = v_vert.neiNext(j)) { int w = v_vert.neiVertex(j); if (_dfs_order[w] == 0) { // Push new edge new_edge.beg = v; new_edge.end = w; _edges_stack.push(new_edge); dfs_stack.push(w); _cur_order++; _dfs_order[w] = _lowest_order[w] = _cur_order; return true; } else if (_dfs_order[w] < _dfs_order[v] && w != u) { new_edge.beg = v; new_edge.end = w; _edges_stack.push(new_edge); if (_lowest_order[v] > _dfs_order[w]) _lowest_order[v] = _dfs_order[w]; } } return false; } void BiconnectedDecomposer::_processIfNotPushed (Array<int> &dfs_stack, int w) { int v = dfs_stack.top(); if (_lowest_order[w] < _lowest_order[v]) _lowest_order[v] = _lowest_order[w]; if (_lowest_order[w] >= _dfs_order[v]) { //v -articulation point in G; //start new BCcomp; Array<int> &new_comp = _components.add(new Array<int>()); new_comp.clear_resize(_graph.vertexEnd()); new_comp.zerofill(); int cur_comp = _components.size() - 1; if (_component_ids[v] == 0) _component_ids[v] = &_component_lists.add(new Array<int>()); _component_ids[v]->push(cur_comp); while (_dfs_order[_edges_stack.top().beg] >= _dfs_order[w]) { _components[cur_comp]->at(_edges_stack.top().beg) = 1; _components[cur_comp]->at(_edges_stack.top().end) = 1; _edges_stack.pop(); } _components[cur_comp]->at(v) = 1; _components[cur_comp]->at(w) = 1; _edges_stack.pop(); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/cycle_basis.cpp�������������������������������������������������������0000664�0000000�0000000�00000004573�12710376503�0021066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/simple_cycle_basis.h" #include "graph/cycle_basis.h" #include "graph/biconnected_decomposer.h" #include "base_cpp/tlscont.h" using namespace indigo; void CycleBasis::create(const Graph& graph) { QS_DEF(Array<int>, mapping_out); // using biconnected decomposer since components will contain smallest cycles BiconnectedDecomposer bic_dec(graph); int comp_num = bic_dec.decompose(); _cycles.clear(); _cycleVertices.clear(); QS_DEF(Graph, subgraph); Filter filter; for (int i = 0; i < comp_num; ++i) { bic_dec.getComponent(i, filter); // create subgraph and store mapping subgraph.makeSubgraph(graph, filter, &mapping_out, 0); if (subgraph.edgeCount() > 1) { SimpleCycleBasis simple_cycle(subgraph); // create cycles for each biconnected component simple_cycle.create(); // create new cycle consider the mapping for (int k = 0; k < simple_cycle.getCyclesCount(); ++k) { const Array<int>& cycle = simple_cycle.getCycle(k); Array<int>& new_cycle = _cycles.push(); for(int j = 0; j < cycle.size(); ++j) { // cycle is edge list so we have to covert from subgraph edge list to graph edge list int source = subgraph.getEdge(cycle[j]).beg; int target = subgraph.getEdge(cycle[j]).end; int edge_idx = graph.findEdgeIndex(mapping_out[source], mapping_out[target]); _cycleVertices.find_or_insert(mapping_out[source]); _cycleVertices.find_or_insert(mapping_out[target]); new_cycle.push(edge_idx); } } } } } bool CycleBasis::containsVertex(int vertex) const { return _cycleVertices.find(vertex); } �������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/cycle_enumerator.cpp��������������������������������������������������0000664�0000000�0000000�00000010121�12710376503�0022130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/cycle_enumerator.h" #include "graph/spanning_tree.h" using namespace indigo; CycleEnumerator::CycleEnumerator (Graph &graph) : _graph(graph) { min_length = 0; max_length = graph.vertexCount(); context = 0; cb_check_vertex = 0; cb_handle_cycle = 0; vfilter = 0; } CycleEnumerator::~CycleEnumerator () { } bool CycleEnumerator::process () { int i; SpanningTree spt(_graph, vfilter); for (i = 0; i < spt.getEdgesNum(); i++) { const SpanningTree::ExtEdge &ext_edge = spt.getExtEdge(i); int v = ext_edge.ext_beg_idx; int w = ext_edge.ext_end_idx; if (cb_check_vertex == 0 || (cb_check_vertex(_graph, v, context) && cb_check_vertex(_graph, w, context))) { if (!_pathFinder(spt, v, w, ext_edge.ext_edge_idx)) return true; } spt.addEdge(ext_edge.beg_idx, ext_edge.end_idx, ext_edge.ext_edge_idx); } return false; } bool CycleEnumerator::_pathFinder (const SpanningTree &spt, int ext_v1, int ext_v2, int ext_e) { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); QS_DEF(Array<int>, flags); QS_DEF(Array<int>, visited_vertices); int cur_start_idx = 0; vertices.clear(); edges.clear(); flags.clear_resize(_graph.vertexEnd()); flags.zerofill(); vertices.push(ext_v1); vertices.push(ext_v2); flags[ext_v1] = 1; flags[ext_v2] = 1; edges.push(ext_e); visited_vertices.clear_resize(spt.getVertexFromExtIdx(ext_v2).neiEnd()); visited_vertices.zerofill(); // DFS all cycles with given edge while (vertices.size() > 1) { const Vertex &v_vertex = spt.getVertexFromExtIdx(vertices.top()); bool no_push = true; if (vertices.size() <= max_length) { for (int i = v_vertex.neiBegin(); i != v_vertex.neiEnd(); i = v_vertex.neiNext(i)) { if (visited_vertices[cur_start_idx + i]) continue; visited_vertices[cur_start_idx + i] = 1; int u = spt.getExtVertexIndex(v_vertex.neiVertex(i)); int e = spt.getExtEdgeIndex(v_vertex.neiEdge(i)); bool cycle = (vertices.size() > 2) && u == vertices[0]; if (!cycle) { if (flags[u]) continue; if (cb_check_vertex != 0 && !cb_check_vertex(_graph, u, context)) continue; } if (cycle) { if (min_length != 0 && vertices.size() < min_length) continue; edges.push(e); if (cb_handle_cycle != 0 && !cb_handle_cycle(_graph, vertices, edges, context)) return false; edges.pop(); } else { edges.push(e); vertices.push(u); flags[u] = 1; cur_start_idx += v_vertex.neiEnd(); const Vertex &u_vertex = spt.getVertexFromExtIdx(u); visited_vertices.expand(cur_start_idx + u_vertex.neiEnd()); memset(&visited_vertices[cur_start_idx], 0, u_vertex.neiEnd() * sizeof(int)); no_push = false; break; } } } if (no_push) { if (edges.size() > 0) edges.pop(); flags[vertices.pop()] = 0; cur_start_idx -= v_vertex.neiEnd(); } } return true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/dfs_walk.cpp����������������������������������������������������������0000664�0000000�0000000�00000014312�12710376503�0020370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/dfs_walk.h" #include "graph/graph.h" #include "base_cpp/array.h" using namespace indigo; IMPL_ERROR(DfsWalk, "DFS walk"); CP_DEF(DfsWalk); DfsWalk::DfsWalk (const Graph &graph) : _graph(graph), CP_INIT, TL_CP_GET(_vertices), TL_CP_GET(_edges), TL_CP_GET(_v_seq), TL_CP_GET(_root_vertices), TL_CP_GET(_closures), TL_CP_GET(_class_dist_from_exit) { ignored_vertices = 0; vertex_ranks = 0; vertex_classes = 0; _root_vertices.resize(graph.vertexEnd()); _root_vertices.zerofill(); } DfsWalk::~DfsWalk () { } void DfsWalk::mustBeRootVertex (int v_idx) { _root_vertices[v_idx] = 1; } void DfsWalk::walk () { QS_DEF(Array<int>, v_stack); int i, j; if (vertex_ranks != 0 && vertex_classes != 0) throw Error("you can not specify both vertex_ranks and vertex_classes"); _vertices.clear_resize(_graph.vertexEnd()); _edges.clear_resize(_graph.edgeEnd()); _vertices.zerofill(); _edges.zerofill(); _closures.clear(); v_stack.clear(); _v_seq.clear(); while (1) { if (v_stack.size() < 1) { int selected = -1; for (i = _graph.vertexBegin(); i != _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (ignored_vertices != 0 && ignored_vertices[i] != 0) continue; if (_vertices[i].dfs_state != 0) continue; if (vertex_classes != 0 && vertex_classes[i] >= 0) continue; if (vertex_ranks == 0) { selected = i; break; } if (selected == -1 || vertex_ranks[i] < vertex_ranks[selected]) selected = i; } if (selected == -1) break; _vertices[selected].parent_vertex = -1; _vertices[selected].parent_edge = -1; v_stack.push(selected); } int v_idx = v_stack.pop(); int parent_vertex = _vertices[v_idx].parent_vertex; { SeqElem &seq_elem = _v_seq.push(); seq_elem.idx = v_idx; seq_elem.parent_vertex = parent_vertex; seq_elem.parent_edge = _vertices[v_idx].parent_edge; } _vertices[v_idx].dfs_state = 2; const Vertex &vertex = _graph.getVertex(v_idx); QS_DEF(Array<VertexEdge>, nei); nei.clear(); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int nei_v = vertex.neiVertex(i); if (ignored_vertices != 0 && ignored_vertices[nei_v] != 0) continue; if (_root_vertices[nei_v] == 1 && _vertices[nei_v].dfs_state == 0) continue; VertexEdge &ve = nei.push(); ve.e = vertex.neiEdge(i); ve.v = nei_v; } if (vertex_ranks != 0) nei.qsort(_cmp_ranks, vertex_ranks); if (vertex_classes != 0 && vertex_classes[v_idx] >= 0) { // prefer not to leave the class if possible _current_class = vertex_classes[v_idx]; nei.qsort(_cmp_classes, this); } for (i = 0; i < nei.size(); i++) { int edge_idx = nei[i].e; int nei_idx = nei[i].v; if (nei_idx == parent_vertex) continue; if (_vertices[nei_idx].dfs_state == 2) { _edges[edge_idx].closing_cycle = 1; Edge &e = _closures.push(); e.beg = v_idx; e.end = nei_idx; _vertices[nei_idx].openings++; _vertices[v_idx].branches++; SeqElem &seq_elem = _v_seq.push(); seq_elem.idx = nei_idx; seq_elem.parent_vertex = v_idx; seq_elem.parent_edge = edge_idx; } else { if (_vertices[nei_idx].dfs_state == 1) { j = v_stack.find(nei_idx); if (j == -1) throw Error("internal: removing vertex from stack"); v_stack.remove(j); int parent = _vertices[nei_idx].parent_vertex; if (parent >= 0) _vertices[parent].branches--; } _vertices[v_idx].branches++; _vertices[nei_idx].parent_vertex = v_idx; _vertices[nei_idx].parent_edge = edge_idx; _vertices[nei_idx].dfs_state = 1; v_stack.push(nei_idx); } } } } const Array<DfsWalk::SeqElem> & DfsWalk::getSequence () const { return _v_seq; } bool DfsWalk::isClosure (int e_idx) const { return _edges[e_idx].closing_cycle != 0; } int DfsWalk::numBranches (int v_idx) const { return _vertices[v_idx].branches; } int DfsWalk::numOpenings (int v_idx) const { return _vertices[v_idx].openings; } void DfsWalk::calcMapping (Array<int> &mapping) const { int i, counter = 0; mapping.clear_resize(_graph.vertexEnd()); mapping.fffill(); for (i = 0; i < _v_seq.size(); i++) { if (mapping[_v_seq[i].idx] == -1) mapping[_v_seq[i].idx] = counter++; } } int DfsWalk::_cmp_ranks (VertexEdge &ve1, VertexEdge &ve2, void *context) { int *ranks = (int *)context; return ranks[ve2.v] - ranks[ve1.v]; } int DfsWalk::_cmp_classes (VertexEdge &ve1, VertexEdge &ve2, void *context) { DfsWalk *self = (DfsWalk *)context; if (self->vertex_classes[ve1.v] == self->_current_class) return 1; if (self->vertex_classes[ve2.v] == self->_current_class) return -1; return 0; } void DfsWalk::getNeighborsClosing (int v_idx, Array<int> &res) { int i; res.clear(); for (i = 0; i < _closures.size(); i++) { if (_closures[i].end == v_idx) res.push(_closures[i].beg); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/edge_rotation_matcher.cpp���������������������������������������������0000664�0000000�0000000�00000023544�12710376503�0023133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/edge_rotation_matcher.h" #include "graph/graph.h" #include "graph/spanning_tree.h" #include "graph/graph_affine_matcher.h" #include "math/algebra.h" using namespace indigo; IMPL_ERROR(EdgeRotationMatcher, "edge rotation matcher"); EdgeRotationMatcher::EdgeRotationMatcher (Graph &subgraph, Graph &supergraph, const int *mapping) : _subgraph(subgraph), _supergraph(supergraph), _mapping(mapping) { cb_get_xyz = 0; cb_can_rotate = 0; equalize_edges = false; } bool EdgeRotationMatcher::match (float rms_threshold, float eps) { if (cb_get_xyz == 0) throw Error("cb_get_xyz not specified"); if (_subgraph.vertexCount() < 2 || _subgraph.edgeCount() < 1) return true; QS_DEF(Array<int>, in_cycle); QS_DEF(Array<_DirEdge>, edge_queue); QS_DEF(Array<int>, vertex_queue); QS_DEF(Array<int>, states); in_cycle.clear_resize(_subgraph.edgeEnd()); edge_queue.clear(); states.clear_resize(__max(_subgraph.edgeEnd(), _subgraph.vertexEnd() + 1)); int i, j, k, bottom; // Find all subgraph bridges SpanningTree spt(_subgraph, 0); in_cycle.zerofill(); spt.markAllEdgesInCycles(in_cycle.ptr(), 1); // Find the first bridge, put it to the queue 2 times for (i = _subgraph.edgeBegin(); i < _subgraph.edgeEnd(); i = _subgraph.edgeNext(i)) if (!in_cycle[i] && (cb_can_rotate == 0 || cb_can_rotate(_subgraph, i))) { const Edge &edge = _subgraph.getEdge(i); if (_mapping[edge.beg] < 0 || _mapping[edge.end] < 0) continue; edge_queue.push(); edge_queue.top().idx = i; edge_queue.top().beg = edge.beg; edge_queue.top().end = edge.end; edge_queue.push(); edge_queue.top().idx = i; edge_queue.top().beg = edge.end; edge_queue.top().end = edge.beg; break; } // If the queue is empty, then we have no bridge if (edge_queue.size() == 0) { GraphAffineMatcher afm(_subgraph, _supergraph, _mapping); afm.cb_get_xyz = cb_get_xyz; return afm.match(rms_threshold); } float scale = 1.f; // detect scaling factor by average bond length if (equalize_edges) { float sum_sub = 0.f, sum_super = 0.f; for (i = _subgraph.edgeBegin(); i < _subgraph.edgeEnd(); i = _subgraph.edgeNext(i)) { const Edge &edge = _subgraph.getEdge(i); Vec3f beg, end; cb_get_xyz(_subgraph, edge.beg, beg); cb_get_xyz(_subgraph, edge.end, end); sum_sub += Vec3f::dist(beg, end); } for (i = _supergraph.edgeBegin(); i < _supergraph.edgeEnd(); i = _supergraph.edgeNext(i)) { const Edge &edge = _supergraph.getEdge(i); Vec3f beg, end; cb_get_xyz(_supergraph, edge.beg, beg); cb_get_xyz(_supergraph, edge.end, end); sum_super += Vec3f::dist(beg, end); } if (sum_sub > EPSILON && sum_super > EPSILON) { sum_sub /= _subgraph.edgeCount(); sum_super /= _supergraph.edgeCount(); scale = sum_super / sum_sub; } } // save vertex positions QS_DEF(Array<Vec3f>, xyz_sub); QS_DEF(Array<Vec3f>, xyz_super); QS_DEF(Array<int>, xyzmap); xyzmap.clear_resize(_supergraph.vertexEnd()); xyz_sub.clear(); xyz_super.clear(); for (i = _subgraph.vertexBegin(); i != _subgraph.vertexEnd(); i = _subgraph.vertexNext(i)) { if (_mapping[i] < 0) continue; Vec3f &pos_sub = xyz_sub.push(); Vec3f &pos_super = xyz_super.push(); cb_get_xyz(_subgraph, i, pos_sub); cb_get_xyz(_supergraph, _mapping[i], pos_super); pos_sub.scale(scale); xyzmap[_mapping[i]] = xyz_sub.size() - 1; } // Make queue of edges states.zerofill(); bottom = 0; while (edge_queue.size() != bottom) { // extract edge from queue int edge_end = edge_queue[bottom].end; int edge_idx = edge_queue[bottom].idx; bottom++; // mark it as 'completed' states[edge_idx] = 2; // look for neighbors const Vertex &end_vertex = _subgraph.getVertex(edge_end); for (i = end_vertex.neiBegin(); i != end_vertex.neiEnd(); i = end_vertex.neiNext(i)) { int nei_edge_idx = end_vertex.neiEdge(i); // check that neighbor have 'untouched' status if (states[nei_edge_idx] != 0) continue; const Edge &nei_edge = _subgraph.getEdge(nei_edge_idx); int other_end = nei_edge.findOtherEnd(edge_end); if (_mapping[other_end] < 0) continue; // set status 'in process' states[nei_edge_idx] = 1; // push the neighbor edge to the queue edge_queue.push(); edge_queue.top().idx = nei_edge_idx; edge_queue.top().beg = edge_end; edge_queue.top().end = other_end; } } // do initial transform (impose first subgraph edge in the queue on corresponding one in the graph) int beg2 = edge_queue[0].beg; int end2 = edge_queue[0].end; int beg1 = _mapping[beg2]; int end1 = _mapping[end2]; Vec3f g1_v1, g1_v2, g2_v1, g2_v2, diff1, diff2; Transform3f matr; cb_get_xyz(_supergraph, beg1, g1_v1); cb_get_xyz(_supergraph, end1, g1_v2); cb_get_xyz(_subgraph, beg2, g2_v1); cb_get_xyz(_subgraph, end2, g2_v2); g2_v1.scale(scale); g2_v2.scale(scale); diff1.diff(g1_v2, g1_v1); diff2.diff(g2_v2, g2_v1); matr.identity(); if (!matr.rotationVecVec(diff2, diff1)) throw Error("error calling RotationVecVec()"); matr.translateLocal(-g2_v1.x, -g2_v1.y, -g2_v1.z); matr.translate(g1_v1); for (k = 0; k < xyz_sub.size(); k++) xyz_sub[k].transformPoint(matr); // for all edges in queue that are subject to rotate... for (i = 0; i < edge_queue.size(); i++) { int edge_beg = edge_queue[i].beg; int edge_end = edge_queue[i].end; int edge_idx = edge_queue[i].idx; if (in_cycle[edge_idx]) continue; if (cb_can_rotate != 0 && !cb_can_rotate(_subgraph, edge_idx)) continue; // start BFS from the end of the edge states.zerofill(); states[edge_end] = 1; vertex_queue.clear(); vertex_queue.push(edge_end); bottom = 0; while (vertex_queue.size() != bottom) { // extract vertex from queue const Vertex &vertex = _subgraph.getVertex(vertex_queue[bottom]); states[vertex_queue[bottom]] = 2; bottom++; // look over neighbors for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int nei_idx = vertex.neiVertex(j); if (nei_idx == edge_beg) continue; if (states[nei_idx] != 0) continue; states[nei_idx] = 1; vertex_queue.push(nei_idx); } } // now states[j] == 0 if j-th vertex shound not be moved Vec3f edge_beg_pos, edge_end_pos, rot_axis; // get rotation axis edge_beg_pos.copy(xyz_sub[xyzmap[_mapping[edge_beg]]]); edge_end_pos.copy(xyz_sub[xyzmap[_mapping[edge_end]]]); rot_axis.diff(edge_end_pos, edge_beg_pos); if (!rot_axis.normalize()) continue; const Vertex &edge_end_vertex = _subgraph.getVertex(edge_end); float max_sum_len = -1; for (j = edge_end_vertex.neiBegin(); j != edge_end_vertex.neiEnd(); j = edge_end_vertex.neiNext(j)) { int nei_idx_2 = edge_end_vertex.neiVertex(j); int nei_idx_1 = _mapping[nei_idx_2]; if (nei_idx_2 == edge_beg) continue; if (nei_idx_1 == -1) continue; Vec3f nei1_pos; Vec3f nei2_pos; nei1_pos.copy(xyz_super[xyzmap[nei_idx_1]]); nei2_pos.copy(xyz_sub[xyzmap[_mapping[nei_idx_2]]]); nei1_pos.sub(edge_end_pos); nei2_pos.sub(edge_end_pos); float dot1 = Vec3f::dot(nei1_pos, rot_axis); float dot2 = Vec3f::dot(nei2_pos, rot_axis); nei1_pos.addScaled(rot_axis, -dot1); nei2_pos.addScaled(rot_axis, -dot2); if (max_sum_len > nei1_pos.length() + nei1_pos.length()) continue; max_sum_len = nei1_pos.length() + nei1_pos.length(); if (!nei1_pos.normalize() || !nei2_pos.normalize()) continue; double dp = Vec3f::dot(nei1_pos, nei2_pos); if (dp > 1 - EPSILON) dp = 1 - EPSILON; if (dp < -1 + EPSILON) dp = -1 + EPSILON; double ang = acos(dp); Vec3f cross; cross.cross(nei1_pos, nei2_pos); if (Vec3f::dot(cross, rot_axis) < 0) ang = -ang; matr.rotation(rot_axis.x, rot_axis.y, rot_axis.z, (float)ang); matr.translateLocalInv(edge_end_pos); matr.translate(edge_end_pos); } if (max_sum_len > 0) { for (j = _subgraph.vertexBegin(); j < _subgraph.vertexEnd(); j = _subgraph.vertexNext(j)) if (_mapping[j] >= 0 && states[j] != 0) xyz_sub[xyzmap[_mapping[j]]].transformPoint(matr); } } float sqsum = 0; for (k = 0; k < xyz_sub.size(); k++) sqsum += Vec3f::distSqr(xyz_sub[k], xyz_super[k]); sqsum = sqrt(sqsum / xyz_sub.size()); if (sqsum > rms_threshold + eps) return false; return true; } ������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/edge_subgraph_enumerator.cpp������������������������������������������0000664�0000000�0000000�00000017173�12710376503�0023646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "graph/spanning_tree.h" #include "graph/edge_subgraph_enumerator.h" using namespace indigo; IMPL_ERROR(EdgeSubgraphEnumerator, "edge subgraph enumerator"); CP_DEF(EdgeSubgraphEnumerator); EdgeSubgraphEnumerator::EdgeSubgraphEnumerator (Graph &graph) : _graph(graph), CP_INIT, TL_CP_GET(_subgraph), TL_CP_GET(_mapping), TL_CP_GET(_inv_mapping), TL_CP_GET(_edge_mapping), TL_CP_GET(_inv_edge_mapping), TL_CP_GET(_pool), TL_CP_GET(_adjacent_edges) { min_edges = 1; max_edges = graph.edgeCount(); cb_subgraph = 0; userdata = 0; } int EdgeSubgraphEnumerator::_fCIS () { int j; int min = _graph.edgeEnd(); for (j = _subgraph.edgeBegin(); j < _subgraph.edgeEnd(); j = _subgraph.edgeNext(j)) { const Edge &edge = _subgraph.getEdge(j); bool good = false; // 'hanging' edge? if (_subgraph.getVertex(edge.beg).degree() == 1 || _subgraph.getVertex(edge.end).degree() == 1) good = true; else if (_subgraph.getEdgeTopology(j) == TOPOLOGY_RING) good = true; if (good && min > _edge_mapping[j]) min = _edge_mapping[j]; } return min; } EdgeSubgraphEnumerator::_Enumerator::_Enumerator (EdgeSubgraphEnumerator &context) : _context(context), _graph(context._graph), _subgraph(context._subgraph), _adjacent_edges_added(context._pool) { _added_vertex = -1; _added_edge = -1; } EdgeSubgraphEnumerator::_Enumerator::_Enumerator (const EdgeSubgraphEnumerator::_Enumerator &other) : _context(other._context), _graph(other._context._graph), _subgraph(other._context._subgraph), _adjacent_edges_added(other._context._pool) { _added_vertex = -1; _added_edge = -1; } void EdgeSubgraphEnumerator::_Enumerator::process () { if (_subgraph.edgeCount() >= _context.max_edges) throw Error("subgraph exceeds max_edges"); int i, j; // find adjacent edges for (j = _subgraph.edgeBegin(); j < _subgraph.edgeEnd(); j = _subgraph.edgeNext(j)) { const Edge &edge = _subgraph.getEdge(j); int vbeg_idx = _context._mapping[edge.beg]; int vend_idx = _context._mapping[edge.end]; const Vertex &vbeg = _graph.getVertex(vbeg_idx); const Vertex &vend = _graph.getVertex(vend_idx); for (i = vbeg.neiBegin(); i != vbeg.neiEnd(); i = vbeg.neiNext(i)) { int edge_idx = vbeg.neiEdge(i); if (!_context._adjacent_edges[edge_idx] && _context._inv_edge_mapping[edge_idx] < 0) _addAdjacentEdge(edge_idx); } for (i = vend.neiBegin(); i != vend.neiEnd(); i = vend.neiNext(i)) { int edge_idx = vend.neiEdge(i); if (!_context._adjacent_edges[edge_idx] && _context._inv_edge_mapping[edge_idx] < 0) _addAdjacentEdge(edge_idx); } } for (j = _graph.edgeBegin(); j < _graph.edgeEnd(); j = _graph.edgeNext(j)) { if (!_context._adjacent_edges[j]) continue; if (_context._inv_edge_mapping[j] >= 0) throw Error("internal error: edge mapped"); _Enumerator next(*this); next._addEdgeToSubgraph(j); // reverse traverse if (j == _context._fCIS()) { if (_subgraph.edgeCount() >= _context.min_edges && _subgraph.edgeCount() <= _context.max_edges) { if (_context.cb_subgraph != 0) _context.cb_subgraph(_graph, _context._inv_mapping.ptr(), _context._inv_edge_mapping.ptr(), _context.userdata); } if (_subgraph.edgeCount() < _context.max_edges) next.process(); } next._removeAddedEdge(); } _removeAdjacentEdges(); } void EdgeSubgraphEnumerator::_Enumerator::_addEdgeToSubgraph (int edge_idx) { const Edge &edge = _graph.getEdge(edge_idx); int beg = _context._inv_mapping[edge.beg]; int end = _context._inv_mapping[edge.end]; if (beg == -1 && end == -1) throw Error("internal error: beg == -1 && end == -1"); if (beg == -1) { beg = _added_vertex = _subgraph.addVertex(); _context._mapping[_added_vertex] = edge.beg; _context._inv_mapping[edge.beg] = _added_vertex; } else if (end == -1) { end = _added_vertex = _subgraph.addVertex(); _context._mapping[_added_vertex] = edge.end; _context._inv_mapping[edge.end] = _added_vertex; } _added_edge = _subgraph.addEdge(beg, end); _context._edge_mapping[_added_edge] = edge_idx; _context._inv_edge_mapping[edge_idx] = _added_edge; _context._adjacent_edges[edge_idx] = 0; } void EdgeSubgraphEnumerator::_Enumerator::_removeAddedEdge () { if (_added_edge >= 0) { int idx = _context._edge_mapping[_added_edge]; _subgraph.removeEdge(_added_edge); _context._edge_mapping[_added_edge] = -1; _context._inv_edge_mapping[idx] = -1; _context._adjacent_edges[idx] = 1; } if (_added_vertex >= 0) { int idx = _context._mapping[_added_vertex]; _subgraph.removeVertex(_added_vertex); _context._inv_mapping[idx] = -1; _context._mapping[_added_vertex] = -1; } } void EdgeSubgraphEnumerator::_Enumerator::_addAdjacentEdge (int edge_idx) { _context._adjacent_edges[edge_idx] = 1; _adjacent_edges_added.add(edge_idx); } void EdgeSubgraphEnumerator::_Enumerator::_removeAdjacentEdges () { int i; for (i = _adjacent_edges_added.begin(); i != _adjacent_edges_added.end(); i = _adjacent_edges_added.next(i)) _context._adjacent_edges[_adjacent_edges_added.at(i)] = 0; _adjacent_edges_added.clear(); } void EdgeSubgraphEnumerator::process () { int i; _subgraph.clear(); _mapping.clear_resize(_graph.vertexCount()); _inv_mapping.clear_resize(_graph.vertexEnd()); _edge_mapping.clear_resize(_graph.edgeCount()); _inv_edge_mapping.clear_resize(_graph.edgeEnd()); _adjacent_edges.clear_resize(_graph.edgeEnd()); _adjacent_edges.zerofill(); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) _inv_mapping[i] = -1; for (i = _graph.edgeBegin(); i < _graph.edgeEnd(); i = _graph.edgeNext(i)) _inv_edge_mapping[i] = -1; for (i = _graph.edgeBegin(); i < _graph.edgeEnd(); i = _graph.edgeNext(i)) { const Edge edge = _graph.getEdge(i); int idx1 = _subgraph.addVertex(); int idx2 = _subgraph.addVertex(); int idx = _subgraph.addEdge(idx1, idx2); _mapping[idx1] = edge.beg; _inv_mapping[edge.beg] = idx1; _mapping[idx2] = edge.end; _inv_mapping[edge.end] = idx2; _edge_mapping[idx] = i; _inv_edge_mapping[i] = idx; if (1 >= min_edges && 1 <= max_edges) { if (cb_subgraph != 0) cb_subgraph(_graph, _inv_mapping.ptr(), _inv_edge_mapping.ptr(), userdata); } if (max_edges > 1) { _Enumerator enumerator(*this); enumerator.process(); } _subgraph.removeEdge(idx); _subgraph.removeVertex(idx1); _subgraph.removeVertex(idx2); _mapping[idx1] = -1; _mapping[idx2] = -1; _inv_mapping[edge.beg] = -1; _inv_mapping[edge.end] = -1; _edge_mapping[idx] = -1; _inv_edge_mapping[i] = -1; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/embedding_enumerator.cpp����������������������������������������������0000664�0000000�0000000�00000044156�12710376503�0022766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/embedding_enumerator.h" #include "base_c/defs.h" #include "base_cpp/tlscont.h" #include "base_cpp/cancellation_handler.h" #include "graph/graph.h" #include "graph/graph_vertex_equivalence.h" using namespace indigo; IMPL_ERROR(EmbeddingEnumerator, "embedding enumerator"); IMPL_TIMEOUT_EXCEPTION(EmbeddingEnumerator, "embedding enumerator"); CP_DEF(EmbeddingEnumerator); EmbeddingEnumerator::EmbeddingEnumerator (Graph &supergraph) : CP_INIT, TL_CP_GET(_core_1), TL_CP_GET(_core_2), TL_CP_GET(_term2), TL_CP_GET(_unterm2), TL_CP_GET(_s_pool), TL_CP_GET(_g1_fast), TL_CP_GET(_g2_fast), TL_CP_GET(_query_match_state), TL_CP_GET(_enumerators) { _g2 = &supergraph; _core_2.clear(); validate(); cb_embedding = 0; cb_match_vertex = 0; cb_match_edge = 0; cb_vertex_remove = 0; cb_edge_add = 0; cb_vertex_add = 0; userdata = 0; _cancellation_handler = getCancellationHandler(); _cancellation_check_number = 0; allow_many_to_one = false; _equivalence_handler = NULL; _enumerators.clear(); _enumerators.push(*this); } EmbeddingEnumerator::~EmbeddingEnumerator () { } void EmbeddingEnumerator::validate () { // _core_2 must be preserved because there might be fixed vertices _core_2.expandFill(_g2->vertexEnd(), -1); _g2_fast.setGraph(*_g2); } void EmbeddingEnumerator::setSubgraph (Graph &subgraph) { // if (subgraph.vertexCount() < 1) // throw Error("empty subgraph given"); _g1 = &subgraph; _core_1.clear_resize(_g1->vertexEnd()); _core_1.fffill(); // fill with UNMAPPED _t1_len_pre = 0; _terminatePreviousMatch(); _g1_fast.setGraph(*_g1); } void EmbeddingEnumerator::ignoreSubgraphVertex (int idx) { if (_g1 == 0) throw Error("no subgraph"); _core_1[idx] = IGNORE; } void EmbeddingEnumerator::ignoreSupergraphVertex (int idx) { _core_2[idx] = IGNORE; } void EmbeddingEnumerator::_terminatePreviousMatch () { for (int i = _g2->vertexBegin(); i < _g2->vertexEnd(); i = _g2->vertexNext(i)) if (_core_2[i] >= 0) _core_2[i] = IGNORE; else if (_core_2[i] == TERM_OUT) _core_2[i] = UNMAPPED; _term2.clear(); _unterm2.clear(); _enumerators[0].reset(); _query_match_state.clear(); } void EmbeddingEnumerator::setEquivalenceHandler (GraphVertexEquivalence *equivalence_handler) { _equivalence_handler = equivalence_handler; } bool EmbeddingEnumerator::fix (int node1, int node2) { return _enumerators[0].fix(node1, node2, true); } bool EmbeddingEnumerator::unsafeFix (int node1, int node2) { return _enumerators[0].fix(node1, node2, false); } int EmbeddingEnumerator::process () { processStart(); if (processNext()) return 0; return 1; } void EmbeddingEnumerator::processStart () { if (_g1 == 0) throw Error("subgraph not set"); if (_equivalence_handler != NULL) _equivalence_handler->prepareForQueries(); if (_equivalence_handler != NULL) _enumerators[0].setUseEquivalence(_equivalence_handler->useHeuristicFurther()); else _enumerators[0].setUseEquivalence(false); // Restore enumerators stack while (_enumerators.size() > 1) _enumerators.pop(); // // Save query indices ordered by preserving connectivity by walk // according to vertex numbers // QS_DEF(Array<int>, core1_pre); core1_pre.copy(_core_1); int t1_len_saved = _t1_len_pre; _query_match_state.clear(); const int FIX_MARK = _g2->vertexEnd(); int node1; while ((node1 = _getNextNode1()) != -1) { // Find node parent const Vertex &v = _g1->getVertex(node1); int parent = -1; for (int j = v.neiBegin(); j != v.neiEnd(); j = v.neiNext(j)) { int nei_vertex = v.neiVertex(j); if (_core_1[nei_vertex] >= 0) { parent = nei_vertex; break; } } _query_match_state.push(_QuertMatchState(node1, parent, _t1_len_pre)); _fixNode1(node1, FIX_MARK); } // Push last element to indicate the end of query atoms queue _query_match_state.push(_QuertMatchState(-1, -1, -1)); // Restore core_1 _core_1.copy(core1_pre); _t1_len_pre = t1_len_saved; _enumerators[0].initForFirstSearch(_t1_len_pre); } void EmbeddingEnumerator::_fixNode1 (int node1, int node2) { if (_core_1[node1] == TERM_OUT) _t1_len_pre--; _core_1[node1] = node2; const Vertex &v1 = _g1->getVertex(node1); for (int i = v1.neiBegin(); i != v1.neiEnd(); i = v1.neiNext(i)) { int other1 = v1.neiVertex(i); if (_core_1[other1] == UNMAPPED) { _core_1[other1] = TERM_OUT; _t1_len_pre++; } } } int EmbeddingEnumerator::_getNextNode1 () { for (int i = _g1->vertexBegin(); i != _g1->vertexEnd(); i = _g1->vertexNext(i)) { int val = _core_1[i]; if (val == TERM_OUT) return i; if (_t1_len_pre == 0 && val == UNMAPPED) return i; } return -1; } bool EmbeddingEnumerator::processNext () { if (_enumerators.size() > 1) { _enumerators.top().restore(); _enumerators.pop(); } while (1) { int command = _enumerators.top().nextPair(); if (command == _NOWAY) { if (_enumerators.size() > 1) { _enumerators.top().restore(); _enumerators.pop(); } else break; } else if (command == _ADD_PAIR) { int node1 = _enumerators.top()._current_node1; int node2 = _enumerators.top()._current_node2; _enumerators.reserve(_enumerators.size() + 1); _enumerators.push(_enumerators.top()); _enumerators.top().addPair(node1, node2); } else if (command == _RETURN0) return true; if (_cancellation_handler != NULL) { // Check only each 100th time if ((_cancellation_check_number % 100) == 0) if (_cancellation_handler->isCancelled()) throw TimeoutException("%s", _cancellation_handler->cancelledRequestMessage()); _cancellation_check_number++; } } while (_enumerators.size() > 1) _enumerators.pop(); return false; } EmbeddingEnumerator::_Enumerator::_Enumerator (EmbeddingEnumerator &context) : _context(context), _mapped_orbit_ids(context._s_pool) { _t1_len = 0; _t2_len = 0; _core_len = 0; _selected_node1 = -1; _selected_node2 = -1; _use_equivalence = false; _initState(); _current_node1_idx = 0; } EmbeddingEnumerator::_Enumerator::_Enumerator (const EmbeddingEnumerator::_Enumerator &other) : _context(other._context), _mapped_orbit_ids(other._context._s_pool) { _core_len = other._core_len; _t1_len = other._t1_len; _t2_len = other._t2_len; if (other._use_equivalence) _use_equivalence = _context._equivalence_handler->useHeuristicFurther(); else _use_equivalence = false; _initState(); _current_node1_idx = other._current_node1_idx; } void EmbeddingEnumerator::_Enumerator::_initState () { _current_node2 = -1; _current_node2_idx = -1; _current_node2_parent = -1; _current_node2_nei_index = -1; _term2_begin = _context._term2.size(); _unterm2_begin = _context._unterm2.size(); } void EmbeddingEnumerator::_Enumerator::_fixPair (int node1, int node2) { _context._fixNode1(node1, node2); _t1_len = _context._t1_len_pre; _addPairNode2(node1, node2); } void EmbeddingEnumerator::_Enumerator::addPair (int node1, int node2) { // Check if such node is added as expected if (_context._query_match_state[_current_node1_idx].atom_index != node1) throw Error("internal error: query atom %d is unexpected in addPair", node1); _current_node1_idx++; const _QuertMatchState &s = _context._query_match_state[_current_node1_idx]; _current_node1 = s.atom_index; _t1_len = s.t1_len; _addPairNode2(node1, node2); } void EmbeddingEnumerator::_Enumerator::_addPairNode2 (int node1, int node2) { if (_context._core_2[node2] == TERM_OUT) _t2_len--; _selected_node1 = node1; _selected_node2 = node2; _node1_prev_value = _context._core_1[node1]; _node2_prev_value = _context._core_2[node2]; _context._core_1[node1] = node2; _context._core_2[node2] = node1; _core_len++; int i; if (_t1_len > 0) { int node2_nei_count; int *node2_nei_v = _context._g2_fast.getVertexNeiVertices(node2, node2_nei_count); for (i = 0; i < node2_nei_count; i++) { int other2 = node2_nei_v[i]; if (_context._core_2[other2] == UNMAPPED) { _context._core_2[other2] = TERM_OUT; _t2_len++; _context._term2.push(other2); } } } else { // A connected component of subgraph has been mapped. // Need to reset TERM_OUT flags on supergraph. // Vertices only from _term2 array can have TERM_OUT marks int *t2_ptr = _context._term2.ptr(); int t2_size = _context._term2.size(); for (i = 0; i < t2_size; i++) { int v = t2_ptr[i]; if (_context._core_2[v] == TERM_OUT) { _context._core_2[v] = UNMAPPED; _context._unterm2.push(v); } } _t2_len = 0; } if (_use_equivalence) _context._equivalence_handler->fixVertex(node2); } bool EmbeddingEnumerator::_Enumerator::_checkNode2 (int node2, int for_node1) { int val = _context._core_2[node2]; if (val == TERM_OUT) return true; if (_t2_len == 0 && val == UNMAPPED) return true; if (_context.allow_many_to_one) { if (val == IGNORE) return false; if (_context.cb_allow_many_to_one != 0) { if (!_context.cb_allow_many_to_one(*_context._g1, for_node1, _context.userdata)) return false; // Check node that has already been mapped if (val >= 0 && !_context.cb_allow_many_to_one(*_context._g1, val, _context.userdata)) return false; } return true; } return false; } bool EmbeddingEnumerator::_Enumerator::_checkPair (int node1, int node2) { if (_context.cb_match_vertex != 0) if (!_context.cb_match_vertex(*_context._g1, *_context._g2, _context._core_1.ptr(), node1, node2, _context.userdata)) return false; int j; bool needRemove = false; int node1_nei_count; int *node1_nei_v = _context._g1_fast.getVertexNeiVertices(node1, node1_nei_count); int *node1_nei_e = _context._g1_fast.getVertexNeiEdges(node1, node1_nei_count); for (j = 0; j < node1_nei_count; j++) { int other1 = node1_nei_v[j]; int other2 = _context._core_1[other1]; if (other2 >= 0) { int edge1 = node1_nei_e[j]; int edge2 = _context._g2_fast.findEdgeIndex(node2, other2); if (edge2 == -1) break; if (_context.cb_match_edge != 0) if (!_context.cb_match_edge(*_context._g1, *_context._g2, edge1, edge2, _context.userdata)) break; if (_context.cb_edge_add != 0) _context.cb_edge_add(*_context._g1, *_context._g2, edge1, edge2, _context.userdata); needRemove = true; } } if (j != node1_nei_count) { if (needRemove && _context.cb_vertex_remove != 0) _context.cb_vertex_remove(*_context._g1, node1, _context.userdata); return false; } // This is vertex equivalence heuristics. if (_use_equivalence) { int eq_class = _context._equivalence_handler->getVertexEquivalenceClassId(node2); // Check if class isn't trivial if (eq_class != -1) { int pair_id = (node1 << 16) + eq_class; if (_mapped_orbit_ids.find_or_insert(pair_id)) { if (needRemove && _context.cb_vertex_remove != 0) _context.cb_vertex_remove(*_context._g1, node1, _context.userdata); return false; } } } if (_context.cb_vertex_add != 0) _context.cb_vertex_add(*_context._g1, *_context._g2, node1, node2, _context.userdata); return true; } void EmbeddingEnumerator::_Enumerator::restore () { int i, size; int *data; size = _context._term2.size(); data = _context._term2.ptr(); for (i = _term2_begin; i < size; i++) _context._core_2[data[i]] = UNMAPPED; _context._term2.resize(_term2_begin); size = _context._unterm2.size(); data = _context._unterm2.ptr(); for (i = _unterm2_begin; i < size; i++) _context._core_2[data[i]] = TERM_OUT; _context._unterm2.resize(_unterm2_begin); if (_selected_node1 >= 0) { _context._core_1[_selected_node1] = _node1_prev_value; _context._core_2[_selected_node2] = _node2_prev_value; if (_context.cb_vertex_remove != 0) _context.cb_vertex_remove(*_context._g1, _selected_node1, _context.userdata); if (_use_equivalence) _context._equivalence_handler->unfixVertex(_selected_node2); } } void EmbeddingEnumerator::_Enumerator::initForFirstSearch (int t1_len) { _t1_len = t1_len; _current_node1_idx = 0; _current_node1 = _context._query_match_state[_current_node1_idx].atom_index; } int EmbeddingEnumerator::_Enumerator::nextPair () { if (_current_node1 == -1) { // _RETURN0 should be returned only once. _current_node1 = -2; // all nodes of subgraph are mapped if (_context.cb_embedding == 0 || _context.cb_embedding(*_context._g1, *_context._g2, _context._core_1.ptr(), _context._core_2.ptr(), _context.userdata) == 0) return _RETURN0; else return _NOWAY; } if (_current_node1 == -2) return _NOWAY; // check for dead state if (_t1_len > _t2_len && !_context.allow_many_to_one) return _NOWAY; if (_t2_len == 0) { int v2_count; int *g2_vertices = _context._g2_fast.prepareVertices(v2_count); // If _current_node2_idx == -1 then _current_node2_idx will be 0 _current_node2_idx++; for (; _current_node2_idx < v2_count; _current_node2_idx++) { _current_node2 = g2_vertices[_current_node2_idx]; if (!_checkNode2(_current_node2, _current_node1)) continue; if (!_checkPair(_current_node1, _current_node2)) continue; break; } if (_current_node2_idx == v2_count) return _NOWAY; } else { // Find parent vertex for query _current_node1 vertex // and take coresponding vertex in target if (_current_node2_parent == -1) { int node1_parent = _context._query_match_state[_current_node1_idx].parent_index; if (node1_parent == -1) throw Error("internal error: node1_parent == -1"); _current_node2_parent = _context._core_1[node1_parent]; if (_current_node2_parent < 0) throw Error("_current_node2_parent < 0"); } int nei_count; int node2_parent_nei_id = _context._g2_fast.prepareVertexNeiVertices(_current_node2_parent, nei_count); _current_node2_nei_index++; for (; _current_node2_nei_index != nei_count; _current_node2_nei_index++) { _current_node2 = _context._g2_fast.getVertexNeiVertiex(node2_parent_nei_id, _current_node2_nei_index); if (!_checkNode2(_current_node2, _current_node1)) continue; if (!_checkPair(_current_node1, _current_node2)) continue; break; } if (_current_node2_nei_index == nei_count) return _NOWAY; } return _ADD_PAIR; } bool EmbeddingEnumerator::_Enumerator::fix (int node1, int node2, bool safe) { if (_context._core_1[node1] != UNMAPPED && _context._core_1[node1] != TERM_OUT) return false; if (_context._core_2[node2] != UNMAPPED && _context._core_2[node2] != TERM_OUT) return false; if (safe && !_checkPair(node1, node2)) return false; _fixPair(node1, node2); return true; } void EmbeddingEnumerator::_Enumerator::setUseEquivalence (bool use) { _use_equivalence = use; } void EmbeddingEnumerator::_Enumerator::reset () { _mapped_orbit_ids.clear(); _current_node1_idx = 0; _initState(); } const int * EmbeddingEnumerator::getSubgraphMapping () { return _core_1.ptr(); } const int * EmbeddingEnumerator::getSupergraphMapping () { return _core_2.ptr(); } int EmbeddingEnumerator::countUnmappedSubgraphVertices () { if (_g1 == 0) throw Error("subgraph not set"); int i, res = 0; for (i = _g1->vertexBegin(); i != _g1->vertexEnd(); i = _g1->vertexNext(i)) if (_core_1[i] == TERM_OUT || _core_1[i] == UNMAPPED) res++; return res; } int EmbeddingEnumerator::countUnmappedSupergraphVertices () { int i, res = 0; for (i = _g2->vertexBegin(); i != _g2->vertexEnd(); i = _g2->vertexNext(i)) if (_core_2[i] == TERM_OUT || _core_2[i] == UNMAPPED) res++; return res; } int EmbeddingEnumerator::countUnmappedSubgraphEdges () { int i, res = 0; for (i = _g1->edgeBegin(); i != _g1->edgeEnd(); i = _g1->edgeNext(i)) { const Edge &edge = _g1->getEdge(i); if (_core_1[edge.beg] != TERM_OUT && _core_1[edge.beg] != UNMAPPED) continue; if (_core_1[edge.end] != TERM_OUT && _core_1[edge.end] != UNMAPPED) continue; res++; } return res; } int EmbeddingEnumerator::countUnmappedSupergraphEdges () { int i, res = 0; for (i = _g2->edgeBegin(); i != _g2->edgeEnd(); i = _g2->edgeNext(i)) { const Edge &edge = _g2->getEdge(i); if (_core_2[edge.beg] != TERM_OUT && _core_2[edge.beg] != UNMAPPED) continue; if (_core_2[edge.end] != TERM_OUT && _core_2[edge.end] != UNMAPPED) continue; res++; } return res; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/embeddings_storage.cpp������������������������������������������������0000664�0000000�0000000�00000013477�12710376503�0022436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" #include "graph/graph.h" #include "graph/embeddings_storage.h" using namespace indigo; IMPL_ERROR(GraphEmbeddingsStorage, "embeddings storage"); GraphEmbeddingsStorage::GraphEmbeddingsStorage () { unique_by_edges = false; check_uniquencess = true; save_edges = false; save_mapping = false; } void GraphEmbeddingsStorage::_prepareForCompare (int id) { _EmbeddingData &data = _embedding_data[id]; if (data.sorted) return; _all_edges.qsort(data.edge_begin, data.edge_begin + data.edge_count - 1, IntCmpFunctor()); _all_vertices.qsort(data.vertex_begin, data.vertex_begin + data.vertex_count - 1, IntCmpFunctor()); data.sorted = true; } bool GraphEmbeddingsStorage::addEmbedding (const Graph &super, const Graph &sub, int *core_sub) { // Add new item to the storage // If it isn't unque then remove it _EmbeddingData &data = _embedding_data.push(); int added_index = _embedding_data.size() - 1; data.vertex_begin = _all_vertices.size(); data.edge_begin = _all_edges.size(); data.sub_mapping_begin = _all_mappings.size(); if (save_mapping) _all_mappings.concat(core_sub, sub.vertexEnd()); for (int i = sub.vertexBegin(); i != sub.vertexEnd(); i = sub.vertexNext(i)) if (core_sub[i] >= 0) _all_vertices.push(core_sub[i]); if (unique_by_edges || save_edges) { for (int i = sub.edgeBegin(); i != sub.edgeEnd(); i = sub.edgeNext(i)) { const Edge &e = sub.getEdge(i); if (core_sub[e.beg] < 0 || core_sub[e.end] < 0) // Such edge isn't mapped because one vertex is ignored continue; int edge_index = Graph::findMappedEdge(sub, super, i, core_sub); if (edge_index == -1) throw Error("Edge should be mapped"); _all_edges.push(edge_index); } } data.vertex_count = _all_vertices.size() - data.vertex_begin; data.edge_count = _all_edges.size() - data.edge_begin; data.sub_mapping_count = _all_mappings.size() - data.sub_mapping_begin; dword hash = _calcSetHash(_all_vertices, data.vertex_begin, data.vertex_count); if (unique_by_edges) hash ^= _calcSetHash(_all_edges, data.edge_begin, data.edge_count); data.sorted = false; // Try to find element with the same hash int *id = _map_hash_to_id.at2(hash); int append_to = -1; if (id != 0) { // Compare elements in the list int cur = *id; while (true) { if (check_uniquencess && _compareEmbedding(cur, added_index)) { // Such embedding already exists // Remove added element _all_vertices.resize(data.vertex_begin); _all_edges.resize(data.edge_begin); _all_mappings.resize(data.sub_mapping_begin); _embedding_data.pop(); return false; } if (_embedding_data[cur].next == -1) break; cur = _embedding_data[cur].next; } append_to = cur; } data.next = -1; if (append_to != -1) // Append embedding to the list of embeddings with the same hashes _embedding_data[append_to].next = added_index; else // Insert embedding into map _map_hash_to_id.insert(hash, added_index); return true; } bool GraphEmbeddingsStorage::isEmpty () const { return _all_vertices.size() == 0; } int GraphEmbeddingsStorage::count () const { return _embedding_data.size(); } const int* GraphEmbeddingsStorage::getVertices (int emb_idx, int &count) const { const _EmbeddingData &data = _embedding_data[emb_idx]; count = data.vertex_count; return _all_vertices.ptr() + data.vertex_begin; } const int* GraphEmbeddingsStorage::getEdges (int emb_idx, int &count) const { const _EmbeddingData &data = _embedding_data[emb_idx]; count = data.edge_count; return _all_edges.ptr() + data.edge_begin; } const int* GraphEmbeddingsStorage::getMappingSub (int emb_idx, int &count) const { const _EmbeddingData &data = _embedding_data[emb_idx]; count = data.sub_mapping_count; return _all_mappings.ptr() + data.sub_mapping_begin; } void GraphEmbeddingsStorage::clear() { _all_vertices.clear(); _all_edges.clear(); _embedding_data.clear(); _map_hash_to_id.clear(); } dword GraphEmbeddingsStorage::_calcSetHash (const Array<int> &set, int offset, int size) { dword hash = 0; const int* data = set.ptr() + offset; for (int i = 0; i < size; i++) hash ^= data[i] * 0x8088405 + 1; return hash; } bool GraphEmbeddingsStorage::_compareEmbedding (int id, int id2) { _prepareForCompare(id); _prepareForCompare(id2); _EmbeddingData &data = _embedding_data[id]; _EmbeddingData &data2 = _embedding_data[id2]; // Compare vertices if (data.vertex_count != data2.vertex_count) return false; for (int i = 0; i < data.vertex_count; i++) if (_all_vertices[i + data.vertex_begin] != _all_vertices[i + data2.vertex_begin]) return false; // Compare edges if (unique_by_edges) { if (data.edge_count != data2.edge_count) return false; for (int i = 0; i < data.edge_count; i++) if (_all_edges[i + data.edge_begin] != _all_edges[i + data2.edge_begin]) return false; } return true; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/filter.cpp������������������������������������������������������������0000664�0000000�0000000�00000006116�12710376503�0020066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/filter.h" #include "graph/graph.h" using namespace indigo; IMPL_ERROR(Filter, "filter"); Filter::Filter () : _filter(0), _value(0), _type(0) { } Filter::Filter (const int *filter, int type, int value) : _filter(filter), _value(value), _type(type) { } void Filter::init (const int *filter, int type, int value) { _own.clear(); _filter = filter; _value = value; _type = type; } void Filter::initAll (int size) { _own.clear_resize(size); _own.zerofill(); _filter = _own.ptr(); _type = EQ; _value = 0; } void Filter::initNone (int size) { _own.clear_resize(size); _own.zerofill(); _filter = _own.ptr(); _type = NEQ; _value = 0; } void Filter::hide (int idx) { if (_own.size() < 1) throw Error("can not hide() without initAll() or initNone()"); if (_type == EQ && _value == 0) _own[idx] = 1; else if (_type == NEQ && _value == 0) _own[idx] = 0; else throw Error("not implemented"); } void Filter::unhide (int idx) { if (_own.size() < 1) throw Error("can not hide() without initAll() or initNone()"); if (_type == EQ && _value == 0) _own[idx] = 0; else if (_type == NEQ && _value == 0) _own[idx] = 1; else throw Error("not implemented"); } bool Filter::valid (int idx) const { if (_filter == 0) throw Error("uninitialized"); if (_type == EQ) return _filter[idx] == _value; if (_type == NEQ) return _filter[idx] != _value; if (_type == LESS) return _filter[idx] < _value; if (_type == MORE) return _filter[idx] > _value; throw Error("unknown filter type %d", _type); } int Filter::count (const Graph &graph) const { if (_filter == 0) throw Error("uninitialized"); int n = 0; for (int i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i)) if (valid(i)) n++; return n; } void Filter::collectGraphVertices (const Graph &graph, Array<int> &indices) const { if (_filter == 0) throw Error("uninitialized"); indices.clear(); for (int i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i)) if (valid(i)) indices.push(i); } void Filter::collectGraphEdges (const Graph &graph, Array<int> &indices) const { if (_filter == 0) throw Error("uninitialized"); indices.clear(); for (int i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i)) if (valid(i)) indices.push(i); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph.cpp�������������������������������������������������������������0000664�0000000�0000000�00000047270�12710376503�0017710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <stdarg.h> #include <stdio.h> #include "base_c/defs.h" #include "base_cpp/tlscont.h" #include "graph/graph.h" #include "graph/spanning_tree.h" #include "graph/cycle_basis.h" #include "graph/graph_decomposer.h" using namespace indigo; NeighborsAuto Vertex::neighbors() const { return NeighborsAuto(*this); } int Vertex::findNeiVertex (int idx) const { for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i)) if (neighbors_list[i].v == idx) return i; return -1; } int Vertex::findNeiEdge (int idx) const { for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i)) if (neighbors_list[i].e == idx) return i; return -1; } IMPL_ERROR(Graph, "graph"); Graph::Graph () { _vertices = new ObjPool<Vertex>(); _neighbors_pool = new Pool<List<VertexEdge>::Elem>(); _sssr_pool = 0; _components_valid = false; } Graph::~Graph () { delete _vertices; delete _neighbors_pool; if (_sssr_pool != 0) { _sssr_vertices.clear(); _sssr_edges.clear(); delete _sssr_pool; } } int Graph::addVertex () { return _vertices->add(*_neighbors_pool); } int Graph::findEdgeIndex (int beg, int end) const { const Vertex &vbeg = getVertex(beg); for (int i = vbeg.neiBegin(); i != vbeg.neiEnd(); i = vbeg.neiNext(i)) if (vbeg.neiVertex(i) == end) return vbeg.neiEdge(i); return -1; } bool Graph::haveEdge (int beg, int end) const { return findEdgeIndex(beg, end) != -1; } bool Graph::hasEdge(int idx) const { return _edges.hasElement(idx); } bool Graph::hasVertex(int idx) const { return _vertices->hasElement(idx); } int Graph::getEdgeEnd (int beg, int edge) const { const Edge &e = getEdge(edge); if (e.beg == beg) return e.end; if (e.end == beg) return e.beg; return -1; } int Graph::addEdge (int beg, int end) { if (beg == end) throw Error("can't have loop-edge on vertex %d", beg); if (findEdgeIndex(beg, end) != -1) throw Error("already have edge between vertices %d and %d", beg, end); int edge_idx = _edges.add(); Vertex &vbeg = _vertices->at(beg); Vertex &vend = _vertices->at(end); int ve1_idx = vbeg.neighbors_list.add(); int ve2_idx = vend.neighbors_list.add(); VertexEdge &ve1 = vbeg.neighbors_list[ve1_idx]; VertexEdge &ve2 = vend.neighbors_list[ve2_idx]; ve1.v = end; ve2.v = beg; ve1.e = edge_idx; ve2.e = edge_idx; _edges[edge_idx].beg = beg; _edges[edge_idx].end = end; _topology_valid = false; _sssr_valid = false; _components_valid = false; return edge_idx; } void Graph::swapEdgeEnds (int edge_idx) { int tmp; __swap(_edges[edge_idx].beg, _edges[edge_idx].end, tmp); } void Graph::removeEdge (int idx) { Edge edge = _edges[idx]; Vertex &beg = _vertices->at(edge.beg); Vertex &end = _vertices->at(edge.end); _edges.remove(idx); beg.neighbors_list.remove(beg.findNeiEdge(idx)); end.neighbors_list.remove(end.findNeiEdge(idx)); _topology_valid = false; _sssr_valid = false; _components_valid = false; } void Graph::removeAllEdges () { for (int i = _vertices->begin(); i != _vertices->end(); i = _vertices->next(i)) _vertices->at(i).neighbors_list.clear(); _edges.clear(); _topology_valid = false; _sssr_valid = false; _components_valid = false; } void Graph::removeVertex (int idx) { QS_DEF(Array<int>, edges); const Vertex &vertex = getVertex(idx); int i; edges.clear(); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) edges.push(vertex.neiEdge(i)); for (i = 0; i < edges.size(); i++) removeEdge(edges[i]); _vertices->remove(idx); _topology_valid = false; _sssr_valid = false; _components_valid = false; } const Vertex & Graph::getVertex (int idx) const { return _vertices->at(idx); } const Edge & Graph::getEdge (int idx) const { return _edges[idx]; } bool Graph::isConnected (Graph &graph) { return graph.countComponents() == 1; } struct BfsState { int state; int prev; int edge; }; /* Finds path, writes edge indices into path_out. Returns false if no path. */ bool Graph::findPath (int from, int where, Array<int> &path_out) const { path_out.clear(); QS_DEF(Array<int>, queue); QS_DEF(Array<BfsState>, states); queue.clear_resize(_vertices->size()); states.clear_resize(_vertices->end()); states.zerofill(); int top = 1, bottom = 0; bool have_path = false; states[where].state = 1; queue[0] = where; while (top != bottom) { if (queue[bottom] == from) { have_path = true; break; } const Vertex &vertex = getVertex(queue[bottom]); states[queue[bottom]].state = 2; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int other = vertex.neiVertex(i); if (states[other].state == 0) { queue[top++] = other; states[other].state = 1; states[other].prev = queue[bottom]; states[other].edge = vertex.neiEdge(i); } } bottom++; } if (have_path) { while (from != where) { path_out.push(states[from].edge); from = states[from].prev; } return true; } return false; } VerticesAuto Graph::vertices () { return VerticesAuto(*this); } EdgesAuto Graph::edges () { return EdgesAuto(*this); } void Graph::clear () { _vertices->clear(); _edges.clear(); _topology_valid = false; _sssr_valid = false; _components_valid = false; } bool Graph::isChain_AssumingConnected (const Graph &graph) { // ensure it is a tree if (graph.vertexCount() - graph.edgeCount() != 1) return false; for (int i = graph.vertexBegin(); i < graph.vertexEnd(); i = graph.vertexNext(i)) if (graph.getVertex(i).degree() > 2) return false; return true; } bool Graph::isTree (Graph &graph) { if (!Graph::isConnected(graph)) return false; if (graph.vertexCount() != graph.edgeCount() + 1) return false; return true; } void Graph::filterVertices (const Graph &graph, const int *filter, int filter_type, int filter_value, Array<int> &result) { result.clear(); for (int i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i)) { if (filter != 0) { if (filter_type == FILTER_EQ && filter_value != filter[i]) continue; if (filter_type == FILTER_NEQ && filter_value == filter[i]) continue; } result.push(i); } } void Graph::filterEdges (const Graph &graph, const int *filter, int filter_type, int filter_value, Array<int> &result) { result.clear(); for (int i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i)) { if (filter != 0) { if (filter_type == FILTER_EQ && filter_value != filter[i]) continue; if (filter_type == FILTER_NEQ && filter_value == filter[i]) continue; } result.push(i); } } void Graph::_mergeWithSubgraph (const Graph &other, const Array<int> &vertices, const Array<int> *edges, Array<int> *vertex_mapping, Array<int> *edge_mapping) { QS_DEF(Array<int>, tmp_mapping); int i; if (vertex_mapping == 0) vertex_mapping = &tmp_mapping; vertex_mapping->clear_resize(other.vertexEnd()); vertex_mapping->fffill(); if (edge_mapping != 0) { edge_mapping->clear_resize(other.edgeEnd()); edge_mapping->fffill(); } for (i = 0; i < vertices.size(); i++) { int idx = vertices[i]; if (vertex_mapping->at(idx) != -1) throw Error("makeSubgraph(): repeated vertex #%d", idx); vertex_mapping->at(idx) = addVertex(); } if (edges != 0) { for (i = 0; i != edges->size(); i++) { const Edge &edge = other.getEdge(edges->at(i)); int beg = vertex_mapping->at(edge.beg); int end = vertex_mapping->at(edge.end); if (beg == -1 || end == -1) throw Error("_mergeWithSubgraph: edge %d maps to (%d, %d)", edges->at(i), beg, end); int idx = addEdge(beg, end); if (edge_mapping != 0) edge_mapping->at(edges->at(i)) = idx; } } else for (i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i)) { const Edge &edge = other.getEdge(i); int beg = vertex_mapping->at(edge.beg); int end = vertex_mapping->at(edge.end); if (beg != -1 && end != -1) { int idx = addEdge(beg, end); if (edge_mapping != 0) edge_mapping->at(i) = idx; } } } void Graph::buildEdgeMapping (const Graph &other, Array<int> *mapping, Array<int> *edge_mapping) { for (int i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i)) { const Edge &edge = other.getEdge(i); int beg = mapping->at(edge.beg); int end = mapping->at(edge.end); if (beg != -1 && end != -1) { int idx = findEdgeIndex(beg, end); if (edge_mapping != 0) edge_mapping->at(i) = idx; } } } void Graph::mergeWith (const Graph &other, Array<int> *mapping) { QS_DEF(Array<int>, vertices); int i; vertices.clear(); for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i)) vertices.push(i); _mergeWithSubgraph(other, vertices, 0, mapping, 0); } void Graph::makeSubgraph (const Graph &other, const Array<int> &vertices, Array<int> *vertex_mapping) { clear(); _mergeWithSubgraph(other, vertices, 0, vertex_mapping, 0); } void Graph::makeSubgraph (const Graph &other, const Array<int> &vertices, Array<int> *vertex_mapping, const Array<int> *edges, Array<int> *edge_mapping) { clear(); _mergeWithSubgraph(other, vertices, edges, vertex_mapping, edge_mapping); } void Graph::makeSubgraph (const Graph &other, const Filter &filter, Array<int> *mapping_out, Array<int> *inv_mapping) { QS_DEF(Array<int>, vertices); if (mapping_out == 0) mapping_out = &vertices; filter.collectGraphVertices(other, *mapping_out); makeSubgraph(other, *mapping_out, inv_mapping); } void Graph::cloneGraph (const Graph &other, Array<int> *mapping) { QS_DEF(Array<int>, vertices); vertices.clear(); for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) vertices.push(i); makeSubgraph(other, vertices, mapping); } void Graph::makeEdgeSubgraph (const Graph &other, const Array<int> &vertices, const Array<int> &edges, Array<int> *v_mapping, Array<int> *e_mapping) { QS_DEF(Array<int>, tmp_mapping); int i; if (v_mapping == 0) v_mapping = &tmp_mapping; v_mapping->clear_resize(other.vertexEnd()); for (i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) v_mapping->at(i) = -1; if (e_mapping != 0) e_mapping->clear_resize(other.edgeEnd()); clear(); for (i = 0; i < vertices.size(); i++) { int idx = vertices[i]; if (v_mapping->at(idx) != -1) throw Error("makeEdgeSubgraph(): repeated vertex #%d", idx); v_mapping->at(idx) = addVertex(); } for (i = 0; i < edges.size(); i++) { int edge_idx = edges[i]; const Edge &edge = other.getEdge(edge_idx); int beg = v_mapping->at(edge.beg); int end = v_mapping->at(edge.end); int new_edge_idx = addEdge(beg, end); if (e_mapping != 0) e_mapping->at(edge_idx) = new_edge_idx; } } int Graph::findMappedEdge (const Graph &graph, const Graph &mapped_graph, int edge_idx, const int *mapping) { const Edge &edge = graph.getEdge(edge_idx); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg == -1 || end == -1) return -1; return mapped_graph.findEdgeIndex(beg, end); } int Graph::getEdgeTopology (int idx) { if (!_topology_valid) _calculateTopology(); return _topology[idx]; } bool Graph::vertexInRing (int idx) { const Vertex &vertex = getVertex(idx); int i; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) if (getEdgeTopology(vertex.neiEdge(i)) == TOPOLOGY_RING) return true; return false; } void Graph::_calculateTopology () { SpanningTree spt(*this, 0); int i; _topology.clear_resize(_edges.end()); for (i = _edges.begin(); i != _edges.end(); i = _edges.next(i)) _topology[i] = TOPOLOGY_CHAIN; spt.markAllEdgesInCycles(_topology.ptr(), TOPOLOGY_RING); _topology_valid = true; } void Graph::setEdgeTopology (int idx, int topology) { _topology.expandFill(idx + 1, -1); _topology[idx] = topology; } void Graph::validateEdgeTopologies () { _topology_valid = true; } int Graph::vertexCountSSSR (int idx) { if (!_sssr_valid) _calculateSSSR(); return _v_sssr_count[idx]; } int Graph::vertexSmallestRingSize (int idx) { if (!_sssr_valid) _calculateSSSR(); return _v_smallest_ring_size[idx]; } int Graph::edgeSmallestRingSize (int idx) { if (!_sssr_valid) _calculateSSSR(); return _e_smallest_ring_size[idx]; } void Graph::_calculateSSSRInit () { _v_smallest_ring_size.clear_resize(vertexEnd()); _e_smallest_ring_size.clear_resize(edgeEnd()); _v_sssr_count.clear_resize(vertexEnd()); _v_smallest_ring_size.fffill(); _e_smallest_ring_size.fffill(); _v_sssr_count.zerofill(); if (_sssr_pool == 0) _sssr_pool = new Pool<List<int>::Elem>(); _sssr_vertices.clear(); _sssr_edges.clear(); } void Graph::_calculateSSSRByCycleBasis (CycleBasis &basis) { _calculateSSSRInit(); for (int i = 0; i < basis.getCyclesCount(); i++) { const Array<int> &cycle = basis.getCycle(i); List<int> &vertices = _sssr_vertices.push(*_sssr_pool); List<int> &edges = _sssr_edges.push(*_sssr_pool); _calculateSSSRAddEdgesAndVertices(cycle, edges, vertices); for (int j = vertices.begin(); j != vertices.end(); j = vertices.next(j)) { int idx = vertices[j]; if (_v_smallest_ring_size[idx] == -1 || _v_smallest_ring_size[idx] > cycle.size()) _v_smallest_ring_size[idx] = cycle.size(); _v_sssr_count[idx]++; } for (int j = edges.begin(); j != edges.end(); j = edges.next(j)) { int idx = edges[j]; if (_e_smallest_ring_size[idx] == -1 || _e_smallest_ring_size[idx] > cycle.size()) _e_smallest_ring_size[idx] = cycle.size(); } } for (int i = 0; i < _v_smallest_ring_size.size(); i++) if (_v_smallest_ring_size[i] == -1) _v_smallest_ring_size[i] = 0; for (int i = 0; i < _e_smallest_ring_size.size(); i++) if (_e_smallest_ring_size[i] == -1) _e_smallest_ring_size[i] = 0; _sssr_valid = true; } void Graph::_calculateSSSR () { // Note: function was split into smaller functions to reduce stack usage QS_DEF(CycleBasis, basis); basis.create(*this); _calculateSSSRByCycleBasis(basis); } void Graph::_calculateComponents () { GraphDecomposer decomposer(*this); int i; decomposer.decompose(); _component_numbers.clear_resize(vertexEnd()); for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) _component_numbers[i] = decomposer.getComponent(i); _components_count = decomposer.getComponentsCount(); _component_vcount.clear_resize(_components_count); _component_ecount.clear_resize(_components_count); for (i = 0; i < _components_count; i++) { _component_vcount[i] = decomposer.getComponentVerticesCount(i); _component_ecount[i] = decomposer.getComponentEdgesCount(i); } _components_valid = true; } int Graph::vertexComponent (int v_idx) { if (!_components_valid) _calculateComponents(); return _component_numbers[v_idx]; } int Graph::countComponents () { if (!_components_valid) _calculateComponents(); return _components_count; } int Graph::countComponentEdges (int comp_idx) { if (!_components_valid) _calculateComponents(); return _component_ecount[comp_idx]; } int Graph::countComponentVertices (int comp_idx) { if (!_components_valid) _calculateComponents(); return _component_vcount[comp_idx]; } const Array<int> & Graph::getDecomposition () { if (!_components_valid) _calculateComponents(); return _component_numbers; } List<int> & Graph::sssrEdges (int idx) { if (!_sssr_valid) _calculateSSSR(); return _sssr_edges[idx]; } List<int> & Graph::sssrVertices (int idx) { if (!_sssr_valid) _calculateSSSR(); return _sssr_vertices[idx]; } int Graph::sssrCount () { if (!_sssr_valid) _calculateSSSR(); return _sssr_vertices.size(); } void Graph::_cloneGraph_KeepIndices (const Graph &other) { if (vertexCount() > 0 || edgeCount() > 0) throw Error("can not _clone_KeepIndices into a non-empty graph"); int i, j, i_prev; int max_vertex_idx = -1; int max_edge_idx = -1; for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i)) if (max_vertex_idx < i) max_vertex_idx = i; for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i)) if (max_edge_idx < i) max_edge_idx = i; for (i = 0; i <= max_vertex_idx; i++) if (addVertex() != i) throw Error("_clone_KeepIndices: unexpected vertex index"); i_prev = -1; for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i)) { for (j = i_prev + 1; j < i; j++) removeVertex(j); i_prev = i; } if (vertexCount() != other.vertexCount()) throw Error("_clone_KeepIndices: internal"); for (i = 0; i <= max_edge_idx; i++) if (_edges.add() != i) throw Error("_clone_KeepIndices: unexpected edge index"); i_prev = -1; for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i)) { for (j = i_prev + 1; j < i; j++) _edges.remove(j); _edges[i].beg = other._edges[i].beg; _edges[i].end = other._edges[i].end; Vertex &vbeg = _vertices->at(_edges[i].beg); Vertex &vend = _vertices->at(_edges[i].end); int ve1_idx = vbeg.neighbors_list.add(); int ve2_idx = vend.neighbors_list.add(); VertexEdge &ve1 = vbeg.neighbors_list[ve1_idx]; VertexEdge &ve2 = vend.neighbors_list[ve2_idx]; ve1.v = _edges[i].end; ve2.v = _edges[i].beg; ve1.e = i; ve2.e = i; i_prev = i; } if (edgeCount() != other.edgeCount()) throw Error("_clone_KeepIndices: internal"); _topology_valid = false; _sssr_valid = false; _components_valid = false; } void Graph::_calculateSSSRAddEdgesAndVertices (const Array<int> &cycle, List<int> &edges, List<int> &vertices) { int prev_beg = -1; int prev_end = -1; for (int j = 0; j < cycle.size(); j++) { const Edge &edge = getEdge(cycle[j]); edges.add(cycle[j]); if (j != cycle.size() - 1) { if (edge.beg != prev_beg && edge.beg != prev_end) vertices.add(edge.beg); if (edge.end != prev_beg && edge.end != prev_end) vertices.add(edge.end); } prev_beg = edge.beg; prev_end = edge.end; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_affine_matcher.cpp����������������������������������������������0000664�0000000�0000000�00000004250�12710376503�0022712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_affine_matcher.h" #include "math/algebra.h" #include "base_cpp/tlscont.h" #include "graph/graph.h" using namespace indigo; IMPL_ERROR(GraphAffineMatcher, "graph affine matcher"); GraphAffineMatcher::GraphAffineMatcher (Graph &subgraph, Graph &supergraph, const int *mapping) : _subgraph(subgraph), _supergraph(supergraph), _mapping(mapping) { cb_get_xyz = 0; fixed_vertices = 0; } bool GraphAffineMatcher::match (float rms_threshold) { if (cb_get_xyz == 0) throw Error("cb_get_xyz not set"); int i; Transform3f matr; Vec3f pos; QS_DEF(Array<Vec3f>, points); QS_DEF(Array<Vec3f>, goals); points.clear(); goals.clear(); if (fixed_vertices != 0) { for (i = 0; i < fixed_vertices->size(); i++) { if (_mapping[fixed_vertices->at(i)] < 0) continue; cb_get_xyz(_subgraph, fixed_vertices->at(i), pos); points.push(pos); cb_get_xyz(_supergraph, _mapping[fixed_vertices->at(i)], pos); goals.push(pos); } } else for (i = _subgraph.vertexBegin(); i < _subgraph.vertexEnd(); i = _subgraph.vertexNext(i)) { if (_mapping[i] < 0) continue; cb_get_xyz(_subgraph, i, pos); points.push(pos); cb_get_xyz(_supergraph, _mapping[i], pos); goals.push(pos); } if (points.size() < 1) return true; float sqsum; if (!matr.bestFit(points.size(), points.ptr(), goals.ptr(), &sqsum)) return false; if (sqsum > rms_threshold * rms_threshold) return false; return true; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_constrained_bmatching_finder.cpp��������������������������������0000664�0000000�0000000�00000014731�12710376503�0025640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_constrained_bmatching_finder.h" #include "graph/skew_symmetric_flow_finder.h" using namespace indigo; IMPL_ERROR(GraphConstrainedBMatchingFinder, "b-matching finder"); CP_DEF(GraphConstrainedBMatchingFinder); GraphConstrainedBMatchingFinder::GraphConstrainedBMatchingFinder ( const Graph &g, const ObjArray< Array<int> > &nodes_per_set, const Array<int> *per_set_set_id) : _g(g), CP_INIT, TL_CP_GET(_network), TL_CP_GET(_edges_graph_to_net), TL_CP_GET(_vertices_graph_to_net), TL_CP_GET(_vertices_capacity_arc_per_set), TL_CP_GET(_constraint_sets), TL_CP_GET(_edge_matching_multiplicity), TL_CP_GET(_node_incident_edges_count) { _edges_graph_to_net.clear_resize(g.edgeEnd()); _vertices_graph_to_net.clear_resize(g.vertexEnd()); _edge_matching_multiplicity.clear_resize(g.edgeEnd()); _node_incident_edges_count.clear_resize(g.vertexEnd()); int n_sets = nodes_per_set.size(); _constraint_sets.resize(n_sets); _constraint_sets.fffill(); _vertices_capacity_arc_per_set.resize(n_sets); for (int i = 0; i < n_sets; i++) { _vertices_capacity_arc_per_set[i].resize(g.vertexEnd()); _vertices_capacity_arc_per_set[i].fffill(); } _network.clear(); int source = _network.addVertex(); _network.setSource(source); int after_source = _network.addVertex(); _source_edge = _network.addArc(source, after_source, 0); _createVertices(); _createSets(n_sets, after_source, per_set_set_id); _connectVerticesWithSets(nodes_per_set); _createEdges(); } void GraphConstrainedBMatchingFinder::_createSet (int idx, int root, const Array<int> *per_set_set_id) { if (_constraint_sets[idx].node != -1) return; int node = _network.addVertex(); int parent_node = root; if (per_set_set_id != NULL) { int parent_set = per_set_set_id->at(idx); if (parent_set != -1) { _createSet(parent_set, root, per_set_set_id); parent_node = _constraint_sets[parent_set].node; } } _constraint_sets[idx].node = node; _constraint_sets[idx].in_arc = _network.addArc(parent_node, node, 0); } void GraphConstrainedBMatchingFinder::_createSets (int n, int root, const Array<int> *per_set_set_id) { for (int s = 0; s < n; s++) _createSet(s, root, per_set_set_id); } void GraphConstrainedBMatchingFinder::_createVertices () { for (int v = _g.vertexBegin(); v != _g.vertexEnd(); v = _g.vertexNext(v)) { int net_vertex = _network.addVertex(); _vertices_graph_to_net[v] = net_vertex; } } void GraphConstrainedBMatchingFinder::_connectVerticesWithSets ( const ObjArray< Array<int> > &nodes_per_set) { for (int s = 0; s < nodes_per_set.size(); s++) { int cs_root = _constraint_sets[s].node; const Array<int> &nodes = nodes_per_set[s]; for (int i = 0; i < nodes.size(); i++) { int vertex = nodes[i]; int node = _vertices_graph_to_net[vertex]; int in_arc = _network.addArc(cs_root, node, 0); _vertices_capacity_arc_per_set[s][vertex] = in_arc; } } } void GraphConstrainedBMatchingFinder::_createEdges () { for (int e = _g.edgeBegin(); e != _g.edgeEnd(); e = _g.edgeNext(e)) { const Edge &edge = _g.getEdge(e); int v1 = _vertices_graph_to_net[edge.beg]; int v2 = _vertices_graph_to_net[edge.end]; int v2_sym = _network.getSymmetricVertex(v2); _edges_graph_to_net[e] = _network.addArc(v1, v2_sym, 1); } } void GraphConstrainedBMatchingFinder::setNodeCapacity (int node, int capacity, int set_id) { int net_arc = _vertices_capacity_arc_per_set[set_id][node]; if (net_arc == -1) throw Error("node has no arc to the specified set"); _network.setArcCapacity(net_arc, capacity); } int GraphConstrainedBMatchingFinder::getNodeCapacity (int node, int set_id) const { int net_arc = _vertices_capacity_arc_per_set[set_id][node]; if (net_arc == -1) throw Error("node has no arc to the specified set"); return _network.getArcCapacity(net_arc); } void GraphConstrainedBMatchingFinder::setNodeSetCapacity (int set_id, int capacity) { ConstraintSet &cs = _constraint_sets[set_id]; _network.setArcCapacity(cs.in_arc, capacity); } int GraphConstrainedBMatchingFinder::getNodeSetCapacity (int set_id) const { const ConstraintSet &cs = _constraint_sets[set_id]; return _network.getArcCapacity(cs.in_arc); } void GraphConstrainedBMatchingFinder::setMaxEdgeMultiplicity (int edge, int capacity) { int net_arc = _edges_graph_to_net[edge]; _network.setArcCapacity(net_arc, capacity); } int GraphConstrainedBMatchingFinder::getMaxEdgeMultiplicity (int edge) const { int net_arc = _edges_graph_to_net[edge]; return _network.getArcCapacity(net_arc); } bool GraphConstrainedBMatchingFinder::findMatching (int cardinality) { _network.setArcCapacity(_source_edge, 2 * cardinality); SkewSymmetricFlowFinder flow_finder(_network); flow_finder.process(); // Copy matching information _node_incident_edges_count.zerofill(); for (int e = _g.edgeBegin(); e != _g.edgeEnd(); e = _g.edgeNext(e)) { int net_edge = _edges_graph_to_net[e]; int mult = flow_finder.getArcValue(net_edge); _edge_matching_multiplicity[e] = mult; const Edge &edge = _g.getEdge(e); _node_incident_edges_count[edge.beg] += mult; _node_incident_edges_count[edge.end] += mult; } int value = flow_finder.getArcValue(_source_edge); if (value % 2 != 0) throw Error("algorithmic error: flow should be even"); return value / 2 == cardinality; } int GraphConstrainedBMatchingFinder::getEdgeMultiplicity (int edge) const { return _edge_matching_multiplicity[edge]; } int GraphConstrainedBMatchingFinder::getNodeIncidentEdgesCount (int node) const { return _node_incident_edges_count[node]; } ���������������������������������������Indigo-indigo-1.2.3/graph/src/graph_decomposer.cpp��������������������������������������������������0000664�0000000�0000000�00000006517�12710376503�0022127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_decomposer.h" #include "graph/graph.h" #include "graph/filter.h" using namespace indigo; IMPL_ERROR(GraphDecomposer, "Graph decomposer"); CP_DEF(GraphDecomposer); GraphDecomposer::GraphDecomposer (const Graph &graph) : _graph(graph), CP_INIT, TL_CP_GET(_component_ids), TL_CP_GET(_component_vertices_count), TL_CP_GET(_component_edges_count) { _component_vertices_count.clear(); _component_edges_count.clear(); } GraphDecomposer::~GraphDecomposer () { } int GraphDecomposer::decompose (const Filter *filter, const Filter *edge_filter) { if (_graph.vertexCount() < 1) { n_comp = 0; return 0; } QS_DEF(Array<int>, queue); _component_ids.clear_resize(_graph.vertexEnd()); _component_ids.fffill(); queue.clear_resize(_graph.vertexEnd()); n_comp = 0; int vertex_idx; while (1) { for (vertex_idx = _graph.vertexBegin(); vertex_idx != _graph.vertexEnd(); vertex_idx = _graph.vertexNext(vertex_idx)) { if (filter != 0 && !filter->valid(vertex_idx)) continue; if (_component_ids[vertex_idx] == -1) break; } if (vertex_idx == _graph.vertexEnd()) break; // BFS int top = 1, bottom = 0; queue[0] = vertex_idx; while (top != bottom) { const Vertex &vertex = _graph.getVertex(queue[bottom]); _component_vertices_count.expandFill(n_comp + 1, 0); _component_edges_count.expandFill(n_comp + 1, 0); _component_ids[queue[bottom]] = n_comp; _component_vertices_count[n_comp]++; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int other = vertex.neiVertex(i); if (filter != 0 && !filter->valid(other)) continue; if (edge_filter != 0 && !edge_filter->valid(vertex.neiEdge(i))) continue; if (_component_ids[other] == -1) { queue[top++] = other; _component_ids[other] = -2; } if (_component_ids[other] == -2) _component_edges_count[n_comp]++; } bottom++; } n_comp++; } return n_comp; } const Array<int> & GraphDecomposer::getDecomposition () const { return _component_ids; } int GraphDecomposer::getComponent (int vertex) const { return _component_ids[vertex]; } int GraphDecomposer::getComponentsCount () const { return n_comp; } int GraphDecomposer::getComponentVerticesCount (int component) const { return _component_vertices_count[component]; } int GraphDecomposer::getComponentEdgesCount (int component) const { return _component_edges_count[component]; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_fast_access.cpp�������������������������������������������������0000664�0000000�0000000�00000006655�12710376503�0022250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_fast_access.h" #include "graph/graph.h" using namespace indigo; void GraphFastAccess::setGraph (Graph &g) { _g = &g; _vertices.clear(); _vertices_nei.resize(g.vertexEnd()); _vertices_nei.fffill(); _nei_vertices_data.clear(); _nei_edges_data.clear(); } int* GraphFastAccess::prepareVertices (int &count) { count = _vertices.size(); if (count != 0) return _vertices.ptr(); for (int v = _g->vertexBegin(); v != _g->vertexEnd(); v = _g->vertexNext(v)) _vertices.push(v); count = _vertices.size(); return _vertices.ptr(); } int GraphFastAccess::getVertex (int idx) { return _vertices.ptr()[idx]; } int GraphFastAccess::vertexCount () { return _vertices.size(); } // Prepare both nei vertices and edges list. Runs faster then // preparing them one by one. void GraphFastAccess::prepareVertexNeiVerticesAndEdges (int v) { if (_vertices_nei[v].v_begin != -1 && _vertices_nei[v].e_begin != -1) return; _vertices_nei[v].v_begin = _nei_vertices_data.size(); _vertices_nei[v].e_begin = _nei_edges_data.size(); const Vertex &vertex = _g->getVertex(v); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { _nei_vertices_data.push(vertex.neiVertex(i)); _nei_edges_data.push(vertex.neiEdge(i)); } _vertices_nei[v].v_count = _nei_vertices_data.size() - _vertices_nei[v].v_begin; _vertices_nei[v].e_count = _nei_edges_data.size() - _vertices_nei[v].e_begin; } // Returns nei vertices and nei edges for specified vertex // Numeration is coherent int* GraphFastAccess::getVertexNeiVertices (int v, int &count) { int offset = prepareVertexNeiVertices(v, count); return _nei_vertices_data.ptr() + offset; } int* GraphFastAccess::getVertexNeiEdges (int v, int &count) { if (_vertices_nei[v].e_begin == -1) prepareVertexNeiVerticesAndEdges(v); count = _vertices_nei[v].e_count; return _nei_edges_data.ptr() + _vertices_nei[v].e_begin; } int GraphFastAccess::findEdgeIndex (int v1, int v2) { int count; int *vertices = getVertexNeiVertices(v1, count); int *edges = getVertexNeiEdges(v1, count); for (int i = 0; i < count; i++) if (vertices[i] == v2) return edges[i]; return -1; } void GraphFastAccess::prepareEdges () { _edges.clear_resize(_g->edgeEnd()); for (int e = _g->edgeBegin(); e != _g->edgeEnd(); e = _g->edgeNext(e)) _edges[e] = _g->getEdge(e); } const Edge& GraphFastAccess::getEdge (int e) { return _edges[e]; } const Edge* GraphFastAccess::getEdges () { return _edges.ptr(); } int GraphFastAccess::prepareVertexNeiVertices (int v, int &count) { getVertexNeiEdges(v, count); return _vertices_nei.ptr()[v].v_begin; } int GraphFastAccess::getVertexNeiVertiex (int v_id, int index) { return _nei_vertices_data.ptr()[v_id + index]; } �����������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_iterators.cpp���������������������������������������������������0000664�0000000�0000000�00000003073�12710376503�0021775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "graph/graph_iterators.h" #include "graph/graph.h" using namespace indigo; AutoIterator::AutoIterator( int idx ) : _idx(idx) { } int AutoIterator::operator*() const { return _idx; } bool AutoIterator::operator!=( const AutoIterator &other ) const { if (_idx != other._idx) return true; return false; } VertexIter::VertexIter( Graph &owner, int idx ) : _owner(owner), AutoIterator(idx) { } VertexIter & VertexIter::operator++() { _idx = _owner.vertexNext(_idx); return *this; } VerticesAuto::VerticesAuto (Graph &owner) : _owner(owner) { } VertexIter VerticesAuto::begin () { return VertexIter(_owner, _owner.vertexBegin()); } VertexIter VerticesAuto::end () { return VertexIter(_owner, _owner.vertexEnd()); } EdgeIter::EdgeIter( Graph &owner, int idx ) : _owner(owner), AutoIterator(idx) { } EdgeIter & EdgeIter::operator++() { _idx = _owner.edgeNext(_idx); return *this; } EdgesAuto::EdgesAuto (Graph &owner) : _owner(owner) { } EdgeIter EdgesAuto::begin () { return EdgeIter(_owner, _owner.edgeBegin()); } EdgeIter EdgesAuto::end () { return EdgeIter(_owner, _owner.edgeEnd()); } NeighborIter::NeighborIter(const Vertex &owner, int idx) : _owner(owner), AutoIterator(idx) { } NeighborIter & NeighborIter::operator++ () { _idx = _owner.neiNext(_idx); return *this; } NeighborsAuto::NeighborsAuto (const Vertex &owner) : _owner(owner) { } NeighborIter NeighborsAuto::begin () { return NeighborIter(_owner, _owner.neiBegin()); } NeighborIter NeighborsAuto::end () { return NeighborIter(_owner, _owner.neiEnd()); }���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_perfect_matching.cpp��������������������������������������������0000664�0000000�0000000�00000022126�12710376503�0023263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // Solve graph perfect matching problem module: // To find maximum independent edge set // Algorithm is based on alternating path algorithm // #include "graph/graph_perfect_matching.h" #include "graph/graph.h" #include "base_c/bitarray.h" using namespace indigo; IMPL_ERROR(GraphPerfectMatching, "graph perfect matching"); CP_DEF(GraphPerfectMatching); GraphPerfectMatching::GraphPerfectMatching (const Graph &graph, int params) : _graph(graph), CP_INIT, TL_CP_GET(_matchingEdgesLocal), TL_CP_GET(_verticesInfo), TL_CP_GET(_path), TL_CP_GET(_edgesMappingLocal), TL_CP_GET(_verticesUsedLocal) { if (params & USE_EXTERNAL_EDGES_PTR) _matchingEdges = NULL; else { _matchingEdgesLocal.resize(bitGetSize(_graph.edgeEnd())); _matchingEdgesLocal.zerofill(); _matchingEdges = _matchingEdgesLocal.ptr(); } if (params & USE_EDGES_MAPPING) _edgesMapping = NULL; else { _edgesMappingLocal.resize(_graph.edgeEnd()); for (int i = 0; i < _edgesMappingLocal.size(); i++) _edgesMappingLocal[i] = i; _edgesMapping = _edgesMappingLocal.ptr(); } if (params & USE_VERTICES_SET) { _verticesUsed = NULL; _verticesUsedCount = 0; } else { _verticesUsedLocal.resize(_graph.vertexEnd()); for (int v_idx = _graph.vertexBegin(); v_idx < _graph.vertexEnd(); v_idx = _graph.vertexNext(v_idx)) _verticesUsedLocal.push(v_idx); _verticesUsed = _verticesUsedLocal.ptr(); } _verticesInfo.resize(_graph.vertexEnd()); _verticesInfo.zerofill(); _pathFinderUsedMark = 1; _path.clear(); _leftExposedVertices = 0; } GraphPerfectMatching::~GraphPerfectMatching () { } void GraphPerfectMatching::reset (void) { _verticesInfo.zerofill(); if (_matchingEdges == _matchingEdgesLocal.ptr()) _matchingEdgesLocal.zerofill(); else throw Error("reset: internal error"); } void GraphPerfectMatching::setEdgeMatching (int edge_idx, bool matching) { const Edge &edge = _graph.getEdge(edge_idx); if (matching) { if (_verticesInfo[edge.beg].isInMatching || _verticesInfo[edge.end].isInMatching) throw Error("setEdgeMatching: internal error"); _verticesInfo[edge.beg].isInMatching = _verticesInfo[edge.end].isInMatching = true; bitSetBit(_matchingEdges, _edgesMapping[edge_idx], 1); _leftExposedVertices -= 2; } else { if (!_verticesInfo[edge.beg].isInMatching || !_verticesInfo[edge.end].isInMatching) throw Error("setEdgeMatching: internal error"); _verticesInfo[edge.beg].isInMatching = _verticesInfo[edge.end].isInMatching = false; bitSetBit(_matchingEdges, _edgesMapping[edge_idx], 0); _leftExposedVertices += 2; } } void GraphPerfectMatching::setAllVerticesInMatching (void) { for (int v_idx = _graph.vertexBegin(); v_idx < _graph.vertexEnd(); v_idx = _graph.vertexNext(v_idx)) _verticesInfo[v_idx].isInMatching = true; } void GraphPerfectMatching::setMatchingEdgesPtr (byte *matchingEdges) { _matchingEdges = matchingEdges; } void GraphPerfectMatching::setEdgesMappingPtr (int *edgesMap) { _edgesMapping = edgesMap; } void GraphPerfectMatching::setVerticesSetPtr (int *verticesSet, int count) { _verticesUsed = verticesSet; _verticesUsedCount = count; } int GraphPerfectMatching::isEdgeMatching (int edge_idx) { return bitGetBit(_matchingEdges, _edgesMapping[edge_idx]); } const byte* GraphPerfectMatching::getEdgesState (void) { return _matchingEdges; } int GraphPerfectMatching::isVertexInMatching (int v_idx) { return _verticesInfo[v_idx].isInMatching; } void GraphPerfectMatching::removeVertexFromMatching (int v_idx) { const Vertex& vertex = _graph.getVertex(v_idx); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int e_idx = vertex.neiEdge(j); int vn_idx = vertex.neiVertex(j); if (_edgesMapping[e_idx] == -1) continue; if (!checkEdge(e_idx) || !bitGetBit(_matchingEdges, _edgesMapping[e_idx]) || !checkVertex(vn_idx)) continue; _verticesInfo[vn_idx].isInMatching = false; _verticesInfo[v_idx].isInMatching = false; bitSetBit(_matchingEdges, _edgesMapping[e_idx], 0); break; } } bool GraphPerfectMatching::findMatching (void) { _leftExposedVertices = 0; for (int i = 0; i < _verticesUsedCount; i++) { int v_idx = _verticesUsed[i]; if (checkVertex(v_idx) && !_verticesInfo[v_idx].isInMatching) _leftExposedVertices++; } if (_leftExposedVertices % 2 == 1) return false; while (_leftExposedVertices > 0 && findAlternatingPath()) processPath(); return _leftExposedVertices == 0; } bool GraphPerfectMatching::findAlternatingPath (void) { _pathFinderState = FIND_ANY_ALTERNATING_PATH; for (int i = 0; i < _verticesUsedCount; i++) { int v_idx = _verticesUsed[i]; if (!_verticesInfo[v_idx].isInMatching && checkVertex(v_idx)) { _path.clear(); _path.push(v_idx); _verticesInfo[v_idx].inPathMark = _pathFinderUsedMark; if (_PathFinder(v_idx, false)) { _pathFinderUsedMark++; return true; } _verticesInfo[v_idx].inPathMark = -1; } } _pathFinderUsedMark++; return false; } bool GraphPerfectMatching::findAlternatingPath (int v1, int v2, bool isFirstEdgeMatching, bool isLastEdgeMatching) { _pathFinderState = FIND_ALTERNATING_CIRCLE; _pathFinderStopVertex = v2; _pathFinderIsLastMatching = isLastEdgeMatching; _path.clear(); _path.push(v1); _verticesInfo[v1].inPathMark = _pathFinderUsedMark; bool ret = _PathFinder(v1, isFirstEdgeMatching); _pathFinderUsedMark++; return ret; } // Remap edges in path void GraphPerfectMatching::processPath (void) { int cur_idx = _path[0]; const Vertex &vertex = _graph.getVertex(cur_idx); int edge_idx = vertex.neiEdge(_path[1]); int matchingState = bitGetBit(_matchingEdges, _edgesMapping[edge_idx]); if (_verticesInfo[cur_idx].isInMatching == !matchingState) throw Error("processPath: invalid alternating path"); _verticesInfo[cur_idx].isInMatching = !matchingState; _leftExposedVertices += matchingState ? 1 : -1; for (int i = 1; i < _path.size(); i++) { const Vertex &vertex = _graph.getVertex(cur_idx); int next_idx = vertex.neiVertex(_path[i]); int edge_idx = vertex.neiEdge(_path[i]); if (_edgesMapping[edge_idx] == -1) continue; if (!matchingState ^ bitGetBit(_matchingEdges, _edgesMapping[edge_idx])) bitSetBit(_matchingEdges, _edgesMapping[edge_idx], !matchingState); else throw Error("processPath: invalid alternating path"); matchingState = !matchingState; cur_idx = next_idx; } if (_verticesInfo[cur_idx].isInMatching == matchingState) throw Error("processPath: invalid alternating path"); _verticesInfo[cur_idx].isInMatching = matchingState; _leftExposedVertices -= matchingState ? 1 : -1; } int* GraphPerfectMatching::getPath (void) { return _path.ptr(); } int GraphPerfectMatching::getPathSize (void) { return _path.size(); } void GraphPerfectMatching::setPath (int *path, int length) { _path.resize(length); memcpy(_path.ptr(), path, length * sizeof(int)); } bool GraphPerfectMatching::_PathFinder (int v_idx, int needMatchingEdge) { const Vertex &vertex = _graph.getVertex(v_idx); for (int i = vertex.neiBegin(); i < vertex.neiEnd(); i = vertex.neiNext(i)) { int e_idx = vertex.neiEdge(i); int vn_idx = vertex.neiVertex(i); if (_edgesMapping[e_idx] == -1) continue; if (_verticesInfo[vn_idx].inPathMark == _pathFinderUsedMark) continue; if (!checkVertex(vn_idx) || !checkEdge(e_idx)) continue; int isInMatching = bitGetBit(_matchingEdges, _edgesMapping[e_idx]); if ((needMatchingEdge ^ isInMatching)) continue; _path.push(i); _verticesInfo[vn_idx].inPathMark = _pathFinderUsedMark; // Check exit condition if (_pathFinderState == FIND_ANY_ALTERNATING_PATH) { if (!needMatchingEdge && !_verticesInfo[vn_idx].isInMatching) return true; } else { if (vn_idx == _pathFinderStopVertex && needMatchingEdge == _pathFinderIsLastMatching) return true; } if (_PathFinder(vn_idx, !needMatchingEdge)) { return true; } _verticesInfo[vn_idx].inPathMark = -1; _path.pop(); } return false; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_subchain_enumerator.cpp�����������������������������������������0000664�0000000�0000000�00000005351�12710376503�0024017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_subchain_enumerator.h" using namespace indigo; CP_DEF(GraphSubchainEnumerator); GraphSubchainEnumerator::GraphSubchainEnumerator (Graph &graph, int min_edges, int max_edges, int mode) : _graph(graph), _max_edges(max_edges), _min_edges(min_edges), _mode(mode), CP_INIT, TL_CP_GET(_vertex_states), TL_CP_GET(_chain_vertices), TL_CP_GET(_chain_edges) { context = 0; cb_handle_chain = 0; if (_mode == MODE_NO_DUPLICATE_VERTICES) { _vertex_states.clear_resize(_graph.vertexEnd()); _vertex_states.zerofill(); } _chain_vertices.clear(); _chain_edges.clear(); } GraphSubchainEnumerator::~GraphSubchainEnumerator () { } void GraphSubchainEnumerator::processChains () { int i; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { _chain_vertices.push(i); if (_mode == MODE_NO_DUPLICATE_VERTICES) _vertex_states[i] = 1; _DFS(i); if (_mode == MODE_NO_DUPLICATE_VERTICES) _vertex_states[i] = 0; _chain_vertices.pop(); } } void GraphSubchainEnumerator::_DFS (int from) { int i; const Vertex &v = _graph.getVertex(from); for (i = v.neiBegin(); i != v.neiEnd(); i = v.neiNext(i)) { int nei_idx = v.neiVertex(i); int edge_idx = v.neiEdge(i); if (_mode == MODE_NO_DUPLICATE_VERTICES) { if (_vertex_states[nei_idx] == 1) continue; _vertex_states[nei_idx] = 1; } else if (_mode == MODE_NO_BACKTURNS) { if (_chain_edges.size() > 0 && _chain_edges.top() == edge_idx) continue; } _chain_vertices.push(nei_idx); _chain_edges.push(edge_idx); if (_chain_edges.size() >= _min_edges && _chain_edges.size() <= _max_edges) if (cb_handle_chain != 0) cb_handle_chain(_graph, _chain_edges.size(), _chain_vertices.ptr(), _chain_edges.ptr(), context); if (_chain_edges.size() < _max_edges) _DFS(nei_idx); _chain_edges.pop(); _chain_vertices.pop(); if (_mode == MODE_NO_DUPLICATE_VERTICES) _vertex_states[nei_idx] = 0; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/graph_subtree_enumerator.cpp������������������������������������������0000664�0000000�0000000�00000012252�12710376503�0023672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph_subtree_enumerator.h" #include "graph/graph_fast_access.h" using namespace indigo; CP_DEF(GraphSubtreeEnumerator); GraphSubtreeEnumerator::GraphSubtreeEnumerator (Graph &graph) : _graph(graph), CP_INIT, TL_CP_GET(_front), TL_CP_GET(_vertices), TL_CP_GET(_edges), TL_CP_GET(_v_processed) { min_vertices = 1; max_vertices = graph.vertexCount(); callback = 0; context = 0; handle_maximal = false; maximal_critera_value_callback = 0; vfilter = 0; } GraphSubtreeEnumerator::~GraphSubtreeEnumerator () { } void GraphSubtreeEnumerator::process () { _edges.clear(); _vertices.clear(); _v_processed.clear_resize(_graph.vertexEnd()); _v_processed.zerofill(); _front.clear_resize(1); _m1.e = _m2.e = -1; _m1.v = _m2.v = -1; if (vfilter != 0) for (int i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (!vfilter->valid(i)) _v_processed[i] = 1; } for (int i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (_v_processed[i] == 1) continue; _vertices.push(i); _v_processed[i] = 1; int cur_maximal_criteria_value = 0; if (handle_maximal && maximal_critera_value_callback != 0) cur_maximal_criteria_value = maximal_critera_value_callback(_graph, _vertices, _edges, context); _front[0].reset(); _front[0].v = i; _reverseSearch(0, cur_maximal_criteria_value); _v_processed[i] = 0; _vertices.pop(); } } void GraphSubtreeEnumerator::_reverseSearch (int front_idx, int cur_maximal_criteria_value) { bool maximal_by_criteria = true; bool has_supergraph = false; int nvertices = _vertices.size(); if (nvertices < max_vertices) { // Save front state int front_size = _front.size(); VertexEdgeParent front_prev_value = _front[front_idx]; _front[front_idx].reset(); // Update front int v = front_prev_value.v; const Vertex &vertex = _graph.getVertex(v); int i = vertex.neiBegin(); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int nei_v = vertex.neiVertex(i); if (_v_processed[nei_v] == 1) continue; VertexEdgeParent &added = _front.push(); added.v = nei_v; added.e = vertex.neiEdge(i); added.parent = v; } // Check if we can reuse front_idx front index int new_front_size = _front.size(); if (front_size < new_front_size) { _front[front_idx] = _front.top(); _front.pop(); new_front_size--; } VertexEdge m1_prev = _m1, m2_prev = _m2; for (int i = 0; i < new_front_size; i++) { VertexEdgeParent cur = _front[i]; if (cur.v == -1 || _v_processed[cur.v] == 1) continue; if (nvertices == 1 && cur.v < v) continue; // Check if edge/vertex follows fCIS maximality rule in the reverse search if (cur.parent == _m1.v) { if (cur.e < _m2.e) continue; _m1.v = cur.v; _m1.e = cur.e; } else { if (cur.e < _m1.e) continue; _m2 = _m1; _m1.v = cur.v; _m1.e = cur.e; } if (nvertices == 1) { _m2.e = _m1.e; _m2.v = v; } // Add this edge _vertices.push(cur.v); _v_processed[cur.v] = 1; _edges.push(cur.e); int descedant_maximal_criteria_value = 0; if (handle_maximal && maximal_critera_value_callback != 0) descedant_maximal_criteria_value = maximal_critera_value_callback(_graph, _vertices, _edges, context); if (descedant_maximal_criteria_value == cur_maximal_criteria_value) maximal_by_criteria = false; _reverseSearch(i, descedant_maximal_criteria_value); has_supergraph = true; _m1 = m1_prev; _m2 = m2_prev; _edges.pop(); _v_processed[cur.v] = 0; _vertices.pop(); } // Restore front _front.resize(front_size); _front[front_idx] = front_prev_value; } if (nvertices >= min_vertices && nvertices <= max_vertices && callback != 0) { if (handle_maximal) { if (has_supergraph && !maximal_by_criteria) return; // This subgraph isn't maximal } if (callback) callback(_graph, _vertices, _edges, context); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/max_common_subgraph.cpp�����������������������������������������������0000664�0000000�0000000�00000157402�12710376503�0022636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/cancellation_handler.h" #include "graph/max_common_subgraph.h" #include "time.h" using namespace indigo; IMPL_ERROR(MaxCommonSubgraph, "MCS"); MaxCommonSubgraph::MaxCommonSubgraph(Graph &subgraph, Graph &supergraph) : conditionEdgeWeight(0), conditionVerticesColor(0), cbSolutionTerm(0), userdata(0), cbEmbedding(0), embeddingUserdata(0), _subgraph(&subgraph), _supergraph(&supergraph){ parametersForExact.isStopped = false; parametersForExact.maxIteration = -1; parametersForExact.numberOfSolutions = 0; parametersForApproximate.error = 0; parametersForApproximate.maxIteration = 1000; parametersForApproximate.numberOfSolutions = 0; parametersForApproximate.standardRandom = false; parametersForApproximate.randomize = false; } MaxCommonSubgraph::~MaxCommonSubgraph () { } void MaxCommonSubgraph::setGraphs(Graph& subgraph, Graph& supergraph) { _subgraph = &subgraph; _supergraph = &supergraph; } bool MaxCommonSubgraph::_findTrivialMcs() { _clearSolutionMaps(); parametersForExact.numberOfSolutions = 0; if(_subgraph->vertexCount() == 0 && _supergraph->vertexCount() == 0) return true; if(_subgraph->vertexCount() > 1 && _supergraph->vertexCount() > 1) return false; QS_DEF(Array<int>, v_map); QS_DEF(Array<int>, e_map); v_map.resize(_subgraph->vertexEnd()); for (int i = 0; i < v_map.size(); ++i) v_map[i] = -1; e_map.clear(); if(_subgraph->vertexCount() == 1) { Graph& q_graph = *_subgraph; Graph& t_graph = *_supergraph; int v_q = q_graph.vertexBegin(); int v_t = t_graph.vertexBegin(); if(conditionVerticesColor == 0) { ++parametersForExact.numberOfSolutions; v_map[v_q] = v_t; _addSolutionMap(v_map, e_map); return true; } for (; v_t != t_graph.vertexEnd(); v_t = t_graph.vertexNext(v_t)) { if(conditionVerticesColor(q_graph, t_graph, 0, v_q, v_t, userdata)) { ++parametersForExact.numberOfSolutions; v_map[v_q] = v_t; _addSolutionMap(v_map, e_map); return true; } } } else if(_supergraph->vertexCount() == 1) { Graph& q_graph = *_supergraph; Graph& t_graph = *_subgraph; int v_q = q_graph.vertexBegin(); int v_t = t_graph.vertexBegin(); if(conditionVerticesColor == 0) { ++parametersForExact.numberOfSolutions; v_map[v_t] = v_q; _addSolutionMap(v_map, e_map); return true; } for (; v_t != t_graph.vertexEnd(); v_t = t_graph.vertexNext(v_t)) { if(conditionVerticesColor(q_graph, t_graph, 0, v_q, v_t, userdata)) { ++parametersForExact.numberOfSolutions; v_map[v_t] = v_q; _addSolutionMap(v_map, e_map); return true; } } } return true; } void MaxCommonSubgraph::_clearSolutionMaps() { _vertEdgeSolMap.clear(); } void MaxCommonSubgraph::_addSolutionMap(Array<int>& v_map, Array<int>& e_map) { int v_size = v_map.size(); int e_size = e_map.size(); _vertEdgeSolMap.push().resize(v_size + e_size + 2); Array<int>& ve_map = _vertEdgeSolMap.top(); for (int i = 0; i < ve_map.size(); ++i) ve_map[i] = -1; ve_map[0] = v_size; ve_map[1] = e_size; for (int i = 0; i < v_size; ++i) { ve_map[2 + i] = v_map[i]; } for (int i = 0; i < e_size; ++i) { ve_map[2 + i + v_size] = e_map[i]; } } void MaxCommonSubgraph::findExactMCS(){ /* * Check for single input molecules */ if(_findTrivialMcs()) return; ReGraph regraph; regraph.setMaxIteration(parametersForExact.maxIteration); ReCreation rc(regraph, *this); rc.createRegraph(); bool find_all_str = rc.setMapping(); regraph.cbEmbedding = cbEmbedding; regraph.userdata = embeddingUserdata; regraph.parse(find_all_str); parametersForExact.isStopped = regraph.stopped(); parametersForExact.numberOfSolutions = rc.createSolutionMaps(); } void MaxCommonSubgraph::findApproximateMCS(){ int max_vsize = __max(_subgraph->vertexEnd(),_supergraph->vertexEnd()); int max_esize = __max(_subgraph->edgeEnd(),_supergraph->edgeEnd()); int max_asize = __max(max_vsize, max_esize); AdjMatricesStore ams(*this, max_asize); ams.create(*_subgraph, *_supergraph); Greedy gr(ams); gr.greedyMethod(); RandomDisDec rdd(ams); rdd.setIterationNumber(parametersForApproximate.maxIteration); rdd.refinementStage(); parametersForApproximate.error = rdd.getError(); parametersForApproximate.numberOfSolutions = ams.createSolutionMaps(); } bool MaxCommonSubgraph::_getEdgeColorCondition(Graph &graph1, Graph &graph2, int i, int j) const { bool v_color, e_weight; if(conditionVerticesColor != 0) { bool a1 = conditionVerticesColor(graph1, graph2, 0, graph1.getEdge(i).beg, graph2.getEdge(j).beg, userdata); bool b1 = conditionVerticesColor(graph1, graph2, 0, graph1.getEdge(i).end, graph2.getEdge(j).end, userdata); bool a2 = conditionVerticesColor(graph1, graph2, 0, graph1.getEdge(i).beg, graph2.getEdge(j).end, userdata); bool b2 = conditionVerticesColor(graph1, graph2, 0, graph1.getEdge(i).end, graph2.getEdge(j).beg, userdata); v_color = (a1 && b1) || (a2 && b2); } else v_color = true; if(conditionEdgeWeight != 0) e_weight = conditionEdgeWeight(graph1, graph2, i, j, userdata); else e_weight = true; return (e_weight && v_color); } void MaxCommonSubgraph::getSolutionMaps(ObjArray< Array<int> >* v_maps, ObjArray< Array<int> >* e_maps) const { QS_DEF(ObjArray< Array<int> >, tmp_v); QS_DEF(ObjArray< Array<int> >, tmp_e); if(v_maps == 0) v_maps = &tmp_v; if(e_maps == 0) e_maps = &tmp_e; _getSolutionMaps(_vertEdgeSolMap.size(), *v_maps, *e_maps); } void MaxCommonSubgraph::getMaxSolutionMap(Array<int>* v_map, Array<int>* e_map) const { QS_DEF(ObjArray< Array<int> >, tmp_v); QS_DEF(ObjArray< Array<int> >, tmp_e); if(v_map) v_map->clear(); if(e_map) e_map->clear(); _getSolutionMaps(1, tmp_v, tmp_e); if(tmp_v.size() > 0) { if(v_map) v_map->copy(tmp_v[0]); if(e_map) e_map->copy(tmp_e[0]); } } //Exact method: Hanser's algorithm //------------------------------------------------------------------------------------------------------------------ MaxCommonSubgraph::ReCreation::ReCreation(ReGraph &rgr, MaxCommonSubgraph& context): _regraph(rgr), _context(context) { } void MaxCommonSubgraph::ReCreation::createRegraph(){ _regraph.clear(); if (_regraph.cancellation_handler != 0) { if (_regraph.cancellation_handler->isCancelled()) throw Error("mcs search was cancelled: %s", _regraph.cancellation_handler->cancelledRequestMessage()); } _nodeConstructor(); _edgesConstructor(); } void MaxCommonSubgraph::ReCreation::_nodeConstructor() { Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; for (int i =sub_graph.edgeBegin(); i <sub_graph.edgeEnd(); i =sub_graph.edgeNext(i)) { for (int j = super_graph.edgeBegin(); j < super_graph.edgeEnd(); j = super_graph.edgeNext(j)) { if (_context._getEdgeColorCondition(sub_graph, super_graph, i, j)) _regraph.addPoint(i, j); } } } void MaxCommonSubgraph::ReCreation::_edgesConstructor(){ Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; _regraph.setSizes(sub_graph.edgeEnd(),super_graph.edgeEnd()); int regraph_size = _regraph.size(); for (int i = 0; i < regraph_size; i++) { _regraph.getPoint(i)->setSizes(regraph_size,sub_graph.edgeEnd(), super_graph.edgeEnd()); _regraph.getPoint(i)->forbidden.set(i); } for (int i = 0; i < regraph_size; i++) { for (int j = i + 1; j < regraph_size; j++) { int a1 = _regraph.getPoint(i)->getid1(); int a2 = _regraph.getPoint(i)->getid2(); int b1 = _regraph.getPoint(j)->getid1(); int b2 = _regraph.getPoint(j)->getid2(); if ((a1 == b1)||(a2 == b2) || !_hasCommonSymbol(a1, b1, a2, b2)) { _regraph.getPoint(i)->forbidden.set(j); _regraph.getPoint(j)->forbidden.set(i); } else if (_hasCommonVertex(a1, b1, sub_graph)) { _regraph.getPoint(i)->extension.set(j); _regraph.getPoint(j)->extension.set(i); } if ((a1 != b1) && (a2 != b2) && _hasCommonSymbol(a1, b1, a2, b2)) { _regraph.getPoint(i)->allowed_g1.set(b1); _regraph.getPoint(i)->allowed_g2.set(b2); _regraph.getPoint(j)->allowed_g1.set(a1); _regraph.getPoint(j)->allowed_g2.set(a2); } } } } bool MaxCommonSubgraph::ReCreation::_hasCommonVertex(int e1, int e2, Graph& graph) const{ return (_getCommonVertex(e1, e2, graph) != -1); } int MaxCommonSubgraph::ReCreation::_getCommonVertex(int e1, int e2, Graph& graph) const{ int result = -1; if(graph.getEdge(e1).beg == graph.getEdge(e2).beg || graph.getEdge(e1).end == graph.getEdge(e2).beg){ result = graph.getEdge(e2).beg; }else if((graph.getEdge(e1).beg == graph.getEdge(e2).end) || (graph.getEdge(e1).end == graph.getEdge(e2).end)){ result = graph.getEdge(e2).end; } return result; } bool MaxCommonSubgraph::ReCreation::_hasCommonSymbol(int e11, int e12, int e21, int e22) const { Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; int s1 = _getCommonVertex(e11, e12, sub_graph); int s2 = _getCommonVertex(e21, e22, super_graph); if (s1 == -1 && s2 == -1) return true; if (s1 != -1 && s2 != -1) return _context.conditionVerticesColor(sub_graph, super_graph, 0, s1, s2, _context.userdata); return false; } void MaxCommonSubgraph::ReCreation::_createList(const Dbitset& proj_bitset, Graph& graph, Array<int> &v_list, Array<int> &e_list) const { int e_num, v_num1, v_num2; RedBlackSet<int> rb_set; v_list.clear(); e_list.clear(); rb_set.clear(); for(int x = proj_bitset.nextSetBit(0); x >= 0; x = proj_bitset.nextSetBit(x+1)) { e_num = x; v_num1 = graph.getEdge(e_num).beg; v_num2 = graph.getEdge(e_num).end; rb_set.find_or_insert(v_num1); rb_set.find_or_insert(v_num2); e_list.push(e_num); } for(int i = rb_set.begin(); i < rb_set.end(); i = rb_set.next(i)) v_list.push(rb_set.key(i)); } void MaxCommonSubgraph::ReCreation::setCorrespondence(const Dbitset& bits, Array<int>& map) const{ Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; map.clear_resize(sub_graph.vertexEnd()); for(int i = 0; i < map.size(); i++) map.at(i) = -1; int a,b,c,d,e,f,x,y; f = 0; for(x = bits.nextSetBit(0);x >= 0;x = bits.nextSetBit(x+1)) ++f; if(f == 1){ x = bits.nextSetBit(0); a = sub_graph.getEdge(_regraph.getPoint(x)->getid1()).beg; b = sub_graph.getEdge(_regraph.getPoint(x)->getid1()).end; c = super_graph.getEdge(_regraph.getPoint(x)->getid2()).beg; d = super_graph.getEdge(_regraph.getPoint(x)->getid2()).end; if(_context.conditionVerticesColor(sub_graph, super_graph, 0, a, c, _context.userdata)){ map.at(a) = c; map.at(b) = d; }else{ map.at(a) = d; map.at(b) = c; } return; } for(x = bits.nextSetBit(0);x >= 0;x = bits.nextSetBit(x+1)){ for(y = bits.nextSetBit(0);y >= 0;y = bits.nextSetBit(y+1)){ if(x!=y){ a = _regraph.getPoint(x)->getid1(); b = _regraph.getPoint(y)->getid1(); e = _getCommonVertex(a, b, sub_graph); if(e != -1){ c = _regraph.getPoint(x)->getid2(); d = _regraph.getPoint(y)->getid2(); f = _getCommonVertex(c, d, super_graph); //first graph if(sub_graph.getEdge(a).beg == e){ a = sub_graph.getEdge(a).end; }else{ a = sub_graph.getEdge(a).beg; } if(sub_graph.getEdge(b).beg == e){ b = sub_graph.getEdge(b).end; }else{ b = sub_graph.getEdge(b).beg; } //second graph if(super_graph.getEdge(c).beg == f){ c = super_graph.getEdge(c).end; }else{ c = super_graph.getEdge(c).beg; } if(super_graph.getEdge(d).beg == f){ d = super_graph.getEdge(d).end; }else{ d = super_graph.getEdge(d).beg; } //set map.at(e) = f; map.at(a) = c; map.at(b) = d; } } } } } bool MaxCommonSubgraph::ReCreation::insertSolution(const Array<int>& mapping){ Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; Dbitset solution(_regraph.size()); Dbitset solution_g1(sub_graph.edgeEnd()); Dbitset solution_g2(super_graph.edgeEnd()); int a,b,c; for(int i = sub_graph.vertexBegin(); i < sub_graph.vertexEnd(); i = sub_graph.vertexNext(i)){ for(int j = sub_graph.vertexBegin(); j < sub_graph.vertexEnd(); j = sub_graph.vertexNext(j)){ if(mapping.at(i) != -1 && mapping.at(j) != -1 && i != j ){ super_graph.getVertex(mapping.at(i)); super_graph.getVertex(mapping.at(j)); a = sub_graph.findEdgeIndex(i, j); b = super_graph.findEdgeIndex(mapping.at(i), mapping.at(j)); if(a != -1 && b != -1){ c = _regraph.getPointIndex(a, b); if(c == -1){ throw Error("input mapping incorrect"); } else { solution.set(c); solution_g1.set(_regraph.getPoint(c)->getid1()); solution_g2.set(_regraph.getPoint(c)->getid2()); } } } } } if(!solution.isEmpty()) { _regraph.insertSolution(0, true, solution, solution_g1, solution_g2, solution_g1.bitsNumber()); return false; } return true; } bool MaxCommonSubgraph::ReCreation::setMapping(){ if(_context.incomingMap.size() == 0) return true; int vertex_number = 0; int vertex_idx = 0; for (int i = 0; i < _context.incomingMap.size(); ++i) { if(_context.incomingMap[i] >= 0) { ++vertex_number; vertex_idx = i; } } /* * One vertex map workaround * The algorithm works only with edges, therefore, we should create edges mapping (for every neighbor) */ if(vertex_number == 0) { return true; } else if(vertex_number == 1) { QS_DEF(Array<int>, mapping); Graph& sub_graph = *_context._subgraph; Graph& super_graph = *_context._supergraph; const Vertex& vert_sub = sub_graph.getVertex(vertex_idx); const Vertex& vert_super = super_graph.getVertex(_context.incomingMap[vertex_idx]); bool result = true; /* * All possibilities for check */ for (int i = vert_sub.neiBegin(); i != vert_sub.neiEnd(); i = vert_sub.neiNext(i)) { int sub_nei_idx = vert_sub.neiVertex(i); for (int j = vert_super.neiBegin(); j != vert_super.neiEnd(); j = vert_super.neiNext(j)) { int super_nei_idx = vert_super.neiVertex(j); /* * Check vertex condition */ if (_context.conditionVerticesColor && !_context.conditionVerticesColor(sub_graph, super_graph, 0, sub_nei_idx, super_nei_idx, _context.userdata)) { continue; } /* * Check edge condition */ int sub_nei_ed = vert_sub.neiEdge(i); int super_nei_ed = vert_super.neiEdge(j); if(_context.conditionEdgeWeight && !_context.conditionEdgeWeight(sub_graph, super_graph, sub_nei_ed, super_nei_ed, _context.userdata)) { continue; } mapping.copy(_context.incomingMap); mapping.at(sub_nei_idx) = super_nei_idx; result &= insertSolution(mapping); } } return result; } else { return insertSolution(_context.incomingMap); } return true; } int MaxCommonSubgraph::ReCreation::createSolutionMaps() { QS_DEF(Array<int>, v_map); QS_DEF(Array<int>, e_map); Graph& sub_graph = *_context._subgraph; _context._vertEdgeSolMap.clear(); int v_size = sub_graph.vertexEnd(); int e_size = sub_graph.edgeEnd(); for(int sol = _regraph.solBegin(); _regraph.solIsNotEnd(sol); sol = _regraph.solNext(sol)) { const Dbitset& bits = _regraph.getSolBitset(sol); setCorrespondence(bits, v_map); v_map.resize(v_size); e_map.resize(e_size); for(int i = 0; i < e_size; ++i) e_map[i] = -1; for(int x = bits.nextSetBit(0); x >= 0; x = bits.nextSetBit(x+1)) { e_map[_regraph.getPoint(x)->getid1()] = _regraph.getPoint(x)->getid2(); } _context._addSolutionMap(v_map, e_map); } if(_context.cbSolutionTerm == 0) _context._vertEdgeSolMap.qsort(_context.ringsSolutionTerm, 0); else _context._vertEdgeSolMap.qsort(_context.cbSolutionTerm, _context.userdata); return _context._vertEdgeSolMap.size(); } void MaxCommonSubgraph::ReCreation::getSolutionListsSub(ObjArray< Array<int> >& v_lists, ObjArray< Array<int> >& e_lists) const { v_lists.clear(); e_lists.clear(); Graph& sub_graph = *_context._subgraph; for(int x = _regraph.solBegin(); _regraph.solIsNotEnd(x); x = _regraph.solNext(x)) { Array<int>& v_list = v_lists.push(); Array<int>& e_list = e_lists.push(); _createList(_regraph.getProj1Bitset(x), sub_graph, v_list, e_list); } } void MaxCommonSubgraph::ReCreation::getSolutionListsSuper(ObjArray< Array<int> >& v_lists, ObjArray< Array<int> >& e_lists) const{ v_lists.clear(); e_lists.clear(); Graph& super_graph = *_context._supergraph; for(int x = _regraph.solBegin(); _regraph.solIsNotEnd(x); x = _regraph.solNext(x)) { Array<int>& v_list = v_lists.push(); Array<int>& e_list = e_lists.push(); _createList(_regraph.getProj2Bitset(x), super_graph, v_list, e_list); } } //------------------------------------------------------------------------------------------------------------- MaxCommonSubgraph::ReGraph::ReGraph(): cbEmbedding(0), userdata(0), cancellation_handler(0), _nbIteration(0), _maxIteration(-1), _firstGraphSize(0), _secondGraphSize(0), _findAllStructure(true), _stop(false), _solutionObjList(_pool) { cancellation_handler = getCancellationHandler(); } MaxCommonSubgraph::ReGraph::ReGraph(MaxCommonSubgraph& context): cbEmbedding(0), userdata(0), cancellation_handler(0), _nbIteration(0), _maxIteration(-1), _firstGraphSize(0), _secondGraphSize(0), _findAllStructure(true), _stop(false), _solutionObjList(_pool) { setMaxIteration(context.parametersForExact.maxIteration); cancellation_handler = getCancellationHandler(); } void MaxCommonSubgraph::ReGraph::setSizes(int n1, int n2){ _firstGraphSize = n1; _secondGraphSize = n2; } void MaxCommonSubgraph::ReGraph::clear(){ _graph.clear(); _solutionObjList.clear(); } void MaxCommonSubgraph::ReGraph::parse(bool findAllStructure){ _size = _graph.size(); _findAllStructure = findAllStructure; Dbitset pnode_g1(_firstGraphSize); Dbitset pnode_g2(_secondGraphSize); QS_DEF(ObjArray<Dbitset>, traversed); QS_DEF(ObjArray<Dbitset>, extension); QS_DEF(ObjArray<Dbitset>, forbidden); QS_DEF(ObjArray<Dbitset>, traversed_g1); QS_DEF(ObjArray<Dbitset>, traversed_g2); QS_DEF(ObjArray<Dbitset>, allowed_g1); QS_DEF(ObjArray<Dbitset>, allowed_g2); QS_DEF(Array<int>, xk); traversed.clear(); extension.clear(); forbidden.clear(); traversed_g1.clear(); traversed_g2.clear(); allowed_g1.clear(); allowed_g2.clear(); int max_size = __min(_firstGraphSize, _secondGraphSize) + 1; xk.clear_resize(max_size); for(int i = 0; i < max_size; i++) { traversed.push(_size); extension.push(_size); forbidden.push(_size); traversed_g1.push(_firstGraphSize); traversed_g2.push(_secondGraphSize); allowed_g1.push(_firstGraphSize); allowed_g2.push(_secondGraphSize); xk[i] = -1; } extension[0].set(); allowed_g1[0].set(); allowed_g2[0].set(); int level = 0; int next_level = 1, xk_level; while(1) { for(xk[level] = extension[level].nextSetBit(xk[level] + 1);xk[level] >= 0 && ! _stop; xk[level] = extension[level].nextSetBit(xk[level] + 1)) { next_level = level + 1; xk_level = xk[level]; forbidden[next_level].bsOrBs(forbidden[level], _graph.at(xk_level)->forbidden); allowed_g1[next_level].bsAndBs(allowed_g1[level], _graph.at(xk_level)->allowed_g1); allowed_g2[next_level].bsAndBs(allowed_g2[level], _graph.at(xk_level)->allowed_g2); if (traversed[level].isEmpty()) { extension[next_level].bsAndNotBs(_graph.at(xk_level)->extension, forbidden[next_level]); } else { extension[next_level].bsOrBs(extension[level], _graph.at(xk_level)->extension); extension[next_level].andNotWith(forbidden[next_level]); } traversed[next_level].copy(traversed[level]); traversed[next_level].set(xk_level); traversed_g1[next_level].copy(traversed_g1[level]); traversed_g2[next_level].copy(traversed_g2[level]); traversed_g1[next_level].set(_graph.at(xk_level)->getid1()); traversed_g2[next_level].set(_graph.at(xk_level)->getid2()); forbidden[level].set(xk_level); ++level; if (extension[level].isEmpty()) { _solution(traversed[level], traversed_g1[level], traversed_g2[level]); xk[level] = -1; --level; if(level <= -1) break; } else { pnode_g1.bsOrBs(allowed_g1[level], traversed_g1[level]); pnode_g2.bsOrBs(allowed_g2[level], traversed_g2[level]); if (_mustContinue(pnode_g1, pnode_g2)) { ++_nbIteration; if(_maxIteration > -1 && _nbIteration >= _maxIteration) _stop = true; if(_nbIteration % 10 == 0) { if(cancellation_handler != 0) { if(cancellation_handler->isCancelled()) throw Error("mcs search was cancelled: %s", cancellation_handler->cancelledRequestMessage()); } } } else { xk[level] = -1; --level; if(level <= -1) break; } } } --level; if(level <= -1) break; } //printf("iter = %d\n", _nbIteration); //printf("size = %d\n", _solutionObjList.size()); } void MaxCommonSubgraph::ReGraph::insertSolution(int ins_index, bool ins_after, const Dbitset& sol, const Dbitset& sol_g1, const Dbitset& sol_g2, int num_bits) { if(_solutionObjList.size() == 0) { ins_index = _solutionObjList.add(); } else { if(ins_after) { ins_index = _solutionObjList.insertAfter(ins_index); } else { ins_index = _solutionObjList.insertBefore(ins_index); } } _solutionObjList.at(ins_index).reSolution.copy(sol); _solutionObjList.at(ins_index).solutionProj1.copy(sol_g1); _solutionObjList.at(ins_index).solutionProj2.copy(sol_g2); _solutionObjList.at(ins_index).numBits = num_bits; if(cbEmbedding != 0) { QS_DEF(Array<int>, sub_edge_map); sub_edge_map.resize(_firstGraphSize); sub_edge_map.zerofill(); for(int x = sol.nextSetBit(0); x >= 0; x = sol.nextSetBit(x+1)) { sub_edge_map[_graph.at(x)->getid1()] = _graph.at(x)->getid2(); } if(!cbEmbedding(0, sub_edge_map.ptr(), 0, userdata)) _stop = true; } } void MaxCommonSubgraph::ReGraph::_solution(const Dbitset& traversed, Dbitset& trav_g1, Dbitset& trav_g2){ bool included = false; bool str_include = false; bool subset, ins_after = false, first_undel = false; int num_bits = trav_g1.bitsNumber(); int insert_idx = _solutionObjList.begin(); int idx_next; int suu = 0; for(int i = _solutionObjList.begin(); i < _solutionObjList.end() && !included;) { ++suu; Solution& solution = _solutionObjList.at(i); if(num_bits < solution.numBits) { if(trav_g1.isSubsetOf(solution.solutionProj1) || trav_g2.isSubsetOf(solution.solutionProj2)) included = true; insert_idx = i; ins_after = true; } if(num_bits >= solution.numBits) { if(_findAllStructure) subset = (solution.solutionProj1.isSubsetOf(trav_g1) || solution.solutionProj2.isSubsetOf(trav_g2)); else subset = (solution.solutionProj1.isSubsetOf(trav_g1) && solution.solutionProj2.isSubsetOf(trav_g2) && solution.reSolution.isSubsetOf(traversed)); if(subset) { idx_next = _solutionObjList.next(i); _solutionObjList.remove(i); i = idx_next; str_include = true; continue; } if(!ins_after && !first_undel) { insert_idx = i; first_undel = true; } } i = _solutionObjList.next(i); } if (!included) { if(_findAllStructure) { insertSolution(insert_idx, ins_after, traversed, trav_g1, trav_g2, num_bits); } else if(str_include) { insertSolution(insert_idx, ins_after, traversed, trav_g1, trav_g2, num_bits); } } } bool MaxCommonSubgraph::ReGraph::_mustContinue (const Dbitset& pnode_g1, const Dbitset& pnode_g2) const { bool result = true; int num_bits = __min(pnode_g1.bitsNumber(), pnode_g2.bitsNumber()); for(int i = _solutionObjList.begin(); i != _solutionObjList.end(); i = _solutionObjList.next(i)) { Solution& solution = _solutionObjList.at(i); if(solution.numBits >= num_bits) { if(pnode_g1.isSubsetOf(solution.solutionProj1) || pnode_g2.isSubsetOf(solution.solutionProj2)) { result = false; break; } } else { break; } } return result; } int MaxCommonSubgraph::ReGraph::getPointIndex(int i,int j) const { for(int x = 0; x < _graph.size(); x++){ if((_graph.at(x)->getid1() == i && _graph.at(x)->getid2() == j)){ return x; } } return -1; } //------------------------------------------------------------------------------------------------------------------- MaxCommonSubgraph::RePoint::RePoint(int n1, int n2): _id1(n1), _id2(n2) { } void MaxCommonSubgraph::RePoint::setSizes(int size, int size_g1, int size_g2){ extension.resize(size); forbidden.resize(size); allowed_g1.resize(size_g1); allowed_g2.resize(size_g2); } // Approximate algorithm: two stage optimization method (2DOM) //------------------------------------------------------------------------------------------------------------------- //Adjacency matrix MaxCommonSubgraph::AdjMatricesStore::AdjMatricesStore(MaxCommonSubgraph& context,int maxsize):_context(context),_maxsize(maxsize),_swap(false){ for(int i = 0;i < maxsize;i++){ _ajEdge1.add(new Array<int>()); _ajEdge1[i]->resize(maxsize); _ajEdge2.add(new Array<int>()); _ajEdge2[i]->resize(maxsize); _aj2.add(new Array<bool>()); _aj2[i]->resize(maxsize); _errorEdgesMatrix.add(new Array<int>());_errorEdgesMatrix[i]->resize(maxsize); _daj1.add(new Dbitset(maxsize)); _daj2.add(new Dbitset(maxsize)); } _map.resize(maxsize); _invmap.resize(maxsize); _degreeVec1.resize(maxsize); _degreeVec2.resize(maxsize); _x.resize(maxsize); _y.resize(maxsize); _cr1.resize(maxsize); _cr2.resize(maxsize); } void MaxCommonSubgraph::AdjMatricesStore::create(Graph &g1, Graph &g2){ _swap = _checkSize(g1, g2); if (_swap){ _graph1 = &g2; _graph2 = &g1; }else{ _graph1 = &g1; _graph2 = &g2; } _createMaps(); _createCorrespondence(); _createAdjacencyMatrices(); _createLabelMatrices(); _createErrorEdgesMatrix(); } int MaxCommonSubgraph::AdjMatricesStore::createSolutionMaps() { QS_DEF(ObjArray< Array<int> >, v_maps); int sub_beg, sub_end, super_beg, super_end, super_edge; getSolutions(v_maps); _context._vertEdgeSolMap.clear(); int v_size = _context._subgraph->vertexEnd(); int e_size = _context._subgraph->edgeEnd(); for(int sol = 0; sol < v_maps.size(); ++sol) { Array<int>& new_map = _context._vertEdgeSolMap.push(); new_map.resize(2 + v_size + e_size); new_map[0] = v_size; new_map[1] = e_size; for(int i = 0; i < v_size; ++i) new_map[2+i] = v_maps[sol].at(i); for(int i = 0; i < e_size; ++i) new_map[2+i+v_size] = SubstructureMcs::UNMAPPED; for(int sub_edge = _context._subgraph->edgeBegin(); sub_edge < _context._subgraph->edgeEnd(); sub_edge = _context._subgraph->edgeNext(sub_edge)) { sub_beg = _context._subgraph->getEdge(sub_edge).beg; sub_end = _context._subgraph->getEdge(sub_edge).end; super_beg = v_maps[sol].at(sub_beg); super_end = v_maps[sol].at(sub_end); if(super_beg >= 0 && super_end >= 0) { const Vertex& sup_vert = _context._supergraph->getVertex(super_beg); int k = sup_vert.findNeiVertex(super_end); if(k != -1) { super_edge = sup_vert.neiEdge(k); if(_context._getEdgeColorCondition(*_context._subgraph, *_context._supergraph, sub_edge, super_edge)) new_map[2+v_size+sub_edge] = super_edge; } } } } if(_context.cbSolutionTerm == 0) _context._vertEdgeSolMap.qsort(_context.ringsSolutionTerm, 0); else _context._vertEdgeSolMap.qsort(_context.cbSolutionTerm, _context.userdata); return _context._vertEdgeSolMap.size(); } void MaxCommonSubgraph::AdjMatricesStore::_setFirstElement(int i, int j, int value){ _ajEdge1[i]->at(j) = value; if(value >= 0){ _daj1[i]->set(j,true); }else{ _daj1[i]->set(j,false); } } void MaxCommonSubgraph::AdjMatricesStore::_setSecondElement(int i, int j, int value){ _ajEdge2[i]->at(j) = value; if(value >= 0){ _aj2[i]->at(j) = true; _daj2[i]->set(j,true); }else{ _daj2[i]->set(j,false); _aj2[i]->at(j) = false; } } void MaxCommonSubgraph::AdjMatricesStore::_createAdjacencyMatrices(){ int i,j,s; for(i = 0;i < _size1;i++){ _daj1[i]->zeroFill(); for(j = 0;j<_size1;j++){ _setFirstElement(i,j,-1); } } for(i = _graph1->edgeBegin();i<_graph1->edgeEnd();i = _graph1->edgeNext(i)){ int j1 = _getFirstC(_graph1->getEdge(i).beg); int j2 = _getFirstC(_graph1->getEdge(i).end); if(j1 >= 0 && j2 >= 0){ _setFirstElement(j1,j2,i); _setFirstElement(j2,j1,i); } } for(i = 0;i < _size1;i++){ s = 0; for(j = getFirstRow(i)->nextSetBit(0);j != -1; j = getFirstRow(i)->nextSetBit(j+1)) ++s; _degreeVec1[i] = s; } for(i = 0;i < _size2;i++){ _daj2[i]->zeroFill(); for(j = 0;j<_size2;j++){ _setSecondElement(i,j,-1); } } for(i = _graph2->edgeBegin();i<_graph2->edgeEnd();i = _graph2->edgeNext(i)){ int j1 = _getSecondC(_graph2->getEdge(i).beg); int j2 = _getSecondC(_graph2->getEdge(i).end); if(j1 >= 0 && j2 >= 0){ _setSecondElement(j1,j2,i); _setSecondElement(j2,j1,i); } } for(i = 0;i < _size2;i++){ s = 0; for(j = getSecondRow(i)->nextSetBit(0);j != -1;j = getSecondRow(i)->nextSetBit(j+1)) ++s; _degreeVec2[i] = s; } } void MaxCommonSubgraph::AdjMatricesStore::_createLabelMatrices(){ _mLabel1.clear(); for(int i = 0;i < _size1;i++){ _mLabel1.add(new Array<int>());; } for(int i = 0;i < _size1;i++){ for(int j = 0;j < _size2;j++){ if(getVerticesColorCondition(i,j)){ _mLabel1[i]->push(j); } } } } void MaxCommonSubgraph::AdjMatricesStore::_createErrorEdgesMatrix(){ for(int i = 0; i < _maxsize; i++){ _errorEdgesMatrix[i]->zerofill(); } for(int i = _graph1->edgeBegin(); i < _graph1->edgeEnd(); i = _graph1->edgeNext(i)){ for(int j = _graph2->edgeBegin(); j < _graph2->edgeEnd(); j = _graph2->edgeNext(j)){ if(!_context._getEdgeColorCondition(*_graph1, *_graph2, i, j)) _errorEdgesMatrix[i]->at(j) = 1; } } } bool MaxCommonSubgraph::AdjMatricesStore::_checkSize(Graph& g1,Graph& g2){ int size1 = g1.vertexCount(); int size2 = g2.vertexCount(); QS_DEF(Array<int>, inv_map); int i, j; _x.zerofill(); _y.zerofill(); if(_context.incomingMap.size() > 0) { inv_map.resize(g2.vertexEnd()); for(int i = 0; i < inv_map.size(); i++) inv_map[i] = -1; _makeInvertMap(_context.incomingMap, inv_map); } for(i = g1.vertexBegin();i < g1.vertexEnd();i = g1.vertexNext(i)){ if(_context.incomingMap.size() > 0 && _context.incomingMap.at(i) != -1) continue; for(j = g2.vertexBegin();j < g2.vertexEnd();j = g2.vertexNext(j)){ if(_context.incomingMap.size() > 0 && inv_map[j] != -1) continue; if(_context.conditionVerticesColor(g1, g2, 0, i, j, _context.userdata)){ ++_x[i];++_y[j]; } } } for(i = g1.vertexBegin(); i < g1.vertexEnd(); i = g1.vertexNext(i)) { if(_x[i] == 0) size1--; } for(j = g2.vertexBegin(); j < g2.vertexEnd(); j = g2.vertexNext(j)) { if(_y[j] == 0) size2--; } if(size1 > size2) return true; return false; } void MaxCommonSubgraph::AdjMatricesStore::_createMaps(){ for(int i = 0;i<_maxsize;i++){ _map[i] = -1; _invmap[i] = -1; } if(_context.incomingMap.size()>0){ if(_swap){ for(int i = _graph2->vertexBegin();i < _graph2->vertexEnd();i = _graph2->vertexNext(i)){ _invmap[i] = _context.incomingMap[i]; } _makeInvertMap(_invmap,_map); }else{ for(int i = _graph1->vertexBegin();i < _graph1->vertexEnd();i = _graph1->vertexNext(i)){ _map[i] = _context.incomingMap[i]; } _makeInvertMap(_map,_invmap); } } } void MaxCommonSubgraph::AdjMatricesStore::_createCorrespondence(){ int i,j,tmp; _x.zerofill(); _y.zerofill(); tmp = 0; for(i = _graph1->vertexBegin();i < _graph1->vertexEnd();i = _graph1->vertexNext(i)){ if(_map[i] == -1){ _cr1[tmp] = i; ++tmp; } } _size1 = tmp; tmp = 0; for(i = _graph2->vertexBegin();i<_graph2->vertexEnd();i = _graph2->vertexNext(i)){ if(_invmap[i] == -1){ _cr2[tmp] = i; ++tmp; } } _size2 = tmp; for(i = 0;i < _size1;i++){ for(j = 0;j < _size2;j++){ if(getVerticesColorCondition(i,j)){ _x[i]++; _y[j]++; } } } for(i = 0;i < _size1-1; i++){ for(j = i+1;j < _size1;j++){ if(_x[i]<_x[j]){ __swap(_cr1[i],_cr1[j],tmp); __swap(_x[i],_x[j],tmp); } } } for(i = 0;i < _size2-1;i++){ for(j = i+1;j<_size2;j++){ if(_y[i]<_y[j]){ __swap(_cr2[i],_cr2[j],tmp); __swap(_y[i],_y[j],tmp); } } } //delete null vertices int size_dec = 0; for(i = 0;i < _size1;i++){ if(_x[i] == 0) ++size_dec; } _size1 = _size1 - size_dec; size_dec = 0; for(i = 0;i < _size2;i++){ if(_y[i] == 0) ++size_dec; } _size2 = _size2 - size_dec; //shuffle cr[i] if(_context.parametersForApproximate.randomize) { time_t t1; time(&t1); srand((int)t1); } else { srand(0); } RandomHandler& random_handler = _context._random; random_handler.strand = _context.parametersForApproximate.standardRandom; int r; for(i = 0;i < _size1;i++){ r = random_handler.next(_size1); __swap(_cr1[i],_cr1[r],tmp); } for(i = 0;i < _size2;i++){ r = random_handler.next(_size2); __swap(_cr2[i],_cr2[r],tmp); } } void MaxCommonSubgraph::AdjMatricesStore::_makeInvertMap(Array<int>& map,Array<int>& invmap){ for(int i = 0;i < map.size();i++){ if(map[i] != -1){ invmap[map[i]] = i; } } } bool MaxCommonSubgraph::AdjMatricesStore:: getVerticesColorCondition(int i, int j) { if(_context.conditionVerticesColor == 0) return true; return _context.conditionVerticesColor(*_graph1, *_graph2, 0, _cr1[i], _cr2[j], _context.userdata); } bool MaxCommonSubgraph::AdjMatricesStore::getVColorOneCondition(int i, int j){ if(_context.conditionVerticesColor == 0) return true; return _context.conditionVerticesColor(*_graph1, *_graph1, 0, _cr1[i], _cr1[j], _context.userdata); } int MaxCommonSubgraph::AdjMatricesStore::countErrorAtEdges(int i, int j){ if(!getSecondElement(_x[i],_x[j])) return 1; else return _errorEdgesMatrix[getFirstIdxEdge(i,j)]->at(getSecondIdxEdge(_x[i],_x[j])); } bool MaxCommonSubgraph::AdjMatricesStore::getEdgeWeightCondition(int i, int j) { bool r1 = true; if(_context.conditionEdgeWeight != 0) r1 = _context.conditionEdgeWeight(*_graph1, *_graph2, getFirstIdxEdge(i,j), getSecondIdxEdge(_x[i],_x[j]), _context.userdata); bool r2 = getVerticesColorCondition(i,_x[i]) && getVerticesColorCondition(j,_x[j]); return r1 && r2; } int MaxCommonSubgraph::AdjMatricesStore::_getFirstC(int x){ for(int j = 0;j<_size1;j++){ if(_cr1[j] == x) return j; } return -1; } int MaxCommonSubgraph::AdjMatricesStore::_getSecondC(int x) { for(int j = 0;j<_size2;j++){ if(_cr2[j] == x) return j; } return -1; } void MaxCommonSubgraph::AdjMatricesStore::_createConnectedGraph(Graph& graph, Array<int>& map_gr) { QS_DEF(Array<int>, filter); int i, j, size_g1; bool c1, c2, c3; size_g1 = _graph1->vertexEnd(); graph.clear(); filter.resize(size_g1); filter.zerofill(); map_gr.clear(); for(i = _graph1->vertexBegin(); i < _graph1->vertexEnd(); i = _graph1->vertexNext(i)) { for(j = _graph1->vertexBegin(); j < _graph1->vertexEnd(); j = _graph1->vertexNext(j)) { if(i != j && _map[i] >= 0 && _map[j] >= 0) { int e_idx1 = _graph1->findEdgeIndex(i,j); int e_idx2 = _graph2->findEdgeIndex(_map[i],_map[j]); c1 = (e_idx1 >= 0) && (e_idx2 >= 0) && _context.conditionEdgeWeight(*_graph1, *_graph2, e_idx1, e_idx2, _context.userdata); c2 = _context.conditionVerticesColor(*_graph1, *_graph2, 0, i, _map[i], _context.userdata); c3 = _context.conditionVerticesColor(*_graph1, *_graph2, 0, j, _map[j], _context.userdata); if(c1 && c2 && c3) { filter[i] = 1; } } } } for(i = 0; i < size_g1; i++){ if(filter[i] == 1) { filter[i] = graph.addVertex(); map_gr.push(i); } } for(i = _graph1->vertexBegin(); i < _graph1->vertexEnd(); i = _graph1->vertexNext(i)) { for(j = _graph1->vertexBegin(); j < _graph1->vertexEnd(); j = _graph1->vertexNext(j)) { if(i != j && _map[i] >= 0 && _map[j] >= 0 ) { int e_idx1 = _graph1->findEdgeIndex(i, j); int e_idx2 = _graph2->findEdgeIndex(_map[i], _map[j]); c1 = (e_idx1 >= 0) && (e_idx2 >= 0) && _context.conditionEdgeWeight(*_graph1, *_graph2, e_idx1, e_idx2, _context.userdata); bool c2 = _context.conditionVerticesColor(*_graph1, *_graph2, 0, i, _map[i], _context.userdata); bool c3 = _context.conditionVerticesColor(*_graph1, *_graph2, 0, j, _map[j], _context.userdata); if(c1 && c2 && c3 && !graph.haveEdge(filter[i], filter[j])) { graph.addEdge(filter[i], filter[j]); } } } } } void MaxCommonSubgraph::AdjMatricesStore::getSolutions(ObjArray< Array<int> >& v_maps) { int i, j, size_g1; QS_DEF(Graph, graph); QS_DEF(Array<int>, map_gr); QS_DEF(Array<int>, tmp_map); size_g1 = _graph1->vertexEnd(); for(i = 0; i < _size1; i++) { if(_x[i] >= 0 && _map[_cr1[i]] == -1) _map[_cr1[i]] = _cr2[_x[i]]; } _createConnectedGraph(graph, map_gr); int ncomp = graph.countComponents(); const Array<int> &decomposition = graph.getDecomposition(); //check for maximum v_maps.clear(); if(ncomp == 0) return; for(i = 0; i < ncomp; i++) v_maps.push(); if(_swap) { tmp_map.resize(size_g1); for(int cur_comp = 0; cur_comp < ncomp; ++cur_comp) { for(i = 0; i < size_g1; i++) tmp_map[i] = SubstructureMcs::UNMAPPED; for(i = 0; i < decomposition.size(); i++){ if(decomposition[i] == cur_comp) tmp_map[map_gr[i]] = _map[map_gr[i]]; } v_maps[cur_comp].resize(_graph2->vertexEnd()); for(j = 0; j < _graph2->vertexEnd(); j++) v_maps[cur_comp].at(j) = SubstructureMcs::UNMAPPED; _makeInvertMap(tmp_map, v_maps[cur_comp]); } } else { for(int cur_comp = 0; cur_comp < ncomp; ++cur_comp) { v_maps[cur_comp].resize(size_g1); for(j = 0; j < size_g1; j++) v_maps[cur_comp].at(j) = SubstructureMcs::UNMAPPED; for(i = 0; i < decomposition.size(); i++) { if(decomposition[i] == cur_comp) v_maps[cur_comp].at(map_gr[i]) = _map[map_gr[i]]; } } } } //Construction stage: greedy method //------------------------------------------------------------------------------------------------------------------- MaxCommonSubgraph::Greedy::Greedy(AdjMatricesStore &aj):_adjMstore(aj){} void MaxCommonSubgraph::Greedy::greedyMethod(){ int ss = 0; int ssmax = 0; _n = _adjMstore.getFirstSize(); _m = _adjMstore.getSecondSize(); _x = _adjMstore.getX(); _y = _adjMstore.getY(); _adjStatus.resize(_m); _createLgLh(); if(_unsignVert1.size() == 0) return; for(int i = 0;i<_n;i++){ _x[i] = -1; } for(int i = 0;i<_m;i++){ _adjStatus[i] = -1; _y[i] = -1; } int i1 = _unsignVert1.size()-1; int p = _unsignVert1[i1]; int i20 = _unsignVert2[0]->at(p); int i2 = _unsignVert2[i20]->size()-1; int q = _unsignVert2[i20]->at(i2); while (true){ _x[p] = q; _y[q] = p; _unsignVert1.remove(i1); _unsignVert2[i20]->remove(i2); if(_unsignVert1.size() == 0) break; i1 = _unsignVert1.size()-1; for(int i = _adjMstore.getSecondRow(q)->nextSetBit(0);i != -1;i = _adjMstore.getSecondRow(q)->nextSetBit(i+1)){ if(_y[i] == -1) _adjStatus[i] = 1; } ssmax = 0; for(int iter = 0;iter<_unsignVert1.size();iter++){ ss = 0; for(int i = _adjMstore.getFirstRow(_unsignVert1[iter])->nextSetBit(0);i!=-1;i = _adjMstore.getFirstRow(_unsignVert1[iter])->nextSetBit(i+1)){ if(_x[i] >= 0) ++ss; } if(ss >= ssmax){ i1 = iter; ssmax = ss; } } p = _unsignVert1[i1]; ssmax = 0; i20 = _unsignVert2[0]->at(p); i2 = _unsignVert2[i20]->size()-1; if(i2 == -1){ ssmax = 1<<16; for(int i = 1; i < _unsignVert2.size(); i++){ if(_unsignVert2[i]->size()>0){ i20 = i; break; } } for(int iter = 0;iter<_unsignVert2[i20]->size();iter++){ _x[p] = _unsignVert2[i20]->at(iter); ss = _matchedEdges(); if(ss<ssmax){ i2 = iter; ssmax = ss; } _x[p] = -1; } }else{ for(int iter = 0;iter<_unsignVert2[i20]->size();iter++){ _x[p] = _unsignVert2[i20]->at(iter); ss = _matchedEdges(); if(ss>ssmax||( ss >= ssmax && _adjStatus[_unsignVert2[i20]->at(iter)] == 1 )){ i2 = iter; ssmax = ss; } _x[p] = -1; } } q = _unsignVert2[i20]->at(i2); } } void MaxCommonSubgraph::Greedy::_createLgLh(){ int i = 0; int j = 0; _unsignVert1.clear(); _unsignVert2.clear(); for(i = 0;i<_n;i++){ _unsignVert1.push(i); } _unsignVert2.add(new Array<int>()); bool nfind; for(i = 0;i<_n;i++){ nfind = true; for(j = 0;j<_unsignVert2[0]->size();j++){ if(_adjMstore.getVColorOneCondition(i,j)){ nfind = false; _unsignVert2[0]->push(_unsignVert2[0]->at(j)); break; } } if(nfind){ _unsignVert2.add(new Array<int>()); int last = _unsignVert2.size()-1; _unsignVert2[last]->clear_resize(_adjMstore.getFLSize(i)); for(j = 0;j<_adjMstore.getFLSize(i);j++){ _unsignVert2[last]->at(j) = _adjMstore.getFLV(i,j); } _unsignVert2[0]->push(last); } } _unsignVert1.qsort(_compareFirstDegree,&_adjMstore); for(i = 1;i<_unsignVert2.size();i++){ _unsignVert2[i]->qsort(_compareSecondDegree,&_adjMstore); } } int MaxCommonSubgraph::Greedy::_compareFirstDegree(int &i1,int &i2,void* context){ AdjMatricesStore &aj = *(AdjMatricesStore *)context; return aj.getFirstVDegree(i2)-aj.getFirstVDegree(i1); } int MaxCommonSubgraph::Greedy::_compareSecondDegree(int &i1,int &i2,void* context){ AdjMatricesStore &aj = *(AdjMatricesStore *)context; return aj.getSecondVDegree(i2)-aj.getSecondVDegree(i1); } int MaxCommonSubgraph::Greedy::_matchedEdges(){ int matchedges = 0; for(int i = 0;i<_n;i++){ if(_x[i] >= 0){ for(int j = _adjMstore.getFirstRow(i)->nextSetBit(0);j!=-1;j = _adjMstore.getFirstRow(i)->nextSetBit(j+1)){ if(_x[j] >= 0 && _adjMstore.getSecondElement(_x[i],_x[j]) && _adjMstore.getEdgeWeightCondition(i,j)){ ++matchedges; } } } } return matchedges>>1; } //Refinement stage: random discrete descent method //------------------------------------------------------------------------------------------------------------------- CP_DEF(MaxCommonSubgraph::RandomDisDec); MaxCommonSubgraph::RandomDisDec::RandomDisDec(AdjMatricesStore &aj): _adjMstore(aj),CP_INIT,TL_CP_GET(_errorList),TL_CP_GET(_listErrVertices),_maxIteration(MAX_ITERATION){ setIterationNumber(aj._context.parametersForApproximate.maxIteration); cancellation_handler = getCancellationHandler(); } void MaxCommonSubgraph::RandomDisDec::refinementStage(){ _n = _adjMstore.getFirstSize(); _m = _adjMstore.getSecondSize(); _x = _adjMstore.getX(); _y = _adjMstore.getY(); _stop = false; _errorList.resize(_n); _listErrVertices.resize(_n+1); int t = 0; _stuckCount = 0; _errorNumber = _goalFunction(); _errorNumberStuck = _errorNumber; _saveState(); _makeLe(); int r,p,q,a,b; if(_adjMstore._context.parametersForApproximate.randomize) { time_t clock; time(&clock); srand((int)clock); } else { srand(0); } RandomHandler& random_handler = _adjMstore._context._random; random_handler.strand = _adjMstore._context.parametersForApproximate.standardRandom; while(true){ ++t; //if iteration reach max limit - when end cycle if(t > (_maxIteration * _n) || _errorNumber == 0){ _stop = true; } if (t % 100 == 0) { if (cancellation_handler != 0) { if (cancellation_handler->isCancelled()) throw Error("mcs search was cancelled: %s", cancellation_handler->cancelledRequestMessage()); } } if(_stop) break; //if algorithm get stuck in local minimum - when give him potencial to override it ++_stuckCount; if(_stuckCount > (log((double)_n) * _n)){ if(_errorNumber <= _stateArray[0]) _saveState(); _stuckCount = 0; for (int i = 0; i < _errorList.size();i++) ++_errorList[i]; } //select random vertex in list of error vertices to swap or move while(true){ r = random_handler.next(_listErrVertices[0])+1; p = _listErrVertices[r]; if(!((_adjMstore.getFLSize(p) == 1 && _x[p] == _adjMstore.getFLV(p,0))||(_adjMstore.getFLSize(p) == 0))) break; } //select random vertex in other graph a = _x[p]; r = a; while(r == a) r = _adjMstore.getFLV(p, random_handler.next(_adjMstore.getFLSize(p))); q = r; if(_y[q] >= 0){ //swap assignments p and y(q) b = _y[q]; _x[b] = a; _x[p] = q; if(_acceptanceSwap(b,p)){ _errorNumber = _newErrorNumber; _y[a] = b; _y[q] = p; _makeLe(); }else{ //change back swap _x[b] = q; _x[p] = a; } }else{ //move _x[p] = q; if(_acceptanceMove(p)){ _errorNumber = _newErrorNumber; _y[a] = -1; _y[q] = p; _makeLe(); }else{ //change back move _x[p] = a; } } } //if error in the end of work more when saved error then load state if(_errorNumber > _stateArray[0]){ _loadState(); } } void MaxCommonSubgraph::RandomDisDec::setIterationNumber(int max){ _maxIteration = max; } int MaxCommonSubgraph::RandomDisDec::_goalFunction(){ //function that collect all errors int err = 0; for(int i = 0;i < _n; i++){ _errorList[i] = 0; for(int j = _adjMstore.getFirstRow(i)->nextSetBit(0); j!=-1; j = _adjMstore.getFirstRow(i)->nextSetBit(j+1)){ _errorList[i] += _adjMstore.countErrorAtEdges(i,j); } err += _errorList[i]; } return err>>1; } void MaxCommonSubgraph::RandomDisDec::_makeLe(){ //creation of error list of the vertices _listErrVertices[0] = 0; int sum = 0; for(int i = 0;i < _n;i++){ if(_errorList[i] > 0){ ++_listErrVertices[0]; _listErrVertices[_listErrVertices[0]] = i; if(_adjMstore.getFLSize(i) == 1 && _x[i] == _adjMstore.getFLV(i,0)) ++sum; } } if(_listErrVertices[0] == sum) _stop = true; if(_errorNumber < _errorNumberStuck){ _stuckCount = 0; _errorNumberStuck = _errorNumber; } } bool MaxCommonSubgraph::RandomDisDec::_acceptanceMove(int x){ int ex = 0; bool result; for(int j = _adjMstore.getFirstRow(x)->nextSetBit(0);j != -1;j = _adjMstore.getFirstRow(x)->nextSetBit(j+1)){ ex += _adjMstore.countErrorAtEdges(x,j); } if(ex <= _errorList[x]){ _newErrorNumber = _goalFunction(); result = true; }else result = false; return result; } bool MaxCommonSubgraph::RandomDisDec::_acceptanceSwap(int x, int y){ int ex = 0; int ey = 0; bool result; for(int j = _adjMstore.getFirstRow(x)->nextSetBit(0);j!=-1;j = _adjMstore.getFirstRow(x)->nextSetBit(j+1)){ ex += _adjMstore.countErrorAtEdges(x,j); } for(int j = _adjMstore.getFirstRow(y)->nextSetBit(0);j!=-1;j = _adjMstore.getFirstRow(y)->nextSetBit(j+1)){ ey += _adjMstore.countErrorAtEdges(y,j); } if((ex + ey) <= (_errorList[x]+_errorList[y])){ _newErrorNumber = _goalFunction(); result = true; }else result = false; return result; } void MaxCommonSubgraph::RandomDisDec::_saveState(){ if((_n + _m + 1) > _stateArray.size()) _stateArray.resize(_n + _m +1); _stateArray[0] = _errorNumber; for(int i = 0; i < _n; i++) _stateArray[i + 1] = _x[i]; for(int i = 0; i < _m; i++) _stateArray[i + 1 + _n] = _y[i]; if(_adjMstore._context.cbEmbedding != 0) { if(!_adjMstore._context.cbEmbedding(0, 0, 0, _adjMstore._context.embeddingUserdata)) _stop = true; } } void MaxCommonSubgraph::RandomDisDec::_loadState(){ _errorNumber = _stateArray[0]; for(int i = 0; i < _n; i++) _x[i] = _stateArray[i + 1]; for(int i = 0; i < _m; i++) _y[i] = _stateArray[i + 1 + _n]; } //maximize number of the rings/ v-e+r=2 there v- number of vertices e - number of edges r - number of rings int MaxCommonSubgraph::ringsSolutionTerm(Array<int>& a1, Array<int>& a2, void* dummy) { int a1_vlen = 0, a2_vlen = 0, a1_elen = 0, a2_elen = 0; for(int i = 0; i < a1[0]; ++i) if(a1[i+2] >= 0) ++a1_vlen; for(int i = 0; i < a1[1]; ++i) if(a1[i+2+a1[0]] >= 0) ++a1_elen; for(int i = 0; i < a2[0]; ++i) if(a2[i+2] >= 0) ++a2_vlen; for(int i = 0; i < a2[1]; ++i) if(a2[i+2+a2[0]] >= 0) ++a2_elen; //maximize number of the rings/ v-e+r=2 there v- number of vertices e - number of edges r - number of rings int result = (a2_elen-a2_vlen) - (a1_elen-a1_vlen); if(result == 0) result = a2_elen - a1_elen; return result; } void MaxCommonSubgraph::_getSolutionMaps(int count, ObjArray< Array<int> >& v_maps, ObjArray< Array<int> >& e_maps) const { v_maps.clear(); e_maps.clear(); for(int i = 0; (i < count) && (i < _vertEdgeSolMap.size()); ++i) { int v_size = _vertEdgeSolMap[i].at(0); int e_size = _vertEdgeSolMap[i].at(1); Array<int>& v_arr = v_maps.push(); Array<int>& e_arr = e_maps.push(); v_arr.resize(v_size); e_arr.resize(e_size); for (int j = 0; j < v_size; ++j) v_arr[j] = _vertEdgeSolMap[i].at(2 + j); for (int j = 0; j < e_size; ++j) e_arr[j] = _vertEdgeSolMap[i].at(2 + v_size + j); } } //---------------------------------------------------------------------------------------------------------------- //substructure search SubstructureMcs::SubstructureMcs(): cbMatchEdge(0), cbMatchVertex(0), userdata(0), _sub(0), _super(0), _invert(false) { } SubstructureMcs::SubstructureMcs(Graph &sub, Graph &super): cbMatchEdge(0), cbMatchVertex(0), userdata(0), _sub(0), _super(0), _invert(false) { setGraphs(sub, super); } void SubstructureMcs::setGraphs(Graph &sub, Graph &super){ if(sub.vertexCount() < super.vertexCount()) _invert = false; else if(sub.vertexCount() > super.vertexCount()) _invert = true; else if((sub.vertexCount() == super.vertexCount()) && (sub.edgeCount() < super.edgeCount())) _invert = false; else _invert = true; if(!_invert) { _sub = ⊂ _super = &super; } else { _sub = &super; _super = ⊂ } } //searches substructure for graphs and maps vertices bool SubstructureMcs::searchSubstructure(Array<int> *map) { if(_sub == 0 || _super == 0) throw MaxCommonSubgraph::Error("internal AAM error: not initialized sub-mcs graphs"); EmbeddingEnumerator emb_enum(*_super); emb_enum.setSubgraph(*_sub); emb_enum.cb_match_edge = cbMatchEdge; emb_enum.cb_match_vertex = cbMatchVertex; emb_enum.cb_embedding = _embedding; emb_enum.userdata = userdata; int result = emb_enum.process(); if(result == 1) return false; if(map != 0) { if(!_invert) { map->clear_resize(_sub->vertexEnd()); for(int i = 0; i < map->size(); i++) map->at(i) = UNMAPPED; for(int i = _sub->vertexBegin(); i < _sub->vertexEnd(); i = _sub->vertexNext(i)) map->at(i) = emb_enum.getSubgraphMapping()[i]; } else { map->clear_resize(_super->vertexEnd()); for(int i = 0; i < map->size(); i++) map->at(i) = UNMAPPED; for(int i = _super->vertexBegin(); i < _super->vertexEnd(); i = _super->vertexNext(i)) map->at(i) = emb_enum.getSupergraphMapping()[i]; } } return true; } int SubstructureMcs::_embedding (Graph &, Graph &, int *, int *, void *) { return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/morgan_code.cpp�������������������������������������������������������0000664�0000000�0000000�00000003043�12710376503�0021052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" #include "graph/morgan_code.h" using namespace indigo; MorganCode::MorganCode (const Graph &g) : _g(g) { } void MorganCode::calculate (Array<long> &codes, int coeff, int iteration_count) { QS_DEF(Array<long>, next_codes); next_codes.clear_resize(_g.vertexEnd()); codes.clear_resize(_g.vertexEnd()); int i, j, k; for (i = _g.vertexBegin(); i < _g.vertexEnd(); i = _g.vertexNext(i)) codes[i] = _g.getVertex(i).degree(); for (j = 0; j < iteration_count; j++) { for (i = _g.vertexBegin(); i < _g.vertexEnd(); i = _g.vertexNext(i)) { next_codes[i] = coeff * codes[i]; const Vertex &vertex = _g.getVertex(i); for (k = vertex.neiBegin(); k < vertex.neiEnd(); k = vertex.neiNext(k)) next_codes[i] += codes[vertex.neiVertex(k)]; } memcpy(codes.ptr(), next_codes.ptr(), sizeof(long) * _g.vertexEnd()); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/path_enumerator.cpp���������������������������������������������������0000664�0000000�0000000�00000005727�12710376503�0022005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" #include "base_cpp/cancellation_handler.h" #include "graph/graph.h" #include "graph/path_enumerator.h" using namespace indigo; IMPL_TIMEOUT_EXCEPTION(PathEnumerator, "path enumerator"); PathEnumerator::PathEnumerator (Graph &graph, int begin, int end) : _graph(graph), _begin(begin), _end(end) { max_length = graph.vertexCount(); context = 0; cb_check_vertex = 0; cb_check_edge = 0; cb_handle_path = 0; } PathEnumerator::~PathEnumerator () { } void PathEnumerator::process() { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); QS_DEF(Array<bool>, flags); QS_DEF(Array<int>, index); QS_DEF(Array<bool>, can_achieve_to_end); vertices.clear(); edges.clear(); flags.clear_resize(_graph.vertexEnd()); flags.zerofill(); vertices.push(_begin); edges.push(-1); // fictitious edge flags[_begin] = true; index.clear_resize(_graph.vertexEnd()); index.zerofill(); can_achieve_to_end.clear_resize(_graph.vertexEnd()); can_achieve_to_end.zerofill(); can_achieve_to_end[_end] = true; index[_begin] = _graph.getVertex(_begin).neiBegin(); while (vertices.size() > 0) { int current_v_number = vertices.top(); const Vertex ¤t_v = _graph.getVertex(current_v_number); int& current_index = index[current_v_number]; if (current_v_number != _end && current_index != current_v.neiEnd()) { int u = current_v.neiVertex(current_index); int e = current_v.neiEdge(current_index); if (can_achieve_to_end[u]) { } { if (!flags[u] && (cb_check_vertex == 0 || cb_check_vertex(_graph, u, context))) { vertices.push(u); edges.push(e); flags[u] = true; index[u] = _graph.getVertex(u).neiBegin(); } } current_index = current_v.neiNext(current_index); } else { if (can_achieve_to_end[vertices.top()] && vertices.size() > 1) { if (vertices.size() > 2 || vertices[1] != _end) cb_check_edge(_graph, edges.top(), context); vertices.top() = -1; vertices.pop(); can_achieve_to_end[vertices.top()] = true; } else { vertices.top() = -1; vertices.pop(); } edges.pop(); } } }�����������������������������������������Indigo-indigo-1.2.3/graph/src/scaffold_detection.cpp������������������������������������������������0000664�0000000�0000000�00000026442�12710376503�0022424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/scaffold_detection.h" #include "base_cpp/array.h" #include "graph/max_common_subgraph.h" #include "base_cpp/ptr_array.h" using namespace indigo; IMPL_ERROR(ScaffoldDetection, "Scaffold detection"); ScaffoldDetection::ScaffoldDetection (ObjArray<Graph>* graph_set): cbEdgeWeight(0), cbVerticesColor(0), cbSortSolutions(0), userdata(0), cbEmbedding(0), embeddingUserdata(0), searchStructures(graph_set), basketStructures(0), maxIterations(0) { } void ScaffoldDetection::_searchScaffold(Graph& scaffold, bool approximate) { GraphBasket graph_basket; QS_DEF(ObjArray<Graph>, temp_set); if(basketStructures == 0) { basketStructures = &temp_set; } graph_basket.initBasket(searchStructures, basketStructures, GraphBasket::MAX_MOLECULES_NUMBER); if(approximate) _searchApproximateScaffold(graph_basket); else _searchExactScaffold(graph_basket); int max_index = graph_basket.getMaxGraphIndex(); if(basketStructures->size() == 0) throw Error("no scaffolds found"); scaffold.cloneGraph(graph_basket.getGraph(max_index), 0); } void ScaffoldDetection::_searchExactScaffold(GraphBasket& basket) { ObjArray< Array<int> > v_lists; ObjArray< Array<int> > e_lists; int graphset_size = basket.getGraphSetSize(); int first_graph_num = 0; SubstructureMcs sub_mcs; sub_mcs.cbMatchEdge = cbEdgeWeight; sub_mcs.cbMatchVertex = cbVerticesColor; sub_mcs.userdata = userdata; MaxCommonSubgraph mcs(basket.getGraph(0), basket.getGraph(0)); mcs.conditionEdgeWeight = cbEdgeWeight; mcs.conditionVerticesColor = cbVerticesColor; mcs.userdata = userdata; if(maxIterations > 0) mcs.parametersForExact.maxIteration = maxIterations; basket.cbMatchEdges = cbEdgeWeight; basket.cbMatchVertices = cbVerticesColor; basket.userdata = userdata; for(int orgraph = first_graph_num+1; orgraph < graphset_size; orgraph++) { Graph& graph_set = basket.getGraphFromSet(orgraph); for(int bgraph = basket.graphBegin(); bgraph >= 0; bgraph = basket.graphNext(bgraph)) { Graph& graph_bask = basket.getGraph(bgraph); sub_mcs.setGraphs(graph_bask, graph_set); if(!sub_mcs.isInverted() && sub_mcs.searchSubstructure(0)) continue; mcs.setGraphs(graph_bask, graph_set); MaxCommonSubgraph::ReGraph regraph(mcs); MaxCommonSubgraph::ReCreation build_graph(regraph, mcs); build_graph.createRegraph(); regraph.parse(true); /* * Throw an exception if max limit was reached */ if(regraph.stopped()) throw Error("scaffold detection exact searching max iteration limit reached"); build_graph.getSolutionListsSuper(v_lists, e_lists); for(int i = 0; i < e_lists.size(); i++) { basket.addToNextEmptySpot(graph_set, v_lists[i], e_lists[i]); } basket.removeGraph(bgraph); } basket.checkAddedGraphs(); if(cbEmbedding != 0) { if(!cbEmbedding(&orgraph, &graphset_size, 0, embeddingUserdata)) break; } } } void ScaffoldDetection::_searchApproximateScaffold(GraphBasket& basket) { ObjArray< Array<int> > v_maps; ObjArray< Array<int> > e_maps; Array<int> v_list; Array<int> e_list; int graphset_size = basket.getGraphSetSize(); int max_size = 0; for(int i = 0; i < graphset_size; i++) { if(basket.getGraphFromSet(i).edgeCount() > max_size) max_size = basket.getGraphFromSet(i).edgeCount(); if(basket.getGraphFromSet(i).vertexCount() > max_size) max_size = basket.getGraphFromSet(i).vertexCount(); } SubstructureMcs sub_mcs; sub_mcs.cbMatchEdge = cbEdgeWeight; sub_mcs.cbMatchVertex = cbVerticesColor; sub_mcs.userdata = userdata; MaxCommonSubgraph mcs(basket.getGraph(0), basket.getGraph(0)); mcs.conditionEdgeWeight = cbEdgeWeight; mcs.conditionVerticesColor = cbVerticesColor; mcs.userdata = userdata; if(maxIterations > 0) mcs.parametersForApproximate.maxIteration = maxIterations; basket.cbMatchEdges = cbEdgeWeight; basket.cbMatchVertices = cbVerticesColor; basket.userdata = userdata; MaxCommonSubgraph::AdjMatricesStore adjm(mcs, 2 * max_size); MaxCommonSubgraph::Greedy greedy(adjm); MaxCommonSubgraph::RandomDisDec randdisdec(adjm); for(int orgraph = 1; orgraph < graphset_size; orgraph++) { Graph& graph_set = basket.getGraphFromSet(orgraph); for(int bgraph = basket.graphBegin(); bgraph >= 0; bgraph = basket.graphNext(bgraph)) { Graph& graph_bask = basket.getGraph(bgraph); //search sub sub_mcs.setGraphs(graph_bask, graph_set); if(!sub_mcs.isInverted() && sub_mcs.searchSubstructure(0)) continue; //search mcs mcs.setGraphs(graph_bask, graph_set); adjm.create(graph_bask, graph_set); greedy.greedyMethod(); randdisdec.refinementStage(); adjm.createSolutionMaps(); mcs.getSolutionMaps(&v_maps, &e_maps); for(int i = 0; i < e_maps.size(); ++i) { v_list.clear(); for(int j = 0 ; j < v_maps[i].size(); ++j) if(v_maps[i].at(j) != SubstructureMcs::UNMAPPED) v_list.push(v_maps[i].at(j)); e_list.clear(); for(int j = 0 ; j < e_maps[i].size(); ++j) if(e_maps[i].at(j) != SubstructureMcs::UNMAPPED) e_list.push(e_maps[i].at(j)); if(v_list.size() > 1) basket.addToNextEmptySpot(graph_set, v_list, e_list); } basket.removeGraph(bgraph); } basket.checkAddedGraphs(); if(cbEmbedding != 0) { if(!cbEmbedding(&orgraph, &graphset_size, 0, embeddingUserdata)) break; } } } void ScaffoldDetection::GraphBasket::_sortGraphsInSet() { int set_size = _searchStructures->size(); if(set_size == 0) throw Error("graph set size == 0"); _orderArray.clear(); for(int i = 0; i < set_size; i++) { if(_searchStructures->at(i).vertexCount() > 0) { _orderArray.push(i); ++_graphSetSize; } } //sort in order of edgeCount _orderArray.qsort(_compareEdgeCount, (void*)_searchStructures); } int ScaffoldDetection::GraphBasket::_compareEdgeCount(int i1, int i2,void* context){ ObjArray<Graph> &graph_set = *(ObjArray<Graph> *)context; return graph_set.at(i1).edgeCount()-graph_set.at(i2).edgeCount(); } int ScaffoldDetection::GraphBasket::_copmpareRingsCount(Graph& g1, Graph& g2, void* ) { //maximize number of the rings/ v-e+r=2 there v- number of vertices e - number of edges r - number of rings int result = (g2.edgeCount() - g2.vertexCount()) - (g1.edgeCount() - g1.vertexCount()); if(result == 0 || g1.edgeCount() == 0 || g2.edgeCount() == 0) result = g2.edgeCount() - g1.edgeCount(); return result; } IMPL_ERROR(ScaffoldDetection::GraphBasket, "Graph basket"); ScaffoldDetection::GraphBasket::GraphBasket(): cbSortSolutions(0), cbMatchEdges(0), cbMatchVertices(0), userdata(0), _searchStructures(0), _basketStructures(0), _graphSetSize(0){ } ScaffoldDetection::GraphBasket::~GraphBasket() { } void ScaffoldDetection::GraphBasket::initBasket(ObjArray<Graph>* graph_set, ObjArray<Graph>* basket_set, int max_number) { if(graph_set == 0) throw Error("graph set is null"); if(basket_set == 0) throw Error("basket set is null"); _searchStructures = graph_set; _basketStructures = basket_set; _sortGraphsInSet(); _basketStructures->clear(); for(int i = 0; i < max_number; i++) _basketStructures->push(); _directIterator.resize(max_number); _reverseIterator.resize(max_number); _reverseIterator.set(); _basketStructures->at(0).cloneGraph(_searchStructures->at(_orderArray[0]),0); _reverseIterator.set(0, false); _directIterator.set(0); } int ScaffoldDetection::GraphBasket::graphBegin() { return _directIterator.nextSetBit(0); } int ScaffoldDetection::GraphBasket::graphNext(int i) { return _directIterator.nextSetBit(i+1); } void ScaffoldDetection::GraphBasket::checkAddedGraphs() { bool add_to_basket; Dbitset added_iter(_reverseIterator.size()); added_iter.copy(_reverseIterator); added_iter.orWith(_directIterator); added_iter.flip(); SubstructureMcs sub_mcs; sub_mcs.cbMatchEdge = cbMatchEdges; sub_mcs.cbMatchVertex = cbMatchVertices; sub_mcs.userdata = userdata; for(int x = added_iter.nextSetBit(0); x >= 0; x = added_iter.nextSetBit(x+1)) { add_to_basket = true; for(int y = graphBegin(); y >= 0; y = graphNext(y)) { sub_mcs.setGraphs(getGraph(x), getGraph(y)); if(sub_mcs.searchSubstructure(0)) { add_to_basket = false; if(sub_mcs.isInverted()) { removeGraph(y); add_to_basket = true; } break; } } if(add_to_basket) _directIterator.set(x); else _reverseIterator.set(x); } } void ScaffoldDetection::GraphBasket::removeGraph(int index) { _directIterator.set(index, false); _reverseIterator.set(index); } Graph& ScaffoldDetection::GraphBasket::getGraph(int index) const { if(index >= _basketStructures->size()) throw Error("basket size < index"); return _basketStructures->at(index); } Graph& ScaffoldDetection::GraphBasket::pickOutNextGraph() { int empty_index = _reverseIterator.nextSetBit(0); if(empty_index == -1) { _directIterator.resize(_directIterator.size()+NEXT_SOLUTION_SIZE_SUM); _reverseIterator.resize(_directIterator.size()); empty_index = _basketStructures->size(); for(int i = _directIterator.size()-NEXT_SOLUTION_SIZE_SUM; i < _directIterator.size(); i++) _reverseIterator.set(i); for(int i = 0; i < NEXT_SOLUTION_SIZE_SUM; i++) _basketStructures->push(); } _reverseIterator.set(empty_index, false); return _basketStructures->at(empty_index); } int ScaffoldDetection::GraphBasket::getMaxGraphIndex() { for(int x = _reverseIterator.nextSetBit(0); x >= 0; x = _reverseIterator.nextSetBit(x+1)) { Graph& graph_basket = _basketStructures->at(x); if(graph_basket.vertexCount() > 0) graph_basket.clear(); } if(cbSortSolutions == 0) _basketStructures->qsort(&_copmpareRingsCount, 0); else _basketStructures->qsort(cbSortSolutions, userdata); while(_basketStructures->size() && _basketStructures->top().vertexCount() == 0) _basketStructures->pop(); return 0; } void ScaffoldDetection::GraphBasket::addToNextEmptySpot(Graph& graph, Array<int>& v_list, Array<int>& e_list) { pickOutNextGraph().makeEdgeSubgraph(graph, v_list, e_list, 0, 0); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/shortest_path_finder.cpp����������������������������������������������0000775�0000000�0000000�00000004356�12710376503�0023026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/graph.h" #include "graph/shortest_path_finder.h" using namespace indigo; ShortestPathFinder::ShortestPathFinder (const Graph &graph) : _graph(graph) { cb_check_vertex = 0; cb_check_edge = 0; check_vertex_context = 0; check_edge_context = 0; queue.setLength(_graph.vertexEnd()); prev.clear_resize(_graph.vertexEnd()); } bool ShortestPathFinder::find (Array<int>& vertices, Array<int>& edges, int u, int v) { // init queue.clear(); prev.fffill(); vertices.clear(); edges.clear(); // push initial vertex queue.push(v); prev[v] = u; while (!queue.isEmpty()) { // pop vertex int w = queue.pop(); const Vertex& vert = _graph.getVertex(w); for (int i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { int e = vert.neiEdge(i); if (cb_check_edge != 0 && !cb_check_edge(_graph, e, check_edge_context)) continue; int n = vert.neiVertex(i); if (cb_check_vertex != 0 && !cb_check_vertex(_graph, n, check_vertex_context)) continue; if (prev[n] >= 0) continue; // vertex is already done if (n == u) { // shortest path found. mark and return prev[u] = w; for (int j = u; j != v; j = prev[j]) { vertices.push(j); edges.push(_graph.findEdgeIndex(j, prev[j])); } vertices.push(v); return true; } queue.push(n); prev[n] = w; } } return false; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/simple_cycle_basis.cpp������������������������������������������������0000664�0000000�0000000�00000033777�12710376503�0022447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/simple_cycle_basis.h" #include "graph/graph.h" #include "base_cpp/red_black.h" #include "graph/shortest_path_finder.h" #include "base_cpp/tlscont.h" #include "graph/spanning_tree.h" #include "base_cpp/list.h" #include "graph/aux_path_finder.h" using namespace indigo; SimpleCycleBasis::SimpleCycleBasis(const Graph& graph) : _graph(graph),_isMinimized(false) { } void SimpleCycleBasis::create() { QS_DEF(Array<int>, vert_mapping); QS_DEF(ObjArray< Array<int> >, subgraph_cycles); subgraph_cycles.clear(); Graph subgraph; subgraph.cloneGraph(_graph, &vert_mapping); _prepareSubgraph(subgraph); // The cycles just created are already minimal, so we can start minimizing at startIndex int startIndex = _cycles.size(); // Now we perform a breadth first traversal and build a fundamental tree base // ("Kirchhoff base") of the remaining subgraph int current_vertex = subgraph.vertexBegin(); // We build a spanning tree as a directed graph to easily find the parent of a // vertex in the tree. This means however that we have to create new Edge objects // for the tree and can't just use the Edge objects of the graph, since the // the edge in the graph might have a wrong or no direction. Graph spanning_tree; QS_DEF(RedBlackSet<int>, visited_edges); visited_edges.clear(); // FIFO for the BFS QS_DEF(Array<int>, vertex_queue); vertex_queue.clear(); // currentVertex is the root of the spanning tree int new_vertex = spanning_tree.addVertex(); vertices_spanning_tree.insert(current_vertex, new_vertex); spanning_tree_vertices.insert(new_vertex, current_vertex); vertex_queue.push(current_vertex); // We need to remember the tree edges so we can add them at once to the // index list for the incidence matrix QS_DEF(Array<int>, tree_edges); tree_edges.clear(); while (vertex_queue.size() > 0) { current_vertex = vertex_queue.pop(); const Vertex& c_vertex = subgraph.getVertex(current_vertex); for (int i = c_vertex.neiBegin(); i < c_vertex.neiEnd(); i = c_vertex.neiNext(i)) { // find a neighbour vertex of the current vertex int edge = c_vertex.neiEdge(i); if (!visited_edges.find(edge)) { // mark edge as visited visited_edges.insert(edge); int next_vertex = subgraph.getEdge(edge).findOtherEnd(current_vertex); if (!vertices_spanning_tree.find(next_vertex)) { // tree edge tree_edges.push(edge); int new_vertex = spanning_tree.addVertex(); vertices_spanning_tree.insert(next_vertex, new_vertex); spanning_tree_vertices.insert(new_vertex, next_vertex); // create a new (directed) Edge object (as explained above) spanning_tree.addEdge(vertices_spanning_tree.at(current_vertex), vertices_spanning_tree.at(next_vertex)); // add the next vertex to the BFS-FIFO vertex_queue.push(next_vertex); } else { // non-tree edge // This edge defines a cycle together with the edges of the spanning tree // along the path to the root of the tree. We create a new cycle containing // these edges (not the tree edges, but the corresponding edges in the graph) Array<int>& edges_of_cycle = subgraph_cycles.push(); // follow the path to the root of the tree int vertex = current_vertex; // get parent of vertex int parent_vertex = -1; while (_getParentVertex(spanning_tree, vertex, parent_vertex)) { // add the corresponding edge to the cycle edges_of_cycle.push(subgraph.findEdgeIndex(vertex, parent_vertex)); // go up the tree vertex = parent_vertex; } // do the same thing for nextVertex vertex = next_vertex; while (_getParentVertex(spanning_tree, vertex, parent_vertex)) { // add the corresponding edge to the cycle edges_of_cycle.push(subgraph.findEdgeIndex(vertex, parent_vertex)); // go up the tree vertex = parent_vertex; } // finally, add the non-tree edge to the cycle edges_of_cycle.push(edge); // add the edge to the index list for the incidence matrix _edgeList.push(edge); } } } } // Add all the tree edges to the index list for the incidence matrix for (int i = 0; i < tree_edges.size(); ++i) { _edgeList.push(tree_edges[i]); } // edgeIndexMap = createEdgeIndexMap(edgeList); // Now the index list is ordered: first the non-tree edges, then the tree edge. // Moreover, since the cycles and the corresponding non-tree edge have been added // to their lists in the same order, the incidence matrix is in upper triangular form. // Now we can minimize the cycles created from the tree base for (int i = 0; i < subgraph_cycles.size(); ++i) { Array<int>& cycle_edges = subgraph_cycles[i]; Array<int>& new_cycle_edges = _cycles.push(); for (int j = 0; j < cycle_edges.size(); ++j) { int edge_s = subgraph.getEdge(cycle_edges[j]).beg; int edge_t = subgraph.getEdge(cycle_edges[j]).end; new_cycle_edges.push(_graph.findEdgeIndex(vert_mapping[edge_s], vert_mapping[edge_t])); } } _createEdgeIndexMap(); _minimize(startIndex); } bool SimpleCycleBasis::_getParentVertex(const Graph& graph, int vertex, int& parent_vertex) { parent_vertex = -1; const Vertex& gv = graph.getVertex(vertices_spanning_tree.at(vertex)); for (int i = gv.neiBegin(); i < gv.neiEnd(); i = gv.neiNext(i)) { const Edge& edge = graph.getEdge(gv.neiEdge(i)); if (edge.end == vertices_spanning_tree.at(vertex)) parent_vertex = spanning_tree_vertices.at(edge.beg); } if (parent_vertex == -1) { return false; } return true; } void SimpleCycleBasis::_minimize(int startIndex) { if (_isMinimized) return; // Implementation of "Algorithm 1" from [BGdV04] QS_DEF(ObjArray< Array<bool> >, a); a.clear(); _getCycleEdgeIncidenceMatrix(a); for (int cur_cycle = startIndex; cur_cycle < _cycles.size(); ++cur_cycle) { // "Subroutine 2" // Construct kernel vector u Array<bool> u; u.resize(_edgeList.size()); constructKernelVector(u, a, cur_cycle); // Construct auxiliary graph gu AuxiliaryGraph gu(_graph, u, _edgeIndexMap); AuxPathFinder path_finder(gu, _graph.vertexEnd()*2); QS_DEF(ObjArray< Array<int> >, all_new_cycles); all_new_cycles.clear(); for (int v = _graph.vertexBegin(); v < _graph.vertexEnd(); v = _graph.vertexNext(v)) { // check if the vertex is incident to an edge with u[edge] == 1 bool shouldSearchCycle = false; const Vertex& vertex = _graph.getVertex(v); for (int e = vertex.neiBegin(); e < vertex.neiEnd(); e = vertex.neiNext(e)) { int edge = vertex.neiEdge(e); int edge_index = _getEdgeIndex(edge); if (u[edge_index]) { shouldSearchCycle = true; break; } } if (shouldSearchCycle) { int auxVertex0 = gu.auxVertex0(v); int auxVertex1 = gu.auxVertex1(v); Array<int>& edges_of_new_cycle = all_new_cycles.push(); Array<int> path_vertices; // Search for shortest path path_finder.find(path_vertices, edges_of_new_cycle, auxVertex0, auxVertex1); } } Array<int>& current_cycle = _cycles.at(cur_cycle); int shortest_cycle_size = current_cycle.size(); int shortest_cycle = -1; for (int i = 0; i < all_new_cycles.size(); ++i) { int cycle_size = all_new_cycles[i].size(); if(cycle_size > 0 && cycle_size < shortest_cycle_size) { shortest_cycle = i; shortest_cycle_size = cycle_size; } } if(shortest_cycle != -1) { current_cycle.clear(); Array<int>& sh_cycle = all_new_cycles[shortest_cycle]; for (int i = 0; i < sh_cycle.size(); ++i) { current_cycle.push(gu.edge(sh_cycle[i])); } } // insert the new cycle into the matrix for (int j = 1; j < _edgeList.size(); j++) { a[cur_cycle][j] = (current_cycle.find(_edgeList.at(j)) != -1); } // perform gaussian elimination on the inserted row for (int j = 0; j < cur_cycle; j++) { if (a[cur_cycle][j]) { for (int k = 0; k < _edgeList.size(); k++) { a[cur_cycle][k] = (a[cur_cycle][k] != a[j][k]); } } } } _isMinimized = true; } void SimpleCycleBasis::_getCycleEdgeIncidenceMatrix(ObjArray< Array<bool> >& result) { for (int i = 0; i < _cycles.size(); ++i) { Array<bool>& new_array = result.push(); new_array.resize(_edgeList.size()); Array<int>& cycle = _cycles[i]; for(int j = 0; j < _edgeList.size(); ++j) { result[i][j] = (cycle.find(_edgeList[j]) != -1); } } } void SimpleCycleBasis::constructKernelVector(Array<bool>& u, ObjArray< Array<bool> >& a, int i) { for (int j = 0; j < u.size(); ++j) { u[j] = false; } // Construct kernel vector u by setting u[i] = true ... u[i] = true; // ... u[j] = 0 (false) for j > i (by initialization)... // ... and solving A u = 0 for (int j = i - 1; j >= 0; j--) { u[j] = false; for (int k = i; k > j; k--) { u[j] = (u[j] != (a[j][k] && u[k])); } } } void SimpleCycleBasis::_createEdgeIndexMap() { _edgeIndexMap.clear(); for (int i = 0; i < _edgeList.size(); ++i) { _edgeIndexMap.insert(_edgeList[i], i); } } int SimpleCycleBasis::_getEdgeIndex(int edge) const { return _edgeIndexMap.at(edge); } void SimpleCycleBasis::_prepareSubgraph (Graph &subgraph) { QS_DEF(Array<int>, path_vertices); path_vertices.clear(); QS_DEF(RedBlackSet<int>, selected_edges); selected_edges.clear(); QS_DEF(RedBlackSet<int>, remaining_edges); remaining_edges.clear(); for (int i = subgraph.edgeBegin(); i < subgraph.edgeEnd(); i = subgraph.edgeNext(i)) { remaining_edges.insert(i); } ShortestPathFinder path_finder(subgraph); while (remaining_edges.size() > 0) { int edge = remaining_edges.begin(); int source = subgraph.getEdge(edge).beg; int target = subgraph.getEdge(edge).end; subgraph.removeEdge(edge); Array<int>& path_edges = _cycles.push(); path_finder.find(path_vertices, path_edges, source, target); path_edges.push(edge); subgraph.addEdge(source, target); selected_edges.insert(edge); _edgeList.push(edge); for (int i = 0; i < path_edges.size(); ++i) { remaining_edges.remove_if_exists(path_edges[i]); } } for (int i = selected_edges.begin(); i < selected_edges.end(); i = selected_edges.next(i)) { subgraph.removeEdge(selected_edges.key(i)); } } int AuxiliaryGraph::auxVertex0(int vertex) { if (!_vertexMap0.find(vertex)) { int newVertex0 = addVertex(); _vertexMap0.insert(vertex, newVertex0); _auxVertexMap.insert(newVertex0, vertex); return newVertex0; } return _vertexMap0.at(vertex); } int AuxiliaryGraph::auxVertex1(int vertex) { if (!_vertexMap1.find(vertex)) { int newVertex1 = addVertex(); _vertexMap1.insert(vertex, newVertex1); _auxVertexMap.insert(newVertex1, vertex); return newVertex1; } return _vertexMap1.at(vertex); } const Vertex& AuxiliaryGraph::getVertexAndBuild(int auxVertex) { const Vertex& vertex = _graph.getVertex(_auxVertexMap.at(auxVertex)); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int edge = vertex.neiEdge(i); int vertex1 = _graph.getEdge(edge).beg; int vertex2 = _graph.getEdge(edge).end; int edge_idx = _edgeIndexMap.at(edge); if (_u[edge_idx]) { int vertex1u = auxVertex0(vertex1); int vertex2u = auxVertex1(vertex2); int ex_aux_edge = findEdgeIndex(vertex1u, vertex2u); if (ex_aux_edge == -1) { int auxEdge = addEdge(vertex1u, vertex2u); _auxEdgeMap.insert(auxEdge, edge); } vertex1u = auxVertex1(vertex1); vertex2u = auxVertex0(vertex2); ex_aux_edge = findEdgeIndex(vertex1u, vertex2u); if (ex_aux_edge == -1) { int auxEdge = addEdge(vertex1u, vertex2u); _auxEdgeMap.insert(auxEdge, edge); } } else { int vertex1u = auxVertex0(vertex1); int vertex2u = auxVertex0(vertex2); int ex_aux_edge = findEdgeIndex(vertex1u, vertex2u); if (ex_aux_edge == -1) { int auxEdge = addEdge(vertex1u, vertex2u); _auxEdgeMap.insert(auxEdge, edge); } vertex1u = auxVertex1(vertex1); vertex2u = auxVertex1(vertex2); ex_aux_edge = findEdgeIndex(vertex1u, vertex2u); if (ex_aux_edge == -1) { int auxEdge = addEdge(vertex1u, vertex2u); _auxEdgeMap.insert(auxEdge, edge); } } } return getVertex(auxVertex); } int AuxiliaryGraph::edge(int auxEdge) { return _auxEdgeMap.value(auxEdge); } �Indigo-indigo-1.2.3/graph/src/skew_symmetric_flow_finder.cpp����������������������������������������0000664�0000000�0000000�00000016105�12710376503�0024223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/skew_symmetric_flow_finder.h" #include "graph/skew_symmetric_network.h" using namespace indigo; IMPL_ERROR(SkewSymmetricFlowFinder, "SkewSymmetricFlowFinder"); CP_DEF(SkewSymmetricFlowFinder); SkewSymmetricFlowFinder::SkewSymmetricFlowFinder (const SkewSymmetricNetwork &network) : CP_INIT, TL_CP_GET(_arc_values), TL_CP_GET(_arc_sym), TL_CP_GET(_edge_used_dir), TL_CP_GET(_vertex_is_used), _network(network) { _init(); } void SkewSymmetricFlowFinder::process () { QS_DEF(Array<int>, path); while (_findAugmentatingPath(path)) _increaseFlowByPath(path); } int SkewSymmetricFlowFinder::getArcValue (int arc) const { return _arc_values[arc]; } void SkewSymmetricFlowFinder::_init () { int n_edges = _network.g().edgeEnd(); _arc_sym.clear_resize(n_edges); _arc_values.clear_resize(n_edges); _edge_used_dir.clear_resize(n_edges); for (int i = 0; i < n_edges; i++) { _arc_sym[i] = 0; _arc_values[i] = 0; _edge_used_dir[i] = 0; } for (int e = _network.g().edgeBegin(); e != _network.g().edgeEnd(); e = _network.g().edgeNext(e)) { _arc_sym[e] = _network.getSymmetricArc(e); } _vertex_is_used.clear_resize(_network.g().vertexEnd()); for (int i = 0; i < _vertex_is_used.size(); i++) _vertex_is_used[i] = 0; _network_sink = _network.getSymmetricVertex(_network.getSource()); } bool SkewSymmetricFlowFinder::_findAugmentatingPath (Array<int> &vertices) { for (int i = 0; i < _vertex_is_used.size(); i++) _vertex_is_used[i] = 0; for (int i = 0; i < _edge_used_dir.size(); i++) _edge_used_dir[i] = 0; vertices.clear(); vertices.push(_network.getSource()); return _findAugmentatingPathRec(vertices); } bool SkewSymmetricFlowFinder::_findAugmentatingPathRec (Array<int> &vertices) { // This is trivial implementation that takes exponential time in worth case // There is fast polynomial algorithm bud trimming algorithm that // will be implemented in future int from = vertices.top(); if (from == _network_sink) return true; _vertex_is_used[from] = 1; const Vertex &v = _network.g().getVertex(from); for (int i = v.neiBegin(); i != v.neiEnd(); i = v.neiNext(i)) { int edge = v.neiEdge(i); int edge_sym = _network.getSymmetricArc(edge); int nei_vertex = v.neiVertex(i); if (_vertex_is_used[nei_vertex]) continue; if (_isEdgeAugmentating(edge, from, _edge_used_dir[edge_sym])) { vertices.push(v.neiVertex(i)); if (_network.getArcType(edge, from) == ARC_OUT) _edge_used_dir[edge] = 1; else _edge_used_dir[edge] = -1; bool ret = _findAugmentatingPathRec(vertices); if (ret) return true; _edge_used_dir[edge] = 0; vertices.pop(); } } _vertex_is_used[from] = 0; return false; } int SkewSymmetricFlowFinder::_getResidualCapacity (int edge, int from) { if (_network.getArcType(edge, from) == ARC_OUT) { // Outgoing arc return _arc_values[edge]; } else { // Incomming arc return _network.getArcCapacity(edge) - _arc_values[edge]; } } bool SkewSymmetricFlowFinder::_isEdgeAugmentating (int edge, int from, int sym_used_dir) { int residual_capacity = _getResidualCapacity(edge, from); int delta = _network.getArcCapacity(edge) - residual_capacity; int edge_dir_cur; if (_network.getArcType(edge, from) == ARC_OUT) edge_dir_cur = 1; else edge_dir_cur = -1; if (edge_dir_cur * sym_used_dir == -1) return 0; // This edge will be changed by 0 if (sym_used_dir != 0 && delta > 1) return true; if (sym_used_dir == 0 && delta > 0) return true; return false; } void SkewSymmetricFlowFinder::_increaseFlowByPath (Array<int> &vertices) { int delta = -1; // Find path maximum delta for (int i = 1; i < vertices.size(); i++) { int from = vertices[i - 1]; int to = vertices[i]; int edge = _network.g().findEdgeIndex(from, to); int edge_sym = _network.getSymmetricArc(edge); int cur_delta = _network.getArcCapacity(edge) - _getResidualCapacity(edge, from); if (_edge_used_dir[edge_sym] != 0) { if (_edge_used_dir[edge_sym] * _edge_used_dir[edge] == 1) cur_delta = cur_delta / 2; else continue; // Delta for this edge is zero } if (delta == -1 || delta > cur_delta) delta = cur_delta; } if (delta == 0) throw Error("algorithm error: delta should be positive"); // Update network flow for (int i = 1; i < vertices.size(); i++) { int from = vertices[i - 1]; int to = vertices[i]; int edge = _network.g().findEdgeIndex(from, to); int edge_sym = _arc_sym[edge]; int cur_delta = delta; if (_edge_used_dir[edge_sym] != 0) { if (_edge_used_dir[edge_sym] * _edge_used_dir[edge] == -1) continue; // Delta for this edge is zero } if (_edge_used_dir[edge] == -1) cur_delta = -cur_delta; _arc_values[edge] += cur_delta; _arc_values[edge_sym] += cur_delta; } // For debug purposes _dbgCheckConsistency(); } void SkewSymmetricFlowFinder::_dbgCheckConsistency () { // Check that all arc values are in bounds const Graph &g = _network.g(); for (int e = g.edgeBegin(); e != g.edgeEnd(); e = g.edgeNext(e)) { if (_arc_values[e] < 0 || _arc_values[e] > _network.getArcCapacity(e)) throw Error("arc values are not in bounds"); int se = _arc_sym[e]; if (_arc_values[e] != _arc_values[se]) throw Error("symmetry arc values are not the same"); } // Check divergence int source_div = -1, sink_div = -1; for (int v = g.vertexBegin(); v != g.vertexEnd(); v = g.vertexNext(v)) { int div = 0; const Vertex &vert = g.getVertex(v); for (int nei = vert.neiBegin(); nei != vert.neiEnd(); nei = vert.neiNext(nei)) { int e = vert.neiEdge(nei); if (_network.getArcType(e, v) == ARC_OUT) div += _arc_values[e]; else div -= _arc_values[e]; } if (v == _network_sink) sink_div = div; else if (v == _network.getSource()) source_div = div; else if (div != 0) throw Error("internal vertex divergence must be zero"); } if (source_div + sink_div != 0) throw Error("source and sink Divergence must be zero in sum"); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/skew_symmetric_network.cpp��������������������������������������������0000664�0000000�0000000�00000006515�12710376503�0023422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/skew_symmetric_network.h" using namespace indigo; IMPL_ERROR(SkewSymmetricNetwork, "skew symmetric network"); const Graph& SkewSymmetricNetwork::g() const { return _g; } void SkewSymmetricNetwork::clear () { _g.clear(); _symmetry.clear(); _arcs.clear(); _source = -1; } void SkewSymmetricNetwork::setSource (int source) { _source = source; } int SkewSymmetricNetwork::getSource () const { return _source; } int SkewSymmetricNetwork::addVertex (int *symmetry_vertex) { int v = _g.addVertex(); int v_sym = _g.addVertex(); _symmetry.resize(_g.vertexEnd()); _symmetry[v] = v_sym; _symmetry[v_sym] = v; if (symmetry_vertex != NULL) *symmetry_vertex = v_sym; return v; } void SkewSymmetricNetwork::removeVertex (int vertex) { int v_sym = getSymmetricVertex(vertex); _g.removeVertex(vertex); _g.removeVertex(v_sym); } int SkewSymmetricNetwork::addArc (int from, int to, int capacity) { int from_sym = getSymmetricVertex(from); int to_sym = getSymmetricVertex(to); if (_g.haveEdge(from, to)) throw Error("both directions arcs are not supported"); if (_g.haveEdge(from_sym, to_sym)) throw Error("inconsistent skew-symmetric network state"); int edge = _g.addEdge(from, to); int edge_sym = _g.addEdge(to_sym, from_sym); _arcs.resize(_g.edgeEnd()); _arcs[edge].from = from; _arcs[edge].to = to; _arcs[edge].capacity = capacity; _arcs[edge_sym].from = to_sym; _arcs[edge_sym].to = from_sym; _arcs[edge_sym].capacity = capacity; return edge; } void SkewSymmetricNetwork::removeArc (int from, int to) { int edge = _g.findEdgeIndex(from, to); int edge_sym = getSymmetricArc(edge); _g.removeEdge(edge); _g.removeEdge(edge_sym); } int SkewSymmetricNetwork::getSymmetricVertex (int vertex) const { return _symmetry[vertex]; } int SkewSymmetricNetwork::getArcType (int edge, int vertex) const { const Arc &arc = _arcs[edge]; if (arc.from == vertex) return ARC_OUT; else if (arc.to == vertex) return ARC_IN; else throw Error("invalid edge passed in getArcType method"); } int SkewSymmetricNetwork::getArcCapacity (int edge) const { return _arcs[edge].capacity; } int SkewSymmetricNetwork::getSymmetricArc (int edge) const { const Arc &arc = _arcs[edge]; int from_sym = getSymmetricVertex(arc.from); int to_sym = getSymmetricVertex(arc.to); int edge_sym = _g.findEdgeIndex(from_sym, to_sym); return edge_sym; } void SkewSymmetricNetwork::setArcCapacity (int edge, int capacity) { if (capacity < 0) throw Error("capacity can't be negative"); int edge_sym = getSymmetricArc(edge); _arcs[edge].capacity = capacity; _arcs[edge_sym].capacity = capacity; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/spanning_tree.cpp�����������������������������������������������������0000664�0000000�0000000�00000010652�12710376503�0021435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/spanning_tree.h" #include "base_cpp/tlscont.h" #include "graph/filter.h" using namespace indigo; IMPL_ERROR(SpanningTree, "spanning tree"); CP_DEF(SpanningTree); SpanningTree::SpanningTree (Graph &graph, const Filter *vertex_filter, const Filter *edge_filter) : _graph(graph), CP_INIT, TL_CP_GET(_edges_list), TL_CP_GET(_depth_counters), TL_CP_GET(_tree), TL_CP_GET(_mapping), TL_CP_GET(_inv_mapping), TL_CP_GET(_edge_mapping), TL_CP_GET(_stack) { int i; _vertex_filter = vertex_filter; _edge_filter = edge_filter; _tree.clear(); _edges_list.clear(); _mapping.clear_resize(_graph.vertexCount()); _edge_mapping.clear_resize(_graph.edgeCount()); _inv_mapping.clear_resize(_graph.vertexEnd()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (vertex_filter != 0 && !vertex_filter->valid(i)) continue; int idx = _tree.addVertex(); _mapping[idx] = i; _inv_mapping[i] = idx; } _depth_counters.clear_resize(_tree.vertexEnd()); _depth_counters.zerofill(); _current_depth = 0; int start = 0; _stack.clear(); while (1) { for (; start < _tree.vertexEnd(); start = _tree.vertexNext(start)) { if (vertex_filter != 0 && !vertex_filter->valid(_mapping[start])) continue; if (_depth_counters[start] == 0) break; } if (start == _tree.vertexEnd()) break; StackElem & elem = _stack.push(); elem.vertex = &_graph.getVertex(_mapping[start]); elem.nei_idx = elem.vertex->neiBegin(); elem.vertex_idx = start; elem.parent_idx = -1; _depth_counters[start] = ++_current_depth; _build(); } } void SpanningTree::_build () { while (_stack.size() > 0) { StackElem &elem = _stack.top(); int v = elem.vertex_idx; int i = elem.nei_idx; if (i < elem.vertex->neiEnd()) { elem.nei_idx = elem.vertex->neiNext(i); int nei_v = elem.vertex->neiVertex(i); if (_vertex_filter != 0 && !_vertex_filter->valid(nei_v)) continue; if (_edge_filter != 0) { int nei_edge = elem.vertex->neiEdge(i); if (!_edge_filter->valid(nei_edge)) continue; } int w = _inv_mapping[elem.vertex->neiVertex(i)]; if (_depth_counters[w] == 0) { int idx = _tree.addEdge(v, w); _edge_mapping[idx] = elem.vertex->neiEdge(i); StackElem &newelem = _stack.push(); _depth_counters[w] = ++_current_depth; newelem.parent_idx = v; newelem.vertex_idx = w; newelem.vertex = &_graph.getVertex(_mapping[w]); newelem.nei_idx = newelem.vertex->neiBegin(); } else if (w != elem.parent_idx && _depth_counters[w] < _depth_counters[v]) { ExtEdge edge; edge.beg_idx = v; edge.end_idx = w; edge.ext_beg_idx = _mapping[v]; edge.ext_end_idx = _mapping[w]; edge.ext_edge_idx = elem.vertex->neiEdge(i); _edges_list.push(edge); } } else _stack.pop(); } } void SpanningTree::addEdge (int beg, int end, int ext_index) { int idx = _tree.addEdge(beg, end); _edge_mapping[idx] = ext_index; } void SpanningTree::markAllEdgesInCycles (int *marks_out, int value) { int i, j; QS_DEF(Array<int>, path); for (i = 0; i < _edges_list.size(); i++) { const ExtEdge &ext_edge = _edges_list[i]; if (!_tree.findPath(ext_edge.beg_idx, ext_edge.end_idx, path)) throw Error("markAllEdgesInCycles(): no path"); for (j = 0; j < path.size(); j++) marks_out[_edge_mapping[path[j]]] = value; marks_out[ext_edge.ext_edge_idx] = value; } } ��������������������������������������������������������������������������������������Indigo-indigo-1.2.3/graph/src/subgraph_hash.cpp�����������������������������������������������������0000664�0000000�0000000�00000007724�12710376503�0021425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/subgraph_hash.h" #include "graph/graph.h" using namespace indigo; CP_DEF(SubgraphHash); SubgraphHash::SubgraphHash (Graph &g) : _g(g), CP_INIT, TL_CP_GET(_codes), TL_CP_GET(_oldcodes), TL_CP_GET(_gf), TL_CP_GET(_default_vertex_codes), TL_CP_GET(_default_edge_codes) { max_iterations = _g.vertexEnd(); _different_codes_count = 0; calc_different_codes_count = false; _codes.clear_resize(_g.vertexEnd()); _oldcodes.clear_resize(_g.vertexEnd()); _default_vertex_codes.clear_resize(_g.vertexEnd()); _default_edge_codes.clear_resize(_g.edgeEnd()); _default_vertex_codes.fill(1); _default_edge_codes.fill(1); vertex_codes = &_default_vertex_codes; edge_codes = &_default_edge_codes; _gf.setGraph(g); _gf.prepareEdges(); } dword SubgraphHash::getHash () { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); int i; vertices.clear(); edges.clear(); for (i = _g.vertexBegin(); i != _g.vertexEnd(); i = _g.vertexNext(i)) vertices.push(i); for (i = _g.edgeBegin(); i != _g.edgeEnd(); i = _g.edgeNext(i)) edges.push(i); return getHash(vertices, edges); } dword SubgraphHash::getHash (const Array<int> &vertices, const Array<int> &edges) { int i, iter; dword *codes_ptr = _codes.ptr(); dword *oldcodes_ptr = _oldcodes.ptr(); if (vertex_codes == 0 || edge_codes == 0) throw Exception("SubgraphHash: vertex_codes and edge_codes are not set"); const int *vc = vertex_codes->ptr(); const int *ec = edge_codes->ptr(); const int *v = vertices.ptr(); const int *e = edges.ptr(); for (i = 0; i < vertices.size(); i++) codes_ptr[v[i]] = vc[v[i]]; const Edge *graph_edges = _gf.getEdges(); for (iter = 0; iter < max_iterations; iter++) { for (i = 0; i < vertices.size(); i++) oldcodes_ptr[v[i]] = codes_ptr[v[i]]; for (i = 0; i < edges.size(); i++) { int edge_index = e[i]; const Edge &edge = graph_edges[edge_index]; int edge_rank = ec[edge_index]; dword v1_code = oldcodes_ptr[edge.beg]; dword v2_code = oldcodes_ptr[edge.end]; codes_ptr[edge.beg] += v2_code * v2_code + (v2_code + 23) * (edge_rank + 1721); codes_ptr[edge.end] += v1_code * v1_code + (v1_code + 23) * (edge_rank + 1721); } } dword result = 0; for (i = 0; i < vertices.size(); i++) { dword code = codes_ptr[v[i]]; result += code * (code + 6849) + 29; } if (calc_different_codes_count) { // Calculate number of different codes Array<dword> &code_was_used = _oldcodes; dword *code_was_used_ptr = code_was_used.ptr(); for (i = 0; i < vertices.size(); i++) code_was_used_ptr[v[i]] = 0; _different_codes_count = 0; for (int i = 0; i < vertices.size(); i++) { if (code_was_used[v[i]]) continue; _different_codes_count++; dword cur_code = codes_ptr[v[i]]; for (int j = 0; j < vertices.size(); j++) if (codes_ptr[v[j]] == cur_code) code_was_used_ptr[v[j]] = 1; } } return result; } int SubgraphHash::getDifferentCodesCount () { return _different_codes_count; } ��������������������������������������������Indigo-indigo-1.2.3/graph/subgraph_hash.h�����������������������������������������������������������0000664�0000000�0000000�00000003053�12710376503�0020272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __subgraph_hash__ #define __subgraph_hash__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "graph/graph_fast_access.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Graph; class DLLEXPORT SubgraphHash { public: SubgraphHash (Graph &g); int max_iterations; bool calc_different_codes_count; dword getHash (); dword getHash (const Array<int> &vertices, const Array<int> &edges); int getDifferentCodesCount (); const Array<int> *vertex_codes, *edge_codes; private: Graph &_g; int _different_codes_count; CP_DECL; TL_CP_DECL(Array<dword>, _codes); TL_CP_DECL(Array<dword>, _oldcodes); TL_CP_DECL(GraphFastAccess, _gf); TL_CP_DECL(Array<int>, _default_vertex_codes); TL_CP_DECL(Array<int>, _default_edge_codes); }; } #endif // __subgraph_hash__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0015516�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/CMakeLists.txt�����������������������������������������������������������0000664�0000000�0000000�00000000472�12710376503�0020261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROJECT(Layout) file (GLOB Layout_src src/*.c src/*.cpp) file (GLOB Layout_headers *.h* src/*.h*) #src/*.inc) include_directories(${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/..) add_library(layout OBJECT ${Layout_src} ${Layout_headers}) set_target_properties(layout PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/attachment_layout.h������������������������������������������������������0000664�0000000�0000000�00000006260�12710376503�0021420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __attachment_layout_h__ #define __attachment_layout_h__ #include "layout/molecule_layout_graph.h" #include "graph/biconnected_decomposer.h" namespace indigo { class AttachmentLayout { public: explicit AttachmentLayout(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex); double calculateEnergy (); virtual void applyLayout () = 0; void markDrawnVertices (); public: int _src_vertex; CP_DECL; TL_CP_DECL(Array<int>, _src_vertex_map); // _src_vertex_map[j] - index of the vertex _src_vertex in j component TL_CP_DECL(Array<int>, _attached_bc); // BCnumbers[j] - index of j component attached; // BCnumbers[size-1] - drawn TL_CP_DECL(Array<float>, _bc_angles); // BCangles[j] - internal angle of j component attached, 0 if single edge TL_CP_DECL(Array<int>, _vertices_l); // _vertices_l[j] - index of the vertex in j component such the j component // lays on the left (CCW) from edge (v, _vertices_l[j]]; float _alpha; // if positive then angle between components TL_CP_DECL(Array<int>, _new_vertices); // indices in source graph of new verices TL_CP_DECL(Array<Vec2f>, _layout); // layout of new vertices double _energy; // current energy between drawn part and new part const PtrArray<MoleculeLayoutGraph> &_bc_components; MoleculeLayoutGraph &_graph; }; class AttachmentLayoutSimple : public AttachmentLayout { public: explicit AttachmentLayoutSimple(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex); void applyLayout(); }; class AttachmentLayoutSmart : public AttachmentLayout { public: explicit AttachmentLayoutSmart(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex); void applyLayout(); }; class LayoutChooser { public: LayoutChooser(AttachmentLayout &layout); void perform () { _perform(_layout._attached_bc.size() - 1); } private: void _perform (int level); void _makeLayout (); int _n_components; double _cur_energy; CP_DECL; TL_CP_DECL(Array<int>, _comp_permutation); TL_CP_DECL(Array<int>, _rest_numbers); AttachmentLayout &_layout; }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/layout_pattern.h���������������������������������������������������������0000664�0000000�0000000�00000004147�12710376503�0020747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __layout_pattern_h__ #define __layout_pattern_h__ #include "base_c/defs.h" #include "base_cpp/array.h" #include "math/algebra.h" #include "graph/graph.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { struct PatternAtom { explicit PatternAtom (Vec2f pos_) : pos(pos_) {} Vec2f pos; }; struct PatternBond { explicit PatternBond (int type_) : type(type_), parity(0) {} int type; // see BOND_*** int parity; }; class DLLEXPORT PatternLayout : public Graph { public: explicit PatternLayout (); virtual ~PatternLayout (); int addBond (int atom_beg, int atom_end, int type); int addAtom (float x, float y); int addOutlinePoint (float x, float y); bool isFixed () const { return _fixed; } void fix () { _fixed = true; } void setName (const char *name) { _name.readString(name, true); } const char * getName () const { return _name.ptr(); } void calcMorganCode (); long morganCode () const { return _morgan_code; } const PatternAtom & getAtom (int idx) const; const PatternBond & getBond (int idx) const; const Array<Vec2f> & getOutline () const { return _outline; } DECL_ERROR; protected: Array<PatternAtom> _atoms; Array<PatternBond> _bonds; Array<Vec2f> _outline; Array<char> _name; long _morgan_code; bool _fixed; // no implicit copy PatternLayout (const PatternLayout &); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/layout_pattern_smart.h���������������������������������������������������0000664�0000000�0000000�00000002305�12710376503�0022147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __layout_pattern_smart_h__ #define __layout_pattern_smart_h__ #include "base_c/defs.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class MoleculeLayoutGraphSmart; class Graph; class DLLEXPORT PatternLayoutFinder { public: static bool tryToFindPattern (MoleculeLayoutGraphSmart &layout_graph); private: static void _initPatterns (); static bool _matchPatternBond (Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/metalayout.h�������������������������������������������������������������0000664�0000000�0000000�00000005473�12710376503�0020064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __metalayout_h__ #define __metalayout_h__ #include "base_cpp/reusable_obj_array.h" #include "math/algebra.h" #include "base_cpp/obj_array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class DLLEXPORT Metalayout { public: struct DLLEXPORT LayoutItem { LayoutItem () { clear(); } void clear() { explicitVerticalOffset = false; over = false; fragment = false; } int type; int id; bool fragment; bool over; bool explicitVerticalOffset; float verticalOffset; Vec2f min, max; Vec2f scaledSize, scaledOffset; Vec2f scaleFactor; }; class DLLEXPORT LayoutLine { public: LayoutLine (); ~LayoutLine (); void clear (); ObjArray<LayoutItem> items; float height; float width; private: LayoutLine (const LayoutLine&); }; Metalayout (); void clear (); bool isEmpty () const; void prepare (); float getAverageBondLength () const; float getScaleFactor () const; const Vec2f& getContentSize () const; void setScaleFactor (); void process (); LayoutLine& newLine (); static void getBoundRect (Vec2f& min, Vec2f& max, BaseMolecule& mol); void calcContentSize(); void scaleSz(); void* context; void (*cb_process) (LayoutItem& item, const Vec2f& pos, void* context); BaseMolecule& (*cb_getMol) (int id, void* context); static float getTotalMoleculeBondLength (BaseMolecule& mol); static float getTotalMoleculeClosestDist (BaseMolecule& mol); // utility function to use in MoleculeLayout & ReactionLayout void adjustMol (BaseMolecule& mol, const Vec2f& min, const Vec2f& pos); float horizontalIntervalFactor; float verticalIntervalFactor; float bondLength; DECL_ERROR; private: Vec2f _contentSize; float _avel, _scaleFactor, _offset; float _getAverageBondLength(); ReusableObjArray<LayoutLine> _layout; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif //__metalayout_h__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/molecule_layout.h��������������������������������������������������������0000664�0000000�0000000�00000004171�12710376503�0021074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_layout_h__ #define __molecule_layout_h__ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "layout/molecule_layout_graph.h" #include "layout/metalayout.h" #include "base_cpp/cancellation_handler.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT MoleculeLayout { public: enum {LAYOUT_MAX_ITERATION=20}; explicit MoleculeLayout(BaseMolecule &molecule, bool smart_layout=false); void make (); void setCancellationHandler (CancellationHandler* cancellation); float bond_length; bool respect_existing_layout; Filter *filter; int max_iterations; bool _smart_layout; DECL_ERROR; protected: Metalayout::LayoutItem& _pushMol (Metalayout::LayoutLine& line, BaseMolecule& mol); BaseMolecule& _getMol (int id); void _make (); void _makeLayout(); void _updateRepeatingUnits(); void _updateMultipleGroups (); static BaseMolecule& cb_getMol (int id, void* context); static void cb_process (Metalayout::LayoutItem& item, const Vec2f& pos, void* context); void _updateDataSGroups (); void _init(bool smart_layout); Metalayout _ml; BaseMolecule &_molecule; AutoPtr<BaseMolecule> _molCollapsed; BaseMolecule* _bm; Array<int> _atomMapping; AutoPtr<MoleculeLayoutGraph> _layout_graph; Array<BaseMolecule*> _map; bool _query; bool _hasMulGroups; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/molecule_layout_graph.h��������������������������������������������������0000664�0000000�0000000�00000052547�12710376503�0022267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_layout_graph_h__ #define __molecule_layout_graph_h__ #include "graph/graph.h" #include "graph/filter.h" #include "math/algebra.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "molecule/molecule.h" #include "layout/layout_pattern.h" #include "base_cpp/obj.h" #include "base_cpp/cancellation_handler.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BiconnectedDecomposer; #ifdef _DEBUG #define M_LAYOUT_DEBUG #endif enum { ELEMENT_NOT_DRAWN = 0, ELEMENT_INTERNAL, ELEMENT_BOUNDARY, ELEMENT_NOT_PLANAR, ELEMENT_IGNORE, ELEMENT_DRAWN }; struct LayoutVertex { LayoutVertex () { memset(this, 0, sizeof(LayoutVertex)); } int ext_idx; int orig_idx; long morgan_code; bool is_cyclic; int type; Vec2f pos; }; struct LayoutEdge { LayoutEdge () { memset(this, 0, sizeof(LayoutEdge)); } int ext_idx; int orig_idx; bool is_cyclic; int type; }; class DLLEXPORT MoleculeLayoutGraph : public Graph { public: explicit MoleculeLayoutGraph(); virtual ~MoleculeLayoutGraph(); virtual MoleculeLayoutGraph* getInstance() = 0; inline const Vec2f & getPos(int idx) const { return _layout_vertices[idx].pos; } inline Vec2f & getPos(int idx) { return _layout_vertices[idx].pos; } inline int getVertexExtIdx(int idx) const { return _layout_vertices[idx].ext_idx; } inline int getVertexType(int idx) const { return _layout_vertices[idx].type; } inline int getEdgeExtIdx(int idx) const { return _layout_edges[idx].ext_idx; } inline int getEdgeType(int idx) const { return _layout_edges[idx].type; } void setVertexType(int idx, int type) { _layout_vertices[idx].type = type; } void setEdgeType(int idx, int type) { _layout_edges[idx].type = type; } virtual void clear(); bool isSingleEdge() const; void registerLayoutVertex(int idx, const LayoutVertex &vertex); void registerLayoutEdge(int idx, const LayoutEdge &edge); int addLayoutVertex(int ext_idx, int type); int addLayoutEdge(int beg, int end, int ext_idx, int type); const LayoutVertex &getLayoutVertex(int idx) const; const LayoutEdge &getLayoutEdge(int idx) const; int findVertexByExtIdx(int ext_idx) const; virtual float calculateAngle(int v, int &v1, int &v2) const = 0; void makeOnGraph(Graph &graph); virtual void makeLayoutSubgraph(MoleculeLayoutGraph &graph, Filter &filter) = 0; void cloneLayoutGraph(MoleculeLayoutGraph &other, Array<int> *mapping); void copyLayoutTo(MoleculeLayoutGraph &other, const Array<int> &mapping) const; virtual void layout(BaseMolecule &molecule, float bond_length, const Filter *filter, bool respect_existing) = 0; const BaseMolecule *getMolecule(const int **molecule_edge_mapping) { *molecule_edge_mapping = _molecule_edge_mapping; return _molecule; } virtual void flipped() = 0; bool isFlipped() const { return _flipped; } bool _flipped; // component was flipped after attaching int max_iterations; CancellationHandler* cancellation; DECL_ERROR; ObjArray<LayoutVertex> _layout_vertices; ObjArray<LayoutEdge> _layout_edges; Array<int> _fixed_vertices; long _total_morgan_code; int _first_vertex_idx; int _n_fixed; // Outline of the graph (from pattern) Obj< Array<Vec2f> > _outline; BaseMolecule *_molecule; const int *_molecule_edge_mapping; struct Cycle { explicit Cycle(); explicit Cycle(const List<int> &edges, const MoleculeLayoutGraph &graph); explicit Cycle(const Array<int> &vertices, const Array<int> &edges); void copy(const List<int> &edges, const MoleculeLayoutGraph &graph); void copy(const Array<int> &vertices, const Array<int> &edges); int vertexCount() const { return _vertices.size(); } int getVertex(int idx) const { return _vertices[idx]; } int getVertexC(int idx) const { return _vertices[(vertexCount() + idx) % vertexCount()]; } int getEdge(int idx) const { return _edges[idx]; } int getEdgeC(int idx) const { return _edges[(_edges.size() + idx) % _edges.size()]; } int getEdgeStart(int idx) const { return getVertexC(idx); } int getEdgeFinish(int idx) const { return getVertexC(idx + 1); } int findVertex(int idx) const { return _vertices.find(idx); } long morganCode() const { if (!_morgan_code_calculated) throw Error("Morgan code does not calculated yet."); return _morgan_code; } void setVertexWeight(int idx, int w) { _attached_weight[idx] = w; } void addVertexWeight(int idx, int w) { _attached_weight[idx] += w; } int getVertexWeight(int idx) const { return _attached_weight[idx]; } void canonize(); bool contains(const Cycle &another) const; void calcMorganCode(const MoleculeLayoutGraph &parent_graph); static int compare_cb(int &idx1, int &idx2, void *context); protected: CP_DECL; TL_CP_DECL(Array<int>, _vertices); TL_CP_DECL(Array<int>, _edges); TL_CP_DECL(Array<int>, _attached_weight); int _max_idx; long _morgan_code; bool _morgan_code_calculated; private: Cycle(const Cycle &other); // No copy constructor }; struct EnumContext { const MoleculeLayoutGraph *graph; RedBlackSet<int> *edges; int iterationNumber; int maxIterationNumber; }; // geometry functions int _calcIntersection(int edge1, int edge2) const; bool _isVertexOnEdge(int vert_idx, int edge_beg, int edge_end) const; bool _isVertexOnSomeEdge(int vert_idx) const; void _shiftEdge(int edge_idx, float delta); bool _drawRegularCurve(const Array<int> &chain, int begin, int end, float length, bool ccw, int type); bool _drawRegularCurveEx(const Array<int> &chain, int begin, int end, float length, bool ccw, int type, const Array<int> &mapping); static void _findAngles(int k, float s, float &x, float &y); static float _dichotomy1(float a0, float b0, int L, float s); static float _dichotomy2(float a0, float b0, int L, float s); static void _calculatePos(float phi, const Vec2f &v1, const Vec2f &v2, Vec2f &v); // border functions virtual void _getBorder(Cycle &border) const = 0; virtual bool _isPointOutside(const Vec2f &p) const = 0; virtual bool _isPointOutsideCycle(const Cycle &cycle, const Vec2f &p) const = 0; bool _isPointOutsideCycleEx(const Cycle &cycle, const Vec2f &p, const Array<int> &mapping) const; static bool _border_cb(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); // for components virtual void _calcMorganCodes() = 0; // for whole graph virtual void _assignAbsoluteCoordinates(float bond_length) = 0; bool _checkBadTryBorderIntersection(Array<int> &chain_ext, MoleculeLayoutGraph &next_bc, Array<int> &mapping); bool _checkBadTryChainOutside(Array<int> &chain_ext, MoleculeLayoutGraph &next_bc, Array<int> & mapping); virtual void _assignRelativeCoordinates(int &fixed_component, const MoleculeLayoutGraph &supergraph) = 0; // refine static bool _edge_check(Graph &graph, int e_idx, void *context); void _refineCoordinates(const BiconnectedDecomposer &bc_decomposer, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree); bool _allowRotateAroundVertex(int idx) const; void _makeBranches(Array<int> &branches, int edge, Filter &filter) const; void _excludeDandlingIntersections(); static bool _path_handle(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); // attaching void _attachEars(int vert_idx, int drawn_idx, int *ears, const Vec2f &rest_pos); // assigning coordinates void _attachDandlingVertices(int vert_idx, Array<int> &adjacent_list); void _calculatePositionsOneNotDrawn(Array<Vec2f> &positions, int n_pos, int vert_idx, int not_drawn_idx); void _calculatePositionsSingleDrawn(int vert_idx, Array<int> &adjacent_list, int &n_pos, int drawn_idx, bool &two_ears, Array<Vec2f> &positions, int &parity); void _orderByEnergy(Array<Vec2f> &positions); void _assignRelativeSingleEdge(int &fixed_component, const MoleculeLayoutGraph &supergraph); void _findFirstVertexIdx(int n_comp, Array<int> & fixed_components, PtrArray<MoleculeLayoutGraph> &bc_components, bool all_trivial); bool _prepareAssignedList(Array<int> &assigned_list, BiconnectedDecomposer &bc_decom, PtrArray<MoleculeLayoutGraph> &bc_components, Array<int> &bc_tree); void _assignFinalCoordinates(float bond_length, const Array<Vec2f> &src_layout); void _copyLayout(MoleculeLayoutGraph &component); void _getAnchor(int &v1, int &v2, int &v3) const; void _findFixedComponents(BiconnectedDecomposer &bc_decom, Array<int> &fixed_components, PtrArray<MoleculeLayoutGraph> &bc_components); bool _assignComponentsRelativeCoordinates(PtrArray<MoleculeLayoutGraph> &bc_components, Array<int> &fixed_components, BiconnectedDecomposer &bc_decom); void _attachCrossingEdges(); void _buildOutline(void); }; class DLLEXPORT MoleculeLayoutGraphSimple : public MoleculeLayoutGraph { public: explicit MoleculeLayoutGraphSimple(); virtual ~MoleculeLayoutGraphSimple(); MoleculeLayoutGraph* getInstance(); virtual void clear (); float calculateAngle (int v, int &v1, int &v2) const; void makeLayoutSubgraph (MoleculeLayoutGraph &graph, Filter &filter); void layout (BaseMolecule &molecule, float bond_length, const Filter *filter, bool respect_existing); void flipped() { _flipped = true; }; #ifdef M_LAYOUT_DEBUG void saveDebug (); #endif DECL_ERROR; protected: // patterns void _initPatterns (); static int _pattern_cmp (PatternLayout &p1, PatternLayout &p2, void *context); static int _pattern_cmp2 (PatternLayout &p1, int n_v, int n_e, long code); static bool _match_pattern_bond (Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata); static int _pattern_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); // THERE // for whole graph void _assignAbsoluteCoordinates (float bond_length); // for components void _calcMorganCodes (); // assigning coordinates void _assignRelativeCoordinates (int &fixed_component, const MoleculeLayoutGraph &supergraph); bool _tryToFindPattern (int &fixed_component); void _assignFirstCycle(const Cycle &cycle); // attaching cycles bool _attachCycleOutside (const Cycle &cycle, float length, int n_common); bool _drawEdgesWithoutIntersection (const Cycle &cycle, Array<int> & cycle_vertex_types); bool _attachCycleInside (const Cycle &cycle, float length); bool _attachCycleWithIntersections (const Cycle &cycle, float length); void _setChainType (const Array<int> &chain, const Array<int> &mapping, int type); bool _splitCycle (const Cycle &cycle, const Array<int> &cycle_vertex_types, bool check_boundary, Array<int> &chain_ext, Array<int> &chain_int, int &c_beg, int &c_end) const; void _splitCycle2 (const Cycle &cycle, const Array<int> &cycle_vertex_types, ObjArray < Array<int> > &chains_ext) const; // border functions void _getBorder (Cycle &border) const; void _splitBorder (int v1, int v2, Array<int> &part1v, Array<int> &part1e, Array<int> &part2v, Array<int> &part2e) const; bool _isPointOutside (const Vec2f &p) const; bool _isPointOutsideCycle (const Cycle &cycle, const Vec2f &p) const; static bool _edge_check (Graph &graph, int e_idx, void *context); // make tree of biconnected components (tree[i] - component incoming to vertex i or -1) static void _makeComponentsTree (BiconnectedDecomposer &decon, PtrArray<MoleculeLayoutGraph> &components, Array<int> &tree); void _layoutMultipleComponents (BaseMolecule & molecule, bool respect_existing, const Filter * filter, float bond_length); void _layoutSingleComponent (BaseMolecule &molecule, bool respect_existing, const Filter * filter, float bond_length); TL_DECL(ObjArray<PatternLayout>, _patterns); }; struct local_pair_ii { int left; int right; local_pair_ii(int l, int r) { left = l; right = r; } }; struct local_pair_id { int left; double right; local_pair_id(int l, double r) { left = l; right = r; } }; class MoleculeLayoutGraphSmart; class MoleculeLayoutMacrocycles; class MoleculeLayoutMacrocyclesLattice; class DLLEXPORT MoleculeLayoutSmoothingSegment { private: float _length; Array<Vec2f> _pos; int _finish_number; int _start_number; Vec2f& _start; Vec2f& _finish; Vec2f _center; int _layout_component_number; double _square; double _radius; Vec2f _getPosition(Vec2f); double calc_radius(Vec2f); public: MoleculeLayoutGraphSmart& _graph; MoleculeLayoutSmoothingSegment(MoleculeLayoutGraphSmart& mol, Vec2f& start, Vec2f& finish); Vec2f getPosition(int); Vec2f getIntPosition(int) const; void shiftStartBy(Vec2f shift); void shiftFinishBy(Vec2f shift); float getLength() const; float getLengthCoef() const; Vec2f getCenter(); Vec2f getIntCenter(); void updateStartFinish(); bool isVertexUp(int v); int get_layout_component_number(); void set_layout_component_number(int number); void inverse(); void set_start_finish_number(int, int); double get_square(); void calculate_square(); int get_start() const; int get_finish() const; double get_radius(); bool can_touch_to(MoleculeLayoutSmoothingSegment&); bool is_start(int v) { return v == _start_number; } bool is_finish(int v) { return v == _finish_number; } float get_min_x(); float get_min_y(); float get_max_x(); float get_max_y(); }; class DLLEXPORT SmoothingCycle { public: CP_DECL; SmoothingCycle(Array<Vec2f>&, Array<float>&); SmoothingCycle(Array<Vec2f>&, Array<float>&, Array<int>&, int); SmoothingCycle(Array<Vec2f>&, Array<float>&, ObjArray<MoleculeLayoutSmoothingSegment>&); int cycle_length; Array<Vec2f>& point; Array<float>& target_angle; MoleculeLayoutSmoothingSegment* segment; TL_CP_DECL(Array<float>, edge_length); bool is_simple_component(int i) { return segment == 0 || segment[i].get_layout_component_number() < 0; } float get_radius(int i) { return segment == 0 ? (point[(i + 1) % cycle_length] - point[i]).length() / 2 : segment[i].get_radius(); } Vec2f get_center(int i) { return segment == 0 ? (point[(i + 1) % cycle_length] + point[i]) / 2 : segment[i].getCenter(); } float get_length(int i) { return edge_length[i]; } DECL_ERROR; void _do_smoothing(int iter_count); void _gradient_step(float coef, Array<local_pair_ii>& touching_segments); static Vec2f _get_len_derivative(Vec2f current_vector, float target_dist); static Vec2f _get_len_derivative_simple(Vec2f current_vector, float target_dist); static Vec2f _get_angle_derivative(Vec2f left_point, Vec2f right_point, float target_angle); }; class DLLEXPORT MoleculeLayoutGraphSmart : public MoleculeLayoutGraph { public: explicit MoleculeLayoutGraphSmart(); virtual ~MoleculeLayoutGraphSmart(); MoleculeLayoutGraph* getInstance(); virtual void clear(); inline int getVertexOrigIdx(int idx) const { return _layout_vertices[idx].orig_idx; } inline int getEdgeOrigIdx(int idx) const { return _layout_edges[idx].orig_idx; } inline bool isEdgeDrawn(int idx) const { return _layout_edges[idx].type != ELEMENT_NOT_DRAWN; } inline bool isVertexDrawn(int idx) const { return _layout_vertices[idx].type != ELEMENT_NOT_DRAWN; } float calculateAngle(int v, int &v1, int &v2) const; void makeLayoutSubgraph(MoleculeLayoutGraph &graph, Filter &vertex_filter); void makeLayoutSubgraph(MoleculeLayoutGraph &graph, Filter &vertex_filter, Filter *edge_filter); void layout(BaseMolecule &molecule, float bond_length, const Filter *filter, bool respect_existing); void calcMorganCode(); long getMorganCode(); void assignFirstVertex(int v); const BaseMolecule *getMolecule(const int **molecule_edge_mapping) { *molecule_edge_mapping = _molecule_edge_mapping; return _molecule; } const BaseMolecule *getMolecule() { return _molecule; } const int *getEdgeMapping() { return _molecule_edge_mapping; } double _get_square(); void flipped() { ; } #ifdef M_LAYOUT_DEBUG void saveDebug(); #endif DECL_ERROR; protected: // THERE // for whole graph void _assignAbsoluteCoordinates(float bond_length); // for components void _calcMorganCodes(); // assigning coordinates struct interval { int left; int right; void init(int _l, int _r) { left = _l; right = _r; } interval(int _l, int _r) { init(_l, _r); } }; void _assignRelativeCoordinates(int &fixed_component, const MoleculeLayoutGraph &supergraph); void _get_toches_to_component(Cycle& cycle, int component_number, Array<interval>& interval_list); int _search_separated_component(Cycle& cycle, Array<interval>& interval_list); void _search_path(int start, int finish, Array<int>& path, int component_number); void _assignEveryCycle(const Cycle& cycle); // smoothing void _segment_smoothing(const Cycle &cycle, const MoleculeLayoutMacrocyclesLattice &layout, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _update_touching_segments(Array<local_pair_ii >&, ObjArray<MoleculeLayoutSmoothingSegment> &); void _segment_smoothing_prepearing(const Cycle &cycle, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment, MoleculeLayoutMacrocyclesLattice& layout); void _segment_calculate_target_angle(const MoleculeLayoutMacrocyclesLattice &layout, Array<int> &rotation_vertex, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _segment_update_rotation_points(const Cycle &cycle, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _segment_smoothing_unstick(ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _do_segment_smoothing(Array<Vec2f> &rotation_point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _segment_improoving(Array<Vec2f> &rotation_point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment, int, float, Array<local_pair_ii>&); void _do_segment_smoothing_gradient(Array<Vec2f> &rotation_point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment); void _gradient_step(Array<Vec2f> &point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment, float coef, Array<local_pair_ii>& touching_segments); void _attachEars(int vert_idx, int drawn_idx, int *ears, const Vec2f &rest_pos); // attaching cycles bool _attachCycleOutside(const Cycle &cycle, float length, int n_common); bool _drawEdgesWithoutIntersection(const Cycle &cycle, Array<int> & cycle_vertex_types); bool _attachCycleInside(const Cycle &cycle, float length); bool _attachCycleWithIntersections(const Cycle &cycle, float length); void _setChainType(const Array<int> &chain, const Array<int> &mapping, int type); bool _splitCycle(const Cycle &cycle, const Array<int> &cycle_vertex_types, bool check_boundary, Array<int> &chain_ext, Array<int> &chain_int, int &c_beg, int &c_end) const; void _splitCycle2(const Cycle &cycle, const Array<int> &cycle_vertex_types, ObjArray < Array<int> > &chains_ext) const; // border functions void _getBorder(Cycle &border) const; void _getSurroundCycle(Cycle &cycle, Vec2f p) const; void _splitBorder(int v1, int v2, Array<int> &part1v, Array<int> &part1e, Array<int> &part2v, Array<int> &part2e) const; bool _isPointOutside(const Vec2f &p) const; bool _isPointOutsideCycle(const Cycle &cycle, const Vec2f &p) const; // geometry functions const float _energyOfPoint(Vec2f p) const; int _isCisConfiguratuin(Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4); // make tree of biconnected components (tree[i] - -1 or component incoming to vertex i) static void _makeComponentsTree(BiconnectedDecomposer &decon, PtrArray<MoleculeLayoutGraph> &components, Array<int> &tree); void _layoutMultipleComponents(BaseMolecule & molecule, bool respect_existing, const Filter * filter, float bond_length); void _layoutSingleComponent(BaseMolecule &molecule, bool respect_existing, const Filter * filter, float bond_length); Array<int> _layout_component_number; // number of layout component of certain edge int _layout_component_count; MoleculeLayoutGraph *_graph; private: MoleculeLayoutGraphSmart(const MoleculeLayoutGraphSmart&); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/molecule_layout_macrocycles.h��������������������������������������������0000664�0000000�0000000�00000025052�12710376503�0023461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_layout_macrocycles_h__ #define __molecule_layout_macrocycles_h__ #include "molecule/molecule.h" #include "layout/molecule_layout_graph.h" #include <algorithm> #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif using namespace std; namespace indigo { static const unsigned short SHORT_INFINITY = 60000; static const int WEIGHT_FACTOR = 12; static const int SIX = 6; static int get_weight(int weight, int rotate) { if (abs(weight) <= WEIGHT_FACTOR) return 0; return max(0, weight * (rotate > 0 ? -1 : 1)); } class DLLEXPORT MoleculeLayoutMacrocycles { public: CP_DECL; MoleculeLayoutMacrocycles(int size); void addVertexOutsideWeight(int v, int weight); void setVertexEdgeParallel(int v, bool parallel); void set_vertex_added_square(int v, double s); void setEdgeStereo(int e, int stereo); void setVertexDrawn(int v, bool drawn); void set_component_finish(int v, int f); void set_target_angle(int v, double angle); void set_angle_importance(int, double); int getVertexStereo(int v); Vec2f &getPos(int v) const; void doLayout(); // private: public: static bool canApply(BaseMolecule &mol); double layout(BaseMolecule &mol); void smoothing(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p, bool profi); void smoothing2(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p); void improvement2(int i, int vertex_count, int cycle_size, int *rotate_angle, int *edge_lenght, int *vertex_number, Vec2f *p, int base_vertex, bool fix_angle, bool fix_next, double multiplyer); double badness(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p, int diff); double depictionMacrocycleGreed(bool profi); double depictionCircle(); DECL_ERROR; private: int length; static const int max_size; static const int init_x; static const int init_y; static const int init_rot; static const double CHANGE_FACTOR; int get_diff_grid(int x, int y, int rot, int value); int get_diff_circle(int x, int y, int rot, int value); struct Data { enum { max_size = 105 }; unsigned short minRotates[max_size][max_size][2][max_size][max_size]; }; TL_CP_DECL(Data, data); TL_CP_DECL(Array<int>, _vertex_weight); TL_CP_DECL(Array<int>, _vertex_stereo); TL_CP_DECL(Array<double>, _vertex_added_square); TL_CP_DECL(Array<int>, _edge_stereo); TL_CP_DECL(Array<bool>, _vertex_drawn); TL_CP_DECL(Array<Vec2f>, _positions); TL_CP_DECL(Array<int>, _component_finish); TL_CP_DECL(Array<double>, _target_angle); TL_CP_DECL(Array<double>, _angle_importance); }; struct answer_point; class DLLEXPORT MoleculeLayoutMacrocyclesLattice { public: CP_DECL; MoleculeLayoutMacrocyclesLattice(int size); void doLayout(); void addVertexOutsideWeight(int v, int weight); void setVertexEdgeParallel(int v, bool parallel); bool getVertexStereo(int v); void setEdgeStereo(int e, int stereo); void setVertexAddedSquare(int v, double s); void setVertexDrawn(int v, bool drawn); void setComponentFinish(int v, int f); void setTargetAngle(int v, double angle); void setAngleImportance(int, double); class DLLEXPORT CycleLayout { CP_DECL; public: int vertex_count; TL_CP_DECL(Array<Vec2f>, point); TL_CP_DECL(Array<int>, rotate); TL_CP_DECL(Array<int>, external_vertex_number); TL_CP_DECL(Array<int>, edge_length); //TL_CP_DECL(Array<int>, component_finish); CycleLayout(); void initStatic(); void init(answer_point* points); void init(int* up); double area(); double perimeter(); Vec2f getWantedVector(int vertex_number); void soft_move_vertex(int vertex_number, Vec2f move_vector); void stright_rotate_chein(int vertex_number, double angle); void stright_move_chein(int vertex_number, Vec2f vector); DECL_ERROR; }; void initCycleLayout(CycleLayout& cl); int internalValue(CycleLayout& cl); double rating(CycleLayout& cl); int period(CycleLayout& cl); bool is_period(CycleLayout& cl, int k); void closingStep(CycleLayout &cl, int index, int base_vertex, bool fix_angle, bool fix_next, double multiplyer); void closing(CycleLayout &cl); void updateTouchingPoints(Array<local_pair_id>&, CycleLayout&); void smoothingStep(CycleLayout &cl, int vertex_number, double coef, Array<local_pair_id>&); void smoothing(CycleLayout &cl); Vec2f &getPos(int v) const; double preliminary_layout(CycleLayout &cl); DECL_ERROR; private: int length; int rotate_length; static const double SMOOTHING_MULTIPLIER; static const double CHANGE_FACTOR; void calculate_rotate_length(); void rotate_cycle(int shift); void _rotate_ar_i(Array<int>& ar, Array<int>& tmp, int shift); void _rotate_ar_d(Array<double>& ar, Array<double>& tmp, int shift); void _rotate_ar_v(Array<Vec2f>& ar, Array<Vec2f>& tmp, int shift); //double rating(Array<answer_point>); TL_CP_DECL(Array<int>, _vertex_weight); TL_CP_DECL(Array<int>, _vertex_stereo); TL_CP_DECL(Array<int>, _edge_stereo); TL_CP_DECL(Array<Vec2f>, _positions); TL_CP_DECL(Array<double>, _vertex_added_square); TL_CP_DECL(Array<bool>, _vertex_drawn); TL_CP_DECL(Array<int>, _component_finish); TL_CP_DECL(Array<double>, _target_angle); TL_CP_DECL(Array<double>, _angle_importance); }; struct rectangle { int min_x; int max_x; int min_y; int max_y; bool empty; rectangle() { set_empty(); } rectangle(int x1, int x2, int y1, int y2) { set(x1, x2, y1, y2); } void set(int x1, int x2, int y1, int y2) { min_x = x1; max_x = x2; min_y = y1; max_y = y2; empty = (min_x > max_x) || (min_y > max_y); } void intersec(int x1, int x2, int y1, int y2) { set(max(min_x, x1), min(max_x, x2), max(min_y, y1), min(max_y, y2)); } void set(rectangle rec) { set(rec.min_x, rec.max_x, rec.min_y, rec.max_y); } rectangle intersec(rectangle rec) { intersec(rec.min_x, rec.max_x, rec.min_y, rec.max_y); return *this; } rectangle expand(int w) { if (empty) return rectangle(1, 0, 1, 0); else return rectangle(min_x - w, max_x + w, min_y - w, max_y + w); } rectangle expand() { return expand(1); } void set_empty() { empty = true; } rectangle shift(int x, int y) { return rectangle(min_x + x, max_x + x, min_y + y, max_y + y); } bool contains(int x, int y) { return !empty && x >= min_x && x <= max_x && y >= min_y && y <= max_y; } static rectangle square(int x, int y, int radius) { return rectangle(x - radius, x + radius, y - radius, y + radius); } }; class DLLEXPORT TriangleLattice { public: TriangleLattice(); TriangleLattice(rectangle rec, int rem, byte* data_link); void init(rectangle rec, int rem, byte* data_link); void init_void(); unsigned short& getCell(int x, int y); bool isValid(int x, int y); int getFirstValidY(int x); bool isIncreaseForValidY(int); int getFirstValidX(); bool isIncreaseForValidX(int); void switchNextY(int&); static int getAllocationSize(rectangle rec) { if (rec.empty) return 0; int sq = (((rec.max_x - rec.min_x + 1) * (rec.max_y - rec.min_y + 1) + 2) / 3) * sizeof(unsigned short) + (rec.max_x - rec.min_x + 1) * sizeof(unsigned short*); return sq; }; DECL_ERROR; private: int _difference_reminder; unsigned short** _starts; unsigned short _sink = SHORT_INFINITY; rectangle _BORDER; }; class DLLEXPORT AnswerField { public: CP_DECL; AnswerField(int len, int target_x, int target_y, double target_rotation, int* vertex_weight_link, int* vertex_stereo_link, int* edge_stereo_link); void fill(); unsigned short& get_field(int len, answer_point p); unsigned short& get_field(answer_point p); void _restore_path(answer_point* point, answer_point finish); TriangleLattice& getLattice(int l, int rot, int p); static int _cmp_answer_points(answer_point& p1, answer_point& p2, void* context); DECL_ERROR; private: const int ACCEPTABLE_ERROR = 10; int length; byte* _hidden_data_field; ObjArray<Array<rectangle> > border_array; Array<rectangle*> border; TL_CP_DECL(Array<int>, _vertex_weight); TL_CP_DECL(Array<int>, _vertex_stereo); TL_CP_DECL(Array<int>, _edge_stereo); TL_CP_DECL(Array<int>, _rotation_parity); TL_CP_DECL(Array<int>, _coord_diff_reminder); // (x - y) % 3 TL_CP_DECL(ObjArray<ObjArray<ObjArray<TriangleLattice>>>, _lattices); TL_CP_DECL(Array<byte>, _hidden_data_field_array); TriangleLattice _sink_lattice; }; struct answer_point { int rot; int p; int x; int y; answer_point() { set(0, 0, 0, 0); } answer_point(int _rot, int _p, int _x, int _y) { set(_rot, _p, _x, _y); } void set(int _rot, int _p, int _x, int _y) { rot = _rot; p = _p; x = _x; y = _y; } const int quality(AnswerField& fld) const { int diffCoord = (x * y >= 0) ? abs(x) + abs(y) : max(abs(x), abs(y)); return diffCoord + 2*abs(rot - SIX) + fld.get_field(*this); } }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/patmake/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0017140�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/patmake/patmake.cpp������������������������������������������������������0000664�0000000�0000000�00000021014�12710376503�0021264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "graph/biconnected_decomposer.h" #include "molecule/molecule.h" #include "molecule/molfile_loader.h" int edge_cmp (const int &e1, const int &e2, const void *context) { Molecule &mol = *(Molecule *)context; const Edge &edge1 = mol.getEdge(e1); const Edge &edge2 = mol.getEdge(e2); Vec3f v1 = mol.getAtomPos(edge1.beg); Vec3f v2 = mol.getAtomPos(edge1.end); v1.z = 0.f; v2.z = 0.f; float len1 = Vec3f::dist(v1, v2); v1 = mol.getAtomPos(edge2.beg); v2 = mol.getAtomPos(edge2.end); v1.z = 0.f; v2.z = 0.f; float len2 = Vec3f::dist(v1, v2); if (len2 > len1) return 1; else if (len2 < len1) return -1; return 0; } bool edge_intersection (const Molecule &mol, int edge1_idx, int edge2_idx, Vec2f &p) { const Edge &edge1 = mol.getEdge(edge1_idx); const Edge &edge2 = mol.getEdge(edge2_idx); if (edge1.beg == edge2.beg || edge1.beg == edge2.end || edge1.end == edge2.beg || edge1.end == edge2.end) return false; Vec2f v1_1(mol.getAtomPos(edge1.beg).x, mol.getAtomPos(edge1.beg).y); Vec2f v1_2(mol.getAtomPos(edge1.end).x, mol.getAtomPos(edge1.end).y); Vec2f v2_1(mol.getAtomPos(edge2.beg).x, mol.getAtomPos(edge2.beg).y); Vec2f v2_2(mol.getAtomPos(edge2.end).x, mol.getAtomPos(edge2.end).y); return Vec2f::intersection(v1_1, v1_2, v2_1, v2_2, p); } void convertMolfile (char *path, char *filename, FileOutput &cpp_file) { FileScanner molfile("%s\\%s", path, filename); MolfileLoader mf_loader(molfile); Molecule mol; QS_DEF(Array<int>, edges); printf("%s\n", filename); mf_loader.loadMolecule(mol, true); BiconnectedDecomposer bd(mol); if (bd.decompose() != 1) { printf("Error: %s is not biconnected\n", filename); return; } int i, j; edges.clear_reserve(mol.edgeCount()); for (i = mol.edgeBegin() ; i < mol.edgeEnd(); i = mol.edgeNext(i)) edges.push(i); edges.qsort(edge_cmp, &mol); const Edge &edge = mol.getEdge(edges[edges.size() / 2]); Vec3f v1 = mol.getAtomPos(edge.beg); Vec3f v2 = mol.getAtomPos(edge.end); v1.z = 0.f; v2.z = 0.f; float scale = Vec3f::dist(v1, v2); if (scale < 0.0001f) { printf("Error: %s has zero bond\n", filename); return; } scale = 1.f / scale; int first_idx = mol.vertexBegin(); Vec3f pos = mol.getAtomPos(first_idx); for (i = mol.vertexNext(first_idx); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.getAtomPos(i).y < pos.y) { pos = mol.getAtomPos(i); first_idx = i; } } for (i = mol.vertexBegin() ; i < mol.vertexEnd(); i = mol.vertexNext(i)) { mol.getAtom2(i).pos.sub(pos); mol.getAtom2(i).pos.scale(scale); } char buf[1024]; sprintf_s(buf, "BEGIN_PATTERN(\"%s\")", filename); cpp_file.writeStringCR(buf); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { sprintf_s(buf, " ADD_ATOM(%d, %ff, %ff)", i, mol.getAtomPos(i).x, mol.getAtomPos(i).y); cpp_file.writeStringCR(buf); } for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); int type = mol.getBond(i).type; int qtype = mol.getQueryBond(i).type; sprintf_s(buf, " ADD_BOND(%d, %d, %d)", edge.beg, edge.end, qtype != 0 ? qtype : type); cpp_file.writeStringCR(buf); } Vec2f v, inter; Vec2f pos_i; int idx = mol.vertexCount(); i = first_idx; float max_angle, cur_angle; float i_angle = 0; int next_nei = 0; int point_idx = 0; pos_i.set(mol.getAtomPos(i).x, mol.getAtomPos(i).y); while (true) { const Vertex &vert = mol.getVertex(i); if (i != first_idx) { v.set(pos_i.x, pos_i.y); pos_i.set(mol.getAtomPos(i).x, mol.getAtomPos(i).y); v.sub(pos_i); i_angle = v.tiltAngle2(); } else if (point_idx > 0) break; sprintf_s(buf, " OUTLINE_POINT(%d, %ff, %ff)", point_idx++, pos_i.x, pos_i.y); cpp_file.writeStringCR(buf); max_angle = 0.f; for (j = vert.neiBegin(); j < vert.neiEnd(); j = vert.neiNext(j)) { const Vec3f &pos_nei = mol.getAtomPos(vert.neiVertex(j)); v.set(pos_nei.x - pos_i.x, pos_nei.y - pos_i.y); cur_angle = v.tiltAngle2() - i_angle; if (cur_angle < 0.f) cur_angle += 2 * PI; if (max_angle < cur_angle) { max_angle = cur_angle; next_nei = j; } } i = vert.neiVertex(next_nei); float dist, min_dist = 0.f; int int_edge; Vec2f cur_v1 = pos_i; Vec2f cur_v2(mol.getAtomPos(i).x, mol.getAtomPos(i).y); while (min_dist < 10000.f) { min_dist = 10001.f; for (j = mol.edgeBegin(); j < mol.edgeEnd(); j = mol.edgeNext(j)) { const Edge &edge = mol.getEdge(j); Vec2f cur_v3(mol.getAtomPos(edge.beg).x, mol.getAtomPos(edge.beg).y); Vec2f cur_v4(mol.getAtomPos(edge.end).x, mol.getAtomPos(edge.end).y); if (Vec2f::intersection(cur_v1, cur_v2, cur_v3, cur_v4, v)) if ((dist = Vec2f::dist(cur_v1, v)) < min_dist) { inter = v; min_dist = dist; int_edge = j; } } if (min_dist < 10000.f) { sprintf_s(buf, " OUTLINE_POINT(%d, %ff, %ff)", point_idx++, v.x, v.y); cpp_file.writeStringCR(buf); const Edge &edge = mol.getEdge(int_edge); Vec2f cur_v3(mol.getAtomPos(edge.beg).x, mol.getAtomPos(edge.beg).y); Vec2f cur_v4(mol.getAtomPos(edge.end).x, mol.getAtomPos(edge.end).y); Vec2f cur_v1v; Vec2f cur_v3v; Vec2f cur_v4v; cur_v1v.diff(cur_v1, inter); cur_v3v.diff(cur_v3, inter); cur_v4v.diff(cur_v4, inter); float angle1 = cur_v1v.tiltAngle2(); float angle3 = cur_v3v.tiltAngle2() - angle1; float angle4 = cur_v4v.tiltAngle2() - angle1; if (angle3 < 0) angle3 += 2 * PI; if (angle4 < 0) angle4 += 2 * PI; cur_v1 = inter; if (angle3 > angle4) { cur_v2 = cur_v3; i = edge.beg; } else { cur_v2 = cur_v4; i = edge.end; } } } } cpp_file.writeStringCR("END_PATTERN()"); } #include <windows.h> void main (int argc, char *argv[]) { WIN32_FIND_DATAA ffd; HANDLE h_find; char file_pattern[MAX_PATH]; if (argc != 3) { printf("patmake.exe <mol-root> <cpp-output>"); return; } if (argv[1][strlen(argv[1]) - 1] == '\\') argv[1][strlen(argv[1]) - 1] = 0; sprintf_s(file_pattern, "%s\\*.mol", argv[1]); h_find = FindFirstFileA(file_pattern, &ffd); if (h_find == INVALID_HANDLE_VALUE) { printf ("FindFirstFile failed (%d)\n", GetLastError()); return; } else { try { FileOutput cpp_file(true, argv[2]); SYSTEMTIME st; GetSystemTime(&st); char buf[200]; sprintf_s(buf, " * Added %02d/%02d/%02d %02d:%02d:%02d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); cpp_file.writeStringCR("/*"); cpp_file.writeStringCR(buf); cpp_file.writeStringCR(" */"); do { try { convertMolfile(argv[1], ffd.cFileName, cpp_file); } catch (Exception &e) { printf("Error: %s\n", e.message()); } } while (FindNextFileA(h_find, &ffd) != 0); printf("Done.\n"); } catch (Exception &e) { printf("Error: %s\n", e.message()); } FindClose(h_find); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/patmake/patmake.sln������������������������������������������������������0000664�0000000�0000000�00000004036�12710376503�0021303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "patmake", "patmake.vcproj", "{A5395DB0-0372-45FB-8415-346F936D3EBD}" ProjectSection(ProjectDependencies) = postProject {D69C5B23-48C2-400B-9BCD-B8276756B673} = {D69C5B23-48C2-400B-9BCD-B8276756B673} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "molecule", "..\..\molecule\molecule.vcproj", "{D69C5B23-48C2-400B-9BCD-B8276756B673}" ProjectSection(ProjectDependencies) = postProject {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5} = {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "graph", "..\..\graph\graph.vcproj", "{C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A5395DB0-0372-45FB-8415-346F936D3EBD}.Debug|Win32.ActiveCfg = Debug|Win32 {A5395DB0-0372-45FB-8415-346F936D3EBD}.Debug|Win32.Build.0 = Debug|Win32 {A5395DB0-0372-45FB-8415-346F936D3EBD}.Release|Win32.ActiveCfg = Release|Win32 {A5395DB0-0372-45FB-8415-346F936D3EBD}.Release|Win32.Build.0 = Release|Win32 {D69C5B23-48C2-400B-9BCD-B8276756B673}.Debug|Win32.ActiveCfg = Debug|Win32 {D69C5B23-48C2-400B-9BCD-B8276756B673}.Debug|Win32.Build.0 = Debug|Win32 {D69C5B23-48C2-400B-9BCD-B8276756B673}.Release|Win32.ActiveCfg = Release|Win32 {D69C5B23-48C2-400B-9BCD-B8276756B673}.Release|Win32.Build.0 = Release|Win32 {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5}.Debug|Win32.ActiveCfg = Debug|Win32 {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5}.Debug|Win32.Build.0 = Debug|Win32 {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5}.Release|Win32.ActiveCfg = Release|Win32 {C6C11F43-43CD-4E5A-9FC5-B113A8B6F4F5}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/patmake/patmake.vcproj���������������������������������������������������0000664�0000000�0000000�00000006656�12710376503�0022024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="windows-1251"?> <VisualStudioProject ProjectType="Visual C++" Version="9.00" Name="patmake" ProjectGUID="{A5395DB0-0372-45FB-8415-346F936D3EBD}" RootNamespace="patmake" Keyword="Win32Proj" TargetFrameworkVersion="196613" > <Platforms> <Platform Name="Win32" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="$(SolutionDir)$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../..;../../common" PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="$(SolutionDir)$(ConfigurationName)" IntermediateDirectory="$(ConfigurationName)" ConfigurationType="1" CharacterSet="1" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="../..;../../common" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="src" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > <File RelativePath=".\patmake.cpp" > </File> </Filter> </Files> <Globals> </Globals> </VisualStudioProject> ����������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/reaction_layout.h��������������������������������������������������������0000664�0000000�0000000�00000003216�12710376503�0021072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_layout__ #include "layout/metalayout.h" namespace indigo { class Reaction; class Molecule; struct Vec2f; class ReactionLayout { public: explicit ReactionLayout (BaseReaction& r, bool smart_layout=false); void make (); float bond_length; float plus_interval_factor; float arrow_interval_factor; bool preserve_molecule_layout; int max_iterations; bool _smart_layout; private: Metalayout::LayoutItem& _pushMol (Metalayout::LayoutLine& line, int id); Metalayout::LayoutItem& _pushSpace (Metalayout::LayoutLine& line, float size); BaseMolecule& _getMol (int id); void _shiftMol(const Metalayout::LayoutItem& item, const Vec2f& pos); void _make (); static BaseMolecule& cb_getMol (int id, void* context); static void cb_process (Metalayout::LayoutItem& item, const Vec2f& pos, void* context); ReactionLayout (const ReactionLayout& r); // no implicit copy BaseReaction& _r; Metalayout _ml; }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/refinement_state.h�������������������������������������������������������0000664�0000000�0000000�00000003134�12710376503�0021224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __refinement_state_h__ #define __refinement_state_h__ #include "layout/molecule_layout_graph.h" namespace indigo { struct RefinementState { explicit RefinementState (MoleculeLayoutGraph &graph); void calcHeight (); void calcDistance (int v1, int v2); void calcEnergy (); void copy (const RefinementState &other);//existing states void copyFromGraph (); void applyToGraph (); void flipBranch (const Filter &branch, const RefinementState &state, int v1_idx, int v2_idx); void rotateBranch (const Filter &branch, const RefinementState &state, int v_idx, float angle); void stretchBranch (const Filter &branch, const RefinementState &state, int v1, int v2, int d); void rotateLayout (const RefinementState &state, int v_idx, float angle); bool is_small_cycle(); float dist; double energy; float height; CP_DECL; TL_CP_DECL(Array<Vec2f>, layout); DECL_ERROR; private: MoleculeLayoutGraph &_graph; }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016305�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/attachment_layout.cpp������������������������������������������������0000664�0000000�0000000�00000033505�12710376503�0022544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/attachment_layout.h" using namespace indigo; CP_DEF(AttachmentLayout); AttachmentLayout::AttachmentLayout(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex) : _src_vertex(src_vertex), CP_INIT, TL_CP_GET(_src_vertex_map), TL_CP_GET(_attached_bc), TL_CP_GET(_bc_angles), TL_CP_GET(_vertices_l), _alpha(0.f), TL_CP_GET(_new_vertices), TL_CP_GET(_layout), _energy(0.f), _bc_components(bc_components), _graph(graph) { int i, v1, v2; float sum = 0.f; int n_comp = bc_decom.getIncomingCount(_src_vertex); if (bc_tree[_src_vertex] != -1) { _attached_bc.clear_resize(n_comp + 1); _attached_bc.top() = bc_tree[_src_vertex]; } else _attached_bc.clear_resize(n_comp); _src_vertex_map.clear_resize(_attached_bc.size()); _bc_angles.clear_resize(_attached_bc.size()); _vertices_l.clear_resize(_attached_bc.size()); for (i = 0; i < _attached_bc.size(); i++) { if (i < n_comp) _attached_bc[i] = bc_decom.getIncomingComponents(_src_vertex)[i]; const MoleculeLayoutGraph &cur_bc = *bc_components[_attached_bc[i]]; _src_vertex_map[i] = cur_bc.findVertexByExtIdx(_src_vertex); _bc_angles[i] = cur_bc.calculateAngle(_src_vertex_map[i], v1, v2); sum += _bc_angles[i]; if (cur_bc.isFlipped()) _vertices_l[i] = v2; else _vertices_l[i] = v1; } _alpha = (2 * PI - sum) / _attached_bc.size(); //TODO: what if negative? // find the one component which is drawn and put it to the end for (i = 0; i < _attached_bc.size() - 1; i++) { if (_graph.getVertexType(_bc_components[_attached_bc[i]]->getVertexExtIdx(_vertices_l[i])) != ELEMENT_NOT_DRAWN) { _src_vertex_map.swap(i, _attached_bc.size() - 1); _attached_bc.swap(i, _attached_bc.size() - 1); _bc_angles.swap(i, _attached_bc.size() - 1); _vertices_l.swap(i, _attached_bc.size() - 1); break; } } int n_new_vert = 0; for (i = 0; i < _attached_bc.size() - 1; i++) n_new_vert += _bc_components[_attached_bc[i]]->vertexCount() - 1; _new_vertices.clear_resize(n_new_vert); _layout.clear_resize(n_new_vert); _layout.zerofill(); } AttachmentLayoutSimple::AttachmentLayoutSimple(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex) : AttachmentLayout(bc_decom, bc_components, bc_tree, graph, src_vertex) { } AttachmentLayoutSmart::AttachmentLayoutSmart(const BiconnectedDecomposer &bc_decom, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree, MoleculeLayoutGraph &graph, int src_vertex) : AttachmentLayout(bc_decom, bc_components, bc_tree, graph, src_vertex) { } // Calculate energy of the drawn part of graph double AttachmentLayout::calculateEnergy () { int i, j; double sum_a; float r; QS_DEF(Array<double>, norm_a); QS_DEF(Array<int>, drawn_vertices); drawn_vertices.clear_resize(_graph.vertexEnd()); drawn_vertices.zerofill(); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) if (_graph.getLayoutVertex(i).type != ELEMENT_NOT_DRAWN) drawn_vertices[i] = 1; for (i = 0; i < _new_vertices.size(); i++) drawn_vertices[_new_vertices[i]] = 2 + i; norm_a.clear_resize(_graph.vertexEnd()); sum_a = 0.0; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (drawn_vertices[i] > 0) { norm_a[i] = _graph.getLayoutVertex(i).morgan_code; sum_a += norm_a[i] * norm_a[i]; } } sum_a = sqrt(sum_a); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) if (drawn_vertices[i] > 0) norm_a[i] = (norm_a[i] / sum_a) + 0.5; _energy = 0.0; const Vec2f *pos_i = 0; const Vec2f *pos_j = 0; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) if (drawn_vertices[i] > 0) { if (drawn_vertices[i] == 1) pos_i = &_graph.getPos(i); else pos_i = &_layout[drawn_vertices[i] - 2]; for (j = _graph.vertexBegin(); j < _graph.vertexEnd(); j = _graph.vertexNext(j)) if (drawn_vertices[j] > 0 && i != j) { if (drawn_vertices[j] == 1) pos_j = &_graph.getPos(j); else pos_j = &_layout[drawn_vertices[j] - 2]; r = Vec2f::distSqr(*pos_i, *pos_j); if (r < EPSILON) r = EPSILON; _energy += (norm_a[i] * norm_a[j] / r); } } return _energy; } void AttachmentLayoutSimple::applyLayout () { int i; for (i = 0; i < _new_vertices.size(); i++) _graph.getPos(_new_vertices[i]) = _layout[i]; } void AttachmentLayoutSmart::applyLayout() { int i; for (i = 0; i < _new_vertices.size(); i++) _graph.getPos(_new_vertices[i]) = _layout[i]; for (int i = 0; i < _attached_bc.size(); i++) { MoleculeLayoutGraph &comp = (MoleculeLayoutGraph &)*_bc_components[_attached_bc[i]]; for (int v = comp.vertexBegin(); v != comp.vertexEnd(); v = comp.vertexNext(v)) { comp.getPos(v) = _graph.getPos(comp.getVertexExtIdx(v)); } } } void AttachmentLayout::markDrawnVertices() { int i, j; for (i = 0; i < _attached_bc.size(); i++) { const MoleculeLayoutGraph &comp = *_bc_components[_attached_bc[i]]; for (j = comp.vertexBegin(); j < comp.vertexEnd(); j = comp.vertexNext(j)) { const LayoutVertex &vert = comp.getLayoutVertex(j); _graph.setVertexType(vert.ext_idx, vert.type); } for (j = comp.edgeBegin(); j < comp.edgeEnd(); j = comp.edgeNext(j)) { const LayoutEdge &edge = comp.getLayoutEdge(j); _graph.setEdgeType(edge.ext_idx, edge.type); } } } CP_DEF(LayoutChooser); LayoutChooser::LayoutChooser(AttachmentLayout &layout) : _n_components(layout._attached_bc.size() - 1), _cur_energy(1E+20f), CP_INIT, TL_CP_GET(_comp_permutation), TL_CP_GET(_rest_numbers), _layout(layout) { _comp_permutation.clear_resize(_n_components); _rest_numbers.clear_resize(_n_components); for (int i = 0; i < _n_components; i++) _rest_numbers[i] = i; } // Look through all perturbations recursively void LayoutChooser::_perform (int level) { int i; if (level == 0) { // Try current perturbation // Draw new components on vertex _makeLayout(); // Check if new layout is better if (_layout.calculateEnergy() < _cur_energy - EPSILON) { _layout.applyLayout(); _cur_energy = _layout._energy; } return; } for (i = 0; i < level; i++) { _comp_permutation[level - 1] = _rest_numbers[i]; _rest_numbers[i] = _rest_numbers[level - 1]; _rest_numbers[level - 1] = _comp_permutation[level - 1]; _perform(level - 1); _rest_numbers[level - 1] = _rest_numbers[i]; _rest_numbers[i] = _comp_permutation[level - 1]; } } // Draw components connected with respect to current order void LayoutChooser::_makeLayout () { int i, j, k; float cur_angle; int v1C, v2C, v1, v2; Vec2f p, p1; float phi, phi1, phi2; float cosa, sina; int v; // cur_angle - angle between first edge of the drawn component and most "right" edge of current component k = -1; v = _layout._src_vertex; cur_angle = _layout._bc_angles[_n_components]; v2 = _layout._bc_components[_layout._attached_bc[_n_components]]->getVertexExtIdx(_layout._vertices_l[_n_components]); // number of the last vertex of drown biconnected component in the connected component for (i = 0; i < _n_components; i++) { cur_angle += _layout._alpha; // Shift and rotate component so cur_angle is the angle between [v1C,v2C] and drawn edge [v,v2] int comp_idx = _comp_permutation[i]; const MoleculeLayoutGraph &comp = *_layout._bc_components[_layout._attached_bc[comp_idx]]; v1C = _layout._src_vertex_map[comp_idx]; v2C = _layout._vertices_l[comp_idx]; v1 = comp.getVertexExtIdx(v2C); // Calculate angle (phi2-phi1) between [v,v2] and [v=v1C,v2C] in CCW order; p.diff(_layout._graph.getPos(v2), _layout._graph.getPos(v)); phi1 = p.tiltAngle(); p.diff(comp.getPos(v2C), comp.getPos(v1C)); phi2 = p.tiltAngle(); // Save current component's coordinates phi = cur_angle - (phi2 - phi1); cosa = cos(phi); sina = sin(phi); p.diff(_layout._graph.getPos(v), comp.getPos(v1C)); for (j = comp.vertexBegin(); j < comp.vertexEnd(); j = comp.vertexNext(j)) if (comp.getVertexExtIdx(j) != v) { k = k + 1; Vec2f &cur_pos = _layout._layout[k]; // 1. Shift cur_pos.sum(comp.getPos(j), p); // 2. Rotate around v p1.diff(cur_pos, _layout._graph.getPos(v)); p1.rotate(sina, cosa); cur_pos.sum(p1, _layout._graph.getPos(v)); _layout._new_vertices[k] = comp.getVertexExtIdx(j); } cur_angle += _layout._bc_angles[comp_idx]; } // respect cis/trans const int *molecule_edge_mapping = 0; const BaseMolecule *molecule = _layout._graph.getMolecule(&molecule_edge_mapping); const MoleculeLayoutGraph &drawn_comp = *_layout._bc_components[_layout._attached_bc[1]]; MoleculeLayoutGraph &attach_comp = (MoleculeLayoutGraph &)*_layout._bc_components[_layout._attached_bc[0]]; if (_n_components == 1 && molecule != 0 && drawn_comp.isSingleEdge()) { int drawn_idx = drawn_comp.edgeBegin(); int drawn_ext_idx = drawn_comp.getEdgeExtIdx(drawn_idx); int parity = molecule->cis_trans.getParity(_layout._graph.getEdgeExtIdx(drawn_ext_idx)); if (parity != 0) { int substituents[4]; ((BaseMolecule *)molecule)->cis_trans.getSubstituents_All(_layout._graph.getEdgeExtIdx(drawn_ext_idx), substituents); int drawn_substituent = -1; int drawn_substituent_idx = -1; int to_draw_substituent = -1; int to_draw_substituent_idx = -1; int drawn_end_idx = -1; const Vertex &vert = attach_comp.getVertex(_layout._src_vertex_map[0]); to_draw_substituent_idx = attach_comp.getVertexExtIdx(vert.neiVertex(vert.neiBegin())); drawn_end_idx = drawn_comp.getVertexExtIdx(drawn_comp.vertexBegin()); if (drawn_end_idx == _layout._src_vertex) drawn_end_idx = drawn_comp.getVertexExtIdx(drawn_comp.vertexNext(drawn_comp.vertexBegin())); const Vertex &drawn_end = _layout._graph.getVertex(drawn_end_idx); drawn_substituent_idx = drawn_end.neiVertex(drawn_end.neiBegin()); if (drawn_substituent_idx == _layout._src_vertex) drawn_substituent_idx = drawn_end.neiVertex(drawn_end.neiNext(drawn_end.neiBegin())); for (i = 0; i < 4; i++) { if (substituents[i] == _layout._graph.getVertexExtIdx(drawn_substituent_idx)) drawn_substituent = i; else if (substituents[i] == _layout._graph.getVertexExtIdx(to_draw_substituent_idx)) to_draw_substituent = i; } bool same_side = false; if ((parity == MoleculeCisTrans::CIS) == (abs(to_draw_substituent - drawn_substituent) == 2)) same_side = true; int to_draw_layout_idx = _layout._new_vertices.find(to_draw_substituent_idx); int side_sign = MoleculeCisTrans::sameside(Vec3f(_layout._graph.getPos(drawn_end_idx)), Vec3f(_layout._graph.getPos(_layout._src_vertex)), Vec3f(_layout._graph.getPos(drawn_substituent_idx)), Vec3f(_layout._layout[to_draw_layout_idx])); bool flip = false; if (same_side) { if (side_sign == -1) flip = true; } else if (side_sign == 1) flip = true; // flip around double bond if (flip) { const Vec2f &v1 = _layout._graph.getPos(drawn_end_idx); const Vec2f &v2 = _layout._graph.getPos(_layout._src_vertex); Vec2f d; d.diff(v2, v1); float r = d.lengthSqr(); //if (r < 0.000000001f) // throw Error("too small edge"); for (i = 0; i < _layout._layout.size(); i++) { const Vec2f &vi = _layout._layout[i]; float t = ((vi.x - v1.x) * d.x + (vi.y - v1.y) * d.y) / r; _layout._layout[i].set(2 * d.x * t + 2 * v1.x - vi.x, 2 * d.y * t + 2 * v1.y - vi.y); } // There's only one possible layout and the component is being flipped attach_comp.flipped(); } } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/layout_pattern.cpp���������������������������������������������������0000664�0000000�0000000�00000003462�12710376503�0022070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/tlscont.h" #include "graph/morgan_code.h" #include "layout/layout_pattern.h" using namespace indigo; IMPL_ERROR(PatternLayout, "molecule"); PatternLayout::PatternLayout () : _morgan_code(0), _fixed(false) { } PatternLayout::~PatternLayout () { } int PatternLayout::addAtom (float x, float y) { int idx = addVertex(); _atoms.expand(idx + 1); _atoms[idx].pos.set(x, y); return idx; } int PatternLayout::addBond (int atom_beg, int atom_end, int type) { int idx = addEdge(atom_beg, atom_end); _bonds.expand(idx + 1); _bonds[idx].type = type; return idx; } int PatternLayout::addOutlinePoint (float x, float y) { Vec2f &p = _outline.push(); p.set(x, y); return _outline.size() - 1; } const PatternAtom & PatternLayout::getAtom (int idx) const { return _atoms[idx]; } const PatternBond & PatternLayout::getBond (int idx) const { return _bonds[idx]; } void PatternLayout::calcMorganCode () { MorganCode morgan(*this); QS_DEF(Array<long>, morgan_codes); morgan.calculate(morgan_codes, 3, 7); _morgan_code = 0; for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _morgan_code += morgan_codes[i]; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/layout_pattern_smart.cpp���������������������������������������������0000664�0000000�0000000�00000011012�12710376503�0023264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/layout_pattern_smart.h" #include "base_cpp/scanner.h" #include "base_cpp/os_sync_wrapper.h" #include "math/algebra.h" #include "graph/graph.h" #include "molecule/query_molecule.h" #include "molecule/molfile_loader.h" #include "molecule/molecule_substructure_matcher.h" #include "layout/molecule_layout_graph.h" #include "base_cpp/profiling.h" #include <memory> #include <vector> #include "templates/layout_patterns.inc" using namespace indigo; using namespace std; class DLLEXPORT PatternLayoutSmart { public: QueryMolecule query_molecule; MoleculeLayoutGraphSmart layout_graph; }; static vector<unique_ptr<PatternLayoutSmart>> _patterns; static OsLock _patterns_lock; bool PatternLayoutFinder::tryToFindPattern (MoleculeLayoutGraphSmart &layout_graph) { _initPatterns(); layout_graph.calcMorganCode(); for (auto & pattern : _patterns) { MoleculeLayoutGraphSmart &plg = pattern->layout_graph; // Compare morgan code and graph size if (plg.getMorganCode() != layout_graph.getMorganCode()) continue; if (plg.vertexCount() != layout_graph.vertexCount()) continue; if (plg.edgeCount() != layout_graph.edgeCount()) continue; OsLocker locker(_patterns_lock); profTimerStart(t0, "layout.find-pattern"); // Check if substructure matching found EmbeddingEnumerator ee(layout_graph); ee.setSubgraph(pattern->query_molecule); ee.cb_match_edge = _matchPatternBond; if (!ee.process()) { // Embedding has been found -> copy coordinates const int *mapping = ee.getSubgraphMapping(); QueryMolecule &qm = pattern->query_molecule; int v0 = layout_graph.vertexBegin(); for (int v = qm.vertexBegin(); v != qm.vertexEnd(); v = qm.vertexNext(v)) { layout_graph.getPos(mapping[v]) = qm.getAtomXyz(v).projectZ() - qm.getAtomXyz(v0).projectZ(); } for (int v = layout_graph.vertexBegin(); v != layout_graph.vertexEnd(); v = layout_graph.vertexNext(v)) layout_graph.setVertexType(v, ELEMENT_DRAWN); for (int e = layout_graph.edgeBegin(); e != layout_graph.edgeEnd(); e = layout_graph.edgeNext(e)) layout_graph.setEdgeType(e, ELEMENT_DRAWN); layout_graph.assignFirstVertex(v0); return true; } } return false; } void PatternLayoutFinder::_initPatterns () { if (!_patterns.empty()) return; OsLocker locker(_patterns_lock); if (!_patterns.empty()) return; profTimerStart(t0, "layout.init-patterns"); _patterns.reserve(NELEM(layout_templates)); for (const char *tpl : layout_templates) { _patterns.emplace_back(new PatternLayoutSmart); auto &pattern = _patterns.back(); BufferScanner scanner(tpl); MolfileLoader loader(scanner); loader.loadQueryMolecule(pattern->query_molecule); pattern->layout_graph.makeOnGraph(pattern->query_molecule); // Copy coordinates QueryMolecule &qm = pattern->query_molecule; for (int v = qm.vertexBegin(); v != qm.vertexEnd(); v = qm.vertexNext(v)) pattern->layout_graph.getPos(v) = qm.getAtomXyz(v).projectZ(); pattern->layout_graph.calcMorganCode(); } } bool PatternLayoutFinder::_matchPatternBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { MoleculeLayoutGraphSmart &target = (MoleculeLayoutGraphSmart &)supergraph; BaseMolecule *mol = (BaseMolecule *)target.getMolecule(); int layout_idx = target.getLayoutEdge(super_idx).ext_idx; if (target.getEdgeMapping() != nullptr) layout_idx = target.getEdgeMapping()[layout_idx]; QueryMolecule &qmol = (QueryMolecule &)subgraph; QueryMolecule::Bond &sub_bond = qmol.getBond(sub_idx); if (!MoleculeSubstructureMatcher::matchQueryBond(&sub_bond, *mol, sub_idx, layout_idx, nullptr, 0xFFFFFFFF)) return false; return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/layout_patterns.inc��������������������������������������������������0000664�0000000�0000000�00000223124�12710376503�0022241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Added 18/03/2011 16:43:12 */ BEGIN_PATTERN("1293.mol") ADD_ATOM(0, -0.940215f, 0.340581f) ADD_ATOM(1, -0.983873f, 2.311095f) ADD_ATOM(2, -1.956171f, 0.363857f) ADD_ATOM(3, 0.000000f, 0.000000f) ADD_ATOM(4, -0.553083f, 1.411770f) ADD_ATOM(5, 0.861581f, 0.308624f) ADD_ATOM(6, 0.442365f, 1.117740f) ADD_BOND(0, 1, 1) ADD_BOND(0, 2, 1) ADD_BOND(0, 3, 1) ADD_BOND(4, 1, 1) ADD_BOND(3, 5, 2) ADD_BOND(4, 6, 1) ADD_BOND(4, 2, 1) ADD_BOND(5, 6, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.940215f, 0.340581f) OUTLINE_POINT(2, -1.956171f, 0.363857f) OUTLINE_POINT(3, -0.957260f, 1.109905f) OUTLINE_POINT(4, -0.983873f, 2.311095f) OUTLINE_POINT(5, -0.553083f, 1.411770f) OUTLINE_POINT(6, 0.442365f, 1.117740f) OUTLINE_POINT(7, 0.861581f, 0.308624f) END_PATTERN() BEGIN_PATTERN("aw00196.mol") ADD_ATOM(0, 0.000000f, 0.000000f) ADD_ATOM(1, -0.799249f, 0.429046f) ADD_ATOM(2, -0.634877f, 2.361760f) ADD_ATOM(3, 0.459562f, 0.919124f) ADD_ATOM(4, -0.400962f, 1.439961f) ADD_ATOM(5, -1.737704f, 0.083645f) ADD_ATOM(6, -1.345131f, 0.941373f) ADD_BOND(1, 0, 1) ADD_BOND(2, 1, 1) ADD_BOND(3, 0, 1) ADD_BOND(4, 3, 1) ADD_BOND(5, 1, 1) ADD_BOND(6, 4, 1) ADD_BOND(4, 2, 1) ADD_BOND(6, 5, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.799249f, 0.429046f) OUTLINE_POINT(2, -1.737704f, 0.083645f) OUTLINE_POINT(3, -1.345131f, 0.941373f) OUTLINE_POINT(4, -0.727959f, 1.267283f) OUTLINE_POINT(5, -0.634877f, 2.361760f) OUTLINE_POINT(6, -0.400962f, 1.439961f) OUTLINE_POINT(7, 0.459562f, 0.919124f) END_PATTERN() BEGIN_PATTERN("big_semicube.mol") ADD_ATOM(0, 1.527197f, 2.648596f) ADD_ATOM(1, 0.738232f, 2.257457f) ADD_ATOM(2, 0.000000f, 3.531023f) ADD_ATOM(3, 1.527197f, 0.881083f) ADD_ATOM(4, -0.012034f, 1.838250f) ADD_ATOM(5, -1.532542f, 2.648596f) ADD_ATOM(6, 0.000000f, 0.000000f) ADD_ATOM(7, -0.706192f, 2.228044f) ADD_ATOM(8, -0.012034f, 0.999928f) ADD_ATOM(9, -1.532542f, 0.881083f) ADD_BOND(0, 1, 1) ADD_BOND(0, 2, 1) ADD_BOND(0, 3, 1) ADD_BOND(1, 4, 1) ADD_BOND(2, 5, 1) ADD_BOND(3, 6, 1) ADD_BOND(4, 7, 1) ADD_BOND(4, 8, 1) ADD_BOND(5, 7, 1) ADD_BOND(5, 9, 1) ADD_BOND(6, 8, 1) ADD_BOND(6, 9, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.532542f, 0.881083f) OUTLINE_POINT(2, -1.532542f, 2.648596f) OUTLINE_POINT(3, 0.000000f, 3.531023f) OUTLINE_POINT(4, 1.527197f, 2.648596f) OUTLINE_POINT(5, 1.527197f, 0.881083f) END_PATTERN() BEGIN_PATTERN("btb12419.mol") ADD_ATOM(0, 0.000000f, 0.000000f) ADD_ATOM(1, -1.351330f, 0.422439f) ADD_ATOM(2, -0.641266f, 1.087644f) ADD_ATOM(3, -1.351330f, 1.342268f) ADD_ATOM(4, -0.641266f, 2.046406f) ADD_ATOM(5, -2.238155f, 1.087644f) ADD_ATOM(6, -2.942292f, 0.422439f) ADD_ATOM(7, -2.942292f, 1.728794f) ADD_ATOM(8, -2.238155f, 2.432931f) ADD_ATOM(9, -2.040476f, 1.728794f) ADD_ATOM(10, -1.345287f, 2.432931f) ADD_ATOM(11, -1.603048f, 3.580423f) ADD_BOND(1, 0, 1) ADD_BOND(2, 0, 1) ADD_BOND(3, 1, 1) ADD_BOND(4, 2, 1) ADD_BOND(5, 2, 1) ADD_BOND(6, 1, 1) ADD_BOND(7, 6, 1) ADD_BOND(8, 5, 1) ADD_BOND(9, 3, 1) ADD_BOND(10, 4, 1) ADD_BOND(11, 10, 1) ADD_BOND(4, 3, 1) ADD_BOND(5, 6, 1) ADD_BOND(10, 8, 1) ADD_BOND(8, 7, 1) ADD_BOND(9, 7, 1) ADD_BOND(11, 9, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.351330f, 0.422439f) OUTLINE_POINT(2, -2.942292f, 0.422439f) OUTLINE_POINT(3, -2.942292f, 1.728794f) OUTLINE_POINT(4, -2.238155f, 2.432931f) OUTLINE_POINT(5, -1.874131f, 2.432931f) OUTLINE_POINT(6, -1.603048f, 3.580423f) OUTLINE_POINT(7, -1.345287f, 2.432931f) OUTLINE_POINT(8, -0.641266f, 2.046406f) OUTLINE_POINT(9, -0.641266f, 1.087644f) END_PATTERN() BEGIN_PATTERN("cd00846.mol") ADD_ATOM(0, 0.752242f, 1.501544f) ADD_ATOM(1, -0.968589f, 1.501544f) ADD_ATOM(2, -0.103482f, 3.002948f) ADD_ATOM(3, -0.764845f, 0.689649f) ADD_ATOM(4, 0.752242f, 2.501359f) ADD_ATOM(5, 0.000000f, 0.000000f) ADD_ATOM(6, -0.103482f, 1.006257f) ADD_ATOM(7, -0.968589f, 2.501359f) ADD_ATOM(8, -1.871364f, 0.275860f) ADD_ATOM(9, -0.764845f, 1.949780f) ADD_BOND(1, 6, 1) ADD_BOND(2, 4, 1) ADD_BOND(3, 5, 1) ADD_BOND(4, 0, 1) ADD_BOND(5, 0, 1) ADD_BOND(6, 0, 1) ADD_BOND(7, 1, 1) ADD_BOND(8, 1, 1) ADD_BOND(9, 3, 1) ADD_BOND(8, 3, 1) ADD_BOND(7, 2, 1) ADD_BOND(9, 2, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.764845f, 0.689649f) OUTLINE_POINT(2, -1.871364f, 0.275860f) OUTLINE_POINT(3, -0.968589f, 1.501544f) OUTLINE_POINT(4, -0.968589f, 2.501359f) OUTLINE_POINT(5, -0.103482f, 3.002948f) OUTLINE_POINT(6, 0.752242f, 2.501359f) OUTLINE_POINT(7, 0.752242f, 1.501544f) END_PATTERN() BEGIN_PATTERN("complexring1.mol") ADD_ATOM(0, 5.375760f, 5.741474f) ADD_ATOM(1, 4.485780f, 6.216890f) ADD_ATOM(2, 5.400126f, 4.741913f) ADD_ATOM(3, 3.632651f, 5.692744f) ADD_ATOM(4, 4.461416f, 7.228572f) ADD_ATOM(5, 4.534631f, 4.217647f) ADD_ATOM(6, 3.657016f, 4.693183f) ADD_ATOM(7, 3.754474f, 7.947756f) ADD_ATOM(8, 4.571117f, 3.218207f) ADD_ATOM(9, 4.034851f, 8.898588f) ADD_ATOM(10, 2.791521f, 7.691745f) ADD_ATOM(11, 3.851933f, 2.523387f) ADD_ATOM(12, 3.315667f, 9.617772f) ADD_ATOM(13, 2.084459f, 8.398807f) ADD_ATOM(14, 4.083580f, 1.560313f) ADD_ATOM(15, 2.888980f, 2.803643f) ADD_ATOM(16, 2.352592f, 9.386125f) ADD_ATOM(17, 3.364396f, 0.853372f) ADD_ATOM(18, 2.169796f, 2.108823f) ADD_ATOM(19, 1.670016f, 10.080945f) ADD_ATOM(20, 2.401443f, 1.133749f) ADD_ATOM(21, 0.682698f, 9.837175f) ADD_ATOM(22, 1.682258f, 0.438929f) ADD_ATOM(23, -0.024365f, 10.556360f) ADD_ATOM(24, 0.414443f, 8.874223f) ADD_ATOM(25, 0.707062f, 0.707062f) ADD_ATOM(26, -0.987318f, 10.300469f) ADD_ATOM(27, -0.560631f, 8.618211f) ADD_ATOM(28, 0.000000f, 0.000000f) ADD_ATOM(29, 0.451051f, 1.670137f) ADD_ATOM(30, -1.255451f, 9.337396f) ADD_ATOM(31, -0.962953f, 0.268255f) ADD_ATOM(32, -0.511902f, 1.938270f) ADD_ATOM(33, -2.230768f, 9.081384f) ADD_ATOM(34, -1.218965f, 1.231207f) ADD_ATOM(35, -2.547631f, 8.045337f) ADD_ATOM(36, -2.023365f, 1.438490f) ADD_ATOM(37, -3.522827f, 7.825933f) ADD_ATOM(38, -1.864933f, 7.301787f) ADD_ATOM(39, -2.376957f, 2.377078f) ADD_ATOM(40, -3.827568f, 6.875101f) ADD_ATOM(41, -2.181917f, 6.338714f) ADD_ATOM(42, -3.376518f, 2.535631f) ADD_ATOM(43, -1.755352f, 3.157235f) ADD_ATOM(44, -3.157113f, 6.131552f) ADD_ATOM(45, -3.742231f, 3.474097f) ADD_ATOM(46, -2.120945f, 4.095823f) ADD_ATOM(47, -3.474097f, 5.180721f) ADD_ATOM(48, -3.108263f, 4.242012f) ADD_BOND(0, 1, 1) ADD_BOND(0, 2, 1) ADD_BOND(1, 3, 1) ADD_BOND(1, 4, 1) ADD_BOND(2, 5, 1) ADD_BOND(3, 6, 1) ADD_BOND(7, 4, 1) ADD_BOND(5, 6, 1) ADD_BOND(5, 8, 1) ADD_BOND(7, 9, 1) ADD_BOND(7, 10, 1) ADD_BOND(11, 8, 1) ADD_BOND(9, 12, 1) ADD_BOND(10, 13, 1) ADD_BOND(11, 14, 1) ADD_BOND(11, 15, 1) ADD_BOND(12, 16, 1) ADD_BOND(13, 16, 1) ADD_BOND(14, 17, 1) ADD_BOND(15, 18, 1) ADD_BOND(16, 19, 1) ADD_BOND(17, 20, 1) ADD_BOND(18, 20, 1) ADD_BOND(21, 19, 1) ADD_BOND(20, 22, 1) ADD_BOND(21, 23, 1) ADD_BOND(21, 24, 1) ADD_BOND(25, 22, 1) ADD_BOND(23, 26, 1) ADD_BOND(24, 27, 1) ADD_BOND(25, 28, 1) ADD_BOND(25, 29, 1) ADD_BOND(26, 30, 1) ADD_BOND(27, 30, 1) ADD_BOND(28, 31, 1) ADD_BOND(29, 32, 1) ADD_BOND(30, 33, 1) ADD_BOND(31, 34, 1) ADD_BOND(32, 34, 1) ADD_BOND(35, 33, 1) ADD_BOND(34, 36, 1) ADD_BOND(35, 37, 1) ADD_BOND(35, 38, 1) ADD_BOND(39, 36, 1) ADD_BOND(37, 40, 1) ADD_BOND(38, 41, 1) ADD_BOND(39, 42, 1) ADD_BOND(39, 43, 1) ADD_BOND(40, 44, 1) ADD_BOND(41, 44, 1) ADD_BOND(42, 45, 1) ADD_BOND(43, 46, 1) ADD_BOND(44, 47, 1) ADD_BOND(45, 48, 1) ADD_BOND(46, 48, 1) ADD_BOND(48, 47, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.962953f, 0.268255f) OUTLINE_POINT(2, -1.218965f, 1.231207f) OUTLINE_POINT(3, -2.023365f, 1.438490f) OUTLINE_POINT(4, -2.376957f, 2.377078f) OUTLINE_POINT(5, -3.376518f, 2.535631f) OUTLINE_POINT(6, -3.742231f, 3.474097f) OUTLINE_POINT(7, -3.108263f, 4.242012f) OUTLINE_POINT(8, -3.474097f, 5.180721f) OUTLINE_POINT(9, -3.157113f, 6.131552f) OUTLINE_POINT(10, -3.827568f, 6.875101f) OUTLINE_POINT(11, -3.522827f, 7.825933f) OUTLINE_POINT(12, -2.547631f, 8.045337f) OUTLINE_POINT(13, -2.230768f, 9.081384f) OUTLINE_POINT(14, -1.255451f, 9.337396f) OUTLINE_POINT(15, -0.987318f, 10.300469f) OUTLINE_POINT(16, -0.024365f, 10.556360f) OUTLINE_POINT(17, 0.682698f, 9.837175f) OUTLINE_POINT(18, 1.670016f, 10.080945f) OUTLINE_POINT(19, 2.352592f, 9.386125f) OUTLINE_POINT(20, 3.315667f, 9.617772f) OUTLINE_POINT(21, 4.034851f, 8.898588f) OUTLINE_POINT(22, 3.754474f, 7.947756f) OUTLINE_POINT(23, 4.461416f, 7.228572f) OUTLINE_POINT(24, 4.485780f, 6.216890f) OUTLINE_POINT(25, 5.375760f, 5.741474f) OUTLINE_POINT(26, 5.400126f, 4.741913f) OUTLINE_POINT(27, 4.534631f, 4.217647f) OUTLINE_POINT(28, 4.571117f, 3.218207f) OUTLINE_POINT(29, 3.851933f, 2.523387f) OUTLINE_POINT(30, 4.083580f, 1.560313f) OUTLINE_POINT(31, 3.364396f, 0.853372f) OUTLINE_POINT(32, 2.401443f, 1.133749f) OUTLINE_POINT(33, 1.682258f, 0.438929f) OUTLINE_POINT(34, 0.707062f, 0.707062f) END_PATTERN() BEGIN_PATTERN("complexring2.mol") ADD_ATOM(0, -3.817446f, 2.657814f) ADD_ATOM(1, -2.866241f, 2.996614f) ADD_ATOM(2, -3.980229f, 1.719723f) ADD_ATOM(3, -4.768409f, 3.120307f) ADD_ATOM(4, -2.117149f, 2.357985f) ADD_ATOM(5, -3.296201f, 1.048680f) ADD_ATOM(6, -4.807496f, 4.377537f) ADD_ATOM(7, -2.338562f, 1.374370f) ADD_ATOM(8, -5.804223f, 4.579529f) ADD_ATOM(9, -4.143133f, 5.165716f) ADD_ATOM(10, -1.596025f, 0.560088f) ADD_ATOM(11, -6.103810f, 5.484970f) ADD_ATOM(12, -4.488242f, 6.045299f) ADD_ATOM(13, -0.618843f, 0.807480f) ADD_ATOM(14, -5.497958f, 6.240614f) ADD_ATOM(15, -0.221293f, 1.739143f) ADD_ATOM(16, 0.000000f, 0.000000f) ADD_ATOM(17, -6.103810f, 7.302528f) ADD_ATOM(18, 0.749214f, 1.817318f) ADD_ATOM(19, 0.957638f, 0.117141f) ADD_ATOM(20, -5.172392f, 7.940916f) ADD_ATOM(21, 1.335523f, 0.990172f) ADD_ATOM(22, -5.497958f, 8.872455f) ADD_ATOM(23, -4.143133f, 7.745476f) ADD_ATOM(24, 2.325815f, 1.087767f) ADD_ATOM(25, -4.853016f, 9.621667f) ADD_ATOM(26, -3.556702f, 8.553201f) ADD_ATOM(27, 3.035699f, 2.103917f) ADD_ATOM(28, -3.875956f, 9.465319f) ADD_ATOM(29, 2.573328f, 2.976948f) ADD_ATOM(30, 4.006327f, 2.038972f) ADD_ATOM(31, -3.413463f, 10.370759f) ADD_ATOM(32, 3.094451f, 3.830192f) ADD_ATOM(33, 4.553670f, 2.879227f) ADD_ATOM(34, -2.299353f, 10.370759f) ADD_ATOM(35, 4.130144f, 3.745706f) ADD_ATOM(36, -1.915157f, 11.282757f) ADD_ATOM(37, -1.654535f, 9.582581f) ADD_ATOM(38, 4.833837f, 4.559863f) ADD_ATOM(39, -0.924865f, 11.406575f) ADD_ATOM(40, -0.696896f, 9.751918f) ADD_ATOM(41, 4.592879f, 5.550155f) ADD_ATOM(42, -0.351665f, 10.703002f) ADD_ATOM(43, 3.622130f, 5.804344f) ADD_ATOM(44, 5.302763f, 6.221193f) ADD_ATOM(45, 0.749214f, 11.061223f) ADD_ATOM(46, 3.381051f, 6.774850f) ADD_ATOM(47, 5.081348f, 7.191819f) ADD_ATOM(48, 1.557058f, 10.149346f) ADD_ATOM(49, 4.130144f, 7.458756f) ADD_ATOM(50, 2.429967f, 10.579309f) ADD_ATOM(51, 1.394275f, 9.178599f) ADD_ATOM(52, 4.130144f, 8.514235f) ADD_ATOM(53, 3.218147f, 9.992877f) ADD_ATOM(54, 2.247641f, 8.592288f) ADD_ATOM(55, 3.113873f, 9.002826f) ADD_BOND(0, 1, 1) ADD_BOND(0, 2, 1) ADD_BOND(0, 3, 1) ADD_BOND(1, 4, 1) ADD_BOND(2, 5, 1) ADD_BOND(3, 6, 1) ADD_BOND(7, 4, 1) ADD_BOND(6, 8, 1) ADD_BOND(6, 9, 1) ADD_BOND(7, 10, 1) ADD_BOND(8, 11, 1) ADD_BOND(9, 12, 1) ADD_BOND(13, 10, 1) ADD_BOND(11, 14, 1) ADD_BOND(13, 15, 1) ADD_BOND(13, 16, 1) ADD_BOND(14, 17, 1) ADD_BOND(15, 18, 1) ADD_BOND(16, 19, 1) ADD_BOND(17, 20, 1) ADD_BOND(21, 18, 1) ADD_BOND(20, 22, 1) ADD_BOND(20, 23, 1) ADD_BOND(21, 24, 1) ADD_BOND(22, 25, 1) ADD_BOND(23, 26, 1) ADD_BOND(27, 24, 1) ADD_BOND(25, 28, 1) ADD_BOND(27, 29, 1) ADD_BOND(27, 30, 1) ADD_BOND(28, 31, 1) ADD_BOND(29, 32, 1) ADD_BOND(30, 33, 1) ADD_BOND(31, 34, 1) ADD_BOND(35, 32, 1) ADD_BOND(34, 36, 1) ADD_BOND(34, 37, 1) ADD_BOND(35, 38, 1) ADD_BOND(36, 39, 1) ADD_BOND(37, 40, 1) ADD_BOND(41, 38, 1) ADD_BOND(39, 42, 1) ADD_BOND(41, 43, 1) ADD_BOND(41, 44, 1) ADD_BOND(42, 45, 1) ADD_BOND(43, 46, 1) ADD_BOND(44, 47, 1) ADD_BOND(45, 48, 1) ADD_BOND(49, 46, 1) ADD_BOND(48, 50, 1) ADD_BOND(48, 51, 1) ADD_BOND(49, 52, 1) ADD_BOND(50, 53, 1) ADD_BOND(51, 54, 1) ADD_BOND(55, 52, 1) ADD_BOND(5, 7, 1) ADD_BOND(12, 14, 1) ADD_BOND(19, 21, 1) ADD_BOND(26, 28, 1) ADD_BOND(33, 35, 1) ADD_BOND(40, 42, 1) ADD_BOND(47, 49, 1) ADD_BOND(53, 55, 1) ADD_BOND(54, 55, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.618843f, 0.807480f) OUTLINE_POINT(2, -1.596025f, 0.560088f) OUTLINE_POINT(3, -2.338562f, 1.374370f) OUTLINE_POINT(4, -3.296201f, 1.048680f) OUTLINE_POINT(5, -3.980229f, 1.719723f) OUTLINE_POINT(6, -3.817446f, 2.657814f) OUTLINE_POINT(7, -4.768409f, 3.120307f) OUTLINE_POINT(8, -4.807496f, 4.377537f) OUTLINE_POINT(9, -5.804223f, 4.579529f) OUTLINE_POINT(10, -6.103810f, 5.484970f) OUTLINE_POINT(11, -5.497958f, 6.240614f) OUTLINE_POINT(12, -6.103810f, 7.302528f) OUTLINE_POINT(13, -5.172392f, 7.940916f) OUTLINE_POINT(14, -5.497958f, 8.872455f) OUTLINE_POINT(15, -4.853016f, 9.621667f) OUTLINE_POINT(16, -3.875956f, 9.465319f) OUTLINE_POINT(17, -3.413463f, 10.370759f) OUTLINE_POINT(18, -2.299353f, 10.370759f) OUTLINE_POINT(19, -1.915157f, 11.282757f) OUTLINE_POINT(20, -0.924865f, 11.406575f) OUTLINE_POINT(21, -0.351665f, 10.703002f) OUTLINE_POINT(22, 0.749214f, 11.061223f) OUTLINE_POINT(23, 1.557058f, 10.149346f) OUTLINE_POINT(24, 2.429967f, 10.579309f) OUTLINE_POINT(25, 3.218147f, 9.992877f) OUTLINE_POINT(26, 3.113873f, 9.002826f) OUTLINE_POINT(27, 4.130144f, 8.514235f) OUTLINE_POINT(28, 4.130144f, 7.458756f) OUTLINE_POINT(29, 5.081348f, 7.191819f) OUTLINE_POINT(30, 5.302763f, 6.221193f) OUTLINE_POINT(31, 4.592879f, 5.550155f) OUTLINE_POINT(32, 4.833837f, 4.559863f) OUTLINE_POINT(33, 4.130144f, 3.745706f) OUTLINE_POINT(34, 4.553670f, 2.879227f) OUTLINE_POINT(35, 4.006327f, 2.038972f) OUTLINE_POINT(36, 3.035699f, 2.103917f) OUTLINE_POINT(37, 2.325815f, 1.087767f) OUTLINE_POINT(38, 1.335523f, 0.990172f) OUTLINE_POINT(39, 0.957638f, 0.117141f) END_PATTERN() BEGIN_PATTERN("complexring3.mol") ADD_ATOM(0, 4.265296f, 5.739903f) ADD_ATOM(1, 5.056469f, 5.246432f) ADD_ATOM(2, 4.265296f, 7.201814f) ADD_ATOM(3, 3.385611f, 5.246432f) ADD_ATOM(4, 5.056469f, 4.246433f) ADD_ATOM(5, 2.974365f, 7.942200f) ADD_ATOM(6, 3.385611f, 4.278113f) ADD_ATOM(7, 4.208343f, 3.752841f) ADD_ATOM(8, 2.974365f, 8.891535f) ADD_ATOM(9, 2.151632f, 7.436033f) ADD_ATOM(10, 4.208343f, 2.272068f) ADD_ATOM(11, 2.094800f, 9.404110f) ADD_ATOM(12, 1.291052f, 7.923337f) ADD_ATOM(13, 2.961668f, 1.601209f) ADD_ATOM(14, 1.291052f, 8.891535f) ADD_ATOM(15, 2.961668f, 0.633011f) ADD_ATOM(16, 2.094800f, 2.094800f) ADD_ATOM(17, -0.101330f, 9.612938f) ADD_ATOM(18, 2.094800f, 0.120435f) ADD_ATOM(19, 1.291052f, 1.620193f) ADD_ATOM(20, -1.392140f, 8.891535f) ADD_ATOM(21, 1.291052f, 0.613906f) ADD_ATOM(22, -2.214873f, 9.435671f) ADD_ATOM(23, -1.392140f, 7.923337f) ADD_ATOM(24, 0.000000f, 0.000000f) ADD_ATOM(25, -3.094558f, 8.891535f) ADD_ATOM(26, -2.252962f, 7.378960f) ADD_ATOM(27, -1.392140f, 0.740508f) ADD_ATOM(28, -3.094558f, 7.891535f) ADD_ATOM(29, -2.176904f, 0.139299f) ADD_ATOM(30, -1.392140f, 1.689722f) ADD_ATOM(31, -4.442564f, 7.201814f) ADD_ATOM(32, -3.094558f, 0.613906f) ADD_ATOM(33, -2.271826f, 2.151874f) ADD_ATOM(34, -4.442564f, 5.739903f) ADD_ATOM(35, -3.094558f, 1.620193f) ADD_ATOM(36, -5.284159f, 5.246432f) ADD_ATOM(37, -3.556469f, 5.246432f) ADD_ATOM(38, -4.442564f, 2.322733f) ADD_ATOM(39, -5.284159f, 4.246433f) ADD_ATOM(40, -3.556469f, 4.246433f) ADD_ATOM(41, -4.442564f, 3.752841f) ADD_BOND(0, 1, 1) ADD_BOND(0, 2, 1) ADD_BOND(0, 3, 1) ADD_BOND(1, 4, 1) ADD_BOND(5, 2, 1) ADD_BOND(3, 6, 1) ADD_BOND(4, 7, 1) ADD_BOND(5, 8, 1) ADD_BOND(5, 9, 1) ADD_BOND(7, 10, 1) ADD_BOND(8, 11, 1) ADD_BOND(9, 12, 1) ADD_BOND(13, 10, 1) ADD_BOND(11, 14, 1) ADD_BOND(13, 15, 1) ADD_BOND(13, 16, 1) ADD_BOND(14, 17, 1) ADD_BOND(15, 18, 1) ADD_BOND(16, 19, 1) ADD_BOND(20, 17, 1) ADD_BOND(18, 21, 1) ADD_BOND(20, 22, 1) ADD_BOND(20, 23, 1) ADD_BOND(21, 24, 1) ADD_BOND(22, 25, 1) ADD_BOND(23, 26, 1) ADD_BOND(27, 24, 1) ADD_BOND(25, 28, 1) ADD_BOND(27, 29, 1) ADD_BOND(27, 30, 1) ADD_BOND(28, 31, 1) ADD_BOND(29, 32, 1) ADD_BOND(30, 33, 1) ADD_BOND(34, 31, 1) ADD_BOND(32, 35, 1) ADD_BOND(34, 36, 1) ADD_BOND(34, 37, 1) ADD_BOND(35, 38, 1) ADD_BOND(36, 39, 1) ADD_BOND(37, 40, 1) ADD_BOND(41, 38, 1) ADD_BOND(6, 7, 1) ADD_BOND(12, 14, 1) ADD_BOND(19, 21, 1) ADD_BOND(26, 28, 1) ADD_BOND(33, 35, 1) ADD_BOND(39, 41, 1) ADD_BOND(40, 41, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.392140f, 0.740508f) OUTLINE_POINT(2, -2.176904f, 0.139299f) OUTLINE_POINT(3, -3.094558f, 0.613906f) OUTLINE_POINT(4, -3.094558f, 1.620193f) OUTLINE_POINT(5, -4.442564f, 2.322733f) OUTLINE_POINT(6, -4.442564f, 3.752841f) OUTLINE_POINT(7, -5.284159f, 4.246433f) OUTLINE_POINT(8, -5.284159f, 5.246432f) OUTLINE_POINT(9, -4.442564f, 5.739903f) OUTLINE_POINT(10, -4.442564f, 7.201814f) OUTLINE_POINT(11, -3.094558f, 7.891535f) OUTLINE_POINT(12, -3.094558f, 8.891535f) OUTLINE_POINT(13, -2.214873f, 9.435671f) OUTLINE_POINT(14, -1.392140f, 8.891535f) OUTLINE_POINT(15, -0.101330f, 9.612938f) OUTLINE_POINT(16, 1.291052f, 8.891535f) OUTLINE_POINT(17, 2.094800f, 9.404110f) OUTLINE_POINT(18, 2.974365f, 8.891535f) OUTLINE_POINT(19, 2.974365f, 7.942200f) OUTLINE_POINT(20, 4.265296f, 7.201814f) OUTLINE_POINT(21, 4.265296f, 5.739903f) OUTLINE_POINT(22, 5.056469f, 5.246432f) OUTLINE_POINT(23, 5.056469f, 4.246433f) OUTLINE_POINT(24, 4.208343f, 3.752841f) OUTLINE_POINT(25, 4.208343f, 2.272068f) OUTLINE_POINT(26, 2.961668f, 1.601209f) OUTLINE_POINT(27, 2.961668f, 0.633011f) OUTLINE_POINT(28, 2.094800f, 0.120435f) OUTLINE_POINT(29, 1.291052f, 0.613906f) END_PATTERN() BEGIN_PATTERN("complexring4.mol") ADD_ATOM(0, -2.999946f, 6.077833f) ADD_ATOM(1, -3.587717f, 6.886821f) ADD_ATOM(2, -4.538889f, 6.577844f) ADD_ATOM(3, -5.489941f, 6.886942f) ADD_ATOM(4, -6.077712f, 6.077833f) ADD_ATOM(5, 3.587959f, 5.268724f) ADD_ATOM(6, 4.539011f, 5.577701f) ADD_ATOM(7, 5.490062f, 5.268602f) ADD_ATOM(8, 6.077833f, 6.077712f) ADD_ATOM(9, 5.490062f, 6.886700f) ADD_ATOM(10, 4.539011f, 6.577723f) ADD_ATOM(11, 3.587959f, 6.886700f) ADD_ATOM(12, 3.000067f, 6.077833f) ADD_ATOM(13, 1.965014f, 2.968673f) ADD_ATOM(14, 2.856064f, 2.514602f) ADD_ATOM(15, 3.309892f, 1.623673f) ADD_ATOM(16, 4.297672f, 1.780040f) ADD_ATOM(17, 4.454160f, 2.767699f) ADD_ATOM(18, 3.563110f, 3.221769f) ADD_ATOM(19, 3.109039f, 4.112819f) ADD_ATOM(20, 2.121381f, 3.956331f) ADD_ATOM(21, -4.454039f, 9.387846f) ADD_ATOM(22, -3.562989f, 8.933776f) ADD_ATOM(23, -3.108918f, 8.042726f) ADD_ATOM(24, -2.121259f, 8.199214f) ADD_ATOM(25, -1.964771f, 9.186872f) ADD_ATOM(26, -2.855822f, 9.640943f) ADD_ATOM(27, -3.309771f, 10.531873f) ADD_ATOM(28, -4.297551f, 10.375504f) ADD_ATOM(29, 3.310134f, 10.531873f) ADD_ATOM(30, 2.856064f, 9.640821f) ADD_ATOM(31, 1.965014f, 9.186751f) ADD_ATOM(32, 2.121502f, 8.199093f) ADD_ATOM(33, 3.109160f, 8.042604f) ADD_ATOM(34, 3.563231f, 8.933776f) ADD_ATOM(35, 4.454160f, 9.387604f) ADD_ATOM(36, 4.297793f, 10.375383f) ADD_ATOM(37, -4.297672f, 1.780161f) ADD_ATOM(38, -3.310013f, 1.623673f) ADD_ATOM(39, -2.855943f, 2.514723f) ADD_ATOM(40, -1.964892f, 2.968794f) ADD_ATOM(41, -2.121381f, 3.956452f) ADD_ATOM(42, -0.808867f, 11.567773f) ADD_ATOM(43, -0.499890f, 10.616722f) ADD_ATOM(44, -0.808867f, 9.665670f) ADD_ATOM(45, 0.000121f, 9.077779f) ADD_ATOM(46, 0.809109f, 9.665670f) ADD_ATOM(47, 0.500132f, 10.616722f) ADD_ATOM(48, 0.809230f, 11.567773f) ADD_ATOM(49, 0.000121f, 12.155544f) ADD_ATOM(50, -0.808988f, 2.489995f) ADD_ATOM(51, -0.500011f, 1.538822f) ADD_ATOM(52, -0.809109f, 0.587771f) ADD_ATOM(53, 0.000000f, 0.000000f) ADD_ATOM(54, 0.808988f, 0.587771f) ADD_ATOM(55, 0.500011f, 1.538822f) ADD_ATOM(56, 0.808988f, 2.489874f) ADD_ATOM(57, 0.000121f, 3.077766f) ADD_ATOM(58, 3.135706f, 7.376529f) ADD_ATOM(59, 5.375514f, 8.304307f) ADD_ATOM(60, -4.454039f, 2.767941f) ADD_ATOM(61, -3.563110f, 3.221769f) ADD_ATOM(62, -3.109039f, 4.112940f) ADD_ATOM(63, -3.587838f, 5.268845f) ADD_ATOM(64, -4.538889f, 5.577822f) ADD_ATOM(65, -5.489941f, 5.268845f) ADD_ATOM(66, -3.135585f, 4.779016f) ADD_ATOM(67, -5.375393f, 3.851238f) ADD_ATOM(68, 1.298938f, 9.213418f) ADD_ATOM(69, 2.226717f, 11.453105f) ADD_ATOM(70, -1.298696f, 9.213418f) ADD_ATOM(71, -2.226474f, 11.453226f) ADD_ATOM(72, -3.135585f, 7.376650f) ADD_ATOM(73, -5.375271f, 8.304428f) ADD_ATOM(74, 3.135706f, 4.778895f) ADD_ATOM(75, 5.375514f, 3.851238f) ADD_ATOM(76, 1.298817f, 2.942127f) ADD_ATOM(77, 2.226595f, 0.702319f) ADD_ATOM(78, -1.298817f, 2.942127f) ADD_ATOM(79, -2.226595f, 0.702440f) ADD_BOND(63, 0, 1) ADD_BOND(0, 1, 1) ADD_BOND(64, 2, 1) ADD_BOND(4, 3, 1) ADD_BOND(65, 4, 1) ADD_BOND(7, 8, 1) ADD_BOND(8, 9, 1) ADD_BOND(10, 9, 1) ADD_BOND(6, 10, 1) ADD_BOND(10, 11, 1) ADD_BOND(12, 11, 1) ADD_BOND(5, 12, 1) ADD_BOND(15, 16, 1) ADD_BOND(16, 17, 1) ADD_BOND(14, 18, 1) ADD_BOND(20, 19, 1) ADD_BOND(13, 20, 1) ADD_BOND(23, 24, 1) ADD_BOND(24, 25, 1) ADD_BOND(26, 22, 1) ADD_BOND(28, 27, 1) ADD_BOND(21, 28, 1) ADD_BOND(31, 32, 1) ADD_BOND(32, 33, 1) ADD_BOND(34, 33, 1) ADD_BOND(34, 30, 1) ADD_BOND(34, 35, 1) ADD_BOND(36, 35, 1) ADD_BOND(29, 36, 1) ADD_BOND(60, 37, 1) ADD_BOND(37, 38, 1) ADD_BOND(61, 39, 1) ADD_BOND(41, 40, 1) ADD_BOND(62, 41, 1) ADD_BOND(44, 45, 1) ADD_BOND(45, 46, 1) ADD_BOND(47, 43, 1) ADD_BOND(49, 48, 1) ADD_BOND(42, 49, 1) ADD_BOND(52, 53, 1) ADD_BOND(53, 54, 1) ADD_BOND(51, 55, 1) ADD_BOND(57, 56, 1) ADD_BOND(50, 57, 1) ADD_BOND(35, 59, 1) ADD_BOND(9, 59, 1) ADD_BOND(33, 58, 1) ADD_BOND(11, 58, 1) ADD_BOND(65, 67, 1) ADD_BOND(60, 67, 1) ADD_BOND(63, 66, 1) ADD_BOND(62, 66, 1) ADD_BOND(61, 60, 1) ADD_BOND(61, 62, 1) ADD_BOND(64, 63, 1) ADD_BOND(64, 65, 1) ADD_BOND(31, 68, 1) ADD_BOND(30, 31, 1) ADD_BOND(30, 29, 1) ADD_BOND(29, 69, 1) ADD_BOND(46, 68, 1) ADD_BOND(47, 46, 1) ADD_BOND(47, 48, 1) ADD_BOND(48, 69, 1) ADD_BOND(25, 70, 1) ADD_BOND(26, 25, 1) ADD_BOND(42, 71, 1) ADD_BOND(43, 42, 1) ADD_BOND(43, 44, 1) ADD_BOND(44, 70, 1) ADD_BOND(26, 27, 1) ADD_BOND(27, 71, 1) ADD_BOND(1, 72, 1) ADD_BOND(2, 1, 1) ADD_BOND(21, 73, 1) ADD_BOND(22, 21, 1) ADD_BOND(22, 23, 1) ADD_BOND(23, 72, 1) ADD_BOND(2, 3, 1) ADD_BOND(3, 73, 1) ADD_BOND(19, 74, 1) ADD_BOND(18, 19, 1) ADD_BOND(7, 75, 1) ADD_BOND(6, 7, 1) ADD_BOND(6, 5, 1) ADD_BOND(5, 74, 1) ADD_BOND(18, 17, 1) ADD_BOND(17, 75, 1) ADD_BOND(13, 76, 1) ADD_BOND(14, 13, 1) ADD_BOND(14, 15, 1) ADD_BOND(15, 77, 1) ADD_BOND(56, 76, 1) ADD_BOND(55, 56, 1) ADD_BOND(55, 54, 1) ADD_BOND(54, 77, 1) ADD_BOND(50, 78, 1) ADD_BOND(51, 50, 1) ADD_BOND(51, 52, 1) ADD_BOND(52, 79, 1) ADD_BOND(40, 78, 1) ADD_BOND(39, 40, 1) ADD_BOND(39, 38, 1) ADD_BOND(38, 79, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.809109f, 0.587771f) OUTLINE_POINT(2, -2.226595f, 0.702440f) OUTLINE_POINT(3, -3.310013f, 1.623673f) OUTLINE_POINT(4, -4.297672f, 1.780161f) OUTLINE_POINT(5, -4.454039f, 2.767941f) OUTLINE_POINT(6, -5.375393f, 3.851238f) OUTLINE_POINT(7, -5.489941f, 5.268845f) OUTLINE_POINT(8, -6.077712f, 6.077833f) OUTLINE_POINT(9, -5.489941f, 6.886942f) OUTLINE_POINT(10, -5.375271f, 8.304428f) OUTLINE_POINT(11, -4.454039f, 9.387846f) OUTLINE_POINT(12, -4.297551f, 10.375504f) OUTLINE_POINT(13, -3.309771f, 10.531873f) OUTLINE_POINT(14, -2.226474f, 11.453226f) OUTLINE_POINT(15, -0.808867f, 11.567773f) OUTLINE_POINT(16, 0.000121f, 12.155544f) OUTLINE_POINT(17, 0.809230f, 11.567773f) OUTLINE_POINT(18, 2.226717f, 11.453105f) OUTLINE_POINT(19, 3.310134f, 10.531873f) OUTLINE_POINT(20, 4.297793f, 10.375383f) OUTLINE_POINT(21, 4.454160f, 9.387604f) OUTLINE_POINT(22, 5.375514f, 8.304307f) OUTLINE_POINT(23, 5.490062f, 6.886700f) OUTLINE_POINT(24, 6.077833f, 6.077712f) OUTLINE_POINT(25, 5.490062f, 5.268602f) OUTLINE_POINT(26, 5.375514f, 3.851238f) OUTLINE_POINT(27, 4.454160f, 2.767699f) OUTLINE_POINT(28, 4.297672f, 1.780040f) OUTLINE_POINT(29, 3.309892f, 1.623673f) OUTLINE_POINT(30, 2.226595f, 0.702319f) OUTLINE_POINT(31, 0.808988f, 0.587771f) END_PATTERN() BEGIN_PATTERN("complexring5.mol") ADD_ATOM(0, 2.419502f, 1.945687f) ADD_ATOM(1, 1.518416f, 1.511871f) ADD_ATOM(2, 1.652718f, 0.520967f) ADD_ATOM(3, 2.636713f, 0.342423f) ADD_ATOM(4, 3.110529f, 1.223024f) ADD_ATOM(5, 2.285200f, 2.936712f) ADD_ATOM(6, 1.301205f, 3.115135f) ADD_ATOM(7, 0.827389f, 2.234655f) ADD_ATOM(8, -1.519143f, 1.511628f) ADD_ATOM(9, -2.420108f, 1.945444f) ADD_ATOM(10, -3.111256f, 1.222660f) ADD_ATOM(11, -2.637319f, 0.342180f) ADD_ATOM(12, -1.653324f, 0.520725f) ADD_ATOM(13, -0.828117f, 2.234534f) ADD_ATOM(14, -1.302053f, 3.115014f) ADD_ATOM(15, -2.286048f, 2.936469f) ADD_ATOM(16, -4.314280f, 4.320341f) ADD_ATOM(17, -4.536824f, 5.295365f) ADD_ATOM(18, -5.532698f, 5.384941f) ADD_ATOM(19, -5.925786f, 4.465431f) ADD_ATOM(20, -5.172700f, 3.807495f) ADD_ATOM(21, -3.318285f, 4.230886f) ADD_ATOM(22, -2.925318f, 5.150275f) ADD_ATOM(23, -3.678283f, 5.808211f) ADD_ATOM(24, -3.861070f, 8.256926f) ADD_ATOM(25, -3.237558f, 9.038739f) ADD_ATOM(26, -3.788464f, 9.873281f) ADD_ATOM(27, -4.752459f, 9.607222f) ADD_ATOM(28, -4.797307f, 8.608196f) ADD_ATOM(29, -3.310043f, 7.422263f) ADD_ATOM(30, -2.346048f, 7.688443f) ADD_ATOM(31, -2.301321f, 8.687347f) ADD_ATOM(32, -0.500725f, 10.356793f) ADD_ATOM(33, 0.499270f, 10.356915f) ADD_ATOM(34, 0.808238f, 11.307939f) ADD_ATOM(35, -0.000970f, 11.895694f) ADD_ATOM(36, -0.809814f, 11.307818f) ADD_ATOM(37, -0.809693f, 9.405767f) ADD_ATOM(38, -0.000727f, 8.818013f) ADD_ATOM(39, 0.808238f, 9.405889f) ADD_ATOM(40, 3.236104f, 9.038981f) ADD_ATOM(41, 3.859616f, 8.257168f) ADD_ATOM(42, 4.795853f, 8.608681f) ADD_ATOM(43, 4.750884f, 9.607585f) ADD_ATOM(44, 3.787010f, 9.873644f) ADD_ATOM(45, 2.299866f, 8.687590f) ADD_ATOM(46, 2.344836f, 7.688565f) ADD_ATOM(47, 3.308831f, 7.422627f) ADD_ATOM(48, 4.535733f, 5.295729f) ADD_ATOM(49, 4.313310f, 4.320826f) ADD_ATOM(50, 5.171730f, 3.807980f) ADD_ATOM(51, 5.924817f, 4.465916f) ADD_ATOM(52, 5.531606f, 5.385426f) ADD_ATOM(53, 3.677314f, 5.808575f) ADD_ATOM(54, 2.924227f, 5.150517f) ADD_ATOM(55, 3.317315f, 4.231129f) ADD_ATOM(56, 2.558289f, 3.777677f) ADD_ATOM(57, 4.548460f, 2.190776f) ADD_ATOM(58, 3.190043f, 6.546389f) ADD_ATOM(59, 5.671605f, 7.113052f) ADD_ATOM(60, 1.419265f, 8.766741f) ADD_ATOM(61, 2.523623f, 11.060184f) ADD_ATOM(62, -1.420598f, 8.766499f) ADD_ATOM(63, -2.525320f, 11.059698f) ADD_ATOM(64, -3.191134f, 6.546026f) ADD_ATOM(65, -5.672818f, 7.112446f) ADD_ATOM(66, -2.559016f, 3.777555f) ADD_ATOM(67, -4.548945f, 2.190170f) ADD_ATOM(68, -0.000485f, 2.545441f) ADD_ATOM(69, 0.000000f, 0.000000f) ADD_BOND(1, 0, 1) ADD_BOND(2, 3, 1) ADD_BOND(4, 3, 1) ADD_BOND(0, 4, 1) ADD_BOND(0, 5, 1) ADD_BOND(5, 6, 1) ADD_BOND(7, 6, 1) ADD_BOND(9, 8, 1) ADD_BOND(10, 11, 1) ADD_BOND(12, 11, 1) ADD_BOND(13, 14, 1) ADD_BOND(15, 14, 1) ADD_BOND(17, 16, 1) ADD_BOND(18, 19, 1) ADD_BOND(20, 19, 1) ADD_BOND(21, 22, 1) ADD_BOND(23, 22, 1) ADD_BOND(25, 24, 1) ADD_BOND(26, 27, 1) ADD_BOND(28, 27, 1) ADD_BOND(29, 30, 1) ADD_BOND(31, 30, 1) ADD_BOND(33, 32, 1) ADD_BOND(34, 35, 1) ADD_BOND(36, 35, 1) ADD_BOND(37, 38, 1) ADD_BOND(39, 38, 1) ADD_BOND(40, 41, 1) ADD_BOND(42, 43, 1) ADD_BOND(44, 43, 1) ADD_BOND(45, 46, 1) ADD_BOND(47, 46, 1) ADD_BOND(49, 48, 1) ADD_BOND(49, 50, 1) ADD_BOND(50, 51, 1) ADD_BOND(52, 51, 1) ADD_BOND(53, 54, 1) ADD_BOND(55, 54, 1) ADD_BOND(49, 55, 1) ADD_BOND(55, 56, 1) ADD_BOND(5, 56, 1) ADD_BOND(50, 57, 1) ADD_BOND(4, 57, 1) ADD_BOND(53, 58, 1) ADD_BOND(48, 53, 1) ADD_BOND(48, 52, 1) ADD_BOND(52, 59, 1) ADD_BOND(41, 42, 1) ADD_BOND(42, 59, 1) ADD_BOND(41, 47, 1) ADD_BOND(47, 58, 1) ADD_BOND(44, 61, 1) ADD_BOND(40, 44, 1) ADD_BOND(40, 45, 1) ADD_BOND(45, 60, 1) ADD_BOND(33, 34, 1) ADD_BOND(34, 61, 1) ADD_BOND(33, 39, 1) ADD_BOND(39, 60, 1) ADD_BOND(37, 62, 1) ADD_BOND(32, 37, 1) ADD_BOND(32, 36, 1) ADD_BOND(36, 63, 1) ADD_BOND(25, 26, 1) ADD_BOND(26, 63, 1) ADD_BOND(25, 31, 1) ADD_BOND(31, 62, 1) ADD_BOND(17, 18, 1) ADD_BOND(18, 65, 1) ADD_BOND(24, 28, 1) ADD_BOND(28, 65, 1) ADD_BOND(24, 29, 1) ADD_BOND(29, 64, 1) ADD_BOND(17, 23, 1) ADD_BOND(23, 64, 1) ADD_BOND(16, 21, 1) ADD_BOND(21, 66, 1) ADD_BOND(16, 20, 1) ADD_BOND(20, 67, 1) ADD_BOND(9, 10, 1) ADD_BOND(10, 67, 1) ADD_BOND(9, 15, 1) ADD_BOND(15, 66, 1) ADD_BOND(13, 68, 1) ADD_BOND(8, 13, 1) ADD_BOND(8, 12, 1) ADD_BOND(12, 69, 1) ADD_BOND(1, 2, 1) ADD_BOND(2, 69, 1) ADD_BOND(1, 7, 1) ADD_BOND(7, 68, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.653324f, 0.520725f) OUTLINE_POINT(2, -2.637319f, 0.342180f) OUTLINE_POINT(3, -3.111256f, 1.222660f) OUTLINE_POINT(4, -4.548945f, 2.190170f) OUTLINE_POINT(5, -5.172700f, 3.807495f) OUTLINE_POINT(6, -5.925786f, 4.465431f) OUTLINE_POINT(7, -5.532698f, 5.384941f) OUTLINE_POINT(8, -5.672818f, 7.112446f) OUTLINE_POINT(9, -4.797307f, 8.608196f) OUTLINE_POINT(10, -4.752459f, 9.607222f) OUTLINE_POINT(11, -3.788464f, 9.873281f) OUTLINE_POINT(12, -2.525320f, 11.059698f) OUTLINE_POINT(13, -0.809814f, 11.307818f) OUTLINE_POINT(14, -0.000970f, 11.895694f) OUTLINE_POINT(15, 0.808238f, 11.307939f) OUTLINE_POINT(16, 2.523623f, 11.060184f) OUTLINE_POINT(17, 3.787010f, 9.873644f) OUTLINE_POINT(18, 4.750884f, 9.607585f) OUTLINE_POINT(19, 4.795853f, 8.608681f) OUTLINE_POINT(20, 5.671605f, 7.113052f) OUTLINE_POINT(21, 5.531606f, 5.385426f) OUTLINE_POINT(22, 5.924817f, 4.465916f) OUTLINE_POINT(23, 5.171730f, 3.807980f) OUTLINE_POINT(24, 4.548460f, 2.190776f) OUTLINE_POINT(25, 3.110529f, 1.223024f) OUTLINE_POINT(26, 2.636713f, 0.342423f) OUTLINE_POINT(27, 1.652718f, 0.520967f) END_PATTERN() BEGIN_PATTERN("complexring6.mol") ADD_ATOM(0, 2.186429f, 1.856610f) ADD_ATOM(1, 1.235397f, 1.547518f) ADD_ATOM(2, 1.235397f, 0.547517f) ADD_ATOM(3, 2.186429f, 0.238546f) ADD_ATOM(4, 2.774188f, 1.047638f) ADD_ATOM(5, 2.186308f, 2.856612f) ADD_ATOM(6, 1.235397f, 3.165461f) ADD_ATOM(7, 0.647517f, 2.356490f) ADD_ATOM(8, -1.237457f, 12.391784f) ADD_ATOM(9, -2.188611f, 12.082693f) ADD_ATOM(10, -2.188611f, 11.082690f) ADD_ATOM(11, -1.237457f, 10.773720f) ADD_ATOM(12, -0.649698f, 11.582811f) ADD_ATOM(13, -1.237821f, 13.391664f) ADD_ATOM(14, -2.188732f, 13.700634f) ADD_ATOM(15, -2.776491f, 12.891664f) ADD_ATOM(16, -1.236730f, 1.547397f) ADD_ATOM(17, -2.187641f, 0.238303f) ADD_ATOM(18, -1.236609f, 0.547395f) ADD_ATOM(19, -0.649092f, 2.356369f) ADD_ATOM(20, -1.236730f, 3.165340f) ADD_ATOM(21, 2.185823f, 12.083055f) ADD_ATOM(22, 1.234669f, 12.392026f) ADD_ATOM(23, 0.646910f, 11.582934f) ADD_ATOM(24, 1.234669f, 10.773962f) ADD_ATOM(25, 2.185823f, 11.083054f) ADD_ATOM(26, 2.773339f, 12.892028f) ADD_ATOM(27, 2.185580f, 13.700998f) ADD_ATOM(28, 1.234427f, 13.392028f) ADD_ATOM(29, 4.773464f, 9.821233f) ADD_ATOM(30, 4.185706f, 10.630083f) ADD_ATOM(31, 3.234674f, 10.320992f) ADD_ATOM(32, 3.234795f, 9.320990f) ADD_ATOM(33, 4.185827f, 9.012140f) ADD_ATOM(34, 5.724618f, 10.130324f) ADD_ATOM(35, 5.724376f, 11.130205f) ADD_ATOM(36, 4.773464f, 11.439176f) ADD_ATOM(37, -4.775646f, 4.118190f) ADD_ATOM(38, -5.726799f, 3.809341f) ADD_ATOM(39, -5.726557f, 2.809218f) ADD_ATOM(40, -3.236977f, 4.618313f) ADD_ATOM(41, -4.188130f, 4.927404f) ADD_ATOM(42, -5.539890f, 6.469468f) ADD_ATOM(43, -5.539890f, 7.469349f) ADD_ATOM(44, -6.490923f, 7.778440f) ADD_ATOM(45, -7.078560f, 6.969348f) ADD_ATOM(46, -6.490923f, 6.160377f) ADD_ATOM(47, -4.588858f, 6.160498f) ADD_ATOM(48, -4.001099f, 6.969348f) ADD_ATOM(49, -4.588858f, 7.778562f) ADD_ATOM(50, 5.537709f, 6.470074f) ADD_ATOM(51, 5.537709f, 7.469955f) ADD_ATOM(52, 4.586555f, 7.779047f) ADD_ATOM(53, 3.998917f, 6.969954f) ADD_ATOM(54, 4.586676f, 6.160983f) ADD_ATOM(55, 6.488741f, 6.161104f) ADD_ATOM(56, 7.076379f, 6.970075f) ADD_ATOM(57, 6.488741f, 7.779168f) ADD_ATOM(58, -4.776131f, 9.820627f) ADD_ATOM(59, -4.188373f, 10.629598f) ADD_ATOM(60, -4.776010f, 11.438691f) ADD_ATOM(61, -5.727163f, 11.129600f) ADD_ATOM(62, -5.727163f, 10.129719f) ADD_ATOM(63, -4.188373f, 9.011655f) ADD_ATOM(64, -3.237340f, 9.320625f) ADD_ATOM(65, -3.237219f, 10.320748f) ADD_ATOM(66, 4.186191f, 3.309704f) ADD_ATOM(67, 4.773949f, 4.118796f) ADD_ATOM(68, 4.186191f, 4.927889f) ADD_ATOM(69, 3.235158f, 4.618798f) ADD_ATOM(70, 3.235158f, 3.618796f) ADD_ATOM(71, 4.773949f, 2.500733f) ADD_ATOM(72, 5.724982f, 2.809824f) ADD_ATOM(73, 5.724982f, 3.809826f) ADD_ATOM(74, 2.563763f, 10.499901f) ADD_ATOM(75, 4.095645f, 12.608269f) ADD_ATOM(76, -4.187888f, 3.309219f) ADD_ATOM(77, -3.236977f, 3.618432f) ADD_ATOM(78, -4.775646f, 2.500369f) ADD_ATOM(79, -2.187762f, 1.856367f) ADD_ATOM(80, -2.188005f, 2.856248f) ADD_ATOM(81, -2.775521f, 1.047396f) ADD_ATOM(82, -2.565460f, 3.439159f) ADD_ATOM(83, -4.096978f, 1.330548f) ADD_ATOM(84, -0.001091f, 11.333236f) ADD_ATOM(85, -0.001212f, 13.939301f) ADD_ATOM(86, -2.566066f, 10.500023f) ADD_ATOM(87, -4.097705f, 12.608389f) ADD_ATOM(88, -4.151281f, 8.318199f) ADD_ATOM(89, -6.629710f, 9.123898f) ADD_ATOM(90, -4.151402f, 5.621345f) ADD_ATOM(91, -6.629710f, 4.815404f) ADD_ATOM(92, 4.148857f, 8.318805f) ADD_ATOM(93, 6.627165f, 9.124504f) ADD_ATOM(94, 4.149099f, 5.621588f) ADD_ATOM(95, 6.627772f, 4.816737f) ADD_ATOM(96, 2.564248f, 3.439643f) ADD_ATOM(97, 4.096372f, 1.331518f) ADD_ATOM(98, -0.000606f, 2.606066f) ADD_ATOM(99, 0.000000f, 0.000000f) ADD_BOND(1, 0, 1) ADD_BOND(2, 3, 1) ADD_BOND(4, 3, 1) ADD_BOND(5, 6, 1) ADD_BOND(7, 6, 1) ADD_BOND(8, 9, 1) ADD_BOND(10, 11, 1) ADD_BOND(12, 11, 1) ADD_BOND(13, 14, 1) ADD_BOND(15, 14, 1) ADD_BOND(79, 16, 1) ADD_BOND(81, 17, 1) ADD_BOND(18, 17, 1) ADD_BOND(19, 20, 1) ADD_BOND(80, 20, 1) ADD_BOND(21, 22, 1) ADD_BOND(23, 24, 1) ADD_BOND(25, 24, 1) ADD_BOND(21, 25, 1) ADD_BOND(21, 26, 1) ADD_BOND(26, 27, 1) ADD_BOND(28, 27, 1) ADD_BOND(29, 30, 1) ADD_BOND(31, 32, 1) ADD_BOND(33, 32, 1) ADD_BOND(34, 35, 1) ADD_BOND(36, 35, 1) ADD_BOND(30, 31, 1) ADD_BOND(30, 36, 1) ADD_BOND(76, 37, 1) ADD_BOND(38, 39, 1) ADD_BOND(78, 39, 1) ADD_BOND(77, 40, 1) ADD_BOND(41, 40, 1) ADD_BOND(43, 42, 1) ADD_BOND(44, 45, 1) ADD_BOND(46, 45, 1) ADD_BOND(47, 48, 1) ADD_BOND(49, 48, 1) ADD_BOND(50, 51, 1) ADD_BOND(52, 53, 1) ADD_BOND(54, 53, 1) ADD_BOND(55, 56, 1) ADD_BOND(57, 56, 1) ADD_BOND(59, 58, 1) ADD_BOND(60, 61, 1) ADD_BOND(62, 61, 1) ADD_BOND(63, 64, 1) ADD_BOND(65, 64, 1) ADD_BOND(66, 67, 1) ADD_BOND(68, 69, 1) ADD_BOND(70, 69, 1) ADD_BOND(71, 72, 1) ADD_BOND(73, 72, 1) ADD_BOND(26, 75, 1) ADD_BOND(36, 75, 1) ADD_BOND(25, 74, 1) ADD_BOND(31, 74, 1) ADD_BOND(78, 83, 1) ADD_BOND(81, 83, 1) ADD_BOND(77, 82, 1) ADD_BOND(80, 82, 1) ADD_BOND(76, 78, 1) ADD_BOND(76, 77, 1) ADD_BOND(79, 80, 1) ADD_BOND(79, 81, 1) ADD_BOND(8, 12, 1) ADD_BOND(12, 84, 1) ADD_BOND(8, 13, 1) ADD_BOND(13, 85, 1) ADD_BOND(22, 23, 1) ADD_BOND(23, 84, 1) ADD_BOND(22, 28, 1) ADD_BOND(28, 85, 1) ADD_BOND(10, 86, 1) ADD_BOND(9, 10, 1) ADD_BOND(59, 65, 1) ADD_BOND(65, 86, 1) ADD_BOND(59, 60, 1) ADD_BOND(60, 87, 1) ADD_BOND(9, 15, 1) ADD_BOND(15, 87, 1) ADD_BOND(62, 89, 1) ADD_BOND(58, 62, 1) ADD_BOND(43, 49, 1) ADD_BOND(49, 88, 1) ADD_BOND(43, 44, 1) ADD_BOND(44, 89, 1) ADD_BOND(58, 63, 1) ADD_BOND(63, 88, 1) ADD_BOND(37, 41, 1) ADD_BOND(41, 90, 1) ADD_BOND(37, 38, 1) ADD_BOND(38, 91, 1) ADD_BOND(42, 47, 1) ADD_BOND(47, 90, 1) ADD_BOND(42, 46, 1) ADD_BOND(46, 91, 1) ADD_BOND(34, 93, 1) ADD_BOND(29, 34, 1) ADD_BOND(51, 52, 1) ADD_BOND(52, 92, 1) ADD_BOND(51, 57, 1) ADD_BOND(57, 93, 1) ADD_BOND(29, 33, 1) ADD_BOND(33, 92, 1) ADD_BOND(73, 95, 1) ADD_BOND(67, 73, 1) ADD_BOND(67, 68, 1) ADD_BOND(68, 94, 1) ADD_BOND(50, 54, 1) ADD_BOND(54, 94, 1) ADD_BOND(50, 55, 1) ADD_BOND(55, 95, 1) ADD_BOND(4, 97, 1) ADD_BOND(0, 4, 1) ADD_BOND(0, 5, 1) ADD_BOND(5, 96, 1) ADD_BOND(66, 70, 1) ADD_BOND(70, 96, 1) ADD_BOND(66, 71, 1) ADD_BOND(71, 97, 1) ADD_BOND(18, 99, 1) ADD_BOND(16, 18, 1) ADD_BOND(16, 19, 1) ADD_BOND(19, 98, 1) ADD_BOND(1, 7, 1) ADD_BOND(7, 98, 1) ADD_BOND(1, 2, 1) ADD_BOND(2, 99, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.236609f, 0.547395f) OUTLINE_POINT(2, -2.187641f, 0.238303f) OUTLINE_POINT(3, -2.775521f, 1.047396f) OUTLINE_POINT(4, -4.096978f, 1.330548f) OUTLINE_POINT(5, -4.775646f, 2.500369f) OUTLINE_POINT(6, -5.726557f, 2.809218f) OUTLINE_POINT(7, -5.726799f, 3.809341f) OUTLINE_POINT(8, -6.629710f, 4.815404f) OUTLINE_POINT(9, -6.490923f, 6.160377f) OUTLINE_POINT(10, -7.078560f, 6.969348f) OUTLINE_POINT(11, -6.490923f, 7.778440f) OUTLINE_POINT(12, -6.629710f, 9.123898f) OUTLINE_POINT(13, -5.727163f, 10.129719f) OUTLINE_POINT(14, -5.727163f, 11.129600f) OUTLINE_POINT(15, -4.776010f, 11.438691f) OUTLINE_POINT(16, -4.097705f, 12.608389f) OUTLINE_POINT(17, -2.776491f, 12.891664f) OUTLINE_POINT(18, -2.188732f, 13.700634f) OUTLINE_POINT(19, -1.237821f, 13.391664f) OUTLINE_POINT(20, -0.001212f, 13.939301f) OUTLINE_POINT(21, 1.234427f, 13.392028f) OUTLINE_POINT(22, 2.185580f, 13.700998f) OUTLINE_POINT(23, 2.773339f, 12.892028f) OUTLINE_POINT(24, 4.095645f, 12.608269f) OUTLINE_POINT(25, 4.773464f, 11.439176f) OUTLINE_POINT(26, 5.724376f, 11.130205f) OUTLINE_POINT(27, 5.724618f, 10.130324f) OUTLINE_POINT(28, 6.627165f, 9.124504f) OUTLINE_POINT(29, 6.488741f, 7.779168f) OUTLINE_POINT(30, 7.076379f, 6.970075f) OUTLINE_POINT(31, 6.488741f, 6.161104f) OUTLINE_POINT(32, 6.627772f, 4.816737f) OUTLINE_POINT(33, 5.724982f, 3.809826f) OUTLINE_POINT(34, 5.724982f, 2.809824f) OUTLINE_POINT(35, 4.773949f, 2.500733f) OUTLINE_POINT(36, 4.096372f, 1.331518f) OUTLINE_POINT(37, 2.774188f, 1.047638f) OUTLINE_POINT(38, 2.186429f, 0.238546f) OUTLINE_POINT(39, 1.235397f, 0.547517f) END_PATTERN() BEGIN_PATTERN("complexring7.mol") ADD_ATOM(0, 4.726183f, 3.153334f) ADD_ATOM(1, 4.138305f, 2.344364f) ADD_ATOM(2, 4.725941f, 1.535394f) ADD_ATOM(3, 5.677214f, 1.844364f) ADD_ATOM(4, 5.677214f, 2.844243f) ADD_ATOM(5, 4.138425f, 3.962546f) ADD_ATOM(6, 3.187395f, 3.653455f) ADD_ATOM(7, 3.187395f, 2.653455f) ADD_ATOM(8, 0.951152f, 1.309091f) ADD_ATOM(9, 0.000121f, 1.618182f) ADD_ATOM(10, -0.587636f, 0.809212f) ADD_ATOM(11, 0.000000f, 0.000000f) ADD_ATOM(12, 0.951031f, 0.309091f) ADD_ATOM(13, 1.539031f, 2.118061f) ADD_ATOM(14, 0.951274f, 2.927031f) ADD_ATOM(15, 0.000121f, 2.618061f) ADD_ATOM(16, 0.951637f, 8.349335f) ADD_ATOM(17, 0.000486f, 8.040486f) ADD_ATOM(18, 0.000486f, 7.040365f) ADD_ATOM(19, 0.951515f, 6.731274f) ADD_ATOM(20, 1.539395f, 7.540243f) ADD_ATOM(21, 0.951637f, 9.349335f) ADD_ATOM(22, 0.000727f, 9.658305f) ADD_ATOM(23, -0.587151f, 8.849335f) ADD_ATOM(24, -1.969333f, 4.329455f) ADD_ATOM(25, -1.969333f, 5.329456f) ADD_ATOM(26, -2.920364f, 5.638547f) ADD_ATOM(27, -3.508243f, 4.829577f) ADD_ATOM(28, -2.920364f, 4.020364f) ADD_ATOM(29, -1.018424f, 4.020243f) ADD_ATOM(30, -0.430545f, 4.829334f) ADD_ATOM(31, -1.018303f, 5.638425f) ADD_ATOM(32, 4.138668f, 7.313577f) ADD_ATOM(33, 4.726305f, 6.504486f) ADD_ATOM(34, 5.677577f, 6.813456f) ADD_ATOM(35, 5.677577f, 7.813456f) ADD_ATOM(36, 4.726426f, 8.122425f) ADD_ATOM(37, 3.187637f, 7.004608f) ADD_ATOM(38, 3.187515f, 6.004608f) ADD_ATOM(39, 4.138546f, 5.695517f) ADD_ATOM(40, 3.811880f, 4.828970f) ADD_ATOM(41, 6.417941f, 4.828850f) ADD_ATOM(42, 2.262667f, 6.961941f) ADD_ATOM(43, 3.067880f, 9.440487f) ADD_ATOM(44, -0.244605f, 6.147274f) ADD_ATOM(45, -2.352728f, 7.679274f) ADD_ATOM(46, -0.244727f, 3.511273f) ADD_ATOM(47, -2.353213f, 1.979395f) ADD_ATOM(48, 2.262183f, 2.696243f) ADD_ATOM(49, 3.067395f, 0.217818f) ADD_BOND(1, 0, 1) ADD_BOND(2, 3, 1) ADD_BOND(4, 3, 1) ADD_BOND(0, 4, 1) ADD_BOND(0, 5, 1) ADD_BOND(5, 6, 1) ADD_BOND(7, 6, 1) ADD_BOND(8, 9, 1) ADD_BOND(10, 11, 1) ADD_BOND(12, 11, 1) ADD_BOND(13, 14, 1) ADD_BOND(15, 14, 1) ADD_BOND(17, 16, 1) ADD_BOND(18, 19, 1) ADD_BOND(20, 19, 1) ADD_BOND(21, 22, 1) ADD_BOND(23, 22, 1) ADD_BOND(24, 25, 1) ADD_BOND(26, 27, 1) ADD_BOND(28, 27, 1) ADD_BOND(29, 30, 1) ADD_BOND(31, 30, 1) ADD_BOND(32, 33, 1) ADD_BOND(34, 35, 1) ADD_BOND(36, 35, 1) ADD_BOND(37, 38, 1) ADD_BOND(39, 38, 1) ADD_BOND(33, 34, 1) ADD_BOND(33, 39, 1) ADD_BOND(39, 40, 1) ADD_BOND(5, 40, 1) ADD_BOND(34, 41, 1) ADD_BOND(4, 41, 1) ADD_BOND(20, 42, 1) ADD_BOND(16, 20, 1) ADD_BOND(32, 36, 1) ADD_BOND(36, 43, 1) ADD_BOND(32, 37, 1) ADD_BOND(37, 42, 1) ADD_BOND(16, 21, 1) ADD_BOND(21, 43, 1) ADD_BOND(26, 45, 1) ADD_BOND(25, 26, 1) ADD_BOND(17, 23, 1) ADD_BOND(23, 45, 1) ADD_BOND(17, 18, 1) ADD_BOND(18, 44, 1) ADD_BOND(25, 31, 1) ADD_BOND(31, 44, 1) ADD_BOND(28, 47, 1) ADD_BOND(24, 28, 1) ADD_BOND(24, 29, 1) ADD_BOND(29, 46, 1) ADD_BOND(9, 10, 1) ADD_BOND(10, 47, 1) ADD_BOND(9, 15, 1) ADD_BOND(15, 46, 1) ADD_BOND(7, 48, 1) ADD_BOND(1, 7, 1) ADD_BOND(8, 12, 1) ADD_BOND(12, 49, 1) ADD_BOND(8, 13, 1) ADD_BOND(13, 48, 1) ADD_BOND(1, 2, 1) ADD_BOND(2, 49, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.587636f, 0.809212f) OUTLINE_POINT(2, -2.353213f, 1.979395f) OUTLINE_POINT(3, -2.920364f, 4.020364f) OUTLINE_POINT(4, -3.508243f, 4.829577f) OUTLINE_POINT(5, -2.920364f, 5.638547f) OUTLINE_POINT(6, -2.352728f, 7.679274f) OUTLINE_POINT(7, -0.587151f, 8.849335f) OUTLINE_POINT(8, 0.000727f, 9.658305f) OUTLINE_POINT(9, 0.951637f, 9.349335f) OUTLINE_POINT(10, 3.067880f, 9.440487f) OUTLINE_POINT(11, 4.726426f, 8.122425f) OUTLINE_POINT(12, 5.677577f, 7.813456f) OUTLINE_POINT(13, 5.677577f, 6.813456f) OUTLINE_POINT(14, 6.417941f, 4.828850f) OUTLINE_POINT(15, 5.677214f, 2.844243f) OUTLINE_POINT(16, 5.677214f, 1.844364f) OUTLINE_POINT(17, 4.725941f, 1.535394f) OUTLINE_POINT(18, 3.067395f, 0.217818f) OUTLINE_POINT(19, 0.951031f, 0.309091f) END_PATTERN() BEGIN_PATTERN("cube.mol") ADD_ATOM(0, 0.000000f, 0.000000f) ADD_ATOM(1, 0.852477f, 0.721579f) ADD_ATOM(2, 1.099590f, 0.000000f) ADD_ATOM(3, 0.016401f, 1.122395f) ADD_ATOM(4, 1.845693f, 0.721579f) ADD_ATOM(5, 0.836154f, 1.695113f) ADD_ATOM(6, 1.099590f, 1.083189f) ADD_ATOM(7, 1.812890f, 1.660748f) ADD_BOND(0, 1, 8) ADD_BOND(0, 2, 8) ADD_BOND(0, 3, 8) ADD_BOND(1, 4, 8) ADD_BOND(1, 5, 8) ADD_BOND(2, 4, 8) ADD_BOND(2, 6, 8) ADD_BOND(3, 5, 8) ADD_BOND(3, 6, 8) ADD_BOND(4, 7, 8) ADD_BOND(5, 7, 8) ADD_BOND(6, 7, 8) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, 0.016401f, 1.122395f) OUTLINE_POINT(2, 0.836154f, 1.695113f) OUTLINE_POINT(3, 1.812890f, 1.660748f) OUTLINE_POINT(4, 1.845693f, 0.721579f) OUTLINE_POINT(5, 1.099590f, 0.000000f) END_PATTERN() BEGIN_PATTERN("highvalence.mol") ADD_ATOM(0, -1.972560f, 0.103398f) ADD_ATOM(1, 0.499990f, 0.865882f) ADD_ATOM(2, -0.169097f, 1.609067f) ADD_ATOM(3, -1.082578f, 1.202275f) ADD_ATOM(4, -0.977980f, 0.207896f) ADD_ATOM(5, 0.000000f, 0.000000f) ADD_ATOM(6, -2.076957f, 1.079478f) ADD_BOND(1, 2, 1) ADD_BOND(2, 3, 1) ADD_BOND(3, 4, 1) ADD_BOND(4, 0, 1) ADD_BOND(4, 5, 1) ADD_BOND(5, 1, 1) ADD_BOND(6, 5, 1) ADD_BOND(6, 0, 1) ADD_BOND(6, 1, 1) ADD_BOND(6, 2, 1) ADD_BOND(6, 3, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.977980f, 0.207896f) OUTLINE_POINT(2, -1.972560f, 0.103398f) OUTLINE_POINT(3, -2.076957f, 1.079478f) OUTLINE_POINT(4, -0.169097f, 1.609067f) OUTLINE_POINT(5, 0.499990f, 0.865882f) END_PATTERN() BEGIN_PATTERN("krabe.mol") ADD_ATOM(0, 1.002530f, 0.220451f) ADD_ATOM(1, 0.000000f, 0.000000f) ADD_ATOM(2, 0.506116f, 0.713426f) ADD_ATOM(3, 0.430709f, 1.883966f) ADD_ATOM(4, -0.697952f, 1.883106f) ADD_ATOM(5, -0.853803f, 0.898261f) ADD_ATOM(6, -0.234206f, 1.419361f) ADD_ATOM(7, 0.944808f, 1.432502f) ADD_ATOM(8, 1.105940f, 2.419435f) ADD_BOND(0, 1, 1) ADD_BOND(1, 2, 1) ADD_BOND(2, 3, 1) ADD_BOND(3, 4, 1) ADD_BOND(4, 5, 1) ADD_BOND(5, 6, 1) ADD_BOND(6, 7, 1) ADD_BOND(0, 7, 1) ADD_BOND(7, 8, 1) ADD_BOND(3, 8, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, 0.506116f, 0.713426f) OUTLINE_POINT(2, 0.460141f, 1.427100f) OUTLINE_POINT(3, -0.234206f, 1.419361f) OUTLINE_POINT(4, -0.853803f, 0.898261f) OUTLINE_POINT(5, -0.697952f, 1.883106f) OUTLINE_POINT(6, 0.430709f, 1.883966f) OUTLINE_POINT(7, 1.105940f, 2.419435f) OUTLINE_POINT(8, 0.944808f, 1.432502f) OUTLINE_POINT(9, 1.002530f, 0.220451f) END_PATTERN() BEGIN_PATTERN("krabe2.mol") ADD_ATOM(0, 0.000000f, 0.000000f) ADD_ATOM(1, 0.751866f, 0.456841f) ADD_ATOM(2, 1.140345f, 1.315736f) ADD_ATOM(3, 0.460841f, 1.848939f) ADD_ATOM(4, 0.630171f, 0.863380f) ADD_ATOM(5, 1.807970f, 0.863380f) ADD_ATOM(6, 2.433656f, 0.343752f) ADD_ATOM(7, 2.266144f, 1.334523f) ADD_BOND(0, 1, 1) ADD_BOND(1, 2, 1) ADD_BOND(2, 3, 1) ADD_BOND(3, 4, 1) ADD_BOND(0, 4, 1) ADD_BOND(4, 5, 1) ADD_BOND(5, 6, 1) ADD_BOND(6, 7, 1) ADD_BOND(2, 7, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, 0.630171f, 0.863380f) OUTLINE_POINT(2, 0.460841f, 1.848939f) OUTLINE_POINT(3, 1.140345f, 1.315736f) OUTLINE_POINT(4, 2.266144f, 1.334523f) OUTLINE_POINT(5, 2.433656f, 0.343752f) OUTLINE_POINT(6, 1.807970f, 0.863380f) OUTLINE_POINT(7, 0.935744f, 0.863380f) OUTLINE_POINT(8, 0.751866f, 0.456841f) END_PATTERN() BEGIN_PATTERN("s13133.mol") ADD_ATOM(0, 2.362774f, 1.016073f) ADD_ATOM(1, -2.537358f, 0.997288f) ADD_ATOM(2, 1.774076f, 0.209697f) ADD_ATOM(3, 1.774076f, 1.800963f) ADD_ATOM(4, -1.911091f, 0.164025f) ADD_ATOM(5, -1.902988f, 1.852037f) ADD_ATOM(6, 0.153221f, 1.309133f) ADD_ATOM(7, -0.881513f, 1.516006f) ADD_ATOM(8, 0.854871f, 1.491820f) ADD_ATOM(9, -0.881513f, 0.499933f) ADD_ATOM(10, 0.854871f, 0.518840f) ADD_ATOM(11, -0.007980f, 2.010660f) ADD_ATOM(12, 0.000000f, 0.000000f) ADD_ATOM(13, -0.147696f, 0.962298f) ADD_BOND(1, 4, 1) ADD_BOND(2, 0, 1) ADD_BOND(3, 0, 1) ADD_BOND(4, 9, 1) ADD_BOND(5, 7, 1) ADD_BOND(6, 11, 1) ADD_BOND(7, 11, 1) ADD_BOND(8, 3, 1) ADD_BOND(9, 12, 1) ADD_BOND(10, 2, 1) ADD_BOND(11, 8, 1) ADD_BOND(12, 10, 1) ADD_BOND(13, 12, 1) ADD_BOND(10, 8, 1) ADD_BOND(13, 6, 2) ADD_BOND(9, 7, 1) ADD_BOND(1, 5, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -0.881513f, 0.499933f) OUTLINE_POINT(2, -1.911091f, 0.164025f) OUTLINE_POINT(3, -2.537358f, 0.997288f) OUTLINE_POINT(4, -1.902988f, 1.852037f) OUTLINE_POINT(5, -0.881513f, 1.516006f) OUTLINE_POINT(6, -0.007980f, 2.010660f) OUTLINE_POINT(7, 0.854871f, 1.491820f) OUTLINE_POINT(8, 1.774076f, 1.800963f) OUTLINE_POINT(9, 2.362774f, 1.016073f) OUTLINE_POINT(10, 1.774076f, 0.209697f) OUTLINE_POINT(11, 0.854871f, 0.518840f) END_PATTERN() BEGIN_PATTERN("sphere.mol") ADD_ATOM(0, 2.759692f, 3.259161f) ADD_ATOM(1, 2.747141f, 2.157137f) ADD_ATOM(2, 2.169687f, 3.827233f) ADD_ATOM(3, 2.647345f, 3.855381f) ADD_ATOM(4, 2.154090f, 1.592111f) ADD_ATOM(5, 2.662942f, 1.617091f) ADD_ATOM(6, 1.582729f, 3.265254f) ADD_ATOM(7, 1.745035f, 4.732589f) ADD_ATOM(8, 2.550473f, 3.305952f) ADD_ATOM(9, 2.041621f, 4.754400f) ADD_ATOM(10, 1.576514f, 2.157137f) ADD_ATOM(11, 1.720177f, 0.689923f) ADD_ATOM(12, 2.556809f, 2.178948f) ADD_ATOM(13, 2.029192f, 0.721117f) ADD_ATOM(14, 0.574408f, 3.608753f) ADD_ATOM(15, 0.730621f, 5.079135f) ADD_ATOM(16, 1.879315f, 3.646162f) ADD_ATOM(17, 1.345484f, 5.107161f) ADD_ATOM(18, 0.568193f, 1.826188f) ADD_ATOM(19, 0.708688f, 0.349592f) ADD_ATOM(20, 1.860550f, 1.841907f) ADD_ATOM(21, 1.295647f, 0.380786f) ADD_ATOM(22, -0.053005f, 2.722162f) ADD_ATOM(23, 0.137448f, 4.513988f) ADD_ATOM(24, 0.034362f, 5.444324f) ADD_ATOM(25, 1.442356f, 2.750310f) ADD_ATOM(26, 1.261284f, 4.548350f) ADD_ATOM(27, 0.343377f, 5.456752f) ADD_ATOM(28, 0.137448f, 0.917785f) ADD_ATOM(29, 0.000000f, 0.000000f) ADD_ATOM(30, 1.236183f, 0.955194f) ADD_ATOM(31, 0.277821f, 0.015597f) ADD_ATOM(32, -1.117622f, 2.734591f) ADD_ATOM(33, -0.927046f, 4.532753f) ADD_ATOM(34, -0.995770f, 5.104114f) ADD_ATOM(35, 0.384076f, 2.750310f) ADD_ATOM(36, 0.203005f, 4.542135f) ADD_ATOM(37, -0.402597f, 5.119711f) ADD_ATOM(38, -0.955194f, 0.920953f) ADD_ATOM(39, -1.008321f, 0.343377f) ADD_ATOM(40, 0.168642f, 0.961408f) ADD_ATOM(41, -0.430745f, 0.408934f) ADD_ATOM(42, -1.560795f, 3.636901f) ADD_ATOM(43, -1.576392f, 1.816805f) ADD_ATOM(44, -1.676311f, 4.732589f) ADD_ATOM(45, -0.234077f, 3.639947f) ADD_ATOM(46, -0.243459f, 1.863718f) ADD_ATOM(47, -1.420301f, 4.788763f) ADD_ATOM(48, -1.713841f, 0.689923f) ADD_ATOM(49, -1.439066f, 0.761694f) ADD_ATOM(50, -2.247550f, 3.265254f) ADD_ATOM(51, -2.272652f, 2.150922f) ADD_ATOM(52, -2.297509f, 3.814804f) ADD_ATOM(53, -1.248734f, 3.308999f) ADD_ATOM(54, -1.254948f, 2.213310f) ADD_ATOM(55, -1.848000f, 3.870977f) ADD_ATOM(56, -2.347469f, 1.588943f) ADD_ATOM(57, -1.857382f, 1.670096f) ADD_ATOM(58, -2.450555f, 3.330932f) ADD_ATOM(59, -2.456769f, 2.228907f) ADD_BOND(0, 1, 8) ADD_BOND(0, 2, 8) ADD_BOND(0, 3, 8) ADD_BOND(1, 4, 8) ADD_BOND(1, 5, 8) ADD_BOND(2, 6, 8) ADD_BOND(2, 7, 8) ADD_BOND(3, 8, 8) ADD_BOND(3, 9, 8) ADD_BOND(4, 10, 8) ADD_BOND(4, 11, 8) ADD_BOND(5, 12, 8) ADD_BOND(5, 13, 8) ADD_BOND(6, 10, 8) ADD_BOND(6, 14, 8) ADD_BOND(7, 9, 8) ADD_BOND(7, 15, 8) ADD_BOND(8, 12, 8) ADD_BOND(8, 16, 8) ADD_BOND(9, 17, 8) ADD_BOND(10, 18, 8) ADD_BOND(11, 13, 8) ADD_BOND(11, 19, 8) ADD_BOND(12, 20, 8) ADD_BOND(13, 21, 8) ADD_BOND(14, 22, 8) ADD_BOND(14, 23, 8) ADD_BOND(15, 23, 8) ADD_BOND(15, 24, 8) ADD_BOND(16, 25, 8) ADD_BOND(16, 26, 8) ADD_BOND(17, 26, 8) ADD_BOND(17, 27, 8) ADD_BOND(18, 22, 8) ADD_BOND(18, 28, 8) ADD_BOND(19, 28, 8) ADD_BOND(19, 29, 8) ADD_BOND(20, 25, 8) ADD_BOND(20, 30, 8) ADD_BOND(21, 30, 8) ADD_BOND(21, 31, 8) ADD_BOND(22, 32, 8) ADD_BOND(23, 33, 8) ADD_BOND(24, 27, 8) ADD_BOND(24, 34, 8) ADD_BOND(25, 35, 8) ADD_BOND(26, 36, 8) ADD_BOND(27, 37, 8) ADD_BOND(28, 38, 8) ADD_BOND(29, 31, 8) ADD_BOND(29, 39, 8) ADD_BOND(30, 40, 8) ADD_BOND(31, 41, 8) ADD_BOND(32, 42, 8) ADD_BOND(32, 43, 8) ADD_BOND(33, 34, 8) ADD_BOND(33, 42, 8) ADD_BOND(34, 44, 8) ADD_BOND(35, 45, 8) ADD_BOND(35, 46, 8) ADD_BOND(36, 37, 8) ADD_BOND(36, 45, 8) ADD_BOND(37, 47, 8) ADD_BOND(38, 39, 8) ADD_BOND(38, 43, 8) ADD_BOND(39, 48, 8) ADD_BOND(40, 41, 8) ADD_BOND(40, 46, 8) ADD_BOND(41, 49, 8) ADD_BOND(42, 50, 8) ADD_BOND(43, 51, 8) ADD_BOND(44, 47, 8) ADD_BOND(44, 52, 8) ADD_BOND(45, 53, 8) ADD_BOND(46, 54, 8) ADD_BOND(47, 55, 8) ADD_BOND(48, 49, 8) ADD_BOND(48, 56, 8) ADD_BOND(49, 57, 8) ADD_BOND(50, 51, 8) ADD_BOND(50, 52, 8) ADD_BOND(51, 56, 8) ADD_BOND(52, 58, 8) ADD_BOND(53, 54, 8) ADD_BOND(53, 55, 8) ADD_BOND(54, 57, 8) ADD_BOND(55, 58, 8) ADD_BOND(56, 59, 8) ADD_BOND(57, 59, 8) ADD_BOND(58, 59, 8) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.008321f, 0.343377f) OUTLINE_POINT(2, -1.713841f, 0.689923f) OUTLINE_POINT(3, -2.347469f, 1.588943f) OUTLINE_POINT(4, -2.456769f, 2.228907f) OUTLINE_POINT(5, -2.450555f, 3.330932f) OUTLINE_POINT(6, -2.297509f, 3.814804f) OUTLINE_POINT(7, -1.676311f, 4.732589f) OUTLINE_POINT(8, -0.995770f, 5.104114f) OUTLINE_POINT(9, 0.034362f, 5.444324f) OUTLINE_POINT(10, 0.343377f, 5.456752f) OUTLINE_POINT(11, 1.345484f, 5.107161f) OUTLINE_POINT(12, 2.041621f, 4.754400f) OUTLINE_POINT(13, 2.647345f, 3.855381f) OUTLINE_POINT(14, 2.759692f, 3.259161f) OUTLINE_POINT(15, 2.747141f, 2.157137f) OUTLINE_POINT(16, 2.662942f, 1.617091f) OUTLINE_POINT(17, 2.029192f, 0.721117f) OUTLINE_POINT(18, 1.295647f, 0.380786f) OUTLINE_POINT(19, 0.277821f, 0.015597f) END_PATTERN() BEGIN_PATTERN("sphere2.mol") ADD_ATOM(0, -0.693502f, 5.486971f) ADD_ATOM(1, -1.611692f, 4.995659f) ADD_ATOM(2, -2.389866f, 4.801763f) ADD_ATOM(3, -1.953765f, 5.174692f) ADD_ATOM(4, -0.904320f, 5.599380f) ADD_ATOM(5, -0.065893f, 5.441914f) ADD_ATOM(6, 0.992377f, 5.170047f) ADD_ATOM(7, 1.201005f, 5.059097f) ADD_ATOM(8, 0.355145f, 5.218223f) ADD_ATOM(9, 0.494297f, 4.456306f) ADD_ATOM(10, -0.415464f, 3.970104f) ADD_ATOM(11, -1.473867f, 4.241374f) ADD_ATOM(12, -2.114084f, 3.284100f) ADD_ATOM(13, -2.885690f, 3.091597f) ADD_ATOM(14, -3.023912f, 3.854244f) ADD_ATOM(15, -3.226701f, 3.270563f) ADD_ATOM(16, -2.795112f, 3.638914f) ADD_ATOM(17, -2.155624f, 4.595922f) ADD_ATOM(18, -1.309566f, 4.436797f) ADD_ATOM(19, -0.269080f, 4.857902f) ADD_ATOM(20, 0.664438f, 4.225249f) ADD_ATOM(21, 1.443276f, 4.418416f) ADD_ATOM(22, 2.097628f, 3.563730f) ADD_ATOM(23, 2.307649f, 3.452117f) ADD_ATOM(24, 1.861528f, 4.196582f) ADD_ATOM(25, 1.426023f, 3.824516f) ADD_ATOM(26, 1.440555f, 2.711966f) ADD_ATOM(27, 0.522433f, 2.219659f) ADD_ATOM(28, -0.401529f, 2.845676f) ADD_ATOM(29, -1.452168f, 2.421054f) ADD_ATOM(30, -1.568625f, 1.374795f) ADD_ATOM(31, -2.347862f, 1.181230f) ADD_ATOM(32, -3.002744f, 2.035650f) ADD_ATOM(33, -3.212434f, 2.147329f) ADD_ATOM(34, -2.765715f, 1.402997f) ADD_ATOM(35, -2.329680f, 1.775396f) ADD_ATOM(36, -2.344345f, 2.887946f) ADD_ATOM(37, -1.426421f, 3.380119f) ADD_ATOM(38, -0.502526f, 2.754102f) ADD_ATOM(39, 0.548047f, 3.178990f) ADD_ATOM(40, 1.980507f, 2.508115f) ADD_ATOM(41, 2.119128f, 1.745933f) ADD_ATOM(42, 2.322115f, 2.329149f) ADD_ATOM(43, 1.890924f, 1.960599f) ADD_ATOM(44, 1.251437f, 1.003525f) ADD_ATOM(45, 0.405312f, 1.163048f) ADD_ATOM(46, -0.635241f, 0.742209f) ADD_ATOM(47, -0.838361f, 0.157732f) ADD_ATOM(48, -1.896764f, 0.429466f) ADD_ATOM(49, -2.105458f, 0.540415f) ADD_ATOM(50, -1.259532f, 0.381357f) ADD_ATOM(51, -1.398219f, 1.143605f) ADD_ATOM(52, -0.488524f, 1.629674f) ADD_ATOM(53, 0.569679f, 1.357741f) ADD_ATOM(54, 0.707371f, 0.603588f) ADD_ATOM(55, 1.485745f, 0.797949f) ADD_ATOM(56, 1.049511f, 0.424821f) ADD_ATOM(57, 0.000000f, 0.000000f) ADD_ATOM(58, -0.211017f, 0.112277f) ADD_ATOM(59, -0.527542f, 1.703597f) ADD_ATOM(60, 0.598213f, 3.386755f) ADD_ATOM(61, -0.566361f, 4.902361f) ADD_ATOM(62, -2.412428f, 4.156834f) ADD_ATOM(63, -2.783632f, 4.350930f) ADD_ATOM(64, -1.169419f, 5.217824f) ADD_ATOM(65, 0.201395f, 5.557310f) ADD_ATOM(66, 1.840426f, 4.445622f) ADD_ATOM(67, 1.484617f, 3.419270f) ADD_ATOM(68, 1.879245f, 1.248649f) ADD_ATOM(69, -2.745078f, 1.153758f) ADD_ATOM(70, -2.389269f, 2.180442f) ADD_ATOM(71, -2.988477f, 2.494844f) ADD_ATOM(72, -1.105848f, 0.042071f) ADD_ATOM(73, -0.337826f, 0.697019f) ADD_ATOM(74, 1.507842f, 1.442811f) ADD_ATOM(75, 0.265165f, 0.381755f) ADD_ATOM(76, 2.084489f, 3.104802f) ADD_ATOM(77, -0.376645f, 3.896381f) ADD_ATOM(78, -1.501936f, 2.213023f) ADD_ATOM(79, 1.209233f, 2.315546f) ADD_BOND(11, 1, 1) ADD_BOND(2, 1, 1) ADD_BOND(12, 11, 1) ADD_BOND(14, 2, 1) ADD_BOND(13, 14, 1) ADD_BOND(3, 2, 1) ADD_BOND(15, 14, 1) ADD_BOND(17, 3, 1) ADD_BOND(16, 17, 1) ADD_BOND(5, 4, 1) ADD_BOND(3, 4, 1) ADD_BOND(19, 5, 1) ADD_BOND(18, 17, 1) ADD_BOND(8, 0, 1) ADD_BOND(4, 0, 1) ADD_BOND(7, 8, 1) ADD_BOND(6, 5, 1) ADD_BOND(1, 0, 1) ADD_BOND(9, 8, 1) ADD_BOND(10, 11, 1) ADD_BOND(25, 9, 1) ADD_BOND(10, 9, 1) ADD_BOND(26, 25, 1) ADD_BOND(28, 10, 1) ADD_BOND(27, 28, 1) ADD_BOND(29, 12, 1) ADD_BOND(13, 12, 1) ADD_BOND(30, 29, 1) ADD_BOND(32, 13, 1) ADD_BOND(31, 32, 1) ADD_BOND(33, 15, 1) ADD_BOND(16, 15, 1) ADD_BOND(34, 33, 1) ADD_BOND(36, 16, 1) ADD_BOND(35, 36, 1) ADD_BOND(20, 19, 1) ADD_BOND(18, 19, 1) ADD_BOND(39, 20, 1) ADD_BOND(37, 18, 1) ADD_BOND(38, 37, 1) ADD_BOND(24, 7, 1) ADD_BOND(6, 7, 1) ADD_BOND(23, 24, 1) ADD_BOND(21, 6, 1) ADD_BOND(22, 21, 1) ADD_BOND(24, 25, 1) ADD_BOND(28, 29, 1) ADD_BOND(32, 33, 1) ADD_BOND(36, 37, 1) ADD_BOND(20, 21, 1) ADD_BOND(26, 43, 1) ADD_BOND(27, 26, 1) ADD_BOND(27, 45, 1) ADD_BOND(44, 45, 1) ADD_BOND(43, 44, 1) ADD_BOND(56, 44, 1) ADD_BOND(30, 46, 1) ADD_BOND(31, 30, 1) ADD_BOND(31, 48, 1) ADD_BOND(47, 48, 1) ADD_BOND(46, 47, 1) ADD_BOND(57, 47, 1) ADD_BOND(23, 42, 1) ADD_BOND(22, 23, 1) ADD_BOND(22, 40, 1) ADD_BOND(41, 40, 1) ADD_BOND(42, 41, 1) ADD_BOND(55, 41, 1) ADD_BOND(39, 79, 1) ADD_BOND(38, 39, 1) ADD_BOND(38, 52, 1) ADD_BOND(53, 52, 1) ADD_BOND(79, 53, 1) ADD_BOND(54, 53, 1) ADD_BOND(34, 49, 1) ADD_BOND(35, 34, 1) ADD_BOND(35, 51, 1) ADD_BOND(50, 51, 1) ADD_BOND(49, 50, 1) ADD_BOND(58, 50, 1) ADD_BOND(42, 43, 1) ADD_BOND(45, 46, 1) ADD_BOND(48, 49, 1) ADD_BOND(51, 52, 1) ADD_BOND(79, 40, 1) ADD_BOND(55, 56, 1) ADD_BOND(56, 57, 1) ADD_BOND(57, 58, 1) ADD_BOND(58, 54, 1) ADD_BOND(54, 55, 1) ADD_BOND(55, 68, 1) ADD_BOND(68, 56, 1) ADD_BOND(36, 71, 1) ADD_BOND(71, 35, 1) ADD_BOND(71, 34, 1) ADD_BOND(71, 33, 1) ADD_BOND(16, 71, 1) ADD_BOND(15, 71, 1) ADD_BOND(41, 68, 1) ADD_BOND(42, 68, 1) ADD_BOND(43, 68, 1) ADD_BOND(68, 44, 1) ADD_BOND(22, 66, 1) ADD_BOND(23, 66, 1) ADD_BOND(24, 66, 1) ADD_BOND(21, 66, 1) ADD_BOND(6, 66, 1) ADD_BOND(7, 66, 1) ADD_BOND(6, 65, 1) ADD_BOND(7, 65, 1) ADD_BOND(5, 65, 1) ADD_BOND(4, 65, 1) ADD_BOND(0, 65, 1) ADD_BOND(8, 65, 1) ADD_BOND(0, 61, 1) ADD_BOND(8, 61, 1) ADD_BOND(9, 61, 1) ADD_BOND(10, 61, 1) ADD_BOND(1, 61, 1) ADD_BOND(11, 61, 1) ADD_BOND(13, 70, 1) ADD_BOND(32, 70, 1) ADD_BOND(12, 70, 1) ADD_BOND(29, 70, 1) ADD_BOND(30, 70, 1) ADD_BOND(31, 70, 1) ADD_BOND(19, 64, 1) ADD_BOND(5, 64, 1) ADD_BOND(4, 64, 1) ADD_BOND(18, 64, 1) ADD_BOND(17, 64, 1) ADD_BOND(3, 64, 1) ADD_BOND(17, 63, 1) ADD_BOND(16, 63, 1) ADD_BOND(3, 63, 1) ADD_BOND(2, 63, 1) ADD_BOND(14, 63, 1) ADD_BOND(15, 63, 1) ADD_BOND(2, 62, 1) ADD_BOND(14, 62, 1) ADD_BOND(13, 62, 1) ADD_BOND(12, 62, 1) ADD_BOND(1, 62, 1) ADD_BOND(11, 62, 1) ADD_BOND(9, 60, 1) ADD_BOND(25, 60, 1) ADD_BOND(26, 60, 1) ADD_BOND(27, 60, 1) ADD_BOND(10, 60, 1) ADD_BOND(28, 60, 1) ADD_BOND(67, 20, 1) ADD_BOND(67, 39, 1) ADD_BOND(67, 79, 1) ADD_BOND(40, 67, 1) ADD_BOND(22, 67, 1) ADD_BOND(21, 67, 1) ADD_BOND(44, 75, 1) ADD_BOND(56, 75, 1) ADD_BOND(75, 46, 1) ADD_BOND(75, 47, 1) ADD_BOND(75, 57, 1) ADD_BOND(33, 69, 1) ADD_BOND(34, 69, 1) ADD_BOND(49, 69, 1) ADD_BOND(32, 69, 1) ADD_BOND(31, 69, 1) ADD_BOND(48, 69, 1) ADD_BOND(50, 72, 1) ADD_BOND(49, 72, 1) ADD_BOND(48, 72, 1) ADD_BOND(58, 72, 1) ADD_BOND(47, 72, 1) ADD_BOND(57, 72, 1) ADD_BOND(55, 74, 1) ADD_BOND(41, 74, 1) ADD_BOND(74, 53, 1) ADD_BOND(74, 54, 1) ADD_BOND(74, 40, 1) ADD_BOND(52, 73, 1) ADD_BOND(50, 73, 1) ADD_BOND(51, 73, 1) ADD_BOND(53, 73, 1) ADD_BOND(54, 73, 1) ADD_BOND(73, 58, 1) ADD_BOND(42, 76, 1) ADD_BOND(23, 76, 1) ADD_BOND(24, 76, 1) ADD_BOND(43, 76, 1) ADD_BOND(25, 76, 1) ADD_BOND(26, 76, 1) ADD_BOND(45, 59, 1) ADD_BOND(27, 59, 1) ADD_BOND(28, 59, 1) ADD_BOND(59, 29, 1) ADD_BOND(59, 46, 1) ADD_BOND(59, 30, 1) ADD_BOND(39, 77, 1) ADD_BOND(20, 77, 1) ADD_BOND(19, 77, 1) ADD_BOND(18, 77, 1) ADD_BOND(38, 77, 1) ADD_BOND(37, 77, 1) ADD_BOND(38, 78, 1) ADD_BOND(37, 78, 1) ADD_BOND(36, 78, 1) ADD_BOND(35, 78, 1) ADD_BOND(51, 78, 1) ADD_BOND(52, 78, 1) ADD_BOND(45, 75, 1) ADD_BOND(74, 79, 1) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.105848f, 0.042071f) OUTLINE_POINT(2, -1.896764f, 0.429466f) OUTLINE_POINT(3, -2.105458f, 0.540415f) OUTLINE_POINT(4, -2.745078f, 1.153758f) OUTLINE_POINT(5, -3.212434f, 2.147329f) OUTLINE_POINT(6, -3.226701f, 3.270563f) OUTLINE_POINT(7, -3.023912f, 3.854244f) OUTLINE_POINT(8, -2.783632f, 4.350930f) OUTLINE_POINT(9, -2.389866f, 4.801763f) OUTLINE_POINT(10, -1.953765f, 5.174692f) OUTLINE_POINT(11, -0.904320f, 5.599380f) OUTLINE_POINT(12, 0.201395f, 5.557310f) OUTLINE_POINT(13, 0.992377f, 5.170047f) OUTLINE_POINT(14, 1.201005f, 5.059097f) OUTLINE_POINT(15, 1.840426f, 4.445622f) OUTLINE_POINT(16, 2.307649f, 3.452117f) OUTLINE_POINT(17, 2.322115f, 2.329149f) OUTLINE_POINT(18, 2.119128f, 1.745933f) OUTLINE_POINT(19, 1.879245f, 1.248649f) OUTLINE_POINT(20, 1.485745f, 0.797949f) OUTLINE_POINT(21, 1.049511f, 0.424821f) END_PATTERN() BEGIN_PATTERN("sphere_mod1.mol") ADD_ATOM(0, 2.745476f, 3.242372f) ADD_ATOM(1, 2.732990f, 2.146024f) ADD_ATOM(2, 2.158510f, 3.807517f) ADD_ATOM(3, 2.633707f, 3.835520f) ADD_ATOM(4, 3.695870f, 3.779635f) ADD_ATOM(5, 2.142993f, 1.583910f) ADD_ATOM(6, 2.649224f, 1.608761f) ADD_ATOM(7, 3.680232f, 1.587062f) ADD_ATOM(8, 1.574575f, 3.248433f) ADD_ATOM(9, 1.736045f, 4.708210f) ADD_ATOM(10, 2.537335f, 3.288922f) ADD_ATOM(11, 2.031104f, 4.729908f) ADD_ATOM(12, 4.646143f, 3.220673f) ADD_ATOM(13, 1.568393f, 2.146024f) ADD_ATOM(14, 1.711316f, 0.686369f) ADD_ATOM(15, 2.543638f, 2.167723f) ADD_ATOM(16, 2.018739f, 0.717402f) ADD_ATOM(17, 4.633657f, 2.124325f) ADD_ATOM(18, 0.571449f, 3.590163f) ADD_ATOM(19, 0.726857f, 5.052970f) ADD_ATOM(20, 1.869634f, 3.627379f) ADD_ATOM(21, 1.338553f, 5.080852f) ADD_ATOM(22, 5.596538f, 3.760967f) ADD_ATOM(23, 0.565266f, 1.816781f) ADD_ATOM(24, 0.705037f, 0.347791f) ADD_ATOM(25, 1.850966f, 1.832418f) ADD_ATOM(26, 1.288972f, 0.378824f) ADD_ATOM(27, 5.574717f, 1.571424f) ADD_ATOM(28, -0.052732f, 2.708139f) ADD_ATOM(29, 0.136740f, 4.490734f) ADD_ATOM(30, 0.034185f, 5.416277f) ADD_ATOM(31, 1.434925f, 2.736142f) ADD_ATOM(32, 1.254787f, 4.524920f) ADD_ATOM(33, 0.341609f, 5.428642f) ADD_ATOM(34, 6.540627f, 3.205035f) ADD_ATOM(35, 0.136740f, 0.913057f) ADD_ATOM(36, 0.000000f, 0.000000f) ADD_ATOM(37, 1.229815f, 0.950273f) ADD_ATOM(38, 0.276390f, 0.015517f) ADD_ATOM(39, 6.528142f, 2.111839f) ADD_ATOM(40, -1.111864f, 2.720504f) ADD_ATOM(41, -0.922271f, 4.509402f) ADD_ATOM(42, -0.990641f, 5.077821f) ADD_ATOM(43, 0.382097f, 2.736142f) ADD_ATOM(44, 0.201959f, 4.518737f) ADD_ATOM(45, -0.400523f, 5.093338f) ADD_ATOM(46, -0.950273f, 0.916209f) ADD_ATOM(47, -1.003127f, 0.341609f) ADD_ATOM(48, 0.167773f, 0.956455f) ADD_ATOM(49, -0.428526f, 0.406827f) ADD_ATOM(50, -1.552755f, 3.618165f) ADD_ATOM(51, -1.568272f, 1.807446f) ADD_ATOM(52, -1.667675f, 4.708210f) ADD_ATOM(53, -0.232871f, 3.621196f) ADD_ATOM(54, -0.242205f, 1.854117f) ADD_ATOM(55, -1.412984f, 4.764094f) ADD_ATOM(56, -1.705012f, 0.686369f) ADD_ATOM(57, -1.431653f, 0.757770f) ADD_ATOM(58, -2.235972f, 3.248433f) ADD_ATOM(59, -2.260944f, 2.139842f) ADD_ATOM(60, -2.285674f, 3.795152f) ADD_ATOM(61, -1.242301f, 3.291952f) ADD_ATOM(62, -1.248484f, 2.201908f) ADD_ATOM(63, -1.838480f, 3.851036f) ADD_ATOM(64, -2.335376f, 1.580758f) ADD_ATOM(65, -1.847814f, 1.661493f) ADD_ATOM(66, -2.437931f, 3.313773f) ADD_ATOM(67, -2.444113f, 2.217425f) ADD_BOND(0, 1, 8) ADD_BOND(0, 2, 8) ADD_BOND(0, 3, 8) ADD_BOND(0, 4, 8) ADD_BOND(1, 5, 8) ADD_BOND(1, 6, 8) ADD_BOND(1, 7, 8) ADD_BOND(2, 8, 8) ADD_BOND(2, 9, 8) ADD_BOND(3, 10, 8) ADD_BOND(3, 11, 8) ADD_BOND(4, 12, 8) ADD_BOND(5, 13, 8) ADD_BOND(5, 14, 8) ADD_BOND(6, 15, 8) ADD_BOND(6, 16, 8) ADD_BOND(7, 17, 8) ADD_BOND(8, 13, 8) ADD_BOND(8, 18, 8) ADD_BOND(9, 11, 8) ADD_BOND(9, 19, 8) ADD_BOND(10, 15, 8) ADD_BOND(10, 20, 8) ADD_BOND(11, 21, 8) ADD_BOND(12, 17, 8) ADD_BOND(12, 22, 8) ADD_BOND(13, 23, 8) ADD_BOND(14, 16, 8) ADD_BOND(14, 24, 8) ADD_BOND(15, 25, 8) ADD_BOND(16, 26, 8) ADD_BOND(17, 27, 8) ADD_BOND(18, 28, 8) ADD_BOND(18, 29, 8) ADD_BOND(19, 29, 8) ADD_BOND(19, 30, 8) ADD_BOND(20, 31, 8) ADD_BOND(20, 32, 8) ADD_BOND(21, 32, 8) ADD_BOND(21, 33, 8) ADD_BOND(22, 34, 8) ADD_BOND(23, 28, 8) ADD_BOND(23, 35, 8) ADD_BOND(24, 35, 8) ADD_BOND(24, 36, 8) ADD_BOND(25, 31, 8) ADD_BOND(25, 37, 8) ADD_BOND(26, 37, 8) ADD_BOND(26, 38, 8) ADD_BOND(27, 39, 8) ADD_BOND(28, 40, 8) ADD_BOND(29, 41, 8) ADD_BOND(30, 33, 8) ADD_BOND(30, 42, 8) ADD_BOND(31, 43, 8) ADD_BOND(32, 44, 8) ADD_BOND(33, 45, 8) ADD_BOND(34, 39, 8) ADD_BOND(35, 46, 8) ADD_BOND(36, 38, 8) ADD_BOND(36, 47, 8) ADD_BOND(37, 48, 8) ADD_BOND(38, 49, 8) ADD_BOND(40, 50, 8) ADD_BOND(40, 51, 8) ADD_BOND(41, 42, 8) ADD_BOND(41, 50, 8) ADD_BOND(42, 52, 8) ADD_BOND(43, 53, 8) ADD_BOND(43, 54, 8) ADD_BOND(44, 45, 8) ADD_BOND(44, 53, 8) ADD_BOND(45, 55, 8) ADD_BOND(46, 47, 8) ADD_BOND(46, 51, 8) ADD_BOND(47, 56, 8) ADD_BOND(48, 49, 8) ADD_BOND(48, 54, 8) ADD_BOND(49, 57, 8) ADD_BOND(50, 58, 8) ADD_BOND(51, 59, 8) ADD_BOND(52, 55, 8) ADD_BOND(52, 60, 8) ADD_BOND(53, 61, 8) ADD_BOND(54, 62, 8) ADD_BOND(55, 63, 8) ADD_BOND(56, 57, 8) ADD_BOND(56, 64, 8) ADD_BOND(57, 65, 8) ADD_BOND(58, 59, 8) ADD_BOND(58, 60, 8) ADD_BOND(59, 64, 8) ADD_BOND(60, 66, 8) ADD_BOND(61, 62, 8) ADD_BOND(61, 63, 8) ADD_BOND(62, 65, 8) ADD_BOND(63, 66, 8) ADD_BOND(64, 67, 8) ADD_BOND(65, 67, 8) ADD_BOND(66, 67, 8) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -1.003127f, 0.341609f) OUTLINE_POINT(2, -1.705012f, 0.686369f) OUTLINE_POINT(3, -2.335376f, 1.580758f) OUTLINE_POINT(4, -2.444113f, 2.217425f) OUTLINE_POINT(5, -2.437931f, 3.313773f) OUTLINE_POINT(6, -2.285674f, 3.795152f) OUTLINE_POINT(7, -1.667675f, 4.708210f) OUTLINE_POINT(8, -0.990641f, 5.077821f) OUTLINE_POINT(9, 0.034185f, 5.416277f) OUTLINE_POINT(10, 0.341609f, 5.428642f) OUTLINE_POINT(11, 1.338553f, 5.080852f) OUTLINE_POINT(12, 2.031104f, 4.729908f) OUTLINE_POINT(13, 2.633707f, 3.835520f) OUTLINE_POINT(14, 2.745476f, 3.242372f) OUTLINE_POINT(15, 3.695870f, 3.779635f) OUTLINE_POINT(16, 4.646143f, 3.220673f) OUTLINE_POINT(17, 5.596538f, 3.760967f) OUTLINE_POINT(18, 6.540627f, 3.205035f) OUTLINE_POINT(19, 6.528142f, 2.111839f) OUTLINE_POINT(20, 5.574717f, 1.571424f) OUTLINE_POINT(21, 4.633657f, 2.124325f) OUTLINE_POINT(22, 3.680232f, 1.587062f) OUTLINE_POINT(23, 2.732990f, 2.146024f) OUTLINE_POINT(24, 2.649224f, 1.608761f) OUTLINE_POINT(25, 2.018739f, 0.717402f) OUTLINE_POINT(26, 1.288972f, 0.378824f) OUTLINE_POINT(27, 0.276390f, 0.015517f) END_PATTERN() BEGIN_PATTERN("superstar.mol") ADD_ATOM(0, 3.903066f, 6.749758f) ADD_ATOM(1, 4.210220f, 5.798113f) ADD_ATOM(2, 4.713134f, 7.335945f) ADD_ATOM(3, 5.210229f, 5.796174f) ADD_ATOM(4, 5.521141f, 6.746485f) ADD_ATOM(5, 3.620881f, 4.990106f) ADD_ATOM(6, 2.620872f, 4.992166f) ADD_ATOM(7, 3.928036f, 4.038582f) ADD_ATOM(8, 2.310082f, 4.041612f) ADD_ATOM(9, 3.117846f, 3.452274f) ADD_ATOM(10, 5.796416f, 4.985863f) ADD_ATOM(11, 5.485505f, 4.035552f) ADD_ATOM(12, 6.796426f, 4.983924f) ADD_ATOM(13, 6.293270f, 3.446092f) ADD_ATOM(14, 7.103580f, 4.032279f) ADD_ATOM(15, 6.472665f, 7.053640f) ADD_ATOM(16, 7.280673f, 6.464180f) ADD_ATOM(17, 6.783819f, 8.003951f) ADD_ATOM(18, 8.090861f, 7.050367f) ADD_ATOM(19, 7.783829f, 8.002011f) ADD_ATOM(20, 4.715073f, 8.335955f) ADD_ATOM(21, 5.525262f, 8.922141f) ADD_ATOM(22, 3.907187f, 8.925293f) ADD_ATOM(23, 5.218230f, 9.873786f) ADD_ATOM(24, 4.218220f, 9.875847f) ADD_ATOM(25, 2.952512f, 7.060549f) ADD_ATOM(26, 2.645358f, 8.012194f) ADD_ATOM(27, 2.142323f, 6.474240f) ADD_ATOM(28, 1.645470f, 8.014012f) ADD_ATOM(29, 1.334558f, 7.063700f) ADD_ATOM(30, 6.395816f, 2.451537f) ADD_ATOM(31, 7.308430f, 2.042927f) ADD_ATOM(32, 8.016073f, 3.623548f) ADD_ATOM(33, 8.118498f, 2.628993f) ADD_ATOM(34, 9.068446f, 6.840426f) ADD_ATOM(35, 9.739120f, 7.582129f) ADD_ATOM(36, 8.454380f, 8.743594f) ADD_ATOM(37, 9.431965f, 8.533774f) ADD_ATOM(38, 5.719810f, 10.738643f) ADD_ATOM(39, 5.221623f, 11.605681f) ADD_ATOM(40, 3.720034f, 10.742764f) ADD_ATOM(41, 4.221614f, 11.607621f) ADD_ATOM(42, 0.977827f, 8.758504f) ADD_ATOM(43, -0.000727f, 8.552563f) ADD_ATOM(44, 0.356125f, 6.857881f) ADD_ATOM(45, -0.311639f, 7.602251f) ADD_ATOM(46, 1.395770f, 3.636760f) ADD_ATOM(47, 1.289345f, 2.642448f) ADD_ATOM(48, 3.011300f, 2.458204f) ADD_ATOM(49, 2.097110f, 2.053109f) ADD_ATOM(50, 0.479277f, 2.056504f) ADD_ATOM(51, 1.786198f, 1.103040f) ADD_ATOM(52, 0.584490f, 0.811280f) ADD_ATOM(53, 7.615342f, 1.091525f) ADD_ATOM(54, 8.926021f, 2.039533f) ADD_ATOM(55, 8.831959f, 0.806553f) ADD_ATOM(56, 10.738885f, 7.580069f) ADD_ATOM(57, 10.242153f, 9.119476f) ADD_ATOM(58, 11.385680f, 8.649048f) ADD_ATOM(59, 5.532535f, 12.555992f) ADD_ATOM(60, 3.914702f, 12.559145f) ADD_ATOM(61, 4.715558f, 13.501455f) ADD_ATOM(62, -0.808250f, 9.141780f) ADD_ATOM(63, -1.311285f, 7.604311f) ADD_ATOM(64, -1.959897f, 8.657169f) ADD_ATOM(65, -2.912390f, 8.962263f) ADD_ATOM(66, 0.000000f, 0.000000f) ADD_ATOM(67, 9.423116f, 0.000000f) ADD_ATOM(68, 12.327870f, 8.951354f) ADD_ATOM(69, 4.719073f, 14.511889f) ADD_BOND(1, 0, 8) ADD_BOND(2, 0, 8) ADD_BOND(3, 1, 8) ADD_BOND(4, 2, 8) ADD_BOND(4, 3, 8) ADD_BOND(6, 5, 8) ADD_BOND(7, 5, 8) ADD_BOND(8, 6, 8) ADD_BOND(9, 7, 8) ADD_BOND(1, 5, 8) ADD_BOND(11, 10, 8) ADD_BOND(12, 10, 8) ADD_BOND(13, 11, 8) ADD_BOND(14, 12, 8) ADD_BOND(3, 10, 8) ADD_BOND(16, 15, 8) ADD_BOND(17, 15, 8) ADD_BOND(18, 16, 8) ADD_BOND(19, 17, 8) ADD_BOND(4, 15, 8) ADD_BOND(21, 20, 8) ADD_BOND(22, 20, 8) ADD_BOND(23, 21, 8) ADD_BOND(24, 22, 8) ADD_BOND(2, 20, 8) ADD_BOND(26, 25, 8) ADD_BOND(27, 25, 8) ADD_BOND(28, 26, 8) ADD_BOND(29, 27, 8) ADD_BOND(0, 25, 8) ADD_BOND(6, 27, 8) ADD_BOND(7, 11, 8) ADD_BOND(12, 16, 8) ADD_BOND(17, 21, 8) ADD_BOND(22, 26, 8) ADD_BOND(30, 13, 8) ADD_BOND(14, 13, 8) ADD_BOND(31, 30, 8) ADD_BOND(32, 14, 8) ADD_BOND(33, 32, 8) ADD_BOND(34, 18, 8) ADD_BOND(19, 18, 8) ADD_BOND(35, 34, 8) ADD_BOND(36, 19, 8) ADD_BOND(37, 36, 8) ADD_BOND(38, 23, 8) ADD_BOND(24, 23, 8) ADD_BOND(39, 38, 8) ADD_BOND(40, 24, 8) ADD_BOND(41, 40, 8) ADD_BOND(42, 28, 8) ADD_BOND(29, 28, 8) ADD_BOND(43, 42, 8) ADD_BOND(44, 29, 8) ADD_BOND(45, 44, 8) ADD_BOND(46, 8, 8) ADD_BOND(9, 8, 8) ADD_BOND(47, 46, 8) ADD_BOND(48, 9, 8) ADD_BOND(49, 48, 8) ADD_BOND(44, 46, 8) ADD_BOND(48, 30, 8) ADD_BOND(32, 34, 8) ADD_BOND(36, 38, 8) ADD_BOND(40, 42, 8) ADD_BOND(47, 50, 8) ADD_BOND(49, 47, 8) ADD_BOND(49, 51, 8) ADD_BOND(52, 51, 8) ADD_BOND(50, 52, 8) ADD_BOND(31, 53, 8) ADD_BOND(33, 31, 8) ADD_BOND(33, 54, 8) ADD_BOND(55, 54, 8) ADD_BOND(53, 55, 8) ADD_BOND(35, 56, 8) ADD_BOND(37, 35, 8) ADD_BOND(37, 57, 8) ADD_BOND(58, 57, 8) ADD_BOND(56, 58, 8) ADD_BOND(39, 59, 8) ADD_BOND(41, 39, 8) ADD_BOND(41, 60, 8) ADD_BOND(61, 60, 8) ADD_BOND(59, 61, 8) ADD_BOND(43, 62, 8) ADD_BOND(45, 43, 8) ADD_BOND(45, 63, 8) ADD_BOND(64, 63, 8) ADD_BOND(62, 64, 8) ADD_BOND(63, 50, 8) ADD_BOND(51, 53, 8) ADD_BOND(54, 56, 8) ADD_BOND(57, 59, 8) ADD_BOND(60, 62, 8) ADD_BOND(65, 64, 8) ADD_BOND(66, 52, 8) ADD_BOND(67, 55, 8) ADD_BOND(68, 58, 8) ADD_BOND(69, 61, 8) ADD_BOND(69, 65, 8) ADD_BOND(65, 66, 8) ADD_BOND(66, 67, 8) ADD_BOND(67, 68, 8) ADD_BOND(68, 69, 8) OUTLINE_POINT(0, 0.000000f, 0.000000f) OUTLINE_POINT(1, -2.912390f, 8.962263f) OUTLINE_POINT(2, 4.719073f, 14.511889f) OUTLINE_POINT(3, 12.327870f, 8.951354f) OUTLINE_POINT(4, 9.423116f, 0.000000f) END_PATTERN() ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/metalayout.cpp�������������������������������������������������������0000664�0000000�0000000�00000021174�12710376503�0021202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "layout/metalayout.h" #include "base_cpp/tlscont.h" using namespace indigo; Metalayout::LayoutLine::LayoutLine () { clear(); } Metalayout::LayoutLine::~LayoutLine () { } void Metalayout::LayoutLine::clear () { items.clear(); height = width = 0; } IMPL_ERROR(Metalayout, "metalayout"); Metalayout::Metalayout () : horizontalIntervalFactor(1.4f), verticalIntervalFactor(0.8f), bondLength(1.0f), _avel(1.0f), _scaleFactor(1.0f) { clear(); } void Metalayout::clear () { _layout.clear(); } bool Metalayout::isEmpty () const { return _layout.size() == 0; } void Metalayout::prepare () { _avel = _getAverageBondLength(); if (_avel < 1e-4) throw Error("average bond length is too small"); _scaleFactor = bondLength / _avel; } float Metalayout::getAverageBondLength () const { return _avel; } float Metalayout::getScaleFactor () const { return _scaleFactor; } const Vec2f& Metalayout::getContentSize () const { return _contentSize; } Metalayout::LayoutLine& Metalayout::newLine () { LayoutLine& line = _layout.push(); return line; } void Metalayout::process() { Vec2f pos; for (int i = 0; i < _layout.size(); ++i) { LayoutLine& line = _layout[i]; pos.y -= line.height / 2; pos.x = 0; for (int j = 0; j < line.items.size(); ++j) { LayoutItem& item = line.items[j]; cb_process(item, pos, context); pos.x += item.scaledSize.x + horizontalIntervalFactor * bondLength; } pos.y -= line.height / 2 + verticalIntervalFactor * bondLength; } } void Metalayout::calcContentSize() { _contentSize.set(0, 0); float regularWidth = 0.0f; for (int i = 0; i < _layout.size(); ++i) { LayoutLine& line = _layout[i]; for (int j = 0; j < line.items.size(); ++j) { line.width += line.items[j].scaledSize.x; Metalayout::LayoutItem& item = line.items[j]; if (item.explicitVerticalOffset) line.height = __max(line.height, 2 * __max(item.verticalOffset, item.scaledSize.y - item.verticalOffset)); else if (item.over) line.height = __max(line.height, item.scaledSize.y * 2 + 2); else line.height = __max(line.height, item.scaledSize.y); } line.width += horizontalIntervalFactor * bondLength * (line.items.size() - 1); _contentSize.x = __max(_contentSize.x, line.width); _contentSize.y += line.height; if (regularWidth < line.width) regularWidth = line.width; } _contentSize.y += verticalIntervalFactor * bondLength * (_layout.size() - 1); } void Metalayout::scaleSz() { for (int i = 0; i < _layout.size(); ++i) for (int j = 0; j < _layout[i].items.size(); ++j) { LayoutItem& item = _layout[i].items[j]; if (item.fragment) { item.scaledSize.diff(item.max, item.min); item.scaledSize.scale(_scaleFactor); item.scaledSize.max(Vec2f(bondLength, bondLength)); } } } float Metalayout::_getAverageBondLength() { // get total bond length and count float totalBondLength = 0; int totalBondCount = 0; for (int i = 0; i < _layout.size(); ++i) { LayoutLine& line = _layout[i]; for (int j = 0; j < line.items.size(); ++j) { LayoutItem& item = line.items[j]; if (item.fragment) { BaseMolecule& mol = cb_getMol(item.id, context); totalBondCount += mol.edgeCount(); totalBondLength += getTotalMoleculeBondLength(mol); } } } // if there are any bonds, calculate the average length if (totalBondCount > 0) return totalBondLength / totalBondCount; // get sum of distances from each vertex to the closest one float totalClosestDist = 0; int totalAtomCount = 0; for (int i = 0; i < _layout.size(); ++i) { LayoutLine& line = _layout[i]; for (int j = 0; j < line.items.size(); ++j) { LayoutItem& item = line.items[j]; if (item.fragment) { BaseMolecule& mol = cb_getMol(item.id, context); int atomCnt = mol.vertexCount(); if (atomCnt > 1) { totalClosestDist += getTotalMoleculeClosestDist(mol); totalAtomCount += atomCnt; } } } } // if there are molecules with more than one vertex, // take average distance to closest vertex instead of // average bond length if (totalAtomCount > 0) return totalClosestDist / totalAtomCount; // if each molecule contains at most one atom, // the average bond length doesn't matter return 1.0f; } void Metalayout::getBoundRect (Vec2f& min, Vec2f& max, BaseMolecule& mol) { if (mol.vertexCount() == 0) { min.zero(); max.zero(); return; } const Vec3f& v0 = mol.getAtomXyz(mol.vertexBegin()); Vec2f::projectZ(min, v0); Vec2f::projectZ(max, v0); Vec2f v2; for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { Vec2f::projectZ(v2, mol.getAtomXyz(i)); min.min(v2); max.max(v2); } } float Metalayout::getTotalMoleculeBondLength (BaseMolecule& mol) { Vec2f v1, v2; float sum = 0; for (int i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge& edge = mol.getEdge(i); Vec2f::projectZ(v1, mol.getAtomXyz(edge.beg)); Vec2f::projectZ(v2, mol.getAtomXyz(edge.end)); sum += Vec2f::dist(v1, v2); } return sum; } float Metalayout::getTotalMoleculeClosestDist (BaseMolecule& mol) { QS_DEF(Array<float>, dst); float sum = 0; dst.clear_resize(mol.vertexEnd()); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) dst[i] = -1; for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) for (int j = mol.vertexNext(i); j < mol.vertexEnd(); j = mol.vertexNext(j)) { Vec2f u, v; Vec2f::projectZ(u, mol.getAtomXyz(i)); Vec2f::projectZ(v, mol.getAtomXyz(j)); float d = Vec2f::dist(u, v); if (dst[i] < 0 || dst[i] > d) dst[i] = d; if (dst[j] < 0 || dst[j] > d) dst[j] = d; } for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) sum += dst[i]; return sum; } void Metalayout::adjustMol (BaseMolecule& mol, const Vec2f& min, const Vec2f& pos) { float scaleFactor = getScaleFactor(); // Compute center points for the data sgroups QS_DEF(Array<Vec2f>, data_centers); data_centers.resize(mol.sgroups.getSGroupCount()); for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &group = (DataSGroup &)sg; if (!group.relative) mol.getSGroupAtomsCenterPoint(group, data_centers[i]); } } for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { Vec2f v; Vec2f::projectZ(v, mol.getAtomXyz(i)); v.sub(min); v.scale(scaleFactor); v.add(pos); mol.setAtomXyz(i, v.x, v.y, 0); } // Adjust data-sgroup label positions with absolute coordinates for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &group = (DataSGroup &)sg; if (!group.relative) { Vec2f new_center; mol.getSGroupAtomsCenterPoint(group, new_center); group.display_pos.add(new_center); group.display_pos.sub(data_centers[i]); } } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout.cpp��������������������������������������������������0000664�0000000�0000000�00000036445�12710376503�0022227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "graph/filter.h" #include "layout/molecule_layout.h" using namespace indigo; IMPL_ERROR(MoleculeLayout, "molecule_layout"); MoleculeLayout::MoleculeLayout(BaseMolecule &molecule, bool smart_layout) : _molecule(molecule), _smart_layout(smart_layout) { _hasMulGroups = _molecule.sgroups.getSGroupCount(SGroup::SG_TYPE_MUL) > 0; _init(smart_layout); _query = _molecule.isQueryMolecule(); } void MoleculeLayout::_init(bool smart_layout) { bond_length = 1.f; respect_existing_layout = false; filter = 0; _smart_layout = smart_layout; if (_smart_layout) _layout_graph.reset(new MoleculeLayoutGraphSmart()); else _layout_graph.reset(new MoleculeLayoutGraphSimple()); max_iterations = LAYOUT_MAX_ITERATION; _query = false; _atomMapping.clear(); _bm = &_molecule; if (_hasMulGroups) { if (_molecule.isQueryMolecule()) _molCollapsed.reset(new QueryMolecule()); else _molCollapsed.reset(new Molecule()); _molCollapsed->clone(_molecule, &_atomMapping, NULL); QS_DEF(BaseMolecule::Mapping, atomMapCollapse); QS_DEF(BaseMolecule::Mapping, bondMapInv); for (int i = _molCollapsed->sgroups.begin(); i != _molCollapsed->sgroups.end(); i = _molCollapsed->sgroups.next(i)) { SGroup &sg = _molCollapsed->sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_MUL) { // collapse multiple group atomMapCollapse.clear(); bondMapInv.clear(); BaseMolecule::collapse(_molCollapsed.ref(), i, atomMapCollapse, bondMapInv); // modify the atom mapping for (int j = 0; j < _atomMapping.size(); ++j) if (atomMapCollapse.find(_atomMapping[j])) _atomMapping[j] = atomMapCollapse.at(_atomMapping[j]); } } _bm = _molCollapsed.get(); } _layout_graph->makeOnGraph(*_bm); for (int i = _layout_graph->vertexBegin(); i < _layout_graph->vertexEnd(); i = _layout_graph->vertexNext(i)) { const Vec3f &pos = _bm->getAtomXyz(_layout_graph->getVertexExtIdx(i)); _layout_graph->getPos(i).set(pos.x, pos.y); } } void _collectCrossBonds (Array<int>& crossBonds, Array<bool>& crossBondOut, BaseMolecule& mol, const Array<int>& atoms) { QS_DEF(Array<bool>, atomMask); atomMask.clear_resize(mol.vertexEnd()); atomMask.fill(false); for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; atomMask[aid] = true; } crossBonds.clear(); crossBondOut.clear(); for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; const Vertex& v = mol.getVertex(aid); for (int j = v.neiBegin(); j < v.neiEnd(); j = v.neiNext(j)) { int naid = v.neiVertex(j); if (!atomMask[naid]) { int bid = v.neiEdge(j); crossBonds.push(bid); crossBondOut.push(mol.getEdge(bid).beg == aid); } } } } void _placeSGroupBracketsCrossBonds (Array<Vec2f[2]>& brackets, BaseMolecule& mol, const Array<int>& atoms, const Array<int>& crossBonds, const Array<bool>& crossBondOut, float bondLength) { brackets.clear(); if (crossBonds.size() == 2) { int bid1 = crossBonds[0], bid2 = crossBonds[1]; const Edge& edge1 = mol.getEdge(bid1); const Edge& edge2 = mol.getEdge(bid2); Vec2f pb1, pe1, pb2, pe2; Vec2f::projectZ(pb1, mol.getAtomXyz(edge1.beg)); Vec2f::projectZ(pe1, mol.getAtomXyz(edge1.end)); Vec2f::projectZ(pb2, mol.getAtomXyz(edge2.beg)); Vec2f::projectZ(pe2, mol.getAtomXyz(edge2.end)); Vec2f d1, d2; d1.diff(pe1, pb1); if (!crossBondOut[0]) d1.scale(-1); d1.normalize(); d2.diff(pe2, pb2); if (!crossBondOut[1]) d2.scale(-1); d2.normalize(); if (Vec2f::dot(d1, d2) < -0.3) { Vec2f d, n; d.add(pb1); d.add(pe1); d.sub(pb2); d.sub(pe2); d.normalize(); n.copy(d); n.rotate(1, 0); Vec2f min, max, a, b, c; c.add(pb1); c.add(pe1); c.add(pb2); c.add(pe2); c.scale(0.25f); for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; const Vec3f& pos = mol.getAtomXyz(aid); Vec2f p2d; Vec2f::projectZ(p2d, pos); p2d.sub(c); p2d.set(Vec2f::dot(p2d, d), Vec2f::dot(p2d, n)); if (i == 0) { min.copy(p2d); max.copy(p2d); } else { min.min(p2d); max.max(p2d); } } Vec2f b1(c), b2; b1.addScaled(d, max.x + 0.3f * bondLength); b2.copy(b1); float factor = 0.5; b1.addScaled(n, factor * bondLength); b2.addScaled(n, -factor * bondLength); Vec2f* const & bracket1 = brackets.push(); bracket1[0].copy(b1); bracket1[1].copy(b2); b1.copy(c); b1.addScaled(d, min.x - 0.3f * bondLength); b2.copy(b1); b1.addScaled(n, -factor * bondLength); b2.addScaled(n, factor * bondLength); Vec2f* const & bracket2 = brackets.push(); bracket2[0].copy(b1); bracket2[1].copy(b2); return; } } for (int i = 0; i < crossBonds.size(); ++i) { int bid = crossBonds[i]; const Edge& edge = mol.getEdge(bid); int aidIn = edge.beg, aidOut = edge.end; if (!crossBondOut[i]) { int t; __swap(aidIn, aidOut, t); } Vec2f p2dIn, p2dOut, d, n, b1, b2; Vec2f::projectZ(p2dIn, mol.getAtomXyz(aidIn)); Vec2f::projectZ(p2dOut, mol.getAtomXyz(aidOut)); d.diff(p2dOut, p2dIn); d.normalize(); n.copy(d); n.rotate(1, 0); float offset = 1.0f / 3; b1.lineCombin2(p2dIn, 1 - offset, p2dOut, offset); b2.copy(b1); float factor = 0.5; b1.addScaled(n, factor * bondLength); b2.addScaled(n, -factor * bondLength); Vec2f* const & bracket = brackets.push(); bracket[0].copy(b1); bracket[1].copy(b2); } } void _placeSGroupBracketsCrossBondSingle (Array<Vec2f[2]>& brackets, BaseMolecule& mol, const Array<int>& atoms, int bid, bool out, float bondLength) { brackets.clear(); const Edge& edge = mol.getEdge(bid); int aidIn = edge.beg, aidOut = edge.end; if (!out) { int t; __swap(aidIn, aidOut, t); } Vec2f p2dIn, p2dOut, d, n, b1, b2; Vec2f::projectZ(p2dIn, mol.getAtomXyz(aidIn)); Vec2f::projectZ(p2dOut, mol.getAtomXyz(aidOut)); d.diff(p2dOut, p2dIn); d.normalize(); n.copy(d); n.rotate(1, 0); Vec2f min, max, a, b; for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; const Vec3f& pos = mol.getAtomXyz(aid); Vec2f p2d; Vec2f::projectZ(p2d, pos); p2d.sub(p2dIn); p2d.set(Vec2f::dot(p2d, d), Vec2f::dot(p2d, n)); if (i == 0) { min.copy(p2d); max.copy(p2d); } else { min.min(p2d); max.max(p2d); } } b1.lineCombin(p2dIn, d, max.x + 0.3f * bondLength); b2.copy(b1); float factor = 0.5; b1.addScaled(n, factor * bondLength); b2.addScaled(n, -factor * bondLength); Vec2f* const & bracket1 = brackets.push(); bracket1[0].copy(b1); bracket1[1].copy(b2); b1.lineCombin(p2dIn, d, min.x - 0.3f * bondLength); b2.copy(b1); b1.addScaled(n, -factor * bondLength); b2.addScaled(n, factor * bondLength); Vec2f* const & bracket2 = brackets.push(); bracket2[0].copy(b1); bracket2[1].copy(b2); } void _placeSGroupBracketsHorizontal (Array<Vec2f[2]>& brackets, BaseMolecule& mol, const Array<int>& atoms, float bondLength) { brackets.clear(); Vec2f min, max, a, b; for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; const Vec3f& pos = mol.getAtomXyz(aid); Vec2f p2d; Vec2f::projectZ(p2d, pos); if (i == 0) { min.copy(p2d); max.copy(p2d); } else { min.min(p2d); max.max(p2d); } } float extent = 0.5f * bondLength; min.sub(Vec2f(extent, extent)); max.add(Vec2f(extent, extent)); Vec2f* const & left = brackets.push(); left[0].set(min.x, min.y); left[1].set(min.x, max.y); Vec2f* const & right = brackets.push(); right[0].set(max.x, max.y); right[1].set(max.x, min.y); } void MoleculeLayout::_updateDataSGroups () { // Move Data-SGroups with absolute coordinates according to new position QS_DEF(Array<int>, layout_graph_mapping); layout_graph_mapping.resize(_molecule.vertexEnd()); layout_graph_mapping.fffill(); for (int i = _layout_graph->vertexBegin(); i < _layout_graph->vertexEnd(); i = _layout_graph->vertexNext(i)) { int vi = _layout_graph->getVertexExtIdx(i); layout_graph_mapping[vi] = i; } for (int i = _molecule.sgroups.begin(); i != _molecule.sgroups.end(); i = _molecule.sgroups.next(i)) { SGroup &sg = _molecule.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &group = (DataSGroup &)sg; if (!group.relative) { Vec2f before; _molecule.getSGroupAtomsCenterPoint(group, before); Vec2f after; for (int j = 0; j < group.atoms.size(); j++) { int ai = group.atoms[j]; const LayoutVertex &vert = _layout_graph->getLayoutVertex(layout_graph_mapping[ai]); after.x += vert.pos.x; after.y += vert.pos.y; } if (group.atoms.size() != 0) after.scale(1.0f / group.atoms.size()); Vec2f delta; delta.diff(after, before); group.display_pos.add(delta); } } } } void MoleculeLayout::_make () { _layout_graph->max_iterations = max_iterations; // 0. Find 2D coordinates via proxy _layout_graph object _layout_graph->max_iterations = max_iterations; _makeLayout(); // 1. Update data-sgroup label position before changing molecule atoms positions _updateDataSGroups(); // 2. Update atoms for (int i = _layout_graph->vertexBegin(); i < _layout_graph->vertexEnd(); i = _layout_graph->vertexNext(i)) { const LayoutVertex &vert = _layout_graph->getLayoutVertex(i); _bm->setAtomXyz(vert.ext_idx, vert.pos.x, vert.pos.y, 0.f); } if (_hasMulGroups) { for (int j = 0; j < _atomMapping.size(); ++j) { int i = _atomMapping[j]; _molecule.setAtomXyz(j, _molCollapsed.ref().getAtomXyz(i)); } _molCollapsed.reset(NULL); } _updateMultipleGroups(); _updateRepeatingUnits(); _molecule.have_xyz = true; } Metalayout::LayoutItem& MoleculeLayout::_pushMol (Metalayout::LayoutLine& line, BaseMolecule& mol) { Metalayout::LayoutItem& item = line.items.push(); item.type = 0; item.fragment = true; item.id = _map.size(); _map.push(&mol); Metalayout::getBoundRect(item.min, item.max, mol); item.scaledSize.diff(item.max, item.min); return item; } BaseMolecule& MoleculeLayout::_getMol (int id) { return *_map[id]; } void MoleculeLayout::make () { _make(); if (_molecule.rgroups.getRGroupCount() > 0) { MoleculeRGroups &rgs = ((QueryMolecule &)_molecule).rgroups; _ml.clear(); _map.clear(); _pushMol(_ml.newLine(), _molecule); for (int i = 1; i <= rgs.getRGroupCount(); ++i) { RGroup& rg = rgs.getRGroup(i); Metalayout::LayoutLine& line = _ml.newLine(); PtrPool<BaseMolecule> &frags = rg.fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { BaseMolecule& mol = *frags[j]; if (filter == NULL) { MoleculeLayout layout(mol, _smart_layout); layout.max_iterations = max_iterations; layout.bond_length = bond_length; layout.make(); } _pushMol(line, mol); // add molecule to metalayout AFTER its own layout is determined } } _ml.bondLength = bond_length; _ml.context = this; _ml.cb_getMol = cb_getMol; _ml.cb_process = cb_process; _ml.prepare(); _ml.scaleSz(); _ml.calcContentSize(); _ml.process(); } } void MoleculeLayout::setCancellationHandler (CancellationHandler* cancellation) { _layout_graph->cancellation = cancellation; } BaseMolecule& MoleculeLayout::cb_getMol (int id, void* context) { return ((MoleculeLayout*)context)->_getMol(id); } void MoleculeLayout::cb_process (Metalayout::LayoutItem& item, const Vec2f& pos, void* context) { MoleculeLayout* layout = (MoleculeLayout*)context; layout->_ml.adjustMol(layout->_getMol(item.id), item.min, pos); } void MoleculeLayout::_makeLayout () { if (filter != 0) { QS_DEF(Array<int>, fixed_vertices); fixed_vertices.clear_resize(_layout_graph->vertexEnd()); fixed_vertices.zerofill(); for (int i = _layout_graph->vertexBegin(); i < _layout_graph->vertexEnd(); i = _layout_graph->vertexNext(i)) if (!filter->valid(_layout_graph->getVertexExtIdx(i))) fixed_vertices[i] = 1; Filter new_filter(fixed_vertices.ptr(), Filter::NEQ, 1); _layout_graph->layout(*_bm, bond_length, &new_filter, respect_existing_layout); } else _layout_graph->layout(*_bm, bond_length, 0, respect_existing_layout); } void MoleculeLayout::_updateRepeatingUnits () { QS_DEF(Array<int>, crossBonds); QS_DEF(Array<bool>, crossBondOut); for (int i = _molecule.sgroups.begin(); i != _molecule.sgroups.end(); i = _molecule.sgroups.next(i)) { SGroup &sg = _molecule.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit& ru = (RepeatingUnit &)sg; crossBonds.clear(); crossBondOut.clear(); _collectCrossBonds(crossBonds, crossBondOut, _molecule, ru.atoms); if (crossBonds.size() > 1) _placeSGroupBracketsCrossBonds (ru.brackets, _molecule, ru.atoms, crossBonds, crossBondOut, bond_length); else if (crossBonds.size() == 1) _placeSGroupBracketsCrossBondSingle (ru.brackets, _molecule, ru.atoms, crossBonds[0], crossBondOut[0], bond_length); else _placeSGroupBracketsHorizontal (ru.brackets, _molecule, ru.atoms, bond_length); } } } void MoleculeLayout::_updateMultipleGroups () { for (int i = _molecule.sgroups.begin(); i != _molecule.sgroups.end(); i = _molecule.sgroups.next(i)) { SGroup &sg = _molecule.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_MUL) { MultipleGroup& mg = (MultipleGroup &)sg; _placeSGroupBracketsHorizontal (mg.brackets, _molecule, mg.atoms, bond_length); } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph.cpp��������������������������������������������0000664�0000000�0000000�00000043453�12710376503�0023405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/biconnected_decomposer.h" #include "graph/morgan_code.h" #include "layout/molecule_layout_graph.h" #include <memory> using namespace indigo; IMPL_ERROR(MoleculeLayoutGraph, "layout_graph"); MoleculeLayoutGraph::MoleculeLayoutGraph():Graph() { _total_morgan_code = 0; _first_vertex_idx = -1; _n_fixed = 0; _molecule = 0; _molecule_edge_mapping = 0; cancellation = 0; _flipped = false; } MoleculeLayoutGraph::~MoleculeLayoutGraph() { } void MoleculeLayoutGraph::clear() { Graph::clear(); _total_morgan_code = 0; _first_vertex_idx = -1; _n_fixed = 0; _layout_vertices.clear(); _layout_edges.clear(); _fixed_vertices.clear(); } const LayoutVertex& MoleculeLayoutGraph::getLayoutVertex(int idx) const { return _layout_vertices[idx]; } const LayoutEdge & MoleculeLayoutGraph::getLayoutEdge(int idx) const { return _layout_edges[idx]; } bool MoleculeLayoutGraph::isSingleEdge() const { return edgeCount() == 1 && vertexCount() == 2; } void MoleculeLayoutGraph::registerLayoutVertex(int idx, const LayoutVertex &vertex) { _layout_vertices.expand(idx + 1); _layout_vertices[idx] = vertex; } void MoleculeLayoutGraph::registerLayoutEdge(int idx, const LayoutEdge &edge) { _layout_edges.expand(idx + 1); _layout_edges[idx] = edge; } int MoleculeLayoutGraph::addLayoutVertex(int ext_idx, int type) { int new_idx = Graph::addVertex(); LayoutVertex new_vertex; new_vertex.ext_idx = ext_idx; new_vertex.type = type; registerLayoutVertex(new_idx, new_vertex); return new_idx; } int MoleculeLayoutGraph::addLayoutEdge(int beg, int end, int ext_idx, int type) { int new_idx = Graph::addEdge(beg, end); LayoutEdge new_edge; new_edge.ext_idx = ext_idx; new_edge.type = type; registerLayoutEdge(new_idx, new_edge); return new_idx; } int MoleculeLayoutGraph::findVertexByExtIdx(int ext_idx) const { for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (getLayoutVertex(i).ext_idx == ext_idx) return i; return -1; } void MoleculeLayoutGraph::cloneLayoutGraph(MoleculeLayoutGraph &other, Array<int> *mapping) { QS_DEF(Array<int>, mapping_tmp); clear(); if (mapping == 0) mapping = &mapping_tmp; cloneGraph(other, mapping); LayoutVertex new_vertex; LayoutEdge new_edge; for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) { new_vertex = other.getLayoutVertex(i); new_vertex.ext_idx = i; registerLayoutVertex(mapping->at(i), new_vertex); } for (int i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i)) { const Edge &edge = other.getEdge(i); new_edge = other.getLayoutEdge(i); new_edge.ext_idx = i; registerLayoutEdge(findEdgeIndex(mapping->at(edge.beg), mapping->at(edge.end)), new_edge); } } void MoleculeLayoutGraph::copyLayoutTo(MoleculeLayoutGraph &other, const Array<int> &mapping) const { for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) { other._layout_vertices[i].type = _layout_vertices[mapping[i]].type; other._layout_vertices[i].pos = _layout_vertices[mapping[i]].pos; } for (int i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i)) { const Edge &edge = other.getEdge(i); const Vertex &vert = other.getVertex(mapping[edge.beg]); int edge_idx = vert.neiEdge(vert.findNeiVertex(mapping[edge.end])); other._layout_edges[i].type = _layout_edges[edge_idx].type; } } void MoleculeLayoutGraph::makeOnGraph(Graph &graph) { QS_DEF(Array<int>, mapping); clear(); // vertices and edges cloneGraph(graph, &mapping); LayoutVertex new_vertex; LayoutEdge new_edge; new_vertex.type = ELEMENT_NOT_DRAWN; new_vertex.is_cyclic = false; for (int i = graph.vertexBegin(); i < graph.vertexEnd(); i = graph.vertexNext(i)) { new_vertex.ext_idx = i; new_vertex.orig_idx = i; registerLayoutVertex(mapping[i], new_vertex); } new_edge.type = ELEMENT_NOT_DRAWN; for (int i = graph.edgeBegin(); i < graph.edgeEnd(); i = graph.edgeNext(i)) { const Edge &edge = graph.getEdge(i); int idx = findEdgeIndex(mapping[edge.beg], mapping[edge.end]); new_edge.ext_idx = i; new_edge.orig_idx = i; registerLayoutEdge(idx, new_edge); } } TL_DEF(MoleculeLayoutGraphSimple, ObjArray<PatternLayout>, _patterns); IMPL_ERROR(MoleculeLayoutGraphSimple, "layout_graph"); MoleculeLayoutGraphSimple::MoleculeLayoutGraphSimple() :MoleculeLayoutGraph() { } MoleculeLayoutGraphSimple::~MoleculeLayoutGraphSimple () { } MoleculeLayoutGraph* MoleculeLayoutGraphSimple::getInstance() { return new MoleculeLayoutGraphSimple(); } void MoleculeLayoutGraphSimple::clear() { MoleculeLayoutGraph::clear(); } void MoleculeLayoutGraphSimple::makeLayoutSubgraph (MoleculeLayoutGraph &graph, Filter &filter) { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, mapping); clear(); filter.collectGraphVertices(graph, vertices); makeSubgraph(graph, vertices, &mapping); LayoutVertex new_vertex; LayoutEdge new_edge; new_vertex.is_cyclic = false; for (int i = 0; i < vertices.size(); i++) { new_vertex.ext_idx = vertices[i]; new_vertex.type = graph._layout_vertices[vertices[i]].type; new_vertex.morgan_code = graph._layout_vertices[vertices[i]].morgan_code; registerLayoutVertex(mapping[vertices[i]], new_vertex); } for (int i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { const Edge &edge = getEdge(i); int ext_idx = graph.findEdgeIndex(vertices[edge.beg], vertices[edge.end]); new_edge.ext_idx = ext_idx; new_edge.type = graph._layout_edges[ext_idx].type; registerLayoutEdge(i, new_edge); } } void MoleculeLayoutGraphSimple::layout (BaseMolecule &molecule, float bond_length, const Filter *filter, bool respect_existing) { TL_GET(ObjArray<PatternLayout>, _patterns); if (molecule.vertexCount() == 0) return; if (_patterns.size() == 0) _initPatterns(); int n_components = countComponents(); if (fabs(bond_length) < EPSILON) throw Error("zero bond length"); if (n_components > 1) _layoutMultipleComponents(molecule, respect_existing, filter, bond_length); else _layoutSingleComponent(molecule, respect_existing, filter, bond_length); } void MoleculeLayoutGraphSimple::_calcMorganCodes () { MorganCode morgan(*this); QS_DEF(Array<long>, morgan_codes); morgan.calculate(morgan_codes, 3, 7); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].morgan_code = morgan_codes[i]; } void MoleculeLayoutGraphSimple::_makeComponentsTree (BiconnectedDecomposer &decon, PtrArray<MoleculeLayoutGraph> &components, Array<int> &tree) { int i, j, v, k; bool from; for (i = 0; i < tree.size(); i++) tree[i] = -1; for (i = 0; i < components.size(); i++) { for (k = components[i]->vertexBegin(); k < components[i]->vertexEnd(); k = components[i]->vertexNext(k)) { v = components[i]->getLayoutVertex(k).ext_idx; if (decon.isArticulationPoint(v)) { // if connection vertex belongs to i-th component from = false; for (j = 0; j < decon.getIncomingComponents(v).size(); j++) { // and component doesn't come from this vertex if (decon.getIncomingComponents(v)[j] == i) from = true; } // TODO: try to remove tree[]; if (!from) tree[v] = i; } } } } int MoleculeLayoutGraphSimple::_pattern_cmp (PatternLayout &p1, PatternLayout &p2, void *context) { long diff = p2.morganCode() - p1.morganCode(); if (diff != 0) return diff; diff = p2.vertexCount() + p2.edgeCount() - p1.vertexCount() - p1.edgeCount(); if (diff != 0) return diff; diff = p2.vertexCount() - p1.vertexCount(); if (diff != 0) return diff; return p2.edgeCount() - p1.edgeCount(); } int MoleculeLayoutGraphSimple::_pattern_cmp2 (PatternLayout &p1, int n_v, int n_e, long code) { long diff = code - p1.morganCode(); if (diff != 0) return diff; diff = n_v + n_e - p1.vertexCount() - p1.edgeCount(); if (diff != 0) return diff; diff = n_v - p1.vertexCount(); if (diff != 0) return diff; return n_e - p1.edgeCount(); } void MoleculeLayoutGraphSimple::_initPatterns () { TL_GET(ObjArray<PatternLayout>, _patterns); struct LayoutPattenItem { enum { _ADD_ATOM, _ADD_BOND, _OUTLINE_POINT }; int type; int idx_or_type; int v1, v2; float x, y; }; #define BEGIN_PATTERN(name) \ { \ PatternLayout &p = _patterns.push(); p.setName(name); \ static LayoutPattenItem _items[] = { #define ADD_ATOM(idx, x, y) { LayoutPattenItem::_ADD_ATOM, idx, -1, -1, x, y}, #define ADD_BOND(idx1, idx2, type) { LayoutPattenItem::_ADD_BOND, type, idx1, idx2, -1.f, -1.f}, #define OUTLINE_POINT(idx, x, y) { LayoutPattenItem::_OUTLINE_POINT, idx, -1, -1, x, y}, //#define FIX_PATTERN #define END_PATTERN() \ }; \ for (int i = 0; i < NELEM(_items); i++) \ { \ LayoutPattenItem &item = _items[i]; \ if (item.type == LayoutPattenItem::_ADD_ATOM) \ if (p.addAtom(item.x, item.y) != item.idx_or_type) \ throw Error("incorrect atom order in the pattern '%s'", p.getName()); \ if (item.type == LayoutPattenItem::_ADD_BOND) \ p.addBond(item.v1, item.v2, item.idx_or_type); \ if (item.type == LayoutPattenItem::_OUTLINE_POINT) \ if (p.addOutlinePoint(item.x, item.y) != item.idx_or_type) \ throw Error("incorrect outline order in the pattern '%s'", p.getName()); \ } \ } #include "layout_patterns.inc" #undef BEGIN_PATTERN //#undef FIX_PATTERN #undef ADD_ATOM #undef ADD_BOND #undef OUTLINE_POINT #undef END_PATTERN for (int i = 0; i < _patterns.size(); i++) _patterns[i].calcMorganCode(); _patterns.qsort(_pattern_cmp, 0); } void MoleculeLayoutGraphSimple::_layoutMultipleComponents (BaseMolecule & molecule, bool respect_existing, const Filter * filter, float bond_length) { QS_DEF(Array<Vec2f>, src_layout); QS_DEF(Array<int>, molecule_edge_mapping); int n_components = countComponents(); const Array<int> &decomposition = getDecomposition(); int i, j, k; molecule_edge_mapping.clear_resize(edgeEnd()); for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) molecule_edge_mapping[i] = getEdgeExtIdx(i); PtrArray<MoleculeLayoutGraph> components; components.clear(); for (i = 0; i < n_components; i++) { Filter comp_filter(decomposition.ptr(), Filter::EQ, i); std::unique_ptr<MoleculeLayoutGraph> current_component(getInstance()); components.add(current_component.release()); MoleculeLayoutGraph& component = *components.top(); component.cancellation = cancellation; component.makeLayoutSubgraph(*this, comp_filter); component.max_iterations = max_iterations; component._molecule = &molecule; component._molecule_edge_mapping = molecule_edge_mapping.ptr(); src_layout.clear_resize(component.vertexEnd()); if (respect_existing) for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) src_layout[j] = getPos(component.getVertexExtIdx(j)); else src_layout.zerofill(); if (filter != 0) { component._fixed_vertices.resize(component.vertexEnd()); component._fixed_vertices.zerofill(); for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) if (!filter->valid(component.getVertexExtIdx(j))) { component._fixed_vertices[j] = 1; component._n_fixed++; component._layout_vertices[j].pos = getPos(component.getVertexExtIdx(j)); } } if (component.vertexCount() > 1) { component._calcMorganCodes(); component._assignAbsoluteCoordinates(bond_length); } component._assignFinalCoordinates(bond_length, src_layout); } // position components float x_min, x_max, x_start = 0.f, dx; float y_min, y_max, y_start = 0.f, max_height = 0.f, dy; int col_count; int row, col; int n_fixed = 0; // fixed first if (filter != 0) { x_min = 1.0E+20f; y_min = 1.0E+20f; // find fixed components for (i = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) { n_fixed++; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) { const Vec2f &pos = component.getPos(j); if (pos.x < x_min) x_min = pos.x; if (pos.y < y_min) y_min = pos.y; if (pos.y > y_start) y_start = pos.y; } } } // position fixed if (n_fixed > 0) { dy = -y_min; dx = -x_min; for (i = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); } y_start += dy + 2 * bond_length; } } col_count = (int)ceil(sqrt((float)n_components - n_fixed)); for (i = 0, k = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) continue; // Component shifting row = k / col_count; col = k % col_count; x_min = 1.0E+20f; x_max = -1.0E+20f; y_min = 1.0E+20f; y_max = -1.0E+20f; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) { const Vec2f &pos = component.getPos(j); if (pos.x < x_min) x_min = pos.x; if (pos.x > x_max) x_max = pos.x; if (pos.y < y_min) y_min = pos.y; if (pos.y > y_max) y_max = pos.y; } if (col == 0 && row > 0) { y_start += max_height + 2 * bond_length; max_height = 0.f; } if (col > 0) dx = x_start - x_min + 2 * bond_length; else dx = -x_min; dy = y_start - y_min; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); x_start = x_max + dx; if (y_max - y_min > max_height) max_height = y_max - y_min; k++; } } void MoleculeLayoutGraphSimple::_layoutSingleComponent (BaseMolecule &molecule, bool respect_existing, const Filter * filter, float bond_length) { QS_DEF(Array<Vec2f>, src_layout); QS_DEF(Array<int>, molecule_edge_mapping); int i; molecule_edge_mapping.clear_resize(molecule.edgeEnd()); for (i = 0; i < molecule_edge_mapping.size(); i++) molecule_edge_mapping[i] = i; _molecule = &molecule; _molecule_edge_mapping = molecule_edge_mapping.ptr(); src_layout.clear_resize(vertexEnd()); if (respect_existing) for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) src_layout[i] = getPos(i); else src_layout.zerofill(); if (filter != 0) { _fixed_vertices.resize(vertexEnd()); _fixed_vertices.zerofill(); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (!filter->valid(i)) { _fixed_vertices[i] = 1; _n_fixed++; } } if (vertexCount() > 1) { _calcMorganCodes(); _assignAbsoluteCoordinates(bond_length); } _assignFinalCoordinates(bond_length, src_layout); } #ifdef M_LAYOUT_DEBUG #include "molecule/molecule.h" #include "molecule/molfile_saver.h" #include "base_cpp/output.h" #include "molecule/elements.h" void MoleculeLayoutGraphSimple::saveDebug () { int i; Molecule mol; QS_DEF(Array<int>, mapping); mapping.clear_resize(vertexEnd()); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { if (getVertexType(i) == ELEMENT_NOT_DRAWN) continue; mapping[i] = mol.addAtom(ELEM_C); mol.setAtomXyz(mapping[i], getPos(i).x, getPos(i).y, 0); } for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { if (getEdgeType(i) == ELEMENT_NOT_DRAWN) continue; const Edge &edge = getEdge(i); mol.addBond(mapping[edge.beg], mapping[edge.end], BOND_SINGLE); } static int id = 0; char out_name[100]; sprintf_s(out_name, "D:\\mf\\draw\\trace_my\\%03d.mol", id); FileOutput fo(out_name); MolfileSaver ms(fo); ms.saveMolecule(mol); if (id == 57) id = id; id++; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_assign.cpp�������������������������������������0000664�0000000�0000000�00000113144�12710376503�0024744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_graph.h" #include "layout/attachment_layout.h" #include "graph/biconnected_decomposer.h" #include "graph/cycle_enumerator.h" #include "graph/embedding_enumerator.h" #include "graph/morgan_code.h" #include <memory> using namespace indigo; enum { QUERY_BOND_SINGLE_OR_DOUBLE = 5, QUERY_BOND_SINGLE_OR_AROMATIC = 6, QUERY_BOND_DOUBLE_OR_AROMATIC = 7, QUERY_BOND_ANY = 8 }; // Make relative coordinates of a component absolute void MoleculeLayoutGraph::_copyLayout (MoleculeLayoutGraph &component) { int i; for (i = component.vertexBegin(); i < component.vertexEnd(); i = component.vertexNext(i)) { LayoutVertex &vert = component._layout_vertices[i]; _layout_vertices[vert.ext_idx].pos.copy(vert.pos); _layout_vertices[vert.ext_idx].type = vert.type; } for (i = component.edgeBegin(); i < component.edgeEnd(); i = component.edgeNext(i)) { LayoutEdge &edge = component._layout_edges[i]; _layout_edges[edge.ext_idx].type = edge.type; } } static int _vertex_cmp (int &n1, int &n2, void *context) { const MoleculeLayoutGraph &graph = *(MoleculeLayoutGraph *)context; const LayoutVertex &v1 = graph.getLayoutVertex(n1); const LayoutVertex &v2 = graph.getLayoutVertex(n2); if (v1.is_cyclic != v2.is_cyclic) { if (v1.is_cyclic == true) return 1; return -1; } return v1.morgan_code - v2.morgan_code; } void MoleculeLayoutGraphSimple::_assignAbsoluteCoordinates (float bond_length) { BiconnectedDecomposer bc_decom(*this); QS_DEF(Array<int>, bc_tree); //QS_DEF(ObjArray<MoleculeLayoutGraphSimple>, bc_components); PtrArray<MoleculeLayoutGraph> bc_components; QS_DEF(Array<int>, fixed_components); bool all_trivial = true; int n_comp = bc_decom.decompose(); fixed_components.clear_resize(n_comp); fixed_components.zerofill(); bc_components.clear(); for (int i = 0; i < n_comp; i++) { Filter comp; bc_decom.getComponent(i, comp); std::unique_ptr<MoleculeLayoutGraph> tmp((MoleculeLayoutGraph *)getInstance()); tmp->makeLayoutSubgraph(*this, comp); bc_components.add(tmp.release()) ; } bc_tree.clear_resize(vertexEnd()); _makeComponentsTree(bc_decom, bc_components, bc_tree); // 1. Find biconnected components forming connected subgraph from fixed vertices _findFixedComponents(bc_decom, fixed_components, bc_components); all_trivial = _assignComponentsRelativeCoordinates(bc_components, fixed_components, bc_decom); _findFirstVertexIdx(n_comp, fixed_components, bc_components, all_trivial); int i, j = -1; // ( 1] atoms assigned absolute coordinates and adjacent to atoms not; // assigned coordinates are put on a list; QS_DEF(Array<int>, assigned_list); QS_DEF(Array<int>, adjacent_list); while (true) { if(cancellation && cancellation->isCancelled()) throw Error("Molecule layout has been cancelled: %s", cancellation->cancelledRequestMessage()); if (!_prepareAssignedList(assigned_list, bc_decom, bc_components, bc_tree)) return; // ( 3.i] let k = 0 ( top of the list];; while (assigned_list.size() != 0) { int k = assigned_list.pop(); const Vertex &vert_k = getVertex(k); // ( 3.ii] a list of atoms adjacent to atom Uzel and not previously; // assigned coordinates is created and ordered with cyclic atoms; // at the top of the list with descending ATCD numbers and acyclic atoms; // at the bottom of the list with descending ATCD numbers;; adjacent_list.clear(); for (i = vert_k.neiBegin(); i < vert_k.neiEnd(); i = vert_k.neiNext(i)) if (_layout_vertices[vert_k.neiVertex(i)].type == ELEMENT_NOT_DRAWN) adjacent_list.push(vert_k.neiVertex(i)); if (adjacent_list.size() == 0) break; // When all components outgoing from vertex are trivial (edges) then use tree algorithm all_trivial = true; for (i = 0; i < bc_decom.getIncomingCount(k); i++) if (!bc_components[bc_decom.getIncomingComponents(k)[i]]->isSingleEdge()) { all_trivial = false; break; } if (all_trivial && bc_tree[k] != -1 && !bc_components[bc_tree[k]]->isSingleEdge()) all_trivial = false; if (all_trivial) { adjacent_list.qsort(_vertex_cmp, this); _attachDandlingVertices(k, adjacent_list); } else { // Component layout in current vertex should have the same angles between components. // So it depends on component order and their flipping (for nontrivial components) AttachmentLayoutSimple att_layout(bc_decom, bc_components, bc_tree, *this, k); // ( 3.iii] Look over all possible orders of component layouts // (vertex itself is already drawn means one component is already drawn) // ( 3.iv] Choose layout with minimal energy LayoutChooser layout_chooser(att_layout); layout_chooser.perform(); att_layout.markDrawnVertices(); } // ( 3.v] let k = k + 1;; // ( 3.vi] repeat steps 3.ii-3.v until all atoms in the list have been processed;; } // ( 4] repeat steps 1-3 until all atoms have been assigned absolute coordinates.; } } bool MoleculeLayoutGraphSimple::_match_pattern_bond (Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata) { if (userdata == 0 || ((MoleculeLayoutGraphSimple *)userdata)->_molecule == 0) return true; BaseMolecule &mol = *((MoleculeLayoutGraphSimple *)userdata)->_molecule; const int *mapping = ((MoleculeLayoutGraphSimple *)userdata)->_molecule_edge_mapping; int layout_idx = ((const MoleculeLayoutGraphSimple &)supergraph).getLayoutEdge(other_idx).ext_idx; const PatternBond &pattern_bond = ((const PatternLayout &)subgraph).getBond(self_idx); switch (pattern_bond.type) { case BOND_SINGLE: case BOND_DOUBLE: case BOND_TRIPLE: case BOND_AROMATIC: if (!mol.possibleBondOrder(mapping[layout_idx], pattern_bond.type)) return false; break; case QUERY_BOND_SINGLE_OR_DOUBLE: if (!mol.possibleBondOrder(mapping[layout_idx], BOND_SINGLE) && !mol.possibleBondOrder(mapping[layout_idx], BOND_DOUBLE)) return false; break; case QUERY_BOND_SINGLE_OR_AROMATIC: if (!mol.possibleBondOrder(mapping[layout_idx], BOND_SINGLE) && !mol.possibleBondOrder(mapping[layout_idx], BOND_AROMATIC)) return false; break; case QUERY_BOND_DOUBLE_OR_AROMATIC: if (!mol.possibleBondOrder(mapping[layout_idx], BOND_DOUBLE) && !mol.possibleBondOrder(mapping[layout_idx], BOND_AROMATIC)) return false; break; } int parity = mol.cis_trans.getParity(mapping[layout_idx]); if (parity != 0 && parity != pattern_bond.parity) return false; return true; } int MoleculeLayoutGraphSimple::_pattern_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { if (userdata == 0) return 1; MoleculeLayoutGraphSimple &layout_graph = *(MoleculeLayoutGraphSimple *)userdata; const PatternLayout &pattern_graph = (const PatternLayout &)subgraph; int i; // TODO: correct element marking (internal and non-planar)? for (i = layout_graph.vertexBegin(); i < layout_graph.vertexEnd(); i = layout_graph.vertexNext(i)) { layout_graph._layout_vertices[i].pos = pattern_graph.getAtom(core_super[i]).pos; layout_graph._layout_vertices[i].type = ELEMENT_BOUNDARY; } for (i = layout_graph.edgeBegin(); i < layout_graph.edgeEnd(); i = layout_graph.edgeNext(i)) layout_graph._layout_edges[i].type = ELEMENT_BOUNDARY; layout_graph._first_vertex_idx = layout_graph.vertexBegin(); if (layout_graph._outline.get() == 0) layout_graph._outline.create(); layout_graph._outline->copy(pattern_graph.getOutline()); return 0; } void MoleculeLayoutGraphSimple::_assignRelativeCoordinates (int &fixed_component, const MoleculeLayoutGraph &supergraph) { int i; if (isSingleEdge()) { _assignRelativeSingleEdge(fixed_component, supergraph); return; } // 2.1. Use layout of fixed components and find border edges and vertices if (fixed_component) { for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos = supergraph.getPos(getVertexExtIdx(i)); CycleEnumerator ce(*this); ce.context = this; ce.cb_handle_cycle = _border_cb; if (ce.process()) return; fixed_component = 0; } else { if (_tryToFindPattern(fixed_component)) return; } //TODO: repair exception with vec2f QS_DEF(ObjPool<Cycle>, cycles); QS_DEF(Array<int>, sorted_cycles); cycles.clear(); int n_cycles = sssrCount(); for (i = 0; i < n_cycles; i++) { int cycle_idx = cycles.add(sssrEdges(i), *this); cycles[cycle_idx].canonize(); } sorted_cycles.clear(); for (i = cycles.begin(); i < cycles.end(); i = cycles.next(i)) { cycles[i].calcMorganCode(*this); sorted_cycles.push(i); } sorted_cycles.qsort(Cycle::compare_cb, &cycles); _assignFirstCycle(cycles[sorted_cycles[0]]); cycles.remove(sorted_cycles[0]); sorted_cycles.remove(0); bool chain_attached; // Try to attach chains with one, two or more common edges outside drawn part do { chain_attached = false; for (i = 0; !chain_attached && i < sorted_cycles.size(); ) { if (_attachCycleOutside(cycles[sorted_cycles[i]], 1.f, 1)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); chain_attached = true; } else i++; } for (i = 0; !chain_attached && i < sorted_cycles.size(); ) { if (_attachCycleOutside(cycles[sorted_cycles[i]], 1.f, 2)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); chain_attached = true; } else i++; } for (i = 0; !chain_attached && i < sorted_cycles.size(); ) { if (_attachCycleOutside(cycles[sorted_cycles[i]], 1.f, 0)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); chain_attached = true; } else i++; } } while (chain_attached); // Try to attach chains inside for (i = 0; i < sorted_cycles.size(); ) { if (_attachCycleInside(cycles[sorted_cycles[i]], 1.f)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); } else i++; } // Try to attach chains inside with lower edge length for (i = 0; i < sorted_cycles.size(); ) { if (_attachCycleInside(cycles[sorted_cycles[i]], 0.75f)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); } else i++; } do { chain_attached = false; for (i = 0; !chain_attached && i < sorted_cycles.size(); ) { // 1.5f (> 1) means to calculate new length; if (_attachCycleOutside(cycles[sorted_cycles[i]], 1.5f, 0)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); chain_attached = true; } else i++; } } while (chain_attached); do { chain_attached = false; for (i = 0; !chain_attached && i < sorted_cycles.size(); ) { if (_attachCycleWithIntersections(cycles[sorted_cycles[i]], 1.f)) { cycles.remove(sorted_cycles[i]); sorted_cycles.remove(i); chain_attached = true; } else i++; } } while (chain_attached); _attachCrossingEdges(); for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { if (_layout_edges[i].type == ELEMENT_NOT_PLANAR) { _buildOutline(); break; } } } void MoleculeLayoutGraphSimple::_assignFirstCycle (const Cycle &cycle) { // TODO: Start drawing from vertex with maximum code and continue to the right with one of two which has maximum code int i, n; float phi; n = cycle.vertexCount(); for (i = 0; i < n; i++) { _layout_vertices[cycle.getVertex(i)].type = ELEMENT_BOUNDARY; _layout_edges[cycle.getEdge(i)].type = ELEMENT_BOUNDARY; } _first_vertex_idx = cycle.getVertex(0); _layout_vertices[cycle.getVertex(0)].pos.set(0.f, 0.f); _layout_vertices[cycle.getVertex(1)].pos.set(1.f, 0.f); phi = (float)M_PI * (n - 2) / n; for (i = 1; i < n - 1; i++) { const Vec2f &v1 = _layout_vertices[cycle.getVertex(i - 1)].pos; const Vec2f &v2 = _layout_vertices[cycle.getVertex(i)].pos; _layout_vertices[cycle.getVertex(i + 1)].pos.rotateAroundSegmentEnd(v1, v2, phi); } } // If vertices are already drawn // draw edges with intersections void MoleculeLayoutGraph::_attachCrossingEdges () { int i, j, pr; bool intersection; for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { const Edge &edge_i = getEdge(i); if (_layout_vertices[edge_i.beg].type != ELEMENT_NOT_DRAWN && _layout_vertices[edge_i.end].type != ELEMENT_NOT_DRAWN && _layout_edges[i].type == ELEMENT_NOT_DRAWN) { intersection = true; while (intersection) { intersection = false; for (j = edgeBegin(); j < edgeEnd(); j = edgeNext(j)) { if (_layout_edges[j].type != ELEMENT_NOT_DRAWN) { pr = _calcIntersection(i, j); // 1. If the edge 1 ends on the edge 2 // then shift edge 2 from edge 1 by epsilon orthogonally to the edge 2 if (pr == 222 || pr == 223) { _shiftEdge(j, 0.2f); intersection = true; break; } if (pr == 224 || pr == 225) { _shiftEdge(i, 0.2f); intersection = true; break; } // 2. If the edge 1 overlaps some other edge shift it by epsilon orthogonally if (pr == 3 || pr == 4) { _shiftEdge(i, 0.2f); intersection = true; break; } } } } _layout_edges[i].type = ELEMENT_NOT_PLANAR; } } } void MoleculeLayoutGraph::_buildOutline (void) { Vec2f v, inter; Vec2f pos_i; int i, j; int first_idx = vertexBegin(); float min_y = getPos(first_idx).y; const float EPS = 0.0001f; const float EPS_ANGLE = 1e-6f; for (i = vertexNext(first_idx); i < vertexEnd(); i = vertexNext(i)) { if (getPos(i).y < min_y) { min_y = getPos(i).y; first_idx = i; } } i = first_idx; float max_angle, cur_angle; float i_angle = 0; int next_nei = 0; pos_i = getPos(i); if (_outline.get() == 0) _outline.create(); else _outline->clear(); while (true) { const Vertex &vert = getVertex(i); if (i != first_idx) { v = pos_i; pos_i = getPos(i); v.sub(pos_i); i_angle = v.tiltAngle2(); } else if (_outline->size() > 0) break; _outline->push(pos_i); max_angle = 0.f; for (j = vert.neiBegin(); j < vert.neiEnd(); j = vert.neiNext(j)) { const Vec2f &pos_nei = getPos(vert.neiVertex(j)); v.diff(pos_nei, pos_i); cur_angle = v.tiltAngle2() - i_angle; // If cur_angle is almost zero but negative due to numeric errors (-1e-8) then // on some structures the results are not stable and even inifinite loop appreas // Example of such structure: ClC1(C(=O)C2(Cl)C3(Cl)C14Cl)C5(Cl)C2(Cl)C3(Cl)C(Cl)(Cl)C45Cl if (fabs(cur_angle) < EPS_ANGLE) cur_angle = 0; if (cur_angle < 0.f) cur_angle += 2 * PI; if (max_angle < cur_angle) { max_angle = cur_angle; next_nei = j; } } i = vert.neiVertex(next_nei); float dist, min_dist = 0.f; int int_edge = -1; Vec2f cur_v1 = pos_i; Vec2f cur_v2 = getPos(i); int prev_edge = -1; int cur_edge = vert.neiEdge(next_nei); while (min_dist < 10000.f) { min_dist = 10001.f; for (j = edgeBegin(); j < edgeEnd(); j = edgeNext(j)) { const Edge &edge = getEdge(j); const Vec2f &cur_v3 = getPos(edge.beg); const Vec2f &cur_v4 = getPos(edge.end); if (Vec2f::intersection(cur_v1, cur_v2, cur_v3, cur_v4, v)) if ((dist = Vec2f::dist(cur_v1, v)) < min_dist) { if (dist > EPS && j != prev_edge && j != cur_edge) { inter = v; min_dist = dist; int_edge = j; } } } if (min_dist < 10000.f) { if (min_dist > EPSILON) _outline->push(v); const Edge &edge = getEdge(int_edge); const Vec2f &cur_v3 = getPos(edge.beg); const Vec2f &cur_v4 = getPos(edge.end); Vec2f cur_v1v; Vec2f cur_v3v; Vec2f cur_v4v; cur_v1v.diff(cur_v1, inter); cur_v3v.diff(cur_v3, inter); cur_v4v.diff(cur_v4, inter); float angle1 = cur_v1v.tiltAngle2(); float angle3 = cur_v3v.tiltAngle2() - angle1; float angle4 = cur_v4v.tiltAngle2() - angle1; if (angle3 < 0) angle3 += 2 * PI; if (angle4 < 0) angle4 += 2 * PI; cur_v1 = inter; if (angle3 > angle4) { cur_v2 = cur_v3; i = edge.beg; } else { cur_v2 = cur_v4; i = edge.end; } prev_edge = cur_edge; cur_edge = int_edge; } } } } // Return 1 - with maximum code, 2 - neighbor of the 1 with maximum code // 3 - neighbor with maximum code from the rest or -1 if it doesn't exist. void MoleculeLayoutGraph::_getAnchor (int &v1, int &v2, int &v3) const { int i; if (vertexCount() ==1) { v1 = v2 = vertexBegin(); v3 = -1; return; } if (vertexCount() == 2) { v1 = vertexBegin(); v2 = vertexNext(v1); v3 = -1; if (_layout_vertices[v1].morgan_code < _layout_vertices[v2].morgan_code) { v2 = vertexBegin(); v1 = vertexNext(v2); } return; } v1 = vertexBegin(); for (i = vertexNext(v1); i < vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].morgan_code > _layout_vertices[v1].morgan_code) v1 = i; const Vertex &vert = getVertex(v1); v2 = vert.neiBegin(); for (i = vert.neiNext(v2); i < vert.neiEnd(); i = vert.neiNext(i)) if(_layout_vertices[vert.neiVertex(i)].morgan_code > _layout_vertices[vert.neiVertex(v2)].morgan_code) v2 = i; if (vert.degree() < 2) { v2 = vert.neiVertex(v2); v3 = -1; return; } v3 = vert.neiBegin(); if (v3 == v2) v3 = vert.neiNext(v2); for (i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) if (i != v2 && _layout_vertices[vert.neiVertex(i)].morgan_code > _layout_vertices[vert.neiVertex(v3)].morgan_code) v3 = i; v2 = vert.neiVertex(v2); v3 = vert.neiVertex(v3); } // Scale and transform void MoleculeLayoutGraph::_assignFinalCoordinates (float bond_length, const Array<Vec2f> &src_layout) { int i; if (_n_fixed > 0) { for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.scale(bond_length); return; } if (vertexCount() == 1) { getPos(vertexBegin()).set(0.f, 0.f); return; } // Flip according to various rules if (_molecule != 0 && _n_fixed == 0) { if (_molecule->countRSites() > 1) { // flip molecule vertically if R1 is not above other R-groups // flip molecule horizontally if R1 is not on the left QS_DEF(Array<int>, rgroup_list); Vec2f r1_pos, highest_pos(0.f, -1000.f); bool r1_exist = false; float center_x = 0.f; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { if (_molecule->isRSite(_layout_vertices[i].ext_idx)) { _molecule->getAllowedRGroups(_layout_vertices[i].ext_idx, rgroup_list); if (rgroup_list.size() == 1 && rgroup_list[0] == 1) { r1_pos = _layout_vertices[i].pos; r1_exist = true; } else if (_layout_vertices[i].pos.y > highest_pos.y) { highest_pos = _layout_vertices[i].pos; } } center_x += _layout_vertices[i].pos.x; } center_x /= vertexCount(); if (r1_exist) { if (r1_pos.y < highest_pos.y) for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.y *= -1; if (r1_pos.x > center_x) for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.x *= -1; } } else { // flip molecule horizontally if the first atom is righter than the last one int first = vertexBegin(); int last = first; for (i = first; i != vertexEnd(); i = vertexNext(i)) last = i; const float EPS = 0.0001f; float diff = _layout_vertices[first].pos.x - _layout_vertices[last].pos.x; if (diff > EPS) for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.x *= -1; } } // 1. Choose scale ratio and first edge to match float scale = bond_length, src_norm, norm; int v1, v2, v3; Vec2f p1, p2, p; _getAnchor(v1, v2, v3); p1.diff(src_layout[v2], src_layout[v1]); p2.diff(getPos(v2), getPos(v1)); src_norm = p1.length(); norm = p2.length(); if (norm < 0.0001) throw Error("too small edge"); // 2.1. If matching edge has zero length - just move to this point and scale if (src_norm < 0.001) { p1 = src_layout[v1]; p2 = getPos(v1); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { p.diff(getPos(i), p2); p.scale(scale); _layout_vertices[i].pos.sum(p1, p); } return; } // 2.2. If it has length from L/2 to 2L - scale by it, otherwise by L if (src_norm >= bond_length / 2 && src_norm <= 2 * bond_length) scale = src_norm / norm; // 3. Move first vertex to (0,0) p = getPos(v1); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.sub(p); // 4. Rotate CCW on Alpha angle between (first, second) edge and (first, second) edge in source graph float phi1, phi2, alpha, sina, cosa; phi1 = p1.tiltAngle(); phi2 = p2.tiltAngle(); alpha = phi1 - phi2; sina = sin(alpha); cosa = cos(alpha); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.rotate(sina, cosa); // 5. Scale for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.scale(scale); // 6. Match first vertices - shift by vector Pos(first) for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.add(src_layout[v1]); // 7. If needed turn around (first, second) if (vertexCount() > 2) { float crit = 0.f; // If v3 lays on the other side of line (first, second) - turn p1 = getPos(v1); p.diff(getPos(v2), p1); if (v3 == -1) { for (v3 = vertexBegin(); v3 < vertexEnd(); v3 = vertexNext(v3)) { if (fabs(p.x) < 0.001f) crit = (src_layout[v3].x - p1.x) * (getPos(v3).x - p1.x); else if (fabs(p.y) < 0.001f) crit = (src_layout[v3].y - p1.y) * (getPos(v3).y - p1.y); else { crit = (p.y * (src_layout[v3].x - p1.x) - p.x * (src_layout[v3].y - p1.y)) * (p.y * (getPos(v3).x - p1.x) - p.x * (getPos(v3).y - p1.y)); } if (fabs(crit) > 0.001) break; } } else crit = -1.0; if (crit < 0 && v3 < vertexEnd()) { // Move first vertex to (0,0) for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos.sub(p1); // Turn by -phi1 and flip vertically sina = -sin(phi1); cosa = cos(phi1); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { _layout_vertices[i].pos.rotate(sina, cosa); _layout_vertices[i].pos.y *= -1; } // Turn by phi1 and translate back sina = -sina; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { _layout_vertices[i].pos.rotate(sina, cosa); _layout_vertices[i].pos.add(p1); } } } } void MoleculeLayoutGraph::_findFixedComponents(BiconnectedDecomposer &bc_decom, Array<int> &fixed_components, PtrArray<MoleculeLayoutGraph> & bc_components) { // 1. Find biconnected components forming connected subgraph from fixed vertices if (_n_fixed == 0) return; int n_comp = bc_decom.componentsCount(); QS_DEF(Array<int>, fixed_count); fixed_count.clear_resize(n_comp); fixed_count.zerofill(); // calculate number of fixed vertices in each component for (int i = 0; i < n_comp; i++) { Filter filter; bc_decom.getComponent(i, filter); for (int j = vertexBegin(); j < vertexEnd(); j = vertexNext(j)) if (filter.valid(j) && _fixed_vertices[j]) fixed_count[i]++; } // keep only with fixed number greater than a half for (int i = 0; i < n_comp; i++) { Filter filter; bc_decom.getComponent(i, filter); if (fixed_count[i] > filter.count(*this) / 2) fixed_components[i] = 1; } _fixed_vertices.zerofill(); // update fixed vertices for (int i = 0; i < n_comp; i++) { if (!fixed_components[i]) continue; MoleculeLayoutGraph &component = *bc_components[i]; for (int j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) _fixed_vertices[component.getVertexExtIdx(j)] = 1; } Filter fixed_filter(_fixed_vertices.ptr(), Filter::EQ, 1); Graph fixed_graph; QS_DEF(Array<int>, fixed_mapping); QS_DEF(Array<int>, fixed_inv_mapping); fixed_graph.makeSubgraph(*this, fixed_filter, &fixed_mapping, &fixed_inv_mapping); if (Graph::isConnected(fixed_graph)) _n_fixed = fixed_filter.count(*this); else { // fixed subgraph is not connected - choose its greatest component int n = fixed_graph.countComponents(); const Array<int> &decomposition = fixed_graph.getDecomposition(); fixed_count.clear_resize(n); fixed_count.zerofill(); for (int i = fixed_graph.vertexBegin(); i < fixed_graph.vertexEnd(); i = fixed_graph.vertexNext(i)) fixed_count[decomposition[i]]++; int j = 0; for (int i = 1; i < n; i++) if (fixed_count[i] > fixed_count[j]) j = i; Filter max_filter(decomposition.ptr(), Filter::EQ, j); // update fixed vertices _fixed_vertices.zerofill(); _n_fixed = 0; for (int i = fixed_graph.vertexBegin(); i < fixed_graph.vertexEnd(); i = fixed_graph.vertexNext(i)) { if (max_filter.valid(i)) { _fixed_vertices[fixed_mapping[i]] = 1; _n_fixed++; } } for (int i = 0; i < n_comp; i++) { if (!fixed_components[i]) continue; MoleculeLayoutGraph &component = *bc_components[i]; int comp_v = component.getVertexExtIdx(component.vertexBegin()); int mapped = fixed_inv_mapping[comp_v]; if (!max_filter.valid(mapped)) fixed_components[i] = 0; } } } bool MoleculeLayoutGraph::_assignComponentsRelativeCoordinates(PtrArray<MoleculeLayoutGraph> & bc_components, Array<int> &fixed_components, BiconnectedDecomposer &bc_decom) { bool all_trivial = true; int n_comp = bc_decom.componentsCount(); // Possible solutions: // 1. a) vertex code is calculated inside component (doesn't depend on neighbors) or // b) vertex code is calculated respecting whole graph // 2. a) component code is the sum of 1a codes // b) component code is the sum of 1b codes // Initially was 1a and 2b then changed to 1b and 2b for (int i = 0; i < n_comp; i++) { MoleculeLayoutGraph &component = *bc_components[i]; component.max_iterations = max_iterations; //component._calcMorganCodes(); component._total_morgan_code = 0; for (int j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) component._total_morgan_code += _layout_vertices[component.getLayoutVertex(j).ext_idx].morgan_code; // Mark cyclic atoms if (!component.isSingleEdge()) { all_trivial = false; for (int j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) { component._layout_vertices[j].is_cyclic = true; _layout_vertices[component._layout_vertices[j].ext_idx].is_cyclic = true; } for (int j = component.edgeBegin(); j < component.edgeEnd(); j = component.edgeNext(j)) { component._layout_edges[j].is_cyclic = true; _layout_edges[component._layout_edges[j].ext_idx].is_cyclic = true; } } int fixed = fixed_components[i]; component._assignRelativeCoordinates(fixed, *this); if (fixed != fixed_components[i]) { fixed_components[i] = fixed; // update fixed vertices _fixed_vertices.resize(vertexEnd()); _fixed_vertices.zerofill(); _n_fixed = 0; for (int j = 0; j < n_comp; j++) { if (!fixed_components[j]) continue; Filter fix_filter; bc_decom.getComponent(j, fix_filter); for (int k = vertexBegin(); k < vertexEnd(); k = vertexNext(k)) if (!_fixed_vertices[k]) { _fixed_vertices[k] = 1; _n_fixed++; } } } } return all_trivial; } void MoleculeLayoutGraph::_assignRelativeSingleEdge (int &fixed_component, const MoleculeLayoutGraph &supergraph) { // Trivial component layout int idx1 = vertexBegin(); int idx2 = vertexNext(idx1); _layout_vertices[idx1].type = ELEMENT_BOUNDARY; _layout_vertices[idx2].type = ELEMENT_BOUNDARY; if (fixed_component) { _layout_vertices[idx1].pos = supergraph.getPos(getVertexExtIdx(idx1)); _layout_vertices[idx2].pos = supergraph.getPos(getVertexExtIdx(idx2)); } else { _layout_vertices[idx1].pos.set(0.f, 0.f); _layout_vertices[idx2].pos.set(0.f, 1.f); } _layout_edges[edgeBegin()].type = ELEMENT_BOUNDARY; } bool MoleculeLayoutGraphSimple::_tryToFindPattern (int &fixed_component) { // try to find pattern TL_GET(ObjArray<PatternLayout>, _patterns); MorganCode morgan(*this); QS_DEF(Array<long>, morgan_codes); morgan.calculate(morgan_codes, 3, 7); long morgan_code = 0; for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) morgan_code += morgan_codes[i]; int left = 0; int right = _patterns.size() - 1; int pat_idx = 0; int cmp; while (left < right) { if (right - left == 1) { if (_pattern_cmp2(_patterns[left], vertexCount(), edgeCount(), morgan_code) == 0) pat_idx = left; else if (_pattern_cmp2(_patterns[right], vertexCount(), edgeCount(), morgan_code) == 0) pat_idx = right; break; } pat_idx = (right + left) / 2; cmp = _pattern_cmp2(_patterns[pat_idx], vertexCount(), edgeCount(), morgan_code); if (cmp < 0) left = pat_idx; else right = pat_idx; } while (pat_idx > 0 && _pattern_cmp2(_patterns[pat_idx - 1], vertexCount(), edgeCount(), morgan_code) == 0) pat_idx--; while (pat_idx < _patterns.size() && _pattern_cmp2(_patterns[pat_idx], vertexCount(), edgeCount(), morgan_code) == 0) { // Match pattern // TODO: check different attachment points PatternLayout &pattern = _patterns[pat_idx]; EmbeddingEnumerator ee(*this); ee.setSubgraph(pattern); ee.cb_match_edge = _match_pattern_bond; ee.cb_embedding = _pattern_embedding; ee.userdata = this; if (!ee.process()) { if (pattern.isFixed()) fixed_component = 1; return true; } pat_idx++; } return false; } void MoleculeLayoutGraph::_findFirstVertexIdx(int n_comp, Array<int> & fixed_components, PtrArray<MoleculeLayoutGraph> &bc_components, bool all_trivial) { if (_n_fixed > 0) { int j = -1; for (int i = 0; i < n_comp; i++) if (fixed_components[i]) { _copyLayout(*bc_components[i]); j = i; } if (j == -1) throw Error("Internal error: cannot find a fixed component with fixed vertices"); MoleculeLayoutGraph &component = *bc_components[j]; _first_vertex_idx = component._layout_vertices[component.vertexBegin()].ext_idx; } else { // ( 0]. Nucleus.; // Begin from nontrivial component with maximum code // if there's no then begin from vertex with maximum code and its neighbor with maximum code too int nucleus_idx = 0; if (!all_trivial) { nucleus_idx = -1; for (int i = 0; i < n_comp; i++) { MoleculeLayoutGraph &component = *bc_components[i]; if (!component.isSingleEdge()) { if (nucleus_idx == -1 || component._total_morgan_code > bc_components[nucleus_idx]->_total_morgan_code) nucleus_idx = i; } } if (nucleus_idx < 0) throw Error("Internal error: cannot find nontrivial component"); MoleculeLayoutGraph &nucleus = *bc_components[nucleus_idx]; _copyLayout(nucleus); _first_vertex_idx = nucleus._layout_vertices[nucleus._first_vertex_idx].ext_idx; } else { for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].morgan_code > _layout_vertices[nucleus_idx].morgan_code) nucleus_idx = i; const Vertex &nucleus = getVertex(nucleus_idx); int nucleus_idx2 = nucleus.neiBegin(); for (int j = nucleus.neiNext(nucleus_idx2); j < nucleus.neiEnd(); j = nucleus.neiNext(j)) if (_layout_vertices[nucleus.neiVertex(j)].morgan_code > _layout_vertices[nucleus.neiVertex(nucleus_idx2)].morgan_code) nucleus_idx2 = j; int nucleus_edge = nucleus.neiEdge(nucleus_idx2); nucleus_idx2 = nucleus.neiVertex(nucleus_idx2); _first_vertex_idx = nucleus_idx; _layout_vertices[nucleus_idx].type = ELEMENT_BOUNDARY; _layout_vertices[nucleus_idx].pos.set(0.f, 0.f); _layout_vertices[nucleus_idx2].type = ELEMENT_BOUNDARY; _layout_vertices[nucleus_idx2].pos.set(1.f, 0.f); _layout_edges[nucleus_edge].type = ELEMENT_BOUNDARY; } } } bool MoleculeLayoutGraph::_prepareAssignedList(Array<int> &assigned_list, BiconnectedDecomposer &bc_decom, PtrArray<MoleculeLayoutGraph> &bc_components, Array<int> &bc_tree) { assigned_list.clear(); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { if (_layout_vertices[i].type == ELEMENT_NOT_DRAWN) continue; const Vertex &vert = getVertex(i); for (int j = vert.neiBegin(); j < vert.neiEnd(); j = vert.neiNext(j)) { if (_layout_vertices[vert.neiVertex(j)].type == ELEMENT_NOT_DRAWN) { assigned_list.push(i); break; } } } if (assigned_list.size() == 0) { // restore ignored ears in chains for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].type == ELEMENT_IGNORE) _layout_vertices[i].type = ELEMENT_BOUNDARY; _refineCoordinates(bc_decom, bc_components, bc_tree); return false; } // ( 2] the list is ordered with cyclic atoms at the top of the list; // with descending ATCD numbers and acyclic atoms at the bottom; // of the list with descending ATCD numbers;; assigned_list.qsort(_vertex_cmp, this); return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_assign_smart.cpp�������������������������������0000664�0000000�0000000�00000173233�12710376503�0026157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ //#include "api/src/indigo_internal.h" #include "base_cpp/profiling.h" #include "layout/molecule_layout_graph.h" #include "layout/molecule_layout_macrocycles.h" #include "layout/attachment_layout.h" #include "graph/biconnected_decomposer.h" #include "graph/cycle_enumerator.h" #include "graph/embedding_enumerator.h" #include "graph/morgan_code.h" #include "layout/layout_pattern_smart.h" #include <math/random.h> #include <algorithm> #include <memory> using namespace indigo; enum { QUERY_BOND_SINGLE_OR_DOUBLE = 5, QUERY_BOND_SINGLE_OR_AROMATIC = 6, QUERY_BOND_DOUBLE_OR_AROMATIC = 7, QUERY_BOND_ANY = 8 }; // Make relative coordinates of a component absolute static int _vertex_cmp (int &n1, int &n2, void *context) { const MoleculeLayoutGraph &graph = *(MoleculeLayoutGraph *)context; const LayoutVertex &v1 = graph.getLayoutVertex(n1); const LayoutVertex &v2 = graph.getLayoutVertex(n2); if (v1.is_cyclic != v2.is_cyclic) { if (v1.is_cyclic == true) return 1; return -1; } return v1.morgan_code - v2.morgan_code; } void MoleculeLayoutGraphSmart::_assignAbsoluteCoordinates (float bond_length) { BiconnectedDecomposer bc_decom(*this); QS_DEF(Array<int>, bc_tree); QS_DEF(PtrArray<MoleculeLayoutGraph>, bc_components); QS_DEF(Array<int>, fixed_components); bool all_trivial = true; int n_comp = bc_decom.decompose(); fixed_components.clear_resize(n_comp); fixed_components.zerofill(); bc_components.clear(); for (int i = 0; i < n_comp; i++) { Filter comp; bc_decom.getComponent(i, comp); std::unique_ptr<MoleculeLayoutGraph> current_component(getInstance()); current_component->makeLayoutSubgraph(*this, comp); bc_components.add(current_component.release()); } bc_tree.clear_resize(vertexEnd()); _makeComponentsTree(bc_decom, bc_components, bc_tree); // 1. Find biconnected components forming connected subgraph from fixed vertices _findFixedComponents(bc_decom, fixed_components, bc_components); all_trivial = _assignComponentsRelativeCoordinates(bc_components, fixed_components, bc_decom); _findFirstVertexIdx(n_comp, fixed_components, bc_components, all_trivial); int i, j = -1; // ( 1] atoms assigned absolute coordinates and adjacent to atoms not; // assigned coordinates are put on a list; QS_DEF(Array<int>, assigned_list); QS_DEF(Array<int>, adjacent_list); while (true) { if(cancellation && cancellation->isCancelled()) throw Error("Molecule layout has been cancelled: %s", cancellation->cancelledRequestMessage()); if (!_prepareAssignedList(assigned_list, bc_decom, bc_components, bc_tree)) return; // ( 3.i] let k = 0 ( top of the list];; while (assigned_list.size() != 0) { int k = assigned_list.pop(); const Vertex &vert_k = getVertex(k); // ( 3.ii] a list of atoms adjacent to atom Uzel and not previously; // assigned coordinates is created and ordered with cyclic atoms; // at the top of the list with descending ATCD numbers and acyclic atoms; // at the bottom of the list with descending ATCD numbers;; adjacent_list.clear(); for (i = vert_k.neiBegin(); i < vert_k.neiEnd(); i = vert_k.neiNext(i)) if (_layout_vertices[vert_k.neiVertex(i)].type == ELEMENT_NOT_DRAWN) adjacent_list.push(vert_k.neiVertex(i)); if (adjacent_list.size() == 0) break; // When all components outgoing from vertex are trivial (edges) then use tree algorithm all_trivial = true; for (i = 0; i < bc_decom.getIncomingCount(k); i++) if (!bc_components[bc_decom.getIncomingComponents(k)[i]]->isSingleEdge()) { all_trivial = false; break; } if (all_trivial && bc_tree[k] != -1 && !bc_components[bc_tree[k]]->isSingleEdge()) all_trivial = false; if (all_trivial) { adjacent_list.qsort(_vertex_cmp, this); _attachDandlingVertices(k, adjacent_list); } else { // Component layout in current vertex should have the same angles between components. // So it depends on component order and their flipping (for nontrivial components) AttachmentLayoutSmart att_layout(bc_decom, bc_components, bc_tree, *this, k); // ( 3.iii] Look over all possible orders of component layouts // (vertex itself is already drawn means one component is already drawn) // ( 3.iv] Choose layout with minimal energy LayoutChooser layout_chooser(att_layout); layout_chooser.perform(); att_layout.markDrawnVertices(); } // ( 3.v] let k = k + 1;; // ( 3.vi] repeat steps 3.ii-3.v until all atoms in the list have been processed;; } // ( 4] repeat steps 1-3 until all atoms have been assigned absolute coordinates.; } } void MoleculeLayoutGraphSmart::_get_toches_to_component(Cycle& cycle, int component_number, Array<interval>& interval_list) { if (component_number < 0 || component_number >= _layout_component_count) return; QS_DEF(Array<bool>, touch_to_current_component); touch_to_current_component.clear_resize(cycle.vertexCount()); touch_to_current_component.zerofill(); for (int i = 0; i < cycle.vertexCount(); i++) { const Vertex& vert = getVertex(cycle.getVertex(i)); for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n)) { if (getEdgeType(vert.neiEdge(n)) != ELEMENT_NOT_DRAWN && _layout_component_number[vert.neiEdge(n)] == component_number) touch_to_current_component[i] = true; } } int first_start = -1; for (int i = 0; i < cycle.vertexCount(); i++) if (touch_to_current_component[i] && _layout_component_number[cycle.getEdgeC(i)] != component_number) { first_start = i; break; } interval_list.clear(); if (first_start == -1) return; int start = first_start; int finish = 0; while (true) { finish = (start + 1) % cycle.vertexCount(); while (!touch_to_current_component[finish]) finish = (finish + 1) % cycle.vertexCount(); interval_list.push(); interval_list.top().init(start, finish); start = finish; while (_layout_component_number[cycle.getEdge(start)] == component_number) start = (start + 1) % cycle.vertexCount(); if (start == first_start) break; } } int MoleculeLayoutGraphSmart::_search_separated_component(Cycle& cycle, Array<interval>& interval_list) { for (int i = 0; i < _layout_component_count; i++) { _get_toches_to_component(cycle, i, interval_list); if (interval_list.size() > 1) return i; } return -1; } void MoleculeLayoutGraphSmart::_search_path(int start, int finish, Array<int>& path, int component_number) { QS_DEF(Array<bool>, visited); visited.clear_resize(vertexEnd()); visited.zerofill(); visited[start] = true; QS_DEF(Array<int>, vertices_list); QS_DEF(Array<int>, previous_list); vertices_list.clear(); vertices_list.push(start); previous_list.clear_resize(vertexEnd()); for (int i = 0; i < vertices_list.size(); i++) { if (vertices_list[i] == finish) { while (finish != start) { path.push(finish); finish = previous_list[finish]; } path.push(finish); for (int j = 0; j < path.size() / 2; j++) swap(path[j], path[path.size() - 1 - j]); return; } const Vertex& vert = getVertex(vertices_list[i]); for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n)) { int e = vert.neiEdge(n); int v = vert.neiVertex(n); if (_layout_component_number[e] == component_number && !visited[v]) { visited[v] = true; vertices_list.push(v); previous_list[v] = vertices_list[i]; } } } } void MoleculeLayoutGraphSmart::_assignRelativeCoordinates (int &fixed_component, const MoleculeLayoutGraph &supergraph) { profTimerStart(t, "_assignRelativeCoordinates"); int i; if (isSingleEdge()) { _assignRelativeSingleEdge(fixed_component, supergraph); return; } // 2.1. Use layout of fixed components and find border edges and vertices if (fixed_component) { for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) _layout_vertices[i].pos = supergraph.getPos(getVertexExtIdx(i)); CycleEnumerator ce(*this); ce.context = this; ce.cb_handle_cycle = _border_cb; if (ce.process()) return; fixed_component = 0; } else { //if (PatternLayoutFinder::tryToFindPattern(*this)) //return; } //TODO: repair exception with vec2f QS_DEF(ObjPool<Cycle>, cycles); cycles.clear(); int n_cycles = sssrCount(); for (i = 0; i < n_cycles; i++) { int cycle_idx = cycles.add(sssrEdges(i), *this); cycles[cycle_idx].canonize(); } while (cycles.size() != 0) { QS_DEF(Array<int>, unused_count); unused_count.clear_resize(cycles.end()); unused_count.zerofill(); for (int i = cycles.begin(); i != cycles.end(); i = cycles.next(i)) { for (int j = 0; j < cycles[i].vertexCount(); j++) { if (_layout_component_number[cycles[i].getEdge(j)] == -1) unused_count[i]++; } } for (int i = cycles.begin(); i != cycles.end(); i = cycles.next(i)) unused_count[i] *= cycles[i].vertexCount(); for (int i = cycles.begin(); i != cycles.end(); i = cycles.next(i)) cycles[i].calcMorganCode(supergraph); int min_i = cycles.begin(); for (int i = cycles.begin(); i != cycles.end(); i = cycles.next(i)) { if (unused_count[i] < unused_count[min_i] || (unused_count[i] == unused_count[min_i] && cycles[i].morganCode() > cycles[min_i].morganCode())) min_i = i; } if (unused_count[min_i] > 0) { QS_DEF(Array<interval>, interval_list); int separating_component = _search_separated_component(cycles[min_i], interval_list); if (separating_component >= 0) { for (int i = 0; i < interval_list.size(); i++) { int start = interval_list[i].left; int finish = interval_list[i].right; QS_DEF(Array<int>, verts); QS_DEF(Array<int>, edges); verts.clear(); edges.clear(); _search_path(cycles[min_i].getVertex(finish), cycles[min_i].getVertex(start), verts, separating_component); for (int j = (start + 1) % cycles[min_i].vertexCount(); j != finish; j = (j + 1) % cycles[min_i].vertexCount()) verts.push(cycles[min_i].getVertex(j)); for (int j = 0; j < verts.size(); j++) { int e = findEdgeIndex(verts[j], verts[(j + 1) % verts.size()]); edges.push(e); } cycles.add(verts, edges); } } else { _assignEveryCycle(cycles[min_i]); } } cycles.remove(min_i); } } void MoleculeLayoutGraphSmart::_assignEveryCycle(const Cycle &cycle) { profTimerStart(t, "_assignFirstCycle"); const int size = cycle.vertexCount(); _first_vertex_idx = cycle.getVertex(0); MoleculeLayoutMacrocyclesLattice layout(size); if (size <= 6) for (int i = 0; i < size; i++) _molecule->cis_trans.setParity(_layout_edges[cycle.getEdge(i)].orig_idx, 0); for (int i = 0; i < size; i++) { // edge parallels // !! int order_next = 0; int edge_number = cycle.getEdge(i); LayoutEdge edge = _layout_edges[edge_number]; int ext_edge_number = edge.orig_idx; int order = _molecule->getBondOrder(ext_edge_number); switch (order) { case BOND_SINGLE: order_next = 1; break; case BOND_DOUBLE: order_next = 2; break; case BOND_TRIPLE: order_next = 3; break; default: order_next = 1; } int order_prev; int ext_edge_number_prev = _layout_edges[cycle.getEdgeC(i - 1)].orig_idx; switch (_molecule->getBondOrder(ext_edge_number_prev)) { case BOND_SINGLE: order_prev = 1; break; case BOND_DOUBLE: order_prev = 2; break; case BOND_TRIPLE: order_prev = 3; break; default: order_prev = 1; } layout.setVertexEdgeParallel(i, order_next + order_prev >= 4); // tras-cis configuration int next_vertex = _layout_vertices[cycle.getEdgeFinish(i + 1)].orig_idx; int prev_vertex = _layout_vertices[cycle.getEdgeStart(i - 1)].orig_idx; if (_molecule->cis_trans.getParity(ext_edge_number)) { int _sameside = _molecule->cis_trans.sameside(ext_edge_number, prev_vertex, next_vertex); if (_sameside) layout.setEdgeStereo(i, MoleculeCisTrans::CIS); else layout.setEdgeStereo(i, MoleculeCisTrans::TRANS); } else { // if (_layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN && // _layout_vertices[cycle.getVertex((i + 1) % size)].type != ELEMENT_NOT_DRAWN) { if (_layout_edges[cycle.getEdge(i)].type != ELEMENT_NOT_DRAWN) { Vec2f prev_point; if (_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN) prev_point = _layout_vertices[cycle.getVertexC(i - 1)].pos; else { for (int j = getVertex(cycle.getVertex(i)).neiBegin(); j != getVertex(cycle.getVertex(i)).neiEnd(); j = getVertex(cycle.getVertex(i)).neiNext(j)) if (_layout_edges[getVertex(cycle.getVertex(i)).neiEdge(j)].type != ELEMENT_NOT_DRAWN && getVertex(cycle.getVertex(i)).neiVertex(j) != cycle.getVertexC(i + 1)) prev_point = _layout_vertices[getVertex(cycle.getVertex(i)).neiVertex(j)].pos; } Vec2f next_point; if (_layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN) next_point = _layout_vertices[cycle.getVertexC(i + 2)].pos; else { for (int j = getVertex(cycle.getVertexC(i + 1)).neiBegin(); j != getVertex(cycle.getVertexC(i + 1)).neiEnd(); j = getVertex(cycle.getVertexC(i + 1)).neiNext(j)) if (_layout_edges[getVertex(cycle.getVertexC(i + 1)).neiEdge(j)].type != ELEMENT_NOT_DRAWN && getVertex(cycle.getVertexC(i + 1)).neiVertex(j) != cycle.getVertex(i)) next_point = _layout_vertices[getVertex(cycle.getVertexC(i + 1)).neiVertex(j)].pos; } int _sameside = _isCisConfiguratuin(prev_point, _layout_vertices[cycle.getVertexC(i)].pos, _layout_vertices[cycle.getVertexC(i + 1)].pos, next_point); if (_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN && _layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN) { if (_sameside) layout.setEdgeStereo(i, MoleculeCisTrans::CIS); else layout.setEdgeStereo(i, MoleculeCisTrans::TRANS); } else { if ((_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN) ^ (_layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN)) { if (_sameside) layout.setEdgeStereo(i, MoleculeCisTrans::TRANS); else layout.setEdgeStereo(i, MoleculeCisTrans::CIS); } else layout.setEdgeStereo(i, MoleculeCisTrans::CIS); } } } layout.setVertexDrawn(i, _layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN); // trees sizes } QS_DEF(ObjArray<MoleculeLayoutSmoothingSegment>, segment); QS_DEF(Array<Vec2f>, rotation_point); QS_DEF(Array<int>, rotation_vertex); _segment_smoothing_prepearing(cycle, rotation_vertex, rotation_point, segment, layout); int segment_count = segment.size(); for (int i = 0; i < segment_count; i++) { for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) { if (segment[i].is_start(v)) if (segment[i]._graph.getVertex(v).degree() > 2) layout.setEdgeStereo(rotation_vertex[i], 0); if (segment[i].is_finish(v)) if (segment[i]._graph.getVertex(v).degree() > 2) layout.setEdgeStereo((rotation_vertex[(i + 1) % segment_count] - 1 + size) % size, 0); } } /*bool easy_case = size <= 9; if (easy_case) { QS_DEF(Array<int>, last); last.clear_resize(_layout_component_count); last.fill(-1); for (int i = 0; i < size; i++) { int comp = _layout_component_number[cycle.getEdge(i)]; if (comp >= 0) { if (last[comp] >= 0) easy_case = false; last[comp] = i; } } QS_DEF(Array<int>, order); order.clear_resize(size); for (int i = 0; i < size; i++) { order[i] = _molecule->getBondOrder(getEdgeOrigIdx(cycle.getEdge(i))); if (order[i] > 3) order[i] = 1; } order.push(order[0]); for (int i = 0; i < size; i++) easy_case &= (order[i] + order[i + 1] < 4); for (int i = 0; i < size; i++) { int next_vertex = _layout_vertices[cycle.getEdgeFinish(i + 1)].orig_idx; int prev_vertex = _layout_vertices[cycle.getEdgeStart(i - 1)].orig_idx; if (_molecule->cis_trans.getParity(getEdgeOrigIdx(cycle.getEdge(i)))) { easy_case &= _molecule->cis_trans.sameside(getEdgeOrigIdx(cycle.getEdge(i)), prev_vertex, next_vertex); } } if (easy_case) { for (int i = 0; i < size; i++) { layout.getPos(cycle.getVertex(i)) = Vec2f(1, 0); layout.getPos(cycle.getVertex(i)).rotate(2 * PI / size * i); } for (int i = 0; i < size; i++) if (getVertexType(cycle.getVertex(i)) == ELEMENT_NOT_DRAWN) getPos(cycle.getVertex(i)) = layout.getPos(i); for (int i = 0; i < size; i++) { setVertexType(cycle.getVertex(i), ELEMENT_DRAWN); setEdgeType(cycle.getEdge(i), ELEMENT_DRAWN); } } */ //printf("%d do layout cycle \n", size); // calculate target angle for (int s = 0; s < segment_count; s++) { for (int i = rotation_vertex[s]; i != rotation_vertex[(s + 1) % segment_count]; i = (i + 1) % size) { int prev_layout_component = _layout_component_number[cycle.getEdgeC(i - 1)]; int next_layout_component = _layout_component_number[cycle.getEdge(i)]; if (prev_layout_component < 0 && next_layout_component < 0) { layout.setTargetAngle(i, 2 * PI / 3); layout.setAngleImportance(i, 0.2); } else if ((prev_layout_component < 0) ^ (next_layout_component < 0)) { const MoleculeLayoutSmoothingSegment& calc_segment = prev_layout_component < 0 ? segment[s] : segment[(s + segment_count - 1) % segment_count]; int calc_vertex = prev_layout_component < 0 ? calc_segment.get_start() : calc_segment.get_finish(); Cycle border; calc_segment._graph._getBorder(border); int calc_vertex_in_border = -1; for (int j = 0; j < border.vertexCount(); j++){ if (border.getVertex(j) == calc_vertex) { calc_vertex_in_border = j; break; } } double angle = 0; int prev_vertex = -1; int next_vertex = -1; if (border.vertexCount() != 0 && calc_vertex_in_border >= 0) { prev_vertex = border.getVertexC(calc_vertex_in_border - 1); next_vertex = border.getVertexC(calc_vertex_in_border + 1); } else { for (int n : calc_segment._graph.getVertex(calc_vertex).neighbors()) { int v = calc_segment._graph.getVertex(calc_vertex).neiVertex(n); if (prev_vertex < 0 || calc_segment.getIntPosition(v).y > calc_segment.getIntPosition(prev_vertex).y) prev_vertex = v; if (next_vertex < 0 || calc_segment.getIntPosition(v).y < calc_segment.getIntPosition(next_vertex).y) next_vertex = v; } if (next_layout_component < 0) { int temp = prev_vertex; prev_vertex = next_vertex; next_vertex = temp; } } angle = (calc_segment.getIntPosition(next_vertex) - calc_segment.getIntPosition(calc_vertex)).tiltAngle2(); angle -= (calc_segment.getIntPosition(prev_vertex) - calc_segment.getIntPosition(calc_vertex)).tiltAngle2(); while (angle < 0) angle += 2 * PI; while (angle >= 2 * PI) angle -= 2 * PI; layout.setTargetAngle(i, PI - angle / 2); } else if (prev_layout_component == next_layout_component) { double angle = (getPos(cycle.getVertexC(i - 1)) - getPos(cycle.getVertexC(i))).tiltAngle2(); angle -= (getPos(cycle.getVertexC(i + 1)) - getPos(cycle.getVertexC(i))).tiltAngle2(); while (angle < 0) angle += 2 * PI; while (angle >= 2 * PI) angle -= 2 * PI; if (angle > PI) angle = 2 * PI - angle; layout.setTargetAngle(i, angle); } // temporary value else { layout.setTargetAngle(i, PI); layout.setAngleImportance(i, 0.2); } } } QS_DEF(Array<int>, _is_vertex_taken); enum { NOT_CONSIDERED, IN_LIST, NOT_IN_LIST }; QS_DEF(Array<int>, _list_of_vertex); QS_DEF(Array<int>, _segment_weight_outside); _segment_weight_outside.clear_resize(segment_count); _segment_weight_outside.zerofill(); for (int i = 0; i < size; i++) if (_layout_component_number[cycle.getEdge(i)] < 0) _layout_component_number[cycle.getEdge(i)] = _layout_component_count; _layout_component_count++; QS_DEF(Array<bool>, _is_layout_component_incoming); _is_layout_component_incoming.clear_resize(_layout_component_count); _is_layout_component_incoming.zerofill(); for (int i = 0; i < size; i++) _is_layout_component_incoming[_layout_component_number[cycle.getEdge(i)]] = true; for (int i = 0; i < segment_count; i++) { for (int up = 0; up <= 1; up++) { _is_vertex_taken.clear_resize(_graph->vertexEnd()); _is_vertex_taken.fill(NOT_CONSIDERED); if (i == segment_count - 1) { int x = 5; } _list_of_vertex.clear_resize(0); bool is_segment_trivial = segment[i].get_layout_component_number() == -1 && segment[(i + segment_count - 1) % segment_count].get_layout_component_number() == -1 && up; for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) { if ((!segment[i].is_finish(v) && !segment[i].is_start(v) && segment[i].isVertexUp(v) ^ !up) || (is_segment_trivial && !segment[i].is_finish(v))) { int ext_v = segment[i]._graph.getVertexExtIdx(v); _is_vertex_taken[getVertexExtIdx(ext_v)] = IN_LIST; _list_of_vertex.push(ext_v); } } bool touch_to_another_segment = false; for (int j = 0; j < _list_of_vertex.size(); j++) { const Vertex& vert = getVertex(_list_of_vertex[j]); for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n)) { int vn = vert.neiVertex(n); if (_is_vertex_taken[getVertexExtIdx(vn)] != NOT_CONSIDERED) continue; bool is_this_comp = false; for (int n2 = getVertex(vn).neiBegin(); n2 != getVertex(vn).neiEnd(); n2 = getVertex(vn).neiNext(n2)) if (_layout_component_number[getVertex(vn).neiEdge(n2)] >= 0) { if (_is_layout_component_incoming[_layout_component_number[getVertex(vn).neiEdge(n2)]]) is_this_comp = true; if (!is_segment_trivial && _layout_component_number[getVertex(vn).neiEdge(n2)] != segment[i].get_layout_component_number() && _layout_component_number[getVertex(vn).neiEdge(n2)] != _layout_component_count - 1) touch_to_another_segment = true; } if (!is_this_comp) { _list_of_vertex.push(vn); _is_vertex_taken[getVertexExtIdx(vn)] = IN_LIST; } else _is_vertex_taken[getVertexExtIdx(vn)] = NOT_IN_LIST; } } for (int j = 0; j < _list_of_vertex.size(); j++) _list_of_vertex[j] = getVertexExtIdx(_list_of_vertex[j]); for (int j = 0; j < _list_of_vertex.size(); j++) { const Vertex& vert = _graph->getVertex(_list_of_vertex[j]); for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n)) { int vn = vert.neiVertex(n); if (_is_vertex_taken[vn] != NOT_CONSIDERED) continue; _list_of_vertex.push(vn); _is_vertex_taken[vn] = IN_LIST; } } _segment_weight_outside[i] += (up ? 1 : -1) * (touch_to_another_segment ? 3 : 1) * _list_of_vertex.size(); } } QS_DEF(Array<int>, _index_in_cycle); _index_in_cycle.clear_resize(vertexEnd()); _index_in_cycle.fffill(); for (int i = 0; i < size; i++) _index_in_cycle[cycle.getVertex(i)] = i; for (int i = 0; i < segment_count; i++) { if (segment[i].get_layout_component_number() < 0 && segment[(i + segment_count - 1) % segment_count].get_layout_component_number() < 0) layout.addVertexOutsideWeight(rotation_vertex[i], _segment_weight_outside[i] - 1); else { layout.setComponentFinish(rotation_vertex[i], rotation_vertex[(i + 1) % segment_count]); layout.setVertexAddedSquare(rotation_vertex[i], segment[i].get_square()); Cycle border; if (segment[i].get_layout_component_number() >= 0) segment[i]._graph._getBorder(border); int count_neibourhoods_outside = 0; if (segment[i].get_layout_component_number() >= 0 && border.vertexCount() != 0) { int start_in_border = -1; int finish_in_border = -1; for (int j = 0; j < border.vertexCount(); j++) { if (border.getVertex(j) == segment[i].get_start()) start_in_border = j; if (border.getVertex(j) == segment[i].get_finish()) finish_in_border = j; } if (start_in_border >= 0 && finish_in_border >= 0) { for (int j = (start_in_border + 1) % border.vertexCount(); j != finish_in_border; j = (j + 1) % border.vertexCount()) { if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[i] + 1) % size) count_neibourhoods_outside++; if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[(i + 1) % segment_count] - 1 + size) % size) count_neibourhoods_outside++; } for (int j = (finish_in_border + 1) % border.vertexCount(); j != start_in_border; j = (j + 1) % border.vertexCount()) { if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[i] + 1) % size) count_neibourhoods_outside--; if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[(i + 1) % segment_count] - 1 + size) % size) count_neibourhoods_outside--; } } } bool right_orientation; if (count_neibourhoods_outside > 0) right_orientation = true; else if (count_neibourhoods_outside < 0) right_orientation = false; else { double y1 = 0, y2 = 0; for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) { if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(v)] == (rotation_vertex[i] + 1) % size) { y1 = segment[i].getIntPosition(v).y; } if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(v)] == (rotation_vertex[(i + 1) % segment_count] + size - 1) % size) { y2 = segment[i].getIntPosition(v).y; } } if ((y1 + y2) / 2 > EPSILON || ((abs((y1 + y2) / 2) <= EPSILON) && (y1 + y2) / 2 > segment[i].getIntCenter().y)) { right_orientation = true; } else { right_orientation = false; } } if (right_orientation) { layout.addVertexOutsideWeight(rotation_vertex[i], -_segment_weight_outside[i]); layout.addVertexOutsideWeight(rotation_vertex[(i + 1) % segment_count], -_segment_weight_outside[i]); } else { layout.addVertexOutsideWeight(rotation_vertex[i], _segment_weight_outside[i]); layout.addVertexOutsideWeight(rotation_vertex[(i + 1) % segment_count], _segment_weight_outside[i]); } } } layout.doLayout(); // now we must to smooth just made layout // lets check if all cycle is layouted ealier in single biconnected compenent /*int start = -1; bool undrawn = false; for (int i = 0; i < size; i++) undrawn |= _layout_vertices[cycle.getVertex(i)].type == ELEMENT_NOT_DRAWN; if (undrawn) { for (int i = size - 1; i >= 0; i--) if (_layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN) start = i; if (start == 0 && _layout_vertices[cycle.getVertex(size - 1)].type != ELEMENT_NOT_DRAWN) { while (_layout_vertices[cycle.getVertex(start)].type != ELEMENT_NOT_DRAWN) start = (start + 1) % size; while (_layout_vertices[cycle.getVertex(start)].type == ELEMENT_NOT_DRAWN) start = (start + 1) % size; } }*/ QS_DEF(Array<bool>, need_to_insert); need_to_insert.clear_resize(size); need_to_insert.zerofill(); for (int i = 0; i < size; i++) need_to_insert[i] = _layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN; int start = 0; bool componentIsWholeCycle = false; QS_DEF(Array<bool>, _is_component_touch); _is_component_touch.clear_resize(_layout_component_count); for (int index = 0; index < size; index++) if (need_to_insert[index]) { // 1. search of connected component QS_DEF(Array<int>, insideVertex); insideVertex.clear_resize(0); insideVertex.push(cycle.getVertex(index)); QS_DEF(Array<bool>, takenVertex); takenVertex.clear_resize(vertexCount()); takenVertex.zerofill(); takenVertex[cycle.getVertex(index)] = true; _is_component_touch.zerofill(); for (int i = 0; i < insideVertex.size(); i++) for (int j = getVertex(insideVertex[i]).neiBegin(); j != getVertex(insideVertex[i]).neiEnd(); j = getVertex(insideVertex[i]).neiNext(j)) { int vertj = getVertex(insideVertex[i]).neiVertex(j); if (_layout_edges[getVertex(insideVertex[i]).neiEdge(j)].type != ELEMENT_NOT_DRAWN && !takenVertex[vertj]) { _is_component_touch[_layout_component_number[getVertex(insideVertex[i]).neiEdge(j)]] = true; insideVertex.push(vertj); takenVertex[vertj] = true; } } if (!componentIsWholeCycle) { componentIsWholeCycle = true; for (int i = 0; i < size; i++) componentIsWholeCycle &= takenVertex[cycle.getVertex(i)]; } for (int i = 0; i < size; i++) if (takenVertex[cycle.getVertex(i)]) need_to_insert[i] = false; if (componentIsWholeCycle) break; int startIndex = index; int endIndex = index; while (takenVertex[cycle.getVertex(startIndex)]) startIndex = (startIndex - 1 + size) % size; startIndex = (startIndex + 1) % size; while (takenVertex[cycle.getVertex(endIndex)]) endIndex = (endIndex + 1) % size; // 2. flip bool need_to_flip = false; float rotate1 = Vec2f::cross(layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex), layout.getPos((startIndex + 2) % size) - layout.getPos((startIndex + 1) % size)); if (isEdgeDrawn(cycle.getEdgeC(startIndex + 1))) { float rotate2 = Vec2f::cross(getPos(cycle.getVertexC(startIndex + 1)) - getPos(cycle.getVertexC(startIndex)), getPos(cycle.getVertexC(startIndex + 2)) - getPos(cycle.getVertexC(startIndex + 1))); if (isEdgeDrawn(cycle.getEdgeC(startIndex))) need_to_flip = rotate1 * rotate2 < 0; } else { float rotate1_next = rotate1; float rotate1_prev = Vec2f::cross(layout.getPos(startIndex) - layout.getPos((startIndex - 1 + size) % size), layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex)); int do_flip_cnt = 0; int dont_flip_cnt = 0; int ind0 = cycle.getVertexC(startIndex); int ind1 = cycle.getVertexC(startIndex + 1); const Vertex& v0 = getVertex(ind0); const Vertex& v1 = getVertex(ind1); for (int j = v0.neiBegin(); j != v0.neiEnd(); j = v0.neiNext(j)) if (v0.neiVertex(j) != ind1 && isEdgeDrawn(v0.neiEdge(j))) { float current_rotate = Vec2f::cross(getPos(ind0) - getPos(v0.neiVertex(j)), getPos(ind1) - getPos(ind0)); if (current_rotate * rotate1_prev > 0) do_flip_cnt++; else dont_flip_cnt++; } for (int j = v1.neiBegin(); j != v1.neiEnd(); j = v1.neiNext(j)) if (v1.neiVertex(j) != ind0 && isEdgeDrawn(v1.neiEdge(j))) { float current_rotate = Vec2f::cross(getPos(ind1) - getPos(ind0), getPos(v1.neiVertex(j)) - getPos(ind1)); if (current_rotate * rotate1_next > 0) do_flip_cnt++; else dont_flip_cnt++; } need_to_flip = do_flip_cnt > dont_flip_cnt; } /*float rotate1 = Vec2f::cross(layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex), layout.getPos((startIndex + 2) % size) - layout.getPos((startIndex + 1) % size)); Vec2f next_point; if (isEdgeDrawn(cycle.getEdgeC(startIndex + 1))) next_point = getPos(cycle.getVertexC(startIndex + 2)); else { for (int j = getVertex(cycle.getVertexC(startIndex + 1)).neiBegin(); j != getVertex(cycle.getVertexC(startIndex + 1)).neiEnd(); j = getVertex(cycle.getVertexC(startIndex + 1)).neiNext(j)) if (isEdgeDrawn(getVertex(cycle.getVertexC(startIndex + 1)).neiEdge(j)) && getVertex(cycle.getVertexC(startIndex + 1)).neiVertex(j) != cycle.getVertex(startIndex)) next_point = _layout_vertices[getVertex(cycle.getVertexC(startIndex + 1)).neiVertex(j)].pos; } float rotate2 = Vec2f::cross(getPos(cycle.getVertexC(startIndex + 1)) - getPos(cycle.getVertexC(startIndex)), next_point - getPos(cycle.getVertexC(startIndex + 1))); if (!isEdgeDrawn(cycle.getEdgeC(startIndex + 1))) { need_to_flip = rotate1 * rotate2 > 0; } else if (isEdgeDrawn(cycle.getEdgeC(startIndex))) need_to_flip = rotate1 * rotate2 < 0; */ if (need_to_flip) { for (int i = 0; i < insideVertex.size(); i++) getPos(insideVertex[i]).x *= -1; for (int i = 0; i < segment.size(); i++) if (segment[i].get_layout_component_number() >= 0 && _is_component_touch[segment[i].get_layout_component_number()]) segment[i].inverse(); } // 3. shift Vec2f middle_host; Vec2f middle_new; int countVertex = 0; for (int i = startIndex; i != endIndex; i = (i + 1) % size) { middle_host += _layout_vertices[cycle.getVertex(i)].pos; middle_new += layout.getPos(i); countVertex++; } middle_host /= countVertex; middle_new /= countVertex; for (int i = 0; i < insideVertex.size(); i++) _layout_vertices[insideVertex[i]].pos += middle_new - middle_host; // 4. rotate Vec2f direction_host; Vec2f direction_new; if (countVertex > 1) { int currentIndex = 0; for (int i = startIndex; i != endIndex; i = (i + 1) % size) { if (2 * currentIndex < countVertex - 1) { direction_host += _layout_vertices[cycle.getVertex(i)].pos; direction_new += layout.getPos(i); } else if (2 * currentIndex > countVertex - 1) { direction_host -= _layout_vertices[cycle.getVertex(i)].pos; direction_new -= layout.getPos(i); } currentIndex++; } float dot = Vec2f::dot(direction_host, direction_new) / (direction_host.length()*direction_new.length()); if (dot > 1) dot = 1; if (dot < -1) dot = -1; float angle = acos(dot); if (Vec2f::cross(direction_host, direction_new) < 0) angle = -angle; for (int i = 0; i < insideVertex.size(); i++) _layout_vertices[insideVertex[i]].pos.rotateAroundSegmentEnd(_layout_vertices[insideVertex[i]].pos, middle_new, angle); } } for (int i = 0; i < size; i++) if (getVertexType(cycle.getVertex(i)) == ELEMENT_NOT_DRAWN) getPos(cycle.getVertex(i)) = layout.getPos(i); for (int i = 0; i < size; i++) { setVertexType(cycle.getVertex(i), ELEMENT_DRAWN); setEdgeType(cycle.getEdge(i), ELEMENT_DRAWN); } // 5. smoothing for (int e = edgeBegin(); e != edgeEnd(); e = edgeNext(e)) if (_layout_component_number[e] >= 0 && _is_layout_component_incoming[_layout_component_number[e]]) _layout_component_number[e] = _layout_component_count - 1; _segment_smoothing(cycle, layout, rotation_vertex, rotation_point, segment); } void MoleculeLayoutGraphSmart::_segment_smoothing(const Cycle &cycle, const MoleculeLayoutMacrocyclesLattice &layout, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { QS_DEF(Array<float>, target_angle); _segment_update_rotation_points(cycle, rotation_vertex, rotation_point, segment); _segment_calculate_target_angle(layout, rotation_vertex, target_angle, segment); if (segment.size() > 2) { _segment_smoothing_unstick(segment); // _do_segment_smoothing(rotation_point, target_angle, segment); _do_segment_smoothing_gradient(rotation_point, target_angle, segment); } } void MoleculeLayoutGraphSmart::_segment_update_rotation_points(const Cycle &cycle, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { for (int i = 0; i < rotation_vertex.size(); i++) rotation_point[i] = getPos(cycle.getVertex(rotation_vertex[i])); for (int i = 0; i < segment.size(); i++) segment[i].updateStartFinish(); } void MoleculeLayoutGraphSmart::_segment_calculate_target_angle(const MoleculeLayoutMacrocyclesLattice &layout, Array<int> &rotation_vertex, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { int segments_count = rotation_vertex.size(); target_angle.clear_resize(segments_count); for (int i = 0; i < segments_count; i++) { Vec2f p1 = layout.getPos(rotation_vertex[(i - 1 + segments_count) % segments_count]); Vec2f p2 = layout.getPos(rotation_vertex[i]); Vec2f p3 = layout.getPos(rotation_vertex[(i + 1) % segments_count]); target_angle[i] = p2.calc_angle(p3, p1); while (target_angle[i] < 0) target_angle[i] += 2 * PI; } for (int i = 0; i < segments_count; i++) for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) { if (segment[i].is_start(v)) if (segment[i]._graph.getVertex(v).degree() > 2) target_angle[i] = PI; if (segment[i].is_finish(v)) if (segment[i]._graph.getVertex(v).degree() > 2) target_angle[(i + 1) % segments_count] = PI; } } void MoleculeLayoutGraphSmart::_segment_smoothing_unstick(ObjArray<MoleculeLayoutSmoothingSegment> &segment) { int segment_count = segment.size(); // prepearing of list of sticked pairs of vertices QS_DEF(Array<float>, min_x); min_x.clear_resize(segment_count); for (int i = 0; i < segment_count; i++) min_x[i] = segment[i].get_min_x(); QS_DEF(Array<float>, max_x); max_x.clear_resize(segment_count); for (int i = 0; i < segment_count; i++) max_x[i] = segment[i].get_max_x(); QS_DEF(Array<float>, min_y); min_y.clear_resize(segment_count); for (int i = 0; i < segment_count; i++) min_y[i] = segment[i].get_min_y(); QS_DEF(Array<float>, max_y); max_y.clear_resize(segment_count); for (int i = 0; i < segment_count; i++) max_y[i] = segment[i].get_max_y(); QS_DEF(Array<int>, component1); QS_DEF(Array<int>, component2); QS_DEF(Array<int>, vertex1); QS_DEF(Array<int>, vertex2); component1.clear_resize(0); component2.clear_resize(0); vertex1.clear_resize(0); vertex2.clear_resize(0); for (int i = 0; i < segment_count; i++) for (int j = (i + segment_count/2) % segment_count; j != i; j = (j + segment_count - 1) % segment_count) { if (segment_count % 2 == 0 && j + segment_count/2 == i) continue; if (min_x[i] <= max_x[j] && min_x[j] <= max_x[i] && min_y[i] <= max_y[j] && min_y[j] <= max_y[i]) { for (int v1 = segment[i]._graph.vertexBegin(); v1 != segment[i]._graph.vertexEnd(); v1 = segment[i]._graph.vertexNext(v1)) for (int v2 = segment[j]._graph.vertexBegin(); v2 != segment[j]._graph.vertexEnd(); v2 = segment[j]._graph.vertexNext(v2)) { // if ((i + 1) % segment_count != j) printf("%10.10f \n", Vec2f::dist(segment[i].getPosition(v1), segment[j].getPosition(v2))); if (Vec2f::distSqr(segment[i].getPosition(v1), segment[j].getPosition(v2)) < 0.1) if ((i + 1) % segment_count != j || !segment[i].is_finish(v1)) { component1.push(i); component2.push(j); vertex1.push(v1); vertex2.push(v2); } } } } int count_sticked_vertices = component1.size(); bool something_done = true; bool something_to_do = false; while (something_done) { something_done = false; something_to_do = false; for (int index = 0; index < count_sticked_vertices; index++) { int i = component1[index]; int j = component2[index]; int v1 = vertex1[index]; int v2 = vertex2[index]; if (Vec2f::distSqr(segment[i].getPosition(v1), segment[j].getPosition(v2)) < EPSILON) { something_to_do = true; bool exist_sepatate_vertex = false; const Vertex &vert1 = segment[i]._graph.getVertex(v1); const Vertex &vert2 = segment[j]._graph.getVertex(v2); for (int u1 = vert1.neiBegin(); u1 != vert1.neiEnd() && !exist_sepatate_vertex; u1 = vert1.neiNext(u1)) { bool exist_same_vertex = false; int nei1 = vert1.neiVertex(u1); for (int u2 = vert2.neiBegin(); u2 != vert2.neiEnd() && !exist_same_vertex; u2 = vert2.neiNext(u2)) { int nei2 = vert2.neiVertex(u2); if (Vec2f::dist(segment[i].getPosition(nei1), segment[j].getPosition(nei2)) < EPSILON) exist_same_vertex = true; } if (!exist_same_vertex) exist_sepatate_vertex = true; } if (exist_sepatate_vertex) { Vec2f direction; if (vert1.degree() == 2) { direction = (segment[i].getPosition(vert1.neiVertex(vert1.neiBegin())) + segment[i].getPosition(vert1.neiVertex(vert1.neiNext(vert1.neiBegin()))))/2; direction -= segment[i].getPosition(v1); } else if (vert2.degree() == 2) { direction = (segment[j].getPosition(vert2.neiVertex(vert2.neiBegin())) + segment[j].getPosition(vert2.neiVertex(vert2.neiNext(vert2.neiBegin()))))/2; direction -= segment[i].getPosition(v1); } else if (vert1.degree() == 1) { direction = segment[i].getPosition(vert1.neiVertex(vert1.neiBegin())); direction -= segment[i].getPosition(v1); direction.rotate(1, 0); } else if (vert2.degree() == 1) { direction = segment[j].getPosition(vert2.neiVertex(vert2.neiBegin())); direction -= segment[i].getPosition(v1); direction.rotate(1, 0); } else continue; direction /= 3; bool moved = false; for (int sign = 1; sign >= -1 && !moved; sign -= 2) { Vec2f newpos = segment[i].getPosition(v1) + (direction * sign); bool can_to_move = true; for (int u1 = vert1.neiBegin(); u1 != vert1.neiEnd() && can_to_move; u1 = vert1.neiNext(u1)) { int nei1 = vert1.neiVertex(u1); for (int u2 = vert2.neiBegin(); u2 != vert2.neiEnd() && can_to_move; u2 = vert2.neiNext(u2)){ int nei2 = vert2.neiVertex(u2); if (Vec2f::segmentsIntersectInternal(newpos, segment[i].getPosition(nei1), segment[j].getPosition(v2), segment[j].getPosition(nei2))) can_to_move = false; } } if (can_to_move) { something_done = true; moved = true; segment[i].shiftStartBy(direction * sign / 2); segment[i].shiftFinishBy(direction * sign / 2); segment[j].shiftStartBy(direction * -sign / 2); segment[j].shiftFinishBy(direction * -sign / 2); } } } } } } for (int i = 0; i < segment_count; i++) for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) getPos(segment[i]._graph.getVertexExtIdx(v)).copy(segment[i].getPosition(v)); } void MoleculeLayoutGraphSmart::_update_touching_segments(Array<local_pair_ii >& pairs, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { int segments_count = segment.size(); float min_dist = 0.7; pairs.clear(); for (int i = 0; i < segments_count; i++) for (int j = i + 2; j < segments_count; j++) if (i != 0 || j != segments_count - 1){ if (segment[i].get_layout_component_number() >= 0 || segment[j].get_layout_component_number() >= 0) continue; bool interseced = false; for (int v1 = segment[j]._graph.vertexBegin(); v1 != segment[j]._graph.vertexEnd() && !interseced; v1 = segment[j]._graph.vertexNext(v1)) { for (int v2 = segment[i]._graph.vertexBegin(); v2 != segment[i]._graph.vertexEnd() && !interseced; v2 = segment[i]._graph.vertexNext(v2)) { if (Vec2f::distSqr(segment[j].getPosition(v1), segment[i].getPosition(v2)) < min_dist * min_dist) interseced = true; } } if (interseced) { pairs.push(local_pair_ii(i, j)); pairs.push(local_pair_ii(j, i)); } } } void MoleculeLayoutGraphSmart::_do_segment_smoothing(Array<Vec2f> &rotation_point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { //profTimerStart(t, "_do_segment_smoothing"); Random rand(34577); int segments_count = segment.size(); QS_DEF(Array< local_pair_ii >, touching_segments); for (int i = 0; i < 10000; i++) { if ((i & (i - 1)) == 0) _update_touching_segments(touching_segments, segment); if (i % 100 == 0 && touching_segments.size() == 0) { bool all_right = true; for (int j = 0; all_right && j < segments_count; j++) all_right &= abs(target_angle[j] - rotation_point[j].calc_angle(rotation_point[(j + 1) % segments_count], rotation_point[(j + segments_count - 1) % segments_count])) < 1e-3; if (all_right) break; } _segment_improoving(rotation_point, target_angle, segment, rand.next() % segments_count, 0.1, touching_segments); } for (int i = 0; i < segments_count; i++) for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) getPos(segment[i]._graph.getVertexExtIdx(v)).copy(segment[i].getPosition(v)); } void MoleculeLayoutGraphSmart::_segment_smoothing_prepearing(const Cycle &cycle, Array<int> &rotation_vertex, Array<Vec2f> &rotation_point, ObjArray<MoleculeLayoutSmoothingSegment> &segment, MoleculeLayoutMacrocyclesLattice& layout) { int cycle_size = cycle.vertexCount(); QS_DEF(Array<bool>, layout_comp_touch); layout_comp_touch.clear_resize(_layout_component_count); layout_comp_touch.zerofill(); for (int i = 0; i < cycle_size; i++) { if (_layout_component_number[cycle.getEdge(i)] >= 0) layout_comp_touch[_layout_component_number[cycle.getEdge(i)]] = true; } QS_DEF(ObjArray<Filter>, segments_filter); segments_filter.clear(); QS_DEF(Array<int>, segment_start); segment_start.clear_resize(0); segment_start.fffill(); QS_DEF(Array<bool>, touch_to_current_component); touch_to_current_component.clear_resize(cycle_size); QS_DEF(Array<int>, segment_component_number); segment_component_number.clear(); segment.clear(); for (int i = 0; i < _layout_component_count; i++) if (layout_comp_touch[i]) { // search of vertices touch to i-th layout component touch_to_current_component.zerofill(); for (int j = 0; j < cycle_size; j++) { const Vertex &vert = getVertex(cycle.getVertex(j)); for (int nei = vert.neiBegin(); nei != vert.neiEnd(); nei = vert.neiNext(nei)) if (getEdgeType(vert.neiEdge(nei)) != ELEMENT_NOT_DRAWN) { if (_layout_component_number[vert.neiEdge(nei)] == i) touch_to_current_component[j] = true; } } // search of start and finish of occupated segment of cycle // if there is at least two starts of finishes then it is separationg layout component int start = -1; int finish = -1; for (int j = 0; j < cycle_size; j++) if (touch_to_current_component[j] && _layout_component_number[cycle.getEdgeC(j - 1)] != i) { if (start != -1) throw Exception("Separating layout component in cycle\n"); else start = j; } for (int j = 0; j < cycle_size; j++) if (touch_to_current_component[j] && _layout_component_number[cycle.getEdge(j)] != i) { if (finish != -1) throw Exception("Separating layout component in cycle\n"); else finish = j; } if (start != finish) { segments_filter.push(); segments_filter.top().initNone(vertexEnd()); for (int e = edgeBegin(); e != edgeEnd(); e = edgeNext(e)) if (_layout_component_number[e] == i) { segments_filter.top().unhide(getEdge(e).beg); segments_filter.top().unhide(getEdge(e).end); } segment_start.push(start); segment_component_number.push(i); } } for (int i = 0; i < cycle_size; i++) if (_layout_component_number[cycle.getEdge(i)] < 0) { int i_1 = (i + cycle_size - 1) % cycle_size; // i - 1 if (_layout_component_number[cycle.getEdge(i_1)] < 0 && !layout.getVertexStereo(i)) continue; int last = i; while (_layout_component_number[cycle.getEdgeC(last + 1)] < 0 && !layout.getVertexStereo((last + 1) % cycle_size)) last = (last + 1) % cycle_size; segment_start.push(i); segments_filter.push(); segments_filter.top().initNone(vertexEnd()); for (int v = i; v != (last + 2) % cycle_size; v = (v + 1) % cycle_size) segments_filter.top().unhide(cycle.getVertex(v)); //segments_filter.top().unhide(cycle.getVertex(i)); //segments_filter.top().unhide(cycle.getVertexC(i + 1)); segment_component_number.push(-1); } int segments_count = segments_filter.size(); if (segments_count == 0) return; QS_DEF(Array<int>, number_of_segment); number_of_segment.clear_resize(cycle_size); number_of_segment.fffill(); for (int i = 0; i < segments_count; i++) number_of_segment[segment_start[i]] = i; rotation_vertex.clear_resize(0); for (int i = 0; i < cycle_size; i++) if (number_of_segment[i] != -1) rotation_vertex.push(segment_start[number_of_segment[i]]); rotation_point.clear_resize(segments_count); _segment_update_rotation_points(cycle, rotation_vertex, rotation_point, segment); QS_DEF(ObjArray<MoleculeLayoutGraphSmart>, segment_graph); segment_graph.clear(); for (int i = 0; i < segments_count; i++) { segment_graph.push().makeLayoutSubgraph(*this, segments_filter[i]); } int segment_count = segment_graph.size(); int current_number = 0; for (int i = 0; i < cycle_size; i++) if (number_of_segment[i] != -1) { segment.push(segment_graph[number_of_segment[i]], rotation_point[current_number], rotation_point[(1 + current_number) % segments_count]); segment.top().set_layout_component_number(segment_component_number[number_of_segment[i]]); segment.top().set_start_finish_number(cycle.getVertex(rotation_vertex[current_number]), cycle.getVertex(rotation_vertex[(current_number + 1) % segments_count])); current_number++; } } void MoleculeLayoutGraphSmart::_segment_improoving(Array<Vec2f> &point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment, int move_vertex, float coef, Array<local_pair_ii>& touching_segments) { int segments_count = segment.size(); Vec2f move_vector(0, 0); // fix intersections to other components for (int i = 0; i < touching_segments.size(); i++) if (touching_segments[i].left == move_vertex || touching_segments[i].left == (move_vertex + 1) % segments_count) { int another_segment = touching_segments[i].right; float min_dist = 0.7; //float dist2 = min_dist; bool interseced = false; for (int v1 = segment[move_vertex]._graph.vertexBegin(); !interseced && v1 != segment[move_vertex]._graph.vertexEnd(); v1 = segment[move_vertex]._graph.vertexNext(v1)) { for (int v2 = segment[another_segment]._graph.vertexBegin(); !interseced && v2 != segment[another_segment]._graph.vertexEnd(); v2 = segment[another_segment]._graph.vertexNext(v2)) { if ((segment[move_vertex].getPosition(v1) - segment[another_segment].getPosition(v2)).lengthSqr() < min_dist * min_dist) interseced = true; //dist2 = min(dist2, (segment[move_vertex].getPosition(v1) - segment[another_segment].getPosition(v2)).lengthSqr()); } } //dist2 = max(dist2, 0.25f); if (interseced) { Vec2f shift1(segment[move_vertex].getCenter()); Vec2f shift2(segment[touching_segments[i].right].getCenter()); Vec2f shift(shift1 - shift2); shift.normalize(); move_vector += shift; } } // fix angle Vec2f prev_point(point[(move_vertex + segments_count - 1) % segments_count]); Vec2f this_point(point[move_vertex]); Vec2f next_point(point[(move_vertex + 1) % segments_count]); if (abs(target_angle[move_vertex] - PI) > 0.01) { Vec2f chord(next_point - prev_point); Vec2f center(prev_point + chord/2); Vec2f rot_chord(chord); rot_chord.rotate(1, 0); center += rot_chord / tan(PI - target_angle[move_vertex]) / 2; float radii = (prev_point - center).length(); float dist = (this_point - center).length(); move_vector += (this_point - center) * (radii - dist)/radii; //move_vector += get_move_vector(this_point, center, radii); } else { double l1 = segment[(move_vertex + segments_count - 1) % segments_count].getLength(); double l2 = segment[move_vertex].getLength(); Vec2f center(prev_point * l2 + next_point * l1); center /= l1 + l2; Vec2f chord(next_point - prev_point); chord.rotate(1, 0); center += chord * (target_angle[move_vertex] - PI) * l1 * l2 / (l1 + l2); move_vector += (center - this_point); } // fix distance to neighborhoods move_vector += (this_point - next_point) * segment[move_vertex].getLengthCoef(); move_vector += (this_point - prev_point) * segment[(move_vertex + segments_count - 1) % segments_count].getLengthCoef(); // move_vector += get_move_vector(this_point, prev_point, segment[(move_vertex + segments_count - 1) % segments_count].getLength()); // move_vector += get_move_vector(this_point, next_point, segment[move_vertex].getLength()); // apply point[move_vertex] += move_vector * coef; } void MoleculeLayoutGraphSmart::_do_segment_smoothing_gradient(Array<Vec2f> &rotation_point, Array<float> &target_angle, ObjArray<MoleculeLayoutSmoothingSegment> &segment) { SmoothingCycle cycle(rotation_point, target_angle, segment); cycle._do_smoothing(100); for (int i = 0; i < cycle.cycle_length; i++) for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v)) getPos(segment[i]._graph.getVertexExtIdx(v)).copy(segment[i].getPosition(v)); } CP_DEF(SmoothingCycle); SmoothingCycle::SmoothingCycle(Array<Vec2f>& p, Array<float>& t_a) : CP_INIT, point(p), target_angle(t_a), segment(0), cycle_length(-1), TL_CP_GET(edge_length) { } SmoothingCycle::SmoothingCycle(Array<Vec2f>& p, Array<float>& t_a, Array<int>& e_l, int l) : SmoothingCycle(p, t_a) { cycle_length = l; edge_length.clear_resize(cycle_length); for (int i = 0; i < cycle_length; i++) edge_length[i] = e_l[i]; } SmoothingCycle::SmoothingCycle(Array<Vec2f>& p, Array<float>& t_a, ObjArray<MoleculeLayoutSmoothingSegment>& s) : SmoothingCycle(p, t_a) { segment = &s[0]; cycle_length = s.size(); edge_length.clear_resize(cycle_length); for (int i = 0; i < cycle_length; i++) edge_length[i] = s[i].getLength(); } void SmoothingCycle::_do_smoothing(int iter_count) { QS_DEF(Array< local_pair_ii >, touching_segments); touching_segments.clear(); float coef = 1.0; float multiplyer = __max(0.5, __min(0.999f, 1 - 10.0 / iter_count)); for (int i = 0; i < 100; i++, coef *= 0.9) { _gradient_step(coef, touching_segments); } } void SmoothingCycle::_gradient_step(float coef, Array<local_pair_ii>& touching_segments) { QS_DEF(Array<Vec2f>, change); change.clear_resize(cycle_length); for (int i = 0; i < cycle_length; i++) change[i] = Vec2f(0, 0); float eps = 0.01; for (int i = 0; i < cycle_length; i++) { int i_1 = (i - 1 + cycle_length) % cycle_length; // i - 1 int i1 = (i + 1) % cycle_length; // i + 1 change[i] += _get_len_derivative(point[i1] - point[i], get_length(i)) * (is_simple_component(i) ? 1 : 5); change[i] += _get_len_derivative(point[i_1] - point[i], get_length(i_1)) * (is_simple_component(i_1) ? 1 : 5); if (abs(target_angle[i] - PI) > eps) change[i] += _get_angle_derivative(point[i] - point[i_1], point[i1] - point[i], PI - target_angle[i]); } for (int i = 0; i < cycle_length; i++) for (int j = i + 2; j < cycle_length; j++) if (j - i != cycle_length - 1) if (!is_simple_component(i) && !is_simple_component(j)) { float current_dist = (get_center(i) - get_center(j)).length(); float target_dist = get_radius(i) + get_radius(j) + 1.0; if (current_dist < target_dist) { float importance = 1; Vec2f ch = _get_len_derivative_simple(get_center(i) - get_center(j), target_dist); change[j] += ch / 2 * importance; change[(j + 1) % cycle_length] += ch / 2 * importance; change[i] -= ch / 2 * importance; change[(i + 1) % cycle_length] -= ch / 2 * importance; } } float len = 0; for (int i = 0; i < cycle_length; i++) len += change[i].lengthSqr(); len = sqrt(len); if (len > 1) for (int i = 0; i < cycle_length; i++) change[i] /= len; for (int i = 0; i < cycle_length; i++) point[i] -= change[i] * coef; } Vec2f SmoothingCycle::_get_len_derivative(Vec2f current_vector, float target_dist) { float dist = current_vector.length(); //dist = __max(dist, 0.01); float coef = 1; if (dist >= target_dist) { coef = (dist / target_dist - 1) * 2 / target_dist / dist; } else { coef = -(target_dist / dist - 1) * 2 * target_dist / dist / dist / dist; } return current_vector * -coef; } Vec2f SmoothingCycle::_get_len_derivative_simple(Vec2f current_vector, float target_dist) { float dist = current_vector.length(); //dist = __max(dist, 0.01); float coef = -1; // dist - target_dist; return current_vector * -coef; } Vec2f SmoothingCycle::_get_angle_derivative(Vec2f left_point, Vec2f right_point, float target_angle) { float len1_sq = left_point.lengthSqr(); float len2_sq = right_point.lengthSqr(); float len12 = sqrt(len1_sq * len2_sq); float cross = Vec2f::cross(left_point, right_point); float signcross = cross > 0 ? 1 : cross == 0 ? 0 : -1; float dot = Vec2f::dot(left_point, right_point); float signdot = dot > 0 ? 1 : dot == 0 ? 0 : -1; float cos = dot / len12; float alpha; Vec2f alphadv; if (fabs(cos) < 0.5) { Vec2f cosdv = ((right_point - left_point) * len12 - (left_point * len2_sq - right_point * len1_sq) * dot / len12) / (len1_sq * len2_sq); alpha = acos(cos)* signcross; alphadv = cosdv * (-1. / sqrt(1 - cos * cos)) * signcross; } else { float sin = cross / len12; Vec2f vec = left_point + right_point; vec.rotate(-1, 0); Vec2f sindv = (vec * len12 - (left_point * len2_sq - right_point * len1_sq) * cross / len12) / (len1_sq * len2_sq); alphadv = sindv * (1. / sqrt(1 - sin * sin)) * signdot; alpha = asin(sin); if (cos < 0) { if (alpha > 0) alpha = PI - alpha; else alpha = -PI - alpha; } } //float diff = abs(alpha) > abs(target_angle) ? alpha / target_angle - 1 : target_angle / alpha - 1; //Vec2f result = abs(alpha) > abs(target_angle) ? alphadv / target_angle : alphadv * (- target_angle) / (alpha * alpha); //return result * diff * 2; return alphadv * (alpha - target_angle) * 2; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_attach.cpp�������������������������������������0000664�0000000�0000000�00000103701�12710376503�0024722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_graph.h" using namespace indigo; void MoleculeLayoutGraphSimple::_setChainType (const Array<int> &chain, const Array<int> &mapping, int type) { for (int i = 0; i < chain.size() - 1; i++) { if (i > 0) _layout_vertices[mapping[chain[i]]].type = type; const Vertex &vert = getVertex(mapping[chain[i]]); int edge_idx = vert.neiEdge(vert.findNeiVertex(mapping[chain[i + 1]])); _layout_edges[edge_idx].type = type; } } bool MoleculeLayoutGraphSimple::_splitCycle (const Cycle &cycle, const Array<int> &cycle_vertex_types, bool check_boundary, Array<int> &chain_ext, Array<int> &chain_int, int &c_beg, int &c_end) const { int i, j, k; // 1. First vertex is drawn if (cycle_vertex_types[0] != ELEMENT_NOT_DRAWN) { // 1. Find first not drawn for (i = 0; i < cycle.vertexCount(); i++) if (cycle_vertex_types[i] == ELEMENT_NOT_DRAWN) break; // 2. Find the last not drawn for (j = i; j < cycle.vertexCount(); j++) if (cycle_vertex_types[j] != ELEMENT_NOT_DRAWN) break; j--; // 3. Check other are drawn for (k = j + 1; k < cycle.vertexCount(); k++) if (cycle_vertex_types[k] == ELEMENT_NOT_DRAWN) return false; // 4. Check boundary vertices are marked as boundary if (check_boundary) if (cycle_vertex_types[i - 1] != ELEMENT_BOUNDARY || cycle_vertex_types[(j + 1) % cycle.vertexCount()] != ELEMENT_BOUNDARY) return false; // 5. Make internal and external chains c_beg = cycle.getVertex(i - 1); c_end = cycle.getVertex((j + 1) % cycle.vertexCount()); chain_ext.clear(); chain_int.clear(); for (k = i - 1; k < j + 2; k++) chain_ext.push(cycle.getVertex(k % cycle.vertexCount())); for (k = i - 1; k >= 0; k--) chain_int.push(cycle.getVertex(k)); for (k = cycle.vertexCount() - 1; k > j; k--) chain_int.push(cycle.getVertex(k)); } else // First vertex is not drawn { // 1. Find first vertex after the last not drawn from the beginning for (i = 0; i < cycle.vertexCount(); i++) if (cycle_vertex_types[i] != ELEMENT_NOT_DRAWN) break; // 2. Find first vertex after the last not drawn from the end for (j = cycle.vertexCount() - 1; j >= 0; j--) if (cycle_vertex_types[j] != ELEMENT_NOT_DRAWN) break; // 3. Check other are drawn for (k = i; k < j + 1; k++) if (cycle_vertex_types[k] == ELEMENT_NOT_DRAWN) return false; // 4. Check boundary vertices are marked as boundary if (check_boundary) if (cycle_vertex_types[i] != ELEMENT_BOUNDARY || cycle_vertex_types[j] != ELEMENT_BOUNDARY) return false; // 5. Make internal and external chains c_beg = cycle.getVertex(i); c_end = cycle.getVertex(j); chain_int.clear(); chain_ext.clear(); for (k = i; k < j + 1; k++) chain_int.push(cycle.getVertex(k)); for (k = i; k >= 0; k--) chain_ext.push(cycle.getVertex(k)); for (k = cycle.vertexCount() - 1; k > j - 1; k--) chain_ext.push(cycle.getVertex(k)); } return true; } // Split cycle into separate chains which are not drawn void MoleculeLayoutGraphSimple::_splitCycle2 (const Cycle &cycle, const Array<int> &cycle_vertex_types, ObjArray < Array<int> > &chains_ext) const { int i; chains_ext.clear(); i = 0; while (cycle_vertex_types[i] == ELEMENT_NOT_DRAWN) i++; while (i < cycle.vertexCount()) { for (; i < cycle.vertexCount(); i++) if (cycle_vertex_types[i] == ELEMENT_NOT_DRAWN) break; if (i == cycle.vertexCount()) break; Array<int> &chain_ext = chains_ext.push(); chain_ext.push(cycle.getVertex(i - 1)); for (; i < cycle.vertexCount() && cycle_vertex_types[i] == ELEMENT_NOT_DRAWN; i++) chain_ext.push(cycle.getVertex(i)); if (i < cycle.vertexCount() || cycle_vertex_types[0] != ELEMENT_NOT_DRAWN) chain_ext.push(cycle.getVertex(i % cycle.vertexCount())); } if (cycle_vertex_types[0] == ELEMENT_NOT_DRAWN) { i = cycle.vertexCount() - 1; Array<int> *chain_ext = 0; if (cycle_vertex_types[i] != ELEMENT_NOT_DRAWN) { chain_ext = &chains_ext.push(); chain_ext->push(cycle.getVertex(i)); } else chain_ext = &chains_ext.top(); for (i = 0; cycle_vertex_types[i] == ELEMENT_NOT_DRAWN; i++) chain_ext->push(cycle.getVertex(i)); chain_ext->push(cycle.getVertex(i)); } } // Attach cycle outside component border. Component must have given number of common edges or any (if 0) bool MoleculeLayoutGraphSimple::_attachCycleOutside (const Cycle &cycle, float length, int n_common_edges) { int n_common_e = 0, n_common_v = 0; QS_DEF(Array<int>, cycle_vertex_types); cycle_vertex_types.clear_resize(cycle.vertexCount()); cycle_vertex_types.zerofill(); for (int i = 0; i < cycle.vertexCount(); i++) { cycle_vertex_types[i] = _layout_vertices[cycle.getVertex(i)].type; if (cycle_vertex_types[i] > 0) n_common_v++; if (_layout_edges[cycle.getEdge(i)].type > 0) n_common_e++; } if (n_common_edges > 0 && n_common_e != n_common_edges) return false; // Everything is drawn if (n_common_e == cycle.vertexCount()) return true; // If all vertices are drawn then draw edges without intersections with already drawn // Find new border // If then all edges are drawn return true, else return false if (n_common_v == cycle.vertexCount()) return _drawEdgesWithoutIntersection(cycle, cycle_vertex_types); // Attach cycle of two parts: // chain_int - internal and boundary vertices of component // chain_ext - not drawn vertices and two boundary // If number of common vertices is less than 2 then skip cycle if (n_common_v < 2) return false; QS_DEF(Array<int>, chain_ext); QS_DEF(Array<int>, chain_int); int c_beg, c_end; if (!_splitCycle(cycle, cycle_vertex_types, true, chain_ext, chain_int, c_beg, c_end)) return false; int i, k; bool is_attached = false; QS_DEF(Array<int>, border1v); QS_DEF(Array<int>, border1e); QS_DEF(Array<int>, border2v); QS_DEF(Array<int>, border2e); Vec2f p; // Make Border1, Border2 from component border (borders have two common vertices) _splitBorder(c_beg, c_end, border1v, border1e, border2v, border2e); QS_DEF(MoleculeLayoutGraphSimple, next_bc); QS_DEF(Array<int>, mapping); for (int n_try = 0; n_try < 2 && !is_attached; n_try++) { // Complete regular polygon by chain_ext (on the one side if n_try == 1 and other side if n_try == 2 next_bc.cloneLayoutGraph(*this, &mapping); if (length > 1) { k = chain_ext.size() - 2; float dist = Vec2f::dist(_layout_vertices[c_beg].pos, _layout_vertices[c_end].pos); if (dist > (k + 1) * length) length = 0.2f + dist / (k + 1); } if (n_try == 0) { if (!next_bc._drawRegularCurveEx(chain_ext, c_beg, c_end, length, true, ELEMENT_BOUNDARY, mapping)) return false; } else // (n_try == 1) { if (!next_bc._drawRegularCurveEx(chain_ext, c_beg, c_end, length, false, ELEMENT_BOUNDARY, mapping)) return false; } if (!_checkBadTryChainOutside(chain_ext, next_bc, mapping)) continue; // Check edges from chain_ext intersect previous border other than in the ends if (!_checkBadTryBorderIntersection(chain_ext, next_bc, mapping)) continue; // If Border1 lays inside cycle [chain_ext,border2] than it becomes internal and Border2 becomes boundary // If Border2 lays inside cycle [chain_ext,border1] than it becomes internal and Border1 becomes boundary // In both cases chain_ext becomes external. // Ignore border1 and check if vertices are inside new bound // if no then restore Border1 and ignore Border2 next_bc._setChainType(border1v, mapping, ELEMENT_IGNORE); p.lineCombin2(next_bc._layout_vertices[mapping[border1v[0]]].pos, 0.9f, next_bc._layout_vertices[mapping[border1v[1]]].pos, 0.1f); if (!next_bc._isPointOutside(p)) next_bc._setChainType(border1v, mapping, ELEMENT_INTERNAL); else { next_bc._setChainType(border1v, mapping, ELEMENT_BOUNDARY); next_bc._setChainType(border2v, mapping, ELEMENT_INTERNAL); } // Replace chain_ext by single edge and check if chain_ext is outside (try to draw convex polygon) if (n_try == 0 && chain_ext.size() > 2) { next_bc._setChainType(chain_ext, mapping, ELEMENT_IGNORE); const Vertex &vert = next_bc.getVertex(mapping[c_beg]); int edge_idx; int type = -1; if ((edge_idx = vert.findNeiVertex(mapping[c_end])) != -1) { edge_idx = vert.neiEdge(edge_idx); type = next_bc._layout_edges[edge_idx].type; next_bc._layout_edges[edge_idx].type = ELEMENT_BOUNDARY; } else edge_idx = next_bc.addLayoutEdge(mapping[c_beg], mapping[c_end], -1, ELEMENT_BOUNDARY); if (!next_bc._isPointOutside(next_bc._layout_vertices[mapping[chain_ext[1]]].pos)) continue; next_bc._setChainType(chain_ext, mapping, ELEMENT_BOUNDARY); if (type < 0) next_bc.removeEdge(edge_idx); else next_bc._layout_edges[edge_idx].type = type; } // Check if border1, border2 are outside cycle // (draw cycle outside not inside) if (n_try == 0) { for (i = 1; i < border1v.size() - 1 && !is_attached; i++) if (next_bc._isPointOutsideCycleEx(cycle, next_bc._layout_vertices[mapping[border1v[i]]].pos, mapping)) is_attached = true; for (i = 1; i < border2v.size() - 1 && !is_attached; i++) if (next_bc._isPointOutsideCycleEx(cycle, next_bc._layout_vertices[mapping[border2v[i]]].pos, mapping)) is_attached = true; } else is_attached = true; } // Copy new layout if (is_attached) next_bc.copyLayoutTo(*this, mapping); return is_attached; } // Attach cycle inside component border. // Everything can be attached outside is already attached. bool MoleculeLayoutGraphSimple::_attachCycleInside (const Cycle &cycle, float length) { int n_common_e = 0, n_common_v = 0; int i, j; QS_DEF(Array<int>, cycle_vertex_types); cycle_vertex_types.clear_resize(cycle.vertexCount()); cycle_vertex_types.zerofill(); for (i = 0; i < cycle.vertexCount(); i++) { cycle_vertex_types[i] = _layout_vertices[cycle.getVertex(i)].type; if (cycle_vertex_types[i] > 0) n_common_v++; if (_layout_edges[cycle.getEdge(i)].type > 0) n_common_e++; } // Everything is drawn if (n_common_e == cycle.vertexCount()) return true; bool attached = false; // If all vertices are drawn then draw edges without intersections with already drawn // Find new border // If then all edges are drawn return true, else return false if (n_common_v == cycle.vertexCount()) { attached = true; for (i = 0; i < cycle.vertexCount(); i++) { if (_layout_edges[cycle.getEdge(i)].type == ELEMENT_NOT_DRAWN) { for (j = edgeBegin(); j < edgeEnd(); j = edgeNext(j)) if (_layout_edges[j].type > ELEMENT_NOT_DRAWN) if ((_calcIntersection(i, j) % 10) != 1) { attached = false; break; } if (!attached) continue; _layout_edges[cycle.getEdge(i)].type = ELEMENT_INTERNAL; } } return attached; } // Attach cycle of two parts: // chain_int - internal and boundary vertices of component // chain_ext - not drawn vertices and two boundary // If number of common vertices is less than 2 then skip cycle if (n_common_v < 2) return false; QS_DEF(Array<int>, chain_ext); QS_DEF(Array<int>, chain_int); int c_beg, c_end; if (!_splitCycle(cycle, cycle_vertex_types, false, chain_ext, chain_int, c_beg, c_end)) return false; QS_DEF(MoleculeLayoutGraphSimple, next_bc); QS_DEF(Array<int>, mapping); for (int n_try = 0; n_try < 2 && !attached; n_try++) { // Complete regular polygon by chain_ext (on the one side if n_try == 1 and other side if n_try == 2 next_bc.cloneLayoutGraph(*this, &mapping); if (n_try == 0) { if (!next_bc._drawRegularCurveEx(chain_ext, c_beg, c_end, length, true, ELEMENT_INTERNAL, mapping)) return false; } else // (n_try == 1) { if (!next_bc._drawRegularCurveEx(chain_ext, c_beg, c_end, length, false, ELEMENT_INTERNAL, mapping)) return false; } bool bad_try = false; // Check edges from chain_ext intersect previous border other than in the ends for (i = 0; i < chain_ext.size() - 1 && !bad_try; i++) for (j = next_bc.edgeBegin(); j < next_bc.edgeEnd(); j = next_bc.edgeNext(j)) { if (_layout_edges[next_bc._layout_edges[j].ext_idx].type != ELEMENT_NOT_DRAWN) { const Vertex &vert = next_bc.getVertex(mapping[chain_ext[i]]); int edge1_idx = vert.neiEdge(vert.findNeiVertex(mapping[chain_ext[i + 1]])); int intersect = next_bc._calcIntersection(edge1_idx, j); const Edge &edge1 = next_bc.getEdge(edge1_idx); const Edge &edge2 = next_bc.getEdge(j); // Check if edges intersect if ((intersect % 10) != 1 || (intersect == 21 && edge1.beg != edge2.beg && edge1.beg != edge2.end && edge1.end != edge2.beg && edge1.end != edge2.end)) { bad_try = true; break; } } } if (!bad_try) attached = true; } // Copy new layout if (attached) next_bc.copyLayoutTo(*this, mapping); return attached; } // Attach cycle with intersections. // Everything can be attached w/o intersections is already attached. // Not all cycle vertices are drawn bool MoleculeLayoutGraphSimple::_attachCycleWithIntersections (const Cycle &cycle, float length) { int n_common_e = 0, n_common_v = 0; int i, j, k; QS_DEF(Array<int>, cycle_vertex_types); cycle_vertex_types.clear_resize(cycle.vertexCount()); cycle_vertex_types.zerofill(); for (i = 0; i < cycle.vertexCount(); i++) { cycle_vertex_types[i] = _layout_vertices[cycle.getVertex(i)].type; if (cycle_vertex_types[i] > 0) n_common_v++; if (_layout_edges[cycle.getEdge(i)].type > 0) n_common_e++; } // All vertices are drawn - return false if (n_common_v == cycle.vertexCount()) return false; // Attach cycle of two parts: // chain_int - internal and boundary vertices of component // chain_ext - not drawn vertices and two boundary // If number of common vertices is less than 2 then skip cycle if (n_common_v < 2) return false; QS_DEF(ObjArray< Array<int> >, chains_ext); // Split cycle into separate external (not drawn) chains _splitCycle2(cycle, cycle_vertex_types, chains_ext); // Attach each chain separately for (int chain_idx = 0; chain_idx < chains_ext.size(); chain_idx++) { Array<int> &chain_ext = chains_ext[chain_idx]; int c_beg = chain_ext[0], c_end = chain_ext[chain_ext.size() - 1]; float max_length = length * 4; // to avoid infinite values // Complete regular polygon by chain_ext (on the one side if n_try == 1 and other side if n_try == 2 // Mark new vertices and edges as not planar k = chain_ext.size() - 2; float dist = Vec2f::dist(getPos(c_beg), getPos(c_end)); if (dist > (k + 1) * length) { length = 0.2f + dist / (k + 1); max_length = __max(max_length, length * 1.5f); // update max length if needed } bool attached = false; while (!attached && length < max_length) { attached = true; while (!_drawRegularCurve(chain_ext, c_beg, c_end, length, true, ELEMENT_NOT_PLANAR)) length *= 1.2f; // Choose position with minimal energy Vec2f &pos1 = getPos(chain_ext[1]); float s = 0; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].type == ELEMENT_INTERNAL || _layout_vertices[i].type == ELEMENT_BOUNDARY) { Vec2f &pos = getPos(i); s += Vec2f::distSqr(pos, pos1); } _drawRegularCurve(chain_ext, c_beg, c_end, length, false, ELEMENT_NOT_PLANAR); float sn = 0; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { int type = _layout_vertices[i].type; if (type == ELEMENT_INTERNAL || type == ELEMENT_BOUNDARY) { Vec2f &pos = getPos(i); sn += Vec2f::distSqr(pos, pos1); } } if (sn < s - 0.001) _drawRegularCurve(chain_ext, c_beg, c_end, length, true, ELEMENT_NOT_PLANAR); // Try to change edge length to avoid bad layout for (i = 1; i < chain_ext.size() - 1; i++) { if (_isVertexOnSomeEdge(chain_ext[i])) { length *= 1.2f; attached = false; break; } } if (!attached) continue; for (j = 0; j < chain_ext.size() - 1 && attached; j++) { for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { int type = _layout_vertices[i].type; if (i != chain_ext[j] && i != chain_ext[j + 1] && (type == ELEMENT_INTERNAL || type == ELEMENT_BOUNDARY)) { if (_isVertexOnEdge(i, chain_ext[j], chain_ext[j + 1])) { length *= 1.2f; attached = false; break; } } } } } } return true; } // Attach two atoms to the same side of chain void MoleculeLayoutGraph::_attachEars (int vert_idx, int drawn_idx, int *ears, const Vec2f &rest_pos) { Vec2f v1, v2, v3, v4; float phi = 13*PI/24; const Vertex &vert = getVertex(vert_idx); _layout_vertices[ears[0]].type = ELEMENT_IGNORE; _layout_vertices[ears[1]].type = ELEMENT_IGNORE; _layout_edges[vert.neiEdge(vert.findNeiVertex(ears[0]))].type = ELEMENT_BOUNDARY; _layout_edges[vert.neiEdge(vert.findNeiVertex(ears[1]))].type = ELEMENT_BOUNDARY; v1 = getPos(vert_idx); v2 = getPos(drawn_idx); _calculatePos(phi, v1, rest_pos, v3); _calculatePos(phi + 2*PI/3, v1, rest_pos, v4); if (Vec2f::dist(v3, v2) < Vec2f::dist(v4, v2)) v3 = v4; _layout_vertices[ears[0]].pos = v3; _calculatePos(PI/4, v1, v3, getPos(ears[1])); } // Attach set of trivial components void MoleculeLayoutGraph::_attachDandlingVertices (int vert_idx, Array<int> &adjacent_list) { int n_pos = 0, not_drawn_idx = 0, drawn_idx = -1; Vec2f v1, v2; QS_DEF(Array<Vec2f>, positions); int parity = 0; bool two_ears = false; // mark the case with two atoms to be drawn on the same side of chain const Vertex &vert = getVertex(vert_idx); // Calculate number of drawn edges for (int i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { if (getVertexType(vert.neiVertex(i)) != ELEMENT_NOT_DRAWN && getEdgeType(vert.neiEdge(i)) != ELEMENT_NOT_DRAWN) { n_pos++; // amount of drown neibourhoods drawn_idx = i; } else not_drawn_idx = i; } if (n_pos > 1 && adjacent_list.size() == 1) { // n_pos of drawn edges and one not drawn _calculatePositionsOneNotDrawn(positions, n_pos, vert_idx, not_drawn_idx); } else { // Single drawn edge _calculatePositionsSingleDrawn(vert_idx, adjacent_list, n_pos, drawn_idx, two_ears, positions, parity); } int ears[2] = {-1, -1}; if (two_ears) { for (int i = 0; i < adjacent_list.size(); i++) { if (getVertex(adjacent_list[i]).degree() != 1) continue; if (ears[0] == -1) ears[0] = adjacent_list[i]; else ears[1] = adjacent_list[i]; } } // Calculate energy if (parity == 0) _orderByEnergy(positions); // Assign coordinates if (two_ears) { for (int i = 0; i < adjacent_list.size(); i++) { int j = adjacent_list[i]; if (getVertex(j).degree() != 1) { _layout_vertices[j].type = ELEMENT_BOUNDARY; _layout_edges[vert.neiEdge(vert.findNeiVertex(j))].type = ELEMENT_BOUNDARY; _layout_vertices[j].pos = positions[0]; break; } } _attachEars(vert_idx, vert.neiVertex(drawn_idx), ears, positions[0]); return; } int j = 0; while (adjacent_list.size() > 0) { int i = adjacent_list.pop(); _layout_vertices[i].pos = positions[j]; _layout_vertices[i].type = ELEMENT_BOUNDARY; _layout_edges[vert.neiEdge(vert.findNeiVertex(i))].type = ELEMENT_BOUNDARY; j++; } } bool MoleculeLayoutGraphSimple::_drawEdgesWithoutIntersection (const Cycle &cycle, Array<int> & cycle_vertex_types) { bool is_attached = true; Vec2f p; QS_DEF(Array<int>, border1v); QS_DEF(Array<int>, border1e); QS_DEF(Array<int>, border2v); QS_DEF(Array<int>, border2e); for (int i = 0; i < cycle.vertexCount(); i++) { if (_layout_edges[cycle.getEdge(i)].type == ELEMENT_NOT_DRAWN) { for (int j = edgeBegin(); j < edgeEnd(); j = edgeNext(j)) if (_layout_edges[j].type > ELEMENT_NOT_DRAWN) if ((_calcIntersection(i, j) % 10) != 1) { is_attached = false; break; } if (!is_attached) continue; if (cycle_vertex_types[i] == ELEMENT_INTERNAL || cycle_vertex_types[(i + 1) % cycle.vertexCount()] == ELEMENT_INTERNAL) { _layout_edges[cycle.getEdge(i)].type = ELEMENT_INTERNAL; } else { // Both vertices are boundary. // Check if edge is boundary by its center p.lineCombin2(_layout_vertices[cycle.getVertex(i)].pos, 0.9f, _layout_vertices[cycle.getVertexC(i + 1)].pos, 0.1f); if (_isPointOutside(p)) { _splitBorder(cycle.getVertex(i), cycle.getVertexC(i + 1), border1v, border1e, border2v, border2e); _layout_edges[cycle.getEdge(i)].type = ELEMENT_BOUNDARY; // Ignore border1 and check if vertices are inside new bound // if no then restore Border1 and ignore Border2 for (int j = 0; j < border1e.size(); j++) { if (j > 0) _layout_vertices[border1v[j]].type = ELEMENT_IGNORE; _layout_edges[border1e[j]].type = ELEMENT_IGNORE; } p.lineCombin2(_layout_vertices[border1v[1]].pos, 0.9f, _layout_vertices[border1v[2]].pos, 0.1f); if (!_isPointOutside(p)) { for (int j = 0; j < border1e.size(); j++) { if (j > 0) _layout_vertices[border1v[j]].type = ELEMENT_INTERNAL; _layout_edges[border1e[j]].type = ELEMENT_INTERNAL; } } else { for (int j = 0; j < border1e.size(); j++) { if (j > 0) _layout_vertices[border1v[j]].type = ELEMENT_BOUNDARY; _layout_edges[border1e[j]].type = ELEMENT_BOUNDARY; } for (int j = 0; j < border2e.size(); j++) { if (j > 0) _layout_vertices[border2v[j]].type = ELEMENT_INTERNAL; _layout_edges[border2e[j]].type = ELEMENT_INTERNAL; } } } else _layout_edges[cycle.getEdge(i)].type = ELEMENT_INTERNAL; } } } return is_attached; } bool MoleculeLayoutGraph::_checkBadTryBorderIntersection (Array<int> &chain_ext, MoleculeLayoutGraph &next_bc, Array<int> &mapping) { for (int i = 0; i < chain_ext.size() - 1; i++) for (int j = next_bc.edgeBegin(); j < next_bc.edgeEnd(); j = next_bc.edgeNext(j)) { if (_layout_edges[next_bc._layout_edges[j].ext_idx].type == ELEMENT_BOUNDARY) { const Vertex &vert = next_bc.getVertex(mapping[chain_ext[i]]); int edge1_idx = vert.neiEdge(vert.findNeiVertex(mapping[chain_ext[i + 1]])); int intersect = next_bc._calcIntersection(edge1_idx, j); const Edge &edge1 = next_bc.getEdge(edge1_idx); const Edge &edge2 = next_bc.getEdge(j); // Check if edges intersect if ((intersect % 10) != 1 || (intersect == 21 && edge1.beg != edge2.beg && edge1.beg != edge2.end && edge1.end != edge2.beg && edge1.end != edge2.end)) { return false; } } } return true; } bool MoleculeLayoutGraph::_checkBadTryChainOutside (Array<int> &chain_ext, MoleculeLayoutGraph &next_bc, Array<int> & mapping) { // Check chain_ext is outside bound for (int i = 1; i < chain_ext.size() - 1; i++) { if (!_isPointOutside(next_bc._layout_vertices[mapping[chain_ext[i]]].pos)) return false; } return true; } void MoleculeLayoutGraph::_calculatePositionsOneNotDrawn (Array<Vec2f> &positions, int n_pos, int vert_idx, int not_drawn_idx) { positions.clear_resize(n_pos); const Vertex &vert = getVertex(vert_idx); Vec2f v1, v2, p0; float phi; QS_DEF(Array<float>, angles); // polar angles of drawn edges QS_DEF(Array<int>, edges); // edge indices in CCW order angles.clear(); edges.clear(); // find angles for (int i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { if (i == not_drawn_idx) continue; edges.push(i); Vec2f &v1 = getPos(vert.neiVertex(i)); Vec2f &v2 = getPos(vert_idx); p0.diff(v1, v2); if (p0.length() < EPSILON) { // Perturbate coordinate v1.y += 0.001; p0.diff(v1, v2); } angles.push(p0.tiltAngle2()); } // sort for (int i = 0; i < n_pos; i++) for (int j = i + 1; j < n_pos; j++) if (angles[i] > angles[j]) { angles.swap(i, j); edges.swap(i, j); } // place new edge between drawn v1 = getPos(vert_idx); for (int i = 0; i < n_pos - 1; i++) { v2 = getPos(vert.neiVertex(edges[i])); phi = (angles[i + 1] - angles[i]) / 2; _calculatePos(phi, v1, v2, positions[i]); } v2 = getPos(vert.neiVertex(edges.top())); phi = (2 * PI + angles[0] - angles.top()) / 2; _calculatePos(phi, v1, v2, positions.top()); } void MoleculeLayoutGraph::_calculatePositionsSingleDrawn (int vert_idx, Array<int> &adjacent_list, int &n_pos, int drawn_idx, bool &two_ears, Array<Vec2f> &positions, int &parity) { // Split 2pi to n_pos+1 parts // Place vertices like regular polygon // Drawn is first vertex, other in CCW order Vec2f v1, v2; float phi; const Vertex &vert = getVertex(vert_idx); if (adjacent_list.size() > 1) { if (n_pos == 1 && adjacent_list.size() == 3) // to avoid four bonds to be drawn like cross { n_pos = 5; int n_matter = 0, n_matter_2 = 0, n_single = 0, n_double_bond = 0; const Vertex &drawn_vert = getVertex(vert.neiVertex(drawn_idx)); if (drawn_vert.degree() > 2) n_matter_2++; else if (drawn_vert.degree() == 1) n_single++; if (_molecule != 0) { int type = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[vert.neiEdge(drawn_idx)].ext_idx]); if (type == BOND_DOUBLE) n_double_bond++; } for (int i = 0; i < adjacent_list.size(); i++) { int adj_degree = getVertex(adjacent_list[i]).degree(); if (adj_degree == 1) n_single++; else n_matter++; if (adj_degree > 2) n_matter_2++; if (_molecule != 0) { int nei_idx = vert.findNeiVertex(adjacent_list[i]); int type = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[vert.neiEdge(nei_idx)].ext_idx]); if (type == BOND_DOUBLE) n_double_bond++; } } if (n_matter == 1 && n_double_bond < 2) // draw ears { two_ears = true; n_pos = 2; } else if (n_matter_2 > 1 || n_double_bond > 1 || n_single == 4) // cross-like case n_pos = 3; } else n_pos = adjacent_list.size(); } else { int type1 = 0, type2 = 0; if (_molecule != 0) { int first_nei = vert.neiBegin(); type1 = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[vert.neiEdge(first_nei)].ext_idx]); type2 = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[vert.neiEdge(vert.neiNext(first_nei))].ext_idx]); } if (n_pos != 1 || (!(type1 == BOND_TRIPLE || type2 == BOND_TRIPLE) && !(type1 == BOND_DOUBLE && type2 == BOND_DOUBLE))) n_pos = 2; } positions.clear_resize(n_pos); phi = 2 * PI / (n_pos + 1); v1 = getPos(vert_idx); v2 = getPos(vert.neiVertex(drawn_idx)); _calculatePos(phi, v1, v2, positions[0]); for (int i = 1; i < n_pos; i++) { v2 = positions[i - 1]; _calculatePos(phi, v1, v2, positions[i]); } // Check cis/trans if (_molecule != 0 && n_pos == 2) { parity = _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[vert.neiEdge(drawn_idx)].ext_idx]); if (parity != 0) { int substituents[4]; _molecule->cis_trans.getSubstituents_All(_molecule_edge_mapping[_layout_edges[vert.neiEdge(drawn_idx)].ext_idx], substituents); int to_draw_substituent = -1; for (int i = 0; i < 4; i++) if (substituents[i] == _layout_vertices[adjacent_list.top()].ext_idx) { to_draw_substituent = i; break; } const Vertex &drawn_vert = getVertex(vert.neiVertex(drawn_idx)); int drawn_substituent = -1; int drawn_substituent_idx = -1; for (int i = drawn_vert.neiBegin(); i < drawn_vert.neiEnd(); i = drawn_vert.neiNext(i)) if (drawn_vert.neiVertex(i) != vert_idx) // must be drawn { for (int j = 0; j < 4; j++) if (substituents[j] == _layout_vertices[drawn_vert.neiVertex(i)].ext_idx) { drawn_substituent_idx = drawn_vert.neiVertex(i); drawn_substituent = j; break; } break; } bool same_side = false; if ((parity == MoleculeCisTrans::CIS) == (abs(to_draw_substituent - drawn_substituent) == 2)) same_side = true; int side_sign = MoleculeCisTrans::sameside(Vec3f(_layout_vertices[vert.neiVertex(drawn_idx)].pos), Vec3f(_layout_vertices[vert_idx].pos), Vec3f(_layout_vertices[drawn_substituent_idx].pos), Vec3f(positions[0])); if (same_side) { if (side_sign == -1) positions.swap(0, 1); } else if (side_sign == 1) positions.swap(0, 1); } } } void MoleculeLayoutGraph::_orderByEnergy (Array<Vec2f> &positions) { QS_DEF(Array<double>, energies); QS_DEF(Array<double>, norm_a); double norm = 0.0; float r = 0.f; Vec2f p0; int n_pos = positions.size(); energies.clear_resize(n_pos); norm_a.clear_resize(vertexEnd()); energies.zerofill(); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (getVertexType(i) != ELEMENT_NOT_DRAWN && getVertexType(i) != ELEMENT_IGNORE) { norm_a[i] = _layout_vertices[i].morgan_code; norm += norm_a[i] * norm_a[i]; } norm = sqrt(norm); for (int i = 0; i < n_pos; i++) { for (int j = vertexBegin(); j < vertexEnd(); j = vertexNext(j)) if (getVertexType(j) != ELEMENT_NOT_DRAWN && getVertexType(j) != ELEMENT_IGNORE) { p0.diff(positions[i], getPos(j)); r = p0.lengthSqr(); if (r < EPSILON) { energies[i] = 1E+20f; continue; } energies[i] += ((norm_a[j] / norm + 0.5) / r); } } // Sort by energies for (int i = 0; i < n_pos; i++) for (int j = i + 1; j < n_pos; j++) if (energies[j] < energies[i]) { energies.swap(i, j); positions.swap(i, j); } } ���������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_border.cpp�������������������������������������0000664�0000000�0000000�00000044206�12710376503�0024737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_graph.h" using namespace indigo; // Return: // 0 - no intersection // 1 - intersection // -1 - unknown. Ray is too near to the one of the points static int _isRayIntersectWithCheck (float a, float b, const Vec2f &p, const Vec2f &v1, const Vec2f &v2, bool check_precision) { // Ray x=at+p.x, y=bt+p.y, t>=0 and segment [V1,V2]; float a11, a12, a21, a22, b1, b2; float delta, delta1, delta2, t, s, a0, b0, pr; const float eps = 0.0001f; a11 = a; a12 = v1.x - v2.x; b1 = v1.x - p.x; a21 = b; a22 = v1.y - v2.y; b2 = v1.y - p.y; delta = a11 * a22 - a12 * a21; delta2 = a11 * b2 - a21 * b1; delta1 = b1 * a22 - b2 * a12; if (fabs(delta) < eps) { if (fabs(b1 * a21 - b2 * a11) > eps) return 0; if (fabs(a11) > eps) { a0 = b1 / a11; b0 = (b1 - a12) / a11; if (b0 < a0) { pr = a0; a0 = b0; b0 = pr; } } else { a0 = b2 / a21; b0 = (b2 - a22) / a21; if (b0 < a0) { pr = a0; a0 = b0; b0 = pr; } } if (check_precision) if (fabs(a0) < eps && fabs(b0) <= eps) return -1; if (a0 <= -eps && b0 <= -eps) return 0; return 1; } t = delta1 / delta; s = delta2 / delta; if (check_precision) if (fabs(s) < eps || fabs(s - 1) < eps) return -1; if (t < -eps || s < -eps || s > 1 + eps) return 0; return 1; } static bool _isRayIntersect (float a, float b, const Vec2f &p, const Vec2f &v1, const Vec2f &v2) { return _isRayIntersectWithCheck(a, b, p, v1, v2, false) == 1; } // Check if point is outside biconnected component // By calculating number of intersections of ray bool MoleculeLayoutGraphSimple::_isPointOutside (const Vec2f &p) const { int i, count = 0; float a, b; const float eps = 0.01f; bool success = false; while (!success) { success = true; a = (float)rand(); b = (float)rand(); a = 2.f * (a / RAND_MAX - 0.5f); b = 2.f * (b / RAND_MAX - 0.5f); if (fabs(a) < eps || fabs(b) < eps) { success = false; continue; } if (_outline.get() == 0) { for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { const Vec2f &pos = getPos(i); if (_layout_vertices[i].type == ELEMENT_BOUNDARY && fabs((pos.x - p.x) / a - (pos.y - p.y) / b) < 0.1f) { count++; if (count > 100) return false; success = false; break; } } } else { const Array<Vec2f> &outline = *_outline.get(); for (i = 0; i < outline.size(); i++) { if (fabs((outline[i].x - p.x) / a - (outline[i].y - p.y) / b) < 0.1f) { count++; if (count > 100) return false; success = false; break; } } } } // Calculate count = 0; if (_outline.get() == 0) { for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { if (_layout_edges[i].type == ELEMENT_BOUNDARY) { const Edge &edge = getEdge(i); if (_isRayIntersect(a, b, p, getPos(edge.beg), getPos(edge.end))) count++; } } } else { const Array<Vec2f> &outline = *_outline.get(); for (i = 0; i < outline.size(); i++) if (_isRayIntersect(a, b, p, outline[i], outline[(i + 1) % outline.size()])) count++; } if (count & 1) return false; return true; } // Check if point is outside cycle // By calculating number of intersections of ray bool MoleculeLayoutGraphSimple::_isPointOutsideCycle (const Cycle &cycle, const Vec2f &p) const { int i, count = 0; float a, b; Vec2f v1, v2; const float eps = 0.01f; bool success = false; while (!success) { success = true; a = (float)rand(); b = (float)rand(); a = 2.f * (a / RAND_MAX - 0.5f); b = 2.f * (b / RAND_MAX - 0.5f); if (fabs(a) < eps || fabs(b) < eps) { success = false; continue; } for (i = 0; i < cycle.vertexCount(); i++) { const Vec2f &pos = getPos(cycle.getVertex(i)); if (fabs((pos.x - p.x) / a - (pos.y - p.y) / b) < EPSILON) { count++; if (count > 50) return false; success = false; break; } } } // Calculate count = 0; for (i = 0; i < cycle.vertexCount(); i++) if (_isRayIntersect(a, b, p, getPos(cycle.getVertex(i)), getPos(cycle.getVertex((i + 1) % cycle.vertexCount())))) count++; if (count & 1) return false; return true; } // The same but with mapping bool MoleculeLayoutGraph::_isPointOutsideCycleEx (const Cycle &cycle, const Vec2f &p, const Array<int> &mapping) const { // TODO: check that point 'p' is equal to the one of cycle points (sometimes it happens) float a, b; int tries = 0; while (tries < 50) { tries++; // Choose random direction a = (float)rand(); b = (float)rand(); a = 2.f * (a / RAND_MAX - 0.5f); b = 2.f * (b / RAND_MAX - 0.5f); // Calculate number of intersection with boundary int count = 0; for (int i = 0; i < cycle.vertexCount(); i++) { int ret = _isRayIntersectWithCheck(a, b, p, getPos(mapping[cycle.getVertex(i)]), getPos(mapping[cycle.getVertex((i + 1) % cycle.vertexCount())]), true); if (ret == -1) { // Ray is too near to the point. Choose another one point count = -1; break; } if (ret == 1) count++; } if (count == -1) // Try again continue; // If number of intersections is even then point is outside if (count & 1) return false; return true; } // Return any value hoping it will never happen return false; } // Extract component border void MoleculeLayoutGraphSimple::_getBorder (Cycle &border) const { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); int i, n = 0; for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) if (_layout_edges[i].type == ELEMENT_BOUNDARY) n++; if (n == 0) return; vertices.clear(); edges.clear(); for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) if (_layout_edges[i].type == ELEMENT_BOUNDARY) break; Edge edge = getEdge(i); vertices.push(edge.beg); edges.push(i); while (edge.end != vertices[0]) { const Vertex &vert = getVertex(edge.end); bool found = false; for (int i = vert.neiBegin(); !found && i < vert.neiEnd(); i = vert.neiNext(i)) { int nei_v = vert.neiVertex(i); int nei_e = vert.neiEdge(i); if (getEdgeType(nei_e) == ELEMENT_BOUNDARY && nei_v != edge.beg) { edge.beg = edge.end; edge.end = nei_v; vertices.push(edge.beg); edges.push(nei_e); found = true; } } if (!found || vertices.size() > n) throw Error("corrupted border"); } border.copy(vertices, edges); border.canonize(); } // Split border in two parts by two vertices void MoleculeLayoutGraphSimple::_splitBorder (int v1, int v2, Array<int> &part1v, Array<int> &part1e, Array<int> &part2v, Array<int> &part2e) const { Cycle border; _getBorder(border); int idx1 = border.findVertex(v1); int idx2 = border.findVertex(v2); int i; if (idx1 == -1 || idx2 == -1) throw Error("border division by non-boundary vertex"); if (idx1 > idx2) __swap(idx1, idx2, i); part1v.clear(); part1e.clear(); part2v.clear(); part2e.clear(); for (i = idx1; i < idx2 + 1; i++) { part1v.push(border.getVertex(i)); part1e.push(border.getEdge(i)); } part1e.pop(); // edge count is less for (i = idx2; i < border.vertexCount(); i++) { part2v.push(border.getVertex(i)); part2e.push(border.getEdge(i)); } for (i = 0; i < idx1 + 1; i++) { part2v.push(border.getVertex(i)); part2e.push(border.getEdge(i)); } part2e.pop(); // edge count is less } // Cycle enumerator callback // Check if cycle is boundary and mark vertices and edges as boundary/internal bool MoleculeLayoutGraph::_border_cb (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { MoleculeLayoutGraph &self = *(MoleculeLayoutGraph *)context; Cycle cycle(vertices, edges); //cycle.canonize(); int i; QS_DEF(Array<int>, types); types.clear_resize(self.vertexEnd()); for (i = self.vertexBegin(); i < self.vertexEnd(); i = self.vertexNext(i)) types[i] = ELEMENT_INTERNAL; for (i = 0; i < cycle.vertexCount(); i++) types[cycle.getVertex(i)] = ELEMENT_BOUNDARY; // Check vertices not in cycle are inside it for (i = self.vertexBegin(); i < self.vertexEnd(); i = self.vertexNext(i)) if (types[i] == ELEMENT_INTERNAL) if (self._isPointOutsideCycle(cycle, self.getPos(i))) return true; //continue // Check edge centers are inside cycle types.clear_resize(self.edgeEnd()); for (i = self.edgeBegin(); i < self.edgeEnd(); i = self.edgeNext(i)) types[i] = ELEMENT_INTERNAL; for (i = 0; i < cycle.vertexCount(); i++) types[cycle.getEdge(i)] = ELEMENT_BOUNDARY; for (i = self.edgeBegin(); i < self.edgeEnd(); i = self.edgeNext(i)) if (types[i] == ELEMENT_INTERNAL) { Vec2f p; const Edge &edge = self.getEdge(i); p.lineCombin2(self.getPos(edge.beg), 0.5f, self.getPos(edge.end), 0.5f); if (self._isPointOutsideCycle(cycle, p)) return true; //continue } // Mark edges and bonds for (i = self.vertexBegin(); i < self.vertexEnd(); i = self.vertexNext(i)) self._layout_vertices[i].type = ELEMENT_INTERNAL; for (i = self.edgeBegin(); i < self.edgeEnd(); i = self.edgeNext(i)) self._layout_edges[i].type = ELEMENT_INTERNAL; for (i = 0; i < cycle.vertexCount(); i++) { self._layout_vertices[cycle.getVertex(i)].type = ELEMENT_BOUNDARY; self._layout_edges[cycle.getEdge(i)].type = ELEMENT_BOUNDARY; } return false; } // Check if point is outside biconnected component // By calculating number of intersections of ray bool MoleculeLayoutGraphSmart::_isPointOutside(const Vec2f &p) const { // return true; QS_DEF(Array<Vec2f>, point); Cycle surround_cycle; _getSurroundCycle(surround_cycle, p); if (surround_cycle.vertexCount() == 0) return 0; return _isPointOutsideCycle(surround_cycle, p); } // Check if point is outside cycle // By calculating number of intersections of ray bool MoleculeLayoutGraphSmart::_isPointOutsideCycle(const Cycle &cycle, const Vec2f &p) const { QS_DEF(Array<Vec2f>, point); float rotate_angle = 0; int size = cycle.vertexCount(); point.resize(size + 1); for (int i = 0; i <= size; i++) point[i] = _layout_vertices[cycle.getVertexC(i)].pos - p; for (int i = 0; i < size; i++) { float cs = Vec2f::dot(point[i], point[i + 1]) / (point[i].length() * point[i + 1].length()); if (cs > 1) cs = 1; if (cs < -1) cs = -1; float angle = acos(cs); if (Vec2f::cross(point[i], point[i + 1]) < 0) angle = -angle; rotate_angle += angle; } // if point is outside, rotate angle is equals to 0. If point is inside rotate angle is equals to 2*PI of -2*PI return abs(rotate_angle) < PI; } double MoleculeLayoutGraphSmart::_get_square() { Cycle cycle; _getBorder(cycle); int len = cycle.vertexCount(); double sq = 0; for (int i = 1; i < len - 1; i++) sq += Vec2f::cross(getPos(cycle.getVertex(i)) - getPos(cycle.getVertex(0)), getPos(cycle.getVertex(i + 1)) - getPos(cycle.getVertex(0))); return abs(sq / 2); } // Extract component border void MoleculeLayoutGraphSmart::_getBorder(Cycle &border) const { Vec2f outside_point(0, 0); for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].type != ELEMENT_NOT_DRAWN) { outside_point.max(_layout_vertices[i].pos); } outside_point += Vec2f(1, 1); _getSurroundCycle(border, outside_point); } void MoleculeLayoutGraphSmart::_getSurroundCycle(Cycle &cycle, Vec2f p) const { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); QS_DEF(Array<Vec2f>, pos); int i, n = 0; float eps = 1e-5; /* for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) if (_layout_edges[i].type == ELEMENT_BOUNDARY) n++; if (n == 0) return;*/ vertices.clear(); edges.clear(); srand(19857615); float sn = 0; float cs = 0; while (sn == 0 && cs == 0) { sn = 2.0*rand() / RAND_MAX - 1; cs = 2.0*rand() / RAND_MAX - 1; } float len = sqrt(sn*sn + cs*cs); sn /= len; cs /= len; pos.resize(vertexEnd()); for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (getVertexType(i) != ELEMENT_NOT_DRAWN) { pos[i].copy(getPos(i) - p); } for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (getVertexType(i) != ELEMENT_NOT_DRAWN) { pos[i].rotate(sn, cs); } int first_edge = -1; float first_edge_x = 1e20; for (int i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) if (_layout_edges[i].type != ELEMENT_NOT_DRAWN) { Edge e = getEdge(i); if (pos[e.beg].y * pos[e.end].y <= 0) { float mid_x = (pos[e.beg].x * pos[e.end].y - pos[e.end].x * pos[e.beg].y) / (pos[e.end].y - pos[e.beg].y); if (abs(mid_x) < eps) return; if (mid_x > 0 && (first_edge == -1 || mid_x < first_edge_x)) { first_edge = i; first_edge_x = mid_x; } } } int firts_vertex = -1; if (first_edge != -1) if (pos[getEdge(first_edge).beg].y < pos[getEdge(first_edge).end].y) firts_vertex = getEdge(first_edge).beg; else firts_vertex = getEdge(first_edge).end; else { // in this case no edges are intersecs (OX) ray // and so p is outside point // Then we are looking for border // and we can take the lowest vertex as the start vertex for searching of surround cycle float first_vertex_y; for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (_layout_vertices[i].type != ELEMENT_NOT_DRAWN) if (firts_vertex == -1 || pos[i].y < first_vertex_y) { first_vertex_y = pos[i].y; firts_vertex = i; } // and now lets find first edge... int second_vertex = -1; for (int i = getVertex(firts_vertex).neiBegin(); i != getVertex(firts_vertex).neiEnd(); i = getVertex(firts_vertex).neiNext(i)) { int new_vertex = getVertex(firts_vertex).neiVertex(i); if (isVertexDrawn(new_vertex) && (second_vertex == -1 || Vec2f::cross(pos[second_vertex] - pos[firts_vertex], pos[new_vertex] - pos[firts_vertex]) > 0)) { second_vertex = new_vertex; first_edge = getVertex(firts_vertex).neiEdge(i); } } } vertices.push(firts_vertex); edges.push(first_edge); while (true) { int current_vertex = vertices.top(); int current_edge = edges.top(); int next_vertex = getEdge(current_edge).findOtherEnd(current_vertex); int next_edge = -1; int next_vertex2 = current_vertex; for (int i = getVertex(next_vertex).neiBegin(); i != getVertex(next_vertex).neiEnd(); i = getVertex(next_vertex).neiNext(i)) { int try_vertex = getVertex(next_vertex).neiVertex(i); if (isVertexDrawn(try_vertex) && try_vertex != current_vertex) { bool need_to_update = false; if (next_vertex2 == current_vertex) { need_to_update = true; } else { if (Vec2f::cross(pos[next_vertex2] - pos[next_vertex], pos[current_vertex] - pos[next_vertex]) >= 0) { need_to_update = Vec2f::cross(pos[next_vertex2] - pos[next_vertex], pos[try_vertex] - pos[next_vertex]) >= 0 && Vec2f::cross(pos[try_vertex] - pos[next_vertex], pos[current_vertex] - pos[next_vertex]) >= 0; } else { need_to_update = Vec2f::cross(pos[next_vertex2] - pos[next_vertex], pos[try_vertex] - pos[next_vertex]) >= 0 || Vec2f::cross(pos[try_vertex] - pos[next_vertex], pos[current_vertex] - pos[next_vertex]) >= 0; } } if (need_to_update) { next_vertex2 = try_vertex; next_edge = getVertex(next_vertex).neiEdge(i); } } } if (next_vertex == vertices[0] && next_edge == edges[0]) break; else { vertices.push(next_vertex); edges.push(next_edge); if (vertices.size() > vertexCount()) { vertices.clear_resize(0); edges.clear_resize(0); break; } } } cycle.copy(vertices, edges); //cycle.canonize(); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_cycle.cpp��������������������������������������0000664�0000000�0000000�00000014067�12710376503�0024563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_graph.h" using namespace indigo; CP_DEF(MoleculeLayoutGraph::Cycle); MoleculeLayoutGraph::Cycle::Cycle () : CP_INIT, TL_CP_GET(_vertices), TL_CP_GET(_edges), TL_CP_GET(_attached_weight) { _vertices.clear(); _edges.clear(); _attached_weight.clear(); _max_idx = 0; _morgan_code_calculated = false; } MoleculeLayoutGraph::Cycle::Cycle (const List<int> &edges, const MoleculeLayoutGraph &graph) : CP_INIT, TL_CP_GET(_vertices), TL_CP_GET(_edges), TL_CP_GET(_attached_weight) { copy(edges, graph); _attached_weight.resize(graph.vertexCount()); _attached_weight.zerofill(); _morgan_code_calculated = false; } MoleculeLayoutGraph::Cycle::Cycle (const Array<int> &vertices, const Array<int> &edges) : CP_INIT, TL_CP_GET(_vertices), TL_CP_GET(_edges), TL_CP_GET(_attached_weight) { copy(vertices, edges); _attached_weight.resize(vertices.size()); _attached_weight.zerofill(); _morgan_code_calculated = false; } void MoleculeLayoutGraph::Cycle::copy (const List<int> &edges, const MoleculeLayoutGraph &graph) { int i = edges.begin(); const Edge &edge1 = graph.getEdge(edges[i]); const Edge &edge2 = graph.getEdge(edges[edges.next(i)]); _vertices.clear(); _edges.clear(); if (edge1.beg == edge2.beg || edge1.beg == edge2.end) _vertices.push(edge1.end); else _vertices.push(edge1.beg); for ( ; i < edges.end(); i = edges.next(i)) { const Edge &edge = graph.getEdge(edges[i]); if (_vertices.top() == edge.beg) _vertices.push(edge.end); else _vertices.push(edge.beg); _edges.push(edges[i]); } _vertices.pop(); _max_idx = 0; for (int i = 0; i < _vertices.size(); i++) if (_vertices[i] > _max_idx) _max_idx = _vertices[i]; } void MoleculeLayoutGraph::Cycle::copy (const Array<int> &vertices, const Array<int> &edges) { _vertices.copy(vertices); _edges.copy(edges); _max_idx = 0; for (int i = 0; i < _vertices.size(); i++) if (_vertices[i] > _max_idx) _max_idx = _vertices[i]; } void MoleculeLayoutGraph::Cycle::calcMorganCode(const MoleculeLayoutGraph &parent_graph) { _morgan_code = 0; for (int i = 0; i < vertexCount(); i++) _morgan_code += parent_graph.getLayoutVertex(_vertices[i]).morgan_code; _morgan_code_calculated = true; } void MoleculeLayoutGraph::Cycle::canonize() { // 1. v(0)<v(i), i=1,...,l-1 ; // 2. v(1)< v(l-2) => unique representation of cycle if (vertexCount() == 0) return; int min_idx = 0, i; bool vert_invert = false; Cycle src_cycle(_vertices, _edges); for (i = 1; i < vertexCount(); i++) if (_vertices[i] < _vertices[min_idx]) min_idx = i; int prev_idx = __max(0, min_idx - 1); int next_idx = __min(vertexCount() - 1, min_idx + 1); // rotate direction if (_vertices[prev_idx] < _vertices[next_idx]) vert_invert = true; // rotate if (vert_invert) { for (i = 0; i < min_idx + 1; i++) { _vertices[i] = src_cycle._vertices[min_idx - i]; _edges[i] = src_cycle._edges[i == min_idx ? vertexCount() - 1 : min_idx - i - 1]; } for ( ; i < vertexCount(); i++) { _vertices[i] = src_cycle._vertices[min_idx - i + vertexCount()]; _edges[i] = src_cycle._edges[min_idx - i + vertexCount() - 1]; } } else { for (i = 0; i < vertexCount() - min_idx; i++) { _vertices[i] = src_cycle._vertices[min_idx + i]; _edges[i] = src_cycle._edges[min_idx + i]; } for ( ; i < vertexCount(); i++) { _vertices[i] = src_cycle._vertices[min_idx + i - vertexCount()]; _edges[i] = src_cycle._edges[min_idx + i - vertexCount()]; } } } bool MoleculeLayoutGraph::Cycle::contains (const Cycle &another) const { if (vertexCount() < another.vertexCount()) return false; QS_DEF(Array<int>, vertex_found); vertex_found.clear_resize(_max_idx + 1); vertex_found.zerofill(); for (int i = 0; i < vertexCount(); i++) vertex_found[_vertices[i]] = 1; for (int i = 0; i < another.vertexCount(); i++) if (another._vertices[i] >= vertex_found.size() || vertex_found[another._vertices[i]] == 0) return false; return true; } // Cycle sorting callback // Order by size: 6, 5, 7, 8, 4, 3, 9, 10, 11, .. // If cycles has the same size then Morgan code in descending order (higher first) int MoleculeLayoutGraph::Cycle::compare_cb (int &idx1, int &idx2, void *context) { const ObjPool<Cycle> &cycles = *(const ObjPool<Cycle> *)context; int size_freq[] = {6, 5, 7, 8, 4, 3}; int freq_idx1, freq_idx2; for (freq_idx1 = 0; freq_idx1 < NELEM(size_freq); freq_idx1++) if (cycles[idx1].vertexCount() == size_freq[freq_idx1]) break; for (freq_idx2 = 0; freq_idx2 < NELEM(size_freq); freq_idx2++) if (cycles[idx2].vertexCount() == size_freq[freq_idx2]) break; if (freq_idx1 != freq_idx2) return freq_idx1 - freq_idx2; if (freq_idx1 == NELEM(size_freq) && cycles[idx1].vertexCount() != cycles[idx2].vertexCount()) return cycles[idx1].vertexCount() - cycles[idx2].vertexCount(); return cycles[idx2].morganCode() - cycles[idx1].morganCode(); } void MoleculeLayoutGraphSmart::calcMorganCode () { _calcMorganCodes(); } long MoleculeLayoutGraphSmart::getMorganCode () { return _total_morgan_code; } void MoleculeLayoutGraphSmart::assignFirstVertex (int v) { _first_vertex_idx = v; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_geom.cpp���������������������������������������0000664�0000000�0000000�00000050755�12710376503�0024417�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_graph.h" using namespace indigo; float f1 (float X, int L, float s) { int i, min1; float f; min1 = 1; f = ( 1 - s) / 2; for (i = 1; i <= L; i++) { min1 = -min1; f += cos(i * X) * min1; } return f; } float f2 (float X, int L, float s) { int i, min1; float f; min1 = -1; f = -s / 2; for (i = 0; i <= L; i++) { min1 = -min1; f += sin((2 * i + 1) * X / 2) * min1; } return f; } void MoleculeLayoutGraph::_findAngles (int k, float s, float &x, float &y) { int L; float a0, b0; if (k % 2 == 0) L = k / 2; else L = (k - 1) / 2; // Choose most right segment with root b0 = PI - EPSILON + PI / 100; a0 = b0 - PI / 100; bool repeat = true; while (repeat) { b0 = b0 - PI / 100; a0 = a0 - PI / 100; if ((a0 < PI / 2 + EPSILON) && k > 3) throw Error("there are no roots"); if (k % 2 == 0) { if (f1(a0, L, s) * f1(b0, L, s) > 0) continue; } else { if (f2(a0, L, s) * f2(b0, L, s) > 0) continue; } repeat = false; } // Find root if (k % 2 == 0) { x = _dichotomy1(a0, b0, L, s); y = L * (PI - x); } else { x = _dichotomy2(a0, b0, L, s); y = (2 * L + 1) * (PI - x) / 2; } } float MoleculeLayoutGraph::_dichotomy1 (float a0, float b0, int L, float s) { // Return root of the equation f1 ( x,l,S]=0; // if there are a root at the [a0,b0].; // Assumption:f1 ( a0]*f1 ( b0]<0; float C, pr; float pr1, fa0; fa0 = f1(a0, L, s); if (fa0 * f1(b0, L, s) > 0) throw Error("there are no roots"); while (true) { C = (a0 + b0) / 2; pr = f1( C, L, s); if (C - a0 < EPSILON) return C; pr1 = pr * fa0; if (pr1 < 0) b0 = C; else { a0 = C; fa0 = pr; } } } float MoleculeLayoutGraph::_dichotomy2 (float a0, float b0, int L, float s) { // Return root of the equation f2 ( x,l,S]=0; // if there are a root at the [a0,b0].; // Assumption:f1 ( a0]*f2 ( b0]<0; float C, pr; float pr1, fa0; fa0 = f2 ( a0, L, s); if (fa0 * f2 ( b0, L, s) > 0) throw Error("there are no roots"); while (true) { C = (a0 + b0) / 2; pr = f2(C, L, s); if (C - a0 < EPSILON) return C; pr1 = pr * fa0; if (pr1 < 0) b0 = C; else { a0 = C; fa0 = pr; } } } // Complete regular curve from v1 to v2 by vertices in chain bool MoleculeLayoutGraph::_drawRegularCurve (const Array<int> &chain, int v1, int v2, float length, bool ccw, int type) { QS_DEF(Array<int>, mapping); mapping.clear_resize(vertexEnd()); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) mapping[i] = i; return _drawRegularCurveEx(chain, v1, v2, length, ccw, type, mapping); } bool MoleculeLayoutGraph::_drawRegularCurveEx (const Array<int> &chain, int v1, int v2, float length, bool ccw, int type, const Array<int> &mapping) { float s, x0 = 0.f, y0 = 0.f; int i, k, L; float x1, x2, y1, y2; int min1; float cosa, sina; k = chain.size() - 2; s = Vec2f::dist(getPos(mapping[v1]), getPos(mapping[v2])); if (s > (k + 1) * length - EPSILON) return false; _findAngles(k, s / length, x0, y0); // Calculate coordinates so that // v1(0,0) and v2(s,0) if (k % 2 == 0) L = k / 2; else L = (k - 1) / 2; x1 = 0; y1 = 0; min1 = -1; for (i = 0; i <= L - 1; i++) { min1 = -min1; x2 = x1 + min1 * cos(y0 + i * x0) * length; y2 = y1 + min1 * sin(y0 + i * x0) * length; getPos(mapping[chain[i + 1]]).set(x2, y2); x1 = x2; y1 = y2; } if (k % 2 == 1) { x2 = x1 + sin (x0 / 2) * length; y2 = y1 + cos (x0 / 2) * length; getPos(mapping[chain[L + 1]]).set(x2, y2); } x1 = s; y1 = 0; min1 = 1; for (i = 0; i <= L - 1; i++) { y2 = y1 + min1 * sin(y0 + i * x0) * length; min1 = -min1; x2 = x1 + min1 * cos(y0 + i * x0) * length; getPos(mapping[chain[chain.size() - i - 2]]).set(x2, y2); x1 = x2; y1 = y2; } // If CW - flip if (!ccw) for (i = 1; i < chain.size() - 1; i++) getPos(mapping[chain[i]]).y *= -1; // Return to source coordinates // Rotate on the angle between (v1,v2) and Ox and translate on vector v1 if (s > EPSILON) { Vec2f &pos1 = getPos(mapping[v1]); Vec2f &pos2 = getPos(mapping[v2]); cosa = (pos2.x - pos1.x) / s; sina = (pos2.y - pos1.y) / s; } else { cosa = 1; sina = 0; } for (i = 1; i < chain.size() - 1; i++) { Vec2f old_pos = getPos(mapping[chain[i]]); Vec2f &pos = getPos(mapping[chain[i]]); pos.set(old_pos.x * cosa - old_pos.y * sina, old_pos.x * sina + old_pos.y * cosa); pos.add(getPos(mapping[chain[0]])); } // Set new types for (i = 0; i < chain.size() - 1; i++) { int beg = mapping[chain[i]]; if (i > 0) _layout_vertices[beg].type = type; const Vertex &vert = getVertex(beg); int edge_idx = vert.neiEdge(vert.findNeiVertex(mapping[chain[i + 1]])); _layout_edges[edge_idx].type = type; } return true; } // Check vertex is inside the edge bool MoleculeLayoutGraph::_isVertexOnEdge (int vert_idx, int edge_beg, int edge_end) const { float a1, a0, b1, b0; float t, eps = 0.05f; const Vec2f &pos = getPos(vert_idx); const Vec2f &pos1 = getPos(edge_beg); const Vec2f &pos2 = getPos(edge_end); a1 = pos2.x - pos1.x; a0 = pos.x - pos1.x; b1 = pos2.y - pos1.y; b0 = pos.y - pos1.y; if (a1 * a1 + b1 * b1 < eps) { if (a0 * a0 + b0 * b0 < eps) return true; else return false; } if (fabs(a1) < eps) { if (fabs(a0) > eps) return false; else { t = b0 / b1; if (t > -eps && t < 1 + eps) return true; else return false; } } if (fabs(b1) < eps) { if (fabs(b0) > eps) return false; else { t = a0 / a1; if (t > -eps && t < 1 + eps) return true; else return false; } } t = a0 / a1; if (fabs(t - (b0 / b1)) < eps) { if (t > -eps && t < 1 + eps) return true; else return false; } else return false; } bool MoleculeLayoutGraph::_isVertexOnSomeEdge (int vert_idx) const { int i; for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { int type = _layout_edges[i].type; if (type == ELEMENT_INTERNAL || type == ELEMENT_BOUNDARY) { const Edge &edge = getEdge(i); if (vert_idx != edge.beg && vert_idx != edge.end && _isVertexOnEdge(vert_idx, edge.beg, edge.end)) return true; } } return false; } // Translate edge by delta orthogonally void MoleculeLayoutGraph::_shiftEdge (int edge_idx, float delta) { float norm; const Edge &edge = getEdge(edge_idx); Vec2f &pos1 = getPos(edge.beg); Vec2f &pos2 = getPos(edge.end); norm = Vec2f::dist(pos1, pos2); Vec2f a(delta * (pos2.y - pos1.y) / norm, delta * (pos1.x - pos2.x) / norm); pos1.add(a); pos2.add(a); } // Calculate angle v1vv2 such the edge (v,v1) is on the right and (v,v2) is on the left // if component is trivial return 0 // if v is internal return 2pi float MoleculeLayoutGraphSimple::calculateAngle (int v, int &v1, int &v2) const { int i, j; Vec2f p, p0; float beta = 0.f; QS_DEF(Array<float>, angles); QS_DEF(Array<int>, edges); QS_DEF(Array<int>, on_left); if (vertexCount() == 2) { if (v == vertexBegin()) v1 = v2 = vertexNext(v); else v1 = v2 = vertexBegin(); return 0.f; } const Vertex &vert = getVertex(v); // Calculate polar angles angles.clear(); edges.clear(); on_left.clear_resize(vert.degree()); for (i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { edges.push(i); p0.diff(getPos(vert.neiVertex(i)), getPos(v)); angles.push(p0.tiltAngle2()); } // Sort for (i = 0; i < angles.size(); i++) for (j = i + 1; j < angles.size(); j++) if (angles[i] > angles[j]) { angles.swap(i, j); edges.swap(i, j); } // Find v1 for (i = 0; i < angles.size() - 1; i++) { beta = (angles[i + 1] + angles[i]) / 2; p = getPos(v); p.x += 0.2f * cos(beta); p.y += 0.2f * sin(beta); on_left[i] = _isPointOutside(p); } beta = PI + (angles.top() + angles[0]) / 2; p = getPos(v); p.x += 0.2f * cos(beta); p.y += 0.2f * sin(beta); on_left.top() = _isPointOutside(p); float comp_angle; if (vert.degree() == 2) { if (on_left[0] || (!on_left[1] && angles[1] - angles[0] > PI)) { comp_angle = 2 * PI - (angles[1] - angles[0]); v1 = vert.neiVertex(edges[1]); v2 = vert.neiVertex(edges[0]); } else { comp_angle = angles[1] - angles[0]; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges[1]); } return comp_angle; } // Find sector outside component for (i = 0; i < vert.degree() - 1; i++) { if (on_left[i]) { comp_angle = 2 * PI - (angles[i + 1] - angles[i]); v1 = vert.neiVertex(edges[i + 1]); v2 = vert.neiVertex(edges[i]); return comp_angle; } } if (on_left.top()) { comp_angle = angles.top() - angles[0]; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges.top()); return comp_angle; } // TODO: if vertex is internal - choose maximal free angle float max_angle = 0.f; for (i = 0; i < vert.degree() - 1; i++) { comp_angle = 2 * PI - (angles[i + 1] - angles[i]); if (comp_angle > max_angle) { max_angle = comp_angle; v1 = vert.neiVertex(edges[i + 1]); v2 = vert.neiVertex(edges[i]); } } comp_angle = angles.top() - angles[0]; if (comp_angle > max_angle) { max_angle = comp_angle; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges.top()); } return max_angle; } // Calculate position by adding one unit with given angle to the segment void MoleculeLayoutGraph::_calculatePos (float phi, const Vec2f &v1, const Vec2f &v2, Vec2f &v) { float alpha; Vec2f dir; dir.diff(v2, v1); alpha = dir.tiltAngle(); alpha += phi; v.set(v1.x + cos(alpha), v1.y + sin(alpha)); } // Explore intersection of two edges: // 0 at least one edge is not drawn // 1 no intersection // 21 one common vertex // 222 one common point: beginning of first edge which lays inside second edge // 223 one common point: end of first edge which lays inside second edge // 224 one common point: beginning of second edge which lays inside first edge // 225 one common point: end of second edge which lays inside first edge // 23 one common point inside both edges // 3 have common segment but not equal // 4 equal // 5 error // Parametric equation: // x = (x1 - x0) * t + x0; // y = (y1 - y0) * t + y0; // 0 <= t <= 1; int MoleculeLayoutGraph::_calcIntersection (int edge1_idx, int edge2_idx) const { float a11, a12, a21, a22, b1, b2; float delta, delta1, delta2, t, s; float a, b, pr, eps; const Edge &edge1 = getEdge(edge1_idx); const Edge &edge2 = getEdge(edge2_idx); eps = 0.01f; if (getVertexType(edge1.beg) == ELEMENT_NOT_DRAWN || getVertexType(edge1.end) == ELEMENT_NOT_DRAWN || getVertexType(edge2.beg) == ELEMENT_NOT_DRAWN || getVertexType(edge2.end) == ELEMENT_NOT_DRAWN) return 0; const Vec2f &v1 = getPos(edge1.beg); const Vec2f &v2 = getPos(edge1.end); const Vec2f &v3 = getPos(edge2.beg); const Vec2f &v4 = getPos(edge2.end); a11 = v2.x - v1.x; a12 = v3.x - v4.x; b1 = v3.x - v1.x; a21 = v2.y - v1.y; a22 = v3.y - v4.y; b2 = v3.y - v1.y; delta = a11 * a22 - a12 * a21; delta2 = a11 * b2 - a21 * b1; delta1 = b1 * a22 - b2 * a12; if (fabs(delta) < eps) { if (fabs(b1 * a21 - b2 * a11) > eps) return 1; if (fabs(a11) > eps) { a = b1 / a11; b = ( b1 - a12) / a11; if (b < a) { pr = a; a = b; b = pr; } } else { a = b2 / a21; b = (b2 - a22) / a21; if (b < a) { pr = a; a = b; b = pr; } } if (a <= -eps) { if (b <= -eps) return 1; if (fabs(b) <= eps) return 21; return 3; } if (fabs(a) <= eps) { if (fabs(1 - b) <= eps) return 4; return 3; } if (a <= 1 - eps) return 3; if (fabs(a - 1) <= eps) return 21; if (a >= eps) return 1; } else { t = delta1 / delta; s = delta2 / delta; if (t < -eps || t > 1 + eps || s < -eps || s > 1 + eps) return 1; if (t > eps && t < 1 - eps && s > eps && s < 1 - eps) return 23; if (t > eps && t < 1 - eps) { if (s > -eps && s < eps) return 224; //v3 if (s > 1 - eps && s < 1 + eps) return 225; //v4 } if (s > eps && s < 1 - eps) { if ((t > -eps && t < eps)) return 222; //v1 if ((t > 1 - eps && t < 1 + eps)) return 223; //v2 } if (((t > -eps && t < eps) || (t > 1 - eps && t < 1 + eps)) && ((s > -eps && s < eps) || (s > 1 - eps && s < 1 + eps))) return 21; } return 5; } // Calculate angle v1vv2 such the edge (v,v1) is on the right and (v,v2) is on the left // if component is trivial return 0 // if v is internal return 2pi float MoleculeLayoutGraphSmart::calculateAngle(int v, int &v1, int &v2) const { int i, j; Vec2f p, p0; float beta = 0.f; QS_DEF(Array<float>, angles); QS_DEF(Array<int>, edges); QS_DEF(Array<int>, on_left); if (vertexCount() == 2) { if (v == vertexBegin()) v1 = v2 = vertexNext(v); else v1 = v2 = vertexBegin(); return 0.f; } const Vertex &vert = getVertex(v); // Calculate polar angles angles.clear(); edges.clear(); on_left.clear_resize(vert.degree()); for (i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { edges.push(i); p0.diff(getPos(vert.neiVertex(i)), getPos(v)); angles.push(p0.tiltAngle2()); } // Sort for (i = 0; i < angles.size(); i++) for (j = i + 1; j < angles.size(); j++) if (angles[i] > angles[j]) { angles.swap(i, j); edges.swap(i, j); } // Find v1 for (i = 0; i < angles.size() - 1; i++) { beta = (angles[i + 1] + angles[i]) / 2; p = getPos(v); p.x += 0.2f * cos(beta); p.y += 0.2f * sin(beta); on_left[i] = _isPointOutside(p); } beta = PI + (angles.top() + angles[0]) / 2; p = getPos(v); p.x += 0.2f * cos(beta); p.y += 0.2f * sin(beta); on_left.top() = _isPointOutside(p); float comp_angle; float cur_energy = 0; /* if (vert.degree() == 2) { // if (on_left[0] || (!on_left[1] && angles[1] - angles[0] > PI)) if (on_left[0] > on_left[1] || (on_left[0] == on_left[1] && angles[1] - angles[0] > PI)) { comp_angle = 2 * PI - (angles[1] - angles[0]); v1 = vert.neiVertex(edges[1]); v2 = vert.neiVertex(edges[0]); } else { comp_angle = angles[1] - angles[0]; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges[1]); } return comp_angle; }*/ // Find sector outside component if (_molecule->cis_trans.getParity(getEdgeExtIdx(vert.neiEdge(vert.neiBegin()))) != 0 || _molecule->cis_trans.getParity(getEdgeExtIdx(vert.neiEdge(vert.neiNext(vert.neiBegin())))) != 0) { float best_angle = 2 * PI; for (i = 0; i < vert.degree(); i++) { int ii = i + 1; if (ii == vert.degree()) ii = 0; comp_angle = 2 * PI - (angles[ii] - angles[i]); if (ii == 0) comp_angle -= 2 * PI; float eps = 0.1; if (i == 0 || comp_angle < best_angle - eps) { best_angle = comp_angle; v1 = vert.neiVertex(edges[ii]); v2 = vert.neiVertex(edges[i]); } } /* comp_angle = angles.top() - angles[0]; if (comp_angle < best_angle) { best_angle = comp_angle; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges.top()); }*/ return best_angle; } if (_graph->getVertexType(getVertexExtIdx(vert.neiVertex(vert.neiBegin()))) == ELEMENT_NOT_DRAWN) { // if this is not layouted component for (i = 0; i < vert.degree() - 1; i++) { if (on_left[i]) { comp_angle = 2 * PI - (angles[i + 1] - angles[i]); v1 = vert.neiVertex(edges[i + 1]); v2 = vert.neiVertex(edges[i]); return comp_angle; } } if (on_left.top()) { comp_angle = angles.top() - angles[0]; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges.top()); return comp_angle; } } // TODO: if vertex is internal - choose maximal free angle float best_angle = 2 * PI; for (i = 0; i < vert.degree(); i++) { int ii = i + 1; if (ii == vert.degree()) ii = 0; beta = (angles[ii] + angles[i]) / 2; if (ii == 0) { beta += PI; if (beta >= 2 * PI) beta -= 2 * PI; } p = _graph->getPos(getVertexExtIdx(v)); p.x += 1 * cos(beta); p.y += 1 * sin(beta); float energy = _energyOfPoint(p); comp_angle = 2 * PI - (angles[ii] - angles[i]); if (ii == 0) comp_angle -= 2 * PI; float eps = 0.1; // printf("%d: %5.5f %5.5f %d\n", i, energy, comp_angle, on_left[i]); if (i == 0 || energy + eps < cur_energy || (abs(energy - cur_energy) < eps && comp_angle < best_angle - eps) || (abs(energy - cur_energy) < eps && abs(comp_angle - best_angle) < eps && on_left[i])) { cur_energy = energy; best_angle = comp_angle; v1 = vert.neiVertex(edges[ii]); v2 = vert.neiVertex(edges[i]); } /* comp_angle = 2 * PI - (angles[i + 1] - angles[i]); if (comp_angle < best_angle) { best_angle = comp_angle; v1 = vert.neiVertex(edges[i + 1]); v2 = vert.neiVertex(edges[i]); }*/ } /* comp_angle = angles.top() - angles[0]; if (comp_angle < best_angle) { best_angle = comp_angle; v1 = vert.neiVertex(edges[0]); v2 = vert.neiVertex(edges.top()); }*/ return best_angle; } const float MoleculeLayoutGraphSmart::_energyOfPoint(Vec2f p) const { float energy = 0; for (int i = _graph->vertexBegin(); i < _graph->vertexEnd(); i = _graph->vertexNext(i)) if (_graph->getLayoutVertex(i).type != ELEMENT_NOT_DRAWN) { float d = Vec2f::dist(p, _graph->getPos(i)); if (d <= 1.5) if (d >= 0.5) energy += 1.0 / d; else energy += 2.0; } return energy; } // Calculate position by adding one unit with given angle to the segment int MoleculeLayoutGraphSmart::_isCisConfiguratuin(Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4) { int rotateCounterclockwise1 = (p3.x - p2.x) * (p2.y - p1.y) - (p3.y - p2.y) * (p2.x - p1.x) > 0; int rotateCounterclockwise2 = (p4.x - p3.x) * (p3.y - p2.y) - (p4.y - p3.y) * (p3.x - p2.x) > 0; return rotateCounterclockwise1 == rotateCounterclockwise2; } �������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_refine.cpp�������������������������������������0000664�0000000�0000000�00000035534�12710376503�0024736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/red_black.h" #include "base_cpp/obj.h" #include "graph/graph_subchain_enumerator.h" #include "graph/biconnected_decomposer.h" #include "graph/path_enumerator.h" #include "layout/molecule_layout_graph.h" #include "layout/refinement_state.h" using namespace indigo; bool MoleculeLayoutGraph::_edge_check(Graph &graph, int e_idx, void *context_) { EnumContext &context = *(EnumContext *)context_; if (!context.graph->getLayoutEdge(e_idx).is_cyclic) { if (context.graph->_n_fixed > 0) { const Edge &edge = context.graph->getEdge(e_idx); if (context.graph->_fixed_vertices[edge.beg] && context.graph->_fixed_vertices[edge.end]) return false; } context.edges->find_or_insert(e_idx); return true; } return false; } bool MoleculeLayoutGraph::_path_handle(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context_) { EnumContext &context = *(EnumContext *)context_; int i; for (i = 0; i < edges.size(); i++) if (!context.graph->getLayoutEdge(edges[i]).is_cyclic) { if (context.graph->_n_fixed > 0) { const Edge &edge = context.graph->getEdge(edges[i]); if (context.graph->_fixed_vertices[edge.beg] && context.graph->_fixed_vertices[edge.end]) continue; } context.edges->find_or_insert(edges[i]); } return false; } // Split graph in two branches by acyclic edge (its vertices are in different branches) void MoleculeLayoutGraph::_makeBranches(Array<int> &branches, int edge, Filter &filter) const { branches.clear_resize(vertexEnd()); branches.zerofill(); QS_DEF(Array<int>, dfs_stack); dfs_stack.clear(); dfs_stack.push(_first_vertex_idx); int i, v, u; // DFS: find paths from v avoiding given edge while (dfs_stack.size() > 0) { v = dfs_stack.top(); branches[v] = 1; const Vertex &vert = getVertex(v); bool no_push = true; for (i = vert.neiBegin(); i < vert.neiEnd(); i = vert.neiNext(i)) { if (vert.neiEdge(i) == edge) continue; u = vert.neiVertex(i); if (!branches[u]) { dfs_stack.push(u); no_push = false; break; } } if (no_push) dfs_stack.pop(); } filter.init(branches.ptr(), Filter::EQ, 1); } bool MoleculeLayoutGraph::_allowRotateAroundVertex(int idx) const { if (_molecule != 0) { const Vertex &v = getVertex(idx); if (v.degree() == 2) { int first_nei = v.neiBegin(); int type1 = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[v.neiEdge(first_nei)].ext_idx]); int type2 = _molecule->getBondOrder(_molecule_edge_mapping[_layout_edges[v.neiEdge(v.neiNext(first_nei))].ext_idx]); if (type1 == BOND_TRIPLE || type2 == BOND_TRIPLE || (type1 == BOND_DOUBLE && type2 == BOND_DOUBLE)) return false; } } return true; } // Increase minimal distance between vertices void MoleculeLayoutGraph::_refineCoordinates(const BiconnectedDecomposer &bc_decomposer, const PtrArray<MoleculeLayoutGraph> &bc_components, const Array<int> &bc_tree) { RefinementState beg_state(*this); RefinementState best_state(*this); RefinementState new_state(*this); QS_DEF(Array<int>, branch); int v1, v2; int v1c, v2c; int i, j, n; v1c = v1 = vertexBegin(); v2c = v2 = vertexNext(v1); // Calculate initial energy beg_state.copyFromGraph(); beg_state.calcEnergy(); beg_state.calcDistance(v1, v2); best_state.copy(beg_state); new_state.copy(beg_state); // Look through all vertex pairs which are closer than 0.6 bool improved = true; QS_DEF(RedBlackSet<int>, edges); QS_DEF(Array<int>, components1); QS_DEF(Array<int>, components2); EnumContext context; context.edges = &edges; context.graph = this; context.maxIterationNumber = max_iterations; int max_improvements = max_iterations * max_iterations; int n_improvements = 0; while (improved) { if (max_improvements > 0 && n_improvements > max_improvements) break; n_improvements++; improved = false; edges.clear(); int n_edges = 0; int n_enumerations = 0; bool to_break = false; new_state.copy(beg_state); for (v1 = vertexBegin(); v1 < vertexEnd() && !to_break; v1 = vertexNext(v1)) for (v2 = vertexNext(v1); v2 < vertexEnd(); v2 = vertexNext(v2)) { new_state.calcDistance(v1, v2); if (new_state.dist > 0.36f) continue; // Check if they are from the same component bool next_pair = false; bc_decomposer.getVertexComponents(v1, components1); bc_decomposer.getVertexComponents(v2, components2); for (i = 0; i < components1.size() && !next_pair; i++) for (j = 0; j < components2.size(); j++) { int comp1 = components1[i]; int comp2 = components2[j]; if (comp1 == comp2 && !bc_components[comp1]->isSingleEdge() && !bc_components[comp2]->isSingleEdge()) { next_pair = true; break; } } if (next_pair) continue; // check iterations limit if (max_iterations > 0 && n_enumerations > max_iterations) { to_break = true; break; } n_enumerations++; // Find acyclic edges on the all paths between v1 and v2 PathEnumerator path_enum(*this, v1, v2); path_enum.cb_check_edge = _edge_check; path_enum.cb_handle_path = _path_handle; path_enum.context = &context; context.iterationNumber = 0; try { path_enum.process(); } catch (Error) { // iterations limit reached } if (edges.size() == n_edges) continue; n_edges = edges.size(); if (beg_state.dist - 0.00001 > new_state.dist) { beg_state.dist = new_state.dist; v1c = v1; v2c = v2; } } if (edges.size() == 0) { beg_state.applyToGraph(); break; } // Flipping // Look through found edges for (i = edges.begin(); i < edges.end(); i = edges.next(i)) { if (max_improvements > 0 && n_improvements > max_improvements) break; n_improvements++; // Try to flip branch const Edge &edge = getEdge(edges.key(i)); if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0) continue; if (getVertex(edge.beg).degree() == 1 || getVertex(edge.end).degree() == 1) continue; Filter filter; _makeBranches(branch, edges.key(i), filter); new_state.flipBranch(filter, beg_state, edge.beg, edge.end); new_state.calcEnergy(); if (new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } } if (improved) {// finished becouse of flipped beg_state.copy(best_state); continue; } // Rotations // Look through found edges for (i = edges.begin(); i < edges.end(); i = edges.next(i)) { if (max_improvements > 0 && n_improvements > max_improvements) break; n_improvements += 3; // Try to rotate one branch by 10 degrees in both directions around both vertices const Edge &edge = getEdge(edges.key(i)); if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0) continue; Filter filter; _makeBranches(branch, edges.key(i), filter); bool around_beg = _allowRotateAroundVertex(edge.beg); bool around_end = _allowRotateAroundVertex(edge.end); if (around_beg) { new_state.rotateBranch(filter, beg_state, edge.beg, 10); new_state.calcDistance(v1c, v2c); new_state.calcEnergy(); if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } new_state.rotateBranch(filter, beg_state, edge.beg, -10); new_state.calcDistance(v1c, v2c); new_state.calcEnergy(); if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } } if (around_end) { new_state.rotateBranch(filter, beg_state, edge.end, 10); new_state.calcDistance(v1c, v2c); new_state.calcEnergy(); if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } new_state.rotateBranch(filter, beg_state, edge.end, -10); new_state.calcDistance(v1c, v2c); new_state.calcEnergy(); if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } } // Stretching // Try to stretch each edge with 1.6 ratio if ((n = filter.count(*this)) != 1 && n != vertexCount() - 1) { new_state.stretchBranch(filter, beg_state, edge.beg, edge.end, 6); new_state.calcDistance(v1c, v2c); new_state.calcEnergy(); if (new_state.dist > beg_state.dist && new_state.energy + 20 < beg_state.energy && new_state.energy < best_state.energy - 0.00001) { improved = true; best_state.copy(new_state); } } } if (improved) beg_state.copy(best_state); } if (_n_fixed == 0) { if (!beg_state.is_small_cycle()) { int center = -1; long max_code = 0; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (getLayoutVertex(i).morgan_code > max_code) { center = i; max_code = getLayoutVertex(i).morgan_code; } beg_state.calcHeight(); for (float angle = -90.f; angle < 90.f + EPSILON; angle += 30.f) { new_state.rotateLayout(beg_state, center, angle); new_state.calcHeight(); if (new_state.height < beg_state.height - EPSILON) beg_state.copy(new_state); } } } beg_state.applyToGraph(); _excludeDandlingIntersections(); } void MoleculeLayoutGraph::_excludeDandlingIntersections() { QS_DEF(Array<int>, edges); int i, j, res, beg1, end1, beg2, end2; float norm1, norm2; Vec2f a, b; edges.clear(); // Find dandling edges for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { const Vertex &vert = getVertex(i); if (vert.degree() == 1) edges.push(vert.neiEdge(vert.neiBegin())); } for (i = 0; i < edges.size(); i++) { const Edge &edge1 = getEdge(edges[i]); if (getVertex(edge1.beg).degree() == 1) { beg1 = edge1.end; end1 = edge1.beg; } else { beg1 = edge1.beg; end1 = edge1.end; } for (j = i + 1; j < edges.size(); j++) { res = _calcIntersection(edges[i], edges[j]); const Edge &edge2 = getEdge(edges[j]); if (getVertex(edge2.beg).degree() == 1) { beg2 = edge2.end; end2 = edge2.beg; } else { beg2 = edge2.beg; end2 = edge2.end; } switch (res) { case 223: // squeeze (v1,v2) a = getPos(beg1); b = getPos(end1); getPos(end1).lineCombin2(a, 0.3f, b, 0.7f); break; case 225: // squeeze (v3,v4) a = getPos(beg2); b = getPos(end2); getPos(end2).lineCombin2(a, 0.3f, b, 0.7f); break; case 23: // swap v2 and v4, equalize lengths __swap(getPos(end1), getPos(end2), a); norm1 = Vec2f::dist(getPos(beg1), getPos(end1)); norm2 = Vec2f::dist(getPos(beg2), getPos(end2)); if (norm1 < 0.0001 || norm2 < 0.0001) break; if (norm1 < norm2) { a = getPos(beg2); b = getPos(end2); float t = norm1 / norm2; getPos(end2).lineCombin2(a, 1.f - t, b, t); break; } a = getPos(beg1); b = getPos(end1); float t = norm2 / norm1; getPos(end1).lineCombin2(a, 1.f - t, b, t); break; } } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_graph_smart.cpp��������������������������������������0000664�0000000�0000000�00000044526�12710376503�0024615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/biconnected_decomposer.h" #include "graph/morgan_code.h" #include "layout/molecule_layout_graph.h" #include <memory> using namespace indigo; IMPL_ERROR(MoleculeLayoutGraphSmart, "layout_graph_smart"); MoleculeLayoutGraphSmart::MoleculeLayoutGraphSmart() : MoleculeLayoutGraph() { } MoleculeLayoutGraphSmart::~MoleculeLayoutGraphSmart () { } MoleculeLayoutGraph* MoleculeLayoutGraphSmart::getInstance() { return new MoleculeLayoutGraphSmart(); } void MoleculeLayoutGraphSmart::clear () { MoleculeLayoutGraph::clear(); _layout_component_number.clear(); _layout_component_count = 0; } void MoleculeLayoutGraphSmart::makeLayoutSubgraph (MoleculeLayoutGraph &graph, Filter &vertex_filter) { makeLayoutSubgraph(graph, vertex_filter, 0); } void MoleculeLayoutGraphSmart::makeLayoutSubgraph (MoleculeLayoutGraph &graph, Filter &vertex_filter, Filter *edge_filter) { _molecule = graph._molecule; _graph = &graph; _molecule_edge_mapping = graph._molecule_edge_mapping; QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, vertex_mapping); QS_DEF(Array<int>, edges); QS_DEF(Array<int>, edge_mapping); clear(); vertex_filter.collectGraphVertices(graph, vertices); if (edge_filter != 0) (*edge_filter).collectGraphEdges(graph, edges); if (edge_filter != 0) makeSubgraph(graph, vertices, &vertex_mapping, &edges, &edge_mapping); else makeSubgraph(graph, vertices, &vertex_mapping); LayoutVertex new_vertex; LayoutEdge new_edge; new_vertex.is_cyclic = false; for (int i = 0; i < vertices.size(); i++) { new_vertex.ext_idx = vertices[i]; new_vertex.orig_idx = graph._layout_vertices[vertices[i]].orig_idx; new_vertex.type = graph._layout_vertices[vertices[i]].type; new_vertex.morgan_code = graph._layout_vertices[vertices[i]].morgan_code; new_vertex.pos.copy(graph._layout_vertices[vertices[i]].pos); registerLayoutVertex(vertex_mapping[vertices[i]], new_vertex); } int index = 0; for (int i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { const Edge &edge = getEdge(i); int ext_idx = graph.findEdgeIndex(vertices[edge.beg], vertices[edge.end]); new_edge.ext_idx = ext_idx; new_edge.orig_idx = graph._layout_edges[ext_idx].orig_idx; new_edge.type = graph._layout_edges[ext_idx].type; registerLayoutEdge(i, new_edge); } _layout_component_number.clear_resize(edgeEnd()); _layout_component_number.fffill(); _layout_component_count = 0; } void MoleculeLayoutGraphSmart::layout (BaseMolecule &molecule, float bond_length, const Filter *filter, bool respect_existing) { if (molecule.vertexCount() == 0) return; int n_components = countComponents(); if (fabs(bond_length) < EPSILON) throw Error("zero bond length"); _molecule = &molecule; if (n_components > 1) _layoutMultipleComponents(molecule, respect_existing, filter, bond_length); else _layoutSingleComponent(molecule, respect_existing, filter, bond_length); } void MoleculeLayoutGraphSmart::_calcMorganCodes () { MorganCode morgan(*this); QS_DEF(Array<long>, morgan_codes); morgan.calculate(morgan_codes, 3, 7); _total_morgan_code = 0; for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { _layout_vertices[i].morgan_code = morgan_codes[i]; _total_morgan_code += morgan_codes[i]; } } void MoleculeLayoutGraphSmart::_makeComponentsTree (BiconnectedDecomposer &decon, PtrArray<MoleculeLayoutGraph> &components, Array<int> &tree) { int i, j, v, k; bool from; for (i = 0; i < tree.size(); i++) tree[i] = -1; for (i = 0; i < components.size(); i++) { for (k = components[i]->vertexBegin(); k < components[i]->vertexEnd(); k = components[i]->vertexNext(k)) { v = components[i]->getLayoutVertex(k).ext_idx; if (decon.isArticulationPoint(v)) { // if connection vertex belongs to i-th component from = false; for (j = 0; j < decon.getIncomingComponents(v).size(); j++) { // and component doesn't come from this vertex if (decon.getIncomingComponents(v)[j] == i) from = true; } // TODO: try to remove tree[]; if (!from) tree[v] = i; } } } } void MoleculeLayoutGraphSmart::_layoutMultipleComponents (BaseMolecule & molecule, bool respect_existing, const Filter * filter, float bond_length) { QS_DEF(Array<Vec2f>, src_layout); QS_DEF(Array<int>, molecule_edge_mapping); int n_components = countComponents(); const Array<int> &decomposition = getDecomposition(); int i, j, k; molecule_edge_mapping.clear_resize(edgeEnd()); for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) molecule_edge_mapping[i] = getEdgeExtIdx(i); _molecule_edge_mapping = molecule_edge_mapping.ptr(); PtrArray<MoleculeLayoutGraph> components; components.clear(); for (i = 0; i < n_components; i++) { Filter comp_filter(decomposition.ptr(), Filter::EQ, i); std::unique_ptr<MoleculeLayoutGraph> current_component(getInstance()); components.add(current_component.release()); MoleculeLayoutGraph &component = *components.top(); component.cancellation = cancellation; component.makeLayoutSubgraph(*this, comp_filter); component.max_iterations = max_iterations; component._molecule = &molecule; component._molecule_edge_mapping = molecule_edge_mapping.ptr(); src_layout.clear_resize(component.vertexEnd()); if (respect_existing) for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) src_layout[j] = getPos(component.getVertexExtIdx(j)); else src_layout.zerofill(); if (filter != 0) { component._fixed_vertices.resize(component.vertexEnd()); component._fixed_vertices.zerofill(); for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) if (!filter->valid(component.getVertexExtIdx(j))) { component._fixed_vertices[j] = 1; component._n_fixed++; component._layout_vertices[j].pos = getPos(component.getVertexExtIdx(j)); } } if (component.vertexCount() > 1) { component._calcMorganCodes(); component._assignAbsoluteCoordinates(bond_length); } component._assignFinalCoordinates(bond_length, src_layout); } // position components float x_min, x_max, x_start = 0.f, dx; float y_min, y_max, y_start = 0.f, max_height = 0.f, dy; int col_count; int row, col; int n_fixed = 0; // fixed first if (filter != 0) { x_min = 1.0E+20f; y_min = 1.0E+20f; // find fixed components for (i = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) { n_fixed++; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) { const Vec2f &pos = component.getPos(j); if (pos.x < x_min) x_min = pos.x; if (pos.y < y_min) y_min = pos.y; if (pos.y > y_start) y_start = pos.y; } } } // position fixed if (n_fixed > 0) { dy = -y_min; dx = -x_min; for (i = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); } y_start += dy + 2 * bond_length; } } col_count = (int)ceil(sqrt((float)n_components - n_fixed)); for (i = 0, k = 0; i < n_components; i++) { MoleculeLayoutGraph &component = *components[i]; if (component._n_fixed > 0) continue; // Component shifting row = k / col_count; col = k % col_count; x_min = 1.0E+20f; x_max = -1.0E+20f; y_min = 1.0E+20f; y_max = -1.0E+20f; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) { const Vec2f &pos = component.getPos(j); if (pos.x < x_min) x_min = pos.x; if (pos.x > x_max) x_max = pos.x; if (pos.y < y_min) y_min = pos.y; if (pos.y > y_max) y_max = pos.y; } if (col == 0 && row > 0) { y_start += max_height + 2 * bond_length; max_height = 0.f; } if (col > 0) dx = x_start - x_min + 2 * bond_length; else dx = -x_min; dy = y_start - y_min; for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); x_start = x_max + dx; if (y_max - y_min > max_height) max_height = y_max - y_min; k++; } } void MoleculeLayoutGraphSmart::_layoutSingleComponent (BaseMolecule &molecule, bool respect_existing, const Filter * filter, float bond_length) { QS_DEF(Array<Vec2f>, src_layout); QS_DEF(Array<int>, molecule_edge_mapping); int i; molecule_edge_mapping.clear_resize(molecule.edgeEnd()); for (i = 0; i < molecule_edge_mapping.size(); i++) molecule_edge_mapping[i] = i; _molecule = &molecule; _molecule_edge_mapping = molecule_edge_mapping.ptr(); src_layout.clear_resize(vertexEnd()); if (respect_existing) for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) src_layout[i] = getPos(i); else src_layout.zerofill(); if (filter != 0) { _fixed_vertices.resize(vertexEnd()); _fixed_vertices.zerofill(); for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) if (!filter->valid(i)) { _fixed_vertices[i] = 1; _n_fixed++; } } if (vertexCount() > 1) { _calcMorganCodes(); _assignAbsoluteCoordinates(bond_length); } _assignFinalCoordinates(bond_length, src_layout); } MoleculeLayoutSmoothingSegment::MoleculeLayoutSmoothingSegment(MoleculeLayoutGraphSmart& mol, Vec2f& start, Vec2f& finish) : _graph(mol), _start(start), _finish(finish) { _center.zero(); Vec2f diameter = (_finish - _start); _length = diameter.length(); Vec2f rotate_vector = diameter / diameter.lengthSqr(); rotate_vector.y *= -1; _pos.clear_resize(_graph.vertexEnd()); bool is_line = false; for (int v : _graph.vertices()) if (_graph.getVertex(v).degree() == 1) is_line = true; if (!is_line) { for (int v : _graph.vertices()) { _pos[v].copy(_graph.getPos(v)); _pos[v] -= _start; _pos[v].rotate(rotate_vector); } } else { // this is straight line QS_DEF(Array<int>, vert); vert.clear(); // list or vertices in order of connection for (int v : _graph.vertices()) if (_graph.getVertex(v).degree() == 1) { vert.push(v); break; } vert.push(_graph.getVertex(vert[0]).neiVertex(_graph.getVertex(vert[0]).neiBegin())); while (vert.size() < _graph.vertexCount()) { for (int n = _graph.getVertex(vert.top()).neiBegin(); n != _graph.getVertex(vert.top()).neiEnd(); n = _graph.getVertex(vert.top()).neiNext(n)) if (_graph.getVertex(vert.top()).neiVertex(n) != vert[vert.size() - 2]) { vert.push(_graph.getVertex(vert.top()).neiVertex(n)); } } for (int i = 0; i < vert.size(); i++) _pos[vert[i]].set(i * 1. / (vert.size() - 1), 0); } // double ternary search of center of component double MLx = 0, Ly = 0, MRx = 0, Ry = 0, Lx, Rx; for (int v : _graph.vertices()) { MLx = __min(MLx, _pos[v].x); MRx = __max(MRx, _pos[v].x); Ly = __min(Ly, _pos[v].y); Ry = __max(Ry, _pos[v].y); } while (Ry - Ly > EPSILON) { double dy = (Ry - Ly) / 3; double ry[2]; double My = Ly + dy; for (int i = 0; i < 2; i++) { Lx = MLx, Rx = MRx; double rx[2]; while (Rx - Lx > EPSILON) { double dx = (Rx - Lx) / 3; double Mx = Lx + dx; for (int j = 0; j < 2; j++) { rx[j] = calc_radius(Vec2f(Mx, My)); Mx += dx; } if (rx[0] > rx[1]) Lx += dx; else Rx -= dx; } ry[i] = calc_radius(Vec2f(Rx, My)); My += dy; } if (ry[0] > ry[1]) Ly += dy; else Ry -= dy; } _center = Vec2f(Rx, Ry); _radius = calc_radius(_center); /* _radius = 0; Vec2f center(0.5, 0); for (int v : _graph.vertices()) { double dist = (center - _pos[v]).length(); if (dist > _radius) _radius = dist; } _center = center;*/ } double MoleculeLayoutSmoothingSegment::calc_radius(Vec2f c) { double answer = 0; for (int v : _graph.vertices()) answer = __max(answer, (c - _pos[v]).lengthSqr()); return sqrt(answer); } Vec2f MoleculeLayoutSmoothingSegment::_getPosition(Vec2f p) { Vec2f point; point.copy(p); point.rotate(_finish - _start); return point + _start; } void MoleculeLayoutSmoothingSegment::updateStartFinish() { _length = (_start - _finish).length(); } double MoleculeLayoutSmoothingSegment::get_radius() { return _radius * _length; } bool MoleculeLayoutSmoothingSegment::can_touch_to(MoleculeLayoutSmoothingSegment& seg) { return ((_start + _finish) / 2 - (seg._start - seg._finish) / 2).length() <= get_radius() + seg.get_radius(); } bool MoleculeLayoutSmoothingSegment::isVertexUp(int v) { return _pos[v].y > 0; } Vec2f MoleculeLayoutSmoothingSegment::getPosition(int v) { return _getPosition(_pos[v]); } Vec2f MoleculeLayoutSmoothingSegment::getIntPosition(int v) const { return _pos[v]; } void MoleculeLayoutSmoothingSegment::shiftStartBy(Vec2f shift) { _start += shift; } void MoleculeLayoutSmoothingSegment::shiftFinishBy(Vec2f shift){ _finish += shift; } float MoleculeLayoutSmoothingSegment::getLength() const { return _length; } float MoleculeLayoutSmoothingSegment::getLengthCoef() const { float l = (_finish - _start).length(); return (_graph.vertexCount() > 2 ? 5 : 1) * (_length - l)/l; } float MoleculeLayoutSmoothingSegment::get_min_x() { float answer = 1000000.0; for (int v : _graph.vertices()) { float xx = getPosition(v).x; answer = __min(answer, xx); } return answer; } float MoleculeLayoutSmoothingSegment::get_min_y() { float answer = 1000000.0; for (int v : _graph.vertices()) { float yy = getPosition(v).y; answer = __min(answer, yy); } return answer; } float MoleculeLayoutSmoothingSegment::get_max_x() { float answer = -1000000.0; for (int v : _graph.vertices()) { float xx = getPosition(v).x; answer = __max(answer, getPosition(v).x); } return answer; } float MoleculeLayoutSmoothingSegment::get_max_y() { float answer = -1000000.0; for (int v : _graph.vertices()) { float yy = getPosition(v).y; answer = __max(answer, yy); } return answer; } /*Vec2f& MoleculeLayoutSmoothingSegment::getStart() { return _start; } Vec2f& MoleculeLayoutSmoothingSegment::getFinish() { return _finish; }*/ Vec2f MoleculeLayoutSmoothingSegment::getCenter() { return _getPosition(_center); } Vec2f MoleculeLayoutSmoothingSegment::getIntCenter() { return _center; } int MoleculeLayoutSmoothingSegment::get_layout_component_number() { return _layout_component_number; } void MoleculeLayoutSmoothingSegment::set_layout_component_number(int number) { _layout_component_number = number; } void MoleculeLayoutSmoothingSegment::inverse() { for (int v : _graph.vertices()) _pos[v].y *= -1; } void MoleculeLayoutSmoothingSegment::set_start_finish_number(int s, int f) { for (int v : _graph.vertices()) { if (_graph.getVertexExtIdx(v) == s) _start_number = v; if (_graph.getVertexExtIdx(v) == f) _finish_number = v; } if (get_layout_component_number() == -1) { _pos[_start_number].set(0, 0); _pos[_finish_number].set(1, 0); } for (int v : _graph.vertices()) _center += _pos[v]; _center /= _graph.vertexCount(); _radius = 0; for (int v : _graph.vertices()) { double dist = (_center - _pos[v]).length(); if (dist > _radius) _radius = dist; } calculate_square(); } double MoleculeLayoutSmoothingSegment::get_square() { return _square; } void MoleculeLayoutSmoothingSegment::calculate_square() { if (_layout_component_number >= 0) _square = _graph._get_square(); else _square = 0; } int MoleculeLayoutSmoothingSegment::get_start() const { return _start_number; } int MoleculeLayoutSmoothingSegment::get_finish() const { return _finish_number; } #ifdef M_LAYOUT_DEBUG #include "molecule/molecule.h" #include "molecule/molfile_saver.h" #include "base_cpp/output.h" #include "molecule/elements.h" void MoleculeLayoutGraphSmart::saveDebug () { int i; Molecule mol; QS_DEF(Array<int>, mapping); mapping.clear_resize(vertexEnd()); for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { if (getVertexType(i) == ELEMENT_NOT_DRAWN) continue; mapping[i] = mol.addAtom(ELEM_C); mol.setAtomXyz(mapping[i], getPos(i).x, getPos(i).y, 0); } for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) { if (getEdgeType(i) == ELEMENT_NOT_DRAWN) continue; const Edge &edge = getEdge(i); mol.addBond(mapping[edge.beg], mapping[edge.end], BOND_SINGLE); } static int id = 0; char out_name[100]; sprintf_s(out_name, "D:\\mf\\draw\\trace_my\\%03d.mol", id); FileOutput fo(out_name); MolfileSaver ms(fo); ms.saveMolecule(mol); id++; } #endif��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_macrocycle_lattice.cpp�������������������������������0000664�0000000�0000000�00000125243�12710376503�0026130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_macrocycles.h" #include "base_cpp/profiling.h" #include <limits.h> #include <string> #include <vector> #include <set> #include <map> #include <stack> #include <cmath> #include <string> #include <sstream> #include <map> #include <stdio.h> #include <math/random.h> #include "layout/molecule_layout.h" #include <algorithm> using namespace std; using namespace indigo; IMPL_ERROR(MoleculeLayoutMacrocyclesLattice, "molecule_layout_macrocycles_lattice"); const double MoleculeLayoutMacrocyclesLattice::CHANGE_FACTOR = 1.0; const double MoleculeLayoutMacrocyclesLattice::SMOOTHING_MULTIPLIER = 0.2; CP_DEF(MoleculeLayoutMacrocyclesLattice); CP_DEF(MoleculeLayoutMacrocyclesLattice::CycleLayout); static const int MAX_ROT = 100; // rotation can be negative. We must add MAX_ROT * SIX to it for correct reminder obtaining static const int dx[6] = { 1, 0, -1, -1, 0, 1 }; static const int dy[6] = { 0, 1, 1, 0, -1, -1 }; static const int getDx(int x) { return dx[(x + SIX * MAX_ROT) % SIX]; } static const int getDy(int y) { return dy[(y + SIX * MAX_ROT) % SIX]; } MoleculeLayoutMacrocyclesLattice::MoleculeLayoutMacrocyclesLattice(int size) : CP_INIT, TL_CP_GET(_vertex_weight), // tree size TL_CP_GET(_vertex_stereo), // there is an angle in the vertex TL_CP_GET(_edge_stereo), // trans-cis configuration TL_CP_GET(_positions), TL_CP_GET(_angle_importance), TL_CP_GET(_component_finish), TL_CP_GET(_target_angle), TL_CP_GET(_vertex_added_square), TL_CP_GET(_vertex_drawn) { length = size; _vertex_weight.clear_resize(size); _vertex_weight.zerofill(); _vertex_stereo.clear_resize(size); _vertex_stereo.zerofill(); _edge_stereo.clear_resize(size); _edge_stereo.zerofill(); _positions.clear_resize(size); _angle_importance.clear_resize(size); _angle_importance.fill(1); _component_finish.clear_resize(size); for (int i = 0; i < size; i++) _component_finish[i] = i; _target_angle.clear_resize(size); _target_angle.zerofill(); _vertex_added_square.clear_resize(size); _vertex_drawn.clear_resize(size); } void MoleculeLayoutMacrocyclesLattice::doLayout() { if (length <= 9) { bool has_trans = false; for (int i = 0; i < length; i++) if (_edge_stereo[i] == 2) has_trans = true; if (!has_trans) { double alpha = 2 * PI / length; double r = 1 / sqrt(2 * (1 - cos(alpha))); for (int i = 0; i < length; i++) { _positions[i] = Vec2f(0, r); _positions[i].rotate(alpha * i); } return; } } calculate_rotate_length(); rotate_cycle(rotate_length); AnswerField answfld(length, 0, 0, 0, _vertex_weight.ptr(), _vertex_stereo.ptr(), _edge_stereo.ptr()); answfld.fill(); QS_DEF(Array<answer_point>, points); points.clear_resize(0); for (int rot = -length; rot <= length; rot++) { for (int p = 1; p < 2; p++) { TriangleLattice& lat = answfld.getLattice(length, rot, p); for (int x = lat.getFirstValidX(); lat.isIncreaseForValidX(x); x++) { for (int y = lat.getFirstValidY(x); lat.isIncreaseForValidY(y); lat.switchNextY(y)) { if (lat.getCell(x, y) < SHORT_INFINITY) { answer_point point(rot, p, x, y); points.push(point); } } } } } _positions.clear_resize(length + 1); CycleLayout cl; initCycleLayout(cl); int best_number = -1; double best_rating = preliminary_layout(cl); //printf("%d\n", points.size()); points.qsort(&AnswerField::_cmp_answer_points, &answfld); Array<answer_point> path; path.clear_resize(length + 1); //printf("%d\n", points.size()); for (int i = 0; i < 100 && i < points.size(); i++) { answfld._restore_path(path.ptr(), points[i]); cl.init(path.ptr()); smoothing(cl); double current_rating = rating(cl); if (current_rating + EPSILON < best_rating) { best_rating = current_rating; best_number = i; } } if (best_number >= 0) { answfld._restore_path(path.ptr(), points[best_number]); cl.init(path.ptr()); smoothing(cl); } else preliminary_layout(cl); for (int i = 0, j = 0; i < cl.vertex_count; i++) for (int t = cl.external_vertex_number[i]; t < cl.external_vertex_number[i + 1]; t++, j++) _positions[j] = cl.point[i] + (cl.point[i + 1] - cl.point[i]) * (t - cl.external_vertex_number[i]) / cl.edge_length[i]; rotate_cycle(-rotate_length); } void MoleculeLayoutMacrocyclesLattice::calculate_rotate_length() { rotate_length = 0; int max_value = -SHORT_INFINITY; for (int i = 0; i < length; i++) { if ((_edge_stereo[i] != 2) && _vertex_stereo[(i + 1) % length]) { int value = 2 * _edge_stereo[i] + 2 * _vertex_weight[i] + 2 * _vertex_weight[(i + 1) % length] - _vertex_weight[(i + length - 1) % length] - _vertex_weight[(i + 2) % length]; if (rotate_length == -1 || value > max_value) { rotate_length = i; max_value = value; } } } rotate_length++; } void MoleculeLayoutMacrocyclesLattice::_rotate_ar_i(Array<int>& ar, Array<int>& tmp, int shift) { for (int i = shift; i < length; i++) tmp[i - shift] = ar[i]; for (int i = 0; i < shift; i++) tmp[i - shift + length] = ar[i]; for (int i = 0; i < length; i++) ar[i] = tmp[i]; } void MoleculeLayoutMacrocyclesLattice::_rotate_ar_d(Array<double>& ar, Array<double>& tmp, int shift) { for (int i = shift; i < length; i++) tmp[i - shift] = ar[i]; for (int i = 0; i < shift; i++) tmp[i - shift + length] = ar[i]; for (int i = 0; i < length; i++) ar[i] = tmp[i]; } void MoleculeLayoutMacrocyclesLattice::_rotate_ar_v(Array<Vec2f>& ar, Array<Vec2f>& tmp, int shift) { for (int i = shift; i < length; i++) tmp[i - shift] = ar[i]; for (int i = 0; i < shift; i++) tmp[i - shift + length] = ar[i]; for (int i = 0; i < length; i++) ar[i] = tmp[i]; } void MoleculeLayoutMacrocyclesLattice::rotate_cycle(int shift) { shift = (shift % length + length) % length; QS_DEF(Array<int>, temp); temp.clear_resize(length); QS_DEF(Array<double>, tempd); tempd.clear_resize(length); QS_DEF(Array<Vec2f>, temp_v); temp_v.clear_resize(length); _rotate_ar_i(_vertex_weight, temp, shift); _rotate_ar_i(_vertex_stereo, temp, shift); _rotate_ar_i(_edge_stereo, temp, shift); _rotate_ar_d(_target_angle, tempd, shift); _rotate_ar_d(_angle_importance, tempd, shift); _rotate_ar_v(_positions, temp_v, shift); } Vec2f &MoleculeLayoutMacrocyclesLattice::getPos(int v) const { return _positions[v]; } void MoleculeLayoutMacrocyclesLattice::setEdgeStereo(int e, int stereo) { _edge_stereo[e] = stereo; } void MoleculeLayoutMacrocyclesLattice::addVertexOutsideWeight(int v, int weight) { _vertex_weight[v] += WEIGHT_FACTOR * weight; } void MoleculeLayoutMacrocyclesLattice::setVertexEdgeParallel(int v, bool parallel) { _vertex_stereo[v] = !parallel; } bool MoleculeLayoutMacrocyclesLattice::getVertexStereo(int v) { return _vertex_stereo[v]; } void MoleculeLayoutMacrocyclesLattice::setVertexAddedSquare(int v, double s) { _vertex_added_square[v] = s; } void MoleculeLayoutMacrocyclesLattice::setVertexDrawn(int v, bool drawn) { _vertex_drawn[v] = drawn; } void MoleculeLayoutMacrocyclesLattice::setComponentFinish(int v, int f) { _component_finish[v] = f; } void MoleculeLayoutMacrocyclesLattice::setTargetAngle(int v, double angle) { _target_angle[v] = angle; } void MoleculeLayoutMacrocyclesLattice::setAngleImportance(int v, double imp) { _angle_importance[v] = imp; } void AnswerField::_restore_path(answer_point* path, answer_point finish) { path[length] = finish; for (int len = length - 1; len >= 0; len--) { // printf("len = %d, x = %d, y = %d, p = %d, rot = %d, value = %d \n", len + 1, path[len + 1].x, path[len + 1].y, path[len + 1].p, path[len + 1].rot, get_field(len + 1, path[len + 1])); path[len] = path[len + 1]; path[len].x -= getDx(path[len + 1].rot); path[len].y -= getDy(path[len + 1].rot); if (_vertex_stereo[len]) { path[len].rot -= path[len + 1].p ? 1 : -1; if (len > 0 && _edge_stereo[len - 1] == 0) { path[len].p ^= 1; int rot = path[len + 1].rot; int add = get_weight(_vertex_weight[len], path[len + 1].p); // choosing rotation closer to circle double l = len * (sqrt(3.0) + 1.5) * PI / 12; Vec2f vec(path[len].y, 0); vec.rotate(PI / 3); vec += Vec2f(path[len].x, 0); double x = vec.length(); double eps = 1e-3; double alpha = 2 * PI; if (x > eps) { double L = eps; double R = 2 * PI - eps; while (R - L > eps) { double M = (L + R) / 2; if (M * x / (2 * sin(M / 2)) > l) R = M; else L = M; } alpha = vec.tiltAngle2() + R / 2; } int preferred_p = alpha > PI / 3 * (path[len].rot);// +PI / 6 / length; path[len].p = preferred_p ^ 1; // enumerating two cases for (int i = 0; i < 3; i++) { if (i == 2) throw Error("Cannot find path"); unsigned short a = get_field(len + 1, path[len + 1]); unsigned short b = get_field(len, path[len]); if (a == add + b) break; path[len].p ^= 1; } } else if (len > 0 && _edge_stereo[len - 1] == 2) path[len].p ^= 1; } } } // TriangleLattice definition // x - y = difference_reminder (mod 3) IMPL_ERROR(TriangleLattice, "triangle_lattice"); TriangleLattice::TriangleLattice() { _BORDER.set_empty(); } TriangleLattice::TriangleLattice(rectangle rec, int rem, byte* data_link) { init(rec, rem, data_link); } void TriangleLattice::init(rectangle rec, int rem, byte* data_link) { _BORDER = rec; _difference_reminder = rem; if (_BORDER.empty) return; //byte_start = data_link + sizeof(unsigned short*) * (_BORDER.max_x - _BORDER.min_x + 1); //byte_end = data_link + TriangleLattice::getAllocationSize(rec); _starts = (unsigned short**)data_link; _starts[0] = (unsigned short*)(_starts + _BORDER.max_x - _BORDER.min_x + 1); _starts -= _BORDER.min_x; for (int x = _BORDER.min_x; x < _BORDER.max_x; x++) { int left = _BORDER.min_y; int right = _BORDER.max_y; while (!isValid(x, left)) left++; while (!isValid(x, right)) right--; _starts[x + 1] = _starts[x] + (right - left + 3)/3; } for (int x = _BORDER.min_x; x <= _BORDER.max_x; x++) { _starts[x] -= (getFirstValidY(x) + _difference_reminder - x) / 3; } } void TriangleLattice::init_void() { _BORDER.set_empty(); _difference_reminder = 0; } unsigned short& TriangleLattice::getCell(int x, int y) { if (_BORDER.empty) return _sink; #if defined(DEBUG) || defined(_DEBUG) if (!isValid(x, y)) throw Error("difference of coordinates reminder is failed: x = %d, y = %d, rem = %d", x, y, _difference_reminder); if (!_BORDER.expand(3).contains(x, y)) throw Error("point (%d, %d) is not close to framework [%d, %d]x[%d, %d].", x, y, _BORDER.min_x, _BORDER.max_x, _BORDER.min_y, _BORDER.max_y); #endif if (!_BORDER.contains(x, y)) return _sink; //if ((byte*)(_starts[x] + (y + _difference_reminder - x) / 3) < byte_start || (byte*)(_starts[x] + (y + _difference_reminder - x) / 3) >= byte_end) printf("ACHTUNG!\n"); return _starts[x][(y + _difference_reminder - x) / 3]; } int TriangleLattice::getFirstValidY(int x) { int y = _BORDER.min_y; while (!isValid(x, y)) y++; return y; } bool TriangleLattice::isValid(int x, int y) { return (y + _difference_reminder - x) % 3 == 0; } bool TriangleLattice::isIncreaseForValidY(int y) { return !_BORDER.empty && y <= _BORDER.max_y; } int TriangleLattice::getFirstValidX() { return _BORDER.min_x; } bool TriangleLattice::isIncreaseForValidX(int x) { return !_BORDER.empty && x <= _BORDER.max_x; } void TriangleLattice::switchNextY(int& y) { y += 3; } // AnswerField definition IMPL_ERROR(AnswerField, "answer_field"); CP_DEF(AnswerField); AnswerField::AnswerField(int len, int target_x, int target_y, double target_rotation, int* vertex_weight_link, int* vertex_stereo_link, int* edge_stereo_link) : CP_INIT, TL_CP_GET(_vertex_weight), // tree size TL_CP_GET(_vertex_stereo), // there is an angle in the vertex TL_CP_GET(_edge_stereo), // trans-cis configuration TL_CP_GET(_rotation_parity), TL_CP_GET(_coord_diff_reminder), TL_CP_GET(_lattices), TL_CP_GET(_hidden_data_field_array) { length = len; _vertex_weight.clear_resize(length); for (int i = 0; i < length; i++) _vertex_weight[i] = vertex_weight_link[i]; _vertex_stereo.clear_resize(length); for (int i = 0; i < length; i++) _vertex_stereo[i] = vertex_stereo_link[i]; _edge_stereo.clear_resize(length); for (int i = 0; i < length; i++) _edge_stereo[i] = edge_stereo_link[i]; _rotation_parity.clear_resize(length + 1); _rotation_parity[0] = 0; for (int i = 0; i < length; i++) { if (_vertex_stereo[i]) _rotation_parity[i + 1] = _rotation_parity[i] ^ 1; else _rotation_parity[i + 1] = _rotation_parity[i]; } _coord_diff_reminder.clear_resize(length + 1); _coord_diff_reminder[0] = 0; for (int i = 1; i <= length; i++) { if (_rotation_parity[i]) _coord_diff_reminder[i] = (_coord_diff_reminder[i - 1] + 2) % 3; else _coord_diff_reminder[i] = (_coord_diff_reminder[i - 1] + 1) % 3; } ObjArray<Array<rectangle> > border_sample_array; border_sample_array.clear(); for (int i = 0; i <= length; i++) { border_sample_array.push().clear_resize(2 * i + 1); } Array<rectangle*> border_sample; border_sample.clear_resize(length + 1); for (int i = 0; i <= length; i++) { border_sample[i] = border_sample_array[i].ptr() + i; } border_sample[0][0].set(0, 0, 0, 0); for (int l = 0; l <= length; l++) { for (int rot = -l; rot <= l; rot++) { int radius = l - abs(rot) + 1; int center_x = rot > 0 ? -1 : 0; int center_y = rot > 0 ? 1 : -1; border_sample[l][rot].set(rectangle::square(0, 0, l).intersec(rectangle::square(center_x, center_y, radius))); } } border_array.clear(); for (int i = 0; i <= length; i++) { border_array.push().clear_resize(2 * i + 1); } border.clear_resize(length + 1); for (int i = 0; i <= length; i++) { border[i] = border_array[i].ptr() + i; } for (int l = 0; l <= length; l++) { for (int rot = -l; rot <= l; rot++) { border[l][rot].set(border_sample[l][rot]); if (l >= ACCEPTABLE_ERROR) { if (abs(rot) <= length + ACCEPTABLE_ERROR - l) border[l][rot].intersec(border_sample[length + ACCEPTABLE_ERROR - l][rot].shift(target_x, target_y)); else border[l][rot].set_empty(); } //if (border[l][rot].empty) printf("========== [%d, %d]x[%d, %d]\n", border[l][rot].min_x, border[l][rot].max_x, border[l][rot].min_y, border[l][rot].max_y); } } int global_size = 0; for (int l = 0; l <= length; l++) { for (int rot = -l; rot <= l; rot++) if (abs(rot) % 2 == _rotation_parity[l]) { for (int p = 0; p < 2; p++) { global_size += TriangleLattice::getAllocationSize(border[l][rot]); } } } //printf("Global Size = %d bytes \n", global_size); _hidden_data_field_array.clear_resize(global_size); byte* free_area = _hidden_data_field_array.ptr(); _lattices.clear(); for (int l = 0; l <= length; l++) { _lattices.push(); for (int rot = -l; rot <= l; rot++) { _lattices.top().push(); for (int p = 0; p < 2; p++) { if (abs(rot) % 2 == _rotation_parity[l]) { _lattices.top().top().push(border[l][rot], _coord_diff_reminder[l], free_area); free_area += TriangleLattice::getAllocationSize(border[l][rot]); } else _lattices.top().top().push(); } } } _sink_lattice.init_void(); } int AnswerField::_cmp_answer_points(answer_point& p1, answer_point& p2, void* context) { AnswerField& fld = *((AnswerField*)context); return p1.quality(fld) - p2.quality(fld); } unsigned short& AnswerField::get_field(int len, answer_point p) { return getLattice(len, p.rot, p.p).getCell(p.x, p.y); }; unsigned short& AnswerField::get_field(answer_point p) { return get_field(length, p); }; TriangleLattice& AnswerField::getLattice(int l, int rot, int p) { if (l < 0 || l > length || rot < -l || rot > l || !!p != p) return _sink_lattice; return _lattices[l][rot + l][p]; } void AnswerField::fill() { for (int l = 0; l <= length; l++) { for (int rot = -l; rot <= l; rot++) { for (int p = 0; p < 2; p++) { TriangleLattice& lat = getLattice(l, rot, p); for (int x = lat.getFirstValidX(); lat.isIncreaseForValidX(x); x++) { for (int y = lat.getFirstValidY(x); lat.isIncreaseForValidY(y); lat.switchNextY(y)) lat.getCell(x, y) = SHORT_INFINITY; } } } } //getLattice(0, 0, 0).getCell(0, 0) = 0; getLattice(0, 0, 1).getCell(0, 0) = 0; for (int l = 0; l < length; l++) { for (int rot = -l; rot <= l; rot++) { for (int p = 0; p < 2; p++) { bool can[3]; can[0] = can[1] = can[2] = false; bool* acceptable_rotation = &can[1]; if (_vertex_stereo[l]) { int current_edge_stereo = _edge_stereo[(l - 1 + length) % length]; if (current_edge_stereo == 0) acceptable_rotation[-1] = acceptable_rotation[1] = true; else if ((current_edge_stereo == MoleculeCisTrans::TRANS) ^ (p == 0)) acceptable_rotation[-1] = true; else acceptable_rotation[1] = true; } else acceptable_rotation[0] = true; for (int chenge_rotation = -1; chenge_rotation <= 1; chenge_rotation++) if (acceptable_rotation[chenge_rotation]) { int newp = chenge_rotation == 0 ? p : chenge_rotation == 1 ? 1 : 0; int next_rot = rot + chenge_rotation; TriangleLattice& donor = getLattice(l, rot, p); TriangleLattice& retsepient = getLattice(l + 1, next_rot, newp); int xchenge = getDx(next_rot); int ychenge = getDy(next_rot); unsigned short add = get_weight(_vertex_weight[l], newp); // unsigned short add = max(0, _vertex_weight[l] * (newp ? -1 : 1)); //add += (p && chenge_rotation > 0) || (!p && chenge_rotation < 0); //add += p == newp; for (int x = donor.getFirstValidX(); donor.isIncreaseForValidX(x); x++) { for (int y = donor.getFirstValidY(x); donor.isIncreaseForValidY(y); donor.switchNextY(y)) { unsigned short& c = retsepient.getCell(x + xchenge, y + ychenge); unsigned short& d = donor.getCell(x, y); if (d < SHORT_INFINITY && c > (unsigned short)(d + add)) c = (unsigned short)(d + add); //c = min(c, (unsigned short) (donor.getCell(x, y) + add)); } } } } } } } //CP_DEF(MoleculeLayoutMacrocyclesLattice::CycleLayout); MoleculeLayoutMacrocyclesLattice::CycleLayout::CycleLayout() : CP_INIT, TL_CP_GET(point), TL_CP_GET(rotate), TL_CP_GET(external_vertex_number), TL_CP_GET(edge_length) { } void MoleculeLayoutMacrocyclesLattice::initCycleLayout(CycleLayout& cl) { cl.external_vertex_number.clear_resize(0); cl.external_vertex_number.push(0); for (int i = 1; i < length; i++) if (_vertex_stereo[i]) cl.external_vertex_number.push(i); cl.external_vertex_number.push(length); cl.vertex_count = cl.external_vertex_number.size() - 1; cl.edge_length.clear_resize(cl.vertex_count); for (int i = 0; i < cl.vertex_count; i++) cl.edge_length[i] = cl.external_vertex_number[i + 1] - cl.external_vertex_number[i]; } void MoleculeLayoutMacrocyclesLattice::CycleLayout::init(answer_point* ext_point) { rotate.clear_resize(vertex_count + 1); for (int i = 0; i < vertex_count; i++) rotate[i] = ext_point[external_vertex_number[i + 1]].rot - ext_point[external_vertex_number[i]].rot; if (ext_point[0].p == 1) rotate[0] = 1; else rotate[0] = -1; rotate[vertex_count] = rotate[0]; point.clear_resize(vertex_count + 1); for (int i = 0; i <= vertex_count; i++) { point[i] = Vec2f(ext_point[external_vertex_number[i]].y, 0); point[i].rotate(PI / 3); point[i] += Vec2f(ext_point[external_vertex_number[i]].x, 0); } /*for (int i = 0; i < vertex_count; i++) { point[i] = Vec2f(vertex_count / (2 * PI), 0); point[i].rotate(2*PI * i / vertex_count); }*/ } void MoleculeLayoutMacrocyclesLattice::CycleLayout::init(int* up_point) { rotate.clear_resize(vertex_count + 1); for (int i = 0; i < vertex_count; i++) { if (up_point[external_vertex_number[i]]) rotate[i] = 1; else if (up_point[external_vertex_number[(i - 1 + vertex_count) % vertex_count]] || up_point[external_vertex_number[(i + 1 + vertex_count) % vertex_count]]) rotate[i] = -1; else rotate[i] = 1; } rotate[vertex_count] = rotate[0]; point.clear_resize(vertex_count + 1); int length = external_vertex_number[vertex_count]; double r = length / 2 / PI; for (int i = 0; i <= vertex_count; i++) { point[i] = Vec2f(r + up_point[external_vertex_number[i]], 0); point[i].rotate(2*PI*external_vertex_number[i] / length); } } double MoleculeLayoutMacrocyclesLattice::preliminary_layout(CycleLayout &cl) { QS_DEF(ObjArray<ObjArray<Array<bool>>>, can); can.clear(); int maxrot = 19; // |[0, 18]| int mask_count = 8; // 1 << 3 for (int i = 0; i <= length; i++) { can.push(); can.top().clear(); for (int j = 0; j < maxrot; j++) { can.top().push(); can.top().top().clear_resize(mask_count); } } bool is_pos_rotate[8]; for (int i = 0; i < 8; i++) is_pos_rotate[i] = i & 2; is_pos_rotate[0] = true; int is_cis[16]; for (int i = 0; i < 16; i++) is_cis[i] = (is_pos_rotate[i & 7] == is_pos_rotate[i >> 1]) ? MoleculeCisTrans::CIS : MoleculeCisTrans::TRANS; int best_rot = -1; QS_DEF(Array<int>, up); up.clear_resize(length + 1); up.zerofill(); for (int start_mask = 0; start_mask < mask_count; start_mask++) { for (int i = 0; i <= length; i++) { for (int j = 0; j < maxrot; j++) { for (int k = 0; k < mask_count; k++) can[i][j][k] = false; } } can[0][6][start_mask] = true; int curr_mask; for (int i = 0; i < length; i++) { for (int j = 0; j < maxrot; j++) { for (int mask = 0; mask < mask_count; mask++) if (can[i][j][mask]) { for (int up = 0; up <= 1; up++) { int previ = i ? i - 1 : length - 1; if (_edge_stereo[previ] == is_cis[curr_mask = ((mask << 1) + up)] || _edge_stereo[previ] == 0) { int rot = j; if (is_pos_rotate[curr_mask & 7]) rot++; else rot--; if (rot >= 0 && rot < maxrot) can[i + 1][rot][curr_mask & 7] = true; } } } } } for (int rot = 0; rot < maxrot; rot++) if (can[length][rot][start_mask]) { if (abs(best_rot - 12) > abs(rot - 12)) { best_rot = rot; curr_mask = start_mask; int curr_rot = rot; for (int i = length - 1;; i--) { up[i + 1] = curr_mask & 1; if (i < 0) break; for (int mask = curr_mask + 8; mask >= curr_mask; mask -= 8) { int newrot = curr_rot; if (is_pos_rotate[mask & 7]) newrot--; else newrot++; int previ = i ? i - 1 : length - 1; if (_edge_stereo[previ] == 0 || _edge_stereo[previ] == is_cis[mask]) { if (can[i][newrot][mask >> 1]) { curr_rot = newrot; curr_mask = mask >> 1; break; } } } } } } } cl.init(up.ptr()); smoothing(cl); for (int i = 0, j = 0; i < cl.vertex_count; i++) for (int t = cl.external_vertex_number[i]; t < cl.external_vertex_number[i + 1]; t++, j++) { _positions[j] = cl.point[i] + (cl.point[i + 1] - cl.point[i]) * (t - cl.external_vertex_number[i]) / cl.edge_length[i]; } return rating(cl); } int MoleculeLayoutMacrocyclesLattice::internalValue(CycleLayout& cl) { int val = 0; for (int i = 0; i < cl.vertex_count; i++) val += get_weight(_vertex_weight[cl.external_vertex_number[i]], cl.rotate[i]); return val; } double MoleculeLayoutMacrocyclesLattice::CycleLayout::area() { double value = 0; for (int i = 1; i < vertex_count - 1; i++) value += Vec2f::cross(point[i] - point[0], point[(i + 1) % vertex_count] - point[0]) / 2; return abs(value); } double MoleculeLayoutMacrocyclesLattice::CycleLayout::perimeter() { double perimeter = 0; for (int i = 0; i < vertex_count; i++) perimeter += (point[(i + 1) % vertex_count] - point[i]).length(); return perimeter; } bool MoleculeLayoutMacrocyclesLattice::is_period(CycleLayout& cl, int k) { if (cl.vertex_count % k != 0) return false; int len = cl.vertex_count / k; for (int i = 0; i + len < cl.vertex_count; i++) if (cl.rotate[i] != cl.rotate[i + len]) return false; for (int i = 0; i + len < cl.vertex_count; i++) if (cl.edge_length[i] != cl.edge_length[i + len]) return false; return true; } int MoleculeLayoutMacrocyclesLattice::period(CycleLayout& cl) { int answer = 1; if (is_period(cl, 2)) { answer = 2; if (is_period(cl, 4)) answer = 4; } if (is_period(cl, 3)) answer *= 3; return answer; } double MoleculeLayoutMacrocyclesLattice::rating(CycleLayout& cl) { double eps = 1e-9; double result = 0; int add = 0; // distances for (int i = 0; i < cl.vertex_count; i++) { double len = Vec2f::dist(cl.point[i], cl.point[(i + 1) % cl.vertex_count]) / cl.edge_length[i]; if (len < eps) add++; else if (len < 1) result = max(result, (1 / len - 1)); else result = max(result, len - 1); } // angles for (int i = 0; i < cl.vertex_count; i++) { Vec2f vp1 = cl.point[(i + 1) % cl.vertex_count] - cl.point[i]; Vec2f vp2 = cl.point[(i + cl.vertex_count - 1) % cl.vertex_count] - cl.point[i]; double len1 = vp1.length(); double len2 = vp2.length(); vp1 /= len1; vp2 /= len2; double angle = acos(Vec2f::dot(vp1, vp2)); if (Vec2f::cross(vp2, vp1) > 0) angle = -angle; angle /= _target_angle[cl.external_vertex_number[i]]; if (angle * cl.rotate[i] <= 0) add += 1000; double angle_badness = abs((((abs(angle) > 1) ? angle : 1 / angle) - cl.rotate[i]) / 2) * _angle_importance[cl.external_vertex_number[i]]; result = max(result, angle_badness); } vector<Vec2f> pp; for (int i = 0; i < cl.vertex_count; i++) for (int a = cl.external_vertex_number[i], t = 0; a != cl.external_vertex_number[(i + 1) % cl.vertex_count]; a = (a + 1) % length, t++) { pp.push_back((cl.point[i] * (cl.edge_length[i] - t) + cl.point[(i + 1) % cl.vertex_count] * t) / cl.edge_length[i]); } int size = pp.size(); for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) if (i != j && (i + 1) % size != j && i != (j + 1) % size) { int nexti = (i + 1) % size; int nextj = (j + 1) % size; double dist = Vec2f::distSegmentSegment(pp[i], pp[nexti], pp[j], pp[nextj]); if (abs(dist) < eps) { add++; //printf("%5.5f %5.5f %5.5f %5.5f %5.5f %5.5f %5.5f %5.5f \n", xx[i], yy[i], xx[nexti], yy[nexti], xx[j], yy[j], xx[nextj], yy[nextj]); } else if (dist < 1) result = max(result, 1 / dist - 1); } // tails double diff = internalValue(cl); result += 1.0 * diff / length; double area = cl.area(); QS_DEF(Array<Vec2f>, current_point); current_point.clear_resize(length); for (int i = 0, t = 0; i < cl.vertex_count; i++) for (int j = cl.external_vertex_number[i], d = 0; j < cl.external_vertex_number[i + 1]; j++, t++, d++) current_point[t] = cl.point[i] + (cl.point[i + 1] - cl.point[i]) * d; for (int i = 0; i < cl.vertex_count; i++) if ((_component_finish[cl.external_vertex_number[i]] != cl.external_vertex_number[i]) && ((_component_finish[cl.external_vertex_number[i]] - cl.external_vertex_number[i] + cl.vertex_count) % cl.vertex_count <= cl.vertex_count / 4) && (cl.rotate[i] == -1)) area += _vertex_added_square[cl.external_vertex_number[i]] * (current_point[_component_finish[cl.external_vertex_number[i]]] - current_point[cl.external_vertex_number[i]]).lengthSqr(); double perimeter = cl.perimeter(); result += (perimeter * perimeter / 4 / PI / area - 1) / 5; result += 1000.0 * add; int p = period(cl); result *= 1.0 / p; return result; } void MoleculeLayoutMacrocyclesLattice::CycleLayout::soft_move_vertex(int vertex_number, Vec2f move_vector) { int i = vertex_number; double count = vertex_count; Vec2f shift_vector = move_vector; Vec2f add_vector = move_vector * (-1.0 / vertex_count); double mult = 1; double add_mult = -1.0 / vertex_count; do { point[i++] += move_vector * (count / vertex_count); count -= 1; //p[i++] += shift_vector; //shift_vector += add_vector; //p[i++] += move_vector * mult; //mult += add_mult; //shift_vector = move_vector * mult; if (i == vertex_count) i = 0; } while (i != vertex_number); point[vertex_count].copy(point[0]); } void MoleculeLayoutMacrocyclesLattice::CycleLayout::stright_rotate_chein(int vertex_number, double angle) { for (int i = 0; i <= vertex_count; i++) if (i != vertex_number) point[i] -= point[vertex_number]; point[vertex_number].set(0, 0); for (int i = vertex_number + 1; i <= vertex_count; i++) point[i].rotate(angle); } void MoleculeLayoutMacrocyclesLattice::CycleLayout::stright_move_chein(int vertex_number, Vec2f vector) { for (int i = vertex_number; i <= vertex_count; i++) point[i] += vector; } void MoleculeLayoutMacrocyclesLattice::closingStep(CycleLayout &cl, int index, int base_vertex, bool fix_angle, bool fix_next, double multiplyer) { int prev_vertex = base_vertex - 1; int next_vertex = base_vertex + 1; if ((cl.point[0] - cl.point[cl.vertex_count]).lengthSqr() == 0) { if (next_vertex == cl.vertex_count) next_vertex = 0; if (prev_vertex == -1) prev_vertex = cl.vertex_count - 1; } int move_vertex = fix_next ? next_vertex : prev_vertex; Vec2f move_vector(0, 0); if (fix_angle) { if ((cl.point[prev_vertex] - cl.point[base_vertex]).length() < 2 * EPSILON || (cl.point[next_vertex] - cl.point[base_vertex]).length() < 2 * EPSILON) return; double current_angle = cl.point[base_vertex].calc_angle(cl.point[next_vertex], cl.point[prev_vertex]); while (current_angle > 2 * PI) current_angle -= 2 * PI; while (current_angle < 0) current_angle += 2 * PI; double current_target_angle = _target_angle[cl.external_vertex_number[base_vertex]]; if (cl.rotate[base_vertex] < 0) current_target_angle = 2 * PI - current_target_angle; double anti_current_target_angle; if (current_target_angle > PI) { if (current_angle > current_target_angle) anti_current_target_angle = 2 * PI; else anti_current_target_angle = PI; } else { if (current_angle < current_target_angle) anti_current_target_angle = 0; else anti_current_target_angle = PI; } double better_change_angle = 0; double worse_chenge_angle = 0; if (abs(current_angle - current_target_angle) < EPSILON) { better_change_angle = current_angle * multiplyer; worse_chenge_angle = -current_angle * multiplyer; } else { better_change_angle = (current_target_angle - current_angle) * multiplyer; worse_chenge_angle = (anti_current_target_angle - current_angle) * multiplyer; } double actual_chenge_angle = 0; /*if ((cl.point[0] - cl.point[cl.vertex_count]).lengthSqr() == 0) { if (abs(current_angle - current_target_angle) < EPSILON) actual_chenge_angle = 0; else actual_chenge_angle = better_change_angle; if (fix_next) actual_chenge_angle *= -1; //actual_chenge_angle *= _angle_importance[vertex_number[base_vertex]]; move_vector.rotateAroundSegmentEnd(cl.point[move_vertex], cl.point[base_vertex], actual_chenge_angle); move_vector -= cl.point[move_vertex]; if (!fix_next) move_vector.negate(); if (fix_next) cl.soft_move_vertex(move_vertex, move_vector); else cl.soft_move_vertex(base_vertex, move_vector); } else*/ { double angle = current_angle; for (int i = next_vertex; i < cl.vertex_count; i++) angle -= cl.point[base_vertex].calc_angle(cl.point[i], cl.point[i + 1]); for (int i = prev_vertex; i > 0; i--) angle += cl.point[base_vertex].calc_angle(cl.point[i], cl.point[i - 1]); if (abs(angle + actual_chenge_angle) > abs(angle + better_change_angle)) actual_chenge_angle = better_change_angle; if (abs(angle + actual_chenge_angle) > abs(angle + worse_chenge_angle)) actual_chenge_angle = worse_chenge_angle; //actual_chenge_angle *= _angle_importance[vertex_number[base_vertex]]; cl.stright_rotate_chein(base_vertex, -actual_chenge_angle); } } else { int first_vertex = fix_next ? base_vertex : prev_vertex; int second_vertex = fix_next ? next_vertex : base_vertex; double current_dist = Vec2f::dist(cl.point[first_vertex], cl.point[second_vertex]); double current_target_dist = cl.edge_length[first_vertex]; Vec2f better_chenge_vector = cl.point[second_vertex] - cl.point[first_vertex]; Vec2f worse_chenge_vector = cl.point[second_vertex] - cl.point[first_vertex]; if (abs(current_target_dist - current_dist) > EPSILON) { better_chenge_vector *= (current_target_dist - current_dist) / current_dist * multiplyer; worse_chenge_vector *= (current_dist - current_target_dist) / current_dist * multiplyer; } else { better_chenge_vector *= multiplyer; worse_chenge_vector *= -multiplyer; } if ((cl.point[0] - cl.point[cl.vertex_count]).lengthSqr() == 0) { if (abs(current_target_dist - current_dist) > EPSILON) cl.soft_move_vertex(second_vertex, better_chenge_vector); } else { Vec2f actual_chenge_vector(0, 0); Vec2f current_discrepancy = cl.point[cl.vertex_count] - cl.point[0]; if ((current_discrepancy + actual_chenge_vector).lengthSqr() > (current_discrepancy + better_chenge_vector).lengthSqr()) actual_chenge_vector = better_chenge_vector; if ((current_discrepancy + actual_chenge_vector).lengthSqr() > (current_discrepancy + worse_chenge_vector).lengthSqr()) actual_chenge_vector = worse_chenge_vector; cl.stright_move_chein(second_vertex, actual_chenge_vector); } } } void MoleculeLayoutMacrocyclesLattice::closing(CycleLayout &cl) { Random rand(931170243); int iter_count = max(200 * cl.vertex_count, 10000); double multiplyer = 0.3; for (int i = 0; i < iter_count; i++) { double lenSqr = (cl.point[0] - cl.point[cl.vertex_count]).lengthSqr(); if (lenSqr < 0.25) { double angle = - PI * cl.vertex_count; for (int i = 0; i < cl.vertex_count; i++) angle += cl.point[i].calc_angle_pos(cl.point[(i + 1) % cl.vertex_count], cl.point[(i + cl.vertex_count - 1) % cl.vertex_count]); if (angle < 0) { cl.point[cl.vertex_count].copy(cl.point[0]); //printf("%d/%d\n", i, iter_count); break; } } bool angle = rand.next() & 1; bool next = rand.next() & 1; int base_vertex = rand.next(cl.vertex_count + 1); if ((cl.point[0] - cl.point[cl.vertex_count]).lengthSqr() != 0) { if (angle && (base_vertex == 0 || base_vertex == cl.vertex_count)) continue; if (!angle && ((base_vertex == 0 && !next) || (base_vertex == cl.vertex_count && next))) continue; } else { if (base_vertex == cl.vertex_count) continue; } closingStep(cl, i, base_vertex, angle, next, multiplyer); if (lenSqr == 0) multiplyer *= CHANGE_FACTOR; //if (i % 100 == 0) printf("%.5f\n", rating(cl)); } } void MoleculeLayoutMacrocyclesLattice::updateTouchingPoints(Array<local_pair_id>& pairs, CycleLayout& cl) { int len = cl.vertex_count; double eps = 1e-4; double eps2 = eps * eps; float good_distance = 1; pairs.clear(); QS_DEF(Array<Vec2f>, all_points); QS_DEF(Array<double>, all_numbers); all_points.clear(); all_numbers.clear(); for (int j = 0; j < len; j++) { for (int t = cl.external_vertex_number[j], s = 0; t < cl.external_vertex_number[(j + 1) % len]; t++, s += 1.0 / cl.edge_length[j]) { all_points.push(cl.point[j] * (1 - s) + cl.point[j] * s); all_numbers.push(j + s); } } for (int i = 0; i < len; i++) { for (int j = 0; j < all_points.size(); j++) { int diff = (i - (int)all_numbers[j] + len) % len; if (diff > 1 && diff != len - 1) { double distSqr = (cl.point[i] - all_points[j]).lengthSqr(); if (eps2 < distSqr && distSqr < good_distance) { pairs.push(local_pair_id(i, all_numbers[j])); } } } } } void MoleculeLayoutMacrocyclesLattice::smoothing(CycleLayout &cl) { closing(cl); Random rand(931170240); int iter_count = max(50 * length, 2000); QS_DEF(Array<local_pair_id>, touching_points); double coef = SMOOTHING_MULTIPLIER; for (int i = 0; i < iter_count; i++) { if ((i & (i - 1)) == 0) updateTouchingPoints(touching_points, cl); smoothingStep(cl, rand.next(cl.vertex_count), coef *= CHANGE_FACTOR, touching_points); } } void MoleculeLayoutMacrocyclesLattice::smoothingStep(CycleLayout &cl, int vertex_number, double coef, Array<local_pair_id>& touching_points) { Vec2f p1 = cl.point[(vertex_number - 1 + cl.vertex_count) % cl.vertex_count]; Vec2f p2 = cl.point[(vertex_number + 1 + cl.vertex_count) % cl.vertex_count]; double r1 = cl.edge_length[(cl.vertex_count + vertex_number - 1) % cl.vertex_count]; double r2 = cl.edge_length[(cl.vertex_count + vertex_number) % cl.vertex_count]; double len1 = Vec2f::dist(p1, cl.point[vertex_number]); double len2 = Vec2f::dist(p2, cl.point[vertex_number]); double r3 = Vec2f::dist(p1, p2) / sqrt(3.0); Vec2f p3; if (cl.rotate[vertex_number] != 0) { p3 = (p1 + p2) / 2; Vec2f a = (p2 - p1) / sqrt(12.0f); a.rotate(PI / 2 * cl.rotate[vertex_number]); p3 += a; } else { p3 = (p1*r2 + p2*r1) / (r1 + r2); } double len3 = Vec2f::dist(p3, cl.point[vertex_number]); if (cl.rotate[vertex_number] == 0) r3 = 0; //printf("%5.5f %5.5f %5.5f %5.5f\n", len1, len2, len3, r3); Vec2f newPoint; double eps = 1e-4; double eps2 = eps * eps; if (len1 < eps || len2 < eps || len3 < eps) { cl.point[vertex_number] = (p1 + p2) / 2.0; } else { double coef1 = (r1 / len1 - 1); double coef2 = (r2 / len2 - 1); double coef3 = (r3 / len3 - 1); //if (!isIntersec(x[worstVertex], y[worstVertex], x3, y3, x1, y1, x2, y2)) coef3 *= 10; if (cl.rotate[vertex_number] == 0) coef3 = -1; //printf("%5.5f %5.5f %5.5f\n", coef1, coef2, coef3); newPoint += (cl.point[vertex_number] - p1)*coef1; newPoint += (cl.point[vertex_number] - p2)*coef2; newPoint += (cl.point[vertex_number] - p3)*coef3; float good_distance = 1; for (int i = 0; i < touching_points.size(); i++) if (touching_points[i].left == vertex_number) { int j = (int)touching_points[i].right; double s = touching_points[i].right - j; Vec2f pp = cl.point[j] * (1 - s) + cl.point[(j + 1) % cl.vertex_count] * s; double distSqr = Vec2f::distSqr(cl.point[vertex_number], pp); double dist = sqrt(distSqr); double coef = (good_distance - dist) / dist; //printf("%5.5f \n", dist); newPoint += (cl.point[vertex_number] - pp)*coef; } newPoint *= coef; cl.point[vertex_number] += newPoint; } } Vec2f MoleculeLayoutMacrocyclesLattice::CycleLayout::getWantedVector(int vertex_number) { Vec2f p1 = point[(vertex_number - 1 + vertex_count) % vertex_count]; Vec2f p2 = point[(vertex_number + 1 + vertex_count) % vertex_count]; double r1 = edge_length[(vertex_count + vertex_number - 1) % vertex_count]; double r2 = edge_length[(vertex_count + vertex_number) % vertex_count]; double len1 = Vec2f::dist(p1, point[vertex_number]); double len2 = Vec2f::dist(p2, point[vertex_number]); double r3 = Vec2f::dist(p1, p2) / sqrt(3.0); Vec2f p3 = (p1 + p2) / 2; if (rotate[vertex_number] != 0) { Vec2f a = (p2 - p1) / sqrt(12.0f); a.rotate(PI / 2 * rotate[vertex_number]); p3 += a; } else { p3 = (p1*r1 + p2*r2) / (r1 + r2); } double len3 = Vec2f::dist(p3, point[vertex_number]); if (rotate[vertex_number] == 0) r3 = 0; //printf("%5.5f %5.5f %5.5f %5.5f\n", len1, len2, len3, r3); Vec2f newPoint; double eps = 1e-4; double eps2 = eps * eps; //if (len1 < eps || len2 < eps || len3 < eps) return ; double coef1 = (r1 / len1 - 1); double coef2 = (r2 / len2 - 1); double coef3 = (r3 / len3 - 1); /* if (rotate[vertex_number] != 0) { double angle = acos(Vec2f::cross(p1 - point[vertex_number], p2 - point[vertex_number]) / (Vec2f::dist(p1, point[vertex_number])*Vec2f::dist(p2, point[vertex_number]))); //if (angle < 2 * PI / 3) coef3 /= 10; }*/ //if (!isIntersec(x[worstVertex], y[worstVertex], x3, y3, x1, y1, x2, y2)) coef3 *= 10; if (rotate[vertex_number] == 0) coef3 = -1; //printf("%5.5f %5.5f %5.5f\n", coef1, coef2, coef3); newPoint += (point[vertex_number] - p1)*coef1; newPoint += (point[vertex_number] - p2)*coef2; newPoint += (point[vertex_number] - p3)*coef3; return newPoint * SMOOTHING_MULTIPLIER; }�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/molecule_layout_macrocycles.cpp��������������������������������������0000664�0000000�0000000�00000126237�12710376503�0024612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/molecule_layout_macrocycles.h" #include "base_cpp/profiling.h" #include <limits.h> #include <string> #include <vector> #include <set> #include <stack> #include <cmath> #include <string> #include <sstream> #include <map> #include <stdio.h> #include <math/random.h> #include "layout/molecule_layout.h" #include <algorithm> using namespace std; using namespace indigo; IMPL_ERROR(MoleculeLayoutMacrocycles, "molecule_layout_macrocycles"); const int MoleculeLayoutMacrocycles::max_size = MoleculeLayoutMacrocycles::Data::max_size; const int MoleculeLayoutMacrocycles::init_x = MoleculeLayoutMacrocycles::max_size / 2; const int MoleculeLayoutMacrocycles::init_y = MoleculeLayoutMacrocycles::max_size / 2; const int MoleculeLayoutMacrocycles::init_rot = MoleculeLayoutMacrocycles::max_size / 2 / 6 * 6; const double MoleculeLayoutMacrocycles::CHANGE_FACTOR = 0.995; CP_DEF(MoleculeLayoutMacrocycles); MoleculeLayoutMacrocycles::MoleculeLayoutMacrocycles (int size) : CP_INIT, TL_CP_GET(data), TL_CP_GET(_vertex_weight), // tree size TL_CP_GET(_vertex_stereo), // there is an angle in the vertex TL_CP_GET(_edge_stereo), // trans-cis configuration TL_CP_GET(_vertex_drawn), // is each vertex has been drawn earlier TL_CP_GET(_positions), // position of vertex TL_CP_GET(_vertex_added_square), // square of attached layout component TL_CP_GET(_component_finish), // finish of component starting with this vertex TL_CP_GET(_target_angle), // prefer angle in this vertex TL_CP_GET(_angle_importance) // importance of angle in this vertiex is close to target { // Set default values... length = size; _vertex_weight.clear_resize(size); _vertex_weight.fill(0); _vertex_stereo.clear_resize(size); _vertex_stereo.zerofill(); _edge_stereo.clear_resize(size); _edge_stereo.zerofill(); _vertex_drawn.clear_resize(size); _vertex_drawn.zerofill(); _positions.clear_resize(size); _vertex_added_square.clear_resize(size); _vertex_added_square.zerofill(); _component_finish.clear_resize(size); for (int i = 0; i < size; i++) _component_finish[i] = i; _target_angle.clear_resize(size); _target_angle.zerofill(); _angle_importance.clear_resize(size); _angle_importance.fill(1); } void MoleculeLayoutMacrocycles::addVertexOutsideWeight (int v, int weight) { _vertex_weight[v] += WEIGHT_FACTOR * weight; } void MoleculeLayoutMacrocycles::setVertexEdgeParallel (int v, bool parallel) { _vertex_stereo[v] = !parallel; } void MoleculeLayoutMacrocycles::set_vertex_added_square(int v, double s) { _vertex_added_square[v] = s; } int MoleculeLayoutMacrocycles::getVertexStereo (int v) { return _vertex_stereo[v]; } void MoleculeLayoutMacrocycles::setEdgeStereo (int e, int stereo) { _edge_stereo[e] = stereo; } void MoleculeLayoutMacrocycles::setVertexDrawn(int v, bool drawn) { _vertex_drawn[v] = drawn; } Vec2f &MoleculeLayoutMacrocycles::getPos (int v) const { return _positions[v]; } void MoleculeLayoutMacrocycles::set_component_finish(int v, int f) { _component_finish[v] = f; } void MoleculeLayoutMacrocycles::set_target_angle(int v, double angle) { _target_angle[v] = angle; } void MoleculeLayoutMacrocycles::set_angle_importance(int v, double imp) { _angle_importance[v] = imp; } void MoleculeLayoutMacrocycles::doLayout () { profTimerStart(t, "bc.layout"); double b2 = depictionCircle(); double b = depictionMacrocycleGreed(false); if (b > b2) { depictionCircle(); } } bool MoleculeLayoutMacrocycles::canApply (BaseMolecule &mol) { if (!mol.isConnected(mol)) { return false; } for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.getVertex(v).degree() != 2) { return false; } } return true; } double sqr(double x) {return x*x;} int improvement(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p, bool profi, bool do_dist, double multiplier, int worstVertex) { Vec2f p1 = p[(worstVertex - 1 + ind) % ind]; Vec2f p2 = p[(worstVertex + 1 + ind) % ind]; double r1 = edgeLenght[(ind + worstVertex - 1) % ind]; double r2 = edgeLenght[(ind + worstVertex) % ind]; double len1 = Vec2f::dist(p1, p[worstVertex]); double len2 = Vec2f::dist(p2, p[worstVertex]); double r3 = Vec2f::dist(p1, p2) / sqrt(3.0); Vec2f p3 = (p1 + p2) / 2; if (rotateAngle[worstVertex] != 0) { Vec2f a = (p2 - p1) / sqrt(12.0f); a.rotate(PI / 2 * rotateAngle[worstVertex]); p3 += a; } else { p3 = (p1*r1 + p2*r2) / (r1 + r2); } double len3 = Vec2f::dist(p3, p[worstVertex]); if (rotateAngle[worstVertex] == 0) r3 = 0; //printf("%5.5f %5.5f %5.5f %5.5f\n", len1, len2, len3, r3); Vec2f newPoint; double eps = 1e-4; double eps2 = eps * eps; if (len1 < eps || len2 < eps || len3 < eps) { p[worstVertex] = (p1 + p2) / 2.0; } else { double coef1 = (r1 / len1 - 1); double coef2 = (r2 / len2 - 1); double coef3 = (r3 / len3 - 1); if (rotateAngle[worstVertex] != 0) { double angle = acos(Vec2f::cross(p1 - p[worstVertex], p2 - p[worstVertex]) / (Vec2f::dist(p1, p[worstVertex])*Vec2f::dist(p2, p[worstVertex]))); //if (angle < 2 * PI / 3) coef3 /= 10; } //if (!isIntersec(x[worstVertex], y[worstVertex], x3, y3, x1, y1, x2, y2)) coef3 *= 10; if (rotateAngle[worstVertex] == 0) coef3 = -1; //printf("%5.5f %5.5f %5.5f\n", coef1, coef2, coef3); newPoint += (p[worstVertex] - p1)*coef1; newPoint += (p[worstVertex] - p2)*coef2; newPoint += (p[worstVertex] - p3)*coef3; if (do_dist) { if (profi) { for (int i = 0; i < ind; i++) if (i != worstVertex && (i + 1) % ind != worstVertex) { double dist = Vec2f::distPointSegment(p[worstVertex], p[i], p[(i + 1) % ind]); if (dist < 1 && dist > eps) { Vec2f normal = (p[(i + 1) % ind] - p[i]); normal.rotate(PI / 2); double c = Vec2f::cross(p[i], p[(i + 1) % ind]); double s = normal.length(); normal /= s; c /= s; double t = -c - Vec2f::dot(p[worstVertex], normal); Vec2f pp; if (s < eps) { pp = p[i]; } else { pp = p[worstVertex] + normal * t; if (Vec2f::dist(p[worstVertex], p[i]) < Vec2f::dist(p[worstVertex], pp)) { pp = p[i]; } if (Vec2f::dist(p[worstVertex], p[(i + 1) % ind]) < Vec2f::dist(p[worstVertex], pp)) { pp = p[(i + 1) % ind]; } } double coef = (1 - dist) / dist; newPoint += (p[worstVertex] - pp) * coef; } } } else { float good_distance = 1; for (int j = 0; j < ind; j++) { int nextj = (j + 1) % ind; Vec2f pp = p[j]; Vec2f dpp = (p[nextj] - p[j]) / edgeLenght[j]; for (int t = vertexNumber[j], s = 0; t != vertexNumber[nextj]; t = (t + 1) % molSize, s++) { if (t != vertexNumber[worstVertex] && (t + 1) % molSize != vertexNumber[worstVertex] && t != (vertexNumber[worstVertex] + 1) % molSize) { double distSqr = Vec2f::distSqr(pp, p[worstVertex]); if (distSqr < good_distance && distSqr > eps2) { double dist = sqrt(distSqr); double coef = (good_distance - dist) / dist; //printf("%5.5f \n", dist); newPoint += (p[worstVertex] - pp)*coef; } } pp += dpp; } } } } newPoint *= multiplier; p[worstVertex] += newPoint; } return worstVertex; } void soft_move_vertex(Vec2f* p, int vertex_count, int vertex_number, Vec2f move_vector) { profTimerStart(tt, "0:soft_move_vertex"); int i = vertex_number; double count = vertex_count; Vec2f shift_vector = move_vector; Vec2f add_vector = move_vector * (-1.0 / vertex_count); double mult = 1; double add_mult = -1.0 / vertex_count; do { p[i++] += move_vector * (count / vertex_count); count -= 1; //p[i++] += shift_vector; //shift_vector += add_vector; //p[i++] += move_vector * mult; //mult += add_mult; //shift_vector = move_vector * mult; if (i == vertex_count) i = 0; } while (i != vertex_number); p[vertex_count].copy(p[0]); } void stright_rotate_chein(Vec2f* p, int vertex_count, int vertex_number, double angle) { profTimerStart(tt, "0:stright_rotate_chein"); for (int i = 0; i <= vertex_count; i++) if (i != vertex_number) p[i] -= p[vertex_number]; p[vertex_number].set(0, 0); for (int i = vertex_number + 1; i <= vertex_count; i++) p[i].rotate(angle); } void stright_move_chein(Vec2f* p, int vertex_count, int vertex_number, Vec2f vector) { profTimerStart(tt, "0:stright_move_chein"); for (int i = vertex_number; i <= vertex_count; i++) p[i] += vector; } void MoleculeLayoutMacrocycles::improvement2(int index, int vertex_count, int cycle_size, int *rotate_angle, int *edge_lenght, int *vertex_number, Vec2f *p, int base_vertex, bool fix_angle, bool fix_next, double multiplyer) { profTimerStart(tt, "improvement2"); int prev_vertex = base_vertex - 1; int next_vertex = base_vertex + 1; if ((p[0] - p[vertex_count]).lengthSqr() == 0) { if (next_vertex == vertex_count) next_vertex = 0; if (prev_vertex == -1) prev_vertex = vertex_count - 1; } int move_vertex = fix_next ? next_vertex : prev_vertex; Vec2f move_vector(0, 0); if (fix_angle) { if ((p[prev_vertex] - p[base_vertex]).length() < 2 * EPSILON || (p[next_vertex] - p[base_vertex]).length() < 2 * EPSILON) return; double current_angle = p[base_vertex].calc_angle(p[next_vertex], p[prev_vertex]); while (current_angle > 2*PI) current_angle -= 2 * PI; while (current_angle < 0) current_angle += 2 * PI; double current_target_angle = _target_angle[vertex_number[base_vertex]]; if (rotate_angle[base_vertex] < 0) current_target_angle = 2 * PI - current_target_angle; double anti_current_target_angle; if (current_target_angle > PI) { if (current_angle > current_target_angle) anti_current_target_angle = 2 * PI; else anti_current_target_angle = PI; } else { if (current_angle < current_target_angle) anti_current_target_angle = 0; else anti_current_target_angle = PI; } double better_change_angle = 0; double worse_chenge_angle = 0; if (abs(current_angle - current_target_angle) < EPSILON) { better_change_angle = current_angle * multiplyer; worse_chenge_angle = -current_angle * multiplyer; } else { better_change_angle = (current_target_angle - current_angle) * multiplyer; worse_chenge_angle = (anti_current_target_angle - current_angle) * multiplyer; } double actual_chenge_angle = 0; if ((p[0] - p[vertex_count]).lengthSqr() == 0) { if (abs(current_angle - current_target_angle) < EPSILON) actual_chenge_angle = 0; else actual_chenge_angle = better_change_angle; if (fix_next) actual_chenge_angle *= -1; //actual_chenge_angle *= _angle_importance[vertex_number[base_vertex]]; move_vector.rotateAroundSegmentEnd(p[move_vertex], p[base_vertex], actual_chenge_angle); move_vector -= p[move_vertex]; if (!fix_next) move_vector.negate(); if (fix_next) soft_move_vertex(p, vertex_count, move_vertex, move_vector); else soft_move_vertex(p, vertex_count, base_vertex, move_vector); } else { double angle = current_angle; while (angle < 0) angle += 2 * PI; while (angle >= 2 * PI) angle -= 2 * PI; for (int i = next_vertex; i < vertex_count; i++) angle -= p[base_vertex].calc_angle(p[i], p[i + 1]); for (int i = prev_vertex; i > 0; i--) angle += p[base_vertex].calc_angle(p[i], p[i - 1]); if (abs(angle + actual_chenge_angle) > abs(angle + better_change_angle)) actual_chenge_angle = better_change_angle; if (abs(angle + actual_chenge_angle) > abs(angle + worse_chenge_angle)) actual_chenge_angle = worse_chenge_angle; //actual_chenge_angle *= _angle_importance[vertex_number[base_vertex]]; stright_rotate_chein(p, vertex_count, base_vertex, -actual_chenge_angle); } } else { int first_vertex = fix_next ? base_vertex : prev_vertex; int second_vertex = fix_next ? next_vertex : base_vertex; double current_dist = Vec2f::dist(p[first_vertex], p[second_vertex]); double current_target_dist = edge_lenght[first_vertex]; Vec2f better_chenge_vector = p[second_vertex] - p[first_vertex]; Vec2f worse_chenge_vector = p[second_vertex] - p[first_vertex]; if (abs(current_target_dist - current_dist) > EPSILON) { better_chenge_vector *= (current_target_dist - current_dist) / current_dist * multiplyer; worse_chenge_vector *= (current_dist - current_target_dist) / current_dist * multiplyer; } else { better_chenge_vector *= multiplyer; worse_chenge_vector *= -multiplyer; } if ((p[0] - p[vertex_count]).lengthSqr() == 0) { if (abs(current_target_dist - current_dist) > EPSILON) soft_move_vertex(p, vertex_count, second_vertex, better_chenge_vector); } else { Vec2f actual_chenge_vector(0, 0); Vec2f current_discrepancy = p[vertex_count] - p[0]; if ((current_discrepancy + actual_chenge_vector).lengthSqr() > (current_discrepancy + better_chenge_vector).lengthSqr()) actual_chenge_vector = better_chenge_vector; if ((current_discrepancy + actual_chenge_vector).lengthSqr() > (current_discrepancy + worse_chenge_vector).lengthSqr()) actual_chenge_vector = worse_chenge_vector; stright_move_chein(p, vertex_count, second_vertex, actual_chenge_vector); } } } void MoleculeLayoutMacrocycles::smoothing(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p, bool profi) { Random rand(931170240); int iter_count = max(50 * molSize, 2000); for (int i = 0; i < iter_count; i++) improvement(ind, molSize, rotateAngle, edgeLenght, vertexNumber, p, profi, i >= iter_count / 2, 0.1, rand.next(ind)); } void MoleculeLayoutMacrocycles::smoothing2(int vertex_count, int cycle_size, int *rotate_angle, int *edge_lenght, int *vertex_number, Vec2f *p) { Random rand(931170241); int iter_count = 200 * vertex_count; double multiplyer = 0.3; for (int i = 0; i < iter_count; i++) { if ((p[0] - p[vertex_count]).lengthSqr() < 0.25) { p[vertex_count].copy(p[0]); //break; } bool angle = rand.next() & 1; bool next = rand.next() & 1; int base_vertex = rand.next(vertex_count + 1); if ((p[0] - p[vertex_count]).lengthSqr() != 0) { if (angle && (base_vertex == 0 || base_vertex == vertex_count)) continue; if (!angle && ((base_vertex == 0 && !next) || (base_vertex == vertex_count && next))) continue; } else { if (base_vertex == vertex_count) continue; } improvement2(i, vertex_count, cycle_size, rotate_angle, edge_lenght, vertex_number, p, base_vertex, angle, next, multiplyer); if ((p[vertex_count] - p[0]).lengthSqr() == 0) multiplyer *= CHANGE_FACTOR; } //smoothing(vertex_count, cycle_size, rotate_angle, edge_lenght, vertex_number, p, false); } double MoleculeLayoutMacrocycles::badness(int ind, int molSize, int *rotateAngle, int *edgeLenght, int *vertexNumber, Vec2f *p, int diff) { double eps = 1e-9; double result = 0; int add = 0; // distances for (int i = 0; i < ind; i++) { double len = Vec2f::dist(p[i], p[(i + 1)%ind])/edgeLenght[i]; if (len < eps) add++; else if (len < 1) result = max(result, (1/len - 1)); else result = max(result, len - 1); } // angles for (int i = 0; i < ind; i++) { Vec2f vp1 = p[(i + 1) % ind] - p[i]; Vec2f vp2 = p[(i + ind - 1) % ind] - p[i]; double len1 = vp1.length(); double len2 = vp2.length(); vp1 /= len1; vp2 /= len2; double angle = acos(Vec2f::dot(vp1, vp2)); if (Vec2f::cross(vp2, vp1) > 0) angle = -angle; angle /= _target_angle[vertexNumber[i]]; if (angle * rotateAngle[i] <= 0) add += 1000; double angle_badness = abs((((abs(angle) > 1) ? angle : 1 / angle) - rotateAngle[i]) / 2) * _angle_importance[vertexNumber[i]]; result = max(result, angle_badness); } vector<Vec2f> pp; for (int i = 0; i < ind; i++) for (int a = vertexNumber[i], t = 0; a != vertexNumber[(i + 1) % ind]; a = (a + 1) % molSize, t++) { pp.push_back((p[i] * (edgeLenght[i] - t) + p[(i + 1)%ind] * t)/edgeLenght[i]); } int size = pp.size(); for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) if (i != j && (i + 1) % size != j && i != (j + 1) % size) { int nexti = (i + 1) % size; int nextj = (j + 1) % size; double dist = Vec2f::distSegmentSegment(pp[i], pp[nexti], pp[j], pp[nextj]); if (abs(dist) < eps) { add++; //printf("%5.5f %5.5f %5.5f %5.5f %5.5f %5.5f %5.5f %5.5f \n", xx[i], yy[i], xx[nexti], yy[nexti], xx[j], yy[j], xx[nextj], yy[nextj]); } else if (dist < 1) result = max(result, 1/dist - 1); } // tails result += 1.0 * diff / molSize; double square = 0; for (int i = 1; i < ind - 1; i++) square += Vec2f::cross(p[i] - p[0], p[(i + 1) % ind] - p[0])/2; square = abs(square); for (int i = 0; i < ind; i++) if (_component_finish[vertexNumber[i]] != vertexNumber[i] && rotateAngle[i] == -1) square += _vertex_added_square[vertexNumber[i]] * (p[_component_finish[vertexNumber[i]]] - p[i]).lengthSqr(); double perimeter = 0; for (int i = 0; i < ind; i++) perimeter += (p[(i + 1) % ind] - p[i]).length(); result += (perimeter * perimeter / 4 / PI / square - 1) / 5; //printf("%5.5f\n", result); return result + 1000.0 * add; } int MoleculeLayoutMacrocycles::get_diff_grid(int x, int y, int rot, int value) { int diffCoord; x -= init_x; y -= init_y; if (x * y >= 0) diffCoord = abs(x) + abs(y); // x and y both positive or negative, vector (y+x) is not neseccary else diffCoord = max(abs(x), abs(y)); // x and y are has got different signs, vector (y-x) is neseccary int diffRot = abs(abs(rot - (init_rot + 6)) - 1); return 2 * diffRot + 1 * diffCoord + value; } int MoleculeLayoutMacrocycles::get_diff_circle(int x, int y, int rot, int value) { int diffCoord; y -= init_y; if (x * y >= 0) diffCoord = abs(x) + abs(y); // x and y both positive or negative, vector (y+x) is not neseccary else diffCoord = max(abs(x), abs(y)); // x and y are has got different signs, vector (y-x) is neseccary int diffRot = abs(rot - (init_rot + 1)); return 2 * diffRot + 1 * diffCoord + value; } void rotate(int* ar, int ar_length, int shift) { QS_DEF(Array<int>, temp); temp.clear_resize(ar_length); shift = (shift % ar_length + ar_length) % ar_length; memcpy(temp.ptr(), ar + shift, (ar_length - shift) * sizeof(int)); memcpy(temp.ptr() + ar_length - shift, ar, shift * sizeof(int)); memcpy(ar, temp.ptr(), ar_length * sizeof(int)); } void rotate(double* ar, int ar_length, int shift) { QS_DEF(Array<double>, temp); temp.clear_resize(ar_length); shift = (shift % ar_length + ar_length) % ar_length; memcpy(temp.ptr(), ar + shift, (ar_length - shift) * sizeof(double)); memcpy(temp.ptr() + ar_length - shift, ar, shift * sizeof(double)); memcpy(ar, temp.ptr(), ar_length * sizeof(double)); } double MoleculeLayoutMacrocycles::depictionMacrocycleGreed(bool profi) { { profTimerStart(tt, "answerField"); //MoleculeLayoutMacrocyclesLattice molecule_layout_lattice(length); } if (length >= max_size) return 1e9; unsigned short (&minRotates)[max_size][max_size][2][max_size][max_size] = data.minRotates; //first : number of edge //second : summary angle of rotation (in PI/3 times) //third : last rotation is contraclockwise //fourth : x-coordinate //fifth : y-coordinate //value : minimum number of vertexes sticked out + count of CIS-configurations int infinity = 60000; int shift = -1; int max_value = -infinity; for (int i = 0; i < length; i++) { if (_edge_stereo[i] != 2) { int value = _edge_stereo[i] + _vertex_weight[i] + _vertex_weight[(i + 1) % length] - _vertex_weight[(i + length - 1) % length]/2 - _vertex_weight[(i + 2) % length]/2; if (shift == -1 || value > max_value) { shift = i; max_value = value; } } } if (shift == -1) return 1e9; rotate(_vertex_weight.ptr(), length, shift); rotate(_vertex_stereo.ptr(), length, shift); rotate(_edge_stereo.ptr(), length, shift); rotate(_angle_importance.ptr(), length, shift); int dx[6]; int dy[6]; dx[0] = 1; dy[0] = 0; dx[1] = 0; dy[1] = 1; dx[2] = -1; dy[2] = 1; dx[3] = -1; dy[3] = 0; dx[4] = 0; dy[4] = -1; dx[5] = 1; dy[5] = -1; int x_left = max(init_x - length, 1); int x_right = min(init_x + length, max_size - 2); int y_left = max(init_y - length, 1); int y_right = min(init_y + length, max_size - 2); int rot_left = max(init_rot - length, 1); int rot_right = min(init_rot + length, max_size - 2); for (int i = 0; i <= length; i++) for (int j = rot_left - 1; j <= rot_right + 1; j++) for (int p = 0; p < 2; p++) for (int k = x_left - 1; k <= x_right + 1; k++) for (int t = y_left - 1; t <= y_right + 1; t++) minRotates[i][j][p][k][t] = infinity; minRotates[0][init_rot][1][init_x][init_y] = 0; minRotates[1][init_rot][1][init_x + 1][init_y] = 0; int max_dist = length; { profTimerStart(tt, "main.for"); for (int k = 1; k < length; k++) { //printf("Step number %d\n", k); int not_this_p = -1; if (k == length - 1) { if (_edge_stereo[length - 1] == MoleculeCisTrans::CIS) not_this_p = 0; if (_edge_stereo[length - 1] == MoleculeCisTrans::TRANS) not_this_p = 1; } for (int rot = rot_left; rot <= rot_right; rot++) { if (!_vertex_stereo[k]) { int xchenge = dx[rot % 6]; int ychenge = dy[rot % 6]; for (int p = 0; p < 2; p++) if (p != not_this_p) { for (int x = x_left; x <= x_right; x++) { unsigned short *ar1 = minRotates[k + 1][rot][p][x + xchenge] + ychenge; unsigned short *ar2 = minRotates[k][rot][p][x]; for (int y = y_left; y <= y_right; y++) { if (ar1[y] > ar2[y]) { ar1[y] = ar2[y]; } } } } } else { for (int p = 0; p < 2; p++) { // trying to rotate like CIS if (_edge_stereo[k - 1] != MoleculeCisTrans::TRANS) if (p != not_this_p) { int nextRot = rot; if (p) nextRot++; else nextRot--; int xchenge = dx[nextRot % 6]; int ychenge = dy[nextRot % 6]; int add = 1; if (abs(_vertex_weight[k]) > WEIGHT_FACTOR) { if (!p && _vertex_weight[k] > 0) add += _vertex_weight[k]; if (p && _vertex_weight[k] < 0) add -= _vertex_weight[k]; } for (int x = x_left; x <= x_right; x++) { unsigned short *ar1 = minRotates[k + 1][nextRot][p][x + xchenge] + ychenge; unsigned short *ar2 = minRotates[k][rot][p][x]; for (int y = y_left; y <= y_right; y++) { if (ar1[y] > ar2[y] + add) { ar1[y] = ar2[y] + add; } } } } // trying to rotate like TRANS if (_edge_stereo[k - 1] != MoleculeCisTrans::CIS) if ((p ^ 1) != not_this_p) { int nextRot = rot; if (p) nextRot--; else nextRot++; int add = 0; if (abs(_vertex_weight[k]) > WEIGHT_FACTOR) { if (p && _vertex_weight[k] > 0) add += _vertex_weight[k]; if (!p && _vertex_weight[k] < 0) add -= _vertex_weight[k]; } int xchenge = dx[nextRot % 6]; int ychenge = dy[nextRot % 6]; for (int x = x_left; x <= x_right; x++) { unsigned short *ar1 = minRotates[k + 1][nextRot][p ^ 1][x + xchenge] + ychenge; unsigned short *ar2 = minRotates[k][rot][p][x]; for (int y = y_left; y <= y_right; y++) { if (ar1[y] > ar2[y] + add) { ar1[y] = ar2[y] + add; } } } } } } } } } /* for (int rot = rot_left; rot <= rot_right; rot++) for (int x = x_left; x <= x_right; x++) for (int y = y_left; y <= y_right; y++) minRotates[length][rot][1][x][y]++;*/ struct point { int diff; int x; int y; int p; int rot; int type; point(int _d, int _x, int _y, int _p, int _r, int _t) { diff = _d; x = _x; y = _y; p = _p; rot = _r; type = _t; } bool operator<(const point& p) const { return diff - p.diff; } }; QS_DEF(ObjArray<point>, points); points.clear(); int critical_diff_grid = infinity - 1; std::multiset<int> diff_set; int var_count = 100; enum { CIRCLE_TYPE, GRID_TYPE }; for (int rot = init_rot; rot <= rot_right; rot++) for (int p = 0; p < 2; p++) for (int x = x_left; x <= x_right; x++) for (int y = y_left; y <= y_right; y++) if (minRotates[length][rot][p][x][y] <= infinity) { int curr_dif = get_diff_grid(x, y, rot, minRotates[length][rot][p][x][y]); if (curr_dif <= critical_diff_grid) { diff_set.insert(curr_dif); if (diff_set.size() > var_count) diff_set.erase(*diff_set.rbegin()); critical_diff_grid = *diff_set.rbegin(); } } for (int rot = init_rot; rot <= rot_right; rot++) { for (int p = 0; p < 2; p++) { for (int x = x_left; x <= x_right; x++) { for (int y = y_left; y <= y_right; y++) { if (minRotates[length][rot][p][x][y] < infinity) { int curdiff = get_diff_grid(x, y, rot, minRotates[length][rot][p][x][y]); if (curdiff <= critical_diff_grid) { point curr_point(curdiff, x, y, p, rot, GRID_TYPE); points.push(curr_point); } } } } } } diff_set.clear(); int critical_diff_circle = infinity - 1; for (int rot = rot_left; rot <= rot_right; rot++) for (int p = 0; p < 2; p++) for (int x = x_left; x <= x_right; x++) for (int y = y_left; y <= y_right; y++) if (minRotates[length][rot][p][x][y] <= infinity) { int curr_dif = get_diff_circle(x - init_x - length/2, y - length/2, rot, minRotates[length][rot][p][x][y]); if (curr_dif <= critical_diff_circle) { diff_set.insert(curr_dif); if (diff_set.size() > var_count) diff_set.erase(*diff_set.rbegin()); critical_diff_circle = *diff_set.rbegin(); } } for (int rot = rot_left; rot <= rot_right; rot++) { for (int p = 0; p < 2; p++) { for (int x = x_left; x <= x_right; x++) { for (int y = y_left; y <= y_right; y++) { if (minRotates[length][rot][p][x][y] < infinity) { int curdiff = get_diff_circle(x - init_x - length / 2, y - length/2, rot, minRotates[length][rot][p][x][y]); if (curdiff <= critical_diff_circle) { point curr_point(curdiff, x, y, p, rot, CIRCLE_TYPE); points.push(curr_point); } } } } } } /* for (int rot = rot_left; rot <= rot_right; rot++) for (int x = x_left; x <= x_right; x++) for (int y = y_left; y <= y_right; y++) minRotates[length][rot][1][x][y]--;*/ int x_result[max_size + 1]; int y_result[max_size + 1]; int rot_result[max_size + 1]; int p_result[max_size + 1]; double bestBadness = 1e30; int bestIndex = 0; for (int index = 0; index < points.size(); index++) { profTimerStart(tt, "selector.for"); x_result[length] = points[index].x; y_result[length] = points[index].y; rot_result[length] = points[index].rot; p_result[length] = points[index].p; for (int k = length - 1; k > 0; k--) { int xchenge = dx[rot_result[k + 1] % 6]; int ychenge = dy[rot_result[k + 1] % 6]; x_result[k] = x_result[k + 1] - xchenge; y_result[k] = y_result[k + 1] - ychenge; if (!_vertex_stereo[k]) { p_result[k] = p_result[k + 1]; rot_result[k] = rot_result[k + 1]; } else { if (p_result[k + 1]) rot_result[k] = rot_result[k + 1] - 1; else rot_result[k] = rot_result[k + 1] + 1; double l = k * (sqrt(3.0) + 1.5) * PI / 12; Vec2f vec(y_result[k] - init_y, 0); vec.rotate(PI/3); vec += Vec2f(x_result[k] - init_x, 0); double x = vec.length(); double eps = 1e-3; double alpha = 0; if (x > eps) { double L = eps; double R = 2*PI - eps; while (R - L > eps) { double M = (L + R)/2; if (M * x/ (2 * sin(M/2)) > l) R = M; else L = M; } alpha = vec.tiltAngle2() + R/2; } p_result[k] = 2; int is_cis_better = (alpha < PI/3 * (rot_result[k] - init_rot) + PI/length) ^ (!p_result[k + 1]); int add = 0; if (abs(_vertex_weight[k]) > WEIGHT_FACTOR) { if (!p_result[k + 1] && _vertex_weight[k] > 0) add = _vertex_weight[k]; if (p_result[k + 1] && _vertex_weight[k] < 0) add = -_vertex_weight[k]; } if (!is_cis_better) { if (_edge_stereo[k - 1] != MoleculeCisTrans::TRANS) { // try CIS if (minRotates[k][rot_result[k]][p_result[k + 1]][x_result[k]][y_result[k]] + add + 1== minRotates[k + 1][rot_result[k + 1]][p_result[k + 1]][x_result[k + 1]][y_result[k + 1]]) { p_result[k] = p_result[k + 1]; } } } if (_edge_stereo[k - 1] != MoleculeCisTrans::CIS) { // try TRANS if (minRotates[k][rot_result[k]][p_result[k + 1] ^ 1][x_result[k]][y_result[k]] + add == minRotates[k + 1][rot_result[k + 1]][p_result[k + 1]][x_result[k + 1]][y_result[k + 1]]) { p_result[k] = p_result[k + 1] ^ 1; } } if (is_cis_better) { if (_edge_stereo[k - 1] != MoleculeCisTrans::TRANS) { // try CIS if (minRotates[k][rot_result[k]][p_result[k + 1]][x_result[k]][y_result[k]] + add + 1 == minRotates[k + 1][rot_result[k + 1]][p_result[k + 1]][x_result[k + 1]][y_result[k + 1]]) { p_result[k] = p_result[k + 1]; } } } if (p_result[k] == 2) { throw Error("Path not find (%d): %d.", length, minRotates[k + 1][rot_result[k + 1]][p_result[k + 1]][x_result[k + 1]][y_result[k + 1]]); } } } x_result[0] = init_x; y_result[0] = init_y; int rotateAngle[max_size]; int edgeLenght[max_size]; int vertexNumber[max_size]; int ind = 0; for (int i = 0; i < length; i++) { if (_vertex_stereo[i]) vertexNumber[ind++] = i; } if (ind < 3) { ind = 0; for (int i = 0; i < length; i++) vertexNumber[ind++] = i; } vertexNumber[ind] = length; for (int i = 0; i < ind - 1; i++) edgeLenght[i] = vertexNumber[i + 1] - vertexNumber[i]; edgeLenght[ind - 1] = vertexNumber[0] - vertexNumber[ind - 1] + length; for (int i = 0; i < ind; i++) if (vertexNumber[i] != 0) { rotateAngle[i] = rot_result[vertexNumber[i] + 1] > rot_result[vertexNumber[i]] ? 1 : rot_result[vertexNumber[i] + 1] == rot_result[vertexNumber[i]] ? 0 : -1; } if (vertexNumber[0] == 0) { rotateAngle[0] = 1; } //rotateAngle[0] = -1; Vec2f p[max_size]; for (int i = 0; i <= ind; i++) { p[i] = Vec2f(y_result[vertexNumber[i]], 0); p[i].rotate(PI/3); p[i] += Vec2f(x_result[vertexNumber[i]], 0); } for (int i = ind; i >= 0; i--) p[i] -= p[0]; if (points[index].type == CIRCLE_TYPE) { if (p[ind].lengthSqr() < EPSILON) continue; Vec2f rotate_vector(p[ind]/p[ind].length()); for (int i = 0; i <= ind; i++) { p[i].rotateL(rotate_vector); } double max_x = p[ind].x; if (max_x <= 0) max_x = 1; double radii = max_x / (2 * PI); for (int i = 0; i <= ind; i++) { double angle = 2 * PI * (p[i].x) / max_x; p[i].set(radii - p[i].y/2, 0); p[i].rotate(angle); } } /* Vec2f last_vector(p[0] - p[ind]); for (int i = 0; i <= ind; i++) p[i] += (last_vector * vertexNumber[i]) / length;*/ //smoothing(ind, length, rotateAngle, edgeLenght, vertexNumber, p, profi); //for (int i = 0; i <= ind; i++ ) printf("%5.5f %5.5f\n", p[i].x, p[i].y); smoothing2(ind, length, rotateAngle, edgeLenght, vertexNumber, p); int diff = minRotates[length][points[index].rot][points[index].p][points[index].x][points[index].y]; for (int i = 0; i < length - 1; i++) { if (rotateAngle[i] == 1 && rotateAngle[i + 1] == 1) diff--; if (rotateAngle[i] == -1 && rotateAngle[i + 1] == -1) diff--; } double newBadness = badness(ind, length, rotateAngle, edgeLenght, vertexNumber, p, diff); if (newBadness < bestBadness) { bestBadness = newBadness; bestIndex = index; for (int i = 0; i < ind; i++) { int nexti = (i + 1) % ind; for (int j = vertexNumber[i], t = 0; j != vertexNumber[nexti]; j = (j + 1) % length, t++) { _positions[j] = (p[i] * (edgeLenght[i] - t) + p[nexti] * t) / edgeLenght[i]; } } } } Vec2f shifted_positons[max_size]; for (int i = 0; i < length; i++) shifted_positons[(i + shift) % length] = _positions[i]; for (int i = 0; i < length; i++) _positions[i] = shifted_positons[i]; rotate(_vertex_weight.ptr(), length, -shift); rotate(_vertex_stereo.ptr(), length, -shift); rotate(_edge_stereo.ptr(), length, -shift); rotate(_angle_importance.ptr(), length, -shift); return bestBadness; } double MoleculeLayoutMacrocycles::depictionCircle() { int cisCount = 0; for (int i = 0; i < length; i++) if (_edge_stereo[i] == MoleculeCisTrans::CIS) cisCount++; int zero_edge_stereo_count = 0; for (int i = 0; i < length; i++) if (_edge_stereo[i] == 0) zero_edge_stereo_count++; if (zero_edge_stereo_count == 0) return 1000000; QS_DEF(Array<bool>, up); QS_DEF(Array<bool>, only_up); up.clear_resize(length + 1); only_up.clear_resize(length + 1); up.zerofill(); only_up.zerofill(); for (int i = 0; i < length; i++) if (_edge_stereo[i] == MoleculeCisTrans::CIS && _edge_stereo[(i + length - 1) % length] == MoleculeCisTrans::CIS) { only_up[(i + 1) % length] = 1; only_up[i] = 1; only_up[(i + length - 1) % length] = 1; } for (int i = 0; i < length; i++) up[i] = only_up[i]; QS_DEF(Array<int>, free); free.clear_resize(length); bool exist_precalc = false; for (int i = 0; i < length; i++) exist_precalc |= only_up[i]; if (exist_precalc) { int index_start = 0; int index_end = 0; int start = 0; for (int i = 0; i < length; i++) if (!only_up[i] && only_up[(i + length - 1) % length]) { index_start = i; index_end = index_start; while (!only_up[index_end]) index_end = (index_end + 1) % length; for (int j = index_start; j != index_end; j = (j + 1) % length) if (_edge_stereo[(j - 1 + length) % length] == MoleculeCisTrans::CIS) up[j] = up[(j - 1 + length) % length]; else up[j] = !up[(j - 1 + length) % length]; if (up[(index_end - 1 + length) % length]) { int index_flip = -1; for (int j = index_start; j != index_end; j = (j + 1) % length) if (_edge_stereo[j] != MoleculeCisTrans::CIS && _edge_stereo[(j + length - 1) % length] != MoleculeCisTrans::CIS) index_flip = j; if (index_flip == -1) { free.zerofill(); int index_free = 0; for (int j = index_start; j != index_end; j = (j + 1) % length) if (_edge_stereo[(j - 1 + length) % length] == 0 || _edge_stereo[j] == 0) free[index_free++] = j; if (index_free > 0) index_flip = free[index_free/2]; else index_flip = index_start; } for (int j = index_flip; j != index_end; j = (j + 1) % length) up[j] = !up[j]; } } } else { for (int i = 0; i < length; i++) { if (_edge_stereo[i] == MoleculeCisTrans::CIS) up[i + 1] = up[i]; else up[i + 1] = !up[i]; } if ((cisCount + length) % 2 == 0) { // if first and last points on the same level int upCisCount = 0; int downCisCount = 0; for (int i = 0; i < length; i++) { if (_edge_stereo[i] == MoleculeCisTrans::CIS && up[i]) upCisCount++; if (_edge_stereo[i] == MoleculeCisTrans::CIS && !up[i]) downCisCount++; } if (downCisCount > upCisCount) { for (int i = 0; i <= length; i++) up[i] = !up[i]; } } else { // if first and last points on the different levels if (cisCount == 0) { int index = 0; if (_edge_stereo[0] != 0 || _edge_stereo[length - 1] != 0 || _vertex_stereo[0] == 0) { for (int i = 1; i < length; i++) if (_edge_stereo[i] != 0 || _edge_stereo[i - 1] != 0 || _vertex_stereo[i] == 1) index = i; } for (int i = index; i <= length; i++) up[i] = !up[i]; if (!up[index]) for (int i = 0; i <= length; i++) up[i] = !up[i]; } else { int bestIndex = 0; int bestDiff = -1; for (int i = 0; i < length; i++) if (_edge_stereo[i] == MoleculeCisTrans::CIS || _edge_stereo[(i - 2 + length) % length] == MoleculeCisTrans::CIS){ int diff = 0; for (int j = 0; j < length; j++) { if (_edge_stereo[i] == MoleculeCisTrans::CIS && ((up[i] && j < i) || (!up[i] && j >= i))) diff++; if (_edge_stereo[i] == MoleculeCisTrans::CIS && !((up[i] && j < i) || (!up[i] && j >= i))) diff--; } if (up[i]) diff = -diff; if (diff > bestDiff) { bestDiff = diff; bestIndex = i; } } for (int i = bestIndex; i <= length; i++) up[i] = !up[i]; if (!up[bestIndex]) { for (int i = 0; i <= length; i++) up[i] = !up[i]; } } } } int diff = 0; for (int i = 0; i < length; i++) { if ((!up[i] && (up[(i + 1) % length] || up[(i + length - 1) % length])) && _vertex_weight[i] > 0) diff += _vertex_weight[i]; if (!(!up[i] && (up[(i + 1) % length] || up[(i + length - 1) % length])) && _vertex_weight[i] < 0) diff -= _vertex_weight[i]; } // for (int i = 0; i < length; i++) // diff += (up[i] && up[(i + 1) % length]) || (!up[i] && !up[(i + 1) % length] && (up[(i - 1 + length) % length] == up[(i + 2) % length])); double r = length * sqrt(3.0)/2 / (2 * PI); QS_DEF(Array<Vec2f>, p); p.clear_resize(length + 1); for (int i = 0; i <= length; i++) { double rr = r; if (up[i]) rr += 0.25; else rr -= 0.25; p[i] = Vec2f(rr, 0); p[i].rotate(2*PI/length*i); } QS_DEF(Array<int>, rotateAngle); rotateAngle.clear_resize(length); for (int i = 0; i < length; i++) rotateAngle[i] = -1; int i = 0; while (_edge_stereo[(i - 1 + length) % length] != 0 && _edge_stereo[i] != 0) i++; for (; rotateAngle[i] == -1; i = (i + 1) % length) if (_edge_stereo[(i - 1 + length) % length] == MoleculeCisTrans::CIS) rotateAngle[i] = rotateAngle[(i - 1 + length) % length]; else if (_edge_stereo[(i - 1 + length) % length] == MoleculeCisTrans::TRANS) rotateAngle[i] = -rotateAngle[(i - 1 + length) % length]; else rotateAngle[i] = up[i] ? 1 : (up[(i + 1) % length] || up[(i + length - 1) % length]) ? -1 : 1; QS_DEF(Array<int>, edgeLength); edgeLength.clear_resize(length); for (i = 0; i < length; i++) edgeLength[i] = 1; QS_DEF(Array<int>, vertexNumber); vertexNumber.clear_resize(length); for (i = 0; i < length; i++) vertexNumber[i] = i; /*double angle = PI * 2 * rand() / RAND_MAX; double sn = sin(angle); double cs = cos(angle); for (int i = 0; i < molSize; i++) { double xx = cs * x[i] - sn * y[i]; double yy = sn * x[i] + cs * y[i]; x[i] = xx; y[i] = yy; }*/ // smoothing(length, length, rotateAngle.ptr(), edgeLength.ptr(), vertexNumber.ptr(), p.ptr(), false); smoothing2(length, length, rotateAngle.ptr(), edgeLength.ptr(), vertexNumber.ptr(), p.ptr()); for (i = 0; i < length; i++) { _positions[i] = p[i]; } return badness(length, length, rotateAngle.ptr(), edgeLength.ptr(), vertexNumber.ptr(), p.ptr(), diff); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/reaction_layout.cpp��������������������������������������������������0000664�0000000�0000000�00000006450�12710376503�0022217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "reaction/reaction.h" #include "layout/molecule_layout.h" #include "layout/reaction_layout.h" using namespace indigo; ReactionLayout::ReactionLayout (BaseReaction& r, bool smart_layout) : bond_length(1), plus_interval_factor(4), arrow_interval_factor(6), preserve_molecule_layout(false), _r(r), _smart_layout(smart_layout) { max_iterations = 0; } void ReactionLayout::make () { // update layout of molecules, if needed if (!preserve_molecule_layout) { for (int i = _r.begin(); i < _r.end(); i = _r.next(i)) { MoleculeLayout molLayout(_r.getBaseMolecule(i), _smart_layout); molLayout.max_iterations = max_iterations; molLayout.bond_length = bond_length; molLayout.make(); } } // layout molecules in a row with the intervals specified Metalayout::LayoutLine& line = _ml.newLine(); for (int i = _r.reactantBegin(); i < _r.reactantEnd(); i = _r.reactantNext(i)) { if (i != _r.reactantBegin()) _pushSpace(line, plus_interval_factor); _pushMol(line, i); } _pushSpace(line, arrow_interval_factor); for (int i = _r.productBegin(); i < _r.productEnd(); i = _r.productNext(i)) { if (i != _r.productBegin()) _pushSpace(line, plus_interval_factor); _pushMol(line, i); } _ml.bondLength = bond_length; _ml.cb_getMol = cb_getMol; _ml.cb_process = cb_process; _ml.context = this; _ml.prepare(); _ml.scaleSz(); _ml.calcContentSize(); _ml.process(); } Metalayout::LayoutItem& ReactionLayout::_pushMol (Metalayout::LayoutLine& line, int id) { Metalayout::LayoutItem& item = line.items.push(); item.type = 0; item.fragment = true; item.id = id; Metalayout::getBoundRect(item.min, item.max, _getMol(id)); return item; } Metalayout::LayoutItem& ReactionLayout::_pushSpace (Metalayout::LayoutLine& line, float size) { Metalayout::LayoutItem& item = line.items.push(); item.type = 1; item.fragment = false; item.scaledSize.set(size, 0); return item; } BaseMolecule& ReactionLayout::_getMol (int id) { return _r.getBaseMolecule(id); } BaseMolecule& ReactionLayout::cb_getMol (int id, void* context) { return ((ReactionLayout*)context)->_getMol(id); } void ReactionLayout::cb_process (Metalayout::LayoutItem& item, const Vec2f& pos, void* context) { Vec2f pos2; pos2.copy(pos); pos2.y -= item.scaledSize.y / 2; if (item.fragment) { ReactionLayout* layout = (ReactionLayout*)context; layout->_ml.adjustMol(layout->_getMol(item.id), item.min, pos2); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/refinement_state.cpp�������������������������������������������������0000664�0000000�0000000�00000012464�12710376503�0022354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "layout/refinement_state.h" using namespace indigo; IMPL_ERROR(RefinementState, "refinement"); CP_DEF(RefinementState); RefinementState::RefinementState (MoleculeLayoutGraph &graph) : dist(0.f), energy(0), height(0.f), CP_INIT, TL_CP_GET(layout), _graph(graph) { } void RefinementState::calcDistance (int v1, int v2) { Vec2f d; d.diff(layout[v1], layout[v2]); dist = d.lengthSqr(); } void RefinementState::calcHeight () { float min = 1000.f, max = -1000.f; int i; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (layout[i].y < min) min = layout[i].y; if (layout[i].y > max) max = layout[i].y; } height = max - min; } void RefinementState::copy (const RefinementState &other) { dist = other.dist; energy = other.energy; height = other.height; layout.copy(other.layout); } void RefinementState::copyFromGraph () { int i; layout.clear_resize(_graph.vertexEnd()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) layout[i] = _graph.getPos(i); } void RefinementState::applyToGraph () { int i; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) _graph.getPos(i) = layout[i]; } void RefinementState::calcEnergy () { int i, j; float r; Vec2f d; energy = 0; for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) for (j = _graph.vertexBegin(); j < _graph.vertexEnd(); j = _graph.vertexNext(j)) { if (i == j) continue; d.diff(layout[i], layout[j]); r = d.lengthSqr(); if (r < 0.0001f) r = 5000000.f; else r = 1 / r; energy += r; } energy /= 2; } // Flip all verices from branch around (v1,v2) void RefinementState::flipBranch (const Filter &branch, const RefinementState &state, int v1_idx, int v2_idx) { int i; float r, t; const Vec2f &v1 = state.layout[v1_idx]; const Vec2f &v2 = state.layout[v2_idx]; Vec2f d; d.diff(v2, v1); r = d.lengthSqr(); if (r < 0.000000001f) throw Error("too small edge"); layout.clear_resize(state.layout.size()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (!branch.valid(i)) { const Vec2f &vi = state.layout[i]; t = ((vi.x - v1.x) * d.x + (vi.y - v1.y) * d.y) / r; layout[i].set(2 * d.x * t + 2 * v1.x - vi.x, 2 * d.y * t + 2 * v1.y - vi.y); } else layout[i] = state.layout[i]; } } // Rotate branch around vertex v1 void RefinementState::rotateBranch (const Filter &branch, const RefinementState &state, int v_idx, float angle) { int i; float co, si; const Vec2f &v = state.layout[v_idx]; Vec2f d; angle = DEG2RAD(angle); co = cos(angle); si = sin(angle); layout.clear_resize(state.layout.size()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (!branch.valid(i)) { d.diff(state.layout[i], v); d.rotate(si, co); layout[i].sum(d, v); } else layout[i] = state.layout[i]; } } // Translate branch on 0.1 of vector [v1,v2] void RefinementState::stretchBranch (const Filter &branch, const RefinementState &state, int v1_idx, int v2_idx, int val) { int i; float r, sh = 0.1f * val; const Vec2f &v1 = state.layout[v1_idx]; const Vec2f &v2 = state.layout[v2_idx]; Vec2f d; d.diff(v2, v1); r = d.length(); if (r < EPSILON) throw Error("too small edge"); d.scale(sh / r); if (branch.valid(v1_idx)) d.negate(); layout.clear_resize(state.layout.size()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { if (!branch.valid(i)) layout[i].sum(state.layout[i], d); else layout[i] = state.layout[i]; } } // Rotate layout around vertex v (in degrees) void RefinementState::rotateLayout (const RefinementState &state, int v_idx, float angle) { int i; float co, si; const Vec2f &v = state.layout[v_idx]; Vec2f d; angle = DEG2RAD(angle); co = cos(angle); si = sin(angle); layout.clear_resize(state.layout.size()); for (i = _graph.vertexBegin(); i < _graph.vertexEnd(); i = _graph.vertexNext(i)) { d.diff(state.layout[i], v); d.rotate(si, co); layout[i].sum(d, v); } } bool RefinementState::is_small_cycle() { if (_graph.vertexCount() >= 10) return false; bool answ = true; for (int v = _graph.vertexBegin(); v != _graph.vertexEnd(); v++) if (_graph.getVertex(v).degree() != 2) answ = false; return answ; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/�����������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0020303�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/generate_layout_patterns.py��������������������������������0000664�0000000�0000000�00000001215�12710376503�0025763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import os templates_file = open('layout_patterns.inc', 'w') names = list(os.listdir('molecules')) templates_file.write('// Templates from the following files:\n') for fname in names: templates_file.write('// {}\n'.format(fname)) templates_file.write('static const char* layout_templates[] =\n') templates_file.write('{\n') for fname in names: content = open(os.path.join('molecules', fname)).read().split("\n") # replace first line with file name content[0] = fname lines = [' "' + line + '\\n"' for line in content] templates_file.write('\n'.join(lines)) templates_file.write(',\n\n') templates_file.write("};\n") �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/layout_patterns.inc����������������������������������������0000664�0000000�0000000�00000300150�12710376503�0024232�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Templates from the following files: // 1293.mol // aw00196.mol // big_semicube.mol // btb12419.mol // cd00846.mol // complexring1.mol // complexring2.mol // complexring3.mol // complexring4.mol // complexring5.mol // complexring6.mol // complexring7.mol // cube.mol // highvalence.mol // krabe.mol // krabe2.mol // s13133.mol // sphere.mol // sphere2.mol // sphere_mod1.mol // superstar.mol static const char* layout_templates[] = { "1293.mol\n" " Marvin 05180922082D \n" "\n" " 7 8 0 0 0 0 999 V2000\n" " -0.3123 -0.6177 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3470 0.9485 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.1198 -0.5992 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4350 -0.8884 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0046 0.2337 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1198 -0.6431 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7866 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 1 4 1 0 0 0 0\n" " 5 2 1 0 0 0 0\n" " 4 6 2 0 0 0 0\n" " 5 7 1 0 0 0 0\n" " 5 3 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" "M END\n" "\n" "\n", "aw00196.mol\n" " Marvin 06090921352D \n" "\n" " 7 8 0 0 0 0 999 V2000\n" " -2.0388 -0.2887 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6962 0.0642 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5610 1.6539 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.6608 0.4673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.3686 0.8957 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4681 -0.2199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.1452 0.4856 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 3 2 1 0 0 0 0\n" " 4 1 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 6 2 1 0 0 0 0\n" " 7 5 1 0 0 0 0\n" " 5 3 1 0 0 0 0\n" " 7 6 1 0 0 0 0\n" "M END\n" "\n" "\n", "big_semicube.mol\n" " Marvin 06090922152D \n" "\n" " 10 12 0 0 0 0 999 V2000\n" " 12.5528 8.6716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.9697 7.3910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.5527 11.5607 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 12.5528 2.8847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.5133 6.0185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5351 8.6716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.5527 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.2406 7.2947 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.5133 3.2738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5351 2.8847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 1 4 1 0 0 0 0\n" " 2 5 1 0 0 0 0\n" " 3 6 1 0 0 0 0\n" " 4 7 1 0 0 0 0\n" " 5 8 1 0 0 0 0\n" " 5 9 1 0 0 0 0\n" " 6 8 1 0 0 0 0\n" " 6 10 1 0 0 0 0\n" " 7 9 1 0 0 0 0\n" " 7 10 1 0 0 0 0\n" "M END\n" "\n", "btb12419.mol\n" " Marvin 06090923042D \n" "\n" " 12 17 0 0 1 0 999 V2000\n" " 4.5956 -45.8571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4328 -45.4936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.0438 -44.9212 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4328 -44.7021 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.0438 -44.0962 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6697 -44.9212 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0638 -45.4936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0638 -44.3695 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6697 -43.7636 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8398 -44.3695 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4380 -43.7636 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.2162 -42.7762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 3 1 1 0 0 0 0\n" " 4 2 1 0 0 0 0\n" " 5 3 1 0 0 0 0\n" " 6 3 1 0 0 0 0\n" " 7 2 1 0 0 0 0\n" " 8 7 1 0 0 0 0\n" " 9 6 1 0 0 0 0\n" " 10 4 1 0 0 0 0\n" " 11 5 1 0 0 0 0\n" " 12 11 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 11 9 1 0 0 0 0\n" " 9 8 1 0 0 0 0\n" " 10 8 1 0 0 0 0\n" " 12 10 1 0 0 0 0\n" "M END\n" "\n", "cd00846.mol\n" " Marvin 05180922142D \n" "\n" " 10 12 0 0 0 0 999 V2000\n" " -2.6370 0.3582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8659 0.3582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.2481 1.4304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7204 -0.2216 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6370 1.0722 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.1742 -0.7141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.2481 0.0045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8659 1.0722 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5106 -0.5171 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7204 0.6783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 7 1 0 0 0 0\n" " 3 5 1 0 0 0 0\n" " 4 6 1 0 0 0 0\n" " 5 1 1 0 0 0 0\n" " 6 1 1 0 0 0 0\n" " 7 1 1 0 0 0 0\n" " 8 2 1 0 0 0 0\n" " 9 2 1 0 0 0 0\n" " 10 4 1 0 0 0 0\n" " 9 4 1 0 0 0 0\n" " 8 3 1 0 0 0 0\n" " 10 3 1 0 0 0 0\n" "M END\n" "\n" "\n", "complexring1.mol\n" " Marvin 06090923582D \n" "\n" " 49 56 0 0 1 0 999 V2000\n" " 8.3868 5.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.6526 5.9130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4069 4.6962 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.9488 5.4806 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.6325 6.7476 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.6929 4.2637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.9689 4.6560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.0493 7.3409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.7230 3.4392 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.2806 8.1253 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.2549 7.1297 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.1297 2.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.6873 8.7186 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.6716 7.7130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.3208 2.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.3353 3.0972 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.8928 8.5275 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.7275 1.4883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.7420 2.5240 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.3297 9.1007 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.9331 1.7196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5152 8.8996 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.3398 1.1464 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9319 9.4929 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2939 8.1052 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5353 1.3676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1375 9.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4895 7.8940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9520 0.7843 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.3241 2.1621 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9163 8.4873 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1576 1.0056 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5297 2.3833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1117 8.2761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9464 1.8000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8503 7.4214 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2828 1.9710 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0458 7.2404 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4135 6.8080 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.9911 2.7453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7944 6.4560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1520 6.0135 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1665 2.8761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5039 3.3889 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.3475 5.8426 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8648 3.6503 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2023 4.1632 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0860 5.0582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.3878 4.2838 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 2 4 1 0 0 0 0\n" " 2 5 1 0 0 0 0\n" " 3 6 1 0 0 0 0\n" " 4 7 1 0 0 0 0\n" " 8 5 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 6 9 1 0 0 0 0\n" " 8 10 1 0 0 0 0\n" " 8 11 1 0 0 0 0\n" " 12 9 1 0 0 0 0\n" " 10 13 1 0 0 0 0\n" " 11 14 1 0 0 0 0\n" " 12 15 1 0 0 0 0\n" " 12 16 1 0 0 0 0\n" " 13 17 1 0 0 0 0\n" " 14 17 1 0 0 0 0\n" " 15 18 1 0 0 0 0\n" " 16 19 1 0 0 0 0\n" " 17 20 1 0 0 0 0\n" " 18 21 1 0 0 0 0\n" " 19 21 1 0 0 0 0\n" " 22 20 1 0 0 0 0\n" " 21 23 1 0 0 0 0\n" " 22 24 1 0 0 0 0\n" " 22 25 1 0 0 0 0\n" " 26 23 1 0 0 0 0\n" " 24 27 1 0 0 0 0\n" " 25 28 1 0 0 0 0\n" " 26 29 1 0 0 0 0\n" " 26 30 1 0 0 0 0\n" " 27 31 1 0 0 0 0\n" " 28 31 1 0 0 0 0\n" " 29 32 1 0 0 0 0\n" " 30 33 1 0 0 0 0\n" " 31 34 1 0 0 0 0\n" " 32 35 1 0 0 0 0\n" " 33 35 1 0 0 0 0\n" " 36 34 1 0 0 0 0\n" " 35 37 1 0 0 0 0\n" " 36 38 1 0 0 0 0\n" " 36 39 1 0 0 0 0\n" " 40 37 1 0 0 0 0\n" " 38 41 1 0 0 0 0\n" " 39 42 1 0 0 0 0\n" " 40 43 1 0 0 0 0\n" " 40 44 1 0 0 0 0\n" " 41 45 1 0 0 0 0\n" " 42 45 1 0 0 0 0\n" " 43 46 1 0 0 0 0\n" " 44 47 1 0 0 0 0\n" " 45 48 1 0 0 0 0\n" " 46 49 1 0 0 0 0\n" " 47 49 1 0 0 0 0\n" " 49 48 1 0 0 0 0\n" "M END\n" "\n", "complexring2.mol\n" " Marvin 06090923582D \n" "\n" " 56 64 0 0 0 0 999 V2000\n" " -13.2758 -47.4415 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -12.4922 -47.1624 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.4099 -48.2143 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.0592 -47.0605 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -11.8751 -47.6885 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -12.8464 -48.7671 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.0914 -46.0248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -12.0575 -48.4988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.9125 -45.8584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.5441 -45.3755 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -11.4458 -49.1696 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -15.1593 -45.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.8284 -44.6509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.6408 -48.9658 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.6602 -44.4900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.3133 -48.1983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.1310 -49.6310 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -15.1593 -43.6152 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -9.5138 -48.1339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -9.3421 -49.5345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.3920 -43.0893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -9.0308 -48.8153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.6602 -42.3219 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.5441 -43.2503 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.2150 -48.7349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -14.1289 -41.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.0610 -42.5849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.6302 -47.8978 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -13.3240 -41.8335 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.0111 -47.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.8306 -47.9513 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -12.9430 -41.0876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.5818 -46.4757 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.3797 -47.2591 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -12.0252 -41.0876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.7286 -46.5453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -11.7087 -40.3363 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -11.4940 -41.7369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.1489 -45.8746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.8929 -40.2343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.7051 -41.5974 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.3474 -45.0588 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -10.4207 -40.8139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.1471 -44.8494 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.7626 -44.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -9.5138 -40.5188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.3457 -44.0499 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.9450 -43.7064 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.8483 -41.2700 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.7286 -43.4865 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.1292 -40.9158 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.9824 -42.0697 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.7286 -42.6170 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.4799 -41.3989 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -8.2794 -42.5527 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -7.5658 -42.2145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 1 4 1 0 0 0 0\n" " 2 5 1 0 0 0 0\n" " 3 6 1 0 0 0 0\n" " 4 7 1 0 0 0 0\n" " 8 5 1 0 0 0 0\n" " 7 9 1 0 0 0 0\n" " 7 10 1 0 0 0 0\n" " 8 11 1 0 0 0 0\n" " 9 12 1 0 0 0 0\n" " 10 13 1 0 0 0 0\n" " 14 11 1 0 0 0 0\n" " 12 15 1 0 0 0 0\n" " 14 16 1 0 0 0 0\n" " 14 17 1 0 0 0 0\n" " 15 18 1 0 0 0 0\n" " 16 19 1 0 0 0 0\n" " 17 20 1 0 0 0 0\n" " 18 21 1 0 0 0 0\n" " 22 19 1 0 0 0 0\n" " 21 23 1 0 0 0 0\n" " 21 24 1 0 0 0 0\n" " 22 25 1 0 0 0 0\n" " 23 26 1 0 0 0 0\n" " 24 27 1 0 0 0 0\n" " 28 25 1 0 0 0 0\n" " 26 29 1 0 0 0 0\n" " 28 30 1 0 0 0 0\n" " 28 31 1 0 0 0 0\n" " 29 32 1 0 0 0 0\n" " 30 33 1 0 0 0 0\n" " 31 34 1 0 0 0 0\n" " 32 35 1 0 0 0 0\n" " 36 33 1 0 0 0 0\n" " 35 37 1 0 0 0 0\n" " 35 38 1 0 0 0 0\n" " 36 39 1 0 0 0 0\n" " 37 40 1 0 0 0 0\n" " 38 41 1 0 0 0 0\n" " 42 39 1 0 0 0 0\n" " 40 43 1 0 0 0 0\n" " 42 44 1 0 0 0 0\n" " 42 45 1 0 0 0 0\n" " 43 46 1 0 0 0 0\n" " 44 47 1 0 0 0 0\n" " 45 48 1 0 0 0 0\n" " 46 49 1 0 0 0 0\n" " 50 47 1 0 0 0 0\n" " 49 51 1 0 0 0 0\n" " 49 52 1 0 0 0 0\n" " 50 53 1 0 0 0 0\n" " 51 54 1 0 0 0 0\n" " 52 55 1 0 0 0 0\n" " 56 53 1 0 0 0 0\n" " 6 8 1 0 0 0 0\n" " 13 15 1 0 0 0 0\n" " 20 22 1 0 0 0 0\n" " 27 29 1 0 0 0 0\n" " 34 36 1 0 0 0 0\n" " 41 43 1 0 0 0 0\n" " 48 50 1 0 0 0 0\n" " 54 56 1 0 0 0 0\n" " 55 56 1 0 0 0 0\n" "M END\n" "\n", "complexring3.mol\n" " Marvin 06240919342D \n" "\n" " 42 48 0 0 1 0 999 V2000\n" " 2.1300 0.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7843 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1300 2.0254 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4025 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7843 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0624 2.6377 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4025 -0.3925 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0829 -0.8269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0624 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3820 2.2191 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0829 -2.0515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3350 3.8467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3297 2.6221 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0519 -2.6063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3297 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0519 -3.4070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3350 -2.1981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4812 4.0194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3350 -3.8309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3297 -2.5906 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5487 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3297 -3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.2291 3.8728 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5487 2.6221 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.3974 -3.9305 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9566 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.2606 2.1719 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5487 -3.3181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9566 2.5958 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.1977 -3.8153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5487 -2.5331 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.0714 2.0254 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9566 -3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.2762 -2.1509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.0714 0.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9566 -2.5906 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.7674 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.3386 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.0714 -2.0096 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.7674 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.3386 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.0714 -0.8269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 1 3 1 0 0 0 0\n" " 1 4 1 0 0 0 0\n" " 2 5 1 0 0 0 0\n" " 6 3 1 0 0 0 0\n" " 4 7 1 0 0 0 0\n" " 5 8 1 0 0 0 0\n" " 6 9 1 0 0 0 0\n" " 6 10 1 0 0 0 0\n" " 8 11 1 0 0 0 0\n" " 9 12 1 0 0 0 0\n" " 10 13 1 0 0 0 0\n" " 14 11 1 0 0 0 0\n" " 12 15 1 0 0 0 0\n" " 14 16 1 0 0 0 0\n" " 14 17 1 0 0 0 0\n" " 15 18 1 0 0 0 0\n" " 16 19 1 0 0 0 0\n" " 17 20 1 0 0 0 0\n" " 21 18 1 0 0 0 0\n" " 19 22 1 0 0 0 0\n" " 21 23 1 0 0 0 0\n" " 21 24 1 0 0 0 0\n" " 22 25 1 0 0 0 0\n" " 23 26 1 0 0 0 0\n" " 24 27 1 0 0 0 0\n" " 28 25 1 0 0 0 0\n" " 26 29 1 0 0 0 0\n" " 28 30 1 0 0 0 0\n" " 28 31 1 0 0 0 0\n" " 29 32 1 0 0 0 0\n" " 30 33 1 0 0 0 0\n" " 31 34 1 0 0 0 0\n" " 35 32 1 0 0 0 0\n" " 33 36 1 0 0 0 0\n" " 35 37 1 0 0 0 0\n" " 35 38 1 0 0 0 0\n" " 36 39 1 0 0 0 0\n" " 37 40 1 0 0 0 0\n" " 38 41 1 0 0 0 0\n" " 42 39 1 0 0 0 0\n" " 7 8 1 0 0 0 0\n" " 13 15 1 0 0 0 0\n" " 20 22 1 0 0 0 0\n" " 27 29 1 0 0 0 0\n" " 34 36 1 0 0 0 0\n" " 40 42 1 0 0 0 0\n" " 41 42 1 0 0 0 0\n" "M END\n" "\n", "complexring4.mol\n" " Mrv0541 03181118532D \n" "\n" " 80104 0 0 1 0 999 V2000\n" " -2.4750 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9599 0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7446 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5292 0.6675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.0141 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9599 -0.6675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7445 -0.4126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5291 -0.6676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.0140 -0.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5291 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7445 0.4124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9599 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4749 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6210 -2.5650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.3561 -2.9396 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7305 -3.6746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5454 -3.5456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6745 -2.7308 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9394 -2.3562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5648 -1.6211 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7500 -1.7502 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.6746 2.7307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9395 2.3561 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5649 1.6210 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.7501 1.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.6210 2.5649 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.3561 2.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7306 3.6745 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5455 3.5455 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7307 3.6745 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.3561 2.9394 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6210 2.5648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7501 1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5649 1.6209 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9395 2.3561 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6745 2.7305 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5455 3.5454 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5456 -3.5455 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7308 -3.6746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.3562 -2.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.6211 -2.5649 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.7502 -1.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6674 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.4125 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6674 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 2.4749 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6674 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4125 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6675 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 5.0140 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6675 -2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.4126 -3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6676 -4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0001 -5.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6673 -4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4124 -3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6673 -2.9600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 -2.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5868 1.0714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.4346 1.8368 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.6746 -2.7306 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9396 -2.3562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5650 -1.6210 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9600 -0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7446 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5292 -0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5869 -1.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.4347 -1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0715 2.5868 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8369 4.4345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0715 2.5868 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8369 4.4346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5869 1.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.4346 1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5868 -1.0716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.4346 -1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0714 -2.5869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8368 -4.4347 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0716 -2.5869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8370 -4.4346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 64 1 1 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 65 3 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 66 5 1 0 0 0 0\n" " 8 9 1 0 0 0 0\n" " 9 10 1 0 0 0 0\n" " 11 10 1 0 0 0 0\n" " 7 11 1 0 0 0 0\n" " 11 12 1 0 0 0 0\n" " 13 12 1 0 0 0 0\n" " 6 13 1 0 0 0 0\n" " 16 17 1 0 0 0 0\n" " 17 18 1 0 0 0 0\n" " 15 19 1 0 0 0 0\n" " 21 20 1 0 0 0 0\n" " 14 21 1 0 0 0 0\n" " 24 25 1 0 0 0 0\n" " 25 26 1 0 0 0 0\n" " 27 23 1 0 0 0 0\n" " 29 28 1 0 0 0 0\n" " 22 29 1 0 0 0 0\n" " 32 33 1 0 0 0 0\n" " 33 34 1 0 0 0 0\n" " 35 34 1 0 0 0 0\n" " 35 31 1 0 0 0 0\n" " 35 36 1 0 0 0 0\n" " 37 36 1 0 0 0 0\n" " 30 37 1 0 0 0 0\n" " 61 38 1 0 0 0 0\n" " 38 39 1 0 0 0 0\n" " 62 40 1 0 0 0 0\n" " 42 41 1 0 0 0 0\n" " 63 42 1 0 0 0 0\n" " 45 46 1 0 0 0 0\n" " 46 47 1 0 0 0 0\n" " 48 44 1 0 0 0 0\n" " 50 49 1 0 0 0 0\n" " 43 50 1 0 0 0 0\n" " 53 54 1 0 0 0 0\n" " 54 55 1 0 0 0 0\n" " 52 56 1 0 0 0 0\n" " 58 57 1 0 0 0 0\n" " 51 58 1 0 0 0 0\n" " 36 60 1 0 0 0 0\n" " 10 60 1 0 0 0 0\n" " 34 59 1 0 0 0 0\n" " 12 59 1 0 0 0 0\n" " 66 68 1 0 0 0 0\n" " 61 68 1 0 0 0 0\n" " 64 67 1 0 0 0 0\n" " 63 67 1 0 0 0 0\n" " 62 61 1 0 0 0 0\n" " 62 63 1 0 0 0 0\n" " 65 64 1 0 0 0 0\n" " 65 66 1 0 0 0 0\n" " 32 69 1 0 0 0 0\n" " 31 32 1 0 0 0 0\n" " 31 30 1 0 0 0 0\n" " 30 70 1 0 0 0 0\n" " 47 69 1 0 0 0 0\n" " 48 47 1 0 0 0 0\n" " 48 49 1 0 0 0 0\n" " 49 70 1 0 0 0 0\n" " 26 71 1 0 0 0 0\n" " 27 26 1 0 0 0 0\n" " 43 72 1 0 0 0 0\n" " 44 43 1 0 0 0 0\n" " 44 45 1 0 0 0 0\n" " 45 71 1 0 0 0 0\n" " 27 28 1 0 0 0 0\n" " 28 72 1 0 0 0 0\n" " 2 73 1 0 0 0 0\n" " 3 2 1 0 0 0 0\n" " 22 74 1 0 0 0 0\n" " 23 22 1 0 0 0 0\n" " 23 24 1 0 0 0 0\n" " 24 73 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 4 74 1 0 0 0 0\n" " 20 75 1 0 0 0 0\n" " 19 20 1 0 0 0 0\n" " 8 76 1 0 0 0 0\n" " 7 8 1 0 0 0 0\n" " 7 6 1 0 0 0 0\n" " 6 75 1 0 0 0 0\n" " 19 18 1 0 0 0 0\n" " 18 76 1 0 0 0 0\n" " 14 77 1 0 0 0 0\n" " 15 14 1 0 0 0 0\n" " 15 16 1 0 0 0 0\n" " 16 78 1 0 0 0 0\n" " 57 77 1 0 0 0 0\n" " 56 57 1 0 0 0 0\n" " 56 55 1 0 0 0 0\n" " 55 78 1 0 0 0 0\n" " 51 79 1 0 0 0 0\n" " 52 51 1 0 0 0 0\n" " 52 53 1 0 0 0 0\n" " 53 80 1 0 0 0 0\n" " 41 79 1 0 0 0 0\n" " 40 41 1 0 0 0 0\n" " 40 39 1 0 0 0 0\n" " 39 80 1 0 0 0 0\n" "M END\n" "\n", "complexring5.mol\n" " Mrv0541 03181119082D \n" "\n" " 70 91 0 0 1 0 999 V2000\n" " 1.9966 -3.1947 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2532 -3.5526 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.3640 -4.3701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1758 -4.5174 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5667 -3.7909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8858 -2.3771 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0740 -2.2299 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6831 -2.9563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.2528 -3.5528 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.9961 -3.1949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5663 -3.7912 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1753 -4.5176 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.3635 -4.3703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6827 -2.9564 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0737 -2.2300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8855 -2.3773 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5588 -1.2356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7424 -0.4312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5640 -0.3573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.8883 -1.1159 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.2670 -1.6587 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7371 -1.3094 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.4129 -0.5509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.0341 -0.0081 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.1849 2.0121 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6705 2.6571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.1250 3.3456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9203 3.1261 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9573 2.3019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7303 1.3235 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.9350 1.5431 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8981 2.3672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.4126 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4124 3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6673 4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0003 5.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6676 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6675 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0001 2.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6673 2.9600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6703 2.6573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1847 2.0123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9571 2.3023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9200 3.1264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1248 3.3459 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8979 2.3674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.9350 1.5432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7303 1.3238 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7425 -0.4309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5590 -1.2352 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2672 -1.6583 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.8885 -1.1155 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5641 -0.3569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0343 -0.0078 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4130 -0.5507 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7373 -1.3092 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1111 -1.6833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7530 -2.9925 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6323 0.6009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.6796 1.0684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1714 2.4327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0825 4.3248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.1715 2.4325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.0829 4.3244 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6322 0.6006 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.6796 1.0679 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1107 -1.6834 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7524 -2.9930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0001 -2.6999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0005 -4.7999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 1 5 1 0 0 0 0\n" " 1 6 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 8 7 1 0 0 0 0\n" " 10 9 1 0 0 0 0\n" " 11 12 1 0 0 0 0\n" " 13 12 1 0 0 0 0\n" " 14 15 1 0 0 0 0\n" " 16 15 1 0 0 0 0\n" " 18 17 1 0 0 0 0\n" " 19 20 1 0 0 0 0\n" " 21 20 1 0 0 0 0\n" " 22 23 1 0 0 0 0\n" " 24 23 1 0 0 0 0\n" " 26 25 1 0 0 0 0\n" " 27 28 1 0 0 0 0\n" " 29 28 1 0 0 0 0\n" " 30 31 1 0 0 0 0\n" " 32 31 1 0 0 0 0\n" " 34 33 1 0 0 0 0\n" " 35 36 1 0 0 0 0\n" " 37 36 1 0 0 0 0\n" " 38 39 1 0 0 0 0\n" " 40 39 1 0 0 0 0\n" " 41 42 1 0 0 0 0\n" " 43 44 1 0 0 0 0\n" " 45 44 1 0 0 0 0\n" " 46 47 1 0 0 0 0\n" " 48 47 1 0 0 0 0\n" " 50 49 1 0 0 0 0\n" " 50 51 1 0 0 0 0\n" " 51 52 1 0 0 0 0\n" " 53 52 1 0 0 0 0\n" " 54 55 1 0 0 0 0\n" " 56 55 1 0 0 0 0\n" " 50 56 1 0 0 0 0\n" " 56 57 1 0 0 0 0\n" " 6 57 1 0 0 0 0\n" " 51 58 1 0 0 0 0\n" " 5 58 1 0 0 0 0\n" " 54 59 1 0 0 0 0\n" " 49 54 1 0 0 0 0\n" " 49 53 1 0 0 0 0\n" " 53 60 1 0 0 0 0\n" " 42 43 1 0 0 0 0\n" " 43 60 1 0 0 0 0\n" " 42 48 1 0 0 0 0\n" " 48 59 1 0 0 0 0\n" " 45 62 1 0 0 0 0\n" " 41 45 1 0 0 0 0\n" " 41 46 1 0 0 0 0\n" " 46 61 1 0 0 0 0\n" " 34 35 1 0 0 0 0\n" " 35 62 1 0 0 0 0\n" " 34 40 1 0 0 0 0\n" " 40 61 1 0 0 0 0\n" " 38 63 1 0 0 0 0\n" " 33 38 1 0 0 0 0\n" " 33 37 1 0 0 0 0\n" " 37 64 1 0 0 0 0\n" " 26 27 1 0 0 0 0\n" " 27 64 1 0 0 0 0\n" " 26 32 1 0 0 0 0\n" " 32 63 1 0 0 0 0\n" " 18 19 1 0 0 0 0\n" " 19 66 1 0 0 0 0\n" " 25 29 1 0 0 0 0\n" " 29 66 1 0 0 0 0\n" " 25 30 1 0 0 0 0\n" " 30 65 1 0 0 0 0\n" " 18 24 1 0 0 0 0\n" " 24 65 1 0 0 0 0\n" " 17 22 1 0 0 0 0\n" " 22 67 1 0 0 0 0\n" " 17 21 1 0 0 0 0\n" " 21 68 1 0 0 0 0\n" " 10 11 1 0 0 0 0\n" " 11 68 1 0 0 0 0\n" " 10 16 1 0 0 0 0\n" " 16 67 1 0 0 0 0\n" " 14 69 1 0 0 0 0\n" " 9 14 1 0 0 0 0\n" " 9 13 1 0 0 0 0\n" " 13 70 1 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3 70 1 0 0 0 0\n" " 2 8 1 0 0 0 0\n" " 8 69 1 0 0 0 0\n" "M END\n" "\n", "complexring6.mol\n" " Mrv0541 03181119092D \n" "\n" "100130 0 0 1 0 999 V2000\n" " 1.8047 -4.2183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0201 -4.4733 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0201 -5.2983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8047 -5.5532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2896 -4.8857 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8046 -3.3933 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0201 -3.1385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.5351 -3.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0200 4.4732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8047 4.2182 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8047 3.3932 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0200 3.1383 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.5351 3.8058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0203 5.2981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8048 5.5530 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.2897 4.8856 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0194 -4.4734 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8039 -5.5534 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0193 -5.2984 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.5346 -3.8060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0194 -3.1386 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8042 4.2185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0195 4.4734 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.5346 3.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0195 3.1385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8042 3.3935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2889 4.8859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8040 5.5533 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0193 5.2984 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9390 2.3525 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4541 3.0198 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6695 2.7648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6696 1.9398 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4542 1.6850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7237 2.6075 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7235 3.4324 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9390 3.6873 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9390 -2.3525 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.7237 -2.6073 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.7235 -3.4324 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6696 -1.9399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4543 -1.6849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5695 -0.4127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5695 0.4122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.3541 0.6672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.8389 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.3541 -0.6677 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7849 -0.6676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.3000 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.7849 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5695 -0.4122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5695 0.4127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7848 0.6677 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3000 0.0002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7849 -0.6672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.3541 -0.6671 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.8389 0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.3541 0.6678 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9394 2.3520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4545 3.0194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9393 3.6869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.7240 3.4319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.7240 2.6070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4545 1.6846 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6699 1.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6698 2.7646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4545 -3.0195 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9394 -2.3520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4545 -1.6845 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6699 -1.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6699 -2.7645 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9394 -3.6869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7240 -3.4319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7240 -2.6069 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1160 2.9124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3798 4.6518 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4541 -3.0199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.6696 -2.7648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.9390 -3.6872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8040 -4.2185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.8042 -3.3936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.2889 -4.8859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1156 -2.9127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.3791 -4.6523 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 3.5999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0001 5.7499 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1161 2.9125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.3797 4.6519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4239 1.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.4686 1.7772 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4240 -1.1124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.4686 -1.7773 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4237 1.1130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.4683 1.7777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4239 -1.1122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.4688 -1.7762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1164 -2.9123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3804 -4.6515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0004 -3.6000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0009 -5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 8 7 1 0 0 0 0\n" " 9 10 1 0 0 0 0\n" " 11 12 1 0 0 0 0\n" " 13 12 1 0 0 0 0\n" " 14 15 1 0 0 0 0\n" " 16 15 1 0 0 0 0\n" " 80 17 1 0 0 0 0\n" " 82 18 1 0 0 0 0\n" " 19 18 1 0 0 0 0\n" " 20 21 1 0 0 0 0\n" " 81 21 1 0 0 0 0\n" " 22 23 1 0 0 0 0\n" " 24 25 1 0 0 0 0\n" " 26 25 1 0 0 0 0\n" " 22 26 1 0 0 0 0\n" " 22 27 1 0 0 0 0\n" " 27 28 1 0 0 0 0\n" " 29 28 1 0 0 0 0\n" " 30 31 1 0 0 0 0\n" " 32 33 1 0 0 0 0\n" " 34 33 1 0 0 0 0\n" " 35 36 1 0 0 0 0\n" " 37 36 1 0 0 0 0\n" " 31 32 1 0 0 0 0\n" " 31 37 1 0 0 0 0\n" " 77 38 1 0 0 0 0\n" " 39 40 1 0 0 0 0\n" " 79 40 1 0 0 0 0\n" " 78 41 1 0 0 0 0\n" " 42 41 1 0 0 0 0\n" " 44 43 1 0 0 0 0\n" " 45 46 1 0 0 0 0\n" " 47 46 1 0 0 0 0\n" " 48 49 1 0 0 0 0\n" " 50 49 1 0 0 0 0\n" " 51 52 1 0 0 0 0\n" " 53 54 1 0 0 0 0\n" " 55 54 1 0 0 0 0\n" " 56 57 1 0 0 0 0\n" " 58 57 1 0 0 0 0\n" " 60 59 1 0 0 0 0\n" " 61 62 1 0 0 0 0\n" " 63 62 1 0 0 0 0\n" " 64 65 1 0 0 0 0\n" " 66 65 1 0 0 0 0\n" " 67 68 1 0 0 0 0\n" " 69 70 1 0 0 0 0\n" " 71 70 1 0 0 0 0\n" " 72 73 1 0 0 0 0\n" " 74 73 1 0 0 0 0\n" " 27 76 1 0 0 0 0\n" " 37 76 1 0 0 0 0\n" " 26 75 1 0 0 0 0\n" " 32 75 1 0 0 0 0\n" " 79 84 1 0 0 0 0\n" " 82 84 1 0 0 0 0\n" " 78 83 1 0 0 0 0\n" " 81 83 1 0 0 0 0\n" " 77 79 1 0 0 0 0\n" " 77 78 1 0 0 0 0\n" " 80 81 1 0 0 0 0\n" " 80 82 1 0 0 0 0\n" " 9 13 1 0 0 0 0\n" " 13 85 1 0 0 0 0\n" " 9 14 1 0 0 0 0\n" " 14 86 1 0 0 0 0\n" " 23 24 1 0 0 0 0\n" " 24 85 1 0 0 0 0\n" " 23 29 1 0 0 0 0\n" " 29 86 1 0 0 0 0\n" " 11 87 1 0 0 0 0\n" " 10 11 1 0 0 0 0\n" " 60 66 1 0 0 0 0\n" " 66 87 1 0 0 0 0\n" " 60 61 1 0 0 0 0\n" " 61 88 1 0 0 0 0\n" " 10 16 1 0 0 0 0\n" " 16 88 1 0 0 0 0\n" " 63 90 1 0 0 0 0\n" " 59 63 1 0 0 0 0\n" " 44 50 1 0 0 0 0\n" " 50 89 1 0 0 0 0\n" " 44 45 1 0 0 0 0\n" " 45 90 1 0 0 0 0\n" " 59 64 1 0 0 0 0\n" " 64 89 1 0 0 0 0\n" " 38 42 1 0 0 0 0\n" " 42 91 1 0 0 0 0\n" " 38 39 1 0 0 0 0\n" " 39 92 1 0 0 0 0\n" " 43 48 1 0 0 0 0\n" " 48 91 1 0 0 0 0\n" " 43 47 1 0 0 0 0\n" " 47 92 1 0 0 0 0\n" " 35 94 1 0 0 0 0\n" " 30 35 1 0 0 0 0\n" " 52 53 1 0 0 0 0\n" " 53 93 1 0 0 0 0\n" " 52 58 1 0 0 0 0\n" " 58 94 1 0 0 0 0\n" " 30 34 1 0 0 0 0\n" " 34 93 1 0 0 0 0\n" " 74 96 1 0 0 0 0\n" " 68 74 1 0 0 0 0\n" " 68 69 1 0 0 0 0\n" " 69 95 1 0 0 0 0\n" " 51 55 1 0 0 0 0\n" " 55 95 1 0 0 0 0\n" " 51 56 1 0 0 0 0\n" " 56 96 1 0 0 0 0\n" " 5 98 1 0 0 0 0\n" " 1 5 1 0 0 0 0\n" " 1 6 1 0 0 0 0\n" " 6 97 1 0 0 0 0\n" " 67 71 1 0 0 0 0\n" " 71 97 1 0 0 0 0\n" " 67 72 1 0 0 0 0\n" " 72 98 1 0 0 0 0\n" " 19100 1 0 0 0 0\n" " 17 19 1 0 0 0 0\n" " 17 20 1 0 0 0 0\n" " 20 99 1 0 0 0 0\n" " 2 8 1 0 0 0 0\n" " 8 99 1 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3100 1 0 0 0 0\n" "M END\n" "\n", "complexring7.mol\n" " Mrv0541 03181119072D \n" "\n" " 50 65 0 0 1 0 999 V2000\n" " 12.3772 5.5954 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.8922 4.9280 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 12.3770 4.2606 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 13.1618 4.5155 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 13.1618 5.3404 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.8923 6.2630 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.1077 6.0080 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.1077 5.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2628 4.0739 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4782 4.3289 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.9933 3.6615 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4781 2.9939 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2627 3.2489 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.7478 4.7413 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2629 5.4087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4782 5.1538 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2632 9.8821 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4785 9.6273 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4785 8.8022 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2631 8.5472 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.7481 9.2146 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 9.2632 10.7071 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.4787 10.9620 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.9937 10.2946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.8534 6.5657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.8534 7.3907 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.0688 7.6457 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.5838 6.9783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.0688 6.3107 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.6379 6.3106 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.1229 6.9781 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.6380 7.6456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.8925 9.0276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 12.3773 8.3601 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 13.1621 8.6150 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 13.1621 9.4400 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 12.3774 9.6949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.1079 8.7727 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.1078 7.9477 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.8924 7.6927 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.6229 6.9778 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 13.7729 6.9777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 10.3448 8.7375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.0091 10.7823 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.2763 8.0654 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.5371 9.3293 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 8.2762 5.8907 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.5367 4.6269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 10.3444 5.2183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 11.0087 3.1736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 5 4 1 0 0 0 0\n" " 1 5 1 0 0 0 0\n" " 1 6 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 8 7 1 0 0 0 0\n" " 9 10 1 0 0 0 0\n" " 11 12 1 0 0 0 0\n" " 13 12 1 0 0 0 0\n" " 14 15 1 0 0 0 0\n" " 16 15 1 0 0 0 0\n" " 18 17 1 0 0 0 0\n" " 19 20 1 0 0 0 0\n" " 21 20 1 0 0 0 0\n" " 22 23 1 0 0 0 0\n" " 24 23 1 0 0 0 0\n" " 25 26 1 0 0 0 0\n" " 27 28 1 0 0 0 0\n" " 29 28 1 0 0 0 0\n" " 30 31 1 0 0 0 0\n" " 32 31 1 0 0 0 0\n" " 33 34 1 0 0 0 0\n" " 35 36 1 0 0 0 0\n" " 37 36 1 0 0 0 0\n" " 38 39 1 0 0 0 0\n" " 40 39 1 0 0 0 0\n" " 34 35 1 0 0 0 0\n" " 34 40 1 0 0 0 0\n" " 40 41 1 0 0 0 0\n" " 6 41 1 0 0 0 0\n" " 35 42 1 0 0 0 0\n" " 5 42 1 0 0 0 0\n" " 21 43 1 0 0 0 0\n" " 17 21 1 0 0 0 0\n" " 33 37 1 0 0 0 0\n" " 37 44 1 0 0 0 0\n" " 33 38 1 0 0 0 0\n" " 38 43 1 0 0 0 0\n" " 17 22 1 0 0 0 0\n" " 22 44 1 0 0 0 0\n" " 27 46 1 0 0 0 0\n" " 26 27 1 0 0 0 0\n" " 18 24 1 0 0 0 0\n" " 24 46 1 0 0 0 0\n" " 18 19 1 0 0 0 0\n" " 19 45 1 0 0 0 0\n" " 26 32 1 0 0 0 0\n" " 32 45 1 0 0 0 0\n" " 29 48 1 0 0 0 0\n" " 25 29 1 0 0 0 0\n" " 25 30 1 0 0 0 0\n" " 30 47 1 0 0 0 0\n" " 10 11 1 0 0 0 0\n" " 11 48 1 0 0 0 0\n" " 10 16 1 0 0 0 0\n" " 16 47 1 0 0 0 0\n" " 8 49 1 0 0 0 0\n" " 2 8 1 0 0 0 0\n" " 9 13 1 0 0 0 0\n" " 13 50 1 0 0 0 0\n" " 9 14 1 0 0 0 0\n" " 14 49 1 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3 50 1 0 0 0 0\n" "M END\n" "\n", "cube.mol\n" " Marvin 06090922082D \n" "\n" " 8 12 0 0 1 0 999 V2000\n" " 2.4259 2.6815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5174 3.6054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.8338 2.6815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4469 4.1186 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7891 3.6054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4965 4.8519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.8338 4.0684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.7471 4.8079 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 8 0 0 0 0\n" " 1 3 8 0 0 0 0\n" " 1 4 8 0 0 0 0\n" " 2 5 8 0 0 0 0\n" " 2 6 8 0 0 0 0\n" " 3 5 8 0 0 0 0\n" " 3 7 8 0 0 0 0\n" " 4 6 8 0 0 0 0\n" " 4 7 8 0 0 0 0\n" " 5 8 8 0 0 0 0\n" " 6 8 8 0 0 0 0\n" " 7 8 8 0 0 0 0\n" "M END\n" "\n", "highvalence.mol\n" " Marvin 06090922252D \n" "\n" " 7 11 0 0 0 0 999 V2000\n" " -1.9726 0.1034 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.5000 0.8659 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.1691 1.6091 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0826 1.2023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.9780 0.2079 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.0770 1.0795 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 4 5 1 0 0 0 0\n" " 5 1 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 6 2 1 0 0 0 0\n" " 7 6 1 0 0 0 0\n" " 7 1 1 0 0 0 0\n" " 7 2 1 0 0 0 0\n" " 7 3 1 0 0 0 0\n" " 7 4 1 0 0 0 0\n" "M END\n" "\n", "krabe.mol\n" " Marvin 06090922252D \n" "\n" " 9 10 0 0 0 0 999 V2000\n" " 1.6290 -1.9119 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8127 -2.0914 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2248 -1.5105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1634 -0.5574 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.2444 -0.5581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1175 -1.3600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6220 -0.9357 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5820 -0.9250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7132 -0.1214 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 4 5 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 7 8 1 0 0 0 0\n" " 1 8 1 0 0 0 0\n" " 8 9 1 0 0 0 0\n" " 4 9 1 0 0 0 0\n" "M END\n" "\n", "krabe2.mol\n" " Mrv0541 03181118022D \n" "\n" " 8 9 0 0 0 0 999 V2000\n" " -0.2228 -1.6501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3975 -1.2732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7180 -0.5646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1574 -0.1247 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.2971 -0.9378 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2688 -0.9378 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7850 -1.3665 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6468 -0.5491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 1 0 0 0 0\n" " 2 3 1 0 0 0 0\n" " 3 4 1 0 0 0 0\n" " 4 5 1 0 0 0 0\n" " 1 5 1 0 0 0 0\n" " 5 6 1 0 0 0 0\n" " 6 7 1 0 0 0 0\n" " 7 8 1 0 0 0 0\n" " 3 8 1 0 0 0 0\n" "M END\n" "\n", "s13133.mol\n" " Marvin 06090923072D \n" "\n" " 14 17 0 0 0 0 999 V2000\n" " 1.9945 -0.4532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.9967 -0.4685 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5150 -1.1100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5150 0.1861 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4866 -1.1472 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4800 0.2277 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1948 -0.2145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6480 -0.0460 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7663 -0.0657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6480 -0.8736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7663 -0.8582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0635 0.3569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0700 -1.2808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0503 -0.4970 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 5 1 0 0 0 0\n" " 3 1 1 0 0 0 0\n" " 4 1 1 0 0 0 0\n" " 5 10 1 0 0 0 0\n" " 6 8 1 0 0 0 0\n" " 7 12 1 0 0 0 0\n" " 8 12 1 0 0 0 0\n" " 9 4 1 0 0 0 0\n" " 10 13 1 0 0 0 0\n" " 11 3 1 0 0 0 0\n" " 12 9 1 0 0 0 0\n" " 13 11 1 0 0 0 0\n" " 14 13 1 0 0 0 0\n" " 11 9 1 0 0 0 0\n" " 14 7 2 0 0 0 0\n" " 10 8 1 0 0 0 0\n" " 2 6 1 0 0 0 0\n" "M END\n" "\n", "sphere.mol\n" " Marvin 06090921502D \n" "\n" " 60 90 0 0 0 0 999 V2000\n" " 4.2810 2.6747 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2707 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7968 3.1409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1888 3.1640 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7840 1.3066 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2016 1.3271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3151 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4483 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1093 2.7131 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6917 3.9018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3100 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4279 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1145 1.7882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6815 0.5918 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4876 2.9616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6158 4.1683 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5585 2.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1204 4.1913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4825 1.4987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5978 0.2869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5431 1.5116 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0795 0.3125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.9727 2.2340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1290 3.7045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0444 4.4680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1999 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0513 3.7327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2980 4.4782 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1290 0.7532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0162 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0307 0.7839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2442 0.0128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0990 2.2442 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2554 3.7199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1990 4.1888 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.3314 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1828 3.7276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6858 4.2016 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2323 0.7558 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1887 0.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1546 0.7890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6627 0.3356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7353 2.9847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7225 1.4910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6405 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8241 2.9872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8164 1.5295 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8506 3.9300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6097 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8352 0.6251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1717 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1511 1.7652 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1307 3.1307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9914 2.7156 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9863 1.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4996 3.1768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0897 1.3040 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4919 1.3706 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0051 2.7336 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 1.8292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 8 0 0 0 0\n" " 1 3 8 0 0 0 0\n" " 1 4 8 0 0 0 0\n" " 2 5 8 0 0 0 0\n" " 2 6 8 0 0 0 0\n" " 3 7 8 0 0 0 0\n" " 3 8 8 0 0 0 0\n" " 4 9 8 0 0 0 0\n" " 4 10 8 0 0 0 0\n" " 5 11 8 0 0 0 0\n" " 5 12 8 0 0 0 0\n" " 6 13 8 0 0 0 0\n" " 6 14 8 0 0 0 0\n" " 7 11 8 0 0 0 0\n" " 7 15 8 0 0 0 0\n" " 8 10 8 0 0 0 0\n" " 8 16 8 0 0 0 0\n" " 9 13 8 0 0 0 0\n" " 9 17 8 0 0 0 0\n" " 10 18 8 0 0 0 0\n" " 11 19 8 0 0 0 0\n" " 12 14 8 0 0 0 0\n" " 12 20 8 0 0 0 0\n" " 13 21 8 0 0 0 0\n" " 14 22 8 0 0 0 0\n" " 15 23 8 0 0 0 0\n" " 15 24 8 0 0 0 0\n" " 16 24 8 0 0 0 0\n" " 16 25 8 0 0 0 0\n" " 17 26 8 0 0 0 0\n" " 17 27 8 0 0 0 0\n" " 18 27 8 0 0 0 0\n" " 18 28 8 0 0 0 0\n" " 19 23 8 0 0 0 0\n" " 19 29 8 0 0 0 0\n" " 20 29 8 0 0 0 0\n" " 20 30 8 0 0 0 0\n" " 21 26 8 0 0 0 0\n" " 21 31 8 0 0 0 0\n" " 22 31 8 0 0 0 0\n" " 22 32 8 0 0 0 0\n" " 23 33 8 0 0 0 0\n" " 24 34 8 0 0 0 0\n" " 25 28 8 0 0 0 0\n" " 25 35 8 0 0 0 0\n" " 26 36 8 0 0 0 0\n" " 27 37 8 0 0 0 0\n" " 28 38 8 0 0 0 0\n" " 29 39 8 0 0 0 0\n" " 30 32 8 0 0 0 0\n" " 30 40 8 0 0 0 0\n" " 31 41 8 0 0 0 0\n" " 32 42 8 0 0 0 0\n" " 33 43 8 0 0 0 0\n" " 33 44 8 0 0 0 0\n" " 34 35 8 0 0 0 0\n" " 34 43 8 0 0 0 0\n" " 35 45 8 0 0 0 0\n" " 36 46 8 0 0 0 0\n" " 36 47 8 0 0 0 0\n" " 37 38 8 0 0 0 0\n" " 37 46 8 0 0 0 0\n" " 38 48 8 0 0 0 0\n" " 39 40 8 0 0 0 0\n" " 39 44 8 0 0 0 0\n" " 40 49 8 0 0 0 0\n" " 41 42 8 0 0 0 0\n" " 41 47 8 0 0 0 0\n" " 42 50 8 0 0 0 0\n" " 43 51 8 0 0 0 0\n" " 44 52 8 0 0 0 0\n" " 45 48 8 0 0 0 0\n" " 45 53 8 0 0 0 0\n" " 46 54 8 0 0 0 0\n" " 47 55 8 0 0 0 0\n" " 48 56 8 0 0 0 0\n" " 49 50 8 0 0 0 0\n" " 49 57 8 0 0 0 0\n" " 50 58 8 0 0 0 0\n" " 51 52 8 0 0 0 0\n" " 51 53 8 0 0 0 0\n" " 52 57 8 0 0 0 0\n" " 53 59 8 0 0 0 0\n" " 54 55 8 0 0 0 0\n" " 54 56 8 0 0 0 0\n" " 55 58 8 0 0 0 0\n" " 56 59 8 0 0 0 0\n" " 57 60 8 0 0 0 0\n" " 58 60 8 0 0 0 0\n" " 59 60 8 0 0 0 0\n" "M END\n" "\n", "sphere2.mol\n" " -INDIGO-12271017453D\n" "\n" " 80210 0 0 0 0 0 0 0 0999 V2000\n" " -0.3637 4.0495 1.3474 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.7474 3.3091 2.0890 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9201 3.0169 0.8453 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.2629 3.5789 -0.6621 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6814 4.2189 -0.3519 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.5821 3.9816 -1.4933 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1769 3.5719 -0.9460 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4913 3.4047 0.7356 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2166 3.6445 1.8883 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4263 2.4963 3.1745 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0553 1.7636 3.9082 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.5397 2.1724 3.3618 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5045 0.7298 3.4053 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.6673 0.4397 2.1731 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8756 1.5890 0.8871 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.1812 0.7094 -0.5760 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5308 1.2645 -2.0670 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5671 2.7067 -2.1117 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.2921 2.4669 -3.2665 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.2759 3.1015 -2.9592 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6827 2.1481 -3.3188 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8564 2.4392 -2.0732 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.8425 1.1512 -1.5043 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1590 0.9830 0.1944 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4867 2.1049 1.3091 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8304 1.5442 2.8158 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8523 -0.1324 3.1937 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4687 -0.8743 3.9353 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0763 0.0691 4.2909 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.5070 -0.5708 3.9808 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.6825 -2.1475 3.3180 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.8568 -2.4392 2.0731 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8437 -1.1516 1.5046 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.1597 -0.9833 -0.1941 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4865 -2.1050 -1.3087 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.8294 -1.5438 -2.8150 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.8515 0.1328 -3.1927 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4682 0.8745 -3.9353 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0759 -0.0689 -4.2914 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5073 0.5714 -3.9818 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6660 -0.4396 -2.1732 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.8749 -1.5882 -0.8869 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1808 -0.7093 0.5766 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5310 -1.2647 2.0677 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5673 -2.7070 2.1116 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2922 -2.4666 3.2660 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.2759 -3.1008 2.9584 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.5820 -3.9816 1.4930 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1770 -3.5721 0.9462 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.4915 -3.4049 -0.7353 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.2167 -3.6446 -1.8883 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4257 -2.4959 -3.1742 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.0548 -1.7634 -3.9080 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5399 -2.1732 -3.3617 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7474 -3.3097 -2.0888 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9204 -3.0168 -0.8452 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2630 -3.5791 0.6618 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6814 -4.2193 0.3517 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.3634 -4.0501 -1.3477 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.1136 -1.6520 3.9523 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.5829 0.8845 3.8734 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.1721 3.1685 2.8645 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9541 2.0450 2.3181 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5135 2.3375 -0.6688 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.0809 3.6439 -1.9706 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9849 4.1555 0.2157 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4549 2.4802 -0.4143 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9187 0.9335 -2.9900 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5134 -2.3376 0.6690 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4554 -2.4806 0.4144 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.9192 -0.9334 2.9896 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8222 -0.4596 -1.8445 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.9851 -4.1559 -0.2157 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1723 -3.1689 -2.8645 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.9537 -2.0450 -2.3180 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0810 -3.6440 1.9702 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.8227 0.4596 1.8450 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1138 1.6525 -3.9533 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.5820 -0.8843 -3.8728 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5037 -0.7298 -3.4058 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 12 2 1 0 0 0 0\n" " 3 2 1 0 0 0 0\n" " 13 12 1 0 0 0 0\n" " 15 3 1 0 0 0 0\n" " 14 15 1 0 0 0 0\n" " 4 3 1 0 0 0 0\n" " 16 15 1 0 0 0 0\n" " 18 4 1 0 0 0 0\n" " 17 18 1 0 0 0 0\n" " 6 5 1 0 0 0 0\n" " 4 5 1 0 0 0 0\n" " 20 6 1 0 0 0 0\n" " 19 18 1 0 0 0 0\n" " 9 1 1 0 0 0 0\n" " 5 1 1 0 0 0 0\n" " 8 9 1 0 0 0 0\n" " 7 6 1 0 0 0 0\n" " 2 1 1 0 0 0 0\n" " 10 9 1 0 0 0 0\n" " 11 12 1 0 0 0 0\n" " 26 10 1 0 0 0 0\n" " 11 10 1 0 0 0 0\n" " 27 26 1 0 0 0 0\n" " 29 11 1 0 0 0 0\n" " 28 29 1 0 0 0 0\n" " 30 13 1 0 0 0 0\n" " 14 13 1 0 0 0 0\n" " 31 30 1 0 0 0 0\n" " 33 14 1 0 0 0 0\n" " 32 33 1 0 0 0 0\n" " 34 16 1 0 0 0 0\n" " 17 16 1 0 0 0 0\n" " 35 34 1 0 0 0 0\n" " 37 17 1 0 0 0 0\n" " 36 37 1 0 0 0 0\n" " 21 20 1 0 0 0 0\n" " 19 20 1 0 0 0 0\n" " 40 21 1 0 0 0 0\n" " 38 19 1 0 0 0 0\n" " 39 38 1 0 0 0 0\n" " 25 8 1 0 0 0 0\n" " 7 8 1 0 0 0 0\n" " 24 25 1 0 0 0 0\n" " 22 7 1 0 0 0 0\n" " 23 22 1 0 0 0 0\n" " 25 26 1 0 0 0 0\n" " 29 30 1 0 0 0 0\n" " 33 34 1 0 0 0 0\n" " 37 38 1 0 0 0 0\n" " 21 22 1 0 0 0 0\n" " 27 44 1 0 0 0 0\n" " 28 27 1 0 0 0 0\n" " 28 46 1 0 0 0 0\n" " 45 46 1 0 0 0 0\n" " 44 45 1 0 0 0 0\n" " 57 45 1 0 0 0 0\n" " 31 47 1 0 0 0 0\n" " 32 31 1 0 0 0 0\n" " 32 49 1 0 0 0 0\n" " 48 49 1 0 0 0 0\n" " 47 48 1 0 0 0 0\n" " 58 48 1 0 0 0 0\n" " 24 43 1 0 0 0 0\n" " 23 24 1 0 0 0 0\n" " 23 41 1 0 0 0 0\n" " 42 41 1 0 0 0 0\n" " 43 42 1 0 0 0 0\n" " 56 42 1 0 0 0 0\n" " 40 80 1 0 0 0 0\n" " 39 40 1 0 0 0 0\n" " 39 53 1 0 0 0 0\n" " 54 53 1 0 0 0 0\n" " 80 54 1 0 0 0 0\n" " 55 54 1 0 0 0 0\n" " 35 50 1 0 0 0 0\n" " 36 35 1 0 0 0 0\n" " 36 52 1 0 0 0 0\n" " 51 52 1 0 0 0 0\n" " 50 51 1 0 0 0 0\n" " 59 51 1 0 0 0 0\n" " 43 44 1 0 0 0 0\n" " 46 47 1 0 0 0 0\n" " 49 50 1 0 0 0 0\n" " 52 53 1 0 0 0 0\n" " 80 41 1 0 0 0 0\n" " 56 57 1 0 0 0 0\n" " 57 58 1 0 0 0 0\n" " 58 59 1 0 0 0 0\n" " 59 55 1 0 0 0 0\n" " 55 56 1 0 0 0 0\n" " 56 69 1 0 0 0 0\n" " 69 57 1 0 0 0 0\n" " 37 72 1 0 0 0 0\n" " 72 36 1 0 0 0 0\n" " 72 35 1 0 0 0 0\n" " 72 34 1 0 0 0 0\n" " 17 72 1 0 0 0 0\n" " 16 72 1 0 0 0 0\n" " 42 69 1 0 0 0 0\n" " 43 69 1 0 0 0 0\n" " 44 69 1 0 0 0 0\n" " 69 45 1 0 0 0 0\n" " 23 67 1 0 0 0 0\n" " 24 67 1 0 0 0 0\n" " 25 67 1 0 0 0 0\n" " 22 67 1 0 0 0 0\n" " 7 67 1 0 0 0 0\n" " 8 67 1 0 0 0 0\n" " 7 66 1 0 0 0 0\n" " 8 66 1 0 0 0 0\n" " 6 66 1 0 0 0 0\n" " 5 66 1 0 0 0 0\n" " 1 66 1 0 0 0 0\n" " 9 66 1 0 0 0 0\n" " 1 62 1 0 0 0 0\n" " 9 62 1 0 0 0 0\n" " 10 62 1 0 0 0 0\n" " 11 62 1 0 0 0 0\n" " 2 62 1 0 0 0 0\n" " 12 62 1 0 0 0 0\n" " 14 71 1 0 0 0 0\n" " 33 71 1 0 0 0 0\n" " 13 71 1 0 0 0 0\n" " 30 71 1 0 0 0 0\n" " 31 71 1 0 0 0 0\n" " 32 71 1 0 0 0 0\n" " 20 65 1 0 0 0 0\n" " 6 65 1 0 0 0 0\n" " 5 65 1 0 0 0 0\n" " 19 65 1 0 0 0 0\n" " 18 65 1 0 0 0 0\n" " 4 65 1 0 0 0 0\n" " 18 64 1 0 0 0 0\n" " 17 64 1 0 0 0 0\n" " 4 64 1 0 0 0 0\n" " 3 64 1 0 0 0 0\n" " 15 64 1 0 0 0 0\n" " 16 64 1 0 0 0 0\n" " 3 63 1 0 0 0 0\n" " 15 63 1 0 0 0 0\n" " 14 63 1 0 0 0 0\n" " 13 63 1 0 0 0 0\n" " 2 63 1 0 0 0 0\n" " 12 63 1 0 0 0 0\n" " 10 61 1 0 0 0 0\n" " 26 61 1 0 0 0 0\n" " 27 61 1 0 0 0 0\n" " 28 61 1 0 0 0 0\n" " 11 61 1 0 0 0 0\n" " 29 61 1 0 0 0 0\n" " 68 21 1 0 0 0 0\n" " 68 40 1 0 0 0 0\n" " 68 80 1 0 0 0 0\n" " 41 68 1 0 0 0 0\n" " 23 68 1 0 0 0 0\n" " 22 68 1 0 0 0 0\n" " 45 76 1 0 0 0 0\n" " 57 76 1 0 0 0 0\n" " 76 47 1 0 0 0 0\n" " 76 48 1 0 0 0 0\n" " 76 58 1 0 0 0 0\n" " 34 70 1 0 0 0 0\n" " 35 70 1 0 0 0 0\n" " 50 70 1 0 0 0 0\n" " 33 70 1 0 0 0 0\n" " 32 70 1 0 0 0 0\n" " 49 70 1 0 0 0 0\n" " 51 73 1 0 0 0 0\n" " 50 73 1 0 0 0 0\n" " 49 73 1 0 0 0 0\n" " 59 73 1 0 0 0 0\n" " 48 73 1 0 0 0 0\n" " 58 73 1 0 0 0 0\n" " 56 75 1 0 0 0 0\n" " 42 75 1 0 0 0 0\n" " 75 54 1 0 0 0 0\n" " 75 55 1 0 0 0 0\n" " 75 41 1 0 0 0 0\n" " 53 74 1 0 0 0 0\n" " 51 74 1 0 0 0 0\n" " 52 74 1 0 0 0 0\n" " 54 74 1 0 0 0 0\n" " 55 74 1 0 0 0 0\n" " 74 59 1 0 0 0 0\n" " 43 77 1 0 0 0 0\n" " 24 77 1 0 0 0 0\n" " 25 77 1 0 0 0 0\n" " 44 77 1 0 0 0 0\n" " 26 77 1 0 0 0 0\n" " 27 77 1 0 0 0 0\n" " 46 60 1 0 0 0 0\n" " 28 60 1 0 0 0 0\n" " 29 60 1 0 0 0 0\n" " 60 30 1 0 0 0 0\n" " 60 47 1 0 0 0 0\n" " 60 31 1 0 0 0 0\n" " 40 78 1 0 0 0 0\n" " 21 78 1 0 0 0 0\n" " 20 78 1 0 0 0 0\n" " 19 78 1 0 0 0 0\n" " 39 78 1 0 0 0 0\n" " 38 78 1 0 0 0 0\n" " 39 79 1 0 0 0 0\n" " 38 79 1 0 0 0 0\n" " 37 79 1 0 0 0 0\n" " 36 79 1 0 0 0 0\n" " 52 79 1 0 0 0 0\n" " 53 79 1 0 0 0 0\n" " 46 76 1 0 0 0 0\n" " 75 80 1 0 0 0 0\n" "M END\n" "\n", "sphere_mod1.mol\n" " Marvin 06090921502D \n" "\n" " 68100 0 0 0 0 999 V2000\n" " 4.2810 2.6747 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2707 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7968 3.1409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1888 3.1640 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.0650 3.1179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.7840 1.3066 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.2016 1.3271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.0521 1.3092 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3151 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4483 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1093 2.7131 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6917 3.9018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.8489 2.6568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.3100 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4279 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1145 1.7882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6815 0.5918 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.8386 1.7524 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4876 2.9616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.6158 4.1683 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5585 2.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1204 4.1913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.6329 3.1025 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4825 1.4987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5978 0.2869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.5431 1.5116 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0795 0.3125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.6149 1.2963 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.9727 2.2340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1290 3.7045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0444 4.4680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1999 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0513 3.7327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2980 4.4782 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.4117 2.6439 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1290 0.7532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.0162 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.0307 0.7839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.2442 0.0128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 7.4014 1.7421 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.0990 2.2442 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2554 3.7199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1990 4.1888 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.3314 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1828 3.7276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6858 4.2016 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.2323 0.7558 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.1887 0.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1546 0.7890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.6627 0.3356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7353 2.9847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.7225 1.4910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6405 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8241 2.9872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.8164 1.5295 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8506 3.9300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6097 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8352 0.6251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1717 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1511 1.7652 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.1307 3.1307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9914 2.7156 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9863 1.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4996 3.1768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0897 1.3040 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4919 1.3706 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0051 2.7336 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0000 1.8292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1 2 8 0 0 0 0\n" " 1 3 8 0 0 0 0\n" " 1 4 8 0 0 0 0\n" " 1 5 8 0 0 0 0\n" " 2 6 8 0 0 0 0\n" " 2 7 8 0 0 0 0\n" " 2 8 8 0 0 0 0\n" " 3 9 8 0 0 0 0\n" " 3 10 8 0 0 0 0\n" " 4 11 8 0 0 0 0\n" " 4 12 8 0 0 0 0\n" " 5 13 8 0 0 0 0\n" " 6 14 8 0 0 0 0\n" " 6 15 8 0 0 0 0\n" " 7 16 8 0 0 0 0\n" " 7 17 8 0 0 0 0\n" " 8 18 8 0 0 0 0\n" " 9 14 8 0 0 0 0\n" " 9 19 8 0 0 0 0\n" " 10 12 8 0 0 0 0\n" " 10 20 8 0 0 0 0\n" " 11 16 8 0 0 0 0\n" " 11 21 8 0 0 0 0\n" " 12 22 8 0 0 0 0\n" " 13 18 8 0 0 0 0\n" " 13 23 8 0 0 0 0\n" " 14 24 8 0 0 0 0\n" " 15 17 8 0 0 0 0\n" " 15 25 8 0 0 0 0\n" " 16 26 8 0 0 0 0\n" " 17 27 8 0 0 0 0\n" " 18 28 8 0 0 0 0\n" " 19 29 8 0 0 0 0\n" " 19 30 8 0 0 0 0\n" " 20 30 8 0 0 0 0\n" " 20 31 8 0 0 0 0\n" " 21 32 8 0 0 0 0\n" " 21 33 8 0 0 0 0\n" " 22 33 8 0 0 0 0\n" " 22 34 8 0 0 0 0\n" " 23 35 8 0 0 0 0\n" " 24 29 8 0 0 0 0\n" " 24 36 8 0 0 0 0\n" " 25 36 8 0 0 0 0\n" " 25 37 8 0 0 0 0\n" " 26 32 8 0 0 0 0\n" " 26 38 8 0 0 0 0\n" " 27 38 8 0 0 0 0\n" " 27 39 8 0 0 0 0\n" " 28 40 8 0 0 0 0\n" " 29 41 8 0 0 0 0\n" " 30 42 8 0 0 0 0\n" " 31 34 8 0 0 0 0\n" " 31 43 8 0 0 0 0\n" " 32 44 8 0 0 0 0\n" " 33 45 8 0 0 0 0\n" " 34 46 8 0 0 0 0\n" " 35 40 8 0 0 0 0\n" " 36 47 8 0 0 0 0\n" " 37 39 8 0 0 0 0\n" " 37 48 8 0 0 0 0\n" " 38 49 8 0 0 0 0\n" " 39 50 8 0 0 0 0\n" " 41 51 8 0 0 0 0\n" " 41 52 8 0 0 0 0\n" " 42 43 8 0 0 0 0\n" " 42 51 8 0 0 0 0\n" " 43 53 8 0 0 0 0\n" " 44 54 8 0 0 0 0\n" " 44 55 8 0 0 0 0\n" " 45 46 8 0 0 0 0\n" " 45 54 8 0 0 0 0\n" " 46 56 8 0 0 0 0\n" " 47 48 8 0 0 0 0\n" " 47 52 8 0 0 0 0\n" " 48 57 8 0 0 0 0\n" " 49 50 8 0 0 0 0\n" " 49 55 8 0 0 0 0\n" " 50 58 8 0 0 0 0\n" " 51 59 8 0 0 0 0\n" " 52 60 8 0 0 0 0\n" " 53 56 8 0 0 0 0\n" " 53 61 8 0 0 0 0\n" " 54 62 8 0 0 0 0\n" " 55 63 8 0 0 0 0\n" " 56 64 8 0 0 0 0\n" " 57 58 8 0 0 0 0\n" " 57 65 8 0 0 0 0\n" " 58 66 8 0 0 0 0\n" " 59 60 8 0 0 0 0\n" " 59 61 8 0 0 0 0\n" " 60 65 8 0 0 0 0\n" " 61 67 8 0 0 0 0\n" " 62 63 8 0 0 0 0\n" " 62 64 8 0 0 0 0\n" " 63 66 8 0 0 0 0\n" " 64 67 8 0 0 0 0\n" " 65 68 8 0 0 0 0\n" " 66 68 8 0 0 0 0\n" " 67 68 8 0 0 0 0\n" "M END\n" "\n", "superstar.mol\n" " Mrv0541 03181119032D \n" "\n" " 70105 0 0 0 0 999 V2000\n" " -0.6526 0.2101 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3992 -0.5750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0157 0.6937 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4258 -0.5766 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6823 0.2074 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.8854 -1.2416 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.7104 -1.2399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6320 -2.0266 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.9668 -2.0241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.3004 -2.5103 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.9094 -1.2451 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6529 -2.0291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7344 -1.2467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.3193 -2.5154 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.9878 -2.0318 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4673 0.4608 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1339 -0.0255 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.7240 1.2448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8023 0.4581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.5490 1.2432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0173 1.5187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6857 2.0023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6492 2.0049 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4324 2.7874 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3926 2.7891 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.4368 0.4665 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.6902 1.2516 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1052 -0.0172 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.5151 1.2531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7716 0.4691 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 1.4039 -3.3359 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.1568 -3.6730 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.7406 -2.3690 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.8251 -3.1895 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.6088 0.2849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.1621 0.8968 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.1022 1.8550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9087 1.6819 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.8462 3.5009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.4352 4.2162 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.8036 3.5043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.3898 4.2178 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.0659 1.8673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8732 1.6974 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.5788 0.2993 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.1297 0.9134 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.7211 -2.3581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.8089 -3.1784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -1.3883 -3.3304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.1425 -3.6646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.4772 -3.6618 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -2.3990 -4.4484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.3904 -4.6891 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2.4100 -4.4579 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4913 -3.6758 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.4137 -4.6930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.9869 0.8951 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 4.5771 2.1651 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 5.5205 1.7770 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.6917 5.0002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -0.6430 5.0028 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0177 5.7802 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.5394 2.1835 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -4.9544 0.9151 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -5.4895 1.7837 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -6.2753 2.0354 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " -3.8726 -5.3584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 3.9014 -5.3584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 6.2978 2.0264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 0.0206 6.6138 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" " 2 1 8 0 0 0 0\n" " 3 1 8 0 0 0 0\n" " 4 2 8 0 0 0 0\n" " 5 3 8 0 0 0 0\n" " 5 4 8 0 0 0 0\n" " 7 6 8 0 0 0 0\n" " 8 6 8 0 0 0 0\n" " 9 7 8 0 0 0 0\n" " 10 8 8 0 0 0 0\n" " 2 6 8 0 0 0 0\n" " 12 11 8 0 0 0 0\n" " 13 11 8 0 0 0 0\n" " 14 12 8 0 0 0 0\n" " 15 13 8 0 0 0 0\n" " 4 11 8 0 0 0 0\n" " 17 16 8 0 0 0 0\n" " 18 16 8 0 0 0 0\n" " 19 17 8 0 0 0 0\n" " 20 18 8 0 0 0 0\n" " 5 16 8 0 0 0 0\n" " 22 21 8 0 0 0 0\n" " 23 21 8 0 0 0 0\n" " 24 22 8 0 0 0 0\n" " 25 23 8 0 0 0 0\n" " 3 21 8 0 0 0 0\n" " 27 26 8 0 0 0 0\n" " 28 26 8 0 0 0 0\n" " 29 27 8 0 0 0 0\n" " 30 28 8 0 0 0 0\n" " 1 26 8 0 0 0 0\n" " 7 28 8 0 0 0 0\n" " 8 12 8 0 0 0 0\n" " 13 17 8 0 0 0 0\n" " 18 22 8 0 0 0 0\n" " 23 27 8 0 0 0 0\n" " 31 14 8 0 0 0 0\n" " 15 14 8 0 0 0 0\n" " 32 31 8 0 0 0 0\n" " 33 15 8 0 0 0 0\n" " 34 33 8 0 0 0 0\n" " 35 19 8 0 0 0 0\n" " 20 19 8 0 0 0 0\n" " 36 35 8 0 0 0 0\n" " 37 20 8 0 0 0 0\n" " 38 37 8 0 0 0 0\n" " 39 24 8 0 0 0 0\n" " 25 24 8 0 0 0 0\n" " 40 39 8 0 0 0 0\n" " 41 25 8 0 0 0 0\n" " 42 41 8 0 0 0 0\n" " 43 29 8 0 0 0 0\n" " 30 29 8 0 0 0 0\n" " 44 43 8 0 0 0 0\n" " 45 30 8 0 0 0 0\n" " 46 45 8 0 0 0 0\n" " 47 9 8 0 0 0 0\n" " 10 9 8 0 0 0 0\n" " 48 47 8 0 0 0 0\n" " 49 10 8 0 0 0 0\n" " 50 49 8 0 0 0 0\n" " 45 47 8 0 0 0 0\n" " 49 31 8 0 0 0 0\n" " 33 35 8 0 0 0 0\n" " 37 39 8 0 0 0 0\n" " 41 43 8 0 0 0 0\n" " 48 51 8 0 0 0 0\n" " 50 48 8 0 0 0 0\n" " 50 52 8 0 0 0 0\n" " 53 52 8 0 0 0 0\n" " 51 53 8 0 0 0 0\n" " 32 54 8 0 0 0 0\n" " 34 32 8 0 0 0 0\n" " 34 55 8 0 0 0 0\n" " 56 55 8 0 0 0 0\n" " 54 56 8 0 0 0 0\n" " 36 57 8 0 0 0 0\n" " 38 36 8 0 0 0 0\n" " 38 58 8 0 0 0 0\n" " 59 58 8 0 0 0 0\n" " 57 59 8 0 0 0 0\n" " 40 60 8 0 0 0 0\n" " 42 40 8 0 0 0 0\n" " 42 61 8 0 0 0 0\n" " 62 61 8 0 0 0 0\n" " 60 62 8 0 0 0 0\n" " 44 63 8 0 0 0 0\n" " 46 44 8 0 0 0 0\n" " 46 64 8 0 0 0 0\n" " 65 64 8 0 0 0 0\n" " 63 65 8 0 0 0 0\n" " 64 51 8 0 0 0 0\n" " 52 54 8 0 0 0 0\n" " 55 57 8 0 0 0 0\n" " 58 60 8 0 0 0 0\n" " 61 63 8 0 0 0 0\n" " 66 65 8 0 0 0 0\n" " 67 53 8 0 0 0 0\n" " 68 56 8 0 0 0 0\n" " 69 59 8 0 0 0 0\n" " 70 62 8 0 0 0 0\n" " 70 66 8 0 0 0 0\n" " 66 67 8 0 0 0 0\n" " 67 68 8 0 0 0 0\n" " 68 69 8 0 0 0 0\n" " 69 70 8 0 0 0 0\n" "M END\n" "\n", }; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/�������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0022273�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/1293.mol�����������������������������������������0000664�0000000�0000000�00000001355�12710376503�0023406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 05180922082D 7 8 0 0 0 0 999 V2000 -0.3123 -0.6177 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3470 0.9485 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1198 -0.5992 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4350 -0.8884 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0046 0.2337 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1198 -0.6431 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7866 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 5 2 1 0 0 0 0 4 6 2 0 0 0 0 5 7 1 0 0 0 0 5 3 1 0 0 0 0 6 7 1 0 0 0 0 M END �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/aw00196.mol��������������������������������������0000664�0000000�0000000�00000001355�12710376503�0024017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090921352D 7 8 0 0 0 0 999 V2000 -2.0388 -0.2887 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6962 0.0642 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5610 1.6539 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.6608 0.4673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.3686 0.8957 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4681 -0.2199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1452 0.4856 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 2 1 0 0 0 0 4 1 1 0 0 0 0 5 4 1 0 0 0 0 6 2 1 0 0 0 0 7 5 1 0 0 0 0 5 3 1 0 0 0 0 7 6 1 0 0 0 0 M END �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/big_semicube.mol���������������������������������0000664�0000000�0000000�00000002026�12710376503�0025421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090922152D 10 12 0 0 0 0 999 V2000 12.5528 8.6716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.9697 7.3910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.5527 11.5607 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.5528 2.8847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.5133 6.0185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5351 8.6716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.5527 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.2406 7.2947 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.5133 3.2738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5351 2.8847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 2 5 1 0 0 0 0 3 6 1 0 0 0 0 4 7 1 0 0 0 0 5 8 1 0 0 0 0 5 9 1 0 0 0 0 6 8 1 0 0 0 0 6 10 1 0 0 0 0 7 9 1 0 0 0 0 7 10 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/btb12419.mol�������������������������������������0000664�0000000�0000000�00000002420�12710376503�0024152�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090923042D 12 17 0 0 1 0 999 V2000 4.5956 -45.8571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4328 -45.4936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.0438 -44.9212 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4328 -44.7021 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.0438 -44.0962 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6697 -44.9212 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0638 -45.4936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0638 -44.3695 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6697 -43.7636 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8398 -44.3695 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4380 -43.7636 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.2162 -42.7762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 1 1 0 0 0 0 4 2 1 0 0 0 0 5 3 1 0 0 0 0 6 3 1 0 0 0 0 7 2 1 0 0 0 0 8 7 1 0 0 0 0 9 6 1 0 0 0 0 10 4 1 0 0 0 0 11 5 1 0 0 0 0 12 11 1 0 0 0 0 5 4 1 0 0 0 0 6 7 1 0 0 0 0 11 9 1 0 0 0 0 9 8 1 0 0 0 0 10 8 1 0 0 0 0 12 10 1 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/cd00846.mol��������������������������������������0000664�0000000�0000000�00000002027�12710376503�0023775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 05180922142D 10 12 0 0 0 0 999 V2000 -2.6370 0.3582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8659 0.3582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2481 1.4304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7204 -0.2216 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6370 1.0722 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1742 -0.7141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2481 0.0045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8659 1.0722 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5106 -0.5171 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7204 0.6783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 7 1 0 0 0 0 3 5 1 0 0 0 0 4 6 1 0 0 0 0 5 1 1 0 0 0 0 6 1 1 0 0 0 0 7 1 1 0 0 0 0 8 2 1 0 0 0 0 9 2 1 0 0 0 0 10 4 1 0 0 0 0 9 4 1 0 0 0 0 8 3 1 0 0 0 0 10 3 1 0 0 0 0 M END ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring1.mol���������������������������������0000664�0000000�0000000�00000011210�12710376503�0025407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090923582D 49 56 0 0 1 0 999 V2000 8.3868 5.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.6526 5.9130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4069 4.6962 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.9488 5.4806 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.6325 6.7476 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.6929 4.2637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.9689 4.6560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.0493 7.3409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.7230 3.4392 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.2806 8.1253 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.2549 7.1297 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.1297 2.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.6873 8.7186 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.6716 7.7130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.3208 2.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.3353 3.0972 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8928 8.5275 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.7275 1.4883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.7420 2.5240 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3297 9.1007 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.9331 1.7196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5152 8.8996 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3398 1.1464 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9319 9.4929 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2939 8.1052 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5353 1.3676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1375 9.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4895 7.8940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9520 0.7843 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.3241 2.1621 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9163 8.4873 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1576 1.0056 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5297 2.3833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1117 8.2761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9464 1.8000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8503 7.4214 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2828 1.9710 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0458 7.2404 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4135 6.8080 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9911 2.7453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7944 6.4560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1520 6.0135 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1665 2.8761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5039 3.3889 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.3475 5.8426 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8648 3.6503 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2023 4.1632 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0860 5.0582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.3878 4.2838 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 2 4 1 0 0 0 0 2 5 1 0 0 0 0 3 6 1 0 0 0 0 4 7 1 0 0 0 0 8 5 1 0 0 0 0 6 7 1 0 0 0 0 6 9 1 0 0 0 0 8 10 1 0 0 0 0 8 11 1 0 0 0 0 12 9 1 0 0 0 0 10 13 1 0 0 0 0 11 14 1 0 0 0 0 12 15 1 0 0 0 0 12 16 1 0 0 0 0 13 17 1 0 0 0 0 14 17 1 0 0 0 0 15 18 1 0 0 0 0 16 19 1 0 0 0 0 17 20 1 0 0 0 0 18 21 1 0 0 0 0 19 21 1 0 0 0 0 22 20 1 0 0 0 0 21 23 1 0 0 0 0 22 24 1 0 0 0 0 22 25 1 0 0 0 0 26 23 1 0 0 0 0 24 27 1 0 0 0 0 25 28 1 0 0 0 0 26 29 1 0 0 0 0 26 30 1 0 0 0 0 27 31 1 0 0 0 0 28 31 1 0 0 0 0 29 32 1 0 0 0 0 30 33 1 0 0 0 0 31 34 1 0 0 0 0 32 35 1 0 0 0 0 33 35 1 0 0 0 0 36 34 1 0 0 0 0 35 37 1 0 0 0 0 36 38 1 0 0 0 0 36 39 1 0 0 0 0 40 37 1 0 0 0 0 38 41 1 0 0 0 0 39 42 1 0 0 0 0 40 43 1 0 0 0 0 40 44 1 0 0 0 0 41 45 1 0 0 0 0 42 45 1 0 0 0 0 43 46 1 0 0 0 0 44 47 1 0 0 0 0 45 48 1 0 0 0 0 46 49 1 0 0 0 0 47 49 1 0 0 0 0 49 48 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring2.mol���������������������������������0000664�0000000�0000000�00000012442�12710376503�0025420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090923582D 56 64 0 0 0 0 999 V2000 -13.2758 -47.4415 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.4922 -47.1624 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.4099 -48.2143 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.0592 -47.0605 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -11.8751 -47.6885 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.8464 -48.7671 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.0914 -46.0248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.0575 -48.4988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.9125 -45.8584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.5441 -45.3755 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -11.4458 -49.1696 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -15.1593 -45.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.8284 -44.6509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.6408 -48.9658 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.6602 -44.4900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.3133 -48.1983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.1310 -49.6310 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -15.1593 -43.6152 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.5138 -48.1339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.3421 -49.5345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.3920 -43.0893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.0308 -48.8153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.6602 -42.3219 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.5441 -43.2503 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.2150 -48.7349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -14.1289 -41.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.0610 -42.5849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.6302 -47.8978 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -13.3240 -41.8335 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.0111 -47.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.8306 -47.9513 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.9430 -41.0876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.5818 -46.4757 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.3797 -47.2591 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -12.0252 -41.0876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.7286 -46.5453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -11.7087 -40.3363 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -11.4940 -41.7369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.1489 -45.8746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.8929 -40.2343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.7051 -41.5974 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.3474 -45.0588 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -10.4207 -40.8139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.1471 -44.8494 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.7626 -44.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -9.5138 -40.5188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.3457 -44.0499 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.9450 -43.7064 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.8483 -41.2700 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.7286 -43.4865 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.1292 -40.9158 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.9824 -42.0697 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.7286 -42.6170 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.4799 -41.3989 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -8.2794 -42.5527 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -7.5658 -42.2145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 2 5 1 0 0 0 0 3 6 1 0 0 0 0 4 7 1 0 0 0 0 8 5 1 0 0 0 0 7 9 1 0 0 0 0 7 10 1 0 0 0 0 8 11 1 0 0 0 0 9 12 1 0 0 0 0 10 13 1 0 0 0 0 14 11 1 0 0 0 0 12 15 1 0 0 0 0 14 16 1 0 0 0 0 14 17 1 0 0 0 0 15 18 1 0 0 0 0 16 19 1 0 0 0 0 17 20 1 0 0 0 0 18 21 1 0 0 0 0 22 19 1 0 0 0 0 21 23 1 0 0 0 0 21 24 1 0 0 0 0 22 25 1 0 0 0 0 23 26 1 0 0 0 0 24 27 1 0 0 0 0 28 25 1 0 0 0 0 26 29 1 0 0 0 0 28 30 1 0 0 0 0 28 31 1 0 0 0 0 29 32 1 0 0 0 0 30 33 1 0 0 0 0 31 34 1 0 0 0 0 32 35 1 0 0 0 0 36 33 1 0 0 0 0 35 37 1 0 0 0 0 35 38 1 0 0 0 0 36 39 1 0 0 0 0 37 40 1 0 0 0 0 38 41 1 0 0 0 0 42 39 1 0 0 0 0 40 43 1 0 0 0 0 42 44 1 0 0 0 0 42 45 1 0 0 0 0 43 46 1 0 0 0 0 44 47 1 0 0 0 0 45 48 1 0 0 0 0 46 49 1 0 0 0 0 50 47 1 0 0 0 0 49 51 1 0 0 0 0 49 52 1 0 0 0 0 50 53 1 0 0 0 0 51 54 1 0 0 0 0 52 55 1 0 0 0 0 56 53 1 0 0 0 0 6 8 1 0 0 0 0 13 15 1 0 0 0 0 20 22 1 0 0 0 0 27 29 1 0 0 0 0 34 36 1 0 0 0 0 41 43 1 0 0 0 0 48 50 1 0 0 0 0 54 56 1 0 0 0 0 55 56 1 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring3.mol���������������������������������0000664�0000000�0000000�00000007756�12710376503�0025435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06240919342D 42 48 0 0 1 0 999 V2000 2.1300 0.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7843 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1300 2.0254 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4025 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7843 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0624 2.6377 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4025 -0.3925 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0829 -0.8269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0624 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3820 2.2191 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0829 -2.0515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3350 3.8467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3297 2.6221 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0519 -2.6063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3297 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0519 -3.4070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3350 -2.1981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4812 4.0194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3350 -3.8309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3297 -2.5906 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5487 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3297 -3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2291 3.8728 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5487 2.6221 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3974 -3.9305 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9566 3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2606 2.1719 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5487 -3.3181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9566 2.5958 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1977 -3.8153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5487 -2.5331 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.0714 2.0254 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9566 -3.4228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2762 -2.1509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.0714 0.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9566 -2.5906 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.7674 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.3386 0.4083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.0714 -2.0096 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.7674 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.3386 -0.4187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.0714 -0.8269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 2 5 1 0 0 0 0 6 3 1 0 0 0 0 4 7 1 0 0 0 0 5 8 1 0 0 0 0 6 9 1 0 0 0 0 6 10 1 0 0 0 0 8 11 1 0 0 0 0 9 12 1 0 0 0 0 10 13 1 0 0 0 0 14 11 1 0 0 0 0 12 15 1 0 0 0 0 14 16 1 0 0 0 0 14 17 1 0 0 0 0 15 18 1 0 0 0 0 16 19 1 0 0 0 0 17 20 1 0 0 0 0 21 18 1 0 0 0 0 19 22 1 0 0 0 0 21 23 1 0 0 0 0 21 24 1 0 0 0 0 22 25 1 0 0 0 0 23 26 1 0 0 0 0 24 27 1 0 0 0 0 28 25 1 0 0 0 0 26 29 1 0 0 0 0 28 30 1 0 0 0 0 28 31 1 0 0 0 0 29 32 1 0 0 0 0 30 33 1 0 0 0 0 31 34 1 0 0 0 0 35 32 1 0 0 0 0 33 36 1 0 0 0 0 35 37 1 0 0 0 0 35 38 1 0 0 0 0 36 39 1 0 0 0 0 37 40 1 0 0 0 0 38 41 1 0 0 0 0 42 39 1 0 0 0 0 7 8 1 0 0 0 0 13 15 1 0 0 0 0 20 22 1 0 0 0 0 27 29 1 0 0 0 0 34 36 1 0 0 0 0 40 42 1 0 0 0 0 41 42 1 0 0 0 0 M END ������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring4.mol���������������������������������0000664�0000000�0000000�00000017442�12710376503�0025427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181118532D 80104 0 0 1 0 999 V2000 -2.4750 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9599 0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7446 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5292 0.6675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.0141 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9599 -0.6675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7445 -0.4126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5291 -0.6676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.0140 -0.0001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5291 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7445 0.4124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9599 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4749 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6210 -2.5650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3561 -2.9396 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7305 -3.6746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5454 -3.5456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6745 -2.7308 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9394 -2.3562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5648 -1.6211 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7500 -1.7502 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.6746 2.7307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9395 2.3561 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5649 1.6210 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7501 1.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.6210 2.5649 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.3561 2.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7306 3.6745 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5455 3.5455 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7307 3.6745 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3561 2.9394 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6210 2.5648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7501 1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5649 1.6209 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9395 2.3561 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6745 2.7305 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5455 3.5454 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5456 -3.5455 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7308 -3.6746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.3562 -2.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.6211 -2.5649 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7502 -1.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6674 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6674 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 2.4749 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6674 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4125 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6675 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 5.0140 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6675 -2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4126 -3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6676 -4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0001 -5.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6673 -4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4124 -3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6673 -2.9600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 -2.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5868 1.0714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.4346 1.8368 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.6746 -2.7306 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9396 -2.3562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5650 -1.6210 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9600 -0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7446 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5292 -0.6674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5869 -1.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4347 -1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0715 2.5868 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8369 4.4345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0715 2.5868 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8369 4.4346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5869 1.0715 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4346 1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5868 -1.0716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.4346 -1.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0714 -2.5869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8368 -4.4347 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0716 -2.5869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8370 -4.4346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 64 1 1 0 0 0 0 1 2 1 0 0 0 0 65 3 1 0 0 0 0 5 4 1 0 0 0 0 66 5 1 0 0 0 0 8 9 1 0 0 0 0 9 10 1 0 0 0 0 11 10 1 0 0 0 0 7 11 1 0 0 0 0 11 12 1 0 0 0 0 13 12 1 0 0 0 0 6 13 1 0 0 0 0 16 17 1 0 0 0 0 17 18 1 0 0 0 0 15 19 1 0 0 0 0 21 20 1 0 0 0 0 14 21 1 0 0 0 0 24 25 1 0 0 0 0 25 26 1 0 0 0 0 27 23 1 0 0 0 0 29 28 1 0 0 0 0 22 29 1 0 0 0 0 32 33 1 0 0 0 0 33 34 1 0 0 0 0 35 34 1 0 0 0 0 35 31 1 0 0 0 0 35 36 1 0 0 0 0 37 36 1 0 0 0 0 30 37 1 0 0 0 0 61 38 1 0 0 0 0 38 39 1 0 0 0 0 62 40 1 0 0 0 0 42 41 1 0 0 0 0 63 42 1 0 0 0 0 45 46 1 0 0 0 0 46 47 1 0 0 0 0 48 44 1 0 0 0 0 50 49 1 0 0 0 0 43 50 1 0 0 0 0 53 54 1 0 0 0 0 54 55 1 0 0 0 0 52 56 1 0 0 0 0 58 57 1 0 0 0 0 51 58 1 0 0 0 0 36 60 1 0 0 0 0 10 60 1 0 0 0 0 34 59 1 0 0 0 0 12 59 1 0 0 0 0 66 68 1 0 0 0 0 61 68 1 0 0 0 0 64 67 1 0 0 0 0 63 67 1 0 0 0 0 62 61 1 0 0 0 0 62 63 1 0 0 0 0 65 64 1 0 0 0 0 65 66 1 0 0 0 0 32 69 1 0 0 0 0 31 32 1 0 0 0 0 31 30 1 0 0 0 0 30 70 1 0 0 0 0 47 69 1 0 0 0 0 48 47 1 0 0 0 0 48 49 1 0 0 0 0 49 70 1 0 0 0 0 26 71 1 0 0 0 0 27 26 1 0 0 0 0 43 72 1 0 0 0 0 44 43 1 0 0 0 0 44 45 1 0 0 0 0 45 71 1 0 0 0 0 27 28 1 0 0 0 0 28 72 1 0 0 0 0 2 73 1 0 0 0 0 3 2 1 0 0 0 0 22 74 1 0 0 0 0 23 22 1 0 0 0 0 23 24 1 0 0 0 0 24 73 1 0 0 0 0 3 4 1 0 0 0 0 4 74 1 0 0 0 0 20 75 1 0 0 0 0 19 20 1 0 0 0 0 8 76 1 0 0 0 0 7 8 1 0 0 0 0 7 6 1 0 0 0 0 6 75 1 0 0 0 0 19 18 1 0 0 0 0 18 76 1 0 0 0 0 14 77 1 0 0 0 0 15 14 1 0 0 0 0 15 16 1 0 0 0 0 16 78 1 0 0 0 0 57 77 1 0 0 0 0 56 57 1 0 0 0 0 56 55 1 0 0 0 0 55 78 1 0 0 0 0 51 79 1 0 0 0 0 52 51 1 0 0 0 0 52 53 1 0 0 0 0 53 80 1 0 0 0 0 41 79 1 0 0 0 0 40 41 1 0 0 0 0 40 39 1 0 0 0 0 39 80 1 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring5.mol���������������������������������0000664�0000000�0000000�00000015510�12710376503�0025422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181119082D 70 91 0 0 1 0 999 V2000 1.9966 -3.1947 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2532 -3.5526 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.3640 -4.3701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1758 -4.5174 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5667 -3.7909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8858 -2.3771 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0740 -2.2299 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6831 -2.9563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.2528 -3.5528 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.9961 -3.1949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5663 -3.7912 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1753 -4.5176 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3635 -4.3703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6827 -2.9564 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0737 -2.2300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8855 -2.3773 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5588 -1.2356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7424 -0.4312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5640 -0.3573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.8883 -1.1159 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.2670 -1.6587 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7371 -1.3094 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.4129 -0.5509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0341 -0.0081 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1849 2.0121 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6705 2.6571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1250 3.3456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9203 3.1261 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9573 2.3019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7303 1.3235 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.9350 1.5431 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8981 2.3672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4126 3.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4124 3.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6673 4.5292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0003 5.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6676 4.5291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6675 2.9599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0001 2.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6673 2.9600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6703 2.6573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1847 2.0123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9571 2.3023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9200 3.1264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1248 3.3459 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8979 2.3674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9350 1.5432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7303 1.3238 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7425 -0.4309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5590 -1.2352 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2672 -1.6583 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.8885 -1.1155 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5641 -0.3569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0343 -0.0078 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4130 -0.5507 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7373 -1.3092 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1111 -1.6833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7530 -2.9925 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6323 0.6009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6796 1.0684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1714 2.4327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0825 4.3248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1715 2.4325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0829 4.3244 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6322 0.6006 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.6796 1.0679 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1107 -1.6834 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7524 -2.9930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0001 -2.6999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0005 -4.7999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 4 1 0 0 0 0 5 4 1 0 0 0 0 1 5 1 0 0 0 0 1 6 1 0 0 0 0 6 7 1 0 0 0 0 8 7 1 0 0 0 0 10 9 1 0 0 0 0 11 12 1 0 0 0 0 13 12 1 0 0 0 0 14 15 1 0 0 0 0 16 15 1 0 0 0 0 18 17 1 0 0 0 0 19 20 1 0 0 0 0 21 20 1 0 0 0 0 22 23 1 0 0 0 0 24 23 1 0 0 0 0 26 25 1 0 0 0 0 27 28 1 0 0 0 0 29 28 1 0 0 0 0 30 31 1 0 0 0 0 32 31 1 0 0 0 0 34 33 1 0 0 0 0 35 36 1 0 0 0 0 37 36 1 0 0 0 0 38 39 1 0 0 0 0 40 39 1 0 0 0 0 41 42 1 0 0 0 0 43 44 1 0 0 0 0 45 44 1 0 0 0 0 46 47 1 0 0 0 0 48 47 1 0 0 0 0 50 49 1 0 0 0 0 50 51 1 0 0 0 0 51 52 1 0 0 0 0 53 52 1 0 0 0 0 54 55 1 0 0 0 0 56 55 1 0 0 0 0 50 56 1 0 0 0 0 56 57 1 0 0 0 0 6 57 1 0 0 0 0 51 58 1 0 0 0 0 5 58 1 0 0 0 0 54 59 1 0 0 0 0 49 54 1 0 0 0 0 49 53 1 0 0 0 0 53 60 1 0 0 0 0 42 43 1 0 0 0 0 43 60 1 0 0 0 0 42 48 1 0 0 0 0 48 59 1 0 0 0 0 45 62 1 0 0 0 0 41 45 1 0 0 0 0 41 46 1 0 0 0 0 46 61 1 0 0 0 0 34 35 1 0 0 0 0 35 62 1 0 0 0 0 34 40 1 0 0 0 0 40 61 1 0 0 0 0 38 63 1 0 0 0 0 33 38 1 0 0 0 0 33 37 1 0 0 0 0 37 64 1 0 0 0 0 26 27 1 0 0 0 0 27 64 1 0 0 0 0 26 32 1 0 0 0 0 32 63 1 0 0 0 0 18 19 1 0 0 0 0 19 66 1 0 0 0 0 25 29 1 0 0 0 0 29 66 1 0 0 0 0 25 30 1 0 0 0 0 30 65 1 0 0 0 0 18 24 1 0 0 0 0 24 65 1 0 0 0 0 17 22 1 0 0 0 0 22 67 1 0 0 0 0 17 21 1 0 0 0 0 21 68 1 0 0 0 0 10 11 1 0 0 0 0 11 68 1 0 0 0 0 10 16 1 0 0 0 0 16 67 1 0 0 0 0 14 69 1 0 0 0 0 9 14 1 0 0 0 0 9 13 1 0 0 0 0 13 70 1 0 0 0 0 2 3 1 0 0 0 0 3 70 1 0 0 0 0 2 8 1 0 0 0 0 8 69 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring6.mol���������������������������������0000664�0000000�0000000�00000023326�12710376503�0025427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181119092D 100130 0 0 1 0 999 V2000 1.8047 -4.2183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0201 -4.4733 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0201 -5.2983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8047 -5.5532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2896 -4.8857 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8046 -3.3933 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0201 -3.1385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.5351 -3.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0200 4.4732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8047 4.2182 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8047 3.3932 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0200 3.1383 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.5351 3.8058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0203 5.2981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8048 5.5530 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2897 4.8856 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0194 -4.4734 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8039 -5.5534 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0193 -5.2984 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.5346 -3.8060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0194 -3.1386 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8042 4.2185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0195 4.4734 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.5346 3.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0195 3.1385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8042 3.3935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2889 4.8859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8040 5.5533 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0193 5.2984 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9390 2.3525 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4541 3.0198 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6695 2.7648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6696 1.9398 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4542 1.6850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7237 2.6075 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7235 3.4324 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9390 3.6873 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9390 -2.3525 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.7237 -2.6073 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.7235 -3.4324 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6696 -1.9399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4543 -1.6849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5695 -0.4127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5695 0.4122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.3541 0.6672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.8389 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.3541 -0.6677 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7849 -0.6676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3000 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7849 0.6673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5695 -0.4122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5695 0.4127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7848 0.6677 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3000 0.0002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7849 -0.6672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3541 -0.6671 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8389 0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3541 0.6678 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9394 2.3520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4545 3.0194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9393 3.6869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.7240 3.4319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.7240 2.6070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4545 1.6846 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6699 1.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6698 2.7646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4545 -3.0195 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9394 -2.3520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4545 -1.6845 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6699 -1.9395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6699 -2.7645 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9394 -3.6869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7240 -3.4319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7240 -2.6069 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1160 2.9124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3798 4.6518 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4541 -3.0199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6696 -2.7648 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9390 -3.6872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8040 -4.2185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8042 -3.3936 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2889 -4.8859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1156 -2.9127 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3791 -4.6523 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 3.5999 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0001 5.7499 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1161 2.9125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3797 4.6519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4239 1.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.4686 1.7772 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4240 -1.1124 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.4686 -1.7773 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4237 1.1130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4683 1.7777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4239 -1.1122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4688 -1.7762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1164 -2.9123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3804 -4.6515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0004 -3.6000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0009 -5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 4 1 0 0 0 0 5 4 1 0 0 0 0 6 7 1 0 0 0 0 8 7 1 0 0 0 0 9 10 1 0 0 0 0 11 12 1 0 0 0 0 13 12 1 0 0 0 0 14 15 1 0 0 0 0 16 15 1 0 0 0 0 80 17 1 0 0 0 0 82 18 1 0 0 0 0 19 18 1 0 0 0 0 20 21 1 0 0 0 0 81 21 1 0 0 0 0 22 23 1 0 0 0 0 24 25 1 0 0 0 0 26 25 1 0 0 0 0 22 26 1 0 0 0 0 22 27 1 0 0 0 0 27 28 1 0 0 0 0 29 28 1 0 0 0 0 30 31 1 0 0 0 0 32 33 1 0 0 0 0 34 33 1 0 0 0 0 35 36 1 0 0 0 0 37 36 1 0 0 0 0 31 32 1 0 0 0 0 31 37 1 0 0 0 0 77 38 1 0 0 0 0 39 40 1 0 0 0 0 79 40 1 0 0 0 0 78 41 1 0 0 0 0 42 41 1 0 0 0 0 44 43 1 0 0 0 0 45 46 1 0 0 0 0 47 46 1 0 0 0 0 48 49 1 0 0 0 0 50 49 1 0 0 0 0 51 52 1 0 0 0 0 53 54 1 0 0 0 0 55 54 1 0 0 0 0 56 57 1 0 0 0 0 58 57 1 0 0 0 0 60 59 1 0 0 0 0 61 62 1 0 0 0 0 63 62 1 0 0 0 0 64 65 1 0 0 0 0 66 65 1 0 0 0 0 67 68 1 0 0 0 0 69 70 1 0 0 0 0 71 70 1 0 0 0 0 72 73 1 0 0 0 0 74 73 1 0 0 0 0 27 76 1 0 0 0 0 37 76 1 0 0 0 0 26 75 1 0 0 0 0 32 75 1 0 0 0 0 79 84 1 0 0 0 0 82 84 1 0 0 0 0 78 83 1 0 0 0 0 81 83 1 0 0 0 0 77 79 1 0 0 0 0 77 78 1 0 0 0 0 80 81 1 0 0 0 0 80 82 1 0 0 0 0 9 13 1 0 0 0 0 13 85 1 0 0 0 0 9 14 1 0 0 0 0 14 86 1 0 0 0 0 23 24 1 0 0 0 0 24 85 1 0 0 0 0 23 29 1 0 0 0 0 29 86 1 0 0 0 0 11 87 1 0 0 0 0 10 11 1 0 0 0 0 60 66 1 0 0 0 0 66 87 1 0 0 0 0 60 61 1 0 0 0 0 61 88 1 0 0 0 0 10 16 1 0 0 0 0 16 88 1 0 0 0 0 63 90 1 0 0 0 0 59 63 1 0 0 0 0 44 50 1 0 0 0 0 50 89 1 0 0 0 0 44 45 1 0 0 0 0 45 90 1 0 0 0 0 59 64 1 0 0 0 0 64 89 1 0 0 0 0 38 42 1 0 0 0 0 42 91 1 0 0 0 0 38 39 1 0 0 0 0 39 92 1 0 0 0 0 43 48 1 0 0 0 0 48 91 1 0 0 0 0 43 47 1 0 0 0 0 47 92 1 0 0 0 0 35 94 1 0 0 0 0 30 35 1 0 0 0 0 52 53 1 0 0 0 0 53 93 1 0 0 0 0 52 58 1 0 0 0 0 58 94 1 0 0 0 0 30 34 1 0 0 0 0 34 93 1 0 0 0 0 74 96 1 0 0 0 0 68 74 1 0 0 0 0 68 69 1 0 0 0 0 69 95 1 0 0 0 0 51 55 1 0 0 0 0 55 95 1 0 0 0 0 51 56 1 0 0 0 0 56 96 1 0 0 0 0 5 98 1 0 0 0 0 1 5 1 0 0 0 0 1 6 1 0 0 0 0 6 97 1 0 0 0 0 67 71 1 0 0 0 0 71 97 1 0 0 0 0 67 72 1 0 0 0 0 72 98 1 0 0 0 0 19100 1 0 0 0 0 17 19 1 0 0 0 0 17 20 1 0 0 0 0 20 99 1 0 0 0 0 2 8 1 0 0 0 0 8 99 1 0 0 0 0 2 3 1 0 0 0 0 3100 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/complexring7.mol���������������������������������0000664�0000000�0000000�00000011624�12710376503�0025426�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181119072D 50 65 0 0 1 0 999 V2000 12.3772 5.5954 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.8922 4.9280 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.3770 4.2606 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13.1618 4.5155 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13.1618 5.3404 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.8923 6.2630 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.1077 6.0080 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.1077 5.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2628 4.0739 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4782 4.3289 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.9933 3.6615 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4781 2.9939 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2627 3.2489 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.7478 4.7413 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2629 5.4087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4782 5.1538 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2632 9.8821 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4785 9.6273 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4785 8.8022 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2631 8.5472 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.7481 9.2146 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 9.2632 10.7071 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.4787 10.9620 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.9937 10.2946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.8534 6.5657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.8534 7.3907 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.0688 7.6457 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.5838 6.9783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.0688 6.3107 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.6379 6.3106 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.1229 6.9781 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.6380 7.6456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.8925 9.0276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.3773 8.3601 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13.1621 8.6150 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13.1621 9.4400 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.3774 9.6949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.1079 8.7727 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.1078 7.9477 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.8924 7.6927 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.6229 6.9778 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 13.7729 6.9777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 10.3448 8.7375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.0091 10.7823 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.2763 8.0654 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.5371 9.3293 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 8.2762 5.8907 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.5367 4.6269 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 10.3444 5.2183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.0087 3.1736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 4 1 0 0 0 0 5 4 1 0 0 0 0 1 5 1 0 0 0 0 1 6 1 0 0 0 0 6 7 1 0 0 0 0 8 7 1 0 0 0 0 9 10 1 0 0 0 0 11 12 1 0 0 0 0 13 12 1 0 0 0 0 14 15 1 0 0 0 0 16 15 1 0 0 0 0 18 17 1 0 0 0 0 19 20 1 0 0 0 0 21 20 1 0 0 0 0 22 23 1 0 0 0 0 24 23 1 0 0 0 0 25 26 1 0 0 0 0 27 28 1 0 0 0 0 29 28 1 0 0 0 0 30 31 1 0 0 0 0 32 31 1 0 0 0 0 33 34 1 0 0 0 0 35 36 1 0 0 0 0 37 36 1 0 0 0 0 38 39 1 0 0 0 0 40 39 1 0 0 0 0 34 35 1 0 0 0 0 34 40 1 0 0 0 0 40 41 1 0 0 0 0 6 41 1 0 0 0 0 35 42 1 0 0 0 0 5 42 1 0 0 0 0 21 43 1 0 0 0 0 17 21 1 0 0 0 0 33 37 1 0 0 0 0 37 44 1 0 0 0 0 33 38 1 0 0 0 0 38 43 1 0 0 0 0 17 22 1 0 0 0 0 22 44 1 0 0 0 0 27 46 1 0 0 0 0 26 27 1 0 0 0 0 18 24 1 0 0 0 0 24 46 1 0 0 0 0 18 19 1 0 0 0 0 19 45 1 0 0 0 0 26 32 1 0 0 0 0 32 45 1 0 0 0 0 29 48 1 0 0 0 0 25 29 1 0 0 0 0 25 30 1 0 0 0 0 30 47 1 0 0 0 0 10 11 1 0 0 0 0 11 48 1 0 0 0 0 10 16 1 0 0 0 0 16 47 1 0 0 0 0 8 49 1 0 0 0 0 2 8 1 0 0 0 0 9 13 1 0 0 0 0 13 50 1 0 0 0 0 9 14 1 0 0 0 0 14 49 1 0 0 0 0 2 3 1 0 0 0 0 3 50 1 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/cube.mol�����������������������������������������0000664�0000000�0000000�00000001612�12710376503�0023722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090922082D 8 12 0 0 1 0 999 V2000 2.4259 2.6815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5174 3.6054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8338 2.6815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4469 4.1186 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7891 3.6054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4965 4.8519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8338 4.0684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7471 4.8079 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 8 0 0 0 0 1 3 8 0 0 0 0 1 4 8 0 0 0 0 2 5 8 0 0 0 0 2 6 8 0 0 0 0 3 5 8 0 0 0 0 3 7 8 0 0 0 0 4 6 8 0 0 0 0 4 7 8 0 0 0 0 5 8 8 0 0 0 0 6 8 8 0 0 0 0 7 8 8 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/highvalence.mol����������������������������������0000664�0000000�0000000�00000001456�12710376503�0025267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090922252D 7 11 0 0 0 0 999 V2000 -1.9726 0.1034 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.5000 0.8659 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1691 1.6091 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0826 1.2023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.9780 0.2079 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0770 1.0795 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 1 1 0 0 0 0 5 6 1 0 0 0 0 6 2 1 0 0 0 0 7 6 1 0 0 0 0 7 1 1 0 0 0 0 7 2 1 0 0 0 0 7 3 1 0 0 0 0 7 4 1 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/krabe.mol����������������������������������������0000664�0000000�0000000�00000001644�12710376503�0024075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090922252D 9 10 0 0 0 0 999 V2000 1.6290 -1.9119 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8127 -2.0914 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2248 -1.5105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1634 -0.5574 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2444 -0.5581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1175 -1.3600 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6220 -0.9357 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5820 -0.9250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7132 -0.1214 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 6 1 0 0 0 0 6 7 1 0 0 0 0 7 8 1 0 0 0 0 1 8 1 0 0 0 0 8 9 1 0 0 0 0 4 9 1 0 0 0 0 M END ��������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/krabe2.mol���������������������������������������0000664�0000000�0000000�00000001510�12710376503�0024147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181118022D 8 9 0 0 0 0 999 V2000 -0.2228 -1.6501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3975 -1.2732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7180 -0.5646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1574 -0.1247 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2971 -0.9378 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2688 -0.9378 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7850 -1.3665 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6468 -0.5491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 1 5 1 0 0 0 0 5 6 1 0 0 0 0 6 7 1 0 0 0 0 7 8 1 0 0 0 0 3 8 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/s13133.mol���������������������������������������0000664�0000000�0000000�00000002634�12710376503�0023646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090923072D 14 17 0 0 0 0 999 V2000 1.9945 -0.4532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.9967 -0.4685 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5150 -1.1100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5150 0.1861 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4866 -1.1472 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4800 0.2277 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1948 -0.2145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6480 -0.0460 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7663 -0.0657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6480 -0.8736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7663 -0.8582 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0635 0.3569 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0700 -1.2808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0503 -0.4970 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 5 1 0 0 0 0 3 1 1 0 0 0 0 4 1 1 0 0 0 0 5 10 1 0 0 0 0 6 8 1 0 0 0 0 7 12 1 0 0 0 0 8 12 1 0 0 0 0 9 4 1 0 0 0 0 10 13 1 0 0 0 0 11 3 1 0 0 0 0 12 9 1 0 0 0 0 13 11 1 0 0 0 0 14 13 1 0 0 0 0 11 9 1 0 0 0 0 14 7 2 0 0 0 0 10 8 1 0 0 0 0 2 6 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/sphere.mol���������������������������������������0000664�0000000�0000000�00000014166�12710376503�0024302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090921502D 60 90 0 0 0 0 999 V2000 4.2810 2.6747 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2707 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7968 3.1409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1888 3.1640 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7840 1.3066 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2016 1.3271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3151 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4483 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1093 2.7131 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6917 3.9018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3100 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4279 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1145 1.7882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6815 0.5918 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4876 2.9616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6158 4.1683 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5585 2.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1204 4.1913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4825 1.4987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5978 0.2869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5431 1.5116 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0795 0.3125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9727 2.2340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1290 3.7045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0444 4.4680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1999 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0513 3.7327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2980 4.4782 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1290 0.7532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0162 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0307 0.7839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2442 0.0128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0990 2.2442 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2554 3.7199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1990 4.1888 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3314 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1828 3.7276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6858 4.2016 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2323 0.7558 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1887 0.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1546 0.7890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6627 0.3356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7353 2.9847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7225 1.4910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6405 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8241 2.9872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8164 1.5295 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8506 3.9300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6097 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8352 0.6251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1717 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1511 1.7652 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1307 3.1307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9914 2.7156 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9863 1.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4996 3.1768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0897 1.3040 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4919 1.3706 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0051 2.7336 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 1.8292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 8 0 0 0 0 1 3 8 0 0 0 0 1 4 8 0 0 0 0 2 5 8 0 0 0 0 2 6 8 0 0 0 0 3 7 8 0 0 0 0 3 8 8 0 0 0 0 4 9 8 0 0 0 0 4 10 8 0 0 0 0 5 11 8 0 0 0 0 5 12 8 0 0 0 0 6 13 8 0 0 0 0 6 14 8 0 0 0 0 7 11 8 0 0 0 0 7 15 8 0 0 0 0 8 10 8 0 0 0 0 8 16 8 0 0 0 0 9 13 8 0 0 0 0 9 17 8 0 0 0 0 10 18 8 0 0 0 0 11 19 8 0 0 0 0 12 14 8 0 0 0 0 12 20 8 0 0 0 0 13 21 8 0 0 0 0 14 22 8 0 0 0 0 15 23 8 0 0 0 0 15 24 8 0 0 0 0 16 24 8 0 0 0 0 16 25 8 0 0 0 0 17 26 8 0 0 0 0 17 27 8 0 0 0 0 18 27 8 0 0 0 0 18 28 8 0 0 0 0 19 23 8 0 0 0 0 19 29 8 0 0 0 0 20 29 8 0 0 0 0 20 30 8 0 0 0 0 21 26 8 0 0 0 0 21 31 8 0 0 0 0 22 31 8 0 0 0 0 22 32 8 0 0 0 0 23 33 8 0 0 0 0 24 34 8 0 0 0 0 25 28 8 0 0 0 0 25 35 8 0 0 0 0 26 36 8 0 0 0 0 27 37 8 0 0 0 0 28 38 8 0 0 0 0 29 39 8 0 0 0 0 30 32 8 0 0 0 0 30 40 8 0 0 0 0 31 41 8 0 0 0 0 32 42 8 0 0 0 0 33 43 8 0 0 0 0 33 44 8 0 0 0 0 34 35 8 0 0 0 0 34 43 8 0 0 0 0 35 45 8 0 0 0 0 36 46 8 0 0 0 0 36 47 8 0 0 0 0 37 38 8 0 0 0 0 37 46 8 0 0 0 0 38 48 8 0 0 0 0 39 40 8 0 0 0 0 39 44 8 0 0 0 0 40 49 8 0 0 0 0 41 42 8 0 0 0 0 41 47 8 0 0 0 0 42 50 8 0 0 0 0 43 51 8 0 0 0 0 44 52 8 0 0 0 0 45 48 8 0 0 0 0 45 53 8 0 0 0 0 46 54 8 0 0 0 0 47 55 8 0 0 0 0 48 56 8 0 0 0 0 49 50 8 0 0 0 0 49 57 8 0 0 0 0 50 58 8 0 0 0 0 51 52 8 0 0 0 0 51 53 8 0 0 0 0 52 57 8 0 0 0 0 53 59 8 0 0 0 0 54 55 8 0 0 0 0 54 56 8 0 0 0 0 55 58 8 0 0 0 0 56 59 8 0 0 0 0 57 60 8 0 0 0 0 58 60 8 0 0 0 0 59 60 8 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/sphere2.mol��������������������������������������0000664�0000000�0000000�00000024064�12710376503�0024362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ -INDIGO-12271017453D 80210 0 0 0 0 0 0 0 0999 V2000 -0.3637 4.0495 1.3474 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7474 3.3091 2.0890 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9201 3.0169 0.8453 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2629 3.5789 -0.6621 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6814 4.2189 -0.3519 C 0 0 0 0 0 0 0 0 0 0 0 0 0.5821 3.9816 -1.4933 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1769 3.5719 -0.9460 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4913 3.4047 0.7356 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2166 3.6445 1.8883 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4263 2.4963 3.1745 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0553 1.7636 3.9082 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5397 2.1724 3.3618 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5045 0.7298 3.4053 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.6673 0.4397 2.1731 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8756 1.5890 0.8871 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1812 0.7094 -0.5760 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5308 1.2645 -2.0670 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5671 2.7067 -2.1117 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.2921 2.4669 -3.2665 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2759 3.1015 -2.9592 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6827 2.1481 -3.3188 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8564 2.4392 -2.0732 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8425 1.1512 -1.5043 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1590 0.9830 0.1944 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4867 2.1049 1.3091 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8304 1.5442 2.8158 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8523 -0.1324 3.1937 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4687 -0.8743 3.9353 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0763 0.0691 4.2909 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5070 -0.5708 3.9808 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.6825 -2.1475 3.3180 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8568 -2.4392 2.0731 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8437 -1.1516 1.5046 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1597 -0.9833 -0.1941 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4865 -2.1050 -1.3087 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8294 -1.5438 -2.8150 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8515 0.1328 -3.1927 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4682 0.8745 -3.9353 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0759 -0.0689 -4.2914 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5073 0.5714 -3.9818 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6660 -0.4396 -2.1732 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8749 -1.5882 -0.8869 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1808 -0.7093 0.5766 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5310 -1.2647 2.0677 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5673 -2.7070 2.1116 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2922 -2.4666 3.2660 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.2759 -3.1008 2.9584 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.5820 -3.9816 1.4930 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1770 -3.5721 0.9462 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.4915 -3.4049 -0.7353 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.2167 -3.6446 -1.8883 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4257 -2.4959 -3.1742 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.0548 -1.7634 -3.9080 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5399 -2.1732 -3.3617 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7474 -3.3097 -2.0888 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9204 -3.0168 -0.8452 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2630 -3.5791 0.6618 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6814 -4.2193 0.3517 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3634 -4.0501 -1.3477 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1136 -1.6520 3.9523 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5829 0.8845 3.8734 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1721 3.1685 2.8645 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9541 2.0450 2.3181 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5135 2.3375 -0.6688 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0809 3.6439 -1.9706 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9849 4.1555 0.2157 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4549 2.4802 -0.4143 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9187 0.9335 -2.9900 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5134 -2.3376 0.6690 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4554 -2.4806 0.4144 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9192 -0.9334 2.9896 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8222 -0.4596 -1.8445 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.9851 -4.1559 -0.2157 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1723 -3.1689 -2.8645 C 0 0 0 0 0 0 0 0 0 0 0 0 2.9537 -2.0450 -2.3180 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0810 -3.6440 1.9702 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8227 0.4596 1.8450 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1138 1.6525 -3.9533 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.5820 -0.8843 -3.8728 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5037 -0.7298 -3.4058 C 0 0 0 0 0 0 0 0 0 0 0 0 12 2 1 0 0 0 0 3 2 1 0 0 0 0 13 12 1 0 0 0 0 15 3 1 0 0 0 0 14 15 1 0 0 0 0 4 3 1 0 0 0 0 16 15 1 0 0 0 0 18 4 1 0 0 0 0 17 18 1 0 0 0 0 6 5 1 0 0 0 0 4 5 1 0 0 0 0 20 6 1 0 0 0 0 19 18 1 0 0 0 0 9 1 1 0 0 0 0 5 1 1 0 0 0 0 8 9 1 0 0 0 0 7 6 1 0 0 0 0 2 1 1 0 0 0 0 10 9 1 0 0 0 0 11 12 1 0 0 0 0 26 10 1 0 0 0 0 11 10 1 0 0 0 0 27 26 1 0 0 0 0 29 11 1 0 0 0 0 28 29 1 0 0 0 0 30 13 1 0 0 0 0 14 13 1 0 0 0 0 31 30 1 0 0 0 0 33 14 1 0 0 0 0 32 33 1 0 0 0 0 34 16 1 0 0 0 0 17 16 1 0 0 0 0 35 34 1 0 0 0 0 37 17 1 0 0 0 0 36 37 1 0 0 0 0 21 20 1 0 0 0 0 19 20 1 0 0 0 0 40 21 1 0 0 0 0 38 19 1 0 0 0 0 39 38 1 0 0 0 0 25 8 1 0 0 0 0 7 8 1 0 0 0 0 24 25 1 0 0 0 0 22 7 1 0 0 0 0 23 22 1 0 0 0 0 25 26 1 0 0 0 0 29 30 1 0 0 0 0 33 34 1 0 0 0 0 37 38 1 0 0 0 0 21 22 1 0 0 0 0 27 44 1 0 0 0 0 28 27 1 0 0 0 0 28 46 1 0 0 0 0 45 46 1 0 0 0 0 44 45 1 0 0 0 0 57 45 1 0 0 0 0 31 47 1 0 0 0 0 32 31 1 0 0 0 0 32 49 1 0 0 0 0 48 49 1 0 0 0 0 47 48 1 0 0 0 0 58 48 1 0 0 0 0 24 43 1 0 0 0 0 23 24 1 0 0 0 0 23 41 1 0 0 0 0 42 41 1 0 0 0 0 43 42 1 0 0 0 0 56 42 1 0 0 0 0 40 80 1 0 0 0 0 39 40 1 0 0 0 0 39 53 1 0 0 0 0 54 53 1 0 0 0 0 80 54 1 0 0 0 0 55 54 1 0 0 0 0 35 50 1 0 0 0 0 36 35 1 0 0 0 0 36 52 1 0 0 0 0 51 52 1 0 0 0 0 50 51 1 0 0 0 0 59 51 1 0 0 0 0 43 44 1 0 0 0 0 46 47 1 0 0 0 0 49 50 1 0 0 0 0 52 53 1 0 0 0 0 80 41 1 0 0 0 0 56 57 1 0 0 0 0 57 58 1 0 0 0 0 58 59 1 0 0 0 0 59 55 1 0 0 0 0 55 56 1 0 0 0 0 56 69 1 0 0 0 0 69 57 1 0 0 0 0 37 72 1 0 0 0 0 72 36 1 0 0 0 0 72 35 1 0 0 0 0 72 34 1 0 0 0 0 17 72 1 0 0 0 0 16 72 1 0 0 0 0 42 69 1 0 0 0 0 43 69 1 0 0 0 0 44 69 1 0 0 0 0 69 45 1 0 0 0 0 23 67 1 0 0 0 0 24 67 1 0 0 0 0 25 67 1 0 0 0 0 22 67 1 0 0 0 0 7 67 1 0 0 0 0 8 67 1 0 0 0 0 7 66 1 0 0 0 0 8 66 1 0 0 0 0 6 66 1 0 0 0 0 5 66 1 0 0 0 0 1 66 1 0 0 0 0 9 66 1 0 0 0 0 1 62 1 0 0 0 0 9 62 1 0 0 0 0 10 62 1 0 0 0 0 11 62 1 0 0 0 0 2 62 1 0 0 0 0 12 62 1 0 0 0 0 14 71 1 0 0 0 0 33 71 1 0 0 0 0 13 71 1 0 0 0 0 30 71 1 0 0 0 0 31 71 1 0 0 0 0 32 71 1 0 0 0 0 20 65 1 0 0 0 0 6 65 1 0 0 0 0 5 65 1 0 0 0 0 19 65 1 0 0 0 0 18 65 1 0 0 0 0 4 65 1 0 0 0 0 18 64 1 0 0 0 0 17 64 1 0 0 0 0 4 64 1 0 0 0 0 3 64 1 0 0 0 0 15 64 1 0 0 0 0 16 64 1 0 0 0 0 3 63 1 0 0 0 0 15 63 1 0 0 0 0 14 63 1 0 0 0 0 13 63 1 0 0 0 0 2 63 1 0 0 0 0 12 63 1 0 0 0 0 10 61 1 0 0 0 0 26 61 1 0 0 0 0 27 61 1 0 0 0 0 28 61 1 0 0 0 0 11 61 1 0 0 0 0 29 61 1 0 0 0 0 68 21 1 0 0 0 0 68 40 1 0 0 0 0 68 80 1 0 0 0 0 41 68 1 0 0 0 0 23 68 1 0 0 0 0 22 68 1 0 0 0 0 45 76 1 0 0 0 0 57 76 1 0 0 0 0 76 47 1 0 0 0 0 76 48 1 0 0 0 0 76 58 1 0 0 0 0 34 70 1 0 0 0 0 35 70 1 0 0 0 0 50 70 1 0 0 0 0 33 70 1 0 0 0 0 32 70 1 0 0 0 0 49 70 1 0 0 0 0 51 73 1 0 0 0 0 50 73 1 0 0 0 0 49 73 1 0 0 0 0 59 73 1 0 0 0 0 48 73 1 0 0 0 0 58 73 1 0 0 0 0 56 75 1 0 0 0 0 42 75 1 0 0 0 0 75 54 1 0 0 0 0 75 55 1 0 0 0 0 75 41 1 0 0 0 0 53 74 1 0 0 0 0 51 74 1 0 0 0 0 52 74 1 0 0 0 0 54 74 1 0 0 0 0 55 74 1 0 0 0 0 74 59 1 0 0 0 0 43 77 1 0 0 0 0 24 77 1 0 0 0 0 25 77 1 0 0 0 0 44 77 1 0 0 0 0 26 77 1 0 0 0 0 27 77 1 0 0 0 0 46 60 1 0 0 0 0 28 60 1 0 0 0 0 29 60 1 0 0 0 0 60 30 1 0 0 0 0 60 47 1 0 0 0 0 60 31 1 0 0 0 0 40 78 1 0 0 0 0 21 78 1 0 0 0 0 20 78 1 0 0 0 0 19 78 1 0 0 0 0 39 78 1 0 0 0 0 38 78 1 0 0 0 0 39 79 1 0 0 0 0 38 79 1 0 0 0 0 37 79 1 0 0 0 0 36 79 1 0 0 0 0 52 79 1 0 0 0 0 53 79 1 0 0 0 0 46 76 1 0 0 0 0 75 80 1 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/sphere_mod1.mol����������������������������������0000664�0000000�0000000�00000015602�12710376503�0025216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Marvin 06090921502D 68100 0 0 0 0 999 V2000 4.2810 2.6747 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2707 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7968 3.1409 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1888 3.1640 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.0650 3.1179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.7840 1.3066 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2016 1.3271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.0521 1.3092 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3151 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4483 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1093 2.7131 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6917 3.9018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8489 2.6568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.3100 1.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4279 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1145 1.7882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6815 0.5918 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8386 1.7524 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4876 2.9616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.6158 4.1683 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5585 2.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1204 4.1913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.6329 3.1025 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4825 1.4987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5978 0.2869 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5431 1.5116 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0795 0.3125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.6149 1.2963 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9727 2.2340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1290 3.7045 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0444 4.4680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1999 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0513 3.7327 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2980 4.4782 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.4117 2.6439 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1290 0.7532 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.0162 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.0307 0.7839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.2442 0.0128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 7.4014 1.7421 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0990 2.2442 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2554 3.7199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1990 4.1888 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.3314 2.2571 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1828 3.7276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6858 4.2016 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2323 0.7558 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1887 0.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1546 0.7890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.6627 0.3356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7353 2.9847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7225 1.4910 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6405 3.8839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8241 2.9872 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8164 1.5295 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8506 3.9300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6097 0.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8352 0.6251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1717 2.6797 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1511 1.7652 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1307 3.1307 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9914 2.7156 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9863 1.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4996 3.1768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0897 1.3040 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4919 1.3706 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0051 2.7336 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 1.8292 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 8 0 0 0 0 1 3 8 0 0 0 0 1 4 8 0 0 0 0 1 5 8 0 0 0 0 2 6 8 0 0 0 0 2 7 8 0 0 0 0 2 8 8 0 0 0 0 3 9 8 0 0 0 0 3 10 8 0 0 0 0 4 11 8 0 0 0 0 4 12 8 0 0 0 0 5 13 8 0 0 0 0 6 14 8 0 0 0 0 6 15 8 0 0 0 0 7 16 8 0 0 0 0 7 17 8 0 0 0 0 8 18 8 0 0 0 0 9 14 8 0 0 0 0 9 19 8 0 0 0 0 10 12 8 0 0 0 0 10 20 8 0 0 0 0 11 16 8 0 0 0 0 11 21 8 0 0 0 0 12 22 8 0 0 0 0 13 18 8 0 0 0 0 13 23 8 0 0 0 0 14 24 8 0 0 0 0 15 17 8 0 0 0 0 15 25 8 0 0 0 0 16 26 8 0 0 0 0 17 27 8 0 0 0 0 18 28 8 0 0 0 0 19 29 8 0 0 0 0 19 30 8 0 0 0 0 20 30 8 0 0 0 0 20 31 8 0 0 0 0 21 32 8 0 0 0 0 21 33 8 0 0 0 0 22 33 8 0 0 0 0 22 34 8 0 0 0 0 23 35 8 0 0 0 0 24 29 8 0 0 0 0 24 36 8 0 0 0 0 25 36 8 0 0 0 0 25 37 8 0 0 0 0 26 32 8 0 0 0 0 26 38 8 0 0 0 0 27 38 8 0 0 0 0 27 39 8 0 0 0 0 28 40 8 0 0 0 0 29 41 8 0 0 0 0 30 42 8 0 0 0 0 31 34 8 0 0 0 0 31 43 8 0 0 0 0 32 44 8 0 0 0 0 33 45 8 0 0 0 0 34 46 8 0 0 0 0 35 40 8 0 0 0 0 36 47 8 0 0 0 0 37 39 8 0 0 0 0 37 48 8 0 0 0 0 38 49 8 0 0 0 0 39 50 8 0 0 0 0 41 51 8 0 0 0 0 41 52 8 0 0 0 0 42 43 8 0 0 0 0 42 51 8 0 0 0 0 43 53 8 0 0 0 0 44 54 8 0 0 0 0 44 55 8 0 0 0 0 45 46 8 0 0 0 0 45 54 8 0 0 0 0 46 56 8 0 0 0 0 47 48 8 0 0 0 0 47 52 8 0 0 0 0 48 57 8 0 0 0 0 49 50 8 0 0 0 0 49 55 8 0 0 0 0 50 58 8 0 0 0 0 51 59 8 0 0 0 0 52 60 8 0 0 0 0 53 56 8 0 0 0 0 53 61 8 0 0 0 0 54 62 8 0 0 0 0 55 63 8 0 0 0 0 56 64 8 0 0 0 0 57 58 8 0 0 0 0 57 65 8 0 0 0 0 58 66 8 0 0 0 0 59 60 8 0 0 0 0 59 61 8 0 0 0 0 60 65 8 0 0 0 0 61 67 8 0 0 0 0 62 63 8 0 0 0 0 62 64 8 0 0 0 0 63 66 8 0 0 0 0 64 67 8 0 0 0 0 65 68 8 0 0 0 0 66 68 8 0 0 0 0 67 68 8 0 0 0 0 M END ������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/layout/src/templates/molecules/superstar.mol������������������������������������0000664�0000000�0000000�00000016174�12710376503�0025045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Mrv0541 03181119032D 70105 0 0 0 0 999 V2000 -0.6526 0.2101 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3992 -0.5750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0157 0.6937 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4258 -0.5766 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6823 0.2074 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.8854 -1.2416 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7104 -1.2399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6320 -2.0266 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.9668 -2.0241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3004 -2.5103 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9094 -1.2451 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6529 -2.0291 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7344 -1.2467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.3193 -2.5154 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9878 -2.0318 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4673 0.4608 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1339 -0.0255 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7240 1.2448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8023 0.4581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5490 1.2432 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0173 1.5187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6857 2.0023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6492 2.0049 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4324 2.7874 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3926 2.7891 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4368 0.4665 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.6902 1.2516 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1052 -0.0172 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5151 1.2531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7716 0.4691 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4039 -3.3359 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1568 -3.6730 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.7406 -2.3690 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8251 -3.1895 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.6088 0.2849 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.1621 0.8968 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1022 1.8550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9087 1.6819 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8462 3.5009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4352 4.2162 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.8036 3.5043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3898 4.2178 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0659 1.8673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8732 1.6974 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.5788 0.2993 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.1297 0.9134 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.7211 -2.3581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8089 -3.1784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3883 -3.3304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1425 -3.6646 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.4772 -3.6618 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.3990 -4.4484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3904 -4.6891 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4100 -4.4579 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4913 -3.6758 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.4137 -4.6930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.9869 0.8951 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.5771 2.1651 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.5205 1.7770 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.6917 5.0002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6430 5.0028 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0177 5.7802 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5394 2.1835 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.9544 0.9151 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.4895 1.7837 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -6.2753 2.0354 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.8726 -5.3584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.9014 -5.3584 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.2978 2.0264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0206 6.6138 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 8 0 0 0 0 3 1 8 0 0 0 0 4 2 8 0 0 0 0 5 3 8 0 0 0 0 5 4 8 0 0 0 0 7 6 8 0 0 0 0 8 6 8 0 0 0 0 9 7 8 0 0 0 0 10 8 8 0 0 0 0 2 6 8 0 0 0 0 12 11 8 0 0 0 0 13 11 8 0 0 0 0 14 12 8 0 0 0 0 15 13 8 0 0 0 0 4 11 8 0 0 0 0 17 16 8 0 0 0 0 18 16 8 0 0 0 0 19 17 8 0 0 0 0 20 18 8 0 0 0 0 5 16 8 0 0 0 0 22 21 8 0 0 0 0 23 21 8 0 0 0 0 24 22 8 0 0 0 0 25 23 8 0 0 0 0 3 21 8 0 0 0 0 27 26 8 0 0 0 0 28 26 8 0 0 0 0 29 27 8 0 0 0 0 30 28 8 0 0 0 0 1 26 8 0 0 0 0 7 28 8 0 0 0 0 8 12 8 0 0 0 0 13 17 8 0 0 0 0 18 22 8 0 0 0 0 23 27 8 0 0 0 0 31 14 8 0 0 0 0 15 14 8 0 0 0 0 32 31 8 0 0 0 0 33 15 8 0 0 0 0 34 33 8 0 0 0 0 35 19 8 0 0 0 0 20 19 8 0 0 0 0 36 35 8 0 0 0 0 37 20 8 0 0 0 0 38 37 8 0 0 0 0 39 24 8 0 0 0 0 25 24 8 0 0 0 0 40 39 8 0 0 0 0 41 25 8 0 0 0 0 42 41 8 0 0 0 0 43 29 8 0 0 0 0 30 29 8 0 0 0 0 44 43 8 0 0 0 0 45 30 8 0 0 0 0 46 45 8 0 0 0 0 47 9 8 0 0 0 0 10 9 8 0 0 0 0 48 47 8 0 0 0 0 49 10 8 0 0 0 0 50 49 8 0 0 0 0 45 47 8 0 0 0 0 49 31 8 0 0 0 0 33 35 8 0 0 0 0 37 39 8 0 0 0 0 41 43 8 0 0 0 0 48 51 8 0 0 0 0 50 48 8 0 0 0 0 50 52 8 0 0 0 0 53 52 8 0 0 0 0 51 53 8 0 0 0 0 32 54 8 0 0 0 0 34 32 8 0 0 0 0 34 55 8 0 0 0 0 56 55 8 0 0 0 0 54 56 8 0 0 0 0 36 57 8 0 0 0 0 38 36 8 0 0 0 0 38 58 8 0 0 0 0 59 58 8 0 0 0 0 57 59 8 0 0 0 0 40 60 8 0 0 0 0 42 40 8 0 0 0 0 42 61 8 0 0 0 0 62 61 8 0 0 0 0 60 62 8 0 0 0 0 44 63 8 0 0 0 0 46 44 8 0 0 0 0 46 64 8 0 0 0 0 65 64 8 0 0 0 0 63 65 8 0 0 0 0 64 51 8 0 0 0 0 52 54 8 0 0 0 0 55 57 8 0 0 0 0 58 60 8 0 0 0 0 61 63 8 0 0 0 0 66 65 8 0 0 0 0 67 53 8 0 0 0 0 68 56 8 0 0 0 0 69 59 8 0 0 0 0 70 62 8 0 0 0 0 70 66 8 0 0 0 0 66 67 8 0 0 0 0 67 68 8 0 0 0 0 68 69 8 0 0 0 0 69 70 8 0 0 0 0 M END ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016006�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/CDXConstants.h���������������������������������������������������������0000664�0000000�0000000�00000131607�12710376503�0020502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $Workfile: CDXConstants.h $ $Revision: 82 $ $Date: 7/02/02 6:39p $ Copyright: © 1986-2002 CambridgeSoft Corp., all rights reserved. Description: Constants defined by the CDX file format Specification */ #ifndef _H_CDXConstants #define _H_CDXConstants // ------------------------------------------------------------ // An ANSI replacement for four-byte character constants. // ------------------------------------------------------------ #ifndef QUADCONST #if defined( _X86_ ) && !defined( _CS3D ) // All 3D projects uses the second QUADCONST definition for both MAC and Windows // littleEndian #define QUADCONST(a, b, c, d) \ (((long) ((d) & 0xff) << 24) \ | ((long) ((c) & 0xff) << 16) \ | ((long) ((b) & 0xff) << 8) \ | ((long) ((a) & 0xff))) #else // defined( _X86_ ) && !defined( _CS3D ) #define QUADCONST(a, b, c, d) \ (((long) ((a) & 0xff) << 24) \ | ((long) ((b) & 0xff) << 16) \ | ((long) ((c) & 0xff) << 8) \ | ((long) ((d) & 0xff))) #endif // defined( _X86_ ) && !defined( _CS3D ) #endif // QUADCONST typedef UINT16 CDXTag; typedef INT32 CDXObjectID; // signed for now, due to mac compiler bug? const CDXObjectID kCDXUndefinedId = (CDXObjectID)-1; const int kCDX_HeaderStringLen = 8; #define kCDX_HeaderString "VjCD0100" #define kCDX_Signature QUADCONST('V','j','C','D') #define kCDX_HeaderLength 28 #define kCDXML_HeaderString \ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << GetTextEOL() << \ "<!DOCTYPE CDXML SYSTEM \"http://www.cambridgesoft.com/xml/cdxml.dtd\" >" << GetTextEOL() const UINT16 kCDXTag_Object = 0x8000; const UINT16 kCDXTag_UserDefined = 0x4000; enum CDXDatumID { // General properties. kCDXProp_EndObject, // 0x0000 Marks end of object. kCDXProp_CreationUserName, // 0x0001 The name of the creator (program user's name) of the document. (CDXString) kCDXProp_CreationDate, // 0x0002 The time of object creation. (CDXDate) kCDXProp_CreationProgram, // 0x0003 The name of the program, including version and platform, that created the associated CDX object. ChemDraw 4.0 uses ChemDraw 4.0 as the value of CreationProgram. (CDXString) kCDXProp_ModificationUserName, // 0x0004 The name of the last modifier (program user's name) of the document. (CDXString) kCDXProp_ModificationDate, // 0x0005 Time of the last modification. (CDXDate) kCDXProp_ModificationProgram, // 0x0006 The name of the program, including version and platform, of the last program to perform a modification. ChemDraw 4.0 uses ChemDraw 4.0 as the value of CreationProgram. (CDXString) kCDXProp_Unused1, // 0x0007 Table of contents. (obsolete) kCDXProp_Name, // 0x0008 Name of an object. (CDXString) kCDXProp_Comment, // 0x0009 An arbitrary string intended to be meaningful to a user. (CDXString) kCDXProp_ZOrder, // 0x000A Back-to-front ordering index in 2D drawing. (INT16) kCDXProp_RegistryNumber, // 0x000B A registry or catalog number of a molecule object. (CDXString) kCDXProp_RegistryAuthority, // 0x000C A string that specifies the authority which issued a registry or catalog number. Some examples of registry authorities are CAS, Beilstein, Aldrich, and Merck. (CDXString) kCDXProp_Unused2, // 0x000D Indicates that this object (the reference object) is an alias to an object elsewhere in the document (the target object). The attributes and contained objects should be taken from the target object. (obsolete) kCDXProp_RepresentsProperty, // 0x000E Indicates that this object represents some property in some other object. (CDXRepresentsProperty) kCDXProp_IgnoreWarnings, // 0x000F Signifies whether chemical warnings should be suppressed on this object. (CDXBooleanImplied) kCDXProp_ChemicalWarning, // 0x0010 A warning concerning possible chemical problems with this object. (CDXString) kCDXProp_Visible, // 0x0011 The object is visible if non-zero. (CDXBoolean) // Fonts. kCDXProp_FontTable = 0x0100, // 0x0100 A list of fonts used in the document. (CDXFontTable) // Coordinates. kCDXProp_2DPosition = 0x0200, // 0x0200 The 2D location (in the order of vertical and horizontal locations) of an object. (CDXPoint2D) kCDXProp_3DPosition, // 0x0201 The 3D location (in the order of X-, Y-, and Z-locations in right-handed coordinate system) of an object in CDX coordinate units. The precise meaning of this attribute varies depending on the type of object. (CDXPoint3D) kCDXProp_2DExtent, // 0x0202 The width and height of an object in CDX coordinate units. The precise meaning of this attribute varies depending on the type of object. (CDXPoint2D) kCDXProp_3DExtent, // 0x0203 The width, height, and depth of an object in CDX coordinate units (right-handed coordinate system). The precise meaning of this attribute varies depending on the type of object. (CDXPoint3D) kCDXProp_BoundingBox, // 0x0204 The smallest rectangle that encloses the graphical representation of the object. (CDXRectangle) kCDXProp_RotationAngle, // 0x0205 The angular orientation of an object in degrees * 65536. (INT32) kCDXProp_BoundsInParent, // 0x0206 The bounds of this object in the coordinate system of its parent (used for pages within tables). (CDXRectangle) kCDXProp_3DHead, // 0x0207 The 3D location (in the order of X-, Y-, and Z-locations in right-handed coordinate system) of the head of an object in CDX coordinate units. The precise meaning of this attribute varies depending on the type of object. (CDXPoint3D) kCDXProp_3DTail, // 0x0208 The 3D location (in the order of X-, Y-, and Z-locations in right-handed coordinate system) of the tail of an object in CDX coordinate units. The precise meaning of this attribute varies depending on the type of object. (CDXPoint3D) kCDXProp_TopLeft, // 0x0209 The location of the top-left corner of a quadrilateral object, possibly in a rotated or skewed frame. (CDXPoint2D) kCDXProp_TopRight, // 0x020A The location of the top-right corner of a quadrilateral object, possibly in a rotated or skewed frame. (CDXPoint2D) kCDXProp_BottomRight, // 0x020B The location of the bottom-right corner of a quadrilateral object, possibly in a rotated or skewed frame. (CDXPoint2D) kCDXProp_BottomLeft, // 0x020C The location of the bottom-left corner of a quadrilateral object, possibly in a rotated or skewed frame. (CDXPoint2D) // Colors. kCDXProp_ColorTable = 0x0300, // 0x0300 The color palette used throughout the document. (CDXColorTable) kCDXProp_ForegroundColor, // 0x0301 The foreground color of an object represented as the two-based index into the object's color table. (UINT16) kCDXProp_BackgroundColor, // 0x0302 The background color of an object represented as the two-based index into the object's color table. (INT16) // Atom properties. kCDXProp_Node_Type = 0x0400, // 0x0400 The type of a node object. (INT16) kCDXProp_Node_LabelDisplay, // 0x0401 The characteristics of node label display. (INT8) kCDXProp_Node_Element, // 0x0402 The atomic number of the atom representing this node. (INT16) kCDXProp_Atom_ElementList, // 0x0403 A list of atomic numbers. (CDXElementList) kCDXProp_Atom_Formula, // 0x0404 The composition of a node representing a fragment whose composition is known, but whose connectivity is not. For example, C<sub>4</sub>H<sub>9</sub> represents a mixture of the 4 butyl isomers. (CDXFormula) kCDXProp_Atom_Isotope = 0x0420, // 0x0420 The absolute isotopic mass of an atom (2 for deuterium, 14 for carbon-14). (INT16) kCDXProp_Atom_Charge, // 0x0421 The atomic charge of an atom. (INT8) kCDXProp_Atom_Radical, // 0x0422 The atomic radical attribute of an atom. (UINT8) kCDXProp_Atom_RestrictFreeSites, // 0x0423 Indicates that up to the specified number of additional substituents are permitted on this atom. (UINT8) kCDXProp_Atom_RestrictImplicitHydrogens,// 0x0424 Signifies that implicit hydrogens are not allowed on this atom. (CDXBooleanImplied) kCDXProp_Atom_RestrictRingBondCount, // 0x0425 The number of ring bonds attached to an atom. (INT8) kCDXProp_Atom_RestrictUnsaturatedBonds, // 0x0426 Indicates whether unsaturation should be present or absent. (INT8) kCDXProp_Atom_RestrictRxnChange, // 0x0427 If present, signifies that the reaction change of an atom must be as specified. (CDXBooleanImplied) kCDXProp_Atom_RestrictRxnStereo, // 0x0428 The change of stereochemistry of an atom during a reaction. (INT8) kCDXProp_Atom_AbnormalValence, // 0x0429 Signifies that an abnormal valence for an atom is permitted. (CDXBooleanImplied) kCDXProp_Unused3, // 0x042A kCDXProp_Atom_NumHydrogens, // 0x042B The number of (explicit) hydrogens in a labeled atom consisting of one heavy atom and (optionally) the symbol H (e.g., CH<sub>3</sub>). (UINT16) kCDXProp_Unused4, // 0x042C kCDXProp_Unused5, // 0x042D kCDXProp_Atom_HDot, // 0x042E Signifies the presence of an implicit hydrogen with stereochemistry specified equivalent to an explicit H atom with a wedged bond. (CDXBooleanImplied) kCDXProp_Atom_HDash, // 0x042F Signifies the presence of an implicit hydrogen with stereochemistry specified equivalent to an explicit H atom with a hashed bond. (CDXBooleanImplied) kCDXProp_Atom_Geometry, // 0x0430 The geometry of the bonds about this atom. (INT8) kCDXProp_Atom_BondOrdering, // 0x0431 An ordering of the bonds to this node, used for stereocenters, fragments, and named alternative groups with more than one attachment. (CDXObjectIDArray) kCDXProp_Node_Attachments, // 0x0432 For multicenter attachment nodes or variable attachment nodes, a list of IDs of the nodes which are multiply or variably attached to this node. (CDXObjectIDArrayWithCounts) kCDXProp_Atom_GenericNickname, // 0x0433 The name of the generic nickname. (CDXString) kCDXProp_Atom_AltGroupID, // 0x0434 The ID of the alternative group object that describes this node. (CDXObjectID) kCDXProp_Atom_RestrictSubstituentsUpTo, // 0x0435 Indicates that substitution is restricted to no more than the specified value. (UINT8) kCDXProp_Atom_RestrictSubstituentsExactly, // 0x0436 Indicates that exactly the specified number of substituents must be present. (UINT8) kCDXProp_Atom_CIPStereochemistry, // 0x0437 The node's absolute stereochemistry according to the Cahn-Ingold-Prelog system. (INT8) kCDXProp_Atom_Translation, // 0x0438 Provides for restrictions on whether a given node may match other more- or less-general nodes. (INT8) kCDXProp_Atom_AtomNumber, // 0x0439 Atom number, as text. (CDXString) kCDXProp_Atom_ShowQuery, // 0x043A Show the query indicator if non-zero. (CDXBoolean) kCDXProp_Atom_ShowStereo, // 0x043B Show the stereochemistry indicator if non-zero. (CDXBoolean) kCDXProp_Atom_ShowAtomNumber, // 0x043C Show the atom number if non-zero. (CDXBoolean) kCDXProp_Atom_LinkCountLow, // 0x043D Low end of repeat count for link nodes. (INT16) kCDXProp_Atom_LinkCountHigh, // 0x043E High end of repeat count for link nodes. (INT16) kCDXProp_Atom_IsotopicAbundance, // 0x043F Isotopic abundance of this atom's isotope. (INT8) kCDXProp_Atom_ExternalConnectionType, // 0x0440 Type of external connection, for atoms of type kCDXNodeType_ExternalConnectionPoint. (INT8) // Molecule properties. kCDXProp_Mole_Racemic = 0x0500, // 0x0500 Indicates that the molecule is a racemic mixture. (CDXBoolean) kCDXProp_Mole_Absolute, // 0x0501 Indicates that the molecule has known absolute configuration. (CDXBoolean) kCDXProp_Mole_Relative, // 0x0502 Indicates that the molecule has known relative stereochemistry, but unknown absolute configuration. (CDXBoolean) kCDXProp_Mole_Formula, // 0x0503 The molecular formula representation of a molecule object. (CDXFormula) kCDXProp_Mole_Weight, // 0x0504 The average molecular weight of a molecule object. (FLOAT64) kCDXProp_Frag_ConnectionOrder, // 0x0505 An ordered list of attachment points within a fragment. (CDXObjectIDArray) // Bond properties. kCDXProp_Bond_Order = 0x0600, // 0x0600 The order of a bond object. (INT16) kCDXProp_Bond_Display, // 0x0601 The display type of a bond object. (INT16) kCDXProp_Bond_Display2, // 0x0602 The display type for the second line of a double bond. (INT16) kCDXProp_Bond_DoublePosition, // 0x0603 The position of the second line of a double bond. (INT16) kCDXProp_Bond_Begin, // 0x0604 The ID of the CDX node object at the first end of a bond. (CDXObjectID) kCDXProp_Bond_End, // 0x0605 The ID of the CDX node object at the second end of a bond. (CDXObjectID) kCDXProp_Bond_RestrictTopology, // 0x0606 Indicates the desired topology of a bond in a query. (INT8) kCDXProp_Bond_RestrictRxnParticipation, // 0x0607 Specifies that a bond is affected by a reaction. (INT8) kCDXProp_Bond_BeginAttach, // 0x0608 Indicates where within the Bond_Begin node a bond is attached. (UINT8) kCDXProp_Bond_EndAttach, // 0x0609 Indicates where within the Bond_End node a bond is attached. (UINT8) kCDXProp_Bond_CIPStereochemistry, // 0x060A The bond's absolute stereochemistry according to the Cahn-Ingold-Prelog system. (INT8) kCDXProp_Bond_BondOrdering, // 0x060B Ordered list of attached bond IDs. (CDXObjectIDArray) kCDXProp_Bond_ShowQuery, // 0x060C Show the query indicator if non-zero. (CDXBoolean) kCDXProp_Bond_ShowStereo, // 0x060D Show the stereochemistry indicator if non-zero. (CDXBoolean) kCDXProp_Bond_CrossingBonds, // 0x060E Unordered list of IDs of bonds that cross this one (either above or below). (CDXObjectIDArray) kCDXProp_Bond_ShowRxn, // 0x060F Show the reaction-change indicator if non-zero. (CDXBoolean) // Text properties. kCDXProp_Text = 0x0700, // 0x0700 The text of a text object. (CDXString) kCDXProp_Justification, // 0x0701 The horizontal justification of a text object. (INT8) kCDXProp_LineHeight, // 0x0702 The line height of a text object. (UINT16) kCDXProp_WordWrapWidth, // 0x0703 The word-wrap width of a text object. (INT16) kCDXProp_LineStarts, // 0x0704 The number of lines of a text object followed by that many values indicating the zero-based text position of each line start. (INT16ListWithCounts) kCDXProp_LabelAlignment, // 0x0705 The alignment of the text with respect to the node position. (INT8) kCDXProp_LabelLineHeight, // 0x0706 Text line height for atom labels (INT16) kCDXProp_CaptionLineHeight, // 0x0707 Text line height for non-atomlabel text objects (INT16) kCDXProp_InterpretChemically, // 0x0708 Signifies whether to the text label should be interpreted chemically (if possible). (CDXBooleanImplied) // Document properties. kCDXProp_MacPrintInfo = 0x0800, // 0x0800 The 120 byte Macintosh TPrint data associated with the CDX document object. Refer to Macintosh Toolbox manual for detailed description. (Unformatted) kCDXProp_WinPrintInfo, // 0x0801 The Windows DEVMODE structure associated with the CDX document object. (Unformatted) kCDXProp_PrintMargins, // 0x0802 The outer margins of the Document. (CDXRectangle) kCDXProp_ChainAngle, // 0x0803 The default chain angle setting in degrees * 65536. (INT32) kCDXProp_BondSpacing, // 0x0804 The spacing between segments of a multiple bond, measured relative to bond length. (INT16) kCDXProp_BondLength, // 0x0805 The default bond length. (CDXCoordinate) kCDXProp_BoldWidth, // 0x0806 The default bold bond width. (CDXCoordinate) kCDXProp_LineWidth, // 0x0807 The default line width. (CDXCoordinate) kCDXProp_MarginWidth, // 0x0808 The default amount of space surrounding atom labels. (CDXCoordinate) kCDXProp_HashSpacing, // 0x0809 The default spacing between hashed lines used in wedged hashed bonds. (CDXCoordinate) kCDXProp_LabelStyle, // 0x080A The default style for atom labels. (CDXFontStyle) kCDXProp_CaptionStyle, // 0x080B The default style for non-atomlabel text objects. (CDXFontStyle) kCDXProp_CaptionJustification, // 0x080C The horizontal justification of a caption (non-atomlabel text object) (INT8) kCDXProp_FractionalWidths, // 0x080D Signifies whether to use fractional width information when drawing text. (CDXBooleanImplied) kCDXProp_Magnification, // 0x080E The view magnification factor (INT16) kCDXProp_WidthPages, // 0x080F The width of the document in pages. (INT16) kCDXProp_HeightPages, // 0x0810 The height of the document in pages. (INT16) kCDXProp_DrawingSpaceType, // 0x0811 The type of drawing space used for this document. (INT8) kCDXProp_Width, // 0x0812 The width of an object in CDX coordinate units, possibly in a rotated or skewed frame. (CDXCoordinate) kCDXProp_Height, // 0x0813 The height of an object in CDX coordinate units, possibly in a rotated or skewed frame. (CDXCoordinate) kCDXProp_PageOverlap, // 0x0814 The amount of overlap of pages when a poster is tiled. (CDXCoordinate) kCDXProp_Header, // 0x0815 The text of the header. (CDXString) kCDXProp_HeaderPosition, // 0x0816 The vertical offset of the header baseline from the top of the page. (CDXCoordinate) kCDXProp_Footer, // 0x0817 The text of the footer. (CDXString) kCDXProp_FooterPosition, // 0x0818 The vertical offset of the footer baseline from the bottom of the page. (CDXCoordinate) kCDXProp_PrintTrimMarks, // 0x0819 If present, trim marks are to printed in the margins. (CDXBooleanImplied) kCDXProp_LabelStyleFont, // 0x081A The default font family for atom labels. (INT16) kCDXProp_CaptionStyleFont, // 0x081B The default font style for captions (non-atom-label text objects). (INT16) kCDXProp_LabelStyleSize, // 0x081C The default font size for atom labels. (INT16) kCDXProp_CaptionStyleSize, // 0x081D The default font size for captions (non-atom-label text objects). (INT16) kCDXProp_LabelStyleFace, // 0x081E The default font style for atom labels. (INT16) kCDXProp_CaptionStyleFace, // 0x081F The default font face for captions (non-atom-label text objects). (INT16) kCDXProp_LabelStyleColor, // 0x0820 The default color for atom labels (INT16) kCDXProp_CaptionStyleColor, // 0x0821 The default color for captions (non-atom-label text objects). (INT16) kCDXProp_BondSpacingAbs, // 0x0822 The absolute distance between segments of a multiple bond. (CDXCoordinate) kCDXProp_LabelJustification, // 0x0823 The default justification for atom labels. (INT8) kCDXProp_FixInplaceExtent, // 0x0824 Defines a size for OLE In-Place editing. (CDXPoint2D) kCDXProp_Side, // 0x0825 A specific side of an object (rectangle). (INT16) kCDXProp_FixInplaceGap, // 0x0826 Defines a padding for OLE In-Place editing. (CDXPoint2D) // Window properties. kCDXProp_Window_IsZoomed = 0x0900, // 0x0900 Signifies whether the main viewing window is zoomed (maximized). (CDXBooleanImplied) kCDXProp_Window_Position, // 0x0901 The top-left position of the main viewing window. (CDXPoint2D) kCDXProp_Window_Size, // 0x0902 Height and width of the document window. (CDXPoint2D) // Graphic object properties. kCDXProp_Graphic_Type = 0x0A00, // 0x0A00 The type of graphical object. (INT16) kCDXProp_Line_Type, // 0x0A01 The type of a line object. (INT16) kCDXProp_Arrow_Type, // 0x0A02 The type of arrow object, which represents line, arrow, arc, rectangle, or orbital. (INT16) kCDXProp_Rectangle_Type, // 0x0A03 The type of a rectangle object. (INT16) kCDXProp_Oval_Type, // 0x0A04 The type of an arrow object that represents a circle or ellipse. (INT16) kCDXProp_Orbital_Type, // 0x0A05 The type of orbital object. (INT16) kCDXProp_Bracket_Type, // 0x0A06 The type of symbol object. (INT16) kCDXProp_Symbol_Type, // 0x0A07 The type of symbol object. (INT16) kCDXProp_Curve_Type, // 0x0A08 The type of curve object. (INT16) kCDXProp_Arrow_HeadSize = 0x0A20, // 0x0A20 The size of the arrow's head. (INT16) kCDXProp_Arc_AngularSize, // 0x0A21 The size of an arc (in degrees * 10, so 90 degrees = 900). (INT16) kCDXProp_Bracket_LipSize, // 0x0A22 The size of a bracket. (INT16) kCDXProp_Curve_Points, // 0x0A23 The Bézier curve's control point locations. (CDXCurvePoints) kCDXProp_Bracket_Usage, // 0x0A24 The syntactical chemical meaning of the bracket (SRU, mer, mon, xlink, etc). (INT8) kCDXProp_Polymer_RepeatPattern, // 0x0A25 The head-to-tail connectivity of objects contained within the bracket. (INT8) kCDXProp_Polymer_FlipType, // 0x0A26 The flip state of objects contained within the bracket. (INT8) kCDXProp_BracketedObjects, // 0x0A27 The set of objects contained in a BracketedGroup. (CDXObjectIDArray) kCDXProp_Bracket_RepeatCount, // 0x0A28 The number of times a multiple-group BracketedGroup is repeated. (INT16) kCDXProp_Bracket_ComponentOrder, // 0x0A29 The component order associated with a BracketedGroup. (INT16) kCDXProp_Bracket_SRULabel, // 0x0A2A The label associated with a BracketedGroup that represents an SRU. (CDXString) kCDXProp_Bracket_GraphicID, // 0x0A2B The ID of a graphical object (bracket, brace, or parenthesis) associated with a Bracket Attachment. (CDXObjectID) kCDXProp_Bracket_BondID, // 0x0A2C The ID of a bond that crosses a Bracket Attachment. (CDXObjectID) kCDXProp_Bracket_InnerAtomID, // 0x0A2D The ID of the node located within the Bracketed Group and attached to a bond that crosses a Bracket Attachment. (CDXObjectID) kCDXProp_Curve_Points3D, // 0x0A2E The Bézier curve's control point locations. (CDXCurvePoints3D) // Embedded pictures. kCDXProp_Picture_Edition = 0x0A60, // 0x0A60 The section information (SectionHandle) of the Macintosh Publish & Subscribe edition embedded in the CDX picture object. (Unformatted) kCDXProp_Picture_EditionAlias, // 0x0A61 The alias information of the Macintosh Publish & Subscribe edition embedded in the CDX picture object. (Unformatted) kCDXProp_MacPICT, // 0x0A62 A Macintosh PICT data object. (Unformatted) kCDXProp_WindowsMetafile, // 0x0A63 A Microsoft Windows Metafile object. (Unformatted) kCDXProp_OLEObject, // 0x0A64 An OLE object. (Unformatted) kCDXProp_EnhancedMetafile, // 0x0A65 A Microsoft Windows Enhanced Metafile object. (Unformatted) // Spectrum properties kCDXProp_Spectrum_XSpacing = 0x0A80, // 0x0A80 The spacing in logical units (ppm, Hz, wavenumbers) between points along the X-axis of an evenly-spaced grid. (FLOAT64) kCDXProp_Spectrum_XLow, // 0x0A81 The first data point for the X-axis of an evenly-spaced grid. (FLOAT64) kCDXProp_Spectrum_XType, // 0x0A82 The type of units the X-axis represents. (INT16) kCDXProp_Spectrum_YType, // 0x0A83 The type of units the Y-axis represents. (INT16) kCDXProp_Spectrum_XAxisLabel, // 0x0A84 A label for the X-axis. (CDXString) kCDXProp_Spectrum_YAxisLabel, // 0x0A85 A label for the Y-axis. (CDXString) kCDXProp_Spectrum_DataPoint, // 0x0A86 The Y-axis values for the spectrum. It is an array of double values corresponding to X-axis values. (FLOAT64) kCDXProp_Spectrum_Class, // 0x0A87 The type of spectrum represented. (INT16) kCDXProp_Spectrum_YLow, // 0x0A88 Y value to be used to offset data when storing XML. (FLOAT64) kCDXProp_Spectrum_YScale, // 0x0A89 Y scaling used to scale data when storing XML. (FLOAT64) // TLC properties kCDXProp_TLC_OriginFraction = 0x0AA0, // 0x0AA0 The distance of the origin line from the bottom of a TLC Plate, as a fraction of the total height of the plate. (FLOAT64) kCDXProp_TLC_SolventFrontFraction, // 0x0AA1 The distance of the solvent front from the top of a TLC Plate, as a fraction of the total height of the plate. (FLOAT64) kCDXProp_TLC_ShowOrigin, // 0x0AA2 Show the origin line near the base of the TLC Plate if non-zero. (CDXBoolean) kCDXProp_TLC_ShowSolventFront, // 0x0AA3 Show the solvent front line near the top of the TLC Plate if non-zero. (CDXBoolean) kCDXProp_TLC_ShowBorders, // 0x0AA4 Show borders around the edges of the TLC Plate if non-zero. (CDXBoolean) kCDXProp_TLC_ShowSideTicks, // 0x0AA5 Show tickmarks up the side of the TLC Plate if non-zero. (CDXBoolean) kCDXProp_TLC_Rf = 0x0AB0, // 0x0AB0 The Retention Factor of an individual spot. (FLOAT64) kCDXProp_TLC_Tail, // 0x0AB1 The length of the "tail" of an individual spot. (CDXCoordinate) kCDXProp_TLC_ShowRf, // 0x0AB2 Show the spot's Retention Fraction (Rf) value if non-zero. (CDXBoolean) // Alternate Group properties kCDXProp_NamedAlternativeGroup_TextFrame = 0x0B00, // 0x0B00 The bounding box of upper portion of the Named Alternative Group, containing the name of the group. (CDXRectangle) kCDXProp_NamedAlternativeGroup_GroupFrame, // 0x0B01 The bounding box of the lower portion of the Named Alternative Group, containing the definition of the group. (CDXRectangle) kCDXProp_NamedAlternativeGroup_Valence, // 0x0B02 The number of attachment points in each alternative in a named alternative group. (INT16) // Geometry and Constraint properties kCDXProp_GeometricFeature = 0x0B80, // 0x0B80 The type of the geometrical feature (point, line, plane, etc.). (INT8) kCDXProp_RelationValue, // 0x0B81 The numeric relationship (if any) among the basis objects used to define this object. (INT8) kCDXProp_BasisObjects, // 0x0B82 An ordered list of objects used to define this object. (CDXObjectIDArray) kCDXProp_ConstraintType, // 0x0B83 The constraint type (distance or angle). (INT8) kCDXProp_ConstraintMin, // 0x0B84 The minimum value of the constraint (FLOAT64) kCDXProp_ConstraintMax, // 0x0B85 The maximum value of the constraint (FLOAT64) kCDXProp_IgnoreUnconnectedAtoms, // 0x0B86 Signifies whether unconnected atoms should be ignored within the exclusion sphere. (CDXBooleanImplied) kCDXProp_DihedralIsChiral, // 0x0B87 Signifies whether a dihedral is signed or unsigned. (CDXBooleanImplied) kCDXProp_PointIsDirected, // 0x0B88 For a point based on a normal, signifies whether it is in a specific direction relative to the reference point. (CDXBooleanImplied) // Reaction properties kCDXProp_ReactionStep_Atom_Map = 0x0C00,// 0x0C00 Represents pairs of mapped atom IDs; each pair is a reactant atom mapped to to a product atom. (CDXObjectIDArray) kCDXProp_ReactionStep_Reactants, // 0x0C01 An order list of reactants present in the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_Products, // 0x0C02 An order list of products present in the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_Plusses, // 0x0C03 An ordered list of pluses used to separate components of the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_Arrows, // 0x0C04 An ordered list of arrows used to separate components of the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_ObjectsAboveArrow,// 0x0C05 An order list of objects above the arrow in the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_ObjectsBelowArrow,// 0x0C06 An order list of objects below the arrow in the Reaction Step. (CDXObjectIDArray) kCDXProp_ReactionStep_Atom_Map_Manual, // 0x0C07 Represents pairs of mapped atom IDs; each pair is a reactant atom mapped to to a product atom. (CDXObjectIDArray) kCDXProp_ReactionStep_Atom_Map_Auto, // 0x0C08 Represents pairs of mapped atom IDs; each pair is a reactant atom mapped to to a product atom. (CDXObjectIDArray) // CDObjectTag properties kCDXProp_ObjectTag_Type = 0x0D00, // 0x0D00 The tag's data type. (INT16) kCDXProp_Unused6, // 0x0D01 obsolete (obsolete) kCDXProp_Unused7, // 0x0D02 obsolete (obsolete) kCDXProp_ObjectTag_Tracking, // 0x0D03 The tag will participate in tracking if non-zero. (CDXBoolean) kCDXProp_ObjectTag_Persistent, // 0x0D04 The tag will be resaved to a CDX file if non-zero. (CDXBoolean) kCDXProp_ObjectTag_Value, // 0x0D05 The value is a INT32, FLOAT64 or unformatted string depending on the value of ObjectTag_Type. (varies) kCDXProp_Positioning, // 0x0D06 How the indicator should be positioned with respect to its containing object. (INT8) kCDXProp_PositioningAngle, // 0x0D07 Angular positioning, in radians * 65536. (INT32) kCDXProp_PositioningOffset, // 0x0D08 Offset positioning. (CDXPoint2D) // CDSequence properties kCDXProp_Sequence_Identifier = 0x0E00, // 0x0E00 A unique (but otherwise random) identifier for a given Sequence object. (CDXString) // CDCrossReference properties kCDXProp_CrossReference_Container = 0x0F00, // 0x0F00 An external object containing (as an embedded object) the document containing the Sequence object being referenced. (CDXString) kCDXProp_CrossReference_Document, // 0x0F01 An external document containing the Sequence object being referenced. (CDXString) kCDXProp_CrossReference_Identifier, // 0x0F02 A unique (but otherwise random) identifier for a given Cross-Reference object. (CDXString) kCDXProp_CrossReference_Sequence, // 0x0F03 A value matching the SequenceIdentifier of the Sequence object to be referenced. (CDXString) // Miscellaneous properties. kCDXProp_Template_PaneHeight = 0x1000, // 0x1000 The height of the viewing window of a template grid. (CDXCoordinate) kCDXProp_Template_NumRows, // 0x1001 The number of rows of the CDX TemplateGrid object. (INT16) kCDXProp_Template_NumColumns, // 0x1002 The number of columns of the CDX TemplateGrid object. (INT16) kCDXProp_Group_Integral = 0x1100, // 0x1100 The group is considered to be integral (non-subdivisible) if non-zero. (CDXBoolean) kCDXProp_SplitterPositions = 0x1ff0, // 0x1FF0 An array of vertical positions that subdivide a page into regions. (CDXObjectIDArray) kCDXProp_PageDefinition, // 0x1FF1 An array of vertical positions that subdivide a page into regions. (CDXObjectIDArray) // User defined properties // First 1024 tags are reserved for temporary tags used only during the runtime. kCDXUser_TemporaryBegin = kCDXTag_UserDefined, kCDXUser_TemporaryEnd = kCDXTag_UserDefined + 0x0400, // Objects. kCDXObj_Document = kCDXTag_Object, // 0x8000 kCDXObj_Page, // 0x8001 kCDXObj_Group, // 0x8002 kCDXObj_Fragment, // 0x8003 kCDXObj_Node, // 0x8004 kCDXObj_Bond, // 0x8005 kCDXObj_Text, // 0x8006 kCDXObj_Graphic, // 0x8007 kCDXObj_Curve, // 0x8008 kCDXObj_EmbeddedObject, // 0x8009 kCDXObj_NamedAlternativeGroup, // 0x800a kCDXObj_TemplateGrid, // 0x800b kCDXObj_RegistryNumber, // 0x800c kCDXObj_ReactionScheme, // 0x800d kCDXObj_ReactionStep, // 0x800e kCDXObj_ObjectDefinition, // 0x800f kCDXObj_Spectrum, // 0x8010 kCDXObj_ObjectTag, // 0x8011 kCDXObj_OleClientItem, // 0x8012 // obsolete kCDXObj_Sequence, // 0x8013 kCDXObj_CrossReference, // 0x8014 kCDXObj_Splitter, // 0x8015 kCDXObj_Table, // 0x8016 kCDXObj_BracketedGroup, // 0x8017 kCDXObj_BracketAttachment, // 0x8018 kCDXObj_CrossingBond, // 0x8019 kCDXObj_Border, // 0x8020 kCDXObj_Geometry, // 0x8021 kCDXObj_Constraint, // 0x8022 kCDXObj_TLCPlate, // 0x8023 kCDXObj_TLCLane, // 0x8024 kCDXObj_TLCSpot, // 0x8025 // Add new objects here kCDXObj_UnknownObject = 0x8FFF }; enum CDXNodeType { kCDXNodeType_Unspecified, kCDXNodeType_Element, kCDXNodeType_ElementList, kCDXNodeType_ElementListNickname, kCDXNodeType_Nickname, kCDXNodeType_Fragment, kCDXNodeType_Formula, kCDXNodeType_GenericNickname, kCDXNodeType_AnonymousAlternativeGroup, kCDXNodeType_NamedAlternativeGroup, kCDXNodeType_MultiAttachment, kCDXNodeType_VariableAttachment, kCDXNodeType_ExternalConnectionPoint, kCDXNodeType_LinkNode }; enum CDXLabelDisplay { kCDXLabelDisplay_Auto, kCDXLabelDisplay_Left, kCDXLabelDisplay_Center, kCDXLabelDisplay_Right, kCDXLabelDisplay_Above, kCDXLabelDisplay_Below, kCDXLabelDisplay_BestInitial }; enum CDXRadical { // Same as MDL codes kCDXRadical_None = 0, kCDXRadical_Singlet = 1, // diradical singlet (two dots) kCDXRadical_Doublet = 2, // monoradical (one dot) kCDXRadical_Triplet = 3 // diradical triplet (two dots) }; enum CDXIsotope { kCDXIsotope_Natural = 0 }; enum CDXRingBondCount { kCDXRingBondCount_Unspecified = -1, kCDXRingBondCount_NoRingBonds = 0, kCDXRingBondCount_AsDrawn = 1, kCDXRingBondCount_SimpleRing = 2, kCDXRingBondCount_Fusion = 3, kCDXRingBondCount_SpiroOrHigher = 4 }; enum CDXUnsaturation { kCDXUnsaturation_Unspecified = 0, kCDXUnsaturation_MustBeAbsent = 1, kCDXUnsaturation_MustBePresent = 2, kCDXUnsaturationLastEnum }; enum CDXReactionStereo { kCDXReactionStereo_Unspecified = 0, kCDXReactionStereo_Inversion = 1, kCDXReactionStereo_Retention = 2 }; enum CDXTranslation { kCDXTranslation_Equal = 0, kCDXTranslation_Broad = 1, kCDXTranslation_Narrow = 2, kCDXTranslation_Any = 3 }; enum CDXAbundance { kCDXAbundance_Unspecified = 0, kCDXAbundance_Any = 1, kCDXAbundance_Natural = 2, kCDXAbundance_Enriched = 3, kCDXAbundance_Deficient = 4, kCDXAbundance_Nonnatural = 5 }; enum CDXExternalConnectionType { kCDXExternalConnection_Unspecified = 0, kCDXExternalConnection_Diamond = 1, kCDXExternalConnection_Star = 2, kCDXExternalConnection_PolymerBead = 3, kCDXExternalConnection_Wavy = 4 }; enum CDXAtomGeometry { kCDXAtomGeometry_Unknown = 0, kCDXAtomGeometry_1Ligand = 1, kCDXAtomGeometry_Linear = 2, kCDXAtomGeometry_Bent = 3, kCDXAtomGeometry_TrigonalPlanar = 4, kCDXAtomGeometry_TrigonalPyramidal = 5, kCDXAtomGeometry_SquarePlanar = 6, kCDXAtomGeometry_Tetrahedral = 7, kCDXAtomGeometry_TrigonalBipyramidal = 8, kCDXAtomGeometry_SquarePyramidal = 9, kCDXAtomGeometry_5Ligand = 10, kCDXAtomGeometry_Octahedral = 11, kCDXAtomGeometry_6Ligand = 12, kCDXAtomGeometry_7Ligand = 13, kCDXAtomGeometry_8Ligand = 14, kCDXAtomGeometry_9Ligand = 15, kCDXAtomGeometry_10Ligand = 16 }; enum CDXBondOrder { kCDXBondOrder_Single = 0x0001, kCDXBondOrder_Double = 0x0002, kCDXBondOrder_Triple = 0x0004, kCDXBondOrder_Quadruple = 0x0008, kCDXBondOrder_Quintuple = 0x0010, kCDXBondOrder_Sextuple = 0x0020, kCDXBondOrder_Half = 0x0040, kCDXBondOrder_OneHalf = 0x0080, kCDXBondOrder_TwoHalf = 0x0100, kCDXBondOrder_ThreeHalf = 0x0200, kCDXBondOrder_FourHalf = 0x0400, kCDXBondOrder_FiveHalf = 0x0800, kCDXBondOrder_Dative = 0x1000, kCDXBondOrder_Ionic = 0x2000, kCDXBondOrder_Hydrogen = 0x4000, kCDXBondOrder_ThreeCenter = 0x8000, kCDXBondOrder_SingleOrDouble = kCDXBondOrder_Single | kCDXBondOrder_Double, kCDXBondOrder_SingleOrAromatic = kCDXBondOrder_Single | kCDXBondOrder_OneHalf, kCDXBondOrder_DoubleOrAromatic = kCDXBondOrder_Double | kCDXBondOrder_OneHalf, kCDXBondOrder_Any = -1 }; // Permit combination of CDXBondOrder values inline CDXBondOrder &operator |= (CDXBondOrder &lhs, const CDXBondOrder &rhs) { return lhs = CDXBondOrder(UINT32(lhs) | UINT32(rhs)); } enum CDXBondDisplay { kCDXBondDisplay_Solid = 0, kCDXBondDisplay_Dash = 1, kCDXBondDisplay_Hash = 2, kCDXBondDisplay_WedgedHashBegin = 3, kCDXBondDisplay_WedgedHashEnd = 4, kCDXBondDisplay_Bold = 5, kCDXBondDisplay_WedgeBegin = 6, kCDXBondDisplay_WedgeEnd = 7, kCDXBondDisplay_Wavy = 8, kCDXBondDisplay_HollowWedgeBegin = 9, kCDXBondDisplay_HollowWedgeEnd = 10, kCDXBondDisplay_WavyWedgeBegin = 11, kCDXBondDisplay_WavyWedgeEnd = 12, kCDXBondDisplay_Dot = 13, kCDXBondDisplay_DashDot = 14 }; enum CDXBondDoublePosition { kCDXBondDoublePosition_AutoCenter = 0x0000, kCDXBondDoublePosition_AutoRight = 0x0001, kCDXBondDoublePosition_AutoLeft = 0x0002, kCDXBondDoublePosition_UserCenter = 0x0100, kCDXBondDoublePosition_UserRight = 0x0101, kCDXBondDoublePosition_UserLeft = 0x0102 }; enum CDXBondTopology { kCDXBondTopology_Unspecified = 0, kCDXBondTopology_Ring = 1, kCDXBondTopology_Chain = 2, kCDXBondTopology_RingOrChain = 3 }; enum CDXBondReactionParticipation { kCDXBondReactionParticipation_Unspecified = 0, kCDXBondReactionParticipation_ReactionCenter = 1, kCDXBondReactionParticipation_MakeOrBreak = 2, kCDXBondReactionParticipation_ChangeType = 3, kCDXBondReactionParticipation_MakeAndChange = 4, kCDXBondReactionParticipation_NotReactionCenter = 5, kCDXBondReactionParticipation_NoChange = 6, kCDXBondReactionParticipation_Unmapped = 7 }; enum CDXTextJustification { kCDXTextJustification_Right = -1, kCDXTextJustification_Left, kCDXTextJustification_Center, kCDXTextJustification_Full, kCDXTextJustification_Above, kCDXTextJustification_Below, kCDXTextJustification_Auto, kCDXTextJustification_BestInitial }; #define kCDXTagType_Unknown "unknown" #define kCDXTagType_Query "query" #define kCDXTagType_Rxn "reaction" #define kCDXTagType_Stereo "stereo" #define kCDXTagType_Number "number" #define kCDXTagType_Heading "heading" #define kCDXTagType_IDTerm "idterm" #define kCDXTagType_BracketUsage "bracketusage" #define kCDXTagType_PolymerRepeat "polymerrepeat" #define kCDXTagType_PolymerFlip "polymerflip" #define kCDXTagType_Deviation "deviation" #define kCDXTagType_Distance "distance" #define kCDXTagType_Angle "angle" #define kCDXTagType_Rf "rf" enum CDXPositioningType { kCDXPositioningType_Auto = 0, kCDXPositioningType_Angle, kCDXPositioningType_Offset, kCDXPositioningType_Absolute }; enum CDXPageDefinition { kCDXPageDefinition_Undefined = 0, kCDXPageDefinition_Center, kCDXPageDefinition_TL4, kCDXPageDefinition_IDTerm, kCDXPageDefinition_FlushLeft, kCDXPageDefinition_FlushRight, kCDXPageDefinition_Reaction1, kCDXPageDefinition_Reaction2, kCDXPageDefinition_MulticolumnTL4, kCDXPageDefinition_MulticolumnNonTL4, kCDXPageDefinition_UserDefined }; #define kCDXLineHeight_Variable 0 #define kCDXLineHeight_Automatic 1 enum CDXGraphicType { kCDXGraphicType_Undefined = 0, kCDXGraphicType_Line, kCDXGraphicType_Arc, kCDXGraphicType_Rectangle, kCDXGraphicType_Oval, kCDXGraphicType_Orbital, kCDXGraphicType_Bracket, kCDXGraphicType_Symbol }; enum CDXBracketType { kCDXBracketType_RoundPair, kCDXBracketType_SquarePair, kCDXBracketType_CurlyPair, kCDXBracketType_Square, kCDXBracketType_Curly, kCDXBracketType_Round }; enum CDXRectangleType { kCDXRectangleType_Plain = 0x0000, kCDXRectangleType_RoundEdge = 0x0001, kCDXRectangleType_Shadow = 0x0002, kCDXRectangleType_Shaded = 0x0004, kCDXRectangleType_Filled = 0x0008, kCDXRectangleType_Dashed = 0x0010, kCDXRectangleType_Bold = 0x0020 }; enum CDXOvalType { kCDXOvalType_Circle = 0x0001, kCDXOvalType_Shaded = 0x0002, kCDXOvalType_Filled = 0x0004, kCDXOvalType_Dashed = 0x0008, kCDXOvalType_Bold = 0x0010, kCDXOvalType_Shadowed = 0x0020 }; enum CDXSymbolType { kCDXSymbolType_LonePair, kCDXSymbolType_Electron, kCDXSymbolType_RadicalCation, kCDXSymbolType_RadicalAnion, kCDXSymbolType_CirclePlus, kCDXSymbolType_CircleMinus, kCDXSymbolType_Dagger, kCDXSymbolType_DoubleDagger, kCDXSymbolType_Plus, kCDXSymbolType_Minus, kCDXSymbolType_Racemic, kCDXSymbolType_Absolute, kCDXSymbolType_Relative }; enum CDXLineType { kCDXLineType_Solid = 0x0000, kCDXLineType_Dashed = 0x0001, kCDXLineType_Bold = 0x0002, kCDXLineType_Wavy = 0x0004 }; enum CDXArrowType { kCDXArrowType_NoHead = 0, kCDXArrowType_HalfHead = 1, kCDXArrowType_FullHead = 2, kCDXArrowType_Resonance = 4, kCDXArrowType_Equilibrium = 8, kCDXArrowType_Hollow = 16, kCDXArrowType_RetroSynthetic = 32 }; enum CDXOrbitalType { kCDXOrbitalType_s, // s orbital kCDXOrbitalType_oval, // Oval-shaped sigma or pi orbital kCDXOrbitalType_lobe, // One lobe of a p orbital kCDXOrbitalType_p, // Complete p orbital kCDXOrbitalType_hybridPlus, // hydrid orbital kCDXOrbitalType_hybridMinus, // hydrid orbital (opposite shading) kCDXOrbitalType_dz2Plus, // dz2 orbital kCDXOrbitalType_dz2Minus, // dz2 orbital (opposite shading) kCDXOrbitalType_dxy, // dxy orbital kCDXOrbitalType_sShaded = 0x0100, // shaded s orbital kCDXOrbitalType_ovalShaded, // shaded Oval-shaped sigma or pi orbital kCDXOrbitalType_lobeShaded, // shaded single lobe of a p orbital kCDXOrbitalType_pShaded, // shaded Complete p orbital kCDXOrbitalType_sFilled = 0x0200, // filled s orbital kCDXOrbitalType_ovalFilled, // filled Oval-shaped sigma or pi orbital kCDXOrbitalType_lobeFilled, // filled single lobe of a p orbital kCDXOrbitalType_pFilled, // filled Complete p orbital kCDXOrbitalType_hybridPlusFilled, // filled hydrid orbital kCDXOrbitalType_hybridMinusFilled, // filled hydrid orbital (opposite shading) kCDXOrbitalType_dz2PlusFilled, // filled dz2 orbital kCDXOrbitalType_dz2MinusFilled, // filled dz2 orbital (opposite shading) kCDXOrbitalType_dxyFilled // filled dxy orbital }; enum CDXBracketUsage { kCDXBracketUsage_Unspecified = 0, kCDXBracketUsage_Anypolymer = 18, kCDXBracketUsage_Component = 13, kCDXBracketUsage_Copolymer = 6, kCDXBracketUsage_CopolymerAlternating = 7, kCDXBracketUsage_CopolymerBlock = 9, kCDXBracketUsage_CopolymerRandom = 8, kCDXBracketUsage_Crosslink = 10, kCDXBracketUsage_Generic = 17, kCDXBracketUsage_Graft = 11, kCDXBracketUsage_Mer = 5, kCDXBracketUsage_MixtureOrdered = 15, kCDXBracketUsage_MixtureUnordered = 14, kCDXBracketUsage_Modification = 12, kCDXBracketUsage_Monomer = 4, kCDXBracketUsage_MultipleGroup = 16, kCDXBracketUsage_SRU = 3, kCDXBracketUsage_Unused1 = 1, kCDXBracketUsage_Unused2 = 2 }; enum CDXPolymerRepeatPattern { kCDXPolymerRepeatPattern_HeadToTail = 0, kCDXPolymerRepeatPattern_HeadToHead, kCDXPolymerRepeatPattern_EitherUnknown }; enum CDXPolymerFlipType { kCDXPolymerFlipType_Unspecified = 0, kCDXPolymerFlipType_NoFlip, kCDXPolymerFlipType_Flip }; enum CDXSpectrumYType { kCDXSpectrumYType_Unknown, kCDXSpectrumYType_Absorbance, kCDXSpectrumYType_Transmittance, kCDXSpectrumYType_PercentTransmittance, kCDXSpectrumYType_Other, kCDXSpectrumYType_ArbitraryUnits }; enum CDXSpectrumXType { kCDXSpectrumXType_Unknown, kCDXSpectrumXType_Wavenumbers, kCDXSpectrumXType_Microns, kCDXSpectrumXType_Hertz, kCDXSpectrumXType_MassUnits, kCDXSpectrumXType_PartsPerMillion, kCDXSpectrumXType_Other }; enum CDXSpectrumClass { kCDXSpectrumClass_Unknown, kCDXSpectrumClass_Chromatogram, kCDXSpectrumClass_Infrared, kCDXSpectrumClass_UVVis, kCDXSpectrumClass_XRayDiffraction, kCDXSpectrumClass_MassSpectrum, kCDXSpectrumClass_NMR, kCDXSpectrumClass_Raman, kCDXSpectrumClass_Fluorescence, kCDXSpectrumClass_Atomic }; enum CDXDrawingSpaceType { kCDXDrawingSpace_Pages, kCDXDrawingSpace_Poster }; enum CDXAtomCIPType { kCDXCIPAtom_Undetermined = 0, kCDXCIPAtom_None, kCDXCIPAtom_R, kCDXCIPAtom_S, kCDXCIPAtom_r, kCDXCIPAtom_s, kCDXCIPAtom_Unspecified // No hash/wedge, but if there were one, it would have stereochemistry. }; enum CDXBondCIPType { kCDXCIPBond_Undetermined = 0, kCDXCIPBond_None, kCDXCIPBond_E, kCDXCIPBond_Z }; enum CDXObjectTagType { kCDXObjectTagType_Undefined = 0, kCDXObjectTagType_Double, kCDXObjectTagType_Long, kCDXObjectTagType_String }; enum CDXSideType { kCDXSideType_Undefined = 0, kCDXSideType_Top, kCDXSideType_Left, kCDXSideType_Bottom, kCDXSideType_Right }; enum CDXGeometricFeature { kCDXGeometricFeature_Undefined = 0, kCDXGeometricFeature_PointFromPointPointDistance, kCDXGeometricFeature_PointFromPointPointPercentage, kCDXGeometricFeature_PointFromPointNormalDistance, kCDXGeometricFeature_LineFromPoints, kCDXGeometricFeature_PlaneFromPoints, kCDXGeometricFeature_PlaneFromPointLine, kCDXGeometricFeature_CentroidFromPoints, kCDXGeometricFeature_NormalFromPointPlane }; enum CDXConstraintType { kCDXConstraintType_Undefined = 0, kCDXConstraintType_Distance, kCDXConstraintType_Angle, kCDXConstraintType_ExclusionSphere }; enum CDXCharSet { kCDXCharSetUnknown = 0, kCDXCharSetEBCDICOEM = 37, kCDXCharSetMSDOSUS = 437, kCDXCharSetEBCDIC500V1 = 500, kCDXCharSetArabicASMO708 = 708, kCDXCharSetArabicASMO449P, kCDXCharSetArabicTransparent, kCDXCharSetArabicTransparentASMO = 720, kCDXCharSetGreek437G = 737, kCDXCharSetBalticOEM = 775, kCDXCharSetMSDOSLatin1 = 850, kCDXCharSetMSDOSLatin2 = 852, kCDXCharSetIBMCyrillic = 855, kCDXCharSetIBMTurkish = 857, kCDXCharSetMSDOSPortuguese = 860, kCDXCharSetMSDOSIcelandic, kCDXCharSetHebrewOEM, kCDXCharSetMSDOSCanadianFrench, kCDXCharSetArabicOEM, kCDXCharSetMSDOSNordic, kCDXCharSetMSDOSRussian, kCDXCharSetIBMModernGreek = 869, kCDXCharSetThai = 874, kCDXCharSetEBCDIC, kCDXCharSetJapanese = 932, kCDXCharSetChineseSimplified = 936, // PRC, Singapore kCDXCharSetKorean = 949, kCDXCharSetChineseTraditional = 950, // Taiwan, Hong Kong kCDXCharSetUnicodeISO10646 = 1200, kCDXCharSetWin31EasternEuropean = 1250, kCDXCharSetWin31Cyrillic, kCDXCharSetWin31Latin1, kCDXCharSetWin31Greek, kCDXCharSetWin31Turkish, kCDXCharSetHebrew, kCDXCharSetArabic, kCDXCharSetBaltic, kCDXCharSetVietnamese, kCDXCharSetKoreanJohab = 1361, kCDXCharSetMacRoman = 10000, kCDXCharSetMacJapanese, kCDXCharSetMacTradChinese, kCDXCharSetMacKorean, kCDXCharSetMacArabic, kCDXCharSetMacHebrew, kCDXCharSetMacGreek, kCDXCharSetMacCyrillic, kCDXCharSetMacReserved, kCDXCharSetMacDevanagari, kCDXCharSetMacGurmukhi, kCDXCharSetMacGujarati, kCDXCharSetMacOriya, kCDXCharSetMacBengali, kCDXCharSetMacTamil, kCDXCharSetMacTelugu, kCDXCharSetMacKannada, kCDXCharSetMacMalayalam, kCDXCharSetMacSinhalese, kCDXCharSetMacBurmese, kCDXCharSetMacKhmer, kCDXCharSetMacThai, kCDXCharSetMacLao, kCDXCharSetMacGeorgian, kCDXCharSetMacArmenian, kCDXCharSetMacSimpChinese, kCDXCharSetMacTibetan, kCDXCharSetMacMongolian, kCDXCharSetMacEthiopic, kCDXCharSetMacCentralEuroRoman, kCDXCharSetMacVietnamese, kCDXCharSetMacExtArabic, kCDXCharSetMacUninterpreted, kCDXCharSetMacIcelandic = 10079, kCDXCharSetMacTurkish = 10081 }; #endif // _H_CDXConstants �������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/CMakeLists.txt���������������������������������������������������������0000664�0000000�0000000�00000001260�12710376503�0020545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������cmake_minimum_required(VERSION 2.8.8) PROJECT(Molecule) file (GLOB Molecule_src src/*.c src/*.cpp) file (GLOB Molecule_headers *.h *.hpp src/*.h src/*.hpp) # Collect file list for GZip file (GLOB GZip_src ${Common_SOURCE_DIR}/gzip/*.c*) file (GLOB GZip_headers ${Common_SOURCE_DIR}/gzip/*.h*) source_group("GZip" FILES ${GZip_src} ${GZip_headers}) include_directories(${Common_SOURCE_DIR} ${Molecule_SOURCE_DIR}/.. ${ZLib_HEADERS_DIR} ${TinyXML_HEADERS_DIR}) add_library(molecule OBJECT ${Molecule_src} ${Molecule_headers} ${GZip_src} ${GZip_headers}) set_target_properties(molecule PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") include_directories (../third_party/inchi/inchi_dll) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/base_molecule.h��������������������������������������������������������0000664�0000000�0000000�00000031665�12710376503�0020771�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __base_molecule__ #define __base_molecule__ #include "graph/graph.h" #include "base_cpp/red_black.h" #include "math/algebra.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_cis_trans.h" #include "molecule/molecule_allene_stereo.h" #include "base_cpp/obj_array.h" #include "molecule/molecule_sgroups.h" #include "molecule/molecule_rgroups.h" #include "molecule/molecule_tgroups.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_standardize.h" #include "molecule/molecule_ionize.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { enum { CHARGE_UNKNOWN = -100 }; enum { ATOM_AROMATIC = 1, ATOM_ALIPHATIC = 2 }; enum { BOND_ZERO = 0, BOND_SINGLE = 1, BOND_DOUBLE = 2, BOND_TRIPLE = 3, BOND_AROMATIC = 4 }; enum { BOND_UP = 1, BOND_DOWN = 2, BOND_EITHER = 3 }; // Flags that disables copying information in making submolecule, // merging with molecule and cloning procedures enum { SKIP_ALL = -1, SKIP_CIS_TRANS = 0x01, SKIP_STEREOCENTERS = 0x02, SKIP_XYZ = 0x04, SKIP_RGROUP_FRAGMENTS = 0x08, SKIP_ATTACHMENT_POINTS = 0x16, SKIP_TGROUPS = 0x32, SKIP_TEMPLATE_ATTACHMENT_POINTS = 0x64 }; class Molecule; class QueryMolecule; class DLLEXPORT BaseMolecule : public Graph { public: typedef RedBlackMap<int,int> Mapping; BaseMolecule (); virtual ~BaseMolecule (); // Casting methods. Invalid casting throws exceptions. virtual Molecule& asMolecule (); virtual QueryMolecule& asQueryMolecule (); virtual bool isQueryMolecule (); virtual void clear (); // 'neu' means 'new' in German virtual BaseMolecule * neu () = 0; virtual int getAtomNumber (int idx) = 0; // > 0 -- ELEM_***, 0 -- pseudo-atom, -1 -- not sure virtual int getAtomCharge (int idx) = 0; // charge or CHARGE_UNKNOWN if not sure virtual int getAtomIsotope (int idx) = 0; // > 0 -- isotope, -1 -- not sure virtual int getAtomRadical (int idx) = 0; // > 0 -- RADICAL_***, -1 -- not sure virtual int getAtomAromaticity (int idx) = 0; // ATOM_AROMATIC, ATOM_ALIPHATIC, or -1 -- not sure virtual int getExplicitValence (int idx) = 0; // explicit valence or -1 if not set virtual int getAtomValence (int idx) = 0; // >= 0 -- valence, -1 is not set explicitly virtual int getAtomSubstCount (int idx) = 0; virtual int getAtomRingBondsCount (int idx) = 0; // >= 0 -- ring bonds count, -1 -- not sure int getAtomRadical_NoThrow (int idx, int fallback); int getAtomValence_NoThrow (int idx, int fallback); virtual int getAtomMaxH (int idx) = 0; virtual int getAtomMinH (int idx) = 0; virtual int getAtomTotalH (int idx) = 0; int possibleAtomTotalH (int idx, int hcount); virtual bool isPseudoAtom (int idx) = 0; virtual const char * getPseudoAtom (int idx) = 0; virtual bool isTemplateAtom (int idx) = 0; virtual const char * getTemplateAtom (int idx) = 0; virtual const int getTemplateAtomSeqid (int idx) = 0; virtual const char * getTemplateAtomClass (int idx) = 0; virtual const int getTemplateAtomDisplayOption (int idx) = 0; int countRSites (); int countSGroups (); static void collapse (BaseMolecule& bm, int id, Mapping& mapAtom, Mapping& mapBondInv); static void collapse (BaseMolecule& bm, int id); static void collapse (BaseMolecule& bm); int transformSCSRtoFullCTAB (); int transformFullCTABtoSCSR (ObjArray<TGroup> &templates); virtual bool isRSite (int atom_idx) = 0; virtual dword getRSiteBits (int atom_idx) = 0; virtual void allowRGroupOnRSite (int atom_idx, int rg_idx) = 0; void getAllowedRGroups (int atom_idx, Array<int> &rgroup_list); int getSingleAllowedRGroup (int atom_idx); int getRSiteAttachmentPointByOrder (int idx, int order) const; void setRSiteAttachmentOrder (int atom_idx, int att_atom_idx, int order); void setTemplateAtomAttachmentOrder (int atom_idx, int att_atom_idx, const char *att_id); int getTemplateAtomAttachmentPoint (int atom_idx, int order); void getTemplateAtomAttachmentPointId (int atom_idx, int order, Array<char> &apid); int getTemplateAtomAttachmentPointsCount (int atom_idx); int getTemplateAtomAttachmentPointById (int atom_idx, Array<char> &att_id); void addAttachmentPoint (int order, int atom_index); int getAttachmentPoint (int order, int index) const; void removeAttachmentPointsFromAtom (int atom_index); int attachmentPointCount () const; void removeAttachmentPoints (); void getAttachmentIndicesForAtom (int atom_idx, Array<int> &res); virtual bool isSaturatedAtom (int idx) = 0; virtual int getBondOrder (int idx) = 0; // > 0 -- BOND_***, -1 -- not sure virtual int getBondTopology (int idx) = 0; // > 0 -- TOPOLOGY_***, -1 -- not sure // true if the atom number belongs to the given list, false otherwise virtual bool atomNumberBelongs (int idx, const int *numbers, int count) = 0; // true if the atom can have that number, false otherwise virtual bool possibleAtomNumber (int idx, int number) = 0; // true if the atom can have that number and that charge, false otherwise virtual bool possibleAtomNumberAndCharge (int idx, int number, int charge) = 0; // true if the atom can have that number and that charge, false otherwise virtual bool possibleAtomNumberAndIsotope (int idx, int number, int isotope) = 0; // true if the atom can have that isotope index, false otherwise virtual bool possibleAtomIsotope (int idx, int isotope) = 0; // true if the atom can have that isotope index, false otherwise virtual bool possibleAtomCharge (int idx, int charge) = 0; // human-readable atom and bond desciptions for diagnostic purposes virtual void getAtomDescription (int idx, Array<char> &description) = 0; virtual void getBondDescription (int idx, Array<char> &description) = 0; // true if the bond can be that order, false otherwise virtual bool possibleBondOrder (int idx, int order) = 0; // true if bond stereoconfiguration is important virtual bool bondStereoCare (int idx) = 0; // Returns true if some bonds were changed virtual bool aromatize (const AromaticityOptions &options) = 0; // Returns true if all bonds were dearomatized virtual bool dearomatize (const AromaticityOptions &options) = 0; enum { CHANGED_ATOM_NUMBER = 0x01, CHANGED_CONNECTIVITY = 0x02, CHANGED_ALL = 0xFF, }; virtual void invalidateAtom (int index, int mask); Vec3f & getAtomXyz (int idx); void setAtomXyz (int idx, float x, float y, float z); void setAtomXyz (int idx, const Vec3f& v); void clearXyz (); MoleculeStereocenters stereocenters; MoleculeCisTrans cis_trans; MoleculeAlleneStereo allene_stereo; bool have_xyz; bool isChrial (); struct TemplateAttPoint { int ap_occur_idx; int ap_aidx; Array<char> ap_id; }; ObjPool<TemplateAttPoint> template_attachment_points; MoleculeSGroups sgroups; MoleculeTGroups tgroups; MoleculeRGroups rgroups; StringPool custom_collections; Array<char> name; static bool hasCoord (BaseMolecule &mol); static bool hasZCoord (BaseMolecule &mol); void mergeWithSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, Array<int> *mapping_out, int skip_flags = 0); int mergeAtoms (int atom1, int atom2); void flipBond (int atom_parent, int atom_from, int atom_to); void makeSubmolecule (BaseMolecule &mol, const Array<int> &vertices, Array<int> *mapping_out, int skip_flags = 0); void makeSubmolecule (BaseMolecule &other, const Filter &filter, Array<int> *mapping_out, Array<int> *inv_mapping, int skip_flags = 0); void makeEdgeSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, Array<int> *v_mapping, int skip_flags = 0); void clone (BaseMolecule &other, Array<int> *mapping, Array<int> *inv_mapping, int skip_flags = 0); // This is a bad hack for those who are too lazy to handle the mappings. // NEVER USE IT. void clone_KeepIndices (BaseMolecule &other, int skip_flags = 0); void mergeWithMolecule (BaseMolecule &other, Array<int> *mapping, int skip_flags = 0); void removeAtoms (const Array<int> &indices); void removeAtoms (const Filter &filter); void removeAtom (int idx); void removeBonds (const Array<int> &indices); void removeBond (int idx); void removeSGroup (int idx); void removeSGroupWithBasis (int idx); void unhighlightAll (); void highlightAtom (int idx); void highlightBond (int idx); void highlightAtoms (const Filter &filter); void highlightBonds (const Filter &filter); void unhighlightAtom (int idx); void unhighlightBond (int idx); int countHighlightedAtoms (); int countHighlightedBonds (); bool hasHighlighting (); bool isAtomHighlighted (int idx); bool isBondHighlighted (int idx); void highlightSubmolecule (BaseMolecule &sub, const int *mapping, bool entire); static int getVacantPiOrbitals (int group, int charge, int radical, int conn, int *lonepairs_out); // Returns edit revision for this molecule. // Each time molecule is changed revision number is increased. // If revision number is the same then molecule hasn't been changed. int getEditRevision (); // Manually update edit revision. This is required when molecule is changed // directly without calling molecule methods (for example mol.cis_trans.clear() and etc.) void updateEditRevision (); void clearBondDirections (); int getBondDirection (int idx) const; void setBondDirection (int idx, int dir); int getBondDirection2 (int center_idx, int nei_idx); void mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array<int> &mapping); void mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array<int> &mapping, Array<int> &edge_mapping); void clearSGroups(); void getSGroupAtomsCenterPoint (SGroup &sgroup, Vec2f &res); void getAtomsCenterPoint (Array<int> &atoms, Vec2f &res); void getAtomSymbol (int v, Array<char> &output); DECL_ERROR; protected: void _mergeWithSubmolecule_Sub (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, Array<int> &mapping, Array<int> &edge_mapping, int skip_flags); void _flipSGroupBond(SGroup &sgroup, int src_bond_idx, int new_bond_idx); void _flipSuperatomBond(Superatom &sa, int src_bond_idx, int new_bond_idx); void _flipTemplateAtomAttachmentPoint(int idx, int atom_from, int atom_to); virtual void _mergeWithSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) = 0; virtual void _postMergeWithSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags); virtual void _flipBond (int atom_parent, int atom_from, int atom_to); virtual void _removeAtoms (const Array<int> &indices, const int *mapping); virtual void _removeBonds (const Array<int> &indices); int _addBaseAtom (); int _addBaseBond (int beg, int end); void _removeAtomsFromSGroup (SGroup &sgroup, Array<int> &mapping); void _removeAtomsFromMultipleGroup (MultipleGroup &mg, Array<int> &mapping); void _removeAtomsFromSuperatom (Superatom &sa, Array<int> &mapping); void _removeBondsFromSGroup (SGroup &sgroup, Array<int> &mapping); void _removeBondsFromSuperatom (Superatom &sa, Array<int> &mapping); bool _mergeSGroupWithSubmolecule (SGroup &sgroup, SGroup &super, BaseMolecule &supermol, Array<int> &mapping, Array<int> &edge_mapping); void _checkSgroupHierarchy(int pidx, int oidx); int _transformTGroupToSGroup (int idx); int _addTemplate (TGroup &tgroup); Array<int> _hl_atoms; Array<int> _hl_bonds; Array<int> _bond_directions; Array<Vec3f> _xyz; ObjArray< Array<int> > _rsite_attachment_points; bool _rGroupFragment; ObjArray< Array<int> > _attachment_index; // When molecule gets edited then edit revision is increased. // If edit revision is the same then molecule wasn't edited int _edit_revision; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/canonical_smiles_saver.h�����������������������������������������������0000664�0000000�0000000�00000002372�12710376503�0022666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __canonical_smiles_saver__ #define __canonical_smiles_saver__ #include "molecule/smiles_saver.h" #include "base_cpp/exception.h" namespace indigo { class DLLEXPORT CanonicalSmilesSaver : public SmilesSaver { public: explicit CanonicalSmilesSaver (Output &output); ~CanonicalSmilesSaver (); bool find_invalid_stereo; const Array<int> *initial_atom_atom_mapping; void saveMolecule (Molecule &mol); DECL_ERROR; protected: typedef RedBlackMap<int, int> MapIntInt; TL_CP_DECL(Array<int>, _actual_atom_atom_mapping); TL_CP_DECL(MapIntInt, _initial_to_actual); int _aam_counter; }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/cmf_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000007657�12710376503�0020271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cmf_loader_h__ #define __cmf_loader_h__ #include "base_cpp/bitinworker.h" #include "lzw/lzw_dictionary.h" #include "lzw/lzw_decoder.h" #include "base_cpp/obj.h" #include "molecule/cmf_saver.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class Scanner; struct Vec3f; class DLLEXPORT CmfLoader { public: // external dictionary, internal decoder explicit CmfLoader (LzwDict &dict, Scanner &scanner); // external dictionary, external decoder explicit CmfLoader (LzwDecoder &decoder); // no dictionary, no decoder explicit CmfLoader (Scanner &scanner); ~CmfLoader(); void loadMolecule (Molecule &mol); void loadXyz (Scanner &scanner); bool skip_cistrans; bool skip_stereocenters; bool skip_valence; int version; // By default the latest version 2 is used Array<int> *atom_flags; Array<int> *bond_flags; bool has_mapping; CP_DECL; TL_CP_DECL(Array<int>, atom_mapping_to_restore); TL_CP_DECL(Array<int>, inv_atom_mapping_to_restore); TL_CP_DECL(Array<int>, bond_mapping_to_restore); TL_CP_DECL(Array<int>, inv_bond_mapping_to_restore); DECL_ERROR; protected: struct _AtomDesc { int label; int pseudo_atom_idx; // refers to _pseudo_labels int isotope; int charge; int hydrogens; int valence; int radical; int stereo_type; int stereo_group; bool stereo_invert_pyramid; int allene_stereo_parity; int flags; bool rsite; int rsite_bits; bool highlighted; }; struct _BondDesc { int beg; int end; int type; int cis_trans; bool in_ring; int direction; bool swap; int flags; bool highlighted; }; struct _AttachmentDesc { int atom; int index; }; void _init (); bool _getNextCode (int &code); void _readBond (int &code, _BondDesc &bond); bool _readAtom (int &code, _AtomDesc &atom, int atom_idx); bool _readCycleNumber (int &code, int &n); void _readExtSection (Molecule &mol); void _readSGroup (int code, Molecule &mol); void _readGeneralSGroup (SGroup &sgroup); void _readSGroupXYZ (Scanner &scanner, int code, Molecule &mol, const CmfSaver::VecRange &range); void _readBaseSGroupXyz (Scanner &scanner, SGroup &sgroup, const CmfSaver::VecRange &range); void _readString (Array<char> &dest); void _readUIntArray (Array<int> &dest); void _readVec3f (Scanner &scanner, Vec3f &pos, const CmfSaver::VecRange &range); void _readVec2f (Scanner &scanner, Vec2f &pos, const CmfSaver::VecRange &range); void _readDir2f (Scanner &scanner, Vec2f &dir, const CmfSaver::VecRange &range); float _readFloatInRange (Scanner &scanner, float min, float range); Scanner *_scanner; Obj<LzwDecoder> _decoder_obj; LzwDecoder *_ext_decoder; Obj<LzwScanner> _lzw_scanner; TL_CP_DECL(Array<_AtomDesc>, _atoms); TL_CP_DECL(Array<_BondDesc>, _bonds); TL_CP_DECL(StringPool, _pseudo_labels); TL_CP_DECL(Array<_AttachmentDesc>, _attachments); TL_CP_DECL(Array<int>, _sgroup_order); Molecule *_mol; private: CmfLoader (const CmfLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif /* __cmf_loader_h__ */ ���������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/cmf_saver.h������������������������������������������������������������0000664�0000000�0000000�00000006323�12710376503�0020130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cmf_saver_h__ #define __cmf_saver_h__ #include "base_cpp/obj.h" #include "lzw/lzw_encoder.h" #include "math/algebra.h" #include "molecule/base_molecule.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Output; class DLLEXPORT CmfSaver { public: // external dictionary, internal encoder explicit CmfSaver (LzwDict &dict, Output &output); // external dictionary, external encoder explicit CmfSaver (LzwEncoder &encoder); // no dictionary, no encoder explicit CmfSaver (Output &output); void saveMolecule (Molecule &mol); void saveXyz (Output &output); const Array<int> & getAtomSequence (); int *atom_flags; int *bond_flags; bool save_bond_dirs; bool save_highlighting; bool save_mapping; DECL_ERROR; struct VecRange { Vec3f xyz_min, xyz_range; bool have_z; }; protected: void _init (); void _encode (byte symbol); void _encodeAtom (Molecule &mol, int idx, const int *mapping); void _encodeBond (Molecule &mol, int idx, const int *mapping); void _encodeCycleNumer (int n); void _writeFloatInRange (Output &output, float v, float min, float range); struct Mapping { Array<int> *atom_mapping, *bond_mapping; }; void _encodeString (const Array<char> &str); void _encodeUIntArray (const Array<int> &data, const Array<int> &mapping); void _encodeUIntArray (const Array<int> &data); void _encodeUIntArraySkipNegative (const Array<int> &data); void _encodeExtSection (Molecule &mol, const Mapping &mapping); void _encodeBaseSGroup (Molecule &mol, SGroup &sgroup, const Mapping &mapping); //void _encodeSGroups (Molecule &mol, const Mapping &mapping); void _writeSGroupsXyz (Molecule &mol, Output &output, const VecRange &range); void _writeBaseSGroupXyz (Output &output, SGroup &sgroup, const VecRange &range); void _writeVec3f (Output &output, const Vec3f &pos, const VecRange &range); void _writeVec2f (Output &output, const Vec2f &pos, const VecRange &range); void _writeDir2f (Output &output, const Vec2f &dir, const VecRange &range); void _updateSGroupsXyzMinMax (Molecule &mol, Vec3f &min, Vec3f &max); void _updateBaseSGroupXyzMinMax (SGroup &sgroup, Vec3f &min, Vec3f &max); CP_DECL; TL_CP_DECL(Array<int>, _atom_sequence); Output *_output; Obj<LzwEncoder> _encoder_obj; LzwEncoder *_ext_encoder; Obj<LzwOutput> _encoder_output_obj; Molecule *_mol; private: CmfSaver (const CmfSaver &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif /* __cmf_saver_h__ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/cmf_symbol_codes.h�����������������������������������������������������0000664�0000000�0000000�00000013106�12710376503�0021467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __cmf_symbol_codes__ #define __cmf_symbol_codes__ #include "molecule/elements.h" namespace indigo { /* Compressed molecule symbols constants */ enum { /* Available values */ CMF_NUM_OF_CYCLES = 16, CMF_MIN_CHARGE = -5, CMF_MAX_CHARGE = 8, CMF_NUM_OF_CHARGES = CMF_MAX_CHARGE - CMF_MIN_CHARGE + 1, CMF_MIN_MASS_DIFF = -30, CMF_MAX_MASS_DIFF = 20, CMF_MAX_STEREOGROUPS = 4, CMF_MAX_VALENCE = 6, CMF_MAX_IMPLICIT_H = 10, CMF_NUM_OF_BOND_FLAGS = 3, CMF_NUM_OF_ATOM_FLAGS = 2, /* Bit code used for compression */ CMF_BIT_CODE_SIZE = 16, /* 105 - pseudo-atom prefix */ CMF_PSEUDOATOM = ELEM_MAX, /* 106 - R-Site */ CMF_RSITE = CMF_PSEUDOATOM + 1, /* 107 - where * * bond codes begin */ CMF_BONDS = CMF_RSITE + 1, CMF_BOND_SINGLE_CHAIN = CMF_BONDS, CMF_BOND_SINGLE_RING = CMF_BONDS + 1, CMF_BOND_DOUBLE_CHAIN = CMF_BONDS + 2, CMF_BOND_DOUBLE_RING = CMF_BONDS + 3, CMF_BOND_DOUBLE_CHAIN_CIS = CMF_BONDS + 4, CMF_BOND_DOUBLE_CHAIN_TRANS = CMF_BONDS + 5, CMF_BOND_DOUBLE_RING_CIS = CMF_BONDS + 6, CMF_BOND_DOUBLE_RING_TRANS = CMF_BONDS + 7, CMF_BOND_TRIPLE_CHAIN = CMF_BONDS + 8, CMF_BOND_TRIPLE_RING = CMF_BONDS + 9, CMF_BOND_AROMATIC = CMF_BONDS + 10, /* 118 - '(' */ CMF_OPEN_BRACKET = CMF_BONDS + 11, /* 119 - ')' */ CMF_CLOSE_BRACKET = CMF_OPEN_BRACKET + 1, /* 120 - where cycle * * codes begin: 0..15 */ CMF_CYCLES = CMF_CLOSE_BRACKET + 1, /* 136 - adds 16 to the subsequent cycle number */ CMF_CYCLES_PLUS = CMF_CYCLES + CMF_NUM_OF_CYCLES, /* 137 - where charge * * codes begin -5..-1,1..8 */ CMF_CHARGES = CMF_CYCLES_PLUS + 1, /* Separator like '.' in SMILES * * (zero charge is not used) */ CMF_SEPARATOR = CMF_CHARGES - CMF_MIN_CHARGE, /* 151..156 - isotope codes */ CMF_ISOTOPE_ZERO = CMF_CHARGES + CMF_NUM_OF_CHARGES, CMF_ISOTOPE_PLUS1 = CMF_ISOTOPE_ZERO + 1, CMF_ISOTOPE_PLUS2 = CMF_ISOTOPE_PLUS1 + 1, CMF_ISOTOPE_MINUS1 = CMF_ISOTOPE_PLUS2 + 1, CMF_ISOTOPE_MINUS2 = CMF_ISOTOPE_MINUS1 + 1, CMF_ISOTOPE_OTHER = CMF_ISOTOPE_MINUS2 + 1, // followed by an extra byte /* 157 - where stereocenter codes begin */ CMF_STEREO = CMF_ISOTOPE_OTHER + 1, /* 157 - 'any' stereocenter */ CMF_STEREO_ANY = CMF_STEREO, /* 158 - 'and' stereo-group (1..4) */ CMF_STEREO_AND_0 = CMF_STEREO + 1, /* 162 - 'and' stereo-group (1..4) */ CMF_STEREO_OR_0 = CMF_STEREO_AND_0 + CMF_MAX_STEREOGROUPS, /* 166 - 'abs' stereocenter */ CMF_STEREO_ABS_0 = CMF_STEREO_OR_0 + CMF_MAX_STEREOGROUPS, /* 167 - 'and' stereo-group (1..4) */ CMF_STEREO_AND_1 = CMF_STEREO_ABS_0 + 1, /* 171 - 'and' stereo-group (1..4) */ CMF_STEREO_OR_1 = CMF_STEREO_AND_1 + CMF_MAX_STEREOGROUPS, /* 175 - 'abs' stereocenter */ CMF_STEREO_ABS_1 = CMF_STEREO_OR_1 + CMF_MAX_STEREOGROUPS, /* 176-177 - allene centers */ CMF_STEREO_ALLENE_0 = CMF_STEREO_ABS_1 + 1, CMF_STEREO_ALLENE_1 = CMF_STEREO_ALLENE_0 + 1, /* 178 - valence (1..5) */ CMF_VALENCE = CMF_STEREO_ALLENE_1 + 1, /* 185 - implicit hydrogen count (1..5) */ CMF_IMPLICIT_H = CMF_VALENCE + CMF_MAX_VALENCE + 1, /* 196 - singlet radical */ CMF_RADICAL_SINGLET = CMF_IMPLICIT_H + CMF_MAX_IMPLICIT_H + 1, /* 197 - douplet radical */ CMF_RADICAL_DOUBLET = CMF_RADICAL_SINGLET + 1, /* 198 - triplet radical */ CMF_RADICAL_TRIPLET = CMF_RADICAL_DOUBLET + 1, /* 199 - bond flags */ CMF_BOND_FLAGS = CMF_RADICAL_TRIPLET + 1, /* 202 - atom flags */ CMF_ATOM_FLAGS = CMF_BOND_FLAGS + CMF_NUM_OF_BOND_FLAGS, /* 204-206- bond directions (up/down/either) */ CMF_BOND_UP = CMF_ATOM_FLAGS + CMF_NUM_OF_ATOM_FLAGS, CMF_BOND_DOWN = CMF_BOND_UP + 1, CMF_BOND_EITHER = CMF_BOND_DOWN + 1, /* 207 - "swap bond ends" flag */ CMF_BOND_SWAP_ENDS = CMF_BOND_EITHER + 1, /* 208 - highlighting */ CMF_HIGHLIGHTED = CMF_BOND_SWAP_ENDS + 1, /* 209 - attachment point */ CMF_ATTACHPT = CMF_HIGHLIGHTED + 1, /* 210 - terminator */ CMF_TERMINATOR = CMF_ATTACHPT + 1, /* 211 - Extended part: sgroups, ... */ CMF_EXT = CMF_TERMINATOR + 1, /* SGroups */ /* 212 - Data SGroup */ CMF_DATASGROUP, /* 213 - Superatom */ CMF_SUPERATOM, /* 214 - Repeating unit */ CMF_REPEATINGUNIT, /* 215 - Multiple Group */ CMF_MULTIPLEGROUP, /* 216 - Generic SGroup */ CMF_GENERICSGROUP, /* 217 - R-Site attachments */ CMF_RSITE_ATTACHMENTS, /* 218, 219 */ CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_RING, CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_CHAIN, /* 220 - R-Site */ CMF_RSITE_EXT, /* 221 - General charge */ CMF_CHARGE_EXT, /* 222 - General valence */ CMF_VALENCE_EXT, /* 223 - Atom mapping to restore */ CMF_MAPPING, /* Alphabet size = 256. Any number can be used because of writing integer indices */ CMF_ALPHABET_SIZE = 256 }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/elements.h�������������������������������������������������������������0000664�0000000�0000000�00000013606�12710376503�0020001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __elements_h__ #define __elements_h__ #include "base_cpp/tlscont.h" #include "base_cpp/red_black.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Scanner; enum { ELEM_MIN = 1, ELEM_H = 1, ELEM_He, ELEM_Li, ELEM_Be, ELEM_B , ELEM_C , ELEM_N , ELEM_O , ELEM_F , ELEM_Ne, ELEM_Na, ELEM_Mg, ELEM_Al, ELEM_Si, ELEM_P , ELEM_S , ELEM_Cl, ELEM_Ar, ELEM_K, ELEM_Ca, ELEM_Sc, ELEM_Ti, ELEM_V, ELEM_Cr, ELEM_Mn, ELEM_Fe, ELEM_Co, ELEM_Ni, ELEM_Cu, ELEM_Zn, ELEM_Ga, ELEM_Ge, ELEM_As, ELEM_Se, ELEM_Br, ELEM_Kr, ELEM_Rb, ELEM_Sr, ELEM_Y, ELEM_Zr, ELEM_Nb, ELEM_Mo, ELEM_Tc, ELEM_Ru, ELEM_Rh, ELEM_Pd, ELEM_Ag, ELEM_Cd, ELEM_In, ELEM_Sn, ELEM_Sb, ELEM_Te, ELEM_I, ELEM_Xe, ELEM_Cs, ELEM_Ba, ELEM_La, ELEM_Ce, ELEM_Pr, ELEM_Nd, ELEM_Pm, ELEM_Sm, ELEM_Eu, ELEM_Gd, ELEM_Tb, ELEM_Dy, ELEM_Ho, ELEM_Er, ELEM_Tm, ELEM_Yb, ELEM_Lu, ELEM_Hf, ELEM_Ta, ELEM_W, ELEM_Re, ELEM_Os, ELEM_Ir, ELEM_Pt, ELEM_Au, ELEM_Hg, ELEM_Tl, ELEM_Pb, ELEM_Bi, ELEM_Po, ELEM_At, ELEM_Rn, ELEM_Fr, ELEM_Ra, ELEM_Ac, ELEM_Th, ELEM_Pa, ELEM_U, ELEM_Np, ELEM_Pu, ELEM_Am, ELEM_Cm, ELEM_Bk, ELEM_Cf, ELEM_Es, ELEM_Fm, ELEM_Md, ELEM_No, ELEM_Lr, ELEM_Rf, ELEM_MAX, ELEM_PSEUDO, // pseudoatom ELEM_RSITE, // 'R' atom ELEM_TEMPLATE, // template ELEM_ATTPOINT // attachment point }; enum { RADICAL_SINGLET = 1, RADICAL_DOUBLET = 2, RADICAL_TRIPLET = 3 }; class DLLEXPORT Element { public: DECL_ERROR; static const char * toString (int element); static int fromString (const char *name); static int fromString2 (const char *name); static int fromChar (char c); static int fromTwoChars (char c1, char c2); static int fromTwoChars2 (char c1, char c2); static int radicalElectrons (int radical); static int radicalOrbitals (int radical); static bool calcValence (int elem, int charge, int radical, int conn, int &valence, int &hyd, bool to_throw); static int calcValenceOfAromaticAtom (int elem, int charge, int n_arom, int min_conn); static int calcValenceMinusHyd (int elem, int charge, int radical, int conn); // Calculate maximum number of single bonds that // can be attached to specified atom. static int getMaximumConnectivity (int elem, int charge, int radical, bool use_d_orbital); static int orbitals (int elem, bool use_d_orbital); static int electrons (int elem, int charge); static int group (int element); static int period (int period); static int read (Scanner &scanner); static bool isHalogen (int element); // Returns isotope that has weight most close to the atomic weight static int getDefaultIsotope (int element); // Return most abundant isotope static int getMostAbundantIsotope (int element); static float getRelativeIsotopicMass (int element, int isotope); static float getStandardAtomicWeight (int element); static bool getIsotopicComposition (int element, int isotope, float &res); static void getMinMaxIsotopeIndex (int element, int &min, int &max); static bool canBeAromatic (int element); static Array<int> & tautomerHeteroatoms (); private: Element (); static Element _instance; void _initAllPeriodic (); void _initPeriodic (int element, const char *name, int period, int group); void _setStandardAtomicWeightIndex (int element, int index); void _addElementIsotope (int element, int isotope, float mass, float isotopic_composition); void _initAllIsotopes (); void _initDefaultIsotopes (); void _initAromatic (); RedBlackStringMap<int> _map; Array<int> _halogens; Array<int> _tau_heteroatoms; // Appear in tautomer chains // Per-element physical parameters struct _Parameters { char name[3]; int group; int period; int natural_isotope_index; // Can be ElementIsotope::NATURAL or anything else // Isotope with mass most close to the atomic weight int default_isotope; // Isotope with highest mole fraction int most_abundant_isotope; // Minimum and maximum isotope index int min_isotope_index, max_isotope_index; bool can_be_aromatic; }; Array<_Parameters> _element_parameters; // Isotopes mass key struct DLLEXPORT _IsotopeKey { int element; int isotope; // Can be equal to NATURAL // Pseudoisotope value for weighted sum of the // atomic masses of all the isotopes for specified element enum { NATURAL = -1, UNINITIALIZED = -2 }; _IsotopeKey (int element, int isotope); bool operator< (const _IsotopeKey &right) const; }; struct _IsotopeValue { _IsotopeValue (float mass, float isotopic_composition); // Isotope mass float mass; // Mole fraction of the various isotopes float isotopic_composition; }; RedBlackMap<_IsotopeKey, _IsotopeValue> _isotope_parameters_map; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/gross_formula.h��������������������������������������������������������0000664�0000000�0000000�00000003615�12710376503�0021046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __gross_formula__ #define __gross_formula__ namespace indigo { #include "base_cpp/array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class BaseMolecule; class DLLEXPORT GrossFormula { public: static void collect (BaseMolecule &molecule, Array<int> &gross); static void toString (const Array<int> &gross, Array<char> &str); static void toString_Hill (const Array<int> &gross, Array<char> &str); static void fromString (const char *str, Array<int> &gross); static void fromString (Scanner &scanner, Array<int> &gross); static bool leq (const Array<int> &gross1, const Array<int> &gross2); static bool geq (const Array<int> &gross1, const Array<int> &gross2); static bool equal (const Array<int> &gross1, const Array<int> &gross2); protected: struct _ElemCounter { int elem; int counter; }; static void _toString (const Array<int> &gross, Array<char> &str, int (*cmp)(_ElemCounter &, _ElemCounter &, void *)); static int _cmp (_ElemCounter &ec1, _ElemCounter &ec2, void *context); static int _cmp_hill (_ElemCounter &ec1, _ElemCounter &ec2, void *context); static int _cmp_hill_no_carbon (_ElemCounter &ec1, _ElemCounter &ec2, void *context); }; } #endif �������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/haworth_projection_finder.h��������������������������������������������0000664�0000000�0000000�00000004027�12710376503�0023421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __haworth_projection_finder__ #define __haworth_projection_finder__ #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/list.h" #include "base_cpp/tlscont.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class DLLEXPORT HaworthProjectionFinder { public: HaworthProjectionFinder (BaseMolecule &mol); void find (); void findAndAddStereocenters (); bool isBoldBond (int e_idx); const Array<bool>& getAtomsMask (); const Array<bool>& getBondsMask (); private: void _find (bool add_stereo); bool _processRing (bool add_stereo, const Array<int> &vertices, const Array<int> &edges); void _markRingBonds (const Array<int> &vertices, const Array<int> &edges); void _addRingStereocenters (const Array<int> &vertices, const Array<int> &edges); bool _isCornerVertex (int v, int e1, int e2); bool _isHorizontalEdge (int e, float cos_threshold); bool _isVerticalEdge (int e, float cos_threshold); float _getAngleCos(int v, int e, float dx, float dy); float _getAngleCos(int v, int e1, int e2); float _getAngleSin(int v, int e1, int e2); BaseMolecule &_mol; CP_DECL; TL_CP_DECL(Array<bool>, _atoms_mask); TL_CP_DECL(Array<bool>, _bonds_mask); TL_CP_DECL(Array<bool>, _bold_bonds_mask); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif // __haworth_projection_finder__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/icm_common.h�����������������������������������������������������������0000664�0000000�0000000�00000001314�12710376503�0020276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ namespace indigo { enum { ICM_XYZ = 1, ICM_BOND_DIRS = 2 }; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/icm_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000002035�12710376503�0020255�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __icm_loader__ #define __icm_loader__ #include "base_cpp/exception.h" namespace indigo { class Scanner; class Molecule; class IcmLoader { public: // external dictionary, internal decoder explicit IcmLoader (Scanner &scanner); void loadMolecule (Molecule &mol); DECL_ERROR; protected: Scanner &_scanner; private: IcmLoader (const IcmLoader &); // no implicit copy }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/icm_saver.h������������������������������������������������������������0000664�0000000�0000000�00000002240�12710376503�0020125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __icm_saver__ #define __icm_saver__ #include "base_cpp/exception.h" namespace indigo { class Molecule; class Output; class IcmSaver { public: static const char *VERSION1, *VERSION2; static bool checkVersion (const char *prefix); explicit IcmSaver (Output &output); void saveMolecule (Molecule &mol); bool save_xyz; bool save_bond_dirs; bool save_highlighting; bool save_ordering; DECL_ERROR; protected: Output &_output; private: IcmSaver (const IcmSaver &); // no implicit copy }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/inchi_parser.h���������������������������������������������������������0000664�0000000�0000000�00000003417�12710376503�0020632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __inchi_parser_h__ #define __inchi_parser_h__ #include "base_cpp/array.h" namespace indigo { DECL_EXCEPTION(InchiParserError); // Molecule InChI code constructor class class InChICodeParser { public: enum { STATIC = 1, MOBILE = 2 }; explicit InChICodeParser(const char *inchi_code); int staticHydrogenPositionBegin() { return _nextElement(STATIC, -1); } int staticHydrogenPositionNext(int index) { return _nextElement(STATIC, index); } int staticHydrogenPositionEnd() { return _hydrogens.size(); } int mobileHydrogenPositionBegin() { return _nextElement(MOBILE, -1); } int mobileHydrogenPositionNext(int index) { return _nextElement(MOBILE, index); } int mobileHydrogenPositionEnd() { return _hydrogens.size(); } int mobileHydrogenCount() const { return _mobileCount; } int getHydrogen(int index) { return _hydrogens.at(index); } DECL_ERROR; private: Array<int> _hydrogens; Array<int> _types; int _mobileCount; int _nextElement(int type, int index); }; } #endif // __inchi_parser_h__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/inchi_wrapper.h��������������������������������������������������������0000664�0000000�0000000�00000003410�12710376503�0021007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __inchi_wrapper_h__ #define __inchi_wrapper_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "inchi_api.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { // Forward declaration class Molecule; struct InchiOutput; class DLLEXPORT InchiWrapper { public: InchiWrapper(); void clear(); // Input parameters void setOptions (const char *opt); // Output additional results Array<char> warning, log, auxInfo; void loadMoleculeFromInchi (const char *inchi, Molecule &mol); void loadMoleculeFromAux (const char *aux, Molecule &mol); void saveMoleculeIntoInchi (Molecule &mol, Array<char> &inchi); void parseInchiOutput (const InchiOutput &inchi_output, Molecule &mol); void generateInchiInput (Molecule &mol, inchi_Input &input, Array<inchi_Atom> &atoms, Array<inchi_Stereo0D> &stereo); void neutralizeV5Nitrogen (Molecule &mol); static const char* version (); static void InChIKey (const char *inchi, Array<char> &output); DECL_EXCEPTION_NO_EXP(Error); private: Array<char> options; }; } #endif // __inchi_wrapper_h__ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/max_common_submolecule.h�����������������������������������������������0000664�0000000�0000000�00000002354�12710376503�0022717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _max_common_submolecule #define _max_common_submolecule #include "base_cpp/scanner.h" #include "molecule/molfile_loader.h" #include "molecule/molfile_saver.h" #include "graph/max_common_subgraph.h" #include "time.h" #include "molecule/molecule.h" namespace indigo { class DLLEXPORT MaxCommonSubmolecule: public MaxCommonSubgraph{ public: MaxCommonSubmolecule(BaseMolecule& submol, BaseMolecule& supermol); static bool matchBonds (Graph &g1, Graph &g2, int i, int j, void* userdata); static bool matchAtoms (Graph &g1, Graph &g2, const int *core_sub, int i, int j, void* userdata); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule.h�������������������������������������������������������������0000664�0000000�0000000�00000020212�12710376503�0017761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_h__ #define __molecule_h__ #include "molecule/base_molecule.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT Molecule : public BaseMolecule { public: Molecule (); virtual ~Molecule (); virtual Molecule & asMolecule (); virtual void clear (); virtual BaseMolecule * neu (); int addAtom (int label); int resetAtom (int idx, int label); void setPseudoAtom (int idx, const char *text); void setTemplateAtom (int idx, const char *text); void setTemplateAtomClass (int idx, const char *text); void setTemplateAtomSeqid (int idx, int seq_id); void setTemplateAtomDisplayOption (int idx, int contracted); int addBond (int beg, int end, int order); int addBond_Silent (int beg, int end, int order); void setAtomCharge (int idx, int charge); void setAtomCharge_Silent (int idx, int charge); void setAtomIsotope (int idx, int isotope); void setAtomRadical (int idx, int radical); void setValence (int idx, int valence); void setExplicitValence (int idx, int valence); void resetExplicitValence (int idx); bool isExplicitValenceSet (int idx); void setImplicitH (int idx, int impl_h); bool isImplicitHSet (int idx); // Set bond order method. // If keep_connectivity is false then connectivity to bond ends // will be recalculated. Connectivity should be kept only when bond order // is changed to/from aromatic. void setBondOrder (int idx, int order, bool keep_connectivity = false); void setBondOrder_Silent (int idx, int order); virtual int getAtomNumber (int idx); virtual int getAtomCharge (int idx); virtual int getAtomIsotope (int idx); virtual int getAtomRadical (int idx); virtual int getBondOrder (int idx); virtual int getBondTopology (int idx); virtual int getAtomAromaticity (int idx); virtual int getExplicitValence (int idx); virtual int getAtomValence (int idx); virtual int getAtomSubstCount (int idx); virtual int getAtomRingBondsCount (int idx); virtual int getAtomMaxH (int idx); virtual int getAtomMinH (int idx); virtual int getAtomTotalH (int idx); virtual bool isPseudoAtom (int idx); virtual const char * getPseudoAtom (int idx); virtual bool isTemplateAtom (int idx); virtual const char * getTemplateAtom (int idx); virtual const int getTemplateAtomSeqid (int idx); virtual const char * getTemplateAtomClass (int idx); virtual const int getTemplateAtomDisplayOption (int idx); virtual bool isRSite (int atom_idx); virtual dword getRSiteBits (int atom_idx); virtual void allowRGroupOnRSite (int atom_idx, int rg_idx); void setRSiteBits (int atom_idx, int bits); virtual bool bondStereoCare (int idx); virtual bool aromatize (const AromaticityOptions &options); virtual bool dearomatize (const AromaticityOptions &options); int getImplicitH (int idx); int getImplicitH_NoThrow (int idx, int fallback); int calcImplicitHForConnectivity (int idx, int conn); int getAtomConnectivity (int idx); int getAtomConnectivity_noImplH (int idx); int getAtomConnectivity_NoThrow (int idx, int fallback); int calcAtomConnectivity_noImplH (int idx); void calcAromaticAtomConnectivity (int idx, int &n_arom, int &min_conn); bool isSaturatedAtom (int idx); int totalHydrogensCount (); virtual bool atomNumberBelongs (int idx, const int *numbers, int count); virtual bool possibleAtomNumber (int idx, int number); virtual bool possibleAtomNumberAndCharge (int idx, int number, int charge); virtual bool possibleAtomNumberAndIsotope (int idx, int number, int isotope); virtual bool possibleAtomIsotope (int idx, int isotope); virtual bool possibleAtomCharge (int idx, int charge); virtual void getAtomDescription (int idx, Array<char> &description); virtual void getBondDescription (int idx, Array<char> &description); virtual bool possibleBondOrder (int idx, int order); int getVacantPiOrbitals (int atom_idx, int *lonepairs_out); int getVacantPiOrbitals (int atom_idx, int conn, int *lonepairs_out); static int matchAtomsCmp (Graph &g1, Graph &g2, int idx1, int idx2, void *userdata); void unfoldHydrogens (Array<int> *markers_out, int max_h_cnt = -1, bool impl_h_no_throw = false); static void saveBondOrders (Molecule &mol, Array<int> &orders); static void loadBondOrders (Molecule &mol, Array<int> &orders); bool convertableToImplicitHydrogen (int idx); void invalidateHCounters (); static void checkForConsistency (Molecule &mol); static bool shouldWriteHCount (Molecule &mol, int idx); static bool shouldWriteHCountEx (Molecule &mol, int idx, int h_to_ignore); bool isAromatized (); // Check bool isNitrogenV5 (int atom_index); bool isNitrogenV5ForConnectivity (int atom_index, int conn); virtual void invalidateAtom (int index, int mask); bool restoreAromaticHydrogens (bool unambiguous_only = true); bool standardize (const StandardizeOptions &options); bool ionize (float ph, float ph_toll, const IonizeOptions &options); bool isPossibleFischerProjection (const char* options); protected: struct _Atom { int number; bool explicit_valence; bool explicit_impl_h; int isotope; int charge; int pseudoatom_value_idx; // if number == ELEM_PSEUDO, this is the corresponding // index from _pseudo_atom_values int rgroup_bits; // if number == ELEM_RSITE, these are 32 bits, each allowing // an r-group with corresponding number to go for this atom. // Simple 'R' atoms have this field equal to zero. int template_occur_idx; // if number == ELEM_TEMPLATE, this is the corresponding // index from _template_occurrences }; Array<_Atom> _atoms; Array<int> _bond_orders; Array<int> _connectivity; // implicit H not included Array<int> _aromaticity; Array<int> _implicit_h; Array<int> _total_h; Array<int> _valence; Array<int> _radicals; StringPool _pseudo_atom_values; struct _AttachOrder { int ap_idx; Array<char> ap_id; }; struct _TemplateOccurrence { int name_idx; // index in _template_names int class_idx; // index in _template_classes int seq_id; // sequence id int contracted; // display option (-1 if undefined, 0 - expanded, 1 - contracted) Array<_AttachOrder> order; // attach order info }; ObjPool<_TemplateOccurrence> _template_occurrences; StringPool _template_classes; StringPool _template_names; bool _aromatized; virtual void _mergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags); virtual void _flipBond (int atom_parent, int atom_from, int atom_to); virtual void _removeAtoms (const Array<int> &indices, const int *mapping); // If 'validate' is true then vertex connectivity and implicit hydrogens // are calculates and stored. If 'validate' is false then connectivity // information is cleared. void _validateVertexConnectivity (int idx, bool validate); void _invalidateVertexCache (int idx); private: int _getImplicitHForConnectivity (int idx, int conn, bool use_cache); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_3d_constraints.h����������������������������������������������0000664�0000000�0000000�00000016107�12710376503�0023006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_3d_constraints__ #define __molecule_3d_constraints__ #include "base_cpp/ptr_array.h" #include "base_cpp/red_black.h" #include "base_cpp/tlscont.h" #include "math/algebra.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class QueryMolecule; class DLLEXPORT Molecule3dConstraints { public: Molecule3dConstraints (); void init (); enum { POINT_ATOM = 1, POINT_DISTANCE = 2, POINT_PERCENTAGE = 3, POINT_NORMALE = 4, POINT_CENTROID = 5, LINE_NORMALE = 6, LINE_BEST_FIT = 7, PLANE_BEST_FIT = 8, PLANE_POINT_LINE = 9, ANGLE_3POINTS = 10, ANGLE_2LINES = 11, ANGLE_2PLANES = 12, ANGLE_DIHEDRAL = 13, DISTANCE_2POINTS = 14, DISTANCE_POINT_LINE = 15, DISTANCE_POINT_PLANE = 16, EXCLUSION_SPHERE = 17 }; struct Base { explicit Base (int type_) : type(type_) {} virtual ~Base () {} int type; }; struct AngleBase : public Base { public: explicit AngleBase (int type) : Base(type) {} virtual ~AngleBase () {} float bottom; float top; }; struct DistanceBase : public Base { explicit DistanceBase (int type) : Base(type) {} virtual ~DistanceBase () {} float bottom; float top; }; struct Normale : public Base { explicit Normale () : Base(LINE_NORMALE) {} virtual ~Normale () {} int point_id; int plane_id; }; struct BestFitLine : public Base { explicit BestFitLine () : Base(LINE_BEST_FIT) {} virtual ~BestFitLine () {} float max_deviation; Array<int> point_ids; }; struct PointByAtom : public Base { explicit PointByAtom () : Base(POINT_ATOM) {} virtual ~PointByAtom () {} int atom_idx; }; struct PointByDistance : public Base { explicit PointByDistance () : Base(POINT_DISTANCE) {} virtual ~PointByDistance () {} int beg_id; int end_id; float distance; }; struct PointByPercentage : public Base { explicit PointByPercentage () : Base(POINT_PERCENTAGE) {} virtual ~PointByPercentage () {} int beg_id; int end_id; float percentage; }; struct PointByNormale : public Base { explicit PointByNormale () : Base(POINT_NORMALE) {} virtual ~PointByNormale () {} int org_id; int norm_id; float distance; }; struct Centroid : public Base { explicit Centroid () : Base(POINT_CENTROID) {} virtual ~Centroid () {} Array<int> point_ids; }; struct BestFitPlane : public Base { explicit BestFitPlane () : Base(PLANE_BEST_FIT) {} virtual ~BestFitPlane () {} float max_deviation; Array<int> point_ids; }; struct PlaneByPoint : public Base { explicit PlaneByPoint () : Base(PLANE_POINT_LINE) {} virtual ~PlaneByPoint () {} int point_id; int line_id; }; struct DistanceByPoints : public DistanceBase { explicit DistanceByPoints () : DistanceBase(DISTANCE_2POINTS) {} virtual ~DistanceByPoints () {} int beg_id; int end_id; }; struct DistanceByLine : public DistanceBase { explicit DistanceByLine () : DistanceBase(DISTANCE_POINT_LINE) {} virtual ~DistanceByLine () {} int point_id; int line_id; }; struct DistanceByPlane : public DistanceBase { explicit DistanceByPlane () : DistanceBase(DISTANCE_POINT_PLANE) {} virtual ~DistanceByPlane () {} int point_id; int plane_id; }; struct AngleByPoints : public AngleBase { explicit AngleByPoints () : AngleBase(ANGLE_3POINTS) {} virtual ~AngleByPoints () {} int point1_id; int point2_id; int point3_id; }; struct AngleByLines : public AngleBase { explicit AngleByLines () : AngleBase(ANGLE_2LINES) {} virtual ~AngleByLines () {} int line1_id; int line2_id; }; struct AngleByPlanes : public AngleBase { explicit AngleByPlanes () : AngleBase(ANGLE_2PLANES) {} virtual ~AngleByPlanes () {} int plane1_id; int plane2_id; }; struct AngleDihedral : public AngleBase { explicit AngleDihedral () : AngleBase(ANGLE_DIHEDRAL) {} virtual ~AngleDihedral () {} int point1_id; int point2_id; int point3_id; int point4_id; }; struct ExclusionSphere : public Base { explicit ExclusionSphere () : Base(EXCLUSION_SPHERE) {} virtual ~ExclusionSphere () {} int center_id; float radius; bool allow_unconnected; Array<int> allowed_atoms; }; Base & add (Base *constraint); int begin () const; int end () const; int next (int idx) const; const Base & at (int idx) const; // takes mapping from supermolecule to submolecule void buildOnSubmolecule (const Molecule3dConstraints &super, const int *mapping); void removeAtoms (const int *mapping); // if have real constraints (not features) bool haveConstraints (); void clear (); DECL_ERROR; protected: QueryMolecule & _getMolecule (); PtrArray<Base> _constraints; static void _buildSub (PtrArray<Base> &sub, const PtrArray<Base> &super, const int *mapping); private: Molecule3dConstraints (const Molecule3dConstraints &); // no implicit copy }; class Molecule3dConstraintsChecker { public: Molecule3dConstraintsChecker (const Molecule3dConstraints &constraints); bool check (BaseMolecule &target, const int *mapping); void markUsedAtoms (int *arr, int value); DECL_ERROR; protected: void _cache (int idx); float _getAngle (int idx); float _getDistance (int idx); void _mark (int idx); const Molecule3dConstraints &_constraints; // saves a bit of typing typedef Molecule3dConstraints MC; // can't have comma-containing type names in macro declarations below typedef RedBlackMap<int, Vec3f> MapV; typedef RedBlackMap<int, Line3f> MapL; typedef RedBlackMap<int, Plane3f> MapP; CP_DECL; TL_CP_DECL(MapV, _cache_v); TL_CP_DECL(MapL, _cache_l); TL_CP_DECL(MapP, _cache_p); BaseMolecule *_target; const int *_mapping; int *_to_mark; int _mark_value; TL_CP_DECL(RedBlackSet<int>, _cache_mark); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_allene_stereo.h�����������������������������������������������0000664�0000000�0000000�00000005525�12710376503�0022674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_allene_stereo__ #define __molecule_allene_stereo__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "base_cpp/exception.h" #include "base_cpp/red_black.h" namespace indigo { class BaseMolecule; class DLLEXPORT MoleculeAlleneStereo { public: MoleculeAlleneStereo (); void clear (); void buildFromBonds (bool ignore_errors, int *sensible_bonds_out); void markBonds (); static int sameside (const Vec3f &dir1, const Vec3f &dir2, const Vec3f &sep); void buildOnSubmolecule (MoleculeAlleneStereo &super, int *mapping); static bool checkSub (BaseMolecule &query, BaseMolecule &target, const int *mapping); static bool possibleCenter (BaseMolecule &mol, int idx, int &left, int &right, int subst[4], bool pure_h[4]); bool isCenter (int atom_idx); int size (); int begin () const; int end () const; int next (int i) const; void get (int i, int &atom_idx, int &left, int &right, int subst[4], int &parity); void getByAtomIdx (int atom_idx, int &left, int &right, int subst[4], int &parity); void invert (int atom_idx); void reset (int atom_idx); void add (int atom_idx, int left, int right, int subst[4], int parity); void removeAtoms (const Array<int> &indices); void removeBonds (const Array<int> &indices); void registerUnfoldedHydrogen (int atom_idx, int added_hydrogen); DECL_ERROR; protected: struct _Atom { int left; // number of the "left" neighbor atom int right; // number of the "right" neighbor atom // substituens: [0] and [1] are connected to the "left" neighbor, // [2] and [3] are connected to the "right" neighbor. // [1] and [3] may be -1 (implicit H) // [0] and [2] are never -1 int subst[4]; // parity = 1 if [2]-nd substituent is rotated CCW w.r.t. [0]-th // substituent when we look at it from "left" to "right" // parity = 2 if it is rotated CW int parity; }; BaseMolecule & _getMolecule(); bool _isAlleneCenter (BaseMolecule &mol, int idx, _Atom &atom, int *sensible_bonds_out); RedBlackMap<int, _Atom> _centers; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_arom.h��������������������������������������������������������0000664�0000000�0000000�00000012671�12710376503�0021011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_arom_h__ #define __molecule_arom_h__ #include "base_cpp/tlscont.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Graph; class Molecule; class QueryMolecule; class BaseMolecule; struct AromaticityOptions { enum Method { BASIC, GENERIC }; Method method; bool dearomatize_check; bool unique_dearomatization; AromaticityOptions (Method method = BASIC) : method(method), dearomatize_check(true), unique_dearomatization(false) {} }; // Aromatization classes class DLLEXPORT AromatizerBase { public: explicit AromatizerBase (BaseMolecule &molecule); virtual ~AromatizerBase (); void aromatize (); void reset (void); bool isBondAromatic (int e_idx); const byte* isBondAromaticArray (void); void addAromaticCycle (int id, const int *cycle, int cycle_len); void removeAromaticCycle (int id, const int *cycle, int cycle_len); bool handleUnsureCycles (); void setBondAromaticCount (int e_idx, int count); DECL_ERROR; protected: // Functions for overloading virtual bool _checkVertex (int v_idx); virtual bool _isCycleAromatic (const int *cycle, int cycle_len) = 0; virtual void _handleAromaticCycle (const int *cycle, int cycle_len); virtual bool _acceptOutgoingDoubleBond (int atom, int bond) { return false; } protected: enum { MAX_CYCLE_LEN = 22 }; struct CycleDef { int id; bool is_empty; int length; int cycle[MAX_CYCLE_LEN]; }; BaseMolecule &_basemol; CP_DECL; TL_CP_DECL(Array<byte>, _bonds_arom); TL_CP_DECL(Array<int>, _bonds_arom_count); TL_CP_DECL(Array<CycleDef>, _unsure_cycles); TL_CP_DECL(Array<int>, _cycle_atoms); int _cycle_atoms_mark; bool _checkDoubleBonds (const int *cycle, int cycle_len); void _aromatizeCycle (const int *cycle, int cycle_len); void _handleCycle (const Array<int> &vertices); static bool _cb_check_vertex (Graph &graph, int v_idx, void *context); static bool _cb_handle_cycle (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); int _cyclesHandled; int _unsureCyclesCount; }; class DLLEXPORT MoleculeAromatizer : public AromatizerBase { public: // Interface function for aromatization static bool aromatizeBonds (Molecule &mol, const AromaticityOptions &options); MoleculeAromatizer (Molecule &molecule, const AromaticityOptions &options); void precalculatePiLabels (); static void findAromaticAtoms (BaseMolecule &mol, Array<int> *atoms, Array<int> *bonds, const AromaticityOptions &options); protected: virtual bool _checkVertex (int v_idx); virtual bool _isCycleAromatic (const int *cycle, int cycle_len); virtual bool _acceptOutgoingDoubleBond (int atom, int bond); int _getPiLabel (int v_idx); int _getPiLabelByConn (int v_idx, int conn); AromaticityOptions _options; CP_DECL; TL_CP_DECL(Array<int>, _pi_labels); }; class QueryMoleculeAromatizer : public AromatizerBase { public: // Interface function for query molecule aromatization static bool aromatizeBonds (QueryMolecule &mol, const AromaticityOptions &options); enum { EXACT, FUZZY }; QueryMoleculeAromatizer (QueryMolecule &molecule, const AromaticityOptions &options); void setMode (int mode); void precalculatePiLabels (); protected: struct PiValue { PiValue () {} PiValue (int min, int max) : min(min), max(max) {} bool canBeAromatic () { return min != -1; } int min, max; }; virtual bool _checkVertex (int v_idx); virtual bool _isCycleAromatic (const int *cycle, int cycle_len); virtual void _handleAromaticCycle (const int *cycle, int cycle_len); virtual bool _acceptOutgoingDoubleBond (int atom, int bond); static bool _aromatizeBondsExact (QueryMolecule &mol, const AromaticityOptions &options); static bool _aromatizeBondsFuzzy (QueryMolecule &mol, const AromaticityOptions &options); static bool _aromatizeBonds (QueryMolecule &mol, int additional_atom, const AromaticityOptions &options); static bool _aromatizeRGroupFragment (QueryMolecule &fragment, bool add_single_bonds, const AromaticityOptions &options); PiValue _getPiLabel (int v_idx); CP_DECL; TL_CP_DECL(Array<PiValue>, _pi_labels); TL_CP_DECL(Array<CycleDef>, _aromatic_cycles); int _mode; bool _collecting; AromaticityOptions _options; }; // Structure that keeps query infromation abount bonds that // can be aromatic in the substructure search. class DLLEXPORT QueryMoleculeAromaticity { public: bool canBeAromatic (int edge_index) const; void setCanBeAromatic (int edge_index, bool state); void clear(); private: Array<bool> can_bond_be_aromatic; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �����������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_arom_match.h��������������������������������������������������0000664�0000000�0000000�00000005341�12710376503�0022161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_arom_match_h__ #define __molecule_arom_match_h__ #include "base_cpp/tlscont.h" #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "molecule/molecule.h" namespace indigo { class Molecule; class QueryMolecule; /* * Aromaticity matcher handles cases of the query possible aromatic cycles. * Some cycles in the query molecule can be aromatic or not depending on the * embedding to the target molecule. For example [#7]C1=C~[#6]~[#6]~C=C1O * query molecule should match both NC1=CCCC=C1O and Nc1ccccc1O target * molecules. Only aromatic bonds can match aromatic bonds, so to match * aromatic bond query must have aromatic realization (assignment exact values * to the any bond and any atoms that leads cycle to be aromatic). */ class AromaticityMatcher { public: AromaticityMatcher (QueryMolecule &query, BaseMolecule &base, const AromaticityOptions &arom_options); // Check if aromaticity matcher is necessary for specified query static bool isNecessary (QueryMolecule &query); // Update internal structures when query molecule changes (grow) void validateQuery (); // Check if query bond can be aromatic if 'aromatic' is true and // nonaromatic otherwise. bool canFixQueryBond (int query_edge_idx, bool aromatic); // Fix query bond to aromatic or nonaromatic state void fixQueryBond (int query_edge_idx, bool aromatic); // Unfix query bond (opposite to fixQueryBond) void unfixQueryBond (int query_edge_idx); // Unfix all neighbour bonds void unfixNeighbourQueryBond (int query_arom_idx); // Check if embedding is possible. 'core_sub' corresponds // to the mapping from the query to the target. Vertices // with negative values are ignored. 'core_super' is // an inverse mapping for 'core_sub'. bool match (int *core_sub, int *core_super); DECL_ERROR; protected: QueryMolecule &_query; BaseMolecule &_base; AromaticityOptions _arom_options; enum { ANY = 0, AROMATIC, NONAROMATIC }; CP_DECL; TL_CP_DECL(Array<int>, _matching_edges_state); AutoPtr<BaseMolecule> _submolecule; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_auto_loader.h�������������������������������������������������0000664�0000000�0000000�00000003766�12710376503�0022356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_auto_loader__ #define __molecule_auto_loader__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/red_black.h" #include "base_cpp/properties_map.h" #include "molecule/molecule_stereocenter_options.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Scanner; class Molecule; class QueryMolecule; class BaseMolecule; class DLLEXPORT MoleculeAutoLoader { public: MoleculeAutoLoader (Scanner &scanner); MoleculeAutoLoader (const Array<char> &arr); MoleculeAutoLoader (const char *str); ~MoleculeAutoLoader (); void loadMolecule (Molecule &mol); void loadQueryMolecule (QueryMolecule &qmol); StereocentersOptions stereochemistry_options; bool ignore_cistrans_errors; bool ignore_closing_bond_direction_mismatch; bool ignore_noncritical_query_features; bool treat_x_as_pseudoatom; bool skip_3d_chirality; // Loaded properties CP_DECL; TL_CP_DECL(PropertiesMap, properties); DECL_ERROR; static bool tryMDLCT (Scanner &scanner, Array<char> &outbuf); protected: Scanner *_scanner; bool _own_scanner; void _init (); bool _isSingleLine (); void _loadMolecule (BaseMolecule &mol, bool query); private: MoleculeAutoLoader (const MoleculeAutoLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������Indigo-indigo-1.2.3/molecule/molecule_automorphism_search.h�����������������������������������������0000664�0000000�0000000�00000011433�12710376503�0024122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_automorphism_search__ #define __molecule_automorphism_search__ #include "graph/automorphism_search.h" namespace indigo { class Molecule; class MoleculeStereocenters; class CancellationHandler; class MoleculeAutomorphismSearch : public AutomorphismSearch { public: MoleculeAutomorphismSearch (); virtual ~MoleculeAutomorphismSearch () {} void process (Molecule &mol); // By default all stereocenters and cis-trans bonds are treated as valid. bool detect_invalid_stereocenters; bool detect_invalid_cistrans_bonds; bool find_canonical_ordering; // By default all the atoms with undefinded number of hydrogens throws an exception. // If allow_undefined is true then such atoms are simply uncomparable and some // symmetries might be lost. bool allow_undefined; bool invalidCisTransBond (int idx); bool invalidStereocenter (int idx); // Bonds indices that will be checked for possible cis-trans property. // After calling "process" method all bonds, that cannot be cis-trans // will be removed from this array. Array<int> possible_cis_trans_to_check; DECL_ERROR; DECL_TIMEOUT_EXCEPTION; protected: static int _vertex_cmp (Graph &graph, int v1, int v2, const void *context); static int _edge_rank (Graph &graph, int edge_idx, const void *context); static bool _check_automorphism (Graph &graph, const Array<int> &mapping, const void *context); static bool _isCisTransBondMappedRigid (Molecule &mol, int i, const int *mapping); static bool _isStereocenterMappedRigid (const MoleculeStereocenters &stereocenters, int i, const int *mapping); static int _compare_mapped (Graph &graph, const Array<int> &mapping1, const Array<int> &mapping2, const void *context); static void _automorphismCallback (const int *automorphism, void *context); static int _compareRSites (Molecule &mol, int v1, int v2, const void *context); static int _compareStereo (Molecule &mol, int v1, int v2, const void *context); int _compareMappedStereocenters (Molecule &mol, const Array<int> &mapping1, const Array<int> &mapping2, const Array<int> &inv_mapping1, const Array<int> &inv_mapping2) const; bool _checkStereocentersAutomorphism (Molecule &mol, const Array<int> &mapping) const; void _initialize (Molecule &mol); void _calculateHydrogensAndDegree (Molecule &mol); void _getFirstApproximation (Molecule &mol); int _validCisTransBond (int idx, const Array<int> &orbits); int _validStereocenter (int idx, Array<int> &orbits, int *parity = 0); int _validStereocenterByAtom (int atom_idx, Array<int> &orbits, int *parity = 0); int _treat_undef_as; int _getStereo (int state) const; bool _findInvalidStereo (Molecule &mol); bool _findInvalidStereoCisTrans (Molecule &mol); void _markValidOrInvalidStereo (bool find_valid, Array<int> &approximation_orbits, bool *found); void _findCisTransStereoBondParirties (Molecule &mol); bool _hasStereo (Molecule &mol); void _markComplicatedStereocentersAsValid (Molecule &mol); bool _checkCisTransInvalid (Molecule &mol, int bond_idx); void _findAllPossibleCisTrans (Molecule &mol); void _findAllPossibleCisTransOneStep (Molecule &mol); struct EdgeInfo { int mapped_vertex; int edge; }; static void _getSortedNei (Graph &g, int v, Array<EdgeInfo> &sorted_nei, Array<int>& inv_mapping); int _getMappedBondOrderAndParity (Molecule &m, int e, Array<int>& inv_mapping) const; TL_CP_DECL(Array<int>, _approximation_orbits); TL_CP_DECL(Array<int>, _approximation_orbits_saved); TL_CP_DECL(Array<int>, _hcount); TL_CP_DECL(Array<int>, _cistrans_stereo_bond_parity); TL_CP_DECL(Array<int>, _degree); TL_CP_DECL(Array<int>, _independent_component_index); enum { _NO_STEREO = -1, _INVALID, _VALID, _UNDEF }; TL_CP_DECL(Array<int>, _stereocenter_state); TL_CP_DECL(Array<int>, _cistrans_bond_state); // Target stereocenters and cis-trans bond for checking permutation parity int _target_stereocenter, _target_bond; bool _target_stereocenter_parity_inv, _target_bond_parity_inv; int _fixed_atom; CancellationHandler *_cancellation_handler; }; } #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cdx_loader.h��������������������������������������������������0000664�0000000�0000000�00000006251�12710376503�0022154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cdx_loader__ #define __molecule_cdx_loader__ #include "base_cpp/exception.h" #include "base_cpp/obj.h" #include "molecule/base_molecule.h" #include "molecule/molecule_stereocenter_options.h" typedef unsigned short int UINT16; typedef int INT32; typedef unsigned int UINT32; #include "molecule/CDXConstants.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Scanner; class Molecule; class DLLEXPORT MoleculeCdxLoader { public: DECL_ERROR; explicit MoleculeCdxLoader (Scanner &scanner); void loadMolecule (Molecule &mol); StereocentersOptions stereochemistry_options; const float COORD_COEF; CP_DECL; TL_CP_DECL(RedBlackStringObjMap< Array<char> >, properties); protected: struct _ExtConnection { int bond_id; int point_id; int atom_id; }; struct _NodeDesc { int id; int type; int label; int isotope; int charge; int radical; int valence; int hydrogens; int stereo; int enchanced_stereo; int stereo_group; int x; int y; int z; int index; Array<_ExtConnection> connections; }; struct _BondDesc { int id; int beg; int end; int type; int stereo; int dir; int index; bool swap_bond; }; Scanner *_scanner; TL_CP_DECL(Array<_NodeDesc>, _nodes); TL_CP_DECL(Array<_BondDesc>, _bonds); TL_CP_DECL(Array<int>, _stereo_care_atoms); TL_CP_DECL(Array<int>, _stereo_care_bonds); TL_CP_DECL(Array<int>, _stereocenter_types); TL_CP_DECL(Array<int>, _stereocenter_groups); TL_CP_DECL(Array<int>, _sensible_bond_directions); TL_CP_DECL(Array<int>, _ignore_cistrans); void _checkHeader (); void _loadMolecule (); void _updateConnectionPoint (int point_id, int atom_id); void _postLoad (); void _readFragment (UINT32 fragmment_id); void _readNode (UINT32 node_id); void _readBond (UINT32 bond_id); void _skipObject (); void _read2DPosition (int &x, int &y); void _read3DPosition (int &x, int &y, int &z); int _getElement (); int _getCharge (int size); int _getRadical (); int _getBondType (); int _getBondDirection (bool &swap_bond); void _getBondOrdering (int size, Array<_ExtConnection> &cons); void _getConnectionOrder (int size, Array<_ExtConnection> &cons); Molecule *_mol; BaseMolecule *_bmol; private: MoleculeCdxLoader (const MoleculeCdxLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cdx_saver.h���������������������������������������������������0000664�0000000�0000000�00000002027�12710376503�0022023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cdx_saver_h__ #define __molecule_cdx_saver_h__ namespace indigo { class Molecule; class Output; class DLLEXPORT MoleculeCdxSaver { public: explicit MoleculeCdxSaver (Output &output); void saveMolecule (Molecule &mol); DECL_ERROR; protected: Molecule *_mol; Output &_output; private: MoleculeCdxSaver (const MoleculeCdxSaver &); // no implicit copy }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cdxml_saver.h�������������������������������������������������0000664�0000000�0000000�00000004124�12710376503�0022354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cdxml_saver_h__ #define __molecule_cdxml_saver_h__ #include "math/algebra.h" class TiXmlDocument; class TiXmlElement; namespace indigo { class Molecule; class Output; class DLLEXPORT MoleculeCdxmlSaver { public: explicit MoleculeCdxmlSaver (Output &output); void saveMolecule (Molecule &mol); enum { BOND_LENGTH = 30 }; struct Bounds { Vec2f min, max; }; void beginDocument (Bounds *bounds); void beginPage (Bounds *bounds); void addFontTable(const char* font); void addFontToTable(int id, const char* charset, const char* name); void addColorTable(const char* color); void addColorToTable(int id, int r, int g, int b); void saveMoleculeFragment (Molecule &mol, const Vec2f &offset, float scale); void addText (const Vec2f &pos, const char *text); void addText (const Vec2f &pos, const char *text, const char *alignment); void addCustomText(const Vec2f &pos, const char *alignment, float line_height, const char *text); void endPage (); void endDocument (); float pageHeight () const; float textLineHeight () const; DECL_ERROR; private: Output &_output; float _bond_length; int _pages_height; float _max_page_height; TiXmlDocument * _doc; TiXmlElement * _root; TiXmlElement * _page; TiXmlElement * _current; TiXmlElement * _fonttable; TiXmlElement * _colortable; MoleculeCdxmlSaver (const MoleculeCdxmlSaver &); // no implicit copy }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_chain_fingerprints.h������������������������������������������0000664�0000000�0000000�00000003515�12710376503�0023724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_chain_fingerprints__ #define __molecule_chain_fingerprints__ #include "base_cpp/tlscont.h" #include "graph/graph_subchain_enumerator.h" namespace indigo { struct MoleculeChainFingerprintParameters { MoleculeChainFingerprintParameters () { size_qwords = 128; min_edges = 1; max_edges = 7; bits_per_chain = 4; mode = GraphSubchainEnumerator::MODE_NO_DUPLICATE_VERTICES; } int size_qwords; // size in bytes = size_qwords * 8 int min_edges; int max_edges; int bits_per_chain; int mode; // one of GraphSubchainEnumerator::MODE_XXX }; class Molecule; class Graph; class MoleculeChainFingerprintBuilder { public: MoleculeChainFingerprintBuilder (Molecule &mol, const MoleculeChainFingerprintParameters ¶meters); void process (); const byte * get (); DECL_ERROR; protected: static void _handleChain (Graph &graph, int size, const int *vertices, const int *edges, void *context); Molecule &_mol; const MoleculeChainFingerprintParameters &_parameters; CP_DECL; TL_CP_DECL(Array<byte>, _fingerprint); private: MoleculeChainFingerprintBuilder (const MoleculeChainFingerprintBuilder &); // no implicit copy }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cis_trans.h���������������������������������������������������0000664�0000000�0000000�00000007413�12710376503�0022036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cis_trans__ #define __molecule_cis_trans__ #include "base_cpp/red_black.h" #include "math/algebra.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class Filter; class DLLEXPORT MoleculeCisTrans { public: enum { CIS = 1, TRANS = 2 }; void clear (); void build (int *exclude_bonds); void buildFromSmiles (int *dirs); bool exists () const; int count (); void setParity (int bond_idx, int parity); int getParity (int bond_idx) const; bool isIgnored (int bond_idx) const; void ignore (int bond_idx); void registerBond (int idx); void flipBond (int atom_parent, int atom_from, int atom_to); const int * getSubstituents (int bond_idx) const; void getSubstituents_All (int bond_idx, int subst[4]); void add (int bond_idx, int substituents[4], int parity); bool registerBondAndSubstituents (int idx); int applyMapping (int idx, const int *mapping, bool sort) const; static int applyMapping (int parity, const int *substituents, const int *mapping, bool sort); // Returns -2 if mapping is not valid static int getMappingParitySign (BaseMolecule &query, BaseMolecule &target, int bond_idx, const int *mapping); static bool checkSub (BaseMolecule &query, BaseMolecule &target, const int *mapping); void buildOnSubmolecule (BaseMolecule &super, int *mapping); static bool sortSubstituents (BaseMolecule &mol, int *substituents, bool *parity_changed); void restoreSubstituents (int bond_idx); void registerUnfoldedHydrogen (int atom_idx, int added_hydrogen); static bool isAutomorphism (BaseMolecule &mol, const Array<int> &mapping, const Filter *edge_filter = NULL); bool isRingTransBond (int bond_idx); bool convertableToImplicitHydrogen (int idx); void validate (); DECL_ERROR; static bool isGeomStereoBond (BaseMolecule &mol, int bond_idx, int *substituents, bool have_xyz); static int sameside (const Vec3f &beg, const Vec3f &end, const Vec3f &nei_beg, const Vec3f &nei_end); static bool sameline (const Vec3f &beg, const Vec3f &end, const Vec3f &nei_beg); bool sameside (int edge_idx, int v1, int v2); protected: BaseMolecule & _getMolecule (); struct _Bond { void clear () { parity = 0; ignored = 0; } int parity; // CIS ot TRANS int ignored; // explicitly ignored cis-trans configuration on this bond int substituents[4]; }; Array<_Bond> _bonds; static bool _pureH (BaseMolecule &mol, int idx); static int _sameside (BaseMolecule &mol, int i_beg, int i_end, int i_nei_beg, int i_nei_end); static bool _sameline (BaseMolecule &molecule, int i_beg, int i_end, int i_nei_beg); static int _getPairParity (int v1, int v2, const int *mapping, bool sort); static bool _commonHasLonePair (BaseMolecule &mol, int v1, int v2); static void _fillExplicitHydrogens (BaseMolecule &mol, int bond_idx, int subst[4]); static void _fillAtomExplicitHydrogens (BaseMolecule &mol, int atom_idx, int subst[2]); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cml_loader.h��������������������������������������������������0000664�0000000�0000000�00000003324�12710376503�0022147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cml_loader__ #define __molecule_cml_loader__ #include "base_cpp/exception.h" #include "base_cpp/array.h" #include "molecule/molecule_stereocenter_options.h" #include <string> #include <vector> #include <unordered_map> #include <sstream> class TiXmlHandle; class TiXmlElement; class TiXmlNode; namespace indigo { class Scanner; class Molecule; class MoleculeCmlLoader { public: DECL_ERROR; MoleculeCmlLoader (Scanner &scanner); MoleculeCmlLoader (TiXmlHandle &handle); void loadMolecule (Molecule &mol); StereocentersOptions stereochemistry_options; protected: Scanner *_scanner; TiXmlHandle *_handle; TiXmlNode *_molecule; void _loadMolecule (TiXmlHandle &handle, Molecule &mol); void _loadSGroup (TiXmlElement *elem, Molecule &mol, std::unordered_map<std::string, int> &atoms_id, int parent); void _loadRgroup (TiXmlHandle &handle, Molecule &mol); bool _findMolecule (TiXmlNode *node); void _parseRlogicRange (const char *str, Array<int> &ranges); private: MoleculeCmlLoader (const MoleculeCmlLoader &); // no implicit copy }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_cml_saver.h���������������������������������������������������0000664�0000000�0000000�00000002357�12710376503�0022026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_cml_saver_h__ #define __molecule_cml_saver_h__ class TiXmlDocument; class TiXmlElement; namespace indigo { class Molecule; class Output; class SGroup; class MoleculeCmlSaver { public: explicit MoleculeCmlSaver (Output &output); void saveMolecule (Molecule &mol); bool skip_cml_tag; // skips <?xml> and <cml> tags DECL_ERROR; protected: void _addSgroupElement (TiXmlElement *elem, SGroup &sgroup); Molecule *_mol; Output &_output; TiXmlDocument * _doc; TiXmlElement * _root; private: MoleculeCmlSaver (const MoleculeCmlSaver &); // no implicit copy }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_dearom.h������������������������������������������������������0000664�0000000�0000000�00000025256�12710376503�0021325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_dearom_h__ #define __molecule_dearom_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "base_cpp/tlscont.h" #include "base_cpp/gray_codes.h" #include "base_cpp/d_bitset.h" #include "graph/graph_perfect_matching.h" #include "molecule/molecule_arom.h" namespace indigo { class BaseMolecule; class Molecule; class Scanner; class Output; DECL_EXCEPTION(DearomatizationException); DECL_EXCEPTION2(NonUniqueDearomatizationException, DearomatizationException); // Storage for dearomatizations class DearomatizationsStorage { friend class DearomatizationsStorageWrapper; public: DECL_ERROR2(DearomatizationException); explicit DearomatizationsStorage (); void clear (void); void clearIndices (void); void clearBondsState (void); void setGroupsCount (int groupsCount); void setGroup (int group, int boundsCount, const int *bonds, int heteroAtomsCount, const int *hetroAtoms); void addGroupDearomatization (int group, const byte *dearomBondsState); void addGroupHeteroAtomsState (int group, const byte *heteroAtomsState); int getGroupDearomatizationsCount (int group) const; byte* getGroupDearomatization (int group, int dearomatizationIndex); const int* getGroupBonds (int group) const; int getGroupBondsCount (int group) const; int getGroupHeterAtomsStateCount (int group) const; const byte* getGroupHeterAtomsState (int group, int index) const; const int* getGroupHeteroAtoms (int group) const; int getGroupHeteroAtomsCount (int group) const; int getGroupsCount (void) const; void saveBinary (Output &output) const; void loadBinary (Scanner &scanner); int getDearomatizationParams (void) { return _dearomParams; } void setDearomatizationParams (int params) { _dearomParams = params; } protected: struct PseudoArray { int count; int offset; }; struct Group { PseudoArray aromBondsIndices; PseudoArray dearomBondsState; PseudoArray heteroAtomsIndices; PseudoArray heteroAtomsState; }; protected: Array<int> _aromBondsArray; // Bonds used in this connectivity group Array<int> _heteroAtomsIndicesArray; // Heteroatoms indices Array<Group> _aromaticGroups; // Data for I/O Array<byte> _dearomBondsStateArray; // Array of array of dearomatization configuration Array<byte> _heteroAtomsStateArray; // States for heteroatoms byte _dearomParams; }; // Class for handling aromatic groups in molecule (contains helpful functions) class DearomatizationsGroups { public: // Constants for Prepare function enum { GET_HETERATOMS_INDICES = 0x01, GET_VERTICES_FILTER = 0x02 }; struct GROUP_DATA { Array<int> bonds; Array<int> bondsInvMapping; Array<int> vertices; Array<int> verticesFilter; Array<int> heteroAtoms; Array<int> heteroAtomsInvMapping; }; public: DearomatizationsGroups (BaseMolecule &molecule); // for flags see GET_*** void getGroupData (int group, int flags, GROUP_DATA *data); // Construct bondsInvMapping, vertices and heteroAtomsInvMapping void getGroupDataFromStorage (DearomatizationsStorage &storage, int group, GROUP_DATA *data); int detectAromaticGroups (const int *atom_external_conn); void constructGroups (DearomatizationsStorage &storage, bool needHeteroAtoms); bool* getAcceptDoubleBonds (void); bool isAcceptDoubleBond (int atom); DECL_ERROR2(DearomatizationException); protected: void _detectAromaticGroups (int v_idx, const int *atom_external_conn); int _getFixedConnectivitySpecific (int label, int charge, int min_conn, int n_arom); protected: BaseMolecule &_molecule; int _aromaticGroups; // Additional data stored here to prevent reallocatoins CP_DECL; TL_CP_DECL(Array<int>, _vertexAromaticGroupIndex); TL_CP_DECL(Array<bool>, _vertexIsAcceptDoubleEdge); TL_CP_DECL(Array<bool>, _vertexIsAcceptSingleEdge); TL_CP_DECL(Array<int>, _vertexProcessed); TL_CP_DECL(Array<int>, _groupVertices); TL_CP_DECL(Array<int>, _groupEdges); TL_CP_DECL(Array<int>, _groupHeteroAtoms); TL_CP_DECL(GROUP_DATA, _groupData); }; // Molecule dearomatization class. class Dearomatizer { public: enum { PARAMS_NO_DEAROMATIZATIONS, PARAMS_SAVE_ALL_DEAROMATIZATIONS, // Store all dearomatizations PARAMS_SAVE_ONE_DEAROMATIZATION, // Store just one dearomatization for every heteroatom configuration PARAMS_SAVE_JUST_HETERATOMS // Store just heteroatoms configuration }; public: explicit Dearomatizer (BaseMolecule &molecule, const int *atom_external_conn, const AromaticityOptions &options); virtual ~Dearomatizer (); void enumerateDearomatizations (DearomatizationsStorage &dearomatizations); static void setDearomatizationParams (int params); protected: class GraphMatchingFixed : public GraphPerfectMatching { public: GraphMatchingFixed (BaseMolecule &molecule); void setFixedInfo (const Dbitset *edgesFixed, const Dbitset *verticesFixed); virtual bool checkVertex (int v_idx); virtual bool checkEdge (int e_idx); protected: const Dbitset *_edgesFixed; const Dbitset *_verticesFixed; }; protected: GraphMatchingFixed _graphMatching; BaseMolecule &_molecule; const AromaticityOptions &_options; int _connectivityGroups; int _activeGroup; DearomatizationsGroups _aromaticGroups; DearomatizationsStorage *_dearomatizations; CP_DECL; TL_CP_DECL(DearomatizationsGroups::GROUP_DATA, _aromaticGroupData); /*TL_CP_DECL(*/Dbitset/*, */_edgesFixed/*)*/; /*TL_CP_DECL(*/Dbitset/*, */_verticesFixed/*)*/; TL_CP_DECL(Array<int>, _submoleculeMapping); protected: void _initEdges (void); void _initVertices (void); void _prepareGroup (int group, Molecule &submolecule); void _fixHeteratom (int atom_idx, bool toFix); void _processMatching (Molecule &submolecule, int group, const byte* hetroAtomsState); void _enumerateMatching (void); void _handleMatching (void); }; // Dearomatization matcher with delayed initialization class DearomatizationMatcher { public: DECL_ERROR2(DearomatizationException); DearomatizationMatcher (DearomatizationsStorage &dearomatizations, BaseMolecule &molecule, const int *atom_external_conn); bool isAbleToFixBond (int edge_idx, int type); bool fixBond (int edge_idx, int type); void unfixBond (int edge_idx); void unfixBondByAtom (int atom_idx); protected: void _prepare (void); void _prepareGroup (int group); void _generateUsedVertices (void); bool _tryToChangeActiveIndex (int dearom_idx, int group, byte *groupEdgesPtr, byte *groupEdgesStatePtr); bool _fixBondInMatching (int group, int indexInGroup, int type); protected: struct GroupExData { int offsetInEdgesState; // Offset in matched edges state int activeEdgeState; int offsetInVertices; int verticesUsed; bool needPrepare; }; // Graph edge matching class to support current dearomatization class GraphMatchingEdgeFixed : public GraphPerfectMatching { public: GraphMatchingEdgeFixed (BaseMolecule &molecule); void setExtraInfo (byte *edgesEdges); virtual bool checkEdge (int e_idx); protected: byte *_edgesState; }; // Graph edge matching class to find dearomatization by heteroatoms state class GraphMatchingVerticesFixed : public GraphPerfectMatching { public: GraphMatchingVerticesFixed (BaseMolecule &molecule); void setVerticesState (const byte *verticesState); void setVerticesMapping (int *verticesMapping); void setVerticesAccept (bool *verticesAcceptDoubleBond); virtual bool checkVertex (int v_idx); protected: const byte *_verticesState; int *_verticesMapping; bool *_verticesAcceptDoubleBond; }; protected: BaseMolecule &_molecule; DearomatizationsStorage &_dearomatizations; GraphMatchingEdgeFixed _graphMatchingFixedEdges; DearomatizationsGroups _aromaticGroups; CP_DECL; TL_CP_DECL(Array<byte>, _matchedEdges); // Edges that have already been matched TL_CP_DECL(Array<byte>, _matchedEdgesState); // State of such edges TL_CP_DECL(Array<GroupExData>, _groupExInfo); // Additional data for group TL_CP_DECL(Array<int>, _verticesInGroup); TL_CP_DECL(Dbitset, _verticesAdded); TL_CP_DECL(Array<int>, _edges2GroupMapping); TL_CP_DECL(Array<int>, _edges2IndexInGroupMapping); TL_CP_DECL(Array<byte>, _correctEdgesArray); TL_CP_DECL(Array<int>, _verticesFixCount); TL_CP_DECL(DearomatizationsGroups::GROUP_DATA, _aromaticGroupsData); bool _needPrepare; int _lastAcceptedEdge; int _lastAcceptedEdgeType; }; class MoleculeDearomatizer { public: MoleculeDearomatizer (Molecule &mol, DearomatizationsStorage &dearomatizations); // Function dearomatizes as much as possible. // Returns true if all bonds were dearomatized, false overwise static bool dearomatizeMolecule (Molecule &mol, const AromaticityOptions &options); static bool restoreHydrogens (Molecule &mol, const AromaticityOptions &options); static bool restoreHydrogens (Molecule &mol, bool unambiguous_only); void dearomatizeGroup (int group, int dearomatization_index); void restoreHydrogens (int group, int dearomatization_index); private: DearomatizationsStorage &_dearomatizations; Molecule &_mol; int _countDoubleBonds (int group, int dearomatization_index); int _getBestDearomatization (int group); CP_DECL; TL_CP_DECL(Array<int>, vertex_connectivity); }; } #endif // __molecule_dearom_h__ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_electrons_localizer.h�����������������������������������������0000664�0000000�0000000�00000016727�12710376503�0024123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_electrons_localizer__ #define __molecule_electrons_localizer__ #include "base_cpp/array.h" #include "base_cpp/obj.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" #include "graph/graph_constrained_bmatching_finder.h" namespace indigo { class BaseMolecule; class Molecule; class Graph; // This class localizes specified electrons on molecule skeleton. // Localization parameters are: number of double bonds and // number of primary and secondary lonepairs. // The more secondary lonepairs are specified, the bigger charge // is assigned to the atom. To minimize the number of atoms with // charges user should find valid configuration with minimum // number of secondary lonepairs. // // During localization only atom labels, radicals, and implicit // hydrogens are taken into account. // // The algorithm works only with p-orbitals. Currently, atoms // with valences more than 4 can't be handled. // // * Implementation details * // For skeleton specified algorithm construct extended skeleton by // adding and connecting lonepairs node to each atom. Every valid electron // configuration is mapped to the b-matching in the extended skeleton. // Atom capacity in b-matching is divided into two groups: primary and secondary. // If the atom is saturated with primary capacity and have zero value with secondary // capapacity, then zero charge is assigned to the atom. If atom is saturated with primary // and secondary capacities, the full octet is assigned to the atom. class MoleculeElectronsLocalizer { public: MoleculeElectronsLocalizer (Molecule &skeleton); void setParameters (int double_bonds, int primary_lonepairs, int secondary_lonepairs); // Perform localization and return true if localization is possible // If 'only_check_possibility' is true that possibility of localization // with specified double bonds and lonepairs is checked without dividing // the lonepairs into primary and secondary groups. bool localize (bool only_check_possibility = false); // Copy localized bonds and charges infomation to the specifed molecule. // 'dest' molecule must have the same structure as 'skeleton' molecule. void copyBondsAndCharges (Molecule &dest, const Array<int> &mapping) const; // Methods for adding constraints for atom charges and bonds. // For better performance fix bonds first. bool fixAtomCharge (int atom, int charge); bool fixAtomConnectivity (int atom, int connectivity); bool fixBond (int bond, int type); bool fixBondSingleDouble (int bond); void unfixAll (); void unfixAtom (int atom); void unfixBond (int bond); int getZeroChargeLonepairs () const { return _zc_lonepairs; } int getLocalizationChargesCount () const; bool isAllAtomsHaveOctet () const; DECL_ERROR; private: void _construct (); void _constructBMatchingFinder (); // Calculate max_connectivity, zc_connectivity and zc_lonepairs void _setupAtomProperties (); void _setupBMatchingNodes (); void _setupBMatchingEdges (); void _setupBMatchingNode (int atom); void _setupBMatchingNodeAtom (int atom); void _setupBMatchingNodeOrbital (int atom); bool _fixAtomConnectivityAndLonepairs (int atom, int added_connectivity, int lonepairs); void _unfixAtomConnectivityAndLonepairs (int atom); void _fixAtomSaturated (int atom); void _unfixAtomSaturated (int atom); // Split connectivity (lonepairs) into primary and secondary void _splitConnectivity (int atom, int conn, int *prim, int *sec) const; void _splitLonepairs (int atom, int lonepairs, int *prim, int *sec) const; // Calculate connectivity and number of lonepairs // for specifed number of electrons bool _calcConnectivityAndLoneparis (int atom, int charge, int *conn, int *lp); // Check whether atom can atom be not saturated bool _canAtomBeUnsaturated (int atom); // Find localization with all atom localized validly bool _findValidSolution (int cardinality); // Check is electrons localization for atom is valid enum { OK = 0, LONEPAIRS, CONNECTIVITY }; int _isLocalizationValid (int atom) const; bool _branchOnLonepairs (int cardinality, int invalid_atom); bool _branchOnConnectivity (int cardinality, int invalid_atom); // Set constraint set parameters for atoms and lonepairs bool _setConstraintSetForAtoms (); bool _setConstraintSetForLonepairs (bool only_check_possibility); void _updateAtomBondFixed (int atom, int bond_type, bool fixed); bool _checkAtomBondFixed (int atom, int bond_type); int _getAtomCharge (int atom) const; void _getAtomConnAndLonepairs (int atom, int &added_conn, int &lonepairs) const; Obj<GraphConstrainedBMatchingFinder> _finder; Molecule &_skeleton; // Localization parameters int _double_bonds, _primary_lonepairs, _secondary_lonepairs; // Nodes constraint sets for constrained b-matching finder enum { _PRIMARY_ATOMS_SET, _SECONDARY_ATOMS_SET, _SUM_ATOMS_SET, _CONSTRAINED_ATOMS_SET, _PRIMARY_LONEPAIRS_SET, _SECONDARY_LONEPAIRS_SET, _CONSTRAINED_LONEPAIRS_SET, _SUM_LONEPAIRS_SET, _SET_MAX }; struct _AtomInfo { int atom_node, orbitals_node; int orbitals_edge; // Atom have fixed number of lonepairs and connectivity bool atom_fixed; bool atom_connectivity_fixed; // Atoms in 6-th and 7-th groups must be saturated in any localization. // This state is true if atom must have full octet in any localization. bool atom_saturated; // Current maximum additional connectivity (atom capacity) int max_add_connectivity; // Maximum additional connectivity without constrained bonds int max_add_connectivity0; // Connectivity and lonepairs for atom with zero charge (zc_) int zc_connectivity, zc_lonepairs; // Information for fixed atoms int fixed_connectivity, fixed_lonepairs; // Connectivity with skeleton + number of bonds fixed int skeleton_connectivity; }; // Summary double bonds and lonepairs for zero charge int _zc_atoms_connectivity, _zc_lonepairs; // Constraints parameters int _constrained_primary_double_bonds_conn, _constrained_secondary_double_bonds_conn, _constrained_primary_lonepairs, _constrained_secondary_lonepairs, _constrained_primary_atoms, _constrained_secondary_atoms, _constrained_saturated_atoms; CP_DECL; // Molecule skeleton with orbitals nodes attached TL_CP_DECL(Graph, _extended_skeleton); // Edge mapping between skeleton and extended skeleton TL_CP_DECL(Array<int>, _edge_mapping); // Additional information per atom for extracting data from // matching in extended skeleton graph TL_CP_DECL(Array<_AtomInfo>, _atom_info); // Array with fixed edges TL_CP_DECL(Array<int>, _edges_fixed_type); }; } #endif // __molecule_electrons_localizer__ �����������������������������������������Indigo-indigo-1.2.3/molecule/molecule_exact_matcher.h�����������������������������������������������0000664�0000000�0000000�00000005605�12710376503�0022661�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_exact_matcher__ #define __molecule_exact_matcher__ #include "base_cpp/obj.h" #include "graph/embedding_enumerator.h" #include "graph/graph_decomposer.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class DLLEXPORT MoleculeExactMatcher { public: enum { // Conditions CONDITION_NONE = 0x0000, CONDITION_ELECTRONS = 0x0001, // bond types, atom charges, valences, radicals must match CONDITION_ISOTOPE = 0x0002, // atom isotopes must match CONDITION_STEREO = 0x0004, // tetrahedral and cis-trans configurations must match CONDITION_FRAGMENTS = 0x0008, // query fragments count must be equal to target fragments count CONDITION_ALL = 0x000F, // all but 3D CONDITION_3D = 0x0010 // atom positions must match up to affine+scale transformation }; MoleculeExactMatcher (BaseMolecule &query, BaseMolecule &target); bool find (); const int * getQueryMapping (); static void parseConditions (const char *params, int &flags, float &rms_threshold); static bool matchAtoms (BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags); static bool matchBonds (BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags); int flags; float rms_threshold; // for affine match bool needCoords (); DECL_ERROR; protected: BaseMolecule &_query; BaseMolecule &_target; EmbeddingEnumerator _ee; Obj<GraphDecomposer> _query_decomposer; Obj<GraphDecomposer> _target_decomposer; struct _MatchToken { bool compare (const char *text) const; const char *t_text; int t_flag; }; static bool _matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); void _collectConnectedComponentsInfo (); private: MoleculeExactMatcher (const MoleculeExactMatcher &); }; } #endif ���������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_exact_substructure_matcher.h����������������������������������0000664�0000000�0000000�00000003762�12710376503�0025515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_exact_substructure_matcher__ #define __molecule_exact_substructure_matcher__ #include "base_cpp/obj.h" #include "graph/embedding_enumerator.h" #include "graph/graph_decomposer.h" namespace indigo { class Molecule; class MoleculeExactSubstructureMatcher { public: MoleculeExactSubstructureMatcher (Molecule &query, Molecule &target); bool find (); bool find_withHydrogens (); const int * getQueryMapping (); void ignoreTargetAtom (int idx); dword flags; DECL_ERROR; protected: Molecule &_query; Molecule &_target; EmbeddingEnumerator _ee; Obj<GraphDecomposer> _query_decomposer; Obj<GraphDecomposer> _target_decomposer; struct _MatchToken { bool compare (const char *text) const; const char *t_text; int t_flag; }; static bool _matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); void _collectConnectedComponentsInfo (); private: MoleculeExactSubstructureMatcher (const MoleculeExactSubstructureMatcher &); }; } #endif ��������������Indigo-indigo-1.2.3/molecule/molecule_fingerprint.h�������������������������������������������������0000664�0000000�0000000�00000014613�12710376503�0022400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_fingerprint__ #define __molecule_fingerprint__ #include "base_cpp/tlscont.h" #include "base_cpp/obj.h" #include "molecule/base_molecule.h" #include "base_cpp/cancellation_handler.h" #include "graph/subgraph_hash.h" #include <unordered_map> #include <limits.h> #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class TautomerSuperStructure; // Fingerprint consists of 5 parts: EXT + ORD + ANY + TAU + SIM. // EXT is always 3 bytes long, other parts' sizes are configured. // ORD, ANY, and SIM parts are build up from fragments. // Each fragments goes to: // SIM -- as long as it has no query atoms/bonds and is small enough // ORD -- as long as it has no query atoms/bonds // ANY (with bond types discarded) -- as long as it has no query atoms // ANY (with atom types discarded) -- as long as it has no query bonds // ANY (with atom and bond types discarded) -- always // TAU part is build up from a 'supermolecule' having some added bonds, // and with all bond types discarded // EXT part is build up from some element, isotope, and charge counters struct MoleculeFingerprintParameters { bool ext; int ord_qwords, any_qwords, tau_qwords, sim_qwords; int fingerprintSize () const { return (ext ? 3 : 0) + (ord_qwords + any_qwords + tau_qwords + sim_qwords) * 8; } int fingerprintSizeExt () const { return (ext ? 3 : 0); } int fingerprintSizeOrd () const { return ord_qwords * 8; } int fingerprintSizeSim () const { return sim_qwords * 8; } int fingerprintSizeTau () const { return tau_qwords * 8; } int fingerprintSizeAny () const { return any_qwords * 8; } int fingerprintSizeExtOrd () const { return (ext ? 3 : 0) + ord_qwords * 8;} int fingerprintSizeExtOrdSim () const { return (ext ? 3 : 0) + ord_qwords * 8 + sim_qwords * 8;} }; class DLLEXPORT MoleculeFingerprintBuilder { public: MoleculeFingerprintBuilder (BaseMolecule &mol, const MoleculeFingerprintParameters ¶meters); ~MoleculeFingerprintBuilder (); bool query; bool skip_ord; // don't build 'ordinary' part of the fingerprint bool skip_sim; // don't build 'similarity' part of the fingerprint bool skip_tau; // don't build 'tautomer' part of the fingerprint bool skip_ext; // don't build 'extra' part of the fingerprint bool skip_ext_charge; // don't store information about charges in 'extra' part bool skip_any_atoms; // don't build 'any atoms' part of the fingerprint bool skip_any_bonds; // don't build 'any bonds' part of the fingerprint bool skip_any_atoms_bonds; // don't build 'any atoms, any bonds' part of the fingerprint void process (); const byte * get (); byte * getOrd (); byte * getSim (); byte * getTau (); byte * getAny (); int countBits_Sim (); void (*cb_fragment) (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, bool use_atoms, bool use_bonds, dword hash); void parseFingerprintType(const char *type, bool query); CancellationHandler* cancellation; DECL_ERROR; protected: void _initHashCalculations (BaseMolecule &mol, const Filter &vfilter); static void _handleTree (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); static bool _handleCycle (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); int _atomCode (BaseMolecule &mol, int vertex_idx); int _bondCode (BaseMolecule &mol, int edge_idx); static int _maximalSubgraphCriteriaValue (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); void _handleSubgraph (Graph &graph, const Array<int> &vertices, const Array<int> &edges); dword _canonicalizeFragment (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, bool use_atoms, bool use_bonds, int *different_vertex_count); void _canonicalizeFragmentAndSetBits (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, bool use_atoms, bool use_bonds, int subgraph_type, dword &bits_to_set); void _makeFingerprint (BaseMolecule &mol); void _calcExtraBits (BaseMolecule &mol); void _setTauBits (const char *str, int nbits); void _setOrdBits (const char *str, int nbits); static void _setBits (dword hash, byte *fp, int size, int nbits); void _calculateFragmentVertexDegree (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges); int _calculateFragmentExternalConn (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges); BaseMolecule &_mol; const MoleculeFingerprintParameters &_parameters; // these parameters are indirectly passed to the callbacks TautomerSuperStructure *_tau_super_structure; bool _is_cycle; struct HashBits { HashBits (dword hash, int bits_per_fragment); bool operator== (const HashBits &right) const; dword hash; int bits_per_fragment; }; struct Hasher { size_t operator () (const HashBits &input) const; }; void _addOrdHashBits (dword hash, int bits_per_fragment); Obj<SubgraphHash> subgraph_hash; CP_DECL; TL_CP_DECL(Array<byte>, _total_fingerprint); TL_CP_DECL(Array<int>, _atom_codes); TL_CP_DECL(Array<int>, _bond_codes); TL_CP_DECL(Array<int>, _atom_codes_empty); TL_CP_DECL(Array<int>, _bond_codes_empty); TL_CP_DECL(Array<int>, _atom_hydrogens); TL_CP_DECL(Array<int>, _atom_charges); TL_CP_DECL(Array<int>, _vertex_connectivity); TL_CP_DECL(Array<int>, _fragment_vertex_degree); TL_CP_DECL(Array<int>, _bond_orders); typedef std::unordered_map<HashBits, int, Hasher> HashesMap; TL_CP_DECL(HashesMap, _ord_hashes); private: MoleculeFingerprintBuilder (const MoleculeFingerprintBuilder &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_inchi.h�������������������������������������������������������0000664�0000000�0000000�00000004543�12710376503�0021144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_inchi_h__ #define __molecule_inchi_h__ #include "base_cpp/tlscont.h" #include "base_cpp/array.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/exception.h" #include "molecule/molecule.h" #include "molecule/molecule_inchi_layers.h" #include "molecule/molecule_inchi_component.h" namespace indigo { class Output; class Graph; // Molecule InChI code constructor class class MoleculeInChI { public: explicit MoleculeInChI (Output &output); // InChI version. By default it is "Indigo=1.1" const char *prefix; // Save InChI code to the output void outputInChI (Molecule &mol); DECL_ERROR; private: // // Components compare methods // // Compare components. Returns DIFFERENCE_**** for the first found difference static int _cmpComponents (int &index1, int &index2, void *context); // // Printing // void _printInChI (); class _PrintLayerFuncBase { public: virtual ~_PrintLayerFuncBase() {} virtual void operator() (MoleculeInChICompoment &comp, Array<char> &result) = 0; }; template <typename Layer> class _ComponentLayerPrintFunction; bool _printInChILayer (_PrintLayerFuncBase &func, const char *delim, const char *multiplier, const char *layer_prefix); void _printInChIComponentCisTrans (MoleculeInChICompoment &comp, Array<char> &result); static void _normalizeMolecule (Molecule &mol); Output &_output; // Array with molecule components and InChI information and sorted indices CP_DECL; TL_CP_DECL(ReusableObjArray<MoleculeInChICompoment>, _components); TL_CP_DECL(Array<int>, _component_indices); }; } #endif // __molecule_inchi_h__ �������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_inchi_component.h���������������������������������������������0000664�0000000�0000000�00000004305�12710376503�0023222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_inchi_component_h__ #define __molecule_inchi_component_h__ #include "base_cpp/array.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/exception.h" #include "molecule/molecule.h" #include "molecule/molecule_inchi_layers.h" namespace indigo { // Whole InChI component with component molecule and InChI layers struct MoleculeInChICompoment { // Canonicaly-ordered molecule Molecule mol; // Layers MoleculeInChILayers::MainLayerFormula main_layer_formula; MoleculeInChILayers::MainLayerConnections main_layer_connections; MoleculeInChILayers::HydrogensLayer hydrogens_layer; MoleculeInChILayers::CisTransStereochemistryLayer cistrans_stereochemistry_layer; MoleculeInChILayers::TetrahedralStereochemistryLayer tetra_stereochemistry_layer; void construct (Molecule &original_component); void clear() {} void getCanonicalOrdering(Molecule &source_mol, Array<int> &mapping); static int cmpVertex(Graph &graph, int v1, int v2, const void *context); private: void _getCanonicalMolecule (Molecule &source_mol, Molecule &cano_mol); static int _cmpVertex (Graph &graph, int v1, int v2, const void *context); static int _cmpVertexStereo (Molecule &graph, int v1, int v2, const void *context); static int _cmpMappings (Graph &graph, const Array<int> &mapping1, const Array<int> &mapping2, const void *context); static bool _checkAutomorphism (Graph &graph, const Array<int> &mapping, const void *context); }; } #endif // __molecule_inchi_component_h__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_inchi_layers.h������������������������������������������������0000664�0000000�0000000�00000011151�12710376503�0022514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_inchi_layers_h__ #define __molecule_inchi_layers_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" #include "molecule/molecule_inchi_utils.h" namespace indigo { class Molecule; class Output; class MoleculeStereocenters; // Namespace with layers // Each layers are independent and contains all // nessesary information namespace MoleculeInChILayers { // Abtract layer class AbstractLayer { public: AbstractLayer (); virtual ~AbstractLayer () {}; // Method for constructing internal layer information void construct (Molecule &mol); DECL_ERROR; protected: Molecule& _getMolecule (); virtual void _construct () {}; private: Molecule *_mol; }; // Main layer formula class MainLayerFormula : public AbstractLayer { public: void printFormula (Array<char> &result); static int compareComponentsAtomsCountNoH (MainLayerFormula &comp1, MainLayerFormula &comp2); static int compareComponentsTotalHydrogensCount (MainLayerFormula &comp1, MainLayerFormula &comp2); protected: virtual void _construct (); private: Array<int> _atoms_count; void _printAtom (Output &output, int label) const; void _collectAtomsCount (); }; // Main layer connections class MainLayerConnections : public AbstractLayer { public: void printConnectionTable (Array<char> &result); int compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2); static int compareComponentsConnectionTables (MainLayerConnections &comp1, MainLayerConnections &comp2); protected: virtual void _construct (); private: Array<int> _connection_table; void _linearizeConnectionTable (); }; // Layer with hydrogens class HydrogensLayer : public AbstractLayer { public: static int compareComponentsHydrogens (HydrogensLayer &comp1, HydrogensLayer &comp2); bool checkAutomorphism (const Array<int> &mapping); int compareMappings (MoleculeInChIUtils::Mapping &m1, MoleculeInChIUtils::Mapping &m2); void print (Array<char> &result); protected: virtual void _construct (); private: // Number of immobile hydrogens for each atom Array<int> _per_atom_immobile; // Atom indices in the 'mol' to avoid vertexBegin/vertexEnd iterations // when comparing components Array<int> _atom_indices; // TODO: Mobile hydrogens, fixed hydrogens }; // Cis-trans stereochemistry class CisTransStereochemistryLayer : public AbstractLayer { public: void print (Array<char> &result); bool checkAutomorphism (const Array<int> &mapping); int compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2); static int compareComponents (CisTransStereochemistryLayer &comp1, CisTransStereochemistryLayer &comp2); protected: virtual void _construct (); private: Array<int> bond_is_cis_trans; }; // Tetrahedral stereochemistry class TetrahedralStereochemistryLayer : public AbstractLayer { public: void print (Array<char> &result); void printEnantiomers (Array<char> &result); bool checkAutomorphism (const Array<int> &mapping); int compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2); static int compareComponentsEnantiomers (TetrahedralStereochemistryLayer &comp1, TetrahedralStereochemistryLayer &comp2); static int compareComponents (TetrahedralStereochemistryLayer &comp1, TetrahedralStereochemistryLayer &comp2); private: int _getMappingSign (const MoleculeStereocenters &stereocenters, const MoleculeInChIUtils::Mapping *m, int index); int _getFirstSign (); }; }; } #endif // __molecule_inchi_layers_h__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_inchi_utils.h�������������������������������������������������0000664�0000000�0000000�00000004325�12710376503�0022362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_inchi_utils_h__ #define __molecule_inchi_utils_h__ #include "base_cpp/array.h" #include "base_cpp/exception.h" namespace indigo { class Molecule; // Utility class for InChI code creation class MoleculeInChIUtils { public: // Helpful structure with mappings struct Mapping { Mapping (const Array<int> &_mapping, const Array<int> &_inv_mapping) : mapping(_mapping), inv_mapping(_inv_mapping) {} const Array<int> &mapping, &inv_mapping; }; // Returns indices for lexicographically-sorted atom labels // with exception that the first atom is Carbon static const Array<int>& getLexSortedAtomLables (); // Returns inverse permutation for getLexSortedLables static const Array<int>& getLexSortedAtomLablesRanks (); // Stable sort for small integer arrays with possibility to use array with ranks // Note: it is better to add stable sort method in Array and // modify qsort (and other stable sort) to accept any comparators. static void stableSmallSort (Array<int> &indices, const Array<int> *ranks); // Compare atoms with hydrogens: C < CH4 < CH3 < CH2 < CH static int compareHydrogens (int hyd1, int hyd2); // Get parity according to InChI standart static int getParityInChI (Molecule &mol, int bond); DECL_ERROR; private: static void _ensureLabelsInitialized (); static void _initializeAtomLabels (); static int _compareAtomLabels (int &label1, int &label2, void *context); static Array<int> _atom_lables_sorted; static Array<int> _atom_lables_ranks; }; } #endif // __molecule_inchi_utils_h__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_ionize.h������������������������������������������������������0000664�0000000�0000000�00000006522�12710376503�0021346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_ionize_h__ #define __molecule_ionize_h__ #include "base_cpp/tlscont.h" #include "base_cpp/obj_array.h" #include "base_cpp/red_black.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class QueryMolecule; struct IonizeOptions { enum PkaModel { PKA_MODEL_SIMPLE, PKA_MODEL_ADVANCED }; PkaModel model; int level = 0; int min_level = 0; IonizeOptions (PkaModel model = PKA_MODEL_SIMPLE) : model(model) {} }; class MoleculePkaModel { public: DECL_ERROR; static void estimate_pKa (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas); static void getAtomLocalFingerprint (Molecule &mol, int idx, Array<char> &fp, int level); static void getAtomLocalKey (Molecule &mol, int idx, Array<char> &fp); static int buildPkaModel (int level, float threshold, const char * filename); static float getAcidPkaValue (Molecule &mol, int idx, int level, int min_level); static float getBasicPkaValue (Molecule &mol, int idx, int level, int min_level); private: MoleculePkaModel (); static MoleculePkaModel _model; static void _loadSimplePkaModel (); static void _loadAdvancedPkaModel (); static void _estimate_pKa_Simple (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas); static void _estimate_pKa_Advanced (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas); static int _asc_cmp_cb (int &v1, int &v2, void *context); static void _checkCanonicalOrder(Molecule &mol, Molecule &can_mol, Array<int> &order); static void _removeExtraHydrogens (Molecule &mol); ObjArray<QueryMolecule> acids; ObjArray<QueryMolecule> basics; Array<float> a_pkas; Array<float> b_pkas; bool simple_model_ready = false; RedBlackStringObjMap<Array <float> > adv_a_pkas; RedBlackStringObjMap<Array <float> > adv_b_pkas; int level; Array<float> max_deviations; bool advanced_model_ready = false; }; class DLLEXPORT MoleculeIonizer { public: MoleculeIonizer (); static bool ionize (Molecule &molecule, float ph, float ph_toll, const IonizeOptions &options); DECL_ERROR; CP_DECL; protected: static void _setCharges (Molecule &mol, float ph, float ph_toll, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_layered_molecules.h�������������������������������������������0000664�0000000�0000000�00000015426�12710376503�0023551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_layered_molecules_h__ #define __molecule_layered_molecules_h__ #include "common/base_cpp/d_bitset.h" #include "molecule/base_molecule.h" #include "molecule/molecule.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT LayeredMolecules : public BaseMolecule { public: enum { BOND_TYPES_NUMBER = 5, MAX_CYCLE_LENGTH=22 }; LayeredMolecules(BaseMolecule& molecule); virtual ~LayeredMolecules(); // This method returns a bitmask of all layers that contain a bond idx of a specific order: const Dbitset &getBondMask(int idx, int order) const; // These methods are used for tracking if the atom is a possible position for a mobile hydrogen: bool isMobilePosition(int idx) const; void setMobilePosition(int idx, bool value); // These methods are used for tracking if the mobile position is occupied already. // The bitmask is the layers where the position is occupied. const Dbitset &getMobilePositionOccupiedMask(int idx) const; void setMobilePositionOccupiedMask(int idx, Dbitset &mask, bool value); // mask: the mask of layers used as prototypes; // edgesPath: the path of single-double bonds to be inverted // beg, end: the mobile positions of hydrogen to swap // forward: the direction to move the hydrogen // returns true if at least one new layer was added, false otherwise bool addLayersWithInvertedPath(const Dbitset &mask, const Array<int> &path, int beg, int end, bool forward); bool addLayerFromMolecule(const Molecule &molecule, Array<int> &aam); bool aromatize (int layerFrom, int layerTo, const AromaticityOptions &options); // construct a molecule that is represented as a layer void constructMolecule(Molecule &molecule, int layer, bool aromatized) const; unsigned getHash(int layer, bool aromatized) { if(aromatized) return _hashsAromatized[layer]; return _hashs[layer]; } virtual void clear (); virtual BaseMolecule * neu (); virtual int getAtomNumber (int idx); virtual int getAtomCharge (int idx); virtual int getAtomIsotope (int idx); virtual int getAtomRadical (int idx); virtual int getAtomAromaticity (int idx); virtual int getExplicitValence (int idx); virtual int getAtomValence (int idx); virtual int getAtomSubstCount (int idx); virtual int getAtomRingBondsCount (int idx); virtual int getAtomMaxH (int idx); virtual int getAtomMinH (int idx); virtual int getAtomTotalH (int idx); virtual bool isPseudoAtom (int idx); virtual const char * getPseudoAtom (int idx); virtual bool isTemplateAtom (int idx); virtual const char * getTemplateAtom (int idx); virtual const int getTemplateAtomSeqid (int idx); virtual const char * getTemplateAtomClass (int idx); virtual const int getTemplateAtomDisplayOption (int idx); virtual bool isRSite (int atom_idx); virtual dword getRSiteBits (int atom_idx); virtual void allowRGroupOnRSite (int atom_idx, int rg_idx); virtual int getBondOrder (int idx); virtual int getBondTopology (int idx); virtual bool atomNumberBelongs (int idx, const int *numbers, int count); virtual bool possibleAtomNumber (int idx, int number); virtual bool possibleAtomNumberAndCharge (int idx, int number, int charge); virtual bool possibleAtomNumberAndIsotope (int idx, int number, int isotope); virtual bool possibleAtomIsotope (int idx, int isotope); virtual bool possibleAtomCharge (int idx, int charge); virtual void getAtomDescription (int idx, Array<char> &description); virtual void getBondDescription (int idx, Array<char> &description); virtual bool possibleBondOrder (int idx, int order); virtual bool isSaturatedAtom (int idx); virtual bool bondStereoCare (int idx); virtual bool aromatize (const AromaticityOptions &options); virtual bool dearomatize (const AromaticityOptions &options); int layers; protected: struct AromatizationContext { LayeredMolecules *self; int layerFrom; int layerTo; bool result; }; Molecule _proto; ObjArray<Dbitset> _bond_masks[BOND_TYPES_NUMBER]; Array<bool> _mobilePositions; ObjArray<Dbitset> _mobilePositionsOccupied; virtual void _mergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags); static bool _cb_handle_cycle(Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context); void _resizeLayers(int newSize); void _calcConnectivity(int layerFrom, int layerTo); void _calcPiLabels(int layerFrom, int layerTo); bool _handleCycle(int layerFrom, int layerTo, const Array<int> &path); bool _isCycleAromaticInLayer(const int *cycle, int cycle_len, int layer); void _aromatizeCycle(const Array<int> &cycle, const Dbitset &mask); void _registerAromatizedLayers(int layerFrom, int layerTo); private: LayeredMolecules(const LayeredMolecules &); // no implicit copy ObjArray<Array<int>> _piLabels; ObjArray<Array<int>> _connectivity; int _layersAromatized; struct TrieNode { static const int ALPHABET_SIZE = 5; TrieNode() { for(auto &n : next) n = -1; } unsigned next[ALPHABET_SIZE]; }; class Trie { public: Trie() { _nodes = new ObjPool<TrieNode>(); // Adding root (index == 0) _nodes->add(); } unsigned getRoot() { return 0; } unsigned follow(unsigned nodeInd, unsigned key) { return _nodes->at(nodeInd).next[key]; } unsigned add(unsigned nodeInd, unsigned key, bool &newlyAdded) { if(_nodes->at(nodeInd).next[key] != -1) { newlyAdded = false; return _nodes->at(nodeInd).next[key]; } newlyAdded = true; int ind = _nodes->add(); _nodes->at(nodeInd).next[key] = ind; return ind; } private: AutoPtr<ObjPool<TrieNode>> _nodes; }; Trie _trie; Array<unsigned> _hashs; Array<unsigned> _hashsAromatized; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_mass.h��������������������������������������������������������0000664�0000000�0000000�00000003152�12710376503�0021010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_mass_h__ #define __molecule_mass_h__ #include "base_cpp/red_black.h" namespace indigo { class Molecule; // Molecular mass calculation class MoleculeMass { public: MoleculeMass(); const RedBlackMap<int, float> *relative_atomic_mass_map; /* Mass of a molecule calculated using the average mass of each * element weighted for its natural isotopic abundance */ float molecularWeight (Molecule &mol); /* Mass of a molecule containing most likely * isotopic composition for a single random molecule. * Notes: in PubChem search engine it is called Exact Mass */ float mostAbundantMass (Molecule &mol); /* Mass of a molecule calculated using the mass of * the most abundant isotope of each element. * Notes: in Marvin it is called Exact Mass */ float monoisotopicMass (Molecule &mol); /* Sum of the mass numbers of all constituent atoms. */ int nominalMass (Molecule &mol); }; } #endif // __molecule_mass_h__ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_neighbourhood_counters.h��������������������������������������0000664�0000000�0000000�00000004610�12710376503�0024623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_neighbourhood_counters_h__ #define __molecule_neighbourhood_counters_h__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" namespace indigo { class BaseMolecule; class Molecule; class QueryMolecule; class MoleculeAtomNeighbourhoodCounters { public: void calculate (Molecule &mol); void calculate (QueryMolecule &mol); bool testSubstructure (const MoleculeAtomNeighbourhoodCounters &target_counters, int query_atom_idx, int target_atom_idx, bool use_bond_types) const; void makeTranspositionForSubstructure (BaseMolecule &mol, Array<int> &output) const; public: void _calculate (BaseMolecule &mol, bool is_query); void _calculateLevel0 (BaseMolecule &mol, bool is_query); void _calculateNextLevel (BaseMolecule &mol, int r); bool _isAtomInformationStored (int atom_idx) const; static int _countersCmp (int &i1, int &i2, void *context); struct CountersPerRadius { // Number of atoms for specified radius: int C_cnt; // carbon int hetero_cnt; // heteroatoms, except N and O int heteroN_cnt; // nitrogen int heteroO_cnt; // oxigen int in_ring_cnt; // in rings int trip_cnt; // with >= 3 bonds of any type int degree_sum; // sum of bonds order bool testSubstructure (const CountersPerRadius &target, bool use_bond_types) const; }; struct Counters { enum { RADIUS = 2 }; CountersPerRadius per_rad[RADIUS]; bool testSubstructure (const Counters &target, bool use_bond_types) const; }; struct Context { const MoleculeAtomNeighbourhoodCounters *cnt; BaseMolecule *mol; }; Array<Counters> _per_atom_counters; Array<int> _use_atom; }; } #endif // __molecule_neighbourhood_counters_h__ ������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_pi_systems_matcher.h������������������������������������������0000664�0000000�0000000�00000005636�12710376503�0023760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_pi_systems_matcher__ #define __molecule_pi_systems_matcher__ #include "base_cpp/array.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_electrons_localizer.h" #include "graph/graph_decomposer.h" namespace indigo { class Molecule; class MoleculePiSystemsMatcher { public: MoleculePiSystemsMatcher (Molecule &target); bool isAtomInPiSystem (int atom); bool isBondInPiSystem (int bond); bool checkEmbedding (QueryMolecule &query, const int *mapping); void copyLocalization (Molecule &target); DECL_ERROR; private: // Returns number of pi-systems int _initMarks (void); void _markAtomsFirst (); void _markUnstablePiSystems (Array<bool> &pi_system_used); void _markVerticesInPiSystemsWithCycles (); void _markVerticesInUnusedPiSystems (Array<bool> &pi_system_used); void _markVerticesInSingleAtomPiSystem (int n_pi_systems); void _calculatePiSystemsSizes (int n_pi_systems, Array<int> &sizes); void _copyPiSystemsIdFromDecomposer (); void _extractPiSystem (int pi_system_index); void _findPiSystemLocalization (int pool_id); bool _fixAtoms (QueryMolecule &query, const int *mapping); bool _fixBonds (QueryMolecule &query, const int *mapping); bool _findMatching (); bool _findMatchingForPiSystem (int pool_id); void _markMappedPiSystems (QueryMolecule &query, const int *mapping); bool _canAtomBeInPiSystem (int v); void _calcConnectivity (Molecule &mol, Array<int> &conn); enum { _NOT_IN_PI_SYSTEM = -3, _UNKNOWN = -2, _IN_AROMATIC = -1 }; Molecule &_target; Obj<GraphDecomposer> _decomposer; CP_DECL; TL_CP_DECL(Array<int>, _atom_pi_system_idx); struct _Pi_System { Molecule pi_system; Array<int> inv_mapping, mapping; Obj<MoleculeElectronsLocalizer> localizer; struct Localizations { int double_bonds, primary_lp, seconary_lp; }; Array<Localizations> localizations; bool pi_system_mapped; bool initialized; void clear (); }; TL_CP_DECL(ReusableObjArray<_Pi_System>, _pi_systems); TL_CP_DECL(Array<int>, _connectivity); }; } #endif // __molecule_pi_systems_matcher__ ��������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_rgroups.h�����������������������������������������������������0000664�0000000�0000000�00000003023�12710376503�0021543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_rgroups__ #define __molecule_rgroups__ #include "base_cpp/red_black.h" #include "base_cpp/obj_array.h" #include "base_cpp/ptr_pool.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; struct RGroup { explicit RGroup (); ~RGroup (); void clear(); void copy (RGroup &other); bool occurrenceSatisfied (int value); PtrPool<BaseMolecule> fragments; int if_then; int rest_h; Array<int> occurrence; protected: explicit RGroup (RGroup &other); }; class DLLEXPORT MoleculeRGroups { public: MoleculeRGroups (); ~MoleculeRGroups (); DECL_ERROR; void copyRGroupsFromMolecule (MoleculeRGroups &other); RGroup &getRGroup (int idx); int getRGroupCount () const; void clear (); protected: ObjArray<RGroup> _rgroups; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_rgroups_composition.h�����������������������������������������0000664�0000000�0000000�00000015347�12710376503�0024202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_rgroups_composition__ #define __molecule_rgroups_composition__ #include "molecule/molecule.h" #include "base_cpp/multimap.h" #include "base_cpp/array.h" #include <memory> namespace indigo { class MoleculeRGroupsComposition : NonCopyable { public: explicit MoleculeRGroupsComposition(BaseMolecule &mol); ~MoleculeRGroupsComposition () {}; //State of search abstracted from chemistry details class AttachmentIter : NonCopyable { public: //Default constructor is used to indicate final state AttachmentIter() : _limits(nullptr), _end(true), _size(-1) {} ~AttachmentIter() {} //Constructs (0,...,0) state, limits indicates max number on every site AttachmentIter(int size, const Array<int> &limits) : _limits(&limits), _end(false), _size(size) { _fragments.resize(size); _fragments.fill(0); } //Operators *, != and ++ are used for range-loop iteration const Array<int>* operator* () const; bool operator!= (const AttachmentIter &other) const; AttachmentIter& operator++ (); //Output numbers assigned to sites void dump(Array<int> &other) const; //Move to next state, returns true if the iterator has next element bool next(); protected: const Array<int>* _limits; Array<int> _fragments; bool _end; int _size; }; //Collection of search states class Attachments : NonCopyable { public: Attachments(int size, const Array<int>& limits) : _limits(limits), _end(), _size(size) {} ~Attachments() {} std::unique_ptr<AttachmentIter> begin_ptr() const { return std::unique_ptr<AttachmentIter>(_begin()); } AttachmentIter& begin() { AttachmentIter *ptr = _begin(); _ptrs.add(ptr); return *ptr; } AttachmentIter& end() { return _end; } private: AttachmentIter* _begin() const { return new AttachmentIter(_size, _limits); } const Array<int>& _limits; PtrPool<AttachmentIter> _ptrs; AttachmentIter _end; const int _size; }; //Searchs all possible assignments of rgroup numbers to sites Attachments& attachments() const { _init(); return *_ats.get(); } //Assembles result molecule from abstract assigment of numbers to sites void decorate(const Array<int> &at, Molecule &out) const; void decorate(const AttachmentIter &at, Molecule &out) const; std::unique_ptr<Molecule> decorate(const Array<int> &at) const; std::unique_ptr<Molecule> decorate(const AttachmentIter &at) const; //Iterator for result molecules, essentially wrapper of AttachmentIter class MoleculeIter { public: MoleculeIter() = delete; MoleculeIter(AttachmentIter& at, const MoleculeRGroupsComposition& parent) : _parent(parent), _at(at) {} //Operators *, != and ++ are used for range-loop iteration std::unique_ptr<Molecule> operator* () const { return _parent.decorate(_at); } bool operator!= (const MoleculeIter &other) const { return _at != other._at; } MoleculeIter& operator++ () { next(); return *this; } //Assemble resulting molecule from attachment void dump(Molecule& out) const { _parent.decorate(_at, out); } //Shift to next molecule, returns true if there is next molecule after bool next() const { return _at.next(); } /* Modify rgroups information according to options: * "composed" erase rgroups in resulting molecule (returns empty rgroups) * "source" leaves rgroups info as is (returns copy of source rgroups) * "ordered" places each attached fragment into its own rgroup * "" defaults to "composed" */ std::unique_ptr<MoleculeRGroups> modifyRGroups(const char *options = "") const; enum Option { ERASE, LEAVE, ORDER }; #define RGCOMP_OPT MoleculeIter::Option #define RGCOMP_OPT_ENUM { RGCOMP_OPT::ERASE, RGCOMP_OPT::LEAVE, RGCOMP_OPT::ORDER } #define RGCOMP_OPT_COUNT 3 static const char * const OPTION(Option opt) { switch (opt) { case ERASE: return "composed"; case LEAVE: return "source"; case ORDER: return "ordered"; } } protected: const MoleculeRGroupsComposition& _parent; AttachmentIter& _at; class OrderedRGroups : public MoleculeRGroups { public: OrderedRGroups(const MoleculeIter &m); }; class SourceRGroups : public MoleculeRGroups { public: SourceRGroups(const MoleculeIter &m); }; }; MoleculeIter begin() const { _init(); return MoleculeIter(_ats->begin(), *this); } MoleculeIter end() const { _init(); return MoleculeIter(_ats->end(), *this); } DECL_ERROR; protected: struct Fragment { int rgroup; int fragment; bool operator<(const Fragment &other) const { return rgroup < other.rgroup || fragment < other.fragment; } }; inline Fragment _fragment_coordinates(int rsite, int fragment) const { const RedBlackSet<int> &rs = _rsite2rgroup[_rsite2vertex.at(rsite)]; int r = -1; int f = fragment; for (int i = rs.begin(); i != rs.end(); i = rs.next(i)) { r = rs.key(i); int size = _rgroup2size[r]; if (f >= size) { f -= size; } else { break; } } return { r, f }; } inline BaseMolecule& _fragment(int rsite, int fragment) const { auto x = _fragment_coordinates(rsite, fragment); return *_rgroups.getRGroup(x.rgroup).fragments[x.fragment]; } private: inline void _init() const { if (_ats.get() == nullptr) { _ats.reset(new Attachments(_rsites_count, _limits)); } } BaseMolecule& _mol; MoleculeRGroups& _rgroups; Array<int> _limits; Array<int> _rgroup2size; MultiMap<int, int> _rsite2rgroup; RedBlackMap<int, int> _rsite2vertex; mutable std::unique_ptr<Attachments> _ats; int _rsites_count, _rgroups_count; }; } #ifdef _WIN32 #endif #endif�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_savers.h������������������������������������������������������0000664�0000000�0000000�00000001763�12710376503�0021356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_savers_h__ #define __molecule_savers_h__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "molecule/base_molecule.h" namespace indigo { class MoleculeSavers { public: static int getHCount (BaseMolecule &mol, int index, int atom_number, int atom_charge); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������Indigo-indigo-1.2.3/molecule/molecule_scaffold_detection.h������������������������������������������0000664�0000000�0000000�00000006536�12710376503�0023675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_scaffold_detection_h_ #define __molecule_scaffold_detection_h_ #include "graph/scaffold_detection.h" #include "graph/max_common_subgraph.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" namespace indigo { //class for searching scaffold molecule from molecules set class MoleculeScaffoldDetection: public ScaffoldDetection { public: //class for keeping molecules class MoleculeBasket:public ScaffoldDetection::GraphBasket { public: MoleculeBasket(); virtual ~MoleculeBasket(); //initializes molecules basket void initBasket(ObjArray<Molecule>* mol_set, ObjArray<QueryMolecule>* basket_set, int max_number); //this method adds molecules from set (defines with edges and vertices lists) to basket queue virtual void addToNextEmptySpot(Graph& graph, Array<int> &v_list, Array<int> &e_list); virtual Graph& getGraphFromSet(int idx) {return (Graph&)_searchStructures->at(_orderArray[idx]); } virtual int getMaxGraphIndex(); //returns ptr of molecule in basket with index virtual Graph& getGraph(int index) const; //adds new molecule to queue and returns ptr of that QueryMolecule& pickOutNextMolecule(); int (*cbSortSolutions) (BaseMolecule &mol1, BaseMolecule &mol2, void *userdata); DECL_ERROR; private: virtual void _sortGraphsInSet(); static int _compareEdgeCount(int &i1,int &i2,void* context); static int _compareRingsCount(BaseMolecule& m1, BaseMolecule& m2, void* context); ObjArray<Molecule>* _searchStructures; ObjArray<QueryMolecule>* _basketStructures; MoleculeBasket(const MoleculeBasket&); //no implicit copy }; private: void _searchScaffold(QueryMolecule& scaffold, bool approximate); public: MoleculeScaffoldDetection (ObjArray<Molecule>* mol_set); //two main methods for extracting scaffolds //extracting exact scaffold from molecules set void extractExactScaffold (QueryMolecule& scaffold) {_searchScaffold(scaffold, false); } void extractApproximateScaffold(QueryMolecule& scaffold) {_searchScaffold(scaffold, true); } //extracting approximate scaffold from molecule set int (*cbSortSolutions) (Molecule &mol1, Molecule &mol2, const void *userdata); static void clone(QueryMolecule& mol, Molecule& other); static void makeEdgeSubmolecule(QueryMolecule& mol, Molecule& other, Array<int> &v_list, Array<int> &e_list); static bool matchBonds (Graph &g1, Graph &g2, int i, int j, void* userdata); static bool matchAtoms (Graph &g1, Graph &g2, const int *core_sub, int i, int j, void* userdata); ObjArray<Molecule>* searchStructures; ObjArray<QueryMolecule>* basketStructures; DECL_ERROR; }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_sgroups.h�����������������������������������������������������0000664�0000000�0000000�00000013454�12710376503�0021555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_sgroups__ #define __molecule_sgroups__ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_pool.h" #include "base_cpp/ptr_pool.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class DLLEXPORT SGroup { public: enum { SG_TYPE_GEN = 0, SG_TYPE_DAT, SG_TYPE_SUP, SG_TYPE_SRU, SG_TYPE_MUL, SG_TYPE_MON, SG_TYPE_MER, SG_TYPE_COP, SG_TYPE_CRO, SG_TYPE_MOD, SG_TYPE_GRA, SG_TYPE_COM, SG_TYPE_MIX, SG_TYPE_FOR, SG_TYPE_ANY }; enum { SG_SUBTYPE_ALT = 1, SG_SUBTYPE_RAN, SG_SUBTYPE_BLO }; enum { HEAD_TO_HEAD = 1, HEAD_TO_TAIL, EITHER }; enum { SG_TYPE = 1, SG_CLASS, SG_LABEL, SG_DISPLAY_OPTION, SG_BRACKET_STYLE, SG_DATA, SG_DATA_NAME, SG_DATA_TYPE, SG_DATA_DESCRIPTION, SG_DATA_DISPLAY, SG_DATA_LOCATION, SG_DATA_TAG, SG_QUERY_CODE, SG_QUERY_OPER, SG_PARENT, SG_CHILD, SG_ATOMS, SG_BONDS }; struct SgType { const int int_type; const char *str_type; }; SGroup (); virtual ~SGroup (); int sgroup_type; // group type, represnted with STY in Molfile format int sgroup_subtype; // group subtype, represnted with SST in Molfile format int original_group; // original group number int parent_group; // parent group number; represented with SPL in Molfile format Array<int> atoms; // represented with SAL in Molfile format Array<int> bonds; // represented with SBL in Molfile format int brk_style; // represented with SBT in Molfile format Array<Vec2f[2]> brackets; // represented with SDI in Molfile format static const char * typeToString(int sg_type); static int getType(const char * sg_type); private: SGroup (const SGroup &); }; class DLLEXPORT DataSGroup : public SGroup { public: DataSGroup (); virtual ~DataSGroup (); Array<char> description; // SDT in Molfile format (filed units or format) Array<char> name; // SDT in Molfile format (field name) Array<char> type; // SDT in Molfile format (field type) Array<char> querycode; // SDT in Molfile format (query code) Array<char> queryoper; // SDT in Molfile format (query operator) Array<char> data; // SCD/SED in Molfile format (field data) Vec2f display_pos; // SDD in Molfile format bool detached; // or attached bool relative; // or absolute bool display_units; int num_chars; // number of characters int dasp_pos; char tag; // tag private: DataSGroup (const DataSGroup &); }; class DLLEXPORT Superatom : public SGroup { public: Superatom (); virtual ~Superatom (); Array<char> subscript; // SMT in Molfile format Array<char> sa_class; // SCL in Molfile format int contracted; // display option (-1 if undefined, 0 - expanded, 1 - contracted) // SDS in Molfile format struct _AttachmentPoint { int aidx; int lvidx; Array<char> apid; }; ObjPool<_AttachmentPoint> attachment_points; // SAP in Molfile format struct _BondConnection { int bond_idx; Vec2f bond_dir; }; Array<_BondConnection> bond_connections; // SBV in Molfile format private: Superatom (const Superatom &); }; class DLLEXPORT RepeatingUnit : public SGroup { public: RepeatingUnit (); virtual ~RepeatingUnit (); int connectivity; Array<char> subscript; // SMT in Molfile format private: RepeatingUnit (const RepeatingUnit &); }; class DLLEXPORT MultipleGroup : public SGroup { public: MultipleGroup (); virtual ~MultipleGroup (); Array<int> parent_atoms; int multiplier; private: MultipleGroup (const MultipleGroup &); }; class DLLEXPORT MoleculeSGroups { public: MoleculeSGroups (); ~MoleculeSGroups (); DECL_ERROR; int addSGroup (const char * sg_type); int addSGroup (int sg_type); SGroup &getSGroup (int idx); SGroup &getSGroup (int idx, int sg_type); int getSGroupCount (); int getSGroupCount (int sg_type); bool isPolimer(); void remove(int idx); void clear (); void clear (int sg_type); int begin(); int end(); int next(int i); enum PropertyTypes { PROPERTY_INT, PROPERTY_BOOL, PROPERTY_STRING, PROPERTY_INT_ARRAY }; static void parseCondition (const char * property, const char * value, int &s_property, int &s_type, int &s_int, Array<int> &s_indices); void findSGroups (const char * property, const char * value, Array<int> &sgs); void findSGroups (int property, int value, Array<int> &sgs); void findSGroups (int property, const char *value, Array<int> &sgs); void findSGroups (int property, Array<int> &value, Array<int> &sgs); void registerUnfoldedHydrogen(int idx, int new_h_idx); protected: PtrPool<SGroup> _sgroups; private: int _findSGroupById (int id); bool _cmpIndices (Array<int> &t_inds, Array<int> &q_inds); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_standardize.h�������������������������������������������������0000664�0000000�0000000�00000011200�12710376503�0022346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_standardize__ #define __molecule_standardize__ #include "base_cpp/tlscont.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class Molecule; class QueryMolecule; class StandardizeOptions; class MoleculeStandardizer { public: MoleculeStandardizer(); // Interface function for stadardize molecule static bool standardize (Molecule &mol, const StandardizeOptions &options); static bool standardize (QueryMolecule &query, const StandardizeOptions &options); static bool isFragmentLinear(BaseMolecule &mol, int idx); DECL_ERROR; protected: static void _standardizeStereo(Molecule &mol); static void _standardizeStereo(QueryMolecule &mol); static void _standardizeCharges(Molecule &mol); static void _standardizeCharges(QueryMolecule &mol); static void _centerMolecule(BaseMolecule &mol); static void _removeSingleAtomFragments(BaseMolecule &mol); static void _keepSmallestFragment(BaseMolecule &mol); static void _keepLargestFragment(BaseMolecule &mol); static void _removeLargestFragment(BaseMolecule &mol); static void _makeNonHAtomsCAtoms(Molecule &mol); static void _makeNonHAtomsCAtoms(QueryMolecule &mol); static void _makeNonHAtomsAAtoms(Molecule &mol); static void _makeNonHAtomsAAtoms(QueryMolecule &mol); static void _makeNonCHAtomsQAtoms(Molecule &mol); static void _makeNonCHAtomsQAtoms(QueryMolecule &mol); static void _makeAllBondsSingle(Molecule &mol); static void _makeAllBondsSingle(QueryMolecule &mol); static void _clearCoordinates(BaseMolecule &mol); static void _fixCoordinateDimension(BaseMolecule &mol); static void _straightenTripleBonds(BaseMolecule &mol); static void _straightenAllenes(BaseMolecule &mol); static void _clearMolecule(BaseMolecule &mol); static void _removeMolecule(BaseMolecule &mol); static void _clearStereo(BaseMolecule &mol); static void _clearEnhancedStereo(BaseMolecule &mol); static void _clearUnknownStereo(BaseMolecule &mol); static void _clearUnknownAtomStereo(BaseMolecule &mol); static void _clearUnknownCisTransBondStereo(BaseMolecule &mol); static void _clearCisTransBondStereo(BaseMolecule &mol); static void _setStereoFromCoordinates(BaseMolecule &mol); static void _repositionStereoBonds(BaseMolecule &mol); static void _repositionAxialStereoBonds(BaseMolecule &mol); static void _fixDirectionOfWedgeBonds(BaseMolecule &mol); static void _clearCharges(Molecule &mol); static void _clearCharges(QueryMolecule &mol); static void _clearPiBonds(BaseMolecule &mol); static void _clearHighlightColors(BaseMolecule &mol); static void _clearQueryInfo(BaseMolecule &mol); static void _clearAtomLabels(Molecule &mol); static void _clearAtomLabels(QueryMolecule &mol); static void _clearBondLabels(Molecule &mol); static void _clearBondLabels(QueryMolecule &mol); static void _neutralizeBondedZwitterions(Molecule &mol); static void _neutralizeBondedZwitterions(QueryMolecule &mol); static void _clearUnusualValence(Molecule &mol); static void _clearUnusualValence(QueryMolecule &mol); static void _clearIsotopes(Molecule &mol); static void _clearIsotopes(QueryMolecule &mol); static void _clearDativeBonds(BaseMolecule &mol); static void _clearHydrogenBonds(BaseMolecule &mol); static void _localizeMarkushRAtomsOnRings(Molecule &mol); static void _localizeMarkushRAtomsOnRings(QueryMolecule &mol); static void _createCoordinationBonds(BaseMolecule &mol); static void _createHydrogenBonds(BaseMolecule &mol); static void _removeExtraStereoBonds(BaseMolecule &mol); CP_DECL; private: static int _getNumberOfBonds(BaseMolecule &mol, int idx, int bond_type, bool with_element_only, int element); static void _linearizeFragment(BaseMolecule &mol, int idx); static bool _isNonMetalAtom(int atom_number); static bool _isMetalAtom(int atom_number); static int _asc_cmp_cb (int &v1, int &v2, void *context); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_standardize_options.h�����������������������������������������0000664�0000000�0000000�00000014454�12710376503�0024137�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_standardize_options__ #define __molecule_standardize_options__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class StandardizeOptions { public: StandardizeOptions (); void reset (); // Sets or repairs the stereo on a molecule to a standard form using the coordinates // as the guide. Default is false. bool standardize_stereo; // Sets the charges on a molecule to a standard form. Default is false. bool standardize_charges; // Translates a molecule so its geometric center lies at the origin. Default is false. bool center_molecule; // Removes fragments that consist of only a single heavy atom. Default is false. bool remove_single_atom_fragments; // Keeps only the smallest fragment in the molecule. Default is false. bool keep_smallest_fragment; // Keeps only the largest fragment in the molecule. Default is false. bool keep_largest_fragment; // Removes the largest fragment in the molecule. Default is false. bool remove_largest_fragment; // Converts all non-Hydrogen atoms atoms in the molecule to carbon. Default is false. bool make_non_h_atoms_c_atoms; // Converts all non-Hydrogen atoms in the molecule to the A query atom type. // Default is false. bool make_non_h_atoms_a_atoms; // Converts all non-Carbon, non-Hydrogen atoms in the molecule to the Q query atom type. // Default is false. bool make_non_c_h_atoms_q_atoms; // Converts all bonds in the molecule to single bonds. Default is false. bool make_all_bonds_single; // Sets all x, y, z coordinates to zero. Default is false. bool clear_coordinates; // Sets the coordinate dimension (0D, 2D, 3D) based on the atomic coordinates. // Default is false. bool fix_coordinate_dimension; // Finds atoms with triple bonds and non-linear geometry and fixes them // so that the bond angles are 180 degrees. Default is false. bool straighten_triple_bonds; // Finds atoms with two double-bonds and non-linear geometry and fixes them // so that the bond angles are 180 degrees.Default is false. bool straighten_allenes; // Deletes all atoms and bonds in the molecule, keeping the molecule object // in the data record. Default is false. bool clear_molecule; // Deletes the molecule object from the data record. Default is false. bool remove_molecule; // Sets all atoms and bonds to NoStereo. Default is false. bool clear_stereo; // Removes all relative stereo groupings. Default is false. bool clear_enhanced_stereo; // Sets all atoms and bonds marked UnknownStereo to NoStereo. Default is false. bool clear_unknown_stereo; // Sets all atoms marked UnknownStereo to NoStereo. Default is false. bool clear_unknown_atom_stereo; // Sets all bonds marked UnknownStereo to NoStereo. Default is false. bool clear_unknown_cis_trans_bond_stereo; // Sets all bonds marked CisStereo or TransStereo to UnknownStereo. Default is false. bool clear_cis_trans_bond_stereo; // Uses 2D coordinates and up/down bond markings (or 3D coordinates) to assign // the stereochemistry of the atoms or bonds. Default is false. bool set_stereo_from_coordinates; // Repositions the stereo bond markings in an attempt to find the best bond // to mark as a wedge bond for each stereo atom. Default is false. bool reposition_stereo_bonds; // Repositions the stereo bond markings for axial stereo centers // (allenes and atropisomers) in an attempt to find the best bond // to mark as a wedge bond for each center. Default is false. bool reposition_axial_stereo_bonds; // Checks the wedge bonds in the molecule to ensure that the wedge // is drawn with the stereo atom at the narrow end of the wedge. Default is false. bool fix_direction_of_wedge_bonds; // Sets all formal charges to zero. Default is false. bool clear_charges; // Clears any pi bonds and Pi systems from the molecule. Default is false. bool clear_pi_bonds; // Clears any highlight colors from atoms and bonds. Default is false. bool clear_highlight_colors; // Deletes all query information from atoms and bonds. Default is false. bool clear_query_info; // Clears labels from atoms. Default is false. bool clear_atom_labels; // Clears labels from bonds. Default is false. bool clear_bond_labels; // Converts directly bonded zwitterions (positively charged atom bonded // to negatively charged atom, A+B-) to the neutral representation (A=B). Default is false. bool neutralize_bonded_zwitterions; // Clears any atom valence query features and resets all implicit hydrogen // counts to their standard values. Default is false. bool clear_unusual_valence; // Clears all isotope markings from atoms. Default is false. bool clear_isotopes; // Clears all explicit zero-order coordination bonds of dative type (V3000 type-9 bonds). // Default is false. bool clear_dative_bonds; // Clears all explicit zero-order hydrogen bonds (V3000 type-10 bonds). Default is false. bool clear_hydrogen_bonds; // R atoms bonded to the centers of rings are converted to R atoms at all open // positions on the ring. Default is false. bool localize_markush_r_atoms_on_rings; // Create coordination bond (zero-order bond) instead of wrong co-valent bond // Default is false. bool create_coordination_bonds; // Create hydrogen bond (zero-order bond) instead of wrong co-valent bond // Default is false. bool create_hydrogen_bonds; // Remove unnecessary stereo bonds // Default is false. bool remove_extra_stereo_bonds; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif // __molecule_standardize_options__ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_stereocenter_options.h����������������������������������������0000664�0000000�0000000�00000002521�12710376503�0024321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_stereocenter_options__ #define __molecule_stereocenter_options__ #include "base_c/defs.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT StereocentersOptions { public: StereocentersOptions (); void reset (); // Ignore all stereocenter errors. Default is false. bool ignore_errors; // Treat stereobond direction bond not only for a pointed stereocenter, but for the // neighbour as well. Default is false. bool bidirectional_mode; // Detect Haworth projection. Default is false. bool detect_haworth_projection; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif // __molecule_stereocenter_options__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_stereocenters.h�����������������������������������������������0000664�0000000�0000000�00000012761�12710376503�0022740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_stereocenters__ #define __molecule_stereocenters__ #include "base_cpp/red_black.h" #include "math/algebra.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class Filter; class StereocentersOptions; class DLLEXPORT MoleculeStereocenters { public: enum { ATOM_ANY = 1, ATOM_AND = 2, ATOM_OR = 3, ATOM_ABS = 4 }; explicit MoleculeStereocenters (); void clear (); void buildFromBonds (const StereocentersOptions &options, int *sensible_bonds_out); void buildFrom3dCoordinates (void); void markBonds (); void markBond (int atom_idx); // takes mapping from supermolecule to submolecule void buildOnSubmolecule (const MoleculeStereocenters &super, int *mapping); void removeAtoms (const Array<int> &indices); void removeBonds (const Array<int> &indices); int size () const; void add (int atom_idx, int type, int group, bool inverse_pyramid); void add (int atom_idx, int type, int group, const int pyramid[4]); void get (int i, int &atom_idx, int &type, int &group, int *pyramid) const; void remove (int idx); bool exists (int atom_idx) const; void get (int atom_idx, int &type, int &group, int *pyramid) const; int getType (int idx) const; int getGroup (int idx) const; void setGroup (int idx, int group); const int * getPyramid (int idx) const; int * getPyramid (int idx); void setType (int idx, int type, int group); void setType (int idx, int type); void invertPyramid (int idx); bool sameGroup (int idx1, int idx2); void getAbsAtoms (Array<int> &indices); void getOrGroups (Array<int> &numbers); void getAndGroups (Array<int> &numbers); void getOrGroup (int number, Array<int> &indices); void getAndGroup (int number, Array<int> &indices); bool haveAbs (); bool haveAllAbs (); bool haveAllAbsAny (); bool haveAllAndAny (); void registerUnfoldedHydrogen (int atom_idx, int added_hydrogen); void flipBond (int atom_parent, int atom_from, int atom_to); int begin () const; int end () const; int next (int i) const; int getAtomIndex (int i) const; static bool checkSub (const MoleculeStereocenters &query, const MoleculeStereocenters &target, const int *mapping, bool reset_h_isotopes, Filter *stereocenters_vertex_filter = 0); static bool isPyramidMappingRigid (const int *pyramid, int size, const int *mapping); static bool isPyramidMappingRigid (const int mapping[4]); static bool isPyramidMappingRigid_Sort (int *pyramid, const int *mapping); static void moveImplicitHydrogenToEnd (int pyramid[4]); static void moveMinimalToEnd (int pyramid[4]); static void moveElementToEnd (int pyramid[4], int element); static bool isAutomorphism (BaseMolecule &mol, const Array<int> &mapping, const Filter *filter = NULL); DECL_ERROR; static void getPyramidMapping (const MoleculeStereocenters &query, const MoleculeStereocenters &target, int query_atom, const int *mapping, int *mapping_out, bool reset_h_isotopes); bool isPossibleStereocenter (int atom_idx, bool *possible_implicit_h = 0, bool *possible_lone_pair = 0); static void rotatePyramid (int *pyramid); protected: struct _Atom { int type; // ANY, AND, OR, ABS int group; // stereogroup index // [X, Y, Z, W] -- atom indices or -1 for implicit hydrogen // (X, Y, Z) go counterclockwise when looking from W. // if there are pure (implicit) hydrogen, it is W int pyramid[4]; }; struct _EdgeIndVec { int edge_idx; int nei_idx; int rank; Vec3f vec; }; struct _Configuration { int elem; int charge; int degree; int n_double_bonds; int implicit_degree; }; RedBlackMap<int, _Atom> _stereocenters; static int _sign (const Vec3f &v1, const Vec3f &v2, const Vec3f &v3); static int _xyzzy (const Vec3f &v1, const Vec3f &v2, const Vec3f &u); static int _onPlane (const Vec3f &v1, const Vec3f &v2, const Vec3f &v3, const Vec3f &v4); bool _buildOneCenter (int atom_idx, int *sensible_bonds_out, bool bidirectional_mode, bool bidirectional_either_mode, const Array<bool> &bond_ignore); void _getGroups (int type, Array<int> &numbers); void _getGroup (int type, int number, Array<int> &indices); void _restorePyramid (int idx, int pyramid[4], int invert_pyramid); static void _convertAtomToImplicitHydrogen (int pyramid[4], int atom_to_remove); void _removeBondDir (int atom_from, int atom_to); int _getDirection (BaseMolecule &mol, int atom_from, int atom_to, bool bidirectional_mode); BaseMolecule & _getMolecule() const; private: MoleculeStereocenters (const MoleculeStereocenters &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ���������������Indigo-indigo-1.2.3/molecule/molecule_substructure_matcher.h����������������������������������������0000664�0000000�0000000�00000021156�12710376503�0024326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_substructure_matcher__ #define __molecule_substructure_matcher__ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_pi_systems_matcher.h" #include "molecule/molecule_arom_match.h" #include "graph/embedding_enumerator.h" #include "graph/embeddings_storage.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/obj.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class AromaticityMatcher; struct Vec3f; class GraphVertexEquivalence; class MoleculeAtomNeighbourhoodCounters; class MoleculePiSystemsMatcher; class DLLEXPORT MoleculeSubstructureMatcher { public: enum { AFFINE = 1, CONFORMATION = 2 }; typedef ObjArray< RedBlackStringMap<int> > FragmentMatchCache; MoleculeSubstructureMatcher (BaseMolecule &target); ~MoleculeSubstructureMatcher (); void setQuery (QueryMolecule &query); QueryMolecule & getQuery (); // Set vertex neibourhood counters for effective matching void setNeiCounters (const MoleculeAtomNeighbourhoodCounters *query_counters, const MoleculeAtomNeighbourhoodCounters *target_counters); // Property indicating that first atom in the query should be ignored because // it will be used later. For example, it is fixed during fragment matching bool not_ignore_first_atom; bool use_aromaticity_matcher; bool use_pi_systems_matcher; GraphVertexEquivalence *vertex_equivalence_handler; AromaticityOptions arom_options; FragmentMatchCache *fmcache; bool highlight; bool disable_unfolding_implicit_h; bool disable_folding_query_h; bool restore_unfolded_h; int match_3d; // 0 or AFFINE or CONFORMATION float rms_threshold; // for AFFINE and CONFORMATION void ignoreQueryAtom (int idx); void ignoreTargetAtom (int idx); bool fix (int query_atom_idx, int target_atom_idx); // for finding the first embedding bool find (); bool findNext (); const int * getQueryMapping (); const int * getTargetMapping (); // Finding all embeddings and iterating them. // Substructure matcher can be used in 3 ways: // 1. Find first embedding // 2. Save all embeddings. // 3. Iterate over all embeddings. bool find_all_embeddings, // false by default find_unique_embeddings, // true if to find only unique embeddings. false by default find_unique_by_edges, // true if to find edges-unique embeddings. false by default save_for_iteration; // true if to save embeddings to the embeddings storage. false by default // Embedding callback. Returns true to continue enumeration. bool (*cb_embedding) (Graph &sub, Graph &super, const int *core1, const int *core2, void *context); void *cb_embedding_context; const GraphEmbeddingsStorage& getEmbeddingsStorage () const; static bool needCoords (int match_3d, QueryMolecule &query); static void removeAtom (Graph &subgraph, int sub_idx, AromaticityMatcher *am); static void addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, AromaticityMatcher *am); static void markIgnoredHydrogens (BaseMolecule &mol, int *arr, int value_keep, int value_ignore); static void markIgnoredQueryHydrogens (QueryMolecule &mol, int *arr, int value_keep, int value_ignore); static void getAtomPos (Graph &graph, int vertex_idx, Vec3f &pos); // Flags for matchQueryAtom and matchQueryBond (by default all flags should be set) enum { // When some flags are not set it means that checking should be done without // such conditions but as precise as possible because such conditions will be // checked later. // For example, MATCH_ATOM_CHARGE isn't set. // It means that // (1) match should return true if it does so for some charge with // MATCH_ATOM_CHARGE set to true. // (2) if for every charge match returns false, then without MATCH_ATOM_CHARGE // match should return false. // It it not easy to implement point (2) exactly, but match algorithm should // always satisfy point (1). So it have to satisfy point (2) as precise as // possible. MATCH_ATOM_CHARGE = 0x01, MATCH_ATOM_VALENCE = 0x02, MATCH_BOND_TYPE = 0x04, // To satisfy point (2) (not precisely) following flag is introduced. // It shows what value should be returned if condition is disabled. // When 'not' operation is applied then such flag is inverted. So // points (1) is satisfied and point (2) generally satisfied too (not always). MATCH_DISABLED_AS_TRUE = 0x1000 }; static bool matchQueryAtom (QueryMolecule::Atom *query, BaseMolecule &target, int super_idx, FragmentMatchCache *fmcache, dword flags); static bool matchQueryBond (QueryMolecule::Bond *query, BaseMolecule &target, int sub_idx, int super_idx, AromaticityMatcher *am, dword flags); static void makeTransposition (BaseMolecule &mol, Array<int> &transposition); DECL_ERROR; static bool shouldUnfoldTargetHydrogens (QueryMolecule &query, bool find_all_embeddings); protected: struct MarkushContext { explicit MarkushContext (QueryMolecule &query_, BaseMolecule &target_); CP_DECL; TL_CP_DECL(QueryMolecule, query); TL_CP_DECL(Array<int>, query_marking); TL_CP_DECL(Array<int>, sites); int depth; }; static bool _matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _removeAtom (Graph &subgraph, int sub_idx, void *userdata); static void _addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); int _embedding_common (int *core_sub, int *core_super); int _embedding_markush (int *core_sub, int *core_super); static bool _canUseEquivalenceHeuristic (QueryMolecule &query); static bool _isSingleBond (Graph &graph, int edge_idx); static bool _shouldUnfoldTargetHydrogens (QueryMolecule &query, bool is_fragment, bool disable_folding_query_h); static bool _shouldUnfoldTargetHydrogens_A (QueryMolecule::Atom *atom, bool is_fragment, bool disable_folding_query_h); static int _countSubstituents (Molecule &mol, int idx); bool _checkRGroupConditions (); bool _attachRGroupAndContinue (int *core1, int *core2, QueryMolecule *fragment, bool two_attachment_points, int att_idx1, int att_idx2, int rgroup_idx, bool rest_h); void _removeUnfoldedHydrogens (); BaseMolecule &_target; QueryMolecule *_query; const MoleculeAtomNeighbourhoodCounters *_query_nei_counters, *_target_nei_counters; Obj<EmbeddingEnumerator> _ee; AutoPtr<MarkushContext> _markush; // Because storage can be big it is not stored into TL_CP_*** // It can be stored as TL_CP_*** if memory allocations will // be critical Obj<GraphEmbeddingsStorage> _embeddings_storage; Obj<Molecule3dConstraintsChecker> _3d_constraints_checker; Obj<AromaticityMatcher> _am; Obj<MoleculePiSystemsMatcher> _pi_systems_matcher; bool _h_unfold; // implicit target hydrogens unfolded CP_DECL; TL_CP_DECL(Array<int>, _3d_constrained_atoms); TL_CP_DECL(Array<int>, _unfolded_target_h); TL_CP_DECL(Array<int>, _used_target_h); static int _compare_degree_asc (BaseMolecule &mol, int i1, int i2); static int _compare_frequency_base (BaseMolecule &mol, int i1, int i2); static int _compare_frequency_asc (BaseMolecule &mol, int i1, int i2); static int _compare_in_loop (BaseMolecule &mol, int i1, int i2); static int _compare (int &i1, int &i2, void *context); void _createEmbeddingsStorage (); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tautomer.h����������������������������������������������������0000664�0000000�0000000�00000020442�12710376503�0021706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tautomer_h__ #define __molecule_tautomer_h__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/obj.h" #include "graph/graph_decomposer.h" #include "molecule/molecule.h" #include "molecule/molecule_dearom.h" #include "molecule/base_molecule.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class AromaticityMatcher; class Dearomatizer; class DearomatizationMatcher; struct Atom; typedef enum { BASIC, INCHI, RSMARTS } TautomerMethod; //#define TRACE_TAUTOMER_MATCHING struct TautomerRule { bool check (BaseMolecule &molecule, int first, int last, char other_arom_first, char other_arom_last) const; Array<int> list1; Array<int> list2; int aromaticity1; int aromaticity2; static bool atomInAromaticRing (BaseMolecule &mol, int atom_idx); }; struct TautomerSearchContext { explicit TautomerSearchContext (BaseMolecule &g1_, BaseMolecule &g2_, GraphDecomposer &decomposer1_, GraphDecomposer &decomposer2_, const PtrArray<TautomerRule> &rules_list_, const AromaticityOptions &arom_options); virtual ~TautomerSearchContext (); BaseMolecule &g1; BaseMolecule &g2; GraphDecomposer &decomposer1; GraphDecomposer &decomposer2; // amount of metal bonds CP_DECL; TL_CP_DECL(Array<int>, h_rep_count_1); TL_CP_DECL(Array<int>, h_rep_count_2); const PtrArray<TautomerRule> &rules_list; bool force_hydrogens; bool ring_chain; int rules; TautomerMethod method; bool substructure; bool (*cb_check_rules) (TautomerSearchContext &context, int first1, int first2, int last1, int last2); int max_chains; AromaticityOptions arom_options; TL_CP_DECL(DearomatizationsStorage, dearomatizations); TL_CP_DECL(Array<int>, core_1); TL_CP_DECL(Array<int>, core_2); int initial_g1_vertexend; TL_CP_DECL(Array<int>, chains_2); TL_CP_DECL(Array<int>, edges_1); TL_CP_DECL(Array<int>, edges_2); TL_CP_DECL(Array<int>, edge_types_2); TL_CP_DECL(Array<int>, n1); TL_CP_DECL(Array<int>, n2); Obj<Dearomatizer> dearomatizer; Obj<DearomatizationMatcher> dearomatizationMatcher; }; class TautomerMatcher { public: explicit TautomerMatcher (TautomerSearchContext &context); explicit TautomerMatcher (TautomerSearchContext &context, int start_path_number, int n_chains); virtual ~TautomerMatcher (); bool findMatch (); void addPair (int n1, int n2, int arom_bond_idx2, int bond_type2); bool nextPair (int &n1, int &n2, int &h_diff, int prev_n1, int prev_n2); bool isFeasiblePair (int n1, int n2, int &h_diff); void restore (); static bool matchAtomsTau (BaseMolecule &g1, BaseMolecule &g2, int n1, int n2); static bool matchBondsTau (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static bool matchBondsTauSub (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static bool fixBondsNotInChains (TautomerSearchContext &context, const int *core1, const int *core2); private: bool _checkInterPathBonds (); static int _remainderEmbedding (Graph &g1, Graph &g2, int *core1, int *core2, void *userdata); static int _preliminaryEmbedding (Graph &g1, Graph &g2, int *core1, int *core2, void *userdata); static bool _matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchAtomsEx (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); struct MatchData { MatchData (TautomerSearchContext &context_) : context(context_) {} TautomerSearchContext &context; int start_path_number; } _d; int _n1; int _n2; int _bond_idx2; int _n_chains; }; class TautomerChainFinder { public: explicit TautomerChainFinder (TautomerSearchContext &context, int h_difference, int start_path_number, int n_chains); explicit TautomerChainFinder (TautomerChainFinder &other); virtual ~TautomerChainFinder (); bool enumeratePaths (); void addPair (int n1, int n2, bool is_zero_bond, int arom_bond_idx2, int bond_type2); void restore (); bool nextPair (int &n1, int &n2, int &e1, int &e2, int prev_e1, int prev_e2); int isFeasiblePair (int n1, int n2, bool &zero_bond, int &arom_bond_idx2, int &bond_type2); private: TautomerSearchContext &_context; int _prev_n1; int _prev_n2; int _bond_idx2; int _path_length; int _h_difference; bool _is_zero_bond_present; int _path_number; int _start_idx1; int _start_idx2; int _n_chains; }; class TautomerChainChecker { public: explicit TautomerChainChecker (TautomerSearchContext &context, const Array<int> &core1, const Array<int> &core2, int start_path_number); explicit TautomerChainChecker (TautomerChainChecker &other); virtual ~TautomerChainChecker (); bool check (); void addPair (int n1, int n2); void restore(); bool nextStartingPair (int &n1, int &n2); bool isFeasibleStartingPair (int n1, int n2, int &h_diff); bool nextPair (int &n1, int &n2, int &e1, int &e2); int isFeasiblePair (int n1, int n2, TautomerChainChecker &next1, TautomerChainChecker &next2); bool releaseChain (); void restoreChain (); DECL_ERROR; private: bool _checkInterPathBonds (); static bool _matchAromBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _removeAtom (Graph &subgraph, int sub_idx, void *userdata); static void _addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); bool _matchAromatizedQuery(); TautomerSearchContext &_context; int _path_length; int _h_difference; // at the end of chain int _final_path_length; int _final_h_difference; bool _is_zero_bond_present; bool _is_query_bond_present; bool _is_non_aromatic_bond_present; int _path_number; const Array<int> &_core_1; const Array<int> &_core_2; int _tau_bonds_to_match; int _prev_n1; int _prev_n2; int _bond_idx1; int _bond_idx2; int _bond_type2; int _start_idx1; int _start_idx2; }; class DLLEXPORT TautomerSuperStructure : public Molecule { public: enum { NONE, TAUTOMER, ORIGINAL }; TautomerSuperStructure (Molecule &mol); virtual ~TautomerSuperStructure (); virtual void clear (); virtual int getBondOrder (int idx); virtual int getBondTopology (int idx); virtual bool possibleBondOrder (int idx, int order); virtual int getAtomTotalH (int idx); bool isZeroedBond (int idx); const int * getMapping (); const Array<int> & getInvMapping (); int getSubgraphType (const Array<int> &vertices, const Array<int> &edges); protected: void _findMinDistance (int source, int maxDist, Array<int> &dest, int *result); void _collectAtomProperties (void); void _getDoubleBondsCount (int i, int &double_count, int &arom_count); bool _isAcceptingHeteroatom (int idx); bool _isEmittingHeteroatom (int idx); int _hetroatomsCount (int idx); int _getBondOrder (int idx); int _getBondTopology (int idx); bool _inside_ctor; CP_DECL; TL_CP_DECL(Array<int>, _atomsEmitBond); TL_CP_DECL(Array<int>, _atomsAcceptBond); TL_CP_DECL(Array<bool>, _isBondAttachedArray); TL_CP_DECL(Array<int>, _mapping); TL_CP_DECL(Array<int>, _inv_mapping); TL_CP_DECL(Array<int>, _edge_mapping); TL_CP_DECL(Array<int>, _total_h); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tautomer_enumerator.h�����������������������������������������0000664�0000000�0000000�00000005445�12710376503�0024155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tautomer_enumerator__ #define __molecule_tautomer_enumerator__ #include "base_cpp/reusable_obj_array.h" #include "molecule/molecule.h" #include "molecule/molecule_layered_molecules.h" #include "molecule/molecule_tautomer.h" #define USE_DEPRECATED_INCHI namespace indigo { class Molecule; class TautomerEnumerator { public: TautomerEnumerator(Molecule &molecule, TautomerMethod method); void constructMolecule(Molecule &molecule, int layer, bool needAromatize) const; bool enumerateLazy(); void enumerateAll(bool needAromatization); bool aromatize(); int beginNotAromatized(); int beginAromatized(); bool isValid(int); int next(int); void constructMolecule(Molecule &molecule, int n) const; protected: struct Breadcrumps { Dbitset forwardMask; Dbitset backwardMask; ObjArray<Dbitset> forwardEdgesHistory; ObjArray<Dbitset> backwardEdgesHistory; Array<int> nodesHistory; Array<int> edgesHistory; }; static bool matchEdge(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static bool matchVertex(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static void edgeAdd(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void vertexAdd(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void vertexRemove(Graph &subgraph, int sub_idx, void *userdata); static bool refine_proc(const Molecule &uncleaned_fragments, Molecule &product, Array<int> &mapping, void *userdata); static void product_proc(Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata); bool _performProcedure(); bool _aromatize(int from, int to); Graph _zebraPattern; public: LayeredMolecules layeredMolecules; #ifdef USE_DEPRECATED_INCHI bool _use_deprecated_inchi; #endif int _currentLayer; int _currentRule; bool _complete; int aromatizedRange[2]; RedBlackSet<unsigned> _enumeratedHistory; }; } #endif /* __molecule_tautomer_enumerator__ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tautomer_matcher.h��������������������������������������������0000664�0000000�0000000�00000004304�12710376503�0023410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tautomer_matcher__ #define __molecule_tautomer_matcher__ #include "graph/embedding_enumerator.h" #include "molecule/molecule_tautomer.h" #include "base_cpp/auto_ptr.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class AromaticityMatcher; class DLLEXPORT MoleculeTautomerMatcher { public: DECL_ERROR; bool highlight; AromaticityOptions arom_options; MoleculeTautomerMatcher (Molecule &target, bool substructure); void setQuery (BaseMolecule &query); void setRulesList (const PtrArray<TautomerRule> *rules_list); void setRules (int rules_set, bool force_hydrogens, bool ring_chain, TautomerMethod method); bool find (); const int * getQueryMapping (); static void parseConditions (const char *tautomer_text, int &rules, bool &force_hydrogens, bool &ring_chain, TautomerMethod &method); static int countNonHydrogens (BaseMolecule &molecule); protected: bool _find (bool substructure); static bool _checkRules (TautomerSearchContext &context, int first1, int first2, int last1, int last2); bool _substructure; bool _force_hydrogens; bool _ring_chain; TautomerMethod _method; int _rules; const PtrArray<TautomerRule> *_rules_list; AutoPtr<TautomerSearchContext> _context; Molecule &_target_src; AutoPtr<BaseMolecule> _query; Obj<TautomerSuperStructure> _target; BaseMolecule *_supermol; Obj<GraphDecomposer> _query_decomposer; Obj<GraphDecomposer> _target_decomposer; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tautomer_substructure_matcher.h�������������������������������0000664�0000000�0000000�00000007406�12710376503�0026250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tautomer_substructure_matcher__ #define __molecule_tautomer_substructure_matcher__ #include "molecule/molecule.h" #include "molecule/molecule_layered_molecules.h" #include "molecule/molecule_tautomer.h" #include "molecule/molecule_tautomer_enumerator.h" #include "graph/embedding_enumerator.h" #include "graph/embeddings_storage.h" #include "base_cpp/obj.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class DLLEXPORT MoleculeTautomerSubstructureMatcher { public: enum { AFFINE = 1, CONFORMATION = 2 }; typedef ObjArray< RedBlackStringMap<int> > FragmentMatchCache; MoleculeTautomerSubstructureMatcher(BaseMolecule &target, TautomerMethod method); ~MoleculeTautomerSubstructureMatcher(); void setQuery (QueryMolecule &query); bool find (); bool findNext (); const int * getQueryMapping (); const int * getTargetMapping (); // Finding all embeddings and iterating them. // Substructure matcher can be used in 3 ways: // 1. Find first embedding // 2. Save all embeddings. // 3. Iterate over all embeddings. bool find_all_embeddings, // false by default find_unique_embeddings, // true if to find only unique embeddings. false by default find_unique_by_edges, // true if to find edges-unique embeddings. false by default save_for_iteration; // true if to save embeddings to the embeddings storage. false by default const GraphEmbeddingsStorage& getEmbeddingsStorage () const; const Dbitset& getMask(int ind) const; void getTautomerFound(Molecule& mol, int enumInd, int tauInd) const; DECL_ERROR; protected: int _embedding_common (int *core_sub, int *core_super, Dbitset &mask); QueryMolecule *_query; TautomerEnumerator _tautomerEnumerator; Obj<EmbeddingEnumerator> _ee; // Because storage can be big it is not stored into TL_CP_*** // It can be stored as TL_CP_*** if memory allocations will // be critical Obj<GraphEmbeddingsStorage> _embeddings_storage; ObjArray<Dbitset> _masks; CP_DECL; void _createEmbeddingsStorage (); struct SubstructureSearchBreadcrumps { Dbitset mask; ObjArray<Dbitset> maskHistory; MoleculeTautomerSubstructureMatcher *self; }; SubstructureSearchBreadcrumps _breadcrumps; bool _needAromatize; bool _allLayersFound; int _layerBeg; int _layerEnd; static bool _matchAtoms(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchAtomsHyper(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchBondsSubHyper(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _edgeAddHyper(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _vertexRemoveHyper(Graph &subgraph, int sub_idx, void *userdata); static int _preliminaryEmbeddingHyper(Graph &g1, Graph &g2, int *core1, int *core2, void *userdata); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tautomer_utils.h����������������������������������������������0000664�0000000�0000000�00000002054�12710376503�0023125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tautomer_utils__ #define __molecule_tautomer_utils__ namespace indigo { class Graph; class Molecule; class MoleculeTautomerUtils { public: static void countHReplacements (BaseMolecule &g, Array<int> &h_rep_count); static void highlightChains (BaseMolecule &g1, BaseMolecule &g2, const Array<int> &chains_2, const int *core_2); private: static bool _isRepMetal (int elem); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molecule_tgroups.h�����������������������������������������������������0000664�0000000�0000000�00000003366�12710376503�0021557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molecule_tgroups__ #define __molecule_tgroups__ #include "base_cpp/tlscont.h" #include "base_cpp/red_black.h" #include "base_cpp/obj_array.h" #include "base_cpp/ptr_pool.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseMolecule; class TGroup { public: Array<char> tgroup_class; Array<char> tgroup_name; Array<char> tgroup_alias; Array<char> tgroup_comment; int tgroup_id; TGroup (); ~TGroup (); void copy (TGroup &other); void clear(); static int cmp (TGroup &tg1, TGroup &tg2, void *context); BaseMolecule* fragment; private: TGroup (const TGroup &); }; class DLLEXPORT MoleculeTGroups { public: MoleculeTGroups (); ~MoleculeTGroups (); DECL_ERROR; int addTGroup (); TGroup &getTGroup (int idx); int getTGroupCount (); void remove(int idx); void clear (); void copyTGroupsFromMolecule (MoleculeTGroups &other); int findTGroup(const char *name); int begin(); int end(); int next(int i); protected: PtrPool<TGroup> _tgroups; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molfile_loader.h�������������������������������������������������������0000664�0000000�0000000�00000010567�12710376503�0021145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molfile_loader__ #define __molfile_loader__ #include "base_cpp/exception.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "molecule/base_molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class Molecule; class QueryMolecule; #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif class DLLEXPORT MolfileLoader { public: DECL_ERROR; MolfileLoader (Scanner &scanner); void loadMolecule (Molecule &mol); void loadQueryMolecule (QueryMolecule &mol); // for Rxnfiles v3000 void loadCtab3000 (Molecule &mol); void loadQueryCtab3000 (QueryMolecule &mol); // optional parameters for reaction Array<int> * reaction_atom_mapping; Array<int> * reaction_atom_inversion; Array<int> * reaction_atom_exact_change; Array<int> * reaction_bond_reacting_center; StereocentersOptions stereochemistry_options; bool treat_x_as_pseudoatom; // normally 'X' means 'any halogen' bool skip_3d_chirality; // do not compute chirality from 3D coordinates // When true, the "bond topology", "stereo care", "ring bond count", and "unsaturation" // specifications are ignored when a non-query molecule is being loaded. // Otherwise, an error is thrown (this is the default). bool ignore_noncritical_query_features; protected: Scanner &_scanner; bool _rgfile; CP_DECL; TL_CP_DECL(Array<int>, _stereo_care_atoms); TL_CP_DECL(Array<int>, _stereo_care_bonds); TL_CP_DECL(Array<int>, _stereocenter_types); TL_CP_DECL(Array<int>, _stereocenter_groups); TL_CP_DECL(Array<int>, _sensible_bond_directions); TL_CP_DECL(Array<int>, _ignore_cistrans); enum { _ATOM_R, _ATOM_A, _ATOM_X, _ATOM_Q, _ATOM_LIST, _ATOM_NOTLIST, _ATOM_PSEUDO, _ATOM_TEMPLATE, _ATOM_ELEMENT }; enum { _BOND_SINGLE_OR_DOUBLE = 5, _BOND_SINGLE_OR_AROMATIC = 6, _BOND_DOUBLE_OR_AROMATIC = 7, _BOND_ANY = 8, _BOND_COORDINATION = 9, _BOND_HYDROGEN = 10 }; enum { _SGROUP_TYPE_SUP = 1, _SGROUP_TYPE_DAT, _SGROUP_TYPE_SRU, _SGROUP_TYPE_MUL, _SGROUP_TYPE_GEN, _SGROUP_TYPE_OTHER // one of unsupported types }; enum { _BRKTYP_SQUARE = 0, _BRKTYP_ROUND }; TL_CP_DECL(Array<int>, _atom_types); TL_CP_DECL(Array<int>, _hcount); TL_CP_DECL(Array<int>, _sgroup_types); TL_CP_DECL(Array<int>, _sgroup_mapping); bool _v2000; int _atoms_num; int _bonds_num; bool _chiral; void _readHeader (); void _readCtabHeader (); void _readCtab2000 (); void _convertCharge (int value, int &charge, int &radical); void _read3dFeature2000 (); void _readRGroupOccurrenceRanges (const char *str, Array<int> &ranges); void _readRGroups2000 (); void _readCtab3000 (); void _readSGroup3000 (const char *str); void _readRGroups3000 (); void _readTGroups3000 (); void _readSGroupDisplay (Scanner &scanner, DataSGroup &dsg); void _readCollectionBlock3000 (); void _readSGroupsBlock3000 (); void _preparePseudoAtomLabel (Array<char> &pseudo); void _readMultiString (Array<char> &str); void _readStringInQuotes (Scanner &scanner, Array<char> *str); void _init (); void _appendQueryAtom (const char *atom_label, AutoPtr<QueryMolecule::Atom> &atom); int _getElement (const char *buf); char* _strtrim (char *buf); static int _asc_cmp_cb (int &v1, int &v2, void *context); void _postLoad (); void _loadMolecule (); Molecule *_mol; BaseMolecule *_bmol; QueryMolecule *_qmol; private: MolfileLoader (const MolfileLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/molfile_saver.h��������������������������������������������������������0000664�0000000�0000000�00000010314�12710376503�0021005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __molfile_saver__ #define __molfile_saver__ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "molecule/base_molecule.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class QueryMolecule; class Output; class DLLEXPORT MolfileSaver { public: enum { MODE_AUTO = 0, // save to v3000 only if the given molecule has any // v3000-specific features MODE_2000, // force saving to v2000 format MODE_3000 // force saving to v3000 format }; struct CIPContext { BaseMolecule *mol; Array<int> *used1; Array<int> *used2; bool next_level; bool isotope_check; }; MolfileSaver (Output &output); void saveBaseMolecule (BaseMolecule &mol); void saveMolecule (Molecule &mol); void saveQueryMolecule (QueryMolecule &mol); void saveCtab3000 (Molecule &mol); void saveQueryCtab3000 (QueryMolecule &mol); int mode; // one of MODE_***, MODE_AUTO by default bool no_chiral; // skip the 'chiral' flag, not regarding of the actual stereochemistry bool skip_date; // If true then zero date is written bool add_stereo_desc; // If true then stereo descriptors will be added as DAT S-groups // optional parameters for reaction const Array<int>* reactionAtomMapping; const Array<int>* reactionAtomInversion; const Array<int>* reactionAtomExactChange; const Array<int>* reactionBondReactingCenter; DECL_ERROR; protected: void _saveMolecule (BaseMolecule &mol, bool query); void _writeHeader (BaseMolecule &mol, Output &output, bool zcoord); void _writeCtabHeader (Output &output); void _writeAtomLabel (Output &output, int label); void _writeMultiString (Output &output, const char *string, int len); void _writeCtab (Output &output, BaseMolecule &mol, bool query); void _writeOccurrenceRanges (Output &out, const Array<int> &occurrences); void _writeRGroup (Output &output, BaseMolecule &mol, int rg_idx); void _writeTGroup (Output &output, BaseMolecule &mol, int tg_idx); void _writeCtabHeader2000 (Output &output, BaseMolecule &mol); void _writeCtab2000 (Output &output, BaseMolecule &mol, bool query); void _checkSGroupIndices (BaseMolecule &mol); void _writeRGroupIndices2000 (Output &output, BaseMolecule &mol); void _writeAttachmentValues2000 (Output &output, BaseMolecule &fragment); void _writeGenericSGroup3000 (SGroup &sgroup, int idx, Output &output); void _writeDataSGroupDisplay (DataSGroup &datasgroup, Output &out); void _writeFormattedString(Output &output, Array<char> &str, int length); static bool _checkAttPointOrder (BaseMolecule &mol, int rsite); static bool _hasNeighborEitherBond (BaseMolecule &mol, int edge_idx); static int _getStereocenterParity (BaseMolecule &mol, int idx); bool _getRingBondCountFlagValue (QueryMolecule &qmol, int idx, int &value); bool _getSubstitutionCountFlagValue (QueryMolecule &qmol, int idx, int &value); void _updateCIPStereoDescriptors(BaseMolecule &mol); void _addCIPStereoDescriptors(BaseMolecule &mol); static int _cip_rules_cmp (int &i1, int &i2, void *context); Output &_output; bool _v2000; CP_DECL; TL_CP_DECL(Array<int>, _atom_mapping); TL_CP_DECL(Array<int>, _bond_mapping); enum { _SGROUP_TYPE_SUP = 1, _SGROUP_TYPE_DAT, _SGROUP_TYPE_SRU, _SGROUP_TYPE_MUL, _SGROUP_TYPE_GEN }; private: MolfileSaver (const MolfileSaver &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/multiple_cdx_loader.h��������������������������������������������������0000664�0000000�0000000�00000003122�12710376503�0022174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __multiple_cdx_loader__ #define __multiple_cdx_loader__ #include "base_cpp/tlscont.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/properties_map.h" namespace indigo { class Scanner; class MultipleCdxLoader { public: DECL_ERROR; MultipleCdxLoader (Scanner &scanner); bool isEOF (); void readNext (); void readAt (int index); int tell (); int currentNumber (); int count (); bool isReaction(); CP_DECL; TL_CP_DECL(Array<char>, data); TL_CP_DECL(PropertiesMap, properties); protected: TL_CP_DECL(Array<int>, _offsets); TL_CP_DECL(Array<char>, _latest_text); Scanner &_scanner; int _current_number; int _max_offset; bool _reaction; void _checkHeader (); bool _findObject (int &beg, int &length); bool _hasNextObject (); void _skipObject (); void _getObject (); void _getString (int size, Array<char> &str); void _getValue (int type, int size, Array<char> &str); }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/multiple_cml_loader.h��������������������������������������������������0000664�0000000�0000000�00000002431�12710376503�0022173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __multiple_cml_loader__ #define __multiple_cml_loader__ #include "base_cpp/tlscont.h" #include "base_cpp/reusable_obj_array.h" namespace indigo { class Scanner; class MultipleCmlLoader { public: DECL_ERROR; MultipleCmlLoader (Scanner &scanner); bool isEOF (); void readNext (); void readAt (int index); int tell (); int currentNumber (); int count (); bool isReaction(); CP_DECL; TL_CP_DECL(Array<char>, data); protected: TL_CP_DECL(ReusableObjArray< Array<char> >, _tags); TL_CP_DECL(Array<int>, _offsets); Scanner &_scanner; int _current_number; int _max_offset; bool _reaction; }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/query_molecule.h�������������������������������������������������������0000664�0000000�0000000�00000027722�12710376503�0021223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __query_molecule_h__ #define __query_molecule_h__ #include "molecule/base_molecule.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/ptr_array.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/molecule_arom.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { enum { SKIP_3D_CONSTRAINTS = 0x0100, SKIP_FIXED_ATOMS = 0x0200, SKIP_RGROUPS = 0x0400, SKIP_AROMATICITY = 0x0800, SKIP_COMPONENTS = 0x1000 }; class Output; class DLLEXPORT QueryMolecule : public BaseMolecule { public: enum OpType { OP_NONE, // used on totally unconstrained atoms OP_AND, OP_OR, OP_NOT, ATOM_NUMBER, ATOM_PSEUDO, ATOM_RSITE, ATOM_CHARGE, ATOM_ISOTOPE, ATOM_RADICAL, ATOM_VALENCE, //ATOM_DEGREE, ATOM_CONNECTIVITY, ATOM_TOTAL_BOND_ORDER, ATOM_TOTAL_H, //ATOM_IMPLICIT_H, ATOM_SUBSTITUENTS, ATOM_SUBSTITUENTS_AS_DRAWN, ATOM_SSSR_RINGS, ATOM_SMALLEST_RING_SIZE, ATOM_RING_BONDS, ATOM_RING_BONDS_AS_DRAWN, ATOM_UNSATURATION, ATOM_FRAGMENT, ATOM_AROMATICITY, ATOM_TEMPLATE, ATOM_TEMPLATE_SEQID, ATOM_TEMPLATE_CLASS, BOND_ORDER, BOND_TOPOLOGY, HIGHLIGHTING }; class DLLEXPORT Node { public: Node (int type_); virtual ~Node (); OpType type; // OP_*** or ATOM_*** or BOND_*** // type is OP_NOT: one child // type is OP_AND or OP_OR: more that one child // otherwise: no children PtrArray<Node> children; // Check if node has any constraint of the specific type bool hasConstraint (int what_type); // Check if there is no other constraint, except specified ones bool hasNoConstraintExcept (int what_type); bool hasNoConstraintExcept (int what_type1, int what_type2); // Remove all constraints of the given type void removeConstraints (int what_type); bool sureValue (int what_type, int &value); bool sureValueInv (int what_type, int &value); bool possibleValue (int what_type, int what_value); bool possibleValueInv (int what_type, int what_value); bool possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2); bool possibleValuePairInv (int what_type1, int what_value1, int what_type2, int what_value2); bool sureValueBelongs (int what_type, const int *arr, int count); bool sureValueBelongsInv (int what_type, const int *arr, int count); // Optimize query for faster substructure search void optimize (); protected: // "neu" means "new" in German. This should have been a static // method, but static methods can not be virtual, and so it is not static. virtual Node * _neu () = 0; static Node * _und (Node *node1, Node *node2); static Node * _oder (Node *node1, Node *node2); static Node * _nicht (Node *node); virtual bool _possibleValue (int what_type, int what_value) = 0; virtual bool _possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2) = 0; Node* _findSureConstraint (int what_type, int &count); virtual bool _sureValue (int what_type, int &value_out) = 0; virtual bool _sureValueBelongs (int what_type, const int *arr, int count) = 0; virtual void _optimize () {}; }; class DLLEXPORT Atom : public Node { public: Atom (); Atom (int type, int value); Atom (int type, int value_min, int value_max); Atom (int type, const char *value); Atom (int type, QueryMolecule *value); virtual ~Atom (); Atom * clone (); void copy (Atom &other); Atom * child (int idx); bool valueWithinRange (int value); bool hasConstraintWithValue (int what_type, int what_value); Atom* sureConstraint (int what_type); int value_min; int value_max; // available only when type is ATOM_PSEUDO or ATOM_TEMPLATE or ATOM_TEMPLATE_CLASS Array<char> alias; // available only when type is ATOM_FRAGMENT AutoPtr<QueryMolecule> fragment; // when type is ATOM_RSITE, the value (value_min=valuemax) // are 32 bits, each allowing an r-group with corresponding number // to go for this atom. Simple 'R' atoms have this field equal to zero. // "und" means "and" in German. "and" is a C++ keyword. static Atom * und (Atom *atom1, Atom *atom2); // "oder" means "or" in German. "or" is a C++ keyword. static Atom * oder (Atom *atom1, Atom *atom2); // "nicht" means "not" in German. "not" is a C++ keyword. static Atom * nicht (Atom *atom); protected: virtual Node * _neu (); virtual bool _possibleValue (int what_type, int what_value); virtual bool _possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2); virtual bool _sureValue (int what_type, int &value_out); virtual bool _sureValueBelongs (int what_type, const int *arr, int count); virtual void _optimize (); DECL_ERROR; }; class DLLEXPORT Bond : public Node { public: Bond (); Bond (int type_, int value_); virtual ~Bond (); int value; Bond * clone (); Bond * child (int idx); // "und" means "and" in German. "and" is a C++ keyword. static Bond * und (Bond *node1, Bond *node2); // "oder" means "or" in German. "or" is a C++ keyword. static Bond * oder (Bond *node1, Bond *node2); // "nicht" means "not" in German. "not" is a C++ keyword. static Bond * nicht (Bond *node); protected: virtual Node * _neu (); virtual bool _possibleValue (int what_type, int what_value); virtual bool _possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2); virtual bool _sureValue (int what_type, int &value_out); virtual bool _sureValueBelongs (int what_type, const int *arr, int count); }; QueryMolecule (); virtual ~QueryMolecule (); virtual void clear (); virtual BaseMolecule * neu (); virtual QueryMolecule& asQueryMolecule (); virtual bool isQueryMolecule (); virtual int getAtomNumber (int idx); virtual int getAtomCharge (int idx); virtual int getAtomIsotope (int idx); virtual int getAtomRadical (int idx); virtual int getExplicitValence (int idx); virtual int getAtomAromaticity (int idx); virtual int getAtomValence (int idx); virtual int getAtomSubstCount (int idx); virtual int getAtomRingBondsCount (int idx); virtual int getAtomMaxH (int idx); virtual int getAtomMinH (int idx); virtual int getAtomTotalH (int idx); virtual bool isPseudoAtom (int idx); virtual const char * getPseudoAtom (int idx); virtual bool isTemplateAtom (int idx); virtual const char * getTemplateAtom (int idx); virtual const int getTemplateAtomSeqid (int idx); virtual const char * getTemplateAtomClass (int idx); virtual const int getTemplateAtomDisplayOption (int idx); virtual bool isRSite (int atom_idx); virtual dword getRSiteBits (int atom_idx); virtual void allowRGroupOnRSite (int atom_idx, int rg_idx); virtual bool isSaturatedAtom (int idx); virtual int getBondOrder (int idx); virtual int getBondTopology (int idx); virtual bool atomNumberBelongs (int idx, const int *numbers, int count); virtual bool possibleAtomNumber (int idx, int number); virtual bool possibleAtomNumberAndCharge (int idx, int number, int charge); virtual bool possibleAtomNumberAndIsotope (int idx, int number, int isotope); virtual bool possibleAtomIsotope (int idx, int number); virtual bool possibleAtomCharge (int idx, int charge); virtual bool possibleAtomRadical (int idx, int radical); virtual void getAtomDescription (int idx, Array<char> &description); virtual void getBondDescription (int idx, Array<char> &description); virtual bool possibleBondOrder (int idx, int order); bool possibleNitrogenV5 (int idx); enum QUERY_ATOM {QUERY_ATOM_A, QUERY_ATOM_X, QUERY_ATOM_Q, QUERY_ATOM_LIST, QUERY_ATOM_NOTLIST}; enum QUERY_BOND {QUERY_BOND_DOUBLE_OR_AROMATIC = 0, QUERY_BOND_SINGLE_OR_AROMATIC, QUERY_BOND_SINGLE_OR_DOUBLE, QUERY_BOND_ANY}; static bool isKnownAttr (QueryMolecule::Atom& qa); static bool isNotAtom (QueryMolecule::Atom& qa, int elem); static QueryMolecule::Atom* stripKnownAttrs (QueryMolecule::Atom& qa); static bool collectAtomList (Atom& qa, Array<int>& list, bool& notList); static int parseQueryAtom (QueryMolecule& qm, int aid, Array<int>& list); static bool queryAtomIsRegular (QueryMolecule& qm, int aid); static Bond* getBondOrderTerm (Bond& qb, bool& complex); static bool isOrBond (Bond& qb, int type1, int type2); static bool isSingleOrDouble (Bond& qb); static int getQueryBondType (Bond& qb); virtual bool bondStereoCare (int idx); void setBondStereoCare (int idx, bool stereo_care); virtual bool aromatize (const AromaticityOptions &options); virtual bool dearomatize (const AromaticityOptions &options); int addAtom (Atom *atom); Atom & getAtom (int idx); Atom * releaseAtom (int idx); void resetAtom (int idx, Atom *atom); Bond & getBond (int idx); Bond * releaseBond (int idx); void resetBond (int idx, Bond *bond); int addBond (int beg, int end, Bond *bond); void optimize (); Molecule3dConstraints spatial_constraints; Array<int> fixed_atoms; QueryMoleculeAromaticity aromaticity; Array<char> fragment_smarts; // for component-level grouping of SMARTS // components[i] = 0 means nothing; // components[i] = components[j] > 0 means that i-th and j-th vertices // must belong to the same connected component of the target molecule; // components[i] != components[j] > 0 means that i-th and j-th vertices // must belong to different connected components of the target molecule Array<int> components; virtual void invalidateAtom (int index, int mask); int getAtomMaxExteralConnectivity (int idx); bool standardize (const StandardizeOptions &options); protected: int _calcAtomConnectivity (int idx); void _getAtomDescription (Atom *atom, Output &out, int depth); void _getBondDescription (Bond *bond, Output &out); int _getAtomMinH (Atom *atom); virtual void _flipBond (int atom_parent, int atom_from, int atom_to); virtual void _mergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags); virtual void _postMergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags); virtual void _removeAtoms (const Array<int> &indices, const int *mapping); virtual void _removeBonds (const Array<int> &indices); Array<int> _min_h; Array<bool> _bond_stereo_care; PtrArray<Atom> _atoms; PtrArray<Bond> _bonds; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������Indigo-indigo-1.2.3/molecule/rdf_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000004324�12710376503�0020263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _RDF_LOADER_H__ #define _RDF_LOADER_H__ #include "base_cpp/tlscont.h" #include "base_cpp/obj.h" #include "base_cpp/properties_map.h" namespace indigo { class Scanner; /* * RD files loader * An RDfile (reaction-data file) consists of a set of editable “records.†Each record defines a * molecule or reaction, and its associated data * Note: internal-regno and external-regno are placed into the properties with corresponding names */ class RdfLoader { /* * Max data size is 100 Mb */ enum { MAX_DATA_SIZE = 104857600 }; public: RdfLoader (Scanner &scanner); ~RdfLoader (); bool isEOF (); void readNext (); void readAt (int index); int tell (); int currentNumber (); int count (); CP_DECL; /* * Data buffer with reaction or molecule for current record */ TL_CP_DECL(Array<char>, data); /* * Properties map for current record */ TL_CP_DECL(PropertiesMap, properties); /* * Defines is molecule or reaction there in the current record */ bool isMolecule() const { return _isMolecule;} DECL_ERROR; protected: bool _readIdentifiers(bool); inline Scanner& _getScanner() const; static bool _readLine(Scanner&, Array<char>&); inline bool _startsWith(const char* str) const { return ((size_t)_innerBuffer.size() >= strlen(str) && strncmp(_innerBuffer.ptr(), str, strlen(str))==0); } TL_CP_DECL(Array<char>, _innerBuffer); bool _ownScanner; Scanner *_scanner; bool _isMolecule; TL_CP_DECL(Array<int>, _offsets); int _current_number; int _max_offset; }; } #endif /* _RDF_READER_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/sdf_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000002567�12710376503�0020273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __sdf_loader__ #define __sdf_loader__ #include "base_cpp/tlscont.h" #include "base_cpp/red_black.h" #include "base_cpp/properties_map.h" namespace indigo { class Scanner; class SdfLoader { /* * Max data size is 10 Mb */ enum { MAX_DATA_SIZE = 10485760 }; public: SdfLoader (Scanner &scanner); ~SdfLoader (); bool isEOF (); void readNext (); int tell (); int currentNumber (); int count (); void readAt (int index); CP_DECL; TL_CP_DECL(Array<char>, data); TL_CP_DECL(PropertiesMap, properties); DECL_ERROR; protected: Scanner *_scanner; bool _own_scanner; TL_CP_DECL(Array<int>, _offsets); TL_CP_DECL(Array<char>, _preread); int _current_number; int _max_offset; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/smiles_loader.h��������������������������������������������������������0000664�0000000�0000000�00000010632�12710376503�0021003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __smiles_loader__ #define __smiles_loader__ #include "base_cpp/exception.h" #include "molecule/molecule.h" #include "base_cpp/tlscont.h" #include "molecule/query_molecule.h" #include "molecule/molecule_stereocenter_options.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Scanner; class BaseMolecule; class Molecule; class QueryMolecule; class DLLEXPORT SmilesLoader { public: DECL_ERROR; SmilesLoader (Scanner &scanner); ~SmilesLoader (); void loadMolecule (Molecule &mol); void loadQueryMolecule (QueryMolecule &mol); void loadSMARTS (QueryMolecule &mol); Array<int> * reaction_atom_mapping; Array<int> * ignorable_aam; bool inside_rsmiles; bool smarts_mode; // set to true to accept buggy SMILES like 'N/C=C\1CCCN[C@H]\1S' // (see http://groups.google.com/group/indigo-bugs/browse_thread/thread/de7da07a3a5cb3ee // for details) bool ignore_closing_bond_direction_mismatch; StereocentersOptions stereochemistry_options; bool ignore_cistrans_errors; protected: enum { _ANY_BOND = -2 }; enum { _POLYMER_START = 1, _POLYMER_END = 2 }; class DLLEXPORT _AtomDesc { public: _AtomDesc (Pool<List<int>::Elem> &neipool); ~_AtomDesc (); void pending (int cycle); void closure (int cycle, int end); List<int> neighbors; int parent; int label; int isotope; int charge; int hydrogens; int chirality; int aromatic; int aam; bool ignorable_aam; bool brackets; bool star_atom; bool starts_polymer; bool ends_polymer; int polymer_index; }; struct _BondDesc { int beg; int end; int type; int dir; // 0 -- undirected; 1 -- goes 'up' from beg to end, 2 -- goes 'down' int topology; int index; }; struct _CycleDesc { void clear () { beg = -1; pending_bond = -1; pending_bond_str = -1; } int beg; int pending_bond; int pending_bond_str; // index in pending_bonds_pool; }; Scanner &_scanner; CP_DECL; TL_CP_DECL(Array<int>, _atom_stack); TL_CP_DECL(Array<_CycleDesc>, _cycles); TL_CP_DECL(StringPool, _pending_bonds_pool); TL_CP_DECL(Pool<List<int>::Elem>, _neipool); TL_CP_DECL(ObjArray<_AtomDesc>, _atoms); TL_CP_DECL(Array<_BondDesc>, _bonds); TL_CP_DECL(Array<int>, _polymer_repetitions); int _balance; int _current_compno; bool _inside_smarts_component; BaseMolecule *_bmol; QueryMolecule *_qmol; Molecule *_mol; void _loadMolecule (); void _parseMolecule (); void _loadParsedMolecule (); void _calcStereocenters (); void _calcCisTrans (); void _readOtherStuff (); void _markAromaticBonds (); void _setRadicalsAndHCounts (); void _forbidHydrogens (); void _addExplicitHForStereo (); void _addLigandsForStereo (); bool _isAlleneLike (int i); void _handleCurlyBrace (_AtomDesc &atom, bool &inside_polymer); void _handlePolymerRepetition (int i); void _readAtom (Array<char> &atom_str, bool first_in_brackets, _AtomDesc &atom, AutoPtr<QueryMolecule::Atom> &qatom); bool _readAtomLogic (Array<char> &atom_str, bool first_in_brackets, _AtomDesc &atom, AutoPtr<QueryMolecule::Atom> &qatom); int _parseCurly (Array<char> &curly, int &repetitions); void _readBond (Array<char> &bond_str, _BondDesc &bond, AutoPtr<QueryMolecule::Bond> &qbond); void _readBondSub (Array<char> &bond_str, _BondDesc &bond, AutoPtr<QueryMolecule::Bond> &qbond); private: SmilesLoader (const SmilesLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/smiles_saver.h���������������������������������������������������������0000664�0000000�0000000�00000012361�12710376503�0020656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __smiles_saver__ #define __smiles_saver__ #include "base_cpp/exception.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "molecule/query_molecule.h" #include "base_cpp/list.h" #include "base_cpp/obj_array.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Output; class BaseMolecule; class QueryMolecule; class Molecule; class DLLEXPORT SmilesSaver { public: DECL_ERROR; SmilesSaver (Output &output); ~SmilesSaver (); void saveMolecule (Molecule &mol); void saveQueryMolecule (QueryMolecule &mol); int *vertex_ranks; const int *atom_atom_mapping; bool ignore_hydrogens; bool canonize_chiralities; bool write_extra_info; bool separate_rsites; bool rsite_indices_as_aam; int writtenComponents (); const Array<int> & writtenAtoms (); const Array<int> & writtenBonds (); static void writePseudoAtom (const char *label, Output &out); bool smarts_mode; bool ignore_invalid_hcount; const Array<int>& getSavedCisTransParities (); protected: void _saveMolecule (); BaseMolecule *_bmol; Molecule *_mol; QueryMolecule *_qmol; struct _Atom { _Atom (Pool<List<int>::Elem> &neipool); ~_Atom (); List<int> neighbors; int parent; bool aromatic; bool lowercase; int chirality; // 0 means no chirality, 1 means CCW pyramid, 2 means CW pyramid int branch_cnt; // runs from 0 to (branches - 1) bool paren_written; bool starts_polymer; bool ends_polymer; }; Output &_output; void _writeCycleNumber (int n) const; void _writeAtom (int idx, bool aromatic, bool lowercase, int chirality) const; void _writeChirality (int chirality) const; void _writeCharge (int charge) const; void _writeSmartsAtom (int idx, QueryMolecule::Atom *atom, int chirality, int depth, bool has_or_parent) const; void _writeSmartsBond (int idx, QueryMolecule::Bond *bond) const; void _markCisTrans (); void _banSlashes (); int _calcBondDirection (int idx, int vprev); bool _updateSideBonds (int bond_idx); void _writeRingCisTrans (); void _writeStereogroups (); void _writeRadicals (); void _writePseudoAtoms (); void _writeHighlighting (); bool _shouldWriteAromaticBond (int bond_idx); void _startExtension (); void _filterCisTransParity (); int _countRBonds (); void _checkSRU (); void _checkRGroupsAndAttachmentPoints (); struct _DBond // directed bond (near cis-trans bond) { int ctbond_beg; // cis-trans bond attached to the beginning (-1 if there isn't any) int ctbond_end; // cis-trans bond attached to the end (-1 if there isn't any) int saved; // 0 -- not saved; 1 -- goes 'up' from begin to end; 2 -- goes 'down' }; CP_DECL; TL_CP_DECL(Pool<List<int>::Elem>, _neipool); TL_CP_DECL(ObjArray<_Atom>, _atoms); TL_CP_DECL(Array<int>, _hcount); TL_CP_DECL(Array<int>, _hcount_ignored); TL_CP_DECL(Array<_DBond>, _dbonds); TL_CP_DECL(Array<int>, _written_atoms); TL_CP_DECL(Array<int>, _written_atoms_inv); TL_CP_DECL(Array<int>, _written_bonds); TL_CP_DECL(Array<int>, _polymer_indices); TL_CP_DECL(Array<int>, _attachment_indices); TL_CP_DECL(Array<int>, _attachment_cycle_numbers); TL_CP_DECL(Array<int>, _aromatic_bonds); TL_CP_DECL(Array<int>, _ignored_vertices); // Some cis-trans bonds are considered "complicated", they are either: // 1. Ring bonds, which can not be saved with slash notation (conflicts are // unavoidable) // 2. Bonds that share their adjacent single with other cis-trans bonds that // have "unset" parity, for example: OC=CC=CC=CN |t:1,5| // for another example: C/C=C/C(/C=C/C)=C(/C=C/C)/C=C/C // Marvin erroneously saves that structure as N\C=C\C=C\C=C\O, which is // incorrect, as it introduces cis-trans parity on the middle bond // 1+2: C[N+](O)=C1C=CC(=CO)C=C1 TL_CP_DECL(Array<int>, _complicated_cistrans); // single bonds that can not be written as slashes; see item 2 above TL_CP_DECL(Array<int>, _ban_slashes); // array with cis-trans parity marks // 0 means ignored TL_CP_DECL(Array<int>, _cis_trans_parity); // This flag does not necessarily mean "any of _complicated_cistrans == 1". // If all _complicated_cistrans are actually ring CIS bonds, then the flag // is not set. bool _have_complicated_cistrans; int _n_attachment_points; int _written_components; int _touched_cistransbonds; bool _comma; private: SmilesSaver (const SmilesSaver &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016575�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/base_molecule.cpp��������������������������������������������������0000664�0000000�0000000�00000141660�12710376503�0022110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/base_molecule.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "molecule/elements.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_exact_substructure_matcher.h" using namespace indigo; IMPL_ERROR(BaseMolecule, "molecule"); BaseMolecule::BaseMolecule () { _edit_revision = 0; } BaseMolecule::~BaseMolecule () { } Molecule& BaseMolecule::asMolecule () { throw Error("casting to molecule is invalid"); } QueryMolecule& BaseMolecule::asQueryMolecule () { throw Error("casting to query molecule is invalid"); } bool BaseMolecule::isQueryMolecule () { return false; } void BaseMolecule::clear () { have_xyz = false; name.clear(); stereocenters.clear(); cis_trans.clear(); allene_stereo.clear(); rgroups.clear(); _xyz.clear(); _rsite_attachment_points.clear(); _attachment_index.clear(); sgroups.clear(); Graph::clear(); _hl_atoms.clear(); _hl_bonds.clear(); _bond_directions.clear(); custom_collections.clear(); updateEditRevision(); } bool BaseMolecule::hasCoord (BaseMolecule &mol) { int i; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { Vec3f &xyz = mol.getAtomXyz(i); if (fabs(xyz.x) > 0.001 || fabs(xyz.y) > 0.001 || fabs(xyz.z) > 0.001) return true; } return false; } bool BaseMolecule::hasZCoord (BaseMolecule &mol) { int i; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (fabs(mol.getAtomXyz(i).z) > 0.001) return true; return false; } void BaseMolecule::mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array<int> &mapping) { QS_DEF(Array<int>, edge_mapping); edge_mapping.clear_resize(mol.edgeEnd()); edge_mapping.fffill(); buildEdgeMapping(mol, &mapping, &edge_mapping); mergeSGroupsWithSubmolecule(mol, mapping, edge_mapping); } void BaseMolecule::mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array<int> &mapping, Array<int> &edge_mapping) { int i; for (i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &supersg = mol.sgroups.getSGroup(i); int idx = sgroups.addSGroup(supersg.sgroup_type); SGroup &sg = sgroups.getSGroup(idx); if (_mergeSGroupWithSubmolecule(sg, supersg, mol, mapping, edge_mapping)) { if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; DataSGroup &superdg = (DataSGroup &)supersg; dg.detached = superdg.detached; dg.display_pos = superdg.display_pos; dg.data.copy(superdg.data); dg.dasp_pos = superdg.dasp_pos; dg.relative = superdg.relative; dg.display_units = superdg.display_units; dg.description.copy(superdg.description); dg.name.copy(superdg.name); dg.type.copy(superdg.type); dg.querycode.copy(superdg.querycode); dg.queryoper.copy(superdg.queryoper); dg.num_chars = superdg.num_chars; dg.tag = superdg.tag; } else if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sg; Superatom &supersa = (Superatom &)supersg; if (supersa.bond_connections.size() > 0) { for (int j = 0; j < supersa.bond_connections.size(); j++) { Superatom::_BondConnection &bond = sa.bond_connections.push(); bond.bond_dir = supersa.bond_connections[j].bond_dir; bond.bond_idx = edge_mapping[supersa.bond_connections[j].bond_idx]; } } sa.subscript.copy(supersa.subscript); sa.sa_class.copy(supersa.sa_class); sa.contracted = supersa.contracted; if (supersa.attachment_points.size() > 0) { for (int j = supersa.attachment_points.begin(); j < supersa.attachment_points.end(); j = supersa.attachment_points.next(j)) { int ap_idx = sa.attachment_points.add(); Superatom::_AttachmentPoint &ap = sa.attachment_points.at(ap_idx); ap.aidx = mapping[supersa.attachment_points[j].aidx]; int leave_idx = supersa.attachment_points[j].lvidx; if (leave_idx > -1) ap.lvidx = mapping[supersa.attachment_points[j].lvidx]; else ap.lvidx = leave_idx; ap.apid.copy(supersa.attachment_points[j].apid); } } } else if (sg.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &ru = (RepeatingUnit &)sg; RepeatingUnit &superru = (RepeatingUnit &)supersg; ru.connectivity = superru.connectivity; ru.subscript.copy(superru.subscript); } else if (sg.sgroup_type == SGroup::SG_TYPE_MUL) { MultipleGroup &mg = (MultipleGroup &)sg; MultipleGroup &supermg = (MultipleGroup &)supersg; mg.multiplier = supermg.multiplier; for (int j = 0; j != supermg.parent_atoms.size(); j++) if (mapping[supermg.parent_atoms[j]] >= 0) mg.parent_atoms.push(mapping[supermg.parent_atoms[j]]); } } else sgroups.remove(idx); } } void BaseMolecule::clearSGroups() { sgroups.clear(); } void BaseMolecule::_mergeWithSubmolecule_Sub (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, Array<int> &mapping, Array<int> &edge_mapping, int skip_flags) { QS_DEF(Array<char>, apid); int i; // XYZ _xyz.resize(vertexEnd()); if (!(skip_flags & SKIP_XYZ)) { if (vertexCount() == 0) have_xyz = mol.have_xyz; else have_xyz = have_xyz || mol.have_xyz; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mapping[i] < 0) continue; _xyz[mapping[i]] = mol.getAtomXyz(i); } } else _xyz.zerofill(); _bond_directions.expandFill(mol.edgeEnd(), 0); // trick for molecules with incorrect stereochemistry, of which we do permutations if (vertexCount() == mol.vertexCount() && edgeCount() == mol.edgeCount()) { for (int j = mol.edgeBegin(); j != mol.edgeEnd(); j = mol.edgeNext(j)) { const Edge &edge = mol.getEdge(j); if (mol.getBondDirection(j) != 0) _bond_directions[findEdgeIndex(mapping[edge.beg], mapping[edge.end])] = mol.getBondDirection(j); } } // RGroups if (!(skip_flags & SKIP_RGROUPS)) { rgroups.copyRGroupsFromMolecule(mol.rgroups); for (i = 0; i < vertices.size(); i++) { if (!mol.isRSite(vertices[i])) continue; int atom_idx = mapping[vertices[i]]; if (atom_idx == -1) continue; if (mol._rsite_attachment_points.size() <= vertices[i]) continue; Array<int> &ap = mol._rsite_attachment_points[vertices[i]]; int j; for (j = 0; j < ap.size(); j++) if (ap[j] >= 0 && ap[j] < mapping.size() && mapping[ap[j]] >= 0) setRSiteAttachmentOrder(atom_idx, mapping[ap[j]], j); } } if (!(skip_flags & SKIP_ATTACHMENT_POINTS)) { if (mol.attachmentPointCount() > 0) { for (i = 1; i <= mol.attachmentPointCount(); i++) { int att_idx; int j; for (j = 0; (att_idx = mol.getAttachmentPoint(i, j)) != -1; j++) if (mapping[att_idx] != -1) this->addAttachmentPoint(i, mapping[att_idx]); } } } if (!(skip_flags & SKIP_TGROUPS)) { tgroups.copyTGroupsFromMolecule(mol.tgroups); } if (!(skip_flags & SKIP_TEMPLATE_ATTACHMENT_POINTS)) { for (i = 0; i < vertices.size(); i++) { if (mol.isTemplateAtom(vertices[i])) { for (int j = 0; j < mol.getTemplateAtomAttachmentPointsCount(vertices[i]); j++) { if ( (mol.getTemplateAtomAttachmentPoint(vertices[i], j) != -1) && (mapping[mol.getTemplateAtomAttachmentPoint(vertices[i], j)] != -1) ) { mol.getTemplateAtomAttachmentPointId(vertices[i], j, apid); setTemplateAtomAttachmentOrder(mapping[vertices[i]], mapping[mol.getTemplateAtomAttachmentPoint(vertices[i], j)], apid.ptr()); } } } } } // SGroups merging mergeSGroupsWithSubmolecule(mol, mapping, edge_mapping); // highlighting highlightSubmolecule(mol, mapping.ptr(), false); // subclass stuff (Molecule or QueryMolecule) _mergeWithSubmolecule(mol, vertices, edges, mapping, skip_flags); // stereo if (!(skip_flags & SKIP_STEREOCENTERS)) stereocenters.buildOnSubmolecule(mol.stereocenters, mapping.ptr()); else stereocenters.clear(); if (!(skip_flags & SKIP_CIS_TRANS)) cis_trans.buildOnSubmolecule(mol, mapping.ptr()); else cis_trans.clear(); allene_stereo.buildOnSubmolecule(mol.allene_stereo, mapping.ptr()); // subclass stuff (Molecule or QueryMolecule) _postMergeWithSubmolecule(mol, vertices, edges, mapping, skip_flags); updateEditRevision(); } void BaseMolecule::_flipSGroupBond(SGroup &sgroup, int src_bond_idx, int new_bond_idx) { int idx = sgroup.bonds.find(src_bond_idx); if (idx != -1) sgroup.bonds[idx] = new_bond_idx; } void BaseMolecule::_flipSuperatomBond(Superatom &sa, int src_bond_idx, int new_bond_idx) { if (sa.bond_connections.size() > 0) { for (int j = 0; j < sa.bond_connections.size(); j++) { Superatom::_BondConnection &bond = sa.bond_connections[j]; if (bond.bond_idx == src_bond_idx) bond.bond_idx = new_bond_idx; } } } void BaseMolecule::_flipTemplateAtomAttachmentPoint(int idx, int atom_from, int atom_to) { for (int j = template_attachment_points.begin(); j != template_attachment_points.end(); j = template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = template_attachment_points.at(j); if ( (ap.ap_occur_idx == idx) && (ap.ap_aidx == atom_from) ) { ap.ap_aidx = atom_to; } } } void BaseMolecule::mergeWithSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, Array<int> *mapping_out, int skip_flags) { QS_DEF(Array<int>, tmp_mapping); QS_DEF(Array<int>, edge_mapping); if (mapping_out == 0) mapping_out = &tmp_mapping; // vertices and edges _mergeWithSubgraph(mol, vertices, edges, mapping_out, &edge_mapping); // all the chemical stuff _mergeWithSubmolecule_Sub(mol, vertices, edges, *mapping_out, edge_mapping, skip_flags); } int BaseMolecule::mergeAtoms (int atom1, int atom2) { updateEditRevision(); const Vertex &v1 = getVertex(atom1); const Vertex &v2 = getVertex(atom2); int is_tetra1 = false, is_cs1 = false, cs_bond1_idx = -1; int is_tetra2 = false, is_cs2 = false, cs_bond2_idx = -1; if (stereocenters.exists(atom1)) is_tetra1 = true; if (stereocenters.exists(atom2)) is_tetra2 = true; for (int i = v1.neiBegin(); i != v1.neiEnd(); i = v1.neiNext(i)) if (MoleculeCisTrans::isGeomStereoBond(*this, v1.neiEdge(i), NULL, false)) { cs_bond1_idx = v1.neiEdge(i); is_cs1 = true; break; } for (int i = v2.neiBegin(); i != v2.neiEnd(); i = v2.neiNext(i)) if (MoleculeCisTrans::isGeomStereoBond(*this, v2.neiEdge(i), NULL, false)) { cs_bond2_idx = v2.neiEdge(i); is_cs2 = true; break; } if (((is_tetra1 || is_cs1) && (is_tetra2 || is_cs2)) || (!is_tetra1 && !is_cs1 && !is_tetra2 && !is_cs2)) { if (is_tetra1) stereocenters.remove(atom1); if (is_cs1) cis_trans.setParity(cs_bond1_idx, 0); if (is_tetra2) stereocenters.remove(atom2); if (is_cs2) cis_trans.setParity(cs_bond2_idx, 0); QS_DEF(Array<int>, neighbors); neighbors.clear(); for (int i = v2.neiBegin(); i != v2.neiEnd(); i = v2.neiNext(i)) neighbors.push(v2.neiVertex(i)); for (int i = 0; i < neighbors.size(); i++) if (findEdgeIndex(neighbors[i], atom1) == -1) flipBond(neighbors[i], atom2, atom1); removeAtom(atom2); return atom1; } if (is_tetra1 || is_cs1) { if (v2.degree() > 1) return -1; if (is_tetra1 && stereocenters.getPyramid(atom1)[3] != -1) return -1; if (is_cs1 && v1.degree() != 2) return -1; flipBond(v2.neiVertex(v2.neiBegin()), atom2, atom1); removeAtom(atom2); return atom1; } else { if (v1.degree() > 1) return -1; if (is_tetra2 && stereocenters.getPyramid(atom2)[3] != -1) return -1; if (is_cs2 && v2.degree() != 2) return -1; flipBond(v1.neiVertex(v1.neiBegin()), atom1, atom2); removeAtom(atom1); return atom2; } } void BaseMolecule::flipBond (int atom_parent, int atom_from, int atom_to) { stereocenters.flipBond(atom_parent, atom_from, atom_to); cis_trans.flipBond(atom_parent, atom_from, atom_to); // subclass (Molecule or QueryMolecule) adds the new bond _flipBond(atom_parent, atom_from, atom_to); int src_bond_idx = findEdgeIndex(atom_parent, atom_from); removeEdge(src_bond_idx); int new_bond_idx = findEdgeIndex(atom_parent, atom_to); // Clear bond direction because sterecenters // should mark bond directions properly setBondDirection(new_bond_idx, 0); // sgroups int j; for (j = sgroups.begin(); j != sgroups.end(); j = sgroups.next(j)) { SGroup &sg = sgroups.getSGroup(j); _flipSGroupBond(sg, src_bond_idx, new_bond_idx); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) _flipSuperatomBond((Superatom &)sg, src_bond_idx, new_bond_idx); } updateEditRevision(); } void BaseMolecule::makeSubmolecule (BaseMolecule &mol, const Array<int> &vertices, Array<int> *mapping_out, int skip_flags) { clear(); mergeWithSubmolecule(mol, vertices, 0, mapping_out, skip_flags); } void BaseMolecule::makeSubmolecule (BaseMolecule &other, const Filter &filter, Array<int> *mapping_out, Array<int> *inv_mapping, int skip_flags) { QS_DEF(Array<int>, vertices); if (mapping_out == 0) mapping_out = &vertices; filter.collectGraphVertices(other, *mapping_out); makeSubmolecule(other, *mapping_out, inv_mapping, skip_flags); } void BaseMolecule::makeEdgeSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, Array<int> *v_mapping, int skip_flags) { clear(); mergeWithSubmolecule(mol, vertices, &edges, v_mapping, skip_flags); } void BaseMolecule::clone (BaseMolecule &other, Array<int> *mapping, Array<int> *inv_mapping, int skip_flags) { QS_DEF(Array<int>, tmp_mapping); if (mapping == 0) mapping = &tmp_mapping; mapping->clear(); for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) mapping->push(i); makeSubmolecule(other, *mapping, inv_mapping, skip_flags); name.copy(other.name); } void BaseMolecule::clone_KeepIndices (BaseMolecule &other, int skip_flags) { QS_DEF(Array<int>, mapping); QS_DEF(Array<int>, edge_mapping); QS_DEF(Array<int>, vertices); int i; mapping.clear_resize(other.vertexEnd()); mapping.fffill(); vertices.clear(); for (i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) { vertices.push(i); mapping[i] = i; } edge_mapping.clear_resize(other.edgeEnd()); edge_mapping.fffill(); for (i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i)) edge_mapping[i] = i; _cloneGraph_KeepIndices(other); _mergeWithSubmolecule_Sub(other, vertices, 0, mapping, edge_mapping, skip_flags); name.copy(other.name); } void BaseMolecule::mergeWithMolecule (BaseMolecule &other, Array<int> *mapping, int skip_flags) { QS_DEF(Array<int>, vertices); int i; vertices.clear(); for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i)) vertices.push(i); mergeWithSubmolecule(other, vertices, 0, mapping, skip_flags); } void BaseMolecule::removeAtoms (const Array<int> &indices) { QS_DEF(Array<int>, mapping); int i, j; mapping.clear_resize(vertexEnd()); for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) mapping[i] = i; // Mark removed vertices for (i = 0; i < indices.size(); i++) mapping[indices[i]] = -1; // sgroups for (j = sgroups.begin(); j != sgroups.end(); j = sgroups.next(j)) { SGroup &sg = sgroups.getSGroup(j); _removeAtomsFromSGroup(sg, mapping); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) _removeAtomsFromSuperatom((Superatom &)sg, mapping); else if (sg.sgroup_type == SGroup::SG_TYPE_MUL) _removeAtomsFromMultipleGroup((MultipleGroup &)sg, mapping); if (sg.atoms.size() < 1) removeSGroup(j); } // stereo stereocenters.removeAtoms(indices); cis_trans.buildOnSubmolecule(*this, mapping.ptr()); allene_stereo.removeAtoms(indices); // highlighting for (i = 0; i < indices.size(); i++) { const Vertex &vertex = getVertex(indices[i]); unhighlightAtom(indices[i]); for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) unhighlightBond(vertex.neiEdge(j)); } // subclass (Molecule or QueryMolecule) removes its data _removeAtoms(indices, mapping.ptr()); // Remove vertices from graph for (i = 0; i < indices.size(); i++) removeVertex(indices[i]); updateEditRevision(); } void BaseMolecule::removeAtom (int idx) { QS_DEF(Array<int>, vertices); vertices.clear(); vertices.push(idx); removeAtoms(vertices); } void BaseMolecule::removeAtoms (const Filter &filter) { QS_DEF(Array<int>, vertices); filter.collectGraphVertices(*this, vertices); removeAtoms(vertices); } void BaseMolecule::removeBonds (const Array<int> &indices) { QS_DEF(Array<int>, mapping); int i, j; mapping.clear_resize(edgeEnd()); for (i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) mapping[i] = i; // Mark removed vertices for (i = 0; i < indices.size(); i++) mapping[indices[i]] = -1; // sgroups for (j = sgroups.begin(); j != sgroups.end(); j = sgroups.next(j)) { SGroup &sg = sgroups.getSGroup(j); _removeBondsFromSGroup(sg, mapping); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) _removeBondsFromSuperatom((Superatom &)sg, mapping); } // subclass (Molecule or QueryMolecule) removes its data _removeBonds(indices); stereocenters.removeBonds(indices); allene_stereo.removeBonds(indices); for (int i = 0; i < indices.size(); i++) { unhighlightBond(indices[i]); removeEdge(indices[i]); } updateEditRevision(); } void BaseMolecule::removeBond (int idx) { QS_DEF(Array<int>, edges); edges.clear(); edges.push(idx); removeBonds(edges); } void BaseMolecule::removeSGroup (int idx) { SGroup &sg = sgroups.getSGroup(idx); _checkSgroupHierarchy(sg.parent_group, sg.original_group); sgroups.remove(idx); } void BaseMolecule::removeSGroupWithBasis (int idx) { QS_DEF(Array<int>, sg_atoms); SGroup &sg = sgroups.getSGroup(idx); _checkSgroupHierarchy(sg.parent_group, sg.original_group); sg_atoms.copy(sg.atoms); removeAtoms(sg_atoms); } int BaseMolecule::getVacantPiOrbitals (int group, int charge, int radical, int conn, int *lonepairs_out) { int orbitals; if (conn < 0) throw Error("invalid connectivity given: %d", conn); switch (group) { case 1: orbitals = 1; break; case 2: orbitals = 2; break; default: orbitals = 4; } int free_electrons = group - conn - charge - radical; if (free_electrons < 0) return -1; int lonepair = free_electrons / 2; int implicit_radical = free_electrons % 2; int vacant = orbitals - conn - lonepair - radical - implicit_radical; if (vacant < 0) return -1; if (lonepairs_out != 0) *lonepairs_out = lonepair; return vacant; } void BaseMolecule::_postMergeWithSubmolecule (BaseMolecule &mol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) { } void BaseMolecule::_flipBond (int atom_parent, int atom_from, int atom_to) { } void BaseMolecule::_removeAtoms (const Array<int> &indices, const int *mapping) { } void BaseMolecule::_removeBonds (const Array<int> &indices) { } Vec3f & BaseMolecule::getAtomXyz (int idx) { return _xyz[idx]; } void BaseMolecule::setAtomXyz (int idx, float x, float y, float z) { _xyz[idx].set(x, y, z); updateEditRevision(); } void BaseMolecule::setAtomXyz (int idx, const Vec3f& v) { _xyz[idx].copy(v); updateEditRevision(); } void BaseMolecule::clearXyz () { for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) setAtomXyz(i, 0, 0, 0); have_xyz = 0; } int BaseMolecule::_addBaseAtom () { int idx = addVertex(); _xyz.expand(idx + 1); _xyz[idx].zero(); updateEditRevision(); return idx; } int BaseMolecule::_addBaseBond (int beg, int end) { int idx = addEdge(beg, end); cis_trans.registerBond(idx); updateEditRevision(); return idx; } int BaseMolecule::getAtomRadical_NoThrow (int idx, int fallback) { try { return getAtomRadical(idx); } catch (Element::Error &) { return fallback; } } int BaseMolecule::getAtomValence_NoThrow (int idx, int fallback) { try { return getAtomValence(idx); } catch (Element::Error &) { return fallback; } } int BaseMolecule::possibleAtomTotalH (int idx, int hcount) { int minh = getAtomMinH(idx); if (minh > hcount) return false; int maxh = getAtomMaxH(idx); if (maxh == -1) return true; if (maxh < hcount) return false; return true; } void BaseMolecule::getAllowedRGroups (int atom_idx, Array<int> &rgroup_list) { rgroup_list.clear(); dword bits = getRSiteBits(atom_idx); int rg_idx = 1; while (bits != 0) { if (bits & 1) rgroup_list.push(rg_idx); rg_idx++; bits >>= 1; } } int BaseMolecule::getSingleAllowedRGroup (int atom_idx) { dword bits = getRSiteBits(atom_idx); int rg_idx = 1; while (bits != 0) { if (bits & 1) { bits >>= 1; if (bits != 0) throw Error("getSingleAllowedRGroup(): multiple r-groups defined on atom #%d", atom_idx); return rg_idx; } rg_idx++; bits >>= 1; } throw Error("getSingleAllowedRGroup(): no r-groups defined on atom #%d", atom_idx); } int BaseMolecule::countRSites () { int i, sum = 0; for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (isRSite(i)) sum++; return sum; } int BaseMolecule::getRSiteAttachmentPointByOrder (int idx, int order) const { if (idx >= _rsite_attachment_points.size()) return -1; if (order >= _rsite_attachment_points[idx].size()) return -1; return _rsite_attachment_points[idx][order]; } void BaseMolecule::setRSiteAttachmentOrder (int atom_idx, int att_atom_idx, int order) { _rsite_attachment_points.expand(atom_idx + 1); _rsite_attachment_points[atom_idx].expandFill(order + 1, -1); _rsite_attachment_points[atom_idx][order] = att_atom_idx; updateEditRevision(); } void BaseMolecule::setTemplateAtomAttachmentOrder (int atom_idx, int att_atom_idx, const char *att_id) { int att_idx = template_attachment_points.add(); TemplateAttPoint &ap = template_attachment_points.at(att_idx); ap.ap_occur_idx = atom_idx; ap.ap_aidx = att_atom_idx; ap.ap_id.readString(att_id, false); ap.ap_id.push(0); updateEditRevision(); } int BaseMolecule::getTemplateAtomAttachmentPoint (int atom_idx, int order) { int ap_count = 0; for (int j = template_attachment_points.begin(); j != template_attachment_points.end(); j = template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = template_attachment_points.at(j); if (ap.ap_occur_idx == atom_idx) { if (ap_count == order) return ap.ap_aidx; ap_count++; } } return -1; } void BaseMolecule::getTemplateAtomAttachmentPointId (int atom_idx, int order, Array<char> &apid) { int ap_count = 0; for (int j = template_attachment_points.begin(); j != template_attachment_points.end(); j = template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = template_attachment_points.at(j); if (ap.ap_occur_idx == atom_idx) { if (ap_count == order) { apid.copy(ap.ap_id); return; } ap_count++; } } throw Error("attachment point order %d is out of range (%d)", order, ap_count); } int BaseMolecule::getTemplateAtomAttachmentPointById (int atom_idx, Array<char> &att_id) { QS_DEF(Array<char>, tmp); int aidx = -1; for (int j = template_attachment_points.begin(); j != template_attachment_points.end(); j = template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = template_attachment_points.at(j); if ( (ap.ap_occur_idx == atom_idx) && (ap.ap_id.memcmp(att_id) == 0) ) { return ap.ap_aidx; } } return aidx; } int BaseMolecule::getTemplateAtomAttachmentPointsCount (int atom_idx) { int count = 0; for (int j = template_attachment_points.begin(); j != template_attachment_points.end(); j = template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = template_attachment_points.at(j); if (ap.ap_occur_idx == atom_idx) { count++; } } return count; } int BaseMolecule::attachmentPointCount () const { return _attachment_index.size(); } void BaseMolecule::addAttachmentPoint (int order, int atom_index) { if (order < 1) throw Error("attachment point order %d no allowed (should start from 1)", order); if (_attachment_index.size() < order) _attachment_index.resize(order); _attachment_index[order - 1].push(atom_index); updateEditRevision(); } void BaseMolecule::removeAttachmentPoints () { _attachment_index.clear(); updateEditRevision(); } void BaseMolecule::removeAttachmentPointsFromAtom (int atom_index) { int i, j; for (i = 0; i < _attachment_index.size(); i++) if ((j = _attachment_index[i].find(atom_index)) != -1) { if (j == _attachment_index[i].size() - 1) _attachment_index[i].pop(); else _attachment_index[i][j] = _attachment_index[i].pop(); } updateEditRevision(); } int BaseMolecule::getAttachmentPoint (int order, int index) const { if (order < 1) throw Error("attachment point order %d no allowed (should start from 1)", order); return index < _attachment_index[order - 1].size() ? _attachment_index[order - 1][index] : -1; } void BaseMolecule::_checkSgroupHierarchy(int pidx, int oidx) { for (int i = sgroups.begin(); i != sgroups.end(); i = sgroups.next(i)) { SGroup &sg = sgroups.getSGroup(i); if (sg.parent_group == oidx) sg.parent_group = pidx; } } int copyBaseBond (BaseMolecule& bm, int beg, int end, int srcId) { int bid = -1; if (bm.isQueryMolecule()) { QueryMolecule& qm = bm.asQueryMolecule(); bid = qm.addBond(beg, end, qm.getBond(srcId).clone()); } else { Molecule& mol = bm.asMolecule(); bid = mol.addBond(beg, end, mol.getBondOrder(srcId)); mol.setEdgeTopology(bid, mol.getBondTopology(srcId)); } return bid; } void BaseMolecule::collapse (BaseMolecule& bm) { for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sg = bm.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_MUL) collapse(bm, i); } } void BaseMolecule::collapse (BaseMolecule& bm, int id) { QS_DEF(Mapping, mapAtom); mapAtom.clear(); QS_DEF(Mapping, mapBondInv); mapBondInv.clear(); collapse(bm, id, mapAtom, mapBondInv); } void BaseMolecule::collapse (BaseMolecule& bm, int id, Mapping& mapAtom, Mapping& mapBondInv) { SGroup &sg = bm.sgroups.getSGroup(id); if (sg.sgroup_type != SGroup::SG_TYPE_MUL) throw Error("The group is wrong type"); const MultipleGroup& group = (MultipleGroup &)sg; if (group.atoms.size() != group.multiplier * group.parent_atoms.size()) throw Error("The group is already collapsed or invalid"); QS_DEF(Array<int>, toRemove); toRemove.clear(); for (int j = 0; j < group.atoms.size(); ++j) { int k = j % group.parent_atoms.size(); int *value = mapAtom.at2(group.atoms[j]); if (value == 0) mapAtom.insert(group.atoms[j], group.atoms[k]); else if (*value != group.atoms[k]) throw Error("Invalid mapping in MultipleGroup::collapse"); if (k != j) toRemove.push(group.atoms[j]); } for (int j = bm.edgeBegin(); j < bm.edgeEnd(); j = bm.edgeNext(j)) { const Edge& edge = bm.getEdge(j); bool in1 = mapAtom.find(edge.beg), in2 = mapAtom.find(edge.end), p1 = in1 && mapAtom.at(edge.beg) == edge.beg, p2 = in2 && mapAtom.at(edge.end) == edge.end; if ((in1 && !p1 && !in2) || (!in1 && !p2 && in2)) { int beg = in1 ? mapAtom.at(edge.beg) : edge.beg; int end = in2 ? mapAtom.at(edge.end) : edge.end; int bid = copyBaseBond(bm, beg, end, j); if (!mapBondInv.find(bid)) mapBondInv.insert(bid, j); } } for (int j = 0; j < toRemove.size(); ++j) { int aid = toRemove[j]; bm.removeAtom(aid); } } int BaseMolecule::transformSCSRtoFullCTAB () { int result = 0; QS_DEF(Array<int>, tinds); tinds.clear(); for (auto i : vertices()) { if (isTemplateAtom(i)) tinds.push(i); } for (auto i = 0; i < tinds.size(); i++) { _transformTGroupToSGroup(tinds[i]); } if (tinds.size() > 0) { removeAtoms(tinds); tgroups.clear(); template_attachment_points.clear(); } return result; } int BaseMolecule::transformFullCTABtoSCSR (ObjArray<TGroup> &templates) { int result = 0; QS_DEF(Molecule, fragment); QS_DEF(Array<int>, added_templates); QS_DEF(Array<int>, mapping); QS_DEF(Array<int>, remove_atoms); QS_DEF(Array<int>, base_sgs); QS_DEF(Array<int>, sgs); QS_DEF(Array<int>, ap_points_atoms); QS_DEF(StringPool, ap_points_ids); QS_DEF(Array<int>, ap_ids); QS_DEF(Array<int>, ignore_atoms); added_templates.clear(); ignore_atoms.clear(); int seq_id = 1; templates.qsort(TGroup::cmp, 0); for (auto i = 0; i < templates.size(); i++) { const TGroup &tg = templates.at(i); fragment.clear(); fragment.clone_KeepIndices(*tg.fragment); sgs.clear(); base_sgs.clear(); fragment.sgroups.findSGroups(SGroup::SG_CLASS, "LGRP", sgs); for (int j = fragment.sgroups.begin(); j != fragment.sgroups.end(); j = fragment.sgroups.next(j)) { if (sgs.find(j) == -1) base_sgs.push(j); } ap_points_atoms.clear(); ap_points_ids.clear(); ap_ids.clear(); for (int j = 0; j < base_sgs.size(); j++) { SGroup &sg = fragment.sgroups.getSGroup(base_sgs[j]); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &su = (Superatom &)sg; if (su.attachment_points.size() > 0) { for (int k = su.attachment_points.begin(); k < su.attachment_points.end(); k = su.attachment_points.next(k)) { Superatom::_AttachmentPoint &ap = su.attachment_points.at(k); ap_points_atoms.push(ap.aidx); ap_ids.push(ap_points_ids.add(ap.apid)); } } } else throw Error("Wrong template structure was found (base SGroup is not Superatom type)"); } for (int j = 0; j < sgs.size(); j++) { fragment.removeSGroupWithBasis(sgs[j]); } int count_occur = 0; ignore_atoms.clear(); for (;;) { MoleculeExactSubstructureMatcher matcher(fragment, this->asMolecule()); for (int j = 0; j < ignore_atoms.size(); j++) matcher.ignoreTargetAtom(ignore_atoms[j]); if (!matcher.find()) break; mapping.clear(); remove_atoms.clear(); mapping.copy(matcher.getQueryMapping(), fragment.vertexEnd()); for (int j = 0; j < mapping.size(); j++) { if (mapping[j] > -1) remove_atoms.push(mapping[j]); } int out_bonds = 0; for (int j = 0; j < remove_atoms.size(); j++) { const Vertex &v = getVertex(remove_atoms[j]); for (int k = v.neiBegin(); k != v.neiEnd(); k = v.neiNext(k)) { if (remove_atoms.find(v.neiVertex(k)) == -1) { out_bonds++; } } } if (out_bonds > ap_points_atoms.size()) { ignore_atoms.concat(remove_atoms); continue; } int idx = this->asMolecule().addAtom(-1); this->asMolecule().setTemplateAtom(idx, tg.tgroup_name.ptr()); this->asMolecule().setTemplateAtomClass(idx, tg.tgroup_class.ptr()); // this->asMolecule().setTemplateAtomSeqid(idx, seq_id); seq_id++; count_occur++; for (int j = 0; j < ap_points_atoms.size(); j++) { int att_point_idx = mapping[ap_points_atoms[j]]; if (remove_atoms.find(att_point_idx) != -1) { const Vertex &v = getVertex(att_point_idx); QS_DEF(Array<int>, neighbors); neighbors.clear(); for (int k = v.neiBegin(); k != v.neiEnd(); k = v.neiNext(k)) { if (remove_atoms.find(v.neiVertex(k)) == -1) { neighbors.push(v.neiVertex(k)); } } for (int k = 0; k < neighbors.size(); k++) { if (findEdgeIndex(neighbors[k], att_point_idx) != -1) { flipBond(neighbors[k], att_point_idx, idx); this->asMolecule().setTemplateAtomAttachmentOrder(idx, neighbors[k], ap_points_ids.at(ap_ids[j])); if (isTemplateAtom(neighbors[k])) _flipTemplateAtomAttachmentPoint(neighbors[k], att_point_idx, idx); } } } } QS_DEF(Vec2f, cp); QS_DEF(Vec3f, p); p.set(0, 0, 0); getAtomsCenterPoint(remove_atoms, cp); p.x = cp.x; p.y = cp.y; setAtomXyz(idx, p); removeAtoms(remove_atoms); } if (count_occur > 0) added_templates.push(i); } for (auto i = 0; i < added_templates.size(); i++) { _addTemplate(templates.at(added_templates[i])); } return result; } int BaseMolecule::_addTemplate (TGroup &tgroup) { int result = 0; int idx = tgroups.addTGroup(); (tgroups.getTGroup(idx)).copy(tgroup); return result; } int BaseMolecule::_transformTGroupToSGroup (int idx) { int result = 0; QS_DEF(Molecule, fragment); QS_DEF(Array<int>, sgs); QS_DEF(Array<int>, mapping); QS_DEF(Array<int>, att_atoms); QS_DEF(Array<int>, tg_atoms); QS_DEF(Array<int>, lvgroups); int tg_idx = tgroups.findTGroup(getTemplateAtom(idx)); TGroup &tgroup = tgroups.getTGroup(tg_idx); fragment.clear(); fragment.clone_KeepIndices(*tgroup.fragment); sgs.clear(); att_atoms.clear(); tg_atoms.clear(); lvgroups.clear(); fragment.sgroups.findSGroups(SGroup::SG_LABEL, getTemplateAtom(idx), sgs); if (sgs.size() > 1) throw Error("transformTGroupToSGroup(): wrong template structure found (more then one base SGroup detected)"); SGroup &sg = fragment.sgroups.getSGroup(sgs[0]); if (sg.sgroup_type != SGroup::SG_TYPE_SUP) throw Error("transformTGroupToSGroup(): wrong template structure found (base SGroup is not Superatom type)"); Superatom &su = (Superatom &)sg; if (su.attachment_points.size() > 0) { for (int j = su.attachment_points.begin(); j < su.attachment_points.end(); j = su.attachment_points.next(j)) { Superatom::_AttachmentPoint &ap = su.attachment_points.at(j); int att_atom_idx = getTemplateAtomAttachmentPointById(idx, ap.apid); if (att_atom_idx > -1) { att_atoms.push(att_atom_idx); tg_atoms.push(ap.aidx); lvgroups.push(ap.lvidx); } } } sgs.clear(); fragment.sgroups.findSGroups(SGroup::SG_CLASS, "LGRP", sgs); for (int i = 0; i < sgs.size(); i++) { SGroup &lvg = fragment.sgroups.getSGroup(sgs[i]); for (int j = 0; j < lvgroups.size(); j++) { if (lvg.atoms.find(lvgroups[j]) > -1) fragment.removeSGroupWithBasis(sgs[i]); } } mergeWithMolecule(fragment, &mapping); for (auto i : fragment.vertices()) { int aidx = mapping[i]; setAtomXyz(aidx, getAtomXyz(idx)); } for (int i = 0; i < att_atoms.size(); i++) { flipBond(att_atoms[i], idx, mapping[tg_atoms[i]]); if (isTemplateAtom(att_atoms[i])) _flipTemplateAtomAttachmentPoint(att_atoms[i], idx, mapping[tg_atoms[i]]); } return result; } void BaseMolecule::_removeAtomsFromSGroup (SGroup &sgroup, Array<int> &mapping) { int i; for (i = sgroup.atoms.size() - 1; i >= 0; i--) { if (mapping[sgroup.atoms[i]] == -1) sgroup.atoms.remove(i); } for (i = sgroup.bonds.size() - 1; i >= 0; i--) { const Edge &edge = getEdge(sgroup.bonds[i]); if (mapping[edge.beg] == -1 || mapping[edge.end] == -1) sgroup.bonds.remove(i); } updateEditRevision(); } void BaseMolecule::_removeAtomsFromMultipleGroup (MultipleGroup &mg, Array<int> &mapping) { int i; for (i = mg.parent_atoms.size() - 1; i >= 0; i--) { if (mapping[mg.parent_atoms[i]] == -1) mg.parent_atoms.remove(i); } updateEditRevision(); } void BaseMolecule::_removeAtomsFromSuperatom (Superatom &sa, Array<int> &mapping) { if (sa.bond_connections.size() > 0) { for (int j = sa.bond_connections.size() - 1; j >= 0; j--) { Superatom::_BondConnection &bond = sa.bond_connections[j]; const Edge &edge = getEdge(bond.bond_idx); if (mapping[edge.beg] == -1 || mapping[edge.end] == -1) sa.bond_connections.remove(j); } } if (sa.attachment_points.size() > 0) { for (int j = sa.attachment_points.begin(); j < sa.attachment_points.end(); j = sa.attachment_points.next(j)) { Superatom::_AttachmentPoint &ap = sa.attachment_points.at(j); if (ap.aidx >= 0 && mapping[ap.aidx] == -1) sa.attachment_points.remove(j); else if (ap.lvidx >= 0 && mapping[ap.lvidx] == -1) ap.lvidx = -1; } } updateEditRevision(); } void BaseMolecule::_removeBondsFromSGroup (SGroup &sgroup, Array<int> &mapping) { int i; for (i = sgroup.bonds.size() - 1; i >= 0; i--) { if (mapping[sgroup.bonds[i]] == -1) sgroup.bonds.remove(i); } updateEditRevision(); } void BaseMolecule::_removeBondsFromSuperatom (Superatom &sa, Array<int> &mapping) { if (sa.bond_connections.size() > 0) { for (int j = sa.bond_connections.size() - 1; j >= 0; j--) { Superatom::_BondConnection &bond = sa.bond_connections[j]; const Edge &edge = getEdge(bond.bond_idx); if (mapping[bond.bond_idx] == -1) sa.bond_connections.remove(j); } } updateEditRevision(); } bool BaseMolecule::_mergeSGroupWithSubmolecule (SGroup &sgroup, SGroup &super, BaseMolecule &supermol, Array<int> &mapping, Array<int> &edge_mapping) { int i; bool merged = false; sgroup.sgroup_subtype = super.sgroup_subtype; sgroup.brackets.copy(super.brackets); for (i = 0; i < super.atoms.size(); i++) { if (mapping[super.atoms[i]] >= 0) { sgroup.atoms.push(mapping[super.atoms[i]]); merged = true; } } for (i = 0; i < super.bonds.size(); i++) { const Edge &edge = supermol.getEdge(super.bonds[i]); if (edge_mapping[super.bonds[i]] < 0) continue; if (mapping[edge.beg] < 0 || mapping[edge.end] < 0) throw Error("internal: edge is not mapped"); sgroup.bonds.push(edge_mapping[super.bonds[i]]); merged = true; } if (merged) updateEditRevision(); return merged; } void BaseMolecule::unhighlightAll () { _hl_atoms.clear(); _hl_bonds.clear(); updateEditRevision(); } void BaseMolecule::highlightAtom (int idx) { _hl_atoms.expandFill(idx + 1, 0); _hl_atoms[idx] = 1; updateEditRevision(); } void BaseMolecule::highlightBond (int idx) { _hl_bonds.expandFill(idx + 1, 0); _hl_bonds[idx] = 1; updateEditRevision(); } void BaseMolecule::highlightAtoms (const Filter &filter) { int i; for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) if (filter.valid(i)) highlightAtom(i); updateEditRevision(); } void BaseMolecule::highlightBonds (const Filter &filter) { int i; for (i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) if (filter.valid(i)) highlightBond(i); updateEditRevision(); } void BaseMolecule::unhighlightAtom (int idx) { if (_hl_atoms.size() > idx) { _hl_atoms[idx] = 0; updateEditRevision(); } } void BaseMolecule::unhighlightBond (int idx) { if (_hl_bonds.size() > idx) { _hl_bonds[idx] = 0; updateEditRevision(); } } int BaseMolecule::countHighlightedAtoms () { int i, res = 0; for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) { if (i >= _hl_atoms.size()) break; res += _hl_atoms[i]; } return res; } int BaseMolecule::countHighlightedBonds () { int i, res = 0; for (i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) { if (i >= _hl_bonds.size()) break; res += _hl_bonds[i]; } return res; } bool BaseMolecule::hasHighlighting () { return countHighlightedAtoms() > 0 || countHighlightedBonds() > 0; } bool BaseMolecule::isAtomHighlighted (int idx) { return _hl_atoms.size() > idx && _hl_atoms[idx] == 1; } bool BaseMolecule::isBondHighlighted (int idx) { return _hl_bonds.size() > idx && _hl_bonds[idx] == 1; } void BaseMolecule::highlightSubmolecule (BaseMolecule &subgraph, const int *mapping, bool entire) { int i; for (i = subgraph.vertexBegin(); i != subgraph.vertexEnd(); i = subgraph.vertexNext(i)) if (mapping[i] >= 0 && (entire || subgraph.isAtomHighlighted(i))) highlightAtom(mapping[i]); for (i = subgraph.edgeBegin(); i != subgraph.edgeEnd(); i = subgraph.edgeNext(i)) { if (!entire && !subgraph.isBondHighlighted(i)) continue; const Edge &edge = subgraph.getEdge(i); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg >= 0 && end >= 0) { int edge_idx = findEdgeIndex(beg, end); if (edge_idx >= 0) highlightBond(edge_idx); } } } int BaseMolecule::countSGroups () { return sgroups.getSGroupCount(); } void BaseMolecule::getAttachmentIndicesForAtom (int atom_idx, Array<int> &res) { res.clear(); for (int i = 1; i <= attachmentPointCount(); i++) { int idx = 0, aidx; for (idx = 0; (aidx = getAttachmentPoint(i, idx)) != -1; idx++) { if (aidx == atom_idx) res.push(i); } } } int BaseMolecule::getEditRevision () { return _edit_revision; } void BaseMolecule::updateEditRevision () { _edit_revision++; } int BaseMolecule::getBondDirection (int idx) const { if (idx > _bond_directions.size() - 1) return 0; return _bond_directions[idx]; } int BaseMolecule::getBondDirection2 (int center_idx, int nei_idx) { int idx = findEdgeIndex(center_idx, nei_idx); if (idx == -1) throw Error("getBondDirection2(): can not find bond"); if (center_idx != getEdge(idx).beg) return 0; return getBondDirection(idx); } void BaseMolecule::setBondDirection (int idx, int dir) { _bond_directions.expandFill(idx + 1, 0); _bond_directions[idx] = dir; } void BaseMolecule::clearBondDirections () { _bond_directions.clear(); } bool BaseMolecule::isChrial () { // Molecule is Chiral if it has at least one Abs stereocenter and all the stereocenters are Abs or Any return stereocenters.size() != 0 && stereocenters.haveAllAbsAny() && stereocenters.haveAbs(); } void BaseMolecule::invalidateAtom (int index, int mask) { if (mask & CHANGED_ATOM_NUMBER) { // Cis-trans and stereocenters can be removed if (stereocenters.exists(index)) { if (!stereocenters.isPossibleStereocenter(index)) stereocenters.remove(index); } const Vertex &v = getVertex(index); for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int edge_idx = v.neiEdge(nei); if (cis_trans.getParity(edge_idx) != 0) { if (!cis_trans.isGeomStereoBond(*this, edge_idx, 0, false)) cis_trans.setParity(edge_idx, 0); } } } } void BaseMolecule::getSGroupAtomsCenterPoint (SGroup &sgroup, Vec2f &res) { getAtomsCenterPoint(sgroup.atoms, res); } void BaseMolecule::getAtomsCenterPoint (Array<int> &atoms, Vec2f &res) { res.set(0, 0); for (int j = 0; j < atoms.size(); j++) { int ai = atoms[j]; Vec3f &p = getAtomXyz(ai); res.x += p.x; res.y += p.y; } if (atoms.size() != 0) res.scale(1.0f / atoms.size()); } void BaseMolecule::getAtomSymbol (int v, Array<char> &result) { if (isPseudoAtom(v)) { result.readString(getPseudoAtom(v), true); } else if (isTemplateAtom(v)) { result.readString(getTemplateAtom(v), true); } else if (isRSite(v)) { QS_DEF(Array<int>, rgroups); int i; getAllowedRGroups(v, rgroups); if (rgroups.size() == 0) { result.readString("R", true); return; } ArrayOutput output(result); for (i = 0; i < rgroups.size(); i++) { if (i > 0) output.writeChar(','); output.printf("R%d", rgroups[i]); } output.writeChar(0); } else { int number = getAtomNumber(v); QS_DEF(Array<int>, list); if (number != -1) { result.readString(Element::toString(number), true); return; } int query_atom_type; if (isQueryMolecule() && (query_atom_type = QueryMolecule::parseQueryAtom(asQueryMolecule(), v, list)) != -1) { if (query_atom_type == QueryMolecule::QUERY_ATOM_A) { result.readString("A", true); return; } else if (query_atom_type == QueryMolecule::QUERY_ATOM_Q) { result.readString("Q", true); return; } else if (query_atom_type == QueryMolecule::QUERY_ATOM_X) { result.readString("X", true); return; } else if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST) { int k; ArrayOutput output(result); if (query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST) output.writeString("NOT"); output.writeChar('['); for (k = 0; k < list.size(); k++) { if (k > 0) output.writeChar(','); output.writeString(Element::toString(list[k])); } output.writeChar(']'); output.writeChar(0); } } } if (result.size() == 0) result.readString("*", true); }��������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/canonical_smiles_saver.cpp�����������������������������������������0000664�0000000�0000000�00000010511�12710376503�0024002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/canonical_smiles_saver.h" #include "base_cpp/output.h" #include "base_cpp/tlscont.h" #include "molecule/molecule.h" #include "molecule/smiles_saver.h" #include "molecule/molecule_automorphism_search.h" #include "molecule/elements.h" #include "molecule/molecule_dearom.h" using namespace indigo; IMPL_ERROR(CanonicalSmilesSaver, "canonical SMILES saver"); CanonicalSmilesSaver::CanonicalSmilesSaver (Output &output) : SmilesSaver(output), TL_CP_GET(_actual_atom_atom_mapping), TL_CP_GET(_initial_to_actual) { find_invalid_stereo = true; ignore_invalid_hcount = false; ignore_hydrogens = true; canonize_chiralities = true; initial_atom_atom_mapping = 0; _initial_to_actual.clear(); _initial_to_actual.insert(0, 0); _aam_counter = 0; } CanonicalSmilesSaver::~CanonicalSmilesSaver () { } void CanonicalSmilesSaver::saveMolecule (Molecule &mol_) { if (mol_.vertexCount() < 1) return; QS_DEF(Array<int>, ignored); QS_DEF(Array<int>, order); QS_DEF(Array<int>, ranks); QS_DEF(Molecule, mol); int i; if (mol_.sgroups.isPolimer()) throw Error("can not canonicalize a polymer"); // Detect hydrogens configuration if aromatic but not ambiguous // We can store this infromation in the original structure mol_. mol_.restoreAromaticHydrogens(); mol.clone(mol_, 0, 0); // TODO: canonicalize allenes properly mol.allene_stereo.clear(); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.convertableToImplicitHydrogen(i)) ignored[i] = 1; // Try to save into ordinary smiles and find what cis-trans bonds were used NullOutput null_output; SmilesSaver saver_cistrans(null_output); saver_cistrans.ignore_hydrogens = true; saver_cistrans.saveMolecule(mol); // Then reset cis-trans infromation that is not saved into SMILES const Array<int>& parities = saver_cistrans.getSavedCisTransParities(); for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { if (mol.cis_trans.getParity(i) != 0 && parities[i] == 0) mol.cis_trans.setParity(i, 0); } MoleculeAutomorphismSearch of; of.detect_invalid_cistrans_bonds = find_invalid_stereo; of.detect_invalid_stereocenters = find_invalid_stereo; of.find_canonical_ordering = true; of.ignored_vertices = ignored.ptr(); of.process(mol); of.getCanonicalNumbering(order); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i) != 0 && of.invalidCisTransBond(i)) mol.cis_trans.setParity(i, 0); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY && of.invalidStereocenter(i)) mol.stereocenters.remove(i); ranks.clear_resize(mol.vertexEnd()); for (i = 0; i < order.size(); i++) ranks[order[i]] = i; vertex_ranks = ranks.ptr(); if (initial_atom_atom_mapping) { _actual_atom_atom_mapping.clear_resize(initial_atom_atom_mapping->size()); _actual_atom_atom_mapping.fill(0); for (int i = 0; i < order.size(); ++i) { int aam = initial_atom_atom_mapping->at(order[i]); if (aam) { if (!_initial_to_actual.find(aam)) { _initial_to_actual.insert(aam, ++_aam_counter); _actual_atom_atom_mapping[order[i]] = _aam_counter; } else { _actual_atom_atom_mapping[order[i]] = _initial_to_actual.at(aam); } } } atom_atom_mapping = _actual_atom_atom_mapping.ptr(); } else { atom_atom_mapping = 0; } SmilesSaver::saveMolecule(mol); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/cmf_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000061076�12710376503�0021406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "molecule/cmf_loader.h" #include "molecule/cmf_symbol_codes.h" using namespace indigo; IMPL_ERROR(CmfLoader, "CMF loader"); CP_DEF(CmfLoader); CmfLoader::CmfLoader (LzwDict &dict, Scanner &scanner) : CP_INIT, TL_CP_GET(_atoms), TL_CP_GET(_bonds), TL_CP_GET(_pseudo_labels), TL_CP_GET(_attachments), TL_CP_GET(_sgroup_order), TL_CP_GET(atom_mapping_to_restore), TL_CP_GET(inv_atom_mapping_to_restore), TL_CP_GET(bond_mapping_to_restore), TL_CP_GET(inv_bond_mapping_to_restore) { _init(); _decoder_obj.create(dict, scanner); _lzw_scanner.create(_decoder_obj.ref()); _scanner = _lzw_scanner.get(); } CmfLoader::CmfLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(_atoms), TL_CP_GET(_bonds), TL_CP_GET(_pseudo_labels), TL_CP_GET(_attachments), TL_CP_GET(_sgroup_order), TL_CP_GET(atom_mapping_to_restore), TL_CP_GET(inv_atom_mapping_to_restore), TL_CP_GET(bond_mapping_to_restore), TL_CP_GET(inv_bond_mapping_to_restore) { _init(); _scanner = &scanner; } CmfLoader::CmfLoader (LzwDecoder &decoder) : CP_INIT, TL_CP_GET(_atoms), TL_CP_GET(_bonds), TL_CP_GET(_pseudo_labels), TL_CP_GET(_attachments), TL_CP_GET(_sgroup_order), TL_CP_GET(atom_mapping_to_restore), TL_CP_GET(inv_atom_mapping_to_restore), TL_CP_GET(bond_mapping_to_restore), TL_CP_GET(inv_bond_mapping_to_restore) { _init(); _lzw_scanner.create(decoder); _scanner = _lzw_scanner.get(); } CmfLoader::~CmfLoader() { // Do nothing, but without explicit destructor Visual Studio produces invalid code // that leads to "Attempted to read or write protected memory". } void CmfLoader::_init () { skip_cistrans = false; skip_stereocenters = false; skip_valence = false; _ext_decoder = 0; _scanner = 0; atom_flags = 0; bond_flags = 0; _sgroup_order.clear(); has_mapping = false; version = 2; } bool CmfLoader::_getNextCode (int &code) { if (_scanner->isEOF()) return false; code = _scanner->readByte(); return true; } bool CmfLoader::_readAtom (int &code, _AtomDesc &atom, int atom_idx) { if (code > 0 && code < ELEM_MAX) atom.label = code; else if (code == CMF_PSEUDOATOM) { int len; if (!_getNextCode(len)) throw Error("pseudo-atom identifier must be followed by length"); if (len < 1) throw Error("empty pseudo-atom"); atom.pseudo_atom_idx = _pseudo_labels.add(len + 1); char *label = _pseudo_labels.at(atom.pseudo_atom_idx); for (int i = 0; i < len; i++) { int c; if (!_getNextCode(c)) throw Error("pseudo-atom label is incomplete"); label[i] = c; } label[len] = 0; } else if (code == CMF_RSITE) { atom.label = ELEM_RSITE; _getNextCode(atom.rsite_bits); } else if (code == CMF_RSITE_EXT) { atom.label = ELEM_RSITE; atom.rsite_bits = (int)_scanner->readPackedUInt(); } else throw Error("bad atom number: %d", code); if (!_getNextCode(code)) return false; if (code >= CMF_CHARGES && code < CMF_CHARGES + CMF_NUM_OF_CHARGES && code != CMF_SEPARATOR) { int charge = code - CMF_CHARGES; charge += CMF_MIN_CHARGE; atom.charge = charge; if (!_getNextCode(code)) return false; } if (code == CMF_CHARGE_EXT) { int charge; _getNextCode(charge); charge -= 128; atom.charge = charge; if (!_getNextCode(code)) return false; } if (code >= CMF_ISOTOPE_ZERO && code <= CMF_ISOTOPE_OTHER) { int deviation; if (code == CMF_ISOTOPE_ZERO) deviation = 0; else if (code == CMF_ISOTOPE_PLUS1) deviation = 1; else if (code == CMF_ISOTOPE_PLUS2) deviation = 2; else if (code == CMF_ISOTOPE_MINUS1) deviation = -1; else if (code == CMF_ISOTOPE_MINUS2) deviation = -2; else // CMF_ISOTOPE_OTHER { if (!_getNextCode(code)) throw Error("expecting mass difference"); deviation = code - 100; } atom.isotope = Element::getDefaultIsotope(atom.label) + deviation; if (!_getNextCode(code)) return false; } if (code >= CMF_RADICAL_SINGLET && code <= CMF_RADICAL_TRIPLET) { if (code == CMF_RADICAL_SINGLET) atom.radical = RADICAL_SINGLET; else if (code == CMF_RADICAL_DOUBLET) atom.radical = RADICAL_DOUBLET; else // code == CMF_RADICAL_TRIPLET atom.radical = RADICAL_TRIPLET; if (!_getNextCode(code)) return false; } if (code >= CMF_STEREO_ANY && code <= CMF_STEREO_ABS_1) { if (code >= CMF_STEREO_AND_1) { /* CMF_STEREO_*_1 -> CMF_STEREO_*_0 */ code -= CMF_MAX_STEREOGROUPS * 2 + 1; atom.stereo_invert_pyramid = true; } if (code == CMF_STEREO_ANY) atom.stereo_type = MoleculeStereocenters::ATOM_ANY; else if (code == CMF_STEREO_ABS_0) atom.stereo_type = MoleculeStereocenters::ATOM_ABS; else if (code < CMF_STEREO_OR_0) { atom.stereo_type = MoleculeStereocenters::ATOM_AND; atom.stereo_group = code - CMF_STEREO_AND_0 + 1; } else { atom.stereo_type = MoleculeStereocenters::ATOM_OR; atom.stereo_group = code - CMF_STEREO_OR_0 + 1; } if (!_getNextCode(code)) return false; } if (code == CMF_STEREO_ALLENE_0 || code == CMF_STEREO_ALLENE_1) { if (code == CMF_STEREO_ALLENE_0) atom.allene_stereo_parity = 1; else atom.allene_stereo_parity = 2; if (!_getNextCode(code)) return false; } if (code >= CMF_IMPLICIT_H && code <= CMF_IMPLICIT_H + CMF_MAX_IMPLICIT_H) { atom.hydrogens = code - CMF_IMPLICIT_H; if (!_getNextCode(code)) return false; } if (code >= CMF_VALENCE && code <= CMF_VALENCE + CMF_MAX_VALENCE) { atom.valence = code - CMF_VALENCE; if (!_getNextCode(code)) return false; } if (code == CMF_VALENCE_EXT) { atom.valence = (int)_scanner->readPackedUInt(); if (!_getNextCode(code)) return false; } while (code == CMF_ATTACHPT) { int aidx; if (!_getNextCode(aidx)) throw Error("expected attachment index"); _AttachmentDesc &att = _attachments.push(); att.atom = atom_idx; att.index = aidx; if (!_getNextCode(code)) return false; } while (code >= CMF_ATOM_FLAGS && code < CMF_ATOM_FLAGS + CMF_NUM_OF_ATOM_FLAGS) { atom.flags |= (1 << (code - CMF_ATOM_FLAGS)); if (!_getNextCode(code)) return false; } if (code == CMF_HIGHLIGHTED) { atom.highlighted = true; if (!_getNextCode(code)) return false; } return true; } void CmfLoader::_readBond (int &code, _BondDesc &bond) { bond.cis_trans = 0; bond.flags = 0; bond.swap = false; bond.direction = 0; bond.highlighted = false; if (code == CMF_BOND_SINGLE_CHAIN) { bond.type = BOND_SINGLE; bond.in_ring = false; } else if (code == CMF_BOND_SINGLE_RING) { bond.type = BOND_SINGLE; bond.in_ring = true; } else if (code == CMF_BOND_DOUBLE_CHAIN) { bond.type = BOND_DOUBLE; bond.in_ring = false; } else if (code == CMF_BOND_DOUBLE_RING) { bond.type = BOND_DOUBLE; bond.in_ring = true; } else if (code == CMF_BOND_DOUBLE_CHAIN_CIS) { bond.type = BOND_DOUBLE; bond.in_ring = false; bond.cis_trans = MoleculeCisTrans::CIS; } else if (code == CMF_BOND_DOUBLE_CHAIN_TRANS) { bond.type = BOND_DOUBLE; bond.in_ring = false; bond.cis_trans = MoleculeCisTrans::TRANS; } else if (code == CMF_BOND_DOUBLE_RING_CIS) { bond.type = BOND_DOUBLE; bond.in_ring = true; bond.cis_trans = MoleculeCisTrans::CIS; } else if (code == CMF_BOND_DOUBLE_RING_TRANS) { bond.type = BOND_DOUBLE; bond.in_ring = true; bond.cis_trans = MoleculeCisTrans::TRANS; } else if (code == CMF_BOND_TRIPLE_CHAIN) { bond.type = BOND_TRIPLE; bond.in_ring = false; } else if (code == CMF_BOND_TRIPLE_RING) { bond.type = BOND_TRIPLE; bond.in_ring = true; } else if (code == CMF_BOND_AROMATIC) { bond.type = BOND_AROMATIC; bond.in_ring = true; } else if (code == CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_CHAIN || code == CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_RING) { bond.cis_trans = -1; bond.type = BOND_DOUBLE; bond.in_ring = (code == CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_RING); } else throw Error("cannot decode bond: code %d", code); while (true) { if (!_getNextCode(code)) throw Error("nothing is after the bond code"); if (code >= CMF_BOND_FLAGS && code < CMF_BOND_FLAGS + CMF_NUM_OF_BOND_FLAGS) bond.flags |= (1 << (code - CMF_BOND_FLAGS)); else if (code == CMF_BOND_UP) bond.direction = BOND_UP; else if (code == CMF_BOND_DOWN) bond.direction = BOND_DOWN; else if (code == CMF_BOND_EITHER) bond.direction = BOND_EITHER; else if (code == CMF_BOND_SWAP_ENDS) bond.swap = true; else if (code == CMF_HIGHLIGHTED) bond.highlighted = true; else break; } } bool CmfLoader::_readCycleNumber (int &code, int &n) { n = 0; while (code == CMF_CYCLES_PLUS) { n += CMF_NUM_OF_CYCLES; if (!_getNextCode(code)) throw Error("CYCLES_PLUS symbol must not be the last one"); } if (code >= CMF_CYCLES && code < CMF_CYCLES + CMF_NUM_OF_CYCLES) { n += code - CMF_CYCLES; return true; } else if (n > 0) throw Error("CYCLES_PLUS symbol must be followed by a cycle number"); return false; } void CmfLoader::loadMolecule (Molecule &mol) { int code; mol.clear(); QS_DEF(Array<int>, cycle_numbers); QS_DEF(Array<int>, atom_stack); _atoms.clear(); _bonds.clear(); _pseudo_labels.clear(); _attachments.clear(); cycle_numbers.clear(); atom_stack.clear(); bool first_atom = true; if (!_getNextCode(code)) return; bool has_ext_part = false; /* Main loop */ do { _BondDesc *bond = 0; if (code > CMF_ALPHABET_SIZE) throw Error("unexpected code"); if (code == CMF_TERMINATOR) break; if (code == CMF_EXT) { has_ext_part = true; // Ext part has to be read till CMF_TERMINATOR break; } if (!first_atom) { int number; while (_readCycleNumber(code, number)) { while (cycle_numbers.size() <= number) cycle_numbers.push(-1); if (cycle_numbers[number] >= 0) throw Error("cycle #%d already in use", number); cycle_numbers[number] = atom_stack.top(); if (!_getNextCode(code)) break; } } if (code == CMF_SEPARATOR) { atom_stack.pop(); first_atom = true; if (!_getNextCode(code)) break; continue; } if (code == CMF_OPEN_BRACKET) { atom_stack.push(atom_stack.top()); if (!_getNextCode(code)) break; continue; } if (code == CMF_CLOSE_BRACKET) { atom_stack.pop(); if (!_getNextCode(code)) break; continue; } if (!first_atom) { bond = &_bonds.push(); bond->beg = atom_stack.top(); } if (bond != 0) { _readBond(code, *bond); int number; if (_readCycleNumber(code, number)) { if (cycle_numbers[number] < 0) throw Error("bad cycle number after bond symbol"); bond->end = cycle_numbers[number]; cycle_numbers[number] = -1; if (!_getNextCode(code)) break; continue; } } _AtomDesc &atom = _atoms.push(); if (!first_atom) atom_stack.pop(); atom_stack.push(_atoms.size() - 1); first_atom = false; if (bond != 0) bond->end = _atoms.size() - 1; memset(&atom, 0, sizeof(_AtomDesc)); atom.hydrogens = -1; atom.valence = -1; atom.pseudo_atom_idx = -1; atom.rsite = false; if (code > 0 && (code < ELEM_MAX || code == CMF_PSEUDOATOM || code == CMF_RSITE || code == CMF_RSITE_EXT)) { if (!_readAtom(code, atom, _atoms.size() - 1)) break; continue; } if (!_getNextCode(code)) break; } while (true); // if have internal decoder, finish it /* if (_decoder_obj.get() != 0) _decoder_obj->finish(); */ /* Reading finished, filling molecule */ int i; for (i = 0; i < _atoms.size(); i++) { mol.addAtom(_atoms[i].label); if (_atoms[i].pseudo_atom_idx >= 0) mol.setPseudoAtom(i, _pseudo_labels.at(_atoms[i].pseudo_atom_idx)); if (_atoms[i].rsite_bits > 0) mol.setRSiteBits(i, _atoms[i].rsite_bits); mol.setAtomCharge(i, _atoms[i].charge); mol.setAtomIsotope(i, _atoms[i].isotope); if (_atoms[i].hydrogens >= 0) mol.setImplicitH(i, _atoms[i].hydrogens); mol.setAtomRadical(i, _atoms[i].radical); if (_atoms[i].highlighted) mol.highlightAtom(i); } for (i = 0; i < _bonds.size(); i++) { int type = _bonds[i].type; int beg = _bonds[i].beg; int end = _bonds[i].end; int tmp; if (_bonds[i].swap) __swap(beg, end, tmp); int idx = mol.addBond_Silent(beg, end, type); if (_bonds[i].in_ring) mol.setEdgeTopology(idx, TOPOLOGY_RING); else mol.setEdgeTopology(idx, TOPOLOGY_CHAIN); if (_bonds[i].direction != 0) mol.setBondDirection(idx, _bonds[i].direction); if (_bonds[i].highlighted) mol.highlightBond(idx); } for (i = 0; i < _attachments.size(); i++) mol.addAttachmentPoint(_attachments[i].index, _attachments[i].atom); mol.validateEdgeTopologies(); if (has_ext_part) _readExtSection(mol); if (atom_flags != 0) { atom_flags->clear(); for (i = 0; i < _atoms.size(); i++) atom_flags->push(_atoms[i].flags); } if (bond_flags != 0) { bond_flags->clear(); for (i = 0; i < _bonds.size(); i++) bond_flags->push(_bonds[i].flags); } if (!skip_cistrans) { for (i = 0; i < _bonds.size(); i++) { if (_bonds[i].cis_trans != 0) { int parity = _bonds[i].cis_trans; if (parity > 0) mol.cis_trans.setParity(i, _bonds[i].cis_trans); else mol.cis_trans.ignore(i); mol.cis_trans.restoreSubstituents(i); } } } if (!skip_valence) { for (i = 0; i < _atoms.size(); i++) { if (_atoms[i].valence >= 0) mol.setValence(i, _atoms[i].valence); } } if (!skip_stereocenters) { for (i = 0; i < _atoms.size(); i++) { if (_atoms[i].stereo_type != 0) mol.stereocenters.add(i, _atoms[i].stereo_type, _atoms[i].stereo_group, _atoms[i].stereo_invert_pyramid); } } for (i = 0; i < _atoms.size(); i++) { if (_atoms[i].allene_stereo_parity != 0) { int left, right, subst[4]; bool pure_h[4]; int parity = _atoms[i].allene_stereo_parity; int tmp; if (!MoleculeAlleneStereo::possibleCenter(mol, i, left, right, subst, pure_h)) throw Error("invalid molecule allene stereo marker"); if (subst[1] != -1 && subst[1] < subst[0]) __swap(subst[1], subst[0], tmp); if (subst[3] != -1 && subst[3] < subst[2]) __swap(subst[3], subst[2], tmp); if (pure_h[0]) { __swap(subst[1], subst[0], tmp); parity = 3 - parity; } if (pure_h[2]) { __swap(subst[2], subst[3], tmp); parity = 3 - parity; } mol.allene_stereo.add(i, left, right, subst, parity); } } // for loadXyz() _mol = &mol; // Check if atom mapping was used if (has_mapping) { // Compute inv_atom_mapping_to_restore inv_atom_mapping_to_restore.clear_resize(atom_mapping_to_restore.size()); for (int i = 0; i < atom_mapping_to_restore.size(); i++) inv_atom_mapping_to_restore[atom_mapping_to_restore[i]] = i; // Compute inv_bond_mapping_to_restore inv_bond_mapping_to_restore.clear_resize(bond_mapping_to_restore.size()); for (int i = 0; i < bond_mapping_to_restore.size(); i++) inv_bond_mapping_to_restore[bond_mapping_to_restore[i]] = i; QS_DEF(Molecule, tmp); tmp.makeEdgeSubmolecule(mol, atom_mapping_to_restore, bond_mapping_to_restore, NULL); mol.clone(tmp, NULL, NULL); } } void CmfLoader::_readSGroup (int code, Molecule &mol) { int idx = -1; if (code == CMF_DATASGROUP) { idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_DAT); DataSGroup &s = (DataSGroup &)mol.sgroups.getSGroup(idx); _readGeneralSGroup(s); _readString(s.description); _readString(s.name); _readString(s.type); _readString(s.querycode); _readString(s.queryoper); _readString(s.data); byte bits = _scanner->readByte(); s.dasp_pos = bits & 0x0F; s.detached = (bits & (1 << 4)) != 0; s.relative = (bits & (1 << 5)) != 0; s.display_units = (bits & (1 << 6)) != 0; s.num_chars = (int)_scanner->readPackedUInt(); s.tag = _scanner->readChar(); } else if (code == CMF_SUPERATOM) { idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_SUP); Superatom &s = (Superatom &)mol.sgroups.getSGroup(idx); _readGeneralSGroup(s); _readString(s.subscript); _readString(s.sa_class); byte bits = _scanner->readByte(); s.contracted = bits & 0x01; int bcons = bits >> 1; if (bcons > 0) { s.bond_connections.resize(bcons); for (int j = 0; j < bcons; j++) { s.bond_connections[j].bond_idx = (int)_scanner->readPackedUInt() - 1; } } } else if (code == CMF_REPEATINGUNIT) { idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_SRU); RepeatingUnit &s = (RepeatingUnit &)mol.sgroups.getSGroup(idx); _readGeneralSGroup(s); if (version >= 2) _readString(s.subscript); else s.subscript.readString("n", true); s.connectivity = _scanner->readPackedUInt(); } else if (code == CMF_MULTIPLEGROUP) { idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_MUL); MultipleGroup &s = (MultipleGroup &)mol.sgroups.getSGroup(idx); _readGeneralSGroup(s); _readUIntArray(s.parent_atoms); s.multiplier = _scanner->readPackedUInt(); } else if (code == CMF_GENERICSGROUP) { idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_GEN); SGroup &s = (SGroup &)mol.sgroups.getSGroup(idx); _readGeneralSGroup(s); } else throw Error("_readExtSection: unexpected SGroup code: %d", code); _sgroup_order.push(idx); } float CmfLoader::_readFloatInRange (Scanner &scanner, float min, float range) { return min + ((float)scanner.readBinaryWord() / 65535) * range; } void CmfLoader::_readVec3f (Scanner &scanner, Vec3f &pos, const CmfSaver::VecRange &range) { pos.x = _readFloatInRange(scanner, range.xyz_min.x, range.xyz_range.x); pos.y = _readFloatInRange(scanner, range.xyz_min.y, range.xyz_range.y); if (range.have_z) pos.z = _readFloatInRange(scanner, range.xyz_min.z, range.xyz_range.z); else pos.z = 0; } void CmfLoader::_readVec2f (Scanner &scanner, Vec2f &pos, const CmfSaver::VecRange &range) { pos.x = _readFloatInRange(scanner, range.xyz_min.x, range.xyz_range.x); pos.y = _readFloatInRange(scanner, range.xyz_min.y, range.xyz_range.y); } void CmfLoader::_readDir2f (Scanner &scanner, Vec2f &dir, const CmfSaver::VecRange &range) { dir.x = _readFloatInRange(scanner, range.xyz_min.x, 2 * range.xyz_range.x); dir.y = _readFloatInRange(scanner, range.xyz_min.y, 2 * range.xyz_range.y); } void CmfLoader::_readBaseSGroupXyz (Scanner &scanner, SGroup &sgroup, const CmfSaver::VecRange &range) { int len = scanner.readPackedUInt(); sgroup.brackets.resize(len); for (int i = 0; i < len; i++) { _readVec2f(scanner, sgroup.brackets[i][0], range); _readVec2f(scanner, sgroup.brackets[i][1], range); } } void CmfLoader::_readSGroupXYZ (Scanner &scanner, int idx, Molecule &mol, const CmfSaver::VecRange &range) { SGroup &sg = mol.sgroups.getSGroup(idx); int sg_type = sg.sgroup_type; if (sg_type == SGroup::SG_TYPE_DAT) { DataSGroup &s = (DataSGroup &)sg; _readBaseSGroupXyz(scanner, s, range); _readVec2f(scanner, s.display_pos, range); } else if (sg_type == SGroup::SG_TYPE_SUP) { Superatom &s = (Superatom &)sg; _readBaseSGroupXyz(scanner, s, range); if (s.bond_connections.size() > 0) { for (int j = 0; j < s.bond_connections.size(); j++) { _readDir2f(scanner, s.bond_connections[j].bond_dir, range); } } } else if ( (sg_type == SGroup::SG_TYPE_SRU) || (sg_type == SGroup::SG_TYPE_MUL) || (sg_type == SGroup::SG_TYPE_GEN) ) { _readBaseSGroupXyz(scanner, sg, range); } else throw Error("_readExtSection: unexpected SGroup type: %d", sg_type); } void CmfLoader::_readString (Array<char> &dest) { unsigned int len = _scanner->readPackedUInt(); dest.resize(len + 1); _scanner->read(len, dest.ptr()); dest[len] = 0; } void CmfLoader::_readUIntArray (Array<int> &dest) { unsigned int len = _scanner->readPackedUInt(); dest.clear_resize(len); for (unsigned int i = 0; i < len; i++) dest[i] = _scanner->readPackedUInt(); } void CmfLoader::_readGeneralSGroup (SGroup &sgroup) { _readUIntArray(sgroup.atoms); _readUIntArray(sgroup.bonds); } void CmfLoader::_readExtSection (Molecule &mol) { _sgroup_order.clear(); int code; while (true) { if (!_getNextCode(code)) throw Error("_readExtSection: unexpected end of the stream"); if (code == CMF_TERMINATOR) break; // TODO provide a readers map to avoid such "if"s else if (code == CMF_DATASGROUP || code == CMF_SUPERATOM || code == CMF_REPEATINGUNIT || code == CMF_MULTIPLEGROUP || code == CMF_GENERICSGROUP) { _readSGroup(code, mol); } else if (code == CMF_RSITE_ATTACHMENTS) { int idx = _scanner->readPackedUInt(); int count = _scanner->readPackedUInt(); for (int i = 0; i < count; i++) { int idx2 = _scanner->readPackedUInt(); mol.setRSiteAttachmentOrder(idx, idx2, i); } } else if (code == CMF_MAPPING) { // atom_mapping_to_restore _readUIntArray(atom_mapping_to_restore); _readUIntArray(bond_mapping_to_restore); has_mapping = true; } else throw Error("unexpected code: %d", code); } } void CmfLoader::loadXyz (Scanner &scanner) { if (_mol == 0) throw Error("loadMolecule() must be called prior to loadXyz()"); int i; CmfSaver::VecRange range; range.xyz_min.x = scanner.readBinaryFloat(); range.xyz_min.y = scanner.readBinaryFloat(); range.xyz_min.z = scanner.readBinaryFloat(); range.xyz_range.x = scanner.readBinaryFloat(); range.xyz_range.y = scanner.readBinaryFloat(); range.xyz_range.z = scanner.readBinaryFloat(); range.have_z = (scanner.readByte() != 0); for (i = 0; i < _atoms.size(); i++) { Vec3f pos; _readVec3f(scanner, pos, range); int idx = i; if (has_mapping) idx = inv_atom_mapping_to_restore[i]; _mol->setAtomXyz(idx, pos.x, pos.y, pos.z); } // Read sgroup coordinates data for (int i = 0; i < _sgroup_order.size(); i++) _readSGroupXYZ(scanner, _sgroup_order[i], *_mol, range); _mol->have_xyz = true; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/cmf_saver.cpp������������������������������������������������������0000664�0000000�0000000�00000060667�12710376503�0021265�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_cis_trans.h" #include "molecule/cmf_saver.h" #include "molecule/cmf_symbol_codes.h" #include "graph/dfs_walk.h" using namespace indigo; IMPL_ERROR(CmfSaver, "CMF saver"); CP_DEF(CmfSaver); CmfSaver::CmfSaver (LzwDict &dict, Output &output) : CP_INIT, TL_CP_GET(_atom_sequence) { _init(); if (!dict.isInitialized()) dict.init(CMF_ALPHABET_SIZE, CMF_BIT_CODE_SIZE); _encoder_obj.create(dict, output); _encoder_output_obj.create(_encoder_obj.ref()); _output = _encoder_output_obj.get(); } CmfSaver::CmfSaver (LzwEncoder &encoder) : CP_INIT, TL_CP_GET(_atom_sequence) { _init(); _ext_encoder = &encoder; _encoder_output_obj.create(encoder); _output = _encoder_output_obj.get(); } CmfSaver::CmfSaver (Output &output) : CP_INIT, TL_CP_GET(_atom_sequence) { _init(); _output = &output; } void CmfSaver::_init () { atom_flags = 0; bond_flags = 0; _mol = 0; _ext_encoder = 0; save_bond_dirs = false; save_highlighting = false; save_mapping = false; } void CmfSaver::saveMolecule (Molecule &mol) { /* Walk molecule */ DfsWalk walk(mol); QS_DEF(Array<int>, mapping); if (_ext_encoder != 0) _ext_encoder->start(); walk.walk(); /* Get walking sequence */ const Array<DfsWalk::SeqElem> &v_seq = walk.getSequence(); /* Calculate mapping to the encoded molecule */ walk.calcMapping(mapping); QS_DEF(Array<int>, branch_counters); QS_DEF(Array<int>, cycle_numbers); branch_counters.clear_resize(mol.vertexEnd()); branch_counters.zerofill(); cycle_numbers.clear(); _atom_sequence.clear(); QS_DEF(Array<int>, bond_mapping); bond_mapping.clear_resize(mol.edgeEnd()); bond_mapping.fffill(); int bond_index = 0; /* Encode first atom */ if (v_seq.size() > 0) { _encodeAtom(mol, v_seq[0].idx, mapping.ptr()); _atom_sequence.push(v_seq[0].idx); int j, openings = walk.numOpenings(v_seq[0].idx); for (j = 0; j < openings; j++) { cycle_numbers.push(v_seq[0].idx); _encodeCycleNumer(j); } } /* Main cycle */ int i, j, k; for (i = 1; i < v_seq.size(); i++) { int v_idx = v_seq[i].idx; int e_idx = v_seq[i].parent_edge; int v_prev_idx = v_seq[i].parent_vertex; bool write_atom = true; if (v_prev_idx >= 0) { if (walk.numBranches(v_prev_idx) > 1) if (branch_counters[v_prev_idx] > 0) _encode(CMF_CLOSE_BRACKET); int branches = walk.numBranches(v_prev_idx); if (branches > 1) if (branch_counters[v_prev_idx] < branches - 1) _encode(CMF_OPEN_BRACKET); branch_counters[v_prev_idx]++; if (branch_counters[v_prev_idx] > branches) throw Error("unexpected branch"); _encodeBond(mol, e_idx, mapping.ptr()); bond_mapping[e_idx] = bond_index++; if (save_bond_dirs) { int dir = mol.getBondDirection(e_idx); if (dir != 0) { if (dir == BOND_UP) dir = CMF_BOND_UP; else if (dir == BOND_DOWN) dir = CMF_BOND_DOWN; else dir = CMF_BOND_EITHER; const Edge &edge = mol.getEdge(e_idx); if (edge.beg == v_prev_idx && edge.end == v_idx) ; else if (edge.beg == v_idx && edge.end == v_prev_idx) _encode(CMF_BOND_SWAP_ENDS); else throw Error("internal"); _encode(dir); } } if (walk.isClosure(e_idx)) { for (j = 0; j < cycle_numbers.size(); j++) if (cycle_numbers[j] == v_idx) break; if (j == cycle_numbers.size()) throw Error("cycle number not found"); _encodeCycleNumer(j); cycle_numbers[j] = -1; write_atom = false; } } else _encode(CMF_SEPARATOR); if (write_atom) { _encodeAtom(mol, v_idx, mapping.ptr()); _atom_sequence.push(v_idx); int openings = walk.numOpenings(v_idx); for (j = 0; j < openings; j++) { for (k = 0; k < cycle_numbers.size(); k++) if (cycle_numbers[k] == -1) break; if (k == cycle_numbers.size()) cycle_numbers.push(v_idx); else cycle_numbers[k] = v_idx; _encodeCycleNumer(k); } } } Mapping mapping_group; mapping_group.atom_mapping = &mapping; mapping_group.bond_mapping = &bond_mapping; _encodeExtSection(mol, mapping_group); _encode(CMF_TERMINATOR); // if have internal encoder, finish it if (_encoder_obj.get() != 0) _encoder_obj->finish(); // for saveXyz() _mol = &mol; } void CmfSaver::_encodeUIntArray (const Array<int> &data, const Array<int> &mapping) { _output->writePackedUInt(data.size()); for (int i = 0; i < data.size(); i++) { int index = data[i]; if (index < 0) throw Error("Internal error: index is invald: %d", index); int mapped = mapping[index]; if (mapped < 0) throw Error("Internal error: mapping is invald"); _output->writePackedUInt(mapped); } } void CmfSaver::_encodeUIntArray (const Array<int> &data) { _output->writePackedUInt(data.size()); for (int i = 0; i < data.size(); i++) { int index = data[i]; if (index < 0) throw Error("Internal error: index is invald: %d", index); _output->writePackedUInt(index); } } void CmfSaver::_encodeUIntArraySkipNegative (const Array<int> &data) { int len = 0; for (int i = 0; i < data.size(); i++) if (data[i] >= 0) len++; _output->writePackedUInt(len); for (int i = 0; i < data.size(); i++) { int index = data[i]; if (index >= 0) _output->writePackedUInt(index); } } void CmfSaver::_encodeBaseSGroup (Molecule &mol, SGroup &sgroup, const Mapping &mapping) { _encodeUIntArray(sgroup.atoms, *mapping.atom_mapping); _encodeUIntArray(sgroup.bonds, *mapping.bond_mapping); } void CmfSaver::_encodeExtSection (Molecule &mol, const Mapping &mapping) { bool ext_printed = false; // Process all R-sites for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.isRSite(i)) { int count = 0; while (mol.getRSiteAttachmentPointByOrder(i, count) >= 0) count++; if (count == 0) continue; if (!ext_printed) { _encode(CMF_EXT); ext_printed = true; } _encode(CMF_RSITE_ATTACHMENTS); int idx = mapping.atom_mapping->at(i); if (idx < 0) throw Error("Internal error: idx < 0"); _output->writePackedUInt(idx); _output->writePackedUInt(count); for (int j = 0; j < count; j++) { int att = mol.getRSiteAttachmentPointByOrder(i, j); int idx2 = mapping.atom_mapping->at(att); if (idx2 < 0) throw Error("Internal error: idx2 < 0"); _output->writePackedUInt(idx2); } } } bool need_print_ext = mol.sgroups.getSGroupCount() > 0; if (need_print_ext && !ext_printed) { _encode(CMF_EXT); ext_printed = true; } for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_GEN) { _encode(CMF_GENERICSGROUP); _encodeBaseSGroup(mol, sg, mapping); } else if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &sd = (DataSGroup &)sg; _encode(CMF_DATASGROUP); _encodeBaseSGroup(mol, sd, mapping); _encodeString(sd.description); _encodeString(sd.name); _encodeString(sd.type); _encodeString(sd.querycode); _encodeString(sd.queryoper); _encodeString(sd.data); // Pack detached, relative, display_units, and sd.dasp_pos into one byte if (sd.dasp_pos < 0 || sd.dasp_pos > 9) throw Error("DataSGroup dasp_pos field should be less than 10: %d", sd.dasp_pos); byte packed = (sd.dasp_pos & 0x0F) | (sd.detached << 4) | (sd.relative << 5) | (sd.display_units << 6); _output->writeByte(packed); _output->writePackedUInt(sd.num_chars); _output->writeChar(sd.tag); } else if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sg; _encode(CMF_SUPERATOM); _encodeBaseSGroup(mol, sa, mapping); _encodeString(sa.subscript); _encodeString(sa.sa_class); byte packed = (sa.contracted & 0x01) | (sa.bond_connections.size() << 1); _output->writeByte(packed); if (sa.bond_connections.size() > 0) { for (int j = 0; j < sa.bond_connections.size(); j++) { _output->writePackedUInt(sa.bond_connections[j].bond_idx + 1); } } } else if (sg.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &su = (RepeatingUnit &)sg; _encode(CMF_REPEATINGUNIT); _encodeBaseSGroup(mol, su, mapping); _encodeString(su.subscript); _output->writePackedUInt(su.connectivity); } else if (sg.sgroup_type == SGroup::SG_TYPE_MUL) { MultipleGroup &sm = (MultipleGroup &)sg; _encode(CMF_MULTIPLEGROUP); _encodeBaseSGroup(mol, sm, mapping); _encodeUIntArray(sm.parent_atoms, *mapping.atom_mapping); if (sm.multiplier < 0) throw Error("internal error: SGroup multiplier is negative: %d", sm.multiplier); _output->writePackedUInt(sm.multiplier); } } // Encode mappings to restore if (save_mapping) { if (!ext_printed) { _encode(CMF_EXT); ext_printed = true; } _encode(CMF_MAPPING); _encodeUIntArraySkipNegative(*mapping.atom_mapping); _encodeUIntArraySkipNegative(*mapping.bond_mapping); } } void CmfSaver::_writeBaseSGroupXyz (Output &output, SGroup &sgroup, const VecRange &range) { output.writePackedUInt(sgroup.brackets.size()); for (int i = 0; i < sgroup.brackets.size(); i++) { _writeVec2f(output, sgroup.brackets[i][0], range); _writeVec2f(output, sgroup.brackets[i][1], range); } } void CmfSaver::_writeSGroupsXyz (Molecule &mol, Output &output, const VecRange &range) { // XYZ data should be written in the same order as in _encodeSGroups for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if ( (sg.sgroup_type == SGroup::SG_TYPE_GEN) || (sg.sgroup_type == SGroup::SG_TYPE_SRU) || (sg.sgroup_type == SGroup::SG_TYPE_MUL) ) { _writeBaseSGroupXyz(output, sg, range); } else if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &sd = (DataSGroup &)sg; _writeBaseSGroupXyz(output, sd, range); _writeVec2f(output, sd.display_pos, range); } else if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sg; _writeBaseSGroupXyz(output, sa, range); if (sa.bond_connections.size() > 0) { for (int j = 0; j < sa.bond_connections.size(); j++) { _writeDir2f(output, sa.bond_connections[j].bond_dir, range); } } } } } void CmfSaver::_encodeString (const Array<char> &str) { unsigned int len = str.size(); if (len > 0 && str[len - 1] == 0) len--; _output->writePackedUInt(len); _output->write(str.ptr(), len); } void CmfSaver::_encodeAtom (Molecule &mol, int idx, const int *mapping) { int number = 0; if (mol.isPseudoAtom(idx)) { const char *str = mol.getPseudoAtom(idx); size_t len = strlen(str); if (len < 1) throw Error("empty pseudo-atom"); if (len > 255) throw Error("pseudo-atom labels %d characters long are not supported (255 is the limit)", len); _encode(CMF_PSEUDOATOM); _encode((byte)len); do { _encode(*str); } while (*(++str) != 0); } else if (mol.isRSite(idx)) { int bits = mol.getRSiteBits(idx); if (bits > 255) { _encode(CMF_RSITE_EXT); _output->writePackedUInt((unsigned int)bits); } else { _encode(CMF_RSITE); _encode(bits); } } else { number = mol.getAtomNumber(idx); if (number <= 0 || number >= ELEM_MAX) throw Error("unexpected atom label"); _encode(number); } int charge = mol.getAtomCharge(idx); if (charge != 0) { int charge2 = charge - CMF_MIN_CHARGE; if (charge2 < 0 || charge2 >= CMF_NUM_OF_CHARGES) { _encode(CMF_CHARGE_EXT); int charge3 = charge + 128; if (charge3 < 0 || charge >= 256) throw Error("unexpected atom charge: %d", charge); _encode(charge3); } else _encode(charge2 + CMF_CHARGES); } int isotope = mol.getAtomIsotope(idx); if (isotope > 0) { int deviation = isotope - Element::getDefaultIsotope(number); if (deviation == 0) _encode(CMF_ISOTOPE_ZERO); else if (deviation == 1) _encode(CMF_ISOTOPE_PLUS1); else if (deviation == 2) _encode(CMF_ISOTOPE_PLUS2); else if (deviation == -1) _encode(CMF_ISOTOPE_MINUS1); else if (deviation == -2) _encode(CMF_ISOTOPE_MINUS2); else { deviation += 100; if (deviation < 0 || deviation > 255) throw Error("unexpected %s isotope: %d", Element::toString(number), isotope); _encode(CMF_ISOTOPE_OTHER); _encode(deviation); } } int radical = 0; if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx)) { try { radical = mol.getAtomRadical(idx); } catch (Element::Error) { } } if (radical > 0) { if (radical == RADICAL_SINGLET) _encode(CMF_RADICAL_SINGLET); else if (radical == RADICAL_DOUBLET) _encode(CMF_RADICAL_DOUBLET); else if (radical == RADICAL_TRIPLET) _encode(CMF_RADICAL_TRIPLET); else throw Error("bad radical value: %d", radical); } MoleculeStereocenters &stereo = mol.stereocenters; int stereo_type = stereo.getType(idx); if (stereo_type == MoleculeStereocenters::ATOM_ANY) _encode(CMF_STEREO_ANY); else if (stereo_type != 0) { bool rigid; int code; const int *pyramid = stereo.getPyramid(idx); if (pyramid[3] == -1) rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 3, mapping); else rigid = MoleculeStereocenters::isPyramidMappingRigid(pyramid, 4, mapping); if (stereo_type == MoleculeStereocenters::ATOM_ABS) code = CMF_STEREO_ABS_0; else { int group = stereo.getGroup(idx); if (group < 1 || group > CMF_MAX_STEREOGROUPS) throw Error("stereogroup number %d out of range", group); if (stereo_type == MoleculeStereocenters::ATOM_AND) code = CMF_STEREO_AND_0 + group - 1; else // stereo_type == MoleculeStereocenters::ATOM_OR code = CMF_STEREO_OR_0 + group - 1; } if (!rigid) // CMF_STEREO_*_0 -> CMF_STEREO_*_1 code += CMF_MAX_STEREOGROUPS * 2 + 1; _encode(code); } if (mol.allene_stereo.isCenter(idx)) { int left, right, parity, subst[4]; mol.allene_stereo.getByAtomIdx(idx, left, right, subst, parity); if (subst[1] != -1 && mapping[subst[1]] != -1 && mapping[subst[1]] < mapping[subst[0]]) parity = 3 - parity; if (subst[3] != -1 && mapping[subst[3]] != -1 && mapping[subst[3]] < mapping[subst[2]]) parity = 3 - parity; if (parity == 1) _encode(CMF_STEREO_ALLENE_0); else _encode(CMF_STEREO_ALLENE_1); } int impl_h = 0; if (!mol.isPseudoAtom(idx) && !mol.isRSite(idx) && Molecule::shouldWriteHCount(mol, idx)) { try { impl_h = mol.getImplicitH(idx); if (impl_h < 0 || impl_h > CMF_MAX_IMPLICIT_H) throw Error("implicit hydrogen count %d out of range", impl_h); _encode(CMF_IMPLICIT_H + impl_h); } catch (Element::Error) { } } if (!mol.isRSite(idx) && !mol.isPseudoAtom(idx)) { if (mol.getAtomAromaticity(idx) == ATOM_AROMATIC && (charge != 0 || (number != ELEM_C && number != ELEM_O))) { try { int valence = mol.getAtomValence(idx); if (valence < 0 || valence > CMF_MAX_VALENCE) { _encode(CMF_VALENCE_EXT); _output->writePackedUInt(valence); } else _encode(CMF_VALENCE + valence); } catch (Element::Error) { } } } int i; for (i = 1; i <= mol.attachmentPointCount(); i++) { int j, aidx; for (j = 0; (aidx = mol.getAttachmentPoint(i, j)) != -1; j++) if (aidx == idx) { _encode(CMF_ATTACHPT); _encode(i); } } if (atom_flags != 0) { int i, flags = atom_flags[idx]; for (i = 0; i < CMF_NUM_OF_ATOM_FLAGS; i++) if (flags & (1 << i)) _encode(CMF_ATOM_FLAGS + i); } if (save_highlighting) if (mol.isAtomHighlighted(idx)) _encode(CMF_HIGHLIGHTED); } void CmfSaver::_encodeBond (Molecule &mol, int idx, const int *mapping) { int order = mol.getBondOrder(idx); if (order == BOND_SINGLE) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_SINGLE_RING); else _encode(CMF_BOND_SINGLE_CHAIN); } else if (order == BOND_DOUBLE) { int parity = mol.cis_trans.getParity(idx); if (parity != 0) { int mapped_parity = MoleculeCisTrans::applyMapping(parity, mol.cis_trans.getSubstituents(idx), mapping, true); if (mapped_parity == MoleculeCisTrans::CIS) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING_CIS); else _encode(CMF_BOND_DOUBLE_CHAIN_CIS); } else // mapped_parity == MoleculeCisTrans::TRANS { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING_TRANS); else _encode(CMF_BOND_DOUBLE_CHAIN_TRANS); } } else if (mol.cis_trans.isIgnored(idx)) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_RING); else _encode(CMF_BOND_DOUBLE_IGNORED_CIS_TRANS_CHAIN); } else { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_DOUBLE_RING); else _encode(CMF_BOND_DOUBLE_CHAIN); } } else if (order == BOND_TRIPLE) { if (mol.getBondTopology(idx) == TOPOLOGY_RING) _encode(CMF_BOND_TRIPLE_RING); else _encode(CMF_BOND_TRIPLE_CHAIN); } else if (order == BOND_AROMATIC) _encode(CMF_BOND_AROMATIC); else throw Error("bad bond order: %d", order); if (bond_flags != 0) { int i, flags = bond_flags[idx]; for (i = 0; i < CMF_NUM_OF_BOND_FLAGS; i++) if (flags & (1 << i)) _encode(CMF_BOND_FLAGS + i); } if (save_highlighting) if (mol.isBondHighlighted(idx)) _encode(CMF_HIGHLIGHTED); } void CmfSaver::_encodeCycleNumer (int n) { while (n >= CMF_NUM_OF_CYCLES) { _encode(CMF_CYCLES_PLUS); n -= CMF_NUM_OF_CYCLES; } _encode(CMF_CYCLES + n); } void CmfSaver::_encode (byte symbol) { _output->writeByte(symbol); } void CmfSaver::_writeFloatInRange (Output &output, float v, float min, float range) { if (range > EPSILON) { float v2 = (((v - min) / range) * 65535 + 0.5f); if (v2 < 0 || v2 > 65536) throw Error("Internal error: Value %f is outsize of [%f, %f]", v, min, min + range); output.writeBinaryWord((word)v2); } else output.writeBinaryWord(0); } void CmfSaver::_writeVec3f (Output &output, const Vec3f &pos, const VecRange &range) { _writeFloatInRange(output, pos.x, range.xyz_min.x, range.xyz_range.x); _writeFloatInRange(output, pos.y, range.xyz_min.y, range.xyz_range.y); if (range.have_z) _writeFloatInRange(output, pos.z, range.xyz_min.z, range.xyz_range.z); } void CmfSaver::_writeVec2f (Output &output, const Vec2f &pos, const VecRange &range) { _writeFloatInRange(output, pos.x, range.xyz_min.x, range.xyz_range.x); _writeFloatInRange(output, pos.y, range.xyz_min.y, range.xyz_range.y); } void CmfSaver::_writeDir2f (Output &output, const Vec2f &dir, const VecRange &range) { _writeFloatInRange(output, dir.x, -range.xyz_range.x, 2 * range.xyz_range.x); _writeFloatInRange(output, dir.y, -range.xyz_range.y, 2 * range.xyz_range.y); } void CmfSaver::_updateSGroupsXyzMinMax (Molecule &mol, Vec3f &min, Vec3f &max) { for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if ( (sg.sgroup_type == SGroup::SG_TYPE_SUP) || (sg.sgroup_type == SGroup::SG_TYPE_SRU) || (sg.sgroup_type == SGroup::SG_TYPE_MUL) || (sg.sgroup_type == SGroup::SG_TYPE_GEN) ) { _updateBaseSGroupXyzMinMax(sg, min, max); } else if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &s = (DataSGroup &)sg; _updateBaseSGroupXyzMinMax(s, min, max); Vec3f display_pos(s.display_pos.x, s.display_pos.y, 0); min.min(display_pos); max.max(display_pos); } } } void CmfSaver::_updateBaseSGroupXyzMinMax (SGroup &sgroup, Vec3f &min, Vec3f &max) { for (int i = 0; i < sgroup.brackets.size(); i++) { Vec2f v1 = sgroup.brackets[i][0]; Vec2f v2 = sgroup.brackets[i][1]; Vec3f v31(v1.x, v1.y, 0); Vec3f v32(v2.x, v2.y, 0); min.min(v31); max.max(v31); min.min(v32); max.max(v32); } } void CmfSaver::saveXyz (Output &output) { int i; if (_mol == 0) throw Error("saveMolecule() must be called prior to saveXyz()"); if (!_mol->have_xyz) throw Error("saveXyz(): molecule has no XYZ"); Vec3f xyz_min( 10000, 10000, 10000); Vec3f xyz_max(-10000, -10000, -10000); VecRange range; for (i = 0; i < _atom_sequence.size(); i++) { const Vec3f &pos = _mol->getAtomXyz(_atom_sequence[i]); xyz_min.min(pos); xyz_max.max(pos); } _updateSGroupsXyzMinMax(*_mol, xyz_min, xyz_max); range.xyz_min = xyz_min; range.xyz_range.diff(xyz_max, xyz_min); output.writeBinaryFloat(range.xyz_min.x); output.writeBinaryFloat(range.xyz_min.y); output.writeBinaryFloat(range.xyz_min.z); output.writeBinaryFloat(range.xyz_range.x); output.writeBinaryFloat(range.xyz_range.y); output.writeBinaryFloat(range.xyz_range.z); if (range.xyz_range.z < EPSILON) { range.have_z = false; output.writeByte(0); } else { range.have_z = true; output.writeByte(1); } for (i = 0; i < _atom_sequence.size(); i++) { const Vec3f &pos = _mol->getAtomXyz(_atom_sequence[i]); _writeVec3f(output, pos, range); } _writeSGroupsXyz(*_mol, output, range); } const Array<int> & CmfSaver::getAtomSequence () { return _atom_sequence; } �������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/elements.cpp�������������������������������������������������������0000664�0000000�0000000�00000074665�12710376503�0021137�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <ctype.h> #include <stdio.h> #include <stdarg.h> #include <math.h> #include "base_c/defs.h" #include "base_cpp/array.h" #include "base_cpp/scanner.h" #include "molecule/elements.h" using namespace indigo; Element Element::_instance; IMPL_ERROR(Element, "element"); Element::Element () { _element_parameters.resize(ELEM_MAX); _element_parameters.zerofill(); _initAllPeriodic(); _initAllIsotopes(); _initAromatic(); _halogens.push(ELEM_F); _halogens.push(ELEM_Cl); _halogens.push(ELEM_Br); _halogens.push(ELEM_I); _halogens.push(ELEM_At); } void Element::_initPeriodic (int element, const char *name, int period, int group) { _Parameters ¶meters = _element_parameters[element]; strncpy(parameters.name, name, 3); parameters.group = group; parameters.period = period; _map.insert(name, element); } int Element::radicalElectrons (int radical) { if (radical == RADICAL_DOUBLET) return 1; if (radical == RADICAL_SINGLET || radical == RADICAL_TRIPLET) return 2; return 0; } int Element::radicalOrbitals (int radical) { if (radical != 0) return 1; return 0; } void Element::_initAllPeriodic () { #define INIT(elem, period, group) _initPeriodic(ELEM_##elem, #elem, period, group) INIT(H, 1, 1); INIT(He, 1, 8); INIT(Li, 2, 1); INIT(Be, 2, 2); INIT(B, 2, 3); INIT(C, 2, 4); INIT(N, 2, 5); INIT(O, 2, 6); INIT(F, 2, 7); INIT(Ne, 2, 8); INIT(Na, 3, 1); INIT(Mg, 3, 2); INIT(Al, 3, 3); INIT(Si, 3, 4); INIT(P, 3, 5); INIT(S, 3, 6); INIT(Cl, 3, 7); INIT(Ar, 3, 8); INIT(K, 4, 1); INIT(Ca, 4, 2); INIT(Sc, 4, 3); INIT(Ti, 4, 4); INIT(V, 4, 5); INIT(Cr, 4, 6); INIT(Mn, 4, 7); INIT(Fe, 4, 8); INIT(Co, 4, 8); INIT(Ni, 4, 8); INIT(Cu, 4, 1); INIT(Zn, 4, 2); INIT(Ga, 4, 3); INIT(Ge, 4, 4); INIT(As, 4, 5); INIT(Se, 4, 6); INIT(Br, 4, 7); INIT(Kr, 4, 8); INIT(Rb, 5, 1); INIT(Sr, 5, 2); INIT(Y, 5, 3); INIT(Zr, 5, 4); INIT(Nb, 5, 5); INIT(Mo, 5, 6); INIT(Tc, 5, 7); INIT(Ru, 5, 8); INIT(Rh, 5, 8); INIT(Pd, 5, 8); INIT(Ag, 5, 1); INIT(Cd, 5, 2); INIT(In, 5, 3); INIT(Sn, 5, 4); INIT(Sb, 5, 5); INIT(Te, 5, 6); INIT(I, 5, 7); INIT(Xe, 5, 8); INIT(Cs, 6, 1); INIT(Ba, 6, 2); INIT(La, 6, 3); INIT(Ce, 6, 3); INIT(Pr, 6, 3); INIT(Nd, 6, 3); INIT(Pm, 6, 3); INIT(Sm, 6, 3); INIT(Eu, 6, 3); INIT(Gd, 6, 3); INIT(Tb, 6, 3); INIT(Dy, 6, 3); INIT(Ho, 6, 3); INIT(Er, 6, 3); INIT(Tm, 6, 3); INIT(Yb, 6, 3); INIT(Lu, 6, 3); INIT(Hf, 6, 4); INIT(Ta, 6, 5); INIT(W, 6, 6); INIT(Re, 6, 7); INIT(Os, 6, 8); INIT(Ir, 6, 8); INIT(Pt, 6, 8); INIT(Au, 6, 1); INIT(Hg, 6, 2); INIT(Tl, 6, 3); INIT(Pb, 6, 4); INIT(Bi, 6, 5); INIT(Po, 6, 6); INIT(At, 6, 7); INIT(Rn, 6, 8); INIT(Fr, 7, 1); INIT(Ra, 7, 2); INIT(Ac, 7, 3); INIT(Th, 7, 3); INIT(Pa, 7, 3); INIT(U, 7, 3); INIT(Np, 7, 3); INIT(Pu, 7, 3); INIT(Am, 7, 3); INIT(Cm, 7, 3); INIT(Bk, 7, 3); INIT(Cf, 7, 3); INIT(Es, 7, 3); INIT(Fm, 7, 3); INIT(Md, 7, 3); INIT(No, 7, 3); INIT(Lr, 7, 3); INIT(Rf, 7, 3); #undef INIT } int Element::fromString (const char *name) { int *value = _instance._map.at2(name); if (value == 0) throw Error("fromString(): element %s not supported", name); return *value; } int Element::fromString2 (const char *name) { int *value = _instance._map.at2(name); if (value == 0) return -1; return *value; } int Element::fromChar (char c) { char str[2] = {c, 0}; return fromString(str); } int Element::fromTwoChars (char c1, char c2) { char str[3] = {c1, c2, 0}; return fromString(str); } int Element::fromTwoChars2 (char c1, char c2) { char str[3] = {c1, c2, 0}; return fromString2(str); } bool Element::isHalogen (int element) { return _instance._halogens.find(element) >= 0; } const char * Element::toString (int element) { if (element < 0 || element > ELEM_MAX) throw Error("bad element number: %d", element); return _instance._element_parameters[element].name; } int Element::calcValenceOfAromaticAtom (int elem, int charge, int n_arom, int min_conn) { if (elem == ELEM_C) return 4; if (elem == ELEM_N) return (charge == 1 ? 4 : 3); if (elem == ELEM_O) return (charge >= 1 ? 3 : 2); if (elem == ELEM_S && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds // There are no cases of implicit hydrogens in that condition // (PubChem search [sHD2] gives no hits) return 2; // ergo, valence is 2 if (min_conn == 3) // one single external bond // there can be a radical (see CID 11972190), // or an implicit hydrogen (see CID 20611310) return 4; // either way, the valence is 4 if (min_conn == 4) // two single or one double external bond // PubChem has no examples of 6-valent aromatic sulphur // (searching [sv6] gives no hits) return 4; // ergo, valence is 4 if (min_conn > 4) // OK, suppose we have an case of 6-valent aromatic sulphur here return 6; } else if (n_arom == 3) { if (min_conn <= 4) // no external bonds or one single external bond // For one external bond, see CID 10091381 // For no external bonds, see CID 20756501, although aromaticity // there is questionable. Anyway, no hydrogens are possible. return 4; else // 6-valent aromatic sulphur? return 6; } else if (n_arom == 4) { if (min_conn == 4) // Happened only on CID 10882272 and CID 24829837 return 4; // Valence = 4 in both structures else // 6-valent aromatic sulphur? return 6; } } else if (elem == ELEM_S && charge == 1) { if (n_arom == 2) { if (min_conn == 2) // common case: "=[S+]-" in an aromatic ring return 3; if (min_conn <= 4) // CID 9922592 return 5; } } else if (elem == ELEM_P && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds // implicit hydrogen (CID 164575) or radical (CID 10568539) is present return 3; // in any case, the valence is 3 if (min_conn == 3) // one single external bond return 3; if (min_conn == 4) // two single on one double external bond // two single: CID 140786, CID 341499 // one double: CID 17776485, CID 20207916 return 5; // valence is 5 in any case } if (n_arom == 3) // three aromatic bonds { if (min_conn == 3) // no external bonds return 3; // CID 15973306; no known examples with valence 5 if (min_conn == 5) // two single or one double external bond return 5; // the only known example is CID 10887416 } if (n_arom == 4) // four aromatic bonds? { if (min_conn == 4) // no external bonds return 5; // the only known example is CID 10887416, // yet the aromaticity of the smaller ring is questionable } } else if (elem == ELEM_P && charge == 1) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 3) // one single external bond return 4; // common case: "=[P+]([*])-" in an aromatic ring } } else if (elem == ELEM_P && charge == -1) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 2; // CID 10932222 } } else if (elem == ELEM_Se && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 2; // common case if (min_conn == 3) // one external bond return 4; // CID 10262587 if (min_conn == 4) // CID 21204858, two single external bonds // CID 14984497, one double aromatic bond return 4; } } else if (elem == ELEM_Se && charge == 1) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 3; // CID 10872228 if (min_conn == 3) // one external bond return 3; // CID 11115581 } } else if (elem == ELEM_As && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 3; // CID 136132 if (min_conn == 3) // one external bond return 3; // CID 237687 // no other cases known from PubChem } } else if (elem == ELEM_Te && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 3; // CID 136053 if (min_conn == 4) // CID 3088544, two single external bonds // CID 11457076, one double external bonds return 4; } else if (n_arom == 4) { if (min_conn == 4) // CID 11070061, four aromatic external bonds return 4; } // no other cases known from PubChem } else if (elem == ELEM_Te && charge == 1) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 3) // one external bond return 3; // CID 20802344 } // no other cases known from PubChem } else if (elem == ELEM_B) { if (n_arom == 2) { if (min_conn == 3) // one external bond return 3; // CID 574072 } } else if (elem == ELEM_Si) { if (n_arom == 2) { if (min_conn == 3) // one external bond return 4; // CID 18943170 } } return -1; } bool Element::calcValence (int elem, int charge, int radical, int conn, int &valence, int &hyd, bool to_throw) { int groupno = Element::group(elem); int rad = radicalElectrons(radical); valence = conn; hyd = 0; if (groupno == 1) { if (elem == ELEM_Li || elem == ELEM_Na || elem == ELEM_K || elem == ELEM_Rb || elem == ELEM_Cs || elem == ELEM_Fr) { valence = 1; hyd = 1 - rad - conn - abs(charge); } if (elem == ELEM_H) { valence = 1; if (charge == 1 && conn == 0) hyd = 0; else if (charge == -1 && conn == 0) hyd = 0; else if (charge == 0 && conn == 1) hyd = 0; else if (charge == 0 && conn == 0) hyd = 0; // elemental hydrogen, hmm... well, OK else hyd = -1; } } else if (groupno == 3) { if (elem == ELEM_B || elem == ELEM_Al || elem == ELEM_Ga || elem == ELEM_In) { if (charge == -1) { valence = 4; hyd = 4 - rad - conn; } else if (charge == -3 && elem != ELEM_B && rad + conn <= 6) { valence = rad + conn; hyd = 0; } else if (elem == ELEM_Al && charge == -2) { if (rad + conn == 5) { valence = 5; hyd = 0; } else hyd = -1; } else { valence = 3; hyd = 3 - rad - conn - abs(charge); } } else if (elem == ELEM_Tl) { if (charge == -1) { if (rad + conn <= 2) { valence = 2; hyd = 2 - rad - conn; } else { valence = 4; hyd = 4 - rad - conn; } } else if (charge == -2) { if (rad + conn <= 3) { valence = 3; hyd = 3 - rad - conn; } else { valence = 5; hyd = 5 - rad - conn; } } else if (charge == -3 && rad + conn == 6) { // ISIS Draw and Marvin allow this valence = 6; hyd = 0; } else { if (rad + conn + abs(charge) <= 1) { valence = 1; hyd = 1 - rad - conn - abs(charge); } else { valence = 3; hyd = 3 - rad - conn - abs(charge); } } } } else if (groupno == 4) { if (elem == ELEM_C) { valence = 4; hyd = 4 - rad - conn - abs(charge); } else if (elem == ELEM_Si || elem == ELEM_Ge || elem == ELEM_Sn || elem == ELEM_Pb) { if (charge == -2 && conn == 6 && rad == 0) { // Zinc fluorosilicate, hexafluorogermanium valence = 6; hyd = 0; } else if (charge == -1 && conn + rad == 5) { // with radical: [Ge-]: CID 18503269 // without radical: [Si-]: CID 358631 // [Ge-]: CID 19891516 valence = 5; hyd = 0; } else if (charge == -1 && conn + rad == 4 && elem == ELEM_Si) { valence = 5; // CID 438107 hyd = 1; } else if ((elem == ELEM_Sn || elem == ELEM_Pb) && conn + rad + abs(charge) <= 2) { // [SnH2]: CID 23962 // [PbH2]: CID 23927 valence = 2; hyd = 2 - rad - conn - abs(charge); } else { // 4-valent Pb with H: CID 24003 // 4-valent Sn with H: CID 5948 // 4-valent Ge with H2: CID 66239 // [GeH4]: CID 23984 valence = 4; hyd = 4 - rad - conn - abs(charge); } } } else if (groupno == 5) { if (elem == ELEM_N || elem == ELEM_P) { if (charge == 1) { valence = 4; hyd = 4 - rad - conn; } else if (charge == 2) { valence = 3; hyd = 3 - rad - conn; } else if (charge == -1 && elem == ELEM_P) { if (rad + conn <= 2) // phosphanide { valence = 2; hyd = 2 - rad - conn; } else if (rad + conn == 3) // no known examples with a hydrogen hyd = -1; else if (rad + conn == 4) { valence = 4; hyd = 0; } else if (rad + conn <= 6) { // w/ hydrogen: CID 3084356, CID 2784547 // w/o hydrogen: hexachlorophosphate valence = 6; hyd = 6 - rad - conn; } } else { if (elem == ELEM_N || rad + conn + abs(charge) <= 3) { valence = 3; hyd = 3 - rad - conn - abs(charge); } else // ELEM_P && rad + conn + abs(charge) > 3 { valence = 5; hyd = 5 - rad - conn - abs(charge); } } } else if (elem == ELEM_Bi || elem == ELEM_Sb || elem == ELEM_As) { if (charge == -1 && rad + conn == 6) { valence = 6; hyd = 0; } else if (charge == 1) { if (rad + conn <= 2 && elem != ELEM_As) { valence = 2; hyd = 2 - rad - conn; } else { valence = 4; hyd = 4 - rad - conn; } } else if (charge == 2) { valence = 3; hyd = 3 - rad - conn; } else if (charge == -2 && rad + conn == 5) { // Bi: CID 45158489 valence = 5; hyd = 0; } else { if (rad + conn + abs(charge) <= 3) { valence = 3; hyd = 3 - rad - conn - abs(charge); } else { valence = 5; hyd = 5 - rad - conn - abs(charge); } } } } else if (groupno == 6) { if (elem == ELEM_O) { if (charge >= 1) { valence = 3; hyd = 3 - rad - conn; } else { valence = 2; hyd = 2 - rad - conn - abs(charge); } } else if (elem == ELEM_S || elem == ELEM_Se || elem == ELEM_Po) { if (charge == 1) { if (conn <= 3) { valence = 3; hyd = 3 - rad - conn; } else { valence = 5; hyd = 5 - rad - conn; } } else if (charge == -1) { if (conn + rad <= 1) { valence = 1; hyd = 1 - rad - conn; } else if (conn + rad <= 3) { valence = 3; hyd = 3 - rad - conn; } // no real examples for the other two cases, just following ISIS/Draw logic else if (conn + rad <= 5) { valence = 5; hyd = 5 - rad - conn; } else { valence = 7; hyd = 7 - rad - conn; } } else { if (conn + rad + abs(charge) <= 2) { valence = 2; hyd = 2 - rad - conn - abs(charge); } else if (conn + rad + abs(charge) <= 4) // See examples in PubChem // [S] : CID 16684216 // [Se]: CID 5242252 // [Po]: no example, just following ISIS/Draw logic here { valence = 4; hyd = 4 - rad - conn - abs(charge); } else // See examples in PubChem // [S] : CID 46937044 // [Se]: CID 59786 // [Po]: no example, just following ISIS/Draw logic here { valence = 6; hyd = 6 - rad - conn - abs(charge); } } } else if (elem == ELEM_Te) { if (charge == -1) { if (rad + conn == 7) // CID 4191414 { valence = 7; hyd = 0; } else if (rad + conn == 5) { // no example, but both Marvin and ISIS are OK with this configuration valence = 5; hyd = 0; } else { valence = 1; hyd = 1 - rad - conn; } } else if (charge == 1) { valence = 3; hyd = 3 - rad - conn; // no known cases of 5-connected [Te+] } else if (charge == 2) { if (conn + rad == 4) { valence = conn + rad; hyd = 0; } else // ISIS Draw logic { hyd = 2 - conn - rad; valence = 2; } } else if (charge == 0) { if (conn + rad <= 2) { hyd = 2 - conn - rad; valence = 2; } else if (conn + rad <= 4) { hyd = 4 - conn - rad; // with hydrogen: CID 11968228 valence = 4; } else { hyd = 6 - conn - rad; // with hydrogen: CID 5231555, CID 6418860 valence = 6; } } } } else if (groupno == 7) { if (elem == ELEM_F) { valence = 1; hyd = 1 - rad - conn - abs(charge); } else if (elem == ELEM_Cl || elem == ELEM_Br || elem == ELEM_I || elem == ELEM_At) { if (charge == 1) { if (conn <= 2) { valence = 2; hyd = 2 - rad - conn; } else if (conn == 3 || conn == 5 || conn >= 7) hyd = -1; } else if (charge == 0) { if (conn <= 1) { valence = 1; hyd = 1 - rad - conn; } // While the halogens can have valence 3, they can not have // hydrogens in that case. else if (conn == 2 || conn == 4 || conn == 6) { if (rad == 1) { valence = conn; hyd = 0; } else hyd = -1; // will throw an error in the end } else if (conn > 7) hyd = -1; // will throw an error in the end } } } if (hyd < 0) { if (to_throw) throw Error("bad valence on %s having %d drawn bonds, charge %d, and %d radical electrons", toString(elem), conn, charge, rad); valence = conn; hyd = 0; return false; } return true; } int Element::calcValenceMinusHyd (int elem, int charge, int radical, int conn) { int groupno = Element::group(elem); int rad = radicalElectrons(radical); if (groupno == 3) { if (elem == ELEM_B || elem == ELEM_Al || elem == ELEM_Ga || elem == ELEM_In) { if (charge == -1) if (rad + conn <= 4) return rad + conn; } } else if (groupno == 5) { if (elem == ELEM_N || elem == ELEM_P) { if (charge == 1) return rad + conn; if (charge == 2) return rad + conn; } else if (elem == ELEM_Sb || elem == ELEM_Bi || elem == ELEM_As) { if (charge == 1) return rad + conn; else if (charge == 2) return rad + conn; } } else if (groupno == 6) { if (elem == ELEM_O) { if (charge >= 1) return rad + conn; } else if (elem == ELEM_S || elem == ELEM_Se || elem == ELEM_Po) { if (charge == 1 || charge == -1) return rad + conn; } } else if (groupno == 7) { if (elem == ELEM_Cl || elem == ELEM_Br || elem == ELEM_I || elem == ELEM_At) { if (charge == 1) return rad + conn; } } return rad + conn + abs(charge); } int Element::group (int elem) { return _instance._element_parameters[elem].group; } int Element::period (int elem) { return _instance._element_parameters[elem].period; } int Element::read (Scanner &scanner) { char str[3] = {0, 0, 0}; str[0] = scanner.readChar(); if (islower(scanner.lookNext())) str[1] = scanner.readChar(); return fromString(str); } void Element::_setStandardAtomicWeightIndex (int element, int index) { _Parameters &p = _instance._element_parameters[element]; p.natural_isotope_index = index; } void Element::_addElementIsotope (int element, int isotope, float mass, float isotopic_composition) { _instance._isotope_parameters_map.insert( _IsotopeKey(element, isotope), _IsotopeValue(mass, isotopic_composition)); } void Element::_initAllIsotopes () { #define ADD _addElementIsotope #define SET _setStandardAtomicWeightIndex #define NATURAL _IsotopeKey::NATURAL #include "elements_isotopes.inc" #undef ADD #undef SET #undef NATURAL _initDefaultIsotopes(); } float Element::getStandardAtomicWeight (int element) { _Parameters &p = _instance._element_parameters[element]; return getRelativeIsotopicMass(element, p.natural_isotope_index); } int Element::getDefaultIsotope (int element) { _Parameters &p = _instance._element_parameters[element]; return p.default_isotope; } int Element::getMostAbundantIsotope (int element) { _Parameters &p = _instance._element_parameters[element]; return p.most_abundant_isotope; } bool Element::getIsotopicComposition (int element, int isotope, float &res) { _IsotopeValue *value = _instance._isotope_parameters_map.at2( _IsotopeKey(element, isotope)); if (value == 0) return false; res = value->isotopic_composition; return true; } void Element::getMinMaxIsotopeIndex (int element, int &min, int &max) { _Parameters &p = _instance._element_parameters[element]; min = p.min_isotope_index; max = p.max_isotope_index; } float Element::getRelativeIsotopicMass (int element, int isotope) { _IsotopeValue *value = _instance._isotope_parameters_map.at2( _IsotopeKey(element, isotope)); if (value == 0) throw Error("getRelativeIsotopicMass: isotope (%s, %d) not found", toString(element), isotope); return value->mass; } void Element::_initDefaultIsotopes () { Array<int> def_isotope_index; def_isotope_index.resize(_element_parameters.size()); def_isotope_index.fffill(); Array<float> most_abundant_isotope_fraction; most_abundant_isotope_fraction.resize(_element_parameters.size()); most_abundant_isotope_fraction.fill(0); for (int i = ELEM_MIN; i < _element_parameters.size(); i++) { _element_parameters[i].default_isotope = -1; _element_parameters[i].most_abundant_isotope = -1; _element_parameters[i].min_isotope_index = 10000; _element_parameters[i].max_isotope_index = 0; } for (int i = _isotope_parameters_map.begin(); i != _isotope_parameters_map.end(); i = _isotope_parameters_map.next(i)) { _IsotopeKey &key = _isotope_parameters_map.key(i); _IsotopeValue &value = _isotope_parameters_map.value(i); if (key.isotope == _IsotopeKey::NATURAL) continue; float atomic_weight = getStandardAtomicWeight(key.element); float diff_best = 1e6; if (def_isotope_index[key.element] != -1) { int best_iso = def_isotope_index[key.element]; _IsotopeValue &best = _isotope_parameters_map.value(best_iso); diff_best = fabs(best.mass - atomic_weight); } float diff_cur = fabs(value.mass - atomic_weight); if (diff_best > diff_cur) { def_isotope_index[key.element] = i; _element_parameters[key.element].default_isotope = key.isotope; } int &min_iso = _element_parameters[key.element].min_isotope_index; int &max_iso = _element_parameters[key.element].max_isotope_index; if (min_iso > key.isotope) min_iso = key.isotope; if (max_iso < key.isotope) max_iso = key.isotope; float most_abundance = 1e6; if (_element_parameters[key.element].default_isotope != -1) { most_abundance = value.isotopic_composition; } if (value.isotopic_composition > most_abundant_isotope_fraction[key.element]) { most_abundant_isotope_fraction[key.element] = value.isotopic_composition; _element_parameters[key.element].most_abundant_isotope = key.isotope; } } for (int i = ELEM_MIN; i < _element_parameters.size(); i++) { _Parameters &element = _element_parameters[i]; if (element.natural_isotope_index != _IsotopeKey::NATURAL) element.default_isotope = element.natural_isotope_index; if (element.most_abundant_isotope == -1) element.most_abundant_isotope = element.default_isotope; } // Post-condition for (int i = ELEM_MIN; i < _element_parameters.size(); i++) if (_element_parameters[i].default_isotope == -1) // usually you can't catch this as it's being thrown before main() throw Error("default isotope is not set on element #%d", i); } int Element::orbitals (int elem, bool use_d_orbital) { int group = Element::group(elem); int period = Element::period(elem); switch (group) { case 1: return 1; case 2: return 2; default: if (use_d_orbital && period > 2 && group >= 4) return 9; else return 4; } } int Element::electrons (int elem, int charge) { return Element::group(elem) - charge; } int Element::getMaximumConnectivity (int elem, int charge, int radical, bool use_d_orbital) { int rad_electrons = radicalElectrons(radical); int electrons = Element::electrons(elem, charge) - rad_electrons; int rad_orbitals = radicalOrbitals(radical); int vacant_orbitals = Element::orbitals(elem, use_d_orbital) - rad_orbitals; if (electrons <= vacant_orbitals) return electrons; else return 2 * vacant_orbitals - electrons; } Element::_IsotopeKey::_IsotopeKey (int element, int isotope) : element(element), isotope(isotope) { } bool Element::_IsotopeKey::operator< (const _IsotopeKey &right) const { if (element < right.element) return true; if (element > right.element) return false; if (isotope < right.isotope) return true; if (isotope > right.isotope) return false; return false; } Element::_IsotopeValue::_IsotopeValue (float mass, float isotopic_composition) : mass(mass), isotopic_composition(isotopic_composition) { } bool Element::canBeAromatic (int element) { return _instance._element_parameters[element].can_be_aromatic; } void Element::_initAromatic () { int i; for (i = ELEM_B; i <= ELEM_F; i++) _element_parameters[i].can_be_aromatic = true; for (i = ELEM_Al; i <= ELEM_Cl; i++) _element_parameters[i].can_be_aromatic = true; for (i = ELEM_Ga; i <= ELEM_Br; i++) _element_parameters[i].can_be_aromatic = true; for (i = ELEM_In; i <= ELEM_I; i++) _element_parameters[i].can_be_aromatic = true; for (i = ELEM_Tl; i <= ELEM_Bi; i++) _element_parameters[i].can_be_aromatic = true; } Array<int> & Element::tautomerHeteroatoms () { return _instance._tau_heteroatoms; }���������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/elements_isotopes.inc����������������������������������������������0000664�0000000�0000000�00000404361�12710376503�0023041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ SET(ELEM_H, NATURAL); ADD(ELEM_H, NATURAL, 1.00794f, 0); ADD(ELEM_H, 1, 1.0078250321f, 99.9885f); ADD(ELEM_H, 2, 2.0141017780f, 0.0115f); ADD(ELEM_H, 3, 3.0160492675f, 0.0f); ADD(ELEM_H, 4, 4.02783f, 0.0f); ADD(ELEM_H, 5, 5.03954f, 0.0f); ADD(ELEM_H, 6, 6.04494f, 0.0f); SET(ELEM_He, NATURAL); ADD(ELEM_He, NATURAL, 4.002602f, 0); ADD(ELEM_He, 3, 3.0160293097f, 0.000137f); ADD(ELEM_He, 4, 4.0026032497f, 99.999863f); ADD(ELEM_He, 5, 5.012220f, 0.0f); ADD(ELEM_He, 6, 6.0188881f, 0.0f); ADD(ELEM_He, 7, 7.028030f, 0.0f); ADD(ELEM_He, 8, 8.033922f, 0.0f); ADD(ELEM_He, 9, 9.043820f, 0.0f); ADD(ELEM_He, 10, 10.052400f, 0.0f); SET(ELEM_Li, NATURAL); ADD(ELEM_Li, NATURAL, 6.941f, 0); ADD(ELEM_Li, 4, 4.02718f, 0.0f); ADD(ELEM_Li, 5, 5.012540f, 0.0f); ADD(ELEM_Li, 6, 6.0151223f, 7.59f); ADD(ELEM_Li, 7, 7.0160040f, 92.41f); ADD(ELEM_Li, 8, 8.0224867f, 0.0f); ADD(ELEM_Li, 9, 9.0267891f, 0.0f); ADD(ELEM_Li, 10, 10.035481f, 0.0f); ADD(ELEM_Li, 11, 11.043796f, 0.0f); ADD(ELEM_Li, 12, 12.05378f, 0.0f); SET(ELEM_Be, NATURAL); ADD(ELEM_Be, NATURAL, 9.012182f, 0); ADD(ELEM_Be, 5, 5.04079f, 0.0f); ADD(ELEM_Be, 6, 6.019726f, 0.0f); ADD(ELEM_Be, 7, 7.0169292f, 0.0f); ADD(ELEM_Be, 8, 8.00530509f, 0.0f); ADD(ELEM_Be, 9, 9.0121821f, 100.0f); ADD(ELEM_Be, 10, 10.0135337f, 0.0f); ADD(ELEM_Be, 11, 11.021658f, 0.0f); ADD(ELEM_Be, 12, 12.026921f, 0.0f); ADD(ELEM_Be, 13, 13.03613f, 0.0f); ADD(ELEM_Be, 14, 14.04282f, 0.0f); SET(ELEM_B, NATURAL); ADD(ELEM_B, NATURAL, 10.811f, 0); ADD(ELEM_B, 7, 7.029920f, 0.0f); ADD(ELEM_B, 8, 8.0246067f, 0.0f); ADD(ELEM_B, 9, 9.0133288f, 0.0f); ADD(ELEM_B, 10, 10.0129370f, 19.9f); ADD(ELEM_B, 11, 11.0093055f, 80.1f); ADD(ELEM_B, 12, 12.0143521f, 0.0f); ADD(ELEM_B, 13, 13.0177803f, 0.0f); ADD(ELEM_B, 14, 14.025404f, 0.0f); ADD(ELEM_B, 15, 15.031097f, 0.0f); ADD(ELEM_B, 16, 16.039810f, 0.0f); ADD(ELEM_B, 17, 17.04693f, 0.0f); ADD(ELEM_B, 18, 18.05617f, 0.0f); ADD(ELEM_B, 19, 19.06373f, 0.0f); SET(ELEM_C, NATURAL); ADD(ELEM_C, NATURAL, 12.0107f, 0); ADD(ELEM_C, 8, 8.037675f, 0.0f); ADD(ELEM_C, 9, 9.0310401f, 0.0f); ADD(ELEM_C, 10, 10.0168531f, 0.0f); ADD(ELEM_C, 11, 11.0114338f, 0.0f); ADD(ELEM_C, 12, 12.0000000f, 98.93f); ADD(ELEM_C, 13, 13.0033548378f, 1.07f); ADD(ELEM_C, 14, 14.003241988f, 0.0f); ADD(ELEM_C, 15, 15.0105993f, 0.0f); ADD(ELEM_C, 16, 16.014701f, 0.0f); ADD(ELEM_C, 17, 17.022584f, 0.0f); ADD(ELEM_C, 18, 18.026760f, 0.0f); ADD(ELEM_C, 19, 19.03525f, 0.0f); ADD(ELEM_C, 20, 20.04032f, 0.0f); ADD(ELEM_C, 21, 21.04934f, 0.0f); ADD(ELEM_C, 22, 22.05645f, 0.0f); SET(ELEM_N, NATURAL); ADD(ELEM_N, NATURAL, 14.0067f, 0); ADD(ELEM_N, 10, 10.04262f, 0.0f); ADD(ELEM_N, 11, 11.02680f, 0.0f); ADD(ELEM_N, 12, 12.0186132f, 0.0f); ADD(ELEM_N, 13, 13.00573858f, 0.0f); ADD(ELEM_N, 14, 14.0030740052f, 99.632f); ADD(ELEM_N, 15, 15.0001088984f, 0.368f); ADD(ELEM_N, 16, 16.0061014f, 0.0f); ADD(ELEM_N, 17, 17.008450f, 0.0f); ADD(ELEM_N, 18, 18.014082f, 0.0f); ADD(ELEM_N, 19, 19.017027f, 0.0f); ADD(ELEM_N, 20, 20.023370f, 0.0f); ADD(ELEM_N, 21, 21.02709f, 0.0f); ADD(ELEM_N, 22, 22.03444f, 0.0f); ADD(ELEM_N, 23, 23.04051f, 0.0f); ADD(ELEM_N, 24, 24.05050f, 0.0f); SET(ELEM_O, NATURAL); ADD(ELEM_O, NATURAL, 15.9994f, 0); ADD(ELEM_O, 12, 12.034405f, 0.0f); ADD(ELEM_O, 13, 13.024810f, 0.0f); ADD(ELEM_O, 14, 14.00859529f, 0.0f); ADD(ELEM_O, 15, 15.0030654f, 0.0f); ADD(ELEM_O, 16, 15.9949146221f, 99.757f); ADD(ELEM_O, 17, 16.99913150f, 0.038f); ADD(ELEM_O, 18, 17.9991604f, 0.205f); ADD(ELEM_O, 19, 19.003579f, 0.0f); ADD(ELEM_O, 20, 20.0040762f, 0.0f); ADD(ELEM_O, 21, 21.008655f, 0.0f); ADD(ELEM_O, 22, 22.009970f, 0.0f); ADD(ELEM_O, 23, 23.01569f, 0.0f); ADD(ELEM_O, 24, 24.02037f, 0.0f); ADD(ELEM_O, 25, 25.02914f, 0.0f); ADD(ELEM_O, 26, 26.03775f, 0.0f); SET(ELEM_F, NATURAL); ADD(ELEM_F, NATURAL, 18.9984032f, 0); ADD(ELEM_F, 14, 14.03608f, 0.0f); ADD(ELEM_F, 15, 15.01801f, 0.0f); ADD(ELEM_F, 16, 16.011466f, 0.0f); ADD(ELEM_F, 17, 17.00209524f, 0.0f); ADD(ELEM_F, 18, 18.0009377f, 0.0f); ADD(ELEM_F, 19, 18.99840320f, 100.0f); ADD(ELEM_F, 20, 19.99998132f, 0.0f); ADD(ELEM_F, 21, 20.9999489f, 0.0f); ADD(ELEM_F, 22, 22.002999f, 0.0f); ADD(ELEM_F, 23, 23.003570f, 0.0f); ADD(ELEM_F, 24, 24.008100f, 0.0f); ADD(ELEM_F, 25, 25.012090f, 0.0f); ADD(ELEM_F, 26, 26.01963f, 0.0f); ADD(ELEM_F, 27, 27.02689f, 0.0f); ADD(ELEM_F, 28, 28.03567f, 0.0f); ADD(ELEM_F, 29, 29.04326f, 0.0f); SET(ELEM_Ne, NATURAL); ADD(ELEM_Ne, NATURAL, 20.1797f, 0); ADD(ELEM_Ne, 16, 16.025757f, 0.0f); ADD(ELEM_Ne, 17, 17.017700f, 0.0f); ADD(ELEM_Ne, 18, 18.0056971f, 0.0f); ADD(ELEM_Ne, 19, 19.0018798f, 0.0f); ADD(ELEM_Ne, 20, 19.9924401759f, 90.48f); ADD(ELEM_Ne, 21, 20.99384674f, 0.27f); ADD(ELEM_Ne, 22, 21.99138551f, 9.25f); ADD(ELEM_Ne, 23, 22.99446734f, 0.0f); ADD(ELEM_Ne, 24, 23.993615f, 0.0f); ADD(ELEM_Ne, 25, 24.997790f, 0.0f); ADD(ELEM_Ne, 26, 26.000460f, 0.0f); ADD(ELEM_Ne, 27, 27.00762f, 0.0f); ADD(ELEM_Ne, 28, 28.01211f, 0.0f); ADD(ELEM_Ne, 29, 29.01935f, 0.0f); ADD(ELEM_Ne, 30, 30.02387f, 0.0f); ADD(ELEM_Ne, 31, 31.03311f, 0.0f); ADD(ELEM_Ne, 32, 32.03991f, 0.0f); SET(ELEM_Na, NATURAL); ADD(ELEM_Na, NATURAL, 22.989770f, 0); ADD(ELEM_Na, 18, 18.02718f, 0.0f); ADD(ELEM_Na, 19, 19.013879f, 0.0f); ADD(ELEM_Na, 20, 20.007348f, 0.0f); ADD(ELEM_Na, 21, 20.9976551f, 0.0f); ADD(ELEM_Na, 22, 21.9944368f, 0.0f); ADD(ELEM_Na, 23, 22.98976967f, 100.0f); ADD(ELEM_Na, 24, 23.99096333f, 0.0f); ADD(ELEM_Na, 25, 24.9899544f, 0.0f); ADD(ELEM_Na, 26, 25.992590f, 0.0f); ADD(ELEM_Na, 27, 26.994010f, 0.0f); ADD(ELEM_Na, 28, 27.998890f, 0.0f); ADD(ELEM_Na, 29, 29.00281f, 0.0f); ADD(ELEM_Na, 30, 30.00923f, 0.0f); ADD(ELEM_Na, 31, 31.01360f, 0.0f); ADD(ELEM_Na, 32, 32.01965f, 0.0f); ADD(ELEM_Na, 33, 33.02739f, 0.0f); ADD(ELEM_Na, 34, 34.03490f, 0.0f); ADD(ELEM_Na, 35, 35.04418f, 0.0f); SET(ELEM_Mg, NATURAL); ADD(ELEM_Mg, NATURAL, 24.3050f, 0); ADD(ELEM_Mg, 20, 20.018863f, 0.0f); ADD(ELEM_Mg, 21, 21.011714f, 0.0f); ADD(ELEM_Mg, 22, 21.9995741f, 0.0f); ADD(ELEM_Mg, 23, 22.9941249f, 0.0f); ADD(ELEM_Mg, 24, 23.98504190f, 78.99f); ADD(ELEM_Mg, 25, 24.98583702f, 10.00f); ADD(ELEM_Mg, 26, 25.98259304f, 11.01f); ADD(ELEM_Mg, 27, 26.98434074f, 0.0f); ADD(ELEM_Mg, 28, 27.9838767f, 0.0f); ADD(ELEM_Mg, 29, 28.988550f, 0.0f); ADD(ELEM_Mg, 30, 29.990460f, 0.0f); ADD(ELEM_Mg, 31, 30.996550f, 0.0f); ADD(ELEM_Mg, 32, 31.99915f, 0.0f); ADD(ELEM_Mg, 33, 33.00559f, 0.0f); ADD(ELEM_Mg, 34, 34.00907f, 0.0f); ADD(ELEM_Mg, 35, 35.01749f, 0.0f); ADD(ELEM_Mg, 36, 36.02245f, 0.0f); ADD(ELEM_Mg, 37, 37.03124f, 0.0f); SET(ELEM_Al, NATURAL); ADD(ELEM_Al, NATURAL, 26.981538f, 0); ADD(ELEM_Al, 21, 21.02804f, 0.0f); ADD(ELEM_Al, 22, 22.01952f, 0.0f); ADD(ELEM_Al, 23, 23.007265f, 0.0f); ADD(ELEM_Al, 24, 23.999941f, 0.0f); ADD(ELEM_Al, 25, 24.9904286f, 0.0f); ADD(ELEM_Al, 26, 25.98689166f, 0.0f); ADD(ELEM_Al, 27, 26.98153844f, 100.0f); ADD(ELEM_Al, 28, 27.98191018f, 0.0f); ADD(ELEM_Al, 29, 28.9804448f, 0.0f); ADD(ELEM_Al, 30, 29.982960f, 0.0f); ADD(ELEM_Al, 31, 30.983946f, 0.0f); ADD(ELEM_Al, 32, 31.988120f, 0.0f); ADD(ELEM_Al, 33, 32.990870f, 0.0f); ADD(ELEM_Al, 34, 33.99693f, 0.0f); ADD(ELEM_Al, 35, 34.99994f, 0.0f); ADD(ELEM_Al, 36, 36.00635f, 0.0f); ADD(ELEM_Al, 37, 37.01031f, 0.0f); ADD(ELEM_Al, 38, 38.01690f, 0.0f); ADD(ELEM_Al, 39, 39.02190f, 0.0f); SET(ELEM_Si, NATURAL); ADD(ELEM_Si, NATURAL, 28.0855f, 0); ADD(ELEM_Si, 22, 22.03453f, 0.0f); ADD(ELEM_Si, 23, 23.02552f, 0.0f); ADD(ELEM_Si, 24, 24.011546f, 0.0f); ADD(ELEM_Si, 25, 25.004107f, 0.0f); ADD(ELEM_Si, 26, 25.992330f, 0.0f); ADD(ELEM_Si, 27, 26.98670476f, 0.0f); ADD(ELEM_Si, 28, 27.9769265327f, 92.2297f); ADD(ELEM_Si, 29, 28.97649472f, 4.6832f); ADD(ELEM_Si, 30, 29.97377022f, 3.0872f); ADD(ELEM_Si, 31, 30.97536327f, 0.0f); ADD(ELEM_Si, 32, 31.9741481f, 0.0f); ADD(ELEM_Si, 33, 32.978001f, 0.0f); ADD(ELEM_Si, 34, 33.978576f, 0.0f); ADD(ELEM_Si, 35, 34.984580f, 0.0f); ADD(ELEM_Si, 36, 35.98669f, 0.0f); ADD(ELEM_Si, 37, 36.99300f, 0.0f); ADD(ELEM_Si, 38, 37.99598f, 0.0f); ADD(ELEM_Si, 39, 39.00230f, 0.0f); ADD(ELEM_Si, 40, 40.00580f, 0.0f); ADD(ELEM_Si, 41, 41.01270f, 0.0f); ADD(ELEM_Si, 42, 42.01610f, 0.0f); SET(ELEM_P, NATURAL); ADD(ELEM_P, NATURAL, 30.973761f, 0); ADD(ELEM_P, 24, 24.03435f, 0.0f); ADD(ELEM_P, 25, 25.02026f, 0.0f); ADD(ELEM_P, 26, 26.01178f, 0.0f); ADD(ELEM_P, 27, 26.999190f, 0.0f); ADD(ELEM_P, 28, 27.992312f, 0.0f); ADD(ELEM_P, 29, 28.9818014f, 0.0f); ADD(ELEM_P, 30, 29.9783138f, 0.0f); ADD(ELEM_P, 31, 30.97376151f, 100.0f); ADD(ELEM_P, 32, 31.97390716f, 0.0f); ADD(ELEM_P, 33, 32.9717253f, 0.0f); ADD(ELEM_P, 34, 33.973636f, 0.0f); ADD(ELEM_P, 35, 34.9733142f, 0.0f); ADD(ELEM_P, 36, 35.978260f, 0.0f); ADD(ELEM_P, 37, 36.979610f, 0.0f); ADD(ELEM_P, 38, 37.98447f, 0.0f); ADD(ELEM_P, 39, 38.98642f, 0.0f); ADD(ELEM_P, 40, 39.99105f, 0.0f); ADD(ELEM_P, 41, 40.99480f, 0.0f); ADD(ELEM_P, 42, 42.00009f, 0.0f); ADD(ELEM_P, 43, 43.00331f, 0.0f); ADD(ELEM_P, 44, 44.00988f, 0.0f); ADD(ELEM_P, 45, 45.01514f, 0.0f); ADD(ELEM_P, 46, 46.02383f, 0.0f); SET(ELEM_S, NATURAL); ADD(ELEM_S, NATURAL, 32.065f, 0); ADD(ELEM_S, 26, 26.02788f, 0.0f); ADD(ELEM_S, 27, 27.01880f, 0.0f); ADD(ELEM_S, 28, 28.00437f, 0.0f); ADD(ELEM_S, 29, 28.996610f, 0.0f); ADD(ELEM_S, 30, 29.984903f, 0.0f); ADD(ELEM_S, 31, 30.9795544f, 0.0f); ADD(ELEM_S, 32, 31.97207069f, 94.93f); ADD(ELEM_S, 33, 32.97145850f, 0.76f); ADD(ELEM_S, 34, 33.96786683f, 4.29f); ADD(ELEM_S, 35, 34.96903214f, 0.0f); ADD(ELEM_S, 36, 35.96708088f, 0.02f); ADD(ELEM_S, 37, 36.97112572f, 0.0f); ADD(ELEM_S, 38, 37.971163f, 0.0f); ADD(ELEM_S, 39, 38.975140f, 0.0f); ADD(ELEM_S, 40, 39.97547f, 0.0f); ADD(ELEM_S, 41, 40.98003f, 0.0f); ADD(ELEM_S, 42, 41.98149f, 0.0f); ADD(ELEM_S, 43, 42.98660f, 0.0f); ADD(ELEM_S, 44, 43.98832f, 0.0f); ADD(ELEM_S, 45, 44.99482f, 0.0f); ADD(ELEM_S, 46, 45.99957f, 0.0f); ADD(ELEM_S, 47, 47.00762f, 0.0f); ADD(ELEM_S, 48, 48.01299f, 0.0f); ADD(ELEM_S, 49, 49.02201f, 0.0f); SET(ELEM_Cl, NATURAL); ADD(ELEM_Cl, NATURAL, 35.453f, 0); ADD(ELEM_Cl, 28, 28.02851f, 0.0f); ADD(ELEM_Cl, 29, 29.01411f, 0.0f); ADD(ELEM_Cl, 30, 30.00477f, 0.0f); ADD(ELEM_Cl, 31, 30.992420f, 0.0f); ADD(ELEM_Cl, 32, 31.985689f, 0.0f); ADD(ELEM_Cl, 33, 32.9774518f, 0.0f); ADD(ELEM_Cl, 34, 33.97376197f, 0.0f); ADD(ELEM_Cl, 35, 34.96885271f, 75.78f); ADD(ELEM_Cl, 36, 35.96830695f, 0.0f); ADD(ELEM_Cl, 37, 36.96590260f, 24.22f); ADD(ELEM_Cl, 38, 37.96801055f, 0.0f); ADD(ELEM_Cl, 39, 38.9680077f, 0.0f); ADD(ELEM_Cl, 40, 39.970420f, 0.0f); ADD(ELEM_Cl, 41, 40.970650f, 0.0f); ADD(ELEM_Cl, 42, 41.97317f, 0.0f); ADD(ELEM_Cl, 43, 42.97420f, 0.0f); ADD(ELEM_Cl, 44, 43.97854f, 0.0f); ADD(ELEM_Cl, 45, 44.97970f, 0.0f); ADD(ELEM_Cl, 46, 45.98412f, 0.0f); ADD(ELEM_Cl, 47, 46.98795f, 0.0f); ADD(ELEM_Cl, 48, 47.99485f, 0.0f); ADD(ELEM_Cl, 49, 48.99989f, 0.0f); ADD(ELEM_Cl, 50, 50.00773f, 0.0f); ADD(ELEM_Cl, 51, 51.01353f, 0.0f); SET(ELEM_Ar, NATURAL); ADD(ELEM_Ar, NATURAL, 39.948f, 0); ADD(ELEM_Ar, 30, 30.02156f, 0.0f); ADD(ELEM_Ar, 31, 31.01213f, 0.0f); ADD(ELEM_Ar, 32, 31.997660f, 0.0f); ADD(ELEM_Ar, 33, 32.989930f, 0.0f); ADD(ELEM_Ar, 34, 33.980270f, 0.0f); ADD(ELEM_Ar, 35, 34.9752567f, 0.0f); ADD(ELEM_Ar, 36, 35.96754628f, 0.3365f); ADD(ELEM_Ar, 37, 36.9667759f, 0.0f); ADD(ELEM_Ar, 38, 37.9627322f, 0.0632f); ADD(ELEM_Ar, 39, 38.964313f, 0.0f); ADD(ELEM_Ar, 40, 39.962383123f, 99.6003f); ADD(ELEM_Ar, 41, 40.9645008f, 0.0f); ADD(ELEM_Ar, 42, 41.963050f, 0.0f); ADD(ELEM_Ar, 43, 42.965670f, 0.0f); ADD(ELEM_Ar, 44, 43.965365f, 0.0f); ADD(ELEM_Ar, 45, 44.968090f, 0.0f); ADD(ELEM_Ar, 46, 45.968090f, 0.0f); ADD(ELEM_Ar, 47, 46.97219f, 0.0f); ADD(ELEM_Ar, 48, 47.97507f, 0.0f); ADD(ELEM_Ar, 49, 48.98218f, 0.0f); ADD(ELEM_Ar, 50, 49.98594f, 0.0f); ADD(ELEM_Ar, 51, 50.99324f, 0.0f); ADD(ELEM_Ar, 52, 51.99817f, 0.0f); ADD(ELEM_Ar, 53, 53.00623f, 0.0f); SET(ELEM_K, NATURAL); ADD(ELEM_K, NATURAL, 39.0983f, 0); ADD(ELEM_K, 32, 32.02192f, 0.0f); ADD(ELEM_K, 33, 33.00726f, 0.0f); ADD(ELEM_K, 34, 33.99841f, 0.0f); ADD(ELEM_K, 35, 34.988012f, 0.0f); ADD(ELEM_K, 36, 35.981293f, 0.0f); ADD(ELEM_K, 37, 36.97337691f, 0.0f); ADD(ELEM_K, 38, 37.9690801f, 0.0f); ADD(ELEM_K, 39, 38.9637069f, 93.2581f); ADD(ELEM_K, 40, 39.96399867f, 0.0117f); ADD(ELEM_K, 41, 40.96182597f, 6.7302f); ADD(ELEM_K, 42, 41.9624031f, 0.0f); ADD(ELEM_K, 43, 42.960716f, 0.0f); ADD(ELEM_K, 44, 43.961560f, 0.0f); ADD(ELEM_K, 45, 44.960700f, 0.0f); ADD(ELEM_K, 46, 45.961976f, 0.0f); ADD(ELEM_K, 47, 46.961678f, 0.0f); ADD(ELEM_K, 48, 47.965513f, 0.0f); ADD(ELEM_K, 49, 48.967450f, 0.0f); ADD(ELEM_K, 50, 49.97278f, 0.0f); ADD(ELEM_K, 51, 50.97638f, 0.0f); ADD(ELEM_K, 52, 51.98261f, 0.0f); ADD(ELEM_K, 53, 52.98712f, 0.0f); ADD(ELEM_K, 54, 53.99399f, 0.0f); ADD(ELEM_K, 55, 54.99939f, 0.0f); SET(ELEM_Ca, NATURAL); ADD(ELEM_Ca, NATURAL, 40.078f, 0); ADD(ELEM_Ca, 34, 34.01412f, 0.0f); ADD(ELEM_Ca, 35, 35.004770f, 0.0f); ADD(ELEM_Ca, 36, 35.993090f, 0.0f); ADD(ELEM_Ca, 37, 36.985872f, 0.0f); ADD(ELEM_Ca, 38, 37.976319f, 0.0f); ADD(ELEM_Ca, 39, 38.9707177f, 0.0f); ADD(ELEM_Ca, 40, 39.9625912f, 96.941f); ADD(ELEM_Ca, 41, 40.9622783f, 0.0f); ADD(ELEM_Ca, 42, 41.9586183f, 0.647f); ADD(ELEM_Ca, 43, 42.9587668f, 0.135f); ADD(ELEM_Ca, 44, 43.9554811f, 2.086f); ADD(ELEM_Ca, 45, 44.9561859f, 0.0f); ADD(ELEM_Ca, 46, 45.9536928f, 0.004f); ADD(ELEM_Ca, 47, 46.9545465f, 0.0f); ADD(ELEM_Ca, 48, 47.952534f, 0.187f); ADD(ELEM_Ca, 49, 48.955673f, 0.0f); ADD(ELEM_Ca, 50, 49.957518f, 0.0f); ADD(ELEM_Ca, 51, 50.96147f, 0.0f); ADD(ELEM_Ca, 52, 51.96510f, 0.0f); ADD(ELEM_Ca, 53, 52.97005f, 0.0f); ADD(ELEM_Ca, 54, 53.97468f, 0.0f); ADD(ELEM_Ca, 55, 54.98055f, 0.0f); ADD(ELEM_Ca, 56, 55.98579f, 0.0f); ADD(ELEM_Ca, 57, 56.99236f, 0.0f); SET(ELEM_Sc, NATURAL); ADD(ELEM_Sc, NATURAL, 44.955910f, 0); ADD(ELEM_Sc, 36, 36.01492f, 0.0f); ADD(ELEM_Sc, 37, 37.00305f, 0.0f); ADD(ELEM_Sc, 38, 37.99470f, 0.0f); ADD(ELEM_Sc, 39, 38.984790f, 0.0f); ADD(ELEM_Sc, 40, 39.977964f, 0.0f); ADD(ELEM_Sc, 41, 40.9692513f, 0.0f); ADD(ELEM_Sc, 42, 41.9655168f, 0.0f); ADD(ELEM_Sc, 43, 42.9611510f, 0.0f); ADD(ELEM_Sc, 44, 43.9594030f, 0.0f); ADD(ELEM_Sc, 45, 44.9559102f, 100.0f); ADD(ELEM_Sc, 46, 45.9551703f, 0.0f); ADD(ELEM_Sc, 47, 46.9524080f, 0.0f); ADD(ELEM_Sc, 48, 47.952235f, 0.0f); ADD(ELEM_Sc, 49, 48.950024f, 0.0f); ADD(ELEM_Sc, 50, 49.952187f, 0.0f); ADD(ELEM_Sc, 51, 50.953603f, 0.0f); ADD(ELEM_Sc, 52, 51.95665f, 0.0f); ADD(ELEM_Sc, 53, 52.95924f, 0.0f); ADD(ELEM_Sc, 54, 53.96300f, 0.0f); ADD(ELEM_Sc, 55, 54.96743f, 0.0f); ADD(ELEM_Sc, 56, 55.97266f, 0.0f); ADD(ELEM_Sc, 57, 56.97704f, 0.0f); ADD(ELEM_Sc, 58, 57.98307f, 0.0f); ADD(ELEM_Sc, 59, 58.98804f, 0.0f); SET(ELEM_Ti, NATURAL); ADD(ELEM_Ti, NATURAL, 47.867f, 0); ADD(ELEM_Ti, 38, 38.00977f, 0.0f); ADD(ELEM_Ti, 39, 39.00132f, 0.0f); ADD(ELEM_Ti, 40, 39.99050f, 0.0f); ADD(ELEM_Ti, 41, 40.983130f, 0.0f); ADD(ELEM_Ti, 42, 41.973032f, 0.0f); ADD(ELEM_Ti, 43, 42.968523f, 0.0f); ADD(ELEM_Ti, 44, 43.9596902f, 0.0f); ADD(ELEM_Ti, 45, 44.9581243f, 0.0f); ADD(ELEM_Ti, 46, 45.9526295f, 8.25f); ADD(ELEM_Ti, 47, 46.9517638f, 7.44f); ADD(ELEM_Ti, 48, 47.9479471f, 73.72f); ADD(ELEM_Ti, 49, 48.9478708f, 5.41f); ADD(ELEM_Ti, 50, 49.9447921f, 5.18f); ADD(ELEM_Ti, 51, 50.9466160f, 0.0f); ADD(ELEM_Ti, 52, 51.946898f, 0.0f); ADD(ELEM_Ti, 53, 52.94973f, 0.0f); ADD(ELEM_Ti, 54, 53.95087f, 0.0f); ADD(ELEM_Ti, 55, 54.95512f, 0.0f); ADD(ELEM_Ti, 56, 55.95799f, 0.0f); ADD(ELEM_Ti, 57, 56.96290f, 0.0f); ADD(ELEM_Ti, 58, 57.96611f, 0.0f); ADD(ELEM_Ti, 59, 58.97196f, 0.0f); ADD(ELEM_Ti, 60, 59.97564f, 0.0f); ADD(ELEM_Ti, 61, 60.98202f, 0.0f); SET(ELEM_V, NATURAL); ADD(ELEM_V, NATURAL, 50.9415f, 0); ADD(ELEM_V, 40, 40.01109f, 0.0f); ADD(ELEM_V, 41, 40.99974f, 0.0f); ADD(ELEM_V, 42, 41.99123f, 0.0f); ADD(ELEM_V, 43, 42.98065f, 0.0f); ADD(ELEM_V, 44, 43.974400f, 0.0f); ADD(ELEM_V, 45, 44.965782f, 0.0f); ADD(ELEM_V, 46, 45.9601995f, 0.0f); ADD(ELEM_V, 47, 46.9549069f, 0.0f); ADD(ELEM_V, 48, 47.9522545f, 0.0f); ADD(ELEM_V, 49, 48.9485169f, 0.0f); ADD(ELEM_V, 50, 49.9471628f, 0.250f); ADD(ELEM_V, 51, 50.9439637f, 99.750f); ADD(ELEM_V, 52, 51.9447797f, 0.0f); ADD(ELEM_V, 53, 52.944343f, 0.0f); ADD(ELEM_V, 54, 53.946444f, 0.0f); ADD(ELEM_V, 55, 54.94724f, 0.0f); ADD(ELEM_V, 56, 55.95036f, 0.0f); ADD(ELEM_V, 57, 56.95236f, 0.0f); ADD(ELEM_V, 58, 57.95665f, 0.0f); ADD(ELEM_V, 59, 58.95930f, 0.0f); ADD(ELEM_V, 60, 59.96450f, 0.0f); ADD(ELEM_V, 61, 60.96741f, 0.0f); ADD(ELEM_V, 62, 61.97314f, 0.0f); ADD(ELEM_V, 63, 62.97675f, 0.0f); SET(ELEM_Cr, NATURAL); ADD(ELEM_Cr, NATURAL, 51.9961f, 0); ADD(ELEM_Cr, 42, 42.00643f, 0.0f); ADD(ELEM_Cr, 43, 42.997710f, 0.0f); ADD(ELEM_Cr, 44, 43.98547f, 0.0f); ADD(ELEM_Cr, 45, 44.97916f, 0.0f); ADD(ELEM_Cr, 46, 45.968362f, 0.0f); ADD(ELEM_Cr, 47, 46.962907f, 0.0f); ADD(ELEM_Cr, 48, 47.954036f, 0.0f); ADD(ELEM_Cr, 49, 48.9513411f, 0.0f); ADD(ELEM_Cr, 50, 49.9460496f, 4.345f); ADD(ELEM_Cr, 51, 50.9447718f, 0.0f); ADD(ELEM_Cr, 52, 51.9405119f, 83.789f); ADD(ELEM_Cr, 53, 52.9406538f, 9.501f); ADD(ELEM_Cr, 54, 53.9388849f, 2.365f); ADD(ELEM_Cr, 55, 54.9408442f, 0.0f); ADD(ELEM_Cr, 56, 55.940645f, 0.0f); ADD(ELEM_Cr, 57, 56.94375f, 0.0f); ADD(ELEM_Cr, 58, 57.94425f, 0.0f); ADD(ELEM_Cr, 59, 58.94863f, 0.0f); ADD(ELEM_Cr, 60, 59.94973f, 0.0f); ADD(ELEM_Cr, 61, 60.95409f, 0.0f); ADD(ELEM_Cr, 62, 61.95580f, 0.0f); ADD(ELEM_Cr, 63, 62.96186f, 0.0f); ADD(ELEM_Cr, 64, 63.96420f, 0.0f); ADD(ELEM_Cr, 65, 64.97037f, 0.0f); SET(ELEM_Mn, NATURAL); ADD(ELEM_Mn, NATURAL, 54.938049f, 0); ADD(ELEM_Mn, 44, 44.00687f, 0.0f); ADD(ELEM_Mn, 45, 44.99451f, 0.0f); ADD(ELEM_Mn, 46, 45.98672f, 0.0f); ADD(ELEM_Mn, 47, 46.97610f, 0.0f); ADD(ELEM_Mn, 48, 47.968870f, 0.0f); ADD(ELEM_Mn, 49, 48.959623f, 0.0f); ADD(ELEM_Mn, 50, 49.9542440f, 0.0f); ADD(ELEM_Mn, 51, 50.9482155f, 0.0f); ADD(ELEM_Mn, 52, 51.9455701f, 0.0f); ADD(ELEM_Mn, 53, 52.9412947f, 0.0f); ADD(ELEM_Mn, 54, 53.9403632f, 0.0f); ADD(ELEM_Mn, 55, 54.9380496f, 100.0f); ADD(ELEM_Mn, 56, 55.9389094f, 0.0f); ADD(ELEM_Mn, 57, 56.938287f, 0.0f); ADD(ELEM_Mn, 58, 57.939990f, 0.0f); ADD(ELEM_Mn, 59, 58.940450f, 0.0f); ADD(ELEM_Mn, 60, 59.94319f, 0.0f); ADD(ELEM_Mn, 61, 60.94446f, 0.0f); ADD(ELEM_Mn, 62, 61.94797f, 0.0f); ADD(ELEM_Mn, 63, 62.94981f, 0.0f); ADD(ELEM_Mn, 64, 63.95373f, 0.0f); ADD(ELEM_Mn, 65, 64.95610f, 0.0f); ADD(ELEM_Mn, 66, 65.96082f, 0.0f); ADD(ELEM_Mn, 67, 66.96382f, 0.0f); SET(ELEM_Fe, NATURAL); ADD(ELEM_Fe, NATURAL, 55.845f, 0); ADD(ELEM_Fe, 45, 45.01456f, 0.0f); ADD(ELEM_Fe, 46, 46.00081f, 0.0f); ADD(ELEM_Fe, 47, 46.99289f, 0.0f); ADD(ELEM_Fe, 48, 47.98056f, 0.0f); ADD(ELEM_Fe, 49, 48.97361f, 0.0f); ADD(ELEM_Fe, 50, 49.962990f, 0.0f); ADD(ELEM_Fe, 51, 50.956825f, 0.0f); ADD(ELEM_Fe, 52, 51.948117f, 0.0f); ADD(ELEM_Fe, 53, 52.9453123f, 0.0f); ADD(ELEM_Fe, 54, 53.9396148f, 5.845f); ADD(ELEM_Fe, 55, 54.9382980f, 0.0f); ADD(ELEM_Fe, 56, 55.9349421f, 91.754f); ADD(ELEM_Fe, 57, 56.9353987f, 2.119f); ADD(ELEM_Fe, 58, 57.9332805f, 0.282f); ADD(ELEM_Fe, 59, 58.9348805f, 0.0f); ADD(ELEM_Fe, 60, 59.934077f, 0.0f); ADD(ELEM_Fe, 61, 60.936749f, 0.0f); ADD(ELEM_Fe, 62, 61.936770f, 0.0f); ADD(ELEM_Fe, 63, 62.94012f, 0.0f); ADD(ELEM_Fe, 64, 63.94087f, 0.0f); ADD(ELEM_Fe, 65, 64.94494f, 0.0f); ADD(ELEM_Fe, 66, 65.94598f, 0.0f); ADD(ELEM_Fe, 67, 66.95000f, 0.0f); ADD(ELEM_Fe, 68, 67.95251f, 0.0f); ADD(ELEM_Fe, 69, 68.95770f, 0.0f); SET(ELEM_Co, NATURAL); ADD(ELEM_Co, NATURAL, 58.933200f, 0); ADD(ELEM_Co, 48, 48.00176f, 0.0f); ADD(ELEM_Co, 49, 48.98972f, 0.0f); ADD(ELEM_Co, 50, 49.98154f, 0.0f); ADD(ELEM_Co, 51, 50.97072f, 0.0f); ADD(ELEM_Co, 52, 51.963590f, 0.0f); ADD(ELEM_Co, 53, 52.954225f, 0.0f); ADD(ELEM_Co, 54, 53.9484641f, 0.0f); ADD(ELEM_Co, 55, 54.9420031f, 0.0f); ADD(ELEM_Co, 56, 55.9398439f, 0.0f); ADD(ELEM_Co, 57, 56.9362962f, 0.0f); ADD(ELEM_Co, 58, 57.9357576f, 0.0f); ADD(ELEM_Co, 59, 58.9332002f, 100.0f); ADD(ELEM_Co, 60, 59.9338222f, 0.0f); ADD(ELEM_Co, 61, 60.9324794f, 0.0f); ADD(ELEM_Co, 62, 61.934054f, 0.0f); ADD(ELEM_Co, 63, 62.933615f, 0.0f); ADD(ELEM_Co, 64, 63.935814f, 0.0f); ADD(ELEM_Co, 65, 64.936485f, 0.0f); ADD(ELEM_Co, 66, 65.93983f, 0.0f); ADD(ELEM_Co, 67, 66.94061f, 0.0f); ADD(ELEM_Co, 68, 67.94436f, 0.0f); ADD(ELEM_Co, 69, 68.94520f, 0.0f); ADD(ELEM_Co, 70, 69.94981f, 0.0f); ADD(ELEM_Co, 71, 70.95173f, 0.0f); ADD(ELEM_Co, 72, 71.95641f, 0.0f); SET(ELEM_Ni, NATURAL); ADD(ELEM_Ni, NATURAL, 58.6934f, 0); ADD(ELEM_Ni, 50, 49.99593f, 0.0f); ADD(ELEM_Ni, 51, 50.98772f, 0.0f); ADD(ELEM_Ni, 52, 51.975680f, 0.0f); ADD(ELEM_Ni, 53, 52.96846f, 0.0f); ADD(ELEM_Ni, 54, 53.957910f, 0.0f); ADD(ELEM_Ni, 55, 54.951336f, 0.0f); ADD(ELEM_Ni, 56, 55.942136f, 0.0f); ADD(ELEM_Ni, 57, 56.939800f, 0.0f); ADD(ELEM_Ni, 58, 57.9353479f, 68.0769f); ADD(ELEM_Ni, 59, 58.9343516f, 0.0f); ADD(ELEM_Ni, 60, 59.9307906f, 26.2231f); ADD(ELEM_Ni, 61, 60.9310604f, 1.1399f); ADD(ELEM_Ni, 62, 61.9283488f, 3.6345f); ADD(ELEM_Ni, 63, 62.9296729f, 0.0f); ADD(ELEM_Ni, 64, 63.9279696f, 0.9256f); ADD(ELEM_Ni, 65, 64.9300880f, 0.0f); ADD(ELEM_Ni, 66, 65.929115f, 0.0f); ADD(ELEM_Ni, 67, 66.931570f, 0.0f); ADD(ELEM_Ni, 68, 67.931845f, 0.0f); ADD(ELEM_Ni, 69, 68.93518f, 0.0f); ADD(ELEM_Ni, 70, 69.93614f, 0.0f); ADD(ELEM_Ni, 71, 70.94000f, 0.0f); ADD(ELEM_Ni, 72, 71.94130f, 0.0f); ADD(ELEM_Ni, 73, 72.94608f, 0.0f); ADD(ELEM_Ni, 74, 73.94791f, 0.0f); ADD(ELEM_Ni, 75, 74.95297f, 0.0f); ADD(ELEM_Ni, 76, 75.95533f, 0.0f); ADD(ELEM_Ni, 77, 76.96083f, 0.0f); ADD(ELEM_Ni, 78, 77.96380f, 0.0f); SET(ELEM_Cu, NATURAL); ADD(ELEM_Cu, NATURAL, 63.546f, 0); ADD(ELEM_Cu, 52, 51.99718f, 0.0f); ADD(ELEM_Cu, 53, 52.98555f, 0.0f); ADD(ELEM_Cu, 54, 53.97671f, 0.0f); ADD(ELEM_Cu, 55, 54.96605f, 0.0f); ADD(ELEM_Cu, 56, 55.95856f, 0.0f); ADD(ELEM_Cu, 57, 56.949216f, 0.0f); ADD(ELEM_Cu, 58, 57.9445407f, 0.0f); ADD(ELEM_Cu, 59, 58.9395041f, 0.0f); ADD(ELEM_Cu, 60, 59.9373681f, 0.0f); ADD(ELEM_Cu, 61, 60.9334622f, 0.0f); ADD(ELEM_Cu, 62, 61.932587f, 0.0f); ADD(ELEM_Cu, 63, 62.9296011f, 69.17f); ADD(ELEM_Cu, 64, 63.9297679f, 0.0f); ADD(ELEM_Cu, 65, 64.9277937f, 30.83f); ADD(ELEM_Cu, 66, 65.9288730f, 0.0f); ADD(ELEM_Cu, 67, 66.927750f, 0.0f); ADD(ELEM_Cu, 68, 67.929640f, 0.0f); ADD(ELEM_Cu, 69, 68.929425f, 0.0f); ADD(ELEM_Cu, 70, 69.932409f, 0.0f); ADD(ELEM_Cu, 71, 70.932620f, 0.0f); ADD(ELEM_Cu, 72, 71.93552f, 0.0f); ADD(ELEM_Cu, 73, 72.93649f, 0.0f); ADD(ELEM_Cu, 74, 73.94020f, 0.0f); ADD(ELEM_Cu, 75, 74.94170f, 0.0f); ADD(ELEM_Cu, 76, 75.94599f, 0.0f); ADD(ELEM_Cu, 77, 76.94795f, 0.0f); ADD(ELEM_Cu, 78, 77.95281f, 0.0f); ADD(ELEM_Cu, 79, 78.95528f, 0.0f); ADD(ELEM_Cu, 80, 79.96189f, 0.0f); SET(ELEM_Zn, NATURAL); ADD(ELEM_Zn, NATURAL, 65.409f, 0); ADD(ELEM_Zn, 54, 53.99295f, 0.0f); ADD(ELEM_Zn, 55, 54.98398f, 0.0f); ADD(ELEM_Zn, 56, 55.97238f, 0.0f); ADD(ELEM_Zn, 57, 56.96491f, 0.0f); ADD(ELEM_Zn, 58, 57.954600f, 0.0f); ADD(ELEM_Zn, 59, 58.949270f, 0.0f); ADD(ELEM_Zn, 60, 59.941832f, 0.0f); ADD(ELEM_Zn, 61, 60.939514f, 0.0f); ADD(ELEM_Zn, 62, 61.934334f, 0.0f); ADD(ELEM_Zn, 63, 62.9332156f, 0.0f); ADD(ELEM_Zn, 64, 63.9291466f, 48.63f); ADD(ELEM_Zn, 65, 64.9292451f, 0.0f); ADD(ELEM_Zn, 66, 65.9260368f, 27.90f); ADD(ELEM_Zn, 67, 66.9271309f, 4.10f); ADD(ELEM_Zn, 68, 67.9248476f, 18.75f); ADD(ELEM_Zn, 69, 68.9265535f, 0.0f); ADD(ELEM_Zn, 70, 69.925325f, 0.62f); ADD(ELEM_Zn, 71, 70.927727f, 0.0f); ADD(ELEM_Zn, 72, 71.926861f, 0.0f); ADD(ELEM_Zn, 73, 72.929780f, 0.0f); ADD(ELEM_Zn, 74, 73.929460f, 0.0f); ADD(ELEM_Zn, 75, 74.932940f, 0.0f); ADD(ELEM_Zn, 76, 75.93339f, 0.0f); ADD(ELEM_Zn, 77, 76.93709f, 0.0f); ADD(ELEM_Zn, 78, 77.93857f, 0.0f); ADD(ELEM_Zn, 79, 78.94268f, 0.0f); ADD(ELEM_Zn, 80, 79.94441f, 0.0f); ADD(ELEM_Zn, 81, 80.95048f, 0.0f); ADD(ELEM_Zn, 82, 81.95484f, 0.0f); SET(ELEM_Ga, NATURAL); ADD(ELEM_Ga, NATURAL, 69.723f, 0); ADD(ELEM_Ga, 56, 55.99491f, 0.0f); ADD(ELEM_Ga, 57, 56.98293f, 0.0f); ADD(ELEM_Ga, 58, 57.97425f, 0.0f); ADD(ELEM_Ga, 59, 58.96337f, 0.0f); ADD(ELEM_Ga, 60, 59.95706f, 0.0f); ADD(ELEM_Ga, 61, 60.94917f, 0.0f); ADD(ELEM_Ga, 62, 61.944180f, 0.0f); ADD(ELEM_Ga, 63, 62.93914f, 0.0f); ADD(ELEM_Ga, 64, 63.936838f, 0.0f); ADD(ELEM_Ga, 65, 64.9327393f, 0.0f); ADD(ELEM_Ga, 66, 65.931592f, 0.0f); ADD(ELEM_Ga, 67, 66.9282049f, 0.0f); ADD(ELEM_Ga, 68, 67.9279835f, 0.0f); ADD(ELEM_Ga, 69, 68.925581f, 60.108f); ADD(ELEM_Ga, 70, 69.926028f, 0.0f); ADD(ELEM_Ga, 71, 70.9247050f, 39.892f); ADD(ELEM_Ga, 72, 71.9263694f, 0.0f); ADD(ELEM_Ga, 73, 72.925170f, 0.0f); ADD(ELEM_Ga, 74, 73.926940f, 0.0f); ADD(ELEM_Ga, 75, 74.926501f, 0.0f); ADD(ELEM_Ga, 76, 75.92893f, 0.0f); ADD(ELEM_Ga, 77, 76.929280f, 0.0f); ADD(ELEM_Ga, 78, 77.931660f, 0.0f); ADD(ELEM_Ga, 79, 78.93292f, 0.0f); ADD(ELEM_Ga, 80, 79.93659f, 0.0f); ADD(ELEM_Ga, 81, 80.93775f, 0.0f); ADD(ELEM_Ga, 82, 81.94316f, 0.0f); ADD(ELEM_Ga, 83, 82.94687f, 0.0f); ADD(ELEM_Ga, 84, 83.95234f, 0.0f); SET(ELEM_Ge, NATURAL); ADD(ELEM_Ge, NATURAL, 72.64f, 0); ADD(ELEM_Ge, 58, 57.99101f, 0.0f); ADD(ELEM_Ge, 59, 58.98175f, 0.0f); ADD(ELEM_Ge, 60, 59.97019f, 0.0f); ADD(ELEM_Ge, 61, 60.96379f, 0.0f); ADD(ELEM_Ge, 62, 61.95465f, 0.0f); ADD(ELEM_Ge, 63, 62.94964f, 0.0f); ADD(ELEM_Ge, 64, 63.94157f, 0.0f); ADD(ELEM_Ge, 65, 64.93944f, 0.0f); ADD(ELEM_Ge, 66, 65.933850f, 0.0f); ADD(ELEM_Ge, 67, 66.932738f, 0.0f); ADD(ELEM_Ge, 68, 67.928097f, 0.0f); ADD(ELEM_Ge, 69, 68.927972f, 0.0f); ADD(ELEM_Ge, 70, 69.9242504f, 20.84f); ADD(ELEM_Ge, 71, 70.9249540f, 0.0f); ADD(ELEM_Ge, 72, 71.9220762f, 27.54f); ADD(ELEM_Ge, 73, 72.9234594f, 7.73f); ADD(ELEM_Ge, 74, 73.9211782f, 36.28f); ADD(ELEM_Ge, 75, 74.9228595f, 0.0f); ADD(ELEM_Ge, 76, 75.9214027f, 7.61f); ADD(ELEM_Ge, 77, 76.9235485f, 0.0f); ADD(ELEM_Ge, 78, 77.922853f, 0.0f); ADD(ELEM_Ge, 79, 78.92540f, 0.0f); ADD(ELEM_Ge, 80, 79.925445f, 0.0f); ADD(ELEM_Ge, 81, 80.92882f, 0.0f); ADD(ELEM_Ge, 82, 81.92955f, 0.0f); ADD(ELEM_Ge, 83, 82.93451f, 0.0f); ADD(ELEM_Ge, 84, 83.93731f, 0.0f); ADD(ELEM_Ge, 85, 84.94269f, 0.0f); ADD(ELEM_Ge, 86, 85.94627f, 0.0f); SET(ELEM_As, NATURAL); ADD(ELEM_As, NATURAL, 74.92160f, 0); ADD(ELEM_As, 60, 59.99313f, 0.0f); ADD(ELEM_As, 61, 60.98062f, 0.0f); ADD(ELEM_As, 62, 61.97320f, 0.0f); ADD(ELEM_As, 63, 62.96369f, 0.0f); ADD(ELEM_As, 64, 63.95757f, 0.0f); ADD(ELEM_As, 65, 64.94948f, 0.0f); ADD(ELEM_As, 66, 65.94437f, 0.0f); ADD(ELEM_As, 67, 66.93919f, 0.0f); ADD(ELEM_As, 68, 67.93679f, 0.0f); ADD(ELEM_As, 69, 68.932280f, 0.0f); ADD(ELEM_As, 70, 69.930930f, 0.0f); ADD(ELEM_As, 71, 70.927115f, 0.0f); ADD(ELEM_As, 72, 71.926753f, 0.0f); ADD(ELEM_As, 73, 72.923825f, 0.0f); ADD(ELEM_As, 74, 73.9239291f, 0.0f); ADD(ELEM_As, 75, 74.9215964f, 100.0f); ADD(ELEM_As, 76, 75.9223939f, 0.0f); ADD(ELEM_As, 77, 76.9206477f, 0.0f); ADD(ELEM_As, 78, 77.921829f, 0.0f); ADD(ELEM_As, 79, 78.920948f, 0.0f); ADD(ELEM_As, 80, 79.922578f, 0.0f); ADD(ELEM_As, 81, 80.922133f, 0.0f); ADD(ELEM_As, 82, 81.92450f, 0.0f); ADD(ELEM_As, 83, 82.92498f, 0.0f); ADD(ELEM_As, 84, 83.92906f, 0.0f); ADD(ELEM_As, 85, 84.93181f, 0.0f); ADD(ELEM_As, 86, 85.93623f, 0.0f); ADD(ELEM_As, 87, 86.93958f, 0.0f); ADD(ELEM_As, 88, 87.94456f, 0.0f); ADD(ELEM_As, 89, 88.94923f, 0.0f); SET(ELEM_Se, NATURAL); ADD(ELEM_Se, NATURAL, 78.96f, 0); ADD(ELEM_Se, 65, 64.96466f, 0.0f); ADD(ELEM_Se, 66, 65.95521f, 0.0f); ADD(ELEM_Se, 67, 66.95009f, 0.0f); ADD(ELEM_Se, 68, 67.94187f, 0.0f); ADD(ELEM_Se, 69, 68.939560f, 0.0f); ADD(ELEM_Se, 70, 69.93350f, 0.0f); ADD(ELEM_Se, 71, 70.93227f, 0.0f); ADD(ELEM_Se, 72, 71.927112f, 0.0f); ADD(ELEM_Se, 73, 72.926767f, 0.0f); ADD(ELEM_Se, 74, 73.9224766f, 0.89f); ADD(ELEM_Se, 75, 74.9225236f, 0.0f); ADD(ELEM_Se, 76, 75.9192141f, 9.37f); ADD(ELEM_Se, 77, 76.9199146f, 7.63f); ADD(ELEM_Se, 78, 77.9173095f, 23.77f); ADD(ELEM_Se, 79, 78.9184998f, 0.0f); ADD(ELEM_Se, 80, 79.9165218f, 49.61f); ADD(ELEM_Se, 81, 80.9179929f, 0.0f); ADD(ELEM_Se, 82, 81.9167000f, 8.73f); ADD(ELEM_Se, 83, 82.919119f, 0.0f); ADD(ELEM_Se, 84, 83.918465f, 0.0f); ADD(ELEM_Se, 85, 84.922240f, 0.0f); ADD(ELEM_Se, 86, 85.924271f, 0.0f); ADD(ELEM_Se, 87, 86.928520f, 0.0f); ADD(ELEM_Se, 88, 87.931420f, 0.0f); ADD(ELEM_Se, 89, 88.93602f, 0.0f); ADD(ELEM_Se, 90, 89.93942f, 0.0f); ADD(ELEM_Se, 91, 90.94537f, 0.0f); ADD(ELEM_Se, 92, 91.94933f, 0.0f); SET(ELEM_Br, NATURAL); ADD(ELEM_Br, NATURAL, 79.904f, 0); ADD(ELEM_Br, 67, 66.96479f, 0.0f); ADD(ELEM_Br, 68, 67.95825f, 0.0f); ADD(ELEM_Br, 69, 68.95018f, 0.0f); ADD(ELEM_Br, 70, 69.94462f, 0.0f); ADD(ELEM_Br, 71, 70.93925f, 0.0f); ADD(ELEM_Br, 72, 71.93650f, 0.0f); ADD(ELEM_Br, 73, 72.93179f, 0.0f); ADD(ELEM_Br, 74, 73.929891f, 0.0f); ADD(ELEM_Br, 75, 74.925776f, 0.0f); ADD(ELEM_Br, 76, 75.924542f, 0.0f); ADD(ELEM_Br, 77, 76.921380f, 0.0f); ADD(ELEM_Br, 78, 77.921146f, 0.0f); ADD(ELEM_Br, 79, 78.9183376f, 50.69f); ADD(ELEM_Br, 80, 79.9185300f, 0.0f); ADD(ELEM_Br, 81, 80.916291f, 49.31f); ADD(ELEM_Br, 82, 81.916805f, 0.0f); ADD(ELEM_Br, 83, 82.915180f, 0.0f); ADD(ELEM_Br, 84, 83.916504f, 0.0f); ADD(ELEM_Br, 85, 84.915608f, 0.0f); ADD(ELEM_Br, 86, 85.918797f, 0.0f); ADD(ELEM_Br, 87, 86.920711f, 0.0f); ADD(ELEM_Br, 88, 87.924070f, 0.0f); ADD(ELEM_Br, 89, 88.926390f, 0.0f); ADD(ELEM_Br, 90, 89.930630f, 0.0f); ADD(ELEM_Br, 91, 90.933970f, 0.0f); ADD(ELEM_Br, 92, 91.939260f, 0.0f); ADD(ELEM_Br, 93, 92.94310f, 0.0f); ADD(ELEM_Br, 94, 93.94868f, 0.0f); SET(ELEM_Kr, NATURAL); ADD(ELEM_Kr, NATURAL, 83.798f, 0); ADD(ELEM_Kr, 69, 68.96532f, 0.0f); ADD(ELEM_Kr, 70, 69.95601f, 0.0f); ADD(ELEM_Kr, 71, 70.95051f, 0.0f); ADD(ELEM_Kr, 72, 71.94191f, 0.0f); ADD(ELEM_Kr, 73, 72.93893f, 0.0f); ADD(ELEM_Kr, 74, 73.933260f, 0.0f); ADD(ELEM_Kr, 75, 74.931034f, 0.0f); ADD(ELEM_Kr, 76, 75.925948f, 0.0f); ADD(ELEM_Kr, 77, 76.924668f, 0.0f); ADD(ELEM_Kr, 78, 77.920386f, 0.35f); ADD(ELEM_Kr, 79, 78.920083f, 0.0f); ADD(ELEM_Kr, 80, 79.916378f, 2.28f); ADD(ELEM_Kr, 81, 80.916592f, 0.0f); ADD(ELEM_Kr, 82, 81.9134846f, 11.58f); ADD(ELEM_Kr, 83, 82.914136f, 11.49f); ADD(ELEM_Kr, 84, 83.911507f, 57.00f); ADD(ELEM_Kr, 85, 84.912527f, 0.0f); ADD(ELEM_Kr, 86, 85.9106103f, 17.30f); ADD(ELEM_Kr, 87, 86.9133543f, 0.0f); ADD(ELEM_Kr, 88, 87.914447f, 0.0f); ADD(ELEM_Kr, 89, 88.917630f, 0.0f); ADD(ELEM_Kr, 90, 89.919524f, 0.0f); ADD(ELEM_Kr, 91, 90.923440f, 0.0f); ADD(ELEM_Kr, 92, 91.926153f, 0.0f); ADD(ELEM_Kr, 93, 92.93127f, 0.0f); ADD(ELEM_Kr, 94, 93.93436f, 0.0f); ADD(ELEM_Kr, 95, 94.93984f, 0.0f); ADD(ELEM_Kr, 96, 95.94307f, 0.0f); ADD(ELEM_Kr, 97, 96.94856f, 0.0f); SET(ELEM_Rb, NATURAL); ADD(ELEM_Rb, NATURAL, 85.4678f, 0); ADD(ELEM_Rb, 71, 70.96532f, 0.0f); ADD(ELEM_Rb, 72, 71.95908f, 0.0f); ADD(ELEM_Rb, 73, 72.95037f, 0.0f); ADD(ELEM_Rb, 74, 73.94447f, 0.0f); ADD(ELEM_Rb, 75, 74.938569f, 0.0f); ADD(ELEM_Rb, 76, 75.935071f, 0.0f); ADD(ELEM_Rb, 77, 76.930407f, 0.0f); ADD(ELEM_Rb, 78, 77.928141f, 0.0f); ADD(ELEM_Rb, 79, 78.923997f, 0.0f); ADD(ELEM_Rb, 80, 79.922519f, 0.0f); ADD(ELEM_Rb, 81, 80.918994f, 0.0f); ADD(ELEM_Rb, 82, 81.918208f, 0.0f); ADD(ELEM_Rb, 83, 82.915112f, 0.0f); ADD(ELEM_Rb, 84, 83.914385f, 0.0f); ADD(ELEM_Rb, 85, 84.9117893f, 72.17f); ADD(ELEM_Rb, 86, 85.9111671f, 0.0f); ADD(ELEM_Rb, 87, 86.9091835f, 27.83f); ADD(ELEM_Rb, 88, 87.911319f, 0.0f); ADD(ELEM_Rb, 89, 88.912280f, 0.0f); ADD(ELEM_Rb, 90, 89.914809f, 0.0f); ADD(ELEM_Rb, 91, 90.916534f, 0.0f); ADD(ELEM_Rb, 92, 91.919725f, 0.0f); ADD(ELEM_Rb, 93, 92.922033f, 0.0f); ADD(ELEM_Rb, 94, 93.926407f, 0.0f); ADD(ELEM_Rb, 95, 94.929319f, 0.0f); ADD(ELEM_Rb, 96, 95.934284f, 0.0f); ADD(ELEM_Rb, 97, 96.937340f, 0.0f); ADD(ELEM_Rb, 98, 97.941700f, 0.0f); ADD(ELEM_Rb, 99, 98.94542f, 0.0f); ADD(ELEM_Rb, 100, 99.94987f, 0.0f); ADD(ELEM_Rb, 101, 100.95320f, 0.0f); ADD(ELEM_Rb, 102, 101.95921f, 0.0f); SET(ELEM_Sr, NATURAL); ADD(ELEM_Sr, NATURAL, 87.62f, 0); ADD(ELEM_Sr, 73, 72.96597f, 0.0f); ADD(ELEM_Sr, 74, 73.95631f, 0.0f); ADD(ELEM_Sr, 75, 74.94992f, 0.0f); ADD(ELEM_Sr, 76, 75.94161f, 0.0f); ADD(ELEM_Sr, 77, 76.93776f, 0.0f); ADD(ELEM_Sr, 78, 77.932179f, 0.0f); ADD(ELEM_Sr, 79, 78.929707f, 0.0f); ADD(ELEM_Sr, 80, 79.924525f, 0.0f); ADD(ELEM_Sr, 81, 80.923213f, 0.0f); ADD(ELEM_Sr, 82, 81.918401f, 0.0f); ADD(ELEM_Sr, 83, 82.917555f, 0.0f); ADD(ELEM_Sr, 84, 83.913425f, 0.56f); ADD(ELEM_Sr, 85, 84.912933f, 0.0f); ADD(ELEM_Sr, 86, 85.9092624f, 9.86f); ADD(ELEM_Sr, 87, 86.9088793f, 7.00f); ADD(ELEM_Sr, 88, 87.9056143f, 82.58f); ADD(ELEM_Sr, 89, 88.9074529f, 0.0f); ADD(ELEM_Sr, 90, 89.9077376f, 0.0f); ADD(ELEM_Sr, 91, 90.910210f, 0.0f); ADD(ELEM_Sr, 92, 91.911030f, 0.0f); ADD(ELEM_Sr, 93, 92.914022f, 0.0f); ADD(ELEM_Sr, 94, 93.915360f, 0.0f); ADD(ELEM_Sr, 95, 94.919358f, 0.0f); ADD(ELEM_Sr, 96, 95.921680f, 0.0f); ADD(ELEM_Sr, 97, 96.926149f, 0.0f); ADD(ELEM_Sr, 98, 97.928471f, 0.0f); ADD(ELEM_Sr, 99, 98.93332f, 0.0f); ADD(ELEM_Sr, 100, 99.93535f, 0.0f); ADD(ELEM_Sr, 101, 100.94052f, 0.0f); ADD(ELEM_Sr, 102, 101.94302f, 0.0f); ADD(ELEM_Sr, 103, 102.94895f, 0.0f); ADD(ELEM_Sr, 104, 103.95233f, 0.0f); SET(ELEM_Y, NATURAL); ADD(ELEM_Y, NATURAL, 88.90585f, 0); ADD(ELEM_Y, 77, 76.94962f, 0.0f); ADD(ELEM_Y, 78, 77.94350f, 0.0f); ADD(ELEM_Y, 79, 78.93735f, 0.0f); ADD(ELEM_Y, 80, 79.93434f, 0.0f); ADD(ELEM_Y, 81, 80.929130f, 0.0f); ADD(ELEM_Y, 82, 81.92679f, 0.0f); ADD(ELEM_Y, 83, 82.922350f, 0.0f); ADD(ELEM_Y, 84, 83.92039f, 0.0f); ADD(ELEM_Y, 85, 84.916427f, 0.0f); ADD(ELEM_Y, 86, 85.914888f, 0.0f); ADD(ELEM_Y, 87, 86.9108778f, 0.0f); ADD(ELEM_Y, 88, 87.9095034f, 0.0f); ADD(ELEM_Y, 89, 88.9058479f, 100.0f); ADD(ELEM_Y, 90, 89.9071514f, 0.0f); ADD(ELEM_Y, 91, 90.907303f, 0.0f); ADD(ELEM_Y, 92, 91.908947f, 0.0f); ADD(ELEM_Y, 93, 92.909582f, 0.0f); ADD(ELEM_Y, 94, 93.911594f, 0.0f); ADD(ELEM_Y, 95, 94.912824f, 0.0f); ADD(ELEM_Y, 96, 95.915898f, 0.0f); ADD(ELEM_Y, 97, 96.918131f, 0.0f); ADD(ELEM_Y, 98, 97.922220f, 0.0f); ADD(ELEM_Y, 99, 98.924635f, 0.0f); ADD(ELEM_Y, 100, 99.927760f, 0.0f); ADD(ELEM_Y, 101, 100.93031f, 0.0f); ADD(ELEM_Y, 102, 101.933560f, 0.0f); ADD(ELEM_Y, 103, 102.93694f, 0.0f); ADD(ELEM_Y, 104, 103.94145f, 0.0f); ADD(ELEM_Y, 105, 104.94509f, 0.0f); ADD(ELEM_Y, 106, 105.95022f, 0.0f); SET(ELEM_Zr, NATURAL); ADD(ELEM_Zr, NATURAL, 91.224f, 0); ADD(ELEM_Zr, 79, 78.94916f, 0.0f); ADD(ELEM_Zr, 80, 79.94055f, 0.0f); ADD(ELEM_Zr, 81, 80.93682f, 0.0f); ADD(ELEM_Zr, 82, 81.93109f, 0.0f); ADD(ELEM_Zr, 83, 82.92865f, 0.0f); ADD(ELEM_Zr, 84, 83.92325f, 0.0f); ADD(ELEM_Zr, 85, 84.92147f, 0.0f); ADD(ELEM_Zr, 86, 85.916470f, 0.0f); ADD(ELEM_Zr, 87, 86.914817f, 0.0f); ADD(ELEM_Zr, 88, 87.910226f, 0.0f); ADD(ELEM_Zr, 89, 88.908889f, 0.0f); ADD(ELEM_Zr, 90, 89.9047037f, 51.45f); ADD(ELEM_Zr, 91, 90.9056450f, 11.22f); ADD(ELEM_Zr, 92, 91.9050401f, 17.15f); ADD(ELEM_Zr, 93, 92.9064756f, 0.0f); ADD(ELEM_Zr, 94, 93.9063158f, 17.38f); ADD(ELEM_Zr, 95, 94.9080427f, 0.0f); ADD(ELEM_Zr, 96, 95.908276f, 2.80f); ADD(ELEM_Zr, 97, 96.910951f, 0.0f); ADD(ELEM_Zr, 98, 97.912746f, 0.0f); ADD(ELEM_Zr, 99, 98.916511f, 0.0f); ADD(ELEM_Zr, 100, 99.917760f, 0.0f); ADD(ELEM_Zr, 101, 100.921140f, 0.0f); ADD(ELEM_Zr, 102, 101.922980f, 0.0f); ADD(ELEM_Zr, 103, 102.92660f, 0.0f); ADD(ELEM_Zr, 104, 103.92878f, 0.0f); ADD(ELEM_Zr, 105, 104.93305f, 0.0f); ADD(ELEM_Zr, 106, 105.93591f, 0.0f); ADD(ELEM_Zr, 107, 106.94086f, 0.0f); ADD(ELEM_Zr, 108, 107.94428f, 0.0f); SET(ELEM_Nb, NATURAL); ADD(ELEM_Nb, NATURAL, 92.90638f, 0); ADD(ELEM_Nb, 81, 80.94905f, 0.0f); ADD(ELEM_Nb, 82, 81.94313f, 0.0f); ADD(ELEM_Nb, 83, 82.93670f, 0.0f); ADD(ELEM_Nb, 84, 83.93357f, 0.0f); ADD(ELEM_Nb, 85, 84.92791f, 0.0f); ADD(ELEM_Nb, 86, 85.925040f, 0.0f); ADD(ELEM_Nb, 87, 86.920360f, 0.0f); ADD(ELEM_Nb, 88, 87.91796f, 0.0f); ADD(ELEM_Nb, 89, 88.913500f, 0.0f); ADD(ELEM_Nb, 90, 89.911264f, 0.0f); ADD(ELEM_Nb, 91, 90.906991f, 0.0f); ADD(ELEM_Nb, 92, 91.9071932f, 0.0f); ADD(ELEM_Nb, 93, 92.9063775f, 100.0f); ADD(ELEM_Nb, 94, 93.9072835f, 0.0f); ADD(ELEM_Nb, 95, 94.9068352f, 0.0f); ADD(ELEM_Nb, 96, 95.908100f, 0.0f); ADD(ELEM_Nb, 97, 96.9080971f, 0.0f); ADD(ELEM_Nb, 98, 97.910331f, 0.0f); ADD(ELEM_Nb, 99, 98.911618f, 0.0f); ADD(ELEM_Nb, 100, 99.914181f, 0.0f); ADD(ELEM_Nb, 101, 100.915252f, 0.0f); ADD(ELEM_Nb, 102, 101.918040f, 0.0f); ADD(ELEM_Nb, 103, 102.919140f, 0.0f); ADD(ELEM_Nb, 104, 103.92246f, 0.0f); ADD(ELEM_Nb, 105, 104.92393f, 0.0f); ADD(ELEM_Nb, 106, 105.92819f, 0.0f); ADD(ELEM_Nb, 107, 106.93031f, 0.0f); ADD(ELEM_Nb, 108, 107.93501f, 0.0f); ADD(ELEM_Nb, 109, 108.93763f, 0.0f); ADD(ELEM_Nb, 110, 109.94268f, 0.0f); SET(ELEM_Mo, NATURAL); ADD(ELEM_Mo, NATURAL, 95.94f, 0); ADD(ELEM_Mo, 83, 82.94874f, 0.0f); ADD(ELEM_Mo, 84, 83.94009f, 0.0f); ADD(ELEM_Mo, 85, 84.93659f, 0.0f); ADD(ELEM_Mo, 86, 85.93070f, 0.0f); ADD(ELEM_Mo, 87, 86.92733f, 0.0f); ADD(ELEM_Mo, 88, 87.921953f, 0.0f); ADD(ELEM_Mo, 89, 88.919481f, 0.0f); ADD(ELEM_Mo, 90, 89.913936f, 0.0f); ADD(ELEM_Mo, 91, 90.911751f, 0.0f); ADD(ELEM_Mo, 92, 91.906810f, 14.84f); ADD(ELEM_Mo, 93, 92.906812f, 0.0f); ADD(ELEM_Mo, 94, 93.9050876f, 9.25f); ADD(ELEM_Mo, 95, 94.9058415f, 15.92f); ADD(ELEM_Mo, 96, 95.9046789f, 16.68f); ADD(ELEM_Mo, 97, 96.9060210f, 9.55f); ADD(ELEM_Mo, 98, 97.9054078f, 24.13f); ADD(ELEM_Mo, 99, 98.9077116f, 0.0f); ADD(ELEM_Mo, 100, 99.907477f, 9.63f); ADD(ELEM_Mo, 101, 100.910347f, 0.0f); ADD(ELEM_Mo, 102, 101.910297f, 0.0f); ADD(ELEM_Mo, 103, 102.913200f, 0.0f); ADD(ELEM_Mo, 104, 103.913760f, 0.0f); ADD(ELEM_Mo, 105, 104.916970f, 0.0f); ADD(ELEM_Mo, 106, 105.918134f, 0.0f); ADD(ELEM_Mo, 107, 106.92169f, 0.0f); ADD(ELEM_Mo, 108, 107.92358f, 0.0f); ADD(ELEM_Mo, 109, 108.92781f, 0.0f); ADD(ELEM_Mo, 110, 109.92973f, 0.0f); ADD(ELEM_Mo, 111, 110.93451f, 0.0f); ADD(ELEM_Mo, 112, 111.93684f, 0.0f); ADD(ELEM_Mo, 113, 112.94203f, 0.0f); SET(ELEM_Tc, 98); ADD(ELEM_Tc, 85, 84.94894f, 0.0f); ADD(ELEM_Tc, 86, 85.94288f, 0.0f); ADD(ELEM_Tc, 87, 86.93653f, 0.0f); ADD(ELEM_Tc, 88, 87.93283f, 0.0f); ADD(ELEM_Tc, 89, 88.92754f, 0.0f); ADD(ELEM_Tc, 90, 89.92356f, 0.0f); ADD(ELEM_Tc, 91, 90.91843f, 0.0f); ADD(ELEM_Tc, 92, 91.915260f, 0.0f); ADD(ELEM_Tc, 93, 92.910248f, 0.0f); ADD(ELEM_Tc, 94, 93.909656f, 0.0f); ADD(ELEM_Tc, 95, 94.907656f, 0.0f); ADD(ELEM_Tc, 96, 95.907871f, 0.0f); ADD(ELEM_Tc, 97, 96.906365f, 0.0f); ADD(ELEM_Tc, 98, 97.907216f, 0.0f); ADD(ELEM_Tc, 99, 98.9062546f, 0.0f); ADD(ELEM_Tc, 100, 99.9076576f, 0.0f); ADD(ELEM_Tc, 101, 100.907314f, 0.0f); ADD(ELEM_Tc, 102, 101.909213f, 0.0f); ADD(ELEM_Tc, 103, 102.909179f, 0.0f); ADD(ELEM_Tc, 104, 103.911440f, 0.0f); ADD(ELEM_Tc, 105, 104.911660f, 0.0f); ADD(ELEM_Tc, 106, 105.914355f, 0.0f); ADD(ELEM_Tc, 107, 106.91508f, 0.0f); ADD(ELEM_Tc, 108, 107.91848f, 0.0f); ADD(ELEM_Tc, 109, 108.91963f, 0.0f); ADD(ELEM_Tc, 110, 109.92339f, 0.0f); ADD(ELEM_Tc, 111, 110.92505f, 0.0f); ADD(ELEM_Tc, 112, 111.92924f, 0.0f); ADD(ELEM_Tc, 113, 112.93133f, 0.0f); ADD(ELEM_Tc, 114, 113.93588f, 0.0f); ADD(ELEM_Tc, 115, 114.93828f, 0.0f); SET(ELEM_Ru, NATURAL); ADD(ELEM_Ru, NATURAL, 101.07f, 0); ADD(ELEM_Ru, 87, 86.94918f, 0.0f); ADD(ELEM_Ru, 88, 87.94042f, 0.0f); ADD(ELEM_Ru, 89, 88.93611f, 0.0f); ADD(ELEM_Ru, 90, 89.92978f, 0.0f); ADD(ELEM_Ru, 91, 90.92638f, 0.0f); ADD(ELEM_Ru, 92, 91.92012f, 0.0f); ADD(ELEM_Ru, 93, 92.917050f, 0.0f); ADD(ELEM_Ru, 94, 93.911360f, 0.0f); ADD(ELEM_Ru, 95, 94.910413f, 0.0f); ADD(ELEM_Ru, 96, 95.907598f, 5.54f); ADD(ELEM_Ru, 97, 96.907555f, 0.0f); ADD(ELEM_Ru, 98, 97.905287f, 1.87f); ADD(ELEM_Ru, 99, 98.9059393f, 12.76f); ADD(ELEM_Ru, 100, 99.9042197f, 12.60f); ADD(ELEM_Ru, 101, 100.9055822f, 17.06f); ADD(ELEM_Ru, 102, 101.9043495f, 31.55f); ADD(ELEM_Ru, 103, 102.9063237f, 0.0f); ADD(ELEM_Ru, 104, 103.905430f, 18.62f); ADD(ELEM_Ru, 105, 104.907750f, 0.0f); ADD(ELEM_Ru, 106, 105.907327f, 0.0f); ADD(ELEM_Ru, 107, 106.90991f, 0.0f); ADD(ELEM_Ru, 108, 107.91019f, 0.0f); ADD(ELEM_Ru, 109, 108.913200f, 0.0f); ADD(ELEM_Ru, 110, 109.91397f, 0.0f); ADD(ELEM_Ru, 111, 110.91756f, 0.0f); ADD(ELEM_Ru, 112, 111.91855f, 0.0f); ADD(ELEM_Ru, 113, 112.92254f, 0.0f); ADD(ELEM_Ru, 114, 113.92400f, 0.0f); ADD(ELEM_Ru, 115, 114.92831f, 0.0f); ADD(ELEM_Ru, 116, 115.93016f, 0.0f); ADD(ELEM_Ru, 117, 116.93479f, 0.0f); ADD(ELEM_Ru, 118, 117.93703f, 0.0f); SET(ELEM_Rh, NATURAL); ADD(ELEM_Rh, NATURAL, 102.90550f, 0); ADD(ELEM_Rh, 89, 88.94938f, 0.0f); ADD(ELEM_Rh, 90, 89.94287f, 0.0f); ADD(ELEM_Rh, 91, 90.93655f, 0.0f); ADD(ELEM_Rh, 92, 91.93198f, 0.0f); ADD(ELEM_Rh, 93, 92.92574f, 0.0f); ADD(ELEM_Rh, 94, 93.92170f, 0.0f); ADD(ELEM_Rh, 95, 94.91590f, 0.0f); ADD(ELEM_Rh, 96, 95.914518f, 0.0f); ADD(ELEM_Rh, 97, 96.911340f, 0.0f); ADD(ELEM_Rh, 98, 97.910716f, 0.0f); ADD(ELEM_Rh, 99, 98.908132f, 0.0f); ADD(ELEM_Rh, 100, 99.908117f, 0.0f); ADD(ELEM_Rh, 101, 100.906164f, 0.0f); ADD(ELEM_Rh, 102, 101.906843f, 0.0f); ADD(ELEM_Rh, 103, 102.905504f, 100.0f); ADD(ELEM_Rh, 104, 103.906655f, 0.0f); ADD(ELEM_Rh, 105, 104.905692f, 0.0f); ADD(ELEM_Rh, 106, 105.907285f, 0.0f); ADD(ELEM_Rh, 107, 106.906751f, 0.0f); ADD(ELEM_Rh, 108, 107.90873f, 0.0f); ADD(ELEM_Rh, 109, 108.908736f, 0.0f); ADD(ELEM_Rh, 110, 109.91095f, 0.0f); ADD(ELEM_Rh, 111, 110.91166f, 0.0f); ADD(ELEM_Rh, 112, 111.91461f, 0.0f); ADD(ELEM_Rh, 113, 112.91542f, 0.0f); ADD(ELEM_Rh, 114, 113.91885f, 0.0f); ADD(ELEM_Rh, 115, 114.92012f, 0.0f); ADD(ELEM_Rh, 116, 115.92371f, 0.0f); ADD(ELEM_Rh, 117, 116.92535f, 0.0f); ADD(ELEM_Rh, 118, 117.92943f, 0.0f); ADD(ELEM_Rh, 119, 118.93136f, 0.0f); ADD(ELEM_Rh, 120, 119.93578f, 0.0f); ADD(ELEM_Rh, 121, 120.93808f, 0.0f); SET(ELEM_Pd, NATURAL); ADD(ELEM_Pd, NATURAL, 106.42f, 0); ADD(ELEM_Pd, 91, 90.94948f, 0.0f); ADD(ELEM_Pd, 92, 91.94042f, 0.0f); ADD(ELEM_Pd, 93, 92.93591f, 0.0f); ADD(ELEM_Pd, 94, 93.92877f, 0.0f); ADD(ELEM_Pd, 95, 94.92469f, 0.0f); ADD(ELEM_Pd, 96, 95.91822f, 0.0f); ADD(ELEM_Pd, 97, 96.91648f, 0.0f); ADD(ELEM_Pd, 98, 97.912721f, 0.0f); ADD(ELEM_Pd, 99, 98.911768f, 0.0f); ADD(ELEM_Pd, 100, 99.908505f, 0.0f); ADD(ELEM_Pd, 101, 100.908289f, 0.0f); ADD(ELEM_Pd, 102, 101.905608f, 1.02f); ADD(ELEM_Pd, 103, 102.906087f, 0.0f); ADD(ELEM_Pd, 104, 103.904035f, 11.14f); ADD(ELEM_Pd, 105, 104.905084f, 22.33f); ADD(ELEM_Pd, 106, 105.903483f, 27.33f); ADD(ELEM_Pd, 107, 106.905128f, 0.0f); ADD(ELEM_Pd, 108, 107.903894f, 26.46f); ADD(ELEM_Pd, 109, 108.905954f, 0.0f); ADD(ELEM_Pd, 110, 109.905152f, 11.72f); ADD(ELEM_Pd, 111, 110.907640f, 0.0f); ADD(ELEM_Pd, 112, 111.907313f, 0.0f); ADD(ELEM_Pd, 113, 112.910150f, 0.0f); ADD(ELEM_Pd, 114, 113.910365f, 0.0f); ADD(ELEM_Pd, 115, 114.913680f, 0.0f); ADD(ELEM_Pd, 116, 115.914160f, 0.0f); ADD(ELEM_Pd, 117, 116.91784f, 0.0f); ADD(ELEM_Pd, 118, 117.91898f, 0.0f); ADD(ELEM_Pd, 119, 118.92268f, 0.0f); ADD(ELEM_Pd, 120, 119.92403f, 0.0f); ADD(ELEM_Pd, 121, 120.92818f, 0.0f); ADD(ELEM_Pd, 122, 121.92980f, 0.0f); ADD(ELEM_Pd, 123, 122.93426f, 0.0f); SET(ELEM_Ag, NATURAL); ADD(ELEM_Ag, NATURAL, 107.8682f, 0); ADD(ELEM_Ag, 94, 93.94278f, 0.0f); ADD(ELEM_Ag, 95, 94.93548f, 0.0f); ADD(ELEM_Ag, 96, 95.93068f, 0.0f); ADD(ELEM_Ag, 97, 96.92400f, 0.0f); ADD(ELEM_Ag, 98, 97.92176f, 0.0f); ADD(ELEM_Ag, 99, 98.91760f, 0.0f); ADD(ELEM_Ag, 100, 99.916070f, 0.0f); ADD(ELEM_Ag, 101, 100.91280f, 0.0f); ADD(ELEM_Ag, 102, 101.912000f, 0.0f); ADD(ELEM_Ag, 103, 102.908972f, 0.0f); ADD(ELEM_Ag, 104, 103.908628f, 0.0f); ADD(ELEM_Ag, 105, 104.906528f, 0.0f); ADD(ELEM_Ag, 106, 105.906666f, 0.0f); ADD(ELEM_Ag, 107, 106.905093f, 51.839f); ADD(ELEM_Ag, 108, 107.905954f, 0.0f); ADD(ELEM_Ag, 109, 108.904756f, 48.161f); ADD(ELEM_Ag, 110, 109.906110f, 0.0f); ADD(ELEM_Ag, 111, 110.905295f, 0.0f); ADD(ELEM_Ag, 112, 111.907004f, 0.0f); ADD(ELEM_Ag, 113, 112.906566f, 0.0f); ADD(ELEM_Ag, 114, 113.908808f, 0.0f); ADD(ELEM_Ag, 115, 114.908760f, 0.0f); ADD(ELEM_Ag, 116, 115.911360f, 0.0f); ADD(ELEM_Ag, 117, 116.911680f, 0.0f); ADD(ELEM_Ag, 118, 117.914580f, 0.0f); ADD(ELEM_Ag, 119, 118.91567f, 0.0f); ADD(ELEM_Ag, 120, 119.918790f, 0.0f); ADD(ELEM_Ag, 121, 120.91985f, 0.0f); ADD(ELEM_Ag, 122, 121.92332f, 0.0f); ADD(ELEM_Ag, 123, 122.92490f, 0.0f); ADD(ELEM_Ag, 124, 123.92853f, 0.0f); ADD(ELEM_Ag, 125, 124.93054f, 0.0f); ADD(ELEM_Ag, 126, 125.93450f, 0.0f); ADD(ELEM_Ag, 127, 126.93688f, 0.0f); SET(ELEM_Cd, NATURAL); ADD(ELEM_Cd, NATURAL, 112.411f, 0); ADD(ELEM_Cd, 96, 95.93977f, 0.0f); ADD(ELEM_Cd, 97, 96.93494f, 0.0f); ADD(ELEM_Cd, 98, 97.92758f, 0.0f); ADD(ELEM_Cd, 99, 98.92501f, 0.0f); ADD(ELEM_Cd, 100, 99.92023f, 0.0f); ADD(ELEM_Cd, 101, 100.91868f, 0.0f); ADD(ELEM_Cd, 102, 101.914780f, 0.0f); ADD(ELEM_Cd, 103, 102.913419f, 0.0f); ADD(ELEM_Cd, 104, 103.909848f, 0.0f); ADD(ELEM_Cd, 105, 104.909468f, 0.0f); ADD(ELEM_Cd, 106, 105.906458f, 1.25f); ADD(ELEM_Cd, 107, 106.906614f, 0.0f); ADD(ELEM_Cd, 108, 107.904183f, 0.89f); ADD(ELEM_Cd, 109, 108.904986f, 0.0f); ADD(ELEM_Cd, 110, 109.903006f, 12.49f); ADD(ELEM_Cd, 111, 110.904182f, 12.80f); ADD(ELEM_Cd, 112, 111.9027572f, 24.13f); ADD(ELEM_Cd, 113, 112.9044009f, 12.22f); ADD(ELEM_Cd, 114, 113.9033581f, 28.73f); ADD(ELEM_Cd, 115, 114.905431f, 0.0f); ADD(ELEM_Cd, 116, 115.904755f, 7.49f); ADD(ELEM_Cd, 117, 116.907218f, 0.0f); ADD(ELEM_Cd, 118, 117.906914f, 0.0f); ADD(ELEM_Cd, 119, 118.909920f, 0.0f); ADD(ELEM_Cd, 120, 119.909851f, 0.0f); ADD(ELEM_Cd, 121, 120.912980f, 0.0f); ADD(ELEM_Cd, 122, 121.91350f, 0.0f); ADD(ELEM_Cd, 123, 122.917000f, 0.0f); ADD(ELEM_Cd, 124, 123.917650f, 0.0f); ADD(ELEM_Cd, 125, 124.921250f, 0.0f); ADD(ELEM_Cd, 126, 125.922350f, 0.0f); ADD(ELEM_Cd, 127, 126.926430f, 0.0f); ADD(ELEM_Cd, 128, 127.92776f, 0.0f); ADD(ELEM_Cd, 129, 128.93226f, 0.0f); ADD(ELEM_Cd, 130, 129.93398f, 0.0f); SET(ELEM_In, NATURAL); ADD(ELEM_In, NATURAL, 114.818f, 0); ADD(ELEM_In, 98, 97.94224f, 0.0f); ADD(ELEM_In, 99, 98.93461f, 0.0f); ADD(ELEM_In, 100, 99.93115f, 0.0f); ADD(ELEM_In, 101, 100.92656f, 0.0f); ADD(ELEM_In, 102, 101.92471f, 0.0f); ADD(ELEM_In, 103, 102.919914f, 0.0f); ADD(ELEM_In, 104, 103.91834f, 0.0f); ADD(ELEM_In, 105, 104.914673f, 0.0f); ADD(ELEM_In, 106, 105.913461f, 0.0f); ADD(ELEM_In, 107, 106.910292f, 0.0f); ADD(ELEM_In, 108, 107.909720f, 0.0f); ADD(ELEM_In, 109, 108.907154f, 0.0f); ADD(ELEM_In, 110, 109.907169f, 0.0f); ADD(ELEM_In, 111, 110.905111f, 0.0f); ADD(ELEM_In, 112, 111.905533f, 0.0f); ADD(ELEM_In, 113, 112.904061f, 4.29f); ADD(ELEM_In, 114, 113.904917f, 0.0f); ADD(ELEM_In, 115, 114.903878f, 95.71f); ADD(ELEM_In, 116, 115.905260f, 0.0f); ADD(ELEM_In, 117, 116.904516f, 0.0f); ADD(ELEM_In, 118, 117.906355f, 0.0f); ADD(ELEM_In, 119, 118.905846f, 0.0f); ADD(ELEM_In, 120, 119.907960f, 0.0f); ADD(ELEM_In, 121, 120.907849f, 0.0f); ADD(ELEM_In, 122, 121.910280f, 0.0f); ADD(ELEM_In, 123, 122.910439f, 0.0f); ADD(ELEM_In, 124, 123.913180f, 0.0f); ADD(ELEM_In, 125, 124.913600f, 0.0f); ADD(ELEM_In, 126, 125.916460f, 0.0f); ADD(ELEM_In, 127, 126.917340f, 0.0f); ADD(ELEM_In, 128, 127.920170f, 0.0f); ADD(ELEM_In, 129, 128.92166f, 0.0f); ADD(ELEM_In, 130, 129.924850f, 0.0f); ADD(ELEM_In, 131, 130.926770f, 0.0f); ADD(ELEM_In, 132, 131.932920f, 0.0f); ADD(ELEM_In, 133, 132.93834f, 0.0f); ADD(ELEM_In, 134, 133.94466f, 0.0f); SET(ELEM_Sn, NATURAL); ADD(ELEM_Sn, NATURAL, 118.710f, 0); ADD(ELEM_Sn, 100, 99.93895f, 0.0f); ADD(ELEM_Sn, 101, 100.93606f, 0.0f); ADD(ELEM_Sn, 102, 101.93049f, 0.0f); ADD(ELEM_Sn, 103, 102.92813f, 0.0f); ADD(ELEM_Sn, 104, 103.92319f, 0.0f); ADD(ELEM_Sn, 105, 104.92139f, 0.0f); ADD(ELEM_Sn, 106, 105.916880f, 0.0f); ADD(ELEM_Sn, 107, 106.915670f, 0.0f); ADD(ELEM_Sn, 108, 107.911970f, 0.0f); ADD(ELEM_Sn, 109, 108.911287f, 0.0f); ADD(ELEM_Sn, 110, 109.907853f, 0.0f); ADD(ELEM_Sn, 111, 110.907735f, 0.0f); ADD(ELEM_Sn, 112, 111.904821f, 0.97f); ADD(ELEM_Sn, 113, 112.905173f, 0.0f); ADD(ELEM_Sn, 114, 113.902782f, 0.66f); ADD(ELEM_Sn, 115, 114.903346f, 0.34f); ADD(ELEM_Sn, 116, 115.901744f, 14.54f); ADD(ELEM_Sn, 117, 116.902954f, 7.68f); ADD(ELEM_Sn, 118, 117.901606f, 24.22f); ADD(ELEM_Sn, 119, 118.903309f, 8.59f); ADD(ELEM_Sn, 120, 119.9021966f, 32.58f); ADD(ELEM_Sn, 121, 120.9042369f, 0.0f); ADD(ELEM_Sn, 122, 121.9034401f, 4.63f); ADD(ELEM_Sn, 123, 122.9057219f, 0.0f); ADD(ELEM_Sn, 124, 123.9052746f, 5.79f); ADD(ELEM_Sn, 125, 124.9077849f, 0.0f); ADD(ELEM_Sn, 126, 125.907654f, 0.0f); ADD(ELEM_Sn, 127, 126.910351f, 0.0f); ADD(ELEM_Sn, 128, 127.910535f, 0.0f); ADD(ELEM_Sn, 129, 128.91344f, 0.0f); ADD(ELEM_Sn, 130, 129.913850f, 0.0f); ADD(ELEM_Sn, 131, 130.916920f, 0.0f); ADD(ELEM_Sn, 132, 131.917744f, 0.0f); ADD(ELEM_Sn, 133, 132.923810f, 0.0f); ADD(ELEM_Sn, 134, 133.92846f, 0.0f); ADD(ELEM_Sn, 135, 134.93473f, 0.0f); ADD(ELEM_Sn, 136, 135.93934f, 0.0f); ADD(ELEM_Sn, 137, 136.94579f, 0.0f); SET(ELEM_Sb, NATURAL); ADD(ELEM_Sb, NATURAL, 121.760f, 0); ADD(ELEM_Sb, 103, 102.94012f, 0.0f); ADD(ELEM_Sb, 104, 103.93629f, 0.0f); ADD(ELEM_Sb, 105, 104.93153f, 0.0f); ADD(ELEM_Sb, 106, 105.92876f, 0.0f); ADD(ELEM_Sb, 107, 106.92415f, 0.0f); ADD(ELEM_Sb, 108, 107.92216f, 0.0f); ADD(ELEM_Sb, 109, 108.918136f, 0.0f); ADD(ELEM_Sb, 110, 109.91676f, 0.0f); ADD(ELEM_Sb, 111, 110.91321f, 0.0f); ADD(ELEM_Sb, 112, 111.912395f, 0.0f); ADD(ELEM_Sb, 113, 112.909378f, 0.0f); ADD(ELEM_Sb, 114, 113.90910f, 0.0f); ADD(ELEM_Sb, 115, 114.906599f, 0.0f); ADD(ELEM_Sb, 116, 115.906797f, 0.0f); ADD(ELEM_Sb, 117, 116.904840f, 0.0f); ADD(ELEM_Sb, 118, 117.905532f, 0.0f); ADD(ELEM_Sb, 119, 118.903946f, 0.0f); ADD(ELEM_Sb, 120, 119.905074f, 0.0f); ADD(ELEM_Sb, 121, 120.9038180f, 57.21f); ADD(ELEM_Sb, 122, 121.9051754f, 0.0f); ADD(ELEM_Sb, 123, 122.9042157f, 42.79f); ADD(ELEM_Sb, 124, 123.9059375f, 0.0f); ADD(ELEM_Sb, 125, 124.905248f, 0.0f); ADD(ELEM_Sb, 126, 125.907250f, 0.0f); ADD(ELEM_Sb, 127, 126.906915f, 0.0f); ADD(ELEM_Sb, 128, 127.909167f, 0.0f); ADD(ELEM_Sb, 129, 128.909150f, 0.0f); ADD(ELEM_Sb, 130, 129.911546f, 0.0f); ADD(ELEM_Sb, 131, 130.911950f, 0.0f); ADD(ELEM_Sb, 132, 131.914413f, 0.0f); ADD(ELEM_Sb, 133, 132.915240f, 0.0f); ADD(ELEM_Sb, 134, 133.920550f, 0.0f); ADD(ELEM_Sb, 135, 134.92517f, 0.0f); ADD(ELEM_Sb, 136, 135.93066f, 0.0f); ADD(ELEM_Sb, 137, 136.93531f, 0.0f); ADD(ELEM_Sb, 138, 137.94096f, 0.0f); ADD(ELEM_Sb, 139, 138.94571f, 0.0f); SET(ELEM_Te, NATURAL); ADD(ELEM_Te, NATURAL, 127.60f, 0); ADD(ELEM_Te, 106, 105.93770f, 0.0f); ADD(ELEM_Te, 107, 106.93504f, 0.0f); ADD(ELEM_Te, 108, 107.92949f, 0.0f); ADD(ELEM_Te, 109, 108.927460f, 0.0f); ADD(ELEM_Te, 110, 109.922410f, 0.0f); ADD(ELEM_Te, 111, 110.921120f, 0.0f); ADD(ELEM_Te, 112, 111.91706f, 0.0f); ADD(ELEM_Te, 113, 112.91593f, 0.0f); ADD(ELEM_Te, 114, 113.91206f, 0.0f); ADD(ELEM_Te, 115, 114.91158f, 0.0f); ADD(ELEM_Te, 116, 115.90842f, 0.0f); ADD(ELEM_Te, 117, 116.908634f, 0.0f); ADD(ELEM_Te, 118, 117.905825f, 0.0f); ADD(ELEM_Te, 119, 118.906408f, 0.0f); ADD(ELEM_Te, 120, 119.904020f, 0.09f); ADD(ELEM_Te, 121, 120.904930f, 0.0f); ADD(ELEM_Te, 122, 121.9030471f, 2.55f); ADD(ELEM_Te, 123, 122.9042730f, 0.89f); ADD(ELEM_Te, 124, 123.9028195f, 4.74f); ADD(ELEM_Te, 125, 124.9044247f, 7.07f); ADD(ELEM_Te, 126, 125.9033055f, 18.84f); ADD(ELEM_Te, 127, 126.905217f, 0.0f); ADD(ELEM_Te, 128, 127.9044614f, 31.74f); ADD(ELEM_Te, 129, 128.906596f, 0.0f); ADD(ELEM_Te, 130, 129.9062228f, 34.08f); ADD(ELEM_Te, 131, 130.9085219f, 0.0f); ADD(ELEM_Te, 132, 131.908524f, 0.0f); ADD(ELEM_Te, 133, 132.910940f, 0.0f); ADD(ELEM_Te, 134, 133.911540f, 0.0f); ADD(ELEM_Te, 135, 134.91645f, 0.0f); ADD(ELEM_Te, 136, 135.920100f, 0.0f); ADD(ELEM_Te, 137, 136.92532f, 0.0f); ADD(ELEM_Te, 138, 137.92922f, 0.0f); ADD(ELEM_Te, 139, 138.93473f, 0.0f); ADD(ELEM_Te, 140, 139.93870f, 0.0f); ADD(ELEM_Te, 141, 140.94439f, 0.0f); ADD(ELEM_Te, 142, 141.94850f, 0.0f); SET(ELEM_I, NATURAL); ADD(ELEM_I, NATURAL, 126.90447f, 0); ADD(ELEM_I, 108, 107.94329f, 0.0f); ADD(ELEM_I, 109, 108.93819f, 0.0f); ADD(ELEM_I, 110, 109.93521f, 0.0f); ADD(ELEM_I, 111, 110.93028f, 0.0f); ADD(ELEM_I, 112, 111.92797f, 0.0f); ADD(ELEM_I, 113, 112.923640f, 0.0f); ADD(ELEM_I, 114, 113.92185f, 0.0f); ADD(ELEM_I, 115, 114.91792f, 0.0f); ADD(ELEM_I, 116, 115.91674f, 0.0f); ADD(ELEM_I, 117, 116.913650f, 0.0f); ADD(ELEM_I, 118, 117.913380f, 0.0f); ADD(ELEM_I, 119, 118.910180f, 0.0f); ADD(ELEM_I, 120, 119.910048f, 0.0f); ADD(ELEM_I, 121, 120.907366f, 0.0f); ADD(ELEM_I, 122, 121.907592f, 0.0f); ADD(ELEM_I, 123, 122.905598f, 0.0f); ADD(ELEM_I, 124, 123.9062114f, 0.0f); ADD(ELEM_I, 125, 124.9046241f, 0.0f); ADD(ELEM_I, 126, 125.905619f, 0.0f); ADD(ELEM_I, 127, 126.904468f, 100.0f); ADD(ELEM_I, 128, 127.905805f, 0.0f); ADD(ELEM_I, 129, 128.904987f, 0.0f); ADD(ELEM_I, 130, 129.906674f, 0.0f); ADD(ELEM_I, 131, 130.9061242f, 0.0f); ADD(ELEM_I, 132, 131.907995f, 0.0f); ADD(ELEM_I, 133, 132.907806f, 0.0f); ADD(ELEM_I, 134, 133.909877f, 0.0f); ADD(ELEM_I, 135, 134.910050f, 0.0f); ADD(ELEM_I, 136, 135.914660f, 0.0f); ADD(ELEM_I, 137, 136.917873f, 0.0f); ADD(ELEM_I, 138, 137.922380f, 0.0f); ADD(ELEM_I, 139, 138.926090f, 0.0f); ADD(ELEM_I, 140, 139.93121f, 0.0f); ADD(ELEM_I, 141, 140.93483f, 0.0f); ADD(ELEM_I, 142, 141.94018f, 0.0f); ADD(ELEM_I, 143, 142.94407f, 0.0f); ADD(ELEM_I, 144, 143.94961f, 0.0f); SET(ELEM_Xe, NATURAL); ADD(ELEM_Xe, NATURAL, 131.293f, 0); ADD(ELEM_Xe, 110, 109.94448f, 0.0f); ADD(ELEM_Xe, 111, 110.94163f, 0.0f); ADD(ELEM_Xe, 112, 111.93567f, 0.0f); ADD(ELEM_Xe, 113, 112.93338f, 0.0f); ADD(ELEM_Xe, 114, 113.92815f, 0.0f); ADD(ELEM_Xe, 115, 114.92654f, 0.0f); ADD(ELEM_Xe, 116, 115.92174f, 0.0f); ADD(ELEM_Xe, 117, 116.92056f, 0.0f); ADD(ELEM_Xe, 118, 117.91657f, 0.0f); ADD(ELEM_Xe, 119, 118.91555f, 0.0f); ADD(ELEM_Xe, 120, 119.912150f, 0.0f); ADD(ELEM_Xe, 121, 120.911386f, 0.0f); ADD(ELEM_Xe, 122, 121.908550f, 0.0f); ADD(ELEM_Xe, 123, 122.908471f, 0.0f); ADD(ELEM_Xe, 124, 123.9058958f, 0.09f); ADD(ELEM_Xe, 125, 124.9063982f, 0.0f); ADD(ELEM_Xe, 126, 125.904269f, 0.09f); ADD(ELEM_Xe, 127, 126.905180f, 0.0f); ADD(ELEM_Xe, 128, 127.9035304f, 1.92f); ADD(ELEM_Xe, 129, 128.9047795f, 26.44f); ADD(ELEM_Xe, 130, 129.9035079f, 4.08f); ADD(ELEM_Xe, 131, 130.9050819f, 21.18f); ADD(ELEM_Xe, 132, 131.9041545f, 26.89f); ADD(ELEM_Xe, 133, 132.905906f, 0.0f); ADD(ELEM_Xe, 134, 133.9053945f, 10.44f); ADD(ELEM_Xe, 135, 134.907207f, 0.0f); ADD(ELEM_Xe, 136, 135.907220f, 8.87f); ADD(ELEM_Xe, 137, 136.911563f, 0.0f); ADD(ELEM_Xe, 138, 137.913990f, 0.0f); ADD(ELEM_Xe, 139, 138.918787f, 0.0f); ADD(ELEM_Xe, 140, 139.921640f, 0.0f); ADD(ELEM_Xe, 141, 140.92665f, 0.0f); ADD(ELEM_Xe, 142, 141.92970f, 0.0f); ADD(ELEM_Xe, 143, 142.93489f, 0.0f); ADD(ELEM_Xe, 144, 143.93823f, 0.0f); ADD(ELEM_Xe, 145, 144.94367f, 0.0f); ADD(ELEM_Xe, 146, 145.94730f, 0.0f); ADD(ELEM_Xe, 147, 146.95301f, 0.0f); SET(ELEM_Cs, NATURAL); ADD(ELEM_Cs, NATURAL, 132.90545f, 0); ADD(ELEM_Cs, 112, 111.95033f, 0.0f); ADD(ELEM_Cs, 113, 112.94454f, 0.0f); ADD(ELEM_Cs, 114, 113.94142f, 0.0f); ADD(ELEM_Cs, 115, 114.93594f, 0.0f); ADD(ELEM_Cs, 116, 115.93291f, 0.0f); ADD(ELEM_Cs, 117, 116.928640f, 0.0f); ADD(ELEM_Cs, 118, 117.926555f, 0.0f); ADD(ELEM_Cs, 119, 118.922371f, 0.0f); ADD(ELEM_Cs, 120, 119.920678f, 0.0f); ADD(ELEM_Cs, 121, 120.917184f, 0.0f); ADD(ELEM_Cs, 122, 121.916122f, 0.0f); ADD(ELEM_Cs, 123, 122.912990f, 0.0f); ADD(ELEM_Cs, 124, 123.912246f, 0.0f); ADD(ELEM_Cs, 125, 124.909725f, 0.0f); ADD(ELEM_Cs, 126, 125.909448f, 0.0f); ADD(ELEM_Cs, 127, 126.907418f, 0.0f); ADD(ELEM_Cs, 128, 127.907748f, 0.0f); ADD(ELEM_Cs, 129, 128.906063f, 0.0f); ADD(ELEM_Cs, 130, 129.906706f, 0.0f); ADD(ELEM_Cs, 131, 130.905460f, 0.0f); ADD(ELEM_Cs, 132, 131.906430f, 0.0f); ADD(ELEM_Cs, 133, 132.905447f, 100.0f); ADD(ELEM_Cs, 134, 133.906713f, 0.0f); ADD(ELEM_Cs, 135, 134.905972f, 0.0f); ADD(ELEM_Cs, 136, 135.907306f, 0.0f); ADD(ELEM_Cs, 137, 136.907084f, 0.0f); ADD(ELEM_Cs, 138, 137.911011f, 0.0f); ADD(ELEM_Cs, 139, 138.913358f, 0.0f); ADD(ELEM_Cs, 140, 139.917277f, 0.0f); ADD(ELEM_Cs, 141, 140.920044f, 0.0f); ADD(ELEM_Cs, 142, 141.924292f, 0.0f); ADD(ELEM_Cs, 143, 142.927330f, 0.0f); ADD(ELEM_Cs, 144, 143.932030f, 0.0f); ADD(ELEM_Cs, 145, 144.935390f, 0.0f); ADD(ELEM_Cs, 146, 145.940160f, 0.0f); ADD(ELEM_Cs, 147, 146.94386f, 0.0f); ADD(ELEM_Cs, 148, 147.94890f, 0.0f); ADD(ELEM_Cs, 149, 148.95272f, 0.0f); ADD(ELEM_Cs, 150, 149.95797f, 0.0f); ADD(ELEM_Cs, 151, 150.96200f, 0.0f); SET(ELEM_Ba, NATURAL); ADD(ELEM_Ba, NATURAL, 137.327f, 0); ADD(ELEM_Ba, 114, 113.95094f, 0.0f); ADD(ELEM_Ba, 115, 114.94771f, 0.0f); ADD(ELEM_Ba, 116, 115.94168f, 0.0f); ADD(ELEM_Ba, 117, 116.93886f, 0.0f); ADD(ELEM_Ba, 118, 117.93344f, 0.0f); ADD(ELEM_Ba, 119, 118.93105f, 0.0f); ADD(ELEM_Ba, 120, 119.92605f, 0.0f); ADD(ELEM_Ba, 121, 120.92449f, 0.0f); ADD(ELEM_Ba, 122, 121.92026f, 0.0f); ADD(ELEM_Ba, 123, 122.91885f, 0.0f); ADD(ELEM_Ba, 124, 123.915088f, 0.0f); ADD(ELEM_Ba, 125, 124.91462f, 0.0f); ADD(ELEM_Ba, 126, 125.911244f, 0.0f); ADD(ELEM_Ba, 127, 126.91112f, 0.0f); ADD(ELEM_Ba, 128, 127.908309f, 0.0f); ADD(ELEM_Ba, 129, 128.908674f, 0.0f); ADD(ELEM_Ba, 130, 129.906310f, 0.106f); ADD(ELEM_Ba, 131, 130.906931f, 0.0f); ADD(ELEM_Ba, 132, 131.905056f, 0.101f); ADD(ELEM_Ba, 133, 132.906002f, 0.0f); ADD(ELEM_Ba, 134, 133.904503f, 2.417f); ADD(ELEM_Ba, 135, 134.905683f, 6.592f); ADD(ELEM_Ba, 136, 135.904570f, 7.854f); ADD(ELEM_Ba, 137, 136.905821f, 11.232f); ADD(ELEM_Ba, 138, 137.905241f, 71.698f); ADD(ELEM_Ba, 139, 138.908835f, 0.0f); ADD(ELEM_Ba, 140, 139.910599f, 0.0f); ADD(ELEM_Ba, 141, 140.914406f, 0.0f); ADD(ELEM_Ba, 142, 141.916448f, 0.0f); ADD(ELEM_Ba, 143, 142.920617f, 0.0f); ADD(ELEM_Ba, 144, 143.922940f, 0.0f); ADD(ELEM_Ba, 145, 144.926920f, 0.0f); ADD(ELEM_Ba, 146, 145.930110f, 0.0f); ADD(ELEM_Ba, 147, 146.93399f, 0.0f); ADD(ELEM_Ba, 148, 147.93768f, 0.0f); ADD(ELEM_Ba, 149, 148.94246f, 0.0f); ADD(ELEM_Ba, 150, 149.94562f, 0.0f); ADD(ELEM_Ba, 151, 150.95070f, 0.0f); ADD(ELEM_Ba, 152, 151.95416f, 0.0f); ADD(ELEM_Ba, 153, 152.95961f, 0.0f); SET(ELEM_La, NATURAL); ADD(ELEM_La, NATURAL, 138.9055f, 0); ADD(ELEM_La, 117, 116.95001f, 0.0f); ADD(ELEM_La, 118, 117.94657f, 0.0f); ADD(ELEM_La, 119, 118.94099f, 0.0f); ADD(ELEM_La, 120, 119.93807f, 0.0f); ADD(ELEM_La, 121, 120.93301f, 0.0f); ADD(ELEM_La, 122, 121.93071f, 0.0f); ADD(ELEM_La, 123, 122.92624f, 0.0f); ADD(ELEM_La, 124, 123.92453f, 0.0f); ADD(ELEM_La, 125, 124.92067f, 0.0f); ADD(ELEM_La, 126, 125.91937f, 0.0f); ADD(ELEM_La, 127, 126.91616f, 0.0f); ADD(ELEM_La, 128, 127.91545f, 0.0f); ADD(ELEM_La, 129, 128.912670f, 0.0f); ADD(ELEM_La, 130, 129.91232f, 0.0f); ADD(ELEM_La, 131, 130.91011f, 0.0f); ADD(ELEM_La, 132, 131.910110f, 0.0f); ADD(ELEM_La, 133, 132.90840f, 0.0f); ADD(ELEM_La, 134, 133.908490f, 0.0f); ADD(ELEM_La, 135, 134.906971f, 0.0f); ADD(ELEM_La, 136, 135.907650f, 0.0f); ADD(ELEM_La, 137, 136.906470f, 0.0f); ADD(ELEM_La, 138, 137.907107f, 0.090f); ADD(ELEM_La, 139, 138.906348f, 99.910f); ADD(ELEM_La, 140, 139.909473f, 0.0f); ADD(ELEM_La, 141, 140.910957f, 0.0f); ADD(ELEM_La, 142, 141.914074f, 0.0f); ADD(ELEM_La, 143, 142.916059f, 0.0f); ADD(ELEM_La, 144, 143.919590f, 0.0f); ADD(ELEM_La, 145, 144.921640f, 0.0f); ADD(ELEM_La, 146, 145.925700f, 0.0f); ADD(ELEM_La, 147, 146.927820f, 0.0f); ADD(ELEM_La, 148, 147.93219f, 0.0f); ADD(ELEM_La, 149, 148.93437f, 0.0f); ADD(ELEM_La, 150, 149.93857f, 0.0f); ADD(ELEM_La, 151, 150.94156f, 0.0f); ADD(ELEM_La, 152, 151.94611f, 0.0f); ADD(ELEM_La, 153, 152.94945f, 0.0f); ADD(ELEM_La, 154, 153.95440f, 0.0f); ADD(ELEM_La, 155, 154.95813f, 0.0f); SET(ELEM_Ce, NATURAL); ADD(ELEM_Ce, NATURAL, 140.116f, 0); ADD(ELEM_Ce, 119, 118.95276f, 0.0f); ADD(ELEM_Ce, 120, 119.94664f, 0.0f); ADD(ELEM_Ce, 121, 120.94367f, 0.0f); ADD(ELEM_Ce, 122, 121.93801f, 0.0f); ADD(ELEM_Ce, 123, 122.93551f, 0.0f); ADD(ELEM_Ce, 124, 123.93052f, 0.0f); ADD(ELEM_Ce, 125, 124.92854f, 0.0f); ADD(ELEM_Ce, 126, 125.92410f, 0.0f); ADD(ELEM_Ce, 127, 126.92275f, 0.0f); ADD(ELEM_Ce, 128, 127.91887f, 0.0f); ADD(ELEM_Ce, 129, 128.91809f, 0.0f); ADD(ELEM_Ce, 130, 129.91469f, 0.0f); ADD(ELEM_Ce, 131, 130.91442f, 0.0f); ADD(ELEM_Ce, 132, 131.91149f, 0.0f); ADD(ELEM_Ce, 133, 132.91155f, 0.0f); ADD(ELEM_Ce, 134, 133.90903f, 0.0f); ADD(ELEM_Ce, 135, 134.909146f, 0.0f); ADD(ELEM_Ce, 136, 135.907140f, 0.185f); ADD(ELEM_Ce, 137, 136.907780f, 0.0f); ADD(ELEM_Ce, 138, 137.905986f, 0.251f); ADD(ELEM_Ce, 139, 138.906647f, 0.0f); ADD(ELEM_Ce, 140, 139.905434f, 88.450f); ADD(ELEM_Ce, 141, 140.908271f, 0.0f); ADD(ELEM_Ce, 142, 141.909240f, 11.114f); ADD(ELEM_Ce, 143, 142.912381f, 0.0f); ADD(ELEM_Ce, 144, 143.913643f, 0.0f); ADD(ELEM_Ce, 145, 144.917230f, 0.0f); ADD(ELEM_Ce, 146, 145.918690f, 0.0f); ADD(ELEM_Ce, 147, 146.922510f, 0.0f); ADD(ELEM_Ce, 148, 147.92439f, 0.0f); ADD(ELEM_Ce, 149, 148.928290f, 0.0f); ADD(ELEM_Ce, 150, 149.93023f, 0.0f); ADD(ELEM_Ce, 151, 150.93404f, 0.0f); ADD(ELEM_Ce, 152, 151.93638f, 0.0f); ADD(ELEM_Ce, 153, 152.94058f, 0.0f); ADD(ELEM_Ce, 154, 153.94332f, 0.0f); ADD(ELEM_Ce, 155, 154.94804f, 0.0f); ADD(ELEM_Ce, 156, 155.95126f, 0.0f); ADD(ELEM_Ce, 157, 156.95634f, 0.0f); SET(ELEM_Pr, NATURAL); ADD(ELEM_Pr, NATURAL, 140.90765f, 0); ADD(ELEM_Pr, 121, 120.95536f, 0.0f); ADD(ELEM_Pr, 122, 121.95165f, 0.0f); ADD(ELEM_Pr, 123, 122.94596f, 0.0f); ADD(ELEM_Pr, 124, 123.94296f, 0.0f); ADD(ELEM_Pr, 125, 124.93783f, 0.0f); ADD(ELEM_Pr, 126, 125.93531f, 0.0f); ADD(ELEM_Pr, 127, 126.93083f, 0.0f); ADD(ELEM_Pr, 128, 127.92880f, 0.0f); ADD(ELEM_Pr, 129, 128.92486f, 0.0f); ADD(ELEM_Pr, 130, 129.92338f, 0.0f); ADD(ELEM_Pr, 131, 130.92006f, 0.0f); ADD(ELEM_Pr, 132, 131.91912f, 0.0f); ADD(ELEM_Pr, 133, 132.91620f, 0.0f); ADD(ELEM_Pr, 134, 133.91567f, 0.0f); ADD(ELEM_Pr, 135, 134.91314f, 0.0f); ADD(ELEM_Pr, 136, 135.912650f, 0.0f); ADD(ELEM_Pr, 137, 136.910680f, 0.0f); ADD(ELEM_Pr, 138, 137.910749f, 0.0f); ADD(ELEM_Pr, 139, 138.908932f, 0.0f); ADD(ELEM_Pr, 140, 139.909071f, 0.0f); ADD(ELEM_Pr, 141, 140.907648f, 100.0f); ADD(ELEM_Pr, 142, 141.910040f, 0.0f); ADD(ELEM_Pr, 143, 142.910812f, 0.0f); ADD(ELEM_Pr, 144, 143.913301f, 0.0f); ADD(ELEM_Pr, 145, 144.914507f, 0.0f); ADD(ELEM_Pr, 146, 145.917590f, 0.0f); ADD(ELEM_Pr, 147, 146.918980f, 0.0f); ADD(ELEM_Pr, 148, 147.92218f, 0.0f); ADD(ELEM_Pr, 149, 148.923791f, 0.0f); ADD(ELEM_Pr, 150, 149.927000f, 0.0f); ADD(ELEM_Pr, 151, 150.928230f, 0.0f); ADD(ELEM_Pr, 152, 151.93160f, 0.0f); ADD(ELEM_Pr, 153, 152.93365f, 0.0f); ADD(ELEM_Pr, 154, 153.93739f, 0.0f); ADD(ELEM_Pr, 155, 154.93999f, 0.0f); ADD(ELEM_Pr, 156, 155.94412f, 0.0f); ADD(ELEM_Pr, 157, 156.94717f, 0.0f); ADD(ELEM_Pr, 158, 157.95178f, 0.0f); ADD(ELEM_Pr, 159, 158.95523f, 0.0f); SET(ELEM_Nd, NATURAL); ADD(ELEM_Nd, NATURAL, 144.24f, 0); ADD(ELEM_Nd, 126, 125.94307f, 0.0f); ADD(ELEM_Nd, 127, 126.94050f, 0.0f); ADD(ELEM_Nd, 128, 127.93539f, 0.0f); ADD(ELEM_Nd, 129, 128.93325f, 0.0f); ADD(ELEM_Nd, 130, 129.92878f, 0.0f); ADD(ELEM_Nd, 131, 130.92710f, 0.0f); ADD(ELEM_Nd, 132, 131.92312f, 0.0f); ADD(ELEM_Nd, 133, 132.92221f, 0.0f); ADD(ELEM_Nd, 134, 133.91865f, 0.0f); ADD(ELEM_Nd, 135, 134.91824f, 0.0f); ADD(ELEM_Nd, 136, 135.915020f, 0.0f); ADD(ELEM_Nd, 137, 136.914640f, 0.0f); ADD(ELEM_Nd, 138, 137.91193f, 0.0f); ADD(ELEM_Nd, 139, 138.911920f, 0.0f); ADD(ELEM_Nd, 140, 139.909310f, 0.0f); ADD(ELEM_Nd, 141, 140.909605f, 0.0f); ADD(ELEM_Nd, 142, 141.907719f, 27.2f); ADD(ELEM_Nd, 143, 142.909810f, 12.2f); ADD(ELEM_Nd, 144, 143.910083f, 23.8f); ADD(ELEM_Nd, 145, 144.912569f, 8.3f); ADD(ELEM_Nd, 146, 145.913112f, 17.2f); ADD(ELEM_Nd, 147, 146.916096f, 0.0f); ADD(ELEM_Nd, 148, 147.916889f, 5.7f); ADD(ELEM_Nd, 149, 148.920144f, 0.0f); ADD(ELEM_Nd, 150, 149.920887f, 5.6f); ADD(ELEM_Nd, 151, 150.923825f, 0.0f); ADD(ELEM_Nd, 152, 151.924680f, 0.0f); ADD(ELEM_Nd, 153, 152.927695f, 0.0f); ADD(ELEM_Nd, 154, 153.92948f, 0.0f); ADD(ELEM_Nd, 155, 154.93263f, 0.0f); ADD(ELEM_Nd, 156, 155.93520f, 0.0f); ADD(ELEM_Nd, 157, 156.93927f, 0.0f); ADD(ELEM_Nd, 158, 157.94187f, 0.0f); ADD(ELEM_Nd, 159, 158.94639f, 0.0f); ADD(ELEM_Nd, 160, 159.94939f, 0.0f); ADD(ELEM_Nd, 161, 160.95433f, 0.0f); SET(ELEM_Pm, 145); ADD(ELEM_Pm, 128, 127.94826f, 0.0f); ADD(ELEM_Pm, 129, 128.94316f, 0.0f); ADD(ELEM_Pm, 130, 129.94045f, 0.0f); ADD(ELEM_Pm, 131, 130.93580f, 0.0f); ADD(ELEM_Pm, 132, 131.93375f, 0.0f); ADD(ELEM_Pm, 133, 132.92972f, 0.0f); ADD(ELEM_Pm, 134, 133.92849f, 0.0f); ADD(ELEM_Pm, 135, 134.92462f, 0.0f); ADD(ELEM_Pm, 136, 135.92345f, 0.0f); ADD(ELEM_Pm, 137, 136.92071f, 0.0f); ADD(ELEM_Pm, 138, 137.91945f, 0.0f); ADD(ELEM_Pm, 139, 138.916760f, 0.0f); ADD(ELEM_Pm, 140, 139.915800f, 0.0f); ADD(ELEM_Pm, 141, 140.913607f, 0.0f); ADD(ELEM_Pm, 142, 141.912950f, 0.0f); ADD(ELEM_Pm, 143, 142.910928f, 0.0f); ADD(ELEM_Pm, 144, 143.912586f, 0.0f); ADD(ELEM_Pm, 145, 144.912744f, 0.0f); ADD(ELEM_Pm, 146, 145.914692f, 0.0f); ADD(ELEM_Pm, 147, 146.915134f, 0.0f); ADD(ELEM_Pm, 148, 147.917468f, 0.0f); ADD(ELEM_Pm, 149, 148.918329f, 0.0f); ADD(ELEM_Pm, 150, 149.920979f, 0.0f); ADD(ELEM_Pm, 151, 150.921203f, 0.0f); ADD(ELEM_Pm, 152, 151.923490f, 0.0f); ADD(ELEM_Pm, 153, 152.924113f, 0.0f); ADD(ELEM_Pm, 154, 153.926550f, 0.0f); ADD(ELEM_Pm, 155, 154.928100f, 0.0f); ADD(ELEM_Pm, 156, 155.931060f, 0.0f); ADD(ELEM_Pm, 157, 156.93320f, 0.0f); ADD(ELEM_Pm, 158, 157.93669f, 0.0f); ADD(ELEM_Pm, 159, 158.93913f, 0.0f); ADD(ELEM_Pm, 160, 159.94299f, 0.0f); ADD(ELEM_Pm, 161, 160.94586f, 0.0f); ADD(ELEM_Pm, 162, 161.95029f, 0.0f); ADD(ELEM_Pm, 163, 162.95352f, 0.0f); SET(ELEM_Sm, NATURAL); ADD(ELEM_Sm, NATURAL, 150.36f, 0); ADD(ELEM_Sm, 130, 129.94863f, 0.0f); ADD(ELEM_Sm, 131, 130.94589f, 0.0f); ADD(ELEM_Sm, 132, 131.94082f, 0.0f); ADD(ELEM_Sm, 133, 132.93873f, 0.0f); ADD(ELEM_Sm, 134, 133.93402f, 0.0f); ADD(ELEM_Sm, 135, 134.93235f, 0.0f); ADD(ELEM_Sm, 136, 135.92830f, 0.0f); ADD(ELEM_Sm, 137, 136.92705f, 0.0f); ADD(ELEM_Sm, 138, 137.92354f, 0.0f); ADD(ELEM_Sm, 139, 138.922302f, 0.0f); ADD(ELEM_Sm, 140, 139.918991f, 0.0f); ADD(ELEM_Sm, 141, 140.918469f, 0.0f); ADD(ELEM_Sm, 142, 141.915193f, 0.0f); ADD(ELEM_Sm, 143, 142.914624f, 0.0f); ADD(ELEM_Sm, 144, 143.911995f, 3.07f); ADD(ELEM_Sm, 145, 144.913406f, 0.0f); ADD(ELEM_Sm, 146, 145.913037f, 0.0f); ADD(ELEM_Sm, 147, 146.914893f, 14.99f); ADD(ELEM_Sm, 148, 147.914818f, 11.24f); ADD(ELEM_Sm, 149, 148.917180f, 13.82f); ADD(ELEM_Sm, 150, 149.917271f, 7.38f); ADD(ELEM_Sm, 151, 150.919928f, 0.0f); ADD(ELEM_Sm, 152, 151.919728f, 26.75f); ADD(ELEM_Sm, 153, 152.922094f, 0.0f); ADD(ELEM_Sm, 154, 153.922205f, 22.75f); ADD(ELEM_Sm, 155, 154.924636f, 0.0f); ADD(ELEM_Sm, 156, 155.925526f, 0.0f); ADD(ELEM_Sm, 157, 156.928350f, 0.0f); ADD(ELEM_Sm, 158, 157.929990f, 0.0f); ADD(ELEM_Sm, 159, 158.93320f, 0.0f); ADD(ELEM_Sm, 160, 159.93514f, 0.0f); ADD(ELEM_Sm, 161, 160.93883f, 0.0f); ADD(ELEM_Sm, 162, 161.94122f, 0.0f); ADD(ELEM_Sm, 163, 162.94536f, 0.0f); ADD(ELEM_Sm, 164, 163.94828f, 0.0f); ADD(ELEM_Sm, 165, 164.95298f, 0.0f); SET(ELEM_Eu, NATURAL); ADD(ELEM_Eu, NATURAL, 151.964f, 0); ADD(ELEM_Eu, 132, 131.95416f, 0.0f); ADD(ELEM_Eu, 133, 132.94890f, 0.0f); ADD(ELEM_Eu, 134, 133.94632f, 0.0f); ADD(ELEM_Eu, 135, 134.94172f, 0.0f); ADD(ELEM_Eu, 136, 135.93950f, 0.0f); ADD(ELEM_Eu, 137, 136.93521f, 0.0f); ADD(ELEM_Eu, 138, 137.93345f, 0.0f); ADD(ELEM_Eu, 139, 138.92984f, 0.0f); ADD(ELEM_Eu, 140, 139.928080f, 0.0f); ADD(ELEM_Eu, 141, 140.924890f, 0.0f); ADD(ELEM_Eu, 142, 141.923400f, 0.0f); ADD(ELEM_Eu, 143, 142.920287f, 0.0f); ADD(ELEM_Eu, 144, 143.918774f, 0.0f); ADD(ELEM_Eu, 145, 144.916261f, 0.0f); ADD(ELEM_Eu, 146, 145.917200f, 0.0f); ADD(ELEM_Eu, 147, 146.916741f, 0.0f); ADD(ELEM_Eu, 148, 147.918154f, 0.0f); ADD(ELEM_Eu, 149, 148.917926f, 0.0f); ADD(ELEM_Eu, 150, 149.919698f, 0.0f); ADD(ELEM_Eu, 151, 150.919846f, 47.81f); ADD(ELEM_Eu, 152, 151.921740f, 0.0f); ADD(ELEM_Eu, 153, 152.921226f, 52.19f); ADD(ELEM_Eu, 154, 153.922975f, 0.0f); ADD(ELEM_Eu, 155, 154.922889f, 0.0f); ADD(ELEM_Eu, 156, 155.924751f, 0.0f); ADD(ELEM_Eu, 157, 156.925419f, 0.0f); ADD(ELEM_Eu, 158, 157.927840f, 0.0f); ADD(ELEM_Eu, 159, 158.929084f, 0.0f); ADD(ELEM_Eu, 160, 159.93197f, 0.0f); ADD(ELEM_Eu, 161, 160.93368f, 0.0f); ADD(ELEM_Eu, 162, 161.93704f, 0.0f); ADD(ELEM_Eu, 163, 162.93921f, 0.0f); ADD(ELEM_Eu, 164, 163.94299f, 0.0f); ADD(ELEM_Eu, 165, 164.94572f, 0.0f); ADD(ELEM_Eu, 166, 165.94997f, 0.0f); ADD(ELEM_Eu, 167, 166.95305f, 0.0f); SET(ELEM_Gd, NATURAL); ADD(ELEM_Gd, NATURAL, 157.25f, 0); ADD(ELEM_Gd, 136, 135.94707f, 0.0f); ADD(ELEM_Gd, 137, 136.94465f, 0.0f); ADD(ELEM_Gd, 138, 137.93997f, 0.0f); ADD(ELEM_Gd, 139, 138.93808f, 0.0f); ADD(ELEM_Gd, 140, 139.93395f, 0.0f); ADD(ELEM_Gd, 141, 140.93221f, 0.0f); ADD(ELEM_Gd, 142, 141.92823f, 0.0f); ADD(ELEM_Gd, 143, 142.92674f, 0.0f); ADD(ELEM_Gd, 144, 143.92279f, 0.0f); ADD(ELEM_Gd, 145, 144.921690f, 0.0f); ADD(ELEM_Gd, 146, 145.918305f, 0.0f); ADD(ELEM_Gd, 147, 146.919089f, 0.0f); ADD(ELEM_Gd, 148, 147.918110f, 0.0f); ADD(ELEM_Gd, 149, 148.919336f, 0.0f); ADD(ELEM_Gd, 150, 149.918655f, 0.0f); ADD(ELEM_Gd, 151, 150.920344f, 0.0f); ADD(ELEM_Gd, 152, 151.919788f, 0.20f); ADD(ELEM_Gd, 153, 152.921746f, 0.0f); ADD(ELEM_Gd, 154, 153.920862f, 2.18f); ADD(ELEM_Gd, 155, 154.922619f, 14.80f); ADD(ELEM_Gd, 156, 155.922120f, 20.47f); ADD(ELEM_Gd, 157, 156.923957f, 15.65f); ADD(ELEM_Gd, 158, 157.924101f, 24.84f); ADD(ELEM_Gd, 159, 158.926385f, 0.0f); ADD(ELEM_Gd, 160, 159.927051f, 21.86f); ADD(ELEM_Gd, 161, 160.929666f, 0.0f); ADD(ELEM_Gd, 162, 161.930981f, 0.0f); ADD(ELEM_Gd, 163, 162.93399f, 0.0f); ADD(ELEM_Gd, 164, 163.93586f, 0.0f); ADD(ELEM_Gd, 165, 164.93938f, 0.0f); ADD(ELEM_Gd, 166, 165.94160f, 0.0f); ADD(ELEM_Gd, 167, 166.94557f, 0.0f); ADD(ELEM_Gd, 168, 167.94836f, 0.0f); ADD(ELEM_Gd, 169, 168.95287f, 0.0f); SET(ELEM_Tb, NATURAL); ADD(ELEM_Tb, NATURAL, 158.92534f, 0); ADD(ELEM_Tb, 138, 137.95287f, 0.0f); ADD(ELEM_Tb, 139, 138.94803f, 0.0f); ADD(ELEM_Tb, 140, 139.94554f, 0.0f); ADD(ELEM_Tb, 141, 140.94116f, 0.0f); ADD(ELEM_Tb, 142, 141.93886f, 0.0f); ADD(ELEM_Tb, 143, 142.93475f, 0.0f); ADD(ELEM_Tb, 144, 143.93253f, 0.0f); ADD(ELEM_Tb, 145, 144.92888f, 0.0f); ADD(ELEM_Tb, 146, 145.927180f, 0.0f); ADD(ELEM_Tb, 147, 146.924037f, 0.0f); ADD(ELEM_Tb, 148, 147.924300f, 0.0f); ADD(ELEM_Tb, 149, 148.923242f, 0.0f); ADD(ELEM_Tb, 150, 149.923654f, 0.0f); ADD(ELEM_Tb, 151, 150.923098f, 0.0f); ADD(ELEM_Tb, 152, 151.924070f, 0.0f); ADD(ELEM_Tb, 153, 152.923431f, 0.0f); ADD(ELEM_Tb, 154, 153.924690f, 0.0f); ADD(ELEM_Tb, 155, 154.923500f, 0.0f); ADD(ELEM_Tb, 156, 155.924744f, 0.0f); ADD(ELEM_Tb, 157, 156.924021f, 0.0f); ADD(ELEM_Tb, 158, 157.925410f, 0.0f); ADD(ELEM_Tb, 159, 158.925343f, 100.0f); ADD(ELEM_Tb, 160, 159.927164f, 0.0f); ADD(ELEM_Tb, 161, 160.927566f, 0.0f); ADD(ELEM_Tb, 162, 161.929480f, 0.0f); ADD(ELEM_Tb, 163, 162.930644f, 0.0f); ADD(ELEM_Tb, 164, 163.93335f, 0.0f); ADD(ELEM_Tb, 165, 164.93488f, 0.0f); ADD(ELEM_Tb, 166, 165.93805f, 0.0f); ADD(ELEM_Tb, 167, 166.94005f, 0.0f); ADD(ELEM_Tb, 168, 167.94364f, 0.0f); ADD(ELEM_Tb, 169, 168.94622f, 0.0f); ADD(ELEM_Tb, 170, 169.95025f, 0.0f); ADD(ELEM_Tb, 171, 170.95330f, 0.0f); SET(ELEM_Dy, NATURAL); ADD(ELEM_Dy, NATURAL, 162.500f, 0); ADD(ELEM_Dy, 140, 139.95379f, 0.0f); ADD(ELEM_Dy, 141, 140.95119f, 0.0f); ADD(ELEM_Dy, 142, 141.94627f, 0.0f); ADD(ELEM_Dy, 143, 142.94383f, 0.0f); ADD(ELEM_Dy, 144, 143.93907f, 0.0f); ADD(ELEM_Dy, 145, 144.93695f, 0.0f); ADD(ELEM_Dy, 146, 145.93272f, 0.0f); ADD(ELEM_Dy, 147, 146.930880f, 0.0f); ADD(ELEM_Dy, 148, 147.927180f, 0.0f); ADD(ELEM_Dy, 149, 148.927334f, 0.0f); ADD(ELEM_Dy, 150, 149.925580f, 0.0f); ADD(ELEM_Dy, 151, 150.926180f, 0.0f); ADD(ELEM_Dy, 152, 151.924714f, 0.0f); ADD(ELEM_Dy, 153, 152.925761f, 0.0f); ADD(ELEM_Dy, 154, 153.924422f, 0.0f); ADD(ELEM_Dy, 155, 154.925749f, 0.0f); ADD(ELEM_Dy, 156, 155.924278f, 0.06f); ADD(ELEM_Dy, 157, 156.925461f, 0.0f); ADD(ELEM_Dy, 158, 157.924405f, 0.10f); ADD(ELEM_Dy, 159, 158.925736f, 0.0f); ADD(ELEM_Dy, 160, 159.925194f, 2.34f); ADD(ELEM_Dy, 161, 160.926930f, 18.91f); ADD(ELEM_Dy, 162, 161.926795f, 25.51f); ADD(ELEM_Dy, 163, 162.928728f, 24.90f); ADD(ELEM_Dy, 164, 163.929171f, 28.18f); ADD(ELEM_Dy, 165, 164.931700f, 0.0f); ADD(ELEM_Dy, 166, 165.932803f, 0.0f); ADD(ELEM_Dy, 167, 166.935650f, 0.0f); ADD(ELEM_Dy, 168, 167.93723f, 0.0f); ADD(ELEM_Dy, 169, 168.94030f, 0.0f); ADD(ELEM_Dy, 170, 169.94267f, 0.0f); ADD(ELEM_Dy, 171, 170.94648f, 0.0f); ADD(ELEM_Dy, 172, 171.94911f, 0.0f); ADD(ELEM_Dy, 173, 172.95344f, 0.0f); SET(ELEM_Ho, NATURAL); ADD(ELEM_Ho, NATURAL, 164.93032f, 0); ADD(ELEM_Ho, 142, 141.95986f, 0.0f); ADD(ELEM_Ho, 143, 142.95469f, 0.0f); ADD(ELEM_Ho, 144, 143.95164f, 0.0f); ADD(ELEM_Ho, 145, 144.94688f, 0.0f); ADD(ELEM_Ho, 146, 145.94410f, 0.0f); ADD(ELEM_Ho, 147, 146.93984f, 0.0f); ADD(ELEM_Ho, 148, 147.93727f, 0.0f); ADD(ELEM_Ho, 149, 148.933790f, 0.0f); ADD(ELEM_Ho, 150, 149.93335f, 0.0f); ADD(ELEM_Ho, 151, 150.931681f, 0.0f); ADD(ELEM_Ho, 152, 151.931740f, 0.0f); ADD(ELEM_Ho, 153, 152.930195f, 0.0f); ADD(ELEM_Ho, 154, 153.930596f, 0.0f); ADD(ELEM_Ho, 155, 154.929079f, 0.0f); ADD(ELEM_Ho, 156, 155.92971f, 0.0f); ADD(ELEM_Ho, 157, 156.928190f, 0.0f); ADD(ELEM_Ho, 158, 157.928950f, 0.0f); ADD(ELEM_Ho, 159, 158.927709f, 0.0f); ADD(ELEM_Ho, 160, 159.928726f, 0.0f); ADD(ELEM_Ho, 161, 160.927852f, 0.0f); ADD(ELEM_Ho, 162, 161.929092f, 0.0f); ADD(ELEM_Ho, 163, 162.928730f, 0.0f); ADD(ELEM_Ho, 164, 163.930231f, 0.0f); ADD(ELEM_Ho, 165, 164.930319f, 100.0f); ADD(ELEM_Ho, 166, 165.932281f, 0.0f); ADD(ELEM_Ho, 167, 166.933126f, 0.0f); ADD(ELEM_Ho, 168, 167.935500f, 0.0f); ADD(ELEM_Ho, 169, 168.936868f, 0.0f); ADD(ELEM_Ho, 170, 169.939610f, 0.0f); ADD(ELEM_Ho, 171, 170.94146f, 0.0f); ADD(ELEM_Ho, 172, 171.94482f, 0.0f); ADD(ELEM_Ho, 173, 172.94729f, 0.0f); ADD(ELEM_Ho, 174, 173.95115f, 0.0f); ADD(ELEM_Ho, 175, 174.95405f, 0.0f); SET(ELEM_Er, NATURAL); ADD(ELEM_Er, NATURAL, 167.259f, 0); ADD(ELEM_Er, 144, 143.96059f, 0.0f); ADD(ELEM_Er, 145, 144.95746f, 0.0f); ADD(ELEM_Er, 146, 145.95212f, 0.0f); ADD(ELEM_Er, 147, 146.94931f, 0.0f); ADD(ELEM_Er, 148, 147.94444f, 0.0f); ADD(ELEM_Er, 149, 148.94217f, 0.0f); ADD(ELEM_Er, 150, 149.93776f, 0.0f); ADD(ELEM_Er, 151, 150.93746f, 0.0f); ADD(ELEM_Er, 152, 151.935080f, 0.0f); ADD(ELEM_Er, 153, 152.935093f, 0.0f); ADD(ELEM_Er, 154, 153.932777f, 0.0f); ADD(ELEM_Er, 155, 154.933200f, 0.0f); ADD(ELEM_Er, 156, 155.931020f, 0.0f); ADD(ELEM_Er, 157, 156.931950f, 0.0f); ADD(ELEM_Er, 158, 157.92991f, 0.0f); ADD(ELEM_Er, 159, 158.930681f, 0.0f); ADD(ELEM_Er, 160, 159.929080f, 0.0f); ADD(ELEM_Er, 161, 160.930001f, 0.0f); ADD(ELEM_Er, 162, 161.928775f, 0.14f); ADD(ELEM_Er, 163, 162.930029f, 0.0f); ADD(ELEM_Er, 164, 163.929197f, 1.61f); ADD(ELEM_Er, 165, 164.930723f, 0.0f); ADD(ELEM_Er, 166, 165.930290f, 33.61f); ADD(ELEM_Er, 167, 166.932045f, 22.93f); ADD(ELEM_Er, 168, 167.932368f, 26.78f); ADD(ELEM_Er, 169, 168.934588f, 0.0f); ADD(ELEM_Er, 170, 169.935460f, 14.93f); ADD(ELEM_Er, 171, 170.938026f, 0.0f); ADD(ELEM_Er, 172, 171.939352f, 0.0f); ADD(ELEM_Er, 173, 172.94240f, 0.0f); ADD(ELEM_Er, 174, 173.94434f, 0.0f); ADD(ELEM_Er, 175, 174.94793f, 0.0f); ADD(ELEM_Er, 176, 175.95029f, 0.0f); ADD(ELEM_Er, 177, 176.95437f, 0.0f); SET(ELEM_Tm, NATURAL); ADD(ELEM_Tm, NATURAL, 168.93421f, 0); ADD(ELEM_Tm, 146, 145.96650f, 0.0f); ADD(ELEM_Tm, 147, 146.96108f, 0.0f); ADD(ELEM_Tm, 148, 147.95755f, 0.0f); ADD(ELEM_Tm, 149, 148.95265f, 0.0f); ADD(ELEM_Tm, 150, 149.94967f, 0.0f); ADD(ELEM_Tm, 151, 150.94543f, 0.0f); ADD(ELEM_Tm, 152, 151.94430f, 0.0f); ADD(ELEM_Tm, 153, 152.942028f, 0.0f); ADD(ELEM_Tm, 154, 153.94142f, 0.0f); ADD(ELEM_Tm, 155, 154.939192f, 0.0f); ADD(ELEM_Tm, 156, 155.939010f, 0.0f); ADD(ELEM_Tm, 157, 156.93676f, 0.0f); ADD(ELEM_Tm, 158, 157.93700f, 0.0f); ADD(ELEM_Tm, 159, 158.934810f, 0.0f); ADD(ELEM_Tm, 160, 159.93509f, 0.0f); ADD(ELEM_Tm, 161, 160.93340f, 0.0f); ADD(ELEM_Tm, 162, 161.933970f, 0.0f); ADD(ELEM_Tm, 163, 162.932648f, 0.0f); ADD(ELEM_Tm, 164, 163.933451f, 0.0f); ADD(ELEM_Tm, 165, 164.932432f, 0.0f); ADD(ELEM_Tm, 166, 165.933553f, 0.0f); ADD(ELEM_Tm, 167, 166.932849f, 0.0f); ADD(ELEM_Tm, 168, 167.934170f, 0.0f); ADD(ELEM_Tm, 169, 168.934211f, 100.0f); ADD(ELEM_Tm, 170, 169.935798f, 0.0f); ADD(ELEM_Tm, 171, 170.936426f, 0.0f); ADD(ELEM_Tm, 172, 171.938396f, 0.0f); ADD(ELEM_Tm, 173, 172.939600f, 0.0f); ADD(ELEM_Tm, 174, 173.942160f, 0.0f); ADD(ELEM_Tm, 175, 174.943830f, 0.0f); ADD(ELEM_Tm, 176, 175.94699f, 0.0f); ADD(ELEM_Tm, 177, 176.94904f, 0.0f); ADD(ELEM_Tm, 178, 177.95264f, 0.0f); ADD(ELEM_Tm, 179, 178.95534f, 0.0f); SET(ELEM_Yb, NATURAL); ADD(ELEM_Yb, NATURAL, 173.04f, 0); ADD(ELEM_Yb, 148, 147.96676f, 0.0f); ADD(ELEM_Yb, 149, 148.96348f, 0.0f); ADD(ELEM_Yb, 150, 149.95799f, 0.0f); ADD(ELEM_Yb, 151, 150.95525f, 0.0f); ADD(ELEM_Yb, 152, 151.95017f, 0.0f); ADD(ELEM_Yb, 153, 152.94921f, 0.0f); ADD(ELEM_Yb, 154, 153.94624f, 0.0f); ADD(ELEM_Yb, 155, 154.94579f, 0.0f); ADD(ELEM_Yb, 156, 155.942850f, 0.0f); ADD(ELEM_Yb, 157, 156.942660f, 0.0f); ADD(ELEM_Yb, 158, 157.939858f, 0.0f); ADD(ELEM_Yb, 159, 158.94015f, 0.0f); ADD(ELEM_Yb, 160, 159.93756f, 0.0f); ADD(ELEM_Yb, 161, 160.93785f, 0.0f); ADD(ELEM_Yb, 162, 161.93575f, 0.0f); ADD(ELEM_Yb, 163, 162.93627f, 0.0f); ADD(ELEM_Yb, 164, 163.93452f, 0.0f); ADD(ELEM_Yb, 165, 164.935398f, 0.0f); ADD(ELEM_Yb, 166, 165.933880f, 0.0f); ADD(ELEM_Yb, 167, 166.934947f, 0.0f); ADD(ELEM_Yb, 168, 167.933894f, 0.13f); ADD(ELEM_Yb, 169, 168.935187f, 0.0f); ADD(ELEM_Yb, 170, 169.934759f, 3.04f); ADD(ELEM_Yb, 171, 170.936322f, 14.28f); ADD(ELEM_Yb, 172, 171.9363777f, 21.83f); ADD(ELEM_Yb, 173, 172.9382068f, 16.13f); ADD(ELEM_Yb, 174, 173.9388581f, 31.83f); ADD(ELEM_Yb, 175, 174.9412725f, 0.0f); ADD(ELEM_Yb, 176, 175.942568f, 12.76f); ADD(ELEM_Yb, 177, 176.945257f, 0.0f); ADD(ELEM_Yb, 178, 177.946643f, 0.0f); ADD(ELEM_Yb, 179, 178.95017f, 0.0f); ADD(ELEM_Yb, 180, 179.95233f, 0.0f); ADD(ELEM_Yb, 181, 180.95615f, 0.0f); SET(ELEM_Lu, NATURAL); ADD(ELEM_Lu, NATURAL, 174.967f, 0); ADD(ELEM_Lu, 150, 149.97267f, 0.0f); ADD(ELEM_Lu, 151, 150.96715f, 0.0f); ADD(ELEM_Lu, 152, 151.96361f, 0.0f); ADD(ELEM_Lu, 153, 152.95869f, 0.0f); ADD(ELEM_Lu, 154, 153.95710f, 0.0f); ADD(ELEM_Lu, 155, 154.95423f, 0.0f); ADD(ELEM_Lu, 156, 155.95291f, 0.0f); ADD(ELEM_Lu, 157, 156.950102f, 0.0f); ADD(ELEM_Lu, 158, 157.94917f, 0.0f); ADD(ELEM_Lu, 159, 158.946620f, 0.0f); ADD(ELEM_Lu, 160, 159.94602f, 0.0f); ADD(ELEM_Lu, 161, 160.94354f, 0.0f); ADD(ELEM_Lu, 162, 161.94322f, 0.0f); ADD(ELEM_Lu, 163, 162.94120f, 0.0f); ADD(ELEM_Lu, 164, 163.94122f, 0.0f); ADD(ELEM_Lu, 165, 164.939610f, 0.0f); ADD(ELEM_Lu, 166, 165.93976f, 0.0f); ADD(ELEM_Lu, 167, 166.93831f, 0.0f); ADD(ELEM_Lu, 168, 167.938700f, 0.0f); ADD(ELEM_Lu, 169, 168.937649f, 0.0f); ADD(ELEM_Lu, 170, 169.938472f, 0.0f); ADD(ELEM_Lu, 171, 170.937910f, 0.0f); ADD(ELEM_Lu, 172, 171.939082f, 0.0f); ADD(ELEM_Lu, 173, 172.938927f, 0.0f); ADD(ELEM_Lu, 174, 173.9403335f, 0.0f); ADD(ELEM_Lu, 175, 174.9407679f, 97.41f); ADD(ELEM_Lu, 176, 175.9426824f, 2.59f); ADD(ELEM_Lu, 177, 176.9437550f, 0.0f); ADD(ELEM_Lu, 178, 177.945951f, 0.0f); ADD(ELEM_Lu, 179, 178.947324f, 0.0f); ADD(ELEM_Lu, 180, 179.949880f, 0.0f); ADD(ELEM_Lu, 181, 180.95197f, 0.0f); ADD(ELEM_Lu, 182, 181.95521f, 0.0f); ADD(ELEM_Lu, 183, 182.95757f, 0.0f); ADD(ELEM_Lu, 184, 183.96117f, 0.0f); SET(ELEM_Hf, NATURAL); ADD(ELEM_Hf, NATURAL, 178.49f, 0); ADD(ELEM_Hf, 154, 153.96425f, 0.0f); ADD(ELEM_Hf, 155, 154.96276f, 0.0f); ADD(ELEM_Hf, 156, 155.95925f, 0.0f); ADD(ELEM_Hf, 157, 156.95813f, 0.0f); ADD(ELEM_Hf, 158, 157.95465f, 0.0f); ADD(ELEM_Hf, 159, 158.95400f, 0.0f); ADD(ELEM_Hf, 160, 159.950710f, 0.0f); ADD(ELEM_Hf, 161, 160.950330f, 0.0f); ADD(ELEM_Hf, 162, 161.947203f, 0.0f); ADD(ELEM_Hf, 163, 162.94706f, 0.0f); ADD(ELEM_Hf, 164, 163.94442f, 0.0f); ADD(ELEM_Hf, 165, 164.94454f, 0.0f); ADD(ELEM_Hf, 166, 165.94225f, 0.0f); ADD(ELEM_Hf, 167, 166.94260f, 0.0f); ADD(ELEM_Hf, 168, 167.94063f, 0.0f); ADD(ELEM_Hf, 169, 168.941160f, 0.0f); ADD(ELEM_Hf, 170, 169.93965f, 0.0f); ADD(ELEM_Hf, 171, 170.94049f, 0.0f); ADD(ELEM_Hf, 172, 171.939460f, 0.0f); ADD(ELEM_Hf, 173, 172.94065f, 0.0f); ADD(ELEM_Hf, 174, 173.940040f, 0.16f); ADD(ELEM_Hf, 175, 174.941503f, 0.0f); ADD(ELEM_Hf, 176, 175.9414018f, 5.26f); ADD(ELEM_Hf, 177, 176.9432200f, 18.60f); ADD(ELEM_Hf, 178, 177.9436977f, 27.28f); ADD(ELEM_Hf, 179, 178.9458151f, 13.62f); ADD(ELEM_Hf, 180, 179.9465488f, 35.08f); ADD(ELEM_Hf, 181, 180.9490991f, 0.0f); ADD(ELEM_Hf, 182, 181.950553f, 0.0f); ADD(ELEM_Hf, 183, 182.953530f, 0.0f); ADD(ELEM_Hf, 184, 183.955450f, 0.0f); ADD(ELEM_Hf, 185, 184.95878f, 0.0f); ADD(ELEM_Hf, 186, 185.96092f, 0.0f); SET(ELEM_Ta, NATURAL); ADD(ELEM_Ta, NATURAL, 180.9479f, 0); ADD(ELEM_Ta, 156, 155.97169f, 0.0f); ADD(ELEM_Ta, 157, 156.96815f, 0.0f); ADD(ELEM_Ta, 158, 157.96637f, 0.0f); ADD(ELEM_Ta, 159, 158.96291f, 0.0f); ADD(ELEM_Ta, 160, 159.96136f, 0.0f); ADD(ELEM_Ta, 161, 160.958370f, 0.0f); ADD(ELEM_Ta, 162, 161.95715f, 0.0f); ADD(ELEM_Ta, 163, 162.954320f, 0.0f); ADD(ELEM_Ta, 164, 163.95357f, 0.0f); ADD(ELEM_Ta, 165, 164.95082f, 0.0f); ADD(ELEM_Ta, 166, 165.95047f, 0.0f); ADD(ELEM_Ta, 167, 166.94797f, 0.0f); ADD(ELEM_Ta, 168, 167.94779f, 0.0f); ADD(ELEM_Ta, 169, 168.94592f, 0.0f); ADD(ELEM_Ta, 170, 169.94609f, 0.0f); ADD(ELEM_Ta, 171, 170.94446f, 0.0f); ADD(ELEM_Ta, 172, 171.94474f, 0.0f); ADD(ELEM_Ta, 173, 172.94354f, 0.0f); ADD(ELEM_Ta, 174, 173.944170f, 0.0f); ADD(ELEM_Ta, 175, 174.94365f, 0.0f); ADD(ELEM_Ta, 176, 175.94474f, 0.0f); ADD(ELEM_Ta, 177, 176.944472f, 0.0f); ADD(ELEM_Ta, 178, 177.94575f, 0.0f); ADD(ELEM_Ta, 179, 178.945934f, 0.0f); ADD(ELEM_Ta, 180, 179.947466f, 0.012f); ADD(ELEM_Ta, 181, 180.947996f, 99.988f); ADD(ELEM_Ta, 182, 181.950152f, 0.0f); ADD(ELEM_Ta, 183, 182.951373f, 0.0f); ADD(ELEM_Ta, 184, 183.954009f, 0.0f); ADD(ELEM_Ta, 185, 184.955559f, 0.0f); ADD(ELEM_Ta, 186, 185.958550f, 0.0f); ADD(ELEM_Ta, 187, 186.96041f, 0.0f); ADD(ELEM_Ta, 188, 187.96371f, 0.0f); SET(ELEM_W, NATURAL); ADD(ELEM_W, NATURAL, 183.84f, 0); ADD(ELEM_W, 158, 157.97394f, 0.0f); ADD(ELEM_W, 159, 158.97228f, 0.0f); ADD(ELEM_W, 160, 159.96837f, 0.0f); ADD(ELEM_W, 161, 160.96709f, 0.0f); ADD(ELEM_W, 162, 161.96334f, 0.0f); ADD(ELEM_W, 163, 162.96253f, 0.0f); ADD(ELEM_W, 164, 163.958980f, 0.0f); ADD(ELEM_W, 165, 164.958340f, 0.0f); ADD(ELEM_W, 166, 165.955020f, 0.0f); ADD(ELEM_W, 167, 166.95467f, 0.0f); ADD(ELEM_W, 168, 167.95186f, 0.0f); ADD(ELEM_W, 169, 168.95176f, 0.0f); ADD(ELEM_W, 170, 169.94929f, 0.0f); ADD(ELEM_W, 171, 170.94946f, 0.0f); ADD(ELEM_W, 172, 171.94742f, 0.0f); ADD(ELEM_W, 173, 172.94783f, 0.0f); ADD(ELEM_W, 174, 173.94616f, 0.0f); ADD(ELEM_W, 175, 174.94677f, 0.0f); ADD(ELEM_W, 176, 175.94559f, 0.0f); ADD(ELEM_W, 177, 176.94662f, 0.0f); ADD(ELEM_W, 178, 177.94585f, 0.0f); ADD(ELEM_W, 179, 178.947072f, 0.0f); ADD(ELEM_W, 180, 179.946706f, 0.12f); ADD(ELEM_W, 181, 180.948198f, 0.0f); ADD(ELEM_W, 182, 181.948206f, 26.50f); ADD(ELEM_W, 183, 182.9502245f, 14.31f); ADD(ELEM_W, 184, 183.9509326f, 30.64f); ADD(ELEM_W, 185, 184.9534206f, 0.0f); ADD(ELEM_W, 186, 185.954362f, 28.43f); ADD(ELEM_W, 187, 186.957158f, 0.0f); ADD(ELEM_W, 188, 187.958487f, 0.0f); ADD(ELEM_W, 189, 188.96191f, 0.0f); ADD(ELEM_W, 190, 189.96318f, 0.0f); SET(ELEM_Re, NATURAL); ADD(ELEM_Re, NATURAL, 186.207f, 0); ADD(ELEM_Re, 160, 159.98149f, 0.0f); ADD(ELEM_Re, 161, 160.97766f, 0.0f); ADD(ELEM_Re, 162, 161.97571f, 0.0f); ADD(ELEM_Re, 163, 162.97197f, 0.0f); ADD(ELEM_Re, 164, 163.97032f, 0.0f); ADD(ELEM_Re, 165, 164.967050f, 0.0f); ADD(ELEM_Re, 166, 165.96580f, 0.0f); ADD(ELEM_Re, 167, 166.96256f, 0.0f); ADD(ELEM_Re, 168, 167.96161f, 0.0f); ADD(ELEM_Re, 169, 168.95883f, 0.0f); ADD(ELEM_Re, 170, 169.95816f, 0.0f); ADD(ELEM_Re, 171, 170.95555f, 0.0f); ADD(ELEM_Re, 172, 171.95529f, 0.0f); ADD(ELEM_Re, 173, 172.95306f, 0.0f); ADD(ELEM_Re, 174, 173.95311f, 0.0f); ADD(ELEM_Re, 175, 174.95139f, 0.0f); ADD(ELEM_Re, 176, 175.95157f, 0.0f); ADD(ELEM_Re, 177, 176.95027f, 0.0f); ADD(ELEM_Re, 178, 177.95085f, 0.0f); ADD(ELEM_Re, 179, 178.949980f, 0.0f); ADD(ELEM_Re, 180, 179.950790f, 0.0f); ADD(ELEM_Re, 181, 180.950065f, 0.0f); ADD(ELEM_Re, 182, 181.95121f, 0.0f); ADD(ELEM_Re, 183, 182.950821f, 0.0f); ADD(ELEM_Re, 184, 183.952524f, 0.0f); ADD(ELEM_Re, 185, 184.9529557f, 37.40f); ADD(ELEM_Re, 186, 185.954987f, 0.0f); ADD(ELEM_Re, 187, 186.9557508f, 62.60f); ADD(ELEM_Re, 188, 187.9581123f, 0.0f); ADD(ELEM_Re, 189, 188.959228f, 0.0f); ADD(ELEM_Re, 190, 189.96182f, 0.0f); ADD(ELEM_Re, 191, 190.963124f, 0.0f); ADD(ELEM_Re, 192, 191.96596f, 0.0f); SET(ELEM_Os, NATURAL); ADD(ELEM_Os, NATURAL, 190.23f, 0); ADD(ELEM_Os, 162, 161.98382f, 0.0f); ADD(ELEM_Os, 163, 162.98205f, 0.0f); ADD(ELEM_Os, 164, 163.97793f, 0.0f); ADD(ELEM_Os, 165, 164.97648f, 0.0f); ADD(ELEM_Os, 166, 165.97253f, 0.0f); ADD(ELEM_Os, 167, 166.97155f, 0.0f); ADD(ELEM_Os, 168, 167.967830f, 0.0f); ADD(ELEM_Os, 169, 168.967080f, 0.0f); ADD(ELEM_Os, 170, 169.963570f, 0.0f); ADD(ELEM_Os, 171, 170.96304f, 0.0f); ADD(ELEM_Os, 172, 171.96008f, 0.0f); ADD(ELEM_Os, 173, 172.95979f, 0.0f); ADD(ELEM_Os, 174, 173.95712f, 0.0f); ADD(ELEM_Os, 175, 174.95708f, 0.0f); ADD(ELEM_Os, 176, 175.95495f, 0.0f); ADD(ELEM_Os, 177, 176.95505f, 0.0f); ADD(ELEM_Os, 178, 177.95335f, 0.0f); ADD(ELEM_Os, 179, 178.95395f, 0.0f); ADD(ELEM_Os, 180, 179.95235f, 0.0f); ADD(ELEM_Os, 181, 180.95327f, 0.0f); ADD(ELEM_Os, 182, 181.952186f, 0.0f); ADD(ELEM_Os, 183, 182.95311f, 0.0f); ADD(ELEM_Os, 184, 183.952491f, 0.02f); ADD(ELEM_Os, 185, 184.954043f, 0.0f); ADD(ELEM_Os, 186, 185.953838f, 1.59f); ADD(ELEM_Os, 187, 186.9557479f, 1.96f); ADD(ELEM_Os, 188, 187.9558360f, 13.24f); ADD(ELEM_Os, 189, 188.9581449f, 16.15f); ADD(ELEM_Os, 190, 189.958445f, 26.26f); ADD(ELEM_Os, 191, 190.960928f, 0.0f); ADD(ELEM_Os, 192, 191.961479f, 40.78f); ADD(ELEM_Os, 193, 192.964148f, 0.0f); ADD(ELEM_Os, 194, 193.965179f, 0.0f); ADD(ELEM_Os, 195, 194.96812f, 0.0f); ADD(ELEM_Os, 196, 195.969620f, 0.0f); SET(ELEM_Ir, NATURAL); ADD(ELEM_Ir, NATURAL, 192.217f, 0); ADD(ELEM_Ir, 165, 164.98758f, 0.0f); ADD(ELEM_Ir, 166, 165.98551f, 0.0f); ADD(ELEM_Ir, 167, 166.98154f, 0.0f); ADD(ELEM_Ir, 168, 167.97997f, 0.0f); ADD(ELEM_Ir, 169, 168.97639f, 0.0f); ADD(ELEM_Ir, 170, 169.97503f, 0.0f); ADD(ELEM_Ir, 171, 170.97178f, 0.0f); ADD(ELEM_Ir, 172, 171.97064f, 0.0f); ADD(ELEM_Ir, 173, 172.96771f, 0.0f); ADD(ELEM_Ir, 174, 173.96680f, 0.0f); ADD(ELEM_Ir, 175, 174.96428f, 0.0f); ADD(ELEM_Ir, 176, 175.96351f, 0.0f); ADD(ELEM_Ir, 177, 176.96117f, 0.0f); ADD(ELEM_Ir, 178, 177.96108f, 0.0f); ADD(ELEM_Ir, 179, 178.95915f, 0.0f); ADD(ELEM_Ir, 180, 179.95925f, 0.0f); ADD(ELEM_Ir, 181, 180.95764f, 0.0f); ADD(ELEM_Ir, 182, 181.95813f, 0.0f); ADD(ELEM_Ir, 183, 182.95681f, 0.0f); ADD(ELEM_Ir, 184, 183.95739f, 0.0f); ADD(ELEM_Ir, 185, 184.95659f, 0.0f); ADD(ELEM_Ir, 186, 185.957951f, 0.0f); ADD(ELEM_Ir, 187, 186.957361f, 0.0f); ADD(ELEM_Ir, 188, 187.958852f, 0.0f); ADD(ELEM_Ir, 189, 188.958716f, 0.0f); ADD(ELEM_Ir, 190, 189.96059f, 0.0f); ADD(ELEM_Ir, 191, 190.960591f, 37.3f); ADD(ELEM_Ir, 192, 191.962602f, 0.0f); ADD(ELEM_Ir, 193, 192.962924f, 62.7f); ADD(ELEM_Ir, 194, 193.965076f, 0.0f); ADD(ELEM_Ir, 195, 194.965977f, 0.0f); ADD(ELEM_Ir, 196, 195.968380f, 0.0f); ADD(ELEM_Ir, 197, 196.969636f, 0.0f); ADD(ELEM_Ir, 198, 197.97228f, 0.0f); ADD(ELEM_Ir, 199, 198.973790f, 0.0f); SET(ELEM_Pt, NATURAL); ADD(ELEM_Pt, NATURAL, 195.078f, 0); ADD(ELEM_Pt, 168, 167.98804f, 0.0f); ADD(ELEM_Pt, 169, 168.98642f, 0.0f); ADD(ELEM_Pt, 170, 169.98233f, 0.0f); ADD(ELEM_Pt, 171, 170.98125f, 0.0f); ADD(ELEM_Pt, 172, 171.977380f, 0.0f); ADD(ELEM_Pt, 173, 172.97650f, 0.0f); ADD(ELEM_Pt, 174, 173.972811f, 0.0f); ADD(ELEM_Pt, 175, 174.97228f, 0.0f); ADD(ELEM_Pt, 176, 175.96900f, 0.0f); ADD(ELEM_Pt, 177, 176.96845f, 0.0f); ADD(ELEM_Pt, 178, 177.96571f, 0.0f); ADD(ELEM_Pt, 179, 178.96548f, 0.0f); ADD(ELEM_Pt, 180, 179.96322f, 0.0f); ADD(ELEM_Pt, 181, 180.96318f, 0.0f); ADD(ELEM_Pt, 182, 181.96127f, 0.0f); ADD(ELEM_Pt, 183, 182.96173f, 0.0f); ADD(ELEM_Pt, 184, 183.95990f, 0.0f); ADD(ELEM_Pt, 185, 184.96075f, 0.0f); ADD(ELEM_Pt, 186, 185.959430f, 0.0f); ADD(ELEM_Pt, 187, 186.96056f, 0.0f); ADD(ELEM_Pt, 188, 187.959396f, 0.0f); ADD(ELEM_Pt, 189, 188.960832f, 0.0f); ADD(ELEM_Pt, 190, 189.959930f, 0.014f); ADD(ELEM_Pt, 191, 190.961685f, 0.0f); ADD(ELEM_Pt, 192, 191.961035f, 0.782f); ADD(ELEM_Pt, 193, 192.962985f, 0.0f); ADD(ELEM_Pt, 194, 193.962664f, 32.967f); ADD(ELEM_Pt, 195, 194.964774f, 33.832f); ADD(ELEM_Pt, 196, 195.964935f, 25.242f); ADD(ELEM_Pt, 197, 196.967323f, 0.0f); ADD(ELEM_Pt, 198, 197.967876f, 7.163f); ADD(ELEM_Pt, 199, 198.970576f, 0.0f); ADD(ELEM_Pt, 200, 199.971424f, 0.0f); ADD(ELEM_Pt, 201, 200.974500f, 0.0f); ADD(ELEM_Pt, 202, 201.97574f, 0.0f); SET(ELEM_Au, NATURAL); ADD(ELEM_Au, NATURAL, 196.96655f, 0); ADD(ELEM_Au, 171, 170.99177f, 0.0f); ADD(ELEM_Au, 172, 171.99011f, 0.0f); ADD(ELEM_Au, 173, 172.98640f, 0.0f); ADD(ELEM_Au, 174, 173.98492f, 0.0f); ADD(ELEM_Au, 175, 174.98155f, 0.0f); ADD(ELEM_Au, 176, 175.98027f, 0.0f); ADD(ELEM_Au, 177, 176.97722f, 0.0f); ADD(ELEM_Au, 178, 177.97598f, 0.0f); ADD(ELEM_Au, 179, 178.97341f, 0.0f); ADD(ELEM_Au, 180, 179.97240f, 0.0f); ADD(ELEM_Au, 181, 180.96995f, 0.0f); ADD(ELEM_Au, 182, 181.96962f, 0.0f); ADD(ELEM_Au, 183, 182.96762f, 0.0f); ADD(ELEM_Au, 184, 183.96747f, 0.0f); ADD(ELEM_Au, 185, 184.96581f, 0.0f); ADD(ELEM_Au, 186, 185.96600f, 0.0f); ADD(ELEM_Au, 187, 186.96456f, 0.0f); ADD(ELEM_Au, 188, 187.96509f, 0.0f); ADD(ELEM_Au, 189, 188.96389f, 0.0f); ADD(ELEM_Au, 190, 189.964699f, 0.0f); ADD(ELEM_Au, 191, 190.963650f, 0.0f); ADD(ELEM_Au, 192, 191.964810f, 0.0f); ADD(ELEM_Au, 193, 192.964132f, 0.0f); ADD(ELEM_Au, 194, 193.965339f, 0.0f); ADD(ELEM_Au, 195, 194.965018f, 0.0f); ADD(ELEM_Au, 196, 195.966551f, 0.0f); ADD(ELEM_Au, 197, 196.966552f, 100.0f); ADD(ELEM_Au, 198, 197.968225f, 0.0f); ADD(ELEM_Au, 199, 198.968748f, 0.0f); ADD(ELEM_Au, 200, 199.970720f, 0.0f); ADD(ELEM_Au, 201, 200.971641f, 0.0f); ADD(ELEM_Au, 202, 201.97379f, 0.0f); ADD(ELEM_Au, 203, 202.975137f, 0.0f); ADD(ELEM_Au, 204, 203.97771f, 0.0f); ADD(ELEM_Au, 205, 204.97961f, 0.0f); SET(ELEM_Hg, NATURAL); ADD(ELEM_Hg, NATURAL, 200.59f, 0); ADD(ELEM_Hg, 175, 174.99141f, 0.0f); ADD(ELEM_Hg, 176, 175.987410f, 0.0f); ADD(ELEM_Hg, 177, 176.98634f, 0.0f); ADD(ELEM_Hg, 178, 177.982476f, 0.0f); ADD(ELEM_Hg, 179, 178.98178f, 0.0f); ADD(ELEM_Hg, 180, 179.97832f, 0.0f); ADD(ELEM_Hg, 181, 180.97781f, 0.0f); ADD(ELEM_Hg, 182, 181.97475f, 0.0f); ADD(ELEM_Hg, 183, 182.97456f, 0.0f); ADD(ELEM_Hg, 184, 183.97190f, 0.0f); ADD(ELEM_Hg, 185, 184.97198f, 0.0f); ADD(ELEM_Hg, 186, 185.96946f, 0.0f); ADD(ELEM_Hg, 187, 186.96979f, 0.0f); ADD(ELEM_Hg, 188, 187.96756f, 0.0f); ADD(ELEM_Hg, 189, 188.96813f, 0.0f); ADD(ELEM_Hg, 190, 189.96628f, 0.0f); ADD(ELEM_Hg, 191, 190.967060f, 0.0f); ADD(ELEM_Hg, 192, 191.96557f, 0.0f); ADD(ELEM_Hg, 193, 192.966644f, 0.0f); ADD(ELEM_Hg, 194, 193.965382f, 0.0f); ADD(ELEM_Hg, 195, 194.966640f, 0.0f); ADD(ELEM_Hg, 196, 195.965815f, 0.15f); ADD(ELEM_Hg, 197, 196.967195f, 0.0f); ADD(ELEM_Hg, 198, 197.966752f, 9.97f); ADD(ELEM_Hg, 199, 198.968262f, 16.87f); ADD(ELEM_Hg, 200, 199.968309f, 23.10f); ADD(ELEM_Hg, 201, 200.970285f, 13.18f); ADD(ELEM_Hg, 202, 201.970626f, 29.86f); ADD(ELEM_Hg, 203, 202.972857f, 0.0f); ADD(ELEM_Hg, 204, 203.973476f, 6.87f); ADD(ELEM_Hg, 205, 204.976056f, 0.0f); ADD(ELEM_Hg, 206, 205.977499f, 0.0f); ADD(ELEM_Hg, 207, 206.98258f, 0.0f); ADD(ELEM_Hg, 208, 207.98594f, 0.0f); SET(ELEM_Tl, NATURAL); ADD(ELEM_Tl, NATURAL, 204.3833f, 0); ADD(ELEM_Tl, 177, 176.99688f, 0.0f); ADD(ELEM_Tl, 178, 177.99523f, 0.0f); ADD(ELEM_Tl, 179, 178.99147f, 0.0f); ADD(ELEM_Tl, 180, 179.99019f, 0.0f); ADD(ELEM_Tl, 181, 180.98690f, 0.0f); ADD(ELEM_Tl, 182, 181.98561f, 0.0f); ADD(ELEM_Tl, 183, 182.98270f, 0.0f); ADD(ELEM_Tl, 184, 183.98176f, 0.0f); ADD(ELEM_Tl, 185, 184.97910f, 0.0f); ADD(ELEM_Tl, 186, 185.97855f, 0.0f); ADD(ELEM_Tl, 187, 186.97617f, 0.0f); ADD(ELEM_Tl, 188, 187.97592f, 0.0f); ADD(ELEM_Tl, 189, 188.97369f, 0.0f); ADD(ELEM_Tl, 190, 189.97379f, 0.0f); ADD(ELEM_Tl, 191, 190.97189f, 0.0f); ADD(ELEM_Tl, 192, 191.97214f, 0.0f); ADD(ELEM_Tl, 193, 192.97055f, 0.0f); ADD(ELEM_Tl, 194, 193.97105f, 0.0f); ADD(ELEM_Tl, 195, 194.96965f, 0.0f); ADD(ELEM_Tl, 196, 195.97052f, 0.0f); ADD(ELEM_Tl, 197, 196.969540f, 0.0f); ADD(ELEM_Tl, 198, 197.970470f, 0.0f); ADD(ELEM_Tl, 199, 198.96981f, 0.0f); ADD(ELEM_Tl, 200, 199.970945f, 0.0f); ADD(ELEM_Tl, 201, 200.970804f, 0.0f); ADD(ELEM_Tl, 202, 201.972091f, 0.0f); ADD(ELEM_Tl, 203, 202.972329f, 29.524f); ADD(ELEM_Tl, 204, 203.973849f, 0.0f); ADD(ELEM_Tl, 205, 204.974412f, 70.476f); ADD(ELEM_Tl, 206, 205.976095f, 0.0f); ADD(ELEM_Tl, 207, 206.977408f, 0.0f); ADD(ELEM_Tl, 208, 207.982005f, 0.0f); ADD(ELEM_Tl, 209, 208.985349f, 0.0f); ADD(ELEM_Tl, 210, 209.990066f, 0.0f); SET(ELEM_Pb, NATURAL); ADD(ELEM_Pb, NATURAL, 207.2f, 0); ADD(ELEM_Pb, 181, 180.99671f, 0.0f); ADD(ELEM_Pb, 182, 181.992676f, 0.0f); ADD(ELEM_Pb, 183, 182.99193f, 0.0f); ADD(ELEM_Pb, 184, 183.98820f, 0.0f); ADD(ELEM_Pb, 185, 184.98758f, 0.0f); ADD(ELEM_Pb, 186, 185.98430f, 0.0f); ADD(ELEM_Pb, 187, 186.98403f, 0.0f); ADD(ELEM_Pb, 188, 187.98106f, 0.0f); ADD(ELEM_Pb, 189, 188.98088f, 0.0f); ADD(ELEM_Pb, 190, 189.97818f, 0.0f); ADD(ELEM_Pb, 191, 190.97820f, 0.0f); ADD(ELEM_Pb, 192, 191.97576f, 0.0f); ADD(ELEM_Pb, 193, 192.97608f, 0.0f); ADD(ELEM_Pb, 194, 193.97397f, 0.0f); ADD(ELEM_Pb, 195, 194.97447f, 0.0f); ADD(ELEM_Pb, 196, 195.97271f, 0.0f); ADD(ELEM_Pb, 197, 196.97338f, 0.0f); ADD(ELEM_Pb, 198, 197.97198f, 0.0f); ADD(ELEM_Pb, 199, 198.972910f, 0.0f); ADD(ELEM_Pb, 200, 199.971816f, 0.0f); ADD(ELEM_Pb, 201, 200.972850f, 0.0f); ADD(ELEM_Pb, 202, 201.972144f, 0.0f); ADD(ELEM_Pb, 203, 202.973375f, 0.0f); ADD(ELEM_Pb, 204, 203.973029f, 1.4f); ADD(ELEM_Pb, 205, 204.974467f, 0.0f); ADD(ELEM_Pb, 206, 205.974449f, 24.1f); ADD(ELEM_Pb, 207, 206.975881f, 22.1f); ADD(ELEM_Pb, 208, 207.976636f, 52.4f); ADD(ELEM_Pb, 209, 208.981075f, 0.0f); ADD(ELEM_Pb, 210, 209.984173f, 0.0f); ADD(ELEM_Pb, 211, 210.988731f, 0.0f); ADD(ELEM_Pb, 212, 211.9918875f, 0.0f); ADD(ELEM_Pb, 213, 212.99650f, 0.0f); ADD(ELEM_Pb, 214, 213.9997981f, 0.0f); SET(ELEM_Bi, NATURAL); ADD(ELEM_Bi, NATURAL, 208.98038f, 0); ADD(ELEM_Bi, 185, 184.99771f, 0.0f); ADD(ELEM_Bi, 186, 185.99648f, 0.0f); ADD(ELEM_Bi, 187, 186.99346f, 0.0f); ADD(ELEM_Bi, 188, 187.99217f, 0.0f); ADD(ELEM_Bi, 189, 188.98951f, 0.0f); ADD(ELEM_Bi, 190, 189.98852f, 0.0f); ADD(ELEM_Bi, 191, 190.98605f, 0.0f); ADD(ELEM_Bi, 192, 191.98537f, 0.0f); ADD(ELEM_Bi, 193, 192.98306f, 0.0f); ADD(ELEM_Bi, 194, 193.98275f, 0.0f); ADD(ELEM_Bi, 195, 194.98075f, 0.0f); ADD(ELEM_Bi, 196, 195.98061f, 0.0f); ADD(ELEM_Bi, 197, 196.97893f, 0.0f); ADD(ELEM_Bi, 198, 197.97902f, 0.0f); ADD(ELEM_Bi, 199, 198.97758f, 0.0f); ADD(ELEM_Bi, 200, 199.97814f, 0.0f); ADD(ELEM_Bi, 201, 200.976970f, 0.0f); ADD(ELEM_Bi, 202, 201.977670f, 0.0f); ADD(ELEM_Bi, 203, 202.976868f, 0.0f); ADD(ELEM_Bi, 204, 203.977805f, 0.0f); ADD(ELEM_Bi, 205, 204.977375f, 0.0f); ADD(ELEM_Bi, 206, 205.978483f, 0.0f); ADD(ELEM_Bi, 207, 206.978455f, 0.0f); ADD(ELEM_Bi, 208, 207.979727f, 0.0f); ADD(ELEM_Bi, 209, 208.980383f, 100.0f); ADD(ELEM_Bi, 210, 209.984105f, 0.0f); ADD(ELEM_Bi, 211, 210.987258f, 0.0f); ADD(ELEM_Bi, 212, 211.991272f, 0.0f); ADD(ELEM_Bi, 213, 212.994375f, 0.0f); ADD(ELEM_Bi, 214, 213.998699f, 0.0f); ADD(ELEM_Bi, 215, 215.00183f, 0.0f); ADD(ELEM_Bi, 216, 216.00620f, 0.0f); SET(ELEM_Po, 209); ADD(ELEM_Po, 190, 189.99511f, 0.0f); ADD(ELEM_Po, 191, 190.99465f, 0.0f); ADD(ELEM_Po, 192, 191.99152f, 0.0f); ADD(ELEM_Po, 193, 192.99110f, 0.0f); ADD(ELEM_Po, 194, 193.98828f, 0.0f); ADD(ELEM_Po, 195, 194.98805f, 0.0f); ADD(ELEM_Po, 196, 195.98551f, 0.0f); ADD(ELEM_Po, 197, 196.98557f, 0.0f); ADD(ELEM_Po, 198, 197.98334f, 0.0f); ADD(ELEM_Po, 199, 198.98360f, 0.0f); ADD(ELEM_Po, 200, 199.98174f, 0.0f); ADD(ELEM_Po, 201, 200.98221f, 0.0f); ADD(ELEM_Po, 202, 201.98070f, 0.0f); ADD(ELEM_Po, 203, 202.981410f, 0.0f); ADD(ELEM_Po, 204, 203.980307f, 0.0f); ADD(ELEM_Po, 205, 204.981170f, 0.0f); ADD(ELEM_Po, 206, 205.980465f, 0.0f); ADD(ELEM_Po, 207, 206.981578f, 0.0f); ADD(ELEM_Po, 208, 207.981231f, 0.0f); ADD(ELEM_Po, 209, 208.982416f, 0.0f); ADD(ELEM_Po, 210, 209.982857f, 0.0f); ADD(ELEM_Po, 211, 210.986637f, 0.0f); ADD(ELEM_Po, 212, 211.988852f, 0.0f); ADD(ELEM_Po, 213, 212.992843f, 0.0f); ADD(ELEM_Po, 214, 213.995186f, 0.0f); ADD(ELEM_Po, 215, 214.999415f, 0.0f); ADD(ELEM_Po, 216, 216.0019052f, 0.0f); ADD(ELEM_Po, 217, 217.00625f, 0.0f); ADD(ELEM_Po, 218, 218.0089658f, 0.0f); SET(ELEM_At, 210); ADD(ELEM_At, 193, 193.00019f, 0.0f); ADD(ELEM_At, 194, 193.99897f, 0.0f); ADD(ELEM_At, 195, 194.99655f, 0.0f); ADD(ELEM_At, 196, 195.99570f, 0.0f); ADD(ELEM_At, 197, 196.99329f, 0.0f); ADD(ELEM_At, 198, 197.99275f, 0.0f); ADD(ELEM_At, 199, 198.99063f, 0.0f); ADD(ELEM_At, 200, 199.99029f, 0.0f); ADD(ELEM_At, 201, 200.98849f, 0.0f); ADD(ELEM_At, 202, 201.98845f, 0.0f); ADD(ELEM_At, 203, 202.98685f, 0.0f); ADD(ELEM_At, 204, 203.98726f, 0.0f); ADD(ELEM_At, 205, 204.986040f, 0.0f); ADD(ELEM_At, 206, 205.986600f, 0.0f); ADD(ELEM_At, 207, 206.985776f, 0.0f); ADD(ELEM_At, 208, 207.986583f, 0.0f); ADD(ELEM_At, 209, 208.986159f, 0.0f); ADD(ELEM_At, 210, 209.987131f, 0.0f); ADD(ELEM_At, 211, 210.987481f, 0.0f); ADD(ELEM_At, 212, 211.990735f, 0.0f); ADD(ELEM_At, 213, 212.992921f, 0.0f); ADD(ELEM_At, 214, 213.996356f, 0.0f); ADD(ELEM_At, 215, 214.998641f, 0.0f); ADD(ELEM_At, 216, 216.002409f, 0.0f); ADD(ELEM_At, 217, 217.004710f, 0.0f); ADD(ELEM_At, 218, 218.008681f, 0.0f); ADD(ELEM_At, 219, 219.011300f, 0.0f); ADD(ELEM_At, 220, 220.01530f, 0.0f); ADD(ELEM_At, 221, 221.01814f, 0.0f); ADD(ELEM_At, 222, 222.02233f, 0.0f); ADD(ELEM_At, 223, 223.02534f, 0.0f); SET(ELEM_Rn, 222); ADD(ELEM_Rn, 196, 196.00231f, 0.0f); ADD(ELEM_Rn, 197, 197.00166f, 0.0f); ADD(ELEM_Rn, 198, 197.99878f, 0.0f); ADD(ELEM_Rn, 199, 198.99831f, 0.0f); ADD(ELEM_Rn, 200, 199.99568f, 0.0f); ADD(ELEM_Rn, 201, 200.99554f, 0.0f); ADD(ELEM_Rn, 202, 201.99322f, 0.0f); ADD(ELEM_Rn, 203, 202.99332f, 0.0f); ADD(ELEM_Rn, 204, 203.99137f, 0.0f); ADD(ELEM_Rn, 205, 204.99167f, 0.0f); ADD(ELEM_Rn, 206, 205.99016f, 0.0f); ADD(ELEM_Rn, 207, 206.990730f, 0.0f); ADD(ELEM_Rn, 208, 207.989631f, 0.0f); ADD(ELEM_Rn, 209, 208.990380f, 0.0f); ADD(ELEM_Rn, 210, 209.989680f, 0.0f); ADD(ELEM_Rn, 211, 210.990585f, 0.0f); ADD(ELEM_Rn, 212, 211.990689f, 0.0f); ADD(ELEM_Rn, 213, 212.993868f, 0.0f); ADD(ELEM_Rn, 214, 213.995346f, 0.0f); ADD(ELEM_Rn, 215, 214.998729f, 0.0f); ADD(ELEM_Rn, 216, 216.000258f, 0.0f); ADD(ELEM_Rn, 217, 217.003915f, 0.0f); ADD(ELEM_Rn, 218, 218.005586f, 0.0f); ADD(ELEM_Rn, 219, 219.009475f, 0.0f); ADD(ELEM_Rn, 220, 220.0113841f, 0.0f); ADD(ELEM_Rn, 221, 221.01546f, 0.0f); ADD(ELEM_Rn, 222, 222.0175705f, 0.0f); ADD(ELEM_Rn, 223, 223.02179f, 0.0f); ADD(ELEM_Rn, 224, 224.02409f, 0.0f); ADD(ELEM_Rn, 225, 225.02844f, 0.0f); ADD(ELEM_Rn, 226, 226.03089f, 0.0f); ADD(ELEM_Rn, 227, 227.03541f, 0.0f); ADD(ELEM_Rn, 228, 228.03808f, 0.0f); SET(ELEM_Fr, 223); ADD(ELEM_Fr, 200, 200.00650f, 0.0f); ADD(ELEM_Fr, 201, 201.00399f, 0.0f); ADD(ELEM_Fr, 202, 202.00329f, 0.0f); ADD(ELEM_Fr, 203, 203.00105f, 0.0f); ADD(ELEM_Fr, 204, 204.00059f, 0.0f); ADD(ELEM_Fr, 205, 204.99866f, 0.0f); ADD(ELEM_Fr, 206, 205.99849f, 0.0f); ADD(ELEM_Fr, 207, 206.99686f, 0.0f); ADD(ELEM_Fr, 208, 207.997130f, 0.0f); ADD(ELEM_Fr, 209, 208.995920f, 0.0f); ADD(ELEM_Fr, 210, 209.996398f, 0.0f); ADD(ELEM_Fr, 211, 210.995529f, 0.0f); ADD(ELEM_Fr, 212, 211.996195f, 0.0f); ADD(ELEM_Fr, 213, 212.996175f, 0.0f); ADD(ELEM_Fr, 214, 213.998955f, 0.0f); ADD(ELEM_Fr, 215, 215.000326f, 0.0f); ADD(ELEM_Fr, 216, 216.003188f, 0.0f); ADD(ELEM_Fr, 217, 217.004616f, 0.0f); ADD(ELEM_Fr, 218, 218.007563f, 0.0f); ADD(ELEM_Fr, 219, 219.009241f, 0.0f); ADD(ELEM_Fr, 220, 220.012313f, 0.0f); ADD(ELEM_Fr, 221, 221.014246f, 0.0f); ADD(ELEM_Fr, 222, 222.017544f, 0.0f); ADD(ELEM_Fr, 223, 223.0197307f, 0.0f); ADD(ELEM_Fr, 224, 224.023240f, 0.0f); ADD(ELEM_Fr, 225, 225.025607f, 0.0f); ADD(ELEM_Fr, 226, 226.02934f, 0.0f); ADD(ELEM_Fr, 227, 227.03183f, 0.0f); ADD(ELEM_Fr, 228, 228.03572f, 0.0f); ADD(ELEM_Fr, 229, 229.03843f, 0.0f); ADD(ELEM_Fr, 230, 230.04251f, 0.0f); ADD(ELEM_Fr, 231, 231.04541f, 0.0f); ADD(ELEM_Fr, 232, 232.04965f, 0.0f); SET(ELEM_Ra, 226); ADD(ELEM_Ra, 203, 203.00921f, 0.0f); ADD(ELEM_Ra, 204, 204.00648f, 0.0f); ADD(ELEM_Ra, 205, 205.00619f, 0.0f); ADD(ELEM_Ra, 206, 206.00378f, 0.0f); ADD(ELEM_Ra, 207, 207.00373f, 0.0f); ADD(ELEM_Ra, 208, 208.00178f, 0.0f); ADD(ELEM_Ra, 209, 209.00194f, 0.0f); ADD(ELEM_Ra, 210, 210.00045f, 0.0f); ADD(ELEM_Ra, 211, 211.000890f, 0.0f); ADD(ELEM_Ra, 212, 211.999783f, 0.0f); ADD(ELEM_Ra, 213, 213.000350f, 0.0f); ADD(ELEM_Ra, 214, 214.000091f, 0.0f); ADD(ELEM_Ra, 215, 215.002704f, 0.0f); ADD(ELEM_Ra, 216, 216.003518f, 0.0f); ADD(ELEM_Ra, 217, 217.006306f, 0.0f); ADD(ELEM_Ra, 218, 218.007124f, 0.0f); ADD(ELEM_Ra, 219, 219.010069f, 0.0f); ADD(ELEM_Ra, 220, 220.011015f, 0.0f); ADD(ELEM_Ra, 221, 221.013908f, 0.0f); ADD(ELEM_Ra, 222, 222.015362f, 0.0f); ADD(ELEM_Ra, 223, 223.018497f, 0.0f); ADD(ELEM_Ra, 224, 224.0202020f, 0.0f); ADD(ELEM_Ra, 225, 225.023604f, 0.0f); ADD(ELEM_Ra, 226, 226.0254026f, 0.0f); ADD(ELEM_Ra, 227, 227.0291707f, 0.0f); ADD(ELEM_Ra, 228, 228.0310641f, 0.0f); ADD(ELEM_Ra, 229, 229.034820f, 0.0f); ADD(ELEM_Ra, 230, 230.037080f, 0.0f); ADD(ELEM_Ra, 231, 231.04122f, 0.0f); ADD(ELEM_Ra, 232, 232.04369f, 0.0f); ADD(ELEM_Ra, 233, 233.04800f, 0.0f); ADD(ELEM_Ra, 234, 234.05055f, 0.0f); SET(ELEM_Ac, 227); ADD(ELEM_Ac, 207, 207.01209f, 0.0f); ADD(ELEM_Ac, 208, 208.01149f, 0.0f); ADD(ELEM_Ac, 209, 209.00957f, 0.0f); ADD(ELEM_Ac, 210, 210.00926f, 0.0f); ADD(ELEM_Ac, 211, 211.00765f, 0.0f); ADD(ELEM_Ac, 212, 212.00781f, 0.0f); ADD(ELEM_Ac, 213, 213.006570f, 0.0f); ADD(ELEM_Ac, 214, 214.006890f, 0.0f); ADD(ELEM_Ac, 215, 215.006450f, 0.0f); ADD(ELEM_Ac, 216, 216.008721f, 0.0f); ADD(ELEM_Ac, 217, 217.009333f, 0.0f); ADD(ELEM_Ac, 218, 218.011630f, 0.0f); ADD(ELEM_Ac, 219, 219.012400f, 0.0f); ADD(ELEM_Ac, 220, 220.014750f, 0.0f); ADD(ELEM_Ac, 221, 221.015580f, 0.0f); ADD(ELEM_Ac, 222, 222.017829f, 0.0f); ADD(ELEM_Ac, 223, 223.019126f, 0.0f); ADD(ELEM_Ac, 224, 224.021708f, 0.0f); ADD(ELEM_Ac, 225, 225.023221f, 0.0f); ADD(ELEM_Ac, 226, 226.026090f, 0.0f); ADD(ELEM_Ac, 227, 227.0277470f, 0.0f); ADD(ELEM_Ac, 228, 228.0310148f, 0.0f); ADD(ELEM_Ac, 229, 229.032930f, 0.0f); ADD(ELEM_Ac, 230, 230.03603f, 0.0f); ADD(ELEM_Ac, 231, 231.03855f, 0.0f); ADD(ELEM_Ac, 232, 232.04202f, 0.0f); ADD(ELEM_Ac, 233, 233.04455f, 0.0f); ADD(ELEM_Ac, 234, 234.04842f, 0.0f); ADD(ELEM_Ac, 235, 235.05110f, 0.0f); ADD(ELEM_Ac, 236, 236.05518f, 0.0f); SET(ELEM_Th, NATURAL); ADD(ELEM_Th, NATURAL, 232.0381f, 0); ADD(ELEM_Th, 210, 210.01503f, 0.0f); ADD(ELEM_Th, 211, 211.01486f, 0.0f); ADD(ELEM_Th, 212, 212.01292f, 0.0f); ADD(ELEM_Th, 213, 213.01296f, 0.0f); ADD(ELEM_Th, 214, 214.01145f, 0.0f); ADD(ELEM_Th, 215, 215.011730f, 0.0f); ADD(ELEM_Th, 216, 216.011051f, 0.0f); ADD(ELEM_Th, 217, 217.013070f, 0.0f); ADD(ELEM_Th, 218, 218.013268f, 0.0f); ADD(ELEM_Th, 219, 219.015520f, 0.0f); ADD(ELEM_Th, 220, 220.015733f, 0.0f); ADD(ELEM_Th, 221, 221.018171f, 0.0f); ADD(ELEM_Th, 222, 222.018454f, 0.0f); ADD(ELEM_Th, 223, 223.020795f, 0.0f); ADD(ELEM_Th, 224, 224.021459f, 0.0f); ADD(ELEM_Th, 225, 225.023941f, 0.0f); ADD(ELEM_Th, 226, 226.024891f, 0.0f); ADD(ELEM_Th, 227, 227.027699f, 0.0f); ADD(ELEM_Th, 228, 228.0287313f, 0.0f); ADD(ELEM_Th, 229, 229.031755f, 0.0f); ADD(ELEM_Th, 230, 230.0331266f, 0.0f); ADD(ELEM_Th, 231, 231.0362971f, 0.0f); ADD(ELEM_Th, 232, 232.0380504f, 100.0f); ADD(ELEM_Th, 233, 233.0415769f, 0.0f); ADD(ELEM_Th, 234, 234.043595f, 0.0f); ADD(ELEM_Th, 235, 235.047500f, 0.0f); ADD(ELEM_Th, 236, 236.04971f, 0.0f); ADD(ELEM_Th, 237, 237.05389f, 0.0f); ADD(ELEM_Th, 238, 238.05624f, 0.0f); SET(ELEM_Pa, NATURAL); ADD(ELEM_Pa, NATURAL, 231.03588f, 0); ADD(ELEM_Pa, 213, 213.02118f, 0.0f); ADD(ELEM_Pa, 214, 214.02074f, 0.0f); ADD(ELEM_Pa, 215, 215.01910f, 0.0f); ADD(ELEM_Pa, 216, 216.01911f, 0.0f); ADD(ELEM_Pa, 217, 217.018290f, 0.0f); ADD(ELEM_Pa, 218, 218.020010f, 0.0f); ADD(ELEM_Pa, 219, 219.019880f, 0.0f); ADD(ELEM_Pa, 220, 220.021880f, 0.0f); ADD(ELEM_Pa, 221, 221.021860f, 0.0f); ADD(ELEM_Pa, 222, 222.023730f, 0.0f); ADD(ELEM_Pa, 223, 223.023960f, 0.0f); ADD(ELEM_Pa, 224, 224.025610f, 0.0f); ADD(ELEM_Pa, 225, 225.026120f, 0.0f); ADD(ELEM_Pa, 226, 226.027933f, 0.0f); ADD(ELEM_Pa, 227, 227.028793f, 0.0f); ADD(ELEM_Pa, 228, 228.031037f, 0.0f); ADD(ELEM_Pa, 229, 229.032089f, 0.0f); ADD(ELEM_Pa, 230, 230.034533f, 0.0f); ADD(ELEM_Pa, 231, 231.0358789f, 100.0f); ADD(ELEM_Pa, 232, 232.038582f, 0.0f); ADD(ELEM_Pa, 233, 233.0402402f, 0.0f); ADD(ELEM_Pa, 234, 234.043302f, 0.0f); ADD(ELEM_Pa, 235, 235.045440f, 0.0f); ADD(ELEM_Pa, 236, 236.04868f, 0.0f); ADD(ELEM_Pa, 237, 237.05114f, 0.0f); ADD(ELEM_Pa, 238, 238.054500f, 0.0f); ADD(ELEM_Pa, 239, 239.05713f, 0.0f); ADD(ELEM_Pa, 240, 240.06098f, 0.0f); SET(ELEM_U, NATURAL); ADD(ELEM_U, NATURAL, 238.02891f, 0); ADD(ELEM_U, 218, 218.02349f, 0.0f); ADD(ELEM_U, 219, 219.024920f, 0.0f); ADD(ELEM_U, 220, 220.02471f, 0.0f); ADD(ELEM_U, 221, 221.02635f, 0.0f); ADD(ELEM_U, 222, 222.02607f, 0.0f); ADD(ELEM_U, 223, 223.027720f, 0.0f); ADD(ELEM_U, 224, 224.027590f, 0.0f); ADD(ELEM_U, 225, 225.029380f, 0.0f); ADD(ELEM_U, 226, 226.029340f, 0.0f); ADD(ELEM_U, 227, 227.031140f, 0.0f); ADD(ELEM_U, 228, 228.031366f, 0.0f); ADD(ELEM_U, 229, 229.033496f, 0.0f); ADD(ELEM_U, 230, 230.033927f, 0.0f); ADD(ELEM_U, 231, 231.036289f, 0.0f); ADD(ELEM_U, 232, 232.0371463f, 0.0f); ADD(ELEM_U, 233, 233.039628f, 0.0f); ADD(ELEM_U, 234, 234.0409456f, 0.0055f); ADD(ELEM_U, 235, 235.0439231f, 0.7200f); ADD(ELEM_U, 236, 236.0455619f, 0.0f); ADD(ELEM_U, 237, 237.0487240f, 0.0f); ADD(ELEM_U, 238, 238.0507826f, 99.2745f); ADD(ELEM_U, 239, 239.0542878f, 0.0f); ADD(ELEM_U, 240, 240.056586f, 0.0f); ADD(ELEM_U, 241, 241.06033f, 0.0f); ADD(ELEM_U, 242, 242.06293f, 0.0f); SET(ELEM_Np, 237); ADD(ELEM_Np, 225, 225.033900f, 0.0f); ADD(ELEM_Np, 226, 226.03513f, 0.0f); ADD(ELEM_Np, 227, 227.034960f, 0.0f); ADD(ELEM_Np, 228, 228.03618f, 0.0f); ADD(ELEM_Np, 229, 229.036250f, 0.0f); ADD(ELEM_Np, 230, 230.037810f, 0.0f); ADD(ELEM_Np, 231, 231.038230f, 0.0f); ADD(ELEM_Np, 232, 232.04010f, 0.0f); ADD(ELEM_Np, 233, 233.040730f, 0.0f); ADD(ELEM_Np, 234, 234.042889f, 0.0f); ADD(ELEM_Np, 235, 235.0440559f, 0.0f); ADD(ELEM_Np, 236, 236.046560f, 0.0f); ADD(ELEM_Np, 237, 237.0481673f, 0.0f); ADD(ELEM_Np, 238, 238.0509405f, 0.0f); ADD(ELEM_Np, 239, 239.0529314f, 0.0f); ADD(ELEM_Np, 240, 240.056169f, 0.0f); ADD(ELEM_Np, 241, 241.058250f, 0.0f); ADD(ELEM_Np, 242, 242.06164f, 0.0f); ADD(ELEM_Np, 243, 243.064270f, 0.0f); ADD(ELEM_Np, 244, 244.06785f, 0.0f); SET(ELEM_Pu, 244); ADD(ELEM_Pu, 228, 228.038730f, 0.0f); ADD(ELEM_Pu, 229, 229.040140f, 0.0f); ADD(ELEM_Pu, 230, 230.039646f, 0.0f); ADD(ELEM_Pu, 231, 231.04126f, 0.0f); ADD(ELEM_Pu, 232, 232.041179f, 0.0f); ADD(ELEM_Pu, 233, 233.042990f, 0.0f); ADD(ELEM_Pu, 234, 234.043305f, 0.0f); ADD(ELEM_Pu, 235, 235.045282f, 0.0f); ADD(ELEM_Pu, 236, 236.0460481f, 0.0f); ADD(ELEM_Pu, 237, 237.0484038f, 0.0f); ADD(ELEM_Pu, 238, 238.0495534f, 0.0f); ADD(ELEM_Pu, 239, 239.0521565f, 0.0f); ADD(ELEM_Pu, 240, 240.0538075f, 0.0f); ADD(ELEM_Pu, 241, 241.0568453f, 0.0f); ADD(ELEM_Pu, 242, 242.0587368f, 0.0f); ADD(ELEM_Pu, 243, 243.061997f, 0.0f); ADD(ELEM_Pu, 244, 244.064198f, 0.0f); ADD(ELEM_Pu, 245, 245.067739f, 0.0f); ADD(ELEM_Pu, 246, 246.070198f, 0.0f); ADD(ELEM_Pu, 247, 247.07407f, 0.0f); SET(ELEM_Am, 243); ADD(ELEM_Am, 231, 231.04556f, 0.0f); ADD(ELEM_Am, 232, 232.04659f, 0.0f); ADD(ELEM_Am, 233, 233.04647f, 0.0f); ADD(ELEM_Am, 234, 234.04779f, 0.0f); ADD(ELEM_Am, 235, 235.04803f, 0.0f); ADD(ELEM_Am, 236, 236.04957f, 0.0f); ADD(ELEM_Am, 237, 237.049970f, 0.0f); ADD(ELEM_Am, 238, 238.051980f, 0.0f); ADD(ELEM_Am, 239, 239.053018f, 0.0f); ADD(ELEM_Am, 240, 240.055288f, 0.0f); ADD(ELEM_Am, 241, 241.0568229f, 0.0f); ADD(ELEM_Am, 242, 242.0595430f, 0.0f); ADD(ELEM_Am, 243, 243.0613727f, 0.0f); ADD(ELEM_Am, 244, 244.0642794f, 0.0f); ADD(ELEM_Am, 245, 245.066445f, 0.0f); ADD(ELEM_Am, 246, 246.069768f, 0.0f); ADD(ELEM_Am, 247, 247.07209f, 0.0f); ADD(ELEM_Am, 248, 248.07575f, 0.0f); ADD(ELEM_Am, 249, 249.07848f, 0.0f); SET(ELEM_Cm, 247); ADD(ELEM_Cm, 233, 233.05080f, 0.0f); ADD(ELEM_Cm, 234, 234.05024f, 0.0f); ADD(ELEM_Cm, 235, 235.05159f, 0.0f); ADD(ELEM_Cm, 236, 236.05141f, 0.0f); ADD(ELEM_Cm, 237, 237.05289f, 0.0f); ADD(ELEM_Cm, 238, 238.053020f, 0.0f); ADD(ELEM_Cm, 239, 239.05495f, 0.0f); ADD(ELEM_Cm, 240, 240.0555190f, 0.0f); ADD(ELEM_Cm, 241, 241.0576467f, 0.0f); ADD(ELEM_Cm, 242, 242.0588293f, 0.0f); ADD(ELEM_Cm, 243, 243.0613822f, 0.0f); ADD(ELEM_Cm, 244, 244.0627463f, 0.0f); ADD(ELEM_Cm, 245, 245.0654856f, 0.0f); ADD(ELEM_Cm, 246, 246.0672176f, 0.0f); ADD(ELEM_Cm, 247, 247.070347f, 0.0f); ADD(ELEM_Cm, 248, 248.072342f, 0.0f); ADD(ELEM_Cm, 249, 249.075947f, 0.0f); ADD(ELEM_Cm, 250, 250.078351f, 0.0f); ADD(ELEM_Cm, 251, 251.082278f, 0.0f); ADD(ELEM_Cm, 252, 252.08487f, 0.0f); SET(ELEM_Bk, 247); ADD(ELEM_Bk, 235, 235.05658f, 0.0f); ADD(ELEM_Bk, 236, 236.05733f, 0.0f); ADD(ELEM_Bk, 237, 237.05713f, 0.0f); ADD(ELEM_Bk, 238, 238.05827f, 0.0f); ADD(ELEM_Bk, 239, 239.05836f, 0.0f); ADD(ELEM_Bk, 240, 240.05975f, 0.0f); ADD(ELEM_Bk, 241, 241.06022f, 0.0f); ADD(ELEM_Bk, 242, 242.06205f, 0.0f); ADD(ELEM_Bk, 243, 243.063002f, 0.0f); ADD(ELEM_Bk, 244, 244.065168f, 0.0f); ADD(ELEM_Bk, 245, 245.0663554f, 0.0f); ADD(ELEM_Bk, 246, 246.068670f, 0.0f); ADD(ELEM_Bk, 247, 247.070299f, 0.0f); ADD(ELEM_Bk, 248, 248.073080f, 0.0f); ADD(ELEM_Bk, 249, 249.074980f, 0.0f); ADD(ELEM_Bk, 250, 250.078311f, 0.0f); ADD(ELEM_Bk, 251, 251.080753f, 0.0f); ADD(ELEM_Bk, 252, 252.08430f, 0.0f); ADD(ELEM_Bk, 253, 253.08688f, 0.0f); ADD(ELEM_Bk, 254, 254.09060f, 0.0f); SET(ELEM_Cf, 251); ADD(ELEM_Cf, 237, 237.06207f, 0.0f); ADD(ELEM_Cf, 238, 238.06141f, 0.0f); ADD(ELEM_Cf, 239, 239.06258f, 0.0f); ADD(ELEM_Cf, 240, 240.06230f, 0.0f); ADD(ELEM_Cf, 241, 241.06372f, 0.0f); ADD(ELEM_Cf, 242, 242.063690f, 0.0f); ADD(ELEM_Cf, 243, 243.06542f, 0.0f); ADD(ELEM_Cf, 244, 244.065990f, 0.0f); ADD(ELEM_Cf, 245, 245.06804f, 0.0f); ADD(ELEM_Cf, 246, 246.0687988f, 0.0f); ADD(ELEM_Cf, 247, 247.070992f, 0.0f); ADD(ELEM_Cf, 248, 248.072178f, 0.0f); ADD(ELEM_Cf, 249, 249.074847f, 0.0f); ADD(ELEM_Cf, 250, 250.0764000f, 0.0f); ADD(ELEM_Cf, 251, 251.079580f, 0.0f); ADD(ELEM_Cf, 252, 252.081620f, 0.0f); ADD(ELEM_Cf, 253, 253.085127f, 0.0f); ADD(ELEM_Cf, 254, 254.087316f, 0.0f); ADD(ELEM_Cf, 255, 255.09104f, 0.0f); ADD(ELEM_Cf, 256, 256.09344f, 0.0f); SET(ELEM_Es, 252); ADD(ELEM_Es, 240, 240.06892f, 0.0f); ADD(ELEM_Es, 241, 241.06866f, 0.0f); ADD(ELEM_Es, 242, 242.06970f, 0.0f); ADD(ELEM_Es, 243, 243.06963f, 0.0f); ADD(ELEM_Es, 244, 244.07097f, 0.0f); ADD(ELEM_Es, 245, 245.07132f, 0.0f); ADD(ELEM_Es, 246, 246.07297f, 0.0f); ADD(ELEM_Es, 247, 247.073650f, 0.0f); ADD(ELEM_Es, 248, 248.075460f, 0.0f); ADD(ELEM_Es, 249, 249.076410f, 0.0f); ADD(ELEM_Es, 250, 250.07865f, 0.0f); ADD(ELEM_Es, 251, 251.079984f, 0.0f); ADD(ELEM_Es, 252, 252.082970f, 0.0f); ADD(ELEM_Es, 253, 253.084818f, 0.0f); ADD(ELEM_Es, 254, 254.088016f, 0.0f); ADD(ELEM_Es, 255, 255.090266f, 0.0f); ADD(ELEM_Es, 256, 256.09359f, 0.0f); ADD(ELEM_Es, 257, 257.09598f, 0.0f); SET(ELEM_Fm, 257); ADD(ELEM_Fm, 242, 242.07343f, 0.0f); ADD(ELEM_Fm, 243, 243.07451f, 0.0f); ADD(ELEM_Fm, 244, 244.07408f, 0.0f); ADD(ELEM_Fm, 245, 245.07538f, 0.0f); ADD(ELEM_Fm, 246, 246.075280f, 0.0f); ADD(ELEM_Fm, 247, 247.07682f, 0.0f); ADD(ELEM_Fm, 248, 248.077184f, 0.0f); ADD(ELEM_Fm, 249, 249.07902f, 0.0f); ADD(ELEM_Fm, 250, 250.079515f, 0.0f); ADD(ELEM_Fm, 251, 251.081566f, 0.0f); ADD(ELEM_Fm, 252, 252.082460f, 0.0f); ADD(ELEM_Fm, 253, 253.085176f, 0.0f); ADD(ELEM_Fm, 254, 254.086848f, 0.0f); ADD(ELEM_Fm, 255, 255.089955f, 0.0f); ADD(ELEM_Fm, 256, 256.091767f, 0.0f); ADD(ELEM_Fm, 257, 257.095099f, 0.0f); ADD(ELEM_Fm, 258, 258.09707f, 0.0f); ADD(ELEM_Fm, 259, 259.10059f, 0.0f); SET(ELEM_Md, 258); ADD(ELEM_Md, 245, 245.08102f, 0.0f); ADD(ELEM_Md, 246, 246.08193f, 0.0f); ADD(ELEM_Md, 247, 247.08180f, 0.0f); ADD(ELEM_Md, 248, 248.08291f, 0.0f); ADD(ELEM_Md, 249, 249.08300f, 0.0f); ADD(ELEM_Md, 250, 250.08449f, 0.0f); ADD(ELEM_Md, 251, 251.08492f, 0.0f); ADD(ELEM_Md, 252, 252.08663f, 0.0f); ADD(ELEM_Md, 253, 253.08728f, 0.0f); ADD(ELEM_Md, 254, 254.08973f, 0.0f); ADD(ELEM_Md, 255, 255.091075f, 0.0f); ADD(ELEM_Md, 256, 256.094050f, 0.0f); ADD(ELEM_Md, 257, 257.095535f, 0.0f); ADD(ELEM_Md, 258, 258.098425f, 0.0f); ADD(ELEM_Md, 259, 259.10050f, 0.0f); ADD(ELEM_Md, 260, 260.10365f, 0.0f); SET(ELEM_No, 259); ADD(ELEM_No, 249, 249.08782f, 0.0f); ADD(ELEM_No, 250, 250.08749f, 0.0f); ADD(ELEM_No, 251, 251.08896f, 0.0f); ADD(ELEM_No, 252, 252.088966f, 0.0f); ADD(ELEM_No, 253, 253.09065f, 0.0f); ADD(ELEM_No, 254, 254.090949f, 0.0f); ADD(ELEM_No, 255, 255.093232f, 0.0f); ADD(ELEM_No, 256, 256.094276f, 0.0f); ADD(ELEM_No, 257, 257.096850f, 0.0f); ADD(ELEM_No, 258, 258.09820f, 0.0f); ADD(ELEM_No, 259, 259.10102f, 0.0f); ADD(ELEM_No, 260, 260.10264f, 0.0f); ADD(ELEM_No, 261, 261.10574f, 0.0f); ADD(ELEM_No, 262, 262.10752f, 0.0f); SET(ELEM_Lr, 262); ADD(ELEM_Lr, 251, 251.09436f, 0.0f); ADD(ELEM_Lr, 252, 252.09533f, 0.0f); ADD(ELEM_Lr, 253, 253.09526f, 0.0f); ADD(ELEM_Lr, 254, 254.09659f, 0.0f); ADD(ELEM_Lr, 255, 255.09677f, 0.0f); ADD(ELEM_Lr, 256, 256.09876f, 0.0f); ADD(ELEM_Lr, 257, 257.09961f, 0.0f); ADD(ELEM_Lr, 258, 258.10188f, 0.0f); ADD(ELEM_Lr, 259, 259.102990f, 0.0f); ADD(ELEM_Lr, 260, 260.10557f, 0.0f); ADD(ELEM_Lr, 261, 261.10694f, 0.0f); ADD(ELEM_Lr, 262, 262.10969f, 0.0f); ADD(ELEM_Lr, 263, 263.11139f, 0.0f); SET(ELEM_Rf, 267); ADD(ELEM_Rf, 253, 253.10069f, 0.0f); ADD(ELEM_Rf, 254, 254.10018f, 0.0f); ADD(ELEM_Rf, 255, 255.10134f, 0.0f); ADD(ELEM_Rf, 256, 256.10116f, 0.0f); ADD(ELEM_Rf, 257, 257.10299f, 0.0f); ADD(ELEM_Rf, 258, 258.10349f, 0.0f); ADD(ELEM_Rf, 259, 259.10564f, 0.0f); ADD(ELEM_Rf, 260, 260.10644f, 0.0f); ADD(ELEM_Rf, 261, 261.10877f, 0.0f); ADD(ELEM_Rf, 262, 262.10993f, 0.0f); ADD(ELEM_Rf, 263, 263.11255f, 0.0f); ADD(ELEM_Rf, 266, 266.11796f, 0.0f); ADD(ELEM_Rf, 267, 267.12153f, 0.0f); ADD(ELEM_Rf, 268, 268.12364f, 0.0f);�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/gross_formula.cpp��������������������������������������������������0000664�0000000�0000000�00000012137�12710376503�0022167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <ctype.h> #include "base_cpp/scanner.h" #include "base_cpp/output.h" #include "molecule/elements.h" #include "molecule/base_molecule.h" #include "molecule/gross_formula.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" using namespace indigo; int GrossFormula::_cmp (_ElemCounter &ec1, _ElemCounter &ec2, void *context) { if (ec1.counter == 0) return 1; if (ec2.counter == 0) return -1; if (ec2.elem == ELEM_H) // move hydrogen to the end return -1; if (ec1.elem == ELEM_H) return 1; return ec1.elem - ec2.elem; } // comparator implementing the Hill system without carbon: // <all atoms in alphabetical order> int GrossFormula::_cmp_hill_no_carbon (_ElemCounter &ec1, _ElemCounter &ec2, void *context) { if (ec1.counter == 0) return 1; if (ec2.counter == 0) return -1; // all elements are compared lexicographically return strcmp(Element::toString(ec1.elem), Element::toString(ec2.elem)); } // comparator implementing the Hill system with carbon: // C H <other atoms in alphabetical order> int GrossFormula::_cmp_hill (_ElemCounter &ec1, _ElemCounter &ec2, void *context) { if (ec1.counter == 0) return 1; if (ec2.counter == 0) return -1; // carbon has the highest priority if (ec2.elem == ELEM_C) return 1; if (ec1.elem == ELEM_C) return -1; // hydrogen has the highest priority after carbon if (ec2.elem == ELEM_H) return 1; if (ec1.elem == ELEM_H) return -1; return _cmp_hill_no_carbon(ec1, ec2, context); } void GrossFormula::collect (BaseMolecule &mol, Array<int> &gross) { if (!mol.isQueryMolecule()) mol.asMolecule().restoreAromaticHydrogens(); gross.clear_resize(ELEM_MAX); gross.zerofill(); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.isPseudoAtom(i) || mol.isRSite(i) || mol.isTemplateAtom(i)) continue; int number = mol.getAtomNumber(i); if (number > 0) gross[number]++; if (!mol.isQueryMolecule()) { int implicit_h = mol.asMolecule().getImplicitH(i); if (implicit_h >= 0) gross[ELEM_H] += implicit_h; } } } void GrossFormula::toString (const Array<int> &gross, Array<char> &str) { _toString(gross, str, _cmp); } void GrossFormula::toString_Hill (const Array<int> &gross, Array<char> &str) { if (gross[ELEM_C] == 0) _toString(gross, str, _cmp_hill_no_carbon); else _toString(gross, str, _cmp_hill); } void GrossFormula::_toString (const Array<int> &gross, Array<char> &str, int (*cmp)(_ElemCounter &, _ElemCounter &, void *)) { QS_DEF(Array<_ElemCounter>, counters); int i; counters.clear(); for (i = 1; i < ELEM_MAX; i++) { _ElemCounter &ec = counters.push(); ec.elem = i; ec.counter = gross[i]; } counters.qsort(cmp, 0); ArrayOutput output(str); bool first_written = false; for (i = 0; i < counters.size(); i++) { if (counters[i].counter < 1) break; if (first_written) output.printf(" "); output.printf(Element::toString(counters[i].elem)); if (counters[i].counter > 1) output.printf("%d", counters[i].counter); first_written = true; } output.writeChar(0); } void GrossFormula::fromString (Scanner &scanner, Array<int> &gross) { gross.clear_resize(ELEM_MAX); gross.zerofill(); scanner.skipSpace(); while (!scanner.isEOF()) { int elem = Element::read(scanner); scanner.skipSpace(); int counter = 1; if (isdigit(scanner.lookNext())) { counter = scanner.readUnsigned(); scanner.skipSpace(); } gross[elem] += counter; } } void GrossFormula::fromString (const char *str, Array<int> &gross) { BufferScanner scanner(str); fromString(scanner, gross); } bool GrossFormula::leq (const Array<int> &gross1, const Array<int> &gross2) { int i; for (i = 1; i < ELEM_MAX; i++) if (gross1[i] > gross2[i]) return false; return true; } bool GrossFormula::geq (const Array<int> &gross1, const Array<int> &gross2) { int i; for (i = 1; i < ELEM_MAX; i++) if (gross2[i] > 0 && gross1[i] < gross2[i]) return false; return true; } bool GrossFormula::equal (const Array<int> &gross1, const Array<int> &gross2) { int i; for (i = 1; i < ELEM_MAX; i++) if (gross2[i] != gross1[i]) return false; return true; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/haworth_projection_finder.cpp��������������������������������������0000664�0000000�0000000�00000030751�12710376503�0024546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "molecule/haworth_projection_finder.h" #include <math.h> #include <algorithm> #include "base_cpp/exception.h" #include "molecule/base_molecule.h" #include "molecule/elements.h" #include "math/algebra.h" using namespace indigo; CP_DEF(HaworthProjectionFinder); const float COS10_THRESHOLD = 0.015f; HaworthProjectionFinder::HaworthProjectionFinder (BaseMolecule &mol) : _mol(mol), CP_INIT, TL_CP_GET(_atoms_mask), TL_CP_GET(_bonds_mask), TL_CP_GET(_bold_bonds_mask) { _atoms_mask.clear_resize(_mol.vertexEnd()); _atoms_mask.fill(false); _bonds_mask.clear_resize(_mol.edgeEnd()); _bonds_mask.fill(false); _bold_bonds_mask.clear_resize(_mol.edgeEnd()); _bold_bonds_mask.fill(false); } void HaworthProjectionFinder::findAndAddStereocenters () { _find(true); } void HaworthProjectionFinder::find () { _find(false); } const Array<bool>& HaworthProjectionFinder::getAtomsMask () { return _atoms_mask; } const Array<bool>& HaworthProjectionFinder::getBondsMask () { return _bonds_mask; } bool HaworthProjectionFinder::isBoldBond (int e_idx) { return _bold_bonds_mask[e_idx]; } void HaworthProjectionFinder::_find (bool add_stereo) { if (BaseMolecule::hasCoord(_mol)) { QS_DEF(Array<int>, vertices); QS_DEF(Array<int>, edges); int sssr_cnt = _mol.sssrCount(); for (int i = 0; i < sssr_cnt; i++) { // Make an array with vertices and edges being corresponded to each other const List<int> &v_list = _mol.sssrVertices(i); const List<int> &e_list = _mol.sssrEdges(i); edges.clear(); for (int j = e_list.begin(); j != e_list.end(); j = e_list.next(j)) edges.push(e_list[j]); int vbegin = _mol.getEdge(edges[0]).beg; const Edge &e2 = _mol.getEdge(edges[1]); if (vbegin == e2.beg || vbegin == e2.end) vbegin = _mol.getEdge(edges[0]).end; vertices.clear(); vertices.push(vbegin); int v = vbegin; for (int j = 0; j < edges.size() - 1; j++) { int v2 = _mol.getEdgeEnd(v, edges[j]); if (v2 == -1) throw Exception("Internal error. Edges are not adjust"); vertices.push(v2); v = v2; } _processRing(add_stereo, vertices, edges); } } } bool HaworthProjectionFinder::_processRing (bool add_stereo, const Array<int> &vertices, const Array<int> &edges) { // We detect rings of size from 3 and 6: // ----- ---- / \ ---- ------ // / \ / \ \ / | | /\ \ / // \ / \ / ---- | | / \ \/ // ====== ==== ==== if (vertices.size() < 3 || vertices.size() > 6) return false; // Check that // each vertex has only 2 bonds in ring, // each angle is not small to form normal ring // polygone is convex int sign = 0; bool not_haworth = false; int vertical_count = 0, corner_count = 0; bool has_bold = false; for (int j = 0; j < vertices.size(); j++) { int prev_j = (j + vertices.size() - 1) % vertices.size(); int vi = vertices[j]; int e1i = edges[j]; int e2i = edges[prev_j]; // Previous edge /* TODO: Uncomment after validation if (_mol.getBondOrder(e1i) != BOND_SINGLE) return false; */ const Vertex &v = _mol.getVertex(vi); if (v.degree() > 4) return false; int ring_cnt = 0; int subs_cnt = 0, subs_bond_idx; for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int b = v.neiEdge(nei); if (_mol.getEdgeTopology(b) == TOPOLOGY_RING) ring_cnt++; else { subs_cnt++; subs_bond_idx = b; } } if (ring_cnt != 2) return false; float c = _getAngleCos(vi, e1i, e2i); if (c > 1 - COS10_THRESHOLD) return false; // Angle is too small float s = _getAngleSin(vi, e1i, e2i); if (sign == 0) sign = __sign(s); else if (sign != __sign(s)) return false; // Rotation direction is different => non-convex bool is_corner = _isCornerVertex(vi, e1i, e2i); if (!is_corner) { // Substituents should be vertical for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int b = v.neiEdge(nei); if (_mol.getEdgeTopology(b) == TOPOLOGY_RING) continue; if (!_isVerticalEdge(b, COS10_THRESHOLD)) return false; // Not vertical } } else { // Corner vertex corner_count++; } // Count number of vertical bonds for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int b = v.neiEdge(nei); if (_mol.getEdgeTopology(b) == TOPOLOGY_RING) continue; if (_isVerticalEdge(b, COS10_THRESHOLD)) vertical_count++; } // Check if bond is a angle bisector if (subs_cnt == 1) { float a1 = _getAngleCos(vi, subs_bond_idx, e1i); float a2 = _getAngleCos(vi, subs_bond_idx, e2i); if (fabs(a1 - a2) < 1e-3) { // Check if angle bisector is allowed if (is_corner) { // Only horizontal line is allowed if (!_isHorizontalEdge(subs_bond_idx, COS10_THRESHOLD)) return false; } else { // Only vertical line is allowed if (!_isVerticalEdge(subs_bond_idx, COS10_THRESHOLD)) return false; } } } // Substituents should be opposite to each other int sub_sign = 0; for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int b = v.neiEdge(nei); if (_mol.getEdgeTopology(b) == TOPOLOGY_RING) continue; float c2 = _getAngleCos(vi, b, 0.0f, 1.0f); if (fabs(c2) > COS10_THRESHOLD) { // Count only non-horizontal bonds if (sub_sign == 0) sub_sign = __sign(c2); else if (sub_sign == __sign(c2)) return false; // Substituents are in the same direction } } // Check bold bond and bold corner int next_j = (j + 1) % vertices.size(); int vpi = vertices[prev_j]; int vni = vertices[next_j]; // Check corner // ---- // / \ // \ / // if (_mol.getBondDirection2(vpi, vi) == BOND_UP && _mol.getBondDirection2(vni, vi) == BOND_UP) has_bold = true; else { // Check bold bond // ----- // / \ // \ / // ====== int next2_j = (j + 2) % vertices.size(); int vn2i = vertices[next2_j]; if (_mol.getBondDirection2(vpi, vi) == BOND_UP && _mol.getBondDirection2(vn2i, vni) == BOND_UP) has_bold = true; } } if (has_bold) { if (vertical_count <= 1) // At least two vertical bonds are required return false; } else { if (vertical_count <= 2) // At least three vertical bonds are required return false; } if (corner_count != 2) // There should be one explicit corner at both sides return false; // Ring seems to have Haworth projection _markRingBonds(vertices, edges); if (add_stereo) _addRingStereocenters(vertices, edges); return true; } void HaworthProjectionFinder::_markRingBonds (const Array<int> &vertices, const Array<int> &edges) { // Mark bonds for (int j = 0; j < vertices.size(); j++) { int vi = vertices[j]; const Vertex &v = _mol.getVertex(vi); // Mark ring atoms _atoms_mask[vi] = true; // Mark all bonds to these atoms for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int b = v.neiEdge(nei); _bonds_mask[b] = true; } // Check bold bond int prev_j = (j + vertices.size() - 1) % vertices.size(); int next_j = (j + 1) % vertices.size(); int next_j2 = (j + 2) % vertices.size(); int vip = vertices[prev_j]; int vin = vertices[next_j]; int vin2 = vertices[next_j2]; if (_mol.getBondDirection2(vip, vi) == BOND_UP && _mol.getBondDirection2(vin2, vin) == BOND_UP) _bold_bonds_mask[edges[j]] = 1; } } void HaworthProjectionFinder::_addRingStereocenters (const Array<int> &vertices, const Array<int> &edges) { // Find left vertex int j_left = -1; float x_left = 1e20f; for (int j = 0; j < vertices.size(); j++) { float x = _mol.getAtomXyz(vertices[j]).x; if (x < x_left) { x_left = x; j_left = j; } } // Check direction and make it from top to bottom int left_next = (j_left + 1) % vertices.size(); int left_prev = (j_left + vertices.size() - 1) % vertices.size(); float yn = _mol.getAtomXyz(vertices[left_next]).y; float yp = _mol.getAtomXyz(vertices[left_prev]).y; int parity = __sign(yn - yp); for (int j = 0; j < vertices.size(); j++) { int vi = vertices[j]; if (_mol.getAtomNumber(vi) != ELEM_C) continue; int v_next = (j + 1) % vertices.size(); int v_prev = (j + vertices.size() - 1) % vertices.size(); int vi_next = vertices[v_next]; int vi_prev = vertices[v_prev]; float yc = _mol.getAtomXyz(vi).y; int vi_top = -1, vi_bottom = -1; const Vertex &v = _mol.getVertex(vi); if (v.degree() == 2) continue; bool skip = false; for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int vn = v.neiVertex(nei); if (vn == vi_next || vn == vi_prev) continue; int b = v.neiEdge(nei); if (_isHorizontalEdge(b, COS10_THRESHOLD)) { skip = true; break; } float yn = _mol.getAtomXyz(vn).y; if (yn > yc) vi_top = vn; else vi_bottom = vn; } if (skip) // Skip this vertex because some edge is horizontal continue; // Add stereocenter int pyramid[4]; pyramid[0] = vi_prev; pyramid[1] = vi_top; pyramid[2] = vi_next; pyramid[3] = vi_bottom; if (parity == -1) std::swap(pyramid[2], pyramid[3]); if (vi_top == -1 || vi_bottom == -1) { int cnt = 0; while (pyramid[3] != -1) { MoleculeStereocenters::rotatePyramid(pyramid); cnt++; } if (cnt % 2 == 1) std::swap(pyramid[0], pyramid[1]); } _mol.stereocenters.add(vi, MoleculeStereocenters::ATOM_ABS, 1, pyramid); } } bool HaworthProjectionFinder::_isCornerVertex (int v, int e1, int e2) { int v1 = _mol.getEdgeEnd(v, e1); int v2 = _mol.getEdgeEnd(v, e2); Vec3f pv = _mol.getAtomXyz(v); Vec3f pv1 = _mol.getAtomXyz(v1); Vec3f pv2 = _mol.getAtomXyz(v2); Vec2f d1(pv1.x - pv.x, pv1.y - pv.y); Vec2f d2(pv2.x - pv.x, pv2.y - pv.y); return __sign(d1.x * d2.x) == 1; } bool HaworthProjectionFinder::_isHorizontalEdge (int e, float cos_threshold) { int v = _mol.getEdge(e).beg; return fabs(_getAngleCos(v, e, 1.0f, 0.0f)) > 1 - cos_threshold; } bool HaworthProjectionFinder::_isVerticalEdge (int e, float cos_threshold) { int v = _mol.getEdge(e).beg; return fabs(_getAngleCos(v, e, 0.0f, 1.0f)) > 1 - cos_threshold; } float HaworthProjectionFinder::_getAngleCos(int v, int e, float dx, float dy) { int v1 = _mol.getEdgeEnd(v, e); Vec3f pv = _mol.getAtomXyz(v); Vec3f pv2 = _mol.getAtomXyz(v1); Vec2f d1(dx, dy); Vec2f d2(pv2.x - pv.x, pv2.y - pv.y); return Vec2f::dot(d1, d2) / d1.length() / d2.length(); } float HaworthProjectionFinder::_getAngleCos(int v, int e1, int e2) { int v2 = _mol.getEdgeEnd(v, e2); Vec3f pv = _mol.getAtomXyz(v); Vec3f pv2 = _mol.getAtomXyz(v2); return _getAngleCos(v, e1, pv2.x - pv.x, pv2.y - pv.y); } float HaworthProjectionFinder::_getAngleSin(int v, int e1, int e2) { int v1 = _mol.getEdgeEnd(v, e1); int v2 = _mol.getEdgeEnd(v, e2); Vec3f pv = _mol.getAtomXyz(v); Vec3f pv1 = _mol.getAtomXyz(v1); Vec3f pv2 = _mol.getAtomXyz(v2); Vec2f d1(pv1.x - pv.x, pv1.y - pv.y); Vec2f d2(pv2.x - pv.x, pv2.y - pv.y); return Vec2f::cross(d1, d2) / d1.length() / d2.length(); } �����������������������Indigo-indigo-1.2.3/molecule/src/icm_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000003377�12710376503�0021411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/icm_loader.h" #include "molecule/icm_saver.h" #include "base_cpp/scanner.h" #include "molecule/cmf_loader.h" #include "molecule/molecule.h" #include "molecule/icm_common.h" using namespace indigo; IMPL_ERROR(IcmLoader, "ICM loader"); IcmLoader::IcmLoader (Scanner &scanner) : _scanner(scanner) { } void IcmLoader::loadMolecule (Molecule &mol) { char id[3]; _scanner.readCharsFix(3, id); int version = -1; if (strncmp(id, IcmSaver::VERSION2, 3) == 0) version = 2; else if (strncmp(id, IcmSaver::VERSION1, 3) == 0) version = 1; else throw Error("expected '%s' or '%s', got %.*s. Resave your molecule with new format.", IcmSaver::VERSION1, IcmSaver::VERSION2, 3, id); char bits = _scanner.readChar(); bool have_xyz = ((bits & ICM_XYZ) != 0); bool have_bond_dirs = ((bits & ICM_BOND_DIRS) != 0); CmfLoader loader(_scanner); loader.version = version; loader.loadMolecule(mol); if (have_xyz) { loader.loadXyz(_scanner); if (!have_bond_dirs) { mol.stereocenters.markBonds(); mol.allene_stereo.markBonds(); } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/icm_saver.cpp������������������������������������������������������0000664�0000000�0000000�00000003226�12710376503�0021254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "molecule/icm_saver.h" #include "molecule/cmf_saver.h" #include "molecule/icm_common.h" using namespace indigo; const char* IcmSaver::VERSION2 = "IM2"; const char* IcmSaver::VERSION1 = "ICM"; IMPL_ERROR(IcmSaver, "ICM saver"); bool IcmSaver::checkVersion (const char *prefix) { return strncmp(prefix, VERSION1, 3) == 0 || strncmp(prefix, VERSION2, 3) == 0; } IcmSaver::IcmSaver (Output &output) : _output(output) { save_xyz = false; save_bond_dirs = false; save_highlighting = false; save_ordering = false; } void IcmSaver::saveMolecule (Molecule &mol) { _output.writeString(VERSION2); int features = 0; if (save_xyz) features |= ICM_XYZ; if (save_bond_dirs) features |= ICM_BOND_DIRS; _output.writeChar(features); CmfSaver saver(_output); saver.save_bond_dirs = save_bond_dirs; saver.save_highlighting = save_highlighting; saver.save_mapping = save_ordering; saver.saveMolecule(mol); if (save_xyz) saver.saveXyz(_output); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/inchi_parser.cpp���������������������������������������������������0000664�0000000�0000000�00000007432�12710376503�0021755�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/inchi_parser.h" #include <string> using namespace indigo; IMPL_EXCEPTION(indigo, InchiParserError, "InchiParser"); InChICodeParser::InChICodeParser(const char *inchi_code) : _mobileCount(0) { std::string str(inchi_code); size_t pos = str.find("/h"); // assert(pos != npos) unsigned num = 0, from = -1; bool isValid; int type = STATIC; for (pos += 2; pos < str.size() && str[pos] != '/'; ++pos) { isValid = false; switch (str[pos]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num = num * 10 + (str[pos] - '0'); break; case ',': if (type == 0) { type = MOBILE; _mobileCount += (num? num: 1); } else { if (from == -1) { _hydrogens.push(num - 1); _types.push(type); } else { for (auto i = from; i <= num; ++i) { _hydrogens.push(i - 1); _types.push(type); } from = -1; } } num = 0; break; case '-': if(from != -1) { throw InchiParserError("Dash without left boundary defined"); } from = num; num = 0; break; case '(': if (pos < str.size() && str[pos + 1] == 'H') { ++pos; type = 0; } else { throw InchiParserError("Invalid InChI format: \"%s\"", inchi_code); } break; case ')': if (from == -1) { _hydrogens.push(num - 1); _types.push(type); } else { for (auto i = from; i <= num; ++i) { _hydrogens.push(i - 1); _types.push(type); } from = -1; } num = 0; type = STATIC; isValid = true; break; case 'H': if (from == -1) { _hydrogens.push(num - 1); _types.push(type); } else { for (auto i = from; i <= num; ++i) { _hydrogens.push(i - 1); _types.push(type); } from = -1; } num = 0; if (pos < str.size() && str[pos + 1] == ',') { ++pos; } else { isValid = true; } break; default: ; } } if (!isValid) { //throw InchiParserError("Invalid InChI format: \"%s\"", inchi_code); } } int InChICodeParser::_nextElement(int type, int index) { if (index == -1) index = 0; else ++index; for (; index != _hydrogens.size(); ++index) { if (_types[index] & type) break; } return index; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/inchi_wrapper.cpp��������������������������������������������������0000664�0000000�0000000�00000046405�12710376503�0022144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/inchi_wrapper.h" #include "base_cpp/obj.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "molecule/molecule_dearom.h" #include "mode.h" #include <algorithm> using namespace indigo; // Inchi doesn't seem to support multithreading static OsLock inchi_lock; namespace indigo { // Structure that matches both inchi_OutputStruct and tagINCHI_Input struct InchiOutput { inchi_Atom *atom; inchi_Stereo0D *stereo0D; AT_NUM num_atoms; AT_NUM num_stereo0D; }; } IMPL_ERROR(InchiWrapper, "inchi-wrapper") template<typename T> class InchiMemObject { public: typedef void (*DestructorT) (T *obj); InchiMemObject (DestructorT destructor) : destructor(destructor) { } ~InchiMemObject() { destructor(&obj); } T& ref () { return obj; } private: T obj; DestructorT destructor; }; const char* InchiWrapper::version() { return INCHI_NAME " version " INCHI_VERSION TARGET_ID_STRING; } InchiWrapper::InchiWrapper() { clear(); } void InchiWrapper::clear() { options.clear(); options.push(0); warning.clear(); log.clear(); auxInfo.clear(); } void InchiWrapper::setOptions(const char *opt) { options.readString(opt, true); // Replace '/' and '-' according to InChI manual: // "(use - instead of / for O.S. other than MS Windows)" #ifdef _WIN32 for (int i = 0; i < options.size(); i++) if (options[i] == '-') options[i] = '/'; #else for (int i = 0; i < options.size(); i++) if (options[i] == '/') options[i] = '-'; #endif } static inchi_BondType getInchiBondType (int bond_order) { switch (bond_order) { case BOND_SINGLE: return INCHI_BOND_TYPE_SINGLE; case BOND_DOUBLE: return INCHI_BOND_TYPE_DOUBLE; case BOND_TRIPLE: return INCHI_BOND_TYPE_TRIPLE; case BOND_AROMATIC: return INCHI_BOND_TYPE_ALTERN; } throw InchiWrapper::Error("unexpected bond order %d", bond_order); } void InchiWrapper::loadMoleculeFromAux(const char *aux, Molecule &mol) { // lock OsLocker locker(inchi_lock); InchiMemObject<inchi_Input> data_inp_obj(Free_inchi_Input); inchi_Input &data_inp = data_inp_obj.ref(); memset(&data_inp, 0, sizeof(data_inp)); InchiInpData data; memset(&data, 0, sizeof(data)); data.pInp = &data_inp; int retcode = Get_inchi_Input_FromAuxInfo((char*)aux, 0, 0, &data); if (retcode != inchi_Ret_OKAY && retcode != inchi_Ret_WARNING ) throw Error("Indigo-InChI: Aux InChI loading failed: %s. Code: %d.", data.szErrMsg, retcode); InchiOutput output; output.atom = data_inp.atom; output.stereo0D = data_inp.stereo0D; output.num_atoms = data_inp.num_atoms; output.num_stereo0D = data_inp.num_stereo0D; parseInchiOutput(output, mol); } void InchiWrapper::loadMoleculeFromInchi(const char *inchi_string, Molecule &mol) { // lock OsLocker locker(inchi_lock); inchi_InputINCHI inchi_input; inchi_input.szInChI = (char *)inchi_string; inchi_input.szOptions = (char *)options.ptr(); InchiMemObject<inchi_OutputStruct> inchi_output_obj(FreeStructFromINCHI); inchi_OutputStruct &inchi_output = inchi_output_obj.ref(); int retcode = GetStructFromINCHI(&inchi_input, &inchi_output); if (inchi_output.szMessage) warning.readString(inchi_output.szMessage, true); if (inchi_output.szLog) log.readString(inchi_output.szLog, true); if (retcode != inchi_Ret_OKAY && retcode != inchi_Ret_WARNING) throw Error("Indigo-InChI: InChI loading failed: %s. Code: %d.", inchi_output.szMessage, retcode); InchiOutput output; output.atom = inchi_output.atom; output.stereo0D = inchi_output.stereo0D; output.num_atoms = inchi_output.num_atoms; output.num_stereo0D = inchi_output.num_stereo0D; parseInchiOutput(output, mol); } void InchiWrapper::neutralizeV5Nitrogen(Molecule &mol) { // Initial structure C[C@H](O)[C@H](COC)/N=[N+](\[O-])/C=CCCCCCC // is loaded via InChI as CCCCCCC=CN(=O)=N[C@@H](COC)[C@H](C)O // and we cannot restore cis-trans configuration for O=N=N-C bond for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) if (mol.isNitrogenV5(v)) { const Vertex &vertex = mol.getVertex(v); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_edge = vertex.neiEdge(nei); if (mol.getBondOrder(nei_edge) != BOND_DOUBLE) continue; int nei_idx = vertex.neiVertex(nei); int number = mol.getAtomNumber(nei_idx); int charge = mol.getAtomCharge(nei_idx); int radical = mol.getAtomRadical(nei_idx); if ((number == ELEM_O || number == ELEM_S) && charge == 0 && radical == 0) { mol.setAtomCharge(v, 1); mol.setAtomCharge(nei_idx, -1); mol.setBondOrder(nei_edge, BOND_SINGLE); break; } } } } void InchiWrapper::parseInchiOutput(const InchiOutput &inchi_output, Molecule &mol) { mol.clear(); Array<int> atom_indices; atom_indices.clear(); // Add atoms for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++) { const inchi_Atom &inchi_atom = inchi_output.atom[i]; int idx = mol.addAtom(Element::fromString(inchi_atom.elname)); atom_indices.push(idx); } // Add bonds for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++) { const inchi_Atom &inchi_atom = inchi_output.atom[i]; for (AT_NUM bi = 0; bi < inchi_atom.num_bonds; bi++) { AT_NUM nei = inchi_atom.neighbor[bi]; if (i > nei) // Add bond only once continue; int bond_order = inchi_atom.bond_type[bi]; if (bond_order == INCHI_BOND_TYPE_NONE) throw Molecule::Error("Indigo-InChI: NONE-typed bonds are not supported"); if (bond_order >= INCHI_BOND_TYPE_ALTERN) throw Molecule::Error("Indigo-InChI: ALTERN-typed bonds are not supported"); int bond = mol.addBond(atom_indices[i], atom_indices[nei], bond_order); } } // Add Hydrogen isotope atoms at the end to preserver // the same atom ordering for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++) { const inchi_Atom &inchi_atom = inchi_output.atom[i]; int root_atom = atom_indices[i]; for (int iso = 1; iso <= NUM_H_ISOTOPES; iso++) { int count = inchi_atom.num_iso_H[iso]; while (count-- > 0) { int h = mol.addAtom(ELEM_H); mol.setAtomIsotope(h, iso); mol.addBond(root_atom, h, BOND_SINGLE); } } } // Set atom charges, radicals and etc. for (int i = 0; i < inchi_output.num_atoms; i++) { const inchi_Atom &inchi_atom = inchi_output.atom[i]; int idx = atom_indices[i]; mol.setAtomCharge(idx, inchi_atom.charge); if (inchi_atom.isotopic_mass) { int default_iso = Element::getDefaultIsotope(mol.getAtomNumber(idx)); mol.setAtomIsotope(idx, default_iso + inchi_atom.isotopic_mass - ISOTOPIC_SHIFT_FLAG); } if (inchi_atom.radical) mol.setAtomRadical(idx, inchi_atom.radical); mol.setImplicitH(idx, inchi_atom.num_iso_H[0]); } neutralizeV5Nitrogen(mol); // Process stereoconfiguration for (int i = 0; i < inchi_output.num_stereo0D; i++) { inchi_Stereo0D &stereo0D = inchi_output.stereo0D[i]; if (stereo0D.type == INCHI_StereoType_DoubleBond) { if (stereo0D.parity != INCHI_PARITY_ODD && stereo0D.parity != INCHI_PARITY_EVEN) continue; int bond = mol.findEdgeIndex(stereo0D.neighbor[1], stereo0D.neighbor[2]); bool valid = mol.cis_trans.registerBondAndSubstituents(bond); if (!valid) throw Error("Indigo-InChI: Unsupported cis-trans configuration for " "bond %d (atoms %d-%d-%d-%d)", bond, stereo0D.neighbor[0], stereo0D.neighbor[1], stereo0D.neighbor[2], stereo0D.neighbor[3]); int vb, ve; const Edge &edge = mol.getEdge(bond); if (edge.beg == stereo0D.neighbor[1]) { vb = stereo0D.neighbor[0]; ve = stereo0D.neighbor[3]; } else if (edge.beg == stereo0D.neighbor[2]) { vb = stereo0D.neighbor[3]; ve = stereo0D.neighbor[0]; } else throw Error("Indigo-InChI: Internal error: cannot find cis-trans bond indices"); const int *subst = mol.cis_trans.getSubstituents(bond); bool same_side; if (subst[0] == vb) same_side = (subst[2] == ve); else if (subst[1] == vb) same_side = (subst[3] == ve); else throw Error("Indigo-InChI: Internal error: cannot find cis-trans bond indices (#2)"); if (stereo0D.parity == INCHI_PARITY_EVEN) same_side = !same_side; mol.cis_trans.setParity(bond, same_side ? MoleculeCisTrans::CIS : MoleculeCisTrans::TRANS); } else if (stereo0D.type == INCHI_StereoType_Tetrahedral) { if (stereo0D.parity != INCHI_PARITY_ODD && stereo0D.parity != INCHI_PARITY_EVEN) continue; int pyramid[4]; if (stereo0D.central_atom == stereo0D.neighbor[0]) { pyramid[1] = stereo0D.neighbor[1]; pyramid[0] = stereo0D.neighbor[2]; pyramid[2] = stereo0D.neighbor[3]; pyramid[3] = -1; } else { pyramid[0] = stereo0D.neighbor[0]; pyramid[1] = stereo0D.neighbor[1]; pyramid[2] = stereo0D.neighbor[2]; pyramid[3] = stereo0D.neighbor[3]; } if (stereo0D.parity == INCHI_PARITY_ODD) std::swap(pyramid[0], pyramid[1]); mol.stereocenters.add(stereo0D.central_atom, MoleculeStereocenters::ATOM_ABS, 0, pyramid); } } } void InchiWrapper::generateInchiInput(Molecule &mol, inchi_Input &input, Array<inchi_Atom> &atoms, Array<inchi_Stereo0D> &stereo) { QS_DEF(Array<int>, mapping); mapping.clear_resize(mol.vertexEnd()); mapping.fffill(); int index = 0; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) mapping[v] = index++; atoms.clear_resize(index); atoms.zerofill(); stereo.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { inchi_Atom &atom = atoms[mapping[v]]; int atom_number = mol.getAtomNumber(v); if (atom_number == ELEM_PSEUDO) throw Error("Molecule with pseudoatom (%s) cannot be converted into InChI", mol.getPseudoAtom(v)); if (atom_number == ELEM_RSITE) throw Error("Molecule with RGroups cannot be converted into InChI"); strncpy(atom.elname, Element::toString(atom_number), ATOM_EL_LEN); Vec3f &c = mol.getAtomXyz(v); atom.x = c.x; atom.y = c.y; atom.z = c.z; // connectivity const Vertex &vtx = mol.getVertex(v); int nei_idx = 0; for (int nei = vtx.neiBegin(); nei != vtx.neiEnd(); nei = vtx.neiNext(nei)) { int v_nei = vtx.neiVertex(nei); atom.neighbor[nei_idx] = mapping[v_nei]; int edge_idx = vtx.neiEdge(nei); atom.bond_type[nei_idx] = getInchiBondType(mol.getBondOrder(edge_idx)); int bond_stereo = INCHI_BOND_STEREO_NONE; if (mol.cis_trans.isIgnored(edge_idx)) bond_stereo = INCHI_BOND_STEREO_DOUBLE_EITHER; else { int dir = mol.getBondDirection2(v, v_nei); if (mol.getBondDirection2(v, v_nei) == BOND_EITHER) bond_stereo = INCHI_BOND_STEREO_SINGLE_1EITHER; else if (mol.getBondDirection2(v_nei, v) == BOND_EITHER) bond_stereo = INCHI_BOND_STEREO_SINGLE_2EITHER; } atom.bond_stereo[nei_idx] = bond_stereo; nei_idx++; } atom.num_bonds = vtx.degree(); // Other properties atom.isotopic_mass = mol.getAtomIsotope(v); atom.radical = mol.getAtomRadical(v); atom.charge = mol.getAtomCharge(v); // Hydrogens int hcount = -1; if (Molecule::shouldWriteHCount(mol, v) || mol.isExplicitValenceSet(v) || mol.isImplicitHSet(v)) { if (mol.getAtomAromaticity(v) == ATOM_AROMATIC && atom_number == ELEM_C && atom.charge == 0 && atom.radical == 0) { // Do not set number of implicit hydrogens here as InChI throws an exception on // the molecule B1=CB=c2cc3B=CC=c3cc12 ; } else // set -1 to tell InChI add implicit hydrogens automatically hcount = mol.getImplicitH_NoThrow(v, -1); } atom.num_iso_H[0] = hcount; } // Process cis-trans bonds for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { if (mol.cis_trans.getParity(e) == 0) continue; int subst[4]; mol.cis_trans.getSubstituents_All(e, subst); const Edge &edge = mol.getEdge(e); inchi_Stereo0D &st = stereo.push(); // Write it as // #0 - #1 = #2 - #3 st.neighbor[0] = mapping[subst[0]]; st.neighbor[1] = mapping[edge.beg]; st.neighbor[2] = mapping[edge.end]; st.neighbor[3] = mapping[subst[2]]; if (mol.cis_trans.getParity(e) == MoleculeCisTrans::CIS) st.parity = INCHI_PARITY_ODD; else st.parity = INCHI_PARITY_EVEN; st.central_atom = NO_ATOM; st.type = INCHI_StereoType_DoubleBond; } // Process tetrahedral stereocenters for (int i = mol.stereocenters.begin(); i != mol.stereocenters.end(); i = mol.stereocenters.next(i)) { int v = mol.stereocenters.getAtomIndex(i); int type, group, pyramid[4]; mol.stereocenters.get(v, type, group, pyramid); if (type == MoleculeStereocenters::ATOM_ANY) continue; for (int i = 0; i < 4; i++) if (pyramid[i] != -1) pyramid[i] = mapping[pyramid[i]]; inchi_Stereo0D &st = stereo.push(); /* 4 neighbors X neighbor[4] : {#W, #X, #Y, #Z} | central_atom: #A W--A--Y type : INCHI_StereoType_Tetrahedral | Z parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' 3 neighbors Y Y neighbor[4] : {#A, #X, #Y, #Z} / / central_atom: #A X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral \ \ Z Z */ int offset = 0; if (pyramid[3] == -1) offset = 1; st.neighbor[offset] = mapping[pyramid[0]]; st.neighbor[offset + 1] = mapping[pyramid[1]]; st.neighbor[offset + 2] = mapping[pyramid[2]]; if (offset == 0) st.neighbor[3] = mapping[pyramid[3]]; else st.neighbor[0] = mapping[v]; st.parity = INCHI_PARITY_ODD; if (offset != 0) st.parity = INCHI_PARITY_ODD; else st.parity = INCHI_PARITY_EVEN; st.central_atom = mapping[v]; st.type = INCHI_StereoType_Tetrahedral; } input.atom = atoms.ptr(); input.num_atoms = atoms.size(); input.stereo0D = stereo.ptr(); input.num_stereo0D = stereo.size(); input.szOptions = options.ptr(); } void InchiWrapper::saveMoleculeIntoInchi(Molecule &mol, Array<char> &inchi) { inchi_Input input; QS_DEF(Array<inchi_Atom>, atoms); QS_DEF(Array<inchi_Stereo0D>, stereo); // Check if structure has aromatic bonds bool has_aromatic = false; for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) if (mol.getBondOrder(e) == BOND_AROMATIC) { has_aromatic = true; break; } Molecule *target = &mol; Obj<Molecule> dearom; if (has_aromatic) { dearom.create(); dearom->clone(mol, 0, 0); try { AromaticityOptions arom_options; arom_options.method = AromaticityOptions::GENERIC; arom_options.unique_dearomatization = true; dearom->dearomatize(arom_options); } catch (NonUniqueDearomatizationException &ex) { // Do not allow non-unique dearomatizations ex.throwSelf(); } catch (DearomatizationException &) { } target = dearom.get(); } generateInchiInput(*target, input, atoms, stereo); InchiMemObject<inchi_Output> inchi_output_obj(FreeINCHI); inchi_Output &output = inchi_output_obj.ref(); // lock OsLocker locker(inchi_lock); int ret = GetINCHI(&input, &output); if (output.szMessage) warning.readString(output.szMessage, true); if (output.szLog) { const char *unrec_opt_prefix = "Unrecognized option:"; if (strncmp(output.szLog, unrec_opt_prefix, strlen(unrec_opt_prefix)) == 0) { size_t i; for (i = 0; i < strlen(output.szLog); i++) if (output.szLog[i] == '\n') break; Array<char> unrec_opt; if (i > 0) unrec_opt.copy(output.szLog, i - 1); unrec_opt.push(0); throw Error("Indigo-InChI: %s.", unrec_opt.ptr());; } log.readString(output.szLog, true); } if (output.szAuxInfo) auxInfo.readString(output.szAuxInfo, true); if (ret != inchi_Ret_OKAY && ret != inchi_Ret_WARNING) { // Construct error before dispoing inchi output to preserve error message Error error("Indigo-InChI: InChI generation failed: %s. Code: %d.", output.szMessage, ret); throw error; } inchi.readString(output.szInChI, true); } void InchiWrapper::InChIKey(const char *inchi, Array<char> &output) { // lock OsLocker locker(inchi_lock); output.resize(28); output.zerofill(); int ret = GetINCHIKeyFromINCHI(inchi, 0, 0, output.ptr(), 0, 0); if (ret != INCHIKEY_OK) { if (ret == INCHIKEY_UNKNOWN_ERROR) throw Error("INCHIKEY_UNKNOWN_ERROR"); else if (ret == INCHIKEY_EMPTY_INPUT) throw Error("INCHIKEY_EMPTY_INPUT"); else if (ret == INCHIKEY_INVALID_INCHI_PREFIX) throw Error("INCHIKEY_INVALID_INCHI_PREFIX"); else if (ret == INCHIKEY_NOT_ENOUGH_MEMORY) throw Error("INCHIKEY_NOT_ENOUGH_MEMORY"); else if (ret == INCHIKEY_INVALID_INCHI) throw Error("INCHIKEY_INVALID_INCHI"); else if (ret == INCHIKEY_INVALID_STD_INCHI) throw Error("INCHIKEY_INVALID_STD_INCHI"); else throw Error("Undefined error"); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/max_common_submolecule.cpp�����������������������������������������0000664�0000000�0000000�00000003702�12710376503�0024037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/max_common_submolecule.h" #include "molecule/molecule.h" #include "molecule/molecule_exact_matcher.h" using namespace indigo; MaxCommonSubmolecule::MaxCommonSubmolecule(BaseMolecule &submol, BaseMolecule &supermol): MaxCommonSubgraph(submol,supermol) { conditionEdgeWeight = matchBonds; conditionVerticesColor = matchAtoms; } bool MaxCommonSubmolecule::matchBonds (Graph &g1, Graph &g2, int i, int j, void* userdata){ BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; int flags = MoleculeExactMatcher::CONDITION_ELECTRONS; if(userdata) flags = *((int*)userdata); if(flags > MoleculeExactMatcher::CONDITION_ALL || flags < 0) throw Error("Wrong userdata...need correct flag"); return MoleculeExactMatcher::matchBonds(mol1, mol2, i, j, flags); } bool MaxCommonSubmolecule::matchAtoms (Graph &g1, Graph &g2, const int *core_sub, int i, int j, void* userdata){ BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; int flags = MoleculeExactMatcher::CONDITION_ELECTRONS; if(userdata) flags = *((int*)userdata); if(flags > MoleculeExactMatcher::CONDITION_ALL || flags < 0) throw Error("Wrong userdata...need correct flag"); return MoleculeExactMatcher::matchAtoms(mol1, mol2, i, j, flags); } ��������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule.cpp�������������������������������������������������������0000664�0000000�0000000�00000132315�12710376503�0021113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "base_c/defs.h" #include "base_cpp/output.h" #include "molecule/elements.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_dearom.h" #include "molecule/molecule_standardize.h" using namespace indigo; Molecule::Molecule () { _aromatized = false; } Molecule::~Molecule () { } Molecule& Molecule::asMolecule () { return *this; } void Molecule::clear () { BaseMolecule::clear(); _pseudo_atom_values.clear(); _atoms.clear(); _bond_orders.clear(); _connectivity.clear(); _aromaticity.clear(); _implicit_h.clear(); _total_h.clear(); _valence.clear(); _radicals.clear(); _template_occurrences.clear(); _template_names.clear(); _template_classes.clear(); _aromatized = false; updateEditRevision(); } void Molecule::_flipBond (int atom_parent, int atom_from, int atom_to) { int src_bond_idx = findEdgeIndex(atom_parent, atom_from); int bond_order = getBondOrder(src_bond_idx); addBond(atom_parent, atom_to, bond_order); updateEditRevision(); } void Molecule::_mergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) { Molecule &mol = bmol.asMolecule(); int i; // atoms and pseudo-atoms and connectivities and implicit H counters for (i = 0; i < vertices.size(); i++) { int newidx = mapping[vertices[i]]; _atoms.expand(newidx + 1); _atoms[newidx] = mol._atoms[vertices[i]]; if (mol.isPseudoAtom(vertices[i])) setPseudoAtom(newidx, mol.getPseudoAtom(vertices[i])); if (mol.isTemplateAtom(vertices[i])) { setTemplateAtom(newidx, mol.getTemplateAtom(vertices[i])); setTemplateAtomClass(newidx, mol.getTemplateAtomClass(vertices[i])); setTemplateAtomSeqid(newidx, mol.getTemplateAtomSeqid(vertices[i])); } bool nei_mapped = (getVertex(newidx).degree() == mol.getVertex(vertices[i]).degree()); if (mol._connectivity.size() > vertices[i]) { _connectivity.expandFill(newidx + 1, -1); if (nei_mapped) _connectivity[newidx] = mol._connectivity[vertices[i]]; } if (mol._valence.size() > vertices[i]) { _valence.expandFill(newidx + 1, -1); if (nei_mapped) _valence[newidx] = mol._valence[vertices[i]]; } if (mol._implicit_h.size() > vertices[i]) { _implicit_h.expandFill(newidx + 1, -1); if (nei_mapped) _implicit_h[newidx] = mol._implicit_h[vertices[i]]; } if (mol._radicals.size() > vertices[i]) { _radicals.expandFill(newidx + 1, -1); if (nei_mapped) _radicals[newidx] = mol._radicals[vertices[i]]; } } // bonds if (edges != 0) for (i = 0; i < edges->size(); i++) { const Edge &edge = mol.getEdge(edges->at(i)); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg == -1 || end == -1) // must have been thrown before in mergeWithSubgraph() throw Error("_mergeWithSubmolecule: internal"); int idx = findEdgeIndex(beg, end); _bond_orders.expand(idx + 1); _bond_orders[idx] = mol._bond_orders[edges->at(i)]; } else for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg == -1 || end == -1) continue; int idx = findEdgeIndex(beg, end); _bond_orders.expand(idx + 1); _bond_orders[idx] = mol._bond_orders[i]; } } /* void Molecule::setQueryBondAromatic (int idx) { setBondAromatic(idx); _q_bonds->at(idx).type = 0; _q_bonds->at(idx).can_be_aromatic = false; } void Molecule::setQueryBondFuzzyAromatic (int idx) { _q_bonds->at(idx).can_be_aromatic = true; } */ void Molecule::_validateVertexConnectivity (int idx, bool validate) { if (validate) { getAtomConnectivity_noImplH(idx); getImplicitH_NoThrow(idx, -1); getAtomValence_NoThrow(idx, -1); } else { if (_connectivity.size() > idx) _connectivity[idx] = -1; if (_implicit_h.size() > idx) { _atoms[idx].explicit_impl_h = false; _implicit_h[idx] = -1; } if (_total_h.size() > idx) _total_h[idx] = -1; if (_valence.size() > idx) { _atoms[idx].explicit_valence = false; _valence[idx] = -1; } if (_radicals.size() > idx) { _radicals[idx] = -1; } } updateEditRevision(); } void Molecule::_invalidateVertexCache (int idx) { if (!isExplicitValenceSet(idx) && _valence.size() > idx) _valence[idx] = -1; if (!isImplicitHSet(idx) && _implicit_h.size() > idx) _implicit_h[idx] = -1; if (_total_h.size() > idx) _total_h[idx] = -1; } void Molecule::setBondOrder (int idx, int order, bool keep_connectivity) { const Edge &edge = getEdge(idx); //if (_atoms[edge.beg].explicit_valence || _atoms[edge.end].explicit_valence) // throw Error("setBondOrder(): explicit valence on atom"); //if (_atoms[edge.beg].explicit_impl_h || _atoms[edge.end].explicit_impl_h) // throw Error("setBondOrder(): explicit H count on atom"); if (keep_connectivity && _bond_orders[idx] != BOND_AROMATIC && order != BOND_AROMATIC) throw Error("setBondOrder(): keep_connectivity must be used only with aromatic bonds"); // If connectivity should be kept then calculate connectivity and // all dependent constants (valence, implicit hydrogens) _validateVertexConnectivity(edge.beg, keep_connectivity); _validateVertexConnectivity(edge.end, keep_connectivity); if (_bond_orders[idx] == BOND_AROMATIC || order == BOND_AROMATIC) _aromaticity.clear(); _bond_orders[idx] = order; if (order != BOND_DOUBLE) cis_trans.setParity(idx, 0); _aromatized = false; updateEditRevision(); } void Molecule::setBondOrder_Silent (int idx, int order) { _bond_orders[idx] = order; updateEditRevision(); } void Molecule::setAtomCharge (int idx, int charge) { _atoms[idx].charge = charge; if (_implicit_h.size() > idx) _implicit_h[idx] = -1; if (_total_h.size() > idx) _total_h[idx] = -1; if (_radicals.size() > idx) _radicals[idx] = -1; updateEditRevision(); } void Molecule::setAtomCharge_Silent (int idx, int charge) { _atoms[idx].charge = charge; updateEditRevision(); } void Molecule::setAtomIsotope (int idx, int isotope) { _atoms[idx].isotope = isotope; updateEditRevision(); } void Molecule::setAtomRadical (int idx, int radical) { _radicals.expandFill(idx + 1, -1); _radicals[idx] = radical; _invalidateVertexCache(idx); updateEditRevision(); } void Molecule::setExplicitValence (int idx, int valence) { _valence.expandFill(idx + 1, -1); _valence[idx] = valence; _atoms[idx].explicit_valence = true; _invalidateVertexCache(idx); updateEditRevision(); } void Molecule::resetExplicitValence (int idx) { if (_valence.size() > idx) _valence[idx] = -1; _atoms[idx].explicit_valence = false; _invalidateVertexCache(idx); updateEditRevision(); } bool Molecule::isExplicitValenceSet (int idx) { return _atoms[idx].explicit_valence; } void Molecule::setValence (int idx, int valence) { _valence.expandFill(idx + 1, -1); _valence[idx] = valence; updateEditRevision(); } void Molecule::setImplicitH (int idx, int impl_h) { _implicit_h.expandFill(idx + 1, -1); _implicit_h[idx] = impl_h; _atoms[idx].explicit_impl_h = true; _invalidateVertexCache(idx); updateEditRevision(); } bool Molecule::isImplicitHSet (int idx) { return _atoms[idx].explicit_impl_h; } void Molecule::setPseudoAtom (int idx, const char *text) { _atoms[idx].number = ELEM_PSEUDO; _atoms[idx].pseudoatom_value_idx = _pseudo_atom_values.add(text); // TODO: take care of memory allocated here in _pseudo_atom_values updateEditRevision(); } void Molecule::setTemplateAtom (int idx, const char *text) { _atoms[idx].number = ELEM_TEMPLATE; _atoms[idx].template_occur_idx = _template_occurrences.add(); _TemplateOccurrence &occur = _template_occurrences.at(_atoms[idx].template_occur_idx); occur.name_idx = _template_names.add(text); occur.seq_id = -1; occur.contracted = -1; updateEditRevision(); } void Molecule::setTemplateAtomClass (int idx, const char *text) { if (_atoms[idx].number != ELEM_TEMPLATE) throw Error("setTemplateAtomClass(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(_atoms[idx].template_occur_idx); occur.class_idx = _template_classes.add(text); updateEditRevision(); } void Molecule::setTemplateAtomSeqid (int idx, int seq_id) { if (_atoms[idx].number != ELEM_TEMPLATE) throw Error("setTemplateAtomSeqid(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(_atoms[idx].template_occur_idx); occur.seq_id = seq_id; updateEditRevision(); } void Molecule::setTemplateAtomDisplayOption (int idx, int option) { if (_atoms[idx].number != ELEM_TEMPLATE) throw Error("setTemplateAtomDisplayOption(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(_atoms[idx].template_occur_idx); occur.contracted = option; updateEditRevision(); } int Molecule::getVacantPiOrbitals (int atom_idx, int conn, int *lonepairs_out) { int group = Element::group(getAtomNumber(atom_idx)); int charge = getAtomCharge(atom_idx); int radical = getAtomRadical(atom_idx); return BaseMolecule::getVacantPiOrbitals(group, charge, radical, conn, lonepairs_out); } int Molecule::getVacantPiOrbitals (int atom_idx, int *lonepairs_out) { return getVacantPiOrbitals(atom_idx, getAtomConnectivity(atom_idx), lonepairs_out); } int Molecule::getAtomConnectivity (int idx) { int conn = getAtomConnectivity_noImplH(idx); if (conn < 0) return -1; int impl_h = getImplicitH(idx); return impl_h + conn; } int Molecule::getAtomConnectivity_NoThrow (int idx, int fallback) { try { return getAtomConnectivity(idx); } catch (Element::Error &) { return fallback; } } int Molecule::getAtomConnectivity_noImplH (int idx) { if (_connectivity.size() > idx && _connectivity[idx] >= 0) return _connectivity[idx]; int conn = calcAtomConnectivity_noImplH(idx); _connectivity.expandFill(idx + 1, -1); _connectivity[idx] = conn; return conn; } int Molecule::calcAtomConnectivity_noImplH (int idx) { const Vertex &vertex = getVertex(idx); int i, conn = 0; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int order = getBondOrder(vertex.neiEdge(i)); if (order == BOND_AROMATIC) return -1; if (order == -1) // can happen on TautomerSuperStructure continue; conn += order; } for (i = 1; i <= attachmentPointCount(); i++) { int j = 0, aidx; for (j = 0; (aidx = getAttachmentPoint(i, j)) != -1; j++) { if (aidx == idx) conn++; } } return conn; } void Molecule::calcAromaticAtomConnectivity (int idx, int &n_arom, int &min_conn) { const Vertex &vertex = getVertex(idx); int i; n_arom = 0; min_conn = 0; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int order = getBondOrder(vertex.neiEdge(i)); if (order == BOND_AROMATIC) { min_conn++; n_arom++; } else min_conn += order; } if (isImplicitHSet(idx)) min_conn += getImplicitH(idx); } int Molecule::totalHydrogensCount () { int i, total_h = 0; for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) { if (getAtomNumber(i) == ELEM_H) total_h++; total_h += getImplicitH(i); } return total_h; } int Molecule::matchAtomsCmp (Graph &g1, Graph &g2, int idx1, int idx2, void *userdata) { Molecule &m1 = ((BaseMolecule &)g1).asMolecule(); Molecule &m2 = ((BaseMolecule &)g2).asMolecule(); if (m1.isPseudoAtom(idx1) && !m2.isPseudoAtom(idx2)) return 1; if (!m1.isPseudoAtom(idx1) && m2.isPseudoAtom(idx2)) return -1; if (m1.isTemplateAtom(idx1) && !m2.isTemplateAtom(idx2)) return 1; if (!m1.isTemplateAtom(idx1) && m2.isTemplateAtom(idx2)) return -1; if (m1.isRSite(idx1) && !m2.isRSite(idx2)) return 1; if (!m1.isRSite(idx1) && m2.isRSite(idx2)) return -1; if (m1.isAtomHighlighted(idx1) && !m2.isAtomHighlighted(idx2)) return 1; if (!m1.isAtomHighlighted(idx1) && m2.isAtomHighlighted(idx2)) return -1; QS_DEF(Array<int>, ai1); QS_DEF(Array<int>, ai2); m1.getAttachmentIndicesForAtom(idx1, ai1); m2.getAttachmentIndicesForAtom(idx2, ai2); if (ai1.size() != ai2.size()) return ai1.size() - ai2.size(); int i; for (i = 0; i != ai1.size(); i++) if (ai1[i] != ai2[i]) return ai1[i] - ai2[i]; bool pseudo = false; if (m1.isRSite(idx1) && m2.isRSite(idx2)) { int res = m2.getRSiteBits(idx2) - m1.getRSiteBits(idx1); if (res != 0) return res; pseudo = true; } if (m1.isPseudoAtom(idx1) && m2.isPseudoAtom(idx2)) { int res = strcmp(m1.getPseudoAtom(idx1), m2.getPseudoAtom(idx2)); if (res != 0) return res; pseudo = true; } else if (m1.isTemplateAtom(idx1) && m2.isTemplateAtom(idx2)) { int res = strcmp(m1.getTemplateAtom(idx1), m2.getTemplateAtom(idx2)); if (res != 0) return res; pseudo = true; } else { if (m1.getAtomNumber(idx1) > m2.getAtomNumber(idx2)) return 1; if (m1.getAtomNumber(idx1) < m2.getAtomNumber(idx2)) return -1; } if (m1.getAtomIsotope(idx1) > m2.getAtomIsotope(idx2)) return 1; if (m1.getAtomIsotope(idx1) < m2.getAtomIsotope(idx2)) return -1; if (m1.getAtomCharge(idx1) > m2.getAtomCharge(idx2)) return 1; if (m1.getAtomCharge(idx1) < m2.getAtomCharge(idx2)) return -1; if (!pseudo && m1.getAtomRadical(idx1) > m2.getAtomRadical(idx2)) return 1; if (!pseudo && m1.getAtomRadical(idx1) < m2.getAtomRadical(idx2)) return -1; return 0; } int Molecule::getAtomAromaticity (int idx) { if (_aromaticity.size() > idx && _aromaticity[idx] >= 0) return _aromaticity[idx]; const Vertex &vertex = getVertex(idx); int i; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int order = getBondOrder(vertex.neiEdge(i)); if (order == BOND_AROMATIC) { _aromaticity.expandFill(idx + 1, -1); _aromaticity[idx] = ATOM_AROMATIC; return ATOM_AROMATIC; } // not checking order == -1 because it is not QueryMolecule } _aromaticity.expandFill(idx + 1, -1); _aromaticity[idx] = ATOM_ALIPHATIC; return ATOM_ALIPHATIC; } void Molecule::_removeAtoms (const Array<int> &indices, const int *mapping) { int i; for (i = 0; i < indices.size(); i++) { int idx = indices[i]; const Vertex &vertex = getVertex(idx); int j; for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int nei = vertex.neiVertex(j); int order = getBondOrder(vertex.neiEdge(j)); if (mapping[nei] < 0) // the neighbor is marked for removal too continue; // Precalculate and store into cache number of implicit hydrogens // This is required for correct hydrogens unfolding for molecules // like [H]S([H])([H])C (that is seems to be invalid) if (!isRSite(nei) && !isPseudoAtom(nei) && !isTemplateAtom(nei)) if (_implicit_h.size() <= nei || _implicit_h[nei] < 0) getImplicitH_NoThrow(nei, -1); if (_implicit_h.size() > nei && _implicit_h[nei] >= 0) { if (order == BOND_SINGLE) _implicit_h[nei]++; else if (order == BOND_DOUBLE) _implicit_h[nei] += 2; else if (order == BOND_TRIPLE) _implicit_h[nei] += 3; else _implicit_h[nei] = -1; } if (_connectivity.size() > nei && _connectivity[nei] >= 0) { if (order == BOND_SINGLE) _connectivity[nei]--; else if (order == BOND_DOUBLE) _connectivity[nei] -= 2; else if (order == BOND_TRIPLE) _connectivity[nei] -= 3; else _connectivity[nei] = -1; } } } updateEditRevision(); } void Molecule::unfoldHydrogens (Array<int> *markers_out, int max_h_cnt, bool impl_h_no_throw) { int v_end = vertexEnd(); QS_DEF(Array<int>, imp_h_count); imp_h_count.clear_resize(vertexEnd()); imp_h_count.zerofill(); // getImplicitH can throw an exception, and we need to get the number of hydrogens // before unfolding them for (int i = vertexBegin(); i < v_end; i = vertexNext(i)) { if (isPseudoAtom(i) || isRSite(i) || isTemplateAtom(i)) continue; if (impl_h_no_throw) imp_h_count[i] = getImplicitH_NoThrow(i, 0); else imp_h_count[i] = getImplicitH(i); } if (markers_out != 0) { markers_out->clear_resize(vertexEnd()); markers_out->zerofill(); } for (int i = vertexBegin(); i < v_end; i = vertexNext(i)) { int impl_h = imp_h_count[i]; if (impl_h == 0) continue; int h_cnt; if ((max_h_cnt == -1) || (max_h_cnt > impl_h)) h_cnt = impl_h; else h_cnt = max_h_cnt; for (int j = 0; j < h_cnt; j++) { int new_h_idx = addAtom(ELEM_H); addBond(i, new_h_idx, BOND_SINGLE); if (markers_out != 0) { markers_out->expandFill(new_h_idx + 1, 0); markers_out->at(new_h_idx) = 1; } stereocenters.registerUnfoldedHydrogen(i, new_h_idx); cis_trans.registerUnfoldedHydrogen(i, new_h_idx); allene_stereo.registerUnfoldedHydrogen(i, new_h_idx); sgroups.registerUnfoldedHydrogen(i, new_h_idx); } _validateVertexConnectivity(i, false); _implicit_h[i] = impl_h - h_cnt; } updateEditRevision(); } int Molecule::getImplicitH (int idx) { int conn = getAtomConnectivity_noImplH(idx); return _getImplicitHForConnectivity(idx, conn, true); } int Molecule::getImplicitH_NoThrow (int idx, int fallback) { try { return getImplicitH(idx); } catch (Element::Error &) { return fallback; } } int Molecule::calcImplicitHForConnectivity (int idx, int conn) { try { return _getImplicitHForConnectivity(idx, conn, false); } catch (Element::Error &) { return -1; } } int Molecule::_getImplicitHForConnectivity (int idx, int conn, bool use_cache) { if (_atoms[idx].number == ELEM_PSEUDO) throw Error("getImplicitH() does not work on pseudo-atoms"); if (_atoms[idx].number == ELEM_RSITE) throw Error("getImplicitH() does not work on R-sites"); if (use_cache) { if (_implicit_h.size() > idx && _implicit_h[idx] >= 0) return _implicit_h[idx]; } const _Atom &atom = _atoms[idx]; int radical = 0; if (_radicals.size() > idx && _radicals[idx] >= 0) radical = _radicals[idx]; int impl_h = -1; if (conn < 0) { if (getAtomAromaticity(idx) == ATOM_AROMATIC) { int degree = getVertex(idx).degree(); int i; for (i = 1; i <= attachmentPointCount(); i++) { int j = 0, aidx; for (j = 0; (aidx = getAttachmentPoint(i, j)) != -1; j++) { if (aidx == idx) degree++; } } if (atom.number == ELEM_C && atom.charge == 0) { if (degree == 3) impl_h = -Element::radicalElectrons(radical); else if (degree == 2) impl_h = 1 - Element::radicalElectrons(radical); } else if (atom.number == ELEM_O && atom.charge == 0) impl_h = 0; else if (atom.number == ELEM_N && atom.charge == 0 && degree == 3) impl_h = 0; else if (atom.number == ELEM_S && atom.charge == 0 && degree == 3) impl_h = 0; } else throw Error("internal: unsure connectivity on an aliphatic atom"); if (impl_h < 0) throw Element::Error("can not calculate implicit hydrogens on aromatic %s, charge %d, degree %d, %d radical electrons", Element::toString(atom.number), atom.charge, getVertex(idx).degree(), Element::radicalElectrons(radical)); } else { if (atom.explicit_valence) { // Explicit valence means that the molecule was converted from Molfile. // Conventions are that if we have explicit valence, we discard radical // and charge when calculating implicit hydgogens. impl_h = _valence[idx] - Element::calcValenceMinusHyd(atom.number, 0, 0, conn); if (impl_h < 0) throw Element::Error("explicit valence %d specified on %s, but %d bonds are drawn", _valence[idx], Element::toString(atom.number), conn); } else if (isNitrogenV5(idx)) { // special case of 5-connected nitrogen like "CN(=O)=O". // It should really be C[N+](O-)=O, but we let people live in happy ignorance. impl_h = 0; } else { int radical = -1; if (_radicals.size() > idx) radical = _radicals[idx]; int valence; if (radical == -1) { // no information about implicit H, not sure about radical either -- // this can happen exclusively in CML. if (Element::calcValence(atom.number, atom.charge, 0, conn, valence, impl_h, false)) radical = 0; else if (Element::calcValence(atom.number, atom.charge, RADICAL_SINGLET, conn, valence, impl_h, false)) radical = RADICAL_SINGLET; else if (Element::calcValence(atom.number, atom.charge, RADICAL_DOUBLET, conn, valence, impl_h, false)) radical = RADICAL_DOUBLET; else throw Element::Error("can not calculate valence on %s, charge %d, connectivity %d", Element::toString(atom.number), atom.charge, conn); if (use_cache) { _radicals.expandFill(idx + 1, -1); _radicals[idx] = radical; } } else // no information about implicit H, but sure about radical -- // this is a commmon situtation for Molfiles or non-bracketed SMILES atoms. // Will throw an error on 5-valent carbon and such. Element::calcValence(atom.number, atom.charge, radical, conn, valence, impl_h, true); } } if (use_cache) { _implicit_h.expandFill(idx + 1, -1); _implicit_h[idx] = impl_h; } if (impl_h < 0) throw Error("_getImplicitHForConnectivity(): internal"); return impl_h; } bool Molecule::isNitrogenV5 (int idx) { int conn = getAtomConnectivity_noImplH(idx); return isNitrogenV5ForConnectivity(idx, conn); } bool Molecule::isNitrogenV5ForConnectivity (int idx, int conn) { if (getAtomNumber(idx) != ELEM_N) return false; if (getAtomCharge(idx) != 0) return false; int radical = 0; if (_radicals.size() > idx && _radicals[idx] >= 0) radical = _radicals[idx]; int radical_elections = Element::radicalElectrons(radical); return (radical_elections == 0 && conn == 5) || (radical_elections == 1 && conn == 4); } int Molecule::getAtomNumber (int idx) { return _atoms[idx].number; } int Molecule::getAtomCharge (int idx) { return _atoms[idx].charge; } int Molecule::getAtomIsotope (int idx) { return _atoms[idx].isotope; } int Molecule::getAtomValence (int idx) { if (_atoms[idx].number == ELEM_PSEUDO) throw Error("getAtomValence() does not work on pseudo-atoms"); if (_atoms[idx].number == ELEM_TEMPLATE) throw Error("getAtomValence() does not work on template atoms"); if (_atoms[idx].number == ELEM_RSITE) throw Error("getAtomValence() does not work on R-sites"); if (_valence.size() > idx && _valence[idx] >= 0) return _valence[idx]; if (isNitrogenV5(idx)) { _valence.expandFill(idx + 1, -1); _valence[idx] = 4; return 4; } const _Atom &atom = _atoms[idx]; int conn = getAtomConnectivity_noImplH(idx); if (conn < 0) { int min_conn, n_arom; calcAromaticAtomConnectivity(idx, n_arom, min_conn); int val = Element::calcValenceOfAromaticAtom(atom.number, atom.charge, n_arom, min_conn); if (val >= 0) { _valence.expandFill(idx + 1, -1); _valence[idx] = val; return val; } throw Element::Error("can not calculate valence of %s (%d aromatic bonds, min connectivity %d, charge %d)", Element::toString(atom.number), n_arom, min_conn, atom.charge); } int radical = -1; int impl_h = -1; int valence; bool unusual_valence = false; if (_radicals.size() > idx && _radicals[idx] >= 0) radical = _radicals[idx]; if (_implicit_h.size() > idx && _implicit_h[idx] >= 0) { impl_h = _implicit_h[idx]; int normal_impl_h; if (radical == -1) { // have implicit H count, but no information about radical. Frequently occurs in SMILES // expressions like [CH2] or [C] if (Element::calcValence(atom.number, atom.charge, 0, conn, valence, normal_impl_h, false) && normal_impl_h == impl_h) radical = 0; // [SiH4] else if (Element::calcValence(atom.number, atom.charge, RADICAL_SINGLET, conn, valence, normal_impl_h, false) && normal_impl_h == impl_h) radical = RADICAL_SINGLET; // [CH2] else if (Element::calcValence(atom.number, atom.charge, RADICAL_DOUBLET, conn, valence, normal_impl_h, false) && normal_impl_h == impl_h) radical = RADICAL_DOUBLET; // [CH3] else if (Element::calcValence(atom.number, atom.charge, 0, conn + impl_h, valence, normal_impl_h, false) && normal_impl_h == 0) { radical = 0; // [PH5] valence = conn + impl_h; unusual_valence = true; } else if (Element::calcValence(atom.number, atom.charge, RADICAL_SINGLET, conn + impl_h, valence, normal_impl_h, false) && normal_impl_h == 0) { radical = RADICAL_SINGLET; valence = conn + impl_h; unusual_valence = true; } else if (Element::calcValence(atom.number, atom.charge, RADICAL_DOUBLET, conn + impl_h, valence, normal_impl_h, false) && normal_impl_h == 0) { radical = RADICAL_DOUBLET; // [PH4] valence = conn + impl_h; unusual_valence = true; } else // [C], [CH] { radical = 0; valence = conn + impl_h; unusual_valence = true; } _radicals.expandFill(idx + 1, -1); _radicals[idx] = radical; } else { // have both implicit H count and radicals -- can happen in CML or in extended SMILES if (Element::calcValence(atom.number, atom.charge, radical, conn, valence, normal_impl_h, false) && normal_impl_h == impl_h) ; else { // rare case valence = conn + impl_h; unusual_valence = true; } } } else { if (radical == -1) { // no information about implicit H, not sure about radical either -- // this can happen exclusively in CML. if (Element::calcValence(atom.number, atom.charge, 0, conn, valence, impl_h, false)) radical = 0; else if (Element::calcValence(atom.number, atom.charge, RADICAL_SINGLET, conn, valence, impl_h, false)) radical = RADICAL_SINGLET; else if (Element::calcValence(atom.number, atom.charge, RADICAL_DOUBLET, conn, valence, impl_h, false)) radical = RADICAL_DOUBLET; else throw Element::Error("can not calculate valence on %s, charge %d, connectivity %d", Element::toString(atom.number), atom.charge, conn); _radicals.expandFill(idx + 1, -1); _radicals[idx] = radical; } else // no information about implicit H, but sure about radical -- // this is a commmon situtation for Molfiles or non-bracketed SMILES atoms. // Will throw an error on 5-valent carbon and such. Element::calcValence(atom.number, atom.charge, radical, conn, valence, impl_h, true); _implicit_h.expandFill(idx + 1, -1); _implicit_h[idx] = impl_h; } _valence.expandFill(idx + 1, -1); _valence[idx] = valence; if (unusual_valence) _atoms[idx].explicit_valence = true; return valence; } int Molecule::getAtomRadical (int idx) { if (_atoms[idx].number == ELEM_PSEUDO) throw Error("getAtomValence() does not work on pseudo-atoms"); if (_atoms[idx].number == ELEM_RSITE) throw Error("getAtomValence() does not work on R-sites"); if (_radicals.size() > idx && _radicals[idx] >= 0) return _radicals[idx]; getAtomValence(idx); if (_radicals.size() > idx && _radicals[idx] >= 0) return _radicals[idx]; // getAtomValence() did not help: now we know that // this is either an aromatic atom or 5-valence nitrogen; // in any case, radical is zero. _radicals.expandFill(idx + 1, -1); _radicals[idx] = 0; return 0; } void Molecule::saveBondOrders (Molecule &mol, Array<int> &orders) { orders.copy(mol._bond_orders); } void Molecule::loadBondOrders (Molecule &mol, Array<int> &orders) { mol._bond_orders.copy(orders); mol.updateEditRevision(); } int Molecule::getAtomSubstCount (int idx) { int i, res = 0; const Vertex &vertex = getVertex(idx); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (_atoms[vertex.neiVertex(i)].number != ELEM_H) res++; } return res; } int Molecule::getAtomRingBondsCount (int idx) { int i, res = 0; const Vertex &vertex = getVertex(idx); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (getEdgeTopology(vertex.neiEdge(i)) == TOPOLOGY_RING) res++; } return res; } bool Molecule::isSaturatedAtom (int idx) { int i; const Vertex &vertex = getVertex(idx); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) if (getBondOrder(vertex.neiEdge(i)) != BOND_SINGLE) return false; return true; } int Molecule::getBondOrder (int idx) { return _bond_orders[idx]; } int Molecule::getBondTopology (int idx) { return getEdgeTopology(idx); } int Molecule::getExplicitValence (int idx) { if (_atoms[idx].explicit_valence) return _valence[idx]; if (_atoms[idx].number == ELEM_PSEUDO || _atoms[idx].number == ELEM_RSITE || _atoms[idx].number == ELEM_TEMPLATE) return -1; // try to calculate explicit valence from hydrogens, as in elemental carbon [C] try { getAtomValence(idx); } catch (Element::Error &) { return -1; } if (_atoms[idx].explicit_valence) return _valence[idx]; return -1; } bool Molecule::atomNumberBelongs (int idx, const int *numbers, int count) { int number = _atoms[idx].number; int i; for (i = 0; i < count; i++) if (number == numbers[i]) return true; return false; } bool Molecule::possibleAtomNumber (int idx, int number) { return _atoms[idx].number == number; } bool Molecule::possibleAtomNumberAndCharge (int idx, int number, int charge) { return _atoms[idx].number == number && _atoms[idx].charge == charge; } bool Molecule::possibleAtomNumberAndIsotope (int idx, int number, int isotope) { return _atoms[idx].number == number && _atoms[idx].isotope == isotope; } bool Molecule::possibleAtomIsotope (int idx, int isotope) { return _atoms[idx].isotope == isotope; } bool Molecule::possibleAtomCharge (int idx, int charge) { return _atoms[idx].charge == charge; } void Molecule::getAtomDescription (int idx, Array<char> &description) { _Atom &atom = _atoms[idx]; ArrayOutput output(description); if (atom.isotope != 0) output.printf("%d", atom.isotope); if (isPseudoAtom(idx)) output.printf("%s", getPseudoAtom(idx)); else if (isTemplateAtom(idx)) output.printf("%s", getTemplateAtom(idx)); else output.printf("%s", Element::toString(atom.number)); if (atom.charge == -1) output.printf("-"); else if (atom.charge == 1) output.printf("+"); else if (atom.charge > 0) output.printf("+%d", atom.charge); else if (atom.charge < 0) output.printf("-%d", -atom.charge); output.writeChar(0); } void Molecule::getBondDescription (int idx, Array<char> &description) { ArrayOutput output(description); switch (_bond_orders[idx]) { case BOND_SINGLE: output.printf("single"); return; case BOND_DOUBLE: output.printf("double"); return; case BOND_TRIPLE: output.printf("triple"); return; case BOND_AROMATIC: output.printf("aromatic"); return; } } bool Molecule::possibleBondOrder (int idx, int order) { return _bond_orders[idx] == order; } int Molecule::addAtom (int number) { int idx = _addBaseAtom(); _atoms.expand(idx + 1); return resetAtom(idx, number); } int Molecule::resetAtom (int idx, int number) { updateEditRevision(); memset(&_atoms[idx], 0, sizeof(_Atom)); _atoms[idx].number = number; _validateVertexConnectivity(idx, false); return idx; } int Molecule::addBond (int beg, int end, int order) { updateEditRevision(); int idx = _addBaseBond(beg, end); _bond_orders.expand(idx + 1); _bond_orders[idx] = order; _aromaticity.clear(); _aromatized = false; _validateVertexConnectivity(beg, false); _validateVertexConnectivity(end, false); return idx; } int Molecule::addBond_Silent (int beg, int end, int order) { updateEditRevision(); int idx = _addBaseBond(beg, end); _bond_orders.expand(idx + 1); _bond_orders[idx] = order; _aromaticity.clear(); _aromatized = false; return idx; } bool Molecule::isPseudoAtom (int idx) { return _atoms[idx].number == ELEM_PSEUDO; } const char * Molecule::getPseudoAtom (int idx) { const _Atom &atom = _atoms[idx]; if (atom.number != ELEM_PSEUDO) throw Error("getPseudoAtom(): atom #%d is not a pseudoatom", idx); const char *res = _pseudo_atom_values.at(atom.pseudoatom_value_idx); if (res == 0) throw Error("pseudoatom string is zero"); return res; } bool Molecule::isTemplateAtom (int idx) { return _atoms[idx].number == ELEM_TEMPLATE; } const char * Molecule::getTemplateAtom (int idx) { const _Atom &atom = _atoms[idx]; if (atom.number != ELEM_TEMPLATE) throw Error("getTemplateAtom(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(atom.template_occur_idx); const char *res = _template_names.at(occur.name_idx); if (res == 0) throw Error("template atom string is zero"); return res; } const char * Molecule::getTemplateAtomClass (int idx) { const _Atom &atom = _atoms[idx]; if (atom.number != ELEM_TEMPLATE) throw Error("getTemplateAtomClass(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(atom.template_occur_idx); const char *res = _template_classes.at(occur.class_idx); return res; } const int Molecule::getTemplateAtomSeqid (int idx) { const _Atom &atom = _atoms[idx]; if (atom.number != ELEM_TEMPLATE) throw Error("getTemplateAtomSeqid(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(atom.template_occur_idx); const int res = occur.seq_id; return res; } const int Molecule::getTemplateAtomDisplayOption (int idx) { const _Atom &atom = _atoms[idx]; if (atom.number != ELEM_TEMPLATE) throw Error("getTemplateAtomDisplayOption(): atom #%d is not a template atom", idx); _TemplateOccurrence &occur = _template_occurrences.at(atom.template_occur_idx); const int res = occur.contracted; return res; } BaseMolecule * Molecule::neu () { return new Molecule(); } bool Molecule::bondStereoCare (int idx) { if (!cis_trans.exists()) return false; // In ordinary molecule all bond's stereoconfigurations are important return cis_trans.getParity(idx) != 0; } bool Molecule::aromatize (const AromaticityOptions &options) { updateEditRevision(); bool arom_found = MoleculeAromatizer::aromatizeBonds(*this, options); _aromatized = true; return arom_found; } bool Molecule::dearomatize (const AromaticityOptions &options) { updateEditRevision(); return MoleculeDearomatizer::dearomatizeMolecule(*this, options); } int Molecule::getAtomMaxH (int idx) { return getAtomTotalH(idx); } int Molecule::getAtomMinH (int idx) { return getAtomTotalH(idx); } int Molecule::getAtomTotalH (int idx) { if (_total_h.size() > idx && _total_h[idx] >= 0) return _total_h[idx]; int i, h = getImplicitH(idx); const Vertex &vertex = getVertex(idx); for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) if (getAtomNumber(vertex.neiVertex(i)) == ELEM_H) h++; _total_h.expandFill(idx + 1, -1); _total_h[idx] = h; return h; } bool Molecule::isRSite (int atom_idx) { return _atoms[atom_idx].number == ELEM_RSITE; } dword Molecule::getRSiteBits (int atom_idx) { if (_atoms[atom_idx].number != ELEM_RSITE) throw Error("getRSiteBits(): atom #%d is not an r-site", atom_idx); return _atoms[atom_idx].rgroup_bits; } void Molecule::setRSiteBits (int atom_idx, int bits) { if (_atoms[atom_idx].number != ELEM_RSITE) throw Error("setRSiteBits(): atom #%d is not an r-site", atom_idx); _atoms[atom_idx].rgroup_bits = bits; updateEditRevision(); } void Molecule::allowRGroupOnRSite (int atom_idx, int rg_idx) { if (_atoms[atom_idx].number != ELEM_RSITE) throw Error("allowRGroupOnRSite(): atom #%d is not an r-site", atom_idx); if (rg_idx < 1 || rg_idx > 32) throw Error("allowRGroupOnRSite(): rgroup number %d is invalid", rg_idx); rg_idx--; _atoms[atom_idx].rgroup_bits |= (1 << rg_idx); updateEditRevision(); } bool Molecule::convertableToImplicitHydrogen (int idx) { if (getAtomNumber(idx) == ELEM_H && getAtomIsotope(idx) == 0 && getVertex(idx).degree() == 1) { int nei = getVertex(idx).neiVertex(getVertex(idx).neiBegin()); if (getAtomNumber(nei) != ELEM_H || getAtomIsotope(nei) != 0) { if (stereocenters.getType(nei) > 0) if (getVertex(nei).degree() == 3) return false; // not ignoring hydrogens around stereocenters with lone pair if (!cis_trans.convertableToImplicitHydrogen(idx)) return false; return true; } } return false; } void Molecule::invalidateHCounters () { _implicit_h.clear(); _total_h.clear(); _connectivity.clear(); } void Molecule::checkForConsistency (Molecule &mol) { // Try to restore hydrogens in aromatic cycles first mol.restoreAromaticHydrogens(); int i; for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { const Vertex &vertex = mol.getVertex(i); if (mol.isPseudoAtom(i) || mol.isRSite(i) || mol.isTemplateAtom(i)) continue; // check that we are sure about valence // (if the radical is not set, it is calculated from the valence anyway) int val = mol.getAtomValence(i); // check that we are sure about implicit H counter and valence mol.getImplicitH(i); } } bool Molecule::isAromatized () { return _aromatized; } bool Molecule::shouldWriteHCount (Molecule &mol, int idx) { return shouldWriteHCountEx(mol, idx, 0); } // Moved this method here to supply both Smiles and CML savers bool Molecule::shouldWriteHCountEx (Molecule &mol, int idx, int h_to_ignore) { bool aromatic = (mol.getAtomAromaticity(idx) == ATOM_AROMATIC); int atom_number = mol.getAtomNumber(idx); int charge = mol.getAtomCharge(idx); // We should write the H count if it is less than the normal (lowest valence) // count, like in atoms with radicals. if (mol.getAtomRadical_NoThrow(idx, -1) > 0) return true; // Should we write the H count for an aromatic atom or not? // In a better world, we would have been checking that the hydrogens // 'make difference' by de-aromatizing the molecule and comparing // the hydrogen counts in the de-aromatized atoms with the atoms we // are writing now. // In the real world, de-aromatization is complicated and takes time, // so we write hydrogen counts on all aromatic atoms, except // uncharged C and O with no radicals, for which we can always tell // the number of hydrogens by the number of bonds. // // Also handle some degerate cases with invalid valences like C[c]1(C)ccccc1 // and store implicit hydrogens for unusual situations if (aromatic) { if (atom_number != ELEM_C && atom_number != ELEM_O) return true; if (charge != 0) return true; int n_arom, min_conn; mol.calcAromaticAtomConnectivity(idx, n_arom, min_conn); if (atom_number == ELEM_C) { // Ensure that there can be double bond connected // But do not save for O=c1ccocc1 if (min_conn > 3 && mol.getVertex(idx).degree() > 3) return true; // Unusual aromatic Carbon atom } if (atom_number == ELEM_O) { // Ensure that there can be double bond connected if (min_conn != 2) return true; // Unusual aromatic Oxigen atom } } // We also should write the H count if it exceeds the normal (lowest valence) // count, like in [PH5] int normal_val, normal_hyd; int impl_h = mol.getImplicitH_NoThrow(idx, -1); if (impl_h >= 0) impl_h += h_to_ignore; if (mol.isNitrogenV5(idx)) { normal_val = 4; normal_hyd = 0; } else { if (impl_h < 0) return false; // can not write an undefined H count int conn = mol.getAtomConnectivity_noImplH(idx) - h_to_ignore; if (conn < 0) return false; // this is an aromatic atom -- dealed with that before if (!Element::calcValence(atom_number, charge, 0, conn, normal_val, normal_hyd, false)) return true; } if (impl_h != normal_hyd) return true; return false; } void Molecule::invalidateAtom (int index, int mask) { BaseMolecule::invalidateAtom(index, mask); } bool Molecule::restoreAromaticHydrogens (bool unambiguous_only) { return MoleculeDearomatizer::restoreHydrogens(*this, unambiguous_only); } bool Molecule::standardize (const StandardizeOptions &options) { updateEditRevision(); return MoleculeStandardizer::standardize(*this, options); } bool Molecule::ionize (float ph, float ph_toll, const IonizeOptions &options) { updateEditRevision(); return MoleculeIonizer::ionize(*this, ph, ph_toll, options); } bool Molecule::isPossibleFischerProjection (const char *options) { if (!BaseMolecule::hasCoord(*this) || BaseMolecule::hasZCoord(*this)) return false; for (auto i : edges()) { if (getBondDirection(i) > 0) return false; } for (auto i : vertices()) { if ((getAtomNumber(i) == ELEM_C) && (getVertex(i).degree() == 4)) { const Vertex &v = getVertex(i); Vec3f ¢ral_atom = getAtomXyz(i); Vec3f nei_coords[4]; int nei_count = 0; for (auto j : v.neighbors()) { nei_coords[nei_count++] = getAtomXyz(v.neiVertex(j)); } float angle; Vec3f bond1, bond2; int ncount = 0; for (auto j = 0; j < 4; j++) { if (j == 3) { bond1.diff(nei_coords[3], central_atom); bond1.normalize(); bond2.diff(nei_coords[0], central_atom); bond2.normalize(); Vec3f::angle(bond1, bond2, angle); } else { bond1.diff(nei_coords[j], central_atom); bond1.normalize(); bond2.diff(nei_coords[j+1], central_atom); bond2.normalize(); Vec3f::angle(bond1, bond2, angle); } if ((fabs(angle - PI/2.f) < EPSILON) || (fabs(angle - PI) < EPSILON)) ncount++; } if (ncount == 4) { return true; } } } return false; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_3d_constraints.cpp����������������������������������������0000664�0000000�0000000�00000100061�12710376503�0024121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/red_black.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/query_molecule.h" using namespace indigo; QueryMolecule & Molecule3dConstraints::_getMolecule () { char dummy[sizeof(QueryMolecule)]; int offset = (int)((char *)(&((QueryMolecule *)dummy)->spatial_constraints) - dummy); return *(QueryMolecule *)((char *)this - offset); } IMPL_ERROR(Molecule3dConstraints, "molecule 3d constraints"); Molecule3dConstraints::Molecule3dConstraints () { } void Molecule3dConstraints::init () { QueryMolecule &mol = _getMolecule(); int i; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { AutoPtr<PointByAtom> constr; constr.create(); constr->atom_idx = i; _constraints.add(constr.release()); } } int Molecule3dConstraints::begin () const { return 0; } int Molecule3dConstraints::end () const { return _constraints.size(); } int Molecule3dConstraints::next (int idx) const { return idx + 1; } const Molecule3dConstraints::Base & Molecule3dConstraints::at (int idx) const { return *_constraints[idx]; } Molecule3dConstraints::Base & Molecule3dConstraints::add (Molecule3dConstraints::Base *constraint) { return _constraints.add(constraint); } bool Molecule3dConstraints::haveConstraints () { for (int i = 0; i < _constraints.size(); i++) { const Base & base = *_constraints.at(i); switch (base.type) { case ANGLE_2LINES: case ANGLE_DIHEDRAL: case ANGLE_2PLANES: case ANGLE_3POINTS: case DISTANCE_2POINTS: case DISTANCE_POINT_LINE: case DISTANCE_POINT_PLANE: case EXCLUSION_SPHERE: case LINE_BEST_FIT: case PLANE_BEST_FIT: return true; } } return false; } void Molecule3dConstraints::_buildSub (PtrArray<Base> &sub, const PtrArray<Base> &super, const int *mapping) { QS_DEF(Array<int>, cmapping); // mapping of constraints from supermolecule to submolecule int i, j; cmapping.resize(super.size()); for (i = 0; i < super.size(); i++) cmapping[i] = -1; sub.clear(); do { for (i = 0; i < super.size(); i++) { int oldsize = sub.size(); if (cmapping[i] >= 0) continue; const Base & base = *super.at(i); switch (base.type) { case POINT_ATOM: { int atom_idx = ((const Molecule3dConstraints::PointByAtom &)base).atom_idx; if (mapping[atom_idx] < 0) continue; AutoPtr<PointByAtom> newconstr; newconstr.create(); newconstr->atom_idx = mapping[atom_idx]; sub.add(newconstr.release()); break; } case POINT_DISTANCE: { const PointByDistance &constr = (const PointByDistance &)base; int beg_id = cmapping[constr.beg_id]; int end_id = cmapping[constr.end_id]; if (beg_id < 0 || end_id < 0) continue; AutoPtr<PointByDistance> newconstr; newconstr.create(); newconstr->beg_id = beg_id; newconstr->end_id = end_id; newconstr->distance = constr.distance; sub.add(newconstr.release()); break; } case POINT_PERCENTAGE: { const PointByPercentage &constr = (const PointByPercentage &)base; int beg_id = cmapping[constr.beg_id]; int end_id = cmapping[constr.end_id]; if (beg_id < 0 || end_id < 0) continue; AutoPtr<PointByPercentage> newconstr; newconstr.create(); newconstr->beg_id = beg_id; newconstr->end_id = end_id; newconstr->percentage = constr.percentage; sub.add(newconstr.release()); break; } case POINT_NORMALE: { const PointByNormale &constr = (const PointByNormale &)base; int org_id = cmapping[constr.org_id]; int norm_id = cmapping[constr.norm_id]; if (org_id < 0 || norm_id < 0) continue; AutoPtr<PointByNormale> newconstr; newconstr.create(); newconstr->norm_id = norm_id; newconstr->org_id = org_id; newconstr->distance = constr.distance; sub.add(newconstr.release()); break; } case POINT_CENTROID: { const Centroid &constr = (const Centroid &)base; AutoPtr<Centroid> newconstr; newconstr.create(); for (j = 0; j < constr.point_ids.size(); j++) { int pt_idx = cmapping[constr.point_ids[j]]; if (pt_idx < 0) break; newconstr->point_ids.push(pt_idx); } if (newconstr->point_ids.size() < constr.point_ids.size()) continue; sub.add(newconstr.release()); break; } case LINE_NORMALE: { const Normale &constr = (const Normale &)base; int plane_id = cmapping[constr.plane_id]; int point_id = cmapping[constr.point_id]; if (plane_id < 0 || point_id < 0) continue; AutoPtr<Normale> newconstr; newconstr.create(); newconstr->plane_id = plane_id; newconstr->point_id = point_id; sub.add(newconstr.release()); break; } case LINE_BEST_FIT: { const BestFitLine &constr = (const BestFitLine &)base; AutoPtr<BestFitLine> newconstr; newconstr.create(); for (j = 0; j < constr.point_ids.size(); j++) { int pt_idx = cmapping[constr.point_ids[j]]; if (pt_idx < 0) break; newconstr->point_ids.push(pt_idx); } if (newconstr->point_ids.size() < constr.point_ids.size()) continue; newconstr->max_deviation = constr.max_deviation; sub.add(newconstr.release()); break; } case PLANE_BEST_FIT: { const BestFitPlane &constr = (const BestFitPlane &)base; AutoPtr<BestFitPlane> newconstr; newconstr.create(); for (j = 0; j < constr.point_ids.size(); j++) { int pt_idx = cmapping[constr.point_ids[j]]; if (pt_idx < 0) break; newconstr->point_ids.push(pt_idx); } if (newconstr->point_ids.size() < constr.point_ids.size()) continue; newconstr->max_deviation = constr.max_deviation; sub.add(newconstr.release()); break; } case PLANE_POINT_LINE: { const PlaneByPoint &constr = (const PlaneByPoint &)base; int point_id = cmapping[constr.point_id]; int line_id = cmapping[constr.line_id]; if (line_id < 0 || point_id < 0) continue; AutoPtr<PlaneByPoint> newconstr; newconstr.create(); newconstr->line_id = line_id; newconstr->point_id = point_id; sub.add(newconstr.release()); break; } case ANGLE_3POINTS: { const AngleByPoints &constr = (const AngleByPoints &)base; int point1_id = cmapping[constr.point1_id]; int point2_id = cmapping[constr.point2_id]; int point3_id = cmapping[constr.point3_id]; if (point1_id < 0 || point2_id < 0 || point3_id < 0) continue; AutoPtr<AngleByPoints> newconstr; newconstr.create(); newconstr->point1_id = point1_id; newconstr->point2_id = point2_id; newconstr->point3_id = point3_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case ANGLE_2LINES: { const AngleByLines &constr = (const AngleByLines &)base; int line1_id = cmapping[constr.line1_id]; int line2_id = cmapping[constr.line2_id]; if (line1_id < 0 || line2_id < 0) continue; AutoPtr<AngleByLines> newconstr; newconstr.create(); newconstr->line1_id = line1_id; newconstr->line2_id = line2_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case ANGLE_2PLANES: { const AngleByPlanes &constr = (const AngleByPlanes &)base; int plane1_id = cmapping[constr.plane1_id]; int plane2_id = cmapping[constr.plane2_id]; if (plane1_id < 0 || plane2_id < 0) continue; AutoPtr<AngleByPlanes> newconstr; newconstr.create(); newconstr->plane1_id = plane1_id; newconstr->plane2_id = plane2_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case ANGLE_DIHEDRAL: { const AngleDihedral &constr = (const AngleDihedral &)base; int point1_id = cmapping[constr.point1_id]; int point2_id = cmapping[constr.point2_id]; int point3_id = cmapping[constr.point3_id]; int point4_id = cmapping[constr.point4_id]; if (point1_id < 0 || point2_id < 0 || point3_id < 0 || point4_id < 0) continue; AutoPtr<AngleDihedral> newconstr; newconstr.create(); newconstr->point1_id = point1_id; newconstr->point2_id = point2_id; newconstr->point3_id = point3_id; newconstr->point4_id = point4_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case DISTANCE_2POINTS: { const DistanceByPoints &constr = (const DistanceByPoints &)base; int beg_id = cmapping[constr.beg_id]; int end_id = cmapping[constr.end_id]; if (beg_id < 0 || end_id < 0) continue; AutoPtr<DistanceByPoints> newconstr; newconstr.create(); newconstr->beg_id = beg_id; newconstr->end_id = end_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case DISTANCE_POINT_LINE: { const DistanceByLine &constr = (const DistanceByLine &)base; int point_id = cmapping[constr.point_id]; int line_id = cmapping[constr.line_id]; if (line_id < 0 || point_id < 0) continue; AutoPtr<DistanceByLine> newconstr; newconstr.create(); newconstr->line_id = line_id; newconstr->point_id = point_id; newconstr->top = constr.top; newconstr->bottom = constr.bottom; sub.add(newconstr.release()); break; } case DISTANCE_POINT_PLANE: { const DistanceByPlane &constr = (const DistanceByPlane &)base; int plane_id = cmapping[constr.plane_id]; int point_id = cmapping[constr.point_id]; if (plane_id < 0 || point_id < 0) continue; AutoPtr<DistanceByPlane> newconstr; newconstr.create(); newconstr->plane_id = plane_id; newconstr->point_id = point_id; newconstr->bottom = constr.bottom; newconstr->top = constr.top; sub.add(newconstr.release()); break; } case EXCLUSION_SPHERE: { const ExclusionSphere &constr = (const ExclusionSphere &)base; int center_id = cmapping[constr.center_id]; if (center_id < 0) continue; AutoPtr<ExclusionSphere> newconstr; newconstr.create(); for (j = 0; j < constr.allowed_atoms.size(); j++) { int atom_idx = mapping[constr.allowed_atoms[j]]; if (atom_idx >= 0) newconstr->allowed_atoms.push(atom_idx); } newconstr->center_id = center_id; newconstr->allow_unconnected = constr.allow_unconnected; newconstr->radius = constr.radius; sub.add(newconstr.release()); break; } default: throw Error("build on submolecule: unknown feature %d", base.type); } cmapping[i] = oldsize; } if (i == super.size()) break; } while (1); } void Molecule3dConstraints::buildOnSubmolecule (const Molecule3dConstraints &super, const int *mapping) { _buildSub(_constraints, super._constraints, mapping); } void Molecule3dConstraints::removeAtoms (const int *mapping) { PtrArray<Base> new_constraints; int i; _buildSub(new_constraints, _constraints, mapping); _constraints.clear(); for (i = 0; i < new_constraints.size(); i++) { _constraints.add(new_constraints.at(i)); new_constraints.release(i); } } IMPL_ERROR(Molecule3dConstraintsChecker, "molecule 3d constraints checker"); CP_DEF(Molecule3dConstraintsChecker); Molecule3dConstraintsChecker::Molecule3dConstraintsChecker (const Molecule3dConstraints &constraints) : _constraints(constraints), CP_INIT, TL_CP_GET(_cache_v), TL_CP_GET(_cache_l), TL_CP_GET(_cache_p), TL_CP_GET(_cache_mark) { } bool Molecule3dConstraintsChecker::check (BaseMolecule &target, const int *mapping) { _cache_l.clear(); _cache_p.clear(); _cache_v.clear(); _cache_mark.clear(); _target = ⌖ _mapping = mapping; for (int i = _constraints.begin(); i != _constraints.end(); i = _constraints.next(i)) { const MC::Base & base = _constraints.at(i); switch (base.type) { case MC::ANGLE_DIHEDRAL: case MC::ANGLE_3POINTS: { float value = _getAngle(i); const MC::AngleBase &constr = (const MC::AngleBase &)base; if (value < constr.bottom || value > constr.top) return false; break; } case MC::ANGLE_2LINES: case MC::ANGLE_2PLANES: { float value = _getAngle(i); const MC::AngleBase &constr = (const MC::AngleBase &)base; if ((value < constr.bottom || value > constr.top) && (M_PI - value < constr.bottom || M_PI - value > constr.top)) return false; break; } case MC::DISTANCE_2POINTS: case MC::DISTANCE_POINT_LINE: case MC::DISTANCE_POINT_PLANE: { float value = _getDistance(i); const MC::DistanceBase &constr = (const MC::DistanceBase &)base; if (value < constr.bottom || value > constr.top) return false; break; } case MC::EXCLUSION_SPHERE: { const MC::ExclusionSphere &constr = (const MC::ExclusionSphere &)base; _cache(constr.center_id); const Vec3f ¢er = _cache_v.at(constr.center_id); QS_DEF(Array<int>, allowed); int i; allowed.clear_resize(_target->vertexCount()); allowed.zerofill(); for (i = 0; i < constr.allowed_atoms.size(); i++) allowed[_mapping[constr.allowed_atoms[i]]] = 1; for (i = _target->vertexBegin(); i < _target->vertexEnd(); i = _target->vertexNext(i)) { if (allowed[i]) continue; if (constr.allow_unconnected && _target->getVertex(i).degree() < 1) continue; const Vec3f &pos = _target->getAtomXyz(i); if (Vec3f::dist(pos, center) < constr.radius - EPSILON) return false; } break; } case MC::LINE_BEST_FIT: { _cache(i); const Line3f &bfl = _cache_l.at(i); const MC::BestFitLine &constr = (const MC::BestFitLine &)base; float rms = .0f, dist = .0f; for (int i = 0; i < constr.point_ids.size(); i++) { dist = bfl.distFromPoint(_cache_v.at(constr.point_ids[i])); rms += dist * dist; } if (rms > constr.max_deviation + 1e-6) return false; break; } case MC::PLANE_BEST_FIT: { _cache(i); const Plane3f &bfp = _cache_p.at(i); const MC::BestFitPlane &constr = (const MC::BestFitPlane &)base; float rms = .0f, dist = .0f; for (int i = 0; i < constr.point_ids.size(); i++) { dist = bfp.distFromPoint(_cache_v.at(constr.point_ids[i])); rms += dist * dist; } if (rms > constr.max_deviation + 1e-6) return false; break; } } } return true; } void Molecule3dConstraintsChecker::_cache (int idx) { if (_cache_v.find(idx) || _cache_l.find(idx) || _cache_p.find(idx)) return; const MC::Base &base = _constraints.at(idx); switch (base.type) { case MC::POINT_ATOM: { int atom_idx = ((const Molecule3dConstraints::PointByAtom &)base).atom_idx; _cache_v.insert(idx, _target->getAtomXyz(_mapping[atom_idx])); break; } case MC::POINT_DISTANCE: { const MC::PointByDistance &constr = (const MC::PointByDistance &)base; _cache(constr.beg_id); _cache(constr.end_id); const Vec3f &beg = _cache_v.at(constr.beg_id); const Vec3f &end = _cache_v.at(constr.end_id); Vec3f dir; dir.diff(end, beg); if (!dir.normalize()) throw Error("point-by-distance: degenerate case"); Vec3f res; res.lineCombin(beg, dir, constr.distance); _cache_v.insert(idx, res); break; } case MC::POINT_PERCENTAGE: { const MC::PointByPercentage &constr = (const MC::PointByPercentage &)base; _cache(constr.beg_id); _cache(constr.end_id); const Vec3f &beg = _cache_v.at(constr.beg_id); const Vec3f &end = _cache_v.at(constr.end_id); Vec3f dir; dir.diff(end, beg); if (!dir.normalize()) throw Error("point-by-percentage: degenerate case"); Vec3f res; res.lineCombin2(beg, 1.f - constr.percentage, end, constr.percentage); _cache_v.insert(idx, res); break; } case MC::POINT_NORMALE: { const MC::PointByNormale &constr = (const MC::PointByNormale &)base; _cache(constr.org_id); _cache(constr.norm_id); const Vec3f &org = _cache_v.at(constr.org_id); const Line3f &norm = _cache_l.at(constr.norm_id); Vec3f res; res.lineCombin(org, norm.dir, constr.distance); _cache_v.insert(idx, res); break; } case MC::POINT_CENTROID: { const MC::Centroid &constr = (const MC::Centroid &)base; Vec3f res; if (constr.point_ids.size() < 1) throw Error("centroid: have %d points", constr.point_ids.size()); for (int i = 0; i < constr.point_ids.size(); i++) { _cache(constr.point_ids[i]); const Vec3f &pt = _cache_v.at(constr.point_ids[i]); res.add(pt); } res.scale(1.f / constr.point_ids.size()); _cache_v.insert(idx, res); break; } case MC::LINE_NORMALE: { const MC::Normale &constr = (const MC::Normale &)base; _cache(constr.plane_id); _cache(constr.point_id); const Plane3f &plane = _cache_p.at(constr.plane_id); const Vec3f &point = _cache_v.at(constr.point_id); Vec3f projection; Line3f res; plane.projection(point, projection); res.dir.copy(plane.getNorm()); res.org.copy(projection); _cache_l.insert(idx, res); break; } case MC::LINE_BEST_FIT: { const MC::BestFitLine &constr = (const MC::BestFitLine &)base; if (constr.point_ids.size() < 2) throw Error("best fit line: only %d points", constr.point_ids.size()); QS_DEF(Array<Vec3f>, points); points.clear(); for (int i = 0; i < constr.point_ids.size(); i++) { _cache(constr.point_ids[i]); points.push(_cache_v.at(constr.point_ids[i])); } Line3f res; res.bestFit(points.size(), points.ptr(), 0); _cache_l.insert(idx, res); break; } case MC::PLANE_BEST_FIT: { const MC::BestFitPlane &constr = (const MC::BestFitPlane &)base; if (constr.point_ids.size() < 3) throw Error("best fit line: only %d points", constr.point_ids.size()); QS_DEF(Array<Vec3f>, points); points.clear(); for (int i = 0; i < constr.point_ids.size(); i++) { _cache(constr.point_ids[i]); points.push(_cache_v.at(constr.point_ids[i])); } Plane3f res; res.bestFit(points.size(), points.ptr(), 0); _cache_p.insert(idx, res); break; } case MC::PLANE_POINT_LINE: { const MC::PlaneByPoint &constr = (const MC::PlaneByPoint &)base; _cache(constr.point_id); _cache(constr.line_id); const Vec3f &point = _cache_v.at(constr.point_id); const Line3f &line = _cache_l.at(constr.line_id); Plane3f res; res.byPointAndLine(point, line); _cache_p.insert(idx, res); break; } default: throw Error("unknown constraint type %d", base.type); } } float Molecule3dConstraintsChecker::_getAngle (int idx) { const MC::Base &base = _constraints.at(idx); switch (base.type) { case MC::ANGLE_3POINTS: { const MC::AngleByPoints &constr = (const MC::AngleByPoints &)base; _cache(constr.point1_id); _cache(constr.point2_id); _cache(constr.point3_id); const Vec3f &v1 = _cache_v.at(constr.point1_id); const Vec3f &v2 = _cache_v.at(constr.point2_id); const Vec3f &v3 = _cache_v.at(constr.point3_id); Vec3f dir1, dir3; dir1.diff(v1, v2); dir3.diff(v3, v2); float ang; if (!Vec3f::angle(dir1, dir3, ang)) throw Error("angle by points: degerenate"); return ang; } case MC::ANGLE_2LINES: { const MC::AngleByLines &constr = (const MC::AngleByLines &)base; _cache(constr.line1_id); _cache(constr.line2_id); const Line3f &line1 = _cache_l.at(constr.line1_id); const Line3f &line2 = _cache_l.at(constr.line2_id); float ang; if (!Vec3f::angle(line1.dir, line2.dir, ang)) throw Error("angle by lines: degerenate"); return ang; } case MC::ANGLE_2PLANES: { const MC::AngleByPlanes &constr = (const MC::AngleByPlanes &)base; _cache(constr.plane1_id); _cache(constr.plane2_id); const Plane3f &plane1 = _cache_p.at(constr.plane1_id); const Plane3f &plane2 = _cache_p.at(constr.plane2_id); float ang; if (!Vec3f::angle(plane1.getNorm(), plane2.getNorm(), ang)) throw Error("angle by planes: degerenate"); return ang; } case MC::ANGLE_DIHEDRAL: { const MC::AngleDihedral &constr = (const MC::AngleDihedral &)base; _cache(constr.point1_id); _cache(constr.point2_id); _cache(constr.point3_id); _cache(constr.point4_id); const Vec3f &v1 = _cache_v.at(constr.point1_id); const Vec3f &v2 = _cache_v.at(constr.point2_id); const Vec3f &v3 = _cache_v.at(constr.point3_id); const Vec3f &v4 = _cache_v.at(constr.point4_id); Vec3f d1, d2, axis; d1.diff(v2, v1); d2.diff(v3, v4); axis.diff(v2, v3); if (!axis.normalize()) throw Error("dihedral angle: degenerate axis"); d1.addScaled(axis, -Vec3f::dot(d1, axis)); d2.addScaled(axis, -Vec3f::dot(d2, axis)); float ang; if (!Vec3f::angle(d1, d2, ang)) throw Error("dihedral angle: degenerate"); return ang; } default: throw Error("get angle: bad constraint type %d", base.type); } } float Molecule3dConstraintsChecker::_getDistance (int idx) { const MC::Base &base = _constraints.at(idx); switch (base.type) { case MC::DISTANCE_2POINTS: { const MC::DistanceByPoints &constr = (const MC::DistanceByPoints &)base; _cache(constr.beg_id); _cache(constr.end_id); const Vec3f &beg = _cache_v.at(constr.beg_id); const Vec3f &end = _cache_v.at(constr.end_id); return Vec3f::dist(beg, end); } case MC::DISTANCE_POINT_LINE: { const MC::DistanceByLine &constr = (const MC::DistanceByLine &)base; _cache(constr.line_id); _cache(constr.point_id); const Vec3f &point = _cache_v.at(constr.point_id); const Line3f &line = _cache_l.at(constr.line_id); return line.distFromPoint(point); } case MC::DISTANCE_POINT_PLANE: { const MC::DistanceByPlane &constr = (const MC::DistanceByPlane &)base; _cache(constr.plane_id); _cache(constr.point_id); const Vec3f &point = _cache_v.at(constr.point_id); const Plane3f &plane = _cache_p.at(constr.plane_id); return plane.distFromPoint(point); } default: throw Error("get distance: bad constraint type %d", base.type); } } void Molecule3dConstraintsChecker::markUsedAtoms (int *arr, int value) { int i; _to_mark = arr; _mark_value = value; for (i = _constraints.begin(); i != _constraints.end(); i = _constraints.next(i)) { const MC::Base & base = _constraints.at(i); switch (base.type) { case MC::ANGLE_2LINES: case MC::ANGLE_DIHEDRAL: case MC::ANGLE_2PLANES: case MC::ANGLE_3POINTS: case MC::DISTANCE_2POINTS: case MC::DISTANCE_POINT_LINE: case MC::DISTANCE_POINT_PLANE: case MC::EXCLUSION_SPHERE: { _mark(i); break; } } } } void Molecule3dConstraintsChecker::_mark (int idx) { if (_cache_mark.find(idx)) return; _cache_mark.insert(idx); const MC::Base &base = _constraints.at(idx); switch (base.type) { case MC::POINT_ATOM: { int atom_idx = ((const Molecule3dConstraints::PointByAtom &)base).atom_idx; _to_mark[atom_idx] = _mark_value; break; } case MC::POINT_DISTANCE: { const MC::PointByDistance &constr = (const MC::PointByDistance &)base; _mark(constr.beg_id); _mark(constr.end_id); break; } case MC::POINT_PERCENTAGE: { const MC::PointByPercentage &constr = (const MC::PointByPercentage &)base; _mark(constr.beg_id); _mark(constr.end_id); break; } case MC::POINT_NORMALE: { const MC::PointByNormale &constr = (const MC::PointByNormale &)base; _mark(constr.org_id); _mark(constr.norm_id); break; } case MC::POINT_CENTROID: { const MC::Centroid &constr = (const MC::Centroid &)base; for (int i = 0; i < constr.point_ids.size(); i++) _mark(constr.point_ids[i]); break; } case MC::LINE_NORMALE: { const MC::Normale &constr = (const MC::Normale &)base; _mark(constr.plane_id); _mark(constr.point_id); break; } case MC::LINE_BEST_FIT: { const MC::BestFitLine &constr = (const MC::BestFitLine &)base; for (int i = 0; i < constr.point_ids.size(); i++) _mark(constr.point_ids[i]); break; } case MC::PLANE_BEST_FIT: { const MC::BestFitPlane &constr = (const MC::BestFitPlane &)base; for (int i = 0; i < constr.point_ids.size(); i++) _mark(constr.point_ids[i]); break; } case MC::PLANE_POINT_LINE: { const MC::PlaneByPoint &constr = (const MC::PlaneByPoint &)base; _mark(constr.point_id); _mark(constr.line_id); break; } case MC::ANGLE_3POINTS: { const MC::AngleByPoints &constr = (const MC::AngleByPoints &)base; _mark(constr.point1_id); _mark(constr.point2_id); _mark(constr.point3_id); break; } case MC::ANGLE_2LINES: { const MC::AngleByLines &constr = (const MC::AngleByLines &)base; _mark(constr.line1_id); _mark(constr.line2_id); break; } case MC::ANGLE_2PLANES: { const MC::AngleByPlanes &constr = (const MC::AngleByPlanes &)base; _mark(constr.plane1_id); _mark(constr.plane2_id); break; } case MC::ANGLE_DIHEDRAL: { const MC::AngleDihedral &constr = (const MC::AngleDihedral &)base; _mark(constr.point1_id); _mark(constr.point2_id); _mark(constr.point3_id); _mark(constr.point4_id); break; } case MC::DISTANCE_2POINTS: { const MC::DistanceByPoints &constr = (const MC::DistanceByPoints &)base; _mark(constr.beg_id); _mark(constr.end_id); break; } case MC::DISTANCE_POINT_LINE: { const MC::DistanceByLine &constr = (const MC::DistanceByLine &)base; _mark(constr.line_id); _mark(constr.point_id); break; } case MC::DISTANCE_POINT_PLANE: { const MC::DistanceByPlane &constr = (const MC::DistanceByPlane &)base; _mark(constr.plane_id); _mark(constr.point_id); break; } case MC::EXCLUSION_SPHERE: { const MC::ExclusionSphere &constr = (const MC::ExclusionSphere &)base; _mark(constr.center_id); break; } } } void Molecule3dConstraints::clear () { _constraints.clear(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_allene_stereo.cpp�����������������������������������������0000664�0000000�0000000�00000054571�12710376503�0024023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/base_molecule.h" #include "molecule/molecule_allene_stereo.h" #include "molecule/elements.h" #include "molecule/cmf_loader.h" #include "molecule/molecule.h" using namespace indigo; IMPL_ERROR(MoleculeAlleneStereo, "allene stereo"); MoleculeAlleneStereo::MoleculeAlleneStereo () { } BaseMolecule & MoleculeAlleneStereo::_getMolecule () { char dummy[sizeof(BaseMolecule)]; int offset = (int)((char *)(&((BaseMolecule *)dummy)->allene_stereo) - dummy); return *(BaseMolecule *)((char *)this - offset); } int MoleculeAlleneStereo::sameside (const Vec3f &dir1, const Vec3f &dir2, const Vec3f &sep) { Vec3f norm, norm_cross; // Use double cross product for getting vector lying the same plane with dir1 and sep norm_cross.cross(sep, dir1); norm.cross(norm_cross, sep); if (!norm.normalize()) throw Error("internal: zero vector length"); float prod1 = Vec3f::dot(dir1, norm); float prod2 = Vec3f::dot(dir2, norm); if ((float)(fabs(prod1)) < 1e-3 || (float)(fabs(prod2)) < 1e-3) return 0; return (prod1 * prod2 > 0) ? 1 : -1; } bool MoleculeAlleneStereo::possibleCenter (BaseMolecule &mol, int idx, int &left, int &right, int subst[4], bool pure_h[4]) { const Vertex &vertex = mol.getVertex(idx); // Check that we have [C,Si]=[C,Si]=[C,Si] fragment with the middle "C" being the i-th atom. if (vertex.degree() != 2) return false; if (mol.getAtomNumber(idx) != ELEM_C && mol.getAtomNumber(idx) != ELEM_Si) return false; int j = vertex.neiBegin(); int left_edge = vertex.neiEdge(j); int right_edge = vertex.neiEdge(vertex.neiNext(j)); left = vertex.neiVertex(j); right = vertex.neiVertex(vertex.neiNext(j)); if (mol.getBondOrder(left_edge) != BOND_DOUBLE || mol.getBondOrder(right_edge) != BOND_DOUBLE) return false; if (mol.getAtomNumber(left) != ELEM_C && mol.getAtomNumber(left) != ELEM_Si) return false; if (mol.getAtomNumber(right) != ELEM_C && mol.getAtomNumber(right) != ELEM_Si) return false; // Also check that left and right "C" atoms have one or two single bonds const Vertex &v_left = mol.getVertex(left); const Vertex &v_right = mol.getVertex(right); if (v_left.degree() < 2 || v_left.degree() > 3) return false; if (v_right.degree() < 2 || v_right.degree() > 3) return false; int k; pure_h[0] = pure_h[1] = pure_h[2] = pure_h[3] = true; // explicit pure H or implicit H subst[0] = subst[1] = subst[2] = subst[3] = -1; for (k = 0, j = v_left.neiBegin(); j != v_left.neiEnd(); j = v_left.neiNext(j)) { if (v_left.neiVertex(j) == idx) continue; if (mol.getBondOrder(v_left.neiEdge(j)) != BOND_SINGLE) return false; subst[k] = v_left.neiVertex(j); if (mol.getAtomNumber(subst[k]) != ELEM_H || !mol.possibleAtomIsotope(subst[k], 0)) pure_h[k] = false; k++; } for (k = 2, j = v_right.neiBegin(); j != v_right.neiEnd(); j = v_right.neiNext(j)) { if (v_right.neiVertex(j) == idx) continue; if (mol.getBondOrder(v_right.neiEdge(j)) != BOND_SINGLE) return false; subst[k] = v_right.neiVertex(j); if (mol.getAtomNumber(subst[k]) != ELEM_H || !mol.possibleAtomIsotope(subst[k], 0)) pure_h[k] = false; k++; } // no non-H substituents => no symmetry if (pure_h[0] && pure_h[1]) return false; if (pure_h[2] && pure_h[3]) return false; return true; } bool MoleculeAlleneStereo::_isAlleneCenter (BaseMolecule &mol, int idx, _Atom &atom, int *sensible_bonds_out) { bool pure_h[4]; if (!possibleCenter(mol, idx, atom.left, atom.right, atom.subst, pure_h)) return false; int dirs[4] = {0, 0, 0, 0}; Vec3f subst_vecs[4]; int j, k; bool zero_bond_length = false; for (k = 0; k < 2; k++) if (atom.subst[k] >= 0) { dirs[k] = mol.getBondDirection2(atom.left, atom.subst[k]); subst_vecs[k].diff(mol.getAtomXyz(atom.subst[k]), mol.getAtomXyz(atom.left)); if (!subst_vecs[k].normalize()) zero_bond_length = true; } for (k = 2; k < 4; k++) if (atom.subst[k] >= 0) { dirs[k] = mol.getBondDirection2(atom.right, atom.subst[k]); subst_vecs[k].diff(mol.getAtomXyz(atom.subst[k]), mol.getAtomXyz(atom.right)); if (!subst_vecs[k].normalize()) zero_bond_length = true; } if (dirs[0] == 0 && dirs[1] == 0 && dirs[2] == 0 && dirs[3] == 0) return false; // no oriented bonds => no stereochemistry // check that they do not have the same orientation if (dirs[0] != 0 && dirs[0] == dirs[1] && dirs[0] != BOND_EITHER) return false; if (dirs[2] != 0 && dirs[2] == dirs[3] && dirs[2] != BOND_EITHER) return false; if (zero_bond_length) throw Error("zero bond length"); Vec3f pos_center = mol.getAtomXyz(idx); Vec3f vec_left = mol.getAtomXyz(atom.left); Vec3f vec_right = mol.getAtomXyz(atom.right); vec_left.sub(pos_center); vec_right.sub(pos_center); if (!vec_left.normalize() || !vec_right.normalize()) throw Error("zero bond length"); // they should go in one line // 0.04 is equivalent to 16 degress because it is hard to draw a straight line accurately if (fabs(Vec3f::dot(vec_left, vec_right) + 1) > 0.04) return false; // check that if there are two left substituents, they do not lie on the same side if (atom.subst[1] != -1 && sameside(subst_vecs[0], subst_vecs[1], vec_left) != -1) return false; // the same check for the two right substituents if (atom.subst[3] != -1 && sameside(subst_vecs[2], subst_vecs[3], vec_right) != -1) return false; if (dirs[0] == BOND_EITHER || dirs[1] == BOND_EITHER || dirs[2] == BOND_EITHER || dirs[3] == BOND_EITHER) atom.parity = 3; else { if (dirs[0] == 0 && dirs[1] != 0) dirs[0] = 3 - dirs[1]; if (dirs[2] == 0 && dirs[3] != 0) dirs[2] = 3 - dirs[3]; int ss = sameside(subst_vecs[0], subst_vecs[2], vec_right); if (ss == 0) return false; if (dirs[0] == 0) dirs[0] = (ss == 1) ? 3 - dirs[2] : dirs[2]; else if (dirs[2] == 0) dirs[2] = (ss == 1) ? 3 - dirs[0] : dirs[0]; if ((ss == 1 && dirs[0] == dirs[2]) || (ss == -1 && dirs[0] != dirs[2])) return false; // square-planar configuration? if ((ss == 1 && dirs[0] == BOND_UP) || (ss == -1 && dirs[0] == BOND_DOWN)) atom.parity = 1; else atom.parity = 2; } const Vertex &v_left = mol.getVertex(atom.left); const Vertex &v_right = mol.getVertex(atom.right); // mark bonds as sensible for (k = 0, j = v_left.neiBegin(); j != v_left.neiEnd(); j = v_left.neiNext(j)) { int dir = mol.getBondDirection2(atom.left, v_left.neiVertex(j)); if (dir != 0) sensible_bonds_out[v_left.neiEdge(j)] = 1; } for (k = 0, j = v_right.neiBegin(); j != v_right.neiEnd(); j = v_right.neiNext(j)) { int dir = mol.getBondDirection2(atom.right, v_right.neiVertex(j)); if (dir != 0) sensible_bonds_out[v_right.neiEdge(j)] = 1; } // "either" allene centers do not count if (atom.parity == 3) return false; Vec3f prod; prod.cross(vec_left, subst_vecs[0]); if (prod.z > 0) atom.parity = 3 - atom.parity; int tmp; // move hydrogens from [0] and [2] to [1] and [3] respectively if (pure_h[0]) { __swap(atom.subst[0], atom.subst[1], tmp); atom.parity = 3 - atom.parity; } if (pure_h[2]) { __swap(atom.subst[2], atom.subst[3], tmp); atom.parity = 3 - atom.parity; } return true; } void MoleculeAlleneStereo::buildFromBonds (bool ignore_errors, int *sensible_bonds_out) { BaseMolecule &mol = _getMolecule(); int i; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { _Atom atom; try { if (!_isAlleneCenter(mol, i, atom, sensible_bonds_out)) continue; } catch (Error &err) { if (!ignore_errors) throw err; } _centers.insert(i, atom); } } void MoleculeAlleneStereo::clear () { _centers.clear(); } bool MoleculeAlleneStereo::isCenter (int atom_idx) { return _centers.at2(atom_idx) != 0; } void MoleculeAlleneStereo::invert (int atom_idx) { _Atom &atom = _centers.at(atom_idx); atom.parity = 3 - atom.parity; } void MoleculeAlleneStereo::reset (int atom_idx) { _centers.remove(atom_idx); } int MoleculeAlleneStereo::size () { return _centers.size(); } bool MoleculeAlleneStereo::checkSub (BaseMolecule &query, BaseMolecule &target, const int *mapping) { int i; for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) { const _Atom *qa = query.allene_stereo._centers.at2(i); if (qa == 0) continue; const _Atom *ta = target.allene_stereo._centers.at2(mapping[i]); if (ta == 0) return false; int parity = qa->parity; int qs[4], ts[4]; int tmp; memcpy(qs, qa->subst, 4 * sizeof(int)); memcpy(ts, ta->subst, 4 * sizeof(int)); if (mapping[qs[0]] == ts[2] || mapping[qs[0]] == ts[3]) { __swap(qs[0], qs[2], tmp); __swap(qs[1], qs[3], tmp); } if (mapping[qs[0]] == ts[0]) ; else if (mapping[qs[0]] == ts[1]) parity = 3 - parity; else throw Error("checkSub() subst[0] not mapped"); if (mapping[qs[2]] == ts[2]) ; else if (mapping[qs[2]] == ts[3]) parity = 3 - parity; else throw Error("checkSub() subst[2] not mapped"); if (parity != ta->parity) return false; } return true; } void MoleculeAlleneStereo::buildOnSubmolecule (MoleculeAlleneStereo &super, int *mapping) { int i, j; BaseMolecule &mol = _getMolecule(); for (i = super._centers.begin(); i != super._centers.end(); i = super._centers.next(i)) { int super_idx = super._centers.key(i); const _Atom &super_center = super._centers.value(i); int sub_idx = mapping[super_idx]; if (sub_idx < 0) continue; _Atom new_center; new_center.left = mapping[super_center.left]; new_center.right = mapping[super_center.right]; new_center.parity = super_center.parity; if (new_center.left < 0 || new_center.right < 0) continue; for (j = 0; j < 4; j++) { if (super_center.subst[j] >= 0) new_center.subst[j] = mapping[super_center.subst[j]]; else new_center.subst[j] = -1; } if (new_center.subst[0] == -1 && new_center.subst[1] == -1) continue; if (new_center.subst[2] == -1 && new_center.subst[3] == -1) continue; int tmp; if (mol.getAtomNumber(new_center.subst[0]) == ELEM_H && mol.possibleAtomIsotope(new_center.subst[0], 0)) { __swap(new_center.subst[0], new_center.subst[1], tmp); new_center.parity = 3 - new_center.parity; } if (mol.getAtomNumber(new_center.subst[2]) == ELEM_H && mol.possibleAtomIsotope(new_center.subst[2], 0)) { __swap(new_center.subst[2], new_center.subst[3], tmp); new_center.parity = 3 - new_center.parity; } if (new_center.subst[0] == -1) { new_center.subst[0] = new_center.subst[1]; new_center.subst[1] = -1; new_center.parity = 3 - new_center.parity; } if (new_center.subst[2] == -1) { new_center.subst[2] = new_center.subst[3]; new_center.subst[3] = -1; new_center.parity = 3 - new_center.parity; } _centers.insert(sub_idx, new_center); const Vertex &super_left = super._getMolecule().getVertex(super_center.left); const Vertex &super_right = super._getMolecule().getVertex(super_center.right); for (j = super_left.neiBegin(); j != super_left.neiEnd(); j = super_left.neiNext(j)) { int super_edge = super_left.neiEdge(j); if (mapping[super_left.neiVertex(j)] == -1) continue; int dir = super._getMolecule().getBondDirection(super_edge); if (dir != 0) mol.setBondDirection(mol.findEdgeIndex(new_center.left, mapping[super_left.neiVertex(j)]), dir); } for (j = super_right.neiBegin(); j != super_right.neiEnd(); j = super_right.neiNext(j)) { int super_edge = super_right.neiEdge(j); if (mapping[super_right.neiVertex(j)] == -1) continue; int dir = super._getMolecule().getBondDirection(super_edge); if (dir != 0) mol.setBondDirection(mol.findEdgeIndex(new_center.right, mapping[super_right.neiVertex(j)]), dir); } } } int MoleculeAlleneStereo::begin () const { return _centers.begin(); } int MoleculeAlleneStereo::end () const { return _centers.end(); } int MoleculeAlleneStereo::next (int i) const { return _centers.next(i); } void MoleculeAlleneStereo::get (int i, int &atom_idx, int &left, int &right, int subst[4], int &parity) { _Atom &atom = _centers.value(i); atom_idx = _centers.key(i); left = atom.left; right = atom.right; parity = atom.parity; memcpy(subst, atom.subst, sizeof(int) * 4); } void MoleculeAlleneStereo::getByAtomIdx (int atom_idx, int &left, int &right, int subst[4], int &parity) { _Atom &atom = _centers.at(atom_idx); left = atom.left; right = atom.right; parity = atom.parity; memcpy(subst, atom.subst, sizeof(int) * 4); } void MoleculeAlleneStereo::add (int atom_idx, int left, int right, int subst[4], int parity) { _Atom atom; atom.left = left; atom.right = right; memcpy(atom.subst, subst, 4 * sizeof(int)); atom.parity = parity; _centers.insert(atom_idx, atom); } void MoleculeAlleneStereo::markBonds () { int i, j; BaseMolecule &mol = _getMolecule(); for (i = _centers.begin(); i != _centers.end(); i = _centers.next(i)) { int idx = _centers.key(i); _Atom &atom = _centers.value(i); Vec3f subst_vecs[4]; int k; for (k = 0; k < 2; k++) if (atom.subst[k] >= 0) { subst_vecs[k].diff(mol.getAtomXyz(atom.subst[k]), mol.getAtomXyz(atom.left)); if (!subst_vecs[k].normalize()) throw Error("zero bond length"); } for (k = 2; k < 4; k++) if (atom.subst[k] >= 0) { subst_vecs[k].diff(mol.getAtomXyz(atom.subst[k]), mol.getAtomXyz(atom.right)); if (!subst_vecs[k].normalize()) throw Error("zero bond length"); } Vec3f pos_center = mol.getAtomXyz(idx); Vec3f vec_left = mol.getAtomXyz(atom.left); Vec3f vec_right = mol.getAtomXyz(atom.right); vec_left.sub(pos_center); vec_right.sub(pos_center); if (!vec_left.normalize() || !vec_right.normalize()) throw Error("zero bond length"); // they should go in one line if (fabs(Vec3f::dot(vec_left, vec_right) + 1) > 0.001) continue; // throw Error("markBonds(): double bonds do not form a flat angle") if (atom.subst[1] != -1 && sameside(subst_vecs[0], subst_vecs[1], vec_left) != -1) continue; // throw Error("markBonds(): same-side substituents on the left") if (atom.subst[3] != -1 && sameside(subst_vecs[2], subst_vecs[3], vec_right) != -1) continue; // throw Error("markBonds(): same-side substituents on the right") int ss = sameside(subst_vecs[0], subst_vecs[2], vec_right); if (ss == 0) continue; // throw Error("markBonds(): same-side substituents") bool to_mark1[4] = {false, false, false, false}; int n_to_mark1 = 0; bool to_mark2[4] = {false, false, false, false}; int n_to_mark2 = 0; bool to_mark3[4] = {false, false, false, false}; int n_to_mark3 = 0; for (j = 0; j < 4; j++) { if (atom.subst[j] < 0) continue; int edge_idx = mol.findEdgeIndex(atom.subst[j], j < 2 ? atom.left : atom.right); if (edge_idx < 0) throw Error("markBonds(): internal: edge not found"); if (mol.getBondDirection(edge_idx) != 0) continue; if (mol.getVertex(atom.subst[j]).degree() == 1) { to_mark1[j] = 1; n_to_mark1++; } if (mol.getEdgeTopology(edge_idx) != TOPOLOGY_RING) { to_mark2[j] = 1; n_to_mark2++; } to_mark3[j] = 1; n_to_mark3++; } bool *to_mark; if (n_to_mark1 > 0) to_mark = to_mark1; else if (n_to_mark2 > 0) to_mark = to_mark2; else if (n_to_mark3 > 0) to_mark = to_mark3; else throw Error("no bond can be marked"); if (to_mark[0] && to_mark[1]) to_mark[2] = to_mark[3] = false; if (to_mark[2] && to_mark[3]) to_mark[0] = to_mark[1] = false; int dirs[4]; if (atom.parity == 2) { dirs[0] = BOND_DOWN; dirs[1] = BOND_UP; } else { dirs[0] = BOND_UP; dirs[1] = BOND_DOWN; } if (ss == -1) { dirs[0] = 3 - dirs[0]; dirs[1] = 3 - dirs[1]; } Vec3f prod; prod.cross(vec_left, subst_vecs[0]); if (prod.z > 0) { dirs[0] = 3 - dirs[0]; dirs[1] = 3 - dirs[1]; } if (ss == 1) { dirs[2] = 3 - dirs[0]; dirs[3] = 3 - dirs[1]; } else { dirs[2] = dirs[0]; dirs[3] = dirs[1]; } for (j = 0; j < 4; j++) { if (to_mark[j]) { int edge_idx = mol.findEdgeIndex(atom.subst[j], j < 2 ? atom.left : atom.right); if (mol.getEdge(edge_idx).beg != (j < 2 ? atom.left : atom.right)) mol.swapEdgeEnds(edge_idx); mol.setBondDirection(edge_idx, dirs[j]); } } } } void MoleculeAlleneStereo::removeAtoms (const Array<int> &indices) { BaseMolecule &mol = _getMolecule(); int i, j; QS_DEF(Array<int>, centers_to_remove); centers_to_remove.clear(); for (i = 0; i < indices.size(); i++) { int idx = indices[i]; if (_centers.find(idx)) { centers_to_remove.push(idx); continue; } // TODO: this can be done without looping through all centers for (j = _centers.begin(); j != _centers.end(); j = _centers.next(j)) { int center_idx = _centers.key(j); _Atom &atom = _centers.value(j); if (idx == atom.left || idx == atom.right) { centers_to_remove.push(center_idx); continue; } if (idx == atom.subst[1]) atom.subst[1] = -1; else if (idx == atom.subst[3]) atom.subst[3] = -1; else if (idx == atom.subst[0]) { if (atom.subst[1] == -1 || (mol.getAtomNumber(atom.subst[1]) == ELEM_H && mol.possibleAtomIsotope(atom.subst[1], 0))) { centers_to_remove.push(center_idx); continue; } atom.subst[0] = atom.subst[1]; atom.parity = 3 - atom.parity; } else if (idx == atom.subst[2]) { if (atom.subst[3] == -1 || (mol.getAtomNumber(atom.subst[3]) == ELEM_H && mol.possibleAtomIsotope(atom.subst[3], 0))) { centers_to_remove.push(center_idx); continue; } atom.subst[2] = atom.subst[3]; atom.parity = 3 - atom.parity; } } } for (int i = 0; i < centers_to_remove.size(); i++) { int idx = centers_to_remove[i]; if (_centers.find(idx)) _centers.remove(idx); } } void MoleculeAlleneStereo::removeBonds (const Array<int> &indices) { BaseMolecule &mol = _getMolecule(); int i, j; for (i = 0; i < indices.size(); i++) { int idx = indices[i]; // TODO: this can be done without looping through all centers for (j = _centers.begin(); j != _centers.end(); j = _centers.next(j)) { int center_idx = _centers.key(j); _Atom &atom = _centers.value(j); if (idx == mol.findEdgeIndex(center_idx, atom.left) || idx == mol.findEdgeIndex(center_idx, atom.right)) { _centers.remove(center_idx); continue; } if (idx == mol.findEdgeIndex(atom.left, atom.subst[1])) atom.subst[1] = -1; else if (idx == mol.findEdgeIndex(atom.right, atom.subst[3])) atom.subst[3] = -1; else if (idx == mol.findEdgeIndex(atom.left, atom.subst[0])) { if (atom.subst[1] == -1 || (mol.getAtomNumber(atom.subst[1]) == ELEM_H && mol.possibleAtomIsotope(atom.subst[1], 0))) { _centers.remove(center_idx); continue; } atom.subst[0] = atom.subst[1]; atom.parity = 3 - atom.parity; } else if (idx == mol.findEdgeIndex(atom.right, atom.subst[2])) { if (atom.subst[3] == -1 || (mol.getAtomNumber(atom.subst[3]) == ELEM_H && mol.possibleAtomIsotope(atom.subst[3], 0))) { _centers.remove(center_idx); continue; } atom.subst[2] = atom.subst[3]; atom.parity = 3 - atom.parity; } } } } void MoleculeAlleneStereo::registerUnfoldedHydrogen (int atom_idx, int added_hydrogen) { int j; // TODO: this can be done without looping through all centers for (j = _centers.begin(); j != _centers.end(); j = _centers.next(j)) { _Atom &atom = _centers.value(j); if (atom_idx == atom.left && atom.subst[1] == -1) atom.subst[1] = added_hydrogen; else if (atom_idx == atom.right && atom.subst[3] == -1) atom.subst[3] = added_hydrogen; } } ���������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_arom.cpp��������������������������������������������������0000664�0000000�0000000�00000072625�12710376503�0022140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_arom.h" #include "base_c/bitarray.h" #include "base_cpp/gray_codes.h" #include "graph/cycle_enumerator.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" using namespace indigo; // // AromatizerBase // IMPL_ERROR(AromatizerBase, "aromatizer"); CP_DEF(AromatizerBase); AromatizerBase::AromatizerBase (BaseMolecule &molecule) : _basemol(molecule), CP_INIT, TL_CP_GET(_bonds_arom), TL_CP_GET(_bonds_arom_count), TL_CP_GET(_unsure_cycles), TL_CP_GET(_cycle_atoms) { _bonds_arom.resize(bitGetSize(molecule.edgeEnd())); _bonds_arom_count.resize(molecule.edgeEnd()); _cycle_atoms.clear_resize(_basemol.vertexEnd()); reset(); } AromatizerBase::~AromatizerBase () { } bool AromatizerBase::_checkDoubleBonds (const int *cycle, int cycle_len) { int j; for (j = 0; j < cycle_len; j++) { int v_left_idx = cycle[j]; int v_center_idx = cycle[(j + 1) % cycle_len]; int v_right_idx = cycle[(j + 2) % cycle_len]; const Vertex &vertex = _basemol.getVertex(v_center_idx); int i; int internal_double_bond_count = 0; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int nei_idx = vertex.neiVertex(i); int e_idx = vertex.neiEdge(i); int type = _basemol.getBondOrder(e_idx); if (type == BOND_DOUBLE && !isBondAromatic(e_idx)) { if (nei_idx != v_left_idx && nei_idx != v_right_idx) { // Double bond going outside if (!_acceptOutgoingDoubleBond(v_center_idx, e_idx)) return false; } else if (nei_idx == v_left_idx || nei_idx == v_right_idx) internal_double_bond_count++; } } if (internal_double_bond_count >= 2) return false; } return true; } void AromatizerBase::_aromatizeCycle (const int *cycle, int cycle_len) { for (int i = 0; i < cycle_len; i++) { int a = cycle[i], b = cycle[(i + 1) % cycle_len]; int e_idx = _basemol.findEdgeIndex(a, b); _bonds_arom_count[e_idx]++; bitSetBit(_bonds_arom.ptr(), e_idx, 1); } // Marks all single bonds that are inside this cycle like in CC1=CC2=CNC=CC2=N1 as aromatic _cycle_atoms_mark++; for (int i = 0; i < cycle_len; i++) _cycle_atoms[cycle[i]] = _cycle_atoms_mark; for (int i = 0; i < cycle_len; i++) { const Vertex &v = _basemol.getVertex(cycle[i]); for (int nei : v.neighbors()) { int nei_index = v.neiVertex(nei); // Check that the end is in the cycle if (_cycle_atoms[nei_index] != _cycle_atoms_mark) continue; // Check if the bond has already been marked as aromatic int nei_edge = v.neiEdge(nei); if (_bonds_arom_count[nei_edge] != 0) continue; // Check that the bond is single if (_basemol.getBondOrder(nei_edge) == BOND_SINGLE) { bitSetBit(_bonds_arom.ptr(), nei_edge, 1); _bonds_arom_count[nei_edge]++; } } } _handleAromaticCycle(cycle, cycle_len); } void AromatizerBase::_handleCycle (const Array<int> &path) { // Check Huckel's rule if (!_isCycleAromatic(path.ptr(), path.size())) return; addAromaticCycle(-1, path.ptr(), path.size()); } bool AromatizerBase::handleUnsureCycles () { bool changed = true; bool is_all_aromatic = true; while (changed) { changed = false; for (int i = 0; i < _unsure_cycles.size(); i++) { if (_unsure_cycles[i].is_empty) continue; if (_checkDoubleBonds(_unsure_cycles[i].cycle, _unsure_cycles[i].length)) { _aromatizeCycle(_unsure_cycles[i].cycle, _unsure_cycles[i].length); _unsure_cycles[i].is_empty = true; changed = true; } else is_all_aromatic = false; } } return is_all_aromatic; } bool AromatizerBase::_cb_check_vertex (Graph &graph, int v_idx, void *context) { AromatizerBase *arom = (AromatizerBase *)context; return arom->_checkVertex(v_idx); } bool AromatizerBase::_cb_handle_cycle (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { AromatizerBase *arom = (AromatizerBase *)context; arom->_handleCycle(vertices); return true; } void AromatizerBase::aromatize () { CycleEnumerator cycle_enumerator(_basemol); cycle_enumerator.cb_check_vertex = _cb_check_vertex; cycle_enumerator.cb_handle_cycle = _cb_handle_cycle; cycle_enumerator.max_length = MAX_CYCLE_LEN; cycle_enumerator.context = this; cycle_enumerator.process(); handleUnsureCycles(); } bool AromatizerBase::isBondAromatic (int e_idx) { return _bonds_arom_count[e_idx] != 0; } const byte* AromatizerBase::isBondAromaticArray (void) { return _bonds_arom.ptr(); } void AromatizerBase::addAromaticCycle (int id, const int *cycle, int cycle_len) { if (!_checkDoubleBonds(cycle, cycle_len)) { int empty_idx; if (_unsureCyclesCount == _unsure_cycles.size()) { empty_idx = _unsure_cycles.size(); _unsure_cycles.push(); } else { // Find first empty space empty_idx = -1; for (int i = 0; i < _unsure_cycles.size(); i++) if (_unsure_cycles[i].is_empty) { empty_idx = i; break; } if (empty_idx == -1) throw Exception("AromatizerBase::addAromaticCycle: internal logic error"); } CycleDef &cycleDef = _unsure_cycles[empty_idx]; cycleDef.id = id; cycleDef.is_empty = false; cycleDef.length = cycle_len; memcpy(cycleDef.cycle, cycle, cycle_len * sizeof(int)); _unsureCyclesCount++; } else _aromatizeCycle(cycle, cycle_len); } void AromatizerBase::removeAromaticCycle (int id, const int *cycle, int cycle_len) { // Find id in unsure cycles for (int i = 0; i < _unsure_cycles.size(); i++) if (!_unsure_cycles[i].is_empty && _unsure_cycles[i].id == id) { _unsure_cycles[i].is_empty = true; return; } // Just decrease bond marks for (int i = 0; i < cycle_len; i++) { int a = cycle[i], b = cycle[(i + 1) % cycle_len]; int e_idx = _basemol.findEdgeIndex(a, b); _bonds_arom_count[e_idx]--; if (_bonds_arom_count[e_idx] == 0) bitSetBit(_bonds_arom.ptr(), e_idx, 0); } } bool AromatizerBase::_checkVertex (int v_idx) { return true; } void AromatizerBase::_handleAromaticCycle (const int *cycle, int cycle_len) { } void AromatizerBase::reset (void) { _unsure_cycles.clear(); _bonds_arom.zerofill(); _bonds_arom_count.zerofill(); _cycle_atoms.zerofill(); _cycle_atoms_mark = 1; _cyclesHandled = 0; _unsureCyclesCount = 0; } void AromatizerBase::setBondAromaticCount (int e_idx, int count) { _bonds_arom_count[e_idx] = count; bitSetBit(_bonds_arom.ptr(), e_idx, count != 0); } // // MoleculeAromatizer // CP_DEF(MoleculeAromatizer); MoleculeAromatizer::MoleculeAromatizer (Molecule &molecule, const AromaticityOptions &options) : AromatizerBase(molecule), CP_INIT, TL_CP_GET(_pi_labels) { _pi_labels.clear_resize(molecule.vertexEnd()); _options = options; } int MoleculeAromatizer::_getPiLabel (int v_idx) { if (!_basemol.vertexInRing(v_idx)) return -1; const Vertex &vertex = _basemol.getVertex(v_idx); if (_basemol.isPseudoAtom(v_idx)) return -1; if (_basemol.isTemplateAtom(v_idx)) return -1; if (_basemol.isRSite(v_idx)) return -1; if (!Element::canBeAromatic(_basemol.getAtomNumber(v_idx))) return -1; Molecule &mol = (Molecule &)_basemol; int non_arom_conn = 0, arom_bonds = 0; int n_double_ext = 0, n_double_ring = 0; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int bond_idx = vertex.neiEdge(i); int type = _basemol.getBondOrder(bond_idx); if (type == BOND_DOUBLE) { if (_basemol.getBondTopology(bond_idx) == TOPOLOGY_RING) n_double_ring++; else { if (!_acceptOutgoingDoubleBond(v_idx, bond_idx)) return -1; n_double_ext++; } } if (type == BOND_TRIPLE) return -1; if (type == BOND_AROMATIC) arom_bonds++; else non_arom_conn++; } if (arom_bonds == 0) { // Verify that this atom has valid valence if (mol.getImplicitH_NoThrow(v_idx, -1) == -1) return -1; } if (n_double_ring > 0) return 1; if (n_double_ext > 1) return -1; else if (n_double_ext == 1) { // Only a single external double bond that was accepted in _acceptOutgoingDoubleBond // It means that it is C=S, C=O, or C=N, like in O=C1NC=CC(=O)N1 int atom_number = _basemol.getAtomNumber(v_idx); if (atom_number == ELEM_S) return 2; return 0; } int conn = mol.getAtomConnectivity_NoThrow(v_idx, -1); if (conn == -1) return -1; // Atom is already aromatic and in general number of hydrogens // cannot be deduced. But if atom can have one single or onle // double bond while being aromatic then pi label can be calculated if (arom_bonds != 0) { int single_bonds_conn = non_arom_conn + arom_bonds; int h_with_single = mol.calcImplicitHForConnectivity(v_idx, single_bonds_conn); bool can_have_single_bonds = false; if (h_with_single >= 0 && _getPiLabelByConn(v_idx, single_bonds_conn) >= 0) { can_have_single_bonds = true; conn = single_bonds_conn; } bool can_have_one_double_bond = (mol.calcImplicitHForConnectivity(v_idx, single_bonds_conn + 1) >= 0); bool can_have_more_double_bonds = false; for (int i = 2; i < arom_bonds; i++) { int h_with_double = mol.calcImplicitHForConnectivity(v_idx, single_bonds_conn + i); if (h_with_double >= 0) can_have_more_double_bonds = true; } if (!can_have_single_bonds && can_have_one_double_bond && !can_have_more_double_bonds) return 1; // This atom must have double bond if (can_have_single_bonds && !can_have_one_double_bond && !can_have_more_double_bonds) ; // This atom must have only single bonds as aromatic ones else return -1; // This case is ambiguous. Treat as nonaromatic. } return _getPiLabelByConn(v_idx, conn); } int MoleculeAromatizer::_getPiLabelByConn (int v_idx, int conn) { Molecule &mol = (Molecule &)_basemol; int radical = _basemol.getAtomRadical(v_idx); if (radical > 0) return 1; int lonepairs = 0; if (mol.getVacantPiOrbitals(v_idx, conn, &lonepairs) > 0) return 0; if (lonepairs > 0) return 2; return -1; } void MoleculeAromatizer::precalculatePiLabels () { for (int v_idx = _basemol.vertexBegin(); v_idx < _basemol.vertexEnd(); v_idx = _basemol.vertexNext(v_idx)) _pi_labels[v_idx] = _getPiLabel(v_idx); } bool MoleculeAromatizer::_checkVertex (int v_idx) { return _pi_labels[v_idx] != -1; } bool MoleculeAromatizer::_isCycleAromatic (const int *cycle, int cycle_len) { int count = 0; // Check Huckel's rule for (int i = 0; i < cycle_len; i++) count += _pi_labels[cycle[i]]; if (((count - 2) % 4) != 0) return false; return true; } bool MoleculeAromatizer::_acceptOutgoingDoubleBond (int atom, int bond) { if (_options.method == AromaticityOptions::GENERIC) { // Note: this method should be in sync with QueryMoleculeAromatizer::_acceptOutgoingDoubleBond // CC1=CC=CC=[N]1=C int atom_number = _basemol.getAtomNumber(atom); if (atom_number == ELEM_C || atom_number == ELEM_S) { int end = _basemol.getEdgeEnd(atom, bond); int end_number = _basemol.getAtomNumber(end); if (atom_number == ELEM_C) { // [O-][N+](=O)C1=CNC=C(Cl)C1=O (see CID 11850826) // CN1SC(=N)N(C)C1=S (see CID 11949795) if (end_number == ELEM_N || end_number == ELEM_O || end_number == ELEM_S) // Corresponding pi label is 0 return true; } if (atom_number == ELEM_S) { // O=S1N=CC=N1 if (end_number == ELEM_O) // Corresponding pi label is 0 return true; } } } Molecule &mol = _basemol.asMolecule(); if (mol.isNitrogenV5(atom)) return true; return false; } bool MoleculeAromatizer::aromatizeBonds (Molecule &mol, const AromaticityOptions &options) { MoleculeAromatizer aromatizer(mol, options); aromatizer.precalculatePiLabels(); aromatizer.aromatize(); bool aromatic_bond_found = false; for (int e_idx = mol.edgeBegin(); e_idx < mol.edgeEnd(); e_idx = mol.edgeNext(e_idx)) if (aromatizer.isBondAromatic(e_idx)) { mol.setBondOrder(e_idx, BOND_AROMATIC, true); aromatic_bond_found = true; } // Aromatize RGroups int n_rgroups = mol.rgroups.getRGroupCount(); for (int i = 1; i <= n_rgroups; i++) { PtrPool<BaseMolecule> &frags = mol.rgroups.getRGroup(i).fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { Molecule &fragment = frags[j]->asMolecule(); aromatic_bond_found |= MoleculeAromatizer::aromatizeBonds(fragment, options); } } return aromatic_bond_found; } // // QueryMoleculeAromatizer // CP_DEF(QueryMoleculeAromatizer); QueryMoleculeAromatizer::QueryMoleculeAromatizer (QueryMolecule &molecule, const AromaticityOptions &options) : AromatizerBase(molecule), CP_INIT, TL_CP_GET(_pi_labels), TL_CP_GET(_aromatic_cycles) { _pi_labels.clear_resize(molecule.vertexEnd()); _aromatic_cycles.clear(); _aromatic_cycles.reserve(100); _mode = FUZZY; _collecting = false; _options = options; } void QueryMoleculeAromatizer::precalculatePiLabels () { for (int v_idx = _basemol.vertexBegin(); v_idx < _basemol.vertexEnd(); v_idx = _basemol.vertexNext(v_idx)) _pi_labels[v_idx] = _getPiLabel(v_idx); } bool QueryMoleculeAromatizer::_checkVertex (int v_idx) { return _pi_labels[v_idx].canBeAromatic(); } bool QueryMoleculeAromatizer::_isCycleAromatic (const int *cycle, int cycle_len) { QueryMolecule &query = (QueryMolecule &)_basemol; // Single/double bond can't be aromatic and Check if cycle wasn't aromatic bool all_aromatic = true; for (int i = 0; i < cycle_len; i++) { int a = cycle[i], b = cycle[(i + 1) % cycle_len]; int e_idx = _basemol.findEdgeIndex(a, b); if (!query.possibleBondOrder(e_idx, BOND_AROMATIC)) all_aromatic = false; } if (all_aromatic) return false; PiValue cycle_sum(0, 0); // Check Huckel's rule for (int i = 0; i < cycle_len; i++) { PiValue &cur = _pi_labels[cycle[i]]; if (cur.min == -1 || cur.max == -1) throw Error("interal error in _isCycleAromatic"); cycle_sum.max += cur.max; cycle_sum.min += cur.min; } // Check Huckel's rule if (_mode == EXACT) { if (cycle_sum.min != cycle_sum.max) return false; int sum = cycle_sum.min; // Check if cycle have pi-lables sum 4n+2 for drawn query if (sum % 4 != 2) return false; return true; } // // Fuzzy mode: check if circle can have 4n-2 value // if (cycle_sum.max - cycle_sum.min > 3) return true; int residue_min = (cycle_sum.min + 2) % 4; int residue_max = (cycle_sum.max + 2) % 4; if (residue_min == 0 || residue_min > residue_max) return true; return false; } QueryMoleculeAromatizer::PiValue QueryMoleculeAromatizer::_getPiLabel (int v_idx) { int exact_double_bonds = 0; bool has_query_bond = false; QueryMolecule &query = (QueryMolecule &)_basemol; // Check double bonds const Vertex &vertex = _basemol.getVertex(v_idx); int min_conn = vertex.degree(); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int bond_type = query.getBondOrder(vertex.neiEdge(i)); switch (bond_type) { case BOND_DOUBLE: exact_double_bonds++; min_conn++; break; case BOND_TRIPLE: // Triple bonds can't be attached to atoms in aromatic rings return PiValue(-1, -1); case -1: case BOND_AROMATIC: has_query_bond = true; break; } } if (query.isRSite(v_idx)) { if (vertex.degree() == 1) { // R-Group with single attachment point cannot be in aromatic ring return PiValue(-1, -1); } else { // R-Group with two attachment points // Here can be chain of atoms with different summary pi-labels return PiValue(0, 4); } } if (exact_double_bonds > 1) { if (_options.method == AromaticityOptions::BASIC) { if (!query.possibleNitrogenV5(v_idx)) return PiValue(-1, -1); } else { bool possible_c = _basemol.possibleAtomNumber(v_idx, ELEM_C); bool possible_s = _basemol.possibleAtomNumber(v_idx, ELEM_S); if (possible_s && possible_c) return PiValue(0, 2); else if (possible_s) return PiValue(2, 2); else return PiValue(0, 0); } } if (has_query_bond) { if (_mode == EXACT) return PiValue(-1, -1); else { if (_options.method == AromaticityOptions::BASIC) if (exact_double_bonds > 0) return PiValue(1, 1); return PiValue(0, 2); // TODO: check different cases } } // For aromaticity treat atoms without constrains as having default constraint. // For example if charge not specified then treat as charge is zero int number = query.getAtomNumber(v_idx); QueryMolecule::Atom &atom = query.getAtom(v_idx); //if (atom.hasConstraint(QueryMolecule::ATOM_FRAGMENT)) // throw Error("not implemented yet"); if (number == -1) return PiValue(0, 2); // TODO: check different cases if (!Element::canBeAromatic(number)) return PiValue(-1, -1); int radical = query.getAtomRadical(v_idx); if (radical == -1) { if (atom.hasConstraint(QueryMolecule::ATOM_RADICAL)) return PiValue(0, 2); // TODO: check different cases radical = 0; } int charge = query.getAtomCharge(v_idx); if (charge == CHARGE_UNKNOWN) { if (atom.hasConstraint(QueryMolecule::ATOM_CHARGE)) return PiValue(0, 2); // TODO: check different cases charge = 0; } if (radical > 0) return PiValue(1, 1); int valence, implicit_h; if (!Element::calcValence(number, charge, radical, min_conn, valence, implicit_h, false) && !query.possibleNitrogenV5(v_idx)) return PiValue(-1, -1); if (_basemol.possibleAtomNumber(v_idx, ELEM_C) && query.getExplicitValence(v_idx) == 5) return PiValue(-1, -1); if (exact_double_bonds >= 1) { if (_options.method == AromaticityOptions::BASIC) { if (exact_double_bonds > 0) return PiValue(1, 1); } else { for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int edge = vertex.neiEdge(i); if (query.possibleBondOrder(edge, BOND_DOUBLE)) { if (_acceptOutgoingDoubleBond(v_idx, edge)) { bool possible_c = _basemol.possibleAtomNumber(v_idx, ELEM_C); bool possible_s = _basemol.possibleAtomNumber(v_idx, ELEM_S); if (possible_s && possible_c) return PiValue(1, 2); else if (possible_s) return PiValue(1, 2); else return PiValue(0, 1); } } } } return PiValue(1, 1); } int pi_label = -1; int lonepairs = 0; int group = Element::group(number); if (BaseMolecule::getVacantPiOrbitals(group, charge, radical, min_conn + implicit_h, &lonepairs) > 0) pi_label = 0; else if (lonepairs > 0) pi_label = 2; return PiValue(pi_label, pi_label); } void QueryMoleculeAromatizer::_handleAromaticCycle (const int *cycle, int cycle_len) { if (!_collecting) return; // Add cycle to storage _aromatic_cycles.push(); CycleDef &def = _aromatic_cycles[_aromatic_cycles.size() - 1]; def.id = _aromatic_cycles.size() - 1; def.is_empty = false; def.length = cycle_len; memcpy(def.cycle, cycle, cycle_len * sizeof(int)); AromatizerBase::_handleAromaticCycle(cycle, cycle_len); } bool QueryMoleculeAromatizer::_acceptOutgoingDoubleBond (int atom, int bond) { if (_mode == EXACT) return false; if (_options.method == AromaticityOptions::GENERIC) { // Note: this method should be in sync with MoleculeAromatizer::_acceptOutgoingDoubleBond // CC1=CC=CC=[N]1=C bool possible_c = _basemol.possibleAtomNumber(atom, ELEM_C); bool possible_s = _basemol.possibleAtomNumber(atom, ELEM_S); if (possible_c || possible_s) { int end = _basemol.getEdgeEnd(atom, bond); int end_number = _basemol.getAtomNumber(end); if (possible_c) { // [O-][N+](=O)C1=CNC=C(Cl)C1=O (see CID 11850826) // CN1SC(=N)N(C)C1=S (see CID 11949795) if (_basemol.possibleAtomNumber(end, ELEM_N) || _basemol.possibleAtomNumber(end, ELEM_O) || _basemol.possibleAtomNumber(end, ELEM_S)) // Corresponding pi label is 0 return true; } if (possible_s) { // O=S1N=CC=N1 if (_basemol.possibleAtomNumber(end, ELEM_O)) // Corresponding pi label is 0 return true; } } } QueryMolecule &qmol = _basemol.asQueryMolecule(); if (qmol.possibleNitrogenV5(atom)) return true; return false; } void QueryMoleculeAromatizer::setMode (int mode) { _mode = mode; } bool QueryMoleculeAromatizer::aromatizeBonds (QueryMolecule &mol, const AromaticityOptions &options) { return _aromatizeBonds(mol, -1, options); } bool QueryMoleculeAromatizer::_aromatizeBonds (QueryMolecule &mol, int additional_atom, const AromaticityOptions &options) { bool aromatized = false; // Mark edges that can be aromatic in some matching aromatized |= _aromatizeBondsFuzzy(mol, options); // Aromatize all aromatic cycles aromatized |= _aromatizeBondsExact(mol, options); MoleculeRGroups &rgroups = mol.rgroups; int n_rgroups = rgroups.getRGroupCount(); // Check if r-groups are attached with single bonds QS_DEF(Array<bool>, rgroups_attached_single); rgroups_attached_single.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (v == additional_atom) continue; if (mol.isRSite(v)) { // Check if neighbor bonds are single const Vertex &vertex = mol.getVertex(v); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int edge = vertex.neiEdge(nei); QueryMolecule::Bond &bond = mol.getBond(edge); // DP TODO: implement smth. like Node::possibleOtherValueExcept() ... bool can_be_double = bond.possibleValue(QueryMolecule::BOND_ORDER, BOND_DOUBLE); bool can_be_triple = bond.possibleValue(QueryMolecule::BOND_ORDER, BOND_TRIPLE); bool can_be_arom = bond.possibleValue(QueryMolecule::BOND_ORDER, BOND_AROMATIC); if (can_be_double || can_be_triple || can_be_arom) { QS_DEF(Array<int>, sites); mol.getAllowedRGroups(v, sites); for (int j = 0; j < sites.size(); j++) { rgroups_attached_single.expandFill(sites[j] + 1, true); rgroups_attached_single[sites[j]] = false; } } } } } rgroups_attached_single.expandFill(n_rgroups + 1, true); for (int i = 1; i <= n_rgroups; i++) { PtrPool<BaseMolecule> &frags = rgroups.getRGroup(i).fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { QueryMolecule &fragment = frags[j]->asQueryMolecule(); aromatized |= _aromatizeRGroupFragment(fragment, rgroups_attached_single[i], options); } } return aromatized; } bool QueryMoleculeAromatizer::_aromatizeRGroupFragment (QueryMolecule &fragment, bool add_single_bonds, const AromaticityOptions &options) { // Add additional atom to attachment points int additional_atom = fragment.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 1)); // Connect it with attachment points int maxOrder = fragment.attachmentPointCount(); for (int i = 1; i <= maxOrder; i++) { int pointIndex = 0; int point; while (true) { point = fragment.getAttachmentPoint(i, pointIndex); if (point == -1) break; if (fragment.findEdgeIndex(point, additional_atom) == -1) { AutoPtr<QueryMolecule::Bond> bond; if (add_single_bonds) bond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); else bond.reset(new QueryMolecule::Bond()); fragment.addBond(point, additional_atom, bond.release()); } pointIndex++; } } bool aromatized = _aromatizeBonds(fragment, additional_atom, options); QS_DEF(Array<int>, indices); indices.clear(); indices.push(additional_atom); fragment.removeAtoms(indices); return aromatized; } // Some cycles with query features can be aromatized bool QueryMoleculeAromatizer::_aromatizeBondsExact (QueryMolecule &qmol, const AromaticityOptions &options) { bool aromatized = false; QueryMoleculeAromatizer aromatizer(qmol, options); aromatizer.setMode(QueryMoleculeAromatizer::EXACT); aromatizer.precalculatePiLabels(); aromatizer.aromatize(); for (int e_idx = qmol.edgeBegin(); e_idx < qmol.edgeEnd(); e_idx = qmol.edgeNext(e_idx)) if (aromatizer.isBondAromatic(e_idx)) { AutoPtr<QueryMolecule::Bond> bond(qmol.releaseBond(e_idx)); bond->removeConstraints(QueryMolecule::BOND_ORDER); AutoPtr<QueryMolecule::Bond> arom_bond( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)); qmol.resetBond(e_idx, QueryMolecule::Bond::und(bond.release(), arom_bond.release())); aromatized = true; } return aromatized; } bool QueryMoleculeAromatizer::_aromatizeBondsFuzzy (QueryMolecule &mol, const AromaticityOptions &options) { bool aromatized = false; QueryMoleculeAromatizer aromatizer(mol, options); aromatizer.setMode(QueryMoleculeAromatizer::FUZZY); aromatizer.precalculatePiLabels(); aromatizer.aromatize(); mol.aromaticity.clear(); for (int e_idx = mol.edgeBegin(); e_idx < mol.edgeEnd(); e_idx = mol.edgeNext(e_idx)) { bool aromatic_constraint = mol.getBond(e_idx).possibleValue(QueryMolecule::BOND_ORDER, BOND_AROMATIC); if (aromatic_constraint || aromatizer.isBondAromatic(e_idx)) { mol.aromaticity.setCanBeAromatic(e_idx, true); aromatized = true; } } return aromatized; } void MoleculeAromatizer::findAromaticAtoms (BaseMolecule &mol, Array<int> *atoms, Array<int> *bonds, const AromaticityOptions &options) { AutoPtr<BaseMolecule> clone; QS_DEF(Array<int>, mapping); clone.reset(mol.neu()); mapping.clear(); if (atoms != 0) { atoms->clear_resize(mol.vertexEnd()); atoms->zerofill(); } if (bonds != 0) { bonds->clear_resize(mol.edgeEnd()); bonds->zerofill(); } clone->clone(mol, &mapping, 0); clone->aromatize(options); for (int i = clone->edgeBegin(); i != clone->edgeEnd(); i = clone->edgeNext(i)) { if (clone->getBondOrder(i) == BOND_AROMATIC) { const Edge &edge = clone->getEdge(i); if (atoms != 0) { atoms->at(mapping[edge.beg]) = 1; atoms->at(mapping[edge.end]) = 1; } if (bonds != 0) bonds->at(mol.findEdgeIndex(mapping[edge.beg], mapping[edge.end])) = 1; } } } // // QueryMoleculeAromaticity // bool QueryMoleculeAromaticity::canBeAromatic (int edge_index) const { if (edge_index >= can_bond_be_aromatic.size()) return false; return can_bond_be_aromatic[edge_index]; } void QueryMoleculeAromaticity::setCanBeAromatic (int edge_index, bool state) { if (state == false && edge_index >= can_bond_be_aromatic.size()) return; can_bond_be_aromatic.expandFill(edge_index + 1, false); can_bond_be_aromatic[edge_index] = state; } void QueryMoleculeAromaticity::clear() { can_bond_be_aromatic.clear(); } �����������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_arom_match.cpp��������������������������������������������0000664�0000000�0000000�00000027615�12710376503�0023313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_arom_match.h" #include "base_cpp/obj.h" #include "graph/filter.h" #include "graph/spanning_tree.h" #include "molecule/query_molecule.h" #include "molecule/molecule_dearom.h" using namespace indigo; IMPL_ERROR(AromaticityMatcher, "AromaticityMatcher"); CP_DEF(AromaticityMatcher); AromaticityMatcher::AromaticityMatcher (QueryMolecule &query, BaseMolecule &base, const AromaticityOptions &arom_options) : _query(query), _base(base), CP_INIT, TL_CP_GET(_matching_edges_state) { _submolecule.reset(base.neu()); _matching_edges_state.clear(); _arom_options = arom_options; validateQuery(); } bool AromaticityMatcher::isNecessary (QueryMolecule &query) { for (int e = query.edgeBegin(); e < query.edgeEnd(); e = query.edgeNext(e)) { if (!query.aromaticity.canBeAromatic(e)) continue; QueryMolecule::Bond &bond = query.getBond(e); // Check if bond isn't aromatic but can be aromatic if (bond.possibleValue(QueryMolecule::BOND_ORDER, BOND_SINGLE)) return true; if (bond.possibleValue(QueryMolecule::BOND_ORDER, BOND_DOUBLE)) return true; } // Check R-groups MoleculeRGroups &rgroups = query.rgroups; int n_rgroups = rgroups.getRGroupCount(); for (int i = 1; i <= n_rgroups; i++) { PtrPool<BaseMolecule> &frags = rgroups.getRGroup(i).fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { QueryMolecule &fragment = frags[j]->asQueryMolecule(); if ( AromaticityMatcher::isNecessary(fragment)) return true; } } return false; } void AromaticityMatcher::validateQuery () { int old_size = _matching_edges_state.size(); _matching_edges_state.resize(_query.edgeEnd()); for (int i = old_size; i < _query.edgeEnd(); i++) { _matching_edges_state[i] = 0; } } bool AromaticityMatcher::canFixQueryBond (int query_edge_idx, bool aromatic) { // Check if bond is fixed then it aromatic state must be the same int _bond_state = _matching_edges_state[query_edge_idx]; if (_bond_state != ANY) return (_bond_state == AROMATIC) == aromatic; if (aromatic && !_query.aromaticity.canBeAromatic(query_edge_idx)) return false; // MR: TODO: Handle this case in more details. // Aromatic bonds connectivity graph can be decomposed // into connected linear components and each component // is aromatic or not. If one edge in such component is // marked as aromatic then all component must be mapped // on aromatic bonds in the target and vise-versa. return true; } void AromaticityMatcher::fixQueryBond (int query_edge_idx, bool aromatic) { int &bond_state = _matching_edges_state[query_edge_idx]; int new_bond_state = aromatic ? AROMATIC : NONAROMATIC; if (bond_state != ANY && bond_state != new_bond_state) throw Error("bond has already been fixed with another state"); bond_state = aromatic ? AROMATIC : NONAROMATIC; } void AromaticityMatcher::unfixQueryBond (int query_edge_idx) { if (_matching_edges_state[query_edge_idx] == ANY) return; _matching_edges_state[query_edge_idx] = ANY; } void AromaticityMatcher::unfixNeighbourQueryBond (int query_arom_idx) { const Vertex &vertex = _query.getVertex(query_arom_idx); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) unfixQueryBond(vertex.neiEdge(i)); } bool AromaticityMatcher::match (int *core_sub, int *core_super) { // Check if detailed checking is necessary bool needCheck = false; for (int i = _query.edgeBegin(); i != _query.edgeEnd(); i = _query.edgeNext(i)) { if (!_query.getBond(i).hasConstraint(QueryMolecule::BOND_ORDER)) continue; if (_matching_edges_state[i] == AROMATIC && _query.getBondOrder(i) != BOND_AROMATIC) { needCheck = true; break; } } if (!needCheck) return true; // By our rules submolecule in the query, that maps on aromatic bonds in // the target, must have aromatic bond configuration to match the target. // To check this such submolecule from query molecule is extracted, then // all bonds are marked as aromatic, and then dearomatizer tries to find // aromatic bonds configuration with partially fixed bonds. // 1. Extract query submolecule that maps on aromatic bonds. It is the same as in target. // Set skip all additional informatio during copying QS_DEF(Array<int>, mapping); mapping.clear(); for (int v_idx = _query.vertexBegin(); v_idx < _query.vertexEnd(); v_idx = _query.vertexNext(v_idx)) { int target_idx = core_sub[v_idx]; if (target_idx < 0) continue; mapping.push(target_idx); } QS_DEF(Array<int>, edges); QS_DEF(Array<int>, base_edges_mask); edges.clear(); base_edges_mask.clear_resize(_base.edgeEnd()); base_edges_mask.zerofill(); for (int e_idx = _query.edgeBegin(); e_idx < _query.edgeEnd(); e_idx = _query.edgeNext(e_idx)) { const Edge &e = _query.getEdge(e_idx); if (core_sub[e.beg] < 0 || core_sub[e.end] < 0) continue; int target_idx = _base.findEdgeIndex(core_sub[e.beg], core_sub[e.end]); if (target_idx == -1) throw Error("(AromaticityMatcher::match) target edge wasn't found"); edges.push(target_idx); base_edges_mask[target_idx] = 1; } QS_DEF(Array<int>, inv_mapping); _submolecule->makeEdgeSubmolecule(_base, mapping, edges, &inv_mapping, SKIP_ALL); QS_DEF(Array<int>, external_conn); external_conn.resize(_submolecule->vertexEnd()); external_conn.zerofill(); // Calculate external connectivity for (int i = 0; i < mapping.size(); i++) { int base_idx = mapping[i]; const Vertex &v = _base.getVertex(base_idx); int cur_external_conn = 0; for (int ni = v.neiBegin(); ni != v.neiEnd(); ni = v.neiNext(ni)) { int ni_edge = v.neiEdge(ni); if (!base_edges_mask[ni_edge]) { int bond_order_diff = _base.getBondOrder(ni_edge); if (bond_order_diff == BOND_AROMATIC) bond_order_diff = 1; cur_external_conn += bond_order_diff; } } external_conn[i] = cur_external_conn; } // 1b. Find bonds in aromatic rings in query and skip aromatic // bonds that are not in cycles QS_DEF(Array<int>, is_edge_in_aromatic_cycle); is_edge_in_aromatic_cycle.clear_resize(_submolecule->edgeEnd()); is_edge_in_aromatic_cycle.zerofill(); // At first just mark aromatic bonds for (int e_idx = _submolecule->edgeBegin(); e_idx < _submolecule->edgeEnd(); e_idx = _submolecule->edgeNext(e_idx)) { if (_submolecule->getBondOrder(e_idx) == BOND_AROMATIC) is_edge_in_aromatic_cycle[e_idx] = 1; } Filter aromatic_edge_filter(is_edge_in_aromatic_cycle.ptr(), Filter::EQ, 1); SpanningTree arom_edges_st(_submolecule.ref(), 0, &aromatic_edge_filter); // Store in is_edge_in_aromatic_cycle marks about such bonds is_edge_in_aromatic_cycle.zerofill(); arom_edges_st.markAllEdgesInCycles(is_edge_in_aromatic_cycle.ptr(), 1); enum { AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE = 2 }; for (int e_idx = _submolecule->edgeBegin(); e_idx < _submolecule->edgeEnd(); e_idx = _submolecule->edgeNext(e_idx)) { if (_submolecule->getBondOrder(e_idx) == BOND_AROMATIC && is_edge_in_aromatic_cycle[e_idx] == 0) { // Such bond is marked as aromatic but it isn't in aromatic ring // Here just change bond order to nonaromatic (single) and later // check if such query bond can be aromatic (not by aromatizer) if (_submolecule->isQueryMolecule()) { QueryMolecule &qmol = _submolecule->asQueryMolecule(); AutoPtr<QueryMolecule::Bond> bond(qmol.releaseBond(e_idx)); bond->removeConstraints(QueryMolecule::BOND_ORDER); AutoPtr<QueryMolecule::Bond> arom_bond( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)); qmol.resetBond(e_idx, QueryMolecule::Bond::und(bond.release(), arom_bond.release())); } else _submolecule->asMolecule().setBondOrder(e_idx, BOND_SINGLE, false); is_edge_in_aromatic_cycle[e_idx] = AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE; } } // 2. Try to find suitable dearomatization QS_DEF(DearomatizationsStorage, dearomatizations); // Dearomatizer and DearomatizationMatcher will be created on demand Obj<Dearomatizer> dearomatizer; Obj<DearomatizationMatcher> dearomatizationMatcher; // Check edges for (int e = _submolecule->edgeBegin(); e != _submolecule->edgeEnd(); e = _submolecule->edgeNext(e)) { const Edge &edge = _submolecule->getEdge(e); int target_edge_index = _base.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); const Edge &target_edge = _base.getEdge(target_edge_index); int query_edge_index = _query.findEdgeIndex(core_super[target_edge.beg], core_super[target_edge.end]); if (query_edge_index == -1) throw Error("(AromaticityMatcher::match) query edge wasn't found"); if (_matching_edges_state[query_edge_index] != AROMATIC) // Such target bond isn't aromatic. So we don't need to fix such bond continue; QueryMolecule::Bond &qbond = _query.getBond(query_edge_index); bool can_have_arom_order = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_AROMATIC, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); if (can_have_arom_order) // This query bond is aromatic. continue; if (is_edge_in_aromatic_cycle[e] == AROMATIC_BOND_NOT_IN_AROMATIC_CYCLE) { // Such bond cannot be aromatic without aromatic // cycle because 'can_have_arom_order' is false return false; } bool has_other = !qbond.hasNoConstraintExcept(QueryMolecule::BOND_ORDER, QueryMolecule::BOND_TOPOLOGY); if (has_other) throw Error("Only bond with order and topology constraints are supported"); bool can_have_single = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_SINGLE, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); bool can_have_double = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_DOUBLE, QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); // Check if query bond can be only single or only double if (can_have_single && can_have_double) // Don't fix such bond. It can be single/double, single/aromatic and etc. continue; if (!can_have_single && !can_have_double) // Bond without and specification and etc. continue; // Find dearomatization if (dearomatizer.get() == NULL) { dearomatizer.create(_submolecule.ref(), external_conn.ptr(), _arom_options); dearomatizer->enumerateDearomatizations(dearomatizations); dearomatizationMatcher.create(dearomatizations, _submolecule.ref(), external_conn.ptr()); } // Fix bond int type = can_have_single ? BOND_SINGLE : BOND_DOUBLE; if (!dearomatizationMatcher->isAbleToFixBond(e, type)) return false; dearomatizationMatcher->fixBond(e, type); } return true; } �������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_auto_loader.cpp�������������������������������������������0000664�0000000�0000000�00000020163�12710376503�0023466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_auto_loader.h" #include "base_cpp/scanner.h" #include "molecule/smiles_loader.h" #include "molecule/molfile_loader.h" #include "molecule/icm_loader.h" #include "molecule/icm_saver.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "gzip/gzip_scanner.h" #include "molecule/molecule_cml_loader.h" #include "molecule/sdf_loader.h" #include "molecule/molecule_cdx_loader.h" using namespace indigo; void MoleculeAutoLoader::_init () { stereochemistry_options.reset(); treat_x_as_pseudoatom = false; ignore_closing_bond_direction_mismatch = false; ignore_noncritical_query_features = false; skip_3d_chirality = false; ignore_cistrans_errors = false; } IMPL_ERROR(MoleculeAutoLoader, "molecule auto loader"); CP_DEF(MoleculeAutoLoader); MoleculeAutoLoader::MoleculeAutoLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(properties) { _scanner = &scanner; _own_scanner = false; _init(); } MoleculeAutoLoader::MoleculeAutoLoader (const Array<char> &arr) : CP_INIT, TL_CP_GET(properties) { _scanner = new BufferScanner(arr); _own_scanner = true; _init(); } MoleculeAutoLoader::MoleculeAutoLoader (const char *str) : CP_INIT, TL_CP_GET(properties) { _scanner = new BufferScanner(str); _own_scanner = true; _init(); } MoleculeAutoLoader::~MoleculeAutoLoader () { if (_own_scanner) delete _scanner; } void MoleculeAutoLoader::loadMolecule (Molecule &mol) { _loadMolecule(mol, false); } void MoleculeAutoLoader::loadQueryMolecule (QueryMolecule &qmol) { _loadMolecule(qmol, true); } bool MoleculeAutoLoader::tryMDLCT (Scanner &scanner, Array<char> &outbuf) { int pos = scanner.tell(); bool endmark = false; QS_DEF(Array<char>, curline); outbuf.clear(); while (!scanner.isEOF()) { int len = scanner.readByte(); if (len > 90) // Molfiles and Rxnfiles actually have 80 characters limit { scanner.seek(pos, SEEK_SET); // Garbage after endmark means end of data. // (See the note below about endmarks) if (endmark) return true; return false; } curline.clear(); while (len-- > 0) { if (scanner.isEOF()) { scanner.seek(pos, SEEK_SET); return false; } int c = scanner.readChar(); curline.push(c); } curline.push(0); if (endmark) { // We can not properly read the data to the end as there // is often garbage after the actual MDLCT data. // Instead, we are doing this lousy check: // "M END" or "$END MOL" can be followed only // by "$END CTAB" (in Markush queries), or // by "$MOL" (in Rxnfiles). Otherwise, this // is actually the end of data. if (strcmp(curline.ptr(), "$END CTAB") != 0 && strcmp(curline.ptr(), "$MOL") != 0) { scanner.seek(pos, SEEK_SET); return true; } } if (strcmp(curline.ptr(), "M END") == 0) endmark = true; else if (strcmp(curline.ptr(), "$END MOL") == 0) endmark = true; else endmark = false; outbuf.appendString(curline.ptr(), false); outbuf.push('\n'); } scanner.seek(pos, SEEK_SET); // It happened once that a valid Molfile had successfully // made its way through the above while() cycle, and thus // falsely recognized as MDLCT. To fight this case, we include // here a check that the last line was actually an endmark return endmark; } void MoleculeAutoLoader::_loadMolecule (BaseMolecule &mol, bool query) { properties.clear(); // check for GZip format if (!query && _scanner->length() >= 2) { byte id[2]; int pos = _scanner->tell(); _scanner->readCharsFix(2, (char *)id); _scanner->seek(pos, SEEK_SET); if (id[0] == 0x1f && id[1] == 0x8b) { GZipScanner gzscanner(*_scanner); QS_DEF(Array<char>, buf); gzscanner.readAll(buf); MoleculeAutoLoader loader2(buf); loader2.stereochemistry_options = stereochemistry_options; loader2.ignore_noncritical_query_features = ignore_noncritical_query_features; loader2.treat_x_as_pseudoatom = treat_x_as_pseudoatom; loader2.skip_3d_chirality = skip_3d_chirality; loader2.loadMolecule((Molecule &)mol); return; } } // check for MDLCT format { QS_DEF(Array<char>, buf); if (tryMDLCT(*_scanner, buf)) { BufferScanner scanner2(buf); MolfileLoader loader(scanner2); loader.stereochemistry_options = stereochemistry_options; loader.ignore_noncritical_query_features = ignore_noncritical_query_features; loader.skip_3d_chirality = skip_3d_chirality; loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; if (query) loader.loadQueryMolecule((QueryMolecule &)mol); else loader.loadMolecule((Molecule &)mol); return; } } // check for ICM format if (!query && _scanner->length() >= 4) { char id[3]; int pos = _scanner->tell(); _scanner->readCharsFix(3, id); _scanner->seek(pos, SEEK_SET); if (IcmSaver::checkVersion(id)) { if (query) throw Error("cannot load query molecule from ICM format"); IcmLoader loader(*_scanner); loader.loadMolecule((Molecule &)mol); return; } } // check for CML format { int pos = _scanner->tell(); _scanner->skipSpace(); if (_scanner->lookNext() == '<') { if (_scanner->findWord("<molecule")) { MoleculeCmlLoader loader(*_scanner); loader.stereochemistry_options = stereochemistry_options; if (query) throw Error("CML queries not supported"); loader.loadMolecule(mol.asMolecule()); return; } } _scanner->seek(pos, SEEK_SET); } // check for SMILES format if (Scanner::isSingleLine(*_scanner)) { SmilesLoader loader(*_scanner); loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.stereochemistry_options = stereochemistry_options; loader.ignore_cistrans_errors = ignore_cistrans_errors; if (query) loader.loadQueryMolecule((QueryMolecule &)mol); else loader.loadMolecule((Molecule &)mol); return; } // check for CDX format /* { if (_scanner->findWord("VjCD0100")) { MoleculeCdxLoader loader(*_scanner); loader.stereochemistry_options = stereochemistry_options; if (query) throw Error("CDX queries not supported yet"); loader.loadMolecule(mol.asMolecule()); properties.copy(loader.properties); return; } } */ // default is Molfile format { SdfLoader sdf_loader(*_scanner); sdf_loader.readNext(); // Copy properties properties.copy(sdf_loader.properties); BufferScanner scanner2(sdf_loader.data); MolfileLoader loader(scanner2); loader.stereochemistry_options = stereochemistry_options; loader.ignore_noncritical_query_features = ignore_noncritical_query_features; loader.skip_3d_chirality = skip_3d_chirality; loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; if (query) loader.loadQueryMolecule((QueryMolecule &)mol); else loader.loadMolecule((Molecule &)mol); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_automorphism_search.cpp�����������������������������������0000664�0000000�0000000�00000115227�12710376503�0025252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_automorphism_search.h" #include "base_cpp/profiling.h" #include "base_cpp/cancellation_handler.h" #include "graph/spanning_tree.h" #include "graph/graph_decomposer.h" #include "molecule/molecule.h" #include "molecule/molecule_scaffold_detection.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeAutomorphismSearch, "Molecule automorphism search"); IMPL_TIMEOUT_EXCEPTION(MoleculeAutomorphismSearch, "Molecule automorphism search"); MoleculeAutomorphismSearch::MoleculeAutomorphismSearch () : TL_CP_GET(_approximation_orbits), TL_CP_GET(_approximation_orbits_saved), TL_CP_GET(_hcount), TL_CP_GET(_cistrans_stereo_bond_parity), TL_CP_GET(_degree), TL_CP_GET(_independent_component_index), TL_CP_GET(_stereocenter_state), TL_CP_GET(_cistrans_bond_state) { cb_vertex_cmp = _vertex_cmp; cb_edge_rank = _edge_rank; cb_check_automorphism = _check_automorphism; cb_compare_mapped = _compare_mapped; cb_automorphism = _automorphismCallback; context_automorphism = this; context = this; detect_invalid_stereocenters = false; detect_invalid_cistrans_bonds = false; find_canonical_ordering = false; _fixed_atom = -1; allow_undefined = false; _cancellation_handler = getCancellationHandler(); } void MoleculeAutomorphismSearch::_getFirstApproximation (Molecule &mol) { _stereocenter_state.clear_resize(mol.vertexEnd()); _cistrans_bond_state.clear_resize(mol.edgeEnd()); const MoleculeStereocenters &stereocenters = mol.stereocenters; for (int i = 0; i < _stereocenter_state.size(); i++) _stereocenter_state[i] = _NO_STEREO; for (int i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int atom_index = stereocenters.getAtomIndex(i); _stereocenter_state[atom_index] = _UNDEF; } for (int i = 0; i < _cistrans_bond_state.size(); i++) _cistrans_bond_state[i] = _NO_STEREO; for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i)) _cistrans_bond_state[i] = _UNDEF; _cistrans_stereo_bond_parity.clear_resize(mol.edgeEnd()); _cistrans_stereo_bond_parity.zerofill(); _treat_undef_as = _INVALID; _target_stereocenter = -1; _target_bond = -1; // Approximation orbits cannot be used on this step because // of existance of check_automorphism method. But order from // first refinment can be used here. _approximation_orbits.fffill(); profTimerStart(t0, "mol_auto.first_search"); AutomorphismSearch::process(mol); profTimerStop(t0); getCanonicallyOrderedOrbits(_approximation_orbits); _findCisTransStereoBondParirties(mol); } void MoleculeAutomorphismSearch::_findCisTransStereoBondParirties (Molecule &mol) { const MoleculeStereocenters &stereocenters = mol.stereocenters; // Mark edges that connects two stereocenters and that common parity can be detected for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); if (!stereocenters.exists(edge.beg) || !stereocenters.exists(edge.end)) continue; if (stereocenters.getGroup(edge.beg) != stereocenters.getGroup(edge.end)) continue; if (stereocenters.getType(edge.beg) != stereocenters.getType(edge.end)) continue; // Both stereocenters exists and groups are the same int orb_beg = _approximation_orbits[edge.beg]; int orb_end = _approximation_orbits[edge.end]; _approximation_orbits[edge.beg] = -2; // Some value different from -1 and other orbits _approximation_orbits[edge.end] = -2; int parity_beg, parity_end; if (_validStereocenterByAtom(edge.beg, _approximation_orbits, &parity_beg) == _VALID && _validStereocenterByAtom(edge.end, _approximation_orbits, &parity_end) == _VALID) { // 1 - means that both stereocenters have the same parity _cistrans_stereo_bond_parity[i] = -parity_beg * parity_end; } // Restore orbits _approximation_orbits[edge.beg] = orb_beg; _approximation_orbits[edge.end] = orb_end; } } void MoleculeAutomorphismSearch::_initialize (Molecule &mol) { _fixed_atom = -1; _calculateHydrogensAndDegree(mol); // Initialize first orbits approximation to -1 _approximation_orbits.clear_resize(mol.vertexEnd()); _approximation_orbits.fffill(); // Get first approximation without stereocenters if // detect_invalid_stereocenters or detect_invalid_cistrans_bonds is set _getFirstApproximation(mol); } void MoleculeAutomorphismSearch::process (Molecule &mol) { _initialize(mol); if (detect_invalid_stereocenters || detect_invalid_cistrans_bonds) { // Mark stereocenters that are valid _markValidOrInvalidStereo(true, _approximation_orbits, NULL); _markValidOrInvalidStereo(false, _approximation_orbits, NULL); // Temposrary solution: mark unknown stereocenters as valid if // number of them is more then 1 in each component _markComplicatedStereocentersAsValid(mol); // Find invalid stereocenters and bonds iteratively // For each component const int *ignored_vertices_saved = ignored_vertices; GraphDecomposer decomposer(mol); int ncomp = decomposer.decompose(); QS_DEF(Array<int>, ignored_vertices_arr); ignored_vertices_arr.resize(mol.vertexEnd()); ignored_vertices = ignored_vertices_arr.ptr(); for (int comp = 0; comp < ncomp; comp++) { // Copy existing ignores vertices if (ignored_vertices_saved != 0) ignored_vertices_arr.copy(ignored_vertices_saved, mol.vertexEnd()); else ignored_vertices_arr.zerofill(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (decomposer.getComponent(i) != comp) ignored_vertices_arr[i] = 1; } while (_findInvalidStereo(mol)) ; } ignored_vertices = ignored_vertices_saved; } // Mark all other stereocenters and stereobonds as valid const MoleculeStereocenters &stereocenters = mol.stereocenters; for (int i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int atom_index = stereocenters.getAtomIndex(i); if (_stereocenter_state[atom_index] == _UNDEF) _stereocenter_state[atom_index] = _VALID; } for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (_cistrans_bond_state[i] == _UNDEF && mol.cis_trans.getParity(i) != 0) _cistrans_bond_state[i] = _VALID; // Find final orbits and canonical ordering with found // valid and invalid stereocenters and bonds _target_stereocenter = -1; _target_bond = -1; _fixed_atom = -1; // Find possible cis-trans bonds _findAllPossibleCisTrans(mol); if (find_canonical_ordering) { profTimerStart(t0, "mol_auto.final_search"); AutomorphismSearch::process(mol); profTimerStop(t0); } } void MoleculeAutomorphismSearch::_findAllPossibleCisTrans (Molecule &mol) { int s = 0; while (s != possible_cis_trans_to_check.size()) { s = possible_cis_trans_to_check.size(); _findAllPossibleCisTransOneStep(mol); } } void MoleculeAutomorphismSearch::_findAllPossibleCisTransOneStep (Molecule &mol) { _approximation_orbits_saved.copy(_approximation_orbits); // Uniquify such bonds and mark them temorary as cis-trans int mark = mol.vertexEnd(); for (int i = 0; i < possible_cis_trans_to_check.size(); i++) { int bond = possible_cis_trans_to_check[i]; int subst[4]; if (!MoleculeCisTrans::isGeomStereoBond(mol, bond, subst, false)) { possible_cis_trans_to_check.remove(i); i--; continue; } if (mol.cis_trans.getParity(bond)) throw Error("Possible cis-trans check allowed only for non cis-trans bonds"); mol.cis_trans.add(bond, subst, MoleculeCisTrans::CIS); // Parity doesn't matter int validity = _validCisTransBond(bond, _approximation_orbits); _cistrans_bond_state[bond] = validity; // Uniqufy this bond const Edge &e = mol.getEdge(bond); _approximation_orbits[e.beg] = mark++; } _findInvalidStereoCisTrans(mol); for (int i = 0; i < possible_cis_trans_to_check.size(); i++) { int bond = possible_cis_trans_to_check[i]; int state = _cistrans_bond_state[bond]; // Restore state _cistrans_bond_state[bond] = _NO_STEREO; mol.cis_trans.setParity(bond, 0); if (state == _INVALID) { possible_cis_trans_to_check.remove(i); i--; } } // Restore orbits _approximation_orbits.copy(_approximation_orbits_saved); } bool MoleculeAutomorphismSearch::_hasStereo (Molecule &mol) { for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i) != 0) return true; if (mol.stereocenters.size() != 0) return true; return false; } int MoleculeAutomorphismSearch::_vertex_cmp (Graph &graph, int v1, int v2, const void *context) { const MoleculeAutomorphismSearch &self = *(MoleculeAutomorphismSearch *)context; Molecule &mol = (Molecule &)graph; int ret; // Compare orbits of the first approximation ret = self._approximation_orbits[v1] - self._approximation_orbits[v2]; if (ret != 0) return ret; ret = Molecule::matchAtomsCmp(graph, graph, v1, v2, 0); if (ret != 0) return ret; if (self._hcount[v1] < self._hcount[v2]) return 1; if (self._hcount[v1] > self._hcount[v2]) return -1; // Stereo should be compared at the end because it is // refinement on the first approximation ret = _compareStereo(mol, v1, v2, context); if (ret != 0) return -ret; int h1 = mol.isAtomHighlighted(v1); int h2 = mol.isAtomHighlighted(v2); if (h1 != h2) return h1 - h2; int i1 = self._independent_component_index[v1]; int i2 = self._independent_component_index[v2]; if (i1 != i2) return i1 - i2; return 0; } int MoleculeAutomorphismSearch::_edge_rank (Graph &graph, int edge_idx, const void *context) { const MoleculeAutomorphismSearch &self = *(const MoleculeAutomorphismSearch *)context; Molecule &mol = (Molecule &)graph; int rank0 = -1; if (self._cistrans_stereo_bond_parity[edge_idx] != 0) { // Check parity const Edge &edge = mol.getEdge(edge_idx); bool beg_valid = (self._getStereo(self._stereocenter_state[edge.beg]) == _VALID); bool end_valid = (self._getStereo(self._stereocenter_state[edge.beg]) == _VALID); if (beg_valid && end_valid) { if (self._cistrans_stereo_bond_parity[edge_idx] == 1) rank0 = 5; else rank0 = 6; } } if (rank0 == -1) rank0 = mol.getBondOrder(edge_idx); // Also take into account highlighting int highlighted = mol.isBondHighlighted(edge_idx); return 2 * rank0 + highlighted; } bool MoleculeAutomorphismSearch::_check_automorphism (Graph &graph, const Array<int> &mapping, const void *context) { const MoleculeAutomorphismSearch &self = *(MoleculeAutomorphismSearch *)context; Molecule &mol = (Molecule &)graph; if (self._cancellation_handler != 0 && self._cancellation_handler->isCancelled()) throw TimeoutException("%s", self._cancellation_handler->cancelledRequestMessage()); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); if (mapping[edge.beg] == -1 || mapping[edge.end] == -1) continue; int edge_idx = mol.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); if (edge_idx == -1) throw Error("internal error: this should be checked in AutomorphismSearch"); if (mol.getBondOrder(i) != mol.getBondOrder(edge_idx)) return false; } for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (self._getStereo(self._cistrans_bond_state[i]) != _VALID || mol.cis_trans.getParity(i) == 0) continue; const Edge &edge = mol.getEdge(i); if (mapping[edge.beg] == -1 || mapping[edge.end] == -1) continue; if (!_isCisTransBondMappedRigid(mol, i, mapping.ptr())) return false; } if (!self._checkStereocentersAutomorphism(mol, mapping)) return false; return true; } bool MoleculeAutomorphismSearch::_checkStereocentersAutomorphism (Molecule &mol, const Array<int> &mapping) const { const MoleculeStereocenters &stereocenters = mol.stereocenters; if (stereocenters.size() != 0) { Filter stereocenters_valid_filter; if (_treat_undef_as == _VALID) stereocenters_valid_filter.init(_stereocenter_state.ptr(), Filter::NEQ, _INVALID); else stereocenters_valid_filter.init(_stereocenter_state.ptr(), Filter::EQ, _VALID); bool ret = MoleculeStereocenters::checkSub(stereocenters, stereocenters, mapping.ptr(), false, &stereocenters_valid_filter); if (!ret) return false; QS_DEF(Array<int>, inv_mapping); inv_mapping.clear_resize(mol.vertexEnd()); inv_mapping.fffill(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mapping[i] != -1) inv_mapping[mapping[i]] = i; } ret = MoleculeStereocenters::checkSub(stereocenters, stereocenters, inv_mapping.ptr(), false, &stereocenters_valid_filter); if (!ret) return false; } return true; } void MoleculeAutomorphismSearch::_getSortedNei (Graph &g, int v_idx, Array<EdgeInfo> &sorted_nei, Array<int>& inv_mapping) { sorted_nei.clear(); const Vertex &v = g.getVertex(v_idx); for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int nei_v_mapped = inv_mapping[v.neiVertex(nei)]; if (nei_v_mapped == -1) continue; int j = sorted_nei.size() - 1; sorted_nei.push(); while (j >= 0) { if (sorted_nei[j].mapped_vertex > nei_v_mapped) { sorted_nei[j + 1] = sorted_nei[j]; j--; } else break; } EdgeInfo &info = sorted_nei[j + 1]; info.mapped_vertex = nei_v_mapped; info.edge = v.neiEdge(nei); } } int MoleculeAutomorphismSearch::_getMappedBondOrderAndParity (Molecule &m, int e, Array<int>& inv_mapping) const { int type = m.getBondOrder(e); // Map parities to the provided new mapping int parity = m.cis_trans.getParity(e); if (parity != 0 && _getStereo(_cistrans_bond_state[e]) == _VALID) { int parity_mapped = MoleculeCisTrans::applyMapping(parity, m.cis_trans.getSubstituents(e), inv_mapping.ptr(), true); type = type * 100 + parity_mapped; } return type; } int MoleculeAutomorphismSearch::_compare_mapped (Graph &graph, const Array<int> &mapping1, const Array<int> &mapping2, const void *context) { const MoleculeAutomorphismSearch &self = *(MoleculeAutomorphismSearch *)context; Molecule &mol = (Molecule &)graph; QS_DEF(Array<int>, inv_mapping1); QS_DEF(Array<int>, inv_mapping2); inv_mapping1.clear_resize(graph.vertexEnd()); inv_mapping2.clear_resize(graph.vertexEnd()); inv_mapping1.fffill(); inv_mapping2.fffill(); for (int i = 0; i < mapping1.size(); i++) { inv_mapping1[mapping1[i]] = i; inv_mapping2[mapping2[i]] = i; } int min_diff_beg = graph.vertexEnd(); int min_diff_end = graph.vertexEnd(); int diff_sign = 0; // This function compares two mappings to select one of it // according to the defined (here) order. // Canonical mapping is taken as the smallest mapping. // To compare two mapping we compare their connectivity matrix QS_DEF(Array<EdgeInfo>, sorted_nei1); QS_DEF(Array<EdgeInfo>, sorted_nei2); // Compare connectivity matrix line by line for (int i = 0; i < mapping1.size(); i++) { _getSortedNei(graph, mapping1[i], sorted_nei1, inv_mapping1); _getSortedNei(graph, mapping2[i], sorted_nei2, inv_mapping2); if (sorted_nei1.size() != sorted_nei2.size()) return sorted_nei1.size() > sorted_nei2.size() ? 1 : -1; for (int j = 0; j < sorted_nei1.size(); j++) { int m1 = sorted_nei1[j].mapped_vertex; int m2 = sorted_nei2[j].mapped_vertex; if (m1 != m2) return m1 > m2 ? 1 : -1; int e1 = sorted_nei1[j].edge; int e2 = sorted_nei2[j].edge; // Compare edge bond orders and parities int type1 = self._getMappedBondOrderAndParity(mol, e1, inv_mapping1); int type2 = self._getMappedBondOrderAndParity(mol, e2, inv_mapping2); if (type1 != type2) return type1 > type2 ? 1 : -1; } } return self._compareMappedStereocenters(mol, mapping1, mapping2, inv_mapping1, inv_mapping2); } int MoleculeAutomorphismSearch::_compareMappedStereocenters (Molecule &mol, const Array<int> &mapping1, const Array<int> &mapping2, const Array<int> &inv_mapping1, const Array<int> &inv_mapping2) const { MoleculeStereocenters &stereocenters = mol.stereocenters; if (stereocenters.size() == 0) return 0; int max_stereogroup = 0; for (int s = stereocenters.begin(); s != stereocenters.end(); s = stereocenters.next(s)) { int atom_idx = stereocenters.getAtomIndex(s); max_stereogroup = __max(stereocenters.getGroup(atom_idx), max_stereogroup); } int groups_count = 2 * (max_stereogroup + 1); QS_DEF(Array<int>, stereogroup1_rank); QS_DEF(Array<int>, stereogroup2_rank); QS_DEF(Array<int>, stereogroup1_parity_mod); QS_DEF(Array<int>, stereogroup2_parity_mod); stereogroup1_rank.clear_resize(groups_count); stereogroup1_rank.fffill(); stereogroup2_rank.clear_resize(groups_count); stereogroup2_rank.fffill(); stereogroup1_parity_mod.clear_resize(groups_count); stereogroup1_parity_mod.fffill(); stereogroup2_parity_mod.clear_resize(groups_count); stereogroup2_parity_mod.fffill(); for (int i = 0; i < mapping1.size(); i++) { int type1 = stereocenters.getType(mapping1[i]); int type2 = stereocenters.getType(mapping2[i]); if (_getStereo(_stereocenter_state[mapping1[i]]) == _INVALID) type1 = 0; if (_getStereo(_stereocenter_state[mapping2[i]]) == _INVALID) type2 = 0; if (type1 != type2) throw Error("internal: stereocenter types mismatch"); if (type1 < MoleculeStereocenters::ATOM_AND) continue; int pyramid1[4], pyramid2[4]; memcpy(pyramid1, stereocenters.getPyramid(mapping1[i]), 4 * sizeof(int)); memcpy(pyramid2, stereocenters.getPyramid(mapping2[i]), 4 * sizeof(int)); int size1 = 0, size2 = 0; for (int j = 0; j < 4; j++) { if (pyramid1[j] >= 0) { if (inv_mapping1[pyramid1[j]] >= 0) size1++; else pyramid1[j] = -1; } if (pyramid2[j] >= 0) { if (inv_mapping2[pyramid2[j]] >= 0) size2++; else pyramid2[j] = -1; } } if (size1 != size2) throw Error("internal: stereocenter sizes mismatch"); bool rigid1 = true, rigid2 = true; if (size1 >= 3) { if (size1 == 3) MoleculeStereocenters::moveImplicitHydrogenToEnd(pyramid1); for (int j = 0; j < size1; j++) pyramid1[j] = inv_mapping1[pyramid1[j]]; rigid1 = MoleculeStereocenters::isPyramidMappingRigid(pyramid1); } if (size2 >= 3) { if (size2 == 3) MoleculeStereocenters::moveImplicitHydrogenToEnd(pyramid2); for (int j = 0; j < size2; j++) pyramid2[j] = inv_mapping2[pyramid2[j]]; rigid2 = MoleculeStereocenters::isPyramidMappingRigid(pyramid2); } int group1 = stereocenters.getGroup(mapping1[i]); int group2 = stereocenters.getGroup(mapping2[i]); // Encode group with type into one index int type_group1 = 2 * group1 + (type1 == MoleculeStereocenters::ATOM_AND ? 0 : 1); int type_group2 = 2 * group2 + (type2 == MoleculeStereocenters::ATOM_AND ? 0 : 1); if (type1 == MoleculeStereocenters::ATOM_AND || type1 == MoleculeStereocenters::ATOM_OR) { int &parity_mod1 = stereogroup1_parity_mod[type_group1]; int &parity_mod2 = stereogroup2_parity_mod[type_group2]; // First stereocenter in group always maps rigid if (parity_mod1 == -1) parity_mod1 = rigid1 ? 0 : 1; if (parity_mod2 == -1) parity_mod2 = rigid2 ? 0 : 1; if (parity_mod1 == 1) // Invert sterecenter rigid1 = !rigid1; if (parity_mod2 == 1) // Invert sterecenter rigid2 = !rigid2; } if (rigid1 && !rigid2) return 1; if (rigid2 && !rigid1) return -1; // Compare stereogroups if (type1 == MoleculeStereocenters::ATOM_AND || type1 == MoleculeStereocenters::ATOM_OR) { int &rank1 = stereogroup1_rank[type_group1]; int &rank2 = stereogroup2_rank[type_group2]; if (rank1 == -1) rank1 = i; if (rank2 == -1) rank2 = i; int diff = rank1 - rank2; if (diff != 0) return diff; } } return 0; } int MoleculeAutomorphismSearch::_validCisTransBond (int idx, const Array<int> &orbits) { Molecule &mol = *(Molecule *)_given_graph; int parity = mol.cis_trans.getParity(idx); if (parity == 0) return _UNDEF; const int *subst = mol.cis_trans.getSubstituents(idx); // Stereobond can be valid even if substituents are in the same orbit. // For example: C\C=C1/C\C(C1)=C\C // But if substituents are in the same orbit and have degree 1 then such // cis-trans bond is invalid. // To detect invalid stereocenters automorphism group structure shold be investigated. // NOTE: C\C=C1/C\C(C1)=C1\C/C(C1)=C\C // Double bond in the middle isn't cis-trans but current algorithm cannot detect this. if (subst[0] != -1 && subst[1] != -1) if (orbits[subst[0]] == orbits[subst[1]]) { if (_degree[subst[0]] == 1) return _INVALID; return _UNDEF; } if (subst[2] != -1 && subst[3] != -1) if (orbits[subst[2]] == orbits[subst[3]]) { if (_degree[subst[2]] == 1) return _INVALID; return _UNDEF; } return _VALID; } int MoleculeAutomorphismSearch::_validStereocenterByAtom (int atom_index, Array<int> &orbits, int *parity) { Molecule &mol = *(Molecule *)_given_graph; int type = mol.stereocenters.getType(atom_index); if (type == 0) return _UNDEF; const int *pyramid = mol.stereocenters.getPyramid(atom_index); int substituents[4]; for (int i = 0; i < 4; i++) if (pyramid[i] != -1) substituents[i] = orbits[pyramid[i]]; else substituents[i] = -1; // Check if orbits for substituents are different. // Even if substituents are in the same orbit stereocenter can be valid. // But if substituents are in the same orbit and have degree 1 then such // stereocenter is invalid. bool has_undef = false; for (int i = 0; i < 4; i++) { if (substituents[i] == -1) continue; int same_count = 0; for (int j = 0; j < 4; j++) if (substituents[i] == substituents[j]) same_count++; if (same_count != 1) { if (_degree[pyramid[i]] == 1) return _INVALID; has_undef = true; } } if (has_undef) return _UNDEF; // Detect parity if (parity != NULL) { if (MoleculeStereocenters::isPyramidMappingRigid(substituents)) *parity = 1; else *parity = -1; } return _VALID; } int MoleculeAutomorphismSearch::_validStereocenter (int idx, Array<int> &orbits, int *parity) { Molecule &mol = *(Molecule *)_given_graph; int atom_index = mol.stereocenters.getAtomIndex(idx); return _validStereocenterByAtom(atom_index, orbits, parity); } bool MoleculeAutomorphismSearch::invalidCisTransBond (int idx) { if (_cistrans_bond_state[idx] == _UNDEF) throw Error("Internal error: stereocenters state must be know here"); return (_cistrans_bond_state[idx] == _INVALID); } bool MoleculeAutomorphismSearch::invalidStereocenter (int idx) { if (_stereocenter_state[idx] == _UNDEF) throw Error("Internal error: stereocenters state must be know here"); return (_stereocenter_state[idx] == _INVALID); } int MoleculeAutomorphismSearch::_compareStereo (Molecule &mol, int v1, int v2, const void *context) { const MoleculeAutomorphismSearch &self = *(MoleculeAutomorphismSearch *)context; int diff; if (self._fixed_atom != -1) { int t1 = (self._fixed_atom == v1) ? 1 : 0; int t2 = (self._fixed_atom == v2) ? 1 : 0; diff = t1 - t2; if (diff != 0) return diff; } const MoleculeStereocenters &stereocenters = mol.stereocenters; int s1 = self._getStereo(self._stereocenter_state[v1]); int s2 = self._getStereo(self._stereocenter_state[v2]); diff = s1 - s2; if (diff != 0) return diff; if (s1 != _VALID) // Both stereocenters are marked as invalid return 0; diff = stereocenters.getType(v1) - stereocenters.getType(v2); if (diff != 0) return diff; return 0; } void MoleculeAutomorphismSearch::_calculateHydrogensAndDegree (Molecule &mol) { _hcount.clear_resize(mol.vertexEnd()); _degree.clear_resize(mol.vertexEnd()); _degree.zerofill(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.isRSite(i) || mol.isPseudoAtom(i) || mol.isTemplateAtom(i)) _hcount[i] = 0; else _hcount[i] = mol.getImplicitH_NoThrow(i, -1); if (_hcount[i] < 0) { if (mol.getAtomAromaticity(i) == ATOM_AROMATIC) { if (mol.getAtomNumber(i) == ELEM_C && mol.getAtomCharge(i) == 0) { if (mol.getVertex(i).degree() == 3) _hcount[i] = 0; else if (mol.getVertex(i).degree() == 2) _hcount[i] = 1; } else if (mol.getAtomNumber(i) == ELEM_O && mol.getAtomCharge(i) == 0) _hcount[i] = 0; else { if (!allow_undefined) // This code will throw an error with a good explanation _hcount[i] = mol.getImplicitH(i); else // Make number of hydrogens unique in order to make such atoms unique _hcount[i] = 101 + i; } } else { // Number of atoms are underfined, but all the properties like // connectivity, charge, and etc., and this mean that such // atoms are comparable even. // For example, this cis-trans bond is invalid even if the number // of hydrogens are undefined: CC=C(N(C)=O)N(C)=O _hcount[i] = 100; // Any big number. // Later this number can be increased including neighbour hydrogens, // and this is correct, because nitrogens in these molecules are different: // C[N](C)=O and [H][N]([H])(C)(C)=O } } const Vertex &vertex = mol.getVertex(i); _degree[i] = 0; if (ignored_vertices != 0 && ignored_vertices[i]) continue; for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { if (mol.getAtomNumber(vertex.neiVertex(j)) == ELEM_H && mol.getAtomIsotope(vertex.neiVertex(j)) == 0) _hcount[i]++; if (ignored_vertices == 0 || ignored_vertices[vertex.neiVertex(j)] == 0) _degree[i]++; } } // Compute independent components if the canonical ordering is not required _independent_component_index.clear_resize(mol.vertexEnd()); if (!find_canonical_ordering) { // We can mark different connected components as independent GraphDecomposer decomposer(mol); decomposer.decompose(); _independent_component_index.copy(decomposer.getDecomposition()); } else _independent_component_index.fffill(); } void MoleculeAutomorphismSearch::_markValidOrInvalidStereo (bool find_valid, Array<int> &orbits, bool *found) { Molecule &mol = *(Molecule *)_given_graph; // If all substituents are belong to the different orbits then // such stereocenter is valid // If some substituents are belong to the one orbit and has one // degree then such stereocenter is invalid for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (mol.cis_trans.getParity(i) == 0) continue; int validity = _validCisTransBond(i, orbits); if (validity == _UNDEF) continue; if (find_valid == (validity == _VALID)) { if (_cistrans_bond_state[i] != validity) { _cistrans_bond_state[i] = validity; if (found != 0) *found = true; } } } const MoleculeStereocenters &stereocenters = mol.stereocenters; for (int i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int validity = _validStereocenter(i, orbits); if (validity == _UNDEF) continue; if (find_valid == (validity == _VALID)) { int atom_index = stereocenters.getAtomIndex(i); if (_stereocenter_state[atom_index] != validity) { _stereocenter_state[atom_index] = validity; if (found != 0) *found = true; } } } } int MoleculeAutomorphismSearch::_getStereo (int state) const { if (state == _NO_STEREO) return _INVALID; if (state != _UNDEF) return state; if (_treat_undef_as != -1) return _treat_undef_as; return state; } void MoleculeAutomorphismSearch::_automorphismCallback (const int *automorphism, void *context) { MoleculeAutomorphismSearch &self = *(MoleculeAutomorphismSearch *)context; Molecule &mol = *(Molecule *)self._given_graph; const MoleculeStereocenters &stereocenters = mol.stereocenters; if (self._target_stereocenter != -1) if (!_isStereocenterMappedRigid(stereocenters, self._target_stereocenter, automorphism)) self._target_stereocenter_parity_inv = true; if (self._target_bond != -1) if (!_isCisTransBondMappedRigid(mol, self._target_bond, automorphism)) self._target_bond_parity_inv = true; } bool MoleculeAutomorphismSearch::_isStereocenterMappedRigid (const MoleculeStereocenters &stereocenters, int i, const int *mapping) { int idx, type, group; int pyramid[4]; int j; stereocenters.get(i, idx, type, group, pyramid); if (mapping[idx] == -1) return true; int size = 0; for (j = 0; j < 4; j++) if (pyramid[j] >= 0) { if (mapping[pyramid[j]] >= 0) size++; else pyramid[j] = -1; } if (size < 3) return true; if (type < MoleculeStereocenters::ATOM_AND) return true; if (stereocenters.getType(mapping[idx]) != type) throw Error("internal: stereocenter types mismatch"); int pyra_map[4]; MoleculeStereocenters::getPyramidMapping(stereocenters, stereocenters, idx, mapping, pyra_map, false); if (!MoleculeStereocenters::isPyramidMappingRigid(pyra_map)) return false; return true; } bool MoleculeAutomorphismSearch::_isCisTransBondMappedRigid (Molecule &mol, int i, const int *mapping) { int parity = mol.cis_trans.getParity(i); int parity2 = MoleculeCisTrans::applyMapping(parity, mol.cis_trans.getSubstituents(i), mapping, false); const Edge &edge = mol.getEdge(i); int i2 = mol.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); if (mol.cis_trans.getParity(i2) != parity2) return false; return true; } bool MoleculeAutomorphismSearch::_findInvalidStereo (Molecule &mol) { bool invalid_found = false; // Mark stereocenters that are invalid _treat_undef_as = _VALID; /* Optimization is disabled because it is impossible to detect * stereocenter or stereobond validity only by orbits. To detect * validity on this step automorphism group should be investigated. AutomorphismSearch::process(mol); QS_DEF(Array<int>, orbits_stereo_restricted); getOrbits(orbits_stereo_restricted); _markValidOrInvalidStereo(false, orbits_stereo_restricted, &invalid_found); */ // Check each possible stereocenters to be valid QS_DEF(Array<int>, invalid_stereo); // // Step 1: find valid and invalid stereocenters // QS_DEF(Array<int>, stereocenters_to_restore); invalid_stereo.clear(); _target_bond = -1; MoleculeStereocenters &stereocenters = mol.stereocenters; for (int i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int atom_index = stereocenters.getAtomIndex(i); if (ignored_vertices != 0 && ignored_vertices[atom_index]) continue; if (_stereocenter_state[atom_index] != _UNDEF) continue; _stereocenter_state[atom_index] = _INVALID; _target_stereocenter = i; _fixed_atom = atom_index; _target_stereocenter_parity_inv = false; // If AND or OR stereogroup is fixed then mark such stereogroup as ABS int type = stereocenters.getType(atom_index); int group = stereocenters.getGroup(atom_index); stereocenters_to_restore.clear(); if (type == MoleculeStereocenters::ATOM_AND || type == MoleculeStereocenters::ATOM_OR) { // Mark whole group as absolute for (int j = stereocenters.begin(); j != stereocenters.end(); j = stereocenters.next(j)) { int atom_index2 = stereocenters.getAtomIndex(j); if (atom_index2 == atom_index) continue; if (stereocenters.getGroup(atom_index) == stereocenters.getGroup(atom_index2) && stereocenters.getType(atom_index2) == type) { stereocenters.setType(atom_index2, MoleculeStereocenters::ATOM_ABS, -1); stereocenters_to_restore.push(atom_index2); } } } AutomorphismSearch::process(mol); if (_target_stereocenter_parity_inv) // Stereocenter is invalid invalid_stereo.push(atom_index); else _stereocenter_state[atom_index] = _UNDEF; for (int j = 0; j < stereocenters_to_restore.size(); j++) { int atom = stereocenters_to_restore[j]; stereocenters.setType(atom, type, group); } } // Mark invalid stereocenters for (int i = 0; i < invalid_stereo.size(); i++) { int atom_index = invalid_stereo[i]; _stereocenter_state[atom_index] = _INVALID; invalid_found = true; } _target_stereocenter = -1; // // Step 2: find valid and invalid cis-trans bonds // invalid_found |= _findInvalidStereoCisTrans(mol); return invalid_found; } bool MoleculeAutomorphismSearch::_findInvalidStereoCisTrans (Molecule &mol) { _treat_undef_as = _VALID; QS_DEF(Array<int>, invalid_stereo); invalid_stereo.clear(); bool invalid_found = false; for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (_cistrans_bond_state[i] != _UNDEF || mol.cis_trans.getParity(i) == 0) continue; if (ignored_vertices != 0) { const Edge &edge = mol.getEdge(i); if (ignored_vertices[edge.beg] || ignored_vertices[edge.end]) continue; } _cistrans_bond_state[i] = _INVALID; if (_checkCisTransInvalid(mol, i)) // Stereobond is invalid invalid_stereo.push(i); _cistrans_bond_state[i] = _UNDEF; } // Mark invalid stereocenters for (int i = 0; i < invalid_stereo.size(); i++) { int bond_index = invalid_stereo[i]; _cistrans_bond_state[bond_index] = _INVALID; invalid_found = true; } return invalid_found; } bool MoleculeAutomorphismSearch::_checkCisTransInvalid (Molecule &mol, int bond_idx) { _target_bond = bond_idx; _target_bond_parity_inv = false; _fixed_atom = mol.getEdge(bond_idx).beg; AutomorphismSearch::process(mol); _target_bond = -1; _fixed_atom = -1; // If _target_bond_parity_inv is true then stereobond is invalid return _target_bond_parity_inv; } void MoleculeAutomorphismSearch::_markComplicatedStereocentersAsValid (Molecule &mol) { // If there is more then 1 unknown stereocenter in the biconnected // component then validity of such stereocenters cannot be found // with current methods. For example C[C@H]1C[C@@H](C)C[C@H](C)C1 // can lead to CC1C[C@H](C)C[C@@H](C)C1 or CC1C[C@@H](C)C[C@H](C)C1, // but canonical codes are different for such molecules. QS_DEF(Array<int>, single_bond_bridge_mark); single_bond_bridge_mark.resize(mol.edgeEnd()); single_bond_bridge_mark.fill(1); SpanningTree sp_tree(mol, 0); sp_tree.markAllEdgesInCycles(single_bond_bridge_mark.ptr(), 0); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i++) if (mol.getBondOrder(i) != BOND_SINGLE) single_bond_bridge_mark[i] = 0; Filter edge_filter(single_bond_bridge_mark.ptr(), Filter::NEQ, 1); GraphDecomposer decomposer(mol); decomposer.decompose(0, &edge_filter); const Array<int> &decomposition = decomposer.getDecomposition(); QS_DEF(Array<int>, undef_stereo_in_component); undef_stereo_in_component.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int comp = decomposition[v]; if (comp < 0) continue; while (undef_stereo_in_component.size() <= comp) undef_stereo_in_component.push(0); if (_stereocenter_state[v] == _UNDEF) undef_stereo_in_component[comp]++; } // Mark stereocenters as valid if there are more then 1 // undefined stereocenter in the component for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int comp = decomposition[v]; if (comp < 0) continue; if (_stereocenter_state[v] == _UNDEF && undef_stereo_in_component[comp] > 1) _stereocenter_state[v] = _VALID; } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cdx_loader.cpp��������������������������������������������0000664�0000000�0000000�00000041330�12710376503�0023273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "molecule/molecule_scaffold_detection.h" #include "molecule/molecule_cdx_loader.h" using namespace indigo; IMPL_ERROR(MoleculeCdxLoader, "molecule CDX loader"); CP_DEF(MoleculeCdxLoader); MoleculeCdxLoader::MoleculeCdxLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(properties), TL_CP_GET(_nodes), TL_CP_GET(_bonds), TL_CP_GET(_stereo_care_atoms), TL_CP_GET(_stereo_care_bonds), TL_CP_GET(_stereocenter_types), TL_CP_GET(_stereocenter_groups), TL_CP_GET(_sensible_bond_directions), TL_CP_GET(_ignore_cistrans), COORD_COEF(1.0/1857710.0) { _scanner = &scanner; } void MoleculeCdxLoader::loadMolecule (Molecule &mol) { mol.clear(); _nodes.clear(); _bonds.clear(); _bmol = &mol; _mol = &mol; if (_scanner != 0) { _checkHeader(); _loadMolecule(); } } void MoleculeCdxLoader::_checkHeader () { int pos_saved = _scanner->tell(); if ((_scanner->length() - pos_saved) < 8) return; char id[8]; _scanner->readCharsFix(8, id); if (strncmp(id, kCDX_HeaderString, kCDX_HeaderStringLen) == 0) { _scanner->seek(kCDX_HeaderLength - kCDX_HeaderStringLen, SEEK_CUR); } else { _scanner->seek(pos_saved, SEEK_SET); } } void MoleculeCdxLoader::_loadMolecule () { UINT16 tag; UINT16 size; UINT32 id; int level = 1; while (!_scanner->isEOF()) { tag = _scanner->readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner->readBinaryDword(); if (tag == kCDXObj_Fragment) { _readFragment(id); } else if ((tag == kCDXObj_Graphic) || (tag == kCDXObj_Text) || (tag == kCDXObj_BracketedGroup) || (tag == kCDXObj_BracketAttachment) || (tag == kCDXObj_CrossingBond) || (tag == kCDXObj_ReactionStep) || (tag == kCDXObj_Curve) || (tag == kCDXObj_EmbeddedObject)) { _skipObject(); } else if ((tag == kCDXObj_Page) || (tag == kCDXObj_Group)) { level++; } else { level++; } } else if (tag == 0) { level--; } else { size = _scanner->readBinaryWord(); switch (tag) { case kCDXProp_Name: _scanner->seek (size, SEEK_CUR); break; default: _scanner->seek (size, SEEK_CUR); break; } } if (level == 0) break; } int idx; RedBlackMap<int, int> _atom_mapping; for (int i = 0; i < _nodes.size(); i++) { if (_nodes[i].type == kCDXNodeType_Element) { idx = _mol->addAtom(_nodes[i].label); _mol->setAtomCharge_Silent(idx, _nodes[i].charge); _mol->setAtomIsotope(idx, _nodes[i].isotope); _mol->setAtomRadical(idx, _nodes[i].radical); // _mol->setExplicitValence(idx, _nodes[i].valence); _bmol->setAtomXyz(idx, (float)_nodes[i].x*COORD_COEF, (float)_nodes[i].y*COORD_COEF, (float)_nodes[i].z*COORD_COEF); _nodes[i].index = idx; _atom_mapping.insert(_nodes[i].id, i); } else if (_nodes[i].type == kCDXNodeType_ExternalConnectionPoint) { _atom_mapping.insert(_nodes[i].id, i); } else { _atom_mapping.insert(_nodes[i].id, i); } } for (int i = 0; i < _bonds.size(); i++) { if ((_nodes[_atom_mapping.at(_bonds[i].beg)].type == kCDXNodeType_Element) && (_nodes[_atom_mapping.at(_bonds[i].end)].type == kCDXNodeType_Element)) { if (_bonds[i].swap_bond) _bonds[i].index = _mol->addBond_Silent(_nodes[_atom_mapping.at(_bonds[i].end)].index, _nodes[_atom_mapping.at(_bonds[i].beg)].index, _bonds[i].type); else _bonds[i].index = _mol->addBond_Silent(_nodes[_atom_mapping.at(_bonds[i].beg)].index, _nodes[_atom_mapping.at(_bonds[i].end)].index, _bonds[i].type); if (_bonds[i].dir > 0) _bmol->setBondDirection(_bonds[i].index, _bonds[i].dir); } else if (_nodes[_atom_mapping.at(_bonds[i].beg)].type == kCDXNodeType_ExternalConnectionPoint) { _updateConnectionPoint(_bonds[i].beg, _bonds[i].end); } else if (_nodes[_atom_mapping.at(_bonds[i].end)].type == kCDXNodeType_ExternalConnectionPoint) { _updateConnectionPoint(_bonds[i].end, _bonds[i].beg); } else if (_nodes[_atom_mapping.at(_bonds[i].beg)].type == kCDXNodeType_Fragment) { int beg = 0; int end = 0; for (int j = 0; j < _nodes[_atom_mapping.at(_bonds[i].beg)].connections.size(); j++) { if (_nodes[_atom_mapping.at(_bonds[i].beg)].connections[j].bond_id == _bonds[i].id) { beg = _nodes[_atom_mapping.at(_bonds[i].beg)].connections[j].atom_id; break; } } for (int j = 0; j < _nodes[_atom_mapping.at(_bonds[i].end)].connections.size(); j++) { if (_nodes[_atom_mapping.at(_bonds[i].end)].connections[j].bond_id == _bonds[i].id) { end = _nodes[_atom_mapping.at(_bonds[i].end)].connections[j].atom_id; break; } } if (beg != 0 && end != 0) { _bonds[i].index = _mol->addBond_Silent(_nodes[_atom_mapping.at(beg)].index, _nodes[_atom_mapping.at(end)].index, _bonds[i].type); if (_bonds[i].dir > 0) _bmol->setBondDirection(_bonds[i].index, _bonds[i].dir); } } } _postLoad(); } void MoleculeCdxLoader::_updateConnectionPoint (int point_id, int atom_id) { for (int i = 0; i < _nodes.size(); i++) { if (_nodes[i].type == kCDXNodeType_Fragment) { for (int j = 0; j < _nodes[i].connections.size(); j++) { if (_nodes[i].connections[j].point_id == point_id) { _nodes[i].connections[j].atom_id = atom_id; break; } } } } } void MoleculeCdxLoader::_postLoad () { _sensible_bond_directions.clear_resize(_bonds.size()); _sensible_bond_directions.zerofill(); _ignore_cistrans.clear_resize(_bonds.size()); _ignore_cistrans.zerofill(); _bmol->stereocenters.buildFromBonds(stereochemistry_options, _sensible_bond_directions.ptr()); _bmol->allene_stereo.buildFromBonds(stereochemistry_options.ignore_errors, _sensible_bond_directions.ptr()); _bmol->cis_trans.build(_ignore_cistrans.ptr()); _bmol->have_xyz = true; } void MoleculeCdxLoader::_readFragment (UINT32 fragment_id) { UINT16 tag; UINT16 size; UINT32 id; int level = 1; while (!_scanner->isEOF()) { tag = _scanner->readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner->readBinaryDword(); if (tag == kCDXObj_Fragment) { _readFragment(id); } else if (tag == kCDXObj_Node) { _readNode(id); } else if (tag == kCDXObj_Bond) { _readBond(id); } else if ((tag == kCDXObj_Graphic) || (tag == kCDXObj_Text)) { _skipObject(); } else { _skipObject(); } } else if (tag == 0) { level--; } else { size = _scanner->readBinaryWord(); _NodeDesc *node = 0; switch (tag) { case kCDXProp_Frag_ConnectionOrder: node = &_nodes.top(); _getConnectionOrder(size, node->connections); break; default: _scanner->seek (size, SEEK_CUR); break; } } if (level == 0) return; } } void MoleculeCdxLoader::_readNode (UINT32 node_id) { UINT16 tag; UINT16 size; UINT32 id; int node_type = 0; int level = 1; _NodeDesc &node = _nodes.push(); memset(&node, 0, sizeof(_NodeDesc)); node.id = node_id; node.type = kCDXNodeType_Element; node.label = ELEM_C; node.hydrogens = -1; node.valence = -1; node.radical = -1; while (!_scanner->isEOF()) { tag = _scanner->readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner->readBinaryDword(); if (tag == kCDXObj_Fragment) { _readFragment(id); } else if (tag == kCDXObj_Group) { } else if ((tag == kCDXObj_Graphic) || (tag == kCDXObj_Text) || (tag == kCDXObj_ObjectTag)) { _skipObject(); } else { _skipObject(); } } else if (tag == 0) { level--; } else { size = _scanner->readBinaryWord(); switch (tag) { case kCDXProp_Atom_NumHydrogens: node.hydrogens = _scanner->readBinaryWord(); break; case kCDXProp_2DPosition: _read2DPosition(node.x, node.y); break; case kCDXProp_3DPosition: _read3DPosition(node.x, node.y, node.z); break; case kCDXProp_Node_Element: node.label = _getElement(); break; case kCDXProp_Atom_Charge: node.charge = _getCharge(size); break; case kCDXProp_Node_Type: node.type = _scanner->readBinaryWord(); break; case kCDXProp_Atom_Isotope: node.isotope = _scanner->readBinaryWord();; break; case kCDXProp_Atom_Radical: node.radical = _getRadical(); break; case kCDXProp_Atom_BondOrdering: _getBondOrdering(size, node.connections); break; // case kCDXProp_Atom_EnhancedStereoType: // node.enchanced_stereo = _scanner->readByte(); // case kCDXProp_Atom_EnhancedStereoGroupNum: // node.stereo_group = _scanner->readBinaryWord(); case kCDXProp_Atom_CIPStereochemistry: node.stereo = _scanner->readByte(); break; case kCDXProp_Atom_ElementList: case kCDXProp_Atom_AbnormalValence: case kCDXProp_Name: case kCDXProp_IgnoreWarnings: case kCDXProp_ChemicalWarning: _scanner->seek (size, SEEK_CUR); break; case kCDXProp_MarginWidth: case kCDXProp_ZOrder: case kCDXProp_Atom_GenericNickname: case kCDXProp_Atom_Geometry: case kCDXProp_Node_LabelDisplay: case kCDXProp_LabelStyle: case kCDXProp_ForegroundColor: case kCDXProp_BackgroundColor: _scanner->seek (size, SEEK_CUR); break; default: _scanner->seek (size, SEEK_CUR); break; } } if (level == 0) { switch (node.type) { case kCDXNodeType_Fragment: break; case kCDXNodeType_Element: break; default: break; } return; } } } void MoleculeCdxLoader::_read2DPosition (int &x, int &y) { y = _scanner->readBinaryDword(); x = _scanner->readBinaryDword(); } void MoleculeCdxLoader::_read3DPosition (int &x, int &y, int &z) { z = _scanner->readBinaryDword(); y = _scanner->readBinaryDword(); x = _scanner->readBinaryDword(); } int MoleculeCdxLoader::_getElement () { return _scanner->readBinaryWord(); } int MoleculeCdxLoader::_getCharge (int size) { if (size == 4) return _scanner->readBinaryDword(); else return _scanner->readByte(); } int MoleculeCdxLoader::_getRadical () { return _scanner->readByte(); } void MoleculeCdxLoader::_getBondOrdering(int size, Array<_ExtConnection> &connections) { int nbonds = size/sizeof(UINT32); connections.clear(); for (int i = 0; i < nbonds; i++) { _ExtConnection &conn = connections.push(); conn.bond_id = _scanner->readBinaryDword(); conn.point_id = 0; conn.atom_id = 0; } } void MoleculeCdxLoader::_getConnectionOrder(int size, Array<_ExtConnection> &connections) { int npoints = size/sizeof(UINT32); if (npoints != connections.size() ) { _scanner->seek (size, SEEK_CUR); return; } for (int i = 0; i < npoints; i++) { connections[i].point_id = _scanner->readBinaryDword(); } } void MoleculeCdxLoader::_readBond (UINT32 bond_id) { UINT16 tag; UINT16 size; UINT32 id; int level = 1; _BondDesc &bond = _bonds.push(); memset(&bond, 0, sizeof(_BondDesc)); bond.id = bond_id; bond.type = BOND_SINGLE; while (!_scanner->isEOF()) { tag = _scanner->readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner->readBinaryDword(); _skipObject(); } else if (tag == 0) { level--; } else { size = _scanner->readBinaryWord(); switch (tag) { case kCDXProp_Bond_Begin: bond.beg = _scanner->readBinaryDword(); break; case kCDXProp_Bond_End: bond.end = _scanner->readBinaryDword(); break; case kCDXProp_Bond_Order: bond.type = _getBondType(); break; case kCDXProp_Bond_Display: bond.dir = _getBondDirection(bond.swap_bond); break; case kCDXProp_BondLength: case kCDXProp_Bond_CIPStereochemistry: bond.stereo = _scanner->readByte(); break; case kCDXProp_Bond_BeginAttach: case kCDXProp_Bond_EndAttach: case kCDXProp_ChemicalWarning: _scanner->seek (size, SEEK_CUR); break; default: _scanner->seek (size, SEEK_CUR); break; } } if (level == 0) { return; } } } int MoleculeCdxLoader::_getBondType () { UINT16 order; int type = BOND_SINGLE; order = _scanner->readBinaryWord(); switch (order) { case kCDXBondOrder_Single: type = BOND_SINGLE; break; case kCDXBondOrder_Double: type = BOND_DOUBLE; break; case kCDXBondOrder_Triple: type = BOND_TRIPLE; break; case kCDXBondOrder_OneHalf: type = BOND_AROMATIC; break; default: type = BOND_SINGLE; break; } return type; } int MoleculeCdxLoader::_getBondDirection (bool &swap_bond) { UINT16 display; int direction = 0; display = _scanner->readBinaryWord(); switch (display) { case kCDXBondDisplay_WedgedHashBegin: direction = BOND_DOWN; swap_bond = false; break; case kCDXBondDisplay_WedgedHashEnd: direction = BOND_DOWN; swap_bond = true; break; case kCDXBondDisplay_WedgeBegin: direction = BOND_UP; swap_bond = false; break; case kCDXBondDisplay_WedgeEnd: direction = BOND_UP; swap_bond = true; break; case kCDXBondDisplay_Wavy: direction = BOND_EITHER; break; default: direction = 0; break; } return direction; } void MoleculeCdxLoader::_skipObject () { UINT16 tag; UINT16 size; UINT32 id; int level = 1; while (!_scanner->isEOF()) { tag = _scanner->readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner->readBinaryDword(); _skipObject(); } else if (tag == 0) { level--; } else { size = _scanner->readBinaryWord(); _scanner->seek (size, SEEK_CUR); } if (level == 0) return; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cdx_saver.cpp���������������������������������������������0000664�0000000�0000000�00000002402�12710376503�0023142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "molecule/molecule_cdx_saver.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "base_cpp/locale_guard.h" using namespace indigo; IMPL_ERROR(MoleculeCdxSaver, "molecule CDX saver"); MoleculeCdxSaver::MoleculeCdxSaver (Output &output) : _output(output) { } void MoleculeCdxSaver::saveMolecule (Molecule &mol) { LocaleGuard locale_guard; int i; _mol = &mol; if (_mol->name.ptr() != 0) { } bool have_hyz = _mol->have_xyz; bool have_z = BaseMolecule::hasZCoord(*_mol); if (_mol->vertexCount() > 0) { } if (_mol->edgeCount() > 0) { } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cdxml_saver.cpp�������������������������������������������0000664�0000000�0000000�00000041015�12710376503�0023476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "molecule/molecule_cdxml_saver.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "base_cpp/locale_guard.h" #include "tinyxml.h" using namespace indigo; IMPL_ERROR(MoleculeCdxmlSaver, "molecule CMXML saver"); MoleculeCdxmlSaver::MoleculeCdxmlSaver (Output &output) : _output(output) { _bond_length = BOND_LENGTH; _max_page_height = 64; _pages_height = 1; _doc = new TiXmlDocument(); _root = new TiXmlElement("CDXML"); } float MoleculeCdxmlSaver::pageHeight () const { return _max_page_height; } float MoleculeCdxmlSaver::textLineHeight () const { return 12.75f / _bond_length; } void MoleculeCdxmlSaver::beginDocument (Bounds *bounds) { TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "UTF-8", ""); _doc->LinkEndChild(decl); TiXmlUnknown * doctype = new TiXmlUnknown(); doctype->SetValue("!DOCTYPE CDXML SYSTEM \"http://www.cambridgesoft.com/xml/cdxml.dtd\" "); _doc->LinkEndChild(doctype); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f", _bond_length); buf.push(0); _root->SetAttribute("_bond_length", buf.ptr()); _doc->LinkEndChild(_root); if (bounds != NULL) { // Generate MacPrintInfo according to the size // http://www.cambridgesoft.com/services/documentation/sdk/chemdraw/cdx/properties/MacPrintInfo.htm int dpi_logical = 72; int dpi_print = 600; float x_inch = bounds->max.x * _bond_length / dpi_logical + 1; float y_inch = bounds->max.y * _bond_length / dpi_logical + 1; int width = (int)(x_inch * dpi_print); int height = (int)(y_inch * dpi_print); // Add 1 to compensate margins = 36 points = 0.5 inches int max_height = (int)((_max_page_height * _bond_length / dpi_logical + 1) * dpi_print); if (height > max_height) { _pages_height = (int)ceil((float)height / max_height); height = max_height; } int mac_print_info[60] = {0}; mac_print_info[0] = 3; // magic number mac_print_info[2] = dpi_print; mac_print_info[3] = dpi_print; mac_print_info[6] = height; mac_print_info[7] = width; mac_print_info[10] = height; mac_print_info[11] = width; mac_print_info[12] = 871; // magic number mac_print_info[13] = height / 5; // magic scaling coeffient mac_print_info[14] = width / 5; mac_print_info[24] = 100; // horizontal scale, in percent mac_print_info[25] = 100; // Vertical scale, in percent _root->SetAttribute("PrintMargins", "36 36 36 36"); buf.clear(); for (int i = 0; i < NELEM(mac_print_info); i++) { out.printf("%04hx", (unsigned short)mac_print_info[i]); } buf.push(0); _root->SetAttribute("MacPrintInfo", buf.ptr()); } _current = _root; } void MoleculeCdxmlSaver::beginPage (Bounds *bounds) { _page = new TiXmlElement("page"); _root->LinkEndChild(_page); _page->SetAttribute("HeightPages", _pages_height); _page->SetAttribute("WidthPages", 1); _current = _page; } void MoleculeCdxmlSaver::addFontTable(const char* font) { if (font != NULL && strlen(font) > 0) { _fonttable = new TiXmlElement("fonttable"); _root->LinkEndChild(_fonttable); TiXmlUnknown * f = new TiXmlUnknown(); _fonttable->LinkEndChild(f); QS_DEF(Array<char>, buf); ArrayOutput out(buf); buf.readString(&font[1], false); buf.remove(buf.size()-1); buf.push(0); f->SetValue(buf.ptr()); } } void MoleculeCdxmlSaver::addFontToTable(int id, const char* charset, const char* name) { TiXmlElement * font = new TiXmlElement("font"); _fonttable->LinkEndChild(font); if (id > 0) font->SetAttribute("id", id); font->SetAttribute("charset", charset); font->SetAttribute("name", name); } void MoleculeCdxmlSaver::addColorTable(const char* color) { if (color != NULL && strlen(color) > 0) { _colortable = new TiXmlElement("colortable"); _root->LinkEndChild(_colortable); addColorToTable(-1, 1, 1, 1); addColorToTable(-1, 0, 0, 0); addColorToTable(-1, 1, 0, 0); addColorToTable(-1, 1, 1, 0); addColorToTable(-1, 0, 1, 0); addColorToTable(-1, 0, 1, 1); addColorToTable(-1, 0, 0, 1); addColorToTable(-1, 1, 0, 1); TiXmlUnknown * c = new TiXmlUnknown(); _colortable->LinkEndChild(c); QS_DEF(Array<char>, buf); ArrayOutput out(buf); buf.readString(&color[1], false); buf.remove(buf.size()-1); buf.push(0); c->SetValue(buf.ptr()); } } void MoleculeCdxmlSaver::addColorToTable(int id, int r, int g, int b) { TiXmlElement * color = new TiXmlElement("color"); _colortable->LinkEndChild(color); if (id > 0) color->SetAttribute("id", id); color->SetAttribute("r", r); color->SetAttribute("g", g); color->SetAttribute("b", b); } void MoleculeCdxmlSaver::saveMoleculeFragment (Molecule &mol, const Vec2f &offset, float structure_scale) { float scale = structure_scale * _bond_length; LocaleGuard locale_guard; TiXmlElement * parent = _current; TiXmlElement * fragment = new TiXmlElement("fragment"); _current->LinkEndChild(fragment); _current = fragment; bool have_hyz = mol.have_xyz; Vec2f min_coord, max_coord; if (mol.vertexCount() > 0) { for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { int atom_number = mol.getAtomNumber(i); int charge = mol.getAtomCharge(i); int radical = 0; TiXmlElement * node = new TiXmlElement("n"); fragment->LinkEndChild(node); if (mol.isRSite(i)) { node->SetAttribute("id", i + 1); node->SetAttribute("NodeType", "GenericNickname"); node->SetAttribute("GenericNickname", "A"); if (charge != 0) node->SetAttribute("Charge", charge); } else if (mol.isPseudoAtom(i)) { node->SetAttribute("id", i + 1); node->SetAttribute("NodeType", "GenericNickname"); node->SetAttribute("GenericNickname", mol.getPseudoAtom(i)); if (charge != 0) node->SetAttribute("Charge", charge); } else { node->SetAttribute("id", i + 1); node->SetAttribute("Element", atom_number); if (charge != 0) node->SetAttribute("Charge", charge); if (mol.getAtomIsotope(i) != 0) node->SetAttribute("Isotope", mol.getAtomIsotope(i)); radical = mol.getAtomRadical_NoThrow(i, 0); if (radical != 0) { const char *radical_str = NULL; if (radical == RADICAL_DOUBLET) radical_str = "Doublet"; else if (radical == RADICAL_SINGLET) radical_str = "Singlet"; else throw Error("Radical type %d is not supported", radical); node->SetAttribute("Radical", radical_str); } if (Molecule::shouldWriteHCount(mol, i)) { int hcount; try { hcount = mol.getAtomTotalH(i); } catch (Exception &) { hcount = -1; } if (hcount >= 0) node->SetAttribute("NumHydrogens", hcount); } } Vec3f pos3 = mol.getAtomXyz(i); Vec2f pos(pos3.x, pos3.y); pos.add(offset); if (i == mol.vertexBegin()) min_coord = max_coord = pos; else { min_coord.min(pos); max_coord.max(pos); } pos.scale(scale); if (have_hyz) { QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", pos.x, -pos.y); buf.push(0); node->SetAttribute("p", buf.ptr()); } else { if (mol.stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY) { node->SetAttribute("Geometry", "Tetrahedral"); const int *pyramid = mol.stereocenters.getPyramid(i); // 0 means atom absence QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%d %d %d %d", pyramid[0] + 1, pyramid[1] + 1, pyramid[2] + 1, pyramid[3] + 1); buf.push(0); node->SetAttribute("BondOrdering", buf.ptr()); } } if (mol.getVertex(i).degree() == 0 && atom_number == ELEM_C && charge == 0 && radical == 0) { TiXmlElement * t = new TiXmlElement("t"); node->LinkEndChild(t); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", pos.x, -pos.y); buf.push(0); t->SetAttribute("p",buf.ptr()); t->SetAttribute("Justification", "Center"); TiXmlElement * s = new TiXmlElement("s"); t->LinkEndChild(s); s->SetAttribute("font", 3); s->SetAttribute("size", 10); s->SetAttribute("face", 96); TiXmlText * txt = new TiXmlText("CH4"); s->LinkEndChild(txt); } else if (mol.isRSite(i)) { TiXmlElement * t = new TiXmlElement("t"); node->LinkEndChild(t); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", pos.x, -pos.y); buf.push(0); t->SetAttribute("p", buf.ptr()); t->SetAttribute("LabelJustification", "Left"); TiXmlElement * s = new TiXmlElement("s"); t->LinkEndChild(s); s->SetAttribute("font", 3); s->SetAttribute("size", 10); s->SetAttribute("face", 96); out.clear(); out.printf("A"); /* * Skip charge since Chemdraw is pure. May be in future it will be fixed by Chemdraw */ /*if (charge != 0) { if (charge > 0) { out.printf("+%d", charge); } else { out.printf("-%d", charge); } }*/ buf.push(0); TiXmlText * txt = new TiXmlText(buf.ptr()); s->LinkEndChild(txt); } else if (mol.isPseudoAtom(i)) { TiXmlElement * t = new TiXmlElement("t"); node->LinkEndChild(t); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", pos.x, -pos.y); buf.push(0); t->SetAttribute("p", buf.ptr()); t->SetAttribute("LabelJustification", "Left"); TiXmlElement * s = new TiXmlElement("s"); t->LinkEndChild(s); s->SetAttribute("font", 3); s->SetAttribute("size", 10); s->SetAttribute("face", 96); out.clear(); out.printf("%s", mol.getPseudoAtom(i)); /* * Skip charge since Chemdraw is pure. May be in future it will be fixed by Chemdraw */ /*if (charge != 0) { if (charge > 0) { out.printf("+%d", charge); } else { out.printf("-%d", charge); } }*/ buf.push(0); TiXmlText * txt = new TiXmlText(buf.ptr()); s->LinkEndChild(txt); } } } if (mol.edgeCount() > 0) { for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); TiXmlElement * bond = new TiXmlElement("b"); fragment->LinkEndChild(bond); bond->SetAttribute("B", edge.beg + 1); bond->SetAttribute("E", edge.end + 1); int order = mol.getBondOrder(i); if (order == BOND_DOUBLE || order == BOND_TRIPLE) bond->SetAttribute("Order", order); else if (order == BOND_AROMATIC) { bond->SetAttribute("Order", "1.5"); bond->SetAttribute("Display", "Dash"); bond->SetAttribute("Display2", "Dash"); } else ; // Do not write single bond order int dir = mol.getBondDirection(i); int parity = mol.cis_trans.getParity(i); if (mol.have_xyz && (dir == BOND_UP || dir == BOND_DOWN)) { bond->SetAttribute("Display", (dir == BOND_UP) ? "WedgeBegin" : "WedgedHashBegin"); } else if (!mol.have_xyz && parity != 0) { const int *subst = mol.cis_trans.getSubstituents(i); int s3 = subst[2] + 1, s4 = subst[3] + 1; if (parity == MoleculeCisTrans::TRANS) { int tmp; __swap(s3, s4, tmp); } QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%d %d %d %d", subst[0] + 1, subst[1] + 1, s3, s4); buf.push(0); bond->SetAttribute("BondCircularOrdering", buf.ptr()); } } } if (mol.isChrial()) { Vec2f chiral_pos(max_coord.x, max_coord.y); Vec2f bbox(scale * chiral_pos.x, -scale * chiral_pos.y); TiXmlElement * graphic = new TiXmlElement("graphic"); fragment->LinkEndChild(graphic); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f %f %f", bbox.x, bbox.y, bbox.x, bbox.y); buf.push(0); graphic->SetAttribute("BoundingBox", buf.ptr()); graphic->SetAttribute("GraphicType", "Symbol"); graphic->SetAttribute("SymbolType", "Absolute"); graphic->SetAttribute("FrameType", "None"); _current = graphic; addText(chiral_pos, "Chiral"); _current = fragment; } _current = parent; } void MoleculeCdxmlSaver::addText (const Vec2f &pos, const char *text) { addText(pos, text, "Center"); } void MoleculeCdxmlSaver::addText (const Vec2f &pos, const char *text, const char *alignment) { TiXmlElement * t = new TiXmlElement("t"); _current->LinkEndChild(t); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", _bond_length * pos.x, -_bond_length * pos.y); buf.push(0); t->SetAttribute("p", buf.ptr()); t->SetAttribute("Justification", alignment); TiXmlElement * s = new TiXmlElement("s"); t->LinkEndChild(s); TiXmlText * txt = new TiXmlText(text); s->LinkEndChild(txt); } void MoleculeCdxmlSaver::addCustomText(const Vec2f &pos, const char *alignment, float line_height, const char *text) { TiXmlElement * t = new TiXmlElement("t"); _current->LinkEndChild(t); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("%f %f", _bond_length * pos.x, -_bond_length * pos.y); buf.push(0); t->SetAttribute("p", buf.ptr()); t->SetAttribute("Justification", alignment); out.clear(); out.printf("%f", line_height); buf.push(0); t->SetAttribute("LineHeight", buf.ptr()); TiXmlUnknown * s = new TiXmlUnknown(); buf.readString(text, false); if (buf.size() > 1) { buf.remove(buf.size()-1); buf.remove(0); buf.push(0); s->SetValue(buf.ptr()); t->LinkEndChild(s); } } void MoleculeCdxmlSaver::endPage () { _current = _root; } void MoleculeCdxmlSaver::endDocument () { TiXmlPrinter printer; _doc->Accept(&printer); _output.printf("%s", printer.CStr()); } void MoleculeCdxmlSaver::saveMolecule (Molecule &mol) { Vec3f min_coord, max_coord; if (mol.have_xyz) { for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { Vec3f &pos = mol.getAtomXyz(i); if (i == mol.vertexBegin()) min_coord = max_coord = pos; else { min_coord.min(pos); max_coord.max(pos); } } // Add margins max_coord.add(Vec3f(1, 1, 1)); min_coord.sub(Vec3f(1, 1, 1)); } else { min_coord.set(0, 0, 0); max_coord.set(0, 0, 0); } beginDocument(NULL); beginPage(NULL); Vec2f offset(-min_coord.x, -max_coord.y); saveMoleculeFragment(mol, offset, 1); endPage(); endDocument(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_chain_fingerprints.cpp������������������������������������0000664�0000000�0000000�00000006374�12710376503�0025054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_chain_fingerprints.h" #include "molecule/molecule.h" #include "graph/graph_subchain_enumerator.h" #include "base_cpp/crc32.h" #include "molecule/molecule_fingerprint.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeChainFingerprintBuilder, "chain fingerprint builder"); CP_DEF(MoleculeChainFingerprintBuilder); MoleculeChainFingerprintBuilder::MoleculeChainFingerprintBuilder (Molecule &mol, const MoleculeChainFingerprintParameters ¶meters) : _mol(mol), _parameters(parameters), CP_INIT, TL_CP_GET(_fingerprint) { } void MoleculeChainFingerprintBuilder::process () { QS_DEF(Molecule, copy_without_h); QS_DEF(Array<int>, vertices); int i; vertices.clear(); // remove hydrogens for (i = _mol.vertexBegin(); i < _mol.vertexEnd(); i = _mol.vertexNext(i)) if (_mol.getAtomNumber(i) != ELEM_H) vertices.push(i); copy_without_h.makeSubmolecule(_mol, vertices, 0); GraphSubchainEnumerator gse(copy_without_h, _parameters.min_edges, _parameters.max_edges, _parameters.mode); gse.context = this; gse.cb_handle_chain = _handleChain; _fingerprint.clear_resize(_parameters.size_qwords * 8); _fingerprint.zerofill(); gse.processChains(); } void MoleculeChainFingerprintBuilder::_handleChain (Graph &graph, int size, const int *vertices, const int *edges, void *context) { MoleculeChainFingerprintBuilder *self = (MoleculeChainFingerprintBuilder *)context; QS_DEF(Array<char>, str); QS_DEF(Array<char>, rev_str); int i; str.clear(); rev_str.clear(); for (i = 0; i <= size; i++) { int number = self->_mol.getAtomNumber(vertices[i]); str.push(number & 0xFF); if (i == size) break; int order = self->_mol.getBondOrder(edges[i]); switch (order) { case BOND_SINGLE: str.push('-'); break; case BOND_DOUBLE: str.push('='); break; case BOND_TRIPLE: str.push('#'); break; default: str.push(':'); } } int len = str.size(); for (i = 0; i < len; i++) rev_str.push(str[len - i - 1]); str.push(0); rev_str.push(0); unsigned seed; unsigned seed1 = CRC32::get(str.ptr()); unsigned seed2 = CRC32::get(rev_str.ptr()); seed = __min(seed1, seed2); for (int i = 0; i < self->_parameters.bits_per_chain; i++) { seed = seed * 0x8088405 + 1; int k = seed % (self->_fingerprint.size() * 8); int nbyte = k / 8; int nbit = k % 8; self->_fingerprint[nbyte] |= (1 << nbit); } } const byte * MoleculeChainFingerprintBuilder::get () { return _fingerprint.ptr(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cis_trans.cpp���������������������������������������������0000664�0000000�0000000�00000065104�12710376503�0023161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_cis_trans.h" #include "graph/filter.h" #include "molecule/molecule.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeCisTrans, "cis-trans"); BaseMolecule & MoleculeCisTrans::_getMolecule () { char dummy[sizeof(BaseMolecule)]; int offset = (int)((char *)(&((BaseMolecule *)dummy)->cis_trans) - dummy); return *(BaseMolecule *)((char *)this - offset); } int MoleculeCisTrans::sameside (const Vec3f &beg, const Vec3f &end, const Vec3f &nei_beg, const Vec3f &nei_end) { Vec3f norm, norm_cross; Vec3f diff, norm_beg, norm_end; diff.diff(beg, end); norm_beg.diff(nei_beg, beg); // Use double cross product for getting vector lying the same plane with (beg-end) and (nei_beg-beg) norm_cross.cross(diff, norm_beg); norm.cross(norm_cross, diff); if (!norm.normalize()) return 0; //throw Error("cannot normalize stereo double bond"); norm_end.diff(nei_end, end); if (!norm_beg.normalize()) return 0; //throw Error("cannot normalize neighbor bond of stereo double bond"); if (!norm_end.normalize()) return 0; //throw Error("cannot normalize neighbor bond of stereo double bond"); float prod_beg = Vec3f::dot(norm_beg, norm); float prod_end = Vec3f::dot(norm_end, norm); if ((float)(fabs(prod_beg)) < 0.1 || (float)(fabs(prod_end)) < 0.1) return 0; //throw Error("double stereo bond is collinear with its neighbor bond"); return (prod_beg * prod_end > 0) ? 1 : -1; } int MoleculeCisTrans::_sameside (BaseMolecule &molecule, int i_beg, int i_end, int i_nei_beg, int i_nei_end) { return sameside(molecule.getAtomXyz(i_beg), molecule.getAtomXyz(i_end), molecule.getAtomXyz(i_nei_beg), molecule.getAtomXyz(i_nei_end)); } bool MoleculeCisTrans::sameline (const Vec3f &beg, const Vec3f &end, const Vec3f &nei_beg) { Vec3f norm_diff, norm_beg; norm_diff.diff(beg, end); if (!norm_diff.normalize()) return true; norm_beg.diff(nei_beg, beg); if (!norm_beg.normalize()) return true; Vec3f cross; cross.cross(norm_diff, norm_beg); float sin_angle = cross.lengthSqr(); if (fabs(sin_angle) < 0.01) return true; return false; } bool MoleculeCisTrans::_sameline (BaseMolecule &molecule, int i_beg, int i_end, int i_nei_beg) { return sameline(molecule.getAtomXyz(i_beg), molecule.getAtomXyz(i_end), molecule.getAtomXyz(i_nei_beg)); } bool MoleculeCisTrans::_pureH (BaseMolecule &mol, int idx) { return mol.getAtomNumber(idx) == ELEM_H && mol.getAtomIsotope(idx) == 0; } bool MoleculeCisTrans::_commonHasLonePair (BaseMolecule &mol, int v1, int v2) { if (v1 != -1 && v2 != -1) return false; const Vertex *v; if (v1 == -1) v = &mol.getVertex(v2); else v = &mol.getVertex(v1); int common = v->neiVertex(v->neiBegin()); if (mol.getAtomNumber(common) == ELEM_N && mol.getAtomCharge(common) == 0) return true; return false; } bool MoleculeCisTrans::convertableToImplicitHydrogen (int idx) { // check [H]\N=C\C BaseMolecule &mol = _getMolecule(); const Vertex &v = mol.getVertex(idx); int nei = v.neiVertex(v.neiBegin()); // Find double bond const Vertex &base = mol.getVertex(nei); for (int i = base.neiBegin(); i != base.neiEnd(); i = base.neiNext(i)) { int edge = base.neiEdge(i); if (mol.getBondOrder(edge) == BOND_DOUBLE) return getParity(edge) == 0 || base.degree() != 2; } return true; } bool MoleculeCisTrans::sortSubstituents (BaseMolecule &mol, int *substituents, bool *parity_changed) { bool e0 = substituents[0] < 0; bool e1 = substituents[1] < 0; bool e2 = substituents[2] < 0; bool e3 = substituents[3] < 0; if (e0 && e1) return false; if (e2 && e3) return false; bool h0 = !e0 && _pureH(mol, substituents[0]); bool h1 = !e1 && _pureH(mol, substituents[1]); bool h2 = !e2 && _pureH(mol, substituents[2]); bool h3 = !e3 && _pureH(mol, substituents[3]); // Query molecules [H]/C=C/C and [H]\C=C/C are different // But normal molecules are the same. if (!mol.isQueryMolecule()) { // Handle [H]/N=C\C and [H]/N=C/C if (!_commonHasLonePair(mol, substituents[0], substituents[1])) { h0 |= e0; h1 |= e1; } if (!_commonHasLonePair(mol, substituents[2], substituents[3])) { h2 |= e2; h3 |= e3; } } if (h0 && h1) return false; if (h2 && h3) return false; int tmp; // If hydrogens are explicit then keep them // And do not place explicit hydrogens to the end, because all static methods // should be converted into non-static with checking whether atom is hydrogen // or not. // For example, molecule [H]\C(O)=C/C can get invalid parity because static // functions getMappingParitySign, applyMapping doesn't know about bool swapped = false; if (!e1) if (e0 || substituents[0] > substituents[1]) { __swap(substituents[0], substituents[1], tmp); swapped = !swapped; } if (!e3) if (e2 || substituents[2] > substituents[3]) { __swap(substituents[2], substituents[3], tmp); swapped = !swapped; } if (parity_changed != 0) *parity_changed = swapped; return true; } bool MoleculeCisTrans::isGeomStereoBond (BaseMolecule &mol, int bond_idx, int *substituents, bool have_xyz) { int substituents_local[4]; if (substituents == 0) substituents = substituents_local; // it must be [C,N,Si,Ge]=[C,N,Si,Ge] bond if (!mol.possibleBondOrder(bond_idx, BOND_DOUBLE)) return false; const Edge &edge = mol.getEdge(bond_idx); int beg_idx = edge.beg; int end_idx = edge.end; if (!mol.possibleAtomNumber(beg_idx, ELEM_C) && !mol.possibleAtomNumber(beg_idx, ELEM_N) && !mol.possibleAtomNumber(beg_idx, ELEM_Si) && !mol.possibleAtomNumber(beg_idx, ELEM_Ge)) return false; if (!mol.possibleAtomNumber(end_idx, ELEM_C) && !mol.possibleAtomNumber(end_idx, ELEM_N) && !mol.possibleAtomNumber(end_idx, ELEM_Si) && !mol.possibleAtomNumber(end_idx, ELEM_Ge)) return false; // Double bonds with R-sites are excluded because cis-trans configuration // cannot be determined when R-site is substituted with R-group if (mol.isRSite(beg_idx) || mol.isRSite(end_idx)) return false; // the atoms should have 1 or 2 single bonds // (apart from the double bond under consideration) const Vertex &beg = mol.getVertex(beg_idx); const Vertex &end = mol.getVertex(end_idx); if (beg.degree() < 2 || beg.degree() > 3 || end.degree() < 2 || end.degree() > 3) return false; substituents[0] = -1; substituents[1] = -1; substituents[2] = -1; substituents[3] = -1; int i; for (i = beg.neiBegin(); i != beg.neiEnd(); i = beg.neiNext(i)) { int nei_edge_idx = beg.neiEdge(i); if (nei_edge_idx == bond_idx) continue; if (!mol.possibleBondOrder(nei_edge_idx, BOND_SINGLE) && !mol.possibleBondOrder(nei_edge_idx, BOND_AROMATIC)) return false; if (substituents[0] == -1) substituents[0] = beg.neiVertex(i); else // (substituents[1] == -1) substituents[1] = beg.neiVertex(i); } for (i = end.neiBegin(); i != end.neiEnd(); i = end.neiNext(i)) { int nei_edge_idx = end.neiEdge(i); if (nei_edge_idx == bond_idx) continue; if (!mol.possibleBondOrder(nei_edge_idx, BOND_SINGLE) && !mol.possibleBondOrder(nei_edge_idx, BOND_AROMATIC)) return false; if (substituents[2] == -1) substituents[2] = end.neiVertex(i); else // (substituents[3] == -1) substituents[3] = end.neiVertex(i); } // Check trianges when substituents are the same: CC1=C(N)C1 if (substituents[0] >= 0) if (substituents[0] == substituents[2] || substituents[0] == substituents[3]) return false; if (substituents[1] >= 0) if (substituents[1] == substituents[2] || substituents[1] == substituents[3]) return false; if (have_xyz) { if (substituents[1] != -1 && _sameside(mol, beg_idx, end_idx, substituents[0], substituents[1]) != -1) return false; else if (_sameline(mol, beg_idx, end_idx, substituents[0])) return false; if (substituents[3] != -1 && _sameside(mol, beg_idx, end_idx, substituents[2], substituents[3]) != -1) return false; else if (_sameline(mol, beg_idx, end_idx, substituents[2])) return false; } return true; } void MoleculeCisTrans::restoreSubstituents (int bond_idx) { BaseMolecule &mol = _getMolecule(); _Bond &bond = _bonds[bond_idx]; int *substituents = bond.substituents; if (!isGeomStereoBond(mol, bond_idx, substituents, false)) throw Error("restoreSubstituents(): not a cis-trans bond"); if (!bond.ignored) if (!sortSubstituents(mol, substituents, 0)) throw Error("can't sort restored substituents"); } void MoleculeCisTrans::registerUnfoldedHydrogen (int atom_idx, int added_hydrogen) { BaseMolecule &mol = _getMolecule(); const Vertex &vertex = mol.getVertex(atom_idx); int i; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int bond_idx = vertex.neiEdge(i); const Edge &edge = mol.getEdge(bond_idx); if (_bonds.size() <= bond_idx) continue; if (_bonds[bond_idx].parity == 0) continue; int *subst = _bonds[bond_idx].substituents; if (atom_idx == edge.beg && subst[1] == -1) subst[1] = added_hydrogen; if (atom_idx == edge.end && subst[3] == -1) subst[3] = added_hydrogen; } } void MoleculeCisTrans::clear () { _bonds.clear(); } bool MoleculeCisTrans::exists () const { return _bonds.size() > 0; } void MoleculeCisTrans::registerBond (int idx) { while (_bonds.size() <= idx) _bonds.push().clear(); _bonds[idx].clear(); } void MoleculeCisTrans::validate () { BaseMolecule &mol = _getMolecule(); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (getParity(i) != 0) { int subs[4]; if (!isGeomStereoBond(mol, i, subs, false)) setParity(i, 0); } } } bool MoleculeCisTrans::registerBondAndSubstituents (int idx) { BaseMolecule &mol = _getMolecule(); registerBond(idx); if (!isGeomStereoBond(mol, idx, _bonds[idx].substituents, false)) return false; if (!sortSubstituents(mol, _bonds[idx].substituents, 0)) return false; return true; } void MoleculeCisTrans::build (int *exclude_bonds) { BaseMolecule &mol = _getMolecule(); int i; clear(); _bonds.clear_resize(mol.edgeEnd()); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { _bonds[i].parity = 0; _bonds[i].ignored = 0; int beg = mol.getEdge(i).beg; int end = mol.getEdge(i).end; int *substituents = _bonds[i].substituents; bool have_xyz = true; // If bond is marked with ignore flag then read this flag // even if coordinates are not valid. if (exclude_bonds != 0 && exclude_bonds[i]) have_xyz = false; if (!isGeomStereoBond(mol, i, substituents, have_xyz)) continue; // Ignore only bonds that can be cis-trans if (exclude_bonds != 0 && exclude_bonds[i]) { _bonds[i].ignored = 1; continue; } if (!sortSubstituents(mol, substituents, 0)) continue; int sign = _sameside(mol, beg, end, substituents[0], substituents[2]); if (sign == 1) setParity(i, CIS); else if (sign == -1) setParity(i, TRANS); } } void MoleculeCisTrans::buildFromSmiles (int *dirs) { QS_DEF(Array<int>, subst_used); int i, j; BaseMolecule &mol = _getMolecule(); clear(); subst_used.clear_resize(mol.vertexEnd()); subst_used.zerofill(); _bonds.clear_resize(mol.edgeEnd()); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (!registerBondAndSubstituents(i)) continue; int beg = mol.getEdge(i).beg; int end = mol.getEdge(i).end; int substituents[4]; getSubstituents_All(i, substituents); int subst_dirs[4] = {0, 0, 0, 0}; int nei_edge; nei_edge = mol.findEdgeIndex(beg, substituents[0]); if (dirs[nei_edge] == 1) subst_dirs[0] = mol.getEdge(nei_edge).beg == beg ? 1 : 2; if (dirs[nei_edge] == 2) subst_dirs[0] = mol.getEdge(nei_edge).beg == beg ? 2 : 1; if (substituents[1] != -1) { nei_edge = mol.findEdgeIndex(beg, substituents[1]); if (dirs[nei_edge] == 1) subst_dirs[1] = mol.getEdge(nei_edge).beg == beg ? 1 : 2; if (dirs[nei_edge] == 2) subst_dirs[1] = mol.getEdge(nei_edge).beg == beg ? 2 : 1; } nei_edge = mol.findEdgeIndex(end, substituents[2]); if (dirs[nei_edge] == 1) subst_dirs[2] = mol.getEdge(nei_edge).beg == end ? 1 : 2; if (dirs[nei_edge] == 2) subst_dirs[2] = mol.getEdge(nei_edge).beg == end ? 2 : 1; if (substituents[3] != -1) { nei_edge = mol.findEdgeIndex(end, substituents[3]); if (dirs[nei_edge] == 1) subst_dirs[3] = mol.getEdge(nei_edge).beg == end ? 1 : 2; if (dirs[nei_edge] == 2) subst_dirs[3] = mol.getEdge(nei_edge).beg == end ? 2 : 1; } if ((subst_dirs[0] != 0 && subst_dirs[0] == subst_dirs[1]) || (subst_dirs[2] != 0 && subst_dirs[2] == subst_dirs[3])) //throw Error("cis-trans bonds %d have co-directed subsituents", i); // can happen on fragments such as CC=C(C=CN)C=CO continue; if ((subst_dirs[0] == 0 && subst_dirs[1] == 0) || (subst_dirs[2] == 0 && subst_dirs[3] == 0)) continue; if (subst_dirs[1] == 1) subst_dirs[0] = 2; else if (subst_dirs[1] == 2) subst_dirs[0] = 1; if (subst_dirs[3] == 1) subst_dirs[2] = 2; else if (subst_dirs[3] == 2) subst_dirs[2] = 1; if (subst_dirs[0] == subst_dirs[2]) setParity(i, CIS); else setParity(i, TRANS); for (j = 0; j < 4; j++) if (substituents[j] != -1) subst_used[substituents[j]] = 1; } /*for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (dirs[i] == 0) continue; const Edge &edge = mol.getEdge(i); if (!subst_used[edge.beg] && !subst_used[edge.end]) throw Error("direction of bond %d makes no sense", i); }*/ } int MoleculeCisTrans::getParity (int bond_idx) const { if (bond_idx >= _bonds.size()) return 0; return _bonds[bond_idx].parity; } bool MoleculeCisTrans::isIgnored (int bond_idx) const { if (bond_idx >= _bonds.size()) return false; return _bonds[bond_idx].ignored == 1; } void MoleculeCisTrans::ignore (int bond_idx) { while (bond_idx >= _bonds.size()) _bonds.push().clear(); _bonds[bond_idx].parity = 0; _bonds[bond_idx].ignored = 1; } void MoleculeCisTrans::setParity (int bond_idx, int parity) { while (_bonds.size() <= bond_idx) _bonds.push().clear(); _bonds[bond_idx].parity = parity; } const int * MoleculeCisTrans::getSubstituents (int bond_idx) const { return _bonds[bond_idx].substituents; } void MoleculeCisTrans::_fillAtomExplicitHydrogens (BaseMolecule &mol, int atom_idx, int subst[2]) { const Vertex &vertex = mol.getVertex(atom_idx); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int nei = vertex.neiVertex(i); // check [H]\N=C\C if (_pureH(mol, nei)) { if (subst[0] != nei) subst[1] = nei; else if (subst[1] != nei) subst[0] = nei; else throw Error("internal error in _fillAtomExplicitHydrogens"); } } } void MoleculeCisTrans::_fillExplicitHydrogens (BaseMolecule &mol, int bond_idx, int subst[4]) { _fillAtomExplicitHydrogens(mol, mol.getEdge(bond_idx).beg, subst); _fillAtomExplicitHydrogens(mol, mol.getEdge(bond_idx).end, subst + 2); } void MoleculeCisTrans::getSubstituents_All (int bond_idx, int subst[4]) { BaseMolecule &mol = _getMolecule(); memcpy(subst, _bonds[bond_idx].substituents, 4 * sizeof(int)); _fillExplicitHydrogens(mol, bond_idx, subst); } void MoleculeCisTrans::add (int bond_idx, int substituents[4], int parity) { registerBond(bond_idx); setParity(bond_idx, parity); for (int i = 0; i < 4; i++) _bonds[bond_idx].substituents[i] = substituents[i]; } int MoleculeCisTrans::_getPairParity (int v1, int v2, const int *mapping, bool sort) { if (v1 < 0 || mapping[v1] < 0) { if (v2 < 0 || mapping[v2] < 0) // Both mapped values result in undefined parity return 0; // v1 and v2 needs to be swapped for sorting, but mapping is stil rigid return sort ? -1 : 1; } // mapping[v1] >= 0 if (v2 < 0 || mapping[v2] < 0) // Second vertex is unmapped => no need to swap return 1; // mapping[v1] >= 0 && mapping[v2] >= 0 int m1 = mapping[v1]; int m2 = mapping[v2]; // Check that ordering is preserved return (m1 < m2) == (v1 < v2) ? 1 : -1; } int MoleculeCisTrans::applyMapping (int parity, const int *substituents, const int *mapping, bool sort) { int sum = 0; int p1 = _getPairParity(substituents[0], substituents[1], mapping, sort); int p2 = _getPairParity(substituents[2], substituents[3], mapping, sort); if (p1 == 0 || p2 == 0) return 0; if (p1 * p2 > 0) return parity; return (parity == CIS) ? TRANS : CIS; } int MoleculeCisTrans::getMappingParitySign (BaseMolecule &query, BaseMolecule &target, int bond_idx, const int *mapping) { int query_parity = query.cis_trans.getParity(bond_idx); int target_edge_idx = Graph::findMappedEdge(query, target, bond_idx, mapping); int target_parity = target.cis_trans.getParity(target_edge_idx); if (target_parity == 0) if (query_parity != 0) return -2; // Mapping is not valid else return 0; const int *query_subst = query.cis_trans.getSubstituents(bond_idx); int query_subst_mapped[4]; for (int i = 0; i < 4; i++) query_subst_mapped[i] = query_subst[i] >= 0 ? mapping[query_subst[i]] : -1; int config_parity = 0; int v1; if (query_subst_mapped[0] >= 0) v1 = query_subst_mapped[0]; else { v1 = query_subst_mapped[1]; config_parity++; } if (v1 < 0) return 0; int v2; if (query_subst_mapped[2] >= 0) v2 = query_subst_mapped[2]; else { v2 = query_subst_mapped[3]; config_parity++; } if (v2 < 0) return 0; // Check configuration of v1 and v2 in the target const int *target_subst = target.cis_trans.getSubstituents(target_edge_idx); if (v1 == target_subst[0] || v1 == target_subst[2]) ; else if (v1 == target_subst[1] || v1 == target_subst[3]) config_parity++; else throw Error("Internal error in MoleculeCisTrans::getMappingParitySign: mapping is invalid"); if (v2 == target_subst[0] || v2 == target_subst[2]) ; else if (v2 == target_subst[1] || v2 == target_subst[3]) config_parity++; else throw Error("Internal error in MoleculeCisTrans::getMappingParitySign: mapping is invalid"); if (query_parity == TRANS) config_parity++; if (target_parity == TRANS) config_parity++; return config_parity % 2 == 0 ? 1 : -1; } bool MoleculeCisTrans::checkSub (BaseMolecule &query, BaseMolecule &target, const int *mapping) { int i; for (i = query.edgeBegin(); i != query.edgeEnd(); i = query.edgeNext(i)) { if (!query.bondStereoCare(i)) continue; int query_parity = query.cis_trans.getParity(i); if (query_parity == 0) throw Error("bond #%d has stereo-care flag, but is not cis-trans bond", i); if (getMappingParitySign(query, target, i, mapping) < 0) return false; } return true; } void MoleculeCisTrans::buildOnSubmolecule (BaseMolecule &super, int *mapping) { BaseMolecule &sub = _getMolecule(); if (!super.cis_trans.exists()) return; while (_bonds.size() < sub.edgeEnd()) { _Bond &bond = _bonds.push(); memset(&bond, 0, sizeof(_Bond)); } int i, j; for (i = super.edgeBegin(); i != super.edgeEnd(); i = super.edgeNext(i)) { int parity = super.cis_trans.getParity(i); int sub_edge_idx = Graph::findMappedEdge(super, sub, i, mapping); if (sub_edge_idx < 0) continue; _Bond &bond = _bonds[sub_edge_idx]; bond.ignored = super.cis_trans.isIgnored(i); if (parity == 0) { bond.parity = 0; continue; } const int *substituents = super.cis_trans.getSubstituents(i); for (j = 0; j < 4; j++) { if (substituents[j] < 0 || mapping[substituents[j]] < 0) bond.substituents[j] = -1; else bond.substituents[j] = mapping[substituents[j]]; } bond.parity = parity; bool parity_changed; if (!sortSubstituents(sub, bond.substituents, &parity_changed)) { bond.parity = 0; continue; } if (parity_changed) bond.parity = 3 - bond.parity; } } bool MoleculeCisTrans::isAutomorphism (BaseMolecule &mol, const Array<int> &mapping, const Filter *edge_filter) { for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (edge_filter && !edge_filter->valid(i)) continue; const Edge &edge = mol.getEdge(i); int parity = mol.cis_trans.getParity(i); int parity2 = MoleculeCisTrans::applyMapping(parity, mol.cis_trans.getSubstituents(i), mapping.ptr(), false); int i2 = mol.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); if (mol.cis_trans.getParity(i2) != parity2) return false; } return true; } int MoleculeCisTrans::applyMapping (int idx, const int *mapping, bool sort) const { return applyMapping(getParity(idx), getSubstituents(idx), mapping, sort); } void MoleculeCisTrans::flipBond (int atom_parent, int atom_from, int atom_to) { BaseMolecule &mol = _getMolecule(); int parent_edge_index = mol.findEdgeIndex(atom_parent, atom_from); if (parent_edge_index == -1 || getParity(parent_edge_index) != 0) // Such call wasn't expected and wasn't implemented throw Error("bond flipping with may cause stereobond destruction. " "Such functionality isn't implemented yet."); const Vertex &parent_vertex = mol.getVertex(atom_parent); for (int i = parent_vertex.neiBegin(); i != parent_vertex.neiEnd(); i = parent_vertex.neiNext(i)) { int edge = parent_vertex.neiEdge(i); if (getParity(edge) == 0) continue; _Bond &bond = _bonds[edge]; for (int i = 0; i < 4; i++) if (bond.substituents[i] == atom_from) { bond.substituents[i] = atom_to; break; } } const Vertex &from_vertex = mol.getVertex(atom_from); for (int i = from_vertex.neiBegin(); i != from_vertex.neiEnd(); i = from_vertex.neiNext(i)) { int edge = parent_vertex.neiEdge(i); if (getParity(edge) == 0) continue; _Bond &bond = _bonds[edge]; for (int i = 0; i < 4; i++) if (bond.substituents[i] == atom_parent) { bond.substituents[i] = atom_to; break; } } const Vertex &to_vertex = mol.getVertex(atom_to); for (int i = to_vertex.neiBegin(); i != to_vertex.neiEnd(); i = to_vertex.neiNext(i)) { int edge = parent_vertex.neiEdge(i); if (getParity(edge) == 0) continue; _Bond &bond = _bonds[edge]; int edge_beg = mol.getEdge(edge).beg; if (atom_to == edge_beg) { if (bond.substituents[1] != -1) throw Error("Cannot flip bond if all substituents are present"); bond.substituents[1] = atom_parent; } else { if (bond.substituents[3] != -1) throw Error("Cannot flip bond if all substituents are present"); bond.substituents[3] = atom_parent; } } } int MoleculeCisTrans::count () { int i, res = 0; for (i = 0; i < _bonds.size(); i++) if (_bonds[i].parity != 0) res++; return res; } bool MoleculeCisTrans::isRingTransBond (int i) { const int *subst = getSubstituents(i); int parity = getParity(i); // 1(CIS) or 2(TRANS) BaseMolecule &mol = _getMolecule(); const Edge &edge = mol.getEdge(i); if (mol.getBondTopology(i) != TOPOLOGY_RING) throw Error("is RingTransBond(): not a ring bond given"); if (mol.getBondTopology(mol.findEdgeIndex(edge.beg, subst[0])) != TOPOLOGY_RING) { if (mol.getBondTopology(mol.findEdgeIndex(edge.beg, subst[1])) != TOPOLOGY_RING) throw Error("unexpected: have not found ring substutient"); // invert parity parity = 3 - parity; } if (mol.getBondTopology(mol.findEdgeIndex(edge.end, subst[2])) != TOPOLOGY_RING) { if (mol.getBondTopology(mol.findEdgeIndex(edge.end, subst[3])) != TOPOLOGY_RING) throw Error("unexpected: have not found ring substutient"); // invert parity parity = 3 - parity; } return (parity == MoleculeCisTrans::TRANS); } bool MoleculeCisTrans::sameside (int edge_idx, int v1, int v2) { int parity = getParity(edge_idx); if (parity == 0) throw Error("Bond %d is not a cis-trans bond", edge_idx); const int *subst = getSubstituents(edge_idx); int v[2] = { v1, v2 }; int v_pos[2] = { -1 }; for (int j = 0; j < 2; j++) { for (int i = 0; i < 4; i++) if (subst[i] == v[j]) { v_pos[j] = i; break; } if (v_pos[j] == -1) throw Error("Vertex %d has not been found near bond %d", v[j], edge_idx); } bool same_parity = (v_pos[0] % 2) == (v_pos[1] % 2); if (parity == TRANS) same_parity = !same_parity; return same_parity; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cml_loader.cpp��������������������������������������������0000664�0000000�0000000�00000065622�12710376503�0023302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_cml_loader.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "tinyxml.h" #include "molecule/elements.h" #include "molecule/molecule_scaffold_detection.h" using namespace indigo; IMPL_ERROR(MoleculeCmlLoader, "molecule CML loader"); MoleculeCmlLoader::MoleculeCmlLoader (Scanner &scanner) { _scanner = &scanner; _handle = 0; } MoleculeCmlLoader::MoleculeCmlLoader (TiXmlHandle &handle) { _handle = &handle; _scanner = 0; } void MoleculeCmlLoader::loadMolecule (Molecule &mol) { mol.clear(); if (_scanner != 0) { QS_DEF(Array<char>, buf); _scanner->readAll(buf); buf.push(0); TiXmlDocument xml; xml.Parse(buf.ptr()); if (xml.Error()) throw Error("XML parsing error: %s", xml.ErrorDesc()); TiXmlHandle hxml(&xml); TiXmlNode *node; if (_findMolecule(hxml.ToNode())) { node = _molecule; TiXmlHandle molecule = _molecule; _loadMolecule(molecule, mol); for (node = node->NextSibling(); node != 0; node = node->NextSibling()) { if (strncmp(node->Value(), "Rgroup", 6) == 0) { TiXmlHandle rgroup = node; _loadRgroup(rgroup, mol); } } } } else _loadMolecule(*_handle, mol); } bool MoleculeCmlLoader::_findMolecule(TiXmlNode *elem) { TiXmlNode *node; for (node = elem->FirstChild(); node != 0; node = node->NextSibling()) { if (strncmp(node->Value(), "molecule", 8) == 0) { _molecule = node; return true; } if (_findMolecule(node)) return true; } return false; } // Atoms struct Atom { std::string id, element_type, label, isotope, formal_charge, spin_multiplicity, radical, valence, rgroupref, attpoint, attorder, hydrogen_count, x, y, z; }; // This methods splits a space-separated string and writes each values into an arbitrary string // property of Atom structure for each atom in the specified list static void splitStringIntoProperties (const char *s, std::vector<Atom> &atoms, std::string Atom::*property) { if (!s) return; std::stringstream stream(s); size_t i = 0; std::string token; while (stream >> token) { if (atoms.size() <= i) atoms.resize(i + 1); atoms[i].*property = token; i++; } } void MoleculeCmlLoader::_loadMolecule (TiXmlHandle &handle, Molecule &mol) { QS_DEF((std::unordered_map<std::string, int>), atoms_id); QS_DEF((std::unordered_map<std::string, size_t>), atoms_id_int); // Function that mappes atom id as a string to an atom index auto getAtomIdx = [&](const char *id) { auto it = atoms_id.find(id); if (it == atoms_id.end()) throw Error("atom id %s cannot be found", id); return it->second; }; QS_DEF(Array<int>, total_h_count); atoms_id.clear(); atoms_id_int.clear(); total_h_count.clear(); const char *title = handle.Element()->Attribute("title"); if (title != 0) mol.name.readString(title, true); QS_DEF(std::vector<Atom>, atoms); atoms.clear(); // // Read elements into an atoms array first and the parse them // TiXmlHandle atom_array = handle.FirstChild("atomArray"); // Read atoms as xml attributes // <atomArray // atomID="a1 a2 a3 ... " // elementType="O C O ..." // hydrogenCount="1 0 0 ..." // /> splitStringIntoProperties(atom_array.Element()->Attribute("atomID"), atoms, &Atom::id); // Fill id if any were found size_t atom_index; for (atom_index = 0; atom_index < atoms.size(); atom_index++) atoms_id_int.emplace(atoms[atom_index].id, atom_index); splitStringIntoProperties(atom_array.Element()->Attribute("elementType"), atoms, &Atom::element_type); splitStringIntoProperties(atom_array.Element()->Attribute("mrvPseudo"), atoms, &Atom::label); splitStringIntoProperties(atom_array.Element()->Attribute("hydrogenCount"), atoms, &Atom::hydrogen_count); splitStringIntoProperties(atom_array.Element()->Attribute("x2"), atoms, &Atom::x); splitStringIntoProperties(atom_array.Element()->Attribute("y2"), atoms, &Atom::y); splitStringIntoProperties(atom_array.Element()->Attribute("x3"), atoms, &Atom::x); splitStringIntoProperties(atom_array.Element()->Attribute("y3"), atoms, &Atom::y); splitStringIntoProperties(atom_array.Element()->Attribute("z3"), atoms, &Atom::z); splitStringIntoProperties(atom_array.Element()->Attribute("isotope"), atoms, &Atom::isotope); splitStringIntoProperties(atom_array.Element()->Attribute("isotopeNumber"), atoms, &Atom::isotope); splitStringIntoProperties(atom_array.Element()->Attribute("formalCharge"), atoms, &Atom::formal_charge); splitStringIntoProperties(atom_array.Element()->Attribute("spinMultiplicity"), atoms, &Atom::spin_multiplicity); splitStringIntoProperties(atom_array.Element()->Attribute("radical"), atoms, &Atom::radical); splitStringIntoProperties(atom_array.Element()->Attribute("mrvValence"), atoms, &Atom::valence); splitStringIntoProperties(atom_array.Element()->Attribute("rgroupRef"), atoms, &Atom::rgroupref); splitStringIntoProperties(atom_array.Element()->Attribute("attachmentPoint"), atoms, &Atom::attpoint); splitStringIntoProperties(atom_array.Element()->Attribute("attachmentOrder"), atoms, &Atom::attorder); // Read atoms as nested xml elements // <atomArray> // <atom id="a1" elementType="H" /> for (TiXmlElement *elem = atom_array.FirstChild().Element(); elem; elem = elem->NextSiblingElement()) { if (strncmp(elem->Value(), "atom", 4) != 0) continue; const char *id = elem->Attribute("id"); if (id == 0) throw Error("atom without an id"); // Check if this atom has already been parsed auto it = atoms_id_int.find(id); if (it != end(atoms_id_int)) atom_index = it->second; else { atom_index = atoms.size(); atoms.emplace_back(); atoms_id_int.emplace(id, atom_index); } Atom &a = atoms[atom_index]; a.id = id; const char *element_type = elem->Attribute("elementType"); if (element_type == 0) throw Error("atom without an elementType"); a.element_type = element_type; const char *pseudo = elem->Attribute("mrvPseudo"); if (pseudo != 0) a.label = pseudo; const char *isotope = elem->Attribute("isotope"); if (isotope == 0) isotope = elem->Attribute("isotopeNumber"); if (isotope != 0) a.isotope = isotope; const char *charge = elem->Attribute("formalCharge"); if (charge != 0) a.formal_charge = charge; const char *spinmultiplicity = elem->Attribute("spinMultiplicity"); if (spinmultiplicity != 0) a.spin_multiplicity = spinmultiplicity; const char *radical = elem->Attribute("radical"); if (radical != 0) a.radical = radical; const char *valence = elem->Attribute("mrvValence"); if (valence != 0) a.valence = valence; const char *hcount = elem->Attribute("hydrogenCount"); if (hcount != 0) a.hydrogen_count = hcount; const char *x2 = elem->Attribute("x2"); const char *y2 = elem->Attribute("y2"); if (x2 != 0 && y2 != 0) { a.x = x2; a.y = y2; } const char *x3 = elem->Attribute("x3"); const char *y3 = elem->Attribute("y3"); const char *z3 = elem->Attribute("z3"); if (x3 != 0 && y3 != 0 && z3 != 0) { a.x = x3; a.y = y3; a.z = z3; } const char *rgroupref = elem->Attribute("rgroupRef"); if (rgroupref != 0) a.rgroupref = rgroupref; const char *attpoint = elem->Attribute("attachmentPoint"); if (attpoint != 0) a.attpoint = attpoint; const char *attorder = elem->Attribute("attachmentOrder"); if (attorder != 0) a.attorder = attorder; } // Parse them for (auto &a : atoms) { int label = Element::fromString2(a.element_type.c_str()); if ( (label == -1) && (strncmp(a.element_type.c_str(), "R", 1) == 0) ) { label = ELEM_RSITE; } else if ( (label == -1) && (strncmp(a.element_type.c_str(), "*", 1) == 0) ) { label = ELEM_ATTPOINT; } else if ( (label == -1) || (!a.label.empty()) ) label = ELEM_PSEUDO; int idx; if (label == ELEM_ATTPOINT) { atoms_id.emplace(a.id, -1); } else { int idx = mol.addAtom(label); if (label == ELEM_PSEUDO) { if (!a.label.empty()) mol.setPseudoAtom(idx, a.label.c_str()); else mol.setPseudoAtom(idx, a.element_type.c_str()); } total_h_count.expandFill(idx + 1, -1); atoms_id.emplace(a.id, idx); if (!a.isotope.empty()) { int val; if (sscanf(a.isotope.c_str(), "%d", &val) != 1) throw Error("error parsing isotope"); mol.setAtomIsotope(idx, val); } if (!a.formal_charge.empty()) { int val; if (sscanf(a.formal_charge.c_str(), "%d", &val) != 1) throw Error("error parsing charge"); mol.setAtomCharge(idx, val); } if (!a.spin_multiplicity.empty()) { int val; if (sscanf(a.spin_multiplicity.c_str(), "%d", &val) != 1) throw Error("error parsing spin multiplicity"); mol.setAtomRadical(idx, val); } if (!a.radical.empty()) { int val = 0; if (strncmp(a.radical.c_str(), "divalent1", 9) == 0) val = 1; else if (strncmp(a.radical.c_str(), "monovalent", 10) == 0) val = 2; else if ( (strncmp(a.radical.c_str(), "divalent3", 9) == 0) || (strncmp(a.radical.c_str(), "divalent", 8) == 0) || (strncmp(a.radical.c_str(), "triplet", 7) == 0) ) val = 3; mol.setAtomRadical(idx, val); } if (!a.valence.empty()) { int val; if (sscanf(a.valence.c_str(), "%d", &val) == 1) mol.setExplicitValence(idx, val); } if (!a.hydrogen_count.empty()) { int val; if (sscanf(a.hydrogen_count.c_str(), "%d", &val) != 1) throw Error("error parsing hydrogen count"); if (val < 0) throw Error("negative hydrogen count"); total_h_count[idx] = val; } if (!a.x.empty()) if (sscanf(a.x.c_str(), "%f", &mol.getAtomXyz(idx).x) != 1) throw Error("error parsing x"); if (!a.y.empty()) if (sscanf(a.y.c_str(), "%f", &mol.getAtomXyz(idx).y) != 1) throw Error("error parsing y"); if (!a.z.empty()) if (sscanf(a.z.c_str(), "%f", &mol.getAtomXyz(idx).z) != 1) throw Error("error parsing z"); if (!a.rgroupref.empty()) { int val; if (sscanf(a.rgroupref.c_str(), "%d", &val) != 1) throw Error("error parsing R-group reference"); mol.allowRGroupOnRSite(idx, val); } if (!a.attpoint.empty()) { int val; if (strncmp(a.attpoint.c_str(), "both", 4) == 0) val = 3; else if (sscanf(a.attpoint.c_str(), "%d", &val) != 1) throw Error("error parsing Attachment point"); for (int att_idx = 0; (1 << att_idx) <= val; att_idx++) if (val & (1 << att_idx)) mol.addAttachmentPoint(att_idx + 1, idx); } } /* if (!a.attorder.empty()) { int val; if (sscanf(a.attorder.c_str(), "%d", &val) != 1) throw Error("error parsing Attachment order"); mol.setRSiteAttachmentOrder(idx, nei_idx - 1, val - 1); } */ } // Bonds bool have_cistrans_notation = false; for (TiXmlElement *elem = handle.FirstChild("bondArray").FirstChild().Element(); elem; elem = elem->NextSiblingElement()) { if (strncmp(elem->Value(), "bond", 4) != 0) continue; const char *atom_refs = elem->Attribute("atomRefs2"); if (atom_refs == 0) throw Error("bond without atomRefs2"); BufferScanner strscan(atom_refs); QS_DEF(Array<char>, id); strscan.readWord(id, 0); int beg = getAtomIdx(id.ptr()); strscan.skipSpace(); strscan.readWord(id, 0); int end = getAtomIdx(id.ptr()); if ((beg < 0) || (end < 0)) continue; const char *order = elem->Attribute("order"); if (order == 0) throw Error("bond without an order"); int order_val; { if (order[0] == 'A' && order[1] == 0) order_val = BOND_AROMATIC; else if (order[0] == 'S' && order[1] == 0) order_val = BOND_SINGLE; else if (order[0] == 'D' && order[1] == 0) order_val = BOND_DOUBLE; else if (order[0] == 'T' && order[1] == 0) order_val = BOND_TRIPLE; else if (sscanf(order, "%d", &order_val) != 1) throw Error("error parsing order"); } int idx = mol.addBond_Silent(beg, end, order_val); int dir = 0; TiXmlElement *bs_elem = elem->FirstChildElement("bondStereo"); if (bs_elem != 0) { const char *text = bs_elem->GetText(); if (text != 0) { if (text[0] == 'W' && text[1] == 0) dir = BOND_UP; if (text[0] == 'H' && text[1] == 0) dir = BOND_DOWN; if ((text[0] == 'C' || text[0] == 'T') && text[1] == 0) have_cistrans_notation = true; } } if (dir != 0) mol.setBondDirection(idx, dir); } // Implicit H counts int i, j; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { int h = total_h_count[i]; if (h < 0) continue; const Vertex &vertex = mol.getVertex(i); for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) if (mol.getAtomNumber(vertex.neiVertex(j)) == ELEM_H) h--; if (h < 0) throw Error("hydrogenCount on atom %d is less than the number of explicit hydrogens"); mol.setImplicitH(i, h); } // Tetrahedral stereocenters for (TiXmlElement *elem = handle.FirstChild("atomArray").FirstChild().Element(); elem; elem = elem->NextSiblingElement()) { const char *id = elem->Attribute("id"); if (id == 0) throw Error("atom without an id"); int idx = getAtomIdx(id); TiXmlElement *ap_elem = elem->FirstChildElement("atomParity"); if (ap_elem == 0) continue; const char *ap_text = ap_elem->GetText(); if (ap_text == 0) continue; int val; if (sscanf(ap_text, "%d", &val) != 1) throw Error("error parsing atomParity"); const char *refs4 = ap_elem->Attribute("atomRefs4"); if (refs4 != 0) { BufferScanner strscan(refs4); QS_DEF(Array<char>, id); int k, pyramid[4]; for (k = 0; k < 4; k++) { strscan.skipSpace(); strscan.readWord(id, 0); pyramid[k] = getAtomIdx(id.ptr()); if (pyramid[k] == idx) pyramid[k] = -1; } if (val < 0) __swap(pyramid[0], pyramid[1], k); MoleculeStereocenters::moveMinimalToEnd(pyramid); mol.stereocenters.add(idx, MoleculeStereocenters::ATOM_ABS, 0, pyramid); } } if (mol.stereocenters.size() == 0 && BaseMolecule::hasCoord(mol)) { QS_DEF(Array<int>, sensible_bond_orientations); sensible_bond_orientations.clear_resize(mol.vertexEnd()); mol.stereocenters.buildFromBonds(stereochemistry_options, sensible_bond_orientations.ptr()); if (!stereochemistry_options.ignore_errors) for (i = 0; i < mol.vertexCount(); i++) if (mol.getBondDirection(i) > 0 && !sensible_bond_orientations[i]) throw Error("direction of bond #%d makes no sense", i); } // Cis-trans stuff if (have_cistrans_notation) { int bond_idx = -1; for (TiXmlElement *elem = handle.FirstChild("bondArray").FirstChild().Element(); elem; elem = elem->NextSiblingElement()) { if (strncmp(elem->Value(), "bond", 4) != 0) continue; bond_idx++; TiXmlElement *bs_elem = elem->FirstChildElement("bondStereo"); if (bs_elem == 0) continue; const char *text = bs_elem->GetText(); if (text == 0) continue; int parity; if (text[0] == 'C' && text[1] == 0) parity = MoleculeCisTrans::CIS; else if (text[0] == 'T' && text[1] == 0) parity = MoleculeCisTrans::TRANS; else continue; const char *atom_refs = bs_elem->Attribute("atomRefs4"); // If there are only one substituent then atomRefs4 cano be omitted bool has_subst = atom_refs != nullptr; int substituents[4]; if (!MoleculeCisTrans::isGeomStereoBond(mol, bond_idx, substituents, false)) throw Error("cis-trans notation on a non cis-trans bond #%d", bond_idx); if (!MoleculeCisTrans::sortSubstituents(mol, substituents, 0)) throw Error("cis-trans notation on a non cis-trans bond #%d", bond_idx); if (has_subst) { BufferScanner strscan(atom_refs); QS_DEF(Array<char>, id); int refs[4] = { -1, -1, -1, -1 }; for (int k = 0; k < 4; k++) { strscan.skipSpace(); strscan.readWord(id, 0); refs[k] = getAtomIdx(id.ptr()); } const Edge &edge = mol.getEdge(bond_idx); if (refs[1] == edge.beg && refs[2] == edge.end) ; else if (refs[1] == edge.end && refs[2] == edge.beg) { std::swap(refs[0], refs[3]); std::swap(refs[1], refs[2]); } else throw Error("atomRefs4 in bondStereo do not match the bond ends"); bool invert = false; if (refs[0] == substituents[0]) ; else if (refs[0] == substituents[1]) invert = !invert; else throw Error("atomRefs4 in bondStereo do not match the substituents"); if (refs[3] == substituents[2]) ; else if (refs[3] == substituents[3]) invert = !invert; else throw Error("atomRefs4 in bondStereo do not match the substituents"); if (invert) parity = 3 - parity; } mol.cis_trans.add(bond_idx, substituents, parity); } } else if (BaseMolecule::hasCoord(mol)) mol.cis_trans.build(0); // Sgroups for (TiXmlElement *elem = handle.FirstChild().Element(); elem; elem = elem->NextSiblingElement()) { if (strncmp(elem->Value(), "molecule", 8) != 0) continue; _loadSGroup(elem, mol, atoms_id, 0); } } void MoleculeCmlLoader::_loadSGroup (TiXmlElement *elem, Molecule &mol, std::unordered_map<std::string, int> &atoms_id, int sg_parent) { auto getAtomIdx = [&](const char *id) { auto it = atoms_id.find(id); if (it == atoms_id.end()) throw Error("atom id %s cannot be found", id); return it->second; }; MoleculeSGroups *sgroups = &mol.sgroups; DataSGroup *dsg = 0; int idx = 0; if (elem != 0) { const char *role = elem->Attribute("role"); if (role == 0) throw Error("Sgroup without type"); if (strncmp(role, "DataSgroup", 10) == 0) { idx = sgroups->addSGroup(SGroup::SG_TYPE_DAT); dsg = (DataSGroup *) &sgroups->getSGroup(idx); } if (dsg == 0) return; if (sg_parent > 0) dsg->parent_group = sg_parent; const char *atom_refs = elem->Attribute("atomRefs"); if (atom_refs != 0) { BufferScanner strscan(atom_refs); QS_DEF(Array<char>, id); while (!strscan.isEOF()) { strscan.readWord(id, 0); dsg->atoms.push(getAtomIdx(id.ptr())); strscan.skipSpace(); } } const char * fieldname = elem->Attribute("fieldName"); if (fieldname != 0) dsg->name.readString(fieldname, true); const char * fielddata = elem->Attribute("fieldData"); if (fieldname != 0) dsg->data.readString(fielddata, true); const char * fieldtype = elem->Attribute("fieldType"); if (fieldtype != 0) dsg->description.readString(fieldtype, true); const char * disp_x = elem->Attribute("x"); if (disp_x != 0) { BufferScanner strscan(disp_x); dsg->display_pos.x = strscan.readFloat(); } const char * disp_y = elem->Attribute("y"); if (disp_y != 0) { BufferScanner strscan(disp_y); dsg->display_pos.y = strscan.readFloat(); } const char * detached = elem->Attribute("dataDetached"); dsg->detached = true; if (detached != 0) { if ( (strncmp(detached, "true", 4) == 0) || (strncmp(detached, "on", 2) == 0) || (strncmp(detached, "1", 1) == 0) || (strncmp(detached, "yes", 3) == 0) ) { dsg->detached = true; } else if ( (strncmp(detached, "false", 5) == 0) || (strncmp(detached, "off", 3) == 0) || (strncmp(detached, "0", 1) == 0) || (strncmp(detached, "no", 2) == 0) ) { dsg->detached = false; } } const char * relative = elem->Attribute("placement"); dsg->relative = false; if (relative != 0) { if (strncmp(relative, "Relative", 8) == 0) { dsg->relative = true; } } const char * disp_units = elem->Attribute("unitsDisplayed"); if (disp_units != 0) { if ( (strncmp(disp_units, "Unit displayed", 14) == 0) || (strncmp(disp_units, "yes", 3) == 0) || (strncmp(disp_units, "on", 2) == 0) || (strncmp(disp_units, "1", 1) == 0) || (strncmp(disp_units, "true", 4) == 0) ) { dsg->display_units = true; } } dsg->num_chars = 0; const char * disp_chars = elem->Attribute("displayedChars"); if (disp_chars != 0) { BufferScanner strscan(disp_chars); dsg->num_chars = strscan.readInt1(); } const char * disp_tag = elem->Attribute("tag"); if (disp_tag != 0) { BufferScanner strscan(disp_tag); dsg->tag = strscan.readChar(); } const char * query_op = elem->Attribute("queryOp"); if (query_op != 0) dsg->queryoper.readString(query_op, true); const char * query_type = elem->Attribute("queryType"); if (query_type != 0) dsg->querycode.readString(query_type, true); TiXmlNode * pChild; for (pChild = elem->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { if (strncmp(pChild->Value(), "molecule", 8) != 0) continue; TiXmlHandle next_mol = pChild; if (next_mol.Element() != 0) _loadSGroup(next_mol.Element(), mol, atoms_id, idx + 1); } } } void MoleculeCmlLoader::_loadRgroup (TiXmlHandle &handle, Molecule &mol) { MoleculeRGroups *rgroups = &mol.rgroups; TiXmlElement *elem = handle.Element(); if (elem != 0) { int rg_idx; const char *rgroup_id = elem->Attribute("rgroupID"); if (rgroup_id == 0) throw Error("Rgroup without ID"); BufferScanner strscan(rgroup_id); rg_idx = strscan.readInt1(); RGroup &rgroup = rgroups->getRGroup(rg_idx); const char *rlogic_range = elem->Attribute("rlogicRange"); if (rlogic_range != 0) _parseRlogicRange (rlogic_range, rgroup.occurrence); const char *rgroup_then = elem->Attribute("thenR"); if (rgroup_then != 0) { BufferScanner strscan(rgroup_then); rgroup.if_then = strscan.readInt1(); } rgroup.rest_h = 0; const char *rgroup_resth = elem->Attribute("restH"); if (rgroup_resth != 0) { if ( (strncmp(rgroup_resth, "true", 4) == 0) || (strncmp(rgroup_resth, "on", 2) == 0) || (strncmp(rgroup_resth, "1", 1) == 0) ) { rgroup.rest_h = 1; } } TiXmlNode * pChild; for (pChild = handle.FirstChild().ToNode(); pChild != 0; pChild = pChild->NextSibling()) { if (strncmp(pChild->Value(), "molecule", 8) != 0) continue; TiXmlHandle molecule = pChild; if (molecule.Element() != 0) { AutoPtr<BaseMolecule> fragment(mol.neu()); _loadMolecule (molecule, fragment.get()->asMolecule()); rgroup.fragments.add(fragment.release()); } } } } void MoleculeCmlLoader::_parseRlogicRange (const char *str, Array<int> &ranges) { int beg = -1, end = -1; int add_beg = 0, add_end = 0; while (*str != 0) { if (*str == '>') { end = 0xFFFF; add_beg = 1; } else if (*str == '<') { beg = 0; add_end = -1; } else if (isdigit(*str)) { sscanf(str, "%d", beg == -1 ? &beg : &end); while (isdigit(*str)) str++; continue; } else if (*str == ',') { if (end == -1) end = beg; else beg += add_beg, end += add_end; ranges.push((beg << 16) | end); beg = end = -1; add_beg = add_end = 0; } str++; } if (beg == -1 && end == -1) return; if (end == -1) end = beg; else beg += add_beg, end += add_end; ranges.push((beg << 16) | end); } ��������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_cml_saver.cpp���������������������������������������������0000664�0000000�0000000�00000024065�12710376503�0023150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "molecule/molecule_cml_saver.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "base_cpp/locale_guard.h" #include "tinyxml.h" using namespace indigo; IMPL_ERROR(MoleculeCmlSaver, "molecule CML saver"); MoleculeCmlSaver::MoleculeCmlSaver (Output &output) : _output(output) { skip_cml_tag = false; } void MoleculeCmlSaver::saveMolecule (Molecule &mol) { LocaleGuard locale_guard; int i; AutoPtr<TiXmlDocument> _doc(new TiXmlDocument()); _root = 0; _mol = &mol; if (!skip_cml_tag) { TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", ""); _doc->LinkEndChild(decl); _root = new TiXmlElement("cml"); _doc->LinkEndChild(_root); } TiXmlElement * molecule = new TiXmlElement("molecule"); if (_root != 0) _root->LinkEndChild(molecule); else _doc->LinkEndChild(molecule); if (_mol->name.ptr() != 0) { if (strchr(_mol->name.ptr(), '\"') != NULL) throw Error("can not save molecule with '\"' in title"); molecule->SetAttribute("title", _mol->name.ptr()); } bool have_xyz = BaseMolecule::hasCoord(*_mol); bool have_z = BaseMolecule::hasZCoord(*_mol); if (_mol->vertexCount() > 0) { TiXmlElement * atomarray = new TiXmlElement("atomArray"); molecule->LinkEndChild(atomarray); for (i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i)) { int atom_number = _mol->getAtomNumber(i); const char *atom_str; if (_mol->isRSite(i)) atom_str = "R"; else if (_mol->isPseudoAtom(i)) atom_str = _mol->getPseudoAtom(i); else atom_str = Element::toString(atom_number); TiXmlElement * atom = new TiXmlElement("atom"); atomarray->LinkEndChild(atom); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("a%d", i); buf.push(0); atom->SetAttribute("id", buf.ptr()); atom->SetAttribute("elementType", atom_str); if (_mol->getAtomIsotope(i) != 0) { atom->SetAttribute("isotope", _mol->getAtomIsotope(i)); // for inchi-1 program which ignores "isotope" property (version 1.03) atom->SetAttribute("isotopeNumber", _mol->getAtomIsotope(i)); } if (_mol->getAtomCharge(i) != 0) atom->SetAttribute("formalCharge", _mol->getAtomCharge(i)); if (!_mol->isRSite(i) && !_mol->isPseudoAtom(i)) { if (_mol->getAtomRadical_NoThrow(i, 0) != 0) { atom->SetAttribute("spinMultiplicity", _mol->getAtomRadical(i)); if (_mol->getAtomRadical_NoThrow(i, 0) == 1) atom->SetAttribute("radical", "divalent1"); else if (_mol->getAtomRadical_NoThrow(i, 0) == 2) atom->SetAttribute("radical", "monovalent"); else if (_mol->getAtomRadical_NoThrow(i, 0) == 3) atom->SetAttribute("radical", "divalent3"); } if (_mol->getExplicitValence(i) > 0) atom->SetAttribute("mrvValence", _mol->getExplicitValence(i)); if (Molecule::shouldWriteHCount(*_mol, i)) { int hcount; try { hcount = _mol->getAtomTotalH(i); } catch (Exception &) { hcount = -1; } if (hcount >= 0) atom->SetAttribute("hydrogenCount", hcount); } } if (have_xyz) { Vec3f &pos = _mol->getAtomXyz(i); if (have_z) { atom->SetDoubleAttribute("x3", pos.x); atom->SetDoubleAttribute("y3", pos.y); atom->SetDoubleAttribute("z3", pos.z); } else { atom->SetDoubleAttribute("x2", pos.x); atom->SetDoubleAttribute("y2", pos.y); } } if (_mol->stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY) { TiXmlElement * atomparity = new TiXmlElement("atomParity"); atom->LinkEndChild(atomparity); QS_DEF(Array<char>, buf); ArrayOutput out(buf); const int *pyramid = _mol->stereocenters.getPyramid(i); if (pyramid[3] == -1) out.printf("a%d a%d a%d a%d", pyramid[0], pyramid[1], pyramid[2], i); else out.printf("a%d a%d a%d a%d", pyramid[0], pyramid[1], pyramid[2], pyramid[3]); buf.push(0); atomparity->SetAttribute("atomRefs4", buf.ptr()); atomparity->LinkEndChild(new TiXmlText("1")); } } } if (_mol->edgeCount() > 0) { TiXmlElement * bondarray = new TiXmlElement("bondArray"); molecule->LinkEndChild(bondarray); for (i = _mol->edgeBegin(); i != _mol->edgeEnd(); i = _mol->edgeNext(i)) { const Edge &edge = _mol->getEdge(i); TiXmlElement * bond = new TiXmlElement("bond"); bondarray->LinkEndChild(bond); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("a%d a%d", edge.beg, edge.end); buf.push(0); bond->SetAttribute("atomRefs2", buf.ptr()); int order = _mol->getBondOrder(i); if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE) bond->SetAttribute("order", order); else bond->SetAttribute("order", "A"); int dir = _mol->getBondDirection(i); int parity = _mol->cis_trans.getParity(i); if (dir == BOND_UP || dir == BOND_DOWN) { TiXmlElement * bondstereo = new TiXmlElement("bondStereo"); bond->LinkEndChild(bondstereo); bondstereo->LinkEndChild(new TiXmlText((dir == BOND_UP) ? "W" : "H")); } else if (parity != 0) { TiXmlElement * bondstereo = new TiXmlElement("bondStereo"); bond->LinkEndChild(bondstereo); QS_DEF(Array<char>, buf); ArrayOutput out(buf); const int *subst = _mol->cis_trans.getSubstituents(i); out.printf("a%d a%d a%d a%d", subst[0], edge.beg, edge.end, subst[2]); buf.push(0); bondstereo->SetAttribute("atomRefs4", buf.ptr()); bondstereo->LinkEndChild(new TiXmlText((parity == MoleculeCisTrans::CIS) ? "C" : "T")); } } } if (_mol->countSGroups() > 0) { MoleculeSGroups *sgroups = &_mol->sgroups; for (i = _mol->sgroups.begin(); i != _mol->sgroups.end(); i = _mol->sgroups.next(i)) { SGroup &sgroup = sgroups->getSGroup(i); if (sgroup.parent_group == 0) _addSgroupElement(molecule, sgroup); } } TiXmlPrinter printer; _doc->Accept(&printer); _output.printf("%s", printer.CStr()); _doc.release(); } void MoleculeCmlSaver::_addSgroupElement (TiXmlElement *molecule, SGroup &sgroup) { if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { TiXmlElement * sg = new TiXmlElement("molecule"); molecule->LinkEndChild(sg); QS_DEF(Array<char>, buf); ArrayOutput out(buf); out.printf("sg%d", sgroup.original_group); buf.push(0); sg->SetAttribute("id", buf.ptr()); sg->SetAttribute("role", "DataSgroup"); if (sgroup.atoms.size() > 0) { QS_DEF(Array<char>, buf); ArrayOutput out(buf); for (int j = 0; j < sgroup.atoms.size(); j++) out.printf("a%d ", sgroup.atoms[j]); buf.pop(); buf.push(0); sg->SetAttribute("atomRefs", buf.ptr()); } DataSGroup &dsg = (DataSGroup &)sgroup; const char *name = dsg.name.ptr(); if (name != 0 && strlen(name) > 0) { sg->SetAttribute("fieldName", name); } const char *desc = dsg.description.ptr(); if (desc != 0 && strlen(desc) > 0) { sg->SetAttribute("fieldType", desc); } const char *querycode = dsg.querycode.ptr(); if (querycode != 0 && strlen(querycode) > 0) { sg->SetAttribute("queryType", querycode); } const char *queryoper = dsg.queryoper.ptr(); if (queryoper != 0 && strlen(queryoper) > 0) { sg->SetAttribute("queryOp", queryoper); } sg->SetDoubleAttribute("x", dsg.display_pos.x); sg->SetDoubleAttribute("y", dsg.display_pos.y); if (!dsg.detached) { sg->SetAttribute("dataDetached", "false"); } if (dsg.relative) { sg->SetAttribute("placement", "Relative"); } if (dsg.display_units) { sg->SetAttribute("unitsDisplayed", "Unit displayed"); } char tag = dsg.tag; if (tag != 0 && tag != ' ') { sg->SetAttribute("tag", tag); } if (dsg.num_chars > 0) { sg->SetAttribute("displayedChars", dsg.num_chars); } if (dsg.data.size() > 0 && dsg.data[0] != 0) { sg->SetAttribute("fieldData", dsg.data.ptr()); } MoleculeSGroups *sgroups = &_mol->sgroups; for (int i = _mol->sgroups.begin(); i != _mol->sgroups.end(); i = _mol->sgroups.next(i)) { SGroup &sg_child = sgroups->getSGroup(i); if ( (sg_child.parent_group != 0) && (sg_child.parent_group == sgroup.original_group) ) _addSgroupElement(sg, sg_child); } } }���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_dearom.cpp������������������������������������������������0000664�0000000�0000000�00000147257�12710376503�0022455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/query_molecule.h" #include "molecule/molecule_dearom.h" #include "base_c/bitarray.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "graph/filter.h" #include "molecule/molecule.h" #include "molecule/molecule_arom.h" #include "molecule/elements.h" using namespace indigo; // Exceptions IMPL_EXCEPTION(indigo, DearomatizationException, "dearomatization"); IMPL_EXCEPTION2(indigo, NonUniqueDearomatizationException, DearomatizationException, "non-unique dearomatization"); // // Indigo aromaticiy model remarks. // C1=CC2=CC=CC=CC2=C1 is aromatized into c1:c2-c(:c:c:c:c:c:2):c:c:1 but not into // c1cc2cccccc2c1 because single bond is not aromatic. // // O=C1C=CC(=O)C2=C1SC=CS2 is not aromatized to O=c1ccc(=O)c2sccsc12 because // of double bond inside cycle that is prohibited. Big cycle has 4n+2 electrons // but is not treated as aromatic because of this double bond. // static int _dearomatizationParams = Dearomatizer::PARAMS_SAVE_ONE_DEAROMATIZATION; CP_DEF(Dearomatizer); Dearomatizer::Dearomatizer (BaseMolecule &molecule, const int *atom_external_conn, const AromaticityOptions &options) : _graphMatching(molecule), _molecule(molecule), _aromaticGroups(molecule), _options(options), CP_INIT, TL_CP_GET(_aromaticGroupData), //TL_CP_GET(_edgesFixed), //TL_CP_GET(_verticesFixed), TL_CP_GET(_submoleculeMapping) { _edgesFixed.resize(_molecule.edgeEnd()); _verticesFixed.resize(_molecule.vertexEnd()); _verticesFixed.zeroFill(); _connectivityGroups = _aromaticGroups.detectAromaticGroups(atom_external_conn); _initVertices(); _initEdges(); _graphMatching.setFixedInfo(&_edgesFixed, &_verticesFixed); } Dearomatizer::~Dearomatizer () { } void Dearomatizer::setDearomatizationParams (int params) { _dearomatizationParams = params; } // Enumerate all dearomatizations for all connectivity groups void Dearomatizer::enumerateDearomatizations (DearomatizationsStorage &dearomatizations) { dearomatizations.clear(); if (_connectivityGroups == 0) return; _dearomatizations = &dearomatizations; QS_DEF(Molecule, submolecule); dearomatizations.setGroupsCount(_connectivityGroups); dearomatizations.setDearomatizationParams(_dearomatizationParams); _aromaticGroups.constructGroups(dearomatizations, true); for (int group = 0; group < _connectivityGroups; group++) { _activeGroup = group; _prepareGroup(group, submolecule); GrayCodesEnumerator grayCodes(_aromaticGroupData.heteroAtoms.size(), true); do { if (_graphMatching.findMatching()) _processMatching(submolecule, group, grayCodes.getCode()); grayCodes.next(); if (!grayCodes.isDone()) { int heteroAtomToInvert = _aromaticGroupData.heteroAtoms[grayCodes.getBitChangeIndex()]; _fixHeteratom(heteroAtomToInvert, !_verticesFixed.get(heteroAtomToInvert)); } } while (!grayCodes.isDone()); } } void Dearomatizer::_fixHeteratom (int atom_idx, bool toFix) { if (!_verticesFixed.get(atom_idx)) { if (_graphMatching.isVertexInMatching(atom_idx)) _graphMatching.removeVertexFromMatching(atom_idx); _verticesFixed.set(atom_idx); } else _verticesFixed.reset(atom_idx); return; } void Dearomatizer::_initVertices (void) { for (int v_idx = _molecule.vertexBegin(); v_idx < _molecule.vertexEnd(); v_idx = _molecule.vertexNext(v_idx)) { if (_molecule.getAtomAromaticity(v_idx) == ATOM_ALIPHATIC) _verticesFixed.set(v_idx); } } // Find all aromatic bonds void Dearomatizer::_initEdges (void) { for (int e_idx = _molecule.edgeBegin(); e_idx < _molecule.edgeEnd(); e_idx = _molecule.edgeNext(e_idx)) { _edgesFixed.set(e_idx, _molecule.getBondOrder(e_idx) != BOND_AROMATIC); } } void Dearomatizer::_enumerateMatching (void) { // Find strong edge in alternating circle const Edge *edge = 0; int e_idx; bool found = false; for (int i = 0; i < _aromaticGroupData.bonds.size(); i++) { e_idx = _aromaticGroupData.bonds[i]; if (!_edgesFixed.get(e_idx) && _graphMatching.isEdgeMatching(e_idx)) { edge = &(_molecule.getEdge(e_idx)); if (_graphMatching.findAlternatingPath(edge->beg, edge->end, false, false)) { found = true; break; } } } if (!found) { _handleMatching(); return; } const int MAX_PATH_SIZE = 100; int pathSize = _graphMatching.getPathSize(); int path[MAX_PATH_SIZE]; memcpy(path, _graphMatching.getPath(), sizeof(int) * pathSize); // Enumerate all matching with this strong edge _verticesFixed.set(edge->beg); _verticesFixed.set(edge->end); _enumerateMatching(); _verticesFixed.reset(edge->beg); _verticesFixed.reset(edge->end); // Enumerate all matching without this strong edge _graphMatching.setPath(path, pathSize); _graphMatching.setEdgeMatching(e_idx, false); _graphMatching.processPath(); _edgesFixed.set(e_idx); _enumerateMatching(); _edgesFixed.reset(e_idx); _graphMatching.setPath(path, pathSize); _graphMatching.processPath(); _graphMatching.setEdgeMatching(e_idx, true); } void Dearomatizer::_handleMatching (void) { // Add dearomatizations _dearomatizations->addGroupDearomatization(_activeGroup, _graphMatching.getEdgesState()); } void Dearomatizer::_processMatching (Molecule &submolecule, int group, const byte* hetroAtomsState) { // Copy bonds for (int e_idx = submolecule.edgeBegin(); e_idx < submolecule.edgeEnd(); e_idx = submolecule.edgeNext(e_idx)) { if (submolecule.getBondTopology(e_idx) != TOPOLOGY_RING) // Do not change any bond orders that are not in rings continue; const Edge &edge = submolecule.getEdge(e_idx); int supIdx = _molecule.findEdgeIndex(_submoleculeMapping[edge.beg], _submoleculeMapping[edge.end]); if (_graphMatching.isEdgeMatching(supIdx)) submolecule.setBondOrder(e_idx, BOND_DOUBLE); else submolecule.setBondOrder(e_idx, BOND_SINGLE); } // Check aromaticity bool valid = true; if (_options.dearomatize_check) { // Check configuration only if antiaromaticity is not allowed // For example structure c1ccc1 is not aromatic but antiaromatic, and // kekulized form is C1=CC=C1 // Dearomatization without verification can be used for finding kekulized form // that is not necessary aromatic MoleculeAromatizer::aromatizeBonds(submolecule, _options); for (int e_idx = submolecule.edgeBegin(); e_idx < submolecule.edgeEnd(); e_idx = submolecule.edgeNext(e_idx)) { if (submolecule.getBondTopology(e_idx) == TOPOLOGY_RING && submolecule.getBondOrder(e_idx) != BOND_AROMATIC) { valid = false; break; } } } if (valid) { if (_dearomatizationParams == PARAMS_SAVE_ALL_DEAROMATIZATIONS) // Enumerate all equivalent dearomatizations _enumerateMatching(); else if (_dearomatizationParams == PARAMS_SAVE_ONE_DEAROMATIZATION) _handleMatching(); else if (_dearomatizationParams == PARAMS_SAVE_JUST_HETERATOMS) _dearomatizations->addGroupHeteroAtomsState(group, hetroAtomsState); } } void Dearomatizer::_prepareGroup (int group, Molecule &submolecule) { _aromaticGroups.getGroupData(group, DearomatizationsGroups::GET_VERTICES_FILTER | DearomatizationsGroups::GET_HETERATOMS_INDICES, &_aromaticGroupData); Filter filter(_aromaticGroupData.verticesFilter.ptr(), Filter::EQ, 1); submolecule.makeSubmolecule(_molecule, filter, &_submoleculeMapping, NULL, SKIP_ALL); // Remove non-aromatic bonds for (int e_idx = submolecule.edgeBegin(); e_idx < submolecule.edgeEnd(); e_idx = submolecule.edgeNext(e_idx)) { // Keep double bonds too if (submolecule.getBondOrder(e_idx) == BOND_SINGLE) submolecule.removeEdge(e_idx); } for (int i = 0; i < _aromaticGroupData.vertices.size(); i++) { int v_idx = _aromaticGroupData.vertices[i]; if (!_aromaticGroups.isAcceptDoubleBond(v_idx)) _verticesFixed.set(v_idx); else _verticesFixed.reset(v_idx); } for (int i = 0; i < _aromaticGroupData.heteroAtoms.size(); i++) { int hetero_idx = _aromaticGroupData.heteroAtoms[i]; _verticesFixed.set(hetero_idx); } _graphMatching.reset(); _graphMatching.setEdgesMappingPtr(_aromaticGroupData.bondsInvMapping.ptr()); _graphMatching.setVerticesSetPtr(_aromaticGroupData.vertices.ptr(), _aromaticGroupData.vertices.size()); } // // Dearomatizer::DearomatizerGraphMatching // bool Dearomatizer::GraphMatchingFixed::checkVertex (int v_idx) { return !_verticesFixed->get(v_idx); } bool Dearomatizer::GraphMatchingFixed::checkEdge (int e_idx) { return !_edgesFixed->get(e_idx); } void Dearomatizer::GraphMatchingFixed::setFixedInfo ( const Dbitset *edgesFixed, const Dbitset *verticesFixed) { _edgesFixed = edgesFixed; _verticesFixed = verticesFixed; } Dearomatizer::GraphMatchingFixed::GraphMatchingFixed (BaseMolecule &molecule) : GraphPerfectMatching(molecule, USE_VERTICES_SET | USE_EDGES_MAPPING) { } // // Dearomatizations // void DearomatizationsStorage::clear (void) { _heteroAtomsStateArray.clear(); _aromaticGroups.clear(); clearIndices(); clearBondsState(); _dearomParams = Dearomatizer::PARAMS_NO_DEAROMATIZATIONS; } void DearomatizationsStorage::clearIndices (void) { _aromBondsArray.clear(); _heteroAtomsIndicesArray.clear(); } void DearomatizationsStorage::clearBondsState (void) { _dearomBondsStateArray.clear(); for (int i = 0; i < _aromaticGroups.size(); i++) { _aromaticGroups[i].dearomBondsState.count = 0; _aromaticGroups[i].dearomBondsState.offset = 0; } } void DearomatizationsStorage::setGroupsCount (int groupsCount) { _aromaticGroups.resize(groupsCount); _aromaticGroups.zerofill(); } void DearomatizationsStorage::setGroup (int group, int boundsCount, const int *bondsPtr, int heteroAtomsCount, const int *hetroAtoms) { _aromaticGroups[group].aromBondsIndices.count = boundsCount; _aromaticGroups[group].aromBondsIndices.offset = _aromBondsArray.size(); if (_dearomParams == Dearomatizer::PARAMS_SAVE_JUST_HETERATOMS) { _aromaticGroups[group].heteroAtomsIndices.count = heteroAtomsCount; _aromaticGroups[group].heteroAtomsIndices.offset = _heteroAtomsIndicesArray.size(); for (int i = 0; i < heteroAtomsCount; i++) _heteroAtomsIndicesArray.push(hetroAtoms[i]); } else { _aromaticGroups[group].heteroAtomsIndices.count = 0; _aromaticGroups[group].heteroAtomsIndices.offset = _heteroAtomsIndicesArray.size(); } for (int i = 0; i < boundsCount; i++) _aromBondsArray.push(bondsPtr[i]); } void DearomatizationsStorage::addGroupDearomatization (int group, const byte *dearomBondsState) { // Check group int dearomStateSize = bitGetSize(_aromaticGroups[group].aromBondsIndices.count); int expectedOffset = _dearomBondsStateArray.size() - dearomStateSize * _aromaticGroups[group].dearomBondsState.count; if (_aromaticGroups[group].dearomBondsState.count != 0 && _aromaticGroups[group].dearomBondsState.offset != expectedOffset) throw Error("Dearomatizations::addGroupDearomatization: unable to add dearomatization"); if (_aromaticGroups[group].dearomBondsState.count == 0) _aromaticGroups[group].dearomBondsState.offset = _dearomBondsStateArray.size(); // Add dearomatization to group for (int i = 0; i < dearomStateSize; i++) _dearomBondsStateArray.push(dearomBondsState[i]); _aromaticGroups[group].dearomBondsState.count++; } void DearomatizationsStorage::addGroupHeteroAtomsState (int group, const byte *heteroAtomsState) { // Check group int heteroAtomsSize = bitGetSize(_aromaticGroups[group].heteroAtomsIndices.count); int expectedOffset = _heteroAtomsStateArray.size() - heteroAtomsSize * _aromaticGroups[group].heteroAtomsState.count; if (_aromaticGroups[group].heteroAtomsState.count != 0 && _aromaticGroups[group].heteroAtomsState.offset != expectedOffset) throw Error("Dearomatizations::addGroupHeteroAtomsState: unable to add heteroatoms state"); if (_aromaticGroups[group].heteroAtomsState.count == 0) _aromaticGroups[group].heteroAtomsState.offset = _heteroAtomsStateArray.size(); for (int i = 0; i < heteroAtomsSize; i++) _heteroAtomsStateArray.push(heteroAtomsState[i]); _aromaticGroups[group].heteroAtomsState.count++; } // Bonds state for dearomatization int DearomatizationsStorage::getGroupDearomatizationsCount (int group) const { return _aromaticGroups[group].dearomBondsState.count; } byte* DearomatizationsStorage::getGroupDearomatization (int group, int dearomatizationIndex) { int offset = _aromaticGroups[group].dearomBondsState.offset + dearomatizationIndex * bitGetSize(_aromaticGroups[group].aromBondsIndices.count); if(offset >= _dearomBondsStateArray.size()) return 0; return &_dearomBondsStateArray[offset]; } const int* DearomatizationsStorage::getGroupBonds (int group) const { int offset = _aromaticGroups[group].aromBondsIndices.offset; if (offset >= _aromBondsArray.size()) return 0; return &_aromBondsArray[offset]; } int DearomatizationsStorage::getGroupBondsCount (int group) const { return _aromaticGroups[group].aromBondsIndices.count; } int DearomatizationsStorage::getGroupsCount (void) const { return _aromaticGroups.size(); } // Heteroatoms int DearomatizationsStorage::getGroupHeterAtomsStateCount (int group) const { return _aromaticGroups[group].heteroAtomsState.count; } const byte* DearomatizationsStorage::getGroupHeterAtomsState (int group, int index) const { int offset = _aromaticGroups[group].heteroAtomsState.offset + index * bitGetSize(_aromaticGroups[group].heteroAtomsIndices.count); return _heteroAtomsStateArray.ptr() + offset; } const int* DearomatizationsStorage::getGroupHeteroAtoms (int group) const { return _heteroAtomsIndicesArray.ptr() + _aromaticGroups[group].heteroAtomsIndices.offset; } int DearomatizationsStorage::getGroupHeteroAtomsCount (int group) const { return _aromaticGroups[group].heteroAtomsIndices.count; } // I/O void DearomatizationsStorage::saveBinary (Output &output) const { output.writeByte(_dearomParams); output.writePackedShort(_aromaticGroups.size()); if (_dearomParams != Dearomatizer::PARAMS_SAVE_JUST_HETERATOMS) { for (int i = 0; i < _aromaticGroups.size(); i++) { int expectedOffset = 0; if (i != 0) expectedOffset = _aromaticGroups[i - 1].dearomBondsState.offset + _aromaticGroups[i - 1].dearomBondsState.count * bitGetSize(_aromaticGroups[i - 1].aromBondsIndices.count); if (i != 0 && _aromaticGroups[i].dearomBondsState.offset != expectedOffset) throw Error("DearomatizationsStorage::saveBinary: invalid data order #1"); output.writePackedShort(_aromaticGroups[i].dearomBondsState.count); } output.writePackedShort(_dearomBondsStateArray.size()); if (_dearomBondsStateArray.size() != 0) output.write(_dearomBondsStateArray.ptr(), _dearomBondsStateArray.size() * sizeof(byte)); } else { for (int i = 0; i < _aromaticGroups.size(); i++) { int expectedOffset = 0; if (i != 0) expectedOffset = _aromaticGroups[i - 1].heteroAtomsState.offset + _aromaticGroups[i - 1].heteroAtomsState.count * bitGetSize(_aromaticGroups[i - 1].heteroAtomsIndices.count); if (i != 0 && _aromaticGroups[i].heteroAtomsState.offset != expectedOffset) throw Error("DearomatizationsStorage::saveBinary: invalid data order #2"); output.writePackedShort(_aromaticGroups[i].heteroAtomsState.count); } output.writePackedShort(_heteroAtomsStateArray.size()); if (_heteroAtomsStateArray.size() != 0) output.write(_heteroAtomsStateArray.ptr(), _heteroAtomsStateArray.size() * sizeof(byte)); } } void DearomatizationsStorage::loadBinary (Scanner &scanner) { clear(); _dearomParams = scanner.readChar(); short groupsCount = scanner.readPackedShort(); _aromaticGroups.resize(groupsCount); _aromaticGroups.zerofill(); if (_dearomParams != Dearomatizer::PARAMS_SAVE_JUST_HETERATOMS) { for (int i = 0; i < groupsCount; i++) { short count = scanner.readPackedShort(); if (i != 0) _aromaticGroups[i].dearomBondsState.offset = _aromaticGroups[i - 1].dearomBondsState.offset + count; _aromaticGroups[i].dearomBondsState.count = count; } short bondsStateSize = scanner.readPackedShort(); _dearomBondsStateArray.resize(bondsStateSize); if (bondsStateSize != 0) scanner.read(bondsStateSize, _dearomBondsStateArray.ptr()); } else { for (int i = 0; i < groupsCount; i++) { short count = scanner.readPackedShort(); if (i != 0) _aromaticGroups[i].heteroAtomsState.offset = _aromaticGroups[i - 1].heteroAtomsState.offset + count; _aromaticGroups[i].heteroAtomsState.count = count; } short heteroAtomsStateSize = scanner.readPackedShort(); _heteroAtomsStateArray.resize(heteroAtomsStateSize); if (heteroAtomsStateSize) scanner.read(heteroAtomsStateSize, _heteroAtomsStateArray.ptr()); } } IMPL_ERROR2(DearomatizationsStorage, DearomatizationException, "Dearomatization storage"); DearomatizationsStorage::DearomatizationsStorage (void) { _dearomParams = Dearomatizer::PARAMS_NO_DEAROMATIZATIONS; } // // DearomatizationsGroups // IMPL_ERROR2(DearomatizationsGroups, DearomatizationException, "Dearomatization groups"); CP_DEF(DearomatizationsGroups); DearomatizationsGroups::DearomatizationsGroups (BaseMolecule &molecule) : _molecule(molecule), CP_INIT, TL_CP_GET(_vertexAromaticGroupIndex), TL_CP_GET(_vertexIsAcceptDoubleEdge), TL_CP_GET(_vertexIsAcceptSingleEdge), TL_CP_GET(_vertexProcessed), TL_CP_GET(_groupVertices), TL_CP_GET(_groupEdges), TL_CP_GET(_groupHeteroAtoms), TL_CP_GET(_groupData) { } void DearomatizationsGroups::getGroupData (int group, int flags, DearomatizationsGroups::GROUP_DATA *data) { data->bonds.clear(); data->bondsInvMapping.resize(_molecule.edgeEnd()); data->heteroAtoms.clear(); data->vertices.clear(); if (flags & GET_VERTICES_FILTER) { data->verticesFilter.resize(_molecule.vertexEnd()); data->verticesFilter.zerofill(); } for (int v_idx = _molecule.vertexBegin(); v_idx < _molecule.vertexEnd(); v_idx = _molecule.vertexNext(v_idx)) { if (_vertexAromaticGroupIndex[v_idx] != group) continue; data->vertices.push(v_idx); if (flags & GET_VERTICES_FILTER) data->verticesFilter[v_idx] = 1; if (flags & GET_HETERATOMS_INDICES) { // Check if atom have lone pair or vacant orbital int lonepairs; int label = _molecule.getAtomNumber(v_idx); int charge = _molecule.getAtomCharge(v_idx); int radical = _molecule.getAtomRadical_NoThrow(v_idx, -1); // Treat unset charge and radical as zero; // We have checked before in detectAromaticGroups() if (charge == CHARGE_UNKNOWN) charge = 0; if (radical == -1) radical = 0; if (label == -1) throw DearomatizationMatcher::Error("internal error"); int max_conn = Element::getMaximumConnectivity(label, charge, radical, false); int group = Element::group(_molecule.getAtomNumber(v_idx)); int vac = _molecule.getVacantPiOrbitals(group, charge, radical, max_conn, &lonepairs); if (_vertexIsAcceptDoubleEdge[v_idx] && _vertexIsAcceptSingleEdge[v_idx] && (vac > 0 || lonepairs > 0)) data->heteroAtoms.push(v_idx); } } memset(data->bondsInvMapping.ptr(), -1, sizeof(int) * data->bondsInvMapping.size()); for (int e_idx = _molecule.edgeBegin(); e_idx < _molecule.edgeEnd(); e_idx = _molecule.edgeNext(e_idx)) { const Edge &edge = _molecule.getEdge(e_idx); int bond_order = _molecule.getBondOrder(e_idx); if (bond_order == BOND_AROMATIC && _vertexAromaticGroupIndex[edge.beg] == group) { data->bonds.push(e_idx); data->bondsInvMapping[e_idx] = data->bonds.size() - 1; } } } // Construct bondsInvMapping, vertices and heteroAtomsInvMapping void DearomatizationsGroups::getGroupDataFromStorage (DearomatizationsStorage &storage, int group, GROUP_DATA *data) { data->bondsInvMapping.resize(_molecule.edgeEnd()); data->vertices.clear(); data->heteroAtomsInvMapping.resize(_molecule.vertexEnd()); _vertexProcessed.resize(_molecule.vertexEnd()); _vertexProcessed.zerofill(); memset(data->bondsInvMapping.ptr(), -1, sizeof(int) * data->bondsInvMapping.size()); memset(data->heteroAtomsInvMapping.ptr(), -1, sizeof(int) * data->heteroAtomsInvMapping.size()); int bondsCount = storage.getGroupBondsCount(group); const int *bonds = storage.getGroupBonds(group); for (int i = 0; i < bondsCount; i++) { int e_idx = bonds[i]; data->bondsInvMapping[e_idx] = i; const Edge &edge = _molecule.getEdge(e_idx); if (!_vertexProcessed[edge.beg]) { data->vertices.push(edge.beg); _vertexProcessed[edge.beg] = 1; } if (!_vertexProcessed[edge.end]) { data->vertices.push(edge.end); _vertexProcessed[edge.end] = 1; } } int heteroAtomsCount = storage.getGroupHeteroAtomsCount(group); const int *heteroAtoms = storage.getGroupHeteroAtoms(group); for (int i = 0; i < heteroAtomsCount; i++) { int h_idx = heteroAtoms[i]; data->heteroAtomsInvMapping[h_idx] = i; } } int DearomatizationsGroups::detectAromaticGroups (const int *atom_external_conn) { _vertexAromaticGroupIndex.resize(_molecule.vertexEnd()); _vertexIsAcceptDoubleEdge.resize(_molecule.vertexEnd()); _vertexIsAcceptSingleEdge.resize(_molecule.vertexEnd()); memset(_vertexAromaticGroupIndex.ptr(), -1, _vertexAromaticGroupIndex.size() * sizeof(int)); int currentAromaticGroup = 0; QueryMolecule *qmol = 0; if (_molecule.isQueryMolecule()) qmol = &_molecule.asQueryMolecule(); for (int v_idx = _molecule.vertexBegin(); v_idx < _molecule.vertexEnd(); v_idx = _molecule.vertexNext(v_idx)) { if (_vertexAromaticGroupIndex[v_idx] != -1) continue; if ((_molecule.getAtomAromaticity(v_idx) == ATOM_ALIPHATIC) || _molecule.isPseudoAtom(v_idx) || _molecule.isTemplateAtom(v_idx)) continue; if (_molecule.getAtomNumber(v_idx) == -1) continue; if (qmol != 0 && qmol->getAtom(v_idx).hasConstraint(QueryMolecule::ATOM_CHARGE) && qmol->getAtomCharge(v_idx) == CHARGE_UNKNOWN) continue; if (qmol != 0 && qmol->getAtom(v_idx).hasConstraint(QueryMolecule::ATOM_RADICAL) && qmol->getAtomCharge(v_idx) == -1) continue; _vertexAromaticGroupIndex[v_idx] = currentAromaticGroup++; _detectAromaticGroups(v_idx, atom_external_conn); } // Add atoms that are connected to the aromaic group with double bonds // like in O=C1NC=CC=C1 for (int e_idx = _molecule.edgeBegin(); e_idx < _molecule.edgeEnd(); e_idx = _molecule.edgeNext(e_idx)) { const Edge &e = _molecule.getEdge(e_idx); int &g1 = _vertexAromaticGroupIndex[e.beg]; int &g2 = _vertexAromaticGroupIndex[e.end]; if (g1 == g2) continue; if (_molecule.getBondOrder(e_idx) == BOND_DOUBLE) { int dangling_v; if (g1 != -1) { g2 = g1; dangling_v = e.end; } else { g1 = g2; dangling_v = e.beg; } // Handle tricky case with 5-valence Nitrogen: CC1=CC=CC=[N]1=C _vertexIsAcceptDoubleEdge[dangling_v] = false; _vertexIsAcceptSingleEdge[dangling_v] = true; } } _aromaticGroups = currentAromaticGroup; return _aromaticGroups; } // Construct group structure in DearomatizationsStorage void DearomatizationsGroups::constructGroups (DearomatizationsStorage &storage, bool needHeteroAtoms) { if (storage.getGroupsCount() == 0 && _aromaticGroups != 0) storage.setGroupsCount(_aromaticGroups); storage.clearIndices(); for (int group = 0; group < _aromaticGroups; group++) { int flags = 0; if (needHeteroAtoms) flags = GET_HETERATOMS_INDICES; getGroupData(group, flags, &_groupData); storage.setGroup(group, _groupData.bonds.size(), _groupData.bonds.ptr(), _groupData.heteroAtoms.size(), _groupData.heteroAtoms.ptr()); } } int DearomatizationsGroups::_getFixedConnectivitySpecific (int elem, int charge, int min_conn, int n_arom) { if (elem == ELEM_Se && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 2; // common case if (min_conn == 3) // one external bond return 4; // CID 10262587 if (min_conn == 4) // CID 21204858, two single external bonds // CID 14984497, one double aromatic bond return 4; } } else if (elem == ELEM_Se && charge == 1) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 3; // CID 10872228 if (min_conn == 3) // one external bond return 3; // CID 11115581 } } else if (elem == ELEM_As && charge == 0) { if (n_arom == 2) // two aromatic bonds { if (min_conn == 2) // no external bonds return 3; // CID 136132 if (min_conn == 3) // one external bond return 3; // CID 237687 // no other cases known from PubChem } } else if (elem == ELEM_S && charge == 0) { if (n_arom == 2) { if (min_conn == 3) return 4; // CS1=[As]C=N[AsH]1 if (min_conn == 4) return 4; // O=S1N=CC=N1 } } else if (elem == ELEM_N && charge == 0) { if (n_arom == 2 && min_conn == 4) return 5; // 5-valence Nitrogen: CC(c1cc[n](=O)cc1)=O } return -1; } void DearomatizationsGroups::_detectAromaticGroups (int v_idx, const int *atom_external_conn) { int non_aromatic_conn = 0; if (atom_external_conn != 0) non_aromatic_conn = atom_external_conn[v_idx]; const Vertex &vertex = _molecule.getVertex(v_idx); int n_arom = 0; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int e_idx = vertex.neiEdge(i); int bond_order = _molecule.getBondOrder(e_idx); if (bond_order == -1) // Ignore such bonds. // It may be zero bonds from TautomerSuperStructure continue; if (bond_order != BOND_AROMATIC) { non_aromatic_conn += bond_order; continue; } non_aromatic_conn++; n_arom++; int vn_idx = vertex.neiVertex(i); if (_vertexAromaticGroupIndex[vn_idx] != -1) continue; _vertexAromaticGroupIndex[vn_idx] = _vertexAromaticGroupIndex[v_idx]; _detectAromaticGroups(vn_idx, atom_external_conn); } bool impl_h_fixed = false; if (!_molecule.isQueryMolecule()) { Molecule &m = _molecule.asMolecule(); // Check if number of hydrogens are fixed if (atom_external_conn == 0) { int impl_h = m.getImplicitH_NoThrow(v_idx, -1); if (impl_h != -1) { non_aromatic_conn += impl_h; impl_h_fixed = true; } } } int label = _molecule.getAtomNumber(v_idx); int charge = _molecule.getAtomCharge(v_idx); // If radical is undefined then treat it as there are not radical // Because if there were a radical it should have been explicitly marked int radical = _molecule.getAtomRadical_NoThrow(v_idx, 0); int max_connectivity = -1; if (!_molecule.isQueryMolecule()) { Molecule &m = _molecule.asMolecule(); if (atom_external_conn == 0) { if (m.isNitrogenV5(v_idx)) max_connectivity = 5; } else { if (m.isNitrogenV5ForConnectivity(v_idx, non_aromatic_conn)) max_connectivity = non_aromatic_conn; if (m.isNitrogenV5ForConnectivity(v_idx, non_aromatic_conn + 1)) max_connectivity = non_aromatic_conn + 1; } } // Explicit values for specific configurations if (max_connectivity == -1) { max_connectivity = _getFixedConnectivitySpecific(label, charge, non_aromatic_conn, n_arom); if (max_connectivity != -1) impl_h_fixed = true; } // Apply general purpose method if (max_connectivity == -1) max_connectivity = Element::getMaximumConnectivity(label, charge, radical, false); int atom_aromatic_connectivity = max_connectivity - non_aromatic_conn; if (atom_aromatic_connectivity < 0) max_connectivity = Element::getMaximumConnectivity(label, charge, radical, true); atom_aromatic_connectivity = max_connectivity - non_aromatic_conn; if (atom_aromatic_connectivity < 0) throw Error("atom_aromatic_connectivity < 0 on %s having %d drawn bonds, charge %d, and %d radical electrons", Element::toString(label), non_aromatic_conn, charge, radical); _vertexIsAcceptSingleEdge[v_idx] = true; if (atom_aromatic_connectivity > 0) { _vertexIsAcceptDoubleEdge[v_idx] = true; // If number of implicit hydrogens are fixed and double bond is possible then // double bond must exist if (impl_h_fixed) _vertexIsAcceptSingleEdge[v_idx] = false; } else _vertexIsAcceptDoubleEdge[v_idx] = false; } bool* DearomatizationsGroups::getAcceptDoubleBonds (void) { return _vertexIsAcceptDoubleEdge.ptr(); } bool DearomatizationsGroups::isAcceptDoubleBond (int atom) { return _vertexIsAcceptDoubleEdge[atom]; } // // DearomatizationMatcher // IMPL_ERROR2(DearomatizationMatcher, DearomatizationException, "Dearomatization matcher"); CP_DEF(DearomatizationMatcher); DearomatizationMatcher::DearomatizationMatcher (DearomatizationsStorage &dearomatizations, BaseMolecule &molecule, const int *atom_external_conn) : _molecule(molecule), _dearomatizations(dearomatizations), _graphMatchingFixedEdges(molecule), _aromaticGroups(molecule), CP_INIT, TL_CP_GET(_matchedEdges), TL_CP_GET(_matchedEdgesState), TL_CP_GET(_groupExInfo), TL_CP_GET(_verticesInGroup), TL_CP_GET(_verticesAdded), TL_CP_GET(_edges2GroupMapping), TL_CP_GET(_edges2IndexInGroupMapping), TL_CP_GET(_correctEdgesArray), TL_CP_GET(_verticesFixCount), TL_CP_GET(_aromaticGroupsData) { _needPrepare = true; _aromaticGroups.detectAromaticGroups(atom_external_conn); } bool DearomatizationMatcher::isAbleToFixBond (int edge_idx, int type) { if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_NO_DEAROMATIZATIONS) return false; _prepare(); int group = _edges2GroupMapping[edge_idx]; if (group == -1) return false; if (type == BOND_TRIPLE) return false; // Triple bonds aren't supported _prepareGroup(group); if (_dearomatizations.getGroupDearomatizationsCount(group) == 0) return false; int offset = _groupExInfo[group].offsetInEdgesState; byte *groupFixedEdgesPtr = _matchedEdges.ptr() + offset; int indexInGroup = _edges2IndexInGroupMapping[edge_idx]; byte *groupFixedEdgesStatePtr = _matchedEdgesState.ptr() + offset; if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_SAVE_ALL_DEAROMATIZATIONS) { bitSetBit(groupFixedEdgesPtr, indexInGroup, 1); bitSetBit(groupFixedEdgesStatePtr, indexInGroup, type - 1); // Try to find dearomatization with the same edges in all dearomatizations int count = _dearomatizations.getGroupDearomatizationsCount(group); int activeEdgeState = _groupExInfo[group].activeEdgeState; int i; for (i = 0; i < count; i++) { const byte *dearomState = _dearomatizations.getGroupDearomatization(group, (i + activeEdgeState) % count); int nbits = _dearomatizations.getGroupBondsCount(group); if (bitTestEqualityByMask(dearomState, groupFixedEdgesStatePtr, groupFixedEdgesPtr, nbits)) { _groupExInfo[group].activeEdgeState = i; break; // Dearomatization was found } } if (i != count) { _lastAcceptedEdge = edge_idx; _lastAcceptedEdgeType = type; } bitSetBit(groupFixedEdgesPtr, indexInGroup, 0); if (i != count) return true; } else { // Try to use active dearomatizations byte *activeDearom = _dearomatizations.getGroupDearomatization(group, _groupExInfo[group].activeEdgeState); if (bitGetBit(activeDearom, indexInGroup) == type - 1) { bitSetBit(groupFixedEdgesStatePtr, indexInGroup, type - 1); _lastAcceptedEdge = edge_idx; _lastAcceptedEdgeType = type; return true; } // Try to modify current dearomatization _graphMatchingFixedEdges.setEdgesMappingPtr(_edges2IndexInGroupMapping.ptr()); _graphMatchingFixedEdges.setMatchingEdgesPtr(activeDearom); _graphMatchingFixedEdges.setExtraInfo(groupFixedEdgesPtr); if (_fixBondInMatching(group, indexInGroup, type)) { bitSetBit(groupFixedEdgesStatePtr, indexInGroup, type - 1); _lastAcceptedEdge = edge_idx; _lastAcceptedEdgeType = type; return true; } // Try to modify other dearomatizations bitSetBit(groupFixedEdgesPtr, indexInGroup, 1); bitSetBit(groupFixedEdgesStatePtr, indexInGroup, type - 1); int count = _dearomatizations.getGroupDearomatizationsCount(group); for (int i = 0; i < count - 1; i++) { int dearom_idx = (i + 1 + _groupExInfo[group].activeEdgeState) % count; // Get difference between current state and dearomatization state in group if (_tryToChangeActiveIndex(dearom_idx, group, groupFixedEdgesPtr, groupFixedEdgesStatePtr)) { bitSetBit(groupFixedEdgesPtr, indexInGroup, 0); _groupExInfo[group].activeEdgeState = dearom_idx; _lastAcceptedEdge = edge_idx; _lastAcceptedEdgeType = type; return true; } } bitSetBit(groupFixedEdgesPtr, indexInGroup, 0); return false; } return false; } bool DearomatizationMatcher::fixBond (int edge_idx, int type) { if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_NO_DEAROMATIZATIONS) return false; _prepare(); int group = _edges2GroupMapping[edge_idx]; if (group == -1) return false; if (_lastAcceptedEdge != edge_idx || _lastAcceptedEdgeType != type) { if (!isAbleToFixBond(edge_idx, type)) return false; if (_lastAcceptedEdge != edge_idx || _lastAcceptedEdgeType != type) throw Error("DearomatizationMatcher::fixBond: internal error"); } int offset = _groupExInfo[group].offsetInEdgesState; byte *groupFixedEdgesPtr = _matchedEdges.ptr() + offset; byte *groupFixedEdgesStatePtr = _matchedEdgesState.ptr() + offset; int indexInGroup = _edges2IndexInGroupMapping[edge_idx]; bitSetBit(groupFixedEdgesPtr, indexInGroup, 1); if (bitGetBit(groupFixedEdgesStatePtr, indexInGroup) != type - 1) throw Error("DearomatizationMatcher::fixBond: internal error #2"); const Edge &edge = _molecule.getEdge(edge_idx); _verticesFixCount[edge.beg]++; _verticesFixCount[edge.end]++; _lastAcceptedEdge = -1; return true; } void DearomatizationMatcher::unfixBond (int edge_idx) { if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_NO_DEAROMATIZATIONS) return; _prepare(); int group = _edges2GroupMapping[edge_idx]; if (group == -1) return; byte *groupFixedEdgesPtr = _matchedEdges.ptr() + _groupExInfo[group].offsetInEdgesState; bitSetBit(groupFixedEdgesPtr, _edges2IndexInGroupMapping[edge_idx], 0); const Edge &edge = _molecule.getEdge(edge_idx); _verticesFixCount[edge.beg]--; _verticesFixCount[edge.end]--; } void DearomatizationMatcher::unfixBondByAtom (int atom_idx) { if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_NO_DEAROMATIZATIONS) return; _prepare(); if (_verticesFixCount[atom_idx] == 0) return; const Vertex &vertex = _molecule.getVertex(atom_idx); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) unfixBond(vertex.neiEdge(i)); } void DearomatizationMatcher::_prepare (void) { if (!_needPrepare) return; if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_SAVE_JUST_HETERATOMS) { _dearomatizations.clearBondsState(); _aromaticGroups.constructGroups(_dearomatizations, true); } else _aromaticGroups.constructGroups(_dearomatizations, false); int offset = 0; _groupExInfo.resize(_dearomatizations.getGroupsCount()); _edges2IndexInGroupMapping.resize(_molecule.edgeEnd()); _edges2GroupMapping.resize(_molecule.edgeEnd()); memset(_edges2IndexInGroupMapping.ptr(), -1, sizeof(int) * _edges2IndexInGroupMapping.size()); memset(_edges2GroupMapping.ptr(), -1, sizeof(int) * _edges2GroupMapping.size()); _verticesFixCount.resize(_molecule.vertexEnd()); _verticesFixCount.zerofill(); int maxGroupDearomatizations = 0; for (int group = 0; group < _dearomatizations.getGroupsCount(); group++) { _groupExInfo[group].offsetInEdgesState = offset; _groupExInfo[group].activeEdgeState = 0; if (_dearomatizations.getDearomatizationParams() == Dearomatizer::PARAMS_SAVE_JUST_HETERATOMS) _groupExInfo[group].needPrepare = true; else _groupExInfo[group].needPrepare = false; maxGroupDearomatizations = __max(maxGroupDearomatizations, _dearomatizations.getGroupDearomatizationsCount(group)); maxGroupDearomatizations = __max(maxGroupDearomatizations, _dearomatizations.getGroupHeterAtomsStateCount(group)); int edgesInGroup = _dearomatizations.getGroupBondsCount(group); const int *edges = _dearomatizations.getGroupBonds(group); for (int i = 0; i < edgesInGroup; i++) { int edge_idx = edges[i]; _edges2GroupMapping[edge_idx] = group; _edges2IndexInGroupMapping[edge_idx] = i; } offset += bitGetSize(edgesInGroup); } _matchedEdges.resize(offset); _matchedEdges.zerofill(); _matchedEdgesState.resize(_matchedEdges.size()); _correctEdgesArray.resize(_matchedEdges.size()); if (_dearomatizations.getDearomatizationParams() != Dearomatizer::PARAMS_SAVE_ALL_DEAROMATIZATIONS) { _verticesInGroup.reserve(_molecule.vertexEnd()); _verticesAdded.resize(_molecule.vertexEnd()); _verticesAdded.zeroFill(); _generateUsedVertices(); _graphMatchingFixedEdges.setAllVerticesInMatching(); } _lastAcceptedEdge = -1; _lastAcceptedEdgeType = -1; _needPrepare = false; } // Generate used vertices per each group void DearomatizationMatcher::_generateUsedVertices() { for (int group = 0; group < _dearomatizations.getGroupsCount(); group++) { _groupExInfo[group].offsetInVertices = _verticesInGroup.size(); const int *groupBonds = _dearomatizations.getGroupBonds(group); int count = _dearomatizations.getGroupBondsCount(group); for (int i = 0; i < count; i++) { const Edge &edge = _molecule.getEdge(groupBonds[i]); if (!_verticesAdded.get(edge.beg)) { _verticesInGroup.push(edge.beg); _verticesAdded.set(edge.beg); } if (!_verticesAdded.get(edge.end)) { _verticesInGroup.push(edge.end); _verticesAdded.set(edge.end); } } _groupExInfo[group].verticesUsed = _verticesInGroup.size() - _groupExInfo[group].offsetInVertices; } } // Try to modify dearomatizations to have the same fixed bonds bool DearomatizationMatcher::_tryToChangeActiveIndex (int dearom_idx, int group, byte *groupFixedEdgesPtr, byte *groupFixedEdgesStatePtr) { int bondsCount = _dearomatizations.getGroupBondsCount(group); byte *dearomState = _dearomatizations.getGroupDearomatization(group, dearom_idx); bitGetAandBxorNotC(groupFixedEdgesPtr, groupFixedEdgesStatePtr, dearomState, _correctEdgesArray.ptr(), bondsCount); _graphMatchingFixedEdges.setExtraInfo(_correctEdgesArray.ptr()); _graphMatchingFixedEdges.setMatchingEdgesPtr(dearomState); int bytesCount = bitGetSize(bondsCount); for (int i = 0; i < bytesCount; i++) { byte dif = groupFixedEdgesPtr[i] & (groupFixedEdgesStatePtr[i] ^ dearomState[i]); while (dif != 0) { int indexInGroup = bitGetOneLOIndex(dif) + i * 8; if (indexInGroup > bondsCount) return true; if (!_fixBondInMatching(group, indexInGroup, bitGetBit(groupFixedEdgesStatePtr, indexInGroup) + 1)) return false; // Update correct edges _correctEdgesArray[i] = groupFixedEdgesPtr[i] & (groupFixedEdgesStatePtr[i] ^ ~dearomState[i]); dif = groupFixedEdgesPtr[i] & (groupFixedEdgesStatePtr[i] ^ dearomState[i]); } } return true; } bool DearomatizationMatcher::_fixBondInMatching (int group, int indexInGroup, int type) { const int *aromEdges = _dearomatizations.getGroupBonds(group); const Edge &edge = _molecule.getEdge(aromEdges[indexInGroup]); bool found = _graphMatchingFixedEdges.findAlternatingPath(edge.beg, edge.end, type != BOND_SINGLE, type != BOND_SINGLE); if (found) { if (type == BOND_SINGLE) { _graphMatchingFixedEdges.setEdgeMatching(aromEdges[indexInGroup], false); _graphMatchingFixedEdges.processPath(); } else { _graphMatchingFixedEdges.processPath(); _graphMatchingFixedEdges.setEdgeMatching(aromEdges[indexInGroup], true); } return true; } return false; } void DearomatizationMatcher::_prepareGroup (int group) { if (!_groupExInfo[group].needPrepare) return; _groupExInfo[group].needPrepare = false; if (_dearomatizations.getGroupHeteroAtomsCount(group) != 0 && _dearomatizations.getGroupHeterAtomsStateCount(group) == 0) return; // Create mapping from local hetero-atoms indices to atom indices in molecule _aromaticGroups.getGroupDataFromStorage(_dearomatizations, group, &_aromaticGroupsData); GraphMatchingVerticesFixed graphMatchingFixedVertices(_molecule); graphMatchingFixedVertices.setEdgesMappingPtr(_aromaticGroupsData.bondsInvMapping.ptr()); graphMatchingFixedVertices.setVerticesSetPtr(_aromaticGroupsData.vertices.ptr(), _aromaticGroupsData.vertices.size()); graphMatchingFixedVertices.setVerticesMapping(_aromaticGroupsData.heteroAtomsInvMapping.ptr()); graphMatchingFixedVertices.setVerticesAccept(_aromaticGroups.getAcceptDoubleBonds()); // Generate one dearomatization for each hetero-atoms configuration int count = _dearomatizations.getGroupHeterAtomsStateCount(group); int index = 0; do { if (count != 0) { const byte *heteroAtomsState = _dearomatizations.getGroupHeterAtomsState(group, index++); graphMatchingFixedVertices.setVerticesState(heteroAtomsState); } if (!graphMatchingFixedVertices.findMatching()) throw Error("DearomatizationMatcher::_prepareGroup: internal error"); _dearomatizations.addGroupDearomatization(group, graphMatchingFixedVertices.getEdgesState()); graphMatchingFixedVertices.reset(); } while(index < count); } // // DearomatizationMatcher::GraphMatchingEdgeFixed // void DearomatizationMatcher::GraphMatchingEdgeFixed::setExtraInfo (byte *edgesEdges) { _edgesState = edgesEdges; } bool DearomatizationMatcher::GraphMatchingEdgeFixed::checkEdge (int e_idx) { return !bitGetBit(_edgesState, _edgesMapping[e_idx]); } DearomatizationMatcher::GraphMatchingEdgeFixed::GraphMatchingEdgeFixed (BaseMolecule &molecule) : GraphPerfectMatching(molecule, USE_EXTERNAL_EDGES_PTR | USE_EDGES_MAPPING | USE_VERTICES_SET) { _edgesState = NULL; } // // DearomatizationMatcher::GraphMatchingVerticesFixed // bool DearomatizationMatcher::GraphMatchingVerticesFixed::checkVertex (int v_idx) { if (_verticesMapping[v_idx] != -1) return bitGetBit(_verticesState, _verticesMapping[v_idx]) == 1; return _verticesAcceptDoubleBond[v_idx]; } void DearomatizationMatcher::GraphMatchingVerticesFixed::setVerticesState (const byte *verticesState) { _verticesState = verticesState; } void DearomatizationMatcher::GraphMatchingVerticesFixed::setVerticesMapping (int *verticesMapping) { _verticesMapping = verticesMapping; } void DearomatizationMatcher::GraphMatchingVerticesFixed::setVerticesAccept (bool *verticesAcceptDoubleBond) { _verticesAcceptDoubleBond = verticesAcceptDoubleBond; } DearomatizationMatcher::GraphMatchingVerticesFixed::GraphMatchingVerticesFixed (BaseMolecule &molecule) : GraphPerfectMatching(molecule, USE_EDGES_MAPPING | USE_VERTICES_SET) { _verticesState = NULL; _verticesMapping = NULL; _verticesAcceptDoubleBond = NULL; } // // MoleculeDearomatizer // CP_DEF(MoleculeDearomatizer); MoleculeDearomatizer::MoleculeDearomatizer (Molecule &mol, DearomatizationsStorage &dearom) : _dearomatizations(dearom), _mol(mol), CP_INIT, TL_CP_GET(vertex_connectivity) { } void MoleculeDearomatizer::dearomatizeGroup (int group, int dearomatization_index) { byte *bondsState = _dearomatizations.getGroupDearomatization(group, dearomatization_index); const int *bondsMap = _dearomatizations.getGroupBonds(group); int bondsCount = _dearomatizations.getGroupBondsCount(group); for (int i = 0; i < bondsCount; i++) { if (bitGetBit(bondsState, i)) _mol.setBondOrder(bondsMap[i], BOND_DOUBLE, true); else _mol.setBondOrder(bondsMap[i], BOND_SINGLE, true); } } int MoleculeDearomatizer::_getBestDearomatization (int group) { // Select group with more double bonds that means less hydrogens // For example for c1cnn2nnnc2c1 one should select C1=CC2=NN=NN2N=C1 // instead of N1NN2NC=CC=C2N1 int groups = _dearomatizations.getGroupDearomatizationsCount(group); int best_index = -1, best_count = -1; for (int i = 0; i < groups; i++) { int cnt = _countDoubleBonds(group, i); if (cnt > best_count) { best_count = cnt; best_index = i; } } return best_index; } int MoleculeDearomatizer::_countDoubleBonds (int group, int dearomatization_index) { byte *bondsState = _dearomatizations.getGroupDearomatization(group, dearomatization_index); int bondsCount = _dearomatizations.getGroupBondsCount(group); int count = 0; for (int i = 0; i < bondsCount; i++) if (bitGetBit(bondsState, i)) count++; return count; } void MoleculeDearomatizer::restoreHydrogens (int group, int dearomatization_index) { byte *bondsState = _dearomatizations.getGroupDearomatization(group, dearomatization_index); const int *bondsMap = _dearomatizations.getGroupBonds(group); int bondsCount = _dearomatizations.getGroupBondsCount(group); for (int i = 0; i < bondsCount; i++) { const Edge &edge = _mol.getEdge(bondsMap[i]); int order = bitGetBit(bondsState, i) ? 2 : 1; int v_indices[2] = { edge.beg, edge.end }; for (int j = 0; j < 2; j++) { int v = v_indices[j]; if (vertex_connectivity[j] == 0) { // Compute non-aromatic connectivity const Vertex &vertex = _mol.getVertex(v); for (int nei = vertex.neiBegin(); nei != vertex.neiEnd(); nei = vertex.neiNext(nei)) { int nei_edge = vertex.neiEdge(nei); int nei_order = _mol.getBondOrder(nei_edge); if (nei_order != BOND_AROMATIC) vertex_connectivity[v] += nei_order; } } } vertex_connectivity[edge.beg] += order; vertex_connectivity[edge.end] += order; } } bool MoleculeDearomatizer::dearomatizeMolecule (Molecule &mol, const AromaticityOptions &options) { DearomatizationsStorage dst; Dearomatizer dearomatizer(mol, 0, options); dearomatizer.setDearomatizationParams(Dearomatizer::PARAMS_SAVE_ONE_DEAROMATIZATION); dearomatizer.enumerateDearomatizations(dst); MoleculeDearomatizer mol_dearom(mol, dst); bool all_dearomatzied = true; for (int i = 0; i < dst.getGroupsCount(); ++i) { int cnt = dst.getGroupDearomatizationsCount(i); if (cnt == 0) all_dearomatzied = false; else if (cnt > 1 && options.unique_dearomatization) throw NonUniqueDearomatizationException("Dearomatization is not unique"); else mol_dearom.dearomatizeGroup(i, mol_dearom._getBestDearomatization(i)); } return all_dearomatzied; } bool MoleculeDearomatizer::restoreHydrogens (Molecule &mol, const AromaticityOptions &options) { bool found_invalid_aromatic_h = false; for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.isRSite(i) || mol.isPseudoAtom(i) || mol.isTemplateAtom(i)) continue; if (mol.getImplicitH_NoThrow(i, -1) == -1 && mol.getAtomAromaticity(i) == ATOM_AROMATIC) found_invalid_aromatic_h = true; } if (!found_invalid_aromatic_h) return false; DearomatizationsStorage dst; Dearomatizer dearomatizer(mol, 0, options); dearomatizer.setDearomatizationParams(Dearomatizer::PARAMS_SAVE_ONE_DEAROMATIZATION); dearomatizer.enumerateDearomatizations(dst); MoleculeDearomatizer mol_dearom(mol, dst); mol_dearom.vertex_connectivity.clear_resize(mol.vertexEnd()); mol_dearom.vertex_connectivity.zerofill(); bool all_dearomatzied = true; for (int i = 0; i < dst.getGroupsCount(); ++i) { int cnt = dst.getGroupDearomatizationsCount(i); if (cnt == 0) all_dearomatzied = false; else if (cnt > 1 && options.unique_dearomatization) throw NonUniqueDearomatizationException("Dearomatization is not unique. Cannot restore hydrogens."); else mol_dearom.restoreHydrogens(i, mol_dearom._getBestDearomatization(i)); } for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { int conn = mol_dearom.vertex_connectivity[i]; if (mol.isRSite(i) || mol.isPseudoAtom(i) || mol.isTemplateAtom(i)) continue; if (mol.getImplicitH_NoThrow(i, -1) == -1 && conn > 0) { int h = mol.calcImplicitHForConnectivity(i, conn); mol.setImplicitH(i, h); } } return all_dearomatzied; } bool MoleculeDearomatizer::restoreHydrogens (Molecule &mol, bool unambiguous_only) { AromaticityOptions options; options.method = AromaticityOptions::GENERIC; options.unique_dearomatization = unambiguous_only; return MoleculeDearomatizer::restoreHydrogens(mol, options); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_electrons_localizer.cpp�����������������������������������0000664�0000000�0000000�00000065715�12710376503�0025246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_electrons_localizer.h" #include "base_cpp/obj_array.h" #include "molecule/molecule.h" #include "molecule/elements.h" using namespace indigo; enum { BOND_SINGLE_OR_DOUBLE = -100 }; IMPL_ERROR(MoleculeElectronsLocalizer, "Electron localizer"); CP_DEF(MoleculeElectronsLocalizer); MoleculeElectronsLocalizer::MoleculeElectronsLocalizer (Molecule &skeleton) : _skeleton(skeleton), CP_INIT, TL_CP_GET(_extended_skeleton), TL_CP_GET(_edge_mapping), TL_CP_GET(_atom_info), TL_CP_GET(_edges_fixed_type) { _edges_fixed_type.clear_resize(_skeleton.edgeEnd()); _edges_fixed_type.fffill(); _atom_info.clear_resize(_skeleton.vertexEnd()); _extended_skeleton.clear(); _edge_mapping.clear(); _constrained_primary_double_bonds_conn = 0; _constrained_secondary_double_bonds_conn = 0; _constrained_primary_lonepairs = 0; _constrained_secondary_lonepairs = 0; _constrained_primary_atoms = 0; _constrained_secondary_atoms = 0; _constrained_saturated_atoms = 0; _construct(); } void MoleculeElectronsLocalizer::_construct () { QS_DEF(Array<int>, atom_mapping); _extended_skeleton.cloneGraph(_skeleton, &atom_mapping); // Find edge mapping between source skeleton and extended skeleton _edge_mapping.resize(_skeleton.edgeEnd()); for (int e = _skeleton.edgeBegin(); e != _skeleton.edgeEnd(); e = _skeleton.edgeNext(e)) { _edge_mapping[e] = Graph::findMappedEdge(_extended_skeleton, _skeleton, e, atom_mapping.ptr()); } // Setup default for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { _AtomInfo &info = _atom_info[v]; info.atom_node = atom_mapping[v]; info.atom_saturated = false; info.atom_fixed = false; info.atom_connectivity_fixed = false; info.fixed_connectivity = -1; info.fixed_lonepairs = -1; info.skeleton_connectivity = -1; // Create and attach orbital node info.orbitals_node = _extended_skeleton.addVertex(); info.orbitals_edge = _extended_skeleton.addEdge(info.atom_node, info.orbitals_node); } _constructBMatchingFinder(); _setupAtomProperties(); _setupBMatchingNodes(); _setupBMatchingEdges(); _zc_atoms_connectivity = 0; _zc_lonepairs = 0; for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { _AtomInfo &info = _atom_info[v]; _zc_atoms_connectivity += __max(info.zc_connectivity, 0); _zc_lonepairs += info.zc_lonepairs; // Store initial maximum add connectivity info.max_add_connectivity0 = info.max_add_connectivity; } // Saturate atom that can't be unsaturated for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { if (!_canAtomBeUnsaturated(v)) _fixAtomSaturated(v); } } void MoleculeElectronsLocalizer::_constructBMatchingFinder () { QS_DEF(ObjArray< Array<int> >, nodes_per_set); nodes_per_set.clear(); nodes_per_set.resize(_SET_MAX); for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { _AtomInfo &info = _atom_info[v]; nodes_per_set[_PRIMARY_ATOMS_SET].push(info.atom_node); nodes_per_set[_SECONDARY_ATOMS_SET].push(info.atom_node); nodes_per_set[_CONSTRAINED_ATOMS_SET].push(info.atom_node); nodes_per_set[_PRIMARY_LONEPAIRS_SET].push(info.orbitals_node); nodes_per_set[_SECONDARY_LONEPAIRS_SET].push(info.orbitals_node); nodes_per_set[_CONSTRAINED_LONEPAIRS_SET].push(info.orbitals_node); } QS_DEF(Array<int>, set_per_set); set_per_set.clear_resize(_SET_MAX); set_per_set.fffill(); set_per_set[_PRIMARY_ATOMS_SET] = _SUM_ATOMS_SET; set_per_set[_SECONDARY_ATOMS_SET] = _SUM_ATOMS_SET; set_per_set[_PRIMARY_LONEPAIRS_SET] = _SUM_LONEPAIRS_SET; set_per_set[_SECONDARY_LONEPAIRS_SET] = _SUM_LONEPAIRS_SET; _finder.create(_extended_skeleton, nodes_per_set, &set_per_set); } void MoleculeElectronsLocalizer::_setupAtomProperties () { for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { _AtomInfo &info = _atom_info[v]; int degree = _skeleton.getVertex(v).degree(); info.skeleton_connectivity = _skeleton.getImplicitH(v) + degree; info.max_add_connectivity = 4 - info.skeleton_connectivity - Element::radicalOrbitals(_skeleton.getAtomRadical(v)); if (info.max_add_connectivity < 0) throw Error("Atoms with d-obitals used are not supported"); bool ret = _calcConnectivityAndLoneparis(v, 0, &info.zc_connectivity, &info.zc_lonepairs); if (!ret) throw Error("Invalid atom"); info.zc_connectivity -= info.skeleton_connectivity; } } bool MoleculeElectronsLocalizer::_calcConnectivityAndLoneparis (int atom, int charge, int *connectivity, int *lonepairs) { int label = _skeleton.getAtomNumber(atom); int radical = _skeleton.getAtomRadical(atom); int electrons = Element::electrons(label, 0) - Element::radicalElectrons(radical) - charge; int orbitals = 4 - Element::radicalOrbitals(radical); if (electrons < 0 || electrons > 2 * orbitals) return false; if (electrons > orbitals) { *lonepairs = electrons - orbitals; *connectivity = electrons - 2 * (*lonepairs); } else { *connectivity = electrons; *lonepairs = 0; } return true; } void MoleculeElectronsLocalizer::_setupBMatchingNodeAtom (int atom) { _AtomInfo &info = _atom_info[atom]; int zero_sum_conn = __max(info.zc_connectivity, 0) + info.zc_lonepairs; _finder->setNodeCapacity(info.atom_node, zero_sum_conn, _PRIMARY_ATOMS_SET); int left_conn = info.max_add_connectivity - zero_sum_conn; if (left_conn < 0) left_conn = 0; _finder->setNodeCapacity(info.atom_node, left_conn, _SECONDARY_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, 0, _CONSTRAINED_ATOMS_SET); } void MoleculeElectronsLocalizer::_setupBMatchingNodeOrbital (int atom) { _AtomInfo &info = _atom_info[atom]; _finder->setNodeCapacity(info.orbitals_node, info.zc_lonepairs, _PRIMARY_LONEPAIRS_SET); int left_lonepairs = info.max_add_connectivity - info.zc_lonepairs; if (left_lonepairs < 0) left_lonepairs = 0; _finder->setNodeCapacity(info.orbitals_node, left_lonepairs, _SECONDARY_LONEPAIRS_SET); _finder->setNodeCapacity(info.orbitals_node, 0, _CONSTRAINED_LONEPAIRS_SET); _finder->setMaxEdgeMultiplicity(info.orbitals_edge, info.max_add_connectivity); } void MoleculeElectronsLocalizer::_setupBMatchingNode (int atom) { _setupBMatchingNodeAtom(atom); _setupBMatchingNodeOrbital(atom); } void MoleculeElectronsLocalizer::_setupBMatchingNodes () { for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { _setupBMatchingNode(v); } } void MoleculeElectronsLocalizer::_setupBMatchingEdges () { for (int e = _skeleton.edgeBegin(); e != _skeleton.edgeEnd(); e = _skeleton.edgeNext(e)) { const Edge &edge = _skeleton.getEdge(e); int beg_cap = _atom_info[edge.beg].max_add_connectivity; int end_cap = _atom_info[edge.end].max_add_connectivity; _finder->setMaxEdgeMultiplicity(e, __min(beg_cap, end_cap)); } } void MoleculeElectronsLocalizer::setParameters (int double_bonds, int primary_lonepairs, int secondary_lonepairs) { _double_bonds = double_bonds; _primary_lonepairs = primary_lonepairs; _secondary_lonepairs = secondary_lonepairs; } bool MoleculeElectronsLocalizer::_setConstraintSetForAtoms () { // Calculate and set atoms primary and secondary connectivity int atoms_connectivity = 2 * _double_bonds + _primary_lonepairs + _secondary_lonepairs; int atoms_primary_conn = atoms_connectivity; int atoms_secondary_conn = 0; int zero_conn = _zc_atoms_connectivity + _zc_lonepairs; if (atoms_primary_conn > zero_conn) { atoms_primary_conn = zero_conn; atoms_secondary_conn = atoms_connectivity - zero_conn; } int atoms_prim = atoms_primary_conn - _constrained_primary_atoms - _constrained_primary_double_bonds_conn; int atoms_sec = atoms_secondary_conn - _constrained_secondary_atoms - _constrained_secondary_double_bonds_conn; if (atoms_prim < 0 || atoms_sec < 0) return false; int atoms_sum = atoms_prim + atoms_sec - _constrained_saturated_atoms; if (atoms_sum < 0) return false; _finder->setNodeSetCapacity(_PRIMARY_ATOMS_SET, atoms_prim); _finder->setNodeSetCapacity(_SECONDARY_ATOMS_SET, atoms_sec); _finder->setNodeSetCapacity(_SUM_ATOMS_SET, atoms_sum); _finder->setNodeSetCapacity(_CONSTRAINED_ATOMS_SET, _constrained_saturated_atoms + _constrained_primary_atoms + _constrained_secondary_atoms); return true; } bool MoleculeElectronsLocalizer::_setConstraintSetForLonepairs ( bool only_check_possibility) { // Calculate and set lonepairs primary and secondary connectivity int lp_prim = _primary_lonepairs - _constrained_primary_lonepairs; int lp_sec = _secondary_lonepairs - _constrained_secondary_lonepairs; if (lp_prim < 0 || lp_sec < 0) return false; int lp_sum = lp_prim + lp_sec; if (only_check_possibility) lp_sec = lp_prim = lp_sum; _finder->setNodeSetCapacity(_PRIMARY_LONEPAIRS_SET, lp_prim); _finder->setNodeSetCapacity(_SECONDARY_LONEPAIRS_SET, lp_sec); _finder->setNodeSetCapacity(_SUM_LONEPAIRS_SET, lp_sum); _finder->setNodeSetCapacity(_CONSTRAINED_LONEPAIRS_SET, _constrained_primary_lonepairs + _constrained_secondary_lonepairs); return true; } bool MoleculeElectronsLocalizer::localize (bool only_check_possibility) { if (!_setConstraintSetForAtoms()) return false; if (!_setConstraintSetForLonepairs(only_check_possibility)) return false; // Find valid solution int constrained_double_bonds_conn = _constrained_primary_double_bonds_conn + _constrained_secondary_double_bonds_conn; if (constrained_double_bonds_conn % 2 != 0) throw Error("Internal error in localize"); int cardinality = _double_bonds - constrained_double_bonds_conn / 2 + _primary_lonepairs + _secondary_lonepairs; if (only_check_possibility) return _finder->findMatching(cardinality); return _findValidSolution(cardinality); } bool MoleculeElectronsLocalizer::_findValidSolution (int cardinality) { bool found = _finder->findMatching(cardinality); if (!found) return false; // Check if localization is valid int invalid_atom = -1; int ret_code; for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { if ((ret_code = _isLocalizationValid(v)) != OK) { invalid_atom = v; break; } } if (invalid_atom == -1) return true; if (ret_code == LONEPAIRS) return _branchOnLonepairs(cardinality, invalid_atom); else return _branchOnConnectivity(cardinality, invalid_atom); } bool MoleculeElectronsLocalizer::_branchOnLonepairs (int cardinality, int invalid_atom) { _AtomInfo &info = _atom_info[invalid_atom]; if (info.atom_fixed || info.atom_saturated) throw Error("Internal algorithm error in _branchOnLonepairs"); // Set number of lonepairs to zero int max_mult = _finder->getMaxEdgeMultiplicity(info.orbitals_edge); _finder->setMaxEdgeMultiplicity(info.orbitals_edge, 0); bool found = _findValidSolution(cardinality); // Restore _finder->setMaxEdgeMultiplicity(info.orbitals_edge, max_mult); if (found) return true; // Saturate atom _fixAtomSaturated(invalid_atom); bool was_set = _setConstraintSetForAtoms(); if (was_set) found = _findValidSolution(cardinality); // Restore _unfixAtomSaturated(invalid_atom); was_set = _setConstraintSetForAtoms(); if (!was_set) throw Error("Internal algorithm error in _branchOnLonepairs #2"); if (found) return true; return false; } bool MoleculeElectronsLocalizer::_branchOnConnectivity (int cardinality, int invalid_atom) { _AtomInfo &info = _atom_info[invalid_atom]; int max_lp = info.max_add_connectivity - info.fixed_connectivity; int lonepairs[2] = {0, max_lp}; for (int i = 0; i < 2; i++) { int lp = lonepairs[i]; if (lp < 0) continue; // Saturate atom bool f1 = _fixAtomConnectivityAndLonepairs(invalid_atom, info.fixed_connectivity, lp); if (!f1) continue; bool f2 = _setConstraintSetForAtoms(); bool f3 = _setConstraintSetForLonepairs(false); bool found = false; if (f1 && f2 && f3) found = _findValidSolution(cardinality); // Restore _unfixAtomConnectivityAndLonepairs(invalid_atom); f2 = _setConstraintSetForAtoms(); f3 = _setConstraintSetForLonepairs(false); if (!f2 || !f3) throw Error("Internal algorithm error in _branchOnConnectivity"); if (found) return true; } return false; } int MoleculeElectronsLocalizer::_isLocalizationValid (int atom) const { const _AtomInfo &info = _atom_info[atom]; // Check if atom unsaturated then number of lonepairs must be zero int sum_added_connectivity = _finder->getNodeIncidentEdgesCount(info.atom_node); int lonepairs = _finder->getEdgeMultiplicity(info.orbitals_edge); if (sum_added_connectivity != info.max_add_connectivity) { if (lonepairs != 0) return LONEPAIRS; } if (info.atom_connectivity_fixed) { int conn = sum_added_connectivity - lonepairs; if (conn != info.fixed_connectivity) return CONNECTIVITY; } return OK; } void MoleculeElectronsLocalizer::copyBondsAndCharges (Molecule &dest, const Array<int> &mapping) const { // Copy atom charges for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { int dv = mapping[v]; int charge = _getAtomCharge(v); dest.setAtomCharge(dv, charge); } // Copy bond types for (int e = _skeleton.edgeBegin(); e != _skeleton.edgeEnd(); e = _skeleton.edgeNext(e)) { int de = Graph::findMappedEdge(_skeleton, dest, e, mapping.ptr()); int multiplicity; int ftype = _edges_fixed_type[e]; if (ftype != -1) { multiplicity = ftype - 1; } else multiplicity = _finder->getEdgeMultiplicity(e); dest.setBondOrder(de, multiplicity + 1); } } bool MoleculeElectronsLocalizer::fixAtomCharge (int atom, int charge) { int conn, lonepairs; if (!_calcConnectivityAndLoneparis(atom, charge, &conn, &lonepairs)) return false; const _AtomInfo &info = _atom_info[atom]; conn -= info.skeleton_connectivity; if (conn < 0) return false; if (!_fixAtomConnectivityAndLonepairs(atom, conn, lonepairs)) return false; return true; } bool MoleculeElectronsLocalizer::fixAtomConnectivity (int atom, int connectivity) { _AtomInfo &info = _atom_info[atom]; if (info.atom_connectivity_fixed) return false; connectivity -= info.skeleton_connectivity; if (info.atom_fixed && info.fixed_connectivity != connectivity) return false; info.atom_connectivity_fixed = true; info.fixed_connectivity = connectivity; return true; } bool MoleculeElectronsLocalizer::fixBond (int bond, int type) { if (type != BOND_SINGLE && type != BOND_DOUBLE && type != BOND_TRIPLE) throw Error("Only single, double and triple bonds are supported"); if (_edges_fixed_type[bond] != -1) throw Error("Bond has already been fixed"); const Edge &edge = _skeleton.getEdge(bond); if (!_checkAtomBondFixed(edge.beg, type) || !_checkAtomBondFixed(edge.end, type)) return false; _updateAtomBondFixed(edge.beg, type, true); _updateAtomBondFixed(edge.end, type, true); _finder->setMaxEdgeMultiplicity(_edge_mapping[bond], 0); _edges_fixed_type[bond] = type; return true; } bool MoleculeElectronsLocalizer::fixBondSingleDouble (int bond) { if (_edges_fixed_type[bond] != -1) throw Error("Bond has already been fixed"); _finder->setMaxEdgeMultiplicity(_edge_mapping[bond], 1); _edges_fixed_type[bond] = BOND_SINGLE_OR_DOUBLE; return true; } bool MoleculeElectronsLocalizer::_checkAtomBondFixed (int atom, int bond_type) { _AtomInfo &info = _atom_info[atom]; if (info.max_add_connectivity < bond_type - 1) return false; if (info.atom_saturated || info.atom_fixed) { int cap = _finder->getNodeCapacity(info.atom_node, _CONSTRAINED_ATOMS_SET); if (cap < bond_type - 1) return false; } return true; } void MoleculeElectronsLocalizer::_updateAtomBondFixed (int atom, int bond_type, bool fixed) { _AtomInfo &info = _atom_info[atom]; int fc = info.fixed_connectivity, fl = info.fixed_lonepairs; bool was_fixed = info.atom_fixed; if (was_fixed) { _unfixAtomConnectivityAndLonepairs(atom); } int sign = fixed ? 1 : -1; int delta = (bond_type - 1) * sign; int constr_prim_conn = -1, constr_sec_conn = -1; if (fixed) _splitConnectivity(atom, bond_type - 1, &constr_prim_conn, &constr_sec_conn); info.skeleton_connectivity += delta; info.max_add_connectivity -= delta; info.zc_connectivity -= delta; if (!fixed) _splitConnectivity(atom, bond_type - 1, &constr_prim_conn, &constr_sec_conn); if (info.atom_saturated) _constrained_saturated_atoms -= delta; if (was_fixed) { bool ret = _fixAtomConnectivityAndLonepairs(atom, fc - delta, fl); if (!ret) throw Error("Internal error while fixing atom"); } else if (info.atom_saturated) { int cap = _finder->getNodeCapacity(info.atom_node, _CONSTRAINED_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, cap - delta, _CONSTRAINED_ATOMS_SET); } else { _setupBMatchingNodeAtom(atom); } if (info.atom_connectivity_fixed && !info.atom_fixed) info.fixed_connectivity -= delta; _constrained_primary_double_bonds_conn += constr_prim_conn * sign; _constrained_secondary_double_bonds_conn += constr_sec_conn * sign; } void MoleculeElectronsLocalizer::unfixBond (int bond) { if (_edges_fixed_type[bond] == -1) throw Error("Bond wasn't fixed"); int type = _edges_fixed_type[bond]; const Edge &edge = _skeleton.getEdge(bond); if (type != BOND_SINGLE_OR_DOUBLE) { _updateAtomBondFixed(edge.beg, type, false); _updateAtomBondFixed(edge.end, type, false); } int beg_cap = _atom_info[edge.beg].max_add_connectivity0; int end_cap = _atom_info[edge.end].max_add_connectivity0; _finder->setMaxEdgeMultiplicity(_edge_mapping[bond], __min(beg_cap, end_cap)); _edges_fixed_type[bond] = -1; } void MoleculeElectronsLocalizer::unfixAll () { // Unfix bonds for (int e = _skeleton.edgeBegin(); e != _skeleton.edgeEnd(); e = _skeleton.edgeNext(e)) { if (_edges_fixed_type[e] != -1) unfixBond(e); } // Unfix atoms for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { if (_atom_info[v].atom_fixed) unfixAtom(v); } } bool MoleculeElectronsLocalizer::_fixAtomConnectivityAndLonepairs (int atom, int added_connectivity, int lonepairs) { _AtomInfo &info = _atom_info[atom]; if (info.max_add_connectivity < added_connectivity + lonepairs) return false; if (info.atom_fixed) return false; if (info.atom_connectivity_fixed && info.fixed_connectivity != added_connectivity) return false; // Check possibility of fixing: atom should be saturated or // shouldn't have lonepairs if (added_connectivity + lonepairs != info.max_add_connectivity) { if (lonepairs != 0) return false; // If atom isn't saturated then lonepairs must be zero if (info.atom_saturated) return false; // Saturated atom can't be unsaturated } int conn_prim, conn_sec; _splitConnectivity(atom, added_connectivity + lonepairs, &conn_prim, &conn_sec); _constrained_primary_atoms += conn_prim; _constrained_secondary_atoms += conn_sec; _finder->setNodeCapacity(info.atom_node, 0, _PRIMARY_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, 0, _SECONDARY_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, added_connectivity + lonepairs, _CONSTRAINED_ATOMS_SET); int lonepairs_prim, lonepairs_sec; _splitLonepairs(atom, lonepairs, &lonepairs_prim, &lonepairs_sec); _constrained_primary_lonepairs += lonepairs_prim; _constrained_secondary_lonepairs += lonepairs_sec; _finder->setNodeCapacity(info.orbitals_node, 0, _PRIMARY_LONEPAIRS_SET); _finder->setNodeCapacity(info.orbitals_node, 0, _SECONDARY_LONEPAIRS_SET); _finder->setNodeCapacity(info.orbitals_node, lonepairs, _CONSTRAINED_LONEPAIRS_SET); info.atom_fixed = true; info.fixed_connectivity = added_connectivity; info.fixed_lonepairs = lonepairs; if (info.atom_saturated) _constrained_saturated_atoms -= added_connectivity + lonepairs; return true; } void MoleculeElectronsLocalizer::_unfixAtomConnectivityAndLonepairs (int atom) { _AtomInfo &info = _atom_info[atom]; if (!info.atom_fixed) throw Error("Can't unfix atom that wasn't fixed"); int conn_prim, conn_sec; _splitConnectivity(atom, info.fixed_connectivity + info.fixed_lonepairs, &conn_prim, &conn_sec); _constrained_primary_atoms -= conn_prim; _constrained_secondary_atoms -= conn_sec; int lonepairs_prim, lonepairs_sec; _splitLonepairs(atom, info.fixed_lonepairs, &lonepairs_prim, &lonepairs_sec); _constrained_primary_lonepairs -= lonepairs_prim; _constrained_secondary_lonepairs -= lonepairs_sec; if (info.atom_saturated) _constrained_saturated_atoms += info.fixed_connectivity + info.fixed_lonepairs; info.atom_fixed = false; if (!info.atom_connectivity_fixed) info.fixed_connectivity = -1; info.fixed_lonepairs = -1; if (!info.atom_saturated) _setupBMatchingNodeAtom(atom); _setupBMatchingNodeOrbital(atom); } void MoleculeElectronsLocalizer::unfixAtom (int atom) { _AtomInfo &info = _atom_info[atom]; if (!info.atom_fixed && !info.atom_connectivity_fixed) throw Error("Can't unfix atom that wasn't fixed"); info.atom_connectivity_fixed = false; if (!info.atom_fixed) info.fixed_connectivity = -1; else _unfixAtomConnectivityAndLonepairs(atom); } void MoleculeElectronsLocalizer::_splitConnectivity (int atom, int conn, int *prim, int *sec) const { const _AtomInfo &info = _atom_info[atom]; int zc_sum_conn = __max(info.zc_connectivity, 0) + info.zc_lonepairs; if (conn < zc_sum_conn) { *prim = conn; *sec = 0; } else { *prim = zc_sum_conn; *sec = conn - zc_sum_conn; } } void MoleculeElectronsLocalizer::_splitLonepairs (int atom, int lonepairs, int *prim, int *sec) const { const _AtomInfo &info = _atom_info[atom]; if (lonepairs < info.zc_lonepairs) { *prim = lonepairs; *sec = 0; } else { *prim = info.zc_lonepairs; *sec = lonepairs - info.zc_lonepairs; } } void MoleculeElectronsLocalizer::_fixAtomSaturated (int atom) { _AtomInfo &info = _atom_info[atom]; if (info.atom_fixed) throw Error("Such call sequence wasn't expected"); if (info.atom_saturated) return; _finder->setNodeCapacity(info.atom_node, 0, _PRIMARY_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, 0, _SECONDARY_ATOMS_SET); _finder->setNodeCapacity(info.atom_node, info.max_add_connectivity, _CONSTRAINED_ATOMS_SET); _constrained_saturated_atoms += info.max_add_connectivity; info.atom_saturated = true; } void MoleculeElectronsLocalizer::_unfixAtomSaturated (int atom) { _AtomInfo &info = _atom_info[atom]; if (info.atom_fixed) throw Error("Such call sequence wasn't expected"); if (!info.atom_saturated) return; _constrained_saturated_atoms -= info.max_add_connectivity; _setupBMatchingNodeAtom(atom); info.atom_saturated = false; } bool MoleculeElectronsLocalizer::_canAtomBeUnsaturated (int atom) { int zero_charge_electrons = Element::electrons(_skeleton.getAtomNumber(atom), 0); if (zero_charge_electrons > 5) { // If such atom is unsaturated then its charge is more than 2 return false; } return true; } int MoleculeElectronsLocalizer::getLocalizationChargesCount () const { int charges = 0; for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { charges += abs(_getAtomCharge(v)); } return charges; } int MoleculeElectronsLocalizer::_getAtomCharge (int v) const { const _AtomInfo &info = _atom_info[v]; int added_conn, lonepairs; _getAtomConnAndLonepairs(v, added_conn, lonepairs); int label = _skeleton.getAtomNumber(v); int radical = _skeleton.getAtomRadical(v); int conn = added_conn + info.skeleton_connectivity; int electrons = conn + 2 * lonepairs + Element::radicalElectrons(radical); int zc_electrons = Element::electrons(label, 0); return zc_electrons - electrons; } void MoleculeElectronsLocalizer::_getAtomConnAndLonepairs (int atom, int &added_conn, int &lonepairs) const { const _AtomInfo &info = _atom_info[atom]; if (info.atom_fixed) { added_conn = info.fixed_connectivity; lonepairs = info.fixed_lonepairs; } else { added_conn = _finder->getNodeIncidentEdgesCount(info.atom_node); lonepairs = _finder->getEdgeMultiplicity(info.orbitals_edge); added_conn -= lonepairs; } } bool MoleculeElectronsLocalizer::isAllAtomsHaveOctet () const { for (int v = _skeleton.vertexBegin(); v != _skeleton.vertexEnd(); v = _skeleton.vertexNext(v)) { int added_conn, lonepairs; _getAtomConnAndLonepairs(v, added_conn, lonepairs); const _AtomInfo &info = _atom_info[v]; if (added_conn + lonepairs != info.max_add_connectivity) return false; } return true; } ���������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_exact_matcher.cpp�����������������������������������������0000664�0000000�0000000�00000026216�12710376503�0024004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_cis_trans.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_substructure_matcher.h" #include "graph/graph_affine_matcher.h" #include "base_cpp/scanner.h" #include "graph/filter.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeExactMatcher, "molecule exact matcher"); MoleculeExactMatcher::MoleculeExactMatcher (BaseMolecule &query, BaseMolecule &target) : _query(query), _target(target), _ee(target) { flags = 0; rms_threshold = 0; _ee.cb_match_vertex = _matchAtoms; _ee.cb_match_edge = _matchBonds; _ee.cb_embedding = _embedding; _ee.userdata = this; _ee.setSubgraph(query); } bool MoleculeExactMatcher::find () { int i; if ((flags & CONDITION_3D) && !_query.have_xyz) throw Error("cannot do 3D match without XYZ in the query"); for (i = _query.vertexBegin(); i != _query.vertexEnd(); i = _query.vertexNext(i)) { const Vertex &vertex = _query.getVertex(i); if (_query.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && _query.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (_query.getAtomIsotope(i) == 0 || !(flags & CONDITION_ISOTOPE)) _ee.ignoreSubgraphVertex(i); } for (i = _target.vertexBegin(); i != _target.vertexEnd(); i = _target.vertexNext(i)) { const Vertex &vertex = _target.getVertex(i); if (_target.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && _target.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (_target.getAtomIsotope(i) == 0 || !(flags & CONDITION_ISOTOPE)) _ee.ignoreSupergraphVertex(i); } if (flags & CONDITION_FRAGMENTS) { // Basic check: number of edges and vertices must be the same if (_ee.countUnmappedSubgraphVertices() != _ee.countUnmappedSupergraphVertices()) return false; if (_ee.countUnmappedSubgraphEdges() != _ee.countUnmappedSupergraphEdges()) return false; } else { _collectConnectedComponentsInfo(); // Basic check: the query must contain no more fragments than the target int query_components = _query_decomposer->getComponentsCount(); int target_components = _target_decomposer->getComponentsCount(); if (query_components > target_components) return false; } if (_ee.process() == 0) return true; return false; } const int * MoleculeExactMatcher::getQueryMapping () { return _ee.getSubgraphMapping(); } void MoleculeExactMatcher::_collectConnectedComponentsInfo () { // Target vertices filter initialization Filter target_vertices_filter; target_vertices_filter.init(_ee.getSupergraphMapping(), Filter::NEQ, EmbeddingEnumerator::IGNORE); // Target decomposition _target_decomposer.create(_target); _target_decomposer->decompose(&target_vertices_filter); // Query vertices filter initialization Filter query_vertices_filter; query_vertices_filter.init(_ee.getSubgraphMapping(), Filter::NEQ, EmbeddingEnumerator::IGNORE); // Query decomposition _query_decomposer.create(_query); _query_decomposer->decompose(&query_vertices_filter); } bool MoleculeExactMatcher::_matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { BaseMolecule &query = (BaseMolecule &)subgraph; BaseMolecule &target = (BaseMolecule &)supergraph; MoleculeExactMatcher *self = (MoleculeExactMatcher *)userdata; int flags = self->flags; if (!(flags & CONDITION_FRAGMENTS)) { const GraphDecomposer &target_decomposer = self->_target_decomposer.ref(); const GraphDecomposer &query_decomposer = self->_query_decomposer.ref(); int super_component = target_decomposer.getComponent(super_idx); int sub_component = query_decomposer.getComponent(sub_idx); int super_vertices = target_decomposer.getComponentVerticesCount(super_component); int sub_vertices = query_decomposer.getComponentVerticesCount(sub_component); if (super_vertices != sub_vertices) return false; int super_edges = target_decomposer.getComponentEdgesCount(super_component); int sub_edges = query_decomposer.getComponentEdgesCount(sub_component); if (super_edges != sub_edges) return false; } return matchAtoms(query, target, sub_idx, super_idx, flags); } bool MoleculeExactMatcher::_matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { BaseMolecule &query = (BaseMolecule &)subgraph; BaseMolecule &molecule = (BaseMolecule &)supergraph; MoleculeExactMatcher *self = (MoleculeExactMatcher *)userdata; int flags = self->flags; return matchBonds(query, molecule, sub_idx, super_idx, flags); } int MoleculeExactMatcher::_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { MoleculeExactMatcher *self = (MoleculeExactMatcher *)userdata; BaseMolecule &query = (BaseMolecule &)subgraph; BaseMolecule &target = (BaseMolecule &)supergraph; if (self->flags & CONDITION_STEREO) { MoleculeStereocenters &qs = query.stereocenters; MoleculeStereocenters &ts = target.stereocenters; if (!MoleculeStereocenters::checkSub(qs, ts, core_sub, !(self->flags & CONDITION_ISOTOPE))) return 1; if (!MoleculeStereocenters::checkSub(ts, qs, core_super, !(self->flags & CONDITION_ISOTOPE))) return 1; if (!MoleculeCisTrans::checkSub(query, target, core_sub)) return 1; if (!MoleculeAlleneStereo::checkSub(query, target, core_sub)) return 1; if (!MoleculeAlleneStereo::checkSub(target, query, core_super)) return 1; } if (self->flags & CONDITION_3D) { GraphAffineMatcher matcher(subgraph, supergraph, core_sub); matcher.cb_get_xyz = MoleculeSubstructureMatcher::getAtomPos; if (!matcher.match(self->rms_threshold)) return 1; } return 0; } bool MoleculeExactMatcher::_MatchToken::compare (const char *text) const { return strcasecmp(t_text, text) == 0 ? true : false; } void MoleculeExactMatcher::parseConditions (const char *params, int &flags, float &rms_threshold) { if (params == 0) throw Error("zero pointer passed to parseConditions()"); static const _MatchToken token_list[] = { {"NONE", CONDITION_NONE}, {"ELE", CONDITION_ELECTRONS}, {"MAS", CONDITION_ISOTOPE}, {"STE", CONDITION_STEREO}, {"FRA", CONDITION_FRAGMENTS}, {"ALL", CONDITION_ALL} }; flags = CONDITION_NONE; rms_threshold = 0; BufferScanner scanner(params); QS_DEF(Array<char>, word); scanner.skipSpace(); if (scanner.isEOF()) { flags = CONDITION_ALL; return; } while (!scanner.isEOF()) { int i; scanner.readWord(word, 0); scanner.skipSpace(); if (word.size() < 2) throw Error("internal error on token reading"); for (i = 0; i < NELEM(token_list); i++) { if (token_list[i].compare(word.ptr())) flags |= token_list[i].t_flag; else if (word[0] == '-' && token_list[i].compare(word.ptr() + 1)) flags &= ~token_list[i].t_flag; else continue; break; } if (i == NELEM(token_list)) { BufferScanner scanner2(word.ptr()); if (scanner2.tryReadFloat(rms_threshold)) { flags |= CONDITION_3D; return; } throw Error("parseConditions(): unknown token %s", word.ptr()); } } } bool MoleculeExactMatcher::matchAtoms (BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags) { if (query.isRSite(sub_idx) && target.isRSite(super_idx)) return query.getRSiteBits(sub_idx) == target.getRSiteBits(super_idx); if (query.isRSite(sub_idx) || target.isRSite(super_idx)) return false; if (query.isPseudoAtom(sub_idx) && target.isPseudoAtom(super_idx)) { if (strcmp(query.getPseudoAtom(sub_idx), target.getPseudoAtom(super_idx)) != 0) return false; } else if (query.isTemplateAtom(sub_idx) && target.isTemplateAtom(super_idx)) { if (strcmp(query.getTemplateAtom(sub_idx), target.getTemplateAtom(super_idx)) != 0) return false; } else if (!query.isPseudoAtom(sub_idx) && !target.isPseudoAtom(super_idx) && !query.isTemplateAtom(sub_idx) && !target.isTemplateAtom(super_idx)) { if (query.getAtomNumber(sub_idx) != target.getAtomNumber(super_idx)) return false; } else return false; if (flags & CONDITION_ISOTOPE) if (query.getAtomIsotope(sub_idx) != target.getAtomIsotope(super_idx)) return false; if (flags & CONDITION_ELECTRONS) { int qcharge = query.getAtomCharge(sub_idx); int tcharge = target.getAtomCharge(super_idx); if (qcharge == CHARGE_UNKNOWN) qcharge = 0; if (tcharge == CHARGE_UNKNOWN) tcharge = 0; if (qcharge != tcharge) return false; if (!query.isPseudoAtom(sub_idx) && !query.isTemplateAtom(sub_idx)) { if (!query.isQueryMolecule() && !target.isQueryMolecule()) { if (query.getAtomValence(sub_idx) != target.getAtomValence(super_idx)) return false; } int qrad = query.getAtomRadical(sub_idx); int trad = target.getAtomRadical(super_idx); if (qrad == -1) qrad = 0; if (trad == -1) trad = 0; if (qrad != trad) return false; if (query.isQueryMolecule()) { int qarom = query.getAtomAromaticity(sub_idx); int tarom = target.getAtomAromaticity(super_idx); if (qarom != -1 && tarom != -1) if (qarom != tarom) return false; } } } if (flags & CONDITION_STEREO) { int qtype = query.stereocenters.getType(sub_idx); if (qtype != target.stereocenters.getType(super_idx)) return false; } return true; } bool MoleculeExactMatcher::matchBonds (BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags) { if (flags & CONDITION_ELECTRONS) if (query.getBondOrder(sub_idx) != target.getBondOrder(super_idx)) return false; return true; } bool MoleculeExactMatcher::needCoords () { return (flags & CONDITION_3D) != 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_exact_substructure_matcher.cpp����������������������������0000664�0000000�0000000�00000016172�12710376503�0026636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_exact_substructure_matcher.h" #include "molecule/molecule.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeExactSubstructureMatcher, "molecule exact substructure matcher"); MoleculeExactSubstructureMatcher::MoleculeExactSubstructureMatcher (Molecule &query, Molecule &target) : _query(query), _target(target), _ee(target) { flags = 0xFFFFFFFFUL; _ee.cb_match_vertex = _matchAtoms; _ee.cb_match_edge = _matchBonds; _ee.cb_embedding = _embedding; _ee.userdata = this; _ee.setSubgraph(query); } const int * MoleculeExactSubstructureMatcher::getQueryMapping () { return _ee.getSubgraphMapping(); } void MoleculeExactSubstructureMatcher::ignoreTargetAtom (int idx) { _ee.ignoreSupergraphVertex(idx); } bool MoleculeExactSubstructureMatcher::find () { int i; for (i = _query.vertexBegin(); i != _query.vertexEnd(); i = _query.vertexNext(i)) { const Vertex &vertex = _query.getVertex(i); if (_query.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && _query.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (_query.getAtomIsotope(i) == 0 || !(flags & MoleculeExactMatcher::CONDITION_ISOTOPE)) _ee.ignoreSubgraphVertex(i); } for (i = _target.vertexBegin(); i != _target.vertexEnd(); i = _target.vertexNext(i)) { const Vertex &vertex = _target.getVertex(i); if (_target.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && _target.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (_target.getAtomIsotope(i) == 0 || !(flags & MoleculeExactMatcher::CONDITION_ISOTOPE)) _ee.ignoreSupergraphVertex(i); } if (flags & MoleculeExactMatcher::CONDITION_FRAGMENTS) { if (_ee.countUnmappedSubgraphVertices() > _ee.countUnmappedSupergraphVertices()) return false; if (_ee.countUnmappedSubgraphEdges() > _ee.countUnmappedSupergraphEdges()) return false; } else { _collectConnectedComponentsInfo(); // Basic check: the query must contain no more fragments than the target int query_components = _query_decomposer->getComponentsCount(); int target_components = _target_decomposer->getComponentsCount(); if (query_components > target_components) return false; } if (_ee.process() == 0) return true; return false; } bool MoleculeExactSubstructureMatcher::find_withHydrogens () { if (flags & MoleculeExactMatcher::CONDITION_FRAGMENTS) { if (_ee.countUnmappedSubgraphVertices() > _ee.countUnmappedSupergraphVertices()) return false; if (_ee.countUnmappedSubgraphEdges() > _ee.countUnmappedSupergraphEdges()) return false; } else { _collectConnectedComponentsInfo(); // Basic check: the query must contain no more fragments than the target int query_components = _query_decomposer->getComponentsCount(); int target_components = _target_decomposer->getComponentsCount(); if (query_components > target_components) return false; } if (_ee.process() == 0) return true; return false; } void MoleculeExactSubstructureMatcher::_collectConnectedComponentsInfo () { // Target vertices filter initialization Filter target_vertices_filter; target_vertices_filter.init(_ee.getSupergraphMapping(), Filter::NEQ, EmbeddingEnumerator::IGNORE); // Target decomposition _target_decomposer.create(_target); _target_decomposer->decompose(&target_vertices_filter); // Query vertices filter initialization Filter query_vertices_filter; query_vertices_filter.init(_ee.getSubgraphMapping(), Filter::NEQ, EmbeddingEnumerator::IGNORE); // Query decomposition _query_decomposer.create(_query); _query_decomposer->decompose(&query_vertices_filter); } bool MoleculeExactSubstructureMatcher::_matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { Molecule &query = (Molecule &)subgraph; Molecule &target = (Molecule &)supergraph; MoleculeExactSubstructureMatcher *self = (MoleculeExactSubstructureMatcher *)userdata; int flags = self->flags; if (!(flags & MoleculeExactMatcher::CONDITION_FRAGMENTS)) { const GraphDecomposer &target_decomposer = self->_target_decomposer.ref(); const GraphDecomposer &query_decomposer = self->_query_decomposer.ref(); int super_component = target_decomposer.getComponent(super_idx); int sub_component = query_decomposer.getComponent(sub_idx); int super_vertices = target_decomposer.getComponentVerticesCount(super_component); int sub_vertices = query_decomposer.getComponentVerticesCount(sub_component); if (super_vertices != sub_vertices) return false; int super_edges = target_decomposer.getComponentEdgesCount(super_component); int sub_edges = query_decomposer.getComponentEdgesCount(sub_component); if (super_edges != sub_edges) return false; } return MoleculeExactMatcher::matchAtoms(query, target, sub_idx, super_idx, flags); } bool MoleculeExactSubstructureMatcher::_matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { Molecule &query = (Molecule &)subgraph; Molecule &molecule = (Molecule &)supergraph; MoleculeExactSubstructureMatcher *self = (MoleculeExactSubstructureMatcher *)userdata; int flags = self->flags; return MoleculeExactMatcher::matchBonds(query, molecule, sub_idx, super_idx, flags); } int MoleculeExactSubstructureMatcher::_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { MoleculeExactSubstructureMatcher *self = (MoleculeExactSubstructureMatcher *)userdata; Molecule &query = (Molecule &)subgraph; Molecule &target = (Molecule &)supergraph; if (self->flags & MoleculeExactMatcher::CONDITION_STEREO) { MoleculeStereocenters &qs = query.stereocenters; MoleculeStereocenters &ts = target.stereocenters; if (!MoleculeStereocenters::checkSub(qs, ts, core_sub, !(self->flags & MoleculeExactMatcher::CONDITION_ISOTOPE))) return 1; if (!MoleculeStereocenters::checkSub(ts, qs, core_super, !(self->flags & MoleculeExactMatcher::CONDITION_ISOTOPE))) return 1; if (!MoleculeCisTrans::checkSub(query, target, core_sub)) return 1; } return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_fingerprint.cpp�������������������������������������������0000664�0000000�0000000�00000053720�12710376503�0023524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/crc32.h" #include "base_c/bitarray.h" #include "base_cpp/output.h" #include "graph/graph_subtree_enumerator.h" #include "graph/cycle_enumerator.h" #include "graph/subgraph_hash.h" #include "molecule/molecule.h" #include "molecule/molecule_fingerprint.h" #include "molecule/molecule_tautomer.h" #include "molecule/elements.h" #include "molecule/query_molecule.h" using namespace indigo; IMPL_ERROR(MoleculeFingerprintBuilder, "fingerprint builder"); CP_DEF(MoleculeFingerprintBuilder); MoleculeFingerprintBuilder::MoleculeFingerprintBuilder (BaseMolecule &mol, const MoleculeFingerprintParameters ¶meters): cancellation(0), _mol(mol), _parameters(parameters), CP_INIT, TL_CP_GET(_total_fingerprint), TL_CP_GET(_atom_codes), TL_CP_GET(_bond_codes), TL_CP_GET(_atom_codes_empty), TL_CP_GET(_bond_codes_empty), TL_CP_GET(_atom_hydrogens), TL_CP_GET(_atom_charges), TL_CP_GET(_vertex_connectivity), TL_CP_GET(_fragment_vertex_degree), TL_CP_GET(_bond_orders), TL_CP_GET(_ord_hashes) { _total_fingerprint.resize(_parameters.fingerprintSize()); cb_fragment = 0; query = false; skip_ord = false; skip_sim = false; skip_tau = false; skip_ext = false; skip_ext_charge = false; skip_any_atoms = false; skip_any_bonds = false; skip_any_atoms_bonds = false; _ord_hashes.clear(); } void MoleculeFingerprintBuilder::_initHashCalculations (BaseMolecule &mol, const Filter &vfilter) { subgraph_hash.create(mol); _atom_codes.clear_resize(mol.vertexEnd()); _atom_codes_empty.clear_resize(mol.vertexEnd()); _bond_codes.clear_resize(mol.edgeEnd()); _bond_codes_empty.clear_resize(mol.edgeEnd()); for (int i : mol.vertices()) { _atom_codes[i] = _atomCode(mol, i); _atom_codes_empty[i] = 0; } for (int i : mol.edges()) { _bond_codes[i] = _bondCode(mol, i); _bond_codes_empty[i] = 0; } // Count number of hydrogens and find non-carbon atoms _atom_hydrogens.clear_resize(mol.vertexEnd()); _atom_charges.clear_resize(mol.vertexEnd()); for (int i : mol.vertices()) { try { _atom_hydrogens[i] = mol.getAtomMinH(i); } catch (Exception e) { // Set number of hydrogens to zero if anything is undefined _atom_hydrogens[i] = 0; } int charge = mol.getAtomCharge(i); if (charge == CHARGE_UNKNOWN) charge = 0; _atom_charges[i] = charge; } // Calculate vertex connectivity for the original structure (not tau) _vertex_connectivity.clear_resize(mol.vertexEnd()); _vertex_connectivity.zerofill(); for (int e : mol.edges()) { if (_tau_super_structure) if (_tau_super_structure->isZeroedBond(e)) continue; const Edge &edge = mol.getEdge(e); if (!vfilter.valid(edge.beg) || !vfilter.valid(edge.end)) continue; _vertex_connectivity[edge.beg]++; _vertex_connectivity[edge.end]++; } if (query) { QueryMolecule &q = mol.asQueryMolecule(); // For the query structure increase _vertex_ord_degree so that only saturated atoms // has original degree, i.e. to distinguish [CH4], [CH3] from [C] // For [CH4] vectex connectivity is 0 // For [CH3] vectex connectivity is 1 meaning that one external bond is allowed for (int v : mol.vertices()) { if (!vfilter.valid(v)) continue; int ext_conn = q.getAtomMaxExteralConnectivity(v); if (ext_conn == -1) _vertex_connectivity[v] = INT_MAX; else _vertex_connectivity[v] += ext_conn; } } _fragment_vertex_degree.clear_resize(mol.vertexEnd()); _bond_orders.clear_resize(mol.edgeEnd()); _bond_orders.zerofill(); for (int e : mol.edges()) { if (_tau_super_structure) if (_tau_super_structure->isZeroedBond(e)) continue; int order = mol.getBondOrder(e); if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE) _bond_orders[e] = order; else _bond_orders[e] = 1; } } MoleculeFingerprintBuilder::~MoleculeFingerprintBuilder () { } void MoleculeFingerprintBuilder::process () { _total_fingerprint.zerofill(); _makeFingerprint(_mol); } /* * Accepted types: 'sim', 'sub', 'sub-res', 'sub-tau', 'full' */ void MoleculeFingerprintBuilder::parseFingerprintType(const char *type, bool query) { this->query = query; if (type == 0 || *type == 0 || strcasecmp(type, "sim") == 0) { // similarity this->skip_tau = true; this->skip_ext = true; this->skip_ord = true; this->skip_any_atoms = true; this->skip_any_bonds = true; this->skip_any_atoms_bonds = true; } else if (strcasecmp(type, "sub") == 0) { // substructure this->skip_sim = true; this->skip_tau = true; } else if (strcasecmp(type, "sub-res") == 0) { // resonance substructure this->skip_sim = true; this->skip_tau = true; this->skip_ord = true; this->skip_any_atoms = true; this->skip_ext_charge = true; } else if (strcasecmp(type, "sub-tau") == 0) { // tautomer this->skip_ord = true; this->skip_sim = true; // tautomer fingerprint part does already contain all necessary any-bits this->skip_any_atoms = true; this->skip_any_bonds = true; this->skip_any_atoms_bonds = true; } else if (strcasecmp(type, "full") == 0) { if (query) throw Error("there can not be 'full' fingerprint of a query molecule"); // full (non-query) fingerprint, do not skip anything } else throw Error("unknown molecule fingerprint type: %s", type); } bool MoleculeFingerprintBuilder::_handleCycle (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { MoleculeFingerprintBuilder *self = (MoleculeFingerprintBuilder *)context; self->_handleSubgraph(graph, vertices, edges); return true; } void MoleculeFingerprintBuilder::_handleTree (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { MoleculeFingerprintBuilder *self = (MoleculeFingerprintBuilder *)context; self->_handleSubgraph(graph, vertices, edges); } int MoleculeFingerprintBuilder::_maximalSubgraphCriteriaValue (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { BaseMolecule &mol = (BaseMolecule &)graph; int ret = 0; int ni; MoleculeFingerprintBuilder *self = (MoleculeFingerprintBuilder *)context; // Check if fragment has query atoms or query bonds for (ni = 0; ni < vertices.size(); ni++) { int i = vertices[ni]; if (mol.getAtomNumber(i) == -1) break; } bool has_query_atoms = (ni != vertices.size()); for (ni = 0; ni < edges.size(); ni++) { int i = edges[ni]; int bond_order = mol.getBondOrder(i); if (bond_order == -1 || (self->query && mol.asQueryMolecule().aromaticity.canBeAromatic(i) && bond_order != BOND_AROMATIC)) break; } bool has_query_bonds = (ni != edges.size()); if (has_query_atoms) ret |= 1; if (has_query_bonds) ret |= 2; return ret; } int MoleculeFingerprintBuilder::_atomCode (BaseMolecule &mol, int vertex_idx) { if (mol.isPseudoAtom(vertex_idx)) return CRC32::get(mol.getPseudoAtom(vertex_idx)); if (mol.isTemplateAtom(vertex_idx)) return CRC32::get(mol.getTemplateAtom(vertex_idx)); return mol.getAtomNumber(vertex_idx); } int MoleculeFingerprintBuilder::_bondCode (BaseMolecule &mol, int edge_idx) { //MoleculeFingerprintBuilder *self = (MoleculeFingerprintBuilder *)context; //if (self->query && mol.asQueryMolecule().aromaticity.canBeAromatic(edge_idx)) // throw Error("internal: _edge_code for possibly aromatic bond"); return mol.getBondOrder(edge_idx); } dword MoleculeFingerprintBuilder::_canonicalizeFragment (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, bool use_atoms, bool use_bonds, int *different_vertex_count) { if (use_bonds) subgraph_hash->edge_codes = &_bond_codes; else subgraph_hash->edge_codes = &_bond_codes_empty; if (use_atoms) subgraph_hash->vertex_codes = &_atom_codes; else subgraph_hash->vertex_codes = &_atom_codes_empty; subgraph_hash->max_iterations = (edges.size() + 1) / 2; subgraph_hash->calc_different_codes_count = true; dword ret = subgraph_hash->getHash(vertices, edges); if (different_vertex_count != 0) *different_vertex_count = subgraph_hash->getDifferentCodesCount(); return ret; } void MoleculeFingerprintBuilder::_addOrdHashBits (dword hash, int bits_per_fragment) { HashBits hash_bits(hash, bits_per_fragment); auto it = _ord_hashes.find(hash_bits); if (it == _ord_hashes.end()) _ord_hashes.emplace(hash_bits, 1); else _ord_hashes.at(hash_bits)++; } void MoleculeFingerprintBuilder::_calculateFragmentVertexDegree (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges) { for (int i = 0; i < vertices.size(); i++) { int v = vertices[i]; _fragment_vertex_degree[v] = 0; } for (int i = 0; i < edges.size(); i++) { int e = edges[i]; const Edge &edge = mol.getEdge(e); int order = _bond_orders[e]; _fragment_vertex_degree[edge.beg] += order; _fragment_vertex_degree[edge.end] += order; } } int MoleculeFingerprintBuilder::_calculateFragmentExternalConn (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges) { _calculateFragmentVertexDegree(mol, vertices, edges); int sum = 0; for (int i = 0; i < vertices.size(); i++) { int v = vertices[i]; sum += _vertex_connectivity[v] - _fragment_vertex_degree[v]; } return sum; } void MoleculeFingerprintBuilder::_canonicalizeFragmentAndSetBits (BaseMolecule &mol, const Array<int> &vertices, const Array<int> &edges, bool use_atoms, bool use_bonds, int subgraph_type, dword &bits_set) { bool set_sim = false, set_ord = false, set_any = false, set_tau = false; if (subgraph_type == TautomerSuperStructure::ORIGINAL) { // SIM is made of: rings of size up to 6, trees of size up to 4 edges if (use_atoms && use_bonds && !skip_sim && _parameters.sim_qwords > 0) { set_sim = true; if (vertices.size() > 6) set_sim = false; else if (edges.size() == vertices.size() - 1 && edges.size() > 4) set_sim = false; } // ORD and ANY are made of all fragments having more than 2 vertices if (use_atoms && use_bonds) { if (!skip_ord && _parameters.ord_qwords > 0) set_ord = true; } else if (_parameters.any_qwords > 0) { if (use_atoms) { if (!skip_any_bonds) set_any = true; } else if (use_bonds) { if (!skip_any_atoms) set_any = true; } else if (!skip_any_atoms_bonds) set_any = true; } } // TAU is made of fragments without bond types if (!use_bonds && !skip_tau && _parameters.tau_qwords > 0) set_tau = true; if (!set_any && !set_ord && !set_sim && !set_tau) return; // different_vertex_count is equal to the number of orbits // if codes have no collisions int different_vertex_count; dword hash = _canonicalizeFragment(mol, vertices, edges, use_atoms, use_bonds, &different_vertex_count); // Calculate bits count factor based on different_vertex_count int bits_per_fragment; if (2 * vertices.size() > 3 * different_vertex_count) bits_per_fragment = 5; else if (vertices.size() <= 3) bits_per_fragment = 2; else if (vertices.size() >= 5 && vertices.size() != edges.size()) bits_per_fragment = 1; else bits_per_fragment = 2; if (cb_fragment != 0) (*cb_fragment)(mol, vertices, edges, use_atoms, use_bonds, hash); // Set bits only if bits_set doesn't have such bits dword bits_set_src = bits_set; if (!query) bits_set_src = 0; if (set_sim && !(bits_set_src & 0x01)) { _setBits(hash, getSim(), _parameters.fingerprintSizeSim(), 1); bits_set |= 0x01; } if (set_ord && !(bits_set_src & 0x02)) { _addOrdHashBits(hash, bits_per_fragment); // Add extra bits for charged fragments int charged = 0; for (int i = 0; i < vertices.size(); i++) { int v = vertices[i]; if (_atom_charges[v] != 0) charged++; } if (charged != 0) { const dword CHARGE_MASK = 0x526e7e24; // random _addOrdHashBits(hash ^ CHARGE_MASK, bits_per_fragment); } /* * IND-692 disable incorrect fingerprint part (some tests are failed) */ // Add extra bits for fragments with a lot of terminal atoms // const dword CONN_MASK = 0x7e24526e; // random // int ext_conn = _calculateFragmentExternalConn(mol, vertices, edges); // if (ext_conn == 0) // // Increase bits per fragment because this is maximal fragment and they are rare // _addOrdHashBits(hash ^ CONN_MASK, bits_per_fragment + 4); // if (ext_conn <= 1 && vertices.size() > 3) // _addOrdHashBits(hash ^ (CONN_MASK << 1), bits_per_fragment); // if (ext_conn <= 2 && vertices.size() > 4) // _addOrdHashBits(hash ^ (CONN_MASK << 2), bits_per_fragment); bits_set |= 0x02; } // Any part is used only if 'ord' bit wasn't set - 0x02 bit mask is checked if (set_any && !(bits_set_src & 0x04) && !(bits_set_src & 0x02)) { _setBits(hash, getAny(), _parameters.fingerprintSizeAny(), bits_per_fragment); bits_set |= 0x04; } if (set_tau && !(bits_set_src & 0x08)) { _setBits(hash, getTau(), _parameters.fingerprintSizeTau(), 2); bits_set |= 0x08; } } void MoleculeFingerprintBuilder::_handleSubgraph (Graph &graph, const Array<int> &vertices, const Array<int> &edges) { if(cancellation && cancellation->isCancelled()) throw Error("Fingerprint calculation has been cancelled: %s", cancellation->cancelledRequestMessage()); BaseMolecule &mol = (BaseMolecule &)graph; int i; int subgraph_type; if (_tau_super_structure != 0) subgraph_type = _tau_super_structure->getSubgraphType(vertices, edges); else subgraph_type = TautomerSuperStructure::ORIGINAL; if (subgraph_type == TautomerSuperStructure::NONE) return; // Check if fragment has query atoms or query bonds for (i = 0; i < vertices.size(); i++) if (mol.getAtomNumber(vertices[i]) == -1) break; bool has_query_atoms = (i != vertices.size()); for (i = 0; i < edges.size(); i++) { int e = edges[i]; int bond_order = mol.getBondOrder(e); if (bond_order == -1 || (query && mol.asQueryMolecule().aromaticity.canBeAromatic(e) && bond_order != BOND_AROMATIC)) break; } bool has_query_bonds = (i != edges.size()); dword bits_set = 0; if (!has_query_atoms && !has_query_bonds) _canonicalizeFragmentAndSetBits(mol, vertices, edges, true, true, subgraph_type, bits_set); dword bits_set_a = bits_set; if (!query || !has_query_atoms) _canonicalizeFragmentAndSetBits(mol, vertices, edges, true, false, subgraph_type, bits_set_a); dword bits_set_b = bits_set; if (!query || !has_query_bonds) _canonicalizeFragmentAndSetBits(mol, vertices, edges, false, true, subgraph_type, bits_set_b); dword bits_set_ab = (bits_set_a | bits_set_b); _canonicalizeFragmentAndSetBits(mol, vertices, edges, false, false, subgraph_type, bits_set_ab); } void MoleculeFingerprintBuilder::_makeFingerprint (BaseMolecule &mol) { Obj<TautomerSuperStructure> tau_super_structure; BaseMolecule *mol_for_enumeration = &mol; if (!query && _parameters.tau_qwords > 0 && !skip_tau) { tau_super_structure.create(mol.asMolecule()); _tau_super_structure = tau_super_structure.get(); mol_for_enumeration = tau_super_structure.get(); } else _tau_super_structure = 0; if (!skip_ord || !skip_any_atoms || !skip_any_atoms_bonds || !skip_any_bonds || !skip_tau || !skip_sim) { QS_DEF(Filter, vfilter); vfilter.initAll(mol_for_enumeration->vertexEnd()); // remove (possible) hydrogens for (auto v : mol_for_enumeration->vertices()) if (mol_for_enumeration->possibleAtomNumber(v, ELEM_H)) vfilter.hide(v); _initHashCalculations(*mol_for_enumeration, vfilter); CycleEnumerator ce(*mol_for_enumeration); GraphSubtreeEnumerator se(*mol_for_enumeration); ce.vfilter = &vfilter; se.vfilter = &vfilter; bool sim_only = skip_ord && skip_tau && skip_any_atoms && skip_any_atoms_bonds && skip_any_bonds; _is_cycle = true; ce.context = this; ce.max_length = sim_only ? 6 : 8; ce.cb_handle_cycle = _handleCycle; ce.process(); _is_cycle = false; se.context = this; se.min_vertices = 1; se.max_vertices = sim_only ? 5 : 7; se.handle_maximal = false; se.maximal_critera_value_callback = _maximalSubgraphCriteriaValue; se.callback = _handleTree; se.process(); // Set hash bits for (auto it : _ord_hashes) { int bits_per_fragment = it.first.bits_per_fragment + (bitLog2Dword(it.second) - 1); // Heuristic: if a fragment has high frequency and they are close to each other (like in substructure query) // then there should be larger fragment with lower frequency if (bits_per_fragment > 8) bits_per_fragment = 8; _setBits(it.first.hash, getOrd(), _parameters.fingerprintSizeOrd(), bits_per_fragment); } } if (!skip_ext && _parameters.ext) _calcExtraBits(mol); } void MoleculeFingerprintBuilder::_calcExtraBits (BaseMolecule &mol) { int counters[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; for (auto i : mol.vertices()) { if (mol.possibleAtomNumber(i, ELEM_H)) continue; int an = mol.getAtomNumber(i); if (an == ELEM_C) counters[0]++; else if (an == ELEM_N) counters[1]++; else if (an == ELEM_O) counters[2]++; else if (an == ELEM_P) counters[3]++; else if (an == ELEM_S) counters[4]++; else if (Element::isHalogen(an)) counters[5]++; else if (an > ELEM_H) counters[6]++; if (!skip_ext_charge && mol.getAtomCharge(i) != 0 && mol.getAtomCharge(i) != CHARGE_UNKNOWN) counters[7]++; if (mol.getAtomIsotope(i) > 0) counters[8]++; } byte *fp = _total_fingerprint.ptr(); if (counters[0] > 13) // > 13 C fp[0] |= 1; if (counters[0] > 16) // > 16 C fp[0] |= 2; if (counters[0] > 19) // > 19 C fp[0] |= 4; if (counters[1] > 1) // > 1 N fp[0] |= 8; if (counters[1] > 2) // > 2 N fp[0] |= 16; if (counters[2] > 3) // > 3 O fp[0] |= 32; if (counters[2] > 4) // > 4 O fp[0] |= 64; if (counters[3] > 0) // have P fp[0] |= 128; if (counters[4] > 0) // have S fp[1] |= 1; if (counters[4] > 1) // > 1 S fp[1] |= 2; if (counters[5] > 1) // > 1 halogen fp[1] |= 4; if (counters[5] > 2) // > 2 halogen fp[1] |= 8; if (counters[6] > 0) // have rare atoms fp[1] |= 16; if (counters[6] > 1) // > 1 rare atom fp[1] |= 32; if (counters[7] > 0) // have charged atoms fp[1] |= 64; if (counters[8] > 1) // have isotopes fp[1] |= 128; } void MoleculeFingerprintBuilder::_setBits (dword hash, byte *fp, int size, int nbits) { unsigned seed = hash; // fill random bits while (nbits-- > 0) { seed = seed * 0x8088405 + 1; // Uniformly distributed bits unsigned k = (unsigned)(((qword)(size * 8) * seed) / (unsigned)(-1)); //unsigned k = seed % (size * 8); unsigned nbyte = k / 8; unsigned nbit = k - nbyte * 8; fp[nbyte] = fp[nbyte] | (1 << nbit); } } const byte * MoleculeFingerprintBuilder::get () { return _total_fingerprint.ptr(); } byte * MoleculeFingerprintBuilder::getOrd () { return _total_fingerprint.ptr() + _parameters.fingerprintSizeExt(); } byte * MoleculeFingerprintBuilder::getSim () { return _total_fingerprint.ptr() + _parameters.fingerprintSizeExt() + _parameters.fingerprintSizeOrd(); } byte * MoleculeFingerprintBuilder::getTau () { return _total_fingerprint.ptr() + _parameters.fingerprintSizeExt() + _parameters.fingerprintSizeOrd() + _parameters.fingerprintSizeSim(); } byte * MoleculeFingerprintBuilder::getAny () { return _total_fingerprint.ptr() + _parameters.fingerprintSizeExt() + _parameters.fingerprintSizeOrd() + _parameters.fingerprintSizeSim() + _parameters.fingerprintSizeTau(); } int MoleculeFingerprintBuilder::countBits_Sim () { return bitGetOnesCount(getSim(), _parameters.fingerprintSizeSim()); } // // MoleculeFingerprintBuilder::HashBits // MoleculeFingerprintBuilder::HashBits::HashBits (dword hash, int bits_per_fragment) : hash(hash), bits_per_fragment(bits_per_fragment) { } bool MoleculeFingerprintBuilder::HashBits::operator== (const HashBits &right) const { return right.bits_per_fragment == bits_per_fragment && right.hash == hash; } // // MoleculeFingerprintBuilder::Hasher // size_t MoleculeFingerprintBuilder::Hasher::operator () (const HashBits &input) const { return (input.bits_per_fragment << 10) + input.hash; } ������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_inchi.cpp�������������������������������������������������0000664�0000000�0000000�00000021445�12710376503�0022266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_inchi.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "molecule/molecule_inchi_utils.h" #include "molecule/molecule_automorphism_search.h" using namespace indigo; using namespace MoleculeInChILayers; IMPL_ERROR(MoleculeInChI, "InChI canonicalizer"); CP_DEF(MoleculeInChI); MoleculeInChI::MoleculeInChI (Output &output) : _output(output), CP_INIT, TL_CP_GET(_components), TL_CP_GET(_component_indices) { prefix = "Indigo=1.1"; } void MoleculeInChI::outputInChI (Molecule &mol) { _output.printf(prefix); if (mol.vertexCount() < 1) return; // Decompose molecule into components _components.clear(); _components.reserve(mol.countComponents()); QS_DEF(Molecule, component); for (int i = 0; i < mol.countComponents(); i++) { MoleculeInChICompoment &comp = _components.push(); Filter filt(mol.getDecomposition().ptr(), Filter::EQ, i); component.makeSubmolecule(mol, filt, 0, 0); _normalizeMolecule(component); comp.construct(component); } // Sort components _component_indices.clear_resize(_components.size()); for (int i = 0; i < _components.size(); i++) _component_indices[i] = i; _component_indices.qsort(_cmpComponents, this); // Print final InChI string _printInChI(); } // Function that call print function in specified layer for component template <typename Layer> class MoleculeInChI::_ComponentLayerPrintFunction : public _PrintLayerFuncBase { public: _ComponentLayerPrintFunction (Layer MoleculeInChICompoment::*layer, void (Layer::*print)(Array<char> &)) : _layer(layer), _print(print) {} void operator() (MoleculeInChICompoment& component, Array<char> &output) { ((component.*_layer).*_print)(output); } private: Layer MoleculeInChICompoment::*_layer; void (Layer::*_print)(Array<char> &); }; void MoleculeInChI::_printInChI () { // Print formula _ComponentLayerPrintFunction<MainLayerFormula> print_formula(&MoleculeInChICompoment::main_layer_formula, &MainLayerFormula::printFormula); _printInChILayer(print_formula, ".", "", "/"); // Print connections table _ComponentLayerPrintFunction<MainLayerConnections> print_connections(&MoleculeInChICompoment::main_layer_connections, &MainLayerConnections::printConnectionTable); _printInChILayer(print_connections, ";", "*", "/c"); // Print hydrogens _ComponentLayerPrintFunction<HydrogensLayer> print_hydrogens(&MoleculeInChICompoment::hydrogens_layer, &HydrogensLayer::print); _printInChILayer(print_hydrogens, ";", "*", "/h"); // Print cis-trans bonds _ComponentLayerPrintFunction<CisTransStereochemistryLayer> print_cis_trans(&MoleculeInChICompoment::cistrans_stereochemistry_layer, &CisTransStereochemistryLayer::print); _printInChILayer(print_cis_trans, ";", "*", "/b"); // Print stereocenters _ComponentLayerPrintFunction<TetrahedralStereochemistryLayer> print_stereocenters(&MoleculeInChICompoment::tetra_stereochemistry_layer, &TetrahedralStereochemistryLayer::print); bool has_stereo = _printInChILayer(print_stereocenters, ";", "*", "/t"); if (has_stereo) { _ComponentLayerPrintFunction<TetrahedralStereochemistryLayer> print_stereocenters_enan(&MoleculeInChICompoment::tetra_stereochemistry_layer, &TetrahedralStereochemistryLayer::printEnantiomers); _printInChILayer(print_stereocenters_enan, "", 0, "/m"); _output.printf("/s1"); } } bool MoleculeInChI::_printInChILayer (_PrintLayerFuncBase &print_func, const char *delim, const char *multiplier, const char *layer_prefix) { QS_DEF(Array<char>, layer_string); ArrayOutput output(layer_string); QS_DEF(Array<char>, previous_component); previous_component.clear(); QS_DEF(Array<char>, current_component); int index = -1; int same_components_count = 1; bool layer_contain_data = false; previous_component.clear(); previous_component.push(0); current_component.clear(); current_component.push(0); do { index++; bool is_last = (index == _components.size()); if (!is_last) { int comp_index = _component_indices[index]; // Call corresponding printing method print_func(_components[comp_index], current_component); current_component.push(0); if (index == 0) { previous_component.swap(current_component); continue; } } // Compare current string and previous bool equal = (strcmp(current_component.ptr(), previous_component.ptr()) == 0); bool has_value = (strlen(current_component.ptr()) != 0); if (!is_last && equal && has_value) same_components_count++; else { // Print previous component if (output.tell() != 0) output.printf("%s", delim); if (same_components_count > 1 && multiplier != 0) output.printf("%d%s", same_components_count, multiplier); output.printf("%s", previous_component.ptr()); if (same_components_count > 1 && multiplier == 0) { for (int i = 1; i < same_components_count; i++) output.printf("%s", previous_component.ptr()); } if (previous_component.size() != 0) layer_contain_data = true; previous_component.swap(current_component); same_components_count = 1; }; } while (index < _components.size()); output.flush(); if (!layer_contain_data) layer_string.clear(); layer_string.push(0); if (layer_string.size() != 0 && layer_string[0] != 0) { _output.printf(layer_prefix); _output.writeString(layer_string.ptr()); return true; } return false; } int MoleculeInChI::_cmpComponents (int &index1, int &index2, void *context) { MoleculeInChI *self = (MoleculeInChI *)context; MoleculeInChICompoment &comp1 = self->_components[index1]; MoleculeInChICompoment &comp2 = self->_components[index2]; int ret = 0; ret = MainLayerFormula::compareComponentsAtomsCountNoH( comp1.main_layer_formula, comp2.main_layer_formula); if (ret != 0) return ret; ret = MainLayerConnections::compareComponentsConnectionTables( comp1.main_layer_connections, comp2.main_layer_connections); if (ret != 0) return ret; ret = HydrogensLayer::compareComponentsHydrogens( comp1.hydrogens_layer, comp2.hydrogens_layer); if (ret != 0) return ret; ret = CisTransStereochemistryLayer::compareComponents( comp1.cistrans_stereochemistry_layer, comp2.cistrans_stereochemistry_layer); if (ret != 0) return ret; ret = TetrahedralStereochemistryLayer::compareComponents( comp1.tetra_stereochemistry_layer, comp2.tetra_stereochemistry_layer); if (ret != 0) return ret; ret = TetrahedralStereochemistryLayer::compareComponentsEnantiomers( comp1.tetra_stereochemistry_layer, comp2.tetra_stereochemistry_layer); if (ret != 0) return ret; return 0; } void MoleculeInChI::_normalizeMolecule(Molecule &mol) { QS_DEF(Array<int>, ignored); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.convertableToImplicitHydrogen(i)) ignored[i] = 1; for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.getBondTopology(i) == TOPOLOGY_RING) mol.cis_trans.setParity(i, 0); MoleculeAutomorphismSearch of; of.detect_invalid_cistrans_bonds = true; of.detect_invalid_stereocenters = true; of.find_canonical_ordering = false; of.ignored_vertices = ignored.ptr(); of.process(mol); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.cis_trans.getParity(i) != 0 && of.invalidCisTransBond(i)) mol.cis_trans.setParity(i, 0); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY && of.invalidStereocenter(i)) mol.stereocenters.remove(i); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_inchi_component.cpp���������������������������������������0000664�0000000�0000000�00000015630�12710376503�0024347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_inchi_component.h" #include "graph/automorphism_search.h" #include "molecule/elements.h" using namespace indigo; using namespace indigo::MoleculeInChILayers; // Code for debug void (*dbg_handle_canonical_component_cb) (const Molecule &cano_component); void MoleculeInChICompoment::construct (Molecule &original_component) { // Array with all layers AbstractLayer* layers[] = { &main_layer_formula, &main_layer_connections, &hydrogens_layer, &cistrans_stereochemistry_layer, &tetra_stereochemistry_layer }; // Do we really need this code? // The results will be owerwritten later... // Construct layers for original molecule for (int i = 0; i < NELEM(layers); i++) layers[i]->construct(original_component); // Construct canonical molecule _getCanonicalMolecule(original_component, mol); // Reconstruct layers for canonical molecule for (int i = 0; i < NELEM(layers); i++) layers[i]->construct(mol); } void MoleculeInChICompoment::getCanonicalOrdering(Molecule &source_mol, Array<int> &mapping) { QS_DEF(Array<int>, ignored); ignored.clear_resize(source_mol.vertexEnd()); ignored.zerofill(); for (int i = source_mol.vertexBegin(); i < source_mol.vertexEnd(); i = source_mol.vertexNext(i)) if (source_mol.getAtomNumber(i) == ELEM_H && source_mol.getVertex(i).degree() == 1) ignored[i] = 1; AutomorphismSearch as; as.getcanon = true; as.compare_vertex_degree_first = false; as.refine_reverse_degree = true; as.refine_by_sorted_neighbourhood = true; as.ignored_vertices = ignored.ptr(); as.cb_vertex_cmp = _cmpVertex; as.cb_compare_mapped = _cmpMappings; as.cb_check_automorphism = _checkAutomorphism; as.context = (void *)this; as.process(source_mol); as.getCanonicalNumbering(mapping); } int MoleculeInChICompoment::cmpVertex(Graph &graph, int v1, int v2, const void *context) { return _cmpVertex(graph, v1, v2, context); } void MoleculeInChICompoment::_getCanonicalMolecule (Molecule &source_mol, Molecule &cano_mol) { QS_DEF(Array<int>, ignored); ignored.clear_resize(source_mol.vertexEnd()); ignored.zerofill(); for (int i = source_mol.vertexBegin(); i < source_mol.vertexEnd(); i = source_mol.vertexNext(i)) if (source_mol.getAtomNumber(i) == ELEM_H && source_mol.getVertex(i).degree() == 1) ignored[i] = 1; AutomorphismSearch as; as.getcanon = true; as.compare_vertex_degree_first = false; as.refine_reverse_degree = true; as.refine_by_sorted_neighbourhood = true; as.ignored_vertices = ignored.ptr(); as.cb_vertex_cmp = _cmpVertex; as.cb_compare_mapped = _cmpMappings; as.cb_check_automorphism = _checkAutomorphism; as.context = (void *)this; as.process(source_mol); QS_DEF(Array<int>, canonical_order); as.getCanonicalNumbering(canonical_order); for (int i = 0; i < canonical_order.size(); ++i) { printf("%d ", canonical_order[i]); } printf("\n"); cano_mol.makeSubmolecule(source_mol, canonical_order, NULL); if (dbg_handle_canonical_component_cb != NULL) dbg_handle_canonical_component_cb(cano_mol); } int MoleculeInChICompoment::_cmpVertex (Graph &graph, int v1, int v2, const void *context) { Molecule &mol = (Molecule &)graph; const Array<int> &atom_lables_ranks = MoleculeInChIUtils::getLexSortedAtomLablesRanks(); int ret = atom_lables_ranks[mol.getAtomNumber(v1)] - atom_lables_ranks[mol.getAtomNumber(v2)]; if (ret != 0) return ret; // Compare number of bonds int bonds_1 = graph.getVertex(v1).degree();// + a1.implicit_h; int bonds_2 = graph.getVertex(v2).degree();// + a2.implicit_h; ret = bonds_1 - bonds_2; if (ret != 0) return ret; return 0; } int MoleculeInChICompoment::_cmpVertexStereo (Molecule &mol, int v1, int v2, const void *context) { // TODO: Implement as in InChI int diff = mol.stereocenters.getType(v1) - mol.stereocenters.getType(v2); if (diff != 0) return diff; return 0; } bool MoleculeInChICompoment::_checkAutomorphism (Graph &graph, const Array<int> &mapping, const void *context) { Molecule &mol = (Molecule &)graph; // Check bond mapping for (int e_idx = mol.edgeBegin(); e_idx != mol.edgeEnd(); e_idx = mol.edgeNext(e_idx)) { const Edge &edge = mol.getEdge(e_idx); if (mapping[edge.beg] == -1 || mapping[edge.end] == -1) continue; int mapped_idx = mol.findEdgeIndex(mapping[edge.beg], mapping[edge.end]); if (mapped_idx == -1) return false; if (mol.getBondOrder(e_idx) != mol.getBondOrder(mapped_idx)) return false; } MoleculeInChICompoment &self = *(MoleculeInChICompoment *)context; if (!self.hydrogens_layer.checkAutomorphism(mapping)) return false; if (!self.cistrans_stereochemistry_layer.checkAutomorphism(mapping)) return false; if (!self.tetra_stereochemistry_layer.checkAutomorphism(mapping)) return false; return true; } int MoleculeInChICompoment::_cmpMappings (Graph &graph, const Array<int> &mapping1, const Array<int> &mapping2, const void *context) { QS_DEF(Array<int>, inv_mapping1); QS_DEF(Array<int>, inv_mapping2); inv_mapping1.clear_resize(graph.vertexEnd()); inv_mapping2.clear_resize(graph.vertexEnd()); inv_mapping1.fffill(); inv_mapping2.fffill(); for (int i = 0; i < mapping1.size(); i++) { inv_mapping1[mapping1[i]] = i; inv_mapping2[mapping2[i]] = i; } MoleculeInChIUtils::Mapping m1(mapping1, inv_mapping1); MoleculeInChIUtils::Mapping m2(mapping2, inv_mapping2); MoleculeInChICompoment &self = *(MoleculeInChICompoment *)context; int diff = self.main_layer_connections.compareMappings(m1, m2); if (diff != 0) return diff; diff = self.hydrogens_layer.compareMappings(m1, m2); if (diff != 0) return diff; diff = self.cistrans_stereochemistry_layer.compareMappings(m1, m2); if (diff != 0) return diff; diff = self.tetra_stereochemistry_layer.compareMappings(m1, m2); if (diff != 0) return diff; return 0; } ��������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_inchi_layers.cpp������������������������������������������0000664�0000000�0000000�00000055627�12710376503�0023656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_inchi_layers.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "graph/dfs_walk.h" #include "molecule/molecule.h" #include "molecule/molecule_inchi_utils.h" #include "molecule/molecule_stereocenters.h" #include "molecule/elements.h" using namespace indigo; using namespace indigo::MoleculeInChILayers; IMPL_ERROR(AbstractLayer, "InChI layer"); // // AbstractLayer // AbstractLayer::AbstractLayer () : _mol(0) { } void AbstractLayer::construct (Molecule &mol) { _mol = &mol; _construct(); } Molecule& AbstractLayer::_getMolecule () { if (_mol == 0) throw Error("_mol == 0 (internal error)"); return *_mol; } // // MainLayerFormula // void MainLayerFormula::_construct () { _collectAtomsCount(); } void MainLayerFormula::_collectAtomsCount () { Molecule &mol = _getMolecule(); _atoms_count.resize(ELEM_MAX); _atoms_count.zerofill(); int implicit_hydrogens_count = 0; for (int v_idx = mol.vertexBegin(); v_idx != mol.vertexEnd(); v_idx = mol.vertexNext(v_idx)) { implicit_hydrogens_count += mol.getImplicitH(v_idx); _atoms_count[mol.getAtomNumber(v_idx)]++; } _atoms_count[ELEM_H] += implicit_hydrogens_count; } void MainLayerFormula::printFormula (Array<char> &result) { ArrayOutput output(result); bool carbon_present = (_atoms_count[ELEM_C] != 0); if (carbon_present) { _printAtom(output, ELEM_C); _printAtom(output, ELEM_H); } const Array<int> &atom_lables_sorted = MoleculeInChIUtils::getLexSortedAtomLables(); for (int i = 0; i < atom_lables_sorted.size(); i++) { int label = atom_lables_sorted[i]; if (carbon_present && (label == ELEM_C || label == ELEM_H)) continue; _printAtom(output, label); } result.push(0); } void MainLayerFormula::_printAtom (Output &output, int label) const { int count = _atoms_count[label]; if (count != 0) { output.printf("%s", Element::toString(label)); if (count != 1) output.printf("%d", count); } } int MainLayerFormula::compareComponentsAtomsCountNoH (MainLayerFormula &comp1, MainLayerFormula &comp2) { const Array<int> &atom_lables_sorted = MoleculeInChIUtils::getLexSortedAtomLables(); for (int i = 0; i < atom_lables_sorted.size(); i++) { int label = atom_lables_sorted[i]; if (label == ELEM_H) continue; int diff = comp1._atoms_count[label] - comp2._atoms_count[label]; if (diff != 0) return -diff; } return 0; } int MainLayerFormula::compareComponentsTotalHydrogensCount (MainLayerFormula &comp1, MainLayerFormula &comp2) { // Compare total number of hydrogens. // H3 < H2 int diff = comp2._atoms_count[ELEM_H] - comp1._atoms_count[ELEM_H]; if (diff != 0) return diff; return 0; } // // MainLayerConnections // void MainLayerConnections::_construct () { _linearizeConnectionTable(); } void MainLayerConnections::_linearizeConnectionTable () { const Molecule &mol = _getMolecule(); _connection_table.clear(); QS_DEF(Array<int>, nei_array); for (int v_idx = mol.vertexBegin(); v_idx != mol.vertexEnd(); v_idx = mol.vertexNext(v_idx)) { const Vertex &vertex = mol.getVertex(v_idx); nei_array.clear(); for (int nei_idx = vertex.neiBegin(); nei_idx != vertex.neiEnd(); nei_idx = vertex.neiNext(nei_idx)) { int nei_vertex_index = vertex.neiVertex(nei_idx); if (v_idx > nei_vertex_index) nei_array.push(nei_vertex_index); } _connection_table.push(v_idx); MoleculeInChIUtils::stableSmallSort(nei_array, NULL); for (int i = 0; i < nei_array.size(); i++) _connection_table.push(nei_array[i]); } } int MainLayerConnections::compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2) { Molecule &mol = _getMolecule(); // Compare low triangles of connection tables row by row QS_DEF(Array<int>, tmp1); QS_DEF(Array<int>, tmp2); for (int i = 0; i < m1.mapping.size(); i++) { const Vertex &v1 = mol.getVertex(m1.mapping[i]); const Vertex &v2 = mol.getVertex(m2.mapping[i]); tmp1.clear(); for (int j = v1.neiBegin(); j != v1.neiEnd(); j = v1.neiNext(j)) { int v_idx = v1.neiVertex(j); if (m1.inv_mapping[v_idx] == -1) continue; tmp1.push(m1.inv_mapping[v_idx]); } MoleculeInChIUtils::stableSmallSort(tmp1, NULL); tmp2.clear(); for (int j = v2.neiBegin(); j != v2.neiEnd(); j = v2.neiNext(j)) { int v_idx = v2.neiVertex(j); if (m2.inv_mapping[v_idx] == -1) continue; tmp2.push(m2.inv_mapping[v_idx]); } MoleculeInChIUtils::stableSmallSort(tmp2, NULL); // Compare rows if (tmp1.size() != tmp2.size()) throw Error("Internal error: vertices degree must be the same"); for (int j = 0; j < tmp1.size(); j++) { int ret = tmp1[j] - tmp2[j]; if (ret != 0) return -ret; } } return 0; } int MainLayerConnections::compareComponentsConnectionTables (MainLayerConnections &comp1, MainLayerConnections &comp2) { // longer connection table first int size1 = comp1._connection_table.size(); int size2 = comp2._connection_table.size(); int diff = size1 - size2; if (diff != 0) return -diff; // greater connection table first for (int i = 0; i < size1; i++) { int value1 = comp1._connection_table[i]; int value2 = comp2._connection_table[i]; int diff = value1 - value2; if (diff != 0) return -diff; } return 0; } void MainLayerConnections::printConnectionTable (Array<char> &result) { Molecule &cano_mol = _getMolecule(); result.clear(); if (cano_mol.edgeCount() == 0) { result.push(0); return; } ArrayOutput output(result); QS_DEF(Array<int>, vertex_ranks); vertex_ranks.clear_resize(cano_mol.vertexEnd()); for (int i = 0; i < cano_mol.vertexEnd(); i++) vertex_ranks[i] = i; // Set minimum rank to the first vertex int min_degree = cano_mol.vertexEnd(), min_degree_vertex = -1; for (int v_idx = cano_mol.vertexBegin(); v_idx != cano_mol.vertexEnd(); v_idx = cano_mol.vertexNext(v_idx)) { const Vertex &v = cano_mol.getVertex(v_idx); if (min_degree > v.degree()) { min_degree = v.degree(); min_degree_vertex = v_idx; } } vertex_ranks[min_degree_vertex] = -1; DfsWalk dfs_walk(cano_mol); dfs_walk.vertex_ranks = vertex_ranks.ptr(); dfs_walk.walk(); // Calculate size of the descedants of each vertex in DFS-tree const Array<DfsWalk::SeqElem> &sequence = dfs_walk.getSequence(); QS_DEF(Array<int>, descedants_size); descedants_size.clear_resize(cano_mol.vertexEnd()); descedants_size.zerofill(); for (int i = sequence.size() - 1; i >= 0; i--) { const DfsWalk::SeqElem &item = sequence[i]; if (item.parent_vertex != -1) { if (dfs_walk.isClosure(item.parent_edge)) descedants_size[item.parent_vertex] += 1; else { descedants_size[item.idx]++; descedants_size[item.parent_vertex] += descedants_size[item.idx]; } } else descedants_size[item.idx]++; } // DFS-walk based on previous walk but with order based on descedants_size QS_DEF(Array<int>, edge_in_dfs); edge_in_dfs.clear_resize(cano_mol.edgeEnd()); edge_in_dfs.zerofill(); for (int i = sequence.size() - 1; i >= 0; i--) { const DfsWalk::SeqElem &item = sequence[i]; if (item.parent_edge != -1 && !dfs_walk.isClosure(item.parent_edge)) edge_in_dfs[item.parent_edge] = 1; } QS_DEF(Array<int>, vertex_visited); vertex_visited.clear_resize(cano_mol.vertexEnd()); vertex_visited.zerofill(); QS_DEF(Array<int>, vertex_stack); vertex_stack.push(min_degree_vertex); QS_DEF(Array<int>, nei_visited_vertices); QS_DEF(Array<int>, nei_dfs_next_vertices); enum { PRINT_BRACKET = -1, PRINT_COMMA = -2 }; bool need_print_dash = false; while (vertex_stack.size() != 0) { int cur_vertex_index = vertex_stack.pop(); // Print close bracket or comma if necessary if (cur_vertex_index == PRINT_BRACKET) { output.writeString(")"); need_print_dash = false; continue; } else if (cur_vertex_index == PRINT_COMMA) { output.writeString(","); need_print_dash = false; continue; } if (need_print_dash) output.writeString("-"); output.printf("%d", cur_vertex_index + 1); need_print_dash = true; const Vertex &cur_vertex = cano_mol.getVertex(cur_vertex_index); vertex_visited[cur_vertex_index] = 1; // Collect neighbourhood visited vertices and // neighbourhood vertices in DFS-tree nei_visited_vertices.clear(); nei_dfs_next_vertices.clear(); for (int nei_idx = cur_vertex.neiBegin(); nei_idx != cur_vertex.neiEnd(); nei_idx = cur_vertex.neiNext(nei_idx)) { int nei_edge = cur_vertex.neiEdge(nei_idx); int nei_vertex = cur_vertex.neiVertex(nei_idx); if (edge_in_dfs[nei_edge]) { if (!vertex_visited[nei_vertex]) nei_dfs_next_vertices.push(nei_vertex); } else { if (vertex_visited[nei_vertex]) nei_visited_vertices.push(nei_vertex); } } // Sort nei_visited_vertices according to their index MoleculeInChIUtils::stableSmallSort(nei_visited_vertices, NULL); // Sort nei_dfs_next_vertices by descedants_size and then by their index MoleculeInChIUtils::stableSmallSort(nei_dfs_next_vertices, NULL); MoleculeInChIUtils::stableSmallSort(nei_dfs_next_vertices, &descedants_size); int total_neighbours = nei_visited_vertices.size() + nei_dfs_next_vertices.size(); if (total_neighbours > 1) { output.printf("("); need_print_dash = false; } int left_neighbours = total_neighbours; // Print visited neighbourhood vertices for (int i = 0; i < nei_visited_vertices.size(); i++) { if (total_neighbours > 1 && left_neighbours == 1) output.printf(")"); else if (i != 0) output.printf(","); else if (total_neighbours == 1) output.printf("-"); output.printf("%d", nei_visited_vertices[i] + 1); left_neighbours--; } if (total_neighbours >= 2 && nei_dfs_next_vertices.size() > 1 && nei_visited_vertices.size() > 0) output.printf(","); // Add next DFS-vertices to the stack in backward order for (int i = nei_dfs_next_vertices.size() - 1; i >= 0; i--) { vertex_stack.push(nei_dfs_next_vertices[i]); if (total_neighbours > 1 && left_neighbours == i + 1) vertex_stack.push(PRINT_BRACKET); else if (i != 0) vertex_stack.push(PRINT_COMMA); } } result.push(0); } // // HydrogensLayer // void HydrogensLayer::_construct () { Molecule &mol = _getMolecule(); _atom_indices.clear(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) _atom_indices.push(v); // Collect number of immobile and mobile hydrogens _per_atom_immobile.clear_resize(mol.vertexEnd()); _per_atom_immobile.zerofill(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) _per_atom_immobile[v] = mol.getImplicitH(v); } int HydrogensLayer::compareComponentsHydrogens (HydrogensLayer &comp1, HydrogensLayer &comp2) { // Compare number of hydrogens for each atom. // C < CH4 < CH3 < CH2 < ... if (comp1._atom_indices.size() != comp2._atom_indices.size()) // At this step number of atoms should be the same throw Error("Algorithmic error: Number of atoms should be the same"); for (int i = 0; i < comp1._atom_indices.size(); i++) { int v1_idx = comp1._atom_indices[i]; int v2_idx = comp2._atom_indices[i]; int hyd1 = comp1._per_atom_immobile[v1_idx]; int hyd2 = comp2._per_atom_immobile[v2_idx]; int diff = MoleculeInChIUtils::compareHydrogens(hyd1, hyd2); if (diff != 0) return diff; } return 0; } void HydrogensLayer::print (Array<char> &result) { // Print hydrogens sublayer for the main layer ArrayOutput output(result); // Find maximum number of hydrogens const Array<int> &hydrogens = _per_atom_immobile; int max_hydrogens = 0; for (int i = 0; i < hydrogens.size(); i++) if (max_hydrogens < hydrogens[i]) max_hydrogens = hydrogens[i]; // Print atoms indices for each number of hydrogens for (int h_num = 1; h_num <= max_hydrogens; h_num++) { int next_value_in_range = -1; bool print_range = false; for (int i = 0; i < hydrogens.size(); i++) if (hydrogens[i] == h_num) { if (next_value_in_range == i) { next_value_in_range = i + 1; print_range = true; continue; } else { if (print_range) output.printf("-%d", next_value_in_range); if (next_value_in_range != -1) output.printf(","); } output.printf("%d", i + 1); print_range = false; next_value_in_range = i + 1; } if (next_value_in_range == -1) continue; // No atoms have such number of hydrogens // Print last atom index if (print_range) { output.printf("-%d", next_value_in_range); } output.writeString("H"); if (h_num != 1) output.printf("%d", h_num); output.writeString(","); } // Remove last comma if (result.size() != 0) result.pop(); result.push(0); } bool HydrogensLayer::checkAutomorphism (const Array<int> &mapping) { Molecule &mol = _getMolecule(); // Check that atoms have the same number of hydrogens for (int v_idx = mol.vertexBegin(); v_idx != mol.vertexEnd(); v_idx = mol.vertexNext(v_idx)) { int mapped_idx = mapping[v_idx]; if (mapped_idx == -1) continue; int hyd = _per_atom_immobile[v_idx]; int hyd_mapped = _per_atom_immobile[mapped_idx]; if (hyd != hyd_mapped) return false; } return true; } int HydrogensLayer::compareMappings (MoleculeInChIUtils::Mapping &m1, MoleculeInChIUtils::Mapping &m2) { // Compare number of immobile hydrogens for (int i = 0; i < m1.mapping.size(); i++) { int hyd1 = _per_atom_immobile[m1.mapping[i]]; int hyd2 = _per_atom_immobile[m2.mapping[i]]; int diff = MoleculeInChIUtils::compareHydrogens(hyd1, hyd2); if (diff != 0) return diff; } return 0; } // // CisTransStereochemistryLayer // void CisTransStereochemistryLayer::print (Array<char> &result) { // Print hydrogens sublayer for the main layer ArrayOutput output(result); Molecule &mol = _getMolecule(); QS_DEF(Array<int[2]>, dbl); dbl.clear_resize(mol.vertexEnd()); dbl.fffill(); for (int e_idx = mol.edgeBegin(); e_idx != mol.edgeEnd(); e_idx = mol.edgeNext(e_idx)) { if (!bond_is_cis_trans[e_idx]) continue; // This is not cis-trans bond const Edge &e = mol.getEdge(e_idx); int max_vertex = __max(e.beg, e.end); int min_vertex = __min(e.beg, e.end); int (&cp)[2] = dbl[max_vertex]; cp[0] = min_vertex; cp[1] = e_idx; } for (int i = 0; i < mol.vertexEnd(); i++) { if (dbl[i][1] == -1) continue; if (result.size() != 0) output.printf(","); output.printf("%d-%d", i + 1, dbl[i][0] + 1); int parity = MoleculeInChIUtils::getParityInChI(mol, dbl[i][1]); if (parity == -1) output.printf("-"); else output.printf("+"); } result.push(0); } bool CisTransStereochemistryLayer::checkAutomorphism (const Array<int> &mapping) { Molecule &mol = _getMolecule(); Filter edge_filter(bond_is_cis_trans.ptr(), Filter::EQ, 1); if (!MoleculeCisTrans::isAutomorphism(mol, mapping, &edge_filter)) return false; return true; } int CisTransStereochemistryLayer::compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2) { Molecule &mol = _getMolecule(); // Compare cis-trans for double bonds (>X=Y<) and cumulene (>W=X=Y=Z<). // TODO: handle cumulene QS_DEF(Array<int[2]>, dbl1); QS_DEF(Array<int[2]>, dbl2); dbl1.clear_resize(m1.mapping.size()); dbl1.zerofill(); dbl2.clear_resize(m1.mapping.size()); dbl2.zerofill(); for (int e_idx = mol.edgeBegin(); e_idx != mol.edgeEnd(); e_idx = mol.edgeNext(e_idx)) { if (!bond_is_cis_trans[e_idx]) continue; // This is not cis-trans bond const Edge &e = mol.getEdge(e_idx); int max_vertex = __max(e.beg, e.end); int min_vertex = __min(e.beg, e.end); // Get mapped parity int parity1; if (mol.cis_trans.applyMapping(e_idx, m1.mapping.ptr(), false) == MoleculeCisTrans::TRANS) parity1 = 1; else parity1 = 2; int parity2; if (mol.cis_trans.applyMapping(e_idx, m2.mapping.ptr(), false) == MoleculeCisTrans::TRANS) parity2 = 1; else parity2 = 2; int (&cp1)[2] = dbl1[m1.inv_mapping[max_vertex]]; int (&cp2)[2] = dbl2[m1.inv_mapping[max_vertex]]; cp1[0] = m1.inv_mapping[min_vertex]; cp1[1] = parity1; cp2[0] = m2.inv_mapping[min_vertex]; cp2[1] = parity2; } for (int i = 0; i < m1.mapping.size(); i++) { // Compare second vertex int diff = dbl1[i][0] - dbl2[i][0]; if (diff != 0) return diff; // Compare parities diff = dbl1[i][1] - dbl2[i][1]; if (diff != 0) return diff; } return 0; } void CisTransStereochemistryLayer::_construct () { Molecule &mol = _getMolecule(); bond_is_cis_trans.clear_resize(mol.edgeEnd()); bond_is_cis_trans.zerofill(); for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { bond_is_cis_trans[e] = true; if (mol.getBondTopology(e) == TOPOLOGY_RING) bond_is_cis_trans[e] = false; if (mol.cis_trans.getParity(e) == 0) bond_is_cis_trans[e] = false; } } int CisTransStereochemistryLayer::compareComponents ( CisTransStereochemistryLayer &comp1, CisTransStereochemistryLayer &comp2) { // TODO return 0; } // // TetrahedralStereochemistryLayer // void TetrahedralStereochemistryLayer::print (Array<char> &result) { ArrayOutput output(result); Molecule &mol = _getMolecule(); int first_sign = 0; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int sign = _getMappingSign(mol.stereocenters, 0, v); if (first_sign == 0) first_sign = -sign; if (sign != 0) { if (result.size() != 0) output.printf(","); output.printf("%d%c", v + 1, sign * first_sign == 1 ? '+' : '-'); } } result.push(0); } int TetrahedralStereochemistryLayer::_getFirstSign () { // Find first sign Molecule &mol = _getMolecule(); for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int sign = _getMappingSign(mol.stereocenters, 0, v); if (sign != 0) return sign; } return 0; } void TetrahedralStereochemistryLayer::printEnantiomers (Array<char> &result) { ArrayOutput output(result); int sign = _getFirstSign(); if (sign != 0) output.printf("%d", sign == 1 ? 1 : 0); else output.printf("."); result.push(0); } bool TetrahedralStereochemistryLayer::checkAutomorphism (const Array<int> &mapping) { Molecule &mol = _getMolecule(); if (!MoleculeStereocenters::isAutomorphism(mol, mapping)) return false; return true; } int TetrahedralStereochemistryLayer::compareMappings (const MoleculeInChIUtils::Mapping &m1, const MoleculeInChIUtils::Mapping &m2) { Molecule &mol = _getMolecule(); QS_DEF(Array<int[2]>, dbl1); QS_DEF(Array<int[2]>, dbl2); dbl1.clear_resize(m1.mapping.size()); dbl1.zerofill(); dbl2.clear_resize(m1.mapping.size()); dbl2.zerofill(); const MoleculeStereocenters &stereocenters = mol.stereocenters; int first_sign1 = 0, first_sign2 = 0; for (int i = 0; i < m1.mapping.size(); i++) { int s1 = _getMappingSign(stereocenters, &m1, i); int s2 = _getMappingSign(stereocenters, &m2, i); if (first_sign1 == 0) first_sign1 = -s1; if (first_sign2 == 0) first_sign2 = -s2; int diff = s1 * first_sign1 - s2 * first_sign2; if (diff != 0) return diff; } return 0; } int TetrahedralStereochemistryLayer::_getMappingSign (const MoleculeStereocenters &stereocenters, const MoleculeInChIUtils::Mapping *m, int index) { int src_vertex = index; if (m != 0) src_vertex = m->mapping[index]; if (!stereocenters.exists(src_vertex)) return 0; int pyramid[4]; memcpy(pyramid, stereocenters.getPyramid(src_vertex), 4 * sizeof(int)); // Apply mapping to the pyramid if (m != 0) for (int i = 0; i < 4; i++) { if (pyramid[i] != -1) pyramid[i] = m->inv_mapping[pyramid[i]]; } MoleculeStereocenters::moveMinimalToEnd(pyramid); int cnt = 0; for (int i = 0; i < 3; i++) if (pyramid[i] > pyramid[(i + 1) % 3]) cnt++; if (cnt % 2 == 0) return 1; return -1; } int TetrahedralStereochemistryLayer::compareComponentsEnantiomers ( TetrahedralStereochemistryLayer &comp1, TetrahedralStereochemistryLayer &comp2) { int s1 = comp1._getFirstSign(); int s2 = comp2._getFirstSign(); return s2 - s1; } int TetrahedralStereochemistryLayer::compareComponents ( TetrahedralStereochemistryLayer &comp1, TetrahedralStereochemistryLayer &comp2) { return 0; } ���������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_inchi_utils.cpp�������������������������������������������0000664�0000000�0000000�00000007510�12710376503�0023503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_inchi_utils.h" #include "base_cpp/os_sync_wrapper.h" #include "molecule/elements.h" #include "molecule/molecule.h" #include "molecule/molecule_cis_trans.h" using namespace indigo; // // Sorted atoms lables // Array<int> MoleculeInChIUtils::_atom_lables_sorted; Array<int> MoleculeInChIUtils::_atom_lables_ranks; IMPL_ERROR(MoleculeInChIUtils, "InChI utility"); const Array<int>& MoleculeInChIUtils::getLexSortedAtomLables () { _ensureLabelsInitialized(); return _atom_lables_sorted; } const Array<int>& MoleculeInChIUtils::getLexSortedAtomLablesRanks () { _ensureLabelsInitialized(); return _atom_lables_ranks; } void MoleculeInChIUtils::_ensureLabelsInitialized () { // Double-checked locking if (_atom_lables_sorted.size() == 0) { static ThreadSafeStaticObj<OsLock> lock; OsLocker locker(lock.ref()); if (_atom_lables_sorted.size() == 0) _initializeAtomLabels(); } } void MoleculeInChIUtils::_initializeAtomLabels () { _atom_lables_sorted.reserve(ELEM_MAX); for (int i = ELEM_MIN; i < ELEM_MAX; i++) _atom_lables_sorted.push(i); _atom_lables_sorted.qsort(_compareAtomLabels, NULL); _atom_lables_ranks.resize(ELEM_MAX); _atom_lables_ranks.fffill(); for (int i = 0; i < _atom_lables_sorted.size(); i++) { int label = _atom_lables_sorted[i]; _atom_lables_ranks[label] = i; } } int MoleculeInChIUtils::_compareAtomLabels (int &label1, int &label2, void *context) { // Compare atom labels in alphabetic order with exception that // atom C is the first atom and H as second atom if (label1 == ELEM_C && label2 != ELEM_C) return -1; if (label1 != ELEM_C && label2 == ELEM_C) return 1; return strcmp(Element::toString(label1), Element::toString(label2)); } // // Sorting // void MoleculeInChIUtils::stableSmallSort (Array<int> &indices, const Array<int> *ranks) { // Stable insersion sort for (int i = 1; i < indices.size(); i++) { int i_value = indices[i]; int i_value_rank = i_value; if (ranks != NULL) i_value_rank = ranks->at(i_value); int j = i - 1; while (j >= 0) { int j_value_rank = indices[j]; if (ranks != NULL) j_value_rank = ranks->at(j_value_rank); if (i_value_rank >= j_value_rank) break; indices[j + 1] = indices[j]; j--; } indices[j + 1] = i_value; } } // // Other // int MoleculeInChIUtils::compareHydrogens (int hyd1, int hyd2) { if (hyd1 == 0) hyd1 = 256; if (hyd2 == 0) hyd2 = 256; return hyd2 - hyd1; } int MoleculeInChIUtils::getParityInChI (Molecule &mol, int bond) { if (mol.cis_trans.getParity(bond) == 0) throw Error("Specified bond ins't stereogenic"); const Edge &edge = mol.getEdge(bond); const int *subst = mol.cis_trans.getSubstituents(bond); // Find substituents with maximal indices int max_first = __max(subst[0], subst[1]); int max_second = __max(subst[2], subst[3]); int value = MoleculeCisTrans::sameside( mol.getAtomXyz(edge.beg), mol.getAtomXyz(edge.end), mol.getAtomXyz(max_first), mol.getAtomXyz(max_second)); if (value > 0) return -1; return 1; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_ionize.cpp������������������������������������������������0000664�0000000�0000000�00000212305�12710376503�0022466�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_ionize.h" #include "molecule/base_molecule.h" #include "molecule/elements.h" #include "molecule/molecule.h" #include "molecule/smiles_loader.h" #include "molecule/molfile_loader.h" #include "molecule/molfile_saver.h" #include "base_cpp/scanner.h" #include "molecule/query_molecule.h" #include "molecule/molecule_substructure_matcher.h" #include "base_cpp/output.h" #include "molecule/sdf_loader.h" #include "base_cpp/queue.h" #include "molecule/molecule_automorphism_search.h" using namespace indigo; MoleculePkaModel MoleculePkaModel::_model; IMPL_ERROR(MoleculePkaModel, "Molecule Pka Model"); MoleculePkaModel::MoleculePkaModel () { // _loadSimplePkaModel(); // _loadAdvancedPkaModel(); } void MoleculePkaModel::estimate_pKa (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas) { if (options.model == IonizeOptions::PKA_MODEL_SIMPLE) { if (!_model.simple_model_ready) _loadSimplePkaModel(); _estimate_pKa_Simple(mol, options, acid_sites, basic_sites, acid_pkas, basic_pkas); } else if (options.model == IonizeOptions::PKA_MODEL_ADVANCED) { if (!_model.advanced_model_ready) _loadAdvancedPkaModel(); _estimate_pKa_Advanced(mol, options, acid_sites, basic_sites, acid_pkas, basic_pkas); } else throw Error("Unsupported pKa model: %d", options.model); } int MoleculePkaModel::buildPkaModel (int max_level, float threshold, const char * filename) { // QS_DEF(Array<int>, order); // QS_DEF(Molecule, can_mol); QS_DEF(Array<char>, fp); RedBlackStringObjMap<Array <float> > acid_pkas; RedBlackStringObjMap<Array <float> > basic_pkas; RedBlackStringObjMap<Array <int> > acid_pka_cids; RedBlackStringObjMap<Array <int> > basic_pka_cids; AromaticityOptions opts; const char * a_pka_sites_id = "ACID PKA SITES"; const char * a_pka_values_id = "ACID PKA VALUES"; const char * b_pka_sites_id = "BASIC PKA SITES"; const char * b_pka_values_id = "BASIC PKA VALUES"; const char * compound_cid = "PUBCHEM_COMPOUND_CID"; Molecule mol; int level = 0; _model.adv_a_pkas.clear(); _model.adv_b_pkas.clear(); for (;;) { FileScanner scanner(filename); SdfLoader loader(scanner); int a_count = 0; int b_count = 0; acid_pkas.clear(); basic_pkas.clear(); acid_pka_cids.clear(); basic_pka_cids.clear(); int mol_count = 0; while (!loader.isEOF()) { mol_count++; loader.readNext(); BufferScanner scanner(loader.data); MolfileLoader mol_loader(scanner); mol_loader.stereochemistry_options.ignore_errors = true; mol_loader.loadMolecule(mol); int cid = 0; if (loader.properties.contains(compound_cid)) { BufferScanner scan_cid(loader.properties.at(compound_cid)); cid = scan_cid.readInt(); } // mol.aromatize(opts); // _checkCanonicalOrder(mol, can_mol, order); _removeExtraHydrogens(mol); if (loader.properties.contains(a_pka_sites_id)) { const char *aids = loader.properties.at(a_pka_sites_id); const char *apkas = loader.properties.at(a_pka_values_id); BufferScanner scan_aids(aids); Array<int> a_sites; while (!scan_aids.isEOF()) { int idx = scan_aids.readInt1() - 1; a_sites.push(idx); } BufferScanner scan_apka(apkas); Array<float> a_pka; while (!scan_apka.isEOF()) { float val = scan_apka.readFloat(); a_pka.push(val); } if (a_sites.size() > 1) { // printf("Warning: multiple acid sites detected. Skip this compound (%d)\n", cid); } else { for (int i = 0; i < a_sites.size(); i++) { fp.clear(); // int idx = order.find(a_sites[i]); int idx = a_sites[i]; float val = a_pka[i]; getAtomLocalFingerprint (mol, idx, fp, level); if (fp.size() == 0) continue; if (!acid_pkas.find(fp.ptr())) { acid_pkas.insert(fp.ptr()); acid_pka_cids.insert(fp.ptr()); a_count++; } acid_pkas.at(fp.ptr()).push(val); acid_pka_cids.at(fp.ptr()).push(cid); } } } if (loader.properties.contains(b_pka_sites_id)) { const char *bids = loader.properties.at(b_pka_sites_id); const char *bpkas = loader.properties.at(b_pka_values_id); BufferScanner scan_bids(bids); Array<int> b_sites; while (!scan_bids.isEOF()) { int idx = scan_bids.readInt1() - 1; b_sites.push(idx); } BufferScanner scan_bpka(bpkas); Array<float> b_pka; while (!scan_bpka.isEOF()) { float val = scan_bpka.readFloat(); b_pka.push(val); } if (b_sites.size() > 1) { // printf("Warning: multiple protonation sites detected. Skip this compound (%d)\n", cid); } else { for (int i = 0; i < b_sites.size(); i++) { fp.clear(); // int idx = order.find(b_sites[i]); int idx = b_sites[i]; float val = b_pka[i]; getAtomLocalFingerprint (mol, idx, fp, level); if (fp.size() == 0) continue; if (!basic_pkas.find(fp.ptr())) { basic_pkas.insert(fp.ptr()); basic_pka_cids.insert(fp.ptr()); b_count++; } basic_pkas.at(fp.ptr()).push(val); basic_pka_cids.at(fp.ptr()).push(cid); } } } } bool model_ready = true; float max_deviation = 0.f; for (int i = 0; i < acid_pkas.size(); i++) { const char * fp = acid_pkas.key(i); Array<float> &pkas = acid_pkas.value(i); Array<int> &cids = acid_pka_cids.value(i); float pka_sum = 0.f; float pka_dev = 0.f; float pka_min = 100.f; float pka_max = -100.f; for (int k = 0; k < pkas.size(); k++) { float val = pkas.at(k); pka_sum = pka_sum + val; if (pka_min > val) pka_min = val; if (pka_max < val) pka_max = val; } if ((pka_max - pka_sum/pkas.size()) > (pka_sum/pkas.size() - pka_min)) pka_dev = pka_max - pka_sum/pkas.size(); else pka_dev = pka_sum/pkas.size() - pka_min; if (pka_dev > max_deviation) max_deviation = pka_dev; if (_model.adv_a_pkas.find(fp)) _model.adv_a_pkas.remove(fp); _model.adv_a_pkas.insert(fp); _model.adv_a_pkas.at(fp).push(pka_sum /pkas.size()); _model.adv_a_pkas.at(fp).push(pka_dev); // printf(" {PKA_ACID, \"%s\", %5.2f, %4.2f},\n", fp, pka_sum/pkas.size(), pka_dev); /* for (int k = 0; k < cids.size(); k++) { printf("%d ", cids.at(k)); printf("\n"); } */ if (pka_dev > threshold) model_ready = false; } for (int i = 0; i < basic_pkas.size(); i++) { const char * fp = basic_pkas.key(i); Array<float> &pkas = basic_pkas.value(i); Array<int> &cids = basic_pka_cids.value(i); float pka_sum = 0.f; float pka_dev = 0.f; float pka_min = 100.f; float pka_max = -100.f; for (int k = 0; k < pkas.size(); k++) { float val = pkas.at(k); pka_sum = pka_sum + val; if (pka_min > val) pka_min = val; if (pka_max < val) pka_max = val; } if ((pka_max - pka_sum/pkas.size()) > (pka_sum/pkas.size() - pka_min)) pka_dev = pka_max - pka_sum/pkas.size(); else pka_dev = pka_sum/pkas.size() - pka_min; if (pka_dev > max_deviation) max_deviation = pka_dev; if (_model.adv_b_pkas.find(fp)) _model.adv_b_pkas.remove(fp); _model.adv_b_pkas.insert(fp); _model.adv_b_pkas.at(fp).push(pka_sum / pkas.size()); _model.adv_b_pkas.at(fp).push(pka_dev); // printf(" {PKA_BASIC, \"%s\", %5.2f, %4.2f},\n", fp, pka_sum/pkas.size(), pka_dev); /* for (int k = 0; k < cids.size(); k++) { printf("%d ", cids.at(k)); printf("\n"); } */ if (pka_dev > threshold) model_ready = false; } /* printf("Model level = %d, number of molecules in model file = %d\n", level, mol_count); printf(" number of unique acid fingeprints = %d\n", a_count); printf(" number of unique basic fingeprints = %d\n", b_count); printf(" number of acid fingeprints included = %d\n", _model.adv_a_pkas.size()); printf(" number of basic fingeprints included = %d\n", _model.adv_b_pkas.size()); printf(" maximum deviation for model = %4.2f\n", max_deviation); */ if (model_ready) { break; } else { if (level >= max_level) { level = -1; break; } else { level++; _model.max_deviations.push(max_deviation); } } } _model.level = level; _model.advanced_model_ready = true; return level; } void MoleculePkaModel::_loadSimplePkaModel() { struct _PkaDef { const char *acid; float pka; const char *basic; }; /* Roger Sayle, Physiological ionization and pKa prediction, * http://www.daylight.com/meetings/emug00/Sayle/pkapredict.html */ static _PkaDef simple_pka_model[] = { {"[F;!H0]", 3.18f, "[F-]"}, {"[Cl;!H0]", -6.50f, "[Cl-]"}, {"[Br;!H0]", -8.50f, "[Br-]"}, {"[I;!H0]", -9.00f, "[I-]"}, {"[c;!H0]", 43.00f, "[c-]"}, {"[$([C]#N);!H0]", 9.30f, "[$([C-]#N)]"}, {"[C;!H0]", 50.00f, "[C-]"}, {"[nH;!H0]", 16.50f, "[n-]"}, {"[$([N]=N=*);!H0]", -99.99f, "[$([N-]=N=*)]"}, {"[$([N]C=O);!H0]", 22.00f, "[$([N-]C=O)]"}, {"[$([N]S(=O)=O);!H0]", 10.10f, "[$([N-]S(=O)=O)]"}, {"[N;!H0]", 32.50f, "[N-]"}, {"[nH2+;!H0]", -3.80f, "[nH]"}, {"[nH+;!H0]", 5.23f, "[n]"}, {"[$([NH+]#*);!H0]", -12.00f, "[$([N]#*)]"}, {"[$([NH+]=C(N)N);!H0]", 14.4f, "[$([N]=C(N)N)]"}, {"[$([NH+]=C(N)a);!H0]", 11.6f, "[$([N]=C(N)a)]"}, {"[$([NH+]=CN);!H0]", 12.4f, "[$([N]=CN)]"}, {"[$([NH+]=*);!H0]", -99.99f, "[$([N]=*)]"}, {"[$([NH+]a);!H0]", 4.69f, "[$([N]a)]"}, {"[$([NH+]C=O);!H0]", 4.74f, "[$([N]C=O)]"}, {"[$([NH+]C=N);!H0]", -99.99f, "[$([N]C=N)]"}, {"[$([NH+]S(=O)=O);!H0]", -99.99f, "[$([N]S(=O)=O)]"}, {"[NH+;!H0]", 10.5f, "[N]"}, {"[NH2+;!H0]", 11.1f, "[$([N](C)C)]"}, {"[NH3+;!H0]", 10.6f, "[NH2]"}, {"[NH4+;!H0]", 9.25f, "[NH3]"}, {"[OH2;!H0]", 15.70f, "[OH-]"}, {"[$([O]c);!H0]", 10.00f, "[O-]a"}, {"[$([O]C(=O)[O-]);!H0]", 10.33f, "[$([O-]C(=O)[O-])]"}, {"[$([O]C(=O)a);!H0]", 4.20f, "[$([O-]C(=O)a)]"}, {"[$([O]C=O);!H0]", 4.80f, "[$([O-]C=O)]"}, {"[$([O]C);!H0]", 15.50f, "[$([O-]C)]"}, {"[$([O]N(=O)=O);!H0]", -1.40f, "[$([O-]N(=O)=O)]"}, {"[$([O][N+]=O);!H0]", -12.00f, "[$([O-][N+]=O)]"}, {"[$([O]NC=O);!H0]", 9.40f, "[$([O-]NC=O)]"}, {"[$([O]N=*);!H0]", 12.34f, "[$([O-]N=*)]"}, {"[$([O]N(*)*);!H0]", 5.2f, "[$([O-]N(*)*)]"}, {"[$([O]N);!H0]", 5.96f, "[$([O-]N)]"}, {"[$([O]P([O-])([O-]));!H0]", 12.50f, "[$([O-]P([O-])([O-]))]"}, {"[$([O]P([O-])=O);!H0]", 6.70f, "[$([O-]P([O-])=O)]"}, {"[$([O]P=O);!H0]", 2.00f, "[$([O-]P=O)]"}, {"[$([O]P[O-]);!H0]", 99.99f, "[$([O-]P[O-])]"}, {"[$([O]Pa);!H0]", 2.10f, "[$([O-]Pa)]"}, {"[$([O]P);!H0]", 3.08f, "[$([O-]P)]"}, {"[$([O]S(=O)(=O)[O-]);!H0]", 2.0f, "[$([O-]S(=O)(=O)[O-])]"}, {"[$([O]S(=O)(=O));!H0]", -99.99f, "[$([O-]S(=O)(=O))]"}, {"[$([O]S(=O)[O-]);!H0]", 7.20f, "[$([O-]S(=O)[O-])]"}, {"[$([O]S(=O));!H0]", 1.80f, "[$([O-]S(=O))]"}, {"[O;!H0]", 99.99f, "[O-]"}, {"[OH+;!H0]", -1.74f, "[O]"}, {"[P;!H0]", 29.00f, "[P-]"}, {"[P+;!H0]", -13.00f, "[P]"}, {"[$([S]*=O);!H0]", 3.52f, "[$([S-]*=O)]"}, {"[$([S]a);!H0]", 6.52f, "[$([S-]a)]"}, {"[SH2;!H0]", 7.00f, "[SH-]"}, {"[S;!H0]", 12.00f, "[S-]"}, {"[SH+;!H0]", -7.00f, "[S]"}, }; _model.acids.clear(); _model.basics.clear(); _model.a_pkas.clear(); _model.b_pkas.clear(); for (auto i = 0; i < NELEM(simple_pka_model); i++) { BufferScanner scanner(simple_pka_model[i].acid); SmilesLoader loader(scanner); QueryMolecule &acid = _model.acids.push(); loader.loadSMARTS(acid); _model.a_pkas.push(simple_pka_model[i].pka); } for (auto i = 0; i < NELEM(simple_pka_model); i++) { BufferScanner scanner(simple_pka_model[i].basic); SmilesLoader loader(scanner); QueryMolecule &basic = _model.basics.push(); loader.loadSMARTS(basic); _model.b_pkas.push(simple_pka_model[i].pka); } _model.simple_model_ready = true; } void MoleculePkaModel::_loadAdvancedPkaModel() { enum PkaType { PKA_ACID, PKA_BASIC }; struct _PkaDef { PkaType type; const char *a_fp; float pka; float deviation; }; static _PkaDef advanced_pka_model[] = { {PKA_ACID, "8200022210000", 5.31, 10.19}, {PKA_ACID, "7300021310000", 8.94, 7.84}, {PKA_ACID, "6400020401000", 8.76, 4.51}, {PKA_ACID, "6400020410000", 10.21, 0.00}, {PKA_ACID, "16200022210000", 6.72, 3.77}, {PKA_ACID, "6400020411000", 12.84, 2.80}, {PKA_ACID, "7300021311000", 1.72, 0.55}, {PKA_ACID, "6400020420000", 6.93, 3.75}, {PKA_ACID, "33500020531000", 6.27, 0.00}, {PKA_ACID, "7300021320000", 7.08, 4.58}, {PKA_ACID, "82-10023110000", 3.06, 1.23}, {PKA_BASIC, "7300021310000", 7.52, 7.77}, {PKA_BASIC, "7300021320000", 7.77, 11.57}, {PKA_BASIC, "7300021311000", 5.39, 8.91}, {PKA_BASIC, "7300021330000", 7.84, 6.14}, {PKA_BASIC, "82-10023110000", 4.65, 0.00}, {PKA_BASIC, "7410020421000", 0.79, 0.00}, {PKA_BASIC, "7410020410000", 8.86, 0.00}, {PKA_ACID, "8200022210000|6400020421000", 4.82, 5.80}, {PKA_ACID, "8200022210000|6400020410010", 3.70, 0.00}, {PKA_ACID, "7300021310000|6400020410010", 1.10, 0.00}, {PKA_ACID, "6400020401000|8200022201000", 13.27, 0.00}, {PKA_ACID, "8200022210000|6400020411000", 3.75, 0.00}, {PKA_ACID, "6400020410000|7410020421000", 10.21, 0.00}, {PKA_ACID, "16200022210000|6400020421000", 4.30, 2.32}, {PKA_ACID, "8200022210000|6400020410000", 10.33, 0.00}, {PKA_ACID, "6400020411000|64000204400008200022201000", 10.04, 0.00}, {PKA_ACID, "8200022210000|6400020420000", 13.56, 1.94}, {PKA_ACID, "7300021311000|64000204110006400020411000", 2.27, 0.00}, {PKA_ACID, "7300021311000|64000204110007300021320000", 1.17, 0.00}, {PKA_ACID, "6400020411000|64000204100008200022201000", 13.57, 0.00}, {PKA_ACID, "7300021310000|6400020421000", 14.05, 1.05}, {PKA_ACID, "8200022210000|7300021320000", 8.70, 0.00}, {PKA_ACID, "6400020420000|64000204100007410020421000", 8.46, 0.00}, {PKA_ACID, "16200022210000|6400020420000", 9.14, 0.87}, {PKA_ACID, "33500020531000|6400020410000640002041000082000222010008200022210000", 6.27, 0.00}, {PKA_ACID, "8200022210000|16600020622000", 2.17, 1.57}, {PKA_ACID, "6400020401000|6400020411000", 4.25, 0.00}, {PKA_ACID, "8200022210000|7300021311000", 12.42, 0.00}, {PKA_ACID, "8200022210000|6400020430000", 12.85, 1.30}, {PKA_ACID, "7300021320000|64000204210006400020421000", 7.54, 3.65}, {PKA_ACID, "7300021310000|16600020622000", 8.34, 1.14}, {PKA_ACID, "7300021320000|64000204110006400020421000", 11.65, 0.00}, {PKA_ACID, "7300021320000|64000204110006400020411000", 11.12, 0.00}, {PKA_ACID, "82-10023110000|6400020421000", 3.06, 1.23}, {PKA_ACID, "8200022210000|16400020421000", 1.30, 0.00}, {PKA_ACID, "8200022210000|5300020330000", 8.83, 0.00}, {PKA_ACID, "6400020420000|64000204210006400020421000", 6.55, 4.13}, {PKA_ACID, "6400020411000|64000204110006400020411000", 14.90, 0.00}, {PKA_ACID, "7300021320000|166000206220006400020421000", 5.42, 2.92}, {PKA_ACID, "8200022210000|15500020531000", 6.30, 0.10}, {PKA_ACID, "7300021320000|64000204210007300021330000", 3.60, 0.00}, {PKA_BASIC, "7300021310000|6400020430000", 9.33, 2.06}, {PKA_BASIC, "7300021310000|6400020420000", 9.93, 4.59}, {PKA_BASIC, "7300021320000|64000204200006400020430000", 10.12, 1.48}, {PKA_BASIC, "7300021310000|6400020410000", 10.66, 0.00}, {PKA_BASIC, "7300021310000|8200022220000", 12.50, 0.00}, {PKA_BASIC, "7300021311000|6400020421000", 11.15, 6.54}, {PKA_BASIC, "7300021320000|64000204200006400020420000", 10.27, 2.23}, {PKA_BASIC, "7300021320000|64000204100006400020410000", 10.73, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000", 5.83, 5.69}, {PKA_BASIC, "7300021311000|64000204110006400020411000", 5.07, 6.98}, {PKA_BASIC, "7300021311000|64000204110008200022220000", -2.00, 0.00}, {PKA_BASIC, "7300021311000|64000204110007300021320000", 2.49, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000", 5.34, 7.34}, {PKA_BASIC, "7300021320000|64000204100006400020420000", 9.42, 1.22}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020410000", 9.80, 0.00}, {PKA_BASIC, "82-10023110000|7410020440000", 4.65, 0.00}, {PKA_BASIC, "7300021311000|64000204110007300021311000", 2.86, 0.62}, {PKA_BASIC, "7300021320000|64000204210006400020421000", 5.05, 5.36}, {PKA_BASIC, "7300021320000|64000204110006400020411000", -0.30, 3.50}, {PKA_BASIC, "7300021310000|6400020440000", 10.01, 1.71}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020420000", 9.42, 1.17}, {PKA_BASIC, "7300021320000|64000204110006400020421000", 3.95, 3.20}, {PKA_BASIC, "7410020421000|6400020411000640002041100082-10023110000", 0.79, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020420000", 9.00, 2.16}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020421000", 5.97, 6.03}, {PKA_BASIC, "7300021310000|6400020421000", 3.71, 6.28}, {PKA_BASIC, "7300021311000|64000204210007300021320000", 1.60, 0.00}, {PKA_BASIC, "7300021310000|7300021320000", 8.79, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000", 8.74, 2.18}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000", 8.30, 3.08}, {PKA_BASIC, "7300021320000|64000204300006400020430000", 10.73, 0.33}, {PKA_BASIC, "7300021320000|1440002044000014400020440000", 7.55, 0.00}, {PKA_BASIC, "7300021320000|166000206220006400020421000", 11.68, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020421000", 4.26, 0.59}, {PKA_BASIC, "7300021311000|64000204210007300021311000", 2.37, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020421000", 4.47, 0.65}, {PKA_BASIC, "7300021330000|640002041000064000204300006400020430000", 6.75, 2.95}, {PKA_BASIC, "7300021320000|64000204100006400020430000", 10.36, 0.63}, {PKA_BASIC, "7300021320000|64000204210006400020430000", 9.06, 3.30}, {PKA_BASIC, "7300021320000|64000204400006400020440000", 11.07, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020440000", 7.00, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020421000", 8.68, 3.54}, {PKA_BASIC, "7300021311000|64000204200006400020421000", 9.58, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204400006400020440000", 11.25, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020420000", 5.20, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020430000", 8.76, 0.00}, {PKA_BASIC, "7410020410000|6400020430000", 8.86, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210008200022210000", 5.50, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020440000", 2.00, 0.10}, {PKA_BASIC, "7300021320000|64000204100006400020440000", 7.50, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204210006400020421000", 3.40, 0.00}, {PKA_BASIC, "7300021311000|64000204100006400020421000", 4.80, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204210006400020421000", 1.70, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020440000", 9.67, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210007300021330000", 4.50, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210006400020430000", 8.26, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000", 8.19, 4.01}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020430000", 8.80, 0.00}, {PKA_BASIC, "7300021320000|64000204110007300021330000", 1.70, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204300006400020430000", 6.66, 0.00}, {PKA_ACID, "8200022210000|6400020421000|640002043000082000222010008200022210000", 2.97, 2.02}, {PKA_ACID, "8200022210000|6400020421000|640002042000082000222010008200022210000", 3.90, 2.42}, {PKA_ACID, "8200022210000|6400020410010|73000213000108200022210000", 3.70, 0.00}, {PKA_ACID, "7300021310000|6400020410010|73000213000107300021310000", 1.10, 0.00}, {PKA_ACID, "6400020401000|8200022201000|6400020401000", 13.27, 0.00}, {PKA_ACID, "8200022210000|6400020411000|82000222010008200022210000", 3.75, 0.00}, {PKA_ACID, "6400020410000|7410020421000|640002041000082-100231100008200022201000", 10.21, 0.00}, {PKA_ACID, "16200022210000|6400020421000|16200022201000162000222100007300021310000", 2.95, 0.00}, {PKA_ACID, "8200022210000|6400020410000|8200022210000", 10.33, 0.00}, {PKA_ACID, "6400020411000|64000204400008200022201000|1710002311000017100023110000171000231100006400020411000", 10.04, 0.00}, {PKA_ACID, "8200022210000|6400020421000|640002044000082000222010008200022210000", 2.93, 3.71}, {PKA_ACID, "8200022210000|6400020421000|640002041100082000222010008200022210000", 4.31, 1.13}, {PKA_ACID, "8200022210000|6400020420000|64000204400008200022210000", 12.31, 0.07}, {PKA_ACID, "7300021311000|64000204110006400020411000|730002131100073000213110007300021320000", 2.27, 0.00}, {PKA_ACID, "7300021311000|64000204110007300021320000|640002041100073000213110007300021311000", 1.17, 0.00}, {PKA_ACID, "6400020411000|64000204100008200022201000|6400020411000", 13.57, 0.00}, {PKA_ACID, "16200022210000|6400020421000|1620002221000064000204100008200022201000", 3.33, 0.00}, {PKA_ACID, "8200022210000|6400020421000|640002041000082000222010008200022210000", 4.76, 0.00}, {PKA_ACID, "7300021310000|6400020421000|640002041000073000213100008200022201000", 15.10, 0.00}, {PKA_ACID, "8200022210000|7300021320000|64000204210008200022210000", 8.70, 0.00}, {PKA_ACID, "6400020420000|64000204100007410020421000|640002042000082-100231100008200022201000", 8.46, 0.00}, {PKA_ACID, "8200022210000|6400020420000|64000204100008200022210000", 15.50, 0.00}, {PKA_ACID, "16200022210000|6400020420000|162000222100006400020420000", 9.00, 0.73}, {PKA_ACID, "8200022210000|6400020420000|64000204200008200022210000", 14.60, 0.70}, {PKA_ACID, "33500020531000|6400020410000640002041000082000222010008200022210000|33500020531000", 6.27, 0.00}, {PKA_ACID, "8200022210000|16600020622000|6400020420000820002220100082000222010008200022210000", 1.45, 0.05}, {PKA_ACID, "8200022210000|6400020421000|640002041001082000222010008200022210000", 2.23, 0.39}, {PKA_ACID, "8200022210000|6400020420000|64000204100108200022210000", 13.60, 0.00}, {PKA_ACID, "6400020401000|6400020411000|64000204010006400020421000", 4.25, 0.00}, {PKA_ACID, "8200022210000|6400020421000|640002042100082000222010008200022210000", 3.50, 2.85}, {PKA_ACID, "8200022210000|6400020420000|64000204110008200022210000", 15.50, 0.00}, {PKA_ACID, "8200022210000|7300021311000|64000204210008200022210000", 12.42, 0.00}, {PKA_ACID, "8200022210000|6400020430000|640002042000064000204200008200022210000", 14.15, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204200007300021320000820002220100073000213200008200022201000", 4.01, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204200007300021320000820002220100064000204200008200022201000", 9.62, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204210007300021320000820002220100073000213200008200022201000", 7.14, 3.25}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204300007300021320000820002220100073000213200008200022201000", 8.96, 0.00}, {PKA_ACID, "7300021310000|16600020622000|6400020421000730002131000082000222010008200022201000", 8.34, 1.14}, {PKA_ACID, "8200022210000|6400020430000|640002042000064000204300008200022210000", 13.90, 0.00}, {PKA_ACID, "7300021320000|64000204110006400020421000|6400020411000730002132000064000204110008200022201000", 11.65, 0.00}, {PKA_ACID, "8200022210000|6400020421000|640002041100064000204110008200022210000", 9.19, 2.04}, {PKA_ACID, "7300021320000|64000204110006400020411000|640002041100073000213200006400020411000", 11.12, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204400007300021320000820002220100082000222010008200022220000", 6.13, 0.00}, {PKA_ACID, "8200022210000|6400020430000|640002042000082000222100008200022220000", 12.61, 0.00}, {PKA_ACID, "8200022210000|6400020430000|640002041100064000204300008200022210000", 12.22, 0.00}, {PKA_ACID, "8200022210000|6400020430000|640002043000082000222100008200022220000", 12.11, 0.03}, {PKA_ACID, "82-10023110000|6400020421000|640002042000082-100231100008200022201000", 2.82, 0.99}, {PKA_ACID, "8200022210000|6400020421000|640002042100064000204210008200022210000", 6.15, 5.73}, {PKA_ACID, "8200022210000|6400020421000|640002041100064000204210008200022210000", 8.56, 4.49}, {PKA_ACID, "8200022210000|16400020421000|640002042100082000222010008200022210000", 1.30, 0.00}, {PKA_ACID, "8200022210000|16600020622000|6400020421000820002220100082000222010008200022210000", 2.53, 1.83}, {PKA_ACID, "16200022210000|6400020421000|1620002221000064000204110006400020411000", 6.62, 0.00}, {PKA_ACID, "8200022210000|5300020330000|640002042100082000222100008200022210000", 8.83, 0.00}, {PKA_ACID, "6400020420000|64000204210006400020421000|64000204200006400020420000820002220100064000204200008200022201000", 5.20, 0.06}, {PKA_ACID, "6400020420000|64000204210006400020421000|64000204200008200022201000820002222000082000222010008200022220000", 5.10, 0.00}, {PKA_ACID, "6400020420000|64000204210006400020421000|64000204100006400020420000820002220100082000222010008200022220000", 10.68, 0.00}, {PKA_ACID, "8200022210000|6400020420000|64000204300008200022210000", 12.81, 0.90}, {PKA_ACID, "6400020411000|64000204110006400020411000|640002041100064000204110006400020411000", 14.90, 0.00}, {PKA_ACID, "7300021310000|6400020421000|640002042100073000213100008200022201000", 13.00, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204210007300021320000820002220100073000213300008200022201000", 7.89, 0.00}, {PKA_ACID, "16200022210000|6400020420000|162000222100006400020421000", 9.43, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204200007300021320000820002220100073000213300008200022201000", 7.20, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204110007300021320000820002220100073000213300008200022201000", 6.70, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204400007300021320000820002220100073000213200008200022201000", 7.72, 0.29}, {PKA_ACID, "7300021320000|166000206220006400020421000|6400020421000730002132000082000222010008200022201000162000222200007300021311000", 7.20, 0.00}, {PKA_ACID, "8200022210000|15500020531000|8200022201000820002221000082000222100008200022220000", 6.30, 0.10}, {PKA_ACID, "7300021320000|64000204210006400020421000|64000204400007300021320000820002220100073000213300008200022201000", 8.45, 0.00}, {PKA_ACID, "7300021320000|166000206220006400020421000|640002042100073000213200008200022201000820002220100064000204110007300021311000", 6.70, 0.00}, {PKA_ACID, "7300021320000|166000206220006400020421000|640002044000073000213200008200022201000820002220100064000204110006400020421000", 3.55, 1.05}, {PKA_ACID, "7300021320000|166000206220006400020421000|640002042100073000213200008200022201000820002220100064000204210008200022220000", 5.00, 0.00}, {PKA_ACID, "7300021320000|166000206220006400020421000|640002042100073000213200008200022201000820002220100073000213110007300021311000", 7.40, 0.00}, {PKA_ACID, "82-10023110000|6400020421000|640002043000082-100231100008200022201000", 3.40, 0.00}, {PKA_ACID, "7300021320000|166000206220006400020421000|640002042100073000213200008200022201000820002220100064000204210008200022201000", 4.57, 0.00}, {PKA_ACID, "7300021320000|64000204210007300021330000|73000213200007300021320000820002220100064000204200006400020420000", 3.60, 0.00}, {PKA_ACID, "7300021320000|64000204210006400020421000|73000213110007300021320000820002220100064000204210008200022201000", 9.69, 0.00}, {PKA_ACID, "82-10023110000|6400020421000|640002042100082-100231100008200022201000", 3.20, 0.00}, {PKA_BASIC, "7300021310000|6400020430000|640002042000064000204210007300021310000", 9.21, 1.61}, {PKA_BASIC, "7300021310000|6400020420000|64000204210007300021310000", 8.73, 1.05}, {PKA_BASIC, "7300021310000|6400020430000|640002042100064000204300007300021310000", 9.53, 0.43}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020420000730002132000064000204200006400020421000", 10.67, 0.05}, {PKA_BASIC, "7300021310000|6400020410000|7300021310000", 10.66, 0.00}, {PKA_BASIC, "7300021310000|8200022220000|64000204100007300021310000", 12.50, 0.00}, {PKA_BASIC, "7300021311000|6400020421000|730002131100073000213100007300021310000", 13.60, 0.00}, {PKA_BASIC, "7300021310000|6400020420000|64000204100107300021310000", 5.34, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020420000|7300021320000", 8.04, 0.00}, {PKA_BASIC, "7300021311000|6400020421000|640002041000073000213100007300021311000", 12.10, 0.00}, {PKA_BASIC, "7300021310000|6400020420000|64000204100007300021310000", 10.65, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020410000|7300021320000", 10.73, 0.00}, {PKA_BASIC, "7300021310000|6400020420000|64000204200007300021310000", 10.36, 2.56}, {PKA_BASIC, "7300021311000|64000204210006400020421000|73000213100007300021310000730002131100073000213100007300021311000", 11.52, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110008200022220000", 0.80, 0.00}, {PKA_BASIC, "7300021311000|64000204110008200022220000|640002041100073000213110006400020411000", -2.00, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|1620002222000073000213110006400020411000", 2.52, 0.00}, {PKA_BASIC, "7300021311000|64000204110007300021320000|640002041100073000213110006400020411000", 2.49, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110007300021320000", 6.99, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|64000204110007300021311000162000222200007300021310000", 5.36, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|73000213100007300021311000730002131100073000213100007300021311000", 5.00, 0.00}, {PKA_BASIC, "7300021310000|6400020420000|64000204110007300021310000", 9.49, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020420000|64000204200007300021320000", 11.29, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020420000|73000213200006400020421000", 10.10, 0.00}, {PKA_BASIC, "7300021310000|6400020430000|640002041000064000204100007300021310000", 10.63, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020410000|7300021330000", 9.80, 0.00}, {PKA_BASIC, "82-10023110000|7410020440000|64000204100006400020410000640002041000082-10023110000", 4.65, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020421000730002131100073000213200008200022201000", 3.26, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110006400020411000", 5.54, 6.51}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110007300021311000", 1.23, 0.00}, {PKA_BASIC, "7300021311000|64000204110007300021311000|640002041100073000213110006400020411000", 2.24, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020421000|64000204110007300021320000820002220100073000213200008200022201000", 9.45, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|73000213100007300021311000730002131100064000204110007410020421000", 0.35, 0.00}, {PKA_BASIC, "7300021320000|64000204110006400020411000|640002041100073000213200006400020411000", -0.30, 3.50}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100073000213100007300021311000", 3.45, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002131100064000204110007300021310000", 5.71, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110007300021330000", 6.95, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020420000|640002042100073000213200006400020421000", 9.89, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|73000213100007300021311000730002133000064000204200008200022201000", 9.20, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110007300021310000730002131100073000213100007300021311000", 6.84, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020420000|640002042000073000213200006400020420000", 10.55, 2.05}, {PKA_BASIC, "7300021310000|6400020440000|6400020410000640002041000064000204210007300021310000", 10.21, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020420000|73000213300006400020421000", 9.89, 0.00}, {PKA_BASIC, "7300021311000|6400020421000|730002131000073000213110007300021330000", 14.30, 0.00}, {PKA_BASIC, "7300021310000|6400020430000|640002041000064000204200007300021310000", 10.63, 0.07}, {PKA_BASIC, "7300021310000|6400020440000|6400020410000640002041000064000204100007300021310000", 10.68, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020420000|640002041000073000213200006400020410000", 10.84, 0.00}, {PKA_BASIC, "7300021310000|6400020440000|6400020420000640002042000064000204200007300021310000", 8.30, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002041100073000213110006400020421000", 5.19, 4.46}, {PKA_BASIC, "7300021311000|64000204110006400020421000|64000204110007300021311000171000231100006400020411000", 0.49, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110009100023110000", -0.44, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002132000064000204210007300021320000", 8.70, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002131100064000204210007300021320000", 10.20, 0.00}, {PKA_BASIC, "7300021320000|64000204110006400020421000|6400020411000730002132000064000204110008200022201000", 0.75, 0.00}, {PKA_BASIC, "7410020421000|6400020411000640002041100082-10023110000|640002041100074100204210006400020411000", 0.79, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110006400020421000", 4.86, 4.36}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110007300021310000", 6.82, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204100006400020411000", 4.81, 3.36}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020421000730002131100064000204110007300021310000", 5.77, 0.72}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110007300021320000", 3.39, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020421000730002131100064000204100007300021320000", 8.36, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020430000730002132000064000204200006400020421000", 9.66, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020420000|730002133000064000204200006400020420000", 9.20, 1.82}, {PKA_BASIC, "7300021320000|64000204200006400020420000|640002042000073000213200006400020421000", 10.19, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020421000|730002133000073000213300008200022201000", 12.00, 0.00}, {PKA_BASIC, "7300021310000|6400020430000|640002042000064000204200007300021310000", 10.61, 0.03}, {PKA_BASIC, "7300021310000|6400020440000|6400020410000640002041000064000204200007300021310000", 10.85, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020420000|73000213300006400020420000", 9.27, 1.02}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020420000|730002133000064000204100006400020410000", 10.35, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002131100064000204210007300021311000", 4.05, 0.00}, {PKA_BASIC, "7300021310000|6400020421000|640002041100064000204110007300021310000", 3.74, 2.72}, {PKA_BASIC, "7300021310000|6400020421000|640002041100064000204210007300021310000", 3.23, 3.48}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110006400020411000", 12.68, 0.00}, {PKA_BASIC, "7300021311000|64000204210007300021320000|6400020411000640002042100073000213110007300021311000", 1.60, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204210007300021320000", 2.27, 0.00}, {PKA_BASIC, "7300021320000|64000204110006400020421000|6400020411000730002132000064000204110006400020411000", 3.59, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110008200022220000", 3.28, 0.00}, {PKA_BASIC, "7300021310000|7300021320000|64000204210007300021310000", 8.79, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020421000730002133000064000204210006400020421000", 10.70, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204100006400020411000730002131100073000213100007300021311000", 4.82, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002042000064000204100006400020420000", 10.21, 0.01}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020410000730002133000064000204200006400020420000", 9.06, 1.39}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020420000730002133000064000204200006400020421000", 8.35, 0.00}, {PKA_BASIC, "7300021320000|64000204300006400020430000|64000204100006400020410000730002132000064000204100006400020410000", 11.05, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020410000730002133000064000204100006400020410000", 10.75, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020420000730002133000064000204200006400020420000", 8.50, 1.97}, {PKA_BASIC, "7300021320000|1440002044000014400020440000|6400020410000640002041000064000204100007300021320000640002041000064000204100006400020410000", 7.55, 0.00}, {PKA_BASIC, "7300021320000|166000206220006400020421000|640002042100073000213200008200022201000820002220100064000204210008200022201000", 11.68, 0.00}, {PKA_BASIC, "7300021320000|64000204110006400020421000|7300021311000730002132000064000204110006400020421000", 6.60, 0.25}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002132000064000204110006400020421000", 5.53, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002132000064000204210007300021330000", 8.77, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020421000|730002132000064000204110006400020411000", 4.85, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204110006400020420000", 5.51, 0.38}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204100006400020421000", 7.14, 0.57}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020421000730002131100064000204100006400020411000", 6.40, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204100006400020411000730002131100064000204100006400020411000", 6.04, 1.39}, {PKA_BASIC, "7300021311000|64000204110006400020411000|640002042100073000213110006400020421000", 6.15, 0.00}, {PKA_BASIC, "7300021311000|64000204210007300021311000|6400020411000640002042100073000213110006400020411000", 2.37, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002131100064000204110006400020421000", 3.43, 0.00}, {PKA_BASIC, "7300021311000|64000204110007300021311000|640002042100073000213110006400020411000", 3.47, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100064000204100007300021320000", 6.19, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020421000|64000204110006400020411000730002132000064000204100008200022201000", 0.50, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020421000|6400020421000730002132000064000204110006400020411000", 4.39, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020421000|730002133000064000204110006400020411000", 5.36, 6.13}, {PKA_BASIC, "7300021320000|64000204200006400020421000|6400020410000730002132000064000204110006400020411000", 5.12, 0.00}, {PKA_BASIC, "7300021310000|6400020421000|640002042100064000204210007300021310000", 6.94, 3.05}, {PKA_BASIC, "7300021310000|6400020420000|64000204300007300021310000", 9.70, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020411000730002131100073000213100007300021311000", 10.77, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020420000|730002133000064000204200006400020421000", 6.84, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204300006400020430000|73000213300006400020420000640002042000064000204200006400020420000", 6.75, 2.95}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020420000730002132000064000204200006400020420000", 11.06, 0.16}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020430000730002132000064000204200006400020420000", 10.30, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020430000|730002132000064000204100006400020420000", 10.99, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020421000730002131100064000204110006400020421000", 4.36, 1.67}, {PKA_BASIC, "7300021320000|64000204210006400020421000|64000204110006400020421000730002132000064000204110008200022201000", -0.31, 0.00}, {PKA_BASIC, "7300021320000|64000204110006400020421000|6400020411000730002132000064000204110006400020421000", 2.23, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204210006400020421000", 4.88, 0.03}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002132000064000204210007300021311000", 8.20, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100064000204110007300021310000", 7.34, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204210007300021310000", 7.62, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100073000213200007300021320000", 4.48, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100064000204200007300021320000", 6.18, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020421000|6400020411000730002132000064000204110006400020411000", 4.17, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020430000|64000204110006400020411000730002132000064000204100006400020410000", 5.77, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020420000|73000213200006400020430000", 9.95, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020421000|64000204110007300021320000820002220100073000213300008200022201000", 9.50, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110007300021310000730002131100073000213300008200022201000", 4.25, 0.03}, {PKA_BASIC, "7300021310000|6400020430000|640002041000064000204300007300021310000", 9.32, 0.12}, {PKA_BASIC, "7300021311000|6400020421000|730002131100073000213110007300021330000", 4.61, 0.00}, {PKA_BASIC, "7300021320000|64000204400006400020440000|6400020410000640002041000064000204200007300021320000640002041000064000204100006400020420000", 11.07, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204100006400020411000730002131100064000204110006400020421000", 5.83, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002133000064000204210006400020421000", 9.12, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|7300021311000730002131100064000204210007300021310000", 3.70, 0.10}, {PKA_BASIC, "7300021320000|64000204210006400020440000|640002041100064000204110007300021320000640002041000064000204100006400020410000", 7.00, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020421000|64000204100007300021330000640002041000064000204110006400020411000", 6.57, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020430000|730002132000064000204100006400020430000", 10.05, 0.09}, {PKA_BASIC, "7300021311000|64000204200006400020421000|6400020420000730002131100073000213100007300021320000", 9.58, 0.00}, {PKA_BASIC, "7300021310000|6400020430000|640002042000064000204400007300021310000", 10.17, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204400006400020440000|7300021330000640002041000064000204100006400020420000640002041000064000204100006400020420000", 11.25, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020421000|730002132000064000204110006400020421000", 3.67, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002042000064000204210008200022210000", 11.38, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020420000|730002131100073000213200006400020430000", 5.20, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020421000|64000204100007300021330000640002041000064000204110006400020421000", 7.24, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020430000730002132000064000204100006400020410000", 9.12, 0.48}, {PKA_BASIC, "7300021311000|64000204210006400020430000|73000213100007300021311000730002132000064000204300008200022210000", 8.76, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100064000204110006400020421000", 5.48, 4.28}, {PKA_BASIC, "7300021320000|64000204210006400020421000|64000204110006400020411000730002132000064000204110006400020411000", 0.79, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020421000|730002133000064000204110006400020421000", 4.83, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110007300021311000730002132000073000213300008200022201000", 6.90, 0.00}, {PKA_BASIC, "7410020410000|6400020430000|640002042000064000204210007410020410000", 8.86, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210008200022210000|64000204110006400020430000730002133000064000204210008200022201000", 5.50, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|162000222200006400020420000730002131100064000204110006400020420000", 2.10, 0.00}, {PKA_BASIC, "7300021320000|64000204300006400020430000|64000204200006400020420000730002132000064000204200006400020420000", 10.40, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020421000730002131100064000204210007300021320000", 5.23, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020421000|6400020420000730002132000064000204100006400020421000", 4.20, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020440000|640002042100073000213110007300021320000640002041000064000204210006400020430000", 2.00, 0.10}, {PKA_BASIC, "7300021320000|64000204100006400020440000|7300021320000640002042000064000204210006400020421000", 7.50, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204110006400020440000730002131100064000204110006400020440000", 3.58, 0.00}, {PKA_BASIC, "7300021311000|64000204210006400020421000|64000204210007300021310000730002131100073000213100007300021311000", 6.60, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020430000|6400020420000730002132000064000204200006400020430000", 8.90, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204210006400020421000|73000213300006400020411000640002042100064000204200008200022201000", 3.40, 0.00}, {PKA_BASIC, "7300021311000|64000204100006400020421000|730002131100064000204200007300021311000", 4.80, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002043000064000204200006400020421000", 7.35, 0.55}, {PKA_BASIC, "7300021310000|6400020430000|640002042100064000204210007300021310000", 7.28, 0.02}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002042000064000204200006400020421000", 6.53, 0.47}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020421000|64000204200007300021330000640002042000064000204110008200022201000", 12.22, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002042000064000204200006400020430000", 9.03, 0.82}, {PKA_BASIC, "7300021330000|640002042000064000204210006400020421000|640002043000073000213300006400020411000640002042100064000204210007300021311000", 1.70, 0.00}, {PKA_BASIC, "7300021320000|64000204200006400020440000|64000204300007300021320000640002041000064000204100006400020410000", 9.67, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210007300021330000|6400020411000640002041100073000213300006400020430000820002220100064000204210006400020421000", 4.50, 0.00}, {PKA_BASIC, "7300021320000|64000204100006400020420000|73000213200006400020420000", 8.20, 0.00}, {PKA_BASIC, "7300021311000|64000204110006400020421000|6400020411000730002131100064000204200006400020421000", 6.40, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020421000|73000213100007300021311000730002132000064000204210007300021320000", 10.40, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204200006400020430000|7300021330000640002042000064000204210006400020430000", 7.80, 0.00}, {PKA_BASIC, "7300021330000|640002042100064000204210006400020430000|6400020411000640002042100073000213300006400020420000820002220100064000204300006400020440000", 8.26, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020411000730002133000064000204200006400020420000", 7.64, 0.00}, {PKA_BASIC, "7300021320000|64000204210006400020430000|64000204100007300021320000820002220100064000204200006400020421000", 12.36, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000|64000204200007300021330000640002044000064000204400008200022220000", 12.20, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020420000|6400020410000730002133000064000204100006400020420000", 6.56, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000|64000204100007300021330000640002044000064000204300006400020440000", 7.70, 1.82}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000|64000204200007300021330000640002043000064000204200006400020421000", 6.60, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000|64000204300007300021330000640002043000064000204200006400020440000", 9.54, 0.00}, {PKA_BASIC, "7300021330000|640002041000064000204100006400020430000|730002133000064000204200006400020430000", 8.80, 0.00}, {PKA_BASIC, "7300021320000|64000204110007300021330000|6400020421000730002132000064000204200006400020420000", 1.70, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204300006400020430000|640002043000073000213300006400020420000640002043000064000204200006400020430000", 6.66, 0.00}, {PKA_BASIC, "7300021330000|640002042000064000204200006400020430000|64000204110007300021330000640002042000064000204400006400020440000", 5.40, 0.00}, }; _model.adv_a_pkas.clear(); _model.adv_b_pkas.clear(); for (auto i = 0; i < NELEM(advanced_pka_model); i++) { if (advanced_pka_model[i].type == PKA_ACID) { if (_model.adv_a_pkas.find(advanced_pka_model[i].a_fp)) _model.adv_a_pkas.remove(advanced_pka_model[i].a_fp); _model.adv_a_pkas.insert(advanced_pka_model[i].a_fp); _model.adv_a_pkas.at(advanced_pka_model[i].a_fp).push(advanced_pka_model[i].pka); _model.adv_a_pkas.at(advanced_pka_model[i].a_fp).push(advanced_pka_model[i].deviation); } else if (advanced_pka_model[i].type == PKA_BASIC) { if (_model.adv_b_pkas.find(advanced_pka_model[i].a_fp)) _model.adv_b_pkas.remove(advanced_pka_model[i].a_fp); _model.adv_b_pkas.insert(advanced_pka_model[i].a_fp); _model.adv_b_pkas.at(advanced_pka_model[i].a_fp).push(advanced_pka_model[i].pka); _model.adv_b_pkas.at(advanced_pka_model[i].a_fp).push(advanced_pka_model[i].deviation); } } _model.advanced_model_ready = true; } void MoleculePkaModel::_estimate_pKa_Simple (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas) { // QS_DEF(Array<int>, can_order); // QS_DEF(Molecule, can_mol); QS_DEF(Array<int>, ignore_atoms); QS_DEF(Array<int>, mapping); AromaticityOptions opts; // _checkCanonicalOrder(mol, can_mol, can_order); if (!mol.isAromatized()) { mol.aromatize(opts); } MoleculeSubstructureMatcher matcher(mol); matcher.fmcache = new MoleculeSubstructureMatcher::FragmentMatchCache; matcher.use_aromaticity_matcher = true; ignore_atoms.clear(); for (auto i = 0; i < _model.acids.size(); i++) { matcher.setQuery(_model.acids[i]); for (int j = 0; j < ignore_atoms.size(); j++) matcher.ignoreTargetAtom(ignore_atoms[j]); if (!matcher.find()) continue; for (;;) { mapping.clear(); mapping.copy(matcher.getQueryMapping(), _model.acids[i].vertexEnd()); for (int j = 0; j < mapping.size(); j++) { if (mapping[j] > -1) { acid_sites.push(mapping[j]); acid_pkas.push(_model.a_pkas[i]); ignore_atoms.push(mapping[j]); } } if (!matcher.findNext()) break; } } ignore_atoms.clear(); for (auto i = 0; i < _model.basics.size(); i++) { matcher.setQuery(_model.basics[i]); for (int j = 0; j < ignore_atoms.size(); j++) matcher.ignoreTargetAtom(ignore_atoms[j]); if (!matcher.find()) continue; for (;;) { mapping.clear(); mapping.copy(matcher.getQueryMapping(), _model.basics[i].vertexEnd()); for (int j = 0; j < mapping.size(); j++) { if (mapping[j] > -1) { basic_sites.push(mapping[j]); basic_pkas.push(_model.b_pkas[i]); ignore_atoms.push(mapping[j]); } } if (!matcher.findNext()) break; } } } void MoleculePkaModel::_estimate_pKa_Advanced (Molecule &mol, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas) { // QS_DEF(Array<int>, can_order); // QS_DEF(Molecule, can_mol); AromaticityOptions opts; bool _dearomatize = false; int level = options.level; int min_level = options.min_level; // _checkCanonicalOrder(mol, can_mol, can_order); for (auto i : mol.vertices()) { int a_lone = 0; mol.getVacantPiOrbitals(i, &a_lone); int a_hcnt = mol.getAtomTotalH(i); if (a_hcnt > 0) { float a_pka = getAcidPkaValue(mol, i, level, min_level); acid_sites.push(i); acid_pkas.push(a_pka); // printf("Acid site: atom index = %d, pKa = %f\n", can_order[i], a_pka); } if (a_lone > 0) { float b_pka = getBasicPkaValue(mol, i, level, min_level); basic_sites.push(i); basic_pkas.push(b_pka); // printf("Basic site: atom index = %d, pKa = %f\n", can_order[i], b_pka); } } } int MoleculePkaModel::_asc_cmp_cb (int &v1, int &v2, void *context) { QS_DEF(Array<char>, key1); QS_DEF(Array<char>, key2); int res = 0; Molecule &mol = *(Molecule *)context; key1.clear(); key2.clear(); getAtomLocalKey (mol, v1, key1); getAtomLocalKey (mol, v2, key2); res = strcmp(key1.ptr(), key2.ptr()); if (res != 0) return res; else { const Vertex &v3 = mol.getVertex(v1); for (auto i : v3.neighbors()) { getAtomLocalKey (mol, v3.neiVertex(i), key1);; } const Vertex &v4 = mol.getVertex(v2); for (auto i : v4.neighbors()) { getAtomLocalKey (mol, v4.neiVertex(i), key2);; } res = strcmp(key1.ptr(), key2.ptr()); } return res; } void MoleculePkaModel::getAtomLocalFingerprint (Molecule &mol, int idx, Array<char> &fp, int level) { QS_DEF(Array<int>, included_atoms); QS_DEF(Array<int>, dist_atoms); QS_DEF(Queue<int>, bfs_queue); QS_DEF(Array<char>, n_key); QS_DEF(Array<char>, bond); QS_DEF(Array<int>, neibs_atoms); QS_DEF(Array<int>, neibs_bonds); fp.clear(); bfs_queue.setLength(mol.vertexEnd()); bfs_queue.clear(); included_atoms.clear(); dist_atoms.clear_resize(mol.vertexEnd()); dist_atoms.zerofill(); n_key.clear(); getAtomLocalKey (mol, idx, n_key); if (n_key.size() != 0) fp.appendString(n_key.ptr(), true); if (level == 0) { return; } else { int cur_level = 0; // Mark new layer after root atom fp.appendString("|", true); bfs_queue.push(idx); while (!bfs_queue.isEmpty()) { int next = bfs_queue.pop(); int dist = dist_atoms.at(next); if (dist == level) continue; if (dist > cur_level) { cur_level = dist; // Mark next new layer fp.appendString("|", true); } const Vertex &v = mol.getVertex(next); neibs_atoms.clear(); neibs_bonds.clear(); for (auto i : v.neighbors()) { neibs_atoms.push(v.neiVertex(i)); } neibs_atoms.qsort(_asc_cmp_cb, &mol); for (auto i = 0; i < neibs_atoms.size(); i++) { for (auto j : v.neighbors()) { if (neibs_atoms[i] == v.neiVertex(j)) neibs_bonds.push(v.neiEdge(j)); } } // for (auto i : v.neighbors()) for (auto i = 0; i < neibs_atoms.size(); i++) { if (included_atoms.find(neibs_atoms[i]) == -1) { bfs_queue.push(neibs_atoms[i]); included_atoms.push(neibs_atoms[i]); dist_atoms[neibs_atoms[i]] = dist + 1; bond.clear(); ArrayOutput tmp(bond); n_key.clear(); getAtomLocalKey(mol, neibs_atoms[i], n_key); if (n_key.size() == 0) continue; // int order = mol.getBondOrder(neibs_bonds[i]); // tmp.printf(":%d:", order); // tmp.writeChar(0); // fp.appendString(bond.ptr(), true); fp.appendString(n_key.ptr(), true); } } } // Remove empty layer, if it was created if (fp[fp.size() - 2] == '|') fp.remove(fp.size() - 2); } return; } void MoleculePkaModel::_removeExtraHydrogens(Molecule &mol) { QS_DEF(Array<int>, to_remove); to_remove.clear(); for (auto i : mol.vertices()) if (mol.convertableToImplicitHydrogen(i)) to_remove.push(i); if (to_remove.size() > 0) mol.removeAtoms(to_remove); } void MoleculePkaModel::_checkCanonicalOrder(Molecule &mol, Molecule &can_mol, Array<int> &order) { QS_DEF(Array<int>, to_remove); QS_DEF(Array<int>, ignored); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); to_remove.clear(); for (auto i : mol.vertices()) if (mol.convertableToImplicitHydrogen(i)) to_remove.push(i); if (to_remove.size() > 0) mol.removeAtoms(to_remove); MoleculeAutomorphismSearch as; as.detect_invalid_cistrans_bonds = false; as.detect_invalid_stereocenters = false; as.find_canonical_ordering = true; as.ignored_vertices = ignored.ptr(); as.process(mol); as.getCanonicalNumbering(order); can_mol.makeSubmolecule(mol, order, NULL); // printMolfile(mol); // printMolfile(can_mol); } void MoleculePkaModel::getAtomLocalKey (Molecule &mol, int idx, Array<char> &fp) { QS_DEF(Array<char>, key); if (mol.isPseudoAtom(idx) || mol.isRSite(idx) || mol.isTemplateAtom(idx)) { QS_DEF(Array<char>, a_desc); mol.getAtomDescription(idx, a_desc); throw Error("pKa model can't used with atom : %s", a_desc.ptr()); } int a_num = mol.getAtomNumber(idx); // Just bypass the hyfrogen atom if (a_num == ELEM_H) return; int a_val = mol.getAtomValence(idx); int a_chg = mol.getAtomCharge(idx); int a_rad = mol.getAtomRadical(idx); int a_iso = mol.getAtomIsotope(idx); int a_arom = mol.getAtomAromaticity(idx); int a_conn = mol.getAtomConnectivity(idx); int a_lone = 0; mol.getVacantPiOrbitals(idx, &a_lone); int a_hcnt = mol.getAtomTotalH(idx); int a_single_cnt = 0; int a_double_cnt = 0; int a_aromatic_cnt = 0; int a_triple_cnt = 0; int a_coord_cnt =0; const Vertex &vertex = mol.getVertex(idx); for (auto i : vertex.neighbors()) { int order = mol.getBondOrder(vertex.neiEdge(i)); if (order == BOND_SINGLE) a_single_cnt++; else if (order == BOND_DOUBLE) a_double_cnt++; else if (order == BOND_AROMATIC) a_aromatic_cnt++; else if (order == BOND_TRIPLE) a_triple_cnt++; else if (order == BOND_ZERO) a_coord_cnt++; } key.clear(); ArrayOutput output(key); // output.printf("%d%d%d%d%d%d%d%d%d%d%d%d%d%d", // a_num, a_val, a_chg, a_rad, a_iso, a_arom, a_lone, a_hcnt, // a_conn, a_single_cnt, a_double_cnt, a_aromatic_cnt, a_triple_cnt, a_coord_cnt); output.printf("%d%d%d%d%d%d%d%d%d%d%d%d%d", a_num, a_val, a_chg, a_rad, a_iso, a_arom, a_lone, a_conn, a_single_cnt, a_double_cnt, a_aromatic_cnt, a_triple_cnt, a_coord_cnt); output.writeChar(0); fp.appendString(key.ptr(), true); return; } float MoleculePkaModel::getAcidPkaValue (Molecule &mol, int idx, int level, int min_level) { QS_DEF(Array<char>, fp); QS_DEF(Array<int>, level_pos); fp.clear(); level_pos.clear(); float pka = 100.f; int a_num = mol.getAtomNumber(idx); if (a_num == ELEM_H) return pka; getAtomLocalFingerprint (mol, idx, fp, level); // printf("Acid site: atom index = %d, fp = %s\n", idx, fp.ptr()); if (_model.adv_a_pkas.find(fp.ptr())) { pka = _model.adv_a_pkas.at(fp.ptr())[0]; // printf("Acid site found: fp = %s level = %d pka = %4.2f, dev = %4.2f\n", fp.ptr(), level, pka, // _model.adv_a_pkas.at(fp.ptr())[1]); } else { int levels = fp.count('|'); int beg = 0; int end = fp.size(); for (int i = 0; i < levels; i++) { beg = fp.find(beg + 1, end, '|'); level_pos.push(beg); } for (int i = 0; i < level_pos.size(); i++) { if ((level_pos.size() - i - 1) < min_level) break; int next_layer = level_pos[level_pos.size() - i - 1]; fp.remove(next_layer, fp.size() - next_layer - 1); // printf("Try FP = %s level = %d\n", fp.ptr(), level_pos.size() - i); if (_model.adv_a_pkas.find(fp.ptr())) { pka = _model.adv_a_pkas.at(fp.ptr())[0]; // printf("Acid site found: fp = %s level = %d pka = %4.2f, dev = %4.2f\n", fp.ptr(), level_pos.size() - i, pka, // _model.adv_a_pkas.at(fp.ptr())[1]); break; } } } return pka; } float MoleculePkaModel::getBasicPkaValue (Molecule &mol, int idx, int level, int min_level) { QS_DEF(Array<char>, fp); QS_DEF(Array<int>, level_pos); fp.clear(); level_pos.clear(); float pka = -100.f; int a_num = mol.getAtomNumber(idx); if (a_num == ELEM_H) return pka; getAtomLocalFingerprint (mol, idx, fp, level); // printf("Basic site: atom index = %d, fp = %s\n", idx, fp.ptr()); if (_model.adv_b_pkas.find(fp.ptr())) { pka = (_model.adv_b_pkas.at(fp.ptr())[0]); // printf("Basic site found: fp = %s pka = %4.2f, dev = %4.2f\n", fp.ptr(), pka, // _model.adv_b_pkas.at(fp.ptr())[1]); } else { int levels = fp.count('|'); int beg = 0; int end = fp.size(); for (int i = 0; i < levels; i++) { beg = fp.find(beg + 1, end, '|'); level_pos.push(beg); } for (int i = 0; i < level_pos.size(); i++) { if ((level_pos.size() - i - 1) < min_level) break; int next_layer = level_pos[level_pos.size() - i - 1]; fp.remove(next_layer, fp.size() - next_layer - 1); if (_model.adv_b_pkas.find(fp.ptr())) { pka = _model.adv_b_pkas.at(fp.ptr())[0]; // printf("Basic site found: fp = %s level = %d pka = %4.2f, dev = %4.2f\n", fp.ptr(), level_pos.size() - i, pka, // _model.adv_b_pkas.at(fp.ptr())[1]); break; } } } return pka; } IMPL_ERROR(MoleculeIonizer, "Molecule Ionizer"); CP_DEF(MoleculeIonizer); MoleculeIonizer::MoleculeIonizer(): CP_INIT{ } bool MoleculeIonizer::ionize (Molecule &mol, float ph, float ph_toll, const IonizeOptions &options) { QS_DEF(Array<int>, acid_sites); QS_DEF(Array<int>, basic_sites); QS_DEF(Array<float>, acid_pkas); QS_DEF(Array<float>, basic_pkas); acid_sites.clear(); basic_sites.clear(); acid_pkas.clear(); basic_pkas.clear(); MoleculePkaModel::estimate_pKa(mol, options, acid_sites, basic_sites, acid_pkas, basic_pkas); if (acid_sites.size() > 0 || basic_sites.size() > 0) _setCharges(mol, ph, ph_toll, options, acid_sites, basic_sites, acid_pkas, basic_pkas); return true; } void MoleculeIonizer::_setCharges (Molecule &mol, float pH, float pH_toll, const IonizeOptions &options, Array<int> &acid_sites, Array<int> &basic_sites, Array<float> &acid_pkas, Array<float> &basic_pkas) { for (auto i = 0; i < acid_sites.size(); i++) { if ((acid_pkas[i] - pH) < pH_toll) { mol.setAtomCharge(acid_sites[i], mol.getAtomCharge(acid_sites[i]) - 1); // printf("Acid site: atom index = %d, pKa = %f\n", acid_sites[i], acid_pkas[i]); } } for (auto i = 0; i < basic_sites.size(); i++) { if ((basic_pkas[i] - pH) > -pH_toll) { mol.setAtomCharge(basic_sites[i], mol.getAtomCharge(basic_sites[i]) + 1); // printf("Basic site: atom index = %d, pKa = %f\n", basic_sites[i], basic_pkas[i]); } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_layered_molecules.cpp�������������������������������������0000664�0000000�0000000�00000054556�12710376503�0024702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_layered_molecules.h" #include "base_c/defs.h" #include "base_cpp/output.h" #include "graph/cycle_enumerator.h" #include "molecule/elements.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_dearom.h" #include "molecule/molecule_standardize.h" using namespace indigo; LayeredMolecules::LayeredMolecules(BaseMolecule& molecule) :_layersAromatized(0) { _proto.clone(molecule.asMolecule(), 0, 0); _proto.dearomatize(AromaticityOptions()); cloneGraph(_proto, 0); for (auto e_idx : _proto.edges()) { for(auto i = 0; i < BOND_TYPES_NUMBER; ++i) { _bond_masks[i].push(); _bond_masks[i].top().resize(1); } _bond_masks[BOND_ZERO].top().reset(0); _bond_masks[BOND_SINGLE].top().reset(0); _bond_masks[BOND_DOUBLE].top().reset(0); _bond_masks[BOND_TRIPLE].top().reset(0); _bond_masks[BOND_AROMATIC].top().reset(0); _bond_masks[_proto.getBondOrder(e_idx)].top().set(0); } _mobilePositions.expandFill(_proto.vertexCount(), false); _mobilePositionsOccupied.expand(_proto.vertexCount()); layers = 1; unsigned node = _trie.getRoot(); for (auto i : _proto.edges()) { bool stub; node = _trie.add(node, _proto.getBondOrder(i), stub); } _hashs.push(node); } LayeredMolecules::~LayeredMolecules() { } void LayeredMolecules::constructMolecule(Molecule &molecule, int layer, bool aromatized) const { molecule.clone(const_cast<Molecule&>(_proto), NULL, NULL); molecule.clearXyz(); for (auto i : const_cast<Molecule&>(_proto).edges()) { int order = BOND_ZERO; _bond_masks[BOND_SINGLE][i].get(layer)? order = BOND_SINGLE: 0; _bond_masks[BOND_DOUBLE][i].get(layer)? order = BOND_DOUBLE: 0; _bond_masks[BOND_TRIPLE][i].get(layer)? order = BOND_TRIPLE: 0; molecule.setBondOrder(i, order); } for (auto i : const_cast<Molecule&>(_proto).vertices()) { molecule.setAtomCharge(i, const_cast<Molecule&>(_proto).getAtomCharge(i)); } // Actually I would prefer to aromatize the molecule manually (and much more effective) as far as I have the list of aromatic bonds already. // But I don't have any approprite molecule API to do it effectively... :( if(aromatized) molecule.aromatize(AromaticityOptions()); } void LayeredMolecules::clear() { BaseMolecule::clear(); } const Dbitset &LayeredMolecules::getBondMask(int idx, int order) const { return _bond_masks[order][idx]; } bool LayeredMolecules::isMobilePosition(int idx) const { return _mobilePositions[idx]; } void LayeredMolecules::setMobilePosition(int idx, bool value) { _mobilePositions[idx] = value; } const Dbitset &LayeredMolecules::getMobilePositionOccupiedMask(int idx) const { return _mobilePositionsOccupied[idx]; } void LayeredMolecules::setMobilePositionOccupiedMask(int idx, Dbitset &mask, bool value) { if (value) _mobilePositionsOccupied[idx].orWith(mask); else _mobilePositionsOccupied[idx].andNotWith(mask); } bool LayeredMolecules::addLayersWithInvertedPath(const Dbitset &mask, const Array<int> &edgesPath, int beg, int end, bool forward) // mask: the mask of layers used as prototypes; // edgesPath: the path of single-double bonds to be inverted // edgesPath: a sequence of edges with intercganging single-double bonds that need to be inverted // beg, end: the mobile positions of hydrogen to swap // forward: the direction to move the hydrogen { QS_DEF(Array<bool>, edgeIsOnPath); // indicates if an edge is on path that needs to be inverted edgeIsOnPath.clear(); edgeIsOnPath.expandFill(edgeCount(), false); for (auto i = 0; i < edgesPath.size(); ++i) { edgeIsOnPath[edgesPath[i]] = true; } QS_DEF(Dbitset, maskCopy); maskCopy.copy(mask); unsigned newTautomerIndex; while (!maskCopy.isEmpty()) { newTautomerIndex = layers; int prototypeIndex = maskCopy.nextSetBit(0); _resizeLayers(newTautomerIndex + 1); unsigned node = _trie.getRoot(); bool unique = false; for (auto i = 0; i < edgeCount(); ++i) { int order = 0; if(_bond_masks[BOND_SINGLE][i].get(prototypeIndex)) order = 1; else if(_bond_masks[BOND_DOUBLE][i].get(prototypeIndex)) order = 2; if(edgeIsOnPath[i]) { order = (order == 1? 2: 1); } bool newlyAdded; node = _trie.add(node, order, newlyAdded); unique = (newlyAdded? true: unique); _bond_masks[order][i].set(newTautomerIndex); _bond_masks[BOND_TRIPLE][i].reset(newTautomerIndex); _bond_masks[BOND_AROMATIC][i].reset(newTautomerIndex); _bond_masks[order == 1? BOND_DOUBLE: BOND_SINGLE][i].reset(newTautomerIndex); } if(!unique) { maskCopy.reset(prototypeIndex); continue; } for (auto i = 0; i < _mobilePositionsOccupied.size(); ++i) { if (_mobilePositionsOccupied[i].get(prototypeIndex)) _mobilePositionsOccupied[i].set(newTautomerIndex); } _hashs.push(node); ++layers; maskCopy.reset(prototypeIndex); _mobilePositionsOccupied[forward? beg: end].reset(newTautomerIndex); _mobilePositionsOccupied[forward? end: beg].set(newTautomerIndex); } if(newTautomerIndex == layers) { // This means that we avoided adding non-unique layer, and we need to reduce the size of bitsets. _resizeLayers(layers); return false; } return true; } bool LayeredMolecules::addLayerFromMolecule(const Molecule &molecule, Array<int> &aam) { Array<int> aam_inverse; aam_inverse.expandFill(aam.size(), -1); for(int i = 0; i < aam.size(); ++i) { if(aam[i] != -1) aam_inverse[aam[i]] = i; } unsigned newTautomerIndex = layers; _resizeLayers(newTautomerIndex + 1); for(auto e1_idx : edges()) { _bond_masks[BOND_ZERO][e1_idx].reset(newTautomerIndex); _bond_masks[BOND_SINGLE][e1_idx].reset(newTautomerIndex); _bond_masks[BOND_DOUBLE][e1_idx].reset(newTautomerIndex); _bond_masks[BOND_TRIPLE][e1_idx].reset(newTautomerIndex); _bond_masks[BOND_AROMATIC][e1_idx].reset(newTautomerIndex); } unsigned node = _trie.getRoot(); bool unique = false; for(auto e2_idx : const_cast<Molecule&>(molecule).edges()) { auto e2 = molecule.getEdge(e2_idx); int u2 = e2.beg; int v2 = e2.end; int u1 = aam_inverse[u2]; int v1 = aam_inverse[v2]; if(u1 == -1 || v1 == -1) continue; int e1_idx = findEdgeIndex(u1, v1); if(e1_idx == -1) { e1_idx = addEdge(u1, v1); _proto.addEdge(u1, v1); _proto.setBondOrder(e1_idx, BOND_ZERO, false); _bond_masks[BOND_ZERO].resize(e1_idx + 1); _bond_masks[BOND_SINGLE].resize(e1_idx + 1); _bond_masks[BOND_DOUBLE].resize(e1_idx + 1); _bond_masks[BOND_TRIPLE].resize(e1_idx + 1); _bond_masks[BOND_AROMATIC].resize(e1_idx + 1); } int order = const_cast<Molecule&>(molecule).getBondOrder(e2_idx); _bond_masks[order][e1_idx].set(newTautomerIndex); } for (auto e1_idx : edges()) { int order = BOND_ZERO; if(_bond_masks[BOND_SINGLE][e1_idx].get(newTautomerIndex)) order = BOND_SINGLE; else if(_bond_masks[BOND_DOUBLE][e1_idx].get(newTautomerIndex)) order = BOND_DOUBLE; else if(_bond_masks[BOND_TRIPLE][e1_idx].get(newTautomerIndex)) order = BOND_TRIPLE; bool newlyAdded; node = _trie.add(node, order, newlyAdded); unique = (newlyAdded ? true : unique); } if(unique) { ++layers; return true; } // This means that we avoided adding non-unique layer, and we need to reduce the size of bitsets. _resizeLayers(layers); return false; } int LayeredMolecules::getAtomNumber(int idx) { return _proto.getAtomNumber(idx); } int LayeredMolecules::getAtomCharge(int idx) { return _proto.getAtomCharge(idx); } int LayeredMolecules::getAtomIsotope(int idx) { return _proto.getAtomIsotope(idx); } int LayeredMolecules::getAtomRadical(int idx) { return _proto.getAtomRadical(idx); } int LayeredMolecules::getAtomAromaticity(int idx) { // return _proto.getAtomAromaticity(idx); return true; } int LayeredMolecules::getExplicitValence(int idx) { return _proto.getExplicitValence(idx); } int LayeredMolecules::getAtomValence(int idx) { return _proto.getAtomValence(idx); } int LayeredMolecules::getAtomSubstCount(int idx) { return _proto.getAtomSubstCount(idx); } int LayeredMolecules::getAtomRingBondsCount(int idx) { return _proto.getAtomSubstCount(idx); } int LayeredMolecules::getAtomMaxH(int idx) { return getAtomTotalH(idx); } int LayeredMolecules::getAtomMinH(int idx) { return getAtomTotalH(idx); } int LayeredMolecules::getAtomTotalH(int idx) { throw Error("getAtomTotalH method has no sense for LayeredMolecules"); } bool LayeredMolecules::isPseudoAtom(int idx) { return _proto.isPseudoAtom(idx); } const char * LayeredMolecules::getPseudoAtom(int idx) { return _proto.getPseudoAtom(idx); } bool LayeredMolecules::isTemplateAtom(int idx) { return _proto.isTemplateAtom(idx); } const char * LayeredMolecules::getTemplateAtom(int idx) { return _proto.getTemplateAtom(idx); } const int LayeredMolecules::getTemplateAtomSeqid(int idx) { return _proto.getTemplateAtomSeqid(idx); } const char * LayeredMolecules::getTemplateAtomClass(int idx) { return _proto.getTemplateAtomClass(idx); } const int LayeredMolecules::getTemplateAtomDisplayOption(int idx) { return _proto.getTemplateAtomDisplayOption(idx); } bool LayeredMolecules::isRSite(int idx) { return _proto.isRSite(idx); } dword LayeredMolecules::getRSiteBits(int idx) { return _proto.getRSiteBits(idx); } void LayeredMolecules::allowRGroupOnRSite(int atom_idx, int rg_idx) { throw Error("allowRGroupOnRSite method is not implemented in LayeredMolecules class"); } int LayeredMolecules::getBondOrder(int idx) { throw Error("getBondOrder method has no sense for LayeredMolecules"); } int LayeredMolecules::getBondTopology(int idx) { throw Error("getBondTopology method is not implemented in LayeredMolecules class"); } bool LayeredMolecules::atomNumberBelongs(int idx, const int *numbers, int count) { return _proto.atomNumberBelongs(idx, numbers, count); } bool LayeredMolecules::possibleAtomNumber(int idx, int number) { return _proto.possibleAtomNumber(idx, number); } bool LayeredMolecules::possibleAtomNumberAndCharge(int idx, int number, int charge) { return _proto.possibleAtomNumberAndCharge(idx, number, charge); } bool LayeredMolecules::possibleAtomNumberAndIsotope(int idx, int number, int isotope) { return _proto.possibleAtomNumberAndIsotope(idx, number, isotope); } bool LayeredMolecules::possibleAtomIsotope(int idx, int isotope) { return _proto.possibleAtomIsotope(idx, isotope); } bool LayeredMolecules::possibleAtomCharge(int idx, int charge) { return _proto.possibleAtomCharge(idx, charge); } void LayeredMolecules::getAtomDescription(int idx, Array<char> &description) { return _proto.getAtomDescription(idx, description); } void LayeredMolecules::getBondDescription(int idx, Array<char> &description) { throw Error("getBondDescription method is not implemented in LayeredMolecules class"); } bool LayeredMolecules::possibleBondOrder(int idx, int order) { throw Error("possibleBondOrder method has no sense for LayeredMolecules"); } bool LayeredMolecules::isSaturatedAtom(int idx) { throw Error("isSaturatedAtom method is not implemented in LayeredMolecules class"); } bool LayeredMolecules::bondStereoCare(int idx) { throw Error("bondStereoCare method is not implemented in LayeredMolecules class"); } bool LayeredMolecules::aromatize(const AromaticityOptions &options) { return aromatize(_layersAromatized, layers, options); } bool LayeredMolecules::dearomatize(const AromaticityOptions &options) { return _proto.dearomatize(options); } void LayeredMolecules::_mergeWithSubmolecule(BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) { throw Error("_mergeWithSubmolecule method is not implemented in LayeredMolecules class"); } BaseMolecule * LayeredMolecules::neu() { throw Error("neu method is not implemented in LayeredMolecules class"); } void LayeredMolecules::_resizeLayers(int newSize) { for (auto i : _proto.edges()) { _bond_masks[BOND_ZERO][i].resize(newSize); _bond_masks[BOND_SINGLE][i].resize(newSize); _bond_masks[BOND_DOUBLE][i].resize(newSize); _bond_masks[BOND_TRIPLE][i].resize(newSize); _bond_masks[BOND_AROMATIC][i].resize(newSize); } for (auto i : _proto.vertices()) { _mobilePositionsOccupied[i].resize(newSize); } } void LayeredMolecules::_calcConnectivity(int layerFrom, int layerTo) { _connectivity.resize(_proto.vertexEnd()); for(auto v_idx : _proto.vertices()) { _connectivity[v_idx].expandFill(layerTo, 0); } for(auto bond_idx : _proto.edges()) { const Edge &edge = _proto.getEdge(bond_idx); const Dbitset &bs1 = _bond_masks[BOND_SINGLE][bond_idx]; const Dbitset &bs2 = _bond_masks[BOND_DOUBLE][bond_idx]; const Dbitset &bs3 = _bond_masks[BOND_TRIPLE][bond_idx]; for(auto l = layerFrom; l < layerTo; ++l) { int order = 0; if(bs1.get(l)) order = 1; else if(bs2.get(l)) order = 2; if(bs3.get(l)) order = 3; _connectivity[edge.beg][l] += order; _connectivity[edge.end][l] += order; } } } void LayeredMolecules::_calcPiLabels(int layerFrom, int layerTo) { _piLabels.resize(_proto.vertexEnd()); QS_DEF(Dbitset, skip); skip.resize(layers); QS_DEF(Array<int>, non_arom_conn); QS_DEF(Array<int>, arom_bonds); QS_DEF(Array<int>, n_double_ext); QS_DEF(Array<int>, n_double_ring); non_arom_conn.resize(layers); arom_bonds.resize(layers); n_double_ext.resize(layers); n_double_ring.resize(layers); for(auto v_idx : _proto.vertices()) { skip.clear(); _piLabels[v_idx].expandFill(layers, -1); if(!_proto.vertexInRing(v_idx) || !Element::canBeAromatic(_proto.getAtomNumber(v_idx))) { _piLabels[v_idx].fill(-1); continue; } const Vertex &vertex = _proto.getVertex(v_idx); non_arom_conn.fill(0); arom_bonds.fill(0); n_double_ext.fill(0); n_double_ring.fill(0); for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int bond_idx = vertex.neiEdge(i); //const Dbitset &bs1 = _bond_masks[BOND_SINGLE][bond_idx]; const Dbitset &bs2 = _bond_masks[BOND_DOUBLE][bond_idx]; const Dbitset &bs3 = _bond_masks[BOND_TRIPLE][bond_idx]; const Dbitset &bsArom = _bond_masks[BOND_AROMATIC][bond_idx]; for(auto l = layerFrom; l < layerTo; ++l) { if(bs3.get(l)) { skip.set(l); continue; } if(bs2.get(l)) { if(_proto.getBondTopology(bond_idx) == TOPOLOGY_RING) // needs to be layer by layer { ++n_double_ring[l]; } else { if(!_proto.isNitrogenV5(v_idx)) // needs to be checked layer by layer { skip.set(l); continue; } else ++n_double_ext[l]; } } if(bsArom.get(l)) ++arom_bonds[l]; else ++non_arom_conn[l]; } } for(auto l = layerFrom; l < layerTo; ++l) { if(skip.get(l)) continue; if (arom_bonds[l] == 0) { // Verify that this atom has valid valence // TBD } if(n_double_ring[l] > 0) _piLabels[v_idx][l] = 1; if(n_double_ext[l] > 1) { _piLabels[v_idx][l] = -1; skip.set(l); } else if (n_double_ext[l] == 1) { // Only a single external double bond that was accepted in _acceptOutgoingDoubleBond // It means that it is C=S, C=O, or C=N, like in O=C1NC=CC(=O)N1 int atom_number = _proto.getAtomNumber(v_idx); if (atom_number == ELEM_S) _piLabels[v_idx][l] = 2; _piLabels[v_idx][l] = 0; } if(_piLabels[v_idx][l] != -1) continue; int conn = _connectivity[v_idx][l]; int valence; int impl_h; Element::calcValence(_proto.getAtomNumber(v_idx), _proto.getAtomCharge(v_idx), 0, conn, valence, impl_h, false); conn += impl_h; if (arom_bonds[l] != 0) { // Atom is already aromatic and in general number of hydrogens // cannot be deduced. But if atom can have one single or onle // double bond while being aromatic then pi label can be calculated // Currently not implemented } int group = Element::group(_proto.getAtomNumber(v_idx)); int charge = 0;// getAtomCharge(atom_idx); int radical = 0;// getAtomRadical(atom_idx); int lonepairs = 0; if(BaseMolecule::getVacantPiOrbitals(group, charge, radical, conn, &lonepairs) > 0) _piLabels[v_idx][l] = 0; else if (lonepairs > 0) _piLabels[v_idx][l] = 2; } } } bool LayeredMolecules::_cb_handle_cycle (Graph &graph, const Array<int> &vertices, const Array<int> &edges, void *context) { AromatizationContext *aromatizationContext = (AromatizationContext *)context; LayeredMolecules *self = aromatizationContext->self; self->_handleCycle(aromatizationContext->layerFrom, aromatizationContext->layerTo, vertices); return true; } bool LayeredMolecules::_handleCycle (int layerFrom, int layerTo, const Array<int> &path) { // Check Huckel's rule QS_DEF(Dbitset, satisfiesRule); satisfiesRule.resize(layerTo); satisfiesRule.clear(); for(auto l = layerFrom; l < layerTo; ++l) { if(_isCycleAromaticInLayer(path.ptr(), path.size(), l)) { satisfiesRule.set(l); } } if(!satisfiesRule.isEmpty()) { _aromatizeCycle(path, satisfiesRule); return true; } return false; } bool LayeredMolecules::_isCycleAromaticInLayer (const int *cycle, int cycle_len, int layer) { int count = 0; // Check Huckel's rule for (int i = 0; i < cycle_len; ++i) count += _piLabels[cycle[i]][layer]; if (((count - 2) % 4) != 0) return false; return true; } void LayeredMolecules::_aromatizeCycle (const Array<int> &cycle, const Dbitset &mask) { for(auto i = 0; i < cycle.size(); ++i) { const Vertex &vertex = _proto.getVertex(cycle[i]); for(int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int bond_idx = vertex.neiEdge(j); _bond_masks[BOND_AROMATIC][bond_idx].orWith(mask); // We are able to store both aromatic and non-aromatic bonds. But in case we need to store only one type, uncomment next lines. //_bond_masks[BOND_ZERO][bond_idx].andNotWith(mask); //_bond_masks[BOND_SINGLE][bond_idx].andNotWith(mask); //_bond_masks[BOND_DOUBLE][bond_idx].andNotWith(mask); //_bond_masks[BOND_TRIPLE][bond_idx].andNotWith(mask); } } } void LayeredMolecules::_registerAromatizedLayers(int layerFrom, int layerTo) { _hashsAromatized.resize(layerTo); for(auto l = layerFrom; l < layerTo; ++l) { unsigned node = _trie.getRoot(); bool unique = false; bool aromatic = false; for (auto i : _proto.edges()) { int order = 0; if(_bond_masks[BOND_AROMATIC][i].get(l)) { order = 4; aromatic = true; } else { if(_bond_masks[BOND_SINGLE][i].get(l)) order = 1; else if(_bond_masks[BOND_DOUBLE][i].get(l)) order = 2; else if(_bond_masks[BOND_TRIPLE][i].get(l)) order = 3; } node = _trie.add(node, order, unique); } if(aromatic) { for(auto i : _proto.vertices()) { int piLabel = 0; if(_piLabels[i][l] != -1) { piLabel = _piLabels[i][l]; } node = _trie.add(node, piLabel, unique); } _hashsAromatized[l] = node; } else { _hashsAromatized[l] = 0; } } } bool LayeredMolecules::aromatize(int layerFrom, int layerTo, const AromaticityOptions &options) { if(layerFrom == layerTo) return false; _calcConnectivity(layerFrom, layerTo); _calcPiLabels(layerFrom, layerTo); CycleEnumerator cycle_enumerator(_proto); cycle_enumerator.cb_handle_cycle = _cb_handle_cycle; cycle_enumerator.max_length = 22; AromatizationContext context; context.self = this; context.layerFrom = layerFrom; context.layerTo = layerTo; context.result = false; cycle_enumerator.context = &context; cycle_enumerator.process(); _registerAromatizedLayers(layerFrom, layerTo); if(layerFrom <= _layersAromatized && _layersAromatized < layerTo) _layersAromatized = layerTo; return context.result; } ��������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_mass.cpp��������������������������������������������������0000664�0000000�0000000�00000013420�12710376503�0022131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_mass.h" #include "molecule/molecule.h" #include "molecule/elements.h" using namespace indigo; MoleculeMass::MoleculeMass() { relative_atomic_mass_map = NULL; } float MoleculeMass::molecularWeight (Molecule &mol) { mol.restoreAromaticHydrogens(); double molmass = 0; int impl_h = 0; int elements_count[ELEM_MAX] = {0}; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.isPseudoAtom(v) || mol.isRSite(v) || mol.isTemplateAtom(v)) { continue; } int number = mol.getAtomNumber(v); int isotope = mol.getAtomIsotope(v); if (isotope == 0) { float *value = 0; if (relative_atomic_mass_map != NULL) { value = relative_atomic_mass_map->at2(number); } if (value == 0) { elements_count[number]++; } else { molmass += *value; } } else { molmass += Element::getRelativeIsotopicMass(number, isotope); } // Add hydrogens impl_h += mol.getImplicitH(v); } for (int i = ELEM_MIN; i < ELEM_MAX; i++) { if (elements_count[i]) { molmass += Element::getStandardAtomicWeight(i) * (double)elements_count[i]; } } molmass += Element::getStandardAtomicWeight(ELEM_H) * impl_h; return (float)molmass; } static int _isotopesCmp (int i1, int i2, void *context) { int element = *(int *)context; float c1, c2; Element::getIsotopicComposition(element, i1, c1); Element::getIsotopicComposition(element, i2, c2); if (c1 < c2) return 1; else if (c1 > c2) return -1; return 0; } float MoleculeMass::mostAbundantMass (Molecule &mol) { mol.restoreAromaticHydrogens(); double molmass = 0; // Count elements without explicit isotope marks int elements_counts[ELEM_MAX] = {0}; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.isPseudoAtom(v) || mol.isTemplateAtom(v)) continue; int number = mol.getAtomNumber(v); int isotope = mol.getAtomIsotope(v); int impl_h = mol.getImplicitH(v); if (isotope == 0) elements_counts[number]++; else molmass += Element::getRelativeIsotopicMass(number, isotope); // Add hydrogens elements_counts[ELEM_H] += impl_h; } QS_DEF(Array<int>, isotopes); // Compute mass of the most abunant composition for (int i = ELEM_MIN; i < ELEM_MAX; i++) { int count = elements_counts[i]; if (count == 0) continue; int count_left = count; int min_iso, max_iso; // Sort by isotope abundance isotopes.clear(); Element::getMinMaxIsotopeIndex(i, min_iso, max_iso); for (int j = min_iso; j <= max_iso; j++) { float composition; if (!Element::getIsotopicComposition(i, j, composition)) continue; if (composition > 0) isotopes.push(j); } isotopes.qsort(_isotopesCmp, (void *)&i); for (int k = 0; k < isotopes.size(); k++) { int j = isotopes[k]; float composition; if (!Element::getIsotopicComposition(i, j, composition)) continue; int such_isotope_count = (int)(composition * count / 100 + 0.5f); molmass += Element::getRelativeIsotopicMass(i, j) * such_isotope_count; count_left -= such_isotope_count; if (count_left == 0) break; } if (count_left != 0) { // Corrections in case of rounding errors int default_iso = Element::getMostAbundantIsotope(i); molmass += Element::getRelativeIsotopicMass(i, default_iso) * count_left; } } return (float)molmass; } float MoleculeMass::monoisotopicMass (Molecule &mol) { mol.restoreAromaticHydrogens(); double molmass = 0; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.isPseudoAtom(v) || mol.isTemplateAtom(v)) continue; int number = mol.getAtomNumber(v); int isotope = mol.getAtomIsotope(v); int impl_h = mol.getImplicitH(v); if (isotope == 0) isotope = Element::getMostAbundantIsotope(number); molmass += Element::getRelativeIsotopicMass(number, isotope); // Add hydrogens molmass += Element::getRelativeIsotopicMass(ELEM_H, 1) * impl_h; } return (float)molmass; } int MoleculeMass::nominalMass (Molecule &mol) { mol.restoreAromaticHydrogens(); int molmass = 0; for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { if (mol.isPseudoAtom(v) || mol.isTemplateAtom(v)) continue; int number = mol.getAtomNumber(v); int isotope = mol.getAtomIsotope(v); int impl_h = mol.getImplicitH(v); if (isotope == 0) molmass += Element::getDefaultIsotope(number); else molmass += isotope; // Add hydrogens molmass += impl_h; } return molmass; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_neighbourhood_counters.cpp��������������������������������0000664�0000000�0000000�00000026704�12710376503�0025755�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_neighbourhood_counters.h" #include "base_cpp/tlscont.h" #include "base_cpp/exception.h" #include "molecule/molecule.h" #include "molecule/elements.h" #include "molecule/query_molecule.h" using namespace indigo; void MoleculeAtomNeighbourhoodCounters::calculate (Molecule &mol) { _calculate(mol, false); } void MoleculeAtomNeighbourhoodCounters::calculate (QueryMolecule &mol) { _calculate(mol, true); } void MoleculeAtomNeighbourhoodCounters::_calculate (BaseMolecule &mol, bool is_query) { _per_atom_counters.resize(mol.vertexEnd()); _per_atom_counters.zerofill(); _use_atom.resize(mol.vertexEnd()); _use_atom.zerofill(); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (!mol.possibleAtomNumber(i, ELEM_H) && !mol.isRSite(i)) _use_atom[i] = 1; /* if (mol.getAtomNumber(i) != -1 && mol.getAtomNumber(i) != ELEM_H) _use_atom[i] = 1; */ } _calculateLevel0(mol, is_query); for (int r = 1; r < Counters::RADIUS; r++) _calculateNextLevel(mol, r); // Find differences to determine counter in neighbourhood ring slices for (int r = Counters::RADIUS - 1; r > 0; r--) { for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { Counters &cnt = _per_atom_counters[i]; CountersPerRadius &cur = cnt.per_rad[r]; CountersPerRadius &prev = cnt.per_rad[r - 1]; cur.C_cnt -= prev.C_cnt; cur.hetero_cnt -= prev.hetero_cnt; cur.heteroN_cnt -= prev.heteroN_cnt; cur.heteroO_cnt -= prev.heteroO_cnt; cur.trip_cnt -= prev.trip_cnt; cur.degree_sum -= prev.degree_sum; } } } bool MoleculeAtomNeighbourhoodCounters::_isAtomInformationStored (int atom_idx) const { if (atom_idx >= _use_atom.size() || _use_atom[atom_idx] == 0) return false; return true; } bool MoleculeAtomNeighbourhoodCounters::testSubstructure (const MoleculeAtomNeighbourhoodCounters &target_counters, int query_atom_idx, int target_atom_idx, bool use_bond_types) const { // Temporary patch to fix issure with RGroups if (!_isAtomInformationStored(query_atom_idx)) return true; if (!target_counters._isAtomInformationStored(target_atom_idx)) return true; // End const Counters &target_atom_counters = target_counters._per_atom_counters[target_atom_idx]; const Counters &query_atom_counters = _per_atom_counters[query_atom_idx]; return query_atom_counters.testSubstructure(target_atom_counters, use_bond_types); } bool MoleculeAtomNeighbourhoodCounters::CountersPerRadius::testSubstructure (const CountersPerRadius &target, bool use_bond_types) const { if (C_cnt > target.C_cnt) return false; if (hetero_cnt > target.hetero_cnt) return false; if (heteroN_cnt > target.heteroN_cnt) return false; if (heteroO_cnt > target.heteroO_cnt) return false; if (trip_cnt > target.trip_cnt) return false; if (use_bond_types) if (degree_sum > target.degree_sum) return false; return true; } bool MoleculeAtomNeighbourhoodCounters::Counters::testSubstructure (const Counters &target, bool use_bond_types) const { for (int i = RADIUS - 1; i >= 0; i--) { if (!per_rad[i].testSubstructure(target.per_rad[i], use_bond_types)) return false; } return true; } void MoleculeAtomNeighbourhoodCounters::_calculateLevel0 ( BaseMolecule &mol, bool is_query) { for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { const Vertex &v = mol.getVertex(i); Counters &cnt = _per_atom_counters[i]; CountersPerRadius &cnt0 = cnt.per_rad[0]; if (!_use_atom[i]) continue; int bonds_count = 0; for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int nei_e = v.neiEdge(nei); int nei_vertex = v.neiVertex(nei); if (!_use_atom[nei_vertex]) continue; int bond_order = mol.getBondOrder(nei_e); if (bond_order == BOND_SINGLE || bond_order == BOND_DOUBLE || bond_order == BOND_TRIPLE) cnt0.degree_sum += bond_order; else { if (is_query) cnt0.degree_sum++; // Query or aromatic bond else cnt0.degree_sum += 3; // Aromatic bond } bonds_count++; if (mol.getBondTopology(nei_e) == TOPOLOGY_RING) cnt0.in_ring_cnt++; } if (bonds_count >= 3) cnt0.trip_cnt = 1; int label = mol.getAtomNumber(i); if (label != -1) { if (label == ELEM_N) cnt0.heteroN_cnt++; else if (label == ELEM_O) cnt0.heteroO_cnt++; else if (label != ELEM_C) cnt0.hetero_cnt++; else cnt0.C_cnt++; } } } void MoleculeAtomNeighbourhoodCounters::_calculateNextLevel ( BaseMolecule &mol, int r) { // Calculate counters in each vertex neighbourhood with radius from 1 to RADIUS for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { Counters &cnt = _per_atom_counters[i]; CountersPerRadius &cnt_r = cnt.per_rad[r]; if (!_use_atom[i]) continue; const Vertex &v = mol.getVertex(i); int degree = 0; for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int nei_vertex = v.neiVertex(nei); if (!_use_atom[nei_vertex]) continue; Counters &nei_cnt = _per_atom_counters[nei_vertex]; CountersPerRadius &nei_cnt_r = nei_cnt.per_rad[r - 1]; cnt_r.C_cnt += nei_cnt_r.C_cnt; cnt_r.hetero_cnt += nei_cnt_r.hetero_cnt; cnt_r.heteroN_cnt += nei_cnt_r.heteroN_cnt; cnt_r.heteroO_cnt += nei_cnt_r.heteroO_cnt; cnt_r.in_ring_cnt += nei_cnt_r.in_ring_cnt; cnt_r.trip_cnt += nei_cnt_r.trip_cnt; cnt_r.degree_sum += nei_cnt_r.degree_sum; degree++; } if (r == 1) { CountersPerRadius &cnt_r1 = cnt.per_rad[r - 1]; cnt_r.C_cnt += cnt_r1.C_cnt; cnt_r.hetero_cnt += cnt_r1.hetero_cnt; cnt_r.heteroN_cnt += cnt_r1.heteroN_cnt; cnt_r.heteroO_cnt += cnt_r1.heteroO_cnt; cnt_r.trip_cnt += cnt_r1.trip_cnt; cnt_r.degree_sum += cnt_r1.degree_sum; } else { int deg_1 = degree - 1; CountersPerRadius &cnt_r2 = cnt.per_rad[r - 2]; cnt_r.C_cnt -= cnt_r2.C_cnt * deg_1; cnt_r.hetero_cnt -= cnt_r2.hetero_cnt * deg_1; cnt_r.heteroN_cnt -= cnt_r2.heteroN_cnt * deg_1; cnt_r.heteroO_cnt -= cnt_r2.heteroO_cnt * deg_1; cnt_r.trip_cnt -= cnt_r2.trip_cnt * deg_1; cnt_r.degree_sum -= cnt_r2.degree_sum * deg_1; } } } int MoleculeAtomNeighbourhoodCounters::_countersCmp ( int &i1, int &i2, void *abstract_context) { const MoleculeAtomNeighbourhoodCounters::Context *context = (const MoleculeAtomNeighbourhoodCounters::Context *)abstract_context; const MoleculeAtomNeighbourhoodCounters *self = context->cnt; // Check queryatoms bool is_query1 = (context->mol->getAtomNumber(i1) == -1); bool is_query2 = (context->mol->getAtomNumber(i2) == -1); if (is_query1 && !is_query2) return 1; if (!is_query1 && is_query2) return -1; const Counters &c1 = self->_per_atom_counters[i1]; const Counters &c2 = self->_per_atom_counters[i2]; const CountersPerRadius &c1r0 = c1.per_rad[0]; const CountersPerRadius &c2r0 = c2.per_rad[0]; int is_hetero1 = c1r0.hetero_cnt + c1r0.heteroN_cnt + c1r0.heteroO_cnt; int is_hetero2 = c2r0.hetero_cnt + c2r0.heteroN_cnt + c2r0.heteroO_cnt; // Heteroatoms if (is_hetero1 != is_hetero2) return is_hetero2 - is_hetero1; // Check loop if (c1r0.in_ring_cnt != c2r0.in_ring_cnt) return c2r0.in_ring_cnt - c1r0.in_ring_cnt; // Rare heteroatoms if (c1r0.hetero_cnt != c2r0.hetero_cnt) return c2r0.hetero_cnt - c1r0.hetero_cnt; for (int r = 0; r < Counters::RADIUS; r++) { const CountersPerRadius &c1r = c1.per_rad[r]; const CountersPerRadius &c2r = c2.per_rad[r]; int hetero1_sum = c1r.hetero_cnt + c1r.heteroN_cnt + c1r.heteroO_cnt; int hetero2_sum = c2r.hetero_cnt + c2r.heteroN_cnt + c2r.heteroO_cnt; if (hetero2_sum != hetero1_sum) return hetero2_sum - hetero1_sum; } int degree_sum1 = 0; int degree_sum2 = 0; for (int r = 0; r < Counters::RADIUS; r++) { degree_sum1 += c1.per_rad[r].degree_sum; degree_sum2 += c2.per_rad[r].degree_sum; } return degree_sum2 - degree_sum1; } void MoleculeAtomNeighbourhoodCounters::makeTranspositionForSubstructure (BaseMolecule &mol, Array<int> &output) const { output.clear(); if (mol.vertexCount() == 0) return; QS_DEF(Array<int>, sorted_indices); sorted_indices.clear(); for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) sorted_indices.push(i); Context context; context.cnt = this; context.mol = &mol; sorted_indices.qsort(_countersCmp, &context); QS_DEF(Array<int>, vertices_state); enum { FREE = 0, FRONT = 1, PROCESSED = 2 }; vertices_state.resize(mol.vertexEnd()); for (int i = 0; i < vertices_state.size(); i++) vertices_state[i] = FREE; // Choose first vertex int first_vertex = sorted_indices[0]; vertices_state[first_vertex] = FRONT; while (true) { // Choose next vertex in front int front_vertex = -1; for (int i = 0; i < sorted_indices.size(); i++) { int idx = sorted_indices[i]; if (vertices_state[idx] == FRONT) { front_vertex = idx; break; } } if (front_vertex == -1) { // If query isn't 1-connected than add other vertex to front int free_vertex = -1; for (int i = 0; i < sorted_indices.size(); i++) { int idx = sorted_indices[i]; if (vertices_state[idx] == FREE) { free_vertex = idx; break; } } if (free_vertex == -1) break; vertices_state[free_vertex] = FRONT; front_vertex = free_vertex; } output.push(front_vertex); vertices_state[front_vertex] = PROCESSED; // Add neiboughbours vertices to front const Vertex &v = mol.getVertex(front_vertex); for (int nei = v.neiBegin(); nei != v.neiEnd(); nei = v.neiNext(nei)) { int nei_vertex = v.neiVertex(nei); if (vertices_state[nei_vertex] != PROCESSED) vertices_state[nei_vertex] = FRONT; } } if (output.size() != mol.vertexCount()) throw Exception("Internal error in makeTranspositionForSubstructure"); } ������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_pi_systems_matcher.cpp������������������������������������0000664�0000000�0000000�00000046615�12710376503�0025104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_pi_systems_matcher.h" #include "graph/filter.h" #include "graph/spanning_tree.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculePiSystemsMatcher, "Pi-system matcher"); CP_DEF(MoleculePiSystemsMatcher); MoleculePiSystemsMatcher::MoleculePiSystemsMatcher (Molecule &target) : _target(target), CP_INIT, TL_CP_GET(_atom_pi_system_idx), TL_CP_GET(_pi_systems), TL_CP_GET(_connectivity) { _calcConnectivity(_target, _connectivity); _atom_pi_system_idx.clear_resize(target.vertexEnd()); int n_pi_systems = _initMarks(); _pi_systems.clear(); _pi_systems.resize(n_pi_systems); } void MoleculePiSystemsMatcher::_calcConnectivity (Molecule &mol, Array<int> &conn) { conn.clear_resize(mol.vertexEnd()); conn.zerofill(); for (int e = mol.edgeBegin(); e != mol.edgeEnd(); e = mol.edgeNext(e)) { int bond_order = mol.getBondOrder(e); const Edge &edge = mol.getEdge(e); conn[edge.beg] += bond_order; conn[edge.end] += bond_order; } for (int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) if (!mol.isPseudoAtom(v) && !mol.isRSite(v) && !mol.isTemplateAtom(v)) conn[v] += mol.getImplicitH(v); } int MoleculePiSystemsMatcher::_initMarks (void) { _markAtomsFirst(); // Decompose molecule into pi-systems Filter filter(_atom_pi_system_idx.ptr(), Filter::NEQ, _NOT_IN_PI_SYSTEM); // Decompose 'pi_systems' into connected components _decomposer.create(_target); int n_comp = _decomposer->decompose(&filter); // Copy pi-system indices _copyPiSystemsIdFromDecomposer(); QS_DEF(Array<bool>, pi_system_used); pi_system_used.clear_resize(n_comp); for (int i = 0; i < n_comp; i++) pi_system_used[i] = false; _markUnstablePiSystems(pi_system_used); bool use_any_pi_system = false; for (int i = 0; i < n_comp; i++) { use_any_pi_system |= pi_system_used[i]; if (use_any_pi_system) break; } _markVerticesInUnusedPiSystems(pi_system_used); if (!use_any_pi_system) return 0; _markVerticesInSingleAtomPiSystem(n_comp); _markVerticesInPiSystemsWithCycles(); // Decompose again because cycles could split pi-system n_comp = _decomposer->decompose(&filter); _copyPiSystemsIdFromDecomposer(); _markVerticesInSingleAtomPiSystem(n_comp); return n_comp; } void MoleculePiSystemsMatcher::_calculatePiSystemsSizes (int n_pi_systems, Array<int> &sizes) { sizes.clear_resize(n_pi_systems); sizes.zerofill(); // Collect sizes for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { int p = _atom_pi_system_idx[v]; if (p != _NOT_IN_PI_SYSTEM) sizes[p]++; } } void MoleculePiSystemsMatcher::_markVerticesInSingleAtomPiSystem (int n_pi_systems) { QS_DEF(Array<int>, pi_system_size); _calculatePiSystemsSizes(n_pi_systems, pi_system_size); // Exclude single atoms for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { int p = _atom_pi_system_idx[v]; if (p != _NOT_IN_PI_SYSTEM && pi_system_size[p] == 1) _atom_pi_system_idx[v] = _NOT_IN_PI_SYSTEM; } } void MoleculePiSystemsMatcher::_copyPiSystemsIdFromDecomposer () { const Array<int>& pi_system_per_vertex = _decomposer->getDecomposition(); for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { int pi_system = pi_system_per_vertex[v]; if (pi_system == -1) pi_system = _NOT_IN_PI_SYSTEM; _atom_pi_system_idx[v] = pi_system; } } void MoleculePiSystemsMatcher::_markUnstablePiSystems (Array<bool> &pi_system_used) { // Mark pi-systems that contain atoms with partail octet or charge for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { int pi_system = _atom_pi_system_idx[v]; if (pi_system < 0) continue; if (_connectivity[v] == -1) continue; // This means that atom is in aromatic ring if (_target.getAtomCharge(v) != 0 || _target.getAtomRadical(v) != 0) pi_system_used[pi_system] = true; int lonepairs; int vac = _target.getVacantPiOrbitals(v, _connectivity[v], &lonepairs); if (vac != 0) pi_system_used[pi_system] = true; } } void MoleculePiSystemsMatcher::_markVerticesInUnusedPiSystems ( Array<bool> &pi_system_used) { for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { int pi_system = _atom_pi_system_idx[v]; if (pi_system == _NOT_IN_PI_SYSTEM) continue; if (!pi_system_used[pi_system]) _atom_pi_system_idx[v] = _NOT_IN_PI_SYSTEM; } } void MoleculePiSystemsMatcher::_markVerticesInPiSystemsWithCycles () { // Developed algorithm doesn't operate with pi-systems with cycles Filter filter(_atom_pi_system_idx.ptr(), Filter::NEQ, _NOT_IN_PI_SYSTEM); SpanningTree sp_tree(_target, &filter); QS_DEF(Array<int>, edge_in_cycle); edge_in_cycle.clear_resize(_target.edgeEnd()); edge_in_cycle.zerofill(); sp_tree.markAllEdgesInCycles(edge_in_cycle.ptr(), 1); for (int e = _target.edgeBegin(); e != _target.edgeEnd(); e = _target.edgeNext(e)) { if (edge_in_cycle[e]) { const Edge &edge = _target.getEdge(e); _atom_pi_system_idx[edge.beg] = _NOT_IN_PI_SYSTEM; _atom_pi_system_idx[edge.end] = _NOT_IN_PI_SYSTEM; } } } void MoleculePiSystemsMatcher::_markAtomsFirst () { for (int i = 0; i < _atom_pi_system_idx.size(); i++) _atom_pi_system_idx[i] = _UNKNOWN; // Aromatic bonds matches by aromaticity matcher for (int e = _target.edgeBegin(); e != _target.edgeEnd(); e = _target.edgeNext(e)) { if (_target.getBondOrder(e) == BOND_AROMATIC) { const Edge &edge = _target.getEdge(e); _atom_pi_system_idx[edge.beg] = _IN_AROMATIC; _atom_pi_system_idx[edge.end] = _IN_AROMATIC; _connectivity[edge.beg] = -1; _connectivity[edge.end] = -1; } } // Mark atoms that can be in pi system for (int v = _target.vertexBegin(); v != _target.vertexEnd(); v = _target.vertexNext(v)) { if (_atom_pi_system_idx[v] == _NOT_IN_PI_SYSTEM) continue; // Atoms in aromatic rings will be exclused later // because they are in rings if (_atom_pi_system_idx[v] == _IN_AROMATIC) continue; if (!_canAtomBeInPiSystem(v)) _atom_pi_system_idx[v] = _NOT_IN_PI_SYSTEM; } } bool MoleculePiSystemsMatcher::_canAtomBeInPiSystem (int v) { if (_target.isPseudoAtom(v) || _target.isRSite(v) || _target.isTemplateAtom(v)) return false; int label = _target.getAtomNumber(v); if (!Element::canBeAromatic(label)) return false; if (label == ELEM_Pb || label == ELEM_Tl) return false; // Skip this elements because of lonepairs behavior // Check if d-orbital is used (not supported now) int electrons = Element::electrons(label, _target.getAtomCharge(v)); int conn = _connectivity[v]; int radical = _target.getAtomRadical(v); int used = electrons + conn + 2 * Element::radicalOrbitals(radical); if (used > 8 || conn > 4) return false; int skeleton_conn = _target.getVertex(v).degree() + _target.getImplicitH(v); if (skeleton_conn == conn && conn == 4) return false; // Check if atom is correct: charge corresponds to connectivity // For examples there are molecules like Cl-Ga(v2)-Cl, that are incorrect // but stored in database int total_electrons = electrons + conn + Element::radicalElectrons(radical); if (total_electrons % 2 != 0) return false; return true; } bool MoleculePiSystemsMatcher::isAtomInPiSystem (int atom) { return _atom_pi_system_idx[atom] != _NOT_IN_PI_SYSTEM; } bool MoleculePiSystemsMatcher::isBondInPiSystem (int bond) { const Edge &edge = _target.getEdge(bond); int p1 = _atom_pi_system_idx[edge.beg]; int p2 = _atom_pi_system_idx[edge.end]; if (p1 != p2) return false; return p1 != _NOT_IN_PI_SYSTEM; } void MoleculePiSystemsMatcher::_extractPiSystem (int pi_system_index) { _Pi_System &pi_system = _pi_systems[pi_system_index]; pi_system.initialized = true; Molecule &ps = pi_system.pi_system; Filter filt(_decomposer->getDecomposition().ptr(), Filter::EQ, pi_system_index); ps.makeSubmolecule(_target, filt, &pi_system.mapping, &pi_system.inv_mapping); // Replace bonds outside pi-system to implicit hydrogens QS_DEF(Array<int>, conn); _calcConnectivity(ps, conn); for(int v = ps.vertexBegin(); v != ps.vertexEnd(); v = ps.vertexNext(v)) { int original_v = pi_system.mapping[v]; int delta = _connectivity[original_v] - conn[v]; if (delta < 0) throw Error("Internal error: delta must be >= 0"); ps.setImplicitH(v, ps.getImplicitH(v) + delta); } pi_system.localizations.clear(); pi_system.localizer.create(pi_system.pi_system); _findPiSystemLocalization(pi_system_index); } void MoleculePiSystemsMatcher::_findPiSystemLocalization (int pi_system_index) { // This function should be implemented outside this class // for finding pi-systems localizations during databse index // creation. _Pi_System &pi_system = _pi_systems[pi_system_index]; // Calculate number of electrons and electrons for zero-charge int electrons = 0, zc_electrons = 0, octet_electron_pairs = 0; Molecule &mol = pi_system.pi_system; for(int v = mol.vertexBegin(); v != mol.vertexEnd(); v = mol.vertexNext(v)) { int degree = mol.getVertex(v).degree(); int min_connectivity = mol.getImplicitH(v) + degree; int label = mol.getAtomNumber(v); electrons += Element::electrons(label, mol.getAtomCharge(v)) - min_connectivity; zc_electrons += __max(Element::electrons(label, 0) - min_connectivity, 0); octet_electron_pairs += 4 - min_connectivity - Element::radicalOrbitals(mol.getAtomRadical(v)); } // Find localizations if (electrons % 2 != 0) throw Error("Electrons number must be even"); int electron_pairs = electrons / 2; int zc_lonepairs = pi_system.localizer->getZeroChargeLonepairs(); int double_bonds = electron_pairs; if (double_bonds > octet_electron_pairs / 2) double_bonds = octet_electron_pairs / 2; bool was_found = false; int other_lp = 0, best_other_lp = -1; bool double_bonds_checks = false; int octet_charges_count = -1; while (double_bonds >= 0) { int zero_lp = electron_pairs - double_bonds - other_lp; if (zero_lp > zc_lonepairs) { other_lp = zero_lp - zc_lonepairs; zero_lp = zc_lonepairs; } if (zero_lp < 0) { other_lp = 0; double_bonds--; double_bonds_checks = false; continue; } if (best_other_lp != -1 && best_other_lp < other_lp) { // Such localization has more atoms with // charges then in previous localization break; } pi_system.localizer->setParameters(double_bonds, zero_lp, other_lp); bool found; if (!double_bonds_checks) { found = pi_system.localizer->localize(true); if (!found) { // Such number of double bonds can't be other_lp = 0; double_bonds--; continue; } double_bonds_checks = true; } found = pi_system.localizer->localize(); if (found) { bool octet = pi_system.localizer->isAllAtomsHaveOctet(); int charges_count = pi_system.localizer->getLocalizationChargesCount(); if (octet) octet_charges_count = charges_count; else { if (octet_charges_count != -1 && octet_charges_count <= charges_count) break; } best_other_lp = other_lp; was_found = true; } else { other_lp++; continue; } // Add localization _Pi_System::Localizations &loc = pi_system.localizations.push(); loc.double_bonds = double_bonds; loc.primary_lp = zero_lp; loc.seconary_lp = other_lp; if (zero_lp == zc_lonepairs) break; other_lp = 0; double_bonds--; double_bonds_checks = false; } } bool MoleculePiSystemsMatcher::checkEmbedding (QueryMolecule &query, const int *mapping) { for (int i = 0; i < _pi_systems.size(); i++) _pi_systems[i].pi_system_mapped = false; _markMappedPiSystems(query, mapping); bool ret; // Fix bonds ret = _fixBonds(query, mapping); if (!ret) return false; // Fix charges ret = _fixAtoms(query, mapping); if (!ret) return false; return _findMatching(); } bool MoleculePiSystemsMatcher::_findMatching () { for (int i = 0; i < _pi_systems.size(); i++) { if (!_pi_systems[i].initialized) continue; _Pi_System &pi_system = _pi_systems[i]; if (!pi_system.pi_system_mapped) continue; if (!_findMatchingForPiSystem(i)) return false; } return true; } bool MoleculePiSystemsMatcher::_findMatchingForPiSystem (int pi_system_index) { _Pi_System &pi_system = _pi_systems[pi_system_index]; for (int i = 0; i < pi_system.localizations.size(); i++) { _Pi_System::Localizations &loc = pi_system.localizations[i]; pi_system.localizer->setParameters(loc.double_bonds, loc.primary_lp, loc.seconary_lp); if (pi_system.localizer->localize()) return true; } return false; } void MoleculePiSystemsMatcher::_markMappedPiSystems (QueryMolecule &query, const int *mapping) { for (int qv = query.vertexBegin(); qv != query.vertexEnd(); qv = query.vertexNext(qv)) { int v = mapping[qv]; if (v < 0) continue; // Such vertex must be ignored int pi_system_idx = _atom_pi_system_idx[v]; if (pi_system_idx == _NOT_IN_PI_SYSTEM) continue; if (!_pi_systems[pi_system_idx].initialized) _extractPiSystem(pi_system_idx); _Pi_System &pi_system = _pi_systems[pi_system_idx]; if (!pi_system.pi_system_mapped) { pi_system.pi_system_mapped = true; pi_system.localizer->unfixAll(); } } } bool MoleculePiSystemsMatcher::_fixAtoms (QueryMolecule &query, const int *mapping) { // Fix charges for (int qv = query.vertexBegin(); qv != query.vertexEnd(); qv = query.vertexNext(qv)) { int v = mapping[qv]; if (v < 0) continue; // Such vertex must be ignored int pi_system_idx = _atom_pi_system_idx[v]; if (pi_system_idx == _NOT_IN_PI_SYSTEM) continue; _Pi_System &pi_system = _pi_systems[pi_system_idx]; QueryMolecule::Atom &qatom = query.getAtom(qv); int pv = pi_system.inv_mapping[v]; int charge = query.getAtomCharge(qv); if (charge != CHARGE_UNKNOWN) { bool ret = pi_system.localizer->fixAtomCharge(pv, charge); if (!ret) return false; } else if (qatom.hasConstraint(QueryMolecule::ATOM_CHARGE)) throw Error("Unsupported atom charge specified"); int valence = query.getExplicitValence(qv); if (valence != -1) { bool ret = pi_system.localizer->fixAtomConnectivity(pv, valence); if (!ret) return false; } else if (qatom.hasConstraint(QueryMolecule::ATOM_VALENCE)) throw Error("Unsupported atom charge specified"); } return true; } bool MoleculePiSystemsMatcher::_fixBonds (QueryMolecule &query, const int *mapping) { for (int e = query.edgeBegin(); e != query.edgeEnd(); e = query.edgeNext(e)) { const Edge &query_edge = query.getEdge(e); if (mapping[query_edge.beg] < 0 || mapping[query_edge.end] < 0) continue; // Edges connected with ignored vertices int target_edge = Graph::findMappedEdge(query, _target, e, mapping); const Edge &edge = _target.getEdge(target_edge); int p1_idx = _atom_pi_system_idx[edge.beg]; int p2_idx = _atom_pi_system_idx[edge.end]; if (p1_idx == _NOT_IN_PI_SYSTEM || p2_idx == _NOT_IN_PI_SYSTEM || p1_idx != p2_idx) continue; if (!_pi_systems[p1_idx].initialized) throw Error("pi-system must be initialized here"); _Pi_System &pi_system = _pi_systems[p1_idx]; int pi_sys_edge = Graph::findMappedEdge(_target, pi_system.pi_system, target_edge, pi_system.inv_mapping.ptr()); // Get target topology int topology = _target.getBondTopology(target_edge); QueryMolecule::Bond &qbond = query.getBond(e); bool can_be_single = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_SINGLE, QueryMolecule::BOND_TOPOLOGY, topology); bool can_be_double = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_DOUBLE, QueryMolecule::BOND_TOPOLOGY, topology); bool can_be_triple = qbond.possibleValuePair( QueryMolecule::BOND_ORDER, BOND_TRIPLE, QueryMolecule::BOND_TOPOLOGY, topology); if (!can_be_single && !can_be_double && !can_be_triple) return false; if (can_be_single && can_be_double && can_be_triple) continue; bool ret = false; // initializing to avoid compiler warning if (can_be_single && can_be_double) // Here can_be_triple = false because of previous check ret = pi_system.localizer->fixBondSingleDouble(pi_sys_edge); else { if (can_be_triple) { if (can_be_single) throw Error("Unsupported bond order specified (can be single or triple)"); else if (can_be_double) throw Error("Unsupported bond order specified (can be double or triple)"); ret = pi_system.localizer->fixBond(pi_sys_edge, BOND_TRIPLE); } if (can_be_single) ret = pi_system.localizer->fixBond(pi_sys_edge, BOND_SINGLE); if (can_be_double) ret = pi_system.localizer->fixBond(pi_sys_edge, BOND_DOUBLE); } if (!ret) return false; } return true; } void MoleculePiSystemsMatcher::copyLocalization (Molecule &target) { for (int i = 0; i < _pi_systems.size(); i++) { if (!_pi_systems[i].initialized) continue; _Pi_System &system = _pi_systems[i]; system.localizer->copyBondsAndCharges(target, system.mapping); } } void MoleculePiSystemsMatcher::_Pi_System::clear () { initialized = false; localizer.free(); pi_system.clear(); inv_mapping.clear(); mapping.clear(); localizations.clear(); } �������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_rgroups.cpp�����������������������������������������������0000664�0000000�0000000�00000004364�12710376503�0022676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_rgroups.h" #include "base_cpp/auto_ptr.h" #include "molecule/query_molecule.h" using namespace indigo; RGroup::RGroup () : if_then(0), rest_h(0) { } RGroup::~RGroup () { } void RGroup::clear() { if_then = 0; rest_h = 0; occurrence.clear(); fragments.clear(); } void RGroup::copy (RGroup &other) { if_then = other.if_then; rest_h = other.rest_h; occurrence.copy(other.occurrence); fragments.clear(); PtrPool<BaseMolecule> &frags = other.fragments; for (int i = frags.begin(); i != frags.end(); i = frags.next(i)) { AutoPtr<BaseMolecule> new_fragment(frags[i]->neu()); new_fragment->clone(*frags[i], 0, 0); fragments.add(new_fragment.release()); } } bool RGroup::occurrenceSatisfied (int value) { for (int i = 0; i < occurrence.size(); i++) if (value >= (occurrence[i] >> 16) && value <= (occurrence[i] & 0xFFFF)) return true; return false; } IMPL_ERROR(MoleculeRGroups, "molecule rgroups"); MoleculeRGroups::MoleculeRGroups () { } MoleculeRGroups::~MoleculeRGroups () { } void MoleculeRGroups::copyRGroupsFromMolecule (MoleculeRGroups &other) { int n_rgroups = other.getRGroupCount(); for (int i = 1; i <= n_rgroups; i++) { RGroup &rgroup = other.getRGroup(i); if (rgroup.fragments.size() > 0) getRGroup(i).copy(rgroup); } } void MoleculeRGroups::clear () { _rgroups.clear(); } RGroup & MoleculeRGroups::getRGroup (int idx) { if (_rgroups.size() < idx) _rgroups.resize(idx); return _rgroups[idx - 1]; } int MoleculeRGroups::getRGroupCount () const { return _rgroups.size(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_rgroups_composition.cpp�����������������������������������0000664�0000000�0000000�00000013755�12710376503�0025325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "molecule/base_molecule.h" #include "molecule/molecule_rgroups_composition.h" using namespace indigo; IMPL_ERROR(MoleculeRGroupsComposition, "molecule rgroups composition"); MoleculeRGroupsComposition::MoleculeRGroupsComposition(BaseMolecule &mol) : _mol(mol), _rgroups(_mol.rgroups), _rsites_count(_mol.countRSites()), _rgroups_count(_rgroups.getRGroupCount()) { _limits.resize(_rsites_count); _rgroup2size.resize(_rgroups_count+1); int rsite = 0; for (auto vertex : _mol.vertices()) { if (!_mol.isRSite(vertex)) { continue; } _rsite2vertex.insert(rsite, vertex); Array<int> rgroups; _mol.getAllowedRGroups(vertex, rgroups); _rsite2rgroup.insert(vertex, rgroups); int total = 0; for (int i = 0; i < rgroups.size(); i++) { int rgroup = rgroups[i]; int size = _rgroups.getRGroup(rgroup).fragments.size(); _rgroup2size[rgroup] = size; total += size; } _limits[rsite] = total-1; rsite++; } } std::unique_ptr<Molecule> MoleculeRGroupsComposition::decorate(const AttachmentIter &at) const { Array<int> fs; at.dump(fs); return decorate(fs); } std::unique_ptr<Molecule> MoleculeRGroupsComposition::decorate(const Array<int> &at) const { std::unique_ptr<Molecule> result(new Molecule()); decorate(at, *result.get()); return result; } void MoleculeRGroupsComposition::decorate(const AttachmentIter &at, Molecule &mol) const { Array<int> fs; at.dump(fs); decorate(fs, mol); } void MoleculeRGroupsComposition::decorate(const Array<int> &fs, Molecule &mol) const { mol.clone(_mol, nullptr, nullptr); for (int i = 0; i < fs.size(); i++) { BaseMolecule &fragment = _fragment(i, fs[i]); int rsite = _rsite2vertex.at(i); int apcount = fragment.attachmentPointCount(); int apoint = fragment.getAttachmentPoint(apcount, 0); Array<int> map; mol.mergeWithMolecule(fragment, &map); int atom = mol.getAtomNumber(map[apoint]); if (mol.mergeAtoms(rsite, map[apoint]) == rsite) { mol.resetAtom(rsite, atom); } } mol.removeAttachmentPoints(); mol.rgroups.clear(); } using MoleculeIter = MoleculeRGroupsComposition::MoleculeIter; #define EMPTY_RGROUPS std::unique_ptr<MoleculeRGroups>(new MoleculeRGroups()) #define MAKE_RGROUPS(T) std::unique_ptr<T>(new T(*this)) std::unique_ptr<MoleculeRGroups> MoleculeIter::modifyRGroups(const char *options) const { if (!strcmp(options, OPTION(ERASE)) || !strcmp(options, "")) { return EMPTY_RGROUPS; } if (!strcmp(options, OPTION(LEAVE))) { return MAKE_RGROUPS(SourceRGroups); } if (!strcmp(options, OPTION(ORDER))) { return MAKE_RGROUPS(OrderedRGroups); } return EMPTY_RGROUPS; } MoleculeIter::SourceRGroups::SourceRGroups(const MoleculeIter &m) { Array<int> fs; m._at.dump(fs); MultiMap<int, int> rgroup2fragment; RedBlackMap<Fragment, int> fragment2count; for (auto i = 0; i < fs.size(); i++) { auto x = m._parent._fragment_coordinates(i, fs[i]); if (rgroup2fragment.find(x.rgroup, x.fragment)) { int count = fragment2count.at(x); fragment2count.remove(x); fragment2count.insert(x, count + 1); } else { rgroup2fragment.insert(x.rgroup, x.fragment); fragment2count.insert(x, 1); } } const RedBlackSet<int> &rgroups = rgroup2fragment.keys(); for (auto i = rgroups.begin(); i != rgroups.end(); i = rgroups.next(i)) { auto r = rgroups.key(i); RGroup &rgroup = _rgroups.push(); RGroup &source = m._parent._rgroups.getRGroup(r); const RedBlackSet<int> &fs_r = rgroup2fragment[r]; for (auto j = fs_r.begin(); j != fs_r.end(); j = fs_r.next(j)) { auto f = fs_r.key(j); for (auto k = 0; k < fragment2count.at({r,f}); k++) { Molecule *fragment = new Molecule(); fragment->clone(*source.fragments[f], nullptr, nullptr); fragment->removeAttachmentPoints(); rgroup.fragments.add(fragment); } } } } MoleculeIter::OrderedRGroups::OrderedRGroups(const MoleculeIter &m) { Array<int> fs; m._at.dump(fs); for (auto i = 0; i < fs.size(); i++) { RGroup &rgroup = _rgroups.push(); Molecule *fragment = new Molecule(); fragment->clone(m._parent._fragment(i, fs[i]), nullptr, nullptr); fragment->removeAttachmentPoints(); rgroup.fragments.add(fragment); } } using AttachmentIter = MoleculeRGroupsComposition::AttachmentIter; bool AttachmentIter::operator!= (const AttachmentIter &other) const { if (_end && other._end) { return false; } if (_end != other._end) { return true; } for (auto i = 0; i < _fragments.size(); i++) { if (_fragments[i] != other._fragments[i]) { return true; } } return false; } AttachmentIter& AttachmentIter::operator++() { next(); return *this; } //todo: gray codes? every digit has its own limit bool AttachmentIter::next() { for (int i = 0; i < _size; i++) { if (_fragments[i] < _limits->at(i)) { _fragments[i]++; for (int j = 0; j < i; j++) { _fragments[j] = 0; } return true; } } _end = true; return false; } const Array<int>* AttachmentIter::operator*() const { return &_fragments; } void AttachmentIter::dump(Array<int> &other) const { other.copy(_fragments); }�������������������Indigo-indigo-1.2.3/molecule/src/molecule_savers.cpp������������������������������������������������0000664�0000000�0000000�00000003455�12710376503�0022500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_savers.h" #include "molecule/elements.h" #include "molecule/query_molecule.h" #include "molecule/molecule.h" using namespace indigo; int MoleculeSavers::getHCount (BaseMolecule &mol, int index, int atom_number, int atom_charge) { int hydrogens_count = -1; if (!mol.isRSite(index) && !mol.isPseudoAtom(index) && !mol.isTemplateAtom(index)) { if (!mol.isQueryMolecule()) { if (mol.getAtomAromaticity(index) == ATOM_AROMATIC && ((atom_number != ELEM_C && atom_number != ELEM_O) || atom_charge != 0)) hydrogens_count = mol.asMolecule().getImplicitH_NoThrow(index, -1); } else { QueryMolecule::Atom &atom = mol.asQueryMolecule().getAtom(index); if (!atom.sureValue(QueryMolecule::ATOM_TOTAL_H, hydrogens_count)) { // Try to check if there are only one constraint QueryMolecule::Atom *constraint = atom.sureConstraint(QueryMolecule::ATOM_TOTAL_H); if (constraint != NULL) hydrogens_count = constraint->value_min; else hydrogens_count = -1; } } } return hydrogens_count; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_scaffold_detection.cpp������������������������������������0000664�0000000�0000000�00000021450�12710376503�0025007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_scaffold_detection.h" #include "molecule/max_common_submolecule.h" #include "base_cpp/array.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/query_molecule.h" #include "molecule/molecule_substructure_matcher.h" using namespace indigo; IMPL_ERROR(MoleculeScaffoldDetection, "Molecule Scaffold detection"); MoleculeScaffoldDetection::MoleculeScaffoldDetection (ObjArray<Molecule>* mol_set): ScaffoldDetection(0), searchStructures(mol_set), basketStructures(0){ cbEdgeWeight = matchBonds; cbVerticesColor = matchAtoms; } void MoleculeScaffoldDetection::_searchScaffold(QueryMolecule& scaffold, bool approximate) { QS_DEF(ObjArray<QueryMolecule>, temp_set); if(basketStructures == 0) { basketStructures = &temp_set; } MoleculeBasket mol_basket; mol_basket.initBasket(searchStructures, basketStructures, GraphBasket::MAX_MOLECULES_NUMBER); if(approximate) _searchApproximateScaffold(mol_basket); else _searchExactScaffold(mol_basket); int max_index = mol_basket.getMaxGraphIndex(); if(basketStructures->size() == 0) throw Error("There are no scaffolds found"); //clear all stereocenters for(int i = 0; i < basketStructures->size(); ++i) basketStructures->at(i).stereocenters.clear(); //get max scaffold from basket scaffold.clone(basketStructures->at(max_index), 0, 0); } void MoleculeScaffoldDetection::clone(QueryMolecule& mol, Molecule& other) { QS_DEF(Array<int>, v_list); QS_DEF(Array<int>, e_list); v_list.clear(); e_list.clear(); for (int v_idx = other.vertexBegin(); v_idx != other.vertexEnd(); v_idx = other.vertexNext(v_idx)) { v_list.push(v_idx); } for (int e_idx = other.edgeBegin(); e_idx != other.edgeEnd(); e_idx = other.edgeNext(e_idx)) { e_list.push(e_idx); } makeEdgeSubmolecule(mol, other, v_list, e_list); } void MoleculeScaffoldDetection::makeEdgeSubmolecule(QueryMolecule& mol, Molecule& other, Array<int> &v_list, Array<int> &e_list) { QS_DEF(Array<int>, tmp_mapping); Array<int>* v_mapping = 0; mol.clear(); int i; if (v_mapping == 0) v_mapping = &tmp_mapping; v_mapping->clear_resize(other.vertexEnd()); for (i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i)) v_mapping->at(i) = -1; for (i = 0; i < v_list.size(); i++) { int idx = v_list[i]; if (v_mapping->at(idx) != -1) throw Error("makeEdgeSubmolecule(): repeated vertex #%d", idx); v_mapping->at(idx) = mol.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, other.getAtomNumber(idx))); } for (i = 0; i < e_list.size(); i++) { int edge_idx = e_list[i]; const Edge &edge = other.getEdge(edge_idx); int beg = v_mapping->at(edge.beg); int end = v_mapping->at(edge.end); mol.addBond(beg, end, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, other.getBondOrder(edge_idx))); } } bool MoleculeScaffoldDetection::matchBonds (Graph &g1, Graph &g2, int i, int j, void*){ BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; QueryMolecule::Bond* q_bond = 0; int sub_idx, super_idx; BaseMolecule* target = 0; if(mol1.isQueryMolecule()) { q_bond = &mol1.asQueryMolecule().getBond(i); sub_idx = i; super_idx = j; target = &mol2; } else if(mol2.isQueryMolecule()){ q_bond = &mol2.asQueryMolecule().getBond(j); sub_idx = j; super_idx = i; target = &mol1; } else { return MoleculeExactMatcher::matchBonds(mol1, mol2, i, j, MoleculeExactMatcher::CONDITION_ELECTRONS); } return MoleculeSubstructureMatcher::matchQueryBond(q_bond, *target, sub_idx, super_idx, 0, 0xFFFFFFFF); } bool MoleculeScaffoldDetection::matchAtoms (Graph &g1, Graph &g2, const int *, int i, int j, void* userdata){ BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; QueryMolecule::Atom* q_atom = 0; int super_idx; BaseMolecule* target = 0; if(mol1.isQueryMolecule()) { q_atom = &mol1.asQueryMolecule().getAtom(i); super_idx = j; target = &mol2; } else if(mol2.isQueryMolecule()){ q_atom = &mol2.asQueryMolecule().getAtom(j); super_idx = i; target = &mol1; } else { return MoleculeExactMatcher::matchAtoms(mol1, mol2, i, j, 0); } return MoleculeSubstructureMatcher::matchQueryAtom(q_atom, *target, super_idx, 0, 0xFFFFFFFF); } IMPL_ERROR(MoleculeScaffoldDetection::MoleculeBasket, "Mol basket"); MoleculeScaffoldDetection::MoleculeBasket::MoleculeBasket(): cbSortSolutions(0), _searchStructures(0), _basketStructures(0) { } MoleculeScaffoldDetection::MoleculeBasket::~MoleculeBasket() { } void MoleculeScaffoldDetection::MoleculeBasket::initBasket(ObjArray<Molecule>* mol_set, ObjArray<QueryMolecule>* basket_set, int max_number) { if(mol_set == 0) throw Error("Graph set null pointer"); if(basket_set == 0) throw Error("Basket set null pointer"); _searchStructures = mol_set; _basketStructures = basket_set; _sortGraphsInSet(); _basketStructures->clear(); for(int i = 0; i < max_number; i++) _basketStructures->push(); _directIterator.resize(max_number); _reverseIterator.resize(max_number); _reverseIterator.set(); clone(_basketStructures->at(0), _searchStructures->at(_orderArray[0])); _reverseIterator.set(0, false); _directIterator.set(0); } QueryMolecule& MoleculeScaffoldDetection::MoleculeBasket::pickOutNextMolecule() { int empty_index = _reverseIterator.nextSetBit(0); if(empty_index == -1) { _directIterator.resize(_directIterator.size()+NEXT_SOLUTION_SIZE_SUM); _reverseIterator.resize(_directIterator.size()); for(int i = _directIterator.size()-NEXT_SOLUTION_SIZE_SUM; i < _directIterator.size(); i++) _reverseIterator.set(i); empty_index = _basketStructures->size(); for(int i = 0; i < NEXT_SOLUTION_SIZE_SUM; i++) _basketStructures->push(); } _reverseIterator.set(empty_index, false); return _basketStructures->at(empty_index); } void MoleculeScaffoldDetection::MoleculeBasket::addToNextEmptySpot(Graph& graph, Array<int> &v_list, Array<int> &e_list) { Molecule& mol = (Molecule&) graph; QueryMolecule & b_mol = pickOutNextMolecule(); makeEdgeSubmolecule(b_mol, mol, v_list, e_list); } int MoleculeScaffoldDetection::MoleculeBasket::getMaxGraphIndex() { for(int x = _reverseIterator.nextSetBit(0); x >= 0; x = _reverseIterator.nextSetBit(x+1)) { QueryMolecule& mol_basket = _basketStructures->at(x); if(mol_basket.vertexCount() > 0) mol_basket.clear(); } if(cbSortSolutions == 0) _basketStructures->qsort(_compareRingsCount, 0); else _basketStructures->qsort(cbSortSolutions, userdata); while(_basketStructures->size() && _basketStructures->top().vertexCount() == 0) _basketStructures->pop(); return 0; } Graph& MoleculeScaffoldDetection::MoleculeBasket::getGraph(int index) const { if(index >= _basketStructures->size()) throw Error("basket size < index"); return (Graph&)_basketStructures->at(index); } void MoleculeScaffoldDetection::MoleculeBasket::_sortGraphsInSet() { int set_size = _searchStructures->size(); if(set_size == 0) throw Error("Graph set size == 0"); _orderArray.clear(); for(int i = 0; i < set_size; i++) { if(_searchStructures->at(i).vertexCount() > 0) { _orderArray.push(i); ++_graphSetSize; } } //sort in order of edgeCount _orderArray.qsort(_compareEdgeCount, _searchStructures); } int MoleculeScaffoldDetection::MoleculeBasket::_compareEdgeCount(int &i1,int &i2,void* context){ ObjArray<Molecule> &graph_set = *(ObjArray<Molecule> *)context; return graph_set.at(i1).edgeCount()-graph_set.at(i2).edgeCount(); } int MoleculeScaffoldDetection::MoleculeBasket::_compareRingsCount(BaseMolecule& g1, BaseMolecule& g2, void* ) { //maximize number of the rings/ v-e+r=2 there v- number of vertices e - number of edges r - number of rings int result = (g2.edgeCount() - g2.vertexCount()) - (g1.edgeCount() - g1.vertexCount()); if(result == 0 || g1.edgeCount() == 0 || g2.edgeCount() == 0) result = g2.edgeCount() - g1.edgeCount(); return result; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_sgroups.cpp�����������������������������������������������0000664�0000000�0000000�00000042243�12710376503�0022675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "base_cpp/tlscont.h" #include "molecule/molecule_sgroups.h" using namespace indigo; static SGroup::SgType mappingForSgTypes[] = { { SGroup::SG_TYPE_GEN, "GEN" }, { SGroup::SG_TYPE_DAT, "DAT" }, { SGroup::SG_TYPE_SUP, "SUP" }, { SGroup::SG_TYPE_SRU, "SRU" }, { SGroup::SG_TYPE_MUL, "MUL" }, { SGroup::SG_TYPE_MON, "MON" }, { SGroup::SG_TYPE_MER, "MER" }, { SGroup::SG_TYPE_COP, "COP" }, { SGroup::SG_TYPE_CRO, "CRO" }, { SGroup::SG_TYPE_MOD, "MOD" }, { SGroup::SG_TYPE_GRA, "GRA" }, { SGroup::SG_TYPE_COM, "COM" }, { SGroup::SG_TYPE_MIX, "MIX" }, { SGroup::SG_TYPE_FOR, "FOR" }, { SGroup::SG_TYPE_ANY, "ANY" }, }; const char * SGroup::typeToString(int sg_type) { for (int i = 0; i < NELEM(mappingForSgTypes); i++) { if (sg_type == mappingForSgTypes[i].int_type) return mappingForSgTypes[i].str_type; } return NULL; } int SGroup::getType(const char * sg_type) { for (int i = 0; i < NELEM(mappingForSgTypes); i++) { if (strcasecmp(sg_type, mappingForSgTypes[i].str_type) == 0) { return mappingForSgTypes[i].int_type; } } return -1; } SGroup::SGroup () { sgroup_type = SGroup::SG_TYPE_GEN; sgroup_subtype = 0; brk_style = 0; original_group = 0; parent_group = 0; } SGroup::~SGroup () { } DataSGroup::DataSGroup () { sgroup_type = SGroup::SG_TYPE_DAT; detached = false; relative = false; display_units = false; dasp_pos = 1; num_chars = 0; tag = ' '; } DataSGroup::~DataSGroup () { } Superatom::Superatom () { sgroup_type = SGroup::SG_TYPE_SUP; contracted = -1; } Superatom::~Superatom () { } RepeatingUnit::RepeatingUnit () { sgroup_type = SGroup::SG_TYPE_SRU; connectivity = 0; } RepeatingUnit::~RepeatingUnit () { } MultipleGroup::MultipleGroup () { sgroup_type = SGroup::SG_TYPE_MUL; multiplier = 1; } MultipleGroup::~MultipleGroup () { } IMPL_ERROR(MoleculeSGroups, "molecule sgroups"); MoleculeSGroups::MoleculeSGroups () { } MoleculeSGroups::~MoleculeSGroups () { } void MoleculeSGroups::clear () { _sgroups.clear(); } void MoleculeSGroups::clear (int sg_type) { int count = 0; for (int i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { if (_sgroups.at(i)->sgroup_type == sg_type) remove(i); } } void MoleculeSGroups::remove (int idx) { _sgroups.remove(idx); } int MoleculeSGroups::begin () { return _sgroups.begin(); } int MoleculeSGroups::end () { return _sgroups.end(); } int MoleculeSGroups::next (int i) { return _sgroups.next(i); } int MoleculeSGroups::addSGroup (const char * sg_type) { int sgroup_type = SGroup::getType(sg_type); if (sgroup_type == -1) throw Error("Unknown SGroup type = %s", sg_type); return addSGroup(sgroup_type); } int MoleculeSGroups::addSGroup (int sg_type) { int idx = -1; if (sg_type == SGroup::SG_TYPE_GEN) { idx = _sgroups.add(new SGroup()); } else if (sg_type == SGroup::SG_TYPE_DAT) { idx = _sgroups.add(new DataSGroup()); } else if (sg_type == SGroup::SG_TYPE_SUP) { idx = _sgroups.add(new Superatom()); } else if (sg_type == SGroup::SG_TYPE_SRU) { idx = _sgroups.add(new RepeatingUnit()); } else if (sg_type == SGroup::SG_TYPE_MUL) { idx = _sgroups.add(new MultipleGroup()); } else { idx = _sgroups.add(new SGroup()); if (idx != -1) _sgroups.at(idx)->sgroup_type = sg_type; } return idx; } SGroup & MoleculeSGroups::getSGroup (int idx) { return *_sgroups.at(idx); } SGroup & MoleculeSGroups::getSGroup (int idx, int sg_type) { int count = -1; for (int i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { if (_sgroups.at(i)->sgroup_type == sg_type) { count++; if (count == idx) return *_sgroups.at(i); } } throw Error("Sgroup index %d or type %d wrong", idx, sg_type); } int MoleculeSGroups::getSGroupCount () { return _sgroups.size(); } int MoleculeSGroups::getSGroupCount (int sg_type) { int count = 0; for (int i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { if (_sgroups.at(i)->sgroup_type == sg_type) count++; } return count; } bool MoleculeSGroups::isPolimer () { return getSGroupCount(SGroup::SG_TYPE_SRU) > 0; } void MoleculeSGroups::findSGroups (const char *property, const char *value, Array<int> &sgs) { QS_DEF(Array<int>, s_indices); int s_property; int s_type; int s_int; sgs.clear(); s_indices.clear(); parseCondition (property, value, s_property, s_type, s_int, s_indices); if (s_type == PropertyTypes::PROPERTY_INT) findSGroups(s_property, s_int, sgs); else if (s_type == PropertyTypes::PROPERTY_STRING) findSGroups(s_property, value, sgs); else if (s_type == PropertyTypes::PROPERTY_INT_ARRAY) findSGroups(s_property, s_indices, sgs); } void MoleculeSGroups::parseCondition (const char* property, const char* value, int &s_property, int &s_type, int &s_int, Array<int> &s_indices) { struct Mapping { const char *property; int sg_property; int property_type; }; static Mapping mappingForProperties[] = { { "SG_TYPE", SGroup::SG_TYPE, PROPERTY_INT }, { "SG_CLASS", SGroup::SG_CLASS, PROPERTY_STRING }, { "SG_LABEL", SGroup::SG_LABEL, PROPERTY_STRING }, { "SG_DISPLAY_OPTION", SGroup::SG_DISPLAY_OPTION, PROPERTY_INT }, { "SG_BRACKET_STYLE", SGroup::SG_BRACKET_STYLE, PROPERTY_INT }, { "SG_DATA", SGroup::SG_DATA, PROPERTY_STRING }, { "SG_DATA_NAME", SGroup::SG_DATA_NAME, PROPERTY_STRING }, { "SG_DATA_TYPE", SGroup::SG_DATA_TYPE, PROPERTY_STRING }, { "SG_DATA_DESCRIPTION", SGroup::SG_DATA_DESCRIPTION, PROPERTY_STRING }, { "SG_DATA_DISPLAY", SGroup::SG_DATA_DISPLAY, PROPERTY_STRING }, { "SG_DATA_LOCATION", SGroup::SG_DATA_LOCATION, PROPERTY_STRING }, { "SG_DATA_TAG", SGroup::SG_DATA_TAG, PROPERTY_STRING }, { "SG_QUERY_CODE", SGroup::SG_QUERY_CODE, PROPERTY_STRING }, { "SG_QUERY_OPER", SGroup::SG_QUERY_OPER, PROPERTY_STRING }, { "SG_PARENT", SGroup::SG_PARENT, PROPERTY_INT }, { "SG_CHILD", SGroup::SG_CHILD, PROPERTY_INT }, { "SG_ATOMS", SGroup::SG_ATOMS, PROPERTY_INT_ARRAY }, { "SG_BONDS", SGroup::SG_BONDS, PROPERTY_INT_ARRAY }, }; for (int i = 0; i < NELEM(mappingForProperties); i++) { if (strcasecmp(property, mappingForProperties[i].property) == 0) { int int_value = 0; if (strcasecmp(property, "SG_TYPE") == 0) { for (int j = 0; j < NELEM(mappingForSgTypes); j++) { if (strcasecmp(value, mappingForSgTypes[j].str_type) == 0) { int_value = mappingForSgTypes[j].int_type; } } } else if (value != NULL) { if (mappingForProperties[i].property_type == PROPERTY_INT) { BufferScanner buf_scanner(value); int_value = buf_scanner.readInt(); } else if (mappingForProperties[i].property_type == PROPERTY_BOOL) { if (strcasecmp(value, "true") == 0) int_value = 1; else if (strcasecmp(value, "false") == 0) int_value = 0; else { BufferScanner buf_scanner(value); int_value = buf_scanner.readInt(); } } else if (mappingForProperties[i].property_type == PROPERTY_INT_ARRAY) { BufferScanner buf_scanner(value); while (!buf_scanner.isEOF()) { s_indices.push(buf_scanner.readInt1()); } } } s_property = mappingForProperties[i].sg_property; s_type = mappingForProperties[i].property_type; s_int = int_value; return; } } throw Error("unsupported condition property: %s", property); } void MoleculeSGroups::findSGroups (int property, int value, Array<int> &sgs) { int i; if (property == SGroup::SG_TYPE) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == value) { sgs.push(i); } } } else if (property == SGroup::SG_BRACKET_STYLE) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.brk_style == value) { sgs.push(i); } } } else if (property == SGroup::SG_DISPLAY_OPTION) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sup = (Superatom &)sg; if (sup.contracted == value) { sgs.push(i); } } } } else if (property == SGroup::SG_PARENT) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.parent_group == value) { sgs.push(i); } } } else if (property == SGroup::SG_CHILD) { if (!_sgroups.hasElement(value)) return; SGroup &sg = *_sgroups.at(value); if (sg.parent_group != 0) { int idx = _findSGroupById(sg.parent_group); if (idx != -1) sgs.push(idx); } } else throw Error("Unknown or incomaptible value Sgroup property: %d", property); } void MoleculeSGroups::findSGroups (int property, const char *str, Array<int> &sgs) { int i; if (property == SGroup::SG_CLASS) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sg; BufferScanner sc(sa.sa_class); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_LABEL) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sa = (Superatom &)sg; BufferScanner sc(sa.subscript); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } else if (sg.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &ru = (RepeatingUnit &)sg; BufferScanner sc(ru.subscript); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.data); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_NAME) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.name); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_TYPE) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.type); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_DESCRIPTION) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.description); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_DISPLAY) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; if (((strcasecmp(str, "detached") == 0) && dg.detached) || ((strcasecmp(str, "attached") == 0) && !dg.detached)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_LOCATION) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; if (((strcasecmp(str, "relative") == 0) && dg.relative) || ((strcasecmp(str, "absolute") == 0) && !dg.relative)) { sgs.push(i); } } } } else if (property == SGroup::SG_DATA_TAG) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; if ((strlen(str) == 1) && str[0] == dg.tag) { sgs.push(i); } } } } else if (property == SGroup::SG_QUERY_CODE) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.querycode); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else if (property == SGroup::SG_QUERY_OPER) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dg = (DataSGroup &)sg; BufferScanner sc(dg.queryoper); if (sc.findWordIgnoreCase(str)) { sgs.push(i); } } } } else throw Error("Unknown or incomaptible value Sgroup property: %d", property); } void MoleculeSGroups::findSGroups (int property, Array<int> &indices, Array<int> &sgs) { int i; if (property == SGroup::SG_ATOMS) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (_cmpIndices(sg.atoms, indices)) { sgs.push(i); } } } else if (property == SGroup::SG_BONDS) { for (i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (_cmpIndices(sg.bonds, indices)) { sgs.push(i); } } } else throw Error("Unknown or incomaptible value Sgroup property: %d", property); } void MoleculeSGroups::registerUnfoldedHydrogen (int idx, int new_h_idx) { for (int i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.atoms.find(idx) != -1) { sg.atoms.push(new_h_idx); } } } int MoleculeSGroups::_findSGroupById (int id) { for (int i = _sgroups.begin(); i != _sgroups.end(); i = _sgroups.next(i)) { SGroup &sg = *_sgroups.at(i); if (sg.original_group == id) { return i; } } return -1; } bool MoleculeSGroups::_cmpIndices (Array<int> &t_inds, Array<int> &q_inds) { for (int i = 0; i < q_inds.size(); i++) { if (t_inds.find(q_inds[i]) == -1) return false; } return true; }�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_standardize.cpp�������������������������������������������0000664�0000000�0000000�00000111523�12710376503�0023501�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_standardize.h" #include "molecule/molecule_standardize_options.h" #include "molecule/base_molecule.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_stereocenter_options.h" #include "molecule/molecule_automorphism_search.h" using namespace indigo; IMPL_ERROR(MoleculeStandardizer, "Molecule Standardizer"); CP_DEF(MoleculeStandardizer); MoleculeStandardizer::MoleculeStandardizer(): CP_INIT{ } bool MoleculeStandardizer::standardize (Molecule &mol, const StandardizeOptions &options) { if (options.standardize_stereo) { _standardizeStereo(mol); } if (options.standardize_charges) { _standardizeCharges(mol); } if (options.center_molecule) { _centerMolecule(mol); } if (options.remove_single_atom_fragments) { _removeSingleAtomFragments(mol); } if (options.keep_smallest_fragment) { _keepSmallestFragment(mol); } if (options.keep_largest_fragment) { _keepLargestFragment(mol); } if (options.remove_largest_fragment) { _removeLargestFragment(mol); } if (options.make_non_h_atoms_c_atoms) { _makeNonHAtomsCAtoms(mol); } if (options.make_non_h_atoms_a_atoms) { _makeNonHAtomsAAtoms(mol); } if (options.make_non_c_h_atoms_q_atoms) { _makeNonCHAtomsQAtoms(mol); } if (options.make_all_bonds_single) { _makeAllBondsSingle(mol); } if (options.clear_coordinates) { _clearCoordinates(mol); } if (options.fix_coordinate_dimension) { _fixCoordinateDimension(mol); } if (options.straighten_triple_bonds) { _straightenTripleBonds(mol); } if (options.straighten_allenes) { _straightenAllenes(mol); } if (options.clear_molecule) { _clearMolecule(mol); } if (options.remove_molecule) { _removeMolecule(mol); } if (options.clear_stereo) { _clearStereo(mol); } if (options.clear_enhanced_stereo) { _clearEnhancedStereo(mol); } if (options.clear_unknown_stereo) { _clearUnknownStereo(mol); } if (options.clear_unknown_atom_stereo) { _clearUnknownAtomStereo(mol); } if (options.clear_unknown_cis_trans_bond_stereo) { _clearUnknownCisTransBondStereo(mol); } if (options.clear_cis_trans_bond_stereo) { _clearCisTransBondStereo(mol); } if (options.set_stereo_from_coordinates) { _setStereoFromCoordinates(mol); } if (options.reposition_stereo_bonds) { _repositionStereoBonds(mol); } if (options.reposition_axial_stereo_bonds) { _repositionAxialStereoBonds(mol); } if (options.fix_direction_of_wedge_bonds) { _fixDirectionOfWedgeBonds(mol); } if (options.clear_charges) { _clearCharges(mol); } if (options.clear_pi_bonds) { _clearPiBonds(mol); } if (options.clear_highlight_colors) { _clearHighlightColors(mol); } if (options.clear_query_info) { _clearQueryInfo(mol); } if (options.clear_atom_labels) { _clearAtomLabels(mol); } if (options.clear_bond_labels) { _clearBondLabels(mol); } if (options.neutralize_bonded_zwitterions) { _neutralizeBondedZwitterions(mol); } if (options.clear_unusual_valence) { _clearUnusualValence(mol); } if (options.clear_isotopes) { _clearIsotopes(mol); } if (options.clear_dative_bonds) { _clearDativeBonds(mol); } if (options.clear_hydrogen_bonds) { _clearHydrogenBonds(mol); } if (options.localize_markush_r_atoms_on_rings) { _localizeMarkushRAtomsOnRings(mol); } if (options.create_coordination_bonds) { _createCoordinationBonds(mol); } if (options.create_hydrogen_bonds) { _createHydrogenBonds(mol); } if (options.remove_extra_stereo_bonds) { _removeExtraStereoBonds(mol); } return true; } bool MoleculeStandardizer::standardize (QueryMolecule &query, const StandardizeOptions &options) { if (options.standardize_stereo) { _standardizeStereo(query); } if (options.standardize_charges) { _standardizeCharges(query); } if (options.center_molecule) { _centerMolecule(query); } if (options.remove_single_atom_fragments) { _removeSingleAtomFragments(query); } if (options.keep_smallest_fragment) { _keepSmallestFragment(query); } if (options.keep_largest_fragment) { _keepLargestFragment(query); } if (options.remove_largest_fragment) { _removeLargestFragment(query); } if (options.make_non_h_atoms_c_atoms) { _makeNonHAtomsCAtoms(query); } if (options.make_non_h_atoms_a_atoms) { _makeNonHAtomsAAtoms(query); } if (options.make_non_c_h_atoms_q_atoms) { _makeNonCHAtomsQAtoms(query); } if (options.make_all_bonds_single) { _makeAllBondsSingle(query); } if (options.clear_coordinates) { _clearCoordinates(query); } if (options.fix_coordinate_dimension) { _fixCoordinateDimension(query); } if (options.straighten_triple_bonds) { _straightenTripleBonds(query); } if (options.straighten_allenes) { _straightenAllenes(query); } if (options.clear_molecule) { _clearMolecule(query); } if (options.remove_molecule) { _removeMolecule(query); } if (options.clear_stereo) { _clearStereo(query); } if (options.clear_enhanced_stereo) { _clearEnhancedStereo(query); } if (options.clear_unknown_stereo) { _clearUnknownStereo(query); } if (options.clear_unknown_atom_stereo) { _clearUnknownAtomStereo(query); } if (options.clear_unknown_cis_trans_bond_stereo) { _clearUnknownCisTransBondStereo(query); } if (options.clear_cis_trans_bond_stereo) { _clearCisTransBondStereo(query); } if (options.set_stereo_from_coordinates) { _setStereoFromCoordinates(query); } if (options.reposition_stereo_bonds) { _repositionStereoBonds(query); } if (options.reposition_axial_stereo_bonds) { _repositionAxialStereoBonds(query); } if (options.fix_direction_of_wedge_bonds) { _fixDirectionOfWedgeBonds(query); } if (options.clear_charges) { _clearCharges(query); } if (options.clear_pi_bonds) { _clearPiBonds(query); } if (options.clear_highlight_colors) { _clearHighlightColors(query); } if (options.clear_query_info) { _clearQueryInfo(query); } if (options.clear_atom_labels) { _clearAtomLabels(query); } if (options.clear_bond_labels) { _clearBondLabels(query); } if (options.neutralize_bonded_zwitterions) { _neutralizeBondedZwitterions(query); } if (options.clear_unusual_valence) { _clearUnusualValence(query); } if (options.clear_isotopes) { _clearIsotopes(query); } if (options.clear_dative_bonds) { _clearDativeBonds(query); } if (options.clear_hydrogen_bonds) { _clearHydrogenBonds(query); } if (options.localize_markush_r_atoms_on_rings) { _localizeMarkushRAtomsOnRings(query); } if (options.create_coordination_bonds) { _createCoordinationBonds(query); } if (options.create_hydrogen_bonds) { _createHydrogenBonds(query); } return true; } void MoleculeStandardizer::_standardizeStereo (Molecule &mol) { QS_DEF(Array<int>, ignored); ignored.clear_resize(mol.vertexEnd()); ignored.zerofill(); for (auto i : mol.vertices()) if (mol.convertableToImplicitHydrogen(i)) ignored[i] = 1; MoleculeAutomorphismSearch as; as.detect_invalid_cistrans_bonds = true; as.detect_invalid_stereocenters = true; as.find_canonical_ordering = false; as.ignored_vertices = ignored.ptr(); as.process(mol); for (auto i : mol.vertices()) { if ((mol.stereocenters.isPossibleStereocenter(i)) && !as.invalidStereocenter(i)) { if (mol.stereocenters.exists(i)) continue; else mol.stereocenters.add(i, MoleculeStereocenters::ATOM_ANY, 0, false); } else { if (mol.stereocenters.exists(i)) mol.stereocenters.setType(i, 0, 0); else continue; } } for (auto i : mol.edges()) { if (!as.invalidCisTransBond(i)) { if (mol.cis_trans.getParity(i) > 0) continue; else if (mol.cis_trans.registerBondAndSubstituents(i)) { mol.setBondDirection(i, BOND_EITHER); mol.cis_trans.setParity(i, 0); } } else { if (mol.cis_trans.getParity(i) > 0) mol.cis_trans.setParity(i, 0); else continue; } } } void MoleculeStandardizer::_standardizeStereo (QueryMolecule &mol) { throw Error("This option is not available for QueryMolecule object"); } void MoleculeStandardizer::_standardizeCharges (Molecule &mol) { for (auto i : mol.vertices()) { switch (mol.getAtomNumber(i)) { case ELEM_N: if (_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 4) { mol.setAtomCharge(i, +1); if (_getNumberOfBonds(mol, i, BOND_SINGLE, true, ELEM_O) == 1) { const Vertex &v = mol.getVertex(i); for (int j : v.neighbors()) { if ((mol.getAtomNumber(v.neiVertex(j)) == ELEM_O) && (mol.getVertex(v.neiVertex(j)).degree() == 1)) mol.setAtomCharge(v.neiVertex(j), -1); } } } else if ((_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 1) && (_getNumberOfBonds(mol, i, BOND_AROMATIC, false, 0) == 2)) { mol.setAtomCharge(i, +1); if (_getNumberOfBonds(mol, i, BOND_SINGLE, true, ELEM_O) == 1) { const Vertex &v = mol.getVertex(i); for (int j : v.neighbors()) { if ((mol.getAtomNumber(v.neiVertex(j)) == ELEM_O) && (mol.getVertex(v.neiVertex(j)).degree() == 1)) mol.setAtomCharge(v.neiVertex(j), -1); } } } else if ((_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 1) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, false, 0) == 2) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, true, ELEM_O) == 2)) { mol.setAtomCharge(i, +1); const Vertex &v = mol.getVertex(i); for (int j : v.neighbors()) { if ((mol.getAtomNumber(v.neiVertex(j)) == ELEM_O) && (mol.getVertex(v.neiVertex(j)).degree() == 1)) { if (mol.getBondOrder(v.neiEdge(j) == BOND_DOUBLE)) { mol.setAtomCharge(v.neiVertex(j), -1); mol.setBondOrder(v.neiEdge(j), BOND_SINGLE); break; } } } } else if ((_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 2) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, false, 0) == 1)) { mol.setAtomCharge(i, +1); } break; case ELEM_O: if (mol.getVertex(i).degree() == 3) { mol.setAtomCharge(i, +1); } else if (mol.getVertex(i).degree() == 2) { if (_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 2) break; if ((_getNumberOfBonds(mol, i, BOND_SINGLE, false, ELEM_C) == 1) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, false, 0) == 1)) { mol.setAtomCharge(i, +1); } } break; case ELEM_S: if (mol.getVertex(i).degree() == 3) { mol.setAtomCharge(i, +1); } else if (mol.getVertex(i).degree() == 2) { if (_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 2) break; if ((_getNumberOfBonds(mol, i, BOND_SINGLE, false, ELEM_C) == 1) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, false, 0) == 1)) { mol.setAtomCharge(i, +1); } } break; case ELEM_F: if (mol.getVertex(i).degree() == 0) { mol.setAtomCharge(i, -1); } break; case ELEM_Cl: if (mol.getVertex(i).degree() == 0) { mol.setAtomCharge(i, -1); } break; case ELEM_Br: if (mol.getVertex(i).degree() == 0) { mol.setAtomCharge(i, -1); } break; case ELEM_I: if (mol.getVertex(i).degree() == 0) { mol.setAtomCharge(i, -1); } break; default: break; } } } void MoleculeStandardizer::_standardizeCharges (QueryMolecule &mol) { throw Error("This option is not available for QueryMolecule object"); } void MoleculeStandardizer::_centerMolecule (BaseMolecule &mol) { if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); Vec3f mol_min = Vec3f(INFINITY, INFINITY, INFINITY); Vec3f mol_max = Vec3f(-INFINITY, -INFINITY, -INFINITY); Vec3f mol_center = Vec3f(0, 0, 0); for (auto i : mol.vertices()) { Vec3f &xyz = mol.getAtomXyz(i); if (xyz.x < mol_min.x) mol_min.x = xyz.x; if (xyz.y < mol_min.y) mol_min.y = xyz.y; if (xyz.z < mol_min.z) mol_min.z = xyz.z; if (xyz.x > mol_max.x) mol_max.x = xyz.x; if (xyz.y > mol_max.y) mol_max.y = xyz.y; if (xyz.z > mol_max.z) mol_max.z = xyz.z; } mol_center.x = (mol_min.x + mol_max.x)/2; mol_center.y = (mol_min.y + mol_max.y)/2; mol_center.z = (mol_min.z + mol_max.z)/2; for (auto i : mol.vertices()) { Vec3f &xyz = mol.getAtomXyz(i); xyz.x -= mol_center.x; xyz.y -= mol_center.y; xyz.z -= mol_center.z; mol.setAtomXyz(i, xyz); } } void MoleculeStandardizer::_removeSingleAtomFragments (BaseMolecule &mol) { QS_DEF(Array<int>, single_atoms); single_atoms.clear(); for (auto i : mol.vertices()) { auto atom_number = mol.getAtomNumber(i); if (atom_number != ELEM_H) { if (mol.getVertex(i).degree() == 0) single_atoms.push(i); } } if (single_atoms.size() > 0) mol.removeAtoms(single_atoms); } void MoleculeStandardizer::_keepSmallestFragment (BaseMolecule &mol) { if (mol.vertexCount() <= 1) return; auto ncomp = mol.countComponents(); if (ncomp == 1) return; auto min_size = mol.vertexCount(); auto min_comp = 0; for (auto i = 0; i < ncomp; i++) { if (mol.countComponentVertices(i) < min_size) { min_size = mol.countComponentVertices(i); min_comp = i; } } QS_DEF(Array<int>, remove_atoms); remove_atoms.clear(); for (auto i : mol.vertices()) { if (mol.vertexComponent(i) != min_comp) remove_atoms.push(i); } if (remove_atoms.size() > 0) mol.removeAtoms(remove_atoms); } void MoleculeStandardizer::_keepLargestFragment(BaseMolecule &mol) { if (mol.vertexCount() <= 1) return; auto ncomp = mol.countComponents(); if (ncomp == 1) return; auto max_size = 0; auto max_comp = 0; for (auto i = 0; i < ncomp; i++) { if (mol.countComponentVertices(i) > max_size) { max_size = mol.countComponentVertices(i); max_comp = i; } } QS_DEF(Array<int>, remove_atoms); remove_atoms.clear(); for (auto i : mol.vertices()) { if (mol.vertexComponent(i) != max_comp) remove_atoms.push(i); } if (remove_atoms.size() > 0) mol.removeAtoms(remove_atoms); } void MoleculeStandardizer::_removeLargestFragment(BaseMolecule &mol) { if (mol.vertexCount() <= 1) return; auto ncomp = mol.countComponents(); if (ncomp == 1) return; auto max_size = 0; auto max_comp = 0; for (auto i = 0; i < ncomp; i++) { if (mol.countComponentVertices(i) > max_size) { max_size = mol.countComponentVertices(i); max_comp = i; } } QS_DEF(Array<int>, remove_atoms); remove_atoms.clear(); for (auto i : mol.vertices()) { if (mol.vertexComponent(i) == max_comp) remove_atoms.push(i); } if (remove_atoms.size() > 0) mol.removeAtoms(remove_atoms); } void MoleculeStandardizer::_makeNonHAtomsCAtoms(Molecule &mol) { for (auto i : mol.vertices()) { auto atom_number = mol.getAtomNumber(i); if ((atom_number > ELEM_H) && (atom_number < ELEM_MAX) && (atom_number != ELEM_C)) mol.resetAtom(i, ELEM_C); } } void MoleculeStandardizer::_makeNonHAtomsCAtoms(QueryMolecule &mol) { for (auto i : mol.vertices()) { auto atom_number = mol.getAtomNumber(i); if ((atom_number != ELEM_H) && (atom_number != ELEM_C)) mol.resetAtom(i, new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)); } } void MoleculeStandardizer::_makeNonHAtomsAAtoms(Molecule &mol) { throw Error("This option is available only for QueryMolecule object"); } void MoleculeStandardizer::_makeNonHAtomsAAtoms(QueryMolecule &mol) { for (auto i : mol.vertices()) { auto atom_number = mol.getAtomNumber(i); if (atom_number != ELEM_H) mol.resetAtom(i, QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H))); } } void MoleculeStandardizer::_makeNonCHAtomsQAtoms(Molecule &mol) { throw Error("This option is available only for QueryMolecule object"); } void MoleculeStandardizer::_makeNonCHAtomsQAtoms(QueryMolecule &mol) { for (auto i : mol.vertices()) { auto atom_number = mol.getAtomNumber(i); if ((atom_number != ELEM_H) && (atom_number != ELEM_C)) mol.resetAtom(i, QueryMolecule::Atom::und( QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)))); } } void MoleculeStandardizer::_makeAllBondsSingle(Molecule &mol) { for (auto i : mol.edges()) { if (mol.getBondOrder(i) != BOND_SINGLE) mol.setBondOrder(i, BOND_SINGLE, false); } } void MoleculeStandardizer::_makeAllBondsSingle(QueryMolecule &mol) { for (auto i : mol.edges()) { if (mol.getBondOrder(i) != BOND_SINGLE) mol.resetBond(i, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } } void MoleculeStandardizer::_clearCoordinates(BaseMolecule &mol) { mol.clearXyz(); } void MoleculeStandardizer::_fixCoordinateDimension(BaseMolecule &mol) { throw Error("This option is not used for Indigo"); } void MoleculeStandardizer::_straightenTripleBonds(BaseMolecule &mol) { if (!Molecule::hasCoord(mol) || (mol.vertexCount() < 2)) throw Error("Atoms coordinates are not defined or too few atoms"); for (auto i : mol.vertices()) { if ((mol.getVertex(i).degree() == 2) && (_getNumberOfBonds(mol, i, BOND_TRIPLE, false, 0) == 1)) { if (!isFragmentLinear(mol, i)) _linearizeFragment(mol, i); } } } void MoleculeStandardizer::_straightenAllenes(BaseMolecule &mol) { if (!Molecule::hasCoord(mol) || (mol.vertexCount() < 3)) throw Error("Atoms coordinates are not defined or too few atoms"); for (auto i : mol.vertices()) { if ((mol.getAtomNumber(i) == ELEM_C) && (mol.getVertex(i).degree() == 2) && (_getNumberOfBonds(mol, i, BOND_DOUBLE, true, ELEM_C) == 2)) { if (!isFragmentLinear(mol, i)) _linearizeFragment(mol, i); } } } void MoleculeStandardizer::_clearMolecule(BaseMolecule &mol) { mol.clear(); } void MoleculeStandardizer::_removeMolecule(BaseMolecule &mol) { throw Error("Not implemented yet"); } void MoleculeStandardizer::_clearStereo(BaseMolecule &mol) { for (auto i : mol.vertices()) { if (mol.stereocenters.exists(i)) mol.stereocenters.setType(i, 0, 0); } for (auto i : mol.edges()) { if (mol.getBondDirection(i) > 0) mol.setBondDirection(i, 0); } if (mol.cis_trans.exists()) { for (auto i : mol.edges()) { if (mol.cis_trans.getParity(i) > 0) mol.cis_trans.setParity(i, 0); } } mol.allene_stereo.clear(); } void MoleculeStandardizer::_clearEnhancedStereo(BaseMolecule &mol) { for (auto i : mol.vertices()) { if (mol.stereocenters.exists(i)) { mol.stereocenters.setType(i, 0, 0); } } } void MoleculeStandardizer::_clearUnknownStereo(BaseMolecule &mol) { for (auto i : mol.vertices()) { if (mol.stereocenters.exists(i) && (mol.stereocenters.getType(i) == MoleculeStereocenters::ATOM_ANY)) mol.stereocenters.setType(i, 0, 0); } for (auto i : mol.edges()) { if (mol.getBondDirection(i) == BOND_EITHER) mol.setBondDirection(i, 0); } } void MoleculeStandardizer::_clearUnknownAtomStereo(BaseMolecule &mol) { for (auto i : mol.vertices()) { if (mol.stereocenters.exists(i) && (mol.stereocenters.getType(i) == MoleculeStereocenters::ATOM_ANY)) mol.stereocenters.setType(i, 0, 0); } } void MoleculeStandardizer::_clearUnknownCisTransBondStereo(BaseMolecule &mol) { if (mol.cis_trans.exists()) { for (auto i : mol.edges()) { if ((mol.cis_trans.getParity(i) == 0) && (mol.getBondDirection(i) == BOND_EITHER)) { mol.setBondDirection(i, 0); } } } } void MoleculeStandardizer::_clearCisTransBondStereo(BaseMolecule &mol) { if (mol.cis_trans.exists()) { for (auto i : mol.edges()) { if (mol.cis_trans.getParity(i) > 0) { mol.setBondDirection(i, BOND_EITHER); mol.cis_trans.setParity(i, 0); } } } } void MoleculeStandardizer::_setStereoFromCoordinates(BaseMolecule &mol) { if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); mol.stereocenters.clear(); mol.cis_trans.clear(); mol.allene_stereo.clear(); StereocentersOptions options; QS_DEF(Array<int>, sensible_bond_orientations); sensible_bond_orientations.clear_resize(mol.vertexEnd()); mol.stereocenters.buildFromBonds(options, sensible_bond_orientations.ptr()); mol.allene_stereo.buildFromBonds(options.ignore_errors, sensible_bond_orientations.ptr()); mol.cis_trans.build(0); if (mol.stereocenters.size() == 0) mol.stereocenters.buildFrom3dCoordinates(); } void MoleculeStandardizer::_repositionStereoBonds(BaseMolecule &mol) { if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); mol.stereocenters.markBonds(); } void MoleculeStandardizer::_repositionAxialStereoBonds(BaseMolecule &mol) { if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); mol.allene_stereo.markBonds(); } void MoleculeStandardizer::_fixDirectionOfWedgeBonds(BaseMolecule &mol) { for (auto i : mol.vertices()) { if (!mol.stereocenters.exists(i)) { const Vertex &vertex = mol.getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) if (mol.getBondDirection2(i, vertex.neiVertex(j)) > 0) mol.setBondDirection(vertex.neiEdge(j), 0); } } } void MoleculeStandardizer::_removeExtraStereoBonds(BaseMolecule &mol) { QS_DEF(Array<int>, stereo_neibs); if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); // Clear wrong stereo for (auto i : mol.vertices()) { if ((mol.stereocenters.exists(i)) && !mol.stereocenters.isPossibleStereocenter(i)) mol.stereocenters.remove(i); if (!mol.stereocenters.exists(i)) { const Vertex &vertex = mol.getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) if (mol.getBondDirection2(i, vertex.neiVertex(j)) > 0) mol.setBondDirection(vertex.neiEdge(j), 0); } } // Check several stereo bonds from one stereocenter for (auto i : mol.vertices()) { int count_bonds = 0; stereo_neibs.clear(); if (mol.stereocenters.exists(i)) { const Vertex &vertex = mol.getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { if (mol.getBondDirection2(i, vertex.neiVertex(j)) > 0) { count_bonds++; stereo_neibs.push(vertex.neiVertex(j)); } } } // Remove unnecessary bonds if (count_bonds > 1) { stereo_neibs.qsort(_asc_cmp_cb, &mol); for (int j = 1; j < stereo_neibs.size(); j++) { int rem_edge_dir = mol.findEdgeIndex(i, stereo_neibs[j]); mol.setBondDirection(rem_edge_dir, 0); } } } } int MoleculeStandardizer::_asc_cmp_cb (int &v1, int &v2, void *context) { int res = 0; Molecule &mol = *(Molecule *)context; if (mol.vertexInRing(v1) && !mol.vertexInRing(v2)) return 1; else if (!mol.vertexInRing(v1) && mol.vertexInRing(v2)) return -1; if ( (mol.getAtomNumber(v1) == ELEM_H) && (mol.getAtomNumber(v2) != ELEM_H) ) return 1; else if ( (mol.getAtomNumber(v1) != ELEM_H) && (mol.getAtomNumber(v2) == ELEM_H) ) return -1; if ( (mol.getAtomNumber(v1) == ELEM_C) && (mol.getAtomNumber(v2) != ELEM_C) ) return 1; else if ( (mol.getAtomNumber(v1) != ELEM_C) && (mol.getAtomNumber(v2) == ELEM_C) ) return -1; return res; } void MoleculeStandardizer::_clearCharges(Molecule &mol) { for (auto i : mol.vertices()) { mol.setAtomCharge(i, 0); } } void MoleculeStandardizer::_clearCharges(QueryMolecule &mol) { for (auto i : mol.vertices()) { mol.getAtom(i).removeConstraints(QueryMolecule::ATOM_CHARGE); mol.resetAtom(i, QueryMolecule::Atom::und(mol.releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, 0))); } } void MoleculeStandardizer::_clearPiBonds(BaseMolecule &mol) { throw Error("This option is not used for Indigo"); } void MoleculeStandardizer::_clearHighlightColors(BaseMolecule &mol) { mol.unhighlightAll(); } void MoleculeStandardizer::_clearQueryInfo(BaseMolecule &mol) { throw Error("This option is not used for Indigo"); } void MoleculeStandardizer::_clearAtomLabels(Molecule &mol) { throw Error("This option is available only for QueryMolecule object?"); } void MoleculeStandardizer::_clearAtomLabels(QueryMolecule &mol) { throw Error("This option is available only for QueryMolecule object?"); } void MoleculeStandardizer::_clearBondLabels(Molecule &mol) { throw Error("This option is available only for QueryMolecule object?"); } void MoleculeStandardizer::_clearBondLabels(QueryMolecule &mol) { throw Error("This option is available only for QueryMolecule object?"); } void MoleculeStandardizer::_neutralizeBondedZwitterions(Molecule &mol) { for (auto i : mol.vertices()) { int elem = mol.getAtomNumber(i); if (mol.getAtomCharge(i) == 0) continue; if ((elem == ELEM_C) || (elem == ELEM_N) || (elem == ELEM_P) || (elem == ELEM_S)) { const Vertex &v = mol.getVertex(i); for (int j : v.neighbors()) { if (mol.getAtomCharge(v.neiVertex(j)) != 0) { int c1 = mol.getAtomCharge(i); int c2 = mol.getAtomCharge(v.neiVertex(j)); int bond = mol.getBondOrder(v.neiEdge(j)); if ((c1 > 0) && (c2 < 0) && (bond != BOND_TRIPLE)) { mol.setAtomCharge(i, c1 - 1); mol.setAtomCharge(v.neiVertex(j), c2 + 1); if (bond == BOND_SINGLE) mol.setBondOrder(v.neiEdge(j), BOND_DOUBLE); else if (bond == BOND_DOUBLE) mol.setBondOrder(v.neiEdge(j), BOND_TRIPLE); } if ((c1 < 0) && (c2 > 0) && (bond != BOND_TRIPLE)) { mol.setAtomCharge(i, c1 + 1); mol.setAtomCharge(v.neiVertex(j), c2 - 1); if (bond == BOND_SINGLE) mol.setBondOrder(v.neiEdge(j), BOND_DOUBLE); else if (bond == BOND_DOUBLE) mol.setBondOrder(v.neiEdge(j), BOND_TRIPLE); } } } } } } void MoleculeStandardizer::_neutralizeBondedZwitterions(QueryMolecule &mol) { throw Error("This option is not available for QueryMolecule object"); } void MoleculeStandardizer::_clearUnusualValence(Molecule &mol) { for (auto i : mol.vertices()) { if (mol.getExplicitValence(i) > 0) { mol.setExplicitValence(i, 0); mol.invalidateHCounters(); } } } void MoleculeStandardizer::_clearUnusualValence(QueryMolecule &mol) { for (auto i : mol.vertices()) { mol.resetAtom(i, QueryMolecule::Atom::und(mol.releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_VALENCE, 0))); } } void MoleculeStandardizer::_clearIsotopes(Molecule &mol) { for (auto i : mol.vertices()) { mol.setAtomIsotope(i, 0); } } void MoleculeStandardizer::_clearIsotopes(QueryMolecule &mol) { for (auto i : mol.vertices()) { mol.resetAtom(i, QueryMolecule::Atom::und(mol.releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, 0))); } } void MoleculeStandardizer::_clearDativeBonds(BaseMolecule &mol) { QS_DEF(Array<int>, remove_bonds); remove_bonds.clear(); for (auto i : mol.edges()) { const Edge &edge = mol.getEdge(i); if ((mol.getBondOrder(i) == BOND_ZERO) && (mol.getAtomNumber(edge.beg) != ELEM_H) && (mol.getAtomNumber(edge.end) != ELEM_H)) remove_bonds.push(i); } if (remove_bonds.size() > 0) mol.removeBonds(remove_bonds); } void MoleculeStandardizer::_clearHydrogenBonds(BaseMolecule &mol) { QS_DEF(Array<int>, remove_bonds); remove_bonds.clear(); for (auto i : mol.edges()) { const Edge &edge = mol.getEdge(i); if ((mol.getBondOrder(i) == BOND_ZERO) && ((mol.getAtomNumber(edge.beg) == ELEM_H) || (mol.getAtomNumber(edge.end) == ELEM_H))) remove_bonds.push(i); } if (remove_bonds.size() > 0) mol.removeBonds(remove_bonds); } void MoleculeStandardizer::_localizeMarkushRAtomsOnRings(Molecule &mol) { throw Error("This option is available only for QueryMolecule object?"); } void MoleculeStandardizer::_localizeMarkushRAtomsOnRings(QueryMolecule &mol) { } void MoleculeStandardizer::_createCoordinationBonds(BaseMolecule &mol) { for (auto i : mol.edges()) { const Edge &edge = mol.getEdge(i); if ((mol.getBondOrder(i) == BOND_SINGLE) && (mol.getAtomNumber(edge.beg) != ELEM_H) && (mol.getAtomNumber(edge.end) != ELEM_H)) { int non_metal_atom; int metal_atom; if (_isNonMetalAtom(mol.getAtomNumber(edge.beg)) && _isMetalAtom(mol.getAtomNumber(edge.end))) { non_metal_atom = edge.beg; metal_atom = edge.end; } else if (_isMetalAtom(mol.getAtomNumber(edge.beg)) && _isNonMetalAtom(mol.getAtomNumber(edge.end))) { non_metal_atom = edge.end; metal_atom = edge.beg; } else continue; try { mol.getAtomValence(non_metal_atom); } catch (Exception e) { if (mol.isQueryMolecule()) (mol.asQueryMolecule()).resetBond(i, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_ZERO)); else (mol.asMolecule()).setBondOrder(i, BOND_ZERO, false); } } } } void MoleculeStandardizer::_createHydrogenBonds(BaseMolecule &mol) { QS_DEF(Array<int>, modified_atoms); modified_atoms.clear(); for (auto i : mol.vertices()) { if ((mol.getAtomNumber(i) == ELEM_H) && (_getNumberOfBonds(mol, i, BOND_SINGLE, false, 0) == 2)) { const Vertex &v = mol.getVertex(i); for (auto j : v.neighbors()) { bool already_modified = false; for (int k = 0; k < modified_atoms.size(); k++) { if (modified_atoms[k] == v.neiVertex(j)) { already_modified = true; break; } } if (!already_modified) { modified_atoms.push(v.neiVertex(j)); if (mol.isQueryMolecule()) { if (mol.findEdgeIndex(i,v.neiVertex(j)) > 0) (mol.asQueryMolecule()).resetBond(mol.findEdgeIndex(i,j), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_ZERO)); else (mol.asQueryMolecule()).resetBond(mol.findEdgeIndex(v.neiVertex(j),i), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_ZERO)); } else { if (mol.findEdgeIndex(i,v.neiVertex(j)) > 0) (mol.asMolecule()).setBondOrder(mol.findEdgeIndex(i,v.neiVertex(j)), BOND_ZERO, false); else (mol.asMolecule()).setBondOrder(mol.findEdgeIndex(v.neiVertex(j),i), BOND_ZERO, false); } break; } } } } } int MoleculeStandardizer::_getNumberOfBonds(BaseMolecule &mol, int idx, int bond_type, bool with_element_only, int element) { auto num_bonds = 0; const Vertex &v = mol.getVertex(idx); for (auto i : v.neighbors()) { if (with_element_only) { if ((mol.getAtomNumber(v.neiVertex(i)) == element) && (mol.getBondOrder(v.neiEdge(i)) == bond_type)) num_bonds++; } else { if (mol.getBondOrder(v.neiEdge(i)) == bond_type) num_bonds++; } } return num_bonds; } bool MoleculeStandardizer::isFragmentLinear(BaseMolecule &mol, int idx) { if (!Molecule::hasCoord(mol)) throw Error("Atoms coordinates are not defined"); Vec3f ¢ral_atom = mol.getAtomXyz(idx); const Vertex &v = mol.getVertex(idx); Vec3f nei_coords[2]; int nei_count = 0; for (auto i : v.neighbors()) { nei_coords[nei_count++] = mol.getAtomXyz(v.neiVertex(i)); } Vec3f bond1, bond2; bond1.diff(nei_coords[0], central_atom); bond1.normalize(); bond2.diff(nei_coords[1], central_atom); bond2.normalize(); float angle; Vec3f::angle(bond1, bond2, angle); if (fabs(angle - PI) > EPSILON) return false; return true; } void MoleculeStandardizer::_linearizeFragment(BaseMolecule &mol, int idx) { Vec3f ¢ral_atom = mol.getAtomXyz(idx); const Vertex &v = mol.getVertex(idx); Vec3f nei_coords[2]; int nei_count = 0; for (auto i : v.neighbors()) { nei_coords[nei_count++] = mol.getAtomXyz(v.neiVertex(i)); } central_atom.x = (nei_coords[0].x + nei_coords[1].x)/2; central_atom.y = (nei_coords[0].y + nei_coords[1].y)/2; central_atom.z = (nei_coords[0].z + nei_coords[1].z)/2; mol.setAtomXyz(idx, central_atom); } bool MoleculeStandardizer::_isNonMetalAtom(int atom_number) { if ((atom_number == ELEM_C) || (atom_number == ELEM_N) || (atom_number == ELEM_O) || (atom_number == ELEM_P) || (atom_number == ELEM_S) || (atom_number == ELEM_Se)) return true; return false; } bool MoleculeStandardizer::_isMetalAtom(int atom_number) { if ((atom_number > 0) && (atom_number < ELEM_MAX) && !_isNonMetalAtom(atom_number) && !Element::isHalogen(atom_number) && (atom_number != ELEM_He) && (atom_number != ELEM_Ne) && (atom_number != ELEM_Ar) && (atom_number != ELEM_Kr) && (atom_number != ELEM_Xe) && (atom_number != ELEM_Rn)) return true; return false; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_standardize_options.cpp�����������������������������������0000664�0000000�0000000�00000004366�12710376503�0025262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_standardize_options.h" using namespace indigo; // // StandardizeOptions // StandardizeOptions::StandardizeOptions () { reset(); } void StandardizeOptions::reset () { standardize_stereo = false; standardize_charges = false; center_molecule = false; remove_single_atom_fragments = false; keep_smallest_fragment = false; keep_largest_fragment = false; remove_largest_fragment = false; make_non_h_atoms_c_atoms = false; make_non_h_atoms_a_atoms = false; make_non_c_h_atoms_q_atoms = false; make_all_bonds_single = false; clear_coordinates = false; fix_coordinate_dimension = false; straighten_triple_bonds = false; straighten_allenes = false; clear_molecule = false; remove_molecule = false; clear_stereo = false; clear_enhanced_stereo = false; clear_unknown_stereo = false; clear_unknown_atom_stereo = false; clear_unknown_cis_trans_bond_stereo = false; clear_cis_trans_bond_stereo = false; set_stereo_from_coordinates = false; reposition_stereo_bonds = false; reposition_axial_stereo_bonds = false; fix_direction_of_wedge_bonds = false; clear_charges = false; clear_pi_bonds = false; clear_highlight_colors = false; clear_query_info = false; clear_atom_labels = false; clear_bond_labels = false; neutralize_bonded_zwitterions = false; clear_unusual_valence = false; clear_isotopes = false; clear_dative_bonds = false; clear_hydrogen_bonds = false; localize_markush_r_atoms_on_rings = false; create_coordination_bonds = false; create_hydrogen_bonds = false; remove_extra_stereo_bonds = false; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_stereocenter_options.cpp����������������������������������0000664�0000000�0000000�00000001665�12710376503�0025453�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_stereocenter_options.h" using namespace indigo; // // StereocentersOptions // StereocentersOptions::StereocentersOptions () { reset(); } void StereocentersOptions::reset () { ignore_errors = false; bidirectional_mode = false; detect_haworth_projection = false; } ���������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_stereocenters.cpp�����������������������������������������0000664�0000000�0000000�00000136631�12710376503�0024065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_stereocenter_options.h" #include "molecule/molecule_automorphism_search.h" #include "molecule/base_molecule.h" #include "molecule/haworth_projection_finder.h" #include "graph/filter.h" #include "molecule/molecule.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeStereocenters, "stereocenters"); MoleculeStereocenters::MoleculeStereocenters () { } BaseMolecule & MoleculeStereocenters::_getMolecule () const { char dummy[sizeof(BaseMolecule)]; int offset = (int)((char *)(&((BaseMolecule *)dummy)->stereocenters) - dummy); return *(BaseMolecule *)((char *)this - offset); } void MoleculeStereocenters::clear () { _stereocenters.clear(); } void MoleculeStereocenters::buildFromBonds (const StereocentersOptions &options, int *sensible_bonds_out) { BaseMolecule &mol = _getMolecule(); HaworthProjectionFinder haworth_finder(mol); if (options.detect_haworth_projection) haworth_finder.findAndAddStereocenters(); const Array<bool> &bonds_ignore = haworth_finder.getBondsMask(); const Array<bool> &atoms_ignore = haworth_finder.getAtomsMask(); for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (bonds_ignore[i] && mol.getBondDirection(i)) sensible_bonds_out[i] = 1; } for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (atoms_ignore[i]) continue; // Try to build sterecenters with bidirectional_mode only for either bonds bool found = false; try { found = _buildOneCenter(i, sensible_bonds_out, false, options.bidirectional_mode, bonds_ignore); } catch (Error &) { if (!options.ignore_errors) throw; } // Try to build a stereocenter with bidirectional_mode for all bonds // but ignore any errors that occur because such bidirection mode has low // priority if (options.bidirectional_mode && !found) { try { _buildOneCenter(i, sensible_bonds_out, true, options.bidirectional_mode, bonds_ignore); } catch (Error &) { } } } } void MoleculeStereocenters::buildFrom3dCoordinates ( void ) { BaseMolecule &bmol = _getMolecule(); if (bmol.isQueryMolecule()) return; Molecule &mol = bmol.asMolecule(); if (!BaseMolecule::hasZCoord(mol)) return; _stereocenters.clear(); int i; for (i = bmol.vertexBegin(); i != bmol.vertexEnd(); i = bmol.vertexNext(i)) { Vec3f &v_pos = bmol.getAtomXyz(i); if (!isPossibleStereocenter(i)) continue; int pyramid[4]; try { _restorePyramid(i, pyramid, false); } catch (Exception &) { continue; } Vec3f nei_coords[4]; int nei_cnt = 0; for (int j = 0; j < 4; j++) { if (pyramid[j] != -1) nei_coords[nei_cnt++] = bmol.getAtomXyz(pyramid[j]); } if (nei_cnt != 4) { Vec3f v1, v2, v3; v1.copy(nei_coords[0]); v2.copy(nei_coords[1]); v3.copy(nei_coords[2]); // Check if substituents with center atom are on the same plane int plane_sign_v_pos = _onPlane(v1, v2, v3, v_pos); if (plane_sign_v_pos == 0) continue; v1.sub(v_pos); v2.sub(v_pos); v3.sub(v_pos); v1.normalize(); v2.normalize(); v3.normalize(); nei_coords[3] = Vec3f(0, 0, 0); nei_coords[3].add(v1); nei_coords[3].add(v2); nei_coords[3].add(v3); nei_coords[3].scale(-1); nei_coords[3].normalize(); nei_coords[3].add(v_pos); } int plane_sign = _onPlane(nei_coords[0], nei_coords[1], nei_coords[2], nei_coords[3]); if (plane_sign == 0) continue; if (plane_sign > 0) add(i, ATOM_ABS, 0, true); else add(i, ATOM_ABS, 0, false); } MoleculeAutomorphismSearch am; am.detect_invalid_stereocenters = true; am.allow_undefined = true; am.process(mol); for (i = bmol.vertexBegin(); i != bmol.vertexEnd(); i = bmol.vertexNext(i)) { if (!bmol.stereocenters.exists(i)) continue; if (am.invalidStereocenter(i)) remove(i); } } bool MoleculeStereocenters::isPossibleStereocenter (int atom_idx, bool *possible_implicit_h, bool *possible_lone_pair ) { BaseMolecule &mol = _getMolecule(); const Vertex &vertex = mol.getVertex(atom_idx); int sure_double_bonds = 0; int possible_double_bonds = 0; int degree = vertex.degree(); if (degree > 4 || degree <= 2) return 0; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int e_idx = vertex.neiEdge(i); if (mol.getBondOrder(e_idx) == BOND_TRIPLE) return false; if (mol.getBondOrder(e_idx) == BOND_AROMATIC) return false; if (mol.getBondOrder(e_idx) == BOND_DOUBLE) sure_double_bonds++; else if (mol.possibleBondOrder(e_idx, BOND_DOUBLE)) possible_double_bonds++; } static const _Configuration allowed_stereocenters [] = { // element, charge, degree, double bonds, implicit degree {ELEM_C, 0, 3, 0, 4}, {ELEM_C, 0, 4, 0, 4}, {ELEM_Si, 0, 3, 0, 4}, {ELEM_Si, 0, 4, 0, 4}, {ELEM_As, 0, 4, 0, 4}, // see PubChem CID 6338551 {ELEM_B, -1, 4, 0, 4}, // see PubChem CID 6852133 {ELEM_N, 1, 3, 0, 4}, {ELEM_N, 1, 4, 0, 4}, {ELEM_N, 0, 4, 1, 4}, {ELEM_N, 0, 3, 0, 3}, {ELEM_S, 0, 4, 2, 4}, {ELEM_S, 1, 3, 0, 3}, {ELEM_S, 1, 4, 1, 4}, {ELEM_S, 0, 3, 1, 3}, {ELEM_P, 0, 3, 0, 3}, {ELEM_P, 1, 4, 0, 4}, {ELEM_P, 0, 4, 1, 4} }; bool possible = false; if (possible_implicit_h != 0) *possible_implicit_h = false; if (possible_lone_pair != 0) *possible_lone_pair = false; int i; for (i = 0; i < (int)NELEM(allowed_stereocenters); i++) { const _Configuration & as = allowed_stereocenters[i]; if (as.degree != vertex.degree()) continue; if (as.n_double_bonds < sure_double_bonds || as.n_double_bonds > sure_double_bonds + possible_double_bonds) continue; if (!mol.possibleAtomNumberAndCharge(atom_idx, as.elem, as.charge)) continue; possible = true; if (possible_implicit_h != 0 && as.implicit_degree == 4 && vertex.degree() == 3) *possible_implicit_h = true; if (possible_lone_pair != 0 && as.implicit_degree == 3) *possible_lone_pair = true; } return possible; } // When bidirectional mode is turned on (like is in ChemDraw) either bonds has // no directions, and up bond means down for the neighbour (and vice versa). // But such opposite directions has lower priority and if stereocenter configuration // can be determined by normal direction then do not check if opposite directions // contradicts original ones. bool MoleculeStereocenters::_buildOneCenter (int atom_idx, int *sensible_bonds_out, bool bidirectional_mode, bool bidirectional_either_mode, const Array<bool> &bond_ignore) { BaseMolecule &mol = _getMolecule(); const Vertex &vertex = mol.getVertex(atom_idx); int degree = vertex.degree(); _Atom stereocenter; stereocenter.group = 1; stereocenter.type = ATOM_ABS; int *pyramid = stereocenter.pyramid; int nei_idx = 0; _EdgeIndVec edge_ids[4]; int last_atom_dir = 0; int sure_double_bonds = 0; int possible_double_bonds = 0; pyramid[0] = -1; pyramid[1] = -1; pyramid[2] = -1; pyramid[3] = -1; int n_pure_hydrogens = 0; if (degree <= 2 || degree > 4) return false; bool is_either = false; bool zero_bond_length = false; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int e_idx = vertex.neiEdge(i); int v_idx = vertex.neiVertex(i); edge_ids[nei_idx].edge_idx = e_idx; edge_ids[nei_idx].nei_idx = v_idx; if (mol.possibleAtomNumberAndIsotope(v_idx, ELEM_H, 0)) { if (mol.getAtomNumber(v_idx) == ELEM_H && mol.getAtomIsotope(v_idx) == 0) n_pure_hydrogens++; edge_ids[nei_idx].rank = 10000; } else edge_ids[nei_idx].rank = v_idx; edge_ids[nei_idx].vec.diff(mol.getAtomXyz(v_idx), mol.getAtomXyz(atom_idx)); if (!edge_ids[nei_idx].vec.normalize()) zero_bond_length = true; if (mol.getBondOrder(e_idx) == BOND_TRIPLE) return false; if (mol.getBondOrder(e_idx) == BOND_AROMATIC) return false; if (mol.getBondOrder(e_idx) == BOND_DOUBLE) sure_double_bonds++; else if (mol.possibleBondOrder(e_idx, BOND_DOUBLE)) possible_double_bonds++; if (_getDirection(mol, atom_idx, v_idx, bidirectional_either_mode) == BOND_EITHER) is_either = true; nei_idx++; } _EdgeIndVec tmp; bool possible_implicit_h = false; bool possible_lone_pair = false; int i; if (!isPossibleStereocenter(atom_idx, &possible_implicit_h, &possible_lone_pair)) return false; // Local synonym to get bond direction auto getDir = [&](int from, int to) { int idx = mol.findEdgeIndex(from, to); if (bond_ignore[idx]) return 0; return _getDirection(mol, from, to, bidirectional_mode); }; if (is_either) { stereocenter.type = ATOM_ANY; for (i = 0; i < degree; i++) { stereocenter.pyramid[i] = edge_ids[i].nei_idx; if (getDir(atom_idx, edge_ids[i].nei_idx) > 0) sensible_bonds_out[edge_ids[i].edge_idx] = 1; } _stereocenters.insert(atom_idx, stereocenter); return true; } if (degree == 4) { // sort by neighbor atom index (ascending) if (edge_ids[0].rank > edge_ids[1].rank) __swap(edge_ids[0], edge_ids[1], tmp); if (edge_ids[1].rank > edge_ids[2].rank) __swap(edge_ids[1], edge_ids[2], tmp); if (edge_ids[2].rank > edge_ids[3].rank) __swap(edge_ids[2], edge_ids[3], tmp); if (edge_ids[1].rank > edge_ids[2].rank) __swap(edge_ids[1], edge_ids[2], tmp); if (edge_ids[0].rank > edge_ids[1].rank) __swap(edge_ids[0], edge_ids[1], tmp); if (edge_ids[1].rank > edge_ids[2].rank) __swap(edge_ids[1], edge_ids[2], tmp); int main1 = -1, main2 = -1, side1 = -1, side2 = -1; int main_dir = 0; for (nei_idx = 0; nei_idx < 4; nei_idx++) { int stereo = getDir(atom_idx, edge_ids[nei_idx].nei_idx); if (stereo == BOND_UP || stereo == BOND_DOWN) { main1 = nei_idx; main_dir = stereo; break; } } if (main1 == -1) return false; if (zero_bond_length) throw Error("zero bond length near atom %d", atom_idx); if (n_pure_hydrogens > 1) throw Error("%d hydrogens near stereocenter %d", n_pure_hydrogens, atom_idx); int xyz1, xyz2; // find main2 as opposite to main1 if (main2 == -1) { xyz1 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 1) % 4].vec, edge_ids[(main1 + 2) % 4].vec); xyz2 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 1) % 4].vec, edge_ids[(main1 + 3) % 4].vec); if (xyz1 + xyz2 == 3 || xyz1 + xyz2 == 12) { main2 = (main1 + 1) % 4; side1 = (main1 + 2) % 4; side2 = (main1 + 3) % 4; } } if (main2 == -1) { xyz1 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 2) % 4].vec, edge_ids[(main1 + 1) % 4].vec); xyz2 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 2) % 4].vec, edge_ids[(main1 + 3) % 4].vec); if (xyz1 + xyz2 == 3 || xyz1 + xyz2 == 12) { main2 = (main1 + 2) % 4; side1 = (main1 + 1) % 4; side2 = (main1 + 3) % 4; } } if (main2 == -1) { xyz1 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 3) % 4].vec, edge_ids[(main1 + 1) % 4].vec); xyz2 = _xyzzy(edge_ids[main1].vec, edge_ids[(main1 + 3) % 4].vec, edge_ids[(main1 + 2) % 4].vec); if (xyz1 + xyz2 == 3 || xyz1 + xyz2 == 12) { main2 = (main1 + 3) % 4; side1 = (main1 + 2) % 4; side2 = (main1 + 1) % 4; } } if (main2 == -1) throw Error("internal error: can not find opposite bond near atom %d", atom_idx); if (main_dir == BOND_UP && getDir(atom_idx, edge_ids[main2].nei_idx) == BOND_DOWN) throw Error("stereo types of the opposite bonds mismatch near atom %d", atom_idx); if (main_dir == BOND_DOWN && getDir(atom_idx, edge_ids[main2].nei_idx) == BOND_UP) throw Error("stereo types of the opposite bonds mismatch near atom %d", atom_idx); if (main_dir == getDir(atom_idx, edge_ids[side1].nei_idx)) throw Error("stereo types of non-opposite bonds match near atom %d", atom_idx); if (main_dir == getDir(atom_idx, edge_ids[side2].nei_idx)) throw Error("stereo types of non-opposite bonds match near atom %d", atom_idx); if (main1 == 3 || main2 == 3) last_atom_dir = main_dir; else last_atom_dir = (main_dir == BOND_UP ? BOND_DOWN : BOND_UP); int sign = _sign(edge_ids[0].vec, edge_ids[1].vec, edge_ids[2].vec); if ((last_atom_dir == BOND_UP && sign > 0) || (last_atom_dir == BOND_DOWN && sign < 0)) { pyramid[0] = edge_ids[0].nei_idx; pyramid[1] = edge_ids[1].nei_idx; pyramid[2] = edge_ids[2].nei_idx; } else { pyramid[0] = edge_ids[0].nei_idx; pyramid[1] = edge_ids[2].nei_idx; pyramid[2] = edge_ids[1].nei_idx; } pyramid[3] = edge_ids[3].nei_idx; } else if (degree == 3) { // sort by neighbor atom index (ascending) if (edge_ids[0].rank > edge_ids[1].rank) __swap(edge_ids[0], edge_ids[1], tmp); if (edge_ids[1].rank > edge_ids[2].rank) __swap(edge_ids[1], edge_ids[2], tmp); if (edge_ids[0].rank > edge_ids[1].rank) __swap(edge_ids[0], edge_ids[1], tmp); bool degenerate = true; int dirs[3] = {0, 0, 0}; int main_nei = -1; // will be assigned if all three neighors belong to the same half-plane int n_up = 0, n_down = 0; for (nei_idx = 0; nei_idx < 3; nei_idx++) { dirs[nei_idx] = getDir(atom_idx, edge_ids[nei_idx].nei_idx); if (dirs[nei_idx] == BOND_UP) n_up++; else if (dirs[nei_idx] == BOND_DOWN) n_down++; } if (n_down == 0 && n_up == 0) return false; for (nei_idx = 0; nei_idx < 3; nei_idx++) { int xyzzy = _xyzzy(edge_ids[(nei_idx + 1) % 3].vec, edge_ids[(nei_idx + 2) % 3].vec, edge_ids[nei_idx].vec); if (xyzzy == 1) main_nei = nei_idx; if (xyzzy == 2) degenerate = false; } int dir = 1; if (main_nei != -1) { if (dirs[main_nei] != 0) { if (dirs[(main_nei + 1) % 3] == dirs[main_nei] || dirs[(main_nei + 2) % 3] == dirs[main_nei]) throw Error("directions of neighbor stereo bonds match near atom %d", atom_idx); if (dirs[main_nei] == BOND_UP) dir = -1; } else { int d1 = dirs[(main_nei + 1) % 3]; int d2 = dirs[(main_nei + 2) % 3]; if (d1 == 0) d1 = d2; else if (d2 != 0 && d1 != d2) throw Error("directions of opposite stereo bonds do not match near atom %d", atom_idx); if (d1 == 0) return false; if (d1 == BOND_DOWN) dir = -1; } } else if (!degenerate) { if (n_down > 0 && n_up > 0) throw Error("one bond up, one bond down -- indefinite case near atom %d", atom_idx); if (!possible_lone_pair) { if (n_up == 3) throw Error("all 3 bonds up near stereoatom %d", atom_idx); if (n_down == 3) throw Error("all 3 bonds down near stereoatom %d", atom_idx); } if (n_down > 0) dir = -1; } else throw Error("degenerate case for 3 bonds near stereoatom %d", atom_idx); if (zero_bond_length) throw Error("zero bond length near atom %d", atom_idx); if (n_pure_hydrogens > 0 && !possible_lone_pair) throw Error("have hydrogen(s) besides implicit hydrogen near stereocenter %d", atom_idx); int sign = _sign(edge_ids[0].vec, edge_ids[1].vec, edge_ids[2].vec); if (sign == dir) { pyramid[0] = edge_ids[0].nei_idx; pyramid[1] = edge_ids[2].nei_idx; pyramid[2] = edge_ids[1].nei_idx; } else { pyramid[0] = edge_ids[0].nei_idx; pyramid[1] = edge_ids[1].nei_idx; pyramid[2] = edge_ids[2].nei_idx; } pyramid[3] = -1; } for (i = 0; i < degree; i++) if (getDir(atom_idx, edge_ids[i].nei_idx) > 0) sensible_bonds_out[edge_ids[i].edge_idx] = 1; _stereocenters.insert(atom_idx, stereocenter); return true; } // 1 -- in the smaller angle, 2 -- in the bigger angle, // 4 -- in the 'positive' straight angle, 8 -- in the 'negative' straight angle int MoleculeStereocenters::_xyzzy (const Vec3f &v1, const Vec3f &v2, const Vec3f &u) { float eps = 1e-3f; Vec3f cross; cross.cross(v1, v2); float sine1 = cross.z; float cosine1 = Vec3f::dot(v1, v2); cross.cross(v1, u); float sine2 = cross.z; float cosine2 = Vec3f::dot(v1 ,u); if ((float)fabs(sine1) < eps) { if ((float)fabs(sine2) < eps) throw Error("degenerate case -- bonds overlap"); return (sine2 > 0) ? 4 : 8; } if (sine1 * sine2 < -eps * eps) return 2; if (cosine2 < cosine1) return 2; return 1; } int MoleculeStereocenters::_sign (const Vec3f &v1, const Vec3f &v2, const Vec3f &v3) { // Check the angle between bonds float dot_eps = 0.997f; // Corresponds to 4.5 degrees // float dot_eps = 0.99999f; // Corresponds to 0 degrees if (Vec3f::dot(v1, v2) > dot_eps * v1.length() * v2.length() || Vec3f::dot(v1, v3) > dot_eps * v1.length() * v3.length() || Vec3f::dot(v2, v3) > dot_eps * v2.length() * v3.length()) throw Error("angle between bonds is too small"); float res = (v1.x - v3.x) * (v2.y - v3.y) - (v1.y - v3.y) * (v2.x - v3.x); float eps = 1e-3f; if (res > eps) return 1; if (res < -eps) return -1; throw Error("degenerate triangle"); } int MoleculeStereocenters::_onPlane (const Vec3f &v1, const Vec3f &v2, const Vec3f &v3, const Vec3f &u) { Vec3f v1u, v2u, v3u, p; float eps = 0.1f; v1u.diff(v1, u); v1u.normalize(); v2u.diff(v2, u); v2u.normalize(); v3u.diff(v3, u); v3u.normalize(); float a12, a23, a13; Vec3f::angle(v1u, v2u, a12); Vec3f::angle(v2u, v3u, a23); Vec3f::angle(v1u, v3u, a13); float angle_sum = a12 + a23 + a13; if (fabs(angle_sum - 2*PI) < eps) return 0; p.cross(v2u, v3u); float det = Vec3f::dot(v1u, p); if (det > 0) return 1; else return -1; } int MoleculeStereocenters::getType (int idx) const { _Atom *atom = _stereocenters.at2(idx); if (atom == 0) return 0; return atom->type; } int MoleculeStereocenters::getGroup (int idx) const { return _stereocenters.at(idx).group; } void MoleculeStereocenters::setGroup (int idx, int group) { _stereocenters.at(idx).group = group; } void MoleculeStereocenters::setType (int idx, int type, int group) { _stereocenters.at(idx).type = type; _stereocenters.at(idx).group = group; } void MoleculeStereocenters::setType (int idx, int type) { _stereocenters.at(idx).type = type; } const int * MoleculeStereocenters::getPyramid (int idx) const { return _stereocenters.at(idx).pyramid; } int * MoleculeStereocenters::getPyramid (int idx) { _Atom *stereo = _stereocenters.at2(idx); if (stereo != 0) return stereo->pyramid; else return 0; } void MoleculeStereocenters::invertPyramid (int idx) { int tmp; int *pyramid = getPyramid(idx); __swap(pyramid[0], pyramid[1], tmp); } void MoleculeStereocenters::getAbsAtoms (Array<int> &indices) { indices.clear(); for (int i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) { if (_stereocenters.value(i).type == ATOM_ABS) indices.push(_stereocenters.key(i)); } } void MoleculeStereocenters::_getGroups (int type, Array<int> &numbers) { numbers.clear(); for (int i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) { if (_stereocenters.value(i).type == type) { int group = _stereocenters.value(i).group; if (numbers.find(group) == -1) numbers.push(group); } } } void MoleculeStereocenters::_getGroup (int type, int number, Array<int> &indices) { indices.clear(); for (int i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) { const _Atom &atom = _stereocenters.value(i); if (atom.type == type && atom.group == number) indices.push(_stereocenters.key(i)); } } void MoleculeStereocenters::getOrGroups (Array<int> &numbers) { _getGroups(ATOM_OR, numbers); } void MoleculeStereocenters::getAndGroups (Array<int> &numbers) { _getGroups(ATOM_AND, numbers); } void MoleculeStereocenters::getOrGroup (int number, Array<int> &indices) { _getGroup(ATOM_OR, number, indices); } void MoleculeStereocenters::getAndGroup (int number, Array<int> &indices) { _getGroup(ATOM_AND, number, indices); } bool MoleculeStereocenters::sameGroup (int idx1, int idx2) { _Atom *center1 = _stereocenters.at2(idx1); _Atom *center2 = _stereocenters.at2(idx2); if (center1 == 0 && center2 == 0) return true; if (center1 == 0 || center2 == 0) return false; if (center1->type == ATOM_ABS) return center2->type == ATOM_ABS; if (center1->type == ATOM_OR) return center2->type == ATOM_OR && center1->group == center2->group; if (center1->type == ATOM_AND) return center2->type == ATOM_AND && center1->group == center2->group; return false; } bool MoleculeStereocenters::haveAllAbs () { int i; for (i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) if (_stereocenters.value(i).type != ATOM_ABS) return false; return true; } bool MoleculeStereocenters::haveAbs () { int i; for (i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) if (_stereocenters.value(i).type == ATOM_ABS) return true; return false; } bool MoleculeStereocenters::haveAllAbsAny () { int i; for (i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) if (_stereocenters.value(i).type != ATOM_ABS && _stereocenters.value(i).type != ATOM_ANY) return false; return true; } bool MoleculeStereocenters::haveAllAndAny () { int i; int groupno = -1; for (i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) { if (_stereocenters.value(i).type == ATOM_ANY) continue; if (_stereocenters.value(i).type != ATOM_AND) return false; if (groupno == -1) groupno = _stereocenters.value(i).group; else if (groupno != _stereocenters.value(i).group) return false; } return true; } bool MoleculeStereocenters::checkSub (const MoleculeStereocenters &query, const MoleculeStereocenters &target, const int *mapping, bool reset_h_isotopes, Filter *stereocenters_vertex_filter) { QS_DEF(Array<int>, flags); flags.clear_resize(query._stereocenters.end()); flags.zerofill(); int i, j; for (i = query._stereocenters.begin(); i != query._stereocenters.end(); i = query._stereocenters.next(i)) { if (flags[i]) continue; flags[i] = 1; const _Atom &cq = query._stereocenters.value(i); int iq = query._stereocenters.key(i); if (mapping[iq] < 0) continue; // happens only on Exact match (when some query fragments are disabled) if (stereocenters_vertex_filter != 0 && !stereocenters_vertex_filter->valid(iq)) continue; if (cq.type < ATOM_AND) continue; int stereo_group_and = -1; int stereo_group_or = -1; int revert = -1; // 0 -- not revert, 1 -- revert bool have_abs = false; int pyramid_mapping[4]; int type = cq.type; if (type == ATOM_ABS) { getPyramidMapping(query, target, iq, mapping, pyramid_mapping, reset_h_isotopes); if (!isPyramidMappingRigid(pyramid_mapping)) return false; } else if (type == ATOM_OR || type == ATOM_AND) { for (j = i; j != query._stereocenters.end(); j = query._stereocenters.next(j)) { int iq2 = query._stereocenters.key(j); const _Atom &cq2 = query._stereocenters.value(j); if (cq2.type != type) continue; if (cq2.group != cq.group) continue; const _Atom *ct2 = target._stereocenters.at2(mapping[iq2]); if (ct2 == 0) return false; if (ct2->type < type) return false; flags[j] = 1; if (ct2->type == ATOM_AND) { if (stereo_group_or != -1) return false; if (have_abs) return false; if (stereo_group_and == -1) stereo_group_and = ct2->group; else if (stereo_group_and != ct2->group) return false; } else if (ct2->type == ATOM_OR) { if (stereo_group_and != -1) return false; if (have_abs) return false; if (stereo_group_or == -1) stereo_group_or = ct2->group; else if (stereo_group_or != ct2->group) return false; } else if (ct2->type == ATOM_ABS) { if (stereo_group_and != -1) return false; if (stereo_group_or != -1) return false; have_abs = true; } getPyramidMapping(query, target, iq2, mapping, pyramid_mapping, reset_h_isotopes); int not_equal = isPyramidMappingRigid(pyramid_mapping) ? 0 : 1; if (revert == -1) revert = not_equal; else if (revert != not_equal) return false; } } } return true; } bool MoleculeStereocenters::isPyramidMappingRigid (const int mapping[4]) { int arr[4], tmp; bool rigid = true; memcpy(arr, mapping, 4 * sizeof(int)); if (arr[0] > arr[1]) __swap(arr[0], arr[1], tmp), rigid = !rigid; if (arr[1] > arr[2]) __swap(arr[1], arr[2], tmp), rigid = !rigid; if (arr[2] > arr[3]) __swap(arr[2], arr[3], tmp), rigid = !rigid; if (arr[1] > arr[2]) __swap(arr[1], arr[2], tmp), rigid = !rigid; if (arr[0] > arr[1]) __swap(arr[0], arr[1], tmp), rigid = !rigid; if (arr[1] > arr[2]) __swap(arr[1], arr[2], tmp), rigid = !rigid; return rigid; } bool MoleculeStereocenters::isPyramidMappingRigid_Sort (int *pyramid, const int *mapping) { bool rigid = true; int i, tmp; for (i = 0; i < 4; i++) if (pyramid[i] != -1 && mapping[pyramid[i]] < 0) pyramid[i] = -1; if (pyramid[0] == -1 || (pyramid[1] >= 0 && mapping[pyramid[0]] > mapping[pyramid[1]])) __swap(pyramid[0], pyramid[1], tmp), rigid = !rigid; if (pyramid[1] == -1 || (pyramid[2] >= 0 && mapping[pyramid[1]] > mapping[pyramid[2]])) __swap(pyramid[1], pyramid[2], tmp), rigid = !rigid; if (pyramid[2] == -1 || (pyramid[3] >= 0 && mapping[pyramid[2]] > mapping[pyramid[3]])) __swap(pyramid[2], pyramid[3], tmp), rigid = !rigid; if (pyramid[1] == -1 || (pyramid[2] >= 0 && mapping[pyramid[1]] > mapping[pyramid[2]])) __swap(pyramid[1], pyramid[2], tmp), rigid = !rigid; if (pyramid[0] == -1 || (pyramid[1] >= 0 && mapping[pyramid[0]] > mapping[pyramid[1]])) __swap(pyramid[0], pyramid[1], tmp), rigid = !rigid; if (pyramid[1] == -1 || (pyramid[2] >= 0 && mapping[pyramid[1]] > mapping[pyramid[2]])) __swap(pyramid[1], pyramid[2], tmp), rigid = !rigid; return rigid; } bool MoleculeStereocenters::isPyramidMappingRigid (const int *pyramid, int size, const int *mapping) { if (size == 3) { int order[3] = {mapping[pyramid[0]], mapping[pyramid[1]], mapping[pyramid[2]]}; int min = __min3(order[0], order[1], order[2]); while (order[0] != min) { int t = order[2]; order[2] = order[1]; order[1] = order[0]; order[0] = t; } return order[1] < order[2]; } if (size == 4) { int arr[4]; arr[0] = mapping[pyramid[0]]; arr[1] = mapping[pyramid[1]]; arr[2] = mapping[pyramid[2]]; arr[3] = mapping[pyramid[3]]; return isPyramidMappingRigid(arr); } throw Error("IsPyramidMappingRigid: size = %d", size); } void MoleculeStereocenters::getPyramidMapping (const MoleculeStereocenters &query, const MoleculeStereocenters &target, int query_atom, const int *mapping, int *mapping_out, bool reset_h_isotopes) { int i, j; BaseMolecule &tmol = target._getMolecule(); BaseMolecule &qmol = query._getMolecule(); const int *seq1 = query.getPyramid(query_atom); const int *seq2 = target.getPyramid(mapping[query_atom]); int seq2_matched[] = {0, 0, 0, 0}; for (i = 0; i < 4; i++) mapping_out[i] = -1; for (i = 0; i < 4; i++) { // skip implicit hydrogen for the first pass if (seq1[i] == -1) continue; // unmapped atom? if (mapping[seq1[i]] < 0) { // only hydrogens are allowed to be unmapped if (qmol.getAtomNumber(seq1[i]) != ELEM_H) throw Error("unmapped non-hydrogen atom (atom number %d)", qmol.getAtomNumber(seq1[i])); continue; } for (j = 0; j < 4; j++) if (seq2[j] == mapping[seq1[i]]) break; if (j == 4) throw Error("cannot map pyramid"); mapping_out[i] = j; seq2_matched[j] = 1; } // take implicit hydrogen to the second pass for (i = 0; i < 4; i++) { if (mapping_out[i] != -1) continue; for (j = 0; j < 4; j++) { if (seq2[j] == -1) break; // match to implicit hydrogen if (!seq2_matched[j]) { if (tmol.getAtomNumber(seq2[j]) == ELEM_H) break; // match to explicit hydrogen // rare cases like '[S@](F)(Cl)=O' on '[S@](F)(Cl)(=O)=N' if (seq1[i] == -1 && tmol.getAtomNumber(mapping[query_atom]) == ELEM_S) break; // match free electron pair to an atom // Match N[C@H](O)S on C[C@@](N)(O)S if (seq1[i] == -1) break; // match vacant place in query to this atom } } if (j == 4) throw Error("cannot map pyramid"); mapping_out[i] = j; seq2_matched[j] = 1; } } void MoleculeStereocenters::remove (int idx) { _stereocenters.remove(idx); } void MoleculeStereocenters::removeAtoms (const Array<int> &indices) { const BaseMolecule &mol = _getMolecule(); for (int i = 0; i < indices.size(); i++) { int idx = indices[i]; if (_stereocenters.find(idx)) _stereocenters.remove(idx); else { const Vertex &vertex = mol.getVertex(idx); for (int k = vertex.neiBegin(); k != vertex.neiEnd(); k = vertex.neiNext(k)) { int nei_vertex = vertex.neiVertex(k); _removeBondDir(idx, nei_vertex); } } } } void MoleculeStereocenters::removeBonds (const Array<int> &indices) { const BaseMolecule &mol = _getMolecule(); for (int i = 0; i < indices.size(); i++) { const Edge &edge = mol.getEdge(indices[i]); _removeBondDir(edge.beg, edge.end); _removeBondDir(edge.end, edge.beg); } } void MoleculeStereocenters::_removeBondDir (int atom_from, int atom_to) { _Atom *stereo_atom = _stereocenters.at2(atom_to); if (stereo_atom != 0) { if (stereo_atom->pyramid[3] == -1) _stereocenters.remove(atom_to); else { BaseMolecule &mol = _getMolecule(); if (!mol.isQueryMolecule() || mol.possibleAtomNumber(atom_from, ELEM_H) || mol.isRSite(atom_from)) _convertAtomToImplicitHydrogen(stereo_atom->pyramid, atom_from); } } } void MoleculeStereocenters::buildOnSubmolecule (const MoleculeStereocenters &super, int *mapping) { int i, j; BaseMolecule &mol = _getMolecule(); for (i = super._stereocenters.begin(); i != super._stereocenters.end(); i = super._stereocenters.next(i)) { int super_idx = super._stereocenters.key(i); const _Atom &super_stereocenter = super._stereocenters.value(i); int sub_idx = mapping[super_idx]; if (sub_idx < 0) continue; _Atom new_stereocenter; new_stereocenter.group = super_stereocenter.group; new_stereocenter.type = super_stereocenter.type; for (j = 0; j < 4; j++) { int idx = super_stereocenter.pyramid[j]; if (idx == -1) new_stereocenter.pyramid[j] = -1; else { int val = mapping[idx]; if (val != -1 && mol.findEdgeIndex(sub_idx, val) == -1) val = -1; new_stereocenter.pyramid[j] = val; } } moveMinimalToEnd(new_stereocenter.pyramid); if (new_stereocenter.pyramid[0] == -1 || new_stereocenter.pyramid[1] == -1 || new_stereocenter.pyramid[2] == -1) // pyramid is not mapped completely continue; _stereocenters.insert(sub_idx, new_stereocenter); const Vertex &super_vertex = super._getMolecule().getVertex(super_idx); for (j = super_vertex.neiBegin(); j != super_vertex.neiEnd(); j = super_vertex.neiNext(j)) { int super_edge = super_vertex.neiEdge(j); if (mapping[super_vertex.neiVertex(j)] == -1) continue; int dir = super._getMolecule().getBondDirection(super_edge); if (dir != 0) mol.setBondDirection(mol.findEdgeIndex(sub_idx, mapping[super_vertex.neiVertex(j)]), dir); } } } int MoleculeStereocenters::size () const { return _stereocenters.size(); } void MoleculeStereocenters::add (int atom_idx, int type, int group, bool inverse_pyramid) { int pyramid[4]; _restorePyramid(atom_idx, pyramid, inverse_pyramid); add(atom_idx, type, group, pyramid); } void MoleculeStereocenters::add (int atom_idx, int type, int group, const int pyramid[4]) { if (atom_idx < 0) throw Error("stereocenter index is invalid"); if (pyramid[0] == -1 || pyramid[1] == -1 || pyramid[2] == -1) throw Error("stereocenter (%d) pyramid must have at least 3 atoms", atom_idx); _Atom center; center.type = type; center.group = group; memcpy(center.pyramid, pyramid, 4 * sizeof(int)); _stereocenters.insert(atom_idx, center); } int MoleculeStereocenters::begin () const { return _stereocenters.begin(); } int MoleculeStereocenters::end () const { return _stereocenters.end(); } int MoleculeStereocenters::next (int i) const { return _stereocenters.next(i); } bool MoleculeStereocenters::exists (int atom_idx) const { return _stereocenters.at2(atom_idx) != 0; } void MoleculeStereocenters::get (int i, int &atom_idx, int &type, int &group, int *pyramid) const { const _Atom ¢er = _stereocenters.value(i); atom_idx = _stereocenters.key(i); type = center.type; group = center.group; if (pyramid != 0) memcpy(pyramid, center.pyramid, 4 * sizeof(int)); } int MoleculeStereocenters::getAtomIndex (int i) const { return _stereocenters.key(i); } void MoleculeStereocenters::get (int atom_idx, int &type, int &group, int *pyramid) const { const _Atom ¢er = _stereocenters.at(atom_idx); type = center.type; group = center.group; if (pyramid != 0) memcpy(pyramid, center.pyramid, 4 * sizeof(int)); } void MoleculeStereocenters::registerUnfoldedHydrogen (int atom_idx, int added_hydrogen) { _Atom *center = _stereocenters.at2(atom_idx); if (center == 0) return; if (center->pyramid[3] != -1) throw Error("cannot unfold hydrogens for stereocenter without implicit hydrogens"); center->pyramid[3] = added_hydrogen; } void MoleculeStereocenters::flipBond (int atom_parent, int atom_from, int atom_to) { if (exists(atom_from)) { _Atom *from_center = _stereocenters.at2(atom_from); if (from_center->pyramid[3] == -1) remove(atom_from); else { for (int i = 0; i < 4; i++) if (from_center->pyramid[i] == atom_parent) from_center->pyramid[i] = -1; moveMinimalToEnd(from_center->pyramid); } } if (exists(atom_to)) { _Atom *to_center = _stereocenters.at2(atom_to); if (to_center->pyramid[3] != -1) throw Error("Bad bond flipping. Stereocenter pyramid is already full"); to_center->pyramid[3] = atom_parent; } if (!exists(atom_parent)) return; _Atom *center = _stereocenters.at2(atom_parent); for (int i = 0; i < 4; i++) if (center->pyramid[i] == atom_from) { center->pyramid[i] = atom_to; break; } } void MoleculeStereocenters::_restorePyramid (int idx, int pyramid[4], int invert_pyramid) { BaseMolecule &mol = _getMolecule(); const Vertex &vertex = mol.getVertex(idx); int j, count = 0; pyramid[0] = -1; pyramid[1] = -1; pyramid[2] = -1; pyramid[3] = -1; for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int nei = vertex.neiVertex(j); if (vertex.degree() == 3 || mol.getAtomNumber(nei) != ELEM_H || mol.getAtomIsotope(nei) != 0) { if (count == 4) throw Error("restorePyramid(): stereocenter has more than 4 neighbors"); pyramid[count++] = nei; } else if (pyramid[3] == -1) pyramid[3] = nei; else throw Error("restorePyramid(): extra hydrogen"); } int tmp; // sort pyramid indices if (pyramid[3] == -1) { if (pyramid[0] > pyramid[1]) __swap(pyramid[0], pyramid[1], tmp); if (pyramid[1] > pyramid[2]) __swap(pyramid[1], pyramid[2], tmp); if (pyramid[0] > pyramid[1]) __swap(pyramid[0], pyramid[1], tmp); } else { if (pyramid[0] > pyramid[1]) __swap(pyramid[0], pyramid[1], tmp); if (pyramid[1] > pyramid[2]) __swap(pyramid[1], pyramid[2], tmp); if (pyramid[2] > pyramid[3]) __swap(pyramid[2], pyramid[3], tmp); if (pyramid[1] > pyramid[2]) __swap(pyramid[1], pyramid[2], tmp); if (pyramid[0] > pyramid[1]) __swap(pyramid[0], pyramid[1], tmp); if (pyramid[1] > pyramid[2]) __swap(pyramid[1], pyramid[2], tmp); } if (invert_pyramid) __swap(pyramid[1], pyramid[2], j); } void MoleculeStereocenters::rotatePyramid (int *pyramid) { int tmp; tmp = pyramid[0]; pyramid[0] = pyramid[1]; pyramid[1] = pyramid[2]; if (pyramid[3] == -1) { pyramid[2] = tmp; } else { pyramid[2] = pyramid[3]; pyramid[3] = tmp; } } void MoleculeStereocenters::moveImplicitHydrogenToEnd (int pyramid[4]) { moveMinimalToEnd(pyramid); if (pyramid[3] != -1) throw Error("moveImplicitHydrogenToEnd(): no implicit hydrogen"); } void MoleculeStereocenters::moveElementToEnd (int pyramid[4], int element) { int cnt = 0; while (pyramid[3] != element) { if (cnt == 4) throw Error("moveElementToEnd(): internal error"); rotatePyramid(pyramid); cnt++; } if (cnt & 1) __swap(pyramid[0], pyramid[1], cnt); } void MoleculeStereocenters::moveMinimalToEnd (int pyramid[4]) { int min_element = __min(__min(pyramid[0], pyramid[1]), __min(pyramid[2], pyramid[3])); moveElementToEnd(pyramid, min_element); } void MoleculeStereocenters::_convertAtomToImplicitHydrogen (int pyramid[4], int atom_to_remove) { if (pyramid[3] == -1) throw Error("Cannot remove atoms form sterecenter with implicit hydrogen. " "Stereocenter should be removed"); bool removed = false; for (int i = 0; i < 4; i++) if (pyramid[i] == atom_to_remove) { pyramid[i] = -1; removed = true; break; } if (!removed) throw Error("Specified atom %d wasn't found in the stereopyramid", atom_to_remove); moveImplicitHydrogenToEnd(pyramid); } void MoleculeStereocenters::markBond (int atom_idx) { const _Atom *atom_ptr = _stereocenters.at2(atom_idx); if (atom_ptr == NULL) return; BaseMolecule &mol = _getMolecule(); const _Atom &atom = *atom_ptr; int pyramid[4]; int mult = 1; int size = 0; int j; memcpy(pyramid, atom.pyramid, 4 * sizeof(int)); const Vertex &vertex = mol.getVertex(atom_idx); if (atom.type <= ATOM_ANY) { // fill the pyramid for (j = vertex.neiBegin(); j != vertex.neiEnd() && size < 4; j = vertex.neiNext(j)) pyramid[size++] = vertex.neiVertex(j); } else size = (pyramid[3] == -1 ? 3 : 4); // clear bond directions that goes to this atom, and not from this atom because they can // be marked by other sterecenter for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) if (mol.getBondDirection2(atom_idx, vertex.neiVertex(j)) != 0) mol.setBondDirection(vertex.neiEdge(j), 0); int edge_idx = -1; for (j = 0; j < size; j++) { edge_idx = mol.findEdgeIndex(atom_idx, pyramid[size - 1]); if (mol.getBondDirection(edge_idx) == 0 && mol.getVertex(pyramid[size - 1]).degree() == 1) break; rotatePyramid(pyramid); if (size == 4) mult = -mult; } if (j == size) { for (j = 0; j < size; j++) { edge_idx = mol.findEdgeIndex(atom_idx, pyramid[size - 1]); if (mol.getBondDirection(edge_idx) == 0 && mol.getBondTopology(edge_idx) == TOPOLOGY_CHAIN && getType(pyramid[size - 1]) == 0) break; rotatePyramid(pyramid); if (size == 4) mult = -mult; } } if (j == size) { for (j = 0; j < size; j++) { edge_idx = mol.findEdgeIndex(atom_idx, pyramid[size - 1]); if (mol.getBondDirection(edge_idx) == 0 && getType(pyramid[size - 1]) == 0) break; rotatePyramid(pyramid); if (size == 4) mult = -mult; } } if (j == size) { for (j = 0; j < size; j++) { edge_idx = mol.findEdgeIndex(atom_idx, pyramid[size - 1]); if (mol.getBondDirection(edge_idx)== 0 && mol.getBondTopology(edge_idx) == TOPOLOGY_CHAIN) break; rotatePyramid(pyramid); if (size == 4) mult = -mult; } } if (j == size) { for (j = 0; j < size; j++) { edge_idx = mol.findEdgeIndex(atom_idx, pyramid[size - 1]); if (mol.getBondDirection(edge_idx) == 0) break; rotatePyramid(pyramid); if (size == 4) mult = -mult; } } if (j == size) throw Error("no bond can be marked"); if (mol.getEdge(edge_idx).beg != atom_idx) mol.swapEdgeEnds(edge_idx); if (atom.type > ATOM_ANY) { Vec3f dirs[4]; for (j = 0; j < size; j++) { dirs[j] = mol.getAtomXyz(pyramid[j]); dirs[j].sub(mol.getAtomXyz(atom_idx)); if (!dirs[j].normalize()) throw Error("zero bond length"); } int sign = _sign(dirs[0], dirs[1], dirs[2]); if (size == 3) { // Check if all the three bonds belong to the same half-plane. // This is equal to that one of the bonds lies in the smaller // angle formed by the other two. if (_xyzzy(dirs[1], dirs[0], dirs[2]) == 1 || _xyzzy(dirs[2], dirs[1], dirs[0]) == 1 || _xyzzy(dirs[0], dirs[2], dirs[1]) == 1) { if (_xyzzy(dirs[1], dirs[0], dirs[2]) == 1) mult = -1; mol.setBondDirection(edge_idx, (sign * mult == 1) ? BOND_DOWN : BOND_UP); } else mol.setBondDirection(edge_idx, (sign == 1) ? BOND_DOWN : BOND_UP); } else mol.setBondDirection(edge_idx, (sign * mult == 1) ? BOND_UP : BOND_DOWN); } else mol.setBondDirection(edge_idx, BOND_EITHER); } void MoleculeStereocenters::markBonds () { int i; for (i = _stereocenters.begin(); i != _stereocenters.end(); i = _stereocenters.next(i)) markBond(_stereocenters.key(i)); } bool MoleculeStereocenters::isAutomorphism (BaseMolecule &mol, const Array<int> &mapping, const Filter *filter) { MoleculeStereocenters &stereocenters = mol.stereocenters; for (int i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { if (filter && !filter->valid(i)) continue; int idx, type, group; int pyramid[4]; int j; stereocenters.get(i, idx, type, group, pyramid); if (mapping[idx] == -1) continue; int size = 0; for (j = 0; j < 4; j++) if (pyramid[j] >= 0) { if (mapping[pyramid[j]] >= 0) size++; else pyramid[j] = -1; } if (size < 3) continue; if (type < MoleculeStereocenters::ATOM_AND) continue; if (stereocenters.getType(mapping[idx]) != type) return false; int pyra_map[4]; MoleculeStereocenters::getPyramidMapping(stereocenters, stereocenters, idx, mapping.ptr(), pyra_map, false); if (!MoleculeStereocenters::isPyramidMappingRigid(pyra_map)) return false; } return true; } int MoleculeStereocenters::_getDirection (BaseMolecule &mol, int atom_from, int atom_to, bool bidirectional_mode) { int dir = mol.getBondDirection2(atom_from, atom_to); if (bidirectional_mode && dir == 0) { // Try to get direction in opposite direction dir = mol.getBondDirection2(atom_to, atom_from); if (dir == BOND_UP) dir = BOND_DOWN; else if (dir == BOND_DOWN) dir = BOND_UP; } return dir; } �������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_substructure_matcher.cpp����������������������������������0000664�0000000�0000000�00000136171�12710376503�0025454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/auto_ptr.h" #include "math/algebra.h" #include "molecule/base_molecule.h" #include "base_cpp/array.h" #include "graph/graph_decomposer.h" #include "graph/filter.h" #include "graph/graph_affine_matcher.h" #include "graph/edge_rotation_matcher.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/molecule_neighbourhood_counters.h" #include "molecule/elements.h" #include "graph/graph.h" #include "molecule/query_molecule.h" using namespace indigo; CP_DEF(MoleculeSubstructureMatcher::MarkushContext); MoleculeSubstructureMatcher::MarkushContext::MarkushContext (QueryMolecule &query_, BaseMolecule &target_) : CP_INIT, TL_CP_GET(query), TL_CP_GET(query_marking), TL_CP_GET(sites), depth(0) { int i; query.clone(query_, 0, 0); sites.clear(); for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) if (query.isRSite(i)) sites.push(i); query_marking.clear_resize(query.vertexEnd()); for (i = query.vertexBegin(); i < query.vertexEnd(); i = query.vertexNext(i)) query_marking[i] = -1; } IMPL_ERROR(MoleculeSubstructureMatcher, "molecule substructure matcher"); CP_DEF(MoleculeSubstructureMatcher); MoleculeSubstructureMatcher::MoleculeSubstructureMatcher (BaseMolecule &target) : _target(target), CP_INIT, TL_CP_GET(_3d_constrained_atoms), TL_CP_GET(_unfolded_target_h), TL_CP_GET(_used_target_h) { vertex_equivalence_handler = NULL; use_aromaticity_matcher = true; use_pi_systems_matcher = false; _query = 0; match_3d = 0; rms_threshold = 0; highlight = false; find_all_embeddings = false; find_unique_embeddings = false; find_unique_by_edges = false; save_for_iteration = false; disable_folding_query_h = false; not_ignore_first_atom = false; cb_embedding = 0; cb_embedding_context = 0; fmcache = 0; disable_unfolding_implicit_h = false; restore_unfolded_h = true; _h_unfold = false; _query_nei_counters = 0; _target_nei_counters = 0; _used_target_h.clear_resize(target.vertexEnd()); // won't ignore target hydrogens because query can contain // 3d features, hydrogen isotopes, etc. } MoleculeSubstructureMatcher::~MoleculeSubstructureMatcher () { } bool MoleculeSubstructureMatcher::_shouldUnfoldTargetHydrogens_A (QueryMolecule::Atom *atom, bool is_fragment, bool find_all_embeddings) { if (atom->type == QueryMolecule::ATOM_FRAGMENT) { if (_shouldUnfoldTargetHydrogens(atom->fragment.ref(), true, find_all_embeddings)) return true; } else if (atom->type == QueryMolecule::OP_AND || atom->type == QueryMolecule::OP_OR || atom->type == QueryMolecule::OP_NOT) { int i; for (i = 0; i < atom->children.size(); i++) if (_shouldUnfoldTargetHydrogens_A((QueryMolecule::Atom *)atom->children[i], is_fragment, find_all_embeddings)) return true; } return false; } bool MoleculeSubstructureMatcher::shouldUnfoldTargetHydrogens (QueryMolecule &query, bool disable_folding_query_h) { return _shouldUnfoldTargetHydrogens(query, false, disable_folding_query_h); } bool MoleculeSubstructureMatcher::_shouldUnfoldTargetHydrogens (QueryMolecule &query, bool is_fragment, bool disable_folding_query_h) { int i, j; for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) { // skip R-atoms if (query.isRSite(i)) continue; if (query.possibleAtomNumberAndIsotope(i, ELEM_H, 0)) { const Vertex &vertex = query.getVertex(i); // Degree 2 or higher => definilely not a hydrogen if (vertex.degree() > 1) continue; // Can be lone hydrogen? if (vertex.degree() == 0) return true; // degree is 1 at this point int edge_idx = vertex.neiEdge(vertex.neiBegin()); // is it is double or triple bond => not hydrogen if (query.getBondOrder(edge_idx) > 1) continue; // ring bond? if (query.getBondTopology(edge_idx) == TOPOLOGY_RING) continue; // can be something other than hydrogen? if (query.getAtomNumber(i) == -1) return true; if (is_fragment && i == query.vertexBegin()) // If first atom in a fragment is hydrogen then hydrogens should // be unfolded because of the matching logic: when fragment will be // matched this first hydrogen should match some atom. // If hydrogens is not be unfolded in this case then // [$([#1][N])]C will not match NC. return true; // If we need to find all embeddings then query hydrogens cannot be ignored: // For example, if we are searching number of matcher for N-[#1] in N then // it should 3 instead of 1 if (disable_folding_query_h) return true; // Check if hydrogen forms a cis-trans bond or stereocenter int nei_vertex_idx = vertex.neiVertex(vertex.neiBegin()); if (query.stereocenters.exists(nei_vertex_idx)) return true; // For example for this query hydrogens should be unfolded: [H]\\C=C/C const Vertex &nei_vertex = query.getVertex(nei_vertex_idx); for (int nei = nei_vertex.neiBegin(); nei != nei_vertex.neiEnd(); nei = nei_vertex.neiNext(nei)) { int edge = nei_vertex.neiEdge(nei); if (query.cis_trans.getParity(edge) != 0) return true; } } if (_shouldUnfoldTargetHydrogens_A(&query.getAtom(i), is_fragment, disable_folding_query_h)) return true; } MoleculeRGroups &rgroups = query.rgroups; int n_rgroups = rgroups.getRGroupCount(); for (i = 1; i <= n_rgroups; i++) { PtrPool<BaseMolecule> &frags = rgroups.getRGroup(i).fragments; for (j = frags.begin(); j != frags.end(); j = frags.next(j)) if (_shouldUnfoldTargetHydrogens(frags[j]->asQueryMolecule(), is_fragment, disable_folding_query_h)) return true; } return false; } void MoleculeSubstructureMatcher::setQuery (QueryMolecule &query) { int i; if (query.rgroups.getRGroupCount() > 0) { _markush.reset(new MarkushContext(query, _target)); _query = &_markush->query; } else { _markush.reset(0); _query = &query; } QS_DEF(Array<int>, ignored); ignored.clear_resize(_query->vertexEnd()); if (!disable_folding_query_h) // If hydrogens are folded then the number of the all matchers is different markIgnoredQueryHydrogens(*_query, ignored.ptr(), 0, 1); else ignored.zerofill(); if (not_ignore_first_atom) ignored[_query->vertexBegin()] = 0; _3d_constrained_atoms.clear_resize(_query->vertexEnd()); _3d_constrained_atoms.zerofill(); { Molecule3dConstraintsChecker checker(query.spatial_constraints); checker.markUsedAtoms(_3d_constrained_atoms.ptr(), 1); } if (!disable_unfolding_implicit_h && shouldUnfoldTargetHydrogens(*_query, disable_folding_query_h) && !_target.isQueryMolecule()) { _h_unfold = true; } else _h_unfold = false; if (_ee.get() != 0) _ee.free(); _ee.create(_target); _ee->cb_match_vertex = _matchAtoms; _ee->cb_match_edge = _matchBonds; _ee->cb_vertex_remove = _removeAtom; _ee->cb_edge_add = _addBond; _ee->cb_embedding = _embedding; _ee->userdata = this; _ee->setSubgraph(*_query); for (i = _query->vertexBegin(); i != _query->vertexEnd(); i = _query->vertexNext(i)) { if ((ignored[i] && !_3d_constrained_atoms[i]) || _query->isRSite(i)) _ee->ignoreSubgraphVertex(i); } _embeddings_storage.free(); } QueryMolecule & MoleculeSubstructureMatcher::getQuery () { if (_query == 0) throw Error("query not set"); return *_query; } void MoleculeSubstructureMatcher::setNeiCounters ( const MoleculeAtomNeighbourhoodCounters *query_counters, const MoleculeAtomNeighbourhoodCounters *target_counters) { _query_nei_counters = query_counters; _target_nei_counters = target_counters; } bool MoleculeSubstructureMatcher::find () { if (_query == 0) throw Error("no query"); if (match_3d != 0 && !_query->have_xyz) throw Error("cannot do 3D match without XYZ in the query"); if (match_3d != 0 && !_target.have_xyz) return false; if (_h_unfold) { _target.asMolecule().unfoldHydrogens(&_unfolded_target_h, -1, true); _ee->validate(); } if (_canUseEquivalenceHeuristic(*_query)) _ee->setEquivalenceHandler(vertex_equivalence_handler); else _ee->setEquivalenceHandler(NULL); _used_target_h.zerofill(); if (use_aromaticity_matcher && AromaticityMatcher::isNecessary(*_query)) _am.create(*_query, _target, arom_options); else _am.free(); if (use_pi_systems_matcher && !_target.isQueryMolecule()) _pi_systems_matcher.create(_target.asMolecule()); else _pi_systems_matcher.free(); _3d_constraints_checker.recreate(_query->spatial_constraints); _createEmbeddingsStorage(); int result = _ee->process(); if (_h_unfold && restore_unfolded_h) _removeUnfoldedHydrogens(); if (!find_all_embeddings) return result == 0; else { if (_embeddings_storage.get() == 0) return false; return !_embeddings_storage->isEmpty(); } } void MoleculeSubstructureMatcher::_createEmbeddingsStorage () { _embeddings_storage.create(); _embeddings_storage->unique_by_edges = find_unique_by_edges; _embeddings_storage->save_edges = save_for_iteration; _embeddings_storage->save_mapping = save_for_iteration; _embeddings_storage->check_uniquencess = find_unique_embeddings; } void MoleculeSubstructureMatcher::_removeUnfoldedHydrogens () { QS_DEF(Array<int>, atoms_to_remove); atoms_to_remove.clear(); for (int i = 0; i < _unfolded_target_h.size(); i++) if (_unfolded_target_h[i]) atoms_to_remove.push(i); if (atoms_to_remove.size() > 0) _target.removeAtoms(atoms_to_remove); } bool MoleculeSubstructureMatcher::findNext () { if (_h_unfold) _target.asMolecule().unfoldHydrogens(&_unfolded_target_h, -1, true); bool found = _ee->processNext(); if (_h_unfold && restore_unfolded_h) _removeUnfoldedHydrogens(); return found; } bool MoleculeSubstructureMatcher::matchQueryAtom (QueryMolecule::Atom *query, BaseMolecule &target, int super_idx, FragmentMatchCache *fmcache, dword flags) { int i; switch (query->type) { case QueryMolecule::OP_NONE: return true; case QueryMolecule::OP_AND: for (i = 0; i < query->children.size(); i++) if (!matchQueryAtom(query->child(i), target, super_idx, fmcache, flags)) return false; return true; case QueryMolecule::OP_OR: for (i = 0; i < query->children.size(); i++) if (matchQueryAtom(query->child(i), target, super_idx, fmcache, flags)) return true; return false; case QueryMolecule::OP_NOT: return !matchQueryAtom(query->child(0), target, super_idx, fmcache, flags ^ MATCH_DISABLED_AS_TRUE); case QueryMolecule::ATOM_NUMBER: return query->valueWithinRange(target.getAtomNumber(super_idx)); case QueryMolecule::ATOM_PSEUDO: return target.isPseudoAtom(super_idx) && strcmp(query->alias.ptr(), target.getPseudoAtom(super_idx)) == 0; case QueryMolecule::ATOM_TEMPLATE: return target.isTemplateAtom(super_idx) && strcmp(query->alias.ptr(), target.getTemplateAtom(super_idx)) == 0; case QueryMolecule::ATOM_RSITE: return true; case QueryMolecule::ATOM_ISOTOPE: return query->valueWithinRange(target.getAtomIsotope(super_idx)); case QueryMolecule::ATOM_CHARGE: { if (flags & MATCH_ATOM_CHARGE) return query->valueWithinRange(target.getAtomCharge(super_idx)); return (flags & MATCH_DISABLED_AS_TRUE) != 0; } case QueryMolecule::ATOM_RADICAL: { if (target.isPseudoAtom(super_idx) || target.isRSite(super_idx)) return false; int radical = target.getAtomRadical_NoThrow(super_idx, -1); if (radical == -1) return false; return query->valueWithinRange(radical); } case QueryMolecule::ATOM_VALENCE: { if (flags & MATCH_ATOM_VALENCE) { if (target.isPseudoAtom(super_idx) || target.isRSite(super_idx)) return false; int valence = target.getAtomValence_NoThrow(super_idx, -1); if (valence == -1) return false; return query->valueWithinRange(valence); } return (flags & MATCH_DISABLED_AS_TRUE) != 0; } case QueryMolecule::ATOM_CONNECTIVITY: { int conn = target.getVertex(super_idx).degree(); if (!target.isPseudoAtom(super_idx) && !target.isRSite(super_idx)) conn += target.asMolecule().getImplicitH_NoThrow(super_idx, 0); return query->valueWithinRange(conn); } case QueryMolecule::ATOM_TOTAL_BOND_ORDER: { // TODO: target.isPseudoAtom(super_idx) || target.isRSite(super_idx) int conn = target.asMolecule().getAtomConnectivity_NoThrow(super_idx, -1); if (conn == -1) return false; return query->valueWithinRange(conn); } case QueryMolecule::ATOM_TOTAL_H: { if (target.isPseudoAtom(super_idx) || target.isRSite(super_idx) || target.isTemplateAtom(super_idx)) return false; return query->valueWithinRange(target.getAtomTotalH(super_idx)); } case QueryMolecule::ATOM_SUBSTITUENTS: case QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN: return query->valueWithinRange(target.getAtomSubstCount(super_idx)); case QueryMolecule::ATOM_SSSR_RINGS: return query->valueWithinRange(target.vertexCountSSSR(super_idx)); case QueryMolecule::ATOM_SMALLEST_RING_SIZE: return query->valueWithinRange(target.vertexSmallestRingSize(super_idx)); case QueryMolecule::ATOM_RING_BONDS: case QueryMolecule::ATOM_RING_BONDS_AS_DRAWN: return query->valueWithinRange(target.getAtomRingBondsCount(super_idx)); case QueryMolecule::ATOM_UNSATURATION: return !target.isSaturatedAtom(super_idx); case QueryMolecule::ATOM_FRAGMENT: { if (fmcache == 0) throw Error("unexpected 'fragment' constraint"); QueryMolecule *fragment = query->fragment.get(); const char *smarts = fragment->fragment_smarts.ptr(); if (fragment->vertexCount() == 0) throw Error("empty fragment"); if (smarts != 0 && strlen(smarts) > 0) { fmcache->expand(super_idx + 1); int *value = fmcache->at(super_idx).at2(smarts); if (value != 0) return *value != 0; } MoleculeSubstructureMatcher matcher(target.asMolecule()); matcher.not_ignore_first_atom = true; matcher.setQuery(*fragment); matcher.fmcache = fmcache; bool result = matcher.fix(fragment->vertexBegin(), super_idx); if (result) result = matcher.find(); if (smarts != 0 && strlen(smarts) > 0) { fmcache->expand(super_idx + 1); fmcache->at(super_idx).insert(smarts, result ? 1 : 0); } return result; } case QueryMolecule::ATOM_AROMATICITY: return query->valueWithinRange(target.getAtomAromaticity(super_idx)); case QueryMolecule::HIGHLIGHTING: return query->valueWithinRange((int)target.isAtomHighlighted(super_idx)); default: throw Error("bad query atom type: %d", query->type); } } bool MoleculeSubstructureMatcher::matchQueryBond (QueryMolecule::Bond *query, BaseMolecule &target, int sub_idx, int super_idx, AromaticityMatcher *am, dword flags) { int i; // MR TODO: match topology. Query bond in ring cannot match // target bond that is not in ring. But R-groups should be // handled carefully switch (query->type) { case QueryMolecule::OP_NONE: return true; case QueryMolecule::OP_AND: for (i = 0; i < query->children.size(); i++) if (!matchQueryBond(query->child(i), target, sub_idx, super_idx, am, flags)) return false; return true; case QueryMolecule::OP_OR: for (i = 0; i < query->children.size(); i++) if (matchQueryBond(query->child(i), target, sub_idx, super_idx, am, flags)) return true; return false; case QueryMolecule::OP_NOT: return !matchQueryBond(query->child(0), target, sub_idx, super_idx, am, flags ^ MATCH_DISABLED_AS_TRUE); case QueryMolecule::BOND_ORDER: { if (flags & MATCH_BOND_TYPE) { if (am != 0) { if (target.getBondOrder(super_idx) == BOND_AROMATIC) return am->canFixQueryBond(sub_idx, true); else { if (!am->canFixQueryBond(sub_idx, false)) return false; } } return target.possibleBondOrder(super_idx, query->value); } return (flags & MATCH_DISABLED_AS_TRUE) != 0; } case QueryMolecule::BOND_TOPOLOGY: return target.getEdgeTopology(super_idx) == query->value; case QueryMolecule::HIGHLIGHTING: return query->value == (int)target.isAtomHighlighted(super_idx); default: throw Error("bad query bond type: %d", query->type); } } bool MoleculeSubstructureMatcher::_matchAtoms(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { MoleculeSubstructureMatcher *self = (MoleculeSubstructureMatcher *)userdata; if (self->_h_unfold && (&subgraph == (Graph *)self->_query)) { if (sub_idx < self->_3d_constrained_atoms.size() && self->_3d_constrained_atoms[sub_idx]) // we can't check 3D constraint on unfolded atom, because it has no actual position if (self->_unfolded_target_h[super_idx]) return false; } dword match_atoms_flags = 0xFFFFFFFF; // If target atom belongs to a pi-system then its charge // should be checked after embedding if (self->_pi_systems_matcher.get()) { if (self->_pi_systems_matcher->isAtomInPiSystem(super_idx)) match_atoms_flags &= ~(MATCH_ATOM_CHARGE | MATCH_ATOM_VALENCE); } QueryMolecule &query = (QueryMolecule &)subgraph; BaseMolecule &target = (BaseMolecule &)supergraph; if (!target.isPseudoAtom(super_idx) && !target.isRSite(super_idx) && !target.isTemplateAtom(super_idx)) { int q_min_h; int t_max_h; try { q_min_h = query.getAtomMinH(sub_idx); } catch (Exception e) { q_min_h = 0; } try { t_max_h = target.getAtomMaxH(super_idx); } catch (Exception e) { t_max_h = 0; } if (q_min_h > 0 && t_max_h >= 0) if (q_min_h > t_max_h) return false; } if (query.components.size() > sub_idx && query.components[sub_idx] > 0) { int i; for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) { if (i == sub_idx) continue; if (core_sub[i] < 0) continue; if (query.components.size() <= i || query.components[i] <= 0) continue; if (query.components[i] == query.components[sub_idx] && target.vertexComponent(core_sub[i]) != target.vertexComponent(super_idx)) return false; if (query.components[i] != query.components[sub_idx] && target.vertexComponent(core_sub[i]) == target.vertexComponent(super_idx)) return false; } } QueryMolecule::Atom &sub_atom = query.getAtom(sub_idx); if (!matchQueryAtom(&sub_atom, target, super_idx, self->fmcache, match_atoms_flags)) return false; if (query.stereocenters.getType(sub_idx) > target.stereocenters.getType(super_idx)) return false; if (self->_query_nei_counters != 0 && self->_target_nei_counters != 0) { bool use_bond_types = (self->_pi_systems_matcher.get() == 0); bool ret = self->_query_nei_counters->testSubstructure( *self->_target_nei_counters, sub_idx, super_idx, use_bond_types); if (!ret) return false; } if (self->match_3d == AFFINE) { QS_DEF(Array<int>, core_sub_full); core_sub_full.copy(core_sub, subgraph.vertexEnd()); core_sub_full[sub_idx] = super_idx; GraphAffineMatcher matcher(subgraph, supergraph, core_sub_full.ptr()); matcher.cb_get_xyz = getAtomPos; int total_fixed = query.vertexCount(); if (query.fixed_atoms.size() > 0) { matcher.fixed_vertices = &query.fixed_atoms; total_fixed = query.fixed_atoms.size(); } if (!matcher.match(self->rms_threshold * sqrt((float)total_fixed))) return false; } return true; } bool MoleculeSubstructureMatcher::_matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { MoleculeSubstructureMatcher *self = (MoleculeSubstructureMatcher *)userdata; dword flags = 0xFFFFFFFF; // If target bond belongs to a pi-system then it // should be checked after embedding if (self->_pi_systems_matcher.get()) { if (self->_pi_systems_matcher->isBondInPiSystem(super_idx)) flags &= ~MATCH_BOND_TYPE; } QueryMolecule &query = (QueryMolecule &)subgraph; BaseMolecule &target = (BaseMolecule &)supergraph; QueryMolecule::Bond &sub_bond = query.getBond(sub_idx); if (!matchQueryBond(&sub_bond, target, sub_idx, super_idx, self->_am.get(), flags)) return false; return true; } void MoleculeSubstructureMatcher::removeAtom (Graph &subgraph, int sub_idx, AromaticityMatcher *am) { if (am == 0) return; am->unfixNeighbourQueryBond(sub_idx); } void MoleculeSubstructureMatcher::_removeAtom (Graph &subgraph, int sub_idx, void *userdata) { MoleculeSubstructureMatcher *self = (MoleculeSubstructureMatcher *)userdata; removeAtom(subgraph, sub_idx, self->_am.get()); } void MoleculeSubstructureMatcher::addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, AromaticityMatcher *am) { if (am == 0) return; BaseMolecule &target = (BaseMolecule &)supergraph; am->fixQueryBond(sub_idx, target.getBondOrder(super_idx) == BOND_AROMATIC); } void MoleculeSubstructureMatcher::_addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { MoleculeSubstructureMatcher *self = (MoleculeSubstructureMatcher *)userdata; addBond(subgraph, supergraph, sub_idx, super_idx, self->_am.get()); } int MoleculeSubstructureMatcher::_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { MoleculeSubstructureMatcher *self = (MoleculeSubstructureMatcher *)userdata; if (self->_markush.get() == 0 || self->_markush->sites.size() == self->_markush->depth) return self->_embedding_common(core_sub, core_super); else return self->_embedding_markush(core_sub, core_super); } int MoleculeSubstructureMatcher::_embedding_common (int *core_sub, int *core_super) { QueryMolecule &query = *_query; if (!MoleculeStereocenters::checkSub(query.stereocenters, _target.stereocenters, core_sub, false)) return 1; if (!MoleculeCisTrans::checkSub(query, _target, core_sub)) return 1; if (!MoleculeAlleneStereo::checkSub(query, _target, core_sub)) return 1; if (!_3d_constraints_checker->check(_target, core_sub)) return 1; // Check possible aromatic configuration if (_am.get() != 0) { if (!_am->match(core_sub, core_super)) return 1; } // Check possible pi-systems configurations if (_pi_systems_matcher.get() != 0) { if (!_pi_systems_matcher->checkEmbedding(query, core_sub)) return 1; } // affine transformation match /*if (match_3d == AFFINE) { GraphAffineMatcher matcher(query, _target, core_sub); matcher.cb_get_xyz = getAtomPos; if (query.fixed_atoms.size() > 0) matcher.fixed_vertices = &query.fixed_atoms; if (!matcher.match(v1, v2)) return 1; }*/ // chemical conformation match if (match_3d == CONFORMATION) { QS_DEF(Array<int>, mapping); GraphDecomposer decomposer(query); Filter filter(core_sub, Filter::MORE, -1); decomposer.decompose(&filter); int i, comp; for (comp = 0; comp < decomposer.getComponentsCount(); comp++) { mapping.clear_resize(query.vertexEnd()); mapping.fffill(); for (i = query.vertexBegin(); i != query.vertexEnd(); i = query.vertexNext(i)) { if (decomposer.getComponent(i) == comp) mapping[i] = core_sub[i]; } EdgeRotationMatcher matcher(query, _target, mapping.ptr()); matcher.cb_get_xyz = getAtomPos; matcher.cb_can_rotate = _isSingleBond; matcher.equalize_edges = true; if (!matcher.match(rms_threshold, 0.1f)) return 1; } } if (_markush.get() != 0) if (!_checkRGroupConditions()) return 1; if (find_unique_embeddings || save_for_iteration) { if (!_embeddings_storage->addEmbedding(_target, query, core_sub)) // This match has already been handled return 1; } if (highlight) _target.highlightSubmolecule(query, core_sub, true); if (cb_embedding != 0) if (!cb_embedding(query, _target, core_sub, core_super, cb_embedding_context)) return 0; if (find_all_embeddings) return 1; return 0; } int MoleculeSubstructureMatcher::_embedding_markush (int *core_sub, int *core_super) { QueryMolecule &g1 = *_query; MarkushContext &context = *_markush.get(); // Current site atom index int old_site_idx = context.sites[context.depth]; bool two_att_points; // Check number of attachment points for current site int site_degree = g1.getVertex(old_site_idx).degree(); switch (site_degree) { case 1: two_att_points = false; break; case 2: two_att_points = true; break; default: throw Error("unsupported number of attachment points (%d)", site_degree); } // Save number of embeddings to check if new embedding appeared int embeddings_count = _embeddings_storage->count(); bool find_all_embeddings_saved = find_all_embeddings; if (save_for_iteration) find_all_embeddings = true; // For all possible rgroups at current site // do not do this: // const Array<int> &rg_list = g1.getRGroups()->getSiteRGroups(old_site_idx); QS_DEF(Array<int>, old_site_rgroups); g1.getAllowedRGroups(old_site_idx, old_site_rgroups); bool all_have_rest_h = true; for (int rg_idx = 0; rg_idx < old_site_rgroups.size(); rg_idx++) { RGroup &rgroup = g1.rgroups.getRGroup(old_site_rgroups[rg_idx]); PtrPool<BaseMolecule> &frags = rgroup.fragments; all_have_rest_h &= (rgroup.rest_h > 0); // For all rgroup fragments for (int fr_idx = frags.begin(); fr_idx != frags.end(); fr_idx = frags.next(fr_idx)) { QueryMolecule &fragment = frags[fr_idx]->asQueryMolecule(); if (fragment.attachmentPointCount() > 2) throw Error("more than two attachment points"); if (site_degree != fragment.attachmentPointCount()) throw Error("number of attachment points must be equal to R-group site degree"); int att_idx1; int att_idx2; int i, j; for (i = 0; (att_idx1 = fragment.getAttachmentPoint(1, i)) != -1; i++) { if (two_att_points) { for (j = 0; (att_idx2 = fragment.getAttachmentPoint(2, j)) != -1; j++) if (!_attachRGroupAndContinue(core_sub, core_super, &fragment, true, att_idx1, att_idx2, old_site_rgroups[rg_idx], false)) return 0; } else if (!_attachRGroupAndContinue(core_sub, core_super, &fragment, false, att_idx1, -1, old_site_rgroups[rg_idx], false)) return 0; } } } if (!two_att_points && !_attachRGroupAndContinue(core_sub, core_super, 0, false, -1, -1, -1, all_have_rest_h)) return 0; find_all_embeddings = find_all_embeddings_saved; if (!find_all_embeddings && save_for_iteration) // If there was found an embedding then return false (stop searching) return _embeddings_storage->count() == embeddings_count; return 1; } bool MoleculeSubstructureMatcher::_attachRGroupAndContinue (int *core1, int *core2, QueryMolecule *fragment, bool two_attachment_points, int att_idx1, int att_idx2, int rgroup_idx, bool rest_h) { MarkushContext &context = *_markush.get(); QS_DEF(Array<int>, fr_mapping); int i; int n_sites = 0; bool ok = true; int hydrogen_attached_to = -1; // Site atom index in new molecule int cur_site = context.sites[context.depth]; int src_att_idx1 = -1, src_att_idx2 = -1; AutoPtr<QueryMolecule::Bond> rg_qbond1; // Parameters for stereocenter restoration bool stereo_was_saved = false; int saved_stereo_type, saved_stereo_group, saved_stereo_pyramid[4]; // If some rgroup must be attached if (rgroup_idx != -1) { // Add rgroup fragment to new molecule context.query.mergeWithMolecule(*fragment, &fr_mapping); // Site atom vertex in new molecule const Vertex &cur_site_vertex = context.query.getVertex(cur_site); // First and second neighbors indices and neighboring atoms indices int nei_idx1 = cur_site_vertex.neiBegin(); int nei_idx2 = cur_site_vertex.neiNext(nei_idx1); int nei_atom_idx1 = context.query.getRSiteAttachmentPointByOrder(cur_site, 0); int nei_atom_idx2 = context.query.getRSiteAttachmentPointByOrder(cur_site, 1); if (two_attachment_points) { if (cur_site_vertex.degree() != 2) throw Error("RGroup atom has wrong number of attachment points"); if (cur_site_vertex.neiVertex(nei_idx1) != nei_atom_idx1) { // Swap attachment neighbors according to attachment orders (first and second) __swap(nei_idx1, nei_idx2, i); } if (cur_site_vertex.neiVertex(nei_idx1) != nei_atom_idx1 || cur_site_vertex.neiVertex(nei_idx2) != nei_atom_idx2) throw Error("Internal or rgroup query file error!"); } else { if (cur_site_vertex.degree() != 1) throw Error("RGroup atom has wrong number of attachment points"); if (att_idx1 == -1) { // Second attachment index of fragment must be attached to second-ordered neighbor if (cur_site_vertex.neiVertex(nei_idx1) != nei_atom_idx2) ok = false; att_idx1 = att_idx2; } else if (cur_site_vertex.neiVertex(nei_idx1) != nei_atom_idx1) // First attachment index of fragment must be attached to first-ordered neighbor ok = false; } if (ok) { // First attachment bond of current rgroup site src_att_idx1 = cur_site_vertex.neiVertex(nei_idx1); context.query.flipBond(src_att_idx1, cur_site, fr_mapping[att_idx1]); if (two_attachment_points) { // Second attachment bond of current rgroup site src_att_idx2 = cur_site_vertex.neiVertex(nei_idx2); context.query.flipBond(src_att_idx2, cur_site, fr_mapping[att_idx2]); } } } else { // no rgroup (rgroup = implicit hydrogen) // This might happen only for rgroup with one attachment point const Vertex &cur_site_vertex = context.query.getVertex(cur_site); if (cur_site_vertex.degree() != 1) throw Error("RGroup atom has wrong number of attachment points"); int nei_idx1 = cur_site_vertex.neiBegin(); src_att_idx1 = cur_site_vertex.neiVertex(nei_idx1); int target_idx = core1[src_att_idx1]; if (rest_h) { if (_target.getAtomTotalH(target_idx) - _used_target_h[target_idx] <= 0) return true; _used_target_h[target_idx]++; } // Remove edge to site rg_qbond1.reset(context.query.releaseBond(cur_site_vertex.neiEdge(nei_idx1))); // Save stereocenter before bond removing because bond // removing can cause stereocenter destruction MoleculeStereocenters &qstereo = context.query.stereocenters; if (qstereo.exists(src_att_idx1)) { qstereo.get(src_att_idx1, saved_stereo_type, saved_stereo_group, saved_stereo_pyramid); stereo_was_saved = true; } context.query.removeBond(cur_site_vertex.neiEdge(nei_idx1)); } // From MR: Why create new EmbeddingEnumerator? Is it possible to use // current without recreating all structures? Just attach and continue... // Init new embedding enumerator context EmbeddingEnumerator ee(_target); ee.cb_match_vertex = _matchAtoms; ee.cb_match_edge = _matchBonds; ee.cb_vertex_remove = _removeAtom; ee.cb_edge_add = _addBond; ee.cb_embedding = _embedding; ee.setSubgraph(context.query); ee.userdata = this; if (rgroup_idx != -1) { context.query_marking.expand(context.query.vertexEnd()); // Check if there are explicit hydrogens or new sites attached- ignore them in further matching // Mark new vertices and add new sites if any QS_DEF(Array<int>, ignored); ignored.clear_resize(fragment->vertexEnd()); markIgnoredQueryHydrogens(*fragment, ignored.ptr(), 0, 1); if (hydrogen_attached_to != -1) { int idx = fragment->vertexBegin(); context.query_marking[fr_mapping[idx]] = context.depth; if (fragment->getAtomNumber(idx) == ELEM_H && fragment->possibleAtomIsotope(idx, 0)) ee.ignoreSubgraphVertex(fr_mapping[idx]); } else { for (i = fragment->vertexBegin(); i < fragment->vertexEnd(); i = fragment->vertexNext(i)) { context.query_marking[fr_mapping[i]] = context.depth; if (ignored[i]) ee.ignoreSubgraphVertex(fr_mapping[i]); else if (fragment->isRSite(i)) { context.sites.push(fr_mapping[i]); n_sites++; ee.ignoreSubgraphVertex(fr_mapping[i]); } } } if (_am.get() != 0) _am->validateQuery(); } // Copy ingnored query vertices from previous state for (i = context.query.vertexBegin(); i < context.query.vertexEnd(); i = context.query.vertexNext(i)) { if (context.query_marking[i] == context.depth) continue; if (core1[i] == EmbeddingEnumerator::IGNORE) ee.ignoreSubgraphVertex(i); } // Copy ignored target vertices for (i = _target.vertexBegin(); i < _target.vertexEnd(); i = _target.vertexNext(i)) if (core2[i] == EmbeddingEnumerator::IGNORE) ee.ignoreSupergraphVertex(i); // Copy partial mapping from previous state for (i = context.query.vertexBegin(); i < context.query.vertexEnd(); i = context.query.vertexNext(i)) { if (context.query_marking[i] == context.depth) continue; if (core1[i] >= 0 && !ee.fix(i, core1[i])) ok = false; } // Store attached rgroup index at current site context.sites[context.depth] = rgroup_idx; context.depth++; // Continue in new state // Call embedding enumerator recursively if (ok && !ee.process()) return false; // Restore state context.depth--; context.sites[context.depth] = cur_site; if (rgroup_idx != -1) { context.query.flipBond(src_att_idx1, fr_mapping[att_idx1], cur_site); if (src_att_idx2 != -1) context.query.flipBond(src_att_idx2, fr_mapping[att_idx2], cur_site); } else // no rgroup { // Restore edge to site context.query.addBond(src_att_idx1, cur_site, rg_qbond1.release()); // Restore used hydrogen if (rest_h) _used_target_h[core1[src_att_idx1]]--; // Restore stereocenter if (stereo_was_saved) { MoleculeStereocenters &qstereo = context.query.stereocenters; if (qstereo.exists(src_att_idx1)) qstereo.remove(src_att_idx1); qstereo.add(src_att_idx1, saved_stereo_type, saved_stereo_group, saved_stereo_pyramid); } } if (rgroup_idx != -1) { Filter remove_filter(context.query_marking.ptr(), Filter::EQ, context.depth); context.query.removeAtoms(remove_filter); while (n_sites-- > 0) context.sites.pop(); if (_am.get() != 0) _am->validateQuery(); } return true; } bool MoleculeSubstructureMatcher::_checkRGroupConditions () { QS_DEF(Array<int>, occurrences); QS_DEF(Array<int>, conditions); QS_DEF(Array<int>, nontop_rgroups); bool resth_satisfied; int i, k; MarkushContext &context = *_markush.get(); MoleculeRGroups &rgroups = _query->rgroups; int n_rgroups = rgroups.getRGroupCount(); occurrences.clear_resize(n_rgroups + 1); occurrences.zerofill(); conditions.clear_resize(n_rgroups + 1); conditions.zerofill(); nontop_rgroups.clear_resize(n_rgroups + 1); nontop_rgroups.zerofill(); // Count occurrences of each rgroup for (i = 0; i < context.sites.size(); i++) if (context.sites[i] >= 0) occurrences[context.sites[i]]++; for (int i = 1; i <= n_rgroups; i++) { RGroup & rgroup = rgroups.getRGroup(i); if (rgroup.fragments.size() == 0) { conditions[i] = -1; continue; } if (rgroup.if_then > 0) nontop_rgroups[rgroup.if_then] = 1; // Check occurrence if (!rgroup.occurrenceSatisfied(occurrences[i])) continue; resth_satisfied = true; // Check RestH if (rgroup.rest_h == 1) { int site_idx, j = 0; for (site_idx = _query->vertexBegin(); site_idx != _query->vertexEnd(); site_idx = _query->vertexNext(site_idx)) { if (!_query->isRSite(site_idx)) continue; QS_DEF(Array<int>, rg_list); _query->getAllowedRGroups(site_idx, rg_list); for (k = 0; k < rg_list.size() && resth_satisfied; k++) if (rg_list[k] == i) { if (context.sites[j] != i) resth_satisfied = false; break; } if (!resth_satisfied) break; j++; } if (!resth_satisfied) continue; } // Without if/then conditions[i] = 1; } // Check if/then relations for (int i = 1; i <= n_rgroups; i++) { if (nontop_rgroups[i] == 1 || conditions[i] == -1) continue; const RGroup &rgroup = rgroups.getRGroup(i); const RGroup *iter = &rgroup; int i_iter = i; int counter = 0; do { if (conditions[i_iter] != 1 || counter >= n_rgroups) return false; if ((i_iter = iter->if_then) > 0) iter = &rgroups.getRGroup(i_iter); counter++; } while (i_iter > 0); } return true; } const int * MoleculeSubstructureMatcher::getQueryMapping () { return _ee->getSubgraphMapping(); } const int * MoleculeSubstructureMatcher::getTargetMapping () { return _ee->getSupergraphMapping(); } void MoleculeSubstructureMatcher::ignoreQueryAtom (int idx) { _ee->ignoreSubgraphVertex(idx); } void MoleculeSubstructureMatcher::ignoreTargetAtom (int idx) { _ee->ignoreSupergraphVertex(idx); } bool MoleculeSubstructureMatcher::fix (int query_atom_idx, int target_atom_idx) { return _ee->fix(query_atom_idx, target_atom_idx); } void MoleculeSubstructureMatcher::markIgnoredHydrogens (BaseMolecule &mol, int *arr, int value_keep, int value_ignore) { int i; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) arr[i] = value_keep; for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.getAtomNumber(i) != ELEM_H) continue; if (!mol.possibleAtomIsotope(i, 0)) continue; if (mol.isQueryMolecule()) { // Check if atom has fragment constraint. // For example [$([#1][N])] should be ignored if (mol.asQueryMolecule().getAtom(i).hasConstraint(QueryMolecule::ATOM_FRAGMENT)) continue; } const Vertex &vertex = mol.getVertex(i); if (vertex.degree() == 1) { int nei_idx = vertex.neiVertex(vertex.neiBegin()); if (mol.getAtomNumber(nei_idx) == ELEM_H && mol.possibleAtomIsotope(nei_idx, 0)) continue; // do not ignore rare H-H fragment // Check if hydrogen forms a cis-trans bond or stereocenter int nei_vertex_idx = vertex.neiVertex(vertex.neiBegin()); if (mol.stereocenters.exists(nei_vertex_idx)) continue; // For example for this query hydrogens should be unfolded: [H]\\C=C/C const Vertex &nei_vertex = mol.getVertex(nei_vertex_idx); bool not_ignore = false; for (int nei = nei_vertex.neiBegin(); nei != nei_vertex.neiEnd(); nei = nei_vertex.neiNext(nei)) { int edge = nei_vertex.neiEdge(nei); if (mol.cis_trans.getParity(edge) != 0) { not_ignore = true; break; } } if (not_ignore) continue; arr[i] = value_ignore; } } } void MoleculeSubstructureMatcher::markIgnoredQueryHydrogens (QueryMolecule &mol, int *arr, int value_keep, int value_ignore) { markIgnoredHydrogens(mol, arr, value_keep, value_ignore); // keep hydrogens that are parts of 3d constraints { Molecule3dConstraintsChecker checker(mol.spatial_constraints); checker.markUsedAtoms(arr, value_keep); } } void MoleculeSubstructureMatcher::getAtomPos (Graph &graph, int vertex_idx, Vec3f &pos) { pos.copy(((BaseMolecule &)graph).getAtomXyz(vertex_idx)); } bool MoleculeSubstructureMatcher::_isSingleBond (Graph &graph, int edge_idx) { return ((BaseMolecule &)graph).getBondOrder(edge_idx) == BOND_SINGLE; } const GraphEmbeddingsStorage& MoleculeSubstructureMatcher::getEmbeddingsStorage () const { return _embeddings_storage.ref(); } bool MoleculeSubstructureMatcher::needCoords (int match_3d, QueryMolecule &query) { if (match_3d != 0) return true; if (query.spatial_constraints.haveConstraints()) return true; return false; } bool MoleculeSubstructureMatcher::_canUseEquivalenceHeuristic (QueryMolecule &query) { if (query.spatial_constraints.haveConstraints()) return false; if (query.stereocenters.size() > 0) return false; return true; } int MoleculeSubstructureMatcher::_compare_degree_asc (BaseMolecule &mol, int i1, int i2) { return mol.getVertex(i2).degree() - mol.getVertex(i1).degree(); } int MoleculeSubstructureMatcher::_compare_frequency_base (BaseMolecule &mol, int i1, int i2) { int label1 = mol.getAtomNumber(i1); int label2 = mol.getAtomNumber(i2); if (label1 == 0 && label2 != 0) return 1; if (label1 != 0 && label2 != 1) return -1; int is_hetero1 = (label1 != 0 && label1 != ELEM_C && label1 != ELEM_H); int is_hetero2 = (label2 != 0 && label2 != ELEM_C && label2 != ELEM_H); return is_hetero2 - is_hetero1; } int MoleculeSubstructureMatcher::_compare_frequency_asc (BaseMolecule &mol, int i1, int i2) { static int labels_by_freq[] = {ELEM_C, ELEM_H, ELEM_O, ELEM_N, ELEM_P, ELEM_F, ELEM_S, ELEM_Si, ELEM_Cl, ELEM_Br, ELEM_I, ELEM_At}; int label1 = mol.getAtomNumber(i1); int label2 = mol.getAtomNumber(i2); int idx1, idx2; for (idx1 = 0; idx1 < (int)NELEM(labels_by_freq); idx1++) if (label1 == labels_by_freq[idx1]) break; for (idx2 = 0; idx2 < (int)NELEM(labels_by_freq); idx2++) if (label2 == labels_by_freq[idx2]) break; return idx2 - idx1; } int MoleculeSubstructureMatcher::_compare_in_loop (BaseMolecule &mol, int i1, int i2) { const Vertex &v1 = mol.getVertex(i1); const Vertex &v2 = mol.getVertex(i2); int in_loop1 = 0; for (int nei = v1.neiBegin(); nei != v1.neiEnd(); nei = v1.neiNext(nei)) { int nei_edge = v1.neiEdge(nei); if (mol.getEdgeTopology(nei_edge) == TOPOLOGY_RING) { in_loop1 = 1; break; } } int in_loop2 = 0; for (int nei = v2.neiBegin(); nei != v2.neiEnd(); nei = v2.neiNext(nei)) { int nei_edge = v2.neiEdge(nei); if (mol.getEdgeTopology(nei_edge) == TOPOLOGY_RING) { in_loop2 = 1; break; } } return in_loop2 - in_loop1; } int MoleculeSubstructureMatcher::_compare (int &i1, int &i2, void *context) { BaseMolecule &mol = *(BaseMolecule *)context; bool is_pseudo1 = mol.isPseudoAtom(i1); bool is_pseudo2 = mol.isPseudoAtom(i2); if (is_pseudo1 && !is_pseudo2) return -1; if (!is_pseudo1 && is_pseudo2) return 1; if (is_pseudo1) return 0; // All pseudoatoms are the same for transposition for substructure bool is_template1 = mol.isTemplateAtom(i1); bool is_template2 = mol.isTemplateAtom(i2); if (is_template1 && !is_template2) return -1; if (!is_template1 && is_template2) return 1; if (is_template1) return 0; // All template atoms are the same for transposition for substructure (?) int res; res = _compare_frequency_base(mol, i1, i2); if (res != 0) return res; res = _compare_in_loop(mol, i1, i2); if (res != 0) return res; res = _compare_frequency_asc(mol, i1, i2); if (res != 0) return res; return _compare_degree_asc(mol, i1, i2); } void MoleculeSubstructureMatcher::makeTransposition (BaseMolecule &mol, Array<int> &transposition_out) { int i; transposition_out.clear(); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) transposition_out.push(i); transposition_out.qsort(_compare, &mol); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_chain.cpp����������������������������������������0000664�0000000�0000000�00000102013�12710376503�0024165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/embedding_enumerator.h" #include "molecule/molecule.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_tautomer.h" #include "molecule/molecule_tautomer_utils.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_exact_matcher.h" using namespace indigo; enum { BOND_ZEROED = 0 // Bond can appear during tautomerism }; TautomerChainFinder::TautomerChainFinder (TautomerSearchContext &context, int h_difference, int start_path_number, int n_chains) : _context(context), _path_length(0), _h_difference(h_difference), _is_zero_bond_present(!context.ring_chain), _path_number(start_path_number + 2), _n_chains(n_chains) { } TautomerChainFinder::TautomerChainFinder (TautomerChainFinder &other) : _context(other._context), _path_length(other._path_length + 1), _h_difference(other._h_difference * -1), _path_number(other._path_number), _start_idx1(other._start_idx1), _start_idx2(other._start_idx2), _n_chains(other._n_chains) { } TautomerChainFinder::~TautomerChainFinder () { } bool TautomerChainFinder::enumeratePaths () { int n1, n2, e1 = -1, e2 = -1; while (nextPair(n1, n2, e1, e2, e1, e2)) { int rc; bool zero_bond; int arom_bond2, bond_type2; if ((rc = isFeasiblePair(n1, n2, zero_bond, arom_bond2, bond_type2)) == 1) { TautomerChainFinder pe(*this); pe.addPair(n1, n2, zero_bond, arom_bond2, bond_type2); if (!pe.enumeratePaths()) return false; pe.restore(); } else if (rc == -1) { TautomerMatcher tm_state(_context, _path_number, _n_chains); tm_state.addPair(n1, n2, arom_bond2, bond_type2); if (!tm_state.findMatch()) return false; tm_state.restore(); } } return true; } bool TautomerChainFinder::nextPair (int &n1, int &n2, int &e1, int &e2, int prev_e1, int prev_e2) { const Vertex &vertex1 = _context.g1.getVertex(_prev_n1); const Vertex &vertex2 = _context.g2.getVertex(_prev_n2); if (_is_zero_bond_present) { if (prev_e1 == -1) e1 = vertex1.neiBegin(); if (prev_e2 == -1) e2 = vertex2.neiBegin(); else e2 = vertex2.neiNext(prev_e2); for (; e1 != vertex1.neiEnd(); e1 = vertex1.neiNext(e1)) { n1 = vertex1.neiVertex(e1); if (_context.core_1[n1] != EmbeddingEnumerator::UNMAPPED) continue; for (; e2 != vertex2.neiEnd(); e2 = vertex2.neiNext(e2)) { n2 = vertex2.neiVertex(e2); if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED) continue; if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2)) return true; } e2 = vertex2.neiBegin(); } } else if (_h_difference == 1) { if (prev_e1 == -1) e1 = _context.g1.vertexBegin(); if (prev_e2 == -1) e2 = vertex2.neiBegin(); else e2 = vertex2.neiNext(prev_e2); for (; e1 < _context.g1.vertexEnd(); e1 = _context.g1.vertexNext(e1)) { n1 = e1; if (_context.core_1[n1] != EmbeddingEnumerator::UNMAPPED) continue; for (; e2 != vertex2.neiEnd(); e2 = vertex2.neiNext(e2)) { n2 = vertex2.neiVertex(e2); if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED) continue; if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2)) return true; } e2 = vertex2.neiBegin(); } } else { if (prev_e1 == -1) e1 = vertex1.neiBegin(); if (prev_e2 == -1) e2 = _context.g2.vertexBegin(); else e2 = _context.g2.vertexNext(prev_e2); for (; e1 != vertex1.neiEnd(); e1 = vertex1.neiNext(e1)) { n1 = vertex1.neiVertex(e1); if (_context.core_1[n1] != EmbeddingEnumerator::UNMAPPED) continue; for (; e2 < _context.g2.vertexEnd(); e2 = _context.g2.vertexNext(e2)) { n2 = e2; if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED) continue; if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2)) return true; } e2 = _context.g2.vertexBegin(); } } return false; } int TautomerChainFinder::isFeasiblePair (int n1, int n2, bool &zero_bond, int &arom_bond_idx2, int &bond_type2) { if (_prev_n1 != -1 && _context.decomposer1.getComponent(_prev_n1) != _context.decomposer1.getComponent(n1)) return 0; if (_prev_n2 != -1 && _context.decomposer2.getComponent(_prev_n2) != _context.decomposer2.getComponent(n2)) return 0; int edge1 = _context.g1.findEdgeIndex(_prev_n1, n1); int edge2 = _context.g2.findEdgeIndex(_prev_n2, n2); int type1 = 0; int type2 = 0; zero_bond = _is_zero_bond_present; arom_bond_idx2 = -1; if (edge1 == -1) { zero_bond = true; } else type1 = _context.g1.getBondOrder(edge1); if (type1 == BOND_AROMATIC || type1 == BOND_TRIPLE) return 0; if (edge2 == -1) { zero_bond = true; } else if (_context.g2.getBondOrder(edge2) == -1 && _context.g2.possibleBondOrder(edge2, BOND_SINGLE)) { if (_is_zero_bond_present || type1 == 0) return 0; zero_bond = true; } else type2 = _context.g2.getBondOrder(edge2); if (type2 == BOND_TRIPLE) return 0; if (type2 == BOND_AROMATIC) arom_bond_idx2 = edge2; if (edge1 == -1 && type2 == BOND_AROMATIC) { if (!_context.dearomatizationMatcher->isAbleToFixBond(edge2, 1)) return 0; bond_type2 = 1; } if (type2 != BOND_AROMATIC) // both non-aromatic { if (type1 + _h_difference != type2) return 0; } else // exactly one aromatic { bond_type2 = type1 + _h_difference; if (bond_type2 < 1 || bond_type2 > 2) return 0; if (!_context.dearomatizationMatcher->isAbleToFixBond(edge2, bond_type2)) return 0; } int h_count_1 = _context.g1.getAtomTotalH(n1); int h_count_2 = _context.g2.getAtomTotalH(n2); if (!_context.force_hydrogens) { h_count_1 += _context.h_rep_count_1[n1]; h_count_2 += _context.h_rep_count_2[n2]; } if (h_count_1 == h_count_2) return (_context.g1.possibleAtomCharge(n1, _context.g2.getAtomCharge(n2)) ? 1 : 0); if ((_path_length & 1) && h_count_1 - _h_difference == h_count_2) { if (_context.cb_check_rules == 0 || _context.cb_check_rules(_context, _start_idx1, _start_idx2, n1, n2)) return -1; } return 0; } void TautomerChainFinder::addPair (int n1, int n2, bool is_zero_bond, int arom_bond_idx2, int bond_type2) { _context.core_1[n1] = n2; _context.core_2[n2] = n1; _context.chains_2[n2] = _path_number; _path_number++; _prev_n1 = n1; _prev_n2 = n2; _is_zero_bond_present = is_zero_bond; if (_path_length == 0) { _start_idx1 = n1; _start_idx2 = n2; } #ifdef TRACE_TAUTOMER_MATCHING for (int i = 0; i < _path_number; i++) printf(" "); printf("%2d\n", n1 + 1); for (int i = 0; i < _path_number; i++) printf(" "); printf("%2d\n", n2 + 1); #endif _bond_idx2 = arom_bond_idx2; if (_bond_idx2 >= 0) _context.dearomatizationMatcher->fixBond(_bond_idx2, bond_type2); } void TautomerChainFinder::restore () { _context.core_1[_prev_n1] = EmbeddingEnumerator::UNMAPPED; _context.core_2[_prev_n2] = EmbeddingEnumerator::UNMAPPED; _context.chains_2[_prev_n2] = 0; if (_bond_idx2 >= 0) _context.dearomatizationMatcher->unfixBond(_bond_idx2); } IMPL_ERROR(TautomerChainChecker, "tautomer chain checker"); TautomerChainChecker::TautomerChainChecker (TautomerSearchContext &context, const Array<int> &core1, const Array<int> &core2, int start_path_number) : _context(context), _path_length(0), _h_difference(0), _is_zero_bond_present(!context.ring_chain), _is_query_bond_present(false), _is_non_aromatic_bond_present(false), _path_number(start_path_number + 2), _core_1(core1), _core_2(core2), _tau_bonds_to_match(0), _prev_n1(-1), _prev_n2(-1), _bond_idx1(-1), _bond_idx2(-1), _bond_type2(0) { // calculate tautomer bonds for (int i = _context.g1.edgeBegin(); i < _context.g1.edgeEnd(); i = _context.g1.edgeNext(i)) { int bond_1 = _context.g1.getBondOrder(i); if (bond_1 == -1) continue; const Edge& edge_1 = _context.g1.getEdge(i); int beg = _core_1[edge_1.beg]; int end = _core_1[edge_1.end]; if (beg < 0 || end < 0) continue; if (_context.chains_2[beg] > 0 && _context.chains_2[end] > 0) continue; const Vertex& vert_2_beg = _context.g2.getVertex(beg); int edge_2_idx = vert_2_beg.neiEdge(vert_2_beg.findNeiVertex(end)); int bond_2 = _context.g2.getBondOrder(edge_2_idx); if (bond_1 != bond_2 && bond_1 != BOND_AROMATIC && bond_2 != BOND_AROMATIC) _tau_bonds_to_match++; } _context.core_1.copy(_core_1.ptr(), _context.initial_g1_vertexend); // see the comment in TautomerMatcher::_preliminaryEmbedding _context.core_2.copy(_core_2.ptr(), _context.g2.vertexEnd()); } TautomerChainChecker::TautomerChainChecker (TautomerChainChecker &other) : _context(other._context), _path_length(other._path_length), _h_difference(other._h_difference * -1), _is_zero_bond_present(other._is_zero_bond_present), _is_query_bond_present(other._is_query_bond_present), _is_non_aromatic_bond_present(other._is_non_aromatic_bond_present), _path_number(other._path_number), _core_1(other._core_1), _core_2(other._core_2), _tau_bonds_to_match(other._tau_bonds_to_match), _prev_n1(-1), _prev_n2(-1), _bond_idx1(-1), _bond_idx2(-1), _bond_type2(0), _start_idx1(other._start_idx1), _start_idx2(other._start_idx2) { } TautomerChainChecker::~TautomerChainChecker () { } bool TautomerChainChecker::check () { int n1 = -1, n2 = -1; if (!_checkInterPathBonds()) return true; if (_tau_bonds_to_match == 0 && _path_length == 0 && _matchAromatizedQuery()) { _context.core_1.clear_resize(_context.g1.vertexEnd()); _context.core_2.clear_resize(_context.g2.vertexEnd()); memcpy(_context.core_1.ptr(), _core_1.ptr(), sizeof(int) * _context.g1.vertexEnd()); memcpy(_context.core_2.ptr(), _core_2.ptr(), sizeof(int) * _context.g2.vertexEnd()); return false; } if (_path_length == 0) { int diff; while (nextStartingPair(n1, n2)) if (isFeasibleStartingPair(n1, n2, diff)) { TautomerChainChecker cc(*this); cc._path_number++; cc.addPair(n1, n2); cc._path_length++; cc._h_difference = -diff; if (!cc.check()) return false; cc.restore(); } } else { int e1 = -1, e2 = -1; int rc; while (nextPair(n1, n2, e1, e2)) { TautomerChainChecker cc1(*this); TautomerChainChecker cc2(*this); rc = isFeasiblePair(n1, n2, cc1, cc2); if (rc & 1) // intermediate pair in chain { cc1.addPair(n1, n2); if (!cc1.check()) return false; cc1.restore(); } if (rc & 2) // last pair in chain { cc2.addPair(n1, n2); if (cc2.releaseChain()) if (!cc2.check()) return false; cc2.restoreChain(); cc2.restore(); } } } return true; } bool TautomerChainChecker::nextStartingPair (int &n1, int &n2) { if (n2 == -1) n2 = _context.g2.vertexBegin(); else n2 = _context.g2.vertexNext(n2); //n1 = _core_2[n2]; for ( ; n2 < _context.g2.vertexEnd(); n2 = _context.g2.vertexNext(n2)) { n1 = _core_2[n2]; if (n1 != EmbeddingEnumerator::IGNORE && _context.chains_2[n2] <= 0) break; } if (n2 >= _context.g2.vertexEnd()) return false; if (n1 < 0) { if (n1 != EmbeddingEnumerator::UNMAPPED && n1 != EmbeddingEnumerator::TERM_OUT) throw Error("some strange situation"); n1 = -1; } return true; } bool TautomerChainChecker::isFeasibleStartingPair (int n1, int n2, int &h_diff) { //if (n2 != -1 && _context.chains_2[n2] > 0) // return false; if (n1 < 0) { h_diff = 0; return true; } if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2)) return false; int h_count_2 = _context.g2.getAtomTotalH(n2); int h_rep_1 = 0; if (!_context.force_hydrogens) { h_rep_1 = _context.h_rep_count_1[n1]; h_count_2 += _context.h_rep_count_2[n2]; } else if (!_context.g1.possibleAtomCharge(n1, _context.g2.getAtomCharge(n2))) return false; bool more = false, less = false; if (_context.g1.possibleAtomTotalH(n1, h_count_2 - h_rep_1 + 1)) more = true; if (_context.g1.possibleAtomTotalH(n1, h_count_2 - h_rep_1 - 1)) less = true; if (!less && !more) return false; if (less && more) h_diff = 0; else if (more) h_diff = -1; else h_diff = 1; return true; } bool TautomerChainChecker::nextPair (int &n1, int &n2, int &e1, int &e2) { const Vertex &vertex2 = _context.g2.getVertex(_prev_n2); if (e2 == -1) e2 = vertex2.neiBegin(); else e2 = vertex2.neiNext(e2); for (; e2 != vertex2.neiEnd(); e2 = vertex2.neiNext(e2)) { n2 = vertex2.neiVertex(e2); if (_context.chains_2[n2] == 0 && _core_2[n2] != EmbeddingEnumerator::IGNORE) break; } if (e2 == vertex2.neiEnd()) return false; e1 = -1; if ((n1 = _core_2[n2]) < 0) n1 = -1; else if (_prev_n1 != -1) { const Vertex &vertex1 = _context.g1.getVertex(_prev_n1); e1 = vertex1.findNeiEdge(_core_2[n2]); } return true; } int TautomerChainChecker::isFeasiblePair (int n1, int n2, TautomerChainChecker &next1, TautomerChainChecker &next2) { //if (n1 == -1 && _prev_n1 == -1) // return 0; //if (n2 != -1 && _context.chains_2[n2] > 0) // return 0; if (_prev_n1 != -1 && n1 != -1 && _context.decomposer1.getComponent(_prev_n1) != _context.decomposer1.getComponent(n1)) return 0; if (_prev_n2 != -1 && n2 != -1 && _context.decomposer2.getComponent(_prev_n2) != _context.decomposer2.getComponent(n2)) return 0; if (_context.g2.isPseudoAtom(n2) || _context.g2.isTemplateAtom(n2)) // n1 are checked in matchAtomsTau later return 0; int h_diff = _h_difference; int edge2 = _context.g2.findEdgeIndex(_prev_n2, n2); int type1 = 0; int type2 = 0; bool zero_bond = _is_zero_bond_present; bool query_bond = _is_query_bond_present; bool na_bond = _is_non_aromatic_bond_present; int bond_type2 = 0; bool tau_bond = true; if (edge2 == -1) { zero_bond = true; } else { type2 = _context.g2.getBondOrder(edge2); if (type2 == -1 && _context.g2.possibleBondOrder(edge2, BOND_SINGLE)) type2 = BOND_ZEROED; if (type2 == BOND_ZEROED) { if (_is_zero_bond_present) return 0; zero_bond = true; } } int edge1 = -1; if (n1 != -1) { if (_prev_n1 != -1) edge1 = _context.g1.findEdgeIndex(_prev_n1, n1); if (edge1 != -1) { type1 = _context.g1.getBondOrder(edge1); if (type1 == -1) return 0; } } if (type1 == BOND_AROMATIC || type1 == BOND_TRIPLE || type2 == BOND_TRIPLE) return 0; // Check degrees on previous vertices if (_prev_n1 != -1 && _path_length != 1) { const Vertex &prev_vert1 = _context.g1.getVertex(_prev_n1); const Vertex &prev_vert2 = _context.g2.getVertex(_prev_n2); int i; int bond_count1 = prev_vert1.degree(); int prev_value_chain = _context.chains_2[_core_1[_prev_n1]]; for (i = prev_vert1.neiBegin(); i < prev_vert1.neiEnd(); i = prev_vert1.neiNext(i)) { int value_chain = _context.chains_2[_core_1[prev_vert1.neiVertex(i)]]; if (value_chain > 0 && abs(value_chain - prev_value_chain) == 1) bond_count1--; } int bond_count2 = prev_vert2.degree(); for (i = prev_vert2.neiBegin(); i < prev_vert2.neiEnd(); i = prev_vert2.neiNext(i)) { int value_chain = _context.chains_2[prev_vert2.neiVertex(i)]; if ((value_chain > 0 && abs(value_chain - prev_value_chain) == 1) || (_context.g2.getBondOrder(prev_vert2.neiEdge(i)) == -1 && _context.g2.possibleBondOrder(prev_vert2.neiEdge(i), BOND_SINGLE))) bond_count2--; } if (type1 == 0) bond_count1++; if (type2 == 0) bond_count2++; //if (bond_count1 != bond_count2) if (bond_count1 > bond_count2 || bond_count2 - bond_count1 > 1) return 0; } // zero bond must be if (edge1 == -1 && type2 == BOND_SINGLE && _h_difference == 1) { if (zero_bond) return 0; zero_bond = true; } if (zero_bond && !_is_zero_bond_present) { // Assume that aromatic ring cannot be destroyed if (type2 > 1) return 0; if (type2 == BOND_SINGLE) { if (_h_difference != 0) { if (_h_difference != 1) return 0; } else h_diff = 1; } } if (edge1 == -1 || type2 == BOND_AROMATIC) tau_bond = false; if (edge1 != -1) query_bond = true; if (type2 != BOND_AROMATIC) na_bond = true; if (type1 != 0) { if (type2 != BOND_AROMATIC) // both non-aromatic { if (_h_difference != 0) { if (type1 + _h_difference != type2) return 0; } else if (abs(h_diff = (type2 - type1)) != 1) return 0; } else // exactly one aromatic { if (_h_difference != 0) { bond_type2 = type1 + _h_difference; if (bond_type2 < 1 || bond_type2 > 2) return 0; if (!_context.dearomatizationMatcher->isAbleToFixBond(edge2, bond_type2)) return 0; } else { if (type1 == BOND_SINGLE) { h_diff = 1; bond_type2 = 2; } else if (type1 == BOND_DOUBLE) { h_diff = -1; bond_type2 = 1; } if (bond_type2 > 0 && !_context.dearomatizationMatcher->isAbleToFixBond(edge2, bond_type2)) return 0; } } } int rc = 0; if (n1 != -1) { if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2)) return 0; int h_rep_1 = 0; int h_count_2 = _context.g2.getAtomTotalH(n2); if (!_context.force_hydrogens) { h_rep_1 = _context.h_rep_count_1[n1]; h_count_2 += _context.h_rep_count_2[n2]; } if (query_bond && na_bond) { if (h_diff != 0) { if (!(_path_length & 1)) if (_context.g1.possibleAtomTotalH(n1, h_count_2 + h_diff - h_rep_1)) { if (_context.cb_check_rules == 0 || _context.cb_check_rules(_context, _start_idx1, _start_idx2, n1, n2)) rc += 2; } } else if (!(_path_length & 1)) if (_context.g1.possibleAtomTotalH(n1, h_count_2 + 1 - h_rep_1) || _context.g1.possibleAtomTotalH(n1, h_count_2 - 1 - h_rep_1)) { if (_context.cb_check_rules == 0 || _context.cb_check_rules(_context, _start_idx1, _start_idx2, n1, n2)) rc += 2; } } if (_context.g1.possibleAtomTotalH(n1, h_count_2 - h_rep_1)) { int charge_2 = _context.g2.getAtomCharge(n2); if (_context.g1.possibleAtomCharge(n1, charge_2)) rc++; } } else { rc++; if (!(_path_length & 1) && query_bond && na_bond) { if (_context.cb_check_rules == 0 || _context.cb_check_rules(_context, _start_idx1, _start_idx2, n1, n2)) rc += 2; } } if (rc & 1) { next1._path_length++; next1._h_difference = -h_diff; next1._is_zero_bond_present = zero_bond; next1._is_query_bond_present = query_bond; next1._is_non_aromatic_bond_present = na_bond; if (tau_bond) next1._tau_bonds_to_match--; next1._bond_idx1 = edge1; next1._bond_idx2 = edge2; next1._bond_type2 = bond_type2 > 0 ? bond_type2 : type2; } if (rc & 2) { next2._final_path_length = next2._path_length + 1; next2._path_length = 0; next2._h_difference = 0; next2._final_h_difference = -h_diff; next2._is_query_bond_present = false; next2._is_non_aromatic_bond_present = false; //next2._is_zero_bond_present = false; next2._is_zero_bond_present = true; if (tau_bond) next2._tau_bonds_to_match--; next2._bond_idx1 = edge1; next2._bond_idx2 = edge2; next2._bond_type2 = bond_type2 > 0 ? bond_type2 : type2; } return rc; } void TautomerChainChecker::addPair (int n1, int n2) { _context.chains_2[n2] = _path_number; _context.n1.expand(_path_number + 1); _context.n2.expand(_path_number + 1); _context.n1[_path_number] = n1; _context.n2[_path_number] = n2; _context.edges_1.expand(_path_number + 1); _context.edges_2.expand(_path_number + 1); _context.edge_types_2.expand(_path_number + 1); _context.edges_1[_path_number] = _bond_idx1; _context.edges_2[_path_number] = _bond_idx2; _context.edge_types_2[_path_number] = _bond_type2; _path_number++; _prev_n1 = n1; _prev_n2 = n2; if (_path_length == 0) { _start_idx1 = n1; _start_idx2 = n2; } if (_bond_type2 > 0 && _bond_type2 != BOND_AROMATIC) _context.dearomatizationMatcher->fixBond(_bond_idx2, _bond_type2); #ifdef TRACE_TAUTOMER_MATCHING for (int i = 0; i < _path_number; i++) printf(" "); printf("%2d\n", n1 + 1); for (int i = 0; i < _path_number; i++) printf(" "); printf("%2d\n", n2 + 1); #endif } void TautomerChainChecker::restore () { _context.chains_2[_prev_n2] = 0; if (_bond_type2 > 0 && _bond_type2 != BOND_AROMATIC) _context.dearomatizationMatcher->unfixBond(_bond_idx2); } // Make chain as in target bool TautomerChainChecker::releaseChain () { if (_final_h_difference == 0) throw Error("unknown hydrogen difference"); //if (_bond_type1 < 0 || _bond_type1 > 2 || _bond_type2 == BOND_AROMATIC) // throw Error("bad bond type"); int i = 1; int type, idx1, idx2, new_idx; int beg, end; BaseMolecule &query = _context.g1; BaseMolecule &target = _context.g2; int h_diff = _final_h_difference; bool res = true; for (i = 1; i < _final_path_length; i++, h_diff *= -1) { type = _context.edge_types_2[_path_number - i]; idx1 = _context.edges_1[_path_number - i]; idx2 = _context.edges_2[_path_number - i]; if (type == BOND_AROMATIC) { if (idx1 < 0) { bool can_be_single = _context.dearomatizationMatcher->isAbleToFixBond(idx2, BOND_SINGLE); bool can_be_double = _context.dearomatizationMatcher->isAbleToFixBond(idx2, BOND_DOUBLE); if (can_be_single != can_be_double) type = can_be_single ? BOND_SINGLE : BOND_DOUBLE; } else { if (h_diff == 1) type = BOND_SINGLE; else type = BOND_DOUBLE; } } if (type > 0 && idx1 < 0) { // Forbidden zero bond if (!_context.ring_chain && type == BOND_SINGLE && h_diff == -1) res = false; const Edge &edge2 = target.getEdge(idx2); beg = _context.core_2[edge2.beg]; if (beg < 0) { if (_context.substructure) { QueryMolecule &qmol = query.asQueryMolecule(); new_idx = qmol.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, target.getAtomNumber(edge2.beg))); if (target.getAtomCharge(edge2.beg) != 0) { qmol.resetAtom(new_idx, QueryMolecule::Atom::und( qmol.releaseAtom(new_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, target.getAtomCharge(edge2.beg)))); } } else { Molecule &mol = query.asMolecule(); new_idx = mol.addAtom(target.getAtomNumber(edge2.beg)); mol.setAtomCharge(new_idx, target.getAtomCharge(edge2.beg)); } _context.core_2[edge2.beg] = new_idx; _context.core_1.expand(new_idx + 1); _context.core_1[new_idx] = edge2.beg; } end = _context.core_2[edge2.end]; if (end < 0) { if (_context.substructure) { QueryMolecule &qmol = query.asQueryMolecule(); new_idx = qmol.addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, target.getAtomNumber(edge2.end))); if (target.getAtomCharge(edge2.end) != 0) { qmol.resetAtom(new_idx, QueryMolecule::Atom::und( qmol.releaseAtom(new_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, target.getAtomCharge(edge2.end)))); } } else { Molecule &mol = query.asMolecule(); new_idx = mol.addAtom(target.getAtomNumber(edge2.end)); mol.setAtomCharge(new_idx, target.getAtomCharge(edge2.end)); } _context.core_2[edge2.end] = new_idx; _context.core_1.expand(new_idx + 1); _context.core_1[new_idx] = edge2.end; } int new_bond_idx; if (_context.substructure) { QueryMolecule &qmol = query.asQueryMolecule(); new_bond_idx = qmol.addBond(_context.core_2[edge2.beg], _context.core_2[edge2.end], new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, type)); } else { Molecule &mol = query.asMolecule(); new_bond_idx = mol.addBond(_context.core_2[edge2.beg], _context.core_2[edge2.end], type); } _context.edges_1[_path_number - i] = -2 - new_bond_idx; } else if (idx1 >= 0) { if (_context.substructure) { QueryMolecule &qmol = query.asQueryMolecule(); qmol.resetBond(idx1, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, type)); } else { Molecule &mol = query.asMolecule(); mol.setBondOrder(idx1, type); } } } return res; } // Restore original chain void TautomerChainChecker::restoreChain () { int diff = _final_h_difference; int i = 1; int type, idx1, vidx1, vidx2; BaseMolecule &query = _context.g1; for (i = 1; i < _final_path_length; i++, diff *= -1) { type = _context.edge_types_2[_path_number - i] + diff; idx1 = _context.edges_1[_path_number - i]; if (idx1 < -1) { idx1 = -idx1 - 2; query.removeBond(idx1); _context.edges_1[_path_number - i] = -1; } else if (idx1 >= 0) { if (_context.substructure) { QueryMolecule &qmol = query.asQueryMolecule(); qmol.resetBond(idx1, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, type)); } else { Molecule &mol = query.asMolecule(); mol.setBondOrder(idx1, type); } } if (_context.n1[_path_number - i] < 0) { vidx2 = _context.n2[_path_number - i]; vidx1 = _context.core_2[vidx2]; if (vidx1 >= 0) { query.removeAtom(vidx1); _context.core_1[vidx1] = -1; _context.core_2[vidx2] = -1; } } } if (_context.n1[_path_number - i] < 0) { vidx2 = _context.n2[_path_number - i]; vidx1 = _context.core_2[vidx2]; if (vidx1 >= 0) { query.removeAtom(vidx1); _context.core_1[vidx1] = -1; _context.core_2[vidx2] = -1; } } } bool TautomerChainChecker::_checkInterPathBonds () { BaseMolecule &g1 = _context.g1; BaseMolecule &g2 = _context.g2; if (_prev_n1 != -1) { const Vertex &vertex = g1.getVertex(_prev_n1); int idx1, idx2; for (int i = vertex.neiBegin(); i < vertex.neiEnd(); i = vertex.neiNext(i)) { int nei_idx = vertex.neiVertex(i); if (_context.core_1[nei_idx] < 0 || _context.chains_2[_context.core_1[nei_idx]] == 0) continue; if (abs(_context.chains_2[_context.core_1[_prev_n1]] - _context.chains_2[_context.core_1[nei_idx]]) != 1) { idx1 = vertex.neiEdge(i); idx2 = g2.findEdgeIndex(_context.core_1[_prev_n1], _context.core_1[nei_idx]); int type1 = g1.getBondOrder(idx1); int type2 = g2.getBondOrder(idx2); bool matched; if (_context.substructure) { matched = MoleculeSubstructureMatcher::matchQueryBond(&(g1.asQueryMolecule()).getBond(idx1), g2, idx1, idx2, 0, 0xFFFFFFFFUL); } else { matched = (g1.getBondOrder(idx1) == g2.getBondOrder(idx2)); } if (!matched) { if (type1 != BOND_AROMATIC && type2 == BOND_AROMATIC) { if (!_context.dearomatizationMatcher->isAbleToFixBond(idx2, type1)) return false; } else return false; } } } } return true; } bool TautomerChainChecker::_matchAromBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { AromaticityMatcher *am = (AromaticityMatcher *)userdata; QueryMolecule::Bond *bond = &((BaseMolecule &)subgraph).asQueryMolecule().getBond(sub_idx); return MoleculeSubstructureMatcher::matchQueryBond(bond, (BaseMolecule &)supergraph, sub_idx, super_idx, am, 0xFFFFFFFFUL); } void TautomerChainChecker::_removeAtom (Graph &subgraph, int sub_idx, void *userdata) { AromaticityMatcher *am = (AromaticityMatcher *)userdata; MoleculeSubstructureMatcher::removeAtom(subgraph, sub_idx, am); } void TautomerChainChecker::_addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { AromaticityMatcher *am = (AromaticityMatcher *)userdata; MoleculeSubstructureMatcher::addBond(subgraph, supergraph, sub_idx, super_idx, am); } int TautomerChainChecker::_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { AromaticityMatcher *am = (AromaticityMatcher *)userdata; QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); BaseMolecule &target = (BaseMolecule &)supergraph; if (!MoleculeStereocenters::checkSub(query.stereocenters, target.stereocenters, core_sub, false)) return 1; if (!MoleculeCisTrans::checkSub(query, target, core_sub)) return 1; // Check possible aromatic configuration if (am != 0) { if (!am->match(core_sub, core_super)) return 1; } return 0; } bool TautomerChainChecker::_matchAromatizedQuery() { QS_DEF(QueryMolecule, aromatized_query); QS_DEF(Array<int>, mapping); aromatized_query.clone(((BaseMolecule &)_context.g1).asQueryMolecule(), 0, &mapping); QueryMoleculeAromatizer::aromatizeBonds(aromatized_query, _context.arom_options); EmbeddingEnumerator ee(_context.g2); ee.setSubgraph(aromatized_query); AromaticityMatcher am(aromatized_query, (Molecule &)_context.g2, _context.arom_options); ee.userdata = &am; ee.cb_match_edge = _matchAromBonds; ee.cb_vertex_remove = _removeAtom; ee.cb_edge_add = _addBond; ee.cb_embedding = _embedding; int i; for (i = _context.g1.vertexBegin(); i < _context.g1.vertexEnd(); i = _context.g1.vertexNext(i)) { int val = _context.core_1[i]; if (val == EmbeddingEnumerator::IGNORE) ee.ignoreSubgraphVertex(i); else if (!ee.fix(mapping[i], val)) return false; } if (!ee.process()) return true; return false; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_enumerator.cpp�����������������������������������0000664�0000000�0000000�00000060375�12710376503�0025302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_tautomer_enumerator.h" #include "base_cpp/scanner.h" #include "graph/embedding_enumerator.h" #include "molecule/inchi_parser.h" #include "molecule/inchi_wrapper.h" #include "molecule/elements.h" #include "molecule/molecule.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_automorphism_search.h" #include "molecule/molecule_tautomer.h" #include "molecule/molecule_tautomer_utils.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_inchi.h" #include "molecule/molecule_layered_molecules.h" #include "reaction/reaction_product_enumerator.h" #include "reaction/rsmiles_loader.h" #include "reaction/reaction_transformation.h" #include "reaction/query_reaction.h" using namespace indigo; TautomerEnumerator::TautomerEnumerator(Molecule &molecule, TautomerMethod method) : layeredMolecules(molecule), #ifdef USE_DEPRECATED_INCHI _use_deprecated_inchi(false), #endif _currentLayer(0), _currentRule(0) { #ifdef USE_DEPRECATED_INCHI if(method == INCHI) { _use_deprecated_inchi = true; } if(_use_deprecated_inchi) { // Obsolete code for tautomer enumeration using InChI code InchiWrapper indigo_inchi; QS_DEF(Array<char>, tmp); indigo_inchi.saveMoleculeIntoInchi(molecule, tmp); const char *params = tmp.ptr(); // We need a canonical mapping. This is something that MoleculeInChI does. // This is the only reason I use it. Maybe it's better to implement this procedure outside of MoleculeInChI. QS_DEF(Array<int>, canonical_mapping); canonical_mapping.resize(molecule.vertexEnd()); canonical_mapping.zerofill(); QS_DEF(Array<int>, ignored); ignored.resize(molecule.vertexEnd()); ignored.zerofill(); MoleculeAutomorphismSearch of; of.detect_invalid_cistrans_bonds = false; of.detect_invalid_stereocenters = false; of.find_canonical_ordering = true; of.ignored_vertices = ignored.ptr(); of.getcanon = true; of.compare_vertex_degree_first = false; of.refine_reverse_degree = true; of.refine_by_sorted_neighbourhood = true; of.cb_vertex_cmp = MoleculeInChICompoment::cmpVertex; of.process(molecule); of.getCanonicalNumbering(canonical_mapping); InChICodeParser inchiParser(params); QS_DEF(Array<int>, hydrogens); // For each position get number of fixed hydrogens hydrogens.resize(molecule.vertexCount()); hydrogens.zerofill(); QS_DEF(Array<int>, inv_mapping); inv_mapping.resize(molecule.vertexEnd()); inv_mapping.fill(-1); int j = 0; for(auto i : molecule.vertices()) { inv_mapping[i] = j++; } for(auto i = inchiParser.staticHydrogenPositionBegin(); i != inchiParser.staticHydrogenPositionEnd(); i = inchiParser.staticHydrogenPositionNext(i)) { int inchiIndex = inchiParser.getHydrogen(i); int molIndex = canonical_mapping[inchiIndex]; int simpleIndex = inv_mapping[molIndex]; ++hydrogens[simpleIndex]; } // Indicate places for mobile hydrogens for(auto i = inchiParser.mobileHydrogenPositionBegin(); i != inchiParser.mobileHydrogenPositionEnd(); i = inchiParser.mobileHydrogenPositionNext(i)) { // Actually this is the only thing we need from InChI: a hint which positions mobile hydrogen can occupy. // If we don't use this, we can avoid using InChI at all. int inchiIndex = inchiParser.getHydrogen(i); int molIndex = canonical_mapping[inchiIndex]; int simpleIndex = inv_mapping[molIndex]; layeredMolecules.setMobilePosition(simpleIndex, true); } /* for (auto i : molecule.vertices()) { // Alternative: set all positions as possible mobile hydrogen positions. // This dramatically icreases total number of tautomers found. int inchiIndex = inchiParser.getHydrogen(i); int molIndex = canonical_mapping[inchiIndex]; int simpleIndex = inv_mapping[molIndex]; layeredMolecules.setMobilePosition(simpleIndex, true); } */ // Indicate occupied mobile positions // Probably this could be done somehow inside of hypermolecule Dbitset Ox01; Ox01.set(0); for(auto i : molecule.vertices()) { bool occupied = (molecule.getAtomTotalH(i) - hydrogens[inv_mapping[i]]) != 0; if(occupied) { occupied = false; const Vertex &v = molecule.getVertex(i); for(auto i = v.neiBegin(); i != v.neiEnd(); i = v.neiNext(i)) { int e_inx = v.neiEdge(i); if(molecule.getBondOrder(e_inx) == 1 || molecule.getBondOrder(e_inx) == 4) { occupied = true; break; } } } layeredMolecules.setMobilePositionOccupiedMask(inv_mapping[i], Ox01, occupied); } // Look for "zebra" pattern (like -=-=-=-=... or =-=-=-=-...) int v1 = _zebraPattern.addVertex(); for(auto i : layeredMolecules.vertices()) { int v2 = _zebraPattern.addVertex(); _zebraPattern.addEdge(v1, v2); v1 = v2; } } #endif _complete = false; aromatizedRange[0] = 0; aromatizedRange[1] = 0; } void TautomerEnumerator::enumerateAll(bool needAromatization) { while (!_performProcedure()) ; if(needAromatization) aromatize(); } int TautomerEnumerator::beginNotAromatized() { _enumeratedHistory.clear(); return 1; } int TautomerEnumerator::beginAromatized() { _enumeratedHistory.clear(); if(aromatizedRange[1] == 0) { _aromatize(aromatizedRange[1], layeredMolecules.layers); aromatizedRange[1] = layeredMolecules.layers; } return -1; } bool TautomerEnumerator::isValid(int n) { if(n > 0) { if(n - 1 < layeredMolecules.layers) return true; if(_complete) return false; if(_performProcedure()) { _complete = true; return false; } return true; } if(n < 0) { if(-(n + 1) < layeredMolecules.layers) { if(-(n + 1) >= aromatizedRange[1]) { _aromatize(aromatizedRange[1], layeredMolecules.layers); aromatizedRange[1] = layeredMolecules.layers; } unsigned hash = layeredMolecules.getHash(-(n + 1), true); return !_enumeratedHistory.find(hash); } if(_complete) return false; if(_performProcedure()) { _complete = true; return false; } _aromatize(aromatizedRange[1], layeredMolecules.layers); aromatizedRange[1] = layeredMolecules.layers; unsigned hash = layeredMolecules.getHash(-(n + 1), true); return !_enumeratedHistory.find(hash); } return false; } int TautomerEnumerator::next(int n) { if(n > 0) return n + 1; else if(n < 0) { unsigned hash = layeredMolecules.getHash(-(n + 1), true); _enumeratedHistory.insert(hash); --n; while(!_complete && !isValid(n)) { --n; } return n; } return 1; } void TautomerEnumerator::constructMolecule(Molecule &molecule, int n) const { if(n > 0) layeredMolecules.constructMolecule(molecule, n - 1, false); else if(n < 0) layeredMolecules.constructMolecule(molecule, -(n + 1), true); else ;//error! } bool TautomerEnumerator::refine_proc(const Molecule &uncleaned_fragments, Molecule &product, Array<int> &mapping, void *userdata) { bool changed = true; while(changed) { changed = false; for(auto v_idx : product.vertices()) { const Vertex &vertex = product.getVertex(v_idx); int aromatic = 0; int aromaticBondInd1 = 0; int aromaticBondInd2 = 0; int connectivity = 0; int explicitHydrogens = 0; for(int j : vertex.neighbors()) { int vn_idx = vertex.neiVertex(j); int e_idx = product.findEdgeIndex(v_idx, vn_idx); int order = product.getBondOrder(e_idx); if(order == BOND_AROMATIC) { if(aromatic == 1) aromaticBondInd2 = e_idx; else aromaticBondInd1 = e_idx; ++aromatic; } else if(product.getAtomNumber(vn_idx) != ELEM_H) connectivity += order; else ++explicitHydrogens; } if(aromatic == 1) { int frags_idx = mapping.find(v_idx); int new_order = const_cast<Molecule&>(uncleaned_fragments).getAtomValence(frags_idx) - explicitHydrogens - connectivity - const_cast<Molecule&>(uncleaned_fragments).getImplicitH(frags_idx); if(new_order < BOND_SINGLE || new_order > BOND_TRIPLE) return false; product.setBondOrder(aromaticBondInd1, new_order); changed = true; } else if(aromatic == 2 && connectivity > 1) { int frags_idx = mapping.find(v_idx); if(const_cast<Molecule&>(uncleaned_fragments).getAtomValence(frags_idx) == connectivity + 2) { product.setBondOrder(aromaticBondInd1, BOND_SINGLE); product.setBondOrder(aromaticBondInd2, BOND_SINGLE); changed = true; } else return false; } else if(aromatic == 0) { if(product.getAtomNumber(v_idx) != ELEM_H) { int frags_idx = mapping.find(v_idx); try { if(product.getAtomNumber(v_idx) != ELEM_N) { if(product.getAtomValence(v_idx) + product.getAtomCharge(v_idx) != connectivity + explicitHydrogens + product.getImplicitH(v_idx)) return false; } else { if(product.getAtomValence(v_idx) + product.getAtomCharge(v_idx) != connectivity + explicitHydrogens + product.getImplicitH(v_idx) && product.getAtomValence(v_idx) != connectivity + explicitHydrogens + product.getImplicitH(v_idx)) return false; } } catch(indigo::Exception&) { return false; } } } } } return true; } void TautomerEnumerator::product_proc( Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata ) { LayeredMolecules *lm = (LayeredMolecules *)userdata; lm->addLayerFromMolecule(product, mapping); } bool TautomerEnumerator::enumerateLazy() { return _performProcedure(); } bool TautomerEnumerator::_performProcedure() { #ifdef USE_DEPRECATED_INCHI if(_use_deprecated_inchi) { // Construct tautomers EmbeddingEnumerator ee(layeredMolecules); ee.setSubgraph(_zebraPattern); ee.cb_match_edge = matchEdge; ee.cb_match_vertex = matchVertex; ee.cb_edge_add = edgeAdd; ee.cb_vertex_add = vertexAdd; ee.cb_vertex_remove = vertexRemove; Breadcrumps breadcrumps; ee.userdata = &breadcrumps; int layersBefore = layeredMolecules.layers; ee.process(); return layeredMolecules.layers == layersBefore; } #endif const char* reactionSmarts[] = { #if 0 // Just InChI-like rules based on heteroatoms "[#1:0][N&v3,n,O,S:1][*:2]=,:[N&v3,n,O,S:3]>>[N,n,O,S:1]=[A,a:2]-[N,n,O,S:3][#1:0]", "[#1:0][N&v3,n,O,S:1][*:2]=,:[*:3][*:4]=,:[N&v3,n,O,S:5]>>[N,n,O,S:1]=[A,a:2]-[A,a:3]=[A,a:4]-[N,n,O,S:5][#1:0]", "[#1:0][N&v3,n,O,S:1][*:2]=,:[*:3][*:4]=,:[*:5][*:6]=,:[N&v3,n,O,S:7]>>[N,n,O,S:1]=[A,a:2]-[A,a:3]=[A,a:4]-[A,a:5]=[A,a:6]-[N,n,O,S:7][#1:0]", "[#1:0][N&v3,n,O,S:1][*:2]=,:[*:3][*:4]=,:[*:5][*:6]=,:[*:7][*:8]=,:[N&v3,n,O,S:9]>>[N,n,O,S:1]=[A,a:2]-[A,a:3]=[A,a:4]-[A,a:5]=[A,a:6]-[A,a:7]=[A,a:8]-[N,n,O,S:9][#1:0]" #else "[O,S,Se,Te;X1:1]=[C:2][CX4;R0,R1,R2:3][#1:0]>>[#1:0][O,S,Se,Te;X2:1]-[#6;X3:2]=[C,c;X3:3]", // Rule 1: 1,3 Keto-enol "[#1:0][O,S,Se,Te;X2:1]-[#6;X3:2]=[C,c;X3:3]>>[O,S,Se,Te;X1:1]=[C:2][CX4;R0,R1,R2:3][#1:0]", // Rule 1: 1,3 Keto-enol "[O,S,Se,Te;X1:1]=[CX3:2]([#6:6])[C:3]=[C:4][CX4,NX3:5]([C:7])[#1:0]>>[#1:0][O,S,Se,Te;X2:1][CX3:2]([C:6])=[C:3][C:4]=[CX3,N:5]([C:7])", // Rule 2: 1,5 Keto-enol "[#1:0][O,S,Se,Te;X2:1][CX3:2]([C:6])=[C:3][C:4]=[CX3,N:5]([C:7])>>[O,S,Se,Te;X1:1]=[CX3:2]([#6:6])[C:3]=[C:4][CX4,NX3:5]([C:7])[#1:0]", // Rule 2: 1,5 Keto-enol "[#1,a,O:5][NX2:1]=[CX3:2]([C,#1:4])[CX4;R0,R1,R2:3][#1:0]>>[#1,a,O:5][NX3:1]([#1:0])[CX3:2]([C,#1:4])=[CX3:3]", // Rule 3: simple (aliphatic) imine "[#1,a,O:5]-[NX3:1](-[#1:0])-[CX3:2](-[C,#1:4])=[CX3:3]>>[#1,a,O:5]-[NX2:1]=[CX3:2](-[C,#1:4])-[CX4;R0,R1,R2:3][#1:0]", // Rule 3: simple (aliphatic) imine "[CX3R0:1]([C,#1:5])([C:4])=[C:2][N:3]([C:6])[#1:0]>>[#1:0][CX4R0:1]([C,#1:5])([C:4])[c:2]:[n:3]:[c:6]", // Rule 4: special imine "[#1:0][CX4R0:1]([C,#1:5])([C:4])[c:2]:[n:3]:[c:6]>>[CX3R0:1]([C,#1:5])([C:4])=[C:2][N:3]([C:6])[#1:0]", // Rule 4: special imine "[#1:0][N:1]-&@[C:2]=[O,NX2:3]>>[NX2,nX2:1]=[C,c:2]-[O,N:3][#1:0]", // Rule 5: aromatic heteroatom H shift "[NX2,nX2:1]=,:[C,c:2][O,N:3][#1:0]>>[#1:0][N:1]-&@[C:2]=[O,NX2:3]", // Rule 5: aromatic heteroatom H shift "[N,n,S,s,O,o,Se,Te:1]=[NX2,nX2,C,c,P,p:2]-[N,n,S,O,Se,Te:3][#1:0]>>[#1:0][N,n,S,O,Se,Te:1]-[NX2,nX2,C,c,P,p:2]=[N,n,S,s,O,o,Se,Te:3]", // Rule 6: 1,3 heteroatom H shift "[NX2,nX2,S,O,Se,Te:1]=[C,c,NX2,nX2:2][C,c:3]=[C,c,nX2:4][N,n,S,s,O,o,Se,Te:5][#1:0]>>[#1:0][N,n,S,O,Se,Te:1][C,c,NX2,nX2:2]=[C,c:3][C,c,nX2:4]=[NX2,S,O,Se,Te:5]", // Rule 7: 1,5 (aromatic) heteroatom H shift (1) "[n,s,o:1]:[c,n:2]:[c:3]:[c,n:4]:[n,s,o:5][#1:0]>>[#1:0][n,s,o:1]:[c,n:2]:[c:3]:[c,n:4]:[n,s,o:5]", // Rule 8: 1,5 aromatic heteroatom H shift (2) "[NX2,nX2,S,O,Se,Te:1]=,:[C,c,NX2,nX2:2][C,c:3]=,:[C,c,NX2,nX2:4][C,c,NX2,nX2:5]=,:[C,c,NX2,nX2:6][N,n,S,s,O,o,Se,Te:7][#1:0]>>[#1:0][N,n,S,O,Se,Te:1]-[C,c,NX2,nX2:2]=[C,c:3]-[C,c,NX2,nX2:4]=[C,c,NX2,nX2:5]-[C,c,NX2,nX2:6]=[NX2,S,O,Se,Te:7]", // Rule 9: 1,7 (aromatic) heteroatom H shift "[#1:0][N,n,O:1][C,c,nX2:2]=,:[C,c,nX2:3][c,nX2:4]=,:[c,nX2:5][c,nX2:6]=,:[c,nX2:7][C,c,nX2:8]=,:[N,n,O:9]>>[NX2,nX2,O:1]=[C,c,nX2:2]-[c,nX2:3]=[c,nX2:4]-[c,nX2:5]=[c,nX2:6]-[c,nX2:7]=[c,nX2:8]-[n,O:9][#1:0]", // Rule 10: 1,9 (aromatic) heteroatom H shift "[#1:0][N,n,O:1][C,c,nX2:2]=,:[C,c,nX2:3][c,nX2:4]=,:[C,c,nX2:5][C,c,nX2:6]=,:[C,c,nX2:7][C,c,nX2:8]=,:[C,c,nX2:9][C,c,nX2:10]=,:[NX2,nX2,O:11]>>[NX2,nX2,O:1]=[C,c,nX2:2]-[C,c,nX2:3]=[C,c,nX2:4]-[C,c,nX2:5]=[C,c,nX2:6]-[C,c,nX2:7]=[C,c,nX2:8]-[C,c,nX2:9]=[C,c,nX2:10]-[O,nX2:11][#1:0]", // Rule 11: 1,11 (aromatic) heteroatom H shift "[#1:0][O,S,N:1][C,c;r5:2]([!#6&!#1:5])=,:[C,c;r5:3][C,c;r5:4]>>[O,S,N:1]=[C;r5:2]([A,a:5])-[Cr5;R0,R1,R2:3]([#1:0])[C,c;r5:4]", // Rule 12: furanones "[O,S,N:1]=[C;r5:2]([A,a:5])[Cr5;R0,R1,R2:3]([#1:0])[C,c;r5:4]>>[#1:0][O,S,N:1]-[C,c;r5:2]([!#6&!#1:5])=[C,c;r5:3]-[C,c;r5:4]", // Rule 12: furanones "[O,S,Se,Te;X1:1]=[C:2]=[C:3][#1:0]>>[#1:0][O,S,Se,Te;X2:1][C:2]#[C:3]", // Rule 13: keten/ynol exchange "[#1:0][O,S,Se,Te;X2:1][C:2]#[C:3]>>[O,S,Se,Te;X1:1]=[C:2]=[C:3][#1:0]", // Rule 13: keten/ynol exchange "[#1:0][C:1][N+:2]([O-:4])=[O:3]>>[C:1]=[N+:2]([O-:4])[O:3][#1:0]", // Rule 14: ionic nitro/aci-nitro "[C:1]=[N+:2]([O-:4])[O:3][#1:0]>>[#1:0][C:1][N+:2]([O-:4])=[O:3]", // Rule 14: ionic nitro/aci-nitro //"[#1:0][C:1][N:2](=[O:4])=[O:3]>>[C:1]=[N:2](=[O:4])[O:3][#1:0]", // Rule 15: pentavalent nitro/aci-nitro "[#1:0][O:1][N:2]=[C:3]>>[O:1]=[N:2][C:3][#1:0]", // Rule 16: oxim/nitroso "[O:1]=[N:2][C:3][#1:0]>>[#1:0][O:1][N:2]=[C:3]", // Rule 16: oxim/nitroso "[#1:0][O:1][N:2]=[C:3][C:4]=[C:5][C:6]=[O:7]>>[O:1]=[N:2][c:3]=[c:4][c:5]=[c:6][O:7][#1:0]", // Rule 17: oxim/nitroso via phenol "[O:1]=[N:2][c:3]:[c:4]:[c:5]:[c:6][O:7][#1:0]>>[#1:0][O:1][N:2]=[C:3][C:4]=[C:5][C:6]=[O:7]", // Rule 17: oxim/nitroso via phenol "[#1:0][O:1][C:2]#[N:3]>>[O:1]=[C:2]=[N:3][#1:0]", // Rule 18: cyanic/iso-cyanic acids "[O:1]=[C:2]=[N:3][#1:0]>>[#1:0][O:1][C:2]#[N:3]", // Rule 18: cyanic/iso-cyanic acids "[#1:0][O,N:1][C:2]=[S,Se,Te:3]=[O:4]>>[O,N:1]=[C:2][S,Se,Te:3][O:4][#1:0]", // Rule 19: formamidinesulfinic acids "[O,N:1]=[C:2][S,Se,Te:3][O:4][#1:0]>>[#1:0][O,N:1][C:2]=[S,Se,Te:3]=[O:4]", // Rule 19: formamidinesulfinic acids "[#1:0][C0:1]#[N0:2]>>[C-:1]#[N+:2][#1:0]", // Rule 20: isocyanides "[C-:1]#[N+:2][#1:0]>>[#1:0][C0:1]#[N0:2]", // Rule 20: isocyanides "[#1:0][O:1][P:2]>>[O:1]=[P:2][#1:0]", // Rule 21: phosphonic acids "[O:1]=[P:2][#1:0]>>[#1:0][O:1][P:2]" // Rule 21: phosphonic acids #endif }; while(_currentLayer < layeredMolecules.layers) { Molecule mol; constructMolecule(mol, _currentLayer, false); while(true) { if(_currentRule == sizeof(reactionSmarts) / sizeof(reactionSmarts[0])) { _currentRule = 0; break; } const char *rule = reactionSmarts[_currentRule++]; QueryReaction reaction; AutoPtr<Scanner> _scanner(new BufferScanner(rule)); RSmilesLoader loader(*_scanner.get()); loader.smarts_mode = true; loader.loadQueryReaction(reaction); #if 0 ReactionTransformation rt; //rt.arom_options.method = AromaticityOptions::BASIC; //rt.arom_options.dearomatize_check = true; //rt.arom_options.unique_dearomatization = false; bool res = rt.transform(mol, reaction); ++_currentRule; if(res) { //mol.clone(layeredMolecules.asMolecule(), NULL, NULL); // add molecule as layer return true; } #else ReactionProductEnumerator rpe(reaction); rpe.addMonomer(0, mol); rpe.is_multistep_reaction = false; rpe.is_one_tube = true; rpe.is_self_react = true; rpe.max_deep_level = 1; rpe.max_product_count = 10; rpe.refine_proc = refine_proc; rpe.product_proc = product_proc; rpe.userdata = &layeredMolecules; int layersBefore = layeredMolecules.layers; rpe.buildProducts(); if(layersBefore < layeredMolecules.layers) return false; #endif } ++_currentLayer; } return true; } bool TautomerEnumerator::aromatize() { return layeredMolecules.aromatize(AromaticityOptions()); } bool TautomerEnumerator::_aromatize(int from, int to) { return layeredMolecules.aromatize(from, to, AromaticityOptions()); } #ifdef USE_DEPRECATED_INCHI bool TautomerEnumerator::matchEdge(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; Breadcrumps &breadcrumps = *(Breadcrumps *)userdata; int forwardSubBondOrder = breadcrumps.forwardEdgesHistory.size() % 2 == 0 ? 1 : 2; int backwardSubBondOrder = breadcrumps.backwardEdgesHistory.size() % 2 == 0 ? 2 : 1; const Dbitset &forwardMask = layeredMolecules.getBondMask(super_idx, forwardSubBondOrder); const Dbitset &backwardMask = layeredMolecules.getBondMask(super_idx, backwardSubBondOrder); return breadcrumps.forwardMask.intersects(forwardMask) || breadcrumps.backwardMask.intersects(backwardMask); } bool TautomerEnumerator::matchVertex(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { // The first vertice matched shall be the mobile hydrogen position. Breadcrumps &breadcrumps = *(Breadcrumps *)userdata; LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; if (breadcrumps.nodesHistory.size() == 0) return layeredMolecules.isMobilePosition(super_idx); return true; } void TautomerEnumerator::edgeAdd(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; Breadcrumps &breadcrumps = *(Breadcrumps *)userdata; int forwardSubBondOrder = breadcrumps.forwardEdgesHistory.size() % 2 == 0 ? 1 : 2; int backwardSubBondOrder = breadcrumps.backwardEdgesHistory.size() % 2 == 0 ? 2 : 1; const Dbitset &forwardMask = layeredMolecules.getBondMask(super_idx, forwardSubBondOrder); const Dbitset &backwardMask = layeredMolecules.getBondMask(super_idx, backwardSubBondOrder); breadcrumps.edgesHistory.push(super_idx); breadcrumps.forwardEdgesHistory.expand(breadcrumps.forwardEdgesHistory.size() + 1); breadcrumps.forwardEdgesHistory.top().copy(breadcrumps.forwardMask); breadcrumps.backwardEdgesHistory.expand(breadcrumps.backwardEdgesHistory.size() + 1); breadcrumps.backwardEdgesHistory.top().copy(breadcrumps.backwardMask); breadcrumps.forwardMask.andWith(forwardMask); breadcrumps.backwardMask.andWith(backwardMask); } void TautomerEnumerator::vertexAdd(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; Breadcrumps &breadcrumps = *(Breadcrumps *)userdata; breadcrumps.nodesHistory.push(super_idx); if (breadcrumps.nodesHistory.size() > 1 && breadcrumps.nodesHistory.size() % 2 && layeredMolecules.isMobilePosition(super_idx)) { if (breadcrumps.forwardMask.complements(layeredMolecules.getMobilePositionOccupiedMask(super_idx))) { layeredMolecules.addLayersWithInvertedPath(breadcrumps.forwardMask, breadcrumps.edgesHistory, breadcrumps.nodesHistory.at(0), breadcrumps.nodesHistory.top(), true); } if (breadcrumps.backwardMask.intersects(layeredMolecules.getMobilePositionOccupiedMask(super_idx))) { Dbitset mask; mask.copy(breadcrumps.backwardMask); mask.andWith(layeredMolecules.getMobilePositionOccupiedMask(super_idx)); layeredMolecules.addLayersWithInvertedPath(mask, breadcrumps.edgesHistory, breadcrumps.nodesHistory.at(0), breadcrumps.nodesHistory.top(), false); } } else if (breadcrumps.nodesHistory.size() == 1) { breadcrumps.forwardMask.resize(layeredMolecules.layers); breadcrumps.backwardMask.resize(layeredMolecules.layers); breadcrumps.forwardMask.copy(layeredMolecules.getMobilePositionOccupiedMask(super_idx)); breadcrumps.backwardMask.set(0, layeredMolecules.layers); } } void TautomerEnumerator::vertexRemove(Graph &subgraph, int sub_idx, void *userdata) { Breadcrumps &breadcrumps = *(Breadcrumps *)userdata; if (breadcrumps.backwardEdgesHistory.size() > 0) { breadcrumps.edgesHistory.pop(); breadcrumps.forwardMask.copy(breadcrumps.forwardEdgesHistory.top()); breadcrumps.forwardEdgesHistory.pop(); breadcrumps.backwardMask.copy(breadcrumps.backwardEdgesHistory.top()); breadcrumps.backwardEdgesHistory.pop(); } breadcrumps.nodesHistory.pop(); } #endif void TautomerEnumerator::constructMolecule(Molecule &molecule, int layer, bool needAromatize) const { layeredMolecules.constructMolecule(molecule, layer, needAromatize); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_match.cpp����������������������������������������0000664�0000000�0000000�00000044313�12710376503�0024207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "graph/embedding_enumerator.h" #include "molecule/molecule.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_tautomer.h" #include "molecule/molecule_tautomer_utils.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/molecule_inchi.h" #include "molecule/molecule_layered_molecules.h" #include "molecule/molecule_tautomer_enumerator.h" using namespace indigo; class PathRulesChecker; CP_DEF(TautomerSearchContext); TautomerSearchContext::TautomerSearchContext (BaseMolecule &g1_, BaseMolecule &g2_, GraphDecomposer &decomposer1_, GraphDecomposer &decomposer2_, const PtrArray<TautomerRule> &rules_list_, const AromaticityOptions &arom_options) : g1(g1_), g2(g2_), decomposer1(decomposer1_), decomposer2(decomposer2_), CP_INIT, TL_CP_GET(h_rep_count_1), TL_CP_GET(h_rep_count_2), rules_list(rules_list_), force_hydrogens(false), ring_chain(false), rules(0), substructure(false), cb_check_rules(0), TL_CP_GET(dearomatizations), TL_CP_GET(core_1), TL_CP_GET(core_2), TL_CP_GET(chains_2), TL_CP_GET(edges_1), TL_CP_GET(edges_2), TL_CP_GET(edge_types_2), TL_CP_GET(n1), TL_CP_GET(n2) { this->arom_options = arom_options; if (g2.vertexCount() + g2.edgeCount() > 80) max_chains = 1; else if (g2.vertexCount() + g2.edgeCount() > 40) max_chains = 2; else max_chains = 0; dearomatizer.create(g2.asMolecule(), (int *)0, arom_options); dearomatizer->enumerateDearomatizations(dearomatizations); dearomatizationMatcher.create(dearomatizations, g2.asMolecule(), (int *)0); } TautomerSearchContext::~TautomerSearchContext () { } bool TautomerMatcher::_matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); QueryMolecule::Atom *atom = &query.getAtom(sub_idx); BaseMolecule &target = (BaseMolecule &)supergraph; if (!MoleculeSubstructureMatcher::matchQueryAtom(atom, (BaseMolecule &)supergraph, super_idx, 0, 0xFFFFFFFFUL)) return false; if (query.stereocenters.getType(sub_idx) > target.stereocenters.getType(super_idx)) return false; if (query.stereocenters.getType(sub_idx) > 0) // Perform hydrogen check only on stereocenters. // This is to avoid "cannot map pyramid" error. // Normally, hydrogen counters are not something to look after // when doing tautomer match.] if (!target.isPseudoAtom(super_idx) && !target.isRSite(super_idx) && !target.isTemplateAtom(super_idx)) if (query.getAtomMinH(sub_idx) > target.getAtomMaxH(super_idx)) return false; return true; } bool TautomerMatcher::_matchAtomsEx (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { return MoleculeExactMatcher::matchAtoms(((BaseMolecule &)subgraph).asMolecule(), (BaseMolecule &)supergraph, sub_idx, super_idx, 0xFFFFFFFFUL); } bool TautomerMatcher::matchBondsTau (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { BaseMolecule &molecule = (BaseMolecule &)supergraph; BaseMolecule &query = (BaseMolecule &)subgraph; // check 'zeroed' bond if (molecule.getBondOrder(super_idx) == -1 && molecule.possibleBondOrder(super_idx, BOND_SINGLE)) return false; /* if (sub_bond.type == BOND_AROMATIC || super_bond.type == BOND_AROMATIC) return true; if (sub_bond.type == super_bond.type) return true; */ int sub_bond_order = query.getBondOrder(sub_idx); int super_bond_order = molecule.getBondOrder(super_idx); if (sub_bond_order == super_bond_order) return true; TautomerMatcher::MatchData &d = *(TautomerMatcher::MatchData *)userdata; if (super_bond_order == BOND_AROMATIC && sub_bond_order != BOND_AROMATIC) return d.context.dearomatizationMatcher->isAbleToFixBond(super_idx, sub_bond_order); return false; } bool TautomerMatcher::matchBondsTauSub (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { BaseMolecule &molecule = (BaseMolecule &)supergraph; QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); if (MoleculeSubstructureMatcher::matchQueryBond(&query.getBond(sub_idx), molecule, sub_idx, super_idx, 0, 0xFFFFFFFFUL)) return true; int sub_bond_order = query.getBondOrder(sub_idx); int super_bond_order = molecule.getBondOrder(super_idx); if (super_bond_order == -1 && molecule.possibleBondOrder(super_idx, 1)) super_bond_order = 0; // single bond that can appear in tautomer chain if (sub_bond_order < 0) // query bond return false; // checked above in matchQueryBond() if (sub_bond_order != super_bond_order) { if (super_bond_order == BOND_AROMATIC) return true; if (sub_bond_order == BOND_AROMATIC) return false; if (abs(sub_bond_order - super_bond_order) == 1) return true; } return false; } bool TautomerMatcher::matchAtomsTau (BaseMolecule &g1, BaseMolecule &g2, int n1, int n2) { if (g1.isPseudoAtom(n1) || g2.isPseudoAtom(n2)) return false; if (g1.isTemplateAtom(n1) || g2.isTemplateAtom(n2)) return false; if (g1.isRSite(n1) || g2.isRSite(n2)) return false; return g1.getAtomNumber(n1) == g2.getAtomNumber(n2) && g1.possibleAtomIsotope(n1, g2.getAtomIsotope(n2)); } int TautomerMatcher::_remainderEmbedding (Graph &g1, Graph &g2, int *core1, int *core2, void *userdata) { TautomerMatcher::MatchData &d = *(TautomerMatcher::MatchData *)userdata; // Check there's no redundant (unmapped) bonds in target for (int i = g2.edgeBegin(); i < g2.edgeEnd(); i = g2.edgeNext(i)) { const Edge &edge = g2.getEdge(i); if (d.context.chains_2[edge.beg] > 0 && d.context.chains_2[edge.end] > 0) continue; if (core2[edge.beg] < 0) continue; if (core2[edge.end] < 0) continue; if (g1.findEdgeIndex(core2[edge.beg], core2[edge.end]) == -1) return 1; } if (!fixBondsNotInChains (d.context, core1, core2)) return 1; d.context.core_1.clear_resize(d.context.g1.vertexEnd()); d.context.core_2.clear_resize(d.context.g2.vertexEnd()); memcpy(d.context.core_1.ptr(), core1, sizeof(int) * d.context.core_1.size()); memcpy(d.context.core_2.ptr(), core2, sizeof(int) * d.context.core_2.size()); return 0; } int TautomerMatcher::_preliminaryEmbedding (Graph &g1, Graph &g2, int *core1, int *core2, void *userdata) { TautomerMatcher::MatchData &d = *(TautomerMatcher::MatchData *)userdata; QS_DEF(Array<int>, core_1); QS_DEF(Array<int>, core_2); core_1.copy(core1, d.context.initial_g1_vertexend); // can not use g1.vertexEnd() because atoms may have been // added to and removed from the query during the // TautomerChainChecker procedure. core_2.copy(core2, g2.vertexEnd()); TautomerChainChecker cha_che(d.context, core_1, core_2, d.start_path_number); if (cha_che.check()) return 1; return 0; } bool TautomerRule::check (BaseMolecule &molecule, int first_idx, int last_idx, char other_arom_first, char other_arom_last) const { if (first_idx != -1 && last_idx != -1) { int first_atom = molecule.getAtomNumber(first_idx); int last_atom = molecule.getAtomNumber(last_idx); if (list1.find(first_atom) >= 0) { if (aromaticity1 == -1 || (aromaticity1 == 1 && (atomInAromaticRing(molecule, first_idx) || other_arom_first == 1)) || (aromaticity1 == 0 && !atomInAromaticRing(molecule, first_idx))) { if (list2.find(last_atom) >= 0) { if (aromaticity2 == -1 || (aromaticity2 == 1 && (atomInAromaticRing(molecule, last_idx) || other_arom_last == 1)) || (aromaticity2 == 0 && !atomInAromaticRing(molecule, last_idx))) { return true; } } } } if (list2.find(first_atom) >= 0) { if (aromaticity2 == -1 || (aromaticity2 == 1 && (atomInAromaticRing(molecule, first_idx) || other_arom_first == 1)) || (aromaticity2 == 0 && !atomInAromaticRing(molecule, first_idx))) { if (list1.find(last_atom) >= 0) { if (aromaticity1 == -1 || (aromaticity1 == 1 && (atomInAromaticRing(molecule, last_idx) || other_arom_last == 1)) || (aromaticity1 == 0 && !atomInAromaticRing(molecule, last_idx))) { return true; } } } } return false; } else if (first_idx != -1 || last_idx != -1) { if (first_idx == -1) first_idx = last_idx; int first_atom = molecule.getAtomNumber(first_idx); if (list1.find(first_atom) >= 0) if (aromaticity1 == -1 || (aromaticity1 == 1 && (atomInAromaticRing(molecule, first_idx) || other_arom_first == 1)) || (aromaticity1 == 0 && !atomInAromaticRing(molecule, first_idx))) return true; if (list2.find(first_atom) >= 0) if (aromaticity2 == -1 || (aromaticity2 == 1 && (atomInAromaticRing(molecule, first_idx) || other_arom_first == 1)) || (aromaticity2 == 0 && !atomInAromaticRing(molecule, first_idx))) return true; return false; } return true; } bool TautomerRule::atomInAromaticRing (BaseMolecule &mol, int atom_idx) { if (atom_idx < 0) return true; return mol.getAtomAromaticity(atom_idx) == ATOM_AROMATIC; } TautomerMatcher::TautomerMatcher (TautomerSearchContext &context) : _d(context), _n_chains(0) { _d.start_path_number = 0; _d.context.chains_2.clear_resize(_d.context.g2.vertexEnd()); _d.context.chains_2.zerofill(); _d.context.core_1.clear_resize(_d.context.g1.vertexEnd()); _d.context.core_2.clear_resize(_d.context.g2.vertexEnd()); _d.context.initial_g1_vertexend = _d.context.g1.vertexEnd(); MoleculeSubstructureMatcher::markIgnoredHydrogens(_d.context.g1, _d.context.core_1.ptr(), EmbeddingEnumerator::UNMAPPED, EmbeddingEnumerator::IGNORE); MoleculeSubstructureMatcher::markIgnoredHydrogens(_d.context.g2, _d.context.core_2.ptr(), EmbeddingEnumerator::UNMAPPED, EmbeddingEnumerator::IGNORE); MoleculeTautomerUtils::countHReplacements(_d.context.g1, _d.context.h_rep_count_1); MoleculeTautomerUtils::countHReplacements(_d.context.g2, _d.context.h_rep_count_2); } TautomerMatcher::TautomerMatcher (TautomerSearchContext &context, int start_path_number, int n_chains) : _d(context), _n_chains(n_chains + 1) { _d.start_path_number = start_path_number; } TautomerMatcher::~TautomerMatcher () { } bool TautomerMatcher::_checkInterPathBonds () { int idx1, idx2; BaseMolecule &g1 = _d.context.g1; BaseMolecule &g2 = _d.context.g2; // TODO: can check only for last chain for (idx1 = g1.edgeBegin(); idx1 < g1.edgeEnd(); idx1 = g1.edgeNext(idx1)) { const Edge &edge = g1.getEdge(idx1); if (_d.context.core_1[edge.beg] < 0) continue; if (_d.context.core_1[edge.end] < 0) continue; if (abs(_d.context.chains_2[_d.context.core_1[edge.beg]] - _d.context.chains_2[_d.context.core_1[edge.end]]) != 1) { idx2 = g2.findEdgeIndex(_d.context.core_1[edge.beg], _d.context.core_1[edge.end]); if (idx2 == -1 || !matchBondsTau(g1, g2, idx1, idx2, &_d)) return false; } } if (!_d.context.substructure) for (idx2 = g2.edgeBegin(); idx2 < g2.edgeEnd(); idx2 = g2.edgeNext(idx2)) { const Edge &edge = g2.getEdge(idx2); if (_d.context.core_2[edge.beg] < 0) continue; if (_d.context.core_2[edge.end] < 0) continue; if (abs(_d.context.chains_2[edge.beg] - _d.context.chains_2[edge.end]) != 1) { idx1 = g1.findEdgeIndex(_d.context.core_2[edge.beg], _d.context.core_2[edge.end]); if (idx1 == -1 || !matchBondsTau(g1, g2, idx1, idx2, &_d)) return false; } } return true; } bool TautomerMatcher::nextPair (int &n1, int &n2, int &h_diff, int prev_n1, int prev_n2) { if (prev_n1 == -1) n1 = _d.context.g1.vertexBegin(); if (prev_n2 == -1) n2 = _d.context.g2.vertexBegin(); else n2 = _d.context.g2.vertexNext(prev_n2); for (; n1 < _d.context.g1.vertexEnd(); n1 = _d.context.g1.vertexNext(n1)) { for (; n2 < _d.context.g2.vertexEnd(); n2 = _d.context.g2.vertexNext(n2)) if (isFeasiblePair(n1, n2, h_diff)) return true; n2 = _d.context.g2.vertexBegin(); } return false; } bool TautomerMatcher::isFeasiblePair (int n1, int n2, int &h_diff) { if (_d.context.core_1[n1] != EmbeddingEnumerator::UNMAPPED || _d.context.core_2[n2] != EmbeddingEnumerator::UNMAPPED) return false; int charge1 = _d.context.g1.getAtomCharge(n1); int charge2 = _d.context.g2.getAtomCharge(n2); if (!matchAtomsTau(_d.context.g1, _d.context.g2, n1, n2)) return false; int h_count_1 = _d.context.g1.getAtomTotalH(n1); int h_count_2 = _d.context.g2.getAtomTotalH(n2); if (!_d.context.force_hydrogens) { h_count_1 += _d.context.h_rep_count_1[n1]; h_count_2 += _d.context.h_rep_count_2[n2]; } else if (charge1 != charge2) return false; h_diff = h_count_1 - h_count_2; if (abs(h_diff) != 1) return false; return true; } void TautomerMatcher::addPair (int n1, int n2, int arom_bond_idx2, int bond_type2) { _n2 = _d.context.core_1[n1] = n2; _n1 = _d.context.core_2[n2] = n1; _d.context.chains_2[n2] = _d.start_path_number; _d.start_path_number++; _bond_idx2 = arom_bond_idx2; if (_bond_idx2 >= 0) _d.context.dearomatizationMatcher->fixBond(_bond_idx2, bond_type2); #ifdef TRACE_TAUTOMER_MATCHING for (int i = 0; i < _d.start_path_number; i++) printf(" "); printf("%2d\n", n1 + 1); for (int i = 0; i < _d.start_path_number; i++) printf(" "); printf("%2d\n", n2 + 1); #endif } void TautomerMatcher::restore () { _d.context.core_1[_n1] = EmbeddingEnumerator::UNMAPPED; _d.context.core_2[_n2] = EmbeddingEnumerator::UNMAPPED; _d.context.chains_2[_n2] = 0; if (_bond_idx2 >= 0) _d.context.dearomatizationMatcher->unfixBond(_bond_idx2); } bool TautomerMatcher::findMatch () { int n1 = -1, n2 = -1; int h_difference; BaseMolecule &g1 = _d.context.g1; BaseMolecule &g2 = _d.context.g2; if (!_checkInterPathBonds()) return true; EmbeddingEnumerator ee(g2); ee.setSubgraph(g1); int i; for (i = g1.vertexBegin(); i < g1.vertexEnd(); i = g1.vertexNext(i)) { int val = _d.context.core_1[i]; if (val == EmbeddingEnumerator::IGNORE) ee.ignoreSubgraphVertex(i); else if (val >= 0) ee.unsafeFix(i, val); } for (i = g2.vertexBegin(); i < g2.vertexEnd(); i = g2.vertexNext(i)) { int val = _d.context.core_2[i]; if (val == EmbeddingEnumerator::IGNORE) ee.ignoreSupergraphVertex(i); } ee.userdata = &_d; if (_d.context.substructure) { ee.userdata = &_d; ee.cb_match_edge = matchBondsTauSub; ee.cb_match_vertex = _matchAtoms; ee.cb_embedding = _preliminaryEmbedding; if (!ee.process()) return false; } else { ee.cb_match_edge = matchBondsTau; ee.cb_match_vertex = _matchAtomsEx; ee.cb_embedding = _remainderEmbedding; if (!ee.process()) return false; if (_d.context.max_chains > 0 && _n_chains >= _d.context.max_chains) return true; while (nextPair(n1, n2, h_difference, n1, n2)) { TautomerChainFinder pe(_d.context, h_difference, _d.start_path_number, _n_chains); //pe.addPair(n1, n2, false, -1, 0); pe.addPair(n1, n2, _d.start_path_number != 0 || !_d.context.ring_chain, -1, 0); if (!pe.enumeratePaths()) return false; pe.restore(); } } return true; } bool TautomerMatcher::fixBondsNotInChains (TautomerSearchContext &context, const int *core1, const int *core2) { bool ok = true; QS_DEF(Array<int>, fixed_bonds); fixed_bonds.clear(); for (int i = context.g2.edgeBegin(); i < context.g2.edgeEnd(); i = context.g2.edgeNext(i)) { const Edge &edge2 = context.g2.getEdge(i); if (context.g2.getBondOrder(i) != BOND_AROMATIC) continue; if (abs(context.chains_2[edge2.beg] - context.chains_2[edge2.end]) == 1) continue; if (core2[edge2.beg] < 0 || core2[edge2.end] < 0) continue; const Vertex &vert_beg1 = context.g1.getVertex(core2[edge2.beg]); int nei_idx1; if ((nei_idx1 = vert_beg1.findNeiVertex(core2[edge2.end])) < 0) continue; int edge_idx1 = vert_beg1.neiEdge(nei_idx1); // query bond? if (context.g1.getBondOrder(edge_idx1) == -1) continue; int type1; if ((type1 = context.g1.getBondOrder(edge_idx1)) == BOND_AROMATIC) continue; if (!context.dearomatizationMatcher->isAbleToFixBond(i, type1)) { ok = false; break; } else { context.dearomatizationMatcher->fixBond(i, type1); fixed_bonds.push(i); } } if (!ok) { for (int i = 0; i < fixed_bonds.size(); i++) context.dearomatizationMatcher->unfixBond(fixed_bonds[i]); } return ok; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_matcher.cpp��������������������������������������0000664�0000000�0000000�00000014151�12710376503�0024533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "graph/embedding_enumerator.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_tautomer_matcher.h" #include "molecule/molecule_tautomer_utils.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(MoleculeTautomerMatcher, "molecule tautomer matcher"); MoleculeTautomerMatcher::MoleculeTautomerMatcher (Molecule &target, bool substructure) : _substructure(substructure), _force_hydrogens(false), _ring_chain(false), _rules(0), _rules_list(0), _context(0), _target_src(target), _query(0) { if (substructure) { _target.create(target); _supermol = _target.get(); } else _supermol = ⌖ _target_decomposer.create(*_supermol); _target_decomposer->decompose(); highlight = false; } void MoleculeTautomerMatcher::setRulesList (const PtrArray<TautomerRule> *rules_list) { _rules_list = rules_list; } void MoleculeTautomerMatcher::setRules (int rules_set, bool force_hydrogens, bool ring_chain, TautomerMethod method) { _rules = rules_set; _force_hydrogens = force_hydrogens; _ring_chain = ring_chain; _method = method; } void MoleculeTautomerMatcher::setQuery (BaseMolecule &query) { if (_substructure) _query.reset(new QueryMolecule()); else _query.reset(new Molecule()); _query->clone(query, 0, 0, 0); _query_decomposer.create(query); _query_decomposer->decompose(); } int MoleculeTautomerMatcher::countNonHydrogens (BaseMolecule &molecule) { int count = 0; for (int i = molecule.vertexBegin(); i < molecule.vertexEnd(); i = molecule.vertexNext(i)) if (molecule.getAtomNumber(i) != ELEM_H) count++; return count; } bool MoleculeTautomerMatcher::find () { if (!_substructure) { int nh_count1 = countNonHydrogens(*_query.get()); int nh_count2 = countNonHydrogens(*_supermol); if (nh_count1 != nh_count2) return false; } const PtrArray<TautomerRule> rules_list; const PtrArray<TautomerRule> *p_rules_list = _rules_list; if (p_rules_list == 0) p_rules_list = &rules_list; _context.reset(new TautomerSearchContext(*_query.get(), *_supermol, *_query_decomposer.get(), *_target_decomposer.get(), *p_rules_list, arom_options)); _context->force_hydrogens = _force_hydrogens; _context->ring_chain = _ring_chain; _context->rules = _rules; _context->method = _method; if (_rules != 0 && _rules_list != 0 && _rules_list->size() != 0) _context->cb_check_rules = _checkRules; _context->substructure = _substructure; TautomerMatcher matcher(*_context.get()); bool found = true; if (matcher.findMatch()) found = false; if (found && highlight) { _target_src.unhighlightAll(); if (_substructure) MoleculeTautomerUtils::highlightChains(*_query.get(), *_supermol, _context->chains_2, _context->core_2.ptr()); else MoleculeTautomerUtils::highlightChains(*_query.get(), *_supermol, _context->chains_2, 0); if (_substructure) _target_src.highlightSubmolecule(*_supermol, _target->getInvMapping().ptr(), true); } return found; } const int * MoleculeTautomerMatcher::getQueryMapping () { return _context->core_1.ptr(); } void MoleculeTautomerMatcher::parseConditions (const char *tautomer_text, int &rules, bool &force_hydrogens, bool &ring_chain, TautomerMethod &method) { if (tautomer_text == 0) throw Error("zero pointer passed to parseConditions()"); rules = 0; force_hydrogens = false; ring_chain = false; method = BASIC; BufferScanner scanner(tautomer_text); QS_DEF(Array<char>, word); while (1) { int i; scanner.skipSpace(); if (scanner.isEOF()) break; scanner.readWord(word, 0); if (word.size() < 2) throw Error("internal error on token reading"); if (strcasecmp(word.ptr(), "TAU") == 0) continue; if (strncasecmp(word.ptr(), "INCHI", 5) == 0) { method = INCHI; continue; } if (strncasecmp(word.ptr(), "RSMARTS", 7) == 0) { method = RSMARTS; continue; } if (strcasecmp(word.ptr(), "HYD") == 0) { force_hydrogens = true; continue; } if (strcasecmp(word.ptr(), "R-C") == 0) { ring_chain = true; continue; } if (strcasecmp(word.ptr(), "R*") == 0) { rules = 0xFFFFFF; continue; } if (word[0] == 'R' || word[0] == 'r') { if (isdigit(word[1])) { i = atoi(word.ptr() + 1); if (i > 0 && i <= 32) { rules |= 1 << (i - 1); continue; } } } throw Error("parseConditions(): unknown token %s", word.ptr()); } } bool MoleculeTautomerMatcher::_checkRules(TautomerSearchContext &context, int first1, int first2, int last1, int last2) { for (int i = 0; i < context.rules_list.size(); i++) if (context.rules & (1 << i) && context.rules_list[i] != 0) { if (context.rules_list[i]->check(context.g1, first1, last1, TautomerRule::atomInAromaticRing(context.g2, first2), TautomerRule::atomInAromaticRing(context.g2, last2)) && context.rules_list[i]->check(context.g2, first2, last2, TautomerRule::atomInAromaticRing(context.g1, first1), TautomerRule::atomInAromaticRing(context.g1, last1))) return true; } return false; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_substructure_matcher.cpp�������������������������0000664�0000000�0000000�00000022272�12710376503�0027370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_tautomer_substructure_matcher.h" #include "molecule/query_molecule.h" using namespace indigo; IMPL_ERROR(MoleculeTautomerSubstructureMatcher, "molecule tautomer substructure matcher"); CP_DEF(MoleculeTautomerSubstructureMatcher); MoleculeTautomerSubstructureMatcher::MoleculeTautomerSubstructureMatcher(BaseMolecule &target, TautomerMethod method) : _tautomerEnumerator(target.asMolecule(), method), CP_INIT { _allLayersFound = false; _layerBeg = 0; _layerEnd = 1; // Instead of full enumeration of tautomers we are using lazy enumeration now. //_tautomerEnumerator.enumerateAll(false); find_all_embeddings = false; find_unique_embeddings = false; find_unique_by_edges = false; save_for_iteration = false; _needAromatize = target.asMolecule().isAromatized(); } MoleculeTautomerSubstructureMatcher::~MoleculeTautomerSubstructureMatcher() { } void MoleculeTautomerSubstructureMatcher::setQuery(QueryMolecule &query) { _query = &query; // We should check that query has any trace of aromatization. Otherwise it has no sense to perform expensive procedure of _tautomerEnumerator aromatization. //if(query.isAromatic()) { _tautomerEnumerator.aromatize(); } QS_DEF(Array<int>, ignored); ignored.clear_resize(_query->vertexEnd()); ignored.zerofill(); if (_ee.get() != 0) _ee.free(); _ee.create(_tautomerEnumerator.layeredMolecules); _ee->cb_match_vertex = _matchAtomsHyper; _ee->cb_match_edge = _matchBondsSubHyper; _ee->cb_edge_add = _edgeAddHyper; _ee->cb_vertex_add = NULL; _ee->cb_vertex_remove = _vertexRemoveHyper; _ee->cb_embedding = _preliminaryEmbeddingHyper; _breadcrumps.self = this; _ee->userdata = &_breadcrumps; _ee->setSubgraph(*_query); _embeddings_storage.free(); _masks.clear(); } bool MoleculeTautomerSubstructureMatcher::_matchAtoms(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); QueryMolecule::Atom *atom = &query.getAtom(sub_idx); BaseMolecule &target = (BaseMolecule &)supergraph; if (!MoleculeSubstructureMatcher::matchQueryAtom(atom, (BaseMolecule &)supergraph, super_idx, 0, 0xFFFFFFFFUL)) return false; if (query.stereocenters.getType(sub_idx) > target.stereocenters.getType(super_idx)) return false; if (query.stereocenters.getType(sub_idx) > 0) if (!target.isPseudoAtom(super_idx) && !target.isRSite(super_idx)) if (query.getAtomMinH(sub_idx) > target.getAtomMaxH(super_idx)) return false; return true; } bool MoleculeTautomerSubstructureMatcher::_matchAtomsHyper(Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { // Currently use common atom match procedure return _matchAtoms(subgraph, supergraph, core_sub, sub_idx, super_idx, userdata); } bool MoleculeTautomerSubstructureMatcher::_matchBondsSubHyper(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { SubstructureSearchBreadcrumps &breadcrumps = *(SubstructureSearchBreadcrumps *)userdata; LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); int sub_bond_order = query.getBondOrder(sub_idx); const Dbitset &mask = layeredMolecules.getBondMask(super_idx, sub_bond_order); return mask.intersects(breadcrumps.mask); } void MoleculeTautomerSubstructureMatcher::_edgeAddHyper(Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { SubstructureSearchBreadcrumps &breadcrumps = *(SubstructureSearchBreadcrumps *)userdata; LayeredMolecules &layeredMolecules = (LayeredMolecules &)supergraph; QueryMolecule &query = ((BaseMolecule &)subgraph).asQueryMolecule(); int sub_bond_order = query.getBondOrder(sub_idx); const Dbitset &mask = layeredMolecules.getBondMask(super_idx, sub_bond_order); breadcrumps.maskHistory.expand(breadcrumps.maskHistory.size() + 1); breadcrumps.maskHistory.top().copy(breadcrumps.mask); breadcrumps.mask.andWith(mask); } void MoleculeTautomerSubstructureMatcher::_vertexRemoveHyper(Graph &subgraph, int sub_idx, void *userdata) { SubstructureSearchBreadcrumps &breadcrumps = *(SubstructureSearchBreadcrumps *)userdata; if (breadcrumps.maskHistory.size()) { breadcrumps.mask.copy(breadcrumps.maskHistory.top()); breadcrumps.maskHistory.pop(); } } int MoleculeTautomerSubstructureMatcher::_preliminaryEmbeddingHyper(Graph &g1, Graph &g2, int *core_sub, int *core_super, void *userdata) { SubstructureSearchBreadcrumps &breadcrumps = *(SubstructureSearchBreadcrumps *)userdata; MoleculeTautomerSubstructureMatcher *self = breadcrumps.self; Dbitset &mask = breadcrumps.mask; return self->_embedding_common(core_sub, core_super, mask); return 0; } bool MoleculeTautomerSubstructureMatcher::find() { if (_query == 0) throw Error("no query"); _createEmbeddingsStorage(); _breadcrumps.maskHistory.clear(); _breadcrumps.mask.resize(_tautomerEnumerator.layeredMolecules.layers); _breadcrumps.mask.zeroFill(); _breadcrumps.mask.flip(_layerBeg, _layerEnd); int result = _ee->process(); while(result == 1) { _layerBeg = _layerEnd; if(!_tautomerEnumerator.enumerateLazy()) { _layerEnd = _tautomerEnumerator.layeredMolecules.layers; _breadcrumps.maskHistory.clear(); _breadcrumps.mask.resize(_tautomerEnumerator.layeredMolecules.layers); _breadcrumps.mask.zeroFill(); _breadcrumps.mask.flip(_layerBeg, _layerEnd); _ee->setSubgraph(*_query); result = _ee->process(); } else { break; } } return result == 0; } bool MoleculeTautomerSubstructureMatcher::findNext() { bool found = _ee->processNext(); while(!found) { _layerBeg = _layerEnd; if(!_tautomerEnumerator.enumerateLazy()) { _layerEnd = _tautomerEnumerator.layeredMolecules.layers; _breadcrumps.maskHistory.clear(); _breadcrumps.mask.resize(_tautomerEnumerator.layeredMolecules.layers); _breadcrumps.mask.zeroFill(); _breadcrumps.mask.flip(_layerBeg, _layerEnd); _ee->setSubgraph(*_query); found = _ee->process() != 1; } else { break; } } return found; } void MoleculeTautomerSubstructureMatcher::_createEmbeddingsStorage() { _embeddings_storage.create(); _embeddings_storage->unique_by_edges = find_unique_by_edges; _embeddings_storage->save_edges = save_for_iteration; _embeddings_storage->save_mapping = save_for_iteration; _embeddings_storage->check_uniquencess = false;// find_unique_embeddings; } int MoleculeTautomerSubstructureMatcher::_embedding_common (int *core_sub, int *core_super, Dbitset &mask) { QueryMolecule &query = *_query; if (find_unique_embeddings || save_for_iteration) { if (!_embeddings_storage->addEmbedding(_tautomerEnumerator.layeredMolecules, query, core_sub)) // This match has already been handled return 1; _masks.push(); _masks.top().copy(mask); if(_needAromatize) { int layer = _masks.top().nextSetBit(0); int enumPosition = _tautomerEnumerator.beginAromatized(); while(layer != -1) { // Magic! layer is a non-negative number. We know that we enumerate aromatized tautomers. That means that the range is [-1, -2, -3, ...] if(!_tautomerEnumerator.isValid(-1 - layer)) { _masks.top().reset(layer); } else { _tautomerEnumerator.next(-1 - layer); } layer = _masks.top().nextSetBit(layer + 1); } } } return 0; } const int * MoleculeTautomerSubstructureMatcher::getQueryMapping () { return _ee->getSubgraphMapping(); } const int * MoleculeTautomerSubstructureMatcher::getTargetMapping() { return _ee->getSupergraphMapping(); } const GraphEmbeddingsStorage& MoleculeTautomerSubstructureMatcher::getEmbeddingsStorage() const { return _embeddings_storage.ref(); } const Dbitset& MoleculeTautomerSubstructureMatcher::getMask(int ind) const { return _masks.at(ind); } void MoleculeTautomerSubstructureMatcher::getTautomerFound(Molecule& mol, int enumInd, int tauInd) const { const Dbitset &mask = _masks.at(enumInd); int layer = mask.nextSetBit(tauInd); return _tautomerEnumerator.constructMolecule(mol, layer, _needAromatize); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_superstructure.cpp�������������������������������0000664�0000000�0000000�00000024742�12710376503�0026236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_tautomer.h" #include "base_cpp/queue.h" #include "molecule/molecule.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/elements.h" #include "molecule/molecule_fingerprint.h" using namespace indigo; // Tautomer superstrucure - molecule with all bonds that can appear in tautomer // This structure is needed to enumerate all submolecule of a tautomer // Tautomer template rules: // A - any atom // G - heteroatom: N, O, S, P, As, Sb, Se, Te // Tautomer bond appear/disappear template: // G=C G-H <--> G-C-G-H // (A=A-)nG=C G-H <--> H-(A-A=)nG-C-G // Atoms can't have any other double/triple bonds // Let's say thet C can emit bond and G with H can accept bond CP_DEF(TautomerSuperStructure); TautomerSuperStructure::TautomerSuperStructure (Molecule &mol) : CP_INIT, TL_CP_GET(_atomsEmitBond), TL_CP_GET(_atomsAcceptBond), TL_CP_GET(_isBondAttachedArray), TL_CP_GET(_mapping), TL_CP_GET(_inv_mapping), TL_CP_GET(_edge_mapping), TL_CP_GET(_total_h) { int i; _inside_ctor = true; clone(mol, &_inv_mapping, &_mapping); _edge_mapping.clear_resize(edgeEnd()); _edge_mapping.fffill(); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); _edge_mapping[findEdgeIndex(_inv_mapping[edge.beg], _inv_mapping[edge.end])] = i; } // Collect atom properties _collectAtomProperties(); // Detect distances from _atomsEmitBond elements to _atomsAcceptBond elements QS_DEF(Array<int>, distancesMatrix); distancesMatrix.resize(_atomsEmitBond.size() * _atomsAcceptBond.size()); for (int i = 0; i < _atomsEmitBond.size(); i++) { int *result = distancesMatrix.ptr() + _atomsAcceptBond.size() * i; _findMinDistance(_atomsEmitBond[i], 6, _atomsAcceptBond, result); } QS_DEF(Array<int>, attachedBonds); attachedBonds.clear(); for (int i = 0; i < _atomsEmitBond.size(); i++) for (int j = 0; j < _atomsAcceptBond.size(); j++) { int v1 = _atomsEmitBond[i]; int v2 = _atomsAcceptBond[j]; if (findEdgeIndex(v1, v2) != -1) continue; // Check new loop size: 5 or 6 int size = distancesMatrix[_atomsAcceptBond.size() * i + j]; if (size != 4 && size != 5) continue; attachedBonds.push(addEdge(v1, v2)); } _isBondAttachedArray.resize(edgeEnd()); _isBondAttachedArray.zerofill(); for (int i = 0; i < attachedBonds.size(); i++) _isBondAttachedArray[attachedBonds[i]] = true; _inside_ctor = false; } TautomerSuperStructure::~TautomerSuperStructure () { } void TautomerSuperStructure::clear () { if (_inside_ctor) Molecule::clear(); else throw Exception("clear(): not supported"); } int TautomerSuperStructure::getBondOrder (int idx) { if (!_inside_ctor && _isBondAttachedArray[idx]) return -1; return Molecule::getBondOrder(idx); } int TautomerSuperStructure::getBondTopology (int idx) { if (!_inside_ctor &&_isBondAttachedArray[idx]) return -1; return Molecule::getBondTopology(idx); } bool TautomerSuperStructure::possibleBondOrder (int idx, int order) { if (!_inside_ctor &&_isBondAttachedArray[idx]) return order == 0 || order == BOND_SINGLE; return Molecule::possibleBondOrder(idx, order); } int TautomerSuperStructure::getSubgraphType (const Array<int> &vertices, const Array<int> &edges) { // For any atoms number of attached bonds must be 0 or 1 QS_DEF(Array<int>, per_vertex_attached_bonds); per_vertex_attached_bonds.clear_resize(vertexEnd()); per_vertex_attached_bonds.zerofill(); int attached_bonds = 0; for (int i = 0; i < edges.size(); i++) { int edge_index = edges[i]; if (!_isBondAttachedArray[edge_index]) continue; const Edge &edge = getEdge(edge_index); per_vertex_attached_bonds[edge.beg]++; per_vertex_attached_bonds[edge.end]++; if (per_vertex_attached_bonds[edge.beg] > 1 || per_vertex_attached_bonds[edge.end] > 1) return NONE; attached_bonds++; } if (attached_bonds == 0) return ORIGINAL; return TAUTOMER; } void TautomerSuperStructure::_getDoubleBondsCount (int i, int &double_count, int &arom_count) { double_count = 0; arom_count = 0; const Vertex &vertex = getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int idx = vertex.neiEdge(j); if (idx >= _edge_mapping.size()) continue; if (Molecule::getBondOrder(idx) == BOND_DOUBLE || Molecule::getBondOrder(idx) == BOND_TRIPLE) double_count++; else if (Molecule::getBondOrder(idx) == BOND_AROMATIC) arom_count++; } } bool TautomerSuperStructure::_isAcceptingHeteroatom (int idx) { static const int list[] = { ELEM_N, ELEM_O, ELEM_P, ELEM_S, ELEM_As, ELEM_Se, ELEM_Sb, ELEM_Te }; return atomNumberBelongs(idx, list, NELEM(list)); } bool TautomerSuperStructure::_isEmittingHeteroatom (int idx) { static const int list[] = { ELEM_O, ELEM_N }; return atomNumberBelongs(idx, list, NELEM(list)); } int TautomerSuperStructure::getAtomTotalH (int idx) { return _total_h[idx]; } void TautomerSuperStructure::_collectAtomProperties (void) { _atomsAcceptBond.clear(); _atomsEmitBond.clear(); _total_h.clear_resize(vertexEnd()); for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) { _total_h[i] = 0; if (!isPseudoAtom(i) && !isRSite(i) && !isTemplateAtom(i)) _total_h[i] = Molecule::getAtomTotalH(i); int double_bonds_count, arom_bonds_count; _getDoubleBondsCount(i, double_bonds_count, arom_bonds_count); // Detect atom type if (double_bonds_count == 0 && _isAcceptingHeteroatom(i)) { bool have_hydrogen = (getAtomTotalH(i) != 0); if (arom_bonds_count != 0) have_hydrogen = true; if (have_hydrogen) _atomsAcceptBond.push(i); } // Aromatic bond can be double if ((double_bonds_count == 1 || arom_bonds_count != 0) && getAtomNumber(i) == ELEM_C) { int nei_heteroatoms = _hetroatomsCount(i); if (nei_heteroatoms != 1) continue; // Check all possible double bond neighbors to have emit atom type const Vertex &vertex = getVertex(i); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { // Double bond can jump from non-heteroatom to heteroatom // that why only existance of heteroatom and existance of any attached double bond // is required (double bond count already has been checked above) if (!_isEmittingHeteroatom(vertex.neiVertex(j))) continue; int nei_double_bonds_count, nei_arom_bonds_count; _getDoubleBondsCount(vertex.neiVertex(j), nei_double_bonds_count, nei_arom_bonds_count); if (nei_double_bonds_count <= 1) { _atomsEmitBond.push(i); break; } } } } } // Find minimum distance between source vertex and vertices from dest array // and check tautomer chain property: // * only one bond can be in ring // * if no one bond is in ring then only one bond can be double and no one can be triple void TautomerSuperStructure::_findMinDistance (int source, int maxDist, Array<int> &dest, int *result) { QS_DEF(Array<int>, distances); QS_DEF(Array<int>, parents); distances.resize(vertexEnd()); parents.resize(vertexEnd()); // Fill distances by infinity const int INFINITY = 100000; for (int j = 0; j < distances.size(); j++) distances[j] = INFINITY; QS_DEF(Queue<int>, front); front.clear(); front.setLength(vertexEnd()); distances[source] = 0; parents[source] = -1; front.push(source); while (!front.isEmpty()) { int active = front.pop(); if (distances[active] == maxDist) break; const Vertex &vertex = getVertex(active); for (int j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int vn = vertex.neiVertex(j); if (distances[vn] == INFINITY) { distances[vn] = distances[active] + 1; parents[vn] = active; front.push(vn); } } } for (int j = 0; j < dest.size(); j++) { // Check chain if (distances[dest[j]] != INFINITY) { int inRingCount = 0; int doubleBondsCount = 0, tripleBondsCount = 0; int cur = dest[j]; int prev = parents[cur]; while (prev != -1) { int edge_idx = findEdgeIndex(cur, prev); if (Molecule::getBondTopology(edge_idx) == TOPOLOGY_RING) inRingCount++; if (Molecule::getBondOrder(edge_idx) == BOND_DOUBLE) doubleBondsCount++; if (Molecule::getBondOrder(edge_idx) == BOND_TRIPLE) tripleBondsCount++; cur = prev; prev = parents[prev]; } if (inRingCount > 1) distances[dest[j]] = 2 * INFINITY; else if (inRingCount == 0) if (doubleBondsCount > 1 || tripleBondsCount > 0) distances[dest[j]] = 2 * INFINITY; } result[j] = distances[dest[j]]; } } int TautomerSuperStructure::_hetroatomsCount (int idx) { const Vertex &vertex = getVertex(idx); int count = 0; for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (_isAcceptingHeteroatom(vertex.neiVertex(i))) count++; } return count; } const int * TautomerSuperStructure::getMapping () { return _mapping.ptr(); } const Array<int> & TautomerSuperStructure::getInvMapping () { return _inv_mapping; } bool TautomerSuperStructure::isZeroedBond (int idx) { return _isBondAttachedArray[idx]; } ������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tautomer_utils.cpp����������������������������������������0000664�0000000�0000000�00000005531�12710376503�0024252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/molecule_tautomer_utils.h" #include "molecule/elements.h" using namespace indigo; bool MoleculeTautomerUtils::_isRepMetal (int elem) { static const int list[] = {ELEM_Li, ELEM_Na, ELEM_K, ELEM_Rb, ELEM_Cs, ELEM_Be, ELEM_Mg, ELEM_Ca, ELEM_Sr, ELEM_Ba}; for (int i = 0; i < (int)NELEM(list); i++) if (elem == list[i]) return true; return false; } // Count potential hydrogens (+, - charges or metal bonds) void MoleculeTautomerUtils::countHReplacements (BaseMolecule &g, Array<int> &h_rep_count) { h_rep_count.clear_resize(g.vertexEnd()); for (int i = g.vertexBegin(); i < g.vertexEnd(); i = g.vertexNext(i)) { const Vertex &vertex = g.getVertex(i); h_rep_count[i] = 0; for (int bond_idx = vertex.neiBegin(); bond_idx != vertex.neiEnd(); bond_idx = vertex.neiNext(bond_idx)) if (_isRepMetal(g.getAtomNumber(vertex.neiVertex(bond_idx)))) { int bond_type = g.getBondOrder(vertex.neiEdge(bond_idx)); if (bond_type != BOND_AROMATIC) h_rep_count[i] += bond_type; } // + or - charge also count as potential hydrogens int charge = g.getAtomCharge(i); if (charge != CHARGE_UNKNOWN) h_rep_count[i] += abs(charge); } } // If core_2 != 0 highlights g1 too void MoleculeTautomerUtils::highlightChains (BaseMolecule &g1, BaseMolecule &g2, const Array<int> &chains_2, const int *core_2) { int i; for (i = g2.vertexBegin(); i < g2.vertexEnd(); i = g2.vertexNext(i)) { if (chains_2[i] > 0 || (core_2 != 0 && core_2[i] >= 0)) g2.highlightAtom(i); } for (i = g2.edgeBegin(); i < g2.edgeEnd(); i = g2.edgeNext(i)) { const Edge &edge = g2.getEdge(i); // zeroed bond? if (g2.getBondOrder(i) == -1 && g2.possibleBondOrder(i, BOND_SINGLE)) continue; if (chains_2[edge.beg] > 0 && chains_2[edge.end] > 0 && abs(chains_2[edge.beg] - chains_2[edge.end]) == 1) g2.highlightBond(i); else if (core_2 != 0 && core_2[edge.beg] >= 0 && core_2[edge.end] >= 0) { if (g1.findEdgeIndex(core_2[edge.beg], core_2[edge.end]) != -1) g2.highlightBond(i); } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molecule_tgroups.cpp�����������������������������������������������0000664�0000000�0000000�00000005137�12710376503�0022677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule_tgroups.h" #include "base_cpp/auto_ptr.h" #include "molecule/base_molecule.h" #include "base_cpp/scanner.h" using namespace indigo; TGroup::TGroup () { } TGroup::~TGroup () { } void TGroup::clear() { } int TGroup::cmp (TGroup &tg1, TGroup &tg2, void *context) { if (tg1.fragment == 0) return -1; if (tg2.fragment == 0) return 1; return tg2.fragment->vertexCount() - tg1.fragment->vertexCount(); } void TGroup::copy (TGroup &other) { tgroup_class.copy(other.tgroup_class); tgroup_name.copy(other.tgroup_name); tgroup_alias.copy(other.tgroup_alias); tgroup_comment.copy(other.tgroup_comment); tgroup_id = other.tgroup_id; fragment = other.fragment->neu(); fragment->clone(*other.fragment, 0, 0); } IMPL_ERROR(MoleculeTGroups, "molecule tgroups"); MoleculeTGroups::MoleculeTGroups () { } MoleculeTGroups::~MoleculeTGroups () { } void MoleculeTGroups::clear () { _tgroups.clear(); } int MoleculeTGroups::begin () { return _tgroups.begin(); } int MoleculeTGroups::end () { return _tgroups.end(); } int MoleculeTGroups::next (int i) { return _tgroups.next(i); } void MoleculeTGroups::remove (int i) { return _tgroups.remove(i); } int MoleculeTGroups::addTGroup () { return _tgroups.add(new TGroup()); } TGroup & MoleculeTGroups::getTGroup (int idx) { return *_tgroups.at(idx); } void MoleculeTGroups::copyTGroupsFromMolecule (MoleculeTGroups &other) { for (int i = other.begin(); i != other.end(); i = other.next(i)) { TGroup &tgroup = other.getTGroup(i); int idx = addTGroup(); getTGroup(idx).copy(tgroup); } } int MoleculeTGroups::getTGroupCount () { return _tgroups.size(); } int MoleculeTGroups::findTGroup (const char *name) { for (int i = _tgroups.begin(); i != _tgroups.end(); i = _tgroups.next(i)) { TGroup &tgroup = *_tgroups.at(i); BufferScanner sc(tgroup.tgroup_name); if (sc.findWordIgnoreCase(name)) return i; } return -1; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molfile_loader.cpp�������������������������������������������������0000664�0000000�0000000�00000326112�12710376503�0022263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/scanner.h" #include "base_cpp/tlscont.h" #include "base_cpp/auto_ptr.h" #include "molecule/molfile_loader.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/elements.h" #include "molecule/smiles_loader.h" #define STRCMP(a, b) strncmp((a), (b), strlen(b)) using namespace indigo; IMPL_ERROR(MolfileLoader, "molfile loader"); CP_DEF(MolfileLoader); MolfileLoader::MolfileLoader (Scanner &scanner) : _scanner(scanner), CP_INIT, TL_CP_GET(_stereo_care_atoms), TL_CP_GET(_stereo_care_bonds), TL_CP_GET(_stereocenter_types), TL_CP_GET(_stereocenter_groups), TL_CP_GET(_sensible_bond_directions), TL_CP_GET(_ignore_cistrans), TL_CP_GET(_atom_types), TL_CP_GET(_hcount), TL_CP_GET(_sgroup_types), TL_CP_GET(_sgroup_mapping) { reaction_atom_mapping = 0; reaction_atom_inversion = 0; reaction_atom_exact_change = 0; reaction_bond_reacting_center = 0; _rgfile = false; treat_x_as_pseudoatom = false; skip_3d_chirality = false; ignore_noncritical_query_features = false; } void MolfileLoader::loadMolecule (Molecule &mol) { mol.clear(); _bmol = &mol; _mol = &mol; _qmol = 0; _loadMolecule(); if (mol.stereocenters.size() == 0 && !skip_3d_chirality) mol.stereocenters.buildFrom3dCoordinates(); } void MolfileLoader::loadQueryMolecule (QueryMolecule &mol) { mol.clear(); _bmol = &mol; _qmol = &mol; _mol = 0; _loadMolecule(); if (mol.stereocenters.size() == 0) mol.stereocenters.buildFrom3dCoordinates(); } void MolfileLoader::_loadMolecule () { _readHeader(); _readCtabHeader(); if (_v2000) { _readCtab2000(); if (_rgfile) _readRGroups2000(); } else { _readCtab3000(); _readRGroups3000(); _readTGroups3000(); } _postLoad(); } void MolfileLoader::loadCtab3000 (Molecule &mol) { _bmol = &mol; _qmol = 0; _mol = &mol; _readCtab3000(); _postLoad(); } void MolfileLoader::loadQueryCtab3000 (QueryMolecule &mol) { _bmol = &mol; _qmol = &mol; _mol = 0; _readCtab3000(); _postLoad(); } void MolfileLoader::_readHeader () { if (_scanner.lookNext() == '$') { _rgfile = true; // It's RGfile _scanner.skipLine(); // Skip $MDL REV 1 Date/Time _scanner.skipLine(); // Skip $MOL _scanner.skipLine(); // Skip $HDR } // Skip header _scanner.readLine(_bmol->name, true); // Check UTF-8 BOM mark in the name if (_bmol->name.size() >= 3 && (unsigned char)_bmol->name[0] == 0xEF && (unsigned char)_bmol->name[1] == 0xBB && (unsigned char)_bmol->name[2] == 0xBF) _bmol->name.remove(0, 3); _scanner.skipLine(); _scanner.skipLine(); if (_rgfile) { _scanner.skipLine(); // Skip $END HDR _scanner.skipLine(); // Skip $CTAB } } void MolfileLoader::_readCtabHeader () { QS_DEF(Array<char>, str); _scanner.readLine(str, false); BufferScanner strscan(str); _atoms_num = strscan.readIntFix(3); _bonds_num = strscan.readIntFix(3); try { char version[6]; int chiral_int; strscan.skip(6); chiral_int = strscan.readIntFix(3); strscan.skip(19); strscan.read(5, version); strscan.skipLine(); version[5] = 0; if (strcasecmp(version, "V2000") == 0 || strcasecmp(version, " ") == 0) // ISISHOST version of Molfile do not have version in the header _v2000 = true; else if (strcasecmp(version, "V3000") == 0) _v2000 = false; else throw Error("bad molfile version : %s", version); _chiral = (chiral_int != 0); } catch (Scanner::Error &) { _chiral = false; _v2000 = true; } } int MolfileLoader::_getElement (const char *buf) { char buf2[4] = {0, 0, 0, 0}; size_t len = strlen(buf); if (len > 3) throw Error("Internal error in MolfileLoader::_getElement: len = %d > 3", len); for (size_t i = 0; i < len; i++) { if (isspace(buf[i])) break; if (!isalpha(buf[i])) return -1; buf2[i] = (i == 0) ? toupper(buf[i]) : tolower(buf[i]); } return Element::fromString2(buf2); } char* MolfileLoader::_strtrim (char *buf) { while (*buf == ' ') buf++; if (*buf != 0) { size_t len = strlen(buf); char *end = buf + len - 1; while (*end == ' ') { *end = 0; end--; } } return buf; } void MolfileLoader::_readCtab2000 () { _init(); int k; QS_DEF(Array<char>, str); // read atoms for (k = 0; k < _atoms_num; k++) { // read coordinates float x = _scanner.readFloatFix(10); float y = _scanner.readFloatFix(10); float z = _scanner.readFloatFix(10); _scanner.skip(1); char atom_label_array[4] = { 0 }; char *buf = atom_label_array; int label = 0; int isotope = 0; int &atom_type = _atom_types.push(); _hcount.push(0); atom_type = _ATOM_ELEMENT; // read atom label and mass difference _scanner.readCharsFix(3, atom_label_array); // Atom label can be both left-bound or right-bound: " N", "N " or even " N ". buf = _strtrim(atom_label_array); isotope = _scanner.readIntFix(2); if (buf[0] == 0) throw Error("Empty atom label"); else if (buf[0] == 'R' && (buf[1] == '#' || buf[1] == 0)) { atom_type = _ATOM_R; label = ELEM_RSITE; } else if (buf[0] == 'A' && buf[1] == 0) atom_type = _ATOM_A; // will later become 'any atom' or pseudo atom else if (buf[0] == 'X' && buf[1] == 0 && !treat_x_as_pseudoatom) { if (_qmol == 0) throw Error("'X' label is allowed only for queries"); atom_type = _ATOM_X; } else if (buf[0] == 'Q' && buf[1] == 0) { if (_qmol == 0) throw Error("'Q' label is allowed only for queries"); atom_type = _ATOM_Q; } else if (buf[0] == 'L' && buf[1] == 0) { if (_qmol == 0) throw Error("atom lists are allowed only for queries"); atom_type = _ATOM_LIST; } else if (buf[0] == 'D' && buf[1] == 0) { label = ELEM_H; isotope = 2; } else if (buf[0] == 'T' && buf[1] == 0) { label = ELEM_H; isotope = 3; } else { label = _getElement(buf); if (label == -1) { atom_type = _ATOM_PSEUDO; if (isotope != 0) throw Error("isotope number not allowed on pseudo-atoms"); } if (isotope != 0) isotope = Element::getDefaultIsotope(label) + isotope; } int stereo_care = 0, valence = 0; int aam = 0, irflag = 0, ecflag = 0; int charge = 0, radical = 0; _convertCharge(_scanner.readIntFix(3), charge, radical); try { _scanner.readLine(str, false); BufferScanner rest(str); rest.skip(3); // skip atom stereo parity _hcount[k] = rest.readIntFix(3); stereo_care = rest.readIntFix(3); if (stereo_care > 0 && _qmol == 0) if (!ignore_noncritical_query_features) throw Error("only a query can have stereo care box"); valence = rest.readIntFix(3); rest.skip(9); // skip "HO designator" and 2 unused fields aam = rest.readIntFix(3); // atom-to-atom mapping number irflag = rest.readIntFix(3); // inversion/retension flag, ecflag = rest.readIntFix(3); // exact change flag } catch (Scanner::Error &) { } int idx; if (_mol != 0) { idx = _mol->addAtom(label); if (atom_type == _ATOM_PSEUDO) _mol->setPseudoAtom(idx, buf); _mol->setAtomCharge_Silent(idx, charge); _mol->setAtomIsotope(idx, isotope); _mol->setAtomRadical(idx, radical); if (valence > 0 && valence <= 14) _mol->setExplicitValence(idx, valence); if (valence == 15) _mol->setExplicitValence(idx, 0); _bmol->setAtomXyz(idx, x, y, z); } else { AutoPtr<QueryMolecule::Atom> atom; if (atom_type == _ATOM_ELEMENT) atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, label)); else if (atom_type == _ATOM_PSEUDO) atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, buf)); else if (atom_type == _ATOM_A) atom.reset(QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H))); else if (atom_type == _ATOM_Q) atom.reset(QueryMolecule::Atom::und( QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)))); else if (atom_type == _ATOM_X) { atom.reset(new QueryMolecule::Atom()); atom->type = QueryMolecule::OP_OR; atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At)); } else if (atom_type == _ATOM_R) atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); else // _ATOM_LIST atom.reset(new QueryMolecule::Atom()); if (charge != 0) atom.reset(QueryMolecule::Atom::und(atom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, charge))); if (isotope != 0) atom.reset(QueryMolecule::Atom::und(atom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, isotope))); if (radical != 0) atom.reset(QueryMolecule::Atom::und(atom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical))); if (valence > 0) { if (valence == 15) valence = 0; atom.reset(QueryMolecule::Atom::und(atom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_VALENCE, valence))); } idx = _qmol->addAtom(atom.release()); _bmol->setAtomXyz(idx, x, y, z); } if (stereo_care) _stereo_care_atoms[idx] = 1; if (reaction_atom_mapping != 0) reaction_atom_mapping->at(idx) = aam; if (reaction_atom_inversion != 0) reaction_atom_inversion->at(idx) = irflag; if (reaction_atom_exact_change != 0) reaction_atom_exact_change->at(idx) = ecflag; } int bond_idx; for (bond_idx = 0; bond_idx < _bonds_num; bond_idx++) { int beg = _scanner.readIntFix(3); int end = _scanner.readIntFix(3); int order = _scanner.readIntFix(3); int stereo = _scanner.readIntFix(3); int topology = 0; try { _scanner.readLine(str, false); BufferScanner rest(str); rest.skip(3); // not used topology = rest.readIntFix(3); if (topology != 0 && _qmol == 0) if (!ignore_noncritical_query_features) throw Error("bond topology is allowed only for queries"); int rcenter = rest.readIntFix(3); if (reaction_bond_reacting_center != 0) reaction_bond_reacting_center->at(bond_idx) = rcenter; } catch (Scanner::Error &) { } if (_mol != 0) { if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC) _mol->addBond_Silent(beg - 1, end - 1, order); else if (order == _BOND_SINGLE_OR_DOUBLE) throw Error("'single or double' bonds are allowed only for queries"); else if (order == _BOND_SINGLE_OR_AROMATIC) throw Error("'single or aromatic' bonds are allowed only for queries"); else if (order == _BOND_DOUBLE_OR_AROMATIC) throw Error("'double or aromatic' bonds are allowed only for queries"); else if (order == _BOND_ANY) throw Error("'any' bonds are allowed only for queries"); else throw Error("unknown bond type: %d", order); } else { AutoPtr<QueryMolecule::Bond> bond; if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC) bond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, order)); else if (order == _BOND_SINGLE_OR_DOUBLE) bond.reset(QueryMolecule::Bond::und( QueryMolecule::Bond::nicht( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)), QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE)))); else if (order == _BOND_SINGLE_OR_AROMATIC) bond.reset(QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); else if (order == _BOND_DOUBLE_OR_AROMATIC) bond.reset(QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); else if (order == _BOND_ANY) bond.reset(new QueryMolecule::Bond()); else throw Error("unknown bond type: %d", order); if (topology != 0) { bond.reset(QueryMolecule::Bond::und(bond.release(), new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topology == 1 ? TOPOLOGY_RING : TOPOLOGY_CHAIN))); } _qmol->addBond(beg - 1, end - 1, bond.release()); } if (stereo == 1) _bmol->setBondDirection(bond_idx, BOND_UP); else if (stereo == 6) _bmol->setBondDirection(bond_idx, BOND_DOWN); else if (stereo == 4) _bmol->setBondDirection(bond_idx, BOND_EITHER); else if (stereo == 3) _ignore_cistrans[bond_idx] = 1; else if (stereo != 0) throw Error("unknown number for bond stereo: %d", stereo); } int n_3d_features = -1; // read groups while (!_scanner.isEOF()) { char c = _scanner.readChar(); if (c == 'G') { _scanner.skipLine(); _scanner.skipLine(); continue; } if (c == 'M') { _scanner.skip(2); char chars[4] = {0, 0, 0, 0}; _scanner.readCharsFix(3, chars); if (strncmp(chars, "END", 3) == 0) { _scanner.skipLine(); break; } // atom list else if (strncmp(chars, "ALS", 3) == 0) { if (_qmol == 0) throw Error("atom lists are allowed only for queries"); int i; _scanner.skip(1); int atom_idx = _scanner.readIntFix(3); int list_size = _scanner.readIntFix(3); _scanner.skip(1); char excl_char = _scanner.readChar(); _scanner.skip(1); atom_idx--; AutoPtr<QueryMolecule::Atom> atomlist; _scanner.readLine(str, false); BufferScanner rest(str); for (i = 0; i < list_size; i++) { int j; memset(chars, 0, sizeof(chars)); for (j = 0; j < 4; j++) { // can not read 4 characters at once because // sqlplus cuts the trailing spaces if (!rest.isEOF()) chars[j] = rest.readChar(); else break; } if (j < 1) throw Error("atom list: can not read element #%d", i); for (j = 0; j < 4; j++) if (chars[j] == ' ') memset(chars + j, 0, 4 - j); if (chars[3] != 0) throw Error("atom list: invalid element '%c%c%c%c'", chars[0], chars[1], chars[2], chars[3]); if (chars[0] == 'A' && chars[1] == 0) { if (list_size == 1) atomlist.reset(QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H))); else throw Error("'A' inside atom list, if present, must be single"); } else if (chars[0] == 'Q' && chars[1] == 0) { if (list_size == 1) atomlist.reset(QueryMolecule::Atom::und( QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)))); else throw Error("'Q' inside atom list, if present, must be single"); } else { _appendQueryAtom(chars, atomlist); } } if (excl_char == 'T') atomlist.reset(QueryMolecule::Atom::nicht(atomlist.release())); _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), atomlist.release())); } // atom charge else if (strncmp(chars, "CHG", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int charge = _scanner.readIntFix(3); if (_mol != 0) _mol->setAtomCharge_Silent(atom_idx, charge); else { _qmol->getAtom(atom_idx).removeConstraints(QueryMolecule::ATOM_CHARGE); _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, charge))); } } _scanner.skipLine(); } else if (strncmp(chars, "RAD", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int radical = _scanner.readIntFix(3); if (_mol != 0) _mol->setAtomRadical(atom_idx, radical); else { _qmol->getAtom(atom_idx).removeConstraints(QueryMolecule::ATOM_RADICAL); _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical))); } } _scanner.skipLine(); } // atom isotope else if (strncmp(chars, "ISO", 3) == 0) { int n = _scanner.readIntFix(3); while (--n >= 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int isotope = _scanner.readIntFix(3); if (_mol != 0) _mol->setAtomIsotope(atom_idx, isotope); else { _qmol->getAtom(atom_idx).removeConstraints(QueryMolecule::ATOM_ISOTOPE); _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, isotope))); } } _scanner.skipLine(); } else if (strncmp(chars, "SUB", 3) == 0) { if (_qmol == 0) throw Error("substitution counts are allowed only for queries"); int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int sub_count = _scanner.readIntFix(3); if (sub_count == -1) // no substitution _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, 0))); else if (sub_count == -2) { _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN, _qmol->getVertex(atom_idx).degree()))); } else if (sub_count > 0) _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, sub_count, (sub_count < 6 ? sub_count : 100)))); else throw Error("invalid SUB value: %d", sub_count); } _scanner.skipLine(); } else if (strncmp(chars, "RBC", 3) == 0) { if (_qmol == 0) { if (!ignore_noncritical_query_features) throw Error("ring bond count is allowed only for queries"); } else { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int rbcount = _scanner.readIntFix(3); if (rbcount == -1) // no ring bonds _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 0))); else if (rbcount == -2) // as drawn { int k, rbonds = 0; const Vertex &vertex = _qmol->getVertex(atom_idx); for (k = vertex.neiBegin(); k != vertex.neiEnd(); k = vertex.neiNext(k)) if (_qmol->getEdgeTopology(vertex.neiEdge(k)) == TOPOLOGY_RING) rbonds++; _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN, rbonds))); } else if (rbcount > 1) _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, rbcount, (rbcount < 4 ? rbcount : 100)))); else throw Error("ring bond count = %d makes no sense", rbcount); } } _scanner.skipLine(); } else if (strncmp(chars, "UNS", 3) == 0) { if (_qmol == 0) { if (!ignore_noncritical_query_features) throw Error("unaturated atoms are allowed only for queries"); } else { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int unsaturation = _scanner.readIntFix(3); if (unsaturation) _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_UNSATURATION, 0))); } } _scanner.skipLine(); } else if (strncmp(chars, "$3D", 3) == 0) { if (_qmol == 0) { if (!ignore_noncritical_query_features) throw Error("3D features are allowed only for queries"); } else { if (n_3d_features == -1) { n_3d_features = _scanner.readIntFix(3); _scanner.skipLine(); } else { n_3d_features--; if (n_3d_features < 0) throw Error("3D feature unexpected"); _read3dFeature2000(); } } } else if (strncmp(chars, "AAL", 3) == 0) { _scanner.skip(1); int site_idx = _scanner.readIntFix(3) - 1; int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int att_type = _scanner.readIntFix(3); _bmol->setRSiteAttachmentOrder(site_idx, atom_idx, att_type - 1); } _scanner.skipLine(); } else if (strncmp(chars, "RGP", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); _bmol->allowRGroupOnRSite(atom_idx, _scanner.readIntFix(3)); } _scanner.skipLine(); } else if (strncmp(chars, "LOG", 3) == 0) { // skip something _scanner.skip(3); _scanner.skip(1); int rgroup_idx = _scanner.readIntFix(3); _scanner.skip(1); int if_then = _scanner.readIntFix(3); _scanner.skip(1); int rest_h = _scanner.readIntFix(3); _scanner.skip(1); QS_DEF(Array<char>, occurrence_str); RGroup &rgroup = _bmol->rgroups.getRGroup(rgroup_idx); rgroup.if_then = if_then; rgroup.rest_h = rest_h; _scanner.readLine(occurrence_str, true); _readRGroupOccurrenceRanges(occurrence_str.ptr(), rgroup.occurrence); } else if (strncmp(chars, "APO", 3) == 0) { int list_length = _scanner.readIntFix(3); while (list_length-- > 0) { _scanner.skip(1); int atom_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); int att_type = _scanner.readIntFix(3); if (att_type == -1) att_type = 3; for (int att_idx = 0; (1 << att_idx) <= att_type; att_idx++) if (att_type & (1 << att_idx)) _bmol->addAttachmentPoint(att_idx + 1, atom_idx); } _scanner.skipLine(); } else if (strncmp(chars, "STY", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); char type[4] = {0, 0, 0, 0}; int sgroup_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); _scanner.readCharsFix(3, type); _sgroup_types.expandFill(sgroup_idx + 1, -1); _sgroup_mapping.expandFill(sgroup_idx + 1, -1); int idx = _bmol->sgroups.addSGroup(type); SGroup *sgroup = &_bmol->sgroups.getSGroup(idx); sgroup->original_group = sgroup_idx + 1; _sgroup_types[sgroup_idx] = sgroup->sgroup_type; _sgroup_mapping[sgroup_idx] = idx; } _scanner.skipLine(); } else if (strncmp(chars, "SPL", 3) == 0 || strncmp(chars, "SBT", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; SGroup *sgroup = &_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); _scanner.skip(1); int value = _scanner.readIntFix(3); if (strncmp(chars, "SPL", 3) == 0) sgroup->parent_group = value; else sgroup->brk_style = value; } _scanner.skipLine(); } else if (strncmp(chars, "SST", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; SGroup *sgroup = &_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); char subtype[4] = {0, 0, 0, 0}; _scanner.readCharsFix(3, subtype); if (strncmp(subtype, "ALT", 3) == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_ALT; else if (strncmp(subtype, "RAN", 3) == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_RAN; else if (strncmp(subtype, "BLO", 3) == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_BLO; } _scanner.skipLine(); } else if (strncmp(chars, "SAL", 3) == 0 || strncmp(chars, "SBL", 3) == 0 || strncmp(chars, "SDI", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_mapping[sgroup_idx] >= 0) { SGroup *sgroup = &_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); int n = _scanner.readIntFix(3); if (strncmp(chars, "SDI", 3) == 0) { if (n == 4) // should always be 4 { Vec2f *brackets = sgroup->brackets.push(); _scanner.skipSpace(); brackets[0].x = _scanner.readFloat(); _scanner.skipSpace(); brackets[0].y = _scanner.readFloat(); _scanner.skipSpace(); brackets[1].x = _scanner.readFloat(); _scanner.skipSpace(); brackets[1].y = _scanner.readFloat(); } } else while (n-- > 0) { _scanner.skip(1); if (strncmp(chars, "SAL", 3) == 0) sgroup->atoms.push(_scanner.readIntFix(3) - 1); else // SBL sgroup->bonds.push(_scanner.readIntFix(3) - 1); } } _scanner.skipLine(); } else if (strncmp(chars, "SDT", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; _scanner.skip(1); if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_DAT) { QS_DEF(Array<char>, rest); _scanner.readLine(rest, false); BufferScanner strscan(rest); DataSGroup &sgroup = (DataSGroup &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); // Read field name int k = 30; while (k-- > 0) { if (strscan.isEOF()) break; int c = strscan.readChar(); sgroup.name.push(c); } // Remove last spaces because name can have multiple words while (sgroup.name.size() > 0) { if (isspace(sgroup.name.top())) sgroup.name.pop(); else break; } sgroup.name.push(0); // Read field type k = 2; while (k-- > 0) { if (strscan.isEOF()) break; int c = strscan.readChar(); sgroup.type.push(c); } sgroup.type.push(0); // Read field description k = 20; while (k-- > 0) { if (strscan.isEOF()) break; int c = strscan.readChar(); sgroup.description.push(c); } // Remove last spaces because dscription can have multiple words? while (sgroup.description.size() > 0) { if (isspace(sgroup.description.top())) sgroup.description.pop(); else break; } sgroup.description.push(0); // Read query code k = 2; while (k-- > 0) { if (strscan.isEOF()) break; int c = strscan.readChar(); sgroup.querycode.push(c); } while (sgroup.querycode.size() > 0) { if (isspace(sgroup.querycode.top())) sgroup.querycode.pop(); else break; } sgroup.querycode.push(0); // Read query operator k = 20; while (k-- > 0) { if (strscan.isEOF()) break; int c = strscan.readChar(); sgroup.queryoper.push(c); } while (sgroup.queryoper.size() > 0) { if (isspace(sgroup.queryoper.top())) sgroup.queryoper.pop(); else break; } sgroup.queryoper.push(0); } } else if (strncmp(chars, "SDD", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_DAT) { _scanner.skip(1); DataSGroup &sgroup = (DataSGroup &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); _readSGroupDisplay(_scanner, sgroup); } _scanner.skipLine(); } else if (strncmp(chars, "SED", 3) == 0 || strncmp(chars, "SCD", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_DAT) { _scanner.skip(1); DataSGroup &sgroup = (DataSGroup &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); int len = sgroup.data.size(); _scanner.appendLine(sgroup.data, true); // Remove last spaces because "SED" means end of a paragraph if (strncmp(chars, "SED", 3) == 0) { if (sgroup.data.top() == 0) sgroup.data.pop(); while (sgroup.data.size() > len) { if (isspace(sgroup.data.top())) sgroup.data.pop(); else break; } // Add new paragraph. Last '\n' will be cleaned at the end sgroup.data.push('\n'); if (sgroup.data.top() != 0) sgroup.data.push(0); } } else _scanner.skipLine(); } else if (strncmp(chars, "SMT", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SUP) { _scanner.skip(1); Superatom &sup = (Superatom &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); _scanner.readLine(sup.subscript, true); } else if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_MUL) { _scanner.skip(1); MultipleGroup &mg = (MultipleGroup &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); mg.multiplier = _scanner.readInt(); _scanner.skipLine(); } else if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SRU) { _scanner.skip(1); RepeatingUnit &sru = (RepeatingUnit &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); _scanner.readLine(sru.subscript, true); } else _scanner.skipLine(); } else if (strncmp(chars, "SCL", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SUP) { _scanner.skip(1); Superatom &sup = (Superatom &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); _scanner.readLine(sup.sa_class, true); } else _scanner.skipLine(); } else if (strncmp(chars, "SBV", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SUP) { Superatom &sup = (Superatom &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); Superatom::_BondConnection &bond = sup.bond_connections.push(); _scanner.skip(1); bond.bond_idx = _scanner.readIntFix(3) - 1; _scanner.skipSpace(); bond.bond_dir.x = _scanner.readFloat(); _scanner.skipSpace(); bond.bond_dir.y = _scanner.readFloat(); } _scanner.skipLine(); } else if (strncmp(chars, "SDS", 3) == 0) { _scanner.skip(1); char expanded[4] = {0, 0, 0, 0}; _scanner.readCharsFix(3, expanded); if (strncmp(expanded, "EXP", 3) == 0) { int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SUP) { Superatom &sup = (Superatom &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); sup.contracted = 0; } } } _scanner.skipLine(); } else if (strncmp(chars, "SPA", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_MUL) { MultipleGroup &mg = (MultipleGroup &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); int n = _scanner.readIntFix(3); while (n-- > 0) { _scanner.skip(1); mg.parent_atoms.push(_scanner.readIntFix(3) - 1); } } _scanner.skipLine(); } else if (strncmp(chars, "SAP", 3) == 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SUP) { Superatom &sup = (Superatom &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); int n = _scanner.readIntFix(3); while (n-- > 0) { int idap = sup.attachment_points.add(); Superatom::_AttachmentPoint &ap = sup.attachment_points.at(idap); _scanner.skip(1); ap.aidx = _scanner.readIntFix(3) - 1; _scanner.skip(1); ap.lvidx = _scanner.readIntFix(3) - 1; _scanner.skip(1); char c = _scanner.readChar(); ap.apid.push(c); c = _scanner.readChar(); ap.apid.push(c); ap.apid.push(0); } } _scanner.skipLine(); } else if (strncmp(chars, "SCN", 3) == 0) { // The format is the following: M SCNnn8 sss ttt ... int n = _scanner.readIntFix(3); bool need_skip_line = true; while (n-- > 0) { _scanner.skip(1); int sgroup_idx = _scanner.readIntFix(3) - 1; char id[4]; _scanner.skip(1); _scanner.readCharsFix(3, id); if (_sgroup_types[sgroup_idx] == SGroup::SG_TYPE_SRU) { RepeatingUnit &ru = (RepeatingUnit &)_bmol->sgroups.getSGroup(_sgroup_mapping[sgroup_idx]); if (strncmp(id, "HH", 2) == 0) ru.connectivity = SGroup::HEAD_TO_HEAD; else if (strncmp(id, "HT", 2) == 0) ru.connectivity = SGroup::HEAD_TO_TAIL; else if (strncmp(id, "EU", 2) == 0) ru.connectivity = SGroup::EITHER; else { id[3] = 0; throw Error("Undefined Sgroup connectivity: '%s'", id); } if (id[2] == '\n') { if (n != 0) throw Error("Unexpected end of M SCN"); else // In some molfiles last space is not written need_skip_line = false; } } } if (need_skip_line) _scanner.skipLine(); } else if (strncmp(chars, "MRV", 3) == 0) { _scanner.readLine(str, false); BufferScanner rest(str); try { rest.skip(1); rest.readCharsFix(3, chars); if (strncmp(chars, "SMA", 3) == 0) { // Marvin's "SMARTS in Molfile" extension if (_qmol == 0) { if (!ignore_noncritical_query_features) throw Error("SMARTS notation allowed only for query molecules"); } else { rest.skip(1); int idx = rest.readIntFix(3) - 1; rest.skip(1); QS_DEF(QueryMolecule, smartsmol); SmilesLoader loader(rest); smartsmol.clear(); loader.smarts_mode = true; loader.loadQueryMolecule(smartsmol); if (smartsmol.vertexCount() != 1) throw Error("expected 1 atom in SMARTS expression, got %d", smartsmol.vertexCount()); _qmol->resetAtom(idx, smartsmol.releaseAtom(smartsmol.vertexBegin())); } } } catch (Scanner::Error &) { } } else _scanner.skipLine(); } else if (c == 'A') { QS_DEF(Array<char>, alias); // There should be 3 characters to the atom index, but some molfiles // has only 2 digits _scanner.skipSpace(); int atom_idx = _scanner.readInt(); atom_idx--; _scanner.skipLine(); _scanner.readLine(alias, true); _preparePseudoAtomLabel(alias); if (_atom_types[atom_idx] == _ATOM_ELEMENT) { int idx = _bmol->sgroups.addSGroup(SGroup::SG_TYPE_DAT); DataSGroup &sgroup = (DataSGroup &)_bmol->sgroups.getSGroup(idx); sgroup.atoms.push(atom_idx); sgroup.name.readString("INDIGO_ALIAS", true); sgroup.data.copy(alias); sgroup.display_pos.x = _bmol->getAtomXyz(atom_idx).x; sgroup.display_pos.y = _bmol->getAtomXyz(atom_idx).y; } else { if (_mol != 0) _mol->setPseudoAtom(atom_idx, alias.ptr()); else _qmol->resetAtom(atom_idx, QueryMolecule::Atom::und(_qmol->releaseAtom(atom_idx), new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, alias.ptr()))); _atom_types[atom_idx] = _ATOM_PSEUDO; } } else if (c == '\n') continue; else _scanner.skipLine(); } // Remove last new lines for data SGroups int sgroups_count = _bmol->sgroups.getSGroupCount(); for (int i = 0; i < sgroups_count; i++) { SGroup &sgroup = _bmol->sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dsg = (DataSGroup &)sgroup; if (dsg.data.size() > 2 && dsg.data.top(1) == '\n') { dsg.data.pop(); dsg.data.top() = 0; } } } if (_qmol == 0) for (int atom_idx = 0; atom_idx < _atoms_num; atom_idx++) if (_atom_types[atom_idx] == _ATOM_A) throw Error("'any' atoms are allowed only for queries"); } void MolfileLoader::_appendQueryAtom (const char *atom_label, AutoPtr<QueryMolecule::Atom> &atom) { int atom_number = Element::fromString2(atom_label); AutoPtr<QueryMolecule::Atom> cur_atom; if (atom_number != -1) cur_atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, atom_number)); else cur_atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, atom_label)); if (atom.get() == 0) atom.reset(cur_atom.release()); else atom.reset(QueryMolecule::Atom::oder(atom.release(), cur_atom.release())); } void MolfileLoader::_convertCharge (int value, int &charge, int &radical) { switch (value) { case 1: charge = 3; break; case 2: charge = 2; break; case 3: charge = 1; break; case 4: radical = 2; break; case 5: charge = -1; break; case 6: charge = -2; break; case 7: charge = -3; break; } } void MolfileLoader::_read3dFeature2000 () { // read 3D feature ID (see MDL ctfile documentation) int feature_id = _scanner.readIntFix(3); _scanner.skipLine(); Molecule3dConstraints *constraints = &_qmol->spatial_constraints; if (constraints->end() == 0) constraints->init(); switch (feature_id) { case -1: // point defined by 2 points and distance { AutoPtr<Molecule3dConstraints::PointByDistance> constr; constr.create(); _scanner.skip(6); constr->beg_id = _scanner.readIntFix(3) - 1; constr->end_id = _scanner.readIntFix(3) - 1; constr->distance = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -2: // point defined by 2 points and percentage { AutoPtr<Molecule3dConstraints::PointByPercentage> constr; constr.create(); _scanner.skip(6); constr->beg_id = _scanner.readIntFix(3) - 1; constr->end_id = _scanner.readIntFix(3) - 1; constr->percentage = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -3: // point defined by point, normal line, and distance { AutoPtr<Molecule3dConstraints::PointByNormale> constr; constr.create(); _scanner.skip(6); constr->org_id = _scanner.readIntFix(3) - 1; constr->norm_id = _scanner.readIntFix(3) - 1; constr->distance = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -4: // line defined by 2 or more points (best fit line if more than 2 points) { AutoPtr<Molecule3dConstraints::BestFitLine> constr; constr.create(); _scanner.skip(6); int amount = _scanner.readIntFix(3); if (amount < 2) throw Error("invalid points amount in M $3D-4 feature"); constr->max_deviation = _scanner.readFloatFix(10); _scanner.skipLine(); _scanner.skip(6); while (amount-- > 0) constr->point_ids.push(_scanner.readIntFix(3) - 1); _scanner.skipLine(); constraints->add(constr.release()); break; } case -5: // plane defined by 3 or more points (best fit line if more than 3 points) { AutoPtr<Molecule3dConstraints::BestFitPlane> constr; constr.create(); _scanner.skip(6); int amount = _scanner.readIntFix(3); if (amount < 3) throw Error("invalid points amount in M $3D-5 feature"); constr->max_deviation = _scanner.readFloatFix(10); _scanner.skipLine(); _scanner.skip(6); while (amount-- > 0) constr->point_ids.push(_scanner.readIntFix(3) - 1); _scanner.skipLine(); constraints->add(constr.release()); break; } case -6: // plane defined by point and line { AutoPtr<Molecule3dConstraints::PlaneByPoint> constr; constr.create(); _scanner.skip(6); constr->point_id = _scanner.readIntFix(3) - 1; constr->line_id = _scanner.readIntFix(3) - 1; _scanner.skipLine(); constraints->add(constr.release()); break; } case -7: // centroid defined by points { AutoPtr<Molecule3dConstraints::Centroid> constr; constr.create(); _scanner.skip(6); int amount = _scanner.readIntFix(3); if (amount < 1) throw Error("invalid amount of points for centroid: %d", amount); _scanner.skipLine(); _scanner.skip(6); while (amount-- > 0) constr->point_ids.push(_scanner.readIntFix(3) - 1); _scanner.skipLine(); constraints->add(constr.release()); break; } case -8: // normal line defined by point and plane { AutoPtr<Molecule3dConstraints::Normale> constr; constr.create(); _scanner.skip(6); constr->point_id = _scanner.readIntFix(3) - 1; constr->plane_id = _scanner.readIntFix(3) - 1; _scanner.skipLine(); constraints->add(constr.release()); break; } case -9: // distance defined by 2 points and range { AutoPtr<Molecule3dConstraints::DistanceByPoints> constr; constr.create(); _scanner.skip(6); constr->beg_id = _scanner.readIntFix(3) - 1; constr->end_id = _scanner.readIntFix(3) - 1; constr->bottom = _scanner.readFloatFix(10); constr->top = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -10: // distance defined by point, line and range { AutoPtr<Molecule3dConstraints::DistanceByLine> constr; constr.create(); _scanner.skip(6); constr->point_id = _scanner.readIntFix(3) - 1; constr->line_id = _scanner.readIntFix(3) - 1; constr->bottom = _scanner.readFloatFix(10); constr->top = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -11: // distance defined by point, plane and range { AutoPtr<Molecule3dConstraints::DistanceByPlane> constr; constr.create(); _scanner.skip(6); constr->point_id = _scanner.readIntFix(3) - 1; constr->plane_id = _scanner.readIntFix(3) - 1; constr->bottom = _scanner.readFloatFix(10); constr->top = _scanner.readFloatFix(10); _scanner.skipLine(); constraints->add(constr.release()); break; } case -12: // angle defined by 3 points and range { AutoPtr<Molecule3dConstraints::AngleByPoints> constr; constr.create(); _scanner.skip(6); constr->point1_id = _scanner.readIntFix(3) - 1; constr->point2_id = _scanner.readIntFix(3) - 1; constr->point3_id = _scanner.readIntFix(3) - 1; constr->bottom = (float)(_scanner.readFloatFix(10) * M_PI / 180); constr->top = (float)(_scanner.readFloatFix(10) * M_PI / 180); _scanner.skipLine(); constraints->add(constr.release()); break; } case -13: // angle defined by 2 lines and range { AutoPtr<Molecule3dConstraints::AngleByLines> constr; constr.create(); _scanner.skip(6); constr->line1_id = _scanner.readIntFix(3) - 1; constr->line2_id = _scanner.readIntFix(3) - 1; constr->bottom = (float)(_scanner.readFloatFix(10) * M_PI / 180); constr->top = (float)(_scanner.readFloatFix(10) * M_PI / 180); _scanner.skipLine(); constraints->add(constr.release()); break; } case -14: // angles defined by 2 planes and range { AutoPtr<Molecule3dConstraints::AngleByPlanes> constr; constr.create(); _scanner.skip(6); constr->plane1_id = _scanner.readIntFix(3) - 1; constr->plane2_id = _scanner.readIntFix(3) - 1; constr->bottom = (float)(_scanner.readFloatFix(10) * M_PI / 180); constr->top = (float)(_scanner.readFloatFix(10) * M_PI / 180); _scanner.skipLine(); constraints->add(constr.release()); break; } case -15: // dihedral angle defined by 4 points { AutoPtr<Molecule3dConstraints::AngleDihedral> constr; constr.create(); _scanner.skip(6); constr->point1_id = _scanner.readIntFix(3) - 1; constr->point2_id = _scanner.readIntFix(3) - 1; constr->point3_id = _scanner.readIntFix(3) - 1; constr->point4_id = _scanner.readIntFix(3) - 1; constr->bottom = (float)(_scanner.readFloatFix(10) * M_PI / 180); constr->top = (float)(_scanner.readFloatFix(10) * M_PI / 180); _scanner.skipLine(); constraints->add(constr.release()); break; } case -16: // exclusion sphere defines by points and distance { AutoPtr<Molecule3dConstraints::ExclusionSphere> constr; int allowed_atoms_amount; Array<int> allowed_atoms; constr.create(); _scanner.skip(6); constr->center_id = _scanner.readIntFix(3) - 1; constr->allow_unconnected = (_scanner.readIntFix(3) != 0); allowed_atoms_amount = _scanner.readIntFix(3); constr->radius = (float)(_scanner.readFloatFix(10)); if (allowed_atoms_amount > 0) { _scanner.skipLine(); _scanner.skip(6); while (allowed_atoms_amount-- > 0) constr->allowed_atoms.push(_scanner.readIntFix(3) - 1); } _scanner.skipLine(); constraints->add(constr.release()); break; } case -17: // fixed atoms { _scanner.skip(6); int amount = _scanner.readIntFix(3); _scanner.skipLine(); _scanner.skip(6); while (amount-- > 0) _qmol->fixed_atoms.push(_scanner.readIntFix(3) - 1); _scanner.skipLine(); break; } default: throw Error("unknown 3D feature in createFromMolfile: %d", feature_id); } } void MolfileLoader::_readRGroupOccurrenceRanges (const char *str, Array<int> &ranges) { int beg = -1, end = -1; int add_beg = 0, add_end = 0; while (*str != 0) { if (*str == '>') { end = 0xFFFF; add_beg = 1; } else if (*str == '<') { beg = 0; add_end = -1; } else if (isdigit(*str)) { sscanf(str, "%d", beg == -1 ? &beg : &end); while (isdigit(*str)) str++; continue; } else if (*str == ',') { if (end == -1) end = beg; else beg += add_beg, end += add_end; ranges.push((beg << 16) | end); beg = end = -1; add_beg = add_end = 0; } str++; } if (beg == -1 && end == -1) return; if (end == -1) end = beg; else beg += add_beg, end += add_end; ranges.push((beg << 16) | end); } int MolfileLoader::_asc_cmp_cb (int &v1, int &v2, void *context) { return v2 - v1; } void MolfileLoader::_postLoad () { int i; for (i = _bmol->vertexBegin(); i < _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { // Update attachment orders for rgroup bonds if they were not specified explicitly if (!_bmol->isRSite(i)) continue; const Vertex &vertex = _bmol->getVertex(i); if (vertex.degree() == 1 && _bmol->getRSiteAttachmentPointByOrder(i, 0) == -1) _bmol->setRSiteAttachmentOrder(i, vertex.neiVertex(vertex.neiBegin()), 0); else if (vertex.degree() == 2 && (_bmol->getRSiteAttachmentPointByOrder(i, 0) == -1 || _bmol->getRSiteAttachmentPointByOrder(i, 1) == -1)) { int nei_idx_1 = vertex.neiVertex(vertex.neiBegin()); int nei_idx_2 = vertex.neiVertex(vertex.neiNext(vertex.neiBegin())); _bmol->setRSiteAttachmentOrder(i, __min(nei_idx_1, nei_idx_2), 0); _bmol->setRSiteAttachmentOrder(i, __max(nei_idx_1, nei_idx_2), 1); } else if (vertex.degree() > 2) { int j; for (j = 0; j < vertex.degree(); j++) if (_bmol->getRSiteAttachmentPointByOrder(i, j) == -1) { QS_DEF(Array<int>, nei_indices); nei_indices.clear(); for (int nei_idx = vertex.neiBegin(); nei_idx < vertex.neiEnd(); nei_idx = vertex.neiNext(nei_idx)) nei_indices.push(vertex.neiVertex(nei_idx)); nei_indices.qsort(_asc_cmp_cb, 0); for (int order = 0; order < vertex.degree(); order++) _bmol->setRSiteAttachmentOrder(i, nei_indices[order], order); break; } } } if (_mol != 0) { int k; for (k = 0; k < _atoms_num; k++) if (_hcount[k] > 0) _mol->setImplicitH(k, _hcount[k] - 1); } if (_qmol != 0) { for (i = _qmol->edgeBegin(); i < _qmol->edgeEnd(); i = _qmol->edgeNext(i)) { if (_ignore_cistrans[i]) continue; const Edge &edge = _qmol->getEdge(i); if ((_stereo_care_atoms[edge.beg] && _stereo_care_atoms[edge.end]) || _stereo_care_bonds[i]) { // in fragments such as C-C=C-C=C-C, the middle single bond // has both ends 'stereo care', but should not be considered // as 'stereo care' itself if (MoleculeCisTrans::isGeomStereoBond(*_bmol, i, 0, true)) _qmol->setBondStereoCare(i, true); } } int k; for (k = 0; k < _atoms_num; k++) { int expl_h = 0; if (_hcount[k] >= 0) { // count explicit hydrogens const Vertex &vertex = _bmol->getVertex(k); int i; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (_bmol->getAtomNumber(vertex.neiVertex(i)) == ELEM_H) expl_h++; } } if (_hcount[k] == 1) { // no hydrogens unless explicitly drawn _qmol->resetAtom(k, QueryMolecule::Atom::und(_qmol->releaseAtom(k), new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_H, expl_h))); } else if (_hcount[k] > 1) { // (_hcount[k] - 1) or more atoms in addition to explicitly drawn // no hydrogens unless explicitly drawn _qmol->resetAtom(k, QueryMolecule::Atom::und(_qmol->releaseAtom(k), new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_H, expl_h + _hcount[k] - 1, 100))); } } } // Some "either" bonds may mean not "either stereocenter", but // "either cis-trans", or "connected to either cis-trans". for (i = 0; i < _bonds_num; i++) if (_bmol->getBondDirection(i) == BOND_EITHER) { if (MoleculeCisTrans::isGeomStereoBond(*_bmol, i, 0, true)) { _ignore_cistrans[i] = 1; _sensible_bond_directions[i] = 1; } else { int k; const Vertex &v = _bmol->getVertex(_bmol->getEdge(i).beg); for (k = v.neiBegin(); k != v.neiEnd(); k = v.neiNext(k)) { if (MoleculeCisTrans::isGeomStereoBond(*_bmol, v.neiEdge(k), 0, true)) { _ignore_cistrans[v.neiEdge(k)] = 1; _sensible_bond_directions[i] = 1; break; } } } } _bmol->stereocenters.buildFromBonds(stereochemistry_options, _sensible_bond_directions.ptr()); _bmol->allene_stereo.buildFromBonds(stereochemistry_options.ignore_errors, _sensible_bond_directions.ptr()); if (!_chiral) for (i = 0; i < _atoms_num; i++) { int type = _bmol->stereocenters.getType(i); if (type == MoleculeStereocenters::ATOM_ABS) _bmol->stereocenters.setType(i, MoleculeStereocenters::ATOM_AND, 1); } for (i = 0; i < _atoms_num; i++) if (_stereocenter_types[i] > 0) { if (_bmol->stereocenters.getType(i) == 0) { if (!stereochemistry_options.ignore_errors) throw Error("stereo type specified for atom #%d, but the bond " "directions does not say that it is a stereocenter", i); } else _bmol->stereocenters.setType(i, _stereocenter_types[i], _stereocenter_groups[i]); } if (!stereochemistry_options.ignore_errors) for (i = 0; i < _bonds_num; i++) if (_bmol->getBondDirection(i) > 0 && !_sensible_bond_directions[i]) throw Error("direction of bond #%d makes no sense", i); _bmol->cis_trans.build(_ignore_cistrans.ptr()); int n_rgroups = _bmol->rgroups.getRGroupCount(); for (i = 1; i <= n_rgroups; i++) if (_bmol->rgroups.getRGroup(i).occurrence.size() == 0 && _bmol->rgroups.getRGroup(i).fragments.size() > 0) _bmol->rgroups.getRGroup(i).occurrence.push((1 << 16) | 0xFFFF); _bmol->have_xyz = true; } void MolfileLoader::_readRGroups2000 () { MoleculeRGroups *rgroups = &_bmol->rgroups; // read groups while (!_scanner.isEOF()) { char chars[5]; chars[4] = 0; _scanner.readCharsFix(4, chars); if (strncmp(chars, "$RGP", 4) == 0) { _scanner.skipLine(); _scanner.skipSpace(); int rgroup_idx = _scanner.readInt(); RGroup &rgroup = rgroups->getRGroup(rgroup_idx); _scanner.skipLine(); while (!_scanner.isEOF()) { char rgp_chars[6]; rgp_chars[5] = 0; _scanner.readCharsFix(5, rgp_chars); if (strncmp(rgp_chars, "$CTAB", 5) == 0) { _scanner.skipLine(); AutoPtr<BaseMolecule> fragment(_bmol->neu()); MolfileLoader loader(_scanner); loader._bmol = fragment.get(); if (_bmol->isQueryMolecule()) { loader._qmol = &loader._bmol->asQueryMolecule(); loader._mol = 0; } else { loader._mol = &loader._bmol->asMolecule(); loader._qmol = 0; } loader._readCtabHeader(); loader._readCtab2000(); if (loader._rgfile) loader._readRGroups2000(); loader._postLoad(); rgroup.fragments.add(fragment.release()); } else if (strncmp(rgp_chars, "$END ", 5) == 0) { rgp_chars[3] = 0; _scanner.readCharsFix(3, rgp_chars); _scanner.skipLine(); if (strncmp(rgp_chars, "RGP", 3) == 0) break; } else _scanner.skipLine(); } } else if (strncmp(chars, "$END", 4) == 0) { chars[4] = 0; _scanner.readCharsFix(4, chars); _scanner.skipLine(); if (strncmp(chars, " MOL", 4) == 0) break; } else _scanner.skipLine(); } } void MolfileLoader::_readCtab3000 () { QS_DEF(Array<char>, str); _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 BEGIN CTAB", 17) != 0) throw Error("error reading CTAB block header"); str.clear_resize(14); _scanner.read(14, str.ptr()); if (strncmp(str.ptr(), "M V30 COUNTS ", 14) != 0) throw Error("error reading COUNTS line"); int i, nsgroups, n3d, chiral_int; _scanner.readLine(str, true); if (sscanf(str.ptr(), "%d %d %d %d %d", &_atoms_num, &_bonds_num, &nsgroups, &n3d, &chiral_int) < 5) throw Error("error parsing COUNTS line"); _chiral = (chiral_int != 0); _init(); _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 BEGIN ATOM", 14) != 0) throw Error("Error reading ATOM block header"); for (i = 0; i < _atoms_num; i++) { _readMultiString(str); BufferScanner strscan(str.ptr()); int &atom_type = _atom_types.push(); _hcount.push(0); atom_type = _ATOM_ELEMENT; int isotope = 0; int label = 0; AutoPtr<QueryMolecule::Atom> query_atom; strscan.readInt1(); // atom index -- ignored QS_DEF(Array<char>, buf); strscan.readWord(buf, " ["); char stopchar = strscan.readChar(); if (stopchar == '[') { if (_qmol == 0) throw Error("atom list is allowed only for queries"); if (buf[0] == 0) atom_type = _ATOM_LIST; else if (strcmp(buf.ptr(), "NOT") == 0) atom_type = _ATOM_NOTLIST; else throw Error("bad word: %s", buf.ptr()); bool was_a = false, was_q = false; while (1) { strscan.readWord(buf, ",]"); stopchar = strscan.readChar(); if (was_a) throw Error("'A' inside atom list, if present, must be single"); if (was_q) throw Error("'Q' inside atom list, if present, must be single"); if (buf.size() == 2 && buf[0] == 'A') { was_a = true; atom_type = _ATOM_A; } else if (buf.size() == 2 && buf[0] == 'Q') { was_q = true; atom_type = _ATOM_Q; } else { _appendQueryAtom(buf.ptr(), query_atom); } if (stopchar == ']') break; } } else if (buf.size() == 2 && buf[0] == 'D') { label = ELEM_H; isotope = 2; } else if (buf.size() == 2 && buf[0] == 'T') { label = ELEM_H; isotope = 3; } else if (buf.size() == 2 && buf[0] == 'Q') { if (_qmol == 0) throw Error("'Q' atom is allowed only for queries"); atom_type = _ATOM_Q; } else if (buf.size() == 2 && buf[0] == 'A') { if (_qmol == 0) throw Error("'A' atom is allowed only for queries"); atom_type = _ATOM_A; } else if (buf.size() == 2 && buf[0] == 'X' && !treat_x_as_pseudoatom) { if (_qmol == 0) throw Error("'X' atom is allowed only for queries"); atom_type = _ATOM_X; } else if (buf.size() == 3 && buf[0] == 'R' && buf[1] == '#') { atom_type = _ATOM_R; label = ELEM_RSITE; } else { label = Element::fromString2(buf.ptr()); if (label == -1) { int cur_pos = strscan.tell(); QS_DEF(ReusableObjArray< Array<char> >, strs); strs.clear(); strs.push().readString("CLASS", false); strs.push().readString("SEQID", false); if (strscan.findWord(strs) != -1) _atom_types[i] = _ATOM_TEMPLATE; else _atom_types[i] = _ATOM_PSEUDO; strscan.seek(cur_pos, SEEK_SET); } } strscan.skipSpace(); float x = strscan.readFloat(); strscan.skipSpace(); float y = strscan.readFloat(); strscan.skipSpace(); float z = strscan.readFloat(); strscan.skipSpace(); int aamap = strscan.readInt1(); if (_mol != 0) { _mol->addAtom(label); if (atom_type == _ATOM_PSEUDO) { _preparePseudoAtomLabel(buf); _mol->setPseudoAtom(i, buf.ptr()); } else if (atom_type == _ATOM_TEMPLATE) { _preparePseudoAtomLabel(buf); _mol->setTemplateAtom(i, buf.ptr()); } } else { if (atom_type == _ATOM_LIST) _qmol->addAtom(query_atom.release()); else if (atom_type == _ATOM_NOTLIST) _qmol->addAtom(QueryMolecule::Atom::nicht(query_atom.release())); else if (atom_type == _ATOM_ELEMENT) _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, label)); else if (atom_type == _ATOM_PSEUDO) _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, buf.ptr())); else if (atom_type == _ATOM_TEMPLATE) _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE, buf.ptr())); else if (atom_type == _ATOM_A) _qmol->addAtom(QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H))); else if (atom_type == _ATOM_X) { AutoPtr<QueryMolecule::Atom> atom(new QueryMolecule::Atom()); atom->type = QueryMolecule::OP_OR; atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I)); atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At)); _qmol->addAtom(atom.release()); } else if (atom_type == _ATOM_Q) _qmol->addAtom(QueryMolecule::Atom::und( QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), QueryMolecule::Atom::nicht( new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)))); else // _ATOM_R _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); } int hcount = 0; int irflag = 0; int ecflag = 0; int radical = 0; while (true) { strscan.skipSpace(); if (strscan.isEOF()) break; QS_DEF(Array<char>, prop_arr); strscan.readWord(prop_arr, "="); strscan.skip(1); const char *prop = prop_arr.ptr(); if (strcmp(prop, "CHG") == 0) { int charge = strscan.readInt1(); if (_mol != 0) _mol->setAtomCharge_Silent(i, charge); else { _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, charge))); } } else if (strcmp(prop, "RAD") == 0) { radical = strscan.readInt1(); if (_qmol != 0) { _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical))); } } else if (strcmp(prop, "CFG") == 0) { strscan.readInt1(); //int cfg = strscan.readInt1(); //if (cfg == 3) // _stereocenter_types[idx] = MoleculeStereocenters::ATOM4; } else if (strcmp(prop, "MASS") == 0) { isotope = strscan.readInt1(); } else if (strcmp(prop, "VAL") == 0) { int valence = strscan.readInt1(); if (valence == -1) valence = 0; if (_mol != 0) { _mol->setExplicitValence(i, valence); } else { _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_VALENCE, valence))); } } else if (strcmp(prop, "HCOUNT") == 0) { int hcount = strscan.readInt1(); if (hcount == -1) _hcount[i] = 1; else if (hcount > 0) _hcount[i] = hcount + 1; // to comply to the code in _postLoad() else throw Error("invalid HCOUNT value: %d", hcount); } else if (strcmp(prop, "STBOX") == 0) _stereo_care_atoms[i] = strscan.readInt1(); else if (strcmp(prop, "INVRET") == 0) irflag = strscan.readInt1(); else if (strcmp(prop, "EXACHG") == 0) ecflag = strscan.readInt1(); else if (strcmp(prop, "SUBST") == 0) { if (_qmol == 0) throw Error("substitution count is allowed only for queries"); int subst = strscan.readInt1(); if (subst != 0) { if (subst == -1) _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, 0))); else if (subst == -2) throw Error("Query substitution count as drawn is not supported yet (SUBST=%d)", subst); else if (subst > 0) _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, subst, (subst < 6 ? subst : 100)))); else throw Error("invalid SUBST value: %d", subst); } } else if (strcmp(prop, "UNSAT") == 0) { if (_qmol == 0) throw Error("unsaturation flag is allowed only for queries"); bool unsat = (strscan.readInt1() > 0); if (unsat) _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_UNSATURATION, 0))); } else if (strcmp(prop, "RBCNT") == 0) { if (_qmol == 0) throw Error("ring bond count is allowed only for queries"); int rb = strscan.readInt1(); if (rb != 0) { if (rb == -1) rb = 0; if (rb > 1) _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, rb, (rb < 4 ? rb : 100)))); else throw Error("invalid RBCNT value: %d", rb); } } else if (strcmp(prop, "RGROUPS") == 0) { int n_rg; strscan.skip(1); // skip '(' n_rg = strscan.readInt1(); while (n_rg-- > 0) _bmol->allowRGroupOnRSite(i, strscan.readInt1()); } else if (strcmp(prop, "ATTCHPT") == 0) { int att_type = strscan.readInt1(); if (att_type == -1) att_type = 3; for (int att_idx = 0; (1 << att_idx) <= att_type; att_idx++) if (att_type & (1 << att_idx)) _bmol->addAttachmentPoint(att_idx + 1, i); } else if (strcmp(prop, "ATTCHORD") == 0) { int n_items, nei_idx, att_type; QS_DEF(Array<char>, att_id); strscan.skip(1); // skip '(' n_items = strscan.readInt1() / 2; while (n_items-- > 0) { nei_idx = strscan.readInt1(); if (atom_type == _ATOM_R) { att_type = strscan.readInt1(); _bmol->setRSiteAttachmentOrder(i, nei_idx - 1, att_type - 1); } else { strscan.readWord(att_id, " )"); att_id.push(0); _bmol->setTemplateAtomAttachmentOrder(i, nei_idx - 1, att_id.ptr()); strscan.skip(1); // skip stop character } } } else if (strcmp(prop, "CLASS") == 0) { QS_DEF(Array<char>, temp_class); strscan.readWord(temp_class, 0); temp_class.push(0); if (_mol != 0) _mol->setTemplateAtomClass(i, temp_class.ptr()); else { _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE_CLASS, temp_class.ptr()))); } } else if (strcmp(prop, "SEQID") == 0) { int seq_id = strscan.readInt1(); if (_mol != 0) _mol->setTemplateAtomSeqid(i, seq_id); else { _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE_SEQID, seq_id))); } } else { throw Error("unsupported property of CTAB3000: %s", prop); } } if (isotope != 0) { if (_mol != 0) _mol->setAtomIsotope(i, isotope); else _qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, isotope))); } if (_mol != 0) _mol->setAtomRadical(i, radical); if (reaction_atom_inversion != 0) reaction_atom_inversion->at(i) = irflag; if (reaction_atom_exact_change != 0) reaction_atom_exact_change->at(i) = ecflag; if (reaction_atom_mapping != 0) reaction_atom_mapping->at(i) = aamap; _bmol->setAtomXyz(i, x, y, z); } _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 END ATOM", 15) != 0) throw Error("Error reading ATOM block footer"); _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 BEGIN BOND", 17) != 0) { if (_bonds_num > 0) throw Error("Error reading BOND block header"); } else { for (i = 0; i < _bonds_num; i++) { int reacting_center = 0; _readMultiString(str); BufferScanner strscan(str.ptr()); strscan.readInt1(); // bond index -- ignored int order = strscan.readInt1(); int beg = strscan.readInt1() - 1; int end = strscan.readInt1() - 1; if (_mol != 0) { if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC) _mol->addBond_Silent(beg, end, order); else if (order == _BOND_COORDINATION || order == _BOND_HYDROGEN) _mol->addBond_Silent(beg, end, BOND_ZERO); else if (order == _BOND_SINGLE_OR_DOUBLE) throw Error("'single or double' bonds are allowed only for queries"); else if (order == _BOND_SINGLE_OR_AROMATIC) throw Error("'single or aromatic' bonds are allowed only for queries"); else if (order == _BOND_DOUBLE_OR_AROMATIC) throw Error("'double or aromatic' bonds are allowed only for queries"); else if (order == _BOND_ANY) throw Error("'any' bonds are allowed only for queries"); else throw Error("unknown bond type: %d", order); } else { AutoPtr<QueryMolecule::Bond> bond; if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC) bond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, order)); else if (order == _BOND_COORDINATION || order == _BOND_HYDROGEN) bond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_ZERO)); else if (order == _BOND_SINGLE_OR_DOUBLE) { bond.reset(QueryMolecule::Bond::und( QueryMolecule::Bond::nicht( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)), QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE)))); } else if (order == _BOND_SINGLE_OR_AROMATIC) bond.reset(QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); else if (order == _BOND_DOUBLE_OR_AROMATIC) bond.reset(QueryMolecule::Bond::oder( new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); else if (order == _BOND_ANY) bond.reset(new QueryMolecule::Bond()); else throw Error("unknown bond type: %d", order); _qmol->addBond(beg, end, bond.release()); } while (true) { strscan.skipSpace(); if (strscan.isEOF()) break; QS_DEF(Array<char>, prop); strscan.readWord(prop, "="); strscan.skip(1); int n; if (strcmp(prop.ptr(), "CFG") == 0) { n = strscan.readInt1(); if (n == 1) _bmol->setBondDirection(i, BOND_UP); else if (n == 3) _bmol->setBondDirection(i, BOND_DOWN); else if (n == 2) { int bond_order = _bmol->getBondOrder(i); if (bond_order == BOND_SINGLE) _bmol->setBondDirection(i, BOND_EITHER); else if (bond_order == BOND_DOUBLE) _ignore_cistrans[i] = 1; else throw Error("unknown bond CFG=%d for thebond order %d", n, bond_order); } else throw Error("unknown bond CFG=%d", n); } else if (strcmp(prop.ptr(), "STBOX") == 0) { if (_qmol == 0) throw Error("stereo care box is allowed only for queries"); if ((strscan.readInt1() != 0)) _stereo_care_bonds[i] = 1; } else if (strcmp(prop.ptr(), "TOPO") == 0) { if (_qmol == 0) throw Error("bond topology setting is allowed only for queries"); int topo = strscan.readInt1(); _qmol->resetBond(i, QueryMolecule::Bond::und(_qmol->releaseBond(i), new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topo == 1 ? TOPOLOGY_RING : TOPOLOGY_CHAIN))); } else if (strcmp(prop.ptr(), "RXCTR") == 0) reacting_center = strscan.readInt1(); else if (strcmp(prop.ptr(), "ENDPTS") == 0) { strscan.skip(1); // ( n = strscan.readInt1(); while (n -- > 0) { strscan.readInt(); strscan.skipSpace(); } strscan.skip(1); // ) } else if (strcmp(prop.ptr(), "ATTACH") == 0) { while (!strscan.isEOF()) { char c = strscan.readChar(); if (c == ' ') break; } } } if (reaction_bond_reacting_center != 0) reaction_bond_reacting_center->at(i) = reacting_center; } _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 END BOND", 15) != 0) throw Error("Error reading BOND block footer"); _scanner.readLine(str, true); } // Read collections and sgroups // There is no predefined order: sgroups may appear before collection bool collection_parsed = false, sgroups_parsed = false; while (strncmp(str.ptr(), "M V30 END CTAB", 15) != 0) { if (strncmp(str.ptr(), "M V30 BEGIN COLLECTION", 23) == 0) { if (collection_parsed) throw Error("COLLECTION block has already been parsed"); _readCollectionBlock3000(); collection_parsed = true; } else if (strncmp(str.ptr(), "M V30 BEGIN SGROUP", 19) == 0) { if (sgroups_parsed) throw Error("SGROUP block has already been parsed"); _readSGroupsBlock3000(); sgroups_parsed = true; } else if (strncmp(str.ptr(), "M V30 LINKNODE", 15) == 0) throw Error("link nodes are not supported yet (%s)", str.ptr()); else throw Error("error reading CTAB block footer: %s", str.ptr()); _scanner.readLine(str, true); } } void MolfileLoader::_readSGroupsBlock3000 () { QS_DEF(Array<char>, str); while (1) { _readMultiString(str); if (strncmp(str.ptr(), "END SGROUP", 10) == 0) break; if (STRCMP(str.ptr(), "M V30 DEFAULT") == 0) continue; _readSGroup3000(str.ptr()); } } void MolfileLoader::_readCollectionBlock3000 () { QS_DEF(Array<char>, str); while (1) { _readMultiString(str); if (strncmp(str.ptr(), "END COLLECTION", 14) == 0) break; BufferScanner strscan(str.ptr()); char coll[14]; strscan.readCharsFix(13, coll); coll[13] = 0; int stereo_type = 0; int stereo_group = 0; int n = 0; if (strcmp(coll, "MDLV30/STERAC") == 0) stereo_type = MoleculeStereocenters::ATOM_AND; else if (strcmp(coll, "MDLV30/STEREL") == 0) stereo_type = MoleculeStereocenters::ATOM_OR; else if (strcmp(coll, "MDLV30/STEABS") == 0) stereo_type = MoleculeStereocenters::ATOM_ABS; else if (strcmp(coll, "MDLV30/HILITE") == 0) { QS_DEF(Array<char>, what); strscan.skipSpace(); strscan.readWord(what, " ="); if (strcmp(what.ptr(), "ATOMS") == 0) { strscan.skip(2); // =( n = strscan.readInt1(); while (n -- > 0) _bmol->highlightAtom(strscan.readInt1() - 1); } else if (strcmp(what.ptr(), "BONDS") == 0) { strscan.skip(2); // =( n = strscan.readInt1(); while (n -- > 0) _bmol->highlightBond(strscan.readInt1() - 1); } else throw Error("unknown highlighted object: %s", what.ptr()); continue; } else { _bmol->custom_collections.add(str); continue; } if (stereo_type == MoleculeStereocenters::ATOM_OR || stereo_type == MoleculeStereocenters::ATOM_AND) stereo_group = strscan.readInt1(); else strscan.skip(1); strscan.skip(7); // ATOMS=( n = strscan.readInt1(); while (n -- > 0) { int atom_idx = strscan.readInt1() - 1; _stereocenter_types[atom_idx] = stereo_type; _stereocenter_groups[atom_idx] = stereo_group; } } } void MolfileLoader::_preparePseudoAtomLabel (Array<char> &pseudo) { // if the string is quoted, unquote it if (pseudo.size() > 2 && pseudo[0] == '\'' && pseudo[pseudo.size() - 2] == '\'') { pseudo.remove(pseudo.size() - 2); pseudo.remove(0); } if (pseudo.size() <= 1) throw Error("empty pseudo-atom"); } void MolfileLoader::_readMultiString (Array<char> &str) { QS_DEF(Array<char>, tmp); str.clear(); tmp.clear_resize(7); while (1) { bool to_next = false; _scanner.read(7, tmp.ptr()); if (strncmp(tmp.ptr(), "M V30 ", 7) != 0) throw Error("error reading multi-string in CTAB v3000"); _scanner.readLine(tmp, true); if (tmp[tmp.size() - 2] == '-') { tmp[tmp.size() - 2] = 0; tmp.pop(); to_next = true; } str.appendString(tmp.ptr(), true); if (!to_next) break; } } void MolfileLoader::_readStringInQuotes (Scanner &scanner, Array<char> *str) { char first = scanner.readChar(); if (first == ' ') return; // Check if data is already present, then append to it using new line if (str && str->size() > 0) { if (str->top() == 0) str->pop(); str->push('\n'); } // If label is in quotes then read till the end quote bool in_quotes = (first == '"'); if (!in_quotes && str) str->push(first); while (!scanner.isEOF()) { char c = scanner.readChar(); if (in_quotes) { // Break if other quote is reached if (c == '"') break; } else { // Break if space is reached if (isspace(c)) break; } if (str) str->push(c); } if (str) str->push(0); } void MolfileLoader::_readRGroups3000 () { QS_DEF(Array<char>, str); MoleculeRGroups *rgroups = &_bmol->rgroups; while (!_scanner.isEOF()) { int next_block_pos = _scanner.tell(); _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 BEGIN RGROUP", 19) == 0) { _rgfile = true; int rg_idx; if (sscanf(str.ptr(), "M V30 BEGIN RGROUP %d", &rg_idx) != 1) throw Error("can not read rgroup index"); RGroup &rgroup = rgroups->getRGroup(rg_idx); _readMultiString(str); BufferScanner strscan(str.ptr()); if (strncmp(str.ptr(), "RLOGIC", 6) != 0) throw Error("Error reading RGROUP block"); strscan.skip(7); rgroup.if_then = strscan.readInt1(); rgroup.rest_h = strscan.readInt1(); if (!strscan.isEOF()) { QS_DEF(Array<char>, occ); strscan.readLine(occ, true); _readRGroupOccurrenceRanges(occ.ptr(), rgroup.occurrence); } while (!_scanner.isEOF()) { int pos = _scanner.tell(); _scanner.readLine(str, true); if (strcmp(str.ptr(), "M V30 BEGIN CTAB") == 0) { _scanner.seek(pos, SEEK_SET); AutoPtr<BaseMolecule> fragment(_bmol->neu()); MolfileLoader loader(_scanner); loader._bmol = fragment.get(); if (_bmol->isQueryMolecule()) { loader._qmol = &fragment.get()->asQueryMolecule(); loader._mol = 0; } else { loader._qmol = 0; loader._mol = &fragment.get()->asMolecule(); } loader._readCtab3000(); loader._postLoad(); rgroup.fragments.add(fragment.release()); } else if (strcmp(str.ptr(), "M V30 END RGROUP") == 0) break; else throw Error("unexpected string in rgroup: %s", str.ptr()); } } else if ((strncmp(str.ptr(), "M END", 6) == 0) || (strncmp(str.ptr(), "M V30 BEGIN TEMPLATE", 21) == 0)) { _scanner.seek(next_block_pos, SEEK_SET); break; } else throw Error("unexpected string in rgroup: %s", str.ptr()); } } void MolfileLoader::_readSGroup3000 (const char *str) { BufferScanner scanner(str); QS_DEF(Array<char>, type); QS_DEF(Array<char>, entity); MoleculeSGroups *sgroups = &_bmol->sgroups; scanner.skipSpace(); int sgroup_idx = scanner.readInt(); scanner.skipSpace(); scanner.readWord(type, 0); type.push(0); scanner.skipSpace(); scanner.readInt(); scanner.skipSpace(); int idx = sgroups->addSGroup(type.ptr()); SGroup *sgroup = &sgroups->getSGroup(idx); sgroup->original_group = sgroup_idx; DataSGroup *dsg = 0; Superatom *sup = 0; RepeatingUnit *sru = 0; if (sgroup->sgroup_type == SGroup::SG_TYPE_DAT) dsg = (DataSGroup *)sgroup; else if (sgroup->sgroup_type == SGroup::SG_TYPE_SUP) sup = (Superatom *)sgroup; else if (sgroup->sgroup_type == SGroup::SG_TYPE_SRU) sru = (RepeatingUnit *)sgroup; int n; while (!scanner.isEOF()) { scanner.readWord(entity, "="); if (scanner.isEOF()) return; // should not actually happen scanner.skip(1); // = entity.push(0); if (strcmp(entity.ptr(), "ATOMS") == 0) { scanner.skip(1); // ( n = scanner.readInt1(); while (n -- > 0) { sgroup->atoms.push(scanner.readInt() - 1); scanner.skipSpace(); } scanner.skip(1); // ) } else if ((strcmp(entity.ptr(), "XBONDS") == 0) || (strcmp(entity.ptr(), "CBONDS") == 0) ) { scanner.skip(1); // ( n = scanner.readInt1(); while (n-- > 0) { sgroup->bonds.push(scanner.readInt() - 1); scanner.skipSpace(); } scanner.skip(1); // ) } else if (strcmp(entity.ptr(), "PATOMS") == 0) { scanner.skip(1); // ( n = scanner.readInt1(); while (n -- > 0) { int idx = scanner.readInt() - 1; if (sgroup->sgroup_type == SGroup::SG_TYPE_MUL) ((MultipleGroup *)sgroup)->parent_atoms.push(idx); scanner.skipSpace(); } scanner.skip(1); // ) } else if (strcmp(entity.ptr(), "SUBTYPE") == 0) { QS_DEF(Array<char>, subtype); scanner.readWord(subtype, 0); if (strcmp(subtype.ptr(), "ALT") == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_ALT; else if (strcmp(subtype.ptr(), "RAN") == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_RAN; else if (strcmp(subtype.ptr(), "BLO") == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_BLO; } else if (strcmp(entity.ptr(), "MULT") == 0) { int mult = scanner.readInt(); if (sgroup->sgroup_type == SGroup::SG_TYPE_MUL) ((MultipleGroup *)sgroup)->multiplier = mult; } else if (strcmp(entity.ptr(), "PARENT") == 0) { int parent = scanner.readInt(); sgroup->parent_group = parent; } else if (strcmp(entity.ptr(), "BRKTYP") == 0) { QS_DEF(Array<char>, style); scanner.readWord(style, 0); if (strcmp(style.ptr(), "BRACKET") == 0) sgroup->brk_style = _BRKTYP_SQUARE; if (strcmp(style.ptr(), "PAREN") == 0) sgroup->brk_style = _BRKTYP_ROUND; } else if (strcmp(entity.ptr(), "BRKXYZ") == 0) { scanner.skip(1); // ( n = scanner.readInt1(); if (n != 9) throw Error("BRKXYZ number is %d (must be 9)", n); scanner.skipSpace(); float x1 = scanner.readFloat(); scanner.skipSpace(); float y1 = scanner.readFloat(); scanner.skipSpace(); scanner.readFloat(); scanner.skipSpace(); // skip z float x2 = scanner.readFloat(); scanner.skipSpace(); float y2 = scanner.readFloat(); scanner.skipSpace(); scanner.readFloat(); scanner.skipSpace(); // skip z // skip 3-rd point scanner.readFloat(); scanner.skipSpace(); scanner.readFloat(); scanner.skipSpace(); scanner.readFloat(); scanner.skipSpace(); Vec2f *brackets = sgroup->brackets.push(); brackets[0].set(x1, y1); brackets[1].set(x2, y2); scanner.skip(1); // ) } else if (strcmp(entity.ptr(), "CONNECT") == 0) { char c1 = scanner.readChar(); char c2 = scanner.readChar(); if (sgroup->sgroup_type == SGroup::SG_TYPE_SRU) { if (c1 == 'H' && c2 == 'T') ((RepeatingUnit *)sgroup)->connectivity = SGroup::HEAD_TO_TAIL; else if (c1 == 'H' && c2 == 'H') ((RepeatingUnit *)sgroup)->connectivity = SGroup::HEAD_TO_HEAD; } } else if (strcmp(entity.ptr(), "FIELDNAME") == 0) { _readStringInQuotes(scanner, dsg ? &dsg->name : NULL); } else if (strcmp(entity.ptr(), "FIELDINFO") == 0) { _readStringInQuotes(scanner, dsg ? &dsg->description : NULL); } else if (strcmp(entity.ptr(), "FIELDDISP") == 0) { QS_DEF(Array<char>, substr); _readStringInQuotes(scanner, &substr); if (dsg != 0) { BufferScanner subscan(substr); _readSGroupDisplay(subscan, *dsg); } } else if (strcmp(entity.ptr(), "FIELDDATA") == 0) { _readStringInQuotes(scanner, &dsg->data); } else if (strcmp(entity.ptr(), "QUERYTYPE") == 0) { _readStringInQuotes(scanner, dsg ? &dsg->querycode : NULL); } else if (strcmp(entity.ptr(), "QUERYOP") == 0) { _readStringInQuotes(scanner, dsg ? &dsg->queryoper : NULL); } else if (strcmp(entity.ptr(), "LABEL") == 0) { while (!scanner.isEOF()) { char c = scanner.readChar(); if (c == ' ') break; if (sup != 0) sup->subscript.push(c); if (sru != 0) sru->subscript.push(c); } if (sup != 0) sup->subscript.push(0); if (sru != 0) sru->subscript.push(0); } else if (strcmp(entity.ptr(), "CLASS") == 0) { while (!scanner.isEOF()) { char c = scanner.readChar(); if (c == ' ') break; if (sup != 0) sup->sa_class.push(c); } if (sup != 0) sup->sa_class.push(0); } else if (strcmp(entity.ptr(), "ESTATE") == 0) { while (!scanner.isEOF()) { char c = scanner.readChar(); if (c == ' ') break; if (c == 'E') { if (sup != 0) sup->contracted = 0; } else { if (sup != 0) sup->contracted = 1; } } } else if (strcmp(entity.ptr(), "CSTATE") == 0) { if (sup != 0) { scanner.skip(1); // ( n = scanner.readInt1(); if (n != 4) throw Error("CSTATE number is %d (must be 4)", n); scanner.skipSpace(); Superatom::_BondConnection &bond = sup->bond_connections.push(); int idx = scanner.readInt() - 1; bond.bond_idx = idx; scanner.skipSpace(); bond.bond_dir.x = scanner.readFloat(); scanner.skipSpace(); bond.bond_dir.y = scanner.readFloat(); scanner.skipSpace(); scanner.skipUntil(")"); // Skip z coordinate scanner.skip(1); // ) } else // skip for all other sgroups { scanner.skipUntil(")"); scanner.skip(1); } } else if (strcmp(entity.ptr(), "SAP") == 0) { if (sup != 0) { scanner.skip(1); // ( n = scanner.readInt1(); if (n != 3) throw Error("SAP number is %d (must be 3)", n); scanner.skipSpace(); int idx = scanner.readInt() - 1; int idap = sup->attachment_points.add(); Superatom::_AttachmentPoint &ap = sup->attachment_points.at(idap); ap.aidx = idx; scanner.skipSpace(); ap.lvidx = scanner.readInt() - 1; scanner.skip(1); ap.apid.clear(); while (!scanner.isEOF()) { char c = scanner.readChar(); if (c == ')') break; ap.apid.push(c); } ap.apid.push(0); } else // skip for all other sgroups { scanner.skipUntil(")"); scanner.skip(1); } } else { if (scanner.lookNext() == '(') { scanner.skip(1); n = scanner.readInt1(); while (n -- > 0) { scanner.readFloat(); scanner.skipSpace(); } scanner.skip(1); // ) } else { // Unknown property that can have value in quotes: PROP=" " _readStringInQuotes(scanner, NULL); } } scanner.skipSpace(); } } void MolfileLoader::_readTGroups3000 () { QS_DEF(Array<char>, str); MoleculeTGroups *tgroups = &_bmol->tgroups; while (!_scanner.isEOF()) { _scanner.readLine(str, true); if (strncmp(str.ptr(), "M V30 BEGIN TEMPLATE", 21) == 0) { while (!_scanner.isEOF()) { int tg_idx = 0; _readMultiString(str); if (strcmp(str.ptr(), "END TEMPLATE") == 0) break; BufferScanner strscan(str.ptr()); if (strncmp(str.ptr(), "TEMPLATE", 8) == 0) { strscan.skip(8); tg_idx = strscan.readInt1(); } if (tg_idx == 0) throw Error("can not read template index"); int idx = tgroups->addTGroup(); TGroup &tgroup = tgroups->getTGroup(idx); tgroup.tgroup_id = tg_idx; QS_DEF(Array<char>, word); strscan.skipSpace(); strscan.readWord(word, " /"); char stop_char = strscan.readChar(); if (stop_char == '/') { tgroup.tgroup_class.copy(word); strscan.readWord(word, " /"); tgroup.tgroup_name.copy(word); stop_char = strscan.readChar(); if (stop_char == '/') { strscan.readWord(word, 0); tgroup.tgroup_alias.copy(word); } } else { tgroup.tgroup_name.copy(word); } while (!strscan.isEOF()) { strscan.readWord(word, "="); if (strcmp(word.ptr(), "COMMENT") == 0) { _readStringInQuotes(strscan, &tgroup.tgroup_comment); } } int pos = _scanner.tell(); _scanner.readLine(str, true); if (strcmp(str.ptr(), "M V30 BEGIN CTAB") == 0) { _scanner.seek(pos, SEEK_SET); tgroup.fragment = _bmol->neu(); MolfileLoader loader(_scanner); loader._bmol = tgroup.fragment; if (_bmol->isQueryMolecule()) { loader._qmol = (QueryMolecule *)tgroup.fragment; loader._mol = 0; } else { loader._qmol = 0; loader._mol = (Molecule *)tgroup.fragment; } loader._readCtab3000(); loader._postLoad(); } else throw Error("unexpected string in template: %s", str.ptr()); } } else if (strncmp(str.ptr(), "M END", 6) == 0) break; else throw Error("unexpected string in template: %s", str.ptr()); } } void MolfileLoader::_readSGroupDisplay (Scanner &scanner, DataSGroup &dsg) { dsg.display_pos.x = scanner.readFloatFix(10); dsg.display_pos.y = scanner.readFloatFix(10); scanner.skip(4); if (scanner.readChar() == 'A') // means "attached" dsg.detached = false; else dsg.detached = true; if (scanner.readChar() == 'R') dsg.relative = true; if (scanner.readChar() == 'U') dsg.display_units = true; int cur = scanner.tell(); scanner.seek(0, SEEK_END); int end = scanner.tell(); scanner.seek(cur, SEEK_SET); scanner.skip(3); char chars[4] = {0, 0, 0, 0}; scanner.readCharsFix(3, chars); if (strncmp(chars, "ALL", 3) == 0) dsg.num_chars = 0; else { scanner.seek(cur + 3, SEEK_CUR); dsg.num_chars = scanner.readInt1(); } scanner.skip(7); dsg.tag = scanner.readChar(); if (end - cur + 1 > 16) { scanner.skip(2); int c = scanner.readChar(); if (c >= '1' && c <= '9') dsg.dasp_pos = c - '0'; } } void MolfileLoader::_init () { _hcount.clear(); _atom_types.clear(); _sgroup_types.clear(); _sgroup_mapping.clear(); _stereo_care_atoms.clear_resize(_atoms_num); _stereo_care_atoms.zerofill(); _stereo_care_bonds.clear_resize(_bonds_num); _stereo_care_bonds.zerofill(); _stereocenter_types.clear_resize(_atoms_num); _stereocenter_types.zerofill(); _stereocenter_groups.clear_resize(_atoms_num); _stereocenter_groups.zerofill(); _sensible_bond_directions.clear_resize(_bonds_num); _sensible_bond_directions.zerofill(); _ignore_cistrans.clear_resize(_bonds_num); _ignore_cistrans.zerofill(); _stereo_care_bonds.clear_resize(_bonds_num); _stereo_care_bonds.zerofill(); if (reaction_atom_mapping != 0) reaction_atom_mapping->clear_resize(_atoms_num); if (reaction_atom_inversion != 0) reaction_atom_inversion->clear_resize(_atoms_num); if (reaction_atom_exact_change != 0) reaction_atom_exact_change->clear_resize(_atoms_num); if (reaction_bond_reacting_center != 0) reaction_bond_reacting_center->clear_resize(_bonds_num); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/molfile_saver.cpp��������������������������������������������������0000664�0000000�0000000�00000241203�12710376503�0022132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molfile_saver.h" #include <time.h> #include <sstream> #include "math/algebra.h" #include "base_cpp/output.h" #include "base_cpp/locale_guard.h" #include "molecule/base_molecule.h" #include "molecule/molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" #include "molecule/molecule_savers.h" using namespace indigo; IMPL_ERROR(MolfileSaver, "molfile saver"); CP_DEF(MolfileSaver); MolfileSaver::MolfileSaver (Output &output) : reactionAtomMapping(0), reactionAtomInversion(0), reactionAtomExactChange(0), reactionBondReactingCenter(0), _output(output), CP_INIT, TL_CP_GET(_atom_mapping), TL_CP_GET(_bond_mapping) { mode = MODE_AUTO; no_chiral = false; skip_date = false; add_stereo_desc = false; } /* * Utility functions */ const float XYZ_EPSILON = 0.0001f; void write_c(float c, std::stringstream& coords) { int strip = (int)c; if (fabs(c- strip) < XYZ_EPSILON) { coords << strip << ".0"; } else { coords << c; } } /* * Converts float to string */ void convert_xyz_to_string(Vec3f& xyz, std::stringstream& coords) { coords.str(""); write_c(xyz.x, coords); coords << " "; write_c(xyz.y, coords); coords << " "; write_c(xyz.z, coords); } void MolfileSaver::saveBaseMolecule (BaseMolecule &mol) { _saveMolecule(mol, mol.isQueryMolecule()); } void MolfileSaver::saveMolecule (Molecule &mol) { _saveMolecule(mol, false); } void MolfileSaver::saveQueryMolecule (QueryMolecule &mol) { _saveMolecule(mol, true); } void MolfileSaver::_saveMolecule (BaseMolecule &mol, bool query) { LocaleGuard locale_guard; QueryMolecule *qmol = 0; if (query) qmol = (QueryMolecule *)(&mol); if (mode == MODE_2000) _v2000 = true; else if (mode == MODE_3000) _v2000 = false; else { // auto-detect the format: save to v3000 molfile only // if v2000 is not enough _v2000 = true; if (mol.hasHighlighting()) _v2000 = false; else if (!mol.stereocenters.haveAllAbsAny() && !mol.stereocenters.haveAllAndAny()) _v2000 = false; else if (mol.vertexCount() > 999 || mol.edgeCount() > 999) _v2000 = false; } bool rg2000 = (_v2000 && mol.rgroups.getRGroupCount() > 0); if (rg2000) { struct tm lt; if (skip_date) memset(<, 0, sizeof(lt)); else { time_t tm = time(NULL); lt = *localtime(&tm); } _output.printfCR("$MDL REV 1 %02d%02d%02d%02d%02d", lt.tm_mon + 1, lt.tm_mday, lt.tm_year % 100, lt.tm_hour, lt.tm_min); _output.writeStringCR("$MOL"); _output.writeStringCR("$HDR"); } _writeHeader(mol, _output, BaseMolecule::hasZCoord(mol)); if (rg2000) { _output.writeStringCR("$END HDR"); _output.writeStringCR("$CTAB"); } if (_v2000) { _writeCtabHeader2000(_output, mol); _writeCtab2000(_output, mol, query); } else { _writeCtabHeader(_output); _writeCtab(_output, mol, query); } if (_v2000) { _writeRGroupIndices2000(_output, mol); _writeAttachmentValues2000(_output, mol); } if (rg2000) { int i, j; MoleculeRGroups &rgroups = mol.rgroups; int n_rgroups = rgroups.getRGroupCount(); for (i = 1; i <= n_rgroups; i++) { RGroup &rgroup = rgroups.getRGroup(i); if (rgroup.fragments.size() == 0) continue; _output.printf("M LOG 1 %3d %3d %3d ", i, rgroup.if_then, rgroup.rest_h); QS_DEF(Array<char>, occ); ArrayOutput occ_out(occ); _writeOccurrenceRanges(occ_out, rgroup.occurrence); for (j = 0; j < 3 - occ.size(); j++) _output.writeChar(' '); _output.write(occ.ptr(), occ.size()); _output.writeCR(); } _output.writeStringCR("M END"); _output.writeStringCR("$END CTAB"); for (i = 1; i <= n_rgroups; i++) { PtrPool<BaseMolecule> &frags = rgroups.getRGroup(i).fragments; if (frags.size() == 0) continue; _output.writeStringCR("$RGP"); _output.printfCR("%4d", i); for (j = frags.begin(); j != frags.end(); j = frags.next(j)) { BaseMolecule *fragment = frags[j]; _output.writeStringCR("$CTAB"); _writeCtabHeader2000(_output, *fragment); _writeCtab2000(_output, *fragment, query); _writeRGroupIndices2000(_output, *fragment); _writeAttachmentValues2000(_output, *fragment); _output.writeStringCR("M END"); _output.writeStringCR("$END CTAB"); } _output.writeStringCR("$END RGP"); } _output.writeStringCR("$END MOL"); } else _output.writeStringCR("M END"); } void MolfileSaver::saveCtab3000 (Molecule &mol) { _writeCtab(_output, mol, false); } void MolfileSaver::saveQueryCtab3000 (QueryMolecule &mol) { _writeCtab(_output, mol, true); } void MolfileSaver::_writeHeader (BaseMolecule &mol, Output &output, bool zcoord) { struct tm lt; if (skip_date) memset(<, 0, sizeof(lt)); else { time_t tm = time(NULL); lt = *localtime(&tm); } const char *dim; if (zcoord) dim = "3D"; else dim = "2D"; if (mol.name.ptr() != 0) output.printfCR("%s", mol.name.ptr()); else output.writeCR(); output.printfCR(" -INDIGO-%02d%02d%02d%02d%02d%s", lt.tm_mon + 1, lt.tm_mday, lt.tm_year % 100, lt.tm_hour, lt.tm_min, dim); output.writeCR(); } void MolfileSaver::_writeCtabHeader (Output &output) { output.printfCR("%3d%3d%3d%3d%3d%3d%3d%3d%3d%3d%3d V3000", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } void MolfileSaver::_writeCtabHeader2000 (Output &output, BaseMolecule &mol) { int chiral = 0; if (!no_chiral && mol.isChrial()) chiral = 1; output.printfCR("%3d%3d%3d%3d%3d%3d%3d%3d%3d%3d%3d V2000", mol.vertexCount(), mol.edgeCount(), 0, 0, chiral, 0, 0, 0, 0, 0, 999); } void MolfileSaver::_writeAtomLabel (Output &output, int label) { output.writeString(Element::toString(label)); } void MolfileSaver::_writeMultiString (Output &output, const char *string, int len) { int limit = 70; while (len > 0) { output.writeString("M V30 "); if (len <= limit) limit = len; output.write(string, limit); if (len != limit) output.writeString("-"); output.writeCR(); len -= limit; string += limit; } } bool MolfileSaver::_getRingBondCountFlagValue (QueryMolecule &qmol, int idx, int &value) { QueryMolecule::Atom &atom = qmol.getAtom(idx); int rbc; if (atom.hasConstraint(QueryMolecule::ATOM_RING_BONDS)) { if (atom.sureValue(QueryMolecule::ATOM_RING_BONDS, rbc)) { value = rbc; if (value == 0) value = -1; return true; } int rbc_values[1] = { 4 }; if (atom.sureValueBelongs(QueryMolecule::ATOM_RING_BONDS, rbc_values, 1)) { value = 4; return true; } } else if (atom.sureValue(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN, rbc)) { value = -2; return true; } return false; } bool MolfileSaver::_getSubstitutionCountFlagValue (QueryMolecule &qmol, int idx, int &value) { QueryMolecule::Atom &atom = qmol.getAtom(idx); int v; if (atom.hasConstraint(QueryMolecule::ATOM_SUBSTITUENTS)) { if (atom.sureValue(QueryMolecule::ATOM_SUBSTITUENTS, v)) { value = v; if (value == 0) value = -1; return true; } int values[1] = { 6 }; if (atom.sureValueBelongs(QueryMolecule::ATOM_SUBSTITUENTS, values, 1)) { value = 6; return true; } } else if (atom.sureValue(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN, v)) { value = -2; return true; } return false; } void MolfileSaver::_writeCtab (Output &output, BaseMolecule &mol, bool query) { QueryMolecule *qmol = 0; if (query) qmol = (QueryMolecule *)(&mol); output.writeStringCR("M V30 BEGIN CTAB"); output.printfCR("M V30 COUNTS %d %d %d 0 0", mol.vertexCount(), mol.edgeCount(), mol.countSGroups()); output.writeStringCR("M V30 BEGIN ATOM"); int i; int iw = 1; QS_DEF(Array<char>, buf); _atom_mapping.clear_resize(mol.vertexEnd()); _bond_mapping.clear_resize(mol.edgeEnd()); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i), iw++) _atom_mapping[i] = iw; std::stringstream coords; for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { int atom_number = mol.getAtomNumber(i); int isotope = mol.getAtomIsotope(i); ArrayOutput out(buf); out.printf("%d ", _atom_mapping[i]); QS_DEF(Array<int>, list); int query_atom_type; if (atom_number == ELEM_H && isotope == 2) { out.writeChar('D'); isotope = 0; } else if (atom_number == ELEM_H && isotope == 3) { out.writeChar('T'); isotope = 0; } else if (mol.isPseudoAtom(i)) out.writeString(mol.getPseudoAtom(i)); else if (mol.isTemplateAtom(i)) out.writeString(mol.getTemplateAtom(i)); else if (mol.isRSite(i)) out.writeString("R#"); else if (atom_number > 0) { _writeAtomLabel(out, atom_number); } else if (qmol != 0 && (query_atom_type = QueryMolecule::parseQueryAtom(*qmol, i, list)) != -1) { if (query_atom_type == QueryMolecule::QUERY_ATOM_A) out.writeChar('A'); else if (query_atom_type == QueryMolecule::QUERY_ATOM_Q) out.writeChar('Q'); else if (query_atom_type == QueryMolecule::QUERY_ATOM_X) out.writeChar('X'); else if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST) { int k; if (query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST) out.writeString("NOT"); out.writeChar('['); for (k = 0; k < list.size(); k++) { if (k > 0) out.writeChar(','); _writeAtomLabel(out, list[k]); } out.writeChar(']'); } } else if (atom_number == -1) out.writeChar('A'); else throw Error("molfile 3000: can not save atom %d because of unsupported " "query feature", i); int aam = 0, ecflag = 0, irflag = 0; if (reactionAtomMapping != 0) aam = reactionAtomMapping->at(i); if (reactionAtomInversion != 0) irflag = reactionAtomInversion->at(i); if (reactionAtomExactChange != 0) ecflag = reactionAtomExactChange->at(i); Vec3f &xyz = mol.getAtomXyz(i); int charge = mol.getAtomCharge(i); int radical = 0; int valence = mol.getExplicitValence(i); int stereo_parity = _getStereocenterParity(mol, i); if (!mol.isRSite(i) && !mol.isPseudoAtom(i) && !mol.isTemplateAtom(i)) radical = mol.getAtomRadical_NoThrow(i, 0); /* * Trailing zeros workaround */ convert_xyz_to_string(xyz, coords); out.printf(" %s %d", coords.str().c_str(), aam); if ((mol.isQueryMolecule() && charge != CHARGE_UNKNOWN) || (!mol.isQueryMolecule() && charge != 0)) out.printf(" CHG=%d", charge); int hcount = MoleculeSavers::getHCount(mol, i, atom_number, charge); if (hcount > 0) out.printf(" HCOUNT=%d", hcount); else if (hcount == 0) out.printf(" HCOUNT=-1"); if (radical > 0) out.printf(" RAD=%d", radical); if (stereo_parity > 0) out.printf(" CFG=%d", stereo_parity); if (isotope > 0) out.printf(" MASS=%d", isotope); if (valence > 0) out.printf(" VAL=%d", valence); if (valence == 0) out.printf(" VAL=-1"); if (irflag > 0) out.printf(" INVRET=%d", irflag); if (ecflag > 0) out.printf(" EXACHG=%d", ecflag); if (mol.isRSite(i)) { int k; QS_DEF(Array<int>, rg_list); mol.getAllowedRGroups(i, rg_list); if (rg_list.size() > 0) { out.printf(" RGROUPS=(%d", rg_list.size()); for (k = 0; k < rg_list.size(); k++) out.printf(" %d", rg_list[k]); out.writeChar(')'); if (!_checkAttPointOrder(mol, i)) { const Vertex &vertex = mol.getVertex(i); out.printf(" ATTCHORD=(%d", vertex.degree() * 2); for (k = 0; k < vertex.degree(); k++) out.printf(" %d %d", _atom_mapping[mol.getRSiteAttachmentPointByOrder(i, k)], k + 1); out.writeChar(')'); } } } if (mol.isTemplateAtom(i)) { if (mol.getTemplateAtomClass(i) != 0 && strlen(mol.getTemplateAtomClass(i)) > 0) out.printf(" CLASS=%s", mol.getTemplateAtomClass(i)); if (mol.getTemplateAtomSeqid(i) != -1) out.printf(" SEQID=%d", mol.getTemplateAtomSeqid(i)); if (mol.template_attachment_points.size() > 0) { int ap_count = 0; for (int j = mol.template_attachment_points.begin(); j != mol.template_attachment_points.end(); j = mol.template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = mol.template_attachment_points.at(j); if (ap.ap_occur_idx == i) ap_count++; } if (ap_count > 0) { out.printf(" ATTCHORD=(%d", ap_count * 2); for (int j = mol.template_attachment_points.begin(); j != mol.template_attachment_points.end(); j = mol.template_attachment_points.next(j)) { BaseMolecule::TemplateAttPoint &ap = mol.template_attachment_points.at(j); if (ap.ap_occur_idx == i) { out.printf(" %d %s", _atom_mapping[ap.ap_aidx], ap.ap_id.ptr()); } } out.printf(")"); } } } if (mol.attachmentPointCount() > 0) { int val = 0; for (int idx = 1; idx <= mol.attachmentPointCount(); idx++) { for (int j = 0; mol.getAttachmentPoint(idx, j) != -1; j++) if (mol.getAttachmentPoint(idx, j) == i) { val |= 1 << (idx - 1); break; } } if (val > 0) out.printf(" ATTCHPT=%d", val == 3 ? -1 : val); } if (qmol != 0) { int unsat; if (qmol->getAtom(i).sureValue(QueryMolecule::ATOM_UNSATURATION, unsat)) out.printf(" UNSAT=1"); int subst; if (_getSubstitutionCountFlagValue(*qmol, i, subst)) out.printf(" SUBST=%d", subst); int rbc; if (_getRingBondCountFlagValue(*qmol, i, rbc)) out.printf(" RBCNT=%d", rbc); } _writeMultiString(output, buf.ptr(), buf.size()); } output.writeStringCR("M V30 END ATOM"); output.writeStringCR("M V30 BEGIN BOND"); iw = 1; for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i), iw++) { const Edge &edge = mol.getEdge(i); int bond_order = mol.getBondOrder(i); ArrayOutput out(buf); _bond_mapping[i] = iw; if (bond_order < 0 && qmol != 0) { int qb = QueryMolecule::getQueryBondType(qmol->getBond(i)); if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE) bond_order = 5; else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC) bond_order = 6; else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC) bond_order = 7; else if (qb == QueryMolecule::QUERY_BOND_ANY) bond_order = 8; } if (bond_order < 0) throw Error("unrepresentable query bond"); if (bond_order == BOND_ZERO) { bond_order = 9; if ((mol.getAtomNumber(edge.beg) == ELEM_H) || (mol.getAtomNumber(edge.end) == ELEM_H)) bond_order = 10; } out.printf("%d %d %d %d", iw, bond_order, _atom_mapping[edge.beg], _atom_mapping[edge.end]); int direction = mol.getBondDirection(i); switch (direction) { case BOND_UP: out.printf(" CFG=1"); break; case BOND_EITHER: out.printf(" CFG=2"); break; case BOND_DOWN: out.printf(" CFG=3"); break; case 0: if (mol.cis_trans.isIgnored(i)) if (!_hasNeighborEitherBond(mol, i)) out.printf(" CFG=2"); break; } int reacting_center = 0; if(reactionBondReactingCenter != 0 && reactionBondReactingCenter->at(i) != 0) reacting_center = reactionBondReactingCenter->at(i); if (reacting_center != 0) out.printf(" RXCTR=%d", reacting_center); int indigo_topology = -1; if (qmol != 0) qmol->getBond(i).sureValue(QueryMolecule::BOND_TOPOLOGY, indigo_topology); int topology = 0; if (indigo_topology == TOPOLOGY_RING) topology = 1; else if (indigo_topology == TOPOLOGY_CHAIN) topology = 2; if (topology != 0) out.printf(" TOPO=%d", topology); _writeMultiString(output, buf.ptr(), buf.size()); } output.writeStringCR("M V30 END BOND"); MoleculeStereocenters &stereocenters = mol.stereocenters; if (stereocenters.begin() != stereocenters.end() || mol.hasHighlighting()) { output.writeStringCR("M V30 BEGIN COLLECTION"); QS_DEF(Array<int>, processed); processed.clear_resize(mol.vertexEnd()); processed.zerofill(); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (processed[i]) continue; ArrayOutput out(buf); int j, type = stereocenters.getType(i); if (type == MoleculeStereocenters::ATOM_ABS) out.writeString("MDLV30/STEABS ATOMS=("); else if (type == MoleculeStereocenters::ATOM_OR) out.printf("MDLV30/STEREL%d ATOMS=(", stereocenters.getGroup(i)); else if (type == MoleculeStereocenters::ATOM_AND) out.printf("MDLV30/STERAC%d ATOMS=(", stereocenters.getGroup(i)); else continue; QS_DEF(Array<int>, list); list.clear(); list.push(i); for (j = mol.vertexNext(i); j < mol.vertexEnd(); j = mol.vertexNext(j)) if (stereocenters.sameGroup(i, j)) { list.push(j); processed[j] = 1; } out.printf("%d", list.size()); for (j = 0; j < list.size(); j++) out.printf(" %d", _atom_mapping[list[j]]); out.writeChar(')'); _writeMultiString(output, buf.ptr(), buf.size()); } if (mol.hasHighlighting()) { if (mol.countHighlightedBonds() > 0) { ArrayOutput out(buf); out.printf("MDLV30/HILITE BONDS=(%d", mol.countHighlightedBonds()); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (mol.isBondHighlighted(i)) out.printf(" %d", _bond_mapping[i]); out.writeChar(')'); _writeMultiString(output, buf.ptr(), buf.size()); } if (mol.countHighlightedAtoms() > 0) { ArrayOutput out(buf); out.printf("MDLV30/HILITE ATOMS=(%d", mol.countHighlightedAtoms()); for (i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) if (mol.isAtomHighlighted(i)) out.printf(" %d", _atom_mapping[i]); out.writeChar(')'); _writeMultiString(output, buf.ptr(), buf.size()); } } if (mol.custom_collections.size() > 0) { for (i = mol.custom_collections.begin(); i != mol.custom_collections.end(); i = mol.custom_collections.next(i)) { ArrayOutput out(buf); out.printf("%s", mol.custom_collections.at(i)); _writeMultiString(output, buf.ptr(), buf.size()); } } output.writeStringCR("M V30 END COLLECTION"); } _updateCIPStereoDescriptors(mol); _checkSGroupIndices(mol); if (mol.countSGroups() > 0) { MoleculeSGroups *sgroups = &mol.sgroups; int idx = 1; output.writeStringCR("M V30 BEGIN SGROUP"); for (i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { ArrayOutput out(buf); SGroup &sgroup = sgroups->getSGroup(i); _writeGenericSGroup3000(sgroup, idx++, out); if (sgroup.sgroup_type == SGroup::SG_TYPE_GEN) { _writeMultiString(output, buf.ptr(), buf.size()); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &sup = (Superatom &)sgroup; if (sup.bond_connections.size() > 0) { for (int j = 0; j < sup.bond_connections.size(); j++) { out.printf(" CSTATE=(4 %d %f %f %f)", _bond_mapping[sup.bond_connections[j].bond_idx], sup.bond_connections[j].bond_dir.x, sup.bond_connections[j].bond_dir.y, 0.f); } } if (sup.subscript.size() > 1) out.printf(" LABEL=%s", sup.subscript.ptr()); if (sup.sa_class.size() > 1) out.printf(" CLASS=%s", sup.sa_class.ptr()); if (sup.contracted == 0) out.printf(" ESTATE=E"); if (sup.attachment_points.size() > 0) { for (int j = sup.attachment_points.begin(); j < sup.attachment_points.end(); j = sup.attachment_points.next(j)) { int leave_idx = 0; if (sup.attachment_points[j].lvidx > -1) leave_idx = _atom_mapping[sup.attachment_points[j].lvidx]; out.printf(" SAP=(3 %d %d %s)", _atom_mapping[sup.attachment_points[j].aidx], leave_idx, sup.attachment_points[j].apid.ptr()); } } _writeMultiString(output, buf.ptr(), buf.size()); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &dsg = (DataSGroup &)sgroup; const char *name = dsg.name.ptr(); if (name != 0 && strlen(name) > 0) { out.writeString(" FIELDNAME="); bool space_found = (strchr(name, ' ') != NULL); if (space_found) out.writeString("\""); out.writeString(name); if (space_found) out.writeString("\""); } const char *desc = dsg.description.ptr(); if (desc != 0 && strlen(desc) > 0) { out.writeString(" FIELDINFO="); bool space_found = (strchr(desc, ' ') != NULL); if (space_found) out.writeString("\""); out.writeString(desc); if (space_found) out.writeString("\""); } const char *querycode = dsg.querycode.ptr(); if (querycode != 0 && strlen(querycode) > 0) { out.writeString(" QUERYTYPE="); bool space_found = (strchr(querycode, ' ') != NULL); if (space_found) out.writeString("\""); out.writeString(querycode); if (space_found) out.writeString("\""); } const char *queryoper = dsg.queryoper.ptr(); if (queryoper != 0 && strlen(queryoper) > 0) { out.writeString(" QUERYOP="); bool space_found = (strchr(queryoper, ' ') != NULL); if (space_found) out.writeString("\""); out.writeString(queryoper); if (space_found) out.writeString("\""); } out.printf(" FIELDDISP=\""); _writeDataSGroupDisplay(dsg, out); out.printf("\""); if (dsg.data.size() > 0 && dsg.data[0] != 0) { // Split field data by new lines int len = dsg.data.size(); char *data = dsg.data.ptr(); while (len > 0) { int j; for (j = 0; j < len; j++) if (data[j] == '\n') break; out.printf(" FIELDDATA=\"%.*s\"", j, data); if (data[j] == '\n') j++; data += j; len -= j; if (*data == 0) break; } } _writeMultiString(output, buf.ptr(), buf.size()); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &ru = (RepeatingUnit &)sgroup; if (ru.connectivity == SGroup::HEAD_TO_HEAD) out.printf(" CONNECT=HH"); else if (ru.connectivity == SGroup::HEAD_TO_TAIL) out.printf(" CONNECT=HT"); else out.printf(" CONNECT=EU"); if (ru.subscript.size() > 1) out.printf(" LABEL=%s", ru.subscript.ptr()); _writeMultiString(output, buf.ptr(), buf.size()); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { MultipleGroup &mg = (MultipleGroup &)sgroup; if (mg.parent_atoms.size() > 0) { out.printf(" PATOMS=(%d", mg.parent_atoms.size()); int j; for (j = 0; j < mg.parent_atoms.size(); j++) out.printf(" %d", _atom_mapping[mg.parent_atoms[j]]); out.printf(")"); } out.printf(" MULT=%d", mg.multiplier); _writeMultiString(output, buf.ptr(), buf.size()); } else { _writeMultiString(output, buf.ptr(), buf.size()); } } output.writeStringCR("M V30 END SGROUP"); } output.writeStringCR("M V30 END CTAB"); int n_rgroups = mol.rgroups.getRGroupCount(); for (i = 1; i <= n_rgroups; i++) if (mol.rgroups.getRGroup(i).fragments.size() > 0) _writeRGroup(output, mol, i); int n_tgroups = mol.tgroups.getTGroupCount(); if (n_tgroups > 0) { output.writeStringCR("M V30 BEGIN TEMPLATE"); for (i = mol.tgroups.begin(); i != mol.tgroups.end(); i = mol.tgroups.next(i)) { _writeTGroup(output, mol, i); } output.writeStringCR("M V30 END TEMPLATE"); } } void MolfileSaver::_writeGenericSGroup3000 (SGroup &sgroup, int idx, Output &output) { int i; output.printf("%d %s %d", sgroup.original_group, SGroup::typeToString(sgroup.sgroup_type), idx); if (sgroup.atoms.size() > 0) { output.printf(" ATOMS=(%d", sgroup.atoms.size()); for (i = 0; i < sgroup.atoms.size(); i++) output.printf(" %d", _atom_mapping[sgroup.atoms[i]]); output.printf(")"); } if (sgroup.bonds.size() > 0) { if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) output.printf(" CBONDS=(%d", sgroup.bonds.size()); else output.printf(" XBONDS=(%d", sgroup.bonds.size()); for (i = 0; i < sgroup.bonds.size(); i++) output.printf(" %d", _bond_mapping[sgroup.bonds[i]]); output.printf(")"); } if (sgroup.sgroup_subtype > 0) { if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_ALT) output.printf(" SUBTYPE=ALT"); else if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_RAN) output.printf(" SUBTYPE=RAN"); else if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_BLO) output.printf(" SUBTYPE=BLO"); } if (sgroup.parent_group > 0) { output.printf(" PARENT=%d", sgroup.parent_group); } for (i = 0; i < sgroup.brackets.size(); i++) { Vec2f *brackets = sgroup.brackets[i]; output.printf(" BRKXYZ=(9 %f %f %f %f %f %f %f %f %f)", brackets[0].x, brackets[0].y, 0.f, brackets[1].x, brackets[1].y, 0.f, 0.f, 0.f, 0.f); } if (sgroup.brackets.size() > 0 && sgroup.brk_style > 0) { output.printf(" BRKTYP=PAREN"); } } void MolfileSaver::_writeOccurrenceRanges (Output &out, const Array<int> &occurrences) { for (int i = 0; i < occurrences.size(); i++) { int occurrence = occurrences[i]; if ((occurrence & 0xFFFF) == 0xFFFF) out.printf(">%d", (occurrence >> 16) - 1); else if ((occurrence >> 16) == (occurrence & 0xFFFF)) out.printf("%d", occurrence >> 16); else if ((occurrence >> 16) == 0) out.printf("<%d", (occurrence & 0xFFFF) + 1); else out.printf("%d-%d", occurrence >> 16, occurrence & 0xFFFF); if (i != occurrences.size() - 1) out.printf(","); } } void MolfileSaver::_writeRGroup (Output &output, BaseMolecule &mol, int rg_idx) { QS_DEF(Array<char>, buf); ArrayOutput out(buf); RGroup &rgroup = mol.rgroups.getRGroup(rg_idx); output.printfCR("M V30 BEGIN RGROUP %d", rg_idx); out.printf("RLOGIC %d %d ", rgroup.if_then, rgroup.rest_h); _writeOccurrenceRanges(out, rgroup.occurrence); _writeMultiString(output, buf.ptr(), buf.size()); PtrPool<BaseMolecule> &frags = rgroup.fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) _writeCtab(output, *rgroup.fragments[j], mol.isQueryMolecule()); output.writeStringCR("M V30 END RGROUP"); } void MolfileSaver::_writeTGroup (Output &output, BaseMolecule &mol, int tg_idx) { QS_DEF(Array<char>, buf); ArrayOutput out(buf); TGroup &tgroup = mol.tgroups.getTGroup(tg_idx); out.printf("TEMPLATE %d ", tgroup.tgroup_id); if (tgroup.tgroup_class.size() > 0) out.printf("%s/", tgroup.tgroup_class.ptr()); if (tgroup.tgroup_name.size() > 0) out.printf("%s", tgroup.tgroup_name.ptr()); if (tgroup.tgroup_alias.size() > 0) out.printf("/%s", tgroup.tgroup_alias.ptr()); if (tgroup.tgroup_comment.size() > 0) out.printf(" COMMENT=%s", tgroup.tgroup_comment.ptr()); _writeMultiString(output, buf.ptr(), buf.size()); _writeCtab(output, *tgroup.fragment, mol.isQueryMolecule()); } void MolfileSaver::_writeCtab2000 (Output &output, BaseMolecule &mol, bool query) { QueryMolecule *qmol = 0; if (query) qmol = (QueryMolecule *)(&mol); int i; QS_DEF(Array<int[2]>, radicals); QS_DEF(Array<int>, charges); QS_DEF(Array<int>, isotopes); QS_DEF(Array<int>, pseudoatoms); QS_DEF(Array<int>, atom_lists); QS_DEF(Array<int>, unsaturated); QS_DEF(Array<int[2]>, substitution_count); QS_DEF(Array<int[2]>, ring_bonds); _atom_mapping.clear_resize(mol.vertexEnd()); _bond_mapping.clear_resize(mol.edgeEnd()); radicals.clear(); charges.clear(); isotopes.clear(); pseudoatoms.clear(); atom_lists.clear(); unsaturated.clear(); substitution_count.clear(); ring_bonds.clear(); int iw = 1; for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i), iw++) { char label[3] = {' ', ' ', ' '}; int valence = 0, radical = 0, charge = 0, stereo_parity = 0, hydrogens_count = 0, stereo_care = 0, aam = 0, irflag = 0, ecflag = 0; int atom_number = mol.getAtomNumber(i); int atom_isotope = mol.getAtomIsotope(i); int atom_charge = mol.getAtomCharge(i); int atom_radical = 0; _atom_mapping[i] = iw; if (!mol.isRSite(i) && !mol.isPseudoAtom(i)) atom_radical = mol.getAtomRadical_NoThrow(i, 0); if (mol.isRSite(i)) { label[0] = 'R'; label[1] = '#'; } else if (mol.isPseudoAtom(i)) { const char *pseudo = mol.getPseudoAtom(i); if (strlen(pseudo) <= 3) memcpy(label, pseudo, __min(strlen(pseudo), 3)); else { label[0] = 'A'; pseudoatoms.push(i); } } else if (mol.isTemplateAtom(i)) { throw Error("internal: template atom is not supported in V2000 format"); } else if (atom_number == -1) { if (qmol == 0) throw Error("internal: atom number = -1, but qmol == 0"); QS_DEF(Array<int>, list); int query_atom_type = QueryMolecule::parseQueryAtom(*qmol, i, list); if (query_atom_type == QueryMolecule::QUERY_ATOM_A) label[0] = 'A'; else if (query_atom_type == QueryMolecule::QUERY_ATOM_Q) label[0] = 'Q'; else if (query_atom_type == QueryMolecule::QUERY_ATOM_X) label[0] = 'X'; else if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST) { label[0] = 'L'; atom_lists.push(i); } else label[0] = 'A'; //throw Error("error saving atom #%d: unsupported query atom", i); } else if (atom_number == ELEM_H && atom_isotope == 2) label[0] = 'D'; else if (atom_number == ELEM_H && atom_isotope == 3) label[0] = 'T'; else { const char *str = Element::toString(atom_number); label[0] = str[0]; if (str[1] != 0) label[1] = str[1]; if (atom_isotope > 0) isotopes.push(i); } if (reactionAtomMapping != 0) aam = reactionAtomMapping->at(i); if (reactionAtomInversion != 0) irflag = reactionAtomInversion->at(i); if (reactionAtomExactChange != 0) ecflag = reactionAtomExactChange->at(i); int explicit_valence = -1; if (!mol.isRSite(i) && !mol.isPseudoAtom(i)) explicit_valence = mol.getExplicitValence(i); if (explicit_valence > 0 && explicit_valence <= 14) valence = explicit_valence; if (explicit_valence == 0) valence = 15; if (atom_charge != CHARGE_UNKNOWN && atom_charge >= -15 && atom_charge <= 15) charge = atom_charge; if (charge != 0) charges.push(i); if (atom_radical >= 0 && atom_radical <= 3) radical = atom_radical; if (radical != 0) { int *r = radicals.push(); r[0] = i; r[1] = radical; } if (qmol != 0) { int unsat; if (qmol->getAtom(i).sureValue(QueryMolecule::ATOM_UNSATURATION, unsat)) unsaturated.push(i); int rbc; if (_getRingBondCountFlagValue(*qmol, i, rbc)) { int *r = ring_bonds.push(); r[0] = i; r[1] = rbc; } int subst; if (_getSubstitutionCountFlagValue(*qmol, i, subst)) { int *s = substitution_count.push(); s[0] = i; s[1] = subst; } } stereo_parity = _getStereocenterParity(mol, i); hydrogens_count = MoleculeSavers::getHCount(mol, i, atom_number, atom_charge); if (hydrogens_count == -1) hydrogens_count = 0; else // molfile stores h+1 hydrogens_count++; Vec3f pos = mol.getAtomXyz(i); if (fabs(pos.x) < 1e-5f) pos.x = 0; if (fabs(pos.y) < 1e-5f) pos.y = 0; if (fabs(pos.z) < 1e-5f) pos.z = 0; output.printfCR("%10.4f%10.4f%10.4f %c%c%c%2d" "%3d%3d%3d%3d%3d" "%3d%3d%3d%3d%3d%3d", pos.x, pos.y, pos.z, label[0], label[1], label[2], 0, 0, stereo_parity, hydrogens_count, stereo_care, valence, 0, 0, 0, aam, irflag, ecflag); } iw = 1; for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); int bond_order = mol.getBondOrder(i); int indigo_topology = -1; if (bond_order < 0 && qmol != 0) { int qb = QueryMolecule::getQueryBondType(qmol->getBond(i)); if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE) bond_order = 5; else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC) bond_order = 6; else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC) bond_order = 7; else if (qb == QueryMolecule::QUERY_BOND_ANY) bond_order = 8; } if (bond_order < 0) { Array<char> buf; qmol->getBondDescription(i, buf); throw Error("unrepresentable query bond: %s", buf.ptr()); } int stereo = 0; int reacting_center = 0; int direction = mol.getBondDirection(i); switch (direction) { case BOND_UP: stereo = 1; break; case BOND_DOWN: stereo = 6; break; case BOND_EITHER: stereo = 4; break; case 0: if (mol.cis_trans.isIgnored(i)) stereo = 3; break; } if (stereo == 3) { // discard it if we have a neighbor "either" bond if (_hasNeighborEitherBond(mol, i)) stereo = 0; } if (qmol != 0 && indigo_topology == -1) qmol->getBond(i).sureValue(QueryMolecule::BOND_TOPOLOGY, indigo_topology); int topology = 0; if (indigo_topology == TOPOLOGY_RING) topology = 1; else if (indigo_topology == TOPOLOGY_CHAIN) topology = 2; if(reactionBondReactingCenter != 0 && reactionBondReactingCenter->at(i) != 0) reacting_center = reactionBondReactingCenter->at(i); output.printfCR("%3d%3d%3d%3d%3d%3d%3d", _atom_mapping[edge.beg], _atom_mapping[edge.end], bond_order, stereo, 0, topology, reacting_center); _bond_mapping[i] = iw++; } if (charges.size() > 0) { int j = 0; while (j < charges.size()) { output.printf("M CHG%3d", __min(charges.size(), j + 8) - j); for (i = j; i < __min(charges.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[charges[i]], mol.getAtomCharge(charges[i])); output.writeCR(); j += 8; } } if (radicals.size() > 0) { int j = 0; while (j < radicals.size()) { output.printf("M RAD%3d", __min(radicals.size(), j + 8) - j); for (i = j; i < __min(radicals.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[radicals[i][0]], radicals[i][1]); output.writeCR(); j += 8; } } if (isotopes.size() > 0) { int j = 0; while (j < isotopes.size()) { output.printf("M ISO%3d", __min(isotopes.size(), j + 8) - j); for (i = j; i < __min(isotopes.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[isotopes[i]], mol.getAtomIsotope(isotopes[i])); output.writeCR(); j += 8; } } if (unsaturated.size() > 0) { int j = 0; while (j < unsaturated.size()) { output.printf("M UNS%3d", __min(unsaturated.size(), j + 8) - j); for (i = j; i < __min(unsaturated.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[unsaturated[i]], 1); output.writeCR(); j += 8; } } if (substitution_count.size() > 0) { int j = 0; while (j < substitution_count.size()) { output.printf("M SUB%3d", __min(substitution_count.size(), j + 8) - j); for (i = j; i < __min(substitution_count.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[substitution_count[i][0]], substitution_count[i][1]); output.writeCR(); j += 8; } } if (ring_bonds.size() > 0) { int j = 0; while (j < ring_bonds.size()) { output.printf("M RBC%3d", __min(ring_bonds.size(), j + 8) - j); for (i = j; i < __min(ring_bonds.size(), j + 8); i++) output.printf(" %3d %3d", _atom_mapping[ring_bonds[i][0]], ring_bonds[i][1]); output.writeCR(); j += 8; } } for (i = 0; i < atom_lists.size(); i++) { int atom_idx = atom_lists[i]; QS_DEF(Array<int>, list); int query_atom_type = QueryMolecule::parseQueryAtom(*qmol, atom_idx, list); if (query_atom_type != QueryMolecule::QUERY_ATOM_LIST && query_atom_type != QueryMolecule::QUERY_ATOM_NOTLIST) throw Error("internal: atom list not recognized"); if (list.size() < 1) throw Error("internal: atom list size is zero"); output.printf("M ALS %3d%3d %c ", _atom_mapping[atom_idx], list.size(), query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST ? 'T' : 'F'); int j; for (j = 0; j < list.size(); j++) { char c1 = ' ', c2 = ' '; const char *str = Element::toString(list[j]); c1 = str[0]; if (str[1] != 0) c2 = str[1]; output.printf("%c%c ", c1, c2); } output.writeCR(); } for (i = 0; i < pseudoatoms.size(); i++) { output.printfCR("A %3d", _atom_mapping[pseudoatoms[i]]); output.writeString(mol.getPseudoAtom(pseudoatoms[i])); output.writeCR(); } _updateCIPStereoDescriptors(mol); QS_DEF(Array<int>, sgroup_ids); QS_DEF(Array<int>, child_ids); QS_DEF(Array<int>, parent_ids); sgroup_ids.clear(); child_ids.clear(); parent_ids.clear(); for (i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { sgroup_ids.push(i); } _checkSGroupIndices(mol); if (sgroup_ids.size() > 0) { int j; for (j = 0; j < sgroup_ids.size(); j += 8) { output.printf("M STY%3d", __min(sgroup_ids.size(), j + 8) - j); for (i = j; i < __min(sgroup_ids.size(), j + 8); i++) { SGroup *sgroup = &mol.sgroups.getSGroup(sgroup_ids[i]); output.printf(" %3d %s", sgroup->original_group, SGroup::typeToString(sgroup->sgroup_type)); } output.writeCR(); } for (j = 0; j < sgroup_ids.size(); j++) { SGroup *sgroup = &mol.sgroups.getSGroup(sgroup_ids[j]); if (sgroup->parent_group > 0) { child_ids.push(sgroup->original_group); parent_ids.push(sgroup->parent_group); } } for (j = 0; j < child_ids.size(); j += 8) { output.printf("M SPL%3d", __min(child_ids.size(), j + 8) - j); for (i = j; i < __min(child_ids.size(), j + 8); i++) { output.printf(" %3d %3d", child_ids[i], parent_ids[i]); } output.writeCR(); } for (j = 0; j < sgroup_ids.size(); j += 8) { output.printf("M SLB%3d", __min(sgroup_ids.size(), j + 8) - j); for (i = j; i < __min(sgroup_ids.size(), j + 8); i++) { SGroup *sgroup = &mol.sgroups.getSGroup(sgroup_ids[i]); output.printf(" %3d %3d", sgroup->original_group, sgroup->original_group); } output.writeCR(); } int sru_count = mol.sgroups.getSGroupCount(SGroup::SG_TYPE_SRU); for (j = 0; j < sru_count; j += 8) { output.printf("M SCN%3d", __min(sru_count, j + 8) - j); for (i = j; i < __min(sru_count, j + 8); i++) { RepeatingUnit *ru = (RepeatingUnit *)&mol.sgroups.getSGroup(i, SGroup::SG_TYPE_SRU); output.printf(" %3d ", ru->original_group); if (ru->connectivity == SGroup::HEAD_TO_HEAD) output.printf("HH "); else if (ru->connectivity == SGroup::HEAD_TO_TAIL) output.printf("HT "); else output.printf("EU "); } output.writeCR(); } for (i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sgroup = mol.sgroups.getSGroup(i); for (j = 0; j < sgroup.atoms.size(); j += 8) { int k; output.printf("M SAL %3d%3d", sgroup.original_group, __min(sgroup.atoms.size(), j + 8) - j); for (k = j; k < __min(sgroup.atoms.size(), j + 8); k++) output.printf(" %3d", _atom_mapping[sgroup.atoms[k]]); output.writeCR(); } for (j = 0; j < sgroup.bonds.size(); j += 8) { int k; output.printf("M SBL %3d%3d", sgroup.original_group, __min(sgroup.bonds.size(), j + 8) - j); for (k = j; k < __min(sgroup.bonds.size(), j + 8); k++) output.printf(" %3d", _bond_mapping[sgroup.bonds[k]]); output.writeCR(); } if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom &superatom = (Superatom &)sgroup; if (superatom.subscript.size() > 1) output.printfCR("M SMT %3d %s", superatom.original_group, superatom.subscript.ptr()); if (superatom.sa_class.size() > 1) output.printfCR("M SCL %3d %s", superatom.original_group, superatom.sa_class.ptr()); if (superatom.bond_connections.size() > 0) { for (j = 0; j < superatom.bond_connections.size(); j++) { output.printfCR("M SBV %3d %3d %9.4f %9.4f", superatom.original_group, _bond_mapping[superatom.bond_connections[j].bond_idx], superatom.bond_connections[j].bond_dir.x, superatom.bond_connections[j].bond_dir.y); } } if (superatom.contracted == 0) { output.printfCR("M SDS EXP 1 %3d", superatom.original_group); } if (superatom.attachment_points.size() > 0) { bool next_line = true; int nrem = superatom.attachment_points.size(); int k = 0; for (int j = superatom.attachment_points.begin(); j < superatom.attachment_points.end(); j = superatom.attachment_points.next(j)) { if (next_line) { output.printf("M SAP %3d%3d", superatom.original_group, __min(nrem, 6)); next_line = false; } int leave_idx = 0; if (superatom.attachment_points[j].lvidx > -1) leave_idx = _atom_mapping[superatom.attachment_points[j].lvidx]; output.printf(" %3d %3d %c%c", _atom_mapping[superatom.attachment_points[j].aidx], leave_idx, superatom.attachment_points[j].apid[0], superatom.attachment_points[j].apid[1]); k++; nrem--; if ((k == 6) || (nrem == 0)) { output.writeCR(); next_line = true; k = 0; } } } } else if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { RepeatingUnit &sru = (RepeatingUnit &)sgroup; if (sru.subscript.size() > 1) output.printfCR("M SMT %3d %s", sru.original_group, sru.subscript.ptr()); } else if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &datasgroup = (DataSGroup &)sgroup; output.printf("M SDT %3d ", datasgroup.original_group); _writeFormattedString(output, datasgroup.name, 30); _writeFormattedString(output, datasgroup.type, 2); _writeFormattedString(output, datasgroup.description, 20); _writeFormattedString(output, datasgroup.querycode, 2); _writeFormattedString(output, datasgroup.queryoper, 15); output.writeCR(); output.printf("M SDD %3d ", datasgroup.original_group); _writeDataSGroupDisplay(datasgroup, output); output.writeCR(); int k = datasgroup.data.size(); if (k > 0 && datasgroup.data.top() == 0) k--; // Exclude terminating zero char *ptr = datasgroup.data.ptr(); while (k > 0) { int j; for (j = 0; j < 69 && j < k; j++) if (ptr[j] == '\n') break; // Print ptr[0..i] output.writeString("M "); if (j != 69 || j == k) output.writeString("SED "); else output.writeString("SCD "); output.printf("%3d ", datasgroup.original_group); output.write(ptr, j); if (ptr[j] == '\n') j++; ptr += j; k -= j; output.writeCR(); } } else if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { MultipleGroup &mg = (MultipleGroup &)sgroup; for (j = 0; j < mg.parent_atoms.size(); j += 8) { int k; output.printf("M SPA %3d%3d", mg.original_group, __min(mg.parent_atoms.size(), j + 8) - j); for (k = j; k < __min(mg.parent_atoms.size(), j + 8); k++) output.printf(" %3d", _atom_mapping[mg.parent_atoms[k]]); output.writeCR(); } output.printf("M SMT %3d %d\n", mg.original_group, mg.multiplier); } for (j = 0; j < sgroup.brackets.size(); j++) { output.printf("M SDI %3d 4 %9.4f %9.4f %9.4f %9.4f\n", sgroup.original_group, sgroup.brackets[j][0].x, sgroup.brackets[j][0].y, sgroup.brackets[j][1].x, sgroup.brackets[j][1].y); } if (sgroup.brackets.size() > 0 && sgroup.brk_style > 0) { output.printf("M SBT 1 %3d %3d\n", sgroup.original_group, sgroup.brk_style); } if (sgroup.sgroup_subtype > 0) { if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_ALT) output.printf("M SST 1 %3d ALT\n", sgroup.original_group); else if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_RAN) output.printf("M SST 1 %3d RAN\n", sgroup.original_group); else if (sgroup.sgroup_subtype == SGroup::SG_SUBTYPE_BLO) output.printf("M SST 1 %3d BLO\n", sgroup.original_group); } } } } void MolfileSaver::_writeFormattedString(Output &output, Array<char> &str, int length) { int k = length; if ((str.size() > 1) && (str.size() <= length)) { output.printf("%s", str.ptr()); k -= str.size() - 1; while (k-- > 0) output.writeChar(' '); } else if (str.size() > 1) { for (k = 0; k < length; k++) { if (str[k] != 0) output.writeChar(str[k]); else output.writeChar(' '); } } else while (k-- > 0) output.writeChar(' '); } void MolfileSaver::_checkSGroupIndices (BaseMolecule &mol) { int max_idx = 0; for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup *sgroup = &mol.sgroups.getSGroup(i); if (sgroup->original_group > 0) { if (sgroup->original_group > max_idx) max_idx = sgroup->original_group; } } for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup *sgroup = &mol.sgroups.getSGroup(i); if (sgroup->original_group == 0) { max_idx++; sgroup->original_group = max_idx; } } } int MolfileSaver::_getStereocenterParity (BaseMolecule &mol, int idx) { int type = mol.stereocenters.getType(idx); if (type == 0) return 0; if (type == MoleculeStereocenters::ATOM_ANY) return 3; // Reference from "CTfile Formats. Appendix A: Stereo Notes": // Number the atoms surrounding the stereo center with 1, 2, 3, and 4 in // order of increasing atom number (position in the atom block) (a hydrogen // atom should be considered the highest numbered atom, in this case atom 4). // View the center from a position such that the bond connecting the // highest-numbered atom (4) projects behind the plane formed by atoms 1, 2, and 3. int pyramid[4]; memcpy(pyramid, mol.stereocenters.getPyramid(idx), sizeof(pyramid)); if (pyramid[3] == -1) { if (mol.isQueryMolecule()) { if (mol.getAtomNumber(idx) == -1) // This atom is not a pure atom // There are no implicit hydrogens for query molecules return 0; } // Assign implicit hydrogen the highest index pyramid[3] = mol.vertexEnd(); } else { // Replace pure hydrogen atom with the highest value for (int i = 0; i < 4; i++) { int p = pyramid[i]; if (mol.getAtomNumber(p) == ELEM_H) { bool pure_hydrogen = (mol.getAtomIsotope(p) == 0); if (!pure_hydrogen && mol.isQueryMolecule()) pure_hydrogen = !mol.asQueryMolecule().getAtom(p).hasConstraint(QueryMolecule::ATOM_ISOTOPE); if (pure_hydrogen) { pyramid[i] = mol.vertexEnd(); break; } } } } if (MoleculeStereocenters::isPyramidMappingRigid(pyramid)) return 1; // odd parity return 2; // even parity } void MolfileSaver::_writeRGroupIndices2000 (Output &output, BaseMolecule &mol) { int i, j; QS_DEF(Array<int>, atom_ids); QS_DEF(Array<int>, rg_ids); atom_ids.clear(); rg_ids.clear(); for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (!mol.isRSite(i)) continue; QS_DEF(Array<int>, rg_list); mol.getAllowedRGroups(i, rg_list); for (j = 0; j < rg_list.size(); j++) { atom_ids.push(_atom_mapping[i]); rg_ids.push(rg_list[j]); } } if (atom_ids.size() > 0) { output.printf("M RGP%3d", atom_ids.size()); for (i = 0; i < atom_ids.size(); i++) output.printf(" %3d %3d", atom_ids[i], rg_ids[i]); output.writeCR(); } for (i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (!mol.isRSite(i)) continue; if (!_checkAttPointOrder(mol, i)) { const Vertex &vertex = mol.getVertex(i); int k; output.printf("M AAL %3d%3d", _atom_mapping[i], vertex.degree()); for (k = 0; k < vertex.degree(); k++) output.printf(" %3d %3d", _atom_mapping[mol.getRSiteAttachmentPointByOrder(i, k)], k + 1); output.writeCR(); } } } void MolfileSaver::_writeAttachmentValues2000 (Output &output, BaseMolecule &fragment) { if (fragment.attachmentPointCount() == 0) return; RedBlackMap<int, int> orders; int i; for (i = 1; i <= fragment.attachmentPointCount(); i++) { int j = 0; int idx; while ((idx = fragment.getAttachmentPoint(i, j++)) != -1) { int *val; if ((val = orders.at2(_atom_mapping[idx])) == 0) orders.insert(_atom_mapping[idx], 1 << (i - 1)); else *val |= 1 << (i - 1); } } output.printf("M APO%3d", orders.size()); for (i = orders.begin(); i < orders.end(); i = orders.next(i)) output.printf(" %3d %3d", orders.key(i), orders.value(i)); output.writeCR(); } bool MolfileSaver::_checkAttPointOrder (BaseMolecule &mol, int rsite) { const Vertex &vertex = mol.getVertex(rsite); int i; for (i = 0; i < vertex.degree() - 1; i++) { int cur = mol.getRSiteAttachmentPointByOrder(rsite, i); int next = mol.getRSiteAttachmentPointByOrder(rsite, i + 1); if (cur == -1 || next == -1) return true; // here we treat "undefined" as "ok" if (cur > next) return false; } return true; } void MolfileSaver::_writeDataSGroupDisplay (DataSGroup &datasgroup, Output &out) { out.printf("%10.4f%10.4f %c%c%c", datasgroup.display_pos.x, datasgroup.display_pos.y, datasgroup.detached ? 'D' : 'A', datasgroup.relative ? 'R' : 'A', datasgroup.display_units ? 'U' : ' '); if (datasgroup.num_chars == 0) out.printf(" ALL 1 %c %1d ", datasgroup.tag, datasgroup.dasp_pos); else out.printf(" %3d 1 %c %1d ", datasgroup.num_chars, datasgroup.tag, datasgroup.dasp_pos); } bool MolfileSaver::_hasNeighborEitherBond (BaseMolecule &mol, int edge_idx) { const Edge &edge = mol.getEdge(edge_idx); const Vertex &beg = mol.getVertex(edge.beg); const Vertex &end = mol.getVertex(edge.end); int k; for (k = beg.neiBegin(); k != beg.neiEnd(); k = beg.neiNext(k)) if (mol.getBondDirection2(edge.beg, beg.neiVertex(k)) == BOND_EITHER) return true; for (k = end.neiBegin(); k != end.neiEnd(); k = end.neiNext(k)) if (mol.getBondDirection2(edge.end, end.neiVertex(k)) == BOND_EITHER) return true; return false; } void MolfileSaver::_updateCIPStereoDescriptors (BaseMolecule &mol) { // clear old stereo descriptors DAT S-groups for (auto i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sgroup = mol.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { DataSGroup &datasgroup = (DataSGroup &)sgroup; if (strcmp(datasgroup.name.ptr(), "INDIGO_CIP_DESC") == 0) { mol.sgroups.remove(i); } } } // add current stereo descriptors DAT S-groups if (add_stereo_desc) { _addCIPStereoDescriptors(mol); } } void MolfileSaver::_addCIPStereoDescriptors (BaseMolecule &mol) { QS_DEF(Molecule, unfolded_h_mol); QS_DEF(Array<int>, markers); QS_DEF(Array<int>, ligands); QS_DEF(Array<int>, used1); QS_DEF(Array<int>, used2); QS_DEF(Array<char>, st_desc); CIPContext context; int atom_idx, type, group, pyramid[4]; int parity = 0; int cip_parity = 0; unfolded_h_mol.clear(); markers.clear(); unfolded_h_mol.clone_KeepIndices(mol); unfolded_h_mol.unfoldHydrogens(&markers, -1); for (auto i = mol.stereocenters.begin(); i != mol.stereocenters.end(); i = mol.stereocenters.next(i)) { mol.stereocenters.get(i, atom_idx, type, group, pyramid); if (type == 0) continue; parity = _getStereocenterParity (mol, atom_idx); ligands.clear(); used1.clear(); used2.clear(); ligands.copy(pyramid, 4); used1.push(atom_idx); used2.push(atom_idx); context.mol = &unfolded_h_mol; context.used1 = &used1; context.used2 = &used2; context.next_level = true; context.isotope_check = false; if ( (_cip_rules_cmp(ligands[0], ligands[1], &context) == 0) || (_cip_rules_cmp(ligands[1], ligands[2], &context) == 0) || (_cip_rules_cmp(ligands[2], ligands[3], &context) == 0) || (_cip_rules_cmp(ligands[0], ligands[2], &context) == 0) || (_cip_rules_cmp(ligands[1], ligands[3], &context) == 0) || (_cip_rules_cmp(ligands[3], ligands[0], &context) == 0) ) { continue; } else { ligands.qsort(_cip_rules_cmp, &context); } if (ligands[3] == -1) { // Assign implicit hydrogen the highest index ligands[3] = mol.vertexEnd(); } else { // Replace pure hydrogen atom with the highest value for (int k = 0; k < 4; k++) { int p = ligands[k]; if (mol.getAtomNumber(p) == ELEM_H) { bool pure_hydrogen = (mol.getAtomIsotope(p) == 0); if (pure_hydrogen) { ligands[k] = mol.vertexEnd(); break; } } } } if (MoleculeStereocenters::isPyramidMappingRigid(ligands.ptr())) { cip_parity = 1; } else { cip_parity = 2; } if (cip_parity == 1) { if (parity == 1) { st_desc.readString("(R)", true); } else { st_desc.readString("(S)", true); } } else { if (parity == 1) { st_desc.readString("(S)", true); } else { st_desc.readString("(R)", true); } } int sg_idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_DAT); DataSGroup &sgroup = (DataSGroup &)mol.sgroups.getSGroup(sg_idx); sgroup.atoms.push(atom_idx); sgroup.data.copy(st_desc); sgroup.name.readString("INDIGO_CIP_DESC", true); sgroup.display_pos.x = mol.getAtomXyz(atom_idx).x; sgroup.display_pos.y = mol.getAtomXyz(atom_idx).y; sgroup.detached = true; } for (auto i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { cip_parity = mol.cis_trans.getParity(i); if (cip_parity > 0) { if (mol.getBondTopology(i) == TOPOLOGY_RING) { if (mol.edgeSmallestRingSize(i) <= 7) continue; } int beg = mol.getEdge(i).beg; int end = mol.getEdge(i).end; memcpy(pyramid, mol.cis_trans.getSubstituents(i), sizeof(pyramid)); used1.clear(); used2.clear(); used1.push(beg); used2.push(beg); context.mol = &unfolded_h_mol; context.used1 = &used1; context.used2 = &used2; context.next_level = true; context.isotope_check = false; int cmp_res1 = _cip_rules_cmp(pyramid[0], pyramid[1], &context); used1.clear(); used2.clear(); used1.push(end); used2.push(end); context.mol = &mol; context.used1 = &used1; context.used2 = &used2; context.next_level = true; context.isotope_check = false; int cmp_res2 = _cip_rules_cmp(pyramid[2], pyramid[3], &context); if ( (cmp_res1 == 0) || (cmp_res2 == 0) ) continue; if (cmp_res1 == cmp_res2) { if (cip_parity == 1) { st_desc.readString("(Z)", true); } else { st_desc.readString("(E)", true); } } else { if (cip_parity == 1) { st_desc.readString("(E)", true); } else { st_desc.readString("(Z)", true); } } int sg_idx = mol.sgroups.addSGroup(SGroup::SG_TYPE_DAT); DataSGroup &sgroup = (DataSGroup &)mol.sgroups.getSGroup(sg_idx); sgroup.atoms.push(beg); sgroup.atoms.push(end); sgroup.data.copy(st_desc); sgroup.name.readString("INDIGO_CIP_DESC", true); sgroup.display_pos.x = (mol.getAtomXyz(beg).x + mol.getAtomXyz(end).x) / 2; sgroup.display_pos.y = (mol.getAtomXyz(beg).y + mol.getAtomXyz(end).y) / 2; sgroup.detached = true; } } } int MolfileSaver::_cip_rules_cmp (int &i1, int &i2, void *context) { int res = 0; Array<int> used1; Array<int> used2; CIPContext *cur_context = (CIPContext *)context; BaseMolecule &mol = *(BaseMolecule *)cur_context->mol; used1.copy(*(Array<int> *)cur_context->used1); used2.copy(*(Array<int> *)cur_context->used2); if ((i1 == -1) && (i2 == -1)) return 0; if (i1 == -1) return 1; if (i2 == -1) return -1; if (mol.getAtomNumber(i1) > mol.getAtomNumber(i2)) return -1; else if (mol.getAtomNumber(i1) < mol.getAtomNumber(i2)) return 1; else { if (cur_context->isotope_check) { int m1 = mol.getAtomIsotope(i1) == 0 ? Element::getDefaultIsotope(mol.getAtomNumber(i1)) : mol.getAtomIsotope(i1); int m2 = mol.getAtomIsotope(i2) == 0 ? Element::getDefaultIsotope(mol.getAtomNumber(i2)) : mol.getAtomIsotope(i2); if (m1 > m2) return -1; else if (m1 < m2) return 1; } const Vertex &v1 = mol.getVertex(i1); Array<int> neibs1; Array<int> cip_neibs1; neibs1.clear(); cip_neibs1.clear(); for (auto i : v1.neighbors()) { if (used1.find(v1.neiVertex(i)) == -1) { neibs1.push(v1.neiVertex(i)); } } if (neibs1.size() > 1) { CIPContext next_context; Array<int> used1_next; Array<int> used2_next; used1_next.copy(used1); used1_next.push(i1); used2_next.copy(used1_next); next_context.mol = &mol; next_context.used1 = &used1_next; next_context.used2 = &used2_next; next_context.next_level = false; next_context.isotope_check = cur_context->isotope_check; neibs1.qsort(_cip_rules_cmp, &next_context); } cip_neibs1.copy(neibs1); if (mol.vertexInRing(i1)) { for (auto i : v1.neighbors()) { if ((used1.find(v1.neiVertex(i)) != -1) && (used1.find(v1.neiVertex(i)) != (used1.size() - 1))) { int at_idx = v1.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs1.size() > 0) { for (int j = 0; j < cip_neibs1.size(); j++) { if (mol.getAtomNumber(cip_neibs1[j]) < an) { cip_neibs1.expand(cip_neibs1.size() + 1); for (auto k = cip_neibs1.size() - 1; k > j; k--) { cip_neibs1[k] = cip_neibs1[k-1]; } cip_neibs1[j] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs1.push(at_idx); } } else { cip_neibs1.push(at_idx); } } } } for (auto i : v1.neighbors()) { if (mol.getBondOrder(v1.neiEdge(i)) == BOND_SINGLE) { } else if (mol.getBondOrder(v1.neiEdge(i)) == BOND_DOUBLE) { int ins = cip_neibs1.find(v1.neiVertex(i)); if (ins > -1) { cip_neibs1.expand(cip_neibs1.size() + 1); if (ins < cip_neibs1.size() - 2) { for (auto k = cip_neibs1.size() - 1; k > ins; k--) { cip_neibs1[k] = cip_neibs1[k-1]; } } cip_neibs1[ins+1] = v1.neiVertex(i); } else if ((used1.find(v1.neiVertex(i)) != -1) && (used1.find(v1.neiVertex(i)) == (used1.size() - 1))) { int at_idx = v1.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs1.size() > 0) { for (int j = 0; j < cip_neibs1.size(); j++) { if (mol.getAtomNumber(cip_neibs1[j]) < an) { cip_neibs1.expand(cip_neibs1.size() + 1); for (auto k = cip_neibs1.size() - 1; k > j; k--) { cip_neibs1[k] = cip_neibs1[k-1]; } cip_neibs1[j] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs1.push(at_idx); } } else { cip_neibs1.push(at_idx); } } } else if (mol.getBondOrder(v1.neiEdge(i)) == BOND_TRIPLE) { int ins = cip_neibs1.find(v1.neiVertex(i)); if (ins > -1) { cip_neibs1.expand(cip_neibs1.size() + 2); if (ins < cip_neibs1.size() - 3) { for (auto k = cip_neibs1.size() - 1; k > ins; k--) { cip_neibs1[k] = cip_neibs1[k-2]; } } cip_neibs1[ins+1] = v1.neiVertex(i); cip_neibs1[ins+2] = v1.neiVertex(i); } else if ((used1.find(v1.neiVertex(i)) != -1) && (used1.find(v1.neiVertex(i)) == (used1.size() - 1))) { int at_idx = v1.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs1.size() > 0) { for (int j = 0; j < cip_neibs1.size(); j++) { if (mol.getAtomNumber(cip_neibs1[j]) < an) { cip_neibs1.expand(cip_neibs1.size() + 2); for (auto k = cip_neibs1.size() - 1; k > j; k--) { cip_neibs1[k] = cip_neibs1[k-1]; cip_neibs1[k-1] = cip_neibs1[k-2]; } cip_neibs1[j] = at_idx; cip_neibs1[j+1] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs1.push(at_idx); cip_neibs1.push(at_idx); } } else { cip_neibs1.push(at_idx); cip_neibs1.push(at_idx); } } } else if (mol.getBondOrder(v1.neiEdge(i)) == BOND_AROMATIC) { } } const Vertex &v2 = mol.getVertex(i2); Array<int> neibs2; Array<int> cip_neibs2; neibs2.clear(); cip_neibs2.clear(); for (auto i : v2.neighbors()) { if (used2.find(v2.neiVertex(i)) == -1) { neibs2.push(v2.neiVertex(i)); } } if (neibs2.size() > 1) { CIPContext next_context; Array<int> used1_next; Array<int> used2_next; used1_next.copy(used2); used1_next.push(i2); used2_next.copy(used1_next); next_context.mol = &mol; next_context.used1 = &used1_next; next_context.used2 = &used2_next; next_context.next_level = false; next_context.isotope_check = cur_context->isotope_check; neibs2.qsort(_cip_rules_cmp, &next_context); } cip_neibs2.copy(neibs2); if (mol.vertexInRing(i2)) { for (auto i : v2.neighbors()) { if ((used2.find(v2.neiVertex(i)) != -1) && (used2.find(v2.neiVertex(i)) != (used2.size() - 1))) { int at_idx = v2.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs2.size() > 0) { for (int j = 0; j < cip_neibs2.size(); j++) { if (mol.getAtomNumber(cip_neibs2[j]) < an) { cip_neibs2.expand(cip_neibs2.size() + 1); for (auto k = cip_neibs2.size() - 1; k > j; k--) { cip_neibs2[k] = cip_neibs2[k-1]; } cip_neibs2[j] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs2.push(at_idx); } } else { cip_neibs2.push(at_idx); } } } } for (auto i : v2.neighbors()) { if (mol.getBondOrder(v2.neiEdge(i)) == BOND_SINGLE) { } else if (mol.getBondOrder(v2.neiEdge(i)) == BOND_DOUBLE) { int ins = cip_neibs2.find(v2.neiVertex(i)); if (ins > -1) { cip_neibs2.expand(cip_neibs2.size() + 1); if (ins < cip_neibs2.size() - 2) { for (auto k = cip_neibs2.size() - 1; k > ins; k--) { cip_neibs2[k] = cip_neibs2[k-1]; } } cip_neibs2[ins+1] = v2.neiVertex(i); } else if ((used2.find(v2.neiVertex(i)) != -1) && (used2.find(v2.neiVertex(i)) == (used2.size() - 1))) { int at_idx = v2.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs2.size() > 0) { for (int j = 0; j < cip_neibs2.size(); j++) { if (mol.getAtomNumber(cip_neibs2[j]) < an) { cip_neibs2.expand(cip_neibs2.size() + 1); for (auto k = cip_neibs2.size() - 1; k > j; k--) { cip_neibs2[k] = cip_neibs2[k-1]; } cip_neibs2[j] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs2.push(at_idx); } } else { cip_neibs2.push(at_idx); } } } else if (mol.getBondOrder(v2.neiEdge(i)) == BOND_TRIPLE) { int ins = cip_neibs2.find(v2.neiVertex(i)); if (ins > -1) { cip_neibs2.expand(cip_neibs2.size() + 2); if (ins < cip_neibs2.size() - 3) { for (auto k = cip_neibs2.size() - 1; k > ins; k--) { cip_neibs2[k] = cip_neibs2[k-2]; } } cip_neibs2[ins+1] = v2.neiVertex(i); cip_neibs2[ins+2] = v2.neiVertex(i); } else if ((used2.find(v2.neiVertex(i)) != -1) && (used2.find(v2.neiVertex(i)) == (used2.size() - 1))) { int at_idx = v2.neiVertex(i); int an = mol.getAtomNumber(at_idx); bool inserted = false; if (cip_neibs2.size() > 0) { for (int j = 0; j < cip_neibs2.size(); j++) { if (mol.getAtomNumber(cip_neibs2[j]) < an) { cip_neibs2.expand(cip_neibs2.size() + 2); for (auto k = cip_neibs2.size() - 1; k > j; k--) { cip_neibs2[k] = cip_neibs2[k-1]; cip_neibs2[k-1] = cip_neibs2[k-2]; } cip_neibs2[j] = at_idx; cip_neibs2[j+1] = at_idx; inserted = true; break; } } if (!inserted) { cip_neibs2.push(at_idx); cip_neibs2.push(at_idx); } } else { cip_neibs2.push(at_idx); cip_neibs2.push(at_idx); } } } else if (mol.getBondOrder(v2.neiEdge(i)) == BOND_AROMATIC) { } } if (cip_neibs2.size() > cip_neibs1.size()) { for (auto i = 0; i < cip_neibs1.size(); i++) { res = mol.getAtomNumber(cip_neibs2[i]) - mol.getAtomNumber(cip_neibs1[i]); if (res > 0) return 1; else if (res < 0) return -1; } return 1; } else if (cip_neibs2.size() < cip_neibs1.size()) { for (auto i = 0; i < cip_neibs2.size(); i++) { res = mol.getAtomNumber(cip_neibs2[i]) - mol.getAtomNumber(cip_neibs1[i]); if (res > 0) return 1; else if (res < 0) return -1; } return -1; } else if (cip_neibs1.size() > 0) { for (auto i = 0; i < cip_neibs1.size(); i++) { res = mol.getAtomNumber(cip_neibs2[i]) - mol.getAtomNumber(cip_neibs1[i]); if (res > 0) return 1; else if (res < 0) return -1; } if (cur_context->isotope_check) { for (auto i = 0; i < neibs1.size(); i++) { int m1 = mol.getAtomIsotope(neibs1[i]) == 0 ? Element::getDefaultIsotope(mol.getAtomNumber(neibs1[i])) : mol.getAtomIsotope(neibs1[i]); int m2 = mol.getAtomIsotope(neibs2[i]) == 0 ? Element::getDefaultIsotope(mol.getAtomNumber(neibs2[i])) : mol.getAtomIsotope(neibs2[i]); if (m1 > m2) return -1; else if (m1 < m2) return 1; } } if (!cur_context->next_level) return 0; int next_level_branches = 0; if (neibs2.size() > neibs1.size()) next_level_branches = neibs1.size(); else if (neibs2.size() < neibs1.size()) next_level_branches = neibs2.size(); else next_level_branches = neibs1.size(); if (next_level_branches > 0) { for (auto i = 0; i < next_level_branches; i++) { CIPContext next_context; Array<int> used1_next; Array<int> used2_next; used1_next.copy(used1); used1_next.push(i1); used2_next.copy(used2); used2_next.push(i2); next_context.mol = &mol; next_context.used1 = &used1_next; next_context.used2 = &used2_next; next_context.next_level = false; next_context.isotope_check = cur_context->isotope_check; res = _cip_rules_cmp(neibs1[i], neibs2[i], &next_context); if (res > 0) return 1; else if (res < 0) return -1; } for (auto i = 0; i < next_level_branches; i++) { CIPContext next_context; Array<int> used1_next; Array<int> used2_next; used1_next.copy(used1); used1_next.push(i1); used2_next.copy(used2); used2_next.push(i2); next_context.mol = &mol; next_context.used1 = &used1_next; next_context.used2 = &used2_next; next_context.next_level = true; next_context.isotope_check = cur_context->isotope_check; res = _cip_rules_cmp(neibs1[i], neibs2[i], &next_context); if (res > 0) return 1; else if (res < 0) return -1; } } else if (neibs2.size() > 0) return 1; else if (neibs1.size() > 0) return -1; } } if (used1.size() == 1 && !cur_context->isotope_check) { int isotope_found = 0; for (auto i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { if (mol.getAtomIsotope(i) > 0) { isotope_found = mol.getAtomIsotope(i); } } if (isotope_found > 0) { CIPContext next_context; Array<int> used1_next; Array<int> used2_next; used1_next.copy(used1); used2_next.copy(used2); next_context.mol = &mol; next_context.used1 = &used1_next; next_context.used2 = &used2_next; next_context.next_level = false; next_context.isotope_check = true; res = _cip_rules_cmp(i1, i2, &next_context); if (res > 0) return 1; else if (res < 0) return -1; } } return res; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/multiple_cdx_loader.cpp��������������������������������������������0000664�0000000�0000000�00000020751�12710376503�0023325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/multiple_cdx_loader.h" #include "base_cpp/scanner.h" #include "base_cpp/output.h" typedef unsigned short int UINT16; typedef int INT32; typedef unsigned int UINT32; #include "molecule/CDXConstants.h" using namespace indigo; IMPL_ERROR(MultipleCdxLoader, "multiple CDX loader"); CP_DEF(MultipleCdxLoader); MultipleCdxLoader::MultipleCdxLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(data), TL_CP_GET(properties), TL_CP_GET(_offsets), TL_CP_GET(_latest_text), _scanner(scanner) { data.clear(); properties.clear(); _current_number = 0; _max_offset = 0; _offsets.clear(); _reaction = false; } bool MultipleCdxLoader::isEOF () { return !_hasNextObject(); } void MultipleCdxLoader::readNext () { int beg; int size; properties.clear(); bool found = _findObject(beg, size); if (!found) throw Error("end of stream"); _offsets.expand(_current_number + 1); _offsets[_current_number++] = _scanner.tell(); _scanner.seek(beg, SEEK_SET); _scanner.read(size, data); if (_scanner.tell() > _max_offset) _max_offset = _scanner.tell(); } int MultipleCdxLoader::tell () { return _scanner.tell(); } int MultipleCdxLoader::currentNumber () { return _current_number; } int MultipleCdxLoader::count () { int offset = _scanner.tell(); int cn = _current_number; if (offset != _max_offset) { _scanner.seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); } while (!isEOF()) readNext(); int res = _current_number; if (res != cn) { _scanner.seek(offset, SEEK_SET); _current_number = cn; } return res; } void MultipleCdxLoader::readAt (int index) { if (index < _offsets.size()) { _scanner.seek(_offsets[index], SEEK_SET); _current_number = index; readNext(); } else { _scanner.seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); do { readNext(); } while (index + 1 != _offsets.size()); } } bool MultipleCdxLoader::isReaction () { return _reaction; } bool MultipleCdxLoader::_hasNextObject () { int pos; int size; if (_scanner.isEOF()) return false; return _findObject(pos, size); } bool MultipleCdxLoader::_findObject (int &beg, int &length) { if (_scanner.isEOF()) return false; int i; int pos_saved = _scanner.tell(); int fragment_pos = -1; int reaction_pos = -1; _latest_text.clear(); _checkHeader(); UINT16 tag; UINT16 size; UINT32 id; int level = 1; int level_found = 0; while (!_scanner.isEOF() && ((_scanner.length() - _scanner.tell()) > 1)) { tag = _scanner.readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner.readBinaryDword(); if (tag == kCDXObj_ReactionScheme) { level_found = level; reaction_pos = _scanner.tell() - sizeof(tag) - sizeof(id); _getObject(); } else if (tag == kCDXObj_Fragment) { level_found = level; fragment_pos = _scanner.tell() - sizeof(tag) - sizeof(id); _getObject(); } else { level++; } } else if (tag == 0) { if (level > 1) level--; } else { size = _scanner.readBinaryWord(); _scanner.seek(size, SEEK_CUR); } if (level == level_found) { if (reaction_pos != -1) { beg = reaction_pos; length = _scanner.tell() - reaction_pos; _reaction = true; _scanner.seek(pos_saved, SEEK_SET); return true; } if (fragment_pos != -1) { beg = fragment_pos; length = _scanner.tell() - fragment_pos; _reaction = false; _scanner.seek(pos_saved, SEEK_SET); return true; } _scanner.seek(pos_saved, SEEK_SET); return false; } } _scanner.seek(pos_saved, SEEK_SET); return false; } void MultipleCdxLoader::_getObject () { UINT16 tag; UINT16 size; UINT32 id; QS_DEF(Array<char>, name); QS_DEF(Array<char>, value); name.clear(); value.clear(); int type = 0; int level = 1; while (!_scanner.isEOF()) { tag = _scanner.readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner.readBinaryDword(); if (tag == kCDXObj_Text) { _getObject(); } else if((tag == kCDXObj_ObjectTag)) { _getObject(); } else if((tag == 0x802b)) { _getObject(); } else _skipObject(); } else if (tag == 0) { level--; } else { size = _scanner.readBinaryWord(); switch (tag) { case kCDXProp_Text: _getString(size, _latest_text); break; case kCDXProp_Name: _getString(size, name); break; case kCDXProp_ObjectTag_Value: _getValue(type, size, value); break; case kCDXProp_ObjectTag_Type: type = _scanner.readBinaryWord(); break; case 0x1500: _getString(size, name); break; case 0x1501: _getString(size, value); break; default: _scanner.seek (size, SEEK_CUR); break; } } if (level == 0) { if (name.size() > 0) { auto& propVal = properties.insert(name.ptr()); if (value.size() > 0) propVal.readString(value.ptr(), true); else if (_latest_text.size() > 0) { propVal.readString(_latest_text.ptr(), true); _latest_text.clear(); } } return; } } } void MultipleCdxLoader::_skipObject () { UINT16 tag; UINT16 size; UINT32 id; int level = 1; while (!_scanner.isEOF()) { tag = _scanner.readBinaryWord(); if (tag & kCDXTag_Object) { id = _scanner.readBinaryDword(); _skipObject(); } else if (tag == 0) { level--; } else { size = _scanner.readBinaryWord(); _scanner.seek (size, SEEK_CUR); } if (level == 0) return; } } void MultipleCdxLoader::_checkHeader () { int pos_saved = _scanner.tell(); if ((_scanner.length() - pos_saved) < 8) return; char id[8]; _scanner.readCharsFix(8, id); if (strncmp(id, kCDX_HeaderString, kCDX_HeaderStringLen) == 0) { _scanner.seek(kCDX_HeaderLength - kCDX_HeaderStringLen, SEEK_CUR); } else { _scanner.seek(pos_saved, SEEK_SET); } } void MultipleCdxLoader::_getString (int size, Array<char> &buf) { UINT16 flag = 0; buf.clear_resize(size); buf.zerofill(); if (size < 3) { _scanner.seek (size, SEEK_CUR); } else { flag = _scanner.readBinaryWord(); if (flag == 0) _scanner.read(size-sizeof(flag), buf); else { _scanner.seek (flag*10, SEEK_CUR); _scanner.read(size-sizeof(flag)-flag*10, buf); } } return; } void MultipleCdxLoader::_getValue (int type, int size, Array<char> &buf) { double dval; int ival; ArrayOutput output(buf); switch (type) { case kCDXObjectTagType_Double: _scanner.read(sizeof(dval), &dval); output.printf("%f", dval); break; case kCDXObjectTagType_Long: _scanner.read(sizeof(ival), &ival); output.printf("%d", ival); break; case kCDXObjectTagType_String: _scanner.read(size, buf); break; default: _scanner.seek (size, SEEK_CUR); break; } return; } �����������������������Indigo-indigo-1.2.3/molecule/src/multiple_cml_loader.cpp��������������������������������������������0000664�0000000�0000000�00000005717�12710376503�0023327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/multiple_cml_loader.h" #include "base_cpp/scanner.h" using namespace indigo; IMPL_ERROR(MultipleCmlLoader, "multiple CML loader"); CP_DEF(MultipleCmlLoader); MultipleCmlLoader::MultipleCmlLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(data), TL_CP_GET(_tags), TL_CP_GET(_offsets), _scanner(scanner) { _tags.clear(); _tags.push().readString("<reaction", false); _tags.push().readString("<molecule", false); _current_number = 0; _max_offset = 0; _offsets.clear(); _reaction = false; } bool MultipleCmlLoader::isEOF () { return _scanner.findWord(_tags) == -1; } void MultipleCmlLoader::readNext () { int k = _scanner.findWord(_tags); if (k == -1) throw Error("end of stream"); _offsets.expand(_current_number + 1); _offsets[_current_number++] = _scanner.tell(); int beg = _scanner.tell(); int size; if (k == 1) { if (!_scanner.findWord("</molecule>")) throw Error("no </molecule> tag"); size = _scanner.tell() - beg + strlen("</molecule>"); _reaction = false; } else { if (!_scanner.findWord("</reaction>")) throw Error("no </reaction> tag"); size = _scanner.tell() - beg + strlen("</reaction>"); _reaction = true; } _scanner.seek(beg, SEEK_SET); _scanner.read(size, data); if (_scanner.tell() > _max_offset) _max_offset = _scanner.tell(); } int MultipleCmlLoader::tell () { return _scanner.tell(); } int MultipleCmlLoader::currentNumber () { return _current_number; } int MultipleCmlLoader::count () { int offset = _scanner.tell(); int cn = _current_number; if (offset != _max_offset) { _scanner.seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); } while (!isEOF()) readNext(); int res = _current_number; if (res != cn) { _scanner.seek(offset, SEEK_SET); _current_number = cn; } return res; } void MultipleCmlLoader::readAt (int index) { if (index < _offsets.size()) { _scanner.seek(_offsets[index], SEEK_SET); _current_number = index; readNext(); } else { _scanner.seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); do { readNext(); } while (index + 1 != _offsets.size()); } } bool MultipleCmlLoader::isReaction () { return _reaction; } �������������������������������������������������Indigo-indigo-1.2.3/molecule/src/query_molecule.cpp�������������������������������������������������0000664�0000000�0000000�00000146633�12710376503�0022350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/query_molecule.h" #include "molecule/molecule_arom.h" #include "base_cpp/output.h" #include "molecule/elements.h" #include "molecule/molecule_standardize.h" using namespace indigo; QueryMolecule::QueryMolecule () { } QueryMolecule::~QueryMolecule () { } QueryMolecule& QueryMolecule::asQueryMolecule () { return *this; } bool QueryMolecule::isQueryMolecule () { return true; } int QueryMolecule::getAtomNumber (int idx) { int res; QueryMolecule::Atom *atom = _atoms[idx]; if (atom->sureValue(ATOM_NUMBER, res)) return res; return -1; } int QueryMolecule::getAtomIsotope (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_ISOTOPE, res)) return res; return -1; } int QueryMolecule::getAtomCharge (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_CHARGE, res)) return res; return CHARGE_UNKNOWN; } int QueryMolecule::getAtomRadical (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_RADICAL, res)) return res; return -1; } int QueryMolecule::getExplicitValence (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_VALENCE, res)) return res; return -1; } int QueryMolecule::getAtomAromaticity (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_AROMATICITY, res)) return res; return -1; } int QueryMolecule::getBondOrder (int idx) { int res; if (_bonds[idx]->sureValue(BOND_ORDER, res)) return res; return -1; } int QueryMolecule::getBondTopology (int idx) { int res; if (getEdgeTopology(idx) == TOPOLOGY_RING) return TOPOLOGY_RING; if (_bonds[idx]->sureValue(BOND_TOPOLOGY, res)) return res; return -1; } int QueryMolecule::getAtomValence (int idx) { throw Error("not implemented"); } int QueryMolecule::getAtomSubstCount (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_SUBSTITUENTS, res)) return res; if (_atoms[idx]->sureValue(ATOM_SUBSTITUENTS_AS_DRAWN, res)) return res; return -1; } int QueryMolecule::getAtomRingBondsCount (int idx) { int res; if (_atoms[idx]->sureValue(ATOM_RING_BONDS, res)) return res; if (_atoms[idx]->sureValue(ATOM_RING_BONDS_AS_DRAWN, res)) return res; return -1; } bool QueryMolecule::atomNumberBelongs (int idx, const int *numbers, int count) { return _atoms[idx]->sureValueBelongs(ATOM_NUMBER, numbers, count); } bool QueryMolecule::possibleAtomNumber (int idx, int number) { QueryMolecule::Atom *atom = _atoms[idx]; if (!atom->possibleValue(ATOM_NUMBER, number)) return false; return true; } bool QueryMolecule::possibleAtomNumberAndCharge (int idx, int number, int charge) { return _atoms[idx]->possibleValuePair(ATOM_NUMBER, number, ATOM_CHARGE, charge); } bool QueryMolecule::possibleAtomNumberAndIsotope (int idx, int number, int isotope) { return _atoms[idx]->possibleValuePair(ATOM_NUMBER, number, ATOM_ISOTOPE, isotope); } bool QueryMolecule::possibleAtomIsotope (int idx, int isotope) { return _atoms[idx]->possibleValue(ATOM_ISOTOPE, isotope); } bool QueryMolecule::possibleAtomCharge (int idx, int charge) { return _atoms[idx]->possibleValue(ATOM_CHARGE, charge); } bool QueryMolecule::possibleAtomRadical (int idx, int radical) { return _atoms[idx]->possibleValue(ATOM_RADICAL, radical); } void QueryMolecule::getAtomDescription (int idx, Array<char> &description) { ArrayOutput out(description); out.writeChar('['); _getAtomDescription(_atoms[idx], out, 0); out.writeChar(']'); out.writeChar(0); } void QueryMolecule::_getAtomDescription (Atom *atom, Output &out, int depth) { int i; switch (atom->type) { case OP_NONE: out.writeChar('*'); return; case OP_AND: { if (depth > 0) out.writeChar('('); for (i = 0; i < atom->children.size(); i++) { if (i > 0) out.writeString(";"); _getAtomDescription((Atom *)atom->children[i], out, depth+1); } if (depth > 0) out.writeChar(')'); return; } case OP_OR: { if (depth > 0) out.writeChar('('); for (i = 0; i < atom->children.size(); i++) { if (i > 0) out.writeString(","); _getAtomDescription((Atom *)atom->children[i], out, depth+1); } if (depth > 0) out.writeChar(')'); return; } case OP_NOT: out.writeString("!"); _getAtomDescription((Atom *)atom->children[0], out,depth+1); return; case ATOM_NUMBER: out.writeString(Element::toString(atom->value_min)); return; case ATOM_PSEUDO: out.writeString(atom->alias.ptr()); return; case ATOM_TEMPLATE: out.writeString(atom->alias.ptr()); return; case ATOM_CHARGE: out.printf("%+d", atom->value_min); return; case ATOM_ISOTOPE: out.printf("i%d", atom->value_min); return; case ATOM_AROMATICITY: if (atom->value_min == ATOM_AROMATIC) out.printf("a"); else out.printf("A"); return; case ATOM_RADICAL: out.printf("^%d", atom->value_min); return; case ATOM_FRAGMENT: out.printf("$("); if (atom->fragment->fragment_smarts.ptr() != 0 && atom->fragment->fragment_smarts.size() > 0) out.printf("%s", atom->fragment->fragment_smarts.ptr()); out.printf(")"); return; case ATOM_TOTAL_H: out.printf("H%d", atom->value_min); return; case ATOM_CONNECTIVITY: out.printf("X%d", atom->value_min); return; case ATOM_SUBSTITUENTS: out.printf("s%d", atom->value_min); return; case ATOM_SUBSTITUENTS_AS_DRAWN: out.printf("s*"); return; case ATOM_RING_BONDS: out.printf("rb%d", atom->value_min); return; case ATOM_RING_BONDS_AS_DRAWN: out.printf("rb*"); return; case ATOM_UNSATURATION: out.printf("u"); return; case ATOM_TOTAL_BOND_ORDER: out.printf("v%d", atom->value_min); return; case ATOM_SSSR_RINGS: out.printf("R%d", atom->value_min); return; case ATOM_SMALLEST_RING_SIZE: out.printf("r%d", atom->value_min); return; default: throw new Error("Unrecognized constraint type %d", atom->type); } } void QueryMolecule::getBondDescription (int idx, Array<char> &description) { ArrayOutput out(description); _getBondDescription(_bonds[idx], out); out.writeChar(0); } void QueryMolecule::_getBondDescription (Bond *bond, Output &out) { int i; switch (bond->type) { case OP_NONE: out.writeChar('~'); return; case OP_AND: { out.writeChar('('); for (i = 0; i < bond->children.size(); i++) { if (i > 0) out.writeString(" & "); _getBondDescription((Bond *)bond->children[i], out); } out.writeChar(')'); return; } case OP_OR: { out.writeChar('('); for (i = 0; i < bond->children.size(); i++) { if (i > 0) out.writeString(" | "); _getBondDescription((Bond *)bond->children[i], out); } out.writeChar(')'); return; } case OP_NOT: out.writeString("!("); _getBondDescription((Bond *)bond->children[0], out); out.writeChar(')'); return; case BOND_ORDER: out.printf("order = %d", bond->value); return; case BOND_TOPOLOGY: out.printf("%s", bond->value == TOPOLOGY_RING ? "ring" : "chain"); return; default: out.printf("<constraint of type %d>", bond->type); } } bool QueryMolecule::possibleBondOrder (int idx, int order) { return _bonds[idx]->possibleValue(BOND_ORDER, order); } bool QueryMolecule::possibleNitrogenV5 (int idx) { if (!possibleAtomNumber(idx, ELEM_N)) return false; if (!possibleAtomCharge(idx, 0)) return false; // Other conditions also can be checked as in Molecule::isNitrogenV5 but // two the meaning of this function can be different: check if nitrogen // can valence 5 in the embedding or check if nitrogen can have valence 5 // in the original query molecule as self-contained molecule return true; } bool QueryMolecule::isPseudoAtom (int idx) { // This is dirty hack; however, it is legal here, as pseudo atoms // can not be present in deep query trees due to Molfile and SMILES // format limitations. If they could, we would have to implement // sureValue() for ATOM_PSEUDO if (_atoms[idx]->type == ATOM_PSEUDO) return true; if (_atoms[idx]->type == OP_AND) { int i; for (i = 0; i < _atoms[idx]->children.size(); i++) if (_atoms[idx]->children[i]->type == ATOM_PSEUDO) return true; } return false; } const char * QueryMolecule::getPseudoAtom (int idx) { // see the comment above in isPseudoAtom() if (_atoms[idx]->type == ATOM_PSEUDO) return _atoms[idx]->alias.ptr(); if (_atoms[idx]->type == OP_AND) { int i; for (i = 0; i < _atoms[idx]->children.size(); i++) if (_atoms[idx]->children[i]->type == ATOM_PSEUDO) return ((Atom *)_atoms[idx]->children[i])->alias.ptr(); } throw Error("getPseudoAtom() applied to something that is not a pseudo-atom"); } bool QueryMolecule::isTemplateAtom (int idx) { // This is dirty hack; however, it is legal here, as template atoms // can not be present in deep query trees due to Molfile and SMILES // format limitations. If they could, we would have to implement // sureValue() for ATOM_TEMPLATE if (_atoms[idx]->type == ATOM_TEMPLATE) return true; if (_atoms[idx]->type == OP_AND) { int i; for (i = 0; i < _atoms[idx]->children.size(); i++) if (_atoms[idx]->children[i]->type == ATOM_TEMPLATE) return true; } return false; } const char * QueryMolecule::getTemplateAtom (int idx) { // see the comment above in isTemplateAtom() if (_atoms[idx]->type == ATOM_TEMPLATE) return _atoms[idx]->alias.ptr(); if (_atoms[idx]->type == OP_AND) { int i; for (i = 0; i < _atoms[idx]->children.size(); i++) if (_atoms[idx]->children[i]->type == ATOM_TEMPLATE) return ((Atom *)_atoms[idx]->children[i])->alias.ptr(); } throw Error("getTemplateAtom() applied to something that is not a template atom"); } const char * QueryMolecule::getTemplateAtomClass (int idx) { return 0; } const int QueryMolecule::getTemplateAtomSeqid (int idx) { return -1; } const int QueryMolecule::getTemplateAtomDisplayOption (int idx) { return -1; } bool QueryMolecule::isSaturatedAtom (int idx) { throw Error("not implemented"); } QueryMolecule::Node::Node (int type_) { type = (OpType)type_; } QueryMolecule::Node::~Node () { } IMPL_ERROR(QueryMolecule::Atom, "query atom"); QueryMolecule::Atom::Atom () : Node(OP_NONE) { value_min = 0; value_max = 0; } QueryMolecule::Atom::Atom (int type_, int value) : Node(type_) { if (type_ == ATOM_NUMBER || type_ == ATOM_CHARGE || type_ == ATOM_ISOTOPE || type_ == ATOM_RADICAL || type_ == ATOM_AROMATICITY || type_ == ATOM_VALENCE || type_ == ATOM_RING_BONDS || type_ == ATOM_RING_BONDS_AS_DRAWN || type_ == ATOM_SUBSTITUENTS || type_ == ATOM_SUBSTITUENTS_AS_DRAWN || type_ == ATOM_TOTAL_H || type_ == ATOM_CONNECTIVITY || type_ == ATOM_TOTAL_BOND_ORDER || type_ == ATOM_UNSATURATION || type == ATOM_SSSR_RINGS || type == ATOM_SMALLEST_RING_SIZE || type == ATOM_RSITE || type == HIGHLIGHTING || type == ATOM_TEMPLATE_SEQID) value_min = value_max = value; else throw Error("bad type: %d", type_); } QueryMolecule::Atom::Atom (int type_, int val_min, int val_max) : Node(type_) { value_min = val_min; value_max = val_max; } QueryMolecule::Atom::Atom (int type_, const char *value) : Node(type_) { if (type_ == ATOM_PSEUDO || type_ == ATOM_TEMPLATE || type_ == ATOM_TEMPLATE_CLASS) alias.readString(value, true); else throw Error("bad type: %d", type_); } QueryMolecule::Atom::Atom (int type_, QueryMolecule *value) : Node(type_) { if (type_ == ATOM_FRAGMENT) fragment.reset(value); else throw Error("bad type: %d", type_); } QueryMolecule::Atom::~Atom () { } QueryMolecule::Bond::Bond () : Node(OP_NONE) { } QueryMolecule::Bond::Bond (int type_, int value_) : Node(type_) { value = value_; } QueryMolecule::Bond::~Bond () { } QueryMolecule::Node * QueryMolecule::Node::_und (QueryMolecule::Node *node1, QueryMolecule::Node *node2) { if (node1->type == QueryMolecule::OP_NONE) { delete node1; return node2; } if (node2->type == QueryMolecule::OP_NONE) { delete node2; return node1; } if (node1->type == QueryMolecule::OP_AND) { if (node2->type == QueryMolecule::OP_AND) { while (node2->children.size() != 0) node1->children.add(node2->children.pop()); } else node1->children.add(node2); return node1; } if (node2->type == QueryMolecule::OP_AND) { node2->children.add(node1); return node2; } AutoPtr<QueryMolecule::Node> newnode(node1->_neu()); newnode->type = QueryMolecule::OP_AND; newnode->children.add(node1); newnode->children.add(node2); return newnode.release(); } QueryMolecule::Node * QueryMolecule::Node::_oder (QueryMolecule::Node *node1, QueryMolecule::Node *node2) { if (node1->type == QueryMolecule::OP_NONE) { delete node2; return node1; } if (node2->type == QueryMolecule::OP_NONE) { delete node1; return node2; } if (node1->type == QueryMolecule::OP_OR) { if (node2->type == QueryMolecule::OP_OR) { while (node2->children.size() != 0) node1->children.add(node2->children.pop()); } else node1->children.add(node2); return node1; } if (node2->type == QueryMolecule::OP_OR) { node2->children.add(node1); return node2; } AutoPtr<QueryMolecule::Node> newnode(node1->_neu()); newnode->type = QueryMolecule::OP_OR; newnode->children.add(node1); newnode->children.add(node2); return newnode.release(); } QueryMolecule::Node * QueryMolecule::Node::_nicht (QueryMolecule::Node *node) { if (node->type == QueryMolecule::OP_NOT) { Node * res = (Node *)node->children.pop(); delete node; return res; } AutoPtr<QueryMolecule::Node> newnode(node->_neu()); newnode->type = QueryMolecule::OP_NOT; newnode->children.add(node); return newnode.release(); } QueryMolecule::Atom * QueryMolecule::Atom::und (Atom *atom1, Atom *atom2) { return (Atom *)_und(atom1, atom2); } QueryMolecule::Atom * QueryMolecule::Atom::oder (Atom *atom1, Atom *atom2) { return (Atom *)_oder(atom1, atom2); } QueryMolecule::Atom * QueryMolecule::Atom::nicht (Atom *atom) { return (Atom *)_nicht(atom); } bool QueryMolecule::Atom::hasConstraintWithValue (int what_type, int what_value) { if (type == what_type) return value_max == what_value && value_min == what_value; if (type == OP_NONE) return false; if (type == OP_AND || type == OP_OR || type == OP_NOT) { int i; for (i = 0; i < children.size(); i++) if (((Atom *)children[i])->hasConstraintWithValue(what_type, what_value)) return true; } return false; } QueryMolecule::Bond * QueryMolecule::Bond::und (Bond *bond1, Bond *bond2) { return (Bond *)_und(bond1, bond2); } QueryMolecule::Bond * QueryMolecule::Bond::oder (Bond *bond1, Bond *bond2) { return (Bond *)_oder(bond1, bond2); } QueryMolecule::Bond * QueryMolecule::Bond::nicht (Bond *bond) { return (Bond *)_nicht(bond); } void QueryMolecule::_flipBond (int atom_parent, int atom_from, int atom_to) { int src_bond_idx = findEdgeIndex(atom_parent, atom_from); int new_bond_idx = addBond(atom_parent, atom_to, releaseBond(src_bond_idx)); // Copy aromaticity information aromaticity.setCanBeAromatic(new_bond_idx, aromaticity.canBeAromatic(src_bond_idx)); setBondStereoCare(new_bond_idx, bondStereoCare(src_bond_idx)); updateEditRevision(); } void QueryMolecule::_mergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) { QueryMolecule &mol = bmol.asQueryMolecule(); int i; // atoms for (i = 0; i < vertices.size(); i++) { int newidx = mapping[vertices[i]]; _atoms.expand(newidx + 1); _atoms.set(newidx, mol.getAtom(vertices[i]).clone()); } // bonds if (edges != 0) for (i = 0; i < edges->size(); i++) { const Edge &edge = mol.getEdge(edges->at(i)); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg == -1 || end == -1) // must have been thrown before in mergeWithSubgraph() throw Error("_mergeWithSubmolecule: internal"); int idx = findEdgeIndex(beg, end); _bonds.expand(idx + 1); _bonds.set(idx, mol.getBond(edges->at(i)).clone()); // Aromaticity if (!(skip_flags & SKIP_AROMATICITY)) aromaticity.setCanBeAromatic(idx, mol.aromaticity.canBeAromatic(edges->at(i))); if (!(skip_flags & SKIP_CIS_TRANS) && mol.cis_trans.getParity(edges->at(i)) != 0) setBondStereoCare(idx, mol.bondStereoCare(edges->at(i))); } else for (i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); int beg = mapping[edge.beg]; int end = mapping[edge.end]; if (beg == -1 || end == -1) continue; int idx = findEdgeIndex(beg, end); _bonds.expand(idx + 1); _bonds.set(idx, mol.getBond(i).clone()); // Aromaticity if (!(skip_flags & SKIP_AROMATICITY)) aromaticity.setCanBeAromatic(idx, mol.aromaticity.canBeAromatic(i)); if (!(skip_flags & SKIP_CIS_TRANS) && mol.cis_trans.getParity(i) != 0) setBondStereoCare(idx, mol.bondStereoCare(i)); } // 3D constraints if (!(skip_flags & SKIP_3D_CONSTRAINTS)) spatial_constraints.buildOnSubmolecule(mol.spatial_constraints, mapping.ptr()); // fixed atoms if (!(skip_flags & SKIP_FIXED_ATOMS)) { for (i = 0; i < mol.fixed_atoms.size(); i++) { int idx = mapping[mol.fixed_atoms[i]]; if (idx >= 0) fixed_atoms.push(idx); } } // components if (!(skip_flags & SKIP_COMPONENTS)) { for (i = 0; i < vertices.size(); i++) { int v_idx = vertices[i]; if (mol.components.size() > v_idx) { int newidx = mapping[v_idx]; components.expandFill(newidx + 1, 0); components[newidx] = mol.components[v_idx]; } } } updateEditRevision(); } void QueryMolecule::_postMergeWithSubmolecule (BaseMolecule &bmol, const Array<int> &vertices, const Array<int> *edges, const Array<int> &mapping, int skip_flags) { // Remove stereocare flags for bonds that are not cis-trans for (int i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) if (cis_trans.getParity(i) == 0) setBondStereoCare(i, false); } void QueryMolecule::_removeAtoms (const Array<int> &indices, const int *mapping) { spatial_constraints.removeAtoms(mapping); if (attachmentPointCount() > 0) { int i; for (i = 0; i < indices.size(); i++) this->removeAttachmentPointsFromAtom(indices[i]); bool empty = true; for (i = 1; i <= this->attachmentPointCount(); i++) if (this->getAttachmentPoint(i, 0) != -1) { empty = false; break; } if (empty) _attachment_index.clear(); } for (int i = 0; i < indices.size(); i++) { int idx = indices[i]; _atoms.reset(idx); if (idx < _rsite_attachment_points.size()) _rsite_attachment_points[idx].clear(); } // Collect bonds to remove QS_DEF(Array<int>, edges_to_remove); edges_to_remove.clear(); for (int i = edgeBegin(); i != edgeEnd(); i = edgeNext(i)) { const Edge &e = getEdge(i); if (mapping[e.beg] == -1 || mapping[e.end] == -1) edges_to_remove.push(i); } _removeBonds(edges_to_remove); updateEditRevision(); } void QueryMolecule::_removeBonds (const Array<int> &indices) { for (int i = 0; i < indices.size(); i++) _bonds.reset(indices[i]); _min_h.clear(); updateEditRevision(); } bool QueryMolecule::Node::sureValue (int what_type, int &value_out) { int i; switch (type) { case OP_AND: { // of course one can fool this around, specifying something like // "(C || O) && (C || N)", but we do not claim to have perfect // logic here, and we do not need one. int child_value = -1; bool child_value_valid = false; for (i = 0; i < children.size(); i++) { int child_value_tmp; if (children[i]->sureValue(what_type, child_value_tmp)) { if (!child_value_valid) { child_value_valid = true; child_value = child_value_tmp; } else { if (child_value_tmp != child_value) return false; // self-contradictory query atom it is } } } if (child_value_valid) { value_out = child_value; return true; } return false; } case OP_OR: { int child_value = -1; for (i = 0; i < children.size(); i++) { int child_value_tmp; if (children[i]->sureValue(what_type, child_value_tmp)) { if (i == 0) child_value = child_value_tmp; else if (child_value_tmp != child_value) return false; } else return false; } value_out = child_value; return true; } case OP_NOT: return children[0]->sureValueInv(what_type, value_out); case OP_NONE: return false; default: return _sureValue(what_type, value_out); } } bool QueryMolecule::Node::sureValueInv (int what_type, int &value_out) { int i; switch (type) { case OP_OR: { int child_value = -1; bool child_value_valid = false; for (i = 0; i < children.size(); i++) { int child_value_tmp; if (children[i]->sureValueInv(what_type, child_value_tmp)) { if (!child_value_valid) { child_value_valid = true; child_value = child_value_tmp; } else { if (child_value_tmp != child_value) return false; // self-contradictory query atom it is } } } if (child_value_valid) { value_out = child_value; return true; } return false; } case OP_AND: { int child_value = -1; for (i = 0; i < children.size(); i++) { int child_value_tmp; if (children[i]->sureValueInv(what_type, child_value_tmp)) { if (i == 0) child_value = child_value_tmp; else if (child_value_tmp != child_value) return false; } else return false; } value_out = child_value; return true; } case OP_NOT: return children[0]->sureValue(what_type, value_out); case OP_NONE: throw QueryMolecule::Error("sureValueInv(OP_NONE) not implemented"); default: return false; } } bool QueryMolecule::Node::possibleValue (int what_type, int what_value) { int i; switch (type) { case OP_AND: { for (i = 0; i < children.size(); i++) if (!children[i]->possibleValue(what_type, what_value)) return false; return true; } case OP_OR: { for (i = 0; i < children.size(); i++) if (children[i]->possibleValue(what_type, what_value)) return true; return false; } case OP_NOT: return children[0]->possibleValueInv(what_type, what_value); case OP_NONE: return true; default: return _possibleValue(what_type, what_value); } } bool QueryMolecule::Node::possibleValueInv (int what_type, int what_value) { int i; switch (type) { case OP_AND: { for (i = 0; i < children.size(); i++) if (children[i]->possibleValueInv(what_type, what_value)) return true; return false; } case OP_OR: { for (i = 0; i < children.size(); i++) if (!children[i]->possibleValueInv(what_type, what_value)) return false; return true; } case OP_NOT: return children[0]->possibleValue(what_type, what_value); case OP_NONE: throw QueryMolecule::Error("possibleValueInv(OP_NONE) not implemented"); default: { int val; if (!_sureValue(what_type, val)) return true; return val != what_value; } } } bool QueryMolecule::Node::possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2) { int i; switch (type) { case OP_AND: { for (i = 0; i < children.size(); i++) if (!children[i]->possibleValuePair(what_type1, what_value1, what_type2, what_value2)) return false; return true; } case OP_OR: { for (i = 0; i < children.size(); i++) if (children[i]->possibleValuePair(what_type1, what_value1, what_type2, what_value2)) return true; return false; } case OP_NOT: return children[0]->possibleValuePairInv(what_type1, what_value1, what_type2, what_value2); case OP_NONE: return true; default: return _possibleValuePair(what_type1, what_value1, what_type2, what_value2); } } bool QueryMolecule::Node::possibleValuePairInv (int what_type1, int what_value1, int what_type2, int what_value2) { int i; switch (type) { case OP_AND: { for (i = 0; i < children.size(); i++) if (children[i]->possibleValuePairInv(what_type1, what_value1, what_type2, what_value2)) return true; return false; } case OP_OR: { for (i = 0; i < children.size(); i++) if (!children[i]->possibleValuePairInv(what_type1, what_value1, what_type2, what_value2)) return false; return true; } case OP_NOT: return children[0]->possibleValuePair(what_type1, what_value1, what_type2, what_value2); case OP_NONE: throw QueryMolecule::Error("possibleValuePairInv(OP_NONE) not implemented"); default: { int val1, val2; bool sure1, sure2; if ((sure1 = _sureValue(what_type1, val1)) && !hasConstraint(what_type2) && val1 == what_value1) return false; if ((sure2 = _sureValue(what_type2, val2)) && !hasConstraint(what_type1) && val2 == what_value2) return false; if (sure1 && sure2 && val1 == what_value1 && val2 == what_value2) return false; return true; } } } bool QueryMolecule::Node::sureValueBelongs (int what_type, const int *arr, int count) { int i; switch (type) { case OP_AND: { // Of course one can fool this around; // see comment in sureValue() for (i = 0; i < children.size(); i++) if (children[i]->sureValueBelongs(what_type, arr, count)) return true; return false; } case OP_OR: { for (i = 0; i < children.size(); i++) if (!children[i]->sureValueBelongs(what_type, arr, count)) return false; return true; } case OP_NOT: return children[0]->sureValueBelongsInv(what_type, arr, count); case OP_NONE: return false; default: return _sureValueBelongs(what_type, arr, count); } } QueryMolecule::Atom* QueryMolecule::Atom::sureConstraint (int what_type) { int count = 0; Atom *found = (Atom*)_findSureConstraint(what_type, count); if (count == 1) return found; return NULL; } QueryMolecule::Node* QueryMolecule::Node::_findSureConstraint (int what_type, int &count) { switch (type) { case OP_AND: case OP_OR: { Node *subnode_found = NULL; for (int i = 0; i < children.size(); i++) { Node *subnode = children[i]->_findSureConstraint(what_type, count); if (subnode != NULL) subnode_found = subnode; } return subnode_found; } case OP_NOT: { Node *subnode = children[0]->_findSureConstraint(what_type, count); return NULL; // Do not return anything in this case but increase count if found } case OP_NONE: return NULL; default: if (type == what_type) { count++; return this; } return NULL; } } bool QueryMolecule::Node::sureValueBelongsInv (int what_type, const int *arr, int count) { int i; switch (type) { case OP_OR: { for (i = 0; i < children.size(); i++) if (children[i]->sureValueBelongsInv(what_type, arr, count)) return true; return false; } case OP_AND: { for (i = 0; i < children.size(); i++) { if (!children[i]->sureValueBelongsInv(what_type, arr, count)) return false; } return true; } case OP_NOT: return children[0]->sureValueBelongs(what_type, arr, count); case OP_NONE: throw QueryMolecule::Error("sureValueBelongsInv(OP_NONE) not implemented"); default: return false; } } void QueryMolecule::Node::optimize () { switch (type) { case OP_AND: case OP_OR: case OP_NOT: for (int i = 0; i < children.size(); i++) children[i]->optimize(); break; case OP_NONE: return; } _optimize(); } bool QueryMolecule::Atom::_sureValue (int what_type, int& value_out) { if (type == what_type && value_max == value_min) { value_out = value_min; return true; } // common case for fragment 'atoms' in SMARTS queries if (type == ATOM_FRAGMENT && fragment->vertexCount() > 0) { if (fragment->getAtom(fragment->vertexBegin()).sureValue(what_type, value_out)) return true; } return false; } bool QueryMolecule::Atom::_sureValueBelongs (int what_type, const int *arr, int count) { int i; if (type == what_type) { for (i = 0; i < count; i++) if (arr[i] < value_min || arr[i] > value_max) return false; return true; } return false; } bool QueryMolecule::Atom::_possibleValue (int what_type, int what_value) { if (type == what_type) return what_value >= value_min && what_value <= value_max; if (type == ATOM_FRAGMENT && fragment->vertexCount() > 0) { if (!fragment->getAtom(fragment->vertexBegin()). possibleValue(what_type, what_value)) return false; } return true; } bool QueryMolecule::Atom::_possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2) { if (type == what_type1) return what_value1 >= value_min && what_value1 <= value_max; if (type == what_type2) return what_value2 >= value_min && what_value2 <= value_max; if (type == ATOM_FRAGMENT && fragment->vertexCount() > 0) { if (!fragment->getAtom(fragment->vertexBegin()). possibleValuePair(what_type1, what_value1, what_type2, what_value2)) return false; } return true; } void QueryMolecule::Atom::_optimize () { // Check if fragment has one atom if (type == ATOM_FRAGMENT && fragment->vertexCount() == 1) { AutoPtr<QueryMolecule> saved_fragment(fragment.release()); copy(saved_fragment->getAtom(saved_fragment->vertexBegin())); } } bool QueryMolecule::Bond::_sureValue (int what_type, int& value_out) { if (type == what_type) { value_out = value; return true; } return false; } bool QueryMolecule::Bond::_sureValueBelongs (int what_type, const int *arr, int count) { throw QueryMolecule::Error("not implemented"); } bool QueryMolecule::Bond::_possibleValue (int what_type, int what_value) { if (type == what_type) return what_value == value; return true; } bool QueryMolecule::Bond::_possibleValuePair (int what_type1, int what_value1, int what_type2, int what_value2) { if (type == what_type1) return what_value1 == value; if (type == what_type2) return what_value2 == value; return false; } bool QueryMolecule::Atom::valueWithinRange (int value) { return value >= value_min && value <= value_max; } QueryMolecule::Atom * QueryMolecule::Atom::child (int idx) { return (Atom *)children[idx]; } QueryMolecule::Bond * QueryMolecule::Bond::child (int idx) { return (Bond *)children[idx]; } QueryMolecule::Node * QueryMolecule::Atom::_neu () { return new Atom(); } QueryMolecule::Node * QueryMolecule::Bond::_neu () { return new Bond(); } int QueryMolecule::addAtom (Atom *atom) { int idx = _addBaseAtom(); _atoms.expand(idx + 1); _atoms.set(idx, atom); updateEditRevision(); return idx; } QueryMolecule::Atom & QueryMolecule::getAtom (int idx) { return *_atoms[idx]; } QueryMolecule::Atom * QueryMolecule::releaseAtom (int idx) { updateEditRevision(); return _atoms.release(idx); } void QueryMolecule::resetAtom (int idx, QueryMolecule::Atom *atom) { _atoms.reset(idx); _atoms[idx] = atom; updateEditRevision(); } int QueryMolecule::addBond (int beg, int end, QueryMolecule::Bond *bond) { int idx = _addBaseBond(beg, end); _bonds.expand(idx + 1); _bonds.set(idx, bond); invalidateAtom(beg, CHANGED_CONNECTIVITY); invalidateAtom(end, CHANGED_CONNECTIVITY); aromaticity.setCanBeAromatic(idx, false); setBondStereoCare(idx, false); updateEditRevision(); return idx; } QueryMolecule::Bond & QueryMolecule::getBond (int idx) { return *_bonds[idx]; } QueryMolecule::Bond * QueryMolecule::releaseBond (int idx) { updateEditRevision(); return _bonds.release(idx); } void QueryMolecule::resetBond (int idx, QueryMolecule::Bond *bond) { _bonds.reset(idx); _bonds[idx] = bond; _min_h.clear(); updateEditRevision(); } void QueryMolecule::Atom::copy (Atom &other) { type = other.type; value_max = other.value_max; value_min = other.value_min; fragment.reset(0); if (other.fragment.get() != 0) { fragment.reset(new QueryMolecule()); fragment->clone(other.fragment.ref(), 0, 0); fragment->fragment_smarts.copy(other.fragment->fragment_smarts); } alias.copy(other.alias); children.clear(); for (int i = 0; i < other.children.size(); i++) children.add(((Atom *)other.children[i])->clone()); } QueryMolecule::Atom * QueryMolecule::Atom::clone () { AutoPtr<Atom> res(new Atom()); res->copy(*this); return res.release(); } QueryMolecule::Bond * QueryMolecule::Bond::clone () { AutoPtr<Bond> res(new Bond()); int i; res->type = type; res->value = value; for (i = 0; i < children.size(); i++) res->children.add(((Bond *)children[i])->clone()); return res.release(); } bool QueryMolecule::Node::hasConstraint (int what_type) { if (type == what_type) return true; if (type == OP_NONE) return false; if (type == OP_AND || type == OP_OR || type == OP_NOT) { for (int i = 0; i < children.size(); i++) if (children[i]->hasConstraint(what_type)) return true; } return false; } bool QueryMolecule::Node::hasNoConstraintExcept (int what_type) { return hasNoConstraintExcept(what_type, what_type); } bool QueryMolecule::Node::hasNoConstraintExcept (int what_type1, int what_type2) { if (type == OP_NONE) return true; if (type == OP_AND || type == OP_OR || type == OP_NOT) { int i; for (i = 0; i < children.size(); i++) if (!children[i]->hasNoConstraintExcept(what_type1, what_type2)) return false; return true; } return type == what_type1 || type == what_type2; } void QueryMolecule::Node::removeConstraints (int what_type) { if (type == what_type) { type = OP_NONE; return; } if (type == OP_AND || type == OP_OR || type == OP_NOT) { int i; for (i = children.size() - 1; i >= 0; i--) { children[i]->removeConstraints(what_type); if (children[i]->type == OP_NONE) children.remove(i); } if (children.size() == 0) type = OP_NONE; } } void QueryMolecule::clear () { BaseMolecule::clear(); _atoms.clear(); _bonds.clear(); spatial_constraints.clear(); fixed_atoms.clear(); _bond_stereo_care.clear(); _min_h.clear(); aromaticity.clear(); components.clear(); fragment_smarts.clear(); updateEditRevision(); } BaseMolecule * QueryMolecule::neu () { return new QueryMolecule(); } bool QueryMolecule::bondStereoCare (int idx) { if (idx >= _bond_stereo_care.size()) return false; if (_bond_stereo_care[idx] && cis_trans.getParity(idx) == 0) throw Error("bond #%d has stereo-care flag, but is not cis-trans bond", idx); return _bond_stereo_care[idx]; } void QueryMolecule::setBondStereoCare (int idx, bool stereo_care) { if (stereo_care == false && idx >= _bond_stereo_care.size()) return; _bond_stereo_care.expandFill(idx + 1, false); _bond_stereo_care[idx] = stereo_care; updateEditRevision(); } bool QueryMolecule::aromatize (const AromaticityOptions &options) { updateEditRevision(); return QueryMoleculeAromatizer::aromatizeBonds(*this, options); } bool QueryMolecule::dearomatize (const AromaticityOptions &options) { throw Error("Dearomatization not implemented"); } int QueryMolecule::getAtomMaxH (int idx) { int total; if (_atoms[idx]->sureValue(ATOM_TOTAL_H, total)) return total; int number = getAtomNumber(idx); if (number == -1) return -1; int conn = _calcAtomConnectivity(idx); if (conn == -1) return -1; int explicit_val = getExplicitValence(idx); int max_h = 0; int charge, radical; for (charge = -5; charge <= 8; charge++) { if (!possibleAtomCharge(idx, charge)) continue; for (radical = 0; radical <= RADICAL_DOUBLET; radical++) { if (!possibleAtomRadical(idx, radical)) continue; if (explicit_val != -1) { int h = explicit_val - Element::calcValenceMinusHyd(number, charge, radical, conn); if (h > max_h) max_h = h; } else { int h, val; if (Element::calcValence(number, charge, radical, conn, val, h, false)) { if (h > max_h) max_h = h; } } } } const Vertex &vertex = getVertex(idx); int i; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (possibleAtomNumber(vertex.neiVertex(i), ELEM_H)) max_h++; } return max_h; } int QueryMolecule::getAtomMinH (int idx) { if (_min_h.size() > idx && _min_h[idx] >= 0) return _min_h[idx]; int i, min_h = _getAtomMinH(_atoms[idx]); if (min_h >= 0) { _min_h.expandFill(idx + 1, -1); _min_h[idx] = min_h; return min_h; } const Vertex &vertex = getVertex(idx); min_h = 0; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { if (getAtomNumber(vertex.neiVertex(i)) == ELEM_H) min_h++; } _min_h.expandFill(idx + 1, -1); _min_h[idx] = min_h; return min_h; } int QueryMolecule::getAtomMaxExteralConnectivity (int idx) { int number = getAtomNumber(idx); if (number == -1) return -1; int min_local_h = _getAtomMinH(_atoms[idx]); if (min_local_h == -1) min_local_h = 0; int min_conn = _calcAtomConnectivity(idx); if (min_conn == -1) min_conn = 0; int max_conn = 0; for (int charge = -5; charge <= 8; charge++) { if (!possibleAtomCharge(idx, charge)) continue; for (int radical = 0; radical <= RADICAL_DOUBLET; radical++) { if (!possibleAtomRadical(idx, radical)) continue; int cur_max_conn = Element::getMaximumConnectivity(number, charge, radical, true); if (max_conn < cur_max_conn) max_conn = cur_max_conn; } } int ext_conn = max_conn - min_conn - min_local_h; if (ext_conn < 0) return 0; return ext_conn; } int QueryMolecule::_getAtomMinH (QueryMolecule::Atom *atom) { if (atom->type == ATOM_TOTAL_H) return atom->value_min; if (atom->type == OP_AND) { int i; for (i = 0; i < atom->children.size(); i++) { int h = _getAtomMinH((Atom *)atom->children[i]); if (h >= 0) return h; } } return -1; } int QueryMolecule::getAtomTotalH (int idx) { int value; if (_atoms[idx]->sureValue(ATOM_TOTAL_H, value)) return value; int minh = getAtomMinH(idx); int maxh = getAtomMaxH(idx); if (minh == maxh) return minh; return -1; } int QueryMolecule::_calcAtomConnectivity (int idx) { const Vertex &vertex = getVertex(idx); int i, conn = 0; for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i)) { int order = getBondOrder(vertex.neiEdge(i)); if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE) conn += order; else conn += 1; } return conn; } bool QueryMolecule::isRSite (int atom_idx) { int bits; return _atoms[atom_idx]->sureValue(ATOM_RSITE, bits); } dword QueryMolecule::getRSiteBits (int atom_idx) { int bits; if (!_atoms[atom_idx]->sureValue(ATOM_RSITE, bits)) throw Error("getRSiteBits(): atom #%d is not an r-site", atom_idx); return (dword)bits; } void QueryMolecule::allowRGroupOnRSite (int atom_idx, int rg_idx) { if (rg_idx < 1 || rg_idx > 32) throw Error("allowRGroupOnRSite(): rgroup number %d is invalid", rg_idx); rg_idx--; // This is dirty hack; however, it is legal here, as rgroups // can not be present in deep query trees due to Molfile // format limitations. if (_atoms[atom_idx]->type == ATOM_RSITE) { _atoms[atom_idx]->value_max |= (1 << rg_idx); _atoms[atom_idx]->value_min |= (1 << rg_idx); return; } if (_atoms[atom_idx]->type == OP_AND) { int i; for (i = 0; i < _atoms[atom_idx]->children.size(); i++) if (_atoms[atom_idx]->children[i]->type == ATOM_RSITE) { ((Atom *)_atoms[atom_idx]->children[i])->value_max |= (1 << rg_idx); ((Atom *)_atoms[atom_idx]->children[i])->value_min |= (1 << rg_idx); } } throw Error("allowRGroupOnRSite(): atom #%d does not seem to be an r-site", atom_idx); } bool QueryMolecule::isKnownAttr (QueryMolecule::Atom& qa) { return (qa.type == QueryMolecule::ATOM_CHARGE || qa.type == QueryMolecule::ATOM_ISOTOPE || qa.type == QueryMolecule::ATOM_RADICAL || qa.type == QueryMolecule::ATOM_VALENCE || qa.type == QueryMolecule::ATOM_TOTAL_H || qa.type == QueryMolecule::ATOM_SUBSTITUENTS || qa.type == QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN || qa.type == QueryMolecule::ATOM_RING_BONDS || qa.type == QueryMolecule::ATOM_RING_BONDS_AS_DRAWN || qa.type == QueryMolecule::ATOM_UNSATURATION) && qa.value_max == qa.value_min; } bool QueryMolecule::isNotAtom (QueryMolecule::Atom& qa, int elem) { if (qa.type == QueryMolecule::OP_NOT) { QueryMolecule::Atom& c = *qa.child(0); if (c.type == QueryMolecule::ATOM_NUMBER && c.value_min == elem && c.value_max == elem) return true; } return false; } bool QueryMolecule::collectAtomList (QueryMolecule::Atom& qa, Array<int>& list, bool& notList) { list.clear(); if (qa.type == QueryMolecule::OP_OR || qa.type == QueryMolecule::OP_NOT) { notList = (qa.type == QueryMolecule::OP_NOT); QueryMolecule::Atom* qap = &qa; if (notList) { qap = qa.child(0); if (qap->type != QueryMolecule::OP_OR) return false; } for (int i = 0; i < qap->children.size(); ++i) { QueryMolecule::Atom& qc = *qap->child(i); if (qc.type != QueryMolecule::ATOM_NUMBER || qc.value_min != qc.value_max) return false; list.push(qc.value_min); } } else if (qa.type == QueryMolecule::OP_AND) { notList = true; for (int i = 0; i < qa.children.size(); ++i) { QueryMolecule::Atom& qc = *qa.child(i); if (qc.type != QueryMolecule::OP_NOT) return false; QueryMolecule::Atom& qd = *qc.child(0); if (qd.type != QueryMolecule::ATOM_NUMBER || qd.value_min != qd.value_max) return false; list.push(qd.value_min); } } return true; } QueryMolecule::Atom* QueryMolecule::stripKnownAttrs (QueryMolecule::Atom& qa) { QueryMolecule::Atom* qd = NULL; if (qa.type == QueryMolecule::OP_AND) { for (int i = 0; i < qa.children.size(); ++i) { QueryMolecule::Atom* qc = qa.child(i); if (!isKnownAttr(*qc)) { if (qd != NULL) return NULL; qd = qc; } } } return qd == NULL ? &qa : qd; } int QueryMolecule::parseQueryAtom (QueryMolecule& qm, int aid, Array<int>& list) { QueryMolecule::Atom& qa = qm.getAtom(aid); QueryMolecule::Atom* qc = stripKnownAttrs(qa); if (qc != NULL && isNotAtom(*qc, ELEM_H)) return QUERY_ATOM_A; bool notList = false; if (collectAtomList(qa, list, notList) || (qa.type == QueryMolecule::OP_NOT && collectAtomList(*qa.child(0), list, notList) && !notList)) { // !notList is to check there's no double negation if (list.size() == 0) return -1; notList = notList || qa.type == QueryMolecule::OP_NOT; if (!notList && list.size() == 5 && list[0] == ELEM_F && list[1] == ELEM_Cl && list[2] == ELEM_Br && list[3] == ELEM_I && list[4] == ELEM_At) return QUERY_ATOM_X; if (notList && list.size() == 2 && ((list[0] == ELEM_C && list[1] == ELEM_H) || (list[0] == ELEM_H && list[1] == ELEM_C))) return QUERY_ATOM_Q; return notList ? QUERY_ATOM_NOTLIST : QUERY_ATOM_LIST; } return -1; } bool QueryMolecule::queryAtomIsRegular (QueryMolecule& qm, int aid) { QueryMolecule::Atom& qa = qm.getAtom(aid); QueryMolecule::Atom* qc = stripKnownAttrs(qa); return qc && qc->type == QueryMolecule::ATOM_NUMBER; } QueryMolecule::Bond* QueryMolecule::getBondOrderTerm (QueryMolecule::Bond& qb, bool& complex) { if (qb.type == QueryMolecule::OP_AND) { QueryMolecule::Bond* r = NULL; for (int i = 0; i < qb.children.size(); ++i) { QueryMolecule::Bond* c = getBondOrderTerm(*qb.child(i), complex); if (complex) return NULL; if (c != NULL) { if (r != NULL) { complex = true; return NULL; } r = c; } } return r; } if (qb.type == QueryMolecule::BOND_TOPOLOGY) return NULL; return &qb; } bool QueryMolecule::isOrBond (QueryMolecule::Bond& qb, int type1, int type2) { if ((qb.type == OP_AND || qb.type == OP_OR) && qb.children.size() == 1) return isOrBond(*qb.child(0), type1, type2); if (qb.type != QueryMolecule::OP_OR || qb.children.size() != 2) return false; QueryMolecule::Bond& b1 = *qb.child(0); QueryMolecule::Bond& b2 = *qb.child(1); if (b1.type != QueryMolecule::BOND_ORDER || b2.type != QueryMolecule::BOND_ORDER) return false; if ((b1.value == type1 && b2.value == type2) || (b1.value == type2 && b2.value == type1)) return true; return false; } bool QueryMolecule::isSingleOrDouble (QueryMolecule::Bond& qb) { if ((qb.type == OP_AND || qb.type == OP_OR) && qb.children.size() == 1) return isSingleOrDouble(*qb.child(0)); if (qb.type != QueryMolecule::OP_AND || qb.children.size() != 2) return false; QueryMolecule::Bond& b1 = *qb.child(0); QueryMolecule::Bond& b2 = *qb.child(1); if (!isOrBond(b2, BOND_SINGLE, BOND_DOUBLE)) return false; if (b1.type != QueryMolecule::OP_NOT) return false; QueryMolecule::Bond& b11 = *b1.child(0); if (b11.type != QueryMolecule::BOND_ORDER || b11.value != BOND_AROMATIC) return false; return true; } int QueryMolecule::getQueryBondType (QueryMolecule::Bond& qb) { if (!qb.hasConstraint(QueryMolecule::BOND_ORDER)) return QUERY_BOND_ANY; QueryMolecule::Bond* qb2 = &qb; AutoPtr<QueryMolecule::Bond> qb_modified; int topology; if (qb.sureValue(QueryMolecule::BOND_TOPOLOGY, topology)) { qb_modified.reset(qb.clone()); qb_modified->removeConstraints(QueryMolecule::BOND_TOPOLOGY); qb2 = qb_modified.get(); } if (isSingleOrDouble(*qb2) || isOrBond(*qb2, BOND_SINGLE, BOND_DOUBLE)) return QUERY_BOND_SINGLE_OR_DOUBLE; if (isOrBond(*qb2, BOND_SINGLE, BOND_AROMATIC)) return QUERY_BOND_SINGLE_OR_AROMATIC; if (isOrBond(*qb2, BOND_DOUBLE, BOND_AROMATIC)) return QUERY_BOND_DOUBLE_OR_AROMATIC; return -1; } void QueryMolecule::invalidateAtom (int index, int mask) { BaseMolecule::invalidateAtom(index, mask); if (_min_h.size() > index) _min_h[index] = -1; } void QueryMolecule::optimize () { for (int i = vertexBegin(); i != vertexEnd(); i = vertexNext(i)) getAtom(i).optimize(); updateEditRevision(); } bool QueryMolecule::standardize (const StandardizeOptions &options) { updateEditRevision(); return MoleculeStandardizer::standardize(*this, options); } �����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/rdf_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000015530�12710376503�0021406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/rdf_loader.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "gzip/gzip_scanner.h" using namespace indigo; IMPL_ERROR(RdfLoader, "RDF loader"); CP_DEF(RdfLoader); RdfLoader::RdfLoader(Scanner &scanner) : CP_INIT, TL_CP_GET(data), TL_CP_GET(properties), TL_CP_GET(_innerBuffer), _scanner(0), _isMolecule(false), TL_CP_GET(_offsets) { data.clear(); properties.clear(); _innerBuffer.clear(); /* * Detect if input is gzipped */ byte id[2]; int pos = scanner.tell(); scanner.readCharsFix(2, (char *) id); scanner.seek(pos, SEEK_SET); if (id[0] == 0x1f && id[1] == 0x8b) { _scanner = new GZipScanner(scanner); _ownScanner = true; } else { _scanner = &scanner; _ownScanner = false; } _current_number = 0; _max_offset = 0; _offsets.clear(); } RdfLoader::~RdfLoader() { if (_ownScanner) delete _scanner; } bool RdfLoader::isEOF() { return _getScanner().isEOF(); } int RdfLoader::count () { int offset = _scanner->tell(); int cn = _current_number; if (offset != _max_offset) { _scanner->seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); } while (!isEOF()) readNext(); int res = _current_number; if (res != cn) { _scanner->seek(offset, SEEK_SET); _current_number = cn; } return res; } void RdfLoader::readNext() { ArrayOutput output(data); data.clear(); properties.clear(); if (_scanner->isEOF()) throw Error("end of stream"); _offsets.expand(_current_number + 1); _offsets[_current_number++] = _scanner->tell(); /* * Read data */ do { /* * Note: no correct format checking at the moment * Header is simply skipped */ if (_startsWith("$RDFILE") || _startsWith("$DATM")) continue; /* * Molecule identefier */ if (_startsWith("$MFMT")) { if(data.size()) break; _isMolecule = true; _readIdentifiers(false); continue; } /* * Reaction identefier */ if (_startsWith("$RFMT")) { if(data.size()) break; _isMolecule = false; _readIdentifiers(false); continue; } /* * Corresponding data */ if (_startsWith("$DTYPE")) break; /* * Write buffer */ if (_innerBuffer.size()) { if (_readIdentifiers(true)) continue; output.printf("%s\n", _innerBuffer.ptr()); } if(data.size() > MAX_DATA_SIZE) throw Error("data size exceeded the acceptable size %d bytes, Please check for correct file format", MAX_DATA_SIZE); } while(_readLine(_getScanner(), _innerBuffer)); /* * Current value for property reading */ Array<char> *current_datum = 0; /* * Read properties */ do { if (_startsWith("$MFMT") || _startsWith("$RFMT")) break; if (_startsWith("$DTYPE")) { QS_DEF(Array<char>, property_name); BufferScanner scanner(_innerBuffer.ptr()); /* * Skip "$DTYPE" string and all spaces */ scanner.skip(6); scanner.skipSpace(); /* * If no key presented then skip value reading */ if(!_readLine(scanner, property_name)) { current_datum = 0; } else { properties.insert(property_name.ptr()); /* * Define current value buffer */ current_datum = &properties.valueBuf(property_name.ptr()); } continue; } if (_startsWith("$DATUM")) { if(!current_datum) continue; BufferScanner scanner(_innerBuffer.ptr()); /* * Skip "$DATUM" string and all spaces */ scanner.skip(6); scanner.skipSpace(); _readLine(scanner, *current_datum); continue; } /* * Read value buffer */ if(_innerBuffer.size() && current_datum && current_datum->size()) { current_datum->appendString("\n", true); current_datum->appendString(_innerBuffer.ptr(), true); } } while(_readLine(_getScanner(), _innerBuffer)); if (_scanner->tell() > _max_offset) _max_offset = _scanner->tell(); } Scanner& RdfLoader::_getScanner() const { return *_scanner; } bool RdfLoader::_readIdentifiers(bool from_begin) { bool result = false; BufferScanner scanner(_innerBuffer.ptr()); QS_DEF(Array<char>, word); scanner.skipSpace(); while (!scanner.isEOF()) { word.clear(); scanner.readWord(word, 0); word.push(0); if(strcmp(word.ptr(), "$MIREG") == 0 || strcmp(word.ptr(), "$RIREG") == 0) { /* * Insert new property key */ Array<char>& val = properties.insert("internal-regno"); scanner.skipSpace(); /* * Insert new property value */ scanner.readWord(val, 0); val.push(0); result = true; } else if (strcmp(word.ptr(), "$MEREG") == 0 || strcmp(word.ptr(), "$REREG") == 0) { /* * Insert new property key */ Array<char>& val = properties.insert("external-regno"); scanner.skipSpace(); /* * Insert new property value */ scanner.readWord(val, 0); val.push(0); result = true; } else if(from_begin) { return false; } from_begin = false; scanner.skipSpace(); } return result; } bool RdfLoader::_readLine(Scanner& scanner, Array<char>& buffer) { buffer.clear(); if(scanner.isEOF()) return false; scanner.readLine(buffer, true); return true; } int RdfLoader::tell () { return _scanner->tell(); } int RdfLoader::currentNumber () { return _current_number; } void RdfLoader::readAt (int index) { if (index < _offsets.size()) { _scanner->seek(_offsets[index], SEEK_SET); _current_number = index; readNext(); } else { _scanner->seek(_max_offset, SEEK_SET); _current_number = _offsets.size(); do { readNext(); } while (index + 1 != _offsets.size()); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/sdf_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000012611�12710376503�0021404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/sdf_loader.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "gzip/gzip_scanner.h" using namespace indigo; IMPL_ERROR(SdfLoader, "SDF loader"); CP_DEF(SdfLoader); SdfLoader::SdfLoader (Scanner &scanner) : CP_INIT, TL_CP_GET(data), TL_CP_GET(properties), TL_CP_GET(_offsets), TL_CP_GET(_preread) { data.clear(); properties.clear(); // detect if input is gzipped byte id[2]; int pos = scanner.tell(); scanner.readCharsFix(2, (char *)id); scanner.seek(pos, SEEK_SET); if (id[0] == 0x1f && id[1] == 0x8b) { _scanner = new GZipScanner(scanner); _own_scanner = true; } else { _scanner = &scanner; _own_scanner = false; } _current_number = 0; _max_offset = 0; _offsets.clear(); _preread.clear(); } SdfLoader::~SdfLoader() { if (_own_scanner) delete _scanner; } int SdfLoader::tell () { return _scanner->tell(); } int SdfLoader::currentNumber () { return _current_number; } int SdfLoader::count () { int offset = _scanner->tell(); int cn = _current_number; if (offset != _max_offset) { _scanner->seek(_max_offset, SEEK_SET); _preread.clear(); _current_number = _offsets.size(); } while (!isEOF()) readNext(); int res = _current_number; if (res != cn) { _scanner->seek(offset, SEEK_SET); _preread.clear(); _current_number = cn; } return res; } bool SdfLoader::isEOF() { // read space characters while (!_scanner->isEOF()) { if (isspace(_scanner->lookNext())) _preread.push(_scanner->readChar()); else return false; } // We are at the end of file now, having only space characters read. return true; } void SdfLoader::readNext () { ArrayOutput output(data); output.writeArray(_preread); int n_preread = _preread.size(); _preread.clear(); QS_DEF(Array<char>, str); if (_scanner->isEOF()) throw Error("end of stream"); _offsets.expand(_current_number + 1); _offsets[_current_number++] = _scanner->tell() - n_preread; properties.clear(); bool pending_emptyline = false; int last_offset = -1; while (!_scanner->isEOF()) { last_offset = _scanner->tell(); _scanner->readLine(str, true); if (str.size() > 0 && str[0] == '>') break; if (str.size() > 3 && strncmp(str.ptr(), "$$$$", 4) == 0) break; if (pending_emptyline) output.printf("\n"); if (str.size() <= 1) pending_emptyline = true; else pending_emptyline = false; if (!pending_emptyline) output.writeStringCR(str.ptr()); if(data.size() > MAX_DATA_SIZE) throw Error("data size exceeded the acceptable size %d bytes, Please check for correct file format", MAX_DATA_SIZE); } int properties_offset = last_offset; while (1) { if (strncmp(str.ptr(), "$$$$", 4) == 0) break; output.writeStringCR(str.ptr()); BufferScanner ws(str.ptr()); while (!ws.isEOF()) if (ws.readChar() == '<') break; QS_DEF(Array<char>, word); bool have_word = false; word.clear(); while (!ws.isEOF()) { char c = ws.readChar(); if (c == '>') { have_word = true; break; } word.push(c); } if (have_word && word.size() > 0) { word.push(0); _scanner->readLine(str, true); auto& propBuf = properties.insert(word.ptr()); // auto& propBuf = properties.valueBuf(word.ptr()); // int idx = properties.findOrInsert(word.ptr()); propBuf.copy(str); output.writeStringCR(str.ptr()); if (str.size() > 1) { do { if (_scanner->isEOF()) break; _scanner->readLine(str, true); output.writeStringCR(str.ptr()); if (str.size() > 1) { propBuf.pop(); // Remove string end marker (0) propBuf.push('\n'); propBuf.appendString(str.ptr(), true); } } while (str.size() > 1); } } if (_scanner->isEOF()) break; _scanner->readLine(str, true); } if (_scanner->tell() > _max_offset) _max_offset = _scanner->tell(); } void SdfLoader::readAt (int index) { if (index < _offsets.size()) { _scanner->seek(_offsets[index], SEEK_SET); _preread.clear(); _current_number = index; readNext(); } else { _scanner->seek(_max_offset, SEEK_SET); _preread.clear(); _current_number = _offsets.size(); do { readNext(); } while (index + 1 != _offsets.size()); } } �����������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/smiles_loader.cpp��������������������������������������������������0000664�0000000�0000000�00000226454�12710376503�0022140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <ctype.h> #include "molecule/smiles_loader.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_stereocenters.h" #include "molecule/elements.h" #include "graph/cycle_basis.h" #include "base_cpp/auto_ptr.h" using namespace indigo; IMPL_ERROR(SmilesLoader, "SMILES loader"); CP_DEF(SmilesLoader); SmilesLoader::SmilesLoader (Scanner &scanner) : _scanner(scanner), CP_INIT, TL_CP_GET(_atom_stack), TL_CP_GET(_cycles), TL_CP_GET(_pending_bonds_pool), TL_CP_GET(_neipool), TL_CP_GET(_atoms), TL_CP_GET(_bonds), TL_CP_GET(_polymer_repetitions) { reaction_atom_mapping = 0; ignorable_aam = 0; inside_rsmiles = false; ignore_closing_bond_direction_mismatch = false; ignore_cistrans_errors = false; _mol = 0; _qmol = 0; _bmol = 0; smarts_mode = false; _balance = 0; _current_compno = 0; _inside_smarts_component = false; } SmilesLoader::~SmilesLoader () { // clear pool-dependent data in this thread to avoid data races _atoms.clear(); } void SmilesLoader::loadMolecule (Molecule &mol) { mol.clear(); _bmol = &mol; _mol = &mol; _qmol = 0; _loadMolecule(); } void SmilesLoader::loadQueryMolecule (QueryMolecule &mol) { mol.clear(); _bmol = &mol; _mol = 0; _qmol = &mol; _loadMolecule(); } void SmilesLoader::_calcStereocenters () { int i, j, tmp; for (i = 0; i < _atoms.size(); i++) { if (_atoms[i].chirality == 0) continue; if (_bmol->getVertex(i).degree() == 2) // allene stereo center { int subst[4]; int subst2[4]; int left, right; bool pure_h[4]; if (!MoleculeAlleneStereo::possibleCenter(*_bmol, i, left, right, subst, pure_h)) { if (!stereochemistry_options.ignore_errors) throw Error("chirality on atom %d makes no sense", i); continue; } int tmp, parity = 3 - _atoms[i].chirality; for (j = 0; j < 4; j++) if (subst[j] == -1) subst2[j] = -1; else subst2[j] = subst[j]; // Daylight doc says: Hydrogens attached to substituted allene-like atoms // are taken to be immediately following that atom if (subst2[1] == -1) subst2[1] = left; if (subst2[3] == -1) subst2[3] = right; if (subst2[1] < subst2[0]) { __swap(subst2[1], subst2[0], tmp); parity = 3 - parity; } if (subst2[3] < subst2[2]) { __swap(subst2[3], subst2[2], tmp); parity = 3 - parity; } // move hydrogens from [0] and [2] to [1] and [3] respectively if (pure_h[0]) { if (subst[1] == -1) throw Error("unexpected: subst[1] = -1"); __swap(subst[0], subst[1], tmp); parity = 3 - parity; } if (pure_h[2]) { if (subst[3] == -1) throw Error("unexpected: subst[3] = -1"); __swap(subst[2], subst[3], tmp); parity = 3 - parity; } _bmol->allene_stereo.add(i, left, right, subst, parity); } else // ordinary tetrahedral stereo center { MoleculeStereocenters &stereocenters = _bmol->stereocenters; int pyramid[4] = {-1, -1, -1, -1}; int counter = 0; int h_index = -1; if (_atoms[i].parent != -1) pyramid[counter++] = _atoms[i].parent; if (_atoms[i].neighbors.size() == 3) { h_index = counter; pyramid[counter++] = -1; } for (j = _atoms[i].neighbors.begin(); j != _atoms[i].neighbors.end(); j = _atoms[i].neighbors.next(j)) { int nei = _atoms[i].neighbors.at(j); if (counter >= 4) { if (!stereochemistry_options.ignore_errors) throw Error("too many bonds for chiral atom %d", i); break; } if (nei != _atoms[i].parent) pyramid[counter++] = nei; } if (j != _atoms[i].neighbors.end()) continue; if (counter < 3) { if (!stereochemistry_options.ignore_errors) throw Error("only %d bonds for chiral atom %d", counter, i); continue; } if (counter == 4) { j = pyramid[0]; pyramid[0] = pyramid[1]; pyramid[1] = pyramid[2]; pyramid[2] = pyramid[3]; pyramid[3] = j; if (h_index == 0) h_index = 3; else if (h_index > 0) h_index--; } if (h_index >= 0) { if (counter != 4) { if (!stereochemistry_options.ignore_errors) throw Error("implicit hydrogen not allowed with %d neighbor atoms", counter - 1); continue; } bool parity = true; for (j = h_index; j < 3; j++) { __swap(pyramid[j], pyramid[j + 1], tmp); parity = !parity; } if (!parity) __swap(pyramid[0], pyramid[1], tmp); } if (_atoms[i].chirality == 2) __swap(pyramid[0], pyramid[1], j); if (!stereocenters.isPossibleStereocenter(i)) { if (!stereochemistry_options.ignore_errors) throw Error("chirality not possible on atom #%d", i); continue; } stereocenters.add(i, MoleculeStereocenters::ATOM_ABS, 0, pyramid); } } } void SmilesLoader::_calcCisTrans () { QS_DEF(Array<int>, dirs); int i; dirs.clear(); for (i = 0; i < _bonds.size(); i++) dirs.push(_bonds[i].dir); // there could be bonds added to stereocenters for (; i < _bmol->edgeEnd(); i++) dirs.push(0); _bmol->cis_trans.buildFromSmiles(dirs.ptr()); if (_qmol != 0) { for (i = 0; i < _bonds.size(); i++) if (_bmol->cis_trans.getParity(i) != 0) _qmol->setBondStereoCare(i, true); } } void SmilesLoader::_readOtherStuff () { MoleculeStereocenters &stereocenters = _bmol->stereocenters; MoleculeCisTrans &cis_trans = _bmol->cis_trans; QS_DEF(Array<int>, to_remove); to_remove.clear(); while (1) { char c = _scanner.readChar(); if (c == '|') break; if (c == 'w') // 'ANY' stereocenters { bool skip = true; // TODO: up/down designators (usually come atom coordinates) -- skipped for now if (_scanner.lookNext() == 'U') _scanner.skip(1); else if (_scanner.lookNext() == 'D') _scanner.skip(1); else skip = false; if (_scanner.readChar() != ':') throw Error("colon expected after 'w'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (!skip) { // This either bond can mark stereocenter or cis-trans double bond // For example CC=CN |w:1.0| const Vertex &v = _bmol->getVertex(idx); bool found = false; for (int nei : v.neighbors()) { int edge_idx = v.neiEdge(nei); if (_bmol->getBondOrder(edge_idx) == BOND_DOUBLE) { cis_trans.ignore(edge_idx); found = true; } } if (!found) { if (!stereocenters.isPossibleStereocenter(idx)) { if (!stereochemistry_options.ignore_errors) throw Error("chirality not possible on atom #%d", idx); } else { // Check if the stereocenter has already been marked as any // For example [H]C1(O)c2ccnn2[C@@H](O)c2ccnn12 |r,w:1.0,1.1| if (stereocenters.getType(idx) != MoleculeStereocenters::ATOM_ANY) stereocenters.add(idx, MoleculeStereocenters::ATOM_ANY, 0, false); } } } if (_scanner.lookNext() == '.') // skip the bond index { _scanner.skip(1); _scanner.readUnsigned(); } if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'a') // 'ABS' stereocenters { if (_scanner.readChar() != ':') throw Error("colon expected after 'a'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (stereocenters.exists(idx)) stereocenters.setType(idx, MoleculeStereocenters::ATOM_ABS, 0); else if (!stereochemistry_options.ignore_errors) throw Error("atom %d is not a stereocenter", idx); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'o') // 'OR' stereocenters { int groupno = _scanner.readUnsigned(); if (_scanner.readChar() != ':') throw Error("colon expected after 'o'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (stereocenters.exists(idx)) stereocenters.setType(idx, MoleculeStereocenters::ATOM_OR, groupno); else if (!stereochemistry_options.ignore_errors) throw Error("atom %d is not a stereocenter", idx); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '&') // 'AND' stereocenters { int groupno = _scanner.readUnsigned(); if (_scanner.readChar() != ':') throw Error("colon expected after '&'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (stereocenters.exists(idx)) stereocenters.setType(idx, MoleculeStereocenters::ATOM_AND, groupno); else if (!stereochemistry_options.ignore_errors) throw Error("atom %d is not a stereocenter", idx); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '^') // radicals { int rad = _scanner.readIntFix(1); int radical; if (rad == 1) radical = RADICAL_DOUBLET; else if (rad == 3) radical = RADICAL_SINGLET; else if (rad == 4) radical = RADICAL_TRIPLET; else throw Error("unsupported radical number: %d", rad); if (_scanner.readChar() != ':') throw Error("colon expected after radical number"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (_mol != 0) _mol->setAtomRadical(idx, radical); else _qmol->resetAtom(idx, QueryMolecule::Atom::und( _qmol->releaseAtom(idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical))); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '$') // pseudoatoms { QS_DEF(Array<char>, label); for (int i = _bmol->vertexBegin(); i != _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { label.clear(); while (1) { if (_scanner.isEOF()) throw Error("end of input while reading $...$ block"); c = _scanner.readChar(); if (c == ';' || c == '$') break; label.push(c); } if (c == '$' && i != _bmol->vertexEnd() - 1) throw Error("only %d atoms found in pseudo-atoms $...$ block", i + 1); if (c == ';' && i == _bmol->vertexEnd() - 1) throw Error("extra ';' in pseudo-atoms $...$ block"); if (label.size() > 0) { label.push(0); int rnum; if (label.size() > 3 && strncmp(label.ptr(), "_R", 2) == 0 && sscanf(label.ptr() + 2, "%d", &rnum) == 1) { // ChemAxon's Extended SMILES notation for R-sites if (_qmol != 0) _qmol->resetAtom(i, new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); _bmol->allowRGroupOnRSite(i, rnum); } else if (label.size() > 4 && strncmp(label.ptr(), "_AP", 3) == 0 && sscanf(label.ptr() + 3, "%d", &rnum) == 1) { // That is ChemAxon's Extended SMILES notation for attachment // points. We mark the atom for removal and place attachment point // markers on its neighbors. int k; const Vertex &v = _bmol->getVertex(i); for (k = v.neiBegin(); k != v.neiEnd(); k = v.neiNext(k)) _bmol->addAttachmentPoint(rnum, v.neiVertex(k)); to_remove.push(i); } else { if (_mol != 0) _mol->setPseudoAtom(i, label.ptr()); else { QueryMolecule::Atom *atom = _qmol->releaseAtom(i); atom->removeConstraints(QueryMolecule::ATOM_NUMBER); _qmol->resetAtom(i, QueryMolecule::Atom::und(atom, new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, label.ptr()))); } } } } } else if (c == 'c' || c == 't') // CIS and TRANS bonds { if (_scanner.readChar() != ':') throw Error("colon expected after '%c' identifier", c); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); bool skip = false; if (ignore_cistrans_errors && !MoleculeCisTrans::isGeomStereoBond(*_bmol, _bonds[idx].index, nullptr, false)) skip = true; if (!skip) { _bmol->cis_trans.restoreSubstituents(_bonds[idx].index); const int *subst = _bmol->cis_trans.getSubstituents(_bonds[idx].index); int parity = ((c == 'c') ? MoleculeCisTrans::CIS : MoleculeCisTrans::TRANS); /* CXSmiles doc says: the double bond has the representation a1-a2=a3-a4, where a1 is the smallest atom index of the generated smiles connected to a2 a2 is the double bond smaller atom index in the generated smiles a3 is the double bond larger atom index in the generated smiles a4 is the smallest atom index of the generated smiles connected to a3 * We need to know if the calculated substituents' indices are not "smallest" * (i.e. they have other substituent with smaller index on the same side). * In that case, we invert the parity. */ if (subst[1] != -1 && subst[1] < subst[0]) parity = 3 - parity; if (subst[3] != -1 && subst[3] < subst[2]) parity = 3 - parity; _bmol->cis_trans.setParity(_bonds[idx].index, parity); } if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '(') // atom coordinates { for (int i = _bmol->vertexBegin(); i != _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { float x, y, z = 0; x = _scanner.readFloat(); if (_scanner.readChar() != ',') throw Error("expected comma after X coordinate"); y = _scanner.readFloat(); if (_scanner.lookNext() != ';' && _scanner.lookNext() != ')') { if (_scanner.readChar() != ',') throw Error("expected comma after Y coordinate"); if (_scanner.lookNext() == ';') _scanner.skip(1); else if (_scanner.lookNext() == ')') ; else z = _scanner.readFloat(); } else { _scanner.skip(1); if (_scanner.readChar() != ';') throw Error("expected ';' after coordinates"); } _bmol->setAtomXyz(i, x, y, z); } if (_scanner.readChar() != ')') throw Error("expected ')' after coordinates"); _bmol->stereocenters.markBonds(); _bmol->allene_stereo.markBonds(); } else if (c == 'h') // highlighting (Indigo's own extension) { c = _scanner.readChar(); int a = false; if (c == 'a') a = true; else if (c != 'b') throw Error("expected 'a' or 'b' after 'h', got '%c'", c); if (_scanner.readChar() != ':') throw Error("colon expected after 'h%c'", a ? 'a' : 'b'); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (a) _bmol->highlightAtom(idx); else _bmol->highlightBond(idx); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'r') { // All stereocenters are relative instead of abs MoleculeStereocenters &s = _bmol->stereocenters; for (int i = s.begin(); i != s.end(); i = s.next(i)) { int atom = s.getAtomIndex(i); if (s.getType(atom) == MoleculeStereocenters::ATOM_ABS) s.setType(atom, MoleculeStereocenters::ATOM_AND, 1); } } } if (to_remove.size() > 0) _bmol->removeAtoms(to_remove); } void SmilesLoader::loadSMARTS (QueryMolecule &mol) { mol.clear(); _bmol = &mol; _mol = 0; _qmol = &mol; smarts_mode = true; _loadMolecule(); } void SmilesLoader::_parseMolecule () { _cycles.clear(); _atom_stack.clear(); _pending_bonds_pool.clear(); bool first_atom = true; bool inside_polymer = false; while (!_scanner.isEOF()) { int next = _scanner.lookNext(); if (isspace(next)) break; _BondDesc *bond = 0; if (!first_atom) { bool added_bond = false; while (isdigit(next) || next == '%') { int number; _scanner.skip(1); if (next == '%') number = _scanner.readIntFix(2); else number = next - '0'; while (_cycles.size() <= number) _cycles.push().clear(); // closing some previously numbered atom, like the last '1' in c1ccccc1 if (_cycles[number].beg >= 0) { bond = &_bonds.push(); bond->dir = 0; bond->topology = 0; bond->beg = _atom_stack.top(); bond->end = _cycles[number].beg; bond->type = -1; // will later become single or aromatic bond bond->index = -1; _cycles[number].clear(); added_bond = true; if (_qmol != 0) bond->index = _qmol->addBond(bond->beg, bond->end, new QueryMolecule::Bond()); _atoms[bond->beg].neighbors.add(bond->end); _atoms[bond->end].closure(number, bond->beg); break; } // closing some previous pending bond, like the last '1' in C-1=CC=CC=C1' else if (_cycles[number].pending_bond >= 0) { bond = &_bonds[_cycles[number].pending_bond]; bond->end = _atom_stack.top(); added_bond = true; _atoms[bond->end].neighbors.add(bond->beg); _atoms[bond->beg].closure(number, bond->end); if (_qmol != 0) { QS_DEF(Array<char>, bond_str); AutoPtr<QueryMolecule::Bond> qbond(new QueryMolecule::Bond()); bond_str.readString(_pending_bonds_pool.at(_cycles[number].pending_bond_str), false); _readBond(bond_str, *bond, qbond); bond->index = _qmol->addBond(bond->beg, bond->end, qbond.release()); } _cycles[number].clear(); break; } // opening new cycle, like the first '1' in c1ccccc1 else { _cycles[number].beg = _atom_stack.top(); _cycles[number].pending_bond = -1; _atoms[_cycles[number].beg].pending(number); } next = _scanner.lookNext(); } if (added_bond) continue; } if (next == '.') { _scanner.skip(1); if (smarts_mode && _balance == 0) { _inside_smarts_component = false; _atom_stack.clear(); // needed to detect errors like "C.(C)(C)" } else { if (_atom_stack.size() < 1) ; // we allow misplaced dots because we are so kind else _atom_stack.pop(); } first_atom = true; continue; } if (next == '(') { _scanner.skip(1); if (smarts_mode && first_atom) { if (_balance > 0) throw Error("hierarchical component-level grouping is not allowed"); _current_compno++; _inside_smarts_component = true; } else { if (_atom_stack.size() < 1) throw Error("probably misplaced '('"); _atom_stack.push(_atom_stack.top()); } _balance++; continue; } if (next == ')') { _scanner.skip(1); if (_balance <= 0) throw Error("unexpected ')'"); _balance--; _atom_stack.pop(); continue; } if (!first_atom) { bond = &_bonds.push(); bond->beg = _atom_stack.top(); bond->end = -1; bond->type = -1; bond->dir = 0; bond->topology = 0; bond->index = -1; } AutoPtr<QueryMolecule::Bond> qbond; if (bond != 0) { QS_DEF(Array<char>, bond_str); bond_str.clear(); while (strchr("-=#:@!;,&~?/\\", next) != NULL) { bond_str.push(_scanner.readChar()); next = _scanner.lookNext(); } if (_qmol != 0) qbond.reset(new QueryMolecule::Bond()); // empty bond designator? if (bond_str.size() < 1) { // In SMARTS mode a missing bond symbol is interpreted as "single or aromatic". // (http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html) // But if both atoms cannot be aromatic, then empty bond can be just // replaced to single. // Such case is processed after } else _readBond(bond_str, *bond, qbond); // The bond "directions" are already saved in _BondDesc::dir, // so we can safely discard them. We are doing that to succeed // the later check 'pending bond vs. closing bond'. { int i; for (i = 0; i < bond_str.size(); i++) if (bond_str[i] == '/' || bond_str[i] == '\\') { // Aromatic bonds can be a part of cis-trans configuration // For example in Cn1c2ccccc2c(-c2ccccc2)n/c(=N\O)c1=O or O\N=c1/c(=O)c2ccccc2c(=O)/c/1=N\O if (_atoms[bond->beg].aromatic && bond_str.size() == 1) { // Erase bond type info bond_str[i] = '?'; bond->type = -1; // single of aromatic } else bond_str[i] = '-'; } } if (bond_str.size() > 0) { if (isdigit(next) || next == '%') { int number; _scanner.skip(1); if (next == '%') number = _scanner.readIntFix(2); else number = next - '0'; // closing some previous numbered atom, like the last '1' in C1C=CC=CC=1 if (number >= 0 && number < _cycles.size() && _cycles[number].beg >= 0) { bond->end = _cycles[number].beg; if (_qmol != 0) bond->index = _qmol->addBond(bond->beg, bond->end, qbond.release()); _atoms[bond->end].closure(number, bond->beg); _atoms[bond->beg].neighbors.add(bond->end); _cycles[number].clear(); continue; } // closing some previous pending cycle bond, like the last '1' in C=1C=CC=CC=1 else if (number >= 0 && number < _cycles.size() && _cycles[number].pending_bond >= 0) { int pending_bond_idx = _cycles[number].pending_bond; _BondDesc &pending_bond = _bonds[pending_bond_idx]; // transfer direction from closing bond to pending bond if (bond->dir > 0) { if (bond->dir == pending_bond.dir) { if (!ignore_closing_bond_direction_mismatch) throw Error("cycle %d: closing bond direction does not match pending bond direction", number); } else pending_bond.dir = 3 - bond->dir; } // apart from the direction, check that the closing bond matches the pending bond const char *str = _pending_bonds_pool.at(_cycles[number].pending_bond_str); if (bond_str.size() > 0) { if (!ignore_closing_bond_direction_mismatch) { if ((int)strlen(str) != bond_str.size() || memcmp(str, bond_str.ptr(), strlen(str)) != 0) throw Error("cycle %d: closing bond description %.*s does not match pending bond description %s", number, bond_str.size(), bond_str.ptr(), str); } } else { bond_str.readString(str, false); _readBond(bond_str, *bond, qbond); } if (_qmol != 0) pending_bond.index = _qmol->addBond(pending_bond.beg, bond->beg, qbond.release()); pending_bond.end = bond->beg; _atoms[pending_bond.end].neighbors.add(pending_bond.beg); _atoms[pending_bond.beg].closure(number, pending_bond.end); // forget the closing bond but move its index here // Bond order should correspons to atoms order // Bond order for the following two molecules should be the same // because later we add cis constraint: // CCSc1nnc2c(OC=Nc3ccccc-23)n1 |c:9| // CCSc1nnc-2c(OC=Nc3ccccc-23)n1 |c:9| // Without this shift the 9th bond in the second structure is not double _bonds.top() = pending_bond; _bonds.remove(pending_bond_idx); _cycles[number].clear(); continue; } // opening some pending cycle bond, like the first '1' in C=1C=CC=CC=1 else { while (_cycles.size() <= number) _cycles.push().clear(); _cycles[number].pending_bond = _bonds.size() - 1; _cycles[number].pending_bond_str = _pending_bonds_pool.add(bond_str); _cycles[number].beg = -1; // have it already in the bond _atoms[bond->beg].pending(number); continue; } } } } _AtomDesc &atom = _atoms.push(_neipool); AutoPtr<QueryMolecule::Atom> qatom; if (_qmol != 0) qatom.reset(new QueryMolecule::Atom()); if (bond != 0) bond->end = _atoms.size() - 1; QS_DEF(Array<char>, atom_str); atom_str.clear(); bool brackets = false; if (next == '[') { _scanner.skip(1); int cnt = 1; while (1) { if (_scanner.isEOF()) throw Error("'[' without a ']'"); char c = _scanner.readChar(); if (c == '[') cnt++; else if (c == ']') { cnt--; if (cnt == 0) break; } atom_str.push(c); } brackets = true; } else if (next == -1) throw Error("unexpected end of input"); else { _scanner.skip(1); atom_str.push(next); if (next == 'B' && _scanner.lookNext() == 'r') atom_str.push(_scanner.readChar()); else if (next == 'C' && _scanner.lookNext() == 'l') atom_str.push(_scanner.readChar()); } _readAtom(atom_str, brackets, atom, qatom); atom.brackets = brackets; if (_qmol != 0) { _qmol->addAtom(qatom.release()); if (bond != 0) bond->index = _qmol->addBond(bond->beg, bond->end, qbond.release()); } if (bond != 0) { _atoms[bond->beg].neighbors.add(bond->end); _atoms[bond->end].neighbors.add(bond->beg); _atoms[bond->end].parent = bond->beg; // when going from a polymer atom, make the new atom belong // to the same polymer if (_atoms[bond->beg].polymer_index >= 0) { // ... unless it goes from the polymer end // and is not in braces if (!_atoms[bond->beg].ends_polymer || (_atom_stack.size() >= 2 && _atom_stack.top() == _atom_stack[_atom_stack.size() - 2])) _atoms[bond->end].polymer_index = _atoms[bond->beg].polymer_index; } } if (_inside_smarts_component) { _qmol->components.expandFill(_atoms.size(), 0); _qmol->components[_atoms.size() - 1] = _current_compno; } if (!first_atom) _atom_stack.pop(); _atom_stack.push(_atoms.size() - 1); first_atom = false; while (_scanner.lookNext() == '{') _handleCurlyBrace(atom, inside_polymer); if (inside_polymer) atom.polymer_index = _polymer_repetitions.size() - 1; } if (smarts_mode) { // SMARTS mode: process only empty bonds, and replace them with single or aromatic for (int i = 0; i < _bonds.size(); i++) { int index = _bonds[i].index; QueryMolecule::Bond &qbond = _qmol->getBond(index); if (qbond.type != QueryMolecule::OP_NONE || _bonds[i].type == _ANY_BOND) continue; // A missing bond symbol is interpreted as "single or aromatic". // (http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html) // But if an atom cannot be aromatic, then bond can be as single const Edge &edge = _qmol->getEdge(index); QueryMolecule::Atom &q_beg = _qmol->getAtom(edge.beg); QueryMolecule::Atom &q_end = _qmol->getAtom(edge.end); AutoPtr<QueryMolecule::Bond> new_qbond(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); bool beg_can_be_aromatic = q_beg.possibleValue(QueryMolecule::ATOM_AROMATICITY, ATOM_AROMATIC); bool end_can_be_aromatic = q_end.possibleValue(QueryMolecule::ATOM_AROMATICITY, ATOM_AROMATIC); int beg_label = _qmol->getAtomNumber(edge.beg); if (beg_label != -1) beg_can_be_aromatic &= Element::canBeAromatic(beg_label); int end_label = _qmol->getAtomNumber(edge.end); if (end_label != -1) end_can_be_aromatic &= Element::canBeAromatic(end_label); if (beg_can_be_aromatic && end_can_be_aromatic) { new_qbond.reset( QueryMolecule::Bond::oder( new_qbond.release(), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); } _qmol->resetBond(index, new_qbond.release()); } } int i; for (i = 0; i < _cycles.size(); i++) { if (_cycles[i].beg >= 0) throw Error("cycle %d not closed", i); } if (inside_polymer) throw Error("polymer not closed"); } void SmilesLoader::_handleCurlyBrace (_AtomDesc &atom, bool &inside_polymer) { QS_DEF(Array<char>, curly); curly.clear(); while (1) { _scanner.skip(1); int next = _scanner.lookNext(); if (next == -1) throw Error("unclosed curly brace"); if (next == '}') { _scanner.skip(1); break; } curly.push((char)next); } int repetitions; int poly = _parseCurly(curly, repetitions); if (poly == _POLYMER_START) { if (inside_polymer) throw Error("nested polymers not allowed"); inside_polymer = true; atom.starts_polymer = true; _polymer_repetitions.push(0); // can change it later } else if (poly == _POLYMER_END) { if (!inside_polymer) throw Error("misplaced polymer ending"); inside_polymer = false; _polymer_repetitions.top() = repetitions; atom.polymer_index = _polymer_repetitions.size() - 1; atom.ends_polymer = true; } } void SmilesLoader::_loadParsedMolecule () { int i; if (_mol != 0) { for (i = 0; i < _atoms.size(); i++) { if (_atoms[i].label == 0) throw Error("atom without a label"); int idx = _mol->addAtom(_atoms[i].label); _mol->setAtomCharge(idx, _atoms[i].charge); _mol->setAtomIsotope(idx, _atoms[i].isotope); } for (i = 0; i < _bonds.size(); i++) { int beg = _bonds[i].beg; int end = _bonds[i].end; if (end == -1) throw Error("probably pending bond %d not closed", i); _bonds[i].index = _mol->addBond_Silent(beg, end, _bonds[i].type); } } if (!smarts_mode) _markAromaticBonds(); if (_mol != 0) { _addExplicitHForStereo(); _setRadicalsAndHCounts(); } if (smarts_mode) // Forbid matching SMARTS atoms to hydrogens _forbidHydrogens(); if (!inside_rsmiles) for (i = 0; i < _atoms.size(); i++) if (_atoms[i].star_atom && _atoms[i].aam != 0) { if (_qmol != 0) _qmol->resetAtom(i, new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); _bmol->allowRGroupOnRSite(i, _atoms[i].aam); } if (_qmol != 0) // Replace implicit H with explicit one at required stereocenter // or add required number of "any atom" ligands _addLigandsForStereo(); _calcStereocenters(); _calcCisTrans(); _scanner.skipSpace(); if (_scanner.lookNext() == '|') { _scanner.skip(1); _readOtherStuff(); } // Update attachment orders for rsites for (i = _bmol->vertexBegin(); i < _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { if (!_bmol->isRSite(i)) continue; const Vertex &vertex = _bmol->getVertex(i); int j, k = 0; for (j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) _bmol->setRSiteAttachmentOrder(i, vertex.neiVertex(j), k++); } if (!inside_rsmiles) { _scanner.skipSpace(); if (!_scanner.isEOF()) _scanner.readLine(_bmol->name, true); } if (reaction_atom_mapping != 0) { reaction_atom_mapping->clear_resize(_bmol->vertexCount()); reaction_atom_mapping->zerofill(); for (i = 0; i < _atoms.size(); i++) reaction_atom_mapping->at(i) = _atoms[i].aam; } if (ignorable_aam != 0) { ignorable_aam->clear_resize(_bmol->vertexCount()); ignorable_aam->zerofill(); for (i = 0; i < _atoms.size(); i++) ignorable_aam->at(i) = _atoms[i].ignorable_aam ? 1 : 0; } // handle the polymers (part of the CurlySMILES specification) for (i = 0; i < _polymer_repetitions.size() ; i++) _handlePolymerRepetition(i); } void SmilesLoader::_markAromaticBonds () { CycleBasis basis; int i; basis.create(*_bmol); // Mark all 'empty' bonds in "aromatic" rings as aromatic. // We use SSSR here because we do not want "empty" bonds to // be aromatic when they are contained in some aliphatic (SSSR) ring. for (i = 0; i < basis.getCyclesCount(); i++) { const Array<int> &cycle = basis.getCycle(i); int j; bool needs_modification = false; for (j = 0; j < cycle.size(); j++) { int idx = cycle[j]; const Edge &edge = _bmol->getEdge(idx); if (!_atoms[edge.beg].aromatic || !_atoms[edge.end].aromatic) break; if (_bonds[idx].type == BOND_SINGLE || _bonds[idx].type == BOND_DOUBLE || _bonds[idx].type == BOND_TRIPLE) break; if (_qmol != 0 && !_qmol->possibleBondOrder(_bonds[idx].index, BOND_AROMATIC)) break; if (_bonds[idx].type == -1) needs_modification = true; } if (j != cycle.size()) continue; if (needs_modification) { for (j = 0; j < cycle.size(); j++) { int idx = cycle[j]; if (_bonds[idx].type == -1) { _bonds[idx].type = BOND_AROMATIC; int bond_index = _bonds[idx].index; if (_mol != 0) _mol->setBondOrder_Silent(bond_index, BOND_AROMATIC); if (_qmol != 0) _qmol->resetBond(bond_index, QueryMolecule::Bond::und(_qmol->releaseBond(bond_index), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC))); } } } } // mark the rest 'empty' bonds as single for (i = 0; i < _bonds.size(); i++) { if (_bonds[i].type == -1) { int bond_index = _bonds[i].index; if (_mol != 0) _mol->setBondOrder_Silent(bond_index, BOND_SINGLE); if (_qmol != 0) _qmol->resetBond(bond_index, QueryMolecule::Bond::und(_qmol->releaseBond(bond_index), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE))); } } } void SmilesLoader::_setRadicalsAndHCounts () { int i; for (i = 0; i < _atoms.size(); i++) { int idx = i; // The SMILES specification says: Elements in the "organic subset" // B, C, N, O, P, S, F, Cl, Br, and I may be written without brackets // if the number of attached hydrogens conforms to the lowest normal // valence consistent with explicit bonds. We assume that there are // no radicals in that case. if (!_atoms[i].brackets) // We set zero radicals explicitly to properly detect errors like FClF // (while F[Cl]F is correct) _mol->setAtomRadical(idx, 0); if (_atoms[i].hydrogens >= 0) _mol->setImplicitH(idx, _atoms[i].hydrogens); else if (_atoms[i].brackets) // no hydrogens in brackets? _mol->setImplicitH(idx, 0); // no implicit hydrogens on atom then else if (_atoms[i].aromatic && _mol->getAtomAromaticity(i) == ATOM_AROMATIC) { // Additional check for _mol->getAtomAromaticity(i) is required because // a cycle can be non-aromatic while atom letters are small if (_atoms[i].label == ELEM_C) { // here we are basing on the fact that // aromatic uncharged carbon always has a double bond if (_mol->getVertex(i).degree() < 3) // 2-connected aromatic carbon must have 1 single bond and 1 double bond, // so we have one implicit hydrogen left _mol->setImplicitH(idx, 1); else _mol->setImplicitH(idx, 0); } else { // Leave the number of hydrogens as unspecified // Dearomatization algorithm can find any suitable configuration } } } } void SmilesLoader::_forbidHydrogens () { int i; for (i = 0; i < _atoms.size(); i++) { // not needed if it is a sure atom or a list without a hydrogen if (_qmol->getAtomNumber(i) == -1 && _qmol->possibleAtomNumber(i, ELEM_H)) { // not desired if it is a list with hydrogen if (!_qmol->getAtom(i).hasConstraintWithValue(QueryMolecule::ATOM_NUMBER, ELEM_H)) { AutoPtr<QueryMolecule::Atom> newatom; AutoPtr<QueryMolecule::Atom> oldatom(_qmol->releaseAtom(i)); newatom.reset(QueryMolecule::Atom::und( QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), oldatom.release())); _qmol->resetAtom(i, newatom.release()); } } } } void SmilesLoader::_addExplicitHForStereo () { for (int i = 0; i < _atoms.size(); i++) { if ((_atoms[i].chirality > 0) && (_bmol->getVertex(i).degree() == 2) && (_atoms[i].hydrogens == 1)) { _AtomDesc &atom = _atoms.push(_neipool); _BondDesc *bond = &_bonds.push(); atom.label = ELEM_H; int exp_h_idx = _mol->addAtom(atom.label); bond->beg = i; bond->end = _atoms.size() - 1; bond->type = BOND_SINGLE; bond->index = _mol->addBond_Silent(bond->beg, bond->end, bond->type); _atoms[i].neighbors.add(exp_h_idx); _atoms[exp_h_idx].neighbors.add(i); _atoms[exp_h_idx].parent = i; _atoms[i].hydrogens = 0; } } } void SmilesLoader::_addLigandsForStereo () { bool add_explicit_h = false; int num_ligands = 0; for (int i = 0; i < _atoms.size(); i++) { if ((_atoms[i].chirality > 0) && (_bmol->getVertex(i).degree() < 3) && !_isAlleneLike(i)) { if (_atoms[i].hydrogens == 1) { add_explicit_h = true; num_ligands = 3 - _bmol->getVertex(i).degree() - _atoms[i].hydrogens; } else num_ligands = 3 - _bmol->getVertex(i).degree(); for (int j = 0; j < num_ligands; j++) { _AtomDesc &atom = _atoms.push(_neipool); _BondDesc *bond = &_bonds.push(); AutoPtr<QueryMolecule::Atom> qatom; if (add_explicit_h) qatom = QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)); else qatom = QueryMolecule::Atom::oder(QueryMolecule::Atom::nicht (new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)), new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)); AutoPtr<QueryMolecule::Bond> qbond(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); atom.star_atom = true; int any_atom_idx = _qmol->addAtom(qatom.release()); bond->beg = i; bond->end = _atoms.size() - 1; bond->type = BOND_SINGLE; bond->dir = 0; bond->topology = 0; bond->index = _qmol->addBond(i, any_atom_idx, qbond.release()); _atoms[i].neighbors.add(any_atom_idx); _atoms[any_atom_idx].neighbors.add(i); _atoms[any_atom_idx].parent = i; } if (_atoms[i].hydrogens == 1) { _AtomDesc &atom = _atoms.push(_neipool); _BondDesc *bond = &_bonds.push(); AutoPtr<QueryMolecule::Atom> qatom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)); AutoPtr<QueryMolecule::Bond> qbond(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); atom.label = ELEM_H; int exp_h_idx = _qmol->addAtom(qatom.release()); bond->beg = i; bond->end = _atoms.size() - 1; bond->type = BOND_SINGLE; bond->dir = 0; bond->topology = 0; bond->index = _qmol->addBond(i, exp_h_idx, qbond.release()); _atoms[i].neighbors.add(exp_h_idx); _atoms[exp_h_idx].neighbors.add(i); _atoms[exp_h_idx].parent = i; _atoms[i].hydrogens = 0; _qmol->getAtom(i).removeConstraints(QueryMolecule::ATOM_TOTAL_H); } } } } bool SmilesLoader::_isAlleneLike (int i) { if (_bmol->getVertex(i).degree() == 2) { int subst[4]; int subst2[4]; int left, right; bool pure_h[4]; if (MoleculeAlleneStereo::possibleCenter(*_bmol, i, left, right, subst, pure_h)) return true; } return false; } void SmilesLoader::_handlePolymerRepetition (int i) { int j, start = -1, end = -1; int start_bond = -1, end_bond = -1; SGroup *sgroup; // no repetitions counter => polymer if (_polymer_repetitions[i] == 0) { int idx = _bmol->sgroups.addSGroup(SGroup::SG_TYPE_SRU); sgroup = &_bmol->sgroups.getSGroup(idx); RepeatingUnit *ru = (RepeatingUnit *)sgroup; ru->connectivity = RepeatingUnit::HEAD_TO_TAIL; } // repetitions counter present => multiple group else { int idx = _bmol->sgroups.addSGroup(SGroup::SG_TYPE_MUL); sgroup = &_bmol->sgroups.getSGroup(idx); MultipleGroup *mg = (MultipleGroup *)sgroup; mg->multiplier = _polymer_repetitions[i]; } for (j = 0; j < _atoms.size(); j++) { if (_atoms[j].polymer_index != i) continue; sgroup->atoms.push(j); if (_polymer_repetitions[i] > 0) ((MultipleGroup *)sgroup)->parent_atoms.push(j); if (_atoms[j].starts_polymer) start = j; if (_atoms[j].ends_polymer) end = j; } if (start == -1) throw Error("internal: polymer start not found"); if (end == -1) throw Error("internal: polymer end not found"); for (j = 0; j < _bonds.size(); j++) { if (!_bmol->hasEdge(j)) // Edge was removed when virtual atoms for // attachment points are removed continue; const Edge &edge = _bmol->getEdge(j); if (_atoms[edge.beg].polymer_index != i && _atoms[edge.end].polymer_index != i) continue; if (_atoms[edge.beg].polymer_index == i && _atoms[edge.end].polymer_index == i) sgroup->bonds.push(j); else { // bond going out of the sgroup if (start_bond == -1 && (edge.beg == start || edge.end == start)) start_bond = j; else if (end_bond == -1 && (edge.beg == end || edge.end == end)) end_bond = j; else throw Error("internal: unknown bond going from sgroup"); } } if (end_bond == -1 && start_bond != -1) { // swap them to make things below easier __swap(start, end, j); __swap(start_bond, end_bond, j); } Vec2f *p = sgroup->brackets.push(); p[0].set(0, 0); p[1].set(0, 0); p = sgroup->brackets.push(); p[0].set(0, 0); p[1].set(0, 0); if (_polymer_repetitions[i] > 1) { QS_DEF(Array<int>, mapping); AutoPtr<BaseMolecule> rep(_bmol->neu()); rep->makeSubmolecule(*_bmol, sgroup->atoms, &mapping, 0); rep->sgroups.clear(SGroup::SG_TYPE_SRU); rep->sgroups.clear(SGroup::SG_TYPE_MUL); int rep_start = mapping[start]; int rep_end = mapping[end]; // already have one instance of the sgroup; add repetitions if they exist for (j = 0; j < _polymer_repetitions[i] - 1; j++) { _bmol->mergeWithMolecule(rep.ref(), &mapping, 0); int k; for (k = rep->vertexBegin(); k != rep->vertexEnd(); k = rep->vertexNext(k)) sgroup->atoms.push(mapping[k]); for (k = rep->edgeBegin(); k != rep->edgeEnd(); k = rep->edgeNext(k)) { const Edge &edge = rep->getEdge(k); sgroup->bonds.push(_bmol->findEdgeIndex(mapping[edge.beg], mapping[edge.end])); } if (rep_end >= 0 && end_bond >= 0) { // make new connections from the end of the old fragment // to the beginning of the new one, and from the end of the // new fragment outwards from the sgroup int external = _bmol->getEdge(end_bond).findOtherEnd(end); _bmol->removeBond(end_bond); if (_mol != 0) { _mol->addBond(end, mapping[rep_start], BOND_SINGLE); end_bond = _mol->addBond(mapping[rep_end], external, BOND_SINGLE); } else { _qmol->addBond(end, mapping[rep_start], new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); end_bond = _qmol->addBond(mapping[rep_end], external, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } end = mapping[rep_end]; } } } else if (_polymer_repetitions[i] == 0) { // if the start atom of the polymer does not have an incoming bond... if (start_bond == -1) { if (_mol != 0) { // ... add one, with a "star" on the other end. int star = _mol->addAtom(ELEM_PSEUDO); _mol->setPseudoAtom(star, "*"); _mol->addBond(start, star, BOND_SINGLE); } else { // if it is a query molecule, add a bond with "any" atom instead int any = _qmol->addAtom(new QueryMolecule::Atom()); _qmol->addBond(start, any, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } } // do the same with the end atom if (end_bond == -1) { if (_mol != 0) { int star = _mol->addAtom(ELEM_PSEUDO); _mol->setPseudoAtom(star, "*"); _mol->addBond(end, star, BOND_SINGLE); } else { int any = _qmol->addAtom(new QueryMolecule::Atom()); _qmol->addBond(end, any, new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE)); } } } } void SmilesLoader::_loadMolecule () { _atoms.clear(); _bonds.clear(); _polymer_repetitions.clear(); _parseMolecule(); _loadParsedMolecule(); } void SmilesLoader::_readBond (Array<char> &bond_str, _BondDesc &bond, AutoPtr<QueryMolecule::Bond> &qbond) { if (bond_str.find(';') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Bond> subqbond; int i; if (_qmol == 0) throw Error("';' is allowed only within queries"); substring.clear(); for (i = 0; i <= bond_str.size(); i++) { if (i == bond_str.size() || bond_str[i] == ';') { subqbond.reset(new QueryMolecule::Bond); _readBond(substring, bond, subqbond); qbond.reset(QueryMolecule::Bond::und(qbond.release(), subqbond.release())); substring.clear(); } else substring.push(bond_str[i]); } return; } if (bond_str.find(',') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Bond> subqbond; int i; if (_qmol == 0) throw Error("',' is allowed only within queries"); substring.clear(); for (i = 0; i <= bond_str.size(); i++) { if (i == bond_str.size() || bond_str[i] == ',') { subqbond.reset(new QueryMolecule::Bond); _readBond(substring, bond, subqbond); if (qbond->type == 0) qbond.reset(subqbond.release()); else qbond.reset(QueryMolecule::Bond::oder(qbond.release(), subqbond.release())); substring.clear(); } else substring.push(bond_str[i]); } return; } if (bond_str.find('&') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Bond> subqbond; int i; if (_qmol == 0) throw Error("'&' is allowed only within queries"); substring.clear(); for (i = 0; i <= bond_str.size(); i++) { if (i == bond_str.size() || bond_str[i] == '&') { subqbond.reset(new QueryMolecule::Bond); _readBond(substring, bond, subqbond); qbond.reset(QueryMolecule::Bond::und(qbond.release(), subqbond.release())); substring.clear(); } else substring.push(bond_str[i]); } return; } _readBondSub(bond_str, bond, qbond); } void SmilesLoader::_readBondSub (Array<char> &bond_str, _BondDesc &bond, AutoPtr<QueryMolecule::Bond> &qbond) { BufferScanner scanner(bond_str); bool neg = false; while (!scanner.isEOF()) { int next = scanner.lookNext(); int order = -1; int topology = -1; if (next == '!') { scanner.skip(1); neg = !neg; if (qbond.get() == 0) throw Error("'!' is allowed only within queries"); continue; } if (next == '-') { scanner.skip(1); order = BOND_SINGLE; } else if (next == '=') { scanner.skip(1); order = BOND_DOUBLE; } else if (next == '#') { scanner.skip(1); order = BOND_TRIPLE; } else if (next == ':') { scanner.skip(1); order = BOND_AROMATIC; } else if (next == '/') { scanner.skip(1); order = BOND_SINGLE; if (bond.dir == 2) throw Error("Specificiation of both cis- and trans- bond restriction is not supported yet."); bond.dir = 1; } else if (next == '\\') { scanner.skip(1); order = BOND_SINGLE; if (bond.dir == 1) throw Error("Specificiation of both cis- and trans- bond restriction is not supported yet."); bond.dir = 2; } else if (next == '~') { scanner.skip(1); order = _ANY_BOND; if (qbond.get() == 0) throw Error("'~' any bond is allowed only for queries"); } else if (next == '@') { scanner.skip(1); if (qbond.get() == 0) throw Error("'@' ring bond is allowed only for queries"); topology = TOPOLOGY_RING; } else throw Error("Character #%d is unexpected during bond parsing", next); AutoPtr<QueryMolecule::Bond> subqbond; if (order > 0) { bond.type = order; if (qbond.get() != 0) { if (subqbond.get() == 0) subqbond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, order)); else subqbond.reset(QueryMolecule::Bond::und(subqbond.release(), new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, order))); } } else if (order == _ANY_BOND) { bond.type = order; } if (topology > 0) { if (subqbond.get() == 0) subqbond.reset(new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topology)); else subqbond.reset(QueryMolecule::Bond::und(subqbond.release(), new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topology))); } if (subqbond.get() != 0) { if (neg) { subqbond.reset(QueryMolecule::Bond::nicht(subqbond.release())); neg = false; } qbond.reset(QueryMolecule::Bond::und(qbond.release(), subqbond.release())); } } } bool SmilesLoader::_readAtomLogic (Array<char> &atom_str, bool first_in_brackets, _AtomDesc &atom, AutoPtr<QueryMolecule::Atom> &qatom) { QS_DEF(Array<char>, atom_str_copy); if (atom_str.size() < 1) throw Error("empty atom?"); atom_str_copy.copy(atom_str); int i, k; while ((k = atom_str_copy.find('$')) != -1) { // fill the "$(...) part of atom_str_copy with '^'" int cnt = 1; atom_str_copy[k] = '^'; for (i = k + 2; i < atom_str_copy.size(); i++) { if (atom_str_copy[i] == '(') cnt++; else if (atom_str_copy[i] == ')') cnt--; if (cnt == 0) break; atom_str_copy[i] = '^'; } } if (atom_str_copy.find(';') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Atom> subqatom; int i, k = 0; if (qatom.get() == 0) throw Error("';' is allowed only for query molecules"); substring.clear(); for (i = 0; i <= atom_str_copy.size(); i++) { if (i == atom_str.size() || atom_str_copy[i] == ';') { subqatom.reset(new QueryMolecule::Atom); _readAtom(substring, first_in_brackets && (k == 0), atom, subqatom); qatom.reset(QueryMolecule::Atom::und(qatom.release(), subqatom.release())); substring.clear(); k++; } else substring.push(atom_str[i]); } return false; } if (atom_str_copy.find(',') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Atom> subqatom; int i, k = 0; if (qatom.get() == 0) throw Error("',' is allowed only for query molecules"); substring.clear(); for (i = 0; i <= atom_str.size(); i++) { if (i == atom_str.size() || atom_str_copy[i] == ',') { subqatom.reset(new QueryMolecule::Atom); _readAtom(substring, first_in_brackets && (k == 0), atom, subqatom); if (qatom->type == 0) qatom.reset(subqatom.release()); else qatom.reset(QueryMolecule::Atom::oder(qatom.release(), subqatom.release())); substring.clear(); k++; } else substring.push(atom_str[i]); } return false; } if (atom_str_copy.find('&') != -1) { QS_DEF(Array<char>, substring); AutoPtr<QueryMolecule::Atom> subqatom; int i, k = 0; if (qatom.get() == 0) throw Error("'&' is allowed only for query molecules"); substring.clear(); for (i = 0; i <= atom_str.size(); i++) { if (i == atom_str.size() || atom_str_copy[i] == '&') { subqatom.reset(new QueryMolecule::Atom); _readAtom(substring, first_in_brackets && (k == 0), atom, subqatom); qatom.reset(QueryMolecule::Atom::und(qatom.release(), subqatom.release())); substring.clear(); k++; } else substring.push(atom_str[i]); } return false; } return true; } void SmilesLoader::_readAtom (Array<char> &atom_str, bool first_in_brackets, _AtomDesc &atom, AutoPtr<QueryMolecule::Atom> &qatom) { if (!_readAtomLogic(atom_str, first_in_brackets, atom, qatom)) return; BufferScanner scanner(atom_str); bool element_assigned = false; bool neg = false; while (!scanner.isEOF()) { bool isotope_set = false; int element = -1; int aromatic = 0; int next = scanner.lookNext(); AutoPtr<QueryMolecule::Atom> subatom; if (next == '!') { if (qatom.get() == 0) throw Error("'!' is allowed only within queries"); scanner.skip(1); neg = !neg; first_in_brackets = false; continue; } else if (next == '$') { scanner.skip(1); if (scanner.readChar() != '(') throw Error("'$' must be followed by '('"); QS_DEF(Array<char>, subexp); subexp.clear(); int cnt = 1; while (1) { char c = scanner.readChar(); if (c == '(') cnt++; else if (c == ')') { cnt--; if (cnt == 0) break; } subexp.push(c); } BufferScanner subscanner(subexp); AutoPtr<SmilesLoader> subloader(new SmilesLoader(subscanner)); AutoPtr<QueryMolecule> fragment(new QueryMolecule()); subloader->loadSMARTS(fragment.ref()); fragment->fragment_smarts.copy(subexp); fragment->fragment_smarts.push(0); if (subatom.get() == 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_FRAGMENT, fragment.release())); else subatom.reset(QueryMolecule::Atom::und(subatom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_FRAGMENT, fragment.release()))); } else if (isdigit(next)) { int isotope = scanner.readUnsigned(); if (qatom.get() != 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, isotope)); else atom.isotope = isotope; isotope_set = true; } else if (next == 'H') { scanner.skip(1); // Now comes the trouble with the 'H' symbol. // As the manual says // (see http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html): // [H] means hydrogen atom. // [*H2] means any atom with exactly two hydrogens attached. // Yet in the combined expressions like [n;H1] 'H' means the hydrogen // count, not the element. To distinguish these things, we use // the 'first in brackets' flag, which is true only for the very // first sub-expression in the brackets. // Also, the following elements begin with H: He, Hs, Hf, Ho, Hg if (strchr("esfog", scanner.lookNext()) == NULL) { if (first_in_brackets) element = ELEM_H; else { atom.hydrogens = 1; if (isdigit(scanner.lookNext())) atom.hydrogens = scanner.readUnsigned(); if (qatom.get() != 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_H, atom.hydrogens)); } } else element = Element::fromTwoChars('H', scanner.readChar()); } // The 'A' symbol is weird too. It can be the 'aliphatic' atomic primitive, // and can also be Al, Ar, As, Ag, Au, At, Ac, or Am. else if (next == 'A') { scanner.skip(1); if (strchr("lrsgutcm", scanner.lookNext()) == NULL) { if (qatom.get() == 0) throw Error("'A' specifier is allowed only for query molecules"); subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, ATOM_ALIPHATIC)); } else element = Element::fromTwoChars('A', scanner.readChar()); } // Similarly, 'R' can start Rb, Ru, Rh, Re, Rn, Ra, Rf, Rg else if (next == 'R') { scanner.skip(1); if (strchr("buhenafg", scanner.lookNext()) == NULL) { if (qatom.get() == 0) throw Error("'R' specifier is allowed only for query molecules"); if (isdigit(scanner.lookNext())) { int rc = scanner.readUnsigned(); if (rc == 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 0)); else subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_SSSR_RINGS, rc)); } else subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 1, 100)); } else element = Element::fromTwoChars('R', scanner.readChar()); } // Yet 'D' can start Db, Ds, Dy else if (next == 'D') { scanner.skip(1); if (strchr("bsy", scanner.lookNext()) == NULL) { if (qatom.get() == 0) throw Error("'D' specifier is allowed only for query molecules"); int degree = 1; if (isdigit(scanner.lookNext())) degree = scanner.readUnsigned(); subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, degree)); } else element = Element::fromTwoChars('D', scanner.readChar()); } // ... and 'X' can start Xe else if (next == 'X') { scanner.skip(1); if (scanner.lookNext() != 'e') { if (qatom.get() == 0) throw Error("'X' specifier is allowed only for query molecules"); int conn = 1; if (isdigit(scanner.lookNext())) conn = scanner.readUnsigned(); subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_CONNECTIVITY, conn)); } else element = Element::fromTwoChars('X', scanner.readChar()); } else if (next == '*') { atom.star_atom = true; if (qatom.get() == 0) atom.label = ELEM_RSITE; else subatom.reset(QueryMolecule::Atom::nicht(new QueryMolecule::Atom (QueryMolecule::ATOM_NUMBER, ELEM_H))); scanner.skip(1); } else if (next == '#') { scanner.skip(1); element = scanner.readUnsigned(); } // Now we check that we have here an element from the periodic table. // We assume that this must be an alphabetic character and also // something not from the alphabetic SMARTS 'atomic primitives' // (see http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html). else if (isalpha(next) && strchr("hrvxast", next) == NULL) { scanner.skip(1); if (next == 'b') { element = ELEM_B; aromatic = ATOM_AROMATIC; } else if (next == 'c') { element = ELEM_C; aromatic = ATOM_AROMATIC; } else if (next == 'n') { element = ELEM_N; aromatic = ATOM_AROMATIC; } else if (next == 'o') { element = ELEM_O; aromatic = ATOM_AROMATIC; } else if (next == 'p') { element = ELEM_P; aromatic = ATOM_AROMATIC; } else if (islower(next)) throw Error("unrecognized lowercase symbol: %c", next); // Now we are sure that 'next' is a capital letter // Check if we have a lowercase letter right after... else if (isalpha(scanner.lookNext()) && islower(scanner.lookNext()) && // If a lowercase letter is following the uppercase letter, // we should consider reading them as a single element. // They can possibly not form an element: for example, // [Nr] is formally a nitrogen in a ring (although nobody would // write it that way: [N;r] is much more clear). (element = Element::fromTwoChars2(next, scanner.lookNext())) > 0) { scanner.skip(1); if (smarts_mode) if (element == ELEM_As || element == ELEM_Se) aromatic = ATOM_ALIPHATIC; } else { // It is a single-char uppercase element identifier then element = Element::fromChar(next); if (smarts_mode) if (element == ELEM_B || element == ELEM_C || element == ELEM_N || element == ELEM_O || element == ELEM_P || element == ELEM_S) aromatic = ATOM_ALIPHATIC; } } else if (next == '@') { atom.chirality = 1; scanner.skip(1); if (scanner.lookNext() == '@') { atom.chirality = 2; scanner.skip(1); } } else if (next == '+' || next == '-') { char c = scanner.readChar(); if (c == '+') atom.charge = 1; else atom.charge = -1; if (isdigit(scanner.lookNext())) atom.charge *= scanner.readUnsigned(); else while (scanner.lookNext() == c) { scanner.skip(1); if (c == '+') atom.charge++; else atom.charge--; } if (qatom.get() != 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, atom.charge)); } else if (next == 'a') // can be [as] or SMARTS aromaticity flag { scanner.skip(1); if (scanner.lookNext() == 's') { scanner.skip(1); element = ELEM_As; aromatic = ATOM_AROMATIC; } else { if (qatom.get() == 0) throw Error("'a' specifier is allowed only for query molecules"); subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, ATOM_AROMATIC)); } } else if (next == 's') // can be [s], [se] or [si] { scanner.skip(1); if (scanner.lookNext() == 'e') { scanner.skip(1); element = ELEM_Se; aromatic = ATOM_AROMATIC; } else if (scanner.lookNext() == 'i') { // Aromatic Si cannot occure in SMILES by specification, but // Cactvs produces it scanner.skip(1); element = ELEM_Si; aromatic = ATOM_AROMATIC; } else { element = ELEM_S; aromatic = ATOM_AROMATIC; } } else if (next == 't') // [te] { // Aromatic Te cannot occure in SMILES by specification, but // RDKit produces it within extended SMILES scanner.skip(1); if (scanner.lookNext() == 'e') { scanner.skip(1); element = ELEM_Te; aromatic = ATOM_AROMATIC; } else throw Error("invalid character within atom description: '%c'", next); } else if (next == 'h') // Why would anybody ever need 'implicit hydrogen' // count rather than total hydrogen count? throw Error("'h' specifier is not supported"); else if (next == 'r') { scanner.skip(1); if (qatom.get() == 0) throw Error("'r' specifier is allowed only for query molecules"); if (isdigit(scanner.lookNext())) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_SMALLEST_RING_SIZE, scanner.readUnsigned())); else subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 1, 100)); } else if (next == 'v') { scanner.skip(1); if (qatom.get() == 0) throw Error("'v' specifier is allowed only for query molecules"); int val = 1; if (isdigit(scanner.lookNext())) val = scanner.readUnsigned(); subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_TOTAL_BOND_ORDER, val)); } else if (next == 'x') { scanner.skip(1); if (qatom.get() == 0) throw Error("'x' specifier is allowed only for query molecules"); if (isdigit(scanner.lookNext())) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, scanner.readUnsigned())); else subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, 1, 100)); } else if (next == ':') { scanner.skip(1); if (scanner.lookNext() == '?') { if (_qmol == 0) throw Error("ignorable AAM numbers are allowed only for queries"); atom.ignorable_aam = true; scanner.skip(1); } atom.aam = scanner.readUnsigned(); } else throw Error("invalid character within atom description: '%c'", next); if (element > 0) { if (qatom.get() != 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, element)); else { if (element_assigned) throw Error("two element labels for one atom"); atom.label = element; } element_assigned = true; } if (aromatic != 0) { if (aromatic == ATOM_AROMATIC) atom.aromatic = true; if (qatom.get() != 0) { if (subatom.get() == 0) subatom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, aromatic)); else subatom.reset(QueryMolecule::Atom::und(subatom.release(), new QueryMolecule::Atom(QueryMolecule::ATOM_AROMATICITY, aromatic))); } } if (subatom.get() != 0) { if (neg) { subatom.reset(QueryMolecule::Atom::nicht(subatom.release())); neg = false; } qatom.reset(QueryMolecule::Atom::und(qatom.release(), subatom.release())); } // we check for isotope_set here to treat [2H] as deuterium atom, // not like something with isotope number 2 and h-count 1 if (!isotope_set) first_in_brackets = false; } } int SmilesLoader::_parseCurly (Array<char> &curly, int &repetitions) { if (curly.size() == 1 && curly[0] == '-') return _POLYMER_START; if (curly.size() >= 2 && curly[0] == '+') { if (curly[1] == 'r') throw Error("ring repeating units not supported"); if (curly[1] == 'n') { repetitions = 0; BufferScanner scanner(curly.ptr() + 2, curly.size() - 2); if (scanner.lookNext() == 'n') { scanner.skip(2); repetitions = scanner.readInt(); } return _POLYMER_END; } } return 0; } SmilesLoader::_AtomDesc::_AtomDesc (Pool<List<int>::Elem> &neipool) : neighbors(neipool) { label = 0; isotope = 0; charge = 0; hydrogens = -1; chirality = 0; aromatic = 0; aam = 0; ignorable_aam = false; brackets = false; star_atom = false; ends_polymer = false; starts_polymer = false; polymer_index = -1; parent = -1; } SmilesLoader::_AtomDesc::~_AtomDesc () { } void SmilesLoader::_AtomDesc::pending (int cycle) { if (cycle < 1) throw Error("cycle number %d is not allowed", cycle); neighbors.add(-cycle); } void SmilesLoader::_AtomDesc::closure (int cycle, int end) { int i; if (cycle < 1) throw Error("cycle number %d is not allowed", cycle); for (i = neighbors.begin(); i != neighbors.end(); i = neighbors.next(i)) { if (neighbors.at(i) == -cycle) { neighbors.at(i) = end; break; } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/molecule/src/smiles_saver.cpp���������������������������������������������������0000664�0000000�0000000�00000153146�12710376503�0022007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/smiles_saver.h" #include "molecule/molecule_rgroups.h" #include "base_cpp/array.h" #include "base_cpp/tlscont.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_stereocenters.h" #include "graph/dfs_walk.h" #include "molecule/elements.h" #include "molecule/molecule_arom_match.h" #include "graph/cycle_basis.h" #include "molecule/molecule_savers.h" using namespace indigo; IMPL_ERROR(SmilesSaver, "SMILES saver"); CP_DEF(SmilesSaver); SmilesSaver::SmilesSaver (Output &output) : _output(output), CP_INIT, TL_CP_GET(_neipool), TL_CP_GET(_atoms), TL_CP_GET(_hcount), TL_CP_GET(_hcount_ignored), TL_CP_GET(_dbonds), TL_CP_GET(_written_atoms), TL_CP_GET(_written_atoms_inv), TL_CP_GET(_written_bonds), TL_CP_GET(_polymer_indices), TL_CP_GET(_attachment_indices), TL_CP_GET(_attachment_cycle_numbers), TL_CP_GET(_aromatic_bonds), TL_CP_GET(_ignored_vertices), TL_CP_GET(_complicated_cistrans), TL_CP_GET(_ban_slashes), TL_CP_GET(_cis_trans_parity) { vertex_ranks = 0; atom_atom_mapping = 0; ignore_hydrogens = false; canonize_chiralities = false; write_extra_info = true; _mol = 0; smarts_mode = false; ignore_invalid_hcount = true; separate_rsites = true; rsite_indices_as_aam = true; _n_attachment_points = 0; _have_complicated_cistrans = false; } SmilesSaver::~SmilesSaver () { _atoms.clear(); // to avoid data race when it is reused in another thread } void SmilesSaver::saveMolecule (Molecule &mol) { _bmol = &mol; _qmol = 0; _mol = &mol; _saveMolecule(); } void SmilesSaver::saveQueryMolecule (QueryMolecule &mol) { _bmol = &mol; _qmol = &mol; _mol = 0; _saveMolecule(); } void SmilesSaver::_saveMolecule () { int i, j, k; _ignored_vertices.clear_resize(_bmol->vertexEnd()); _ignored_vertices.zerofill(); if (ignore_hydrogens) { if (_qmol != 0) throw Error("ignore_hydrogens does not make sense for query molecules"); for (i = _bmol->vertexBegin(); i < _bmol->vertexEnd(); i = _bmol->vertexNext(i)) if (_mol->asMolecule().convertableToImplicitHydrogen(i)) _ignored_vertices[i] = 1; } _checkRGroupsAndAttachmentPoints(); _checkSRU(); _touched_cistransbonds = 0; _complicated_cistrans.clear_resize(_bmol->edgeEnd()); _complicated_cistrans.zerofill(); _ban_slashes.clear_resize(_bmol->edgeEnd()); _ban_slashes.zerofill(); _cis_trans_parity.clear_resize(_bmol->edgeEnd()); _cis_trans_parity.zerofill(); _markCisTrans(); _atoms.clear(); while (_atoms.size() < _bmol->vertexEnd()) _atoms.push(_neipool); _written_atoms.clear(); _written_bonds.clear(); _written_components = 0; _aromatic_bonds.clear(); _hcount.clear_resize(_bmol->vertexEnd()); _hcount_ignored.clear_resize(_bmol->vertexEnd()); for (i = _bmol->vertexBegin(); i < _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { _hcount[i] = 0; if (_mol != 0) { if (!_mol->isPseudoAtom(i) && !_mol->isRSite(i)) _hcount[i] = _mol->getImplicitH_NoThrow(i, -1); } else { int atom_number = _bmol->getAtomNumber(i); int atom_charge = _bmol->getAtomCharge(i); _hcount[i] = MoleculeSavers::getHCount(*_bmol, i, atom_number, atom_charge); } _hcount_ignored[i] = 0; const Vertex &vertex = _bmol->getVertex(i); if (ignore_hydrogens) { if (_hcount[i] >= 0) { for (j = vertex.neiBegin(); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int idx = vertex.neiVertex(j); if (_bmol->getAtomNumber(idx) == ELEM_H && _bmol->getAtomIsotope(idx) == 0) if (_ignored_vertices[idx]) _hcount_ignored[i]++; } _hcount[i] += _hcount_ignored[i]; } } if (_bmol->getAtomAromaticity(i) == ATOM_AROMATIC) { _atoms[i].aromatic = true; // From the SMILES specification: // Please note that only atoms on the following list // can be considered aromatic: C, N, O, P, S, As, Se, and * (wildcard). static int allowed_lowercase[] = {ELEM_B, ELEM_C, ELEM_N, ELEM_O, ELEM_P, ELEM_S, ELEM_Se, ELEM_As}; if (_bmol->atomNumberBelongs(i, allowed_lowercase, NELEM(allowed_lowercase))) _atoms[i].lowercase = true; } } DfsWalk walk(*_bmol); walk.ignored_vertices = _ignored_vertices.ptr(); walk.vertex_ranks = vertex_ranks; if (_bmol->sgroups.isPolimer()) walk.vertex_classes = _polymer_indices.ptr(); if (separate_rsites) { for (i = _bmol->vertexBegin(); i < _bmol->vertexEnd(); i = _bmol->vertexNext(i)) { if (_bmol->isRSite(i)) // We break the DFS walk when going through R-sites. For details, see // http://blueobelisk.shapado.com/questions/how-r-group-atoms-should-be-represented-in-smiles walk.mustBeRootVertex(i); } } walk.walk(); const Array<DfsWalk::SeqElem> &v_seq = walk.getSequence(); // fill up neighbor lists for the stereocenters calculation for (i = 0; i < v_seq.size(); i++) { int v_idx = v_seq[i].idx; int e_idx = v_seq[i].parent_edge; int v_prev_idx = v_seq[i].parent_vertex; _Atom &atom = _atoms[v_idx]; if (e_idx >= 0) { if (walk.isClosure(e_idx)) { int k; for (k = atom.neighbors.begin(); k != atom.neighbors.end(); k = atom.neighbors.next(k)) { if (atom.neighbors[k] == -1) { atom.neighbors[k] = v_prev_idx; break; } } if (k == atom.neighbors.end()) throw Error("internal: can not put closing bond to its place"); } else { atom.neighbors.add(v_prev_idx); atom.parent = v_prev_idx; } _atoms[v_prev_idx].neighbors.add(v_idx); } if (e_idx < 0 || !walk.isClosure(e_idx)) { int openings = walk.numOpenings(v_idx); for (j = 0; j < openings; j++) atom.neighbors.add(-1); } // also, detect polymer borders if (_polymer_indices[v_idx] >= 0 && (v_prev_idx == -1 || _polymer_indices[v_prev_idx] != _polymer_indices[v_idx])) atom.starts_polymer = true; if (v_prev_idx >= 0 && _polymer_indices[v_prev_idx] >= 0 && _polymer_indices[v_prev_idx] != _polymer_indices[v_idx]) _atoms[v_prev_idx].ends_polymer = true; } _written_atoms_inv.clear_resize(_bmol->vertexEnd()); _written_atoms_inv.fffill(); for (i = 0; i < v_seq.size(); i++) { int v_idx = v_seq[i].idx; int e_idx = v_seq[i].parent_edge; if (e_idx == -1 || !walk.isClosure(e_idx)) { _written_atoms.push(v_idx); _written_atoms_inv[v_idx] = _written_atoms.size() - 1; } if (e_idx != -1) _written_bonds.push(e_idx); } // detect chiral configurations MoleculeStereocenters &stereocenters = _bmol->stereocenters; for (i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int atom_idx, type, group, pyramid[4]; stereocenters.get(i, atom_idx, type, group, pyramid); if (type < MoleculeStereocenters::ATOM_AND) continue; int implicit_h_idx = -1; if (pyramid[3] == -1) implicit_h_idx = 3; else for (j = 0; j < 4; j++) if (_ignored_vertices[pyramid[j]]) { implicit_h_idx = j; break; } int pyramid_mapping[4]; int counter = 0; _Atom &atom = _atoms[atom_idx]; if (atom.parent != -1) for (k = 0; k < 4; k++) if (pyramid[k] == atom.parent) { pyramid_mapping[counter++] = k; break; } if (implicit_h_idx != -1) pyramid_mapping[counter++] = implicit_h_idx; for (j = atom.neighbors.begin(); j != atom.neighbors.end(); j = atom.neighbors.next(j)) { if (atom.neighbors[j] == atom.parent) continue; for (k = 0; k < 4; k++) if (atom.neighbors[j] == pyramid[k]) { if (counter >= 4) throw Error("internal: pyramid overflow"); pyramid_mapping[counter++] = k; break; } } if (counter == 4) { // move the 'from' atom to the end counter = pyramid_mapping[0]; pyramid_mapping[0] = pyramid_mapping[1]; pyramid_mapping[1] = pyramid_mapping[2]; pyramid_mapping[2] = pyramid_mapping[3]; pyramid_mapping[3] = counter; } else if (counter != 3) throw Error("cannot calculate chirality"); if (MoleculeStereocenters::isPyramidMappingRigid(pyramid_mapping)) _atoms[atom_idx].chirality = 1; else _atoms[atom_idx].chirality = 2; } MoleculeAlleneStereo &allene_stereo = _bmol->allene_stereo; for (i = allene_stereo.begin(); i != allene_stereo.end(); i = allene_stereo.next(i)) { int atom_idx, left, right, parity, subst[4], subst_map[4] = {-1, -1, -1, -1}; int tmp; allene_stereo.get(i, atom_idx, left, right, subst, parity); for (j = 0; j < 4; j++) if (subst[j] >= 0) subst_map[j] = _written_atoms_inv[subst[j]]; // Daylight doc says: Hydrogens attached to substituted allene-like atoms // are taken to be immediately following that atom if (subst_map[0] < 0) subst_map[0] = _written_atoms_inv[left]; else if (subst_map[1] < 0) subst_map[1] = _written_atoms_inv[left]; if (subst_map[2] < 0) subst_map[2] = _written_atoms_inv[right]; else if (subst_map[3] < 0) subst_map[3] = _written_atoms_inv[right]; if (subst_map[0] < 0 || subst_map[1] < 0 || subst_map[2] < 0 || subst_map[3] < 0) throw Error("internal: allene subsituent not mapped"); if (subst_map[1] < subst_map[0]) { __swap(subst_map[0], subst_map[1], tmp); parity = 3 - parity; } if (subst_map[3] < subst_map[2]) { __swap(subst_map[2], subst_map[3], tmp); parity = 3 - parity; } _atoms[atom_idx].chirality = 3 - parity; } if (canonize_chiralities) { int i, j; QS_DEF(Array<int>, marked); QS_DEF(Array<int>, ids); const MoleculeStereocenters &stereocenters = _bmol->stereocenters; marked.clear_resize(_bmol->vertexEnd()); marked.zerofill(); for (i = 0; i < _written_atoms.size(); i++) { if (marked[i]) continue; int idx = _written_atoms[i]; if (_atoms[idx].chirality == 0) continue; int type = stereocenters.getType(idx); if (type != MoleculeStereocenters::ATOM_AND && type != MoleculeStereocenters::ATOM_OR) continue; ids.clear(); ids.push(idx); int group = stereocenters.getGroup(idx); for (j = i + 1; j < _written_atoms.size(); j++) { if (marked[j]) continue; int idx2 = _written_atoms[j]; if (_atoms[idx2].chirality == 0) continue; int type2 = stereocenters.getType(idx2); int group2 = stereocenters.getGroup(idx2); if (type2 == type && group2 == group) { ids.push(idx2); marked[j] = 1; } } if (_atoms[ids[0]].chirality == 1) for (j = 0; j < ids.size(); j++) _atoms[ids[j]].chirality = 3 - _atoms[ids[j]].chirality; } } // write the SMILES itself // cycle_numbers[i] == -1 means that the number is available // cycle_numbers[i] == n means that the number is used by vertex n QS_DEF(Array<int>, cycle_numbers); int rsites_closures_starting_num = 91; int rbonds = _countRBonds() + _n_attachment_points; if (rbonds > 9) rsites_closures_starting_num = 99 - rbonds; cycle_numbers.clear(); cycle_numbers.push(0); // never used bool first_component = true; for (i = 0; i < v_seq.size(); i++) { int v_idx = v_seq[i].idx; int e_idx = v_seq[i].parent_edge; int v_prev_idx = v_seq[i].parent_vertex; bool write_atom = true; if (v_prev_idx >= 0) { int branches = walk.numBranches(v_prev_idx); if (branches > 1) if (_atoms[v_prev_idx].branch_cnt > 0 && _atoms[v_prev_idx].paren_written) _output.writeChar(')'); /* * Fix IND-673 unused if-statement */ // if (v_prev_idx >= 0) // { if (branches > 1) if (_atoms[v_prev_idx].branch_cnt < branches - 1) { if (walk.isClosure(e_idx)) _atoms[v_prev_idx].paren_written = false; else { _output.writeChar('('); _atoms[v_prev_idx].paren_written = true; } } _atoms[v_prev_idx].branch_cnt++; if (_atoms[v_prev_idx].branch_cnt > branches) throw Error("unexpected branch"); /* * Fix IND-673 unused if-statement */ // } const Edge &edge = _bmol->getEdge(e_idx); bool bond_written = true; int dir = 0; int bond_order = _bmol->getBondOrder(e_idx); if (bond_order == BOND_SINGLE) dir = _calcBondDirection(e_idx, v_prev_idx); if ((dir == 1 && v_idx == edge.end) || (dir == 2 && v_idx == edge.beg)) _output.writeChar('/'); else if ((dir == 2 && v_idx == edge.end) || (dir == 1 && v_idx == edge.beg)) _output.writeChar('\\'); else if (smarts_mode) _writeSmartsBond(e_idx, &_qmol->getBond(e_idx)); else if (bond_order == BOND_DOUBLE) _output.writeChar('='); else if (bond_order == BOND_TRIPLE) _output.writeChar('#'); else if (bond_order == BOND_AROMATIC && _shouldWriteAromaticBond(e_idx)) _output.writeChar(':'); else if (bond_order == BOND_SINGLE && _atoms[edge.beg].aromatic && _atoms[edge.end].aromatic) _output.writeChar('-'); else bond_written = false; if (walk.isClosure(e_idx)) { for (j = 1; j < cycle_numbers.size(); j++) if (cycle_numbers[j] == v_idx) break; if (j == cycle_numbers.size()) throw Error("cycle number not found"); _writeCycleNumber(j); cycle_numbers[j] = -1; write_atom = false; } } else { if (!first_component) _output.writeChar('.'); first_component = false; _written_components++; } if (write_atom) { if (!smarts_mode) _writeAtom(v_idx, _atoms[v_idx].aromatic, _atoms[v_idx].lowercase, _atoms[v_idx].chirality); else _writeSmartsAtom(v_idx, &_qmol->getAtom(v_idx), _atoms[v_idx].chirality, 0, false); QS_DEF(Array<int>, closing); walk.getNeighborsClosing(v_idx, closing); for (int ap = 1; ap <= _bmol->attachmentPointCount(); ap++) { int idx = 0, atom_idx; for (idx = 0; (atom_idx = _bmol->getAttachmentPoint(ap, idx)) != -1; idx++) if (atom_idx == v_idx) { // Here is the problem: if we have an attachment point, the resulting // SMILES is supposed to contain an extra atom not present in the given // molecule. For example, chlorine with an attachment point will // become Cl%91.[*:1]%91 |;_AP1| (two atoms). // We can not modify the given molecule, but we want the closure to // be here. To achieve that, we add a link to an imagimary atom with // incredibly big number. closing.push(10000 + ap); } } for (j = 0; j < closing.size(); j++) { bool ap = (closing[j] >= 10000); bool rsite = !ap && (separate_rsites && _bmol->isRSite(closing[j])); if (ap || rsite) { cycle_numbers.expandFill(rsites_closures_starting_num + 1, -1); for (k = rsites_closures_starting_num; k < cycle_numbers.size(); k++) if (cycle_numbers[k] == -1) break; } else { for (k = 1; k < cycle_numbers.size(); k++) if (cycle_numbers[k] == -1) break; } if (ap) { _attachment_indices.push(closing[j] - 10000); _attachment_cycle_numbers.push(k); } if (k == cycle_numbers.size()) cycle_numbers.push(v_idx); else cycle_numbers[k] = v_idx; _writeCycleNumber(k); } if (_atoms[v_idx].starts_polymer) _output.writeString("{-}"); if (_atoms[v_idx].ends_polymer) _output.writeString("{+n}"); } } if (write_extra_info) { // Before we write the |...| block (ChemAxon's Extended SMILES), // we must clean up the mess we did with the attachment points // (see big comment above). That is, we append separated atoms, // not present in the original molecule, to the end, and connect // them with the "cycle" closure to the real atoms that are the // attachment points. for (i = 0; i < _attachment_indices.size(); i++) { _output.printf(".[*:%d]", _attachment_indices[i]); _writeCycleNumber(_attachment_cycle_numbers[i]); } _comma = false; _writeRingCisTrans(); _writeStereogroups(); _writeRadicals(); _writePseudoAtoms(); _writeHighlighting(); if (_comma) _output.writeChar('|'); } } bool SmilesSaver::_shouldWriteAromaticBond (int e_idx) { const Edge &edge = _bmol->getEdge(e_idx); if (_mol == 0) return true; if (!_atoms[edge.beg].lowercase || !_atoms[edge.end].lowercase) return true; if (_bmol->getBondTopology(e_idx) != TOPOLOGY_RING) return true; // We need to check that the bond belongs to some aromatic ring // with all lowercase atoms. That is done for not to miss aromatic // bonds that belong (oddly enough) to some aliphatic SSSR ring // and do not belong to any aromatic SSSR ring if (_aromatic_bonds.size() == 0) { // enumerate SSSR rings CycleBasis basis; basis.create(*_bmol); _aromatic_bonds.clear_resize(_bmol->edgeEnd()); _aromatic_bonds.zerofill(); for (int i = 0; i < basis.getCyclesCount(); i++) { const Array<int> &cycle = basis.getCycle(i); int j; for (j = 0; j < cycle.size(); j++) { int idx = cycle[j]; const Edge &edge = _bmol->getEdge(idx); if (!_atoms[edge.beg].lowercase || !_atoms[edge.end].lowercase) break; if (_mol->getBondOrder(idx) != BOND_AROMATIC) break; } if (j == cycle.size()) // all-lowercase aromatic ring { for (j = 0; j < cycle.size(); j++) _aromatic_bonds[cycle[j]] = 1; } } } if (_aromatic_bonds[e_idx] != 0) return false; return true; } void SmilesSaver::_writeCycleNumber (int n) const { if (n > 0 && n < 10) _output.printf("%d", n); else if (n >= 10 && n < 100) _output.printf("%%%2d", n); else throw Error("bad cycle number: %d", n); } void SmilesSaver::_writeAtom (int idx, bool aromatic, bool lowercase, int chirality) const { int i; bool need_brackets = false; int hydro = -1; int aam = 0; if (_bmol->isRSite(idx)) { if (rsite_indices_as_aam && _bmol->getRSiteBits(idx) != 0) _output.printf("[*:%d]", _bmol->getSingleAllowedRGroup(idx)); else _output.printf("[*]"); return; } int atom_number = _bmol->getAtomNumber(idx); int charge = _bmol->getAtomCharge(idx); int isotope = _bmol->getAtomIsotope(idx); if (charge == CHARGE_UNKNOWN) charge = 0; if (_bmol->isPseudoAtom(idx)) // pseudo-atom { _output.printf("[*"); _writeChirality(chirality); _writeCharge(charge); _output.printf("]"); return; } if (atom_number < 1) { if (_qmol != 0) { // Check for !H meaning any atom in SMILES int value; if (_qmol->getAtom(idx).sureValueInv(QueryMolecule::ATOM_NUMBER, value)) { if (value == ELEM_H) { _output.printf("[*"); _writeChirality(chirality); _writeCharge(charge); _output.printf("]"); return; } } // Check atom list QS_DEF(Array<int>, list); bool not_list; if (QueryMolecule::collectAtomList(_qmol->getAtom(idx), list, not_list) && !not_list) { if (list.size() < 1) throw Error("atom list size is zero"); _output.printf("["); for (int j = 0; j < list.size(); j++) { const char *str = Element::toString(list[j]); if (j != 0) _output.printf(","); _output.printf("#%d", list[j]); } _output.printf("]"); return; } } throw Error("undefined atom number"); } if (atom_atom_mapping != 0) aam = atom_atom_mapping[idx]; if (atom_number != ELEM_C && atom_number != ELEM_P && atom_number != ELEM_N && atom_number != ELEM_S && atom_number != ELEM_O && atom_number != ELEM_Cl && atom_number != ELEM_F && atom_number != ELEM_Br && atom_number != ELEM_B && atom_number != ELEM_I) need_brackets = true; if (chirality > 0 || charge != 0 || isotope > 0 || aam > 0) need_brackets = true; // Ignored hydrogens will be converted to implicit hydrogens. // So number of ignored hydrogens should be passed into // shouldWriteHCount to save correctly [H]S([H])([H])C // as C[SH3] when hydrogens are ignored (as in canonical smiles) // instead of getting CS. if (_mol != 0) { if (Molecule::shouldWriteHCountEx(*_mol, idx, _hcount_ignored[idx])) { hydro = _hcount[idx]; if (hydro < 0 && !ignore_invalid_hcount && need_brackets) { // This function will throw better error message with a description _mol->getImplicitH(idx); // If not error was thrown then throw it explicitly throw Error("unsure hydrogen count on atom #%d", idx); } } } else if (_qmol != 0) { // For query molecules write hydrogens if is was specified hydro = _hcount[idx]; } if (_qmol != 0) _qmol->getAtom(idx).sureValue(QueryMolecule::ATOM_TOTAL_H, hydro); if (hydro >= 0) need_brackets = true; if (need_brackets) { // Check sure number of hydrogens only for the ordinary molecule if (hydro == -1 && _mol != 0) { hydro = _hcount[idx]; if (hydro < 0 && !ignore_invalid_hcount) throw Error("unsure hydrogen count on atom #%d", idx); } _output.writeChar('['); } if (isotope > 0) _output.printf("%d", isotope); const char *elem = Element::toString(atom_number); if (lowercase) { for (i = 0; i < (int)strlen(elem); i++) _output.printf("%c", tolower(elem[i])); } else _output.printf("%s", elem); _writeChirality(chirality); if (hydro > 1) _output.printf("H%d", hydro); else if (hydro == 1) _output.printf("H"); _writeCharge(charge); if (aam > 0) _output.printf(":%d", aam); if (need_brackets) _output.writeChar(']'); } void SmilesSaver::_writeChirality(int chirality) const { if (chirality > 0) { if (chirality == 1) _output.printf("@"); else // chirality == 2 _output.printf("@@"); } } void SmilesSaver::_writeCharge(int charge) const { if (charge > 1) _output.printf("+%d", charge); else if (charge < -1) _output.printf("-%d", -charge); else if (charge == 1) _output.printf("+"); else if (charge == -1) _output.printf("-"); } void SmilesSaver::_writeSmartsAtom (int idx, QueryMolecule::Atom *atom, int chirality, int depth, bool has_or_parent) const { int i; if (depth == 0) _output.printf("["); switch (atom->type) { case QueryMolecule::OP_NOT: { _output.writeChar('!'); _writeSmartsAtom(idx, (QueryMolecule::Atom *)atom->children[0], chirality, depth + 1, has_or_parent); break; } case QueryMolecule::OP_AND: { for (i = 0; i < atom->children.size(); i++) { if (i > 0) _output.writeChar(has_or_parent ? '&' : ';'); _writeSmartsAtom(idx, (QueryMolecule::Atom *)atom->children[i], chirality, depth + 1, has_or_parent); } break; } case QueryMolecule::OP_OR: { for (i = 0; i < atom->children.size(); i++) { if (i > 0) _output.printf(","); _writeSmartsAtom(idx, (QueryMolecule::Atom *)atom->children[i], chirality, depth + 1, true); } break; } case QueryMolecule::ATOM_ISOTOPE: _output.printf("%d", atom->value_max); break; case QueryMolecule::ATOM_NUMBER: { _output.printf("#%d", atom->value_max); if (chirality == 1) _output.printf("@"); else if (chirality == 2) _output.printf("@@"); if (chirality > 0 || _bmol->getAtomRadical_NoThrow(idx, 0) > 0) { int hydro = _bmol->getAtomTotalH(idx); if (hydro > 1) _output.printf("H%d", hydro); else if (hydro == 1) _output.printf("H"); } if (atom_atom_mapping != 0) { int aam = atom_atom_mapping[idx]; if (aam > 0) _output.printf(":%d", aam); } break; } case QueryMolecule::ATOM_CHARGE: { int charge = atom->value_max; if (charge > 1) _output.printf("+%d", charge); else if (charge < -1) _output.printf("-%d", -charge); else if (charge == 1) _output.printf("+"); else if (charge == -1) _output.printf("-"); break; } case QueryMolecule::ATOM_FRAGMENT: { if (atom->fragment->fragment_smarts.ptr() == 0) throw Error("fragment_smarts has unexpectedly gone"); _output.printf("$(%s)", atom->fragment->fragment_smarts.ptr()); break; } case QueryMolecule::ATOM_AROMATICITY: { if (atom->value_min == ATOM_AROMATIC) _output.printf("a"); else _output.printf("A"); break; } case QueryMolecule::OP_NONE: _output.writeChar('*'); break; case QueryMolecule::ATOM_TOTAL_H: { int hydro = atom->value_min; if (hydro > 1) _output.printf("H%d", hydro); else if (hydro == 1) _output.printf("H"); break; } default: ; } if (depth == 0) _output.writeChar(']'); } void SmilesSaver::_writeSmartsBond (int idx, QueryMolecule::Bond *bond) const { int i; switch (bond->type) { case QueryMolecule::OP_NONE: _output.writeChar('~'); break; case QueryMolecule::OP_NOT: { _output.writeChar('!'); _writeSmartsBond(idx, (QueryMolecule::Bond *)bond->children[0]); break; } case QueryMolecule::OP_OR: { for (i = 0; i < bond->children.size(); i++) { if (i > 0) _output.printf(","); _writeSmartsBond(idx, (QueryMolecule::Bond *)bond->children[i]); } break; } case QueryMolecule::BOND_ORDER: { int bond_order = bond->value; if (bond_order == BOND_SINGLE) _output.writeChar('-'); if (bond_order == BOND_DOUBLE) _output.writeChar('='); else if (bond_order == BOND_TRIPLE) _output.writeChar('#'); else if (bond_order == BOND_AROMATIC) _output.writeChar(':'); break; } default: ; } } void SmilesSaver::_banSlashes () { QS_DEF(Array<int>, slashes); BaseMolecule &mol = *_bmol; int i, j; slashes.clear_resize(mol.edgeEnd()); slashes.zerofill(); // mark bonds that are are about to be written as slashes for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { // those are only chain cis-trans bonds if (_cis_trans_parity[i] != 0) { const Vertex &beg = mol.getVertex(mol.getEdge(i).beg); const Vertex &end = mol.getVertex(mol.getEdge(i).end); if (mol.getEdgeTopology(i) == TOPOLOGY_CHAIN) { for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) if (mol.getBondOrder(beg.neiEdge(j)) == BOND_SINGLE) slashes[beg.neiEdge(j)] = 1; for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) if (mol.getBondOrder(end.neiEdge(j)) == BOND_SINGLE) slashes[end.neiEdge(j)] = 1; } else { // Do not write slashes in rings till it is not fixed for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) if (mol.getBondOrder(beg.neiEdge(j)) == BOND_SINGLE) _ban_slashes[beg.neiEdge(j)] = 1; for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) if (mol.getBondOrder(end.neiEdge(j)) == BOND_SINGLE) _ban_slashes[end.neiEdge(j)] = 1; } } } // find if any "potentially but not actively cis-trans" or ring cis-trans bond will be affected // by the slashes for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { if (!mol.cis_trans.isGeomStereoBond(mol, i, 0, false)) continue; if (mol.getEdgeTopology(i) == TOPOLOGY_RING || _cis_trans_parity[i] == 0) { const Vertex &beg = mol.getVertex(mol.getEdge(i).beg); const Vertex &end = mol.getVertex(mol.getEdge(i).end); bool slash_beg = false; bool slash_end = false; for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) if (slashes[beg.neiEdge(j)]) { slash_beg = true; break; } for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) if (slashes[end.neiEdge(j)]) { slash_end = true; break; } if (!slash_beg || !slash_end) // the bond will not be affected by slashes (at lest, not both sides of it) continue; for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) if (slashes[beg.neiEdge(j)]) _ban_slashes[beg.neiEdge(j)] = 1; for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) if (slashes[end.neiEdge(j)]) _ban_slashes[end.neiEdge(j)] = 1; } } } void SmilesSaver::_filterCisTransParity () { BaseMolecule &mol = *_bmol; for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); if (mol.cis_trans.getParity(i) != 0) { const Vertex &beg = mol.getVertex(edge.beg); const Vertex &end = mol.getVertex(edge.end); // Aromatic bonds can not be cis or trans. That is a limitation of the SMILES syntax. // Structures like N1\C=C/N\C=C/C=C/C=C\C=C\1, when aromatized, // become SMILES like c1cccc[nH]cc[nH]ccc1, and the cis-trans info is lost. // Rare structures like C1C=NC2=C1C=C(S2=CC3=CC=CN3)C(=O)[O-] (CID 17932462), // which have cis-trans double bond attached to an aromatic ring, lose the // cis-trans information too (although Marvin works that around saving to // N#Cc1cc2CC=Nc2\s1=C/c1ccc[nH]1). bool have_singlebond_beg = false, have_singlebond_end = false; bool have_allowed_singlebond_beg = false, have_allowed_singlebond_end = false; for (int j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) { if (_ignored_vertices[beg.neiVertex(j)]) continue; int idx = beg.neiEdge(j); if (idx != i && mol.getBondOrder(idx) == BOND_SINGLE) have_singlebond_beg = true; } for (int j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) { if (_ignored_vertices[end.neiVertex(j)]) continue; int idx = end.neiEdge(j); if (idx != i && mol.getBondOrder(idx) == BOND_SINGLE) have_singlebond_end = true; } if (!have_singlebond_beg || !have_singlebond_end) continue; if (mol.getBondTopology(i) == TOPOLOGY_RING) { // But cis-trans bonds can have only rings with size more then 7: // C1=C\C=C/C=C/C=C\1 // C1=CC=CC=CC=C1 // C1=C/C=C\C=C/C=C\1 if (mol.edgeSmallestRingSize(i) <= 7) continue; } // This bond will be saved as cis-trans _cis_trans_parity[i] = mol.cis_trans.getParity(i); } } } void SmilesSaver::_markCisTrans () { BaseMolecule &mol = *_bmol; int i, j; _dbonds.clear_resize(mol.edgeEnd()); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { _dbonds[i].ctbond_beg = -1; _dbonds[i].ctbond_end = -1; _dbonds[i].saved = 0; } _filterCisTransParity(); if (!mol.cis_trans.exists()) return; _banSlashes(); for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge &edge = mol.getEdge(i); if (_cis_trans_parity[i] != 0) { // This cis-trans bond has already been filtered in _filterCisTransParity // and we have to write cis-trans infromation for this bond const Vertex &beg = mol.getVertex(edge.beg); const Vertex &end = mol.getVertex(edge.end); bool have_allowed_singlebond_beg = false, have_allowed_singlebond_end = false; for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) { if (_ignored_vertices[beg.neiVertex(j)]) continue; int idx = beg.neiEdge(j); if (idx != i && mol.getBondOrder(idx) == BOND_SINGLE) { if (!_ban_slashes[idx]) have_allowed_singlebond_beg = true; } } for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) { if (_ignored_vertices[end.neiVertex(j)]) continue; int idx = end.neiEdge(j); if (idx != i && mol.getBondOrder(idx) == BOND_SINGLE) { if (!_ban_slashes[idx]) have_allowed_singlebond_end = true; } } if (!have_allowed_singlebond_beg || !have_allowed_singlebond_end) { _complicated_cistrans[i] = 1; _have_complicated_cistrans = true; continue; } for (j = beg.neiBegin(); j != beg.neiEnd(); j = beg.neiNext(j)) { if (_ignored_vertices[beg.neiVertex(j)]) continue; int idx = beg.neiEdge(j); if (idx != i) { if (mol.getEdge(idx).beg == edge.beg) _dbonds[idx].ctbond_beg = i; else _dbonds[idx].ctbond_end = i; } } for (j = end.neiBegin(); j != end.neiEnd(); j = end.neiNext(j)) { if (_ignored_vertices[end.neiVertex(j)]) continue; int idx = end.neiEdge(j); if (idx != i) { if (mol.getEdge(idx).beg == edge.end) _dbonds[idx].ctbond_beg = i; else _dbonds[idx].ctbond_end = i; } } } } } bool SmilesSaver::_updateSideBonds (int bond_idx) { BaseMolecule &mol = *_bmol; const Edge &edge = mol.getEdge(bond_idx); int subst[4]; mol.cis_trans.getSubstituents_All(bond_idx, subst); int parity = _cis_trans_parity[bond_idx]; int sidebonds[4] = {-1, -1, -1, -1}; sidebonds[0] = mol.findEdgeIndex(subst[0], edge.beg); if (subst[1] != -1) sidebonds[1] = mol.findEdgeIndex(subst[1], edge.beg); sidebonds[2] = mol.findEdgeIndex(subst[2], edge.end); if (subst[3] != -1) sidebonds[3] = mol.findEdgeIndex(subst[3], edge.end); int n1 = 0, n2 = 0, n3 = 0, n4 = 0; if (_dbonds[sidebonds[0]].saved != 0) { if ((_dbonds[sidebonds[0]].saved == 1 && mol.getEdge(sidebonds[0]).beg == edge.beg) || (_dbonds[sidebonds[0]].saved == 2 && mol.getEdge(sidebonds[0]).end == edge.beg)) n1++; else n2++; } if (sidebonds[1] != -1 && _dbonds[sidebonds[1]].saved != 0) { if ((_dbonds[sidebonds[1]].saved == 2 && mol.getEdge(sidebonds[1]).beg == edge.beg) || (_dbonds[sidebonds[1]].saved == 1 && mol.getEdge(sidebonds[1]).end == edge.beg)) n1++; else n2++; } if (_dbonds[sidebonds[2]].saved != 0) { if ((_dbonds[sidebonds[2]].saved == 1 && mol.getEdge(sidebonds[2]).beg == edge.end) || (_dbonds[sidebonds[2]].saved == 2 && mol.getEdge(sidebonds[2]).end == edge.end)) n3++; else n4++; } if (sidebonds[3] != -1 && _dbonds[sidebonds[3]].saved != 0) { if ((_dbonds[sidebonds[3]].saved == 2 && mol.getEdge(sidebonds[3]).beg == edge.end) || (_dbonds[sidebonds[3]].saved == 1 && mol.getEdge(sidebonds[3]).end == edge.end)) n3++; else n4++; } if (parity == MoleculeCisTrans::CIS) { n1 += n3; n2 += n4; } else { n1 += n4; n2 += n3; } if (n1 > 0 && n2 > 0) throw Error("incompatible cis-trans configuration"); if (n1 == 0 && n2 == 0) return false; if (n1 > 0) { _dbonds[sidebonds[0]].saved = (mol.getEdge(sidebonds[0]).beg == edge.beg) ? 1 : 2; if (sidebonds[1] != -1) _dbonds[sidebonds[1]].saved = (mol.getEdge(sidebonds[1]).beg == edge.beg) ? 2 : 1; _dbonds[sidebonds[2]].saved = ((mol.getEdge(sidebonds[2]).beg == edge.end) == (parity == MoleculeCisTrans::CIS)) ? 1 : 2; if (sidebonds[3] != -1) _dbonds[sidebonds[3]].saved = ((mol.getEdge(sidebonds[3]).beg == edge.end) == (parity == MoleculeCisTrans::CIS)) ? 2 : 1; } if (n2 > 0) { _dbonds[sidebonds[0]].saved = (mol.getEdge(sidebonds[0]).beg == edge.beg) ? 2 : 1; if (sidebonds[1] != -1) _dbonds[sidebonds[1]].saved = (mol.getEdge(sidebonds[1]).beg == edge.beg) ? 1 : 2; _dbonds[sidebonds[2]].saved = ((mol.getEdge(sidebonds[2]).beg == edge.end) == (parity == MoleculeCisTrans::CIS)) ? 2 : 1; if (sidebonds[3] != -1) _dbonds[sidebonds[3]].saved = ((mol.getEdge(sidebonds[3]).beg == edge.end) == (parity == MoleculeCisTrans::CIS)) ? 1 : 2; } return true; } int SmilesSaver::_calcBondDirection (int idx, int vprev) { BaseMolecule &mol = *_bmol; int i, ntouched; int ctbond_beg = _dbonds[idx].ctbond_beg; int ctbond_end = _dbonds[idx].ctbond_end; if (ctbond_beg == -1 && ctbond_end == -1) return 0; if (mol.getBondOrder(idx) != BOND_SINGLE) throw Error("internal: directed bond order %d", mol.getBondOrder(idx)); while (1) { ntouched = 0; for (i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) if (_cis_trans_parity[i] != 0 && mol.getEdgeTopology(i) == TOPOLOGY_CHAIN) { if (_updateSideBonds(i)) ntouched++; } if (ntouched == _touched_cistransbonds) break; _touched_cistransbonds = ntouched; } if (_dbonds[idx].saved == 0) { if (vprev == mol.getEdge(idx).beg) _dbonds[idx].saved = 1; else _dbonds[idx].saved = 2; } return _dbonds[idx].saved; } void SmilesSaver::_startExtension () { if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } } void SmilesSaver::_writeStereogroups () { BaseMolecule &mol = *_bmol; MoleculeStereocenters &stereocenters = mol.stereocenters; int i, j; for (i = stereocenters.begin(); i != stereocenters.end(); i = stereocenters.next(i)) { int atom, type, group; stereocenters.get(i, atom, type, group, 0); if (type != MoleculeStereocenters::ATOM_ABS) break; } if (i == stereocenters.end()) return; int and_group_idx = 1; int or_group_idx = 1; QS_DEF(Array<int>, marked); marked.clear_resize(_written_atoms.size()); marked.zerofill(); for (i = 0; i < _written_atoms.size(); i++) { if (marked[i]) continue; int type = stereocenters.getType(_written_atoms[i]); if (type == MoleculeStereocenters::ATOM_ANY) { _startExtension(); _output.printf("w:%d", i); for (j = i + 1; j < _written_atoms.size(); j++) if (stereocenters.getType(_written_atoms[j]) == MoleculeStereocenters::ATOM_ANY) { marked[j] = 1; _output.printf(",%d", j); } } else { if (type == MoleculeStereocenters::ATOM_ABS) { _startExtension(); _output.printf("a:%d", i); for (j = i + 1; j < _written_atoms.size(); j++) if (stereocenters.getType(_written_atoms[j]) == MoleculeStereocenters::ATOM_ABS) { marked[j] = 1; _output.printf(",%d", j); } } else if (type == MoleculeStereocenters::ATOM_AND) { int group = stereocenters.getGroup(_written_atoms[i]); _startExtension(); _output.printf("&%d:%d", and_group_idx++, i); for (j = i + 1; j < _written_atoms.size(); j++) if (stereocenters.getType(_written_atoms[j]) == MoleculeStereocenters::ATOM_AND && stereocenters.getGroup(_written_atoms[j]) == group) { marked[j] = 1; _output.printf(",%d", j); } } else if (type == MoleculeStereocenters::ATOM_OR) { int group = stereocenters.getGroup(_written_atoms[i]); _startExtension(); _output.printf("o%d:%d", or_group_idx++, i); for (j = i + 1; j < _written_atoms.size(); j++) if (stereocenters.getType(_written_atoms[j]) == MoleculeStereocenters::ATOM_OR && stereocenters.getGroup(_written_atoms[j]) == group) { marked[j] = 1; _output.printf(",%d", j); } } } } } void SmilesSaver::_writeRadicals () { BaseMolecule &mol = *_bmol; QS_DEF(Array<int>, marked); int i, j; marked.clear_resize(_written_atoms.size()); marked.zerofill(); for (i = 0; i < _written_atoms.size(); i++) { if (marked[i] || mol.isRSite(_written_atoms[i]) || mol.isPseudoAtom(_written_atoms[i])) continue; int radical = mol.getAtomRadical_NoThrow(_written_atoms[i], 0); if (radical <= 0) continue; _startExtension(); if (radical == RADICAL_SINGLET) _output.writeString("^3:"); else if (radical == RADICAL_DOUBLET) _output.writeString("^1:"); else // RADICAL_TRIPLET _output.writeString("^4:"); _output.printf("%d", i); for (j = i + 1; j < _written_atoms.size(); j++) { if (mol.isPseudoAtom(_written_atoms[j]) || mol.isRSite(_written_atoms[j])) continue; if (mol.getAtomRadical_NoThrow(_written_atoms[j], 0) == radical) { marked[j] = 1; _output.printf(",%d", j); } } } } void SmilesSaver::_writePseudoAtoms () { BaseMolecule &mol = *_bmol; int i; if (_attachment_indices.size() == 0) { for (i = 0; i < _written_atoms.size(); i++) { if (mol.isPseudoAtom(_written_atoms[i]) || (mol.isRSite(_written_atoms[i]) && mol.getRSiteBits(_written_atoms[i]) != 0)) break; } if (i == _written_atoms.size()) return; } _startExtension(); _output.writeChar('$'); for (i = 0; i < _written_atoms.size(); i++) { if (i > 0) _output.writeChar(';'); if (mol.isPseudoAtom(_written_atoms[i])) writePseudoAtom(mol.getPseudoAtom(_written_atoms[i]), _output); else if (mol.isRSite(_written_atoms[i]) && mol.getRSiteBits(_written_atoms[i]) != 0) // ChemAxon's Extended SMILES notation for R-sites _output.printf("_R%d", mol.getSingleAllowedRGroup(_written_atoms[i])); } for (i = 0; i < _attachment_indices.size(); i++) // ChemAxon's Extended SMILES notation for attachment points _output.printf(";_AP%d", _attachment_indices[i]); _output.writeChar('$'); } void SmilesSaver::writePseudoAtom (const char *label, Output &out) { if (*label == 0) throw Error("empty pseudo-atom"); do { if (*label == '\n' || *label == '\r' || *label == '\t') throw Error("character 0x%x is not allowed inside pseudo-atom", *label); if (*label == '$' || *label == ';') throw Error("'%c' not allowed inside pseudo-atom", *label); out.writeChar(*label); } while (*(++label) != 0); } void SmilesSaver::_writeHighlighting () { if (!_bmol->hasHighlighting()) return; int i; bool ha = false; for (i = 0; i < _written_atoms.size(); i++) { if (_bmol->isAtomHighlighted(_written_atoms[i])) { if (ha) _output.writeChar(','); else { _startExtension(); _output.writeString("ha:"); ha = true; } _output.printf("%d", i); } } bool hb = false; for (i = 0; i < _written_bonds.size(); i++) { if (_bmol->isBondHighlighted(_written_bonds[i])) { if (hb) _output.writeChar(','); else { _startExtension(); _output.writeString("hb:"); hb = true; } _output.printf("%d", i); } } } int SmilesSaver::writtenComponents () { return _written_components; } const Array<int> & SmilesSaver::writtenAtoms () { return _written_atoms; } const Array<int> & SmilesSaver::writtenBonds () { return _written_bonds; } SmilesSaver::_Atom::_Atom (Pool<List<int>::Elem> &neipool) : neighbors(neipool) { aromatic = 0; lowercase = false; chirality = 0; branch_cnt = 0; paren_written = false; starts_polymer = false; ends_polymer = false; parent = -1; } SmilesSaver::_Atom::~_Atom () { } void SmilesSaver::_checkSRU () { _polymer_indices.clear_resize(_bmol->vertexEnd()); _polymer_indices.fffill(); int i, j, k; // check overlapping (particularly nested) blocks for (i = _bmol->sgroups.begin(); i != _bmol->sgroups.end(); i = _bmol->sgroups.next(i)) { SGroup *sg = &_bmol->sgroups.getSGroup(i); if (sg->sgroup_type == SGroup::SG_TYPE_SRU) { Array<int> &atoms = sg->atoms; for (j = 0; j < atoms.size(); j++) { if (_polymer_indices[atoms[j]] >= 0) throw Error("overlapping (nested?) repeating units can not be saved"); _polymer_indices[atoms[j]] = i; // check also disconnected blocks (possible to handle, but unsupported at the moment) if (_bmol->vertexComponent(atoms[j]) != _bmol->vertexComponent(atoms[0])) throw Error("disconnected repeating units not supported"); } } } // check that each block has exactly two outgoing bonds for (i = _bmol->sgroups.begin(); i != _bmol->sgroups.end(); i = _bmol->sgroups.next(i)) { SGroup *sg = &_bmol->sgroups.getSGroup(i); if (sg->sgroup_type == SGroup::SG_TYPE_SRU) { Array<int> &atoms = sg->atoms; int cnt = 0; for (j = 0; j < atoms.size(); j++) { const Vertex &vertex = _bmol->getVertex(atoms[j]); for (k = vertex.neiBegin(); k != vertex.neiEnd(); k = vertex.neiNext(k)) if (_polymer_indices[vertex.neiVertex(k)] != i) cnt++; } if (cnt != 2) throw Error("repeating units must have exactly two outgoing bonds, has %d", cnt); } } } int SmilesSaver::_countRBonds () { int i, sum = 0; for (i = _bmol->vertexBegin(); i != _bmol->vertexEnd(); i = _bmol->vertexNext(i)) if (_bmol->isRSite(i)) sum += _bmol->getVertex(i).degree(); return sum; } void SmilesSaver::_checkRGroupsAndAttachmentPoints () { _attachment_indices.clear(); _attachment_cycle_numbers.clear(); _n_attachment_points = 0; for (int i = 1; i <= _bmol->attachmentPointCount(); i++) for (int idx = 0; _bmol->getAttachmentPoint(i, idx) != -1; idx++) _n_attachment_points++; if (_n_attachment_points && !write_extra_info) throw Error("can not write attachment points without permission to write " "the Extended SMILES block (probably because you are saving reaction SMILES)"); } void SmilesSaver::_writeRingCisTrans () { if (!_have_complicated_cistrans) return; _startExtension(); int i, j; QS_DEF(Array<int>, cis_bonds); QS_DEF(Array<int>, trans_bonds); cis_bonds.clear(); trans_bonds.clear(); for (i = 0; i < _written_bonds.size(); i++) { int bond_idx = _written_bonds[i]; if (!_complicated_cistrans[bond_idx]) continue; int nei_atom_beg = -1, nei_atom_end = -1; const Edge &edge = _bmol->getEdge(bond_idx); // TODO: a more effective looping is possible for (j = 0; j < _written_atoms.size(); j++) { if (nei_atom_beg == -1) if (_written_atoms[j] != edge.end && _bmol->findEdgeIndex(_written_atoms[j], edge.beg) >= 0) nei_atom_beg = _written_atoms[j]; if (nei_atom_end == -1) if (_written_atoms[j] != edge.beg && _bmol->findEdgeIndex(_written_atoms[j], edge.end) >= 0) nei_atom_end = _written_atoms[j]; } if (nei_atom_beg == -1 || nei_atom_end == -1) throw Error("_writeRingCisTrans(): internal"); int parity = _cis_trans_parity[bond_idx]; if (parity == 0 && _mol != 0 && _mol->getAtomRingBondsCount(edge.beg) == 2 && _mol->getAtomRingBondsCount(edge.end) == 2) { // That is some "simple" cis-trans bond, i.e. in a benzene ring. // It came unspecified, but we better write it explicitly, not to // produce different canonical SMILES for identical structures. // We write is as "cis" (if both substituents are in a ring), // or "trans" if one substituent is in a ring and another is not. parity = MoleculeCisTrans::CIS; if (_bmol->getEdgeTopology(_bmol->findEdgeIndex(nei_atom_beg, edge.beg)) == TOPOLOGY_CHAIN) parity = 3 - parity; if (_bmol->getEdgeTopology(_bmol->findEdgeIndex(nei_atom_end, edge.end)) == TOPOLOGY_CHAIN) parity = 3 - parity; } else { const int *subst = _bmol->cis_trans.getSubstituents(bond_idx); if (nei_atom_beg == subst[0]) ; else if (nei_atom_beg == subst[1]) parity = 3 - parity; else throw Error("_writeRingCisTrans(): internal (substituent not found)"); if (nei_atom_end == subst[2]) ; else if (nei_atom_end == subst[3]) parity = 3 - parity; else throw Error("_writeRingCisTrans(): internal (substituent not found)"); } if (parity == MoleculeCisTrans::CIS) cis_bonds.push(i); else trans_bonds.push(i); } if (cis_bonds.size() != 0) { _output.printf("c:%d", cis_bonds[0]); for (i = 1; i < cis_bonds.size(); i++) _output.printf(",%d", cis_bonds[i]); } if (trans_bonds.size() != 0) { if (cis_bonds.size() != 0) _output.printf(","); _output.printf("t:%d", trans_bonds[0]); for (i = 1; i < trans_bonds.size(); i++) _output.printf(",%d", trans_bonds[i]); } } const Array<int>& SmilesSaver::getSavedCisTransParities () { return _cis_trans_parity; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016005�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/CMakeLists.txt���������������������������������������������������������0000664�0000000�0000000�00000000523�12710376503�0020545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROJECT(Reaction) file (GLOB Reaction_src src/*.c*) file (GLOB Reaction_headers *.h) include_directories(${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/.. ${ZLib_HEADERS_DIR} ${TinyXML_HEADERS_DIR}) add_library(reaction OBJECT ${Reaction_src} ${Reaction_headers}) set_target_properties(reaction PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/base_reaction.h��������������������������������������������������������0000664�0000000�0000000�00000012155�12710376503�0020760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __base_reaction_h__ #define __base_reaction_h__ #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif #include "molecule/base_molecule.h" #include "base_cpp/obj_array.h" #include "base_cpp/ptr_pool.h" #include "base_cpp/auto_iter.h" #include "base_cpp/non_copyable.h" namespace indigo { class Reaction; class QueryReaction; class BaseReaction; class SideIter : public AutoIterator { public: DECL_ERROR; SideIter(BaseReaction &owner, int idx, int side); SideIter & operator++ (); private: BaseReaction &_owner; int _side; }; class SideAuto { public: SideAuto(BaseReaction &owner, int side); SideIter begin(); SideIter end(); private: BaseReaction &_owner; int _side; }; class DLLEXPORT BaseReaction : public NonCopyable { public: enum { REACTANT = 1, PRODUCT = 2, CATALYST = 4 }; BaseReaction(); virtual ~BaseReaction (); // 'neu' means 'new' in German virtual BaseReaction * neu () = 0; int begin (); int end (); int next (int i); int count (); void remove (int i); int reactantBegin() { return _nextElement(REACTANT, -1); } int reactantNext(int index) { return _nextElement(REACTANT, index); } int reactantEnd() { return _allMolecules.end(); } int productBegin() { return _nextElement(PRODUCT, -1); } int productNext(int index) { return _nextElement(PRODUCT, index); } int productEnd() { return _allMolecules.end(); } int catalystBegin() { return _nextElement(CATALYST, -1); } int catalystNext(int index) { return _nextElement(CATALYST, index); } int catalystEnd() { return _allMolecules.end(); } int sideBegin (int side) { return _nextElement(side, -1); } int sideNext (int side, int index) { return _nextElement(side, index); } // dkuzminov: we either need to have a parameter "side" for method sideEnd() or we should exclude the set of "different" xxxEnd methods for sake of the single "end" method int sideEnd () { return _allMolecules.end(); } int getSideType(int index) {return _types[index]; } int reactantsCount() const { return _reactantCount; } int productsCount() const { return _productCount; } int catalystCount() const { return _catalystCount; } SideAuto reactants; SideAuto catalysts; SideAuto products; virtual void clear(); // Returns true if some bonds were changed virtual bool aromatize (const AromaticityOptions &options) = 0; // Returns true if all bonds were dearomatized virtual bool dearomatize (const AromaticityOptions &options) = 0; // poor man's dynamic casting virtual Reaction & asReaction (); virtual QueryReaction & asQueryReaction (); virtual bool isQueryReaction (); BaseMolecule & getBaseMolecule(int index) { return *_allMolecules.at(index); } int getAAM(int index, int atom); int getReactingCenter(int index, int bond); int getInversion (int index, int atom); Array<int> & getAAMArray(int index); Array<int> & getReactingCenterArray(int index); Array<int> & getInversionArray (int index); void clearAAM (); int addReactant (); int addProduct (); int addCatalyst (); int addReactantCopy (BaseMolecule &mol, Array<int>* mapping, Array<int> *inv_mapping); int addProductCopy (BaseMolecule &mol, Array<int>* mapping, Array<int> *inv_mapping); int addCatalystCopy (BaseMolecule &mol, Array<int>* mapping, Array<int> *inv_mapping); int findAtomByAAM (int mol_idx, int aam); int findAamNumber (BaseMolecule *mol, int atom_number); int findReactingCenter (BaseMolecule *mol, int bond_number); int findMolecule (BaseMolecule *mol); void markStereocenterBonds(); static bool haveCoord (BaseReaction &reaction); void clone (BaseReaction &other, Array<int> *mol_mapping, ObjArray< Array<int> >* mappings, ObjArray< Array<int> >* inv_mappings); Array<char> name; DECL_ERROR; protected: virtual int _addBaseMolecule (int side) = 0; virtual void _addedBaseMolecule (int idx, int side, BaseMolecule &mol); PtrPool<BaseMolecule> _allMolecules; ObjArray< Array<int> > _atomAtomMapping; ObjArray< Array<int> > _reactingCenters; ObjArray< Array<int> > _inversionNumbers; Array<int> _types; int _reactantCount; int _productCount; int _catalystCount; int _nextElement(int type, int index); virtual void _clone (BaseReaction &other, int index, int i, ObjArray< Array<int> >* mol_mappings); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/base_reaction_substructure_matcher.h�����������������������������������0000664�0000000�0000000�00000011600�12710376503�0025307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __base_reaction__substructure_matcher__ #define __base_reaction__substructure_matcher__ #include "base_cpp/red_black.h" #include "base_cpp/tlscont.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/obj.h" #include "graph/embedding_enumerator.h" #include "molecule/molecule_arom_match.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Reaction; class ReactionAtomNeighbourhoodCounters; class BaseReaction; class Graph; class BaseMolecule; class Molecule; typedef RedBlackMap<int, int> RedBlackIntMap; class DLLEXPORT BaseReactionSubstructureMatcher { protected: class _Matcher; public: BaseReactionSubstructureMatcher (Reaction &target); void setQuery (BaseReaction &query); void setNeiCounters (const ReactionAtomNeighbourhoodCounters *query_counters, const ReactionAtomNeighbourhoodCounters *target_counters); bool highlight; bool use_aromaticity_matcher; AromaticityOptions arom_options; bool find (); int getTargetMoleculeIndex (int query_molecule_idx); const int * getQueryMoleculeMapping (int query_mol_idx); DECL_ERROR; bool (*match_atoms) (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, void *context); bool (*match_bonds) (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, AromaticityMatcher *am, void *context); void (*remove_atom) (BaseMolecule &submol, int sub_idx, AromaticityMatcher *am); void (*add_bond) (BaseMolecule &submol, Molecule &supermol, int sub_idx, int super_idx, AromaticityMatcher *am); bool (*prepare) (BaseReaction &query_, Reaction &target, void *context); bool (*prepare_ee) (EmbeddingEnumerator &ee, BaseMolecule &submol, Molecule &supermol, void *context); void *context; protected: void _initMap (BaseReaction &reaction, int side, RedBlackMap<int, int> &aam_map); virtual bool _checkAAM (); void _highlight (); bool _match_stereo; enum { _FIRST_SIDE, _SECOND_SIDE, _SECOND_SIDE_REST, _CONTINUE, _NO_WAY, _RETURN }; class _Matcher { public: _Matcher (BaseReactionSubstructureMatcher &context); _Matcher (const _Matcher &other); int nextPair (); void setMode (int mode) { _mode = mode; } int getMode () { return _mode; } bool addPair (int mol1_idx, int mol2_idx, const Array<int> &core1, const Array<int> &core2, bool from_first_side); void restore (); bool match_stereo; int _current_molecule_1, _current_molecule_2; CP_DECL; TL_CP_DECL(Array<int>, _current_core_1); TL_CP_DECL(Array<int>, _current_core_2); protected: int _nextPair (); bool _initEnumerator (BaseMolecule &mol_1, Molecule &mol_2); static int _embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata); static bool _matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata); static bool _matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); static void _removeAtom (Graph &subgraph, int sub_idx, void *userdata); static void _addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata); BaseReactionSubstructureMatcher &_context; AutoPtr<AromaticityMatcher> _am; Obj<EmbeddingEnumerator> _enumerator; int _mode; int _selected_molecule_1; int _selected_molecule_2; TL_CP_DECL(Array<int>, _mapped_aams); }; Reaction &_target; CP_DECL; TL_CP_DECL(PtrArray<_Matcher>, _matchers); TL_CP_DECL(RedBlackIntMap, _aam_to_second_side_1); TL_CP_DECL(RedBlackIntMap, _aam_to_second_side_2); TL_CP_DECL(Array<int>, _molecule_core_1); TL_CP_DECL(Array<int>, _molecule_core_2); TL_CP_DECL(RedBlackIntMap, _aam_core_first_side); int _first_side; int _second_side; BaseReaction *_query; const ReactionAtomNeighbourhoodCounters *_query_nei_counters, *_target_nei_counters; }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/canonical_rsmiles_saver.h����������������������������������������������0000664�0000000�0000000�00000002263�12710376503�0023046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __canonical_rsmiles_saver__ #define __canonical_rsmiles_saver__ #include "reaction/rsmiles_saver.h" #include "base_cpp/exception.h" namespace indigo { class Output; class BaseReaction; class CanonicalSmilesSaver; class Reaction; class DLLEXPORT CanonicalRSmilesSaver : public RSmilesSaver { public: explicit CanonicalRSmilesSaver (Output &output); ~CanonicalRSmilesSaver (); void saveReaction(Reaction &react); DECL_ERROR; protected: void _saveReaction(); void _writeMolecule(int i, CanonicalSmilesSaver &saver); }; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/crf_common.h�����������������������������������������������������������0000664�0000000�0000000�00000001440�12710376503�0020277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __crf_common__ #define __crf_common__ namespace indigo { struct CrfFeatureFlags { enum { CRF_AAM = 0x01, CRF_CATALYST = 0x02, }; }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/crf_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000003214�12710376503�0020256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __crf_loader__ #define __crf_loader__ #include "lzw/lzw_decoder.h" #include "base_cpp/obj.h" #include "crf_saver.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Reaction; class DLLEXPORT CrfLoader { public: // external dictionary, internal encoder explicit CrfLoader (LzwDict &dict, Scanner &scanner); // no dictionary, no encoder explicit CrfLoader (Scanner &scanner); void loadReaction (Reaction &reaction); Scanner *xyz_scanner; int version; // By default the latest version 2 is used DECL_ERROR; protected: void _init (); void _loadMolecule (Molecule &molecule); void _loadReactionMolecule (Reaction &reaction, int index, bool have_aam); Scanner &_scanner; Obj<LzwDecoder> _decoder; LzwDict *_dict; Array<int> *_bond_rc_flags; Array<int> *_atom_stereo_flags; Array<int> *_aam; private: CrfLoader (const CrfLoader &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/crf_saver.h������������������������������������������������������������0000664�0000000�0000000�00000003334�12710376503�0020133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __crf_saver__ #define __crf_saver__ #include "lzw/lzw_encoder.h" #include "base_cpp/obj.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Molecule; class Reaction; class LzwDict; class DLLEXPORT CrfSaver { public: // external dictionary, internal encoder explicit CrfSaver (LzwDict &dict, Output &output); // no dictionary, no encoder explicit CrfSaver (Output &output); void saveReaction (Reaction &reaction); Output *xyz_output; bool save_bond_dirs; bool save_highlighting; bool save_mapping; DECL_ERROR; protected: void _init (); void _writeReactionInfo (Reaction &reaction); void _writeAam (const int *aam, const Array<int> &sequence); void _writeMolecule (Molecule &molecule); void _writeReactionMolecule (Reaction &reaction, int idx); Output &_output; Obj<LzwEncoder> _encoder; const int *_atom_stereo_flags; const int *_bond_rc_flags; const int *_aam; private: CrfSaver (const CrfSaver &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/icr_loader.h�����������������������������������������������������������0000664�0000000�0000000�00000002042�12710376503�0020257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __icr_loader__ #define __icr_loader__ #include "base_cpp/exception.h" namespace indigo { class Scanner; class Reaction; class IcrLoader { public: // external dictionary, internal decoder explicit IcrLoader (Scanner &scanner); void loadReaction (Reaction &reaction); DECL_ERROR; protected: Scanner &_scanner; private: IcrLoader (const IcrLoader &); // no implicit copy }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/icr_saver.h������������������������������������������������������������0000664�0000000�0000000�00000002246�12710376503�0020137�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __icr_saver__ #define __icr_saver__ namespace indigo { class Reaction; class Output; #include "base_cpp/exception.h" class IcrSaver { public: static const char *VERSION1, *VERSION2; static bool checkVersion (const char *prefix); explicit IcrSaver (Output &output); void saveReaction (Reaction &reaction); bool save_xyz; bool save_bond_dirs; bool save_highlighting; bool save_ordering; DECL_ERROR; protected: Output &_output; private: IcrSaver (const IcrSaver &); // no implicit copy }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/query_reaction.h�������������������������������������������������������0000664�0000000�0000000�00000004562�12710376503�0021216�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __query_reaction_h__ #define __query_reaction_h__ #include "reaction/base_reaction.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class QueryMolecule; class DLLEXPORT QueryReaction : public BaseReaction { public: QueryReaction (); virtual ~QueryReaction (); virtual void clear (); QueryMolecule & getQueryMolecule (int index); Array<int> & getExactChangeArray (int index); int getExactChange (int index, int atom); void makeTransposedForSubstructure (QueryReaction &other); int _addedQueryMolecule (int side, QueryMolecule &mol); virtual bool aromatize (const AromaticityOptions &options); virtual bool dearomatize (const AromaticityOptions &options); virtual BaseReaction * neu (); virtual QueryReaction & asQueryReaction (); virtual bool isQueryReaction (); Array<int> & getIgnorableAAMArray (int index); int getIgnorableAAM (int index, int atom); void optimize (); protected: void _transposeMoleculeForSubstructure (int index, Array<int> &transposition); virtual int _addBaseMolecule (int side); virtual void _addedBaseMolecule (int idx, int side, BaseMolecule &mol); struct _SortingContext { explicit _SortingContext (QueryMolecule &mol, const Array<int> &r) : m(mol), rdata(r) { } QueryMolecule &m; const Array<int> &rdata; }; static int _compare(int &i1, int &i2, void *c); ObjArray< Array<int> > _exactChanges; ObjArray< Array<int> > _ignorableAAM; virtual void _clone (BaseReaction &other, int index, int i, ObjArray< Array<int> >* mol_mappings); private: QueryReaction (const QueryReaction &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction.h�������������������������������������������������������������0000664�0000000�0000000�00000003700�12710376503�0017762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_h__ #define __reaction_h__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "molecule/molecule.h" #include "reaction/base_reaction.h" namespace indigo { // Stereo changes during reaction enum { STEREO_UNMARKED = 0, STEREO_INVERTS = 1, STEREO_RETAINS = 2 }; // Reacting centers enum { RC_NOT_CENTER = -1, RC_UNMARKED = 0, RC_CENTER = 1, RC_UNCHANGED = 2, RC_MADE_OR_BROKEN = 4, RC_ORDER_CHANGED = 8, RC_TOTAL = 16 }; class DLLEXPORT Reaction : public BaseReaction { public: Reaction (); virtual ~Reaction (); virtual void clear(); virtual BaseReaction * neu (); Molecule & getMolecule (int index); virtual bool aromatize (const AromaticityOptions &options); virtual bool dearomatize (const AromaticityOptions &options); virtual Reaction & asReaction (); /* void dearomatizeBonds(); void aromatizeQueryBonds(); bool isAllConnected() const;*/ static void saveBondOrders (Reaction& reaction, ObjArray< Array<int> > &bond_types); static void loadBondOrders (Reaction& reaction, ObjArray< Array<int> > &bond_types); static void checkForConsistency (Reaction &rxn); void unfoldHydrogens (); DECL_ERROR; protected: virtual int _addBaseMolecule (int side); }; } #endif ����������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_auto_loader.h�������������������������������������������������0000664�0000000�0000000�00000003200�12710376503�0022333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_auto_loader__ #define __reaction_auto_loader__ #include "base_cpp/array.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class BaseReaction; class Reaction; class QueryReaction; class DLLEXPORT ReactionAutoLoader { public: ReactionAutoLoader (Scanner &scanner); ReactionAutoLoader (const Array<char> &arr); ReactionAutoLoader (const char *); ~ReactionAutoLoader (); void loadReaction (Reaction &reaction); void loadQueryReaction (QueryReaction &reaction); bool treat_x_as_pseudoatom; bool ignore_closing_bond_direction_mismatch; StereocentersOptions stereochemistry_options; bool ignore_cistrans_errors; bool ignore_noncritical_query_features; DECL_ERROR; protected: Scanner *_scanner; bool _own_scanner; void _init (); void _loadReaction (BaseReaction &reaction, bool query); bool _isSingleLine (); private: ReactionAutoLoader (const ReactionAutoLoader &); // no implicit copy }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_automapper.h��������������������������������������������������0000664�0000000�0000000�00000021454�12710376503�0022225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef _reaction_automapper #define _reaction_automapper #include "base_cpp/array.h" #include "base_cpp/ptr_array.h" #include "molecule/max_common_submolecule.h" #include "base_cpp/cancellation_handler.h" namespace indigo { class BaseReaction; //util class for keeping map generating from aam in reaction class ReactionMapMatchingData{ public: ReactionMapMatchingData(BaseReaction& r); //sets maps for atoms in molecules void createAtomMatchingData(); //sets maps for bonds in molecules void createBondMatchingData(); int beginMap(int mol_idx) const { return nextMap(mol_idx, -1); } int endMap() const ; int nextMap(int mol_idx, int opp_idx) const; int beginAtomMap(int mol_idx, int atom_idx) const { return nextAtomMap(mol_idx, -1, atom_idx); } int endAtomMap() const; int nextAtomMap(int mol_idx, int opposite_idx, int atom_idx) const; bool getAtomMap(int mol_idx, int opposite_idx, int atom_idx, Array<int>* mapping) const; int beginBondMap(int mol_idx, int bond_idx) const { return nextBondMap(mol_idx, -1, bond_idx); } int endBondMap() const; int nextBondMap(int mol_idx, int opposite_idx, int bond_idx) const; bool getBondMap(int mol_idx, int opposite_idx, int bond_idx, Array<int>* mapping) const; private: BaseReaction& _reaction; //array to set correspondence between atoms due to input mapping ObjArray< Array<int> > _vertexMatchingArray; //array to set correspondence between bonds due to input mapping ObjArray< Array<int> > _edgeMatchingArray; int _getVertexId(int mol_idx, int vert) const; int _getEdgeId(int mol_idx, int edge) const; }; class ReactionAutomapper { public: enum { // disregards any existing maps or bond change marks. AAM_REGEN_DISCARD = 0, // assumes the existing marks are absolutely correct. auto_aam changes only null marks AAM_REGEN_KEEP = 1, // assumes the existing marks might be wrong and can be altered. auto_amm consider input mapping and change it if map wrong AAM_REGEN_ALTER = 2, // special mode for clearing a mapping AAM_REGEN_CLEAR = 3, MAX_PERMUTATIONS_NUMBER = 5000, MIN_PERMUTATION_SIZE = 3 }; ReactionAutomapper(BaseReaction& reaction); //main method for searching AAM void automap(int mode); void correctReactingCenters(bool change_null_map) { _checkAtomMapping(true, false, change_null_map); } /* * Special flags */ bool ignore_atom_charges; bool ignore_atom_valence; bool ignore_atom_isotopes; bool ignore_atom_radicals; AromaticityOptions arom_options; DECL_ERROR; CancellationHandler* cancellation; private: //parameter for dimerization and dissociation enum { _MIN_VERTEX_SUB = 3 }; void _createReactionCopy(Array<int> &mol_mapping, ObjArray< Array<int> >& mappings); void _createMoleculeCopy(int mol_idx, bool reactant, Array<int> &mol_mapping, ObjArray< Array<int> >& mappings); void _makeInvertMap(Array<int>& map,Array<int>& invmap); //sets up input mapping void _initMappings(BaseReaction& reaction); //searches AAM using mcs and substructure functions void _createReactionMap(); //controls AAM due to reacting centers in reaction; this could change reacting centers or AAM void _cleanReactants(BaseReaction& reaction); int _handleWithProduct(const Array<int>& reactant_cons, Array<int>& product_mapping_tmp, BaseReaction& reaction, int product, ReactionMapMatchingData& react_map_match) ; bool _chooseBestMapping(BaseReaction& reaction, Array<int>& product_mapping, int product, int map_complete); bool _checkAtomMapping(bool change_rc, bool change_aam, bool change_rc_null); //arranges all maps to AAM void _setupReactionMap(Array<int> &mol_mapping, ObjArray< Array<int> >& mappings); void _setupReactionInvMap(Array<int> &mol_mapping, ObjArray< Array<int> >& mappings); //takes account of possibility for molecule dissociation void _considerDissociation(); //takes account of possibility for molecule dimerization void _considerDimerization(); int _validMapFound(BaseReaction& reaction, int react, int prod, Array<int>& sub_map) const; void _removeUnusedInfo(BaseReaction& reaction, int mol_idx, bool aam_presented) const; void _removeSmallComponents(BaseMolecule& mol) const ; void _createPermutations(BaseReaction& reaction, ObjArray< Array<int> > &); //all permutation static void _permutation(Array<int>& ,ObjArray< Array<int> > &); BaseReaction& _initReaction; AutoPtr<BaseReaction> _reactionCopy; Array<int> _usedVertices; int _maxMapUsed; int _maxVertUsed; int _maxCompleteMap; int _mode; }; class RSubstructureMcs : public SubstructureMcs { public: enum { // Conditions CONDITION_NONE = 0x0000, CONDITION_ATOM_CHARGES = 0x0001, CONDITION_ATOM_VALENCE = 0x0002, CONDITION_ATOM_RADICAL = 0x0004, CONDITION_ATOM_ISOTOPES = 0x0008, CONDITION_ALL = 0x000F, //maximum iteratins in exact mcs method MAX_ITERATION_NUMBER = 50000, HIGH_PRIORITY_SCORE = 1000 }; RSubstructureMcs(BaseReaction& reaction, const ReactionAutomapper& context); RSubstructureMcs(BaseReaction& reaction, int subNum, int superNum, const ReactionAutomapper& context); RSubstructureMcs(BaseReaction &reaction, BaseMolecule& sub, BaseMolecule& super, const ReactionAutomapper& context); virtual ~RSubstructureMcs(){} //molecules substructure search virtual bool searchSubstructure(Array<int>* map); //reaction molecules substructure search bool searchSubstructureReact(BaseMolecule& init_rmol, const Array<int>* in_map, Array<int> *out_map); //reaction molecules mcs search bool searchMaxCommonSubReact(const Array<int>* in_map, Array<int> *out_map); //callback for atom matching static bool atomConditionReact (Graph &g1, Graph &g2, const int *core_sub, int i, int j, void* userdata); //callback for bonds matching static bool bondConditionReact (Graph &g1, Graph &g2, int i, int j, void* userdata); //callback for bonds matching strict rules static bool bondConditionReactStrict (Graph &g1, Graph &g2, int i, int j, void* userdata); // simple exact bond matching static bool bondConditionReactSimple (Graph &g1, Graph &g2, int i, int j, void* userdata); //callback for mcs sorting solutions static int cbMcsSolutionTerm(Array<int>&, Array<int>&, void*); int findReactionCenter (BaseMolecule &mol, int bondNum) const ; void getReactingCenters(BaseMolecule& mol1, BaseMolecule& mol2, int bond1, int bond2, int& rc_reactant, int& rc_product) const; inline int getReactNumber() const { return _subReactNumber; } inline int getProductNumber() const { return _superProductNumber;} BaseReaction& getReaction() const { return _reaction; } void setUpFlags(const ReactionAutomapper& context); int flags; AromaticityOptions arom_options; private: int _searchSubstructure(EmbeddingEnumerator& emb_enum, const Array<int>* in_map, Array<int> *out_map); static bool _matchAtoms(BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags); void _selectBestAutomorphism(Array<int>* map_out); static int _cbAutoVertexReact (Graph &graph, int idx1, int idx2, const void *context); static bool _cbAutoCheckAutomorphismReact (Graph &graph, const Array<int> &mapping, const void *context); int _scoreSolution(BaseMolecule *sub_molecule, BaseMolecule *super_molecule, Array<int>& map); void _createQueryTransposition(); void _detransposeOutputMap(Array<int>* map) const; void _transposeInputMap(const Array<int>* map, Array<int>& input_map) const; inline int _getTransposedBondIndex(BaseMolecule& mol, int bond) const; const ReactionAutomapper& _context; BaseReaction& _reaction; int _subReactNumber; int _superProductNumber; ObjArray< Array<int> > _autoMaps; AutoPtr<BaseMolecule> _transposedQuery; Array<int> _transposition; Array<int> _invTransposition; Array<int> _bondTransposition; }; class AAMCancellationWrapper { public: AAMCancellationWrapper(CancellationHandler*); ~AAMCancellationWrapper(); private: AAMCancellationWrapper(const AAMCancellationWrapper&); CancellationHandler* _prev; }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_cdx_loader.h��������������������������������������������������0000664�0000000�0000000�00000002223�12710376503�0022145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_cdx_loader__ #define __reaction_cdx_loader__ #include "base_cpp/exception.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class Reaction; class ReactionCdxLoader { public: DECL_ERROR; ReactionCdxLoader (Scanner &scanner); ~ReactionCdxLoader (); void loadReaction (Reaction &rxn); StereocentersOptions stereochemistry_options; protected: Scanner &_scanner; private: ReactionCdxLoader (const ReactionCdxLoader &); // no implicit copy }; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_cdx_saver.h���������������������������������������������������0000664�0000000�0000000�00000002075�12710376503�0022024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_cdx_saver__ #define __reaction_cdx_saver__ #include "base_cpp/exception.h" namespace indigo { class Output; class Reaction; class ReactionCdxSaver { public: explicit ReactionCdxSaver (Output &output); ~ReactionCdxSaver (); void saveReaction (Reaction &rxn); DECL_ERROR; protected: Reaction *_rxn; Output &_output; private: ReactionCdxSaver (const ReactionCdxSaver &); // no implicit copy }; } #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_cml_loader.h��������������������������������������������������0000664�0000000�0000000�00000002223�12710376503�0022142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_cml_loader__ #define __reaction_cml_loader__ #include "base_cpp/exception.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class Reaction; class ReactionCmlLoader { public: DECL_ERROR; ReactionCmlLoader (Scanner &scanner); ~ReactionCmlLoader (); void loadReaction (Reaction &rxn); StereocentersOptions stereochemistry_options; protected: Scanner &_scanner; private: ReactionCmlLoader (const ReactionCmlLoader &); // no implicit copy }; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_cml_saver.h���������������������������������������������������0000664�0000000�0000000�00000002162�12710376503�0022016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_cml_saver__ #define __reaction_cml_saver__ #include "base_cpp/exception.h" namespace indigo { class Output; class Reaction; class ReactionCmlSaver { public: explicit ReactionCmlSaver (Output &output); ~ReactionCmlSaver (); void saveReaction (Reaction &rxn); bool skip_cml_tag; // skips <?xml> and <cml> tags DECL_ERROR; protected: Reaction *_rxn; Output &_output; private: ReactionCmlSaver (const ReactionCmlSaver &); // no implicit copy }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_enumerator_state.h��������������������������������������������0000664�0000000�0000000�00000022077�12710376503�0023433�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_enumerator_state__ #define __reaction_enumerator_state__ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_substructure_matcher.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "graph/embedding_enumerator.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/red_black.h" #include "base_cpp/obj.h" namespace indigo { class ReactionEnumeratorContext { public: AromaticityOptions arom_options; }; class ReactionEnumeratorState { public: DECL_ERROR; class ReactionMonomers { public: DECL_ERROR; CP_DECL; TL_CP_DECL(ReusableObjArray<Molecule>, _monomers); TL_CP_DECL(Array<int>, _reactant_indexes); TL_CP_DECL(Array<int>, _deep_levels); TL_CP_DECL(Array<int>, _tube_indexes); ReactionMonomers(); int size(); void clear(); Molecule & getMonomer( int reactant_idx, int index ); Molecule & getMonomer( int mon_index ); void addMonomer( int reactant_idx, Molecule &monomer, int deep_level = 0, int tube_idx = -1 ); void removeMonomer( int idx ); }; bool (*refine_proc)( const Molecule &uncleaned_fragments, Molecule &product, Array<int> &mapping, void *userdata ); void (*product_proc)( Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata ); void *userdata; bool is_multistep_reaction; bool is_self_react; bool is_one_tube; bool is_same_keeping; bool is_transform; int max_deep_level; int max_product_count; int max_reuse_count; ReactionEnumeratorState(ReactionEnumeratorContext &context, QueryReaction &cur_reaction, QueryMolecule &cur_full_product, Array<int> &cur_product_aam_array, RedBlackStringMap<int> &cur_smiles_array, ReactionMonomers &cur_reaction_monomers, int &cur_product_coint, ObjArray< Array<int> > &cur_tubes_monomers ); ReactionEnumeratorState( ReactionEnumeratorState &cur_rpe_state ); int buildProduct( void ); bool performSingleTransformation( Molecule &molecule, Array<int> &mapping, Array<int> &forbidden_atoms, Array<int> &original_hydrogens, bool &need_layout ); private: ReactionEnumeratorContext &_context; QueryReaction &_reaction; int _reactant_idx; int _is_simple_transform; int &_product_count; ObjArray< Array<int> > &_tubes_monomers; Array<int> &_product_aam_array; RedBlackStringMap<int> &_smiles_array; ReactionMonomers &_reaction_monomers; CP_DECL; TL_CP_DECL(Array<int>, _fragments_aam_array); TL_CP_DECL(QueryMolecule, _full_product); TL_CP_DECL(Array<int>, _product_monomers); TL_CP_DECL(Array<int>, _mapping); TL_CP_DECL(Molecule, _fragments); TL_CP_DECL(Array<int>, _is_needless_atom); TL_CP_DECL(Array<int>, _is_needless_bond); TL_CP_DECL(Array<int>, _bonds_mapping_sub); TL_CP_DECL(Array<int>, _bonds_mapping_super); TL_CP_DECL(ObjArray< Array<int> >, _att_points); TL_CP_DECL(MoleculeSubstructureMatcher::FragmentMatchCache, _fmcache); TL_CP_DECL(Array<int>, _monomer_forbidden_atoms); TL_CP_DECL(Array<int>, _product_forbidden_atoms); TL_CP_DECL(Array<int>, _original_hydrogens); AromaticityMatcher *_am; EmbeddingEnumerator *_ee; int _tube_idx; int _deep_level; bool _is_frag_search; bool _is_rg_exist; int _findCurTube( void ); bool _isMonomerFromCurTube( int monomer_idx ); static void _foldHydrogens(BaseMolecule &molecule, Array<int> *atoms_to_keep = 0, Array<int> *original_hydrogens = 0, Array<int> *mol_mapping = 0 ); void _productProcess( void ); bool _checkForSimplicity() { if (_reaction.reactantsCount() != 1 || _reaction.productsCount() != 1) return false; QueryMolecule &reactant = _reaction.getQueryMolecule(_reaction.reactantBegin()); QueryMolecule &product = _reaction.getQueryMolecule(_reaction.productBegin()); if ((reactant.vertexCount() != product.vertexCount()) || (reactant.edgeCount() != product.edgeCount())) return false; Array<int> &reactant_aam = _reaction.getAAMArray(_reaction.reactantBegin()); Array<int> &product_aam = _reaction.getAAMArray(_reaction.productBegin()); Array<int> aam_mapping; aam_mapping.resize(reactant.vertexEnd()); aam_mapping.fffill(); for (int i = reactant.vertexBegin(); i != reactant.vertexEnd(); i = reactant.vertexNext(i)) { if (reactant_aam[i] == 0) return false; int product_idx = product_aam.find(reactant_aam[i]); if (product_idx == -1) return false; aam_mapping[i] = product_idx; } for (int i = reactant.edgeBegin(); i != reactant.edgeEnd(); i = reactant.edgeNext(i)) { const Edge &edge = reactant.getEdge(i); int product_beg = aam_mapping[edge.beg]; int product_end = aam_mapping[edge.end]; if (product_beg == -1 || product_end == -1) return false; if (product.findEdgeIndex(product_beg, product_end) == -1) return false; if (!MoleculeCisTrans::isGeomStereoBond(reactant, i, NULL, false)) continue; int ct_sign = MoleculeCisTrans::getMappingParitySign(reactant, product, i, aam_mapping.ptr()); if (ct_sign <= 0) return false; } if (!MoleculeStereocenters::checkSub(reactant.stereocenters, product.stereocenters, aam_mapping.ptr(), false)) return false; return true; } bool _nextMatchProcess( EmbeddingEnumerator &ee, const QueryMolecule &reactant, const Molecule &monomer ); int _calcMaxHCnt( QueryMolecule &molecule ); bool _startEmbeddingEnumerator( Molecule &monomer ); void _changeQueryNode( QueryMolecule &ee_reactant, int change_atom_idx ); void _findFragAtoms( Array<byte> &unfrag_mon_atoms, QueryMolecule &submolecule, Molecule &fragment, int *core_sub, int *core_super ); void _cleanFragments( void ); void _findR2PMapping( QueryMolecule &reactant, Array<int> &mapping); void _invertStereocenters( Molecule &molecule, int edge_idx ); void _cistransUpdate( QueryMolecule &submolecule, Molecule &supermolecule, int *frag_mapping, const Array<int> &rp_mapping, int *core_sub); QueryMolecule::Atom * _getReactantAtom( int atom_aam ); void _buildMolProduct( QueryMolecule &product, Molecule &mol_product, Molecule &uncleaned_fragments, Array<int> &all_forbidden_atoms, Array<int> &mapping_out ); void _stereocentersUpdate( QueryMolecule &submolecule, Molecule &supermolecule, const Array<int> &rp_mapping, int *core_sub, int *core_super ); void _findFragments2ProductMapping( Array<int> &f2p_mapping ); void _completeCisTrans( Molecule &product, Molecule &uncleaned_fragments, Array<int> &frags_mapping ); bool _checkValence( Molecule &mol, int atom_idx ); bool _attachFragments( Molecule &ready_product_out, Array<int> &ucfrag_mapping ); bool _checkFragment( QueryMolecule &submolecule, Molecule &monomer, Array<byte> &unfrag_mon_atoms, int *core_sub ); void _checkFragmentNecessity ( Array<int> &is_needless_att_point ); bool _addFragment( Molecule &fragment, QueryMolecule &submolecule, Array<int> &rp_mapping, const Array<int> &sub_rg_atoms, int *core_sub, int *core_super ); static bool _matchVertexCallback( Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata ); static bool _matchEdgeCallback( Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata ); static bool _allowManyToOneCallback( Graph &subgraph, int sub_idx, void *userdata ); static void _removeAtomCallback( Graph &subgraph, int sub_idx, void *userdata ); static void _addBondCallback( Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata ); static bool _checkForNeverUsed(ReactionEnumeratorState *rpe_state, Molecule &supermolecule); static int _embeddingCallback( Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata ); }; } #endif /* __reaction_enumerator_state__ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_exact_matcher.h�����������������������������������������������0000664�0000000�0000000�00000004010�12710376503�0022644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_exact_matcher__ #define __reaction_exact_matcher__ #include "base_cpp/exception.h" #include "reaction/base_reaction_substructure_matcher.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Reaction; class DLLEXPORT ReactionExactMatcher : public BaseReactionSubstructureMatcher { public: enum { // start from 0x0100 not to conflict with MoleculExactMatcher CONDITION_AAM = 0x0100, // atom-to-atom mapping values CONDITION_REACTING_CENTERS = 0x0200, // reacting centers CONDITION_ALL = 0x0300 }; ReactionExactMatcher (Reaction &query, Reaction &target); dword flags; DECL_ERROR; protected: Reaction &_query; Reaction &_target; static bool _match_atoms (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, void *context); static bool _match_bonds (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, AromaticityMatcher *am, void *context); static bool _prepare (BaseReaction &query, Reaction &target, void *context); static bool _prepare_ee (EmbeddingEnumerator &ee, BaseMolecule &submol, Molecule &supermol, void *context); }; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_fingerprint.h�������������������������������������������������0000664�0000000�0000000�00000003210�12710376503�0022365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_fingerprint__ #define __reaction_fingerprint__ #include "base_cpp/tlscont.h" #include "base_cpp/cancellation_handler.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class BaseReaction; struct MoleculeFingerprintParameters; class DLLEXPORT ReactionFingerprintBuilder { public: ReactionFingerprintBuilder (BaseReaction &reaction, const MoleculeFingerprintParameters ¶meters); bool query; bool skip_ord; bool skip_sim; bool skip_ext; void process (); byte * get (); byte * getSim (); void parseFingerprintType(const char *type, bool query); CancellationHandler* cancellation; DECL_ERROR; protected: BaseReaction &_reaction; const MoleculeFingerprintParameters &_parameters; CP_DECL; TL_CP_DECL(Array<byte>, _fingerprint); private: ReactionFingerprintBuilder (const ReactionFingerprintBuilder &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_neighborhood_counters.h���������������������������������������0000664�0000000�0000000�00000002303�12710376503�0024431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_neighborhood_counters_h__ #define __reaction_neighborhood_counters_h__ #include "base_cpp/obj_array.h" #include "molecule/molecule_neighbourhood_counters.h" namespace indigo { class BaseReaction; class Reaction; class QueryReaction; class MoleculeAtomNeighbourhoodCounters; class ReactionAtomNeighbourhoodCounters { public: void calculate (Reaction &reac); void calculate (QueryReaction &reac); const MoleculeAtomNeighbourhoodCounters & getCounters (int idx) const; private: ObjArray<MoleculeAtomNeighbourhoodCounters> _counters; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_product_enumerator.h������������������������������������������0000664�0000000�0000000�00000005712�12710376503�0023770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_product_enumerator__ #define __reaction_product_enumerator__ #include "molecule/molecule.h" #include "reaction/reaction.h" #include "base_cpp/reusable_obj_array.h" #include "reaction/reaction_enumerator_state.h" namespace indigo { class ReactionProductEnumerator { public: DECL_ERROR; bool is_multistep_reaction; /* if true - all reactants in monomer take part in reaction, false - one */ bool is_self_react; /* if true - monomer's molecule can react with itself, false - can't */ bool is_one_tube; /* if true - all monomers are in one test-tube */ int max_product_count; int max_deep_level; void *userdata; AromaticityOptions arom_options; ReactionProductEnumerator( QueryReaction &reaction ); ~ReactionProductEnumerator() {} void addMonomer( int reactant_idx, Molecule &monomer ); void clearReactantMonomers( int reactant_idx ); Molecule & getMonomer( int reactant_idx, int index ); Molecule & getMonomer( int mon_index ); const QueryReaction & getReaction( void ); int getMonomersCount( int reactant_idx ); void buildProducts( void ); // This callback should be used for validation and refining of the results of applying the pattern. // uncleaned_fragments: the molecule before applying the reaction (with aromatization and unfolded hydrogens) // product: the molecule after transformation (possibly broken), may be modified in callback // mapping: atom to atom mapping // result: true if the molecule shall be accepted, false otherwise bool (*refine_proc)( const Molecule &uncleaned_fragments, Molecule &product, Array<int> &mapping, void *userdata ); // This callback provides the results of applying the pattern, one for each possible mapping. // product: the molecule after transformation // mapping: atom to atom mapping void (*product_proc)( Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata ); private: bool _is_rg_exist; int _product_count; QueryReaction &_reaction; ReactionEnumeratorState::ReactionMonomers _reaction_monomers; CP_DECL; TL_CP_DECL(Array<int>, _product_aam_array); TL_CP_DECL(RedBlackStringMap<int>, _smiles_array); TL_CP_DECL(ObjArray< Array<int> >, _tubes_monomers); void _buildTubesGrid( void ); }; } #endif /* __reaction_product_enumerator__ */ ������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_substructure_matcher.h����������������������������������������0000664�0000000�0000000�00000004470�12710376503�0024324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_substructure_matcher__ #define __reaction_substructure_matcher__ #include "graph/embedding_enumerator.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_substructure_matcher.h" #include "base_cpp/auto_ptr.h" #include "base_cpp/obj.h" #include "reaction/base_reaction_substructure_matcher.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class QueryReaction; class DLLEXPORT ReactionSubstructureMatcher : public BaseReactionSubstructureMatcher { public: ReactionSubstructureMatcher (Reaction &target); bool use_daylight_aam_mode; DECL_ERROR; protected: TL_CP_DECL(ObjArray<MoleculeSubstructureMatcher::FragmentMatchCache>, _fmcaches); virtual bool _checkAAM (); static bool _match_atoms (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, void *context); static bool _match_bonds (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, AromaticityMatcher *am, void *context); static void _remove_atom (BaseMolecule &submol, int sub_idx, AromaticityMatcher *am); static void _add_bond (BaseMolecule &submol, Molecule &supermol, int sub_idx, int super_idx, AromaticityMatcher *am); static bool _prepare (BaseReaction &query_, Reaction &target, void *context); static bool _prepare_ee (EmbeddingEnumerator &ee, BaseMolecule &submol, Molecule &supermol, void *context); }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/reaction_transformation.h����������������������������������������������0000664�0000000�0000000�00000004244�12710376503�0023114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __reaction_transformation__ #define __reaction_transformation__ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_arom.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/reaction_enumerator_state.h" #include "graph/embedding_enumerator.h" #include "base_cpp/reusable_obj_array.h" #include "base_cpp/cancellation_handler.h" namespace indigo { class ReactionTransformation// : public ReactionEnumeratorState { public: DECL_ERROR; ReactionTransformation( void ); bool transform(Molecule &molecule, QueryReaction &reaction, Array<int> *mapping = 0 ); bool transform(ReusableObjArray<Molecule> &molecules, QueryReaction &reaction, ReusableObjArray<Array<int>> *mapping_array = 0 ); AromaticityOptions arom_options; bool layout_flag; bool smart_layout; CancellationHandler *cancellation; private: CP_DECL; TL_CP_DECL(QueryReaction, _merged_reaction); TL_CP_DECL(Molecule, _cur_monomer); TL_CP_DECL(Array<int>, _mapping); static void _product_proc( Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata ); void _mergeReactionComponents( QueryReaction &reaction, int mol_type, QueryMolecule &merged_molecule, Array<int> &merged_aam); void _generateMergedReaction( QueryReaction &reaction ); }; } #endif /*__reaction_transformation__*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/rsmiles_loader.h�������������������������������������������������������0000664�0000000�0000000�00000003231�12710376503�0021161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rsmiles_loader__ #define __rsmiles_loader__ #include "base_cpp/exception.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class BaseReaction; class Reaction; class QueryReaction; class DLLEXPORT RSmilesLoader { public: DECL_ERROR; RSmilesLoader (Scanner &scanner); void loadReaction (Reaction &rxn); void loadQueryReaction (QueryReaction &rxn); // see comment in SmilesLoader bool ignore_closing_bond_direction_mismatch; bool smarts_mode; bool ignore_cistrans_errors; StereocentersOptions stereochemistry_options; protected: struct _Atom { int mol_idx; int atom_idx; }; int _selectGroup (int& idx, int rcnt, int ccnt, int pcnt) const; int _selectGroupByPair (int &lead_idx, int& idx, int rcnt, int ccnt, int pcnt) const; Scanner &_scanner; BaseReaction *_brxn; QueryReaction *_qrxn; Reaction *_rxn; void _loadReaction (); private: RSmilesLoader (const RSmilesLoader &); // no implicit copy }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/rsmiles_saver.h��������������������������������������������������������0000664�0000000�0000000�00000003460�12710376503�0021037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rsmiles_saver__ #define __rsmiles_saver__ #include "base_cpp/exception.h" #include "base_cpp/obj_array.h" #include "base_cpp/tlscont.h" #include "reaction.h" #ifdef _WIN32 #pragma warning(push) #pragma warning(disable:4251) #endif namespace indigo { class Output; class BaseReaction; class QueryReaction; class Reaction; class DLLEXPORT RSmilesSaver { public: DECL_ERROR; RSmilesSaver (Output &output); void saveReaction (Reaction &reaction); void saveQueryReaction (QueryReaction &reaction); bool smarts_mode; protected: BaseReaction *_brxn; QueryReaction *_qrxn; Reaction *_rxn; void _saveReaction (); struct _Idx { int mol; int idx; }; Output &_output; CP_DECL; TL_CP_DECL(Array<_Idx>, _written_atoms); TL_CP_DECL(Array<_Idx>, _written_bonds); TL_CP_DECL(Array<int>, _ncomp); void _writeMolecule (int i); void _writeFragmentsInfo (); void _writeStereogroups (); void _writeRadicals (); void _writePseudoAtoms (); void _writeHighlighting (); bool _comma; private: RSmilesSaver (const RSmilesSaver &); // no implicit copy }; } #ifdef _WIN32 #pragma warning(pop) #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/rxnfile_loader.h�������������������������������������������������������0000664�0000000�0000000�00000003341�12710376503�0021154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rxnfile_loader__ #define __rxnfile_loader__ #include "base_cpp/exception.h" #include "molecule/molecule_stereocenter_options.h" namespace indigo { class Scanner; class BaseReaction; class Reaction; class QueryReaction; class MolfileLoader; class DLLEXPORT RxnfileLoader { public: RxnfileLoader (Scanner &scanner); ~RxnfileLoader (); void loadReaction (Reaction& reaction); void loadQueryReaction (QueryReaction& reaction); bool treat_x_as_pseudoatom; StereocentersOptions stereochemistry_options; bool ignore_noncritical_query_features; DECL_ERROR; protected: BaseReaction *_brxn; QueryReaction *_qrxn; Reaction *_rxn; void _loadReaction (); Scanner &_scanner; void _readRxnHeader(); void _readReactantsHeader(); void _readProductsHeader(); void _readCatalystsHeader(); void _readReactantsFooter(); void _readProductsFooter(); void _readCatalystsFooter(); void _readMolHeader(); void _readMol (MolfileLoader &loader, int index); int _n_reactants; int _n_products; int _n_catalysts; bool _v3000; }; } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/rxnfile_saver.h��������������������������������������������������������0000664�0000000�0000000�00000003270�12710376503�0021027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __rxnfile_saver__ #define __rxnfile_saver__ #include "base_cpp/exception.h" namespace indigo { class Output; class Reaction; class BaseReaction; class QueryReaction; class MolfileSaver; class RxnfileSaver { public: RxnfileSaver(Output& output); ~RxnfileSaver(); void saveBaseReaction(BaseReaction& reaction); void saveReaction(Reaction& reaction); void saveQueryReaction(QueryReaction& reaction); int molfile_saving_mode; // MolfileSaver::MODE_***, default zero bool skip_date; bool add_stereo_desc; DECL_ERROR; protected: void _saveReaction(); bool _v2000; BaseReaction *_brxn; QueryReaction *_qrxn; Reaction *_rxn; Output &_output; void _writeRxnHeader (BaseReaction &reaction); void _writeReactantsHeader (); void _writeProductsHeader (); void _writeCatalystsHeader (); void _writeReactantsFooter (); void _writeProductsFooter (); void _writeCatalystsFooter (); void _writeMolHeader (); void _writeMol (MolfileSaver &saver, int index); void _writeRxnFooter (); }; } #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016574�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/base_reaction.cpp��������������������������������������������������0000664�0000000�0000000�00000022507�12710376503�0022104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/base_reaction.h" #include "base_cpp/tlscont.h" using namespace indigo; IMPL_ERROR(BaseReaction, "reaction"); IMPL_ERROR(SideIter, "iterator"); SideIter::SideIter(BaseReaction &owner, int idx, int side) : _owner(owner), AutoIterator(idx), _side(side) { } SideIter & SideIter::operator++() { switch (_side) { case BaseReaction::REACTANT: _idx = _owner.reactantNext(_idx); break; case BaseReaction::PRODUCT: _idx = _owner.productNext(_idx); break; case BaseReaction::CATALYST: _idx = _owner.catalystNext(_idx); break; default: throw Error("Invalid BaseReaction side iterator type"); } return *this; } SideAuto::SideAuto(BaseReaction &owner, int side) : _owner(owner), _side(side) { } SideIter SideAuto::begin() { int idx; switch (_side) { case BaseReaction::REACTANT: idx = _owner.reactantBegin(); break; case BaseReaction::PRODUCT: idx = _owner.productBegin(); break; case BaseReaction::CATALYST: idx = _owner.catalystBegin(); break; default: throw SideIter::Error("Invalid BaseReaction side iterator type"); } return SideIter(_owner, idx, _side); } SideIter SideAuto::end() { return SideIter(_owner, _owner.end(), _side); } BaseReaction::BaseReaction () : reactants(*this, REACTANT), catalysts(*this, CATALYST), products(*this, PRODUCT) { clear(); } BaseReaction::~BaseReaction () { } void BaseReaction::clear() { _reactantCount = 0; _productCount = 0; _catalystCount = 0; _allMolecules.clear(); _atomAtomMapping.clear(); _reactingCenters.clear(); _inversionNumbers.clear(); _types.clear(); name.clear(); } int BaseReaction::getAAM (int index, int atom) { return _atomAtomMapping[index][atom]; } int BaseReaction::getReactingCenter(int index, int bond) { return _reactingCenters[index][bond]; } int BaseReaction::getInversion (int index, int atom) { return _inversionNumbers[index][atom]; } Array<int> & BaseReaction::getAAMArray (int index) { return _atomAtomMapping[index]; } Array<int> & BaseReaction::getReactingCenterArray (int index) { return _reactingCenters[index]; } Array<int> & BaseReaction::getInversionArray (int index) { return _inversionNumbers[index]; } int BaseReaction::addReactant () { return _addBaseMolecule(REACTANT); } int BaseReaction::addProduct () { return _addBaseMolecule(PRODUCT); } int BaseReaction::addCatalyst () { return _addBaseMolecule(CATALYST); } void BaseReaction::_addedBaseMolecule (int idx, int side, BaseMolecule &mol) { if (side == REACTANT) _reactantCount++; else if (side == PRODUCT) _productCount++; else // CATALYST _catalystCount++; _types.expand(idx + 1); _types[idx] = side; _atomAtomMapping.expand(idx + 1); _atomAtomMapping[idx].clear_resize(mol.vertexEnd()); _atomAtomMapping[idx].zerofill(); _reactingCenters.expand(idx + 1); _reactingCenters[idx].clear_resize(mol.edgeEnd()); _reactingCenters[idx].zerofill(); _inversionNumbers.expand(idx + 1); _inversionNumbers[idx].clear_resize(mol.vertexEnd()); _inversionNumbers[idx].zerofill(); } int BaseReaction::findAtomByAAM (int mol_idx, int aam) { BaseMolecule &mol = *_allMolecules.at(mol_idx); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) if (getAAM(mol_idx, i) == aam) return i; return -1; } int BaseReaction::findAamNumber (BaseMolecule *mol, int atom_number) { for (int i = begin(); i < end(); i = next(i)) if (mol == _allMolecules.at(i)) return getAAM(i, atom_number); throw Error("cannot find aam number"); } int BaseReaction::findReactingCenter (BaseMolecule *mol, int bond_number) { for (int i = begin(); i < end(); i = next(i)) if (mol == _allMolecules.at(i)) return getReactingCenter(i, bond_number); throw Error("cannot find reacting center"); } void BaseReaction::markStereocenterBonds() { for (int i = begin(); i < end(); i = next(i)) { _allMolecules[i]->clearBondDirections(); _allMolecules[i]->stereocenters.markBonds(); _allMolecules[i]->allene_stereo.markBonds(); } } bool BaseReaction::haveCoord (BaseReaction &reaction) { for (int i = reaction.begin(); i < reaction.end(); i = reaction.next(i)) if (!reaction.getBaseMolecule(i).have_xyz) return false; return true; } int BaseReaction::_nextElement (int type, int index) { if (index == -1) index = _allMolecules.begin(); else index = _allMolecules.next(index); for (; index != _allMolecules.end(); index = _allMolecules.next(index)) { if (_types[index] & type) break; } return index; } void BaseReaction::clearAAM () { for (int i = begin(); i < end(); i = next(i)) _atomAtomMapping[i].zerofill(); } int BaseReaction::addReactantCopy (BaseMolecule& mol, Array<int>* mapping, Array<int> *inv_mapping) { int idx = _allMolecules.add(mol.neu()); _allMolecules[idx]->clone(mol, mapping, inv_mapping); _addedBaseMolecule(idx, REACTANT, *_allMolecules[idx]); return idx; } int BaseReaction::addProductCopy (BaseMolecule& mol, Array<int>* mapping, Array<int> *inv_mapping) { int idx = _allMolecules.add(mol.neu()); _allMolecules[idx]->clone(mol, mapping, inv_mapping); _addedBaseMolecule(idx, PRODUCT, *_allMolecules[idx]); return idx; } int BaseReaction::addCatalystCopy (BaseMolecule& mol, Array<int>* mapping, Array<int> *inv_mapping) { int idx = _allMolecules.add(mol.neu()); _allMolecules[idx]->clone(mol, mapping, inv_mapping); _addedBaseMolecule(idx, CATALYST, *_allMolecules[idx]); return idx; } void BaseReaction::clone (BaseReaction &other, Array<int> *mol_mapping, ObjArray< Array<int> >* mappings, ObjArray< Array<int> >* inv_mappings) { clear(); int i, index = 0; QS_DEF(ObjArray< Array<int> >, tmp_mappings); if (mol_mapping != 0) { mol_mapping->clear_resize(other.end()); mol_mapping->fffill(); } if(mappings == 0) mappings = &tmp_mappings; mappings->clear(); for (i = 0; i < other.end(); ++i) mappings->push(); if (inv_mappings != 0) inv_mappings->clear(); for(int i = other.begin(); i < other.end(); i = other.next(i)) { BaseMolecule& rmol = other.getBaseMolecule(i); QS_DEF(Array<int>, inv_mapping); switch (other._types[i]) { case REACTANT: index = addReactantCopy(rmol, &mappings->at(i), &inv_mapping); break; case PRODUCT: index = addProductCopy(rmol, &mappings->at(i), &inv_mapping); break; case CATALYST: index = addCatalystCopy(rmol, &mappings->at(i), &inv_mapping); break; } if (inv_mappings != 0) { inv_mappings->expand(index + 1); inv_mappings->at(index).copy(inv_mapping); } if (mol_mapping != 0) mol_mapping->at(i) = index; BaseMolecule &lmol = getBaseMolecule(index); for(int j = lmol.vertexBegin(); j < lmol.vertexEnd(); j = lmol.vertexNext(j)) { getAAMArray(index).at(j) = other.getAAM(i, mappings->at(i)[j]); getInversionArray(index).at(j) = other.getInversion(i, mappings->at(i)[j]); } for (int j = lmol.edgeBegin(); j < lmol.edgeEnd(); j = lmol.edgeNext(j)) { const Edge &edge = lmol.getEdge(j); int edge_idx = other.getBaseMolecule(i).findEdgeIndex(mappings->at(i)[edge.beg], mappings->at(i)[edge.end]); getReactingCenterArray(index).at(j) = other.getReactingCenter(i, edge_idx); } // subclass' stuff _clone(other, index, i, mappings); } name.copy(other.name); } void BaseReaction::_clone (BaseReaction &other, int index, int i, ObjArray< Array<int> >* mol_mappings) { } Reaction & BaseReaction::asReaction () { throw Error("asReaction(): not a Reaction"); } QueryReaction & BaseReaction::asQueryReaction () { throw Error("asQueryReaction(): not a QueryReaction"); } bool BaseReaction::isQueryReaction () { return false; } void BaseReaction::remove (int i) { int side = _types[i]; if (side == REACTANT) _reactantCount--; else if (side == PRODUCT) _productCount--; else // CATALYST _catalystCount--; _allMolecules.remove(i); } int BaseReaction::begin () { return _nextElement(REACTANT | PRODUCT | CATALYST, -1); } int BaseReaction::end () { return _allMolecules.end(); } int BaseReaction::next (int index) { return _nextElement(REACTANT | PRODUCT | CATALYST, index); } int BaseReaction::count () { return _allMolecules.size(); } int BaseReaction::findMolecule (BaseMolecule *mol) { for (int i = begin(); i != end(); i = next(i)) if (&getBaseMolecule(i) == mol) return i; return -1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/base_reaction_substructure_matcher.cpp�����������������������������0000664�0000000�0000000�00000051305�12710376503�0026437�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/molecule_substructure_matcher.h" #include "reaction/base_reaction_substructure_matcher.h" #include "reaction/reaction_neighborhood_counters.h" #include "molecule/molecule_neighbourhood_counters.h" #include "reaction/base_reaction_substructure_matcher.h" using namespace indigo; IMPL_ERROR(BaseReactionSubstructureMatcher, "reaction substructure matcher"); CP_DEF(BaseReactionSubstructureMatcher); BaseReactionSubstructureMatcher::BaseReactionSubstructureMatcher (Reaction &target) : _target(target), CP_INIT, TL_CP_GET(_matchers), TL_CP_GET(_aam_to_second_side_1), TL_CP_GET(_aam_to_second_side_2), TL_CP_GET(_molecule_core_1), TL_CP_GET(_molecule_core_2), TL_CP_GET(_aam_core_first_side) { use_aromaticity_matcher = true; highlight = false; match_atoms = 0; match_bonds = 0; context = 0; remove_atom = 0; add_bond = 0; prepare = 0; _match_stereo = true; _query_nei_counters = 0; _target_nei_counters = 0; _query = 0; _matchers.clear(); _matchers.add(new _Matcher(*this)); } void BaseReactionSubstructureMatcher::setQuery (BaseReaction &query) { _query = &query; } void BaseReactionSubstructureMatcher::setNeiCounters (const ReactionAtomNeighbourhoodCounters *query_counters, const ReactionAtomNeighbourhoodCounters *target_counters) { _query_nei_counters = query_counters; _target_nei_counters = target_counters; } bool BaseReactionSubstructureMatcher::find () { if (_query == 0) throw Error("no query"); if (prepare != 0 && !prepare(*_query, _target, context)) return false; if (_query->reactantsCount() > _target.reactantsCount() || _query->productsCount() > _target.productsCount()) return false; if (_query->reactantsCount() * _target.reactantsCount() < _query->productsCount() * _target.productsCount()) _first_side = Reaction::REACTANT, _second_side = Reaction::PRODUCT; else _first_side = Reaction::PRODUCT, _second_side = Reaction::REACTANT; _initMap(*_query, _second_side, _aam_to_second_side_1); _initMap(_target, _second_side, _aam_to_second_side_2); _molecule_core_1.resize(_query->end()); _molecule_core_1.fffill(); _molecule_core_2.resize(_target.end()); _molecule_core_2.fffill(); _aam_core_first_side.clear(); _matchers.top()->match_stereo = _match_stereo; while (1) { int command = _matchers.top()->nextPair(); if (command == _CONTINUE) continue; if (command == _RETURN) { if (_checkAAM()) { _highlight(); return true; } command = _NO_WAY; } else if (command != _NO_WAY) { int mol1 = _matchers.top()->_current_molecule_1; int mol2 = _matchers.top()->_current_molecule_2; Array<int> &core1 = _matchers.top()->_current_core_1; Array<int> &core2 = _matchers.top()->_current_core_2; int mode = _matchers.top()->getMode(); //_matchers.reserve(_matchers.size() + 1); AutoPtr<_Matcher> top_matcher(new _Matcher(*_matchers.top())); _matchers.add(top_matcher.release()); _matchers.top()->setMode(command); if (!_matchers.top()->addPair(mol1, mol2, core1, core2, mode == _FIRST_SIDE)) _matchers.removeLast(); } if (command == _NO_WAY) { if (_matchers.size() > 1) { _matchers.top()->restore(); _matchers.removeLast(); } else return false; } } } // Init data for reaction substructure search void BaseReactionSubstructureMatcher::_initMap (BaseReaction &reaction, int side, RedBlackMap<int, int> &aam_map) { int i, j; int *val; aam_map.clear(); // collect aam-to-molecule index mapping for reaction second side for (i = reaction.sideBegin(side); i < reaction.sideEnd(); i = reaction.sideNext(side, i)) { BaseMolecule &i_mol = reaction.getBaseMolecule(i); for (j = i_mol.vertexBegin(); j < i_mol.vertexEnd(); j = i_mol.vertexNext(j)) { int aam_number = reaction.getAAM(i, j); if (aam_number != 0) { if ((val = aam_map.at2(aam_number)) == 0) aam_map.insert(aam_number, i); else if (*val < 0) (*val)--; else (*val) = -1; } } } } // Check correct AAM relationship between query and target reaction bool BaseReactionSubstructureMatcher::_checkAAM () { int *aam, aam1, aam2; int i, j; for (i = 1; i < _matchers.size() - 1; i++) { if (_matchers[i]->getMode() == _FIRST_SIDE) continue; BaseMolecule &mol1 = _query->getBaseMolecule(_matchers[i]->_current_molecule_1); for (j = mol1.vertexBegin(); j < mol1.vertexEnd(); j = mol1.vertexNext(j)) { int k = _matchers[i]->_current_core_1[j]; if (k < 0) continue; aam1 = _query->getAAM(_matchers[i]->_current_molecule_1, j); aam2 = _target.getAAM(_matchers[i]->_current_molecule_2, k); if (aam1 > 0 && aam2 > 0) if ((aam = _aam_core_first_side.at2(aam1)) != 0 && *aam != aam2) return false; } } return true; } int BaseReactionSubstructureMatcher::getTargetMoleculeIndex (int query_mol_idx) { // can be optimized, but as the number of molecules // seldom exceeds 5, the linear search is acceptable for (int i = 0; i < _matchers.size() - 1; i++) if (_matchers[i]->_current_molecule_1 == query_mol_idx) return _matchers[i]->_current_molecule_2; throw Error("getTargetMoleculeIndex(): can not find mapping for query molecule %d", query_mol_idx); } const int * BaseReactionSubstructureMatcher::getQueryMoleculeMapping (int query_mol_idx) { for (int i = 0; i < _matchers.size() - 1; i++) if (_matchers[i]->_current_molecule_1 == query_mol_idx) return _matchers[i]->_current_core_1.ptr(); throw Error("getQueryMoleculeMapping(): can not find mapping for query molecule %d", query_mol_idx); } void BaseReactionSubstructureMatcher::_highlight () { if (!highlight) return; int i; for (i = 0; i < _matchers.size() - 1; i++) _target.getBaseMolecule(_matchers[i]->_current_molecule_2).highlightSubmolecule( _query->getBaseMolecule(_matchers[i]->_current_molecule_1), _matchers[i]->_current_core_1.ptr(), true); } CP_DEF(BaseReactionSubstructureMatcher::_Matcher); BaseReactionSubstructureMatcher::_Matcher::_Matcher (BaseReactionSubstructureMatcher &context) : CP_INIT, TL_CP_GET(_current_core_1), TL_CP_GET(_current_core_2), _context(context), TL_CP_GET(_mapped_aams) { _mode = _FIRST_SIDE; _selected_molecule_1 = -1; _selected_molecule_2 = -1; _current_molecule_1 = -1; _current_molecule_2 = -1; _mapped_aams.clear(); match_stereo = true; _current_core_1.clear(); _current_core_2.clear(); } BaseReactionSubstructureMatcher::_Matcher::_Matcher (const BaseReactionSubstructureMatcher::_Matcher &other) : CP_INIT, TL_CP_GET(_current_core_1), TL_CP_GET(_current_core_2), _context(other._context), TL_CP_GET(_mapped_aams) { _current_molecule_1 = -1; _current_molecule_2 = -1; _mapped_aams.clear(); match_stereo = other.match_stereo; _current_core_1.clear(); _current_core_2.clear(); _selected_molecule_1 = -1; _selected_molecule_2 = -1; } int BaseReactionSubstructureMatcher::_Matcher::_nextPair () { int side; if (_mode == _FIRST_SIDE) side = _context._first_side; else // _SECOND_SIDE_REST side = _context._second_side; if (_enumerator.get() == 0 || !_enumerator->processNext()) { do { while (1) { if (_current_molecule_1 == -1) { for (_current_molecule_1 = _context._query->sideBegin(side); _current_molecule_1 < _context._query->sideEnd(); _current_molecule_1 = _context._query->sideNext(side, _current_molecule_1)) if (_context._molecule_core_1[_current_molecule_1] < 0) break; if (_current_molecule_1 == _context._query->sideEnd()) { if (_mode == _FIRST_SIDE) { _mode = _SECOND_SIDE_REST; _current_molecule_1 = -1; return _nextPair(); } return _RETURN; } } if (_current_molecule_2 == -1) _current_molecule_2 = _context._target.sideBegin(side); else _current_molecule_2 = _context._target.sideNext(side, _current_molecule_2); for ( ; _current_molecule_2 < _context._target.sideEnd(); _current_molecule_2 = _context._target.sideNext(side, _current_molecule_2)) if (_context._molecule_core_2[_current_molecule_2] < 0) break; if (_current_molecule_2 == _context._target.sideEnd()) return _NO_WAY; _enumerator.free(); BaseMolecule &mol_1 = _context._query->getBaseMolecule(_current_molecule_1); Molecule &mol_2 = _context._target.getMolecule(_current_molecule_2); if (!_initEnumerator(mol_1, mol_2)) { _enumerator.free(); continue; } break; } _enumerator->processStart(); } while (!_enumerator->processNext()); } return _mode == _FIRST_SIDE ? _SECOND_SIDE : _SECOND_SIDE_REST; } int BaseReactionSubstructureMatcher::_Matcher::nextPair () { if (_mode != _SECOND_SIDE) { int next = _nextPair(); if (next != _SECOND_SIDE) return next; // Switch to _SECOND_SIDE BaseMolecule &mol_1 = _context._query->getBaseMolecule(_current_molecule_1); int first_aam_1 = 0; int first_aam_2 = 0; int i; for (i = mol_1.vertexBegin(); i < mol_1.vertexEnd(); i = mol_1.vertexNext(i)) if (_current_core_1[i] >= 0) { first_aam_1 = _context._query->getAAM(_current_molecule_1, i); first_aam_2 = _context._target.getAAM(_current_molecule_2, _current_core_1[i]); break; } if (first_aam_1 > 0 && first_aam_2 > 0) { // Check the other side if needed int* mol_1_idx_ss_ptr = _context._aam_to_second_side_1.at2(first_aam_1); int* mol_2_idx_ss_ptr = _context._aam_to_second_side_2.at2(first_aam_2); if (mol_1_idx_ss_ptr == 0 && mol_2_idx_ss_ptr == 0) // There is no pair for both atom return _FIRST_SIDE; if (mol_1_idx_ss_ptr == 0 || mol_2_idx_ss_ptr == 0) // One atom has a pair atom while other hasn't one return _CONTINUE; int mol_1_idx_ss = *mol_1_idx_ss_ptr; int mol_2_idx_ss = *mol_2_idx_ss_ptr; if ((mol_1_idx_ss < 0 && mol_1_idx_ss < mol_2_idx_ss)) return _CONTINUE; // subreactions equal AAM-numbers more than superreaction if (mol_2_idx_ss < 0) return _FIRST_SIDE; // check this molecules in the completion phase if (_context._molecule_core_1[mol_1_idx_ss] >= 0) { if (_context._molecule_core_1[mol_1_idx_ss] != mol_2_idx_ss) return _CONTINUE; int first_idx_1_ss = _context._query->findAtomByAAM(mol_1_idx_ss, first_aam_1); int first_idx_2_ss = _context._target.findAtomByAAM(mol_2_idx_ss, first_aam_2); int i; for (i = 0; i < _context._matchers.size(); i++) if (_context._matchers[i]->_current_molecule_1 == mol_1_idx_ss) { if (_context._matchers[i]->_current_core_1[first_idx_1_ss] != first_idx_2_ss) return _CONTINUE; return _FIRST_SIDE; } } return _SECOND_SIDE; } return _FIRST_SIDE; } // _SECOND_SIDE if (_enumerator.get() == 0) { BaseMolecule &src_mol_1 = _context._query->getBaseMolecule(_selected_molecule_1); Molecule &src_mol_2 = _context._target.getMolecule(_selected_molecule_2); int src_aam_1 = 0; int src_aam_2 = 0; Array<int> &prev_core_1 = _context._matchers[_context._matchers.size() - 2]->_current_core_1; for (int i = src_mol_1.vertexBegin(); i < src_mol_1.vertexEnd(); i = src_mol_1.vertexNext(i)) if (prev_core_1[i] >= 0) { src_aam_1 = _context._query->getAAM(_selected_molecule_1, i); src_aam_2 = _context._target.getAAM(_selected_molecule_2, prev_core_1[i]); break; } BaseMolecule &mol_1 = _context._query->getBaseMolecule(_current_molecule_1); Molecule &mol_2 = _context._target.getMolecule(_current_molecule_2); int first_idx_1 = _context._query->findAtomByAAM(_current_molecule_1, src_aam_1); int first_idx_2 = _context._target.findAtomByAAM(_current_molecule_2, src_aam_2); // init embedding enumerator context _initEnumerator(mol_1, mol_2); if (!_enumerator->fix(first_idx_1, first_idx_2)) return _NO_WAY; _enumerator->processStart(); } if (!_enumerator->processNext()) return _NO_WAY; return _FIRST_SIDE; } bool BaseReactionSubstructureMatcher::_Matcher::_initEnumerator (BaseMolecule &mol_1, Molecule &mol_2) { // init embedding enumerator context _enumerator.create(mol_2); _enumerator->cb_match_edge = _matchBonds; _enumerator->cb_match_vertex = _matchAtoms; _enumerator->cb_edge_add = _addBond; _enumerator->cb_vertex_remove = _removeAtom; _enumerator->cb_embedding = _embedding; if (mol_1.isQueryMolecule() && _context.use_aromaticity_matcher && AromaticityMatcher::isNecessary(mol_1.asQueryMolecule())) _am.reset(new AromaticityMatcher(mol_1.asQueryMolecule(), mol_2, _context.arom_options)); else _am.reset(0); _enumerator->userdata = this; _enumerator->setSubgraph(mol_1); if (_context.prepare_ee != 0) { if (!_context.prepare_ee(_enumerator.ref(), mol_1, mol_2, _context.context)) return false; } return true; } bool BaseReactionSubstructureMatcher::_Matcher::addPair (int mol1_idx, int mol2_idx, const Array<int> &core1, const Array<int> &core2, bool from_first_side) { _selected_molecule_1 = mol1_idx; _selected_molecule_2 = mol2_idx; _mapped_aams.clear(); BaseMolecule &mol1 = _context._query->getBaseMolecule(mol1_idx); if (from_first_side) { int i; for (i = mol1.vertexBegin(); i < mol1.vertexEnd(); i = mol1.vertexNext(i)) if (core1[i] >= 0) { int aam1 = _context._query->getAAM(mol1_idx, i); int aam2 = _context._target.getAAM(mol2_idx, core1[i]); int *aam; if (aam1 > 0 && aam2 > 0) { if ((aam = _context._aam_core_first_side.at2(aam1)) == 0) { _context._aam_core_first_side.insert(aam1, aam2); _mapped_aams.push(aam1); } else if (*aam != aam2) { while (_mapped_aams.size() > 0) _context._aam_core_first_side.remove(_mapped_aams.pop()); return false; } } } } if (_mode == _SECOND_SIDE) { int first_aam_1 = 0; int first_aam_2 = 0; int i; for (i = mol1.vertexBegin(); i < mol1.vertexEnd(); i = mol1.vertexNext(i)) if (core1[i] >= 0) { first_aam_1 = _context._query->getAAM(mol1_idx, i); first_aam_2 = _context._target.getAAM(mol2_idx, core1[i]); break; } _current_molecule_1 = _context._aam_to_second_side_1.at(first_aam_1); _current_molecule_2 = _context._aam_to_second_side_2.at(first_aam_2); } _context._molecule_core_1[mol1_idx] = mol2_idx; _context._molecule_core_2[mol2_idx] = mol1_idx; return true; } void BaseReactionSubstructureMatcher::_Matcher::restore() { _context._molecule_core_1[_selected_molecule_1] = -1; _context._molecule_core_2[_selected_molecule_2] = -1; while (_mapped_aams.size() > 0) _context._aam_core_first_side.remove(_mapped_aams.pop()); } int BaseReactionSubstructureMatcher::_Matcher::_embedding (Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata) { BaseReactionSubstructureMatcher::_Matcher &self = *(BaseReactionSubstructureMatcher::_Matcher *)userdata; QueryMolecule &query = (QueryMolecule &)subgraph; Molecule &target = (Molecule &)supergraph; if (self.match_stereo) { if (!MoleculeStereocenters::checkSub(query.stereocenters, target.stereocenters, core_sub, false)) return 1; if (!MoleculeCisTrans::checkSub(query, target, core_sub)) return 1; } // Check possible aromatic configuration if (self._am.get() != 0) { if (!self._am->match(core_sub, core_super)) return 1; } self._current_core_1.copy(core_sub, subgraph.vertexEnd()); self._current_core_2.copy(core_super, supergraph.vertexEnd()); return 0; } bool BaseReactionSubstructureMatcher::_Matcher::_matchAtoms (Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata) { BaseReactionSubstructureMatcher::_Matcher *self = (BaseReactionSubstructureMatcher::_Matcher *)userdata; if (self->_context.match_atoms != 0 && !self->_context.match_atoms( *self->_context._query, self->_context._target, self->_current_molecule_1, sub_idx, self->_current_molecule_2, super_idx, self->_context.context)) return false; if (self->_mode == _SECOND_SIDE) { int *aam, aam1, aam2; aam1 = self->_context._query->getAAM(self->_current_molecule_1, sub_idx); if (aam1 != 0) { aam2 = self->_context._target.getAAM(self->_current_molecule_2, super_idx); if (aam2 != 0) if ((aam = self->_context._aam_core_first_side.at2(aam1)) != 0 && *aam != aam2) return false; } } if (self->_context._query_nei_counters != 0 && self->_context._target_nei_counters != 0) { const MoleculeAtomNeighbourhoodCounters &mol_count1 = self->_context._query_nei_counters->getCounters(self->_current_molecule_1); const MoleculeAtomNeighbourhoodCounters &mol_count2 = self->_context._target_nei_counters->getCounters(self->_current_molecule_2); if (!mol_count1.testSubstructure(mol_count2, sub_idx, super_idx, true)) return false; } int sub_atom_inv = self->_context._query->getInversion(self->_current_molecule_1, sub_idx); int super_atom_inv = self->_context._target.getInversion(self->_current_molecule_2, super_idx); if (sub_atom_inv != STEREO_UNMARKED && sub_atom_inv != super_atom_inv) return false; return true; } bool BaseReactionSubstructureMatcher::_Matcher::_matchBonds (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { BaseReactionSubstructureMatcher::_Matcher *self = (BaseReactionSubstructureMatcher::_Matcher *)userdata; if (self->_context.match_bonds != 0 && !self->_context.match_bonds( *self->_context._query, self->_context._target, self->_current_molecule_1, sub_idx, self->_current_molecule_2, super_idx, self->_am.get(), self->_context.context)) return false; return true; } void BaseReactionSubstructureMatcher::_Matcher::_removeAtom (Graph &subgraph, int sub_idx, void *userdata) { BaseReactionSubstructureMatcher::_Matcher *self = (BaseReactionSubstructureMatcher::_Matcher *)userdata; if (self->_context.remove_atom != 0) self->_context.remove_atom((BaseMolecule &)subgraph, sub_idx, self->_am.get()); } void BaseReactionSubstructureMatcher::_Matcher::_addBond (Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata) { BaseReactionSubstructureMatcher::_Matcher *self = (BaseReactionSubstructureMatcher::_Matcher *)userdata; if (self->_context.add_bond != 0) self->_context.add_bond((BaseMolecule &)subgraph, (Molecule &)supergraph, sub_idx, super_idx, self->_am.get()); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/canonical_rsmiles_saver.cpp����������������������������������������0000664�0000000�0000000�00000011140�12710376503�0024162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/canonical_rsmiles_saver.h" #include "base_cpp/output.h" #include "molecule/smiles_saver.h" #include "reaction/reaction.h" #include "reaction/rsmiles_saver.h" #include "molecule/canonical_smiles_saver.h" using namespace indigo; IMPL_ERROR(CanonicalRSmilesSaver, "canonical SMILES saver for reactions"); CanonicalRSmilesSaver::CanonicalRSmilesSaver (Output &output) : RSmilesSaver(output) { } CanonicalRSmilesSaver::~CanonicalRSmilesSaver () { } void CanonicalRSmilesSaver::saveReaction(Reaction &reaction_) { int j; QS_DEF(Reaction, reaction); reaction.clear(); reaction.name.copy(reaction_.name); if (reaction_.reactantsCount()) { j = reaction.addReactant(); Molecule &mol = reaction.getMolecule(j); Array<int> &aamArray = reaction.getAAMArray(j); for (auto i : reaction_.reactants) { Array<int> &aamArray_ = reaction_.getAAMArray(i); mol.mergeWithMolecule(reaction_.getMolecule(i), 0); aamArray.reserve(aamArray.size() + aamArray_.size()); for (int i = 0; i < aamArray_.size(); ++i) { aamArray.push(); aamArray.at(aamArray.size() - 1) = aamArray_.at(i); } } } if (reaction_.catalystCount()) { j = reaction.addCatalyst(); Molecule &mol = reaction.getMolecule(j); Array<int> &aamArray = reaction.getAAMArray(j); for (auto i : reaction_.catalysts) { Array<int> &aamArray_ = reaction_.getAAMArray(i); mol.mergeWithMolecule(reaction_.getMolecule(i), 0); aamArray.reserve(aamArray.size() + aamArray_.size()); for (int i = 0; i < aamArray_.size(); ++i) { aamArray.push(); aamArray.at(aamArray.size() - 1) = aamArray_.at(i); } } } if (reaction_.productsCount()) { j = reaction.addProduct(); Molecule &mol = reaction.getMolecule(j); Array<int> &aamArray = reaction.getAAMArray(j); for (auto i : reaction_.products) { Array<int> &aamArray_ = reaction_.getAAMArray(i); mol.mergeWithMolecule(reaction_.getMolecule(i), 0); aamArray.reserve(aamArray.size() + aamArray_.size()); for (int i = 0; i < aamArray_.size(); ++i) { aamArray.push(); aamArray.at(aamArray.size() - 1) = aamArray_.at(i); } } } _brxn = &reaction; _qrxn = 0; _rxn = &reaction; _saveReaction(); } void CanonicalRSmilesSaver::_saveReaction() { _written_atoms.clear(); _written_bonds.clear(); _ncomp.clear(); _comma = false; CanonicalSmilesSaver moleculeSaver(_output); // Invariant: there are only one molecule for each part of the reaction. Thus we can use _brxn->xxxBegin instead of the loop. // The only exception is catalyst. There could be zero catalysts. if (_brxn->reactantsCount()) _writeMolecule(_brxn->reactantBegin(), moleculeSaver); _output.writeString(">"); if (_brxn->catalystCount()) _writeMolecule(_brxn->catalystBegin(), moleculeSaver); _output.writeString(">"); if (_brxn->productsCount()) _writeMolecule(_brxn->productBegin(), moleculeSaver); _writeFragmentsInfo(); _writeStereogroups(); _writeRadicals(); _writePseudoAtoms(); _writeHighlighting(); if (_comma) _output.writeChar('|'); } void CanonicalRSmilesSaver::_writeMolecule(int i, CanonicalSmilesSaver &saver) { int j; saver.initial_atom_atom_mapping = &_brxn->getAAMArray(i); saver.smarts_mode = smarts_mode; if (_rxn != 0) saver.saveMolecule(_rxn->getMolecule(i)); //else // saver.saveQueryMolecule(_qrxn->getQueryMolecule(i)); _ncomp.push(saver.writtenComponents()); const Array<int> &atoms = saver.writtenAtoms(); for (j = 0; j < atoms.size(); j++) { _Idx &idx = _written_atoms.push(); idx.mol = i; idx.idx = atoms[j]; } const Array<int> &bonds = saver.writtenBonds(); for (j = 0; j < bonds.size(); j++) { _Idx &idx = _written_bonds.push(); idx.mol = i; idx.idx = bonds[j]; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/crf_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000010443�12710376503�0021402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/crf_loader.h" #include "base_cpp/scanner.h" #include "molecule/cmf_loader.h" #include "reaction/reaction.h" #include "reaction/crf_common.h" using namespace indigo; IMPL_ERROR(CrfLoader, "CRF loader"); void CrfLoader::_init () { xyz_scanner = 0; version = 2; } CrfLoader::CrfLoader (LzwDict &dict, Scanner &scanner) : _scanner(scanner) { _dict = &dict; _decoder.create(dict, scanner); _init(); } CrfLoader::CrfLoader (Scanner &scanner) : _scanner(scanner) { _dict = 0; _init(); } void CrfLoader::loadReaction (Reaction &reaction) { int i; int nreactants = _scanner.readPackedUInt(); int nproducts = _scanner.readPackedUInt(); byte flags = _scanner.readByte(); int ncatalyst = 0; if (flags & CrfFeatureFlags::CRF_CATALYST) ncatalyst = _scanner.readPackedUInt(); reaction.clear(); _bond_rc_flags = 0; _atom_stereo_flags = 0; _aam = 0; bool have_aam = (flags != 0); for (i = 0; i < nreactants; i++) { int index = reaction.addReactant(); _loadReactionMolecule(reaction, index, have_aam); } for (i = 0; i < nproducts; i++) { int index = reaction.addProduct(); _loadReactionMolecule(reaction, index, have_aam); } for (i = 0; i < ncatalyst; i++) { int index = reaction.addCatalyst(); _loadReactionMolecule(reaction, index, have_aam); } } void CrfLoader::_loadReactionMolecule (Reaction &reaction, int index, bool have_aam) { _bond_rc_flags = &reaction.getReactingCenterArray(index); _atom_stereo_flags = &reaction.getInversionArray(index); if (have_aam) _aam = &reaction.getAAMArray(index); _loadMolecule(reaction.getMolecule(index)); } void CrfLoader::_loadMolecule (Molecule &molecule) { Obj<CmfLoader> loader; int i; if (_decoder.get() != 0) loader.create(_decoder.ref()); else loader.create(_scanner); QS_DEF(Array<int>, atom_flags); QS_DEF(Array<int>, bond_flags); loader->atom_flags = &atom_flags; loader->bond_flags = &bond_flags; loader->version = version; loader->loadMolecule(molecule); bool has_mapping = loader->has_mapping; if (_atom_stereo_flags != 0) { _atom_stereo_flags->clear_resize(molecule.vertexCount()); _atom_stereo_flags->zerofill(); for (i = 0; i < molecule.vertexCount(); i++) { int idx = i; if (has_mapping) idx = loader->inv_atom_mapping_to_restore[i]; if (atom_flags[i] & 1) _atom_stereo_flags->at(idx) |= STEREO_RETAINS; if (atom_flags[i] & 2) _atom_stereo_flags->at(idx) |= STEREO_INVERTS; } } if (_bond_rc_flags != 0) { _bond_rc_flags->clear_resize(molecule.edgeCount()); _bond_rc_flags->zerofill(); for (i = 0; i < molecule.edgeCount(); i++) { int idx = i; if (has_mapping) idx = loader->inv_bond_mapping_to_restore[i]; if (bond_flags[i] & 1) _bond_rc_flags->at(idx) |= RC_UNCHANGED; if (bond_flags[i] & 2) _bond_rc_flags->at(idx) |= RC_MADE_OR_BROKEN; if (bond_flags[i] & 4) _bond_rc_flags->at(idx) |= RC_ORDER_CHANGED; } } if (_aam != 0) { _aam->clear_resize(molecule.vertexCount()); _aam->zerofill(); for (i = 0; i < molecule.vertexCount(); i++) { int value; if (_decoder.get() != 0) value = _decoder->get(); else value = _scanner.readByte(); int idx = i; if (has_mapping) idx = loader->inv_atom_mapping_to_restore[i]; _aam->at(idx) = value - 1; } } if (xyz_scanner != 0) loader->loadXyz(*xyz_scanner); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/crf_saver.cpp������������������������������������������������������0000664�0000000�0000000�00000011325�12710376503�0021254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "reaction/crf_saver.h" #include "lzw/lzw_encoder.h" #include "molecule/cmf_saver.h" #include "base_cpp/output.h" #include "molecule/cmf_symbol_codes.h" #include "reaction/crf_common.h" using namespace indigo; IMPL_ERROR(CrfSaver, "CRF saver"); void CrfSaver::_init () { xyz_output = 0; save_bond_dirs = false; save_highlighting = false; save_mapping = false; } CrfSaver::CrfSaver (LzwDict &dict, Output &output) : _output(output) { if (!dict.isInitialized()) dict.init(CMF_ALPHABET_SIZE, CMF_BIT_CODE_SIZE); _encoder.create(dict, output); _init(); } CrfSaver::CrfSaver (Output &output) : _output(output) { _init(); } void CrfSaver::saveReaction (Reaction &reaction) { _writeReactionInfo(reaction); int i; _atom_stereo_flags = 0; _bond_rc_flags = 0; _aam = 0; for (i = reaction.reactantBegin(); i < reaction.reactantEnd(); i = reaction.reactantNext(i)) _writeReactionMolecule(reaction, i); for (i = reaction.productBegin(); i < reaction.productEnd(); i = reaction.productNext(i)) _writeReactionMolecule(reaction, i); if (reaction.catalystCount() > 0) { for (i = reaction.catalystBegin(); i < reaction.catalystEnd(); i = reaction.catalystNext(i)) _writeReactionMolecule(reaction, i); } if (_encoder.get() != 0) _encoder->finish(); } void CrfSaver::_writeReactionMolecule (Reaction &reaction, int i) { _atom_stereo_flags = reaction.getInversionArray(i).ptr(); _bond_rc_flags = reaction.getReactingCenterArray(i).ptr(); _aam = reaction.getAAMArray(i).ptr(); _writeMolecule(reaction.getMolecule(i)); } void CrfSaver::_writeMolecule (Molecule &molecule) { Obj<CmfSaver> saver; int i; if (_encoder.get() != 0) saver.create(_encoder.ref()); else saver.create(_output); QS_DEF(Array<int>, atom_flags); QS_DEF(Array<int>, bond_flags); if (_atom_stereo_flags != 0) { atom_flags.clear_resize(molecule.vertexEnd()); atom_flags.zerofill(); for (i = molecule.vertexBegin(); i != molecule.vertexEnd(); i = molecule.vertexNext(i)) if (_atom_stereo_flags[i] == STEREO_RETAINS) atom_flags[i] = 1; else if (_atom_stereo_flags[i] == STEREO_INVERTS) atom_flags[i] = 2; saver->atom_flags = atom_flags.ptr(); } if (_bond_rc_flags != 0) { bond_flags.clear_resize(molecule.edgeEnd()); bond_flags.zerofill(); for (i = molecule.edgeBegin(); i != molecule.edgeEnd(); i = molecule.edgeNext(i)) { if (_bond_rc_flags[i] & RC_UNCHANGED) bond_flags[i] |= 1; if (_bond_rc_flags[i] & RC_MADE_OR_BROKEN) bond_flags[i] |= 2; if (_bond_rc_flags[i] & RC_ORDER_CHANGED) bond_flags[i] |= 4; } saver->bond_flags = bond_flags.ptr(); } saver->save_bond_dirs = save_bond_dirs; saver->save_highlighting = save_highlighting; saver->save_mapping = save_mapping; saver->saveMolecule(molecule); if (_aam != 0) _writeAam(_aam, saver->getAtomSequence()); if (xyz_output != 0) { if (xyz_output == &_output && _encoder.get() != 0) _encoder->finish(); saver->saveXyz(*xyz_output); if (xyz_output == &_output && _encoder.get() != 0) _encoder->start(); } } void CrfSaver::_writeReactionInfo (Reaction &reaction) { _output.writePackedUInt(reaction.reactantsCount()); _output.writePackedUInt(reaction.productsCount()); byte features = CrfFeatureFlags::CRF_AAM; if (reaction.catalystCount() > 0) features |= CrfFeatureFlags::CRF_CATALYST; _output.writeByte(features); if (reaction.catalystCount() > 0) _output.writePackedUInt(reaction.catalystCount()); } void CrfSaver::_writeAam (const int *aam, const Array<int> &sequence) { int i; for (i = 0; i < sequence.size(); i++) { int value = aam[sequence[i]] + 1; if (value < 1 || value >= CMF_ALPHABET_SIZE) throw Error("bad AAM value: %d", value); if (_encoder.get() != 0) _encoder->send(value); else _output.writeByte(value); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/icr_loader.cpp�����������������������������������������������������0000664�0000000�0000000�00000003343�12710376503�0021406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/icr_loader.h" #include "reaction/icr_saver.h" #include "base_cpp/scanner.h" #include "reaction/crf_loader.h" #include "reaction/reaction.h" #include "molecule/icm_common.h" using namespace indigo; IMPL_ERROR(IcrLoader, "ICR loader"); IcrLoader::IcrLoader (Scanner &scanner) : _scanner(scanner) { } void IcrLoader::loadReaction (Reaction &reaction) { char id[3]; _scanner.readCharsFix(3, id); int version = -1; if (strncmp(id, IcrSaver::VERSION2, 3) == 0) version = 2; else if (strncmp(id, IcrSaver::VERSION1, 3) == 0) version = 1; else throw Error("expected '%s' or '%s', got %.*s. Resave your reaction with new format.", IcrSaver::VERSION1, IcrSaver::VERSION2, 3, id); char bits = _scanner.readChar(); bool have_xyz = ((bits & ICM_XYZ) != 0); bool have_bond_dirs = ((bits & ICM_BOND_DIRS) != 0); CrfLoader loader(_scanner); if (have_xyz) loader.xyz_scanner = &_scanner; loader.version = version; loader.loadReaction(reaction); if (have_xyz) if (!have_bond_dirs) reaction.markStereocenterBonds(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/icr_saver.cpp������������������������������������������������������0000664�0000000�0000000�00000003242�12710376503�0021256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "reaction/icr_saver.h" #include "reaction/crf_saver.h" #include "molecule/icm_common.h" using namespace indigo; const char* IcrSaver::VERSION2 = "IR2"; const char* IcrSaver::VERSION1 = "ICR"; IMPL_ERROR(IcrSaver, "ICR saver"); bool IcrSaver::checkVersion (const char *prefix) { return strncmp(prefix, VERSION1, 3) == 0 || strncmp(prefix, VERSION2, 3) == 0; } IcrSaver::IcrSaver (Output &output) : _output(output) { save_xyz = false; save_bond_dirs = false; save_highlighting = false; save_ordering = false; } void IcrSaver::saveReaction (Reaction &reaction) { _output.writeString(VERSION2); int features = 0; if (save_xyz) features |= ICM_XYZ; if (save_bond_dirs) features |= ICM_BOND_DIRS; _output.writeChar(features); CrfSaver saver(_output); if (save_xyz) saver.xyz_output = &_output; saver.save_bond_dirs = save_bond_dirs; saver.save_highlighting = save_highlighting; saver.save_mapping = save_ordering; saver.saveReaction(reaction); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/query_reaction.cpp�������������������������������������������������0000664�0000000�0000000�00000015115�12710376503�0022334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/query_reaction.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" using namespace indigo; QueryReaction::QueryReaction () { } QueryReaction::~QueryReaction () { } void QueryReaction::clear () { BaseReaction::clear(); _exactChanges.clear(); _ignorableAAM.clear(); } QueryMolecule & QueryReaction::getQueryMolecule (int index) { return (QueryMolecule &)getBaseMolecule(index); } Array<int> & QueryReaction::getExactChangeArray (int index) { return _exactChanges[index]; } int QueryReaction::getExactChange (int index, int atom) { return _exactChanges[index][atom]; } void QueryReaction::_addedBaseMolecule (int idx, int side, BaseMolecule &mol) { BaseReaction::_addedBaseMolecule(idx, side, mol); _exactChanges.expand(idx + 1); _exactChanges[idx].clear_resize(mol.vertexEnd()); _exactChanges[idx].zerofill(); _ignorableAAM.expand(idx + 1); _ignorableAAM[idx].clear_resize(mol.vertexEnd()); _ignorableAAM[idx].zerofill(); } void QueryReaction::makeTransposedForSubstructure (QueryReaction &other) { QS_DEF(Array<int>, transposition); clear(); for (int i = other.begin(); i < other.end(); i = other.next(i)) { other._transposeMoleculeForSubstructure(i, transposition); int index = _allMolecules.add(new QueryMolecule()); QueryMolecule &qmol = *(QueryMolecule *)_allMolecules[index]; qmol.makeSubmolecule(other.getQueryMolecule(i), transposition, 0); _addedBaseMolecule(index, other._types[i], qmol); for (int j = 0; j < transposition.size(); j++) { getAAMArray(index).at(j) = other.getAAM(i, transposition[j]); getInversionArray(index).at(j) = other.getInversion(i, transposition[j]); getExactChangeArray(index).at(j) = other.getExactChange(i, transposition[j]); } for (int j = qmol.edgeBegin(); j != qmol.edgeEnd(); j = qmol.edgeNext(j)) { const Edge &edge = getBaseMolecule(index).getEdge(j); int edge_idx = other.getBaseMolecule(i).findEdgeIndex(transposition[edge.beg], transposition[edge.end]); getReactingCenterArray(index).at(j) = other.getReactingCenter(i, edge_idx); } } } void QueryReaction::_transposeMoleculeForSubstructure (int index, Array<int> &transposition) { QS_DEF(Array<int>, has_reacting_info); QueryMolecule &mol = *(QueryMolecule *)_allMolecules[index]; Array<int> &aam = getAAMArray(index); Array<int> &rc = getReactingCenterArray(index); Array<int> &inv = getInversionArray(index); Array<int> &ex = getExactChangeArray(index); has_reacting_info.clear_resize(mol.vertexEnd()); has_reacting_info.zerofill(); transposition.clear(); for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (aam[i] > 0) has_reacting_info[i] += 4; if (inv[i] > 0 || ex[i] > 0) has_reacting_info[i] += 1; transposition.push(i); } for (int i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) if (rc[i] > 0) { const Edge &edge = mol.getEdge(i); has_reacting_info[edge.beg] += 2; has_reacting_info[edge.end] += 2; } _SortingContext context(mol, has_reacting_info); transposition.qsort(_compare, &context); } int QueryReaction::_compare (int &i1, int &i2, void *c) { _SortingContext &context = *(_SortingContext *) c; bool is_pseudo1 = context.m.isPseudoAtom(i1); bool is_pseudo2 = context.m.isPseudoAtom(i2); if (is_pseudo1 != is_pseudo2) { if (is_pseudo1) return -1; return 1; } // Compare by AAM, reacting centers and other reacting flags int res = context.rdata[i2] - context.rdata[i1]; if (res != 0 || is_pseudo1) return res; // Compare by atom frequency int labels_by_freq[] = {ELEM_C, ELEM_H, ELEM_O, ELEM_N, ELEM_P, ELEM_F, ELEM_S, ELEM_Si, ELEM_Cl, ELEM_Br, ELEM_I, ELEM_At}; int label1 = context.m.getAtomNumber(i1); int label2 = context.m.getAtomNumber(i2); int idx1, idx2; for (idx1 = 0; idx1 < (int) NELEM(labels_by_freq); idx1++) if (label1 == labels_by_freq[idx1]) break; for (idx2 = 0; idx2 < (int) NELEM(labels_by_freq); idx2++) if (label2 == labels_by_freq[idx2]) break; res = idx2 - idx1; if (res != 0) return res; // compare by degree return context.m.getVertex(i2).degree() - context.m.getVertex(i1).degree(); } int QueryReaction::_addBaseMolecule (int side) { int idx = _allMolecules.add(new QueryMolecule()); _addedBaseMolecule(idx, side, *_allMolecules[idx]); return idx; } bool QueryReaction::aromatize(const AromaticityOptions &options) { bool arom_found = false; for (int i = begin(); i < end(); i = next(i)) { arom_found |= QueryMoleculeAromatizer::aromatizeBonds(*(QueryMolecule *)_allMolecules[i], options); } return arom_found; } bool QueryReaction::dearomatize(const AromaticityOptions &options) { throw Error("Dearomatization not implemented"); } void QueryReaction::_clone (BaseReaction &other, int index, int i, ObjArray< Array<int> >* mol_mappings) { BaseMolecule& rmol = other.getBaseMolecule(i); //for query getExactChangeArray(index).resize(other.asQueryReaction().getExactChangeArray(i).size()); if(getExactChangeArray(index).size() > 0) { for(int j = rmol.vertexBegin(); j < rmol.vertexEnd(); j = rmol.vertexNext(j)) { getExactChangeArray(index).at(j) = other.asQueryReaction().getExactChange(i, mol_mappings->at(i)[j]); } } } QueryReaction & QueryReaction::asQueryReaction () { return *this; } bool QueryReaction::isQueryReaction () { return true; } BaseReaction * QueryReaction::neu () { return new QueryReaction(); } Array<int> & QueryReaction::getIgnorableAAMArray (int index) { return _ignorableAAM[index]; } int QueryReaction::getIgnorableAAM (int index, int atom) { return _ignorableAAM[index][atom]; } void QueryReaction::optimize () { for (int i = begin(); i < end(); i = next(i)) _allMolecules[i]->asQueryMolecule().optimize(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction.cpp�������������������������������������������������������0000664�0000000�0000000�00000006657�12710376503�0021122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_dearom.h" #include "reaction/reaction_automapper.h" using namespace indigo; IMPL_ERROR(Reaction, "reaction"); Reaction::Reaction() { } Reaction::~Reaction () { } void Reaction::clear () { BaseReaction::clear(); } Molecule & Reaction::getMolecule (int index) { return getBaseMolecule(index).asMolecule(); } int Reaction::_addBaseMolecule (int side) { int idx = _allMolecules.add(new Molecule()); _addedBaseMolecule(idx, side, *_allMolecules[idx]); return idx; } void Reaction::saveBondOrders (Reaction& reaction, ObjArray< Array<int> > &bond_types) { while (bond_types.size() < reaction.end()) bond_types.push(); int i; for (i = reaction.begin(); i != reaction.end(); i = reaction.next(i)) { Molecule::saveBondOrders(reaction.getMolecule(i), bond_types[i]); } } void Reaction::loadBondOrders (Reaction& reaction, ObjArray< Array<int> > &bond_types) { int i; for (i = reaction.begin(); i != reaction.end(); i = reaction.next(i)) { Molecule::loadBondOrders(reaction.getMolecule(i), bond_types[i]); } } bool Reaction::aromatize(const AromaticityOptions &options) { bool arom_found = false; for (int i = begin(); i < end(); i = next(i)) { arom_found |= MoleculeAromatizer::aromatizeBonds(*(Molecule *)_allMolecules[i], options); } return arom_found; } bool Reaction::dearomatize(const AromaticityOptions &options) { bool all_dearomatized = true; for (int i = begin(); i < end(); i = next(i)) { all_dearomatized &= MoleculeDearomatizer::dearomatizeMolecule(*(Molecule *)_allMolecules[i], options); } return all_dearomatized; } Reaction & Reaction::asReaction () { return *this; } BaseReaction * Reaction::neu () { return new Reaction(); } void Reaction::checkForConsistency (Reaction &rxn) { int i; for (i = rxn.begin(); i != rxn.end(); i = rxn.next(i)) Molecule::checkForConsistency(rxn.getMolecule(i)); } void Reaction::unfoldHydrogens () { QS_DEF(Array<int>, markers); int i, j; for (i = begin(); i != end(); i = next(i)) { Molecule &mol = getMolecule(i); mol.unfoldHydrogens(&markers, -1); _atomAtomMapping[i].expand(markers.size()); _inversionNumbers[i].expand(markers.size()); for (j = mol.vertexBegin(); j != mol.vertexEnd(); j = mol.vertexNext(j)) if (markers[j]) { _atomAtomMapping[i][j] = 0; _inversionNumbers[i][j] = 0; int edge_idx = mol.getVertex(j).neiEdge(mol.getVertex(j).neiBegin()); _reactingCenters[i].expand(edge_idx + 1); _reactingCenters[i][edge_idx] = 0; } } } ���������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_auto_loader.cpp�������������������������������������������0000664�0000000�0000000�00000012532�12710376503�0023465�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_auto_loader.h" #include "reaction/rsmiles_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/icr_loader.h" #include "reaction/icr_saver.h" #include "gzip/gzip_scanner.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule_auto_loader.h" #include "reaction/reaction_cml_loader.h" using namespace indigo; void ReactionAutoLoader::_init () { treat_x_as_pseudoatom = false; ignore_closing_bond_direction_mismatch = false; stereochemistry_options.reset(); ignore_noncritical_query_features = false; ignore_cistrans_errors = false; } IMPL_ERROR(ReactionAutoLoader, "reaction auto loader"); ReactionAutoLoader::ReactionAutoLoader (Scanner &scanner) { _scanner = &scanner; _own_scanner = false; _init(); } ReactionAutoLoader::ReactionAutoLoader (const Array<char> &arr) { _scanner = new BufferScanner(arr); _own_scanner = true; _init(); } ReactionAutoLoader::ReactionAutoLoader (const char *str) { _scanner = new BufferScanner(str); _own_scanner = true; _init(); } ReactionAutoLoader::~ReactionAutoLoader () { if (_own_scanner) delete _scanner; } void ReactionAutoLoader::loadReaction (Reaction &reaction) { _loadReaction(reaction, false); } void ReactionAutoLoader::loadQueryReaction (QueryReaction &reaction) { _loadReaction(reaction, true); } void ReactionAutoLoader::_loadReaction (BaseReaction &reaction, bool query) { // check fir GZip format if (_scanner->length() >= 2) { byte id[2]; int pos = _scanner->tell(); _scanner->readCharsFix(2, (char *)id); _scanner->seek(pos, SEEK_SET); if (id[0] == 0x1f && id[1] == 0x8b) { GZipScanner gzscanner(*_scanner); QS_DEF(Array<char>, buf); gzscanner.readAll(buf); ReactionAutoLoader loader2(buf); loader2.stereochemistry_options = stereochemistry_options; loader2.ignore_noncritical_query_features = ignore_noncritical_query_features; loader2.treat_x_as_pseudoatom = treat_x_as_pseudoatom; if (query) loader2.loadQueryReaction((QueryReaction &)reaction); else loader2.loadReaction((Reaction &)reaction); return; } } // check for MDLCT format { QS_DEF(Array<char>, buf); if (MoleculeAutoLoader::tryMDLCT(*_scanner, buf)) { BufferScanner scanner2(buf); RxnfileLoader loader(scanner2); loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; loader.stereochemistry_options = stereochemistry_options; loader.ignore_noncritical_query_features = ignore_noncritical_query_features; if (query) loader.loadQueryReaction((QueryReaction &)reaction); else loader.loadReaction((Reaction &)reaction); return; } } // check for ICM format if (_scanner->length() >= 4) { char id[3]; int pos = _scanner->tell(); _scanner->readCharsFix(3, id); _scanner->seek(pos, SEEK_SET); if (IcrSaver::checkVersion(id)) { if (query) throw Error("cannot load query reaction from ICR format"); IcrLoader loader(*_scanner); loader.loadReaction((Reaction &)reaction); return; } } // check for CML format { int pos = _scanner->tell(); _scanner->skipSpace(); if (_scanner->lookNext() == '<') { if (_scanner->findWord("<reaction")) { ReactionCmlLoader loader(*_scanner); loader.stereochemistry_options = stereochemistry_options; if (query) throw Error("CML queries not supported"); loader.loadReaction((Reaction &)reaction); return; } } _scanner->seek(pos, SEEK_SET); } // check for SMILES format if (Scanner::isSingleLine(*_scanner)) { RSmilesLoader loader(*_scanner); loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; loader.ignore_cistrans_errors = ignore_cistrans_errors; loader.stereochemistry_options = stereochemistry_options; if (query) loader.loadQueryReaction((QueryReaction &)reaction); else loader.loadReaction((Reaction &)reaction); } // default is Rxnfile format else { RxnfileLoader loader(*_scanner); loader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; loader.stereochemistry_options = stereochemistry_options; loader.ignore_noncritical_query_features = ignore_noncritical_query_features; if (query) loader.loadQueryReaction((QueryReaction &)reaction); else loader.loadReaction((Reaction &)reaction); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_automapper.cpp��������������������������������������������0000664�0000000�0000000�00000210300�12710376503�0023335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_automapper.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule_arom.h" #include "base_cpp/red_black.h" #include "molecule/elements.h" #include "base_cpp/auto_ptr.h" #include "graph/automorphism_search.h" #include "reaction/crf_saver.h" #include "molecule/molecule_neighbourhood_counters.h" using namespace indigo; IMPL_ERROR(ReactionAutomapper, "Reaction automapper"); ReactionAutomapper::ReactionAutomapper(BaseReaction& reaction): ignore_atom_charges(false), ignore_atom_valence(false), ignore_atom_isotopes(false), ignore_atom_radicals(false), cancellation(0), _initReaction(reaction), _maxMapUsed(0), _maxVertUsed(0), _maxCompleteMap(0), _mode(AAM_REGEN_DISCARD){ } void ReactionAutomapper::automap(int mode) { _mode = mode; QS_DEF(ObjArray< Array<int> >, mol_mappings); QS_DEF(Array<int>, react_mapping); /* * Set cancellation handler */ AAMCancellationWrapper canc_wrapper(cancellation); /* * Check input atom mapping (if any) */ if (mode != AAM_REGEN_DISCARD) _checkAtomMapping(true, false, false); /* * Clone reaction */ _createReactionCopy(react_mapping, mol_mappings); /* * Create AAM map */ _createReactionMap(); _setupReactionInvMap(react_mapping, mol_mappings); _considerDissociation(); _considerDimerization(); /* * Check output atom mapping */ _checkAtomMapping(false, true, false); } void ReactionAutomapper::_createReactionCopy(Array<int>& mol_mapping, ObjArray< Array<int> >& mappings) { _reactionCopy.reset(_initReaction.neu()); mol_mapping.clear(); mappings.clear(); int mol_idx = _initReaction.reactantBegin(); for (; mol_idx != _initReaction.reactantEnd(); mol_idx = _initReaction.reactantNext(mol_idx)) { _createMoleculeCopy(mol_idx, true, mol_mapping, mappings); } mol_idx = _initReaction.productBegin(); for (; mol_idx != _initReaction.productEnd(); mol_idx = _initReaction.productNext(mol_idx)) { _createMoleculeCopy(mol_idx, false, mol_mapping, mappings); } _reactionCopy->aromatize(arom_options); } void ReactionAutomapper::_createMoleculeCopy(int mol_idx, bool reactant, Array<int> &mol_mapping, ObjArray< Array<int> >& mappings) { QS_DEF(Array<int>, vertices_map); QS_DEF(Array<int>, vertices_to_clone); int cmol_idx, ncomp, edge_beg, edge_end, edge_idx; BaseReaction& reaction = _reactionCopy.ref(); BaseMolecule& mol = _initReaction.getBaseMolecule(mol_idx); /* * Calculate components */ ncomp = mol.countComponents(); const Array<int> &decomposition = mol.getDecomposition(); /* * Add each component as a separate molecule */ for (int comp_idx = 0; comp_idx < ncomp; ++comp_idx) { vertices_to_clone.clear(); for (int j = mol.vertexBegin(); j < mol.vertexEnd(); j = mol.vertexNext(j)) { if (decomposition[j] == comp_idx) vertices_to_clone.push(j); } if(reactant) cmol_idx = reaction.addReactant(); else cmol_idx = reaction.addProduct(); while (cmol_idx >= mol_mapping.size()) mol_mapping.push(-1); while (cmol_idx >= mappings.size()) mappings.push(); mol_mapping[cmol_idx] = mol_idx; BaseMolecule& cmol = reaction.getBaseMolecule(cmol_idx); /* * Create component clone */ cmol.makeSubmolecule(mol, vertices_to_clone, &vertices_map, 0); Array<int>& vertices_inv_map = mappings[cmol_idx]; vertices_inv_map.resize(cmol.vertexEnd()); _makeInvertMap(vertices_map, vertices_inv_map); /* * Fulfil AAM information */ Array<int>& aam_array = reaction.getAAMArray(cmol_idx); aam_array.resize(cmol.vertexEnd()); aam_array.zerofill(); for (int i = cmol.vertexBegin(); i != cmol.vertexEnd(); i = cmol.vertexNext(i)) { if(vertices_inv_map[i] < 0) throw Error("internal error: invalid clone for disconnected component"); aam_array[i] = _initReaction.getAAM(mol_idx, vertices_inv_map[i]); } /* * Fulfil inversion information */ Array<int>& inv_array = reaction.getInversionArray(cmol_idx); inv_array.resize(cmol.vertexEnd()); inv_array.zerofill(); for (int i = cmol.vertexBegin(); i != cmol.vertexEnd(); i = cmol.vertexNext(i)) { if(vertices_inv_map[i] < 0) throw Error("internal error: invalid clone for disconnected component"); inv_array[i] = _initReaction.getInversion(mol_idx, vertices_inv_map[i]); } /* * Fulfil Reacting centers information */ Array<int>& rc_array = reaction.getReactingCenterArray(cmol_idx); rc_array.resize(cmol.edgeEnd()); rc_array.zerofill(); for (int i = cmol.edgeBegin(); i != cmol.edgeEnd(); i = cmol.edgeNext(i)) { edge_beg = vertices_inv_map[cmol.getEdge(i).beg]; edge_end = vertices_inv_map[cmol.getEdge(i).end]; if(edge_beg < 0 || edge_end < 0) throw Error("internal error: invalid clone for disconnected component"); edge_idx = mol.findEdgeIndex(edge_beg, edge_end); if(edge_idx < 0) throw Error("internal error: invalid clone for disconnected component"); rc_array[i] = _initReaction.getReactingCenter(mol_idx, edge_idx); } } } void ReactionAutomapper::_makeInvertMap(Array<int>& map,Array<int>& invmap){ invmap.fffill(); for(int i = 0; i < map.size(); i++){ if(map[i] != -1){ invmap[map[i]] = i; } } } void ReactionAutomapper::_initMappings(BaseReaction& reaction){ int i,j; BaseReaction& copy_reaction = _reactionCopy.ref(); if(_mode == AAM_REGEN_ALTER || _mode == AAM_REGEN_DISCARD){ int current_map = 0; for (i = reaction.reactantBegin(); i < reaction.reactantEnd(); i = reaction.reactantNext(i)){ for (j = 0; j < reaction.getAAMArray(i).size(); j++){ ++current_map; reaction.getAAMArray(i).at(j) = current_map; } } _usedVertices.resize(current_map+1); _usedVertices.zerofill(); } if(_mode == AAM_REGEN_KEEP){ RedBlackSet<int> used_maps; int max_value = 0; for (i = reaction.reactantBegin(); i < reaction.reactantEnd(); i = reaction.reactantNext(i)){ for (j = 0; j < reaction.getAAMArray(i).size(); j++){ used_maps.find_or_insert(reaction.getAAM(i, j)); if(reaction.getAAM(i, j) > max_value) max_value = copy_reaction.getAAM(i, j); } } int new_size = used_maps.size(); int current_map = 0; for (i = reaction.reactantBegin(); i < reaction.reactantEnd(); i = reaction.reactantNext(i)){ for (j = 0; j < reaction.getAAMArray(i).size(); j++){ if(reaction.getAAM(i, j) == 0 ){ while(new_size == used_maps.size()){ ++current_map; used_maps.find_or_insert(current_map); } new_size = used_maps.size(); reaction.getAAMArray(i).at(j) = current_map; } } } if(current_map > max_value) max_value = current_map; _usedVertices.resize(max_value + 1); _usedVertices.zerofill(); } for (i = reaction.productBegin(); i < reaction.productEnd(); i = reaction.productNext(i)){ reaction.getAAMArray(i).zerofill(); } } void ReactionAutomapper::_createReactionMap(){ QS_DEF(ObjArray< Array<int> >, reactant_permutations); QS_DEF(Array<int>, product_mapping_tmp); BaseReaction& reaction = _reactionCopy.ref(); ReactionMapMatchingData react_map_match(reaction); react_map_match.createAtomMatchingData(); _initMappings(reaction); AutoPtr<BaseReaction> reaction_clone; reaction_clone.reset(reaction.neu()); /* * Create all possible permutations for reactants */ _createPermutations(reaction, reactant_permutations); for(int product = reaction.productBegin(); product < reaction.productEnd(); product = reaction.productNext(product)){ product_mapping_tmp.clear_resize(reaction.getAAMArray(product).size()); _maxMapUsed = 0; _maxVertUsed = 0; _maxCompleteMap = 0; for(int pmt = 0; pmt < reactant_permutations.size(); pmt++) { reaction_clone->clone(reaction, 0, 0, 0); /* * Apply new permutation */ int map_complete = _handleWithProduct(reactant_permutations[pmt], product_mapping_tmp, reaction_clone.ref(), product, react_map_match); /* * Collect statistic and choose the best mapping */ if(_chooseBestMapping(reaction, product_mapping_tmp, product, map_complete)) break; /* * Check for cancellation */ if(cancellation && cancellation->isCancelled()) break; } _usedVertices.zerofill(); for(int k = reaction.productBegin(); k <= product; k = reaction.productNext(k)){ for (int j = 0; j < reaction.getAAMArray(k).size(); j++){ int m = reaction.getAAM(k ,j); if(m > 0) _usedVertices[m] = 1; } } // _cleanReactants(reaction); } } void ReactionAutomapper::_cleanReactants(BaseReaction& reaction) { for(int react = reaction.reactantBegin(); react < reaction.reactantEnd(); react = reaction.reactantNext(react)) { BaseMolecule& rmol = reaction.getBaseMolecule(react); for(int vert = rmol.vertexBegin(); vert < rmol.vertexEnd();) { if(_usedVertices[reaction.getAAM(react, vert)]) { int next_vert = rmol.vertexNext(vert); rmol.removeAtom(vert); vert = next_vert; continue; } vert = rmol.vertexNext(vert); } } } int ReactionAutomapper::_handleWithProduct(const Array<int>& reactant_cons, Array<int>& product_mapping_tmp, BaseReaction& reaction, int product, ReactionMapMatchingData& react_map_match) { QS_DEF(Array<int>, matching_map); QS_DEF(Array<int>, rsub_map_in); QS_DEF(Array<int>, rsub_map_out); QS_DEF(Array<int>, vertices_to_remove); int map_complete = 0; BaseReaction& _reaction = _reactionCopy.ref(); BaseMolecule& product_cut = reaction.getBaseMolecule(product); /* *delete hydrogens */ vertices_to_remove.clear(); for(int k = product_cut.vertexBegin(); k < product_cut.vertexEnd(); k = product_cut.vertexNext(k)) if(product_cut.getAtomNumber(k) == ELEM_H) vertices_to_remove.push(k); product_cut.removeAtoms(vertices_to_remove); product_mapping_tmp.zerofill(); _usedVertices[0] = 0; for(int perm_idx = 0; perm_idx < reactant_cons.size(); perm_idx++){ int react = reactant_cons.at(perm_idx); int react_vsize = reaction.getBaseMolecule(react).vertexEnd(); rsub_map_in.resize(react_vsize); for(int k = 0; k < react_vsize; k++) rsub_map_in[k] = SubstructureMcs::UNMAPPED; bool map_exc = false; if(_mode != AAM_REGEN_DISCARD){ for(int m = product_cut.vertexBegin(); m < product_cut.vertexEnd(); m = product_cut.vertexNext(m)){ react_map_match.getAtomMap(product, react, m, &matching_map); for(int k = 0; k < matching_map.size();k++) { rsub_map_in[matching_map[k]] = m; map_exc = true; break; } } } if(!map_exc) rsub_map_in.clear(); RSubstructureMcs react_sub_mcs(reaction, react, product, *this); bool find_sub = react_sub_mcs.searchSubstructureReact(_reaction.getBaseMolecule(react), &rsub_map_in, &rsub_map_out); if (!find_sub) { react_sub_mcs.searchMaxCommonSubReact(&rsub_map_in, &rsub_map_out); } bool cur_used = false; for (int j = 0; j < rsub_map_out.size(); j++) { int v = rsub_map_out.at(j); if (v >= 0) { /* * Check delta Y exchange problem possibility */ if(!product_cut.hasVertex(v)) continue; cur_used = true; product_mapping_tmp[v] = reaction.getAAM(react, j); if (_usedVertices[product_mapping_tmp[v]] == 0) ++_usedVertices[0]; product_cut.removeAtom(v); } } if(!cur_used) ++map_complete; if(product_cut.vertexCount() == 0) { map_complete += reactant_cons.size() - perm_idx - 1; break; } } return map_complete; } bool ReactionAutomapper::_chooseBestMapping(BaseReaction& reaction, Array<int>& product_mapping, int product, int map_complete) { int map_used = 0, total_map_used; for (int map_idx = 0; map_idx < product_mapping.size(); ++map_idx) if (product_mapping[map_idx] > 0) ++map_used; bool map_u = map_used > _maxMapUsed; bool map_c = (map_used == _maxMapUsed) && (map_complete > _maxCompleteMap); bool map_v = (map_used == _maxMapUsed) && (map_complete == _maxCompleteMap) && (_usedVertices[0] > _maxVertUsed); if(map_u || map_c || map_v){ _maxMapUsed = map_used; _maxVertUsed = _usedVertices[0]; _maxCompleteMap = map_complete; reaction.getAAMArray(product).copy(product_mapping); /* * Check if map covers all a reaction molecules */ total_map_used = 0; for (int i = 1; i < _usedVertices.size(); ++i) { if(_usedVertices[i]) ++total_map_used; } if((total_map_used + _usedVertices[0]) >= (_usedVertices.size() - 1)) return true; } return false; } bool ReactionAutomapper::_checkAtomMapping(bool change_rc, bool change_aam, bool change_rc_null) { ReactionMapMatchingData map_match(_initReaction); map_match.createBondMatchingData(); QS_DEF(ObjArray< Array<int> >, bond_centers); QS_DEF(Array<int>, mapping); QS_DEF(Array<int>, v_mapping); QS_DEF(Array<int>, null_map); QS_DEF(Array<int>, rmol_map); AutoPtr<BaseReaction> reaction_copy_ptr; QS_DEF(ObjArray< Array<int> > , react_invmap); null_map.clear(); bool unchanged = true; bond_centers.clear(); for(int i = 0; i < _initReaction.end(); ++i) bond_centers.push(); for(int i = _initReaction.begin(); i < _initReaction.end(); i = _initReaction.next(i)) { bond_centers[i].resize(_initReaction.getBaseMolecule(i).edgeEnd()); bond_centers[i].zerofill(); } reaction_copy_ptr.reset(_initReaction.neu()); BaseReaction &reaction_copy = reaction_copy_ptr.ref(); reaction_copy.clone(_initReaction, &rmol_map, 0, &react_invmap); reaction_copy.aromatize(arom_options); for (int mol_idx = _initReaction.begin(); mol_idx != _initReaction.end(); mol_idx = _initReaction.next(mol_idx)) { BaseMolecule& rmol = _initReaction.getBaseMolecule(mol_idx); for (int vert = rmol.vertexBegin(); vert < rmol.vertexEnd(); vert = rmol.vertexNext(vert)) { if (_initReaction.getAAM(mol_idx, vert) > 0 && map_match.beginAtomMap(mol_idx, vert) >= map_match.endAtomMap()) { _initReaction.getAAMArray(mol_idx).at(vert) = 0; } } } /* * Initialize reaction substructure handler by null molecules since it will not be callable by itself */ RSubstructureMcs rsm(reaction_copy, *this); for (int mol_idx = _initReaction.begin(); mol_idx != _initReaction.end(); mol_idx = _initReaction.next(mol_idx)) { BaseMolecule& rmol = _initReaction.getBaseMolecule(mol_idx); for (int bond_idx = rmol.edgeBegin(); bond_idx < rmol.edgeEnd(); bond_idx = rmol.edgeNext(bond_idx)) { for(int opp_idx = map_match.beginMap(mol_idx); opp_idx < map_match.endMap(); opp_idx = map_match.nextMap(mol_idx, opp_idx)) { BaseMolecule& pmol = _initReaction.getBaseMolecule(opp_idx); map_match.getBondMap(mol_idx, opp_idx, bond_idx, &mapping); if(mapping.size() == 0) { int ve_beg = rmol.getEdge(bond_idx).beg; int ve_end = rmol.getEdge(bond_idx).end; map_match.getAtomMap(mol_idx, opp_idx, ve_beg, &v_mapping); if(v_mapping.size() > 0) { bool change_broken = true; for(int v_map = 0; v_map < v_mapping.size(); ++v_map) { const Vertex& end_vert = pmol.getVertex(v_mapping[v_map]); for (int nei_vert = end_vert.neiBegin(); nei_vert < end_vert.neiEnd(); nei_vert = end_vert.neiNext(nei_vert)) { int end_nei_vert = end_vert.neiVertex(nei_vert); if (_initReaction.getAAM(opp_idx, end_nei_vert) == 0 && RSubstructureMcs::atomConditionReact(rmol, pmol, 0, ve_end, end_nei_vert, &rsm)) { change_broken = false; break; } } } if (change_broken) bond_centers[mol_idx][bond_idx] |= RC_MADE_OR_BROKEN; } map_match.getAtomMap(mol_idx, opp_idx, ve_end, &v_mapping); if (v_mapping.size() > 0) { bool change_broken = true; for (int v_map = 0; v_map < v_mapping.size(); ++v_map) { const Vertex& beg_vert = pmol.getVertex(v_mapping[v_map]); for (int nei_vert = beg_vert.neiBegin(); nei_vert < beg_vert.neiEnd(); nei_vert = beg_vert.neiNext(nei_vert)) { int beg_nei_vert = beg_vert.neiVertex(nei_vert); if (_initReaction.getAAM(opp_idx, beg_nei_vert) == 0 && RSubstructureMcs::atomConditionReact(rmol, pmol, 0, ve_beg, beg_nei_vert, &rsm)) { change_broken = false; break; } } } if (change_broken) bond_centers[mol_idx][bond_idx] |= RC_MADE_OR_BROKEN; } } else { for(int i = 0; i < mapping.size(); ++i) { const Edge &r_edge = rmol.getEdge(bond_idx); const Edge &p_edge = pmol.getEdge(mapping[i]); int copy_mol_idx = rmol_map[mol_idx]; int copy_opp_idx = rmol_map[opp_idx]; BaseMolecule& cr_mol = reaction_copy.getBaseMolecule(copy_mol_idx); BaseMolecule& cp_mol = reaction_copy.getBaseMolecule(copy_opp_idx); int mol_edge_idx = cr_mol.findEdgeIndex(react_invmap.at(mol_idx)[r_edge.beg], react_invmap.at(mol_idx)[r_edge.end]); int opp_edge_idx = cp_mol.findEdgeIndex(react_invmap.at(opp_idx)[p_edge.beg], react_invmap.at(opp_idx)[p_edge.end]); bool react_arom = cr_mol.getBondOrder(mol_edge_idx) == BOND_AROMATIC; bool prod_arom = cp_mol.getBondOrder(opp_edge_idx) == BOND_AROMATIC; if (change_aam && (react_arom || prod_arom) ) { bond_centers[mol_idx][bond_idx] |= _initReaction.getReactingCenter(mol_idx, bond_idx); continue; } bool bond_cond_simple = RSubstructureMcs::bondConditionReactSimple(pmol, rmol, mapping[i], bond_idx, &rsm); if (bond_cond_simple || (react_arom && prod_arom)) bond_centers[mol_idx][bond_idx] |= RC_UNCHANGED; else bond_centers[mol_idx][bond_idx] |= RC_ORDER_CHANGED; if(bond_cond_simple && (react_arom != prod_arom)) bond_centers[mol_idx][bond_idx] |= RC_ORDER_CHANGED; } } } } } for (int mol_idx = _initReaction.begin(); mol_idx != _initReaction.end(); mol_idx = _initReaction.next(mol_idx)) { BaseMolecule& rmol = _initReaction.getBaseMolecule(mol_idx); for (int bond_idx = rmol.edgeBegin(); bond_idx < rmol.edgeEnd(); bond_idx = rmol.edgeNext(bond_idx)) { int rc_bond = _initReaction.getReactingCenter(mol_idx, bond_idx); if(rc_bond == RC_NOT_CENTER) rc_bond = RC_UNCHANGED; bool aam_bond = ((_initReaction.getAAM(mol_idx, rmol.getEdge(bond_idx).beg) > 0) && (_initReaction.getAAM(mol_idx, rmol.getEdge(bond_idx).end) > 0)) || change_rc_null; if (aam_bond && ((bond_centers[mol_idx][bond_idx] & ~rc_bond) || rc_bond == 0)) { if (!change_rc && !change_aam) { return false; } else if(change_rc) { _initReaction.getReactingCenterArray(mol_idx).at(bond_idx) = bond_centers[mol_idx][bond_idx]; unchanged = false; } else if(change_aam && rc_bond && _initReaction.getSideType(mol_idx)== Reaction::REACTANT) { //only rc != 0 0 can match on every type of the bond //only reactants rules (lazy users) null_map.push(_initReaction.getAAM(mol_idx, rmol.getEdge(bond_idx).beg)); null_map.push(_initReaction.getAAM(mol_idx, rmol.getEdge(bond_idx).end)); unchanged = false; } } } } /* * erase all wrong map */ if(change_aam) { for(int i = _initReaction.begin(); i < _initReaction.end(); i = _initReaction.next(i)) { BaseMolecule& rmol = _initReaction.getBaseMolecule(i); for(int atom = rmol.vertexBegin(); atom < rmol.vertexEnd(); atom = rmol.vertexNext(atom)) { for(int j = 0; j < null_map.size(); ++j) { if(null_map[j] == _initReaction.getAAM(i, atom)) _initReaction.getAAMArray(i).at(atom) = 0; } } } null_map.clear(); /* * Check for single aam for atoms was removed since mcs can be one atom */ // for(int i = _reaction.begin(); i < _reaction.end(); i = _reaction.next(i)) { // BaseMolecule& rmol = _reaction.getBaseMolecule(i); // for(int atom = rmol.vertexBegin(); atom < rmol.vertexEnd(); atom = rmol.vertexNext(atom)) { // const Vertex& vertex = rmol.getVertex(atom); // if(_reaction.getAAM(i ,atom) > 0 && vertex.degree() > 0) { // bool has_aam = false; // for(int nei = vertex.neiBegin(); nei < vertex.neiEnd(); nei = vertex.neiNext(nei)) { // int nei_atom = vertex.neiVertex(nei); // if(_reaction.getAAM(i, nei_atom) > 0) { // has_aam = true; // break; // } // } // if(!has_aam) // null_map.push(_reaction.getAAM(i, atom)); // } // } // } // for(int i = _reaction.begin(); i < _reaction.end(); i = _reaction.next(i)) { // BaseMolecule& rmol = _reaction.getBaseMolecule(i); // for(int atom = rmol.vertexBegin(); atom < rmol.vertexEnd(); atom = rmol.vertexNext(atom)) { // for(int j = 0; j < null_map.size(); ++j) { // if(null_map[j] == _reaction.getAAM(i, atom)) // _reaction.getAAMArray(i).at(atom) = 0; // } // } // } } return unchanged; } void ReactionAutomapper::_setupReactionMap(Array<int> &react_mapping, ObjArray< Array<int> >& mol_mappings){ int mol_idx,j,v; if(_mode == AAM_REGEN_KEEP) _usedVertices.zerofill(); BaseReaction& reaction_copy = _reactionCopy.ref(); for (mol_idx = _initReaction.productBegin(); mol_idx < _initReaction.productEnd(); mol_idx = _initReaction.productNext(mol_idx)){ int mol_idx_u = react_mapping[mol_idx]; Array<int>& react_aam = _initReaction.getAAMArray(mol_idx); for (j = 0; j < react_aam.size(); j++){ if(mol_mappings[mol_idx][j] == -1) continue; v = reaction_copy.getAAM(mol_idx_u, mol_mappings[mol_idx][j]); if(_mode == AAM_REGEN_DISCARD) react_aam[j] = v; if(_mode == AAM_REGEN_ALTER) react_aam[j] = v; if(_mode == AAM_REGEN_KEEP && _initReaction.getAAM(mol_idx, j) == 0){ react_aam[j] = v; _usedVertices[v] = 1; } } } for (mol_idx = _initReaction.reactantBegin(); mol_idx < _initReaction.reactantEnd(); mol_idx = _initReaction.reactantNext(mol_idx)){ int mol_idx_u = react_mapping[mol_idx]; Array<int>& react_aam = _initReaction.getAAMArray(mol_idx); for (j = 0; j < react_aam.size(); j++) { if(mol_mappings[mol_idx][j] == -1) continue; v = reaction_copy.getAAM(mol_idx_u, mol_mappings[mol_idx][j]); if(_mode == AAM_REGEN_DISCARD) react_aam[j] = v*_usedVertices[v]; if(_mode == AAM_REGEN_ALTER) react_aam[j] = v*_usedVertices[v]; if(_mode == AAM_REGEN_KEEP && _initReaction.getAAM(mol_idx, j) == 0) react_aam[j] = v*_usedVertices[v]; } } } void ReactionAutomapper::_setupReactionInvMap(Array<int> &react_mapping, ObjArray< Array<int> >& mol_mappings){ int mol_idx, mol_idx_map, j, v, map_j; if(_mode == AAM_REGEN_KEEP) _usedVertices.zerofill(); BaseReaction& reaction_copy = _reactionCopy.ref(); mol_idx = reaction_copy.productBegin(); for (; mol_idx < reaction_copy.productEnd(); mol_idx = reaction_copy.productNext(mol_idx)){ mol_idx_map = react_mapping[mol_idx]; Array<int>& react_aam = _initReaction.getAAMArray(mol_idx_map); Array<int>& creact_aam = reaction_copy.getAAMArray(mol_idx); for (j = 0; j < creact_aam.size(); j++) { map_j = mol_mappings[mol_idx][j]; if(map_j < 0) continue; v = creact_aam[j]; if(_mode == AAM_REGEN_DISCARD) react_aam[map_j] = v; if(_mode == AAM_REGEN_ALTER) react_aam[map_j] = v; if(_mode == AAM_REGEN_KEEP && _initReaction.getAAM(mol_idx_map, map_j) == 0){ react_aam[map_j] = v; _usedVertices[v] = 1; } } } mol_idx = reaction_copy.reactantBegin(); for (; mol_idx < reaction_copy.reactantEnd(); mol_idx = reaction_copy.reactantNext(mol_idx)){ mol_idx_map = react_mapping[mol_idx]; Array<int>& react_aam = _initReaction.getAAMArray(mol_idx_map); Array<int>& creact_aam = reaction_copy.getAAMArray(mol_idx); for (j = 0; j < creact_aam.size(); j++) { map_j = mol_mappings[mol_idx][j]; if(map_j < 0) continue; v = creact_aam[j]; if(_mode == AAM_REGEN_DISCARD) react_aam[map_j] = v*_usedVertices[v]; if(_mode == AAM_REGEN_ALTER) react_aam[map_j] = v*_usedVertices[v]; if(_mode == AAM_REGEN_KEEP && _initReaction.getAAM(mol_idx_map, map_j) == 0) react_aam[map_j] = v*_usedVertices[v]; } } } void ReactionAutomapper::_considerDissociation(){ AutoPtr<BaseMolecule> null_map_cut; AutoPtr<BaseMolecule> full_map_cut; QS_DEF(Array<int>,map); int i, j, mcv, mcvsum; for (i = _initReaction.begin(); i < _initReaction.end(); i = _initReaction.next(i)){ mcvsum = 0; mcv = 0; for (j = 0; j < _initReaction.getAAMArray(i).size(); j++){ if(_initReaction.getAAM(i, j) == 0) mcvsum++; else mcv++; } if(mcvsum < mcv || mcv <= _MIN_VERTEX_SUB) continue; BaseMolecule& ibase_mol = _initReaction.getBaseMolecule(i); full_map_cut.reset(ibase_mol.neu()); full_map_cut->clone_KeepIndices(ibase_mol, 0); full_map_cut->aromatize(arom_options); for (j = 0; j < _initReaction.getAAMArray(i).size(); j++){ if(_initReaction.getAAM(i, j) == 0) full_map_cut->removeAtom(j); } if(full_map_cut->vertexCount() == 0) continue; while(mcvsum >= mcv){ null_map_cut.reset(ibase_mol.neu()); null_map_cut->clone_KeepIndices(ibase_mol, 0); null_map_cut->aromatize(arom_options); for (j = 0; j < _initReaction.getAAMArray(i).size(); j++){ if(_initReaction.getAAM(i, j) > 0 || _initReaction.getBaseMolecule(i).getAtomNumber(j) == ELEM_H) null_map_cut->removeAtom(j); } if(null_map_cut->vertexCount() == 0) break; RSubstructureMcs rsm(_initReaction, full_map_cut.ref(), null_map_cut.ref(), *this); rsm.userdata = &rsm; map.clear(); if(!rsm.searchSubstructure(&map)) break; for(j = 0; j < map.size(); j++){ if(map[j] >= 0 && map[j] < _initReaction.getAAMArray(i).size()){ _initReaction.getAAMArray(i)[map[j]] = _initReaction.getAAM(i, j); } } mcvsum = 0; for (j = 0; j < _initReaction.getAAMArray(i).size(); j++){ if(_initReaction.getAAM(i, j) == 0) mcvsum++; } } } } void ReactionAutomapper::_considerDimerization() { QS_DEF(Array<int>, mol_mapping); QS_DEF(ObjArray< Array<int> >, inv_mappings); QS_DEF(Array<int>, sub_map); QS_DEF(Array<int>, max_sub_map); AutoPtr<BaseReaction> reaction_copy_ptr; bool way_exit = true, map_changed = false; int map_found, max_found , max_react_index = -1; reaction_copy_ptr.reset(_initReaction.neu()); BaseReaction &reaction_copy = reaction_copy_ptr.ref(); reaction_copy.clone(_initReaction, &mol_mapping, 0, &inv_mappings); /* * Clear reactants */ for(int react = reaction_copy.reactantBegin(); react < reaction_copy.reactantEnd(); react = reaction_copy.reactantNext(react)) { _removeUnusedInfo(reaction_copy, react, false); _removeSmallComponents(reaction_copy.getBaseMolecule(react)); } for(int prod = reaction_copy.productBegin(); prod < reaction_copy.productEnd(); prod = reaction_copy.productNext(prod)) { BaseMolecule& pmol = reaction_copy.getBaseMolecule(prod); pmol.aromatize(arom_options); way_exit = true; while(way_exit) { /* * Clear presented AAM */ _removeUnusedInfo(reaction_copy, prod, true); _removeSmallComponents(pmol); if(pmol.vertexCount() < _MIN_VERTEX_SUB) way_exit = false; max_found = _MIN_VERTEX_SUB; for(int react = reaction_copy.reactantBegin(); react < reaction_copy.reactantEnd() && way_exit; react = reaction_copy.reactantNext(react)) { map_found = _validMapFound(reaction_copy, react, prod, sub_map); if(map_found > max_found){ max_found = map_found; max_sub_map.copy(sub_map); max_react_index = react; } } if(max_found > _MIN_VERTEX_SUB) { for(int i = 0; i < max_sub_map.size(); ++i) { if(max_sub_map[i] >= 0) { reaction_copy.getAAMArray(prod).at(max_sub_map[i]) = reaction_copy.getAAM(max_react_index, i); map_changed = true; } } } else { way_exit = false; } } } if(map_changed) { for(int rindex = _initReaction.productBegin(); rindex < _initReaction.productEnd(); rindex = _initReaction.productNext(rindex)) { BaseMolecule& rmol = _initReaction.getBaseMolecule(rindex); int mrindex = mol_mapping[rindex]; for(int vert = rmol.vertexBegin(); vert < rmol.vertexEnd(); vert = rmol.vertexNext(vert)) { int copy_aam = reaction_copy.getAAM(mrindex, inv_mappings[rindex].at(vert)); if(_initReaction.getAAM(rindex, vert) == 0 && copy_aam > 0) _initReaction.getAAMArray(rindex).at(vert) = copy_aam ; } } } } int ReactionAutomapper::_validMapFound(BaseReaction& reaction, int react, int prod, Array<int>& sub_map) const { BaseMolecule &react_copy = reaction.getBaseMolecule(react); int result = 0; if(react_copy.vertexCount() < _MIN_VERTEX_SUB) return result; RSubstructureMcs rsub_mcs(reaction, react, prod, *this); rsub_mcs.cbMatchVertex = RSubstructureMcs::atomConditionReact; rsub_mcs.cbMatchEdge = RSubstructureMcs::bondConditionReact; rsub_mcs.userdata = &rsub_mcs; if(rsub_mcs.searchSubstructure(&sub_map)) result = __min(react_copy.vertexCount(), reaction.getBaseMolecule(prod).vertexCount()); return result; } void ReactionAutomapper::_removeUnusedInfo(BaseReaction& reaction, int mol_idx, bool aam_presented) const { QS_DEF(Array<int>, vertices_to_remove); QS_DEF(Array<int>, edges_to_remove); vertices_to_remove.clear(); edges_to_remove.clear(); BaseMolecule& mol = reaction.getBaseMolecule(mol_idx); int i; bool aam; for(i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if(aam_presented) aam = (reaction.getAAM(mol_idx, i) > 0); else aam = (reaction.getAAM(mol_idx, i) == 0); if(aam || (mol.getAtomNumber(i) == ELEM_H)) vertices_to_remove.push(i); } for (i = 0; i < vertices_to_remove.size(); ++i) { mol.removeAtom(vertices_to_remove[i]); } for(i = mol.edgeBegin(); i < mol.edgeEnd(); i = mol.edgeNext(i)) { if(reaction.getReactingCenter(mol_idx, i) == RC_MADE_OR_BROKEN) edges_to_remove.push(i); } for (i = 0; i < edges_to_remove.size(); ++i) { mol.removeBond(edges_to_remove[i]); } } void ReactionAutomapper::_removeSmallComponents(BaseMolecule& mol) const { int ncomp = mol.countComponents(); const Array<int> &decomposition = mol.getDecomposition(); QS_DEF(Array<int>, vertices_to_remove); vertices_to_remove.clear(); for(int comp_idx = 0; comp_idx < ncomp; ++comp_idx) { if(mol.countComponentVertices(comp_idx) < _MIN_VERTEX_SUB) { for(int j = mol.vertexBegin(); j < mol.vertexEnd(); j = mol.vertexNext(j)) if(decomposition[j] == comp_idx) vertices_to_remove.push(j); } } for (int i = 0; i < vertices_to_remove.size(); ++i) { mol.removeAtom(vertices_to_remove[i]); } } void ReactionAutomapper::_createPermutations(BaseReaction& reaction, ObjArray< Array<int> > & permutations) { QS_DEF(Array<int>, reactant_indexes); QS_DEF(Array<int>, reactant_small); QS_DEF(Array<int>, reactant_buf); reactant_indexes.clear(); reactant_small.clear(); /* * Permutate only big components */ int v_count; for(int r_idx = reaction.reactantBegin(); r_idx < reaction.reactantEnd(); r_idx = reaction.reactantNext(r_idx)) { BaseMolecule& mol = reaction.getBaseMolecule(r_idx); /* * Count only nonhydrogens */ v_count = 0; for (int j = mol.vertexBegin(); j < mol.vertexEnd(); j = mol.vertexNext(j)) { if (mol.getAtomNumber(j) != ELEM_H) v_count++; } if(v_count < MIN_PERMUTATION_SIZE) reactant_small.push(r_idx); else reactant_indexes.push(r_idx); } _permutation(reactant_indexes, permutations); /* * If there are no small components then exit */ if(reactant_small.size() == 0) return; /* * Appent small components */ int p_size = permutations.size(); /* * Workaround to consider permutation limit */ if(p_size * 2 < MAX_PERMUTATIONS_NUMBER) { /* * Add possible small components before and after permutations (increase p size) */ for (int i = 0; i < p_size; ++i) { permutations.push().copy(permutations[i]); } } else { /* * Add possible small components and not increase p size */ p_size /= 2; } /* * Add small components before and after */ for (int i = 0; i < permutations.size(); ++i) { Array<int>& perm = permutations[i]; if(i < p_size) { /* * After */ perm.concat(reactant_small); } else { /* * Before */ reactant_buf.copy(reactant_small); reactant_buf.concat(perm); perm.copy(reactant_buf); } } } //all transpositions for numbers from 0 to n-1 void ReactionAutomapper::_permutation(Array<int>& s_array, ObjArray< Array<int> > &p_array) { p_array.clear(); QS_DEF(Array<int>, per); QS_DEF(Array<int>, obr); int n = s_array.size(); int i, j, k, tmp, min = -1, raz; bool flag; per.resize(n); obr.resize(n); for (i = 0; i < n; i++) { per[i] = i+1; } while (1) { /* * Break to escape permutations out of memory error */ if(p_array.size() > MAX_PERMUTATIONS_NUMBER) break; Array<int>& new_array = p_array.push(); new_array.resize(n); for (k = 0; k < n; k++) { new_array.at(k) = s_array.at(per[k]-1); } flag = false; for (i = n - 2; i >= 0; i--) { if (per[i] < per[i + 1]) { flag = true; break; } } if (flag == false) { break; } raz = per[i+1]; for (j = i+1; j < n; j++) { if (((per[j] - per[i]) < raz) && (per[i] < per[j])) { min = j; } } tmp = per[i]; per[i] = per[min]; per[min] = tmp; for (j = i + 1; j < n; j++) { obr[j] = per[j]; } j = i + 1; for (k = n-1; k >= i+1; k--) { per[j] = obr[k]; j++; } } } ReactionMapMatchingData::ReactionMapMatchingData(BaseReaction &r) : _reaction(r) { } void ReactionMapMatchingData::createAtomMatchingData(){ _vertexMatchingArray.clear(); for(int i = _reaction.begin(); i < _reaction.end(); i = _reaction.next(i)) { for(int j = 0; j < _reaction.getBaseMolecule(i).vertexEnd(); j++) { _vertexMatchingArray.push(); } } for(int react = _reaction.reactantBegin(); react < _reaction.reactantEnd(); react = _reaction.reactantNext(react)) { BaseMolecule& rmol = _reaction.getBaseMolecule(react); for(int rvert = rmol.vertexBegin(); rvert < rmol.vertexEnd(); rvert = rmol.vertexNext(rvert)) { if(_reaction.getAAM(react, rvert) > 0){ for(int prod = _reaction.productBegin(); prod < _reaction.productEnd(); prod = _reaction.productNext(prod)) { BaseMolecule& pmol = _reaction.getBaseMolecule(prod); for(int pvert = pmol.vertexBegin(); pvert < pmol.vertexEnd(); pvert = pmol.vertexNext(pvert)) { if(_reaction.getAAM(react, rvert) == _reaction.getAAM(prod, pvert)){ int v_id1 = _getVertexId(react, rvert); int v_id2 = _getVertexId(prod, pvert); _vertexMatchingArray[v_id1].push(v_id2); _vertexMatchingArray[v_id2].push(v_id1); } } } } } } } void ReactionMapMatchingData::createBondMatchingData() { QS_DEF(Array<int>, matchingMap1); QS_DEF(Array<int>, matchingMap2); int v1,v2; createAtomMatchingData(); _edgeMatchingArray.clear(); for(int i = _reaction.begin(); i < _reaction.end(); i = _reaction.next(i)) { for(int j = 0; j < _reaction.getBaseMolecule(i).edgeEnd(); j++) { _edgeMatchingArray.push(); } } for(int react = _reaction.reactantBegin(); react < _reaction.reactantEnd(); react = _reaction.reactantNext(react)) { BaseMolecule& rmol = _reaction.getBaseMolecule(react); for(int redge = rmol.edgeBegin(); redge < rmol.edgeEnd(); redge = rmol.edgeNext(redge)){ for(int prod = _reaction.productBegin(); prod < _reaction.productEnd(); prod = _reaction.productNext(prod)) { v1 = rmol.getEdge(redge).beg; v2 = rmol.getEdge(redge).end; getAtomMap(react, prod, v1, &matchingMap1); getAtomMap(react, prod, v2, &matchingMap2); for(int m = 0; m < matchingMap1.size(); m++){ for(int n = 0; n < matchingMap2.size(); n++){ int pedge = _reaction.getBaseMolecule(prod).findEdgeIndex(matchingMap1[m],matchingMap2[n]); if(pedge >= 0){ int e_id1 = _getEdgeId(react,redge); int e_id2 = _getEdgeId(prod, pedge); _edgeMatchingArray[e_id1].push(e_id2); _edgeMatchingArray[e_id2].push(e_id1); } } } } } } } int ReactionMapMatchingData::endMap() const { return _reaction.sideEnd(); } int ReactionMapMatchingData::nextMap(int mol_idx, int opposite_idx) const { int side_type = _reaction.getSideType(mol_idx) == Reaction::REACTANT ? Reaction::PRODUCT:Reaction::REACTANT; return _reaction.sideNext(side_type, opposite_idx); } int ReactionMapMatchingData::endAtomMap() const { return _reaction.sideEnd(); } int ReactionMapMatchingData::nextAtomMap(int mol_idx, int opposite_idx, int atom_idx) const { int side_type = _reaction.getSideType(mol_idx) == Reaction::REACTANT ? Reaction::PRODUCT:Reaction::REACTANT; int rm_idx = _reaction.sideNext(side_type, opposite_idx); for(;rm_idx < _reaction.sideEnd(); rm_idx = _reaction.sideNext(side_type, rm_idx)) { if(getAtomMap(mol_idx, rm_idx, atom_idx, 0)) break; } return rm_idx; } bool ReactionMapMatchingData::getAtomMap(int mol_idx, int opposite_idx, int atom_idx, Array<int>* mapping) const { bool result = false; int vertex_id = _getVertexId(mol_idx, atom_idx); int first_r_vertex = _getVertexId(opposite_idx, 0); int last_r_vertex = _getVertexId(opposite_idx+1, 0); if(mapping) mapping->clear(); for(int i = 0; i < _vertexMatchingArray[vertex_id].size(); i++){ int m_vertex = _vertexMatchingArray[vertex_id].at(i); if(m_vertex >= first_r_vertex && m_vertex < last_r_vertex) { if(mapping) mapping->push(m_vertex - first_r_vertex); else return true; result = true; } } return result; } int ReactionMapMatchingData::endBondMap() const { return _reaction.sideEnd(); } int ReactionMapMatchingData::nextBondMap(int mol_idx, int opposite_idx, int bond_idx) const { int side_type = _reaction.getSideType(mol_idx) == Reaction::REACTANT ? Reaction::PRODUCT:Reaction::REACTANT; int rm_idx = _reaction.sideNext(side_type, opposite_idx); for(;rm_idx < _reaction.sideEnd(); rm_idx = _reaction.sideNext(side_type, rm_idx)) { if(getBondMap(mol_idx, rm_idx, bond_idx, 0)) break; } return rm_idx; } bool ReactionMapMatchingData::getBondMap(int mol_idx, int opposite_idx, int bond_idx, Array<int>* mapping) const { bool result = false; int edge = _getEdgeId(mol_idx, bond_idx); int first_r_edge = _getEdgeId(opposite_idx,0); int last_r_edge = _getEdgeId(opposite_idx+1,0); if(mapping) mapping->clear(); for(int i = 0; i < _edgeMatchingArray[edge].size(); i++){ int m_edge = _edgeMatchingArray[edge].at(i); if(m_edge >= first_r_edge && m_edge < last_r_edge) { if(mapping) mapping->push(m_edge - first_r_edge); else return true; result = true; } } return result; } int ReactionMapMatchingData::_getVertexId(int mol_idx, int vert) const { int result = 0; for(int i = _reaction.begin(); i < mol_idx; i = _reaction.next(i)) { result += _reaction.getBaseMolecule(i).vertexEnd(); } result += vert; return result; } int ReactionMapMatchingData::_getEdgeId(int mol_idx, int edge) const { int result = 0; for(int i = _reaction.begin(); i < mol_idx; i = _reaction.next(i)) { result += _reaction.getBaseMolecule(i).edgeEnd(); } result += edge; return result; } RSubstructureMcs::RSubstructureMcs(BaseReaction& reaction, const ReactionAutomapper& context): flags(CONDITION_ALL), _context(context), _reaction(reaction), _subReactNumber(-1), _superProductNumber(-1) { setUpFlags(context); } RSubstructureMcs::RSubstructureMcs(BaseReaction &reaction, int sub_num, int super_num, const ReactionAutomapper& context): flags(CONDITION_ALL), _context(context), _reaction(reaction), _subReactNumber(sub_num), _superProductNumber(super_num) { setGraphs(reaction.getBaseMolecule(sub_num), reaction.getBaseMolecule(super_num)); _createQueryTransposition(); setUpFlags(context); } RSubstructureMcs::RSubstructureMcs(BaseReaction &reaction, BaseMolecule& sub, BaseMolecule& super, const ReactionAutomapper& context): flags(CONDITION_ALL), _context(context), _reaction(reaction), _subReactNumber(-1), _superProductNumber(-1){ setGraphs(sub, super); _createQueryTransposition(); setUpFlags(context); cbMatchVertex = atomConditionReact; cbMatchEdge = bondConditionReactSimple; } void RSubstructureMcs::setUpFlags(const ReactionAutomapper& context) { flags = CONDITION_NONE; if(!context.ignore_atom_charges) flags |= CONDITION_ATOM_CHARGES; if(!context.ignore_atom_isotopes) flags |= CONDITION_ATOM_ISOTOPES; if(!context.ignore_atom_radicals) flags |= CONDITION_ATOM_RADICAL; if(!context.ignore_atom_valence) flags |= CONDITION_ATOM_VALENCE; arom_options = context.arom_options; } bool RSubstructureMcs::searchSubstructure(Array<int>* map) { bool result = false; if (_context.cancellation) { try { result = SubstructureMcs::searchSubstructure(map); } catch (Exception&) { result = false; } } else { result = SubstructureMcs::searchSubstructure(map); } /* * Transpose map back */ if(result) _detransposeOutputMap(map); return result; } bool RSubstructureMcs::searchSubstructureReact(BaseMolecule& init_rmol, const Array<int>* in_map, Array<int> *out_map){ if(_sub == 0 || _super == 0) throw ReactionAutomapper::Error("internal AAM error: not initialized sub-mcs molecules"); QS_DEF(ObjArray< Array<int> >, tmp_maps); QS_DEF(ObjArray<EmbeddingEnumerator>, emb_enums); QS_DEF(Array<int>, in_map_cut); QS_DEF(Array<int>, results); emb_enums.clear(); tmp_maps.clear(); int map_result = 0; results.resize(4); BaseMolecule& mol_react = _reaction.getBaseMolecule(_subReactNumber); int react_vsize = mol_react.vertexCount(); if(react_vsize < 2) { mol_react.clone(init_rmol, 0, 0); react_vsize = mol_react.vertexCount(); mol_react.aromatize(arom_options); } if(_super->vertexCount() < 2 || _sub->vertexCount() < 2) return false; for(int i = 0; i < 4; ++i) { EmbeddingEnumerator& emb_enum = emb_enums.push(*_super); emb_enum.setSubgraph(*_sub); emb_enum.cb_match_vertex = atomConditionReact; emb_enum.cb_embedding = _embedding; emb_enum.userdata = this; if(i & 1) emb_enum.cb_match_edge = bondConditionReact; else emb_enum.cb_match_edge = bondConditionReactStrict; tmp_maps.push().clear(); results[i] = -1; } Array<int>* in_map_c = 0; if(react_vsize > 0 && in_map != 0 && in_map->size() > 0) { in_map_c = &in_map_cut; in_map_cut.clear(); in_map_cut.resize(mol_react.vertexEnd()); for(int k = 0; k < in_map_cut.size(); k++) in_map_cut[k] = SubstructureMcs::UNMAPPED; for(int k = mol_react.vertexBegin(); k < mol_react.vertexEnd(); k = mol_react.vertexNext(k)) { in_map_cut[k] = in_map->at(k); } } //first more strict rules then more weak results[0] = _searchSubstructure(emb_enums[0], in_map_c, &tmp_maps[0]); results[1] = _searchSubstructure(emb_enums[1], in_map_c, &tmp_maps[1]); mol_react.clone(init_rmol, 0, 0); mol_react.aromatize(arom_options); if(mol_react.vertexCount() > react_vsize) { results[2] = _searchSubstructure(emb_enums[2], in_map, &tmp_maps[2]); results[3] = _searchSubstructure(emb_enums[3], in_map, &tmp_maps[3]); } map_result = 3; for(int i = 2; i >= 0; --i) { if(results[i] >= results[map_result]) map_result = i; } if(results[map_result] < 2) return false; if(out_map != 0) out_map->copy(tmp_maps[map_result]); return true; } bool RSubstructureMcs::searchMaxCommonSubReact(const Array<int>* in_map, Array<int> *out_map) { if(_sub == 0 || _super == 0) throw ReactionAutomapper::Error("internal AAM error: not initialized sub-mcs molecules"); if(out_map != 0) out_map->clear(); // if(_super->vertexCount() < 2 || _sub->vertexCount() < 2) // return false; Molecule *sub_molecule; Molecule *super_molecule; if(_invert) { sub_molecule = (Molecule*)_super; super_molecule = (Molecule*)_sub; } else { sub_molecule = (Molecule*)_sub; super_molecule = (Molecule*)_super; } MaxCommonSubmolecule mcs(*sub_molecule, *super_molecule); mcs.parametersForExact.maxIteration = MAX_ITERATION_NUMBER; mcs.conditionVerticesColor = atomConditionReact; mcs.conditionEdgeWeight = bondConditionReact; mcs.cbSolutionTerm = cbMcsSolutionTerm; mcs.userdata = this; /* * Do not randomize */ // mcs.parametersForApproximate.randomize = true; if(in_map != 0) { _transposeInputMap(in_map, mcs.incomingMap); } try { /* * Search for exact mcs first */ mcs.findExactMCS(); /* * Search for approximate mcs */ if (mcs.parametersForExact.isStopped) { mcs.findApproximateMCS(); } } catch (Exception& e) { if(strstr(e.message(), "input mapping incorrect") != 0) throw e; return false; } mcs.getMaxSolutionMap(out_map, 0); /* * Search best solution as far as mcs is not consider auto permutations */ _selectBestAutomorphism(out_map); /* * Transpose map back */ _detransposeOutputMap(out_map); return true; } int RSubstructureMcs::findReactionCenter(BaseMolecule& mol, int bondNum) const { if(_sub == 0 || _super == 0) throw ReactionAutomapper::Error("internal AAM error: not initialized sub-mcs molecules"); if(_sub == &mol){ bondNum = _getTransposedBondIndex(mol, bondNum); if(_invert){ return _reaction.getReactingCenter(_superProductNumber, bondNum); }else{ return _reaction.getReactingCenter(_subReactNumber, bondNum); } } if(_super == &mol){ if(_invert){ return _reaction.getReactingCenter(_subReactNumber, bondNum); }else{ return _reaction.getReactingCenter(_superProductNumber, bondNum); } } return -2; } void RSubstructureMcs::getReactingCenters(BaseMolecule& mol1, BaseMolecule& mol2, int bond1, int bond2, int& rc_reactant, int& rc_product) const { if(_sub == 0 || _super == 0) throw ReactionAutomapper::Error("internal AAM error: not initialized sub-mcs molecules"); if(_sub == &mol1 && _super == &mol2) { bond1 = _getTransposedBondIndex(mol1, bond1); if(_invert) { rc_reactant = _reaction.getReactingCenter(_subReactNumber, bond2); rc_product = _reaction.getReactingCenter(_superProductNumber, bond1); }else{ rc_reactant = _reaction.getReactingCenter(_subReactNumber, bond1); rc_product = _reaction.getReactingCenter(_superProductNumber, bond2); } } if(_sub == &mol2 && _super == &mol1){ bond2 = _getTransposedBondIndex(mol2, bond2); if(_invert) { rc_reactant = _reaction.getReactingCenter(_subReactNumber, bond1); rc_product = _reaction.getReactingCenter(_superProductNumber, bond2); }else{ rc_reactant = _reaction.getReactingCenter(_subReactNumber, bond2); rc_product = _reaction.getReactingCenter(_superProductNumber, bond1); } } } bool RSubstructureMcs::atomConditionReact (Graph &g1, Graph &g2, const int *, int i, int j, void* userdata) { BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; if(userdata == 0) throw ReactionAutomapper::Error("internal AAM error: userdata should be not null for atom match"); RSubstructureMcs &rsm = *(RSubstructureMcs *)userdata; return _matchAtoms(mol1, mol2, i, j, rsm.flags); } bool RSubstructureMcs::bondConditionReact (Graph &g1, Graph &g2, int i, int j, void* userdata){ BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; if(userdata == 0) throw ReactionAutomapper::Error("internal AAM error: userdata should be not null for bond match"); RSubstructureMcs &rsm = *(RSubstructureMcs *)userdata; int rc_reactant; int rc_product; rsm.getReactingCenters(mol1, mol2, i, j, rc_reactant, rc_product); //not consider if(rc_reactant == RC_MADE_OR_BROKEN || rc_product == RC_MADE_OR_BROKEN) return false; //aromatic if(mol1.getBondOrder(i) == BOND_AROMATIC || mol2.getBondOrder(j) == BOND_AROMATIC) return true; //not change if(rc_reactant == RC_UNMARKED && rc_product == RC_UNMARKED) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //not change reactants if((rc_reactant == RC_NOT_CENTER || rc_reactant == RC_UNCHANGED || rc_reactant == RC_UNCHANGED + RC_MADE_OR_BROKEN)) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //change reactants if((rc_reactant == RC_ORDER_CHANGED || rc_reactant == RC_ORDER_CHANGED+RC_MADE_OR_BROKEN)) return mol1.getBondOrder(i) != mol2.getBondOrder(j); //not change products if((rc_product == RC_NOT_CENTER || rc_product == RC_UNCHANGED || rc_product == RC_UNCHANGED + RC_MADE_OR_BROKEN)) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //change products if((rc_product == RC_ORDER_CHANGED || rc_product == RC_ORDER_CHANGED+RC_MADE_OR_BROKEN)) return mol1.getBondOrder(i) != mol2.getBondOrder(j); //can change return true; } bool RSubstructureMcs::bondConditionReactStrict (Graph &g1, Graph &g2, int i, int j, void* userdata) { BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; if(userdata == 0) throw ReactionAutomapper::Error("internal AAM error: userdata should be not null for bond strict match"); RSubstructureMcs &rsm = *(RSubstructureMcs *)userdata; int rc_reactant; int rc_product; rsm.getReactingCenters(mol1, mol2, i, j, rc_reactant, rc_product); //not consider if((rc_reactant & RC_MADE_OR_BROKEN) || (rc_product & RC_MADE_OR_BROKEN)) return false; //aromatic if(mol1.getBondOrder(i) == BOND_AROMATIC || mol2.getBondOrder(j) == BOND_AROMATIC) return true; //not change if(rc_reactant == RC_UNMARKED && rc_product == RC_UNMARKED) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //not change reactants if(rc_reactant == RC_NOT_CENTER || rc_reactant == RC_UNCHANGED) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //change reactants if(rc_reactant == RC_ORDER_CHANGED) return mol1.getBondOrder(i) != mol2.getBondOrder(j); //not change products if((rc_product == RC_NOT_CENTER || rc_product == RC_UNCHANGED)) return mol1.getBondOrder(i) == mol2.getBondOrder(j); //change products if(rc_product == RC_ORDER_CHANGED) return mol1.getBondOrder(i) != mol2.getBondOrder(j); //can change return true; } bool RSubstructureMcs::bondConditionReactSimple (Graph &g1, Graph &g2, int i, int j, void* userdata) { BaseMolecule &mol1 = (BaseMolecule &)g1; BaseMolecule &mol2 = (BaseMolecule &)g2; if (mol1.getBondOrder(i) != mol2.getBondOrder(j)) return false; return true; } int RSubstructureMcs::cbMcsSolutionTerm(Array<int>& a1, Array<int>& a2, void* context) { int result = MaxCommonSubgraph::ringsSolutionTerm(a1, a2, context); if(result == 0) { const RSubstructureMcs* r_sub_mcs = (const RSubstructureMcs *)context; BaseReaction& reaction = r_sub_mcs->getReaction(); int react = r_sub_mcs->getReactNumber(); int prod = r_sub_mcs->getProductNumber(); int e_size1 = a1[1]; int e_size2 = a2[1]; int r1_rc= 0, r2_rc = 0; for(int i = 0; i < e_size1; ++i) { int e_map = a1.at(2+a1[0]+i); if(e_map >= 0) { if((reaction.getReactingCenter(react, i) & RC_MADE_OR_BROKEN)) ++r1_rc; if((reaction.getReactingCenter(prod, e_map) & RC_MADE_OR_BROKEN)) ++r1_rc; } } for(int i = 0; i < e_size2; ++i) { int e_map = a2.at(2+a2[0]+i); if(e_map >= 0) { if((reaction.getReactingCenter(react, i) & RC_MADE_OR_BROKEN)) ++r2_rc; if((reaction.getReactingCenter(prod, e_map) & RC_MADE_OR_BROKEN)) ++r2_rc; } } result = r1_rc - r2_rc; } return result; } int RSubstructureMcs::_searchSubstructure(EmbeddingEnumerator& emb_enum, const Array<int>* in_map, Array<int> *out_map) { if(_sub == 0 || _super == 0) throw ReactionAutomapper::Error("internal AAM error: not initialized sub-mcs molecules"); QS_DEF(Array<int>, input_map); if(in_map != 0) { _transposeInputMap(in_map, input_map); in_map = &input_map; } int result = 0; if(in_map != 0) { for(int i = 0; i < in_map->size(); i++) { if(in_map->at(i) >= 0 && !_invert) { if(!emb_enum.fix(i, in_map->at(i))){ result = -1; break; } } if(in_map->at(i) >= 0 && _invert) { if(!emb_enum.fix(in_map->at(i), i)){ result = -1; break; } } } } if(result == -1) return result; int proc = 1; if (_context.cancellation) { try { proc = emb_enum.process(); } catch (Exception&) { proc = 1; } } else { proc = emb_enum.process(); } if(proc == 1) return -1; int ncomp = _sub->countComponents(); const Array<int> &decomposition = _sub->getDecomposition(); int j, max_index = 0; for (j = 1; j < ncomp; j++) if (_sub->countComponentVertices(j) > _sub->countComponentVertices(max_index)) max_index = j; if(out_map != 0){ if(!_invert){ out_map->clear_resize(_sub->vertexEnd()); for(int i = 0;i < out_map->size();i++) out_map->at(i) = SubstructureMcs::UNMAPPED; for(int i = _sub->vertexBegin(); i < _sub->vertexEnd(); i = _sub->vertexNext(i)) { out_map->at(i) = emb_enum.getSubgraphMapping()[i]; if(max_index != decomposition[i]) out_map->at(i) = SubstructureMcs::UNMAPPED; if(out_map->at(i) >= 0) ++result; } }else{ out_map->clear_resize(_super->vertexEnd()); for(int i = 0;i < out_map->size();i++) out_map->at(i) = SubstructureMcs::UNMAPPED; for(int i = _super->vertexBegin(); i < _super->vertexEnd(); i = _super->vertexNext(i)) { out_map->at(i) = emb_enum.getSupergraphMapping()[i]; if((out_map->at(i) >= 0) && (max_index != decomposition[out_map->at(i)])) out_map->at(i) = SubstructureMcs::UNMAPPED; if(out_map->at(i) >= 0) ++result; } } } _detransposeOutputMap(out_map); return result; } bool RSubstructureMcs::_matchAtoms(BaseMolecule& query, BaseMolecule& target, int sub_idx, int super_idx, int flags) { if (query.isRSite(sub_idx) && target.isRSite(super_idx)) return query.getRSiteBits(sub_idx) == target.getRSiteBits(super_idx); if (query.isRSite(sub_idx) || target.isRSite(super_idx)) return false; if (query.isPseudoAtom(sub_idx) && target.isPseudoAtom(super_idx)) { if (strcmp(query.getPseudoAtom(sub_idx), target.getPseudoAtom(super_idx)) != 0) return false; } else if (!query.isPseudoAtom(sub_idx) && !target.isPseudoAtom(super_idx)) { if (query.getAtomNumber(sub_idx) != target.getAtomNumber(super_idx)) return false; } else return false; if (flags & CONDITION_ATOM_ISOTOPES) if (query.getAtomIsotope(sub_idx) != target.getAtomIsotope(super_idx)) return false; if (flags & CONDITION_ATOM_CHARGES) { int qcharge = query.getAtomCharge(sub_idx); int tcharge = target.getAtomCharge(super_idx); if (qcharge == CHARGE_UNKNOWN) qcharge = 0; if (tcharge == CHARGE_UNKNOWN) tcharge = 0; if (qcharge != tcharge) return false; } if (flags & CONDITION_ATOM_VALENCE) { if (!query.isPseudoAtom(sub_idx)) { if (!query.isQueryMolecule() && !target.isQueryMolecule()) { if (query.getAtomValence(sub_idx) != target.getAtomValence(super_idx)) return false; } } } if (flags & CONDITION_ATOM_RADICAL) { if (!query.isPseudoAtom(sub_idx)) { int qrad = query.getAtomRadical(sub_idx); int trad = target.getAtomRadical(super_idx); if (qrad == -1) qrad = 0; if (trad == -1) trad = 0; if (qrad != trad) return false; } } return true; } void RSubstructureMcs::_selectBestAutomorphism(Array<int>* map_out) { if(map_out == 0) return; QS_DEF(Array<int>, ignore_atoms); QS_DEF(Array<int>, current_map); BaseMolecule *sub_molecule; BaseMolecule *super_molecule; if(_invert) { sub_molecule = (BaseMolecule*)_super; super_molecule = (BaseMolecule*)_sub; } else { sub_molecule = (BaseMolecule*)_sub; super_molecule = (BaseMolecule*)_super; } ignore_atoms.resize(super_molecule->vertexEnd()); ignore_atoms.fill(1); for (int i = 0; i < map_out->size(); ++i) { if(map_out->at(i) >= 0) ignore_atoms[map_out->at(i)] = 0; } /* * Set callbacks */ AutomorphismSearch auto_search; auto_search.cb_check_automorphism = _cbAutoCheckAutomorphismReact; auto_search.ignored_vertices = ignore_atoms.ptr(); auto_search.getcanon = false; auto_search.context = this; /* * Find all automorphisms */ _autoMaps.clear(); auto_search.process(*super_molecule); /* * Score initial solution */ int best_solution = -1; int best_score; best_score = _scoreSolution(sub_molecule, super_molecule, *map_out); /* * Score the best solution */ for (int aut_idx = 0; aut_idx < _autoMaps.size(); ++aut_idx) { /* * Convert mapping */ current_map.copy(*map_out); for (int i = 0; i < current_map.size(); ++i) { int sup_idx = current_map[i]; if(sup_idx >= 0) current_map[i] = _autoMaps[aut_idx][sup_idx]; } /* * Calculate current score */ int current_score = _scoreSolution(sub_molecule, super_molecule, current_map); if(current_score > best_score) { best_score = current_score; best_solution = aut_idx; } } /* * Select best scoring solution */ if(best_solution >= 0) { current_map.copy(*map_out); for (int i = 0; i < current_map.size(); ++i) { int sup_idx = current_map[i]; if(sup_idx >= 0) current_map[i] = _autoMaps[best_solution][sup_idx]; } map_out->copy(current_map); } } int RSubstructureMcs::_cbAutoVertexReact (Graph &graph, int idx1, int idx2, const void *context) { return atomConditionReact(graph, graph, 0, idx1, idx2, (void*)context); } bool RSubstructureMcs::_cbAutoCheckAutomorphismReact (Graph &graph, const Array<int> &mapping, const void *context) { RSubstructureMcs &rsm = *(RSubstructureMcs *)context; rsm._autoMaps.push().copy(mapping); return false; } int RSubstructureMcs::_scoreSolution(BaseMolecule *sub_molecule, BaseMolecule *super_molecule, Array<int>& v_map) { int res_score = 0; QS_DEF(Array<int>, edge_map); edge_map.clear_resize(sub_molecule->edgeEnd()); edge_map.fffill(); for (int vert_idx = 0; vert_idx < v_map.size(); ++vert_idx) { int super_vert = v_map[vert_idx]; if(super_vert >= 0) { /* * Score vertex degree keeping */ const Vertex& sub_vert = sub_molecule->getVertex(vert_idx); if(sub_vert.degree() == super_molecule->getVertex(super_vert).degree()) ++res_score; for (int k = sub_vert.neiBegin(); k != sub_vert.neiEnd(); k = sub_vert.neiNext(k)) { int nei_vert = sub_vert.neiVertex(k); if(v_map[nei_vert] >= 0) { int sub_edge = sub_molecule->findEdgeIndex(vert_idx, nei_vert); int super_edge = super_molecule->findEdgeIndex(super_vert, v_map[nei_vert]); if(sub_edge != -1 && super_edge != -1) edge_map[sub_edge] = super_edge; } } } } /* * Score bond order keeping with priority */ for (int sub_idx = 0; sub_idx < edge_map.size(); ++sub_idx) { int super_idx = edge_map[sub_idx]; if(super_idx >= 0) { if(bondConditionReact(*sub_molecule, *super_molecule, sub_idx, super_idx, this) &&(sub_molecule->getBondOrder(sub_idx) == super_molecule->getBondOrder(super_idx))) { res_score += HIGH_PRIORITY_SCORE; } } } /* * Score atom number keeping with priority */ for (int sub_idx = 0; sub_idx < v_map.size(); ++sub_idx) { int super_idx = v_map[sub_idx]; if(super_idx >= 0) { if(atomConditionReact(*sub_molecule, *super_molecule, 0, sub_idx, super_idx, this)) res_score += HIGH_PRIORITY_SCORE; } } return res_score; } void RSubstructureMcs::_createQueryTransposition() { QS_DEF(Array<int>, transposition); MoleculeAtomNeighbourhoodCounters nei_counters; if(_reaction.isQueryReaction()) { nei_counters.calculate((QueryMolecule&)*_sub); _transposedQuery.reset(new QueryMolecule()); } else { nei_counters.calculate((Molecule&)*_sub); _transposedQuery.reset(new Molecule()); } nei_counters.makeTranspositionForSubstructure((BaseMolecule&)*_sub, transposition); /* * Create map */ _transposedQuery->makeSubmolecule((BaseMolecule&)*_sub, transposition, &_transposition); /* * Create inv map */ _invTransposition.resize(_transposition.size()); _invTransposition.fffill(); for (int i = 0; i < _transposition.size(); ++i) { if(_transposition[i] >= 0) _invTransposition[_transposition[i]] = i; } /* * Create bond map */ _bondTransposition.resize(_transposedQuery->edgeEnd()); _bondTransposition.fffill(); for (int e_idx = _sub->edgeBegin(); e_idx != _sub->edgeEnd(); e_idx = _sub->edgeNext(e_idx)) { int vert1 = _transposition[_sub->getEdge(e_idx).beg]; int vert2 = _transposition[_sub->getEdge(e_idx).end]; int edge = _transposedQuery->findEdgeIndex(vert1, vert2); if(edge >= 0) _bondTransposition[edge] = e_idx; } _sub = _transposedQuery.get(); } void RSubstructureMcs::_detransposeOutputMap(Array<int>* map) const { if(map && _transposedQuery.get()) { QS_DEF(Array<int>, buf_map); if(_invert) { buf_map.resize(map->size()); buf_map.fffill(); for (int i = 0; i < map->size(); ++i) { if(map->at(i) >= 0) buf_map[i] = _invTransposition[map->at(i)]; } map->copy(buf_map); } else { buf_map.resize(_transposition.size()); buf_map.fffill(); for (int i = 0; i < map->size(); ++i) { if(_invTransposition[i] >= 0) buf_map[_invTransposition[i]] = map->at(i); } map->copy(buf_map); } } } void RSubstructureMcs::_transposeInputMap(const Array<int>* map, Array<int>& input_map) const { input_map.clear(); if (map == 0) return; if (_transposedQuery.get()) { input_map.resize(map->size()); input_map.fffill(); if (_invert) { for (int i = 0; i < map->size(); ++i) { if (map->at(i) >= 0) input_map[i] = _transposition[map->at(i)]; } } else { for (int i = 0; i < map->size(); ++i) { if (_invTransposition[i] >= 0) input_map[_transposition[i]] = map->at(i); } } } else { input_map.copy(*map); } } int RSubstructureMcs::_getTransposedBondIndex(BaseMolecule& mol, int bond) const { int result = bond; if(_transposedQuery.get() != 0 && _sub == &mol) result = _bondTransposition[bond]; return result; } AAMCancellationWrapper::AAMCancellationWrapper(CancellationHandler* canc) { _prev = setCancellationHandler(canc); } AAMCancellationWrapper::~AAMCancellationWrapper() { if(_prev) { setCancellationHandler(_prev); } }��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_cdx_loader.cpp��������������������������������������������0000664�0000000�0000000�00000002211�12710376503�0023264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_cdx_loader.h" #include "reaction/reaction.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "molecule/molecule_cdx_loader.h" using namespace indigo; IMPL_ERROR(ReactionCdxLoader, "reaction CDX loader"); ReactionCdxLoader::ReactionCdxLoader (Scanner &scanner) : _scanner(scanner) { } ReactionCdxLoader::~ReactionCdxLoader () { } void ReactionCdxLoader::loadReaction (Reaction &rxn) { rxn.clear(); QS_DEF(Array<char>, buf); _scanner.readAll(buf); buf.push(0); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_cdx_saver.cpp���������������������������������������������0000664�0000000�0000000�00000002267�12710376503�0023151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_cdx_saver.h" #include "base_cpp/output.h" #include "reaction/reaction.h" #include "molecule/molecule_cdx_saver.h" using namespace indigo; IMPL_ERROR(ReactionCdxSaver, "reaction CDX saver"); ReactionCdxSaver::ReactionCdxSaver (Output &output) : _output(output) { } ReactionCdxSaver::~ReactionCdxSaver () { } void ReactionCdxSaver::saveReaction (Reaction &rxn) { int i; MoleculeCdxSaver molsaver(_output); if (rxn.reactantsCount() > 0) { } if (rxn.productsCount() > 0) { } if (rxn.catalystCount() > 0) { } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_cml_loader.cpp��������������������������������������������0000664�0000000�0000000�00000005634�12710376503�0023275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_cml_loader.h" #include "reaction/reaction.h" #include "base_cpp/tlscont.h" #include "base_cpp/scanner.h" #include "tinyxml.h" #include "molecule/molecule_cml_loader.h" using namespace indigo; IMPL_ERROR(ReactionCmlLoader, "reaction CML loader"); ReactionCmlLoader::ReactionCmlLoader (Scanner &scanner) : _scanner(scanner) { } ReactionCmlLoader::~ReactionCmlLoader () { } void ReactionCmlLoader::loadReaction (Reaction &rxn) { rxn.clear(); QS_DEF(Array<char>, buf); _scanner.readAll(buf); buf.push(0); TiXmlDocument xml; xml.Parse(buf.ptr()); if (xml.Error()) throw Error("XML parsing error: %s", xml.ErrorDesc()); TiXmlHandle hxml(&xml); TiXmlElement *elem; TiXmlHandle hroot(0); elem = hxml.FirstChild("reaction").Element(); if (elem == 0) elem = hxml.FirstChild("cml").FirstChild("reaction").Element(); if (elem == 0) throw Error("no <reaction>?"); hroot = TiXmlHandle(elem); const char *title = elem->Attribute("title"); if (title != 0) rxn.name.readString(title, true); QS_DEF(Molecule, mol); elem = hroot.FirstChild("reactantList").FirstChild().Element(); for (; elem; elem = elem->NextSiblingElement()) { if (strcasecmp(elem->Value(), "molecule") != 0) continue; TiXmlHandle handle(elem); MoleculeCmlLoader loader(handle); loader.stereochemistry_options = stereochemistry_options; loader.loadMolecule(mol); rxn.addReactantCopy(mol, 0, 0); } elem = hroot.FirstChild("productList").FirstChild().Element(); for (; elem; elem = elem->NextSiblingElement()) { if (strcasecmp(elem->Value(), "molecule") != 0) continue; TiXmlHandle handle(elem); MoleculeCmlLoader loader(handle); loader.stereochemistry_options = stereochemistry_options; loader.loadMolecule(mol); rxn.addProductCopy(mol, 0, 0); } elem = hroot.FirstChild("spectatorList").FirstChild().Element(); for (; elem; elem = elem->NextSiblingElement()) { if (strcasecmp(elem->Value(), "molecule") != 0) continue; TiXmlHandle handle(elem); MoleculeCmlLoader loader(handle); loader.stereochemistry_options = stereochemistry_options; loader.loadMolecule(mol); rxn.addCatalystCopy(mol, 0, 0); } } ����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_cml_saver.cpp���������������������������������������������0000664�0000000�0000000�00000004545�12710376503�0023147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2011 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_cml_saver.h" #include "base_cpp/output.h" #include "reaction/reaction.h" #include "molecule/molecule_cml_saver.h" using namespace indigo; IMPL_ERROR(ReactionCmlSaver, "reaction CML saver"); ReactionCmlSaver::ReactionCmlSaver (Output &output) : _output(output) { skip_cml_tag = false; } ReactionCmlSaver::~ReactionCmlSaver () { } void ReactionCmlSaver::saveReaction (Reaction &rxn) { if (!skip_cml_tag) { _output.printf("<?xml version=\"1.0\" ?>\n"); _output.printf("<cml>\n"); } if (rxn.name.ptr() != 0) { if (strchr(rxn.name.ptr(), '\"') != NULL) throw Error("can not save reaction with '\"' in title"); _output.printf("<reaction title=\"%s\">\n", rxn.name.ptr()); } else _output.printf("<reaction>\n"); int i; MoleculeCmlSaver molsaver(_output); molsaver.skip_cml_tag = true; if (rxn.reactantsCount() > 0) { _output.printf("<reactantList>\n"); for (i = rxn.reactantBegin(); i != rxn.reactantEnd(); i = rxn.reactantNext(i)) molsaver.saveMolecule(rxn.getMolecule(i)); _output.printf("</reactantList>\n"); } if (rxn.productsCount() > 0) { _output.printf("<productList>\n"); for (i = rxn.productBegin(); i != rxn.productEnd(); i = rxn.productNext(i)) molsaver.saveMolecule(rxn.getMolecule(i)); _output.printf("</productList>\n"); } if (rxn.catalystCount() > 0) { _output.printf("<spectatorList>\n"); for (i = rxn.catalystBegin(); i != rxn.catalystEnd(); i = rxn.catalystNext(i)) molsaver.saveMolecule(rxn.getMolecule(i)); _output.printf("</spectatorList>\n"); } _output.printf("</reaction>\n"); if (!skip_cml_tag) _output.printf("</cml>\n"); } �����������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_enumerator_state.cpp��������������������������������������0000664�0000000�0000000�00000213343�12710376503�0024553�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_product_enumerator.h" #include "reaction/reaction_enumerator_state.h" #include "base_cpp/output.h" #include "base_cpp/gray_codes.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "graph/graph.h" #include "graph/embedding_enumerator.h" #include "graph/spanning_tree.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/molecule_arom.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/molecule_substructure_matcher.h" #include "molecule/molecule_arom_match.h" #include "molecule/molfile_saver.h" #include "molecule/elements.h" #include "graph/dfs_walk.h" using namespace indigo; IMPL_ERROR(ReactionEnumeratorState::ReactionMonomers, "Reaction product enumerator"); CP_DEF(ReactionEnumeratorState::ReactionMonomers); ReactionEnumeratorState::ReactionMonomers::ReactionMonomers() : CP_INIT, TL_CP_GET(_monomers), TL_CP_GET(_reactant_indexes), TL_CP_GET(_deep_levels), TL_CP_GET(_tube_indexes) { _monomers.clear(); _reactant_indexes.clear(); _deep_levels.clear(); _tube_indexes.clear(); } int ReactionEnumeratorState::ReactionMonomers::size() { return _monomers.size(); } void ReactionEnumeratorState::ReactionMonomers::clear() { _monomers.clear(); _reactant_indexes.clear(); _deep_levels.clear(); _tube_indexes.clear(); } Molecule & ReactionEnumeratorState::ReactionMonomers::getMonomer( int reactant_idx, int index ) { int cur_idx = 0; for (int i = 0; i < _reactant_indexes.size(); i++) if (_reactant_indexes[i] == reactant_idx) if (cur_idx++ == index) return _monomers[i]; throw Error("can't find reactant's #%d monomer #%d", reactant_idx, index); } Molecule & ReactionEnumeratorState::ReactionMonomers::getMonomer( int mon_index ) { if (mon_index >= _monomers.size() || mon_index < 0) throw Error("can't find monomer #%d", mon_index); else return _monomers[mon_index]; } void ReactionEnumeratorState::ReactionMonomers::addMonomer( int reactant_idx, Molecule &monomer, int deep_level, int tube_idx ) { Molecule &new_monomer = _monomers.push(); new_monomer.clone(monomer, NULL, NULL); _reactant_indexes.push(reactant_idx); _deep_levels.push(deep_level); _tube_indexes.push(tube_idx); } void ReactionEnumeratorState::ReactionMonomers::removeMonomer( int idx ) { for (int j = idx + 1; j < _monomers.size(); j++) { _reactant_indexes[j - 1] = _reactant_indexes[j]; _monomers[j - 1].clone(_monomers[j], NULL, NULL); _deep_levels[j - 1] = _deep_levels[j]; _tube_indexes[j - 1] = _tube_indexes[j]; } _reactant_indexes.pop(); _monomers.pop(); _deep_levels.pop(); _tube_indexes.pop(); } IMPL_ERROR(ReactionEnumeratorState, "Reaction product enumerator state"); CP_DEF(ReactionEnumeratorState); ReactionEnumeratorState::ReactionEnumeratorState(ReactionEnumeratorContext &context, QueryReaction &cur_reaction, QueryMolecule &cur_full_product, Array<int> &cur_product_aam_array, RedBlackStringMap<int> &cur_smiles_array, ReactionMonomers &cur_reaction_monomers, int &cur_product_count, ObjArray< Array<int> > &cur_tubes_monomers ) : _reaction(cur_reaction), _product_count(cur_product_count), _tubes_monomers(cur_tubes_monomers), _product_aam_array(cur_product_aam_array), _smiles_array(cur_smiles_array), _reaction_monomers(cur_reaction_monomers), _context(context), CP_INIT, TL_CP_GET(_fragments_aam_array), TL_CP_GET(_full_product), TL_CP_GET(_product_monomers), TL_CP_GET(_mapping), TL_CP_GET(_fragments), TL_CP_GET(_is_needless_atom), TL_CP_GET(_is_needless_bond), TL_CP_GET(_bonds_mapping_sub), TL_CP_GET(_bonds_mapping_super), TL_CP_GET(_att_points), TL_CP_GET(_fmcache), TL_CP_GET(_monomer_forbidden_atoms), TL_CP_GET(_product_forbidden_atoms), TL_CP_GET(_original_hydrogens) { _reactant_idx = _reaction.reactantBegin(); _fmcache.clear(); _fragments_aam_array.clear(); _full_product.clear(); _full_product.clone(cur_full_product, NULL, NULL); _mapping.clear(); _fragments.clear(); _is_needless_atom.clear(); _is_needless_bond.clear(); _bonds_mapping_sub.clear(); _bonds_mapping_super.clear(); _original_hydrogens.clear(); _att_points.clear(); _att_points.resize(cur_full_product.vertexEnd()); _monomer_forbidden_atoms.clear(); _product_forbidden_atoms.clear(); _product_monomers.clear(); _am = NULL; _ee = NULL; is_multistep_reaction = false; is_self_react = false; is_one_tube = false; is_same_keeping = false; is_transform = false; _is_frag_search = false; _is_rg_exist = false; _is_simple_transform = false; _tube_idx = -1; for (int i = _reaction.reactantBegin(); i != _reaction.reactantEnd(); i = _reaction.reactantNext(i)) if (_reaction.getQueryMolecule(i).countRSites() != 0) _is_rg_exist = true; _deep_level = 0; max_deep_level = 2; max_product_count = 1000; max_reuse_count = 10; refine_proc = NULL; product_proc = NULL; userdata = NULL; } ReactionEnumeratorState::ReactionEnumeratorState( ReactionEnumeratorState &cur_rpe_state ) : _context(cur_rpe_state._context), _reaction(cur_rpe_state._reaction), _product_count(cur_rpe_state._product_count), _tubes_monomers(cur_rpe_state._tubes_monomers), _product_aam_array(cur_rpe_state._product_aam_array), _smiles_array(cur_rpe_state._smiles_array), _reaction_monomers(cur_rpe_state._reaction_monomers), CP_INIT, TL_CP_GET(_fragments_aam_array), TL_CP_GET(_full_product), TL_CP_GET(_product_monomers), TL_CP_GET(_mapping), TL_CP_GET(_fragments), TL_CP_GET(_is_needless_atom), TL_CP_GET(_is_needless_bond), TL_CP_GET(_bonds_mapping_sub), TL_CP_GET(_bonds_mapping_super), TL_CP_GET(_att_points), TL_CP_GET(_fmcache), TL_CP_GET(_monomer_forbidden_atoms), TL_CP_GET(_product_forbidden_atoms), TL_CP_GET(_original_hydrogens) { _reactant_idx = cur_rpe_state._reactant_idx; _fmcache.clear(); _fragments_aam_array.clear(); _fragments_aam_array.copy(cur_rpe_state._fragments_aam_array); _full_product.clear(); _full_product.clone(cur_rpe_state._full_product, NULL, NULL); _mapping.clear(); _mapping.copy(cur_rpe_state._mapping); _fragments.clear(); _fragments.clone(cur_rpe_state._fragments, NULL, NULL); _is_needless_atom.clear(); _is_needless_atom.copy(cur_rpe_state._is_needless_atom); _is_needless_bond.clear(); _is_needless_bond.copy(cur_rpe_state._is_needless_bond); _bonds_mapping_sub.clear(); _bonds_mapping_sub.copy(cur_rpe_state._bonds_mapping_sub); _bonds_mapping_super.clear(); _bonds_mapping_super.copy(cur_rpe_state._bonds_mapping_super); _att_points.clear(); _monomer_forbidden_atoms.clear(); _monomer_forbidden_atoms.copy(cur_rpe_state._monomer_forbidden_atoms); _product_forbidden_atoms.clear(); _product_forbidden_atoms.copy(cur_rpe_state._product_forbidden_atoms); _original_hydrogens.clear(); _original_hydrogens.copy(cur_rpe_state._original_hydrogens); for (int i = 0; i < cur_rpe_state._att_points.size(); i++) { Array<int> &new_array = _att_points.push(); new_array.copy(cur_rpe_state._att_points[i]); } _am = cur_rpe_state._am; _ee = cur_rpe_state._ee; _product_monomers.clear(); _product_monomers.copy(cur_rpe_state._product_monomers); max_product_count = cur_rpe_state.max_product_count; _tube_idx = cur_rpe_state._tube_idx; _deep_level = cur_rpe_state._deep_level; max_deep_level = cur_rpe_state.max_deep_level; max_reuse_count = cur_rpe_state.max_reuse_count; is_multistep_reaction = cur_rpe_state.is_multistep_reaction; is_self_react = cur_rpe_state.is_self_react; is_one_tube = cur_rpe_state.is_one_tube; is_same_keeping = cur_rpe_state.is_same_keeping; is_transform = cur_rpe_state.is_transform; _is_rg_exist = cur_rpe_state._is_rg_exist; _is_simple_transform = cur_rpe_state._is_simple_transform; _is_frag_search = false; refine_proc = cur_rpe_state.refine_proc; product_proc = cur_rpe_state.product_proc; userdata = cur_rpe_state.userdata; } int ReactionEnumeratorState::buildProduct( void ) { if (_product_count >= max_product_count) return 0; if (_reactant_idx == _reaction.reactantEnd()) { /* Product is ready */ _productProcess(); return 0; } if (is_transform) return 0; for (int i = 0; i < _reaction_monomers._monomers.size(); i++) { QS_DEF(Molecule, ee_monomer); ee_monomer.clear(); ee_monomer.clone(_reaction_monomers._monomers[i], NULL, NULL); ee_monomer.cis_trans.build(NULL); if (!is_one_tube) if (!_isMonomerFromCurTube(i)) continue; if (!is_self_react) if ((_reaction_monomers._deep_levels[i] != 0) && (_product_monomers.find(i) != -1)) continue; ReactionEnumeratorState rpe_state(*this); rpe_state._deep_level += _reaction_monomers._deep_levels[i]; if (rpe_state._deep_level - 1 > rpe_state.max_deep_level) return 0; rpe_state._product_monomers.push(i); rpe_state._startEmbeddingEnumerator(ee_monomer); } return 0; } int ReactionEnumeratorState::_findCurTube( void ) { int cur_tube_idx = -1; if (_product_monomers.size() == 0) return -1; for (int j = 0; j < _tubes_monomers.size(); j++) { int k; for (k = 0; k < _product_monomers.size(); k++) if (_tubes_monomers[j].find(_product_monomers[k]) == -1) break; if (k != _product_monomers.size()) continue; if (cur_tube_idx == -1) cur_tube_idx = j; else return -1; } return cur_tube_idx; } bool ReactionEnumeratorState::_isMonomerFromCurTube( int monomer_idx ) { int j; if (!is_one_tube) { for (j = 0; j < _product_monomers.size(); j++) { if (_reaction_monomers._reactant_indexes[_product_monomers[j]] == _reaction_monomers._reactant_indexes[monomer_idx]) break; } if (j != _product_monomers.size()) return false; } _tube_idx = _findCurTube(); if (_tube_idx != -1) { if (_tubes_monomers[_tube_idx].find(monomer_idx) == -1) return false; } /* Finding current tube index */ else if (_reaction_monomers._tube_indexes[monomer_idx] != -1) { for (j = 0; j < _tubes_monomers.size(); j++) { if (_tubes_monomers[j].find(monomer_idx) == -1) continue; int k; for (k = 0; k < _product_monomers.size(); k++) if (_tubes_monomers[j].find(_product_monomers[k]) == -1) break; if (k == _product_monomers.size()) break; } if (_tubes_monomers.size() == 0) _tube_idx = -1; else if (j != _tubes_monomers.size()) _tube_idx = j; else return false; } return true; } void ReactionEnumeratorState::_productProcess( void ) { if (_deep_level >= max_deep_level) return; QS_DEF(Molecule, ready_product); ready_product.clear(); QS_DEF(Array<int>, ucfrag_mapping); ucfrag_mapping.clear(); if (!_attachFragments(ready_product, ucfrag_mapping)) return; if (!is_transform) _foldHydrogens(ready_product, 0, 0, &_mapping); ready_product.dearomatize(_context.arom_options); if (!is_same_keeping) { QS_DEF(Array<char>, cur_smiles); cur_smiles.clear(); try { ArrayOutput arr_out(cur_smiles); CanonicalSmilesSaver product_cs_saver(arr_out); product_cs_saver.saveMolecule(ready_product); } catch (Exception &) { return; } cur_smiles.push(0); if (_smiles_array.find(cur_smiles.ptr())) { int *found_count = _smiles_array.at2(cur_smiles.ptr()); (*found_count)++; return; } _product_count++; _smiles_array.insert(cur_smiles.ptr(), 1); } for (int i = 0; i < _product_monomers.size(); i++) { if (_reaction_monomers._monomers[_product_monomers[i]].name.size() == 0) continue; bool is_deep = false; if (_reaction_monomers._monomers[_product_monomers[i]].name.find('+') != -1) { is_deep = true; ready_product.name.push('('); } ready_product.name.concat(_reaction_monomers._monomers[_product_monomers[i]].name); ready_product.name.pop(); if (is_deep) ready_product.name.push(')'); ready_product.name.push('+'); } if (ready_product.name.size() != 0) ready_product.name.top() = 0; /* Adding a product to monomers lists */ if (is_multistep_reaction && !is_transform) { int tube_idx = _findCurTube(); for (int i = _reaction.reactantBegin(); i != _reaction.reactantEnd(); i = _reaction.reactantNext(i)) { if (!is_one_tube) _tubes_monomers[tube_idx].push(_reaction_monomers.size()); _reaction_monomers.addMonomer(i, ready_product, _deep_level + 1, tube_idx); } } if (!_is_simple_transform) ready_product.clearXyz(); if (product_proc != NULL) product_proc(ready_product, _product_monomers, _mapping, userdata); } void ReactionEnumeratorState::_foldHydrogens(BaseMolecule &molecule, Array<int> *atoms_to_keep, Array<int> *original_hydrogens, Array<int> *mol_mapping) { QS_DEF(Array<int>, hydrogens); hydrogens.clear(); for (int i = molecule.vertexBegin(); i != molecule.vertexEnd(); i = molecule.vertexNext(i)) { if ((atoms_to_keep != 0) && (atoms_to_keep->at(i))) continue; if ((original_hydrogens != 0) && (original_hydrogens->find(i) != -1)) continue; if (molecule.getAtomNumber(i) != ELEM_H || (molecule.getAtomIsotope(i) != 0 && molecule.getAtomIsotope(i) != -1)) continue; const Vertex &v = molecule.getVertex(i); if (v.degree() == 0) continue; if (v.degree() == 1) { int h_nei = v.neiVertex(v.neiBegin()); if (molecule.getAtomNumber(h_nei) == ELEM_H && molecule.getAtomIsotope(h_nei) == 0) continue; // do not remove rare H-H fragment if (molecule.stereocenters.exists(h_nei) && molecule.stereocenters.getPyramid(h_nei)[3] == -1) continue; } hydrogens.push(i); } molecule.removeAtoms(hydrogens); if (mol_mapping != 0) { for (int i = 0; i < hydrogens.size(); i++) { int h_id = mol_mapping->find(hydrogens[i]); if (h_id != -1) mol_mapping->at(h_id) = -1; } } } bool ReactionEnumeratorState::_nextMatchProcess( EmbeddingEnumerator &ee, const QueryMolecule &reactant, const Molecule &monomer ) { ReactionEnumeratorState rpe_state(*this); rpe_state._ee = ⅇ rpe_state._is_frag_search = _is_frag_search; ee.userdata = &rpe_state; bool stop_flag = ee.processNext(); _bonds_mapping_sub.copy(rpe_state._bonds_mapping_sub); _bonds_mapping_super.copy(rpe_state._bonds_mapping_super); _product_forbidden_atoms.copy(rpe_state._product_forbidden_atoms); _original_hydrogens.copy(rpe_state._original_hydrogens); return stop_flag; } int ReactionEnumeratorState::_calcMaxHCnt( QueryMolecule &molecule ) { int max_possible_h_cnt = 0; for (int i = molecule.vertexBegin(); i != molecule.vertexEnd(); i = molecule.vertexNext(i)) { int possible_h_cnt = 0; const Vertex &v = molecule.getVertex(i); for (int j = v.neiBegin(); j != v.neiEnd(); j = v.neiNext(j)) if (molecule.possibleAtomNumber(v.neiVertex(j), ELEM_H)) possible_h_cnt++; if (possible_h_cnt > max_possible_h_cnt) max_possible_h_cnt = possible_h_cnt; } return max_possible_h_cnt; } bool ReactionEnumeratorState::performSingleTransformation( Molecule &molecule, Array<int> &mapping, Array<int> &forbidden_atoms, Array<int> &original_hydrogens, bool &need_layout) { is_transform = true; _is_simple_transform = _checkForSimplicity(); if (forbidden_atoms.size() != molecule.vertexEnd()) throw Error("forbidden atoms array size is incorrect"); _monomer_forbidden_atoms.copy(forbidden_atoms); _original_hydrogens.copy(original_hydrogens); _mapping.copy(mapping); if (!_startEmbeddingEnumerator(molecule)) { _foldHydrogens(molecule, &forbidden_atoms, &_original_hydrogens, &_mapping); return false; } original_hydrogens.copy(_original_hydrogens); forbidden_atoms.copy(_product_forbidden_atoms); need_layout = !_is_simple_transform; return true; } bool ReactionEnumeratorState::_startEmbeddingEnumerator( Molecule &monomer ) { QS_DEF(QueryMolecule, ee_reactant); ee_reactant.clear(); ee_reactant.clone(_reaction.getQueryMolecule(_reactant_idx), NULL, NULL); ee_reactant.cis_trans.build(NULL); ee_reactant.aromatize(_context.arom_options); for (int i = ee_reactant.edgeBegin(); i != ee_reactant.edgeEnd(); i = ee_reactant.edgeNext(i)) { const Edge &edge = ee_reactant.getEdge(i); if (ee_reactant.isRSite(edge.beg) && ee_reactant.isRSite(edge.end)) throw Error("one RGroup can't be a neighbor of another"); } /* Finding in reactant query atoms with two neighbors */ QS_DEF(Array<int>, qa_array); qa_array.clear(); for (int j = ee_reactant.vertexBegin(); j != ee_reactant.vertexEnd(); j = ee_reactant.vertexNext(j)) { const Vertex &vertex = ee_reactant.getVertex(j); if (!ee_reactant.isRSite(j)) continue; if (vertex.degree() > 2) throw Error("query atom can't have more than two neighbors"); if (vertex.degree() == 2) _changeQueryNode(ee_reactant, j); } QS_DEF(Molecule, ee_monomer); ee_monomer.clear(); ee_monomer.clone(monomer, NULL, NULL); ee_monomer.aromatize(_context.arom_options); if (BaseMolecule::hasCoord(ee_monomer)) { // Double Cis or Trans bonds are excluded from cis-trans build QS_DEF(Array<int>, cis_trans_excluded); cis_trans_excluded.clear_resize(ee_monomer.edgeEnd()); cis_trans_excluded.zerofill(); for (int i = ee_monomer.edgeBegin(); i < ee_monomer.edgeEnd(); i = ee_monomer.edgeNext(i)) { if (ee_monomer.cis_trans.isIgnored(i)) cis_trans_excluded[i] = 1; } ee_monomer.cis_trans.build(cis_trans_excluded.ptr()); } QS_DEF(Obj<AromaticityMatcher>, am); am.free(); am.create(ee_reactant, ee_monomer, _context.arom_options); _am = am.get(); ee_monomer.unfoldHydrogens(NULL, _calcMaxHCnt(ee_reactant), true); _bonds_mapping_sub.clear_resize(ee_reactant.edgeEnd()); _bonds_mapping_sub.fffill(); _bonds_mapping_super.clear_resize(ee_monomer.edgeEnd()); _bonds_mapping_super.fffill(); EmbeddingEnumerator ee(ee_monomer); ee.cb_embedding = _embeddingCallback; ee.cb_match_vertex = _matchVertexCallback; ee.cb_match_edge = _matchEdgeCallback; ee.cb_vertex_remove = _removeAtomCallback; ee.cb_edge_add = _addBondCallback; ee.cb_allow_many_to_one = _allowManyToOneCallback; ee.userdata = this; ee.setSubgraph(ee_reactant); ee.allow_many_to_one = true; ee.processStart(); while (true) { bool stop_flag = _nextMatchProcess(ee, ee_reactant, ee_monomer); if (!stop_flag) return false; if (is_transform) break; } return true; } void ReactionEnumeratorState::_changeQueryNode( QueryMolecule &ee_reactant, int change_atom_idx ) { QS_DEF(QueryMolecule, reactant_copy); reactant_copy.clear(); reactant_copy.clone(ee_reactant, NULL, NULL); /* Making one query atom for one bond */ const Vertex &vertex = ee_reactant.getVertex(change_atom_idx); QueryMolecule::Atom &atom = ee_reactant.getAtom(change_atom_idx); for (int j = vertex.neiNext(vertex.neiBegin()); j != vertex.neiEnd(); j = vertex.neiNext(j)) { int nv_idx = vertex.neiVertex(j); int new_atom_idx = reactant_copy.addAtom(atom.clone()); reactant_copy.setRSiteAttachmentOrder(new_atom_idx, nv_idx, 0); reactant_copy.flipBond(nv_idx, change_atom_idx, new_atom_idx); } ee_reactant.clone(reactant_copy, NULL, NULL); } bool ReactionEnumeratorState::_matchVertexCallback( Graph &subgraph, Graph &supergraph, const int *core_sub, int sub_idx, int super_idx, void *userdata ) { ReactionEnumeratorState *rpe_state = (ReactionEnumeratorState *)userdata; Molecule &supermolecule = (Molecule &)supergraph; QueryMolecule &submolecule = (QueryMolecule &)subgraph; QueryMolecule::Atom &qa_sub = submolecule.getAtom(sub_idx); const Vertex &sub_v = submolecule.getVertex(sub_idx); const Vertex &super_v = supermolecule.getVertex(super_idx); bool res = MoleculeSubstructureMatcher::matchQueryAtom(&qa_sub, supermolecule, super_idx, &(rpe_state->_fmcache), 0xFFFFFFFFUL); if (!res) return false; if (rpe_state->is_transform) if (super_idx < rpe_state->_monomer_forbidden_atoms.size()) // otherwise super atom is unfolded hydrogen if (rpe_state->_monomer_forbidden_atoms[super_idx] >= rpe_state->max_reuse_count) return false; if (supermolecule.getAtomNumber(super_idx) == ELEM_H && sub_v.degree() != 0 && super_v.degree() != 0) { int sub_free_rg_count = 0; int sub_nei = sub_v.neiVertex(sub_v.neiBegin()); const Vertex &sub_nei_v = submolecule.getVertex(sub_nei); for (int i = sub_nei_v.neiBegin(); i != sub_nei_v.neiEnd(); i = sub_nei_v.neiNext(i)) { if (rpe_state->_bonds_mapping_sub[sub_nei_v.neiEdge(i)] < 0 && sub_nei_v.neiVertex(i) != sub_idx) sub_free_rg_count++; } int super_free_atoms_count = 0; const Vertex &super_v = supermolecule.getVertex(super_idx); int super_nei = super_v.neiVertex(super_v.neiBegin()); const Vertex &super_nei_v = supermolecule.getVertex(super_nei); for (int i = super_nei_v.neiBegin(); i != super_nei_v.neiEnd(); i = super_nei_v.neiNext(i)) { if (supermolecule.getAtomNumber(super_nei_v.neiVertex(i)) == ELEM_H) { int h_idx = super_nei_v.neiVertex(i); if ((rpe_state->_ee->getSupergraphMapping()[h_idx] < 0) && (h_idx < super_idx)) return false; } else if (rpe_state->_bonds_mapping_super[super_nei_v.neiEdge(i)] < 0) super_free_atoms_count++; } if (rpe_state->_is_rg_exist && sub_free_rg_count < super_free_atoms_count) return false; } if (rpe_state->_is_rg_exist && !submolecule.isRSite(sub_idx) && !submolecule.isPseudoAtom(sub_idx)) { int super_unfolded_h_cnt = supermolecule.getAtomTotalH(super_idx) - supermolecule.getImplicitH(super_idx); if (super_v.degree() - super_unfolded_h_cnt > sub_v.degree()) return false; } return res; } bool ReactionEnumeratorState::_matchEdgeCallback( Graph &subgraph, Graph &supergraph, int self_idx, int other_idx, void *userdata ) { ReactionEnumeratorState *rpe_state = (ReactionEnumeratorState *)userdata; Molecule &supermolecule = (Molecule &)supergraph; QueryMolecule &submolecule = (QueryMolecule &)subgraph; QueryMolecule::Bond &qb_sub = submolecule.getBond(self_idx); if (rpe_state->_bonds_mapping_super[other_idx] >= 0) return false; bool res = MoleculeSubstructureMatcher::matchQueryBond(&qb_sub, supermolecule, self_idx, other_idx, rpe_state->_am, 0xFFFFFFFFUL); return res; } void ReactionEnumeratorState::_findFragAtoms( Array<byte> &unfrag_mon_atoms, QueryMolecule &submolecule, Molecule &fragment, int *core_sub, int *core_super ) { for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i)) { if (_is_rg_exist && !submolecule.isRSite(i)) unfrag_mon_atoms[core_sub[i]] = 1; const Vertex &sub_v = submolecule.getVertex(i); const Vertex &frag_v = fragment.getVertex(core_sub[i]); if (!_is_rg_exist && (sub_v.degree() == frag_v.degree())) unfrag_mon_atoms[core_sub[i]] = 1; } } void ReactionEnumeratorState::_cleanFragments( void ) { if (_is_rg_exist) { QS_DEF(Array<int>, is_attached_hydrogen); is_attached_hydrogen.clear(); is_attached_hydrogen.resize(_fragments.vertexEnd()); is_attached_hydrogen.zerofill(); for (int i = 0; i < _att_points.size(); i++) for (int j = 0; j < _att_points[i].size(); j++) if (_fragments.getAtomNumber(_att_points[i][j]) == ELEM_H) is_attached_hydrogen[_att_points[i][j]] = 1; for (int i = _fragments.vertexBegin(); i != _fragments.vertexEnd(); i = _fragments.vertexNext(i)) { if (_fragments.getAtomNumber(i) != ELEM_H) continue; const Vertex &h = _fragments.getVertex(i); if (h.degree() == 0) continue; int h_nei = h.neiVertex(h.neiBegin()); if (_fragments.stereocenters.exists(h_nei) && !_is_needless_atom[h_nei]) continue; if (!is_attached_hydrogen[i]) _fragments.removeAtom(i); } } for (int i = _fragments.vertexBegin(); i != _fragments.vertexEnd(); i = _fragments.vertexNext(i)) if (_is_needless_atom[i]) _fragments.removeAtom(i); for (int i = _fragments.edgeBegin(); i != _fragments.edgeEnd(); i = _fragments.edgeNext(i)) if (_is_needless_bond[i]) _fragments.removeBond(i); } void ReactionEnumeratorState::_findR2PMapping( QueryMolecule &reactant, Array<int> &mapping ) { const Array<int> &reactant_aam_array = _reaction.getAAMArray(_reactant_idx); for(int i = reactant.vertexBegin(); i != reactant.vertexEnd(); i = reactant.vertexNext(i)) { if ((i >= reactant_aam_array.size()) && (!reactant.isRSite(i))) break; if (reactant.isRSite(i)) { for (int j = _full_product.vertexBegin(); j != _full_product.vertexEnd(); j = _full_product.vertexNext(j)) { if (!_full_product.isRSite(j)) continue; int pr_rg_idx = _full_product.getSingleAllowedRGroup(j); int sub_rg_idx = reactant.getSingleAllowedRGroup(i); if (pr_rg_idx != sub_rg_idx) continue; mapping[i] = j; break; } } else if (reactant_aam_array[i] != 0) mapping[i] = _product_aam_array.find(reactant_aam_array[i]); } } void ReactionEnumeratorState::_invertStereocenters( Molecule &molecule, int edge_idx ) { const Edge &edge = molecule.getEdge(edge_idx); int edge_end_idx = edge.end; const Vertex &edge_end = molecule.getVertex(edge_end_idx); int other_end_idx = edge.findOtherEnd(edge_end_idx); QS_DEF(Array<int>, was_atoms); was_atoms.clear_resize(molecule.vertexEnd()); was_atoms.zerofill(); for (int i = edge_end.neiBegin(); i != edge_end.neiEnd(); i = edge_end.neiNext(i)) { int nei_atom_idx = edge_end.neiVertex(i); if (nei_atom_idx == other_end_idx) continue; QS_DEF(Array<int>, ignored_atoms); ignored_atoms.clear_resize(molecule.vertexEnd()); ignored_atoms.zerofill(); ignored_atoms[edge_end_idx] = 1; QS_DEF(Array<int>, atom_ranks); atom_ranks.clear_resize(molecule.vertexEnd()); atom_ranks.zerofill(); atom_ranks[nei_atom_idx] = -1; DfsWalk dfs(molecule); dfs.ignored_vertices = ignored_atoms.ptr(); dfs.vertex_ranks = atom_ranks.ptr(); dfs.walk(); const Array<DfsWalk::SeqElem> &atoms_to_reflect = dfs.getSequence(); for (int j = 0; j < atoms_to_reflect.size(); j++) { if (((j > 0) && (atoms_to_reflect[j].parent_vertex == -1)) || was_atoms[atoms_to_reflect[j].idx]) break; if (molecule.stereocenters.exists(atoms_to_reflect[j].idx)) molecule.stereocenters.invertPyramid(atoms_to_reflect[j].idx); was_atoms[atoms_to_reflect[j].idx] = 1; } } } void ReactionEnumeratorState::_cistransUpdate( QueryMolecule &submolecule, Molecule &supermolecule, int *frag_mapping, const Array<int> &rp_mapping, int *core_sub) { QS_DEF(Array<int>, cistrans_changed_bonds); cistrans_changed_bonds.clear(); for(int i = submolecule.edgeBegin(); i != submolecule.edgeEnd(); i = submolecule.edgeNext(i)) { if (!MoleculeCisTrans::isGeomStereoBond(submolecule, i, NULL, false)) continue; const Edge &edge = submolecule.getEdge(i); const int *subs = submolecule.cis_trans.getSubstituents(i); if ((rp_mapping[edge.beg] == -1) || (rp_mapping[edge.end] == -1)) { continue; //or throw Error("Incorrect AAM on stereo bond"); } for (int j = 0; j < 2; j++) { if ((subs[j] != -1) && (rp_mapping[subs[j]] != -1) && (_full_product.findEdgeIndex(rp_mapping[subs[j]], rp_mapping[edge.beg]) == -1)) return; if ((subs[j + 2] != -1) && (rp_mapping[subs[j + 2]] != -1) && (_full_product.findEdgeIndex(rp_mapping[subs[j + 2]], rp_mapping[edge.end]) == -1)) return; } int ss_sign, sp_sign; ss_sign = MoleculeCisTrans::getMappingParitySign(submolecule, supermolecule, i, core_sub); sp_sign = MoleculeCisTrans::getMappingParitySign(submolecule, _full_product, i, rp_mapping.ptr()); if (sp_sign > 0) continue; int product_edge_idx = _full_product.findMappedEdge(submolecule, _full_product, i, rp_mapping.ptr()); if (product_edge_idx == -1) continue; if (_full_product.bondStereoCare(product_edge_idx)) continue; if (sp_sign * ss_sign > 0) { int new_parity = _full_product.cis_trans.getParity(product_edge_idx); new_parity = (new_parity == MoleculeCisTrans::CIS ? MoleculeCisTrans::TRANS : MoleculeCisTrans::CIS); _full_product.cis_trans.setParity(product_edge_idx, new_parity); int super_edge_idx = supermolecule.findMappedEdge(submolecule, supermolecule, i, core_sub); _invertStereocenters(supermolecule, super_edge_idx); } } } QueryMolecule::Atom * ReactionEnumeratorState::_getReactantAtom( int atom_aam ) { for (int i = _reaction.reactantBegin(); i != _reaction.reactantEnd(); i = _reaction.reactantNext(i)) { int atom_idx = _reaction.getAAMArray(i).find(atom_aam); if (atom_idx != -1) { return &_reaction.getQueryMolecule(i).getAtom(atom_idx); } } return NULL; } void ReactionEnumeratorState::_buildMolProduct( QueryMolecule &product, Molecule &mol_product, Molecule &uncleaned_fragments, Array<int> &all_forbidden_atoms, Array<int> &mapping_out ) { mol_product.clear(); mapping_out.clear_resize(product.vertexEnd()); mapping_out.fffill(); for (int i = product.vertexBegin(); i != product.vertexEnd(); i = product.vertexNext(i)) { bool has_aam = true, is_default = false; int pr_aam = _product_aam_array[i]; int frags_idx = -1; if (pr_aam == 0) has_aam = false; else { frags_idx = _fragments_aam_array.find(pr_aam); if (frags_idx == -1) throw Error("Incorrect AAM"); } int mol_atom_idx = -1; if (has_aam && (uncleaned_fragments.getAtomNumber(frags_idx) != product.getAtomNumber(i)) && (product.getAtomNumber(i) != -1)) is_default = true; QueryMolecule::Atom *reactant_atom = _getReactantAtom(pr_aam); if ((product.getAtomNumber(i) == -1 && !product.isPseudoAtom(i)) && !is_default && !product.isRSite(i)) { if (!has_aam) throw Error("Incorrect AAM"); if (!product.getAtom(i).possibleValue(QueryMolecule::ATOM_NUMBER, uncleaned_fragments.getAtomNumber(frags_idx))) throw Error("product atom's impossible number"); else mol_atom_idx = mol_product.addAtom(uncleaned_fragments.getAtomNumber(frags_idx)); } else { mol_atom_idx = mol_product.addAtom(product.getAtomNumber(i)); if (product.isPseudoAtom(i)) mol_product.setPseudoAtom(i, product.getPseudoAtom(i)); } /* "charge", "radical" or "isotope" parameters have no sense for pseudoatoms */ if (!product.isPseudoAtom(i) && !(has_aam && uncleaned_fragments.isPseudoAtom(frags_idx))) { /* Charge copying */ int reactant_atom_charge = CHARGE_UNKNOWN; if (reactant_atom != 0) reactant_atom->sureValue(QueryMolecule::ATOM_CHARGE, reactant_atom_charge); if ((product.getAtomCharge(i) == CHARGE_UNKNOWN) && (!is_default) && (reactant_atom_charge == product.getAtomCharge(i))) { if (has_aam) { try { mol_product.setAtomCharge(mol_atom_idx, uncleaned_fragments.getAtomCharge(frags_idx)); } catch (Element::Error &) { } catch (Molecule::Error &) { } } } else { int pr_charge = product.getAtomCharge(i); mol_product.setAtomCharge(mol_atom_idx, (pr_charge != CHARGE_UNKNOWN ? pr_charge : 0)); } /* Isotope copying*/ int reactant_atom_isotope = -1; if (reactant_atom != 0) reactant_atom->sureValue(QueryMolecule::ATOM_ISOTOPE, reactant_atom_isotope); if ((product.getAtomIsotope(i) == -1) && (!is_default) && (reactant_atom_isotope == product.getAtomIsotope(i))) { if (has_aam) { try { mol_product.setAtomIsotope(mol_atom_idx, uncleaned_fragments.getAtomIsotope(frags_idx)); } catch (Element::Error &) { } catch (Molecule::Error &) { } } } else { int pr_isotope = product.getAtomIsotope(i); mol_product.setAtomIsotope(mol_atom_idx, (pr_isotope != -1 ? pr_isotope : 0)); } /* Radical copying */ int reactant_atom_radical = -1; if (reactant_atom != 0) reactant_atom->sureValue(QueryMolecule::ATOM_RADICAL, reactant_atom_radical); if ((product.getAtomRadical(i) == -1) && (!is_default) && (reactant_atom_radical == product.getAtomRadical(i))) { if (has_aam) { try { int frag_radical = uncleaned_fragments.getAtomRadical(frags_idx); mol_product.setAtomRadical(mol_atom_idx, frag_radical); } catch (Element::Error &) { } catch (Molecule::Error &) { } } } else { int pr_radical = product.getAtomRadical(i); mol_product.setAtomRadical(mol_atom_idx, (pr_radical != -1 ? pr_radical : 0)); } } if (_is_simple_transform && frags_idx == -1) throw Error("Incorrect AAM"); if (_is_simple_transform) mol_product.setAtomXyz(mol_atom_idx, uncleaned_fragments.getAtomXyz(frags_idx)); else mol_product.setAtomXyz(mol_atom_idx, product.getAtomXyz(i).x, product.getAtomXyz(i).y, product.getAtomXyz(i).z); mapping_out[i] = mol_atom_idx; if (frags_idx != -1 && frags_idx < _monomer_forbidden_atoms.size()) all_forbidden_atoms[mapping_out[i]] += _monomer_forbidden_atoms[frags_idx]; else all_forbidden_atoms[mapping_out[i]] = max_reuse_count; } for (int i = product.edgeBegin(); i != product.edgeEnd(); i = product.edgeNext(i)) { const Edge &pr_edge = product.getEdge(i); if (product.getBondOrder(i) == -1) { bool has_aam = true; int pr_beg_aam = _product_aam_array[pr_edge.beg]; int pr_end_aam = _product_aam_array[pr_edge.end]; int frags_beg = -1, frags_end = -1; if (pr_beg_aam != 0) { frags_beg = _fragments_aam_array.find(pr_beg_aam); if (frags_beg == -1) throw Error("Incorrect AAM"); } if (pr_end_aam != 0) { frags_end = _fragments_aam_array.find(pr_end_aam); if (frags_end == -1) throw Error("Incorrect AAM"); } if (frags_beg != -1 && frags_end == -1) { if (product.isRSite(pr_edge.end)) { frags_end = _att_points[pr_edge.end][0]; if (uncleaned_fragments.findEdgeIndex(frags_beg, frags_end) == -1) frags_end = _att_points[pr_edge.end][1]; } } else if (frags_beg == -1 && frags_end != -1) { if (product.isRSite(pr_edge.beg)) { frags_beg = _att_points[pr_edge.beg][0]; if (uncleaned_fragments.findEdgeIndex(frags_beg, frags_end) == -1) frags_beg = _att_points[pr_edge.beg][1]; } } int frags_bond_idx = -1; if ((frags_beg != -1) && (frags_end != -1)) frags_bond_idx = uncleaned_fragments.findEdgeIndex(frags_beg, frags_end); if (frags_bond_idx == -1) has_aam = false; if (has_aam) { mol_product.addBond(mapping_out[pr_edge.beg], mapping_out[pr_edge.end], uncleaned_fragments.getBondOrder(frags_bond_idx)); } else { // If there is no information about this bond in smarts QueryMolecule::Atom &q_pr_beg = product.getAtom(pr_edge.beg); QueryMolecule::Atom &q_pr_end = product.getAtom(pr_edge.end); //int beg_value, end_value; bool can_be_aromatic = product.getBond(i).possibleValue(QueryMolecule::BOND_ORDER, BOND_AROMATIC); bool can_be_single = product.getBond(i).possibleValue(QueryMolecule::BOND_ORDER, BOND_SINGLE); bool can_be_double = product.getBond(i).possibleValue(QueryMolecule::BOND_ORDER, BOND_DOUBLE); bool can_be_triple = product.getBond(i).possibleValue(QueryMolecule::BOND_ORDER, BOND_TRIPLE); if ((can_be_aromatic && can_be_single && !can_be_double && !can_be_triple)/* && (q_pr_beg.sureValue(QueryMolecule::ATOM_AROMATICITY, beg_value) && q_pr_end.sureValue(QueryMolecule::ATOM_AROMATICITY, end_value)) && (beg_value == ATOM_AROMATIC) && (end_value == ATOM_AROMATIC)*/) { mol_product.addBond(mapping_out[pr_edge.beg], mapping_out[pr_edge.end], BOND_AROMATIC); } else if ((!can_be_aromatic && can_be_single && !can_be_double && !can_be_triple)) { mol_product.addBond(mapping_out[pr_edge.beg], mapping_out[pr_edge.end], BOND_SINGLE); } else throw Error("There is no information about products bond #%d", i); } } else mol_product.addBond(mapping_out[pr_edge.beg], mapping_out[pr_edge.end], product.getBondOrder(i)); } mol_product.stereocenters.buildOnSubmolecule(product.stereocenters, mapping_out.ptr()); mol_product.cis_trans.buildOnSubmolecule(product, mapping_out.ptr()); mol_product.mergeSGroupsWithSubmolecule(product, mapping_out); } void ReactionEnumeratorState::_stereocentersUpdate( QueryMolecule &submolecule, Molecule &supermolecule, const Array<int> &rp_mapping, int *core_sub, int *core_super ) { QS_DEF(Array<int>, mp_mapping); mp_mapping.clear_resize(supermolecule.vertexEnd()); mp_mapping.fffill(); /* Finding of monomer to product atom to atom mapping */ for(int i = supermolecule.vertexBegin(); i != supermolecule.vertexEnd(); i = supermolecule.vertexNext(i)) mp_mapping[i] = ((core_super[i] != -1) ? rp_mapping[core_super[i]] : -1); for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i)) { int sub_ex = 1, pr_ex = 1; if (submolecule.isRSite(i)) continue; if (!submolecule.stereocenters.exists(i)) sub_ex = -1; if (!_full_product.stereocenters.exists(rp_mapping[i])) pr_ex = -1; if (!supermolecule.stereocenters.exists(core_sub[i])) continue; if (rp_mapping[i] == -1) continue; if (_full_product.getVertex(rp_mapping[i]).degree() < 3) continue; int mon_type, mon_group, mon_pyramid[4]; supermolecule.stereocenters.get(core_sub[i], mon_type, mon_group, mon_pyramid); int new_pr_pyramid[4]; for (int j = 0; j < 4; j++) new_pr_pyramid[j] = ((mon_pyramid[j] != -1) ? mp_mapping[mon_pyramid[j]] : -1); MoleculeStereocenters::moveMinimalToEnd(new_pr_pyramid); if (new_pr_pyramid[0] == -1 || new_pr_pyramid[1] == -1 || new_pr_pyramid[2] == -1) continue; if (sub_ex * pr_ex < 0) { continue; } else if ((sub_ex < 0) && (pr_ex < 0)) { /* if there is no stereo info in reaction take monomer stereo info*/ _full_product.stereocenters.add(rp_mapping[i], mon_type, mon_group, new_pr_pyramid); continue; } else { int rct_type = submolecule.stereocenters.getType(i); int pr_type = _full_product.stereocenters.getType(rp_mapping[i]); int pr_group = _full_product.stereocenters.getGroup(rp_mapping[i]); if ((rct_type == MoleculeStereocenters::ATOM_ANY) || (pr_type == MoleculeStereocenters::ATOM_ANY)) continue; if (pr_type == MoleculeStereocenters::ATOM_ABS) continue; if (pr_type != MoleculeStereocenters::ATOM_OR) { pr_type = mon_type; pr_group = mon_group; } int mapping[4]; /* Reactant to product stereocenter's pyramid mapping finding */ MoleculeStereocenters::getPyramidMapping(submolecule.stereocenters, _full_product.stereocenters, i, rp_mapping.ptr(), mapping, false); _full_product.stereocenters.remove(rp_mapping[i]); _full_product.stereocenters.add(rp_mapping[i], pr_type, pr_group, new_pr_pyramid); if (MoleculeStereocenters::isPyramidMappingRigid(mapping)) continue; _full_product.stereocenters.invertPyramid(rp_mapping[i]); _full_product.clearBondDirections(); _full_product.stereocenters.markBonds(); _full_product.allene_stereo.markBonds(); } } } void ReactionEnumeratorState::_completeCisTrans( Molecule &product, Molecule &uncleaned_fragments, Array<int> &frags_mapping ) { for (int i = _fragments.edgeBegin(); i != _fragments.edgeEnd(); i = _fragments.edgeNext(i)) { if ((_fragments.getBondOrder(i) != BOND_DOUBLE) || (_fragments.cis_trans.getParity(i) != 0) || (uncleaned_fragments.cis_trans.getParity(i) == 0)) continue; const Edge &edge = uncleaned_fragments.getEdge(i); const int *subs = uncleaned_fragments.cis_trans.getSubstituents(i); int new_subs[4]; int k; for (k = 0; k < 4; k++) { if (subs[k] == -1 || ((uncleaned_fragments.getAtomNumber(subs[k]) == ELEM_H) && frags_mapping[subs[k]] == -1)) // it's removed hydrogen { new_subs[k] = -1; continue; } if (frags_mapping[subs[k]] == -1) { int sub_aam = _fragments_aam_array[subs[k]]; if (sub_aam == 0) break; int substiuent = _product_aam_array.find(sub_aam); if (substiuent == -1) break; new_subs[k] = substiuent; continue; } new_subs[k] = frags_mapping[subs[k]]; } if (k < 4) continue; int pr_bond_idx = product.findEdgeIndex(frags_mapping[edge.beg], frags_mapping[edge.end]); /* if begin of edge in fragments matches end off edge in product subs pairs should be swaped */ int tmp; if (frags_mapping[edge.beg] == product.getEdge(pr_bond_idx).end) { __swap(new_subs[0], new_subs[2], tmp); __swap(new_subs[1], new_subs[3], tmp); } product.cis_trans.add(pr_bond_idx, new_subs, uncleaned_fragments.cis_trans.getParity(i)); } } bool ReactionEnumeratorState::_checkValence( Molecule &mol, int atom_idx ) { if (mol.isPseudoAtom(atom_idx)) return true; try { mol.getAtomValence(atom_idx); } catch (Element::Error &) { return false; } return true; } void ReactionEnumeratorState::_findFragments2ProductMapping( Array<int> &f2p_mapping ) { f2p_mapping.clear_resize(_fragments.vertexEnd()); f2p_mapping.fffill(); for (int i = _full_product.vertexBegin(); i != _full_product.vertexEnd(); i = _full_product.vertexNext(i)) { int pr_aam = _product_aam_array[i]; if (pr_aam <= 0) continue; int nei_in_fragments = -1; nei_in_fragments = _fragments_aam_array.find(pr_aam); if (nei_in_fragments == -1) continue; f2p_mapping[nei_in_fragments] = i; } } bool ReactionEnumeratorState::_attachFragments( Molecule &ready_product_out, Array<int> &ucfrag_mapping ) { QS_DEF(Array<int>, frags2product_mapping); _findFragments2ProductMapping(frags2product_mapping); QS_DEF(QueryMolecule, product); product.clear(); product.clone(_full_product, NULL, NULL); QS_DEF(Molecule, uncleaned_fragments); uncleaned_fragments.clear(); uncleaned_fragments.clone(_fragments, NULL, NULL); QS_DEF(Molecule, mol_product); mol_product.clear(); QS_DEF(Array<int>, mapping); mapping.clear(); QS_DEF(Array<int>, all_forbidden_atoms); all_forbidden_atoms.clear(); all_forbidden_atoms.clear_resize(product.vertexEnd() + _fragments.vertexCount()); all_forbidden_atoms.zerofill(); for (int i = product.vertexBegin(); i != product.vertexEnd(); i = product.vertexNext(i)) all_forbidden_atoms[i] = 1; _buildMolProduct(_full_product, mol_product, uncleaned_fragments, all_forbidden_atoms, mapping); _cleanFragments(); QS_DEF(Array<int>, frags_mapping); frags_mapping.clear_resize(_fragments.vertexEnd()); frags_mapping.fffill(); mol_product.mergeWithMolecule(_fragments, &frags_mapping); for (int i = _fragments.vertexBegin(); i < _fragments.vertexEnd(); i = _fragments.vertexNext(i)) if (i < _monomer_forbidden_atoms.size() && _monomer_forbidden_atoms[i]) all_forbidden_atoms[frags_mapping[i]] = _monomer_forbidden_atoms[i]; QS_DEF(Array<int>, product_mapping); product_mapping.clear_resize(_full_product.vertexEnd()); for (int i = 0; i < _full_product.vertexEnd(); i++) product_mapping[i] = i; for (int i = _full_product.vertexBegin(); i != _full_product.vertexEnd(); i = _full_product.vertexNext(i)) { if (_att_points[i].size() == 0) continue; const Vertex &pr_v = mol_product.getVertex(i); QS_DEF(Array<int>, pr_neibours); pr_neibours.clear(); for (int j = pr_v.neiBegin(); j != pr_v.neiEnd(); j = pr_v.neiNext(j)) pr_neibours.push(pr_v.neiVertex(j)); if (_is_rg_exist && (pr_neibours.size() == 2)) for (int j = 0; j < pr_neibours.size(); j++) if (_product_aam_array[pr_neibours[j]] == 0) throw Error("There are no AAM on RGroups attachment points"); if (_is_rg_exist) { if (pr_neibours.size() > 2) throw Error("RGroup atom can't have more than two neighbors"); /* Setting the order of rgroup atom neighbors by AAM (less is first) */ if (pr_neibours.size() == 2) { if (((_product_aam_array.size() > pr_neibours[0]) && (_product_aam_array.size() > pr_neibours[1])) && _product_aam_array[pr_neibours[0]] > _product_aam_array[pr_neibours[1]]) { int tmp = pr_neibours[0]; pr_neibours[0] = pr_neibours[1]; pr_neibours[1] = tmp; } } } bool is_valid = false; if (is_transform && _att_points[i].size() != 0 && _checkValence(mol_product, frags_mapping[_att_points[i][0]])) is_valid = true; if (_is_rg_exist) { for (int j = 0; j < pr_neibours.size(); j++) { if (mol_product.findEdgeIndex(pr_neibours[j], frags_mapping[_att_points[i][j]]) != -1) return false; int atom_from = mapping[i]; int atom_to = frags_mapping[_att_points[i][j]]; if (mol_product.stereocenters.exists(atom_from) && mol_product.stereocenters.getPyramid(atom_from)[3] == -1) return false; if (mol_product.stereocenters.exists(atom_to) && mol_product.stereocenters.getPyramid(atom_to)[3] != -1) return false; mol_product.flipBond(pr_neibours[j], atom_from, atom_to); // TODO: // Check that corresponding R-group fragment in monomer has cis-trans bond // and check that AAM mapping is specified for that. // For example for reaction OC([*])=O>>OC([*])=O and monomer C\C=C\C(O)=O // product shouldn't have should have cis-trans bonds because // AAM is not specified on R-group atom neighbor // Cis-trans bonds should be saved for such reaction: O[C:1]([*])=O>>O[C:1]([*])=O } mol_product.removeAtom(mapping[i]); } else { for (int j = 0; j < _att_points[i].size(); j++) { int mon_atom = frags_mapping[_att_points[i][j]]; int pr_atom = mapping[i]; const Vertex &mon_v = mol_product.getVertex(mon_atom); const Vertex &pr_v = mol_product.getVertex(pr_atom); for (int k = mon_v.neiBegin(); k != mon_v.neiEnd(); k = mon_v.neiNext(k)) if (MoleculeCisTrans::isGeomStereoBond(mol_product, mon_v.neiEdge(k), NULL, false)) mol_product.cis_trans.setParity(mon_v.neiEdge(k), 0); if (mol_product.stereocenters.exists(mon_atom)) mol_product.stereocenters.remove(mon_atom); QS_DEF(Array<int>, neighbors); neighbors.clear(); for (int k = mon_v.neiBegin(); k != mon_v.neiEnd(); k = mon_v.neiNext(k)) neighbors.push(mon_v.neiVertex(k)); for (int k = 0; k < neighbors.size(); k++) if (mol_product.findEdgeIndex(neighbors[k], pr_atom) == -1) mol_product.flipBond(neighbors[k], mon_atom, pr_atom); frags_mapping[_att_points[i][j]] = pr_atom; mol_product.removeAtom(mon_atom); //if (mol_product.mergeAtoms(frags_mapping[_att_points[i][0]], mapping[i]) == -1) // return false; } } product_mapping[mapping[i]] = frags_mapping[_att_points[i][0]]; /* if (is_transform && _att_points[i].size() != 0 && is_valid && !_checkValence(mol_product, mapping[i])) { _product_forbidden_atoms.copy(_monomer_forbidden_atoms); _product_forbidden_atoms[_att_points[i][0]] = max_reuse_count; return false; }*/ /* Border stereocenters copying */ int nv_idx = 0; for (int j = 0; j < _att_points[i].size(); j++) { if (uncleaned_fragments.stereocenters.exists(_att_points[i][j]) && !mol_product.stereocenters.exists(frags_mapping[_att_points[i][j]])) { int type, group, pyramid[4]; uncleaned_fragments.stereocenters.get(_att_points[i][j], type, group, pyramid); int new_pyramid[4]; bool invalid_stereocenter = false; for (int k = 0; k < 4; k++) { if (pyramid[k] == -1) new_pyramid[k] = -1; else if (!_is_needless_atom[pyramid[k]]) new_pyramid[k] = frags_mapping[pyramid[k]]; else if (frags2product_mapping[pyramid[k]] != -1) { new_pyramid[k] = frags2product_mapping[pyramid[k]]; } else { invalid_stereocenter = true; break; } } if (!invalid_stereocenter) mol_product.stereocenters.add(frags_mapping[_att_points[i][j]], type, group, new_pyramid); } if (nv_idx == 2) break; } } /* Updating of cis-trans information on product & monomer's fragment border */ _completeCisTrans(mol_product, uncleaned_fragments, frags_mapping); QS_DEF(Array<int>, out_mapping); out_mapping.clear_resize(mol_product.vertexEnd()); ready_product_out.clone(mol_product, NULL, &out_mapping); _product_forbidden_atoms.clear_resize(ready_product_out.vertexEnd()); _product_forbidden_atoms.zerofill(); QS_DEF(Array<int>, temp_orig_hydr); temp_orig_hydr.clear(); if (is_transform) { for (int i = mol_product.vertexBegin(); i != mol_product.vertexEnd(); i = mol_product.vertexNext(i)) if (out_mapping[i] != -1 && all_forbidden_atoms[i]) _product_forbidden_atoms[out_mapping[i]] = all_forbidden_atoms[i]; for (int i = 0; i <_original_hydrogens.size(); i++) { int new_h_idx = frags_mapping[_original_hydrogens[i]]; if (new_h_idx == -1) continue; temp_orig_hydr.push(out_mapping[new_h_idx]); } _original_hydrogens.copy(temp_orig_hydr); } ucfrag_mapping.clear_resize(_fragments.vertexEnd()); ucfrag_mapping.fffill(); QS_DEF(Array<int>, old_mapping); old_mapping.copy(_mapping); _mapping.clear_resize(_fragments.vertexEnd()); _mapping.fffill(); for (int i = uncleaned_fragments.vertexBegin(); i != uncleaned_fragments.vertexEnd(); i++) { if (frags_mapping[i] != -1) ucfrag_mapping[i] = frags_mapping[i]; else if (frags2product_mapping[i] != -1) ucfrag_mapping[i] = frags2product_mapping[i]; else continue; ucfrag_mapping[i] = out_mapping[ucfrag_mapping[i]]; if (old_mapping.size() > 0) { int i_id = old_mapping.find(i); if ((i_id != -1) && (i_id < _mapping.size())) _mapping[i_id] = ucfrag_mapping[i]; } else _mapping[i] = ucfrag_mapping[i]; } if(refine_proc) return refine_proc(uncleaned_fragments, ready_product_out, ucfrag_mapping, userdata); return true; } bool ReactionEnumeratorState::_checkFragment( QueryMolecule &submolecule, Molecule &monomer, Array<byte> &unfrag_mon_atoms, int *core_sub ) { QS_DEF(ObjArray< Array<int> >, attachment_pairs); attachment_pairs.clear(); QS_DEF(Molecule, fragment); fragment.clone(monomer, NULL, NULL); for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i) ) { if (!_is_rg_exist) continue; if (!submolecule.isRSite(i)) continue; int rg_idx = submolecule.getSingleAllowedRGroup(i); if (attachment_pairs.size() <= rg_idx) attachment_pairs.expand(rg_idx + 1); attachment_pairs[rg_idx].push(core_sub[i]); } for (int i = fragment.vertexBegin(); i != fragment.vertexEnd(); i = fragment.vertexNext(i)) if (unfrag_mon_atoms[i]) fragment.removeAtom(i); QS_DEF(Array<int>, path); path.clear(); for (int i = 0; i < attachment_pairs.size(); i++) if (attachment_pairs[i].size() == 2) if (!fragment.findPath(attachment_pairs[i][0], attachment_pairs[i][1], path)) return false; return true; } void ReactionEnumeratorState::_checkFragmentNecessity ( Array<int> &is_needless_att_point ) { QS_DEF(Array<int>, ranks); ranks.clear(); ranks.resize(_fragments.vertexEnd()); ranks.fill(1); for (int i = _fragments.vertexBegin(); i != _fragments.vertexEnd(); i = _fragments.vertexNext(i)) { if (is_needless_att_point[i] != 1) continue; DfsWalk dfs(_fragments); ranks[i] = 0; dfs.ignored_vertices = _is_needless_atom.ptr(); dfs.vertex_ranks = ranks.ptr(); dfs.walk(); const Array<DfsWalk::SeqElem> &sequence = dfs.getSequence(); ranks[i] = 1; QS_DEF(Array<int>, needless_atoms); needless_atoms.clear(); int j; bool is_fragment_needful = false; for (j = 0; j < sequence.size(); j++) { if ((sequence[j].parent_vertex == -1) && (j != 0)) break; if (is_needless_att_point[sequence[j].idx] == 0) { is_fragment_needful = true; break; } needless_atoms.push(sequence[j].idx); } if (is_fragment_needful) continue; for (j = 0; j < needless_atoms.size(); j++) _is_needless_atom[needless_atoms[j]] = 1; } } bool ReactionEnumeratorState::_addFragment( Molecule &fragment, QueryMolecule &submolecule, Array<int> &rp_mapping, const Array<int> &sub_rg_atoms, int *core_sub, int *core_super ) { QS_DEF(Array<byte>, unfrag_mon_atoms); unfrag_mon_atoms.clear_resize(fragment.vertexEnd()); unfrag_mon_atoms.zerofill(); _findFragAtoms(unfrag_mon_atoms, submolecule, fragment, core_sub, core_super); //Checking for connectivity of rgroup fragments of one rgroup if (!_checkFragment(submolecule, fragment, unfrag_mon_atoms, core_sub)) return false; const Array<int> &reactant_aam_array = _reaction.getAAMArray(_reactant_idx); QS_DEF(Array<int>, frag_mapping); for (int i = 0; i < frag_mapping.size(); i++) _fragments_aam_array.push(0); if (!_is_frag_search) { frag_mapping.clear_resize(fragment.vertexEnd()); frag_mapping.fffill(); _fragments.mergeWithMolecule(fragment, &frag_mapping); /* Fragments AAM array expanding */ for (int i = 0; i < frag_mapping.size(); i++) _fragments_aam_array.push(0); } else { frag_mapping.clear_resize(_fragments.vertexEnd()); for (int i = 0; i < frag_mapping.size(); i++) frag_mapping[i] = i; } /* Fragments AAM array updating */ for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i)) { if (_fragments_aam_array[frag_mapping[core_sub[i]]] != 0) return false; if (i < reactant_aam_array.size()) _fragments_aam_array[frag_mapping[core_sub[i]]] = reactant_aam_array[i]; else _fragments_aam_array[frag_mapping[core_sub[i]]] = 0; } /* _is_needless arrays expanding */ for (int i = _is_needless_atom.size(); i < _fragments.vertexEnd(); i++) _is_needless_atom.push(0); for (int i = _is_needless_bond.size(); i < _fragments.edgeEnd(); i++) _is_needless_bond.push(0); /* marked atoms array expanding */ //for (int i = 0; i < frag_mapping.size(); i++) // _product_forbidden_atoms.push(0); /* _is_needless atom array updating */ for (int i = 0; i < frag_mapping.size(); i++) if (unfrag_mon_atoms[i]) _is_needless_atom[frag_mapping[i]] = 1; /* _is_needless bond array updating */ for (int i = submolecule.edgeBegin(); i < submolecule.edgeEnd(); i = submolecule.edgeNext(i)) { const Edge &sub_e = submolecule.getEdge(i); int fr_e_idx = fragment.findEdgeIndex(core_sub[sub_e.beg], core_sub[sub_e.end]); if (fr_e_idx == -1) continue; const Edge &fr_e = fragment.getEdge(fr_e_idx); if (_is_rg_exist && submolecule.isRSite(sub_e.beg) && submolecule.isRSite(sub_e.end)) { _is_needless_bond[_fragments.findEdgeIndex(frag_mapping[fr_e.beg], frag_mapping[fr_e.end])] = 1; continue; } const Vertex &sub_beg_v = submolecule.getVertex(sub_e.beg); const Vertex &frag_beg_v = _fragments.getVertex(frag_mapping[core_sub[sub_e.beg]]); const Vertex &sub_end_v = submolecule.getVertex(sub_e.end); const Vertex &frag_end_v = _fragments.getVertex(frag_mapping[core_sub[sub_e.end]]); if ((!_is_rg_exist) && (sub_beg_v.degree() != frag_beg_v.degree()) && (sub_end_v.degree() != frag_end_v.degree())) { _is_needless_bond[_fragments.findEdgeIndex(frag_mapping[fr_e.beg], frag_mapping[fr_e.end])] = 1; continue; } } QS_DEF(Array<int>, is_needless_att_point); is_needless_att_point.clear(); is_needless_att_point.resize(_fragments.vertexEnd()); is_needless_att_point.fffill(); for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i) ) { if (_is_rg_exist && !submolecule.isRSite(i)) continue; const Vertex &sub_v = submolecule.getVertex(i); const Vertex &frag_v = _fragments.getVertex(frag_mapping[core_sub[i]]); if ((!_is_rg_exist) && (sub_v.degree() == frag_v.degree())) continue; int frag_rg_idx = frag_mapping[core_sub[i]]; int pr_i = rp_mapping[i]; if (pr_i == -1) { if (!_is_rg_exist && (reactant_aam_array[i] == 0)) throw Error("Incorrect AAM"); is_needless_att_point[frag_rg_idx] = 1; continue; // No such RGroup in product } is_needless_att_point[frag_rg_idx] = 0; if (_is_rg_exist) { int sub_nv_idx = sub_v.neiVertex(sub_v.neiBegin()); _att_points[pr_i].push(frag_rg_idx); if (_att_points[pr_i].size() == 2) { int another_sub_v_idx = core_super[frag_mapping.find(_att_points[pr_i][0])]; /* RGroup atom that have neighbor with less AAM is first */ const Vertex &another_sub_v = submolecule.getVertex(another_sub_v_idx); int another_sub_nv_idx = another_sub_v.neiVertex(another_sub_v.neiBegin()); if ((reactant_aam_array.size() <= another_sub_nv_idx) || (reactant_aam_array.size() <= sub_nv_idx)) throw Error("Incorrect AAM"); if (reactant_aam_array[another_sub_nv_idx] > reactant_aam_array[sub_nv_idx]) { int tmp = _att_points[pr_i][0]; _att_points[pr_i][0] = _att_points[pr_i][1]; _att_points[pr_i][1] = tmp; } } } else { int frag_rg_idx = frag_mapping[core_sub[i]]; _att_points[pr_i].push(frag_rg_idx); } } _checkFragmentNecessity(is_needless_att_point); return true; } bool ReactionEnumeratorState::_allowManyToOneCallback( Graph &subgraph, int sub_idx, void *userdata ) { QueryMolecule &submolecule = (QueryMolecule &)subgraph; if (submolecule.isRSite(sub_idx) && (submolecule.getVertex(sub_idx).degree() == 1)) return true; return false; } void ReactionEnumeratorState::_removeAtomCallback( Graph &subgraph, int sub_idx, void *userdata ) { ReactionEnumeratorState *rpe_state = (ReactionEnumeratorState *)userdata; QueryMolecule &submolecule = (QueryMolecule &)subgraph; const Vertex &v = submolecule.getVertex(sub_idx); rpe_state->_am->unfixNeighbourQueryBond(sub_idx); for (int i = v.neiBegin(); i != v.neiEnd(); i = v.neiNext(i)) { int bond_idx = v.neiEdge(i); int super_idx = rpe_state->_bonds_mapping_sub[bond_idx]; if (super_idx >= 0) { rpe_state->_bonds_mapping_sub[bond_idx] = -1; rpe_state->_bonds_mapping_super[super_idx] = -1; } } } void ReactionEnumeratorState::_addBondCallback( Graph &subgraph, Graph &supergraph, int sub_idx, int super_idx, void *userdata ) { ReactionEnumeratorState *rpe_state = (ReactionEnumeratorState *)userdata; Molecule &supermolecule = (Molecule &)supergraph; rpe_state->_am->fixQueryBond(sub_idx, supermolecule.getBondOrder(super_idx) == BOND_AROMATIC); rpe_state->_bonds_mapping_sub[sub_idx] = super_idx; rpe_state->_bonds_mapping_super[super_idx] = sub_idx; } bool ReactionEnumeratorState::_checkForNeverUsed(ReactionEnumeratorState *rpe_state, Molecule &supermolecule) { int never_used_vertex = -1; for (int i = supermolecule.vertexBegin(); i != supermolecule.vertexEnd(); i = supermolecule.vertexNext(i)) { if ((i >= rpe_state->_monomer_forbidden_atoms.size()) || (rpe_state->_monomer_forbidden_atoms[i] == 0)) { never_used_vertex = i; return true; } } return false; } int ReactionEnumeratorState::_embeddingCallback( Graph &subgraph, Graph &supergraph, int *core_sub, int *core_super, void *userdata ) { ReactionEnumeratorState *rpe_state = (ReactionEnumeratorState *)userdata; Molecule &cur_monomer = (Molecule &)supergraph; QueryMolecule &cur_reactant = (QueryMolecule &)subgraph; QS_DEF(QueryMolecule, submolecule); submolecule.clear(); submolecule.clone(cur_reactant, NULL, NULL); QS_DEF(Molecule, supermolecule); supermolecule.clear(); supermolecule.clone(cur_monomer, NULL, NULL); if (!_checkForNeverUsed(rpe_state, supermolecule)) return 1; QS_DEF(Array<int>, sub_qa_array); sub_qa_array.clear() ; QS_DEF(Molecule, mol_fragments); mol_fragments.clear(); if (!rpe_state->_is_rg_exist && !rpe_state->_am->match(core_sub, core_super)) return 1; if (!MoleculeStereocenters::checkSub(submolecule.stereocenters, supermolecule.stereocenters, core_sub, false)) return 1; if (!MoleculeCisTrans::checkSub(submolecule, supermolecule, core_sub)) return 1; /* Cis-Trans structure updating */ QS_DEF(Array<int>, rp_mapping); rp_mapping.clear_resize(submolecule.vertexEnd()); rp_mapping.fffill(); rpe_state->_findR2PMapping(submolecule, rp_mapping); rpe_state->_cistransUpdate(submolecule, supermolecule, NULL, rp_mapping, core_sub); rpe_state->_stereocentersUpdate(submolecule, supermolecule, rp_mapping, core_sub, core_super); /* Finding indices of ignored query atoms */ for (int i = submolecule.vertexBegin(); i != submolecule.vertexEnd(); i = submolecule.vertexNext(i)) { if (submolecule.isRSite(i)) sub_qa_array.push(i); } mol_fragments.clone(supermolecule, NULL, NULL); /* Updating fragments molecule */ if (!rpe_state->_addFragment(mol_fragments, submolecule, rp_mapping, sub_qa_array, core_sub, core_super)) return 1; int next_reactant_idx = rpe_state->_reaction.reactantNext(rpe_state->_reactant_idx); if (rpe_state->is_transform) { rpe_state->_productProcess(); return 0; } if (rpe_state->is_one_tube && rpe_state->is_self_react) { ReactionEnumeratorState self_rxn_rpe_state(*rpe_state); self_rxn_rpe_state._is_frag_search = true; self_rxn_rpe_state._reactant_idx = next_reactant_idx; if (self_rxn_rpe_state._reactant_idx != self_rxn_rpe_state._reaction.reactantEnd()) self_rxn_rpe_state._startEmbeddingEnumerator(self_rxn_rpe_state._fragments); } ReactionEnumeratorState new_rpe_state(*rpe_state); new_rpe_state._reactant_idx = next_reactant_idx; new_rpe_state.buildProduct(); return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_exact_matcher.cpp�����������������������������������������0000664�0000000�0000000�00000010734�12710376503�0024000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_exact_matcher.h" #include "reaction/reaction.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_exact_matcher.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(ReactionExactMatcher, "reaction exact matcher"); ReactionExactMatcher::ReactionExactMatcher (Reaction &query, Reaction &target) : BaseReactionSubstructureMatcher(target), _query(query), _target(target) { setQuery(query); match_atoms = _match_atoms; match_bonds = _match_bonds; context = this; prepare = _prepare; prepare_ee = _prepare_ee; flags = 0xFFFFFFFFUL; } bool ReactionExactMatcher::_match_atoms (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, void *context) { ReactionExactMatcher &self = *(ReactionExactMatcher *)context; Reaction &query = query_.asReaction(); Molecule &submol = query.getMolecule(sub_mol_idx); Molecule &supermol = target.getMolecule(super_mol_idx); if (!MoleculeExactMatcher::matchAtoms(submol, supermol, sub_atom_idx, super_atom_idx, self.flags)) return false; if (self.flags & CONDITION_AAM) { if ((query.getAAM(sub_mol_idx, sub_atom_idx) == 0) != (target.getAAM(super_mol_idx, super_atom_idx) == 0)) return false; } return true; } bool ReactionExactMatcher::_match_bonds (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_bond_idx, int super_mol_idx, int super_bond_idx, AromaticityMatcher *am, void *context) { ReactionExactMatcher &self = *(ReactionExactMatcher *)context; Reaction &query = query_.asReaction(); Molecule &submol = query.getMolecule(sub_mol_idx); Molecule &supermol = target.getMolecule(super_mol_idx); if (!MoleculeExactMatcher::matchBonds(submol, supermol, sub_bond_idx, super_bond_idx, self.flags)) return false; if (self.flags & CONDITION_REACTING_CENTERS) { int sub_change = query.getReactingCenter(sub_mol_idx, sub_bond_idx); int super_change = target.getReactingCenter(super_mol_idx, super_bond_idx); if (sub_change != super_change) return false; } return true; } bool ReactionExactMatcher::_prepare (BaseReaction &query, Reaction &target, void *context) { if (query.count() != target.count()) return false; ReactionExactMatcher &self = *(ReactionExactMatcher *)context; if (self.flags & MoleculeExactMatcher::CONDITION_STEREO) self._match_stereo = true; else self._match_stereo = false; return true; } bool ReactionExactMatcher::_prepare_ee (EmbeddingEnumerator &ee, BaseMolecule &submol, Molecule &supermol, void *context) { int i; ReactionExactMatcher &self = *(ReactionExactMatcher *)context; for (i = submol.vertexBegin(); i != submol.vertexEnd(); i = submol.vertexNext(i)) { const Vertex &vertex = submol.getVertex(i); if (submol.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && submol.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (submol.getAtomIsotope(i) == 0 || !(self.flags & MoleculeExactMatcher::CONDITION_ISOTOPE)) ee.ignoreSubgraphVertex(i); } for (i = supermol.vertexBegin(); i != supermol.vertexEnd(); i = supermol.vertexNext(i)) { const Vertex &vertex = supermol.getVertex(i); if (supermol.getAtomNumber(i) == ELEM_H && vertex.degree() == 1 && supermol.getAtomNumber(vertex.neiVertex(vertex.neiBegin())) != ELEM_H) if (supermol.getAtomIsotope(i) == 0 || !(self.flags & MoleculeExactMatcher::CONDITION_ISOTOPE)) ee.ignoreSupergraphVertex(i); } if (ee.countUnmappedSubgraphVertices() != ee.countUnmappedSupergraphVertices()) return false; if (ee.countUnmappedSubgraphEdges() != ee.countUnmappedSupergraphEdges()) return false; return true; } ������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_fingerprint.cpp�������������������������������������������0000664�0000000�0000000�00000007270�12710376503�0023521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_fingerprint.h" #include "molecule/molecule_fingerprint.h" #include "reaction/base_reaction.h" #include "base_c/bitarray.h" using namespace indigo; IMPL_ERROR(ReactionFingerprintBuilder, "fingerprint builder"); CP_DEF(ReactionFingerprintBuilder); ReactionFingerprintBuilder::ReactionFingerprintBuilder (BaseReaction &reaction, const MoleculeFingerprintParameters ¶meters) : cancellation(0), _reaction(reaction), _parameters(parameters), CP_INIT, TL_CP_GET(_fingerprint) { query = false; skip_sim = false; skip_ord = false; skip_ext = false; } void ReactionFingerprintBuilder::process () { int i, one_fp_size = _parameters.fingerprintSizeExtOrdSim(); _fingerprint.clear_resize(one_fp_size * 2); _fingerprint.zerofill(); for (i = _reaction.reactantBegin(); i < _reaction.reactantEnd(); i = _reaction.reactantNext(i)) { MoleculeFingerprintBuilder builder(_reaction.getBaseMolecule(i), _parameters); builder.cancellation = cancellation; builder.query = query; builder.skip_tau = true; builder.skip_sim = skip_sim; builder.skip_ord = skip_ord; builder.skip_ext = skip_ext; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); bitOr(get(), builder.get(), _parameters.fingerprintSizeExtOrd()); bitOr(getSim(), builder.getSim(), _parameters.fingerprintSizeSim()); } for (i = _reaction.productBegin(); i < _reaction.productEnd(); i = _reaction.productNext(i)) { MoleculeFingerprintBuilder builder(_reaction.getBaseMolecule(i), _parameters); builder.cancellation = cancellation; builder.query = query; builder.skip_tau = true; builder.skip_sim = skip_sim; builder.skip_ord = skip_ord; builder.skip_ext = skip_ext; builder.skip_any_atoms = true; builder.skip_any_bonds = true; builder.skip_any_atoms_bonds = true; builder.process(); bitOr(get() + _parameters.fingerprintSizeExtOrd(), builder.get(), _parameters.fingerprintSizeExtOrd()); bitOr(getSim() + + _parameters.fingerprintSizeSim(), builder.getSim(), _parameters.fingerprintSizeSim()); } } byte * ReactionFingerprintBuilder::get () { return _fingerprint.ptr(); } byte * ReactionFingerprintBuilder::getSim () { return _fingerprint.ptr() + _parameters.fingerprintSizeExtOrd() * 2; } void ReactionFingerprintBuilder::parseFingerprintType(const char *type, bool query) { this->query = query; if (type == 0 || *type == 0 || strcasecmp(type, "sim") == 0) { // similarity this->skip_ext = true; this->skip_ord = true; } else if (strcasecmp(type, "sub") == 0) // substructure this->skip_sim = true; else if (strcasecmp(type, "full") == 0) { if (query) throw Error("there can not be 'full' fingerprint of a query reaction"); // full (non-query) fingerprint, do not skip anything } else throw Error("unknown molecule fingerprint type: %s", type); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_neighborhood_counters.cpp���������������������������������0000664�0000000�0000000�00000002642�12710376503�0025561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_neighborhood_counters.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule_neighbourhood_counters.h" using namespace indigo; void ReactionAtomNeighbourhoodCounters::calculate(Reaction &reac) { int i; _counters.resize(reac.count()); for (i = reac.begin(); i < reac.end(); i = reac.next(i)) _counters[i].calculate(reac.getMolecule(i)); } void ReactionAtomNeighbourhoodCounters::calculate(QueryReaction &reac) { int i; _counters.resize(reac.count()); for (i = reac.begin(); i < reac.end(); i = reac.next(i)) _counters[i].calculate(reac.getQueryMolecule(i)); } const MoleculeAtomNeighbourhoodCounters & ReactionAtomNeighbourhoodCounters::getCounters (int idx) const { return _counters[idx]; } ����������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_product_enumerator.cpp������������������������������������0000664�0000000�0000000�00000013222�12710376503�0025105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "reaction/reaction_product_enumerator.h" #include "reaction/reaction_enumerator_state.h" #include "base_cpp/gray_codes.h" #include "base_c/bitarray.h" #include "base_cpp/tlscont.h" #include "graph/graph.h" #include "graph/embedding_enumerator.h" #include "graph/spanning_tree.h" #include "molecule/molecule.h" #include "molecule/canonical_smiles_saver.h" #include "graph/dfs_walk.h" using namespace indigo; IMPL_ERROR(ReactionProductEnumerator, "Reaction product enumerator"); CP_DEF(ReactionProductEnumerator); ReactionProductEnumerator::ReactionProductEnumerator( QueryReaction &reaction ) : is_multistep_reaction(false), is_self_react(false), is_one_tube(false), max_product_count(1000), max_deep_level(2), _reaction(reaction), CP_INIT, TL_CP_GET(_product_aam_array), TL_CP_GET(_smiles_array), TL_CP_GET(_tubes_monomers) { _product_aam_array.clear(); _smiles_array.clear(); _tubes_monomers.clear(); _product_count = 0; _is_rg_exist = false; refine_proc = 0; product_proc = 0; } void ReactionProductEnumerator::addMonomer( int reactant_idx, Molecule &monomer ) { _reaction_monomers.addMonomer(reactant_idx, monomer); } void ReactionProductEnumerator::clearReactantMonomers( int reactant_idx ) { QS_DEF(Array<int>, unused_monomers); unused_monomers.clear(); for (int i = _reaction_monomers._monomers.size() - 1; i >= 0; i--) if (_reaction_monomers._reactant_indexes[i] == reactant_idx) _reaction_monomers.removeMonomer(i); } Molecule & ReactionProductEnumerator::getMonomer( int reactant_idx, int index ) { return _reaction_monomers.getMonomer(reactant_idx, index); } Molecule & ReactionProductEnumerator::getMonomer( int mon_index ) { return _reaction_monomers.getMonomer(mon_index); } const QueryReaction & ReactionProductEnumerator::getReaction( void ) { return _reaction; } int ReactionProductEnumerator::getMonomersCount( int reactant_idx ) { int monomers_count = 0; for (int i = 0; i < _reaction_monomers._reactant_indexes.size(); i++) if (_reaction_monomers._reactant_indexes[i] == reactant_idx) monomers_count++; return monomers_count; } void ReactionProductEnumerator::buildProducts( void ) { QS_DEF(QueryMolecule, all_products); all_products.clear(); for (int i = 0; i < _reaction_monomers.size(); i++) { if (_reaction_monomers._deep_levels[i] > 0) { _reaction_monomers.removeMonomer(i); i--; } } /* Building of monomer tubes grid */ if (!is_one_tube) _buildTubesGrid(); for (int i = _reaction.productBegin(); i != _reaction.productEnd(); i = _reaction.productNext(i)) { QueryMolecule &product = _reaction.getQueryMolecule(i); QS_DEF(Array<int>, mapping); mapping.clear(); all_products.mergeWithMolecule(product, &mapping); _product_aam_array.expand(all_products.vertexEnd()); for (int j = product.vertexBegin(); j != product.vertexEnd(); j = product.vertexNext(j)) _product_aam_array[mapping[j]] = _reaction.getAAM(i, j); } all_products.cis_trans.build(NULL); _smiles_array.clear(); _product_count = 0; ReactionEnumeratorContext context; context.arom_options = arom_options; ReactionEnumeratorState rpe_state(context, _reaction, all_products, _product_aam_array, _smiles_array, _reaction_monomers, _product_count, _tubes_monomers); rpe_state.refine_proc = refine_proc; rpe_state.product_proc = product_proc; rpe_state.userdata = userdata; rpe_state.is_multistep_reaction = is_multistep_reaction; rpe_state.is_self_react = is_self_react; rpe_state.max_deep_level = max_deep_level; rpe_state.max_product_count = max_product_count; rpe_state.is_one_tube = is_one_tube; rpe_state.buildProduct(); } void ReactionProductEnumerator::_buildTubesGrid( void ) { QS_DEF(ObjArray< Array<int> >, digits); digits.clear(); int digit_idx = 0; int val = 0; for (int i = _reaction.reactantBegin(); i != _reaction.reactantEnd(); i = _reaction.reactantNext(i)) { int monomers_count = getMonomersCount(i); Array<int> &new_array = digits.push(); for (int j = 0; j < monomers_count; j++) new_array.push(val++); digit_idx++; } int tubes_count = 1; for (int i = _reaction.reactantBegin(); i != _reaction.reactantEnd(); i = _reaction.reactantNext(i)) tubes_count *= getMonomersCount(i); _tubes_monomers.resize(tubes_count); for (int i = 0; i < tubes_count; i++) { int cur_tube_code = i; int code_pos = 0; int dev = 1; for (int j = _reaction.reactantBegin(); j != _reaction.reactantEnd(); j = _reaction.reactantNext(j)) { int monomers_count = getMonomersCount(j); val = cur_tube_code % monomers_count; cur_tube_code /= monomers_count; _tubes_monomers[i].push(digits[code_pos][val]); dev *= getMonomersCount(j); code_pos++; } } }������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_substructure_matcher.cpp����������������������������������0000664�0000000�0000000�00000020524�12710376503�0025444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule_arom_match.h" #include "molecule/molecule_3d_constraints.h" #include "molecule/molecule_substructure_matcher.h" #include "reaction/reaction_substructure_matcher.h" #include "reaction/reaction_neighborhood_counters.h" #include "molecule/molecule_neighbourhood_counters.h" using namespace indigo; IMPL_ERROR(ReactionSubstructureMatcher, "reaction substructure matcher"); ReactionSubstructureMatcher::ReactionSubstructureMatcher (Reaction &target) : BaseReactionSubstructureMatcher(target), TL_CP_GET(_fmcaches) { match_atoms = _match_atoms; match_bonds = _match_bonds; add_bond = _add_bond; remove_atom = _remove_atom; prepare_ee = _prepare_ee; use_daylight_aam_mode = false; context = this; _fmcaches.clear(); } bool ReactionSubstructureMatcher::_match_atoms (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_atom_idx, int super_mol_idx, int super_atom_idx, void *context) { QueryReaction &query = query_.asQueryReaction(); QueryMolecule &submol = query.getQueryMolecule(sub_mol_idx); Molecule &supermol = target.getMolecule(super_mol_idx); ReactionSubstructureMatcher &self = *(ReactionSubstructureMatcher *)context; self._fmcaches.expand(sub_mol_idx + 1); if (!MoleculeSubstructureMatcher::matchQueryAtom(&submol.getAtom(sub_atom_idx), supermol, super_atom_idx, &self._fmcaches[sub_mol_idx], 0xFFFFFFFFUL)) return false; if (submol.stereocenters.getType(sub_atom_idx) > supermol.stereocenters.getType(super_atom_idx)) return false; if (query.getExactChange(sub_mol_idx, sub_atom_idx) == 1) { const Vertex &can_vertex = submol.getVertex(sub_atom_idx); int ch_flag; int bonds_changes[RC_TOTAL] = {0}; int i; for (i = can_vertex.neiBegin(); i != can_vertex.neiEnd(); i = can_vertex.neiNext(i)) { ch_flag = query.getReactingCenter(sub_atom_idx, can_vertex.neiEdge(i)); if (ch_flag == RC_NOT_CENTER) ch_flag = RC_UNCHANGED; if (ch_flag > 0) bonds_changes[ch_flag]++; } const Vertex &pat_vertex = supermol.getVertex(super_atom_idx); for (i = pat_vertex.neiBegin(); i != pat_vertex.neiEnd(); i = pat_vertex.neiNext(i)) { ch_flag = target.getReactingCenter(super_mol_idx, pat_vertex.neiEdge(i)); if (ch_flag > 0) bonds_changes[ch_flag]--; } int n_centers = bonds_changes[RC_CENTER]; bonds_changes[RC_CENTER] = 0; if (n_centers < 0) return false; for (int i = 0; i < NELEM(bonds_changes); i++) { if ((ch_flag = bonds_changes[i]) > 0) return false; else if (ch_flag < 0) { if ((n_centers += ch_flag) < 0) return false; } } if (n_centers != 0) return false; } return true; } bool ReactionSubstructureMatcher::_match_bonds (BaseReaction &query_, Reaction &target, int sub_mol_idx, int sub_bond_idx, int super_mol_idx, int super_bond_idx, AromaticityMatcher *am, void *context) { QueryReaction &query = query_.asQueryReaction(); QueryMolecule &submol = query.getQueryMolecule(sub_mol_idx); Molecule &supermol = target.getMolecule(super_mol_idx); if (!MoleculeSubstructureMatcher::matchQueryBond(&submol.getBond(sub_bond_idx), supermol, sub_bond_idx, super_bond_idx, am, 0xFFFFFFFFUL)) return false; int sub_change = query.getReactingCenter(sub_mol_idx, sub_bond_idx); int super_change = target.getReactingCenter(super_mol_idx, super_bond_idx); if (super_change == RC_UNMARKED) return true; // super_change == (RC_UNCHANGED + RC_ORDER_CHANGED) is for changed aromatics if (sub_change == RC_NOT_CENTER || sub_change == RC_UNCHANGED) return super_change == 0 || super_change == RC_UNCHANGED || super_change == (RC_UNCHANGED + RC_ORDER_CHANGED); if (sub_change == RC_CENTER) return super_change != 0 && super_change != RC_UNCHANGED && super_change != RC_NOT_CENTER; if ((sub_change & super_change) != sub_change) return false; return true; } void ReactionSubstructureMatcher::_remove_atom (BaseMolecule &submol, int sub_idx, AromaticityMatcher *am) { MoleculeSubstructureMatcher::removeAtom(submol, sub_idx, am); } void ReactionSubstructureMatcher::_add_bond (BaseMolecule &submol, Molecule &supermol, int sub_idx, int super_idx, AromaticityMatcher *am) { MoleculeSubstructureMatcher::addBond(submol, supermol, sub_idx, super_idx, am); } bool ReactionSubstructureMatcher::_prepare (BaseReaction &query_, Reaction &target, void *context) { ReactionSubstructureMatcher &self = *(ReactionSubstructureMatcher *)context; self._fmcaches.clear(); return true; } bool ReactionSubstructureMatcher::_prepare_ee (EmbeddingEnumerator &ee, BaseMolecule &submol, Molecule &supermol, void *context) { // mark hydrogens to ignore QS_DEF(Array<int>, ignored); ignored.clear_resize(submol.vertexEnd()); MoleculeSubstructureMatcher::markIgnoredQueryHydrogens(submol.asQueryMolecule(), ignored.ptr(), 0, 1); for (int i = submol.vertexBegin(); i != submol.vertexEnd(); i = submol.vertexNext(i)) if (ignored[i]) ee.ignoreSubgraphVertex(i); return true; } bool ReactionSubstructureMatcher::_checkAAM () { if (!use_daylight_aam_mode) return BaseReactionSubstructureMatcher::_checkAAM(); QS_DEF(ObjArray< RedBlackSet<int> >, classes_mapping_left); QS_DEF(ObjArray< RedBlackSet<int> >, classes_mapping_right); int i; classes_mapping_left.clear(); classes_mapping_right.clear(); // Collect AAM class-to-class mappings separately for // the reactands and products for (i = 0; i < _matchers.size() - 1; i++) { int qmol_idx = _matchers[i]->_current_molecule_1; int tmol_idx = _matchers[i]->_current_molecule_2; BaseMolecule &qmol = _query->getBaseMolecule(qmol_idx); ObjArray< RedBlackSet<int> > *cm; int j; if (_query->getSideType(qmol_idx) == BaseReaction::REACTANT) cm = &classes_mapping_left; else cm = &classes_mapping_right; for (j = qmol.vertexBegin(); j != qmol.vertexEnd(); j = qmol.vertexNext(j)) { int qaam = _query->getAAM(qmol_idx, j); if (qaam == 0) continue; if (_query->asQueryReaction().getIgnorableAAM(qmol_idx, j)) continue; int mapped = _matchers[i]->_current_core_1[j]; if(mapped < 0) throw Error("internal error: can not call atom with negative number %d", mapped); int taam = _target.getAAM(tmol_idx, mapped); if (taam == 0) return false; cm->expand(qaam + 1); cm->at(qaam).find_or_insert(taam); } } // http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html // "The query reactants can be bound to any classes in the target. // These bindings form the set of allowed product bindings. // The product query atoms are then tested against this list. // If all of the product atoms pass, then the path is a match" for (i = 0; i < classes_mapping_right.size(); i++) { if (i >= classes_mapping_left.size()) // "When a query class is not found on both sides of the query, it is ignored" break; RedBlackSet<int> &right = classes_mapping_right[i]; RedBlackSet<int> &left = classes_mapping_left[i]; for (int j = right.begin(); j != right.end(); j = right.next(j)) { int aam_class = right.key(j); if (!left.find(aam_class)) return false; } } return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/reaction_transformation.cpp����������������������������������������0000664�0000000�0000000�00000015035�12710376503�0024236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction_transformation.h" #include "reaction/reaction_enumerator_state.h" #include "layout/molecule_layout.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(ReactionTransformation, "Reaction transformation"); CP_DEF(ReactionTransformation); ReactionTransformation::ReactionTransformation( void ) : CP_INIT, TL_CP_GET(_merged_reaction), TL_CP_GET(_cur_monomer), TL_CP_GET(_mapping) { _merged_reaction.clear(); _cur_monomer.clear(); _mapping.clear(); layout_flag = true; cancellation = 0; smart_layout = false; } bool ReactionTransformation::transform( Molecule &molecule, QueryReaction &reaction, Array<int> *mapping ) { _generateMergedReaction(reaction); int reactant_idx = _merged_reaction.reactantBegin(); int product_idx = _merged_reaction.productBegin(); bool has_coord = BaseMolecule::hasCoord(molecule); QS_DEF(QueryMolecule, cur_full_product); cur_full_product.clear(); cur_full_product.clone(_merged_reaction.getQueryMolecule(product_idx), NULL, NULL); Array<int> &cur_cur_monomer_aam_array = _merged_reaction.getAAMArray(product_idx); QS_DEF(RedBlackStringMap<int>, cur_smiles_array); cur_smiles_array.clear(); QS_DEF(ReactionEnumeratorState::ReactionMonomers, cur_reaction_monomers); cur_reaction_monomers.clear(); cur_reaction_monomers.addMonomer(reactant_idx, molecule); QS_DEF(ObjArray< Array<int> >, cur_tubes_monomers); cur_tubes_monomers.clear(); int product_count = 0; ReactionEnumeratorContext context; context.arom_options = arom_options; ReactionEnumeratorState re_state(context, _merged_reaction, cur_full_product, cur_cur_monomer_aam_array, cur_smiles_array, cur_reaction_monomers, product_count, cur_tubes_monomers); re_state.is_multistep_reaction = false; re_state.is_one_tube = false; re_state.is_same_keeping = true; re_state.is_self_react = false; re_state.is_transform = true; re_state.userdata = this; re_state.product_proc = _product_proc; _cur_monomer.clone(molecule, NULL, NULL); QS_DEF(Array<int>, forbidden_atoms); forbidden_atoms.clear_resize(_cur_monomer.vertexEnd()); forbidden_atoms.zerofill(); QS_DEF(Array<int>, original_hydrogens); original_hydrogens.clear(); for (int i = _cur_monomer.vertexBegin(); i != _cur_monomer.vertexEnd(); i = _cur_monomer.vertexNext(i)) { if (_cur_monomer.getAtomNumber(i) == ELEM_H) original_hydrogens.push(i); } bool need_layout = false; bool transformed_flag = false; while (re_state.performSingleTransformation(_cur_monomer, _mapping, forbidden_atoms, original_hydrogens, need_layout)) transformed_flag = true; molecule.clone(_cur_monomer, NULL, NULL); if (has_coord) { if (need_layout) { if (layout_flag) { MoleculeLayout ml(molecule, smart_layout); ml.setCancellationHandler(cancellation); ml.make(); } else molecule.clearXyz(); } else molecule.stereocenters.markBonds(); } mapping->copy(_mapping); return transformed_flag; } bool ReactionTransformation::transform(ReusableObjArray<Molecule> &molecules, QueryReaction &reaction, ReusableObjArray<Array<int>> *mapping_array) { for (int i = 0; i < molecules.size(); i++) { Array<int> *mapping = 0; if (mapping_array != 0) mapping = &(mapping_array->at(i)); if (!transform(molecules[i], reaction, mapping)) return false; } return true; } void ReactionTransformation::_product_proc( Molecule &product, Array<int> &monomers_indices, Array<int> &mapping, void *userdata ) { ReactionTransformation *rt = (ReactionTransformation *)userdata; rt->_mapping.copy(mapping); rt->_cur_monomer.clone(product, NULL, NULL); return; } void ReactionTransformation::_mergeReactionComponents( QueryReaction &reaction, int mol_type, QueryMolecule &merged_molecule, Array<int> &merged_aam) { merged_molecule.clear(); merged_aam.clear(); for (int i = reaction.begin(); i < reaction.end(); i = reaction.next(i)) { if (reaction.getSideType(i) != mol_type) continue; QueryMolecule &molecule_i = reaction.getQueryMolecule(i); merged_aam.concat(reaction.getAAMArray(i)); merged_molecule.mergeWithMolecule(molecule_i, NULL, 0); } } void ReactionTransformation::_generateMergedReaction( QueryReaction &reaction ) { QS_DEF(QueryMolecule, merged_reactant); merged_reactant.clear(); QS_DEF(Array<int>, reactant_aam); reactant_aam.clear(); QS_DEF(QueryMolecule, merged_cur_monomer); merged_cur_monomer.clear(); QS_DEF(Array<int>, product_aam); product_aam.clear(); // Reactants merging _mergeReactionComponents(reaction, BaseReaction::REACTANT, merged_reactant, reactant_aam); // Products merging _mergeReactionComponents(reaction, BaseReaction::PRODUCT, merged_cur_monomer, product_aam); _merged_reaction.clear(); int reactant_idx = _merged_reaction.addReactant(); int product_idx = _merged_reaction.addProduct(); QueryMolecule &reactant = _merged_reaction.getQueryMolecule(reactant_idx); QueryMolecule &product = _merged_reaction.getQueryMolecule(product_idx); reactant.clone(merged_reactant, NULL, NULL); product.clone(merged_cur_monomer, NULL, NULL); Array<int> &r_aam = _merged_reaction.getAAMArray(reactant_idx); r_aam.clear(); r_aam.concat(reactant_aam); Array<int> &p_aam = _merged_reaction.getAAMArray(product_idx); p_aam.clear(); p_aam.concat(product_aam); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/rsmiles_loader.cpp�������������������������������������������������0000664�0000000�0000000�00000041555�12710376503�0022316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/rsmiles_loader.h" #include "base_cpp/scanner.h" #include "molecule/smiles_loader.h" #include "reaction/base_reaction.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "molecule/elements.h" using namespace indigo; IMPL_ERROR(RSmilesLoader, "reaction SMILES loader"); RSmilesLoader::RSmilesLoader (Scanner &scanner) : _scanner(scanner) { ignore_closing_bond_direction_mismatch = false; smarts_mode = false; ignore_cistrans_errors = false; } int RSmilesLoader::_selectGroupByPair (int &lead_idx, int& idx, int rcnt, int ccnt, int pcnt) const { if (lead_idx < rcnt) return 0; lead_idx -= rcnt; idx -= rcnt; if (lead_idx < ccnt) return 1; lead_idx -= ccnt; idx -= ccnt; if (lead_idx < pcnt) return 2; throw Error("RSmilesLoader::_selectGroup(): Index out of range"); } int RSmilesLoader::_selectGroup (int& idx, int rcnt, int ccnt, int pcnt) const { int iidx = idx; return _selectGroupByPair(iidx, idx, rcnt, ccnt, pcnt); } void RSmilesLoader::loadReaction (Reaction &reaction) { _rxn = &reaction; _brxn = &reaction; _qrxn = 0; _loadReaction(); } void RSmilesLoader::loadQueryReaction (QueryReaction& rxn) { _rxn = 0; _brxn = &rxn; _qrxn = &rxn; _loadReaction(); } void RSmilesLoader::_loadReaction () { _brxn->clear(); int i; AutoPtr<BaseMolecule> rcnt; AutoPtr<BaseMolecule> ctlt; AutoPtr<BaseMolecule> prod; AutoPtr<BaseMolecule> *mols[] = {&rcnt, &ctlt, &prod}; QS_DEF(Array<int>, rcnt_aam); QS_DEF(Array<int>, ctlt_aam); QS_DEF(Array<int>, prod_aam); QS_DEF(Array<int>, rcnt_aam_ignorable); QS_DEF(Array<int>, prod_aam_ignorable); QS_DEF(Array<char>, buf); Array<int> *aams[] = {&rcnt_aam, &ctlt_aam, &prod_aam}; Array<int> *ignorable_aams[] = {&rcnt_aam_ignorable, 0, &prod_aam_ignorable}; // read the reactants buf.clear(); while (1) { char c = _scanner.readChar(); if (c == '>') break; buf.push(c); } BufferScanner r_scanner(buf); SmilesLoader r_loader(r_scanner); r_loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; r_loader.inside_rsmiles = true; r_loader.reaction_atom_mapping = &rcnt_aam; r_loader.ignorable_aam = &rcnt_aam_ignorable; r_loader.smarts_mode = smarts_mode; r_loader.ignore_cistrans_errors = ignore_cistrans_errors; r_loader.stereochemistry_options = stereochemistry_options; if (_rxn != 0) { rcnt.reset(new Molecule()); r_loader.loadMolecule((Molecule &)rcnt.ref()); } else { rcnt.reset(new QueryMolecule()); r_loader.loadQueryMolecule((QueryMolecule &)rcnt.ref()); } // read the catalysts (agents) buf.clear(); while (1) { char c = _scanner.readChar(); if (c == '>') break; buf.push(c); } if (_rxn != 0) ctlt.reset(new Molecule()); else ctlt.reset(new QueryMolecule()); BufferScanner c_scanner(buf); SmilesLoader c_loader(c_scanner); c_loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; c_loader.inside_rsmiles = true; c_loader.reaction_atom_mapping = &ctlt_aam; c_loader.smarts_mode = smarts_mode; c_loader.ignore_cistrans_errors = ignore_cistrans_errors; c_loader.stereochemistry_options = stereochemistry_options; if (_rxn != 0) { ctlt.reset(new Molecule()); c_loader.loadMolecule((Molecule &)ctlt.ref()); } else { ctlt.reset(new QueryMolecule()); c_loader.loadQueryMolecule((QueryMolecule &)ctlt.ref()); } bool vbar = false; // read the products buf.clear(); while (!_scanner.isEOF()) { char c = _scanner.readChar(); if (c == '|') { vbar = true; break; } buf.push(c); } BufferScanner p_scanner(buf); SmilesLoader p_loader(p_scanner); p_loader.ignore_closing_bond_direction_mismatch = ignore_closing_bond_direction_mismatch; p_loader.inside_rsmiles = true; p_loader.reaction_atom_mapping = &prod_aam; p_loader.ignorable_aam = &prod_aam_ignorable; p_loader.smarts_mode = smarts_mode; p_loader.ignore_cistrans_errors = ignore_cistrans_errors; p_loader.stereochemistry_options = stereochemistry_options; if (_rxn != 0) { prod.reset(new Molecule()); p_loader.loadMolecule((Molecule &)prod.ref()); } else { prod.reset(new QueryMolecule()); p_loader.loadQueryMolecule((QueryMolecule &)prod.ref()); } QS_DEF(Array<int>, r_fragments); QS_DEF(Array<int>, c_fragments); QS_DEF(Array<int>, p_fragments); Array<int>* fragments[] = {&r_fragments, &c_fragments, &p_fragments}; r_fragments.clear_resize(rcnt->countComponents()); c_fragments.clear_resize(ctlt->countComponents()); p_fragments.clear_resize(prod->countComponents()); for (i = 0; i < r_fragments.size(); i++) r_fragments[i] = i; for (i = 0; i < c_fragments.size(); i++) c_fragments[i] = i; for (i = 0; i < p_fragments.size(); i++) p_fragments[i] = i; bool have_highlighting = false; QS_DEF(Array<int>, hl_atoms); QS_DEF(Array<int>, hl_bonds); hl_atoms.clear_resize(rcnt->vertexCount() + ctlt->vertexCount() + prod->vertexCount()); hl_bonds.clear_resize(rcnt->edgeCount() + ctlt->edgeCount() + prod->edgeCount()); hl_atoms.zerofill(); hl_bonds.zerofill(); if (vbar) { MoleculeStereocenters &r_stereo = rcnt->stereocenters; MoleculeStereocenters &c_stereo = ctlt->stereocenters; MoleculeStereocenters &p_stereo = prod->stereocenters; MoleculeStereocenters *stereo[] = {&r_stereo, &c_stereo, &p_stereo}; while (1) { char c = _scanner.readChar(); if (c == '|') break; if (c == 'w') { if (_scanner.readChar() != ':') throw Error("colon expected after 'w'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); stereo[group]->add(idx, MoleculeStereocenters::ATOM_ANY, 0, false); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'a') { if (_scanner.readChar() != ':') throw Error("colon expected after 'a'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); stereo[group]->setType(idx, MoleculeStereocenters::ATOM_ABS, 0); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'o') { int groupno = _scanner.readUnsigned(); if (_scanner.readChar() != ':') throw Error("colon expected after 'o'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); stereo[group]->setType(idx, MoleculeStereocenters::ATOM_OR, groupno); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '&') { int groupno = _scanner.readUnsigned(); if (_scanner.readChar() != ':') throw Error("colon expected after '&'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); stereo[group]->setType(idx, MoleculeStereocenters::ATOM_AND, groupno); if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '^') { int rad = _scanner.readIntFix(1); int radical; if (rad == 1) radical = RADICAL_DOUBLET; else if (rad == 3) radical = RADICAL_SINGLET; else if (rad == 4) radical = RADICAL_TRIPLET; else throw Error("unsupported radical number: %d", rad); if (_scanner.readChar() != ':') throw Error("colon expected after radical number"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); if (_rxn != 0) ((Molecule &)mols[group]->ref()).setAtomRadical(idx, radical); else { QueryMolecule &qmol = (QueryMolecule &)mols[group]->ref(); qmol.resetAtom(idx, (QueryMolecule::Atom *)QueryMolecule::Atom::und( qmol.releaseAtom(idx), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical))); } if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == 'f') { if (_scanner.readChar() != ':') throw Error("colon expected after 'f'"); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); while (_scanner.lookNext() == '.') { _scanner.skip(1); int idx1 = idx; int index_in_group = _scanner.readUnsigned(); int group = _selectGroupByPair(idx1, index_in_group, r_fragments.size(), c_fragments.size(), p_fragments.size()); (*fragments[group])[index_in_group] = idx1; } if (_scanner.lookNext() == ',') _scanner.skip(1); } } else if (c == '$') { int k = rcnt->vertexCount() + ctlt->vertexCount() + prod->vertexCount(); QS_DEF(Array<char>, label); for (i = 0; i < k; i++) { label.clear(); while (1) { if (_scanner.isEOF()) throw Error("end of input while reading $...$ block"); c = _scanner.readChar(); if (c == ';' || c == '$') break; label.push(c); } if (c == '$' && i != k - 1) throw Error("only %d atoms found in pseudo-atoms $...$ block", i + 1); if (label.size() > 0) { label.push(0); int idx = i; int group = _selectGroup(idx, rcnt->vertexCount(), ctlt->vertexCount(), prod->vertexCount()); int rnum; if (label.size() > 3 && label[0] == '_' && label[1] == 'R' && sscanf(label.ptr() + 2, "%d", &rnum) == 1) { // ChemAxon's Extended SMILES notation for R-sites if (_qrxn != 0) { QueryMolecule &qmol = (QueryMolecule &)mols[group]->ref(); qmol.resetAtom(idx, new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0)); } mols[group]->ref().allowRGroupOnRSite(idx, rnum); } else { if (_rxn != 0) ((Molecule &)mols[group]->ref()).setPseudoAtom(idx, label.ptr()); else { QueryMolecule &qmol = (QueryMolecule &)mols[group]->ref(); qmol.resetAtom(idx, (QueryMolecule::Atom *)QueryMolecule::Atom::und(qmol.releaseAtom(idx), new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, label.ptr()))); } } } } } else if (c == 'h') { have_highlighting = true; c = _scanner.readChar(); int a = false; if (c == 'a') a = true; else if (c != 'b') throw Error("expected 'a' or 'b' after 'h', got '%c'", c); if (_scanner.readChar() != ':') throw Error("colon expected after 'h%c'", a ? 'a' : 'b'); while (isdigit(_scanner.lookNext())) { int idx = _scanner.readUnsigned(); if (a) hl_atoms[idx] = 1; else hl_bonds[idx] = 1; if (_scanner.lookNext() == ',') _scanner.skip(1); } } } } // Read name Scanner *scanner_for_name; if (vbar) scanner_for_name = &_scanner; else scanner_for_name = &p_scanner; scanner_for_name->skipSpace(); if (!scanner_for_name->isEOF()) scanner_for_name->readLine(_brxn->name, true); AutoPtr<BaseMolecule> mol; QS_DEF(Array<int>, aam); QS_DEF(Array<int>, ignorable_aam); QS_DEF(Array<int>, mapping); QS_DEF(Array<int>, hl_atoms_frag); QS_DEF(Array<int>, hl_bonds_frag); if (_rxn != 0) mol.reset(new Molecule()); else mol.reset(new QueryMolecule()); for (int v = 0; v < 3; ++v) { for (i = 0; i < fragments[v]->size(); i++) { int j, k; if ((*fragments[v])[i] == -1) continue; mol->clear(); aam.clear(); ignorable_aam.clear(); hl_atoms_frag.clear(); hl_bonds_frag.clear(); for (j = i; j < fragments[v]->size(); j++) { AutoPtr<BaseMolecule> fragment; if (_rxn != 0) fragment.reset(new Molecule()); else fragment.reset(new QueryMolecule()); if ((*fragments[v])[j] == i) { (*fragments[v])[j] = -1; Filter filt(mols[v]->ref().getDecomposition().ptr(), Filter::EQ, j); fragment->makeSubmolecule(mols[v]->ref(), filt, &mapping, 0); mol->mergeWithMolecule(fragment.ref(), 0); for (k = 0; k < fragment->vertexCount(); k++) { aam.push((*aams[v])[mapping[k]]); if (ignorable_aams[v] != 0) ignorable_aam.push((*ignorable_aams[v])[mapping[k]]); int idx = mapping[k]; for (int w = 0; w < v; w++) idx += mols[w]->ref().vertexCount(); hl_atoms_frag.push(hl_atoms[idx]); } for (k = 0; k < fragment->edgeCount(); k++) { const Edge &edge = fragment->getEdge(k); int idx = mols[v]->ref().findEdgeIndex(mapping[edge.beg], mapping[edge.end]); if (idx < 0) throw Error("internal: can not find edge"); for (int w = 0; w < v; w++) idx += mols[w]->ref().edgeCount(); hl_bonds_frag.push(hl_bonds[idx]); } } } int idx; if (v == 0) idx = _brxn->addReactantCopy(mol.ref(), 0, 0); else if (v == 1) idx = _brxn->addCatalystCopy(mol.ref(), 0, 0); else if (v == 2) idx = _brxn->addProductCopy(mol.ref(), 0, 0); _brxn->getAAMArray(idx).copy(aam); if (_qrxn != 0) _qrxn->getIgnorableAAMArray(idx).copy(ignorable_aam); if (have_highlighting) { Filter vfilter(hl_atoms_frag.ptr(), Filter::NEQ, 0); Filter efilter(hl_bonds_frag.ptr(), Filter::NEQ, 0); _brxn->getBaseMolecule(idx).highlightAtoms(vfilter); _brxn->getBaseMolecule(idx).highlightBonds(efilter); } } } } ���������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/rsmiles_saver.cpp��������������������������������������������������0000664�0000000�0000000�00000024402�12710376503�0022160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/rsmiles_saver.h" #include "base_cpp/output.h" #include "molecule/canonical_smiles_saver.h" #include "molecule/elements.h" #include "molecule/smiles_saver.h" #include "reaction/query_reaction.h" #include "reaction/reaction.h" using namespace indigo; IMPL_ERROR(RSmilesSaver, "reaction SMILES saver"); CP_DEF(RSmilesSaver); RSmilesSaver::RSmilesSaver (Output &output) : _output(output), CP_INIT, TL_CP_GET(_written_atoms), TL_CP_GET(_written_bonds), TL_CP_GET(_ncomp) { smarts_mode = false; } void RSmilesSaver::saveReaction (Reaction &reaction) { _rxn = &reaction; _brxn = &reaction; _qrxn = 0; _saveReaction(); } void RSmilesSaver::saveQueryReaction (QueryReaction &reaction) { _qrxn = &reaction; _brxn = &reaction; _rxn = 0; _saveReaction(); } void RSmilesSaver::_writeMolecule (int i) { SmilesSaver saver(_output); saver.write_extra_info = false; saver.separate_rsites = false; saver.rsite_indices_as_aam = false; saver.atom_atom_mapping = _brxn->getAAMArray(i).ptr(); saver.smarts_mode = smarts_mode; if (_rxn != 0) saver.saveMolecule(_rxn->getMolecule(i)); else saver.saveQueryMolecule(_qrxn->getQueryMolecule(i)); _ncomp.push(saver.writtenComponents()); const Array<int> &atoms = saver.writtenAtoms(); int j; for (j = 0; j < atoms.size(); j++) { _Idx &idx = _written_atoms.push(); idx.mol = i; idx.idx = atoms[j]; } const Array<int> &bonds = saver.writtenBonds(); for (j = 0; j < bonds.size(); j++) { _Idx &idx = _written_bonds.push(); idx.mol = i; idx.idx = bonds[j]; } } void RSmilesSaver::_saveReaction () { _written_atoms.clear(); _written_bonds.clear(); _ncomp.clear(); _comma = false; bool dot = false; for (auto i : _brxn->reactants) { if (dot) _output.writeChar('.'); else dot = true; _writeMolecule(i); } _output.writeString(">"); dot = false; for (auto i : _brxn->catalysts) { if (dot) _output.writeChar('.'); else dot = true; _writeMolecule(i); } _output.writeString(">"); dot = false; for (auto i : _brxn->products) { if (dot) _output.writeChar('.'); else dot = true; _writeMolecule(i); } _writeFragmentsInfo(); _writeStereogroups(); _writeRadicals(); _writePseudoAtoms(); _writeHighlighting(); if (_comma) _output.writeChar('|'); } void RSmilesSaver::_writeFragmentsInfo () { int i, j, cnt = 0; for (i = 0; i < _ncomp.size(); i++) { if (_ncomp[i] > 1) break; cnt += _ncomp[i]; } if (i == _ncomp.size()) return; if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } _output.writeString("f:"); bool comma = false; for (; i < _ncomp.size(); i++) { if (_ncomp[i] > 1) { if (comma) _output.writeChar(','); else comma = true; _output.printf("%d", cnt); for (j = 1; j < _ncomp[i]; j++) _output.printf(".%d", cnt + j); } cnt += _ncomp[i]; } } void RSmilesSaver::_writeStereogroups () { QS_DEF(Array<int>, marked); int i, j; for (i = 0; i < _written_atoms.size(); i++) { const _Idx &idx = _written_atoms[i]; int type = _brxn->getBaseMolecule(idx.mol).stereocenters.getType(idx.idx); if (type != 0 && type != MoleculeStereocenters::ATOM_ABS) break; } if (i == _written_atoms.size()) return; marked.clear_resize(_written_atoms.size()); marked.zerofill(); int and_group_idx = 1; int or_group_idx = 1; for (i = 0; i < _written_atoms.size(); i++) { if (marked[i]) continue; const _Idx &idx = _written_atoms[i]; int type = _brxn->getBaseMolecule(idx.mol).stereocenters.getType(idx.idx); if (type > 0) { if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } } if (type == MoleculeStereocenters::ATOM_ANY) { _output.printf("w:%d", i); for (j = i + 1; j < _written_atoms.size(); j++) { const _Idx &jdx = _written_atoms[j]; if (_brxn->getBaseMolecule(jdx.mol).stereocenters.getType(jdx.idx) == MoleculeStereocenters::ATOM_ANY) { marked[j] = 1; _output.printf(",%d", j); } } } else if (type == MoleculeStereocenters::ATOM_ABS) { _output.printf("a:%d", i); for (j = i + 1; j < _written_atoms.size(); j++) { const _Idx &jdx = _written_atoms[j]; if (_brxn->getBaseMolecule(jdx.mol).stereocenters.getType(jdx.idx) == MoleculeStereocenters::ATOM_ABS) { marked[j] = 1; _output.printf(",%d", j); } } } else if (type == MoleculeStereocenters::ATOM_AND) { int group = _brxn->getBaseMolecule(idx.mol).stereocenters.getGroup(idx.idx); _output.printf("&%d:%d", and_group_idx++, i); for (j = i + 1; j < _written_atoms.size(); j++) { const _Idx &jdx = _written_atoms[j]; if (_brxn->getBaseMolecule(jdx.mol).stereocenters.getType(jdx.idx) == MoleculeStereocenters::ATOM_AND && _brxn->getBaseMolecule(jdx.mol).stereocenters.getGroup(jdx.idx) == group) { marked[j] = 1; _output.printf(",%d", j); } } } else if (type == MoleculeStereocenters::ATOM_OR) { int group = _brxn->getBaseMolecule(idx.mol).stereocenters.getGroup(idx.idx); _output.printf("&%d:%d", or_group_idx++, i); for (j = i + 1; j < _written_atoms.size(); j++) { const _Idx &jdx = _written_atoms[j]; if (_brxn->getBaseMolecule(jdx.mol).stereocenters.getType(jdx.idx) == MoleculeStereocenters::ATOM_OR && _brxn->getBaseMolecule(jdx.mol).stereocenters.getGroup(jdx.idx) == group) { marked[j] = 1; _output.printf(",%d", j); } } } } } void RSmilesSaver::_writeRadicals () { QS_DEF(Array<int>, marked); int i, j; marked.clear_resize(_written_atoms.size()); marked.zerofill(); for (i = 0; i < _written_atoms.size(); i++) { if (marked[i]) continue; const _Idx &idx = _written_atoms[i]; BaseMolecule &bmol = _brxn->getBaseMolecule(idx.mol); if (bmol.isRSite(idx.idx) || bmol.isPseudoAtom(idx.idx)) continue; int radical = bmol.getAtomRadical(idx.idx); if (radical <= 0) continue; if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } if (radical == RADICAL_SINGLET) _output.writeString("^3:"); else if (radical == RADICAL_DOUBLET) _output.writeString("^1:"); else // RADICAL_TRIPLET _output.writeString("^4:"); _output.printf("%d", i); for (j = i + 1; j < _written_atoms.size(); j++) { const _Idx &jdx = _written_atoms[j]; if (_brxn->getBaseMolecule(jdx.mol).getAtomRadical(jdx.idx) == radical) { marked[j] = 1; _output.printf(",%d", j); } } } } void RSmilesSaver::_writePseudoAtoms () { int i; for (i = 0; i < _written_atoms.size(); i++) { BaseMolecule &mol = _brxn->getBaseMolecule(_written_atoms[i].mol); int idx = _written_atoms[i].idx; if (mol.isPseudoAtom(idx)) break; if (mol.isRSite(idx) && mol.getRSiteBits(idx) > 0) break; } if (i == _written_atoms.size()) return; if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } _output.writeChar('$'); for (i = 0; i < _written_atoms.size(); i++) { _Idx &idx = _written_atoms[i]; if (i > 0) _output.writeChar(';'); BaseMolecule &mol = _brxn->getBaseMolecule(idx.mol); if (mol.isPseudoAtom(idx.idx)) SmilesSaver::writePseudoAtom(mol.getPseudoAtom(idx.idx), _output); else if (mol.isRSite(idx.idx) && mol.getRSiteBits(idx.idx) > 0) // ChemAxon's Extended SMILES notation for R-sites _output.printf("_R%d", mol.getSingleAllowedRGroup(idx.idx)); } _output.writeChar('$'); } void RSmilesSaver::_writeHighlighting () { int i; bool ha = false; for (i = 0; i < _written_atoms.size(); i++) { if (_brxn->getBaseMolecule(_written_atoms[i].mol).isAtomHighlighted(_written_atoms[i].idx)) { if (ha) _output.writeChar(','); else { if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } _output.writeString("ha:"); ha = true; } _output.printf("%d", i); } } bool hb = false; for (i = 0; i < _written_bonds.size(); i++) { if (_brxn->getBaseMolecule(_written_bonds[i].mol).isBondHighlighted(_written_bonds[i].idx)) { if (hb) _output.writeChar(','); else { if (_comma) _output.writeChar(','); else { _output.writeString(" |"); _comma = true; } _output.writeString("hb:"); hb = true; } _output.printf("%d", i); } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/rxnfile_loader.cpp�������������������������������������������������0000664�0000000�0000000�00000013574�12710376503�0022307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/rxnfile_loader.h" #include "molecule/molfile_loader.h" #include "base_cpp/scanner.h" using namespace indigo; IMPL_ERROR(RxnfileLoader, "RXN loader"); RxnfileLoader::RxnfileLoader (Scanner& scanner): _scanner(scanner){ _v3000 = false; ignore_noncritical_query_features = false; } RxnfileLoader::~RxnfileLoader(){ } void RxnfileLoader::loadReaction(Reaction &reaction){ _rxn = &reaction; _brxn = &reaction; _qrxn = 0; _loadReaction(); } void RxnfileLoader::loadQueryReaction(QueryReaction& rxn){ _rxn = 0; _brxn = &rxn; _qrxn = &rxn; _loadReaction(); } void RxnfileLoader::_loadReaction(){ _brxn->clear(); MolfileLoader molfileLoader(_scanner); molfileLoader.treat_x_as_pseudoatom = treat_x_as_pseudoatom; molfileLoader.stereochemistry_options = stereochemistry_options; molfileLoader.ignore_noncritical_query_features = ignore_noncritical_query_features; _readRxnHeader(); _readReactantsHeader(); for (int i = 0; i < _n_reactants; i++) { int index = _brxn->addReactant(); _readMolHeader(); _readMol(molfileLoader, index); } _readReactantsFooter(); _readProductsHeader(); for (int i = 0; i < _n_products; i++) { int index = _brxn->addProduct(); _readMolHeader(); _readMol(molfileLoader, index); } _readProductsFooter(); if (_n_catalysts > 0) { _readCatalystsHeader(); for (int i = 0; i < _n_catalysts; i++) { int index = _brxn->addCatalyst(); _readMolHeader(); _readMol(molfileLoader, index); } _readCatalystsFooter(); } } void RxnfileLoader::_readRxnHeader(){ QS_DEF(Array<char>, header); _scanner.readLine(header, true); if (strcmp(header.ptr(), "$RXN") == 0) _v3000 = false; else if (strcmp(header.ptr(), "$RXN V3000") == 0) _v3000 = true; else throw Error("bad header %s", header.ptr()); _scanner.readLine(_brxn->name, true); _scanner.skipLine(); _scanner.skipLine(); if (_v3000) { _scanner.skip(14); // "M V30 COUNTS " _scanner.readLine(header, true); int n = sscanf(header.ptr(), "%d %d %d", &_n_reactants, &_n_products, &_n_catalysts); if (n < 2) throw Error("error reading counts: %s", header.ptr()); if (n == 2) _n_catalysts = 0; } else { _scanner.readLine(header, false); BufferScanner strscan(header); _n_reactants = strscan.readIntFix(3); _n_products = strscan.readIntFix(3); try { _n_catalysts = strscan.readIntFix(3); } catch (Scanner::Error) { _n_catalysts = 0; } } } void RxnfileLoader::_readProductsHeader(){ if (!_v3000) { return; } QS_DEF(Array<char>, header); _scanner.readLine(header, true); if (strcmp(header.ptr(), "M V30 BEGIN PRODUCT") != 0){ throw Error("bad products header: %s", header.ptr()); } } void RxnfileLoader::_readReactantsHeader(){ if (!_v3000) { return; } QS_DEF(Array<char>, header); _scanner.readLine(header, true); if (strcmp(header.ptr(), "M V30 BEGIN REACTANT") != 0){ throw Error("bad reactants header: %s", header.ptr()); } } void RxnfileLoader::_readCatalystsHeader(){ if (!_v3000) { return; } QS_DEF(Array<char>, header); _scanner.readLine(header, true); if (strcmp(header.ptr(), "M V30 BEGIN AGENT") != 0){ throw Error("bad catalysts header: %s", header.ptr()); } } void RxnfileLoader::_readMolHeader(){ if (_v3000) { return; } QS_DEF(Array<char>, header); _scanner.readLine(header, true); if (strcmp(header.ptr(), "$MOL") != 0) throw Error("bad molecule header: %s", header.ptr()); } void RxnfileLoader::_readReactantsFooter(){ if (!_v3000) { return; } QS_DEF(Array<char>, footer); _scanner.readLine(footer, true); if (strcmp(footer.ptr(), "M V30 END REACTANT") != 0) throw Error("bad reactants footer: %s", footer.ptr()); } void RxnfileLoader::_readProductsFooter(){ if (!_v3000) { return; } QS_DEF(Array<char>, footer); _scanner.readLine(footer, true); if (strcmp(footer.ptr(), "M V30 END PRODUCT") != 0) throw Error("bad products footer: %s", footer.ptr()); } void RxnfileLoader::_readCatalystsFooter(){ if (!_v3000) { return; } QS_DEF(Array<char>, footer); _scanner.readLine(footer, true); if (strcmp(footer.ptr(), "M V30 END AGENT") != 0) throw Error("bad agents footer: %s", footer.ptr()); } void RxnfileLoader::_readMol (MolfileLoader &loader, int index) { loader.reaction_atom_mapping = &_brxn->getAAMArray(index); loader.reaction_atom_inversion = &_brxn->getInversionArray(index); loader.reaction_bond_reacting_center = &_brxn->getReactingCenterArray(index); if (_qrxn != 0) loader.reaction_atom_exact_change = &_qrxn->getExactChangeArray(index); if (_qrxn != 0) { if (_v3000) loader.loadQueryCtab3000(_qrxn->getQueryMolecule(index)); else loader.loadQueryMolecule(_qrxn->getQueryMolecule(index)); } else { if (_v3000) loader.loadCtab3000(_rxn->getMolecule(index)); else loader.loadMolecule(_rxn->getMolecule(index)); } } ������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/reaction/src/rxnfile_saver.cpp��������������������������������������������������0000664�0000000�0000000�00000013653�12710376503�0022157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include <time.h> #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "reaction/rxnfile_saver.h" #include "base_cpp/output.h" #include "molecule/molfile_saver.h" using namespace indigo; IMPL_ERROR(RxnfileSaver, "Rxnfile saver"); RxnfileSaver::RxnfileSaver(Output &output) : _output(output) { molfile_saving_mode = MolfileSaver::MODE_AUTO; skip_date = false; add_stereo_desc = false; } RxnfileSaver::~RxnfileSaver(){ } void RxnfileSaver::saveBaseReaction (BaseReaction& reaction) { if (reaction.isQueryReaction()) saveQueryReaction(reaction.asQueryReaction()); else saveReaction(reaction.asReaction()); } void RxnfileSaver::saveReaction (Reaction &reaction) { _rxn = &reaction; _brxn = &reaction; _qrxn = 0; _saveReaction(); } void RxnfileSaver::saveQueryReaction (QueryReaction &reaction) { _qrxn = &reaction; _brxn = &reaction; _rxn = 0; _saveReaction(); } void RxnfileSaver::_saveReaction(){ int i; if (molfile_saving_mode == MolfileSaver::MODE_3000) _v2000 = false; else if (molfile_saving_mode == MolfileSaver::MODE_2000) _v2000 = true; else { _v2000 = true; for (i = _brxn->begin(); i != _brxn->end(); i = _brxn->next(i)) { if (_brxn->getBaseMolecule(i).hasHighlighting()) { _v2000 = false; break; } if (!_brxn->getBaseMolecule(i).stereocenters.haveAllAbsAny() && !_brxn->getBaseMolecule(i).stereocenters.haveAllAndAny()) { _v2000 = false; break; } } } MolfileSaver molfileSaver(_output); molfileSaver.mode = _v2000 ? MolfileSaver::MODE_2000 : MolfileSaver::MODE_3000; molfileSaver.add_stereo_desc = add_stereo_desc; _writeRxnHeader(*_brxn); _writeReactantsHeader(); for (i = _brxn->reactantBegin(); i < _brxn->reactantEnd(); i = _brxn->reactantNext(i) ) { _writeMolHeader(); _writeMol(molfileSaver, i); } _writeReactantsFooter(); _writeProductsHeader(); for (int i = _brxn->productBegin(); i < _brxn->productEnd(); i = _brxn->productNext(i)) { _writeMolHeader(); _writeMol(molfileSaver, i); } _writeProductsFooter(); if (_brxn->catalystCount() > 0) { _writeCatalystsHeader(); for (int i = _brxn->catalystBegin(); i < _brxn->catalystEnd(); i = _brxn->catalystNext(i)) { _writeMolHeader(); _writeMol(molfileSaver, i); } _writeCatalystsFooter(); } _writeRxnFooter(); } void RxnfileSaver::_writeRxnHeader(BaseReaction &reaction) { if (_v2000) _output.writeStringCR("$RXN"); else _output.writeStringCR("$RXN V3000"); struct tm lt; if (skip_date) memset(<, 0, sizeof(lt)); else { time_t tm = time(NULL); lt = *localtime(&tm); } if (reaction.name.ptr() != 0) _output.printfCR("%s", reaction.name.ptr()); else _output.writeCR(); _output.printfCR(" -INDIGO- %02d%02d%02d%02d%02d", lt.tm_mon + 1, lt.tm_mday, lt.tm_year % 100, lt.tm_hour, lt.tm_min); _output.writeCR(); if (_v2000) { if (reaction.catalystCount() > 0) _output.printf("%3d%3d%3d\n", reaction.reactantsCount(), reaction.productsCount(), reaction.catalystCount()); else _output.printf("%3d%3d\n", reaction.reactantsCount(), reaction.productsCount()); } else { if (reaction.catalystCount() > 0) _output.printf("M V30 COUNTS %d %d %d\n", reaction.reactantsCount(), reaction.productsCount(), reaction.catalystCount()); else _output.printf("M V30 COUNTS %d %d\n", reaction.reactantsCount(), reaction.productsCount()); } } void RxnfileSaver::_writeMolHeader () { if (_v2000) _output.writeStringCR("$MOL"); } void RxnfileSaver::_writeReactantsHeader () { if (!_v2000) _output.writeStringCR("M V30 BEGIN REACTANT"); } void RxnfileSaver::_writeProductsHeader () { if (!_v2000) _output.writeStringCR("M V30 BEGIN PRODUCT"); } void RxnfileSaver::_writeCatalystsHeader () { if (!_v2000) _output.writeStringCR("M V30 BEGIN AGENT"); } void RxnfileSaver::_writeMol (MolfileSaver &saver, int index) { saver.reactionAtomMapping = &_brxn->getAAMArray(index); saver.reactionAtomInversion = &_brxn->getInversionArray(index); saver.reactionBondReactingCenter = &_brxn->getReactingCenterArray(index); saver.skip_date = skip_date; if (_qrxn != 0) saver.reactionAtomExactChange = &_qrxn->getExactChangeArray(index); if (_qrxn != 0) { if (_v2000) saver.saveQueryMolecule(_qrxn->getQueryMolecule(index)); else saver.saveQueryCtab3000(_qrxn->getQueryMolecule(index)); } else { if (_v2000) saver.saveMolecule(_rxn->getMolecule(index)); else saver.saveCtab3000(_rxn->getMolecule(index)); } } void RxnfileSaver::_writeReactantsFooter () { if (!_v2000) _output.writeStringCR("M V30 END REACTANT"); } void RxnfileSaver::_writeProductsFooter () { if (!_v2000) _output.writeStringCR("M V30 END PRODUCT"); } void RxnfileSaver::_writeCatalystsFooter () { if (!_v2000) _output.writeStringCR("M V30 END AGENT"); } void RxnfileSaver::_writeRxnFooter () { if (!_v2000) _output.writeStringCR("M END"); } �������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0015706�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/CMakeLists.txt���������������������������������������������������������0000664�0000000�0000000�00000000560�12710376503�0020447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������project(Render2D) file (GLOB Render2D_src src/*.c src/*.cpp) file (GLOB Render2D_headers *.h *.hpp src/*.h src/*.hpp src/*.inc) include_directories(. src ${Cairo_headers_dir} ${Common_SOURCE_DIR} ${Molecule_SOURCE_DIR}/..) add_library(render2d OBJECT ${Render2D_src} ${Render2D_headers}) set_target_properties(render2d PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") ������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render.h���������������������������������������������������������������0000664�0000000�0000000�00000002732�12710376503�0017342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_h__ #define __render_h__ #include "render_internal.h" namespace indigo { class Render { public: Render (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); virtual ~Render() = 0; DECL_ERROR; protected: float _getObjScale (int item); int _getMaxWidth (); int _getMaxHeight (); float _getScale (int w, int h); float _getMaxScale (int w, int h); virtual float _getScaleGivenSize (int w, int h) = 0; virtual int _getDefaultWidth (const float s) = 0; virtual int _getDefaultHeight (const float s) = 0; int minMarg; RenderContext& _rc; const RenderSettings& _settings; const CanvasOptions& _cnvOpt; const RenderOptions& _opt; RenderItemFactory& _factory; int _bondLength; bool _bondLengthSet; }; } #endif //__render_h__ ��������������������������������������Indigo-indigo-1.2.3/render2d/render_cdxml.h���������������������������������������������������������0000664�0000000�0000000�00000003631�12710376503�0020530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_cdxml_h__ #define __render_cdxml_h__ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" namespace indigo { class RenderParams; class Molecule; struct Vec2f; class RenderCdxmlContext { public: enum { ALIGNMENT_LEFT, ALIGNMENT_RIGHT }; class PropertyData { public: Array<char> propertyName; Array<char> propertyValue; PropertyData(){}; private: PropertyData(PropertyData&); }; bool enabled; int keyAlignment; float propertyFontSize; Array<char> titleFont; Array<char> titleFace; Array<char> fonttable; Array<char> colortable; Array<char> propertyNameCaption; Array<char> propertyValueCaption; ObjArray<PropertyData> property_data; void clear() { enabled = false; keyAlignment = ALIGNMENT_LEFT; propertyFontSize = 12.0f; titleFont.clear(); titleFace.clear(); fonttable.clear(); colortable.clear(); propertyNameCaption.clear(); propertyValueCaption.clear(); property_data.clear(); } RenderCdxmlContext() :enabled(false){ clear(); }; private: RenderCdxmlContext(RenderCdxmlContext&); }; class RenderParamCdxmlInterface { public: static void render(RenderParams& params); }; } #endif // __render_cdxml_h__ �������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_common.h��������������������������������������������������������0000664�0000000�0000000�00000027550�12710376503�0020717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_common_h__ #define __render_common_h__ #include "molecule/elements.h" #include "base_cpp/tlscont.h" #include "layout/metalayout.h" #include "graph/graph.h" #include "reaction/base_reaction.h" #include "render2d/render_cdxml.h" typedef void* PVOID; namespace indigo { struct Vec2f; struct Edge; class BaseMolecule; class BaseReaction; class BaseMolecule; class Reaction; class QueryMolecule; class QueryReaction; class Output; enum DINGO_MODE {MODE_NONE, MODE_PDF, MODE_PNG, MODE_SVG, MODE_EMF, MODE_HDC, MODE_PRN, MODE_CDXML}; enum LABEL_MODE {LABEL_MODE_NONE, LABEL_MODE_HETERO, LABEL_MODE_TERMINAL_HETERO, LABEL_MODE_ALL}; enum STEREO_STYLE {STEREO_STYLE_EXT, STEREO_STYLE_OLD, STEREO_STYLE_NONE}; enum {CWC_BASE = -2, CWC_WHITE=0, CWC_BLACK, CWC_RED, CWC_GREEN, CWC_BLUE, CWC_DARKGREEN, CWC_COUNT}; enum FONT_SIZE {FONT_SIZE_LABEL=0, FONT_SIZE_ATTR, FONT_SIZE_RGROUP_LOGIC, FONT_SIZE_RGROUP_LOGIC_INDEX, FONT_SIZE_INDICES, FONT_SIZE_ATTACHMENT_POINT_INDEX, FONT_SIZE_RSITE_ATTACHMENT_INDEX, FONT_SIZE_COMMENT, FONT_SIZE_TITLE, FONT_SIZE_DATA_SGROUP, FONT_SIZE_COUNT/*must be the last*/}; enum COMMENT_POS {COMMENT_POS_TOP, COMMENT_POS_BOTTOM}; enum HYDRO_POS {HYDRO_POS_RIGHT = 0, HYDRO_POS_UP, HYDRO_POS_DOWN, HYDRO_POS_LEFT}; // cos(a) to cos(a/2) double cos2c (const double cs); // cos(a) to sin(a/2) double sin2c (const double cs); // cos(a) to tg(a/2) double tg2c (const double cs); // cos(a) to ctg(a/2) double ctg2c (const double cs); struct RenderItem { enum TYPE { RIT_NULL = 0, RIT_LABEL, RIT_PSEUDO, RIT_HYDROGEN, RIT_HYDROINDEX, RIT_ISOTOPE, RIT_CHARGESIGN, RIT_CHARGEVAL, RIT_RADICAL, RIT_STEREOGROUP, RIT_VALENCE, RIT_AAM, RIT_CHIRAL, RIT_ATTACHMENTPOINT, RIT_ATOMID, RIT_TOPOLOGY, RIT_SGROUP, RIT_DATASGROUP, RIT_COMMENT, RIT_TITLE }; RenderItem(); void clear(); TYPE ritype; Vec2f bbp; Vec2f bbsz; Vec2f relpos; int color; bool highlighted; bool noBondOffset; }; struct TextItem : public RenderItem { TextItem() { clear(); } void clear(); Array<char> text; FONT_SIZE fontsize; }; struct GraphItem : public RenderItem { enum TYPE {DOT, CAP, PLUS, MINUS}; GraphItem() { clear(); } void clear(); TYPE type; }; struct RenderItemBracket : public RenderItem { RenderItemBracket() { clear(); } void clear(); Vec2f p0, p1, q0, q1, d, n; float width, length; bool invertUpperLowerIndex; }; struct RenderItemAttachmentPoint : public RenderItem { RenderItemAttachmentPoint() { clear(); } void clear(); int number; Vec2f p0, p1, dir; }; struct RenderItemRSiteAttachmentIndex : public RenderItem { RenderItemRSiteAttachmentIndex() { clear(); } void clear(); int number; float radius; }; struct AtomDesc { enum TYPE {TYPE_REGULAR, TYPE_PSEUDO, TYPE_QUERY}; AtomDesc(); void clear (); int tibegin, ticount; int gibegin, gicount; int attachmentPointBegin, attachmentPointCount; int rSiteAttachmentIndexBegin, rSiteAttachmentIndexCount; int type; bool showLabel; bool showHydro; HYDRO_POS hydroPos; bool isRGroupAttachmentPoint; bool fixed; bool exactChange; bool pseudoAtomStringVerbose; Vec2f pos; Vec2f boundBoxMin; Vec2f boundBoxMax; Vec3f hcolor; bool hcolorSet; int label; int queryLabel; int color; int stereoGroupType; int stereoGroupNumber; int implicit_h; Array<int> list; Array<char> pseudo; Array<int> nearbyAtoms; int aam; int inversion; float implHPosWeights[4]; float upperSin, lowerSin, rightSin, leftSin; float leftMargin, rightMargin, ypos, height; private: AtomDesc(const AtomDesc& ad); }; struct Sgroup { Sgroup(); void clear (); int tibegin, ticount; int gibegin, gicount; int bibegin, bicount; private: Sgroup(const SGroup& sg); }; struct BondEnd { BondEnd (); void clear (); Vec2f dir; Vec2f lnorm; Vec2f p; // corrected position float rcos; float rsin; // these are bond end ids in the _data.bondend array, NOT neighbor indices in the molecule! int rnei; int lnei; float rang; float lcos; float lsin; int next; float lang; bool centered; int aid; int bid; float offset; bool prolong; int lRing; float width; private: BondEnd (const BondEnd& be); }; struct BondDescr : public Edge { BondDescr (); DECL_ERROR; void clear (); int getBondEnd (int aid) const; Vec2f norm, dir, vb, ve, center; float thickness; float bahs, eahs; bool inRing; bool aromRing; bool stereoCare; bool centered; bool lineOnTheRight; bool isShort; int stereodir; bool cistrans; int type; int queryType; float length; int be1, be2; float extP, extN; int tiTopology; int topology; int reactingCenter; private: BondDescr (const BondDescr& bd); }; struct Ring { Ring (); void clear (); Array<int> bondEnds; Array<float> angles; int dblBondCount; bool aromatic; Vec2f center; float radius; private: Ring (const Ring& r); }; struct MoleculeRenderData { MoleculeRenderData (); void clear (); ObjArray<Sgroup> sgroups; ObjArray<AtomDesc> atoms; ObjArray<BondDescr> bonds; ObjArray<Ring> rings; ObjArray<BondEnd> bondends; ObjArray<TextItem> textitems; ObjArray<GraphItem> graphitems; ObjArray<RenderItemAttachmentPoint> attachmentPoints; ObjArray<RenderItemRSiteAttachmentIndex> rSiteAttachmentIndices; ObjArray<RenderItemBracket> brackets; Array<int> aam; Array<int> reactingCenters; Array<int> inversions; Array<int> exactChanges; private: MoleculeRenderData (const MoleculeRenderData& data); }; class RenderSettings { public: RenderSettings (); void init (float sf, float lwf); CP_DECL; TL_CP_DECL(Array<double>, bondDashAromatic); TL_CP_DECL(Array<double>, bondDashAny); TL_CP_DECL(Array<double>, bondDashSingleOrAromatic); TL_CP_DECL(Array<double>, bondDashDoubleOrAromatic); float labelInternalOffset; float lowerIndexShift; float unit; float bondLineWidth; float bondSpace; float boundExtent; float upperIndexShift; float radicalRightOffset; float radicalRightVertShift; float radicalTopOffset; float radicalTopDistDot; float radicalTopDistCap; float stereoGroupLabelOffset; float dashUnit; float eps; float stereoCareBoxSize; float cosineTreshold; float prolongAdjSinTreshold; float minBondLength; float graphItemDotRadius; float graphItemCapSlope; float graphItemCapBase; float graphItemCapWidth; float graphItemDigitWidth; float graphItemDigitHeight; float graphItemSignLineWidth; float graphItemPlusEdge; float fzz[FONT_SIZE_COUNT]; // Layout params, relative to average bond length units float layoutMarginHorizontal; float layoutMarginVertical; float plusSize; float metaLineWidth; float arrowLength; float arrowHeadWidth; float arrowHeadSize; float equalityInterval; float rGroupIfThenInterval; float neighboringLabelTolerance; float minSin; float neighboringAtomDistanceTresholdA; float neighboringAtomDistanceTresholdB; private: RenderSettings (const RenderSettings& settings); }; struct MultilineTextLayout { enum Alignment { Left, Right, Center }; MultilineTextLayout (); MultilineTextLayout (Alignment bbox, Alignment inbox); // Text can be aligned in different ways: left, right, center. But if text has multiple // lines then this lines can also be aligned in different ways relative to each other // +-----------------------------------+--------------+ // | View | Type | // +===================================+==============+ // | Line | left | // | Quite Long line | | // +-----------------------------------+--------------+ // | Line | right | // | Quite Long line | | // +-----------------------------------+--------------+ // | Line | center | // | Quite a Long line | | // +-----------------------------------+--------------+ // | Line | center-left | // | Quite a Long line | | // +-----------------------------------+--------------+ // Alignment of the bounding box Alignment bbox_alignment; // Text alignment insdie bounding box Alignment inbox_alignment; void clear (); // Returns values from 0.0 to 1.0 depending on the title box alignment float getBboxRelativeOffset () const; float getInboxRelativeOffset () const; static float getRelativeOffset (Alignment alignment); float getAnchorPoint (float area_x, float area_width, float text_width); }; struct CanvasOptions { CanvasOptions (); void clear (); int width; int height; int maxWidth; int maxHeight; int xOffset; int yOffset; float bondLength; int gridMarginX; int gridMarginY; int marginX; int marginY; int commentOffset; int titleOffset; Array<char> comment; Array<char> titleProp; COMMENT_POS commentPos; MultilineTextLayout commentAlign; MultilineTextLayout titleAlign; int gridColumnNumber; private: CanvasOptions (const CanvasOptions&); }; class RenderOptions { public: RenderOptions (); void clear(); Vec3f backgroundColor; Vec3f baseColor; bool highlightThicknessEnable; float highlightThicknessFactor; bool highlightColorEnable; Vec3f highlightColor; Vec3f aamColor; float commentFontFactor; float commentSpacing; float titleFontFactor; float titleSpacing; Vec3f commentColor; Vec3f titleColor; Vec3f dataGroupColor; LABEL_MODE labelMode; bool highlightedLabelsVisible; bool boldBondDetection; bool implHVisible; DINGO_MODE mode; Output* output; PVOID hdc; bool showBondIds; bool showBondEndIds; bool atomBondIdsFromOne; bool showNeighborArcs; bool showAtomIds; bool showValences; bool atomColoring; STEREO_STYLE stereoMode; bool showReactingCenterUnchanged; bool centerDoubleBondWhenStereoAdjacent; bool showCycles; // for diagnostic purposes bool agentsBelowArrow; bool collapseSuperatoms; Array<char> atomColorProp; AutoPtr<RenderCdxmlContext> cdxml_context; private: RenderOptions (const RenderOptions& ); }; } #define QUERY_MOL_BEGIN(mol) if (mol->isQueryMolecule()) { QueryMolecule& qmol = mol->asQueryMolecule() #define QUERY_MOL_END } #define QUERY_RXN_BEGIN1(rxn) if (rxn->isQueryReaction()) { QueryReaction& qr = rxn->asQueryReaction() #define QUERY_RXN_BEGIN if (_r->isQueryReaction()) { QueryReaction& qr = _r->asQueryReaction() #define QUERY_RXN_END } #endif //__render_common_h__��������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_context.h�������������������������������������������������������0000664�0000000�0000000�00000015760�12710376503�0021113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_context_h__ #define __render_context_h__ #include <cairo.h> #include <cairo-pdf.h> #include <cairo-svg.h> #include "render_common.h" namespace indigo { class RenderContext { public: DECL_ERROR; void checkPathNonEmpty () const; RenderContext (const RenderOptions& opt, float sf, float lwf); void setDefaultScale (float scale); void setHDC (PVOID hdc); int getMaxPageSize () const; void setLineWidth (double width); void setFontFamily (const char* ff); void setOutput (Output* output); void createSurface(cairo_write_func_t writer, Output* output, int width, int height); void init(); void fillBackground(); void initNullContext (); void initContext (int width, int height); void closeContext (bool discard); void translate (float dx, float dy); void scale (float s); void storeTransform (); void restoreTransform (); void resetTransform (); void removeStoredTransform (); void drawRectangle (const Vec2f& p, const Vec2f& sz); void drawItemBackground (const RenderItem& item); void drawTextItemText (const TextItem& ti); void drawTextItemText (const TextItem& ti, const Vec3f& color); void drawTextItemText (const TextItem& ti, const Vec3f& color, bool bold); void drawBracket (RenderItemBracket& bracket); void drawAttachmentPoint (RenderItemAttachmentPoint& ri); void drawRSiteAttachmentIndex (RenderItemRSiteAttachmentIndex& ri); void drawLine (const Vec2f& v0, const Vec2f& v1); void fillHex (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4, const Vec2f& v5); void fillQuad (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3); void fillQuadStripes (const Vec2f& v0r, const Vec2f& v0l, const Vec2f& v1r, const Vec2f& v1l, int cnt); void fillPentagon (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4); void drawQuad (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3); void drawTriangleZigzag (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, int cnt); void drawCircle (const Vec2f& center, const float r); void fillCircle (const Vec2f& center, const float r); void drawArc (const Vec2f& center, const float r, const float a0, const float a1); void drawPoly (const Array<Vec2f>& v); void setFontSize (double fontSize); void setTextItemSize (TextItem& ti); void setTextItemSize (TextItem& ti, const Vec2f& c); void setGraphItemSizeDot (GraphItem& gi); void setGraphItemSizeCap (GraphItem& gi); void setGraphItemSizeSign (GraphItem& gi, GraphItem::TYPE type); void drawGraphItem (GraphItem& gi); void drawGraphItem (GraphItem& gi, const Vec3f& color); void fillRect (double x, double y, double w, double h); void getColor (float& r, float& g, float& b, int c); int getElementColor (int label); void getColorVec (Vec3f& v, int color); void setSingleSource (int color); void setSingleSource (const Vec3f& color); void setGradientSource (const Vec3f& color1, const Vec3f& color2, const Vec2f& pos1, const Vec2f& pos2); void clearPattern (); float _getDashedLineAlignmentOffset (float length); void setDash (const Array<double>& dash, float offset = 0); void resetDash (); void drawPlus (const Vec2f& pos, const float linewidth, const float size); void drawEquality (const Vec2f& pos, const float linewidth, const float size, const float interval); void drawArrow (const Vec2f& p1, const Vec2f& p2, const float width, const float headwidth, const float headsize); float highlightedBondLineWidth () const; float currentLineWidth () const; void setHighlight(); void resetHighlight(); void resetHighlightThickness(); const RenderSettings& getRenderSettings () const {return _settings;} int getWidth() const {return _width;} int getHeight() const {return _height;} void cairoCheckStatus () const; #ifdef _WIN32 cairo_surface_t* createWin32Surface (); cairo_surface_t* createWin32PrintingSurfaceForHDC (); cairo_surface_t* createWin32PrintingSurfaceForMetafile (bool& isLarge); void storeAndDestroyMetafile (bool discard); #endif void fontsClear(); void fontsInit(); void fontsDispose(); double fontGetSize(FONT_SIZE size); void fontsSetFont(cairo_t* cr, FONT_SIZE size, bool bold); void fontsGetTextExtents(cairo_t* cr, const char* text, int size, float& dx, float& dy, float& rx, float& ry); void fontsDrawText(const TextItem& ti, const Vec3f& color, bool bold); void bbIncludePoint (const Vec2f& v); void bbIncludePoint (double x, double y); void bbIncludePath (bool stroke); void bbGetMin (Vec2f& v); void bbGetMax (Vec2f& v); void _bbVecToUser (Vec2f& d, const Vec2f& s); Vec2f bbmin, bbmax; private: static cairo_status_t writer (void *closure, const unsigned char *data, unsigned int length); void _drawGraphItem (GraphItem& gi); void lineTo (const Vec2f& v); void lineToRel (float x, float y); void lineToRel (const Vec2f& v); void moveTo (const Vec2f& v); void moveToRel (float x, float y); void moveToRel (const Vec2f& v); int _width; int _height; float _defaultScale; Vec3f _backColor; Vec3f _baseColor; float _currentLineWidth; cairo_pattern_t* _pattern; class TextLock { public: TextLock () { #ifdef _WIN32 osMutexCreate(&_mutex); #endif } ~TextLock () { #ifdef _WIN32 osMutexDelete(&_mutex); #endif } void lock () { #ifdef _WIN32 osMutexLock(&_mutex); #endif } void unlock () { #ifdef _WIN32 osMutexUnlock(&_mutex); #endif } private: #ifdef _WIN32 os_mutex _mutex; #endif }; static TextLock _tlock; CP_DECL; TL_CP_DECL(Array<char>, _fontfamily); TL_CP_DECL(Array<cairo_matrix_t>, transforms); #ifdef _WIN32 void* _h_fonts[FONT_SIZE_COUNT * 2]; #endif cairo_font_face_t *cairoFontFaceRegular, *cairoFontFaceBold; cairo_matrix_t fontScale, fontCtm; cairo_font_options_t *fontOptions; cairo_scaled_font_t *_scaled_fonts[FONT_SIZE_COUNT * 2]; bool metafileFontsToCurves; cairo_t* _cr; cairo_surface_t* _surface; void* _meta_hdc; public: RenderSettings _settings; const RenderOptions& opt; }; } #endif ����������������Indigo-indigo-1.2.3/render2d/render_grid.h����������������������������������������������������������0000664�0000000�0000000�00000002700�12710376503�0020342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_grid_h__ #define __render_grid_h__ #include "render.h" namespace indigo { class RenderGrid : Render { public: RenderGrid (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); virtual ~RenderGrid(); void draw (); DECL_ERROR; Array<int> objs; Array<int> titles; Array<int> refAtoms; int titleOffset; int nColumns; int commentOffset; int comment; private: void _drawComment(); int nRows; float scale; Vec2f maxsz; Vec2f cellsz; Vec2f outerMargin; Vec2f maxTitleSize; Vec2f clientArea; Vec2f commentSize; int _width, _height; float _getScaleGivenSize (int w, int h); int _getDefaultWidth (const float s); int _getDefaultHeight (const float s); }; } #endif //__render_grid_h__ ����������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_internal.h������������������������������������������������������0000664�0000000�0000000�00000016106�12710376503�0021236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_internal_h__ #define __render_internal_h__ #include "render_common.h" namespace indigo { class RenderContext; class MoleculeRenderInternal { public: MoleculeRenderInternal (const RenderOptions& opt, const RenderSettings& settings, RenderContext& cw); void setMolecule (BaseMolecule* mol); void setIsRFragment (bool isRFragment); void setScaleFactor (const float scaleFactor, const Vec2f& min, const Vec2f& max); void render (); void setReactionComponentProperties (const Array<int>* aam, const Array<int>* reactingCenters, const Array<int>* inversions); void setQueryReactionComponentProperties (const Array<int>* exactChanges); DECL_ERROR; private: enum STEREOGROUPS_MODE {STEREOGROUPS_SHOW, STEREOGROUPS_HIDE}; struct LocalOptions { STEREOGROUPS_MODE stereoMode; }; BondEnd& _be (int beid); const BondEnd& _be (int beid) const; BondDescr& _bd (int bid); const BondDescr& _bd (int bid) const; AtomDesc& _ad (int aid); const AtomDesc& _ad (int aid) const; void _checkSettings (); void _extendRenderItem (RenderItem& item, const float extent); bool _clipRaySegment (float& offset, const Vec2f& p, const Vec2f& d, const Vec2f& n0, const Vec2f& a, const Vec2f& b, const float w); bool _clipRayBox (float& offset, const Vec2f& p, const Vec2f& d, const Vec2f& rp, const Vec2f& sz, const float w); void _findMinMax(); void _objCoordTransform(Vec2f& p, const Vec2f& v) const; void _objDistTransform(Vec2f& p, const Vec2f& v) const; void _initCoordinates(); void _determineDoubleBondShift(); void _determineStereoGroupsMode(); static const char* _getStereoGroupText (int type); bool _ringHasSelfIntersectionsSimple(const Ring& ring); bool _ringHasSelfIntersections(const Ring& ring); void _findRings(); void _prepareLabels(); void _rotateHalfCenteredBonds(); bool _isSingleHighlighted (int aid); bool _vertexIsHighlighted (int aid); bool _edgeIsHighlighted (int bid); bool _hasQueryModifiers (int aid); void _findNearbyAtoms(); void _initHydroPos(int aid); int _hydroPosFindConflict(int i); bool _hydroPosCorrectGreedy (); void _hydroPosCorrectRepulse (); void _initAtomData(); void _initRGroups(); void _loadBrackets(Sgroup& sg, const Array<Vec2f[2]>& coord, bool transformCoordinates); void _placeBrackets(Sgroup& sg, const Array<int>& atoms); void _positionIndex(Sgroup& sg, int ti, bool lower); void _loadBracketsAuto(const SGroup& group, Sgroup& sg); void _initDataSGroups(); void _initSruGroups(); void _initMulGroups(); void _initSupGroups(); void _prepareSGroups(); void _findAnglesOverPi(); void _renderBondIds(); void _renderAtomIds(); void _renderEmptyRFragment(); void _renderLabels(); void _renderRings(); void _renderSGroups (); void _setHighlightOpt(); void _resetHighlightOpt(); void _renderBonds(); void _applyBondOffset(); void _setBondCenter (); float _getBondOffset (int aid, const Vec2f& pos, const Vec2f& dir, const float bondWidth); void _calculateBondOffset(); void _findNeighbors(); void _findCenteredCase(); void _initBondData(); void _initBondEndData(); void _initBoldStereoBonds(); void _extendRenderItems(); BondEnd& _getBondEnd(int aid, int nei); int _getBondEndIdx (int aid, int nei); int _getOpposite (int beid) const; void _drawAtom (const AtomDesc& desc); void _writeQueryAtomToString (Output& output, int aid); bool _writeDelimiter (bool needDelimiter, Output &output); void _writeQueryModifier (Output& output, int aid); int _findClosestCircle (Vec2f& p, int aid, float radius, int skip = -1); int _findClosestBox (Vec2f& p, int aid, const Vec2f& sz, float mrg, int skip = -1); void _preparePseudoAtom (int aid, int color, bool highlighted); void _prepareLabelText (int aid); void _prepareAAM (); int _pushTextItem (RenderItem::TYPE type, int color, bool highlighted); int _pushTextItem (AtomDesc& ad, RenderItem::TYPE type, int color, bool highlighted); int _pushTextItem (Sgroup& sg, RenderItem::TYPE ritype, int color = CWC_BASE); int _pushGraphItem (RenderItem::TYPE type, int color, bool highlighted); int _pushGraphItem (AtomDesc& ad, RenderItem::TYPE type, int color, bool highlighted); int _pushGraphItem (Sgroup& ad, RenderItem::TYPE type, int color = CWC_BASE); const char* _valenceText (const int valence); float _ctghalf (float cs); void _drawBond (int b); void _drawTopology (BondDescr& bd); void _drawReactingCenter (BondDescr& bd, int rc); float _doubleBondShiftValue (const BondEnd& be, bool right, bool centered); void _prepareDoubleBondCoords (Vec2f* coord, BondDescr& bd, const BondEnd& be1, const BondEnd& be2, bool allowCentered); void _drawStereoCareBox (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); double _getAdjustmentFactor (const int aid, const int anei, const double acos, const double asin, const double tgb, const double csb, const double snb, const double len, const double w, double& csg, double& sng); void _adjustAngle (Vec2f& l, const BondEnd& be1, const BondEnd& be2, bool left); void _bondBoldStereo (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondSingle (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondDouble (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondSingleOrAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondDoubleOrAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondSingleOrDouble (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondTriple (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); void _bondAny (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); int _parseColorString (Scanner& str, float& r, float& g, float& b); // local void* _hdc; BaseMolecule* _mol; RenderContext& _cw; float _scale; Vec2f _min, _max; LocalOptions _lopt; bool isRFragment; const RenderSettings& _settings; const RenderOptions& _opt; CP_DECL; TL_CP_DECL(MoleculeRenderData, _data); TL_CP_DECL(Array<int>, _atomMapping); TL_CP_DECL(Array<int>, _atomMappingInv); TL_CP_DECL(BaseMolecule::Mapping, _bondMappingInv); }; } #endif //__render_internal_h__ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item.h����������������������������������������������������������0000664�0000000�0000000�00000002666�12710376503�0020366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_h__ #define __render_item_h__ #include "render_context.h" namespace indigo { class RenderItemFactory; class RenderItemBase { public: RenderItemBase (RenderItemFactory& factory); virtual ~RenderItemBase () { } DECL_ERROR; virtual void estimateSize () = 0; virtual void setObjScale (float scale) = 0; virtual void init () = 0; virtual void render () = 0; virtual float getTotalBondLength () = 0; virtual float getTotalClosestAtomDistance() = 0; virtual int getBondCount () = 0; virtual int getAtomCount () = 0; Vec2f size; Vec2f origin; float referenceY; protected: void renderIdle (); RenderItemFactory& _factory; RenderContext& _rc; const RenderSettings& _settings; const RenderOptions& _opt; }; } #endif //__render_item_h__ ��������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_aux.h������������������������������������������������������0000664�0000000�0000000�00000003173�12710376503�0021235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_aux_h__ #define __render_item_aux_h__ #include "render_item.h" namespace indigo { class RenderItemAuxiliary : public RenderItemBase { public: enum AUX_TYPE {AUX_COMMENT = 0, AUX_TITLE, AUX_RXN_PLUS, AUX_RXN_ARROW, AUX_RGROUP_LABEL, AUX_RGROUP_IFTHEN}; RenderItemAuxiliary (RenderItemFactory& factory); virtual ~RenderItemAuxiliary (); DECL_ERROR; virtual void estimateSize () { renderIdle(); } virtual void setObjScale (float scale) {} virtual void init () {} virtual void render (); virtual float getTotalBondLength () { return 0.0f; } virtual float getTotalClosestAtomDistance () { return 0.0f; } virtual int getBondCount () { return 0; } virtual int getAtomCount () { return 0; } AUX_TYPE type; Array<char> text; BaseMolecule* mol; int rLabelIdx; float arrowLength; private: void _drawRGroupLabel (); void _drawRIfThen (); void _drawText (); void _drawPlus (); void _drawArrow (); }; } #endif //__render_item_aux_h__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_column.h���������������������������������������������������0000664�0000000�0000000�00000002316�12710376503�0021733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_column_h__ #define __render_item_column_h__ #include "render_item_container.h" namespace indigo { class RenderItemColumn : public RenderItemContainer { public: RenderItemColumn (RenderItemFactory& factory); virtual ~RenderItemColumn () {} DECL_ERROR; virtual void init (); void setVerticalSpacing(float spacing); void setAlignment(MultilineTextLayout::Alignment alignment); virtual void estimateSize (); virtual void render (); private: float vSpace; MultilineTextLayout::Alignment alignment; }; } #endif //__render_item_column_h__ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_container.h������������������������������������������������0000664�0000000�0000000�00000002307�12710376503�0022420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_container_h__ #define __render_item_container_h__ #include "render_item.h" namespace indigo { class RenderItemContainer : public RenderItemBase { public: RenderItemContainer (RenderItemFactory& factory); virtual ~RenderItemContainer () {} DECL_ERROR; virtual void estimateSize (); virtual void setObjScale (float scale); virtual float getTotalBondLength (); virtual float getTotalClosestAtomDistance (); virtual int getBondCount (); virtual int getAtomCount (); Array<int> items; }; } #endif //__render_item_container_h__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_factory.h��������������������������������������������������0000664�0000000�0000000�00000005321�12710376503�0022104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_factory_h__ #define __render_item_factory_h__ #include "render_context.h" #include "render_item.h" #include "render_item_fragment.h" #include "render_item_aux.h" #include "render_item_hline.h" #include "render_item_column.h" #include "render_item_molecule.h" #include "render_item_reaction.h" namespace indigo { #define GET_ITEM(name) case TYPE_##name: return _item##name[item.id]; #define IMPL_ITEM(name) \ int addItem##name() \ { \ int id = _item##name.add(*this); \ int num = _items.add(id); \ _items[num].type = TYPE_##name; \ return num; \ } \ RenderItem##name& getItem##name (int i) \ { \ Item& item = _items[i]; \ if (item.type != TYPE_##name) \ throw Error("Item type mismatch"); \ return _item##name[item.id]; \ } \ bool isItem##name (int i) \ { \ Item& item = _items[i]; \ return item.type == TYPE_##name; \ } #define DEF_POOL(name) ObjPool<RenderItem##name> _item##name; class RenderItemFactory { public: RenderItemFactory (RenderContext& rc_) : rc(rc_) { } virtual ~RenderItemFactory () { } DECL_ERROR; enum TYPE { TYPE_Fragment, TYPE_Auxiliary, TYPE_HLine, TYPE_Column, TYPE_Molecule, TYPE_Reaction, TYPE_Comment, }; RenderItemBase& getItem (int i) { Item& item = _items[i]; switch (item.type) { GET_ITEM(Fragment); GET_ITEM(Auxiliary); GET_ITEM(HLine); GET_ITEM(Column); GET_ITEM(Molecule); GET_ITEM(Reaction); default: throw Error("Item type unrecognized"); } } IMPL_ITEM(Fragment); IMPL_ITEM(Auxiliary); IMPL_ITEM(HLine); IMPL_ITEM(Column); IMPL_ITEM(Molecule); IMPL_ITEM(Reaction); RenderContext& rc; private: struct Item { Item (int id_) : id(id_) { } int type; int id; }; DEF_POOL(Fragment); DEF_POOL(Auxiliary); DEF_POOL(HLine); DEF_POOL(Column); DEF_POOL(Molecule); DEF_POOL(Reaction); ObjPool<Item> _items; }; } #endif //__render_item_factory_h__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_fragment.h�������������������������������������������������0000664�0000000�0000000�00000003031�12710376503�0022234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_fragment_h__ #define __render_item_fragment_h__ #include "render_item.h" namespace indigo { class RenderItemFactory; class RenderItemFragment : public RenderItemBase { public: RenderItemFragment (RenderItemFactory& factory); virtual ~RenderItemFragment (); DECL_ERROR; virtual void estimateSize (); virtual void setObjScale (float scale) { _scaleFactor = scale; } virtual void init (); virtual void render (); virtual float getTotalBondLength (); virtual float getTotalClosestAtomDistance (); virtual int getBondCount (); virtual int getAtomCount (); BaseMolecule* mol; bool isRFragment; Array<int>* aam; Array<int>* reactingCenters; Array<int>* inversionArray; Array<int>* exactChangeArray; int refAtom; Vec2f refAtomPos; private: float _scaleFactor; Vec2f _min, _max; }; } #endif //__render_item_fragment_h__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_hline.h����������������������������������������������������0000664�0000000�0000000�00000002052�12710376503�0021532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_hline_h__ #define __render_item_hline_h__ #include "render_item_container.h" namespace indigo { class RenderItemHLine : public RenderItemContainer { public: RenderItemHLine (RenderItemFactory& factory); virtual ~RenderItemHLine () {} DECL_ERROR; virtual void init (); virtual void estimateSize (); virtual void render (); float hSpace; }; } #endif //__render_item_hline_h__ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_molecule.h�������������������������������������������������0000664�0000000�0000000�00000002324�12710376503�0022242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_molecule_h__ #define __render_item_molecule_h__ #include "render_item_fragment.h" #include "render_item_aux.h" #include "render_item_hline.h" namespace indigo { class RenderItemMolecule : public RenderItemContainer { public: RenderItemMolecule (RenderItemFactory& factory); virtual ~RenderItemMolecule () {} DECL_ERROR; virtual void init (); virtual void estimateSize (); virtual void render (); BaseMolecule* mol; int refAtom; Vec2f refAtomPos; private: int _core; int _getRIfThenCount (); }; } #endif //__render_item_molecule_h__ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_item_reaction.h�������������������������������������������������0000664�0000000�0000000�00000002533�12710376503�0022243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_item_reaction_h__ #define __render_item_reaction_h__ #include "render_item_fragment.h" #include "render_item_aux.h" #include "render_item_hline.h" namespace indigo { class RenderItemReaction : public RenderItemContainer { public: RenderItemReaction (RenderItemFactory& factory); virtual ~RenderItemReaction () {} DECL_ERROR; virtual void init (); virtual void estimateSize (); virtual void render (); BaseReaction* rxn; float hSpace, catalystOffset; private: int _addFragment (int id); int _addPlus (); int _reactantLine, _catalystLineUpper, _catalystLineLower, _productLine, _arrow; bool _splitCatalysts; float _arrowWidth; }; } #endif //__render_item_reaction_h__ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_params.h��������������������������������������������������������0000664�0000000�0000000�00000004047�12710376503�0020706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_params_h__ #define __render_params_h__ #include "base_cpp/auto_ptr.h" #include "render_common.h" namespace indigo { class BaseMolecule; class Reaction; class Scanner; class Output; class RenderItemFactory; enum RENDER_MODE {RENDER_MOL, RENDER_RXN, RENDER_NONE}; class RenderParams { public: RenderParams (); ~RenderParams (); void clear (); void clearArrays (); float relativeThickness; float bondLineWidthFactor; RENDER_MODE rmode; AutoPtr<BaseMolecule> mol; AutoPtr<BaseReaction> rxn; PtrArray<BaseMolecule> mols; PtrArray<BaseReaction> rxns; ObjArray<Array <char> > titles; Array<int> refAtoms; RenderOptions rOpt; CanvasOptions cnvOpt; }; class RenderParamInterface { public: DECL_ERROR; static void render (RenderParams& params); static int multilineTextUnit (RenderItemFactory& factory, int type, const Array<char>& titleStr, const float spacing, const MultilineTextLayout::Alignment alignment); private: static void _prepareMolecule (RenderParams& params, BaseMolecule& bm); static void _prepareReaction (RenderParams& params, BaseReaction& rxn); static bool needsLayoutSub (BaseMolecule& mol); static bool needsLayout (BaseMolecule& mol); RenderParamInterface (); RenderParamInterface (const RenderParamInterface&); }; } #endif //__render_params_h__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/render_single.h��������������������������������������������������������0000664�0000000�0000000�00000002513�12710376503�0020700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #ifndef __render_single_h__ #define __render_single_h__ #include "render.h" namespace indigo { class RenderSingle : Render { public: RenderSingle (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet); virtual ~RenderSingle(); void draw (); DECL_ERROR; int obj; int comment; float scale; int commentOffset; Vec2f objSize; Vec2f commentSize; Vec2f outerMargin; Vec2f objArea; int width, height; private: float _getScaleGivenSize (int w, int h); int _getDefaultWidth (const float s); int _getDefaultHeight (const float s); void _drawComment (); void _drawObj (); }; } #endif //__render_single_h__ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016475�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render.cpp���������������������������������������������������������0000664�0000000�0000000�00000006030�12710376503�0020457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/output.h" #include "base_cpp/reusable_obj_array.h" #include "layout/metalayout.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "render_context.h" #include "render_item.h" #include "render_item_factory.h" #include "render.h" using namespace indigo; IMPL_ERROR(Render, "Render"); Render::Render (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) : minMarg(2), _rc(rc), _settings(rc.getRenderSettings()), _cnvOpt(cnvOpt), _opt(rc.opt), _factory(factory), _bondLength(bondLength), _bondLengthSet(bondLengthSet) {} Render::~Render() {} float Render::_getObjScale (int item) { float avgBondLength = 1.0f; int bondCount = _factory.getItem(item).getBondCount(); int atomCount = _factory.getItem(item).getAtomCount(); if (bondCount > 0) { avgBondLength = _factory.getItem(item).getTotalBondLength() / bondCount; } else { avgBondLength = _factory.getItem(item).getTotalClosestAtomDistance() / atomCount; } if (avgBondLength < 1e-4) avgBondLength = 1.0f; float objScale = 1 / avgBondLength; return objScale; } int Render::_getMaxWidth () { int maxPageSize = _rc.getMaxPageSize(); return _cnvOpt.maxWidth > 0 ? __min(_cnvOpt.maxWidth, maxPageSize) : maxPageSize; } int Render::_getMaxHeight () { int maxPageSize = _rc.getMaxPageSize(); return _cnvOpt.maxHeight > 0 ? __min(_cnvOpt.maxHeight, maxPageSize) : maxPageSize; } float Render::_getScale (int w, int h) { float scale = _getMaxScale(w, h); if (_bondLength > 0 && _bondLength < scale) return (float)_bondLength; return scale; } float Render::_getMaxScale (int w, int h) { float s = (float)(_bondLength > 0 ? _bondLength : 100); int maxWidth = _getMaxWidth(); int maxHeight = _getMaxHeight(); int defaultWidth = _getDefaultWidth(s); int defaultHeight = _getDefaultHeight(s); if (h >= 1 && w >= 1) return _getScaleGivenSize(w, h); if (h >= 1) return _getScaleGivenSize(maxWidth, h); if (w >= 1) return _getScaleGivenSize(w, maxHeight); if (defaultWidth <= maxWidth && defaultHeight <= maxHeight) return s; return _getScaleGivenSize(__min(defaultWidth, maxWidth), __min(defaultHeight, maxHeight)); }��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_cdxml.cpp���������������������������������������������������0000664�0000000�0000000�00000034216�12710376503�0021655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "render_cdxml.h" #include "render_params.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "molecule/molecule_cdxml_saver.h" using namespace indigo; // Molecule position on the page struct Pos { // Structure min and max coordinates Vec2f str_min, str_max; // Offset and size on the page Vec2f page_offset; Vec2f size, all_size; // Final offset for the coordinates Vec2f offset; float title_offset_y; // Structure scaling coefficient float scale; }; void _getBounds (RenderParams& params, BaseMolecule &mol, Vec2f &min, Vec2f &max, float &scale) { // Compute average bond length float avg_bond_length = 1; if (mol.edgeCount() > 0) { float bond_length_sum = 0; for (int i = mol.edgeBegin(); i != mol.edgeEnd(); i = mol.edgeNext(i)) { const Edge& edge = mol.getEdge(i); const Vec3f &p1 = mol.getAtomXyz(edge.beg); const Vec3f &p2 = mol.getAtomXyz(edge.end); bond_length_sum += Vec3f::dist(p1, p2); } avg_bond_length = bond_length_sum / mol.edgeCount(); } float bond_length = 1; if (params.cnvOpt.bondLength > 0) bond_length = params.cnvOpt.bondLength / 100.0f; scale = bond_length / avg_bond_length; for (int i = mol.vertexBegin(); i != mol.vertexEnd(); i = mol.vertexNext(i)) { Vec3f &p = mol.getAtomXyz(i); Vec2f p2(p.x, p.y); if (i == mol.vertexBegin()) min = max = p2; else { min.min(p2); max.max(p2); } } min.scale(scale); max.scale(scale); } int _findReverse(int from, int to, const Array<char>& _array, char value) { for (int i = to - 1; i >= from; i--) { if (_array[i] == value) return i; } return -1; } int _getLongestLineXml(const Array<char>& line) { int longest_line = 0; if (line.size() > 0) { int start = 0; while (start < line.size() - 1) { int next = line.find(start + 1, line.size(), '\n'); if (next == -1) next = line.size() - 1; int st = _findReverse(start + 1, next - 1, line, '>'); if (st == -1) { st = start; } longest_line = __max(next - st, longest_line); start = next; } } return longest_line; } int _getLongestLine(const Array<char>& line) { int longest_line = 0; if (line.size() > 0) { int start = 0; while (start < line.size()) { int next = line.find(start + 1, line.size(), '\n'); if (next == -1) next = line.size(); longest_line = __max(next - start, longest_line); start = next; } } return longest_line; } void RenderParamCdxmlInterface::render (RenderParams& params) { MoleculeCdxmlSaver saver(*params.rOpt.output); Array<BaseMolecule*> mols; if (params.mols.size() != 0) for (int i = 0; i < params.mols.size(); ++i) mols.push(params.mols[i]); else mols.push(params.mol.get()); Vec2f offset(0, 0); Array<float> column_widths; column_widths.resize(params.cnvOpt.gridColumnNumber); column_widths.fill(0); Array<float> title_widths; title_widths.resize(mols.size()); title_widths.fill(0); Array<float> key_widths; key_widths.resize(mols.size()); key_widths.fill(0); Array<float> prop_widths; prop_widths.resize(mols.size()); prop_widths.fill(0); Array<Pos> positions; positions.resize(mols.size()); Array<float> title_heights; title_heights.resize(mols.size()); title_heights.fill(0); for (int mol_idx = 0; mol_idx < mols.size(); ++mol_idx) { int column = mol_idx % params.cnvOpt.gridColumnNumber; Pos &p = positions[mol_idx]; _getBounds(params, mols[mol_idx]->asMolecule(), p.str_min, p.str_max, p.scale); float width = p.str_max.x - p.str_min.x; // Check titles width if (mol_idx < params.titles.size()) { const Array<char> &title = params.titles[mol_idx]; if (title.size() > 0) { int longest_line = _getLongestLine(title); // On average letters has width 6 float letter_width = params.rOpt.titleFontFactor / 1.5f; float title_width = longest_line * letter_width / MoleculeCdxmlSaver::BOND_LENGTH; title_widths[mol_idx] = title_width; width = __max(width, title_width); } } if (params.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = params.rOpt.cdxml_context.ref(); if (context.enabled) { RenderCdxmlContext::PropertyData& data = context.property_data.at(mol_idx); float letter_width = context.propertyFontSize / 1.5f; int longest_line = _getLongestLineXml(data.propertyName); key_widths[mol_idx] = longest_line * letter_width / MoleculeCdxmlSaver::BOND_LENGTH; longest_line += _getLongestLineXml(data.propertyValue); float prop_width = longest_line * letter_width / MoleculeCdxmlSaver::BOND_LENGTH; prop_widths[mol_idx] = prop_width; width = __max(width, prop_width); } } column_widths[column] = __max(width, column_widths[column]); } float x_margins_base = 1.1f, y_margins_base = 1.1f; float x_grid_base = 1.5f; Array<float> column_offset; column_offset.resize(params.cnvOpt.gridColumnNumber); column_offset[0] = params.cnvOpt.marginX / 10.0f + x_margins_base; for (int i = 1; i < params.cnvOpt.gridColumnNumber; i++) column_offset[i] = column_offset[i - 1] + column_widths[i - 1] + x_grid_base; float page_y_offset_base = params.cnvOpt.marginY / 10.0f + y_margins_base; float row_y_offset = page_y_offset_base; int last_row = 0; float max_y = 0; float title_y = 0; // Get each structure bounds int row_moved = 0; for (int mol_idx = 0; mol_idx < mols.size(); ++mol_idx) { Pos &p = positions[mol_idx]; int column = mol_idx % params.cnvOpt.gridColumnNumber; int row = mol_idx / params.cnvOpt.gridColumnNumber; p.page_offset.x = column_offset[column]; p.page_offset.y = row_y_offset; p.size.diff(p.str_max, p.str_min); p.all_size = p.size; if (mol_idx < params.titles.size()) { const Array<char> &title = params.titles[mol_idx]; if (title.size() > 0) { int lines = title.count('\n') + 1; float letter_height = params.rOpt.titleFontFactor / MoleculeCdxmlSaver::BOND_LENGTH; //float title_height = lines * saver.textLineHeight(); //p.all_size.y += title_height + saver.textLineHeight(); // Add blank line float title_height = lines * letter_height; title_heights[mol_idx] = title_height; p.all_size.y += title_height + letter_height; // Add blank line } } if (params.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = params.rOpt.cdxml_context.ref(); if (context.enabled) { RenderCdxmlContext::PropertyData& data = context.property_data.at(mol_idx); int lines = data.propertyName.count('\n') + 1; float letter_height = params.rOpt.titleFontFactor / MoleculeCdxmlSaver::BOND_LENGTH; float prop_height = lines * letter_height; p.all_size.y += prop_height + letter_height; // Add blank line } } // Check that the structure is fully on a single page int pbegin = (int)(p.page_offset.y / saver.pageHeight()); int pend = (int)((p.page_offset.y + p.all_size.y) / saver.pageHeight()); // Additional check that we didn't moved this row before if (pbegin != pend && row_moved != row) { // Update starting row_y_offset for the whole row and start this row again row_y_offset = (pbegin + 1) * saver.pageHeight() + page_y_offset_base; mol_idx = row * params.cnvOpt.gridColumnNumber - 1; row_moved = row; continue; } p.offset.x = p.page_offset.x - p.str_min.x + (column_widths[column] - p.size.x) / 2; p.offset.y = -p.page_offset.y - p.str_max.y; p.title_offset_y = -p.page_offset.y - p.size.y - 1.0f; max_y = __max(max_y, p.page_offset.y + p.all_size.y); int next_row = (mol_idx + 1) / params.cnvOpt.gridColumnNumber; if (last_row != next_row) { row_y_offset = max_y + 1.0f; last_row = next_row; } } if (params.cnvOpt.comment.size() > 0) { int lines = params.cnvOpt.comment.count('\n') + 1; float comment_height = lines * 0.3f; max_y += 0.3f; title_y = max_y; max_y += comment_height; } MoleculeCdxmlSaver::Bounds b; b.min.set(0, 0); float w = column_offset[params.cnvOpt.gridColumnNumber - 1] + column_widths[params.cnvOpt.gridColumnNumber - 1]; w += x_margins_base + params.cnvOpt.marginX / 10.0f; b.max.set(w, max_y + y_margins_base); saver.beginDocument(&b); Array<char> font_attr; ArrayOutput font_out(font_attr); font_out.printf("<s size=\"%f\"", params.rOpt.titleFontFactor); if (params.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = params.rOpt.cdxml_context.ref(); if (context.fonttable.size() > 0) { saver.addFontTable(context.fonttable.ptr()); } if (context.colortable.size() > 0) { saver.addColorTable(context.colortable.ptr()); } if (context.titleFont.size() > 0) { font_out.printf(" font=\"%s\"", context.titleFont.ptr()); } if (context.titleFace.size() > 0) { font_out.printf(" face=\"%s\"", context.titleFace.ptr()); } } font_out.printf(">"); font_attr.push(0); //if (params.rOtitleFont.size() > 0) { // font_out.printf("id=\"5\" charset=\"iso-8859-1\" name=\"%s\"", params.cnvOpt.titleFont.ptr()); // font_attr.push(0); // saver.addFontTable(font_attr.ptr()); // /* // * Set font as id 5 always // */ // font_attr.clear(); // if (params.rOpt.titleFontFactor > 1) // font_out.printf(" font=\"5\" size=\"%.0f\"", params.rOpt.titleFontFactor); // else // font_out.printf(" font=\"5\""); //} else { // if (params.rOpt.titleFontFactor > 1) // font_out.printf(" size=\"%.0f\"", params.rOpt.titleFontFactor); //} //font_attr.push(0); saver.beginPage(&b); Array<char> title_font; for (int mol_idx = 0; mol_idx < mols.size(); ++mol_idx) { int column = mol_idx % params.cnvOpt.gridColumnNumber; Pos &p = positions[mol_idx]; Vec2f offset = p.offset; offset.scale(1 / p.scale); saver.saveMoleculeFragment(mols[mol_idx]->asMolecule(), offset, p.scale); if (mol_idx < params.titles.size()) { const Array<char> &title = params.titles[mol_idx]; if (title.size() > 0) { title_font.clear(); title_font.readString(font_attr.ptr(), false); // Get title bounding box float x = params.cnvOpt.titleAlign.getAnchorPoint(p.page_offset.x, column_widths[column], title_widths[mol_idx]); const char *alignment_str = ""; MultilineTextLayout alignment = params.cnvOpt.titleAlign; if (alignment.inbox_alignment == MultilineTextLayout::Center) alignment_str = "Center"; if (alignment.inbox_alignment == MultilineTextLayout::Left) alignment_str = "Left"; if (alignment.inbox_alignment == MultilineTextLayout::Right) alignment_str = "Right"; Vec2f title_offset(x, p.title_offset_y); title_font.appendString(title.ptr(), true); title_font.appendString("</s>", true); saver.addCustomText(title_offset, alignment_str, params.rOpt.titleFontFactor, title_font.ptr()); } } if (params.rOpt.cdxml_context.get() != NULL) { RenderCdxmlContext& context = params.rOpt.cdxml_context.ref(); if (context.enabled) { RenderCdxmlContext::PropertyData& data = context.property_data.at(mol_idx); float prop_width = prop_widths[mol_idx]; float key_width = key_widths[mol_idx]; float prop_offset_y = p.title_offset_y - title_heights[mol_idx]; float x = params.cnvOpt.titleAlign.getAnchorPoint(p.page_offset.x, column_widths[column], prop_width); float prop_offset_key = prop_width * 0.5f; float prop_offset_val = prop_offset_key - (prop_width - key_width); if (context.keyAlignment == RenderCdxmlContext::ALIGNMENT_LEFT) { Vec2f title_offset_key(x - prop_offset_key, prop_offset_y); Vec2f title_offset_val(x + prop_offset_val, prop_offset_y); saver.addCustomText(title_offset_key, "Left", context.propertyFontSize, data.propertyName.ptr()); saver.addCustomText(title_offset_val, "Left", context.propertyFontSize, data.propertyValue.ptr()); } else { Vec2f title_offset_key(x + prop_offset_val, prop_offset_y); Vec2f title_offset_val(x + prop_offset_val, prop_offset_y); saver.addCustomText(title_offset_key, "Right", context.propertyFontSize, data.propertyName.ptr()); saver.addCustomText(title_offset_val, "Left", context.propertyFontSize, data.propertyValue.ptr()); } } } } if (params.cnvOpt.comment.size() > 0) { Vec2f pos(b.max.x / 2, -title_y); saver.addText(pos, params.cnvOpt.comment.ptr()); } saver.endPage(); saver.endDocument(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_common.cpp��������������������������������������������������0000664�0000000�0000000�00000021053�12710376503�0022031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "render_common.h" using namespace indigo; namespace indigo { // cos(a) to cos(a/2) double cos2c (const double cs) { return sqrt((1 + cs)/2); } // cos(a) to sin(a/2) double sin2c (const double cs) { return sqrt((1 - cs)/2); } // cos(a) to tg(a/2) double tg2c (const double cs) { return sqrt((1 - cs) / (1 + cs)); } // cos(a) to ctg(a/2) double ctg2c (const double cs) { return sqrt((1 + cs) / (1 - cs)); } } RenderItem::RenderItem() { clear(); } void RenderItem::clear() { ritype = RIT_NULL; bbp.set(0, 0); bbsz.set(0, 0); relpos.set(0, 0); color = CWC_BASE; highlighted = false; noBondOffset = false; } void TextItem::clear() { RenderItem::clear(); text.clear(); } void GraphItem::clear() { RenderItem::clear(); } void RenderItemAttachmentPoint::clear() { RenderItem::clear(); number = -1; } void RenderItemBracket::clear() { p0.set(0,0); p1.set(0,0); q0.set(0,0); q1.set(0,0); d.set(0,0); n.set(0,0); length = 0; width = 0; } void RenderItemRSiteAttachmentIndex::clear() { RenderItem::clear(); number = -1; radius = 0; } AtomDesc::AtomDesc() { clear(); } void AtomDesc::clear () { showLabel = showHydro = true; tibegin = gibegin = -1; ticount = gicount = 0; attachmentPointBegin = -1; attachmentPointCount = 0; rSiteAttachmentIndexBegin = -1; rSiteAttachmentIndexCount = 0; stereoGroupType = stereoGroupNumber = -1; isRGroupAttachmentPoint = false; pseudoAtomStringVerbose = false; hcolorSet = false; fixed = false; exactChange = false; color = CWC_BASE; implicit_h = 0; hydroPos = HYDRO_POS_RIGHT; aam = -1; inversion = STEREO_UNMARKED; nearbyAtoms.clear(); list.clear(); pseudo.clear(); memset(implHPosWeights, 0, sizeof(implHPosWeights)); upperSin = lowerSin = rightSin = leftSin = 0; } Sgroup::Sgroup() { clear(); } void Sgroup::clear () { tibegin = gibegin = bibegin = -1; ticount = gicount = bicount = 0; } BondEnd::BondEnd () { clear(); } void BondEnd::clear () { lRing = next = -1; centered = false; prolong = false; lang = (float)(2 * M_PI); rang = (float)(2 * M_PI); rcos = lcos = -1; rsin = lsin = 0; rnei = lnei = -1; offset = 0; width = 0; } IMPL_ERROR(BondDescr, "molrender bond description"); BondDescr::BondDescr () { clear(); } void BondDescr::clear () { type = -1; queryType = -1; inRing = false; aromRing = false; stereoCare = false; thickness = 0.0f; stereodir = 0; cistrans = false; centered = false; extP = extN = 0; bahs = eahs = 0; tiTopology = -1; topology = 0; reactingCenter = RC_UNMARKED; } int BondDescr::getBondEnd (int aid) const { if (aid == beg) return be1; if (aid == end) return be2; throw Error("atom given is not adjacent to the bond"); } Ring::Ring () { clear(); } void Ring::clear () { bondEnds.clear(); angles.clear(); dblBondCount = 0; aromatic = true; center.set(0, 0); radius = 0; } MoleculeRenderData::MoleculeRenderData () { clear(); } void MoleculeRenderData::clear () { atoms.clear(); bonds.clear(); bondends.clear(); graphitems.clear(); rings.clear(); textitems.clear(); aam.clear(); reactingCenters.clear(); inversions.clear(); exactChanges.clear(); sgroups.clear(); } CP_DEF(RenderSettings); RenderSettings::RenderSettings () : CP_INIT, TL_CP_GET(bondDashAromatic), TL_CP_GET(bondDashAny), TL_CP_GET(bondDashSingleOrAromatic), TL_CP_GET(bondDashDoubleOrAromatic) { init(1.0f, 1.0f); } void RenderSettings::init (float sf, float lwf) { unit = sf / 30; bondLineWidth = lwf * unit; bondSpace = 2.5f * unit; fzz[FONT_SIZE_LABEL] = unit * 12; fzz[FONT_SIZE_ATTR] = unit * 8; fzz[FONT_SIZE_RGROUP_LOGIC] = unit * 12; fzz[FONT_SIZE_RGROUP_LOGIC_INDEX] = unit * 8; fzz[FONT_SIZE_INDICES] = unit * 6; fzz[FONT_SIZE_ATTACHMENT_POINT_INDEX] = unit * 6; fzz[FONT_SIZE_RSITE_ATTACHMENT_INDEX] = unit * 6; fzz[FONT_SIZE_COMMENT] = 0; // not used, value taken from RenderOptions.commentFontFactor fzz[FONT_SIZE_TITLE] = 0; // not used, value taken from RenderOptions.titleFontFactor fzz[FONT_SIZE_DATA_SGROUP] = unit * 8; upperIndexShift = -0.4f; lowerIndexShift = 0.4f; boundExtent = 1.3f * unit; labelInternalOffset = unit; stereoGroupLabelOffset = 2 * unit; radicalRightOffset = unit / 2; radicalRightVertShift = -0.2f; radicalTopOffset = 0.8f * unit; radicalTopDistDot = unit; radicalTopDistCap = unit / 2; dashUnit = unit; eps = 1e-4f; cosineTreshold = 0.98f; prolongAdjSinTreshold = 0.2f; stereoCareBoxSize = bondSpace * 3 + unit * 3; minBondLength = unit * 5; graphItemDotRadius = unit; graphItemCapSlope = 2; graphItemCapBase = 0.7f * unit; graphItemCapWidth = 1.2f * unit; graphItemDigitWidth = 4.5f * unit; graphItemDigitHeight = 6.5f * unit; graphItemSignLineWidth = 0.8f * unit; graphItemPlusEdge = (graphItemDigitWidth - graphItemSignLineWidth) / 2; const int dashDot[] = {5,2,1,2}; const int dash[] = {3,2}; bondDashSingleOrAromatic.clear(); bondDashDoubleOrAromatic.clear(); bondDashAny.clear(); bondDashAromatic.clear(); for (int i = 0; i < NELEM(dashDot); ++i) { double val = dashDot[i] * dashUnit; bondDashSingleOrAromatic.push(val); bondDashDoubleOrAromatic.push(val); } for (int i = 0; i < NELEM(dash); ++i) { double val = dash[i] * dashUnit; bondDashAny.push(val); bondDashAromatic.push(val); } layoutMarginHorizontal = 0.4f; layoutMarginVertical = 0.6f; plusSize = 0.5; metaLineWidth = 1.0 / 16; arrowLength = 3 * plusSize; arrowHeadWidth = plusSize / 2; arrowHeadSize = plusSize / 2; equalityInterval = plusSize / 2; rGroupIfThenInterval = unit * 4; neighboringLabelTolerance = 1.3f; minSin = 0.49f; neighboringAtomDistanceTresholdA = 0.8f; neighboringAtomDistanceTresholdB = 0.5f; } CanvasOptions::CanvasOptions () { clear(); } void CanvasOptions::clear () { width = height = -1; maxWidth = maxHeight = -1; xOffset = yOffset = 0; bondLength = -1; gridMarginX = gridMarginY = 0; marginX = marginY = 0; commentOffset = 0; commentPos = COMMENT_POS_BOTTOM; commentAlign.clear(); titleAlign.clear(); titleOffset = 0; gridColumnNumber = 1; comment.clear(); titleProp.clear(); titleProp.appendString("^NAME", true); } // // MultilineTextLayout // MultilineTextLayout::MultilineTextLayout () { clear(); } MultilineTextLayout::MultilineTextLayout (Alignment bbox, Alignment inbox) : bbox_alignment(bbox), inbox_alignment(inbox) { } float MultilineTextLayout::getRelativeOffset (Alignment alignment) { if (alignment == Center) return 0.5f; if (alignment == Left) return 0.0f; // alignment == Right return 1.0f; } float MultilineTextLayout::getBboxRelativeOffset () const { return getRelativeOffset(bbox_alignment); } float MultilineTextLayout::getInboxRelativeOffset () const { return getRelativeOffset(inbox_alignment); } void MultilineTextLayout::clear () { bbox_alignment = Center; inbox_alignment = Center; } float MultilineTextLayout::getAnchorPoint (float area_x, float area_width, float text_width) { float bbox_x = area_x + (area_width - text_width) * getBboxRelativeOffset(); return bbox_x + text_width * getInboxRelativeOffset(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_context.cpp�������������������������������������������������0000664�0000000�0000000�00000073344�12710376503�0022237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "reaction/reaction.h" #include "render_context.h" #include <limits.h> using namespace indigo; RenderContext::TextLock RenderContext::_tlock; IMPL_ERROR(RenderContext, "render context"); #ifdef _WIN32 #define NOMINMAX #include <windows.h> #include "cairo-win32.h" cairo_surface_t* RenderContext::createWin32PrintingSurfaceForHDC () { cairo_surface_t* surface = cairo_win32_printing_surface_create((HDC)opt.hdc); cairoCheckStatus(); return surface; } cairo_surface_t* RenderContext::createWin32Surface () { cairo_surface_t* surface = cairo_win32_surface_create((HDC)opt.hdc); cairoCheckStatus(); return surface; } static void _init_language_pack () { typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); gdi_init_lang_pack_func_t gdi_init_lang_pack; HMODULE module; if (GetModuleHandleA ("LPK.DLL")) return; module = GetModuleHandleA ("GDI32.DLL"); if (module) { gdi_init_lang_pack = (gdi_init_lang_pack_func_t) GetProcAddress (module, "GdiInitializeLanguagePack"); if (gdi_init_lang_pack) gdi_init_lang_pack (0); } } cairo_surface_t* RenderContext::createWin32PrintingSurfaceForMetafile (bool& isLarge) { HDC dc = GetDC(NULL); int hr = GetDeviceCaps(dc, HORZRES); int hs = GetDeviceCaps(dc, HORZSIZE); int vr = GetDeviceCaps(dc, VERTRES); int vs = GetDeviceCaps(dc, VERTSIZE); // physical display size in millimeters, divided over the resolution and // multiplied by 100, as metafile dimensions are specified in 0.01mm units float cfx = hs * 100.0f / hr; float cfy = vs * 100.0f / vr; // it may differ for x and y int w = (int)(_width * cfx); int h = (int)(_height * cfy); RECT rc = {0, 0, w, h}, crc; _init_language_pack(); GetClipBox(dc, &crc); isLarge = (_width >= crc.right || _height >= crc.bottom); _meta_hdc = CreateEnhMetaFileA(dc, 0, &rc, "Indigo Render2D\0\0"); ReleaseDC(NULL, dc); cairo_surface_t* s = cairo_win32_printing_surface_create((HDC)_meta_hdc); cairoCheckStatus(); StartPage((HDC)_meta_hdc); return s; } void RenderContext::storeAndDestroyMetafile (bool discard) { cairo_surface_show_page(_surface); cairoCheckStatus(); EndPage((HDC)_meta_hdc); cairo_surface_destroy(_surface); cairoCheckStatus(); _surface = NULL; HENHMETAFILE hemf = CloseEnhMetaFile((HDC)_meta_hdc); if (!discard) { int size = GetEnhMetaFileBits(hemf, 0, NULL); Array<char> buf; buf.resize(size); GetEnhMetaFileBits(hemf, size, (BYTE*)(buf.ptr())); opt.output->writeArray(buf); } DeleteEnhMetaFile(hemf); } #endif CP_DEF(RenderContext); RenderContext::RenderContext (const RenderOptions& ropt, float sf, float lwf): CP_INIT, TL_CP_GET(_fontfamily), TL_CP_GET(transforms), metafileFontsToCurves(false), _cr(NULL), _surface(NULL), _meta_hdc(NULL), opt(ropt), _pattern(NULL) { _settings.init(sf, lwf); bprintf(_fontfamily, "Arial"); bbmin.x = bbmin.y = 1; bbmax.x = bbmax.y = -1; _defaultScale = 0.0f; } void RenderContext::bbIncludePoint (const Vec2f& v) { double x = v.x, y = v.y; cairo_user_to_device(_cr, &x, &y); Vec2f u((float)x, (float)y); if (bbmin.x > bbmax.x) { // init bbmin.x = bbmax.x = u.x; bbmin.y = bbmax.y = u.y; } else { bbmin.min(u); bbmax.max(u); } } void RenderContext::_bbVecToUser (Vec2f& d, const Vec2f& s) { double x = s.x, y = s.y; cairo_device_to_user(_cr, &x, &y); d.set((float)x, (float)y); } void RenderContext::bbGetMin (Vec2f& v) { _bbVecToUser(v, bbmin); } void RenderContext::bbGetMax (Vec2f& v) { _bbVecToUser(v, bbmax); } void RenderContext::bbIncludePoint (double x, double y) { Vec2f v((float)x, (float)y); bbIncludePoint(v); } void RenderContext::bbIncludePath (bool stroke) { double x1, x2, y1, y2; if (stroke) cairo_stroke_extents(_cr, &x1, &y1, &x2, &y2); else cairo_path_extents(_cr, &x1, &y1, &x2, &y2); bbIncludePoint(x1, y1); bbIncludePoint(x2, y2); } void RenderContext::setDefaultScale (float scale) { _defaultScale = scale; } void RenderContext::setFontFamily (const char* ff) { bprintf(_fontfamily, "%s", ff); } int RenderContext::getMaxPageSize () const { if (opt.mode == MODE_PDF) return 14400; return INT_MAX; } cairo_status_t RenderContext::writer (void *closure, const unsigned char *data, unsigned int length) { try { ((Output*)closure)->write(data, length); } catch (Output::Error &) { return CAIRO_STATUS_WRITE_ERROR; } return CAIRO_STATUS_SUCCESS; } void RenderContext::createSurface(cairo_write_func_t writer, Output* output, int width, int height) { int mode = opt.mode; if (writer == NULL && (mode == MODE_HDC || mode == MODE_PRN)) mode = MODE_PDF; switch (mode) { case MODE_NONE: throw Error("mode not set"); case MODE_PDF: _surface = cairo_pdf_surface_create_for_stream(writer, opt.output, _width, _height); cairoCheckStatus(); break; case MODE_SVG: _surface = cairo_svg_surface_create_for_stream(writer, opt.output, _width, _height); cairoCheckStatus(); break; case MODE_PNG: _surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, _width, _height); cairoCheckStatus(); break; case MODE_HDC: #ifdef _WIN32 _surface = createWin32Surface(); #else throw Error("mode \"HDC\" is not supported on this platform"); #endif break; case MODE_PRN: #ifdef _WIN32 _surface = createWin32PrintingSurfaceForHDC(); #else throw Error("mode \"PRN\" is not supported on this platform"); #endif break; case MODE_EMF: #ifdef _WIN32 bool isLarge; _surface = createWin32PrintingSurfaceForMetafile(isLarge); if (isLarge) metafileFontsToCurves = true; #else throw Error("mode \"EMF\" is not supported on this platform"); #endif break; default: throw Error("unknown mode: %d", mode); } } void RenderContext::init() { fontsInit(); cairo_text_extents_t te; cairo_select_font_face(_cr, _fontfamily.ptr(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairoCheckStatus(); cairo_set_font_size(_cr, _settings.fzz[FONT_SIZE_ATTR]); cairoCheckStatus(); _tlock.lock(); cairo_text_extents(_cr, "N", &te); _tlock.unlock(); cairoCheckStatus(); cairo_set_antialias(_cr, CAIRO_ANTIALIAS_GRAY); cairoCheckStatus(); _currentLineWidth = _settings.bondLineWidth; } void RenderContext::fillBackground() { cairo_set_source_rgb(_cr, opt.backgroundColor.x, opt.backgroundColor.y, opt.backgroundColor.z); cairoCheckStatus(); cairo_paint(_cr); cairoCheckStatus(); } void RenderContext::initNullContext () { _width = 10; _height = 10; if (_surface != NULL || _cr != NULL) throw Error("context is already open (or invalid)"); createSurface(NULL, NULL, 1, 1); cairoCheckStatus(); _cr = cairo_create(_surface); scale(_defaultScale); } void RenderContext::initContext (int width, int height) { _width = width; _height = height; if (opt.mode != MODE_HDC && opt.mode != MODE_PRN && opt.output == NULL) throw Error("output not set"); if (_surface != NULL || _cr != NULL) throw Error("context is already open (or invalid)"); createSurface(writer, opt.output, _width, _height); _cr = cairo_create(_surface); if (opt.backgroundColor.x >= 0 && opt.backgroundColor.y >= 0 && opt.backgroundColor.z >= 0) fillBackground(); } void RenderContext::closeContext (bool discard) { if (_cr != NULL) { cairo_destroy (_cr); _cr = NULL; } switch (opt.mode) { case MODE_NONE: throw Error("mode not set"); case MODE_PNG: if (!discard) cairo_surface_write_to_png_stream(_surface, writer, opt.output); break; case MODE_PDF: case MODE_SVG: case MODE_HDC: case MODE_PRN: break; case MODE_EMF: #ifdef _WIN32 storeAndDestroyMetafile(discard); #endif break; default: throw Error("unknown mode: %d", opt.mode); } if (_surface != NULL) { cairo_surface_destroy (_surface); _surface = NULL; } bbmin.x = bbmin.y = 1; bbmax.x = bbmax.y = -1; fontsDispose(); } void RenderContext::translate (float dx, float dy) { cairo_translate(_cr, dx, dy); cairoCheckStatus(); } void RenderContext::scale (float s) { cairo_scale(_cr, s, s); cairoCheckStatus(); } void RenderContext::storeTransform () { cairo_matrix_t& t = transforms.push(); cairo_get_matrix(_cr, &t); cairoCheckStatus(); } void RenderContext::restoreTransform () { cairo_matrix_t& t = transforms.top(); cairo_set_matrix(_cr, &t); cairoCheckStatus(); } void RenderContext::removeStoredTransform () { transforms.pop(); } void RenderContext::resetTransform () { cairo_matrix_t t; cairo_matrix_init_identity(&t); cairo_set_matrix(_cr, &t); cairoCheckStatus(); } void RenderContext::setLineWidth (double width) { _currentLineWidth = (float)width; cairo_set_line_width(_cr, width); cairoCheckStatus(); } void RenderContext::drawRectangle (const Vec2f& p, const Vec2f& sz) { cairo_rectangle(_cr, p.x, p.y, sz.x, sz.y); cairoCheckStatus(); checkPathNonEmpty(); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawItemBackground (const RenderItem& item) { cairo_rectangle(_cr, item.bbp.x, item.bbp.y, item.bbsz.x, item.bbsz.y); cairoCheckStatus(); if (opt.backgroundColor.x >= 0 && opt.backgroundColor.y >= 0 && opt.backgroundColor.z >= 0) setSingleSource(opt.backgroundColor); else setSingleSource(CWC_WHITE); checkPathNonEmpty(); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawTextItemText (const TextItem& ti) { Vec3f color; if (ti.ritype == RenderItem::RIT_AAM) color.copy(opt.aamColor); else if (ti.ritype == RenderItem::RIT_DATASGROUP) color.copy(opt.dataGroupColor); else if (ti.ritype == RenderItem::RIT_COMMENT) color.copy(opt.commentColor); else if (ti.ritype == RenderItem::RIT_TITLE) color.copy(opt.titleColor); else { getColorVec(color, ti.color); if (ti.highlighted && opt.highlightColorEnable) color.copy(opt.highlightColor); } drawTextItemText (ti, color); } void RenderContext::drawTextItemText (const TextItem& ti, const Vec3f& color) { bool bold = ti.highlighted && opt.highlightThicknessEnable; drawTextItemText (ti, color, bold); } void RenderContext::drawTextItemText (const TextItem& ti, const Vec3f& color, bool bold) { fontsSetFont(_cr, ti.fontsize, bold); fontsDrawText(ti, color, bold); } void RenderContext::drawLine (const Vec2f& v0, const Vec2f& v1) { moveTo(v0); lineTo(v1); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::drawPoly (const Array<Vec2f>& v) { moveTo(v[0]); for (int i = 1; i < v.size(); ++i) lineTo(v[i]); lineTo(v[0]); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::checkPathNonEmpty () const { #ifdef DEBUG cairo_path_t* p = cairo_copy_path(_cr); cairoCheckStatus(); if (p->num_data == 0) throw Error("Empty path"); cairo_path_destroy(p); cairoCheckStatus(); #endif } void RenderContext::fillQuad (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3) { moveTo(v0); lineTo(v1); lineTo(v2); lineTo(v3); checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::fillHex (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4, const Vec2f& v5) { moveTo(v0); lineTo(v1); lineTo(v2); lineTo(v3); lineTo(v4); lineTo(v5); checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::fillQuadStripes (const Vec2f& v0r, const Vec2f& v0l, const Vec2f& v1r, const Vec2f& v1l, int cnt) { Vec2f r(v0r), dr; Vec2f l(v0l), dl; dr.diff(v1r, v0r); dr.scale(1.0f/cnt); dl.diff(v1l, v0l); dl.scale(1.0f/cnt); if (cnt < 3) cnt = 3; for (int i = 0; i < cnt; ++i) { r.add(dr); l.add(dl); moveTo(r); lineTo(l); } checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::fillPentagon (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3, const Vec2f& v4) { moveTo(v0); lineTo(v1); lineTo(v2); lineTo(v3); lineTo(v4); checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawQuad (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, const Vec2f& v3) { moveTo(v0); lineTo(v1); lineTo(v2); lineTo(v3); cairo_close_path(_cr); cairoCheckStatus(); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::drawTriangleZigzag (const Vec2f& v0, const Vec2f& v1, const Vec2f& v2, int cnt) { Vec2f r(v0), dr; Vec2f l(v0), dl; dr.diff(v1, v0); dr.scale(1.0f/cnt); dl.diff(v2, v0); dl.scale(1.0f/cnt); cairo_set_line_join(_cr, CAIRO_LINE_JOIN_MITER); cairoCheckStatus(); moveTo(v0); if (cnt < 3) cnt = 3; for (int i = 0; i < cnt; ++i) { r.add(dr); l.add(dl); if (i & 1) lineTo(l); else lineTo(r); } checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); cairo_set_line_join(_cr, CAIRO_LINE_JOIN_BEVEL); cairoCheckStatus(); } void RenderContext::drawCircle (const Vec2f& center, const float r) { cairo_new_path(_cr); cairo_arc(_cr, center.x, center.y, r, 0, 2 * PI); cairoCheckStatus(); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); cairo_new_path(_cr); } void RenderContext::fillCircle (const Vec2f& center, const float r) { cairo_arc(_cr, center.x, center.y, r, 0, 2 * PI); cairoCheckStatus(); checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawArc (const Vec2f& center, const float r, const float a0, const float a1) { cairo_new_path(_cr); cairoCheckStatus(); cairo_arc(_cr, center.x, center.y, r, a0, a1); cairoCheckStatus(); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::setFontSize (double fontSize) { cairo_set_font_size(_cr, fontSize); cairoCheckStatus(); } void RenderContext::setTextItemSize (TextItem& ti) { bool bold = ti.highlighted && opt.highlightThicknessEnable; fontsSetFont(_cr, ti.fontsize, bold); fontsGetTextExtents(_cr, ti.text.ptr(), ti.fontsize, ti.bbsz.x, ti.bbsz.y, ti.relpos.x, ti.relpos.y); } void RenderContext::setTextItemSize (TextItem& ti, const Vec2f& c) { setTextItemSize(ti); cairo_font_extents_t fe; cairo_font_extents (_cr, &fe); ti.bbp.x = c.x - ti.bbsz.x / 2; ti.bbp.y = c.y - ti.bbsz.y / 2; } void RenderContext::setGraphItemSizeDot (GraphItem& gi) { gi.type = GraphItem::DOT; gi.bbsz.set(2 * _settings.graphItemDotRadius, 2 * _settings.graphItemDotRadius); gi.relpos.set(_settings.graphItemDotRadius, _settings.graphItemDotRadius); } void RenderContext::setGraphItemSizeCap (GraphItem& gi) { gi.type = GraphItem::CAP; gi.bbsz.set(2 * _settings.graphItemCapWidth, _settings.graphItemCapWidth * _settings.graphItemCapSlope); gi.relpos.set(0, _settings.graphItemCapWidth * _settings.graphItemCapSlope); } void RenderContext::setGraphItemSizeSign (GraphItem& gi, GraphItem::TYPE type) { gi.type = type; gi.bbsz.set(_settings.graphItemDigitWidth, _settings.graphItemDigitHeight); gi.relpos.set(0, 0); } void RenderContext::drawAttachmentPoint (RenderItemAttachmentPoint& ri) { setSingleSource(ri.color); if (ri.highlighted && opt.highlightColorEnable) setSingleSource(opt.highlightColor); setLineWidth(_settings.unit); moveTo(ri.p0); lineTo(ri.p1); checkPathNonEmpty(); bbIncludePath(false); cairo_stroke(_cr); cairoCheckStatus(); Vec2f n; n.copy(ri.dir); n.rotateL(1, 0); Vec2f p, q, r; int waveCnt = 10; float waveLength = 0.5f; float waveWidth = waveLength / waveCnt; float slopeFactor = 0.2f; p.lineCombin(ri.p1, n, -0.5f * waveLength); moveTo(p); float step = waveLength / waveCnt; for (int i = 0; i < waveCnt; ++i) { int turn = ((i & 1) ? 1 : -1); q.lineCombin(p, ri.dir, waveWidth * turn); q.addScaled(n, waveWidth * slopeFactor); p.addScaled(n, step); r.lineCombin(p, ri.dir, waveWidth * turn); r.addScaled(n, -waveWidth * slopeFactor); cairo_curve_to(_cr, q.x, q.y, r.x, r.y, p.x, p.y); } checkPathNonEmpty(); bbIncludePath(false); cairo_stroke(_cr); cairoCheckStatus(); QS_DEF(TextItem, ti); ti.clear(); if (ri.number > 0) { bprintf(ti.text, "%d", ri.number); ti.fontsize = FONT_SIZE_ATTACHMENT_POINT_INDEX; setTextItemSize(ti, ri.p1); float sz = ti.bbsz.length(); ti.bbp.addScaled(n, -(sz/2 + _settings.unit)); ti.bbp.addScaled(ri.dir, -(sz/2 + waveWidth + _settings.unit)); drawTextItemText(ti); } } void RenderContext::drawRSiteAttachmentIndex (RenderItemRSiteAttachmentIndex& ri) { setSingleSource(ri.color); setLineWidth(_settings.unit/2); drawCircle(ri.bbp, ri.radius); } void RenderContext::drawGraphItem (GraphItem& gi) { setSingleSource(gi.color); if (gi.highlighted && opt.highlightColorEnable) setSingleSource(opt.highlightColor); _drawGraphItem(gi); } void RenderContext::drawGraphItem (GraphItem& gi, const Vec3f& color) { setSingleSource(color); _drawGraphItem(gi); } void RenderContext::_drawGraphItem (GraphItem& gi) { Vec2f v0; v0.sum(gi.bbp, gi.relpos); switch (gi.type) { case GraphItem::CAP: moveTo(v0); lineToRel(_settings.graphItemCapWidth, _settings.graphItemCapSlope * -_settings.graphItemCapWidth); lineToRel(_settings.graphItemCapWidth, _settings.graphItemCapSlope * _settings.graphItemCapWidth); lineToRel(-_settings.graphItemCapBase, 0); lineToRel(_settings.graphItemCapBase - _settings.graphItemCapWidth, _settings.graphItemCapSlope * (_settings.graphItemCapBase - _settings.graphItemCapWidth)); lineToRel(_settings.graphItemCapBase - _settings.graphItemCapWidth, _settings.graphItemCapSlope * (_settings.graphItemCapWidth - _settings.graphItemCapBase)); break; case GraphItem::DOT: moveTo(v0); cairo_arc(_cr, v0.x, v0.y, _settings.graphItemDotRadius, 0, 2 * M_PI); cairoCheckStatus(); break; case GraphItem::PLUS: moveTo(v0); moveToRel(0, (_settings.graphItemDigitHeight - _settings.graphItemSignLineWidth) / 2); lineToRel(_settings.graphItemPlusEdge, 0); lineToRel(0, -_settings.graphItemPlusEdge); lineToRel(_settings.graphItemSignLineWidth, 0); lineToRel(0, _settings.graphItemPlusEdge); lineToRel(_settings.graphItemPlusEdge, 0); lineToRel(0, _settings.graphItemSignLineWidth); lineToRel(-_settings.graphItemPlusEdge, 0); lineToRel(0, _settings.graphItemPlusEdge); lineToRel(-_settings.graphItemSignLineWidth, 0); lineToRel(0, -_settings.graphItemPlusEdge); lineToRel(-_settings.graphItemPlusEdge, 0); break; case GraphItem::MINUS: moveTo(v0); moveToRel(0, (_settings.graphItemDigitHeight - _settings.graphItemSignLineWidth) / 2); lineToRel(_settings.graphItemDigitWidth, 0); lineToRel(0, _settings.graphItemSignLineWidth); lineToRel(-_settings.graphItemDigitWidth, 0); break; } checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawBracket (RenderItemBracket& bracket) { setSingleSource(bracket.color); setLineWidth(_settings.unit); Vec2f p; moveTo(bracket.q0); lineTo(bracket.p0); lineTo(bracket.p1); lineTo(bracket.q1); checkPathNonEmpty(); bbIncludePath(false); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::fillRect (double x, double y, double w, double h) { cairo_rectangle(_cr, x, y, w, h); cairoCheckStatus(); checkPathNonEmpty(); cairo_fill(_cr); cairoCheckStatus(); } void RenderContext::drawEquality (const Vec2f& pos, const float linewidth, const float size, const float interval) { moveTo(pos); moveToRel(0, -interval/2); lineToRel(size, 0); moveTo(pos); moveToRel(0, interval/2); lineToRel(size, 0); setLineWidth(linewidth); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::drawPlus (const Vec2f& pos, const float linewidth, const float size) { float hsz = size / 2; moveTo(pos); moveToRel(-hsz, 0); lineToRel(2 * hsz, 0); moveToRel(-hsz, -hsz); lineToRel(0, 2 * hsz); setLineWidth(linewidth); checkPathNonEmpty(); bbIncludePath(true); cairo_stroke(_cr); cairoCheckStatus(); } void RenderContext::drawArrow (const Vec2f& p1, const Vec2f& p2, const float width, const float headwidth, const float headsize) { Vec2f d, n, p(p1); d.diff(p2, p1); float len = d.length(); d.normalize(); n.copy(d); n.rotate(1, 0); p.addScaled(n, width / 2); moveTo(p); p.addScaled(d, len - headsize); lineTo(p); p.addScaled(n, (headwidth - width) / 2); lineTo(p); p.addScaled(n, -headwidth / 2); p.addScaled(d, headsize); lineTo(p); p.addScaled(n, -headwidth / 2); p.addScaled(d, -headsize); lineTo(p); p.addScaled(n, (headwidth - width) / 2); lineTo(p); p.addScaled(d, -len + headsize); lineTo(p); checkPathNonEmpty(); bbIncludePath(false); cairo_fill(_cr); cairoCheckStatus(); } float RenderContext::highlightedBondLineWidth () const { return _settings.bondLineWidth * (opt.highlightThicknessEnable ? opt.highlightThicknessFactor : 1.0f); } float RenderContext::currentLineWidth () const { return _currentLineWidth; } void RenderContext::setHighlight() { if (opt.highlightColorEnable) setSingleSource(opt.highlightColor); if (opt.highlightThicknessEnable) setLineWidth(opt.highlightThicknessFactor * _settings.bondLineWidth); } void RenderContext::resetHighlightThickness() { setLineWidth(_settings.bondLineWidth); } void RenderContext::resetHighlight() { setSingleSource(CWC_BASE); resetHighlightThickness(); } void RenderContext::getColorVec (Vec3f& v, int color) { getColor(v.x, v.y, v.z, color); float y, ymax = 0.5f; if (color >= CWC_COUNT) { y = 0.299f * v.x + 0.587f * v.y + 0.114f * v.z; if (y > ymax) v.scale(ymax / y); } } void RenderContext::setSingleSource (int color) { Vec3f v; getColorVec(v, color); cairo_set_source_rgb(_cr, v.x, v.y, v.z); cairoCheckStatus(); } void RenderContext::setSingleSource (const Vec3f& color) { cairo_set_source_rgb(_cr, color.x, color.y, color.z); cairoCheckStatus(); } void RenderContext::setGradientSource (const Vec3f& color1, const Vec3f& color2, const Vec2f& pos1, const Vec2f& pos2) { if (_pattern != NULL) { throw new Error("Pattern already initialized"); } _pattern = cairo_pattern_create_linear (pos1.x, pos1.y, pos2.x, pos2.y); cairo_pattern_add_color_stop_rgb (_pattern, 0, color1.x, color1.y, color1.z); cairo_pattern_add_color_stop_rgb (_pattern, 1, color2.x, color2.y, color2.z); cairo_set_source (_cr, _pattern); cairoCheckStatus(); } void RenderContext::clearPattern () { if (_pattern != NULL) { cairo_pattern_destroy (_pattern); _pattern = NULL; cairoCheckStatus(); } } float RenderContext::_getDashedLineAlignmentOffset (float length) { float offset = 0; float delta = length - floorf(length / _settings.dashUnit); if (delta > 0.5) offset = 1-delta - _settings.eps * _settings.dashUnit; else offset = -delta - _settings.eps * _settings.dashUnit; return offset; } void RenderContext::setDash (const Array<double>& dash, float length) { cairo_set_dash(_cr, dash.ptr(), dash.size(), _getDashedLineAlignmentOffset(length)); cairoCheckStatus(); } void RenderContext::resetDash () { cairo_set_dash(_cr, NULL, 0, 0); cairoCheckStatus(); } void RenderContext::lineTo (const Vec2f& v) { cairo_line_to(_cr, v.x, v.y); cairoCheckStatus(); } void RenderContext::lineToRel (float x, float y) { cairo_rel_line_to(_cr, x, y); cairoCheckStatus(); } void RenderContext::lineToRel (const Vec2f& v) { cairo_rel_line_to(_cr, v.x, v.y); cairoCheckStatus(); } void RenderContext::moveTo (const Vec2f& v) { cairo_move_to(_cr, v.x, v.y); cairoCheckStatus(); } void RenderContext::moveToRel (float x, float y) { cairo_rel_move_to(_cr, x, y); cairoCheckStatus(); } void RenderContext::moveToRel (const Vec2f& v) { cairo_rel_move_to(_cr, v.x, v.y); cairoCheckStatus(); } int RenderContext::getElementColor (int label) { return label - ELEM_H + CWC_COUNT; } void RenderContext::getColor (float& r, float& g, float& b, int c) { static double colors[][3] = { {1.0f, 1.0f, 1.0f}, // WHITE {0.0f, 0.0f, 0.0f}, // BLACK {1.0f, 0.0f, 0.0f}, // RED {0.0f, 0.8f, 0.0f}, // GREEN {0.0f, 0.0f, 1.0f}, // BLUE {0.0f, 0.5f, 0.0f}, // DARKGREEN {0.0, 0.0, 0.0}, // ELEM_H {0.85, 1.0, 1.0}, // .... {0.80, 0.50, 1.0}, {0.76, 1.0, 0}, {1.0, 0.71, 0.71}, {0.0, 0.0, 0.0}, {0.19, 0.31, 0.97}, {1.0, 0.051, 0.051}, {0.56, 0.88, 0.31}, {0.70, 0.89, 0.96}, {0.67, 0.36, 0.95}, {0.54, 1.0, 0}, {0.75, 0.65, 0.65}, {0.94, 0.78, 0.63}, {1.0, 0.50, 0}, {0.85, 0.65, 0.10}, {0.12, 0.94, 0.12}, {0.50, 0.82, 0.89}, {0.56, 0.25, 0.83}, {0.24, 1.0, 0}, {0.90, 0.90, 0.90}, {0.75, 0.76, 0.78}, {0.65, 0.65, 0.67}, {0.54, 0.60, 0.78}, {0.61, 0.48, 0.78}, {0.88, 0.40, 0.20}, {0.94, 0.56, 0.63}, {0.31, 0.82, 0.31}, {0.78, 0.50, 0.20}, {0.49, 0.50, 0.69}, {0.76, 0.56, 0.56}, {0.40, 0.56, 0.56}, {0.74, 0.50, 0.89}, {1.0, 0.63, 0}, {0.65, 0.16, 0.16}, {0.36, 0.72, 0.82}, {0.44, 0.18, 0.69}, {0, 1.0, 0}, {0.58, 1.0, 1.0}, {0.58, 0.88, 0.88}, {0.45, 0.76, 0.79}, {0.33, 0.71, 0.71}, {0.23, 0.62, 0.62}, {0.14, 0.56, 0.56}, {0.039, 0.49, 0.55}, {0, 0.41, 0.52}, {0.75, 0.75, 0.75}, {1.0, 0.85, 0.56}, {0.65, 0.46, 0.45}, {0.40, 0.50, 0.50}, {0.62, 0.39, 0.71}, {0.83, 0.48, 0}, {0.58, 0, 0.58}, {0.26, 0.62, 0.69}, {0.34, 0.090, 0.56}, {0, 0.79, 0}, {0.44, 0.83, 1.0}, {1.0, 1.0, 0.78}, {0.85, 1.0, 0.78}, {0.78, 1.0, 0.78}, {0.64, 1.0, 0.78}, {0.56, 1.0, 0.78}, {0.38, 1.0, 0.78}, {0.27, 1.0, 0.78}, {0.19, 1.0, 0.78}, {0.12, 1.0, 0.78}, {0, 1.0, 0.61}, {0, 0.90, 0.46}, {0, 0.83, 0.32}, {0, 0.75, 0.22}, {0, 0.67, 0.14}, {0.30, 0.76, 1.0}, {0.30, 0.65, 1.0}, {0.13, 0.58, 0.84}, {0.15, 0.49, 0.67}, {0.15, 0.40, 0.59}, {0.090, 0.33, 0.53}, {0.82, 0.82, 0.88}, {1.0, 0.82, 0.14}, {0.72, 0.72, 0.82}, {0.65, 0.33, 0.30}, {0.34, 0.35, 0.38}, {0.62, 0.31, 0.71}, {0.67, 0.36, 0}, {0.46, 0.31, 0.27}, {0.26, 0.51, 0.59}, {0.26, 0, 0.40}, {0, 0.49, 0}, {0.44, 0.67, 0.98}, {0, 0.73, 1.0}, {0, 0.63, 1.0}, {0, 0.56, 1.0}, {0, 0.50, 1.0}, {0, 0.42, 1.0}, {0.33, 0.36, 0.95}, {0.47, 0.36, 0.89}, {0.54, 0.31, 0.89}, {0.63, 0.21, 0.83}, {0.70, 0.12, 0.83}, {0.70, 0.22, 0.83}, {0.60, 0.12, 0.83}, {0.50, 0.12, 0.83}, {0.70, 0.12, 0.63}, {0.70, 0.12, 0.83}, {0.70, 0.32, 0.83}, {0.60, 0.12, 0.83}, {0.70, 0.12, 0.43} }; if (c == CWC_BASE) { r = (float)opt.baseColor.x; g = (float)opt.baseColor.y; b = (float)opt.baseColor.z; return; } if (c < 0 || c >= NELEM(colors)) throw Error("unknown color: %d", c); r = (float)colors[c][0]; g = (float)colors[c][1]; b = (float)colors[c][2]; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_fonts.cpp���������������������������������������������������0000664�0000000�0000000�00000007526�12710376503�0021703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/output.h" #include "molecule/molecule.h" #include "reaction/reaction.h" #include "render_context.h" #ifdef _WIN32 #include <windows.h> #include <cairo-win32.h> #endif using namespace indigo; void RenderContext::cairoCheckStatus () const { #ifdef DEBUG cairo_status_t s; if (_cr) { s = cairo_status(_cr); if (s != CAIRO_STATUS_SUCCESS /*&& s <= CAIRO_STATUS_INVALID_WEIGHT*/) throw Error("Cairo error: %i -- %s\n", s, cairo_status_to_string(s)); } #endif } void RenderContext::fontsClear() { memset(_scaled_fonts, 0, FONT_SIZE_COUNT * 2 * sizeof(cairo_scaled_font_t*)); cairoFontFaceRegular = NULL; cairoFontFaceBold = NULL; fontOptions = NULL; cairo_matrix_init_identity(&fontCtm); cairoCheckStatus(); cairo_matrix_init_identity(&fontScale); cairoCheckStatus(); } void RenderContext::fontsInit() { fontOptions = cairo_font_options_create(); cairoCheckStatus(); cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_GRAY); cairoCheckStatus(); cairo_set_font_options(_cr, fontOptions); cairoCheckStatus(); } void RenderContext::fontsDispose() { for (int i = 0; i < FONT_SIZE_COUNT * 2; ++i) { if (_scaled_fonts[i] != NULL) { cairo_scaled_font_destroy(_scaled_fonts[i]); cairoCheckStatus(); } } if (cairoFontFaceRegular != NULL) { cairo_font_face_destroy(cairoFontFaceRegular); cairoCheckStatus(); } if (cairoFontFaceBold != NULL) { cairo_font_face_destroy(cairoFontFaceBold); cairoCheckStatus(); } if (fontOptions != NULL) { cairo_font_options_destroy(fontOptions); cairoCheckStatus(); } fontsClear(); } double RenderContext::fontGetSize(FONT_SIZE size) { if (size == FONT_SIZE_COMMENT) return opt.commentFontFactor; if (size == FONT_SIZE_TITLE) return opt.titleFontFactor; return _settings.fzz[size]; } void RenderContext::fontsSetFont(cairo_t* cr, FONT_SIZE size, bool bold) { cairo_select_font_face(cr, _fontfamily.ptr(), CAIRO_FONT_SLANT_NORMAL, bold ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL); cairoCheckStatus(); cairo_set_font_size(cr, fontGetSize(size)); cairoCheckStatus(); } void RenderContext::fontsGetTextExtents(cairo_t* cr, const char* text, int size, float& dx, float& dy, float& rx, float& ry) { cairo_text_extents_t te; _tlock.lock(); cairo_text_extents(cr, text, &te); _tlock.unlock(); cairoCheckStatus(); dx = (float)te.width; dy = (float)te.height; rx = (float)-te.x_bearing; ry = (float)-te.y_bearing; } void RenderContext::fontsDrawText(const TextItem& ti, const Vec3f& color, bool bold) { setSingleSource(color); moveTo(ti.bbp); cairo_matrix_t m; cairo_get_matrix(_cr, &m); float scale = (float)m.xx; double v = scale * fontGetSize(ti.fontsize); if (opt.mode != MODE_PDF && opt.mode != MODE_SVG && v < 1.5) { cairo_rectangle(_cr, ti.bbp.x + ti.bbsz.x / 4, ti.bbp.y + ti.bbsz.y / 4, ti.bbsz.x/2, ti.bbsz.y/2); bbIncludePath(false); cairo_set_line_width(_cr, _settings.unit / 2); cairo_stroke(_cr); return; } moveToRel(ti.relpos); _tlock.lock(); cairo_text_path(_cr, ti.text.ptr()); bbIncludePath(false); _tlock.unlock(); cairo_new_path(_cr); moveTo(ti.bbp); moveToRel(ti.relpos); if (metafileFontsToCurves) { // TODO: remove _tlock.lock(); cairo_text_path(_cr, ti.text.ptr()); _tlock.unlock(); cairoCheckStatus(); cairo_fill(_cr); cairoCheckStatus(); } else { _tlock.lock(); cairo_show_text(_cr, ti.text.ptr()); _tlock.unlock(); cairoCheckStatus(); } }��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_grid.cpp����������������������������������������������������0000664�0000000�0000000�00000020442�12710376503�0021467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/output.h" #include "base_cpp/reusable_obj_array.h" #include "layout/metalayout.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "render_context.h" #include "render_item.h" #include "render_item_factory.h" #include "render_grid.h" using namespace indigo; IMPL_ERROR(RenderGrid, "RenderGrid"); RenderGrid::RenderGrid (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) : Render(rc, factory, cnvOpt, bondLength, bondLengthSet), nColumns(cnvOpt.gridColumnNumber), comment(-1) {} RenderGrid::~RenderGrid() {} void RenderGrid::_drawComment () { if (comment < 0) return; _rc.storeTransform(); { float diff = (float)(_width - 2 * outerMargin.x - commentSize.x); _rc.translate(diff * _cnvOpt.commentAlign.getBboxRelativeOffset(), 0); _factory.getItem(comment).render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(0, commentSize.y); } void RenderGrid::draw () { _width = _cnvOpt.width; _height = _cnvOpt.height; _rc.fontsClear(); bool enableRefAtoms = refAtoms.size() > 0 && _factory.isItemMolecule(objs[0]); if (enableRefAtoms && refAtoms.size() != objs.size()) throw Error("Number of reference atoms should be same as the number of objects"); bool enableTitles = titles.size() > 0; if (enableTitles && titles.size() != objs.size()) throw Error("Number of titles should be same as the number of objects"); nRows = (objs.size() + nColumns - 1) / nColumns; commentSize.set(0,0); commentOffset = 0; if (comment >= 0) { _factory.getItem(comment).init(); _factory.getItem(comment).estimateSize(); commentSize.copy(_factory.getItem(comment).size); commentOffset = _cnvOpt.commentOffset; } maxsz.set(0,0); Vec2f refSizeLT, refSizeRB; Array<float> columnExtentLeft, columnExtentRight, rowExtentTop, rowExtentBottom; columnExtentLeft.clear_resize(nColumns); columnExtentRight.clear_resize(nColumns); columnExtentLeft.fill(0); columnExtentRight.fill(0); rowExtentTop.clear_resize(nRows); rowExtentBottom.clear_resize(nRows); rowExtentTop.fill(0); rowExtentBottom.fill(0); for (int i = 0; i < objs.size(); ++i) { if (enableRefAtoms) _factory.getItemMolecule(objs[i]).refAtom = refAtoms[i]; _factory.getItem(objs[i]).init(); _factory.getItem(objs[i]).setObjScale(_getObjScale(objs[i])); _factory.getItem(objs[i]).estimateSize(); if (enableRefAtoms) { const Vec2f& r = _factory.getItemMolecule(objs[i]).refAtomPos; Vec2f d; d.diff(_factory.getItemMolecule(objs[i]).size, r); refSizeLT.max(r); int col = i % nColumns; int row = i / nColumns; columnExtentLeft[col] = __max(columnExtentLeft[col], r.x); columnExtentRight[col] = __max(columnExtentRight[col], d.x); rowExtentTop[row] = __max(rowExtentTop[row], r.y); rowExtentBottom[row] = __max(rowExtentBottom[row], d.y); refSizeRB.max(d); } else { maxsz.max(_factory.getItem(objs[i]).size); } } if (enableRefAtoms) maxsz.sum(refSizeLT, refSizeRB); maxTitleSize.set(0,0); titleOffset = 0; if (enableTitles) { titleOffset = _cnvOpt.titleOffset; for (int i = 0; i < titles.size(); ++i) { _factory.getItem(titles[i]).init(); _factory.getItem(titles[i]).estimateSize(); maxTitleSize.max(_factory.getItem(titles[i]).size); } } outerMargin.x = (float)(minMarg + _cnvOpt.marginX); outerMargin.y = (float)(minMarg + _cnvOpt.marginY); _width = __min(_width, _getMaxWidth()); _height = __min(_height, _getMaxHeight()); scale = _getScale(_width, _height); if (_width < 1) _width = _getDefaultWidth(scale); if (_height < 1) _height = _getDefaultHeight(scale); _rc.initContext(_width, _height); cellsz.set(__max(maxsz.x * scale, maxTitleSize.x), maxsz.y * scale + maxTitleSize.y + titleOffset); clientArea.set(cellsz.x * nColumns + _cnvOpt.gridMarginX * (nColumns - 1), cellsz.y * nRows + _cnvOpt.gridMarginY * (nRows - 1)); _rc.init(); if (_cnvOpt.xOffset > 0 || _cnvOpt.yOffset > 0) _rc.translate((float)_cnvOpt.xOffset, (float)_cnvOpt.yOffset); _rc.translate(outerMargin.x, outerMargin.y); if (_cnvOpt.commentPos == COMMENT_POS_TOP) { _drawComment(); _rc.translate(0, (float)commentOffset); } _rc.storeTransform(); { _rc.translate((_width - clientArea.x) / 2 - outerMargin.x, (_height - commentSize.y - commentOffset - clientArea.y) / 2 - outerMargin.y); for (int i = 0; i < objs.size(); ++i) { _rc.storeTransform(); { int y = i / nColumns; int x = i % nColumns; Vec2f size(_factory.getItem(objs[i]).size); _rc.translate(x * (cellsz.x + _cnvOpt.gridMarginX), y * (cellsz.y + _cnvOpt.gridMarginY)); _rc.storeTransform(); { if (enableRefAtoms) { _rc.translate(0.5f * (cellsz.x - (columnExtentRight[x] + columnExtentLeft[x]) * scale), 0.5f * (maxsz.y - (rowExtentBottom[y]+ rowExtentTop[y])) * scale); const Vec2f r = _factory.getItemMolecule(objs[i]).refAtomPos; _rc.translate((columnExtentLeft[x] - r.x) * scale, (rowExtentTop[y] - r.y) * scale); } else { _rc.translate(0.5f * (cellsz.x - size.x * scale), 0.5f * (maxsz.y - size.y) * scale); } _rc.scale(scale); _factory.getItem(objs[i]).render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(0, maxsz.y * scale + titleOffset); if (enableTitles) { Vec2f titleSize(_factory.getItem(titles[i]).size); _rc.translate(_cnvOpt.titleAlign.getBboxRelativeOffset() * (cellsz.x - titleSize.x), 0.5f * (maxTitleSize.y - titleSize.y)); _factory.getItem(titles[i]).render(); } } _rc.restoreTransform(); _rc.removeStoredTransform(); } } _rc.restoreTransform(); _rc.removeStoredTransform(); if (_cnvOpt.commentPos == COMMENT_POS_BOTTOM) { _rc.translate(0, _height - commentOffset - commentSize.y - 2*outerMargin.y); _drawComment(); } } int RenderGrid::_getDefaultWidth (const float s) { return (int)ceil(__max(__max(maxsz.x * s, maxTitleSize.x) * nColumns + _cnvOpt.gridMarginX * (nColumns - 1), commentSize.x) + outerMargin.x * 2); } int RenderGrid::_getDefaultHeight (const float s) { return (int)ceil((maxsz.y * s + maxTitleSize.y + titleOffset) * nRows + _cnvOpt.gridMarginY * (nRows - 1) + outerMargin.y * 2 + commentSize.y + commentOffset); } float RenderGrid::_getScaleGivenSize(int w, int h) { float absX = _cnvOpt.gridMarginX * (nColumns - 1) + outerMargin.x * 2; float absY = (maxTitleSize.y + titleOffset) * nRows + _cnvOpt.gridMarginY * (nRows - 1) + outerMargin.y * 2 + commentSize.y + commentOffset; float x = w - absX, y = h - absY; if (x < maxTitleSize.x * nRows + 1 || w < commentSize.x + outerMargin.x * 2 + 1 || y < 1) throw Error("Image too small, the layout requires at least %dx%d", (int)__max(absX + maxTitleSize.x * nRows + 2,commentSize.x + outerMargin.x * 2 + 2), (int)(absY + 2)); Vec2f totalScaleableSize(maxsz.x * nColumns, maxsz.y * nRows); if (x * totalScaleableSize.y < y * totalScaleableSize.x) return x / totalScaleableSize.x; return y / totalScaleableSize.y; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_internal.cpp������������������������������������������������0000664�0000000�0000000�00000373070�12710376503�0022366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "render_context.h" #include "render_internal.h" using namespace indigo; #define BOND_STEREO_BOLD 10001 static bool ElementHygrodenOnLeft[] = { false, // filler false, //ELEM_H false, //ELEM_He false, //ELEM_Li false, //ELEM_Be false, //ELEM_B false, //ELEM_C false, //ELEM_N true, //ELEM_O true, //ELEM_F false, //ELEM_Ne false, //ELEM_Na false, //ELEM_Mg false, //ELEM_Al false, //ELEM_Si false, //ELEM_P true, //ELEM_S true, //ELEM_Cl false, //ELEM_Ar false, //ELEM_K false, //ELEM_Ca false, //ELEM_Sc false, //ELEM_Ti false, //ELEM_V false, //ELEM_Cr false, //ELEM_Mn false, //ELEM_Fe false, //ELEM_Co false, //ELEM_Ni false, //ELEM_Cu false, //ELEM_Zn false, //ELEM_Ga false, //ELEM_Ge false, //ELEM_As true, //ELEM_Se true, //ELEM_Br false, //ELEM_Kr false, //ELEM_Rb false, //ELEM_Sr false, //ELEM_Y false, //ELEM_Zr false, //ELEM_Nb false, //ELEM_Mo false, //ELEM_Tc false, //ELEM_Ru false, //ELEM_Rh false, //ELEM_Pd false, //ELEM_Ag false, //ELEM_Cd false, //ELEM_In false, //ELEM_Sn false, //ELEM_Sb false, //ELEM_Te true, //ELEM_I false, //ELEM_Xe false, //ELEM_Cs false, //ELEM_Ba false, //ELEM_La false, //ELEM_Ce false, //ELEM_Pr false, //ELEM_Nd false, //ELEM_Pm false, //ELEM_Sm false, //ELEM_Eu false, //ELEM_Gd false, //ELEM_Tb false, //ELEM_Dy false, //ELEM_Ho false, //ELEM_Er false, //ELEM_Tm false, //ELEM_Yb false, //ELEM_Lu false, //ELEM_Hf false, //ELEM_Ta false, //ELEM_W false, //ELEM_Re false, //ELEM_Os false, //ELEM_Ir false, //ELEM_Pt false, //ELEM_Au false, //ELEM_Hg false, //ELEM_Tl false, //ELEM_Pb false, //ELEM_Bi false, //ELEM_Po false, //ELEM_At false, //ELEM_Rn false, //ELEM_Fr false, //ELEM_Ra false, //ELEM_Ac false, //ELEM_Th false, //ELEM_Pa false, //ELEM_U false, //ELEM_Np false, //ELEM_Pu false, //ELEM_Am false, //ELEM_Cm false, //ELEM_Bk false, //ELEM_Cf false, //ELEM_Es false, //ELEM_Fm false, //ELEM_Md false, //ELEM_No false //ELEM_Lr }; static bool _isBondWide (const BondDescr& bd) { return bd.type == BOND_DOUBLE || bd.type == BOND_TRIPLE || bd.queryType == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC || bd.queryType == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC || bd.queryType == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE; } RenderOptions::RenderOptions () { clear(); } void RenderOptions::clear() { baseColor.set(0, 0, 0); backgroundColor.set(-1, -1, -1); highlightThicknessEnable = false; highlightThicknessFactor = 1.8f; highlightColorEnable = true; highlightColor.set(1, 0, 0); aamColor.set(0, 0, 0); commentFontFactor = 20; commentSpacing = 0.5; titleFontFactor = 20; titleSpacing = 0.5; labelMode = LABEL_MODE_TERMINAL_HETERO; highlightedLabelsVisible = false; boldBondDetection = true; implHVisible = true; commentColor.set(0,0,0); titleColor.set(0,0,0); dataGroupColor.set(0,0,0); mode = MODE_NONE; hdc = 0; output = NULL; showAtomIds = false; showBondIds = false; atomBondIdsFromOne = false; showBondEndIds = false; showNeighborArcs = false; showValences = true; atomColoring = false; stereoMode = STEREO_STYLE_OLD; showReactingCenterUnchanged = false; centerDoubleBondWhenStereoAdjacent = false; showCycles = false; agentsBelowArrow = true; collapseSuperatoms = false; atomColorProp.clear(); } IMPL_ERROR(MoleculeRenderInternal, "molecule render internal"); CP_DEF(MoleculeRenderInternal); MoleculeRenderInternal::MoleculeRenderInternal (const RenderOptions& opt, const RenderSettings& settings, RenderContext& cw) : _mol(NULL), _cw(cw), _settings(settings), _opt(opt), CP_INIT, TL_CP_GET(_data), TL_CP_GET(_atomMapping), TL_CP_GET(_atomMappingInv), TL_CP_GET(_bondMappingInv), isRFragment(false) { _data.clear(); _atomMapping.clear(); _atomMappingInv.clear(); _bondMappingInv.clear(); } void MoleculeRenderInternal::setMolecule (BaseMolecule* mol) { _mol = mol; _data.clear(); _atomMapping.clear(); if ((_opt.collapseSuperatoms && _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_SUP) > 0) || _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_MUL) > 0) { _prepareSGroups(); } int i; // data _data.atoms.clear(); _data.atoms.resize(_mol->vertexEnd()); for (i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i)) _ad(i).clear(); _data.bonds.clear(); _data.bonds.resize(_mol->edgeEnd()); for (i = _mol->edgeBegin(); i != _mol->edgeEnd(); i = _mol->edgeNext(i)) _bd(i).clear(); } void MoleculeRenderInternal::setIsRFragment (bool isRFragment) { this->isRFragment = isRFragment; } void MoleculeRenderInternal::setScaleFactor (const float scaleFactor, const Vec2f& min, const Vec2f& max) { _scale = scaleFactor; _min.copy(min); _max.copy(max); } void mapArray (Array<int>& dst, const Array<int>& src, const int* mapping) { for (int i = 0; i < src.size(); ++i) { int j = mapping == NULL ? i : mapping[i]; dst[j] = src[i]; } } void MoleculeRenderInternal::setReactionComponentProperties (const Array<int>* aam, const Array<int>* reactingCenters, const Array<int>* inversions) { if (aam != NULL) _data.aam.copy(*aam); if (reactingCenters != NULL) _data.reactingCenters.copy(*reactingCenters); if (inversions != NULL) _data.inversions.copy(*inversions); } void MoleculeRenderInternal::setQueryReactionComponentProperties (const Array<int>* exactChanges) { if (exactChanges != NULL) _data.exactChanges.copy(*exactChanges); } void MoleculeRenderInternal::render () { _initCoordinates(); _initBondData(); _initBondEndData(); _findNeighbors(); _initBoldStereoBonds(); _findRings(); _determineDoubleBondShift(); _determineStereoGroupsMode(); _initAtomData(); _initRGroups(); _findCenteredCase(); _prepareLabels(); _initDataSGroups(); _initSruGroups(); _initMulGroups(); _initSupGroups(); _extendRenderItems(); _findAnglesOverPi(); _calculateBondOffset(); _applyBondOffset(); _setBondCenter(); _renderLabels(); _renderBonds(); _renderRings(); _renderSGroups(); _renderBondIds(); _renderAtomIds(); _renderEmptyRFragment(); } BondEnd& MoleculeRenderInternal::_be (int beid) { return _data.bondends[beid]; } const BondEnd& MoleculeRenderInternal::_be (int beid) const { return _data.bondends[beid]; } BondDescr& MoleculeRenderInternal::_bd (int bid) { return _data.bonds[bid]; } const BondDescr& MoleculeRenderInternal::_bd (int bid) const { return _data.bonds[bid]; } AtomDesc& MoleculeRenderInternal::_ad (int aid) { return _data.atoms[aid]; } const AtomDesc& MoleculeRenderInternal::_ad (int aid) const { return _data.atoms[aid]; } int MoleculeRenderInternal::_getOpposite (int beid) const { int bid = _be(beid).bid; const BondDescr& bd = _bd(bid); if (bd.be1 == beid) return bd.be2; if (bd.be2 == beid) return bd.be1; throw Error("The bond end given is not adjacent to this bond"); } void MoleculeRenderInternal::_determineDoubleBondShift() { // determine double bond shift direction, if any // (the bond may not be double or appear to be centered later on - so far we don't care) for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr& bd = _bd(i); const BondEnd& be1 = _be(bd.be1); const BondEnd& be2 = _be(bd.be2); if (bd.inRing) if (be1.lRing < 0) bd.lineOnTheRight = true; else if (be2.lRing < 0) bd.lineOnTheRight = false; else { const Ring& r1 = _data.rings[be1.lRing]; const Ring& r2 = _data.rings[be2.lRing]; // compare the ratios of double bonds in the two rings bd.lineOnTheRight = r1.dblBondCount * r2.bondEnds.size() < r2.dblBondCount * r1.bondEnds.size(); } else { const BondEnd& bel1 = _be(be1.lnei); const BondEnd& ber1 = _be(be1.rnei); // right neighbor for the second bond end is on the left of the bond! const BondEnd& bel2 = _be(be2.rnei); const BondEnd& ber2 = _be(be2.lnei); // angles adjacent to the bond float al1 = be1.lang, ar1 = ber1.lang, al2 = bel2.lang, ar2 = be2.lang; int neiBalance = (al1 < M_PI ? 1 : 0) + (al2 < M_PI ? 1 : 0) + (ar1 < M_PI ? -1 : 0) + (ar2 < M_PI ? -1 : 0); if (neiBalance > 0) bd.lineOnTheRight = false; else if (neiBalance < 0) bd.lineOnTheRight = true; else { // compare the number of wide (double, triple, etc.) bonds on both sides int wideNeiBalance = (al1 < M_PI && _isBondWide(_bd(bel1.bid)) ? 1 : 0) + (al2 < M_PI && _isBondWide(_bd(bel2.bid)) ? 1 : 0) + (ar1 < M_PI && _isBondWide(_bd(ber1.bid)) ? -1 : 0) + (ar2 < M_PI && _isBondWide(_bd(ber2.bid)) ? -1 : 0); if (wideNeiBalance > 0) bd.lineOnTheRight = false; else if (wideNeiBalance < 0) bd.lineOnTheRight = true; else { // compare the number of wide (double, triple, etc.) bonds on both sides int stereoBalance = (al1 < M_PI && _bd(bel1.bid).stereodir != 0 ? 1 : 0) + (al2 < M_PI && _bd(bel2.bid).stereodir != 0 ? 1 : 0) + (ar1 < M_PI && _bd(ber1.bid).stereodir != 0 ? -1 : 0) + (ar2 < M_PI && _bd(ber2.bid).stereodir != 0 ? -1 : 0); if (stereoBalance > 0) bd.lineOnTheRight = true; else if (stereoBalance < 0) bd.lineOnTheRight = false; else bd.lineOnTheRight = (al1 + al2 < ar1 + ar2); } } } } } void MoleculeRenderInternal::_extendRenderItem (RenderItem& item, const float extent) { Vec2f exv(extent, extent); item.bbsz.addScaled(exv, 2); item.bbp.sub(exv); item.relpos.add(exv); } #define __minmax(inv, t1, t2) (inv ? __min(t1, t2) : __max(t1, t2)) bool MoleculeRenderInternal::_clipRaySegment (float& offset, const Vec2f& p, const Vec2f& d, const Vec2f& n0, const Vec2f& a, const Vec2f& b, const float w) { Vec2f ab, pa, pb; ab.diff(b, a); ab.normalize(); Vec2f n(n0); float dot = Vec2f::dot(ab, n); if (fabs(dot) < 1e-4) return 0; if (dot < 0) n.negate(); pa.diff(a, p); pb.diff(b, p); float ta = Vec2f::dot(pa, n); float tb = Vec2f::dot(pb, n); float tl = -w/2; float tr = w/2; float t = 0; bool f = false; bool inv = Vec2f::dot(ab, d) < 0; if (ta < tl + 1e-8 && tl < tb + 1e-8) { t = f ? __minmax(inv, t, tl) : tl; f = true; } if (ta < tr + 1e-8 && tr < tb + 1e-8) { t = f ? __minmax(inv, t, tr) : tr; f = true; } if (tl < ta + 1e-8 && ta < tr + 1e-8) { t = f ? __minmax(inv, t, ta) : ta; f = true; } if (tl < tb + 1e-8 && tb < tr + 1e-8) { t = f ? __minmax(inv, t, tb) : tb; f = true; } if (!f) return false; pa.addScaled(ab, (t - ta)/fabs(dot)); offset = Vec2f::dot(d, pa); return true; } bool MoleculeRenderInternal::_clipRayBox (float& offset, const Vec2f& p, const Vec2f& d, const Vec2f& rp, const Vec2f& sz, const float w) { Vec2f n(-d.y, d.x); Vec2f a, b; bool f = false; float t = 0, tt = 0; a.set(rp.x, rp.y); b.set(rp.x + sz.x, rp.y); if (_clipRaySegment(tt, p, d, n, a, b, w)) { f = true; t = __max(t, tt); } a.set(rp.x, rp.y); b.set(rp.x, rp.y + sz.y); if (_clipRaySegment(tt, p, d, n, a, b, w)) { f = true; t = __max(t, tt); } a.set(rp.x + sz.x, rp.y); b.set(rp.x + sz.x, rp.y + sz.y); if (_clipRaySegment(tt, p, d, n, a, b, w)) { f = true; t = __max(t, tt); } a.set(rp.x, rp.y + sz.y); b.set(rp.x + sz.x, rp.y + sz.y); if (_clipRaySegment(tt, p, d, n, a, b, w)) { f = true; t = __max(t, tt); } if (f) offset = t; return f; } const char* MoleculeRenderInternal::_getStereoGroupText (int type) { switch (type) { case MoleculeStereocenters::ATOM_ABS: return "abs"; case MoleculeStereocenters::ATOM_AND: return "and"; case MoleculeStereocenters::ATOM_OR: return "or"; case MoleculeStereocenters::ATOM_ANY: return "any"; default: throw Error("Unknown stereocenter type"); } } void MoleculeRenderInternal::_initRGroups() { if (_mol->attachmentPointCount() > 0) { for (int i = 1; i <= _mol->attachmentPointCount(); ++i) for (int j = 0, k; (k = _mol->getAttachmentPoint(i, j)) >= 0; ++j) _ad(k).isRGroupAttachmentPoint = true; } } int MoleculeRenderInternal::_parseColorString (Scanner& scanner, float& r, float& g, float& b) { if (!scanner.tryReadFloat(r)) return -1; scanner.skipSpace(); if (scanner.isEOF()) return -1; if (scanner.readChar() != ',') return -1; scanner.skipSpace(); if (!scanner.tryReadFloat(g)) return -1; scanner.skipSpace(); if (scanner.isEOF()) return -1; if (scanner.readChar() != ',') return -1; scanner.skipSpace(); if (!scanner.tryReadFloat(b)) return -1; return 1; } void MoleculeRenderInternal::_initDataSGroups() { BaseMolecule& bm = *_mol; const char* atomColorProp = _opt.atomColorProp.size() > 0 ? _opt.atomColorProp.ptr() : NULL; for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { const DataSGroup& group = (DataSGroup &)sgroup; if (atomColorProp != NULL && strcmp(atomColorProp, group.name.ptr()) == 0) { Vec3f color; BufferScanner scanner(group.data); if (_parseColorString(scanner, color.x, color.y, color.z) < 0) throw Error("Color value format invalid"); for (int j = 0; j < group.atoms.size(); ++j) { AtomDesc& ad = _ad(group.atoms[j]); if (ad.hcolorSet) throw Error("An atom belongs to more then one color group"); ad.hcolor.copy(color); ad.hcolorSet = true; } continue; } Sgroup& sg = _data.sgroups.push(); int tii = _pushTextItem(sg, RenderItem::RIT_DATASGROUP); TextItem& ti = _data.textitems[tii]; ti.text.copy(group.data); ti.text.push(0); ti.fontsize = FONT_SIZE_DATA_SGROUP; _cw.setTextItemSize(ti); const AtomDesc& ad = _ad(group.atoms[0]); if (!group.detached) { ti.bbp.copy(_ad(group.atoms[0]).pos); ti.bbp.x += ad.boundBoxMax.x + _settings.unit * 2; ti.bbp.y -= ti.bbsz.y/2; } else if (group.relative) { _objDistTransform(ti.bbp, group.display_pos); ti.bbp.add(_ad(group.atoms[0]).pos); } else { _objCoordTransform(ti.bbp, group.display_pos); } } } } void MoleculeRenderInternal::_loadBrackets(Sgroup& sg, const Array<Vec2f[2]>& coord, bool transformCoordinates) { for (int j = 0; j < coord.size(); ++j) { Vec2f a(coord[j][0]), b(coord[j][1]); int bracketId = _data.brackets.size(); if (j == 0) sg.bibegin = bracketId, sg.bicount = 1; else sg.bicount++; RenderItemBracket& bracket =_data.brackets.push(); bracket.p0.copy(a); bracket.p1.copy(b); if (transformCoordinates) { bracket.p0.set(a.x - _min.x, _max.y - a.y); bracket.p0.scale(_scale); bracket.p1.set(b.x - _min.x, _max.y - b.y); bracket.p1.scale(_scale); } bracket.d.diff(bracket.p1, bracket.p0); bracket.length = bracket.d.length(); bracket.d.normalize(); bracket.n.copy(bracket.d); bracket.n.rotateL(-1, 0); bracket.width = bracket.length * 0.15f; bracket.q0.lineCombin(bracket.p0, bracket.n, bracket.width); bracket.q1.lineCombin(bracket.p1, bracket.n, bracket.width); bracket.invertUpperLowerIndex = bracket.n.x > 0; } } void MoleculeRenderInternal::_loadBracketsAuto(const SGroup& group, Sgroup& sg) { if (group.brackets.size() == 0 || Vec2f::distSqr(group.brackets.at(0)[0], group.brackets.at(0)[1]) < EPSILON) _placeBrackets(sg, group.atoms); else _loadBrackets(sg, group.brackets, true); } void MoleculeRenderInternal::_positionIndex(Sgroup& sg, int ti, bool lower) { RenderItemBracket& bracket = _data.brackets[sg.bibegin + sg.bicount - 1]; TextItem& index = _data.textitems[ti]; if (bracket.invertUpperLowerIndex) lower = !lower; _cw.setTextItemSize(index, lower ? bracket.p1 : bracket.p0); float xShift = (fabs(index.bbsz.x * bracket.n.x) + fabs(index.bbsz.y * bracket.n.y)) / 2 + _settings.unit; float yShift = (fabs(index.bbsz.x * bracket.d.x) + fabs(index.bbsz.y * bracket.d.y)) / 2; index.bbp.addScaled(bracket.n, -xShift); index.bbp.addScaled(bracket.d, lower ? -yShift : yShift); } void MoleculeRenderInternal::_initSruGroups() { BaseMolecule& bm = *_mol; for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { const RepeatingUnit& group = (RepeatingUnit &)sgroup; Sgroup& sg = _data.sgroups.push(); _loadBracketsAuto(group, sg); int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); TextItem& index = _data.textitems[tiIndex]; index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, group.subscript.size() > 0 ? group.subscript.ptr() : "n"); _positionIndex(sg, tiIndex, true); if (group.connectivity != RepeatingUnit::HEAD_TO_TAIL) { int tiConn = _pushTextItem(sg, RenderItem::RIT_SGROUP); TextItem& conn = _data.textitems[tiConn]; conn.fontsize = FONT_SIZE_ATTR; if (group.connectivity == RepeatingUnit::HEAD_TO_HEAD) { bprintf(conn.text, "hh"); } else { bprintf(conn.text, "eu"); } _positionIndex(sg, tiConn, false); } } } } void MoleculeRenderInternal::_initMulGroups() { BaseMolecule& bm = *_mol; for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { const MultipleGroup& group = (MultipleGroup &)sgroup; Sgroup& sg = _data.sgroups.push(); _loadBracketsAuto(group, sg); int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); TextItem& index = _data.textitems[tiIndex]; index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, "%d", group.multiplier); _positionIndex(sg, tiIndex, true); } } } void MoleculeRenderInternal::_placeBrackets(Sgroup& sg, const Array<int>& atoms) { QS_DEF(Array<Vec2f[2]>, brackets); brackets.clear(); Vec2f min, max, a, b; for (int i = 0; i < atoms.size(); ++i) { int aid = atoms[i]; const AtomDesc& ad = _ad(aid); a.sum(ad.pos, ad.boundBoxMin); b.sum(ad.pos, ad.boundBoxMax); if (i == 0) { min.copy(a); max.copy(b); } else { min.min(a); max.max(b); } } float extent = _settings.unit * 3; min.sub(Vec2f(extent, extent)); max.add(Vec2f(extent, extent)); Vec2f* const & left = brackets.push(); left[0].set(min.x, max.y); left[1].set(min.x, min.y); Vec2f* const & right = brackets.push(); right[0].set(max.x, min.y); right[1].set(max.x, max.y); _loadBrackets(sg, brackets, false); } void MoleculeRenderInternal::_initSupGroups() { BaseMolecule& bm = *_mol; for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { const Superatom& group = (Superatom &)sgroup; Sgroup& sg = _data.sgroups.push(); _placeBrackets(sg, group.atoms); int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); TextItem& index = _data.textitems[tiIndex]; index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, "%s", group.subscript.ptr()); _positionIndex(sg, tiIndex, true); } } } void MoleculeRenderInternal::_prepareSGroups() { { BaseMolecule* newMol = NULL; BaseMolecule& bm1 = *_mol; if (bm1.isQueryMolecule()) newMol = new QueryMolecule(); else newMol = new Molecule(); newMol->clone(bm1, &_atomMapping, &_atomMappingInv); _bondMappingInv.clear(); for (int i = newMol->edgeBegin(); i < newMol->edgeEnd(); i = newMol->edgeNext(i)) _bondMappingInv.insert(i, BaseMolecule::findMappedEdge(*newMol, *_mol, i, _atomMappingInv.ptr())); _mol = newMol; } BaseMolecule& bm = *_mol; if (_opt.collapseSuperatoms) { for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { const Superatom& group = (Superatom &)sgroup; Vec3f centre; for (int i = 0; i < group.atoms.size(); ++i) { int aid = group.atoms[i]; centre.add(bm.getAtomXyz(aid)); } centre.scale(1.0f / group.atoms.size()); int said = -1; if (bm.isQueryMolecule()) { AutoPtr<QueryMolecule::Atom> atom; atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, group.subscript.ptr())); said = bm.asQueryMolecule().addAtom(atom.release()); } else { Molecule& mol = bm.asMolecule(); said = mol.addAtom(ELEM_PSEUDO); mol.setPseudoAtom(said, group.subscript.ptr()); } QS_DEF(RedBlackSet<int>, groupAtoms); groupAtoms.clear(); for (int j = 0; j < group.atoms.size(); ++j) { groupAtoms.insert(group.atoms[j]); } Vec3f pos; int posCnt = 0; while (group.atoms.size() > 0) { int aid = group.atoms[0]; const Vertex& v = bm.getVertex(aid); bool posCounted = false; for (int j = v.neiBegin(); j < v.neiEnd(); j = v.neiNext(j)) { int naid = v.neiVertex(j); if (!groupAtoms.find(naid)) { pos.add(bm.getAtomXyz(aid)); posCounted = true; posCnt++; int nbid = v.neiEdge(j), bid = -1; if (bm.findEdgeIndex(naid, said) < 0) { if (bm.isQueryMolecule()) { QueryMolecule& qm = bm.asQueryMolecule(); bid = qm.addBond(said, naid, qm.getBond(nbid).clone()); }else{ Molecule& mol = bm.asMolecule(); bid = mol.addBond(said, naid, mol.getBondOrder(nbid)); mol.setEdgeTopology(bid, mol.getBondTopology(nbid)); } if (_bondMappingInv.find(bid)) _bondMappingInv.remove(bid); _bondMappingInv.insert(bid, _bondMappingInv.at(nbid)); } } } bm.removeAtom(aid); } if (posCnt == 0) pos.copy(centre); else pos.scale(1.f / posCnt); bm.setAtomXyz(said, pos.x, pos.y, pos.z); } } } QS_DEF(BaseMolecule::Mapping, mapAtom); mapAtom.clear(); for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { SGroup &sgroup = bm.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { BaseMolecule::collapse(bm, i, mapAtom, _bondMappingInv); } } } int dblcmp (double a, double b, void* context) { return a > b ? 1 : (a < b ? -1 : 0); } struct Segment { int id; Vec2f p0, p1; int beg, end; int pos; }; struct Event { int id; Vec2f p; bool begin; }; int evcmp (const Event& a, const Event& b, void* context) { if (a.p.x > b.p.x) return 1; if (a.p.x < b.p.x) return -1; if (a.p.y > b.p.y) return 1; if (a.p.y < b.p.y) return -1; if (a.begin && !b.begin) return 1; if (!a.begin && b.begin) return -1; return 0; } float getFreeAngle (const ObjArray<Vec2f>& pp) { QS_DEF(Array<float>, angle); angle.clear(); int len = pp.size(); for (int j = 0; j < len; ++j) { Vec2f d; d.diff(pp[(j + 1) % len], pp[j]); angle.push(atan2f(d.y, d.x)); } angle.qsort(dblcmp, NULL); int j0 = -1; float maxAngle = -1; for (int j = 0; j < angle.size() - 1; ++j) { float a = angle[j + 1] - angle[j]; if (a > maxAngle) { maxAngle = a; j0 = j; } } return angle[j0] + maxAngle / 4; } int loopDist (int i, int j, int len) { if (i > j) { int t; __swap(i, j, t); } int d1 = j - i; int d2 = i + len - j; return __min(d1, d2); } class SegmentList : protected RedBlackSet<int> { public: SegmentList (ObjArray<Segment>& ss) : segments(ss) { xPos = 0; } // returns true if no intersection was detected upon this insertion bool insertSegment (double pos, int seg) { xPos = pos; if (find(seg)) return false; int curId = insert(seg); int nextId = next(curId); int prevId = nextPost(curId); Segment& segmentCurrent = segments[seg]; segmentCurrent.pos = curId; if (nextId < end()) { int nextSeg = key(nextId); if (loopDist(seg, nextSeg, segments.size()) > 1) { const Segment& segmentNext = segments[nextSeg]; bool intersectNext = Vec2f::segmentsIntersect(segmentCurrent.p0, segmentCurrent.p1, segmentNext.p0, segmentNext.p1); if (intersectNext) return false; } } if (prevId < end()) { int prevSeg = key(prevId); if (loopDist(seg, prevSeg, segments.size()) > 1) { const Segment& segmentPrev = segments[prevSeg]; bool intersectPrev = Vec2f::segmentsIntersect(segmentCurrent.p0, segmentCurrent.p1, segmentPrev.p0, segmentPrev.p1); if (intersectPrev) return false; } } return true; } void removeSegment (int segmentId) { _removeNode(segments[segmentId].pos); } double xPos; protected: virtual int _compare (int key, const Node &node) const { const Segment& a = segments[key]; const Segment& b = segments[node.key]; double ya = a.p0.y + (xPos - a.p0.x) * (a.p1.y - a.p0.y) / (a.p1.x - a.p0.x); double yb = b.p0.y + (xPos - b.p0.x) * (b.p1.y - b.p0.y) / (b.p1.x - b.p0.x); return ya > yb ? 1 : (ya < yb ? -1 : 0); } private: ObjArray<Segment>& segments; SegmentList (const SegmentList& other); }; float getMinDotProduct (const ObjArray<Vec2f>& pp, float tilt) { float minDot = 1.0; for (int j = 0; j < pp.size(); ++j) { Vec2f a, b, d; a.copy(pp[j]); b.copy(pp[(j + 1) % pp.size()]); a.rotate(tilt); b.rotate(tilt); d.diff(b, a); float dot = fabs(d.x / d.length()); if (dot < minDot) minDot = dot; } return minDot; } bool MoleculeRenderInternal::_ringHasSelfIntersectionsSimple(const Ring& ring) { for (int j = 0; j < ring.bondEnds.size(); ++j) { for (int k = j + 2; k < __min(ring.bondEnds.size(), ring.bondEnds.size() + j - 1); ++k) { const BondEnd& be1 = _be(ring.bondEnds[j]); const BondEnd& be2 = _be(ring.bondEnds[k]); const BondDescr& b1 = _bd(be1.bid); const BondDescr& b2 = _bd(be2.bid); const Vec2f& a00 = _ad(b1.beg).pos; const Vec2f& a01 = _ad(b1.end).pos; const Vec2f& a10 = _ad(b2.beg).pos; const Vec2f& a11 = _ad(b2.end).pos; if (Vec2f::segmentsIntersect(a00, a01, a10, a11)) return true; } } return false; } bool MoleculeRenderInternal::_ringHasSelfIntersections(const Ring& ring) { QS_DEF(ObjArray<Vec2f>, pp); pp.clear(); int len = ring.bondEnds.size(); for (int j = 0; j < len; ++j) { pp.push().copy(_ad(_be(ring.bondEnds[j]).aid).pos); } float tilt = getFreeAngle(pp) + (float)(M_PI / 2); QS_DEF(ObjArray<Event>, events); events.clear(); events.reserve(2 * len); QS_DEF(ObjArray<Segment>, segments); segments.clear(); segments.reserve(len); for (int j = 0; j < len; ++j) { Vec2f p1, p2; p1.copy(pp[j]); p2.copy(pp[(j + 1) % len]); p1.rotate(tilt); p2.rotate(tilt); bool revOrder = (p1.x > p2.x) || (p1.x == p2.x && p1.y > p2.y); Segment& segment = segments.push(); segment.id = j; segment.p0.copy(revOrder ? p2 : p1); segment.p1.copy(revOrder ? p1 : p2); Event& ev1 = events.push(); ev1.id = j; ev1.begin = true; ev1.p.copy(segment.p0); Event& ev2 = events.push(); ev2.id = j; ev2.begin = false; ev2.p.copy(segment.p1); } // order the events events.qsort(evcmp, NULL); // sweep line pass SegmentList sl(segments); for (int i = 0; i < events.size(); ++i) { Event& ev = events[i]; if (ev.begin) { if (!sl.insertSegment(ev.p.x + 1e-4, ev.id)) return true; // intersection detected } else { sl.removeSegment(ev.id); } } return false; } void MoleculeRenderInternal::_findRings() { QS_DEF(RedBlackSet<int>, mask); for (int i = 0; i < _data.bondends.size(); ++i) { BondEnd& be = _be(i); if (be.lRing != -1) continue; mask.clear(); int rid = _data.rings.size(); _data.rings.push(); Ring& ring = _data.rings[rid]; ring.bondEnds.push(i); int j = be.next; mask.insert(be.aid); for (int c = 0; j != i; j = _be(j).next, ++c) { if (c > _data.bondends.size() || j < 0 || _be(j).lRing != -1) break; int aid = _be(j).aid; if (mask.find(aid)) { while(ring.bondEnds.size() > 1 && _be(ring.bondEnds.top()).aid != aid) { _be(ring.bondEnds.top()).lRing = -2; ring.bondEnds.pop(); } ring.bondEnds.pop(); } else { mask.insert(aid); } ring.bondEnds.push(j); } if (i != j || ring.bondEnds.size() < 3) { for (int j = 0; j < ring.bondEnds.size(); ++j) _be(ring.bondEnds[j]).lRing = -2; _data.rings.pop(); continue; } bool selfIntersection = _ringHasSelfIntersections(ring); if (selfIntersection) { for (int j = 0; j < ring.bondEnds.size(); ++j) _be(ring.bondEnds[j]).lRing = -2; _data.rings.pop(); continue; } // for the inner loops, sum of the angles should be (n-2)*pi, // for the outer ones (n+2)*pi float angleSum = 0; for (int j = 0; j < ring.bondEnds.size(); ++j) { int j1 = (j + 1) % ring.bondEnds.size(); const Vec2f& da = _be(ring.bondEnds[j]).dir; const Vec2f& db = _be(ring.bondEnds[j1]).dir; float angle = (float)M_PI - atan2(-Vec2f::cross(da, db), Vec2f::dot(da, db)); angleSum += angle; } // sum of all angles for inner loop is (n - 2) Pi and (n + 2) Pi for the outer one bool inner = (angleSum < ring.bondEnds.size() * M_PI); if (!inner) { for (int j = 0; j < ring.bondEnds.size(); ++j) _be(ring.bondEnds[j]).lRing = -2; _data.rings.pop(); continue; } for (int j = 0; j < ring.bondEnds.size(); ++j) _be(ring.bondEnds[j]).lRing = rid; if (_opt.showCycles) { float cycleLineOffset = _settings.unit * 9; QS_DEF(Array<Vec2f>, vv); vv.clear(); for (int j = 0; j < ring.bondEnds.size() + 1; ++j) { const BondEnd& be = _be(ring.bondEnds[j % ring.bondEnds.size()]); Vec2f v = be.dir; v.rotateL(be.lang / 2); v.scale(cycleLineOffset); v.add(_ad(be.aid).pos); vv.push(v); } _cw.setSingleSource(CWC_BLUE); _cw.setLineWidth(_settings.unit); _cw.drawPoly(vv); } } for (int i = 0; i < _data.rings.size(); ++i) { Ring& ring = _data.rings[i]; Array<Vec2f> pp; for (int j = 0; j < ring.bondEnds.size(); ++j) pp.push().copy(_ad(_be(ring.bondEnds[j]).aid).pos); for (int j = 0; j < ring.bondEnds.size(); ++j) ring.center.add(pp[j]); ring.center.scale(1.0f / ring.bondEnds.size()); //{ // TextItem ti; // bprintf(ti.text, "%.2f", angleSum); // ti.color = CWC_BLUE; // ti.fontsize = _settings.labelFont; // _cw.setTextItemSize(ti, ring.center); // _cw.drawTextItemText(ti); //} float r = -1; for (int j = 0; j < ring.bondEnds.size(); ++j) { Vec2f df; BondEnd& be = _be(ring.bondEnds[j]); df.diff(pp[j], ring.center); float l = fabs(Vec2f::dot(df, be.lnorm)); if (r < 0 || l < r) r = l; ring.angles.push(atan2(df.y, df.x)); } ring.radius = r; ring.aromatic = true; int dblBondCount = 0; for (int i = 0; i < ring.bondEnds.size(); ++i) { int type = _bd(_be(ring.bondEnds[i]).bid).type; if (type != BOND_AROMATIC) ring.aromatic = false; if (type == BOND_DOUBLE) dblBondCount++; } ring.dblBondCount = dblBondCount; } for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr& bd = _bd(i); BondEnd& be1 = _be(bd.be1); BondEnd& be2 = _be(bd.be2); bd.inRing = (be1.lRing >= 0 || be2.lRing >= 0); bd.aromRing = ((be1.lRing >= 0) ? _data.rings[be1.lRing].aromatic : false) || ((be2.lRing >= 0) ? _data.rings[be2.lRing].aromatic : false); } } void MoleculeRenderInternal::_prepareLabels() { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) _prepareLabelText(i); } void MoleculeRenderInternal::_objCoordTransform(Vec2f& p, const Vec2f& v) const { p.set((v.x - _min.x) * _scale, (_max.y - v.y) * _scale); } void MoleculeRenderInternal::_objDistTransform(Vec2f& p, const Vec2f& v) const { p.set(v.x * _scale, -v.y * _scale); } void MoleculeRenderInternal::_initCoordinates() { Vec2f v; for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { Vec2f::projectZ(v, _mol->getAtomXyz(i)); _objCoordTransform(_ad(i).pos, v); } } void MoleculeRenderInternal::_determineStereoGroupsMode() { const MoleculeStereocenters& sc = _mol->stereocenters; _lopt.stereoMode = STEREOGROUPS_HIDE; if (_opt.stereoMode == STEREO_STYLE_NONE) return; bool allAbs = true, singleAndGroup = true, none = true; int andGid = -1; for (int i = sc.begin(); i < sc.end(); i = sc.next(i)) { int aid, type, gid, pyramid[4]; sc.get(i, aid, type, gid, pyramid); if (type != MoleculeStereocenters::ATOM_ANY) none = false; if (type != MoleculeStereocenters::ATOM_ABS) allAbs = false; if (type != MoleculeStereocenters::ATOM_AND || (andGid > 0 && andGid != gid)) singleAndGroup = false; else andGid = gid; if (!allAbs && !singleAndGroup) break; } if (_opt.stereoMode == STEREO_STYLE_OLD) { if (singleAndGroup) return; if (allAbs && !none) { TextItem& tiChiral = _data.textitems[_pushTextItem(RenderItem::RIT_CHIRAL, CWC_BASE, false)]; bprintf(tiChiral.text, "Chiral"); tiChiral.fontsize = FONT_SIZE_LABEL; _cw.setTextItemSize(tiChiral); tiChiral.bbp.set((_max.x - _min.x) * _scale - tiChiral.bbsz.x, -tiChiral.bbsz.y * 2); _cw.setSingleSource(CWC_BASE); _cw.drawTextItemText(tiChiral); return; } } _lopt.stereoMode = STEREOGROUPS_SHOW; int aid, type, groupId, pyramid[4]; for (int i = sc.begin(); i < sc.end(); i = sc.next(i)) { sc.get(i, aid, type, groupId, pyramid); AtomDesc& ad = _ad(aid); ad.stereoGroupType = type; if (type == MoleculeStereocenters::ATOM_AND || type == MoleculeStereocenters::ATOM_OR) ad.stereoGroupNumber = groupId; } } bool MoleculeRenderInternal::_isSingleHighlighted (int aid) { const Vertex& vertex = _mol->getVertex(aid); if (!_vertexIsHighlighted(aid)) return false; if (_opt.highlightedLabelsVisible) return true; for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) if (_edgeIsHighlighted(vertex.neiEdge(j))) return false; return true; } bool MoleculeRenderInternal::_vertexIsHighlighted (int aid) { return _mol->isAtomHighlighted(aid); } bool MoleculeRenderInternal::_edgeIsHighlighted (int bid) { return _mol->isBondHighlighted(bid); } bool MoleculeRenderInternal::_hasQueryModifiers (int aid) { bool hasConstraints = false; QUERY_MOL_BEGIN(_mol); QueryMolecule::Atom& qa = qmol.getAtom(aid); hasConstraints = qa.hasConstraint(QueryMolecule::ATOM_RING_BONDS) || qa.hasConstraint(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN) || qa.hasConstraint(QueryMolecule::ATOM_SUBSTITUENTS) || qa.hasConstraint(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN) || qa.hasConstraint(QueryMolecule::ATOM_UNSATURATION) || qa.hasConstraint(QueryMolecule::ATOM_TOTAL_H); QUERY_MOL_END; return hasConstraints || _ad(aid).fixed || _ad(aid).exactChange; } void MoleculeRenderInternal::_findNearbyAtoms () { float maxDistance = _settings.neighboringAtomDistanceTresholdA * 2; RedBlackObjMap<int, RedBlackObjMap<int, Array<int> > > buckets; for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const Vec2f& v = _ad(i).pos; int xBucket = (int)(v.x / maxDistance); int yBucket = (int)(v.y / maxDistance); RedBlackObjMap<int, Array<int> >& bucketRow = buckets.findOrInsert(yBucket); Array<int>& bucket = bucketRow.findOrInsert(xBucket); bucket.push(i); } for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const Vec2f& v = _ad(i).pos; int xBucket = (int)(v.x / maxDistance); int yBucket = (int)(v.y / maxDistance); for (int j = 0; j < 3; ++j) { for (int k = 0; k < 3; ++k) { int x = xBucket + j - 1; int y = yBucket + k - 1; if (!buckets.find(y)) continue; RedBlackObjMap<int, Array<int> >& bucketRow = buckets.at(y); if (!bucketRow.find(x)) continue; const Array<int>& bucket = bucketRow.at(x); for (int r = 0; r < bucket.size(); ++r) { int aid = bucket[r]; if (aid == i) continue; const Vec2f& v1 = _ad(aid).pos; if (Vec2f::dist(v, v1) < maxDistance) { _ad(i).nearbyAtoms.push(aid); } } } } //const Array<int>& natoms = _ad(i).nearbyAtoms; //printf("%02d:", i); //for (int j = 0; j < natoms.size(); ++j) { // printf(" %02d", natoms[j]); //} //printf("\n"); } //printf("\n"); } int _argMax (float* vv, int length) { int iMax = 0; for (int i = 1; i < length; ++i) { if (vv[iMax] < vv[i]) iMax = i; } return iMax; } float _sqr (float a) { return a * a; } void MoleculeRenderInternal::_initHydroPos (int aid) { AtomDesc& ad = _ad(aid); const Vertex& v = _mol->getVertex(aid); if (v.degree() == 0 && ElementHygrodenOnLeft[ad.label]) { ad.implHPosWeights[HYDRO_POS_RIGHT] = 0.2f; // weights are relative, absoute values don't matter ad.implHPosWeights[HYDRO_POS_LEFT] = 0.3f; } else { ad.implHPosWeights[HYDRO_POS_RIGHT] = 0.3f; ad.implHPosWeights[HYDRO_POS_LEFT] = 0.2f; } ad.implHPosWeights[HYDRO_POS_UP] = 0.1f; ad.implHPosWeights[HYDRO_POS_DOWN] = 0.0f; ad.implHPosWeights[HYDRO_POS_RIGHT] -= ad.rightSin > _settings.minSin ? ad.rightSin : 0; ad.implHPosWeights[HYDRO_POS_LEFT] -= ad.leftSin > _settings.minSin ? ad.leftSin : 0; ad.implHPosWeights[HYDRO_POS_UP] -= ad.upperSin > _settings.minSin ? ad.upperSin : 0; ad.implHPosWeights[HYDRO_POS_DOWN] -= ad.lowerSin > _settings.minSin ? ad.lowerSin : 0; } int MoleculeRenderInternal::_hydroPosFindConflict(int i) { const AtomDesc& ad = _ad(i); for (int j = 0; j < ad.nearbyAtoms.size(); ++j) { int aid = ad.nearbyAtoms[j]; Vec2f d; d.diff(_ad(aid).pos, ad.pos); HYDRO_POS orientation = d.x < d.y ? (d.x > -d.y ? HYDRO_POS_DOWN : HYDRO_POS_LEFT) : (d.x > -d.y ? HYDRO_POS_RIGHT : HYDRO_POS_UP); float aDist = __max(fabs(d.x), fabs(d.y)); float bDist = __min(fabs(d.x), fabs(d.y)); if (orientation == ad.hydroPos && bDist < _settings.neighboringAtomDistanceTresholdB && (aDist < _settings.neighboringAtomDistanceTresholdA || (aDist < _settings.neighboringAtomDistanceTresholdA * 2 && _ad(aid).hydroPos == 3 - ad.hydroPos))) return orientation; } return -1; } bool MoleculeRenderInternal::_hydroPosCorrectGreedy () { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { AtomDesc& ad = _ad(i); if (!ad.showLabel || ad.implicit_h <= 0) continue; int orientation = _hydroPosFindConflict(i); if (orientation >= 0) { ad.implHPosWeights[orientation] -= 1; ad.hydroPos = (HYDRO_POS)_argMax(ad.implHPosWeights,4); } } for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { AtomDesc& ad = _ad(i); if (!ad.showLabel || ad.implicit_h <= 0) continue; int orientation = _hydroPosFindConflict(i); if (orientation >= 0) return false; } return true; } void MoleculeRenderInternal::_hydroPosCorrectRepulse () { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { AtomDesc& ad = _ad(i); if (!ad.showLabel || ad.implicit_h <= 0) continue; _initHydroPos(i); for (int j = 0; j < ad.nearbyAtoms.size(); ++j) { int aid = ad.nearbyAtoms[j]; Vec2f d; d.diff(_ad(aid).pos, ad.pos); if (d.length() < _settings.neighboringLabelTolerance && _ad(aid).showLabel) { ad.implHPosWeights[d.x < d.y ? (d.x > -d.y ? HYDRO_POS_DOWN : HYDRO_POS_LEFT) : (d.x > -d.y ? HYDRO_POS_RIGHT : HYDRO_POS_UP)] -= 1; } } ad.hydroPos = (HYDRO_POS)_argMax(ad.implHPosWeights,4); } } void MoleculeRenderInternal::_initAtomData () { QUERY_MOL_BEGIN(_mol); for (int i = 0; i < qmol.fixed_atoms.size(); ++i) _ad(i).fixed = true; QUERY_MOL_END; for (int i = 0; i < _data.exactChanges.size(); ++i) if (_data.exactChanges[i]) _ad(i).exactChange = true; _findNearbyAtoms(); for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { AtomDesc& ad = _ad(i); BaseMolecule& bm = *_mol; const Vertex& vertex = bm.getVertex(i); //QS_DEF(Array<char>, buf); //buf.clear(); //bm.getAtomDescription(i, buf); //printf("%s\n", buf.ptr()); int atomNumber = bm.getAtomNumber(i); QUERY_MOL_BEGIN(_mol); if (!QueryMolecule::queryAtomIsRegular(qmol, i)) atomNumber = -1; QUERY_MOL_END; if (bm.isPseudoAtom(i)) { ad.type = AtomDesc::TYPE_PSEUDO; ad.pseudo.readString(bm.getPseudoAtom(i), true); } else if (bm.isTemplateAtom(i)) { ad.type = AtomDesc::TYPE_PSEUDO; ad.pseudo.readString(bm.getTemplateAtom(i), true); } else if (atomNumber < 0 || atomNumber == ELEM_RSITE) ad.type = AtomDesc::TYPE_QUERY; else ad.type = AtomDesc::TYPE_REGULAR; ad.label = -1; if (ad.type == AtomDesc::TYPE_REGULAR) ad.label = atomNumber; ad.queryLabel = -1; if (ad.type == AtomDesc::TYPE_QUERY) { if (!bm.isRSite(i)) { QUERY_MOL_BEGIN(_mol); ad.queryLabel = QueryMolecule::parseQueryAtom(qmol, i, ad.list); if (ad.queryLabel < 0) { bm.getAtomDescription(i, ad.pseudo); ad.type = AtomDesc::TYPE_PSEUDO; ad.pseudoAtomStringVerbose = true; } QUERY_MOL_END; } } if (_opt.atomColoring && ad.label > 0) ad.color = _cw.getElementColor(ad.label); Vec2f h(1, 0); bool hasBondOnRight = false, hasBondOnLeft = false; for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { Vec2f d(_ad(vertex.neiVertex(j)).pos); d.sub(ad.pos); d.normalize(); if (d.x > 0) ad.rightSin = __max(ad.rightSin, d.x); else ad.leftSin = __max(ad.leftSin, -d.x); if (d.y > 0) ad.lowerSin = __max(ad.lowerSin, d.y); else ad.upperSin = __max(ad.upperSin, -d.y); } if (ad.rightSin > 0.7) hasBondOnRight = true; if (ad.leftSin > 0.7) hasBondOnLeft = true; int charge = bm.getAtomCharge(i); int isotope = bm.getAtomIsotope(i); int radical = -1; int valence; bool query = bm.isQueryMolecule(); valence = bm.getExplicitValence(i); if (!bm.isRSite(i) && !bm.isPseudoAtom(i) && !bm.isTemplateAtom(i)) { radical = bm.getAtomRadical_NoThrow(i, -1); if (!bm.isQueryMolecule()) ad.implicit_h = bm.asMolecule().getImplicitH_NoThrow(i, 0); } bool plainCarbon = ad.label == ELEM_C && charge == (query ? CHARGE_UNKNOWN : 0) && isotope == (query ? -1 : 0) && radical <= 0 && valence == -1 && !_hasQueryModifiers(i); ad.showLabel = true; if (_opt.labelMode == LABEL_MODE_ALL || vertex.degree() == 0) ; else if (_opt.labelMode == LABEL_MODE_NONE) ad.showLabel = false; else if (plainCarbon && (_opt.labelMode == LABEL_MODE_HETERO || vertex.degree() > 1) && !_isSingleHighlighted(i)) { ad.showLabel = false; if (vertex.degree() == 2) { int k1 = vertex.neiBegin(); int k2 = vertex.neiNext(k1); if (_bd(vertex.neiEdge(k1)).type == _bd(vertex.neiEdge(k2)).type) { float dot = Vec2f::dot(_getBondEnd(i, k1).dir, _getBondEnd(i, k2).dir); if (dot < -0.97) ad.showLabel = true; } } } _initHydroPos(i); ad.hydroPos = (HYDRO_POS)_argMax(ad.implHPosWeights,4); int uaid = _atomMappingInv.size() > i ? _atomMappingInv[i] : i; if (_data.inversions.size() > uaid) { ad.inversion = _data.inversions[uaid]; } if (_data.aam.size() > uaid) { ad.aam = _data.aam[uaid]; } } if (!_hydroPosCorrectGreedy()) if (!_hydroPosCorrectGreedy()) _hydroPosCorrectRepulse(); } void MoleculeRenderInternal::_findAnglesOverPi () { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { int rightmost = -1, leftmost = -1; const Vertex& vertex = _mol->getVertex(i); if (vertex.degree() <= 1) continue; for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { int rbeidx = _getBondEndIdx(i, j); BondEnd& rbe = _be(rbeidx); if (rbe.lang > M_PI) { leftmost = rbe.lnei; rightmost = rbeidx; break; } } if (leftmost < 0) { for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { int rbeidx = _getBondEndIdx(i, j); BondEnd& rbe = _be(rbeidx); BondEnd& lbe = _be(rbe.lnei); if (_bd(rbe.bid).type == BOND_DOUBLE && _bd(lbe.bid).type == BOND_DOUBLE && rbe.lang < M_PI) { rightmost = rbeidx; leftmost = rbe.lnei; } } } if (leftmost < 0) { for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { BondDescr& bd = _bd(vertex.neiEdge(j)); if (bd.type == BOND_SINGLE && bd.stereodir == 0) continue; BondEnd& be = _getBondEnd(i, j); if (be.lsin > 0 && be.rsin > 0 && be.lang + be.rang > M_PI) { leftmost = be.lnei; rightmost = be.rnei; } } } if (leftmost < 0) continue; if (!_ad(i).showLabel) { BondEnd& rmbe = _be(rightmost); BondEnd& lmbe = _be(leftmost); float dot = Vec2f::dot(rmbe.dir, lmbe.dir); if (dot > 0 || 1 + dot < 1e-4) continue; float ahs = sqrt((1 + dot)/(1 - dot)); lmbe.offset = rmbe.offset = -ahs * __min(_bd(lmbe.bid).thickness, _bd(rmbe.bid).thickness) / 2; } } } void MoleculeRenderInternal::_renderBondIds () { // show bond ids if (_opt.showBondIds) { for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { TextItem ti; ti.fontsize = FONT_SIZE_INDICES; ti.color = CWC_DARKGREEN; int base = _opt.atomBondIdsFromOne ? 1 : 0; bprintf(ti.text, "%i", i + base); Vec2f v; v.sum(_be(_bd(i).be1).p, _be(_bd(i).be2).p); v.scale(0.5); _cw.setTextItemSize(ti, v); _extendRenderItem(ti, _settings.boundExtent); _cw.drawItemBackground(ti); _cw.drawTextItemText(ti); } } // show bond end ids if (_opt.showBondEndIds) { for (int i = 0; i < _data.bondends.size(); ++i) { TextItem ti; ti.fontsize = FONT_SIZE_INDICES; ti.color = CWC_RED; bprintf(ti.text, "%i", i); Vec2f v; v.lineCombin2(_be(i).p, 0.75, _be(_getOpposite(i)).p, 0.25); _cw.setTextItemSize(ti, v); _extendRenderItem(ti, _settings.boundExtent); _cw.drawItemBackground(ti); _cw.drawTextItemText(ti); } } if (_opt.showNeighborArcs) { for (int i = 0; i < _data.bondends.size(); ++i) { BondEnd& be = _be(i); BondEnd& be1 = _be(be.lnei); BondEnd& be2 = _be(be.rnei); float a0 = atan2(be.dir.y, be.dir.x) - 0.1f; float a1 = atan2(be1.dir.y, be1.dir.x) + 0.1f; _cw.setSingleSource(CWC_RED); _cw.drawArc(_ad(be.aid).pos, _settings.bondSpace * 3, a1, a0); float a2 = atan2(be2.dir.y, be2.dir.x) - 0.1f; float a3 = atan2(be.dir.y, be.dir.x) + 0.1f; _cw.setSingleSource(CWC_DARKGREEN); _cw.drawArc(_ad(be.aid).pos, _settings.bondSpace * 3 + _settings.unit, a3, a2); } } } void MoleculeRenderInternal::_renderAtomIds () { if (_opt.showAtomIds) { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const AtomDesc &desc = _ad(i); for (int i = 0; i < desc.ticount; ++i) { const TextItem &ti = _data.textitems[i + desc.tibegin]; if (ti.ritype == RenderItem::RIT_ATOMID) { _cw.drawItemBackground(ti); _cw.drawTextItemText(ti); } } } } } void MoleculeRenderInternal::_renderEmptyRFragment () { if (!isRFragment || _data.atoms.size() > 0) return; int attachmentPointBegin = _data.attachmentPoints.size(); // always 0 int attachmentPointCount = 2; Vec2f pos, dir1(1,0), dir2(-1,0); float offset = 0.4f; { RenderItemAttachmentPoint& attachmentPoint = _data.attachmentPoints.push(); attachmentPoint.dir.copy(dir1); attachmentPoint.p0.set(0,0); attachmentPoint.p1.lineCombin(pos, dir1, offset); attachmentPoint.color = CWC_BASE; attachmentPoint.highlighted = false; attachmentPoint.number = 1; } { RenderItemAttachmentPoint& attachmentPoint = _data.attachmentPoints.push(); attachmentPoint.dir.copy(dir2); attachmentPoint.p0.set(0,0); attachmentPoint.p1.lineCombin(pos, dir2, offset); attachmentPoint.color = CWC_BASE; attachmentPoint.highlighted = false; attachmentPoint.number = 2; } _cw.setSingleSource(CWC_BASE); for (int i = 0; i < attachmentPointCount; ++i) _cw.drawAttachmentPoint(_data.attachmentPoints[attachmentPointBegin + i]); } void MoleculeRenderInternal::_renderLabels () { for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) _drawAtom(_ad(i)); } void MoleculeRenderInternal::_renderRings () { for (int i = 0; i < _data.rings.size(); ++i) { const Ring& ring = _data.rings[i]; if (ring.aromatic) { float r = 0.75f * ring.radius; for (int k = 0; k < ring.bondEnds.size(); ++k) { BondEnd& be = _be(ring.bondEnds[k]); if (_edgeIsHighlighted(be.bid)) _cw.setHighlight(); float a0 = ring.angles[k], a1 = ring.angles[(k + 1) % ring.bondEnds.size()]; if (fabs(a1 - a0) > PI) _cw.drawArc(ring.center, r, __max(a0,a1), __min(a0,a1)); else _cw.drawArc(ring.center, r, __min(a0,a1), __max(a0,a1)); if (_edgeIsHighlighted(be.bid)) _cw.resetHighlight(); } } } } void MoleculeRenderInternal::_renderBonds () { for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) _drawBond(i); } void MoleculeRenderInternal::_renderSGroups () { for (int i = 0; i < _data.sgroups.size(); ++i) { const Sgroup& sg = _data.sgroups[i]; for (int j = 0; j < sg.ticount; ++j) _cw.drawTextItemText(_data.textitems[j + sg.tibegin]); for (int j = 0; j < sg.gicount; ++j) _cw.drawGraphItem(_data.graphitems[j + sg.gibegin]); for (int j = 0; j < sg.bicount; ++j) _cw.drawBracket(_data.brackets[j + sg.bibegin]); } } void MoleculeRenderInternal::_applyBondOffset () { for (int i = 0; i < _data.bondends.size(); ++i) { BondEnd& be = _be(i); be.p.addScaled(be.dir, be.offset); } } void MoleculeRenderInternal::_setBondCenter () { // find bond center for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr& bd = _bd(i); bd.center.lineCombin2(_be(bd.be1).p, 0.5f, _be(bd.be2).p, 0.5f); } } void MoleculeRenderInternal::_findNeighbors () { // prepare bond end data for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const Vertex& vertex = _mol->getVertex(i); for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { int be1idx = _getBondEndIdx(i, j); BondEnd& be1 = _be(be1idx); if (vertex.degree() > 1) { // find right and left neighbor of each bond end for (int k = vertex.neiBegin(); k < vertex.neiEnd(); k = vertex.neiNext(k)) { if (k == j) continue; int be2idx = _getBondEndIdx(i, k); BondEnd& be2 = _be(be2idx); float dot = Vec2f::dot(be1.dir, be2.dir); float cross = -Vec2f::cross(be1.dir, be2.dir); float angle = atan2(cross, dot); if (angle < 0) angle += 2 * (float)M_PI; float rAngle = (2 * (float)M_PI - angle); if (be1.lnei < 0 || angle < be1.lang) { be1.lnei = be2idx; be1.lsin = cross; be1.lcos = dot; be1.lang = angle; } if (be1.rnei < 0 || rAngle < be1.rang) { be1.rnei = be2idx; be1.rsin = sin(rAngle); be1.rcos = cos(rAngle); be1.rang = rAngle; } } int prev = _getOpposite(be1.lnei); _be(prev).next = be1idx; } else if (vertex.degree() == 1) { int prev = _getOpposite(be1idx); _be(prev).next = be1idx; be1.lnei = be1.rnei = be1idx; be1.lsin = be1.rsin = 0; be1.lcos = be1.rcos = 1; be1.lang = be1.rang = (float)(2 * M_PI); } } } } void MoleculeRenderInternal::_findCenteredCase () { // bonds with both labels visible for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondEnd& be1 = _be(_bd(i).be1); BondEnd& be2 = _be(_bd(i).be2); if (_ad(be1.aid).showLabel && _ad(be2.aid).showLabel) be1.centered = be2.centered = true; } // other cases for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const Vertex& vertex = _mol->getVertex(i); const AtomDesc& ad = _ad(i); for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { if (_bd(vertex.neiEdge(j)).inRing) continue; BondEnd& be = _getBondEnd(i, j); if (ad.showLabel) { be.centered = true; continue; // prolongation is not required when label is shown } if (be.lnei == be.rnei) // zero or one neighbor continue; if (be.lsin < _settings.prolongAdjSinTreshold || be.rsin < _settings.prolongAdjSinTreshold) // angle should not be to large or too small continue; const BondDescr& bdl = _bd(_be(be.lnei).bid); const BondDescr& bdr = _bd(_be(be.rnei).bid); if (((bdl.type == BOND_SINGLE && (bdl.stereodir == 0 || _opt.centerDoubleBondWhenStereoAdjacent)) && (bdr.type == BOND_SINGLE && (bdr.stereodir == 0 || _opt.centerDoubleBondWhenStereoAdjacent))) || (bdr.type == BOND_SINGLE && bdl.type == BOND_SINGLE && bdl.stereodir != 0 && bdr.stereodir != 0)) { be.centered = true; be.prolong = true; } } } for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr& bd = _bd(i); bd.centered = _be(bd.be1).centered && _be(bd.be2).centered; } } float MoleculeRenderInternal::_getBondOffset (int aid, const Vec2f& pos, const Vec2f& dir, const float bondWidth) { if (!_ad(aid).showLabel) return -1; float maxOffset = 0, offset = 0; for (int k = 0; k < _ad(aid).ticount; ++k) { TextItem& item = _data.textitems[_ad(aid).tibegin + k]; if (item.noBondOffset) continue; if (_clipRayBox(offset, pos, dir, item.bbp, item.bbsz, bondWidth)) maxOffset = __max(maxOffset, offset); } for (int k = 0; k < _ad(aid).gicount; ++k) { GraphItem& item = _data.graphitems[_ad(aid).gibegin + k]; if (item.noBondOffset) continue; if (_clipRayBox(offset, pos, dir, item.bbp, item.bbsz, bondWidth)) maxOffset = __max(maxOffset, offset); } return maxOffset + _settings.unit * 2; } void MoleculeRenderInternal::_calculateBondOffset () { // calculate offset for bonds for (int i = _mol->vertexBegin(); i < _mol->vertexEnd(); i = _mol->vertexNext(i)) { const Vertex& vertex = _mol->getVertex(i); for (int j = vertex.neiBegin(); j < vertex.neiEnd(); j = vertex.neiNext(j)) { BondEnd& be1 = _getBondEnd(i, j); be1.offset = __max(be1.offset, _getBondOffset(i, be1.p, be1.dir, be1.width)); } } for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { const BondDescr& bd = _bd(i); BondEnd& be1 = _be(bd.be1); BondEnd& be2 = _be(bd.be2); // if the offsets may result in too short bond, float offsum = be1.offset + be2.offset; if (offsum > 1e-3f && bd.length < offsum + _settings.minBondLength) { // if possible, scale down the offsets so that the bond has at least the specified length if (bd.length > _settings.minBondLength) { float factor = (bd.length - _settings.minBondLength) / offsum; be1.offset *= factor; be2.offset *= factor; } // otherwise, ignore offsets (extreme case) else { be1.offset = be2.offset = 0; } } } } void MoleculeRenderInternal::_initBondData () { float thicknessHighlighted = _cw.highlightedBondLineWidth(); for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr &d = _bd(i); d.type = _mol->getBondOrder(i); d.thickness = _edgeIsHighlighted(i) ? thicknessHighlighted : _settings.bondLineWidth; d.queryType = -1; QUERY_MOL_BEGIN(_mol); { QueryMolecule::Bond& qb = qmol.getBond(i); d.queryType = QueryMolecule::getQueryBondType(qb); d.stereoCare = qmol.bondStereoCare(i); if (qb.hasConstraint(QueryMolecule::BOND_TOPOLOGY)) { bool chainPossible = qb.possibleValue(QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_CHAIN); bool ringPossible = qb.possibleValue(QueryMolecule::BOND_TOPOLOGY, TOPOLOGY_RING); d.topology = 0; if (chainPossible && !ringPossible) { d.topology = TOPOLOGY_CHAIN; } if (ringPossible && !chainPossible) { d.topology = TOPOLOGY_RING; } } } QUERY_MOL_END; const Edge& edge = _mol->getEdge(i); d.beg = edge.beg; d.end = edge.end; d.vb = _ad(d.beg).pos; d.ve = _ad(d.end).pos; d.dir.diff(d.ve, d.vb); d.length = d.dir.length(); d.dir.normalize(); d.norm.set(-d.dir.y, d.dir.x); d.isShort = d.length < (_settings.bondSpace + _settings.bondLineWidth) * 2; // TODO: check d.stereodir = _mol->getBondDirection(i); d.cistrans = _mol->cis_trans.isIgnored(i); int ubid = _bondMappingInv.size() > i ? _bondMappingInv.at(i) : i; if (_data.reactingCenters.size() > ubid) d.reactingCenter = _data.reactingCenters[ubid]; } } void MoleculeRenderInternal::_initBoldStereoBonds () { if (!_opt.boldBondDetection) return; for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { BondDescr &d = _bd(i); const Vertex& v1 = _mol->getVertex(d.beg); const Vertex& v2 = _mol->getVertex(d.end); bool hasNeighboringUpBond1 = false; for (int j = v1.neiBegin(); j < v1.neiEnd(); j = v1.neiNext(j)) if (v1.neiEdge(j) != i && _bd(v1.neiEdge(j)).stereodir == BOND_UP && _bd(v1.neiEdge(j)).end == d.beg) hasNeighboringUpBond1 = true; bool hasNeighboringUpBond2 = false; for (int j = v2.neiBegin(); j < v2.neiEnd(); j = v2.neiNext(j)) if (v2.neiEdge(j) != i && _bd(v2.neiEdge(j)).stereodir == BOND_UP && _bd(v2.neiEdge(j)).end == d.end) hasNeighboringUpBond2 = true; if (hasNeighboringUpBond1 && hasNeighboringUpBond2) d.stereodir = BOND_STEREO_BOLD; } } void MoleculeRenderInternal::_initBondEndData () { for (int i = _mol->edgeBegin(); i < _mol->edgeEnd(); i = _mol->edgeNext(i)) { const Edge& edge = _mol->getEdge(i); const AtomDesc& bdesc = _ad(edge.beg); const AtomDesc& edesc = _ad(edge.end); BondDescr& bondd = _bd(i); bondd.be1 = _data.bondends.size(); _data.bondends.push(); bondd.be2 = _data.bondends.size(); _data.bondends.push(); BondEnd& be1 = _be(bondd.be1); BondEnd& be2 = _be(bondd.be2); be1.clear(); be2.clear(); be1.bid = be2.bid = i; be1.aid = edge.beg; be1.dir.diff(edesc.pos, bdesc.pos); be1.dir.normalize(); be1.lnorm.copy(be1.dir); be1.lnorm.rotate(1, 0); be1.p.copy(bdesc.pos); be2.aid = edge.end; be2.dir.negation(be1.dir); be2.lnorm.negation(be1.lnorm); be2.p.copy(edesc.pos); } for (int i = 0; i < _data.bondends.size(); ++i) { BondEnd& be = _be(i); const BondDescr& bd = _bd(be.bid); if (bd.type == BOND_SINGLE) if (bd.stereodir != 0) if (bd.be1 == i) // is source end? be.width = 0; else be.width = 2 * (_settings.bondSpace + _settings.bondLineWidth); else be.width = 2 * _settings.bondSpace + _settings.bondLineWidth; else if (bd.type == BOND_DOUBLE || bd.type == BOND_AROMATIC || bd.type == BOND_TRIPLE || bd.queryType >= 0) be.width = 4 * _settings.bondSpace + _settings.bondLineWidth; else { Array<char> buf; _mol->getBondDescription(be.bid, buf); throw Error("Unknown bond type %s. Can not determine bond width.", buf.ptr()); } } } void MoleculeRenderInternal::_extendRenderItems () { for (int i = 0; i < _data.textitems.size(); ++i) _extendRenderItem(_data.textitems[i], _settings.boundExtent); for (int i = 0; i < _data.graphitems.size(); ++i) _extendRenderItem(_data.graphitems[i], _settings.boundExtent); } BondEnd& MoleculeRenderInternal::_getBondEnd (int aid, int nei) { return _be(_getBondEndIdx(aid, nei)); } int MoleculeRenderInternal::_getBondEndIdx (int aid, int nei) { int ne = _mol->getVertex(aid).neiEdge(nei); int be = _bd(ne).getBondEnd(aid); return be; } void MoleculeRenderInternal::_drawAtom (const AtomDesc& desc) { #ifdef RENDER_SHOW_BACKGROUND for (int i = 0; i < desc.ticount; ++i) _cw.drawItemBackground(_data.textitems[i + desc.tibegin]); for (int i = 0; i < desc.gicount; ++i) _cw.drawItemBackground(_data.graphitems[i + desc.gibegin]); #endif _cw.setSingleSource(desc.color); for (int i = 0; i < desc.ticount; ++i) { if (desc.hcolorSet) _cw.drawTextItemText(_data.textitems[i + desc.tibegin], desc.hcolor); else _cw.drawTextItemText(_data.textitems[i + desc.tibegin]); } for (int i = 0; i < desc.attachmentPointCount; ++i) _cw.drawAttachmentPoint(_data.attachmentPoints[desc.attachmentPointBegin + i]); for (int i = 0; i < desc.rSiteAttachmentIndexCount; ++i) _cw.drawRSiteAttachmentIndex(_data.rSiteAttachmentIndices[desc.rSiteAttachmentIndexBegin + i]); for (int i = 0; i < desc.gicount; ++i) { if (desc.hcolorSet) _cw.drawGraphItem(_data.graphitems[i + desc.gibegin], desc.hcolor); else _cw.drawGraphItem(_data.graphitems[i + desc.gibegin]); } } void MoleculeRenderInternal::_writeQueryAtomToString (Output& output, int aid) { BaseMolecule& bm = *_mol; AtomDesc& ad = _ad(aid); if (bm.isRSite(aid)) { QS_DEF(Array<int>, rg); bm.getAllowedRGroups(aid, rg); if (rg.size() == 0) output.printf("R"); else for (int i = 0; i < rg.size(); ++i) { if (i > 0) output.printf(","); output.printf("R%i", rg[i]); } } else { if (!bm.isQueryMolecule()) throw Error("Atom type %d not supported in non-queries", ad.queryLabel); if (ad.queryLabel == QueryMolecule::QUERY_ATOM_A) { output.printf("A"); } else if (ad.queryLabel == QueryMolecule::QUERY_ATOM_X) { output.printf("X"); } else if (ad.queryLabel == QueryMolecule::QUERY_ATOM_Q) { output.printf("Q"); } else if (ad.queryLabel == QueryMolecule::QUERY_ATOM_LIST || ad.queryLabel == QueryMolecule::QUERY_ATOM_NOTLIST) { if (ad.queryLabel == QueryMolecule::QUERY_ATOM_NOTLIST) output.printf("!"); output.printf("["); for (int i = 0; i < ad.list.size(); ++i) { if (i > 0) output.printf(","); output.printf("%s", Element::toString(ad.list[i])); } output.printf("]"); } else { throw Error("Query atom type %d not supported", ad.queryLabel); } } } bool MoleculeRenderInternal::_writeDelimiter (bool needDelimiter, Output &output) { if (needDelimiter) output.printf(","); else output.printf("("); return true; } QueryMolecule::Atom* atomNodeInConjunction (QueryMolecule::Atom& qa, int type) { if (qa.type != QueryMolecule::OP_AND) return NULL; for (int i = 0; i < qa.children.size(); ++i) if (qa.child(i)->type == type) return qa.child(i); return NULL; } void MoleculeRenderInternal::_writeQueryModifier (Output& output, int aid) { QUERY_MOL_BEGIN(_mol); { bool needDelimiter = false; QueryMolecule::Atom& qa = qmol.getAtom(aid); if (qa.hasConstraint(QueryMolecule::ATOM_SUBSTITUENTS)) { int subst = qmol.getAtomSubstCount(aid); needDelimiter = _writeDelimiter(needDelimiter, output); if (subst >= 0) output.printf("s%i", subst); } if (qa.hasConstraint(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN)) { needDelimiter = _writeDelimiter(needDelimiter, output); output.printf("s*"); } if (qa.hasConstraint(QueryMolecule::ATOM_RING_BONDS)) { int ringBondCount = qmol.getAtomRingBondsCount(aid); needDelimiter = _writeDelimiter(needDelimiter, output); if (ringBondCount >= 0) output.printf("rb%i", ringBondCount); } if (qa.hasConstraint(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN)) { needDelimiter = _writeDelimiter(needDelimiter, output); output.printf("rb*"); } if (qa.hasConstraint(QueryMolecule::ATOM_UNSATURATION)) { needDelimiter = _writeDelimiter(needDelimiter, output); output.printf("u"); } if (qa.hasConstraint(QueryMolecule::ATOM_TOTAL_H)) { QueryMolecule::Atom *qc = atomNodeInConjunction(qa, QueryMolecule::ATOM_TOTAL_H); if (qc != NULL) { int totalH = qc->value_min; needDelimiter = _writeDelimiter(needDelimiter, output); output.printf("H%i", totalH); } } if (_ad(aid).fixed) { needDelimiter = _writeDelimiter(needDelimiter, output); output.printf("f"); } if (needDelimiter) output.printf(")"); if (_ad(aid).exactChange) { output.printf(".ext."); } } QUERY_MOL_END; } static void _expandBoundRect (AtomDesc& ad, const RenderItem& item) { Vec2f min, max; min.diff(item.bbp, ad.pos); max.sum(min, item.bbsz); ad.boundBoxMin.min(min); ad.boundBoxMax.max(max); } int MoleculeRenderInternal::_findClosestBox (Vec2f& p, int aid, const Vec2f& sz, float mrg, int skip) { const Vertex& vertex = _mol->getVertex(aid); const AtomDesc& ad = _ad(aid); float w2 = sz.x / 2 + mrg; float h2 = sz.y / 2 + mrg; if (vertex.degree() == 0) { p.set(0, -h2); p.add(ad.pos); return -1; } if (vertex.degree() == 1) { float offset = 0; const Vec2f& d = _getBondEnd(aid, vertex.neiBegin()).dir; float val = -1, dx = fabs(d.x), dy = fabs(d.y); if (dx > 0.01) if (val < 0 || val > w2 / dx) val = w2 / dx; if (dy > 0.01) if (val < 0 || val > h2 / dy) val = h2 / dy; if (val > offset) offset = val; p.lineCombin(ad.pos, d, -offset); return vertex.neiBegin(); } int iMin = -1; for (int i = vertex.neiBegin(); i < vertex.neiEnd(); i = vertex.neiNext(i)) { if (skip == i) continue; Vec2f q; const BondEnd& lbe = _getBondEnd(aid, i); const BondEnd& rbe = _be(lbe.rnei); const BondDescr& lbd = _bd(lbe.bid); const BondDescr& rdb = _bd(rbe.bid); // correction of positioning according to bond types float turn = atan(_settings.bondSpace / lbd.length); float leftShift = 0, rightShift = 0; bool leftBondCentered = _be(lbd.be1).centered && _be(lbd.be2).centered; bool leftTurn = false, rightTurn = false; bool leftIsSourceEnd = (lbd.beg == aid); bool leftBondOrientedInwards = (leftIsSourceEnd == lbd.lineOnTheRight); bool leftIsStereo = lbd.stereodir > 0; bool rightBondCentered = _be(rdb.be1).centered && _be(rdb.be2).centered; bool rightIsSourceEnd = (rdb.beg == aid); bool rightIsStereo = rdb.stereodir > 0; bool rightBondOrientedInwards = (rightIsSourceEnd == !rdb.lineOnTheRight); if (lbd.type == BOND_DOUBLE) { if (leftBondCentered) leftShift = _settings.bondSpace; else if (leftBondOrientedInwards) leftShift = 2 * _settings.bondSpace; } else if (lbd.type == BOND_TRIPLE) leftShift = 2 * _settings.bondSpace; else if (leftIsStereo) { if (leftIsSourceEnd) leftTurn = true; else leftShift = _settings.bondSpace; } if (rdb.type == BOND_DOUBLE) { if (rightBondCentered) rightShift = _settings.bondSpace; else if (rightBondOrientedInwards) rightShift = 2 * _settings.bondSpace; } else if (rdb.type == BOND_TRIPLE) rightShift = 2 * _settings.bondSpace; else if (rightIsStereo) { if (rightIsSourceEnd) rightTurn = true; else rightShift = _settings.bondSpace; } Vec2f leftDir, rightDir; leftDir.copy(lbe.dir); rightDir.copy(rbe.dir); if (leftTurn) leftDir.rotateL(turn); if (rightTurn) rightDir.rotateL(-turn); Vec2f origin; float si = Vec2f::cross(leftDir, rightDir); float co = Vec2f::dot(leftDir, rightDir); float ang = atan2(si, co); if (ang < 0) ang += 2 * (float)M_PI; float factor = fabs(sin(ang/2)); Vec2f rightNorm(rightDir), leftNorm(leftDir); rightNorm.rotateL((float)M_PI/2); leftNorm.rotateL(-(float)M_PI/2); float rightOffset = 0, leftOffset = 0; rightOffset = w2 * fabs(leftNorm.x) + h2 * fabs(leftNorm.y); leftOffset = w2 * fabs(rightNorm.x) + h2 * fabs(rightNorm.y); float t = __max(leftShift + rightOffset, leftOffset + rightShift) / factor; Vec2f median(rightDir); median.rotateL(ang / 2); q.addScaled(median, t); if (iMin < 0 || q.lengthSqr() < p.lengthSqr()) { iMin = i; p.copy(q); } } p.add(ad.pos); return iMin; } int MoleculeRenderInternal::_findClosestCircle (Vec2f& p, int aid, float radius, int skip) { const Vertex& vertex = _mol->getVertex(aid); const AtomDesc& ad = _ad(aid); if (vertex.degree() == 0) { p.copy(ad.pos); p.y -= radius; return -1; } if (vertex.degree() == 1) { p.lineCombin(ad.pos, _getBondEnd(aid, vertex.neiBegin()).dir, -radius); return vertex.neiBegin(); } int iMin = -1; for (int i = vertex.neiBegin(); i < vertex.neiEnd(); i = vertex.neiNext(i)) { if (skip == i) continue; Vec2f q; const BondEnd& rbe = _be(i); const BondEnd& lbe = _be(rbe.lnei);; const BondDescr& rbd = _bd(rbe.bid); const BondDescr& lbd = _bd(lbe.bid); // correction of positioning according to bond types float turn = atan(_settings.bondSpace / rbd.length); float rightShift = 0, leftShift = 0; bool rightBondCentered = _be(rbd.be1).centered && _be(rbd.be2).centered; bool rightTurn = false, leftTurn = false; bool rightIsSourceEnd = rbd.beg == aid; bool rightBondOrientationLeft = !rbd.lineOnTheRight; bool rightBondOrientedInwards = (rightIsSourceEnd && rightBondOrientationLeft) || (!rightIsSourceEnd && !rightBondOrientationLeft); bool rightIsStereo = rbd.stereodir > 0; bool leftBondCentered = _getBondEnd(aid, lbd.be1).centered && _getBondEnd(aid, lbd.be2).centered; bool leftIsSourceEnd = lbd.beg == aid; bool leftIsStereo = lbd.stereodir > 0; bool leftBondOrientationRight = lbd.lineOnTheRight; bool leftBondOrientedInwards = (leftIsSourceEnd && leftBondOrientationRight) || (!leftIsSourceEnd && !leftBondOrientationRight); if (rbd.type == BOND_DOUBLE) { if (rightBondCentered) rightShift = _settings.bondSpace; else if (rightBondOrientedInwards) rightShift = 2 * _settings.bondSpace; } else if (rbd.type == BOND_TRIPLE) rightShift = 2 * _settings.bondSpace; else if (rightIsStereo) { if (rightIsSourceEnd) rightTurn = true; else rightShift = _settings.bondSpace; } if (lbd.type == BOND_DOUBLE) { if (leftBondCentered) leftShift = _settings.bondSpace; else if (leftBondOrientedInwards) leftShift = 2 * _settings.bondSpace; } else if (lbd.type == BOND_TRIPLE) leftShift = 2 * _settings.bondSpace; else if (leftIsStereo) { if (leftIsSourceEnd) leftTurn = true; else leftShift = _settings.bondSpace; } Vec2f rightDir, leftDir; rightDir.copy(rbe.dir); leftDir.copy(lbe.dir); if (rightTurn) rightDir.rotate(turn); if (leftTurn) leftDir.rotate(-turn); Vec2f origin; float si = Vec2f::cross(rightDir, leftDir); float co = Vec2f::dot(rightDir, leftDir); float ang = atan2(si, co); if (ang < 0) ang += (float)(2 * M_PI); float factor = __max(fabs(si), 0.01f); if (rightShift > 0) origin.addScaled(leftDir, rightShift / factor); if (leftShift > 0) origin.addScaled(rightDir, leftShift / factor); q.copy(rightDir); q.rotate(ang/2); float dst = radius / (float)sin2c(Vec2f::dot(rightDir, leftDir)); q.scale(dst); q.add(origin); if (iMin < 0 || q.lengthSqr() < p.lengthSqr()) { iMin = i; p.copy(q); } q.add(ad.pos); //_cw.setLineWidth(_settings.unit); //_cw.setSingleSource(CWC_BLUE); //_cw.drawCircle(q, radius); } p.add(ad.pos); return iMin; } enum CHARCAT {DIGIT = 0, LETTER, TAG_SUPERSCRIPT, TAG_SUBSCRIPT, TAG_NORMAL, SIGN, WHITESPACE}; enum SCRIPT {MAIN, SUB, SUPER}; void MoleculeRenderInternal::_preparePseudoAtom (int aid, int color, bool highlighted) { AtomDesc& ad = _ad(aid); const char* str = ad.pseudo.ptr(); int cnt = 0; CHARCAT a = WHITESPACE, b = WHITESPACE; int i0 = 0, i1; SCRIPT script = MAIN, newscript = MAIN; int len = (int)strlen(str); GraphItem::TYPE signType; // TODO: replace remembering item ids and shifting each of them with single offset value for an atom Array<int> tis, gis; TextItem fake; fake.fontsize = FONT_SIZE_LABEL; fake.text.push('C'); fake.text.push((char)0); _cw.setTextItemSize(fake, ad.pos); float xpos = fake.bbp.x, width = fake.bbsz.x, offset = _settings.unit/2, totalwdt = 0, upshift = -0.6f, downshift = 0.2f, space = width / 2; ad.ypos = fake.bbp.y; ad.height = fake.bbsz.y; ad.leftMargin = ad.rightMargin = xpos; if (ad.pseudoAtomStringVerbose) { int id = _pushTextItem(ad, RenderItem::RIT_PSEUDO, color, highlighted); tis.push(id); TextItem& item = _data.textitems[id]; item.fontsize = FONT_SIZE_ATTR; item.text.copy(str, len); item.text.push((char)0); _cw.setTextItemSize(item); item.bbp.set(xpos, ad.ypos); _expandBoundRect(ad, item); totalwdt += item.bbsz.x; } else { for (int i = 0; i <= len; ++i) { a = b; i1 = i; bool tag = false; char c = (i == len ? ' ' : str[i]); if (isspace(c)) b = WHITESPACE; else if (isdigit(c)) b = DIGIT; else if (c == '+' || c == '-') b = SIGN, signType = ((c == '+') ? GraphItem::PLUS : GraphItem::MINUS); else if (c == '\\' && i < len - 1 && str[i+1] == 'S') b = TAG_SUPERSCRIPT, ++i, tag = true; else if (c == '\\' && i < len - 1 && str[i+1] == 's') b = TAG_SUBSCRIPT, ++i, tag = true; else if (c == '\\' && i < len - 1 && str[i+1] == 'n') b = TAG_NORMAL, ++i, tag = true; else b = LETTER; bool aTag = a == TAG_SUPERSCRIPT || a == TAG_SUBSCRIPT || a == TAG_NORMAL; if (b == TAG_SUPERSCRIPT) { newscript = SUPER; } else if (b == TAG_SUBSCRIPT) { newscript = SUB; } else if (b == TAG_NORMAL) { newscript = MAIN; } else if ((b == WHITESPACE && a != WHITESPACE) || (b != WHITESPACE && a == WHITESPACE) || (b == LETTER && !aTag)) { newscript = MAIN; } else if (b == DIGIT && a == SIGN) { newscript = script; } else if (b == DIGIT && a != DIGIT && !aTag) { newscript = ((a == LETTER) ? SUB : MAIN); } else if (b == SIGN) { if (a == LETTER || a == DIGIT) newscript = SUPER; } else if (a == SIGN && script == SUPER) { newscript = MAIN; } else { continue; } if (i1 > i0) { if (a == SIGN && script == SUPER) { int id = _pushGraphItem(ad, RenderItem::RIT_CHARGESIGN, color, highlighted); gis.push(id); GraphItem& sign = _data.graphitems[id]; _cw.setGraphItemSizeSign(sign, signType); totalwdt += offset; sign.bbp.set(xpos + totalwdt, ad.ypos + ad.height - sign.bbsz.y + upshift * ad.height); _expandBoundRect(ad, sign); totalwdt += sign.bbsz.x; } else if (a == WHITESPACE) { totalwdt += space * (i1 - i0); } else { float shift = (script == SUB) ? downshift : ((script == SUPER) ? upshift : 0); int id = _pushTextItem(ad, RenderItem::RIT_PSEUDO, color, highlighted); tis.push(id); TextItem& item = _data.textitems[id]; item.fontsize = (script == MAIN) ? FONT_SIZE_LABEL : FONT_SIZE_ATTR; item.text.copy(str + i0, i1 - i0); item.text.push((char)0); _cw.setTextItemSize(item); if (cnt > 0) totalwdt += offset; item.bbp.set(xpos + totalwdt, ad.ypos + ad.height - item.relpos.y + shift * ad.height); _expandBoundRect(ad, item); totalwdt += item.bbsz.x; } cnt++; } script = newscript; i0 = i + (tag ? 1 : 0); } } ad.rightMargin += totalwdt; if (ad.hydroPos == HYDRO_POS_LEFT) { float dx = totalwdt - width; for (int i = 0; i < tis.size(); ++i) _data.textitems[tis[i]].bbp.x -= dx; for (int i = 0; i < gis.size(); ++i) _data.graphitems[gis[i]].bbp.x -= dx; ad.leftMargin -= dx; ad.rightMargin -= dx; } } void MoleculeRenderInternal::_prepareLabelText (int aid) { AtomDesc& ad = _ad(aid); BaseMolecule& bm = *_mol; ad.boundBoxMin.set(0, 0); ad.boundBoxMax.set(0, 0); int color = ad.color; bool highlighted = _vertexIsHighlighted(aid); int tilabel = -1, tihydro = -1, tiHydroIndex = -1, tiChargeValue = -1, tiValence = -1, tiIsotope = -1, tiindex = -1; int giChargeSign = -1, giRadical = -1, giRadical1 = -1, giRadical2 = -1; ad.rightMargin = ad.leftMargin = ad.ypos = ad.height = 0; int isotope = bm.getAtomIsotope(aid); if (ad.type == AtomDesc::TYPE_PSEUDO) { _preparePseudoAtom(aid, CWC_BASE, highlighted); } else if (ad.showLabel) { tilabel = _pushTextItem(ad, RenderItem::RIT_LABEL, color, highlighted); { TextItem& label = _data.textitems[tilabel]; label.fontsize = FONT_SIZE_LABEL; ArrayOutput output(label.text); if (ad.type == AtomDesc::TYPE_REGULAR) if (ad.label == ELEM_H && isotope == 2) output.printf("D"); else if (ad.label == ELEM_H && isotope == 3) output.printf("T"); else output.printf(Element::toString(ad.label)); else if (ad.type == AtomDesc::TYPE_QUERY) _writeQueryAtomToString(output, aid); else throw Error("Neither label nor query atom type available"); _writeQueryModifier(output, aid); output.writeChar(0); _cw.setTextItemSize(label, ad.pos); _expandBoundRect(ad, label); ad.rightMargin = label.bbp.x + label.bbsz.x; ad.leftMargin = label.bbp.x; ad.ypos = label.bbp.y; ad.height = label.bbsz.y; } if (bm.isRSite(aid) && bm.getVertex(aid).degree() > 1) { // show indices for attachment bonds const Vertex& v = bm.getVertex(aid); ad.rSiteAttachmentIndexBegin = _data.rSiteAttachmentIndices.size(); // Check if there are indices for attachment bonds bool hasAttachmentIndex = false, hasNoAttachmentIndex = false; for (int k = v.neiBegin(), j = 0; k < v.neiEnd(); k = v.neiNext(k), ++j) { int apIdx = bm.getRSiteAttachmentPointByOrder(aid, j); hasAttachmentIndex |= (apIdx != -1); hasNoAttachmentIndex |= (apIdx == -1); } if (hasAttachmentIndex && hasNoAttachmentIndex) throw Error("RSite %d is invalid: some attachments indices are specified and some are not"); if (hasNoAttachmentIndex) ad.rSiteAttachmentIndexCount = 0; else { ad.rSiteAttachmentIndexCount = v.degree(); for (int k = v.neiBegin(), j = 0; k < v.neiEnd(); k = v.neiNext(k), ++j) { int apIdx = bm.getRSiteAttachmentPointByOrder(aid, j); int i = v.findNeiVertex(apIdx); BondEnd& be = _getBondEnd(aid, i); int tii = _pushTextItem(ad, RenderItem::RIT_ATTACHMENTPOINT, CWC_BASE, false); TextItem& ti = _data.textitems[tii]; RenderItemRSiteAttachmentIndex& item = _data.rSiteAttachmentIndices.push(); item.number = j + 1; item.radius = 0.7f * _settings.fzz[FONT_SIZE_RSITE_ATTACHMENT_INDEX]; item.bbsz.set(2 * item.radius, 2 * item.radius); item.bbp = ad.pos; item.color = CWC_BASE; item.highlighted = false; item.noBondOffset = true; bprintf(ti.text, "%d", item.number); ti.fontsize = FONT_SIZE_RSITE_ATTACHMENT_INDEX; ti.noBondOffset = true; _cw.setTextItemSize(ti, ad.pos); TextItem& label = _data.textitems[tilabel]; // this is just an upper bound, it won't be used float shift = item.bbsz.length() + label.bbsz.length(); // one of the next conditions should be satisfied if (fabs(be.dir.x) > 1e-3) shift = __min(shift, (item.bbsz.x + label.bbsz.x) / 2 / fabs(be.dir.x)); if (fabs(be.dir.y) > 1e-3) shift = __min(shift, (item.bbsz.y + label.bbsz.y) / 2 / fabs(be.dir.y)); shift += _settings.unit; item.bbp.addScaled(be.dir, shift); ti.bbp.addScaled(be.dir, shift); be.offset = shift + item.radius; } } } // isotope if (isotope > 0 && (ad.label != ELEM_H || isotope > 3 || isotope < 2)) { tiIsotope = _pushTextItem(ad, RenderItem::RIT_ISOTOPE, color, highlighted); TextItem& itemIsotope = _data.textitems[tiIsotope]; itemIsotope.fontsize = FONT_SIZE_ATTR; bprintf(itemIsotope.text, "%i", isotope); _cw.setTextItemSize(itemIsotope); ad.leftMargin -= _settings.labelInternalOffset + itemIsotope.bbsz.x; itemIsotope.bbp.set(ad.leftMargin, ad.ypos + _settings.upperIndexShift * ad.height); _expandBoundRect(ad, itemIsotope); } // hydrogen drawing ad.showHydro = false; if (!bm.isQueryMolecule()) { int implicit_h = 0; if (!bm.isRSite(aid) && !bm.isPseudoAtom(aid) && !bm.isTemplateAtom(aid)) implicit_h = bm.asMolecule().getImplicitH_NoThrow(aid, 0); if (implicit_h > 0 && _opt.implHVisible) { ad.showHydro = true; tihydro = _pushTextItem(ad, RenderItem::RIT_HYDROGEN, color, highlighted); Vec2f hydrogenGroupSz; { TextItem& itemHydrogen = _data.textitems[tihydro]; itemHydrogen.fontsize = FONT_SIZE_LABEL; bprintf(itemHydrogen.text, "H"); _cw.setTextItemSize(itemHydrogen, ad.pos); hydrogenGroupSz.x = itemHydrogen.bbsz.x + _settings.labelInternalOffset; hydrogenGroupSz.y = itemHydrogen.bbsz.y; } if (implicit_h > 1) { tiHydroIndex = _pushTextItem(ad, RenderItem::RIT_HYDROINDEX, color, highlighted); TextItem& itemHydroIndex = _data.textitems[tiHydroIndex]; TextItem& itemHydrogen = _data.textitems[tihydro]; itemHydroIndex.fontsize = FONT_SIZE_ATTR; bprintf(itemHydroIndex.text, "%i", implicit_h); _cw.setTextItemSize(itemHydroIndex, ad.pos); hydrogenGroupSz.x += itemHydroIndex.bbsz.x + _settings.labelInternalOffset; hydrogenGroupSz.y = __max(hydrogenGroupSz.y, _settings.lowerIndexShift * itemHydrogen.bbsz.y + itemHydroIndex.bbsz.y); } // take new reference, old one may be corrupted after adding 'tiHydroIndex' TextItem& itemHydrogen = _data.textitems[tihydro]; if (ad.hydroPos == HYDRO_POS_LEFT) { ad.leftMargin -= hydrogenGroupSz.x; itemHydrogen.bbp.set(ad.leftMargin, ad.ypos); } else if (ad.hydroPos == HYDRO_POS_RIGHT) { ad.rightMargin += _settings.labelInternalOffset; itemHydrogen.bbp.set(ad.rightMargin, ad.ypos); ad.rightMargin += hydrogenGroupSz.x; } else if (ad.hydroPos == HYDRO_POS_UP) { itemHydrogen.bbp.y = ad.pos.y + ad.boundBoxMin.y - hydrogenGroupSz.y - _settings.unit; } else if (ad.hydroPos == HYDRO_POS_DOWN) { itemHydrogen.bbp.y = ad.pos.y + ad.boundBoxMax.y + _settings.unit; } else { throw Error("hydrogen position value invalid"); } _expandBoundRect(ad, itemHydrogen); if (tiHydroIndex > 0) { _data.textitems[tiHydroIndex].bbp.set(itemHydrogen.bbp.x + itemHydrogen.bbsz.x + _settings.labelInternalOffset, itemHydrogen.bbp.y + _settings.lowerIndexShift * itemHydrogen.bbsz.y); _expandBoundRect(ad, _data.textitems[tiHydroIndex]); } } } // charge int charge = bm.getAtomCharge(aid); if (charge != CHARGE_UNKNOWN && charge != 0) { ad.rightMargin += _settings.labelInternalOffset; if (abs(charge) != 1) { tiChargeValue = _pushTextItem(ad, RenderItem::RIT_CHARGEVAL, color, highlighted); TextItem& itemChargeValue = _data.textitems[tiChargeValue]; itemChargeValue.fontsize = FONT_SIZE_ATTR; bprintf(itemChargeValue.text, "%i", abs(charge)); _cw.setTextItemSize(itemChargeValue); itemChargeValue.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); _expandBoundRect(ad, itemChargeValue); ad.rightMargin += itemChargeValue.bbsz.x; } GraphItem::TYPE type = charge > 0 ? GraphItem::PLUS : GraphItem::MINUS; giChargeSign = _pushGraphItem(ad, RenderItem::RIT_CHARGESIGN, color, highlighted); GraphItem& itemChargeSign = _data.graphitems[giChargeSign]; _cw.setGraphItemSizeSign(itemChargeSign, type); itemChargeSign.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); _expandBoundRect(ad, itemChargeSign); ad.rightMargin += itemChargeSign.bbsz.x; } // valence int valence = bm.getExplicitValence(aid); if (_opt.showValences && valence >= 0) { tiValence = _pushTextItem(ad, RenderItem::RIT_VALENCE, color, highlighted); TextItem& itemValence = _data.textitems[tiValence]; itemValence.fontsize = FONT_SIZE_ATTR; bprintf(itemValence.text, _valenceText(valence)); _cw.setTextItemSize(itemValence); ad.rightMargin += _settings.labelInternalOffset; itemValence.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); _expandBoundRect(ad, itemValence); ad.rightMargin += itemValence.bbsz.x; } // radical int radical = -1; if (!bm.isRSite(aid) && !bm.isPseudoAtom(aid) && !bm.isTemplateAtom(aid)) radical = bm.getAtomRadical_NoThrow(aid, -1); if (radical > 0) { const TextItem& label = _data.textitems[tilabel]; Vec2f ltc(label.bbp); if (radical == RADICAL_DOUBLET) { giRadical = _pushGraphItem(ad, RenderItem::RIT_RADICAL, color, highlighted); GraphItem& itemRadical = _data.graphitems[giRadical]; _cw.setGraphItemSizeDot(itemRadical); if (!(ad.showHydro && ad.hydroPos == HYDRO_POS_RIGHT) && giChargeSign < 0 && tiValence < 0) { ltc.x += label.bbsz.x + _settings.radicalRightOffset; ltc.y += _settings.radicalRightVertShift * ad.height; itemRadical.bbp.copy(ltc); } else { ltc.x += label.bbsz.x / 2 - itemRadical.bbsz.x / 2; ltc.y -= itemRadical.bbsz.y + _settings.radicalTopOffset; itemRadical.bbp.copy(ltc); } _expandBoundRect(ad, itemRadical); } else { giRadical1 = _pushGraphItem(ad, RenderItem::RIT_RADICAL, color, highlighted); giRadical2 = _pushGraphItem(ad, RenderItem::RIT_RADICAL, color, highlighted); GraphItem& itemRadical1 = _data.graphitems[giRadical1]; GraphItem& itemRadical2 = _data.graphitems[giRadical2]; float dist; if (radical == RADICAL_SINGLET) { _cw.setGraphItemSizeDot(itemRadical1); _cw.setGraphItemSizeDot(itemRadical2); dist = _settings.radicalTopDistDot; } else //if (radical == RADICAL_TRIPLET) { _cw.setGraphItemSizeCap(itemRadical1); _cw.setGraphItemSizeCap(itemRadical2); dist = _settings.radicalTopDistCap; } ltc.y -= itemRadical1.bbsz.y + _settings.radicalTopOffset; ltc.x += label.bbsz.x / 2 - dist / 2 - itemRadical1.bbsz.x; itemRadical1.bbp.copy(ltc); ltc.x += dist + itemRadical1.bbsz.x; itemRadical2.bbp.copy(ltc); _expandBoundRect(ad, itemRadical1); _expandBoundRect(ad, itemRadical2); } } } int bondEndRightToStereoGroupLabel = -1; // prepare stereogroup labels if ((ad.stereoGroupType > 0 && ad.stereoGroupType != MoleculeStereocenters::ATOM_ANY) || ad.inversion == STEREO_INVERTS || ad.inversion == STEREO_RETAINS) { int tiStereoGroup = _pushTextItem(ad, RenderItem::RIT_STEREOGROUP, CWC_BASE, false); TextItem& itemStereoGroup = _data.textitems[tiStereoGroup]; itemStereoGroup.fontsize = FONT_SIZE_ATTR; ArrayOutput itemOutput(itemStereoGroup.text); if (ad.stereoGroupType > 0 && ad.stereoGroupType != MoleculeStereocenters::ATOM_ANY) { const char* stereoGroupText = _getStereoGroupText(ad.stereoGroupType); itemOutput.printf("%s", stereoGroupText); if (ad.stereoGroupType != MoleculeStereocenters::ATOM_ABS) itemOutput.printf("%i", ad.stereoGroupNumber); } if (ad.inversion == STEREO_INVERTS || ad.inversion == STEREO_RETAINS) { if (itemOutput.tell() > 0) itemOutput.printf(","); itemOutput.printf("%s", ad.inversion == STEREO_INVERTS ? "Inv" : "Ret"); } itemOutput.writeChar(0); _cw.setTextItemSize(itemStereoGroup); if (ad.showLabel) { // label visible - put stereo group label on the over or under the label const Vertex& v = bm.getVertex(aid); float vMin = 0, vMax = 0; for (int i = v.neiBegin(); i < v.neiEnd(); i = v.neiNext(i)) { float y = _getBondEnd(aid, i).dir.y; if (y > vMax) vMax = y; if (y < vMin) vMin = y; } if (vMax > -vMin) itemStereoGroup.bbp.set(ad.pos.x - itemStereoGroup.bbsz.x / 2, ad.pos.y + ad.boundBoxMin.y - itemStereoGroup.bbsz.y - _settings.stereoGroupLabelOffset); else itemStereoGroup.bbp.set(ad.pos.x - itemStereoGroup.bbsz.x / 2, ad.pos.y + ad.boundBoxMax.y + _settings.stereoGroupLabelOffset); } else { // label hidden - position stereo group label independently Vec2f p; bondEndRightToStereoGroupLabel = _findClosestBox(p, aid, itemStereoGroup.bbsz, _settings.unit); p.addScaled(itemStereoGroup.bbsz, -0.5); itemStereoGroup.bbp.copy(p); } _expandBoundRect(ad, itemStereoGroup); } // prepare AAM labels if (ad.aam > 0) { int tiAAM = _pushTextItem(ad, RenderItem::RIT_AAM, CWC_BASE, false); TextItem& itemAAM = _data.textitems[tiAAM]; itemAAM.fontsize = FONT_SIZE_ATTR; bprintf(itemAAM.text, "%i", abs(ad.aam)); _cw.setTextItemSize(itemAAM); if (ad.showLabel) { ad.leftMargin -= itemAAM.bbsz.x + _settings.labelInternalOffset; itemAAM.bbp.set(ad.leftMargin, ad.ypos + _settings.lowerIndexShift * ad.height); } else { Vec2f p; _findClosestBox(p, aid, itemAAM.bbsz, _settings.unit, bondEndRightToStereoGroupLabel); p.addScaled(itemAAM.bbsz, -0.5); itemAAM.bbp.copy(p); } _expandBoundRect(ad, itemAAM); } // prepare R-group attachment point labels QS_DEF(Array<float>, angles); QS_DEF(Array<int>, split); QS_DEF(Array<int>, rGroupAttachmentIndices); if (ad.isRGroupAttachmentPoint) { // collect the angles between adjacent bonds const Vertex& v = bm.getVertex(aid); angles.clear(); split.clear(); if (v.degree() != 0) { for (int i = v.neiBegin(); i < v.neiEnd(); i = v.neiNext(i)) { float a = _getBondEnd(aid, i).lang; angles.push(a); split.push(1); } } // collect attachment point indices rGroupAttachmentIndices.clear(); bool multipleAttachmentPoints = _mol->attachmentPointCount() > 1; for (int i = 1; i <= _mol->attachmentPointCount(); ++i) for (int j = 0, k; (k = _mol->getAttachmentPoint(i, j)) >= 0; ++j) if (k == aid) rGroupAttachmentIndices.push(i); if (v.degree() != 0) { for (int j = 0; j < rGroupAttachmentIndices.size(); ++j) { int i0 = -1; for (int i = 0; i < angles.size(); ++i) if (i0 < 0 || angles[i] / (split[i] + 1) > angles[i0] / (split[i0] + 1)) i0 = i; split[i0]++; } } // arrange the directions of the attachment points QS_DEF(Array<Vec2f>, attachmentDirection); attachmentDirection.clear(); if (v.degree() == 0) { // if no adjacent bonds present if (rGroupAttachmentIndices.size() == 1) attachmentDirection.push().set(0, -1); else if (rGroupAttachmentIndices.size() == 2) { attachmentDirection.push().set(cos((float)M_PI / 6), -sin((float)M_PI / 6)); attachmentDirection.push().set(cos(5 * (float)M_PI / 6), -sin(5 * (float)M_PI / 6)); } else { for (int j = 0; j < rGroupAttachmentIndices.size(); ++j) { float a = j * 2 * (float)M_PI / rGroupAttachmentIndices.size(); attachmentDirection.push().set(cos(a), sin(a)); } } } else { // split the angles for (int i = 0; i < split.size(); ++i) { angles[i] /= split[i]; } for (int j = 0; j < rGroupAttachmentIndices.size(); ++j) { int i0 = -1, n = v.neiBegin(); for (int i = 0; i < split.size(); ++i, n = v.neiNext(n)) { if (split[i] > 1) { i0 = i; break; } } if (i0 < 0) throw Error("Error while arranging attachment points"); Vec2f d; d.copy(_getBondEnd(aid, n).dir); d.rotateL(angles[i0] * (--split[i0])); attachmentDirection.push(d); } } // create the attachment point items ad.attachmentPointBegin = _data.attachmentPoints.size(); ad.attachmentPointCount = rGroupAttachmentIndices.size(); for (int j = 0; j < rGroupAttachmentIndices.size(); ++j) { RenderItemAttachmentPoint& attachmentPoint = _data.attachmentPoints.push(); float offset = __min(__max(_getBondOffset(aid, ad.pos, attachmentDirection[j], _settings.unit),0), 0.4f); attachmentPoint.dir.copy(attachmentDirection[j]); attachmentPoint.p0.lineCombin(ad.pos, attachmentDirection[j], offset); attachmentPoint.p1.lineCombin(ad.pos, attachmentDirection[j], 0.8f); attachmentPoint.color = CWC_BASE; attachmentPoint.highlighted = false; if (multipleAttachmentPoints) { attachmentPoint.number = rGroupAttachmentIndices[j]; } } } // prepare atom id's if (_opt.showAtomIds) { tiindex = _pushTextItem(ad, RenderItem::RIT_ATOMID, CWC_BLUE, false); TextItem& index = _data.textitems[tiindex]; index.fontsize = FONT_SIZE_INDICES; int base = _opt.atomBondIdsFromOne ? 1 : 0; bprintf(index.text, "%i", aid + base); _cw.setTextItemSize(index, ad.pos); if (ad.showLabel) index.bbp.set(ad.rightMargin + _settings.labelInternalOffset, ad.ypos + 0.5f * ad.height); } } int MoleculeRenderInternal::_pushTextItem (RenderItem::TYPE ritype, int color, bool highlighted) { _data.textitems.push(); _data.textitems.top().clear(); _data.textitems.top().ritype = ritype; _data.textitems.top().color = color; _data.textitems.top().highlighted = highlighted; return _data.textitems.size() - 1; } int MoleculeRenderInternal::_pushTextItem (AtomDesc& ad, RenderItem::TYPE ritype, int color, bool highlighted) { int res = _pushTextItem(ritype, color, highlighted); if (ad.tibegin < 0) ad.tibegin = res; ad.ticount++; return res; } int MoleculeRenderInternal::_pushTextItem (Sgroup& sg, RenderItem::TYPE ritype, int color) { int res = _pushTextItem(ritype, color, false); if (sg.tibegin < 0) sg.tibegin = res; sg.ticount++; return res; } int MoleculeRenderInternal::_pushGraphItem (RenderItem::TYPE ritype, int color, bool highlighted) { _data.graphitems.push(); _data.graphitems.top().clear(); _data.graphitems.top().ritype = ritype; _data.graphitems.top().color = color; _data.graphitems.top().highlighted = highlighted; return _data.graphitems.size() - 1; } int MoleculeRenderInternal::_pushGraphItem (AtomDesc& ad, RenderItem::TYPE ritype, int color, bool highlighted) { int res = _pushGraphItem(ritype, color, highlighted); if (ad.gibegin < 0) ad.gibegin = res; ad.gicount++; return res; } const char* MoleculeRenderInternal::_valenceText (const int valence) { const char* vt[] = {"(0)", "(I)", "(II)", "(III)", "(IV)", "(V)", "(VI)", "(VII)", "(VIII)", "(IX)", "(X)", "(XI)", "(XII)", "(XIII)"}; if (valence < 0 || valence >= NELEM(vt)) throw Error("valence value %i out of range", valence); return vt[valence]; } void MoleculeRenderInternal::_drawBond (int b) { BondDescr& bd = _bd(b); const BondEnd& be1 = _be(bd.be1); const BondEnd& be2 = _be(bd.be2); const AtomDesc& ad1 = _ad(be1.aid); const AtomDesc& ad2 = _ad(be2.aid); _cw.setLineWidth(_settings.bondLineWidth); _cw.setSingleSource(CWC_BASE); if (_edgeIsHighlighted(b)) _cw.setHighlight(); else if (ad1.hcolorSet || ad2.hcolorSet) { Vec3f color1, color2; _cw.getColorVec(color1, CWC_BASE); _cw.getColorVec(color2, CWC_BASE); if (ad1.hcolorSet) color1.copy(ad1.hcolor); if (ad2.hcolorSet) color2.copy(ad2.hcolor); if (color1.x == color2.x && color1.y == color2.y && color1.z == color2.z) _cw.setSingleSource(color1); else _cw.setGradientSource(color1, color2, ad1.pos, ad2.pos); } switch (bd.type) { case BOND_SINGLE: _bondSingle(bd, be1, be2); break; case BOND_DOUBLE: _bondDouble(bd, be1, be2); break; case BOND_TRIPLE: _bondTriple(bd, be1, be2); break; case BOND_AROMATIC: _bondAromatic(bd, be1, be2); break; default: switch (bd.queryType) { case QueryMolecule::QUERY_BOND_ANY: _bondAny(bd, be1, be2); break; case QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE: _bondSingleOrDouble(bd, be1, be2); break; case QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC: _bondDoubleOrAromatic(bd, be1, be2); break; case QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC: _bondSingleOrAromatic(bd, be1, be2); break; default: throw Error("Unknown type"); } } _cw.resetHighlightThickness(); if (bd.reactingCenter != RC_UNMARKED) { int rc = bd.reactingCenter; if (rc == RC_NOT_CENTER) _drawReactingCenter(bd, RC_NOT_CENTER); else { if (rc & RC_CENTER) _drawReactingCenter(bd, RC_CENTER); if ((rc & RC_UNCHANGED) && (_opt.showReactingCenterUnchanged || (rc & (~RC_UNCHANGED)))) _drawReactingCenter(bd, RC_UNCHANGED); if (rc & RC_MADE_OR_BROKEN) _drawReactingCenter(bd, RC_MADE_OR_BROKEN); if (rc & RC_ORDER_CHANGED) _drawReactingCenter(bd, RC_ORDER_CHANGED); } } _cw.resetHighlight(); _cw.clearPattern(); // destroy the linear gradient pattern if one was used if (bd.topology > 0) _drawTopology(bd); } void MoleculeRenderInternal::_drawTopology (BondDescr& bd) { if (bd.topology < 0) return; bd.tiTopology = _pushTextItem(RenderItem::RIT_TOPOLOGY, CWC_BASE, false); TextItem& ti = _data.textitems[bd.tiTopology]; ti.fontsize = FONT_SIZE_ATTR; if (bd.topology == TOPOLOGY_RING) bprintf(ti.text, "rng"); else if (bd.topology == TOPOLOGY_CHAIN) bprintf(ti.text, "chn"); else throw Error("Unknown topology value"); _cw.setTextItemSize(ti); float shift = (fabs(bd.norm.x * ti.bbsz.x)+fabs(bd.norm.y * ti.bbsz.y)) / 2 + _settings.unit; if (bd.extP < bd.extN) shift = shift + bd.extP; else shift = -shift - bd.extN; Vec2f c; c.copy(bd.center); c.addScaled(bd.norm, shift); c.addScaled(ti.bbsz, -0.5f); ti.bbp.copy(c); _cw.drawTextItemText(ti); } void MoleculeRenderInternal::_drawReactingCenter (BondDescr& bd, int rc) { const int rcNumPnts = 8; Vec2f p[rcNumPnts]; for (int i = 0; i < rcNumPnts; ++i) p[i].copy(bd.center); float alongIntRc = _settings.unit, // half interval along for RC_CENTER alongIntMadeBroken = 2 * _settings.unit, // half interval between along for RC_MADE_OR_BROKEN alongSz = 1.5f * _settings.bondSpace, // half size along for RC_CENTER acrossInt = 1.5f * _settings.bondSpace, // half interval across for RC_CENTER acrossSz = 3.0f * _settings.bondSpace, // half size across for all tiltTan = 0.2f, // tangent of the tilt angle radius = _settings.bondSpace; // radius of the circle for RC_UNCHANGED int numLines = 0; _cw.setLineWidth(_settings.unit); switch (rc) { case RC_NOT_CENTER: // X // across p[0].addScaled(bd.norm, acrossSz); p[1].addScaled(bd.norm, -acrossSz); p[2].copy(p[0]); p[3].copy(p[1]); p[0].addScaled(bd.dir, tiltTan * acrossSz); p[1].addScaled(bd.dir, -tiltTan * acrossSz); p[2].addScaled(bd.dir, -tiltTan * acrossSz); p[3].addScaled(bd.dir, tiltTan * acrossSz); numLines = 2; break; case RC_CENTER: // # // across p[0].addScaled(bd.norm, acrossSz); p[0].addScaled(bd.dir, tiltTan * acrossSz); p[1].addScaled(bd.norm, -acrossSz); p[1].addScaled(bd.dir, -tiltTan * acrossSz); p[2].copy(p[0]); p[3].copy(p[1]); p[0].addScaled(bd.dir, alongIntRc); p[1].addScaled(bd.dir, alongIntRc); p[2].addScaled(bd.dir, -alongIntRc); p[3].addScaled(bd.dir, -alongIntRc); // along p[4].addScaled(bd.dir, alongSz); p[5].addScaled(bd.dir, -alongSz); p[6].copy(p[4]); p[7].copy(p[5]); p[4].addScaled(bd.norm, acrossInt); p[5].addScaled(bd.norm, acrossInt); p[6].addScaled(bd.norm, -acrossInt); p[7].addScaled(bd.norm, -acrossInt); numLines = 4; break; case RC_UNCHANGED: // o _cw.fillCircle(bd.center, radius); break; case RC_MADE_OR_BROKEN: // across p[0].addScaled(bd.norm, acrossSz); p[1].addScaled(bd.norm, -acrossSz); p[2].copy(p[0]); p[3].copy(p[1]); p[0].addScaled(bd.dir, alongIntMadeBroken); p[1].addScaled(bd.dir, alongIntMadeBroken); p[2].addScaled(bd.dir, -alongIntMadeBroken); p[3].addScaled(bd.dir, -alongIntMadeBroken); numLines = 2; break; case RC_ORDER_CHANGED: // across p[0].addScaled(bd.norm, acrossSz); p[1].addScaled(bd.norm, -acrossSz); numLines = 1; break; case RC_TOTAL: break; } for (int i = 0; i < numLines; ++i) _cw.drawLine(p[2 * i], p[2 * i + 1]); if (rc == RC_UNCHANGED) { bd.extN = __max(bd.extN, radius); bd.extP = __max(bd.extP, radius); } else { bd.extN = __max(bd.extN, acrossSz); bd.extP = __max(bd.extP, acrossSz); } } double MoleculeRenderInternal::_getAdjustmentFactor (const int aid, const int anei, const double acos, const double asin, const double tgb, const double csb, const double snb, const double len, const double w, double& csg, double& sng) { csg = csb; sng = snb; bool adjustLeft = acos < 0.99 && acos > -0.99; if (!adjustLeft || _bd(_be(anei).bid).isShort) return -1; const BondDescr& nbd = _bd(_be(anei).bid); if (nbd.type == BOND_DOUBLE && nbd.centered) { if (asin <= 0) return -1; return (len * asin - _settings.bondSpace) / (snb * acos + csb * asin); } if ((_bd(_be(anei).bid).stereodir == BOND_UP && _bd(_be(anei).bid).end == aid) || _bd(_be(anei).bid).stereodir == BOND_STEREO_BOLD) { if (fabs(asin) < 0.01) return -1; double sna = sqrt((1 - acos)/2); // sin(a/2) double csa = (asin > 0 ? 1 : -1) * sqrt((1 + acos)/2); // cos(a/2) double gamma = w / sna; double x = sqrt(len * len + gamma * gamma - 2 * gamma * len * csa); sng = gamma * sna / x; csg = sqrt(1-sng*sng); return x; } if (asin > 0.01) return len/(acos * snb / asin + csb); return -1; } void MoleculeRenderInternal::_adjustAngle (Vec2f& l, const BondEnd& be1, const BondEnd& be2, bool left) { const Vec2f& p1 = _ad(be1.aid).pos; const Vec2f& p2 = _ad(be2.aid).pos; const double len = Vec2f::dist(p1, p2); double w = _settings.bondSpace; double tgb = w / len; double csb = sqrt(1 / (1 + tgb * tgb)); double snb = tgb * csb; double sng = 0, csg = 0; double ttr = left ? _getAdjustmentFactor(be2.aid, be2.rnei, be2.rcos, be2.rsin, tgb, csb, snb, len, w, csg, sng): _getAdjustmentFactor(be2.aid, be2.lnei, be2.lcos, be2.lsin, tgb, csb, snb, len, w, csg, sng); if (ttr < 0) return; l.diff(p2, p1); l.normalize(); l.scale(ttr); l.rotateL(left ? sng : -sng, csg); l.add(p1); } void MoleculeRenderInternal::_bondBoldStereo (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f r0(be1.p), l0(be1.p), r1(be2.p), l1(be2.p); double w = _settings.bondSpace; l0.addScaled(bd.norm, -w); r0.addScaled(bd.norm, w); l1.addScaled(bd.norm, -w); r1.addScaled(bd.norm, w); _adjustAngle(l1, be1, be2, true); _adjustAngle(r1, be1, be2, false); _adjustAngle(r0, be2, be1, true); _adjustAngle(l0, be2, be1, false); _cw.fillHex(be1.p, r0, r1, be2.p, l1, l0); } void MoleculeRenderInternal::_bondSingle (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { double len = Vec2f::dist(be2.p, be1.p); if (bd.stereodir == BOND_STEREO_BOLD) { _bondBoldStereo(bd, be1, be2); return; } Vec2f l(be2.p), r(be2.p); double w = _settings.bondSpace; l.addScaled(bd.norm, -w); r.addScaled(bd.norm, w); bd.extP = bd.extN = w; double lw = _cw.currentLineWidth(); Vec2f r0(be1.p), l0(be1.p); l0.addScaled(bd.norm, -lw/2); r0.addScaled(bd.norm, lw/2); if (bd.stereodir == 0) { _cw.drawLine(be1.p, be2.p); bd.extP = bd.extN = lw / 2; } else if (bd.stereodir == BOND_UP) { if (_ad(be2.aid).showLabel == false && !bd.isShort) { _adjustAngle(l, be1, be2, true); _adjustAngle(r, be1, be2, false); _cw.fillPentagon(r0, r, be2.p, l, l0); } else { _cw.fillQuad(r0, r, l, l0); } } else if (bd.stereodir == BOND_DOWN) { int stripeCnt = __max((int)((len) / lw / 2), 4); _cw.fillQuadStripes(r0, l0, r, l, stripeCnt); } else if (bd.stereodir == BOND_EITHER) { int stripeCnt = __max((int)((len) / lw / 1.5), 5); _cw.drawTriangleZigzag(be1.p, r, l, stripeCnt); } else throw Error("Unknown single bond stereo type"); } void MoleculeRenderInternal::_bondAny (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { _cw.setDash(_settings.bondDashAny, Vec2f::dist(be1.p, be2.p)); _cw.drawLine(be1.p, be2.p); _cw.resetDash(); bd.extP = bd.extN = _settings.bondLineWidth / 2; } float MoleculeRenderInternal::_ctghalf (float cs) { return sqrt(1 - cs * cs) / (1 - cs); } float MoleculeRenderInternal::_doubleBondShiftValue (const BondEnd& be, bool right, bool centered) { const BondDescr& bd = _bd(_be(right ? be.rnei : be.lnei).bid); float si = right ? be.rsin : be.lsin, co = right ? be.rcos : be.lcos; if (centered && bd.type == BOND_SINGLE && bd.end == be.aid && bd.stereodir != 0) { float tga = si / co; Vec2f dd; dd.diff(_be(bd.be1).p, _be(bd.be2).p); float len = dd.length(); float tgb = (_settings.bondSpace + _settings.bondLineWidth) / len; float tgab = (tga + tgb) / (1 - tga * tgb); return -(len * si - _settings.bondSpace) / tgab + len * co - _settings.bondLineWidth / 2; } else return co * _settings.bondSpace / si; } void MoleculeRenderInternal::_prepareDoubleBondCoords (Vec2f* coord, BondDescr& bd, const BondEnd& be1, const BondEnd& be2, bool allowCentered) { Vec2f ns, ds; ns.scaled(bd.norm, 2 * _settings.bondSpace + (bd.stereodir == BOND_STEREO_BOLD ? 1 : 0) * _settings.bondLineWidth); if (!(bd.stereodir == BOND_STEREO_BOLD) && ((allowCentered && bd.centered) || bd.cistrans)) { Vec2f p0, p1, q0, q1; ns.scale(0.5f); p0.sum(be1.p, ns); p1.sum(be2.p, ns); q0.diff(be1.p, ns); q1.diff(be2.p, ns); if (be1.prolong) { p0.addScaled(be1.dir, _doubleBondShiftValue(be1, true, bd.centered)); q0.addScaled(be1.dir, _doubleBondShiftValue(be1, false, bd.centered)); } if (be2.prolong) { p1.addScaled(be2.dir, _doubleBondShiftValue(be2, false, bd.centered)); q1.addScaled(be2.dir, _doubleBondShiftValue(be2, true, bd.centered)); } coord[0].copy(p0); coord[1].copy(p1); coord[2].copy(q0); coord[3].copy(q1); bd.extP = bd.extN = _settings.bondSpace + _settings.bondLineWidth / 2; } else { bd.extP = ns.length() + _settings.bondLineWidth / 2; bd.extN = _settings.bondLineWidth / 2; if (!bd.lineOnTheRight) { float t; __swap(bd.extP, bd.extN, t); ns.negate(); } Vec2f p0, p1; p0.sum(be1.p, ns); p1.sum(be2.p, ns); float cs; if (!_ad(be1.aid).showLabel) { cs = bd.lineOnTheRight ? be1.rcos : be1.lcos; if (fabs(cs) < _settings.cosineTreshold) p0.addScaled(be1.dir, _settings.bondSpace * _ctghalf(cs) * 2); } if (!_ad(be2.aid).showLabel) { cs = bd.lineOnTheRight ? be2.lcos : be2.rcos; if (fabs(cs) < _settings.cosineTreshold) p1.addScaled(be2.dir, _settings.bondSpace * _ctghalf(cs) * 2); } coord[0].copy(be1.p); coord[1].copy(be2.p); coord[2].copy(p0); coord[3].copy(p1); } } void MoleculeRenderInternal::_drawStereoCareBox (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f ns; ns.scaled(bd.norm, _settings.bondSpace); if (!bd.lineOnTheRight) ns.negate(); if (bd.stereoCare) { Vec2f p0, p1, p2, p3; p0.lineCombin(be1.p, bd.dir, (bd.length - _settings.stereoCareBoxSize) / 2); p0.addScaled(bd.norm, -_settings.stereoCareBoxSize / 2); bd.extP = bd.extN = _settings.stereoCareBoxSize / 2 + _settings.unit / 2; if (!bd.centered) { float shift = Vec2f::dot(ns,bd.norm); bd.extP += shift; bd.extN -= shift; p0.add(ns); } p1.lineCombin(p0, bd.dir, _settings.stereoCareBoxSize); p2.lineCombin(p1, bd.norm, _settings.stereoCareBoxSize); p3.lineCombin(p0, bd.norm, _settings.stereoCareBoxSize); _cw.setLineWidth(_settings.unit); _cw.drawQuad(p0, p1, p2, p3); } } void MoleculeRenderInternal::_bondDouble (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f coord[4]; _prepareDoubleBondCoords(coord, bd, be1, be2, true); if (bd.stereodir == BOND_STEREO_BOLD) { _bondBoldStereo(bd, be1, be2); _cw.drawLine(coord[2], coord[3]); } else if (bd.cistrans) { _cw.drawLine(coord[0], coord[3]); _cw.drawLine(coord[2], coord[1]); } else { _cw.drawLine(coord[0], coord[1]); _cw.drawLine(coord[2], coord[3]); } _drawStereoCareBox(bd, be1, be2); } void MoleculeRenderInternal::_bondSingleOrAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f coord[4]; _prepareDoubleBondCoords(coord, bd, be1, be2, true); _cw.drawLine(coord[0], coord[1]); _cw.setDash(_settings.bondDashSingleOrAromatic); _cw.drawLine(coord[2], coord[3]); _cw.resetDash(); _drawStereoCareBox(bd, be1, be2); } void MoleculeRenderInternal::_bondDoubleOrAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f coord[4]; _prepareDoubleBondCoords(coord, bd, be1, be2, true); _cw.setDash(_settings.bondDashDoubleOrAromatic); _cw.drawLine(coord[0], coord[1]); _cw.drawLine(coord[2], coord[3]); _cw.resetDash(); _drawStereoCareBox(bd, be1, be2); } void MoleculeRenderInternal::_bondSingleOrDouble (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f ns, ds; ns.scaled(bd.norm, 2 * _settings.bondSpace); ds.diff(be2.p, be1.p); float len = ds.length(); ds.normalize(); // Get number of segments of single-or-double bond // An average bond in our coordinates has length 1. We want an average bond to have 5 segments, like -=-=- // For longer bond more segments may be necessary, for shorter one - less, but not less then 3 segments, like -=- int numSegments = __max((int)(len / 0.4f), 1) * 2 + 1; Vec2f r0, r1, p0, p1, q0, q1; float step = len / numSegments; ns.scale(0.5f); for (int i = 0; i < numSegments; ++i) { r0.lineCombin(be1.p, ds, i * step); r1.lineCombin(be1.p, ds, (i + 1) * step); if (i & 1) { p0.sum(r0, ns); p1.sum(r1, ns); q0.diff(r0, ns); q1.diff(r1, ns); _cw.drawLine(p0, p1); _cw.drawLine(q0, q1); } else _cw.drawLine(r0, r1); } } void MoleculeRenderInternal::_bondAromatic (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { if (bd.aromRing) { // bond is in a ring, draw only a single line _cw.drawLine(be1.p, be2.p); bd.extP = bd.extN = _settings.bondLineWidth / 2; } else { Vec2f coord[4]; _prepareDoubleBondCoords(coord, bd, be1, be2, false); _cw.drawLine(coord[0], coord[1]); _cw.setDash(_settings.bondDashAromatic); _cw.drawLine(coord[2], coord[3]); _cw.resetDash(); } } void MoleculeRenderInternal::_bondTriple (BondDescr& bd, const BondEnd& be1, const BondEnd& be2) { Vec2f ns; ns.scaled(bd.norm, _settings.bondSpace * 2); Vec2f vr1(be1.p), vr2(be2.p), vl1(be1.p), vl2(be2.p); vr1.add(ns); vr2.add(ns); vl1.sub(ns); vl2.sub(ns); _cw.drawLine(be1.p, be2.p); _cw.drawLine(vr1, vr2); _cw.drawLine(vl1, vl2); bd.extP = bd.extN = _settings.bondSpace * 2 + _settings.bondLineWidth / 2; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item.cpp����������������������������������������������������0000664�0000000�0000000�00000002362�12710376503�0021501�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/base_molecule.h" #include "base_cpp/output.h" #include "render_item.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemBase, "RenderItemBase"); RenderItemBase::RenderItemBase (RenderItemFactory& factory) : referenceY(0), _factory(factory), _rc(factory.rc), _settings(factory.rc._settings), _opt(factory.rc.opt) { } void RenderItemBase::renderIdle () { _rc.initNullContext(); Vec2f bbmin, bbmax; Vec2f pos; render(); _rc.bbGetMin(bbmin); _rc.bbGetMax(bbmax); _rc.closeContext(true); size.diff(bbmax, bbmin); origin.copy(bbmin); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_aux.cpp������������������������������������������������0000664�0000000�0000000�00000011311�12710376503�0022350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_aux.h" using namespace indigo; IMPL_ERROR(RenderItemAuxiliary, "RenderItemAuxiliary"); RenderItemAuxiliary::RenderItemAuxiliary (RenderItemFactory& factory) : RenderItemBase(factory), arrowLength(_settings.arrowLength) { } RenderItemAuxiliary::~RenderItemAuxiliary () { } void RenderItemAuxiliary::_drawText () { TextItem ti; ti.text.copy(text); if (type == AUX_COMMENT) { ti.fontsize = FONT_SIZE_COMMENT; ti.ritype = RenderItem::RIT_COMMENT; } else if (type == AUX_TITLE) { ti.fontsize = FONT_SIZE_TITLE; ti.ritype = RenderItem::RIT_TITLE; } else { throw Error("Font size unknown"); } _rc.setTextItemSize(ti); ti.bbp.set(0,0); _rc.drawTextItemText(ti); } void RenderItemAuxiliary::_drawRGroupLabel () { BaseMolecule& bm = *mol; MoleculeRGroups& rgs = bm.rgroups; RGroup& rg = rgs.getRGroup(rLabelIdx); TextItem tiR; tiR.fontsize = FONT_SIZE_LABEL; tiR.color = CWC_BASE; bprintf(tiR.text, "R%d=", rLabelIdx); _rc.setTextItemSize(tiR); referenceY = tiR.bbsz.y / 2; tiR.bbp.set(0,0); _rc.drawTextItemText(tiR); float ypos = tiR.bbp.y + tiR.bbsz.y + _settings.unit; if (rg.occurrence.size() > 0) { TextItem tiOccurrence; tiOccurrence.fontsize = FONT_SIZE_RGROUP_LOGIC_INDEX; tiOccurrence.color = CWC_BASE; ArrayOutput output(tiOccurrence.text); for (int i = 0; i < rg.occurrence.size(); ++i) { int v = rg.occurrence[i]; int a = (v >> 16) & 0xFFFF; int b = v & 0xFFFF; if (i > 0) output.printf(", "); if (a == b) output.printf("%d", a); else if (a == 0) output.printf("<%d", b+1); else if (b == 0xFFFF) output.printf(">%d", a-1); else output.printf("%d-%d", a, b); } output.writeByte(0); _rc.setTextItemSize(tiOccurrence); tiOccurrence.bbp.set(0, ypos); _rc.drawTextItemText(tiOccurrence); ypos += tiOccurrence.bbsz.y + _settings.unit; } if (rg.rest_h > 0) { TextItem tiRestH; tiRestH.fontsize = FONT_SIZE_RGROUP_LOGIC_INDEX; tiRestH.color = CWC_BASE; bprintf(tiRestH.text, "RestH"); _rc.setTextItemSize(tiRestH); tiRestH.bbp.set(0, ypos); _rc.drawTextItemText(tiRestH); } } void RenderItemAuxiliary::_drawRIfThen () { BaseMolecule& bm = *mol; MoleculeRGroups& rgs = bm.rgroups; float ypos = 0; for (int i = 1; i <= rgs.getRGroupCount(); ++i) { const RGroup& rg = rgs.getRGroup(i); if (rg.if_then > 0) { TextItem tiIfThen; tiIfThen.fontsize = FONT_SIZE_RGROUP_LOGIC; tiIfThen.color = CWC_BASE; bprintf(tiIfThen.text, "IF R%d THEN R%d", i, rg.if_then); _rc.setTextItemSize(tiIfThen); tiIfThen.bbp.set(0, ypos); _rc.drawTextItemText(tiIfThen); ypos += tiIfThen.bbsz.y + _settings.rGroupIfThenInterval; } } } void RenderItemAuxiliary::_drawPlus () { _rc.setSingleSource(CWC_BASE); _rc.drawPlus(Vec2f(_settings.plusSize/2, 0), _settings.metaLineWidth, _settings.plusSize); } void RenderItemAuxiliary::_drawArrow () { _rc.setSingleSource(CWC_BASE); _rc.drawArrow(Vec2f(0, 0), Vec2f(arrowLength, 0), _settings.metaLineWidth, _settings.arrowHeadWidth, _settings.arrowHeadSize); } void RenderItemAuxiliary::render () { _rc.translate(-origin.x, -origin.y); switch (type) { case AUX_COMMENT: case AUX_TITLE: _drawText(); return; case AUX_RXN_PLUS: _drawPlus(); return; case AUX_RXN_ARROW: _drawArrow(); return; case AUX_RGROUP_LABEL: _drawRGroupLabel(); return; case AUX_RGROUP_IFTHEN: _drawRIfThen(); return; default: throw Error("Item type not set or invalid"); } }�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_column.cpp���������������������������������������������0000664�0000000�0000000�00000004312�12710376503�0023053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_column.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemColumn, "RenderItemColumn"); RenderItemColumn::RenderItemColumn (RenderItemFactory& factory) : RenderItemContainer(factory), vSpace(0.0), alignment(MultilineTextLayout::Center) { } void RenderItemColumn::init () { } void RenderItemColumn::setVerticalSpacing (float spacing) { vSpace = spacing; } void RenderItemColumn::setAlignment (MultilineTextLayout::Alignment alignment) { this->alignment = alignment; } void RenderItemColumn::estimateSize () { RenderItemContainer::estimateSize(); size.set(0,0); for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); size.y += (i > 0 ? vSpace : 0) + item.size.y; size.x = __max(size.x, item.size.x); //__max(size.x, 2 * (fabs(item.referenceY) + item.size.x / 2)); } } void RenderItemColumn::render () { _rc.translate(-origin.x, -origin.y); _rc.storeTransform(); for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); _rc.storeTransform(); _rc.translate(MultilineTextLayout::getRelativeOffset(alignment) * (size.x - item.size.x), 0); item.render(); _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(0, item.size.y + vSpace); } _rc.restoreTransform(); _rc.removeStoredTransform(); }����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_container.cpp������������������������������������������0000664�0000000�0000000�00000004677�12710376503�0023556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_container.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemContainer, "RenderItemContainer"); RenderItemContainer::RenderItemContainer (RenderItemFactory& factory) : RenderItemBase(factory) { } void RenderItemContainer::estimateSize () { for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); item.estimateSize(); } } void RenderItemContainer::setObjScale (float scale) { for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); item.setObjScale(scale); } } float RenderItemContainer::getTotalBondLength () { float sum = 0.0; for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); sum += item.getTotalBondLength(); } return sum; } float RenderItemContainer::getTotalClosestAtomDistance() { float sum = 0.0; for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); sum += item.getTotalClosestAtomDistance(); } return sum; } int RenderItemContainer::getBondCount () { int sum = 0; for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); sum += item.getBondCount(); } return sum; } int RenderItemContainer::getAtomCount () { int sum = 0; for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); sum += item.getAtomCount(); } return sum; }�����������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_factory.cpp��������������������������������������������0000664�0000000�0000000�00000001371�12710376503�0023227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "render2d/render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemFactory, "RenderItemFactory"); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_fragment.cpp�������������������������������������������0000664�0000000�0000000�00000006663�12710376503�0023374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_factory.h" #include "render_item_fragment.h" using namespace indigo; IMPL_ERROR(RenderItemFragment, "RenderItemFragment"); RenderItemFragment::RenderItemFragment (RenderItemFactory& factory) : RenderItemBase(factory), mol(NULL), aam(NULL), reactingCenters(NULL), inversionArray(NULL), exactChangeArray(NULL), refAtom(-1), _scaleFactor(1.0f), isRFragment(false) { } RenderItemFragment::~RenderItemFragment () { } void RenderItemFragment::init () { _min.set(0,0); _max.set(0,0); for (int i = mol->vertexBegin(); i < mol->vertexEnd(); i = mol->vertexNext(i)) { const Vec3f& v = mol->getAtomXyz(i); Vec2f v2(v.x, v.y); if (i == mol->vertexBegin()) { _min.copy(v2); _max.copy(v2); } else { _min.min(v2); _max.max(v2); } } } void RenderItemFragment::estimateSize () { renderIdle(); if (refAtom >= 0) { const Vec3f& v = mol->getAtomXyz(refAtom); Vec2f v2(v.x, v.y); refAtomPos.set(v2.x - _min.x, _max.y - v2.y); refAtomPos.scale(_scaleFactor); refAtomPos.sub(origin); } } void RenderItemFragment::render () { _rc.translate(-origin.x, -origin.y); MoleculeRenderInternal rnd(_opt, _settings, _rc); rnd.setMolecule(mol); rnd.setIsRFragment(isRFragment); rnd.setScaleFactor(_scaleFactor, _min, _max); rnd.setReactionComponentProperties(aam, reactingCenters, inversionArray); rnd.setQueryReactionComponentProperties(exactChangeArray); rnd.render(); } static float get2dDist (const Vec3f& v1, const Vec3f& v2) { return sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y)); } float RenderItemFragment::getTotalBondLength () { float sum = 0.0; for (int i = mol->edgeBegin(); i < mol->edgeEnd(); i = mol->edgeNext(i)) { const Edge& edge = mol->getEdge(i); sum += get2dDist(mol->getAtomXyz(edge.beg), mol->getAtomXyz(edge.end)); } return sum; } float RenderItemFragment::getTotalClosestAtomDistance() { if (mol->vertexCount() < 2) return 0; float sum = 0.0; for (int i = mol->vertexBegin(); i < mol->vertexEnd(); i = mol->vertexNext(i)) { float minDist = -1; for (int j = mol->vertexBegin(); j < mol->vertexEnd(); j = mol->vertexNext(j)) { if (i == j) continue; float dist = get2dDist(mol->getAtomXyz(i), mol->getAtomXyz(j)); if (minDist < 0 || dist < minDist) minDist = dist; } sum += minDist; } return sum; } int RenderItemFragment::getBondCount () { return mol->edgeCount(); } int RenderItemFragment::getAtomCount () { return mol->vertexCount(); }�����������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_hline.cpp����������������������������������������������0000664�0000000�0000000�00000003743�12710376503�0022664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_hline.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemHLine, "RenderItemHLine"); RenderItemHLine::RenderItemHLine (RenderItemFactory& factory) : RenderItemContainer(factory) { } void RenderItemHLine::init () { hSpace = _settings.layoutMarginHorizontal; } void RenderItemHLine::estimateSize () { RenderItemContainer::estimateSize(); size.set(0,0); for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); size.y = __max(size.y, 2 * (fabs(item.referenceY) + item.size.y / 2)); size.x += (i > 0 ? hSpace : 0) + item.size.x; } } void RenderItemHLine::render () { _rc.translate(-origin.x, -origin.y); _rc.storeTransform(); for (int i = 0; i < items.size(); ++i) { RenderItemBase& item = _factory.getItem(items[i]); _rc.storeTransform(); _rc.translate(0, 0.5f * (size.y - item.size.y) + item.referenceY); item.render(); _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(item.size.x + hSpace, 0); } _rc.restoreTransform(); _rc.removeStoredTransform(); }�����������������������������Indigo-indigo-1.2.3/render2d/src/render_item_molecule.cpp�������������������������������������������0000664�0000000�0000000�00000010364�12710376503�0023367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_molecule.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemMolecule, "RenderItemMolecule"); RenderItemMolecule::RenderItemMolecule (RenderItemFactory& factory) : RenderItemContainer(factory), mol(NULL), refAtom(-1), _core(-1) { } void RenderItemMolecule::init () { if (mol == NULL) throw Error("molecule not set"); if (mol->vertexCount() == 0) return; _core = _factory.addItemFragment(); _factory.getItemFragment(_core).mol = mol; _factory.getItemFragment(_core).refAtom = refAtom; _factory.getItemFragment(_core).init(); int lineCore = _factory.addItemHLine(); _factory.getItemHLine(lineCore).init(); _factory.getItemHLine(lineCore).items.push(_core); items.push(lineCore); { MoleculeRGroups& rGroups = mol->rgroups; if (_getRIfThenCount() > 0) { int _ifThen = _factory.addItemAuxiliary(); _factory.getItemAuxiliary(_ifThen).type = RenderItemAuxiliary::AUX_RGROUP_IFTHEN; _factory.getItemAuxiliary(_ifThen).mol = mol; int lineIfThen = _factory.addItemHLine(); _factory.getItemHLine(lineIfThen).init(); _factory.getItemHLine(lineIfThen).items.push(_ifThen); _factory.getItemAuxiliary(_ifThen).init(); items.push(lineIfThen); } for (int i = 1; i <= rGroups.getRGroupCount(); ++i) { RGroup& rg = rGroups.getRGroup(i); if (rg.fragments.size() == 0) continue; int lineRFrag = _factory.addItemHLine(); _factory.getItemHLine(lineRFrag).init(); items.push(lineRFrag); int label = _factory.addItemAuxiliary(); _factory.getItemAuxiliary(label).type = RenderItemAuxiliary::AUX_RGROUP_LABEL; _factory.getItemAuxiliary(label).mol = mol; _factory.getItemAuxiliary(label).rLabelIdx = i; _factory.getItemHLine(lineRFrag).items.push(label); _factory.getItemAuxiliary(label).init(); PtrPool<BaseMolecule> &frags = rg.fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) { int id = _factory.addItemFragment(); _factory.getItemFragment(id).mol = frags[j]; _factory.getItemFragment(id).isRFragment = true; _factory.getItemFragment(id).init(); _factory.getItemHLine(lineRFrag).items.push(id); } } } } int RenderItemMolecule::_getRIfThenCount () { MoleculeRGroups& rgs = mol->rgroups; int cnt = 0; for (int i = 1; i <= rgs.getRGroupCount(); ++i) if (rgs.getRGroup(i).if_then > 0) ++cnt; return cnt; } void RenderItemMolecule::estimateSize () { RenderItemContainer::estimateSize(); origin.set(0, 0); size.set(0, 0); float vSpace = _settings.layoutMarginVertical; for (int i = 0; i < items.size(); ++i) { RenderItemHLine& line = _factory.getItemHLine(items[i]); size.x = __max(size.x, line.size.x); if (i > 0) size.y += vSpace; size.y += line.size.y; } if (_core >= 0) refAtomPos.copy(_factory.getItemFragment(_core).refAtomPos); } void RenderItemMolecule::render () { _rc.translate(-origin.x, -origin.y); float vSpace = _settings.layoutMarginVertical; for (int i = 0; i < items.size(); ++i) { RenderItemHLine& line = _factory.getItemHLine(items[i]); line.render(); _rc.translate(0, line.size.y + vSpace); } }����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_item_reaction.cpp�������������������������������������������0000664�0000000�0000000�00000016743�12710376503�0023375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "base_cpp/output.h" #include "layout/metalayout.h" #include "render_context.h" #include "render_internal.h" #include "render_item_reaction.h" #include "render_item_factory.h" using namespace indigo; IMPL_ERROR(RenderItemReaction, "RenderItemReaction"); RenderItemReaction::RenderItemReaction (RenderItemFactory& factory) : RenderItemContainer(factory), rxn(NULL), hSpace(_settings.layoutMarginHorizontal), catalystOffset(_settings.layoutMarginVertical / 2), _reactantLine(-1), _catalystLineUpper(-1), _catalystLineLower(-1), _productLine(-1), _arrow(-1), _splitCatalysts(false) { } void RenderItemReaction::init () { if (rxn == NULL) throw Error("reaction not set"); if (rxn->begin() >= rxn->end()) // no reactants or products return; _splitCatalysts = _opt.agentsBelowArrow; _reactantLine = _factory.addItemHLine(); _factory.getItemHLine(_reactantLine).init(); items.push(_reactantLine); for (int i = rxn->reactantBegin(); i < rxn->reactantEnd(); i = rxn->reactantNext(i)) { if (i > rxn->reactantBegin()) _factory.getItemHLine(_reactantLine).items.push(_addPlus()); _factory.getItemHLine(_reactantLine).items.push(_addFragment(i)); } if (rxn->catalystCount() > 0) { _catalystLineUpper = _factory.addItemHLine(); _factory.getItemHLine(_catalystLineUpper).init(); items.push(_catalystLineUpper); if (_splitCatalysts) { _catalystLineLower = _factory.addItemHLine(); _factory.getItemHLine(_catalystLineLower).init(); items.push(_catalystLineLower); } int halfTheNumberOfCatalysts = (rxn->catalystCount() + 1) / 2; for (int i = rxn->catalystBegin(), j = 0; i < rxn->catalystEnd(); i = rxn->catalystNext(i), ++j) { if (!_splitCatalysts || j < halfTheNumberOfCatalysts) _factory.getItemHLine(_catalystLineUpper).items.push(_addFragment(i)); else _factory.getItemHLine(_catalystLineLower).items.push(_addFragment(i)); } } _productLine = _factory.addItemHLine(); _factory.getItemHLine(_productLine).init(); items.push(_productLine); for (int i = rxn->productBegin(); i < rxn->productEnd(); i = rxn->productNext(i)) { if (i > rxn->productBegin()) _factory.getItemHLine(_productLine).items.push(_addPlus()); _factory.getItemHLine(_productLine).items.push(_addFragment(i)); } _arrow = _factory.addItemAuxiliary(); _factory.getItemAuxiliary(_arrow).type = RenderItemAuxiliary::AUX_RXN_ARROW; _factory.getItemAuxiliary(_arrow).init(); items.push(_arrow); } int RenderItemReaction::_addPlus () { int plus = _factory.addItemAuxiliary(); _factory.getItemAuxiliary(plus).init(); _factory.getItemAuxiliary(plus).type = RenderItemAuxiliary::AUX_RXN_PLUS; return plus; } int RenderItemReaction::_addFragment (int i) { int mol = _factory.addItemFragment(); _factory.getItemFragment(mol).mol = &rxn->getBaseMolecule(i); _factory.getItemFragment(mol).aam = &rxn->getAAMArray(i); _factory.getItemFragment(mol).reactingCenters = &rxn->getReactingCenterArray(i); _factory.getItemFragment(mol).inversionArray = &rxn->getInversionArray(i); QUERY_RXN_BEGIN1(rxn); _factory.getItemFragment(mol).exactChangeArray = &qr.getExactChangeArray(i); QUERY_RXN_END; _factory.getItemFragment(mol).init(); return mol; } void RenderItemReaction::estimateSize () { RenderItemContainer::estimateSize(); size.set(0,0); origin.set(0,0); size.x = 0; size.y = 0; if (_reactantLine >= 0) { RenderItemBase& reactants = _factory.getItem(_reactantLine); size.x += reactants.size.x; size.y = __max(size.y, reactants.size.y); } if (_productLine >= 0) { RenderItemBase& products = _factory.getItem(_productLine); size.x += products.size.x; size.y = __max(size.y, products.size.y); } if (_arrow >= 0) { RenderItemAuxiliary& arrow = _factory.getItemAuxiliary(_arrow); _arrowWidth = arrow.size.x; size.y = __max(size.y, arrow.size.y); if (_catalystLineUpper >= 0) { RenderItemBase& catalysts = _factory.getItem(_catalystLineUpper); _arrowWidth = __max(_arrowWidth, catalysts.size.x); size.y = __max(size.y, 2 * catalysts.size.y + 2 * catalystOffset + arrow.size.y); } if (_catalystLineLower >= 0) { RenderItemBase& catalysts = _factory.getItem(_catalystLineLower); _arrowWidth = __max(_arrowWidth, catalysts.size.x); size.y = __max(size.y, 2 * catalysts.size.y + 2 * catalystOffset + arrow.size.y); } size.x += _arrowWidth + 2 * hSpace; } } void RenderItemReaction::render () { _rc.translate(-origin.x, -origin.y); _rc.storeTransform(); { if (_reactantLine >= 0) { RenderItemBase& reactants = _factory.getItem(_reactantLine); _rc.storeTransform(); { _rc.translate(0, 0.5f * (size.y - reactants.size.y)); reactants.render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(reactants.size.x, 0); } if (_arrow >= 0) { RenderItemAuxiliary& arrow = _factory.getItemAuxiliary(_arrow); _rc.translate(hSpace, 0); if (_catalystLineUpper >= 0) { RenderItemBase& catalysts = _factory.getItem(_catalystLineUpper); _rc.storeTransform(); { _rc.translate(0.5f * (_arrowWidth - catalysts.size.x), 0.5f * (size.y - arrow.size.y) - catalysts.size.y - catalystOffset); catalysts.render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); } if (_catalystLineLower >= 0) { RenderItemBase& catalysts = _factory.getItem(_catalystLineLower); _rc.storeTransform(); { _rc.translate(0.5f * (_arrowWidth - catalysts.size.x), 0.5f * (size.y + arrow.size.y) /*+ catalysts.size.y*/ + catalystOffset); catalysts.render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); } _rc.storeTransform(); { _rc.translate(0, 0.5f * (size.y - arrow.size.y)); arrow.arrowLength = _arrowWidth; arrow.render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(_arrowWidth + hSpace, 0); } if (_productLine >= 0) { RenderItemBase& products = _factory.getItem(_productLine); _rc.storeTransform(); { _rc.translate(0, 0.5f * (size.y - products.size.y)); products.render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); } } _rc.restoreTransform(); _rc.removeStoredTransform(); }�����������������������������Indigo-indigo-1.2.3/render2d/src/render_params.cpp��������������������������������������������������0000664�0000000�0000000�00000022040�12710376503�0022021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "base_cpp/array.h" #include "base_cpp/output.h" #include "base_cpp/os_sync_wrapper.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "layout/metalayout.h" #include "layout/reaction_layout.h" #include "layout/molecule_layout.h" #include "molecule/molecule_arom.h" #include "molecule/molecule_dearom.h" #include "molecule/molecule_auto_loader.h" #include "molecule/molfile_loader.h" #include "molecule/smiles_loader.h" #include "molecule/molfile_saver.h" #include "reaction/reaction_auto_loader.h" #include "reaction/rxnfile_loader.h" #include "reaction/rsmiles_loader.h" #include "reaction/rxnfile_saver.h" #include "render_context.h" #include "render_params.h" #include "render_item_molecule.h" #include "render_item_factory.h" #include "render_single.h" #include "render_grid.h" #include "render_cdxml.h" using namespace indigo; RenderParams::RenderParams () { clear(); } RenderParams::~RenderParams () { } void RenderParams::clearArrays () { mols.clear(); rxns.clear(); titles.clear(); refAtoms.clear(); } void RenderParams::clear () { relativeThickness = 1.0f; bondLineWidthFactor = 1.0f; rmode = RENDER_NONE; mol.reset(NULL); rxn.reset(NULL); rOpt.clear(); cnvOpt.clear(); clearArrays(); } IMPL_ERROR(RenderParamInterface, "render param interface"); bool RenderParamInterface::needsLayoutSub (BaseMolecule& mol) { QS_DEF(RedBlackSet<int>, atomsToIgnore); atomsToIgnore.clear(); for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { SGroup &sg = mol.sgroups.getSGroup(i); if (sg.sgroup_type == SGroup::SG_TYPE_MUL) { const Array<int>& atoms = sg.atoms; const Array<int>& patoms = ((MultipleGroup &)sg).parent_atoms; for (int j = 0; j < atoms.size(); ++j) atomsToIgnore.find_or_insert(atoms[j]); for (int j = 0; j < patoms.size(); ++j) if (atomsToIgnore.find(patoms[j])) atomsToIgnore.remove(patoms[j]); } } for (int i = mol.vertexBegin(); i < mol.vertexEnd(); i = mol.vertexNext(i)) { if (atomsToIgnore.find(i)) continue; for (int j = mol.vertexNext(i); j < mol.vertexEnd(); j = mol.vertexNext(j)) { if (atomsToIgnore.find(j)) continue; const Vec3f& v = mol.getAtomXyz(i); const Vec3f& w = mol.getAtomXyz(j); Vec3f d; d.diff(v, w); d.z = 0; if (d.length() < 1e-3) return true; } } return false; } bool RenderParamInterface::needsLayout (BaseMolecule& mol) { if (needsLayoutSub(mol)) return true; MoleculeRGroups& rGroups = mol.rgroups; for (int i = 1; i <= rGroups.getRGroupCount(); ++i) { PtrPool<BaseMolecule> &frags = rGroups.getRGroup(i).fragments; for (int j = frags.begin(); j != frags.end(); j = frags.next(j)) if (needsLayoutSub(*frags[j])) return true; } return false; } void RenderParamInterface::_prepareMolecule (RenderParams& params, BaseMolecule& bm) { if (needsLayout(bm)) { MoleculeLayout ml(bm, false); ml.make(); bm.clearBondDirections(); bm.stereocenters.markBonds(); bm.allene_stereo.markBonds(); } } void RenderParamInterface::_prepareReaction (RenderParams& params, BaseReaction& rxn) { for (int i = rxn.begin(); i < rxn.end(); i = rxn.next(i)) { BaseMolecule& mol = rxn.getBaseMolecule(i); if (needsLayout(mol)) { MoleculeLayout ml(mol, false); ml.make(); mol.clearBondDirections(); mol.stereocenters.markBonds(); mol.allene_stereo.markBonds(); } } } int RenderParamInterface::multilineTextUnit (RenderItemFactory& factory, int type, const Array<char>& titleStr, const float spacing, const MultilineTextLayout::Alignment alignment) { int title = factory.addItemColumn(); int start = 0; while (start < titleStr.size()) { int next = titleStr.find(start + 1, titleStr.size(), '\n'); if (next == -1) next = titleStr.size(); int title_line = factory.addItemAuxiliary(); factory.getItemAuxiliary(title_line).type = (RenderItemAuxiliary::AUX_TYPE)type; factory.getItemAuxiliary(title_line).text.copy(titleStr.ptr() + start, next - start); factory.getItemAuxiliary(title_line).text.push('\0'); factory.getItemColumn(title).items.push(title_line); start = next+1; } factory.getItemColumn(title).setVerticalSpacing(spacing); factory.getItemColumn(title).setAlignment(alignment); return title; } void RenderParamInterface::render (RenderParams& params) { // Disable multithreaded SVG rendering due to the Cairo issue. See IND-482 OsLock *render_lock = 0; if (params.rOpt.mode == MODE_SVG) { static ThreadSafeStaticObj<OsLock> svg_lock; render_lock = svg_lock.ptr(); } OsLockerNullable locker(render_lock); if (params.rmode == RENDER_NONE) throw Error("No object to render specified"); RenderContext rc(params.rOpt, params.relativeThickness, params.bondLineWidthFactor); bool bondLengthSet = params.cnvOpt.bondLength > 0; int bondLength = (int)(bondLengthSet ? params.cnvOpt.bondLength : 100); rc.setDefaultScale((float)bondLength); // TODO: fix bondLength type RenderItemFactory factory(rc); int obj = -1; Array<int> objs; Array<int> titles; if (params.rmode == RENDER_MOL) { if (params.mols.size() == 0) { obj = factory.addItemMolecule(); BaseMolecule& bm = params.mol.ref(); _prepareMolecule(params, bm); factory.getItemMolecule(obj).mol = &bm; } else { for (int i = 0; i < params.mols.size(); ++i) { int mol = factory.addItemMolecule(); BaseMolecule& bm = *params.mols[i]; _prepareMolecule(params, bm); factory.getItemMolecule(mol).mol = &bm; objs.push(mol); if (params.titles.size() > 0) { titles.push(multilineTextUnit(factory, RenderItemAuxiliary::AUX_TITLE, params.titles[i], params.rOpt.titleSpacing * params.rOpt.titleFontFactor, params.cnvOpt.titleAlign.inbox_alignment)); } } } } else if (params.rmode == RENDER_RXN) { if (params.rxns.size() == 0) { obj = factory.addItemReaction(); factory.getItemReaction(obj); BaseReaction& rxn = params.rxn.ref(); _prepareReaction(params, rxn); factory.getItemReaction(obj).rxn = &rxn; } else { for (int i = 0; i < params.rxns.size(); ++i) { int rxn = factory.addItemReaction(); BaseReaction& br = *params.rxns[i]; _prepareReaction(params, br); factory.getItemReaction(rxn).rxn = &br; objs.push(rxn); if (params.titles.size() > 0) { titles.push(multilineTextUnit(factory, RenderItemAuxiliary::AUX_TITLE, params.titles[i], params.rOpt.titleSpacing * params.rOpt.titleFontFactor, params.cnvOpt.titleAlign.inbox_alignment)); } } } } else { throw Error("Invalid rendering mode: %i", params.rmode); } int comment = -1; if (params.cnvOpt.comment.size() > 0) { comment = multilineTextUnit(factory, RenderItemAuxiliary::AUX_COMMENT, params.cnvOpt.comment, params.rOpt.commentSpacing * params.rOpt.commentFontFactor, params.cnvOpt.commentAlign.inbox_alignment); } // Render into other formats after objects has been prepared (layout, etc.) if (params.rOpt.mode == MODE_CDXML) { // Render into CDXML format RenderParamCdxmlInterface::render(params); return; } if (obj >= 0) { RenderSingle render(rc, factory, params.cnvOpt, bondLength, bondLengthSet); render.obj = obj; render.comment = comment; render.draw(); } else { RenderGrid render(rc, factory, params.cnvOpt, bondLength, bondLengthSet); render.objs.copy(objs); render.comment = comment; render.titles.copy(titles); render.refAtoms.copy(params.refAtoms); render.draw(); } rc.closeContext(false); }������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/render2d/src/render_single.cpp��������������������������������������������������0000664�0000000�0000000�00000010654�12710376503�0022027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include "math/algebra.h" #include "base_cpp/array.h" #include "base_cpp/obj_array.h" #include "base_cpp/output.h" #include "base_cpp/reusable_obj_array.h" #include "layout/metalayout.h" #include "molecule/molecule.h" #include "molecule/query_molecule.h" #include "reaction/reaction.h" #include "reaction/query_reaction.h" #include "render_context.h" #include "render_item.h" #include "render_item_factory.h" #include "render_single.h" using namespace indigo; IMPL_ERROR(RenderSingle, "RenderSingle"); RenderSingle::RenderSingle (RenderContext& rc, RenderItemFactory& factory, const CanvasOptions& cnvOpt, int bondLength, bool bondLengthSet) : Render(rc, factory, cnvOpt, bondLength, bondLengthSet) {} RenderSingle::~RenderSingle() {} void RenderSingle::_drawObj () { _rc.storeTransform(); { _rc.translate((objArea.x - objSize.x * scale) / 2, (objArea.y - objSize.y * scale) / 2); _rc.scale(scale); _factory.getItem(obj).render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(0, objArea.y); } void RenderSingle::_drawComment () { if (comment < 0) return; _rc.storeTransform(); { float diff = (float)(width - 2 * outerMargin.x - commentSize.x); _rc.translate(diff * _cnvOpt.commentAlign.getBboxRelativeOffset(), 0); _factory.getItem(comment).render(); } _rc.restoreTransform(); _rc.removeStoredTransform(); _rc.translate(0, commentSize.y); } void RenderSingle::draw () { width = _cnvOpt.width; height = _cnvOpt.height; _rc.fontsClear(); _factory.getItem(obj).init(); float objScale = _getObjScale(obj); _factory.getItem(obj).setObjScale(objScale); _factory.getItem(obj).estimateSize(); objSize.copy(_factory.getItem(obj).size); commentSize.set(0,0); commentOffset = 0; if (comment >= 0) { _factory.getItem(comment).init(); _factory.getItem(comment).estimateSize(); commentSize.copy(_factory.getItem(comment).size); commentOffset = _cnvOpt.commentOffset; } outerMargin.x = (float)(minMarg + _cnvOpt.marginX); outerMargin.y = (float)(minMarg + _cnvOpt.marginY); width = __min(width, _getMaxWidth()); height = __min(height, _getMaxHeight()); scale = _getScale(width, height); if (width < 1) width = _getDefaultWidth(scale); if (height < 1) height = _getDefaultHeight(scale); _rc.initContext(width, height); objArea.set((float)width, (float)height); objArea.addScaled(outerMargin, -2); objArea.y -= commentSize.y + commentOffset; _rc.init(); _rc.translate((float)outerMargin.x, (float)outerMargin.y); if (_cnvOpt.xOffset > 0 || _cnvOpt.yOffset > 0) _rc.translate((float)_cnvOpt.xOffset, (float)_cnvOpt.yOffset); _rc.storeTransform(); { if (_cnvOpt.commentPos == COMMENT_POS_TOP) { _drawComment(); _rc.translate(0, (float)commentOffset); _drawObj(); } else { _drawObj(); _rc.translate(0, (float)commentOffset); _drawComment(); } } _rc.resetTransform(); _rc.removeStoredTransform(); } int RenderSingle::_getDefaultWidth (const float s) { return (int)ceil(__max(__max(objSize.x * s, commentSize.x) + outerMargin.x * 2, 1)); } int RenderSingle::_getDefaultHeight (const float s) { return (int)ceil(__max(objSize.y * s + commentOffset + commentSize.y + outerMargin.y * 2, 1)); } float RenderSingle::_getScaleGivenSize(int w, int h) { float absX = 2 * outerMargin.x; float absY = commentSize.y + 2 * outerMargin.y + commentOffset; float x = w - absX, y = h - absY; if (x < commentSize.x + 1 || y < 1) throw Error("Image too small, the layout requires at least %dx%d", (int)(absX + commentSize.x + 2), (int)(absY + 2)); if (x * objSize.y < y * objSize.x) return x / objSize.x; else return y / objSize.y; }������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0016532�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/����������������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0020414�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/����������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0021511�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/CMakeLists.txt��������������������������������������0000664�0000000�0000000�00000023655�12710376503�0024264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������cmake_minimum_required(VERSION 2.8) project(Cairo C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) if (USE_SYSTEM_CAIRO) find_package(CAIRO) if (NOT CAIRO_FOUND) MESSAGE(FATAL_ERROR "Cannot find system Cairo library") endif() endif() find_package(PIXMAN) if (NOT CAIRO_FOUND) message(STATUS "Using local Cairo library") set(Cairo_headers_dir ${CMAKE_CURRENT_SOURCE_DIR}/src/) include_directories(${Cairo_headers_dir} ${Pixman_headers_dir}) set(CAIRO_HAS_QUARTZ_FONT 0) set(CAIRO_HAS_QUARTZ_SURFACE 0) set(CAIRO_HAS_QUARTZ_IMAGE_SURFACE 0) set(CAIRO_HAS_WIN32_FONT 0) set(CAIRO_HAS_WIN32_SURFACE 0) set(CAIRO_HAS_FC_FONT 0) set(CAIRO_HAS_FT_FONT 0) set(CAIRO_HAS_GL_SURFACE 0) set(CAIRO_HAS_VG_SURFACE 0) set(CAIRO_HAS_GLX_FUNCTIONS 0) set(CAIRO_HAS_EGL_FUNCTIONS 0) set(CAIRO_HAS_GLESV2_SURFACE 0) set(CAIRO_WIN32_STATIC_BUILD 0) if(UNIX OR APPLE OR MINGW) add_definitions(-DHAVE_STDINT_H -DCAIRO_HAS_PTHREAD -D_GNU_SOURCE -DHAVE_UINT64_T) endif() if (UNIX AND NOT APPLE) set(CAIRO_HAS_FC_FONT 1) set(CAIRO_HAS_FT_FONT 1) if(NOT DEFINED $ENV{VERBOSE}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-attributes") endif() endif() if (APPLE) set(CAIRO_HAS_QUARTZ_FONT 1) set(CAIRO_HAS_QUARTZ_SURFACE 1) set(CAIRO_HAS_QUARTZ_IMAGE_SURFACE 1) if(NOT DEFINED $ENV{VERBOSE}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-enum-conversion -Wno-tautological-constant-out-of-range-compare -Wno-deprecated-declarations -Wno-return-type -Wno-parentheses-equality") endif() endif() if(MSVC) add_definitions(-DDISABLE_SOME_FLOATING_POINT) # Fix lround missing problem endif() if (MSVC OR MINGW) set(CAIRO_HAS_WIN32_FONT 1) set(CAIRO_HAS_WIN32_SURFACE 1) set(CAIRO_WIN32_STATIC_BUILD 1) file (GLOB CAIRO_WIN32_SOURCES src/win32/*.c) endif() if(WITH_CAIRO_VG) set(CAIRO_HAS_VG_SURFACE 1) endif() if(WITH_CAIRO_GL) set(CAIRO_HAS_GL_SURFACE 1) endif() if(WITH_CAIRO_EGL) set(CAIRO_HAS_EGL_FUNCTIONS 1) endif() if(WITH_CAIRO_GLESV2) set(CAIRO_HAS_GLESV2_SURFACE 1) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-features.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-features.h) file (GLOB CAIRO_SOURCES src/*.c) file (GLOB CAIRO_HEADERS src/*.h) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-screen.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-visual.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-display.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-os2-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/check-has-hidden-symbols.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-glitz-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-directfb-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/check-link.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/test-fallback-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/test-meta-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_FCURRENT_SOURCE_DIR}/src/test-paginated-surface.c) if (NOT WITH_CAIRO_EGL) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-egl-context.c) endif() if (NOT WITH_CAIRO_GL AND NOT WITH_CAIRO_GLESV2) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-composite.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-device.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-dispatch.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-glyphs.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-gradient.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-info.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-msaa-compositor.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-operand.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-shaders.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-source.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-spans-compositor.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-gl-traps-compositor.c) endif() list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-glx-context.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-script-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-tee-surface.c) if (NOT WITH_CAIRO_VG) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-vg-surface.c) endif() list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-wgl-context.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-connection-core.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-connection-render.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-connection-shm.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-connection.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-screen.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-shm.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-surface-cairo.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-surface-core.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xcb-surface-render.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-xcb-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-render-compositor.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-source.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-core-compositor.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-fallback-compositor.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xlib-surface-shm.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-cogl-gradient.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-cogl-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-cogl-utils.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-cogl-context.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-xml-surface.c) if((NOT MSVC) AND (NOT MINGW)) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-win32-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-win32-printing-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-win32-font-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-win32-font.c) endif() if (NOT APPLE) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-quartz-image-surface.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-quartz-font.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-quartz-surface.c) endif(NOT APPLE) if (APPLE) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-fixed.c) list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-freed-pool.c) endif() if((UNIX) AND (NOT APPLE)) find_package(Freetype REQUIRED) if(NOT FREETYPE_FOUND) message(FATAL_ERROR "No Freetype found") endif() add_definitions(-DCAIRO_HAS_FT_FONT) add_definitions(-DCAIRO_HAS_FC_FONT) include_directories(${FREETYPE_INCLUDE_DIRS}) else() list(REMOVE_ITEM CAIRO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/cairo-ft-font.c) endif() include_directories(${PNG_INCLUDE_DIR}) include_directories(${ZLib_HEADERS_DIR}) if(MSVC AND NOT DEFINED $ENV{VERBOSE}) add_definitions(/wd4244 /wd4146 /wd4267) endif() # To remove GLIBC_2.11 dependency set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0") if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4244 /wd4146 /wd4334 /wd4267 /wd4101") endif() add_library(cairo STATIC ${CAIRO_SOURCES} ${CAIRO_WIN32_SOURCES}) set_target_properties(cairo PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") if(UNIX AND NOT APPLE) target_link_libraries(cairo freetype fontconfig) endif() if(MSVC OR MINGW) target_link_libraries(cairo msimg32) endif() if (WITH_CAIRO_GL) target_link_libraries(cairo GL) endif() if (WITH_CAIRO_VG) target_link_libraries(cairo OpenVG) endif() if (WITH_CAIRO_EGL) target_link_libraries(cairo egl) endif() if (WITH_CAIRO_GLESV2) target_link_libraries(cairo glesv2) endif() set_property(TARGET cairo PROPERTY FOLDER "third_party") if (NOT NO_STATIC) pack_static(cairo) endif() else() set(Cairo_headers_dir ${CAIRO_INCLUDE_DIRS}) endif() �����������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/cairo-version.h�������������������������������������0000664�0000000�0000000�00000000225�12710376503�0024441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef CAIRO_VERSION_H #define CAIRO_VERSION_H #define CAIRO_VERSION_MAJOR 1 #define CAIRO_VERSION_MINOR 12 #define CAIRO_VERSION_MICRO 16 #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/������������������������������������������������0000775�0000000�0000000�00000000000�12710376503�0022300�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-analysis-surface-private.h����������������0000664�0000000�0000000�00000005057�12710376503�0030474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2005 Keith Packard * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith Packard <keithp@keithp.com> */ #ifndef CAIRO_ANALYSIS_SURFACE_H #define CAIRO_ANALYSIS_SURFACE_H #include "cairoint.h" cairo_private cairo_surface_t * _cairo_analysis_surface_create (cairo_surface_t *target); cairo_private void _cairo_analysis_surface_set_ctm (cairo_surface_t *surface, const cairo_matrix_t *ctm); cairo_private void _cairo_analysis_surface_get_ctm (cairo_surface_t *surface, cairo_matrix_t *ctm); cairo_private cairo_region_t * _cairo_analysis_surface_get_supported (cairo_surface_t *surface); cairo_private cairo_region_t * _cairo_analysis_surface_get_unsupported (cairo_surface_t *surface); cairo_private cairo_bool_t _cairo_analysis_surface_has_supported (cairo_surface_t *surface); cairo_private cairo_bool_t _cairo_analysis_surface_has_unsupported (cairo_surface_t *surface); cairo_private void _cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface, cairo_box_t *bbox); cairo_private cairo_int_status_t _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_int_status_t status_b); cairo_private cairo_surface_t * _cairo_null_surface_create (cairo_content_t content); #endif /* CAIRO_ANALYSIS_SURFACE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-analysis-surface.c������������������������0000664�0000000�0000000�00000067120�12710376503�0027016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2006 Keith Packard * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith Packard <keithp@keithp.com> * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #include "cairo-analysis-surface-private.h" #include "cairo-box-inline.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-inline.h" #include "cairo-region-private.h" typedef struct { cairo_surface_t base; cairo_surface_t *target; cairo_bool_t first_op; cairo_bool_t has_supported; cairo_bool_t has_unsupported; cairo_region_t supported_region; cairo_region_t fallback_region; cairo_box_t page_bbox; cairo_bool_t has_ctm; cairo_matrix_t ctm; } cairo_analysis_surface_t; cairo_int_status_t _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_int_status_t status_b) { /* fatal errors should be checked and propagated at source */ assert (! _cairo_int_status_is_error (status_a)); assert (! _cairo_int_status_is_error (status_b)); /* return the most important status */ if (status_a == CAIRO_INT_STATUS_UNSUPPORTED || status_b == CAIRO_INT_STATUS_UNSUPPORTED) return CAIRO_INT_STATUS_UNSUPPORTED; if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK || status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK) return CAIRO_INT_STATUS_IMAGE_FALLBACK; if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN || status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; /* at this point we have checked all the valid internal codes, so... */ assert (status_a == CAIRO_INT_STATUS_SUCCESS && status_b == CAIRO_INT_STATUS_SUCCESS); return CAIRO_INT_STATUS_SUCCESS; } struct proxy { cairo_surface_t base; cairo_surface_t *target; }; static cairo_status_t proxy_finish (void *abstract_surface) { return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t proxy_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, proxy_finish, }; static cairo_surface_t * attach_proxy (cairo_surface_t *source, cairo_surface_t *target) { struct proxy *proxy; proxy = malloc (sizeof (*proxy)); if (unlikely (proxy == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content); proxy->target = target; _cairo_surface_attach_snapshot (source, &proxy->base, NULL); return &proxy->base; } static void detach_proxy (cairo_surface_t *proxy) { cairo_surface_finish (proxy); cairo_surface_destroy (proxy); } static cairo_int_status_t _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, const cairo_pattern_t *pattern) { const cairo_surface_pattern_t *surface_pattern; cairo_analysis_surface_t *tmp; cairo_surface_t *source, *proxy; cairo_matrix_t p2d; cairo_status_t status, analysis_status; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); source = surface_pattern->surface; proxy = _cairo_surface_has_snapshot (source, &proxy_backend); if (proxy != NULL) { /* nothing untoward found so far */ return CAIRO_STATUS_SUCCESS; } tmp = (cairo_analysis_surface_t *) _cairo_analysis_surface_create (surface->target); if (unlikely (tmp->base.status)) return tmp->base.status; proxy = attach_proxy (source, &tmp->base); p2d = pattern->matrix; status = cairo_matrix_invert (&p2d); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm); tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm); source = _cairo_surface_get_source (source, NULL); status = _cairo_recording_surface_replay_and_create_regions (source, &tmp->base); analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS; detach_proxy (proxy); cairo_surface_destroy (&tmp->base); if (unlikely (status)) return status; return analysis_status; } static cairo_int_status_t _add_operation (cairo_analysis_surface_t *surface, cairo_rectangle_int_t *rect, cairo_int_status_t backend_status) { cairo_int_status_t status; cairo_box_t bbox; if (rect->width == 0 || rect->height == 0) { /* Even though the operation is not visible we must be careful * to not allow unsupported operations to be replayed to the * backend during CAIRO_PAGINATED_MODE_RENDER */ if (backend_status == CAIRO_INT_STATUS_SUCCESS || backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) { return CAIRO_INT_STATUS_SUCCESS; } else { return CAIRO_INT_STATUS_IMAGE_FALLBACK; } } _cairo_box_from_rectangle (&bbox, rect); if (surface->has_ctm) { int tx, ty; if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) { rect->x += tx; rect->y += ty; tx = _cairo_fixed_from_int (tx); bbox.p1.x += tx; bbox.p2.x += tx; ty = _cairo_fixed_from_int (ty); bbox.p1.y += ty; bbox.p2.y += ty; } else { _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, &bbox, NULL); if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { /* Even though the operation is not visible we must be * careful to not allow unsupported operations to be * replayed to the backend during * CAIRO_PAGINATED_MODE_RENDER */ if (backend_status == CAIRO_INT_STATUS_SUCCESS || backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) { return CAIRO_INT_STATUS_SUCCESS; } else { return CAIRO_INT_STATUS_IMAGE_FALLBACK; } } _cairo_box_round_to_rectangle (&bbox, rect); } } if (surface->first_op) { surface->first_op = FALSE; surface->page_bbox = bbox; } else _cairo_box_add_box(&surface->page_bbox, &bbox); /* If the operation is completely enclosed within the fallback * region there is no benefit in emitting a native operation as * the fallback image will be painted on top. */ if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) return CAIRO_INT_STATUS_IMAGE_FALLBACK; if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates * that the backend only supports this operation if the * transparency removed. If the extents of this operation does * not intersect any other native operation, the operation is * natively supported and the backend will blend the * transparency into the white background. */ if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) backend_status = CAIRO_INT_STATUS_SUCCESS; } if (backend_status == CAIRO_INT_STATUS_SUCCESS) { /* Add the operation to the supported region. Operations in * this region will be emitted as native operations. */ surface->has_supported = TRUE; return cairo_region_union_rectangle (&surface->supported_region, rect); } /* Add the operation to the unsupported region. This region will * be painted as an image after all native operations have been * emitted. */ surface->has_unsupported = TRUE; status = cairo_region_union_rectangle (&surface->fallback_region, rect); /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate * unsupported operations to the recording surface as using * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to * invoke the cairo-surface-fallback path then return * CAIRO_STATUS_SUCCESS. */ if (status == CAIRO_INT_STATUS_SUCCESS) return CAIRO_INT_STATUS_IMAGE_FALLBACK; else return status; } static cairo_status_t _cairo_analysis_surface_finish (void *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; _cairo_region_fini (&surface->supported_region); _cairo_region_fini (&surface->fallback_region); cairo_surface_destroy (surface->target); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_analysis_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_analysis_surface_t *surface = abstract_surface; return _cairo_surface_get_extents (surface->target, rectangle); } static void _rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip) { if (clip != NULL) _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip)); } static void _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip, cairo_rectangle_int_t *extents) { cairo_bool_t is_empty; is_empty = _cairo_surface_get_extents (&surface->base, extents); if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; _cairo_pattern_get_extents (source, &source_extents); _cairo_rectangle_intersect (extents, &source_extents); } _rectangle_intersect_clip (extents, clip); } static cairo_int_status_t _cairo_analysis_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->paint == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->paint (surface->target, op, source, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->mask == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->mask (surface->target, op, source, mask, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface; src_surface = _cairo_surface_get_source (src_surface, NULL); if (_cairo_surface_is_recording (src_surface)) { backend_source_status = _analyze_recording_surface_pattern (surface, source); if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; } } if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface; mask_surface = _cairo_surface_get_source (mask_surface, NULL); if (_cairo_surface_is_recording (mask_surface)) { backend_mask_status = _analyze_recording_surface_pattern (surface, mask); if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; } } backend_status = _cairo_analysis_surface_merge_status (backend_source_status, backend_mask_status); } _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; _cairo_pattern_get_extents (mask, &mask_extents); _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t _cairo_analysis_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->stroke == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->stroke (surface->target, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; cairo_int_status_t status; status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, &mask_extents); if (unlikely (status)) return status; _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t _cairo_analysis_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->fill == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { backend_status = surface->target->backend->fill (surface->target, op, source, path, fill_rule, tolerance, antialias, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, &mask_extents); _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; /* Adapted from _cairo_surface_show_glyphs */ if (surface->target->backend->show_glyphs != NULL) { backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } else if (surface->target->backend->show_text_glyphs != NULL) { backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, FALSE, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } else { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents, NULL); if (unlikely (status)) return status; _cairo_rectangle_intersect (&extents, &glyph_extents); } return _add_operation (surface, &extents, backend_status); } static cairo_bool_t _cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface) { cairo_analysis_surface_t *surface = abstract_surface; return cairo_surface_has_show_text_glyphs (surface->target); } static cairo_int_status_t _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->target->backend->show_text_glyphs != NULL) { backend_status = surface->target->backend->show_text_glyphs (surface->target, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs != NULL) { backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) backend_status = _analyze_recording_surface_pattern (surface, source); _cairo_analysis_surface_operation_extents (surface, op, source, clip, &extents); if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &glyph_extents, NULL); if (unlikely (status)) return status; _cairo_rectangle_intersect (&extents, &glyph_extents); } return _add_operation (surface, &extents, backend_status); } static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, _cairo_analysis_surface_finish, NULL, NULL, /* create_similar */ NULL, /* create_similar_image */ NULL, /* map_to_image */ NULL, /* unmap */ NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_analysis_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_analysis_surface_paint, _cairo_analysis_surface_mask, _cairo_analysis_surface_stroke, _cairo_analysis_surface_fill, NULL, /* fill_stroke */ _cairo_analysis_surface_show_glyphs, _cairo_analysis_surface_has_show_text_glyphs, _cairo_analysis_surface_show_text_glyphs }; cairo_surface_t * _cairo_analysis_surface_create (cairo_surface_t *target) { cairo_analysis_surface_t *surface; cairo_status_t status; status = target->status; if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = malloc (sizeof (cairo_analysis_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); /* I believe the content type here is truly arbitrary. I'm quite * sure nothing will ever use this value. */ _cairo_surface_init (&surface->base, &cairo_analysis_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); cairo_matrix_init_identity (&surface->ctm); surface->has_ctm = FALSE; surface->target = cairo_surface_reference (target); surface->first_op = TRUE; surface->has_supported = FALSE; surface->has_unsupported = FALSE; _cairo_region_init (&surface->supported_region); _cairo_region_init (&surface->fallback_region); surface->page_bbox.p1.x = 0; surface->page_bbox.p1.y = 0; surface->page_bbox.p2.x = 0; surface->page_bbox.p2.y = 0; return &surface->base; } void _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, const cairo_matrix_t *ctm) { cairo_analysis_surface_t *surface; if (abstract_surface->status) return; surface = (cairo_analysis_surface_t *) abstract_surface; surface->ctm = *ctm; surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); } void _cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface, cairo_matrix_t *ctm) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; *ctm = surface->ctm; } cairo_region_t * _cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; return &surface->supported_region; } cairo_region_t * _cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; return &surface->fallback_region; } cairo_bool_t _cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; return surface->has_supported; } cairo_bool_t _cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; return surface->has_unsupported; } void _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, cairo_box_t *bbox) { cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; *bbox = surface->page_bbox; } /* null surface type: a surface that does nothing (has no side effects, yay!) */ static cairo_int_status_t _return_success (void) { return CAIRO_STATUS_SUCCESS; } /* These typedefs are just to silence the compiler... */ typedef cairo_int_status_t (*_paint_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); typedef cairo_int_status_t (*_mask_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); typedef cairo_int_status_t (*_stroke_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); typedef cairo_int_status_t (*_fill_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); typedef cairo_int_status_t (*_show_glyphs_func) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); static const cairo_surface_backend_t cairo_null_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, NULL, /* finish */ NULL, /* only accessed through the surface functions */ NULL, /* create_similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image*/ NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ NULL, /* get_extents */ NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ (_paint_func) _return_success, /* paint */ (_mask_func) _return_success, /* mask */ (_stroke_func) _return_success, /* stroke */ (_fill_func) _return_success, /* fill */ NULL, /* fill_stroke */ (_show_glyphs_func) _return_success, /* show_glyphs */ NULL, /* has_show_text_glyphs */ NULL /* show_text_glyphs */ }; cairo_surface_t * _cairo_null_surface_create (cairo_content_t content) { cairo_surface_t *surface; surface = malloc (sizeof (cairo_surface_t)); if (unlikely (surface == NULL)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } _cairo_surface_init (surface, &cairo_null_surface_backend, NULL, /* device */ content); return surface; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-arc-private.h�����������������������������0000664�0000000�0000000�00000003634�12710376503�0025767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_ARC_PRIVATE_H #define CAIRO_ARC_PRIVATE_H #include "cairoint.h" CAIRO_BEGIN_DECLS cairo_private void _cairo_arc_path (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); cairo_private void _cairo_arc_path_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); CAIRO_END_DECLS #endif /* CAIRO_ARC_PRIVATE_H */ ����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-arc.c�������������������������������������0000664�0000000�0000000�00000021270�12710376503�0024306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-arc-private.h" #define MAX_FULL_CIRCLES 65536 /* Spline deviation from the circle in radius would be given by: error = sqrt (x**2 + y**2) - 1 A simpler error function to work with is: e = x**2 + y**2 - 1 From "Good approximation of circles by curvature-continuous Bezier curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric Design 8 (1990) 22-41, we learn: abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) and abs (error) =~ 1/2 * e Of course, this error value applies only for the particular spline approximation that is used in _cairo_gstate_arc_segment. */ static double _arc_error_normalized (double angle) { return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); } static double _arc_max_angle_for_tolerance_normalized (double tolerance) { double angle, error; int i; /* Use table lookup to reduce search time in most cases. */ struct { double angle; double error; } table[] = { { M_PI / 1.0, 0.0185185185185185036127 }, { M_PI / 2.0, 0.000272567143730179811158 }, { M_PI / 3.0, 2.38647043651461047433e-05 }, { M_PI / 4.0, 4.2455377443222443279e-06 }, { M_PI / 5.0, 1.11281001494389081528e-06 }, { M_PI / 6.0, 3.72662000942734705475e-07 }, { M_PI / 7.0, 1.47783685574284411325e-07 }, { M_PI / 8.0, 6.63240432022601149057e-08 }, { M_PI / 9.0, 3.2715520137536980553e-08 }, { M_PI / 10.0, 1.73863223499021216974e-08 }, { M_PI / 11.0, 9.81410988043554039085e-09 }, }; int table_size = ARRAY_LENGTH (table); for (i = 0; i < table_size; i++) if (table[i].error < tolerance) return table[i].angle; ++i; do { angle = M_PI / i++; error = _arc_error_normalized (angle); } while (error > tolerance); return angle; } static int _arc_segments_needed (double angle, double radius, cairo_matrix_t *ctm, double tolerance) { double major_axis, max_angle; /* the error is amplified by at most the length of the * major axis of the circle; see cairo-pen.c for a more detailed analysis * of this. */ major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius); max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis); return ceil (fabs (angle) / max_angle); } /* We want to draw a single spline approximating a circular arc radius R from angle A to angle B. Since we want a symmetric spline that matches the endpoints of the arc in position and slope, we know that the spline control points must be: (R * cos(A), R * sin(A)) (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) (R * cos(B), R * sin(B)) for some value of h. "Approximation of circular arcs by cubic polynomials", Michael Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides various values of h along with error analysis for each. From that paper, a very practical value of h is: h = 4/3 * R * tan(angle/4) This value does not give the spline with minimal error, but it does provide a very good approximation, (6th-order convergence), and the error expression is quite simple, (see the comment for _arc_error_normalized). */ static void _cairo_arc_segment (cairo_t *cr, double xc, double yc, double radius, double angle_A, double angle_B) { double r_sin_A, r_cos_A; double r_sin_B, r_cos_B; double h; r_sin_A = radius * sin (angle_A); r_cos_A = radius * cos (angle_A); r_sin_B = radius * sin (angle_B); r_cos_B = radius * cos (angle_B); h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); cairo_curve_to (cr, xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A, xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B, xc + r_cos_B, yc + r_sin_B); } static void _cairo_arc_in_direction (cairo_t *cr, double xc, double yc, double radius, double angle_min, double angle_max, cairo_direction_t dir) { if (cairo_status (cr)) return; assert (angle_max >= angle_min); if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { angle_max = fmod (angle_max - angle_min, 2 * M_PI); angle_min = fmod (angle_min, 2 * M_PI); angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES; } /* Recurse if drawing arc larger than pi */ if (angle_max - angle_min > M_PI) { double angle_mid = angle_min + (angle_max - angle_min) / 2.0; if (dir == CAIRO_DIRECTION_FORWARD) { _cairo_arc_in_direction (cr, xc, yc, radius, angle_min, angle_mid, dir); _cairo_arc_in_direction (cr, xc, yc, radius, angle_mid, angle_max, dir); } else { _cairo_arc_in_direction (cr, xc, yc, radius, angle_mid, angle_max, dir); _cairo_arc_in_direction (cr, xc, yc, radius, angle_min, angle_mid, dir); } } else if (angle_max != angle_min) { cairo_matrix_t ctm; int i, segments; double step; cairo_get_matrix (cr, &ctm); segments = _arc_segments_needed (angle_max - angle_min, radius, &ctm, cairo_get_tolerance (cr)); step = (angle_max - angle_min) / segments; segments -= 1; if (dir == CAIRO_DIRECTION_REVERSE) { double t; t = angle_min; angle_min = angle_max; angle_max = t; step = -step; } for (i = 0; i < segments; i++, angle_min += step) { _cairo_arc_segment (cr, xc, yc, radius, angle_min, angle_min + step); } _cairo_arc_segment (cr, xc, yc, radius, angle_min, angle_max); } else { cairo_line_to (cr, xc + radius * cos (angle_min), yc + radius * sin (angle_min)); } } /** * _cairo_arc_path: * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc * @angle1: the start angle, in radians * @angle2: the end angle, in radians * * Compute a path for the given arc and append it onto the current * path within @cr. The arc will be accurate within the current * tolerance and given the current transformation. **/ void _cairo_arc_path (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2) { _cairo_arc_in_direction (cr, xc, yc, radius, angle1, angle2, CAIRO_DIRECTION_FORWARD); } /** * _cairo_arc_path_negative: * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc * @angle1: the start angle, in radians * @angle2: the end angle, in radians * @ctm: the current transformation matrix * @tolerance: the current tolerance value * @path: the path onto which the arc will be appended * * Compute a path for the given arc (defined in the negative * direction) and append it onto the current path within @cr. The arc * will be accurate within the current tolerance and given the current * transformation. **/ void _cairo_arc_path_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2) { _cairo_arc_in_direction (cr, xc, yc, radius, angle2, angle1, CAIRO_DIRECTION_REVERSE); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-array-private.h���������������������������0000664�0000000�0000000�00000005700�12710376503�0026334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_ARRAY_PRIVATE_H #define CAIRO_ARRAY_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" CAIRO_BEGIN_DECLS /* cairo-array.c structures and functions */ cairo_private void _cairo_array_init (cairo_array_t *array, unsigned int element_size); cairo_private void _cairo_array_fini (cairo_array_t *array); cairo_private cairo_status_t _cairo_array_grow_by (cairo_array_t *array, unsigned int additional); cairo_private void _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements); cairo_private cairo_status_t _cairo_array_append (cairo_array_t *array, const void *element); cairo_private cairo_status_t _cairo_array_append_multiple (cairo_array_t *array, const void *elements, unsigned int num_elements); cairo_private cairo_status_t _cairo_array_allocate (cairo_array_t *array, unsigned int num_elements, void **elements); cairo_private void * _cairo_array_index (cairo_array_t *array, unsigned int index); cairo_private const void * _cairo_array_index_const (const cairo_array_t *array, unsigned int index); cairo_private void _cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst); cairo_private unsigned int _cairo_array_num_elements (const cairo_array_t *array); cairo_private unsigned int _cairo_array_size (const cairo_array_t *array); CAIRO_END_DECLS #endif /* CAIRO_ARRAY_PRIVATE_H */ ����������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-array.c�����������������������������������0000664�0000000�0000000�00000035253�12710376503�0024665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" /** * _cairo_array_init: * * Initialize a new #cairo_array_t object to store objects each of size * @element_size. * * The #cairo_array_t object provides grow-by-doubling storage. It * never interprets the data passed to it, nor does it provide any * sort of callback mechanism for freeing resources held onto by * stored objects. * * When finished using the array, _cairo_array_fini() should be * called to free resources allocated during use of the array. **/ void _cairo_array_init (cairo_array_t *array, unsigned int element_size) { array->size = 0; array->num_elements = 0; array->element_size = element_size; array->elements = NULL; } /** * _cairo_array_fini: * @array: A #cairo_array_t * * Free all resources associated with @array. After this call, @array * should not be used again without a subsequent call to * _cairo_array_init() again first. **/ void _cairo_array_fini (cairo_array_t *array) { free (array->elements); } /** * _cairo_array_grow_by: * @array: a #cairo_array_t * * Increase the size of @array (if needed) so that there are at least * @additional free spaces in the array. The actual size of the array * is always increased by doubling as many times as necessary. **/ cairo_status_t _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) { char *new_elements; unsigned int old_size = array->size; unsigned int required_size = array->num_elements + additional; unsigned int new_size; /* check for integer overflow */ if (required_size > INT_MAX || required_size < array->num_elements) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (required_size <= old_size) return CAIRO_STATUS_SUCCESS; if (old_size == 0) new_size = 1; else new_size = old_size * 2; while (new_size < required_size) new_size = new_size * 2; array->size = new_size; new_elements = _cairo_realloc_ab (array->elements, array->size, array->element_size); if (unlikely (new_elements == NULL)) { array->size = old_size; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } array->elements = new_elements; return CAIRO_STATUS_SUCCESS; } /** * _cairo_array_truncate: * @array: a #cairo_array_t * * Truncate size of the array to @num_elements if less than the * current size. No memory is actually freed. The stored objects * beyond @num_elements are simply "forgotten". **/ void _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) { if (num_elements < array->num_elements) array->num_elements = num_elements; } /** * _cairo_array_index: * @array: a #cairo_array_t * Returns: A pointer to the object stored at @index. * * If the resulting value is assigned to a pointer to an object of the same * element_size as initially passed to _cairo_array_init() then that * pointer may be used for further direct indexing with []. For * example: * * <informalexample><programlisting> * cairo_array_t array; * double *values; * * _cairo_array_init (&array, sizeof(double)); * ... calls to _cairo_array_append() here ... * * values = _cairo_array_index (&array, 0); * for (i = 0; i < _cairo_array_num_elements (&array); i++) * ... use values[i] here ... * </programlisting></informalexample> **/ void * _cairo_array_index (cairo_array_t *array, unsigned int index) { /* We allow an index of 0 for the no-elements case. * This makes for cleaner calling code which will often look like: * * elements = _cairo_array_index (array, 0); * for (i=0; i < num_elements; i++) { * ... use elements[i] here ... * } * * which in the num_elements==0 case gets the NULL pointer here, * but never dereferences it. */ if (index == 0 && array->num_elements == 0) return NULL; assert (index < array->num_elements); return array->elements + index * array->element_size; } /** * _cairo_array_index_const: * @array: a #cairo_array_t * Returns: A pointer to the object stored at @index. * * If the resulting value is assigned to a pointer to an object of the same * element_size as initially passed to _cairo_array_init() then that * pointer may be used for further direct indexing with []. For * example: * * <informalexample><programlisting> * cairo_array_t array; * const double *values; * * _cairo_array_init (&array, sizeof(double)); * ... calls to _cairo_array_append() here ... * * values = _cairo_array_index_const (&array, 0); * for (i = 0; i < _cairo_array_num_elements (&array); i++) * ... read values[i] here ... * </programlisting></informalexample> **/ const void * _cairo_array_index_const (const cairo_array_t *array, unsigned int index) { /* We allow an index of 0 for the no-elements case. * This makes for cleaner calling code which will often look like: * * elements = _cairo_array_index_const (array, 0); * for (i=0; i < num_elements; i++) { * ... read elements[i] here ... * } * * which in the num_elements==0 case gets the NULL pointer here, * but never dereferences it. */ if (index == 0 && array->num_elements == 0) return NULL; assert (index < array->num_elements); return array->elements + index * array->element_size; } /** * _cairo_array_copy_element: * @array: a #cairo_array_t * * Copy a single element out of the array from index @index into the * location pointed to by @dst. **/ void _cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst) { memcpy (dst, _cairo_array_index_const (array, index), array->element_size); } /** * _cairo_array_append: * @array: a #cairo_array_t * * Append a single item onto the array by growing the array by at * least one element, then copying element_size bytes from @element * into the array. The address of the resulting object within the * array can be determined with: * * _cairo_array_index (array, _cairo_array_num_elements (array) - 1); * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the * operation. **/ cairo_status_t _cairo_array_append (cairo_array_t *array, const void *element) { return _cairo_array_append_multiple (array, element, 1); } /** * _cairo_array_append_multiple: * @array: a #cairo_array_t * * Append one or more items onto the array by growing the array by * @num_elements, then copying @num_elements * element_size bytes from * @elements into the array. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the * operation. **/ cairo_status_t _cairo_array_append_multiple (cairo_array_t *array, const void *elements, unsigned int num_elements) { cairo_status_t status; void *dest; status = _cairo_array_allocate (array, num_elements, &dest); if (unlikely (status)) return status; memcpy (dest, elements, num_elements * array->element_size); return CAIRO_STATUS_SUCCESS; } /** * _cairo_array_allocate: * @array: a #cairo_array_t * * Allocate space at the end of the array for @num_elements additional * elements, providing the address of the new memory chunk in * @elements. This memory will be unitialized, but will be accounted * for in the return value of _cairo_array_num_elements(). * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the * operation. **/ cairo_status_t _cairo_array_allocate (cairo_array_t *array, unsigned int num_elements, void **elements) { cairo_status_t status; status = _cairo_array_grow_by (array, num_elements); if (unlikely (status)) return status; assert (array->num_elements + num_elements <= array->size); *elements = array->elements + array->num_elements * array->element_size; array->num_elements += num_elements; return CAIRO_STATUS_SUCCESS; } /** * _cairo_array_num_elements: * @array: a #cairo_array_t * Returns: The number of elements stored in @array. * * This space was left intentionally blank, but gtk-doc filled it. **/ unsigned int _cairo_array_num_elements (const cairo_array_t *array) { return array->num_elements; } /** * _cairo_array_size: * @array: a #cairo_array_t * Returns: The number of elements for which there is currently space * allocated in @array. * * This space was left intentionally blank, but gtk-doc filled it. **/ unsigned int _cairo_array_size (const cairo_array_t *array) { return array->size; } /** * _cairo_user_data_array_init: * @array: a #cairo_user_data_array_t * * Initializes a #cairo_user_data_array_t structure for future * use. After initialization, the array has no keys. Call * _cairo_user_data_array_fini() to free any allocated memory * when done using the array. **/ void _cairo_user_data_array_init (cairo_user_data_array_t *array) { _cairo_array_init (array, sizeof (cairo_user_data_slot_t)); } /** * _cairo_user_data_array_fini: * @array: a #cairo_user_data_array_t * * Destroys all current keys in the user data array and deallocates * any memory allocated for the array itself. **/ void _cairo_user_data_array_fini (cairo_user_data_array_t *array) { unsigned int num_slots; num_slots = array->num_elements; if (num_slots) { cairo_user_data_slot_t *slots; slots = _cairo_array_index (array, 0); while (num_slots--) { cairo_user_data_slot_t *s = &slots[num_slots]; if (s->user_data != NULL && s->destroy != NULL) s->destroy (s->user_data); } } _cairo_array_fini (array); } /** * _cairo_user_data_array_get_data: * @array: a #cairo_user_data_array_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Returns user data previously attached using the specified * key. If no user data has been attached with the given key this * function returns %NULL. * * Return value: the user data previously attached or %NULL. **/ void * _cairo_user_data_array_get_data (cairo_user_data_array_t *array, const cairo_user_data_key_t *key) { int i, num_slots; cairo_user_data_slot_t *slots; /* We allow this to support degenerate objects such as cairo_surface_nil. */ if (array == NULL) return NULL; num_slots = array->num_elements; slots = _cairo_array_index (array, 0); for (i = 0; i < num_slots; i++) { if (slots[i].key == key) return slots[i].user_data; } return NULL; } /** * _cairo_user_data_array_set_data: * @array: a #cairo_user_data_array_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach * @destroy: a #cairo_destroy_func_t which will be called when the * user data array is destroyed or when new user data is attached using the * same key. * * Attaches user data to a user data array. To remove user data, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. **/ cairo_status_t _cairo_user_data_array_set_data (cairo_user_data_array_t *array, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { cairo_status_t status; int i, num_slots; cairo_user_data_slot_t *slots, *slot, new_slot; if (user_data) { new_slot.key = key; new_slot.user_data = user_data; new_slot.destroy = destroy; } else { new_slot.key = NULL; new_slot.user_data = NULL; new_slot.destroy = NULL; } slot = NULL; num_slots = array->num_elements; slots = _cairo_array_index (array, 0); for (i = 0; i < num_slots; i++) { if (slots[i].key == key) { slot = &slots[i]; if (slot->destroy && slot->user_data) slot->destroy (slot->user_data); break; } if (user_data && slots[i].user_data == NULL) { slot = &slots[i]; /* Have to keep searching for an exact match */ } } if (slot) { *slot = new_slot; return CAIRO_STATUS_SUCCESS; } status = _cairo_array_append (array, &new_slot); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_user_data_array_copy (cairo_user_data_array_t *dst, const cairo_user_data_array_t *src) { /* discard any existing user-data */ if (dst->num_elements != 0) { _cairo_user_data_array_fini (dst); _cairo_user_data_array_init (dst); } return _cairo_array_append_multiple (dst, _cairo_array_index_const (src, 0), src->num_elements); } void _cairo_user_data_array_foreach (cairo_user_data_array_t *array, void (*func) (const void *key, void *elt, void *closure), void *closure) { cairo_user_data_slot_t *slots; int i, num_slots; num_slots = array->num_elements; slots = _cairo_array_index (array, 0); for (i = 0; i < num_slots; i++) { if (slots[i].user_data != NULL) func (slots[i].key, slots[i].user_data, closure); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-atomic-private.h��������������������������0000664�0000000�0000000�00000021654�12710376503�0026500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * Copyright © 2010 Andrea Canciani * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> * Andrea Canciani <ranma42@gmail.com> */ #ifndef CAIRO_ATOMIC_PRIVATE_H #define CAIRO_ATOMIC_PRIVATE_H # include "cairo-compiler-private.h" #if HAVE_CONFIG_H #include "config.h" #endif /* The autoconf on OpenBSD 4.5 produces the malformed constant name * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ #if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__) # define SIZEOF_VOID_P SIZEOF_VOID__ #endif CAIRO_BEGIN_DECLS #if HAVE_INTEL_ATOMIC_PRIMITIVES #define HAS_ATOMIC_OPS 1 typedef int cairo_atomic_int_t; #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER static cairo_always_inline cairo_atomic_int_t _cairo_atomic_int_get (cairo_atomic_int_t *x) { __sync_synchronize (); return *x; } static cairo_always_inline void * _cairo_atomic_ptr_get (void **x) { __sync_synchronize (); return *x; } #else # define _cairo_atomic_int_get(x) (*x) # define _cairo_atomic_ptr_get(x) (*x) #endif # define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) # define _cairo_atomic_int_dec(x) ((void) __sync_fetch_and_add(x, -1)) # define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv) # define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) #if SIZEOF_VOID_P==SIZEOF_INT typedef int cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG typedef long cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG_LONG typedef long long cairo_atomic_intptr_t; #else #error No matching integer pointer type #endif # define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ __sync_bool_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) # define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ _cairo_atomic_intptr_to_voidptr (__sync_val_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)) #endif #if HAVE_LIB_ATOMIC_OPS #include <atomic_ops.h> #define HAS_ATOMIC_OPS 1 typedef AO_t cairo_atomic_int_t; # define _cairo_atomic_int_get(x) (AO_load_full (x)) # define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) # define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x)) # define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv) #if SIZEOF_VOID_P==SIZEOF_INT typedef unsigned int cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG typedef unsigned long cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG_LONG typedef unsigned long long cairo_atomic_intptr_t; #else #error No matching integer pointer type #endif # define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x)) # define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) #endif #if HAVE_OS_ATOMIC_OPS #include <libkern/OSAtomic.h> #define HAS_ATOMIC_OPS 1 typedef int32_t cairo_atomic_int_t; # define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x)) # define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x)) # define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x)) # define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x) #if SIZEOF_VOID_P==4 typedef int32_t cairo_atomic_intptr_t; # define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) #elif SIZEOF_VOID_P==8 typedef int64_t cairo_atomic_intptr_t; # define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) #else #error No matching integer pointer type #endif # define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x)) #endif #ifndef HAS_ATOMIC_OPS #if SIZEOF_VOID_P==SIZEOF_INT typedef unsigned int cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG typedef unsigned long cairo_atomic_intptr_t; #elif SIZEOF_VOID_P==SIZEOF_LONG_LONG typedef unsigned long long cairo_atomic_intptr_t; #else #error No matching integer pointer type #endif typedef cairo_atomic_intptr_t cairo_atomic_int_t; cairo_private void _cairo_atomic_int_inc (cairo_atomic_int_t *x); #define _cairo_atomic_int_dec(x) _cairo_atomic_int_dec_and_test(x) cairo_private cairo_bool_t _cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x); cairo_private cairo_atomic_int_t _cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv); cairo_private void * _cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv); #define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv) #define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv) #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER cairo_private cairo_atomic_int_t _cairo_atomic_int_get (cairo_atomic_int_t *x); # define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) #else # define _cairo_atomic_int_get(x) (*x) # define _cairo_atomic_ptr_get(x) (*x) #endif #else /* Workaround GCC complaining about casts */ static cairo_always_inline void * _cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x) { return (void *) x; } static cairo_always_inline cairo_atomic_int_t _cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) { cairo_atomic_int_t curr; do { curr = _cairo_atomic_int_get (x); } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv)); return curr; } static cairo_always_inline void * _cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) { void *curr; do { curr = _cairo_atomic_ptr_get (x); } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv)); return curr; } #endif #ifndef _cairo_atomic_int_cmpxchg_return_old #define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv) #endif #ifndef _cairo_atomic_ptr_cmpxchg_return_old #define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv) #endif #ifndef _cairo_atomic_int_cmpxchg #define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv) #endif #ifndef _cairo_atomic_ptr_cmpxchg #define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv) #endif #define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) #define _cairo_atomic_uint_cmpxchg(x, oldv, newv) \ _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv) #define _cairo_status_set_error(status, err) do { \ int ret__; \ assert (err < CAIRO_STATUS_LAST_STATUS); \ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ * an unsigned integer instead, and about ignoring the return value. */ \ ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ (void) ret__; \ } while (0) CAIRO_END_DECLS #endif ������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-atomic.c����������������������������������0000664�0000000�0000000�00000005611�12710376503�0025016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-atomic-private.h" #include "cairo-mutex-private.h" #ifdef HAS_ATOMIC_OPS COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) || sizeof(void*) == sizeof(long) || sizeof(void*) == sizeof(long long)); #else void _cairo_atomic_int_inc (cairo_atomic_intptr_t *x) { CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); *x += 1; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); } cairo_bool_t _cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) { cairo_bool_t ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = --*x == 0; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); return ret; } cairo_atomic_intptr_t _cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv) { cairo_atomic_intptr_t ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = *x; if (ret == oldv) *x = newv; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); return ret; } void * _cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv) { void *ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = *x; if (ret == oldv) *x = newv; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); return ret; } #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER cairo_atomic_intptr_t _cairo_atomic_int_get (cairo_atomic_intptr_t *x) { cairo_atomic_intptr_t ret; CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); ret = *x; CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); return ret; } #endif #endif �����������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-backend-private.h�������������������������0000664�0000000�0000000�00000021454�12710376503�0026611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_BACKEND_PRIVATE_H #define CAIRO_BACKEND_PRIVATE_H #include "cairo-types-private.h" #include "cairo-private.h" typedef enum _cairo_backend_type { CAIRO_TYPE_DEFAULT, CAIRO_TYPE_SKIA, } cairo_backend_type_t; struct _cairo_backend { cairo_backend_type_t type; void (*destroy) (void *cr); cairo_surface_t *(*get_original_target) (void *cr); cairo_surface_t *(*get_current_target) (void *cr); cairo_status_t (*save) (void *cr); cairo_status_t (*restore) (void *cr); cairo_status_t (*push_group) (void *cr, cairo_content_t content); cairo_pattern_t *(*pop_group) (void *cr); cairo_status_t (*set_source_rgba) (void *cr, double red, double green, double blue, double alpha); cairo_status_t (*set_source_surface) (void *cr, cairo_surface_t *surface, double x, double y); cairo_status_t (*set_source) (void *cr, cairo_pattern_t *source); cairo_pattern_t *(*get_source) (void *cr); cairo_status_t (*set_antialias) (void *cr, cairo_antialias_t antialias); cairo_status_t (*set_dash) (void *cr, const double *dashes, int num_dashes, double offset); cairo_status_t (*set_fill_rule) (void *cr, cairo_fill_rule_t fill_rule); cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap); cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join); cairo_status_t (*set_line_width) (void *cr, double line_width); cairo_status_t (*set_miter_limit) (void *cr, double limit); cairo_status_t (*set_opacity) (void *cr, double opacity); cairo_status_t (*set_operator) (void *cr, cairo_operator_t op); cairo_status_t (*set_tolerance) (void *cr, double tolerance); cairo_antialias_t (*get_antialias) (void *cr); void (*get_dash) (void *cr, double *dashes, int *num_dashes, double *offset); cairo_fill_rule_t (*get_fill_rule) (void *cr); cairo_line_cap_t (*get_line_cap) (void *cr); cairo_line_join_t (*get_line_join) (void *cr); double (*get_line_width) (void *cr); double (*get_miter_limit) (void *cr); double (*get_opacity) (void *cr); cairo_operator_t (*get_operator) (void *cr); double (*get_tolerance) (void *cr); cairo_status_t (*translate) (void *cr, double tx, double ty); cairo_status_t (*scale) (void *cr, double sx, double sy); cairo_status_t (*rotate) (void *cr, double theta); cairo_status_t (*transform) (void *cr, const cairo_matrix_t *matrix); cairo_status_t (*set_matrix) (void *cr, const cairo_matrix_t *matrix); cairo_status_t (*set_identity_matrix) (void *cr); void (*get_matrix) (void *cr, cairo_matrix_t *matrix); void (*user_to_device) (void *cr, double *x, double *y); void (*user_to_device_distance) (void *cr, double *x, double *y); void (*device_to_user) (void *cr, double *x, double *y); void (*device_to_user_distance) (void *cr, double *x, double *y); void (*user_to_backend) (void *cr, double *x, double *y); void (*user_to_backend_distance) (void *cr, double *x, double *y); void (*backend_to_user) (void *cr, double *x, double *y); void (*backend_to_user_distance) (void *cr, double *x, double *y); cairo_status_t (*new_path) (void *cr); cairo_status_t (*new_sub_path) (void *cr); cairo_status_t (*move_to) (void *cr, double x, double y); cairo_status_t (*rel_move_to) (void *cr, double dx, double dy); cairo_status_t (*line_to) (void *cr, double x, double y); cairo_status_t (*rel_line_to) (void *cr, double dx, double dy); cairo_status_t (*curve_to) (void *cr, double x1, double y1, double x2, double y2, double x3, double y3); cairo_status_t (*rel_curve_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3); cairo_status_t (*arc_to) (void *cr, double x1, double y1, double x2, double y2, double radius); cairo_status_t (*rel_arc_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double radius); cairo_status_t (*close_path) (void *cr); cairo_status_t (*arc) (void *cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward); cairo_status_t (*rectangle) (void *cr, double x, double y, double width, double height); void (*path_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); cairo_bool_t (*has_current_point) (void *cr); cairo_bool_t (*get_current_point) (void *cr, double *x, double *y); cairo_path_t *(*copy_path) (void *cr); cairo_path_t *(*copy_path_flat) (void *cr); cairo_status_t (*append_path) (void *cr, const cairo_path_t *path); cairo_status_t (*stroke_to_path) (void *cr); cairo_status_t (*clip) (void *cr); cairo_status_t (*clip_preserve) (void *cr); cairo_status_t (*in_clip) (void *cr, double x, double y, cairo_bool_t *inside); cairo_status_t (*clip_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); cairo_status_t (*reset_clip) (void *cr); cairo_rectangle_list_t *(*clip_copy_rectangle_list) (void *cr); cairo_status_t (*paint) (void *cr); cairo_status_t (*paint_with_alpha) (void *cr, double opacity); cairo_status_t (*mask) (void *cr, cairo_pattern_t *pattern); cairo_status_t (*stroke) (void *cr); cairo_status_t (*stroke_preserve) (void *cr); cairo_status_t (*in_stroke) (void *cr, double x, double y, cairo_bool_t *inside); cairo_status_t (*stroke_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); cairo_status_t (*fill) (void *cr); cairo_status_t (*fill_preserve) (void *cr); cairo_status_t (*in_fill) (void *cr, double x, double y, cairo_bool_t *inside); cairo_status_t (*fill_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); cairo_status_t (*set_font_face) (void *cr, cairo_font_face_t *font_face); cairo_font_face_t *(*get_font_face) (void *cr); cairo_status_t (*set_font_size) (void *cr, double size); cairo_status_t (*set_font_matrix) (void *cr, const cairo_matrix_t *matrix); void (*get_font_matrix) (void *cr, cairo_matrix_t *matrix); cairo_status_t (*set_font_options) (void *cr, const cairo_font_options_t *options); void (*get_font_options) (void *cr, cairo_font_options_t *options); cairo_status_t (*set_scaled_font) (void *cr, cairo_scaled_font_t *scaled_font); cairo_scaled_font_t *(*get_scaled_font) (void *cr); cairo_status_t (*font_extents) (void *cr, cairo_font_extents_t *extents); cairo_status_t (*glyphs) (void *cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_glyph_text_info_t *info); cairo_status_t (*glyph_path) (void *cr, const cairo_glyph_t *glyphs, int num_glyphs); cairo_status_t (*glyph_extents) (void *cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); cairo_status_t (*copy_page) (void *cr); cairo_status_t (*show_page) (void *cr); }; static inline void _cairo_backend_to_user (cairo_t *cr, double *x, double *y) { cr->backend->backend_to_user (cr, x, y); } static inline void _cairo_backend_to_user_distance (cairo_t *cr, double *x, double *y) { cr->backend->backend_to_user_distance (cr, x, y); } static inline void _cairo_user_to_backend (cairo_t *cr, double *x, double *y) { cr->backend->user_to_backend (cr, x, y); } static inline void _cairo_user_to_backend_distance (cairo_t *cr, double *x, double *y) { cr->backend->user_to_backend_distance (cr, x, y); } #endif /* CAIRO_BACKEND_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-base64-stream.c���������������������������0000664�0000000�0000000�00000010430�12710376503�0026112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr> * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Author(s): * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" typedef struct _cairo_base64_stream { cairo_output_stream_t base; cairo_output_stream_t *output; unsigned int in_mem; unsigned int trailing; unsigned char src[3]; } cairo_base64_stream_t; static char const base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static cairo_status_t _cairo_base64_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { cairo_base64_stream_t * stream = (cairo_base64_stream_t *) base; unsigned char *src = stream->src; unsigned int i; if (stream->in_mem + length < 3) { for (i = 0; i < length; i++) { src[i + stream->in_mem] = *data++; } stream->in_mem += length; return CAIRO_STATUS_SUCCESS; } do { unsigned char dst[4]; for (i = stream->in_mem; i < 3; i++) { src[i] = *data++; length--; } stream->in_mem = 0; dst[0] = base64_table[src[0] >> 2]; dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; dst[3] = base64_table[src[2] & 0xfc >> 2]; /* Special case for the last missing bits */ switch (stream->trailing) { case 2: dst[2] = '='; case 1: dst[3] = '='; default: break; } _cairo_output_stream_write (stream->output, dst, 4); } while (length >= 3); for (i = 0; i < length; i++) { src[i] = *data++; } stream->in_mem = length; return _cairo_output_stream_get_status (stream->output); } static cairo_status_t _cairo_base64_stream_close (cairo_output_stream_t *base) { cairo_base64_stream_t *stream = (cairo_base64_stream_t *) base; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (stream->in_mem > 0) { memset (stream->src + stream->in_mem, 0, 3 - stream->in_mem); stream->trailing = 3 - stream->in_mem; stream->in_mem = 3; status = _cairo_base64_stream_write (base, NULL, 0); } return status; } cairo_output_stream_t * _cairo_base64_stream_create (cairo_output_stream_t *output) { cairo_base64_stream_t *stream; if (output->status) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (cairo_base64_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _cairo_base64_stream_write, NULL, _cairo_base64_stream_close); stream->output = output; stream->in_mem = 0; stream->trailing = 0; return &stream->base; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-base85-stream.c���������������������������0000664�0000000�0000000�00000010145�12710376503�0026120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Author(s): * Kristian Høgsberg <krh@redhat.com> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" typedef struct _cairo_base85_stream { cairo_output_stream_t base; cairo_output_stream_t *output; unsigned char four_tuple[4]; int pending; } cairo_base85_stream_t; static void _expand_four_tuple_to_five (unsigned char four_tuple[4], unsigned char five_tuple[5], cairo_bool_t *all_zero) { uint32_t value; int digit, i; value = four_tuple[0] << 24 | four_tuple[1] << 16 | four_tuple[2] << 8 | four_tuple[3]; if (all_zero) *all_zero = TRUE; for (i = 0; i < 5; i++) { digit = value % 85; if (digit != 0 && all_zero) *all_zero = FALSE; five_tuple[4-i] = digit + 33; value = value / 85; } } static cairo_status_t _cairo_base85_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; const unsigned char *ptr = data; unsigned char five_tuple[5]; cairo_bool_t is_zero; while (length) { stream->four_tuple[stream->pending++] = *ptr++; length--; if (stream->pending == 4) { _expand_four_tuple_to_five (stream->four_tuple, five_tuple, &is_zero); if (is_zero) _cairo_output_stream_write (stream->output, "z", 1); else _cairo_output_stream_write (stream->output, five_tuple, 5); stream->pending = 0; } } return _cairo_output_stream_get_status (stream->output); } static cairo_status_t _cairo_base85_stream_close (cairo_output_stream_t *base) { cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; unsigned char five_tuple[5]; if (stream->pending) { memset (stream->four_tuple + stream->pending, 0, 4 - stream->pending); _expand_four_tuple_to_five (stream->four_tuple, five_tuple, NULL); _cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1); } return _cairo_output_stream_get_status (stream->output); } cairo_output_stream_t * _cairo_base85_stream_create (cairo_output_stream_t *output) { cairo_base85_stream_t *stream; if (output->status) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (cairo_base85_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _cairo_base85_stream_write, NULL, _cairo_base85_stream_close); stream->output = output; stream->pending = 0; return &stream->base; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-bentley-ottmann-rectangular.c�������������0000664�0000000�0000000�00000054575�12710376503�0031204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-combsort-inline.h" #include "cairo-list-private.h" #include "cairo-traps-private.h" #include <setjmp.h> typedef struct _rectangle rectangle_t; typedef struct _edge edge_t; struct _edge { edge_t *next, *prev; edge_t *right; cairo_fixed_t x, top; int dir; }; struct _rectangle { edge_t left, right; int32_t top, bottom; }; #define UNROLL3(x) x x x /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef struct _sweep_line { rectangle_t **rectangles; rectangle_t **stop; edge_t head, tail, *insert, *cursor; int32_t current_y; int32_t last_y; int stop_size; int32_t insert_x; cairo_fill_rule_t fill_rule; cairo_bool_t do_traps; void *container; jmp_buf unwind; } sweep_line_t; #define DEBUG_TRAPS 0 #if DEBUG_TRAPS static void dump_traps (cairo_traps_t *traps, const char *filename) { FILE *file; int n; if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) return; file = fopen (filename, "a"); if (file != NULL) { for (n = 0; n < traps->num_traps; n++) { fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", traps->traps[n].top, traps->traps[n].bottom, traps->traps[n].left.p1.x, traps->traps[n].left.p1.y, traps->traps[n].left.p2.x, traps->traps[n].left.p2.y, traps->traps[n].right.p1.x, traps->traps[n].right.p1.y, traps->traps[n].right.p2.x, traps->traps[n].right.p2.y); } fprintf (file, "\n"); fclose (file); } } #else #define dump_traps(traps, filename) #endif static inline int rectangle_compare_start (const rectangle_t *a, const rectangle_t *b) { return a->top - b->top; } static inline int rectangle_compare_stop (const rectangle_t *a, const rectangle_t *b) { return a->bottom - b->bottom; } static inline void pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) { rectangle_t **elements; int i, parent; elements = sweep->stop; for (i = ++sweep->stop_size; i != PQ_FIRST_ENTRY && rectangle_compare_stop (rectangle, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = rectangle; } static inline void rectangle_pop_stop (sweep_line_t *sweep) { rectangle_t **elements = sweep->stop; rectangle_t *tail; int child, i; tail = elements[sweep->stop_size--]; if (sweep->stop_size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size; i = child) { if (child != sweep->stop_size && rectangle_compare_stop (elements[child+1], elements[child]) < 0) { child++; } if (rectangle_compare_stop (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline rectangle_t * rectangle_pop_start (sweep_line_t *sweep_line) { return *sweep_line->rectangles++; } static inline rectangle_t * rectangle_peek_stop (sweep_line_t *sweep_line) { return sweep_line->stop[PQ_FIRST_ENTRY]; } CAIRO_COMBSORT_DECLARE (_rectangle_sort, rectangle_t *, rectangle_compare_start) static void sweep_line_init (sweep_line_t *sweep_line, rectangle_t **rectangles, int num_rectangles, cairo_fill_rule_t fill_rule, cairo_bool_t do_traps, void *container) { rectangles[-2] = NULL; rectangles[-1] = NULL; rectangles[num_rectangles] = NULL; sweep_line->rectangles = rectangles; sweep_line->stop = rectangles - 2; sweep_line->stop_size = 0; sweep_line->insert = NULL; sweep_line->insert_x = INT_MAX; sweep_line->cursor = &sweep_line->tail; sweep_line->head.dir = 0; sweep_line->head.x = INT32_MIN; sweep_line->head.right = NULL; sweep_line->head.prev = NULL; sweep_line->head.next = &sweep_line->tail; sweep_line->tail.prev = &sweep_line->head; sweep_line->tail.next = NULL; sweep_line->tail.right = NULL; sweep_line->tail.x = INT32_MAX; sweep_line->tail.dir = 0; sweep_line->current_y = INT32_MIN; sweep_line->last_y = INT32_MIN; sweep_line->fill_rule = fill_rule; sweep_line->container = container; sweep_line->do_traps = do_traps; } static void edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot) { cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (left->top < bot)) { if (sweep_line->do_traps) { cairo_line_t _left = { { left->x, left->top }, { left->x, bot }, }, _right = { { left->right->x, left->top }, { left->right->x, bot }, }; _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right); status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container); } else { cairo_box_t box; box.p1.x = left->x; box.p1.y = left->top; box.p2.x = left->right->x; box.p2.y = bot; status = _cairo_boxes_add (sweep_line->container, CAIRO_ANTIALIAS_DEFAULT, &box); } } if (unlikely (status)) longjmp (sweep_line->unwind, status); left->right = NULL; } /* Start a new trapezoid at the given top y coordinate, whose edges * are `edge' and `edge->next'. If `edge' already has a trapezoid, * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ static inline void edge_start_or_continue_box (sweep_line_t *sweep_line, edge_t *left, edge_t *right, int top) { if (left->right == right) return; if (left->right != NULL) { if (left->right->x == right->x) { /* continuation on right, so just swap edges */ left->right = right; return; } edge_end_box (sweep_line, left, top); } if (left->x != right->x) { left->top = top; left->right = right; } } /* * Merge two sorted edge lists. * Input: * - head_a: The head of the first list. * - head_b: The head of the second list; head_b cannot be NULL. * Output: * Returns the head of the merged list. * * Implementation notes: * To make it fast (in particular, to reduce to an insertion sort whenever * one of the two input lists only has a single element) we iterate through * a list until its head becomes greater than the head of the other list, * then we switch their roles. As soon as one of the two lists is empty, we * just attach the other one to the current list and exit. * Writes to memory are only needed to "switch" lists (as it also requires * attaching to the output list the list which we will be iterating next) and * to attach the last non-empty list. */ static edge_t * merge_sorted_edges (edge_t *head_a, edge_t *head_b) { edge_t *head, *prev; int32_t x; prev = head_a->prev; if (head_a->x <= head_b->x) { head = head_a; } else { head_b->prev = prev; head = head_b; goto start_with_b; } do { x = head_b->x; while (head_a != NULL && head_a->x <= x) { prev = head_a; head_a = head_a->next; } head_b->prev = prev; prev->next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x; while (head_b != NULL && head_b->x <= x) { prev = head_b; head_b = head_b->next; } head_a->prev = prev; prev->next = head_a; if (head_b == NULL) return head; } while (1); } /* * Sort (part of) a list. * Input: * - list: The list to be sorted; list cannot be NULL. * - limit: Recursion limit. * Output: * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the * input list; if the input list has fewer elements, head_out be a sorted list * containing all the elements of the input list. * Returns the head of the list of unprocessed elements (NULL if the sorted list contains * all the elements of the input list). * * Implementation notes: * Special case single element list, unroll/inline the sorting of the first two elements. * Some tail recursion is used since we iterate on the bottom-up solution of the problem * (we start with a small sorted list and keep merging other lists of the same size to it). */ static edge_t * sort_edges (edge_t *list, unsigned int level, edge_t **head_out) { edge_t *head_other, *remaining; unsigned int i; head_other = list->next; if (head_other == NULL) { *head_out = list; return NULL; } remaining = head_other->next; if (list->x <= head_other->x) { *head_out = list; head_other->next = NULL; } else { *head_out = head_other; head_other->prev = list->prev; head_other->next = list; list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } return remaining; } static edge_t * merge_unsorted_edges (edge_t *head, edge_t *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); return merge_sorted_edges (head, unsorted); } static void active_edges_insert (sweep_line_t *sweep) { edge_t *prev; int x; x = sweep->insert_x; prev = sweep->cursor; if (prev->x > x) { do { prev = prev->prev; } while (prev->x > x); } else { while (prev->next->x < x) prev = prev->next; } prev->next = merge_unsorted_edges (prev->next, sweep->insert); sweep->cursor = sweep->insert; sweep->insert = NULL; sweep->insert_x = INT_MAX; } static inline void active_edges_to_traps (sweep_line_t *sweep) { int top = sweep->current_y; edge_t *pos; if (sweep->last_y == sweep->current_y) return; if (sweep->insert) active_edges_insert (sweep); pos = sweep->head.next; if (pos == &sweep->tail) return; if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) { do { edge_t *left, *right; int winding; left = pos; winding = left->dir; right = left->next; /* Check if there is a co-linear edge with an existing trap */ while (right->x == left->x) { if (right->right != NULL) { assert (left->right == NULL); /* continuation on left */ left->top = right->top; left->right = right->right; right->right = NULL; } winding += right->dir; right = right->next; } if (winding == 0) { if (left->right != NULL) edge_end_box (sweep, left, top); pos = right; continue; } do { /* End all subsumed traps */ if (unlikely (right->right != NULL)) edge_end_box (sweep, right, top); /* Greedily search for the closing edge, so that we generate * the * maximal span width with the minimal number of * boxes. */ winding += right->dir; if (winding == 0 && right->x != right->next->x) break; right = right->next; } while (TRUE); edge_start_or_continue_box (sweep, left, right, top); pos = right->next; } while (pos != &sweep->tail); } else { do { edge_t *right = pos->next; int count = 0; do { /* End all subsumed traps */ if (unlikely (right->right != NULL)) edge_end_box (sweep, right, top); /* skip co-linear edges */ if (++count & 1 && right->x != right->next->x) break; right = right->next; } while (TRUE); edge_start_or_continue_box (sweep, pos, right, top); pos = right->next; } while (pos != &sweep->tail); } sweep->last_y = sweep->current_y; } static inline void sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge) { if (edge->right != NULL) { edge_t *next = edge->next; if (next->x == edge->x) { next->top = edge->top; next->right = edge->right; } else edge_end_box (sweep, edge, sweep->current_y); } if (sweep->cursor == edge) sweep->cursor = edge->prev; edge->prev->next = edge->next; edge->next->prev = edge->prev; } static inline cairo_bool_t sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle) { cairo_bool_t update; update = TRUE; if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && rectangle->left.prev->dir == rectangle->left.dir) { update = rectangle->left.next != &rectangle->right; } sweep_line_delete_edge (sweep, &rectangle->left); sweep_line_delete_edge (sweep, &rectangle->right); rectangle_pop_stop (sweep); return update; } static inline void sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) { if (sweep->insert) sweep->insert->prev = &rectangle->right; rectangle->right.next = sweep->insert; rectangle->right.prev = &rectangle->left; rectangle->left.next = &rectangle->right; rectangle->left.prev = NULL; sweep->insert = &rectangle->left; if (rectangle->left.x < sweep->insert_x) sweep->insert_x = rectangle->left.x; pqueue_push (sweep, rectangle); } static cairo_status_t _cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, int num_rectangles, cairo_fill_rule_t fill_rule, cairo_bool_t do_traps, void *container) { sweep_line_t sweep_line; rectangle_t *rectangle; cairo_status_t status; cairo_bool_t update = FALSE; sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule, do_traps, container); if ((status = setjmp (sweep_line.unwind))) return status; rectangle = rectangle_pop_start (&sweep_line); do { if (rectangle->top != sweep_line.current_y) { rectangle_t *stop; stop = rectangle_peek_stop (&sweep_line); while (stop != NULL && stop->bottom < rectangle->top) { if (stop->bottom != sweep_line.current_y) { if (update) { active_edges_to_traps (&sweep_line); update = FALSE; } sweep_line.current_y = stop->bottom; } update |= sweep_line_delete (&sweep_line, stop); stop = rectangle_peek_stop (&sweep_line); } if (update) { active_edges_to_traps (&sweep_line); update = FALSE; } sweep_line.current_y = rectangle->top; } do { sweep_line_insert (&sweep_line, rectangle); } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL && sweep_line.current_y == rectangle->top); update = TRUE; } while (rectangle); while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { if (rectangle->bottom != sweep_line.current_y) { if (update) { active_edges_to_traps (&sweep_line); update = FALSE; } sweep_line.current_y = rectangle->bottom; } update |= sweep_line_delete (&sweep_line, rectangle); } return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; rectangle_t *rectangles, **rectangles_ptrs; cairo_status_t status; int i; if (unlikely (traps->num_traps <= 1)) return CAIRO_STATUS_SUCCESS; assert (traps->is_rectangular); dump_traps (traps, "bo-rects-traps-in.txt"); rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); } for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { rectangles[i].left.x = traps->traps[i].left.p1.x; rectangles[i].left.dir = 1; rectangles[i].right.x = traps->traps[i].right.p1.x; rectangles[i].right.dir = -1; } else { rectangles[i].right.x = traps->traps[i].left.p1.x; rectangles[i].right.dir = 1; rectangles[i].left.x = traps->traps[i].right.p1.x; rectangles[i].left.dir = -1; } rectangles[i].left.right = NULL; rectangles[i].right.right = NULL; rectangles[i].top = traps->traps[i].top; rectangles[i].bottom = traps->traps[i].bottom; rectangles_ptrs[i+2] = &rectangles[i]; } /* XXX incremental sort */ _rectangle_sort (rectangles_ptrs+2, i); _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; traps->is_rectangular = TRUE; if (rectangles != stack_rectangles) free (rectangles); dump_traps (traps, "bo-rects-traps-out.txt"); return status; } cairo_status_t _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_fill_rule_t fill_rule, cairo_boxes_t *out) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; rectangle_t *rectangles, **rectangles_ptrs; rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ]; rectangle_t **rectangles_chain = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i, j, y_min, y_max; if (unlikely (in->num_boxes == 0)) { _cairo_boxes_clear (out); return CAIRO_STATUS_SUCCESS; } if (in->num_boxes == 1) { if (in == out) { cairo_box_t *box = &in->chunks.base[0]; if (box->p1.x > box->p2.x) { cairo_fixed_t tmp = box->p1.x; box->p1.x = box->p2.x; box->p2.x = tmp; } } else { cairo_box_t box = in->chunks.base[0]; if (box.p1.x > box.p2.x) { cairo_fixed_t tmp = box.p1.x; box.p1.x = box.p2.x; box.p2.x = tmp; } _cairo_boxes_clear (out); status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } y_min = INT_MAX; y_max = INT_MIN; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { if (box[i].p1.y < y_min) y_min = box[i].p1.y; if (box[i].p1.y > y_max) y_max = box[i].p1.y; } } y_min = _cairo_fixed_integer_floor (y_min); y_max = _cairo_fixed_integer_floor (y_max) + 1; y_max -= y_min; if (y_max < in->num_boxes) { rectangles_chain = stack_rectangles_chain; if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) { rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *)); if (unlikely (rectangles_chain == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); } rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) { if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); } j = 0; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int h; if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; rectangles[j].right.x = box[i].p2.x; rectangles[j].right.dir = -1; } else { rectangles[j].right.x = box[i].p1.x; rectangles[j].right.dir = 1; rectangles[j].left.x = box[i].p2.x; rectangles[j].left.dir = -1; } rectangles[j].left.right = NULL; rectangles[j].right.right = NULL; rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; if (rectangles_chain) { h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min; rectangles[j].left.next = (edge_t *)rectangles_chain[h]; rectangles_chain[h] = &rectangles[j]; } else { rectangles_ptrs[j+2] = &rectangles[j]; } j++; } } if (rectangles_chain) { j = 2; for (y_min = 0; y_min < y_max; y_min++) { rectangle_t *r; int start = j; for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next) rectangles_ptrs[j++] = r; if (j > start + 1) _rectangle_sort (rectangles_ptrs + start, j - start); } if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); j -= 2; } else { _rectangle_sort (rectangles_ptrs + 2, j); } _cairo_boxes_clear (out); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, fill_rule, FALSE, out); if (rectangles != stack_rectangles) free (rectangles); return status; } �����������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-bentley-ottmann-rectilinear.c�������������0000664�0000000�0000000�00000037257�12710376503�0031174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-combsort-inline.h" #include "cairo-error-private.h" #include "cairo-traps-private.h" typedef struct _cairo_bo_edge cairo_bo_edge_t; typedef struct _cairo_bo_trap cairo_bo_trap_t; /* A deferred trapezoid of an edge */ struct _cairo_bo_trap { cairo_bo_edge_t *right; int32_t top; }; struct _cairo_bo_edge { cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; cairo_bo_trap_t deferred_trap; }; typedef enum { CAIRO_BO_EVENT_TYPE_START, CAIRO_BO_EVENT_TYPE_STOP } cairo_bo_event_type_t; typedef struct _cairo_bo_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t *edge; } cairo_bo_event_t; typedef struct _cairo_bo_sweep_line { cairo_bo_event_t **events; cairo_bo_edge_t *head; cairo_bo_edge_t *stopped; int32_t current_y; cairo_bo_edge_t *current_edge; } cairo_bo_sweep_line_t; static inline int _cairo_point_compare (const cairo_point_t *a, const cairo_point_t *b) { int cmp; cmp = a->y - b->y; if (likely (cmp)) return cmp; return a->x - b->x; } static inline int _cairo_bo_edge_compare (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { int cmp; cmp = a->edge.line.p1.x - b->edge.line.p1.x; if (likely (cmp)) return cmp; return b->edge.bottom - a->edge.bottom; } static inline int cairo_bo_event_compare (const cairo_bo_event_t *a, const cairo_bo_event_t *b) { int cmp; cmp = _cairo_point_compare (&a->point, &b->point); if (likely (cmp)) return cmp; cmp = a->type - b->type; if (cmp) return cmp; return a - b; } static inline cairo_bo_event_t * _cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line) { return *sweep_line->events++; } CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, cairo_bo_event_t *, cairo_bo_event_compare) static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, cairo_bo_event_t **events, int num_events) { _cairo_bo_event_queue_sort (events, num_events); events[num_events] = NULL; sweep_line->events = events; sweep_line->head = NULL; sweep_line->current_y = INT32_MIN; sweep_line->current_edge = NULL; } static void _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (sweep_line->current_edge != NULL) { cairo_bo_edge_t *prev, *next; int cmp; cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge); if (cmp < 0) { prev = sweep_line->current_edge; next = prev->next; while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0) prev = next, next = prev->next; prev->next = edge; edge->prev = prev; edge->next = next; if (next != NULL) next->prev = edge; } else if (cmp > 0) { next = sweep_line->current_edge; prev = next->prev; while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0) next = prev, prev = next->prev; next->prev = edge; edge->next = next; edge->prev = prev; if (prev != NULL) prev->next = edge; else sweep_line->head = edge; } else { prev = sweep_line->current_edge; edge->prev = prev; edge->next = prev->next; if (prev->next != NULL) prev->next->prev = edge; prev->next = edge; } } else { sweep_line->head = edge; } sweep_line->current_edge = edge; } static void _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (edge->prev != NULL) edge->prev->next = edge->next; else sweep_line->head = edge->next; if (edge->next != NULL) edge->next->prev = edge->prev; if (sweep_line->current_edge == edge) sweep_line->current_edge = edge->prev ? edge->prev : edge->next; } static inline cairo_bool_t edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { return a->edge.line.p1.x == b->edge.line.p1.x; } static cairo_status_t _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, int32_t bot, cairo_bool_t do_traps, void *container) { cairo_bo_trap_t *trap = &left->deferred_trap; cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (trap->top < bot)) { if (do_traps) { _cairo_traps_add_trap (container, trap->top, bot, &left->edge.line, &trap->right->edge.line); status = _cairo_traps_status ((cairo_traps_t *) container); } else { cairo_box_t box; box.p1.x = left->edge.line.p1.x; box.p1.y = trap->top; box.p2.x = trap->right->edge.line.p1.x; box.p2.y = bot; status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box); } } trap->right = NULL; return status; } /* Start a new trapezoid at the given top y coordinate, whose edges * are `edge' and `edge->next'. If `edge' already has a trapezoid, * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ static inline cairo_status_t _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, cairo_bo_edge_t *right, int top, cairo_bool_t do_traps, void *container) { cairo_status_t status; if (left->deferred_trap.right == right) return CAIRO_STATUS_SUCCESS; if (left->deferred_trap.right != NULL) { if (right != NULL && edges_collinear (left->deferred_trap.right, right)) { /* continuation on right, so just swap edges */ left->deferred_trap.right = right; return CAIRO_STATUS_SUCCESS; } status = _cairo_bo_edge_end_trap (left, top, do_traps, container); if (unlikely (status)) return status; } if (right != NULL && ! edges_collinear (left, right)) { left->deferred_trap.top = top; left->deferred_trap.right = right; } return CAIRO_STATUS_SUCCESS; } static inline cairo_status_t _active_edges_to_traps (cairo_bo_edge_t *left, int32_t top, cairo_fill_rule_t fill_rule, cairo_bool_t do_traps, void *container) { cairo_bo_edge_t *right; cairo_status_t status; if (fill_rule == CAIRO_FILL_RULE_WINDING) { while (left != NULL) { int in_out; /* Greedily search for the closing edge, so that we generate the * maximal span width with the minimal number of trapezoids. */ in_out = left->edge.dir; /* Check if there is a co-linear edge with an existing trap */ right = left->next; if (left->deferred_trap.right == NULL) { while (right != NULL && right->deferred_trap.right == NULL) right = right->next; if (right != NULL && edges_collinear (left, right)) { /* continuation on left */ left->deferred_trap = right->deferred_trap; right->deferred_trap.right = NULL; } } /* End all subsumed traps */ right = left->next; while (right != NULL) { if (right->deferred_trap.right != NULL) { status = _cairo_bo_edge_end_trap (right, top, do_traps, container); if (unlikely (status)) return status; } in_out += right->edge.dir; if (in_out == 0) { /* skip co-linear edges */ if (right->next == NULL || ! edges_collinear (right, right->next)) { break; } } right = right->next; } status = _cairo_bo_edge_start_or_continue_trap (left, right, top, do_traps, container); if (unlikely (status)) return status; left = right; if (left != NULL) left = left->next; } } else { while (left != NULL) { int in_out = 0; right = left->next; while (right != NULL) { if (right->deferred_trap.right != NULL) { status = _cairo_bo_edge_end_trap (right, top, do_traps, container); if (unlikely (status)) return status; } if ((in_out++ & 1) == 0) { cairo_bo_edge_t *next; cairo_bool_t skip = FALSE; /* skip co-linear edges */ next = right->next; if (next != NULL) skip = edges_collinear (right, next); if (! skip) break; } right = right->next; } status = _cairo_bo_edge_start_or_continue_trap (left, right, top, do_traps, container); if (unlikely (status)) return status; left = right; if (left != NULL) left = left->next; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events, int num_events, cairo_fill_rule_t fill_rule, cairo_bool_t do_traps, void *container) { cairo_bo_sweep_line_t sweep_line; cairo_bo_event_t *event; cairo_status_t status; _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events); while ((event = _cairo_bo_event_dequeue (&sweep_line))) { if (event->point.y != sweep_line.current_y) { status = _active_edges_to_traps (sweep_line.head, sweep_line.current_y, fill_rule, do_traps, container); if (unlikely (status)) return status; sweep_line.current_y = event->point.y; } switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: _cairo_bo_sweep_line_insert (&sweep_line, event->edge); break; case CAIRO_BO_EVENT_TYPE_STOP: _cairo_bo_sweep_line_delete (&sweep_line, event->edge); if (event->edge->deferred_trap.right != NULL) { status = _cairo_bo_edge_end_trap (event->edge, sweep_line.current_y, do_traps, container); if (unlikely (status)) return status; } break; } } return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes) { cairo_status_t status; cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; cairo_bo_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; cairo_bo_edge_t *edges; int num_events; int i, j; if (unlikely (polygon->num_edges == 0)) return CAIRO_STATUS_SUCCESS; num_events = 2 * polygon->num_edges; events = stack_events; event_ptrs = stack_event_ptrs; edges = stack_edges; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_event_t) + sizeof (cairo_bo_edge_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); } for (i = j = 0; i < polygon->num_edges; i++) { edges[i].edge = polygon->edges[i]; edges[i].deferred_trap.right = NULL; edges[i].prev = NULL; edges[i].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = polygon->edges[i].top; events[j].point.x = polygon->edges[i].line.p1.x; events[j].edge = &edges[i]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = polygon->edges[i].bottom; events[j].point.x = polygon->edges[i].line.p1.x; events[j].edge = &edges[i]; j++; } status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, fill_rule, FALSE, boxes); if (events != stack_events) free (events); return status; } cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; cairo_bo_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; cairo_bo_edge_t *edges; cairo_status_t status; int i, j, k; if (unlikely (traps->num_traps == 0)) return CAIRO_STATUS_SUCCESS; assert (traps->is_rectilinear); i = 4 * traps->num_traps; events = stack_events; event_ptrs = stack_event_ptrs; edges = stack_edges; if (i > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (i, sizeof (cairo_bo_event_t) + sizeof (cairo_bo_edge_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + i); edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); } for (i = j = k = 0; i < traps->num_traps; i++) { edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].left; edges[k].edge.dir = 1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; k++; edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].right; edges[k].edge.dir = -1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; k++; } _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; if (events != stack_events) free (events); return status; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-bentley-ottmann.c�������������������������0000664�0000000�0000000�00000164153�12710376503�0026671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-inline.h" #include "cairo-traps-private.h" #define DEBUG_PRINT_STATE 0 #define DEBUG_EVENTS 0 #define DEBUG_TRAPS 0 typedef cairo_point_t cairo_bo_point32_t; typedef struct _cairo_bo_intersect_ordinate { int32_t ordinate; enum { EXACT, INEXACT } exactness; } cairo_bo_intersect_ordinate_t; typedef struct _cairo_bo_intersect_point { cairo_bo_intersect_ordinate_t x; cairo_bo_intersect_ordinate_t y; } cairo_bo_intersect_point_t; typedef struct _cairo_bo_edge cairo_bo_edge_t; typedef struct _cairo_bo_trap cairo_bo_trap_t; /* A deferred trapezoid of an edge */ struct _cairo_bo_trap { cairo_bo_edge_t *right; int32_t top; }; struct _cairo_bo_edge { cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; cairo_bo_edge_t *colinear; cairo_bo_trap_t deferred_trap; }; /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef enum { CAIRO_BO_EVENT_TYPE_STOP, CAIRO_BO_EVENT_TYPE_INTERSECTION, CAIRO_BO_EVENT_TYPE_START } cairo_bo_event_type_t; typedef struct _cairo_bo_event { cairo_bo_event_type_t type; cairo_point_t point; } cairo_bo_event_t; typedef struct _cairo_bo_start_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t edge; } cairo_bo_start_event_t; typedef struct _cairo_bo_queue_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t *e1; cairo_bo_edge_t *e2; } cairo_bo_queue_event_t; typedef struct _pqueue { int size, max_size; cairo_bo_event_t **elements; cairo_bo_event_t *elements_embedded[1024]; } pqueue_t; typedef struct _cairo_bo_event_queue { cairo_freepool_t pool; pqueue_t pqueue; cairo_bo_event_t **start_events; } cairo_bo_event_queue_t; typedef struct _cairo_bo_sweep_line { cairo_bo_edge_t *head; cairo_bo_edge_t *stopped; int32_t current_y; cairo_bo_edge_t *current_edge; } cairo_bo_sweep_line_t; #if DEBUG_TRAPS static void dump_traps (cairo_traps_t *traps, const char *filename) { FILE *file; cairo_box_t extents; int n; if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) return; #if 0 if (traps->has_limits) { printf ("%s: limits=(%d, %d, %d, %d)\n", filename, traps->limits.p1.x, traps->limits.p1.y, traps->limits.p2.x, traps->limits.p2.y); } #endif _cairo_traps_extents (traps, &extents); printf ("%s: extents=(%d, %d, %d, %d)\n", filename, extents.p1.x, extents.p1.y, extents.p2.x, extents.p2.y); file = fopen (filename, "a"); if (file != NULL) { for (n = 0; n < traps->num_traps; n++) { fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", traps->traps[n].top, traps->traps[n].bottom, traps->traps[n].left.p1.x, traps->traps[n].left.p1.y, traps->traps[n].left.p2.x, traps->traps[n].left.p2.y, traps->traps[n].right.p1.x, traps->traps[n].right.p1.y, traps->traps[n].right.p2.x, traps->traps[n].right.p2.y); } fprintf (file, "\n"); fclose (file); } } static void dump_edges (cairo_bo_start_event_t *events, int num_edges, const char *filename) { FILE *file; int n; if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) return; file = fopen (filename, "a"); if (file != NULL) { for (n = 0; n < num_edges; n++) { fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n", events[n].edge.edge.line.p1.x, events[n].edge.edge.line.p1.y, events[n].edge.edge.line.p2.x, events[n].edge.edge.line.p2.y, events[n].edge.edge.top, events[n].edge.edge.bottom, events[n].edge.edge.dir); } fprintf (file, "\n"); fclose (file); } } #endif static cairo_fixed_t _line_compute_intersection_x_for_y (const cairo_line_t *line, cairo_fixed_t y) { cairo_fixed_t x, dy; if (y == line->p1.y) return line->p1.x; if (y == line->p2.y) return line->p2.x; x = line->p1.x; dy = line->p2.y - line->p1.y; if (dy != 0) { x += _cairo_fixed_mul_div_floor (y - line->p1.y, line->p2.x - line->p1.x, dy); } return x; } static inline int _cairo_bo_point32_compare (cairo_bo_point32_t const *a, cairo_bo_point32_t const *b) { int cmp; cmp = a->y - b->y; if (cmp) return cmp; return a->x - b->x; } /* Compare the slope of a to the slope of b, returning 1, 0, -1 if the * slope a is respectively greater than, equal to, or less than the * slope of b. * * For each edge, consider the direction vector formed from: * * top -> bottom * * which is: * * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) * * We then define the slope of each edge as dx/dy, (which is the * inverse of the slope typically used in math instruction). We never * compute a slope directly as the value approaches infinity, but we * can derive a slope comparison without division as follows, (where * the ? represents our compare operator). * * 1. slope(a) ? slope(b) * 2. adx/ady ? bdx/bdy * 3. (adx * bdy) ? (bdx * ady) * * Note that from step 2 to step 3 there is no change needed in the * sign of the result since both ady and bdy are guaranteed to be * greater than or equal to 0. * * When using this slope comparison to sort edges, some care is needed * when interpreting the results. Since the slope compare operates on * distance vectors from top to bottom it gives a correct left to * right sort for edges that have a common top point, (such as two * edges with start events at the same location). On the other hand, * the sense of the result will be exactly reversed for two edges that * have a common stop point. */ static inline int _slope_compare (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; /* Since the dy's are all positive by construction we can fast * path several common cases. */ /* First check for vertical lines. */ if (adx == 0) return -bdx; if (bdx == 0) return adx; /* Then where the two edges point in different directions wrt x. */ if ((adx ^ bdx) < 0) return adx; /* Finally we actually need to do the general comparison. */ { int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } } /* * We need to compare the x-coordinates of a pair of lines for a particular y, * without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 128 bit arithmetic. For certain, but common, * input we can reduce this down to a single 32 bit compare by inspecting the * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) * * See the similar discussion for _slope_compare(). */ static int edges_compare_x_for_y_general (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t dx; int32_t adx, ady; int32_t bdx, bdy; enum { HAVE_NONE = 0x0, HAVE_DX = 0x1, HAVE_ADX = 0x2, HAVE_DX_ADX = HAVE_DX | HAVE_ADX, HAVE_BDX = 0x4, HAVE_DX_BDX = HAVE_DX | HAVE_BDX, HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX } have_dx_adx_bdx = HAVE_ALL; /* don't bother solving for abscissa if the edges' bounding boxes * can be used to order them. */ { int32_t amin, amax; int32_t bmin, bmax; if (a->edge.line.p1.x < a->edge.line.p2.x) { amin = a->edge.line.p1.x; amax = a->edge.line.p2.x; } else { amin = a->edge.line.p2.x; amax = a->edge.line.p1.x; } if (b->edge.line.p1.x < b->edge.line.p2.x) { bmin = b->edge.line.p1.x; bmax = b->edge.line.p2.x; } else { bmin = b->edge.line.p2.x; bmax = b->edge.line.p1.x; } if (amax < bmin) return -1; if (amin > bmax) return +1; } ady = a->edge.line.p2.y - a->edge.line.p1.y; adx = a->edge.line.p2.x - a->edge.line.p1.x; if (adx == 0) have_dx_adx_bdx &= ~HAVE_ADX; bdy = b->edge.line.p2.y - b->edge.line.p1.y; bdx = b->edge.line.p2.x - b->edge.line.p1.x; if (bdx == 0) have_dx_adx_bdx &= ~HAVE_BDX; dx = a->edge.line.p1.x - b->edge.line.p1.x; if (dx == 0) have_dx_adx_bdx &= ~HAVE_DX; #define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) #define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) #define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) switch (have_dx_adx_bdx) { default: case HAVE_NONE: return 0; case HAVE_DX: /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ return dx; /* ady * bdy is positive definite */ case HAVE_ADX: /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ return adx; /* bdy * (y - a->top.y) is positive definite */ case HAVE_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy */ return -bdx; /* ady * (y - b->top.y) is positive definite */ case HAVE_ADX_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ if ((adx ^ bdx) < 0) { return adx; } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ cairo_int64_t adx_bdy, bdx_ady; /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ adx_bdy = _cairo_int32x32_64_mul (adx, bdy); bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } else return _cairo_int128_cmp (A, B); case HAVE_DX_ADX: /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ if ((-adx ^ dx) < 0) { return dx; } else { cairo_int64_t ady_dx, dy_adx; ady_dx = _cairo_int32x32_64_mul (ady, dx); dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); return _cairo_int64_cmp (ady_dx, dy_adx); } case HAVE_DX_BDX: /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ if ((bdx ^ dx) < 0) { return dx; } else { cairo_int64_t bdy_dx, dy_bdx; bdy_dx = _cairo_int32x32_64_mul (bdy, dx); dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); return _cairo_int64_cmp (bdy_dx, dy_bdx); } case HAVE_ALL: /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); } #undef B #undef A #undef L } /* * We need to compare the x-coordinate of a line for a particular y wrt to a * given x, without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ X * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. * * See the similar discussion for _slope_compare() and * edges_compare_x_for_y_general(). */ static int edge_compare_for_y_against_x (const cairo_bo_edge_t *a, int32_t y, int32_t x) { int32_t adx, ady; int32_t dx, dy; cairo_int64_t L, R; if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) return 1; if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) return -1; adx = a->edge.line.p2.x - a->edge.line.p1.x; dx = x - a->edge.line.p1.x; if (adx == 0) return -dx; if (dx == 0 || (adx ^ dx) < 0) return adx; dy = y - a->edge.line.p1.y; ady = a->edge.line.p2.y - a->edge.line.p1.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); return _cairo_int64_cmp (L, R); } static int edges_compare_x_for_y (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* If the sweep-line is currently on an end-point of a line, * then we know its precise x value (and considering that we often need to * compare events at end-points, this happens frequently enough to warrant * special casing). */ enum { HAVE_NEITHER = 0x0, HAVE_AX = 0x1, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; int32_t ax, bx; if (y == a->edge.line.p1.y) ax = a->edge.line.p1.x; else if (y == a->edge.line.p2.y) ax = a->edge.line.p2.x; else have_ax_bx &= ~HAVE_AX; if (y == b->edge.line.p1.y) bx = b->edge.line.p1.x; else if (y == b->edge.line.p2.y) bx = b->edge.line.p2.x; else have_ax_bx &= ~HAVE_BX; switch (have_ax_bx) { default: case HAVE_NEITHER: return edges_compare_x_for_y_general (a, b, y); case HAVE_AX: return -edge_compare_for_y_against_x (b, y, ax); case HAVE_BX: return edge_compare_for_y_against_x (a, y, bx); case HAVE_BOTH: return ax - bx; } } static inline int _line_equal (const cairo_line_t *a, const cairo_line_t *b) { return a->p1.x == b->p1.x && a->p1.y == b->p1.y && a->p2.x == b->p2.x && a->p2.y == b->p2.y; } static inline int _cairo_bo_sweep_line_compare_edges (const cairo_bo_sweep_line_t *sweep_line, const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { int cmp; /* compare the edges if not identical */ if (! _line_equal (&a->edge.line, &b->edge.line)) { if (MAX (a->edge.line.p1.x, a->edge.line.p2.x) < MIN (b->edge.line.p1.x, b->edge.line.p2.x)) return -1; else if (MIN (a->edge.line.p1.x, a->edge.line.p2.x) > MAX (b->edge.line.p1.x, b->edge.line.p2.x)) return 1; cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); if (cmp) return cmp; /* The two edges intersect exactly at y, so fall back on slope * comparison. We know that this compare_edges function will be * called only when starting a new edge, (not when stopping an * edge), so we don't have to worry about conditionally inverting * the sense of _slope_compare. */ cmp = _slope_compare (a, b); if (cmp) return cmp; } /* We've got two collinear edges now. */ return b->edge.bottom - a->edge.bottom; } static inline cairo_int64_t det32_64 (int32_t a, int32_t b, int32_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), _cairo_int32x32_64_mul (b, c)); } static inline cairo_int128_t det64x32_128 (cairo_int64_t a, int32_t b, cairo_int64_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), _cairo_int64x32_128_mul (c, b)); } /* Compute the intersection of two lines as defined by two edges. The * result is provided as a coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. */ static cairo_bool_t intersect_lines (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_intersect_point_t *intersection) { cairo_int64_t a_det, b_det; /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm begins. * What we're doing to mitigate this is to perform clamping in * cairo_bo_tessellate_polygon(). */ int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; cairo_int64_t den_det; cairo_int64_t R; cairo_quorem64_t qr; den_det = det32_64 (dx1, dy1, dx2, dy2); /* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by * avoiding the division? * * X = ax + t * adx = bx + s * bdx; * Y = ay + t * ady = by + s * bdy; * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) * => t * L = R * * Therefore we can reject any intersection (under the criteria for * valid intersection events) if: * L^R < 0 => t < 0, or * L<R => t > 1 * * (where top/bottom must at least extend to the line endpoints). * * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) */ R = det32_64 (dx2, dy2, b->edge.line.p1.x - a->edge.line.p1.x, b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } R = det32_64 (dy1, dx1, a->edge.line.p1.y - b->edge.line.p1.y, a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } /* We now know that the two lines should intersect within range. */ a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, a->edge.line.p2.x, a->edge.line.p2.y); b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, b->edge.line.p2.x, b->edge.line.p2.y); /* x = det (a_det, dx1, b_det, dx2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, b_det, dx2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->x.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->x.exactness = INEXACT; } #endif intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); /* y = det (a_det, dy1, b_det, dy2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, b_det, dy2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->y.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->y.exactness = INEXACT; } #endif intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); return TRUE; } static int _cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, int32_t b) { /* First compare the quotient */ if (a.ordinate > b) return +1; if (a.ordinate < b) return -1; /* With quotient identical, if remainder is 0 then compare equal */ /* Otherwise, the non-zero remainder makes a > b */ return INEXACT == a.exactness; } /* Does the given edge contain the given point. The point must already * be known to be contained within the line determined by the edge, * (most likely the point results from an intersection of this edge * with another). * * If we had exact arithmetic, then this function would simply be a * matter of examining whether the y value of the point lies within * the range of y values of the edge. But since intersection points * are not exact due to being rounded to the nearest integer within * the available precision, we must also examine the x value of the * point. * * The definition of "contains" here is that the given intersection * point will be seen by the sweep line after the start event for the * given edge and before the stop event for the edge. See the comments * in the implementation for more details. */ static cairo_bool_t _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, cairo_bo_intersect_point_t *point) { int cmp_top, cmp_bottom; /* XXX: When running the actual algorithm, we don't actually need to * compare against edge->top at all here, since any intersection above * top is eliminated early via a slope comparison. We're leaving these * here for now only for the sake of the quadratic-time intersection * finder which needs them. */ cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.top); cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.bottom); if (cmp_top < 0 || cmp_bottom > 0) { return FALSE; } if (cmp_top > 0 && cmp_bottom < 0) { return TRUE; } /* At this stage, the point lies on the same y value as either * edge->top or edge->bottom, so we have to examine the x value in * order to properly determine containment. */ /* If the y value of the point is the same as the y value of the * top of the edge, then the x value of the point must be greater * to be considered as inside the edge. Similarly, if the y value * of the point is the same as the y value of the bottom of the * edge, then the x value of the point must be less to be * considered as inside. */ if (cmp_top == 0) { cairo_fixed_t top_x; top_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.top); return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; } else { /* cmp_bottom == 0 */ cairo_fixed_t bot_x; bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.bottom); return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; } } /* Compute the intersection of two edges. The result is provided as a * coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the * intersection of the lines defined by the edges occurs outside of * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges * are exactly parallel. * * Note that when determining if a candidate intersection is "inside" * an edge, we consider both the infinitesimal shortening and the * infinitesimal tilt rules described by John Hobby. Specifically, if * the intersection is exactly the same as an edge point, it is * effectively outside (no intersection is returned). Also, if the * intersection point has the same */ static cairo_bool_t _cairo_bo_edge_intersect (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_point32_t *intersection) { cairo_bo_intersect_point_t quorem; if (! intersect_lines (a, b, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) return FALSE; /* Now that we've correctly compared the intersection point and * determined that it lies within the edge, then we know that we * no longer need any more bits of storage for the intersection * than we do for our edge coordinates. We also no longer need the * remainder from the division. */ intersection->x = quorem.x.ordinate; intersection->y = quorem.y.ordinate; return TRUE; } static inline int cairo_bo_event_compare (const cairo_bo_event_t *a, const cairo_bo_event_t *b) { int cmp; cmp = _cairo_bo_point32_compare (&a->point, &b->point); if (cmp) return cmp; cmp = a->type - b->type; if (cmp) return cmp; return a - b; } static inline void _pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; } static inline void _pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_status_t _pqueue_grow (pqueue_t *pq) { cairo_bo_event_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pq->elements = new_elements; return CAIRO_STATUS_SUCCESS; } static inline cairo_status_t _pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) { cairo_bo_event_t **elements; int i, parent; if (unlikely (pq->size + 1 == pq->max_size)) { cairo_status_t status; status = _pqueue_grow (pq); if (unlikely (status)) return status; } elements = pq->elements; for (i = ++pq->size; i != PQ_FIRST_ENTRY && cairo_bo_event_compare (event, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = event; return CAIRO_STATUS_SUCCESS; } static inline void _pqueue_pop (pqueue_t *pq) { cairo_bo_event_t **elements = pq->elements; cairo_bo_event_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && cairo_bo_event_compare (elements[child+1], elements[child]) < 0) { child++; } if (cairo_bo_event_compare (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline cairo_status_t _cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, cairo_bo_event_type_t type, cairo_bo_edge_t *e1, cairo_bo_edge_t *e2, const cairo_point_t *point) { cairo_bo_queue_event_t *event; event = _cairo_freepool_alloc (&queue->pool); if (unlikely (event == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event->type = type; event->e1 = e1; event->e2 = e2; event->point = *point; return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); } static void _cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, cairo_bo_event_t *event) { _cairo_freepool_free (&queue->pool, event); } static cairo_bo_event_t * _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) { cairo_bo_event_t *event, *cmp; event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; cmp = *event_queue->start_events; if (event == NULL || (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) { event = cmp; event_queue->start_events++; } else { _pqueue_pop (&event_queue->pqueue); } return event; } CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, cairo_bo_event_t *, cairo_bo_event_compare) static void _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_event_t **start_events, int num_events) { event_queue->start_events = start_events; _cairo_freepool_init (&event_queue->pool, sizeof (cairo_bo_queue_event_t)); _pqueue_init (&event_queue->pqueue); event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; } static cairo_status_t _cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *edge) { cairo_bo_point32_t point; point.y = edge->edge.bottom; point.x = _line_compute_intersection_x_for_y (&edge->edge.line, point.y); return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_STOP, edge, NULL, &point); } static void _cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) { _pqueue_fini (&event_queue->pqueue); _cairo_freepool_fini (&event_queue->pool); } static inline cairo_status_t _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { cairo_bo_point32_t intersection; if (MAX (left->edge.line.p1.x, left->edge.line.p2.x) <= MIN (right->edge.line.p1.x, right->edge.line.p2.x)) return CAIRO_STATUS_SUCCESS; if (_line_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS; /* The names "left" and "right" here are correct descriptions of * the order of the two edges within the active edge list. So if a * slope comparison also puts left less than right, then we know * that the intersection of these two segments has already * occurred before the current sweep line position. */ if (_slope_compare (left, right) <= 0) return CAIRO_STATUS_SUCCESS; if (! _cairo_bo_edge_intersect (left, right, &intersection)) return CAIRO_STATUS_SUCCESS; return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_INTERSECTION, left, right, &intersection); } static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) { sweep_line->head = NULL; sweep_line->stopped = NULL; sweep_line->current_y = INT32_MIN; sweep_line->current_edge = NULL; } static void _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (sweep_line->current_edge != NULL) { cairo_bo_edge_t *prev, *next; int cmp; cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, sweep_line->current_edge, edge); if (cmp < 0) { prev = sweep_line->current_edge; next = prev->next; while (next != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, next, edge) < 0) { prev = next, next = prev->next; } prev->next = edge; edge->prev = prev; edge->next = next; if (next != NULL) next->prev = edge; } else if (cmp > 0) { next = sweep_line->current_edge; prev = next->prev; while (prev != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, prev, edge) > 0) { next = prev, prev = next->prev; } next->prev = edge; edge->next = next; edge->prev = prev; if (prev != NULL) prev->next = edge; else sweep_line->head = edge; } else { prev = sweep_line->current_edge; edge->prev = prev; edge->next = prev->next; if (prev->next != NULL) prev->next->prev = edge; prev->next = edge; } } else { sweep_line->head = edge; edge->next = NULL; } sweep_line->current_edge = edge; } static void _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (edge->prev != NULL) edge->prev->next = edge->next; else sweep_line->head = edge->next; if (edge->next != NULL) edge->next->prev = edge->prev; if (sweep_line->current_edge == edge) sweep_line->current_edge = edge->prev ? edge->prev : edge->next; } static void _cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { if (left->prev != NULL) left->prev->next = right; else sweep_line->head = right; if (right->next != NULL) right->next->prev = left; left->next = right->next; right->next = left; right->prev = left->prev; left->prev = right; } #if DEBUG_PRINT_STATE static void _cairo_bo_edge_print (cairo_bo_edge_t *edge) { printf ("(0x%x, 0x%x)-(0x%x, 0x%x)", edge->edge.line.p1.x, edge->edge.line.p1.y, edge->edge.line.p2.x, edge->edge.line.p2.y); } static void _cairo_bo_event_print (cairo_bo_event_t *event) { switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: printf ("Start: "); break; case CAIRO_BO_EVENT_TYPE_STOP: printf ("Stop: "); break; case CAIRO_BO_EVENT_TYPE_INTERSECTION: printf ("Intersection: "); break; } printf ("(%d, %d)\t", event->point.x, event->point.y); _cairo_bo_edge_print (event->e1); if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) { printf (" X "); _cairo_bo_edge_print (event->e2); } printf ("\n"); } static void _cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue) { /* XXX: fixme to print the start/stop array too. */ printf ("Event queue:\n"); } static void _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) { cairo_bool_t first = TRUE; cairo_bo_edge_t *edge; printf ("Sweep line from edge list: "); first = TRUE; for (edge = sweep_line->head; edge; edge = edge->next) { if (!first) printf (", "); _cairo_bo_edge_print (edge); first = FALSE; } printf ("\n"); } static void print_state (const char *msg, cairo_bo_event_t *event, cairo_bo_event_queue_t *event_queue, cairo_bo_sweep_line_t *sweep_line) { printf ("%s ", msg); _cairo_bo_event_print (event); _cairo_bo_event_queue_print (event_queue); _cairo_bo_sweep_line_print (sweep_line); printf ("\n"); } #endif #if DEBUG_EVENTS static void CAIRO_PRINTF_FORMAT (1, 2) event_log (const char *fmt, ...) { FILE *file; if (getenv ("CAIRO_DEBUG_EVENTS") == NULL) return; file = fopen ("bo-events.txt", "a"); if (file != NULL) { va_list ap; va_start (ap, fmt); vfprintf (file, fmt, ap); va_end (ap); fclose (file); } } #endif #define HAS_COLINEAR(a, b) ((cairo_bo_edge_t *)(((uintptr_t)(a))&~1) == (b)) #define IS_COLINEAR(e) (((uintptr_t)(e))&1) #define MARK_COLINEAR(e, v) ((cairo_bo_edge_t *)(((uintptr_t)(e))|(v))) static inline cairo_bool_t edges_colinear (cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { unsigned p; if (HAS_COLINEAR(a->colinear, b)) return IS_COLINEAR(a->colinear); if (HAS_COLINEAR(b->colinear, a)) { p = IS_COLINEAR(b->colinear); a->colinear = MARK_COLINEAR(b, p); return p; } p = 0; p |= (a->edge.line.p1.x == b->edge.line.p1.x) << 0; p |= (a->edge.line.p1.y == b->edge.line.p1.y) << 1; p |= (a->edge.line.p2.x == b->edge.line.p2.x) << 3; p |= (a->edge.line.p2.y == b->edge.line.p2.y) << 4; if (p == ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4))) { a->colinear = MARK_COLINEAR(b, 1); return TRUE; } if (_slope_compare (a, b)) { a->colinear = MARK_COLINEAR(b, 0); return FALSE; } /* The choice of y is not truly arbitrary since we must guarantee that it * is greater than the start of either line. */ if (p != 0) { /* colinear if either end-point are coincident */ p = (((p >> 1) & p) & 5) != 0; } else if (a->edge.line.p1.y < b->edge.line.p1.y) { p = edge_compare_for_y_against_x (b, a->edge.line.p1.y, a->edge.line.p1.x) == 0; } else { p = edge_compare_for_y_against_x (a, b->edge.line.p1.y, b->edge.line.p1.x) == 0; } a->colinear = MARK_COLINEAR(b, p); return p; } /* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ static void _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, int32_t bot, cairo_traps_t *traps) { cairo_bo_trap_t *trap = &left->deferred_trap; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (trap->top < bot)) { _cairo_traps_add_trap (traps, trap->top, bot, &left->edge.line, &trap->right->edge.line); #if DEBUG_PRINT_STATE printf ("Deferred trap: left=(%x, %x)-(%x,%x) " "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n", left->edge.line.p1.x, left->edge.line.p1.y, left->edge.line.p2.x, left->edge.line.p2.y, trap->right->edge.line.p1.x, trap->right->edge.line.p1.y, trap->right->edge.line.p2.x, trap->right->edge.line.p2.y, trap->top, bot); #endif #if DEBUG_EVENTS event_log ("end trap: %lu %lu %d %d\n", (long) left, (long) trap->right, trap->top, bot); #endif } trap->right = NULL; } /* Start a new trapezoid at the given top y coordinate, whose edges * are `edge' and `edge->next'. If `edge' already has a trapezoid, * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ static inline void _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, cairo_bo_edge_t *right, int top, cairo_traps_t *traps) { if (left->deferred_trap.right == right) return; assert (right); if (left->deferred_trap.right != NULL) { if (edges_colinear (left->deferred_trap.right, right)) { /* continuation on right, so just swap edges */ left->deferred_trap.right = right; return; } _cairo_bo_edge_end_trap (left, top, traps); } if (! edges_colinear (left, right)) { left->deferred_trap.top = top; left->deferred_trap.right = right; #if DEBUG_EVENTS event_log ("begin trap: %lu %lu %d\n", (long) left, (long) right, top); #endif } } static inline void _active_edges_to_traps (cairo_bo_edge_t *pos, int32_t top, unsigned mask, cairo_traps_t *traps) { cairo_bo_edge_t *left; int in_out; #if DEBUG_PRINT_STATE printf ("Processing active edges for %x\n", top); #endif in_out = 0; left = pos; while (pos != NULL) { if (pos != left && pos->deferred_trap.right) { /* XXX It shouldn't be possible to here with 2 deferred traps * on colinear edges... See bug-bo-rictoz. */ if (left->deferred_trap.right == NULL && edges_colinear (left, pos)) { /* continuation on left */ left->deferred_trap = pos->deferred_trap; pos->deferred_trap.right = NULL; } else { _cairo_bo_edge_end_trap (pos, top, traps); } } in_out += pos->edge.dir; if ((in_out & mask) == 0) { /* skip co-linear edges */ if (pos->next == NULL || ! edges_colinear (pos, pos->next)) { _cairo_bo_edge_start_or_continue_trap (left, pos, top, traps); left = pos->next; } } pos = pos->next; } } /* Execute a single pass of the Bentley-Ottmann algorithm on edges, * generating trapezoids according to the fill_rule and appending them * to traps. */ static cairo_status_t _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, int num_events, unsigned fill_rule, cairo_traps_t *traps, int *num_intersections) { cairo_status_t status; int intersection_count = 0; cairo_bo_event_queue_t event_queue; cairo_bo_sweep_line_t sweep_line; cairo_bo_event_t *event; cairo_bo_edge_t *left, *right; cairo_bo_edge_t *e1, *e2; /* convert the fill_rule into a winding mask */ if (fill_rule == CAIRO_FILL_RULE_WINDING) fill_rule = (unsigned) -1; else fill_rule = 1; #if DEBUG_EVENTS { int i; for (i = 0; i < num_events; i++) { cairo_bo_start_event_t *event = ((cairo_bo_start_event_t **) start_events)[i]; event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n", (long) &events[i].edge, event->edge.edge.line.p1.x, event->edge.edge.line.p1.y, event->edge.edge.line.p2.x, event->edge.edge.line.p2.y, event->edge.top, event->edge.bottom, event->edge.edge.dir); } } #endif _cairo_bo_event_queue_init (&event_queue, start_events, num_events); _cairo_bo_sweep_line_init (&sweep_line); while ((event = _cairo_bo_event_dequeue (&event_queue))) { if (event->point.y != sweep_line.current_y) { for (e1 = sweep_line.stopped; e1; e1 = e1->next) { if (e1->deferred_trap.right != NULL) { _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); } } sweep_line.stopped = NULL; _active_edges_to_traps (sweep_line.head, sweep_line.current_y, fill_rule, traps); sweep_line.current_y = event->point.y; } #if DEBUG_EVENTS event_log ("event: %d (%ld, %ld) %lu, %lu\n", event->type, (long) event->point.x, (long) event->point.y, (long) event->e1, (long) event->e2); #endif switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: e1 = &((cairo_bo_start_event_t *) event)->edge; _cairo_bo_sweep_line_insert (&sweep_line, e1); status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); if (unlikely (status)) goto unwind; /* check to see if this is a continuation of a stopped edge */ /* XXX change to an infinitesimal lengthening rule */ for (left = sweep_line.stopped; left; left = left->next) { if (e1->edge.top <= left->edge.bottom && edges_colinear (e1, left)) { e1->deferred_trap = left->deferred_trap; if (left->prev != NULL) left->prev = left->next; else sweep_line.stopped = left->next; if (left->next != NULL) left->next->prev = left->prev; break; } } left = e1->prev; right = e1->next; if (left != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); if (unlikely (status)) goto unwind; } if (right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_STOP: e1 = ((cairo_bo_queue_event_t *) event)->e1; _cairo_bo_event_queue_delete (&event_queue, event); left = e1->prev; right = e1->next; _cairo_bo_sweep_line_delete (&sweep_line, e1); /* first, check to see if we have a continuation via a fresh edge */ if (e1->deferred_trap.right != NULL) { e1->next = sweep_line.stopped; if (sweep_line.stopped != NULL) sweep_line.stopped->prev = e1; sweep_line.stopped = e1; e1->prev = NULL; } if (left != NULL && right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_INTERSECTION: e1 = ((cairo_bo_queue_event_t *) event)->e1; e2 = ((cairo_bo_queue_event_t *) event)->e2; _cairo_bo_event_queue_delete (&event_queue, event); /* skip this intersection if its edges are not adjacent */ if (e2 != e1->next) break; intersection_count++; left = e1->prev; right = e2->next; _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); /* after the swap e2 is left of e1 */ if (left != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); if (unlikely (status)) goto unwind; } if (right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; } } *num_intersections = intersection_count; for (e1 = sweep_line.stopped; e1; e1 = e1->next) { if (e1->deferred_trap.right != NULL) { _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); } } status = traps->status; unwind: _cairo_bo_event_queue_fini (&event_queue); #if DEBUG_EVENTS event_log ("\n"); #endif return status; } cairo_status_t _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule) { int intersections; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_start_event_t *stack_event_y[64]; cairo_bo_start_event_t **event_y = NULL; int i, num_events, y, ymin, ymax; cairo_status_t status; num_events = polygon->num_edges; if (unlikely (0 == num_events)) return CAIRO_STATUS_SUCCESS; if (polygon->num_limits) { ymin = _cairo_fixed_integer_floor (polygon->limit.p1.y); ymax = _cairo_fixed_integer_ceil (polygon->limit.p2.y) - ymin; if (ymax > 64) event_y = _cairo_malloc_ab(sizeof (cairo_bo_event_t*), ymax); else event_y = stack_event_y; memset (event_y, 0, ymax * sizeof(cairo_bo_event_t *)); } events = stack_events; event_ptrs = stack_event_ptrs; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); } for (i = 0; i < num_events; i++) { events[i].type = CAIRO_BO_EVENT_TYPE_START; events[i].point.y = polygon->edges[i].top; events[i].point.x = _line_compute_intersection_x_for_y (&polygon->edges[i].line, events[i].point.y); events[i].edge.edge = polygon->edges[i]; events[i].edge.deferred_trap.right = NULL; events[i].edge.prev = NULL; events[i].edge.next = NULL; events[i].edge.colinear = NULL; if (event_y) { y = _cairo_fixed_integer_floor (events[i].point.y) - ymin; events[i].edge.next = (cairo_bo_edge_t *) event_y[y]; event_y[y] = (cairo_bo_start_event_t *) &events[i]; } else event_ptrs[i] = (cairo_bo_event_t *) &events[i]; } if (event_y) { for (y = i = 0; y < ymax && i < num_events; y++) { cairo_bo_start_event_t *e; int j = i; for (e = event_y[y]; e; e = (cairo_bo_start_event_t *)e->edge.next) event_ptrs[i++] = (cairo_bo_event_t *) e; if (i > j + 1) _cairo_bo_event_queue_sort (event_ptrs+j, i-j); } if (event_y != stack_event_y) free (event_y); } else _cairo_bo_event_queue_sort (event_ptrs, i); event_ptrs[i] = NULL; #if DEBUG_TRAPS dump_edges (events, num_events, "bo-polygon-edges.txt"); #endif /* XXX: This would be the convenient place to throw in multiple * passes of the Bentley-Ottmann algorithm. It would merely * require storing the results of each pass into a temporary * cairo_traps_t. */ status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events, fill_rule, traps, &intersections); #if DEBUG_TRAPS dump_traps (traps, "bo-polygon-out.txt"); #endif if (events != stack_events) free (events); return status; } cairo_status_t _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { cairo_status_t status; cairo_polygon_t polygon; int i; if (unlikely (0 == traps->num_traps)) return CAIRO_STATUS_SUCCESS; #if DEBUG_TRAPS dump_traps (traps, "bo-traps-in.txt"); #endif _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); for (i = 0; i < traps->num_traps; i++) { status = _cairo_polygon_add_line (&polygon, &traps->traps[i].left, traps->traps[i].top, traps->traps[i].bottom, 1); if (unlikely (status)) goto CLEANUP; status = _cairo_polygon_add_line (&polygon, &traps->traps[i].right, traps->traps[i].top, traps->traps[i].bottom, -1); if (unlikely (status)) goto CLEANUP; } _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, fill_rule); #if DEBUG_TRAPS dump_traps (traps, "bo-traps-out.txt"); #endif CLEANUP: _cairo_polygon_fini (&polygon); return status; } #if 0 static cairo_bool_t edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, int num_edges) { int i, j; cairo_bo_edge_t *a, *b; cairo_bo_point32_t intersection; /* We must not be given any upside-down edges. */ for (i = 0; i < num_edges; i++) { assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0); edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS; edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS; edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS; edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS; } for (i = 0; i < num_edges; i++) { for (j = 0; j < num_edges; j++) { if (i == j) continue; a = &edges[i]; b = &edges[j]; if (! _cairo_bo_edge_intersect (a, b, &intersection)) continue; printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n", intersection.x, intersection.y, a->line.p1.x, a->line.p1.y, a->line.p2.x, a->line.p2.y, b->line.p1.x, b->line.p1.y, b->line.p2.x, b->line.p2.y); return TRUE; } } return FALSE; } #define TEST_MAX_EDGES 10 typedef struct test { const char *name; const char *description; int num_edges; cairo_bo_edge_t edges[TEST_MAX_EDGES]; } test_t; static test_t tests[] = { { "3 near misses", "3 edges all intersecting very close to each other", 3, { { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL }, { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL }, { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL } } }, { "inconsistent data", "Derived from random testing---was leading to skip list and edge list disagreeing.", 2, { { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL }, { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL } } }, { "failed sort", "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?", 3, { { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL }, { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL }, { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL }, } }, { "minimal-intersection", "Intersection of a two from among the smallest possible edges.", 2, { { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL }, { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL } } }, { "simple", "A simple intersection of two edges at an integer (2,2).", 2, { { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL }, { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL } } }, { "bend-to-horizontal", "With intersection truncation one edge bends to horizontal", 2, { { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL }, { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL } } } }; /* { "endpoint", "An intersection that occurs at the endpoint of a segment.", { { { 4, 6}, { 5, 6}, NULL, { { NULL }} }, { { 4, 5}, { 5, 7}, NULL, { { NULL }} }, { { 0, 0}, { 0, 0}, NULL, { { NULL }} }, } } { name = "overlapping", desc = "Parallel segments that share an endpoint, with different slopes.", edges = { { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}}, { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}}, { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}}, { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}}, { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}}, { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}} } }, { name = "hobby_stage_3", desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.", edges = { { top = { x = -1, y = -2}, bottom = { x = 4, y = 2}}, { top = { x = 5, y = 3}, bottom = { x = 9, y = 5}}, { top = { x = 5, y = 3}, bottom = { x = 6, y = 3}}, } }, { name = "hobby", desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.", edges = { { top = { x = 0, y = 0}, bottom = { x = 9, y = 5}}, { top = { x = 0, y = 0}, bottom = { x = 13, y = 6}}, { top = { x = -1, y = -2}, bottom = { x = 9, y = 5}} } }, { name = "slope", desc = "Edges with same start/stop points but different slopes", edges = { { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}}, { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}}, { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}}, { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}} } }, { name = "horizontal", desc = "Test of a horizontal edge", edges = { { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}}, { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}} } }, { name = "vertical", desc = "Test of a vertical edge", edges = { { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} } }, { name = "congruent", desc = "Two overlapping edges with the same slope", edges = { { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}}, { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} } }, { name = "multi", desc = "Several segments with a common intersection point", edges = { { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} }, { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} }, { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} }, { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} }, { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} }, { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} } } } }; */ static int run_test (const char *test_name, cairo_bo_edge_t *test_edges, int num_edges) { int i, intersections, passes; cairo_bo_edge_t *edges; cairo_array_t intersected_edges; printf ("Testing: %s\n", test_name); _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges); if (intersections) printf ("Pass 1 found %d intersections:\n", intersections); /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a * pass of Hobby's tolerance-square algorithm instead. */ passes = 1; while (intersections) { int num_edges = _cairo_array_num_elements (&intersected_edges); passes++; edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t)); assert (edges != NULL); memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t)); _cairo_array_fini (&intersected_edges); _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges); free (edges); if (intersections){ printf ("Pass %d found %d remaining intersections:\n", passes, intersections); } else { if (passes > 3) for (i = 0; i < passes; i++) printf ("*"); printf ("No remainining intersections found after pass %d\n", passes); } } if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0), _cairo_array_num_elements (&intersected_edges))) printf ("*** FAIL ***\n"); else printf ("PASS\n"); _cairo_array_fini (&intersected_edges); return 0; } #define MAX_RANDOM 300 int main (void) { char random_name[] = "random-XX"; cairo_bo_edge_t random_edges[MAX_RANDOM], *edge; unsigned int i, num_random; test_t *test; for (i = 0; i < ARRAY_LENGTH (tests); i++) { test = &tests[i]; run_test (test->name, test->edges, test->num_edges); } for (num_random = 0; num_random < MAX_RANDOM; num_random++) { srand (0); for (i = 0; i < num_random; i++) { do { edge = &random_edges[i]; edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); if (edge->line.p1.y > edge->line.p2.y) { int32_t tmp = edge->line.p1.y; edge->line.p1.y = edge->line.p2.y; edge->line.p2.y = tmp; } } while (edge->line.p1.y == edge->line.p2.y); } sprintf (random_name, "random-%02d", num_random); run_test (random_name, random_edges, num_random); } return 0; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-beos-surface.cpp��������������������������0000664�0000000�0000000�00000066146�12710376503�0026472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vim:set ts=8 sw=4 noet cin: */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Christian Biesinger <cbiesinger@web.de> * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Christian Biesinger * <cbiesinger@web.de> * * Contributor(s): */ // This is a C++ file in order to use the C++ BeOS API #include "cairoint.h" #include "cairo-beos.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include <new> #include <Bitmap.h> #include <Region.h> #if 0 #include <DirectWindow.h> #endif #include <Screen.h> #include <Window.h> #include <Locker.h> /** * SECTION:beos-surface * @Title: BeOS Surfaces * @Short_Description: BeOS surface support * @See_Also: #cairo_surface_t * * The BeOS surface is used to render cairo graphics to BeOS views * and bitmaps. **/ #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS) struct cairo_beos_surface_t { cairo_surface_t base; cairo_region_t *clip_region; BView* view; /* * A view is either attached to a bitmap, a window, or unattached. * If it is attached to a window, we can copy data out of it using BScreen. * If it is attached to a bitmap, we can read the bitmap data. * If it is not attached, it doesn't draw anything, we need not bother. * * Since there doesn't seem to be a way to get the bitmap from a view if it * is attached to one, we have to use a special surface creation function. */ BBitmap* bitmap; // If true, surface and view should be deleted when this surface is // destroyed bool owns_bitmap_view; }; class AutoLockView { public: AutoLockView(BView* view) : mView(view) { mOK = mView->LockLooper(); } ~AutoLockView() { if (mOK) mView->UnlockLooper(); } operator bool() { return mOK; } private: BView* mView; bool mOK; }; static cairo_surface_t * _cairo_beos_surface_create_internal (BView* view, BBitmap* bmp, bool owns_bitmap_view = false); static inline BRect _cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect) { // A BRect is one pixel wider than you'd think return BRect (rect->x, rect->y, rect->x + rect->width - 1, rect->y + rect->height - 1); } static inline cairo_rectangle_int_t _brect_to_cairo_rectangle (const BRect &rect) { cairo_rectangle_int_t retval; retval.x = floor (rect.left); retval.y = floor (rect.top); retval.width = ceil (rect.right) - retval.x + 1; retval.height = ceil (rect.bottom) - rectval.y + 1; return retval; } static inline rgb_color _cairo_color_to_be_color (const cairo_color_t *color) { // This factor ensures a uniform distribution of numbers const float factor = 256 - 1e-5; // Using doubles to have non-premultiplied colors rgb_color be_color = { uint8(color->red * factor), uint8(color->green * factor), uint8(color->blue * factor), uint8(color->alpha * factor) }; return be_color; } enum ViewCopyStatus { OK, NOT_VISIBLE, // The view or the interest rect is not visible on screen ERROR // The view was visible, but the rect could not be copied. Probably OOM }; /** * _cairo_beos_view_to_bitmap: * @bitmap: [out] The resulting bitmap. * @rect: [out] The rectangle that was copied, in the view's coordinate system * @interestRect: If non-null, only this part of the view will be copied (view's coord system). * * Gets the contents of the view as a BBitmap*. Caller must delete the bitmap. **/ static ViewCopyStatus _cairo_beos_view_to_bitmap (BView* view, BBitmap** bitmap, BRect* rect = NULL, const BRect* interestRect = NULL) { *bitmap = NULL; BWindow* wnd = view->Window(); // If we have no window, can't do anything if (!wnd) return NOT_VISIBLE; view->Sync(); wnd->Sync(); #if 0 // Is it a direct window? BDirectWindow* directWnd = dynamic_cast<BDirectWindow*>(wnd); if (directWnd) { // WRITEME } #endif // Is it visible? If so, we can copy the content off the screen if (wnd->IsHidden()) return NOT_VISIBLE; BRect rectToCopy(view->Bounds()); if (interestRect) rectToCopy = rectToCopy & *interestRect; if (!rectToCopy.IsValid()) return NOT_VISIBLE; BScreen screen(wnd); BRect screenRect(view->ConvertToScreen(rectToCopy)); screenRect = screenRect & screen.Frame(); if (!screen.IsValid()) return NOT_VISIBLE; if (rect) *rect = view->ConvertFromScreen(screenRect); if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK) return OK; return ERROR; } static void unpremultiply_bgra (unsigned char* data, int width, int height, int stride, unsigned char* retdata) { unsigned char* end = data + stride * height; for (unsigned char* in = data, *out = retdata; in < end; in += stride, out += stride) { for (int i = 0; i < width; i ++) { uint8_t *b = &out[4*i]; uint32_t pixel; uint8_t alpha; memcpy (&pixel, &data[4*i], sizeof (uint32_t)); alpha = pixel & 0xff; if (alpha == 0) { b[0] = b[1] = b[2] = b[3] = 0; } else { b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha; b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha; b[2] = (((pixel >> 8) & 0xff) * 255 + alpha / 2) / alpha; b[3] = alpha; } } } } static inline int multiply_alpha (int alpha, int color) { int temp = (alpha * color) + 0x80; return ((temp + (temp >> 8)) >> 8); } static unsigned char* premultiply_bgra (unsigned char* data, int width, int height, int stride) { uint8_t * retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride)); if (!retdata) return NULL; uint8_t * end = data + stride * height; for (uint8_t * in = data, *out = retdata; in < end; in += stride, out += stride) { for (int i = 0; i < width; i ++) { uint8_t *base = &in[4*i]; uint8_t alpha = base[3]; uint32_t p; if (alpha == 0) { p = 0; } else { uint8_t blue = base[0]; uint8_t green = base[1]; uint8_t red = base[2]; if (alpha != 0xff) { blue = multiply_alpha (alpha, blue); green = multiply_alpha (alpha, green); red = multiply_alpha (alpha, red); } p = (alpha << 0) | (red << 8) | (green << 16) | (blue << 24); } memcpy (&out[4*i], &p, sizeof (uint32_t)); } } return retdata; } static cairo_int_status_t _cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface, cairo_region_t *region) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); AutoLockView locker(surface->view); assert (locker); if (region == surface->clip_region) return CAIRO_INT_STATUS_SUCCESS; cairo_region_destroy (surface->clip_region); surface->clip_region = cairo_region_reference (region); if (region == NULL) { // No clipping surface->view->ConstrainClippingRegion(NULL); return CAIRO_INT_STATUS_SUCCESS; } int count = cairo_region_num_rectangles (region); BRegion bregion; for (int i = 0; i < count; ++i) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); // Have to subtract one, because for pixman, the second coordinate // lies outside the rectangle. bregion.Include (_cairo_rectangle_to_brect (&rect)); } surface->view->ConstrainClippingRegion(&bregion); return CAIRO_INT_STATUS_SUCCESS; } /** * _cairo_beos_bitmap_to_surface: * * Returns an addrefed image surface for a BBitmap. The bitmap need not outlive * the surface. **/ static cairo_image_surface_t* _cairo_beos_bitmap_to_surface (BBitmap* bitmap) { color_space format = bitmap->ColorSpace(); if (format != B_RGB32 && format != B_RGBA32) { BBitmap bmp(bitmap->Bounds(), B_RGB32, true); BView view(bitmap->Bounds(), "Cairo bitmap drawing view", B_FOLLOW_ALL_SIDES, 0); bmp.AddChild(&view); view.LockLooper(); view.DrawBitmap(bitmap, BPoint(0.0, 0.0)); view.Sync(); cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp); view.UnlockLooper(); bmp.RemoveChild(&view); return imgsurf; } cairo_format_t cformat = format == B_RGB32 ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; BRect bounds(bitmap->Bounds()); unsigned char* bits = reinterpret_cast<unsigned char*>(bitmap->Bits()); int width = bounds.IntegerWidth() + 1; int height = bounds.IntegerHeight() + 1; unsigned char* premultiplied; if (cformat == CAIRO_FORMAT_ARGB32) { premultiplied = premultiply_bgra (bits, width, height, bitmap->BytesPerRow()); } else { premultiplied = reinterpret_cast<unsigned char*>( _cairo_malloc_ab(bitmap->BytesPerRow(), height)); if (premultiplied) memcpy(premultiplied, bits, bitmap->BytesPerRow() * height); } if (!premultiplied) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_t*> (cairo_image_surface_create_for_data(premultiplied, cformat, width, height, bitmap->BytesPerRow())); if (surf->base.status) free(premultiplied); else _cairo_image_surface_assume_ownership_of_data(surf); return surf; } /** * _cairo_image_surface_to_bitmap: * * Converts an image surface to a BBitmap. The return value must be freed with * delete. **/ static BBitmap* _cairo_image_surface_to_bitmap (cairo_image_surface_t* surface) { BRect size(0.0, 0.0, surface->width - 1, surface->height - 1); switch (surface->format) { case CAIRO_FORMAT_ARGB32: { BBitmap* data = new BBitmap(size, B_RGBA32); unpremultiply_bgra (surface->data, surface->width, surface->height, surface->stride, reinterpret_cast<unsigned char*>(data->Bits())); return data; } case CAIRO_FORMAT_RGB24: { BBitmap* data = new BBitmap(size, B_RGB32); memcpy(data->Bits(), surface->data, surface->height * surface->stride); return data; } default: assert(0); return NULL; } } /** * _cairo_op_to_be_op: * * Converts a cairo drawing operator to a beos drawing_mode. Returns true if * the operator could be converted, false otherwise. **/ static bool _cairo_op_to_be_op (cairo_operator_t cairo_op, drawing_mode* beos_op) { switch (cairo_op) { case CAIRO_OPERATOR_SOURCE: *beos_op = B_OP_COPY; return true; case CAIRO_OPERATOR_OVER: *beos_op = B_OP_ALPHA; return true; case CAIRO_OPERATOR_ADD: // Does not actually work // XXX This is a fundamental compositing operator, it has to work! #if 1 return false; #else *beos_op = B_OP_ADD; return true; #endif case CAIRO_OPERATOR_CLEAR: // Does not map to B_OP_ERASE - it replaces the dest with the low // color, instead of transparency; could be done by setting low // color appropriately. case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_SATURATE: default: return false; } } static cairo_surface_t * _cairo_beos_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); if (width <= 0) width = 1; if (height <= 0) height = 1; BRect rect(0.0, 0.0, width - 1, height - 1); BBitmap* bmp; switch (content) { case CAIRO_CONTENT_ALPHA: return NULL; case CAIRO_CONTENT_COLOR_ALPHA: bmp = new BBitmap(rect, B_RGBA32, true); break; case CAIRO_CONTENT_COLOR: // Match the color depth if (surface->bitmap) { color_space space = surface->bitmap->ColorSpace(); // No alpha was requested -> make sure not to return // a surface with alpha if (space == B_RGBA32) space = B_RGB32; if (space == B_RGBA15) space = B_RGB15; bmp = new BBitmap(rect, space, true); } else { BScreen scr(surface->view->Window()); color_space space = B_RGB32; if (scr.IsValid()) space = scr.ColorSpace(); bmp = new BBitmap(rect, space, true); } break; default: ASSERT_NOT_REACHED; return NULL; } BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0); bmp->AddChild(view); return _cairo_beos_surface_create_internal(view, bmp, true); } static cairo_status_t _cairo_beos_surface_finish (void *abstract_surface) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); if (surface->owns_bitmap_view) { if (surface->bitmap) surface->bitmap->RemoveChild(surface->view); delete surface->view; delete surface->bitmap; surface->view = NULL; surface->bitmap = NULL; } cairo_region_destroy (surface->clip_region); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_beos_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); AutoLockView locker(surface->view); if (!locker) return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do? surface->view->Sync(); if (surface->bitmap) { *image_out = _cairo_beos_bitmap_to_surface (surface->bitmap); if (unlikely ((*image_out)->base.status)) return (*image_out)->base.status; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } BBitmap* bmp; if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK) return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE *image_out = _cairo_beos_bitmap_to_surface (bmp); if (unlikely ((*image_out)->base.status)) { delete bmp; return (*image_out)->base.status; } *image_extra = bmp; return CAIRO_STATUS_SUCCESS; } static void _cairo_beos_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); if (image_extra != NULL) { BBitmap* bmp = static_cast<BBitmap*>(image_extra); delete bmp; } } static cairo_status_t _cairo_beos_surface_acquire_dest_image (void *abstract_surface, cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, cairo_rectangle_int_t *image_rect, void **image_extra) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); AutoLockView locker(surface->view); if (!locker) { *image_out = NULL; *image_extra = NULL; return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO; } if (surface->bitmap) { surface->view->Sync(); *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap); if (unlikely ((*image_out)->base.status)) return (*image_out)->base.status; image_rect->x = 0; image_rect->y = 0; image_rect->width = (*image_out)->width; image_rect->height = (*image_out)->height; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect)); BRect rect; BBitmap* bitmap; ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap, &rect, &b_interest_rect); if (status == NOT_VISIBLE) { *image_out = NULL; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } if (status == ERROR) return CAIRO_STATUS_NO_MEMORY; *image_rect = _brect_to_cairo_rectangle(rect); *image_out = _cairo_beos_bitmap_to_surface(bitmap); delete bitmap; if (unlikely ((*image_out)->base.status)) return (*image_out)->base.status; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_beos_surface_release_dest_image (void *abstract_surface, cairo_rectangle_int_t *intersect_rect, cairo_image_surface_t *image, cairo_rectangle_int_t *image_rect, void *image_extra) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); AutoLockView locker(surface->view); if (!locker) return; BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image); surface->view->PushState(); surface->view->SetDrawingMode(B_OP_COPY); surface->view->DrawBitmap (bitmap_to_draw, _cairo_rectangle_to_brect (image_rect)); surface->view->PopState(); delete bitmap_to_draw; cairo_surface_destroy(&image->base); } static cairo_int_status_t _cairo_beos_surface_composite (cairo_operator_t op, cairo_pattern_t *src, cairo_pattern_t *mask, void *dst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height, cairo_region_t *clip_region) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( dst); cairo_int_status_t status; AutoLockView locker(surface->view); if (!locker) return CAIRO_INT_STATUS_SUCCESS; drawing_mode mode; if (!_cairo_op_to_be_op(op, &mode)) return CAIRO_INT_STATUS_UNSUPPORTED; // XXX Masks are not yet supported if (mask) return CAIRO_INT_STATUS_UNSUPPORTED; // XXX should eventually support the others if (src->type != CAIRO_PATTERN_TYPE_SURFACE || src->extend != CAIRO_EXTEND_NONE) { return CAIRO_INT_STATUS_UNSUPPORTED; } // Can we maybe support other matrices as well? (scale? if the filter is right) int itx, ity; if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_beos_surface_set_clip_region (surface, clip_region); if (unlikely (status)) return status; BRect srcRect(src_x + itx, src_y + ity, src_x + itx + width - 1, src_y + ity + height - 1); BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1); cairo_surface_t* src_surface = reinterpret_cast<cairo_surface_pattern_t*>(src)-> surface; // Get a bitmap BBitmap* bmp = NULL; bool free_bmp = false; if (_cairo_surface_is_image(src_surface)) { cairo_image_surface_t* img_surface = reinterpret_cast<cairo_image_surface_t*>(src_surface); bmp = _cairo_image_surface_to_bitmap(img_surface); free_bmp = true; } else if (src_surface->backend == surface->base.backend) { cairo_beos_surface_t *beos_surface = reinterpret_cast<cairo_beos_surface_t*>(src_surface); if (beos_surface->bitmap) { AutoLockView locker(beos_surface->view); if (locker) beos_surface->view->Sync(); bmp = beos_surface->bitmap; } else { _cairo_beos_view_to_bitmap(surface->view, &bmp); free_bmp = true; } } if (!bmp) return CAIRO_INT_STATUS_UNSUPPORTED; // So, BeOS seems to screw up painting an opaque bitmap onto a // translucent one (it makes them partly transparent). Just return // unsupported. if (bmp->ColorSpace() == B_RGB32 && surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32 && (mode == B_OP_COPY || mode == B_OP_ALPHA)) { if (free_bmp) delete bmp; return CAIRO_INT_STATUS_UNSUPPORTED; } // Draw it on screen. surface->view->PushState(); // If our image rect is only a subrect of the desired size, and we // aren't using B_OP_ALPHA, then we need to fill the rect first. if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) { rgb_color black = { 0, 0, 0, 0 }; surface->view->SetDrawingMode(mode); surface->view->SetHighColor(black); surface->view->FillRect(dstRect); } if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) { mode = B_OP_COPY; } surface->view->SetDrawingMode(mode); if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); else surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); surface->view->DrawBitmap(bmp, srcRect, dstRect); surface->view->PopState(); if (free_bmp) delete bmp; return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_beos_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects, cairo_region_t *clip_region) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); cairo_int_status_t status; if (num_rects <= 0) return CAIRO_INT_STATUS_SUCCESS; AutoLockView locker(surface->view); if (!locker) return CAIRO_INT_STATUS_SUCCESS; drawing_mode mode; if (!_cairo_op_to_be_op(op, &mode)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_beos_surface_set_clip_region (surface, clip_region); if (unlikely (status)) return status; rgb_color be_color = _cairo_color_to_be_color(color); if (mode == B_OP_ALPHA && be_color.alpha == 0xFF) mode = B_OP_COPY; // For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied // color info. This is only relevant when drawing into an rgb24 buffer // (as for others, we can convert when asked for the image) if (mode == B_OP_COPY && be_color.alpha != 0xFF && (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32)) { be_color.red = color->red_short >> 8; be_color.green = color->green_short >> 8; be_color.blue = color->blue_short >> 8; } surface->view->PushState(); surface->view->SetDrawingMode(mode); surface->view->SetHighColor(be_color); if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); else surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); for (int i = 0; i < num_rects; ++i) surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i])); surface->view->PopState(); return CAIRO_INT_STATUS_SUCCESS; } static cairo_bool_t _cairo_beos_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>( abstract_surface); AutoLockView locker(surface->view); if (!locker) return FALSE; *rectangle = _brect_to_cairo_rectangle (surface->view->Bounds()); return TRUE; } static const struct _cairo_surface_backend cairo_beos_surface_backend = { CAIRO_SURFACE_TYPE_BEOS, _cairo_beos_surface_create_similar, _cairo_beos_surface_finish, _cairo_beos_surface_acquire_source_image, _cairo_beos_surface_release_source_image, _cairo_beos_surface_acquire_dest_image, _cairo_beos_surface_release_dest_image, NULL, /* clone_similar */ _cairo_beos_surface_composite, /* composite */ _cairo_beos_surface_fill_rectangles, NULL, /* composite_trapezoids */ NULL, /* create_span_renderer */ NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_beos_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ NULL, /* scaled_font_fini */ NULL, /* scaled_glyph_fini */ NULL, /* paint */ NULL, /* mask */ NULL, /* stroke */ NULL, /* fill */ NULL /* show_glyphs */ }; static cairo_surface_t * _cairo_beos_surface_create_internal (BView* view, BBitmap* bmp, bool owns_bitmap_view) { // Must use malloc, because cairo code will use free() on the surface cairo_beos_surface_t *surface = static_cast<cairo_beos_surface_t*>( malloc(sizeof(cairo_beos_surface_t))); if (surface == NULL) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return const_cast<cairo_surface_t*>(&_cairo_surface_nil); } cairo_content_t content = CAIRO_CONTENT_COLOR; if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15)) content = CAIRO_CONTENT_COLOR_ALPHA; _cairo_surface_init (&surface->base, &cairo_beos_surface_backend, NULL, /* device */ content); surface->view = view; surface->bitmap = bmp; surface->owns_bitmap_view = owns_bitmap_view; surface->clip_region = NULL; return &surface->base; } /** * cairo_beos_surface_create: * @view: The view to draw on * * Creates a Cairo surface that draws onto a BeOS BView. * The caller must ensure that the view does not get deleted before the surface. * If the view is attached to a bitmap rather than an on-screen window, use * cairo_beos_surface_create_for_bitmap() instead of this function. * * Since: TBD **/ cairo_surface_t * cairo_beos_surface_create (BView* view) { return cairo_beos_surface_create_for_bitmap(view, NULL); } /** * cairo_beos_surface_create_for_bitmap: * @view: The view to draw on * @bmp: The bitmap to which the view is attached * * Creates a Cairo surface that draws onto a BeOS BView which is attached to a * BBitmap. * The caller must ensure that the view and the bitmap do not get deleted * before the surface. * * For views that draw to a bitmap (as opposed to a screen), use this function * rather than cairo_beos_surface_create(). Not using this function WILL lead to * incorrect behaviour. * * For now, only views that draw to the entire area of bmp are supported. * The view must already be attached to the bitmap. * * Since: TBD **/ cairo_surface_t * cairo_beos_surface_create_for_bitmap (BView* view, BBitmap* bmp) { return _cairo_beos_surface_create_internal(view, bmp); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-beos.h������������������������������������0000664�0000000�0000000�00000003725�12710376503�0024503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Christian Biesinger <cbiesinger@web.de> * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Christian Biesinger * <cbiesinger@web.de> * * Contributor(s): */ #ifndef CAIRO_BEOS_H #define CAIRO_BEOS_H #include "cairo.h" #if CAIRO_HAS_BEOS_SURFACE #include <View.h> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_beos_surface_create (BView* view); cairo_public cairo_surface_t * cairo_beos_surface_create_for_bitmap (BView* view, BBitmap* bmp); CAIRO_END_DECLS #else /* CAIRO_HAS_BEOS_SURFACE */ # error Cairo was not compiled with support for the beos backend #endif /* CAIRO_HAS_BEOS_SURFACE */ #endif /* CAIRO_BEOS_H */ �������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-botor-scan-converter.c��������������������0000664�0000000�0000000�00000152730�12710376503�0027623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2007 David Turner * Copyright © 2008 M Joonas Pihlaja * Copyright © 2008 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * M Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-freelist-private.h" #include "cairo-combsort-inline.h" #include <setjmp.h> #define STEP_X CAIRO_FIXED_ONE #define STEP_Y CAIRO_FIXED_ONE #define UNROLL3(x) x x x #define STEP_XY (2*STEP_X*STEP_Y) /* Unit area in the step. */ #define AREA_TO_ALPHA(c) (((c)*255 + STEP_XY/2) / STEP_XY) typedef struct _cairo_bo_intersect_ordinate { int32_t ordinate; enum { EXACT, INEXACT } exactness; } cairo_bo_intersect_ordinate_t; typedef struct _cairo_bo_intersect_point { cairo_bo_intersect_ordinate_t x; cairo_bo_intersect_ordinate_t y; } cairo_bo_intersect_point_t; struct quorem { cairo_fixed_t quo; cairo_fixed_t rem; }; struct run { struct run *next; int sign; cairo_fixed_t y; }; typedef struct edge { cairo_list_t link; cairo_edge_t edge; /* Current x coordinate and advancement. * Initialised to the x coordinate of the top of the * edge. The quotient is in cairo_fixed_t units and the * remainder is mod dy in cairo_fixed_t units. */ cairo_fixed_t dy; struct quorem x; struct quorem dxdy; struct quorem dxdy_full; cairo_bool_t vertical; unsigned int flags; int current_sign; struct run *runs; } edge_t; enum { START = 0x1, STOP = 0x2, }; /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef enum { EVENT_TYPE_STOP, EVENT_TYPE_INTERSECTION, EVENT_TYPE_START } event_type_t; typedef struct _event { cairo_fixed_t y; event_type_t type; } event_t; typedef struct _start_event { cairo_fixed_t y; event_type_t type; edge_t *edge; } start_event_t; typedef struct _queue_event { cairo_fixed_t y; event_type_t type; edge_t *e1; edge_t *e2; } queue_event_t; typedef struct _pqueue { int size, max_size; event_t **elements; event_t *elements_embedded[1024]; } pqueue_t; struct cell { struct cell *prev; struct cell *next; int x; int uncovered_area; int covered_height; }; typedef struct _sweep_line { cairo_list_t active; cairo_list_t stopped; cairo_list_t *insert_cursor; cairo_bool_t is_vertical; cairo_fixed_t current_row; cairo_fixed_t current_subrow; struct coverage { struct cell head; struct cell tail; struct cell *cursor; int count; cairo_freepool_t pool; } coverage; struct event_queue { pqueue_t pq; event_t **start_events; cairo_freepool_t pool; } queue; cairo_freepool_t runs; jmp_buf unwind; } sweep_line_t; cairo_always_inline static struct quorem floored_divrem (int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo--; qr.rem += b; } return qr; } static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo--; qr.rem += b; } return qr; } static cairo_fixed_t line_compute_intersection_x_for_y (const cairo_line_t *line, cairo_fixed_t y) { cairo_fixed_t x, dy; if (y == line->p1.y) return line->p1.x; if (y == line->p2.y) return line->p2.x; x = line->p1.x; dy = line->p2.y - line->p1.y; if (dy != 0) { x += _cairo_fixed_mul_div_floor (y - line->p1.y, line->p2.x - line->p1.x, dy); } return x; } /* * We need to compare the x-coordinates of a pair of lines for a particular y, * without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 128 bit arithmetic. For certain, but common, * input we can reduce this down to a single 32 bit compare by inspecting the * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) * * See the similar discussion for _slope_compare(). */ static int edges_compare_x_for_y_general (const cairo_edge_t *a, const cairo_edge_t *b, int32_t y) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t dx; int32_t adx, ady; int32_t bdx, bdy; enum { HAVE_NONE = 0x0, HAVE_DX = 0x1, HAVE_ADX = 0x2, HAVE_DX_ADX = HAVE_DX | HAVE_ADX, HAVE_BDX = 0x4, HAVE_DX_BDX = HAVE_DX | HAVE_BDX, HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX } have_dx_adx_bdx = HAVE_ALL; /* don't bother solving for abscissa if the edges' bounding boxes * can be used to order them. */ { int32_t amin, amax; int32_t bmin, bmax; if (a->line.p1.x < a->line.p2.x) { amin = a->line.p1.x; amax = a->line.p2.x; } else { amin = a->line.p2.x; amax = a->line.p1.x; } if (b->line.p1.x < b->line.p2.x) { bmin = b->line.p1.x; bmax = b->line.p2.x; } else { bmin = b->line.p2.x; bmax = b->line.p1.x; } if (amax < bmin) return -1; if (amin > bmax) return +1; } ady = a->line.p2.y - a->line.p1.y; adx = a->line.p2.x - a->line.p1.x; if (adx == 0) have_dx_adx_bdx &= ~HAVE_ADX; bdy = b->line.p2.y - b->line.p1.y; bdx = b->line.p2.x - b->line.p1.x; if (bdx == 0) have_dx_adx_bdx &= ~HAVE_BDX; dx = a->line.p1.x - b->line.p1.x; if (dx == 0) have_dx_adx_bdx &= ~HAVE_DX; #define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) #define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->line.p1.y) #define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->line.p1.y) switch (have_dx_adx_bdx) { default: case HAVE_NONE: return 0; case HAVE_DX: /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ return dx; /* ady * bdy is positive definite */ case HAVE_ADX: /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ return adx; /* bdy * (y - a->top.y) is positive definite */ case HAVE_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy */ return -bdx; /* ady * (y - b->top.y) is positive definite */ case HAVE_ADX_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ if ((adx ^ bdx) < 0) { return adx; } else if (a->line.p1.y == b->line.p1.y) { /* common origin */ cairo_int64_t adx_bdy, bdx_ady; /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ adx_bdy = _cairo_int32x32_64_mul (adx, bdy); bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } else return _cairo_int128_cmp (A, B); case HAVE_DX_ADX: /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ if ((-adx ^ dx) < 0) { return dx; } else { cairo_int64_t ady_dx, dy_adx; ady_dx = _cairo_int32x32_64_mul (ady, dx); dy_adx = _cairo_int32x32_64_mul (a->line.p1.y - y, adx); return _cairo_int64_cmp (ady_dx, dy_adx); } case HAVE_DX_BDX: /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ if ((bdx ^ dx) < 0) { return dx; } else { cairo_int64_t bdy_dx, dy_bdx; bdy_dx = _cairo_int32x32_64_mul (bdy, dx); dy_bdx = _cairo_int32x32_64_mul (y - b->line.p1.y, bdx); return _cairo_int64_cmp (bdy_dx, dy_bdx); } case HAVE_ALL: /* XXX try comparing (a->line.p2.x - b->line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); } #undef B #undef A #undef L } /* * We need to compare the x-coordinate of a line for a particular y wrt to a * given x, without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ X * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. * * See the similar discussion for _slope_compare() and * edges_compare_x_for_y_general(). */ static int edge_compare_for_y_against_x (const cairo_edge_t *a, int32_t y, int32_t x) { int32_t adx, ady; int32_t dx, dy; cairo_int64_t L, R; if (a->line.p1.x <= a->line.p2.x) { if (x < a->line.p1.x) return 1; if (x > a->line.p2.x) return -1; } else { if (x < a->line.p2.x) return 1; if (x > a->line.p1.x) return -1; } adx = a->line.p2.x - a->line.p1.x; dx = x - a->line.p1.x; if (adx == 0) return -dx; if (dx == 0 || (adx ^ dx) < 0) return adx; dy = y - a->line.p1.y; ady = a->line.p2.y - a->line.p1.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); return _cairo_int64_cmp (L, R); } static int edges_compare_x_for_y (const cairo_edge_t *a, const cairo_edge_t *b, int32_t y) { /* If the sweep-line is currently on an end-point of a line, * then we know its precise x value (and considering that we often need to * compare events at end-points, this happens frequently enough to warrant * special casing). */ enum { HAVE_NEITHER = 0x0, HAVE_AX = 0x1, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; int32_t ax, bx; /* XXX given we have x and dx? */ if (y == a->line.p1.y) ax = a->line.p1.x; else if (y == a->line.p2.y) ax = a->line.p2.x; else have_ax_bx &= ~HAVE_AX; if (y == b->line.p1.y) bx = b->line.p1.x; else if (y == b->line.p2.y) bx = b->line.p2.x; else have_ax_bx &= ~HAVE_BX; switch (have_ax_bx) { default: case HAVE_NEITHER: return edges_compare_x_for_y_general (a, b, y); case HAVE_AX: return -edge_compare_for_y_against_x (b, y, ax); case HAVE_BX: return edge_compare_for_y_against_x (a, y, bx); case HAVE_BOTH: return ax - bx; } } static inline int slope_compare (const edge_t *a, const edge_t *b) { cairo_int64_t L, R; int cmp; cmp = a->dxdy.quo - b->dxdy.quo; if (cmp) return cmp; if (a->dxdy.rem == 0) return -b->dxdy.rem; if (b->dxdy.rem == 0) return a->dxdy.rem; L = _cairo_int32x32_64_mul (b->dy, a->dxdy.rem); R = _cairo_int32x32_64_mul (a->dy, b->dxdy.rem); return _cairo_int64_cmp (L, R); } static inline int line_equal (const cairo_line_t *a, const cairo_line_t *b) { return a->p1.x == b->p1.x && a->p1.y == b->p1.y && a->p2.x == b->p2.x && a->p2.y == b->p2.y; } static inline int sweep_line_compare_edges (const edge_t *a, const edge_t *b, cairo_fixed_t y) { int cmp; if (line_equal (&a->edge.line, &b->edge.line)) return 0; cmp = edges_compare_x_for_y (&a->edge, &b->edge, y); if (cmp) return cmp; return slope_compare (a, b); } static inline cairo_int64_t det32_64 (int32_t a, int32_t b, int32_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), _cairo_int32x32_64_mul (b, c)); } static inline cairo_int128_t det64x32_128 (cairo_int64_t a, int32_t b, cairo_int64_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), _cairo_int64x32_128_mul (c, b)); } /* Compute the intersection of two lines as defined by two edges. The * result is provided as a coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. */ static cairo_bool_t intersect_lines (const edge_t *a, const edge_t *b, cairo_bo_intersect_point_t *intersection) { cairo_int64_t a_det, b_det; /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm begins. * What we're doing to mitigate this is to perform clamping in * cairo_bo_tessellate_polygon(). */ int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; cairo_int64_t den_det; cairo_int64_t R; cairo_quorem64_t qr; den_det = det32_64 (dx1, dy1, dx2, dy2); /* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by * avoiding the division? * * X = ax + t * adx = bx + s * bdx; * Y = ay + t * ady = by + s * bdy; * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) * => t * L = R * * Therefore we can reject any intersection (under the criteria for * valid intersection events) if: * L^R < 0 => t < 0, or * L<R => t > 1 * * (where top/bottom must at least extend to the line endpoints). * * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) */ R = det32_64 (dx2, dy2, b->edge.line.p1.x - a->edge.line.p1.x, b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } R = det32_64 (dy1, dx1, a->edge.line.p1.y - b->edge.line.p1.y, a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } /* We now know that the two lines should intersect within range. */ a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, a->edge.line.p2.x, a->edge.line.p2.y); b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, b->edge.line.p2.x, b->edge.line.p2.y); /* x = det (a_det, dx1, b_det, dx2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, b_det, dx2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->x.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->x.exactness = INEXACT; } #endif intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); /* y = det (a_det, dy1, b_det, dy2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, b_det, dy2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->y.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { /* compute ceiling away from zero */ qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); intersection->y.exactness = INEXACT; } #endif intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); return TRUE; } static int bo_intersect_ordinate_32_compare (int32_t a, int32_t b, int exactness) { int cmp; /* First compare the quotient */ cmp = a - b; if (cmp) return cmp; /* With quotient identical, if remainder is 0 then compare equal */ /* Otherwise, the non-zero remainder makes a > b */ return -(INEXACT == exactness); } /* Does the given edge contain the given point. The point must already * be known to be contained within the line determined by the edge, * (most likely the point results from an intersection of this edge * with another). * * If we had exact arithmetic, then this function would simply be a * matter of examining whether the y value of the point lies within * the range of y values of the edge. But since intersection points * are not exact due to being rounded to the nearest integer within * the available precision, we must also examine the x value of the * point. * * The definition of "contains" here is that the given intersection * point will be seen by the sweep line after the start event for the * given edge and before the stop event for the edge. See the comments * in the implementation for more details. */ static cairo_bool_t bo_edge_contains_intersect_point (const edge_t *edge, cairo_bo_intersect_point_t *point) { int cmp_top, cmp_bottom; /* XXX: When running the actual algorithm, we don't actually need to * compare against edge->top at all here, since any intersection above * top is eliminated early via a slope comparison. We're leaving these * here for now only for the sake of the quadratic-time intersection * finder which needs them. */ cmp_top = bo_intersect_ordinate_32_compare (point->y.ordinate, edge->edge.top, point->y.exactness); if (cmp_top < 0) return FALSE; cmp_bottom = bo_intersect_ordinate_32_compare (point->y.ordinate, edge->edge.bottom, point->y.exactness); if (cmp_bottom > 0) return FALSE; if (cmp_top > 0 && cmp_bottom < 0) return TRUE; /* At this stage, the point lies on the same y value as either * edge->top or edge->bottom, so we have to examine the x value in * order to properly determine containment. */ /* If the y value of the point is the same as the y value of the * top of the edge, then the x value of the point must be greater * to be considered as inside the edge. Similarly, if the y value * of the point is the same as the y value of the bottom of the * edge, then the x value of the point must be less to be * considered as inside. */ if (cmp_top == 0) { cairo_fixed_t top_x; top_x = line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.top); return bo_intersect_ordinate_32_compare (top_x, point->x.ordinate, point->x.exactness) < 0; } else { /* cmp_bottom == 0 */ cairo_fixed_t bot_x; bot_x = line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.bottom); return bo_intersect_ordinate_32_compare (point->x.ordinate, bot_x, point->x.exactness) < 0; } } static cairo_bool_t edge_intersect (const edge_t *a, const edge_t *b, cairo_point_t *intersection) { cairo_bo_intersect_point_t quorem; if (! intersect_lines (a, b, &quorem)) return FALSE; if (a->edge.top != a->edge.line.p1.y || a->edge.bottom != a->edge.line.p2.y) { if (! bo_edge_contains_intersect_point (a, &quorem)) return FALSE; } if (b->edge.top != b->edge.line.p1.y || b->edge.bottom != b->edge.line.p2.y) { if (! bo_edge_contains_intersect_point (b, &quorem)) return FALSE; } /* Now that we've correctly compared the intersection point and * determined that it lies within the edge, then we know that we * no longer need any more bits of storage for the intersection * than we do for our edge coordinates. We also no longer need the * remainder from the division. */ intersection->x = quorem.x.ordinate; intersection->y = quorem.y.ordinate; return TRUE; } static inline int event_compare (const event_t *a, const event_t *b) { return a->y - b->y; } static void pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; } static void pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_bool_t pqueue_grow (pqueue_t *pq) { event_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (event_t *)); if (unlikely (new_elements == NULL)) return FALSE; memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (event_t *)); if (unlikely (new_elements == NULL)) return FALSE; } pq->elements = new_elements; return TRUE; } static inline void pqueue_push (sweep_line_t *sweep_line, event_t *event) { event_t **elements; int i, parent; if (unlikely (sweep_line->queue.pq.size + 1 == sweep_line->queue.pq.max_size)) { if (unlikely (! pqueue_grow (&sweep_line->queue.pq))) { longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } } elements = sweep_line->queue.pq.elements; for (i = ++sweep_line->queue.pq.size; i != PQ_FIRST_ENTRY && event_compare (event, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = event; } static inline void pqueue_pop (pqueue_t *pq) { event_t **elements = pq->elements; event_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && event_compare (elements[child+1], elements[child]) < 0) { child++; } if (event_compare (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline void event_insert (sweep_line_t *sweep_line, event_type_t type, edge_t *e1, edge_t *e2, cairo_fixed_t y) { queue_event_t *event; event = _cairo_freepool_alloc (&sweep_line->queue.pool); if (unlikely (event == NULL)) { longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } event->y = y; event->type = type; event->e1 = e1; event->e2 = e2; pqueue_push (sweep_line, (event_t *) event); } static void event_delete (sweep_line_t *sweep_line, event_t *event) { _cairo_freepool_free (&sweep_line->queue.pool, event); } static inline event_t * event_next (sweep_line_t *sweep_line) { event_t *event, *cmp; event = sweep_line->queue.pq.elements[PQ_FIRST_ENTRY]; cmp = *sweep_line->queue.start_events; if (event == NULL || (cmp != NULL && event_compare (cmp, event) < 0)) { event = cmp; sweep_line->queue.start_events++; } else { pqueue_pop (&sweep_line->queue.pq); } return event; } CAIRO_COMBSORT_DECLARE (start_event_sort, event_t *, event_compare) static inline void event_insert_stop (sweep_line_t *sweep_line, edge_t *edge) { event_insert (sweep_line, EVENT_TYPE_STOP, edge, NULL, edge->edge.bottom); } static inline void event_insert_if_intersect_below_current_y (sweep_line_t *sweep_line, edge_t *left, edge_t *right) { cairo_point_t intersection; /* start points intersect */ if (left->edge.line.p1.x == right->edge.line.p1.x && left->edge.line.p1.y == right->edge.line.p1.y) { return; } /* end points intersect, process DELETE events first */ if (left->edge.line.p2.x == right->edge.line.p2.x && left->edge.line.p2.y == right->edge.line.p2.y) { return; } if (slope_compare (left, right) <= 0) return; if (! edge_intersect (left, right, &intersection)) return; event_insert (sweep_line, EVENT_TYPE_INTERSECTION, left, right, intersection.y); } static inline edge_t * link_to_edge (cairo_list_t *link) { return (edge_t *) link; } static void sweep_line_insert (sweep_line_t *sweep_line, edge_t *edge) { cairo_list_t *pos; cairo_fixed_t y = sweep_line->current_subrow; pos = sweep_line->insert_cursor; if (pos == &sweep_line->active) pos = sweep_line->active.next; if (pos != &sweep_line->active) { int cmp; cmp = sweep_line_compare_edges (link_to_edge (pos), edge, y); if (cmp < 0) { while (pos->next != &sweep_line->active && sweep_line_compare_edges (link_to_edge (pos->next), edge, y) < 0) { pos = pos->next; } } else if (cmp > 0) { do { pos = pos->prev; } while (pos != &sweep_line->active && sweep_line_compare_edges (link_to_edge (pos), edge, y) > 0); } } cairo_list_add (&edge->link, pos); sweep_line->insert_cursor = &edge->link; } inline static void coverage_rewind (struct coverage *cells) { cells->cursor = &cells->head; } static void coverage_init (struct coverage *cells) { _cairo_freepool_init (&cells->pool, sizeof (struct cell)); cells->head.prev = NULL; cells->head.next = &cells->tail; cells->head.x = INT_MIN; cells->tail.prev = &cells->head; cells->tail.next = NULL; cells->tail.x = INT_MAX; cells->count = 0; coverage_rewind (cells); } static void coverage_fini (struct coverage *cells) { _cairo_freepool_fini (&cells->pool); } inline static void coverage_reset (struct coverage *cells) { cells->head.next = &cells->tail; cells->tail.prev = &cells->head; cells->count = 0; _cairo_freepool_reset (&cells->pool); coverage_rewind (cells); } static struct cell * coverage_alloc (sweep_line_t *sweep_line, struct cell *tail, int x) { struct cell *cell; cell = _cairo_freepool_alloc (&sweep_line->coverage.pool); if (unlikely (NULL == cell)) { longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } tail->prev->next = cell; cell->prev = tail->prev; cell->next = tail; tail->prev = cell; cell->x = x; cell->uncovered_area = 0; cell->covered_height = 0; sweep_line->coverage.count++; return cell; } inline static struct cell * coverage_find (sweep_line_t *sweep_line, int x) { struct cell *cell; cell = sweep_line->coverage.cursor; if (unlikely (cell->x > x)) { do { if (cell->prev->x < x) break; cell = cell->prev; } while (TRUE); } else { if (cell->x == x) return cell; do { UNROLL3({ cell = cell->next; if (cell->x >= x) break; }); } while (TRUE); } if (cell->x != x) cell = coverage_alloc (sweep_line, cell, x); return sweep_line->coverage.cursor = cell; } static void coverage_render_cells (sweep_line_t *sweep_line, cairo_fixed_t left, cairo_fixed_t right, cairo_fixed_t y1, cairo_fixed_t y2, int sign) { int fx1, fx2; int ix1, ix2; int dx, dy; /* Orient the edge left-to-right. */ dx = right - left; if (dx >= 0) { ix1 = _cairo_fixed_integer_part (left); fx1 = _cairo_fixed_fractional_part (left); ix2 = _cairo_fixed_integer_part (right); fx2 = _cairo_fixed_fractional_part (right); dy = y2 - y1; } else { ix1 = _cairo_fixed_integer_part (right); fx1 = _cairo_fixed_fractional_part (right); ix2 = _cairo_fixed_integer_part (left); fx2 = _cairo_fixed_fractional_part (left); dx = -dx; sign = -sign; dy = y1 - y2; y1 = y2 - dy; y2 = y1 + dy; } /* Add coverage for all pixels [ix1,ix2] on this row crossed * by the edge. */ { struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx); struct cell *cell; cell = sweep_line->coverage.cursor; if (cell->x != ix1) { if (unlikely (cell->x > ix1)) { do { if (cell->prev->x < ix1) break; cell = cell->prev; } while (TRUE); } else do { UNROLL3({ if (cell->x >= ix1) break; cell = cell->next; }); } while (TRUE); if (cell->x != ix1) cell = coverage_alloc (sweep_line, cell, ix1); } cell->uncovered_area += sign * y.quo * (STEP_X + fx1); cell->covered_height += sign * y.quo; y.quo += y1; cell = cell->next; if (cell->x != ++ix1) cell = coverage_alloc (sweep_line, cell, ix1); if (ix1 < ix2) { struct quorem dydx_full = floored_divrem (STEP_X*dy, dx); do { cairo_fixed_t y_skip = dydx_full.quo; y.rem += dydx_full.rem; if (y.rem >= dx) { ++y_skip; y.rem -= dx; } y.quo += y_skip; y_skip *= sign; cell->covered_height += y_skip; cell->uncovered_area += y_skip*STEP_X; cell = cell->next; if (cell->x != ++ix1) cell = coverage_alloc (sweep_line, cell, ix1); } while (ix1 != ix2); } cell->uncovered_area += sign*(y2 - y.quo)*fx2; cell->covered_height += sign*(y2 - y.quo); sweep_line->coverage.cursor = cell; } } inline static void full_inc_edge (edge_t *edge) { edge->x.quo += edge->dxdy_full.quo; edge->x.rem += edge->dxdy_full.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } } static void full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign) { struct cell *cell; cairo_fixed_t x1, x2; int ix1, ix2; int frac; edge->current_sign = sign; ix1 = _cairo_fixed_integer_part (edge->x.quo); if (edge->vertical) { frac = _cairo_fixed_fractional_part (edge->x.quo); cell = coverage_find (sweep_line, ix1); cell->covered_height += sign * STEP_Y; cell->uncovered_area += sign * 2 * frac * STEP_Y; return; } x1 = edge->x.quo; full_inc_edge (edge); x2 = edge->x.quo; ix2 = _cairo_fixed_integer_part (edge->x.quo); /* Edge is entirely within a column? */ if (likely (ix1 == ix2)) { frac = _cairo_fixed_fractional_part (x1) + _cairo_fixed_fractional_part (x2); cell = coverage_find (sweep_line, ix1); cell->covered_height += sign * STEP_Y; cell->uncovered_area += sign * frac * STEP_Y; return; } coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign); } static void full_nonzero (sweep_line_t *sweep_line) { cairo_list_t *pos; sweep_line->is_vertical = TRUE; pos = sweep_line->active.next; do { edge_t *left = link_to_edge (pos), *right; int winding = left->edge.dir; sweep_line->is_vertical &= left->vertical; pos = left->link.next; do { if (unlikely (pos == &sweep_line->active)) { full_add_edge (sweep_line, left, +1); return; } right = link_to_edge (pos); pos = pos->next; sweep_line->is_vertical &= right->vertical; winding += right->edge.dir; if (0 == winding) { if (pos == &sweep_line->active || link_to_edge (pos)->x.quo != right->x.quo) { break; } } if (! right->vertical) full_inc_edge (right); } while (TRUE); full_add_edge (sweep_line, left, +1); full_add_edge (sweep_line, right, -1); } while (pos != &sweep_line->active); } static void full_evenodd (sweep_line_t *sweep_line) { cairo_list_t *pos; sweep_line->is_vertical = TRUE; pos = sweep_line->active.next; do { edge_t *left = link_to_edge (pos), *right; int winding = 0; sweep_line->is_vertical &= left->vertical; pos = left->link.next; do { if (pos == &sweep_line->active) { full_add_edge (sweep_line, left, +1); return; } right = link_to_edge (pos); pos = pos->next; sweep_line->is_vertical &= right->vertical; if (++winding & 1) { if (pos == &sweep_line->active || link_to_edge (pos)->x.quo != right->x.quo) { break; } } if (! right->vertical) full_inc_edge (right); } while (TRUE); full_add_edge (sweep_line, left, +1); full_add_edge (sweep_line, right, -1); } while (pos != &sweep_line->active); } static void render_rows (cairo_botor_scan_converter_t *self, sweep_line_t *sweep_line, int y, int height, cairo_span_renderer_t *renderer) { cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; cairo_half_open_span_t *spans = spans_stack; struct cell *cell; int prev_x, cover; int num_spans; cairo_status_t status; if (unlikely (sweep_line->coverage.count == 0)) { status = renderer->render_rows (renderer, y, height, NULL, 0); if (unlikely (status)) longjmp (sweep_line->unwind, status); return; } /* Allocate enough spans for the row. */ num_spans = 2*sweep_line->coverage.count+2; if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) { spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t)); if (unlikely (spans == NULL)) { longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } } /* Form the spans from the coverage and areas. */ num_spans = 0; prev_x = self->xmin; cover = 0; cell = sweep_line->coverage.head.next; do { int x = cell->x; int area; if (x > prev_x) { spans[num_spans].x = prev_x; spans[num_spans].inverse = 0; spans[num_spans].coverage = AREA_TO_ALPHA (cover); ++num_spans; } cover += cell->covered_height*STEP_X*2; area = cover - cell->uncovered_area; spans[num_spans].x = x; spans[num_spans].coverage = AREA_TO_ALPHA (area); ++num_spans; prev_x = x + 1; } while ((cell = cell->next) != &sweep_line->coverage.tail); if (prev_x <= self->xmax) { spans[num_spans].x = prev_x; spans[num_spans].inverse = 0; spans[num_spans].coverage = AREA_TO_ALPHA (cover); ++num_spans; } if (cover && prev_x < self->xmax) { spans[num_spans].x = self->xmax; spans[num_spans].inverse = 1; spans[num_spans].coverage = 0; ++num_spans; } status = renderer->render_rows (renderer, y, height, spans, num_spans); if (unlikely (spans != spans_stack)) free (spans); coverage_reset (&sweep_line->coverage); if (unlikely (status)) longjmp (sweep_line->unwind, status); } static void full_repeat (sweep_line_t *sweep) { edge_t *edge; cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { if (edge->current_sign) full_add_edge (sweep, edge, edge->current_sign); else if (! edge->vertical) full_inc_edge (edge); } } static void full_reset (sweep_line_t *sweep) { edge_t *edge; cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) edge->current_sign = 0; } static void full_step (cairo_botor_scan_converter_t *self, sweep_line_t *sweep_line, cairo_fixed_t row, cairo_span_renderer_t *renderer) { int top, bottom; top = _cairo_fixed_integer_part (sweep_line->current_row); bottom = _cairo_fixed_integer_part (row); if (cairo_list_is_empty (&sweep_line->active)) { cairo_status_t status; status = renderer->render_rows (renderer, top, bottom - top, NULL, 0); if (unlikely (status)) longjmp (sweep_line->unwind, status); return; } if (self->fill_rule == CAIRO_FILL_RULE_WINDING) full_nonzero (sweep_line); else full_evenodd (sweep_line); if (sweep_line->is_vertical || bottom == top + 1) { render_rows (self, sweep_line, top, bottom - top, renderer); full_reset (sweep_line); return; } render_rows (self, sweep_line, top++, 1, renderer); do { full_repeat (sweep_line); render_rows (self, sweep_line, top, 1, renderer); } while (++top != bottom); full_reset (sweep_line); } cairo_always_inline static void sub_inc_edge (edge_t *edge, cairo_fixed_t height) { if (height == 1) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } } else { edge->x.quo += height * edge->dxdy.quo; edge->x.rem += height * edge->dxdy.rem; if (edge->x.rem >= 0) { int carry = edge->x.rem / edge->dy + 1; edge->x.quo += carry; edge->x.rem -= carry * edge->dy; } } } static void sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign) { struct run *run; run = _cairo_freepool_alloc (&sweep_line->runs); if (unlikely (run == NULL)) longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); run->y = y; run->sign = sign; run->next = edge->runs; edge->runs = run; edge->current_sign = sign; } inline static cairo_bool_t edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y) { /* XXX is compare_x_for_y() worth executing during sub steps? */ return line_equal (&left->edge.line, &right->edge.line); //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0; } static void sub_nonzero (sweep_line_t *sweep_line) { cairo_fixed_t y = sweep_line->current_subrow; cairo_fixed_t fy = _cairo_fixed_fractional_part (y); cairo_list_t *pos; pos = sweep_line->active.next; do { edge_t *left = link_to_edge (pos), *right; int winding = left->edge.dir; pos = left->link.next; do { if (unlikely (pos == &sweep_line->active)) { if (left->current_sign != +1) sub_add_run (sweep_line, left, fy, +1); return; } right = link_to_edge (pos); pos = pos->next; winding += right->edge.dir; if (0 == winding) { if (pos == &sweep_line->active || ! edges_coincident (right, link_to_edge (pos), y)) { break; } } if (right->current_sign) sub_add_run (sweep_line, right, fy, 0); } while (TRUE); if (left->current_sign != +1) sub_add_run (sweep_line, left, fy, +1); if (right->current_sign != -1) sub_add_run (sweep_line, right, fy, -1); } while (pos != &sweep_line->active); } static void sub_evenodd (sweep_line_t *sweep_line) { cairo_fixed_t y = sweep_line->current_subrow; cairo_fixed_t fy = _cairo_fixed_fractional_part (y); cairo_list_t *pos; pos = sweep_line->active.next; do { edge_t *left = link_to_edge (pos), *right; int winding = 0; pos = left->link.next; do { if (unlikely (pos == &sweep_line->active)) { if (left->current_sign != +1) sub_add_run (sweep_line, left, fy, +1); return; } right = link_to_edge (pos); pos = pos->next; if (++winding & 1) { if (pos == &sweep_line->active || ! edges_coincident (right, link_to_edge (pos), y)) { break; } } if (right->current_sign) sub_add_run (sweep_line, right, fy, 0); } while (TRUE); if (left->current_sign != +1) sub_add_run (sweep_line, left, fy, +1); if (right->current_sign != -1) sub_add_run (sweep_line, right, fy, -1); } while (pos != &sweep_line->active); } cairo_always_inline static void sub_step (cairo_botor_scan_converter_t *self, sweep_line_t *sweep_line) { if (cairo_list_is_empty (&sweep_line->active)) return; if (self->fill_rule == CAIRO_FILL_RULE_WINDING) sub_nonzero (sweep_line); else sub_evenodd (sweep_line); } static void coverage_render_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y1, cairo_fixed_t y2) { struct run tail; struct run *run = &tail; tail.next = NULL; tail.y = y2; /* Order the runs top->bottom */ while (edge->runs) { struct run *r; r = edge->runs; edge->runs = r->next; r->next = run; run = r; } if (run->y > y1) sub_inc_edge (edge, run->y - y1); do { cairo_fixed_t x1, x2; y1 = run->y; y2 = run->next->y; x1 = edge->x.quo; if (y2 - y1 == STEP_Y) full_inc_edge (edge); else sub_inc_edge (edge, y2 - y1); x2 = edge->x.quo; if (run->sign) { int ix1, ix2; ix1 = _cairo_fixed_integer_part (x1); ix2 = _cairo_fixed_integer_part (x2); /* Edge is entirely within a column? */ if (likely (ix1 == ix2)) { struct cell *cell; int frac; frac = _cairo_fixed_fractional_part (x1) + _cairo_fixed_fractional_part (x2); cell = coverage_find (sweep, ix1); cell->covered_height += run->sign * (y2 - y1); cell->uncovered_area += run->sign * (y2 - y1) * frac; } else { coverage_render_cells (sweep, x1, x2, y1, y2, run->sign); } } run = run->next; } while (run->next != NULL); } static void coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2) { struct cell *cell; struct run *run; int height = 0; for (run = edge->runs; run != NULL; run = run->next) { if (run->sign) height += run->sign * (y2 - run->y); y2 = run->y; } cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo)); cell->covered_height += height; cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height; } cairo_always_inline static void sub_emit (cairo_botor_scan_converter_t *self, sweep_line_t *sweep, cairo_span_renderer_t *renderer) { edge_t *edge; sub_step (self, sweep); /* convert the runs into coverages */ cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { if (edge->runs == NULL) { if (! edge->vertical) { if (edge->flags & START) { sub_inc_edge (edge, STEP_Y - _cairo_fixed_fractional_part (edge->edge.top)); edge->flags &= ~START; } else full_inc_edge (edge); } } else { if (edge->vertical) { coverage_render_vertical_runs (sweep, edge, STEP_Y); } else { int y1 = 0; if (edge->flags & START) { y1 = _cairo_fixed_fractional_part (edge->edge.top); edge->flags &= ~START; } coverage_render_runs (sweep, edge, y1, STEP_Y); } } edge->current_sign = 0; edge->runs = NULL; } cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) { int y2 = _cairo_fixed_fractional_part (edge->edge.bottom); if (edge->vertical) { coverage_render_vertical_runs (sweep, edge, y2); } else { int y1 = 0; if (edge->flags & START) y1 = _cairo_fixed_fractional_part (edge->edge.top); coverage_render_runs (sweep, edge, y1, y2); } } cairo_list_init (&sweep->stopped); _cairo_freepool_reset (&sweep->runs); render_rows (self, sweep, _cairo_fixed_integer_part (sweep->current_row), 1, renderer); } static void sweep_line_init (sweep_line_t *sweep_line, event_t **start_events, int num_events) { cairo_list_init (&sweep_line->active); cairo_list_init (&sweep_line->stopped); sweep_line->insert_cursor = &sweep_line->active; sweep_line->current_row = INT32_MIN; sweep_line->current_subrow = INT32_MIN; coverage_init (&sweep_line->coverage); _cairo_freepool_init (&sweep_line->runs, sizeof (struct run)); start_event_sort (start_events, num_events); start_events[num_events] = NULL; sweep_line->queue.start_events = start_events; _cairo_freepool_init (&sweep_line->queue.pool, sizeof (queue_event_t)); pqueue_init (&sweep_line->queue.pq); sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL; } static void sweep_line_delete (sweep_line_t *sweep_line, edge_t *edge) { if (sweep_line->insert_cursor == &edge->link) sweep_line->insert_cursor = edge->link.prev; cairo_list_del (&edge->link); if (edge->runs) cairo_list_add_tail (&edge->link, &sweep_line->stopped); edge->flags |= STOP; } static void sweep_line_swap (sweep_line_t *sweep_line, edge_t *left, edge_t *right) { right->link.prev = left->link.prev; left->link.next = right->link.next; right->link.next = &left->link; left->link.prev = &right->link; left->link.next->prev = &left->link; right->link.prev->next = &right->link; } static void sweep_line_fini (sweep_line_t *sweep_line) { pqueue_fini (&sweep_line->queue.pq); _cairo_freepool_fini (&sweep_line->queue.pool); coverage_fini (&sweep_line->coverage); _cairo_freepool_fini (&sweep_line->runs); } static cairo_status_t botor_generate (cairo_botor_scan_converter_t *self, event_t **start_events, cairo_span_renderer_t *renderer) { cairo_status_t status; sweep_line_t sweep_line; cairo_fixed_t ybot; event_t *event; cairo_list_t *left, *right; edge_t *e1, *e2; int bottom; sweep_line_init (&sweep_line, start_events, self->num_edges); if ((status = setjmp (sweep_line.unwind))) goto unwind; ybot = self->extents.p2.y; sweep_line.current_subrow = self->extents.p1.y; sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y); event = *sweep_line.queue.start_events++; do { /* Can we process a full step in one go? */ if (event->y >= sweep_line.current_row + STEP_Y) { bottom = _cairo_fixed_floor (event->y); full_step (self, &sweep_line, bottom, renderer); sweep_line.current_row = bottom; sweep_line.current_subrow = bottom; } do { if (event->y > sweep_line.current_subrow) { sub_step (self, &sweep_line); sweep_line.current_subrow = event->y; } do { /* Update the active list using Bentley-Ottmann */ switch (event->type) { case EVENT_TYPE_START: e1 = ((start_event_t *) event)->edge; sweep_line_insert (&sweep_line, e1); event_insert_stop (&sweep_line, e1); left = e1->link.prev; right = e1->link.next; if (left != &sweep_line.active) { event_insert_if_intersect_below_current_y (&sweep_line, link_to_edge (left), e1); } if (right != &sweep_line.active) { event_insert_if_intersect_below_current_y (&sweep_line, e1, link_to_edge (right)); } break; case EVENT_TYPE_STOP: e1 = ((queue_event_t *) event)->e1; event_delete (&sweep_line, event); left = e1->link.prev; right = e1->link.next; sweep_line_delete (&sweep_line, e1); if (left != &sweep_line.active && right != &sweep_line.active) { event_insert_if_intersect_below_current_y (&sweep_line, link_to_edge (left), link_to_edge (right)); } break; case EVENT_TYPE_INTERSECTION: e1 = ((queue_event_t *) event)->e1; e2 = ((queue_event_t *) event)->e2; event_delete (&sweep_line, event); if (e1->flags & STOP) break; if (e2->flags & STOP) break; /* skip this intersection if its edges are not adjacent */ if (&e2->link != e1->link.next) break; left = e1->link.prev; right = e2->link.next; sweep_line_swap (&sweep_line, e1, e2); /* after the swap e2 is left of e1 */ if (left != &sweep_line.active) { event_insert_if_intersect_below_current_y (&sweep_line, link_to_edge (left), e2); } if (right != &sweep_line.active) { event_insert_if_intersect_below_current_y (&sweep_line, e1, link_to_edge (right)); } break; } event = event_next (&sweep_line); if (event == NULL) goto end; } while (event->y == sweep_line.current_subrow); } while (event->y < sweep_line.current_row + STEP_Y); bottom = sweep_line.current_row + STEP_Y; sub_emit (self, &sweep_line, renderer); sweep_line.current_subrow = bottom; sweep_line.current_row = sweep_line.current_subrow; } while (TRUE); end: /* flush any partial spans */ if (sweep_line.current_subrow != sweep_line.current_row) { sub_emit (self, &sweep_line, renderer); sweep_line.current_row += STEP_Y; sweep_line.current_subrow = sweep_line.current_row; } /* clear the rest */ if (sweep_line.current_subrow < ybot) { bottom = _cairo_fixed_integer_part (sweep_line.current_row); status = renderer->render_rows (renderer, bottom, _cairo_fixed_integer_ceil (ybot) - bottom, NULL, 0); } unwind: sweep_line_fini (&sweep_line); return status; } static cairo_status_t _cairo_botor_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_botor_scan_converter_t *self = converter; start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)]; start_event_t *events; event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; event_t **event_ptrs; struct _cairo_botor_scan_converter_chunk *chunk; cairo_status_t status; int num_events; int i, j; num_events = self->num_edges; if (unlikely (0 == num_events)) { return renderer->render_rows (renderer, _cairo_fixed_integer_floor (self->extents.p1.y), _cairo_fixed_integer_ceil (self->extents.p2.y) - _cairo_fixed_integer_floor (self->extents.p1.y), NULL, 0); } events = stack_events; event_ptrs = stack_event_ptrs; if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (start_event_t) + sizeof (event_t *), sizeof (event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (event_t **) (events + num_events); } j = 0; for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { edge_t *edge; edge = chunk->base; for (i = 0; i < chunk->count; i++) { event_ptrs[j] = (event_t *) &events[j]; events[j].y = edge->edge.top; events[j].type = EVENT_TYPE_START; events[j].edge = edge; edge++, j++; } } status = botor_generate (self, event_ptrs, renderer); if (events != stack_events) free (events); return status; } static edge_t * botor_allocate_edge (cairo_botor_scan_converter_t *self) { struct _cairo_botor_scan_converter_chunk *chunk; chunk = self->tail; if (chunk->count == chunk->size) { int size; size = chunk->size * 2; chunk->next = _cairo_malloc_ab_plus_c (size, sizeof (edge_t), sizeof (struct _cairo_botor_scan_converter_chunk)); if (unlikely (chunk->next == NULL)) return NULL; chunk = chunk->next; chunk->next = NULL; chunk->count = 0; chunk->size = size; chunk->base = chunk + 1; self->tail = chunk; } return (edge_t *) chunk->base + chunk->count++; } static cairo_status_t botor_add_edge (cairo_botor_scan_converter_t *self, const cairo_edge_t *edge) { edge_t *e; cairo_fixed_t dx, dy; e = botor_allocate_edge (self); if (unlikely (e == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_list_init (&e->link); e->edge = *edge; dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; e->dy = dy; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } else { e->vertical = FALSE; e->dxdy = floored_divrem (dx, dy); if (edge->top == edge->line.p1.y) { e->x.quo = edge->line.p1.x; e->x.rem = 0; } else { e->x = floored_muldivrem (edge->top - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) { e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy); } else { e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } } e->x.rem = -e->dy; e->current_sign = 0; e->runs = NULL; e->flags = START; self->num_edges++; return CAIRO_STATUS_SUCCESS; } static void _cairo_botor_scan_converter_destroy (void *converter) { cairo_botor_scan_converter_t *self = converter; struct _cairo_botor_scan_converter_chunk *chunk, *next; for (chunk = self->chunks.next; chunk != NULL; chunk = next) { next = chunk->next; free (chunk); } } void _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, const cairo_box_t *extents, cairo_fill_rule_t fill_rule) { self->base.destroy = _cairo_botor_scan_converter_destroy; self->base.generate = _cairo_botor_scan_converter_generate; self->extents = *extents; self->fill_rule = fill_rule; self->xmin = _cairo_fixed_integer_floor (extents->p1.x); self->xmax = _cairo_fixed_integer_ceil (extents->p2.x); self->chunks.base = self->buf; self->chunks.next = NULL; self->chunks.count = 0; self->chunks.size = sizeof (self->buf) / sizeof (edge_t); self->tail = &self->chunks; self->num_edges = 0; } ����������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-box-inline.h������������������������������0000664�0000000�0000000�00000007203�12710376503�0025612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2010 Andrea Canciani * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Andrea Canciani <ranma42@gmail.com> */ #ifndef CAIRO_BOX_H #define CAIRO_BOX_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" #include "cairo-fixed-private.h" static inline void _cairo_box_set (cairo_box_t *box, const cairo_point_t *p1, const cairo_point_t *p2) { box->p1 = *p1; box->p2 = *p2; } static inline void _cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h) { box->p1.x = _cairo_fixed_from_int (x); box->p1.y = _cairo_fixed_from_int (y); box->p2.x = _cairo_fixed_from_int (x + w); box->p2.y = _cairo_fixed_from_int (y + h); } /* assumes box->p1 is top-left, p2 bottom-right */ static inline void _cairo_box_add_point (cairo_box_t *box, const cairo_point_t *point) { if (point->x < box->p1.x) box->p1.x = point->x; else if (point->x > box->p2.x) box->p2.x = point->x; if (point->y < box->p1.y) box->p1.y = point->y; else if (point->y > box->p2.y) box->p2.y = point->y; } static inline void _cairo_box_add_box (cairo_box_t *box, const cairo_box_t *add) { if (add->p1.x < box->p1.x) box->p1.x = add->p1.x; if (add->p2.x > box->p2.x) box->p2.x = add->p2.x; if (add->p1.y < box->p1.y) box->p1.y = add->p1.y; if (add->p2.y > box->p2.y) box->p2.y = add->p2.y; } /* assumes box->p1 is top-left, p2 bottom-right */ static inline cairo_bool_t _cairo_box_contains_point (const cairo_box_t *box, const cairo_point_t *point) { return box->p1.x <= point->x && point->x <= box->p2.x && box->p1.y <= point->y && point->y <= box->p2.y; } static inline cairo_bool_t _cairo_box_is_pixel_aligned (const cairo_box_t *box) { #if CAIRO_FIXED_FRAC_BITS <= 8 && 0 return ((box->p1.x & CAIRO_FIXED_FRAC_MASK) << 24 | (box->p1.y & CAIRO_FIXED_FRAC_MASK) << 16 | (box->p2.x & CAIRO_FIXED_FRAC_MASK) << 8 | (box->p2.y & CAIRO_FIXED_FRAC_MASK) << 0) == 0; #else /* GCC on i7 prefers this variant (bizarrely according to the profiler) */ cairo_fixed_t f; f = 0; f |= box->p1.x & CAIRO_FIXED_FRAC_MASK; f |= box->p1.y & CAIRO_FIXED_FRAC_MASK; f |= box->p2.x & CAIRO_FIXED_FRAC_MASK; f |= box->p2.y & CAIRO_FIXED_FRAC_MASK; return f == 0; #endif } #endif /* CAIRO_BOX_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-boxes-intersect.c�������������������������0000664�0000000�0000000�00000040653�12710376503�0026665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2009 Chris Wilson * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-combsort-inline.h" #include "cairo-list-private.h" #include <setjmp.h> typedef struct _rectangle rectangle_t; typedef struct _edge edge_t; struct _edge { edge_t *next, *prev; edge_t *right; cairo_fixed_t x, top; int a_or_b; int dir; }; struct _rectangle { edge_t left, right; int32_t top, bottom; }; #define UNROLL3(x) x x x /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef struct _pqueue { int size, max_size; rectangle_t **elements; rectangle_t *elements_embedded[1024]; } pqueue_t; typedef struct _sweep_line { rectangle_t **rectangles; pqueue_t pq; edge_t head, tail; edge_t *insert_left, *insert_right; int32_t current_y; int32_t last_y; jmp_buf unwind; } sweep_line_t; #define DEBUG_TRAPS 0 #if DEBUG_TRAPS static void dump_traps (cairo_traps_t *traps, const char *filename) { FILE *file; int n; if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) return; file = fopen (filename, "a"); if (file != NULL) { for (n = 0; n < traps->num_traps; n++) { fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", traps->traps[n].top, traps->traps[n].bottom, traps->traps[n].left.p1.x, traps->traps[n].left.p1.y, traps->traps[n].left.p2.x, traps->traps[n].left.p2.y, traps->traps[n].right.p1.x, traps->traps[n].right.p1.y, traps->traps[n].right.p2.x, traps->traps[n].right.p2.y); } fprintf (file, "\n"); fclose (file); } } #else #define dump_traps(traps, filename) #endif static inline int rectangle_compare_start (const rectangle_t *a, const rectangle_t *b) { return a->top - b->top; } static inline int rectangle_compare_stop (const rectangle_t *a, const rectangle_t *b) { return a->bottom - b->bottom; } static inline void pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; pq->elements[PQ_FIRST_ENTRY] = NULL; } static inline void pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_bool_t pqueue_grow (pqueue_t *pq) { rectangle_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (rectangle_t *)); if (unlikely (new_elements == NULL)) return FALSE; memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (rectangle_t *)); if (unlikely (new_elements == NULL)) return FALSE; } pq->elements = new_elements; return TRUE; } static inline void pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) { rectangle_t **elements; int i, parent; if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) { if (unlikely (! pqueue_grow (&sweep->pq))) { longjmp (sweep->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } } elements = sweep->pq.elements; for (i = ++sweep->pq.size; i != PQ_FIRST_ENTRY && rectangle_compare_stop (rectangle, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = rectangle; } static inline void pqueue_pop (pqueue_t *pq) { rectangle_t **elements = pq->elements; rectangle_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && rectangle_compare_stop (elements[child+1], elements[child]) < 0) { child++; } if (rectangle_compare_stop (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline rectangle_t * rectangle_pop_start (sweep_line_t *sweep_line) { return *sweep_line->rectangles++; } static inline rectangle_t * rectangle_peek_stop (sweep_line_t *sweep_line) { return sweep_line->pq.elements[PQ_FIRST_ENTRY]; } CAIRO_COMBSORT_DECLARE (_rectangle_sort, rectangle_t *, rectangle_compare_start) static void sweep_line_init (sweep_line_t *sweep_line, rectangle_t **rectangles, int num_rectangles) { _rectangle_sort (rectangles, num_rectangles); rectangles[num_rectangles] = NULL; sweep_line->rectangles = rectangles; sweep_line->head.x = INT32_MIN; sweep_line->head.right = NULL; sweep_line->head.dir = 0; sweep_line->head.next = &sweep_line->tail; sweep_line->tail.x = INT32_MAX; sweep_line->tail.right = NULL; sweep_line->tail.dir = 0; sweep_line->tail.prev = &sweep_line->head; sweep_line->insert_left = &sweep_line->tail; sweep_line->insert_right = &sweep_line->tail; sweep_line->current_y = INT32_MIN; sweep_line->last_y = INT32_MIN; pqueue_init (&sweep_line->pq); } static void sweep_line_fini (sweep_line_t *sweep_line) { pqueue_fini (&sweep_line->pq); } static void end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out) { if (likely (left->top < bot)) { cairo_status_t status; cairo_box_t box; box.p1.x = left->x; box.p1.y = left->top; box.p2.x = left->right->x; box.p2.y = bot; status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); if (unlikely (status)) longjmp (sweep_line->unwind, status); } left->right = NULL; } /* Start a new trapezoid at the given top y coordinate, whose edges * are `edge' and `edge->next'. If `edge' already has a trapezoid, * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ static inline void start_or_continue_box (sweep_line_t *sweep_line, edge_t *left, edge_t *right, int top, cairo_boxes_t *out) { if (left->right == right) return; if (left->right != NULL) { if (right != NULL && left->right->x == right->x) { /* continuation on right, so just swap edges */ left->right = right; return; } end_box (sweep_line, left, top, out); } if (right != NULL && left->x != right->x) { left->top = top; left->right = right; } } static inline int is_zero(const int *winding) { return winding[0] == 0 || winding[1] == 0; } static inline void active_edges (sweep_line_t *sweep, cairo_boxes_t *out) { int top = sweep->current_y; int winding[2] = { 0 }; edge_t *pos; if (sweep->last_y == sweep->current_y) return; pos = sweep->head.next; if (pos == &sweep->tail) return; do { edge_t *left, *right; left = pos; do { winding[left->a_or_b] += left->dir; if (!is_zero (winding)) break; if (left->next == &sweep->tail) goto out; if (unlikely (left->right != NULL)) end_box (sweep, left, top, out); left = left->next; } while (1); right = left->next; do { if (unlikely (right->right != NULL)) end_box (sweep, right, top, out); winding[right->a_or_b] += right->dir; if (is_zero (winding)) { /* skip co-linear edges */ if (likely (right->x != right->next->x)) break; } right = right->next; } while (TRUE); start_or_continue_box (sweep, left, right, top, out); pos = right->next; } while (pos != &sweep->tail); out: sweep->last_y = sweep->current_y; } static inline void sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out) { if (edge->right != NULL) { edge_t *next = edge->next; if (next->x == edge->x) { next->top = edge->top; next->right = edge->right; } else { end_box (sweep_line, edge, sweep_line->current_y, out); } } if (sweep_line->insert_left == edge) sweep_line->insert_left = edge->next; if (sweep_line->insert_right == edge) sweep_line->insert_right = edge->next; edge->prev->next = edge->next; edge->next->prev = edge->prev; } static inline void sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle, cairo_boxes_t *out) { sweep_line_delete_edge (sweep, &rectangle->left, out); sweep_line_delete_edge (sweep, &rectangle->right, out); pqueue_pop (&sweep->pq); } static inline void insert_edge (edge_t *edge, edge_t *pos) { if (pos->x != edge->x) { if (pos->x > edge->x) { do { UNROLL3({ if (pos->prev->x <= edge->x) break; pos = pos->prev; }) } while (TRUE); } else { do { UNROLL3({ pos = pos->next; if (pos->x >= edge->x) break; }) } while (TRUE); } } pos->prev->next = edge; edge->prev = pos->prev; edge->next = pos; pos->prev = edge; } static inline void sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) { edge_t *pos; /* right edge */ pos = sweep->insert_right; insert_edge (&rectangle->right, pos); sweep->insert_right = &rectangle->right; /* left edge */ pos = sweep->insert_left; if (pos->x > sweep->insert_right->x) pos = sweep->insert_right->prev; insert_edge (&rectangle->left, pos); sweep->insert_left = &rectangle->left; pqueue_push (sweep, rectangle); } static cairo_status_t intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out) { sweep_line_t sweep_line; rectangle_t *rectangle; cairo_status_t status; sweep_line_init (&sweep_line, rectangles, num_rectangles); if ((status = setjmp (sweep_line.unwind))) goto unwind; rectangle = rectangle_pop_start (&sweep_line); do { if (rectangle->top != sweep_line.current_y) { rectangle_t *stop; stop = rectangle_peek_stop (&sweep_line); while (stop != NULL && stop->bottom < rectangle->top) { if (stop->bottom != sweep_line.current_y) { active_edges (&sweep_line, out); sweep_line.current_y = stop->bottom; } sweep_line_delete (&sweep_line, stop, out); stop = rectangle_peek_stop (&sweep_line); } active_edges (&sweep_line, out); sweep_line.current_y = rectangle->top; } sweep_line_insert (&sweep_line, rectangle); } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL); while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { if (rectangle->bottom != sweep_line.current_y) { active_edges (&sweep_line, out); sweep_line.current_y = rectangle->bottom; } sweep_line_delete (&sweep_line, rectangle, out); } unwind: sweep_line_fini (&sweep_line); return status; } static cairo_status_t _cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes, const cairo_box_t *box, cairo_boxes_t *out) { cairo_status_t status; int i, j; if (out == boxes) { /* inplace update */ struct _cairo_boxes_chunk *chunk; out->num_boxes = 0; for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) { for (i = j = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; b->p1.x = MAX (b->p1.x, box->p1.x); b->p1.y = MAX (b->p1.y, box->p1.y); b->p2.x = MIN (b->p2.x, box->p2.x); b->p2.y = MIN (b->p2.y, box->p2.y); if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) { if (i != j) chunk->base[j] = *b; j++; } } /* XXX unlink empty chains? */ chunk->count = j; out->num_boxes += j; } } else { const struct _cairo_boxes_chunk *chunk; _cairo_boxes_clear (out); _cairo_boxes_limit (out, box, 1); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &chunk->base[i]); if (unlikely (status)) return status; } } } return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_boxes_intersect (const cairo_boxes_t *a, const cairo_boxes_t *b, cairo_boxes_t *out) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *rectangles; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; rectangle_t **rectangles_ptrs; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i, j, count; if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) { _cairo_boxes_clear (out); return CAIRO_STATUS_SUCCESS; } if (a->num_boxes == 1) { cairo_box_t box = a->chunks.base[0]; return _cairo_boxes_intersect_with_box (b, &box, out); } if (b->num_boxes == 1) { cairo_box_t box = b->chunks.base[0]; return _cairo_boxes_intersect_with_box (a, &box, out); } rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; count = a->num_boxes + b->num_boxes; if (count > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (count, sizeof (rectangle_t) + sizeof (rectangle_t *), sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); rectangles_ptrs = (rectangle_t **) (rectangles + count); } j = 0; for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; rectangles[j].right.x = box[i].p2.x; rectangles[j].right.dir = -1; } else { rectangles[j].right.x = box[i].p1.x; rectangles[j].right.dir = 1; rectangles[j].left.x = box[i].p2.x; rectangles[j].left.dir = -1; } rectangles[j].left.a_or_b = 0; rectangles[j].left.right = NULL; rectangles[j].right.a_or_b = 0; rectangles[j].right.right = NULL; rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; rectangles_ptrs[j] = &rectangles[j]; j++; } } for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; rectangles[j].right.x = box[i].p2.x; rectangles[j].right.dir = -1; } else { rectangles[j].right.x = box[i].p1.x; rectangles[j].right.dir = 1; rectangles[j].left.x = box[i].p2.x; rectangles[j].left.dir = -1; } rectangles[j].left.a_or_b = 1; rectangles[j].left.right = NULL; rectangles[j].right.a_or_b = 1; rectangles[j].right.right = NULL; rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; rectangles_ptrs[j] = &rectangles[j]; j++; } } assert (j == count); _cairo_boxes_clear (out); status = intersect (rectangles_ptrs, j, out); if (rectangles != stack_rectangles) free (rectangles); return status; } �������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-boxes-private.h���������������������������0000664�0000000�0000000�00000006722�12710376503�0026343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_BOXES_H #define CAIRO_BOXES_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" #include <stdio.h> #include <stdlib.h> struct _cairo_boxes_t { cairo_status_t status; cairo_box_t limit; const cairo_box_t *limits; int num_limits; int num_boxes; unsigned int is_pixel_aligned; struct _cairo_boxes_chunk { struct _cairo_boxes_chunk *next; cairo_box_t *base; int count; int size; } chunks, *tail; cairo_box_t boxes_embedded[32]; }; cairo_private void _cairo_boxes_init (cairo_boxes_t *boxes); cairo_private void _cairo_boxes_init_with_clip (cairo_boxes_t *boxes, cairo_clip_t *clip); cairo_private void _cairo_boxes_init_for_array (cairo_boxes_t *boxes, cairo_box_t *array, int num_boxes); cairo_private void _cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes, int x, int y, int w, int h); cairo_private void _cairo_boxes_limit (cairo_boxes_t *boxes, const cairo_box_t *limits, int num_limits); cairo_private cairo_status_t _cairo_boxes_add (cairo_boxes_t *boxes, cairo_antialias_t antialias, const cairo_box_t *box); cairo_private void _cairo_boxes_extents (const cairo_boxes_t *boxes, cairo_box_t *box); cairo_private cairo_box_t * _cairo_boxes_to_array (const cairo_boxes_t *boxes, int *num_boxes, cairo_bool_t force_allocation); cairo_private cairo_status_t _cairo_boxes_intersect (const cairo_boxes_t *a, const cairo_boxes_t *b, cairo_boxes_t *out); cairo_private void _cairo_boxes_clear (cairo_boxes_t *boxes); cairo_private_no_warn cairo_bool_t _cairo_boxes_for_each_box (cairo_boxes_t *boxes, cairo_bool_t (*func) (cairo_box_t *box, void *data), void *data); cairo_private cairo_status_t _cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes); cairo_private void _cairo_boxes_fini (cairo_boxes_t *boxes); cairo_private void _cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes); #endif /* CAIRO_BOXES_H */ ����������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-boxes.c�����������������������������������0000664�0000000�0000000�00000026700�12710376503�0024664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" void _cairo_boxes_init (cairo_boxes_t *boxes) { boxes->status = CAIRO_STATUS_SUCCESS; boxes->num_limits = 0; boxes->num_boxes = 0; boxes->tail = &boxes->chunks; boxes->chunks.next = NULL; boxes->chunks.base = boxes->boxes_embedded; boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); boxes->chunks.count = 0; boxes->is_pixel_aligned = TRUE; } void _cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes, int x, int y, int w, int h) { _cairo_boxes_init (boxes); _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h); boxes->num_boxes = 1; } void _cairo_boxes_init_with_clip (cairo_boxes_t *boxes, cairo_clip_t *clip) { _cairo_boxes_init (boxes); if (clip) _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes); } void _cairo_boxes_init_for_array (cairo_boxes_t *boxes, cairo_box_t *array, int num_boxes) { int n; boxes->status = CAIRO_STATUS_SUCCESS; boxes->num_limits = 0; boxes->num_boxes = num_boxes; boxes->tail = &boxes->chunks; boxes->chunks.next = NULL; boxes->chunks.base = array; boxes->chunks.size = num_boxes; boxes->chunks.count = num_boxes; for (n = 0; n < num_boxes; n++) { if (! _cairo_fixed_is_integer (array[n].p1.x) || ! _cairo_fixed_is_integer (array[n].p1.y) || ! _cairo_fixed_is_integer (array[n].p2.x) || ! _cairo_fixed_is_integer (array[n].p2.y)) { break; } } boxes->is_pixel_aligned = n == num_boxes; } void _cairo_boxes_limit (cairo_boxes_t *boxes, const cairo_box_t *limits, int num_limits) { int n; boxes->limits = limits; boxes->num_limits = num_limits; if (boxes->num_limits) { boxes->limit = limits[0]; for (n = 1; n < num_limits; n++) { if (limits[n].p1.x < boxes->limit.p1.x) boxes->limit.p1.x = limits[n].p1.x; if (limits[n].p1.y < boxes->limit.p1.y) boxes->limit.p1.y = limits[n].p1.y; if (limits[n].p2.x > boxes->limit.p2.x) boxes->limit.p2.x = limits[n].p2.x; if (limits[n].p2.y > boxes->limit.p2.y) boxes->limit.p2.y = limits[n].p2.y; } } } static void _cairo_boxes_add_internal (cairo_boxes_t *boxes, const cairo_box_t *box) { struct _cairo_boxes_chunk *chunk; if (unlikely (boxes->status)) return; chunk = boxes->tail; if (unlikely (chunk->count == chunk->size)) { int size; size = chunk->size * 2; chunk->next = _cairo_malloc_ab_plus_c (size, sizeof (cairo_box_t), sizeof (struct _cairo_boxes_chunk)); if (unlikely (chunk->next == NULL)) { boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return; } chunk = chunk->next; boxes->tail = chunk; chunk->next = NULL; chunk->count = 0; chunk->size = size; chunk->base = (cairo_box_t *) (chunk + 1); } chunk->base[chunk->count++] = *box; boxes->num_boxes++; if (boxes->is_pixel_aligned) boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box); } cairo_status_t _cairo_boxes_add (cairo_boxes_t *boxes, cairo_antialias_t antialias, const cairo_box_t *box) { cairo_box_t b; if (antialias == CAIRO_ANTIALIAS_NONE) { b.p1.x = _cairo_fixed_round_down (box->p1.x); b.p1.y = _cairo_fixed_round_down (box->p1.y); b.p2.x = _cairo_fixed_round_down (box->p2.x); b.p2.y = _cairo_fixed_round_down (box->p2.y); box = &b; } if (box->p1.y == box->p2.y) return CAIRO_STATUS_SUCCESS; if (box->p1.x == box->p2.x) return CAIRO_STATUS_SUCCESS; if (boxes->num_limits) { cairo_point_t p1, p2; cairo_bool_t reversed = FALSE; int n; /* support counter-clockwise winding for rectangular tessellation */ if (box->p1.x < box->p2.x) { p1.x = box->p1.x; p2.x = box->p2.x; } else { p2.x = box->p1.x; p1.x = box->p2.x; reversed = ! reversed; } if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x) return CAIRO_STATUS_SUCCESS; if (box->p1.y < box->p2.y) { p1.y = box->p1.y; p2.y = box->p2.y; } else { p2.y = box->p1.y; p1.y = box->p2.y; reversed = ! reversed; } if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y) return CAIRO_STATUS_SUCCESS; for (n = 0; n < boxes->num_limits; n++) { const cairo_box_t *limits = &boxes->limits[n]; cairo_box_t _box; cairo_point_t _p1, _p2; if (p1.x >= limits->p2.x || p2.x <= limits->p1.x) continue; if (p1.y >= limits->p2.y || p2.y <= limits->p1.y) continue; /* Otherwise, clip the box to the limits. */ _p1 = p1; if (_p1.x < limits->p1.x) _p1.x = limits->p1.x; if (_p1.y < limits->p1.y) _p1.y = limits->p1.y; _p2 = p2; if (_p2.x > limits->p2.x) _p2.x = limits->p2.x; if (_p2.y > limits->p2.y) _p2.y = limits->p2.y; if (_p2.y <= _p1.y || _p2.x <= _p1.x) continue; _box.p1.y = _p1.y; _box.p2.y = _p2.y; if (reversed) { _box.p1.x = _p2.x; _box.p2.x = _p1.x; } else { _box.p1.x = _p1.x; _box.p2.x = _p2.x; } _cairo_boxes_add_internal (boxes, &_box); } } else { _cairo_boxes_add_internal (boxes, box); } return boxes->status; } void _cairo_boxes_extents (const cairo_boxes_t *boxes, cairo_box_t *box) { const struct _cairo_boxes_chunk *chunk; cairo_box_t b; int i; if (boxes->num_boxes == 0) { box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0; return; } b = boxes->chunks.base[0]; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { if (chunk->base[i].p1.x < b.p1.x) b.p1.x = chunk->base[i].p1.x; if (chunk->base[i].p1.y < b.p1.y) b.p1.y = chunk->base[i].p1.y; if (chunk->base[i].p2.x > b.p2.x) b.p2.x = chunk->base[i].p2.x; if (chunk->base[i].p2.y > b.p2.y) b.p2.y = chunk->base[i].p2.y; } } *box = b; } void _cairo_boxes_clear (cairo_boxes_t *boxes) { struct _cairo_boxes_chunk *chunk, *next; for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { next = chunk->next; free (chunk); } boxes->tail = &boxes->chunks; boxes->chunks.next = 0; boxes->chunks.count = 0; boxes->chunks.base = boxes->boxes_embedded; boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); boxes->num_boxes = 0; boxes->is_pixel_aligned = TRUE; } cairo_box_t * _cairo_boxes_to_array (const cairo_boxes_t *boxes, int *num_boxes, cairo_bool_t force_allocation) { const struct _cairo_boxes_chunk *chunk; cairo_box_t *box; int i, j; *num_boxes = boxes->num_boxes; if (boxes->chunks.next == NULL && ! force_allocation) return boxes->chunks.base; box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t)); if (box == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } j = 0; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) box[j++] = chunk->base[i]; } return box; } void _cairo_boxes_fini (cairo_boxes_t *boxes) { struct _cairo_boxes_chunk *chunk, *next; for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { next = chunk->next; free (chunk); } } cairo_bool_t _cairo_boxes_for_each_box (cairo_boxes_t *boxes, cairo_bool_t (*func) (cairo_box_t *box, void *data), void *data) { struct _cairo_boxes_chunk *chunk; int i; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) if (! func (&chunk->base[i], data)) return FALSE; } return TRUE; } struct cairo_box_renderer { cairo_span_renderer_t base; cairo_boxes_t *boxes; }; static cairo_status_t span_to_boxes (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { struct cairo_box_renderer *r = abstract_renderer; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_box_t box; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; box.p1.y = _cairo_fixed_from_int (y); box.p2.y = _cairo_fixed_from_int (y + h); do { if (spans[0].coverage) { box.p1.x = _cairo_fixed_from_int(spans[0].x); box.p2.x = _cairo_fixed_from_int(spans[1].x); status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box); } spans++; } while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS); return status; } cairo_status_t _cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes) { struct cairo_box_renderer renderer; cairo_scan_converter_t *converter; cairo_int_status_t status; cairo_rectangle_int_t r; TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule)); _cairo_box_round_to_rectangle (&polygon->extents, &r); converter = _cairo_mono_scan_converter_create (r.x, r.y, r.x + r.width, r.y + r.height, fill_rule); status = _cairo_mono_scan_converter_add_polygon (converter, polygon); if (unlikely (status)) goto cleanup_converter; renderer.boxes = boxes; renderer.base.render_rows = span_to_boxes; status = converter->generate (converter, &renderer.base); cleanup_converter: converter->destroy (converter); return status; } void _cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes) { const struct _cairo_boxes_chunk *chunk; cairo_box_t extents; int i; _cairo_boxes_extents (boxes, &extents); fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n", boxes->num_boxes, _cairo_fixed_to_double (extents.p1.x), _cairo_fixed_to_double (extents.p1.y), _cairo_fixed_to_double (extents.p2.x), _cairo_fixed_to_double (extents.p2.y)); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { fprintf (stderr, " box[%d]: (%f, %f), (%f, %f)\n", i, _cairo_fixed_to_double (chunk->base[i].p1.x), _cairo_fixed_to_double (chunk->base[i].p1.y), _cairo_fixed_to_double (chunk->base[i].p2.x), _cairo_fixed_to_double (chunk->base[i].p2.y)); } } } ����������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cache-private.h���������������������������0000664�0000000�0000000�00000011750�12710376503�0026263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc. * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Keith Packard <keithp@keithp.com> * Graydon Hoare <graydon@redhat.com> * Carl Worth <cworth@cworth.org> */ #ifndef CAIRO_CACHE_PRIVATE_H #define CAIRO_CACHE_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" /** * cairo_cache_entry_t: * * A #cairo_cache_entry_t contains both a key and a value for * #cairo_cache_t. User-derived types for #cairo_cache_entry_t must * have a #cairo_cache_entry_t as their first field. For example: * * typedef _my_entry { * cairo_cache_entry_t base; * ... Remainder of key and value fields here .. * } my_entry_t; * * which then allows a pointer to my_entry_t to be passed to any of * the #cairo_cache_t functions as follows without requiring a cast: * * _cairo_cache_insert (cache, &my_entry->base, size); * * IMPORTANT: The caller is responsible for initializing * my_entry->base.hash with a hash code derived from the key. The * essential property of the hash code is that keys_equal must never * return %TRUE for two keys that have different hashes. The best hash * code will reduce the frequency of two keys with the same code for * which keys_equal returns %FALSE. * * The user must also initialize my_entry->base.size to indicate * the size of the current entry. What units to use for size is * entirely up to the caller, (though the same units must be used for * the max_size parameter passed to _cairo_cache_create()). If all * entries are close to the same size, the simplest thing to do is to * just use units of "entries", (eg. set size==1 in all entries and * set max_size to the number of entries which you want to be saved * in the cache). * * Which parts of the entry make up the "key" and which part make up * the value are entirely up to the caller, (as determined by the * computation going into base.hash as well as the keys_equal * function). A few of the #cairo_cache_t functions accept an entry which * will be used exclusively as a "key", (indicated by a parameter name * of key). In these cases, the value-related fields of the entry need * not be initialized if so desired. **/ typedef struct _cairo_cache_entry { unsigned long hash; unsigned long size; } cairo_cache_entry_t; typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry); struct _cairo_cache { cairo_hash_table_t *hash_table; cairo_cache_predicate_func_t predicate; cairo_destroy_func_t entry_destroy; unsigned long max_size; unsigned long size; int freeze_count; }; typedef cairo_bool_t (*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); typedef void (*cairo_cache_callback_func_t) (void *entry, void *closure); cairo_private cairo_status_t _cairo_cache_init (cairo_cache_t *cache, cairo_cache_keys_equal_func_t keys_equal, cairo_cache_predicate_func_t predicate, cairo_destroy_func_t entry_destroy, unsigned long max_size); cairo_private void _cairo_cache_fini (cairo_cache_t *cache); cairo_private void _cairo_cache_freeze (cairo_cache_t *cache); cairo_private void _cairo_cache_thaw (cairo_cache_t *cache); cairo_private void * _cairo_cache_lookup (cairo_cache_t *cache, cairo_cache_entry_t *key); cairo_private cairo_status_t _cairo_cache_insert (cairo_cache_t *cache, cairo_cache_entry_t *entry); cairo_private void _cairo_cache_remove (cairo_cache_t *cache, cairo_cache_entry_t *entry); cairo_private void _cairo_cache_foreach (cairo_cache_t *cache, cairo_cache_callback_func_t cache_callback, void *closure); #endif ������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cache.c�����������������������������������0000664�0000000�0000000�00000024027�12710376503�0024607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc. * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Keith Packard <keithp@keithp.com> * Graydon Hoare <graydon@redhat.com> * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" static void _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, unsigned long additional); static cairo_bool_t _cairo_cache_entry_is_non_zero (const void *entry) { return ((const cairo_cache_entry_t *) entry)->size; } /** * _cairo_cache_init: * @cache: the #cairo_cache_t to initialise * @keys_equal: a function to return %TRUE if two keys are equal * @entry_destroy: destroy notifier for cache entries * @max_size: the maximum size for this cache * Returns: the newly created #cairo_cache_t * * Creates a new cache using the keys_equal() function to determine * the equality of entries. * * Data is provided to the cache in the form of user-derived version * of #cairo_cache_entry_t. A cache entry must be able to hold hash * code, a size, and the key/value pair being stored in the * cache. Sometimes only the key will be necessary, (as in * _cairo_cache_lookup()), and in these cases the value portion of the * entry need not be initialized. * * The units for max_size can be chosen by the caller, but should be * consistent with the units of the size field of cache entries. When * adding an entry with _cairo_cache_insert() if the total size of * entries in the cache would exceed max_size then entries will be * removed at random until the new entry would fit or the cache is * empty. Then the new entry is inserted. * * There are cases in which the automatic removal of entries is * undesired. If the cache entries have reference counts, then it is a * simple matter to use the reference counts to ensure that entries * continue to live even after being ejected from the cache. However, * in some cases the memory overhead of adding a reference count to * the entry would be objectionable. In such cases, the * _cairo_cache_freeze() and _cairo_cache_thaw() calls can be * used to establish a window during which no automatic removal of * entries will occur. **/ cairo_status_t _cairo_cache_init (cairo_cache_t *cache, cairo_cache_keys_equal_func_t keys_equal, cairo_cache_predicate_func_t predicate, cairo_destroy_func_t entry_destroy, unsigned long max_size) { cache->hash_table = _cairo_hash_table_create (keys_equal); if (unlikely (cache->hash_table == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (predicate == NULL) predicate = _cairo_cache_entry_is_non_zero; cache->predicate = predicate; cache->entry_destroy = entry_destroy; cache->max_size = max_size; cache->size = 0; cache->freeze_count = 0; return CAIRO_STATUS_SUCCESS; } static void _cairo_cache_pluck (void *entry, void *closure) { _cairo_cache_remove (closure, entry); } /** * _cairo_cache_fini: * @cache: a cache to destroy * * Immediately destroys the given cache, freeing all resources * associated with it. As part of this process, the entry_destroy() * function, (as passed to _cairo_cache_init()), will be called for * each entry in the cache. **/ void _cairo_cache_fini (cairo_cache_t *cache) { _cairo_hash_table_foreach (cache->hash_table, _cairo_cache_pluck, cache); assert (cache->size == 0); _cairo_hash_table_destroy (cache->hash_table); } /** * _cairo_cache_freeze: * @cache: a cache with some precious entries in it (or about to be * added) * * Disable the automatic ejection of entries from the cache. For as * long as the cache is "frozen", calls to _cairo_cache_insert() will * add new entries to the cache regardless of how large the cache * grows. See _cairo_cache_thaw(). * * Note: Multiple calls to _cairo_cache_freeze() will stack, in that * the cache will remain "frozen" until a corresponding number of * calls are made to _cairo_cache_thaw(). **/ void _cairo_cache_freeze (cairo_cache_t *cache) { assert (cache->freeze_count >= 0); cache->freeze_count++; } /** * _cairo_cache_thaw: * @cache: a cache, just after the entries in it have become less * precious * * Cancels the effects of _cairo_cache_freeze(). * * When a number of calls to _cairo_cache_thaw() is made corresponding * to the number of calls to _cairo_cache_freeze() the cache will no * longer be "frozen". If the cache had grown larger than max_size * while frozen, entries will immediately be ejected (by random) from * the cache until the cache is smaller than max_size. Also, the * automatic ejection of entries on _cairo_cache_insert() will resume. **/ void _cairo_cache_thaw (cairo_cache_t *cache) { assert (cache->freeze_count > 0); if (--cache->freeze_count == 0) _cairo_cache_shrink_to_accommodate (cache, 0); } /** * _cairo_cache_lookup: * @cache: a cache * @key: the key of interest * @entry_return: pointer for return value * * Performs a lookup in @cache looking for an entry which has a key * that matches @key, (as determined by the keys_equal() function * passed to _cairo_cache_init()). * * Return value: %TRUE if there is an entry in the cache that matches * @key, (which will now be in *entry_return). %FALSE otherwise, (in * which case *entry_return will be %NULL). **/ void * _cairo_cache_lookup (cairo_cache_t *cache, cairo_cache_entry_t *key) { return _cairo_hash_table_lookup (cache->hash_table, (cairo_hash_entry_t *) key); } /** * _cairo_cache_remove_random: * @cache: a cache * * Remove a random entry from the cache. * * Return value: %TRUE if an entry was successfully removed. * %FALSE if there are no entries that can be removed. **/ static cairo_bool_t _cairo_cache_remove_random (cairo_cache_t *cache) { cairo_cache_entry_t *entry; entry = _cairo_hash_table_random_entry (cache->hash_table, cache->predicate); if (unlikely (entry == NULL)) return FALSE; _cairo_cache_remove (cache, entry); return TRUE; } /** * _cairo_cache_shrink_to_accommodate: * @cache: a cache * @additional: additional size requested in bytes * * If cache is not frozen, eject entries randomly until the size of * the cache is at least @additional bytes less than * cache->max_size. That is, make enough room to accommodate a new * entry of size @additional. **/ static void _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, unsigned long additional) { while (cache->size + additional > cache->max_size) { if (! _cairo_cache_remove_random (cache)) return; } } /** * _cairo_cache_insert: * @cache: a cache * @entry: an entry to be inserted * * Insert @entry into the cache. If an entry exists in the cache with * a matching key, then the old entry will be removed first, (and the * entry_destroy() callback will be called on it). * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. **/ cairo_status_t _cairo_cache_insert (cairo_cache_t *cache, cairo_cache_entry_t *entry) { cairo_status_t status; if (entry->size && ! cache->freeze_count) _cairo_cache_shrink_to_accommodate (cache, entry->size); status = _cairo_hash_table_insert (cache->hash_table, (cairo_hash_entry_t *) entry); if (unlikely (status)) return status; cache->size += entry->size; return CAIRO_STATUS_SUCCESS; } /** * _cairo_cache_remove: * @cache: a cache * @entry: an entry that exists in the cache * * Remove an existing entry from the cache. **/ void _cairo_cache_remove (cairo_cache_t *cache, cairo_cache_entry_t *entry) { cache->size -= entry->size; _cairo_hash_table_remove (cache->hash_table, (cairo_hash_entry_t *) entry); if (cache->entry_destroy) cache->entry_destroy (entry); } /** * _cairo_cache_foreach: * @cache: a cache * @cache_callback: function to be called for each entry * @closure: additional argument to be passed to @cache_callback * * Call @cache_callback for each entry in the cache, in a * non-specified order. **/ void _cairo_cache_foreach (cairo_cache_t *cache, cairo_cache_callback_func_t cache_callback, void *closure) { _cairo_hash_table_foreach (cache->hash_table, cache_callback, closure); } unsigned long _cairo_hash_string (const char *c) { /* This is the djb2 hash. */ unsigned long hash = _CAIRO_HASH_INIT_VALUE; while (c && *c) hash = ((hash << 5) + hash) + *c++; return hash; } unsigned long _cairo_hash_bytes (unsigned long hash, const void *ptr, unsigned int length) { const uint8_t *bytes = ptr; /* This is the djb2 hash. */ while (length--) hash = ((hash << 5) + hash) + *bytes++; return hash; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cff-subset.c������������������������������0000664�0000000�0000000�00000310430�12710376503�0025601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2006 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson <ajohnson@redneon.com> * Eugeniy Meshcheryakov <eugen@debian.org> */ /* * Useful links: * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-scaled-font-subsets-private.h" #include "cairo-truetype-subset-private.h" #include <string.h> #include <locale.h> /* CFF Dict Operators. If the high byte is 0 the command is encoded * with a single byte. */ #define BASEFONTNAME_OP 0x0c16 #define CIDCOUNT_OP 0x0c22 #define CHARSET_OP 0x000f #define CHARSTRINGS_OP 0x0011 #define COPYRIGHT_OP 0x0c00 #define DEFAULTWIDTH_OP 0x0014 #define ENCODING_OP 0x0010 #define FAMILYNAME_OP 0x0003 #define FDARRAY_OP 0x0c24 #define FDSELECT_OP 0x0c25 #define FONTBBOX_OP 0x0005 #define FONTMATRIX_OP 0x0c07 #define FONTNAME_OP 0x0c26 #define FULLNAME_OP 0x0002 #define LOCAL_SUB_OP 0x0013 #define NOMINALWIDTH_OP 0x0015 #define NOTICE_OP 0x0001 #define POSTSCRIPT_OP 0x0c15 #define PRIVATE_OP 0x0012 #define ROS_OP 0x0c1e #define UNIQUEID_OP 0x000d #define VERSION_OP 0x0000 #define WEIGHT_OP 0x0004 #define XUID_OP 0x000e #define NUM_STD_STRINGS 391 /* Type 2 Charstring operators */ #define TYPE2_hstem 0x0001 #define TYPE2_vstem 0x0003 #define TYPE2_callsubr 0x000a #define TYPE2_return 0x000b #define TYPE2_endchar 0x000e #define TYPE2_hstemhm 0x0012 #define TYPE2_hintmask 0x0013 #define TYPE2_cntrmask 0x0014 #define TYPE2_vstemhm 0x0017 #define TYPE2_callgsubr 0x001d #define TYPE2_rmoveto 0x0015 #define TYPE2_hmoveto 0x0016 #define TYPE2_vmoveto 0x0004 #define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */ typedef struct _cff_header { uint8_t major; uint8_t minor; uint8_t header_size; uint8_t offset_size; } cff_header_t; typedef struct _cff_index_element { cairo_bool_t is_copy; unsigned char *data; int length; } cff_index_element_t; typedef struct _cff_dict_operator { cairo_hash_entry_t base; unsigned short operator; unsigned char *operand; int operand_length; int operand_offset; } cff_dict_operator_t; typedef struct _cairo_cff_font { cairo_scaled_font_subset_t *scaled_font_subset; const cairo_scaled_font_backend_t *backend; /* Font Data */ unsigned char *data; unsigned long data_length; unsigned char *current_ptr; unsigned char *data_end; cff_header_t *header; char *font_name; char *ps_name; cairo_hash_table_t *top_dict; cairo_hash_table_t *private_dict; cairo_array_t strings_index; cairo_array_t charstrings_index; cairo_array_t global_sub_index; cairo_array_t local_sub_index; unsigned char *charset; int num_glyphs; cairo_bool_t is_cid; cairo_bool_t is_opentype; int units_per_em; int global_sub_bias; int local_sub_bias; double default_width; double nominal_width; /* CID Font Data */ int *fdselect; unsigned int num_fontdicts; cairo_hash_table_t **fd_dict; cairo_hash_table_t **fd_private_dict; cairo_array_t *fd_local_sub_index; int *fd_local_sub_bias; double *fd_default_width; double *fd_nominal_width; /* Subsetted Font Data */ char *subset_font_name; cairo_array_t charstrings_subset_index; cairo_array_t strings_subset_index; int euro_sid; int *fdselect_subset; unsigned int num_subset_fontdicts; int *fd_subset_map; int *private_dict_offset; cairo_bool_t subset_subroutines; cairo_bool_t *global_subs_used; cairo_bool_t *local_subs_used; cairo_bool_t **fd_local_subs_used; cairo_array_t output; /* Subset Metrics */ int *widths; int x_min, y_min, x_max, y_max; int ascent, descent; /* Type 2 charstring data */ int type2_stack_size; int type2_stack_top_value; cairo_bool_t type2_stack_top_is_int; int type2_num_hints; int type2_hintmask_bytes; int type2_nesting_level; cairo_bool_t type2_seen_first_int; cairo_bool_t type2_find_width; cairo_bool_t type2_found_width; int type2_width; cairo_bool_t type2_has_path; } cairo_cff_font_t; /* Encoded integer using maximum sized encoding. This is required for * operands that are later modified after encoding. */ static unsigned char * encode_integer_max (unsigned char *p, int i) { *p++ = 29; *p++ = i >> 24; *p++ = (i >> 16) & 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; return p; } static unsigned char * encode_integer (unsigned char *p, int i) { if (i >= -107 && i <= 107) { *p++ = i + 139; } else if (i >= 108 && i <= 1131) { i -= 108; *p++ = (i >> 8)+ 247; *p++ = i & 0xff; } else if (i >= -1131 && i <= -108) { i = -i - 108; *p++ = (i >> 8)+ 251; *p++ = i & 0xff; } else if (i >= -32768 && i <= 32767) { *p++ = 28; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; } else { p = encode_integer_max (p, i); } return p; } static unsigned char * decode_integer (unsigned char *p, int *integer) { if (*p == 28) { *integer = (int)(p[1]<<8 | p[2]); p += 3; } else if (*p == 29) { *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); p += 5; } else if (*p >= 32 && *p <= 246) { *integer = *p++ - 139; } else if (*p <= 250) { *integer = (p[0] - 247) * 256 + p[1] + 108; p += 2; } else if (*p <= 254) { *integer = -(p[0] - 251) * 256 - p[1] - 108; p += 2; } else { *integer = 0; p += 1; } return p; } static char * decode_nibble (int n, char *buf) { switch (n) { case 0xa: *buf++ = '.'; break; case 0xb: *buf++ = 'E'; break; case 0xc: *buf++ = 'E'; *buf++ = '-'; break; case 0xd: *buf++ = '-'; break; case 0xe: *buf++ = '-'; break; case 0xf: break; default: *buf++ = '0' + n; break; } return buf; } static unsigned char * decode_real (unsigned char *p, double *real) { struct lconv *locale_data; const char *decimal_point; int decimal_point_len; int n; char buffer[100]; char buffer2[200]; char *q; char *buf = buffer; char *buf_end = buffer + sizeof (buffer); locale_data = localeconv (); decimal_point = locale_data->decimal_point; decimal_point_len = strlen (decimal_point); assert (decimal_point_len != 0); assert (sizeof(buffer) + decimal_point_len < sizeof(buffer2)); p++; while (buf + 2 < buf_end) { n = *p >> 4; buf = decode_nibble (n, buf); n = *p & 0x0f; buf = decode_nibble (n, buf); if ((*p & 0x0f) == 0x0f) { p++; break; } p++; }; *buf = 0; buf = buffer; if (strchr (buffer, '.')) { q = strchr (buffer, '.'); strncpy (buffer2, buffer, q - buffer); buf = buffer2 + (q - buffer); strncpy (buf, decimal_point, decimal_point_len); buf += decimal_point_len; strcpy (buf, q + 1); buf = buffer2; } if (sscanf(buf, "%lf", real) != 1) *real = 0.0; return p; } static unsigned char * decode_number (unsigned char *p, double *number) { if (*p == 30) { p = decode_real (p, number); } else { int i; p = decode_integer (p, &i); *number = i; } return p; } static unsigned char * decode_operator (unsigned char *p, unsigned short *operator) { unsigned short op = 0; op = *p++; if (op == 12) { op <<= 8; op |= *p++; } *operator = op; return p; } /* return 0 if not an operand */ static int operand_length (unsigned char *p) { unsigned char *begin = p; if (*p == 28) return 3; if (*p == 29) return 5; if (*p >= 32 && *p <= 246) return 1; if (*p >= 247 && *p <= 254) return 2; if (*p == 30) { while ((*p & 0x0f) != 0x0f) p++; return p - begin + 1; } return 0; } static unsigned char * encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) { while (--offset_size >= 0) { p[offset_size] = (unsigned char) (offset & 0xff); offset >>= 8; } return p + offset_size; } static unsigned long decode_index_offset(unsigned char *p, int off_size) { unsigned long offset = 0; while (off_size-- > 0) offset = offset*256 + *p++; return offset; } static void cff_index_init (cairo_array_t *index) { _cairo_array_init (index, sizeof (cff_index_element_t)); } static cairo_int_status_t cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr) { cff_index_element_t element; unsigned char *data, *p; cairo_status_t status; int offset_size, count, start, i; int end = 0; p = *ptr; if (p + 2 > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; count = be16_to_cpu( *((uint16_t *)p) ); p += 2; if (count > 0) { offset_size = *p++; if (p + (count + 1)*offset_size > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; data = p + offset_size*(count + 1) - 1; start = decode_index_offset (p, offset_size); p += offset_size; for (i = 0; i < count; i++) { end = decode_index_offset (p, offset_size); p += offset_size; if (p > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; element.length = end - start; element.is_copy = FALSE; element.data = data + start; status = _cairo_array_append (index, &element); if (unlikely (status)) return status; start = end; } p = data + end; } *ptr = p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cff_index_write (cairo_array_t *index, cairo_array_t *output) { int offset_size; int offset; int num_elem; int i; cff_index_element_t *element; uint16_t count; unsigned char buf[5]; cairo_status_t status; num_elem = _cairo_array_num_elements (index); count = cpu_to_be16 ((uint16_t) num_elem); status = _cairo_array_append_multiple (output, &count, 2); if (unlikely (status)) return status; if (num_elem == 0) return CAIRO_STATUS_SUCCESS; /* Find maximum offset to determine offset size */ offset = 1; for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); offset += element->length; } if (offset < 0x100) offset_size = 1; else if (offset < 0x10000) offset_size = 2; else if (offset < 0x1000000) offset_size = 3; else offset_size = 4; buf[0] = (unsigned char) offset_size; status = _cairo_array_append (output, buf); if (unlikely (status)) return status; offset = 1; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); if (unlikely (status)) return status; for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); offset += element->length; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); if (unlikely (status)) return status; } for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); if (element->length > 0) { status = _cairo_array_append_multiple (output, element->data, element->length); } if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static void cff_index_set_object (cairo_array_t *index, int obj_index, unsigned char *object , int length) { cff_index_element_t *element; element = _cairo_array_index (index, obj_index); if (element->is_copy) free (element->data); element->data = object; element->length = length; element->is_copy = FALSE; } static cairo_status_t cff_index_append (cairo_array_t *index, unsigned char *object , int length) { cff_index_element_t element; element.length = length; element.is_copy = FALSE; element.data = object; return _cairo_array_append (index, &element); } static cairo_status_t cff_index_append_copy (cairo_array_t *index, const unsigned char *object, unsigned int length) { cff_index_element_t element; cairo_status_t status; element.length = length; element.is_copy = TRUE; element.data = malloc (element.length); if (unlikely (element.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (element.data, object, element.length); status = _cairo_array_append (index, &element); if (unlikely (status)) { free (element.data); return status; } return CAIRO_STATUS_SUCCESS; } static void cff_index_fini (cairo_array_t *index) { cff_index_element_t *element; unsigned int i; for (i = 0; i < _cairo_array_num_elements (index); i++) { element = _cairo_array_index (index, i); if (element->is_copy && element->data) free (element->data); } _cairo_array_fini (index); } static cairo_bool_t _cairo_cff_dict_equal (const void *key_a, const void *key_b) { const cff_dict_operator_t *op_a = key_a; const cff_dict_operator_t *op_b = key_b; return op_a->operator == op_b->operator; } static cairo_status_t cff_dict_init (cairo_hash_table_t **dict) { *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); if (unlikely (*dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static void _cairo_dict_init_key (cff_dict_operator_t *key, int operator) { key->base.hash = (unsigned long) operator; key->operator = operator; } static cairo_status_t cff_dict_create_operator (int operator, unsigned char *operand, int size, cff_dict_operator_t **out) { cff_dict_operator_t *op; op = malloc (sizeof (cff_dict_operator_t)); if (unlikely (op == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_dict_init_key (op, operator); op->operand = malloc (size); if (unlikely (op->operand == NULL)) { free (op); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (op->operand, operand, size); op->operand_length = size; op->operand_offset = -1; *out = op; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) { unsigned char *end; cairo_array_t operands; cff_dict_operator_t *op; unsigned short operator; cairo_status_t status = CAIRO_STATUS_SUCCESS; int size; end = p + dict_size; _cairo_array_init (&operands, 1); while (p < end) { size = operand_length (p); if (size != 0) { status = _cairo_array_append_multiple (&operands, p, size); if (unlikely (status)) goto fail; p += size; } else { p = decode_operator (p, &operator); status = cff_dict_create_operator (operator, _cairo_array_index (&operands, 0), _cairo_array_num_elements (&operands), &op); if (unlikely (status)) goto fail; status = _cairo_hash_table_insert (dict, &op->base); if (unlikely (status)) goto fail; _cairo_array_truncate (&operands, 0); } } fail: _cairo_array_fini (&operands); return status; } static void cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { free (op->operand); _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); free (op); } } static unsigned char * cff_dict_get_operands (cairo_hash_table_t *dict, unsigned short operator, int *size) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { *size = op->operand_length; return op->operand; } return NULL; } static cairo_status_t cff_dict_set_operands (cairo_hash_table_t *dict, unsigned short operator, unsigned char *operand, int size) { cff_dict_operator_t key, *op; cairo_status_t status; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { free (op->operand); op->operand = malloc (size); if (unlikely (op->operand == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (op->operand, operand, size); op->operand_length = size; } else { status = cff_dict_create_operator (operator, operand, size, &op); if (unlikely (status)) return status; status = _cairo_hash_table_insert (dict, &op->base); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static int cff_dict_get_location (cairo_hash_table_t *dict, unsigned short operator, int *size) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { *size = op->operand_length; return op->operand_offset; } return -1; } typedef struct _dict_write_info { cairo_array_t *output; cairo_status_t status; } dict_write_info_t; static void cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info) { unsigned char data; op->operand_offset = _cairo_array_num_elements (write_info->output); write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length); if (write_info->status) return; if (op->operator & 0xff00) { data = op->operator >> 8; write_info->status = _cairo_array_append (write_info->output, &data); if (write_info->status) return; } data = op->operator & 0xff; write_info->status = _cairo_array_append (write_info->output, &data); } static void _cairo_dict_collect (void *entry, void *closure) { dict_write_info_t *write_info = closure; cff_dict_operator_t *op = entry; if (write_info->status) return; /* The ROS operator is handled separately in cff_dict_write() */ if (op->operator != ROS_OP) cairo_dict_write_operator (op, write_info); } static cairo_status_t cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) { dict_write_info_t write_info; cff_dict_operator_t key, *op; write_info.output = output; write_info.status = CAIRO_STATUS_SUCCESS; /* The CFF specification requires that the Top Dict of CID fonts * begin with the ROS operator. */ _cairo_dict_init_key (&key, ROS_OP); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) cairo_dict_write_operator (op, &write_info); _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); return write_info.status; } static void _cff_dict_entry_pluck (void *_entry, void *dict) { cff_dict_operator_t *entry = _entry; _cairo_hash_table_remove (dict, &entry->base); free (entry->operand); free (entry); } static void cff_dict_fini (cairo_hash_table_t *dict) { _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); _cairo_hash_table_destroy (dict); } static cairo_int_status_t cairo_cff_font_read_header (cairo_cff_font_t *font) { if (font->data_length < sizeof (cff_header_t)) return CAIRO_INT_STATUS_UNSUPPORTED; font->header = (cff_header_t *) font->data; font->current_ptr = font->data + font->header->header_size; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_name (cairo_cff_font_t *font) { cairo_array_t index; cairo_int_status_t status; cff_index_element_t *element; unsigned char *p; int i, len; cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); if (!font->is_opentype) { element = _cairo_array_index (&index, 0); p = element->data; len = element->length; /* If font name is prefixed with a subset tag, strip it off. */ if (len > 7 && p[6] == '+') { for (i = 0; i < 6; i++) if (p[i] < 'A' || p[i] > 'Z') break; if (i == 6) { p += 7; len -= 7; } } font->ps_name = malloc (len + 1); if (unlikely (font->ps_name == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (font->ps_name, p, len); font->ps_name[len] = 0; } cff_index_fini (&index); return status; } static cairo_int_status_t cairo_cff_font_read_private_dict (cairo_cff_font_t *font, cairo_hash_table_t *private_dict, cairo_array_t *local_sub_index, int *local_sub_bias, cairo_bool_t **local_subs_used, double *default_width, double *nominal_width, unsigned char *ptr, int size) { cairo_int_status_t status; unsigned char buf[10]; unsigned char *end_buf; int offset; int i; unsigned char *operand; unsigned char *p; int num_subs; status = cff_dict_read (private_dict, ptr, size); if (unlikely (status)) return status; operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); if (operand) { decode_integer (operand, &offset); p = ptr + offset; status = cff_index_read (local_sub_index, &p, font->data_end); if (unlikely (status)) return status; /* Use maximum sized encoding to reserve space for later modification. */ end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); if (unlikely (status)) return status; } *default_width = 0; operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i); if (operand) decode_number (operand, default_width); *nominal_width = 0; operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i); if (operand) decode_number (operand, nominal_width); num_subs = _cairo_array_num_elements (local_sub_index); *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t)); if (unlikely (*local_subs_used == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_subs < 1240) *local_sub_bias = 107; else if (num_subs < 33900) *local_sub_bias = 1131; else *local_sub_bias = 32768; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) { int type, num_ranges, first, last, fd, i, j; font->fdselect = calloc (font->num_glyphs, sizeof (int)); if (unlikely (font->fdselect == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); type = *p++; if (type == 0) { for (i = 0; i < font->num_glyphs; i++) font->fdselect[i] = *p++; } else if (type == 3) { num_ranges = be16_to_cpu( *((uint16_t *)p) ); p += 2; for (i = 0; i < num_ranges; i++) { first = be16_to_cpu( *((uint16_t *)p) ); p += 2; fd = *p++; last = be16_to_cpu( *((uint16_t *)p) ); for (j = first; j < last; j++) font->fdselect[j] = fd; } } else { return CAIRO_INT_STATUS_UNSUPPORTED; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) { cairo_array_t index; cff_index_element_t *element; unsigned int i; int size; unsigned char *operand; int offset; cairo_int_status_t status; unsigned char buf[100]; unsigned char *end_buf; cff_index_init (&index); status = cff_index_read (&index, &ptr, font->data_end); if (unlikely (status)) goto fail; font->num_fontdicts = _cairo_array_num_elements (&index); font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); if (unlikely (font->fd_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); if (unlikely (font->fd_private_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); if (unlikely (font->fd_local_sub_index == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts); if (unlikely (font->fd_local_sub_bias == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts); if (unlikely (font->fd_local_subs_used == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_default_width = calloc (font->num_fontdicts, sizeof (double)); if (unlikely (font->fd_default_width == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_nominal_width = calloc (font->num_fontdicts, sizeof (double)); if (unlikely (font->fd_nominal_width == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } for (i = 0; i < font->num_fontdicts; i++) { status = cff_dict_init (&font->fd_dict[i]); if (unlikely (status)) goto fail; element = _cairo_array_index (&index, i); status = cff_dict_read (font->fd_dict[i], element->data, element->length); if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); if (operand == NULL) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto fail; } operand = decode_integer (operand, &size); decode_integer (operand, &offset); status = cff_dict_init (&font->fd_private_dict[i]); if (unlikely (status)) goto fail; cff_index_init (&font->fd_local_sub_index[i]); status = cairo_cff_font_read_private_dict (font, font->fd_private_dict[i], &font->fd_local_sub_index[i], &font->fd_local_sub_bias[i], &font->fd_local_subs_used[i], &font->fd_default_width[i], &font->fd_nominal_width[i], font->data + offset, size); if (unlikely (status)) goto fail; /* Set integer operand to max value to use max size encoding to reserve * space for any value later */ end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; } return CAIRO_STATUS_SUCCESS; fail: cff_index_fini (&index); return status; } static void cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t *top_dict) { unsigned char *p; unsigned char *end; int size; double x_min, y_min, x_max, y_max; double xx, yx, xy, yy; x_min = 0.0; y_min = 0.0; x_max = 0.0; y_max = 0.0; p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size); if (p) { end = p + size; if (p < end) p = decode_number (p, &x_min); if (p < end) p = decode_number (p, &y_min); if (p < end) p = decode_number (p, &x_max); if (p < end) p = decode_number (p, &y_max); } font->x_min = floor (x_min); font->y_min = floor (y_min); font->x_max = floor (x_max); font->y_max = floor (y_max); font->ascent = font->y_max; font->descent = font->y_min; xx = 0.001; yx = 0.0; xy = 0.0; yy = 0.001; p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size); if (p) { end = p + size; if (p < end) p = decode_number (p, &xx); if (p < end) p = decode_number (p, &yx); if (p < end) p = decode_number (p, &xy); if (p < end) p = decode_number (p, &yy); } /* Freetype uses 1/yy to get units per EM */ font->units_per_em = _cairo_round(1.0/yy); } static cairo_int_status_t cairo_cff_font_read_top_dict (cairo_cff_font_t *font) { cairo_array_t index; cff_index_element_t *element; unsigned char buf[20]; unsigned char *end_buf; unsigned char *operand; cairo_int_status_t status; unsigned char *p; int size; int offset; cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); if (unlikely (status)) goto fail; element = _cairo_array_index (&index, 0); status = cff_dict_read (font->top_dict, element->data, element->length); if (unlikely (status)) goto fail; if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) font->is_cid = TRUE; else font->is_cid = FALSE; operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size); decode_integer (operand, &offset); p = font->data + offset; status = cff_index_read (&font->charstrings_index, &p, font->data_end); if (unlikely (status)) goto fail; font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); if (font->is_cid) { operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size); if (!operand) return CAIRO_INT_STATUS_UNSUPPORTED; decode_integer (operand, &offset); font->charset = font->data + offset; if (font->charset >= font->data_end) return CAIRO_INT_STATUS_UNSUPPORTED; } if (!font->is_opentype) cairo_cff_font_read_font_metrics (font, font->top_dict); if (font->is_cid) { operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_fdselect (font, font->data + offset); if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); if (unlikely (status)) goto fail; } else { operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); operand = decode_integer (operand, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_private_dict (font, font->private_dict, &font->local_sub_index, &font->local_sub_bias, &font->local_subs_used, &font->default_width, &font->nominal_width, font->data + offset, size); if (unlikely (status)) goto fail; } /* Use maximum sized encoding to reserve space for later modification. */ end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; if (font->scaled_font_subset->is_latin) { status = cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; /* Private has two operands - size and offset */ end_buf = encode_integer_max (end_buf, 0); cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; } else { status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; cff_dict_remove (font->top_dict, ENCODING_OP); cff_dict_remove (font->top_dict, PRIVATE_OP); } /* Remove the unique identifier operators as the subsetted font is * not the same is the original font. */ cff_dict_remove (font->top_dict, UNIQUEID_OP); cff_dict_remove (font->top_dict, XUID_OP); fail: cff_index_fini (&index); return status; } static cairo_int_status_t cairo_cff_font_read_strings (cairo_cff_font_t *font) { return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end); } static cairo_int_status_t cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) { cairo_int_status_t status; int num_subs; status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); if (unlikely (status)) return status; num_subs = _cairo_array_num_elements (&font->global_sub_index); font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t)); if (unlikely (font->global_subs_used == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_subs < 1240) font->global_sub_bias = 107; else if (num_subs < 33900) font->global_sub_bias = 1131; else font->global_sub_bias = 32768; return CAIRO_STATUS_SUCCESS; } typedef cairo_int_status_t (*font_read_t) (cairo_cff_font_t *font); static const font_read_t font_read_funcs[] = { cairo_cff_font_read_header, cairo_cff_font_read_name, cairo_cff_font_read_top_dict, cairo_cff_font_read_strings, cairo_cff_font_read_global_subroutines, }; static cairo_int_status_t cairo_cff_font_read_font (cairo_cff_font_t *font) { cairo_int_status_t status; unsigned int i; for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { status = font_read_funcs[i] (font); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) { cairo_status_t status; unsigned char buf[30]; unsigned char *p; int sid1, sid2; const char *registry = "Adobe"; const char *ordering = "Identity"; sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)registry, strlen(registry)); if (unlikely (status)) return status; sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)ordering, strlen(ordering)); if (unlikely (status)) return status; p = encode_integer (buf, sid1); p = encode_integer (p, sid2); p = encode_integer (p, 0); status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); if (unlikely (status)) return status; p = encode_integer (buf, font->scaled_font_subset->num_glyphs); status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, cairo_hash_table_t *dict, int operator) { int size; unsigned char *p; int sid; unsigned char buf[100]; cff_index_element_t *element; cairo_status_t status; p = cff_dict_get_operands (dict, operator, &size); if (!p) return CAIRO_STATUS_SUCCESS; decode_integer (p, &sid); if (sid < NUM_STD_STRINGS) return CAIRO_STATUS_SUCCESS; element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append (&font->strings_subset_index, element->data, element->length); if (unlikely (status)) return status; p = encode_integer (buf, sid); status = cff_dict_set_operands (dict, operator, buf, p - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static const int dict_strings[] = { VERSION_OP, NOTICE_OP, COPYRIGHT_OP, FULLNAME_OP, FAMILYNAME_OP, WEIGHT_OP, POSTSCRIPT_OP, BASEFONTNAME_OP, FONTNAME_OP, }; static cairo_status_t cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, cairo_hash_table_t *dict) { cairo_status_t status; unsigned int i; for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static unsigned char * type2_decode_integer (unsigned char *p, int *integer) { if (*p == 28) { *integer = p[1] << 8 | p[2]; p += 3; } else if (*p <= 246) { *integer = *p++ - 139; } else if (*p <= 250) { *integer = (p[0] - 247) * 256 + p[1] + 108; p += 2; } else if (*p <= 254) { *integer = -(p[0] - 251) * 256 - p[1] - 108; p += 2; } else { /* *p == 255 */ /* 16.16 fixed-point number. The fraction is ignored. */ *integer = (int16_t)((p[1] << 8) | p[2]); p += 5; } return p; } /* Type 2 charstring parser for finding calls to local or global * subroutines. For non Opentype CFF fonts it also gets the glyph * widths. * * When we find a subroutine operator, the subroutine is marked as in * use and recursively followed. The subroutine number is the value on * the top of the stack when the subroutine operator is executed. In * most fonts the subroutine number is encoded in an integer * immediately preceding the subroutine operator. However it is * possible for the subroutine number on the stack to be the result of * a computation (in which case there will be an operator preceding * the subroutine operator). If this occurs, subroutine subsetting is * disabled since we can't easily determine which subroutines are * used. * * The width, if present, is the first integer in the charstring. The * only way to confirm if the integer at the start of the charstring is * the width is when the first stack clearing operator is parsed, * check if there is an extra integer left over on the stack. * * When the first stack clearing operator is encountered * type2_find_width is set to FALSE and type2_found_width is set to * TRUE if an extra argument is found, otherwise FALSE. */ static cairo_status_t cairo_cff_parse_charstring (cairo_cff_font_t *font, unsigned char *charstring, int length, int glyph_id, cairo_bool_t need_width) { unsigned char *p = charstring; unsigned char *end = charstring + length; int integer; int hint_bytes; int sub_num; cff_index_element_t *element; int fd; while (p < end) { if (*p == 28 || *p >= 32) { /* Integer value */ p = type2_decode_integer (p, &integer); font->type2_stack_size++; font->type2_stack_top_value = integer; font->type2_stack_top_is_int = TRUE; if (!font->type2_seen_first_int) { font->type2_width = integer; font->type2_seen_first_int = TRUE; } } else if (*p == TYPE2_hstem || *p == TYPE2_vstem || *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) { /* Hint operator. The number of hints declared by the * operator depends on the size of the stack. */ font->type2_stack_top_is_int = FALSE; font->type2_num_hints += font->type2_stack_size/2; if (font->type2_find_width && font->type2_stack_size % 2) font->type2_found_width = TRUE; font->type2_stack_size = 0; font->type2_find_width = FALSE; p++; } else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) { /* Hintmask operator. These operators are followed by a * variable length mask where the length depends on the * number of hints declared. The first time this is called * it is also an implicit vstem if there are arguments on * the stack. */ if (font->type2_hintmask_bytes == 0) { font->type2_stack_top_is_int = FALSE; font->type2_num_hints += font->type2_stack_size/2; if (font->type2_find_width && font->type2_stack_size % 2) font->type2_found_width = TRUE; font->type2_stack_size = 0; font->type2_find_width = FALSE; font->type2_hintmask_bytes = (font->type2_num_hints+7)/8; } hint_bytes = font->type2_hintmask_bytes; p++; p += hint_bytes; } else if (*p == TYPE2_rmoveto) { if (font->type2_find_width && font->type2_stack_size > 2) font->type2_found_width = TRUE; font->type2_stack_size = 0; font->type2_find_width = FALSE; font->type2_has_path = TRUE; p++; } else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) { if (font->type2_find_width && font->type2_stack_size > 1) font->type2_found_width = TRUE; font->type2_stack_size = 0; font->type2_find_width = FALSE; font->type2_has_path = TRUE; p++; } else if (*p == TYPE2_endchar) { if (!font->type2_has_path && font->type2_stack_size > 3) return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */ if (font->type2_find_width && font->type2_stack_size > 0) font->type2_found_width = TRUE; return CAIRO_STATUS_SUCCESS; } else if (*p == TYPE2_callsubr) { /* call to local subroutine */ if (! font->type2_stack_top_is_int) return CAIRO_INT_STATUS_UNSUPPORTED; if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) return CAIRO_INT_STATUS_UNSUPPORTED; p++; font->type2_stack_top_is_int = FALSE; font->type2_stack_size--; if (font->type2_find_width && font->type2_stack_size == 0) font->type2_seen_first_int = FALSE; if (font->is_cid) { fd = font->fdselect[glyph_id]; sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd]; element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num); if (! font->fd_local_subs_used[fd][sub_num]) { font->fd_local_subs_used[fd][sub_num] = TRUE; cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } } else { sub_num = font->type2_stack_top_value + font->local_sub_bias; element = _cairo_array_index (&font->local_sub_index, sub_num); if (! font->local_subs_used[sub_num] || (need_width && !font->type2_found_width)) { font->local_subs_used[sub_num] = TRUE; cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } } font->type2_nesting_level--; } else if (*p == TYPE2_callgsubr) { /* call to global subroutine */ if (! font->type2_stack_top_is_int) return CAIRO_INT_STATUS_UNSUPPORTED; if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) return CAIRO_INT_STATUS_UNSUPPORTED; p++; font->type2_stack_size--; font->type2_stack_top_is_int = FALSE; if (font->type2_find_width && font->type2_stack_size == 0) font->type2_seen_first_int = FALSE; sub_num = font->type2_stack_top_value + font->global_sub_bias; element = _cairo_array_index (&font->global_sub_index, sub_num); if (! font->global_subs_used[sub_num] || (need_width && !font->type2_found_width)) { font->global_subs_used[sub_num] = TRUE; cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } font->type2_nesting_level--; } else if (*p == 12) { /* 2 byte instruction */ /* All the 2 byte operators are either not valid before a * stack clearing operator or they are one of the * arithmetic, storage, or conditional operators. */ if (need_width && font->type2_find_width) return CAIRO_INT_STATUS_UNSUPPORTED; p += 2; font->type2_stack_top_is_int = FALSE; } else { /* 1 byte instruction */ p++; font->type2_stack_top_is_int = FALSE; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t *font, unsigned char *charstring, int length, int glyph_id, int subset_id) { cairo_status_t status; int width; int fd; font->type2_stack_size = 0; font->type2_stack_top_value = 0;; font->type2_stack_top_is_int = FALSE; font->type2_num_hints = 0; font->type2_hintmask_bytes = 0; font->type2_nesting_level = 0; font->type2_seen_first_int = FALSE; font->type2_find_width = TRUE; font->type2_found_width = FALSE; font->type2_width = 0; font->type2_has_path = FALSE; status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE); if (status) return status; if (!font->is_opentype) { if (font->is_cid) { fd = font->fdselect[glyph_id]; if (font->type2_found_width) width = font->fd_nominal_width[fd] + font->type2_width; else width = font->fd_default_width[fd]; } else { if (font->type2_found_width) width = font->nominal_width + font->type2_width; else width = font->default_width; } font->widths[subset_id] = width; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_get_gid_for_cid (cairo_cff_font_t *font, unsigned long cid, unsigned long *gid) { unsigned char *p; unsigned long first_gid; unsigned long first_cid; int num_left; unsigned long c, g; if (cid == 0) { *gid = 0; return CAIRO_STATUS_SUCCESS; } switch (font->charset[0]) { /* Format 0 */ case 0: p = font->charset + 1; g = 1; while (g <= (unsigned)font->num_glyphs && p < font->data_end) { c = be16_to_cpu( *((uint16_t *)p) ); if (c == cid) { *gid = g; return CAIRO_STATUS_SUCCESS; } g++; p += 2; } break; /* Format 1 */ case 1: first_gid = 1; p = font->charset + 1; while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) { first_cid = be16_to_cpu( *((uint16_t *)p) ); num_left = p[2]; if (cid >= first_cid && cid <= first_cid + num_left) { *gid = first_gid + cid - first_cid; return CAIRO_STATUS_SUCCESS; } first_gid += num_left + 1; p += 3; } break; /* Format 2 */ case 2: first_gid = 1; p = font->charset + 1; while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) { first_cid = be16_to_cpu( *((uint16_t *)p) ); num_left = be16_to_cpu( *((uint16_t *)(p+2)) ); if (cid >= first_cid && cid <= first_cid + num_left) { *gid = first_gid + cid - first_cid; return CAIRO_STATUS_SUCCESS; } first_gid += num_left + 1; p += 4; } break; default: break; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font) { cff_index_element_t *element; unsigned int i; cairo_int_status_t status; unsigned long glyph, cid; font->subset_subroutines = TRUE; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { if (font->is_cid) { cid = font->scaled_font_subset->glyphs[i]; status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph); if (unlikely (status)) return status; } else { glyph = font->scaled_font_subset->glyphs[i]; } element = _cairo_array_index (&font->charstrings_index, glyph); status = cff_index_append (&font->charstrings_subset_index, element->data, element->length); if (unlikely (status)) return status; if (font->subset_subroutines) { status = cairo_cff_find_width_and_subroutines_used (font, element->data, element->length, glyph, i); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* If parsing the charstrings fails we embed all the * subroutines. But if the font is not opentype we * need to successfully parse all charstrings to get * the widths. */ font->subset_subroutines = FALSE; if (!font->is_opentype) return status; } else if (unlikely (status)) { return status; } } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) { unsigned int i; int fd; int *reverse_map; unsigned long cid, gid; cairo_int_status_t status; font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->fdselect_subset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); reverse_map = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (reverse_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < font->num_fontdicts; i++) reverse_map[i] = -1; font->num_subset_fontdicts = 0; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { cid = font->scaled_font_subset->glyphs[i]; status = cairo_cff_font_get_gid_for_cid (font, cid, &gid); if (unlikely (status)) return status; fd = font->fdselect[gid]; if (reverse_map[fd] < 0) { font->fd_subset_map[font->num_subset_fontdicts] = fd; reverse_map[fd] = font->num_subset_fontdicts++; } font->fdselect_subset[i] = reverse_map[fd]; } free (reverse_map); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) { unsigned char buf[100]; unsigned char *end_buf; cairo_status_t status; font->num_fontdicts = 1; font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); if (unlikely (font->fd_dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (cff_dict_init (&font->fd_dict[0])) { free (font->fd_dict); font->fd_dict = NULL; font->num_fontdicts = 0; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } font->fd_subset_map = malloc (sizeof (int)); if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map[0] = 0; font->num_subset_fontdicts = 1; /* Set integer operand to max value to use max size encoding to reserve * space for any value later */ end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_strings (cairo_cff_font_t *font) { cairo_status_t status; unsigned int i; status = cairo_cff_font_subset_dict_strings (font, font->top_dict); if (unlikely (status)) return status; if (font->is_cid) { for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; } } else { status = cairo_cff_font_subset_dict_strings (font, font->private_dict); } return status; } /* The Euro is the only the only character in the winansi encoding * with a glyph name that is not a CFF standard string. As the strings * are written before the charset, we need to check during the * subsetting phase if the Euro glyph is required and add the * glyphname to the list of strings to write out. */ static cairo_status_t cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font) { cairo_status_t status; unsigned int i; int ch; const char *euro = "Euro"; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { ch = font->scaled_font_subset->to_latin_char[i]; if (ch == 128) { font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)euro, strlen(euro)); return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_font (cairo_cff_font_t *font) { cairo_status_t status; if (!font->scaled_font_subset->is_latin) { status = cairo_cff_font_set_ros_strings (font); if (unlikely (status)) return status; } status = cairo_cff_font_subset_charstrings_and_subroutines (font); if (unlikely (status)) return status; if (!font->scaled_font_subset->is_latin) { if (font->is_cid) status = cairo_cff_font_subset_fontdict (font); else status = cairo_cff_font_create_cid_fontdict (font); if (unlikely (status)) return status; } else { font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } status = cairo_cff_font_subset_strings (font); if (unlikely (status)) return status; if (font->scaled_font_subset->is_latin) status = cairo_cff_font_add_euro_charset_string (font); return status; } /* Set the operand of the specified operator in the (already written) * top dict to point to the current position in the output * array. Operands updated with this function must have previously * been encoded with the 5-byte (max) integer encoding. */ static void cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font, int operator) { int cur_pos; int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *op_ptr; cur_pos = _cairo_array_num_elements (&font->output); buf_end = encode_integer_max (buf, cur_pos); offset = cff_dict_get_location (font->top_dict, operator, &size); assert (offset > 0); op_ptr = _cairo_array_index (&font->output, offset); memcpy (op_ptr, buf, buf_end - buf); } static cairo_status_t cairo_cff_font_write_header (cairo_cff_font_t *font) { return _cairo_array_append_multiple (&font->output, font->header, font->header->header_size); } static cairo_status_t cairo_cff_font_write_name (cairo_cff_font_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_array_t index; cff_index_init (&index); status = cff_index_append_copy (&index, (unsigned char *) font->ps_name, strlen(font->ps_name)); if (unlikely (status)) goto FAIL; status = cff_index_write (&index, &font->output); if (unlikely (status)) goto FAIL; FAIL: cff_index_fini (&index); return status; } static cairo_status_t cairo_cff_font_write_top_dict (cairo_cff_font_t *font) { uint16_t count; unsigned char buf[10]; unsigned char *p; int offset_index; int dict_start, dict_size; int offset_size = 4; cairo_status_t status; /* Write an index containing the top dict */ count = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &count, 2); if (unlikely (status)) return status; buf[0] = offset_size; status = _cairo_array_append (&font->output, buf); if (unlikely (status)) return status; encode_index_offset (buf, offset_size, 1); status = _cairo_array_append_multiple (&font->output, buf, offset_size); if (unlikely (status)) return status; /* Reserve space for last element of offset array and update after * dict is written */ offset_index = _cairo_array_num_elements (&font->output); status = _cairo_array_append_multiple (&font->output, buf, offset_size); if (unlikely (status)) return status; dict_start = _cairo_array_num_elements (&font->output); status = cff_dict_write (font->top_dict, &font->output); if (unlikely (status)) return status; dict_size = _cairo_array_num_elements (&font->output) - dict_start; encode_index_offset (buf, offset_size, dict_size + 1); p = _cairo_array_index (&font->output, offset_index); memcpy (p, buf, offset_size); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_strings (cairo_cff_font_t *font) { return cff_index_write (&font->strings_subset_index, &font->output); } static cairo_status_t cairo_cff_font_write_global_subrs (cairo_cff_font_t *font) { unsigned int i; unsigned char return_op = TYPE2_return; /* poppler and fontforge don't like zero length subroutines so we * replace unused subroutines with a 'return' instruction. */ if (font->subset_subroutines) { for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) { if (! font->global_subs_used[i]) cff_index_set_object (&font->global_sub_index, i, &return_op, 1); } } return cff_index_write (&font->global_sub_index, &font->output); } static cairo_status_t cairo_cff_font_write_encoding (cairo_cff_font_t *font) { unsigned char buf[2]; cairo_status_t status; unsigned int i; cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP); buf[0] = 0; /* Format 0 */ buf[1] = font->scaled_font_subset->num_glyphs - 1; status = _cairo_array_append_multiple (&font->output, buf, 2); if (unlikely (status)) return status; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { unsigned char ch = font->scaled_font_subset->to_latin_char[i]; status = _cairo_array_append (&font->output, &ch); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_fdselect (cairo_cff_font_t *font) { unsigned char data; unsigned int i; cairo_int_status_t status; cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP); if (font->is_cid) { data = 0; status = _cairo_array_append (&font->output, &data); if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { data = font->fdselect_subset[i]; status = _cairo_array_append (&font->output, &data); if (unlikely (status)) return status; } } else { unsigned char byte; uint16_t word; status = _cairo_array_grow_by (&font->output, 9); if (unlikely (status)) return status; byte = 3; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (0); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); byte = 0; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } /* Winansi to CFF standard strings mapping for characters 128 to 255 */ static const int winansi_to_cff_std_string[] = { /* 128 */ 0, 0, 117, 101, 118, 121, 112, 113, 126, 122, 192, 107, 142, 0, 199, 0, /* 144 */ 0, 65, 8, 105, 119, 116, 111, 137, 127, 153, 221, 108, 148, 0, 228, 198, /* 160 */ 0, 96, 97, 98, 103, 100, 160, 102, 131, 170, 139, 106, 151, 0, 165, 128, /* 176 */ 161, 156, 164, 169, 125, 152, 115, 114, 133, 150, 143, 120, 158, 155, 163, 123, /* 192 */ 174, 171, 172, 176, 173, 175, 138, 177, 181, 178, 179, 180, 185, 182, 183, 184, /* 208 */ 154, 186, 190, 187, 188, 191, 189, 168, 141, 196, 193, 194, 195, 197, 157, 149, /* 224 */ 203, 200, 201, 205, 202, 204, 144, 206, 210, 207, 208, 209, 214, 211, 212, 213, /* 240 */ 167, 215, 219, 216, 217, 220, 218, 159, 147, 225, 222, 223, 224, 226, 162, 227, }; static int cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch) { int sid; if (ch == 39) { sid = 104; } else if (ch == 96) { sid = 124; } else if (ch >= 32 && ch <= 126) { sid = ch - 31; } else if (ch == 128) { assert (font->euro_sid >= NUM_STD_STRINGS); sid = font->euro_sid; } else if (ch >= 128 && ch <= 255) { sid = winansi_to_cff_std_string[ch - 128]; } else { sid = 0; } return sid; } static cairo_status_t cairo_cff_font_write_type1_charset (cairo_cff_font_t *font) { unsigned char format = 0; unsigned int i; int ch, sid; cairo_status_t status; uint16_t sid_be16; cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); status = _cairo_array_append (&font->output, &format); if (unlikely (status)) return status; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { ch = font->scaled_font_subset->to_latin_char[i]; sid = cairo_cff_font_get_sid_for_winansi_char (font, ch); if (unlikely (status)) return status; sid_be16 = cpu_to_be16(sid); status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16)); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_cid_charset (cairo_cff_font_t *font) { unsigned char byte; uint16_t word; cairo_status_t status; cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); status = _cairo_array_grow_by (&font->output, 5); if (unlikely (status)) return status; byte = 2; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_STATUS_SUCCESS); word = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_STATUS_SUCCESS); word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_STATUS_SUCCESS); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_charstrings (cairo_cff_font_t *font) { cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP); return cff_index_write (&font->charstrings_subset_index, &font->output); } static cairo_status_t cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) { unsigned int i; cairo_int_status_t status; unsigned int offset_array; uint32_t *offset_array_ptr; int offset_base; uint16_t count; uint8_t offset_size = 4; cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); count = cpu_to_be16 (font->num_subset_fontdicts); status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); if (unlikely (status)) return status; status = _cairo_array_append (&font->output, &offset_size); if (unlikely (status)) return status; offset_array = _cairo_array_num_elements (&font->output); status = _cairo_array_allocate (&font->output, (font->num_subset_fontdicts + 1)*offset_size, (void **) &offset_array_ptr); if (unlikely (status)) return status; offset_base = _cairo_array_num_elements (&font->output) - 1; *offset_array_ptr = cpu_to_be32(1); offset_array += sizeof(uint32_t); for (i = 0; i < font->num_subset_fontdicts; i++) { status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], &font->output); if (unlikely (status)) return status; offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array); *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); offset_array += sizeof(uint32_t); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_private_dict (cairo_cff_font_t *font, int dict_num, cairo_hash_table_t *parent_dict, cairo_hash_table_t *private_dict) { int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *p; cairo_status_t status; /* Write private dict and update offset and size in top dict */ font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); status = cff_dict_write (private_dict, &font->output); if (unlikely (status)) return status; size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; /* private entry has two operands - size and offset */ buf_end = encode_integer_max (buf, size); buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]); offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size); assert (offset > 0); p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_local_sub (cairo_cff_font_t *font, int dict_num, cairo_hash_table_t *private_dict, cairo_array_t *local_sub_index, cairo_bool_t *local_subs_used) { int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *p; cairo_status_t status; unsigned int i; unsigned char return_op = TYPE2_return; if (_cairo_array_num_elements (local_sub_index) > 0) { /* Write local subroutines and update offset in private * dict. Local subroutines offset is relative to start of * private dict */ offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; buf_end = encode_integer_max (buf, offset); offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size); assert (offset > 0); p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); /* poppler and fontforge don't like zero length subroutines so * we replace unused subroutines with a 'return' instruction. */ if (font->subset_subroutines) { for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) { if (! local_subs_used[i]) cff_index_set_object (local_sub_index, i, &return_op, 1); } } status = cff_index_write (local_sub_index, &font->output); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) { unsigned int i; cairo_int_status_t status; if (font->is_cid) { for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_write_private_dict ( font, i, font->fd_dict[font->fd_subset_map[i]], font->fd_private_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; } for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_write_local_sub ( font, i, font->fd_private_dict[font->fd_subset_map[i]], &font->fd_local_sub_index[font->fd_subset_map[i]], font->fd_local_subs_used[font->fd_subset_map[i]]); if (unlikely (status)) return status; } } else { status = cairo_cff_font_write_private_dict (font, 0, font->fd_dict[0], font->private_dict); if (unlikely (status)) return status; status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, &font->local_sub_index, font->local_subs_used); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font) { cairo_int_status_t status; status = cairo_cff_font_write_private_dict (font, 0, font->top_dict, font->private_dict); if (unlikely (status)) return status; status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, &font->local_sub_index, font->local_subs_used); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*font_write_t) (cairo_cff_font_t *font); static const font_write_t font_write_cid_funcs[] = { cairo_cff_font_write_header, cairo_cff_font_write_name, cairo_cff_font_write_top_dict, cairo_cff_font_write_strings, cairo_cff_font_write_global_subrs, cairo_cff_font_write_cid_charset, cairo_cff_font_write_fdselect, cairo_cff_font_write_charstrings, cairo_cff_font_write_cid_fontdict, cairo_cff_font_write_cid_private_dict_and_local_sub, }; static const font_write_t font_write_type1_funcs[] = { cairo_cff_font_write_header, cairo_cff_font_write_name, cairo_cff_font_write_top_dict, cairo_cff_font_write_strings, cairo_cff_font_write_global_subrs, cairo_cff_font_write_encoding, cairo_cff_font_write_type1_charset, cairo_cff_font_write_charstrings, cairo_cff_font_write_type1_private_dict_and_local_sub, }; static cairo_status_t cairo_cff_font_write_subset (cairo_cff_font_t *font) { cairo_int_status_t status; unsigned int i; if (font->scaled_font_subset->is_latin) { for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) { status = font_write_type1_funcs[i] (font); if (unlikely (status)) return status; } } else { for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) { status = font_write_cid_funcs[i] (font); if (unlikely (status)) return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_generate (cairo_cff_font_t *font, const char **data, unsigned long *length) { cairo_int_status_t status; status = cairo_cff_font_read_font (font); if (unlikely (status)) return status; /* If the PS name is not found, create a CairoFont-x-y name. */ if (font->ps_name == NULL) { font->ps_name = malloc (30); if (unlikely (font->ps_name == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); snprintf(font->ps_name, 30, "CairoFont-%u-%u", font->scaled_font_subset->font_id, font->scaled_font_subset->subset_id); } status = cairo_cff_font_subset_font (font); if (unlikely (status)) return status; status = cairo_cff_font_write_subset (font); if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_create_set_widths (cairo_cff_font_t *font) { unsigned long size; unsigned long long_entry_size; unsigned long short_entry_size; unsigned int i; tt_hhea_t hhea; int num_hmetrics; unsigned char buf[10]; int glyph_index; cairo_int_status_t status; size = sizeof (tt_hhea_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); if (unlikely (status)) return status; num_hmetrics = be16_to_cpu (hhea.num_hmetrics); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { glyph_index = font->scaled_font_subset->glyphs[i]; long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); if (glyph_index < num_hmetrics) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, glyph_index * long_entry_size, buf, &short_entry_size); if (unlikely (status)) return status; } else { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, buf, &short_entry_size); if (unlikely (status)) return status; } font->widths[i] = be16_to_cpu (*((int16_t*)buf)); } return CAIRO_STATUS_SUCCESS; } static cairo_bool_t check_fontdata_is_cff (const unsigned char *data, long length) { cff_header_t *header; if (length < (long)sizeof (cff_header_t)) return FALSE; header = (cff_header_t *) data; if (header->major == 1 && header->minor == 0 && header->header_size == 4) { return TRUE; } return FALSE; } static cairo_int_status_t _cairo_cff_font_load_opentype_cff (cairo_cff_font_t *font) { const cairo_scaled_font_backend_t *backend = font->backend; cairo_status_t status; tt_head_t head; tt_hhea_t hhea; unsigned long size, data_length; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; data_length = 0; status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, NULL, &data_length); if (status) return status; size = sizeof (tt_head_t); status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); if (unlikely (status)) return status; size = sizeof (tt_hhea_t); status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); if (unlikely (status)) return status; size = 0; status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, 0, NULL, &size); if (unlikely (status)) return status; font->x_min = (int16_t) be16_to_cpu (head.x_min); font->y_min = (int16_t) be16_to_cpu (head.y_min); font->x_max = (int16_t) be16_to_cpu (head.x_max); font->y_max = (int16_t) be16_to_cpu (head.y_max); font->ascent = (int16_t) be16_to_cpu (hhea.ascender); font->descent = (int16_t) be16_to_cpu (hhea.descender); font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em); if (font->units_per_em == 0) font->units_per_em = 1000; font->font_name = NULL; status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font, &font->ps_name, &font->font_name); if (_cairo_status_is_error (status)) return status; font->is_opentype = TRUE; font->data_length = data_length; font->data = malloc (data_length); if (unlikely (font->data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, font->data, &font->data_length); if (unlikely (status)) return status; if (!check_fontdata_is_cff (font->data, data_length)) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_cff_font_load_cff (cairo_cff_font_t *font) { const cairo_scaled_font_backend_t *backend = font->backend; cairo_status_t status; unsigned long data_length; if (!backend->load_type1_data) return CAIRO_INT_STATUS_UNSUPPORTED; data_length = 0; status = backend->load_type1_data (font->scaled_font_subset->scaled_font, 0, NULL, &data_length); if (unlikely (status)) return status; font->font_name = NULL; font->is_opentype = FALSE; font->data_length = data_length; font->data = malloc (data_length); if (unlikely (font->data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font, 0, font->data, &font->data_length); if (unlikely (status)) return status; if (!check_fontdata_is_cff (font->data, data_length)) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_cff_font_t **font_return, const char *subset_name) { const cairo_scaled_font_backend_t *backend; cairo_int_status_t status; cairo_cff_font_t *font; backend = scaled_font_subset->scaled_font->backend; /* We need to use a fallback font generated from the synthesized outlines. */ if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; font = calloc (1, sizeof (cairo_cff_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; font->scaled_font_subset = scaled_font_subset; status = _cairo_cff_font_load_opentype_cff (font); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = _cairo_cff_font_load_cff (font); if (status) goto fail1; font->data_end = font->data + font->data_length; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); if (unlikely (status)) goto fail2; font->subset_font_name = strdup (subset_name); if (unlikely (font->subset_font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } if (font->is_opentype) { status = cairo_cff_font_create_set_widths (font); if (unlikely (status)) goto fail4; } status = cff_dict_init (&font->top_dict); if (unlikely (status)) goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); cff_index_init (&font->global_sub_index); cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); font->euro_sid = 0; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; font->fd_local_sub_index = NULL; font->fd_local_sub_bias = NULL; font->fdselect_subset = NULL; font->fd_subset_map = NULL; font->private_dict_offset = NULL; font->global_subs_used = NULL; font->local_subs_used = NULL; font->fd_local_subs_used = NULL; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_hash_table_destroy (font->top_dict); fail4: free (font->widths); fail3: free (font->subset_font_name); fail2: free (font->ps_name); _cairo_array_fini (&font->output); fail1: free (font->data); free (font->font_name); free (font); return status; } static void cairo_cff_font_destroy (cairo_cff_font_t *font) { unsigned int i; free (font->widths); free (font->font_name); free (font->ps_name); free (font->subset_font_name); _cairo_array_fini (&font->output); cff_dict_fini (font->top_dict); cff_dict_fini (font->private_dict); cff_index_fini (&font->strings_index); cff_index_fini (&font->charstrings_index); cff_index_fini (&font->global_sub_index); cff_index_fini (&font->local_sub_index); cff_index_fini (&font->charstrings_subset_index); cff_index_fini (&font->strings_subset_index); /* If we bailed out early as a result of an error some of the * following cairo_cff_font_t members may still be NULL */ if (font->fd_dict) { for (i = 0; i < font->num_fontdicts; i++) { if (font->fd_dict[i]) cff_dict_fini (font->fd_dict[i]); } free (font->fd_dict); } free (font->global_subs_used); free (font->local_subs_used); free (font->fd_subset_map); free (font->private_dict_offset); if (font->is_cid) { free (font->fdselect); free (font->fdselect_subset); if (font->fd_private_dict) { for (i = 0; i < font->num_fontdicts; i++) { if (font->fd_private_dict[i]) cff_dict_fini (font->fd_private_dict[i]); } free (font->fd_private_dict); } if (font->fd_local_sub_index) { for (i = 0; i < font->num_fontdicts; i++) cff_index_fini (&font->fd_local_sub_index[i]); free (font->fd_local_sub_index); } free (font->fd_local_sub_bias); if (font->fd_local_subs_used) { for (i = 0; i < font->num_fontdicts; i++) { free (font->fd_local_subs_used[i]); } free (font->fd_local_subs_used); } free (font->fd_default_width); free (font->fd_nominal_width); } free (font->data); free (font); } cairo_status_t _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, const char *subset_name, cairo_scaled_font_subset_t *font_subset) { cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned int i; status = _cairo_cff_font_create (font_subset, &font, subset_name); if (unlikely (status)) return status; status = cairo_cff_font_generate (font, &data, &length); if (unlikely (status)) goto fail1; cff_subset->ps_name = strdup (font->ps_name); if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } if (font->font_name) { cff_subset->family_name_utf8 = strdup (font->font_name); if (cff_subset->family_name_utf8 == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { cff_subset->family_name_utf8 = NULL; } cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em; cff_subset->x_min = (double)font->x_min/font->units_per_em; cff_subset->y_min = (double)font->y_min/font->units_per_em; cff_subset->x_max = (double)font->x_max/font->units_per_em; cff_subset->y_max = (double)font->y_max/font->units_per_em; cff_subset->ascent = (double)font->ascent/font->units_per_em; cff_subset->descent = (double)font->descent/font->units_per_em; cff_subset->data = malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (cff_subset->data, data, length); cff_subset->data_length = length; cairo_cff_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail4: free (cff_subset->widths); fail3: free (cff_subset->family_name_utf8); fail2: free (cff_subset->ps_name); fail1: cairo_cff_font_destroy (font); return status; } void _cairo_cff_subset_fini (cairo_cff_subset_t *subset) { free (subset->ps_name); free (subset->family_name_utf8); free (subset->widths); free (subset->data); } cairo_bool_t _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) { const cairo_scaled_font_backend_t *backend; cairo_int_status_t status; unsigned char *data; unsigned long data_length; unsigned char *current_ptr; unsigned char *data_end; cff_header_t *header; cff_index_element_t *element; cairo_hash_table_t *top_dict; cairo_array_t index; int size; cairo_bool_t is_cid = FALSE; backend = scaled_font->backend; data = NULL; data_length = 0; status = CAIRO_INT_STATUS_UNSUPPORTED; /* Try to load an OpenType/CFF font */ if (backend->load_truetype_table && (status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) { data = malloc (data_length); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, 0, data, &data_length); if (unlikely (status)) goto fail1; } /* Try to load a CFF font */ if (status == CAIRO_INT_STATUS_UNSUPPORTED && backend->load_type1_data && (status = backend->load_type1_data (scaled_font, 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) { data = malloc (data_length); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } status = backend->load_type1_data (scaled_font, 0, data, &data_length); if (unlikely (status)) goto fail1; } if (status) goto fail1; /* Check if it looks like a CFF font */ if (!check_fontdata_is_cff (data, data_length)) goto fail1; data_end = data + data_length; /* skip header */ if (data_length < sizeof (cff_header_t)) goto fail1; header = (cff_header_t *) data; current_ptr = data + header->header_size; /* skip name */ cff_index_init (&index); status = cff_index_read (&index, ¤t_ptr, data_end); cff_index_fini (&index); if (status) goto fail1; /* read top dict */ cff_index_init (&index); status = cff_index_read (&index, ¤t_ptr, data_end); if (unlikely (status)) goto fail2; status = cff_dict_init (&top_dict); if (unlikely (status)) goto fail2; element = _cairo_array_index (&index, 0); status = cff_dict_read (top_dict, element->data, element->length); if (unlikely (status)) goto fail3; /* check for ROS operator indicating a CID font */ if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL) is_cid = TRUE; fail3: cff_dict_fini (top_dict); fail2: cff_index_fini (&index); fail1: free (data); return is_cid; } static cairo_int_status_t _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_cff_font_t **font_return, const char *subset_name) { cairo_status_t status; cairo_cff_font_t *font; font = malloc (sizeof (cairo_cff_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = NULL; font->scaled_font_subset = scaled_font_subset; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); if (unlikely (status)) goto fail1; font->subset_font_name = strdup (subset_name); if (unlikely (font->subset_font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } font->ps_name = strdup (subset_name); if (unlikely (font->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } font->font_name = NULL; font->x_min = 0; font->y_min = 0; font->x_max = 0; font->y_max = 0; font->ascent = 0; font->descent = 0; font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } font->data_length = 0; font->data = NULL; font->data_end = NULL; status = cff_dict_init (&font->top_dict); if (unlikely (status)) goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); cff_index_init (&font->global_sub_index); cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); font->global_subs_used = NULL; font->local_subs_used = NULL; font->subset_subroutines = FALSE; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; font->fd_local_sub_index = NULL; font->fdselect_subset = NULL; font->fd_subset_map = NULL; font->private_dict_offset = NULL; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_hash_table_destroy (font->top_dict); fail4: free (font->widths); fail3: free (font->font_name); free (font->ps_name); fail2: free (font->subset_font_name); fail1: _cairo_array_fini (&font->output); free (font); return status; } static cairo_int_status_t cairo_cff_font_fallback_generate (cairo_cff_font_t *font, cairo_type2_charstrings_t *type2_subset, const char **data, unsigned long *length) { cairo_int_status_t status; cff_header_t header; cairo_array_t *charstring; unsigned char buf[40]; unsigned char *end_buf, *end_buf2; unsigned int i; int sid; /* Create header */ header.major = 1; header.minor = 0; header.header_size = 4; header.offset_size = 4; font->header = &header; /* Create Top Dict */ font->is_cid = FALSE; snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u", font->scaled_font_subset->font_id, font->scaled_font_subset->subset_id); sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)buf, strlen((char*)buf)); if (unlikely (status)) return status; end_buf = encode_integer (buf, sid); status = cff_dict_set_operands (font->top_dict, FULLNAME_OP, buf, end_buf - buf); if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP, buf, end_buf - buf); if (unlikely (status)) return status; end_buf = encode_integer (buf, type2_subset->x_min); end_buf = encode_integer (end_buf, type2_subset->y_min); end_buf = encode_integer (end_buf, type2_subset->x_max); end_buf = encode_integer (end_buf, type2_subset->y_max); status = cff_dict_set_operands (font->top_dict, FONTBBOX_OP, buf, end_buf - buf); if (unlikely (status)) return status; end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); if (unlikely (status)) return status; if (font->scaled_font_subset->is_latin) { status = cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf); if (unlikely (status)) return status; /* Private has two operands - size and offset */ end_buf2 = encode_integer_max (end_buf, 0); cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf); if (unlikely (status)) return status; } else { status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); if (unlikely (status)) return status; } status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) return status; if (!font->scaled_font_subset->is_latin) { status = cairo_cff_font_set_ros_strings (font); if (unlikely (status)) return status; /* Create CID FD dictionary */ status = cairo_cff_font_create_cid_fontdict (font); if (unlikely (status)) return status; } else { font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Create charstrings */ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { charstring = _cairo_array_index(&type2_subset->charstrings, i); status = cff_index_append (&font->charstrings_subset_index, _cairo_array_index (charstring, 0), _cairo_array_num_elements (charstring)); if (unlikely (status)) return status; } if (font->scaled_font_subset->is_latin) status = cairo_cff_font_add_euro_charset_string (font); status = cairo_cff_font_write_subset (font); if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, const char *subset_name, cairo_scaled_font_subset_t *font_subset) { cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned int i; cairo_type2_charstrings_t type2_subset; status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); if (unlikely (status)) return status; status = _cairo_type2_charstrings_init (&type2_subset, font_subset); if (unlikely (status)) goto fail1; status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); if (unlikely (status)) goto fail2; cff_subset->family_name_utf8 = NULL; cff_subset->ps_name = strdup (font->ps_name); if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = (double)type2_subset.widths[i]/1000; cff_subset->x_min = (double)type2_subset.x_min/1000; cff_subset->y_min = (double)type2_subset.y_min/1000; cff_subset->x_max = (double)type2_subset.x_max/1000; cff_subset->y_max = (double)type2_subset.y_max/1000; cff_subset->ascent = (double)type2_subset.y_max/1000; cff_subset->descent = (double)type2_subset.y_min/1000; cff_subset->data = malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (cff_subset->data, data, length); cff_subset->data_length = length; _cairo_type2_charstrings_fini (&type2_subset); cairo_cff_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail4: free (cff_subset->widths); fail3: free (cff_subset->ps_name); fail2: _cairo_type2_charstrings_fini (&type2_subset); fail1: cairo_cff_font_destroy (font); return status; } void _cairo_cff_fallback_fini (cairo_cff_subset_t *subset) { free (subset->ps_name); free (subset->widths); free (subset->data); } #endif /* CAIRO_HAS_FONT_SUBSET */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-boxes.c������������������������������0000664�0000000�0000000�00000035256�12710376503�0025617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" static inline int pot (int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } static cairo_bool_t _cairo_clip_contains_rectangle_box (const cairo_clip_t *clip, const cairo_rectangle_int_t *rect, const cairo_box_t *box) { int i; /* clip == NULL means no clip, so the clip contains everything */ if (clip == NULL) return TRUE; if (_cairo_clip_is_all_clipped (clip)) return FALSE; /* If we have a non-trivial path, just say no */ if (clip->path) return FALSE; if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect)) return FALSE; if (clip->num_boxes == 0) return TRUE; /* Check for a clip-box that wholly contains the rectangle */ for (i = 0; i < clip->num_boxes; i++) { if (box->p1.x >= clip->boxes[i].p1.x && box->p1.y >= clip->boxes[i].p1.y && box->p2.x <= clip->boxes[i].p2.x && box->p2.y <= clip->boxes[i].p2.y) { return TRUE; } } return FALSE; } cairo_bool_t _cairo_clip_contains_box (const cairo_clip_t *clip, const cairo_box_t *box) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (box, &rect); return _cairo_clip_contains_rectangle_box(clip, &rect, box); } cairo_bool_t _cairo_clip_contains_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *rect) { cairo_box_t box; box.p1.x = _cairo_fixed_from_int (rect->x); box.p1.y = _cairo_fixed_from_int (rect->y); box.p2.x = _cairo_fixed_from_int (rect->x + rect->width); box.p2.y = _cairo_fixed_from_int (rect->y + rect->height); return _cairo_clip_contains_rectangle_box (clip, rect, &box); } cairo_clip_t * _cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_status_t status; cairo_boxes_t boxes; _cairo_boxes_init (&boxes); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes)) clip = _cairo_clip_intersect_boxes (clip, &boxes); else clip = _cairo_clip_set_all_clipped (clip); _cairo_boxes_fini (&boxes); return clip; } static cairo_clip_t * _cairo_clip_intersect_rectangle_box (cairo_clip_t *clip, const cairo_rectangle_int_t *r, const cairo_box_t *box) { cairo_box_t extents_box; cairo_bool_t changed = FALSE; int i, j; if (clip == NULL) { clip = _cairo_clip_create (); if (clip == NULL) return _cairo_clip_set_all_clipped (clip); } if (clip->num_boxes == 0) { clip->boxes = &clip->embedded_box; clip->boxes[0] = *box; clip->num_boxes = 1; if (clip->path == NULL) { clip->extents = *r; } else { if (! _cairo_rectangle_intersect (&clip->extents, r)) clip = _cairo_clip_set_all_clipped (clip); } if (clip->path == NULL) clip->is_region = _cairo_box_is_pixel_aligned (box); return clip; } /* Does the new box wholly subsume the clip? Perform a cheap check * for the common condition of a single clip rectangle. */ if (clip->num_boxes == 1 && clip->boxes[0].p1.x >= box->p1.x && clip->boxes[0].p1.y >= box->p1.y && clip->boxes[0].p2.x <= box->p2.x && clip->boxes[0].p2.y <= box->p2.y) { return clip; } for (i = j = 0; i < clip->num_boxes; i++) { cairo_box_t *b = &clip->boxes[j]; if (j != i) *b = clip->boxes[i]; if (box->p1.x > b->p1.x) b->p1.x = box->p1.x, changed = TRUE; if (box->p2.x < b->p2.x) b->p2.x = box->p2.x, changed = TRUE; if (box->p1.y > b->p1.y) b->p1.y = box->p1.y, changed = TRUE; if (box->p2.y < b->p2.y) b->p2.y = box->p2.y, changed = TRUE; j += b->p2.x > b->p1.x && b->p2.y > b->p1.y; } clip->num_boxes = j; if (clip->num_boxes == 0) return _cairo_clip_set_all_clipped (clip); if (! changed) return clip; extents_box = clip->boxes[0]; for (i = 1; i < clip->num_boxes; i++) { if (clip->boxes[i].p1.x < extents_box.p1.x) extents_box.p1.x = clip->boxes[i].p1.x; if (clip->boxes[i].p1.y < extents_box.p1.y) extents_box.p1.y = clip->boxes[i].p1.y; if (clip->boxes[i].p2.x > extents_box.p2.x) extents_box.p2.x = clip->boxes[i].p2.x; if (clip->boxes[i].p2.y > extents_box.p2.y) extents_box.p2.y = clip->boxes[i].p2.y; } if (clip->path == NULL) { _cairo_box_round_to_rectangle (&extents_box, &clip->extents); } else { cairo_rectangle_int_t extents_rect; _cairo_box_round_to_rectangle (&extents_box, &extents_rect); if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect)) return _cairo_clip_set_all_clipped (clip); } if (clip->region) { cairo_region_destroy (clip->region); clip->region = NULL; } clip->is_region = FALSE; return clip; } cairo_clip_t * _cairo_clip_intersect_box (cairo_clip_t *clip, const cairo_box_t *box) { cairo_rectangle_int_t r; _cairo_box_round_to_rectangle (box, &r); if (r.width == 0 || r.height == 0) return _cairo_clip_set_all_clipped (clip); return _cairo_clip_intersect_rectangle_box (clip, &r, box); } cairo_clip_t * _cairo_clip_intersect_boxes (cairo_clip_t *clip, const cairo_boxes_t *boxes) { cairo_boxes_t clip_boxes; cairo_box_t limits; cairo_rectangle_int_t extents; if (_cairo_clip_is_all_clipped (clip)) return clip; if (boxes->num_boxes == 0) return _cairo_clip_set_all_clipped (clip); if (boxes->num_boxes == 1) return _cairo_clip_intersect_box (clip, boxes->chunks.base); if (clip == NULL) clip = _cairo_clip_create (); if (clip->num_boxes) { _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes); if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) { clip = _cairo_clip_set_all_clipped (clip); goto out; } if (clip->boxes != &clip->embedded_box) free (clip->boxes); clip->boxes = NULL; boxes = &clip_boxes; } if (boxes->num_boxes == 0) { clip = _cairo_clip_set_all_clipped (clip); goto out; } else if (boxes->num_boxes == 1) { clip->boxes = &clip->embedded_box; clip->boxes[0] = boxes->chunks.base[0]; clip->num_boxes = 1; } else { clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE); } _cairo_boxes_extents (boxes, &limits); _cairo_box_round_to_rectangle (&limits, &extents); if (clip->path == NULL) clip->extents = extents; else if (! _cairo_rectangle_intersect (&clip->extents, &extents)) clip = _cairo_clip_set_all_clipped (clip); if (clip->region) { cairo_region_destroy (clip->region); clip->region = NULL; } clip->is_region = FALSE; out: if (boxes == &clip_boxes) _cairo_boxes_fini (&clip_boxes); return clip; } cairo_clip_t * _cairo_clip_intersect_rectangle (cairo_clip_t *clip, const cairo_rectangle_int_t *r) { cairo_box_t box; if (_cairo_clip_is_all_clipped (clip)) return clip; if (r->width == 0 || r->height == 0) return _cairo_clip_set_all_clipped (clip); box.p1.x = _cairo_fixed_from_int (r->x); box.p1.y = _cairo_fixed_from_int (r->y); box.p2.x = _cairo_fixed_from_int (r->x + r->width); box.p2.y = _cairo_fixed_from_int (r->y + r->height); return _cairo_clip_intersect_rectangle_box (clip, r, &box); } struct reduce { cairo_clip_t *clip; cairo_box_t limit; cairo_box_t extents; cairo_bool_t inside; cairo_point_t current_point; cairo_point_t last_move_to; }; static void _add_clipped_edge (struct reduce *r, const cairo_point_t *p1, const cairo_point_t *p2, int y1, int y2) { cairo_fixed_t x; x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1); if (x < r->extents.p1.x) r->extents.p1.x = x; x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2); if (x > r->extents.p2.x) r->extents.p2.x = x; if (y1 < r->extents.p1.y) r->extents.p1.y = y1; if (y2 > r->extents.p2.y) r->extents.p2.y = y2; r->inside = TRUE; } static void _add_edge (struct reduce *r, const cairo_point_t *p1, const cairo_point_t *p2) { int top, bottom; int top_y, bot_y; int n; if (p1->y < p2->y) { top = p1->y; bottom = p2->y; } else { top = p2->y; bottom = p1->y; } if (bottom < r->limit.p1.y || top > r->limit.p2.y) return; if (p1->x > p2->x) { const cairo_point_t *t = p1; p1 = p2; p2 = t; } if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x) return; for (n = 0; n < r->clip->num_boxes; n++) { const cairo_box_t *limits = &r->clip->boxes[n]; if (bottom < limits->p1.y || top > limits->p2.y) continue; if (p2->x <= limits->p1.x || p1->x >= limits->p2.x) continue; if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) { top_y = top; bot_y = bottom; } else { int p1_y, p2_y; p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p1.x); p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p2.x); if (p1_y < p2_y) { top_y = p1_y; bot_y = p2_y; } else { top_y = p2_y; bot_y = p1_y; } if (top_y < top) top_y = top; if (bot_y > bottom) bot_y = bottom; } if (top_y < limits->p1.y) top_y = limits->p1.y; if (bot_y > limits->p2.y) bot_y = limits->p2.y; if (bot_y > top_y) _add_clipped_edge (r, p1, p2, top_y, bot_y); } } static cairo_status_t _reduce_line_to (void *closure, const cairo_point_t *point) { struct reduce *r = closure; _add_edge (r, &r->current_point, point); r->current_point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _reduce_close (void *closure) { struct reduce *r = closure; return _reduce_line_to (r, &r->last_move_to); } static cairo_status_t _reduce_move_to (void *closure, const cairo_point_t *point) { struct reduce *r = closure; cairo_status_t status; /* close current subpath */ status = _reduce_close (closure); /* make sure that the closure represents a degenerate path */ r->current_point = *point; r->last_move_to = *point; return status; } static cairo_clip_t * _cairo_clip_reduce_to_boxes (cairo_clip_t *clip) { struct reduce r; cairo_clip_path_t *clip_path; cairo_status_t status; return clip; if (clip->path == NULL) return clip; r.clip = clip; r.extents.p1.x = r.extents.p1.y = INT_MAX; r.extents.p2.x = r.extents.p2.y = INT_MIN; r.inside = FALSE; r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x); r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y); r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width); r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height); clip_path = clip->path; do { r.current_point.x = 0; r.current_point.y = 0; r.last_move_to = r.current_point; status = _cairo_path_fixed_interpret_flat (&clip_path->path, _reduce_move_to, _reduce_line_to, _reduce_close, &r, clip_path->tolerance); assert (status == CAIRO_STATUS_SUCCESS); _reduce_close (&r); } while ((clip_path = clip_path->prev)); if (! r.inside) { _cairo_clip_path_destroy (clip->path); clip->path = NULL; } return _cairo_clip_intersect_box (clip, &r.extents); } cairo_clip_t * _cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *r) { cairo_clip_t *copy; if (_cairo_clip_is_all_clipped (clip)) return (cairo_clip_t *) clip; if (_cairo_clip_contains_rectangle (clip, r)) return _cairo_clip_intersect_rectangle (NULL, r); copy = _cairo_clip_copy_intersect_rectangle (clip, r); if (_cairo_clip_is_all_clipped (copy)) return copy; return _cairo_clip_reduce_to_boxes (copy); } cairo_clip_t * _cairo_clip_reduce_for_composite (const cairo_clip_t *clip, cairo_composite_rectangles_t *extents) { const cairo_rectangle_int_t *r; r = extents->is_bounded ? &extents->bounded : &extents->unbounded; return _cairo_clip_reduce_to_rectangle (clip, r); } cairo_clip_t * _cairo_clip_from_boxes (const cairo_boxes_t *boxes) { cairo_box_t extents; cairo_clip_t *clip = _cairo_clip_create (); if (clip == NULL) return _cairo_clip_set_all_clipped (clip); /* XXX cow-boxes? */ if(boxes->num_boxes == 1) { clip->boxes = &clip->embedded_box; clip->boxes[0] = boxes->chunks.base[0]; clip->num_boxes = 1; } else { clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE); if (clip->boxes == NULL) return _cairo_clip_set_all_clipped (clip); } _cairo_boxes_extents (boxes, &extents); _cairo_box_round_to_rectangle (&extents, &clip->extents); return clip; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-inline.h�����������������������������0000664�0000000�0000000�00000005271�12710376503�0025754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_CLIP_INLINE_H #define CAIRO_CLIP_INLINE_H #include "cairo-clip-private.h" static inline cairo_bool_t _cairo_clip_is_all_clipped(const cairo_clip_t *clip) { return clip == &__cairo_clip_all; } static inline cairo_clip_t * _cairo_clip_set_all_clipped (cairo_clip_t *clip) { _cairo_clip_destroy (clip); return (cairo_clip_t *) &__cairo_clip_all; } static inline cairo_clip_t * _cairo_clip_copy_intersect_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *r) { return _cairo_clip_intersect_rectangle (_cairo_clip_copy (clip), r); } static inline cairo_clip_t * _cairo_clip_copy_intersect_clip (const cairo_clip_t *clip, const cairo_clip_t *other) { return _cairo_clip_intersect_clip (_cairo_clip_copy (clip), other); } static inline void _cairo_clip_steal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes) { _cairo_boxes_init_for_array (boxes, clip->boxes, clip->num_boxes); clip->boxes = NULL; clip->num_boxes = 0; } static inline void _cairo_clip_unsteal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes) { clip->boxes = boxes->chunks.base; clip->num_boxes = boxes->num_boxes; } #endif /* CAIRO_CLIP_INLINE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-polygon.c����������������������������0000664�0000000�0000000�00000011120�12710376503�0026146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" static cairo_bool_t can_convert_to_polygon (const cairo_clip_t *clip) { cairo_clip_path_t *clip_path = clip->path; cairo_antialias_t antialias = clip_path->antialias; while ((clip_path = clip_path->prev) != NULL) { if (clip_path->antialias != antialias) return FALSE; } return TRUE; } cairo_int_status_t _cairo_clip_get_polygon (const cairo_clip_t *clip, cairo_polygon_t *polygon, cairo_fill_rule_t *fill_rule, cairo_antialias_t *antialias) { cairo_status_t status; cairo_clip_path_t *clip_path; if (_cairo_clip_is_all_clipped (clip)) { _cairo_polygon_init (polygon, NULL, 0); return CAIRO_INT_STATUS_SUCCESS; } /* If there is no clip, we need an infinite polygon */ assert (clip && (clip->path || clip->num_boxes)); if (clip->path == NULL) { *fill_rule = CAIRO_FILL_RULE_WINDING; *antialias = CAIRO_ANTIALIAS_DEFAULT; return _cairo_polygon_init_box_array (polygon, clip->boxes, clip->num_boxes); } /* check that residual is all of the same type/tolerance */ if (! can_convert_to_polygon (clip)) return CAIRO_INT_STATUS_UNSUPPORTED; if (clip->num_boxes < 2) _cairo_polygon_init_with_clip (polygon, clip); else _cairo_polygon_init_with_clip (polygon, NULL); clip_path = clip->path; *fill_rule = clip_path->fill_rule; *antialias = clip_path->antialias; status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, polygon); if (unlikely (status)) goto err; if (clip->num_boxes > 1) { status = _cairo_polygon_intersect_with_boxes (polygon, fill_rule, clip->boxes, clip->num_boxes); if (unlikely (status)) goto err; } polygon->limits = NULL; polygon->num_limits = 0; while ((clip_path = clip_path->prev) != NULL) { cairo_polygon_t next; _cairo_polygon_init (&next, NULL, 0); status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, &next); if (likely (status == CAIRO_STATUS_SUCCESS)) status = _cairo_polygon_intersect (polygon, *fill_rule, &next, clip_path->fill_rule); _cairo_polygon_fini (&next); if (unlikely (status)) goto err; *fill_rule = CAIRO_FILL_RULE_WINDING; } return CAIRO_STATUS_SUCCESS; err: _cairo_polygon_fini (polygon); return status; } cairo_bool_t _cairo_clip_is_polygon (const cairo_clip_t *clip) { if (_cairo_clip_is_all_clipped (clip)) return TRUE; /* If there is no clip, we need an infinite polygon */ if (clip == NULL) return FALSE; if (clip->path == NULL) return TRUE; /* check that residual is all of the same type/tolerance */ return can_convert_to_polygon (clip); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-private.h����������������������������0000664�0000000�0000000�00000014110�12710376503�0026140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_CLIP_PRIVATE_H #define CAIRO_CLIP_PRIVATE_H #include "cairo-types-private.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-reference-count-private.h" extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; struct _cairo_clip_path { cairo_reference_count_t ref_count; cairo_path_fixed_t path; cairo_fill_rule_t fill_rule; double tolerance; cairo_antialias_t antialias; cairo_clip_path_t *prev; }; struct _cairo_clip { cairo_rectangle_int_t extents; cairo_clip_path_t *path; cairo_box_t *boxes; int num_boxes; cairo_region_t *region; cairo_bool_t is_region; cairo_box_t embedded_box; }; cairo_private cairo_clip_t * _cairo_clip_create (void); cairo_private cairo_clip_path_t * _cairo_clip_path_reference (cairo_clip_path_t *clip_path); cairo_private void _cairo_clip_path_destroy (cairo_clip_path_t *clip_path); cairo_private void _cairo_clip_destroy (cairo_clip_t *clip); cairo_private extern const cairo_clip_t __cairo_clip_all; cairo_private cairo_clip_t * _cairo_clip_copy (const cairo_clip_t *clip); cairo_private cairo_clip_t * _cairo_clip_copy_region (const cairo_clip_t *clip); cairo_private cairo_clip_t * _cairo_clip_copy_path (const cairo_clip_t *clip); cairo_private cairo_clip_t * _cairo_clip_translate (cairo_clip_t *clip, int tx, int ty); cairo_private cairo_clip_t * _cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m); cairo_private cairo_clip_t * _cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty); cairo_private cairo_bool_t _cairo_clip_equal (const cairo_clip_t *clip_a, const cairo_clip_t *clip_b); cairo_private cairo_clip_t * _cairo_clip_intersect_rectangle (cairo_clip_t *clip, const cairo_rectangle_int_t *rectangle); cairo_private cairo_clip_t * _cairo_clip_intersect_clip (cairo_clip_t *clip, const cairo_clip_t *other); cairo_private cairo_clip_t * _cairo_clip_intersect_box (cairo_clip_t *clip, const cairo_box_t *box); cairo_private cairo_clip_t * _cairo_clip_intersect_boxes (cairo_clip_t *clip, const cairo_boxes_t *boxes); cairo_private cairo_clip_t * _cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); cairo_private cairo_clip_t * _cairo_clip_intersect_path (cairo_clip_t *clip, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias); cairo_private const cairo_rectangle_int_t * _cairo_clip_get_extents (const cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty); cairo_private cairo_surface_t * _cairo_clip_get_image (const cairo_clip_t *clip, cairo_surface_t *target, const cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_clip_combine_with_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int dst_x, int dst_y); cairo_private cairo_clip_t * _cairo_clip_from_boxes (const cairo_boxes_t *boxes); cairo_private cairo_region_t * _cairo_clip_get_region (const cairo_clip_t *clip); cairo_private cairo_bool_t _cairo_clip_is_region (const cairo_clip_t *clip); cairo_private cairo_clip_t * _cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *r); cairo_private cairo_clip_t * _cairo_clip_reduce_for_composite (const cairo_clip_t *clip, cairo_composite_rectangles_t *extents); cairo_private cairo_bool_t _cairo_clip_contains_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *rect); cairo_private cairo_bool_t _cairo_clip_contains_box (const cairo_clip_t *clip, const cairo_box_t *box); cairo_private cairo_bool_t _cairo_clip_contains_extents (const cairo_clip_t *clip, const cairo_composite_rectangles_t *extents); cairo_private cairo_rectangle_list_t* _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate); cairo_private cairo_rectangle_list_t * _cairo_rectangle_list_create_in_error (cairo_status_t status); cairo_private cairo_bool_t _cairo_clip_is_polygon (const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_clip_get_polygon (const cairo_clip_t *clip, cairo_polygon_t *polygon, cairo_fill_rule_t *fill_rule, cairo_antialias_t *antialias); #endif /* CAIRO_CLIP_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-region.c�����������������������������0000664�0000000�0000000�00000007232�12710376503�0025753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" static void _cairo_clip_extract_region (cairo_clip_t *clip) { cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *r = stack_rects; cairo_bool_t is_region; int i; if (clip->num_boxes == 0) return; if (clip->num_boxes > ARRAY_LENGTH (stack_rects)) { r = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_rectangle_int_t)); if (r == NULL){ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return; } } is_region = clip->path == NULL; for (i = 0; i < clip->num_boxes; i++) { cairo_box_t *b = &clip->boxes[i]; if (is_region) is_region = _cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y); r[i].x = _cairo_fixed_integer_floor (b->p1.x); r[i].y = _cairo_fixed_integer_floor (b->p1.y); r[i].width = _cairo_fixed_integer_ceil (b->p2.x) - r[i].x; r[i].height = _cairo_fixed_integer_ceil (b->p2.y) - r[i].y; } clip->is_region = is_region; clip->region = cairo_region_create_rectangles (r, i); if (r != stack_rects) free (r); } cairo_region_t * _cairo_clip_get_region (const cairo_clip_t *clip) { if (clip == NULL) return NULL; if (clip->region == NULL) _cairo_clip_extract_region ((cairo_clip_t *) clip); return clip->region; } cairo_bool_t _cairo_clip_is_region (const cairo_clip_t *clip) { if (clip == NULL) return TRUE; if (clip->is_region) return TRUE; /* XXX Geometric reduction? */ if (clip->path) return FALSE; if (clip->num_boxes == 0) return TRUE; if (clip->region == NULL) _cairo_clip_extract_region ((cairo_clip_t *) clip); return clip->is_region; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-surface.c����������������������������0000664�0000000�0000000�00000016032�12710376503�0026116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" cairo_status_t _cairo_clip_combine_with_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int dst_x, int dst_y) { cairo_clip_path_t *copy_path; cairo_clip_path_t *clip_path; cairo_clip_t *copy; cairo_status_t status = CAIRO_STATUS_SUCCESS; copy = _cairo_clip_copy_with_translation (clip, -dst_x, -dst_y); copy_path = copy->path; copy->path = NULL; if (copy->boxes) { status = _cairo_surface_paint (dst, CAIRO_OPERATOR_IN, &_cairo_pattern_white.base, copy); } clip = NULL; if (_cairo_clip_is_region (copy)) clip = copy; clip_path = copy_path; while (status == CAIRO_STATUS_SUCCESS && clip_path) { status = _cairo_surface_fill (dst, CAIRO_OPERATOR_IN, &_cairo_pattern_white.base, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias, clip); clip_path = clip_path->prev; } copy->path = copy_path; _cairo_clip_destroy (copy); return status; } static cairo_status_t _cairo_path_fixed_add_box (cairo_path_fixed_t *path, const cairo_box_t *box, cairo_fixed_t fx, cairo_fixed_t fy) { cairo_status_t status; status = _cairo_path_fixed_move_to (path, box->p1.x + fx, box->p1.y + fy); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p1.y + fy); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p2.y + fy); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p1.x + fx, box->p2.y + fy); if (unlikely (status)) return status; return _cairo_path_fixed_close_path (path); } cairo_surface_t * _cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty) { cairo_surface_t *surface; cairo_status_t status; cairo_clip_t *copy, *region; cairo_clip_path_t *copy_path, *clip_path; if (clip->num_boxes) { cairo_path_fixed_t path; int i; surface = _cairo_surface_create_similar_solid (target, CAIRO_CONTENT_ALPHA, clip->extents.width, clip->extents.height, CAIRO_COLOR_TRANSPARENT); if (unlikely (surface->status)) return surface; _cairo_path_fixed_init (&path); status = CAIRO_STATUS_SUCCESS; for (i = 0; status == CAIRO_STATUS_SUCCESS && i < clip->num_boxes; i++) { status = _cairo_path_fixed_add_box (&path, &clip->boxes[i], -_cairo_fixed_from_int (clip->extents.x), -_cairo_fixed_from_int (clip->extents.y)); } if (status == CAIRO_STATUS_SUCCESS) status = _cairo_surface_fill (surface, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, &path, CAIRO_FILL_RULE_WINDING, 1., CAIRO_ANTIALIAS_DEFAULT, NULL); _cairo_path_fixed_fini (&path); if (unlikely (status)) { cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); } } else { surface = _cairo_surface_create_similar_solid (target, CAIRO_CONTENT_ALPHA, clip->extents.width, clip->extents.height, CAIRO_COLOR_WHITE); if (unlikely (surface->status)) return surface; } copy = _cairo_clip_copy_with_translation (clip, -clip->extents.x, -clip->extents.y); copy_path = copy->path; copy->path = NULL; region = copy; if (! _cairo_clip_is_region (copy)) region = _cairo_clip_copy_region (copy); status = CAIRO_STATUS_SUCCESS; clip_path = copy_path; while (status == CAIRO_STATUS_SUCCESS && clip_path) { status = _cairo_surface_fill (surface, CAIRO_OPERATOR_IN, &_cairo_pattern_white.base, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias, region); clip_path = clip_path->prev; } copy->path = copy_path; _cairo_clip_destroy (copy); if (region != copy) _cairo_clip_destroy (region); if (unlikely (status)) { cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); } *tx = clip->extents.x; *ty = clip->extents.y; return surface; } cairo_surface_t * _cairo_clip_get_image (const cairo_clip_t *clip, cairo_surface_t *target, const cairo_rectangle_int_t *extents) { cairo_surface_t *surface; cairo_status_t status; surface = cairo_surface_create_similar_image (target, CAIRO_FORMAT_A8, extents->width, extents->height); if (unlikely (surface->status)) return surface; status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE, &_cairo_pattern_white.base, NULL); if (likely (status == CAIRO_STATUS_SUCCESS)) status = _cairo_clip_combine_with_surface (clip, surface, extents->x, extents->y); if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } return surface; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip-tor-scan-converter.c�����������������0000664�0000000�0000000�00000142576�12710376503�0030236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* glitter-paths - polygon scan converter * * Copyright (c) 2008 M Joonas Pihlaja * Copyright (c) 2007 David Turner * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* This is the Glitter paths scan converter incorporated into cairo. * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 * of * * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths */ /* Glitter-paths is a stand alone polygon rasteriser derived from * David Turner's reimplementation of Tor Anderssons's 15x17 * supersampling rasteriser from the Apparition graphics library. The * main new feature here is cheaply choosing per-scan line between * doing fully analytical coverage computation for an entire row at a * time vs. using a supersampling approach. * * David Turner's code can be found at * * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 * * In particular this file incorporates large parts of ftgrays_tor10.h * from raster-comparison-20070813.tar.bz2 */ /* Overview * * A scan converter's basic purpose to take polygon edges and convert * them into an RLE compressed A8 mask. This one works in two phases: * gathering edges and generating spans. * * 1) As the user feeds the scan converter edges they are vertically * clipped and bucketted into a _polygon_ data structure. The edges * are also snapped from the user's coordinates to the subpixel grid * coordinates used during scan conversion. * * user * | * | edges * V * polygon buckets * * 2) Generating spans works by performing a vertical sweep of pixel * rows from top to bottom and maintaining an _active_list_ of edges * that intersect the row. From the active list the fill rule * determines which edges are the left and right edges of the start of * each span, and their contribution is then accumulated into a pixel * coverage list (_cell_list_) as coverage deltas. Once the coverage * deltas of all edges are known we can form spans of constant pixel * coverage by summing the deltas during a traversal of the cell list. * At the end of a pixel row the cell list is sent to a coverage * blitter for rendering to some target surface. * * The pixel coverages are computed by either supersampling the row * and box filtering a mono rasterisation, or by computing the exact * coverages of edges in the active list. The supersampling method is * used whenever some edge starts or stops within the row or there are * edge intersections in the row. * * polygon bucket for \ * current pixel row | * | | * | activate new edges | Repeat GRID_Y times if we * V \ are supersampling this row, * active list / or just once if we're computing * | | analytical coverage. * | coverage deltas | * V | * pixel coverage list / * | * V * coverage blitter */ #include "cairoint.h" #include "cairo-spans-private.h" #include "cairo-error-private.h" #include <assert.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <setjmp.h> /* The input coordinate scale and the rasterisation grid scales. */ #define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS #define GRID_X_BITS CAIRO_FIXED_FRAC_BITS #define GRID_Y 15 /* Set glitter up to use a cairo span renderer to do the coverage * blitting. */ struct pool; struct cell_list; /*------------------------------------------------------------------------- * glitter-paths.h */ /* "Input scaled" numbers are fixed precision reals with multiplier * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as * pixel scaled numbers. These get converted to the internal grid * scaled numbers as soon as possible. Internal overflow is possible * if GRID_X/Y inside glitter-paths.c is larger than * 1<<GLITTER_INPUT_BITS. */ #ifndef GLITTER_INPUT_BITS # define GLITTER_INPUT_BITS 8 #endif #define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS) typedef int glitter_input_scaled_t; /* Opaque type for scan converting. */ typedef struct glitter_scan_converter glitter_scan_converter_t; /*------------------------------------------------------------------------- * glitter-paths.c: Implementation internal types */ #include <stdlib.h> #include <string.h> #include <limits.h> /* All polygon coordinates are snapped onto a subsample grid. "Grid * scaled" numbers are fixed precision reals with multiplier GRID_X or * GRID_Y. */ typedef int grid_scaled_t; typedef int grid_scaled_x_t; typedef int grid_scaled_y_t; /* Default x/y scale factors. * You can either define GRID_X/Y_BITS to get a power-of-two scale * or define GRID_X/Y separately. */ #if !defined(GRID_X) && !defined(GRID_X_BITS) # define GRID_X_BITS 8 #endif #if !defined(GRID_Y) && !defined(GRID_Y_BITS) # define GRID_Y 15 #endif /* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ #ifdef GRID_X_BITS # define GRID_X (1 << GRID_X_BITS) #endif #ifdef GRID_Y_BITS # define GRID_Y (1 << GRID_Y_BITS) #endif /* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into * integer and fractional parts. The integer part is floored. */ #if defined(GRID_X_TO_INT_FRAC) /* do nothing */ #elif defined(GRID_X_BITS) # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) #else # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) #endif #define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ (i) = (t) / (m); \ (f) = (t) % (m); \ if ((f) < 0) { \ --(i); \ (f) += (m); \ } \ } while (0) #define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ (f) = (t) & ((1 << (b)) - 1); \ (i) = (t) >> (b); \ } while (0) /* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want * to be able to represent exactly areas of subpixel trapezoids whose * vertices are given in grid scaled coordinates. The scale factor * comes from needing to accurately represent the area 0.5*dx*dy of a * triangle with base dx and height dy in grid scaled numbers. */ typedef int grid_area_t; #define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ /* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ #if GRID_XY == 510 # define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) #elif GRID_XY == 255 # define GRID_AREA_TO_ALPHA(c) (c) #elif GRID_XY == 64 # define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) #elif GRID_XY == 128 # define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) #elif GRID_XY == 256 # define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) #elif GRID_XY == 15 # define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) #elif GRID_XY == 2*256*15 # define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) #else # define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) #endif #define UNROLL3(x) x x x struct quorem { int32_t quo; int32_t rem; }; /* Header for a chunk of memory in a memory pool. */ struct _pool_chunk { /* # bytes used in this chunk. */ size_t size; /* # bytes total in this chunk */ size_t capacity; /* Pointer to the previous chunk or %NULL if this is the sentinel * chunk in the pool header. */ struct _pool_chunk *prev_chunk; /* Actual data starts here. Well aligned for pointers. */ }; /* A memory pool. This is supposed to be embedded on the stack or * within some other structure. It may optionally be followed by an * embedded array from which requests are fulfilled until * malloc needs to be called to allocate a first real chunk. */ struct pool { /* Chunk we're allocating from. */ struct _pool_chunk *current; jmp_buf *jmp; /* Free list of previously allocated chunks. All have >= default * capacity. */ struct _pool_chunk *first_free; /* The default capacity of a chunk. */ size_t default_capacity; /* Header for the sentinel chunk. Directly following the pool * struct should be some space for embedded elements from which * the sentinel chunk allocates from. */ struct _pool_chunk sentinel[1]; }; /* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ struct edge *next; /* Current x coordinate while the edge is on the active * list. Initialised to the x coordinate of the top of the * edge. The quotient is in grid_scaled_x_t units and the * remainder is mod dy in grid_scaled_y_t units.*/ struct quorem x; /* Advance of the current x when moving down a subsample line. */ struct quorem dxdy; /* Advance of the current x when moving down a full pixel * row. Only initialised when the height of the edge is large * enough that there's a chance the edge could be stepped by a * full row's worth of subsample rows at a time. */ struct quorem dxdy_full; /* The clipped y of the top of the edge. */ grid_scaled_y_t ytop; /* y2-y1 after orienting the edge downwards. */ grid_scaled_y_t dy; /* Number of subsample rows remaining to scan convert of this * edge. */ grid_scaled_y_t height_left; /* Original sign of the edge: +1 for downwards, -1 for upwards * edges. */ int dir; int vertical; int clip; }; /* Number of subsample rows per y-bucket. Must be GRID_Y. */ #define EDGE_Y_BUCKET_HEIGHT GRID_Y #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { /* The vertical clip extents. */ grid_scaled_y_t ymin, ymax; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64]; struct { struct pool base[1]; struct edge embedded[32]; } edge_pool; }; /* A cell records the effect on pixel coverage of polygon edges * passing through a pixel. It contains two accumulators of pixel * coverage. * * Consider the effects of a polygon edge on the coverage of a pixel * it intersects and that of the following one. The coverage of the * following pixel is the height of the edge multiplied by the width * of the pixel, and the coverage of the pixel itself is the area of * the trapezoid formed by the edge and the right side of the pixel. * * +-----------------------+-----------------------+ * | | | * | | | * |_______________________|_______________________| * | \...................|.......................|\ * | \..................|.......................| | * | \.................|.......................| | * | \....covered.....|.......................| | * | \....area.......|.......................| } covered height * | \..............|.......................| | * |uncovered\.............|.......................| | * | area \............|.......................| | * |___________\...........|.......................|/ * | | | * | | | * | | | * +-----------------------+-----------------------+ * * Since the coverage of the following pixel will always be a multiple * of the width of the pixel, we can store the height of the covered * area instead. The coverage of the pixel itself is the total * coverage minus the area of the uncovered area to the left of the * edge. As it's faster to compute the uncovered area we only store * that and subtract it from the total coverage later when forming * spans to blit. * * The heights and areas are signed, with left edges of the polygon * having positive sign and right edges having negative sign. When * two edges intersect they swap their left/rightness so their * contribution above and below the intersection point must be * computed separately. */ struct cell { struct cell *next; int x; grid_area_t uncovered_area; grid_scaled_y_t covered_height; grid_scaled_y_t clipped_height; }; /* A cell list represents the scan line sparsely as cells ordered by * ascending x. It is geared towards scanning the cells in order * using an internal cursor. */ struct cell_list { /* Sentinel nodes */ struct cell head, tail; /* Cursor state for iterating through the cell list. */ struct cell *cursor; /* Cells in the cell list are owned by the cell list and are * allocated from this pool. */ struct { struct pool base[1]; struct cell embedded[32]; } cell_pool; }; struct cell_pair { struct cell *cell1; struct cell *cell2; }; /* The active list contains edges in the current scan line ordered by * the x-coordinate of the intercept of the edge and the scan line. */ struct active_list { /* Leftmost edge on the current scan line. */ struct edge *head; /* A lower bound on the height of the active edges is used to * estimate how soon some active edge ends. We can't advance the * scan conversion by a full pixel row if an edge ends somewhere * within it. */ grid_scaled_y_t min_height; }; struct glitter_scan_converter { struct polygon polygon[1]; struct active_list active[1]; struct cell_list coverages[1]; /* Clip box. */ grid_scaled_y_t ymin, ymax; }; /* Compute the floored division a/b. Assumes / and % perform symmetric * division. */ inline static struct quorem floored_divrem(int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } /* Compute the floored division (x*a)/b. Assumes / and % perform symmetric * division. */ static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } static struct _pool_chunk * _pool_chunk_init( struct _pool_chunk *p, struct _pool_chunk *prev_chunk, size_t capacity) { p->prev_chunk = prev_chunk; p->size = 0; p->capacity = capacity; return p; } static struct _pool_chunk * _pool_chunk_create(struct pool *pool, size_t size) { struct _pool_chunk *p; p = malloc(size + sizeof(struct _pool_chunk)); if (unlikely (NULL == p)) longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); return _pool_chunk_init(p, pool->current, size); } static void pool_init(struct pool *pool, jmp_buf *jmp, size_t default_capacity, size_t embedded_capacity) { pool->jmp = jmp; pool->current = pool->sentinel; pool->first_free = NULL; pool->default_capacity = default_capacity; _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); } static void pool_fini(struct pool *pool) { struct _pool_chunk *p = pool->current; do { while (NULL != p) { struct _pool_chunk *prev = p->prev_chunk; if (p != pool->sentinel) free(p); p = prev; } p = pool->first_free; pool->first_free = NULL; } while (NULL != p); } /* Satisfy an allocation by first allocating a new large enough chunk * and adding it to the head of the pool's chunk list. This function * is called as a fallback if pool_alloc() couldn't do a quick * allocation from the current chunk in the pool. */ static void * _pool_alloc_from_new_chunk( struct pool *pool, size_t size) { struct _pool_chunk *chunk; void *obj; size_t capacity; /* If the allocation is smaller than the default chunk size then * try getting a chunk off the free list. Force alloc of a new * chunk for large requests. */ capacity = size; chunk = NULL; if (size < pool->default_capacity) { capacity = pool->default_capacity; chunk = pool->first_free; if (chunk) { pool->first_free = chunk->prev_chunk; _pool_chunk_init(chunk, pool->current, chunk->capacity); } } if (NULL == chunk) chunk = _pool_chunk_create (pool, capacity); pool->current = chunk; obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } /* Allocate size bytes from the pool. The first allocated address * returned from a pool is aligned to sizeof(void*). Subsequent * addresses will maintain alignment as long as multiples of void* are * allocated. Returns the address of a new memory area or %NULL on * allocation failures. The pool retains ownership of the returned * memory. */ inline static void * pool_alloc (struct pool *pool, size_t size) { struct _pool_chunk *chunk = pool->current; if (size <= chunk->capacity - chunk->size) { void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } else { return _pool_alloc_from_new_chunk(pool, size); } } /* Relinquish all pool_alloced memory back to the pool. */ static void pool_reset (struct pool *pool) { /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; if (chunk != pool->sentinel) { while (chunk->prev_chunk != pool->sentinel) { chunk = chunk->prev_chunk; } chunk->prev_chunk = pool->first_free; pool->first_free = pool->current; } /* Reset the sentinel as the current chunk. */ pool->current = pool->sentinel; pool->sentinel->size = 0; } /* Rewinds the cell list's cursor to the beginning. After rewinding * we're good to cell_list_find() the cell any x coordinate. */ inline static void cell_list_rewind (struct cell_list *cells) { cells->cursor = &cells->head; } /* Rewind the cell list if its cursor has been advanced past x. */ inline static void cell_list_maybe_rewind (struct cell_list *cells, int x) { struct cell *tail = cells->cursor; if (tail->x > x) cell_list_rewind (cells); } static void cell_list_init(struct cell_list *cells, jmp_buf *jmp) { pool_init(cells->cell_pool.base, jmp, 256*sizeof(struct cell), sizeof(cells->cell_pool.embedded)); cells->tail.next = NULL; cells->tail.x = INT_MAX; cells->head.x = INT_MIN; cells->head.next = &cells->tail; cell_list_rewind (cells); } static void cell_list_fini(struct cell_list *cells) { pool_fini (cells->cell_pool.base); } /* Empty the cell list. This is called at the start of every pixel * row. */ inline static void cell_list_reset (struct cell_list *cells) { cell_list_rewind (cells); cells->head.next = &cells->tail; pool_reset (cells->cell_pool.base); } static struct cell * cell_list_alloc (struct cell_list *cells, struct cell *tail, int x) { struct cell *cell; cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); cell->next = tail->next; tail->next = cell; cell->x = x; cell->uncovered_area = 0; cell->covered_height = 0; cell->clipped_height = 0; return cell; } /* Find a cell at the given x-coordinate. Returns %NULL if a new cell * needed to be allocated but couldn't be. Cells must be found with * non-decreasing x-coordinate until the cell list is rewound using * cell_list_rewind(). Ownership of the returned cell is retained by * the cell list. */ inline static struct cell * cell_list_find (struct cell_list *cells, int x) { struct cell *tail = cells->cursor; while (1) { UNROLL3({ if (tail->next->x > x) break; tail = tail->next; }); } if (tail->x != x) tail = cell_list_alloc (cells, tail, x); return cells->cursor = tail; } /* Find two cells at x1 and x2. This is exactly equivalent * to * * pair.cell1 = cell_list_find(cells, x1); * pair.cell2 = cell_list_find(cells, x2); * * except with less function call overhead. */ inline static struct cell_pair cell_list_find_pair(struct cell_list *cells, int x1, int x2) { struct cell_pair pair; pair.cell1 = cells->cursor; while (1) { UNROLL3({ if (pair.cell1->next->x > x1) break; pair.cell1 = pair.cell1->next; }); } if (pair.cell1->x != x1) { struct cell *cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); cell->x = x1; cell->uncovered_area = 0; cell->covered_height = 0; cell->clipped_height = 0; cell->next = pair.cell1->next; pair.cell1->next = cell; pair.cell1 = cell; } pair.cell2 = pair.cell1; while (1) { UNROLL3({ if (pair.cell2->next->x > x2) break; pair.cell2 = pair.cell2->next; }); } if (pair.cell2->x != x2) { struct cell *cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); cell->uncovered_area = 0; cell->covered_height = 0; cell->clipped_height = 0; cell->x = x2; cell->next = pair.cell2->next; pair.cell2->next = cell; pair.cell2 = cell; } cells->cursor = pair.cell2; return pair; } /* Add a subpixel span covering [x1, x2) to the coverage cells. */ inline static void cell_list_add_subspan(struct cell_list *cells, grid_scaled_x_t x1, grid_scaled_x_t x2) { int ix1, fx1; int ix2, fx2; GRID_X_TO_INT_FRAC(x1, ix1, fx1); GRID_X_TO_INT_FRAC(x2, ix2, fx2); if (ix1 != ix2) { struct cell_pair p; p = cell_list_find_pair(cells, ix1, ix2); p.cell1->uncovered_area += 2*fx1; ++p.cell1->covered_height; p.cell2->uncovered_area -= 2*fx2; --p.cell2->covered_height; } else { struct cell *cell = cell_list_find(cells, ix1); cell->uncovered_area += 2*(fx1-fx2); } } /* Adds the analytical coverage of an edge crossing the current pixel * row to the coverage cells and advances the edge's x position to the * following row. * * This function is only called when we know that during this pixel row: * * 1) The relative order of all edges on the active list doesn't * change. In particular, no edges intersect within this row to pixel * precision. * * 2) No new edges start in this row. * * 3) No existing edges end mid-row. * * This function depends on being called with all edges from the * active list in the order they appear on the list (i.e. with * non-decreasing x-coordinate.) */ static void cell_list_render_edge( struct cell_list *cells, struct edge *edge, int sign) { grid_scaled_y_t y1, y2, dy; grid_scaled_x_t dx; int ix1, ix2; grid_scaled_x_t fx1, fx2; struct quorem x1 = edge->x; struct quorem x2 = x1; if (! edge->vertical) { x2.quo += edge->dxdy_full.quo; x2.rem += edge->dxdy_full.rem; if (x2.rem >= 0) { ++x2.quo; x2.rem -= edge->dy; } edge->x = x2; } GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); /* Edge is entirely within a column? */ if (ix1 == ix2) { /* We always know that ix1 is >= the cell list cursor in this * case due to the no-intersections precondition. */ struct cell *cell = cell_list_find(cells, ix1); cell->covered_height += sign*GRID_Y; cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; return; } /* Orient the edge left-to-right. */ dx = x2.quo - x1.quo; if (dx >= 0) { y1 = 0; y2 = GRID_Y; } else { int tmp; tmp = ix1; ix1 = ix2; ix2 = tmp; tmp = fx1; fx1 = fx2; fx2 = tmp; dx = -dx; sign = -sign; y1 = GRID_Y; y2 = 0; } dy = y2 - y1; /* Add coverage for all pixels [ix1,ix2] on this row crossed * by the edge. */ { struct cell_pair pair; struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); /* When rendering a previous edge on the active list we may * advance the cell list cursor past the leftmost pixel of the * current edge even though the two edges don't intersect. * e.g. consider two edges going down and rightwards: * * --\_+---\_+-----+-----+---- * \_ \_ | | * | \_ | \_ | | * | \_| \_| | * | \_ \_ | * ----+-----+-\---+-\---+---- * * The left edge touches cells past the starting cell of the * right edge. Fortunately such cases are rare. * * The rewinding is never necessary if the current edge stays * within a single column because we've checked before calling * this function that the active list order won't change. */ cell_list_maybe_rewind(cells, ix1); pair = cell_list_find_pair(cells, ix1, ix1+1); pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); pair.cell1->covered_height += sign*y.quo; y.quo += y1; if (ix1+1 < ix2) { struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); struct cell *cell = pair.cell2; ++ix1; do { grid_scaled_y_t y_skip = dydx_full.quo; y.rem += dydx_full.rem; if (y.rem >= dx) { ++y_skip; y.rem -= dx; } y.quo += y_skip; y_skip *= sign; cell->uncovered_area += y_skip*GRID_X; cell->covered_height += y_skip; ++ix1; cell = cell_list_find(cells, ix1); } while (ix1 != ix2); pair.cell2 = cell; } pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; pair.cell2->covered_height += sign*(y2 - y.quo); } } static void polygon_init (struct polygon *polygon, jmp_buf *jmp) { polygon->ymin = polygon->ymax = 0; polygon->y_buckets = polygon->y_buckets_embedded; pool_init (polygon->edge_pool.base, jmp, 8192 - sizeof (struct _pool_chunk), sizeof (polygon->edge_pool.embedded)); } static void polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); pool_fini (polygon->edge_pool.base); } /* Empties the polygon of all edges. The polygon is then prepared to * receive new edges and clip them to the vertical range * [ymin,ymax). */ static cairo_status_t polygon_reset (struct polygon *polygon, grid_scaled_y_t ymin, grid_scaled_y_t ymax) { unsigned h = ymax - ymin; unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, ymin); pool_reset(polygon->edge_pool.base); if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) goto bail_no_mem; /* even if you could, you wouldn't want to. */ if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); polygon->y_buckets = polygon->y_buckets_embedded; if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (num_buckets, sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) goto bail_no_mem; } memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); polygon->ymin = ymin; polygon->ymax = ymax; return CAIRO_STATUS_SUCCESS; bail_no_mem: polygon->ymin = 0; polygon->ymax = 0; return CAIRO_STATUS_NO_MEMORY; } static void _polygon_insert_edge_into_its_y_bucket( struct polygon *polygon, struct edge *e) { unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); struct edge **ptail = &polygon->y_buckets[ix]; e->next = *ptail; *ptail = e; } inline static void polygon_add_edge (struct polygon *polygon, const cairo_edge_t *edge, int clip) { struct edge *e; grid_scaled_x_t dx; grid_scaled_y_t dy; grid_scaled_y_t ytop, ybot; grid_scaled_y_t ymin = polygon->ymin; grid_scaled_y_t ymax = polygon->ymax; assert (edge->bottom > edge->top); if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) return; e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; e->dy = dy; e->dir = edge->dir; e->clip = clip; ytop = edge->top >= ymin ? edge->top : ymin; ybot = edge->bottom <= ymax ? edge->bottom : ymax; e->ytop = ytop; e->height_left = ybot - ytop; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } else { e->vertical = FALSE; e->dxdy = floored_divrem (dx, dy); if (ytop == edge->line.p1.y) { e->x.quo = edge->line.p1.x; e->x.rem = 0; } else { e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } if (e->height_left >= GRID_Y) { e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); } else { e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } } _polygon_insert_edge_into_its_y_bucket (polygon, e); e->x.rem -= dy; /* Bias the remainder for faster * edge advancement. */ } static void active_list_reset (struct active_list *active) { active->head = NULL; active->min_height = 0; } static void active_list_init(struct active_list *active) { active_list_reset(active); } /* * Merge two sorted edge lists. * Input: * - head_a: The head of the first list. * - head_b: The head of the second list; head_b cannot be NULL. * Output: * Returns the head of the merged list. * * Implementation notes: * To make it fast (in particular, to reduce to an insertion sort whenever * one of the two input lists only has a single element) we iterate through * a list until its head becomes greater than the head of the other list, * then we switch their roles. As soon as one of the two lists is empty, we * just attach the other one to the current list and exit. * Writes to memory are only needed to "switch" lists (as it also requires * attaching to the output list the list which we will be iterating next) and * to attach the last non-empty list. */ static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { struct edge *head, **next; int32_t x; if (head_a == NULL) return head_b; next = &head; if (head_a->x.quo <= head_b->x.quo) { head = head_a; } else { head = head_b; goto start_with_b; } do { x = head_b->x.quo; while (head_a != NULL && head_a->x.quo <= x) { next = &head_a->next; head_a = head_a->next; } *next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x.quo; while (head_b != NULL && head_b->x.quo <= x) { next = &head_b->next; head_b = head_b->next; } *next = head_a; if (head_b == NULL) return head; } while (1); } /* * Sort (part of) a list. * Input: * - list: The list to be sorted; list cannot be NULL. * - limit: Recursion limit. * Output: * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the * input list; if the input list has fewer elements, head_out be a sorted list * containing all the elements of the input list. * Returns the head of the list of unprocessed elements (NULL if the sorted list contains * all the elements of the input list). * * Implementation notes: * Special case single element list, unroll/inline the sorting of the first two elements. * Some tail recursion is used since we iterate on the bottom-up solution of the problem * (we start with a small sorted list and keep merging other lists of the same size to it). */ static struct edge * sort_edges (struct edge *list, unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; unsigned int i; head_other = list->next; /* Single element list -> return */ if (head_other == NULL) { *head_out = list; return NULL; } /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges): * - Initialize remaining to be the list containing the elements after the second in the input list. * - Initialize *head_out to be the sorted list containing the first two element. */ remaining = head_other->next; if (list->x.quo <= head_other->x.quo) { *head_out = list; /* list->next = head_other; */ /* The input list is already like this. */ head_other->next = NULL; } else { *head_out = head_other; head_other->next = list; list->next = NULL; } for (i = 0; i < level && remaining; i++) { /* Extract a sorted list of the same size as *head_out * (2^(i+1) elements) from the list of remaining elements. */ remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } /* *head_out now contains (at most) 2^(level+1) elements. */ return remaining; } /* Test if the edges on the active list can be safely advanced by a * full row without intersections or any edges ending. */ inline static int active_list_can_step_full_row (struct active_list *active) { const struct edge *e; int prev_x = INT_MIN; /* Recomputes the minimum height of all edges on the active * list if we have been dropping edges. */ if (active->min_height <= 0) { int min_height = INT_MAX; e = active->head; while (NULL != e) { if (e->height_left < min_height) min_height = e->height_left; e = e->next; } active->min_height = min_height; } if (active->min_height < GRID_Y) return 0; /* Check for intersections as no edges end during the next row. */ e = active->head; while (NULL != e) { struct quorem x = e->x; if (! e->vertical) { x.quo += e->dxdy_full.quo; x.rem += e->dxdy_full.rem; if (x.rem >= 0) ++x.quo; } if (x.quo <= prev_x) return 0; prev_x = x.quo; e = e->next; } return 1; } /* Merges edges on the given subpixel row from the polygon to the * active_list. */ inline static void active_list_merge_edges_from_polygon(struct active_list *active, struct edge **ptail, grid_scaled_y_t y, struct polygon *polygon) { /* Split off the edges on the current subrow and merge them into * the active list. */ int min_height = active->min_height; struct edge *subrow_edges = NULL; struct edge *tail = *ptail; do { struct edge *next = tail->next; if (y == tail->ytop) { tail->next = subrow_edges; subrow_edges = tail; if (tail->height_left < min_height) min_height = tail->height_left; *ptail = next; } else ptail = &tail->next; tail = next; } while (tail); if (subrow_edges) { sort_edges (subrow_edges, UINT_MAX, &subrow_edges); active->head = merge_sorted_edges (active->head, subrow_edges); active->min_height = min_height; } } /* Advance the edges on the active list by one subsample row by * updating their x positions. Drop edges from the list that end. */ inline static void active_list_substep_edges(struct active_list *active) { struct edge **cursor = &active->head; grid_scaled_x_t prev_x = INT_MIN; struct edge *unsorted = NULL; struct edge *edge = *cursor; do { UNROLL3({ struct edge *next; if (NULL == edge) break; next = edge->next; if (--edge->height_left) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } if (edge->x.quo < prev_x) { *cursor = next; edge->next = unsorted; unsorted = edge; } else { prev_x = edge->x.quo; cursor = &edge->next; } } else { *cursor = next; } edge = next; }) } while (1); if (unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); active->head = merge_sorted_edges (active->head, unsorted); } } inline static void apply_nonzero_fill_rule_for_subrow (struct active_list *active, struct cell_list *coverages) { struct edge *edge = active->head; int winding = 0; int xstart; int xend; cell_list_rewind (coverages); while (NULL != edge) { xstart = edge->x.quo; winding = edge->dir; while (1) { edge = edge->next; if (NULL == edge) { ASSERT_NOT_REACHED; return; } winding += edge->dir; if (0 == winding) { if (edge->next == NULL || edge->next->x.quo != edge->x.quo) break; } } xend = edge->x.quo; cell_list_add_subspan (coverages, xstart, xend); edge = edge->next; } } static void apply_evenodd_fill_rule_for_subrow (struct active_list *active, struct cell_list *coverages) { struct edge *edge = active->head; int xstart; int xend; cell_list_rewind (coverages); while (NULL != edge) { xstart = edge->x.quo; while (1) { edge = edge->next; if (NULL == edge) { ASSERT_NOT_REACHED; return; } if (edge->next == NULL || edge->next->x.quo != edge->x.quo) break; edge = edge->next; } xend = edge->x.quo; cell_list_add_subspan (coverages, xstart, xend); edge = edge->next; } } static void apply_nonzero_fill_rule_and_step_edges (struct active_list *active, struct cell_list *coverages) { struct edge **cursor = &active->head; struct edge *left_edge; left_edge = *cursor; while (NULL != left_edge) { struct edge *right_edge; int winding = left_edge->dir; left_edge->height_left -= GRID_Y; if (left_edge->height_left) cursor = &left_edge->next; else *cursor = left_edge->next; while (1) { right_edge = *cursor; if (NULL == right_edge) { cell_list_render_edge (coverages, left_edge, +1); return; } right_edge->height_left -= GRID_Y; if (right_edge->height_left) cursor = &right_edge->next; else *cursor = right_edge->next; winding += right_edge->dir; if (0 == winding) { if (right_edge->next == NULL || right_edge->next->x.quo != right_edge->x.quo) { break; } } if (! right_edge->vertical) { right_edge->x.quo += right_edge->dxdy_full.quo; right_edge->x.rem += right_edge->dxdy_full.rem; if (right_edge->x.rem >= 0) { ++right_edge->x.quo; right_edge->x.rem -= right_edge->dy; } } } cell_list_render_edge (coverages, left_edge, +1); cell_list_render_edge (coverages, right_edge, -1); left_edge = *cursor; } } static void apply_evenodd_fill_rule_and_step_edges (struct active_list *active, struct cell_list *coverages) { struct edge **cursor = &active->head; struct edge *left_edge; left_edge = *cursor; while (NULL != left_edge) { struct edge *right_edge; left_edge->height_left -= GRID_Y; if (left_edge->height_left) cursor = &left_edge->next; else *cursor = left_edge->next; while (1) { right_edge = *cursor; if (NULL == right_edge) { cell_list_render_edge (coverages, left_edge, +1); return; } right_edge->height_left -= GRID_Y; if (right_edge->height_left) cursor = &right_edge->next; else *cursor = right_edge->next; if (right_edge->next == NULL || right_edge->next->x.quo != right_edge->x.quo) { break; } if (! right_edge->vertical) { right_edge->x.quo += right_edge->dxdy_full.quo; right_edge->x.rem += right_edge->dxdy_full.rem; if (right_edge->x.rem >= 0) { ++right_edge->x.quo; right_edge->x.rem -= right_edge->dy; } } } cell_list_render_edge (coverages, left_edge, +1); cell_list_render_edge (coverages, right_edge, -1); left_edge = *cursor; } } static void _glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) { polygon_init(converter->polygon, jmp); active_list_init(converter->active); cell_list_init(converter->coverages, jmp); converter->ymin=0; converter->ymax=0; } static void _glitter_scan_converter_fini(glitter_scan_converter_t *converter) { polygon_fini(converter->polygon); cell_list_fini(converter->coverages); converter->ymin=0; converter->ymax=0; } static grid_scaled_t int_to_grid_scaled(int i, int scale) { /* Clamp to max/min representable scaled number. */ if (i >= 0) { if (i >= INT_MAX/scale) i = INT_MAX/scale; } else { if (i <= INT_MIN/scale) i = INT_MIN/scale; } return i*scale; } #define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) #define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) static cairo_status_t glitter_scan_converter_reset(glitter_scan_converter_t *converter, int ymin, int ymax) { cairo_status_t status; converter->ymin = 0; converter->ymax = 0; ymin = int_to_grid_scaled_y(ymin); ymax = int_to_grid_scaled_y(ymax); active_list_reset(converter->active); cell_list_reset(converter->coverages); status = polygon_reset(converter->polygon, ymin, ymax); if (status) return status; converter->ymin = ymin; converter->ymax = ymax; return CAIRO_STATUS_SUCCESS; } /* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) * These macros convert an input coordinate in the client's * device space to the rasterisation grid. */ /* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use * shifts if possible, and something saneish if not. */ #if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) #else # define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) #endif #if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) #else # define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) #endif #define INPUT_TO_GRID_general(in, out, grid_scale) do { \ long long tmp__ = (long long)(grid_scale) * (in); \ tmp__ >>= GLITTER_INPUT_BITS; \ (out) = tmp__; \ } while (0) static void glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, const cairo_edge_t *edge, int clip) { cairo_edge_t e; INPUT_TO_GRID_Y (edge->top, e.top); INPUT_TO_GRID_Y (edge->bottom, e.bottom); if (e.top >= e.bottom) return; /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); if (e.line.p1.y == e.line.p2.y) return; INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); e.dir = edge->dir; polygon_add_edge (converter->polygon, &e, clip); } static cairo_bool_t active_list_is_vertical (struct active_list *active) { struct edge *e; for (e = active->head; e != NULL; e = e->next) { if (! e->vertical) return FALSE; } return TRUE; } static void step_edges (struct active_list *active, int count) { struct edge **cursor = &active->head; struct edge *edge; for (edge = *cursor; edge != NULL; edge = *cursor) { edge->height_left -= GRID_Y * count; if (edge->height_left) cursor = &edge->next; else *cursor = edge->next; } } static cairo_status_t blit_coverages (struct cell_list *cells, cairo_span_renderer_t *renderer, struct pool *span_pool, int y, int height) { struct cell *cell = cells->head.next; int prev_x = -1; int cover = 0, last_cover = 0; int clip = 0; cairo_half_open_span_t *spans; unsigned num_spans; assert (cell != &cells->tail); /* Count number of cells remaining. */ { struct cell *next = cell; num_spans = 2; while (next->next) { next = next->next; ++num_spans; } num_spans = 2*num_spans; } /* Allocate enough spans for the row. */ pool_reset (span_pool); spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); num_spans = 0; /* Form the spans from the coverages and areas. */ for (; cell->next; cell = cell->next) { int x = cell->x; int area; if (x > prev_x && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); spans[num_spans].inverse = 0; last_cover = cover; ++num_spans; } cover += cell->covered_height*GRID_X*2; clip += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; if (area != last_cover) { spans[num_spans].x = x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); spans[num_spans].inverse = 0; last_cover = area; ++num_spans; } prev_x = x+1; } /* Dump them into the renderer. */ return renderer->render_rows (renderer, y, height, spans, num_spans); } static void glitter_scan_converter_render(glitter_scan_converter_t *converter, int nonzero_fill, cairo_span_renderer_t *span_renderer, struct pool *span_pool) { int i, j; int ymax_i = converter->ymax / GRID_Y; int ymin_i = converter->ymin / GRID_Y; int h = ymax_i - ymin_i; struct polygon *polygon = converter->polygon; struct cell_list *coverages = converter->coverages; struct active_list *active = converter->active; /* Render each pixel row. */ for (i = 0; i < h; i = j) { int do_full_step = 0; j = i + 1; /* Determine if we can ignore this row or use the full pixel * stepper. */ if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) { if (! active->head) { for (; j < h && ! polygon->y_buckets[j]; j++) ; continue; } do_full_step = active_list_can_step_full_row (active); } if (do_full_step) { /* Step by a full pixel row's worth. */ if (nonzero_fill) apply_nonzero_fill_rule_and_step_edges (active, coverages); else apply_evenodd_fill_rule_and_step_edges (active, coverages); if (active_list_is_vertical (active)) { while (j < h && polygon->y_buckets[j] == NULL && active->min_height >= 2*GRID_Y) { active->min_height -= GRID_Y; j++; } if (j != i + 1) step_edges (active, j - (i + 1)); } } else { grid_scaled_y_t suby; /* Subsample this row. */ for (suby = 0; suby < GRID_Y; suby++) { grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; if (polygon->y_buckets[i]) { active_list_merge_edges_from_polygon (active, &polygon->y_buckets[i], y, polygon); } if (nonzero_fill) apply_nonzero_fill_rule_for_subrow (active, coverages); else apply_evenodd_fill_rule_for_subrow (active, coverages); active_list_substep_edges(active); } } blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i); cell_list_reset (coverages); if (! active->head) active->min_height = INT_MAX; else active->min_height -= GRID_Y; } } struct _cairo_clip_tor_scan_converter { cairo_scan_converter_t base; glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_fill_rule_t clip_fill_rule; cairo_antialias_t clip_antialias; jmp_buf jmp; struct { struct pool base[1]; cairo_half_open_span_t embedded[32]; } span_pool; }; typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t; static void _cairo_clip_tor_scan_converter_destroy (void *converter) { cairo_clip_tor_scan_converter_t *self = converter; if (self == NULL) { return; } _glitter_scan_converter_fini (self->converter); pool_fini (self->span_pool.base); free(self); } static cairo_status_t _cairo_clip_tor_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_clip_tor_scan_converter_t *self = converter; cairo_status_t status; if ((status = setjmp (self->jmp))) return _cairo_scan_converter_set_error (self, _cairo_error (status)); glitter_scan_converter_render (self->converter, self->fill_rule == CAIRO_FILL_RULE_WINDING, renderer, self->span_pool.base); return CAIRO_STATUS_SUCCESS; } cairo_scan_converter_t * _cairo_clip_tor_scan_converter_create (cairo_clip_t *clip, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_clip_tor_scan_converter_t *self; cairo_polygon_t clipper; cairo_status_t status; int i; self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_clip_tor_scan_converter_destroy; self->base.generate = _cairo_clip_tor_scan_converter_generate; pool_init (self->span_pool.base, &self->jmp, 250 * sizeof(self->span_pool.embedded[0]), sizeof(self->span_pool.embedded)); _glitter_scan_converter_init (self->converter, &self->jmp); status = glitter_scan_converter_reset (self->converter, clip->extents.y, clip->extents.y + clip->extents.height); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; self->antialias = antialias; for (i = 0; i < polygon->num_edges; i++) glitter_scan_converter_add_edge (self->converter, &polygon->edges[i], FALSE); status = _cairo_clip_get_polygon (clip, &clipper, &self->clip_fill_rule, &self->clip_antialias); if (unlikely (status)) goto bail; for (i = 0; i < clipper.num_edges; i++) glitter_scan_converter_add_edge (self->converter, &clipper.edges[i], TRUE); _cairo_polygon_fini (&clipper); return &self->base; bail: self->base.destroy(&self->base); bail_nomem: return _cairo_scan_converter_create_in_error (status); } ����������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-clip.c������������������������������������0000664�0000000�0000000�00000052675�12710376503�0024505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" static freed_pool_t clip_path_pool; static freed_pool_t clip_pool; const cairo_clip_t __cairo_clip_all; static cairo_clip_path_t * _cairo_clip_path_create (cairo_clip_t *clip) { cairo_clip_path_t *clip_path; clip_path = _freed_pool_get (&clip_path_pool); if (unlikely (clip_path == NULL)) { clip_path = malloc (sizeof (cairo_clip_path_t)); if (unlikely (clip_path == NULL)) return NULL; } CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); clip_path->prev = clip->path; clip->path = clip_path; return clip_path; } cairo_clip_path_t * _cairo_clip_path_reference (cairo_clip_path_t *clip_path) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); _cairo_reference_count_inc (&clip_path->ref_count); return clip_path; } void _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) return; _cairo_path_fixed_fini (&clip_path->path); if (clip_path->prev != NULL) _cairo_clip_path_destroy (clip_path->prev); _freed_pool_put (&clip_path_pool, clip_path); } cairo_clip_t * _cairo_clip_create (void) { cairo_clip_t *clip; clip = _freed_pool_get (&clip_pool); if (unlikely (clip == NULL)) { clip = malloc (sizeof (cairo_clip_t)); if (unlikely (clip == NULL)) return NULL; } clip->extents = _cairo_unbounded_rectangle; clip->path = NULL; clip->boxes = NULL; clip->num_boxes = 0; clip->region = NULL; clip->is_region = FALSE; return clip; } void _cairo_clip_destroy (cairo_clip_t *clip) { if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return; if (clip->path != NULL) _cairo_clip_path_destroy (clip->path); if (clip->boxes != &clip->embedded_box) free (clip->boxes); cairo_region_destroy (clip->region); _freed_pool_put (&clip_pool, clip); } cairo_clip_t * _cairo_clip_copy (const cairo_clip_t *clip) { cairo_clip_t *copy; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return (cairo_clip_t *) clip; copy = _cairo_clip_create (); if (clip->path) copy->path = _cairo_clip_path_reference (clip->path); if (clip->num_boxes) { if (clip->num_boxes == 1) { copy->boxes = ©->embedded_box; } else { copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); if (unlikely (copy->boxes == NULL)) return _cairo_clip_set_all_clipped (copy); } memcpy (copy->boxes, clip->boxes, clip->num_boxes * sizeof (cairo_box_t)); copy->num_boxes = clip->num_boxes; } copy->extents = clip->extents; copy->region = cairo_region_reference (clip->region); copy->is_region = clip->is_region; return copy; } cairo_clip_t * _cairo_clip_copy_path (const cairo_clip_t *clip) { cairo_clip_t *copy; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return (cairo_clip_t *) clip; assert (clip->num_boxes); copy = _cairo_clip_create (); copy->extents = clip->extents; if (clip->path) copy->path = _cairo_clip_path_reference (clip->path); return copy; } cairo_clip_t * _cairo_clip_copy_region (const cairo_clip_t *clip) { cairo_clip_t *copy; int i; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return (cairo_clip_t *) clip; assert (clip->num_boxes); copy = _cairo_clip_create (); copy->extents = clip->extents; if (clip->num_boxes == 1) { copy->boxes = ©->embedded_box; } else { copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); if (unlikely (copy->boxes == NULL)) return _cairo_clip_set_all_clipped (copy); } for (i = 0; i < clip->num_boxes; i++) { copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x); copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y); copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x); copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y); } copy->num_boxes = clip->num_boxes; copy->region = cairo_region_reference (clip->region); copy->is_region = TRUE; return copy; } cairo_clip_t * _cairo_clip_intersect_path (cairo_clip_t *clip, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_clip_path_t *clip_path; cairo_status_t status; cairo_rectangle_int_t extents; cairo_box_t box; if (_cairo_clip_is_all_clipped (clip)) return clip; /* catch the empty clip path */ if (_cairo_path_fixed_fill_is_empty (path)) return _cairo_clip_set_all_clipped (clip); if (_cairo_path_fixed_is_box (path, &box)) { if (antialias == CAIRO_ANTIALIAS_NONE) { box.p1.x = _cairo_fixed_round_down (box.p1.x); box.p1.y = _cairo_fixed_round_down (box.p1.y); box.p2.x = _cairo_fixed_round_down (box.p2.x); box.p2.y = _cairo_fixed_round_down (box.p2.y); } return _cairo_clip_intersect_box (clip, &box); } if (_cairo_path_fixed_fill_is_rectilinear (path)) return _cairo_clip_intersect_rectilinear_path (clip, path, fill_rule, antialias); _cairo_path_fixed_approximate_clip_extents (path, &extents); if (extents.width == 0 || extents.height == 0) return _cairo_clip_set_all_clipped (clip); clip = _cairo_clip_intersect_rectangle (clip, &extents); if (_cairo_clip_is_all_clipped (clip)) return clip; clip_path = _cairo_clip_path_create (clip); if (unlikely (clip_path == NULL)) return _cairo_clip_set_all_clipped (clip); status = _cairo_path_fixed_init_copy (&clip_path->path, path); if (unlikely (status)) return _cairo_clip_set_all_clipped (clip); clip_path->fill_rule = fill_rule; clip_path->tolerance = tolerance; clip_path->antialias = antialias; if (clip->region) { cairo_region_destroy (clip->region); clip->region = NULL; } clip->is_region = FALSE; return clip; } static cairo_clip_t * _cairo_clip_intersect_clip_path (cairo_clip_t *clip, const cairo_clip_path_t *clip_path) { if (clip_path->prev) clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev); return _cairo_clip_intersect_path (clip, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias); } cairo_clip_t * _cairo_clip_intersect_clip (cairo_clip_t *clip, const cairo_clip_t *other) { if (_cairo_clip_is_all_clipped (clip)) return clip; if (other == NULL) return clip; if (clip == NULL) return _cairo_clip_copy (other); if (_cairo_clip_is_all_clipped (other)) return _cairo_clip_set_all_clipped (clip); if (! _cairo_rectangle_intersect (&clip->extents, &other->extents)) return _cairo_clip_set_all_clipped (clip); if (other->num_boxes) { cairo_boxes_t boxes; _cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes); clip = _cairo_clip_intersect_boxes (clip, &boxes); } if (! _cairo_clip_is_all_clipped (clip)) { if (other->path) { if (clip->path == NULL) clip->path = _cairo_clip_path_reference (other->path); else clip = _cairo_clip_intersect_clip_path (clip, other->path); } } if (clip->region) { cairo_region_destroy (clip->region); clip->region = NULL; } clip->is_region = FALSE; return clip; } cairo_bool_t _cairo_clip_equal (const cairo_clip_t *clip_a, const cairo_clip_t *clip_b) { const cairo_clip_path_t *cp_a, *cp_b; /* are both all-clipped or no-clip? */ if (clip_a == clip_b) return TRUE; /* or just one of them? */ if (clip_a == NULL || clip_b == NULL || _cairo_clip_is_all_clipped (clip_a) || _cairo_clip_is_all_clipped (clip_b)) { return FALSE; } /* We have a pair of normal clips, check their contents */ if (clip_a->num_boxes != clip_b->num_boxes) return FALSE; if (memcmp (clip_a->boxes, clip_b->boxes, sizeof (cairo_box_t) * clip_a->num_boxes)) return FALSE; cp_a = clip_a->path; cp_b = clip_b->path; while (cp_a && cp_b) { if (cp_a == cp_b) return TRUE; /* XXX compare reduced polygons? */ if (cp_a->antialias != cp_b->antialias) return FALSE; if (cp_a->tolerance != cp_b->tolerance) return FALSE; if (cp_a->fill_rule != cp_b->fill_rule) return FALSE; if (! _cairo_path_fixed_equal (&cp_a->path, &cp_b->path)) return FALSE; cp_a = cp_a->prev; cp_b = cp_b->prev; } return cp_a == NULL && cp_b == NULL; } static cairo_clip_t * _cairo_clip_path_copy_with_translation (cairo_clip_t *clip, cairo_clip_path_t *other_path, int fx, int fy) { cairo_status_t status; cairo_clip_path_t *clip_path; if (other_path->prev != NULL) clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev, fx, fy); if (_cairo_clip_is_all_clipped (clip)) return clip; clip_path = _cairo_clip_path_create (clip); if (unlikely (clip_path == NULL)) return _cairo_clip_set_all_clipped (clip); status = _cairo_path_fixed_init_copy (&clip_path->path, &other_path->path); if (unlikely (status)) return _cairo_clip_set_all_clipped (clip); _cairo_path_fixed_translate (&clip_path->path, fx, fy); clip_path->fill_rule = other_path->fill_rule; clip_path->tolerance = other_path->tolerance; clip_path->antialias = other_path->antialias; return clip; } cairo_clip_t * _cairo_clip_translate (cairo_clip_t *clip, int tx, int ty) { int fx, fy, i; cairo_clip_path_t *clip_path; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return clip; if (tx == 0 && ty == 0) return clip; fx = _cairo_fixed_from_int (tx); fy = _cairo_fixed_from_int (ty); for (i = 0; i < clip->num_boxes; i++) { clip->boxes[i].p1.x += fx; clip->boxes[i].p2.x += fx; clip->boxes[i].p1.y += fy; clip->boxes[i].p2.y += fy; } clip->extents.x += tx; clip->extents.y += ty; if (clip->path == NULL) return clip; clip_path = clip->path; clip->path = NULL; clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy); _cairo_clip_path_destroy (clip_path); return clip; } static cairo_status_t _cairo_path_fixed_add_box (cairo_path_fixed_t *path, const cairo_box_t *box) { cairo_status_t status; status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); if (unlikely (status)) return status; return _cairo_path_fixed_close_path (path); } static cairo_status_t _cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path, const cairo_boxes_t *boxes) { cairo_status_t status; const struct _cairo_boxes_chunk *chunk; int i; _cairo_path_fixed_init (path); if (boxes->num_boxes == 0) return CAIRO_STATUS_SUCCESS; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_path_fixed_add_box (path, &chunk->base[i]); if (unlikely (status)) { _cairo_path_fixed_fini (path); return status; } } } return CAIRO_STATUS_SUCCESS; } static cairo_clip_t * _cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip, const cairo_clip_path_t *clip_path, const cairo_matrix_t *m) { cairo_path_fixed_t path; if (clip_path->prev) clip = _cairo_clip_intersect_clip_path_transformed (clip, clip_path->prev, m); if (_cairo_path_fixed_init_copy (&path, &clip_path->path)) return _cairo_clip_set_all_clipped (clip); _cairo_path_fixed_transform (&path, m); clip = _cairo_clip_intersect_path (clip, &path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias); _cairo_path_fixed_fini (&path); return clip; } cairo_clip_t * _cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m) { cairo_clip_t *copy; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return clip; if (_cairo_matrix_is_translation (m)) return _cairo_clip_translate (clip, m->x0, m->y0); copy = _cairo_clip_create (); if (clip->num_boxes) { cairo_path_fixed_t path; cairo_boxes_t boxes; _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); _cairo_path_fixed_init_from_boxes (&path, &boxes); _cairo_path_fixed_transform (&path, m); copy = _cairo_clip_intersect_path (copy, &path, CAIRO_FILL_RULE_WINDING, 0.1, CAIRO_ANTIALIAS_DEFAULT); _cairo_path_fixed_fini (&path); } if (clip->path) copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m); _cairo_clip_destroy (clip); return copy; } cairo_clip_t * _cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty) { cairo_clip_t *copy; int fx, fy, i; if (clip == NULL || _cairo_clip_is_all_clipped (clip)) return (cairo_clip_t *)clip; if (tx == 0 && ty == 0) return _cairo_clip_copy (clip); copy = _cairo_clip_create (); if (copy == NULL) return _cairo_clip_set_all_clipped (copy); fx = _cairo_fixed_from_int (tx); fy = _cairo_fixed_from_int (ty); if (clip->num_boxes) { if (clip->num_boxes == 1) { copy->boxes = ©->embedded_box; } else { copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); if (unlikely (copy->boxes == NULL)) return _cairo_clip_set_all_clipped (copy); } for (i = 0; i < clip->num_boxes; i++) { copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx; copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx; copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy; copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy; } copy->num_boxes = clip->num_boxes; } copy->extents = clip->extents; copy->extents.x += tx; copy->extents.y += ty; if (clip->path == NULL) return copy; return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy); } cairo_bool_t _cairo_clip_contains_extents (const cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { const cairo_rectangle_int_t *rect; rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; return _cairo_clip_contains_rectangle (clip, rect); } void _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip) { int i; if (clip == NULL) { fprintf (stream, "no clip\n"); return; } if (_cairo_clip_is_all_clipped (clip)) { fprintf (stream, "clip: all-clipped\n"); return; } fprintf (stream, "clip:\n"); fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d", clip->extents.x, clip->extents.y, clip->extents.width, clip->extents.height, clip->is_region); fprintf (stream, " num_boxes = %d\n", clip->num_boxes); for (i = 0; i < clip->num_boxes; i++) { fprintf (stream, " [%d] = (%f, %f), (%f, %f)\n", i, _cairo_fixed_to_double (clip->boxes[i].p1.x), _cairo_fixed_to_double (clip->boxes[i].p1.y), _cairo_fixed_to_double (clip->boxes[i].p2.x), _cairo_fixed_to_double (clip->boxes[i].p2.y)); } if (clip->path) { cairo_clip_path_t *clip_path = clip->path; do { fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ", clip_path->antialias, clip_path->tolerance, clip_path->fill_rule); _cairo_debug_print_path (stream, &clip_path->path); fprintf (stream, "\n"); } while ((clip_path = clip_path->prev) != NULL); } } const cairo_rectangle_int_t * _cairo_clip_get_extents (const cairo_clip_t *clip) { if (clip == NULL) return &_cairo_unbounded_rectangle; if (_cairo_clip_is_all_clipped (clip)) return &_cairo_empty_rectangle; return &clip->extents; } const cairo_rectangle_list_t _cairo_rectangles_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; static const cairo_rectangle_list_t _cairo_rectangles_not_representable = { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 }; static cairo_bool_t _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, cairo_rectangle_int_t *clip_rect, cairo_rectangle_t *user_rect) { cairo_bool_t is_tight; double x1 = clip_rect->x; double y1 = clip_rect->y; double x2 = clip_rect->x + (int) clip_rect->width; double y2 = clip_rect->y + (int) clip_rect->height; _cairo_gstate_backend_to_user_rectangle (gstate, &x1, &y1, &x2, &y2, &is_tight); user_rect->x = x1; user_rect->y = y1; user_rect->width = x2 - x1; user_rect->height = y2 - y1; return is_tight; } cairo_rectangle_list_t * _cairo_rectangle_list_create_in_error (cairo_status_t status) { cairo_rectangle_list_t *list; if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; list = malloc (sizeof (*list)); if (unlikely (list == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } list->status = status; list->rectangles = NULL; list->num_rectangles = 0; return list; } cairo_rectangle_list_t * _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) { #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)) cairo_rectangle_list_t *list; cairo_rectangle_t *rectangles = NULL; cairo_region_t *region = NULL; int n_rects = 0; int i; if (clip == NULL) return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); if (_cairo_clip_is_all_clipped (clip)) goto DONE; if (! _cairo_clip_is_region (clip)) return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); region = _cairo_clip_get_region (clip); if (region == NULL) return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); n_rects = cairo_region_num_rectangles (region); if (n_rects) { rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); if (unlikely (rectangles == NULL)) { return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_rects; ++i) { cairo_rectangle_int_t clip_rect; cairo_region_get_rectangle (region, i, &clip_rect); if (! _cairo_clip_int_rect_to_user (gstate, &clip_rect, &rectangles[i])) { free (rectangles); return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); } } } DONE: list = malloc (sizeof (cairo_rectangle_list_t)); if (unlikely (list == NULL)) { free (rectangles); return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); } list->status = CAIRO_STATUS_SUCCESS; list->rectangles = rectangles; list->num_rectangles = n_rects; return list; #undef ERROR_LIST } /** * cairo_rectangle_list_destroy: * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list() * * Unconditionally frees @rectangle_list and all associated * references. After this call, the @rectangle_list pointer must not * be dereferenced. * * Since: 1.4 **/ void cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) { if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil || rectangle_list == &_cairo_rectangles_not_representable) return; free (rectangle_list->rectangles); free (rectangle_list); } void _cairo_clip_reset_static_data (void) { _freed_pool_reset (&clip_path_pool); _freed_pool_reset (&clip_pool); } �������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-context-private.h��������������������0000664�0000000�0000000�00000003535�12710376503�0027630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #ifndef CAIRO_COGL_CONTEXT_PRIVATE_H #define CAIRO_COGL_CONTEXT_PRIVATE_H #include "cairo-default-context-private.h" #include "cairo-cogl-private.h" typedef struct _cairo_cogl_context { cairo_default_context_t base; cairo_cogl_device_t *dev; int path_ctm_age; cairo_path_fixed_t user_path; cairo_bool_t path_is_rectangle; double x, y, width, height; } cairo_cogl_context_t; cairo_t * _cairo_cogl_context_create (void *target); #endif /* CAIRO_COGL_CONTEXT_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-context.c����������������������������0000664�0000000�0000000�00000056065�12710376503�0026161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ /* so long as we can verify that the ctm doesn't change multiple times * during the construction of a path we can build a shadow * #cairo_path_fixed_t in user coordinates that we can use to create a * hash value for caching tessellations of that path. * * We need to hook into all the points where the ctm can be changed * so we can bump a cr->path_ctm_age counter. * * We need to hook into all the points where the path can be modified * so we can catch the start of a path and reset the cr->path_ctm_age * counter at that point. * * When a draw operation is hit we can then check that the * path_ctm_age == 0 and if so we create a hash of the path. * * We use this hash to lookup a #cairo_cogl_path_meta_t struct which * may contain tessellated triangles for the path or may just contain * a count of how many times the path has been re-seen (we only cache * tessellated triangles if there is evidence that the path is being * used multiple times because there is a cost involved in allocating * a separate buffer for the triangles). */ #include "cairoint.h" #include "cairo-cogl-context-private.h" #include "cairo-freed-pool-private.h" #include "cairo-arc-private.h" #include "cairo-path-fixed-private.h" #include <glib.h> static freed_pool_t context_pool; void _cairo_cogl_context_reset_static_data (void) { _freed_pool_reset (&context_pool); } static cairo_status_t _cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr, double x, double y, double width, double height) { cairo_status_t status; status = cr->dev->backend_parent.rectangle (cr, x, y, width, height); if (unlikely (status)) return status; return _cairo_cogl_path_fixed_rectangle (&cr->user_path, _cairo_fixed_from_double (x), _cairo_fixed_from_double (y), _cairo_fixed_from_double (width), _cairo_fixed_from_double (height)); } /* The idea here is that we have a simplified way of tracking rectangle paths * because rectangles are perhaps the most common shape drawn with cairo. * * Basically we have a speculative store for a rectangle path that doesn't * need to use the #cairo_path_fixed_t api to describe a rectangle in terms of * (move_to,rel_line_to,rel_line_to,_rel_line_to,close) because if you profile * heavy rectangle drawing with Cairo that process can be overly expensive. * * If the user asks to add more than just a rectangle to their current path * then we "flush" any speculative rectangle stored into the current path * before continuing to append their operations. * * In addition to the speculative store cairo-cogl also has a fast-path * fill_rectangle drawing operation that further aims to minimize the cost * of drawing rectangles. */ static cairo_status_t _flush_cr_rectangle (cairo_cogl_context_t *cr) { if (!cr->path_is_rectangle) return CAIRO_STATUS_SUCCESS; cr->path_is_rectangle = FALSE; return _cairo_cogl_context_rectangle_real (cr, cr->x, cr->y, cr->width, cr->height); } static cairo_status_t _cairo_cogl_context_restore (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.restore (abstract_cr); } static cairo_status_t _cairo_cogl_context_translate (void *abstract_cr, double tx, double ty) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.translate (abstract_cr, tx, ty); } static cairo_status_t _cairo_cogl_context_scale (void *abstract_cr, double sx, double sy) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.scale (abstract_cr, sx, sy); } static cairo_status_t _cairo_cogl_context_rotate (void *abstract_cr, double theta) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.rotate (abstract_cr, theta); } static cairo_status_t _cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.transform (abstract_cr, matrix); } static cairo_status_t _cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.set_matrix (abstract_cr, matrix); } static cairo_status_t _cairo_cogl_context_set_identity_matrix (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } cr->path_ctm_age++; return cr->dev->backend_parent.set_identity_matrix (abstract_cr); } static cairo_status_t _cairo_cogl_context_new_path (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.new_path (abstract_cr); if (unlikely (status)) return status; _cairo_path_fixed_fini (&cr->user_path); _cairo_path_fixed_init (&cr->user_path); cr->path_is_rectangle = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_new_sub_path (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.new_sub_path (abstract_cr); if (unlikely (status)) return status; _cairo_path_fixed_new_sub_path (&cr->user_path); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_move_to (void *abstract_cr, double x, double y) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t x_fixed, y_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.move_to (abstract_cr, x, y); if (unlikely (status)) return status; x_fixed = _cairo_fixed_from_double (x); y_fixed = _cairo_fixed_from_double (y); return _cairo_path_fixed_move_to (&cr->user_path, x_fixed, y_fixed); } static cairo_status_t _cairo_cogl_context_line_to (void *abstract_cr, double x, double y) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t x_fixed, y_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.line_to (abstract_cr, x, y); if (unlikely (status)) return status; x_fixed = _cairo_fixed_from_double (x); y_fixed = _cairo_fixed_from_double (y); if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; return _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed); } static cairo_status_t _cairo_cogl_context_curve_to (void *abstract_cr, double x1, double y1, double x2, double y2, double x3, double y3) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t x1_fixed, y1_fixed; cairo_fixed_t x2_fixed, y2_fixed; cairo_fixed_t x3_fixed, y3_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3); if (unlikely (status)) return status; x1_fixed = _cairo_fixed_from_double (x1); y1_fixed = _cairo_fixed_from_double (y1); x2_fixed = _cairo_fixed_from_double (x2); y2_fixed = _cairo_fixed_from_double (y2); x3_fixed = _cairo_fixed_from_double (x3); y3_fixed = _cairo_fixed_from_double (y3); if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; return _cairo_path_fixed_curve_to (&cr->user_path, x1_fixed, y1_fixed, x2_fixed, y2_fixed, x3_fixed, y3_fixed); } static cairo_status_t _cairo_cogl_context_arc (void *abstract_cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward); if (unlikely (status)) return status; if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; /* Do nothing, successfully, if radius is <= 0 */ if (radius <= 0.0) { cairo_fixed_t x_fixed, y_fixed; x_fixed = _cairo_fixed_from_double (xc); y_fixed = _cairo_fixed_from_double (yc); status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } status = _cairo_cogl_context_line_to (cr, xc + radius * cos (angle1), yc + radius * sin (angle1)); if (unlikely (status)) return status; if (forward) _cairo_arc_path (&cr->base.base, xc, yc, radius, angle1, angle2); else _cairo_arc_path_negative (&cr->base.base, xc, yc, radius, angle1, angle2); return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */ } static cairo_status_t _cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t dx_fixed, dy_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.rel_move_to (abstract_cr, dx, dy); if (unlikely (status)) return status; dx_fixed = _cairo_fixed_from_double (dx); dy_fixed = _cairo_fixed_from_double (dy); return _cairo_path_fixed_rel_move_to (&cr->user_path, dx_fixed, dy_fixed); } static cairo_status_t _cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t dx_fixed, dy_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.rel_line_to (abstract_cr, dx, dy); if (unlikely (status)) return status; dx_fixed = _cairo_fixed_from_double (dx); dy_fixed = _cairo_fixed_from_double (dy); if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; return _cairo_path_fixed_rel_line_to (&cr->user_path, dx_fixed, dy_fixed); } static cairo_status_t _cairo_cogl_context_rel_curve_to (void *abstract_cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_fixed_t dx1_fixed, dy1_fixed; cairo_fixed_t dx2_fixed, dy2_fixed; cairo_fixed_t dx3_fixed, dy3_fixed; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3); if (unlikely (status)) return status; dx1_fixed = _cairo_fixed_from_double (dx1); dy1_fixed = _cairo_fixed_from_double (dy1); dx2_fixed = _cairo_fixed_from_double (dx2); dy2_fixed = _cairo_fixed_from_double (dy2); dx3_fixed = _cairo_fixed_from_double (dx3); dy3_fixed = _cairo_fixed_from_double (dy3); if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; return _cairo_path_fixed_rel_curve_to (&cr->user_path, dx1_fixed, dy1_fixed, dx2_fixed, dy2_fixed, dx3_fixed, dy3_fixed); } #if 0 static cairo_status_t _cairo_cogl_context_arc_to (void *abstract_cr, double x1, double y1, double x2, double y2, double radius) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius); if (unlikely (status)) return status; #warning "FIXME" } static cairo_status_t _cairo_cogl_rel_arc_to (void *cr, double dx1, double dy1, double dx2, double dy2, double radius) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius); if (unlikely (status)) return status; #warning "FIXME" } #endif static cairo_status_t _cairo_cogl_context_close_path (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; if (cr->path_is_rectangle) { status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } status = cr->dev->backend_parent.close_path (abstract_cr); if (unlikely (status)) return status; if (cr->user_path.buf.base.num_ops == 0) cr->path_ctm_age = 0; return _cairo_path_fixed_close_path (&cr->user_path); } static cairo_status_t _cairo_cogl_context_rectangle (void *abstract_cr, double x, double y, double width, double height) { cairo_cogl_context_t *cr = abstract_cr; if (cr->user_path.buf.base.num_ops == 0) { cr->path_ctm_age = 0; #if 1 /* XXX: Since drawing rectangles is so common we have a * fast-path for drawing a single rectangle. */ cr->x = x; cr->y = y; cr->width = width; cr->height = height; cr->path_is_rectangle = TRUE; return CAIRO_STATUS_SUCCESS; #endif } if (cr->path_is_rectangle) { cairo_status_t status = _flush_cr_rectangle (cr); if (unlikely (status)) return status; } return _cairo_cogl_context_rectangle_real (cr, x, y, width, height); } /* Since the surface backend drawing operator functions don't get * passed the current #cairo_t context we don't have a good way * to get our user-coordinates path into our surface operator * functions. * * For now we use this function to set side band data on the surface * itself. */ static void _cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface, cairo_cogl_context_t *cr) { if (cr->path_ctm_age <= 1) { surface->user_path = &cr->user_path; surface->ctm = &cr->base.gstate->ctm; surface->ctm_inverse = &cr->base.gstate->ctm_inverse; surface->path_is_rectangle = cr->path_is_rectangle; if (surface->path_is_rectangle) { surface->path_rectangle_x = cr->x; surface->path_rectangle_y = cr->y; surface->path_rectangle_width = cr->width; surface->path_rectangle_height = cr->height; } } else { surface->user_path = NULL; surface->path_is_rectangle = FALSE; } } static cairo_status_t _cairo_cogl_context_fill (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target; if (cr->path_is_rectangle) { status = _cairo_cogl_surface_fill_rectangle (cr->base.gstate->target, cr->base.gstate->op, cr->base.gstate->source, cr->x, cr->y, cr->width, cr->height, &cr->base.gstate->ctm, cr->base.gstate->clip); if (status == CAIRO_STATUS_SUCCESS) goto DONE; _flush_cr_rectangle (cr); } _cairo_cogl_surface_set_side_band_state (surface, cr); status = cr->dev->backend_parent.fill (abstract_cr); if (unlikely (status)) return status; DONE: _cairo_path_fixed_fini (&cr->user_path); _cairo_path_fixed_init (&cr->user_path); cr->path_is_rectangle = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_fill_preserve (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target; _cairo_cogl_surface_set_side_band_state (surface, cr); status = cr->dev->backend_parent.fill_preserve (abstract_cr); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_stroke (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target; _cairo_cogl_surface_set_side_band_state (surface, cr); status = cr->dev->backend_parent.stroke (abstract_cr); if (unlikely (status)) return status; _cairo_path_fixed_fini (&cr->user_path); _cairo_path_fixed_init (&cr->user_path); cr->path_is_rectangle = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_stroke_preserve (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target; _cairo_cogl_surface_set_side_band_state (surface, cr); status = cr->dev->backend_parent.stroke_preserve (abstract_cr); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_context_clip (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; cairo_status_t status; status = cr->dev->backend_parent.clip (abstract_cr); if (unlikely (status)) return status; _cairo_path_fixed_fini (&cr->user_path); _cairo_path_fixed_init (&cr->user_path); cr->path_is_rectangle = FALSE; return CAIRO_STATUS_SUCCESS; } static void _cairo_cogl_context_destroy (void *abstract_cr) { cairo_cogl_context_t *cr = abstract_cr; _cairo_default_context_fini (&cr->base); _cairo_path_fixed_fini (&cr->user_path); /* mark the context as invalid to protect against misuse */ cr->base.base.status = CAIRO_STATUS_NULL_POINTER; _freed_pool_put (&context_pool, cr); } /* We want to hook into the frontend of the path construction APIs so * we can build up a path description in user coordinates instead of * backend coordinates so that we can recognize user coordinate * rectangles and so we can hash a user path independent of its * transform. (With some care to catch unusual cases where the ctm * changes mid-path) */ cairo_t * _cairo_cogl_context_create (void *target) { cairo_cogl_surface_t *surface = target; cairo_cogl_context_t *cr; cairo_status_t status; cr = _freed_pool_get (&context_pool); if (unlikely (cr == NULL)) { cr = malloc (sizeof (cairo_cogl_context_t)); if (unlikely (cr == NULL)) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } status = _cairo_default_context_init (&cr->base, target); if (unlikely (status)) { _freed_pool_put (&context_pool, cr); return _cairo_create_in_error (status); } cr->dev = (cairo_cogl_device_t *)surface->base.device; if (unlikely (cr->dev->backend_vtable_initialized == FALSE)) { cairo_backend_t *backend = &cr->dev->backend; memcpy (backend, cr->base.base.backend, sizeof (cairo_backend_t)); memcpy (&cr->dev->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t)); backend->destroy = _cairo_cogl_context_destroy; backend->restore = _cairo_cogl_context_restore; backend->translate = _cairo_cogl_context_translate; backend->scale = _cairo_cogl_context_scale; backend->rotate = _cairo_cogl_context_rotate; backend->transform = _cairo_cogl_context_transform; backend->set_matrix = _cairo_cogl_context_set_matrix; backend->set_identity_matrix = _cairo_cogl_context_set_identity_matrix; backend->new_path = _cairo_cogl_context_new_path; backend->new_sub_path = _cairo_cogl_context_new_sub_path; backend->move_to = _cairo_cogl_context_move_to; backend->rel_move_to = _cairo_cogl_context_rel_move_to; backend->line_to = _cairo_cogl_context_line_to; backend->rel_line_to = _cairo_cogl_context_rel_line_to; backend->curve_to = _cairo_cogl_context_curve_to; backend->rel_curve_to = _cairo_cogl_context_rel_curve_to; #if 0 backend->arc_to = _cairo_cogl_context_arc_to; backend->rel_arc_to = _cairo_cogl_context_rel_arc_to; #endif backend->close_path = _cairo_cogl_context_close_path; //backend->arc = _cairo_cogl_context_arc; backend->rectangle = _cairo_cogl_context_rectangle; /* Try to automatically catch if any new path APIs are added that mean * we may need to overload more functions... */ assert (((char *)&backend->path_extents - (char *)&backend->device_to_user_distance) == (sizeof (void *) * 14)); backend->fill = _cairo_cogl_context_fill; backend->fill_preserve = _cairo_cogl_context_fill_preserve; backend->stroke = _cairo_cogl_context_stroke; backend->stroke_preserve = _cairo_cogl_context_stroke_preserve; backend->clip = _cairo_cogl_context_clip; cr->dev->backend_vtable_initialized = TRUE; } cr->base.base.backend = &cr->dev->backend; _cairo_path_fixed_init (&cr->user_path); cr->path_is_rectangle = FALSE; return &cr->base.base; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-gradient-private.h�������������������0000664�0000000�0000000�00000006432�12710376503�0027740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #ifndef CAIRO_COGL_GRADIENT_PRIVATE_H #define CAIRO_COGL_GRADIENT_PRIVATE_H #include "cairoint.h" #include "cairo-pattern-private.h" #include <cogl/cogl2-experimental.h> #define CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE (1024 * 1024) typedef enum _cairo_cogl_gradient_compatibility { CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD = 1<<0, CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT = 1<<1, CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT = 1<<2, CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE = 1<<3 } cairo_cogl_gradient_compatibility_t; #define CAIRO_COGL_GRADIENT_CAN_EXTEND_ALL (CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD |\ CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT|\ CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT|\ CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE) typedef struct _cairo_cogl_linear_texture_entry { cairo_cogl_gradient_compatibility_t compatibility; CoglTexture *texture; float translate_x; float scale_x; } cairo_cogl_linear_texture_entry_t; typedef struct _cairo_cogl_linear_gradient { cairo_cache_entry_t cache_entry; cairo_reference_count_t ref_count; GList *textures; int n_stops; const cairo_gradient_stop_t *stops; cairo_gradient_stop_t stops_embedded[1]; } cairo_cogl_linear_gradient_t; cairo_int_status_t _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *context, cairo_extend_t extend_mode, int n_stops, const cairo_gradient_stop_t *stops, cairo_cogl_linear_gradient_t **gradient_out); cairo_cogl_linear_texture_entry_t * _cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient, cairo_extend_t extend_mode); cairo_cogl_linear_gradient_t * _cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient); void _cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient); cairo_bool_t _cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b); #endif /* CAIRO_COGL_GRADIENT_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-gradient.c���������������������������0000664�0000000�0000000�00000050572�12710376503�0026267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ //#include "cairoint.h" #include "cairo-cogl-private.h" #include "cairo-cogl-gradient-private.h" #include "cairo-image-surface-private.h" #include <cogl/cogl2-experimental.h> #include <glib.h> #define DUMP_GRADIENTS_TO_PNG static unsigned long _cairo_cogl_linear_gradient_hash (unsigned int n_stops, const cairo_gradient_stop_t *stops) { return _cairo_hash_bytes (n_stops, stops, sizeof (cairo_gradient_stop_t) * n_stops); } static cairo_cogl_linear_gradient_t * _cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t *ctx, unsigned long hash, unsigned int n_stops, const cairo_gradient_stop_t *stops) { cairo_cogl_linear_gradient_t lookup; lookup.cache_entry.hash = hash, lookup.n_stops = n_stops; lookup.stops = stops; return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry); } cairo_bool_t _cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b) { const cairo_cogl_linear_gradient_t *a = key_a; const cairo_cogl_linear_gradient_t *b = key_b; if (a->n_stops != b->n_stops) return FALSE; return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0; } cairo_cogl_linear_gradient_t * _cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); _cairo_reference_count_inc (&gradient->ref_count); return gradient; } void _cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient) { GList *l; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); if (! _cairo_reference_count_dec_and_test (&gradient->ref_count)) return; for (l = gradient->textures; l; l = l->next) { cairo_cogl_linear_texture_entry_t *entry = l->data; cogl_object_unref (entry->texture); free (entry); } g_list_free (gradient->textures); free (gradient); } static int _cairo_cogl_util_next_p2 (int a) { int rval = 1; while (rval < a) rval <<= 1; return rval; } static float get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1) { float range; float max = 0; range = fabs (color0->red - color1->red); max = MAX (range, max); range = fabs (color0->green - color1->green); max = MAX (range, max); range = fabs (color0->blue - color1->blue); max = MAX (range, max); range = fabs (color0->alpha - color1->alpha); max = MAX (range, max); return max; } static int _cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t extend, unsigned int n_stops, const cairo_gradient_stop_t *stops) { unsigned int n; float max_texels_per_unit_offset = 0; float total_offset_range; /* Find the stop pair demanding the most precision because we are * interpolating the largest color-component range. * * From that we can define the relative sizes of all the other * stop pairs within our texture and thus the overall size. * * To determine the maximum number of texels for a given gap we * look at the range of colors we are expected to interpolate (so * long as the stop offsets are not degenerate) and we simply * assume we want one texel for each unique color value possible * for a one byte-per-component representation. * XXX: maybe this is overkill and just allowing 128 levels * instead of 256 would be enough and then we'd rely on the * bilinear filtering to give the full range. * * XXX: potentially we could try and map offsets to pixels to come * up with a more precise mapping, but we are aiming to cache * the gradients so we can't make assumptions about how it will be * scaled in the future. */ for (n = 1; n < n_stops; n++) { float color_range; float offset_range; float texels; float texels_per_unit_offset; /* note: degenerate stops don't need to be represented in the * texture but we want to be sure that solid gaps get at least * one texel and all other gaps get at least 2 texels. */ if (stops[n].offset == stops[n-1].offset) continue; color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color); if (color_range == 0) texels = 1; else texels = MAX (2, 256.0f * color_range); /* So how many texels would we need to map over the full [0,1] * gradient range so this gap would have enough texels? ... */ offset_range = stops[n].offset - stops[n - 1].offset; texels_per_unit_offset = texels / offset_range; if (texels_per_unit_offset > max_texels_per_unit_offset) max_texels_per_unit_offset = texels_per_unit_offset; } total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset); return max_texels_per_unit_offset * total_offset_range; } /* Aim to create gradient textures without an alpha component so we can avoid * needing to use blending... */ static CoglPixelFormat _cairo_cogl_linear_gradient_format_for_stops (cairo_extend_t extend, unsigned int n_stops, const cairo_gradient_stop_t *stops) { unsigned int n; /* We have to add extra transparent texels to the end of the gradient to * handle CAIRO_EXTEND_NONE... */ if (extend == CAIRO_EXTEND_NONE) return COGL_PIXEL_FORMAT_BGRA_8888_PRE; for (n = 1; n < n_stops; n++) { if (stops[n].color.alpha != 1.0) return COGL_PIXEL_FORMAT_BGRA_8888_PRE; } return COGL_PIXEL_FORMAT_BGR_888; } static cairo_cogl_gradient_compatibility_t _cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode) { switch (extend_mode) { case CAIRO_EXTEND_NONE: return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE; case CAIRO_EXTEND_PAD: return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD; case CAIRO_EXTEND_REPEAT: return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT; case CAIRO_EXTEND_REFLECT: return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT; } assert (0); /* not reached */ return CAIRO_EXTEND_NONE; } cairo_cogl_linear_texture_entry_t * _cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient, cairo_extend_t extend_mode) { GList *l; cairo_cogl_gradient_compatibility_t compatibility = _cairo_cogl_compatibility_from_extend_mode (extend_mode); for (l = gradient->textures; l; l = l->next) { cairo_cogl_linear_texture_entry_t *entry = l->data; if (entry->compatibility & compatibility) return entry; } return NULL; } static void color_stop_lerp (const cairo_color_stop_t *c0, const cairo_color_stop_t *c1, float factor, cairo_color_stop_t *dest) { /* NB: we always ignore the short members in this file so we don't need to * worry about initializing them here. */ dest->red = c0->red * (1.0f-factor) + c1->red * factor; dest->green = c0->green * (1.0f-factor) + c1->green * factor; dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor; dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor; } static size_t _cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient) { GList *l; size_t size = 0; for (l = gradient->textures; l; l = l->next) { cairo_cogl_linear_texture_entry_t *entry = l->data; size += cogl_texture_get_width (entry->texture) * 4; } return size; } static void emit_stop (CoglVertexP2C4 **position, float left, float right, const cairo_color_stop_t *left_color, const cairo_color_stop_t *right_color) { CoglVertexP2C4 *p = *position; guint8 lr = left_color->red * 255; guint8 lg = left_color->green * 255; guint8 lb = left_color->blue * 255; guint8 la = left_color->alpha * 255; guint8 rr = right_color->red * 255; guint8 rg = right_color->green * 255; guint8 rb = right_color->blue * 255; guint8 ra = right_color->alpha * 255; p[0].x = left; p[0].y = 0; p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la; p[1].x = left; p[1].y = 1; p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la; p[2].x = right; p[2].y = 1; p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra; p[3].x = left; p[3].y = 0; p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la; p[4].x = right; p[4].y = 1; p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra; p[5].x = right; p[5].y = 0; p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra; *position = &p[6]; } #ifdef DUMP_GRADIENTS_TO_PNG static void dump_gradient_to_png (CoglTexture *texture) { cairo_image_surface_t *image = (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cogl_texture_get_width (texture), cogl_texture_get_height (texture)); CoglPixelFormat format; static int gradient_id = 0; char *gradient_name; if (image->base.status) return; #if G_BYTE_ORDER == G_LITTLE_ENDIAN format = COGL_PIXEL_FORMAT_BGRA_8888_PRE; #else format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; #endif cogl_texture_get_data (texture, format, 0, image->data); gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++); g_print ("writing gradient: %s\n", gradient_name); cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name); g_free (gradient_name); } #endif cairo_int_status_t _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device, cairo_extend_t extend_mode, int n_stops, const cairo_gradient_stop_t *stops, cairo_cogl_linear_gradient_t **gradient_out) { unsigned long hash; cairo_cogl_linear_gradient_t *gradient; cairo_cogl_linear_texture_entry_t *entry; cairo_gradient_stop_t *internal_stops; int stop_offset; int n_internal_stops; int n; cairo_cogl_gradient_compatibility_t compatibilities; int width; int left_padding = 0; cairo_color_stop_t left_padding_color; int right_padding = 0; cairo_color_stop_t right_padding_color; CoglPixelFormat format; CoglTexture2D *tex; GError *error = NULL; int un_padded_width; CoglHandle offscreen; cairo_int_status_t status; int n_quads; int n_vertices; float prev; float right; CoglVertexP2C4 *vertices; CoglVertexP2C4 *p; CoglPrimitive *prim; hash = _cairo_cogl_linear_gradient_hash (n_stops, stops); gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops); if (gradient) { cairo_cogl_linear_texture_entry_t *entry = _cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode); if (entry) { *gradient_out = _cairo_cogl_linear_gradient_reference (gradient); return CAIRO_INT_STATUS_SUCCESS; } } if (!gradient) { gradient = malloc (sizeof (cairo_cogl_linear_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1)); if (!gradient) return CAIRO_INT_STATUS_NO_MEMORY; CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1); /* NB: we update the cache_entry size at the end before * [re]adding it to the cache. */ gradient->cache_entry.hash = hash; gradient->textures = NULL; gradient->n_stops = n_stops; gradient->stops = gradient->stops_embedded; memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops); } else _cairo_cogl_linear_gradient_reference (gradient); entry = malloc (sizeof (cairo_cogl_linear_texture_entry_t)); if (!entry) { status = CAIRO_INT_STATUS_NO_MEMORY; goto BAIL; } compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode); n_internal_stops = n_stops; stop_offset = 0; /* We really need stops covering the full [0,1] range for repeat/reflect * if we want to use sampler REPEAT/MIRROR wrap modes so we may need * to add some extra stops... */ if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT) { /* If we don't need any extra stops then actually the texture * will be shareable for repeat and reflect... */ compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT | CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT); if (stops[0].offset != 0) { n_internal_stops++; stop_offset++; } if (stops[n_stops - 1].offset != 1) n_internal_stops++; } internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t)); memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops); /* cairo_color_stop_t values are all unpremultiplied but we need to * interpolate premultiplied colors so we premultiply all the double * components now. (skipping any extra stops added for repeat/reflect) * * Anothing thing to note is that by premultiplying the colors * early we'll also reduce the range of colors to interpolate * which can result in smaller gradient textures. */ for (n = stop_offset; n < n_stops; n++) { cairo_color_stop_t *color = &internal_stops[n].color; color->red *= color->alpha; color->green *= color->alpha; color->blue *= color->alpha; } if (n_internal_stops != n_stops) { if (extend_mode == CAIRO_EXTEND_REPEAT) { compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT; if (stops[0].offset != 0) { /* what's the wrap-around distance between the user's end-stops? */ double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset; internal_stops[0].offset = 0; color_stop_lerp (&stops[0].color, &stops[n_stops - 1].color, stops[0].offset / dx, &internal_stops[0].color); } if (stops[n_stops - 1].offset != 1) { internal_stops[n_internal_stops - 1].offset = 1; internal_stops[n_internal_stops - 1].color = internal_stops[0].color; } } else if (extend_mode == CAIRO_EXTEND_REFLECT) { compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT; if (stops[0].offset != 0) { internal_stops[0].offset = 0; internal_stops[0].color = stops[n_stops - 1].color; } if (stops[n_stops - 1].offset != 1) { internal_stops[n_internal_stops - 1].offset = 1; internal_stops[n_internal_stops - 1].color = stops[0].color; } } } stops = internal_stops; n_stops = n_internal_stops; width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops); if (extend_mode == CAIRO_EXTEND_PAD) { /* Here we need to guarantee that the edge texels of our * texture correspond to the desired padding color so we * can use CLAMP_TO_EDGE. * * For short stop-gaps and especially for degenerate stops * it's possible that without special consideration the * user's end stop colors would not be present in our final * texture. * * To handle this we forcibly add two extra padding texels * at the edges which extend beyond the [0,1] range of the * gradient itself and we will later report a translate and * scale transform to compensate for this. */ /* XXX: If we consider generating a mipmap for our 1d texture * at some point then we also need to consider how much * padding to add to be sure lower mipmap levels still have * the desired edge color (as opposed to a linear blend with * other colors of the gradient). */ left_padding = 1; left_padding_color = stops[0].color; right_padding = 1; right_padding_color = stops[n_stops - 1].color; } else if (extend_mode == CAIRO_EXTEND_NONE) { /* We handle EXTEND_NONE by adding two extra, transparent, texels at * the ends of the texture and use CLAMP_TO_EDGE. * * We add a scale and translate transform so to account for our texels * extending beyond the [0,1] range. */ left_padding = 1; left_padding_color.red = 0; left_padding_color.green = 0; left_padding_color.blue = 0; left_padding_color.alpha = 0; right_padding = 1; right_padding_color = left_padding_color; } /* If we still have stops that don't cover the full [0,1] range * then we need to define a texture-coordinate scale + translate * transform to account for that... */ if (stops[n_stops - 1].offset - stops[0].offset < 1) { float range = stops[n_stops - 1].offset - stops[0].offset; entry->scale_x = 1.0 / range; entry->translate_x = -(stops[0].offset * entry->scale_x); } width += left_padding + right_padding; width = _cairo_cogl_util_next_p2 (width); width = MIN (4096, width); /* lets not go too stupidly big! */ format = _cairo_cogl_linear_gradient_format_for_stops (extend_mode, n_stops, stops); do { tex = cogl_texture_2d_new_with_size (device->cogl_context, width, 1, format, &error); if (!tex) g_error_free (error); } while (tex == NULL && width >> 1); if (!tex) { status = CAIRO_INT_STATUS_NO_MEMORY; goto BAIL; } entry->texture = COGL_TEXTURE (tex); entry->compatibility = compatibilities; un_padded_width = width - left_padding - right_padding; /* XXX: only when we know the final texture width can we calculate the * scale and translate factors needed to account for padding... */ if (un_padded_width != width) entry->scale_x *= (float)un_padded_width / (float)width; if (left_padding) entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding; offscreen = cogl_offscreen_new_to_texture (tex); cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen)); cogl_ortho (0, width, 1, 0, -1, 100); cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen), COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); n_quads = n_stops - 1 + !!left_padding + !!right_padding; n_vertices = 6 * n_quads; vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices); p = vertices; if (left_padding) emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color); prev = (float)left_padding; for (n = 1; n < n_stops; n++) { right = (float)left_padding + (float)un_padded_width * stops[n].offset; emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color); prev = right; } if (right_padding) emit_stop (&p, prev, width, &right_padding_color, &right_padding_color); prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES, n_vertices, vertices); /* Just use this as the simplest way to setup a default pipeline... */ cogl_set_source_color4f (0, 0, 0, 0); cogl_primitive_draw (prim); cogl_object_unref (prim); cogl_pop_framebuffer (); cogl_object_unref (offscreen); gradient->textures = g_list_prepend (gradient->textures, entry); gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient); #ifdef DUMP_GRADIENTS_TO_PNG dump_gradient_to_png (COGL_TEXTURE (tex)); #endif #warning "FIXME:" /* XXX: it seems the documentation of _cairo_cache_insert isn't true - it * doesn't handle re-adding the same entry gracefully - the cache will * just keep on growing and then it will start randomly evicting things * pointlessly */ /* we ignore errors here and just return an uncached gradient */ if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry))) _cairo_cogl_linear_gradient_reference (gradient); *gradient_out = gradient; return CAIRO_INT_STATUS_SUCCESS; BAIL: free (entry); if (gradient) _cairo_cogl_linear_gradient_destroy (gradient); return status; } ��������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-private.h����������������������������0000664�0000000�0000000�00000012515�12710376503�0026144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #ifndef CAIRO_COGL_PRIVATE_H #define CAIRO_COGL_PRIVATE_H #include "cairo-device-private.h" #include "cairo-cache-private.h" #include "cairo-backend-private.h" #include "cairo-default-context-private.h" #include "cairo-surface-private.h" #include <cogl/cogl2-experimental.h> typedef enum _cairo_cogl_template_type { CAIRO_COGL_TEMPLATE_TYPE_SOLID, CAIRO_COGL_TEMPLATE_TYPE_TEXTURE, CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID, CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE, CAIRO_COGL_TEMPLATE_TYPE_COUNT } cairo_cogl_template_type; typedef struct _cairo_cogl_device { cairo_device_t base; cairo_bool_t backend_vtable_initialized; cairo_backend_t backend; /* We save a copy of all the original backend methods that we override so * we can chain up... */ cairo_backend_t backend_parent; CoglContext *cogl_context; CoglTexture *dummy_texture; /* This is a sparsely filled set of templates because we don't support * the full range of operators that cairo has. All entries corresponding * to unsupported operators are NULL. * * The CAIRO_OPERATOR_ADD is the operator enum with the highest value that * we support so we at least cap the size of the array by that. * * For each operator we have a template for when we have a solid source * and another for each texture format that could be used as a source. */ CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT]; CoglMatrix identity; /* Caches 1d linear gradient textures */ cairo_cache_t linear_cache; cairo_cache_t path_fill_staging_cache; cairo_cache_t path_fill_prim_cache; cairo_cache_t path_stroke_staging_cache; cairo_cache_t path_stroke_prim_cache; } cairo_cogl_device_t; typedef struct _cairo_cogl_clip_primitives { cairo_t *clip; CoglPrimitive **primitives; } cairo_cogl_clip_primitives_t; typedef struct _cairo_cogl_surface { cairo_surface_t base; CoglPixelFormat cogl_format; cairo_bool_t ignore_alpha; /* We currently have 3 basic kinds of Cogl surfaces: * 1) A light surface simply wrapping a CoglTexture * 2) A CoglOffscreen framebuffer that implicitly also wraps a CoglTexture * 3) A CoglOnscreen framebuffer which could potentially be mapped to * a CoglTexture (e.g. via tfp on X11) but we don't currently do * that. */ CoglTexture *texture; CoglFramebuffer *framebuffer; int width; int height; GQueue *journal; CoglAttributeBuffer *buffer_stack; size_t buffer_stack_size; size_t buffer_stack_offset; guint8 *buffer_stack_pointer; cairo_clip_t *last_clip; /* A small fifo of recently used cairo_clip_ts paired with CoglPrimitives * that can be used to mask the stencil buffer. */ GList *clips_fifo; int n_clip_updates_per_frame; /* Since the surface backend drawing operator functions don't get * passed the current cairo_t context we don't have a good way * to get our user-coordinates path into our surface_fill function. * * For now we use our _cairo_cogl_context_fill() wrapper to set this * side band data on the surface... */ cairo_path_fixed_t *user_path; cairo_matrix_t *ctm; cairo_matrix_t *ctm_inverse; cairo_bool_t path_is_rectangle; double path_rectangle_x; double path_rectangle_y; double path_rectangle_width; double path_rectangle_height; } cairo_cogl_surface_t; cairo_status_t _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y, cairo_fixed_t width, cairo_fixed_t height); cairo_int_status_t _cairo_cogl_surface_fill_rectangle (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, double x, double y, double width, double height, cairo_matrix_t *ctm, const cairo_clip_t *clip); #endif /* CAIRO_COGL_PRIVATE_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-surface.c����������������������������0000664�0000000�0000000�00000247150�12710376503�0026122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #include "cairoint.h" #include "cairo-cache-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-fixed-private.h" #include "cairo-device-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-image-surface-inline.h" #include "cairo-cogl-private.h" #include "cairo-cogl-gradient-private.h" #include "cairo-arc-private.h" #include "cairo-traps-private.h" #include "cairo-cogl-context-private.h" #include "cairo-cogl-utils-private.h" #include "cairo-box-inline.h" #include "cairo-surface-subsurface-inline.h" #include "cairo-surface-fallback-private.h" #include "cairo-surface-offset-private.h" #include "cairo-cogl.h" #include <cogl/cogl2-experimental.h> #include <glib.h> #define CAIRO_COGL_DEBUG 0 //#define FILL_WITH_COGL_PATH //#define USE_CAIRO_PATH_FLATTENER #define ENABLE_PATH_CACHE //#define DISABLE_BATCHING #define USE_COGL_RECTANGLE_API #define ENABLE_RECTANGLES_FASTPATH #if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE) #define NEED_COGL_CONTEXT #endif #if CAIRO_COGL_DEBUG && __GNUC__ #define UNSUPPORTED(reason) ({ \ g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \ CAIRO_INT_STATUS_UNSUPPORTED; \ }) #else #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED #endif #define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024) typedef struct _cairo_cogl_texture_attributes { /* nabbed from cairo_surface_attributes_t... */ cairo_matrix_t matrix; cairo_extend_t extend; cairo_filter_t filter; cairo_bool_t has_component_alpha; CoglPipelineWrapMode s_wrap; CoglPipelineWrapMode t_wrap; } cairo_cogl_texture_attributes_t; typedef enum _cairo_cogl_journal_entry_type { CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE, CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE, CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH, CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP } cairo_cogl_journal_entry_type_t; typedef struct _cairo_cogl_journal_entry { cairo_cogl_journal_entry_type_t type; } cairo_cogl_journal_entry_t; typedef struct _cairo_cogl_journal_clip_entry { cairo_cogl_journal_entry_t base; cairo_clip_t *clip; } cairo_cogl_journal_clip_entry_t; typedef struct _cairo_cogl_journal_rect_entry { cairo_cogl_journal_entry_t base; CoglPipeline *pipeline; float x; float y; float width; float height; int n_layers; cairo_matrix_t ctm; } cairo_cogl_journal_rect_entry_t; typedef struct _cairo_cogl_journal_prim_entry { cairo_cogl_journal_entry_t base; CoglPipeline *pipeline; CoglPrimitive *primitive; gboolean has_transform; cairo_matrix_t transform; } cairo_cogl_journal_prim_entry_t; typedef struct _cairo_cogl_journal_path_entry { cairo_cogl_journal_entry_t base; CoglPipeline *pipeline; CoglPath *path; } cairo_cogl_journal_path_entry_t; typedef struct _cairo_cogl_path_fill_meta { cairo_cache_entry_t cache_entry; cairo_reference_count_t ref_count; int counter; cairo_path_fixed_t *user_path; cairo_matrix_t ctm_inverse; /* TODO */ #if 0 /* A cached path tessellation should be re-usable with different rotations * and translations but not for different scales. * * one idea is to track the diagonal lenghts of a unit rectangle * transformed through the original ctm use to tesselate the geometry * so we can check what the lengths are for any new ctm to know if * this geometry is compatible. */ #endif CoglPrimitive *prim; } cairo_cogl_path_fill_meta_t; typedef struct _cairo_cogl_path_stroke_meta { cairo_cache_entry_t cache_entry; cairo_reference_count_t ref_count; int counter; cairo_path_fixed_t *user_path; cairo_matrix_t ctm_inverse; cairo_stroke_style_t style; double tolerance; /* TODO */ #if 0 /* A cached path tessellation should be re-usable with different rotations * and translations but not for different scales. * * one idea is to track the diagonal lenghts of a unit rectangle * transformed through the original ctm use to tesselate the geometry * so we can check what the lengths are for any new ctm to know if * this geometry is compatible. */ #endif CoglPrimitive *prim; } cairo_cogl_path_stroke_meta_t; static cairo_surface_t * _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, cairo_bool_t ignore_alpha, CoglFramebuffer *framebuffer, CoglTexture *texture); static cairo_int_status_t _cairo_cogl_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); static void _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface); cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend; slim_hidden_proto (cairo_cogl_device_create); slim_hidden_proto (cairo_cogl_surface_create); slim_hidden_proto (cairo_cogl_surface_get_framebuffer); slim_hidden_proto (cairo_cogl_surface_get_texture); slim_hidden_proto (cairo_cogl_surface_end_frame); static cairo_cogl_device_t * to_device (cairo_device_t *device) { return (cairo_cogl_device_t *)device; } /* moves trap points such that they become the actual corners of the trapezoid */ static void _sanitize_trap (cairo_trapezoid_t *t) { cairo_trapezoid_t s = *t; #define FIX(lr, tb, p) \ if (t->lr.p.y != t->tb) { \ t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ t->lr.p.y = s.tb; \ } FIX (left, top, p1); FIX (left, bottom, p2); FIX (right, top, p1); FIX (right, bottom, p2); } static cairo_status_t _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface) { GError *error = NULL; if (surface->framebuffer) return CAIRO_STATUS_SUCCESS; surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture)); if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) { g_error_free (error); cogl_object_unref (surface->framebuffer); surface->framebuffer = NULL; return CAIRO_STATUS_NO_MEMORY; } cogl_push_framebuffer (surface->framebuffer); cogl_ortho (0, surface->width, surface->height, 0, -1, 100); cogl_pop_framebuffer (); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_cogl_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_cogl_surface_t *reference_surface = abstract_surface; cairo_cogl_surface_t *surface; CoglTexture *texture; cairo_status_t status; texture = cogl_texture_new_with_size (width, height, COGL_TEXTURE_NO_SLICING, (content & CAIRO_CONTENT_COLOR) ? COGL_PIXEL_FORMAT_BGRA_8888_PRE : COGL_PIXEL_FORMAT_A_8); if (!texture) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface = (cairo_cogl_surface_t *) _cairo_cogl_surface_create_full (to_device(reference_surface->base.device), (content & CAIRO_CONTENT_ALPHA) == 0, NULL, texture); if (unlikely (surface->base.status)) return &surface->base; status = _cairo_cogl_surface_ensure_framebuffer (surface); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } return &surface->base; } static cairo_bool_t _cairo_cogl_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_cogl_surface_t *surface = abstract_surface; extents->x = 0; extents->y = 0; extents->width = surface->width; extents->height = surface->height; return TRUE; } static void _cairo_cogl_journal_free (cairo_cogl_surface_t *surface) { GList *l; for (l = surface->journal->head; l; l = l->next) { cairo_cogl_journal_entry_t *entry = l->data; if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) { cairo_cogl_journal_prim_entry_t *prim_entry = (cairo_cogl_journal_prim_entry_t *)entry; cogl_object_unref (prim_entry->primitive); } else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) { cairo_cogl_journal_path_entry_t *path_entry = (cairo_cogl_journal_path_entry_t *)entry; cogl_object_unref (path_entry->path); } } g_queue_free (surface->journal); surface->journal = NULL; } #ifdef FILL_WITH_COGL_PATH static void _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface, CoglPipeline *pipeline, CoglPath *path) { cairo_cogl_journal_path_entry_t *entry; if (unlikely (surface->journal == NULL)) surface->journal = g_queue_new (); /* FIXME: Instead of a GList here we should stack allocate the journal * entries so it would be cheaper to allocate and they can all be freed in * one go after flushing! */ entry = g_slice_new (cairo_cogl_journal_path_entry_t); entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH; entry->pipeline = cogl_object_ref (pipeline); entry->path = cogl_object_ref (path); g_queue_push_tail (surface->journal, entry); #ifdef DISABLE_BATCHING _cairo_cogl_journal_flush (surface); #endif } #endif /* FILL_WITH_COGL_PATH */ static void _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface, CoglPipeline *pipeline, CoglPrimitive *primitive, cairo_matrix_t *transform) { cairo_cogl_journal_prim_entry_t *entry; if (unlikely (surface->journal == NULL)) surface->journal = g_queue_new (); /* FIXME: Instead of a GList here we should stack allocate the journal * entries so it would be cheaper to allocate and they can all be freed in * one go after flushing! */ entry = g_slice_new (cairo_cogl_journal_prim_entry_t); entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE; entry->pipeline = cogl_object_ref (pipeline); if (transform) { entry->transform = *transform; entry->has_transform = TRUE; } else entry->has_transform = FALSE; entry->primitive = cogl_object_ref (primitive); g_queue_push_tail (surface->journal, entry); #ifdef DISABLE_BATCHING _cairo_cogl_journal_flush (surface); #endif } static void _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface, CoglPipeline *pipeline, float x, float y, float width, float height, int n_layers, cairo_matrix_t *ctm) { cairo_cogl_journal_rect_entry_t *entry; if (unlikely (surface->journal == NULL)) surface->journal = g_queue_new (); /* FIXME: Instead of a GList here we should stack allocate the journal * entries so it would be cheaper to allocate and they can all be freed in * one go after flushing! */ entry = g_slice_new (cairo_cogl_journal_rect_entry_t); entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE; entry->pipeline = cogl_object_ref (pipeline); entry->x = x; entry->y = y; entry->width = width; entry->height = height; entry->ctm = *ctm; entry->n_layers = n_layers; g_queue_push_tail (surface->journal, entry); #ifdef DISABLE_BATCHING _cairo_cogl_journal_flush (surface); #endif } static void _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface, const cairo_clip_t *clip) { cairo_cogl_journal_clip_entry_t *entry; if (unlikely (surface->journal == NULL)) surface->journal = g_queue_new (); /* FIXME: Instead of a GList here we should stack allocate the journal * entries so it would be cheaper to allocate and they can all be freed in * one go after flushing! */ entry = g_slice_new (cairo_cogl_journal_clip_entry_t); entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP; entry->clip = _cairo_clip_copy (clip); g_queue_push_tail (surface->journal, entry); } static void _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface) { GList *l; if (!surface->journal) { assert (surface->last_clip == NULL); return; } if (surface->buffer_stack && surface->buffer_stack_offset) { cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); cogl_object_unref (surface->buffer_stack); surface->buffer_stack = NULL; } for (l = surface->journal->head; l; l = l->next) { cairo_cogl_journal_entry_t *entry = l->data; gsize entry_size; switch (entry->type) { case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { cairo_cogl_journal_clip_entry_t *clip_entry = (cairo_cogl_journal_clip_entry_t *)entry; _cairo_clip_destroy (clip_entry->clip); entry_size = sizeof (cairo_cogl_journal_clip_entry_t); break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { cairo_cogl_journal_rect_entry_t *rect_entry = (cairo_cogl_journal_rect_entry_t *)entry; cogl_object_unref (rect_entry->pipeline); entry_size = sizeof (cairo_cogl_journal_rect_entry_t); break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { cairo_cogl_journal_prim_entry_t *prim_entry = (cairo_cogl_journal_prim_entry_t *)entry; cogl_object_unref (prim_entry->pipeline); cogl_object_unref (prim_entry->primitive); entry_size = sizeof (cairo_cogl_journal_prim_entry_t); break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: { cairo_cogl_journal_path_entry_t *path_entry = (cairo_cogl_journal_path_entry_t *)entry; cogl_object_unref (path_entry->pipeline); cogl_object_unref (path_entry->path); entry_size = sizeof (cairo_cogl_journal_path_entry_t); break; } default: assert (0); /* not reached! */ entry_size = 0; /* avoid compiler warning */ } g_slice_free1 (entry_size, entry); } g_queue_clear (surface->journal); if (surface->last_clip) { _cairo_clip_destroy (surface->last_clip); surface->last_clip = NULL; } } static CoglAttributeBuffer * _cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface, size_t size, size_t *offset, void **pointer) { /* XXX: In the Cogl journal we found it more efficient to have a pool of * buffers that we re-cycle but for now we simply thow away our stack * buffer each time we flush. */ if (unlikely (surface->buffer_stack && (surface->buffer_stack_size - surface->buffer_stack_offset) < size)) { cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); cogl_object_unref (surface->buffer_stack); surface->buffer_stack = NULL; surface->buffer_stack_size *= 2; } if (unlikely (surface->buffer_stack_size < size)) surface->buffer_stack_size = size * 2; if (unlikely (surface->buffer_stack == NULL)) { surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL); surface->buffer_stack_pointer = cogl_buffer_map (COGL_BUFFER (surface->buffer_stack), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); surface->buffer_stack_offset = 0; } *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset; *offset = surface->buffer_stack_offset; surface->buffer_stack_offset += size; return cogl_object_ref (surface->buffer_stack); } static CoglAttributeBuffer * _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface, cairo_traps_t *traps, size_t *offset, gboolean one_shot) { CoglAttributeBuffer *buffer; int n_traps = traps->num_traps; int i; CoglVertexP2 *triangles; if (one_shot) { buffer = _cairo_cogl_surface_allocate_buffer_space (surface, n_traps * sizeof (CoglVertexP2) * 6, offset, (void **)&triangles); if (!buffer) return NULL; } else { buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL); if (!buffer) return NULL; triangles = cogl_buffer_map (COGL_BUFFER (buffer), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (!triangles) return NULL; *offset = 0; } /* XXX: This is can be very expensive. I'm not sure a.t.m if it's * predominantly the bandwidth required or the cost of the fixed_to_float * conversions but either way we should try using an index buffer to * reduce the amount we upload by 1/3 (offset by allocating and uploading * indices though) sadly though my experience with the intel mesa drivers * is that slow paths can easily be hit when starting to use indices. */ for (i = 0; i < n_traps; i++) { CoglVertexP2 *p = &triangles[i * 6]; cairo_trapezoid_t *trap = &traps->traps[i]; p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x); p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y); p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x); p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y); p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x); p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y); p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x); p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y); p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x); p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y); p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x); p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y); } if (!one_shot) cogl_buffer_unmap (COGL_BUFFER (buffer)); return buffer; } /* Used for solid fills, in this case we just need a mesh made of * a single (2-component) position attribute. */ static CoglPrimitive * _cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface, cairo_traps_t *traps, gboolean one_shot) { size_t offset; CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot); CoglAttribute *pos = cogl_attribute_new (buffer, "cogl_position_in", sizeof (CoglVertexP2), offset, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglPrimitive *prim; /* The attribute will have taken a reference on the buffer */ cogl_object_unref (buffer); prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, traps->num_traps * 6, pos, NULL); /* The primitive will now keep the attribute alive... */ cogl_object_unref (pos); return prim; } /* Used for surface fills, in this case we need a mesh made of a single * (2-component) position attribute + we also alias the same attribute as * (2-component) texture coordinates */ static CoglPrimitive * _cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface, cairo_traps_t *traps, gboolean one_shot) { size_t offset; CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot); CoglAttribute *pos = cogl_attribute_new (buffer, "cogl_position_in", sizeof (CoglVertexP2), offset, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglAttribute *tex_coords = cogl_attribute_new (buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2), 0, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglPrimitive *prim; /* The attributes will have taken references on the buffer */ cogl_object_unref (buffer); prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, traps->num_traps * 6, pos, tex_coords, NULL); /* The primitive will now keep the attributes alive... */ cogl_object_unref (pos); cogl_object_unref (tex_coords); return prim; } static CoglPrimitive * _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface, cairo_traps_t *traps, int n_layers, gboolean one_shot) { int n_traps = traps->num_traps; int i; /* XXX: Ideally we would skip tessellating to traps entirely since * given their representation, conversion to triangles is quite expensive. * * This simplifies the conversion to triangles by making the end points of * the two side lines actually just correspond to the corners of the * traps. */ for (i = 0; i < n_traps; i++) _sanitize_trap (&traps->traps[i]); if (n_layers == 0) return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot); else { assert (n_layers == 1); return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot); } } static cairo_int_status_t _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, int n_layers, cairo_bool_t one_shot, CoglPrimitive **primitive, size_t *size) { cairo_traps_t traps; cairo_int_status_t status; _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps); if (unlikely (status)) goto BAIL; if (traps.num_traps == 0) { status = CAIRO_INT_STATUS_NOTHING_TO_DO; goto BAIL; } *size = traps.num_traps * sizeof (CoglVertexP2) * 6; *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot); if (!*primitive) { status = CAIRO_INT_STATUS_NO_MEMORY; goto BAIL; } BAIL: _cairo_traps_fini (&traps); return status; } static void _cairo_cogl_clip_push_box (const cairo_box_t *box) { if (_cairo_box_is_pixel_aligned (box)) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (box, &rect); cogl_clip_push_window_rectangle (rect.x, rect.y, rect.width, rect.height); } else { double x1, y1, x2, y2; _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2); cogl_clip_push_rectangle (x1, y1, x2, y2); } } static void _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface) { GList *l; int clip_stack_depth = 0; int i; if (!surface->journal) return; if (surface->buffer_stack && surface->buffer_stack_offset) { cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack)); cogl_object_unref (surface->buffer_stack); surface->buffer_stack = NULL; } cogl_set_framebuffer (surface->framebuffer); cogl_push_matrix (); for (l = surface->journal->head; l; l = l->next) { cairo_cogl_journal_entry_t *entry = l->data; switch (entry->type) { case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { cairo_cogl_journal_clip_entry_t *clip_entry = (cairo_cogl_journal_clip_entry_t *)entry; cairo_clip_path_t *path; #if 0 cairo_bool_t checked_for_primitives = FALSE; cairo_cogl_clip_primitives_t *clip_primitives; #endif for (i = 0; i < clip_stack_depth; i++) cogl_clip_pop (); clip_stack_depth = 0; for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) { cairo_rectangle_int_t extents; cairo_int_status_t status; CoglPrimitive *prim; size_t prim_size; _cairo_path_fixed_approximate_clip_extents (&path->path, &extents); /* TODO - maintain a fifo of the last 10 used clips with cached * primitives to see if we can avoid tesselating the path and * uploading the vertices... */ #if 0 if (!checked_for_primitives) { clip_primitives = find_clip_primitives (clip); checked_for_primitives = TRUE; } if (clip_primitives) prim = clip_primitives->primitives[i]; #endif status = _cairo_cogl_fill_to_primitive (surface, &path->path, path->fill_rule, path->tolerance, 0, TRUE, &prim, &prim_size); if (unlikely (status)) { g_warning ("Failed to get primitive for clip path while flushing journal"); continue; } clip_stack_depth++; cogl_clip_push_primitive (prim, extents.x, extents.y, extents.x + extents.width, extents.y + extents.height); cogl_object_unref (prim); } for (i = 0; i < clip_entry->clip->num_boxes; i++) { clip_stack_depth++; _cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]); } surface->n_clip_updates_per_frame++; break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { cairo_cogl_journal_rect_entry_t *rect_entry = (cairo_cogl_journal_rect_entry_t *)entry; float tex_coords[8]; float x1 = rect_entry->x; float y1 = rect_entry->y; float x2 = rect_entry->x + rect_entry->width; float y2 = rect_entry->y + rect_entry->height; cairo_matrix_t *ctm = &rect_entry->ctm; float ctmfv[16] = { ctm->xx, ctm->yx, 0, 0, ctm->xy, ctm->yy, 0, 0, 0, 0, 1, 0, ctm->x0, ctm->y0, 0, 1 }; CoglMatrix transform; cogl_matrix_init_from_array (&transform, ctmfv); if (rect_entry->n_layers) { g_assert (rect_entry->n_layers <= 2); tex_coords[0] = x1; tex_coords[1] = y1; tex_coords[2] = x2; tex_coords[3] = y2; if (rect_entry->n_layers > 1) memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4); } cogl_set_source (rect_entry->pipeline); cogl_push_matrix (); cogl_transform (&transform); cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2, tex_coords, 4 * rect_entry->n_layers); cogl_pop_matrix (); break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { cairo_cogl_journal_prim_entry_t *prim_entry = (cairo_cogl_journal_prim_entry_t *)entry; CoglMatrix transform; cogl_push_matrix (); if (prim_entry->has_transform) { cairo_matrix_t *ctm = &prim_entry->transform; float ctmfv[16] = { ctm->xx, ctm->yx, 0, 0, ctm->xy, ctm->yy, 0, 0, 0, 0, 1, 0, ctm->x0, ctm->y0, 0, 1 }; cogl_matrix_init_from_array (&transform, ctmfv); cogl_transform (&transform); } else { cogl_matrix_init_identity (&transform); cogl_set_modelview_matrix (&transform); } cogl_set_source (prim_entry->pipeline); cogl_primitive_draw (prim_entry->primitive); cogl_pop_matrix (); break; } case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: { cairo_cogl_journal_path_entry_t *path_entry = (cairo_cogl_journal_path_entry_t *)entry; cogl_set_source (path_entry->pipeline); cogl_path_fill (path_entry->path); break; } default: assert (0); /* not reached! */ } } cogl_pop_matrix (); for (i = 0; i < clip_stack_depth; i++) cogl_clip_pop (); _cairo_cogl_journal_discard (surface); } static cairo_status_t _cairo_cogl_surface_flush (void *abstract_surface, unsigned flags) { cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; if (flags) return CAIRO_STATUS_SUCCESS; _cairo_cogl_journal_flush (surface); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_surface_finish (void *abstract_surface) { cairo_cogl_surface_t *surface = abstract_surface; if (surface->texture) cogl_object_unref (surface->texture); if (surface->framebuffer) cogl_object_unref (surface->framebuffer); if (surface->journal) _cairo_cogl_journal_free (surface); /*XXX wtf */ cairo_device_release (surface->base.device); return CAIRO_STATUS_SUCCESS; } static CoglPixelFormat get_cogl_format_from_cairo_format (cairo_format_t cairo_format); /* XXX: We often use RGBA format for onscreen framebuffers so make sure * to handle CAIRO_FORMAT_INVALID sensibly */ static cairo_format_t get_cairo_format_from_cogl_format (CoglPixelFormat format) { switch ((int)format) { case COGL_PIXEL_FORMAT_A_8: return CAIRO_FORMAT_A8; case COGL_PIXEL_FORMAT_RGB_565: return CAIRO_FORMAT_RGB16_565; case COGL_PIXEL_FORMAT_BGRA_8888_PRE: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: /* Note: this is ambiguous since CAIRO_FORMAT_RGB24 * would also map to the same CoglPixelFormat */ return CAIRO_FORMAT_ARGB32; default: g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n", format, format & COGL_A_BIT, format & COGL_BGR_BIT, format & COGL_PREMULT_BIT, format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT)); return CAIRO_FORMAT_INVALID; } } static CoglPixelFormat get_cogl_format_from_cairo_format (cairo_format_t cairo_format) { switch (cairo_format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: #if G_BYTE_ORDER == G_LITTLE_ENDIAN return COGL_PIXEL_FORMAT_BGRA_8888_PRE; #else return COGL_PIXEL_FORMAT_ARGB_8888_PRE; #endif case CAIRO_FORMAT_A8: return COGL_PIXEL_FORMAT_A_8; case CAIRO_FORMAT_RGB16_565: return COGL_PIXEL_FORMAT_RGB_565; case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB30: return 0; } g_warn_if_reached (); return 0; } static cairo_status_t _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface, cairo_rectangle_int_t *interest, cairo_image_surface_t **image_out) { cairo_image_surface_t *image; cairo_status_t status; cairo_format_t cairo_format; CoglPixelFormat cogl_format; /* TODO: Add cogl_texture_get_region() API so we don't have to ensure the * surface is bound to an fbo to read back pixels */ status = _cairo_cogl_surface_ensure_framebuffer (surface); if (unlikely (status)) return status; cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format); if (cairo_format == CAIRO_FORMAT_INVALID) { cairo_format = CAIRO_FORMAT_ARGB32; cogl_format = get_cogl_format_from_cairo_format (cairo_format); } else { cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer); } image = (cairo_image_surface_t *) cairo_image_surface_create (cairo_format, surface->width, surface->height); if (image->base.status) return image->base.status; /* TODO: Add cogl_framebuffer_read_pixels() API */ cogl_push_framebuffer (surface->framebuffer); cogl_read_pixels (0, 0, surface->width, surface->height, COGL_READ_PIXELS_COLOR_BUFFER, cogl_format, image->data); cogl_pop_framebuffer (); *image_out = image; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_cogl_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_cogl_surface_t *surface = abstract_surface; cairo_status_t status; if (surface->texture) { cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format); cairo_image_surface_t *image = (cairo_image_surface_t *) cairo_image_surface_create (format, surface->width, surface->height); if (image->base.status) return image->base.status; cogl_texture_get_data (surface->texture, cogl_texture_get_format (surface->texture), 0, image->data); image->base.is_clear = FALSE; *image_out = image; } else { cairo_rectangle_int_t extents = { 0, 0, surface->width, surface->height }; status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents, image_out); if (unlikely (status)) return status; } *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_cogl_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _cairo_cogl_surface_clear (cairo_cogl_surface_t *surface, const cairo_color_t *color) { /* Anything batched in the journal up until now is redundant... */ _cairo_cogl_journal_discard (surface); /* XXX: we currently implicitly clear the depth and stencil buffer here * but since we use the framebuffer_discard extension when available I * suppose this doesn't matter too much. * * The main concern is that we want to avoid re-loading an external z * buffer at the start of each frame, but also many gpu architectures have * optimizations for how they handle the depth/stencil buffers and can get * upset if they aren't cleared together at the start of the frame. * * FIXME: we need a way to assert that the clip stack currently isn't * using the stencil buffer before clearing it here! */ cogl_framebuffer_clear4f (surface->framebuffer, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_STENCIL, color->red * color->alpha, color->green * color->alpha, color->blue * color->alpha, color->alpha); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y, cairo_fixed_t width, cairo_fixed_t height) { cairo_status_t status; status = _cairo_path_fixed_move_to (path, x, y); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, width, 0); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, 0, height); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, -width, 0); if (unlikely (status)) return status; status = _cairo_path_fixed_close_path (path); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_cogl_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_cogl_surface_t *surface; cairo_path_fixed_t path; cairo_status_t status; cairo_matrix_t identity; if (clip == NULL) { if (op == CAIRO_OPERATOR_CLEAR) return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT); else if (source->type == CAIRO_PATTERN_TYPE_SOLID && (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) { return _cairo_cogl_surface_clear (abstract_surface, &((cairo_solid_pattern_t *) source)->color); } } /* fallback to handling the paint in terms of a fill... */ surface = abstract_surface; _cairo_path_fixed_init (&path); status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height); if (unlikely (status)) goto BAIL; #ifdef NEED_COGL_CONTEXT /* XXX: in cairo-cogl-context.c we set some sideband data on the * surface before issuing a fill so we need to do that here too... */ surface->user_path = &path; cairo_matrix_init_identity (&identity); surface->ctm = &identity; surface->ctm_inverse = &identity; surface->path_is_rectangle = TRUE; surface->path_rectangle_x = 0; surface->path_rectangle_y = 0; surface->path_rectangle_width = surface->width; surface->path_rectangle_height = surface->height; #endif status = _cairo_cogl_surface_fill (abstract_surface, op, source, &path, CAIRO_FILL_RULE_WINDING, 1, CAIRO_ANTIALIAS_DEFAULT, clip); BAIL: _cairo_path_fixed_fini (&path); return status; } static CoglPipelineWrapMode get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode) { switch (extend_mode) { case CAIRO_EXTEND_NONE: return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; case CAIRO_EXTEND_PAD: return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; case CAIRO_EXTEND_REPEAT: return COGL_PIPELINE_WRAP_MODE_REPEAT; case CAIRO_EXTEND_REFLECT: /* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */ return CAIRO_EXTEND_REPEAT; } assert (0); /* not reached */ return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; } #if 0 /* Given an arbitrary texture, check if it's already a pot texture and simply * return it back if so. If not create a new pot texture, scale the old to * fill it, unref the old and return a pointer to the new pot texture. */ static cairo_int_status_t _cairo_cogl_get_pot_texture (CoglContext *context, CoglTexture *texture, CoglTexture **pot_texture) { int width = cogl_texture_get_width (texture); int height = cogl_texture_get_height (texture); int pot_width; int pot_height; CoglHandle offscreen = NULL; CoglTexture2D *pot = NULL; GError *error; pot_width = _cairo_cogl_util_next_p2 (width); pot_height = _cairo_cogl_util_next_p2 (height); if (pot_width == width && pot_height == height) return CAIRO_INT_STATUS_SUCCESS; for (;;) { error = NULL; pot = cogl_texture_2d_new_with_size (context, pot_width, pot_height, cogl_texture_get_format (texture), &error); if (pot) break; else g_error_free (error); if (pot_width > pot_height) pot_width >>= 1; else pot_height >>= 1; if (!pot_width || !pot_height) break; } *pot_texture = COGL_TEXTURE (pot); if (!pot) return CAIRO_INT_STATUS_NO_MEMORY; /* Use the GPU to do a bilinear filtered scale from npot to pot... */ offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot)); error = NULL; if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) { /* NB: if we don't pass an error then Cogl is allowed to simply abort * automatically. */ g_error_free (error); cogl_object_unref (pot); *pot_texture = NULL; return CAIRO_INT_STATUS_NO_MEMORY; } cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen)); cogl_set_source_texture (texture); cogl_rectangle (-1, 1, 1, -1); cogl_pop_framebuffer (); cogl_object_unref (offscreen); } #endif /* NB: a reference for the texture is transferred to the caller which should * be unrefed */ static CoglTexture * _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface, cairo_surface_t *abstract_surface) { cairo_image_surface_t *image; cairo_image_surface_t *acquired_image = NULL; void *image_extra; CoglPixelFormat format; cairo_image_surface_t *image_clone = NULL; CoglTexture2D *texture; GError *error = NULL; cairo_surface_t *clone; if (abstract_surface->device == reference_surface->base.device) { cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; _cairo_cogl_surface_flush (surface, 0); return surface->texture ? cogl_object_ref (surface->texture) : NULL; } if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) { if (_cairo_surface_is_subsurface (abstract_surface)) { cairo_cogl_surface_t *surface; surface = (cairo_cogl_surface_t *) _cairo_surface_subsurface_get_target (abstract_surface); if (surface->base.device == reference_surface->base.device) return surface->texture ? cogl_object_ref (surface->texture) : NULL; } } clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend); if (clone) { cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone; return surface->texture ? cogl_object_ref (surface->texture) : NULL; } g_warning ("Uploading image surface to texture"); if (_cairo_surface_is_image (abstract_surface)) { image = (cairo_image_surface_t *)abstract_surface; } else { cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface, &acquired_image, &image_extra); if (unlikely (status)) { g_warning ("acquire_source_image failed: %s [%d]\n", cairo_status_to_string (status), status); return NULL; } image = acquired_image; } format = get_cogl_format_from_cairo_format (image->format); if (!format) { image_clone = _cairo_image_surface_coerce (image); if (unlikely (image_clone->base.status)) { g_warning ("image_surface_coerce failed"); texture = NULL; goto BAIL; } format = get_cogl_format_from_cairo_format (image_clone->format); assert (format); } texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context, image->width, image->height, format, /* incoming */ format, /* desired */ image->stride, image->data, &error); if (!texture) { g_warning ("Failed to allocate texture: %s", error->message); g_error_free (error); goto BAIL; } clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device), reference_surface->ignore_alpha, NULL, COGL_TEXTURE (texture)); _cairo_surface_attach_snapshot (abstract_surface, clone, NULL); /* Attaching the snapshot will take a reference on the clone surface... */ cairo_surface_destroy (clone); BAIL: if (image_clone) cairo_surface_destroy (&image_clone->base); if (acquired_image) _cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra); return COGL_TEXTURE (texture); } /* NB: a reference for the texture is transferred to the caller which should * be unrefed */ static CoglTexture * _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern, cairo_cogl_surface_t *destination, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, cairo_cogl_texture_attributes_t *attributes) { CoglTexture *texture = NULL; switch ((int)pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface; texture = _cairo_cogl_acquire_surface_texture (destination, surface); if (!texture) return NULL; /* XXX: determine if it would have no effect to change the * extend mode to EXTEND_PAD instead since we can simply map * EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader * tricks or extra border texels. */ #if 0 /* TODO: We still need to consider HW such as SGX which doesn't have * full support for NPOT textures. */ if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) { _cairo_cogl_get_pot_texture (); } #endif cairo_matrix_init_identity (&attributes->matrix); /* Convert from un-normalized source coordinates in backend * coordinates to normalized texture coordinates */ cairo_matrix_scale (&attributes->matrix, 1.0f / cogl_texture_get_width (texture), 1.0f / cogl_texture_get_height (texture)); /* XXX: need to multiply in the pattern->matrix */ attributes->extend = pattern->extend; attributes->filter = CAIRO_FILTER_BILINEAR; attributes->has_component_alpha = pattern->has_component_alpha; attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend); attributes->t_wrap = attributes->s_wrap; return texture; } case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: { cairo_surface_t *surface; cairo_matrix_t texture_matrix; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, extents->width, extents->height); if (_cairo_surface_offset_paint (surface, extents->x, extents->y, CAIRO_OPERATOR_SOURCE, pattern, NULL)) { cairo_surface_destroy (surface); return NULL; } texture = _cairo_cogl_acquire_surface_texture (destination, surface); if (!texture) goto BAIL; cairo_matrix_init_identity (&texture_matrix); /* Convert from un-normalized source coordinates in backend * coordinates to normalized texture coordinates */ cairo_matrix_scale (&texture_matrix, 1.0f / cogl_texture_get_width (texture), 1.0f / cogl_texture_get_height (texture)); cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y); attributes->matrix = texture_matrix; attributes->extend = pattern->extend; attributes->filter = CAIRO_FILTER_NEAREST; attributes->has_component_alpha = pattern->has_component_alpha; /* any pattern extend modes have already been dealt with... */ attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; attributes->t_wrap = attributes->s_wrap; BAIL: cairo_surface_destroy (surface); return texture; } case CAIRO_PATTERN_TYPE_LINEAR: { cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern; cairo_cogl_linear_gradient_t *gradient; cairo_cogl_linear_texture_entry_t *linear_texture; cairo_int_status_t status; float a, b; float dist; float scale; float angle; status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device), pattern->extend, linear_pattern->base.n_stops, linear_pattern->base.stops, &gradient); if (unlikely (status)) return NULL; linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend); attributes->extend = pattern->extend; attributes->filter = CAIRO_FILTER_BILINEAR; attributes->has_component_alpha = pattern->has_component_alpha; attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend); attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT; cairo_matrix_init_identity (&attributes->matrix); a = linear_pattern->pd2.x - linear_pattern->pd1.x; b = linear_pattern->pd2.y - linear_pattern->pd1.y; dist = sqrtf (a*a + b*b); scale = 1.0f / dist; angle = - atan2f (b, a); cairo_matrix_rotate (&attributes->matrix, angle); cairo_matrix_scale (&attributes->matrix, scale, scale); cairo_matrix_translate (&attributes->matrix, -linear_pattern->pd1.x, -linear_pattern->pd1.y); /* XXX: this caught me out: cairo doesn't follow the standard * maths convention for multiplying two matrices A x B - cairo * does B x A so the final matrix is as if A's transforms were * applied first. */ cairo_matrix_multiply (&attributes->matrix, &pattern->matrix, &attributes->matrix); return cogl_object_ref (linear_texture->texture); } default: g_warning ("Un-supported source type"); return NULL; } } static void set_layer_texture_with_attributes (CoglPipeline *pipeline, int layer_index, CoglTexture *texture, cairo_cogl_texture_attributes_t *attributes) { cogl_pipeline_set_layer_texture (pipeline, layer_index, texture); if (!_cairo_matrix_is_identity (&attributes->matrix)) { cairo_matrix_t *m = &attributes->matrix; float texture_matrixfv[16] = { m->xx, m->yx, 0, 0, m->xy, m->yy, 0, 0, 0, 0, 1, 0, m->x0, m->y0, 0, 1 }; CoglMatrix texture_matrix; cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv); cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix); } if (attributes->s_wrap != attributes->t_wrap) { cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap); cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap); } else cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap); } static CoglPipeline * get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask, const cairo_pattern_t *source, cairo_operator_t op, cairo_cogl_surface_t *destination, cairo_composite_rectangles_t *extents) { cairo_cogl_template_type template_type; CoglPipeline *pipeline; switch ((int)source->type) { case CAIRO_PATTERN_TYPE_SOLID: template_type = mask ? CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID; break; case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: template_type = mask ? CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE; break; default: g_warning ("Un-supported source type"); return NULL; } pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]); if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; cogl_pipeline_set_color4f (pipeline, solid_pattern->color.red * solid_pattern->color.alpha, solid_pattern->color.green * solid_pattern->color.alpha, solid_pattern->color.blue * solid_pattern->color.alpha, solid_pattern->color.alpha); } else { cairo_cogl_texture_attributes_t attributes; CoglTexture *texture = _cairo_cogl_acquire_pattern_texture (source, destination, &extents->bounded, &extents->source_sample_area, &attributes); if (!texture) goto BAIL; set_layer_texture_with_attributes (pipeline, 0, texture, &attributes); cogl_object_unref (texture); } if (mask) { if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask; CoglColor color; cogl_color_init_from_4f (&color, solid_pattern->color.red * solid_pattern->color.alpha, solid_pattern->color.green * solid_pattern->color.alpha, solid_pattern->color.blue * solid_pattern->color.alpha, solid_pattern->color.alpha); cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); } else { cairo_cogl_texture_attributes_t attributes; CoglTexture *texture = _cairo_cogl_acquire_pattern_texture (mask, destination, &extents->bounded, &extents->mask_sample_area, &attributes); if (!texture) goto BAIL; set_layer_texture_with_attributes (pipeline, 1, texture, &attributes); cogl_object_unref (texture); } } return pipeline; BAIL: cogl_object_unref (pipeline); return NULL; } #if 0 CoglPrimitive * _cairo_cogl_rectangle_new_p2t2t2 (float x, float y, float width, float height) { CoglVertexP2 vertices[] = { {x, y}, {x, y + height}, {x + width, y + height}, {x, y}, {x + width, y + height}, {x + width, y} }; CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices)); CoglAttribute *pos = cogl_attribute_new (buffer, "cogl_position_in", sizeof (CoglVertexP2), 0, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2), 0, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2), 0, 2, COGL_ATTRIBUTE_TYPE_FLOAT); CoglPrimitive *prim; cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices)); /* The attributes will now keep the buffer alive... */ cogl_object_unref (buffer); prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, 6, pos, tex_coords, NULL); /* The primitive will now keep the attribute alive... */ cogl_object_unref (pos); return prim; } #endif static void _cairo_cogl_log_clip (cairo_cogl_surface_t *surface, const cairo_clip_t *clip) { if (!_cairo_clip_equal (clip, surface->last_clip)) { _cairo_cogl_journal_log_clip (surface, clip); _cairo_clip_destroy (surface->last_clip); surface->last_clip = _cairo_clip_copy (clip); } } static void _cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface, cairo_composite_rectangles_t *composite) { cairo_clip_t *clip = composite->clip; if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) clip = NULL; if (clip == NULL) { if (_cairo_composite_rectangles_can_reduce_clip (composite, surface->last_clip)) return; } _cairo_cogl_log_clip (surface, clip); } static cairo_bool_t is_operator_supported (cairo_operator_t op) { switch ((int)op) { case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_ADD: return TRUE; default: return FALSE; } } static cairo_int_status_t _cairo_cogl_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_cogl_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_status_t status; CoglPipeline *pipeline; cairo_matrix_t identity; /* XXX: Use this to smoke test the acquire_source/dest_image fallback * paths... */ //return CAIRO_INT_STATUS_UNSUPPORTED; if (!is_operator_supported (op)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_composite_rectangles_init_for_mask (&extents, &surface->base, op, source, mask, clip); if (unlikely (status)) return status; pipeline = get_source_mask_operator_destination_pipeline (mask, source, op, surface, &extents); if (!pipeline){ status = CAIRO_INT_STATUS_UNSUPPORTED; goto BAIL; } _cairo_cogl_maybe_log_clip (surface, &extents); cairo_matrix_init_identity (&identity); _cairo_cogl_journal_log_rectangle (surface, pipeline, extents.bounded.x, extents.bounded.y, extents.bounded.width, extents.bounded.height, 2, &identity); /* The journal will take a reference on the pipeline and clip_path... */ cogl_object_unref (pipeline); BAIL: return status; } static int _cairo_cogl_source_n_layers (const cairo_pattern_t *source) { switch ((int)source->type) { case CAIRO_PATTERN_TYPE_SOLID: return 0; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_SURFACE: return 1; default: g_warning ("Unsupported source type"); return 0; } } static cairo_bool_t _cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b) { const cairo_cogl_path_fill_meta_t *meta0 = key_a; const cairo_cogl_path_fill_meta_t *meta1 = key_b; return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path); } static cairo_bool_t _cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a, const cairo_stroke_style_t *b) { if (a->line_width == b->line_width && a->line_cap == b->line_cap && a->line_join == b->line_join && a->miter_limit == b->miter_limit && a->num_dashes == b->num_dashes && a->dash_offset == b->dash_offset) { unsigned int i; for (i = 0; i < a->num_dashes; i++) { if (a->dash[i] != b->dash[i]) return FALSE; } } return TRUE; } static cairo_bool_t _cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b) { const cairo_cogl_path_stroke_meta_t *meta0 = key_a; const cairo_cogl_path_stroke_meta_t *meta1 = key_b; return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) && _cairo_path_fixed_equal (meta0->user_path, meta1->user_path); } static cairo_cogl_path_stroke_meta_t * _cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); _cairo_reference_count_inc (&meta->ref_count); return meta; } static void _cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); if (! _cairo_reference_count_dec_and_test (&meta->ref_count)) return; _cairo_path_fixed_fini (meta->user_path); free (meta->user_path); _cairo_stroke_style_fini (&meta->style); if (meta->prim) cogl_object_unref (meta->prim); free (meta); } static cairo_cogl_path_stroke_meta_t * _cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t *ctx, unsigned long hash, cairo_path_fixed_t *user_path, const cairo_stroke_style_t *style, double tolerance) { cairo_cogl_path_stroke_meta_t *ret; cairo_cogl_path_stroke_meta_t lookup; lookup.cache_entry.hash = hash; lookup.user_path = user_path; lookup.style = *style; lookup.tolerance = tolerance; ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry); if (!ret) ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry); return ret; } static void _cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface, cairo_cogl_path_stroke_meta_t *meta, size_t size) { /* now that we know the meta structure is associated with a primitive * we promote it from the staging cache into the primitive cache. */ /* XXX: _cairo_cache borks if you try and remove an entry that's already * been evicted so we explicitly look it up first... */ if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) { _cairo_cogl_path_stroke_meta_reference (meta); _cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry); } meta->cache_entry.size = size; if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) != CAIRO_STATUS_SUCCESS) _cairo_cogl_path_stroke_meta_destroy (meta); } static unsigned int _cairo_cogl_stroke_style_hash (unsigned int hash, const cairo_stroke_style_t *style) { unsigned int i; hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width)); hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap)); hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join)); hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit)); hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes)); hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset)); for (i = 0; i < style->num_dashes; i++) hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double)); return hash; } static cairo_cogl_path_stroke_meta_t * _cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface, const cairo_stroke_style_t *style, double tolerance) { unsigned long hash; cairo_cogl_path_stroke_meta_t *meta = NULL; cairo_path_fixed_t *meta_path = NULL; cairo_status_t status; if (!surface->user_path) return NULL; hash = _cairo_path_fixed_hash (surface->user_path); hash = _cairo_cogl_stroke_style_hash (hash, style); hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance)); meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash, surface->user_path, style, tolerance); if (meta) return meta; meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t)); if (!meta) goto BAIL; CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1); meta->cache_entry.hash = hash; meta->counter = 0; meta_path = malloc (sizeof (cairo_path_fixed_t)); if (!meta_path) goto BAIL; /* FIXME: we should add a ref-counted wrapper for our user_paths * so we don't have to keep copying them here! */ status = _cairo_path_fixed_init_copy (meta_path, surface->user_path); if (unlikely (status)) goto BAIL; meta->user_path = meta_path; meta->ctm_inverse = *surface->ctm_inverse; status = _cairo_stroke_style_init_copy (&meta->style, style); if (unlikely (status)) { _cairo_path_fixed_fini (meta_path); goto BAIL; } meta->tolerance = tolerance; return meta; BAIL: free (meta_path); free (meta); return NULL; } static cairo_int_status_t _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, int n_layers, cairo_bool_t one_shot, CoglPrimitive **primitive, size_t *size) { cairo_traps_t traps; cairo_int_status_t status; _cairo_traps_init (&traps); status = _cairo_path_fixed_stroke_polygon_to_traps (path, style, ctm, ctm_inverse, tolerance, &traps); if (unlikely (status)) goto BAIL; if (traps.num_traps == 0) { status = CAIRO_INT_STATUS_NOTHING_TO_DO; goto BAIL; } *size = traps.num_traps * sizeof (CoglVertexP2) * 6; //g_print ("new stroke prim\n"); *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot); if (!*primitive) { status = CAIRO_INT_STATUS_NO_MEMORY; goto BAIL; } BAIL: _cairo_traps_fini (&traps); return status; } static cairo_int_status_t _cairo_cogl_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; cairo_composite_rectangles_t extents; CoglPipeline *pipeline; cairo_status_t status; #ifdef ENABLE_PATH_CACHE cairo_cogl_path_stroke_meta_t *meta = NULL; cairo_matrix_t transform_matrix; #endif cairo_matrix_t *transform = NULL; gboolean one_shot = TRUE; CoglPrimitive *prim = NULL; cairo_bool_t new_prim = FALSE; if (! is_operator_supported (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* FIXME - support unbounded operators */ if (!_cairo_operator_bounded_by_mask (op)) { /* Currently IN this is the only unbounded operator we aim to support * in cairo-cogl. */ assert (op == CAIRO_OPERATOR_IN); g_warning ("FIXME: handle stroking with unbounded operators!"); return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_composite_rectangles_init_for_stroke (&extents, &surface->base, op, source, path, style, ctm, clip); if (unlikely (status)) return status; #ifdef ENABLE_PATH_CACHE /* FIXME: we are currently leaking the meta state if we don't reach * the cache_insert at the end. */ meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance); if (meta) { prim = meta->prim; if (prim) { cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm); transform = &transform_matrix; } else if (meta->counter++ > 10) one_shot = FALSE; } #endif if (!prim) { int n_layers = _cairo_cogl_source_n_layers (source); size_t prim_size = 0; status = _cairo_cogl_stroke_to_primitive (surface, path, style, ctm, ctm_inverse, tolerance, n_layers, one_shot, &prim, &prim_size); if (unlikely (status)) return status; new_prim = TRUE; #if defined (ENABLE_PATH_CACHE) if (meta) { meta->prim = cogl_object_ref (prim); _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size); } #endif } pipeline = get_source_mask_operator_destination_pipeline (NULL, source, op, surface, &extents); if (!pipeline) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_cogl_maybe_log_clip (surface, &extents); _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform); /* The journal will take a reference on the pipeline and primitive... */ cogl_object_unref (pipeline); if (new_prim) cogl_object_unref (prim); return CAIRO_INT_STATUS_SUCCESS; } static cairo_cogl_path_fill_meta_t * _cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); _cairo_reference_count_inc (&meta->ref_count); return meta; } static void _cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count)); if (! _cairo_reference_count_dec_and_test (&meta->ref_count)) return; _cairo_path_fixed_fini (meta->user_path); free (meta->user_path); if (meta->prim) cogl_object_unref (meta->prim); free (meta); } static cairo_cogl_path_fill_meta_t * _cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t *ctx, unsigned long hash, cairo_path_fixed_t *user_path) { cairo_cogl_path_fill_meta_t *ret; cairo_cogl_path_fill_meta_t lookup; lookup.cache_entry.hash = hash; lookup.user_path = user_path; ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry); if (!ret) ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry); return ret; } static void _cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface, cairo_cogl_path_fill_meta_t *meta, size_t size) { /* now that we know the meta structure is associated with a primitive * we promote it from the staging cache into the primitive cache. */ /* XXX: _cairo_cache borks if you try and remove an entry that's already * been evicted so we explicitly look it up first... */ if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) { _cairo_cogl_path_fill_meta_reference (meta); _cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry); } meta->cache_entry.size = size; if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) != CAIRO_STATUS_SUCCESS) _cairo_cogl_path_fill_meta_destroy (meta); } static cairo_cogl_path_fill_meta_t * _cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface) { unsigned long hash; cairo_cogl_path_fill_meta_t *meta = NULL; cairo_path_fixed_t *meta_path = NULL; cairo_status_t status; if (!surface->user_path) return NULL; hash = _cairo_path_fixed_hash (surface->user_path); meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device), hash, surface->user_path); if (meta) return meta; meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t)); if (!meta) goto BAIL; meta->cache_entry.hash = hash; meta->counter = 0; CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1); meta_path = malloc (sizeof (cairo_path_fixed_t)); if (!meta_path) goto BAIL; /* FIXME: we should add a ref-counted wrapper for our user_paths * so we don't have to keep copying them here! */ status = _cairo_path_fixed_init_copy (meta_path, surface->user_path); if (unlikely (status)) goto BAIL; meta->user_path = meta_path; meta->ctm_inverse = *surface->ctm_inverse; /* To start with - until we associate a CoglPrimitive with the meta * structure - we keep the meta in a staging structure until we * see whether it actually gets re-used. */ meta->cache_entry.size = 1; if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) != CAIRO_STATUS_SUCCESS) _cairo_cogl_path_fill_meta_destroy (meta); return meta; BAIL: free (meta_path); free (meta); return NULL; } static cairo_int_status_t _cairo_cogl_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_cogl_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_status_t status; #ifdef ENABLE_PATH_CACHE cairo_cogl_path_fill_meta_t *meta = NULL; cairo_matrix_t transform_matrix; #endif cairo_matrix_t *transform = NULL; cairo_bool_t one_shot = TRUE; CoglPrimitive *prim = NULL; cairo_bool_t new_prim = FALSE; CoglPipeline *pipeline; if (! is_operator_supported (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* FIXME - support unbounded operators */ if (!_cairo_operator_bounded_by_mask (op)) { /* Currently IN this is the only unbounded operator we aim to support * in cairo-cogl. */ assert (op == CAIRO_OPERATOR_IN); g_warning ("FIXME: handle filling with unbounded operators!"); return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_composite_rectangles_init_for_fill (&extents, &surface->base, op, source, path, clip); if (unlikely (status)) return status; #ifndef FILL_WITH_COGL_PATH #ifdef ENABLE_PATH_CACHE meta = _cairo_cogl_get_path_fill_meta (surface); if (meta) { prim = meta->prim; if (prim) { cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm); transform = &transform_matrix; } else if (meta->counter++ > 10) one_shot = FALSE; } #endif /* ENABLE_PATH_CACHE */ if (!prim) { int n_layers = _cairo_cogl_source_n_layers (source); size_t prim_size; status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance, one_shot, n_layers, &prim, &prim_size); if (unlikely (status)) return status; new_prim = TRUE; #ifdef ENABLE_PATH_CACHE if (meta) { meta->prim = cogl_object_ref (prim); _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size); } #endif /* ENABLE_PATH_CACHE */ } #endif /* !FILL_WITH_COGL_PATH */ pipeline = get_source_mask_operator_destination_pipeline (NULL, source, op, surface, &extents); if (!pipeline) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_cogl_maybe_log_clip (surface, &extents); #ifndef FILL_WITH_COGL_PATH _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform); /* The journal will take a reference on the prim */ if (new_prim) cogl_object_unref (prim); #else CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance); _cairo_cogl_journal_log_path (surface, pipeline, cogl_path); cogl_object_unref (cogl_path); #endif /* The journal will take a reference on the pipeline... */ cogl_object_unref (pipeline); return CAIRO_INT_STATUS_SUCCESS; } cairo_int_status_t _cairo_cogl_surface_fill_rectangle (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, double x, double y, double width, double height, cairo_matrix_t *ctm, const cairo_clip_t *clip) { cairo_cogl_surface_t *surface = abstract_surface; CoglPipeline *pipeline; if (! is_operator_supported (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* FIXME - support unbounded operators */ if (!_cairo_operator_bounded_by_mask (op)) { /* Currently IN this is the only unbounded operator we aim to support * in cairo-cogl. */ assert (op == CAIRO_OPERATOR_IN); g_warning ("FIXME: handle filling with unbounded operators!"); return CAIRO_INT_STATUS_UNSUPPORTED; } /* FIXME */ #if 0 status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents, &surface->base, op, source, path, clip); if (unlikely (status)) return status; #endif if (source->type == CAIRO_PATTERN_TYPE_SOLID) { double x1 = x; double y1 = y; double x2 = x1 + width; double y2 = y1 + height; pipeline = get_source_mask_operator_destination_pipeline (NULL, source, op, surface, NULL); if (!pipeline) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_cogl_log_clip (surface, clip); _cairo_cogl_journal_log_rectangle (surface, pipeline, x1, y1, x2, y2, 0, ctm); return CAIRO_INT_STATUS_SUCCESS; } else return CAIRO_INT_STATUS_UNSUPPORTED; /* TODO: * We need to acquire the textures here, look at the corresponding * attributes and see if this can be trivially handled by logging * a textured rectangle only needing simple scaling or translation * of texture coordinates. * * At this point we should also aim to remap the default * EXTEND_NONE mode to EXTEND_PAD which is more efficient if we * know it makes no difference either way since we can map that to * CLAMP_TO_EDGE. */ } static cairo_int_status_t _cairo_cogl_surface_show_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { return CAIRO_INT_STATUS_UNSUPPORTED; } const cairo_surface_backend_t _cairo_cogl_surface_backend = { CAIRO_SURFACE_TYPE_COGL, _cairo_cogl_surface_finish, #ifdef NEED_COGL_CONTEXT _cairo_cogl_context_create, #else _cairo_default_context_create, #endif _cairo_cogl_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, _cairo_cogl_surface_acquire_source_image, _cairo_cogl_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_cogl_surface_get_extents, NULL, /* get_font_options */ _cairo_cogl_surface_flush, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_cogl_surface_paint, _cairo_cogl_surface_mask, _cairo_cogl_surface_stroke, _cairo_cogl_surface_fill, NULL, /* fill_stroke*/ _cairo_surface_fallback_glyphs, }; static cairo_surface_t * _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, cairo_bool_t ignore_alpha, CoglFramebuffer *framebuffer, CoglTexture *texture) { cairo_cogl_surface_t *surface; cairo_status_t status; status = cairo_device_acquire (&dev->base); if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = malloc (sizeof (cairo_cogl_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->ignore_alpha = ignore_alpha; surface->framebuffer = framebuffer; if (framebuffer) { surface->width = cogl_framebuffer_get_width (framebuffer); surface->height = cogl_framebuffer_get_height (framebuffer); surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer); cogl_object_ref (framebuffer); } /* FIXME: If texture == NULL and we are given an offscreen framebuffer * then we want a way to poke inside the framebuffer to get a texture */ surface->texture = texture; if (texture) { if (!framebuffer) { surface->width = cogl_texture_get_width (texture); surface->height = cogl_texture_get_height (texture); surface->cogl_format = cogl_texture_get_format (texture); } cogl_object_ref (texture); } assert(surface->width && surface->height); surface->journal = NULL; surface->buffer_stack = NULL; surface->buffer_stack_size = 4096; surface->last_clip = NULL; surface->n_clip_updates_per_frame = 0; _cairo_surface_init (&surface->base, &_cairo_cogl_surface_backend, &dev->base, CAIRO_CONTENT_COLOR_ALPHA); return &surface->base; } cairo_surface_t * cairo_cogl_surface_create (cairo_device_t *abstract_device, CoglFramebuffer *framebuffer) { cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device; if (abstract_device == NULL) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR); if (abstract_device->status) return _cairo_surface_create_in_error (abstract_device->status); if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL); } slim_hidden_def (cairo_cogl_surface_create); CoglFramebuffer * cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface) { cairo_cogl_surface_t *surface; if (abstract_surface->backend != &_cairo_cogl_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } surface = (cairo_cogl_surface_t *) abstract_surface; return surface->framebuffer; } slim_hidden_def (cairo_cogl_surface_get_framebuffer); CoglTexture * cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface) { cairo_cogl_surface_t *surface; if (abstract_surface->backend != &_cairo_cogl_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } surface = (cairo_cogl_surface_t *) abstract_surface; return surface->texture; } slim_hidden_def (cairo_cogl_surface_get_texture); static cairo_status_t _cairo_cogl_device_flush (void *device) { cairo_status_t status; status = cairo_device_acquire (device); if (unlikely (status)) return status; /* XXX: we don't need to flush Cogl here, we just need to flush * any batching we do of compositing primitives. */ cairo_device_release (device); return CAIRO_STATUS_SUCCESS; } static void _cairo_cogl_device_finish (void *device) { cairo_status_t status; status = cairo_device_acquire (device); if (unlikely (status)) return; /* XXX: Drop references to external resources */ cairo_device_release (device); } static void _cairo_cogl_device_destroy (void *device) { cairo_cogl_device_t *dev = device; /* FIXME: Free stuff! */ g_free (dev); } static const cairo_device_backend_t _cairo_cogl_device_backend = { CAIRO_DEVICE_TYPE_COGL, NULL, /* lock */ NULL, /* unlock */ _cairo_cogl_device_flush, _cairo_cogl_device_finish, _cairo_cogl_device_destroy, }; static cairo_bool_t set_blend (CoglPipeline *pipeline, const char *blend_string) { GError *error = NULL; if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) { g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string); g_error_free (error); return FALSE; } return TRUE; } static cairo_bool_t _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op) { cairo_bool_t status = FALSE; switch ((int)op) { case CAIRO_OPERATOR_SOURCE: status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)"); break; case CAIRO_OPERATOR_OVER: status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))"); break; case CAIRO_OPERATOR_IN: status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)"); break; case CAIRO_OPERATOR_DEST_OVER: status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)"); break; case CAIRO_OPERATOR_DEST_IN: status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])"); break; case CAIRO_OPERATOR_ADD: status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)"); break; } return status; } static void create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op) { CoglPipeline *base = cogl_pipeline_new (); CoglPipeline *pipeline; CoglColor color; if (!_cairo_cogl_setup_op_state (base, op)) { cogl_object_unref (base); return; } dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base; pipeline = cogl_pipeline_copy (base); cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture); dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline; pipeline = cogl_pipeline_copy (base); cogl_pipeline_set_layer_combine (pipeline, 1, "RGBA = MODULATE (PREVIOUS, CONSTANT[A])", NULL); cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture); dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline; pipeline = cogl_pipeline_copy (base); cogl_pipeline_set_layer_combine (pipeline, 1, "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", NULL); cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture); dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline; } cairo_device_t * cairo_cogl_device_create (CoglContext *cogl_context) { cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1); cairo_status_t status; dev->backend_vtable_initialized = FALSE; dev->cogl_context = cogl_context; dev->dummy_texture = cogl_texture_new_with_size (1, 1, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY); if (!dev->dummy_texture) goto ERROR; memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines)); create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE); create_templates_for_op (dev, CAIRO_OPERATOR_OVER); create_templates_for_op (dev, CAIRO_OPERATOR_IN); create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER); create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN); create_templates_for_op (dev, CAIRO_OPERATOR_ADD); status = _cairo_cache_init (&dev->linear_cache, _cairo_cogl_linear_gradient_equal, NULL, (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy, CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE); if (unlikely (status)) return _cairo_device_create_in_error(status); status = _cairo_cache_init (&dev->path_fill_staging_cache, _cairo_cogl_path_fill_meta_equal, NULL, (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy, 1000); status = _cairo_cache_init (&dev->path_stroke_staging_cache, _cairo_cogl_path_stroke_meta_equal, NULL, (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy, 1000); status = _cairo_cache_init (&dev->path_fill_prim_cache, _cairo_cogl_path_fill_meta_equal, NULL, (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy, CAIRO_COGL_PATH_META_CACHE_SIZE); status = _cairo_cache_init (&dev->path_stroke_prim_cache, _cairo_cogl_path_stroke_meta_equal, NULL, (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy, CAIRO_COGL_PATH_META_CACHE_SIZE); _cairo_device_init (&dev->base, &_cairo_cogl_device_backend); return &dev->base; ERROR: g_free (dev); return NULL; } slim_hidden_def (cairo_cogl_device_create); void cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface) { cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; cairo_surface_flush (abstract_surface); //g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame); surface->n_clip_updates_per_frame = 0; } slim_hidden_def (cairo_cogl_surface_end_frame); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-utils-private.h����������������������0000664�0000000�0000000�00000003625�12710376503�0027304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #ifndef CAIRO_COGL_UTILS_PRIVATE_H #define CAIRO_COGL_UTILS_PRIVATE_H #include "cairo-path-fixed-private.h" #include <cogl/cogl2-experimental.h> CoglPath * _cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, float tolerance); int _cairo_cogl_util_next_p2 (int a); #define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS)) static inline float _cairo_cogl_util_fixed_to_float (cairo_fixed_t f) { return ((float) f) / CAIRO_FIXED_ONE_FLOAT; } #endif /* CAIRO_COGL_UTILS_PRIVATE_H */ �����������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl-utils.c������������������������������0000664�0000000�0000000�00000007213�12710376503�0025624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #include "cairoint.h" #include "cairo-cogl-utils-private.h" #include <cogl/cogl.h> #include <glib.h> static cairo_status_t _cogl_move_to (void *closure, const cairo_point_t *point) { cogl_path_move_to (closure, _cairo_cogl_util_fixed_to_float (point->x), _cairo_cogl_util_fixed_to_float (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cogl_line_to (void *closure, const cairo_point_t *point) { cogl_path_line_to (closure, _cairo_cogl_util_fixed_to_float (point->x), _cairo_cogl_util_fixed_to_float (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cogl_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { cogl_path_curve_to (closure, _cairo_cogl_util_fixed_to_float (p0->x), _cairo_cogl_util_fixed_to_float (p0->y), _cairo_cogl_util_fixed_to_float (p1->x), _cairo_cogl_util_fixed_to_float (p1->y), _cairo_cogl_util_fixed_to_float (p2->x), _cairo_cogl_util_fixed_to_float (p2->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cogl_close_path (void *closure) { cogl_path_close (closure); return CAIRO_STATUS_SUCCESS; } CoglPath * _cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, float tolerance) { CoglPath *cogl_path = cogl_path_new (); cairo_status_t status; if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD) cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_EVEN_ODD); else cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_NON_ZERO); #ifdef USE_CAIRO_PATH_FLATTENER /* XXX: rely on cairo to do path flattening, since it seems Cogl's * curve_to flattening is much slower */ status = _cairo_path_fixed_interpret_flat (path, _cogl_move_to, _cogl_line_to, _cogl_close_path, cogl_path, tolerance); #else status = _cairo_path_fixed_interpret (path, _cogl_move_to, _cogl_line_to, _cogl_curve_to, _cogl_close_path, cogl_path); #endif assert (status == CAIRO_STATUS_SUCCESS); return cogl_path; } int _cairo_cogl_util_next_p2 (int a) { int rval = 1; while (rval < a) rval <<= 1; return rval; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-cogl.h������������������������������������0000664�0000000�0000000�00000004370�12710376503�0024474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): * Robert Bragg <robert@linux.intel.com> */ #ifndef CAIRO_VG_H #define CAIRO_VG_H #include "cairo.h" #if CAIRO_HAS_COGL_SURFACE #include <cogl/cogl2-experimental.h> CAIRO_BEGIN_DECLS cairo_public cairo_device_t * cairo_cogl_device_create (CoglContext *context); cairo_public cairo_surface_t * cairo_cogl_surface_create (cairo_device_t *device, CoglFramebuffer *framebuffer); cairo_public CoglFramebuffer * cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface); cairo_public CoglTexture * cairo_cogl_surface_get_texture (cairo_surface_t *surface); cairo_public void cairo_cogl_surface_end_frame (cairo_surface_t *surface); CAIRO_END_DECLS #else /* CAIRO_HAS_COGL_SURFACE*/ # error Cairo was not compiled with support for the Cogl backend #endif /* CAIRO_HAS_COGL_SURFACE*/ #endif /* CAIRO_COGL_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-color.c�����������������������������������0000664�0000000�0000000�00000012747�12710376503�0024670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" static cairo_color_t const cairo_color_white = { 1.0, 1.0, 1.0, 1.0, 0xffff, 0xffff, 0xffff, 0xffff }; static cairo_color_t const cairo_color_black = { 0.0, 0.0, 0.0, 1.0, 0x0, 0x0, 0x0, 0xffff }; static cairo_color_t const cairo_color_transparent = { 0.0, 0.0, 0.0, 0.0, 0x0, 0x0, 0x0, 0x0 }; static cairo_color_t const cairo_color_magenta = { 1.0, 0.0, 1.0, 1.0, 0xffff, 0x0, 0xffff, 0xffff }; const cairo_color_t * _cairo_stock_color (cairo_stock_t stock) { switch (stock) { case CAIRO_STOCK_WHITE: return &cairo_color_white; case CAIRO_STOCK_BLACK: return &cairo_color_black; case CAIRO_STOCK_TRANSPARENT: return &cairo_color_transparent; case CAIRO_STOCK_NUM_COLORS: default: ASSERT_NOT_REACHED; /* If the user can get here somehow, give a color that indicates a * problem. */ return &cairo_color_magenta; } } /* Convert a double in [0.0, 1.0] to an integer in [0, 65535] * The conversion is designed to divide the input range into 65536 * equally-sized regions. This is achieved by multiplying by 65536 and * then special-casing the result of an input value of 1.0 so that it * maps to 65535 instead of 65536. */ uint16_t _cairo_color_double_to_short (double d) { uint32_t i; i = (uint32_t) (d * 65536); i -= (i >> 16); return i; } static void _cairo_color_compute_shorts (cairo_color_t *color) { color->red_short = _cairo_color_double_to_short (color->red * color->alpha); color->green_short = _cairo_color_double_to_short (color->green * color->alpha); color->blue_short = _cairo_color_double_to_short (color->blue * color->alpha); color->alpha_short = _cairo_color_double_to_short (color->alpha); } void _cairo_color_init_rgba (cairo_color_t *color, double red, double green, double blue, double alpha) { color->red = red; color->green = green; color->blue = blue; color->alpha = alpha; _cairo_color_compute_shorts (color); } void _cairo_color_multiply_alpha (cairo_color_t *color, double alpha) { color->alpha *= alpha; _cairo_color_compute_shorts (color); } void _cairo_color_get_rgba (cairo_color_t *color, double *red, double *green, double *blue, double *alpha) { *red = color->red; *green = color->green; *blue = color->blue; *alpha = color->alpha; } void _cairo_color_get_rgba_premultiplied (cairo_color_t *color, double *red, double *green, double *blue, double *alpha) { *red = color->red * color->alpha; *green = color->green * color->alpha; *blue = color->blue * color->alpha; *alpha = color->alpha; } /* NB: This function works both for unmultiplied and premultiplied colors */ cairo_bool_t _cairo_color_equal (const cairo_color_t *color_a, const cairo_color_t *color_b) { if (color_a == color_b) return TRUE; if (color_a->alpha_short != color_b->alpha_short) return FALSE; if (color_a->alpha_short == 0) return TRUE; return color_a->red_short == color_b->red_short && color_a->green_short == color_b->green_short && color_a->blue_short == color_b->blue_short; } cairo_bool_t _cairo_color_stop_equal (const cairo_color_stop_t *color_a, const cairo_color_stop_t *color_b) { if (color_a == color_b) return TRUE; return color_a->alpha_short == color_b->alpha_short && color_a->red_short == color_b->red_short && color_a->green_short == color_b->green_short && color_a->blue_short == color_b->blue_short; } cairo_content_t _cairo_color_get_content (const cairo_color_t *color) { if (CAIRO_COLOR_IS_OPAQUE (color)) return CAIRO_CONTENT_COLOR; if (color->red_short == 0 && color->green_short == 0 && color->blue_short == 0) { return CAIRO_CONTENT_ALPHA; } return CAIRO_CONTENT_COLOR_ALPHA; } �������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-combsort-inline.h�������������������������0000664�0000000�0000000�00000005412�12710376503�0026652�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ /* This fragment implements a comb sort (specifically combsort11) */ #ifndef _HAVE_CAIRO_COMBSORT_NEWGAP #define _HAVE_CAIRO_COMBSORT_NEWGAP static inline unsigned int _cairo_combsort_newgap (unsigned int gap) { gap = 10 * gap / 13; if (gap == 9 || gap == 10) gap = 11; if (gap < 1) gap = 1; return gap; } #endif #define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \ static void \ NAME (TYPE *base, unsigned int nmemb) \ { \ unsigned int gap = nmemb; \ unsigned int i, j; \ int swapped; \ do { \ gap = _cairo_combsort_newgap (gap); \ swapped = gap > 1; \ for (i = 0; i < nmemb-gap ; i++) { \ j = i + gap; \ if (CMP (base[i], base[j]) > 0 ) { \ TYPE tmp; \ tmp = base[i]; \ base[i] = base[j]; \ base[j] = tmp; \ swapped = 1; \ } \ } \ } while (swapped); \ } #define CAIRO_COMBSORT_DECLARE_WITH_DATA(NAME, TYPE, CMP) \ static void \ NAME (TYPE *base, unsigned int nmemb, void *data) \ { \ unsigned int gap = nmemb; \ unsigned int i, j; \ int swapped; \ do { \ gap = _cairo_combsort_newgap (gap); \ swapped = gap > 1; \ for (i = 0; i < nmemb-gap ; i++) { \ j = i + gap; \ if (CMP (base[i], base[j], data) > 0 ) { \ TYPE tmp; \ tmp = base[i]; \ base[i] = base[j]; \ base[j] = tmp; \ swapped = 1; \ } \ } \ } while (swapped); \ } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-compiler-private.h������������������������0000664�0000000�0000000�00000022231�12710376503�0027026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_COMPILER_PRIVATE_H #define CAIRO_COMPILER_PRIVATE_H #include "cairo.h" #if HAVE_CONFIG_H #include "config.h" #endif /* Size in bytes of buffer to use off the stack per functions. * Mostly used by text functions. For larger allocations, they'll * malloc(). */ #ifndef CAIRO_STACK_BUFFER_SIZE #define CAIRO_STACK_BUFFER_SIZE (512 * sizeof (int)) #endif #define CAIRO_STACK_ARRAY_LENGTH(T) (CAIRO_STACK_BUFFER_SIZE / sizeof(T)) /* * The goal of this block is to define the following macros for * providing faster linkage to functions in the public API for calls * from within cairo. * * slim_hidden_proto(f) * slim_hidden_proto_no_warn(f) * * Declares `f' as a library internal function and hides the * function from the global symbol table. This macro must be * expanded after `f' has been declared with a prototype but before * any calls to the function are seen by the compiler. The no_warn * variant inhibits warnings about the return value being unused at * call sites. The macro works by renaming `f' to an internal name * in the symbol table and hiding that. As far as cairo internal * calls are concerned they're calling a library internal function * and thus don't need to bounce via the PLT. * * slim_hidden_def(f) * * Exports `f' back to the global symbol table. This macro must be * expanded right after the function definition and only for symbols * hidden previously with slim_hidden_proto(). The macro works by * adding a global entry to the symbol table which points at the * internal name of `f' created by slim_hidden_proto(). * * Functions in the public API which aren't called by the library * don't need to be hidden and re-exported using the slim hidden * macros. */ #if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun) # define slim_hidden_proto(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private # define slim_hidden_proto_no_warn(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private_no_warn # define slim_hidden_def(name) slim_hidden_def1(name, slim_hidden_int_name(name)) # define slim_hidden_int_name(name) INT_##name # define slim_hidden_proto1(name, internal) \ extern __typeof (name) name \ __asm__ (slim_hidden_asmname (internal)) # define slim_hidden_def1(name, internal) \ extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \ __attribute__((__alias__(slim_hidden_asmname(internal)))) # define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__) # define slim_hidden_ulp1(x) slim_hidden_ulp2(x) # define slim_hidden_ulp2(x) #x # define slim_hidden_asmname(name) slim_hidden_asmname1(name) # define slim_hidden_asmname1(name) slim_hidden_ulp #name #else # define slim_hidden_proto(name) int _cairo_dummy_prototype(void) # define slim_hidden_proto_no_warn(name) int _cairo_dummy_prototype(void) # define slim_hidden_def(name) int _cairo_dummy_prototype(void) #endif #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #define CAIRO_PRINTF_FORMAT(fmt_index, va_index) \ __attribute__((__format__(__printf__, fmt_index, va_index))) #else #define CAIRO_PRINTF_FORMAT(fmt_index, va_index) #endif /* slim_internal.h */ #define CAIRO_HAS_HIDDEN_SYMBOLS 1 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && \ (defined(__ELF__) || defined(__APPLE__)) && \ !defined(__sun) #define cairo_private_no_warn __attribute__((__visibility__("hidden"))) #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) #define cairo_private_no_warn __hidden #else /* not gcc >= 3.3 and not Sun Studio >= 8 */ #define cairo_private_no_warn #undef CAIRO_HAS_HIDDEN_SYMBOLS #endif #ifndef WARN_UNUSED_RESULT #define WARN_UNUSED_RESULT #endif /* Add attribute(warn_unused_result) if supported */ #define cairo_warn WARN_UNUSED_RESULT #define cairo_private cairo_private_no_warn cairo_warn /* This macro allow us to deprecate a function by providing an alias for the old function name to the new function name. With this macro, binary compatibility is preserved. The macro only works on some platforms --- tough. Meanwhile, new definitions in the public header file break the source code so that it will no longer link against the old symbols. Instead it will give a descriptive error message indicating that the old function has been deprecated by the new function. */ #if __GNUC__ >= 2 && defined(__ELF__) # define CAIRO_FUNCTION_ALIAS(old, new) \ extern __typeof (new) old \ __asm__ ("" #old) \ __attribute__((__alias__("" #new))) #else # define CAIRO_FUNCTION_ALIAS(old, new) #endif /* * Cairo uses the following function attributes in order to improve the * generated code (effectively by manual inter-procedural analysis). * * 'cairo_pure': The function is only allowed to read from its arguments * and global memory (i.e. following a pointer argument or * accessing a shared variable). The return value should * only depend on its arguments, and for an identical set of * arguments should return the same value. * * 'cairo_const': The function is only allowed to read from its arguments. * It is not allowed to access global memory. The return * value should only depend its arguments, and for an * identical set of arguments should return the same value. * This is currently the most strict function attribute. * * Both these function attributes allow gcc to perform CSE and * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents * do not change across the function call. */ #if __GNUC__ >= 3 #define cairo_pure __attribute__((pure)) #define cairo_const __attribute__((const)) #define cairo_always_inline inline __attribute__((always_inline)) #else #define cairo_pure #define cairo_const #define cairo_always_inline inline #endif #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) #define likely(expr) (__builtin_expect (!!(expr), 1)) #define unlikely(expr) (__builtin_expect (!!(expr), 0)) #else #define likely(expr) (expr) #define unlikely(expr) (expr) #endif #ifndef __GNUC__ #undef __attribute__ #define __attribute__(x) #endif #if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) #define access _access #define fdopen _fdopen #define hypot _hypot #define pclose _pclose #define popen _popen #define snprintf _snprintf #define strdup _strdup #define unlink _unlink #define vsnprintf _vsnprintf #endif #ifdef _MSC_VER #ifndef __cplusplus #undef inline #define inline __inline #endif #endif #if defined(_MSC_VER) && defined(_M_IX86) /* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and will never be folded into another one. Something like this might eventually be needed for GCC but it seems fine for now. */ #define CAIRO_ENSURE_UNIQUE \ do { \ char func[] = __FUNCTION__; \ char file[] = __FILE__; \ __asm { \ __asm jmp __internal_skip_line_no \ __asm _emit (__LINE__ & 0xff) \ __asm _emit ((__LINE__>>8) & 0xff) \ __asm _emit ((__LINE__>>16) & 0xff) \ __asm _emit ((__LINE__>>24) & 0xff) \ __asm lea eax, func \ __asm lea eax, file \ __asm __internal_skip_line_no: \ }; \ } while (0) #else #define CAIRO_ENSURE_UNIQUE do { } while (0) #endif #ifdef __STRICT_ANSI__ #undef inline #define inline __inline__ #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-composite-rectangles-private.h������������0000664�0000000�0000000�00000013437�12710376503�0031353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.u> */ #ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H #define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H #include "cairo-types-private.h" #include "cairo-error-private.h" #include "cairo-pattern-private.h" CAIRO_BEGIN_DECLS /* Rectangles that take part in a composite operation. * * The source and mask track the extents of the respective patterns in device * space. The unbounded rectangle is essentially the clip rectangle. And the * intersection of all is the bounded rectangle, which is the minimum extents * the operation may require. Whether or not the operation is actually bounded * is tracked in the is_bounded boolean. * */ struct _cairo_composite_rectangles { cairo_surface_t *surface; cairo_operator_t op; cairo_rectangle_int_t source; cairo_rectangle_int_t mask; cairo_rectangle_int_t destination; cairo_rectangle_int_t bounded; /* source? IN mask? IN unbounded */ cairo_rectangle_int_t unbounded; /* destination IN clip */ uint32_t is_bounded; cairo_rectangle_int_t source_sample_area; cairo_rectangle_int_t mask_sample_area; cairo_pattern_union_t source_pattern; cairo_pattern_union_t mask_pattern; const cairo_pattern_t *original_source_pattern; const cairo_pattern_t *original_mask_pattern; cairo_clip_t *clip; /* clip will be reduced to the minimal container */ }; cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_boxes_t *boxes, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_polygon_t *polygon, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, const cairo_clip_t *clip, cairo_bool_t *overlap); cairo_private cairo_int_status_t _cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents, const cairo_box_t *box); cairo_private cairo_int_status_t _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents, const cairo_box_t *box); cairo_private cairo_bool_t _cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite, cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite, cairo_boxes_t *damage); cairo_private void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents); CAIRO_END_DECLS #endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-composite-rectangles.c��������������������0000664�0000000�0000000�00000040113�12710376503�0027665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-error-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-pattern-private.h" /* A collection of routines to facilitate writing compositors. */ void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents) { _cairo_clip_destroy (extents->clip); } static void _cairo_composite_reduce_pattern (const cairo_pattern_t *src, cairo_pattern_union_t *dst) { int tx, ty; _cairo_pattern_init_static_copy (&dst->base, src); if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID) return; dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL), tx = ty = 0; if (_cairo_matrix_is_pixman_translation (&dst->base.matrix, dst->base.filter, &tx, &ty)) { dst->base.matrix.x0 = tx; dst->base.matrix.y0 = ty; } } static inline cairo_bool_t _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { if (_cairo_clip_is_all_clipped (clip)) return FALSE; extents->surface = surface; extents->op = op; _cairo_surface_get_extents (surface, &extents->destination); extents->clip = NULL; extents->unbounded = extents->destination; if (clip && ! _cairo_rectangle_intersect (&extents->unbounded, _cairo_clip_get_extents (clip))) return FALSE; extents->bounded = extents->unbounded; extents->is_bounded = _cairo_operator_bounded_by_either (op); extents->original_source_pattern = source; _cairo_composite_reduce_pattern (source, &extents->source_pattern); _cairo_pattern_get_extents (&extents->source_pattern.base, &extents->source); if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) { if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source)) return FALSE; } extents->original_mask_pattern = NULL; extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID; extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */ extents->mask_pattern.solid.color.alpha_short = 0xffff; return TRUE; } cairo_int_status_t _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } extents->mask = extents->destination; extents->clip = _cairo_clip_reduce_for_composite (clip, extents); if (_cairo_clip_is_all_clipped (extents->clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (! _cairo_rectangle_intersect (&extents->unbounded, _cairo_clip_get_extents (extents->clip))) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) _cairo_pattern_sampled_area (&extents->source_pattern.base, &extents->bounded, &extents->source_sample_area); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents, const cairo_clip_t *clip) { cairo_bool_t ret; ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { extents->unbounded = extents->bounded; } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) return CAIRO_INT_STATUS_NOTHING_TO_DO; } extents->clip = _cairo_clip_reduce_for_composite (clip, extents); if (_cairo_clip_is_all_clipped (extents->clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (! _cairo_rectangle_intersect (&extents->unbounded, _cairo_clip_get_extents (extents->clip))) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (! _cairo_rectangle_intersect (&extents->bounded, _cairo_clip_get_extents (extents->clip)) && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) _cairo_pattern_sampled_area (&extents->source_pattern.base, &extents->bounded, &extents->source_sample_area); if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { _cairo_pattern_sampled_area (&extents->mask_pattern.base, &extents->bounded, &extents->mask_sample_area); if (extents->mask_sample_area.width == 0 || extents->mask_sample_area.height == 0) { _cairo_composite_rectangles_fini (extents); return CAIRO_INT_STATUS_NOTHING_TO_DO; } } return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents, const cairo_box_t *box) { cairo_rectangle_int_t rect; cairo_clip_t *clip; _cairo_box_round_to_rectangle (box, &rect); if (rect.x == extents->source.x && rect.y == extents->source.y && rect.width == extents->source.width && rect.height == extents->source.height) { return CAIRO_INT_STATUS_SUCCESS; } _cairo_rectangle_intersect (&extents->source, &rect); rect = extents->bounded; if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (rect.width == extents->bounded.width && rect.height == extents->bounded.height) return CAIRO_INT_STATUS_SUCCESS; if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { extents->unbounded = extents->bounded; } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) return CAIRO_INT_STATUS_NOTHING_TO_DO; } clip = extents->clip; extents->clip = _cairo_clip_reduce_for_composite (clip, extents); if (clip != extents->clip) _cairo_clip_destroy (clip); if (_cairo_clip_is_all_clipped (extents->clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (! _cairo_rectangle_intersect (&extents->unbounded, _cairo_clip_get_extents (extents->clip))) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) _cairo_pattern_sampled_area (&extents->source_pattern.base, &extents->bounded, &extents->source_sample_area); if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { _cairo_pattern_sampled_area (&extents->mask_pattern.base, &extents->bounded, &extents->mask_sample_area); if (extents->mask_sample_area.width == 0 || extents->mask_sample_area.height == 0) return CAIRO_INT_STATUS_NOTHING_TO_DO; } return CAIRO_INT_STATUS_SUCCESS; } cairo_int_status_t _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents, const cairo_box_t *box) { cairo_rectangle_int_t mask; cairo_clip_t *clip; _cairo_box_round_to_rectangle (box, &mask); if (mask.x == extents->mask.x && mask.y == extents->mask.y && mask.width == extents->mask.width && mask.height == extents->mask.height) { return CAIRO_INT_STATUS_SUCCESS; } _cairo_rectangle_intersect (&extents->mask, &mask); mask = extents->bounded; if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (mask.width == extents->bounded.width && mask.height == extents->bounded.height) return CAIRO_INT_STATUS_SUCCESS; if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { extents->unbounded = extents->bounded; } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) return CAIRO_INT_STATUS_NOTHING_TO_DO; } clip = extents->clip; extents->clip = _cairo_clip_reduce_for_composite (clip, extents); if (clip != extents->clip) _cairo_clip_destroy (clip); if (_cairo_clip_is_all_clipped (extents->clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (! _cairo_rectangle_intersect (&extents->unbounded, _cairo_clip_get_extents (extents->clip))) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) _cairo_pattern_sampled_area (&extents->source_pattern.base, &extents->bounded, &extents->source_sample_area); if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { _cairo_pattern_sampled_area (&extents->mask_pattern.base, &extents->bounded, &extents->mask_sample_area); if (extents->mask_sample_area.width == 0 || extents->mask_sample_area.height == 0) return CAIRO_INT_STATUS_NOTHING_TO_DO; } return CAIRO_INT_STATUS_SUCCESS; } cairo_int_status_t _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, cairo_surface_t*surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } extents->original_mask_pattern = mask; _cairo_composite_reduce_pattern (mask, &extents->mask_pattern); _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_path_fixed_approximate_fill_extents (path, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_polygon_t *polygon, const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_boxes_t *boxes, const cairo_clip_t *clip) { cairo_box_t box; if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_boxes_extents (boxes, &box); _cairo_box_round_to_rectangle (&box, &extents->mask); return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, const cairo_clip_t *clip, cairo_bool_t *overlap) { cairo_status_t status; if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; /* Computing the exact bbox and the overlap is expensive. * First perform a cheap test to see if the glyphs are all clipped out. */ if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK && _cairo_scaled_font_glyph_approximate_extents (scaled_font, glyphs, num_glyphs, &extents->mask)) { if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) return CAIRO_INT_STATUS_NOTHING_TO_DO; } status = _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, &extents->mask, overlap); if (unlikely (status)) return status; if (overlap && *overlap && scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE && _cairo_pattern_is_opaque_solid (&extents->source_pattern.base)) { *overlap = FALSE; } return _cairo_composite_rectangles_intersect (extents, clip); } cairo_bool_t _cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite, cairo_clip_t *clip) { cairo_rectangle_int_t extents; cairo_box_t box; if (clip == NULL) return TRUE; extents = composite->destination; if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) _cairo_rectangle_intersect (&extents, &composite->source); if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) _cairo_rectangle_intersect (&extents, &composite->mask); _cairo_box_from_rectangle (&box, &extents); return _cairo_clip_contains_box (clip, &box); } cairo_int_status_t _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite, cairo_boxes_t *damage) { cairo_int_status_t status; int n; for (n = 0; n < composite->clip->num_boxes; n++) { status = _cairo_boxes_add (damage, CAIRO_ANTIALIAS_NONE, &composite->clip->boxes[n]); if (unlikely (status)) return status; } return CAIRO_INT_STATUS_SUCCESS; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-compositor-private.h����������������������0000664�0000000�0000000�00000025134�12710376503�0027417�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_COMPOSITOR_PRIVATE_H #define CAIRO_COMPOSITOR_PRIVATE_H #include "cairo-composite-rectangles-private.h" CAIRO_BEGIN_DECLS typedef struct { cairo_scaled_font_t *font; cairo_glyph_t *glyphs; int num_glyphs; cairo_bool_t use_mask; cairo_rectangle_int_t extents; } cairo_composite_glyphs_info_t; struct cairo_compositor { const cairo_compositor_t *delegate; cairo_warn cairo_int_status_t (*paint) (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents); cairo_warn cairo_int_status_t (*mask) (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents); cairo_warn cairo_int_status_t (*stroke) (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias); cairo_warn cairo_int_status_t (*fill) (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias); cairo_warn cairo_int_status_t (*glyphs) (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap); }; struct cairo_mask_compositor { cairo_compositor_t base; cairo_int_status_t (*acquire) (void *surface); cairo_int_status_t (*release) (void *surface); cairo_int_status_t (*set_clip_region) (void *surface, cairo_region_t *clip_region); cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_int_status_t (*draw_image_boxes) (void *surface, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy); cairo_int_status_t (*copy_boxes) (void *surface, cairo_surface_t *src, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents, int dx, int dy); cairo_int_status_t (*fill_rectangles) (void *surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rectangles, int num_rects); cairo_int_status_t (*fill_boxes) (void *surface, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes); cairo_int_status_t (*check_composite) (const cairo_composite_rectangles_t *extents); cairo_int_status_t (*composite) (void *dst, cairo_operator_t op, cairo_surface_t *src, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height); cairo_int_status_t (*composite_boxes) (void *surface, cairo_operator_t op, cairo_surface_t *source, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents); cairo_int_status_t (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs); cairo_int_status_t (*composite_glyphs) (void *surface, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info); }; struct cairo_traps_compositor { cairo_compositor_t base; cairo_int_status_t (*acquire) (void *surface); cairo_int_status_t (*release) (void *surface); cairo_int_status_t (*set_clip_region) (void *surface, cairo_region_t *clip_region); cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_int_status_t (*draw_image_boxes) (void *surface, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy); cairo_int_status_t (*copy_boxes) (void *surface, cairo_surface_t *src, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents, int dx, int dy); cairo_int_status_t (*fill_boxes) (void *surface, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes); cairo_int_status_t (*check_composite) (const cairo_composite_rectangles_t *extents); cairo_int_status_t (*composite) (void *dst, cairo_operator_t op, cairo_surface_t *src, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height); cairo_int_status_t (*lerp) (void *_dst, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height); cairo_int_status_t (*composite_boxes) (void *surface, cairo_operator_t op, cairo_surface_t *source, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents); cairo_int_status_t (*composite_traps) (void *dst, cairo_operator_t op, cairo_surface_t *source, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_traps_t *traps); cairo_int_status_t (*composite_tristrip) (void *dst, cairo_operator_t op, cairo_surface_t *source, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_tristrip_t *tristrip); cairo_int_status_t (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs); cairo_int_status_t (*composite_glyphs) (void *surface, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info); }; cairo_private extern const cairo_compositor_t __cairo_no_compositor; cairo_private extern const cairo_compositor_t _cairo_fallback_compositor; cairo_private void _cairo_mask_compositor_init (cairo_mask_compositor_t *compositor, const cairo_compositor_t *delegate); cairo_private void _cairo_shape_mask_compositor_init (cairo_compositor_t *compositor, const cairo_compositor_t *delegate); cairo_private void _cairo_traps_compositor_init (cairo_traps_compositor_t *compositor, const cairo_compositor_t *delegate); cairo_private cairo_int_status_t _cairo_compositor_paint (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_compositor_mask (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_compositor_stroke (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_compositor_fill (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_compositor_glyphs (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); CAIRO_END_DECLS #endif /* CAIRO_COMPOSITOR_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-compositor.c������������������������������0000664�0000000�0000000�00000020113�12710376503�0025732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" #include "cairo-damage-private.h" #include "cairo-error-private.h" cairo_int_status_t _cairo_compositor_paint (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_composite_rectangles_init_for_paint (&extents, surface, op, source, clip); if (unlikely (status)) return status; do { while (compositor->paint == NULL) compositor = compositor->delegate; status = compositor->paint (compositor, &extents); compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", __FUNCTION__, extents.unbounded.x, extents.unbounded.y, extents.unbounded.width, extents.unbounded.height)); surface->damage = _cairo_damage_add_rectangle (surface->damage, &extents.unbounded); } _cairo_composite_rectangles_fini (&extents); return status; } cairo_int_status_t _cairo_compositor_mask (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_composite_rectangles_init_for_mask (&extents, surface, op, source, mask, clip); if (unlikely (status)) return status; do { while (compositor->mask == NULL) compositor = compositor->delegate; status = compositor->mask (compositor, &extents); compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", __FUNCTION__, extents.unbounded.x, extents.unbounded.y, extents.unbounded.width, extents.unbounded.height)); surface->damage = _cairo_damage_add_rectangle (surface->damage, &extents.unbounded); } _cairo_composite_rectangles_fini (&extents); return status; } cairo_int_status_t _cairo_compositor_stroke (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (_cairo_pen_vertices_needed (tolerance, style->line_width/2, ctm) <= 1) return CAIRO_INT_STATUS_NOTHING_TO_DO; status = _cairo_composite_rectangles_init_for_stroke (&extents, surface, op, source, path, style, ctm, clip); if (unlikely (status)) return status; do { while (compositor->stroke == NULL) compositor = compositor->delegate; status = compositor->stroke (compositor, &extents, path, style, ctm, ctm_inverse, tolerance, antialias); compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", __FUNCTION__, extents.unbounded.x, extents.unbounded.y, extents.unbounded.width, extents.unbounded.height)); surface->damage = _cairo_damage_add_rectangle (surface->damage, &extents.unbounded); } _cairo_composite_rectangles_fini (&extents); return status; } cairo_int_status_t _cairo_compositor_fill (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_composite_rectangles_init_for_fill (&extents, surface, op, source, path, clip); if (unlikely (status)) return status; do { while (compositor->fill == NULL) compositor = compositor->delegate; status = compositor->fill (compositor, &extents, path, fill_rule, tolerance, antialias); compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", __FUNCTION__, extents.unbounded.x, extents.unbounded.y, extents.unbounded.width, extents.unbounded.height)); surface->damage = _cairo_damage_add_rectangle (surface->damage, &extents.unbounded); } _cairo_composite_rectangles_fini (&extents); return status; } cairo_int_status_t _cairo_compositor_glyphs (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_bool_t overlap; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); if (unlikely (status)) return status; do { while (compositor->glyphs == NULL) compositor = compositor->delegate; status = compositor->glyphs (compositor, &extents, scaled_font, glyphs, num_glyphs, overlap); compositor = compositor->delegate; } while (status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", __FUNCTION__, extents.unbounded.x, extents.unbounded.y, extents.unbounded.width, extents.unbounded.height)); surface->damage = _cairo_damage_add_rectangle (surface->damage, &extents.unbounded); } _cairo_composite_rectangles_fini (&extents); return status; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-contour-inline.h��������������������������0000664�0000000�0000000�00000004734�12710376503�0026521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_CONTOUR_INLINE_H #define CAIRO_CONTOUR_INLINE_H #include "cairo-contour-private.h" CAIRO_BEGIN_DECLS static inline cairo_int_status_t _cairo_contour_add_point (cairo_contour_t *contour, const cairo_point_t *point) { struct _cairo_contour_chain *tail = contour->tail; if (unlikely (tail->num_points == tail->size_points)) return __cairo_contour_add_point (contour, point); tail->points[tail->num_points++] = *point; return CAIRO_INT_STATUS_SUCCESS; } static inline cairo_point_t * _cairo_contour_first_point (cairo_contour_t *c) { return &c->chain.points[0]; } static inline cairo_point_t * _cairo_contour_last_point (cairo_contour_t *c) { return &c->tail->points[c->tail->num_points-1]; } static inline void _cairo_contour_remove_last_point (cairo_contour_t *contour) { if (contour->chain.num_points == 0) return; if (--contour->tail->num_points == 0) __cairo_contour_remove_last_chain (contour); } CAIRO_END_DECLS #endif /* CAIRO_CONTOUR_INLINE_H */ ������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-contour-private.h�������������������������0000664�0000000�0000000�00000007224�12710376503�0026712�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_CONTOUR_PRIVATE_H #define CAIRO_CONTOUR_PRIVATE_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-list-private.h" #include <stdio.h> CAIRO_BEGIN_DECLS /* A contour is simply a closed chain of points that divide the infinite plane * into inside and outside. Each contour is a simple polygon, that is it * contains no holes or self-intersections, but maybe either concave or convex. */ struct _cairo_contour_chain { cairo_point_t *points; int num_points, size_points; struct _cairo_contour_chain *next; }; struct _cairo_contour_iter { cairo_point_t *point; cairo_contour_chain_t *chain; }; struct _cairo_contour { cairo_list_t next; int direction; cairo_contour_chain_t chain, *tail; cairo_point_t embedded_points[64]; }; /* Initial definition of a shape is a set of contours (some representing holes) */ struct _cairo_shape { cairo_list_t contours; }; typedef struct _cairo_shape cairo_shape_t; #if 0 cairo_private cairo_status_t _cairo_shape_init_from_polygon (cairo_shape_t *shape, const cairo_polygon_t *polygon); cairo_private cairo_status_t _cairo_shape_reduce (cairo_shape_t *shape, double tolerance); #endif cairo_private void _cairo_contour_init (cairo_contour_t *contour, int direction); cairo_private cairo_int_status_t __cairo_contour_add_point (cairo_contour_t *contour, const cairo_point_t *point); cairo_private void _cairo_contour_simplify (cairo_contour_t *contour, double tolerance); cairo_private void _cairo_contour_reverse (cairo_contour_t *contour); cairo_private cairo_int_status_t _cairo_contour_add (cairo_contour_t *dst, const cairo_contour_t *src); cairo_private cairo_int_status_t _cairo_contour_add_reversed (cairo_contour_t *dst, const cairo_contour_t *src); cairo_private void __cairo_contour_remove_last_chain (cairo_contour_t *contour); cairo_private void _cairo_contour_reset (cairo_contour_t *contour); cairo_private void _cairo_contour_fini (cairo_contour_t *contour); cairo_private void _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour); CAIRO_END_DECLS #endif /* CAIRO_CONTOUR_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-contour.c���������������������������������0000664�0000000�0000000�00000026416�12710376503�0025241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2008 Chris Wilson * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-inline.h" #include "cairo-contour-inline.h" #include "cairo-contour-private.h" void _cairo_contour_init (cairo_contour_t *contour, int direction) { contour->direction = direction; contour->chain.points = contour->embedded_points; contour->chain.next = NULL; contour->chain.num_points = 0; contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points); contour->tail = &contour->chain; } cairo_int_status_t __cairo_contour_add_point (cairo_contour_t *contour, const cairo_point_t *point) { cairo_contour_chain_t *tail = contour->tail; cairo_contour_chain_t *next; assert (tail->next == NULL); next = _cairo_malloc_ab_plus_c (tail->size_points*2, sizeof (cairo_point_t), sizeof (cairo_contour_chain_t)); if (unlikely (next == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); next->size_points = tail->size_points*2; next->num_points = 1; next->points = (cairo_point_t *)(next+1); next->next = NULL; tail->next = next; contour->tail = next; next->points[0] = *point; return CAIRO_INT_STATUS_SUCCESS; } static void first_inc (cairo_contour_t *contour, cairo_point_t **p, cairo_contour_chain_t **chain) { if (*p == (*chain)->points + (*chain)->num_points) { assert ((*chain)->next); *chain = (*chain)->next; *p = &(*chain)->points[0]; } else ++*p; } static void last_dec (cairo_contour_t *contour, cairo_point_t **p, cairo_contour_chain_t **chain) { if (*p == (*chain)->points) { cairo_contour_chain_t *prev; assert (*chain != &contour->chain); for (prev = &contour->chain; prev->next != *chain; prev = prev->next) ; *chain = prev; *p = &(*chain)->points[(*chain)->num_points-1]; } else --*p; } void _cairo_contour_reverse (cairo_contour_t *contour) { cairo_contour_chain_t *first_chain, *last_chain; cairo_point_t *first, *last; contour->direction = -contour->direction; if (contour->chain.num_points <= 1) return; first_chain = &contour->chain; last_chain = contour->tail; first = &first_chain->points[0]; last = &last_chain->points[last_chain->num_points-1]; while (first != last) { cairo_point_t p; p = *first; *first = *last; *last = p; first_inc (contour, &first, &first_chain); last_dec (contour, &last, &last_chain); } } cairo_int_status_t _cairo_contour_add (cairo_contour_t *dst, const cairo_contour_t *src) { const cairo_contour_chain_t *chain; cairo_int_status_t status; int i; for (chain = &src->chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { status = _cairo_contour_add_point (dst, &chain->points[i]); if (unlikely (status)) return status; } } return CAIRO_INT_STATUS_SUCCESS; } static inline cairo_bool_t iter_next (cairo_contour_iter_t *iter) { if (iter->point == &iter->chain->points[iter->chain->size_points-1]) { iter->chain = iter->chain->next; if (iter->chain == NULL) return FALSE; iter->point = &iter->chain->points[0]; return TRUE; } else { iter->point++; return TRUE; } } static cairo_bool_t iter_equal (const cairo_contour_iter_t *i1, const cairo_contour_iter_t *i2) { return i1->chain == i2->chain && i1->point == i2->point; } static void iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour) { iter->chain = &contour->chain; iter->point = &contour->chain.points[0]; } static void iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour) { iter->chain = contour->tail; iter->point = &contour->tail->points[contour->tail->num_points-1]; } static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour, const cairo_contour_chain_t *chain) { const cairo_contour_chain_t *prev; if (chain == &contour->chain) return NULL; for (prev = &contour->chain; prev->next != chain; prev = prev->next) ; return prev; } cairo_int_status_t _cairo_contour_add_reversed (cairo_contour_t *dst, const cairo_contour_t *src) { const cairo_contour_chain_t *last; cairo_int_status_t status; int i; if (src->chain.num_points == 0) return CAIRO_INT_STATUS_SUCCESS; for (last = src->tail; last; last = prev_const_chain (src, last)) { for (i = last->num_points-1; i >= 0; i--) { status = _cairo_contour_add_point (dst, &last->points[i]); if (unlikely (status)) return status; } } return CAIRO_INT_STATUS_SUCCESS; } static cairo_uint64_t point_distance_sq (const cairo_point_t *p1, const cairo_point_t *p2) { int32_t dx = p1->x - p2->x; int32_t dy = p1->y - p2->y; return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); } #define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX) #define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX) static cairo_bool_t _cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance, const cairo_contour_iter_t *first, const cairo_contour_iter_t *last) { cairo_contour_iter_t iter, furthest; uint64_t max_error; int x0, y0; int nx, ny; int count; iter = *first; iter_next (&iter); if (iter_equal (&iter, last)) return FALSE; x0 = first->point->x; y0 = first->point->y; nx = last->point->y - y0; ny = x0 - last->point->x; count = 0; max_error = 0; do { cairo_point_t *p = iter.point; if (! DELETED(p)) { uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y); if (d * d > max_error) { max_error = d * d; furthest = iter; } count++; } iter_next (&iter); } while (! iter_equal (&iter, last)); if (count == 0) return FALSE; if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) { cairo_bool_t simplified; simplified = FALSE; simplified |= _cairo_contour_simplify_chain (contour, tolerance, first, &furthest); simplified |= _cairo_contour_simplify_chain (contour, tolerance, &furthest, last); return simplified; } else { iter = *first; iter_next (&iter); do { MARK_DELETED (iter.point); iter_next (&iter); } while (! iter_equal (&iter, last)); return TRUE; } } void _cairo_contour_simplify (cairo_contour_t *contour, double tolerance) { cairo_contour_chain_t *chain; cairo_point_t *last = NULL; cairo_contour_iter_t iter, furthest; cairo_bool_t simplified; uint64_t max = 0; int i; if (contour->chain.num_points <= 2) return; tolerance = tolerance * CAIRO_FIXED_ONE; tolerance *= tolerance; /* stage 1: vertex reduction */ for (chain = &contour->chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { if (last == NULL || point_distance_sq (last, &chain->points[i]) > tolerance) { last = &chain->points[i]; } else { MARK_DELETED (&chain->points[i]); } } } /* stage2: polygon simplification using Douglas-Peucker */ simplified = FALSE; do { last = &contour->chain.points[0]; iter_init (&furthest, contour); max = 0; for (chain = &contour->chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { uint64_t d; if (DELETED (&chain->points[i])) continue; d = point_distance_sq (last, &chain->points[i]); if (d > max) { furthest.chain = chain; furthest.point = &chain->points[i]; max = d; } } } assert (max); simplified = FALSE; iter_init (&iter, contour); simplified |= _cairo_contour_simplify_chain (contour, tolerance, &iter, &furthest); iter_init_last (&iter, contour); if (! iter_equal (&furthest, &iter)) simplified |= _cairo_contour_simplify_chain (contour, tolerance, &furthest, &iter); } while (simplified); iter_init (&iter, contour); for (chain = &contour->chain; chain; chain = chain->next) { int num_points = chain->num_points; chain->num_points = 0; for (i = 0; i < num_points; i++) { if (! DELETED(&chain->points[i])) { if (iter.point != &chain->points[i]) *iter.point = chain->points[i]; iter.chain->num_points++; iter_next (&iter); } } } if (iter.chain) { cairo_contour_chain_t *next; for (chain = iter.chain->next; chain; chain = next) { next = chain->next; free (chain); } iter.chain->next = NULL; contour->tail = iter.chain; } } void _cairo_contour_reset (cairo_contour_t *contour) { _cairo_contour_fini (contour); _cairo_contour_init (contour, contour->direction); } void _cairo_contour_fini (cairo_contour_t *contour) { cairo_contour_chain_t *chain, *next; for (chain = contour->chain.next; chain; chain = next) { next = chain->next; free (chain); } } void _cairo_debug_print_contour (FILE *file, cairo_contour_t *contour) { cairo_contour_chain_t *chain; int num_points, size_points; int i; num_points = 0; size_points = 0; for (chain = &contour->chain; chain; chain = chain->next) { num_points += chain->num_points; size_points += chain->size_points; } fprintf (file, "contour: direction=%d, num_points=%d / %d\n", contour->direction, num_points, size_points); num_points = 0; for (chain = &contour->chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { fprintf (file, " [%d] = (%f, %f)\n", num_points++, _cairo_fixed_to_double (chain->points[i].x), _cairo_fixed_to_double (chain->points[i].y)); } } } void __cairo_contour_remove_last_chain (cairo_contour_t *contour) { cairo_contour_chain_t *chain; if (contour->tail == &contour->chain) return; for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next) ; free (contour->tail); contour->tail = chain; chain->next = NULL; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-damage-private.h��������������������������0000664�0000000�0000000�00000005061�12710376503�0026434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_DAMAGE_PRIVATE_H #define CAIRO_DAMAGE_PRIVATE_H #include "cairo-types-private.h" #include <pixman.h> CAIRO_BEGIN_DECLS struct _cairo_damage { cairo_status_t status; cairo_region_t *region; int dirty, remain; struct _cairo_damage_chunk { struct _cairo_damage_chunk *next; cairo_box_t *base; int count; int size; } chunks, *tail; cairo_box_t boxes[32]; }; cairo_private cairo_damage_t * _cairo_damage_create (void); cairo_private cairo_damage_t * _cairo_damage_create_in_error (cairo_status_t status); cairo_private cairo_damage_t * _cairo_damage_add_box (cairo_damage_t *damage, const cairo_box_t *box); cairo_private cairo_damage_t * _cairo_damage_add_rectangle (cairo_damage_t *damage, const cairo_rectangle_int_t *rect); cairo_private cairo_damage_t * _cairo_damage_add_region (cairo_damage_t *damage, const cairo_region_t *region); cairo_private cairo_damage_t * _cairo_damage_reduce (cairo_damage_t *damage); cairo_private void _cairo_damage_destroy (cairo_damage_t *damage); CAIRO_END_DECLS #endif /* CAIRO_DAMAGE_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-damage.c����������������������������������0000664�0000000�0000000�00000014253�12710376503�0024762�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-damage-private.h" #include "cairo-region-private.h" static const cairo_damage_t __cairo_damage__nil = { CAIRO_STATUS_NO_MEMORY }; cairo_damage_t * _cairo_damage_create_in_error (cairo_status_t status) { _cairo_error_throw (status); return (cairo_damage_t *) &__cairo_damage__nil; } cairo_damage_t * _cairo_damage_create (void) { cairo_damage_t *damage; damage = malloc (sizeof (*damage)); if (unlikely (damage == NULL)) { _cairo_error_throw(CAIRO_STATUS_NO_MEMORY); return (cairo_damage_t *) &__cairo_damage__nil; } damage->status = CAIRO_STATUS_SUCCESS; damage->region = NULL; damage->dirty = 0; damage->tail = &damage->chunks; damage->chunks.base = damage->boxes; damage->chunks.size = ARRAY_LENGTH(damage->boxes); damage->chunks.count = 0; damage->chunks.next = NULL; damage->remain = damage->chunks.size; return damage; } void _cairo_damage_destroy (cairo_damage_t *damage) { struct _cairo_damage_chunk *chunk, *next; if (damage == (cairo_damage_t *) &__cairo_damage__nil) return; for (chunk = damage->chunks.next; chunk != NULL; chunk = next) { next = chunk->next; free (chunk); } cairo_region_destroy (damage->region); free (damage); } static cairo_damage_t * _cairo_damage_add_boxes(cairo_damage_t *damage, const cairo_box_t *boxes, int count) { struct _cairo_damage_chunk *chunk; int n, size; TRACE ((stderr, "%s x%d\n", __FUNCTION__, count)); if (damage == NULL) damage = _cairo_damage_create (); if (damage->status) return damage; damage->dirty += count; n = count; if (n > damage->remain) n = damage->remain; memcpy (damage->tail->base + damage->tail->count, boxes, n * sizeof (cairo_box_t)); count -= n; damage->tail->count += n; damage->remain -= n; if (count == 0) return damage; size = 2 * damage->tail->size; if (size < count) size = (count + 64) & ~63; chunk = malloc (sizeof (*chunk) + sizeof (cairo_box_t) * size); if (unlikely (chunk == NULL)) { _cairo_damage_destroy (damage); return (cairo_damage_t *) &__cairo_damage__nil; } chunk->next = NULL; chunk->base = (cairo_box_t *) (chunk + 1); chunk->size = size; chunk->count = count; damage->tail->next = chunk; damage->tail = chunk; memcpy (damage->tail->base, boxes + n, count * sizeof (cairo_box_t)); damage->remain = size - count; return damage; } cairo_damage_t * _cairo_damage_add_box(cairo_damage_t *damage, const cairo_box_t *box) { TRACE ((stderr, "%s: (%d, %d),(%d, %d)\n", __FUNCTION__, box->p1.x, box->p1.y, box->p2.x, box->p2.y)); return _cairo_damage_add_boxes(damage, box, 1); } cairo_damage_t * _cairo_damage_add_rectangle(cairo_damage_t *damage, const cairo_rectangle_int_t *r) { cairo_box_t box; TRACE ((stderr, "%s: (%d, %d)x(%d, %d)\n", __FUNCTION__, r->x, r->y, r->width, r->height)); box.p1.x = r->x; box.p1.y = r->y; box.p2.x = r->x + r->width; box.p2.y = r->y + r->height; return _cairo_damage_add_boxes(damage, &box, 1); } cairo_damage_t * _cairo_damage_add_region (cairo_damage_t *damage, const cairo_region_t *region) { cairo_box_t *boxes; int nbox; TRACE ((stderr, "%s\n", __FUNCTION__)); boxes = _cairo_region_get_boxes (region, &nbox); return _cairo_damage_add_boxes(damage, boxes, nbox); } cairo_damage_t * _cairo_damage_reduce (cairo_damage_t *damage) { cairo_box_t *free_boxes = NULL; cairo_box_t *boxes, *b; struct _cairo_damage_chunk *chunk, *last; TRACE ((stderr, "%s: dirty=%d\n", __FUNCTION__, damage ? damage->dirty : -1)); if (damage == NULL || damage->status || !damage->dirty) return damage; if (damage->region) { cairo_region_t *region; region = damage->region; damage->region = NULL; damage = _cairo_damage_add_region (damage, region); cairo_region_destroy (region); if (unlikely (damage->status)) return damage; } boxes = damage->tail->base; if (damage->dirty > damage->tail->size) { boxes = free_boxes = malloc (damage->dirty * sizeof (cairo_box_t)); if (unlikely (boxes == NULL)) { _cairo_damage_destroy (damage); return (cairo_damage_t *) &__cairo_damage__nil; } b = boxes; last = NULL; } else { b = boxes + damage->tail->count; last = damage->tail; } for (chunk = &damage->chunks; chunk != last; chunk = chunk->next) { memcpy (b, chunk->base, chunk->count * sizeof (cairo_box_t)); b += chunk->count; } damage->region = _cairo_region_create_from_boxes (boxes, damage->dirty); free (free_boxes); if (unlikely (damage->region->status)) { _cairo_damage_destroy (damage); return (cairo_damage_t *) &__cairo_damage__nil; } damage->dirty = 0; return damage; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-debug.c�����������������������������������0000664�0000000�0000000�00000020404�12710376503�0024625�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-image-surface-private.h" /** * cairo_debug_reset_static_data: * * Resets all static data within cairo to its original state, * (ie. identical to the state at the time of program invocation). For * example, all caches within cairo will be flushed empty. * * This function is intended to be useful when using memory-checking * tools such as valgrind. When valgrind's memcheck analyzes a * cairo-using program without a call to cairo_debug_reset_static_data(), * it will report all data reachable via cairo's static objects as * "still reachable". Calling cairo_debug_reset_static_data() just prior * to program termination will make it easier to get squeaky clean * reports from valgrind. * * WARNING: It is only safe to call this function when there are no * active cairo objects remaining, (ie. the appropriate destroy * functions have been called as necessary). If there are active cairo * objects, this call is likely to cause a crash, (eg. an assertion * failure due to a hash table being destroyed when non-empty). * * Since: 1.0 **/ void cairo_debug_reset_static_data (void) { CAIRO_MUTEX_INITIALIZE (); _cairo_scaled_font_map_destroy (); _cairo_toy_font_face_reset_static_data (); #if CAIRO_HAS_FT_FONT _cairo_ft_font_reset_static_data (); #endif #if CAIRO_HAS_WIN32_FONT _cairo_win32_font_reset_static_data (); #endif _cairo_intern_string_reset_static_data (); _cairo_scaled_font_reset_static_data (); _cairo_pattern_reset_static_data (); _cairo_clip_reset_static_data (); _cairo_image_reset_static_data (); #if CAIRO_HAS_DRM_SURFACE _cairo_drm_device_reset_static_data (); #endif _cairo_default_context_reset_static_data (); #if CAIRO_HAS_COGL_SURFACE _cairo_cogl_context_reset_static_data (); #endif CAIRO_MUTEX_FINALIZE (); } #if HAVE_VALGRIND void _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) { const cairo_image_surface_t *image = (cairo_image_surface_t *) surface; const uint8_t *bits; int row, width; if (surface == NULL) return; if (! RUNNING_ON_VALGRIND) return; bits = image->data; switch (image->format) { case CAIRO_FORMAT_A1: width = (image->width + 7)/8; break; case CAIRO_FORMAT_A8: width = image->width; break; case CAIRO_FORMAT_RGB16_565: width = image->width*2; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: width = image->width*4; break; case CAIRO_FORMAT_INVALID: default: /* XXX compute width from pixman bpp */ return; } for (row = 0; row < image->height; row++) { VALGRIND_CHECK_MEM_IS_DEFINED (bits, width); /* and then silence any future valgrind warnings */ VALGRIND_MAKE_MEM_DEFINED (bits, width); bits += image->stride; } } #endif #if 0 void _cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn) { char *fmt; if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) fmt = "P6"; else if (isurf->format == CAIRO_FORMAT_A8) fmt = "P5"; else return; FILE *fp = fopen(fn, "wb"); if (!fp) return; fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height); for (int j = 0; j < isurf->height; j++) { unsigned char *row = isurf->data + isurf->stride * j; for (int i = 0; i < isurf->width; i++) { if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) { unsigned char r = *row++; unsigned char g = *row++; unsigned char b = *row++; *row++; putc(r, fp); putc(g, fp); putc(b, fp); } else { unsigned char a = *row++; putc(a, fp); } } } fclose (fp); fprintf (stderr, "Wrote %s\n", fn); } #endif static cairo_status_t _print_move_to (void *closure, const cairo_point_t *point) { fprintf (closure, " %f %f m", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _print_line_to (void *closure, const cairo_point_t *point) { fprintf (closure, " %f %f l", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _print_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { fprintf (closure, " %f %f %f %f %f %f c", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y), _cairo_fixed_to_double (p3->x), _cairo_fixed_to_double (p3->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _print_close (void *closure) { fprintf (closure, " h"); return CAIRO_STATUS_SUCCESS; } void _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) { cairo_status_t status; cairo_box_t box; fprintf (stream, "path: extents=(%f, %f), (%f, %f)\n", _cairo_fixed_to_double (path->extents.p1.x), _cairo_fixed_to_double (path->extents.p1.y), _cairo_fixed_to_double (path->extents.p2.x), _cairo_fixed_to_double (path->extents.p2.y)); status = _cairo_path_fixed_interpret (path, _print_move_to, _print_line_to, _print_curve_to, _print_close, stream); assert (status == CAIRO_STATUS_SUCCESS); if (_cairo_path_fixed_is_box (path, &box)) { fprintf (stream, "[box (%d, %d), (%d, %d)]", box.p1.x, box.p1.y, box.p2.x, box.p2.y); } printf ("\n"); } void _cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon) { int n; fprintf (stream, "polygon: extents=(%f, %f), (%f, %f)\n", _cairo_fixed_to_double (polygon->extents.p1.x), _cairo_fixed_to_double (polygon->extents.p1.y), _cairo_fixed_to_double (polygon->extents.p2.x), _cairo_fixed_to_double (polygon->extents.p2.y)); if (polygon->num_limits) { fprintf (stream, " : limit=(%f, %f), (%f, %f) x %d\n", _cairo_fixed_to_double (polygon->limit.p1.x), _cairo_fixed_to_double (polygon->limit.p1.y), _cairo_fixed_to_double (polygon->limit.p2.x), _cairo_fixed_to_double (polygon->limit.p2.y), polygon->num_limits); } for (n = 0; n < polygon->num_edges; n++) { cairo_edge_t *edge = &polygon->edges[n]; fprintf (stream, " [%d] = [(%f, %f), (%f, %f)], top=%f, bottom=%f, dir=%d\n", n, _cairo_fixed_to_double (edge->line.p1.x), _cairo_fixed_to_double (edge->line.p1.y), _cairo_fixed_to_double (edge->line.p2.x), _cairo_fixed_to_double (edge->line.p2.y), _cairo_fixed_to_double (edge->top), _cairo_fixed_to_double (edge->bottom), edge->dir); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-default-context-private.h�����������������0000664�0000000�0000000�00000004326�12710376503�0030327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_DEFAULT_CONTEXT_PRIVATE_H #define CAIRO_DEFAULT_CONTEXT_PRIVATE_H #include "cairo-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" CAIRO_BEGIN_DECLS typedef struct _cairo_default_context cairo_default_context_t; struct _cairo_default_context { cairo_t base; cairo_gstate_t *gstate; cairo_gstate_t gstate_tail[2]; cairo_gstate_t *gstate_freelist; cairo_path_fixed_t path[1]; }; cairo_private cairo_t * _cairo_default_context_create (void *target); cairo_private cairo_status_t _cairo_default_context_init (cairo_default_context_t *cr, void *target); cairo_private void _cairo_default_context_fini (cairo_default_context_t *cr); CAIRO_END_DECLS #endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-default-context.c�������������������������0000664�0000000�0000000�00000117406�12710376503�0026656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-private.h" #include "cairo-arc-private.h" #include "cairo-backend-private.h" #include "cairo-clip-inline.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-path-private.h" #include "cairo-pattern-private.h" #define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) #if !defined(INFINITY) #define INFINITY HUGE_VAL #endif static freed_pool_t context_pool; void _cairo_default_context_reset_static_data (void) { _freed_pool_reset (&context_pool); } void _cairo_default_context_fini (cairo_default_context_t *cr) { while (cr->gstate != &cr->gstate_tail[0]) { if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) break; } _cairo_gstate_fini (cr->gstate); cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ while (cr->gstate_freelist != NULL) { cairo_gstate_t *gstate = cr->gstate_freelist; cr->gstate_freelist = gstate->next; free (gstate); } _cairo_path_fixed_fini (cr->path); _cairo_fini (&cr->base); } static void _cairo_default_context_destroy (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; _cairo_default_context_fini (cr); /* mark the context as invalid to protect against misuse */ cr->base.status = CAIRO_STATUS_NULL_POINTER; _freed_pool_put (&context_pool, cr); } static cairo_surface_t * _cairo_default_context_get_original_target (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_original_target (cr->gstate); } static cairo_surface_t * _cairo_default_context_get_current_target (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_target (cr->gstate); } static cairo_status_t _cairo_default_context_save (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); } static cairo_status_t _cairo_default_context_restore (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; if (unlikely (_cairo_gstate_is_group (cr->gstate))) return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); return _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); } static cairo_status_t _cairo_default_context_push_group (void *abstract_cr, cairo_content_t content) { cairo_default_context_t *cr = abstract_cr; cairo_surface_t *group_surface; cairo_clip_t *clip; cairo_status_t status; clip = _cairo_gstate_get_clip (cr->gstate); if (_cairo_clip_is_all_clipped (clip)) { group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); status = group_surface->status; if (unlikely (status)) goto bail; } else { cairo_surface_t *parent_surface; cairo_rectangle_int_t extents; cairo_bool_t bounded, is_empty; parent_surface = _cairo_gstate_get_target (cr->gstate); if (unlikely (parent_surface->status)) return parent_surface->status; if (unlikely (parent_surface->finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); /* Get the extents that we'll use in creating our new group surface */ bounded = _cairo_surface_get_extents (parent_surface, &extents); if (clip) /* XXX: This assignment just fixes a compiler warning? */ is_empty = _cairo_rectangle_intersect (&extents, _cairo_clip_get_extents (clip)); if (!bounded) { /* XXX: Generic solution? */ group_surface = cairo_recording_surface_create (content, NULL); extents.x = extents.y = 0; } else { group_surface = _cairo_surface_create_similar_solid (parent_surface, content, extents.width, extents.height, CAIRO_COLOR_TRANSPARENT); } status = group_surface->status; if (unlikely (status)) goto bail; /* Set device offsets on the new surface so that logically it appears at * the same location on the parent surface -- when we pop_group this, * the source pattern will get fixed up for the appropriate target surface * device offsets, so we want to set our own surface offsets from /that/, * and not from the device origin. */ cairo_surface_set_device_offset (group_surface, parent_surface->device_transform.x0 - extents.x, parent_surface->device_transform.y0 - extents.y); /* If we have a current path, we need to adjust it to compensate for * the device offset just applied. */ _cairo_path_fixed_translate (cr->path, _cairo_fixed_from_int (-extents.x), _cairo_fixed_from_int (-extents.y)); } /* create a new gstate for the redirect */ status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); if (unlikely (status)) goto bail; status = _cairo_gstate_redirect_target (cr->gstate, group_surface); bail: cairo_surface_destroy (group_surface); return status; } static cairo_pattern_t * _cairo_default_context_pop_group (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_surface_t *group_surface; cairo_pattern_t *group_pattern; cairo_matrix_t group_matrix, device_transform_matrix; cairo_status_t status; /* Verify that we are at the right nesting level */ if (unlikely (! _cairo_gstate_is_group (cr->gstate))) return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP); /* Get a reference to the active surface before restoring */ group_surface = _cairo_gstate_get_target (cr->gstate); group_surface = cairo_surface_reference (group_surface); status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); assert (status == CAIRO_STATUS_SUCCESS); group_pattern = cairo_pattern_create_for_surface (group_surface); status = group_pattern->status; if (unlikely (status)) goto done; _cairo_gstate_get_matrix (cr->gstate, &group_matrix); /* Transform by group_matrix centered around device_transform so that when * we call _cairo_gstate_copy_transformed_pattern the result is a pattern * with a matrix equivalent to the device_transform of group_surface. */ if (_cairo_surface_has_device_transform (group_surface)) { cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); _cairo_pattern_transform (group_pattern, &group_matrix); _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); } else { cairo_pattern_set_matrix (group_pattern, &group_matrix); } /* If we have a current path, we need to adjust it to compensate for * the device offset just removed. */ cairo_matrix_multiply (&device_transform_matrix, &_cairo_gstate_get_target (cr->gstate)->device_transform, &group_surface->device_transform_inverse); _cairo_path_fixed_transform (cr->path, &device_transform_matrix); done: cairo_surface_destroy (group_surface); return group_pattern; } static cairo_status_t _cairo_default_context_set_source (void *abstract_cr, cairo_pattern_t *source) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_source (cr->gstate, source); } static cairo_bool_t _current_source_matches_solid (const cairo_pattern_t *pattern, double red, double green, double blue, double alpha) { cairo_color_t color; if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return FALSE; red = _cairo_restrict_value (red, 0.0, 1.0); green = _cairo_restrict_value (green, 0.0, 1.0); blue = _cairo_restrict_value (blue, 0.0, 1.0); alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_color_init_rgba (&color, red, green, blue, alpha); return _cairo_color_equal (&color, &((cairo_solid_pattern_t *) pattern)->color); } static cairo_status_t _cairo_default_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha) { cairo_default_context_t *cr = abstract_cr; cairo_pattern_t *pattern; cairo_status_t status; if (_current_source_matches_solid (cr->gstate->source, red, green, blue, alpha)) return CAIRO_STATUS_SUCCESS; /* push the current pattern to the freed lists */ _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); pattern = cairo_pattern_create_rgba (red, green, blue, alpha); if (unlikely (pattern->status)) return pattern->status; status = _cairo_default_context_set_source (cr, pattern); cairo_pattern_destroy (pattern); return status; } static cairo_status_t _cairo_default_context_set_source_surface (void *abstract_cr, cairo_surface_t *surface, double x, double y) { cairo_default_context_t *cr = abstract_cr; cairo_pattern_t *pattern; cairo_matrix_t matrix; cairo_status_t status; /* push the current pattern to the freed lists */ _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); pattern = cairo_pattern_create_for_surface (surface); if (unlikely (pattern->status)) return pattern->status; cairo_matrix_init_translate (&matrix, -x, -y); cairo_pattern_set_matrix (pattern, &matrix); status = _cairo_default_context_set_source (cr, pattern); cairo_pattern_destroy (pattern); return status; } static cairo_pattern_t * _cairo_default_context_get_source (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_source (cr->gstate); } static cairo_status_t _cairo_default_context_set_tolerance (void *abstract_cr, double tolerance) { cairo_default_context_t *cr = abstract_cr; if (tolerance < CAIRO_TOLERANCE_MINIMUM) tolerance = CAIRO_TOLERANCE_MINIMUM; return _cairo_gstate_set_tolerance (cr->gstate, tolerance); } static cairo_status_t _cairo_default_context_set_operator (void *abstract_cr, cairo_operator_t op) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_operator (cr->gstate, op); } static cairo_status_t _cairo_default_context_set_opacity (void *abstract_cr, double opacity) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_opacity (cr->gstate, opacity); } static cairo_status_t _cairo_default_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_antialias (cr->gstate, antialias); } static cairo_status_t _cairo_default_context_set_fill_rule (void *abstract_cr, cairo_fill_rule_t fill_rule) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); } static cairo_status_t _cairo_default_context_set_line_width (void *abstract_cr, double line_width) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_line_width (cr->gstate, line_width); } static cairo_status_t _cairo_default_context_set_line_cap (void *abstract_cr, cairo_line_cap_t line_cap) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_line_cap (cr->gstate, line_cap); } static cairo_status_t _cairo_default_context_set_line_join (void *abstract_cr, cairo_line_join_t line_join) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_line_join (cr->gstate, line_join); } static cairo_status_t _cairo_default_context_set_dash (void *abstract_cr, const double *dashes, int num_dashes, double offset) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_dash (cr->gstate, dashes, num_dashes, offset); } static cairo_status_t _cairo_default_context_set_miter_limit (void *abstract_cr, double limit) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_miter_limit (cr->gstate, limit); } static cairo_antialias_t _cairo_default_context_get_antialias (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_antialias (cr->gstate); } static void _cairo_default_context_get_dash (void *abstract_cr, double *dashes, int *num_dashes, double *offset) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_get_dash (cr->gstate, dashes, num_dashes, offset); } static cairo_fill_rule_t _cairo_default_context_get_fill_rule (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_fill_rule (cr->gstate); } static double _cairo_default_context_get_line_width (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_line_width (cr->gstate); } static cairo_line_cap_t _cairo_default_context_get_line_cap (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_line_cap (cr->gstate); } static cairo_line_join_t _cairo_default_context_get_line_join (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_line_join (cr->gstate); } static double _cairo_default_context_get_miter_limit (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_miter_limit (cr->gstate); } static cairo_operator_t _cairo_default_context_get_operator (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_operator (cr->gstate); } static double _cairo_default_context_get_opacity (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_opacity (cr->gstate); } static double _cairo_default_context_get_tolerance (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_tolerance (cr->gstate); } /* Current tranformation matrix */ static cairo_status_t _cairo_default_context_translate (void *abstract_cr, double tx, double ty) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_translate (cr->gstate, tx, ty); } static cairo_status_t _cairo_default_context_scale (void *abstract_cr, double sx, double sy) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_scale (cr->gstate, sx, sy); } static cairo_status_t _cairo_default_context_rotate (void *abstract_cr, double theta) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_rotate (cr->gstate, theta); } static cairo_status_t _cairo_default_context_transform (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_transform (cr->gstate, matrix); } static cairo_status_t _cairo_default_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_matrix (cr->gstate, matrix); } static cairo_status_t _cairo_default_context_set_identity_matrix (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_identity_matrix (cr->gstate); return CAIRO_STATUS_SUCCESS; } static void _cairo_default_context_get_matrix (void *abstract_cr, cairo_matrix_t *matrix) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_get_matrix (cr->gstate, matrix); } static void _cairo_default_context_user_to_device (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_user_to_device (cr->gstate, x, y); } static void _cairo_default_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); } static void _cairo_default_context_device_to_user (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_device_to_user (cr->gstate, x, y); } static void _cairo_default_context_device_to_user_distance (void *abstract_cr, double *dx, double *dy) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); } static void _cairo_default_context_backend_to_user (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_backend_to_user (cr->gstate, x, y); } static void _cairo_default_context_backend_to_user_distance (void *abstract_cr, double *dx, double *dy) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_backend_to_user_distance (cr->gstate, dx, dy); } static void _cairo_default_context_user_to_backend (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_user_to_backend (cr->gstate, x, y); } static void _cairo_default_context_user_to_backend_distance (void *abstract_cr, double *dx, double *dy) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_user_to_backend_distance (cr->gstate, dx, dy); } /* Path constructor */ static cairo_status_t _cairo_default_context_new_path (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; _cairo_path_fixed_fini (cr->path); _cairo_path_fixed_init (cr->path); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_default_context_new_sub_path (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; _cairo_path_fixed_new_sub_path (cr->path); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_default_context_move_to (void *abstract_cr, double x, double y) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t x_fixed, y_fixed; _cairo_gstate_user_to_backend (cr->gstate, &x, &y); x_fixed = _cairo_fixed_from_double (x); y_fixed = _cairo_fixed_from_double (y); return _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); } static cairo_status_t _cairo_default_context_line_to (void *abstract_cr, double x, double y) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t x_fixed, y_fixed; _cairo_gstate_user_to_backend (cr->gstate, &x, &y); x_fixed = _cairo_fixed_from_double (x); y_fixed = _cairo_fixed_from_double (y); return _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); } static cairo_status_t _cairo_default_context_curve_to (void *abstract_cr, double x1, double y1, double x2, double y2, double x3, double y3) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t x1_fixed, y1_fixed; cairo_fixed_t x2_fixed, y2_fixed; cairo_fixed_t x3_fixed, y3_fixed; _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); x1_fixed = _cairo_fixed_from_double (x1); y1_fixed = _cairo_fixed_from_double (y1); x2_fixed = _cairo_fixed_from_double (x2); y2_fixed = _cairo_fixed_from_double (y2); x3_fixed = _cairo_fixed_from_double (x3); y3_fixed = _cairo_fixed_from_double (y3); return _cairo_path_fixed_curve_to (cr->path, x1_fixed, y1_fixed, x2_fixed, y2_fixed, x3_fixed, y3_fixed); } static cairo_status_t _cairo_default_context_arc (void *abstract_cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward) { cairo_default_context_t *cr = abstract_cr; cairo_status_t status; /* Do nothing, successfully, if radius is <= 0 */ if (radius <= 0.0) { cairo_fixed_t x_fixed, y_fixed; _cairo_gstate_user_to_backend (cr->gstate, &xc, &yc); x_fixed = _cairo_fixed_from_double (xc); y_fixed = _cairo_fixed_from_double (yc); status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } status = _cairo_default_context_line_to (cr, xc + radius * cos (angle1), yc + radius * sin (angle1)); if (unlikely (status)) return status; if (forward) _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2); else _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2); return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */ } static cairo_status_t _cairo_default_context_rel_move_to (void *abstract_cr, double dx, double dy) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t dx_fixed, dy_fixed; _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy); dx_fixed = _cairo_fixed_from_double (dx); dy_fixed = _cairo_fixed_from_double (dy); return _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); } static cairo_status_t _cairo_default_context_rel_line_to (void *abstract_cr, double dx, double dy) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t dx_fixed, dy_fixed; _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy); dx_fixed = _cairo_fixed_from_double (dx); dy_fixed = _cairo_fixed_from_double (dy); return _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); } static cairo_status_t _cairo_default_context_rel_curve_to (void *abstract_cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t dx1_fixed, dy1_fixed; cairo_fixed_t dx2_fixed, dy2_fixed; cairo_fixed_t dx3_fixed, dy3_fixed; _cairo_gstate_user_to_backend_distance (cr->gstate, &dx1, &dy1); _cairo_gstate_user_to_backend_distance (cr->gstate, &dx2, &dy2); _cairo_gstate_user_to_backend_distance (cr->gstate, &dx3, &dy3); dx1_fixed = _cairo_fixed_from_double (dx1); dy1_fixed = _cairo_fixed_from_double (dy1); dx2_fixed = _cairo_fixed_from_double (dx2); dy2_fixed = _cairo_fixed_from_double (dy2); dx3_fixed = _cairo_fixed_from_double (dx3); dy3_fixed = _cairo_fixed_from_double (dy3); return _cairo_path_fixed_rel_curve_to (cr->path, dx1_fixed, dy1_fixed, dx2_fixed, dy2_fixed, dx3_fixed, dy3_fixed); } static cairo_status_t _cairo_default_context_close_path (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_path_fixed_close_path (cr->path); } static cairo_status_t _cairo_default_context_rectangle (void *abstract_cr, double x, double y, double width, double height) { cairo_default_context_t *cr = abstract_cr; cairo_status_t status; status = _cairo_default_context_move_to (cr, x, y); if (unlikely (status)) return status; status = _cairo_default_context_rel_line_to (cr, width, 0); if (unlikely (status)) return status; status = _cairo_default_context_rel_line_to (cr, 0, height); if (unlikely (status)) return status; status = _cairo_default_context_rel_line_to (cr, -width, 0); if (unlikely (status)) return status; return _cairo_default_context_close_path (cr); } static void _cairo_default_context_path_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_path_extents (cr->gstate, cr->path, x1, y1, x2, y2); } static cairo_bool_t _cairo_default_context_has_current_point (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return cr->path->has_current_point; } static cairo_bool_t _cairo_default_context_get_current_point (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t x_fixed, y_fixed; if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) { *x = _cairo_fixed_to_double (x_fixed); *y = _cairo_fixed_to_double (y_fixed); _cairo_gstate_backend_to_user (cr->gstate, x, y); return TRUE; } else { return FALSE; } } static cairo_path_t * _cairo_default_context_copy_path (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_path_create (cr->path, &cr->base); } static cairo_path_t * _cairo_default_context_copy_path_flat (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_path_create_flat (cr->path, &cr->base); } static cairo_status_t _cairo_default_context_append_path (void *abstract_cr, const cairo_path_t *path) { cairo_default_context_t *cr = abstract_cr; return _cairo_path_append_to_context (path, &cr->base); } static cairo_status_t _cairo_default_context_paint (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_paint (cr->gstate); } static cairo_status_t _cairo_default_context_paint_with_alpha (void *abstract_cr, double alpha) { cairo_default_context_t *cr = abstract_cr; cairo_solid_pattern_t pattern; cairo_status_t status; cairo_color_t color; if (CAIRO_ALPHA_IS_OPAQUE (alpha)) return _cairo_gstate_paint (cr->gstate); if (CAIRO_ALPHA_IS_ZERO (alpha) && _cairo_operator_bounded_by_mask (cr->gstate->op)) { return CAIRO_STATUS_SUCCESS; } _cairo_color_init_rgba (&color, 0., 0., 0., alpha); _cairo_pattern_init_solid (&pattern, &color); status = _cairo_gstate_mask (cr->gstate, &pattern.base); _cairo_pattern_fini (&pattern.base); return status; } static cairo_status_t _cairo_default_context_mask (void *abstract_cr, cairo_pattern_t *mask) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_mask (cr->gstate, mask); } static cairo_status_t _cairo_default_context_stroke_preserve (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_stroke (cr->gstate, cr->path); } static cairo_status_t _cairo_default_context_stroke (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_status_t status; status = _cairo_gstate_stroke (cr->gstate, cr->path); if (unlikely (status)) return status; return _cairo_default_context_new_path (cr); } static cairo_status_t _cairo_default_context_in_stroke (void *abstract_cr, double x, double y, cairo_bool_t *inside) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_in_stroke (cr->gstate, cr->path, x, y, inside); } static cairo_status_t _cairo_default_context_stroke_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_stroke_extents (cr->gstate, cr->path, x1, y1, x2, y2); } static cairo_status_t _cairo_default_context_fill_preserve (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_fill (cr->gstate, cr->path); } static cairo_status_t _cairo_default_context_fill (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_status_t status; status = _cairo_gstate_fill (cr->gstate, cr->path); if (unlikely (status)) return status; return _cairo_default_context_new_path (cr); } static cairo_status_t _cairo_default_context_in_fill (void *abstract_cr, double x, double y, cairo_bool_t *inside) { cairo_default_context_t *cr = abstract_cr; *inside = _cairo_gstate_in_fill (cr->gstate, cr->path, x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_default_context_fill_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_fill_extents (cr->gstate, cr->path, x1, y1, x2, y2); } static cairo_status_t _cairo_default_context_clip_preserve (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_clip (cr->gstate, cr->path); } static cairo_status_t _cairo_default_context_clip (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_status_t status; status = _cairo_gstate_clip (cr->gstate, cr->path); if (unlikely (status)) return status; return _cairo_default_context_new_path (cr); } static cairo_status_t _cairo_default_context_in_clip (void *abstract_cr, double x, double y, cairo_bool_t *inside) { cairo_default_context_t *cr = abstract_cr; *inside = _cairo_gstate_in_clip (cr->gstate, x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_default_context_reset_clip (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_reset_clip (cr->gstate); } static cairo_status_t _cairo_default_context_clip_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { cairo_default_context_t *cr = abstract_cr; if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { *x1 = -INFINITY; *y1 = -INFINITY; *x2 = +INFINITY; *y2 = +INFINITY; } return CAIRO_STATUS_SUCCESS; } static cairo_rectangle_list_t * _cairo_default_context_copy_clip_rectangle_list (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_copy_clip_rectangle_list (cr->gstate); } static cairo_status_t _cairo_default_context_copy_page (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_copy_page (cr->gstate); } static cairo_status_t _cairo_default_context_show_page (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_show_page (cr->gstate); } static cairo_status_t _cairo_default_context_set_font_face (void *abstract_cr, cairo_font_face_t *font_face) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_font_face (cr->gstate, font_face); } static cairo_font_face_t * _cairo_default_context_get_font_face (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_font_face_t *font_face; cairo_status_t status; status = _cairo_gstate_get_font_face (cr->gstate, &font_face); if (unlikely (status)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *) &_cairo_font_face_nil; } return font_face; } static cairo_status_t _cairo_default_context_font_extents (void *abstract_cr, cairo_font_extents_t *extents) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_get_font_extents (cr->gstate, extents); } static cairo_status_t _cairo_default_context_set_font_size (void *abstract_cr, double size) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_font_size (cr->gstate, size); } static cairo_status_t _cairo_default_context_set_font_matrix (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_set_font_matrix (cr->gstate, matrix); } static void _cairo_default_context_get_font_matrix (void *abstract_cr, cairo_matrix_t *matrix) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_get_font_matrix (cr->gstate, matrix); } static cairo_status_t _cairo_default_context_set_font_options (void *abstract_cr, const cairo_font_options_t *options) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_set_font_options (cr->gstate, options); return CAIRO_STATUS_SUCCESS; } static void _cairo_default_context_get_font_options (void *abstract_cr, cairo_font_options_t *options) { cairo_default_context_t *cr = abstract_cr; _cairo_gstate_get_font_options (cr->gstate, options); } static cairo_status_t _cairo_default_context_set_scaled_font (void *abstract_cr, cairo_scaled_font_t *scaled_font) { cairo_default_context_t *cr = abstract_cr; cairo_bool_t was_previous; cairo_status_t status; if (scaled_font == cr->gstate->scaled_font) return CAIRO_STATUS_SUCCESS; was_previous = scaled_font == cr->gstate->previous_scaled_font; status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); if (unlikely (status)) return status; status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); if (unlikely (status)) return status; _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); if (was_previous) cr->gstate->scaled_font = cairo_scaled_font_reference (scaled_font); return CAIRO_STATUS_SUCCESS; } static cairo_scaled_font_t * _cairo_default_context_get_scaled_font (void *abstract_cr) { cairo_default_context_t *cr = abstract_cr; cairo_scaled_font_t *scaled_font; cairo_status_t status; status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); if (unlikely (status)) return _cairo_scaled_font_create_in_error (status); return scaled_font; } static cairo_status_t _cairo_default_context_glyphs (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_glyph_text_info_t *info) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_show_text_glyphs (cr->gstate, glyphs, num_glyphs, info); } static cairo_status_t _cairo_default_context_glyph_path (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs, cr->path); } static cairo_status_t _cairo_default_context_glyph_extents (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) { cairo_default_context_t *cr = abstract_cr; return _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents); } static const cairo_backend_t _cairo_default_context_backend = { CAIRO_TYPE_DEFAULT, _cairo_default_context_destroy, _cairo_default_context_get_original_target, _cairo_default_context_get_current_target, _cairo_default_context_save, _cairo_default_context_restore, _cairo_default_context_push_group, _cairo_default_context_pop_group, _cairo_default_context_set_source_rgba, _cairo_default_context_set_source_surface, _cairo_default_context_set_source, _cairo_default_context_get_source, _cairo_default_context_set_antialias, _cairo_default_context_set_dash, _cairo_default_context_set_fill_rule, _cairo_default_context_set_line_cap, _cairo_default_context_set_line_join, _cairo_default_context_set_line_width, _cairo_default_context_set_miter_limit, _cairo_default_context_set_opacity, _cairo_default_context_set_operator, _cairo_default_context_set_tolerance, _cairo_default_context_get_antialias, _cairo_default_context_get_dash, _cairo_default_context_get_fill_rule, _cairo_default_context_get_line_cap, _cairo_default_context_get_line_join, _cairo_default_context_get_line_width, _cairo_default_context_get_miter_limit, _cairo_default_context_get_opacity, _cairo_default_context_get_operator, _cairo_default_context_get_tolerance, _cairo_default_context_translate, _cairo_default_context_scale, _cairo_default_context_rotate, _cairo_default_context_transform, _cairo_default_context_set_matrix, _cairo_default_context_set_identity_matrix, _cairo_default_context_get_matrix, _cairo_default_context_user_to_device, _cairo_default_context_user_to_device_distance, _cairo_default_context_device_to_user, _cairo_default_context_device_to_user_distance, _cairo_default_context_user_to_backend, _cairo_default_context_user_to_backend_distance, _cairo_default_context_backend_to_user, _cairo_default_context_backend_to_user_distance, _cairo_default_context_new_path, _cairo_default_context_new_sub_path, _cairo_default_context_move_to, _cairo_default_context_rel_move_to, _cairo_default_context_line_to, _cairo_default_context_rel_line_to, _cairo_default_context_curve_to, _cairo_default_context_rel_curve_to, NULL, /* arc-to */ NULL, /* rel-arc-to */ _cairo_default_context_close_path, _cairo_default_context_arc, _cairo_default_context_rectangle, _cairo_default_context_path_extents, _cairo_default_context_has_current_point, _cairo_default_context_get_current_point, _cairo_default_context_copy_path, _cairo_default_context_copy_path_flat, _cairo_default_context_append_path, NULL, /* stroke-to-path */ _cairo_default_context_clip, _cairo_default_context_clip_preserve, _cairo_default_context_in_clip, _cairo_default_context_clip_extents, _cairo_default_context_reset_clip, _cairo_default_context_copy_clip_rectangle_list, _cairo_default_context_paint, _cairo_default_context_paint_with_alpha, _cairo_default_context_mask, _cairo_default_context_stroke, _cairo_default_context_stroke_preserve, _cairo_default_context_in_stroke, _cairo_default_context_stroke_extents, _cairo_default_context_fill, _cairo_default_context_fill_preserve, _cairo_default_context_in_fill, _cairo_default_context_fill_extents, _cairo_default_context_set_font_face, _cairo_default_context_get_font_face, _cairo_default_context_set_font_size, _cairo_default_context_set_font_matrix, _cairo_default_context_get_font_matrix, _cairo_default_context_set_font_options, _cairo_default_context_get_font_options, _cairo_default_context_set_scaled_font, _cairo_default_context_get_scaled_font, _cairo_default_context_font_extents, _cairo_default_context_glyphs, _cairo_default_context_glyph_path, _cairo_default_context_glyph_extents, _cairo_default_context_copy_page, _cairo_default_context_show_page, }; cairo_status_t _cairo_default_context_init (cairo_default_context_t *cr, void *target) { _cairo_init (&cr->base, &_cairo_default_context_backend); _cairo_path_fixed_init (cr->path); cr->gstate = &cr->gstate_tail[0]; cr->gstate_freelist = &cr->gstate_tail[1]; cr->gstate_tail[1].next = NULL; return _cairo_gstate_init (cr->gstate, target); } cairo_t * _cairo_default_context_create (void *target) { cairo_default_context_t *cr; cairo_status_t status; cr = _freed_pool_get (&context_pool); if (unlikely (cr == NULL)) { cr = malloc (sizeof (cairo_default_context_t)); if (unlikely (cr == NULL)) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } status = _cairo_default_context_init (cr, target); if (unlikely (status)) { _freed_pool_put (&context_pool, cr); return _cairo_create_in_error (status); } return &cr->base; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-deflate-stream.c��������������������������0000664�0000000�0000000�00000012120�12710376503�0026430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2006 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Author(s): * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #if CAIRO_HAS_DEFLATE_STREAM #include "cairo-error-private.h" #include "cairo-output-stream-private.h" #include <zlib.h> #define BUFFER_SIZE 16384 typedef struct _cairo_deflate_stream { cairo_output_stream_t base; cairo_output_stream_t *output; z_stream zlib_stream; unsigned char input_buf[BUFFER_SIZE]; unsigned char output_buf[BUFFER_SIZE]; } cairo_deflate_stream_t; static void cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush) { int ret; cairo_bool_t finished; do { ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH); if (flush || stream->zlib_stream.avail_out == 0) { _cairo_output_stream_write (stream->output, stream->output_buf, BUFFER_SIZE - stream->zlib_stream.avail_out); stream->zlib_stream.next_out = stream->output_buf; stream->zlib_stream.avail_out = BUFFER_SIZE; } finished = TRUE; if (stream->zlib_stream.avail_in != 0) finished = FALSE; if (flush && ret != Z_STREAM_END) finished = FALSE; } while (!finished); stream->zlib_stream.next_in = stream->input_buf; } static cairo_status_t _cairo_deflate_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; unsigned int count; const unsigned char *p = data; while (length) { count = length; if (count > BUFFER_SIZE - stream->zlib_stream.avail_in) count = BUFFER_SIZE - stream->zlib_stream.avail_in; memcpy (stream->input_buf + stream->zlib_stream.avail_in, p, count); p += count; stream->zlib_stream.avail_in += count; length -= count; if (stream->zlib_stream.avail_in == BUFFER_SIZE) cairo_deflate_stream_deflate (stream, FALSE); } return _cairo_output_stream_get_status (stream->output); } static cairo_status_t _cairo_deflate_stream_close (cairo_output_stream_t *base) { cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; cairo_deflate_stream_deflate (stream, TRUE); deflateEnd (&stream->zlib_stream); return _cairo_output_stream_get_status (stream->output); } cairo_output_stream_t * _cairo_deflate_stream_create (cairo_output_stream_t *output) { cairo_deflate_stream_t *stream; if (output->status) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (cairo_deflate_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _cairo_deflate_stream_write, NULL, _cairo_deflate_stream_close); stream->output = output; stream->zlib_stream.zalloc = Z_NULL; stream->zlib_stream.zfree = Z_NULL; stream->zlib_stream.opaque = Z_NULL; if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { free (stream); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } stream->zlib_stream.next_in = stream->input_buf; stream->zlib_stream.avail_in = 0; stream->zlib_stream.next_out = stream->output_buf; stream->zlib_stream.avail_out = BUFFER_SIZE; return &stream->base; } #endif /* CAIRO_HAS_DEFLATE_STREAM */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-deprecated.h������������������������������0000664�0000000�0000000�00000020772�12710376503�0025654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_DEPRECATED_H #define CAIRO_DEPRECATED_H #define CAIRO_FONT_TYPE_ATSUI CAIRO_FONT_TYPE_QUARTZ /* Obsolete functions. These definitions exist to coerce the compiler * into providing a little bit of guidance with its error * messages. The idea is to help users port their old code without * having to dig through lots of documentation. * * The first set of REPLACED_BY functions is for functions whose names * have just been changed. So fixing these up is mechanical, (and * automated by means of the cairo/util/cairo-api-update script. * * The second set of DEPRECATED_BY functions is for functions where * the replacement is used in a different way, (ie. different * arguments, multiple functions instead of one, etc). Fixing these up * will require a bit more work on the user's part, (and hopefully we * can get cairo-api-update to find these and print some guiding * information). */ #define cairo_current_font_extents cairo_current_font_extents_REPLACED_BY_cairo_font_extents #define cairo_get_font_extents cairo_get_font_extents_REPLACED_BY_cairo_font_extents #define cairo_current_operator cairo_current_operator_REPLACED_BY_cairo_get_operator #define cairo_current_tolerance cairo_current_tolerance_REPLACED_BY_cairo_get_tolerance #define cairo_current_point cairo_current_point_REPLACED_BY_cairo_get_current_point #define cairo_current_fill_rule cairo_current_fill_rule_REPLACED_BY_cairo_get_fill_rule #define cairo_current_line_width cairo_current_line_width_REPLACED_BY_cairo_get_line_width #define cairo_current_line_cap cairo_current_line_cap_REPLACED_BY_cairo_get_line_cap #define cairo_current_line_join cairo_current_line_join_REPLACED_BY_cairo_get_line_join #define cairo_current_miter_limit cairo_current_miter_limit_REPLACED_BY_cairo_get_miter_limit #define cairo_current_matrix cairo_current_matrix_REPLACED_BY_cairo_get_matrix #define cairo_current_target_surface cairo_current_target_surface_REPLACED_BY_cairo_get_target #define cairo_get_status cairo_get_status_REPLACED_BY_cairo_status #define cairo_concat_matrix cairo_concat_matrix_REPLACED_BY_cairo_transform #define cairo_scale_font cairo_scale_font_REPLACED_BY_cairo_set_font_size #define cairo_select_font cairo_select_font_REPLACED_BY_cairo_select_font_face #define cairo_transform_font cairo_transform_font_REPLACED_BY_cairo_set_font_matrix #define cairo_transform_point cairo_transform_point_REPLACED_BY_cairo_user_to_device #define cairo_transform_distance cairo_transform_distance_REPLACED_BY_cairo_user_to_device_distance #define cairo_inverse_transform_point cairo_inverse_transform_point_REPLACED_BY_cairo_device_to_user #define cairo_inverse_transform_distance cairo_inverse_transform_distance_REPLACED_BY_cairo_device_to_user_distance #define cairo_init_clip cairo_init_clip_REPLACED_BY_cairo_reset_clip #define cairo_surface_create_for_image cairo_surface_create_for_image_REPLACED_BY_cairo_image_surface_create_for_data #define cairo_default_matrix cairo_default_matrix_REPLACED_BY_cairo_identity_matrix #define cairo_matrix_set_affine cairo_matrix_set_affine_REPLACED_BY_cairo_matrix_init #define cairo_matrix_set_identity cairo_matrix_set_identity_REPLACED_BY_cairo_matrix_init_identity #define cairo_pattern_add_color_stop cairo_pattern_add_color_stop_REPLACED_BY_cairo_pattern_add_color_stop_rgba #define cairo_set_rgb_color cairo_set_rgb_color_REPLACED_BY_cairo_set_source_rgb #define cairo_set_pattern cairo_set_pattern_REPLACED_BY_cairo_set_source #define cairo_xlib_surface_create_for_pixmap_with_visual cairo_xlib_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xlib_surface_create #define cairo_xlib_surface_create_for_window_with_visual cairo_xlib_surface_create_for_window_with_visual_REPLACED_BY_cairo_xlib_surface_create #define cairo_xcb_surface_create_for_pixmap_with_visual cairo_xcb_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xcb_surface_create #define cairo_xcb_surface_create_for_window_with_visual cairo_xcb_surface_create_for_window_with_visual_REPLACED_BY_cairo_xcb_surface_create #define cairo_ps_surface_set_dpi cairo_ps_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution #define cairo_pdf_surface_set_dpi cairo_pdf_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution #define cairo_svg_surface_set_dpi cairo_svg_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution #define cairo_atsui_font_face_create_for_atsu_font_id cairo_atsui_font_face_create_for_atsu_font_id_REPLACED_BY_cairo_quartz_font_face_create_for_atsu_font_id #define cairo_current_path cairo_current_path_DEPRECATED_BY_cairo_copy_path #define cairo_current_path_flat cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat #define cairo_get_path cairo_get_path_DEPRECATED_BY_cairo_copy_path #define cairo_get_path_flat cairo_get_path_flat_DEPRECATED_BY_cairo_get_path_flat #define cairo_set_alpha cairo_set_alpha_DEPRECATED_BY_cairo_set_source_rgba_OR_cairo_paint_with_alpha #define cairo_show_surface cairo_show_surface_DEPRECATED_BY_cairo_set_source_surface_AND_cairo_paint #define cairo_copy cairo_copy_DEPRECATED_BY_cairo_create_AND_MANY_INDIVIDUAL_FUNCTIONS #define cairo_surface_set_repeat cairo_surface_set_repeat_DEPRECATED_BY_cairo_pattern_set_extend #define cairo_surface_set_matrix cairo_surface_set_matrix_DEPRECATED_BY_cairo_pattern_set_matrix #define cairo_surface_get_matrix cairo_surface_get_matrix_DEPRECATED_BY_cairo_pattern_get_matrix #define cairo_surface_set_filter cairo_surface_set_filter_DEPRECATED_BY_cairo_pattern_set_filter #define cairo_surface_get_filter cairo_surface_get_filter_DEPRECATED_BY_cairo_pattern_get_filter #define cairo_matrix_create cairo_matrix_create_DEPRECATED_BY_cairo_matrix_t #define cairo_matrix_destroy cairo_matrix_destroy_DEPRECATED_BY_cairo_matrix_t #define cairo_matrix_copy cairo_matrix_copy_DEPRECATED_BY_cairo_matrix_t #define cairo_matrix_get_affine cairo_matrix_get_affine_DEPRECATED_BY_cairo_matrix_t #define cairo_set_target_surface cairo_set_target_surface_DEPRECATED_BY_cairo_create #define cairo_set_target_image cairo_set_target_image_DEPRECATED_BY_cairo_image_surface_create_for_data #define cairo_set_target_pdf cairo_set_target_pdf_DEPRECATED_BY_cairo_pdf_surface_create #define cairo_set_target_png cairo_set_target_png_DEPRECATED_BY_cairo_surface_write_to_png #define cairo_set_target_ps cairo_set_target_ps_DEPRECATED_BY_cairo_ps_surface_create #define cairo_set_target_quartz cairo_set_target_quartz_DEPRECATED_BY_cairo_quartz_surface_create #define cairo_set_target_win32 cairo_set_target_win32_DEPRECATED_BY_cairo_win32_surface_create #define cairo_set_target_xcb cairo_set_target_xcb_DEPRECATED_BY_cairo_xcb_surface_create #define cairo_set_target_drawable cairo_set_target_drawable_DEPRECATED_BY_cairo_xlib_surface_create #define cairo_get_status_string cairo_get_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string #define cairo_status_string cairo_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string #endif /* CAIRO_DEPRECATED_H */ ������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-device-private.h��������������������������0000664�0000000�0000000�00000005452�12710376503�0026461�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributors(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef _CAIRO_DEVICE_PRIVATE_H_ #define _CAIRO_DEVICE_PRIVATE_H_ #include "cairo-compiler-private.h" #include "cairo-mutex-private.h" #include "cairo-reference-count-private.h" #include "cairo-types-private.h" struct _cairo_device { cairo_reference_count_t ref_count; cairo_status_t status; cairo_user_data_array_t user_data; const cairo_device_backend_t *backend; cairo_recursive_mutex_t mutex; unsigned mutex_depth; cairo_bool_t finished; }; struct _cairo_device_backend { cairo_device_type_t type; void (*lock) (void *device); void (*unlock) (void *device); cairo_warn cairo_status_t (*flush) (void *device); void (*finish) (void *device); void (*destroy) (void *device); }; cairo_private cairo_device_t * _cairo_device_create_in_error (cairo_status_t status); cairo_private void _cairo_device_init (cairo_device_t *device, const cairo_device_backend_t *backend); cairo_private cairo_status_t _cairo_device_set_error (cairo_device_t *device, cairo_status_t error); slim_hidden_proto_no_warn (cairo_device_reference); slim_hidden_proto (cairo_device_acquire); slim_hidden_proto (cairo_device_release); slim_hidden_proto (cairo_device_flush); slim_hidden_proto (cairo_device_finish); slim_hidden_proto (cairo_device_destroy); #endif /* _CAIRO_DEVICE_PRIVATE_H_ */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-device.c����������������������������������0000664�0000000�0000000�00000036654�12710376503�0025014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributors(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-device-private.h" #include "cairo-error-private.h" /** * SECTION:cairo-device * @Title: cairo_device_t * @Short_Description: interface to underlying rendering system * @See_Also: #cairo_surface_t * * Devices are the abstraction Cairo employs for the rendering system * used by a #cairo_surface_t. You can get the device of a surface using * cairo_surface_get_device(). * * Devices are created using custom functions specific to the rendering * system you want to use. See the documentation for the surface types * for those functions. * * An important function that devices fulfill is sharing access to the * rendering system between Cairo and your application. If you want to * access a device directly that you used to draw to with Cairo, you must * first call cairo_device_flush() to ensure that Cairo finishes all * operations on the device and resets it to a clean state. * * Cairo also provides the functions cairo_device_acquire() and * cairo_device_release() to synchronize access to the rendering system * in a multithreaded environment. This is done internally, but can also * be used by applications. * * Putting this all together, a function that works with devices should * look something like this: * <informalexample><programlisting> * void * my_device_modifying_function (cairo_device_t *device) * { * cairo_status_t status; * * // Ensure the device is properly reset * cairo_device_flush (device); * // Try to acquire the device * status = cairo_device_acquire (device); * if (status != CAIRO_STATUS_SUCCESS) { * printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status)); * return; * } * * // Do the custom operations on the device here. * // But do not call any Cairo functions that might acquire devices. * * // Release the device when done. * cairo_device_release (device); * } * </programlisting></informalexample> * * <note><para>Please refer to the documentation of each backend for * additional usage requirements, guarantees provided, and * interactions with existing surface API of the device functions for * surfaces of that type. * </para></note> **/ static const cairo_device_t _nil_device = { CAIRO_REFERENCE_COUNT_INVALID, CAIRO_STATUS_NO_MEMORY, }; static const cairo_device_t _mismatch_device = { CAIRO_REFERENCE_COUNT_INVALID, CAIRO_STATUS_DEVICE_TYPE_MISMATCH, }; static const cairo_device_t _invalid_device = { CAIRO_REFERENCE_COUNT_INVALID, CAIRO_STATUS_DEVICE_ERROR, }; cairo_device_t * _cairo_device_create_in_error (cairo_status_t status) { switch (status) { case CAIRO_STATUS_NO_MEMORY: return (cairo_device_t *) &_nil_device; case CAIRO_STATUS_DEVICE_ERROR: return (cairo_device_t *) &_invalid_device; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: return (cairo_device_t *) &_mismatch_device; case CAIRO_STATUS_SUCCESS: case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; /* fall-through */ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: case CAIRO_STATUS_INVALID_STATUS: case CAIRO_STATUS_INVALID_FORMAT: case CAIRO_STATUS_INVALID_VISUAL: case CAIRO_STATUS_READ_ERROR: case CAIRO_STATUS_WRITE_ERROR: case CAIRO_STATUS_FILE_NOT_FOUND: case CAIRO_STATUS_TEMP_FILE_ERROR: case CAIRO_STATUS_INVALID_STRIDE: case CAIRO_STATUS_INVALID_SIZE: case CAIRO_STATUS_INVALID_RESTORE: case CAIRO_STATUS_INVALID_POP_GROUP: case CAIRO_STATUS_NO_CURRENT_POINT: case CAIRO_STATUS_INVALID_MATRIX: case CAIRO_STATUS_NULL_POINTER: case CAIRO_STATUS_INVALID_STRING: case CAIRO_STATUS_INVALID_PATH_DATA: case CAIRO_STATUS_SURFACE_FINISHED: case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: case CAIRO_STATUS_INVALID_DASH: case CAIRO_STATUS_INVALID_DSC_COMMENT: case CAIRO_STATUS_INVALID_INDEX: case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: case CAIRO_STATUS_FONT_TYPE_MISMATCH: case CAIRO_STATUS_USER_FONT_IMMUTABLE: case CAIRO_STATUS_USER_FONT_ERROR: case CAIRO_STATUS_NEGATIVE_COUNT: case CAIRO_STATUS_INVALID_CLUSTERS: case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: case CAIRO_STATUS_INVALID_CONTENT: case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_device_t *) &_nil_device; } } void _cairo_device_init (cairo_device_t *device, const cairo_device_backend_t *backend) { CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1); device->status = CAIRO_STATUS_SUCCESS; device->backend = backend; CAIRO_RECURSIVE_MUTEX_INIT (device->mutex); device->mutex_depth = 0; device->finished = FALSE; _cairo_user_data_array_init (&device->user_data); } /** * cairo_device_reference: * @device: a #cairo_device_t * * Increases the reference count on @device by one. This prevents * @device from being destroyed until a matching call to * cairo_device_destroy() is made. * * The number of references to a #cairo_device_t can be get using * cairo_device_get_reference_count(). * * Return value: the referenced #cairo_device_t. * * Since: 1.10 **/ cairo_device_t * cairo_device_reference (cairo_device_t *device) { if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) { return device; } assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); _cairo_reference_count_inc (&device->ref_count); return device; } slim_hidden_def (cairo_device_reference); /** * cairo_device_status: * @device: a #cairo_device_t * * Checks whether an error has previously occurred for this * device. * * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if * the device is in an error state. * * Since: 1.10 **/ cairo_status_t cairo_device_status (cairo_device_t *device) { if (device == NULL) return CAIRO_STATUS_NULL_POINTER; return device->status; } /** * cairo_device_flush: * @device: a #cairo_device_t * * Finish any pending operations for the device and also restore any * temporary modifications cairo has made to the device's state. * This function must be called before switching from using the * device with Cairo to operating on it directly with native APIs. * If the device doesn't support direct access, then this function * does nothing. * * This function may acquire devices. * * Since: 1.10 **/ void cairo_device_flush (cairo_device_t *device) { cairo_status_t status; if (device == NULL || device->status) return; if (device->finished) return; if (device->backend->flush != NULL) { status = device->backend->flush (device); if (unlikely (status)) status = _cairo_device_set_error (device, status); } } slim_hidden_def (cairo_device_flush); /** * cairo_device_finish: * @device: the #cairo_device_t to finish * * This function finishes the device and drops all references to * external resources. All surfaces, fonts and other objects created * for this @device will be finished, too. * Further operations on the @device will not affect the @device but * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error. * * When the last call to cairo_device_destroy() decreases the * reference count to zero, cairo will call cairo_device_finish() if * it hasn't been called already, before freeing the resources * associated with the device. * * This function may acquire devices. * * Since: 1.10 **/ void cairo_device_finish (cairo_device_t *device) { if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) { return; } if (device->finished) return; cairo_device_flush (device); if (device->backend->finish != NULL) device->backend->finish (device); /* We only finish the device after the backend's callback returns because * the device might still be needed during the callback * (e.g. for cairo_device_acquire ()). */ device->finished = TRUE; } slim_hidden_def (cairo_device_finish); /** * cairo_device_destroy: * @device: a #cairo_device_t * * Decreases the reference count on @device by one. If the result is * zero, then @device and all associated resources are freed. See * cairo_device_reference(). * * This function may acquire devices if the last reference was dropped. * * Since: 1.10 **/ void cairo_device_destroy (cairo_device_t *device) { cairo_user_data_array_t user_data; if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) { return; } assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); if (! _cairo_reference_count_dec_and_test (&device->ref_count)) return; cairo_device_finish (device); assert (device->mutex_depth == 0); CAIRO_MUTEX_FINI (device->mutex); user_data = device->user_data; device->backend->destroy (device); _cairo_user_data_array_fini (&user_data); } slim_hidden_def (cairo_device_destroy); /** * cairo_device_get_type: * @device: a #cairo_device_t * * This function returns the type of the device. See #cairo_device_type_t * for available types. * * Return value: The type of @device. * * Since: 1.10 **/ cairo_device_type_t cairo_device_get_type (cairo_device_t *device) { if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) { return CAIRO_DEVICE_TYPE_INVALID; } return device->backend->type; } /** * cairo_device_acquire: * @device: a #cairo_device_t * * Acquires the @device for the current thread. This function will block * until no other thread has acquired the device. * * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the * device. From now on your thread owns the device and no other thread will be * able to acquire it until a matching call to cairo_device_release(). It is * allowed to recursively acquire the device multiple times from the same * thread. * * <note><para>You must never acquire two different devices at the same time * unless this is explicitly allowed. Otherwise the possibility of deadlocks * exist. * * As various Cairo functions can acquire devices when called, these functions * may also cause deadlocks when you call them with an acquired device. So you * must not have a device acquired when calling them. These functions are * marked in the documentation. * </para></note> * * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if * the device is in an error state and could not be * acquired. After a successful call to cairo_device_acquire(), * a matching call to cairo_device_release() is required. * * Since: 1.10 **/ cairo_status_t cairo_device_acquire (cairo_device_t *device) { if (device == NULL) return CAIRO_STATUS_SUCCESS; if (unlikely (device->status)) return device->status; if (unlikely (device->finished)) return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED); CAIRO_MUTEX_LOCK (device->mutex); if (device->mutex_depth++ == 0) { if (device->backend->lock != NULL) device->backend->lock (device); } return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_device_acquire); /** * cairo_device_release: * @device: a #cairo_device_t * * Releases a @device previously acquired using cairo_device_acquire(). See * that function for details. * * Since: 1.10 **/ void cairo_device_release (cairo_device_t *device) { if (device == NULL) return; assert (device->mutex_depth > 0); if (--device->mutex_depth == 0) { if (device->backend->unlock != NULL) device->backend->unlock (device); } CAIRO_MUTEX_UNLOCK (device->mutex); } slim_hidden_def (cairo_device_release); cairo_status_t _cairo_device_set_error (cairo_device_t *device, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; _cairo_status_set_error (&device->status, status); return _cairo_error (status); } /** * cairo_device_get_reference_count: * @device: a #cairo_device_t * * Returns the current reference count of @device. * * Return value: the current reference count of @device. If the * object is a nil object, 0 will be returned. * * Since: 1.10 **/ unsigned int cairo_device_get_reference_count (cairo_device_t *device) { if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count); } /** * cairo_device_get_user_data: * @device: a #cairo_device_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @device using the * specified key. If no user data has been attached with the given * key this function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.10 **/ void * cairo_device_get_user_data (cairo_device_t *device, const cairo_user_data_key_t *key) { return _cairo_user_data_array_get_data (&device->user_data, key); } /** * cairo_device_set_user_data: * @device: a #cairo_device_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the #cairo_device_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @device. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.10 **/ cairo_status_t cairo_device_set_user_data (cairo_device_t *device, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) return device->status; return _cairo_user_data_array_set_data (&device->user_data, key, user_data, destroy); } ������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-directfb-surface.c������������������������0000664�0000000�0000000�00000040132�12710376503�0026747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-directfb.h" #include "cairo-clip-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-pattern-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-fallback-private.h" #include <pixman.h> #include <directfb.h> #include <direct/types.h> #include <direct/debug.h> #include <direct/memcpy.h> #include <direct/util.h> slim_hidden_proto(cairo_directfb_surface_create); typedef struct _cairo_dfb_surface { cairo_image_surface_t image; IDirectFB *dfb; IDirectFBSurface *dfb_surface; unsigned blit_premultiplied : 1; } cairo_dfb_surface_t; static cairo_content_t _directfb_format_to_content (DFBSurfacePixelFormat format) { cairo_content_t content = 0; if (DFB_PIXELFORMAT_HAS_ALPHA (format)) content |= CAIRO_CONTENT_ALPHA; if (DFB_COLOR_BITS_PER_PIXEL (format)) content |= CAIRO_CONTENT_COLOR_ALPHA; assert(content); return content; } static inline pixman_format_code_t _directfb_to_pixman_format (DFBSurfacePixelFormat format) { switch (format) { case DSPF_UNKNOWN: return 0; case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; case DSPF_RGB16: return PIXMAN_r5g6b5; case DSPF_RGB24: return PIXMAN_r8g8b8; case DSPF_RGB32: return PIXMAN_x8r8g8b8; case DSPF_ARGB: return PIXMAN_a8r8g8b8; case DSPF_A8: return PIXMAN_a8; case DSPF_YUY2: return PIXMAN_yuy2; case DSPF_RGB332: return PIXMAN_r3g3b2; case DSPF_UYVY: return 0; case DSPF_I420: return 0; case DSPF_YV12: return PIXMAN_yv12; case DSPF_LUT8: return 0; case DSPF_ALUT44: return 0; case DSPF_AiRGB: return 0; case DSPF_A1: return 0; /* bit reversed, oops */ case DSPF_NV12: return 0; case DSPF_NV16: return 0; case DSPF_ARGB2554: return 0; case DSPF_ARGB4444: return PIXMAN_a4r4g4b4; case DSPF_NV21: return 0; case DSPF_AYUV: return 0; case DSPF_A4: return PIXMAN_a4; case DSPF_ARGB1666: return 0; case DSPF_ARGB6666: return 0; case DSPF_RGB18: return 0; case DSPF_LUT2: return 0; case DSPF_RGB444: return PIXMAN_x4r4g4b4; case DSPF_RGB555: return PIXMAN_x1r5g5b5; #if DFB_NUM_PIXELFORMATS >= 29 case DSPF_BGR555: return PIXMAN_x1b5g5r5; #endif } return 0; } static cairo_surface_t * _cairo_dfb_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_dfb_surface_t *other = abstract_src; DFBSurfacePixelFormat format; IDirectFBSurface *buffer; DFBSurfaceDescription dsc; cairo_surface_t *surface; if (width <= 0 || height <= 0) return _cairo_image_surface_create_with_content (content, width, height); switch (content) { default: ASSERT_NOT_REACHED; case CAIRO_CONTENT_COLOR_ALPHA: format = DSPF_ARGB; break; case CAIRO_CONTENT_COLOR: format = DSPF_RGB32; break; case CAIRO_CONTENT_ALPHA: format = DSPF_A8; break; } dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT; dsc.caps = DSCAPS_PREMULTIPLIED; dsc.width = width; dsc.height = height; dsc.pixelformat = format; if (other->dfb->CreateSurface (other->dfb, &dsc, &buffer)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_ERROR)); surface = cairo_directfb_surface_create (other->dfb, buffer); buffer->Release (buffer); return surface; } static cairo_status_t _cairo_dfb_surface_finish (void *abstract_surface) { cairo_dfb_surface_t *surface = abstract_surface; surface->dfb_surface->Release (surface->dfb_surface); return _cairo_image_surface_finish (abstract_surface); } static cairo_image_surface_t * _cairo_dfb_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_dfb_surface_t *surface = abstract_surface; if (surface->image.pixman_image == NULL) { IDirectFBSurface *buffer = surface->dfb_surface; pixman_image_t *image; void *data; int pitch; if (buffer->Lock (buffer, DSLF_READ | DSLF_WRITE, &data, &pitch)) return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); image = pixman_image_create_bits (surface->image.pixman_format, surface->image.width, surface->image.height, data, pitch); if (image == NULL) { buffer->Unlock (buffer); return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } _cairo_image_surface_init (&surface->image, image, surface->image.pixman_format); } return _cairo_image_surface_map_to_image (&surface->image.base, extents); } static cairo_int_status_t _cairo_dfb_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_dfb_surface_t *surface = abstract_surface; return _cairo_image_surface_unmap_image (&surface->image.base, image); } static cairo_status_t _cairo_dfb_surface_flush (void *abstract_surface, unsigned flags) { cairo_dfb_surface_t *surface = abstract_surface; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->image.pixman_image) { surface->dfb_surface->Unlock (surface->dfb_surface); pixman_image_unref (surface->image.pixman_image); surface->image.pixman_image = NULL; surface->image.data = NULL; } return CAIRO_STATUS_SUCCESS; } #if 0 static inline DFBSurfacePixelFormat _directfb_from_pixman_format (pixman_format_code_t format) { switch ((int) format) { case PIXMAN_a1r5g5b5: return DSPF_ARGB1555; case PIXMAN_r5g6b5: return DSPF_RGB16; case PIXMAN_r8g8b8: return DSPF_RGB24; case PIXMAN_x8r8g8b8: return DSPF_RGB32; case PIXMAN_a8r8g8b8: return DSPF_ARGB; case PIXMAN_a8: return DSPF_A8; case PIXMAN_yuy2: return DSPF_YUY2; case PIXMAN_r3g3b2: return DSPF_RGB332; case PIXMAN_yv12: return DSPF_YV12; case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */ case PIXMAN_a4r4g4b4: return DSPF_ARGB4444; case PIXMAN_a4: return DSPF_A4; case PIXMAN_x4r4g4b4: return DSPF_RGB444; case PIXMAN_x1r5g5b5: return DSPF_RGB555; #if DFB_NUM_PIXELFORMATS >= 29 case PIXMAN_x1b5g5r5: return DSPF_BGR555; #endif default: return 0; } } static cairo_bool_t _directfb_get_operator (cairo_operator_t operator, DFBSurfaceBlendFunction *ret_srcblend, DFBSurfaceBlendFunction *ret_dstblend) { DFBSurfaceBlendFunction srcblend = DSBF_ONE; DFBSurfaceBlendFunction dstblend = DSBF_ZERO; switch (operator) { case CAIRO_OPERATOR_CLEAR: srcblend = DSBF_ZERO; dstblend = DSBF_ZERO; break; case CAIRO_OPERATOR_SOURCE: srcblend = DSBF_ONE; dstblend = DSBF_ZERO; break; case CAIRO_OPERATOR_OVER: srcblend = DSBF_ONE; dstblend = DSBF_INVSRCALPHA; break; case CAIRO_OPERATOR_IN: srcblend = DSBF_DESTALPHA; dstblend = DSBF_ZERO; break; case CAIRO_OPERATOR_OUT: srcblend = DSBF_INVDESTALPHA; dstblend = DSBF_ZERO; break; case CAIRO_OPERATOR_ATOP: srcblend = DSBF_DESTALPHA; dstblend = DSBF_INVSRCALPHA; break; case CAIRO_OPERATOR_DEST: srcblend = DSBF_ZERO; dstblend = DSBF_ONE; break; case CAIRO_OPERATOR_DEST_OVER: srcblend = DSBF_INVDESTALPHA; dstblend = DSBF_ONE; break; case CAIRO_OPERATOR_DEST_IN: srcblend = DSBF_ZERO; dstblend = DSBF_SRCALPHA; break; case CAIRO_OPERATOR_DEST_OUT: srcblend = DSBF_ZERO; dstblend = DSBF_INVSRCALPHA; break; case CAIRO_OPERATOR_DEST_ATOP: srcblend = DSBF_INVDESTALPHA; dstblend = DSBF_SRCALPHA; break; case CAIRO_OPERATOR_XOR: srcblend = DSBF_INVDESTALPHA; dstblend = DSBF_INVSRCALPHA; break; case CAIRO_OPERATOR_ADD: srcblend = DSBF_ONE; dstblend = DSBF_ONE; break; case CAIRO_OPERATOR_SATURATE: /* XXX This does not work. */ #if 0 srcblend = DSBF_SRCALPHASAT; dstblend = DSBF_ONE; break; #endif case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: default: return FALSE; } *ret_srcblend = srcblend; *ret_dstblend = dstblend; return TRUE; } #define RUN_CLIPPED(surface, clip_region, clip, func) {\ if ((clip_region) != NULL) {\ int n_clips = cairo_region_num_rectangles (clip_region), n; \ for (n = 0; n < n_clips; n++) {\ if (clip) {\ DFBRegion reg, *cli = (clip); \ cairo_rectangle_int_t rect; \ cairo_region_get_rectangle (clip_region, n, &rect); \ reg.x1 = rect.x; \ reg.y1 = rect.y; \ reg.x2 = rect.x + rect.width - 1; \ reg.y2 = rect.y + rect.height - 1; \ if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ reg.x1 > cli->x2 || reg.y1 > cli->y2)\ continue;\ if (reg.x1 < cli->x1)\ reg.x1 = cli->x1;\ if (reg.y1 < cli->y1)\ reg.y1 = cli->y1;\ if (reg.x2 > cli->x2)\ reg.x2 = cli->x2;\ if (reg.y2 > cli->y2)\ reg.y2 = cli->y2;\ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ } else {\ DFBRegion reg; \ cairo_rectangle_int_t rect; \ cairo_region_get_rectangle (clip_region, n, &rect); \ reg.x1 = rect.x; \ reg.y1 = rect.y; \ reg.x2 = rect.x + rect.width - 1; \ reg.y2 = rect.y + rect.height - 1; \ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ }\ func;\ }\ } else {\ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\ func;\ }\ } static cairo_int_status_t _cairo_dfb_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int n_rects) { cairo_dfb_surface_t *dst = abstract_surface; DFBSurfaceDrawingFlags flags; DFBSurfaceBlendFunction sblend; DFBSurfaceBlendFunction dblend; DFBRectangle r[n_rects]; int i; D_DEBUG_AT (CairoDFB_Render, "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n", __FUNCTION__, dst, op, color, rects, n_rects); if (! dst->supported_destination) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _directfb_get_operator (op, &sblend, &dblend)) return CAIRO_INT_STATUS_UNSUPPORTED; if (CAIRO_COLOR_IS_OPAQUE (color)) { if (sblend == DSBF_SRCALPHA) sblend = DSBF_ONE; else if (sblend == DSBF_INVSRCALPHA) sblend = DSBF_ZERO; if (dblend == DSBF_SRCALPHA) dblend = DSBF_ONE; else if (dblend == DSBF_INVSRCALPHA) dblend = DSBF_ZERO; } if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { if (sblend == DSBF_DESTALPHA) sblend = DSBF_ONE; else if (sblend == DSBF_INVDESTALPHA) sblend = DSBF_ZERO; if (dblend == DSBF_DESTALPHA) dblend = DSBF_ONE; else if (dblend == DSBF_INVDESTALPHA) dblend = DSBF_ZERO; } flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND; dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags); if (flags & DSDRAW_BLEND) { dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); } dst->dfbsurface->SetColor (dst->dfbsurface, color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8, color->alpha_short >> 8); for (i = 0; i < n_rects; i++) { r[i].x = rects[i].x; r[i].y = rects[i].y; r[i].w = rects[i].width; r[i].h = rects[i].height; } RUN_CLIPPED (dst, NULL, NULL, dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects)); return CAIRO_STATUS_SUCCESS; } #endif static cairo_surface_backend_t _cairo_dfb_surface_backend = { CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/ _cairo_dfb_surface_finish, /*finish*/ _cairo_default_context_create, _cairo_dfb_surface_create_similar,/*create_similar*/ NULL, /* create similar image */ _cairo_dfb_surface_map_to_image, _cairo_dfb_surface_unmap_image, _cairo_surface_default_source, _cairo_surface_default_acquire_source_image, _cairo_surface_default_release_source_image, NULL, NULL, /* copy_page */ NULL, /* show_page */ _cairo_image_surface_get_extents, _cairo_image_surface_get_font_options, _cairo_dfb_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_surface_fallback_paint, _cairo_surface_fallback_mask, _cairo_surface_fallback_stroke, _cairo_surface_fallback_fill, NULL, /* fill-stroke */ _cairo_surface_fallback_glyphs, }; cairo_surface_t * cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) { cairo_dfb_surface_t *surface; DFBSurfacePixelFormat format; DFBSurfaceCapabilities caps; pixman_format_code_t pixman_format; int width, height; D_ASSERT (dfb != NULL); D_ASSERT (dfbsurface != NULL); dfbsurface->GetPixelFormat (dfbsurface, &format); dfbsurface->GetSize (dfbsurface, &width, &height); pixman_format = _directfb_to_pixman_format (format); if (! pixman_format_supported_destination (pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); surface = calloc (1, sizeof (cairo_dfb_surface_t)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); /* XXX dfb -> device */ _cairo_surface_init (&surface->image.base, &_cairo_dfb_surface_backend, NULL, /* device */ _directfb_format_to_content (format)); surface->image.pixman_format = pixman_format; surface->image.format = _cairo_format_from_pixman_format (pixman_format); surface->image.width = width; surface->image.height = height; surface->image.depth = PIXMAN_FORMAT_DEPTH(pixman_format); surface->dfb = dfb; surface->dfb_surface = dfbsurface; dfbsurface->AddRef (dfbsurface); dfbsurface->GetCapabilities (dfbsurface, &caps); if (caps & DSCAPS_PREMULTIPLIED) surface->blit_premultiplied = TRUE; return &surface->image.base; } slim_hidden_def(cairo_directfb_surface_create); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-directfb.h��������������������������������0000664�0000000�0000000�00000004260�12710376503�0025330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@isi.edu> */ /* * Environment variables affecting the backend: * * %CAIRO_DIRECTFB_NO_ACCEL (boolean) * if found, disables acceleration at all * * %CAIRO_DIRECTFB_ARGB_FONT (boolean) * if found, enables using ARGB fonts instead of A8 */ #ifndef CAIRO_DIRECTFB_H #define CAIRO_DIRECTFB_H #include "cairo.h" #if CAIRO_HAS_DIRECTFB_SURFACE #include <directfb.h> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *surface); CAIRO_END_DECLS #else /*CAIRO_HAS_DIRECTFB_SURFACE*/ # error Cairo was not compiled with support for the directfb backend #endif /*CAIRO_HAS_DIRECTFB_SURFACE*/ #endif /*CAIRO_DIRECTFB_H*/ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-drm.h�������������������������������������0000664�0000000�0000000�00000007641�12710376503�0024336�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. */ #ifndef CAIRO_DRM_H #define CAIRO_DRM_H #include "cairo.h" #if CAIRO_HAS_DRM_SURFACE CAIRO_BEGIN_DECLS struct udev_device; cairo_public cairo_device_t * cairo_drm_device_get (struct udev_device *device); cairo_public cairo_device_t * cairo_drm_device_get_for_fd (int fd); cairo_public cairo_device_t * cairo_drm_device_default (void); cairo_public int cairo_drm_device_get_fd (cairo_device_t *device); cairo_public void cairo_drm_device_throttle (cairo_device_t *device); cairo_public cairo_surface_t * cairo_drm_surface_create (cairo_device_t *device, cairo_format_t format, int width, int height); cairo_public cairo_surface_t * cairo_drm_surface_create_for_name (cairo_device_t *device, unsigned int name, cairo_format_t format, int width, int height, int stride); cairo_public cairo_surface_t * cairo_drm_surface_create_from_cacheable_image (cairo_device_t *device, cairo_surface_t *surface); cairo_public cairo_status_t cairo_drm_surface_enable_scan_out (cairo_surface_t *surface); cairo_public unsigned int cairo_drm_surface_get_handle (cairo_surface_t *surface); cairo_public unsigned int cairo_drm_surface_get_name (cairo_surface_t *surface); cairo_public cairo_format_t cairo_drm_surface_get_format (cairo_surface_t *surface); cairo_public int cairo_drm_surface_get_width (cairo_surface_t *surface); cairo_public int cairo_drm_surface_get_height (cairo_surface_t *surface); cairo_public int cairo_drm_surface_get_stride (cairo_surface_t *surface); /* XXX map/unmap, general surface layer? */ /* Rough outline, culled from a conversation on IRC: * map() returns an image-surface representation of the drm-surface, * which you unmap() when you are finished, i.e. map() pulls the buffer back * from the GPU, maps it into the CPU domain and gives you direct access to * the pixels. With the unmap(), the buffer is ready to be used again by the * GPU and *until* the unmap(), all operations will be done in software. * * (Technically calling cairo_surface_flush() on the underlying drm-surface * will also disassociate the mapping.) */ cairo_public cairo_surface_t * cairo_drm_surface_map_to_image (cairo_surface_t *surface); cairo_public void cairo_drm_surface_unmap (cairo_surface_t *drm_surface, cairo_surface_t *image_surface); CAIRO_END_DECLS #else /* CAIRO_HAS_DRM_SURFACE */ # error Cairo was not compiled with support for the DRM backend #endif /* CAIRO_HAS_DRM_SURFACE */ #endif /* CAIRO_DRM_H */ �����������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-egl-context.c�����������������������������0000664�0000000�0000000�00000021460�12710376503�0025773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-error-private.h" typedef struct _cairo_egl_context { cairo_gl_context_t base; EGLDisplay display; EGLContext context; EGLSurface dummy_surface; EGLContext previous_context; EGLSurface previous_surface; } cairo_egl_context_t; typedef struct _cairo_egl_surface { cairo_gl_surface_t base; EGLSurface egl; } cairo_egl_surface_t; static cairo_bool_t _context_acquisition_changed_egl_state (cairo_egl_context_t *ctx, EGLSurface current_surface) { return ctx->previous_context != ctx->context || ctx->previous_surface != current_surface; } static EGLSurface _egl_get_current_surface (cairo_egl_context_t *ctx) { if (ctx->base.current_target == NULL || _cairo_gl_surface_is_texture (ctx->base.current_target)) { return ctx->dummy_surface; } return ((cairo_egl_surface_t *) ctx->base.current_target)->egl; } static void _egl_query_current_state (cairo_egl_context_t *ctx) { ctx->previous_surface = eglGetCurrentSurface (EGL_DRAW); ctx->previous_context = eglGetCurrentContext (); /* If any of the values were none, assume they are all none. Not all drivers seem well behaved when it comes to using these values across multiple threads. */ if (ctx->previous_surface == EGL_NO_SURFACE || ctx->previous_context == EGL_NO_CONTEXT) { ctx->previous_surface = EGL_NO_SURFACE; ctx->previous_context = EGL_NO_CONTEXT; } } static void _egl_acquire (void *abstract_ctx) { cairo_egl_context_t *ctx = abstract_ctx; EGLSurface current_surface = _egl_get_current_surface (ctx); _egl_query_current_state (ctx); if (!_context_acquisition_changed_egl_state (ctx, current_surface)) return; eglMakeCurrent (ctx->display, current_surface, current_surface, ctx->context); } static void _egl_release (void *abstract_ctx) { cairo_egl_context_t *ctx = abstract_ctx; if (!ctx->base.thread_aware || !_context_acquisition_changed_egl_state (ctx, _egl_get_current_surface (ctx))) { return; } eglMakeCurrent (ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } static void _egl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_egl_context_t *ctx = abstract_ctx; cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface; eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context); } static void _egl_swap_buffers (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_egl_context_t *ctx = abstract_ctx; cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface; eglSwapBuffers (ctx->display, surface->egl); } static void _egl_destroy (void *abstract_ctx) { cairo_egl_context_t *ctx = abstract_ctx; eglMakeCurrent (ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (ctx->dummy_surface != EGL_NO_SURFACE) eglDestroySurface (ctx->display, ctx->dummy_surface); } static cairo_bool_t _egl_make_current_surfaceless(cairo_egl_context_t *ctx) { const char *extensions; extensions = eglQueryString(ctx->display, EGL_EXTENSIONS); if (strstr(extensions, "EGL_KHR_surfaceless_context") == NULL && strstr(extensions, "EGL_KHR_surfaceless_opengl") == NULL) return FALSE; if (!eglMakeCurrent(ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context)) return FALSE; return TRUE; } cairo_device_t * cairo_egl_device_create (EGLDisplay dpy, EGLContext egl) { cairo_egl_context_t *ctx; cairo_status_t status; int attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, }; EGLConfig config; EGLint numConfigs; ctx = calloc (1, sizeof (cairo_egl_context_t)); if (unlikely (ctx == NULL)) return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); ctx->display = dpy; ctx->context = egl; ctx->base.acquire = _egl_acquire; ctx->base.release = _egl_release; ctx->base.make_current = _egl_make_current; ctx->base.swap_buffers = _egl_swap_buffers; ctx->base.destroy = _egl_destroy; /* We are about the change the current state of EGL, so we should * query the pre-existing surface now instead of later. */ _egl_query_current_state (ctx); if (!_egl_make_current_surfaceless (ctx)) { /* Fall back to dummy surface, meh. */ EGLint config_attribs[] = { EGL_CONFIG_ID, 0, EGL_NONE }; /* * In order to be able to make an egl context current when using a * pbuffer surface, that surface must have been created with a config * that is compatible with the context config. For Mesa, this means * that the configs must be the same. */ eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]); eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs); ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs); if (ctx->dummy_surface == NULL) { free (ctx); return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); } if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) { free (ctx); return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); } } status = _cairo_gl_dispatch_init (&ctx->base.dispatch, eglGetProcAddress); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } status = _cairo_gl_context_init (&ctx->base); if (unlikely (status)) { if (ctx->dummy_surface != EGL_NO_SURFACE) eglDestroySurface (dpy, ctx->dummy_surface); free (ctx); return _cairo_gl_context_create_in_error (status); } eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return &ctx->base.base; } cairo_surface_t * cairo_gl_surface_create_for_egl (cairo_device_t *device, EGLSurface egl, int width, int height) { cairo_egl_surface_t *surface; if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); if (device->backend->type != CAIRO_DEVICE_TYPE_GL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); if (width <= 0 || height <= 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); surface = calloc (1, sizeof (cairo_egl_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_gl_surface_init (device, &surface->base, CAIRO_CONTENT_COLOR_ALPHA, width, height); surface->egl = egl; return &surface->base.base; } static cairo_bool_t is_egl_device (cairo_device_t *device) { return (device->backend != NULL && device->backend->type == CAIRO_DEVICE_TYPE_GL); } static cairo_egl_context_t *to_egl_context (cairo_device_t *device) { return (cairo_egl_context_t *) device; } EGLDisplay cairo_egl_device_get_display (cairo_device_t *device) { if (! is_egl_device (device)) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return EGL_NO_DISPLAY; } return to_egl_context (device)->display; } cairo_public EGLContext cairo_egl_device_get_context (cairo_device_t *device) { if (! is_egl_device (device)) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return EGL_NO_CONTEXT; } return to_egl_context (device)->context; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-error-inline.h����������������������������0000664�0000000�0000000�00000003551�12710376503�0026155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef _CAIRO_ERROR_INLINE_H_ #define _CAIRO_ERROR_INLINE_H_ #include "cairo-error-private.h" CAIRO_BEGIN_DECLS static inline cairo_status_t _cairo_public_status (cairo_int_status_t status) { assert (status <= CAIRO_INT_STATUS_LAST_STATUS); return (cairo_status_t) status; } #endif /* _CAIRO_ERROR_INLINE_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-error-private.h���������������������������0000664�0000000�0000000�00000010453�12710376503�0026350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef _CAIRO_ERROR_PRIVATE_H_ #define _CAIRO_ERROR_PRIVATE_H_ #include "cairo.h" #include "cairo-compiler-private.h" #include "cairo-types-private.h" #include <assert.h> CAIRO_BEGIN_DECLS /* Sure wish C had a real enum type so that this would be distinct * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 * offset. We want to keep it fit in int8_t as the compiler may choose * that for #cairo_status_t */ enum _cairo_int_status { CAIRO_INT_STATUS_SUCCESS = 0, CAIRO_INT_STATUS_NO_MEMORY, CAIRO_INT_STATUS_INVALID_RESTORE, CAIRO_INT_STATUS_INVALID_POP_GROUP, CAIRO_INT_STATUS_NO_CURRENT_POINT, CAIRO_INT_STATUS_INVALID_MATRIX, CAIRO_INT_STATUS_INVALID_STATUS, CAIRO_INT_STATUS_NULL_POINTER, CAIRO_INT_STATUS_INVALID_STRING, CAIRO_INT_STATUS_INVALID_PATH_DATA, CAIRO_INT_STATUS_READ_ERROR, CAIRO_INT_STATUS_WRITE_ERROR, CAIRO_INT_STATUS_SURFACE_FINISHED, CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH, CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH, CAIRO_INT_STATUS_INVALID_CONTENT, CAIRO_INT_STATUS_INVALID_FORMAT, CAIRO_INT_STATUS_INVALID_VISUAL, CAIRO_INT_STATUS_FILE_NOT_FOUND, CAIRO_INT_STATUS_INVALID_DASH, CAIRO_INT_STATUS_INVALID_DSC_COMMENT, CAIRO_INT_STATUS_INVALID_INDEX, CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE, CAIRO_INT_STATUS_TEMP_FILE_ERROR, CAIRO_INT_STATUS_INVALID_STRIDE, CAIRO_INT_STATUS_FONT_TYPE_MISMATCH, CAIRO_INT_STATUS_USER_FONT_IMMUTABLE, CAIRO_INT_STATUS_USER_FONT_ERROR, CAIRO_INT_STATUS_NEGATIVE_COUNT, CAIRO_INT_STATUS_INVALID_CLUSTERS, CAIRO_INT_STATUS_INVALID_SLANT, CAIRO_INT_STATUS_INVALID_WEIGHT, CAIRO_INT_STATUS_INVALID_SIZE, CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED, CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH, CAIRO_INT_STATUS_DEVICE_ERROR, CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION, CAIRO_INT_STATUS_DEVICE_FINISHED, CAIRO_INT_STATUS_LAST_STATUS, CAIRO_INT_STATUS_UNSUPPORTED = 100, CAIRO_INT_STATUS_DEGENERATE, CAIRO_INT_STATUS_NOTHING_TO_DO, CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, CAIRO_INT_STATUS_IMAGE_FALLBACK, CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, }; typedef enum _cairo_int_status cairo_int_status_t; #define _cairo_status_is_error(status) \ (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS) #define _cairo_int_status_is_error(status) \ (status != CAIRO_INT_STATUS_SUCCESS && status < CAIRO_INT_STATUS_LAST_STATUS) cairo_private cairo_status_t _cairo_error (cairo_status_t status); /* hide compiler warnings when discarding the return value */ #define _cairo_error_throw(status) do { \ cairo_status_t status__ = _cairo_error (status); \ (void) status__; \ } while (0) CAIRO_END_DECLS #endif /* _CAIRO_ERROR_PRIVATE_H_ */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-error.c�����������������������������������0000664�0000000�0000000�00000005007�12710376503�0024672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-private.h" #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include <assert.h> /** * _cairo_error: * @status: a status value indicating an error, (eg. not * %CAIRO_STATUS_SUCCESS) * * Checks that status is an error status, but does nothing else. * * All assignments of an error status to any user-visible object * within the cairo application should result in a call to * _cairo_error(). * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. * * Return value: the error status. **/ cairo_status_t _cairo_error (cairo_status_t status) { CAIRO_ENSURE_UNIQUE; assert (_cairo_status_is_error (status)); return status; } COMPILE_TIME_ASSERT ((int)CAIRO_INT_STATUS_LAST_STATUS == (int)CAIRO_STATUS_LAST_STATUS); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-fallback-compositor.c���������������������0000664�0000000�0000000�00000013521�12710376503�0027474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-offset-private.h" /* high-level compositor interface */ static cairo_int_status_t _cairo_fallback_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_image_surface_t *image; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); status = _cairo_surface_offset_paint (&image->base, extents->unbounded.x, extents->unbounded.y, extents->op, &extents->source_pattern.base, extents->clip); return _cairo_surface_unmap_image (extents->surface, image); } static cairo_int_status_t _cairo_fallback_compositor_mask (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_image_surface_t *image; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); status = _cairo_surface_offset_mask (&image->base, extents->unbounded.x, extents->unbounded.y, extents->op, &extents->source_pattern.base, &extents->mask_pattern.base, extents->clip); return _cairo_surface_unmap_image (extents->surface, image); } static cairo_int_status_t _cairo_fallback_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_image_surface_t *image; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); status = _cairo_surface_offset_stroke (&image->base, extents->unbounded.x, extents->unbounded.y, extents->op, &extents->source_pattern.base, path, style, ctm, ctm_inverse, tolerance, antialias, extents->clip); return _cairo_surface_unmap_image (extents->surface, image); } static cairo_int_status_t _cairo_fallback_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_image_surface_t *image; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); status = _cairo_surface_offset_fill (&image->base, extents->unbounded.x, extents->unbounded.y, extents->op, &extents->source_pattern.base, path, fill_rule, tolerance, antialias, extents->clip); return _cairo_surface_unmap_image (extents->surface, image); } static cairo_int_status_t _cairo_fallback_compositor_glyphs (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_image_surface_t *image; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); status = _cairo_surface_offset_glyphs (&image->base, extents->unbounded.x, extents->unbounded.y, extents->op, &extents->source_pattern.base, scaled_font, glyphs, num_glyphs, extents->clip); return _cairo_surface_unmap_image (extents->surface, image); } const cairo_compositor_t _cairo_fallback_compositor = { &__cairo_no_compositor, _cairo_fallback_compositor_paint, _cairo_fallback_compositor_mask, _cairo_fallback_compositor_stroke, _cairo_fallback_compositor_fill, _cairo_fallback_compositor_glyphs, }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-features.h.in�����������������������������0000664�0000000�0000000�00000001702�12710376503�0025767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef CAIRO_FEATURES_H #define CAIRO_FEATURES_H #define CAIRO_HAS_SVG_SURFACE 1 #define CAIRO_HAS_PDF_SURFACE 1 #define CAIRO_HAS_PS_SURFACE 1 #define CAIRO_HAS_PNG_FUNCTIONS 1 #define CAIRO_HAS_IMAGE_SURFACE 1 #define CAIRO_HAS_USER_FONT 1 #define CAIRO_HAS_WIN32_FONT ${CAIRO_HAS_WIN32_FONT} #define CAIRO_HAS_WIN32_SURFACE ${CAIRO_HAS_WIN32_SURFACE} #define CAIRO_WIN32_STATIC_BUILD ${CAIRO_WIN32_STATIC_BUILD} #define CAIRO_HAS_FC_FONT ${CAIRO_HAS_FC_FONT} #define CAIRO_HAS_FT_FONT ${CAIRO_HAS_FT_FONT} #define CAIRO_HAS_QUARTZ_FONT ${CAIRO_HAS_QUARTZ_FONT} #define CAIRO_HAS_QUARTZ_SURFACE ${CAIRO_HAS_QUARTZ_SURFACE} #define CAIRO_HAS_QUARTZ_IMAGE_SURFACE ${CAIRO_HAS_QUARTZ_IMAGE_SURFACE} #define CAIRO_HAS_GL_SURFACE ${CAIRO_HAS_GL_SURFACE} #define CAIRO_HAS_VG_SURFACE ${CAIRO_HAS_VG_SURFACE} #define CAIRO_HAS_EGL_FUNCTIONS ${CAIRO_HAS_EGL_FUNCTIONS} #define CAIRO_HAS_GLESV2_SURFACE ${CAIRO_HAS_GLESV2_SURFACE} #endif /* CAIRO_FEATURES_H */ ��������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-fixed-private.h���������������������������0000664�0000000�0000000�00000026005�12710376503�0026316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation * * Contributor(s): * Vladimir Vukicevic <vladimir@pobox.com> */ #ifndef CAIRO_FIXED_PRIVATE_H #define CAIRO_FIXED_PRIVATE_H #include "cairo-fixed-type-private.h" #include "cairo-wideint-private.h" #include "cairoint.h" /* Implementation */ #if (CAIRO_FIXED_BITS != 32) # error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type. # error To remove this limitation, you will have to fix the tesselator. #endif #define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS)) #define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS)) #define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1)) #define CAIRO_FIXED_ERROR_DOUBLE (1. / (2 * CAIRO_FIXED_ONE_DOUBLE)) #define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS))) #define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK) static inline cairo_fixed_t _cairo_fixed_from_int (int i) { return i << CAIRO_FIXED_FRAC_BITS; } /* This is the "magic number" approach to converting a double into fixed * point as described here: * * http://www.stereopsis.com/sree/fpu2006.html (an overview) * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail) * * The basic idea is to add a large enough number to the double that the * literal floating point is moved up to the extent that it forces the * double's value to be shifted down to the bottom of the mantissa (to make * room for the large number being added in). Since the mantissa is, at a * given moment in time, a fixed point integer itself, one can convert a * float to various fixed point representations by moving around the point * of a floating point number through arithmetic operations. This behavior * is reliable on most modern platforms as it is mandated by the IEEE-754 * standard for floating point arithmetic. * * For our purposes, a "magic number" must be carefully selected that is * both large enough to produce the desired point-shifting effect, and also * has no lower bits in its representation that would interfere with our * value at the bottom of the mantissa. The magic number is calculated as * follows: * * (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5 * * where in our case: * - MANTISSA_SIZE for 64-bit doubles is 52 * - FRACTIONAL_SIZE for 16.16 fixed point is 16 * * Although this approach provides a very large speedup of this function * on a wide-array of systems, it does come with two caveats: * * 1) It uses banker's rounding as opposed to arithmetic rounding. * 2) It doesn't function properly if the FPU is in single-precision * mode. */ /* The 16.16 number must always be available */ #define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0) #if CAIRO_FIXED_BITS <= 32 #define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5) /* For 32-bit fixed point numbers */ static inline cairo_fixed_t _cairo_fixed_from_double (double d) { union { double d; int32_t i[2]; } u; u.d = d + CAIRO_MAGIC_NUMBER_FIXED; #ifdef FLOAT_WORDS_BIGENDIAN return u.i[1]; #else return u.i[0]; #endif } #else # error Please define a magic number for your fixed point type! # error See cairo-fixed-private.h for details. #endif static inline cairo_fixed_t _cairo_fixed_from_26_6 (uint32_t i) { #if CAIRO_FIXED_FRAC_BITS > 6 return i << (CAIRO_FIXED_FRAC_BITS - 6); #else return i >> (6 - CAIRO_FIXED_FRAC_BITS); #endif } static inline cairo_fixed_t _cairo_fixed_from_16_16 (uint32_t i) { #if CAIRO_FIXED_FRAC_BITS > 16 return i << (CAIRO_FIXED_FRAC_BITS - 16); #else return i >> (16 - CAIRO_FIXED_FRAC_BITS); #endif } static inline double _cairo_fixed_to_double (cairo_fixed_t f) { return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; } static inline int _cairo_fixed_is_integer (cairo_fixed_t f) { return (f & CAIRO_FIXED_FRAC_MASK) == 0; } static inline cairo_fixed_t _cairo_fixed_floor (cairo_fixed_t f) { return f & ~CAIRO_FIXED_FRAC_MASK; } static inline cairo_fixed_t _cairo_fixed_ceil (cairo_fixed_t f) { return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK); } static inline cairo_fixed_t _cairo_fixed_round (cairo_fixed_t f) { return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2); } static inline cairo_fixed_t _cairo_fixed_round_down (cairo_fixed_t f) { return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2); } static inline int _cairo_fixed_integer_part (cairo_fixed_t f) { return f >> CAIRO_FIXED_FRAC_BITS; } static inline int _cairo_fixed_integer_round (cairo_fixed_t f) { return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2); } static inline int _cairo_fixed_integer_round_down (cairo_fixed_t f) { return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2); } static inline int _cairo_fixed_fractional_part (cairo_fixed_t f) { return f & CAIRO_FIXED_FRAC_MASK; } static inline int _cairo_fixed_integer_floor (cairo_fixed_t f) { if (f >= 0) return f >> CAIRO_FIXED_FRAC_BITS; else return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1; } static inline int _cairo_fixed_integer_ceil (cairo_fixed_t f) { if (f > 0) return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1; else return - (-f >> CAIRO_FIXED_FRAC_BITS); } /* A bunch of explicit 16.16 operators; we need these * to interface with pixman and other backends that require * 16.16 fixed point types. */ static inline cairo_fixed_16_16_t _cairo_fixed_to_16_16 (cairo_fixed_t f) { #if (CAIRO_FIXED_FRAC_BITS == 16) && (CAIRO_FIXED_BITS == 32) return f; #elif CAIRO_FIXED_FRAC_BITS > 16 /* We're just dropping the low bits, so we won't ever got over/underflow here */ return f >> (CAIRO_FIXED_FRAC_BITS - 16); #else cairo_fixed_16_16_t x; /* Handle overflow/underflow by clamping to the lowest/highest * value representable as 16.16 */ if ((f >> CAIRO_FIXED_FRAC_BITS) < INT16_MIN) { x = INT32_MIN; } else if ((f >> CAIRO_FIXED_FRAC_BITS) > INT16_MAX) { x = INT32_MAX; } else { x = f << (16 - CAIRO_FIXED_FRAC_BITS); } return x; #endif } static inline cairo_fixed_16_16_t _cairo_fixed_16_16_from_double (double d) { union { double d; int32_t i[2]; } u; u.d = d + CAIRO_MAGIC_NUMBER_FIXED_16_16; #ifdef FLOAT_WORDS_BIGENDIAN return u.i[1]; #else return u.i[0]; #endif } static inline int _cairo_fixed_16_16_floor (cairo_fixed_16_16_t f) { if (f >= 0) return f >> 16; else return -((-f - 1) >> 16) - 1; } static inline double _cairo_fixed_16_16_to_double (cairo_fixed_16_16_t f) { return ((double) f) / (double) (1 << 16); } #if CAIRO_FIXED_BITS == 32 static inline cairo_fixed_t _cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b) { cairo_int64_t temp = _cairo_int32x32_64_mul (a, b); return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS)); } /* computes round (a * b / c) */ static inline cairo_fixed_t _cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) { cairo_int64_t ab = _cairo_int32x32_64_mul (a, b); cairo_int64_t c64 = _cairo_int32_to_int64 (c); return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo); } /* computes floor (a * b / c) */ static inline cairo_fixed_t _cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) { return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c); } static inline cairo_fixed_t _cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, const cairo_point_t *p2, cairo_fixed_t x) { cairo_fixed_t y, dx; if (x == p1->x) return p1->y; if (x == p2->x) return p2->y; y = p1->y; dx = p2->x - p1->x; if (dx != 0) y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx); return y; } static inline cairo_fixed_t _cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, const cairo_point_t *p2, cairo_fixed_t y) { cairo_fixed_t x, dy; if (y == p1->y) return p1->x; if (y == p2->y) return p2->x; x = p1->x; dy = p2->y - p1->y; if (dy != 0) x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy); return x; } /* Intersect two segments based on the algorithm described at * http://paulbourke.net/geometry/pointlineplane/. This implementation * uses floating point math. */ static inline cairo_bool_t _slow_segment_intersection (const cairo_point_t *seg1_p1, const cairo_point_t *seg1_p2, const cairo_point_t *seg2_p1, const cairo_point_t *seg2_p2, cairo_point_t *intersection) { double denominator, u_a, u_b; double seg1_dx, seg1_dy, seg2_dx, seg2_dy, seg_start_dx, seg_start_dy; seg1_dx = _cairo_fixed_to_double (seg1_p2->x - seg1_p1->x); seg1_dy = _cairo_fixed_to_double (seg1_p2->y - seg1_p1->y); seg2_dx = _cairo_fixed_to_double (seg2_p2->x - seg2_p1->x); seg2_dy = _cairo_fixed_to_double (seg2_p2->y - seg2_p1->y); denominator = (seg2_dy * seg1_dx) - (seg2_dx * seg1_dy); if (denominator == 0) return FALSE; seg_start_dx = _cairo_fixed_to_double (seg1_p1->x - seg2_p1->x); seg_start_dy = _cairo_fixed_to_double (seg1_p1->y - seg2_p1->y); u_a = ((seg2_dx * seg_start_dy) - (seg2_dy * seg_start_dx)) / denominator; u_b = ((seg1_dx * seg_start_dy) - (seg1_dy * seg_start_dx)) / denominator; if (u_a <= 0 || u_a >= 1 || u_b <= 0 || u_b >= 1) return FALSE; intersection->x = seg1_p1->x + _cairo_fixed_from_double ((u_a * seg1_dx)); intersection->y = seg1_p1->y + _cairo_fixed_from_double ((u_a * seg1_dy)); return TRUE; } #else # error Please define multiplication and other operands for your fixed-point type size #endif #endif /* CAIRO_FIXED_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-fixed-type-private.h����������������������0000664�0000000�0000000�00000005143�12710376503�0027275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation * * Contributor(s): * Vladimir Vukicevic <vladimir@pobox.com> */ #ifndef CAIRO_FIXED_TYPE_PRIVATE_H #define CAIRO_FIXED_TYPE_PRIVATE_H #include "cairo-wideint-type-private.h" /* * Fixed-point configuration */ typedef int32_t cairo_fixed_16_16_t; typedef cairo_int64_t cairo_fixed_32_32_t; typedef cairo_int64_t cairo_fixed_48_16_t; typedef cairo_int128_t cairo_fixed_64_64_t; typedef cairo_int128_t cairo_fixed_96_32_t; /* Eventually, we should allow changing this, but I think * there are some assumptions in the tesselator about the * size of a fixed type. For now, it must be 32. */ #define CAIRO_FIXED_BITS 32 /* The number of fractional bits. Changing this involves * making sure that you compute a double-to-fixed magic number. * (see below). */ #define CAIRO_FIXED_FRAC_BITS 8 /* A signed type %CAIRO_FIXED_BITS in size; the main fixed point type */ typedef int32_t cairo_fixed_t; /* An unsigned type of the same size as #cairo_fixed_t */ typedef uint32_t cairo_fixed_unsigned_t; typedef struct _cairo_point { cairo_fixed_t x; cairo_fixed_t y; } cairo_point_t; #endif /* CAIRO_FIXED_TYPE_PRIVATE_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-fixed.c�����������������������������������0000664�0000000�0000000�00000003072�12710376503�0024640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-fixed-private.h" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-font-face-twin-data.c���������������������0000664�0000000�0000000�00000060100�12710376503�0027264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* See cairo-font-face-twin.c for copyright info */ #include "cairoint.h" const int8_t _cairo_twin_outlines[] = { /* 0x0 '\0' offset 0 */ 0, 24, 42, 0, 2, 2, 0, 24, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 24, -42, 'l', 24, 0, 'l', 0, 0, 'e', 'X', 'X', /* 0x20 ' ' offset 28 */ 0, 4, 0, 0, 0, 0, /* snap_x */ /* snap_y */ 'e', 'X', 'X', 'X', 'X', 'X', /* 0x21 '!' offset 40 */ 0, 0, 42, 0, 1, 3, 0, /* snap_x */ -42, -14, 0, /* snap_y */ 'm', 0, -42, 'l', 0, -14, 'm', 0, 0, 'l', 0, 0, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x22 '"' offset 90 */ 0, 16, 42, -28, 2, 2, 0, 16, /* snap_x */ -42, -28, /* snap_y */ 'm', 0, -42, 'l', 0, -28, 'm', 16, -42, 'l', 16, -28, 'e', 'X', /* 0x23 '#' offset 114 */ 0, 30, 50, 14, 2, 5, 0, 30, /* snap_x */ -24, -21, -15, -12, 0, /* snap_y */ 'm', 16, -50, 'l', 2, 14, 'm', 28, -50, 'l', 14, 14, 'm', 2, -24, 'l', 30, -24, 'm', 0, -12, 'l', 28, -12, 'e', /* 0x24 '$' offset 152 */ 0, 28, 50, 8, 4, 4, 0, 10, 18, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 10, -50, 'l', 10, 8, 'm', 18, -50, 'l', 18, 8, 'm', 28, -36, 'c', 24, -42, 18, -42, 14, -42, 'c', 10, -42, 0, -42, 0, -34, 'c', 0, -25, 8, -24, 14, -22, 'c', 20, -20, 28, -19, 28, -9, 'c', 28, 0, 18, 0, 14, 0, 'c', 10, 0, 4, 0, 0, -6, 'e', /* 0x25 '%' offset 224 */ 0, 36, 42, 0, 4, 7, 0, 14, 22, 36, /* snap_x */ -42, -38, -28, -21, -15, -14, 0, /* snap_y */ 'm', 10, -42, 'c', 12, -41, 14, -40, 14, -36, 'c', 14, -30, 11, -28, 6, -28, 'c', 2, -28, 0, -30, 0, -34, 'c', 0, -39, 3, -42, 8, -42, 'l', 10, -42, 'c', 18, -37, 28, -37, 36, -42, 'l', 0, 0, 'm', 28, -14, 'c', 24, -14, 22, -11, 22, -6, 'c', 22, -2, 24, 0, 28, 0, 'c', 33, 0, 36, -2, 36, -8, 'c', 36, -12, 34, -14, 30, -14, 'l', 28, -14, 'e', 'X', 'X', 'X', /* 0x26 '&' offset 323 */ 0, 40, 42, 0, 4, 4, 0, 10, 22, 40, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 40, -24, 'c', 40, -27, 39, -28, 37, -28, 'c', 29, -28, 32, 0, 12, 0, 'c', 0, 0, 0, -8, 0, -10, 'c', 0, -24, 22, -20, 22, -34, 'c', 22, -45, 10, -45, 10, -34, 'c', 10, -27, 25, 0, 36, 0, 'c', 39, 0, 40, -1, 40, -4, 'e', /* 0x27 ''' offset 390 */ 0, 4, 42, -30, 2, 2, 0, 4, /* snap_x */ -42, -28, /* snap_y */ 'm', 2, -38, 'c', -1, -38, -1, -42, 2, -42, 'c', 6, -42, 5, -33, 0, -30, 'e', 'X', /* 0x28 '(' offset 419 */ 0, 14, 50, 14, 2, 2, 0, 14, /* snap_x */ -50, 14, /* snap_y */ 'm', 14, -50, 'c', -5, -32, -5, -5, 14, 14, 'e', 'X', /* 0x29 ')' offset 441 */ 0, 14, 50, 14, 2, 2, 0, 14, /* snap_x */ -15, 14, /* snap_y */ 'm', 0, -50, 'c', 19, -34, 19, -2, 0, 14, 'e', 'X', /* 0x2a '*' offset 463 */ 0, 20, 30, -6, 3, 3, 0, 10, 20, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 10, -30, 'l', 10, -6, 'm', 0, -24, 'l', 20, -12, 'm', 20, -24, 'l', 0, -12, 'e', /* 0x2b '+' offset 494 */ 0, 36, 36, 0, 3, 4, 0, 18, 36, /* snap_x */ -21, -18, -15, 0, /* snap_y */ 'm', 18, -36, 'l', 18, 0, 'm', 0, -18, 'l', 36, -18, 'e', /* 0x2c ',' offset 520 */ 0, 4, 4, 8, 2, 3, 0, 4, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 4, -2, 'c', 4, 1, 0, 1, 0, -2, 'c', 0, -5, 4, -5, 4, -2, 'c', 4, 4, 2, 6, 0, 8, 'e', /* 0x2d '-' offset 556 */ 0, 36, 18, -18, 2, 4, 0, 36, /* snap_x */ -21, -18, -15, 0, /* snap_y */ 'm', 0, -18, 'l', 36, -18, 'e', /* 0x2e '.' offset 575 */ 0, 4, 4, 0, 2, 3, 0, 4, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 2, -4, 'c', -1, -4, -1, 0, 2, 0, 'c', 5, 0, 5, -4, 2, -4, 'e', /* 0x2f '/' offset 604 */ 0, 36, 50, 14, 2, 3, 0, 36, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 36, -50, 'l', 0, 14, 'e', /* 0x30 '0' offset 622 */ 0, 28, 42, 0, 2, 4, 0, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 14, -42, 'c', 9, -42, 0, -42, 0, -21, 'c', 0, 0, 9, 0, 14, 0, 'c', 19, 0, 28, 0, 28, -21, 'c', 28, -42, 19, -42, 14, -42, 'E', /* 0x31 '1' offset 666 */ 0, 28, 42, 0, 2, 3, 0, 17, 28 /* snap_x */ -42, -34, 0, /* snap_y */ 'm', 7, -34, 'c', 11, -35, 15, -38, 17, -42, 'l', 17, 0, 'e', /* 0x32 '2' offset 691 */ 0, 28, 42, 0, 4, 4, 0, 2, 26, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 2, -32, 'c', 2, -34, 2, -42, 14, -42, 'c', 26, -42, 26, -34, 26, -32, 'c', 26, -30, 25, -25, 10, -10, 'l', 0, 0, 'l', 28, 0, 'e', /* 0x33 '3' offset 736 */ 0, 28, 42, 0, 2, 5, 0, 28, /* snap_x */ -42, -26, -21, -15, 0, /* snap_y */ 'm', 4, -42, 'l', 26, -42, 'l', 14, -26, 'c', 21, -26, 28, -26, 28, -14, 'c', 28, 0, 17, 0, 13, 0, 'c', 8, 0, 3, -1, 0, -8, 'e', /* 0x34 '4' offset 780 */ 0, 28, 42, 0, 3, 3, 0, 20, 30, /* snap_x */ -42, -14, 0, /* snap_y */ 'm', 20, 0, 'l', 20, -42, 'l', 0, -14, 'l', 30, -14, 'e', 'X', 'X', 'X', 'X', /* 0x35 '5' offset 809 */ 0, 28, 42, 0, 2, 5, 0, 28, /* snap_x */ -42, -28, -21, -15, 0, /* snap_y */ 'm', 24, -42, 'l', 4, -42, 'l', 2, -24, 'c', 5, -27, 10, -28, 13, -28, 'c', 16, -28, 28, -28, 28, -14, 'c', 28, 0, 16, 0, 13, 0, 'c', 10, 0, 3, 0, 0, -8, 'e', /* 0x36 '6' offset 860 */ 0, 28, 42, 0, 2, 5, 0, 26, /* snap_x */ -42, -26, -21, -15, 0, /* snap_y */ 'm', 24, -36, 'c', 22, -41, 19, -42, 14, -42, 'c', 9, -42, 0, -41, 0, -19, 'c', 0, -1, 9, 0, 13, 0, 'c', 18, 0, 26, -3, 26, -13, 'c', 26, -18, 23, -26, 13, -26, 'c', 10, -26, 1, -24, 0, -14, 'e', /* 0x37 '7' offset 919 */ 0, 28, 42, 0, 2, 4, 0, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 28, -42, 'l', 8, 0, 'e', 'X', 'X', 'X', /* 0x38 '8' offset 944 */ 0, 28, 42, 0, 4, 4, 0, 2, 26, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 14, -42, 'c', 5, -42, 2, -40, 2, -34, 'c', 2, -18, 28, -32, 28, -11, 'c', 28, 0, 18, 0, 14, 0, 'c', 10, 0, 0, 0, 0, -11, 'c', 0, -32, 26, -18, 26, -34, 'c', 26, -40, 23, -42, 14, -42, 'E', /* 0x39 '9' offset 1004 */ 0, 28, 42, 0, 2, 5, 0, 26, /* snap_x */ -42, -21, -16, -15, 0, /* snap_y */ 'm', 26, -28, 'c', 25, -16, 13, -16, 13, -16, 'c', 8, -16, 0, -19, 0, -29, 'c', 0, -34, 3, -42, 13, -42, 'c', 24, -42, 26, -32, 26, -23, 'c', 26, -14, 24, 0, 12, 0, 'c', 7, 0, 4, -2, 2, -6, 'e', /* 0x3a ':' offset 1063 */ 0, 4, 28, 0, 2, 3, 0, 4, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 2, -28, 'c', -1, -28, -1, -24, 2, -24, 'c', 5, -24, 5, -28, 2, -28, 'm', 2, -4, 'c', -1, -4, -1, 0, 2, 0, 'c', 5, 0, 5, -4, 2, -4, 'e', /* 0x3b ';' offset 1109 */ 0, 4, 28, 8, 2, 3, 0, 4, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 2, -28, 'c', -1, -28, -1, -24, 2, -24, 'c', 5, -24, 5, -28, 2, -28, 'm', 4, -2, 'c', 4, 1, 0, 1, 0, -2, 'c', 0, -5, 4, -5, 4, -2, 'c', 4, 3, 2, 6, 0, 8, 'e', /* 0x3c '<' offset 1162 */ 0, 32, 36, 0, 2, 3, 0, 32, /* snap_x */ -36, -18, 0, /* snap_y */ 'm', 32, -36, 'l', 0, -18, 'l', 32, 0, 'e', /* 0x3d '=' offset 1183 */ 0, 36, 24, -12, 2, 2, 0, 36, /* snap_x */ -24, -15, /* snap_y */ 'm', 0, -24, 'l', 36, -24, 'm', 0, -12, 'l', 36, -12, 'e', 'X', 'X', 'X', /* 0x3e '>' offset 1209 */ 0, 32, 36, 0, 2, 3, 0, 32, /* snap_x */ -36, -18, 0, /* snap_y */ 'm', 0, -36, 'l', 32, -18, 'l', 0, 0, 'e', /* 0x3f '?' offset 1230 */ 0, 24, 42, 0, 3, 4, 0, 12, 24, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 0, -32, 'c', 0, -34, 0, -42, 12, -42, 'c', 24, -42, 24, -34, 24, -32, 'c', 24, -29, 24, -24, 12, -20, 'l', 12, -14, 'm', 12, 0, 'l', 12, 0, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x40 '@' offset 1288 */ 0, 42, 42, 0, 1, 6, 30, /* snap_x */ -42, -32, -21, -15, -10, 0, /* snap_y */ 'm', 30, -26, 'c', 28, -31, 24, -32, 21, -32, 'c', 10, -32, 10, -23, 10, -19, 'c', 10, -13, 11, -10, 19, -10, 'c', 30, -10, 28, -21, 30, -32, 'c', 27, -10, 30, -10, 34, -10, 'c', 41, -10, 42, -19, 42, -22, 'c', 42, -34, 34, -42, 21, -42, 'c', 9, -42, 0, -34, 0, -21, 'c', 0, -9, 8, 0, 21, 0, 'c', 30, 0, 34, -3, 36, -6, 'e', /* 0x41 'A' offset 1375 */ 0, 32, 42, 0, 2, 3, 0, 32, /* snap_x */ -42, -14, 0, /* snap_y */ 'm', 0, 0, 'l', 16, -42, 'l', 32, 0, 'm', 6, -14, 'l', 26, -14, 'e', 'X', 'X', 'X', 'X', /* 0x42 'B' offset 1406 */ 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ -42, -22, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 18, -42, 'c', 32, -42, 32, -22, 18, -22, 'l', 0, -22, 'l', 18, -22, 'c', 32, -22, 32, 0, 18, 0, 'E', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x43 'C' offset 1455 */ 0, 30, 42, 0, 2, 4, 0, 30, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 30, -32, 'c', 26, -42, 21, -42, 16, -42, 'c', 2, -42, 0, -29, 0, -21, 'c', 0, -13, 2, 0, 16, 0, 'c', 21, 0, 26, 0, 30, -10, 'e', /* 0x44 'D' offset 1499 */ 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 14, -42, 'c', 33, -42, 33, 0, 14, 0, 'E', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x45 'E' offset 1534 */ 0, 26, 42, 0, 2, 3, 0, 26, /* snap_x */ -42, -22, 0, /* snap_y */ 'm', 26, -42, 'l', 0, -42, 'l', 0, 0, 'l', 26, 0, 'm', 0, -22, 'l', 16, -22, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x46 'F' offset 1572 */ 0, 26, 42, 0, 2, 3, 0, 26, /* snap_x */ -42, -22, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 26, -42, 'm', 0, -22, 'l', 16, -22, 'e', 'X', 'X', 'X', 'X', 'X', /* 0x47 'G' offset 1604 */ 0, 30, 42, 0, 2, 5, 0, 30, /* snap_x */ -42, -21, -16, -15, 0, /* snap_y */ 'm', 30, -32, 'c', 26, -42, 21, -42, 16, -42, 'c', 2, -42, 0, -29, 0, -21, 'c', 0, -13, 2, 0, 16, 0, 'c', 28, 0, 30, -7, 30, -16, 'l', 20, -16, 'e', 'X', 'X', 'X', /* 0x48 'H' offset 1655 */ 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ -42, -22, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 28, -42, 'l', 28, 0, 'm', 0, -22, 'l', 28, -22, 'e', 'X', /* 0x49 'I' offset 1686 */ 0, 0, 42, 0, 1, 2, 0, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'e', 'X', /* 0x4a 'J' offset 1703 */ 0, 20, 42, 0, 2, 3, 0, 20, /* snap_x */ -42, -15, 0, /* snap_y */ 'm', 20, -42, 'l', 20, -10, 'c', 20, 3, 0, 3, 0, -10, 'l', 0, -14, 'e', /* 0x4b 'K' offset 1731 */ 0, 28, 42, 0, 2, 3, 0, 28, /* snap_x */ -42, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 28, -42, 'l', 0, -14, 'm', 10, -24, 'l', 28, 0, 'e', /* 0x4c 'L' offset 1761 */ 0, 24, 42, 0, 2, 2, 0, 24, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'l', 24, 0, 'e', 'X', 'X', 'X', 'X', /* 0x4d 'M' offset 1785 */ 0, 32, 42, 0, 2, 2, 0, 32, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 16, 0, 'l', 32, -42, 'l', 32, 0, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x4e 'N' offset 1821 */ 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 28, 0, 'l', 28, -42, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x4f 'O' offset 1851 */ 0, 32, 42, 0, 2, 4, 0, 32, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 16, -42, 'c', 2, -42, 0, -29, 0, -21, 'c', 0, -13, 2, 0, 16, 0, 'c', 30, 0, 32, -13, 32, -21, 'c', 32, -29, 30, -42, 16, -42, 'E', /* 0x50 'P' offset 1895 */ 0, 28, 42, 0, 2, 5, 0, 28, /* snap_x */ -42, -21, -20, -15, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 18, -42, 'c', 32, -42, 32, -20, 18, -20, 'l', 0, -20, 'e', 'X', 'X', 'X', /* 0x51 'Q' offset 1931 */ 0, 32, 42, 4, 2, 4, 0, 32, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 16, -42, 'c', 2, -42, 0, -29, 0, -21, 'c', 0, -13, 2, 0, 16, 0, 'c', 30, 0, 32, -13, 32, -21, 'c', 32, -29, 30, -42, 16, -42, 'M', 18, -8, 'l', 30, 4, 'e', /* 0x52 'R' offset 1981 */ 0, 28, 42, 0, 2, 5, 0, 28, /* snap_x */ -42, -22, -21, -15, 0, /* snap_y */ 'm', 0, 0, 'l', 0, -42, 'l', 18, -42, 'c', 32, -42, 31, -22, 18, -22, 'l', 0, -22, 'm', 14, -22, 'l', 28, 0, 'e', 'X', 'X', 'X', /* 0x53 'S' offset 2023 */ 0, 28, 42, 0, 2, 4, 0, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 28, -36, 'c', 25, -41, 21, -42, 14, -42, 'c', 10, -42, 0, -42, 0, -34, 'c', 0, -17, 28, -28, 28, -9, 'c', 28, 0, 19, 0, 14, 0, 'c', 7, 0, 3, -1, 0, -6, 'e', /* 0x54 'T' offset 2074 */ 0, 28, 42, 0, 3, 4, 0, 14, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 14, -42, 'l', 14, 0, 'm', 0, -42, 'l', 28, -42, 'e', /* 0x55 'U' offset 2100 */ 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, -12, 'c', 0, 4, 28, 4, 28, -12, 'l', 28, -42, 'e', 'X', /* 0x56 'V' offset 2128 */ 0, 32, 42, 0, 2, 2, 0, 32, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 16, 0, 'l', 32, -42, 'e', 'X', 'X', 'X', 'X', /* 0x57 'W' offset 2152 */ 0, 40, 42, 0, 2, 2, 0, 40, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 10, 0, 'l', 20, -42, 'l', 30, 0, 'l', 40, -42, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x58 'X' offset 2188 */ 0, 28, 42, 0, 2, 2, 0, 28, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 28, 0, 'm', 28, -42, 'l', 0, 0, 'e', 'X', /* 0x59 'Y' offset 2212 */ 0, 32, 42, 0, 3, 3, 0, 16, 32, /* snap_x */ -42, -21, 0, /* snap_y */ 'm', 0, -42, 'l', 16, -22, 'l', 16, 0, 'm', 32, -42, 'l', 16, -22, 'e', /* 0x5a 'Z' offset 2240 */ 0, 28, 42, 0, 2, 4, 0, 28, /* snap_x */ -42, -21, -15, 0, /* snap_y */ 'm', 28, 0, 'l', 0, 0, 'l', 28, -42, 'l', 0, -42, 'e', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x5b '[' offset 2271 */ 0, 14, 44, 0, 2, 4, 0, 14, /* snap_x */ -44, -21, -15, 0, /* snap_y */ 'm', 14, -44, 'l', 0, -44, 'l', 0, 0, 'l', 14, 0, 'e', /* 0x5c '\' offset 2296 */ 0, 36, 50, 14, 2, 3, 0, 36, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 0, -50, 'l', 36, 14, 'e', /* 0x5d ']' offset 2314 */ 0, 14, 44, 0, 2, 4, 0, 14, /* snap_x */ -44, -21, -15, 0, /* snap_y */ 'm', 0, -44, 'l', 14, -44, 'l', 14, 0, 'l', 0, 0, 'e', /* 0x5e '^' offset 2339 */ 0, 32, 46, -18, 2, 3, 0, 32, /* snap_x */ -21, -15, 0, /* snap_y */ 'm', 0, -18, 'l', 16, -46, 'l', 32, -18, 'e', 'X', 'X', 'X', /* 0x5f '_' offset 2363 */ 0, 36, 0, 0, 2, 1, 0, 36, /* snap_x */ 0, /* snap_y */ 'm', 0, 0, 'l', 36, 0, 'e', 'X', 'X', /* 0x60 '`' offset 2381 */ 0, 4, 42, -30, 2, 2, 0, 4, /* snap_x */ -42, 0, /* snap_y */ 'm', 4, -42, 'c', 2, -40, 0, -39, 0, -32, 'c', 0, -31, 1, -30, 2, -30, 'c', 5, -30, 5, -34, 2, -34, 'e', 'X', /* 0x61 'a' offset 2417 */ 0, 24, 28, 0, 2, 4, 0, 24, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 24, -28, 'l', 24, 0, 'm', 24, -22, 'c', 21, -27, 18, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -1, 24, -6, 'e', /* 0x62 'b' offset 2467 */ 0, 24, 42, 0, 2, 4, 0, 24, /* snap_x */ -42, -28, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 0, -22, 'c', 3, -26, 6, -28, 11, -28, 'c', 22, -28, 24, -19, 24, -14, 'c', 24, -9, 22, 0, 11, 0, 'c', 6, 0, 3, -2, 0, -6, 'e', /* 0x63 'c' offset 2517 */ 0, 24, 28, 0, 2, 4, 0, 24, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 24, -22, 'c', 21, -26, 18, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -2, 24, -6, 'e', /* 0x64 'd' offset 2561 */ 0, 24, 42, 0, 2, 4, 0, 24, /* snap_x */ -42, -28, -15, 0, /* snap_y */ 'm', 24, -42, 'l', 24, 0, 'm', 24, -22, 'c', 21, -26, 18, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -2, 24, -6, 'e', /* 0x65 'e' offset 2611 */ 0, 24, 28, 0, 2, 5, 0, 24, /* snap_x */ -28, -21, -16, -15, 0, /* snap_y */ 'm', 0, -16, 'l', 24, -16, 'c', 24, -20, 24, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -2, 24, -6, 'e', /* 0x66 'f' offset 2659 */ 0, 16, 42, 0, 3, 5, 0, 6, 16, /* snap_x */ -42, -28, -21, -15, 0, /* snap_y */ 'm', 16, -42, 'c', 8, -42, 6, -40, 6, -34, 'l', 6, 0, 'm', 0, -28, 'l', 14, -28, 'e', /* 0x67 'g' offset 2693 */ 0, 24, 28, 14, 2, 5, 0, 24, /* snap_x */ -28, -21, -15, 0, 14, /* snap_y */ 'm', 24, -28, 'l', 24, 4, 'c', 23, 14, 16, 14, 13, 14, 'c', 10, 14, 8, 14, 6, 12, 'm', 24, -22, 'c', 21, -26, 18, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -2, 24, -6, 'e', /* 0x68 'h' offset 2758 */ 0, 22, 42, 0, 2, 4, 0, 22, /* snap_x */ -42, -28, -15, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 0, -20, 'c', 8, -32, 22, -31, 22, -20, 'l', 22, 0, 'e', /* 0x69 'i' offset 2790 */ 0, 0, 44, 0, 1, 3, 0, /* snap_x */ -42, -28, 0, /* snap_y */ 'm', 0, -42, 'l', 0, -42, 'm', 0, -28, 'l', 0, 0, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x6a 'j' offset 2826 */ -8, 4, 44, 14, 3, 5, -8, 2, 4, /* snap_x */ -42, -21, -15, 0, 14, /* snap_y */ 'm', 2, -42, 'l', 2, -42, 'm', 2, -28, 'l', 2, 6, 'c', 2, 13, -1, 14, -8, 14, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x6b 'k' offset 2870 */ 0, 22, 42, 0, 2, 3, 0, 22, /* snap_x */ -42, -28, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'm', 20, -28, 'l', 0, -8, 'm', 8, -16, 'l', 22, 0, 'e', /* 0x6c 'l' offset 2900 */ 0, 0, 42, 0, 1, 2, 0, /* snap_x */ -42, 0, /* snap_y */ 'm', 0, -42, 'l', 0, 0, 'e', 'X', /* 0x6d 'm' offset 2917 */ 0, 44, 28, 0, 3, 3, 0, 22, 44, /* snap_x */ -28, -21, 0, /* snap_y */ 'm', 0, -28, 'l', 0, 0, 'm', 0, -20, 'c', 5, -29, 22, -33, 22, -20, 'l', 22, 0, 'm', 22, -20, 'c', 27, -29, 44, -33, 44, -20, 'l', 44, 0, 'e', 'X', /* 0x6e 'n' offset 2963 */ 0, 22, 28, 0, 2, 3, 0, 22, /* snap_x */ -28, -21, 0, /* snap_y */ 'm', 0, -28, 'l', 0, 0, 'm', 0, -20, 'c', 4, -28, 22, -34, 22, -20, 'l', 22, 0, 'e', 'X', /* 0x6f 'o' offset 2995 */ 0, 26, 28, 0, 2, 4, 0, 26, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 24, 0, 26, -9, 26, -14, 'c', 26, -19, 24, -28, 13, -28, 'E', /* 0x70 'p' offset 3039 */ 0, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ -28, -21, 0, 14, /* snap_y */ 'm', 0, -28, 'l', 0, 14, 'm', 0, -22, 'c', 3, -26, 6, -28, 11, -28, 'c', 22, -28, 24, -19, 24, -14, 'c', 24, -9, 22, 0, 11, 0, 'c', 6, 0, 3, -2, 0, -6, 'e', /* 0x71 'q' offset 3089 */ 0, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ -28, -21, 0, 14, /* snap_y */ 'm', 24, -28, 'l', 24, 14, 'm', 24, -22, 'c', 21, -26, 18, -28, 13, -28, 'c', 2, -28, 0, -19, 0, -14, 'c', 0, -9, 2, 0, 13, 0, 'c', 18, 0, 21, -2, 24, -6, 'e', /* 0x72 'r' offset 3139 */ 0, 16, 28, 0, 2, 4, 0, 16, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 0, 0, 'm', 0, -16, 'c', 2, -27, 7, -28, 16, -28, 'e', /* 0x73 's' offset 3168 */ 0, 22, 28, 0, 2, 4, 0, 22, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 22, -22, 'c', 22, -27, 16, -28, 11, -28, 'c', 4, -28, 0, -26, 0, -22, 'c', 0, -11, 22, -20, 22, -7, 'c', 22, 0, 17, 0, 11, 0, 'c', 6, 0, 0, -1, 0, -6, 'e', /* 0x74 't' offset 3219 */ 0, 16, 42, 0, 3, 4, 0, 6, 16, /* snap_x */ -42, -28, -21, 0, /* snap_y */ 'm', 6, -42, 'l', 6, -8, 'c', 6, -2, 8, 0, 16, 0, 'm', 0, -28, 'l', 14, -28, 'e', /* 0x75 'u' offset 3252 */ 0, 22, 28, 0, 2, 3, 0, 22, /* snap_x */ -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 0, -8, 'c', 0, 6, 18, 0, 22, -8, 'm', 22, -28, 'l', 22, 0, 'e', /* 0x76 'v' offset 3283 */ 0, 24, 28, 0, 2, 3, 0, 24, /* snap_x */ -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 12, 0, 'l', 24, -28, 'e', 'X', 'X', 'X', /* 0x77 'w' offset 3307 */ 0, 32, 28, 0, 2, 3, 0, 32, /* snap_x */ -28, -15, 0, /* snap_y */ 'm', 0, -28, 'l', 8, 0, 'l', 16, -28, 'l', 24, 0, 'l', 32, -28, 'e', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x78 'x' offset 3343 */ 0, 22, 28, 0, 2, 2, 0, 22, /* snap_x */ -28, 0, /* snap_y */ 'm', 0, -28, 'l', 22, 0, 'm', 22, -28, 'l', 0, 0, 'e', 'X', /* 0x79 'y' offset 3367 */ -2, 24, 28, 14, 2, 4, 0, 24, /* snap_x */ -28, -15, 0, 14, /* snap_y */ 'm', 0, -28, 'l', 12, 0, 'm', 24, -28, 'l', 12, 0, 'c', 6, 13, 0, 14, -2, 14, 'e', /* 0x7a 'z' offset 3399 */ 0, 22, 28, 0, 2, 4, 0, 22, /* snap_x */ -28, -21, -15, 0, /* snap_y */ 'm', 22, 0, 'l', 0, 0, 'l', 22, -28, 'l', 0, -28, 'e', 'X', 'X', 'X', 'X', 'X', 'X', /* 0x7b '{' offset 3430 */ 0, 16, 44, 0, 3, 5, 0, 6, 16, /* snap_x */ -44, -24, -21, -15, 0, /* snap_y */ 'm', 16, -44, 'c', 10, -44, 6, -42, 6, -36, 'l', 6, -24, 'l', 0, -24, 'l', 6, -24, 'l', 6, -8, 'c', 6, -2, 10, 0, 16, 0, 'e', /* 0x7c '|' offset 3474 */ 0, 0, 50, 14, 1, 2, 0, /* snap_x */ -50, 14, /* snap_y */ 'm', 0, -50, 'l', 0, 14, 'e', 'X', /* 0x7d '}' offset 3491 */ 0, 16, 44, 0, 3, 5, 0, 10, 16, /* snap_x */ -44, -24, -21, -15, 0, /* snap_y */ 'm', 0, -44, 'c', 6, -44, 10, -42, 10, -36, 'l', 10, -24, 'l', 16, -24, 'l', 10, -24, 'l', 10, -8, 'c', 10, -2, 6, 0, 0, 0, 'e', /* 0x7e '~' offset 3535 */ 0, 36, 24, -12, 2, 5, 0, 36, /* snap_x */ -24, -21, -15, -12, 0, /* snap_y */ 'm', 0, -14, 'c', 1, -21, 4, -24, 8, -24, 'c', 18, -24, 18, -12, 28, -12, 'c', 32, -12, 35, -15, 36, -22, 'e', }; const uint16_t _cairo_twin_charmap[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 40, 90, 114, 152, 224, 323, 390, 419, 441, 463, 494, 520, 556, 575, 604, 622, 666, 691, 736, 780, 809, 860, 919, 944, 1004, 1063, 1109, 1162, 1183, 1209, 1230, 1288, 1375, 1406, 1455, 1499, 1534, 1572, 1604, 1655, 1686, 1703, 1731, 1761, 1785, 1821, 1851, 1895, 1931, 1981, 2023, 2074, 2100, 2128, 2152, 2188, 2212, 2240, 2271, 2296, 2314, 2339, 2363, 2381, 2417, 2467, 2517, 2561, 2611, 2659, 2693, 2758, 2790, 2826, 2870, 2900, 2917, 2963, 2995, 3039, 3089, 3139, 3168, 3219, 3252, 3283, 3307, 3343, 3367, 3399, 3430, 3474, 3491, 3535, 0, }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-font-face-twin.c��������������������������0000664�0000000�0000000�00000045021�12710376503�0026362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Keith Packard * Copyright © 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith Packard <keithp@keithp.com> * Behdad Esfahbod <behdad@behdad.org> */ #include "cairoint.h" #include "cairo-error-private.h" #include <math.h> /* * This file implements a user-font rendering the descendant of the Hershey * font coded by Keith Packard for use in the Twin window system. * The actual font data is in cairo-font-face-twin-data.c * * Ported to cairo user font and extended by Behdad Esfahbod. */ static cairo_user_data_key_t twin_properties_key; /* * Face properties */ /* We synthesize multiple faces from the twin data. Here is the parameters. */ /* The following tables and matching code are copied from Pango */ /* CSS weight */ typedef enum { TWIN_WEIGHT_THIN = 100, TWIN_WEIGHT_ULTRALIGHT = 200, TWIN_WEIGHT_LIGHT = 300, TWIN_WEIGHT_BOOK = 380, TWIN_WEIGHT_NORMAL = 400, TWIN_WEIGHT_MEDIUM = 500, TWIN_WEIGHT_SEMIBOLD = 600, TWIN_WEIGHT_BOLD = 700, TWIN_WEIGHT_ULTRABOLD = 800, TWIN_WEIGHT_HEAVY = 900, TWIN_WEIGHT_ULTRAHEAVY = 1000 } twin_face_weight_t; /* CSS stretch */ typedef enum { TWIN_STRETCH_ULTRA_CONDENSED, TWIN_STRETCH_EXTRA_CONDENSED, TWIN_STRETCH_CONDENSED, TWIN_STRETCH_SEMI_CONDENSED, TWIN_STRETCH_NORMAL, TWIN_STRETCH_SEMI_EXPANDED, TWIN_STRETCH_EXPANDED, TWIN_STRETCH_EXTRA_EXPANDED, TWIN_STRETCH_ULTRA_EXPANDED } twin_face_stretch_t; typedef struct { int value; const char str[16]; } FieldMap; static const FieldMap slant_map[] = { { CAIRO_FONT_SLANT_NORMAL, "" }, { CAIRO_FONT_SLANT_NORMAL, "Roman" }, { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, { CAIRO_FONT_SLANT_ITALIC, "Italic" } }; static const FieldMap smallcaps_map[] = { { FALSE, "" }, { TRUE, "Small-Caps" } }; static const FieldMap weight_map[] = { { TWIN_WEIGHT_THIN, "Thin" }, { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, { TWIN_WEIGHT_LIGHT, "Light" }, { TWIN_WEIGHT_BOOK, "Book" }, { TWIN_WEIGHT_NORMAL, "" }, { TWIN_WEIGHT_NORMAL, "Regular" }, { TWIN_WEIGHT_MEDIUM, "Medium" }, { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, { TWIN_WEIGHT_BOLD, "Bold" }, { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, { TWIN_WEIGHT_HEAVY, "Heavy" }, { TWIN_WEIGHT_HEAVY, "Black" }, { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } }; static const FieldMap stretch_map[] = { { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, { TWIN_STRETCH_CONDENSED, "Condensed" }, { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, { TWIN_STRETCH_NORMAL, "" }, { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, { TWIN_STRETCH_EXPANDED, "Expanded" }, { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } }; static const FieldMap monospace_map[] = { { FALSE, "" }, { TRUE, "Mono" }, { TRUE, "Monospace" } }; typedef struct _twin_face_properties { cairo_font_slant_t slant; twin_face_weight_t weight; twin_face_stretch_t stretch; /* lets have some fun */ cairo_bool_t monospace; cairo_bool_t smallcaps; } twin_face_properties_t; static cairo_bool_t field_matches (const char *s1, const char *s2, int len) { int c1, c2; while (len && *s1 && *s2) { #define TOLOWER(c) \ (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) c1 = TOLOWER (*s1); c2 = TOLOWER (*s2); if (c1 != c2) { if (c1 == '-') { s1++; continue; } return FALSE; } s1++; s2++; len--; } return len == 0 && *s1 == '\0'; } static cairo_bool_t parse_int (const char *word, size_t wordlen, int *out) { char *end; long val = strtol (word, &end, 10); int i = val; if (end != word && (end == word + wordlen) && val >= 0 && val == i) { if (out) *out = i; return TRUE; } return FALSE; } static cairo_bool_t find_field (const char *what, const FieldMap *map, int n_elements, const char *str, int len, int *val) { int i; cairo_bool_t had_prefix = FALSE; if (what) { i = strlen (what); if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') { str += i + 1; len -= i + 1; had_prefix = TRUE; } } for (i=0; i<n_elements; i++) { if (map[i].str[0] && field_matches (map[i].str, str, len)) { if (val) *val = map[i].value; return TRUE; } } if (!what || had_prefix) return parse_int (str, len, val); return FALSE; } static void parse_field (twin_face_properties_t *props, const char *str, int len) { if (field_matches ("Normal", str, len)) return; #define FIELD(NAME) \ if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \ (int *)(void *)&props->NAME)) \ return; \ FIELD (weight); FIELD (slant); FIELD (stretch); FIELD (smallcaps); FIELD (monospace); #undef FIELD } static void face_props_parse (twin_face_properties_t *props, const char *s) { const char *start, *end; for (start = end = s; *end; end++) { if (*end != ' ' && *end != ':') continue; if (start < end) parse_field (props, start, end - start); start = end + 1; } if (start < end) parse_field (props, start, end - start); } static twin_face_properties_t * twin_font_face_create_properties (cairo_font_face_t *twin_face) { twin_face_properties_t *props; props = malloc (sizeof (twin_face_properties_t)); if (unlikely (props == NULL)) return NULL; props->stretch = TWIN_STRETCH_NORMAL; props->slant = CAIRO_FONT_SLANT_NORMAL; props->weight = TWIN_WEIGHT_NORMAL; props->monospace = FALSE; props->smallcaps = FALSE; if (unlikely (cairo_font_face_set_user_data (twin_face, &twin_properties_key, props, free))) { free (props); return NULL; } return props; } static cairo_status_t twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, cairo_toy_font_face_t *toy_face) { twin_face_properties_t *props; props = twin_font_face_create_properties (twin_face); if (unlikely (props == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); props->slant = toy_face->slant; props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; face_props_parse (props, toy_face->family); return CAIRO_STATUS_SUCCESS; } /* * Scaled properties */ typedef struct _twin_scaled_properties { twin_face_properties_t *face_props; cairo_bool_t snap; /* hint outlines */ double weight; /* unhinted pen width */ double penx, peny; /* hinted pen width */ double marginl, marginr; /* hinted side margins */ double stretch; /* stretch factor */ } twin_scaled_properties_t; static void compute_hinting_scale (cairo_t *cr, double x, double y, double *scale, double *inv) { cairo_user_to_device_distance (cr, &x, &y); *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); *inv = 1 / *scale; } static void compute_hinting_scales (cairo_t *cr, double *x_scale, double *x_scale_inv, double *y_scale, double *y_scale_inv) { double x, y; x = 1; y = 0; compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); x = 0; y = 1; compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); } #define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) #define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) /* This controls the global font size */ #define F(g) ((g) / 72.) static void twin_hint_pen_and_margins(cairo_t *cr, double *penx, double *peny, double *marginl, double *marginr) { double x_scale, x_scale_inv; double y_scale, y_scale_inv; double margin; compute_hinting_scales (cr, &x_scale, &x_scale_inv, &y_scale, &y_scale_inv); *penx = SNAPXI (*penx); if (*penx < x_scale_inv) *penx = x_scale_inv; *peny = SNAPYI (*peny); if (*peny < y_scale_inv) *peny = y_scale_inv; margin = *marginl + *marginr; *marginl = SNAPXI (*marginl); if (*marginl < x_scale_inv) *marginl = x_scale_inv; *marginr = margin - *marginl; if (*marginr < 0) *marginr = 0; *marginr = SNAPXI (*marginr); } static cairo_status_t twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, cairo_t *cr) { cairo_status_t status; twin_scaled_properties_t *props; props = malloc (sizeof (twin_scaled_properties_t)); if (unlikely (props == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), &twin_properties_key); props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; /* weight */ props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); /* pen & margins */ props->penx = props->peny = props->weight; props->marginl = props->marginr = F (4); if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) twin_hint_pen_and_margins(cr, &props->penx, &props->peny, &props->marginl, &props->marginr); /* stretch */ props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); /* Save it */ status = cairo_scaled_font_set_user_data (scaled_font, &twin_properties_key, props, free); if (unlikely (status)) goto FREE_PROPS; return CAIRO_STATUS_SUCCESS; FREE_PROPS: free (props); return status; } /* * User-font implementation */ static cairo_status_t twin_scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_font_extents_t *metrics) { metrics->ascent = F (54); metrics->descent = 1 - metrics->ascent; return twin_scaled_font_compute_properties (scaled_font, cr); } #define TWIN_GLYPH_MAX_SNAP_X 4 #define TWIN_GLYPH_MAX_SNAP_Y 7 typedef struct { int n_snap_x; int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; int n_snap_y; int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; } twin_snap_info_t; #define twin_glyph_left(g) ((g)[0]) #define twin_glyph_right(g) ((g)[1]) #define twin_glyph_ascent(g) ((g)[2]) #define twin_glyph_descent(g) ((g)[3]) #define twin_glyph_n_snap_x(g) ((g)[4]) #define twin_glyph_n_snap_y(g) ((g)[5]) #define twin_glyph_snap_x(g) (&g[6]) #define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) #define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) static void twin_compute_snap (cairo_t *cr, twin_snap_info_t *info, const signed char *b) { int s, n; const signed char *snap; double x_scale, x_scale_inv; double y_scale, y_scale_inv; compute_hinting_scales (cr, &x_scale, &x_scale_inv, &y_scale, &y_scale_inv); snap = twin_glyph_snap_x (b); n = twin_glyph_n_snap_x (b); info->n_snap_x = n; assert (n <= TWIN_GLYPH_MAX_SNAP_X); for (s = 0; s < n; s++) { info->snap_x[s] = snap[s]; info->snapped_x[s] = SNAPXI (F (snap[s])); } snap = twin_glyph_snap_y (b); n = twin_glyph_n_snap_y (b); info->n_snap_y = n; assert (n <= TWIN_GLYPH_MAX_SNAP_Y); for (s = 0; s < n; s++) { info->snap_y[s] = snap[s]; info->snapped_y[s] = SNAPYI (F (snap[s])); } } static double twin_snap (int8_t v, int n, int8_t *snap, double *snapped) { int s; if (!n) return F(v); if (snap[0] == v) return snapped[0]; for (s = 0; s < n - 1; s++) { if (snap[s+1] == v) return snapped[s+1]; if (snap[s] <= v && v <= snap[s+1]) { int before = snap[s]; int after = snap[s+1]; int dist = after - before; double snap_before = snapped[s]; double snap_after = snapped[s+1]; double dist_before = v - before; return snap_before + (snap_after - snap_before) * dist_before / dist; } } return F(v); } #define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) #define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) static cairo_status_t twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics) { double x1, y1, x2, y2, x3, y3; double marginl; twin_scaled_properties_t *props; twin_snap_info_t info; const int8_t *b; const int8_t *g; int8_t w; double gw; props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); /* Save glyph space, we need it when stroking */ cairo_save (cr); /* center the pen */ cairo_translate (cr, props->penx * .5, -props->peny * .5); /* small-caps */ if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { glyph += 'A' - 'a'; /* 28 and 42 are small and capital letter heights of the glyph data */ cairo_scale (cr, 1, 28. / 42); } /* slant */ if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; cairo_transform (cr, &shear); } b = _cairo_twin_outlines + _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; g = twin_glyph_draw(b); w = twin_glyph_right(b); gw = F(w); marginl = props->marginl; /* monospace */ if (props->face_props->monospace) { double monow = F(24); double extra = props->penx + props->marginl + props->marginr; cairo_scale (cr, (monow + extra) / (gw + extra), 1); gw = monow; /* resnap margin for new transform */ { double x, y, x_scale, x_scale_inv; x = 1; y = 0; compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); marginl = SNAPXI (marginl); } } cairo_translate (cr, marginl, 0); /* stretch */ cairo_scale (cr, props->stretch, 1); if (props->snap) twin_compute_snap (cr, &info, b); else info.n_snap_x = info.n_snap_y = 0; /* advance width */ metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; /* glyph shape */ for (;;) { switch (*g++) { case 'M': cairo_close_path (cr); /* fall through */ case 'm': x1 = SNAPX(*g++); y1 = SNAPY(*g++); cairo_move_to (cr, x1, y1); continue; case 'L': cairo_close_path (cr); /* fall through */ case 'l': x1 = SNAPX(*g++); y1 = SNAPY(*g++); cairo_line_to (cr, x1, y1); continue; case 'C': cairo_close_path (cr); /* fall through */ case 'c': x1 = SNAPX(*g++); y1 = SNAPY(*g++); x2 = SNAPX(*g++); y2 = SNAPY(*g++); x3 = SNAPX(*g++); y3 = SNAPY(*g++); cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); continue; case 'E': cairo_close_path (cr); /* fall through */ case 'e': cairo_restore (cr); /* restore glyph space */ cairo_set_tolerance (cr, 0.01); cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_width (cr, 1); cairo_scale (cr, props->penx, props->peny); cairo_stroke (cr); break; case 'X': /* filler */ continue; } break; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, unsigned long unicode, unsigned long *glyph) { /* We use an identity charmap. Which means we could live * with no unicode_to_glyph method too. But we define this * to map all unknown chars to a single unknown glyph to * reduce pressure on cache. */ if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) *glyph = unicode; else *glyph = 0; return CAIRO_STATUS_SUCCESS; } /* * Face constructor */ static cairo_font_face_t * _cairo_font_face_twin_create_internal (void) { cairo_font_face_t *twin_font_face; twin_font_face = cairo_user_font_face_create (); cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); return twin_font_face; } cairo_font_face_t * _cairo_font_face_twin_create_fallback (void) { cairo_font_face_t *twin_font_face; twin_font_face = _cairo_font_face_twin_create_internal (); if (! twin_font_face_create_properties (twin_font_face)) { cairo_font_face_destroy (twin_font_face); return (cairo_font_face_t *) &_cairo_font_face_nil; } return twin_font_face; } cairo_status_t _cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face) { cairo_status_t status; cairo_font_face_t *twin_font_face; twin_font_face = _cairo_font_face_twin_create_internal (); status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); if (status) { cairo_font_face_destroy (twin_font_face); return status; } *font_face = twin_font_face; return CAIRO_STATUS_SUCCESS; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-font-face.c�������������������������������0000664�0000000�0000000�00000022604�12710376503�0025405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> */ #include "cairoint.h" #include "cairo-error-private.h" /** * SECTION:cairo-font-face * @Title: cairo_font_face_t * @Short_Description: Base class for font faces * @See_Also: #cairo_scaled_font_t * * #cairo_font_face_t represents a particular font at a particular weight, * slant, and other characteristic but no size, transformation, or size. * * Font faces are created using <firstterm>font-backend</firstterm>-specific * constructors, typically of the form * <function>cairo_<emphasis>backend</emphasis>_font_face_create(<!-- -->)</function>, * or implicitly using the <firstterm>toy</firstterm> text API by way of * cairo_select_font_face(). The resulting face can be accessed using * cairo_get_font_face(). **/ /* #cairo_font_face_t */ const cairo_font_face_t _cairo_font_face_nil = { { 0 }, /* hash_entry */ CAIRO_STATUS_NO_MEMORY, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL }; cairo_status_t _cairo_font_face_set_error (cairo_font_face_t *font_face, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS) return status; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (&font_face->status, status); return _cairo_error (status); } void _cairo_font_face_init (cairo_font_face_t *font_face, const cairo_font_face_backend_t *backend) { CAIRO_MUTEX_INITIALIZE (); font_face->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1); font_face->backend = backend; _cairo_user_data_array_init (&font_face->user_data); } /** * cairo_font_face_reference: * @font_face: a #cairo_font_face_t, (may be %NULL in which case this * function does nothing). * * Increases the reference count on @font_face by one. This prevents * @font_face from being destroyed until a matching call to * cairo_font_face_destroy() is made. * * The number of references to a #cairo_font_face_t can be get using * cairo_font_face_get_reference_count(). * * Return value: the referenced #cairo_font_face_t. * * Since: 1.0 **/ cairo_font_face_t * cairo_font_face_reference (cairo_font_face_t *font_face) { if (font_face == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return font_face; /* We would normally assert that we have a reference here but we * can't get away with that due to the zombie case as documented * in _cairo_ft_font_face_destroy. */ _cairo_reference_count_inc (&font_face->ref_count); return font_face; } slim_hidden_def (cairo_font_face_reference); /** * cairo_font_face_destroy: * @font_face: a #cairo_font_face_t * * Decreases the reference count on @font_face by one. If the result * is zero, then @font_face and all associated resources are freed. * See cairo_font_face_reference(). * * Since: 1.0 **/ void cairo_font_face_destroy (cairo_font_face_t *font_face) { if (font_face == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)); if (! _cairo_reference_count_dec_and_test (&font_face->ref_count)) return; if (font_face->backend->destroy) font_face->backend->destroy (font_face); /* We allow resurrection to deal with some memory management for the * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t * need to effectively mutually reference each other */ if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)) return; _cairo_user_data_array_fini (&font_face->user_data); free (font_face); } slim_hidden_def (cairo_font_face_destroy); /** * cairo_font_face_get_type: * @font_face: a font face * * This function returns the type of the backend used to create * a font face. See #cairo_font_type_t for available types. * * Return value: The type of @font_face. * * Since: 1.2 **/ cairo_font_type_t cairo_font_face_get_type (cairo_font_face_t *font_face) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return CAIRO_FONT_TYPE_TOY; return font_face->backend->type; } /** * cairo_font_face_get_reference_count: * @font_face: a #cairo_font_face_t * * Returns the current reference count of @font_face. * * Return value: the current reference count of @font_face. If the * object is a nil object, 0 will be returned. * * Since: 1.4 **/ unsigned int cairo_font_face_get_reference_count (cairo_font_face_t *font_face) { if (font_face == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count); } /** * cairo_font_face_status: * @font_face: a #cairo_font_face_t * * Checks whether an error has previously occurred for this * font face * * Return value: %CAIRO_STATUS_SUCCESS or another error such as * %CAIRO_STATUS_NO_MEMORY. * * Since: 1.0 **/ cairo_status_t cairo_font_face_status (cairo_font_face_t *font_face) { return font_face->status; } /** * cairo_font_face_get_user_data: * @font_face: a #cairo_font_face_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @font_face using the specified * key. If no user data has been attached with the given key this * function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.0 **/ void * cairo_font_face_get_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key) { return _cairo_user_data_array_get_data (&font_face->user_data, key); } slim_hidden_def (cairo_font_face_get_user_data); /** * cairo_font_face_set_user_data: * @font_face: a #cairo_font_face_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the font face * @destroy: a #cairo_destroy_func_t which will be called when the * font face is destroyed or when new user data is attached using the * same key. * * Attach user data to @font_face. To remove user data from a font face, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.0 **/ cairo_status_t cairo_font_face_set_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return font_face->status; return _cairo_user_data_array_set_data (&font_face->user_data, key, user_data, destroy); } slim_hidden_def (cairo_font_face_set_user_data); void _cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, const cairo_unscaled_font_backend_t *backend) { CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1); unscaled_font->backend = backend; } cairo_unscaled_font_t * _cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font) { if (unscaled_font == NULL) return NULL; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); _cairo_reference_count_inc (&unscaled_font->ref_count); return unscaled_font; } void _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) { if (unscaled_font == NULL) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count)) return; unscaled_font->backend->destroy (unscaled_font); free (unscaled_font); } ����������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-font-options.c����������������������������0000664�0000000�0000000�00000037241�12710376503�0026205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Owen Taylor <otaylor@redhat.com> */ #include "cairoint.h" #include "cairo-error-private.h" /** * SECTION:cairo-font-options * @Title: cairo_font_options_t * @Short_Description: How a font should be rendered * @See_Also: #cairo_scaled_font_t * * The font options specify how fonts should be rendered. Most of the * time the font options implied by a surface are just right and do not * need any changes, but for pixel-based targets tweaking font options * may result in superior output on a particular display. **/ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_ANTIALIAS_DEFAULT, CAIRO_SUBPIXEL_ORDER_DEFAULT, CAIRO_LCD_FILTER_DEFAULT, CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_METRICS_DEFAULT, CAIRO_ROUND_GLYPH_POS_DEFAULT }; /** * _cairo_font_options_init_default: * @options: a #cairo_font_options_t * * Initializes all fields of the font options object to default values. **/ void _cairo_font_options_init_default (cairo_font_options_t *options) { options->antialias = CAIRO_ANTIALIAS_DEFAULT; options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT; options->hint_style = CAIRO_HINT_STYLE_DEFAULT; options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT; options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT; } void _cairo_font_options_init_copy (cairo_font_options_t *options, const cairo_font_options_t *other) { options->antialias = other->antialias; options->subpixel_order = other->subpixel_order; options->lcd_filter = other->lcd_filter; options->hint_style = other->hint_style; options->hint_metrics = other->hint_metrics; options->round_glyph_positions = other->round_glyph_positions; } /** * cairo_font_options_create: * * Allocates a new font options object with all options initialized * to default values. * * Return value: a newly allocated #cairo_font_options_t. Free with * cairo_font_options_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_font_options_status(). * * Since: 1.0 **/ cairo_font_options_t * cairo_font_options_create (void) { cairo_font_options_t *options; options = malloc (sizeof (cairo_font_options_t)); if (!options) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_options_t *) &_cairo_font_options_nil; } _cairo_font_options_init_default (options); return options; } /** * cairo_font_options_copy: * @original: a #cairo_font_options_t * * Allocates a new font options object copying the option values from * @original. * * Return value: a newly allocated #cairo_font_options_t. Free with * cairo_font_options_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_font_options_status(). * * Since: 1.0 **/ cairo_font_options_t * cairo_font_options_copy (const cairo_font_options_t *original) { cairo_font_options_t *options; if (cairo_font_options_status ((cairo_font_options_t *) original)) return (cairo_font_options_t *) &_cairo_font_options_nil; options = malloc (sizeof (cairo_font_options_t)); if (!options) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_options_t *) &_cairo_font_options_nil; } _cairo_font_options_init_copy (options, original); return options; } /** * cairo_font_options_destroy: * @options: a #cairo_font_options_t * * Destroys a #cairo_font_options_t object created with * cairo_font_options_create() or cairo_font_options_copy(). * * Since: 1.0 **/ void cairo_font_options_destroy (cairo_font_options_t *options) { if (cairo_font_options_status (options)) return; free (options); } /** * cairo_font_options_status: * @options: a #cairo_font_options_t * * Checks whether an error has previously occurred for this * font options object * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.0 **/ cairo_status_t cairo_font_options_status (cairo_font_options_t *options) { if (options == NULL) return CAIRO_STATUS_NULL_POINTER; else if (options == (cairo_font_options_t *) &_cairo_font_options_nil) return CAIRO_STATUS_NO_MEMORY; else return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_font_options_status); /** * cairo_font_options_merge: * @options: a #cairo_font_options_t * @other: another #cairo_font_options_t * * Merges non-default options from @other into @options, replacing * existing values. This operation can be thought of as somewhat * similar to compositing @other onto @options with the operation * of %CAIRO_OPERATOR_OVER. * * Since: 1.0 **/ void cairo_font_options_merge (cairo_font_options_t *options, const cairo_font_options_t *other) { if (cairo_font_options_status (options)) return; if (cairo_font_options_status ((cairo_font_options_t *) other)) return; if (other->antialias != CAIRO_ANTIALIAS_DEFAULT) options->antialias = other->antialias; if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) options->subpixel_order = other->subpixel_order; if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) options->lcd_filter = other->lcd_filter; if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT) options->hint_style = other->hint_style; if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT) options->hint_metrics = other->hint_metrics; if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) options->round_glyph_positions = other->round_glyph_positions; } slim_hidden_def (cairo_font_options_merge); /** * cairo_font_options_equal: * @options: a #cairo_font_options_t * @other: another #cairo_font_options_t * * Compares two font options objects for equality. * * Return value: %TRUE if all fields of the two font options objects match. * Note that this function will return %FALSE if either object is in * error. * * Since: 1.0 **/ cairo_bool_t cairo_font_options_equal (const cairo_font_options_t *options, const cairo_font_options_t *other) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return FALSE; if (cairo_font_options_status ((cairo_font_options_t *) other)) return FALSE; if (options == other) return TRUE; return (options->antialias == other->antialias && options->subpixel_order == other->subpixel_order && options->lcd_filter == other->lcd_filter && options->hint_style == other->hint_style && options->hint_metrics == other->hint_metrics && options->round_glyph_positions == other->round_glyph_positions); } slim_hidden_def (cairo_font_options_equal); /** * cairo_font_options_hash: * @options: a #cairo_font_options_t * * Compute a hash for the font options object; this value will * be useful when storing an object containing a #cairo_font_options_t * in a hash table. * * Return value: the hash value for the font options object. * The return value can be cast to a 32-bit type if a * 32-bit hash value is needed. * * Since: 1.0 **/ unsigned long cairo_font_options_hash (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) options = &_cairo_font_options_nil; /* force default values */ return ((options->antialias) | (options->subpixel_order << 4) | (options->lcd_filter << 8) | (options->hint_style << 12) | (options->hint_metrics << 16)); } slim_hidden_def (cairo_font_options_hash); /** * cairo_font_options_set_antialias: * @options: a #cairo_font_options_t * @antialias: the new antialiasing mode * * Sets the antialiasing mode for the font options object. This * specifies the type of antialiasing to do when rendering text. * * Since: 1.0 **/ void cairo_font_options_set_antialias (cairo_font_options_t *options, cairo_antialias_t antialias) { if (cairo_font_options_status (options)) return; options->antialias = antialias; } slim_hidden_def (cairo_font_options_set_antialias); /** * cairo_font_options_get_antialias: * @options: a #cairo_font_options_t * * Gets the antialiasing mode for the font options object. * * Return value: the antialiasing mode * * Since: 1.0 **/ cairo_antialias_t cairo_font_options_get_antialias (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_ANTIALIAS_DEFAULT; return options->antialias; } /** * cairo_font_options_set_subpixel_order: * @options: a #cairo_font_options_t * @subpixel_order: the new subpixel order * * Sets the subpixel order for the font options object. The subpixel * order specifies the order of color elements within each pixel on * the display device when rendering with an antialiasing mode of * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for * #cairo_subpixel_order_t for full details. * * Since: 1.0 **/ void cairo_font_options_set_subpixel_order (cairo_font_options_t *options, cairo_subpixel_order_t subpixel_order) { if (cairo_font_options_status (options)) return; options->subpixel_order = subpixel_order; } slim_hidden_def (cairo_font_options_set_subpixel_order); /** * cairo_font_options_get_subpixel_order: * @options: a #cairo_font_options_t * * Gets the subpixel order for the font options object. * See the documentation for #cairo_subpixel_order_t for full details. * * Return value: the subpixel order for the font options object * * Since: 1.0 **/ cairo_subpixel_order_t cairo_font_options_get_subpixel_order (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_SUBPIXEL_ORDER_DEFAULT; return options->subpixel_order; } /** * _cairo_font_options_set_lcd_filter: * @options: a #cairo_font_options_t * @lcd_filter: the new LCD filter * * Sets the LCD filter for the font options object. The LCD filter * specifies how pixels are filtered when rendered with an antialiasing * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for * #cairo_lcd_filter_t for full details. **/ void _cairo_font_options_set_lcd_filter (cairo_font_options_t *options, cairo_lcd_filter_t lcd_filter) { if (cairo_font_options_status (options)) return; options->lcd_filter = lcd_filter; } /** * _cairo_font_options_get_lcd_filter: * @options: a #cairo_font_options_t * * Gets the LCD filter for the font options object. * See the documentation for #cairo_lcd_filter_t for full details. * * Return value: the LCD filter for the font options object **/ cairo_lcd_filter_t _cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_LCD_FILTER_DEFAULT; return options->lcd_filter; } /** * _cairo_font_options_set_round_glyph_positions: * @options: a #cairo_font_options_t * @round: the new rounding value * * Sets the rounding options for the font options object. If rounding is set, a * glyph's position will be rounded to integer values. **/ void _cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, cairo_round_glyph_positions_t round) { if (cairo_font_options_status (options)) return; options->round_glyph_positions = round; } /** * _cairo_font_options_get_round_glyph_positions: * @options: a #cairo_font_options_t * * Gets the glyph position rounding option for the font options object. * * Return value: The round glyph posistions flag for the font options object. **/ cairo_round_glyph_positions_t _cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_ROUND_GLYPH_POS_DEFAULT; return options->round_glyph_positions; } /** * cairo_font_options_set_hint_style: * @options: a #cairo_font_options_t * @hint_style: the new hint style * * Sets the hint style for font outlines for the font options object. * This controls whether to fit font outlines to the pixel grid, * and if so, whether to optimize for fidelity or contrast. * See the documentation for #cairo_hint_style_t for full details. * * Since: 1.0 **/ void cairo_font_options_set_hint_style (cairo_font_options_t *options, cairo_hint_style_t hint_style) { if (cairo_font_options_status (options)) return; options->hint_style = hint_style; } slim_hidden_def (cairo_font_options_set_hint_style); /** * cairo_font_options_get_hint_style: * @options: a #cairo_font_options_t * * Gets the hint style for font outlines for the font options object. * See the documentation for #cairo_hint_style_t for full details. * * Return value: the hint style for the font options object * * Since: 1.0 **/ cairo_hint_style_t cairo_font_options_get_hint_style (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_HINT_STYLE_DEFAULT; return options->hint_style; } /** * cairo_font_options_set_hint_metrics: * @options: a #cairo_font_options_t * @hint_metrics: the new metrics hinting mode * * Sets the metrics hinting mode for the font options object. This * controls whether metrics are quantized to integer values in * device units. * See the documentation for #cairo_hint_metrics_t for full details. * * Since: 1.0 **/ void cairo_font_options_set_hint_metrics (cairo_font_options_t *options, cairo_hint_metrics_t hint_metrics) { if (cairo_font_options_status (options)) return; options->hint_metrics = hint_metrics; } slim_hidden_def (cairo_font_options_set_hint_metrics); /** * cairo_font_options_get_hint_metrics: * @options: a #cairo_font_options_t * * Gets the metrics hinting mode for the font options object. * See the documentation for #cairo_hint_metrics_t for full details. * * Return value: the metrics hinting mode for the font options object * * Since: 1.0 **/ cairo_hint_metrics_t cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return CAIRO_HINT_METRICS_DEFAULT; return options->hint_metrics; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-fontconfig-private.h����������������������0000664�0000000�0000000�00000004721�12710376503�0027354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2000 Keith Packard * Copyright © 2005 Red Hat, Inc * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> * Keith Packard <keithp@keithp.com> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef _CAIRO_FONTCONFIG_PRIVATE_H #define _CAIRO_FONTCONFIG_PRIVATE_H #include "cairo.h" #if CAIRO_HAS_FC_FONT #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> #endif /* sub-pixel order */ #ifndef FC_RGBA_UNKNOWN #define FC_RGBA_UNKNOWN 0 #define FC_RGBA_RGB 1 #define FC_RGBA_BGR 2 #define FC_RGBA_VRGB 3 #define FC_RGBA_VBGR 4 #define FC_RGBA_NONE 5 #endif /* hinting style */ #ifndef FC_HINT_NONE #define FC_HINT_NONE 0 #define FC_HINT_SLIGHT 1 #define FC_HINT_MEDIUM 2 #define FC_HINT_FULL 3 #endif /* LCD filter */ #ifndef FC_LCD_NONE #define FC_LCD_NONE 0 #define FC_LCD_DEFAULT 1 #define FC_LCD_LIGHT 2 #define FC_LCD_LEGACY 3 #endif #endif /* _CAIRO_FONTCONFIG_PRIVATE_H */ �����������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-freed-pool-private.h����������������������0000664�0000000�0000000�00000006714�12710376503�0027260�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_FREED_POOL_H #define CAIRO_FREED_POOL_H #include "cairoint.h" #include "cairo-atomic-private.h" CAIRO_BEGIN_DECLS #define DISABLE_FREED_POOLS 0 #if HAS_ATOMIC_OPS && ! DISABLE_FREED_POOLS /* Keep a stash of recently freed clip_paths, since we need to * reallocate them frequently. */ #define MAX_FREED_POOL_SIZE 16 typedef struct { void *pool[MAX_FREED_POOL_SIZE]; int top; } freed_pool_t; static cairo_always_inline void * _atomic_fetch (void **slot) { void *ptr; do { ptr = _cairo_atomic_ptr_get (slot); } while (! _cairo_atomic_ptr_cmpxchg (slot, ptr, NULL)); return ptr; } static cairo_always_inline cairo_bool_t _atomic_store (void **slot, void *ptr) { return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr); } cairo_private void * _freed_pool_get_search (freed_pool_t *pool); static inline void * _freed_pool_get (freed_pool_t *pool) { void *ptr; int i; i = pool->top - 1; if (i < 0) i = 0; ptr = _atomic_fetch (&pool->pool[i]); if (likely (ptr != NULL)) { pool->top = i; return ptr; } /* either empty or contended */ return _freed_pool_get_search (pool); } cairo_private void _freed_pool_put_search (freed_pool_t *pool, void *ptr); static inline void _freed_pool_put (freed_pool_t *pool, void *ptr) { int i; i = pool->top; if (likely (i < ARRAY_LENGTH (pool->pool) && _atomic_store (&pool->pool[i], ptr))) { pool->top = i + 1; return; } /* either full or contended */ _freed_pool_put_search (pool, ptr); } cairo_private void _freed_pool_reset (freed_pool_t *pool); #define HAS_FREED_POOL 1 #else /* A warning about an unused freed-pool in a build without atomics * enabled usually indicates a missing _freed_pool_reset() in the * static reset function */ typedef int freed_pool_t; #define _freed_pool_get(pool) NULL #define _freed_pool_put(pool, ptr) free(ptr) #define _freed_pool_reset(ptr) #endif CAIRO_END_DECLS #endif /* CAIRO_FREED_POOL_PRIVATE_H */ ����������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-freed-pool.c������������������������������0000664�0000000�0000000�00000004605�12710376503�0025600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-freed-pool-private.h" #if HAS_FREED_POOL void * _freed_pool_get_search (freed_pool_t *pool) { void *ptr; int i; for (i = ARRAY_LENGTH (pool->pool); i--;) { ptr = _atomic_fetch (&pool->pool[i]); if (ptr != NULL) { pool->top = i; return ptr; } } /* empty */ pool->top = 0; return NULL; } void _freed_pool_put_search (freed_pool_t *pool, void *ptr) { int i; for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { if (_atomic_store (&pool->pool[i], ptr)) { pool->top = i + 1; return; } } /* full */ pool->top = i; free (ptr); } void _freed_pool_reset (freed_pool_t *pool) { int i; for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { free (pool->pool[i]); pool->pool[i] = NULL; } pool->top = 0; } #endif ���������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-freelist-private.h������������������������0000664�0000000�0000000�00000011043�12710376503�0027030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2006 Joonas Pihlaja * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef CAIRO_FREELIST_H #define CAIRO_FREELIST_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" #include "cairo-freelist-type-private.h" /* for stand-alone compilation*/ #ifndef VG #define VG(x) #endif #ifndef NULL #define NULL (void *) 0 #endif /* Initialise a freelist that will be responsible for allocating * nodes of size nodesize. */ cairo_private void _cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize); /* Deallocate any nodes in the freelist. */ cairo_private void _cairo_freelist_fini (cairo_freelist_t *freelist); /* Allocate a new node from the freelist. If the freelist contains no * nodes, a new one will be allocated using malloc(). The caller is * responsible for calling _cairo_freelist_free() or free() on the * returned node. Returns %NULL on memory allocation error. */ cairo_private void * _cairo_freelist_alloc (cairo_freelist_t *freelist); /* Allocate a new node from the freelist. If the freelist contains no * nodes, a new one will be allocated using calloc(). The caller is * responsible for calling _cairo_freelist_free() or free() on the * returned node. Returns %NULL on memory allocation error. */ cairo_private void * _cairo_freelist_calloc (cairo_freelist_t *freelist); /* Return a node to the freelist. This does not deallocate the memory, * but makes it available for later reuse by * _cairo_freelist_alloc(). */ cairo_private void _cairo_freelist_free (cairo_freelist_t *freelist, void *node); cairo_private void _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize); cairo_private void _cairo_freepool_fini (cairo_freepool_t *freepool); static inline void _cairo_freepool_reset (cairo_freepool_t *freepool) { while (freepool->pools != &freepool->embedded_pool) { cairo_freelist_pool_t *pool = freepool->pools; freepool->pools = pool->next; pool->next = freepool->freepools; freepool->freepools = pool; } freepool->embedded_pool.rem = sizeof (freepool->embedded_data); freepool->embedded_pool.data = freepool->embedded_data; } cairo_private void * _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool); static inline void * _cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool) { cairo_freelist_pool_t *pool; uint8_t *ptr; pool = freepool->pools; if (unlikely (freepool->nodesize > pool->rem)) return _cairo_freepool_alloc_from_new_pool (freepool); ptr = pool->data; pool->data += freepool->nodesize; pool->rem -= freepool->nodesize; VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize)); return ptr; } static inline void * _cairo_freepool_alloc (cairo_freepool_t *freepool) { cairo_freelist_node_t *node; node = freepool->first_free_node; if (node == NULL) return _cairo_freepool_alloc_from_pool (freepool); VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); freepool->first_free_node = node->next; VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); return node; } cairo_private cairo_status_t _cairo_freepool_alloc_array (cairo_freepool_t *freepool, int count, void **array); static inline void _cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) { cairo_freelist_node_t *node = ptr; node->next = freepool->first_free_node; freepool->first_free_node = node; VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize)); } #endif /* CAIRO_FREELIST_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-freelist-type-private.h�������������������0000664�0000000�0000000�00000003770�12710376503�0030017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2010 Joonas Pihlaja * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef CAIRO_FREELIST_TYPE_H #define CAIRO_FREELIST_TYPE_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" typedef struct _cairo_freelist_node cairo_freelist_node_t; struct _cairo_freelist_node { cairo_freelist_node_t *next; }; typedef struct _cairo_freelist { cairo_freelist_node_t *first_free_node; unsigned nodesize; } cairo_freelist_t; typedef struct _cairo_freelist_pool cairo_freelist_pool_t; struct _cairo_freelist_pool { cairo_freelist_pool_t *next; unsigned size, rem; uint8_t *data; }; typedef struct _cairo_freepool { cairo_freelist_node_t *first_free_node; cairo_freelist_pool_t *pools; cairo_freelist_pool_t *freepools; unsigned nodesize; cairo_freelist_pool_t embedded_pool; uint8_t embedded_data[1000]; } cairo_freepool_t; #endif /* CAIRO_FREELIST_TYPE_H */ ��������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-freelist.c��������������������������������0000664�0000000�0000000�00000012070�12710376503�0025354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2006 Joonas Pihlaja * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" void _cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize) { memset (freelist, 0, sizeof (cairo_freelist_t)); freelist->nodesize = nodesize; } void _cairo_freelist_fini (cairo_freelist_t *freelist) { cairo_freelist_node_t *node = freelist->first_free_node; while (node) { cairo_freelist_node_t *next; VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); next = node->next; free (node); node = next; } } void * _cairo_freelist_alloc (cairo_freelist_t *freelist) { if (freelist->first_free_node) { cairo_freelist_node_t *node; node = freelist->first_free_node; VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); freelist->first_free_node = node->next; VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); return node; } return malloc (freelist->nodesize); } void * _cairo_freelist_calloc (cairo_freelist_t *freelist) { void *node = _cairo_freelist_alloc (freelist); if (node) memset (node, 0, freelist->nodesize); return node; } void _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) { cairo_freelist_node_t *node = voidnode; if (node) { node->next = freelist->first_free_node; freelist->first_free_node = node; VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize)); } } void _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize) { freepool->first_free_node = NULL; freepool->pools = &freepool->embedded_pool; freepool->freepools = NULL; freepool->nodesize = nodesize; freepool->embedded_pool.next = NULL; freepool->embedded_pool.size = sizeof (freepool->embedded_data); freepool->embedded_pool.rem = sizeof (freepool->embedded_data); freepool->embedded_pool.data = freepool->embedded_data; VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, sizeof (freepool->embedded_data))); } void _cairo_freepool_fini (cairo_freepool_t *freepool) { cairo_freelist_pool_t *pool; pool = freepool->pools; while (pool != &freepool->embedded_pool) { cairo_freelist_pool_t *next = pool->next; free (pool); pool = next; } pool = freepool->freepools; while (pool != NULL) { cairo_freelist_pool_t *next = pool->next; free (pool); pool = next; } VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool))); } void * _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) { cairo_freelist_pool_t *pool; int poolsize; if (freepool->freepools != NULL) { pool = freepool->freepools; freepool->freepools = pool->next; poolsize = pool->size; } else { if (freepool->pools != &freepool->embedded_pool) poolsize = 2 * freepool->pools->size; else poolsize = (128 * freepool->nodesize + 8191) & -8192; pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize); if (unlikely (pool == NULL)) return pool; pool->size = poolsize; } pool->next = freepool->pools; freepool->pools = pool; pool->rem = poolsize - freepool->nodesize; pool->data = (uint8_t *) (pool + 1) + freepool->nodesize; VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, pool->rem)); return pool + 1; } cairo_status_t _cairo_freepool_alloc_array (cairo_freepool_t *freepool, int count, void **array) { int i; for (i = 0; i < count; i++) { cairo_freelist_node_t *node; node = freepool->first_free_node; if (likely (node != NULL)) { VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); freepool->first_free_node = node->next; VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); } else { node = _cairo_freepool_alloc_from_pool (freepool); if (unlikely (node == NULL)) goto CLEANUP; } array[i] = node; } return CAIRO_STATUS_SUCCESS; CLEANUP: while (i--) _cairo_freepool_free (freepool, array[i]); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ft-font.c���������������������������������0000664�0000000�0000000�00000311001�12710376503�0025110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2000 Keith Packard * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> * Keith Packard <keithp@keithp.com> * Carl Worth <cworth@cworth.org> */ #define _BSD_SOURCE /* for strdup() */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-ft-private.h" #include "cairo-pattern-private.h" #include "cairo-pixman-private.h" #include <float.h> #include "cairo-fontconfig-private.h" #include <ft2build.h> #include FT_FREETYPE_H #include FT_OUTLINE_H #include FT_IMAGE_H #include FT_BITMAP_H #include FT_TRUETYPE_TABLES_H #include FT_XFREE86_H #if HAVE_FT_GLYPHSLOT_EMBOLDEN #include FT_SYNTHESIS_H #endif #if HAVE_FT_LIBRARY_SETLCDFILTER #include FT_LCD_FILTER_H #endif #if HAVE_UNISTD_H #include <unistd.h> #else #define access(p, m) 0 #endif /* Fontconfig version older than 2.6 didn't have these options */ #ifndef FC_LCD_FILTER #define FC_LCD_FILTER "lcdfilter" #endif /* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */ #ifndef FC_LCD_NONE #define FC_LCD_NONE 0 #define FC_LCD_DEFAULT 1 #define FC_LCD_LIGHT 2 #define FC_LCD_LEGACY 3 #endif /* FreeType version older than 2.3.5(?) didn't have these options */ #ifndef FT_LCD_FILTER_NONE #define FT_LCD_FILTER_NONE 0 #define FT_LCD_FILTER_DEFAULT 1 #define FT_LCD_FILTER_LIGHT 2 #define FT_LCD_FILTER_LEGACY 16 #endif #define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) #define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) /* This is the max number of FT_face objects we keep open at once */ #define MAX_OPEN_FACES 10 /** * SECTION:cairo-ft * @Title: FreeType Fonts * @Short_Description: Font support for FreeType * @See_Also: #cairo_font_face_t * * The FreeType font backend is primarily used to render text on GNU/Linux * systems, but can be used on other platforms too. **/ /** * CAIRO_HAS_FT_FONT: * * Defined if the FreeType font backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.0 **/ /** * CAIRO_HAS_FC_FONT: * * Defined if the Fontconfig-specific functions of the FreeType font backend * are available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.10 **/ /* * The simple 2x2 matrix is converted into separate scale and shape * factors so that hinting works right */ typedef struct _cairo_ft_font_transform { double x_scale, y_scale; double shape[2][2]; } cairo_ft_font_transform_t; /* * We create an object that corresponds to a single font on the disk; * (identified by a filename/id pair) these are shared between all * fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we * just create a one-off version with a permanent face value. */ typedef struct _cairo_ft_font_face cairo_ft_font_face_t; struct _cairo_ft_unscaled_font { cairo_unscaled_font_t base; cairo_bool_t from_face; /* was the FT_Face provided by user? */ FT_Face face; /* provided or cached face */ /* only set if from_face is false */ char *filename; int id; /* We temporarily scale the unscaled font as needed */ cairo_bool_t have_scale; cairo_matrix_t current_scale; double x_scale; /* Extracted X scale factor */ double y_scale; /* Extracted Y scale factor */ cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/ cairo_matrix_t current_shape; FT_Matrix Current_Shape; cairo_mutex_t mutex; int lock_count; cairo_ft_font_face_t *faces; /* Linked list of faces for this font */ }; static int _cairo_ft_unscaled_font_keys_equal (const void *key_a, const void *key_b); static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); typedef struct _cairo_ft_options { cairo_font_options_t base; unsigned int load_flags; /* flags for FT_Load_Glyph */ unsigned int synth_flags; } cairo_ft_options_t; struct _cairo_ft_font_face { cairo_font_face_t base; cairo_ft_unscaled_font_t *unscaled; cairo_ft_options_t ft_options; cairo_ft_font_face_t *next; #if CAIRO_HAS_FC_FONT FcPattern *pattern; /* if pattern is set, the above fields will be NULL */ cairo_font_face_t *resolved_font_face; FcConfig *resolved_config; #endif }; static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; #if CAIRO_HAS_FC_FONT static cairo_status_t _cairo_ft_font_options_substitute (const cairo_font_options_t *options, FcPattern *pattern); static cairo_font_face_t * _cairo_ft_resolve_pattern (FcPattern *pattern, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options); #endif /* * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t. * The hash table itself isn't limited in size. However, we limit the * number of FT_Face objects we keep around; when we've exceeded that * limit and need to create a new FT_Face, we dump the FT_Face from a * random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if * there are any). */ typedef struct _cairo_ft_unscaled_font_map { cairo_hash_table_t *hash_table; FT_Library ft_library; int num_open_faces; } cairo_ft_unscaled_font_map_t; static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL; static FT_Face _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled); static void _cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); static cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); static void _font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map, cairo_ft_unscaled_font_t *unscaled) { if (unscaled->face) { FT_Done_Face (unscaled->face); unscaled->face = NULL; unscaled->have_scale = FALSE; font_map->num_open_faces--; } } static cairo_status_t _cairo_ft_unscaled_font_map_create (void) { cairo_ft_unscaled_font_map_t *font_map; /* This function is only intended to be called from * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can * detect some other call path. */ assert (cairo_ft_unscaled_font_map == NULL); font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t)); if (unlikely (font_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font_map->hash_table = _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal); if (unlikely (font_map->hash_table == NULL)) goto FAIL; if (unlikely (FT_Init_FreeType (&font_map->ft_library))) goto FAIL; font_map->num_open_faces = 0; cairo_ft_unscaled_font_map = font_map; return CAIRO_STATUS_SUCCESS; FAIL: if (font_map->hash_table) _cairo_hash_table_destroy (font_map->hash_table); free (font_map); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } static void _cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) { cairo_ft_unscaled_font_t *unscaled = entry; cairo_ft_unscaled_font_map_t *font_map = closure; _cairo_hash_table_remove (font_map->hash_table, &unscaled->base.hash_entry); if (! unscaled->from_face) _font_map_release_face_lock_held (font_map, unscaled); _cairo_ft_unscaled_font_fini (unscaled); free (unscaled); } static void _cairo_ft_unscaled_font_map_destroy (void) { cairo_ft_unscaled_font_map_t *font_map; CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); font_map = cairo_ft_unscaled_font_map; cairo_ft_unscaled_font_map = NULL; CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); if (font_map != NULL) { _cairo_hash_table_foreach (font_map->hash_table, _cairo_ft_unscaled_font_map_pluck_entry, font_map); assert (font_map->num_open_faces == 0); FT_Done_FreeType (font_map->ft_library); _cairo_hash_table_destroy (font_map->hash_table); free (font_map); } } static cairo_ft_unscaled_font_map_t * _cairo_ft_unscaled_font_map_lock (void) { CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); if (unlikely (cairo_ft_unscaled_font_map == NULL)) { if (unlikely (_cairo_ft_unscaled_font_map_create ())) { CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); return NULL; } } return cairo_ft_unscaled_font_map; } static void _cairo_ft_unscaled_font_map_unlock (void) { CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); } static void _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key, cairo_bool_t from_face, char *filename, int id, FT_Face face) { unsigned long hash; key->from_face = from_face; key->filename = filename; key->id = id; key->face = face; hash = _cairo_hash_string (filename); /* the constants are just arbitrary primes */ hash += ((unsigned long) id) * 1607; hash += ((unsigned long) face) * 2137; key->base.hash_entry.hash = hash; } /** * _cairo_ft_unscaled_font_init: * * Initialize a #cairo_ft_unscaled_font_t. * * There are two basic flavors of #cairo_ft_unscaled_font_t, one * created from an FT_Face and the other created from a filename/id * pair. These two flavors are identified as from_face and !from_face. * * To initialize a from_face font, pass filename==%NULL, id=0 and the * desired face. * * To initialize a !from_face font, pass the filename/id as desired * and face==%NULL. * * Note that the code handles these two flavors in very distinct * ways. For example there is a hash_table mapping * filename/id->#cairo_unscaled_font_t in the !from_face case, but no * parallel in the from_face case, (where the calling code would have * to do its own mapping to ensure similar sharing). **/ static cairo_status_t _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled, cairo_bool_t from_face, const char *filename, int id, FT_Face face) { _cairo_unscaled_font_init (&unscaled->base, &cairo_ft_unscaled_font_backend); if (from_face) { unscaled->from_face = TRUE; _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face); } else { char *filename_copy; unscaled->from_face = FALSE; unscaled->face = NULL; filename_copy = strdup (filename); if (unlikely (filename_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL); } unscaled->have_scale = FALSE; CAIRO_MUTEX_INIT (unscaled->mutex); unscaled->lock_count = 0; unscaled->faces = NULL; return CAIRO_STATUS_SUCCESS; } /** * _cairo_ft_unscaled_font_fini: * * Free all data associated with a #cairo_ft_unscaled_font_t. * * CAUTION: The unscaled->face field must be %NULL before calling this * function. This is because the #cairo_ft_unscaled_font_t_map keeps a * count of these faces (font_map->num_open_faces) so it maintains the * unscaled->face field while it has its lock held. See * _font_map_release_face_lock_held(). **/ static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) { assert (unscaled->face == NULL); free (unscaled->filename); unscaled->filename = NULL; CAIRO_MUTEX_FINI (unscaled->mutex); } static int _cairo_ft_unscaled_font_keys_equal (const void *key_a, const void *key_b) { const cairo_ft_unscaled_font_t *unscaled_a = key_a; const cairo_ft_unscaled_font_t *unscaled_b = key_b; if (unscaled_a->id == unscaled_b->id && unscaled_a->from_face == unscaled_b->from_face) { if (unscaled_a->from_face) return unscaled_a->face == unscaled_b->face; if (unscaled_a->filename == NULL && unscaled_b->filename == NULL) return TRUE; else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL) return FALSE; else return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0); } return FALSE; } /* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from * pattern. Returns a new reference to the unscaled font. */ static cairo_status_t _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, char *filename, int id, FT_Face font_face, cairo_ft_unscaled_font_t **out) { cairo_ft_unscaled_font_t key, *unscaled; cairo_ft_unscaled_font_map_t *font_map; cairo_status_t status; font_map = _cairo_ft_unscaled_font_map_lock (); if (unlikely (font_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); /* Return existing unscaled font if it exists in the hash table. */ unscaled = _cairo_hash_table_lookup (font_map->hash_table, &key.base.hash_entry); if (unscaled != NULL) { _cairo_unscaled_font_reference (&unscaled->base); goto DONE; } /* Otherwise create it and insert into hash table. */ unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); if (unlikely (unscaled == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_FONT_MAP_LOCK; } status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); if (unlikely (status)) goto UNWIND_UNSCALED_MALLOC; assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash); status = _cairo_hash_table_insert (font_map->hash_table, &unscaled->base.hash_entry); if (unlikely (status)) goto UNWIND_UNSCALED_FONT_INIT; DONE: _cairo_ft_unscaled_font_map_unlock (); *out = unscaled; return CAIRO_STATUS_SUCCESS; UNWIND_UNSCALED_FONT_INIT: _cairo_ft_unscaled_font_fini (unscaled); UNWIND_UNSCALED_MALLOC: free (unscaled); UNWIND_FONT_MAP_LOCK: _cairo_ft_unscaled_font_map_unlock (); return status; } #if CAIRO_HAS_FC_FONT static cairo_status_t _cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, cairo_ft_unscaled_font_t **out) { FT_Face font_face = NULL; char *filename = NULL; int id = 0; FcResult ret; ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); if (ret == FcResultMatch) goto DONE; if (ret == FcResultOutOfMemory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename); if (ret == FcResultOutOfMemory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (ret == FcResultMatch) { if (access (filename, R_OK) == 0) { /* If FC_INDEX is not set, we just use 0 */ ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); if (ret == FcResultOutOfMemory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } else return _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); } /* The pattern contains neither a face nor a filename, resolve it later. */ *out = NULL; return CAIRO_STATUS_SUCCESS; DONE: return _cairo_ft_unscaled_font_create_internal (font_face != NULL, filename, id, font_face, out); } #endif static cairo_status_t _cairo_ft_unscaled_font_create_from_face (FT_Face face, cairo_ft_unscaled_font_t **out) { return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out); } static void _cairo_ft_unscaled_font_destroy (void *abstract_font) { cairo_ft_unscaled_font_t *unscaled = abstract_font; cairo_ft_unscaled_font_map_t *font_map; if (unscaled == NULL) return; font_map = _cairo_ft_unscaled_font_map_lock (); /* All created objects must have been mapped in the font map. */ assert (font_map != NULL); if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) { /* somebody recreated the font whilst we waited for the lock */ _cairo_ft_unscaled_font_map_unlock (); return; } _cairo_hash_table_remove (font_map->hash_table, &unscaled->base.hash_entry); if (unscaled->from_face) { /* See comments in _ft_font_face_destroy about the "zombie" state * for a _ft_font_face. */ if (unscaled->faces && unscaled->faces->unscaled == NULL) { assert (unscaled->faces->next == NULL); cairo_font_face_destroy (&unscaled->faces->base); } } else { _font_map_release_face_lock_held (font_map, unscaled); } unscaled->face = NULL; _cairo_ft_unscaled_font_map_unlock (); _cairo_ft_unscaled_font_fini (unscaled); } static cairo_bool_t _has_unlocked_face (const void *entry) { const cairo_ft_unscaled_font_t *unscaled = entry; return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face); } /* Ensures that an unscaled font has a face object. If we exceed * MAX_OPEN_FACES, try to close some. * * This differs from _cairo_ft_scaled_font_lock_face in that it doesn't * set the scale on the face, but just returns it at the last scale. */ static cairo_warn FT_Face _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) { cairo_ft_unscaled_font_map_t *font_map; FT_Face face = NULL; CAIRO_MUTEX_LOCK (unscaled->mutex); unscaled->lock_count++; if (unscaled->face) return unscaled->face; /* If this unscaled font was created from an FT_Face then we just * returned it above. */ assert (!unscaled->from_face); font_map = _cairo_ft_unscaled_font_map_lock (); { assert (font_map != NULL); while (font_map->num_open_faces >= MAX_OPEN_FACES) { cairo_ft_unscaled_font_t *entry; entry = _cairo_hash_table_random_entry (font_map->hash_table, _has_unlocked_face); if (entry == NULL) break; _font_map_release_face_lock_held (font_map, entry); } } _cairo_ft_unscaled_font_map_unlock (); if (FT_New_Face (font_map->ft_library, unscaled->filename, unscaled->id, &face) != FT_Err_Ok) { unscaled->lock_count--; CAIRO_MUTEX_UNLOCK (unscaled->mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } unscaled->face = face; font_map->num_open_faces++; return face; } /* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face */ static void _cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled) { assert (unscaled->lock_count > 0); unscaled->lock_count--; CAIRO_MUTEX_UNLOCK (unscaled->mutex); } static cairo_status_t _compute_transform (cairo_ft_font_transform_t *sf, cairo_matrix_t *scale, cairo_ft_unscaled_font_t *unscaled) { cairo_status_t status; double x_scale, y_scale; cairo_matrix_t normalized = *scale; /* The font matrix has x and y "scale" components which we extract and * use as character scale values. These influence the way freetype * chooses hints, as well as selecting different bitmaps in * hand-rendered fonts. We also copy the normalized matrix to * freetype's transformation. */ status = _cairo_matrix_compute_basis_scale_factors (scale, &x_scale, &y_scale, 1); if (unlikely (status)) return status; /* FreeType docs say this about x_scale and y_scale: * "A character width or height smaller than 1pt is set to 1pt;" * So, we cap them from below at 1.0 and let the FT transform * take care of sub-1.0 scaling. */ if (x_scale < 1.0) x_scale = 1.0; if (y_scale < 1.0) y_scale = 1.0; if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) { double min_distance = DBL_MAX; cairo_bool_t magnify = TRUE; int i; int best_i = 0; double best_x_size = 0; double best_y_size = 0; for (i = 0; i < unscaled->face->num_fixed_sizes; i++) { double x_size = unscaled->face->available_sizes[i].y_ppem / 64.; double y_size = unscaled->face->available_sizes[i].y_ppem / 64.; double distance = y_size - y_scale; /* * distance is positive if current strike is larger than desired * size, and negative if smaller. * * We like to prefer down-scaling to upscaling. */ if ((magnify && distance >= 0) || fabs (distance) <= min_distance) { magnify = distance < 0; min_distance = fabs (distance); best_i = i; best_x_size = x_size; best_y_size = y_size; } } x_scale = best_x_size; y_scale = best_y_size; } sf->x_scale = x_scale; sf->y_scale = y_scale; cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); _cairo_matrix_get_affine (&normalized, &sf->shape[0][0], &sf->shape[0][1], &sf->shape[1][0], &sf->shape[1][1], NULL, NULL); return CAIRO_STATUS_SUCCESS; } /* Temporarily scales an unscaled font to the give scale. We catch * scaling to the same size, since changing a FT_Face is expensive. */ static cairo_status_t _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, cairo_matrix_t *scale) { cairo_status_t status; cairo_ft_font_transform_t sf; FT_Matrix mat; FT_Error error; assert (unscaled->face != NULL); if (unscaled->have_scale && scale->xx == unscaled->current_scale.xx && scale->yx == unscaled->current_scale.yx && scale->xy == unscaled->current_scale.xy && scale->yy == unscaled->current_scale.yy) return CAIRO_STATUS_SUCCESS; unscaled->have_scale = TRUE; unscaled->current_scale = *scale; status = _compute_transform (&sf, scale, unscaled); if (unlikely (status)) return status; unscaled->x_scale = sf.x_scale; unscaled->y_scale = sf.y_scale; mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); unscaled->have_shape = (mat.xx != 0x10000 || mat.yx != 0x00000 || mat.xy != 0x00000 || mat.yy != 0x10000); unscaled->Current_Shape = mat; cairo_matrix_init (&unscaled->current_shape, sf.shape[0][0], sf.shape[0][1], sf.shape[1][0], sf.shape[1][1], 0.0, 0.0); FT_Set_Transform(unscaled->face, &mat, NULL); error = FT_Set_Char_Size (unscaled->face, sf.x_scale * 64.0 + .5, sf.y_scale * 64.0 + .5, 0, 0); if (error) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } /* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot * into a different format. For example, we want to convert a * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit * ARGB or ABGR bitmap. * * this function prepares a target descriptor for this operation. * * input :: target bitmap descriptor. The function will set its * 'width', 'rows' and 'pitch' fields, and only these * * slot :: the glyph slot containing the source bitmap. this * function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP * * mode :: the requested final rendering mode. supported values are * MONO, NORMAL (i.e. gray), LCD and LCD_V * * the function returns the size in bytes of the corresponding buffer, * it's up to the caller to allocate the corresponding memory block * before calling _fill_xrender_bitmap * * it also returns -1 in case of error (e.g. incompatible arguments, * like trying to convert a gray bitmap into a monochrome one) */ static int _compute_xrender_bitmap_size(FT_Bitmap *target, FT_GlyphSlot slot, FT_Render_Mode mode) { FT_Bitmap *ftbit; int width, height, pitch; if (slot->format != FT_GLYPH_FORMAT_BITMAP) return -1; /* compute the size of the final bitmap */ ftbit = &slot->bitmap; width = ftbit->width; height = ftbit->rows; pitch = (width + 3) & ~3; switch (ftbit->pixel_mode) { case FT_PIXEL_MODE_MONO: if (mode == FT_RENDER_MODE_MONO) { pitch = (((width + 31) & ~31) >> 3); break; } /* fall-through */ case FT_PIXEL_MODE_GRAY: if (mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V) { /* each pixel is replicated into a 32-bit ARGB value */ pitch = width * 4; } break; case FT_PIXEL_MODE_LCD: if (mode != FT_RENDER_MODE_LCD) return -1; /* horz pixel triplets are packed into 32-bit ARGB values */ width /= 3; pitch = width * 4; break; case FT_PIXEL_MODE_LCD_V: if (mode != FT_RENDER_MODE_LCD_V) return -1; /* vert pixel triplets are packed into 32-bit ARGB values */ height /= 3; pitch = width * 4; break; default: /* unsupported source format */ return -1; } target->width = width; target->rows = height; target->pitch = pitch; target->buffer = NULL; return pitch * height; } /* this functions converts the glyph bitmap found in a FT_GlyphSlot * into a different format (see _compute_xrender_bitmap_size) * * you should call this function after _compute_xrender_bitmap_size * * target :: target bitmap descriptor. Note that its 'buffer' pointer * must point to memory allocated by the caller * * slot :: the glyph slot containing the source bitmap * * mode :: the requested final rendering mode * * bgr :: boolean, set if BGR or VBGR pixel ordering is needed */ static void _fill_xrender_bitmap(FT_Bitmap *target, FT_GlyphSlot slot, FT_Render_Mode mode, int bgr) { FT_Bitmap *ftbit = &slot->bitmap; unsigned char *srcLine = ftbit->buffer; unsigned char *dstLine = target->buffer; int src_pitch = ftbit->pitch; int width = target->width; int height = target->rows; int pitch = target->pitch; int subpixel; int h; subpixel = (mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V); if (src_pitch < 0) srcLine -= src_pitch * (ftbit->rows - 1); target->pixel_mode = ftbit->pixel_mode; switch (ftbit->pixel_mode) { case FT_PIXEL_MODE_MONO: if (subpixel) { /* convert mono to ARGB32 values */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { int x; for (x = 0; x < width; x++) { if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) ((unsigned int *) dstLine)[x] = 0xffffffffU; } } target->pixel_mode = FT_PIXEL_MODE_LCD; } else if (mode == FT_RENDER_MODE_NORMAL) { /* convert mono to 8-bit gray */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { int x; for (x = 0; x < width; x++) { if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) dstLine[x] = 0xff; } } target->pixel_mode = FT_PIXEL_MODE_GRAY; } else { /* copy mono to mono */ int bytes = (width + 7) >> 3; for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) memcpy (dstLine, srcLine, bytes); } break; case FT_PIXEL_MODE_GRAY: if (subpixel) { /* convert gray to ARGB32 values */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { int x; unsigned int *dst = (unsigned int *) dstLine; for (x = 0; x < width; x++) { unsigned int pix = srcLine[x]; pix |= (pix << 8); pix |= (pix << 16); dst[x] = pix; } } target->pixel_mode = FT_PIXEL_MODE_LCD; } else { /* copy gray into gray */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) memcpy (dstLine, srcLine, width); } break; case FT_PIXEL_MODE_LCD: if (!bgr) { /* convert horizontal RGB into ARGB32 */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { int x; unsigned char *src = srcLine; unsigned int *dst = (unsigned int *) dstLine; for (x = 0; x < width; x++, src += 3) { unsigned int pix; pix = ((unsigned int)src[0] << 16) | ((unsigned int)src[1] << 8) | ((unsigned int)src[2] ) | ((unsigned int)src[1] << 24) ; dst[x] = pix; } } } else { /* convert horizontal BGR into ARGB32 */ for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { int x; unsigned char *src = srcLine; unsigned int *dst = (unsigned int *) dstLine; for (x = 0; x < width; x++, src += 3) { unsigned int pix; pix = ((unsigned int)src[2] << 16) | ((unsigned int)src[1] << 8) | ((unsigned int)src[0] ) | ((unsigned int)src[1] << 24) ; dst[x] = pix; } } } break; default: /* FT_PIXEL_MODE_LCD_V */ /* convert vertical RGB into ARGB32 */ if (!bgr) { for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) { int x; unsigned char* src = srcLine; unsigned int* dst = (unsigned int *) dstLine; for (x = 0; x < width; x++, src += 1) { unsigned int pix; pix = ((unsigned int)src[0] << 16) | ((unsigned int)src[src_pitch] << 8) | ((unsigned int)src[src_pitch*2] ) | ((unsigned int)src[src_pitch] << 24) ; dst[x] = pix; } } } else { for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) { int x; unsigned char *src = srcLine; unsigned int *dst = (unsigned int *) dstLine; for (x = 0; x < width; x++, src += 1) { unsigned int pix; pix = ((unsigned int)src[src_pitch * 2] << 16) | ((unsigned int)src[src_pitch] << 8) | ((unsigned int)src[0] ) | ((unsigned int)src[src_pitch] << 24) ; dst[x] = pix; } } } } } /* Fills in val->image with an image surface created from @bitmap */ static cairo_status_t _get_bitmap_surface (FT_Bitmap *bitmap, FT_Library library, cairo_bool_t own_buffer, cairo_font_options_t *font_options, cairo_image_surface_t **surface) { unsigned int width, height; unsigned char *data; int format = CAIRO_FORMAT_A8; int stride; cairo_image_surface_t *image; cairo_bool_t component_alpha = FALSE; width = bitmap->width; height = bitmap->rows; if (width == 0 || height == 0) { *surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); return (*surface)->base.status; } switch (bitmap->pixel_mode) { case FT_PIXEL_MODE_MONO: stride = (((width + 31) & ~31) >> 3); if (own_buffer) { data = bitmap->buffer; assert (stride == bitmap->pitch); } else { data = _cairo_malloc_ab (height, stride); if (!data) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (stride == bitmap->pitch) { memcpy (data, bitmap->buffer, stride * height); } else { int i; unsigned char *source, *dest; source = bitmap->buffer; dest = data; for (i = height; i; i--) { memcpy (dest, source, stride); source += bitmap->pitch; dest += stride; } } } #ifndef WORDS_BIGENDIAN { uint8_t *d = data; int count = stride * height; while (count--) { *d = CAIRO_BITSWAP8 (*d); d++; } } #endif format = CAIRO_FORMAT_A1; break; case FT_PIXEL_MODE_LCD: case FT_PIXEL_MODE_LCD_V: case FT_PIXEL_MODE_GRAY: if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL || bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) { stride = bitmap->pitch; /* We don't support stride not multiple of 4. */ if (stride & 3) { assert (!own_buffer); goto convert; } if (own_buffer) { data = bitmap->buffer; } else { data = _cairo_malloc_ab (height, stride); if (!data) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (data, bitmap->buffer, stride * height); } format = CAIRO_FORMAT_A8; } else { data = bitmap->buffer; stride = bitmap->pitch; format = CAIRO_FORMAT_ARGB32; component_alpha = TRUE; } break; #ifdef FT_LOAD_COLOR case FT_PIXEL_MODE_BGRA: stride = width * 4; if (own_buffer) { data = bitmap->buffer; } else { data = _cairo_malloc_ab (height, stride); if (!data) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (data, bitmap->buffer, stride * height); } format = CAIRO_FORMAT_ARGB32; break; #endif case FT_PIXEL_MODE_GRAY2: case FT_PIXEL_MODE_GRAY4: convert: if (!own_buffer && library) { /* This is pretty much the only case that we can get in here. */ /* Convert to 8bit grayscale. */ FT_Bitmap tmp; FT_Int align; format = CAIRO_FORMAT_A8; align = cairo_format_stride_for_width (format, bitmap->width); FT_Bitmap_New( &tmp ); if (FT_Bitmap_Convert( library, bitmap, &tmp, align )) return _cairo_error (CAIRO_STATUS_NO_MEMORY); FT_Bitmap_Done( library, bitmap ); *bitmap = tmp; stride = bitmap->pitch; data = _cairo_malloc_ab (height, stride); if (!data) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (bitmap->num_grays != 256) { unsigned int x, y; unsigned int mul = 255 / (bitmap->num_grays - 1); FT_Byte *p = bitmap->buffer; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) p[x] *= mul; p += bitmap->pitch; } } memcpy (data, bitmap->buffer, stride * height); break; } /* These could be triggered by very rare types of TrueType fonts */ default: if (own_buffer) free (bitmap->buffer); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* XXX */ *surface = image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (data, format, width, height, stride); if (image->base.status) { free (data); return (*surface)->base.status; } if (component_alpha) pixman_image_set_component_alpha (image->pixman_image, TRUE); _cairo_image_surface_assume_ownership_of_data (image); _cairo_debug_check_image_surface_is_defined (&image->base); return CAIRO_STATUS_SUCCESS; } /* Converts an outline FT_GlyphSlot into an image * * This could go through _render_glyph_bitmap as well, letting * FreeType convert the outline to a bitmap, but doing it ourselves * has two minor advantages: first, we save a copy of the bitmap * buffer: we can directly use the buffer that FreeType renders * into. * * Second, it may help when we add support for subpixel * rendering: the Xft code does it this way. (Keith thinks that * it may also be possible to get the subpixel rendering with * FT_Render_Glyph: something worth looking into in more detail * when we add subpixel support. If so, we may want to eliminate * this version of the code path entirely. */ static cairo_status_t _render_glyph_outline (FT_Face face, cairo_font_options_t *font_options, cairo_image_surface_t **surface) { int rgba = FC_RGBA_UNKNOWN; int lcd_filter = FT_LCD_FILTER_LEGACY; FT_GlyphSlot glyphslot = face->glyph; FT_Outline *outline = &glyphslot->outline; FT_Bitmap bitmap; FT_BBox cbox; unsigned int width, height; cairo_status_t status; FT_Error fterror; FT_Library library = glyphslot->library; FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; switch (font_options->antialias) { case CAIRO_ANTIALIAS_NONE: render_mode = FT_RENDER_MODE_MONO; break; case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_BEST: switch (font_options->subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: case CAIRO_SUBPIXEL_ORDER_BGR: render_mode = FT_RENDER_MODE_LCD; break; case CAIRO_SUBPIXEL_ORDER_VRGB: case CAIRO_SUBPIXEL_ORDER_VBGR: render_mode = FT_RENDER_MODE_LCD_V; break; } switch (font_options->lcd_filter) { case CAIRO_LCD_FILTER_NONE: lcd_filter = FT_LCD_FILTER_NONE; break; case CAIRO_LCD_FILTER_DEFAULT: case CAIRO_LCD_FILTER_INTRA_PIXEL: lcd_filter = FT_LCD_FILTER_LEGACY; break; case CAIRO_LCD_FILTER_FIR3: lcd_filter = FT_LCD_FILTER_LIGHT; break; case CAIRO_LCD_FILTER_FIR5: lcd_filter = FT_LCD_FILTER_DEFAULT; break; } break; case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_FAST: render_mode = FT_RENDER_MODE_NORMAL; } FT_Outline_Get_CBox (outline, &cbox); cbox.xMin &= -64; cbox.yMin &= -64; cbox.xMax = (cbox.xMax + 63) & -64; cbox.yMax = (cbox.yMax + 63) & -64; width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); if (width * height == 0) { cairo_format_t format; /* Looks like fb handles zero-sized images just fine */ switch (render_mode) { case FT_RENDER_MODE_MONO: format = CAIRO_FORMAT_A1; break; case FT_RENDER_MODE_LCD: case FT_RENDER_MODE_LCD_V: format= CAIRO_FORMAT_ARGB32; break; case FT_RENDER_MODE_LIGHT: case FT_RENDER_MODE_NORMAL: case FT_RENDER_MODE_MAX: default: format = CAIRO_FORMAT_A8; break; } (*surface) = (cairo_image_surface_t *) cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); if ((*surface)->base.status) return (*surface)->base.status; } else { int bitmap_size; switch (render_mode) { case FT_RENDER_MODE_LCD: if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) rgba = FC_RGBA_BGR; else rgba = FC_RGBA_RGB; break; case FT_RENDER_MODE_LCD_V: if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) rgba = FC_RGBA_VBGR; else rgba = FC_RGBA_VRGB; break; case FT_RENDER_MODE_MONO: case FT_RENDER_MODE_LIGHT: case FT_RENDER_MODE_NORMAL: case FT_RENDER_MODE_MAX: default: break; } #if HAVE_FT_LIBRARY_SETLCDFILTER FT_Library_SetLcdFilter (library, lcd_filter); #endif fterror = FT_Render_Glyph (face->glyph, render_mode); #if HAVE_FT_LIBRARY_SETLCDFILTER FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); #endif if (fterror != 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); bitmap_size = _compute_xrender_bitmap_size (&bitmap, face->glyph, render_mode); if (bitmap_size < 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); bitmap.buffer = calloc (1, bitmap_size); if (bitmap.buffer == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); /* Note: * _get_bitmap_surface will free bitmap.buffer if there is an error */ status = _get_bitmap_surface (&bitmap, NULL, TRUE, font_options, surface); if (unlikely (status)) return status; /* Note: the font's coordinate system is upside down from ours, so the * Y coordinate of the control box needs to be negated. Moreover, device * offsets are position of glyph origin relative to top left while xMin * and yMax are offsets of top left relative to origin. Another negation. */ cairo_surface_set_device_offset (&(*surface)->base, (double)-glyphslot->bitmap_left, (double)+glyphslot->bitmap_top); } return CAIRO_STATUS_SUCCESS; } /* Converts a bitmap (or other) FT_GlyphSlot into an image */ static cairo_status_t _render_glyph_bitmap (FT_Face face, cairo_font_options_t *font_options, cairo_image_surface_t **surface) { FT_GlyphSlot glyphslot = face->glyph; cairo_status_t status; FT_Error error; /* According to the FreeType docs, glyphslot->format could be * something other than FT_GLYPH_FORMAT_OUTLINE or * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType * the opportunity to convert such to * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since * we avoid the FT_LOAD_NO_RECURSE flag. */ error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); /* XXX ignoring all other errors for now. They are not fatal, typically * just a glyph-not-found. */ if (error == FT_Err_Out_Of_Memory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _get_bitmap_surface (&glyphslot->bitmap, glyphslot->library, FALSE, font_options, surface); if (unlikely (status)) return status; /* * Note: the font's coordinate system is upside down from ours, so the * Y coordinate of the control box needs to be negated. Moreover, device * offsets are position of glyph origin relative to top left while * bitmap_left and bitmap_top are offsets of top left relative to origin. * Another negation. */ cairo_surface_set_device_offset (&(*surface)->base, -glyphslot->bitmap_left, +glyphslot->bitmap_top); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _transform_glyph_bitmap (cairo_matrix_t * shape, cairo_image_surface_t ** surface) { cairo_matrix_t original_to_transformed; cairo_matrix_t transformed_to_original; cairo_image_surface_t *old_image; cairo_surface_t *image; double x[4], y[4]; double origin_x, origin_y; int orig_width, orig_height; int i; int x_min, y_min, x_max, y_max; int width, height; cairo_status_t status; cairo_surface_pattern_t pattern; /* We want to compute a transform that takes the origin * (device_x_offset, device_y_offset) to 0,0, then applies * the "shape" portion of the font transform */ original_to_transformed = *shape; cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y); orig_width = (*surface)->width; orig_height = (*surface)->height; cairo_matrix_translate (&original_to_transformed, -origin_x, -origin_y); /* Find the bounding box of the original bitmap under that * transform */ x[0] = 0; y[0] = 0; x[1] = orig_width; y[1] = 0; x[2] = orig_width; y[2] = orig_height; x[3] = 0; y[3] = orig_height; for (i = 0; i < 4; i++) cairo_matrix_transform_point (&original_to_transformed, &x[i], &y[i]); x_min = floor (x[0]); y_min = floor (y[0]); x_max = ceil (x[0]); y_max = ceil (y[0]); for (i = 1; i < 4; i++) { if (x[i] < x_min) x_min = floor (x[i]); else if (x[i] > x_max) x_max = ceil (x[i]); if (y[i] < y_min) y_min = floor (y[i]); else if (y[i] > y_max) y_max = ceil (y[i]); } /* Adjust the transform so that the bounding box starts at 0,0 ... * this gives our final transform from original bitmap to transformed * bitmap. */ original_to_transformed.x0 -= x_min; original_to_transformed.y0 -= y_min; /* Create the transformed bitmap */ width = x_max - x_min; height = y_max - y_min; transformed_to_original = original_to_transformed; status = cairo_matrix_invert (&transformed_to_original); if (unlikely (status)) return status; if ((*surface)->format == CAIRO_FORMAT_ARGB32 && !pixman_image_get_component_alpha ((*surface)->pixman_image)) image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); else image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); if (unlikely (image->status)) return image->status; /* Draw the original bitmap transformed into the new bitmap */ _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); status = _cairo_surface_paint (image, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (image); return status; } /* Now update the cache entry for the new bitmap, recomputing * the origin based on the final transform. */ cairo_matrix_transform_point (&original_to_transformed, &origin_x, &origin_y); old_image = (*surface); (*surface) = (cairo_image_surface_t *)image; cairo_surface_destroy (&old_image->base); cairo_surface_set_device_offset (&(*surface)->base, _cairo_lround (origin_x), _cairo_lround (origin_y)); return CAIRO_STATUS_SUCCESS; } static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { _cairo_ft_unscaled_font_destroy, #if 0 _cairo_ft_unscaled_font_create_glyph #endif }; /* #cairo_ft_scaled_font_t */ typedef struct _cairo_ft_scaled_font { cairo_scaled_font_t base; cairo_ft_unscaled_font_t *unscaled; cairo_ft_options_t ft_options; } cairo_ft_scaled_font_t; static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; #if CAIRO_HAS_FC_FONT /* The load flags passed to FT_Load_Glyph control aspects like hinting and * antialiasing. Here we compute them from the fields of a FcPattern. */ static void _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) { FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden; cairo_ft_options_t ft_options; int rgba; #ifdef FC_HINT_STYLE int hintstyle; #endif _cairo_font_options_init_default (&ft_options.base); ft_options.load_flags = FT_LOAD_DEFAULT; ft_options.synth_flags = 0; #ifndef FC_EMBEDDED_BITMAP #define FC_EMBEDDED_BITMAP "embeddedbitmap" #endif /* Check whether to force use of embedded bitmaps */ if (FcPatternGetBool (pattern, FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch) bitmap = FcFalse; /* disable antialiasing if requested */ if (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch) antialias = FcTrue; if (antialias) { cairo_subpixel_order_t subpixel_order; int lcd_filter; /* disable hinting if requested */ if (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting) != FcResultMatch) hinting = FcTrue; if (FcPatternGetInteger (pattern, FC_RGBA, 0, &rgba) != FcResultMatch) rgba = FC_RGBA_UNKNOWN; switch (rgba) { case FC_RGBA_RGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; break; case FC_RGBA_BGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; break; case FC_RGBA_VRGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; break; case FC_RGBA_VBGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; break; case FC_RGBA_UNKNOWN: case FC_RGBA_NONE: default: subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; break; } if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) { ft_options.base.subpixel_order = subpixel_order; ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; } if (FcPatternGetInteger (pattern, FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch) { switch (lcd_filter) { case FC_LCD_NONE: ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE; break; case FC_LCD_DEFAULT: ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5; break; case FC_LCD_LIGHT: ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3; break; case FC_LCD_LEGACY: ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; break; } } #ifdef FC_HINT_STYLE if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) hintstyle = FC_HINT_FULL; if (!hinting) hintstyle = FC_HINT_NONE; switch (hintstyle) { case FC_HINT_NONE: ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; break; case FC_HINT_SLIGHT: ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT; break; case FC_HINT_MEDIUM: default: ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM; break; case FC_HINT_FULL: ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL; break; } #else /* !FC_HINT_STYLE */ if (!hinting) { ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; } #endif /* FC_HINT_STYLE */ /* Force embedded bitmaps off if no hinting requested */ if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE) bitmap = FcFalse; if (!bitmap) ft_options.load_flags |= FT_LOAD_NO_BITMAP; } else { ft_options.base.antialias = CAIRO_ANTIALIAS_NONE; } /* force autohinting if requested */ if (FcPatternGetBool (pattern, FC_AUTOHINT, 0, &autohint) != FcResultMatch) autohint = FcFalse; if (autohint) ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT; if (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch) vertical_layout = FcFalse; if (vertical_layout) ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; #ifndef FC_EMBOLDEN #define FC_EMBOLDEN "embolden" #endif if (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &embolden) != FcResultMatch) embolden = FcFalse; if (embolden) ft_options.synth_flags |= CAIRO_FT_SYNTHESIZE_BOLD; *ret = ft_options; } #endif static void _cairo_ft_options_merge (cairo_ft_options_t *options, cairo_ft_options_t *other) { int load_flags = other->load_flags; int load_target = FT_LOAD_TARGET_NORMAL; /* clear load target mode */ load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); if (load_flags & FT_LOAD_NO_HINTING) other->base.hint_style = CAIRO_HINT_STYLE_NONE; if (other->base.antialias == CAIRO_ANTIALIAS_NONE || options->base.antialias == CAIRO_ANTIALIAS_NONE) { options->base.antialias = CAIRO_ANTIALIAS_NONE; options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; } if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL && (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT || options->base.antialias == CAIRO_ANTIALIAS_GRAY)) { options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; options->base.subpixel_order = other->base.subpixel_order; } if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT) options->base.hint_style = other->base.hint_style; if (other->base.hint_style == CAIRO_HINT_STYLE_NONE) options->base.hint_style = CAIRO_HINT_STYLE_NONE; if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT) options->base.lcd_filter = other->base.lcd_filter; if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE) options->base.lcd_filter = CAIRO_LCD_FILTER_NONE; if (options->base.antialias == CAIRO_ANTIALIAS_NONE) { if (options->base.hint_style == CAIRO_HINT_STYLE_NONE) load_flags |= FT_LOAD_NO_HINTING; else load_target = FT_LOAD_TARGET_MONO; load_flags |= FT_LOAD_MONOCHROME; } else { switch (options->base.hint_style) { case CAIRO_HINT_STYLE_NONE: load_flags |= FT_LOAD_NO_HINTING; break; case CAIRO_HINT_STYLE_SLIGHT: load_target = FT_LOAD_TARGET_LIGHT; break; case CAIRO_HINT_STYLE_MEDIUM: break; case CAIRO_HINT_STYLE_FULL: case CAIRO_HINT_STYLE_DEFAULT: if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { switch (options->base.subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: case CAIRO_SUBPIXEL_ORDER_BGR: load_target = FT_LOAD_TARGET_LCD; break; case CAIRO_SUBPIXEL_ORDER_VRGB: case CAIRO_SUBPIXEL_ORDER_VBGR: load_target = FT_LOAD_TARGET_LCD_V; break; } } break; } } options->load_flags = load_flags | load_target; options->synth_flags = other->synth_flags; } static cairo_status_t _cairo_ft_font_face_scaled_font_create (void *abstract_font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font_out) { cairo_ft_font_face_t *font_face = abstract_font_face; cairo_ft_scaled_font_t *scaled_font; FT_Face face; FT_Size_Metrics *metrics; cairo_font_extents_t fs_metrics; cairo_status_t status; cairo_ft_unscaled_font_t *unscaled; assert (font_face->unscaled); face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled); if (unlikely (face == NULL)) /* backend error */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); scaled_font = malloc (sizeof (cairo_ft_scaled_font_t)); if (unlikely (scaled_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } scaled_font->unscaled = unscaled = font_face->unscaled; _cairo_unscaled_font_reference (&unscaled->base); _cairo_font_options_init_copy (&scaled_font->ft_options.base, options); _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options); status = _cairo_scaled_font_init (&scaled_font->base, &font_face->base, font_matrix, ctm, options, &_cairo_ft_scaled_font_backend); if (unlikely (status)) goto CLEANUP_SCALED_FONT; status = _cairo_ft_unscaled_font_set_scale (unscaled, &scaled_font->base.scale); if (unlikely (status)) { /* This can only fail if we encounter an error with the underlying * font, so propagate the error back to the font-face. */ _cairo_ft_unscaled_font_unlock_face (unscaled); _cairo_unscaled_font_destroy (&unscaled->base); free (scaled_font); return status; } metrics = &face->size->metrics; /* * Get to unscaled metrics so that the upper level can get back to * user space * * Also use this path for bitmap-only fonts. The other branch uses * face members that are only relevant for scalable fonts. This is * detected by simply checking for units_per_EM==0. */ if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF || face->units_per_EM == 0) { double x_factor, y_factor; if (unscaled->x_scale == 0) x_factor = 0; else x_factor = 1 / unscaled->x_scale; if (unscaled->y_scale == 0) y_factor = 0; else y_factor = 1 / unscaled->y_scale; fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; fs_metrics.max_y_advance = 0; } else { fs_metrics.max_x_advance = 0; fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor; } } else { double scale = face->units_per_EM; fs_metrics.ascent = face->ascender / scale; fs_metrics.descent = - face->descender / scale; fs_metrics.height = face->height / scale; if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { fs_metrics.max_x_advance = face->max_advance_width / scale; fs_metrics.max_y_advance = 0; } else { fs_metrics.max_x_advance = 0; fs_metrics.max_y_advance = face->max_advance_height / scale; } } status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); if (unlikely (status)) goto CLEANUP_SCALED_FONT; _cairo_ft_unscaled_font_unlock_face (unscaled); *font_out = &scaled_font->base; return CAIRO_STATUS_SUCCESS; CLEANUP_SCALED_FONT: _cairo_unscaled_font_destroy (&unscaled->base); free (scaled_font); FAIL: _cairo_ft_unscaled_font_unlock_face (font_face->unscaled); *font_out = _cairo_scaled_font_create_in_error (status); return CAIRO_STATUS_SUCCESS; /* non-backend error */ } cairo_bool_t _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) { return scaled_font->backend == &_cairo_ft_scaled_font_backend; } static void _cairo_ft_scaled_font_fini (void *abstract_font) { cairo_ft_scaled_font_t *scaled_font = abstract_font; if (scaled_font == NULL) return; _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); } static int _move_to (FT_Vector *to, void *closure) { cairo_path_fixed_t *path = closure; cairo_fixed_t x, y; x = _cairo_fixed_from_26_6 (to->x); y = _cairo_fixed_from_26_6 (to->y); if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS) return 1; if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS) return 1; return 0; } static int _line_to (FT_Vector *to, void *closure) { cairo_path_fixed_t *path = closure; cairo_fixed_t x, y; x = _cairo_fixed_from_26_6 (to->x); y = _cairo_fixed_from_26_6 (to->y); if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS) return 1; return 0; } static int _conic_to (FT_Vector *control, FT_Vector *to, void *closure) { cairo_path_fixed_t *path = closure; cairo_fixed_t x0, y0; cairo_fixed_t x1, y1; cairo_fixed_t x2, y2; cairo_fixed_t x3, y3; cairo_point_t conic; if (! _cairo_path_fixed_get_current_point (path, &x0, &y0)) return 1; conic.x = _cairo_fixed_from_26_6 (control->x); conic.y = _cairo_fixed_from_26_6 (control->y); x3 = _cairo_fixed_from_26_6 (to->x); y3 = _cairo_fixed_from_26_6 (to->y); x1 = x0 + 2.0/3.0 * (conic.x - x0); y1 = y0 + 2.0/3.0 * (conic.y - y0); x2 = x3 + 2.0/3.0 * (conic.x - x3); y2 = y3 + 2.0/3.0 * (conic.y - y3); if (_cairo_path_fixed_curve_to (path, x1, y1, x2, y2, x3, y3) != CAIRO_STATUS_SUCCESS) return 1; return 0; } static int _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure) { cairo_path_fixed_t *path = closure; cairo_fixed_t x0, y0; cairo_fixed_t x1, y1; cairo_fixed_t x2, y2; x0 = _cairo_fixed_from_26_6 (control1->x); y0 = _cairo_fixed_from_26_6 (control1->y); x1 = _cairo_fixed_from_26_6 (control2->x); y1 = _cairo_fixed_from_26_6 (control2->y); x2 = _cairo_fixed_from_26_6 (to->x); y2 = _cairo_fixed_from_26_6 (to->y); if (_cairo_path_fixed_curve_to (path, x0, y0, x1, y1, x2, y2) != CAIRO_STATUS_SUCCESS) return 1; return 0; } static cairo_status_t _decompose_glyph_outline (FT_Face face, cairo_font_options_t *options, cairo_path_fixed_t **pathp) { static const FT_Outline_Funcs outline_funcs = { (FT_Outline_MoveToFunc)_move_to, (FT_Outline_LineToFunc)_line_to, (FT_Outline_ConicToFunc)_conic_to, (FT_Outline_CubicToFunc)_cubic_to, 0, /* shift */ 0, /* delta */ }; static const FT_Matrix invert_y = { DOUBLE_TO_16_16 (1.0), 0, 0, DOUBLE_TO_16_16 (-1.0), }; FT_GlyphSlot glyph; cairo_path_fixed_t *path; cairo_status_t status; path = _cairo_path_fixed_create (); if (!path) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glyph = face->glyph; /* Font glyphs have an inverted Y axis compared to cairo. */ FT_Outline_Transform (&glyph->outline, &invert_y); if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) { _cairo_path_fixed_destroy (path); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } status = _cairo_path_fixed_close_path (path); if (unlikely (status)) { _cairo_path_fixed_destroy (path); return status; } *pathp = path; return CAIRO_STATUS_SUCCESS; } /* * Translate glyph to match its metrics. */ static void _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, FT_GlyphSlot glyph) { cairo_ft_scaled_font_t *scaled_font = abstract_font; FT_Vector vector; vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX; vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY; if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape); FT_Outline_Translate(&glyph->outline, vector.x, vector.y); } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { glyph->bitmap_left += vector.x / 64; glyph->bitmap_top += vector.y / 64; } } static cairo_int_status_t _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info) { cairo_text_extents_t fs_metrics; cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_GlyphSlot glyph; FT_Face face; FT_Error error; int load_flags = scaled_font->ft_options.load_flags; FT_Glyph_Metrics *metrics; double x_factor, y_factor; cairo_bool_t vertical_layout = FALSE; cairo_status_t status; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); if (unlikely (status)) goto FAIL; /* Ignore global advance unconditionally */ load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) load_flags |= FT_LOAD_NO_BITMAP; /* * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as * suggested by freetype people. */ if (load_flags & FT_LOAD_VERTICAL_LAYOUT) { load_flags &= ~FT_LOAD_VERTICAL_LAYOUT; vertical_layout = TRUE; } #ifdef FT_LOAD_COLOR /* Color-glyph support: * * This flags needs plumbing through fontconfig (does it?), and * maybe we should cache color and grayscale bitmaps separately * such that users of the font (ie. the surface) can choose which * version to use based on target content type. * * Moreover, none of our backends and compositors currently support * color glyphs. As such, this is currently disabled. */ /* load_flags |= FT_LOAD_COLOR; */ #endif error = FT_Load_Glyph (face, _cairo_scaled_glyph_index(scaled_glyph), load_flags); /* XXX ignoring all other errors for now. They are not fatal, typically * just a glyph-not-found. */ if (error == FT_Err_Out_Of_Memory) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } glyph = face->glyph; /* * synthesize glyphs if requested */ #if HAVE_FT_GLYPHSLOT_EMBOLDEN if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) FT_GlyphSlot_Embolden (glyph); #endif #if HAVE_FT_GLYPHSLOT_OBLIQUE if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) FT_GlyphSlot_Oblique (glyph); #endif if (vertical_layout) _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; /* * Compute font-space metrics */ metrics = &glyph->metrics; if (unscaled->x_scale == 0) x_factor = 0; else x_factor = 1 / unscaled->x_scale; if (unscaled->y_scale == 0) y_factor = 0; else y_factor = 1 / unscaled->y_scale; /* * Note: Y coordinates of the horizontal bearing need to be negated. * * Scale metrics back to glyph space from the scaled glyph space returned * by FreeType * * If we want hinted metrics but aren't asking for hinted glyphs from * FreeType, then we need to do the metric hinting ourselves. */ if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) { FT_Pos x1, x2; FT_Pos y1, y2; FT_Pos advance; if (!vertical_layout) { x1 = (metrics->horiBearingX) & -64; x2 = (metrics->horiBearingX + metrics->width + 63) & -64; y1 = (-metrics->horiBearingY) & -64; y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; advance = ((metrics->horiAdvance + 32) & -64); fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; fs_metrics.y_advance = 0; } else { x1 = (metrics->vertBearingX) & -64; x2 = (metrics->vertBearingX + metrics->width + 63) & -64; y1 = (metrics->vertBearingY) & -64; y2 = (metrics->vertBearingY + metrics->height + 63) & -64; advance = ((metrics->vertAdvance + 32) & -64); fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; fs_metrics.x_advance = 0; fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; } } else { fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; if (!vertical_layout) { fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; else fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; fs_metrics.y_advance = 0 * y_factor; } else { fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; fs_metrics.x_advance = 0 * x_factor; if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; else fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; } } _cairo_scaled_glyph_set_metrics (scaled_glyph, &scaled_font->base, &fs_metrics); } if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { cairo_image_surface_t *surface; if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { status = _render_glyph_outline (face, &scaled_font->ft_options.base, &surface); } else { status = _render_glyph_bitmap (face, &scaled_font->ft_options.base, &surface); if (likely (status == CAIRO_STATUS_SUCCESS) && unscaled->have_shape) { status = _transform_glyph_bitmap (&unscaled->current_shape, &surface); if (unlikely (status)) cairo_surface_destroy (&surface->base); } } if (unlikely (status)) goto FAIL; _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, surface); } if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { cairo_path_fixed_t *path = NULL; /* hide compiler warning */ /* * A kludge -- the above code will trash the outline, * so reload it. This will probably never occur though */ if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { error = FT_Load_Glyph (face, _cairo_scaled_glyph_index(scaled_glyph), load_flags | FT_LOAD_NO_BITMAP); /* XXX ignoring all other errors for now. They are not fatal, typically * just a glyph-not-found. */ if (error == FT_Err_Out_Of_Memory) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } #if HAVE_FT_GLYPHSLOT_EMBOLDEN if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) FT_GlyphSlot_Embolden (glyph); #endif #if HAVE_FT_GLYPHSLOT_OBLIQUE if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) FT_GlyphSlot_Oblique (glyph); #endif if (vertical_layout) _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); } if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, &path); else status = CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (status)) goto FAIL; _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); } FAIL: _cairo_ft_unscaled_font_unlock_face (unscaled); return status; } static unsigned long _cairo_ft_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_Face face; FT_UInt index; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return 0; #if CAIRO_HAS_FC_FONT index = FcFreeTypeCharIndex (face, ucs4); #else index = FT_Get_Char_Index (face, ucs4); #endif _cairo_ft_unscaled_font_unlock_face (unscaled); return index; } static cairo_int_status_t _cairo_ft_load_truetype_table (void *abstract_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length) { cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_Face face; cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; /* We don't support the FreeType feature of loading a table * without specifying the size since this may overflow our * buffer. */ assert (length != NULL); if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) return CAIRO_INT_STATUS_UNSUPPORTED; #if HAVE_FT_LOAD_SFNT_TABLE face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (FT_IS_SFNT (face)) { if (buffer == NULL) *length = 0; if (FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) status = CAIRO_STATUS_SUCCESS; } _cairo_ft_unscaled_font_unlock_face (unscaled); #endif return status; } static cairo_int_status_t _cairo_ft_index_to_ucs4(void *abstract_font, unsigned long index, uint32_t *ucs4) { cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_Face face; FT_ULong charcode; FT_UInt gindex; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); *ucs4 = (uint32_t) -1; charcode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { if (gindex == index) { *ucs4 = charcode; break; } charcode = FT_Get_Next_Char (face, charcode, &gindex); } _cairo_ft_unscaled_font_unlock_face (unscaled); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_ft_is_synthetic (void *abstract_font) { cairo_ft_scaled_font_t *scaled_font = abstract_font; return scaled_font->ft_options.synth_flags != 0; } static cairo_int_status_t _cairo_index_to_glyph_name (void *abstract_font, char **glyph_names, int num_glyph_names, unsigned long glyph_index, unsigned long *glyph_array_index) { cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_Face face; char buffer[256]; /* PLRM spcifies max name length of 127 */ FT_Error error; int i; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); error = FT_Get_Glyph_Name (face, glyph_index, buffer, sizeof buffer); _cairo_ft_unscaled_font_unlock_face (unscaled); if (error != FT_Err_Ok) { /* propagate fatal errors from FreeType */ if (error == FT_Err_Out_Of_Memory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_INT_STATUS_UNSUPPORTED; } /* FT first numbers the glyphs in the order they are read from the * Type 1 font. Then if .notdef is not the first glyph, the first * glyph is swapped with .notdef to ensure that .notdef is at * glyph index 0. * * As all but two glyphs in glyph_names already have the same * index as the FT glyph index, we first check if * glyph_names[glyph_index] is the name we are looking for. If not * we fall back to searching the entire array. */ if ((long)glyph_index < num_glyph_names && strcmp (glyph_names[glyph_index], buffer) == 0) { *glyph_array_index = glyph_index; return CAIRO_STATUS_SUCCESS; } for (i = 0; i < num_glyph_names; i++) { if (strcmp (glyph_names[i], buffer) == 0) { *glyph_array_index = i; return CAIRO_STATUS_SUCCESS; } } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_bool_t _ft_is_type1 (FT_Face face) { #if HAVE_FT_GET_X11_FONT_FORMAT const char *font_format = FT_Get_X11_Font_Format (face); if (font_format && (strcmp (font_format, "Type 1") == 0 || strcmp (font_format, "CFF") == 0)) { return TRUE; } #endif return FALSE; } static cairo_int_status_t _cairo_ft_load_type1_data (void *abstract_font, long offset, unsigned char *buffer, unsigned long *length) { cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_Face face; cairo_status_t status = CAIRO_STATUS_SUCCESS; unsigned long available_length; unsigned long ret; assert (length != NULL); if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) return CAIRO_INT_STATUS_UNSUPPORTED; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); #if HAVE_FT_LOAD_SFNT_TABLE if (FT_IS_SFNT (face)) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto unlock; } #endif if (! _ft_is_type1 (face)) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto unlock; } available_length = MAX (face->stream->size - offset, 0); if (!buffer) { *length = available_length; } else { if (*length > available_length) { status = CAIRO_INT_STATUS_UNSUPPORTED; } else if (face->stream->read != NULL) { /* Note that read() may be implemented as a macro, thanks POSIX!, so we * need to wrap the following usage in parentheses in order to * disambiguate it for the pre-processor - using the verbose function * pointer dereference for clarity. */ ret = (* face->stream->read) (face->stream, offset, buffer, *length); if (ret != *length) status = _cairo_error (CAIRO_STATUS_READ_ERROR); } else { memcpy (buffer, face->stream->base + offset, *length); } } unlock: _cairo_ft_unscaled_font_unlock_face (unscaled); return status; } static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { CAIRO_FONT_TYPE_FT, _cairo_ft_scaled_font_fini, _cairo_ft_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_ft_ucs4_to_index, _cairo_ft_load_truetype_table, _cairo_ft_index_to_ucs4, _cairo_ft_is_synthetic, _cairo_index_to_glyph_name, _cairo_ft_load_type1_data }; /* #cairo_ft_font_face_t */ #if CAIRO_HAS_FC_FONT static cairo_font_face_t * _cairo_ft_font_face_create_for_pattern (FcPattern *pattern); static cairo_status_t _cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face_out) { cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil; FcPattern *pattern; int fcslant; int fcweight; pattern = FcPatternCreate (); if (!pattern) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return font_face->status; } if (!FcPatternAddString (pattern, FC_FAMILY, (unsigned char *) toy_face->family)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } switch (toy_face->slant) { case CAIRO_FONT_SLANT_ITALIC: fcslant = FC_SLANT_ITALIC; break; case CAIRO_FONT_SLANT_OBLIQUE: fcslant = FC_SLANT_OBLIQUE; break; case CAIRO_FONT_SLANT_NORMAL: default: fcslant = FC_SLANT_ROMAN; break; } if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } switch (toy_face->weight) { case CAIRO_FONT_WEIGHT_BOLD: fcweight = FC_WEIGHT_BOLD; break; case CAIRO_FONT_WEIGHT_NORMAL: default: fcweight = FC_WEIGHT_MEDIUM; break; } if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } font_face = _cairo_ft_font_face_create_for_pattern (pattern); FREE_PATTERN: FcPatternDestroy (pattern); *font_face_out = font_face; return font_face->status; } #endif static void _cairo_ft_font_face_destroy (void *abstract_face) { cairo_ft_font_face_t *font_face = abstract_face; /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, * we have a special "zombie" state for the face when the unscaled font * is still alive but there are no other references to a font face with * the same FT_Face. * * We go from: * * font_face ------> unscaled * <-....weak....../ * * To: * * font_face <------- unscaled */ if (font_face->unscaled && font_face->unscaled->from_face && font_face->next == NULL && font_face->unscaled->faces == font_face && CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) { cairo_font_face_reference (&font_face->base); _cairo_unscaled_font_destroy (&font_face->unscaled->base); font_face->unscaled = NULL; return; } if (font_face->unscaled) { cairo_ft_font_face_t *tmp_face = NULL; cairo_ft_font_face_t *last_face = NULL; /* Remove face from linked list */ for (tmp_face = font_face->unscaled->faces; tmp_face; tmp_face = tmp_face->next) { if (tmp_face == font_face) { if (last_face) last_face->next = tmp_face->next; else font_face->unscaled->faces = tmp_face->next; } last_face = tmp_face; } _cairo_unscaled_font_destroy (&font_face->unscaled->base); font_face->unscaled = NULL; } #if CAIRO_HAS_FC_FONT if (font_face->pattern) { FcPatternDestroy (font_face->pattern); cairo_font_face_destroy (font_face->resolved_font_face); } #endif } static cairo_font_face_t * _cairo_ft_font_face_get_implementation (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { cairo_ft_font_face_t *font_face = abstract_face; /* The handling of font options is different depending on how the * font face was created. When the user creates a font face with * cairo_ft_font_face_create_for_ft_face(), then the load flags * passed in augment the load flags for the options. But for * cairo_ft_font_face_create_for_pattern(), the load flags are * derived from a pattern where the user has called * cairo_ft_font_options_substitute(), so *just* use those load * flags and ignore the options. */ #if CAIRO_HAS_FC_FONT /* If we have an unresolved pattern, resolve it and create * unscaled font. Otherwise, use the ones stored in font_face. */ if (font_face->pattern) { cairo_font_face_t *resolved; /* Cache the resolved font whilst the FcConfig remains consistent. */ resolved = font_face->resolved_font_face; if (resolved != NULL) { if (! FcInitBringUptoDate ()) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *) &_cairo_font_face_nil; } if (font_face->resolved_config == FcConfigGetCurrent ()) return cairo_font_face_reference (resolved); cairo_font_face_destroy (resolved); font_face->resolved_font_face = NULL; } resolved = _cairo_ft_resolve_pattern (font_face->pattern, font_matrix, ctm, options); if (unlikely (resolved->status)) return resolved; font_face->resolved_font_face = cairo_font_face_reference (resolved); font_face->resolved_config = FcConfigGetCurrent (); return resolved; } #endif return abstract_face; } const cairo_font_face_backend_t _cairo_ft_font_face_backend = { CAIRO_FONT_TYPE_FT, #if CAIRO_HAS_FC_FONT _cairo_ft_font_face_create_for_toy, #else NULL, #endif _cairo_ft_font_face_destroy, _cairo_ft_font_face_scaled_font_create, _cairo_ft_font_face_get_implementation }; #if CAIRO_HAS_FC_FONT static cairo_font_face_t * _cairo_ft_font_face_create_for_pattern (FcPattern *pattern) { cairo_ft_font_face_t *font_face; font_face = malloc (sizeof (cairo_ft_font_face_t)); if (unlikely (font_face == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *) &_cairo_font_face_nil; } font_face->unscaled = NULL; font_face->next = NULL; font_face->pattern = FcPatternDuplicate (pattern); if (unlikely (font_face->pattern == NULL)) { free (font_face); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *) &_cairo_font_face_nil; } font_face->resolved_font_face = NULL; font_face->resolved_config = NULL; _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); return &font_face->base; } #endif static cairo_font_face_t * _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, cairo_ft_options_t *ft_options) { cairo_ft_font_face_t *font_face, **prev_font_face; /* Looked for an existing matching font face */ for (font_face = unscaled->faces, prev_font_face = &unscaled->faces; font_face; prev_font_face = &font_face->next, font_face = font_face->next) { if (font_face->ft_options.load_flags == ft_options->load_flags && font_face->ft_options.synth_flags == ft_options->synth_flags && cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base)) { if (font_face->base.status) { /* The font_face has been left in an error state, abandon it. */ *prev_font_face = font_face->next; break; } if (font_face->unscaled == NULL) { /* Resurrect this "zombie" font_face (from * _cairo_ft_font_face_destroy), switching its unscaled_font * from owner to ownee. */ font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); return &font_face->base; } else return cairo_font_face_reference (&font_face->base); } } /* No match found, create a new one */ font_face = malloc (sizeof (cairo_ft_font_face_t)); if (unlikely (!font_face)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); font_face->ft_options = *ft_options; if (unscaled->faces && unscaled->faces->unscaled == NULL) { /* This "zombie" font_face (from _cairo_ft_font_face_destroy) * is no longer needed. */ assert (unscaled->from_face && unscaled->faces->next == NULL); cairo_font_face_destroy (&unscaled->faces->base); unscaled->faces = NULL; } font_face->next = unscaled->faces; unscaled->faces = font_face; #if CAIRO_HAS_FC_FONT font_face->pattern = NULL; #endif _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); return &font_face->base; } /* implement the platform-specific interface */ #if CAIRO_HAS_FC_FONT static cairo_status_t _cairo_ft_font_options_substitute (const cairo_font_options_t *options, FcPattern *pattern) { FcValue v; if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) { if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) { if (! FcPatternAddBool (pattern, FC_ANTIALIAS, options->antialias != CAIRO_ANTIALIAS_NONE)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { FcPatternDel (pattern, FC_RGBA); if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } } if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) { if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch) { int rgba; if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { switch (options->subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: default: rgba = FC_RGBA_RGB; break; case CAIRO_SUBPIXEL_ORDER_BGR: rgba = FC_RGBA_BGR; break; case CAIRO_SUBPIXEL_ORDER_VRGB: rgba = FC_RGBA_VRGB; break; case CAIRO_SUBPIXEL_ORDER_VBGR: rgba = FC_RGBA_VBGR; break; } } else { rgba = FC_RGBA_NONE; } if (! FcPatternAddInteger (pattern, FC_RGBA, rgba)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) { if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch) { int lcd_filter; switch (options->lcd_filter) { case CAIRO_LCD_FILTER_NONE: lcd_filter = FT_LCD_FILTER_NONE; break; case CAIRO_LCD_FILTER_DEFAULT: case CAIRO_LCD_FILTER_INTRA_PIXEL: lcd_filter = FT_LCD_FILTER_LEGACY; break; case CAIRO_LCD_FILTER_FIR3: lcd_filter = FT_LCD_FILTER_LIGHT; break; default: case CAIRO_LCD_FILTER_FIR5: lcd_filter = FT_LCD_FILTER_DEFAULT; break; } if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT) { if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch) { if (! FcPatternAddBool (pattern, FC_HINTING, options->hint_style != CAIRO_HINT_STYLE_NONE)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } #ifdef FC_HINT_STYLE if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch) { int hint_style; switch (options->hint_style) { case CAIRO_HINT_STYLE_NONE: hint_style = FC_HINT_NONE; break; case CAIRO_HINT_STYLE_SLIGHT: hint_style = FC_HINT_SLIGHT; break; case CAIRO_HINT_STYLE_MEDIUM: hint_style = FC_HINT_MEDIUM; break; case CAIRO_HINT_STYLE_FULL: case CAIRO_HINT_STYLE_DEFAULT: default: hint_style = FC_HINT_FULL; break; } if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } #endif } return CAIRO_STATUS_SUCCESS; } /** * cairo_ft_font_options_substitute: * @options: a #cairo_font_options_t object * @pattern: an existing #FcPattern * * Add options to a #FcPattern based on a #cairo_font_options_t font * options object. Options that are already in the pattern, are not overridden, * so you should call this function after calling FcConfigSubstitute() (the * user's settings should override options based on the surface type), but * before calling FcDefaultSubstitute(). * * Since: 1.0 **/ void cairo_ft_font_options_substitute (const cairo_font_options_t *options, FcPattern *pattern) { if (cairo_font_options_status ((cairo_font_options_t *) options)) return; _cairo_ft_font_options_substitute (options, pattern); } static cairo_font_face_t * _cairo_ft_resolve_pattern (FcPattern *pattern, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *font_options) { cairo_status_t status; cairo_matrix_t scale; FcPattern *resolved; cairo_ft_font_transform_t sf; FcResult result; cairo_ft_unscaled_font_t *unscaled; cairo_ft_options_t ft_options; cairo_font_face_t *font_face; scale = *ctm; scale.x0 = scale.y0 = 0; cairo_matrix_multiply (&scale, font_matrix, &scale); status = _compute_transform (&sf, &scale, NULL); if (unlikely (status)) return (cairo_font_face_t *)&_cairo_font_face_nil; pattern = FcPatternDuplicate (pattern); if (pattern == NULL) return (cairo_font_face_t *)&_cairo_font_face_nil; if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_PATTERN; } if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_PATTERN; } status = _cairo_ft_font_options_substitute (font_options, pattern); if (status) { font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_PATTERN; } FcDefaultSubstitute (pattern); status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); if (unlikely (status)) { font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_PATTERN; } if (unscaled == NULL) { resolved = FcFontMatch (NULL, pattern, &result); if (!resolved) { /* We failed to find any font. Substitute twin so that the user can * see something (and hopefully recognise that the font is missing) * and not just receive a NO_MEMORY error during rendering. */ font_face = _cairo_font_face_twin_create_fallback (); goto FREE_PATTERN; } status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); if (unlikely (status || unscaled == NULL)) { font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_RESOLVED; } } else resolved = pattern; _get_pattern_ft_options (resolved, &ft_options); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); _cairo_unscaled_font_destroy (&unscaled->base); FREE_RESOLVED: if (resolved != pattern) FcPatternDestroy (resolved); FREE_PATTERN: FcPatternDestroy (pattern); return font_face; } /** * cairo_ft_font_face_create_for_pattern: * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern * if it needs to. You are free to modify or free @pattern after this call. * * Creates a new font face for the FreeType font backend based on a * fontconfig pattern. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). The * #cairo_scaled_font_t returned from cairo_scaled_font_create() is * also for the FreeType backend and can be used with functions such * as cairo_ft_scaled_font_lock_face(). * * Font rendering options are represented both here and when you * call cairo_scaled_font_create(). Font options that have a representation * in a #FcPattern must be passed in here; to modify #FcPattern * appropriately to reflect the options in a #cairo_font_options_t, call * cairo_ft_font_options_substitute(). * * The pattern's FC_FT_FACE element is inspected first and if that is set, * that will be the FreeType font face associated with the returned cairo * font face. Otherwise the FC_FILE element is checked. If it's set, * that and the value of the FC_INDEX element (defaults to zero) of @pattern * are used to load a font face from file. * * If both steps from the previous paragraph fails, @pattern will be passed * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch, * and the resulting font pattern is used. * * If the FC_FT_FACE element of @pattern is set, the user is responsible * for making sure that the referenced FT_Face remains valid for the life * time of the returned #cairo_font_face_t. See * cairo_ft_font_face_create_for_ft_face() for an example of how to couple * the life time of the FT_Face to that of the cairo font-face. * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.0 **/ cairo_font_face_t * cairo_ft_font_face_create_for_pattern (FcPattern *pattern) { cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; cairo_status_t status; status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); if (unlikely (status)) return (cairo_font_face_t *) &_cairo_font_face_nil; if (unlikely (unscaled == NULL)) { /* Store the pattern. We will resolve it and create unscaled * font when creating scaled fonts */ return _cairo_ft_font_face_create_for_pattern (pattern); } _get_pattern_ft_options (pattern, &ft_options); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); _cairo_unscaled_font_destroy (&unscaled->base); return font_face; } #endif /** * cairo_ft_font_face_create_for_ft_face: * @face: A FreeType face object, already opened. This must * be kept around until the face's ref_count drops to * zero and it is freed. Since the face may be referenced * internally to Cairo, the best way to determine when it * is safe to free the face is to pass a * #cairo_destroy_func_t to cairo_font_face_set_user_data() * @load_flags: flags to pass to FT_Load_Glyph when loading * glyphs from the font. These flags are OR'ed together with * the flags derived from the #cairo_font_options_t passed * to cairo_scaled_font_create(), so only a few values such * as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT * are useful. You should not pass any of the flags affecting * the load target, such as %FT_LOAD_TARGET_LIGHT. * * Creates a new font face for the FreeType font backend from a * pre-opened FreeType face. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). The * #cairo_scaled_font_t returned from cairo_scaled_font_create() is * also for the FreeType backend and can be used with functions such * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference * to the FT_Face alive in a font-cache and the exact lifetime of the reference * depends highly upon the exact usage pattern and is subject to external * factors. You must not call FT_Done_Face() before the last reference to the * #cairo_font_face_t has been dropped. * * As an example, below is how one might correctly couple the lifetime of * the FreeType face object to the #cairo_font_face_t. * * <informalexample><programlisting> * static const cairo_user_data_key_t key; * * font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); * status = cairo_font_face_set_user_data (font_face, &key, * ft_face, (cairo_destroy_func_t) FT_Done_Face); * if (status) { * cairo_font_face_destroy (font_face); * FT_Done_Face (ft_face); * return ERROR; * } * </programlisting></informalexample> * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.0 **/ cairo_font_face_t * cairo_ft_font_face_create_for_ft_face (FT_Face face, int load_flags) { cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; cairo_status_t status; status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); if (unlikely (status)) return (cairo_font_face_t *)&_cairo_font_face_nil; ft_options.load_flags = load_flags; ft_options.synth_flags = 0; _cairo_font_options_init_default (&ft_options.base); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); _cairo_unscaled_font_destroy (&unscaled->base); return font_face; } /** * cairo_ft_font_face_set_synthesize: * @font_face: The #cairo_ft_font_face_t object to modify * @synth_flags: the set of synthesis options to enable * * FreeType provides the ability to synthesize different glyphs from a base * font, which is useful if you lack those glyphs from a true bold or oblique * font. See also #cairo_ft_synthesize_t. * * Since: 1.12 **/ void cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face, unsigned int synth_flags) { cairo_ft_font_face_t *ft; if (font_face->backend->type != CAIRO_FONT_TYPE_FT) return; ft = (cairo_ft_font_face_t *) font_face; ft->ft_options.synth_flags |= synth_flags; } /** * cairo_ft_font_face_unset_synthesize: * @font_face: The #cairo_ft_font_face_t object to modify * @synth_flags: the set of synthesis options to disable * * See cairo_ft_font_face_set_synthesize(). * * Since: 1.12 **/ void cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face, unsigned int synth_flags) { cairo_ft_font_face_t *ft; if (font_face->backend->type != CAIRO_FONT_TYPE_FT) return; ft = (cairo_ft_font_face_t *) font_face; ft->ft_options.synth_flags &= ~synth_flags; } /** * cairo_ft_font_face_get_synthesize: * @font_face: The #cairo_ft_font_face_t object to query * * See #cairo_ft_synthesize_t. * * Returns: the current set of synthesis options. * * Since: 1.12 **/ unsigned int cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face) { cairo_ft_font_face_t *ft; if (font_face->backend->type != CAIRO_FONT_TYPE_FT) return 0; ft = (cairo_ft_font_face_t *) font_face; return ft->ft_options.synth_flags; } /** * cairo_ft_scaled_font_lock_face: * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an * object can be created by calling cairo_scaled_font_create() on a * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), * cairo_ft_font_face_create_for_ft_face()). * * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType * backend font and scales it appropriately for the font. You must * release the face with cairo_ft_scaled_font_unlock_face() * when you are done using it. Since the #FT_Face object can be * shared between multiple #cairo_scaled_font_t objects, you must not * lock any other font objects until you unlock this one. A count is * kept of the number of times cairo_ft_scaled_font_lock_face() is * called. cairo_ft_scaled_font_unlock_face() must be called the same number * of times. * * You must be careful when using this function in a library or in a * threaded application, because freetype's design makes it unsafe to * call freetype functions simultaneously from multiple threads, (even * if using distinct FT_Face objects). Because of this, application * code that acquires an FT_Face object with this call must add its * own locking to protect any use of that object, (and which also must * protect any other calls into cairo as almost any cairo function * might result in a call into the freetype library). * * Return value: The #FT_Face object for @font, scaled appropriately, * or %NULL if @scaled_font is in an error state (see * cairo_scaled_font_status()) or there is insufficient memory. * * Since: 1.0 **/ FT_Face cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) { cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; FT_Face face; cairo_status_t status; if (! _cairo_scaled_font_is_ft (abstract_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); return NULL; } if (scaled_font->base.status) return NULL; face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); if (unlikely (face == NULL)) { status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY); return NULL; } status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &scaled_font->base.scale); if (unlikely (status)) { _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); status = _cairo_scaled_font_set_error (&scaled_font->base, status); return NULL; } /* Note: We deliberately release the unscaled font's mutex here, * so that we are not holding a lock across two separate calls to * cairo function, (which would give the application some * opportunity for creating deadlock. This is obviously unsafe, * but as documented, the user must add manual locking when using * this function. */ CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex); return face; } /** * cairo_ft_scaled_font_unlock_face: * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an * object can be created by calling cairo_scaled_font_create() on a * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), * cairo_ft_font_face_create_for_ft_face()). * * Releases a face obtained with cairo_ft_scaled_font_lock_face(). * * Since: 1.0 **/ void cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) { cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; if (! _cairo_scaled_font_is_ft (abstract_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); return; } if (scaled_font->base.status) return; /* Note: We released the unscaled font's mutex at the end of * cairo_ft_scaled_font_lock_face, so we have to acquire it again * as _cairo_ft_unscaled_font_unlock_face expects it to be held * when we call into it. */ CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex); _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); } static cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) { cairo_ft_scaled_font_t *ft_scaled_font; if (!_cairo_scaled_font_is_ft (scaled_font)) return FALSE; ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT) return TRUE; return FALSE; } unsigned int _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) { cairo_ft_scaled_font_t *ft_scaled_font; if (! _cairo_scaled_font_is_ft (scaled_font)) return 0; ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; return ft_scaled_font->ft_options.load_flags; } void _cairo_ft_font_reset_static_data (void) { _cairo_ft_unscaled_font_map_destroy (); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ft-private.h������������������������������0000664�0000000�0000000�00000004174�12710376503�0025633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> */ #ifndef CAIRO_FT_PRIVATE_H #define CAIRO_FT_PRIVATE_H #include "cairo-ft.h" #include "cairoint.h" #if CAIRO_HAS_FT_FONT CAIRO_BEGIN_DECLS typedef struct _cairo_ft_unscaled_font cairo_ft_unscaled_font_t; cairo_private cairo_bool_t _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); /* These functions are needed by the PDF backend, which needs to keep track of the * the different fonts-on-disk used by a document, so it can embed them */ cairo_private unsigned int _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); CAIRO_END_DECLS #endif /* CAIRO_HAS_FT_FONT */ #endif /* CAIRO_FT_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ft.h��������������������������������������0000664�0000000�0000000�00000007211�12710376503�0024156�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> */ #ifndef CAIRO_FT_H #define CAIRO_FT_H #include "cairo.h" #if CAIRO_HAS_FT_FONT /* Fontconfig/Freetype platform-specific font interface */ #include <ft2build.h> #include FT_FREETYPE_H #if CAIRO_HAS_FC_FONT #include <fontconfig/fontconfig.h> #endif CAIRO_BEGIN_DECLS cairo_public cairo_font_face_t * cairo_ft_font_face_create_for_ft_face (FT_Face face, int load_flags); /** * cairo_ft_synthesize_t: * @CAIRO_FT_SYNTHESIZE_BOLD: Embolden the glyphs (redraw with a pixel offset) * @CAIRO_FT_SYNTHESIZE_OBLIQUE: Slant the glyph outline by 12 degrees to the * right. * * A set of synthesis options to control how FreeType renders the glyphs * for a particular font face. * * Individual synthesis features of a #cairo_ft_font_face_t can be set * using cairo_ft_font_face_set_synthesize(), or disabled using * cairo_ft_font_face_unset_synthesize(). The currently enabled set of * synthesis options can be queried with cairo_ft_font_face_get_synthesize(). * * Note: that when synthesizing glyphs, the font metrics returned will only * be estimates. * * Since: 1.12 **/ typedef enum { CAIRO_FT_SYNTHESIZE_BOLD = 1 << 0, CAIRO_FT_SYNTHESIZE_OBLIQUE = 1 << 1 } cairo_ft_synthesize_t; cairo_public void cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face, unsigned int synth_flags); cairo_public void cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face, unsigned int synth_flags); cairo_public unsigned int cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face); cairo_public FT_Face cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font); cairo_public void cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); #if CAIRO_HAS_FC_FONT cairo_public cairo_font_face_t * cairo_ft_font_face_create_for_pattern (FcPattern *pattern); cairo_public void cairo_ft_font_options_substitute (const cairo_font_options_t *options, FcPattern *pattern); #endif CAIRO_END_DECLS #else /* CAIRO_HAS_FT_FONT */ # error Cairo was not compiled with support for the freetype font backend #endif /* CAIRO_HAS_FT_FONT */ #endif /* CAIRO_FT_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-composite.c����������������������������0000664�0000000�0000000�00000115753�12710376503�0026155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2011 Linaro Limited * Copyright © 2011 Samsung Electronics * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> * Alexandros Frantzis <alexandros.frantzis@linaro.org> * Henry Song <hsong@sisa.samsung.com> * Martin Robinson <mrobinson@igalia.com> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" cairo_int_status_t _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { _cairo_gl_operand_destroy (&setup->src); return _cairo_gl_operand_init (&setup->src, pattern, setup->dst, sample, extents, use_texgen); } void _cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, const cairo_gl_operand_t *source) { _cairo_gl_operand_destroy (&setup->src); _cairo_gl_operand_copy (&setup->src, source); } void _cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, const cairo_color_t *color) { _cairo_gl_operand_destroy (&setup->src); _cairo_gl_solid_operand_init (&setup->src, color); } cairo_int_status_t _cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { _cairo_gl_operand_destroy (&setup->mask); if (pattern == NULL) return CAIRO_STATUS_SUCCESS; return _cairo_gl_operand_init (&setup->mask, pattern, setup->dst, sample, extents, use_texgen); } void _cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, const cairo_gl_operand_t *mask) { _cairo_gl_operand_destroy (&setup->mask); if (mask) _cairo_gl_operand_copy (&setup->mask, mask); } void _cairo_gl_composite_set_spans (cairo_gl_composite_t *setup) { setup->spans = TRUE; } void _cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup) { setup->multisample = TRUE; } void _cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, cairo_region_t *clip_region) { setup->clip_region = clip_region; } void _cairo_gl_composite_set_clip (cairo_gl_composite_t *setup, cairo_clip_t *clip) { setup->clip = clip; } static void _cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup) { _cairo_gl_shader_bind_matrix4f(ctx, ctx->current_shader->mvp_location, ctx->modelviewprojection_matrix); _cairo_gl_operand_bind_to_shader (ctx, &setup->src, CAIRO_GL_TEX_SOURCE); _cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK); } static void _cairo_gl_texture_set_filter (cairo_gl_context_t *ctx, GLuint target, cairo_filter_t filter) { switch (filter) { case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); break; default: case CAIRO_FILTER_GAUSSIAN: ASSERT_NOT_REACHED; } } static void _cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, GLuint target, cairo_extend_t extend) { GLint wrap_mode; assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) || (extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT)); switch (extend) { case CAIRO_EXTEND_NONE: if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) wrap_mode = GL_CLAMP_TO_EDGE; else wrap_mode = GL_CLAMP_TO_BORDER; break; case CAIRO_EXTEND_PAD: wrap_mode = GL_CLAMP_TO_EDGE; break; case CAIRO_EXTEND_REPEAT: if (ctx->has_npot_repeat) wrap_mode = GL_REPEAT; else wrap_mode = GL_CLAMP_TO_EDGE; break; case CAIRO_EXTEND_REFLECT: if (ctx->has_npot_repeat) wrap_mode = GL_MIRRORED_REPEAT; else wrap_mode = GL_CLAMP_TO_EDGE; break; default: wrap_mode = 0; } if (likely (wrap_mode)) { glTexParameteri (target, GL_TEXTURE_WRAP_S, wrap_mode); glTexParameteri (target, GL_TEXTURE_WRAP_T, wrap_mode); } } static void _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit, cairo_gl_operand_t *operand, unsigned int vertex_offset, cairo_bool_t vertex_size_changed) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; cairo_bool_t needs_setup; /* XXX: we need to do setup when switching from shaders * to no shaders (or back) */ needs_setup = vertex_size_changed; needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit], operand, vertex_offset); if (needs_setup) { _cairo_gl_composite_flush (ctx); _cairo_gl_context_destroy_operand (ctx, tex_unit); } memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t)); ctx->operands[tex_unit].vertex_offset = vertex_offset; if (! needs_setup) return; switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; /* fall through */ case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_TEXTURE: glActiveTexture (GL_TEXTURE0 + tex_unit); glBindTexture (ctx->tex_target, operand->texture.tex); _cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->texture.attributes.extend); _cairo_gl_texture_set_filter (ctx, ctx->tex_target, operand->texture.attributes.filter); if (! operand->texture.texgen) { dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, GL_FLOAT, GL_FALSE, ctx->vertex_size, ctx->vb + vertex_offset); dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); } break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: glActiveTexture (GL_TEXTURE0 + tex_unit); glBindTexture (ctx->tex_target, operand->gradient.gradient->tex); _cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->gradient.extend); _cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR); if (! operand->gradient.texgen) { dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2, GL_FLOAT, GL_FALSE, ctx->vertex_size, ctx->vb + vertex_offset); dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); } break; } } static void _cairo_gl_context_setup_spans (cairo_gl_context_t *ctx, cairo_bool_t spans_enabled, unsigned int vertex_size, unsigned int vertex_offset) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; if (! spans_enabled) { dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); ctx->spans = FALSE; return; } dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertex_size, ctx->vb + vertex_offset); dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); ctx->spans = TRUE; } void _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; if (!_cairo_gl_context_is_flushed (ctx)) _cairo_gl_composite_flush (ctx); switch (ctx->operands[tex_unit].type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; /* fall through */ case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_TEXTURE: dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit); break; } memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t)); } static void _cairo_gl_set_operator (cairo_gl_context_t *ctx, cairo_operator_t op, cairo_bool_t component_alpha) { struct { GLenum src; GLenum dst; } blend_factors[] = { { GL_ZERO, GL_ZERO }, /* Clear */ { GL_ONE, GL_ZERO }, /* Source */ { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */ { GL_DST_ALPHA, GL_ZERO }, /* In */ { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */ { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */ { GL_ZERO, GL_ONE }, /* Dest */ { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */ { GL_ZERO, GL_SRC_ALPHA }, /* DestIn */ { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */ { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */ { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */ { GL_ONE, GL_ONE }, /* Add */ }; GLenum src_factor, dst_factor; assert (op < ARRAY_LENGTH (blend_factors)); /* different dst and component_alpha changes cause flushes elsewhere */ if (ctx->current_operator != op) _cairo_gl_composite_flush (ctx); ctx->current_operator = op; src_factor = blend_factors[op].src; dst_factor = blend_factors[op].dst; /* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA * due to texture filtering of GL_CLAMP_TO_BORDER. So fix those * bits in that case. */ if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { if (src_factor == GL_ONE_MINUS_DST_ALPHA) src_factor = GL_ZERO; if (src_factor == GL_DST_ALPHA) src_factor = GL_ONE; } if (component_alpha) { if (dst_factor == GL_ONE_MINUS_SRC_ALPHA) dst_factor = GL_ONE_MINUS_SRC_COLOR; if (dst_factor == GL_SRC_ALPHA) dst_factor = GL_SRC_COLOR; } if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) { glBlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor); } else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) { glBlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE); } else { glBlendFunc (src_factor, dst_factor); } } static cairo_status_t _cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup) { cairo_gl_shader_t *pre_shader = NULL; cairo_status_t status; /* For CLEAR, cairo's rendering equation (quoting Owen's description in: * http://lists.cairographics.org/archives/cairo/2005-August/004992.html) * is: * mask IN clip ? src OP dest : dest * or more simply: * mask IN CLIP ? 0 : dest * * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C). * * The model we use in _cairo_gl_set_operator() is Render's: * src IN mask IN clip OP dest * which would boil down to: * 0 (bounded by the extents of the drawing). * * However, we can do a Render operation using an opaque source * and DEST_OUT to produce: * 1 IN mask IN clip DEST_OUT dest * which is * mask IN clip ? 0 : dest */ if (setup->op == CAIRO_OPERATOR_CLEAR) { _cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE); setup->op = CAIRO_OPERATOR_DEST_OUT; } /* * implements component-alpha %CAIRO_OPERATOR_OVER using two passes of * the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD. * * From http://anholt.livejournal.com/32058.html: * * The trouble is that component-alpha rendering requires two different sources * for blending: one for the source value to the blender, which is the * per-channel multiplication of source and mask, and one for the source alpha * for multiplying with the destination channels, which is the multiplication * of the source channels by the mask alpha. So the equation for Over is: * * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B * * But we can do some simpler operations, right? How about PictOpOutReverse, * which has a source factor of 0 and dest factor of (1 - source alpha). We * can get the source alpha value (srca.X = src.A * mask.X) out of the texture * blenders pretty easily. So we can do a component-alpha OutReverse, which * gets us: * * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B * * OK. And if an op doesn't use the source alpha value for the destination * factor, then we can do the channel multiplication in the texture blenders * to get the source value, and ignore the source alpha that we wouldn't use. * We've supported this in the Radeon driver for a long time. An example would * be PictOpAdd, which does: * * dst.A = src.A * mask.A + dst.A * dst.R = src.R * mask.R + dst.R * dst.G = src.G * mask.G + dst.G * dst.B = src.B * mask.B + dst.B * * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right * after it, we get: * * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) * * This two-pass trickery could be avoided using a new GL extension that * lets two values come out of the shader and into the blend unit. */ if (setup->op == CAIRO_OPERATOR_OVER) { setup->op = CAIRO_OPERATOR_ADD; status = _cairo_gl_get_shader_by_type (ctx, &setup->src, &setup->mask, setup->spans, CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, &pre_shader); if (unlikely (status)) return status; } if (ctx->pre_shader != pre_shader) _cairo_gl_composite_flush (ctx); ctx->pre_shader = pre_shader; return CAIRO_STATUS_SUCCESS; } static void _scissor_to_doubles (cairo_gl_surface_t *surface, double x1, double y1, double x2, double y2) { double height; height = y2 - y1; if (_cairo_gl_surface_is_texture (surface) == FALSE) y1 = surface->height - (y1 + height); glScissor (x1, y1, x2 - x1, height); glEnable (GL_SCISSOR_TEST); } void _cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, const cairo_rectangle_int_t *r) { _scissor_to_doubles (surface, r->x, r->y, r->x+r->width, r->y+r->height); } static void _scissor_to_box (cairo_gl_surface_t *surface, const cairo_box_t *box) { double x1, y1, x2, y2; _cairo_box_to_doubles (box, &x1, &y1, &x2, &y2); _scissor_to_doubles (surface, x1, y1, x2, y2); } static cairo_bool_t _cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx, unsigned int size_per_vertex) { cairo_bool_t vertex_size_changed = ctx->vertex_size != size_per_vertex; if (vertex_size_changed) { ctx->vertex_size = size_per_vertex; _cairo_gl_composite_flush (ctx); } if (_cairo_gl_context_is_flushed (ctx)) { ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2, GL_FLOAT, GL_FALSE, size_per_vertex, ctx->vb); ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX); } return vertex_size_changed; } static void _disable_stencil_buffer (void) { glDisable (GL_STENCIL_TEST); glDepthMask (GL_FALSE); } static cairo_int_status_t _cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup, cairo_gl_context_t *ctx, int vertex_size) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_gl_surface_t *dst = setup->dst; cairo_clip_t *clip = setup->clip; if (clip->num_boxes == 1 && clip->path == NULL) { _scissor_to_box (dst, &clip->boxes[0]); goto disable_stencil_buffer_and_return; } if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto disable_stencil_buffer_and_return; } /* We only want to clear the part of the stencil buffer * that we are about to use. It also does not hurt to * scissor around the painted clip. */ _cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip)); /* The clip is not rectangular, so use the stencil buffer. */ glDepthMask (GL_TRUE); glEnable (GL_STENCIL_TEST); /* Texture surfaces have private depth/stencil buffers, so we can * rely on any previous clip being cached there. */ if (_cairo_gl_surface_is_texture (setup->dst)) { cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer; if (_cairo_clip_equal (old_clip, setup->clip)) goto activate_stencil_buffer_and_return; if (old_clip) { _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); } setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip); } glClearStencil (0); glClear (GL_STENCIL_BUFFER_BIT); glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); glStencilFunc (GL_EQUAL, 1, 0xffffffff); glColorMask (0, 0, 0, 0); status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip); if (unlikely (status)) { glColorMask (1, 1, 1, 1); goto disable_stencil_buffer_and_return; } /* We want to only render to the stencil buffer, so draw everything now. Flushing also unbinds the VBO, which we want to rebind for regular drawing. */ _cairo_gl_composite_flush (ctx); _cairo_gl_composite_setup_vbo (ctx, vertex_size); activate_stencil_buffer_and_return: glColorMask (1, 1, 1, 1); glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc (GL_EQUAL, 1, 0xffffffff); return CAIRO_INT_STATUS_SUCCESS; disable_stencil_buffer_and_return: _disable_stencil_buffer (); return status; } static cairo_int_status_t _cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup, cairo_gl_context_t *ctx, int vertex_size) { cairo_bool_t clip_changing = TRUE; cairo_bool_t clip_region_changing = TRUE; if (! ctx->clip && ! setup->clip && ! setup->clip_region && ! ctx->clip_region) goto disable_all_clipping; clip_changing = ! _cairo_clip_equal (ctx->clip, setup->clip); clip_region_changing = ! cairo_region_equal (ctx->clip_region, setup->clip_region); if (! _cairo_gl_context_is_flushed (ctx) && (clip_region_changing || clip_changing)) _cairo_gl_composite_flush (ctx); assert (!setup->clip_region || !setup->clip); /* setup->clip is only used by the msaa compositor and setup->clip_region * only by the other compositors, so it's safe to wait to clean up obsolete * clips. */ if (clip_region_changing) { cairo_region_destroy (ctx->clip_region); ctx->clip_region = cairo_region_reference (setup->clip_region); } if (clip_changing) { _cairo_clip_destroy (ctx->clip); ctx->clip = _cairo_clip_copy (setup->clip); } /* For clip regions, we scissor right before drawing. */ if (setup->clip_region) goto disable_all_clipping; if (setup->clip) return _cairo_gl_composite_setup_painted_clipping (setup, ctx, vertex_size); disable_all_clipping: _disable_stencil_buffer (); glDisable (GL_SCISSOR_TEST); return CAIRO_INT_STATUS_SUCCESS; } cairo_status_t _cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, cairo_gl_context_t *ctx) { unsigned int dst_size, src_size, mask_size, vertex_size; cairo_status_t status; cairo_gl_shader_t *shader; cairo_bool_t component_alpha; cairo_bool_t vertex_size_changed; component_alpha = setup->mask.type == CAIRO_GL_OPERAND_TEXTURE && setup->mask.texture.attributes.has_component_alpha; /* Do various magic for component alpha */ if (component_alpha) { status = _cairo_gl_composite_begin_component_alpha (ctx, setup); if (unlikely (status)) return status; } else { if (ctx->pre_shader) { _cairo_gl_composite_flush (ctx); ctx->pre_shader = NULL; } } status = _cairo_gl_get_shader_by_type (ctx, &setup->src, &setup->mask, setup->spans, component_alpha ? CAIRO_GL_SHADER_IN_CA_SOURCE : CAIRO_GL_SHADER_IN_NORMAL, &shader); if (unlikely (status)) { ctx->pre_shader = NULL; return status; } if (ctx->current_shader != shader) _cairo_gl_composite_flush (ctx); status = CAIRO_STATUS_SUCCESS; dst_size = 2 * sizeof (GLfloat); src_size = _cairo_gl_operand_get_vertex_size (&setup->src); mask_size = _cairo_gl_operand_get_vertex_size (&setup->mask); vertex_size = dst_size + src_size + mask_size; if (setup->spans) vertex_size += sizeof (GLfloat); vertex_size_changed = _cairo_gl_composite_setup_vbo (ctx, vertex_size); _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, dst_size, vertex_size_changed); _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, dst_size + src_size, vertex_size_changed); _cairo_gl_context_setup_spans (ctx, setup->spans, vertex_size, dst_size + src_size + mask_size); _cairo_gl_set_operator (ctx, setup->op, component_alpha); if (_cairo_gl_context_is_flushed (ctx)) { if (ctx->pre_shader) { _cairo_gl_set_shader (ctx, ctx->pre_shader); _cairo_gl_composite_bind_to_shader (ctx, setup); } _cairo_gl_set_shader (ctx, shader); _cairo_gl_composite_bind_to_shader (ctx, setup); } return status; } cairo_status_t _cairo_gl_composite_begin (cairo_gl_composite_t *setup, cairo_gl_context_t **ctx_out) { cairo_gl_context_t *ctx; cairo_status_t status; assert (setup->dst); status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx); if (unlikely (status)) return status; _cairo_gl_context_set_destination (ctx, setup->dst, setup->multisample); glEnable (GL_BLEND); status = _cairo_gl_set_operands_and_operator (setup, ctx); if (unlikely (status)) goto FAIL; status = _cairo_gl_composite_setup_clipping (setup, ctx, ctx->vertex_size); if (unlikely (status)) goto FAIL; *ctx_out = ctx; FAIL: if (unlikely (status)) status = _cairo_gl_context_release (ctx, status); return status; } static inline void _cairo_gl_composite_draw_tristrip (cairo_gl_context_t *ctx) { cairo_array_t* indices = &ctx->tristrip_indices; const unsigned short *indices_array = _cairo_array_index_const (indices, 0); if (ctx->pre_shader) { cairo_gl_shader_t *prev_shader = ctx->current_shader; _cairo_gl_set_shader (ctx, ctx->pre_shader); _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); _cairo_gl_set_shader (ctx, prev_shader); _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); } glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array); _cairo_array_truncate (indices, 0); } static inline void _cairo_gl_composite_draw_triangles (cairo_gl_context_t *ctx, unsigned int count) { if (! ctx->pre_shader) { glDrawArrays (GL_TRIANGLES, 0, count); } else { cairo_gl_shader_t *prev_shader = ctx->current_shader; _cairo_gl_set_shader (ctx, ctx->pre_shader); _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE); glDrawArrays (GL_TRIANGLES, 0, count); _cairo_gl_set_shader (ctx, prev_shader); _cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE); glDrawArrays (GL_TRIANGLES, 0, count); } } static void _cairo_gl_composite_draw_triangles_with_clip_region (cairo_gl_context_t *ctx, unsigned int count) { int i, num_rectangles; if (!ctx->clip_region) { _cairo_gl_composite_draw_triangles (ctx, count); return; } num_rectangles = cairo_region_num_rectangles (ctx->clip_region); for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (ctx->clip_region, i, &rect); _cairo_gl_scissor_to_rectangle (ctx->current_target, &rect); _cairo_gl_composite_draw_triangles (ctx, count); } } static void _cairo_gl_composite_unmap_vertex_buffer (cairo_gl_context_t *ctx) { ctx->vb_offset = 0; } void _cairo_gl_composite_flush (cairo_gl_context_t *ctx) { unsigned int count; int i; if (_cairo_gl_context_is_flushed (ctx)) return; count = ctx->vb_offset / ctx->vertex_size; _cairo_gl_composite_unmap_vertex_buffer (ctx); if (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS) { _cairo_gl_composite_draw_tristrip (ctx); } else { assert (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); _cairo_gl_composite_draw_triangles_with_clip_region (ctx, count); } for (i = 0; i < ARRAY_LENGTH (&ctx->glyph_cache); i++) _cairo_gl_glyph_cache_unlock (&ctx->glyph_cache[i]); } static void _cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx, unsigned int n_vertices, cairo_gl_primitive_type_t primitive_type) { if (ctx->primitive_type != primitive_type) { _cairo_gl_composite_flush (ctx); ctx->primitive_type = primitive_type; } if (ctx->vb_offset + n_vertices * ctx->vertex_size > CAIRO_GL_VBO_SIZE) _cairo_gl_composite_flush (ctx); } static inline void _cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx, GLfloat x, GLfloat y) { GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; *vb++ = x; *vb++ = y; _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); ctx->vb_offset += ctx->vertex_size; } static inline void _cairo_gl_composite_emit_alpha_vertex (cairo_gl_context_t *ctx, GLfloat x, GLfloat y, uint8_t alpha) { GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; union fi { float f; GLbyte bytes[4]; } fi; *vb++ = x; *vb++ = y; _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y); fi.bytes[0] = 0; fi.bytes[1] = 0; fi.bytes[2] = 0; fi.bytes[3] = alpha; *vb++ = fi.f; ctx->vb_offset += ctx->vertex_size; } static void _cairo_gl_composite_emit_point (cairo_gl_context_t *ctx, const cairo_point_t *point) { _cairo_gl_composite_emit_vertex (ctx, _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)); } static void _cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) { _cairo_gl_composite_prepare_buffer (ctx, 6, CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); _cairo_gl_composite_emit_vertex (ctx, x1, y1); _cairo_gl_composite_emit_vertex (ctx, x2, y1); _cairo_gl_composite_emit_vertex (ctx, x1, y2); _cairo_gl_composite_emit_vertex (ctx, x2, y1); _cairo_gl_composite_emit_vertex (ctx, x2, y2); _cairo_gl_composite_emit_vertex (ctx, x1, y2); } cairo_gl_emit_rect_t _cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx) { return _cairo_gl_composite_emit_rect; } void _cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) { _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2); } static void _cairo_gl_composite_emit_span (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, uint8_t alpha) { _cairo_gl_composite_prepare_buffer (ctx, 6, CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y1, alpha); _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha); _cairo_gl_composite_emit_alpha_vertex (ctx, x2, y2, alpha); _cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha); } static void _cairo_gl_composite_emit_solid_span (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, uint8_t alpha) { GLfloat *v; union fi { float f; GLbyte bytes[4]; } fi; _cairo_gl_composite_prepare_buffer (ctx, 6, CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; v[15] = v[ 6] = v[0] = x1; v[10] = v[ 4] = v[1] = y1; v[12] = v[ 9] = v[3] = x2; v[16] = v[13] = v[7] = y2; fi.bytes[0] = 0; fi.bytes[1] = 0; fi.bytes[2] = 0; fi.bytes[3] = alpha; v[17] =v[14] = v[11] = v[8] = v[5] = v[2] = fi.f; ctx->vb_offset += 6*3 * sizeof(GLfloat); } cairo_gl_emit_span_t _cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx) { if (ctx->operands[CAIRO_GL_TEX_MASK].type != CAIRO_GL_OPERAND_NONE) { switch (ctx->operands[CAIRO_GL_TEX_MASK].type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: if (!ctx->operands[CAIRO_GL_TEX_MASK].gradient.texgen) return _cairo_gl_composite_emit_span; break; case CAIRO_GL_OPERAND_TEXTURE: if (!ctx->operands[CAIRO_GL_TEX_MASK].texture.texgen) return _cairo_gl_composite_emit_span; break; } } switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: if (!ctx->operands[CAIRO_GL_TEX_SOURCE].gradient.texgen) return _cairo_gl_composite_emit_span; break; case CAIRO_GL_OPERAND_TEXTURE: if (!ctx->operands[CAIRO_GL_TEX_SOURCE].texture.texgen) return _cairo_gl_composite_emit_span; } return _cairo_gl_composite_emit_solid_span; } static inline void _cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx, GLfloat x, GLfloat y, GLfloat glyph_x, GLfloat glyph_y) { GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; *vb++ = x; *vb++ = y; _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y); *vb++ = glyph_x; *vb++ = glyph_y; ctx->vb_offset += ctx->vertex_size; } static void _cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat glyph_x1, GLfloat glyph_y1, GLfloat glyph_x2, GLfloat glyph_y2) { _cairo_gl_composite_prepare_buffer (ctx, 6, CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1); _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1); _cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2); _cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2); } static void _cairo_gl_composite_emit_solid_glyph (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat glyph_x1, GLfloat glyph_y1, GLfloat glyph_x2, GLfloat glyph_y2) { GLfloat *v; _cairo_gl_composite_prepare_buffer (ctx, 6, CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES); v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset]; v[20] = v[ 8] = v[0] = x1; v[13] = v[ 5] = v[1] = y1; v[22] = v[10] = v[2] = glyph_x1; v[15] = v[ 7] = v[3] = glyph_y1; v[16] = v[12] = v[4] = x2; v[18] = v[14] = v[6] = glyph_x2; v[21] = v[17] = v[ 9] = y2; v[23] = v[19] = v[11] = glyph_y2; ctx->vb_offset += 4 * 6 * sizeof (GLfloat); } cairo_gl_emit_glyph_t _cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx) { switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: return _cairo_gl_composite_emit_solid_glyph; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: case CAIRO_GL_OPERAND_TEXTURE: return _cairo_gl_composite_emit_glyph; } } void _cairo_gl_composite_fini (cairo_gl_composite_t *setup) { _cairo_gl_operand_destroy (&setup->src); _cairo_gl_operand_destroy (&setup->mask); } cairo_status_t _cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, cairo_operator_t op, cairo_bool_t assume_component_alpha) { if (assume_component_alpha) { if (op != CAIRO_OPERATOR_CLEAR && op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD) return UNSUPPORTED ("unsupported component alpha operator"); } else { if (! _cairo_gl_operator_is_supported (op)) return UNSUPPORTED ("unsupported operator"); } setup->op = op; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gl_composite_init (cairo_gl_composite_t *setup, cairo_operator_t op, cairo_gl_surface_t *dst, cairo_bool_t assume_component_alpha) { cairo_status_t status; memset (setup, 0, sizeof (cairo_gl_composite_t)); status = _cairo_gl_composite_set_operator (setup, op, assume_component_alpha); if (status) return status; setup->dst = dst; setup->clip_region = dst->clip_region; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_gl_composite_append_vertex_indices (cairo_gl_context_t *ctx, int number_of_new_indices) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_array_t *indices = &ctx->tristrip_indices; int number_of_indices = _cairo_array_num_elements (indices); unsigned short current_vertex_index = 0; int i; assert (number_of_new_indices > 0); /* If any preexisting triangle triangle strip indices exist on this context, we insert a set of degenerate triangles from the last preexisting vertex to our first one. */ if (number_of_indices > 0) { const unsigned short *indices_array = _cairo_array_index_const (indices, 0); current_vertex_index = indices_array[number_of_indices - 1]; status = _cairo_array_append (indices, ¤t_vertex_index); if (unlikely (status)) return status; current_vertex_index++; status =_cairo_array_append (indices, ¤t_vertex_index); if (unlikely (status)) return status; } for (i = 0; i < number_of_new_indices; i++) { status = _cairo_array_append (indices, ¤t_vertex_index); current_vertex_index++; if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_point_t quad[4]) { _cairo_gl_composite_prepare_buffer (ctx, 4, CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); _cairo_gl_composite_emit_point (ctx, &quad[0]); _cairo_gl_composite_emit_point (ctx, &quad[1]); /* Cairo stores quad vertices in counter-clockwise order, but we need to emit them from top to bottom in the triangle strip, so we need to reverse the order of the last two vertices. */ _cairo_gl_composite_emit_point (ctx, &quad[3]); _cairo_gl_composite_emit_point (ctx, &quad[2]); return _cairo_gl_composite_append_vertex_indices (ctx, 4); } cairo_int_status_t _cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_point_t triangle[3]) { _cairo_gl_composite_prepare_buffer (ctx, 3, CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS); _cairo_gl_composite_emit_point (ctx, &triangle[0]); _cairo_gl_composite_emit_point (ctx, &triangle[1]); _cairo_gl_composite_emit_point (ctx, &triangle[2]); return _cairo_gl_composite_append_vertex_indices (ctx, 3); } ���������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-device.c�������������������������������0000664�0000000�0000000�00000056761�12710376503�0025415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-gl-private.h" #define MAX_MSAA_SAMPLES 4 static void _gl_lock (void *device) { cairo_gl_context_t *ctx = (cairo_gl_context_t *) device; ctx->acquire (ctx); } static void _gl_unlock (void *device) { cairo_gl_context_t *ctx = (cairo_gl_context_t *) device; ctx->release (ctx); } static cairo_status_t _gl_flush (void *device) { cairo_gl_context_t *ctx; cairo_status_t status; status = _cairo_gl_context_acquire (device, &ctx); if (unlikely (status)) return status; _cairo_gl_composite_flush (ctx); _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); if (ctx->clip_region) { cairo_region_destroy (ctx->clip_region); ctx->clip_region = NULL; } ctx->current_target = NULL; ctx->current_operator = -1; ctx->vertex_size = 0; ctx->pre_shader = NULL; _cairo_gl_set_shader (ctx, NULL); ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0); glDisable (GL_SCISSOR_TEST); glDisable (GL_BLEND); return _cairo_gl_context_release (ctx, status); } static void _gl_finish (void *device) { cairo_gl_context_t *ctx = device; int n; _gl_lock (device); _cairo_cache_fini (&ctx->gradients); _cairo_gl_context_fini_shaders (ctx); for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) _cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]); _gl_unlock (device); } static void _gl_destroy (void *device) { cairo_gl_context_t *ctx = device; ctx->acquire (ctx); while (! cairo_list_is_empty (&ctx->fonts)) { cairo_gl_font_t *font; font = cairo_list_first_entry (&ctx->fonts, cairo_gl_font_t, link); cairo_list_del (&font->base.link); cairo_list_del (&font->link); free (font); } _cairo_array_fini (&ctx->tristrip_indices); cairo_region_destroy (ctx->clip_region); _cairo_clip_destroy (ctx->clip); free (ctx->vb); ctx->destroy (ctx); free (ctx); } static const cairo_device_backend_t _cairo_gl_device_backend = { CAIRO_DEVICE_TYPE_GL, _gl_lock, _gl_unlock, _gl_flush, /* flush */ _gl_finish, _gl_destroy, }; static cairo_bool_t _cairo_gl_msaa_compositor_enabled (void) { const char *env = getenv ("CAIRO_GL_COMPOSITOR"); return env && strcmp(env, "msaa") == 0; } static cairo_bool_t test_can_read_bgra (cairo_gl_flavor_t gl_flavor) { /* Desktop GL always supports BGRA formats. */ if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) return TRUE; assert (gl_flavor == CAIRO_GL_FLAVOR_ES); /* For OpenGL ES we have to look for the specific extension and BGRA only * matches cairo's integer packed bytes on little-endian machines. */ if (!_cairo_is_little_endian()) return FALSE; return _cairo_gl_has_extension ("EXT_read_format_bgra"); } cairo_status_t _cairo_gl_context_init (cairo_gl_context_t *ctx) { cairo_status_t status; cairo_gl_dispatch_t *dispatch = &ctx->dispatch; int gl_version = _cairo_gl_get_version (); cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor (); int n; cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP; cairo_bool_t is_gles = gl_flavor == CAIRO_GL_FLAVOR_ES; _cairo_device_init (&ctx->base, &_cairo_gl_device_backend); /* XXX The choice of compositor should be made automatically at runtime. * However, it is useful to force one particular compositor whilst * testing. */ if (_cairo_gl_msaa_compositor_enabled ()) ctx->compositor = _cairo_gl_msaa_compositor_get (); else ctx->compositor = _cairo_gl_span_compositor_get (); ctx->thread_aware = TRUE; memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache)); cairo_list_init (&ctx->fonts); /* Support only GL version >= 1.3 */ if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3)) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); /* Check for required extensions */ if (is_desktop) { if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two")) { ctx->tex_target = GL_TEXTURE_2D; ctx->has_npot_repeat = TRUE; } else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle")) { ctx->tex_target = GL_TEXTURE_RECTANGLE; ctx->has_npot_repeat = FALSE; } else return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); } else { ctx->tex_target = GL_TEXTURE_2D; if (_cairo_gl_has_extension ("GL_OES_texture_npot") || _cairo_gl_has_extension ("GL_IMG_texture_npot")) ctx->has_npot_repeat = TRUE; else ctx->has_npot_repeat = FALSE; } if (is_desktop && gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) && ! _cairo_gl_has_extension ("GL_ARB_pixel_buffer_object")) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); if (is_gles && ! _cairo_gl_has_extension ("GL_EXT_texture_format_BGRA8888")) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); ctx->has_map_buffer = is_desktop || (is_gles && _cairo_gl_has_extension ("GL_OES_mapbuffer")); ctx->can_read_bgra = test_can_read_bgra (gl_flavor); ctx->has_mesa_pack_invert = _cairo_gl_has_extension ("GL_MESA_pack_invert"); ctx->has_packed_depth_stencil = (is_desktop && _cairo_gl_has_extension ("GL_EXT_packed_depth_stencil")) || (is_gles && _cairo_gl_has_extension ("GL_OES_packed_depth_stencil")); ctx->num_samples = 1; #if CAIRO_HAS_GL_SURFACE if (is_desktop && ctx->has_packed_depth_stencil && (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) || _cairo_gl_has_extension ("GL_ARB_framebuffer_object") || (_cairo_gl_has_extension ("GL_EXT_framebuffer_blit") && _cairo_gl_has_extension ("GL_EXT_framebuffer_multisample")))) { glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples); } #endif #if CAIRO_HAS_GLESV2_SURFACE && defined(GL_MAX_SAMPLES_EXT) if (is_gles && ctx->has_packed_depth_stencil && _cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) { glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples); } #endif #if CAIRO_HAS_GLESV2_SURFACE && defined(GL_MAX_SAMPLES_IMG) if (is_gles && ctx->has_packed_depth_stencil && _cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) { glGetIntegerv(GL_MAX_SAMPLES_IMG, &ctx->num_samples); } #endif ctx->supports_msaa = ctx->num_samples > 1; if (ctx->num_samples > MAX_MSAA_SAMPLES) ctx->num_samples = MAX_MSAA_SAMPLES; ctx->current_operator = -1; ctx->gl_flavor = gl_flavor; status = _cairo_gl_context_init_shaders (ctx); if (unlikely (status)) return status; status = _cairo_cache_init (&ctx->gradients, _cairo_gl_gradient_equal, NULL, (cairo_destroy_func_t) _cairo_gl_gradient_destroy, CAIRO_GL_GRADIENT_CACHE_SIZE); if (unlikely (status)) return status; ctx->vb = malloc (CAIRO_GL_VBO_SIZE); if (unlikely (ctx->vb == NULL)) { _cairo_cache_fini (&ctx->gradients); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } ctx->primitive_type = CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES; _cairo_array_init (&ctx->tristrip_indices, sizeof (unsigned short)); /* PBO for any sort of texture upload */ dispatch->GenBuffers (1, &ctx->texture_load_pbo); ctx->max_framebuffer_size = 0; glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size); ctx->max_texture_size = 0; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size); ctx->max_textures = 0; glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures); for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) _cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]); return CAIRO_STATUS_SUCCESS; } void _cairo_gl_context_activate (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit) { if (ctx->max_textures <= (GLint) tex_unit) { if (tex_unit < 2) { _cairo_gl_composite_flush (ctx); _cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1); } glActiveTexture (ctx->max_textures - 1); } else { glActiveTexture (GL_TEXTURE0 + tex_unit); } } static GLenum _get_depth_stencil_format (cairo_gl_context_t *ctx) { /* This is necessary to properly handle the situation where both OpenGL and OpenGLES are active and returning a sane default. */ #if CAIRO_HAS_GL_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) return GL_DEPTH_STENCIL; #endif #if CAIRO_HAS_GLESV2_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) return GL_DEPTH24_STENCIL8_OES; #endif #if CAIRO_HAS_GL_SURFACE return GL_DEPTH_STENCIL; #elif CAIRO_HAS_GLESV2_SURFACE return GL_DEPTH24_STENCIL8_OES; #endif } #if CAIRO_HAS_GLESV2_SURFACE static void _cairo_gl_ensure_msaa_gles_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { if (surface->msaa_active) return; ctx->dispatch.FramebufferTexture2DMultisample(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ctx->tex_target, surface->tex, 0, ctx->num_samples); /* From now on MSAA will always be active on this surface. */ surface->msaa_active = TRUE; } #endif static void _cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { GLenum status; cairo_gl_dispatch_t *dispatch = &ctx->dispatch; if (likely (surface->fb)) return; /* Create a framebuffer object wrapping the texture so that we can render * to it. */ dispatch->GenFramebuffers (1, &surface->fb); dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb); /* Unlike for desktop GL we only maintain one multisampling framebuffer for OpenGLES since the EXT_multisampled_render_to_texture extension does not require an explicit multisample resolution. */ #if CAIRO_HAS_GLESV2_SURFACE if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () && ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { _cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface); } else #endif dispatch->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ctx->tex_target, surface->tex, 0); #if CAIRO_HAS_GL_SURFACE glDrawBuffer (GL_COLOR_ATTACHMENT0); glReadBuffer (GL_COLOR_ATTACHMENT0); #endif status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { const char *str; switch (status) { //case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break; case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break; default: str = "unknown error"; break; } fprintf (stderr, "destination is framebuffer incomplete: %s [%#x]\n", str, status); } } #if CAIRO_HAS_GL_SURFACE static void _cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { assert (surface->supports_msaa); assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP); if (surface->msaa_fb) return; /* We maintain a separate framebuffer for multisampling operations. This allows us to do a fast paint to the non-multisampling framebuffer when mulitsampling is disabled. */ ctx->dispatch.GenFramebuffers (1, &surface->msaa_fb); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); ctx->dispatch.GenRenderbuffers (1, &surface->msaa_rb); ctx->dispatch.BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_rb); /* FIXME: For now we assume that textures passed from the outside have GL_RGBA format, but eventually we need to expose a way for the API consumer to pass this information. */ ctx->dispatch.RenderbufferStorageMultisample (GL_RENDERBUFFER, ctx->num_samples, GL_RGBA, surface->width, surface->height); ctx->dispatch.FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->msaa_rb); /* Cairo surfaces start out initialized to transparent (black) */ glDisable (GL_SCISSOR_TEST); glClearColor (0, 0, 0, 0); glClear (GL_COLOR_BUFFER_BIT); } #endif static cairo_bool_t _cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; if (surface->msaa_depth_stencil) return TRUE; _cairo_gl_ensure_framebuffer (ctx, surface); #if CAIRO_HAS_GL_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) _cairo_gl_ensure_multisampling (ctx, surface); #endif dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil); dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_depth_stencil); dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER, ctx->num_samples, _get_depth_stencil_format (ctx), surface->width, surface->height); #if CAIRO_HAS_GL_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, surface->msaa_depth_stencil); } #endif #if CAIRO_HAS_GLESV2_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, surface->msaa_depth_stencil); dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, surface->msaa_depth_stencil); } #endif if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil); surface->msaa_depth_stencil = 0; return FALSE; } return TRUE; } static cairo_bool_t _cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; if (surface->depth_stencil) return TRUE; _cairo_gl_ensure_framebuffer (ctx, surface); dispatch->GenRenderbuffers (1, &surface->depth_stencil); dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil); dispatch->RenderbufferStorage (GL_RENDERBUFFER, _get_depth_stencil_format (ctx), surface->width, surface->height); dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, surface->depth_stencil); dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, surface->depth_stencil); if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { dispatch->DeleteRenderbuffers (1, &surface->depth_stencil); surface->depth_stencil = 0; return FALSE; } return TRUE; } cairo_bool_t _cairo_gl_ensure_stencil (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { if (! _cairo_gl_surface_is_texture (surface)) return TRUE; /* best guess for now, will check later */ if (! ctx->has_packed_depth_stencil) return FALSE; if (surface->msaa_active) return _cairo_gl_ensure_msaa_depth_stencil_buffer (ctx, surface); else return _cairo_gl_ensure_depth_stencil_buffer (ctx, surface); } /* * Stores a parallel projection transformation in matrix 'm', * using column-major order. * * This is equivalent to: * * glLoadIdentity() * gluOrtho2D() * * The calculation for the ortho tranformation was taken from the * mesa source code. */ static void _gl_identity_ortho (GLfloat *m, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top) { #define M(row,col) m[col*4+row] M(0,0) = 2.f / (right - left); M(0,1) = 0.f; M(0,2) = 0.f; M(0,3) = -(right + left) / (right - left); M(1,0) = 0.f; M(1,1) = 2.f / (top - bottom); M(1,2) = 0.f; M(1,3) = -(top + bottom) / (top - bottom); M(2,0) = 0.f; M(2,1) = 0.f; M(2,2) = -1.f; M(2,3) = 0.f; M(3,0) = 0.f; M(3,1) = 0.f; M(3,2) = 0.f; M(3,3) = 1.f; #undef M } #if CAIRO_HAS_GL_SURFACE static void bind_multisample_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { cairo_bool_t stencil_test_enabled; cairo_bool_t scissor_test_enabled; assert (surface->supports_msaa); assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP); _cairo_gl_ensure_framebuffer (ctx, surface); _cairo_gl_ensure_multisampling (ctx, surface); if (surface->msaa_active) { glEnable (GL_MULTISAMPLE); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); return; } _cairo_gl_composite_flush (ctx); stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST); scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST); glDisable (GL_STENCIL_TEST); glDisable (GL_SCISSOR_TEST); glEnable (GL_MULTISAMPLE); /* The last time we drew to the surface, we were not using multisampling, so we need to blit from the non-multisampling framebuffer into the multisampling framebuffer. */ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->msaa_fb); ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->fb); ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height, 0, 0, surface->width, surface->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb); if (stencil_test_enabled) glEnable (GL_STENCIL_TEST); if (scissor_test_enabled) glEnable (GL_SCISSOR_TEST); } #endif #if CAIRO_HAS_GL_SURFACE static void bind_singlesample_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface) { cairo_bool_t stencil_test_enabled; cairo_bool_t scissor_test_enabled; assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP); _cairo_gl_ensure_framebuffer (ctx, surface); if (! surface->msaa_active) { glDisable (GL_MULTISAMPLE); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); return; } _cairo_gl_composite_flush (ctx); stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST); scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST); glDisable (GL_STENCIL_TEST); glDisable (GL_SCISSOR_TEST); glDisable (GL_MULTISAMPLE); /* The last time we drew to the surface, we were using multisampling, so we need to blit from the multisampling framebuffer into the non-multisampling framebuffer. */ ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb); ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb); ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height, 0, 0, surface->width, surface->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); if (stencil_test_enabled) glEnable (GL_STENCIL_TEST); if (scissor_test_enabled) glEnable (GL_SCISSOR_TEST); } #endif void _cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface, cairo_bool_t multisampling) { if (_cairo_gl_surface_is_texture (surface)) { /* OpenGL ES surfaces only have either a multisample framebuffer or a * singlesample framebuffer, so we cannot switch back and forth. */ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { _cairo_gl_ensure_framebuffer (ctx, surface); ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); return; } #if CAIRO_HAS_GL_SURFACE if (multisampling) bind_multisample_framebuffer (ctx, surface); else bind_singlesample_framebuffer (ctx, surface); #endif } else { ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0); #if CAIRO_HAS_GL_SURFACE if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { if (multisampling) glEnable (GL_MULTISAMPLE); else glDisable (GL_MULTISAMPLE); } #endif } surface->msaa_active = multisampling; } void _cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface, cairo_bool_t multisampling) { cairo_bool_t changing_surface, changing_sampling; /* The decision whether or not to use multisampling happens when * we create an OpenGL ES surface, so we can never switch modes. */ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) multisampling = surface->msaa_active; changing_surface = ctx->current_target != surface || surface->needs_update; changing_sampling = surface->msaa_active != multisampling; if (! changing_surface && ! changing_sampling) return; if (! changing_surface) { _cairo_gl_composite_flush (ctx); _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling); return; } _cairo_gl_composite_flush (ctx); ctx->current_target = surface; surface->needs_update = FALSE; if (! _cairo_gl_surface_is_texture (surface)) { ctx->make_current (ctx, surface); } _cairo_gl_context_bind_framebuffer (ctx, surface, multisampling); if (! _cairo_gl_surface_is_texture (surface)) { #if CAIRO_HAS_GL_SURFACE glDrawBuffer (GL_BACK_LEFT); glReadBuffer (GL_BACK_LEFT); #endif } glDisable (GL_DITHER); glViewport (0, 0, surface->width, surface->height); if (_cairo_gl_surface_is_texture (surface)) _gl_identity_ortho (ctx->modelviewprojection_matrix, 0, surface->width, 0, surface->height); else _gl_identity_ortho (ctx->modelviewprojection_matrix, 0, surface->width, surface->height, 0); } void cairo_gl_device_set_thread_aware (cairo_device_t *device, cairo_bool_t thread_aware) { if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return; } ((cairo_gl_context_t *) device)->thread_aware = thread_aware; } ���������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-dispatch-private.h���������������������0000664�0000000�0000000�00000012102�12710376503�0027407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #ifndef CAIRO_GL_DISPATCH_PRIVATE_H #define CAIRO_GL_DISPATCH_PRIVATE_H #include "cairo-gl-private.h" #include <stddef.h> typedef enum _cairo_gl_dispatch_name { CAIRO_GL_DISPATCH_NAME_CORE, CAIRO_GL_DISPATCH_NAME_EXT, CAIRO_GL_DISPATCH_NAME_ES, CAIRO_GL_DISPATCH_NAME_COUNT } cairo_gl_dispatch_name_t; typedef struct _cairo_gl_dispatch_entry { const char *name[CAIRO_GL_DISPATCH_NAME_COUNT]; size_t offset; } cairo_gl_dispatch_entry_t; #define DISPATCH_ENTRY_ARB(name) { { "gl"#name, "gl"#name"ARB", "gl"#name }, \ offsetof(cairo_gl_dispatch_t, name) } #define DISPATCH_ENTRY_EXT(name) { { "gl"#name, "gl"#name"EXT", "gl"#name }, \ offsetof(cairo_gl_dispatch_t, name) } #define DISPATCH_ENTRY_ARB_OES(name) { { "gl"#name, "gl"#name"ARB", "gl"#name"OES" }, \ offsetof(cairo_gl_dispatch_t, name) } #define DISPATCH_ENTRY_EXT_IMG(name) { { "gl"#name, "gl"#name"EXT", "gl"#name"IMG" }, \ offsetof(cairo_gl_dispatch_t, name) } #define DISPATCH_ENTRY_CUSTOM(name, name2) { { "gl"#name, "gl"#name2, "gl"#name }, \ offsetof(cairo_gl_dispatch_t, name)} #define DISPATCH_ENTRY_LAST { { NULL, NULL, NULL }, 0 } cairo_private cairo_gl_dispatch_entry_t dispatch_buffers_entries[] = { DISPATCH_ENTRY_ARB (GenBuffers), DISPATCH_ENTRY_ARB (BindBuffer), DISPATCH_ENTRY_ARB (BufferData), DISPATCH_ENTRY_ARB_OES (MapBuffer), DISPATCH_ENTRY_ARB_OES (UnmapBuffer), DISPATCH_ENTRY_LAST }; cairo_private cairo_gl_dispatch_entry_t dispatch_shaders_entries[] = { /* Shaders */ DISPATCH_ENTRY_CUSTOM (CreateShader, CreateShaderObjectARB), DISPATCH_ENTRY_ARB (ShaderSource), DISPATCH_ENTRY_ARB (CompileShader), DISPATCH_ENTRY_CUSTOM (GetShaderiv, GetObjectParameterivARB), DISPATCH_ENTRY_CUSTOM (GetShaderInfoLog, GetInfoLogARB), DISPATCH_ENTRY_CUSTOM (DeleteShader, DeleteObjectARB), /* Programs */ DISPATCH_ENTRY_CUSTOM (CreateProgram, CreateProgramObjectARB), DISPATCH_ENTRY_CUSTOM (AttachShader, AttachObjectARB), DISPATCH_ENTRY_CUSTOM (DeleteProgram, DeleteObjectARB), DISPATCH_ENTRY_ARB (LinkProgram), DISPATCH_ENTRY_CUSTOM (UseProgram, UseProgramObjectARB), DISPATCH_ENTRY_CUSTOM (GetProgramiv, GetObjectParameterivARB), DISPATCH_ENTRY_CUSTOM (GetProgramInfoLog, GetInfoLogARB), /* Uniforms */ DISPATCH_ENTRY_ARB (GetUniformLocation), DISPATCH_ENTRY_ARB (Uniform1f), DISPATCH_ENTRY_ARB (Uniform2f), DISPATCH_ENTRY_ARB (Uniform3f), DISPATCH_ENTRY_ARB (Uniform4f), DISPATCH_ENTRY_ARB (UniformMatrix3fv), DISPATCH_ENTRY_ARB (UniformMatrix4fv), DISPATCH_ENTRY_ARB (Uniform1i), /* Attributes */ DISPATCH_ENTRY_ARB (BindAttribLocation), DISPATCH_ENTRY_ARB (VertexAttribPointer), DISPATCH_ENTRY_ARB (EnableVertexAttribArray), DISPATCH_ENTRY_ARB (DisableVertexAttribArray), DISPATCH_ENTRY_LAST }; cairo_private cairo_gl_dispatch_entry_t dispatch_fbo_entries[] = { DISPATCH_ENTRY_EXT (GenFramebuffers), DISPATCH_ENTRY_EXT (BindFramebuffer), DISPATCH_ENTRY_EXT (FramebufferTexture2D), DISPATCH_ENTRY_EXT (CheckFramebufferStatus), DISPATCH_ENTRY_EXT (DeleteFramebuffers), DISPATCH_ENTRY_EXT (GenRenderbuffers), DISPATCH_ENTRY_EXT (BindRenderbuffer), DISPATCH_ENTRY_EXT (RenderbufferStorage), DISPATCH_ENTRY_EXT (FramebufferRenderbuffer), DISPATCH_ENTRY_EXT (DeleteRenderbuffers), DISPATCH_ENTRY_EXT (BlitFramebuffer), DISPATCH_ENTRY_LAST }; cairo_private cairo_gl_dispatch_entry_t dispatch_multisampling_entries[] = { DISPATCH_ENTRY_EXT_IMG (RenderbufferStorageMultisample), DISPATCH_ENTRY_EXT_IMG (FramebufferTexture2DMultisample), DISPATCH_ENTRY_LAST }; #endif /* CAIRO_GL_DISPATCH_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-dispatch.c�����������������������������0000664�0000000�0000000�00000017444�12710376503�0025750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-gl-dispatch-private.h" #if CAIRO_HAS_DLSYM #include <dlfcn.h> #endif #if CAIRO_HAS_DLSYM static void * _cairo_gl_dispatch_open_lib (void) { return dlopen (NULL, RTLD_LAZY); } static void _cairo_gl_dispatch_close_lib (void *handle) { dlclose (handle); } static cairo_gl_generic_func_t _cairo_gl_dispatch_get_proc_addr (void *handle, const char *name) { return (cairo_gl_generic_func_t) dlsym (handle, name); } #else static void * _cairo_gl_dispatch_open_lib (void) { return NULL; } static void _cairo_gl_dispatch_close_lib (void *handle) { return; } static cairo_gl_generic_func_t _cairo_gl_dispatch_get_proc_addr (void *handle, const char *name) { return NULL; } #endif /* CAIRO_HAS_DLSYM */ static void _cairo_gl_dispatch_init_entries (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr, cairo_gl_dispatch_entry_t *entries, cairo_gl_dispatch_name_t dispatch_name) { cairo_gl_dispatch_entry_t *entry = entries; void *handle = _cairo_gl_dispatch_open_lib (); while (entry->name[CAIRO_GL_DISPATCH_NAME_CORE] != NULL) { void *dispatch_ptr = &((char *) dispatch)[entry->offset]; const char *name = entry->name[dispatch_name]; /* * In strictly conforming EGL implementations, eglGetProcAddress() can * be used only to get extension functions, but some of the functions * we want belong to core GL(ES). If the *GetProcAddress function * provided by the context fails, try to get the address of the wanted * GL function using standard system facilities (eg dlsym() in *nix * systems). */ cairo_gl_generic_func_t func = get_proc_addr (name); if (func == NULL) func = _cairo_gl_dispatch_get_proc_addr (handle, name); *((cairo_gl_generic_func_t *) dispatch_ptr) = func; ++entry; } _cairo_gl_dispatch_close_lib (handle); } static cairo_status_t _cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr, int gl_version, cairo_gl_flavor_t gl_flavor) { cairo_gl_dispatch_name_t dispatch_name; if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { if (gl_version >= CAIRO_GL_VERSION_ENCODE (1, 5)) dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; else if (_cairo_gl_has_extension ("GL_ARB_vertex_buffer_object")) dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; else return CAIRO_STATUS_DEVICE_ERROR; } else if (gl_flavor == CAIRO_GL_FLAVOR_ES && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; } else { return CAIRO_STATUS_DEVICE_ERROR; } _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, dispatch_buffers_entries, dispatch_name); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr, int gl_version, cairo_gl_flavor_t gl_flavor) { cairo_gl_dispatch_name_t dispatch_name; if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { if (gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; else if (_cairo_gl_has_extension ("GL_ARB_shader_objects")) dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; else return CAIRO_STATUS_DEVICE_ERROR; } else if (gl_flavor == CAIRO_GL_FLAVOR_ES && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; } else { return CAIRO_STATUS_DEVICE_ERROR; } _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, dispatch_shaders_entries, dispatch_name); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr, int gl_version, cairo_gl_flavor_t gl_flavor) { cairo_gl_dispatch_name_t dispatch_name; if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) { if (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) || _cairo_gl_has_extension ("GL_ARB_framebuffer_object")) dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; else if (_cairo_gl_has_extension ("GL_EXT_framebuffer_object")) dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; else return CAIRO_STATUS_DEVICE_ERROR; } else if (gl_flavor == CAIRO_GL_FLAVOR_ES && gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0)) { dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; } else { return CAIRO_STATUS_DEVICE_ERROR; } _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, dispatch_fbo_entries, dispatch_name); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_dispatch_init_multisampling (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr, int gl_version, cairo_gl_flavor_t gl_flavor) { /* For the multisampling table, there are two GLES versions of the * extension, so we put one in the EXT slot and one in the real ES slot.*/ cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE; if (gl_flavor == CAIRO_GL_FLAVOR_ES) { if (_cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT; else if (_cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) dispatch_name = CAIRO_GL_DISPATCH_NAME_ES; } _cairo_gl_dispatch_init_entries (dispatch, get_proc_addr, dispatch_multisampling_entries, dispatch_name); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gl_dispatch_init (cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr) { cairo_status_t status; int gl_version; cairo_gl_flavor_t gl_flavor; gl_version = _cairo_gl_get_version (); gl_flavor = _cairo_gl_get_flavor (); status = _cairo_gl_dispatch_init_buffers (dispatch, get_proc_addr, gl_version, gl_flavor); if (status != CAIRO_STATUS_SUCCESS) return status; status = _cairo_gl_dispatch_init_shaders (dispatch, get_proc_addr, gl_version, gl_flavor); if (status != CAIRO_STATUS_SUCCESS) return status; status = _cairo_gl_dispatch_init_fbo (dispatch, get_proc_addr, gl_version, gl_flavor); if (status != CAIRO_STATUS_SUCCESS) return status; status = _cairo_gl_dispatch_init_multisampling (dispatch, get_proc_addr, gl_version, gl_flavor); if (status != CAIRO_STATUS_SUCCESS) return status; return CAIRO_STATUS_SUCCESS; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-ext-def-private.h����������������������0000664�0000000�0000000�00000007112�12710376503�0027151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #ifndef CAIRO_GL_EXT_DEF_PRIVATE_H #define CAIRO_GL_EXT_DEF_PRIVATE_H #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif #ifndef GL_ARRAY_BUFFER #define GL_ARRAY_BUFFER 0x8892 #endif #ifndef GL_STREAM_DRAW #define GL_STREAM_DRAW 0x88E0 #endif #ifndef GL_WRITE_ONLY #define GL_WRITE_ONLY 0x88B9 #endif #ifndef GL_PIXEL_UNPACK_BUFFER #define GL_PIXEL_UNPACK_BUFFER 0x88EC #endif #ifndef GL_FRAMEBUFFER #define GL_FRAMEBUFFER 0x8D40 #endif #ifndef GL_COLOR_ATTACHMENT0 #define GL_COLOR_ATTACHMENT0 0x8CE0 #endif #ifndef GL_FRAMEBUFFER_COMPLETE #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #endif #ifndef GL_FRAMEBUFFER_UNSUPPORTED #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #endif #ifndef GL_PACK_INVERT_MESA #define GL_PACK_INVERT_MESA 0x8758 #endif #ifndef GL_CLAMP_TO_BORDER #define GL_CLAMP_TO_BORDER 0x812D #endif #ifndef GL_BGR #define GL_BGR 0x80E0 #endif #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif #ifndef GL_RGBA8 #define GL_RGBA8 0x8058 #endif #ifndef GL_UNSIGNED_INT_8_8_8_8 #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #endif #ifndef GL_UNSIGNED_SHORT_5_6_5_REV #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #endif #ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #endif #ifndef GL_UNSIGNED_INT_8_8_8_8_REV #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #endif #ifndef GL_PACK_ROW_LENGTH #define GL_PACK_ROW_LENGTH 0x0D02 #endif #ifndef GL_UNPACK_ROW_LENGTH #define GL_UNPACK_ROW_LENGTH 0x0CF2 #endif #ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #endif #endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-glyphs.c�������������������������������0000664�0000000�0000000�00000036257�12710376503�0025462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * Copyright © 2010 Intel Corporation * Copyright © 2010 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributors: * Benjamin Otte <otte@gnome.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-compositor-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-rtree-private.h" #define GLYPH_CACHE_WIDTH 1024 #define GLYPH_CACHE_HEIGHT 1024 #define GLYPH_CACHE_MIN_SIZE 4 #define GLYPH_CACHE_MAX_SIZE 128 typedef struct _cairo_gl_glyph { cairo_rtree_node_t node; cairo_scaled_glyph_private_t base; cairo_scaled_glyph_t *glyph; cairo_gl_glyph_cache_t *cache; struct { float x, y; } p1, p2; } cairo_gl_glyph_t; static void _cairo_gl_node_destroy (cairo_rtree_node_t *node) { cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node); cairo_scaled_glyph_t *glyph; glyph = priv->glyph; if (glyph == NULL) return; if (glyph->dev_private_key == priv->cache) { glyph->dev_private = NULL; glyph->dev_private_key = NULL; } cairo_list_del (&priv->base.link); priv->glyph = NULL; } static void _cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font) { cairo_gl_glyph_t *priv = cairo_container_of (glyph_private, cairo_gl_glyph_t, base); assert (priv->glyph); _cairo_gl_node_destroy (&priv->node); /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ if (! priv->node.pinned) _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node); assert (priv->glyph == NULL); } static cairo_int_status_t _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, cairo_gl_glyph_cache_t *cache, cairo_scaled_glyph_t *scaled_glyph) { cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_gl_glyph_t *glyph_private; cairo_rtree_node_t *node = NULL; cairo_int_status_t status; int width, height; width = glyph_surface->width; if (width < GLYPH_CACHE_MIN_SIZE) width = GLYPH_CACHE_MIN_SIZE; height = glyph_surface->height; if (height < GLYPH_CACHE_MIN_SIZE) height = GLYPH_CACHE_MIN_SIZE; /* search for an available slot */ status = _cairo_rtree_insert (&cache->rtree, width, height, &node); /* search for an unlocked slot */ if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_rtree_evict_random (&cache->rtree, width, height, &node); if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_rtree_node_insert (&cache->rtree, node, width, height, &node); } } if (status) return status; /* XXX: Make sure we use the mask texture. This should work automagically somehow */ glActiveTexture (GL_TEXTURE1); status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface, 0, 0, glyph_surface->width, glyph_surface->height, node->x, node->y, FALSE); if (unlikely (status)) return status; glyph_private = (cairo_gl_glyph_t *) node; glyph_private->cache = cache; glyph_private->glyph = scaled_glyph; _cairo_scaled_glyph_attach_private (scaled_glyph, &glyph_private->base, cache, _cairo_gl_glyph_fini); scaled_glyph->dev_private = glyph_private; scaled_glyph->dev_private_key = cache; /* compute tex coords */ glyph_private->p1.x = node->x; glyph_private->p1.y = node->y; glyph_private->p2.x = node->x + glyph_surface->width; glyph_private->p2.y = node->y + glyph_surface->height; if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { glyph_private->p1.x /= GLYPH_CACHE_WIDTH; glyph_private->p2.x /= GLYPH_CACHE_WIDTH; glyph_private->p1.y /= GLYPH_CACHE_HEIGHT; glyph_private->p2.y /= GLYPH_CACHE_HEIGHT; } return CAIRO_STATUS_SUCCESS; } static cairo_gl_glyph_t * _cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, cairo_scaled_glyph_t *scaled_glyph) { return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private); } static cairo_status_t cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, cairo_format_t format, cairo_gl_glyph_cache_t **cache_out) { cairo_gl_glyph_cache_t *cache; cairo_content_t content; switch (format) { case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: cache = &ctx->glyph_cache[0]; content = CAIRO_CONTENT_COLOR_ALPHA; break; case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: cache = &ctx->glyph_cache[1]; content = CAIRO_CONTENT_ALPHA; break; default: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } if (unlikely (cache->surface == NULL)) { cairo_surface_t *surface; surface = _cairo_gl_surface_create_scratch_for_caching (ctx, content, GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT); if (unlikely (surface->status)) return surface->status; _cairo_surface_release_device_reference (surface); cache->surface = (cairo_gl_surface_t *)surface; cache->surface->operand.texture.attributes.has_component_alpha = content == CAIRO_CONTENT_COLOR_ALPHA; } *cache_out = cache; return CAIRO_STATUS_SUCCESS; } static cairo_status_t render_glyphs (cairo_gl_surface_t *dst, int dst_x, int dst_y, cairo_operator_t op, cairo_surface_t *source, cairo_composite_glyphs_info_t *info, cairo_bool_t *has_component_alpha, cairo_clip_t *clip) { cairo_format_t last_format = CAIRO_FORMAT_INVALID; cairo_gl_glyph_cache_t *cache = NULL; cairo_gl_context_t *ctx; cairo_gl_emit_glyph_t emit = NULL; cairo_gl_composite_t setup; cairo_int_status_t status; int i = 0; TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, info->extents.x, info->extents.y, info->extents.width, info->extents.height)); *has_component_alpha = FALSE; status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; status = _cairo_gl_composite_init (&setup, op, dst, TRUE); if (unlikely (status)) goto FINISH; if (source == NULL) { _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); } else { _cairo_gl_composite_set_source_operand (&setup, source_to_operand (source)); } _cairo_gl_composite_set_clip (&setup, clip); for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; cairo_gl_glyph_t *glyph; double x_offset, y_offset; double x1, x2, y1, y2; status = _cairo_scaled_glyph_lookup (info->font, info->glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto FINISH; if (scaled_glyph->surface->width == 0 || scaled_glyph->surface->height == 0) { continue; } if (scaled_glyph->surface->format != last_format) { status = cairo_gl_context_get_glyph_cache (ctx, scaled_glyph->surface->format, &cache); if (unlikely (status)) goto FINISH; last_format = scaled_glyph->surface->format; _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; /* XXX Shoot me. */ status = _cairo_gl_composite_begin (&setup, &ctx); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) goto FINISH; emit = _cairo_gl_context_choose_emit_glyph (ctx); } if (scaled_glyph->dev_private_key != cache) { cairo_scaled_glyph_private_t *priv; priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); if (priv) { scaled_glyph->dev_private_key = cache; scaled_glyph->dev_private = cairo_container_of (priv, cairo_gl_glyph_t, base); } else { status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* Cache is full, so flush existing prims and try again. */ _cairo_gl_composite_flush (ctx); _cairo_gl_glyph_cache_unlock (cache); status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); } if (unlikely (_cairo_int_status_is_error (status))) goto FINISH; } } x_offset = scaled_glyph->surface->base.device_transform.x0; y_offset = scaled_glyph->surface->base.device_transform.y0; x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); x2 = x1 + scaled_glyph->surface->width; y2 = y1 + scaled_glyph->surface->height; glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); assert (emit); emit (ctx, x1, y1, x2, y2, glyph->p1.x, glyph->p1.y, glyph->p2.x, glyph->p2.y); } status = CAIRO_STATUS_SUCCESS; FINISH: status = _cairo_gl_context_release (ctx, status); _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t render_glyphs_via_mask (cairo_gl_surface_t *dst, int dst_x, int dst_y, cairo_operator_t op, cairo_surface_t *source, cairo_composite_glyphs_info_t *info, cairo_clip_t *clip) { cairo_surface_t *mask; cairo_status_t status; cairo_bool_t has_component_alpha; TRACE ((stderr, "%s\n", __FUNCTION__)); /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ mask = cairo_gl_surface_create (dst->base.device, CAIRO_CONTENT_COLOR_ALPHA, info->extents.width, info->extents.height); if (unlikely (mask->status)) return mask->status; status = render_glyphs ((cairo_gl_surface_t *) mask, info->extents.x, info->extents.y, CAIRO_OPERATOR_ADD, NULL, info, &has_component_alpha, NULL); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_surface_pattern_t mask_pattern; cairo_surface_pattern_t source_pattern; cairo_rectangle_int_t clip_extents; mask->is_clear = FALSE; _cairo_pattern_init_for_surface (&mask_pattern, mask); mask_pattern.base.has_component_alpha = has_component_alpha; mask_pattern.base.filter = CAIRO_FILTER_NEAREST; mask_pattern.base.extend = CAIRO_EXTEND_NONE; cairo_matrix_init_translate (&mask_pattern.base.matrix, dst_x-info->extents.x, dst_y-info->extents.y); _cairo_pattern_init_for_surface (&source_pattern, source); cairo_matrix_init_translate (&source_pattern.base.matrix, dst_x-info->extents.x, dst_y-info->extents.y); clip = _cairo_clip_copy (clip); clip_extents.x = info->extents.x - dst_x; clip_extents.y = info->extents.y - dst_y; clip_extents.width = info->extents.width; clip_extents.height = info->extents.height; clip = _cairo_clip_intersect_rectangle (clip, &clip_extents); status = _cairo_surface_mask (&dst->base, op, &source_pattern.base, &mask_pattern.base, clip); _cairo_clip_destroy (clip); _cairo_pattern_fini (&mask_pattern.base); _cairo_pattern_fini (&source_pattern.base); } cairo_surface_destroy (mask); return status; } cairo_int_status_t _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs) { if (! _cairo_gl_operator_is_supported (extents->op)) return UNSUPPORTED ("unsupported operator"); /* XXX use individual masks for large glyphs? */ if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE) return UNSUPPORTED ("glyphs too large"); return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_gl_composite_glyphs_with_clip (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info, cairo_clip_t *clip) { cairo_gl_surface_t *dst = _dst; cairo_bool_t has_component_alpha; TRACE ((stderr, "%s\n", __FUNCTION__)); /* If any of the glyphs require component alpha, we have to go through * a mask, since only _cairo_gl_surface_composite() currently supports * component alpha. */ if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER && (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || info->font->options.antialias == CAIRO_ANTIALIAS_BEST)) { info->use_mask = TRUE; } if (info->use_mask) { return render_glyphs_via_mask (dst, dst_x, dst_y, op, _src, info, clip); } else { return render_glyphs (dst, dst_x, dst_y, op, _src, info, &has_component_alpha, clip); } } cairo_int_status_t _cairo_gl_composite_glyphs (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y, dst_x, dst_y, info, NULL); } void _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) { _cairo_rtree_init (&cache->rtree, GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, GLYPH_CACHE_MIN_SIZE, sizeof (cairo_gl_glyph_t), _cairo_gl_node_destroy); } void _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, cairo_gl_glyph_cache_t *cache) { _cairo_rtree_fini (&cache->rtree); cairo_surface_destroy (&cache->surface->base); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-gradient-private.h���������������������0000664�0000000�0000000�00000006323�12710376503�0027415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #ifndef CAIRO_GL_GRADIENT_PRIVATE_H #define CAIRO_GL_GRADIENT_PRIVATE_H #define GL_GLEXT_PROTOTYPES #include "cairo-cache-private.h" #include "cairo-device-private.h" #include "cairo-reference-count-private.h" #include "cairo-pattern-private.h" #include "cairo-types-private.h" #include "cairo-gl.h" #if CAIRO_HAS_GL_SURFACE #include <GL/gl.h> #include <GL/glext.h> #elif CAIRO_HAS_GLESV2_SURFACE #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #endif #define CAIRO_GL_GRADIENT_CACHE_SIZE 4096 /* XXX: Declare in a better place */ typedef struct _cairo_gl_context cairo_gl_context_t; typedef struct _cairo_gl_gradient { cairo_cache_entry_t cache_entry; cairo_reference_count_t ref_count; cairo_device_t *device; /* NB: we don't hold a reference */ GLuint tex; unsigned int n_stops; const cairo_gradient_stop_t *stops; cairo_gradient_stop_t stops_embedded[1]; } cairo_gl_gradient_t; cairo_private cairo_int_status_t _cairo_gl_gradient_create (cairo_gl_context_t *ctx, unsigned int n_stops, const cairo_gradient_stop_t *stops, cairo_gl_gradient_t **gradient_out); cairo_private_no_warn cairo_gl_gradient_t * _cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient); cairo_private void _cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient); cairo_private cairo_bool_t _cairo_gl_gradient_equal (const void *key_a, const void *key_b); #endif /* CAIRO_GL_GRADIENT_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-gradient.c�����������������������������0000664�0000000�0000000�00000025153�12710376503�0025742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-gl-gradient-private.h" #include "cairo-gl-private.h" static int _cairo_gl_gradient_sample_width (unsigned int n_stops, const cairo_gradient_stop_t *stops) { unsigned int n; int width; width = 8; for (n = 1; n < n_stops; n++) { double dx = stops[n].offset - stops[n-1].offset; double delta, max; int ramp; if (dx == 0) return 1024; /* we need to emulate an infinitely sharp step */ max = fabs (stops[n].color.red - stops[n-1].color.red); delta = fabs (stops[n].color.green - stops[n-1].color.green); if (delta > max) max = delta; delta = fabs (stops[n].color.blue - stops[n-1].color.blue); if (delta > max) max = delta; delta = fabs (stops[n].color.alpha - stops[n-1].color.alpha); if (delta > max) max = delta; ramp = 128 * max / dx; if (ramp > width) width = ramp; } return (width + 7) & -8; } static uint8_t premultiply(double c, double a) { int v = c * a * 256; return v - (v >> 8); } static uint32_t color_stop_to_pixel(const cairo_gradient_stop_t *stop) { uint8_t a, r, g, b; a = stop->color.alpha_short >> 8; r = premultiply(stop->color.red, stop->color.alpha); g = premultiply(stop->color.green, stop->color.alpha); b = premultiply(stop->color.blue, stop->color.alpha); if (_cairo_is_little_endian ()) return a << 24 | r << 16 | g << 8 | b << 0; else return a << 0 | r << 8 | g << 16 | b << 24; } static cairo_status_t _cairo_gl_gradient_render (const cairo_gl_context_t *ctx, unsigned int n_stops, const cairo_gradient_stop_t *stops, void *bytes, int width) { pixman_image_t *gradient, *image; pixman_gradient_stop_t pixman_stops_stack[32]; pixman_gradient_stop_t *pixman_stops; pixman_point_fixed_t p1, p2; unsigned int i; pixman_format_code_t gradient_pixman_format; /* * Ensure that the order of the gradient's components in memory is BGRA. * This is done so that the gradient's pixel data is always suitable for * texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE. */ if (_cairo_is_little_endian ()) gradient_pixman_format = PIXMAN_a8r8g8b8; else gradient_pixman_format = PIXMAN_b8g8r8a8; pixman_stops = pixman_stops_stack; if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) { pixman_stops = _cairo_malloc_ab (n_stops, sizeof (pixman_gradient_stop_t)); if (unlikely (pixman_stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_stops; i++) { pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset); pixman_stops[i].color.red = stops[i].color.red_short; pixman_stops[i].color.green = stops[i].color.green_short; pixman_stops[i].color.blue = stops[i].color.blue_short; pixman_stops[i].color.alpha = stops[i].color.alpha_short; } p1.x = _cairo_fixed_16_16_from_double (0.5); p1.y = 0; p2.x = _cairo_fixed_16_16_from_double (width - 0.5); p2.y = 0; gradient = pixman_image_create_linear_gradient (&p1, &p2, pixman_stops, n_stops); if (pixman_stops != pixman_stops_stack) free (pixman_stops); if (unlikely (gradient == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0); pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD); image = pixman_image_create_bits (gradient_pixman_format, width, 1, bytes, sizeof(uint32_t)*width); if (unlikely (image == NULL)) { pixman_image_unref (gradient); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pixman_image_composite32 (PIXMAN_OP_SRC, gradient, NULL, image, 0, 0, 0, 0, 0, 0, width, 1); pixman_image_unref (gradient); pixman_image_unref (image); /* We need to fudge pixel 0 to hold the left-most color stop and not * the neareset stop to the zeroth pixel centre in order to correctly * populate the border color. For completeness, do both edges. */ ((uint32_t*)bytes)[0] = color_stop_to_pixel(&stops[0]); ((uint32_t*)bytes)[width-1] = color_stop_to_pixel(&stops[n_stops-1]); return CAIRO_STATUS_SUCCESS; } static unsigned long _cairo_gl_gradient_hash (unsigned int n_stops, const cairo_gradient_stop_t *stops) { return _cairo_hash_bytes (n_stops, stops, sizeof (cairo_gradient_stop_t) * n_stops); } static cairo_gl_gradient_t * _cairo_gl_gradient_lookup (cairo_gl_context_t *ctx, unsigned long hash, unsigned int n_stops, const cairo_gradient_stop_t *stops) { cairo_gl_gradient_t lookup; lookup.cache_entry.hash = hash, lookup.n_stops = n_stops; lookup.stops = stops; return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry); } cairo_bool_t _cairo_gl_gradient_equal (const void *key_a, const void *key_b) { const cairo_gl_gradient_t *a = key_a; const cairo_gl_gradient_t *b = key_b; if (a->n_stops != b->n_stops) return FALSE; return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0; } cairo_int_status_t _cairo_gl_gradient_create (cairo_gl_context_t *ctx, unsigned int n_stops, const cairo_gradient_stop_t *stops, cairo_gl_gradient_t **gradient_out) { unsigned long hash; cairo_gl_gradient_t *gradient; cairo_status_t status; int tex_width; GLint internal_format; void *data; if ((unsigned int) ctx->max_texture_size / 2 <= n_stops) return CAIRO_INT_STATUS_UNSUPPORTED; hash = _cairo_gl_gradient_hash (n_stops, stops); gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops); if (gradient) { *gradient_out = _cairo_gl_gradient_reference (gradient); return CAIRO_STATUS_SUCCESS; } gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1)); if (gradient == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); tex_width = _cairo_gl_gradient_sample_width (n_stops, stops); if (tex_width > ctx->max_texture_size) tex_width = ctx->max_texture_size; CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2); gradient->cache_entry.hash = hash; gradient->cache_entry.size = tex_width; gradient->device = &ctx->base; gradient->n_stops = n_stops; gradient->stops = gradient->stops_embedded; memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t)); glGenTextures (1, &gradient->tex); _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); glBindTexture (ctx->tex_target, gradient->tex); data = _cairo_malloc_ab (tex_width, sizeof (uint32_t)); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup_gradient; } status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width); if (unlikely (status)) goto cleanup_data; /* * In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat' * must match 'format' in glTexImage2D. */ if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) internal_format = GL_BGRA; else internal_format = GL_RGBA; glTexImage2D (ctx->tex_target, 0, internal_format, tex_width, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, data); free (data); /* we ignore errors here and just return an uncached gradient */ if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry))) CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1); *gradient_out = gradient; return CAIRO_STATUS_SUCCESS; cleanup_data: free (data); cleanup_gradient: free (gradient); return status; } cairo_gl_gradient_t * _cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); _cairo_reference_count_inc (&gradient->ref_count); return gradient; } void _cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient) { cairo_gl_context_t *ctx; cairo_status_t ignore; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); if (! _cairo_reference_count_dec_and_test (&gradient->ref_count)) return; if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) { /* The gradient my still be active in the last operation, so flush */ _cairo_gl_composite_flush (ctx); glDeleteTextures (1, &gradient->tex); ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); } free (gradient); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-info.c���������������������������������0000664�0000000�0000000�00000005440�12710376503�0025075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #include "cairoint.h" #include "cairo-gl-private.h" int _cairo_gl_get_version (void) { int major, minor; const char *version = (const char *) glGetString (GL_VERSION); const char *dot = version == NULL ? NULL : strchr (version, '.'); const char *major_start = dot; /* Sanity check */ if (dot == NULL || dot == version || *(dot + 1) == '\0') { major = 0; minor = 0; } else { /* Find the start of the major version in the string */ while (major_start > version && *major_start != ' ') --major_start; major = strtol (major_start, NULL, 10); minor = strtol (dot + 1, NULL, 10); } return CAIRO_GL_VERSION_ENCODE (major, minor); } cairo_gl_flavor_t _cairo_gl_get_flavor (void) { const char *version = (const char *) glGetString (GL_VERSION); cairo_gl_flavor_t flavor; if (version == NULL) flavor = CAIRO_GL_FLAVOR_NONE; else if (strstr (version, "OpenGL ES") != NULL) flavor = CAIRO_GL_FLAVOR_ES; else flavor = CAIRO_GL_FLAVOR_DESKTOP; return flavor; } cairo_bool_t _cairo_gl_has_extension (const char *ext) { const char *extensions = (const char *) glGetString (GL_EXTENSIONS); size_t len = strlen (ext); const char *ext_ptr = extensions; if (unlikely (ext_ptr == NULL)) return 0; while ((ext_ptr = strstr (ext_ptr, ext)) != NULL) { if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0') break; ext_ptr += len; } return (ext_ptr != NULL); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-msaa-compositor.c����������������������0000664�0000000�0000000�00000067161�12710376503�0027267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * Copyright © 2011 Samsung Electronics * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Henry Song <hsong@sisa.samsung.com> * Martin Robinson <mrobinson@igalia.com> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-gl-private.h" #include "cairo-path-private.h" #include "cairo-traps-private.h" static cairo_bool_t can_use_msaa_compositor (cairo_gl_surface_t *surface, cairo_antialias_t antialias); static void query_surface_capabilities (cairo_gl_surface_t *surface); struct _tristrip_composite_info { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; }; static cairo_int_status_t _draw_trap (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_trapezoid_t *trap) { cairo_point_t quad[4]; quad[0].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, &trap->left.p2, trap->top); quad[0].y = trap->top; quad[1].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1, &trap->left.p2, trap->bottom); quad[1].y = trap->bottom; quad[2].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, &trap->right.p2, trap->bottom); quad[2].y = trap->bottom; quad[3].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1, &trap->right.p2, trap->top); quad[3].y = trap->top; return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); } static cairo_int_status_t _draw_traps (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_traps_t *traps) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; int i; for (i = 0; i < traps->num_traps; i++) { cairo_trapezoid_t *trap = traps->traps + i; if (unlikely ((status = _draw_trap (ctx, setup, trap)))) return status; } return status; } static cairo_int_status_t _draw_int_rect (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_rectangle_int_t *rect) { cairo_box_t box; cairo_point_t quad[4]; _cairo_box_from_rectangle (&box, rect); quad[0].x = box.p1.x; quad[0].y = box.p1.y; quad[1].x = box.p1.x; quad[1].y = box.p2.y; quad[2].x = box.p2.x; quad[2].y = box.p2.y; quad[3].x = box.p2.x; quad[3].y = box.p1.y; return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad); } static cairo_int_status_t _draw_triangle_fan (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_point_t *midpt, const cairo_point_t *points, int npoints) { int i; /* Our strategy here is to not even try to build a triangle fan, but to draw each triangle as if it was an unconnected member of a triangle strip. */ for (i = 1; i < npoints; i++) { cairo_int_status_t status; cairo_point_t triangle[3]; triangle[0] = *midpt; triangle[1] = points[i - 1]; triangle[2] = points[i]; status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _clip_to_traps (cairo_clip_t *clip, cairo_traps_t *traps) { cairo_int_status_t status; cairo_polygon_t polygon; cairo_antialias_t antialias; cairo_fill_rule_t fill_rule; _cairo_traps_init (traps); if (clip->num_boxes == 1 && clip->path == NULL) { cairo_boxes_t boxes; _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); return _cairo_traps_init_boxes (traps, &boxes); } status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); if (unlikely (status)) return status; /* We ignore the antialias mode of the clip here, since the user requested * unantialiased rendering of their path and we expect that this stencil * based rendering of the clip to be a reasonable approximation to * the intersection between that clip and the path. * * In other words, what the user expects when they try to perform * a geometric intersection between an unantialiased polygon and an * antialiased polygon is open to interpretation. And we choose the fast * option. */ _cairo_traps_init (traps); status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, fill_rule); _cairo_polygon_fini (&polygon); return status; } cairo_int_status_t _cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_clip_t *clip) { cairo_int_status_t status; cairo_traps_t traps; status = _clip_to_traps (clip, &traps); if (unlikely (status)) return status; status = _draw_traps (ctx, setup, &traps); _cairo_traps_fini (&traps); return status; } static cairo_bool_t _should_use_unbounded_surface (cairo_composite_rectangles_t *composite) { cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; cairo_rectangle_int_t *source = &composite->source; if (composite->is_bounded) return FALSE; /* This isn't just an optimization. It also detects when painting is used to paint back the unbounded surface, preventing infinite recursion. */ return ! (source->x <= 0 && source->y <= 0 && source->height + source->y >= dst->height && source->width + source->x >= dst->width); } static cairo_surface_t* _prepare_unbounded_surface (cairo_gl_surface_t *dst) { cairo_surface_t* surface = cairo_gl_surface_create (dst->base.device, dst->base.content, dst->width, dst->height); if (surface == NULL) return NULL; if (unlikely (surface->status)) { cairo_surface_destroy (surface); return NULL; } return surface; } static cairo_int_status_t _paint_back_unbounded_surface (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, cairo_surface_t *surface) { cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; cairo_int_status_t status; cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); if (unlikely (pattern->status)) { status = pattern->status; goto finish; } status = _cairo_compositor_paint (compositor, &dst->base, composite->op, pattern, composite->clip); finish: cairo_pattern_destroy (pattern); cairo_surface_destroy (surface); return status; } static cairo_bool_t can_use_msaa_compositor (cairo_gl_surface_t *surface, cairo_antialias_t antialias) { query_surface_capabilities (surface); if (! surface->supports_stencil) return FALSE; /* Multisampling OpenGL ES surfaces only maintain one multisampling framebuffer and thus must use the spans compositor to do non-antialiased rendering. */ if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES && surface->supports_msaa && antialias == CAIRO_ANTIALIAS_NONE) return FALSE; /* The MSAA compositor has a single-sample mode, so we can support non-antialiased rendering. */ if (antialias == CAIRO_ANTIALIAS_NONE) return TRUE; if (antialias == CAIRO_ANTIALIAS_FAST || antialias == CAIRO_ANTIALIAS_DEFAULT) return surface->supports_msaa; return FALSE; } static void _cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite, cairo_gl_composite_t *setup) { if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip)) return; _cairo_gl_composite_set_clip (setup, composite->clip); } /* Masking with the SOURCE operator requires two passes. In the first * pass we use the mask as the source to get: * result = (1 - ma) * dst * In the second pass we use the add operator to achieve: * result = (src * ma) + dst * Combined this produces: * result = (src * ma) + (1 - ma) * dst */ static cairo_int_status_t _cairo_gl_msaa_compositor_mask_source_operator (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_gl_composite_t setup; cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; cairo_gl_context_t *ctx = NULL; cairo_int_status_t status; cairo_clip_t *clip = composite->clip; cairo_traps_t traps; /* If we have a non-rectangular clip, we can avoid using the stencil buffer * for clipping and just draw the clip polygon. */ if (clip) { status = _clip_to_traps (clip, &traps); if (unlikely (status)) { _cairo_traps_fini (&traps); return status; } } status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_DEST_OUT, dst, FALSE /* assume_component_alpha */); if (unlikely (status)) return status; status = _cairo_gl_composite_set_source (&setup, &composite->mask_pattern.base, &composite->mask_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto finish; _cairo_gl_composite_set_multisample (&setup); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto finish; if (! clip) status = _draw_int_rect (ctx, &setup, &composite->bounded); else status = _draw_traps (ctx, &setup, &traps); if (unlikely (status)) goto finish; /* Now draw the second pass. */ status = _cairo_gl_composite_set_operator (&setup, CAIRO_OPERATOR_ADD, FALSE /* assume_component_alpha */); if (unlikely (status)) goto finish; status = _cairo_gl_composite_set_source (&setup, &composite->source_pattern.base, &composite->source_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto finish; status = _cairo_gl_composite_set_mask (&setup, &composite->mask_pattern.base, &composite->source_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto finish; status = _cairo_gl_set_operands_and_operator (&setup, ctx); if (unlikely (status)) goto finish; if (! clip) status = _draw_int_rect (ctx, &setup, &composite->bounded); else status = _draw_traps (ctx, &setup, &traps); finish: _cairo_gl_composite_fini (&setup); if (ctx) status = _cairo_gl_context_release (ctx, status); if (clip) _cairo_traps_fini (&traps); return status; } static cairo_int_status_t _cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_gl_composite_t setup; cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; cairo_gl_context_t *ctx = NULL; cairo_int_status_t status; cairo_operator_t op = composite->op; cairo_clip_t *clip = composite->clip; if (! can_use_msaa_compositor (dst, CAIRO_ANTIALIAS_DEFAULT)) return CAIRO_INT_STATUS_UNSUPPORTED; if (composite->op == CAIRO_OPERATOR_CLEAR && composite->original_mask_pattern != NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* GL compositing operators cannot properly represent a mask operation using the SOURCE compositing operator in one pass. This only matters if there actually is a mask (there isn't in a paint operation) and if the mask isn't totally opaque. */ if (op == CAIRO_OPERATOR_SOURCE && composite->original_mask_pattern != NULL && ! _cairo_pattern_is_opaque (&composite->mask_pattern.base, &composite->mask_sample_area)) { if (! _cairo_pattern_is_opaque (&composite->source_pattern.base, &composite->source_sample_area)) { return _cairo_gl_msaa_compositor_mask_source_operator (compositor, composite); } /* If the source is opaque the operation reduces to OVER. */ op = CAIRO_OPERATOR_OVER; } if (_should_use_unbounded_surface (composite)) { cairo_surface_t* surface = _prepare_unbounded_surface (dst); if (unlikely (surface == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; /* This may be a paint operation. */ if (composite->original_mask_pattern == NULL) { status = _cairo_compositor_paint (compositor, surface, CAIRO_OPERATOR_SOURCE, &composite->source_pattern.base, NULL); } else { status = _cairo_compositor_mask (compositor, surface, CAIRO_OPERATOR_SOURCE, &composite->source_pattern.base, &composite->mask_pattern.base, NULL); } if (unlikely (status)) { cairo_surface_destroy (surface); return status; } return _paint_back_unbounded_surface (compositor, composite, surface); } status = _cairo_gl_composite_init (&setup, op, dst, FALSE /* assume_component_alpha */); if (unlikely (status)) return status; status = _cairo_gl_composite_set_source (&setup, &composite->source_pattern.base, &composite->source_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto finish; if (composite->original_mask_pattern != NULL) { status = _cairo_gl_composite_set_mask (&setup, &composite->mask_pattern.base, &composite->mask_sample_area, &composite->bounded, FALSE); } if (unlikely (status)) goto finish; /* We always use multisampling here, because we do not yet have the smarts to calculate when the clip or the source requires it. */ _cairo_gl_composite_set_multisample (&setup); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto finish; if (! clip) status = _draw_int_rect (ctx, &setup, &composite->bounded); else status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip); finish: _cairo_gl_composite_fini (&setup); if (ctx) status = _cairo_gl_context_release (ctx, status); return status; } static cairo_int_status_t _cairo_gl_msaa_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { return _cairo_gl_msaa_compositor_mask (compositor, composite); } static cairo_status_t _stroke_shaper_add_triangle (void *closure, const cairo_point_t triangle[3]) { struct _tristrip_composite_info *info = closure; return _cairo_gl_composite_emit_triangle_as_tristrip (info->ctx, &info->setup, triangle); } static cairo_status_t _stroke_shaper_add_triangle_fan (void *closure, const cairo_point_t *midpoint, const cairo_point_t *points, int npoints) { struct _tristrip_composite_info *info = closure; return _draw_triangle_fan (info->ctx, &info->setup, midpoint, points, npoints); } static cairo_status_t _stroke_shaper_add_quad (void *closure, const cairo_point_t quad[4]) { struct _tristrip_composite_info *info = closure; return _cairo_gl_composite_emit_quad_as_tristrip (info->ctx, &info->setup, quad); } static cairo_int_status_t _prevent_overlapping_strokes (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm) { cairo_rectangle_int_t stroke_extents; if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_pattern_is_opaque (&composite->source_pattern.base, &composite->source_sample_area)) return CAIRO_INT_STATUS_SUCCESS; if (glIsEnabled (GL_STENCIL_TEST) == FALSE) { cairo_bool_t scissor_was_enabled; /* In case we have pending operations we have to flush before adding the stencil buffer. */ _cairo_gl_composite_flush (ctx); /* Enable the stencil buffer, even if we are not using it for clipping, so we can use it below to prevent overlapping shapes. We initialize it all to one here which represents infinite clip. */ glDepthMask (GL_TRUE); glEnable (GL_STENCIL_TEST); /* We scissor here so that we don't have to clear the entire stencil * buffer. If the scissor test is already enabled, it was enabled * for clipping. In that case, instead of calculating an intersection, * we just reuse it, and risk clearing too much. */ scissor_was_enabled = glIsEnabled (GL_SCISSOR_TEST); if (! scissor_was_enabled) { _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &stroke_extents); _cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents); } glClearStencil (1); glClear (GL_STENCIL_BUFFER_BIT); if (! scissor_was_enabled) glDisable (GL_SCISSOR_TEST); glStencilFunc (GL_EQUAL, 1, 1); } /* This means that once we draw to a particular pixel nothing else can be drawn there until the stencil buffer is reset or the stencil test is disabled. */ glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO); _cairo_clip_destroy (setup->dst->clip_on_stencil_buffer); setup->dst->clip_on_stencil_buffer = NULL; return CAIRO_INT_STATUS_SUCCESS; } static void query_surface_capabilities (cairo_gl_surface_t *surface) { GLint samples, stencil_bits; cairo_gl_context_t *ctx; cairo_int_status_t status; /* Texture surfaces are create in such a way that they always have stencil and multisample bits if possible, so we don't need to query their capabilities lazily. */ if (_cairo_gl_surface_is_texture (surface)) return; if (surface->stencil_and_msaa_caps_initialized) return; surface->stencil_and_msaa_caps_initialized = TRUE; surface->supports_stencil = FALSE; surface->supports_msaa = FALSE; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return; _cairo_gl_context_set_destination (ctx, surface, FALSE); glGetIntegerv(GL_SAMPLES, &samples); glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); surface->supports_stencil = stencil_bits > 0; surface->supports_msaa = samples > 1; status = _cairo_gl_context_release (ctx, status); } static cairo_int_status_t _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_int_status_t status; cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; struct _tristrip_composite_info info; if (! can_use_msaa_compositor (dst, antialias)) return CAIRO_INT_STATUS_UNSUPPORTED; if (composite->is_bounded == FALSE) { cairo_surface_t* surface = _prepare_unbounded_surface (dst); if (unlikely (surface == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_stroke (compositor, surface, CAIRO_OPERATOR_SOURCE, &composite->source_pattern.base, path, style, ctm, ctm_inverse, tolerance, antialias, NULL); if (unlikely (status)) { cairo_surface_destroy (surface); return status; } return _paint_back_unbounded_surface (compositor, composite, surface); } status = _cairo_gl_composite_init (&info.setup, composite->op, dst, FALSE /* assume_component_alpha */); if (unlikely (status)) return status; info.ctx = NULL; status = _cairo_gl_composite_set_source (&info.setup, &composite->source_pattern.base, &composite->source_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto finish; _cairo_gl_msaa_compositor_set_clip (composite, &info.setup); if (antialias != CAIRO_ANTIALIAS_NONE) _cairo_gl_composite_set_multisample (&info.setup); status = _cairo_gl_composite_begin (&info.setup, &info.ctx); if (unlikely (status)) goto finish; status = _prevent_overlapping_strokes (info.ctx, &info.setup, composite, path, style, ctm); if (unlikely (status)) goto finish; status = _cairo_path_fixed_stroke_to_shaper ((cairo_path_fixed_t *) path, style, ctm, ctm_inverse, tolerance, _stroke_shaper_add_triangle, _stroke_shaper_add_triangle_fan, _stroke_shaper_add_quad, &info); if (unlikely (status)) goto finish; finish: _cairo_gl_composite_fini (&info.setup); if (info.ctx) status = _cairo_gl_context_release (info.ctx, status); return status; } static cairo_int_status_t _draw_simple_quad_path (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_path_fixed_t *path) { cairo_point_t triangle[3]; cairo_int_status_t status; const cairo_point_t *points; points = cairo_path_head (path)->points; triangle[0] = points[0]; triangle[1] = points[1]; triangle[2] = points[2]; status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); if (status) return status; triangle[0] = points[2]; triangle[1] = points[3]; triangle[2] = points[0]; return _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle); } static cairo_int_status_t _cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_gl_composite_t setup; cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; cairo_gl_context_t *ctx = NULL; cairo_int_status_t status; cairo_traps_t traps; cairo_bool_t draw_path_with_traps; if (! can_use_msaa_compositor (dst, antialias)) return CAIRO_INT_STATUS_UNSUPPORTED; if (composite->is_bounded == FALSE) { cairo_surface_t* surface = _prepare_unbounded_surface (dst); if (unlikely (surface == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_fill (compositor, surface, CAIRO_OPERATOR_SOURCE, &composite->source_pattern.base, path, fill_rule, tolerance, antialias, NULL); if (unlikely (status)) { cairo_surface_destroy (surface); return status; } return _paint_back_unbounded_surface (compositor, composite, surface); } draw_path_with_traps = ! _cairo_path_fixed_is_simple_quad (path); if (draw_path_with_traps) { _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps); if (unlikely (status)) goto cleanup_traps; } status = _cairo_gl_composite_init (&setup, composite->op, dst, FALSE /* assume_component_alpha */); if (unlikely (status)) goto cleanup_traps; status = _cairo_gl_composite_set_source (&setup, &composite->source_pattern.base, &composite->source_sample_area, &composite->bounded, FALSE); if (unlikely (status)) goto cleanup_setup; _cairo_gl_msaa_compositor_set_clip (composite, &setup); if (antialias != CAIRO_ANTIALIAS_NONE) _cairo_gl_composite_set_multisample (&setup); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto cleanup_setup; if (! draw_path_with_traps) status = _draw_simple_quad_path (ctx, &setup, path); else status = _draw_traps (ctx, &setup, &traps); if (unlikely (status)) goto cleanup_setup; cleanup_setup: _cairo_gl_composite_fini (&setup); if (ctx) status = _cairo_gl_context_release (ctx, status); cleanup_traps: if (draw_path_with_traps) _cairo_traps_fini (&traps); return status; } static cairo_int_status_t _cairo_gl_msaa_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_int_status_t status; cairo_surface_t *src = NULL; int src_x, src_y; cairo_composite_glyphs_info_t info; cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface; query_surface_capabilities (dst); if (! dst->supports_stencil) return CAIRO_INT_STATUS_UNSUPPORTED; if (composite->op == CAIRO_OPERATOR_CLEAR) return CAIRO_INT_STATUS_UNSUPPORTED; if (composite->is_bounded == FALSE) { cairo_surface_t* surface = _prepare_unbounded_surface (dst); if (unlikely (surface == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_glyphs (compositor, surface, CAIRO_OPERATOR_SOURCE, &composite->source_pattern.base, glyphs, num_glyphs, scaled_font, composite->clip); if (unlikely (status)) { cairo_surface_destroy (surface); return status; } return _paint_back_unbounded_surface (compositor, composite, surface); } src = _cairo_gl_pattern_to_source (&dst->base, &composite->source_pattern.base, FALSE, &composite->bounded, &composite->source_sample_area, &src_x, &src_y); if (unlikely (src->status)) { status = src->status; goto finish; } status = _cairo_gl_check_composite_glyphs (composite, scaled_font, glyphs, &num_glyphs); if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) goto finish; info.font = scaled_font; info.glyphs = glyphs; info.num_glyphs = num_glyphs; info.use_mask = overlap || ! composite->is_bounded || composite->op == CAIRO_OPERATOR_SOURCE; info.extents = composite->bounded; _cairo_scaled_font_freeze_cache (scaled_font); status = _cairo_gl_composite_glyphs_with_clip (dst, composite->op, src, src_x, src_y, 0, 0, &info, composite->clip); _cairo_scaled_font_thaw_cache (scaled_font); finish: if (src) cairo_surface_destroy (src); return status; } static void _cairo_gl_msaa_compositor_init (cairo_compositor_t *compositor, const cairo_compositor_t *delegate) { compositor->delegate = delegate; compositor->paint = _cairo_gl_msaa_compositor_paint; compositor->mask = _cairo_gl_msaa_compositor_mask; compositor->fill = _cairo_gl_msaa_compositor_fill; compositor->stroke = _cairo_gl_msaa_compositor_stroke; compositor->glyphs = _cairo_gl_msaa_compositor_glyphs; } const cairo_compositor_t * _cairo_gl_msaa_compositor_get (void) { static cairo_compositor_t compositor; if (compositor.delegate == NULL) _cairo_gl_msaa_compositor_init (&compositor, _cairo_gl_span_compositor_get ()); return &compositor; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-operand.c������������������������������0000664�0000000�0000000�00000060405�12710376503�0025574�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-subsurface-inline.h" static cairo_int_status_t _cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst, const cairo_gradient_pattern_t *pattern, cairo_gl_gradient_t **gradient) { cairo_gl_context_t *ctx; cairo_status_t status; status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient); return _cairo_gl_context_release (ctx, status); } static cairo_status_t _cairo_gl_subsurface_clone_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *_src, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; cairo_surface_pattern_t local_pattern; cairo_surface_subsurface_t *sub; cairo_gl_surface_t *surface; cairo_gl_context_t *ctx; cairo_surface_attributes_t *attributes; cairo_status_t status; sub = (cairo_surface_subsurface_t *) src->surface; if (sub->snapshot && sub->snapshot->type == CAIRO_SURFACE_TYPE_GL && sub->snapshot->device == dst->base.device) { surface = (cairo_gl_surface_t *) cairo_surface_reference (sub->snapshot); } else { status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; /* XXX Trim surface to the sample area within the subsurface? */ surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_scratch (ctx, sub->target->content, sub->extents.width, sub->extents.height); if (surface->base.status) return _cairo_gl_context_release (ctx, surface->base.status); _cairo_pattern_init_for_surface (&local_pattern, sub->target); cairo_matrix_init_translate (&local_pattern.base.matrix, sub->extents.x, sub->extents.y); local_pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (&surface->base, CAIRO_OPERATOR_SOURCE, &local_pattern.base, NULL); _cairo_pattern_fini (&local_pattern.base); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return status; } _cairo_surface_subsurface_set_snapshot (&sub->base, &surface->base); } status = _cairo_gl_surface_resolve_multisampling (surface); if (unlikely (status)) return status; attributes = &operand->texture.attributes; operand->type = CAIRO_GL_OPERAND_TEXTURE; operand->texture.surface = surface; operand->texture.owns_surface = surface; operand->texture.tex = surface->tex; if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) { attributes->matrix = src->base.matrix; } else { cairo_matrix_t m; cairo_matrix_init_scale (&m, 1.0 / surface->width, 1.0 / surface->height); cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &m); } attributes->extend = src->base.extend; attributes->filter = src->base.filter; attributes->has_component_alpha = src->base.has_component_alpha; operand->texture.texgen = use_texgen; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_subsurface_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *_src, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; cairo_surface_subsurface_t *sub; cairo_gl_surface_t *surface; cairo_surface_attributes_t *attributes; cairo_int_status_t status; sub = (cairo_surface_subsurface_t *) src->surface; if (sample->x < 0 || sample->y < 0 || sample->x + sample->width > sub->extents.width || sample->y + sample->height > sub->extents.height) { return _cairo_gl_subsurface_clone_operand_init (operand, _src, dst, sample, extents, use_texgen); } surface = (cairo_gl_surface_t *) sub->target; if (surface->base.device && surface->base.device != dst->base.device) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_gl_surface_is_texture (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_surface_resolve_multisampling (surface); if (unlikely (status)) return status; /* Translate the matrix from * (unnormalized src -> unnormalized src) to * (unnormalized dst -> unnormalized src) */ _cairo_gl_operand_copy(operand, &surface->operand); attributes = &operand->texture.attributes; attributes->matrix = src->base.matrix; attributes->matrix.x0 += sub->extents.x; attributes->matrix.y0 += sub->extents.y; cairo_matrix_multiply (&attributes->matrix, &attributes->matrix, &surface->operand.texture.attributes.matrix); attributes->extend = src->base.extend; attributes->filter = src->base.filter; attributes->has_component_alpha = src->base.has_component_alpha; operand->texture.texgen = use_texgen; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_surface_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *_src, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src; cairo_gl_surface_t *surface; cairo_surface_attributes_t *attributes; cairo_int_status_t status; surface = (cairo_gl_surface_t *) src->surface; if (surface->base.type != CAIRO_SURFACE_TYPE_GL) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) { if (_cairo_surface_is_subsurface (&surface->base)) return _cairo_gl_subsurface_operand_init (operand, _src, dst, sample, extents, use_texgen); return CAIRO_INT_STATUS_UNSUPPORTED; } if (surface->base.device && surface->base.device != dst->base.device) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->base.device && ! _cairo_gl_surface_is_texture (surface)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_surface_resolve_multisampling (surface); if (unlikely (status)) return status; _cairo_gl_operand_copy(operand, &surface->operand); attributes = &operand->texture.attributes; cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &attributes->matrix); attributes->extend = src->base.extend; attributes->filter = src->base.filter; attributes->has_component_alpha = src->base.has_component_alpha; operand->texture.texgen = use_texgen; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand, const cairo_pattern_t *_src, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *extents) { cairo_status_t status; cairo_gl_surface_t *surface; cairo_gl_context_t *ctx; cairo_image_surface_t *image; cairo_bool_t src_is_gl_surface = FALSE; cairo_rectangle_int_t map_extents; if (_src->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t* src_surface = ((cairo_surface_pattern_t *) _src)->surface; src_is_gl_surface = src_surface->type == CAIRO_SURFACE_TYPE_GL; } status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_scratch (ctx, CAIRO_CONTENT_COLOR_ALPHA, extents->width, extents->height); map_extents = *extents; map_extents.x = map_extents.y = 0; image = _cairo_surface_map_to_image (&surface->base, &map_extents); /* If the pattern is a GL surface, it belongs to some other GL context, so we need to release this device while we paint it to the image. */ if (src_is_gl_surface) { status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) goto fail; } status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y, CAIRO_OPERATOR_SOURCE, _src, NULL); if (src_is_gl_surface) { status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) goto fail; } status = _cairo_surface_unmap_image (&surface->base, image); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) goto fail; *operand = surface->operand; operand->texture.owns_surface = surface; operand->texture.attributes.matrix.x0 -= extents->x * operand->texture.attributes.matrix.xx; operand->texture.attributes.matrix.y0 -= extents->y * operand->texture.attributes.matrix.yy; return CAIRO_STATUS_SUCCESS; fail: cairo_surface_destroy (&surface->base); return status; } void _cairo_gl_solid_operand_init (cairo_gl_operand_t *operand, const cairo_color_t *color) { operand->type = CAIRO_GL_OPERAND_CONSTANT; operand->constant.color[0] = color->red * color->alpha; operand->constant.color[1] = color->green * color->alpha; operand->constant.color[2] = color->blue * color->alpha; operand->constant.color[3] = color->alpha; } void _cairo_gl_operand_translate (cairo_gl_operand_t *operand, double tx, double ty) { switch (operand->type) { case CAIRO_GL_OPERAND_TEXTURE: operand->texture.attributes.matrix.x0 -= tx * operand->texture.attributes.matrix.xx; operand->texture.attributes.matrix.y0 -= ty * operand->texture.attributes.matrix.yy; break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: operand->gradient.m.x0 -= tx * operand->gradient.m.xx; operand->gradient.m.y0 -= ty * operand->gradient.m.yy; break; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: case CAIRO_GL_OPERAND_COUNT: default: break; } } static cairo_status_t _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *pattern, cairo_gl_surface_t *dst, cairo_bool_t use_texgen) { const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern; cairo_status_t status; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (! _cairo_gl_device_has_glsl (dst->base.device)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_create_gradient_texture (dst, gradient, &operand->gradient.gradient); if (unlikely (status)) return status; if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; double x0, y0, dx, dy, sf, offset; dx = linear->pd2.x - linear->pd1.x; dy = linear->pd2.y - linear->pd1.y; sf = 1.0 / (dx * dx + dy * dy); dx *= sf; dy *= sf; x0 = linear->pd1.x; y0 = linear->pd1.y; offset = dx * x0 + dy * y0; operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT; cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0); if (! _cairo_matrix_is_identity (&pattern->matrix)) { cairo_matrix_multiply (&operand->gradient.m, &pattern->matrix, &operand->gradient.m); } } else { cairo_matrix_t m; cairo_circle_double_t circles[2]; double x0, y0, r0, dx, dy, dr; /* * Some fragment shader implementations use half-floats to * represent numbers, so the maximum number they can represent * is about 2^14. Some intermediate computations used in the * radial gradient shaders can produce results of up to 2*k^4. * Setting k=8 makes the maximum result about 8192 (assuming * that the extreme circles are not much smaller than the * destination image). */ _cairo_gradient_pattern_fit_to_range (gradient, 8., &operand->gradient.m, circles); x0 = circles[0].center.x; y0 = circles[0].center.y; r0 = circles[0].radius; dx = circles[1].center.x - x0; dy = circles[1].center.y - y0; dr = circles[1].radius - r0; operand->gradient.a = dx * dx + dy * dy - dr * dr; operand->gradient.radius_0 = r0; operand->gradient.circle_d.center.x = dx; operand->gradient.circle_d.center.y = dy; operand->gradient.circle_d.radius = dr; if (operand->gradient.a == 0) operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0; else if (pattern->extend == CAIRO_EXTEND_NONE) operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE; else operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT; cairo_matrix_init_translate (&m, -x0, -y0); cairo_matrix_multiply (&operand->gradient.m, &operand->gradient.m, &m); } operand->gradient.extend = pattern->extend; operand->gradient.texgen = use_texgen; return CAIRO_STATUS_SUCCESS; } void _cairo_gl_operand_copy (cairo_gl_operand_t *dst, const cairo_gl_operand_t *src) { *dst = *src; switch (dst->type) { case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_gradient_reference (dst->gradient.gradient); break; case CAIRO_GL_OPERAND_TEXTURE: cairo_surface_reference (&dst->texture.owns_surface->base); break; default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; } } void _cairo_gl_operand_destroy (cairo_gl_operand_t *operand) { switch (operand->type) { case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_gradient_destroy (operand->gradient.gradient); break; case CAIRO_GL_OPERAND_TEXTURE: cairo_surface_destroy (&operand->texture.owns_surface->base); break; default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; } operand->type = CAIRO_GL_OPERAND_NONE; } cairo_int_status_t _cairo_gl_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *pattern, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen) { cairo_int_status_t status; TRACE ((stderr, "%s: type=%d\n", __FUNCTION__, pattern->type)); switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: _cairo_gl_solid_operand_init (operand, &((cairo_solid_pattern_t *) pattern)->color); return CAIRO_STATUS_SUCCESS; case CAIRO_PATTERN_TYPE_SURFACE: status = _cairo_gl_surface_operand_init (operand, pattern, dst, sample, extents, use_texgen); if (status == CAIRO_INT_STATUS_UNSUPPORTED) break; return status; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_gl_gradient_operand_init (operand, pattern, dst, use_texgen); if (status == CAIRO_INT_STATUS_UNSUPPORTED) break; return status; default: case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: break; } return _cairo_gl_pattern_texture_setup (operand, pattern, dst, extents); } cairo_filter_t _cairo_gl_operand_get_filter (cairo_gl_operand_t *operand) { cairo_filter_t filter; switch ((int) operand->type) { case CAIRO_GL_OPERAND_TEXTURE: filter = operand->texture.attributes.filter; break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: filter = CAIRO_FILTER_BILINEAR; break; default: filter = CAIRO_FILTER_DEFAULT; break; } return filter; } GLint _cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand) { cairo_filter_t filter = _cairo_gl_operand_get_filter (operand); return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ? GL_LINEAR : GL_NEAREST; } cairo_extend_t _cairo_gl_operand_get_extend (cairo_gl_operand_t *operand) { cairo_extend_t extend; switch ((int) operand->type) { case CAIRO_GL_OPERAND_TEXTURE: extend = operand->texture.attributes.extend; break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: extend = operand->gradient.extend; break; default: extend = CAIRO_EXTEND_NONE; break; } return extend; } void _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, cairo_gl_operand_t *operand, cairo_gl_tex_t tex_unit) { const cairo_matrix_t *texgen = NULL; switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: return; case CAIRO_GL_OPERAND_CONSTANT: _cairo_gl_shader_bind_vec4 (ctx, ctx->current_shader->constant_location[tex_unit], operand->constant.color[0], operand->constant.color[1], operand->constant.color[2], operand->constant.color[3]); return; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_shader_bind_float (ctx, ctx->current_shader->a_location[tex_unit], operand->gradient.a); /* fall through */ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: _cairo_gl_shader_bind_vec3 (ctx, ctx->current_shader->circle_d_location[tex_unit], operand->gradient.circle_d.center.x, operand->gradient.circle_d.center.y, operand->gradient.circle_d.radius); _cairo_gl_shader_bind_float (ctx, ctx->current_shader->radius_0_location[tex_unit], operand->gradient.radius_0); /* fall through */ case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_TEXTURE: /* * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled, * these shaders need the texture dimensions for their calculations. */ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE && _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR) { float width, height; if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { width = operand->texture.surface->width; height = operand->texture.surface->height; } else { width = operand->gradient.gradient->cache_entry.size, height = 1; } _cairo_gl_shader_bind_vec2 (ctx, ctx->current_shader->texdims_location[tex_unit], width, height); } break; } if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { if (operand->texture.texgen) texgen = &operand->texture.attributes.matrix; } else { if (operand->gradient.texgen) texgen = &operand->gradient.m; } if (texgen) { _cairo_gl_shader_bind_matrix(ctx, ctx->current_shader->texgen_location[tex_unit], texgen); } } cairo_bool_t _cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest, cairo_gl_operand_t *source, unsigned int vertex_offset) { if (dest->type != source->type) return TRUE; if (dest->vertex_offset != vertex_offset) return TRUE; switch (source->type) { case CAIRO_GL_OPERAND_NONE: return FALSE; case CAIRO_GL_OPERAND_CONSTANT: return dest->constant.color[0] != source->constant.color[0] || dest->constant.color[1] != source->constant.color[1] || dest->constant.color[2] != source->constant.color[2] || dest->constant.color[3] != source->constant.color[3]; case CAIRO_GL_OPERAND_TEXTURE: return dest->texture.surface != source->texture.surface || dest->texture.attributes.extend != source->texture.attributes.extend || dest->texture.attributes.filter != source->texture.attributes.filter || dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: /* XXX: improve this */ return TRUE; default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; break; } return TRUE; } unsigned int _cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand) { switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: return 0; case CAIRO_GL_OPERAND_TEXTURE: return operand->texture.texgen ? 0 : 2 * sizeof (GLfloat); case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: return operand->gradient.texgen ? 0 : 2 * sizeof (GLfloat); } } void _cairo_gl_operand_emit (cairo_gl_operand_t *operand, GLfloat ** vb, GLfloat x, GLfloat y) { switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: if (! operand->gradient.texgen) { double s = x; double t = y; cairo_matrix_transform_point (&operand->gradient.m, &s, &t); *(*vb)++ = s; *(*vb)++ = t; } break; case CAIRO_GL_OPERAND_TEXTURE: if (! operand->texture.texgen) { cairo_surface_attributes_t *src_attributes = &operand->texture.attributes; double s = x; double t = y; cairo_matrix_transform_point (&src_attributes->matrix, &s, &t); *(*vb)++ = s; *(*vb)++ = t; } break; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-private.h������������������������������0000664�0000000�0000000�00000062741�12710376503�0025630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2011 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> * T. Zachary Laine <whatwasthataddress@gmail.com> * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #ifndef CAIRO_GL_PRIVATE_H #define CAIRO_GL_PRIVATE_H #define GL_GLEXT_PROTOTYPES #include "cairoint.h" #include "cairo-gl.h" #include "cairo-gl-gradient-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #include "cairo-rtree-private.h" #include "cairo-scaled-font-private.h" #include "cairo-spans-compositor-private.h" #include "cairo-array-private.h" #include <assert.h> #if CAIRO_HAS_GL_SURFACE #include <GL/gl.h> #include <GL/glext.h> #elif CAIRO_HAS_GLESV2_SURFACE #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #endif #include "cairo-gl-ext-def-private.h" #define DEBUG_GL 0 #if DEBUG_GL && __GNUC__ #define UNSUPPORTED(reason) ({ \ fprintf (stderr, \ "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ __FUNCTION__, __LINE__, reason); \ CAIRO_INT_STATUS_UNSUPPORTED; \ }) #else #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED #endif #define CAIRO_GL_VERSION_ENCODE(major, minor) ( \ ((major) * 256) \ + ((minor) * 1)) /* maximal number of shaders we keep in the cache. * Random number that is hopefully big enough to not cause many cache evictions. */ #define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 /* VBO size that we allocate, smaller size means we gotta flush more often, * but larger means hogging more memory and can cause trouble for drivers * (especially on embedded devices). */ #define CAIRO_GL_VBO_SIZE (16*1024) typedef struct _cairo_gl_surface cairo_gl_surface_t; /* GL flavor */ typedef enum cairo_gl_flavor { CAIRO_GL_FLAVOR_NONE = 0, CAIRO_GL_FLAVOR_DESKTOP = 1, CAIRO_GL_FLAVOR_ES = 2 } cairo_gl_flavor_t; /* Indices for vertex attributes used by BindAttribLocation etc */ enum { CAIRO_GL_VERTEX_ATTRIB_INDEX = 0, CAIRO_GL_COLOR_ATTRIB_INDEX = 1, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 2, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + 1 }; typedef enum cairo_gl_operand_type { CAIRO_GL_OPERAND_NONE, CAIRO_GL_OPERAND_CONSTANT, CAIRO_GL_OPERAND_TEXTURE, CAIRO_GL_OPERAND_LINEAR_GRADIENT, CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0, CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE, CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT, CAIRO_GL_OPERAND_COUNT } cairo_gl_operand_type_t; /* This union structure describes a potential source or mask operand to the * compositing equation. */ typedef struct cairo_gl_operand { cairo_gl_operand_type_t type; union { struct { GLuint tex; cairo_gl_surface_t *surface; cairo_gl_surface_t *owns_surface; cairo_surface_attributes_t attributes; int texgen; } texture; struct { GLfloat color[4]; } constant; struct { cairo_gl_gradient_t *gradient; cairo_matrix_t m; cairo_circle_double_t circle_d; double radius_0, a; cairo_extend_t extend; int texgen; } gradient; }; unsigned int vertex_offset; } cairo_gl_operand_t; typedef struct cairo_gl_source { cairo_surface_t base; cairo_gl_operand_t operand; } cairo_gl_source_t; struct _cairo_gl_surface { cairo_surface_t base; cairo_gl_operand_t operand; int width, height; GLuint tex; /* GL texture object containing our data. */ GLuint fb; /* GL framebuffer object wrapping our data. */ GLuint depth_stencil; /* GL renderbuffer object for holding stencil buffer clip. */ #if CAIRO_HAS_GL_SURFACE GLuint msaa_rb; /* The ARB MSAA path uses a renderbuffer. */ GLuint msaa_fb; #endif GLuint msaa_depth_stencil; cairo_bool_t stencil_and_msaa_caps_initialized; cairo_bool_t supports_stencil; /* Stencil support for for non-texture surfaces. */ cairo_bool_t supports_msaa; cairo_bool_t msaa_active; /* Whether the multisampling framebuffer is active or not. */ cairo_clip_t *clip_on_stencil_buffer; int owns_tex; cairo_bool_t needs_update; cairo_region_t *clip_region; }; typedef struct cairo_gl_glyph_cache { cairo_rtree_t rtree; cairo_gl_surface_t *surface; } cairo_gl_glyph_cache_t; typedef enum cairo_gl_tex { CAIRO_GL_TEX_SOURCE = 0, CAIRO_GL_TEX_MASK = 1, CAIRO_GL_TEX_TEMP = 2 } cairo_gl_tex_t; typedef struct cairo_gl_shader { GLuint fragment_shader; GLuint program; GLint mvp_location; GLint constant_location[2]; GLint a_location[2]; GLint circle_d_location[2]; GLint radius_0_location[2]; GLint texdims_location[2]; GLint texgen_location[2]; } cairo_gl_shader_t; typedef enum cairo_gl_shader_in { CAIRO_GL_SHADER_IN_NORMAL, CAIRO_GL_SHADER_IN_CA_SOURCE, CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, CAIRO_GL_SHADER_IN_COUNT } cairo_gl_shader_in_t; typedef enum cairo_gl_var_type { CAIRO_GL_VAR_NONE, CAIRO_GL_VAR_TEXCOORDS, CAIRO_GL_VAR_TEXGEN, } cairo_gl_var_type_t; typedef enum cairo_gl_primitive_type { CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES, CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS } cairo_gl_primitive_type_t; typedef void (*cairo_gl_emit_rect_t) (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); typedef void (*cairo_gl_emit_span_t) (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, uint8_t alpha); typedef void (*cairo_gl_emit_glyph_t) (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat glyph_x1, GLfloat glyph_y1, GLfloat glyph_x2, GLfloat glyph_y2); #define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 5) | ((mask) << 3 | (src << 1) | (dest)) #define CAIRO_GL_VAR_TYPE_MAX (1 << 6) typedef void (*cairo_gl_generic_func_t)(void); typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(const char *procname); typedef struct _cairo_gl_dispatch { /* Buffers */ void (*GenBuffers) (GLsizei n, GLuint *buffers); void (*BindBuffer) (GLenum target, GLuint buffer); void (*BufferData) (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); GLvoid *(*MapBuffer) (GLenum target, GLenum access); GLboolean (*UnmapBuffer) (GLenum target); /* Shaders */ GLuint (*CreateShader) (GLenum type); void (*ShaderSource) (GLuint shader, GLsizei count, const GLchar** string, const GLint* length); void (*CompileShader) (GLuint shader); void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params); void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); void (*DeleteShader) (GLuint shader); /* Programs */ GLuint (*CreateProgram) (void); void (*AttachShader) (GLuint program, GLuint shader); void (*DeleteProgram) (GLuint program); void (*LinkProgram) (GLuint program); void (*UseProgram) (GLuint program); void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params); void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); /* Uniforms */ GLint (*GetUniformLocation) (GLuint program, const GLchar* name); void (*Uniform1f) (GLint location, GLfloat x); void (*Uniform2f) (GLint location, GLfloat x, GLfloat y); void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z); void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); void (*UniformMatrix3fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void (*UniformMatrix4fv) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void (*Uniform1i) (GLint location, GLint x); /* Attributes */ void (*BindAttribLocation) (GLuint program, GLuint index, const GLchar *name); void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); void (*EnableVertexAttribArray) (GLuint index); void (*DisableVertexAttribArray) (GLuint index); /* Framebuffer objects */ void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers); void (*BindFramebuffer) (GLenum target, GLuint framebuffer); void (*FramebufferTexture2D) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLenum (*CheckFramebufferStatus) (GLenum target); void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers); void (*GenRenderbuffers) (GLsizei n, GLuint *renderbuffers); void (*BindRenderbuffer) (GLenum target, GLuint renderbuffer); void (*RenderbufferStorage) (GLenum target, GLenum internal_format, GLsizei width, GLsizei height); void (*FramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffer_ttarget, GLuint renderbuffer); void (*DeleteRenderbuffers) (GLsizei n, GLuint *renderbuffers); void (*BlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); void (*RenderbufferStorageMultisample) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); void (*FramebufferTexture2DMultisample) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); } cairo_gl_dispatch_t; struct _cairo_gl_context { cairo_device_t base; const cairo_compositor_t *compositor; GLuint texture_load_pbo; GLint max_framebuffer_size; GLint max_texture_size; GLint max_textures; GLenum tex_target; GLint num_samples; cairo_bool_t supports_msaa; char *vb; cairo_bool_t has_shader_support; GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX]; cairo_gl_shader_t fill_rectangles_shader; cairo_cache_t shaders; cairo_cache_t gradients; cairo_gl_glyph_cache_t glyph_cache[2]; cairo_list_t fonts; cairo_gl_surface_t *current_target; cairo_operator_t current_operator; cairo_gl_shader_t *pre_shader; /* for component alpha */ cairo_gl_shader_t *current_shader; cairo_gl_operand_t operands[2]; cairo_bool_t spans; unsigned int vb_offset; unsigned int vertex_size; cairo_region_t *clip_region; cairo_clip_t *clip; cairo_gl_primitive_type_t primitive_type; cairo_array_t tristrip_indices; cairo_bool_t has_mesa_pack_invert; cairo_gl_dispatch_t dispatch; GLfloat modelviewprojection_matrix[16]; cairo_gl_flavor_t gl_flavor; cairo_bool_t has_map_buffer; cairo_bool_t has_packed_depth_stencil; cairo_bool_t has_npot_repeat; cairo_bool_t can_read_bgra; cairo_bool_t thread_aware; void (*acquire) (void *ctx); void (*release) (void *ctx); void (*make_current) (void *ctx, cairo_gl_surface_t *surface); void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); void (*destroy) (void *ctx); }; typedef struct _cairo_gl_composite { cairo_gl_surface_t *dst; cairo_operator_t op; cairo_region_t *clip_region; cairo_gl_operand_t src; cairo_gl_operand_t mask; cairo_bool_t spans; cairo_clip_t *clip; cairo_bool_t multisample; } cairo_gl_composite_t; typedef struct _cairo_gl_font { cairo_scaled_font_private_t base; cairo_device_t *device; cairo_list_t link; } cairo_gl_font_t; static cairo_always_inline GLenum _cairo_gl_get_error (void) { GLenum err = glGetError(); if (unlikely (err)) while (glGetError ()); return err; } static inline cairo_device_t * _cairo_gl_context_create_in_error (cairo_status_t status) { return (cairo_device_t *) _cairo_device_create_in_error (status); } cairo_private cairo_status_t _cairo_gl_context_init (cairo_gl_context_t *ctx); cairo_private void _cairo_gl_surface_init (cairo_device_t *device, cairo_gl_surface_t *surface, cairo_content_t content, int width, int height); static cairo_always_inline cairo_bool_t cairo_warn _cairo_gl_surface_is_texture (cairo_gl_surface_t *surface) { return surface->tex != 0; } cairo_private cairo_status_t _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, cairo_image_surface_t *src, int src_x, int src_y, int width, int height, int dst_x, int dst_y, cairo_bool_t force_flush); cairo_private cairo_int_status_t _cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface); static cairo_always_inline cairo_bool_t _cairo_gl_device_has_glsl (cairo_device_t *device) { return ((cairo_gl_context_t *) device)->has_shader_support; } static cairo_always_inline cairo_bool_t _cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device) { return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE; } static cairo_always_inline cairo_status_t cairo_warn _cairo_gl_context_acquire (cairo_device_t *device, cairo_gl_context_t **ctx) { cairo_status_t status; status = cairo_device_acquire (device); if (unlikely (status)) return status; /* clear potential previous GL errors */ _cairo_gl_get_error (); *ctx = (cairo_gl_context_t *) device; return CAIRO_STATUS_SUCCESS; } static cairo_always_inline cairo_warn cairo_status_t _cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) { GLenum err; err = _cairo_gl_get_error (); if (unlikely (err)) { cairo_status_t new_status; new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); if (status == CAIRO_STATUS_SUCCESS) status = new_status; } cairo_device_release (&(ctx)->base); return status; } cairo_private void _cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface, cairo_bool_t multisampling); cairo_private void _cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface, cairo_bool_t multisampling); cairo_private cairo_gl_emit_rect_t _cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx); cairo_private void _cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); cairo_private cairo_gl_emit_span_t _cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx); cairo_private cairo_gl_emit_glyph_t _cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx); cairo_private void _cairo_gl_context_activate (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit); cairo_private cairo_bool_t _cairo_gl_operator_is_supported (cairo_operator_t op); cairo_private cairo_bool_t _cairo_gl_ensure_stencil (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface); cairo_private cairo_status_t _cairo_gl_composite_init (cairo_gl_composite_t *setup, cairo_operator_t op, cairo_gl_surface_t *dst, cairo_bool_t has_component_alpha); cairo_private void _cairo_gl_composite_fini (cairo_gl_composite_t *setup); cairo_private cairo_status_t _cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, cairo_operator_t op, cairo_bool_t assume_component_alpha); cairo_private void _cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, cairo_region_t *clip_region); cairo_private void _cairo_gl_composite_set_clip(cairo_gl_composite_t *setup, cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen); cairo_private void _cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, const cairo_color_t *color); cairo_private void _cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, const cairo_gl_operand_t *source); cairo_private cairo_int_status_t _cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen); cairo_private void _cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, const cairo_gl_operand_t *mask); cairo_private void _cairo_gl_composite_set_spans (cairo_gl_composite_t *setup); cairo_private void _cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup); cairo_private cairo_status_t _cairo_gl_composite_begin (cairo_gl_composite_t *setup, cairo_gl_context_t **ctx); cairo_private cairo_status_t _cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, cairo_gl_context_t *ctx); cairo_private void _cairo_gl_composite_flush (cairo_gl_context_t *ctx); cairo_private cairo_int_status_t _cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_point_t quad[4]); cairo_private cairo_int_status_t _cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, const cairo_point_t triangle[3]); cairo_private void _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit); cairo_private cairo_bool_t _cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, pixman_format_code_t pixman_format, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha, cairo_bool_t *needs_swap); cairo_private void _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); cairo_private void _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, cairo_gl_glyph_cache_t *cache); cairo_private cairo_int_status_t _cairo_gl_surface_show_glyphs (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip, int *remaining_glyphs); cairo_private cairo_status_t _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); cairo_private void _cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx); static cairo_always_inline cairo_bool_t _cairo_gl_context_is_flushed (cairo_gl_context_t *ctx) { return ctx->vb_offset == 0; } cairo_private cairo_status_t _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, cairo_gl_operand_t *source, cairo_gl_operand_t *mask, cairo_bool_t use_coverage, cairo_gl_shader_in_t in, cairo_gl_shader_t **shader); cairo_private void _cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, GLint location, float value); cairo_private void _cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, GLint location, float value0, float value1); cairo_private void _cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, GLint location, float value0, float value1, float value2); cairo_private void _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, GLint location, float value0, float value1, float value2, float value3); cairo_private void _cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, GLint location, const cairo_matrix_t* m); cairo_private void _cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, GLint location, GLfloat* gl_m); cairo_private void _cairo_gl_set_shader (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); cairo_private void _cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); cairo_private int _cairo_gl_get_version (void); cairo_private cairo_gl_flavor_t _cairo_gl_get_flavor (void); cairo_private cairo_bool_t _cairo_gl_has_extension (const char *ext); cairo_private cairo_status_t _cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch, cairo_gl_get_proc_addr_func_t get_proc_addr); cairo_private cairo_int_status_t _cairo_gl_operand_init (cairo_gl_operand_t *operand, const cairo_pattern_t *pattern, cairo_gl_surface_t *dst, const cairo_rectangle_int_t *sample, const cairo_rectangle_int_t *extents, cairo_bool_t use_texgen); cairo_private void _cairo_gl_solid_operand_init (cairo_gl_operand_t *operand, const cairo_color_t *color); cairo_private cairo_filter_t _cairo_gl_operand_get_filter (cairo_gl_operand_t *operand); cairo_private GLint _cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand); cairo_private cairo_extend_t _cairo_gl_operand_get_extend (cairo_gl_operand_t *operand); cairo_private unsigned int _cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand); cairo_private cairo_bool_t _cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest, cairo_gl_operand_t *source, unsigned int vertex_offset); cairo_private void _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, cairo_gl_operand_t *operand, cairo_gl_tex_t tex_unit); cairo_private void _cairo_gl_operand_emit (cairo_gl_operand_t *operand, GLfloat ** vb, GLfloat x, GLfloat y); cairo_private void _cairo_gl_operand_copy (cairo_gl_operand_t *dst, const cairo_gl_operand_t *src); cairo_private void _cairo_gl_operand_translate (cairo_gl_operand_t *operand, double tx, double ty); cairo_private void _cairo_gl_operand_destroy (cairo_gl_operand_t *operand); cairo_private const cairo_compositor_t * _cairo_gl_msaa_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_gl_span_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_gl_traps_compositor_get (void); cairo_private cairo_int_status_t _cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs); cairo_private cairo_int_status_t _cairo_gl_composite_glyphs (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info); cairo_private cairo_int_status_t _cairo_gl_composite_glyphs_with_clip (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info, cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height); cairo_private cairo_surface_t * _cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height); cairo_private cairo_surface_t * _cairo_gl_pattern_to_source (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_private cairo_int_status_t _cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup, cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_gl_white_source (void); cairo_private void _cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, const cairo_rectangle_int_t *r); static inline cairo_gl_operand_t * source_to_operand (cairo_surface_t *surface) { cairo_gl_source_t *source = (cairo_gl_source_t *)surface; return source ? &source->operand : NULL; } static inline void _cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) { _cairo_rtree_unpin (&cache->rtree); } slim_hidden_proto (cairo_gl_surface_create); slim_hidden_proto (cairo_gl_surface_create_for_texture); #endif /* CAIRO_GL_PRIVATE_H */ �������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-shaders.c������������������������������0000664�0000000�0000000�00000103010�12710376503�0025563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 T. Zachary Laine * Copyright © 2010 Eric Anholt * Copyright © 2010 Red Hat, Inc * Copyright © 2010 Linaro Limited * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is T. Zachary Laine. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Eric Anholt <eric@anholt.net> * T. Zachary Laine <whatwasthataddress@gmail.com> * Alexandros Frantzis <alexandros.frantzis@linaro.org> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" static cairo_status_t _cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader, cairo_gl_var_type_t src, cairo_gl_var_type_t mask, cairo_bool_t use_coverage, const char *fragment_text); typedef struct _cairo_shader_cache_entry { cairo_cache_entry_t base; unsigned vertex; cairo_gl_operand_type_t src; cairo_gl_operand_type_t mask; cairo_gl_operand_type_t dest; cairo_bool_t use_coverage; cairo_gl_shader_in_t in; GLint src_gl_filter; cairo_bool_t src_border_fade; cairo_extend_t src_extend; GLint mask_gl_filter; cairo_bool_t mask_border_fade; cairo_extend_t mask_extend; cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ cairo_gl_shader_t shader; } cairo_shader_cache_entry_t; static cairo_bool_t _cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b) { const cairo_shader_cache_entry_t *a = key_a; const cairo_shader_cache_entry_t *b = key_b; cairo_bool_t both_have_npot_repeat = a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; return (a->vertex == b->vertex && a->src == b->src && a->mask == b->mask && a->dest == b->dest && a->use_coverage == b->use_coverage && a->in == b->in && (both_have_npot_repeat || a->src_extend == b->src_extend) && (both_have_npot_repeat || a->mask_extend == b->mask_extend)); } /* * For GLES2 we use more complicated shaders to implement missing GL * features. In this case we need more parameters to uniquely identify * a shader (vs _cairo_gl_shader_cache_equal_desktop()). */ static cairo_bool_t _cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b) { const cairo_shader_cache_entry_t *a = key_a; const cairo_shader_cache_entry_t *b = key_b; cairo_bool_t both_have_npot_repeat = a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; return (a->vertex == b->vertex && a->src == b->src && a->mask == b->mask && a->dest == b->dest && a->use_coverage == b->use_coverage && a->in == b->in && a->src_gl_filter == b->src_gl_filter && a->src_border_fade == b->src_border_fade && (both_have_npot_repeat || a->src_extend == b->src_extend) && a->mask_gl_filter == b->mask_gl_filter && a->mask_border_fade == b->mask_border_fade && (both_have_npot_repeat || a->mask_extend == b->mask_extend)); } static unsigned long _cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) { return ((entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex; } static void _cairo_gl_shader_cache_destroy (void *data) { cairo_shader_cache_entry_t *entry = data; _cairo_gl_shader_fini (entry->ctx, &entry->shader); if (entry->ctx->current_shader == &entry->shader) entry->ctx->current_shader = NULL; free (entry); } static void _cairo_gl_shader_init (cairo_gl_shader_t *shader) { shader->fragment_shader = 0; shader->program = 0; } cairo_status_t _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) { static const char *fill_fs_source = "#ifdef GL_ES\n" "precision mediump float;\n" "#endif\n" "uniform vec4 color;\n" "void main()\n" "{\n" " gl_FragColor = color;\n" "}\n"; cairo_status_t status; if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) || (_cairo_gl_has_extension ("GL_ARB_shader_objects") && _cairo_gl_has_extension ("GL_ARB_fragment_shader") && _cairo_gl_has_extension ("GL_ARB_vertex_shader"))) { ctx->has_shader_support = TRUE; } else { ctx->has_shader_support = FALSE; fprintf (stderr, "Error: The cairo gl backend requires shader support!\n"); return CAIRO_STATUS_DEVICE_ERROR; } memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); status = _cairo_cache_init (&ctx->shaders, ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ? _cairo_gl_shader_cache_equal_desktop : _cairo_gl_shader_cache_equal_gles2, NULL, _cairo_gl_shader_cache_destroy, CAIRO_GL_MAX_SHADERS_PER_CONTEXT); if (unlikely (status)) return status; _cairo_gl_shader_init (&ctx->fill_rectangles_shader); status = _cairo_gl_shader_compile_and_link (ctx, &ctx->fill_rectangles_shader, CAIRO_GL_VAR_NONE, CAIRO_GL_VAR_NONE, FALSE, fill_fs_source); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } void _cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) { int i; for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) { if (ctx->vertex_shaders[i]) ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]); } _cairo_cache_fini (&ctx->shaders); } void _cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader) { if (shader->fragment_shader) ctx->dispatch.DeleteShader (shader->fragment_shader); if (shader->program) ctx->dispatch.DeleteProgram (shader->program); } static const char *operand_names[] = { "source", "mask", "dest" }; static cairo_gl_var_type_t cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand) { switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: return CAIRO_GL_VAR_NONE; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; case CAIRO_GL_OPERAND_TEXTURE: return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; } } static void cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, "attribute vec4 MultiTexCoord%d;\n" "varying vec2 %s_texcoords;\n", name, operand_names[name]); break; case CAIRO_GL_VAR_TEXGEN: _cairo_output_stream_printf (stream, "uniform mat3 %s_texgen;\n" "varying vec2 %s_texcoords;\n", operand_names[name], operand_names[name]); break; } } static void cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, cairo_gl_var_type_t type, cairo_gl_tex_t name) { switch (type) { default: ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: _cairo_output_stream_printf (stream, " %s_texcoords = MultiTexCoord%d.xy;\n", operand_names[name], name); break; case CAIRO_GL_VAR_TEXGEN: _cairo_output_stream_printf (stream, " %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n", operand_names[name], operand_names[name]); break; } } static void cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream) { _cairo_output_stream_printf (stream, "varying float coverage;\n"); } static void cairo_gl_shader_def_coverage (cairo_output_stream_t *stream) { _cairo_output_stream_printf (stream, " coverage = Color.a;\n"); } static cairo_status_t cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, cairo_gl_var_type_t mask, cairo_bool_t use_coverage, cairo_gl_var_type_t dest, char **out) { cairo_output_stream_t *stream = _cairo_memory_stream_create (); unsigned char *source; unsigned long length; cairo_status_t status; cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); if (use_coverage) cairo_gl_shader_dcl_coverage (stream); _cairo_output_stream_printf (stream, "attribute vec4 Vertex;\n" "attribute vec4 Color;\n" "uniform mat4 ModelViewProjectionMatrix;\n" "void main()\n" "{\n" " gl_Position = ModelViewProjectionMatrix * Vertex;\n"); cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); if (use_coverage) cairo_gl_shader_def_coverage (stream); _cairo_output_stream_write (stream, "}\n\0", 3); status = _cairo_memory_stream_destroy (stream, &source, &length); if (unlikely (status)) return status; *out = (char *) source; return CAIRO_STATUS_SUCCESS; } /* * Returns whether an operand needs a special border fade fragment shader * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2. */ static cairo_bool_t _cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand) { cairo_extend_t extend =_cairo_gl_operand_get_extend (operand); return extend == CAIRO_EXTEND_NONE && (operand->type == CAIRO_GL_OPERAND_TEXTURE || operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT || operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE || operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0); } static void cairo_gl_shader_emit_color (cairo_output_stream_t *stream, cairo_gl_context_t *ctx, cairo_gl_operand_t *op, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : ""); switch (op->type) { case CAIRO_GL_OPERAND_COUNT: default: ASSERT_NOT_REACHED; break; case CAIRO_GL_OPERAND_NONE: _cairo_output_stream_printf (stream, "vec4 get_%s()\n" "{\n" " return vec4 (0, 0, 0, 1);\n" "}\n", namestr); break; case CAIRO_GL_OPERAND_CONSTANT: _cairo_output_stream_printf (stream, "uniform vec4 %s_constant;\n" "vec4 get_%s()\n" "{\n" " return %s_constant;\n" "}\n", namestr, namestr, namestr); break; case CAIRO_GL_OPERAND_TEXTURE: _cairo_output_stream_printf (stream, "uniform sampler2D%s %s_sampler;\n" "uniform vec2 %s_texdims;\n" "varying vec2 %s_texcoords;\n" "vec4 get_%s()\n" "{\n", rectstr, namestr, namestr, namestr, namestr); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n" " vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n" " return texel * border_fade.x * border_fade.y;\n" "}\n", namestr, namestr, namestr, rectstr, namestr, namestr); } else { _cairo_output_stream_printf (stream, " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n" "}\n", rectstr, namestr, namestr, namestr); } break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n" "uniform vec2 %s_texdims;\n" "uniform sampler2D%s %s_sampler;\n" "\n" "vec4 get_%s()\n" "{\n", namestr, namestr, rectstr, namestr, namestr); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, " float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n" " vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n" " return texel * border_fade;\n" "}\n", namestr, namestr, namestr, rectstr, namestr, namestr); } else { _cairo_output_stream_printf (stream, " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n" "}\n", rectstr, namestr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n" "uniform vec2 %s_texdims;\n" "uniform sampler2D%s %s_sampler;\n" "uniform vec3 %s_circle_d;\n" "uniform float %s_radius_0;\n" "\n" "vec4 get_%s()\n" "{\n" " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" " \n" " float B = dot (pos, %s_circle_d);\n" " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" " \n" " float t = 0.5 * C / B;\n" " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n", namestr, namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, " float border_fade = %s_border_fade (t, %s_texdims.x);\n" " vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n" " return mix (vec4 (0.0), texel * border_fade, is_valid);\n" "}\n", namestr, namestr, rectstr, namestr); } else { _cairo_output_stream_printf (stream, " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n" " return mix (vec4 (0.0), texel, is_valid);\n" "}\n", rectstr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n" "uniform vec2 %s_texdims;\n" "uniform sampler2D%s %s_sampler;\n" "uniform vec3 %s_circle_d;\n" "uniform float %s_a;\n" "uniform float %s_radius_0;\n" "\n" "vec4 get_%s()\n" "{\n" " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" " \n" " float B = dot (pos, %s_circle_d);\n" " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" " \n" " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" " float sqrtdet = sqrt (abs (det));\n" " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" " \n" " vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n" " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" " \n" " float upper_t = mix (t.y, t.x, is_valid.x);\n", namestr, namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_shader_needs_border_fade (op)) { _cairo_output_stream_printf (stream, " float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n" " vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n" " return mix (vec4 (0.0), texel * border_fade, has_color);\n" "}\n", namestr, namestr, rectstr, namestr); } else { _cairo_output_stream_printf (stream, " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" " return mix (vec4 (0.0), texel, has_color);\n" "}\n", rectstr, namestr, namestr); } break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_output_stream_printf (stream, "varying vec2 %s_texcoords;\n" "uniform sampler2D%s %s_sampler;\n" "uniform vec3 %s_circle_d;\n" "uniform float %s_a;\n" "uniform float %s_radius_0;\n" "\n" "vec4 get_%s()\n" "{\n" " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" " \n" " float B = dot (pos, %s_circle_d);\n" " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" " \n" " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" " float sqrtdet = sqrt (abs (det));\n" " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" " \n" " vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n" " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" " \n" " float upper_t = mix (t.y, t.x, is_valid.x);\n" " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" " return mix (vec4 (0.0), texel, has_color);\n" "}\n", namestr, rectstr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, rectstr, namestr, namestr); break; } } /* * Emits the border fade functions used by an operand. * * If bilinear filtering is used, the emitted function performs a linear * fade to transparency effect in the intervals [-1/2n, 1/2n] and * [1 - 1/2n, 1 + 1/2n] (n: texture size). * * If nearest filtering is used, the emitted function just returns * 0.0 for all values outside [0, 1). */ static void _cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand); /* 2D version */ _cairo_output_stream_printf (stream, "vec2 %s_border_fade (vec2 coords, vec2 dims)\n" "{\n", namestr); if (gl_filter == GL_LINEAR) _cairo_output_stream_printf (stream, " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n"); else _cairo_output_stream_printf (stream, " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n" " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n" " return vec2 (float (all (in_tex1) && all (in_tex2)));\n"); _cairo_output_stream_printf (stream, "}\n"); /* 1D version */ _cairo_output_stream_printf (stream, "float %s_border_fade (float x, float dim)\n" "{\n", namestr); if (gl_filter == GL_LINEAR) _cairo_output_stream_printf (stream, " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n"); else _cairo_output_stream_printf (stream, " bool in_tex = x >= 0.0 && x < 1.0;\n" " return float (in_tex);\n"); _cairo_output_stream_printf (stream, "}\n"); } /* * Emits the wrap function used by an operand. * * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are * only available for NPOT textures if the GL_OES_texture_npot is supported. * If GL_OES_texture_npot is not supported, we need to implement the wrapping * functionality in the shader. */ static void _cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); _cairo_output_stream_printf (stream, "vec2 %s_wrap(vec2 coords)\n" "{\n", namestr); if (! ctx->has_npot_repeat && (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) { if (extend == CAIRO_EXTEND_REPEAT) { _cairo_output_stream_printf (stream, " return fract(coords);\n"); } else { /* CAIRO_EXTEND_REFLECT */ _cairo_output_stream_printf (stream, " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); } } else { _cairo_output_stream_printf (stream, " return coords;\n"); } _cairo_output_stream_printf (stream, "}\n"); } static cairo_status_t cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, cairo_gl_shader_in_t in, cairo_gl_operand_t *src, cairo_gl_operand_t *mask, cairo_bool_t use_coverage, cairo_gl_operand_type_t dest_type, char **out) { cairo_output_stream_t *stream = _cairo_memory_stream_create (); unsigned char *source; unsigned long length; cairo_status_t status; const char *coverage_str; _cairo_output_stream_printf (stream, "#ifdef GL_ES\n" "precision mediump float;\n" "#endif\n"); _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE); _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { if (_cairo_gl_shader_needs_border_fade (src)) _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE); if (_cairo_gl_shader_needs_border_fade (mask)) _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK); } cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE); cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK); coverage_str = ""; if (use_coverage) { _cairo_output_stream_printf (stream, "varying float coverage;\n"); coverage_str = " * coverage"; } _cairo_output_stream_printf (stream, "void main()\n" "{\n"); switch (in) { case CAIRO_GL_SHADER_IN_COUNT: default: ASSERT_NOT_REACHED; case CAIRO_GL_SHADER_IN_NORMAL: _cairo_output_stream_printf (stream, " gl_FragColor = get_source() * get_mask().a%s;\n", coverage_str); break; case CAIRO_GL_SHADER_IN_CA_SOURCE: _cairo_output_stream_printf (stream, " gl_FragColor = get_source() * get_mask()%s;\n", coverage_str); break; case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: _cairo_output_stream_printf (stream, " gl_FragColor = get_source().a * get_mask()%s;\n", coverage_str); break; } _cairo_output_stream_write (stream, "}\n\0", 3); status = _cairo_memory_stream_destroy (stream, &source, &length); if (unlikely (status)) return status; *out = (char *) source; return CAIRO_STATUS_SUCCESS; } static void compile_shader (cairo_gl_context_t *ctx, GLuint *shader, GLenum type, const char *source) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; GLint success, log_size, num_chars; char *log; *shader = dispatch->CreateShader (type); dispatch->ShaderSource (*shader, 1, &source, 0); dispatch->CompileShader (*shader); dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success); if (success) return; dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); if (log_size < 0) { printf ("OpenGL shader compilation failed.\n"); ASSERT_NOT_REACHED; return; } log = _cairo_malloc (log_size + 1); dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log); log[num_chars] = '\0'; printf ("OpenGL shader compilation failed. Shader:\n%s\n", source); printf ("OpenGL compilation log:\n%s\n", log); free (log); ASSERT_NOT_REACHED; } static void link_shader_program (cairo_gl_context_t *ctx, GLuint *program, GLuint vert, GLuint frag) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; GLint success, log_size, num_chars; char *log; *program = dispatch->CreateProgram (); dispatch->AttachShader (*program, vert); dispatch->AttachShader (*program, frag); dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX, "Vertex"); dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX, "Color"); dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX, "MultiTexCoord0"); dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX, "MultiTexCoord1"); dispatch->LinkProgram (*program); dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success); if (success) return; dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); if (log_size < 0) { printf ("OpenGL shader link failed.\n"); ASSERT_NOT_REACHED; return; } log = _cairo_malloc (log_size + 1); dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log); log[num_chars] = '\0'; printf ("OpenGL shader link failed:\n%s\n", log); free (log); ASSERT_NOT_REACHED; } static GLint _cairo_gl_get_op_uniform_location(cairo_gl_context_t *ctx, cairo_gl_shader_t *shader, cairo_gl_tex_t tex_unit, const char *suffix) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; char uniform_name[100]; const char *unit_name[2] = { "source", "mask" }; snprintf (uniform_name, sizeof (uniform_name), "%s_%s", unit_name[tex_unit], suffix); return dispatch->GetUniformLocation (shader->program, uniform_name); } static cairo_status_t _cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader, cairo_gl_var_type_t src, cairo_gl_var_type_t mask, cairo_bool_t use_coverage, const char *fragment_text) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; unsigned int vertex_shader; cairo_status_t status; int i; assert (shader->program == 0); vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage, CAIRO_GL_VAR_NONE); if (ctx->vertex_shaders[vertex_shader] == 0) { char *source; status = cairo_gl_shader_get_vertex_source (src, mask, use_coverage, CAIRO_GL_VAR_NONE, &source); if (unlikely (status)) goto FAILURE; compile_shader (ctx, &ctx->vertex_shaders[vertex_shader], GL_VERTEX_SHADER, source); free (source); } compile_shader (ctx, &shader->fragment_shader, GL_FRAGMENT_SHADER, fragment_text); link_shader_program (ctx, &shader->program, ctx->vertex_shaders[vertex_shader], shader->fragment_shader); shader->mvp_location = dispatch->GetUniformLocation (shader->program, "ModelViewProjectionMatrix"); for (i = 0; i < 2; i++) { shader->constant_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "constant"); shader->a_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "a"); shader->circle_d_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "circle_d"); shader->radius_0_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "radius_0"); shader->texdims_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "texdims"); shader->texgen_location[i] = _cairo_gl_get_op_uniform_location (ctx, shader, i, "texgen"); } return CAIRO_STATUS_SUCCESS; FAILURE: _cairo_gl_shader_fini (ctx, shader); shader->fragment_shader = 0; shader->program = 0; return status; } /* We always bind the source to texture unit 0 if present, and mask to * texture unit 1 if present, so we can just initialize these once at * compile time. */ static void _cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; GLint location; GLint saved_program; /* We have to save/restore the current program because we might be * asked for a different program while a shader is bound. This shouldn't * be a performance issue, since this is only called once per compile. */ glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program); dispatch->UseProgram (shader->program); location = dispatch->GetUniformLocation (shader->program, "source_sampler"); if (location != -1) { dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE); } location = dispatch->GetUniformLocation (shader->program, "mask_sampler"); if (location != -1) { dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK); } dispatch->UseProgram (saved_program); } void _cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, GLint location, float value) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; assert (location != -1); dispatch->Uniform1f (location, value); } void _cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, GLint location, float value0, float value1) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; assert (location != -1); dispatch->Uniform2f (location, value0, value1); } void _cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, GLint location, float value0, float value1, float value2) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; assert (location != -1); dispatch->Uniform3f (location, value0, value1, value2); } void _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, GLint location, float value0, float value1, float value2, float value3) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; assert (location != -1); dispatch->Uniform4f (location, value0, value1, value2, value3); } void _cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, GLint location, const cairo_matrix_t* m) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; float gl_m[9] = { m->xx, m->xy, m->x0, m->yx, m->yy, m->y0, 0, 0, 1 }; assert (location != -1); dispatch->UniformMatrix3fv (location, 1, GL_TRUE, gl_m); } void _cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, GLint location, GLfloat* gl_m) { cairo_gl_dispatch_t *dispatch = &ctx->dispatch; assert (location != -1); dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m); } void _cairo_gl_set_shader (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader) { if (ctx->current_shader == shader) return; if (shader) ctx->dispatch.UseProgram (shader->program); else ctx->dispatch.UseProgram (0); ctx->current_shader = shader; } cairo_status_t _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, cairo_gl_operand_t *source, cairo_gl_operand_t *mask, cairo_bool_t use_coverage, cairo_gl_shader_in_t in, cairo_gl_shader_t **shader) { cairo_shader_cache_entry_t lookup, *entry; char *fs_source; cairo_status_t status; lookup.ctx = ctx; lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source), cairo_gl_operand_get_var_type (mask), use_coverage, CAIRO_GL_VAR_NONE); lookup.src = source->type; lookup.mask = mask->type; lookup.dest = CAIRO_GL_OPERAND_NONE; lookup.use_coverage = use_coverage; lookup.in = in; lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source); lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source); lookup.src_extend = _cairo_gl_operand_get_extend (source); lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask); lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask); lookup.mask_extend = _cairo_gl_operand_get_extend (mask); lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); lookup.base.size = 1; entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); if (entry) { assert (entry->shader.program); *shader = &entry->shader; return CAIRO_STATUS_SUCCESS; } status = cairo_gl_shader_get_fragment_source (ctx, in, source, mask, use_coverage, CAIRO_GL_OPERAND_NONE, &fs_source); if (unlikely (status)) return status; entry = malloc (sizeof (cairo_shader_cache_entry_t)); if (unlikely (entry == NULL)) { free (fs_source); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); entry->ctx = ctx; _cairo_gl_shader_init (&entry->shader); status = _cairo_gl_shader_compile_and_link (ctx, &entry->shader, cairo_gl_operand_get_var_type (source), cairo_gl_operand_get_var_type (mask), use_coverage, fs_source); free (fs_source); if (unlikely (status)) { free (entry); return status; } _cairo_gl_shader_set_samplers (ctx, &entry->shader); status = _cairo_cache_insert (&ctx->shaders, &entry->base); if (unlikely (status)) { _cairo_gl_shader_fini (ctx, &entry->shader); free (entry); return status; } *shader = &entry->shader; return CAIRO_STATUS_SUCCESS; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-source.c�������������������������������0000664�0000000�0000000�00000006620�12710376503�0025443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-surface-backend-private.h" static cairo_status_t _cairo_gl_source_finish (void *abstract_surface) { cairo_gl_source_t *source = abstract_surface; _cairo_gl_operand_destroy (&source->operand); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_gl_source_backend = { CAIRO_SURFACE_TYPE_GL, _cairo_gl_source_finish, NULL, /* read-only wrapper */ }; cairo_surface_t * _cairo_gl_pattern_to_source (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_gl_source_t *source; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (pattern == NULL) return _cairo_gl_white_source (); source = malloc (sizeof (*source)); if (unlikely (source == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&source->base, &cairo_gl_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); *src_x = *src_y = 0; status = _cairo_gl_operand_init (&source->operand, pattern, (cairo_gl_surface_t *)dst, sample, extents, FALSE); if (unlikely (status)) { cairo_surface_destroy (&source->base); return _cairo_surface_create_in_error (status); } return &source->base; } cairo_surface_t * _cairo_gl_white_source (void) { cairo_gl_source_t *source; source = malloc (sizeof (*source)); if (unlikely (source == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&source->base, &cairo_gl_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); _cairo_gl_solid_operand_init (&source->operand, CAIRO_COLOR_WHITE); return &source->base; } ����������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-spans-compositor.c���������������������0000664�0000000�0000000�00000035553�12710376503�0027472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-spans-compositor-private.h" #include "cairo-surface-backend-private.h" typedef struct _cairo_gl_span_renderer { cairo_span_renderer_t base; cairo_gl_composite_t setup; double opacity; cairo_gl_emit_span_t emit; int xmin, xmax; int ymin, ymax; cairo_gl_context_t *ctx; } cairo_gl_span_renderer_t; static cairo_status_t _cairo_gl_bounded_opaque_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_span_renderer_t *r = abstract_renderer; cairo_gl_emit_span_t emit = r->emit; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { emit (r->ctx, spans[0].x, y, spans[1].x, y + height, spans[0].coverage); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_span_renderer_t *r = abstract_renderer; cairo_gl_emit_span_t emit = r->emit; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { emit (r->ctx, spans[0].x, y, spans[1].x, y + height, r->opacity * spans[0].coverage); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_span_renderer_t *r = abstract_renderer; cairo_gl_emit_span_t emit = r->emit; if (y > r->ymin) { emit (r->ctx, r->xmin, r->ymin, r->xmax, y, 0); } if (num_spans == 0) { emit (r->ctx, r->xmin, y, r->xmax, y + height, 0); } else { if (spans[0].x != r->xmin) { emit (r->ctx, r->xmin, y, spans[0].x, y + height, 0); } do { emit (r->ctx, spans[0].x, y, spans[1].x, y + height, r->opacity * spans[0].coverage); spans++; } while (--num_spans > 1); if (spans[0].x != r->xmax) { emit (r->ctx, spans[0].x, y, r->xmax, y + height, 0); } } r->ymin = y + height; return CAIRO_STATUS_SUCCESS; } /* XXX */ static cairo_status_t _cairo_gl_clipped_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_gl_span_renderer_t *r = abstract_renderer; cairo_gl_emit_span_t emit = r->emit; if (y > r->ymin) { emit (r->ctx, r->xmin, r->ymin, r->xmax, y, 0); } if (num_spans == 0) { emit (r->ctx, r->xmin, y, r->xmax, y + height, 0); } else { if (spans[0].x != r->xmin) { emit (r->ctx, r->xmin, y, spans[0].x, y + height, 0); } do { emit (r->ctx, spans[0].x, y, spans[1].x, y + height, r->opacity * spans[0].coverage); spans++; } while (--num_spans > 1); if (spans[0].x != r->xmax) { emit (r->ctx, spans[0].x, y, r->xmax, y + height, 0); } } r->ymin = y + height; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gl_finish_unbounded_spans (void *abstract_renderer) { cairo_gl_span_renderer_t *r = abstract_renderer; cairo_gl_emit_span_t emit = r->emit; if (r->ymax > r->ymin) { emit (r->ctx, r->xmin, r->ymin, r->xmax, r->ymax, 0); } return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS); } static cairo_status_t _cairo_gl_finish_bounded_spans (void *abstract_renderer) { cairo_gl_span_renderer_t *r = abstract_renderer; return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS); } static void emit_aligned_boxes (cairo_gl_context_t *ctx, const cairo_boxes_t *boxes) { const struct _cairo_boxes_chunk *chunk; cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx); int i; TRACE ((stderr, "%s: num_boxes=%d\n", __FUNCTION__, boxes->num_boxes)); for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); emit (ctx, x1, y1, x2, y2); } } } static cairo_int_status_t fill_boxes (void *_dst, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_solid_source (&setup, color); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; emit_aligned_boxes (ctx, boxes); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t draw_image_boxes (void *_dst, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy) { cairo_gl_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; int i; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int w = _cairo_fixed_integer_part (b->p2.x) - x; int h = _cairo_fixed_integer_part (b->p2.y) - y; cairo_status_t status; status = _cairo_gl_surface_draw_image (dst, image, x + dx, y + dy, w, h, x, y, TRUE); if (unlikely (status)) return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t copy_boxes (void *_dst, cairo_surface_t *_src, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents, int dx, int dy) { cairo_gl_surface_t *dst = _dst; cairo_gl_surface_t *src = (cairo_gl_surface_t *)_src; cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (! _cairo_gl_surface_is_texture (src)) return CAIRO_INT_STATUS_UNSUPPORTED; if (src->base.device != dst->base.device) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, &src->operand); _cairo_gl_operand_translate (&setup.src, -dx, -dy); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; emit_aligned_boxes (ctx, boxes); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t composite_boxes (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; cairo_gl_operand_t tmp_operand; cairo_gl_operand_t *src_operand; TRACE ((stderr, "%s mask=(%d,%d), dst=(%d, %d)\n", __FUNCTION__, mask_x, mask_y, dst_x, dst_y)); if (abstract_mask) { if (op == CAIRO_OPERATOR_CLEAR) { _cairo_gl_solid_operand_init (&tmp_operand, CAIRO_COLOR_WHITE); src_operand = &tmp_operand; op = CAIRO_OPERATOR_DEST_OUT; } else if (op == CAIRO_OPERATOR_SOURCE) { /* requires a LERP in the shader between dest and source */ return CAIRO_INT_STATUS_UNSUPPORTED; } else src_operand = source_to_operand (abstract_src); } else src_operand = source_to_operand (abstract_src); status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, src_operand); _cairo_gl_operand_translate (&setup.src, -src_x, -src_y); _cairo_gl_composite_set_mask_operand (&setup, source_to_operand (abstract_mask)); _cairo_gl_operand_translate (&setup.mask, -mask_x, -mask_y); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; emit_aligned_boxes (ctx, boxes); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); if (src_operand == &tmp_operand) _cairo_gl_operand_destroy (&tmp_operand); return status; } static cairo_int_status_t _cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t *_r, const cairo_composite_rectangles_t *composite, cairo_antialias_t antialias, cairo_bool_t needs_clip) { cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *)_r; const cairo_pattern_t *source = &composite->source_pattern.base; cairo_operator_t op = composite->op; cairo_int_status_t status; if (op == CAIRO_OPERATOR_SOURCE) { if (! _cairo_pattern_is_opaque (&composite->source_pattern.base, &composite->source_sample_area)) return CAIRO_INT_STATUS_UNSUPPORTED; op = CAIRO_OPERATOR_OVER; } /* XXX earlier! */ if (op == CAIRO_OPERATOR_CLEAR) { source = &_cairo_pattern_white.base; op = CAIRO_OPERATOR_DEST_OUT; } else if (composite->surface->is_clear && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) { op = CAIRO_OPERATOR_SOURCE; } else if (op == CAIRO_OPERATOR_SOURCE) { /* no lerp equivalent without some major PITA */ return CAIRO_INT_STATUS_UNSUPPORTED; } else if (! _cairo_gl_operator_is_supported (op)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_gl_composite_init (&r->setup, op, (cairo_gl_surface_t *)composite->surface, FALSE); if (unlikely (status)) goto FAIL; status = _cairo_gl_composite_set_source (&r->setup, source, &composite->source_sample_area, &composite->unbounded, TRUE); if (unlikely (status)) goto FAIL; r->opacity = 1.0; if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { r->opacity = composite->mask_pattern.solid.color.alpha; } else { status = _cairo_gl_composite_set_mask (&r->setup, &composite->mask_pattern.base, &composite->mask_sample_area, &composite->unbounded, TRUE); if (unlikely (status)) goto FAIL; } _cairo_gl_composite_set_spans (&r->setup); status = _cairo_gl_composite_begin (&r->setup, &r->ctx); if (unlikely (status)) goto FAIL; r->emit = _cairo_gl_context_choose_emit_span (r->ctx); if (composite->is_bounded) { if (r->opacity == 1.) r->base.render_rows = _cairo_gl_bounded_opaque_spans; else r->base.render_rows = _cairo_gl_bounded_spans; r->base.finish = _cairo_gl_finish_bounded_spans; } else { if (needs_clip) r->base.render_rows = _cairo_gl_clipped_spans; else r->base.render_rows = _cairo_gl_unbounded_spans; r->base.finish = _cairo_gl_finish_unbounded_spans; r->xmin = composite->unbounded.x; r->xmax = composite->unbounded.x + composite->unbounded.width; r->ymin = composite->unbounded.y; r->ymax = composite->unbounded.y + composite->unbounded.height; } return CAIRO_STATUS_SUCCESS; FAIL: return status; } static void _cairo_gl_span_renderer_fini (cairo_abstract_span_renderer_t *_r, cairo_int_status_t status) { cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *) _r; if (status == CAIRO_INT_STATUS_UNSUPPORTED) return; if (status == CAIRO_INT_STATUS_SUCCESS) r->base.finish (r); _cairo_gl_composite_fini (&r->setup); } const cairo_compositor_t * _cairo_gl_span_compositor_get (void) { static cairo_spans_compositor_t spans; static cairo_compositor_t shape; if (spans.base.delegate == NULL) { /* The fallback to traps here is essentially just for glyphs... */ _cairo_shape_mask_compositor_init (&shape, _cairo_gl_traps_compositor_get()); shape.glyphs = NULL; _cairo_spans_compositor_init (&spans, &shape); spans.fill_boxes = fill_boxes; spans.draw_image_boxes = draw_image_boxes; spans.copy_boxes = copy_boxes; //spans.check_composite_boxes = check_composite_boxes; spans.pattern_to_surface = _cairo_gl_pattern_to_source; spans.composite_boxes = composite_boxes; //spans.check_span_renderer = check_span_renderer; spans.renderer_init = _cairo_gl_span_renderer_init; spans.renderer_fini = _cairo_gl_span_renderer_fini; } return &spans.base; } �����������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-surface.c������������������������������0000664�0000000�0000000�00000122157�12710376503�0025577�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-surface-backend-private.h" static const cairo_surface_backend_t _cairo_gl_surface_backend; static cairo_status_t _cairo_gl_surface_flush (void *abstract_surface, unsigned flags); static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) { return surface->backend == &_cairo_gl_surface_backend; } static cairo_bool_t _cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha, cairo_bool_t *needs_swap) { cairo_bool_t is_little_endian = _cairo_is_little_endian (); *has_alpha = TRUE; switch ((int) pixman_format) { case PIXMAN_a8r8g8b8: *internal_format = GL_BGRA; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; *needs_swap = !is_little_endian; return TRUE; case PIXMAN_x8r8g8b8: *internal_format = GL_BGRA; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; *has_alpha = FALSE; *needs_swap = !is_little_endian; return TRUE; case PIXMAN_a8b8g8r8: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; *needs_swap = !is_little_endian; return TRUE; case PIXMAN_x8b8g8r8: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; *has_alpha = FALSE; *needs_swap = !is_little_endian; return TRUE; case PIXMAN_b8g8r8a8: *internal_format = GL_BGRA; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; *needs_swap = is_little_endian; return TRUE; case PIXMAN_b8g8r8x8: *internal_format = GL_BGRA; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; *has_alpha = FALSE; *needs_swap = is_little_endian; return TRUE; case PIXMAN_r8g8b8: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_BYTE; *needs_swap = is_little_endian; return TRUE; case PIXMAN_b8g8r8: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_BYTE; *needs_swap = !is_little_endian; return TRUE; case PIXMAN_r5g6b5: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5; *needs_swap = FALSE; return TRUE; case PIXMAN_b5g6r5: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5; *needs_swap = TRUE; return TRUE; case PIXMAN_a1b5g5r5: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_5_5_5_1; *needs_swap = TRUE; return TRUE; case PIXMAN_x1b5g5r5: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_5_5_5_1; *has_alpha = FALSE; *needs_swap = TRUE; return TRUE; case PIXMAN_a8: *internal_format = GL_ALPHA; *format = GL_ALPHA; *type = GL_UNSIGNED_BYTE; *needs_swap = FALSE; return TRUE; default: return FALSE; } } static cairo_bool_t _cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha, cairo_bool_t *needs_swap) { *has_alpha = TRUE; *needs_swap = FALSE; switch (pixman_format) { case PIXMAN_a8r8g8b8: *internal_format = GL_RGBA; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; return TRUE; case PIXMAN_x8r8g8b8: *internal_format = GL_RGB; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; *has_alpha = FALSE; return TRUE; case PIXMAN_a8b8g8r8: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; return TRUE; case PIXMAN_x8b8g8r8: *internal_format = GL_RGB; *format = GL_RGBA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; *has_alpha = FALSE; return TRUE; case PIXMAN_b8g8r8a8: *internal_format = GL_RGBA; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8; return TRUE; case PIXMAN_b8g8r8x8: *internal_format = GL_RGB; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8; *has_alpha = FALSE; return TRUE; case PIXMAN_r8g8b8: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_BYTE; return TRUE; case PIXMAN_b8g8r8: *internal_format = GL_RGB; *format = GL_BGR; *type = GL_UNSIGNED_BYTE; return TRUE; case PIXMAN_r5g6b5: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5; return TRUE; case PIXMAN_b5g6r5: *internal_format = GL_RGB; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5_REV; return TRUE; case PIXMAN_a1r5g5b5: *internal_format = GL_RGBA; *format = GL_BGRA; *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; return TRUE; case PIXMAN_x1r5g5b5: *internal_format = GL_RGB; *format = GL_BGRA; *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; *has_alpha = FALSE; return TRUE; case PIXMAN_a1b5g5r5: *internal_format = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; return TRUE; case PIXMAN_x1b5g5r5: *internal_format = GL_RGB; *format = GL_RGBA; *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; *has_alpha = FALSE; return TRUE; case PIXMAN_a8: *internal_format = GL_ALPHA; *format = GL_ALPHA; *type = GL_UNSIGNED_BYTE; return TRUE; case PIXMAN_a2b10g10r10: case PIXMAN_x2b10g10r10: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: case PIXMAN_c8: case PIXMAN_x4a4: /* case PIXMAN_x4c4: */ case PIXMAN_x4g4: case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: case PIXMAN_g4: case PIXMAN_a1: case PIXMAN_g1: case PIXMAN_yuy2: case PIXMAN_yv12: case PIXMAN_x2r10g10b10: case PIXMAN_a2r10g10b10: case PIXMAN_r8g8b8x8: case PIXMAN_r8g8b8a8: case PIXMAN_x14r6g6b6: default: return FALSE; } } /* * Extracts pixel data from an image surface. */ static cairo_status_t _cairo_gl_surface_extract_image_data (cairo_image_surface_t *image, int x, int y, int width, int height, void **output) { int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8; char *data = _cairo_malloc_ab (width * height, cpp); char *dst = data; unsigned char *src = image->data + y * image->stride + x * cpp; int i; if (unlikely (data == NULL)) return CAIRO_STATUS_NO_MEMORY; for (i = 0; i < height; i++) { memcpy (dst, src, width * cpp); src += image->stride; dst += width * cpp; } *output = data; return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, pixman_format_code_t pixman_format, GLenum *internal_format, GLenum *format, GLenum *type, cairo_bool_t *has_alpha, cairo_bool_t *needs_swap) { if (flavor == CAIRO_GL_FLAVOR_DESKTOP) return _cairo_gl_get_image_format_and_type_gl (pixman_format, internal_format, format, type, has_alpha, needs_swap); else return _cairo_gl_get_image_format_and_type_gles2 (pixman_format, internal_format, format, type, has_alpha, needs_swap); } cairo_bool_t _cairo_gl_operator_is_supported (cairo_operator_t op) { return op < CAIRO_OPERATOR_SATURATE; } static void _cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface) { cairo_gl_operand_t *operand = &surface->operand; cairo_surface_attributes_t *attributes = &operand->texture.attributes; memset (operand, 0, sizeof (cairo_gl_operand_t)); operand->type = CAIRO_GL_OPERAND_TEXTURE; operand->texture.surface = surface; operand->texture.tex = surface->tex; if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) { cairo_matrix_init_identity (&attributes->matrix); } else { cairo_matrix_init_scale (&attributes->matrix, 1.0 / surface->width, 1.0 / surface->height); } attributes->extend = CAIRO_EXTEND_NONE; attributes->filter = CAIRO_FILTER_NEAREST; } void _cairo_gl_surface_init (cairo_device_t *device, cairo_gl_surface_t *surface, cairo_content_t content, int width, int height) { assert (width > 0 && height > 0); _cairo_surface_init (&surface->base, &_cairo_gl_surface_backend, device, content); surface->width = width; surface->height = height; surface->needs_update = FALSE; _cairo_gl_surface_embedded_operand_init (surface); } static cairo_bool_t _cairo_gl_surface_size_valid_for_context (cairo_gl_context_t *ctx, int width, int height) { return width > 0 && height > 0 && width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size; } static cairo_bool_t _cairo_gl_surface_size_valid (cairo_gl_surface_t *surface, int width, int height) { cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; return _cairo_gl_surface_size_valid_for_context (ctx, width, height); } static cairo_surface_t * _cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, cairo_content_t content, GLuint tex, int width, int height) { cairo_gl_surface_t *surface; surface = calloc (1, sizeof (cairo_gl_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->tex = tex; _cairo_gl_surface_init (&ctx->base, surface, content, width, height); surface->supports_msaa = ctx->supports_msaa; surface->supports_stencil = TRUE; /* Create the texture used to store the surface's data. */ _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); glBindTexture (ctx->tex_target, surface->tex); glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return &surface->base; } static cairo_surface_t * _create_scratch_internal (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height, cairo_bool_t for_caching) { cairo_gl_surface_t *surface; GLenum format; GLuint tex; glGenTextures (1, &tex); surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_scratch_for_texture (ctx, content, tex, width, height); if (unlikely (surface->base.status)) return &surface->base; surface->owns_tex = TRUE; /* adjust the texture size after setting our real extents */ if (width < 1) width = 1; if (height < 1) height = 1; switch (content) { default: ASSERT_NOT_REACHED; case CAIRO_CONTENT_COLOR_ALPHA: format = GL_RGBA; break; case CAIRO_CONTENT_ALPHA: /* When using GL_ALPHA, compositing doesn't work properly, but for * caching surfaces, we are just uploading pixel data, so it isn't * an issue. */ if (for_caching) format = GL_ALPHA; else format = GL_RGBA; break; case CAIRO_CONTENT_COLOR: /* GL_RGB is almost what we want here -- sampling 1 alpha when * texturing, using 1 as destination alpha factor in blending, * etc. However, when filtering with GL_CLAMP_TO_BORDER, the * alpha channel of the border color will also be clamped to * 1, when we actually want the border color we explicitly * specified. So, we have to store RGBA, and fill the alpha * channel with 1 when blending. */ format = GL_RGBA; break; } glTexImage2D (ctx->tex_target, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); return &surface->base; } cairo_surface_t * _cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height) { return _create_scratch_internal (ctx, content, width, height, FALSE); } cairo_surface_t * _cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height) { return _create_scratch_internal (ctx, content, width, height, TRUE); } static cairo_status_t _cairo_gl_surface_clear (cairo_gl_surface_t *surface, const cairo_color_t *color) { cairo_gl_context_t *ctx; cairo_status_t status; double r, g, b, a; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return status; _cairo_gl_context_set_destination (ctx, surface, surface->msaa_active); if (surface->base.content & CAIRO_CONTENT_COLOR) { r = color->red * color->alpha; g = color->green * color->alpha; b = color->blue * color->alpha; } else { r = g = b = 0; } if (surface->base.content & CAIRO_CONTENT_ALPHA) { a = color->alpha; } else { a = 1.0; } glDisable (GL_SCISSOR_TEST); glClearColor (r, g, b, a); glClear (GL_COLOR_BUFFER_BIT); if (a == 0) surface->base.is_clear = TRUE; return _cairo_gl_context_release (ctx, status); } static cairo_surface_t * _cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx, cairo_content_t content, int width, int height) { cairo_gl_surface_t *surface; cairo_int_status_t status; surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_scratch (ctx, content, width, height); if (unlikely (surface->base.status)) return &surface->base; /* Cairo surfaces start out initialized to transparent (black) */ status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } return &surface->base; } cairo_surface_t * cairo_gl_surface_create (cairo_device_t *abstract_device, cairo_content_t content, int width, int height) { cairo_gl_context_t *ctx; cairo_gl_surface_t *surface; cairo_status_t status; if (! CAIRO_CONTENT_VALID (content)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); if (abstract_device == NULL) return _cairo_image_surface_create_with_content (content, width, height); if (abstract_device->status) return _cairo_surface_create_in_error (abstract_device->status); if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); status = _cairo_gl_context_acquire (abstract_device, &ctx); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) { status = _cairo_gl_context_release (ctx, status); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); if (unlikely (surface->base.status)) { status = _cairo_gl_context_release (ctx, surface->base.status); cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } return &surface->base; } slim_hidden_def (cairo_gl_surface_create); /** * cairo_gl_surface_create_for_texture: * @content: type of content in the surface * @tex: name of texture to use for storage of surface pixels * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a GL surface for the specified texture with the specified * content and dimensions. The texture must be kept around until the * #cairo_surface_t is destroyed or cairo_surface_finish() is called * on the surface. The initial contents of @tex will be used as the * initial image contents; you must explicitly clear the buffer, * using, for example, cairo_rectangle() and cairo_fill() if you want * it cleared. The format of @tex should be compatible with @content, * in the sense that it must have the color components required by * @content. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: TBD **/ cairo_surface_t * cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, cairo_content_t content, unsigned int tex, int width, int height) { cairo_gl_context_t *ctx; cairo_gl_surface_t *surface; cairo_status_t status; if (! CAIRO_CONTENT_VALID (content)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); if (abstract_device == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); if (abstract_device->status) return _cairo_surface_create_in_error (abstract_device->status); if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH)); status = _cairo_gl_context_acquire (abstract_device, &ctx); if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = (cairo_gl_surface_t *) _cairo_gl_surface_create_scratch_for_texture (ctx, content, tex, width, height); status = _cairo_gl_context_release (ctx, status); return &surface->base; } slim_hidden_def (cairo_gl_surface_create_for_texture); void cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_gl (abstract_surface) || _cairo_gl_surface_is_texture (surface)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (surface->width != width || surface->height != height) { surface->needs_update = TRUE; surface->width = width; surface->height = height; } } int cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; if (! _cairo_surface_is_gl (abstract_surface)) return 0; return surface->width; } int cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; if (! _cairo_surface_is_gl (abstract_surface)) return 0; return surface->height; } void cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_gl (abstract_surface)) { _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } if (! _cairo_gl_surface_is_texture (surface)) { cairo_gl_context_t *ctx; cairo_status_t status; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return; /* For swapping on EGL, at least, we need a valid context/target. */ _cairo_gl_context_set_destination (ctx, surface, FALSE); /* And in any case we should flush any pending operations. */ _cairo_gl_composite_flush (ctx); ctx->swap_buffers (ctx, surface); status = _cairo_gl_context_release (ctx, status); if (status) status = _cairo_surface_set_error (abstract_surface, status); } } static cairo_surface_t * _cairo_gl_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_surface_t *surface = abstract_surface; cairo_gl_context_t *ctx; cairo_status_t status; if (! _cairo_gl_surface_size_valid (abstract_surface, width, height)) return _cairo_image_surface_create_with_content (content, width, height); status = _cairo_gl_context_acquire (surface->device, &ctx); if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); } return surface; } static cairo_int_status_t _cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst, cairo_gl_context_t *ctx, int x, int y, int width, int height) { cairo_gl_composite_t setup; cairo_status_t status; _cairo_gl_composite_flush (ctx); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, dst, FALSE); if (unlikely (status)) goto CLEANUP; _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto CLEANUP; _cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height); status = _cairo_gl_context_release (ctx, status); CLEANUP: _cairo_gl_composite_fini (&setup); _cairo_gl_composite_flush (ctx); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); return status; } cairo_status_t _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, cairo_image_surface_t *src, int src_x, int src_y, int width, int height, int dst_x, int dst_y, cairo_bool_t force_flush) { GLenum internal_format, format, type; cairo_bool_t has_alpha, needs_swap; cairo_image_surface_t *clone = NULL; cairo_gl_context_t *ctx; int cpp; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; status = _cairo_gl_context_acquire (dst->base.device, &ctx); if (unlikely (status)) return status; if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor, src->pixman_format, &internal_format, &format, &type, &has_alpha, &needs_swap)) { cairo_bool_t is_supported; clone = _cairo_image_surface_coerce (src); if (unlikely (status = clone->base.status)) goto FAIL; is_supported = _cairo_gl_get_image_format_and_type (ctx->gl_flavor, clone->pixman_format, &internal_format, &format, &type, &has_alpha, &needs_swap); assert (is_supported); assert (!needs_swap); src = clone; } cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; if (force_flush) { status = _cairo_gl_surface_flush (&dst->base, 0); if (unlikely (status)) goto FAIL; } if (_cairo_gl_surface_is_texture (dst)) { void *data_start = src->data + src_y * src->stride + src_x * cpp; void *data_start_gles2 = NULL; /* * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the * image data ourselves in some cases. In particular, we must extract * the pixels if: * a. we don't want full-length lines or * b. the row stride cannot be handled by GL itself using a 4 byte * alignment constraint */ if (src->stride < 0 || (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && (src->width * cpp < src->stride - 3 || width != src->width))) { glPixelStorei (GL_UNPACK_ALIGNMENT, 1); status = _cairo_gl_surface_extract_image_data (src, src_x, src_y, width, height, &data_start_gles2); if (unlikely (status)) goto FAIL; data_start = data_start_gles2; } else { glPixelStorei (GL_UNPACK_ALIGNMENT, 4); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); } _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); glBindTexture (ctx->tex_target, dst->tex); glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexSubImage2D (ctx->tex_target, 0, dst_x, dst_y, width, height, format, type, data_start); free (data_start_gles2); /* If we just treated some rgb-only data as rgba, then we have to * go back and fix up the alpha channel where we filled in this * texture data. */ if (!has_alpha) { _cairo_gl_surface_fill_alpha_channel (dst, ctx, dst_x, dst_y, width, height); } } else { cairo_surface_t *tmp; tmp = _cairo_gl_surface_create_scratch (ctx, dst->base.content, width, height); if (unlikely (tmp->status)) goto FAIL; status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, src, src_x, src_y, width, height, 0, 0, force_flush); if (status == CAIRO_INT_STATUS_SUCCESS) { cairo_surface_pattern_t tmp_pattern; cairo_rectangle_int_t r; cairo_clip_t *clip; _cairo_pattern_init_for_surface (&tmp_pattern, tmp); cairo_matrix_init_translate (&tmp_pattern.base.matrix, -dst_x, -dst_y); tmp_pattern.base.filter = CAIRO_FILTER_NEAREST; tmp_pattern.base.extend = CAIRO_EXTEND_NONE; r.x = dst_x; r.y = dst_y; r.width = width; r.height = height; clip = _cairo_clip_intersect_rectangle (NULL, &r); status = _cairo_surface_paint (&dst->base, CAIRO_OPERATOR_SOURCE, &tmp_pattern.base, clip); _cairo_clip_destroy (clip); _cairo_pattern_fini (&tmp_pattern.base); } cairo_surface_destroy (tmp); } FAIL: status = _cairo_gl_context_release (ctx, status); if (clone) cairo_surface_destroy (&clone->base); return status; } static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface) { cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; return ctx->gl_flavor; } static cairo_status_t _cairo_gl_surface_finish (void *abstract_surface) { cairo_gl_surface_t *surface = abstract_surface; cairo_status_t status; cairo_gl_context_t *ctx; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return status; if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); if (ctx->current_target == surface) ctx->current_target = NULL; if (surface->fb) ctx->dispatch.DeleteFramebuffers (1, &surface->fb); if (surface->depth_stencil) ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil); if (surface->owns_tex) glDeleteTextures (1, &surface->tex); if (surface->msaa_depth_stencil) ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil); #if CAIRO_HAS_GL_SURFACE if (surface->msaa_fb) ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb); if (surface->msaa_rb) ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb); #endif _cairo_clip_destroy (surface->clip_on_stencil_buffer); return _cairo_gl_context_release (ctx, status); } static cairo_image_surface_t * _cairo_gl_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_gl_surface_t *surface = abstract_surface; cairo_image_surface_t *image; cairo_gl_context_t *ctx; GLenum format, type; pixman_format_code_t pixman_format; unsigned int cpp; cairo_bool_t flipped, mesa_invert; cairo_status_t status; int y; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) { return _cairo_image_surface_create_in_error (status); } /* Want to use a switch statement here but the compiler gets whiny. */ if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { format = GL_BGRA; pixman_format = PIXMAN_a8r8g8b8; type = GL_UNSIGNED_INT_8_8_8_8_REV; cpp = 4; } else if (surface->base.content == CAIRO_CONTENT_COLOR) { format = GL_BGRA; pixman_format = PIXMAN_x8r8g8b8; type = GL_UNSIGNED_INT_8_8_8_8_REV; cpp = 4; } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { format = GL_ALPHA; pixman_format = PIXMAN_a8; type = GL_UNSIGNED_BYTE; cpp = 1; } else { ASSERT_NOT_REACHED; return NULL; } if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) { /* If only RGBA is supported, we must download data in a compatible * format. This means that pixman will convert the data on the CPU when * interacting with other image surfaces. For ALPHA, GLES2 does not * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the * pixman image that is created has row_stride = row_width * bpp. */ if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) { cairo_bool_t little_endian = _cairo_is_little_endian (); format = GL_RGBA; if (surface->base.content == CAIRO_CONTENT_COLOR) { pixman_format = little_endian ? PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8; } else { pixman_format = little_endian ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8; } } /* GLES2 only supports GL_UNSIGNED_BYTE. */ type = GL_UNSIGNED_BYTE; cpp = 4; } image = (cairo_image_surface_t*) _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, extents->width, extents->height, -1); if (unlikely (image->base.status)) { status = _cairo_gl_context_release (ctx, status); return image; } cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y); /* If the original surface has not been modified or * is clear, we can avoid downloading data. */ if (surface->base.is_clear || surface->base.serial == 0) { status = _cairo_gl_context_release (ctx, status); return image; } /* This is inefficient, as we'd rather just read the thing without making * it the destination. But then, this is the fallback path, so let's not * fall back instead. */ _cairo_gl_composite_flush (ctx); _cairo_gl_context_set_destination (ctx, surface, FALSE); flipped = ! _cairo_gl_surface_is_texture (surface); mesa_invert = flipped && ctx->has_mesa_pack_invert; glPixelStorei (GL_PACK_ALIGNMENT, 4); if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); if (mesa_invert) glPixelStorei (GL_PACK_INVERT_MESA, 1); y = extents->y; if (flipped) y = surface->height - extents->y - extents->height; glReadPixels (extents->x, y, extents->width, extents->height, format, type, image->data); if (mesa_invert) glPixelStorei (GL_PACK_INVERT_MESA, 0); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (&image->base); return _cairo_image_surface_create_in_error (status); } /* We must invert the image manualy if we lack GL_MESA_pack_invert */ if (flipped && ! mesa_invert) { uint8_t stack[1024], *row = stack; uint8_t *top = image->data; uint8_t *bot = image->data + (image->height-1)*image->stride; if (image->stride > (int)sizeof(stack)) { row = malloc (image->stride); if (unlikely (row == NULL)) { cairo_surface_destroy (&image->base); return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } while (top < bot) { memcpy (row, top, image->stride); memcpy (top, bot, image->stride); memcpy (bot, row, image->stride); top += image->stride; bot -= image->stride; } if (row != stack) free(row); } image->base.is_clear = FALSE; return image; } static cairo_surface_t * _cairo_gl_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_gl_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } static cairo_status_t _cairo_gl_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_gl_surface_t *surface = abstract_surface; cairo_rectangle_int_t extents; *image_extra = NULL; extents.x = extents.y = 0; extents.width = surface->width; extents.height = surface->height; *image_out = (cairo_image_surface_t *) _cairo_gl_surface_map_to_image (surface, &extents); return (*image_out)->base.status; } static void _cairo_gl_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_int_status_t _cairo_gl_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_int_status_t status; status = _cairo_gl_surface_draw_image (abstract_surface, image, 0, 0, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0, TRUE); cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return status; } static cairo_bool_t _cairo_gl_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_gl_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static cairo_status_t _cairo_gl_surface_flush (void *abstract_surface, unsigned flags) { cairo_gl_surface_t *surface = abstract_surface; cairo_status_t status; cairo_gl_context_t *ctx; if (flags) return CAIRO_STATUS_SUCCESS; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return status; if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) || (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) || (ctx->current_target == surface)) _cairo_gl_composite_flush (ctx); status = _cairo_gl_surface_resolve_multisampling (surface); return _cairo_gl_context_release (ctx, status); } cairo_int_status_t _cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface) { cairo_gl_context_t *ctx; cairo_int_status_t status; if (! surface->msaa_active) return CAIRO_INT_STATUS_SUCCESS; if (surface->base.device == NULL) return CAIRO_INT_STATUS_SUCCESS; /* GLES surfaces do not need explicit resolution. */ if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES) return CAIRO_INT_STATUS_SUCCESS; if (! _cairo_gl_surface_is_texture (surface)) return CAIRO_INT_STATUS_SUCCESS; status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) return status; ctx->current_target = surface; #if CAIRO_HAS_GL_SURFACE _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE); #endif status = _cairo_gl_context_release (ctx, status); return status; } static const cairo_compositor_t * get_compositor (cairo_gl_surface_t *surface) { cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; return ctx->compositor; } static cairo_int_status_t _cairo_gl_surface_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { /* simplify the common case of clearing the surface */ if (clip == NULL) { if (op == CAIRO_OPERATOR_CLEAR) return _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); else if (source->type == CAIRO_PATTERN_TYPE_SOLID && (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) { return _cairo_gl_surface_clear (surface, &((cairo_solid_pattern_t *) source)->color); } } return _cairo_compositor_paint (get_compositor (surface), surface, op, source, clip); } static cairo_int_status_t _cairo_gl_surface_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { return _cairo_compositor_mask (get_compositor (surface), surface, op, source, mask, clip); } static cairo_int_status_t _cairo_gl_surface_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_stroke (get_compositor (surface), surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_gl_surface_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_fill (get_compositor (surface), surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_gl_surface_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *font, const cairo_clip_t *clip) { return _cairo_compositor_glyphs (get_compositor (surface), surface, op, source, glyphs, num_glyphs, font, clip); } static const cairo_surface_backend_t _cairo_gl_surface_backend = { CAIRO_SURFACE_TYPE_GL, _cairo_gl_surface_finish, _cairo_default_context_create, _cairo_gl_surface_create_similar, NULL, /* similar image */ _cairo_gl_surface_map_to_image, _cairo_gl_surface_unmap_image, _cairo_gl_surface_source, _cairo_gl_surface_acquire_source_image, _cairo_gl_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_gl_surface_get_extents, _cairo_image_surface_get_font_options, _cairo_gl_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_gl_surface_paint, _cairo_gl_surface_mask, _cairo_gl_surface_stroke, _cairo_gl_surface_fill, NULL, /* fill/stroke */ _cairo_gl_surface_glyphs, }; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl-traps-compositor.c���������������������0000664�0000000�0000000�00000037325�12710376503�0027476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005,2010 Red Hat, Inc * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Benjamin Otte <otte@gnome.org> * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> * Eric Anholt <eric@anholt.net> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-spans-compositor-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-offset-private.h" static cairo_int_status_t acquire (void *abstract_dst) { return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t release (void *abstract_dst) { return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t set_clip_region (void *_surface, cairo_region_t *region) { cairo_gl_surface_t *surface = _surface; surface->clip_region = region; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t draw_image_boxes (void *_dst, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy) { cairo_gl_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; int i; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int w = _cairo_fixed_integer_part (b->p2.x) - x; int h = _cairo_fixed_integer_part (b->p2.y) - y; cairo_status_t status; status = _cairo_gl_surface_draw_image (dst, image, x + dx, y + dy, w, h, x, y, TRUE); if (unlikely (status)) return status; } } return CAIRO_STATUS_SUCCESS; } static void emit_aligned_boxes (cairo_gl_context_t *ctx, const cairo_boxes_t *boxes) { const struct _cairo_boxes_chunk *chunk; cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx); int i; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); emit (ctx, x1, y1, x2, y2); } } } static cairo_int_status_t fill_boxes (void *_dst, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_solid_source (&setup, color); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; emit_aligned_boxes (ctx, boxes); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t composite_boxes (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, source_to_operand (abstract_src)); _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y); _cairo_gl_composite_set_mask_operand (&setup, source_to_operand (abstract_mask)); _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; emit_aligned_boxes (ctx, boxes); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t composite (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, source_to_operand (abstract_src)); _cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y); _cairo_gl_composite_set_mask_operand (&setup, source_to_operand (abstract_mask)); _cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; /* XXX clip */ _cairo_gl_context_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_int_status_t lerp (void *dst, cairo_surface_t *src, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_int_status_t status; /* we could avoid some repetition... */ status = composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, mask_x, mask_y, 0, 0, dst_x, dst_y, width, height); if (unlikely (status)) return status; status = composite (dst, CAIRO_OPERATOR_ADD, src, mask, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t traps_to_operand (void *_dst, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_traps_t *traps, cairo_gl_operand_t *operand, int dst_x, int dst_y) { pixman_format_code_t pixman_format; pixman_image_t *pixman_image; cairo_surface_t *image, *mask; cairo_surface_pattern_t pattern; cairo_status_t status; pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1; pixman_image = pixman_image_create_bits (pixman_format, extents->width, extents->height, NULL, 0); if (unlikely (pixman_image == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _pixman_image_add_traps (pixman_image, extents->x, extents->y, traps); image = _cairo_image_surface_create_for_pixman_image (pixman_image, pixman_format); if (unlikely (image->status)) { pixman_image_unref (pixman_image); return image->status; } /* GLES2 only supports RGB/RGBA when uploading */ if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) { cairo_surface_pattern_t pattern; cairo_surface_t *rgba_image; /* XXX perform this fixup inside _cairo_gl_draw_image() */ rgba_image = _cairo_image_surface_create_with_pixman_format (NULL, _cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8, extents->width, extents->height, 0); if (unlikely (rgba_image->status)) return rgba_image->status; _cairo_pattern_init_for_surface (&pattern, image); status = _cairo_surface_paint (rgba_image, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); cairo_surface_destroy (image); image = rgba_image; if (unlikely (status)) { cairo_surface_destroy (image); return status; } } mask = _cairo_surface_create_similar_scratch (_dst, CAIRO_CONTENT_COLOR_ALPHA, extents->width, extents->height); if (unlikely (mask->status)) { cairo_surface_destroy (image); return mask->status; } status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask, (cairo_image_surface_t *)image, 0, 0, extents->width, extents->height, 0, 0, TRUE); cairo_surface_destroy (image); if (unlikely (status)) goto error; _cairo_pattern_init_for_surface (&pattern, mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->x+dst_x, -extents->y+dst_y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; status = _cairo_gl_operand_init (operand, &pattern.base, _dst, &_cairo_unbounded_rectangle, &_cairo_unbounded_rectangle, FALSE); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto error; operand->texture.owns_surface = (cairo_gl_surface_t *)mask; return CAIRO_STATUS_SUCCESS; error: cairo_surface_destroy (mask); return status; } static cairo_int_status_t composite_traps (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_traps_t *traps) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_int_status_t status; status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, source_to_operand (abstract_src)); _cairo_gl_operand_translate (&setup.src, -src_x-dst_x, -src_y-dst_y); status = traps_to_operand (_dst, extents, antialias, traps, &setup.mask, dst_x, dst_y); if (unlikely (status)) goto FAIL; status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; /* XXX clip */ _cairo_gl_context_emit_rect (ctx, extents->x-dst_x, extents->y-dst_y, extents->x-dst_x+extents->width, extents->y-dst_y+extents->height); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); return status; } static cairo_gl_surface_t * tristrip_to_surface (void *_dst, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_tristrip_t *strip) { pixman_format_code_t pixman_format; pixman_image_t *pixman_image; cairo_surface_t *image, *mask; cairo_status_t status; pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, pixman_image = pixman_image_create_bits (pixman_format, extents->width, extents->height, NULL, 0); if (unlikely (pixman_image == NULL)) return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _pixman_image_add_tristrip (pixman_image, extents->x, extents->y, strip); image = _cairo_image_surface_create_for_pixman_image (pixman_image, pixman_format); if (unlikely (image->status)) { pixman_image_unref (pixman_image); return (cairo_gl_surface_t *)image; } mask = _cairo_surface_create_similar_scratch (_dst, CAIRO_CONTENT_COLOR_ALPHA, extents->width, extents->height); if (unlikely (mask->status)) { cairo_surface_destroy (image); return (cairo_gl_surface_t *)mask; } status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask, (cairo_image_surface_t *)image, 0, 0, extents->width, extents->height, 0, 0, TRUE); cairo_surface_destroy (image); if (unlikely (status)) { cairo_surface_destroy (mask); return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status); } return (cairo_gl_surface_t*)mask; } static cairo_int_status_t composite_tristrip (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_tristrip_t *strip) { cairo_gl_composite_t setup; cairo_gl_context_t *ctx; cairo_gl_surface_t *mask; cairo_int_status_t status; mask = tristrip_to_surface (_dst, extents, antialias, strip); if (unlikely (mask->base.status)) return mask->base.status; status = _cairo_gl_composite_init (&setup, op, _dst, FALSE); if (unlikely (status)) goto FAIL; _cairo_gl_composite_set_source_operand (&setup, source_to_operand (abstract_src)); //_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0); status = _cairo_gl_composite_begin (&setup, &ctx); if (unlikely (status)) goto FAIL; /* XXX clip */ _cairo_gl_context_emit_rect (ctx, dst_x, dst_y, dst_x+extents->width, dst_y+extents->height); status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); FAIL: _cairo_gl_composite_fini (&setup); cairo_surface_destroy (&mask->base); return status; } static cairo_int_status_t check_composite (const cairo_composite_rectangles_t *extents) { if (! _cairo_gl_operator_is_supported (extents->op)) return UNSUPPORTED ("unsupported operator"); return CAIRO_STATUS_SUCCESS; } const cairo_compositor_t * _cairo_gl_traps_compositor_get (void) { static cairo_traps_compositor_t compositor; if (compositor.base.delegate == NULL) { _cairo_traps_compositor_init (&compositor, &_cairo_fallback_compositor); compositor.acquire = acquire; compositor.release = release; compositor.set_clip_region = set_clip_region; compositor.pattern_to_surface = _cairo_gl_pattern_to_source; compositor.draw_image_boxes = draw_image_boxes; //compositor.copy_boxes = copy_boxes; compositor.fill_boxes = fill_boxes; compositor.check_composite = check_composite; compositor.composite = composite; compositor.lerp = lerp; //compositor.check_composite_boxes = check_composite_boxes; compositor.composite_boxes = composite_boxes; //compositor.check_composite_traps = check_composite_traps; compositor.composite_traps = composite_traps; //compositor.check_composite_tristrip = check_composite_traps; compositor.composite_tristrip = composite_tristrip; compositor.check_composite_glyphs = _cairo_gl_check_composite_glyphs; compositor.composite_glyphs = _cairo_gl_composite_glyphs; } return &compositor.base; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gl.h��������������������������������������0000664�0000000�0000000�00000011741�12710376503�0024152�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Eric Anholt. */ /* * cairo-gl.h: * * The cairo-gl backend provides an implementation of possibly * hardware-accelerated cairo rendering by targeting the OpenGL API. * The goal of the cairo-gl backend is to provide better performance * with equal functionality to cairo-image where possible. It does * not directly provide for applying additional OpenGL effects to * cairo surfaces. * * Cairo-gl allows interoperability with other GL rendering through GL * context sharing. Cairo-gl surfaces are created in reference to a * #cairo_device_t, which represents a GL context created by the user. * When that GL context is created with its sharePtr set to another * context (or vice versa), its objects (textures backing cairo-gl * surfaces) can be accessed in the other OpenGL context. This allows * cairo-gl to maintain its drawing state in one context while the * user's 3D rendering occurs in the user's other context. * * However, as only one context can be current to a thread at a time, * cairo-gl may make its context current to the thread on any cairo * call which interacts with a cairo-gl surface or the cairo-gl * device. As a result, the user must make their own context current * between any cairo calls and their own OpenGL rendering. **/ #ifndef CAIRO_GL_H #define CAIRO_GL_H #include "cairo.h" #if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_gl_surface_create (cairo_device_t *device, cairo_content_t content, int width, int height); cairo_public cairo_surface_t * cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, cairo_content_t content, unsigned int tex, int width, int height); cairo_public void cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); cairo_public int cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); cairo_public int cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); cairo_public void cairo_gl_surface_swapbuffers (cairo_surface_t *surface); cairo_public void cairo_gl_device_set_thread_aware (cairo_device_t *device, cairo_bool_t thread_aware); #if CAIRO_HAS_GLX_FUNCTIONS #include <GL/glx.h> cairo_public cairo_device_t * cairo_glx_device_create (Display *dpy, GLXContext gl_ctx); cairo_public Display * cairo_glx_device_get_display (cairo_device_t *device); cairo_public GLXContext cairo_glx_device_get_context (cairo_device_t *device); cairo_public cairo_surface_t * cairo_gl_surface_create_for_window (cairo_device_t *device, Window win, int width, int height); #endif #if CAIRO_HAS_WGL_FUNCTIONS #include <windows.h> cairo_public cairo_device_t * cairo_wgl_device_create (HGLRC rc); cairo_public HGLRC cairo_wgl_device_get_context (cairo_device_t *device); cairo_public cairo_surface_t * cairo_gl_surface_create_for_dc (cairo_device_t *device, HDC dc, int width, int height); #endif #if CAIRO_HAS_EGL_FUNCTIONS #include <EGL/egl.h> cairo_public cairo_device_t * cairo_egl_device_create (EGLDisplay dpy, EGLContext egl); cairo_public cairo_surface_t * cairo_gl_surface_create_for_egl (cairo_device_t *device, EGLSurface egl, int width, int height); cairo_public EGLDisplay cairo_egl_device_get_display (cairo_device_t *device); cairo_public EGLSurface cairo_egl_device_get_context (cairo_device_t *device); #endif CAIRO_END_DECLS #else /* CAIRO_HAS_GL_SURFACE */ # error Cairo was not compiled with support for the GL backend #endif /* CAIRO_HAS_GL_SURFACE */ #endif /* CAIRO_GL_H */ �������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-glx-context.c�����������������������������0000664�0000000�0000000�00000021623�12710376503�0026017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-error-private.h" #include <X11/Xutil.h> /* XXX needs hooking into XCloseDisplay() */ typedef struct _cairo_glx_context { cairo_gl_context_t base; Display *display; Window dummy_window; GLXContext context; GLXDrawable previous_drawable; GLXContext previous_context; cairo_bool_t has_multithread_makecurrent; } cairo_glx_context_t; typedef struct _cairo_glx_surface { cairo_gl_surface_t base; Window win; } cairo_glx_surface_t; static cairo_bool_t _context_acquisition_changed_glx_state (cairo_glx_context_t *ctx, GLXDrawable current_drawable) { return ctx->previous_drawable != current_drawable || ctx->previous_context != ctx->context; } static GLXDrawable _glx_get_current_drawable (cairo_glx_context_t *ctx) { if (ctx->base.current_target == NULL || _cairo_gl_surface_is_texture (ctx->base.current_target)) { return ctx->dummy_window; } return ((cairo_glx_surface_t *) ctx->base.current_target)->win; } static void _glx_query_current_state (cairo_glx_context_t * ctx) { ctx->previous_drawable = glXGetCurrentDrawable (); ctx->previous_context = glXGetCurrentContext (); /* If any of the values were none, assume they are all none. Not all drivers seem well behaved when it comes to using these values across multiple threads. */ if (ctx->previous_drawable == None || ctx->previous_context == None) { ctx->previous_drawable = None; ctx->previous_context = None; } } static void _glx_acquire (void *abstract_ctx) { cairo_glx_context_t *ctx = abstract_ctx; GLXDrawable current_drawable = _glx_get_current_drawable (ctx); _glx_query_current_state (ctx); if (!_context_acquisition_changed_glx_state (ctx, current_drawable)) return; glXMakeCurrent (ctx->display, current_drawable, ctx->context); } static void _glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_glx_context_t *ctx = abstract_ctx; cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; /* Set the window as the target of our context. */ glXMakeCurrent (ctx->display, surface->win, ctx->context); } static void _glx_release (void *abstract_ctx) { cairo_glx_context_t *ctx = abstract_ctx; if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware || !_context_acquisition_changed_glx_state (ctx, _glx_get_current_drawable (ctx))) { return; } glXMakeCurrent (ctx->display, None, None); } static void _glx_swap_buffers (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_glx_context_t *ctx = abstract_ctx; cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; glXSwapBuffers (ctx->display, surface->win); } static void _glx_destroy (void *abstract_ctx) { cairo_glx_context_t *ctx = abstract_ctx; if (ctx->dummy_window != None) XDestroyWindow (ctx->display, ctx->dummy_window); glXMakeCurrent (ctx->display, None, None); } static cairo_status_t _glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy) { int attr[3] = { GLX_FBCONFIG_ID, 0, None }; GLXFBConfig *config; XVisualInfo *vi; Colormap cmap; XSetWindowAttributes swa; Window win = None; int cnt; /* Create a dummy window created for the target GLX context that we can * use to query the available GL/GLX extensions. */ glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); cnt = 0; config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); if (unlikely (cnt == 0)) return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); vi = glXGetVisualFromFBConfig (dpy, config[0]); XFree (config); if (unlikely (vi == NULL)) return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); cmap = XCreateColormap (dpy, RootWindow (dpy, vi->screen), vi->visual, AllocNone); swa.colormap = cmap; swa.border_pixel = 0; win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), -1, -1, 1, 1, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap, &swa); XFreeColormap (dpy, cmap); XFree (vi); XFlush (dpy); if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { XDestroyWindow (dpy, win); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } *dummy = win; return CAIRO_STATUS_SUCCESS; } cairo_device_t * cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) { cairo_glx_context_t *ctx; cairo_status_t status; Window dummy = None; const char *glx_extensions; ctx = calloc (1, sizeof (cairo_glx_context_t)); if (unlikely (ctx == NULL)) return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); /* glx_dummy_window will call glXMakeCurrent, so we need to * query the current state of the context now. */ _glx_query_current_state (ctx); status = _glx_dummy_window (dpy, gl_ctx, &dummy); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } ctx->display = dpy; ctx->dummy_window = dummy; ctx->context = gl_ctx; ctx->base.acquire = _glx_acquire; ctx->base.release = _glx_release; ctx->base.make_current = _glx_make_current; ctx->base.swap_buffers = _glx_swap_buffers; ctx->base.destroy = _glx_destroy; status = _cairo_gl_dispatch_init (&ctx->base.dispatch, (cairo_gl_get_proc_addr_func_t) glXGetProcAddress); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } status = _cairo_gl_context_init (&ctx->base); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy)); if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) { ctx->has_multithread_makecurrent = TRUE; } ctx->base.release (ctx); return &ctx->base.base; } Display * cairo_glx_device_get_display (cairo_device_t *device) { cairo_glx_context_t *ctx; if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return NULL; } ctx = (cairo_glx_context_t *) device; return ctx->display; } GLXContext cairo_glx_device_get_context (cairo_device_t *device) { cairo_glx_context_t *ctx; if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return NULL; } ctx = (cairo_glx_context_t *) device; return ctx->context; } cairo_surface_t * cairo_gl_surface_create_for_window (cairo_device_t *device, Window win, int width, int height) { cairo_glx_surface_t *surface; if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); if (device->backend->type != CAIRO_DEVICE_TYPE_GL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); if (width <= 0 || height <= 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); surface = calloc (1, sizeof (cairo_glx_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_gl_surface_init (device, &surface->base, CAIRO_CONTENT_COLOR_ALPHA, width, height); surface->win = win; return &surface->base.base; } �������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gstate-private.h��������������������������0000664�0000000�0000000�00000027722�12710376503�0026515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_GSTATE_PRIVATE_H #define CAIRO_GSTATE_PRIVATE_H #include "cairo-clip-private.h" struct _cairo_gstate { cairo_operator_t op; double opacity; double tolerance; cairo_antialias_t antialias; cairo_stroke_style_t stroke_style; cairo_fill_rule_t fill_rule; cairo_font_face_t *font_face; cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ cairo_scaled_font_t *previous_scaled_font; /* holdover */ cairo_matrix_t font_matrix; cairo_font_options_t font_options; cairo_clip_t *clip; cairo_surface_t *target; /* The target to which all rendering is directed */ cairo_surface_t *parent_target; /* The previous target which was receiving rendering */ cairo_surface_t *original_target; /* The original target the initial gstate was created with */ /* the user is allowed to update the device after we have cached the matrices... */ cairo_observer_t device_transform_observer; cairo_matrix_t ctm; cairo_matrix_t ctm_inverse; cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */ cairo_bool_t is_identity; cairo_pattern_t *source; struct _cairo_gstate *next; }; /* cairo-gstate.c */ cairo_private cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate, cairo_surface_t *target); cairo_private void _cairo_gstate_fini (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist); cairo_private cairo_status_t _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist); cairo_private cairo_bool_t _cairo_gstate_is_group (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child); cairo_private cairo_surface_t * _cairo_gstate_get_target (cairo_gstate_t *gstate); cairo_private cairo_surface_t * _cairo_gstate_get_original_target (cairo_gstate_t *gstate); cairo_private cairo_clip_t * _cairo_gstate_get_clip (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source); cairo_private cairo_pattern_t * _cairo_gstate_get_source (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op); cairo_private cairo_operator_t _cairo_gstate_get_operator (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_opacity (cairo_gstate_t *gstate, double opacity); cairo_private double _cairo_gstate_get_opacity (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance); cairo_private double _cairo_gstate_get_tolerance (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule); cairo_private cairo_fill_rule_t _cairo_gstate_get_fill_rule (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width); cairo_private double _cairo_gstate_get_line_width (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); cairo_private cairo_line_cap_t _cairo_gstate_get_line_cap (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join); cairo_private cairo_line_join_t _cairo_gstate_get_line_join (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset); cairo_private void _cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset); cairo_private cairo_status_t _cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit); cairo_private double _cairo_gstate_get_miter_limit (cairo_gstate_t *gstate); cairo_private void _cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty); cairo_private cairo_status_t _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy); cairo_private cairo_status_t _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle); cairo_private cairo_status_t _cairo_gstate_transform (cairo_gstate_t *gstate, const cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_gstate_set_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix); cairo_private void _cairo_gstate_identity_matrix (cairo_gstate_t *gstate); cairo_private void _cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y); cairo_private void _cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy); cairo_private void _cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y); cairo_private void _cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy); cairo_private void _do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); static inline void _cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) { if (! gstate->is_identity) _do_cairo_gstate_user_to_backend (gstate, x, y); } cairo_private void _do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y); static inline void _cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y) { if (! gstate->is_identity) _do_cairo_gstate_user_to_backend_distance (gstate, x, y); } cairo_private void _do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); static inline void _cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) { if (! gstate->is_identity) _do_cairo_gstate_backend_to_user (gstate, x, y); } cairo_private void _do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y); static inline void _cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y) { if (! gstate->is_identity) _do_cairo_gstate_backend_to_user_distance (gstate, x, y); } cairo_private void _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2, cairo_bool_t *is_tight); cairo_private void _cairo_gstate_path_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_t *mask); cairo_private cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_copy_page (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_show_page (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_status_t _cairo_gstate_fill_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_status_t _cairo_gstate_in_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, double y, cairo_bool_t *inside_ret); cairo_private cairo_bool_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, double y); cairo_private cairo_bool_t _cairo_gstate_in_clip (cairo_gstate_t *gstate, double x, double y); cairo_private cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_reset_clip (cairo_gstate_t *gstate); cairo_private cairo_bool_t _cairo_gstate_clip_extents (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2); cairo_private cairo_rectangle_list_t* _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_show_surface (cairo_gstate_t *gstate, cairo_surface_t *surface, double x, double y, double width, double height); cairo_private cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, double size); cairo_private void _cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix); cairo_private void _cairo_gstate_get_font_options (cairo_gstate_t *gstate, cairo_font_options_t *options); cairo_private void _cairo_gstate_set_font_options (cairo_gstate_t *gstate, const cairo_font_options_t *options); cairo_private cairo_status_t _cairo_gstate_get_font_face (cairo_gstate_t *gstate, cairo_font_face_t **font_face); cairo_private cairo_status_t _cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, cairo_scaled_font_t **scaled_font); cairo_private cairo_status_t _cairo_gstate_get_font_extents (cairo_gstate_t *gstate, cairo_font_extents_t *extents); cairo_private cairo_status_t _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face); cairo_private cairo_status_t _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); cairo_private cairo_status_t _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_glyph_text_info_t *info); cairo_private cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_gstate_set_antialias (cairo_gstate_t *gstate, cairo_antialias_t antialias); cairo_private cairo_antialias_t _cairo_gstate_get_antialias (cairo_gstate_t *gstate); #endif /* CAIRO_GSTATE_PRIVATE_H */ ����������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-gstate.c����������������������������������0000664�0000000�0000000�00000176061�12710376503�0025041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-gstate-private.h" #include "cairo-pattern-private.h" #include "cairo-traps-private.h" #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) #define ISFINITE(x) isfinite (x) #else #define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ #endif static cairo_status_t _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other); static cairo_status_t _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); static cairo_status_t _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); static void _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_glyph_t *transformed_glyphs, int *num_transformed_glyphs, cairo_text_cluster_t *transformed_clusters); static void _cairo_gstate_update_device_transform (cairo_observer_t *observer, void *arg) { cairo_gstate_t *gstate = cairo_container_of (observer, cairo_gstate_t, device_transform_observer); gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) && _cairo_matrix_is_identity (&gstate->target->device_transform)); } cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate, cairo_surface_t *target) { VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); gstate->next = NULL; gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT; gstate->opacity = 1.; gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; gstate->antialias = CAIRO_ANTIALIAS_DEFAULT; _cairo_stroke_style_init (&gstate->stroke_style); gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; gstate->font_face = NULL; gstate->scaled_font = NULL; gstate->previous_scaled_font = NULL; cairo_matrix_init_scale (&gstate->font_matrix, CAIRO_GSTATE_DEFAULT_FONT_SIZE, CAIRO_GSTATE_DEFAULT_FONT_SIZE); _cairo_font_options_init_default (&gstate->font_options); gstate->clip = NULL; gstate->target = cairo_surface_reference (target); gstate->parent_target = NULL; gstate->original_target = cairo_surface_reference (target); gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; cairo_list_add (&gstate->device_transform_observer.link, &gstate->target->device_transform_observers); gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); cairo_matrix_init_identity (&gstate->ctm); gstate->ctm_inverse = gstate->ctm; gstate->source_ctm_inverse = gstate->ctm; gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base; /* Now that the gstate is fully initialized and ready for the eventual * _cairo_gstate_fini(), we can check for errors (and not worry about * the resource deallocation). */ return target->status; } /** * _cairo_gstate_init_copy: * * Initialize @gstate by performing a deep copy of state fields from * @other. Note that gstate->next is not copied but is set to %NULL by * this function. **/ static cairo_status_t _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) { cairo_status_t status; VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); gstate->op = other->op; gstate->opacity = other->opacity; gstate->tolerance = other->tolerance; gstate->antialias = other->antialias; status = _cairo_stroke_style_init_copy (&gstate->stroke_style, &other->stroke_style); if (unlikely (status)) return status; gstate->fill_rule = other->fill_rule; gstate->font_face = cairo_font_face_reference (other->font_face); gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font); gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font); gstate->font_matrix = other->font_matrix; _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); gstate->clip = _cairo_clip_copy (other->clip); gstate->target = cairo_surface_reference (other->target); /* parent_target is always set to NULL; it's only ever set by redirect_target */ gstate->parent_target = NULL; gstate->original_target = cairo_surface_reference (other->original_target); gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; cairo_list_add (&gstate->device_transform_observer.link, &gstate->target->device_transform_observers); gstate->is_identity = other->is_identity; gstate->ctm = other->ctm; gstate->ctm_inverse = other->ctm_inverse; gstate->source_ctm_inverse = other->source_ctm_inverse; gstate->source = cairo_pattern_reference (other->source); gstate->next = NULL; return CAIRO_STATUS_SUCCESS; } void _cairo_gstate_fini (cairo_gstate_t *gstate) { _cairo_stroke_style_fini (&gstate->stroke_style); cairo_font_face_destroy (gstate->font_face); gstate->font_face = NULL; cairo_scaled_font_destroy (gstate->previous_scaled_font); gstate->previous_scaled_font = NULL; cairo_scaled_font_destroy (gstate->scaled_font); gstate->scaled_font = NULL; _cairo_clip_destroy (gstate->clip); cairo_list_del (&gstate->device_transform_observer.link); cairo_surface_destroy (gstate->target); gstate->target = NULL; cairo_surface_destroy (gstate->parent_target); gstate->parent_target = NULL; cairo_surface_destroy (gstate->original_target); gstate->original_target = NULL; cairo_pattern_destroy (gstate->source); gstate->source = NULL; VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t))); } /** * _cairo_gstate_save: * @gstate: input/output gstate pointer * * Makes a copy of the current state of @gstate and saves it * to @gstate->next, then put the address of the newly allcated * copy into @gstate. _cairo_gstate_restore() reverses this. **/ cairo_status_t _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) { cairo_gstate_t *top; cairo_status_t status; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); top = *freelist; if (top == NULL) { top = malloc (sizeof (cairo_gstate_t)); if (unlikely (top == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else *freelist = top->next; status = _cairo_gstate_init_copy (top, *gstate); if (unlikely (status)) { top->next = *freelist; *freelist = top; return status; } top->next = *gstate; *gstate = top; return CAIRO_STATUS_SUCCESS; } /** * _cairo_gstate_restore: * @gstate: input/output gstate pointer * * Reverses the effects of one _cairo_gstate_save() call. **/ cairo_status_t _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) { cairo_gstate_t *top; top = *gstate; if (top->next == NULL) return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); *gstate = top->next; _cairo_gstate_fini (top); VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *))); top->next = *freelist; *freelist = top; return CAIRO_STATUS_SUCCESS; } /** * _cairo_gstate_redirect_target: * @gstate: a #cairo_gstate_t * @child: the new child target * * Redirect @gstate rendering to a "child" target. The original * "parent" target with which the gstate was created will not be * affected. See _cairo_gstate_get_target(). **/ cairo_status_t _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) { /* If this gstate is already redirected, this is an error; we need a * new gstate to be able to redirect */ assert (gstate->parent_target == NULL); /* Set up our new parent_target based on our current target; * gstate->parent_target will take the ref that is held by gstate->target */ gstate->parent_target = gstate->target; /* Now set up our new target; we overwrite gstate->target directly, * since its ref is now owned by gstate->parent_target */ gstate->target = cairo_surface_reference (child); gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform); cairo_list_move (&gstate->device_transform_observer.link, &gstate->target->device_transform_observers); /* The clip is in surface backend coordinates for the previous target; * translate it into the child's backend coordinates. */ _cairo_clip_destroy (gstate->clip); gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip, child->device_transform.x0 - gstate->parent_target->device_transform.x0, child->device_transform.y0 - gstate->parent_target->device_transform.y0); return CAIRO_STATUS_SUCCESS; } /** * _cairo_gstate_is_group: * @gstate: a #cairo_gstate_t * * Check if _cairo_gstate_redirect_target has been called on the head * of the stack. * * Return value: %TRUE if @gstate is redirected to a target different * than the previous state in the stack, %FALSE otherwise. **/ cairo_bool_t _cairo_gstate_is_group (cairo_gstate_t *gstate) { return gstate->parent_target != NULL; } /** * _cairo_gstate_get_target: * @gstate: a #cairo_gstate_t * * Return the current drawing target; if drawing is not redirected, * this will be the same as _cairo_gstate_get_original_target(). * * Return value: the current target surface **/ cairo_surface_t * _cairo_gstate_get_target (cairo_gstate_t *gstate) { return gstate->target; } /** * _cairo_gstate_get_original_target: * @gstate: a #cairo_gstate_t * * Return the original target with which @gstate was created. This * function always returns the original target independent of any * child target that may have been set with * _cairo_gstate_redirect_target. * * Return value: the original target surface **/ cairo_surface_t * _cairo_gstate_get_original_target (cairo_gstate_t *gstate) { return gstate->original_target; } /** * _cairo_gstate_get_clip: * @gstate: a #cairo_gstate_t * * This space left intentionally blank. * * Return value: a pointer to the gstate's #cairo_clip_t structure. **/ cairo_clip_t * _cairo_gstate_get_clip (cairo_gstate_t *gstate) { return gstate->clip; } cairo_status_t _cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source) { if (source->status) return source->status; source = cairo_pattern_reference (source); cairo_pattern_destroy (gstate->source); gstate->source = source; gstate->source_ctm_inverse = gstate->ctm_inverse; return CAIRO_STATUS_SUCCESS; } cairo_pattern_t * _cairo_gstate_get_source (cairo_gstate_t *gstate) { if (gstate->source == &_cairo_pattern_black.base) { /* do not expose the static object to the user */ gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); } return gstate->source; } cairo_status_t _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op) { gstate->op = op; return CAIRO_STATUS_SUCCESS; } cairo_operator_t _cairo_gstate_get_operator (cairo_gstate_t *gstate) { return gstate->op; } cairo_status_t _cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op) { gstate->opacity = op; return CAIRO_STATUS_SUCCESS; } double _cairo_gstate_get_opacity (cairo_gstate_t *gstate) { return gstate->opacity; } cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) { gstate->tolerance = tolerance; return CAIRO_STATUS_SUCCESS; } double _cairo_gstate_get_tolerance (cairo_gstate_t *gstate) { return gstate->tolerance; } cairo_status_t _cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) { gstate->fill_rule = fill_rule; return CAIRO_STATUS_SUCCESS; } cairo_fill_rule_t _cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) { return gstate->fill_rule; } cairo_status_t _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) { gstate->stroke_style.line_width = width; return CAIRO_STATUS_SUCCESS; } double _cairo_gstate_get_line_width (cairo_gstate_t *gstate) { return gstate->stroke_style.line_width; } cairo_status_t _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) { gstate->stroke_style.line_cap = line_cap; return CAIRO_STATUS_SUCCESS; } cairo_line_cap_t _cairo_gstate_get_line_cap (cairo_gstate_t *gstate) { return gstate->stroke_style.line_cap; } cairo_status_t _cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) { gstate->stroke_style.line_join = line_join; return CAIRO_STATUS_SUCCESS; } cairo_line_join_t _cairo_gstate_get_line_join (cairo_gstate_t *gstate) { return gstate->stroke_style.line_join; } cairo_status_t _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset) { double dash_total, on_total, off_total; int i, j; free (gstate->stroke_style.dash); gstate->stroke_style.num_dashes = num_dashes; if (gstate->stroke_style.num_dashes == 0) { gstate->stroke_style.dash = NULL; gstate->stroke_style.dash_offset = 0.0; return CAIRO_STATUS_SUCCESS; } gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double)); if (unlikely (gstate->stroke_style.dash == NULL)) { gstate->stroke_style.num_dashes = 0; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } on_total = off_total = dash_total = 0.0; for (i = j = 0; i < num_dashes; i++) { if (dash[i] < 0) return _cairo_error (CAIRO_STATUS_INVALID_DASH); if (dash[i] == 0 && i > 0 && i < num_dashes - 1) { if (dash[++i] < 0) return _cairo_error (CAIRO_STATUS_INVALID_DASH); gstate->stroke_style.dash[j-1] += dash[i]; gstate->stroke_style.num_dashes -= 2; } else gstate->stroke_style.dash[j++] = dash[i]; if (dash[i]) { dash_total += dash[i]; if ((i & 1) == 0) on_total += dash[i]; else off_total += dash[i]; } } if (dash_total == 0.0) return _cairo_error (CAIRO_STATUS_INVALID_DASH); /* An odd dash value indicate symmetric repeating, so the total * is twice as long. */ if (gstate->stroke_style.num_dashes & 1) { dash_total *= 2; on_total += off_total; } if (dash_total - on_total < CAIRO_FIXED_ERROR_DOUBLE) { /* Degenerate dash -> solid line */ free (gstate->stroke_style.dash); gstate->stroke_style.dash = NULL; gstate->stroke_style.num_dashes = 0; gstate->stroke_style.dash_offset = 0.0; return CAIRO_STATUS_SUCCESS; } /* The dashing code doesn't like a negative offset or a big positive * offset, so we compute an equivalent offset which is guaranteed to be * positive and less than twice the pattern length. */ offset = fmod (offset, dash_total); if (offset < 0.0) offset += dash_total; if (offset <= 0.0) /* Take care of -0 */ offset = 0.0; gstate->stroke_style.dash_offset = offset; return CAIRO_STATUS_SUCCESS; } void _cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dashes, int *num_dashes, double *offset) { if (dashes) { memcpy (dashes, gstate->stroke_style.dash, sizeof (double) * gstate->stroke_style.num_dashes); } if (num_dashes) *num_dashes = gstate->stroke_style.num_dashes; if (offset) *offset = gstate->stroke_style.dash_offset; } cairo_status_t _cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) { gstate->stroke_style.miter_limit = limit; return CAIRO_STATUS_SUCCESS; } double _cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) { return gstate->stroke_style.miter_limit; } void _cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { *matrix = gstate->ctm; } cairo_status_t _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) { cairo_matrix_t tmp; if (! ISFINITE (tx) || ! ISFINITE (ty)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_translate (&tmp, tx, ty); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); gstate->is_identity = FALSE; /* paranoid check against gradual numerical instability */ if (! _cairo_matrix_is_invertible (&gstate->ctm)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); cairo_matrix_init_translate (&tmp, -tx, -ty); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) { cairo_matrix_t tmp; if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (! ISFINITE (sx) || ! ISFINITE (sy)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_scale (&tmp, sx, sy); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); gstate->is_identity = FALSE; /* paranoid check against gradual numerical instability */ if (! _cairo_matrix_is_invertible (&gstate->ctm)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) { cairo_matrix_t tmp; if (angle == 0.) return CAIRO_STATUS_SUCCESS; if (! ISFINITE (angle)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_rotate (&tmp, angle); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); gstate->is_identity = FALSE; /* paranoid check against gradual numerical instability */ if (! _cairo_matrix_is_invertible (&gstate->ctm)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); cairo_matrix_init_rotate (&tmp, -angle); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_transform (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { cairo_matrix_t tmp; cairo_status_t status; if (! _cairo_matrix_is_invertible (matrix)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (_cairo_matrix_is_identity (matrix)) return CAIRO_STATUS_SUCCESS; tmp = *matrix; status = cairo_matrix_invert (&tmp); if (unlikely (status)) return status; _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm); cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); gstate->is_identity = FALSE; /* paranoid check against gradual numerical instability */ if (! _cairo_matrix_is_invertible (&gstate->ctm)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_set_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { cairo_status_t status; if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0) return CAIRO_STATUS_SUCCESS; if (! _cairo_matrix_is_invertible (matrix)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (_cairo_matrix_is_identity (matrix)) { _cairo_gstate_identity_matrix (gstate); return CAIRO_STATUS_SUCCESS; } _cairo_gstate_unset_scaled_font (gstate); gstate->ctm = *matrix; gstate->ctm_inverse = *matrix; status = cairo_matrix_invert (&gstate->ctm_inverse); assert (status == CAIRO_STATUS_SUCCESS); gstate->is_identity = FALSE; return CAIRO_STATUS_SUCCESS; } void _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { if (_cairo_matrix_is_identity (&gstate->ctm)) return; _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_identity (&gstate->ctm); cairo_matrix_init_identity (&gstate->ctm_inverse); gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); } void _cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->ctm, x, y); } void _cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy) { cairo_matrix_transform_distance (&gstate->ctm, dx, dy); } void _cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); } void _cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy) { cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); } void _do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->ctm, x, y); cairo_matrix_transform_point (&gstate->target->device_transform, x, y); } void _do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_distance (&gstate->ctm, x, y); cairo_matrix_transform_distance (&gstate->target->device_transform, x, y); } void _do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y); cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); } void _do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y) { cairo_matrix_transform_distance (&gstate->target->device_transform_inverse, x, y); cairo_matrix_transform_distance (&gstate->ctm_inverse, x, y); } void _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2, cairo_bool_t *is_tight) { cairo_matrix_t matrix_inverse; if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) || ! _cairo_matrix_is_identity (&gstate->ctm_inverse)) { cairo_matrix_multiply (&matrix_inverse, &gstate->target->device_transform_inverse, &gstate->ctm_inverse); _cairo_matrix_transform_bounding_box (&matrix_inverse, x1, y1, x2, y2, is_tight); } else { if (is_tight) *is_tight = TRUE; } } /* XXX: NYI cairo_status_t _cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) { cairo_status_t status; _cairo_pen_init (&gstate); return CAIRO_STATUS_SUCCESS; } */ void _cairo_gstate_path_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_box_t box; double px1, py1, px2, py2; if (_cairo_path_fixed_extents (path, &box)) { px1 = _cairo_fixed_to_double (box.p1.x); py1 = _cairo_fixed_to_double (box.p1.y); px2 = _cairo_fixed_to_double (box.p2.x); py2 = _cairo_fixed_to_double (box.p2.y); _cairo_gstate_backend_to_user_rectangle (gstate, &px1, &py1, &px2, &py2, NULL); } else { px1 = 0.0; py1 = 0.0; px2 = 0.0; py2 = 0.0; } if (x1) *x1 = px1; if (y1) *y1 = py1; if (x2) *x2 = px2; if (y2) *y2 = py2; } static void _cairo_gstate_copy_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original) { /* First check if the we can replace the original with a much simpler * pattern. For example, gradients that are uniform or just have a single * stop can sometimes be replaced with a solid. */ if (_cairo_pattern_is_clear (original)) { _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, CAIRO_COLOR_TRANSPARENT); return; } if (original->type == CAIRO_PATTERN_TYPE_LINEAR || original->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_color_t color; if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original, NULL, &color)) { _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, &color); return; } } _cairo_pattern_init_static_copy (pattern, original); } static void _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern, const cairo_pattern_t *original, const cairo_matrix_t *ctm_inverse) { _cairo_gstate_copy_pattern (pattern, original); /* apply device_transform first so that it is transformed by ctm_inverse */ if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern; cairo_surface_t *surface; surface_pattern = (cairo_surface_pattern_t *) original; surface = surface_pattern->surface; if (_cairo_surface_has_device_transform (surface)) _cairo_pattern_transform (pattern, &surface->device_transform); } if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); if (_cairo_surface_has_device_transform (gstate->target)) { _cairo_pattern_transform (pattern, &gstate->target->device_transform_inverse); } } static void _cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, cairo_pattern_t *pattern) { _cairo_gstate_copy_transformed_pattern (gstate, pattern, gstate->source, &gstate->source_ctm_inverse); } static void _cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, cairo_pattern_t *pattern, cairo_pattern_t *mask) { _cairo_gstate_copy_transformed_pattern (gstate, pattern, mask, &gstate->ctm_inverse); } static cairo_operator_t _reduce_op (cairo_gstate_t *gstate) { cairo_operator_t op; const cairo_pattern_t *pattern; op = gstate->op; if (op != CAIRO_OPERATOR_SOURCE) return op; pattern = gstate->source; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; if (solid->color.alpha_short <= 0x00ff) { op = CAIRO_OPERATOR_CLEAR; } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) { if ((solid->color.red_short | solid->color.green_short | solid->color.blue_short) <= 0x00ff) { op = CAIRO_OPERATOR_CLEAR; } } } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; if (surface->surface->is_clear && surface->surface->content & CAIRO_CONTENT_ALPHA) { op = CAIRO_OPERATOR_CLEAR; } } else { const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; if (gradient->n_stops == 0) op = CAIRO_OPERATOR_CLEAR; } return op; } static cairo_status_t _cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern) { if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH && ((const cairo_mesh_pattern_t *) pattern)->current_patch)) { /* If current patch != NULL, the pattern is under construction * and cannot be used as a source */ return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION; } return pattern->status; } cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate) { cairo_pattern_union_t source_pattern; const cairo_pattern_t *pattern; cairo_status_t status; cairo_operator_t op; status = _cairo_gstate_get_pattern_status (gstate->source); if (unlikely (status)) return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; op = _reduce_op (gstate); if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_clear.base; } else { _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); pattern = &source_pattern.base; } return _cairo_surface_paint (gstate->target, op, pattern, gstate->clip); } cairo_status_t _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_t *mask) { cairo_pattern_union_t source_pattern, mask_pattern; const cairo_pattern_t *source; cairo_operator_t op; cairo_status_t status; status = _cairo_gstate_get_pattern_status (mask); if (unlikely (status)) return status; status = _cairo_gstate_get_pattern_status (gstate->source); if (unlikely (status)) return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; assert (gstate->opacity == 1.0); if (_cairo_pattern_is_opaque (mask, NULL)) return _cairo_gstate_paint (gstate); if (_cairo_pattern_is_clear (mask) && _cairo_operator_bounded_by_mask (gstate->op)) { return CAIRO_STATUS_SUCCESS; } op = _reduce_op (gstate); if (op == CAIRO_OPERATOR_CLEAR) { source = &_cairo_pattern_clear.base; } else { _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); source = &source_pattern.base; } _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); if (source->type == CAIRO_PATTERN_TYPE_SOLID && mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && _cairo_operator_bounded_by_source (op)) { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; cairo_color_t combined; if (mask_pattern.base.has_component_alpha) { #define M(R, A, B, c) R.c = A.c * B.c M(combined, solid->color, mask_pattern.solid.color, red); M(combined, solid->color, mask_pattern.solid.color, green); M(combined, solid->color, mask_pattern.solid.color, blue); M(combined, solid->color, mask_pattern.solid.color, alpha); #undef M } else { combined = solid->color; _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); } _cairo_pattern_init_solid (&source_pattern.solid, &combined); status = _cairo_surface_paint (gstate->target, op, &source_pattern.base, gstate->clip); } else { status = _cairo_surface_mask (gstate->target, op, source, &mask_pattern.base, gstate->clip); } return status; } cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_pattern_union_t source_pattern; cairo_stroke_style_t style; double dash[2]; cairo_status_t status; status = _cairo_gstate_get_pattern_status (gstate->source); if (unlikely (status)) return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (gstate->stroke_style.line_width <= 0.0) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; assert (gstate->opacity == 1.0); memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) { style.dash = dash; _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, &style.dash_offset, style.dash, &style.num_dashes); } _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); return _cairo_surface_stroke (gstate->target, gstate->op, &source_pattern.base, path, &style, &gstate->ctm, &gstate->ctm_inverse, gstate->tolerance, gstate->antialias, gstate->clip); } cairo_status_t _cairo_gstate_in_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, double y, cairo_bool_t *inside_ret) { cairo_status_t status; cairo_rectangle_int_t extents; cairo_box_t limit; cairo_traps_t traps; if (gstate->stroke_style.line_width <= 0.0) { *inside_ret = FALSE; return CAIRO_STATUS_SUCCESS; } _cairo_gstate_user_to_backend (gstate, &x, &y); /* Before we perform the expensive stroke analysis, * check whether the point is within the extents of the path. */ _cairo_path_fixed_approximate_stroke_extents (path, &gstate->stroke_style, &gstate->ctm, &extents); if (x < extents.x || x > extents.x + extents.width || y < extents.y || y > extents.y + extents.height) { *inside_ret = FALSE; return CAIRO_STATUS_SUCCESS; } limit.p1.x = _cairo_fixed_from_double (x) - 1; limit.p1.y = _cairo_fixed_from_double (y) - 1; limit.p2.x = limit.p1.x + 2; limit.p2.y = limit.p1.y + 2; _cairo_traps_init (&traps); _cairo_traps_limit (&traps, &limit, 1); status = _cairo_path_fixed_stroke_polygon_to_traps (path, &gstate->stroke_style, &gstate->ctm, &gstate->ctm_inverse, gstate->tolerance, &traps); if (unlikely (status)) goto BAIL; *inside_ret = _cairo_traps_contain (&traps, x, y); BAIL: _cairo_traps_fini (&traps); return status; } cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { cairo_status_t status; status = _cairo_gstate_get_pattern_status (gstate->source); if (unlikely (status)) return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; assert (gstate->opacity == 1.0); if (_cairo_path_fixed_fill_is_empty (path)) { if (_cairo_operator_bounded_by_mask (gstate->op)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_paint (gstate->target, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, gstate->clip); } else { cairo_pattern_union_t source_pattern; const cairo_pattern_t *pattern; cairo_operator_t op; cairo_rectangle_int_t extents; cairo_box_t box; op = _reduce_op (gstate); if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_clear.base; } else { _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); pattern = &source_pattern.base; } /* Toolkits often paint the entire background with a fill */ if (_cairo_surface_get_extents (gstate->target, &extents) && _cairo_path_fixed_is_box (path, &box) && box.p1.x <= _cairo_fixed_from_int (extents.x) && box.p1.y <= _cairo_fixed_from_int (extents.y) && box.p2.x >= _cairo_fixed_from_int (extents.x + extents.width) && box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height)) { status = _cairo_surface_paint (gstate->target, op, pattern, gstate->clip); } else { status = _cairo_surface_fill (gstate->target, op, pattern, path, gstate->fill_rule, gstate->tolerance, gstate->antialias, gstate->clip); } } return status; } cairo_bool_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double x, double y) { _cairo_gstate_user_to_backend (gstate, &x, &y); return _cairo_path_fixed_in_fill (path, gstate->fill_rule, gstate->tolerance, x, y); } cairo_bool_t _cairo_gstate_in_clip (cairo_gstate_t *gstate, double x, double y) { cairo_clip_t *clip = gstate->clip; int i; if (_cairo_clip_is_all_clipped (clip)) return FALSE; if (clip == NULL) return TRUE; _cairo_gstate_user_to_backend (gstate, &x, &y); if (x < clip->extents.x || x >= clip->extents.x + clip->extents.width || y < clip->extents.y || y >= clip->extents.y + clip->extents.height) { return FALSE; } if (clip->num_boxes) { int fx, fy; fx = _cairo_fixed_from_double (x); fy = _cairo_fixed_from_double (y); for (i = 0; i < clip->num_boxes; i++) { if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x && fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y) break; } if (i == clip->num_boxes) return FALSE; } if (clip->path) { cairo_clip_path_t *clip_path = clip->path; do { if (! _cairo_path_fixed_in_fill (&clip_path->path, clip_path->fill_rule, clip_path->tolerance, x, y)) return FALSE; } while ((clip_path = clip_path->prev) != NULL); } return TRUE; } cairo_status_t _cairo_gstate_copy_page (cairo_gstate_t *gstate) { cairo_surface_copy_page (gstate->target); return cairo_surface_status (gstate->target); } cairo_status_t _cairo_gstate_show_page (cairo_gstate_t *gstate) { cairo_surface_show_page (gstate->target); return cairo_surface_status (gstate->target); } static void _cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate, const cairo_box_t *extents, double *x1, double *y1, double *x2, double *y2) { double px1, py1, px2, py2; px1 = _cairo_fixed_to_double (extents->p1.x); py1 = _cairo_fixed_to_double (extents->p1.y); px2 = _cairo_fixed_to_double (extents->p2.x); py2 = _cairo_fixed_to_double (extents->p2.y); _cairo_gstate_backend_to_user_rectangle (gstate, &px1, &py1, &px2, &py2, NULL); if (x1) *x1 = px1; if (y1) *y1 = py1; if (x2) *x2 = px2; if (y2) *y2 = py2; } cairo_status_t _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_int_status_t status; cairo_box_t extents; cairo_bool_t empty; if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; if (gstate->stroke_style.line_width <= 0.0) return CAIRO_STATUS_SUCCESS; status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, &gstate->stroke_style, &gstate->ctm, gstate->antialias, &boxes); empty = boxes.num_boxes == 0; if (! empty) _cairo_boxes_extents (&boxes, &extents); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_polygon_t polygon; _cairo_polygon_init (&polygon, NULL, 0); status = _cairo_path_fixed_stroke_to_polygon (path, &gstate->stroke_style, &gstate->ctm, &gstate->ctm_inverse, gstate->tolerance, &polygon); empty = polygon.num_edges == 0; if (! empty) extents = polygon.extents; _cairo_polygon_fini (&polygon); } if (! empty) { _cairo_gstate_extents_to_user_rectangle (gstate, &extents, x1, y1, x2, y2); } return status; } cairo_status_t _cairo_gstate_fill_extents (cairo_gstate_t *gstate, cairo_path_fixed_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; cairo_box_t extents; cairo_bool_t empty; if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; if (_cairo_path_fixed_fill_is_empty (path)) return CAIRO_STATUS_SUCCESS; if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, gstate->fill_rule, gstate->antialias, &boxes); empty = boxes.num_boxes == 0; if (! empty) _cairo_boxes_extents (&boxes, &extents); _cairo_boxes_fini (&boxes); } else { cairo_traps_t traps; _cairo_traps_init (&traps); status = _cairo_path_fixed_fill_to_traps (path, gstate->fill_rule, gstate->tolerance, &traps); empty = traps.num_traps == 0; if (! empty) _cairo_traps_extents (&traps, &extents); _cairo_traps_fini (&traps); } if (! empty) { _cairo_gstate_extents_to_user_rectangle (gstate, &extents, x1, y1, x2, y2); } return status; } cairo_status_t _cairo_gstate_reset_clip (cairo_gstate_t *gstate) { _cairo_clip_destroy (gstate->clip); gstate->clip = NULL; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { gstate->clip = _cairo_clip_intersect_path (gstate->clip, path, gstate->fill_rule, gstate->tolerance, gstate->antialias); /* XXX */ return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, cairo_rectangle_int_t *extents) { cairo_bool_t is_bounded; is_bounded = _cairo_surface_get_extents (gstate->target, extents); if (gstate->clip) { _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (gstate->clip)); is_bounded = TRUE; } return is_bounded; } cairo_bool_t _cairo_gstate_clip_extents (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2) { cairo_rectangle_int_t extents; double px1, py1, px2, py2; if (! _cairo_gstate_int_clip_extents (gstate, &extents)) return FALSE; px1 = extents.x; py1 = extents.y; px2 = extents.x + (int) extents.width; py2 = extents.y + (int) extents.height; _cairo_gstate_backend_to_user_rectangle (gstate, &px1, &py1, &px2, &py2, NULL); if (x1) *x1 = px1; if (y1) *y1 = py1; if (x2) *x2 = px2; if (y2) *y2 = py2; return TRUE; } cairo_rectangle_list_t* _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) { cairo_rectangle_int_t extents; cairo_rectangle_list_t *list; cairo_clip_t *clip; if (_cairo_surface_get_extents (gstate->target, &extents)) clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents); else clip = gstate->clip; list = _cairo_clip_copy_rectangle_list (clip, gstate); if (clip != gstate->clip) _cairo_clip_destroy (clip); return list; } static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) { if (gstate->scaled_font == NULL) return; if (gstate->previous_scaled_font != NULL) cairo_scaled_font_destroy (gstate->previous_scaled_font); gstate->previous_scaled_font = gstate->scaled_font; gstate->scaled_font = NULL; } cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, double size) { _cairo_gstate_unset_scaled_font (gstate); cairo_matrix_init_scale (&gstate->font_matrix, size, size); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, const cairo_matrix_t *matrix) { if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0) return CAIRO_STATUS_SUCCESS; _cairo_gstate_unset_scaled_font (gstate); gstate->font_matrix = *matrix; return CAIRO_STATUS_SUCCESS; } void _cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { *matrix = gstate->font_matrix; } void _cairo_gstate_set_font_options (cairo_gstate_t *gstate, const cairo_font_options_t *options) { if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0) return; _cairo_gstate_unset_scaled_font (gstate); _cairo_font_options_init_copy (&gstate->font_options, options); } void _cairo_gstate_get_font_options (cairo_gstate_t *gstate, cairo_font_options_t *options) { *options = gstate->font_options; } cairo_status_t _cairo_gstate_get_font_face (cairo_gstate_t *gstate, cairo_font_face_t **font_face) { cairo_status_t status; status = _cairo_gstate_ensure_font_face (gstate); if (unlikely (status)) return status; *font_face = gstate->font_face; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, cairo_scaled_font_t **scaled_font) { cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; *scaled_font = gstate->scaled_font; return CAIRO_STATUS_SUCCESS; } /* * Like everything else in this file, fonts involve Too Many Coordinate Spaces; * it is easy to get confused about what's going on. * * The user's view * --------------- * * Users ask for things in user space. When cairo starts, a user space unit * is about 1/96 inch, which is similar to (but importantly different from) * the normal "point" units most users think in terms of. When a user * selects a font, its scale is set to "one user unit". The user can then * independently scale the user coordinate system *or* the font matrix, in * order to adjust the rendered size of the font. * * Metrics are returned in user space, whether they are obtained from * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t * which is a font specialized to a particular scale matrix, CTM, and target * surface. * * The font's view * --------------- * * Fonts are designed and stored (in say .ttf files) in "font space", which * describes an "EM Square" (a design tile) and has some abstract number * such as 1000, 1024, or 2048 units per "EM". This is basically an * uninteresting space for us, but we need to remember that it exists. * * Font resources (from libraries or operating systems) render themselves * to a particular device. Since they do not want to make most programmers * worry about the font design space, the scaling API is simplified to * involve just telling the font the required pixel size of the EM square * (that is, in device space). * * * Cairo's gstate view * ------------------- * * In addition to the CTM and CTM inverse, we keep a matrix in the gstate * called the "font matrix" which describes the user's most recent * font-scaling or font-transforming request. This is kept in terms of an * abstract scale factor, composed with the CTM and used to set the font's * pixel size. So if the user asks to "scale the font by 12", the matrix * is: * * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] * * It is an affine matrix, like all cairo matrices, where its tx and ty * components are used to "nudging" fonts around and are handled in gstate * and then ignored by the "scaled-font" layer. * * In order to perform any action on a font, we must build an object * called a #cairo_font_scale_t; this contains the central 2x2 matrix * resulting from "font matrix * CTM" (sans the font matrix translation * components as stated in the previous paragraph). * * We pass this to the font when making requests of it, which causes it to * reply for a particular [user request, device] combination, under the CTM * (to accommodate the "zoom in" == "bigger fonts" issue above). * * The other terms in our communication with the font are therefore in * device space. When we ask it to perform text->glyph conversion, it will * produce a glyph string in device space. Glyph vectors we pass to it for * measuring or rendering should be in device space. The metrics which we * get back from the font will be in device space. The contents of the * global glyph image cache will be in device space. * * * Cairo's public view * ------------------- * * Since the values entering and leaving via public API calls are in user * space, the gstate functions typically need to multiply arguments by the * CTM (for user-input glyph vectors), and return values by the CTM inverse * (for font responses such as metrics or glyph vectors). * */ static cairo_status_t _cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) { cairo_font_face_t *font_face; if (gstate->font_face != NULL) return gstate->font_face->status; font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, CAIRO_FONT_SLANT_DEFAULT, CAIRO_FONT_WEIGHT_DEFAULT); if (font_face->status) return font_face->status; gstate->font_face = font_face; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) { cairo_status_t status; cairo_font_options_t options; cairo_scaled_font_t *scaled_font; if (gstate->scaled_font != NULL) return gstate->scaled_font->status; status = _cairo_gstate_ensure_font_face (gstate); if (unlikely (status)) return status; cairo_surface_get_font_options (gstate->target, &options); cairo_font_options_merge (&options, &gstate->font_options); scaled_font = cairo_scaled_font_create (gstate->font_face, &gstate->font_matrix, &gstate->ctm, &options); status = cairo_scaled_font_status (scaled_font); if (unlikely (status)) return status; gstate->scaled_font = scaled_font; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_get_font_extents (cairo_gstate_t *gstate, cairo_font_extents_t *extents) { cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; cairo_scaled_font_extents (gstate->scaled_font, extents); return cairo_scaled_font_status (gstate->scaled_font); } cairo_status_t _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face) { if (font_face && font_face->status) return _cairo_error (font_face->status); if (font_face == gstate->font_face) return CAIRO_STATUS_SUCCESS; cairo_font_face_destroy (gstate->font_face); gstate->font_face = cairo_font_face_reference (font_face); _cairo_gstate_unset_scaled_font (gstate); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) { cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; cairo_scaled_font_glyph_extents (gstate->scaled_font, glyphs, num_glyphs, extents); return cairo_scaled_font_status (gstate->scaled_font); } cairo_status_t _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_glyph_text_info_t *info) { cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; cairo_pattern_union_t source_pattern; cairo_glyph_t *transformed_glyphs; const cairo_pattern_t *pattern; cairo_text_cluster_t *transformed_clusters; cairo_operator_t op; cairo_status_t status; status = _cairo_gstate_get_pattern_status (gstate->source); if (unlikely (status)) return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; transformed_glyphs = stack_transformed_glyphs; transformed_clusters = stack_transformed_clusters; if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { transformed_glyphs = cairo_glyph_allocate (num_glyphs); if (unlikely (transformed_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } if (info != NULL) { if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { transformed_clusters = cairo_text_cluster_allocate (info->num_clusters); if (unlikely (transformed_clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_GLYPHS; } } _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs, info->clusters, info->num_clusters, info->cluster_flags, transformed_glyphs, &num_glyphs, transformed_clusters); } else { _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs, NULL, 0, 0, transformed_glyphs, &num_glyphs, NULL); } if (num_glyphs == 0) goto CLEANUP_GLYPHS; op = _reduce_op (gstate); if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_clear.base; } else { _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); pattern = &source_pattern.base; } /* For really huge font sizes, we can just do path;fill instead of * show_glyphs, as show_glyphs would put excess pressure on the cache, * and moreover, not all components below us correctly handle huge font * sizes. I wanted to set the limit at 256. But alas, seems like cairo's * rasterizer is something like ten times slower than freetype's for huge * sizes. So, no win just yet. For now, do it for insanely-huge sizes, * just to make sure we don't make anyone unhappy. When we get a really * fast rasterizer in cairo, we may want to readjust this. * * Needless to say, do this only if show_text_glyphs is not available. */ if (cairo_surface_has_show_text_glyphs (gstate->target) || _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) { if (info != NULL) { status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, info->utf8, info->utf8_len, transformed_glyphs, num_glyphs, transformed_clusters, info->num_clusters, info->cluster_flags, gstate->scaled_font, gstate->clip); } else { status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, NULL, 0, transformed_glyphs, num_glyphs, NULL, 0, 0, gstate->scaled_font, gstate->clip); } } else { cairo_path_fixed_t path; _cairo_path_fixed_init (&path); status = _cairo_scaled_font_glyph_path (gstate->scaled_font, transformed_glyphs, num_glyphs, &path); if (status == CAIRO_STATUS_SUCCESS) { status = _cairo_surface_fill (gstate->target, op, pattern, &path, CAIRO_FILL_RULE_WINDING, gstate->tolerance, gstate->scaled_font->options.antialias, gstate->clip); } _cairo_path_fixed_fini (&path); } CLEANUP_GLYPHS: if (transformed_glyphs != stack_transformed_glyphs) cairo_glyph_free (transformed_glyphs); if (transformed_clusters != stack_transformed_clusters) cairo_text_cluster_free (transformed_clusters); return status; } cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path) { cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_glyph_t *transformed_glyphs; cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { transformed_glyphs = stack_transformed_glyphs; } else { transformed_glyphs = cairo_glyph_allocate (num_glyphs); if (unlikely (transformed_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs, NULL, 0, 0, transformed_glyphs, &num_glyphs, NULL); status = _cairo_scaled_font_glyph_path (gstate->scaled_font, transformed_glyphs, num_glyphs, path); if (transformed_glyphs != stack_transformed_glyphs) cairo_glyph_free (transformed_glyphs); return status; } cairo_status_t _cairo_gstate_set_antialias (cairo_gstate_t *gstate, cairo_antialias_t antialias) { gstate->antialias = antialias; return CAIRO_STATUS_SUCCESS; } cairo_antialias_t _cairo_gstate_get_antialias (cairo_gstate_t *gstate) { return gstate->antialias; } /** * _cairo_gstate_transform_glyphs_to_backend: * @gstate: a #cairo_gstate_t * @glyphs: the array of #cairo_glyph_t objects to be transformed * @num_glyphs: the number of elements in @glyphs * @transformed_glyphs: a pre-allocated array of at least @num_glyphs * #cairo_glyph_t objects * @num_transformed_glyphs: the number of elements in @transformed_glyphs * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be * dropped * * Transform an array of glyphs to backend space by first adding the offset * of the font matrix, then transforming from user space to backend space. * The result of the transformation is placed in @transformed_glyphs. * * This also uses information from the scaled font and the surface to * cull/drop glyphs that will not be visible. **/ static void _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_glyph_t *transformed_glyphs, int *num_transformed_glyphs, cairo_text_cluster_t *transformed_clusters) { cairo_rectangle_int_t surface_extents; cairo_matrix_t *ctm = &gstate->ctm; cairo_matrix_t *font_matrix = &gstate->font_matrix; cairo_matrix_t *device_transform = &gstate->target->device_transform; cairo_bool_t drop = FALSE; double x1 = 0, x2 = 0, y1 = 0, y2 = 0; int i, j, k; drop = FALSE; if (!drop) *num_transformed_glyphs = num_glyphs; #define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) j = 0; if (_cairo_matrix_is_identity (ctm) && _cairo_matrix_is_identity (device_transform) && font_matrix->x0 == 0 && font_matrix->y0 == 0) { if (! drop) { memcpy (transformed_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); memcpy (transformed_clusters, clusters, num_clusters * sizeof (cairo_text_cluster_t)); j = num_glyphs; } else if (num_clusters == 0) { for (i = 0; i < num_glyphs; i++) { transformed_glyphs[j].index = glyphs[i].index; transformed_glyphs[j].x = glyphs[i].x; transformed_glyphs[j].y = glyphs[i].y; if (KEEP_GLYPH (transformed_glyphs[j])) j++; } } else { const cairo_glyph_t *cur_glyph; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph = glyphs + num_glyphs - 1; else cur_glyph = glyphs; for (i = 0; i < num_clusters; i++) { cairo_bool_t cluster_visible = FALSE; for (k = 0; k < clusters[i].num_glyphs; k++) { transformed_glyphs[j+k].index = cur_glyph->index; transformed_glyphs[j+k].x = cur_glyph->x; transformed_glyphs[j+k].y = cur_glyph->y; if (KEEP_GLYPH (transformed_glyphs[j+k])) cluster_visible = TRUE; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph--; else cur_glyph++; } transformed_clusters[i] = clusters[i]; if (cluster_visible) j += k; else transformed_clusters[i].num_glyphs = 0; } } } else if (_cairo_matrix_is_translation (ctm) && _cairo_matrix_is_translation (device_transform)) { double tx = font_matrix->x0 + ctm->x0 + device_transform->x0; double ty = font_matrix->y0 + ctm->y0 + device_transform->y0; if (! drop || num_clusters == 0) { for (i = 0; i < num_glyphs; i++) { transformed_glyphs[j].index = glyphs[i].index; transformed_glyphs[j].x = glyphs[i].x + tx; transformed_glyphs[j].y = glyphs[i].y + ty; if (!drop || KEEP_GLYPH (transformed_glyphs[j])) j++; } memcpy (transformed_clusters, clusters, num_clusters * sizeof (cairo_text_cluster_t)); } else { const cairo_glyph_t *cur_glyph; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph = glyphs + num_glyphs - 1; else cur_glyph = glyphs; for (i = 0; i < num_clusters; i++) { cairo_bool_t cluster_visible = FALSE; for (k = 0; k < clusters[i].num_glyphs; k++) { transformed_glyphs[j+k].index = cur_glyph->index; transformed_glyphs[j+k].x = cur_glyph->x + tx; transformed_glyphs[j+k].y = cur_glyph->y + ty; if (KEEP_GLYPH (transformed_glyphs[j+k])) cluster_visible = TRUE; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph--; else cur_glyph++; } transformed_clusters[i] = clusters[i]; if (cluster_visible) j += k; else transformed_clusters[i].num_glyphs = 0; } } } else { cairo_matrix_t aggregate_transform; cairo_matrix_init_translate (&aggregate_transform, gstate->font_matrix.x0, gstate->font_matrix.y0); cairo_matrix_multiply (&aggregate_transform, &aggregate_transform, ctm); cairo_matrix_multiply (&aggregate_transform, &aggregate_transform, device_transform); if (! drop || num_clusters == 0) { for (i = 0; i < num_glyphs; i++) { transformed_glyphs[j] = glyphs[i]; cairo_matrix_transform_point (&aggregate_transform, &transformed_glyphs[j].x, &transformed_glyphs[j].y); if (! drop || KEEP_GLYPH (transformed_glyphs[j])) j++; } memcpy (transformed_clusters, clusters, num_clusters * sizeof (cairo_text_cluster_t)); } else { const cairo_glyph_t *cur_glyph; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph = glyphs + num_glyphs - 1; else cur_glyph = glyphs; for (i = 0; i < num_clusters; i++) { cairo_bool_t cluster_visible = FALSE; for (k = 0; k < clusters[i].num_glyphs; k++) { transformed_glyphs[j+k] = *cur_glyph; cairo_matrix_transform_point (&aggregate_transform, &transformed_glyphs[j+k].x, &transformed_glyphs[j+k].y); if (KEEP_GLYPH (transformed_glyphs[j+k])) cluster_visible = TRUE; if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) cur_glyph--; else cur_glyph++; } transformed_clusters[i] = clusters[i]; if (cluster_visible) j += k; else transformed_clusters[i].num_glyphs = 0; } } } *num_transformed_glyphs = j; if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) { for (i = 0; i < --j; i++) { cairo_glyph_t tmp; tmp = transformed_glyphs[i]; transformed_glyphs[i] = transformed_glyphs[j]; transformed_glyphs[j] = tmp; } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-hash-private.h����������������������������0000664�0000000�0000000�00000005761�12710376503�0026150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc. * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Keith Packard <keithp@keithp.com> * Graydon Hoare <graydon@redhat.com> * Carl Worth <cworth@cworth.org> */ #ifndef CAIRO_HASH_PRIVATE_H #define CAIRO_HASH_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" /* XXX: I'd like this file to be self-contained in terms of * includeability, but that's not really possible with the current * monolithic cairoint.h. So, for now, just include cairoint.h instead * if you want to include this file. */ typedef cairo_bool_t (*cairo_hash_keys_equal_func_t) (const void *key_a, const void *key_b); typedef cairo_bool_t (*cairo_hash_predicate_func_t) (const void *entry); typedef void (*cairo_hash_callback_func_t) (void *entry, void *closure); cairo_private cairo_hash_table_t * _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal); cairo_private void _cairo_hash_table_destroy (cairo_hash_table_t *hash_table); cairo_private void * _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key); cairo_private void * _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, cairo_hash_predicate_func_t predicate); cairo_private cairo_status_t _cairo_hash_table_insert (cairo_hash_table_t *hash_table, cairo_hash_entry_t *entry); cairo_private void _cairo_hash_table_remove (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key); cairo_private void _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, cairo_hash_callback_func_t hash_callback, void *closure); #endif ���������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-hash.c������������������������������������0000664�0000000�0000000�00000041753�12710376503�0024474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc. * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Keith Packard <keithp@keithp.com> * Graydon Hoare <graydon@redhat.com> * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" /* * An entry can be in one of three states: * * FREE: Entry has never been used, terminates all searches. * Appears in the table as a %NULL pointer. * * DEAD: Entry had been live in the past. A dead entry can be reused * but does not terminate a search for an exact entry. * Appears in the table as a pointer to DEAD_ENTRY. * * LIVE: Entry is currently being used. * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer. */ #define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1) #define ENTRY_IS_FREE(entry) ((entry) == NULL) #define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) #define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) /* * This table is open-addressed with double hashing. Each table size * is a prime and it makes for the "first" hash modulus; a second * prime (2 less than the first prime) serves as the "second" hash * modulus, which is smaller and thus guarantees a complete * permutation of table indices. * * Hash tables are rehashed in order to keep between 12.5% and 50% * entries in the hash table alive and at least 25% free. When table * size is changed, the new table has about 25% live elements. * * The free entries guarantee an expected constant-time lookup. * Doubling/halving the table in the described fashion guarantees * amortized O(1) insertion/removal. * * This structure, and accompanying table, is borrowed/modified from the * file xserver/render/glyph.c in the freedesktop.org x server, with * permission (and suggested modification of doubling sizes) by Keith * Packard. */ static const unsigned long hash_table_sizes[] = { 43, 73, 151, 283, 571, 1153, 2269, 4519, 9013, 18043, 36109, 72091, 144409, 288361, 576883, 1153459, 2307163, 4613893, 9227641, 18455029, 36911011, 73819861, 147639589, 295279081, 590559793 }; struct _cairo_hash_table { cairo_hash_keys_equal_func_t keys_equal; cairo_hash_entry_t *cache[32]; const unsigned long *table_size; cairo_hash_entry_t **entries; unsigned long live_entries; unsigned long free_entries; unsigned long iterating; /* Iterating, no insert, no resize */ }; /** * _cairo_hash_table_uid_keys_equal: * @key_a: the first key to be compared * @key_b: the second key to be compared * * Provides a #cairo_hash_keys_equal_func_t which always returns * %TRUE. This is useful to create hash tables using keys whose hash * completely describes the key, because in this special case * comparing the hashes is sufficient to guarantee that the keys are * equal. * * Return value: %TRUE. **/ static cairo_bool_t _cairo_hash_table_uid_keys_equal (const void *key_a, const void *key_b) { return TRUE; } /** * _cairo_hash_table_create: * @keys_equal: a function to return %TRUE if two keys are equal * * Creates a new hash table which will use the keys_equal() function * to compare hash keys. Data is provided to the hash table in the * form of user-derived versions of #cairo_hash_entry_t. A hash entry * must be able to hold both a key (including a hash code) and a * value. Sometimes only the key will be necessary, (as in * _cairo_hash_table_remove), and other times both a key and a value * will be necessary, (as in _cairo_hash_table_insert). * * If @keys_equal is %NULL, two keys will be considered equal if and * only if their hashes are equal. * * See #cairo_hash_entry_t for more details. * * Return value: the new hash table or %NULL if out of memory. **/ cairo_hash_table_t * _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) { cairo_hash_table_t *hash_table; hash_table = malloc (sizeof (cairo_hash_table_t)); if (unlikely (hash_table == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } if (keys_equal == NULL) hash_table->keys_equal = _cairo_hash_table_uid_keys_equal; else hash_table->keys_equal = keys_equal; memset (&hash_table->cache, 0, sizeof (hash_table->cache)); hash_table->table_size = &hash_table_sizes[0]; hash_table->entries = calloc (*hash_table->table_size, sizeof (cairo_hash_entry_t *)); if (unlikely (hash_table->entries == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (hash_table); return NULL; } hash_table->live_entries = 0; hash_table->free_entries = *hash_table->table_size; hash_table->iterating = 0; return hash_table; } /** * _cairo_hash_table_destroy: * @hash_table: an empty hash table to destroy * * Immediately destroys the given hash table, freeing all resources * associated with it. * * WARNING: The hash_table must have no live entries in it before * _cairo_hash_table_destroy is called. It is a fatal error otherwise, * and this function will halt. The rationale for this behavior is to * avoid memory leaks and to avoid needless complication of the API * with destroy notifiy callbacks. * * WARNING: The hash_table must have no running iterators in it when * _cairo_hash_table_destroy is called. It is a fatal error otherwise, * and this function will halt. **/ void _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) { /* The hash table must be empty. Otherwise, halt. */ assert (hash_table->live_entries == 0); /* No iterators can be running. Otherwise, halt. */ assert (hash_table->iterating == 0); free (hash_table->entries); free (hash_table); } static cairo_hash_entry_t ** _cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key) { unsigned long table_size, i, idx, step; cairo_hash_entry_t **entry; table_size = *hash_table->table_size; idx = key->hash % table_size; entry = &hash_table->entries[idx]; if (! ENTRY_IS_LIVE (*entry)) return entry; i = 1; step = 1 + key->hash % (table_size - 2); do { idx += step; if (idx >= table_size) idx -= table_size; entry = &hash_table->entries[idx]; if (! ENTRY_IS_LIVE (*entry)) return entry; } while (++i < table_size); ASSERT_NOT_REACHED; return NULL; } /** * _cairo_hash_table_manage: * @hash_table: a hash table * * Resize the hash table if the number of entries has gotten much * bigger or smaller than the ideal number of entries for the current * size and guarantee some free entries to be used as lookup * termination points. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if out of memory. **/ static cairo_status_t _cairo_hash_table_manage (cairo_hash_table_t *hash_table) { cairo_hash_table_t tmp; unsigned long new_size, i; /* Keep between 12.5% and 50% entries in the hash table alive and * at least 25% free. */ unsigned long live_high = *hash_table->table_size >> 1; unsigned long live_low = live_high >> 2; unsigned long free_low = live_high >> 1; tmp = *hash_table; if (hash_table->live_entries > live_high) { tmp.table_size = hash_table->table_size + 1; /* This code is being abused if we can't make a table big enough. */ assert (tmp.table_size - hash_table_sizes < ARRAY_LENGTH (hash_table_sizes)); } else if (hash_table->live_entries < live_low) { /* Can't shrink if we're at the smallest size */ if (hash_table->table_size == &hash_table_sizes[0]) tmp.table_size = hash_table->table_size; else tmp.table_size = hash_table->table_size - 1; } if (tmp.table_size == hash_table->table_size && hash_table->free_entries > free_low) { /* The number of live entries is within the desired bounds * (we're not going to resize the table) and we have enough * free entries. Do nothing. */ return CAIRO_STATUS_SUCCESS; } new_size = *tmp.table_size; tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); if (unlikely (tmp.entries == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < *hash_table->table_size; ++i) { if (ENTRY_IS_LIVE (hash_table->entries[i])) { *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i]) = hash_table->entries[i]; } } free (hash_table->entries); hash_table->entries = tmp.entries; hash_table->table_size = tmp.table_size; hash_table->free_entries = new_size - hash_table->live_entries; return CAIRO_STATUS_SUCCESS; } /** * _cairo_hash_table_lookup: * @hash_table: a hash table * @key: the key of interest * * Performs a lookup in @hash_table looking for an entry which has a * key that matches @key, (as determined by the keys_equal() function * passed to _cairo_hash_table_create). * * Return value: the matching entry, of %NULL if no match was found. **/ void * _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key) { cairo_hash_entry_t *entry; unsigned long table_size, i, idx, step; unsigned long hash = key->hash; entry = hash_table->cache[hash & 31]; if (entry && entry->hash == hash && hash_table->keys_equal (key, entry)) return entry; table_size = *hash_table->table_size; idx = hash % table_size; entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry)) { if (entry->hash == hash && hash_table->keys_equal (key, entry)) goto insert_cache; } else if (ENTRY_IS_FREE (entry)) return NULL; i = 1; step = 1 + hash % (table_size - 2); do { idx += step; if (idx >= table_size) idx -= table_size; entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry)) { if (entry->hash == hash && hash_table->keys_equal (key, entry)) goto insert_cache; } else if (ENTRY_IS_FREE (entry)) return NULL; } while (++i < table_size); ASSERT_NOT_REACHED; return NULL; insert_cache: hash_table->cache[hash & 31] = entry; return entry; } /** * _cairo_hash_table_random_entry: * @hash_table: a hash table * @predicate: a predicate function. * * Find a random entry in the hash table satisfying the given * @predicate. * * We use the same algorithm as the lookup algorithm to walk over the * entries in the hash table in a pseudo-random order. Walking * linearly would favor entries following gaps in the hash table. We * could also call rand() repeatedly, which works well for almost-full * tables, but degrades when the table is almost empty, or predicate * returns %TRUE for most entries. * * Return value: a random live entry or %NULL if there are no entries * that match the given predicate. In particular, if predicate is * %NULL, a %NULL return value indicates that the table is empty. **/ void * _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, cairo_hash_predicate_func_t predicate) { cairo_hash_entry_t *entry; unsigned long hash; unsigned long table_size, i, idx, step; assert (predicate != NULL); table_size = *hash_table->table_size; hash = rand (); idx = hash % table_size; entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry) && predicate (entry)) return entry; i = 1; step = 1 + hash % (table_size - 2); do { idx += step; if (idx >= table_size) idx -= table_size; entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry) && predicate (entry)) return entry; } while (++i < table_size); return NULL; } /** * _cairo_hash_table_insert: * @hash_table: a hash table * @key_and_value: an entry to be inserted * * Insert the entry #key_and_value into the hash table. * * WARNING: There must not be an existing entry in the hash table * with a matching key. * * WARNING: It is a fatal error to insert an element while * an iterator is running * * Instead of using insert to replace an entry, consider just editing * the entry obtained with _cairo_hash_table_lookup. Or if absolutely * necessary, use _cairo_hash_table_remove first. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. **/ cairo_status_t _cairo_hash_table_insert (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key_and_value) { cairo_hash_entry_t **entry; cairo_status_t status; /* Insert is illegal while an iterator is running. */ assert (hash_table->iterating == 0); status = _cairo_hash_table_manage (hash_table); if (unlikely (status)) return status; entry = _cairo_hash_table_lookup_unique_key (hash_table, key_and_value); if (ENTRY_IS_FREE (*entry)) hash_table->free_entries--; *entry = key_and_value; hash_table->cache[key_and_value->hash & 31] = key_and_value; hash_table->live_entries++; return CAIRO_STATUS_SUCCESS; } static cairo_hash_entry_t ** _cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key) { unsigned long table_size, i, idx, step; cairo_hash_entry_t **entry; table_size = *hash_table->table_size; idx = key->hash % table_size; entry = &hash_table->entries[idx]; if (*entry == key) return entry; i = 1; step = 1 + key->hash % (table_size - 2); do { idx += step; if (idx >= table_size) idx -= table_size; entry = &hash_table->entries[idx]; if (*entry == key) return entry; } while (++i < table_size); ASSERT_NOT_REACHED; return NULL; } /** * _cairo_hash_table_remove: * @hash_table: a hash table * @key: key of entry to be removed * * Remove an entry from the hash table which points to @key. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if out of memory. **/ void _cairo_hash_table_remove (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key) { *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY; hash_table->live_entries--; hash_table->cache[key->hash & 31] = NULL; /* Check for table resize. Don't do this when iterating as this will * reorder elements of the table and cause the iteration to potentially * skip some elements. */ if (hash_table->iterating == 0) { /* This call _can_ fail, but only in failing to allocate new * memory to shrink the hash table. It does leave the table in a * consistent state, and we've already succeeded in removing the * entry, so we don't examine the failure status of this call. */ _cairo_hash_table_manage (hash_table); } } /** * _cairo_hash_table_foreach: * @hash_table: a hash table * @hash_callback: function to be called for each live entry * @closure: additional argument to be passed to @hash_callback * * Call @hash_callback for each live entry in the hash table, in a * non-specified order. * * Entries in @hash_table may be removed by code executed from @hash_callback. * * Entries may not be inserted to @hash_table, nor may @hash_table * be destroyed by code executed from @hash_callback. The relevant * functions will halt in these cases. **/ void _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, cairo_hash_callback_func_t hash_callback, void *closure) { unsigned long i; cairo_hash_entry_t *entry; /* Mark the table for iteration */ ++hash_table->iterating; for (i = 0; i < *hash_table->table_size; i++) { entry = hash_table->entries[i]; if (ENTRY_IS_LIVE(entry)) hash_callback (entry, closure); } /* If some elements were deleted during the iteration, * the table may need resizing. Just do this every time * as the check is inexpensive. */ if (--hash_table->iterating == 0) { /* Should we fail to shrink the hash table, it is left unaltered, * and we don't need to propagate the error status. */ _cairo_hash_table_manage (hash_table); } } ���������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-hull.c������������������������������������0000664�0000000�0000000�00000014336�12710376503�0024512�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-slope-private.h" typedef struct cairo_hull { cairo_point_t point; cairo_slope_t slope; int discard; int id; } cairo_hull_t; static void _cairo_hull_init (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int num_vertices) { cairo_point_t *p, *extremum, tmp; int i; extremum = &vertices[0].point; for (i = 1; i < num_vertices; i++) { p = &vertices[i].point; if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x)) extremum = p; } /* Put the extremal point at the beginning of the array */ tmp = *extremum; *extremum = vertices[0].point; vertices[0].point = tmp; for (i = 0; i < num_vertices; i++) { hull[i].point = vertices[i].point; _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point); /* give each point a unique id for later comparison */ hull[i].id = i; /* Don't discard by default */ hull[i].discard = 0; /* Discard all points coincident with the extremal point */ if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0) hull[i].discard = 1; } } static inline cairo_int64_t _slope_length (cairo_slope_t *slope) { return _cairo_int64_add (_cairo_int32x32_64_mul (slope->dx, slope->dx), _cairo_int32x32_64_mul (slope->dy, slope->dy)); } static int _cairo_hull_vertex_compare (const void *av, const void *bv) { cairo_hull_t *a = (cairo_hull_t *) av; cairo_hull_t *b = (cairo_hull_t *) bv; int ret; /* Some libraries are reported to actually compare identical * pointers and require the result to be 0. This is the crazy world we * have to live in. */ if (a == b) return 0; ret = _cairo_slope_compare (&a->slope, &b->slope); /* * In the case of two vertices with identical slope from the * extremal point discard the nearer point. */ if (ret == 0) { int cmp; cmp = _cairo_int64_cmp (_slope_length (&a->slope), _slope_length (&b->slope)); /* * Use the points' ids to ensure a well-defined ordering, * and avoid setting discard on both points. */ if (cmp < 0 || (cmp == 0 && a->id < b->id)) { a->discard = 1; ret = -1; } else { b->discard = 1; ret = 1; } } return ret; } static int _cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index) { /* hull[0] is always valid, and we never need to wraparound, (if * we are passed an index of 0 here, then the calling loop is just * about to terminate). */ if (index == 0) return 0; do { index--; } while (hull[index].discard); return index; } static int _cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index) { do { index = (index + 1) % num_hull; } while (hull[index].discard); return index; } static void _cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull) { int i, j, k; cairo_slope_t slope_ij, slope_jk; i = 0; j = _cairo_hull_next_valid (hull, num_hull, i); k = _cairo_hull_next_valid (hull, num_hull, j); do { _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point); _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point); /* Is the angle formed by ij and jk concave? */ if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) { if (i == k) return; hull[j].discard = 1; j = i; i = _cairo_hull_prev_valid (hull, num_hull, j); } else { i = j; j = k; k = _cairo_hull_next_valid (hull, num_hull, j); } } while (j != 0); } static void _cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices) { int i, j = 0; for (i = 0; i < *num_vertices; i++) { if (hull[i].discard) continue; vertices[j++].point = hull[i].point; } *num_vertices = j; } /* Given a set of vertices, compute the convex hull using the Graham scan algorithm. */ cairo_status_t _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) { cairo_hull_t hull_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_hull_t)]; cairo_hull_t *hull; int num_hull = *num_vertices; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_hull > ARRAY_LENGTH (hull_stack)) { hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); if (unlikely (hull == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { hull = hull_stack; } _cairo_hull_init (hull, vertices, num_hull); qsort (hull + 1, num_hull - 1, sizeof (cairo_hull_t), _cairo_hull_vertex_compare); _cairo_hull_eliminate_concave (hull, num_hull); _cairo_hull_to_pen (hull, vertices, num_vertices); if (hull != hull_stack) free (hull); return CAIRO_STATUS_SUCCESS; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-compositor.c������������������������0000664�0000000�0000000�00000251234�12710376503�0027024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2009,2010,2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* The primarily reason for keeping a traps-compositor around is * for validating cairo-xlib (which currently also uses traps). */ #include "cairoint.h" #include "cairo-image-surface-private.h" #include "cairo-compositor-private.h" #include "cairo-spans-compositor-private.h" #include "cairo-region-private.h" #include "cairo-traps-private.h" #include "cairo-tristrip-private.h" #include "cairo-pixman-private.h" static pixman_image_t * to_pixman_image (cairo_surface_t *s) { return ((cairo_image_surface_t *)s)->pixman_image; } static cairo_int_status_t acquire (void *abstract_dst) { return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t release (void *abstract_dst) { return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t set_clip_region (void *_surface, cairo_region_t *region) { cairo_image_surface_t *surface = _surface; pixman_region32_t *rgn = region ? ®ion->rgn : NULL; if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t draw_image_boxes (void *_dst, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy) { cairo_image_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; int i; TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int w = _cairo_fixed_integer_part (b->p2.x) - x; int h = _cairo_fixed_integer_part (b->p2.y) - y; if (dst->pixman_format != image->pixman_format || ! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data, image->stride / sizeof (uint32_t), dst->stride / sizeof (uint32_t), PIXMAN_FORMAT_BPP (image->pixman_format), PIXMAN_FORMAT_BPP (dst->pixman_format), x + dx, y + dy, x, y, w, h)) { pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, dst->pixman_image, x + dx, y + dy, 0, 0, x, y, w, h); } } } return CAIRO_STATUS_SUCCESS; } static inline uint32_t color_to_uint32 (const cairo_color_t *color) { return (color->alpha_short >> 8 << 24) | (color->red_short >> 8 << 16) | (color->green_short & 0xff00) | (color->blue_short >> 8); } static inline cairo_bool_t color_to_pixel (const cairo_color_t *color, pixman_format_code_t format, uint32_t *pixel) { uint32_t c; if (!(format == PIXMAN_a8r8g8b8 || format == PIXMAN_x8r8g8b8 || format == PIXMAN_a8b8g8r8 || format == PIXMAN_x8b8g8r8 || format == PIXMAN_b8g8r8a8 || format == PIXMAN_b8g8r8x8 || format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5 || format == PIXMAN_a8)) { return FALSE; } c = color_to_uint32 (color); if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { c = ((c & 0xff000000) >> 0) | ((c & 0x00ff0000) >> 16) | ((c & 0x0000ff00) >> 0) | ((c & 0x000000ff) << 16); } if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { c = ((c & 0xff000000) >> 24) | ((c & 0x00ff0000) >> 8) | ((c & 0x0000ff00) << 8) | ((c & 0x000000ff) << 24); } if (format == PIXMAN_a8) { c = c >> 24; } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { c = ((((c) >> 3) & 0x001f) | (((c) >> 5) & 0x07e0) | (((c) >> 8) & 0xf800)); } *pixel = c; return TRUE; } static pixman_op_t _pixman_operator (cairo_operator_t op) { switch ((int) op) { case CAIRO_OPERATOR_CLEAR: return PIXMAN_OP_CLEAR; case CAIRO_OPERATOR_SOURCE: return PIXMAN_OP_SRC; case CAIRO_OPERATOR_OVER: return PIXMAN_OP_OVER; case CAIRO_OPERATOR_IN: return PIXMAN_OP_IN; case CAIRO_OPERATOR_OUT: return PIXMAN_OP_OUT; case CAIRO_OPERATOR_ATOP: return PIXMAN_OP_ATOP; case CAIRO_OPERATOR_DEST: return PIXMAN_OP_DST; case CAIRO_OPERATOR_DEST_OVER: return PIXMAN_OP_OVER_REVERSE; case CAIRO_OPERATOR_DEST_IN: return PIXMAN_OP_IN_REVERSE; case CAIRO_OPERATOR_DEST_OUT: return PIXMAN_OP_OUT_REVERSE; case CAIRO_OPERATOR_DEST_ATOP: return PIXMAN_OP_ATOP_REVERSE; case CAIRO_OPERATOR_XOR: return PIXMAN_OP_XOR; case CAIRO_OPERATOR_ADD: return PIXMAN_OP_ADD; case CAIRO_OPERATOR_SATURATE: return PIXMAN_OP_SATURATE; case CAIRO_OPERATOR_MULTIPLY: return PIXMAN_OP_MULTIPLY; case CAIRO_OPERATOR_SCREEN: return PIXMAN_OP_SCREEN; case CAIRO_OPERATOR_OVERLAY: return PIXMAN_OP_OVERLAY; case CAIRO_OPERATOR_DARKEN: return PIXMAN_OP_DARKEN; case CAIRO_OPERATOR_LIGHTEN: return PIXMAN_OP_LIGHTEN; case CAIRO_OPERATOR_COLOR_DODGE: return PIXMAN_OP_COLOR_DODGE; case CAIRO_OPERATOR_COLOR_BURN: return PIXMAN_OP_COLOR_BURN; case CAIRO_OPERATOR_HARD_LIGHT: return PIXMAN_OP_HARD_LIGHT; case CAIRO_OPERATOR_SOFT_LIGHT: return PIXMAN_OP_SOFT_LIGHT; case CAIRO_OPERATOR_DIFFERENCE: return PIXMAN_OP_DIFFERENCE; case CAIRO_OPERATOR_EXCLUSION: return PIXMAN_OP_EXCLUSION; case CAIRO_OPERATOR_HSL_HUE: return PIXMAN_OP_HSL_HUE; case CAIRO_OPERATOR_HSL_SATURATION: return PIXMAN_OP_HSL_SATURATION; case CAIRO_OPERATOR_HSL_COLOR: return PIXMAN_OP_HSL_COLOR; case CAIRO_OPERATOR_HSL_LUMINOSITY: return PIXMAN_OP_HSL_LUMINOSITY; default: ASSERT_NOT_REACHED; return PIXMAN_OP_OVER; } } static cairo_bool_t __fill_reduces_to_source (cairo_operator_t op, const cairo_color_t *color, const cairo_image_surface_t *dst) { if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) return TRUE; if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color)) return TRUE; if (dst->base.is_clear) return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD; return FALSE; } static cairo_bool_t fill_reduces_to_source (cairo_operator_t op, const cairo_color_t *color, const cairo_image_surface_t *dst, uint32_t *pixel) { if (__fill_reduces_to_source (op, color, dst)) { color_to_pixel (color, dst->pixman_format, pixel); return TRUE; } return FALSE; } static cairo_int_status_t fill_rectangles (void *_dst, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_image_surface_t *dst = _dst; uint32_t pixel; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); if (fill_reduces_to_source (op, color, dst, &pixel)) { for (i = 0; i < num_rects; i++) { pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), PIXMAN_FORMAT_BPP (dst->pixman_format), rects[i].x, rects[i].y, rects[i].width, rects[i].height, pixel); } } else { pixman_image_t *src = _pixman_image_for_color (color); op = _pixman_operator (op); for (i = 0; i < num_rects; i++) { pixman_image_composite32 (op, src, NULL, dst->pixman_image, 0, 0, 0, 0, rects[i].x, rects[i].y, rects[i].width, rects[i].height); } pixman_image_unref (src); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t fill_boxes (void *_dst, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_image_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; uint32_t pixel; int i; TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); if (fill_reduces_to_source (op, color, dst, &pixel)) { for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y = _cairo_fixed_integer_part (chunk->base[i].p1.y); int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x; int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y; pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), PIXMAN_FORMAT_BPP (dst->pixman_format), x, y, w, h, pixel); } } } else { pixman_image_t *src = _pixman_image_for_color (color); op = _pixman_operator (op); for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); pixman_image_composite32 (op, src, NULL, dst->pixman_image, 0, 0, 0, 0, x1, y1, x2-x1, y2-y1); } } pixman_image_unref (src); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_image_source_t *src = (cairo_image_source_t *)abstract_src; cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; TRACE ((stderr, "%s\n", __FUNCTION__)); if (mask) { pixman_image_composite32 (_pixman_operator (op), src->pixman_image, mask->pixman_image, to_pixman_image (_dst), src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); } else { pixman_image_composite32 (_pixman_operator (op), src->pixman_image, NULL, to_pixman_image (_dst), src_x, src_y, 0, 0, dst_x, dst_y, width, height); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t lerp (void *_dst, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_image_surface_t *dst = _dst; cairo_image_source_t *src = (cairo_image_source_t *)abstract_src; cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; TRACE ((stderr, "%s\n", __FUNCTION__)); #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, src->pixman_image, mask->pixman_image, dst->pixman_image, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); #else /* Punch the clip out of the destination */ TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n", __FUNCTION__, mask->base.unique_id, mask->pixman_image, dst->base.unique_id, dst->pixman_image)); pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, mask->pixman_image, NULL, dst->pixman_image, mask_x, mask_y, 0, 0, dst_x, dst_y, width, height); /* Now add the two results together */ TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n", __FUNCTION__, src->base.unique_id, src->pixman_image, mask->base.unique_id, mask->pixman_image, dst->base.unique_id, dst->pixman_image)); pixman_image_composite32 (PIXMAN_OP_ADD, src->pixman_image, mask->pixman_image, dst->pixman_image, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); #endif return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_boxes (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents) { pixman_image_t *dst = to_pixman_image (_dst); pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image; pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL; pixman_image_t *free_src = NULL; struct _cairo_boxes_chunk *chunk; int i; /* XXX consider using a region? saves multiple prepare-composite */ TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); if (((cairo_surface_t *)_dst)->is_clear && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) { op = PIXMAN_OP_SRC; } else if (mask) { if (op == CAIRO_OPERATOR_CLEAR) { #if PIXMAN_HAS_OP_LERP op = PIXMAN_OP_LERP_CLEAR; #else free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE); op = PIXMAN_OP_OUT_REVERSE; #endif } else if (op == CAIRO_OPERATOR_SOURCE) { #if PIXMAN_HAS_OP_LERP op = PIXMAN_OP_LERP_SRC; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } else { op = _pixman_operator (op); } } else { op = _pixman_operator (op); } for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); pixman_image_composite32 (op, src, mask, dst, x1 + src_x, y1 + src_y, x1 + mask_x, y1 + mask_y, x1 + dst_x, y1 + dst_y, x2 - x1, y2 - y1); } } if (free_src) pixman_image_unref (free_src); return CAIRO_STATUS_SUCCESS; } #define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) #define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) static cairo_bool_t line_exceeds_16_16 (const cairo_line_t *line) { return line->p1.x <= CAIRO_FIXED_16_16_MIN || line->p1.x >= CAIRO_FIXED_16_16_MAX || line->p2.x <= CAIRO_FIXED_16_16_MIN || line->p2.x >= CAIRO_FIXED_16_16_MAX || line->p1.y <= CAIRO_FIXED_16_16_MIN || line->p1.y >= CAIRO_FIXED_16_16_MAX || line->p2.y <= CAIRO_FIXED_16_16_MIN || line->p2.y >= CAIRO_FIXED_16_16_MAX; } static void project_line_x_onto_16_16 (const cairo_line_t *line, cairo_fixed_t top, cairo_fixed_t bottom, pixman_line_fixed_t *out) { /* XXX use fixed-point arithmetic? */ cairo_point_double_t p1, p2; double m; p1.x = _cairo_fixed_to_double (line->p1.x); p1.y = _cairo_fixed_to_double (line->p1.y); p2.x = _cairo_fixed_to_double (line->p2.x); p2.y = _cairo_fixed_to_double (line->p2.y); m = (p2.x - p1.x) / (p2.y - p1.y); out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); } void _pixman_image_add_traps (pixman_image_t *image, int dst_x, int dst_y, cairo_traps_t *traps) { cairo_trapezoid_t *t = traps->traps; int num_traps = traps->num_traps; while (num_traps--) { pixman_trapezoid_t trap; /* top/bottom will be clamped to surface bounds */ trap.top = _cairo_fixed_to_16_16 (t->top); trap.bottom = _cairo_fixed_to_16_16 (t->bottom); /* However, all the other coordinates will have been left untouched so * as not to introduce numerical error. Recompute them if they * exceed the 16.16 limits. */ if (unlikely (line_exceeds_16_16 (&t->left))) { project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); trap.left.p1.y = trap.top; trap.left.p2.y = trap.bottom; } else { trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); } if (unlikely (line_exceeds_16_16 (&t->right))) { project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); trap.right.p1.y = trap.top; trap.right.p2.y = trap.bottom; } else { trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); } pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); t++; } } static cairo_int_status_t composite_traps (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_traps_t *traps) { cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst; cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; pixman_image_t *mask; pixman_format_code_t format; TRACE ((stderr, "%s\n", __FUNCTION__)); /* Special case adding trapezoids onto a mask surface; we want to avoid * creating an intermediate temporary mask unnecessarily. * * We make the assumption here that the portion of the trapezoids * contained within the surface is bounded by [dst_x,dst_y,width,height]; * the Cairo core code passes bounds based on the trapezoid extents. */ format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; if (dst->pixman_format == format && (abstract_src == NULL || (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) { _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps); return CAIRO_STATUS_SUCCESS; } mask = pixman_image_create_bits (format, extents->width, extents->height, NULL, 0); if (unlikely (mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _pixman_image_add_traps (mask, extents->x, extents->y, traps); pixman_image_composite32 (_pixman_operator (op), src->pixman_image, mask, dst->pixman_image, extents->x + src_x, extents->y + src_y, 0, 0, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); pixman_image_unref (mask); return CAIRO_STATUS_SUCCESS; } #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) static void set_point (pixman_point_fixed_t *p, cairo_point_t *c) { p->x = _cairo_fixed_to_16_16 (c->x); p->y = _cairo_fixed_to_16_16 (c->y); } void _pixman_image_add_tristrip (pixman_image_t *image, int dst_x, int dst_y, cairo_tristrip_t *strip) { pixman_triangle_t tri; pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 }; int n; set_point (p[0], &strip->points[0]); set_point (p[1], &strip->points[1]); set_point (p[2], &strip->points[2]); pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); for (n = 3; n < strip->num_points; n++) { set_point (p[n%3], &strip->points[n]); pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); } } static cairo_int_status_t composite_tristrip (void *_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_tristrip_t *strip) { cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst; cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; pixman_image_t *mask; pixman_format_code_t format; TRACE ((stderr, "%s\n", __FUNCTION__)); if (strip->num_points < 3) return CAIRO_STATUS_SUCCESS; format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; if (dst->pixman_format == format && (abstract_src == NULL || (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) { _pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip); return CAIRO_STATUS_SUCCESS; } mask = pixman_image_create_bits (format, extents->width, extents->height, NULL, 0); if (unlikely (mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _pixman_image_add_tristrip (mask, extents->x, extents->y, strip); pixman_image_composite32 (_pixman_operator (op), src->pixman_image, mask, dst->pixman_image, extents->x + src_x, extents->y + src_y, 0, 0, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); pixman_image_unref (mask); return CAIRO_STATUS_SUCCESS; } #endif static cairo_int_status_t check_composite_glyphs (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs) { return CAIRO_STATUS_SUCCESS; } #if HAS_PIXMAN_GLYPHS static pixman_glyph_cache_t *global_glyph_cache; static inline pixman_glyph_cache_t * get_glyph_cache (void) { if (!global_glyph_cache) global_glyph_cache = pixman_glyph_cache_create (); return global_glyph_cache; } void _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); if (global_glyph_cache) { pixman_glyph_cache_remove ( global_glyph_cache, scaled_font, (void *)_cairo_scaled_glyph_index (scaled_glyph)); } CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); } static cairo_int_status_t composite_glyphs (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; pixman_glyph_cache_t *glyph_cache; pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)]; pixman_glyph_t *pglyphs = pglyphs_stack; pixman_glyph_t *pg; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); glyph_cache = get_glyph_cache(); if (unlikely (glyph_cache == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_unlock; } pixman_glyph_cache_freeze (glyph_cache); if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) { pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t)); if (unlikely (pglyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_thaw; } } pg = pglyphs; for (i = 0; i < info->num_glyphs; i++) { unsigned long index = info->glyphs[i].index; const void *glyph; glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index); if (!glyph) { cairo_scaled_glyph_t *scaled_glyph; cairo_image_surface_t *glyph_surface; /* This call can actually end up recursing, so we have to * drop the mutex around it. */ CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); status = _cairo_scaled_glyph_lookup (info->font, index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); if (unlikely (status)) goto out_thaw; glyph_surface = scaled_glyph->surface; glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index, glyph_surface->base.device_transform.x0, glyph_surface->base.device_transform.y0, glyph_surface->pixman_image); if (unlikely (!glyph)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto out_thaw; } } pg->x = _cairo_lround (info->glyphs[i].x); pg->y = _cairo_lround (info->glyphs[i].y); pg->glyph = glyph; pg++; } if (info->use_mask) { pixman_format_code_t mask_format; mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs); pixman_composite_glyphs (_pixman_operator (op), ((cairo_image_source_t *)_src)->pixman_image, to_pixman_image (_dst), mask_format, info->extents.x + src_x, info->extents.y + src_y, info->extents.x, info->extents.y, info->extents.x - dst_x, info->extents.y - dst_y, info->extents.width, info->extents.height, glyph_cache, pg - pglyphs, pglyphs); } else { pixman_composite_glyphs_no_mask (_pixman_operator (op), ((cairo_image_source_t *)_src)->pixman_image, to_pixman_image (_dst), src_x, src_y, - dst_x, - dst_y, glyph_cache, pg - pglyphs, pglyphs); } out_thaw: pixman_glyph_cache_thaw (glyph_cache); if (pglyphs != pglyphs_stack) free(pglyphs); out_unlock: CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); return status; } #else void _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { } static cairo_int_status_t composite_one_glyph (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { cairo_image_surface_t *glyph_surface; cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; int x, y; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_scaled_glyph_lookup (info->font, info->glyphs[0].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) return status; glyph_surface = scaled_glyph->surface; if (glyph_surface->width == 0 || glyph_surface->height == 0) return CAIRO_INT_STATUS_NOTHING_TO_DO; /* round glyph locations to the nearest pixel */ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ x = _cairo_lround (info->glyphs[0].x - glyph_surface->base.device_transform.x0); y = _cairo_lround (info->glyphs[0].y - glyph_surface->base.device_transform.y0); pixman_image_composite32 (_pixman_operator (op), ((cairo_image_source_t *)_src)->pixman_image, glyph_surface->pixman_image, to_pixman_image (_dst), x + src_x, y + src_y, 0, 0, x - dst_x, y - dst_y, glyph_surface->width, glyph_surface->height); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t composite_glyphs_via_mask (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { cairo_scaled_glyph_t *glyph_cache[64]; pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE); cairo_scaled_glyph_t *scaled_glyph; uint8_t buf[2048]; pixman_image_t *mask; pixman_format_code_t format; cairo_status_t status; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (white == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit * optimised paths through pixman. Should we increase the bit * depth of the target surface, we should reconsider the appropriate * mask formats. */ status = _cairo_scaled_glyph_lookup (info->font, info->glyphs[0].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) { pixman_image_unref (white); return status; } memset (glyph_cache, 0, sizeof (glyph_cache)); glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph; format = PIXMAN_a8; i = (info->extents.width + 3) & ~3; if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) { format = PIXMAN_a8r8g8b8; i = info->extents.width * 4; } if (i * info->extents.height > (int) sizeof (buf)) { mask = pixman_image_create_bits (format, info->extents.width, info->extents.height, NULL, 0); } else { memset (buf, 0, i * info->extents.height); mask = pixman_image_create_bits (format, info->extents.width, info->extents.height, (uint32_t *)buf, i); } if (unlikely (mask == NULL)) { pixman_image_unref (white); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } status = CAIRO_STATUS_SUCCESS; for (i = 0; i < info->num_glyphs; i++) { unsigned long glyph_index = info->glyphs[i].index; int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); cairo_image_surface_t *glyph_surface; int x, y; scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) { status = _cairo_scaled_glyph_lookup (info->font, glyph_index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) { pixman_image_unref (mask); pixman_image_unref (white); return status; } glyph_cache[cache_index] = scaled_glyph; } glyph_surface = scaled_glyph->surface; if (glyph_surface->width && glyph_surface->height) { if (glyph_surface->base.content & CAIRO_CONTENT_COLOR && format == PIXMAN_a8) { pixman_image_t *ca_mask; format = PIXMAN_a8r8g8b8; ca_mask = pixman_image_create_bits (format, info->extents.width, info->extents.height, NULL, 0); if (unlikely (ca_mask == NULL)) { pixman_image_unref (mask); pixman_image_unref (white); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pixman_image_composite32 (PIXMAN_OP_SRC, white, mask, ca_mask, 0, 0, 0, 0, 0, 0, info->extents.width, info->extents.height); pixman_image_unref (mask); mask = ca_mask; } /* round glyph locations to the nearest pixel */ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ x = _cairo_lround (info->glyphs[i].x - glyph_surface->base.device_transform.x0); y = _cairo_lround (info->glyphs[i].y - glyph_surface->base.device_transform.y0); if (glyph_surface->pixman_format == format) { pixman_image_composite32 (PIXMAN_OP_ADD, glyph_surface->pixman_image, NULL, mask, 0, 0, 0, 0, x - info->extents.x, y - info->extents.y, glyph_surface->width, glyph_surface->height); } else { pixman_image_composite32 (PIXMAN_OP_ADD, white, glyph_surface->pixman_image, mask, 0, 0, 0, 0, x - info->extents.x, y - info->extents.y, glyph_surface->width, glyph_surface->height); } } } if (format == PIXMAN_a8r8g8b8) pixman_image_set_component_alpha (mask, TRUE); pixman_image_composite32 (_pixman_operator (op), ((cairo_image_source_t *)_src)->pixman_image, mask, to_pixman_image (_dst), info->extents.x + src_x, info->extents.y + src_y, 0, 0, info->extents.x - dst_x, info->extents.y - dst_y, info->extents.width, info->extents.height); pixman_image_unref (mask); pixman_image_unref (white); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_glyphs (void *_dst, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { cairo_scaled_glyph_t *glyph_cache[64]; pixman_image_t *dst, *src; cairo_status_t status; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); if (info->num_glyphs == 1) return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); if (info->use_mask) return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); op = _pixman_operator (op); dst = to_pixman_image (_dst); src = ((cairo_image_source_t *)_src)->pixman_image; memset (glyph_cache, 0, sizeof (glyph_cache)); status = CAIRO_STATUS_SUCCESS; for (i = 0; i < info->num_glyphs; i++) { int x, y; cairo_image_surface_t *glyph_surface; cairo_scaled_glyph_t *scaled_glyph; unsigned long glyph_index = info->glyphs[i].index; int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) { status = _cairo_scaled_glyph_lookup (info->font, glyph_index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) break; glyph_cache[cache_index] = scaled_glyph; } glyph_surface = scaled_glyph->surface; if (glyph_surface->width && glyph_surface->height) { /* round glyph locations to the nearest pixel */ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ x = _cairo_lround (info->glyphs[i].x - glyph_surface->base.device_transform.x0); y = _cairo_lround (info->glyphs[i].y - glyph_surface->base.device_transform.y0); pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst, x + src_x, y + src_y, 0, 0, x - dst_x, y - dst_y, glyph_surface->width, glyph_surface->height); } } return status; } #endif static cairo_int_status_t check_composite (const cairo_composite_rectangles_t *extents) { return CAIRO_STATUS_SUCCESS; } const cairo_compositor_t * _cairo_image_traps_compositor_get (void) { static cairo_traps_compositor_t compositor; if (compositor.base.delegate == NULL) { _cairo_traps_compositor_init (&compositor, &__cairo_no_compositor); compositor.acquire = acquire; compositor.release = release; compositor.set_clip_region = set_clip_region; compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; compositor.draw_image_boxes = draw_image_boxes; //compositor.copy_boxes = copy_boxes; compositor.fill_boxes = fill_boxes; compositor.check_composite = check_composite; compositor.composite = composite; compositor.lerp = lerp; //compositor.check_composite_boxes = check_composite_boxes; compositor.composite_boxes = composite_boxes; //compositor.check_composite_traps = check_composite_traps; compositor.composite_traps = composite_traps; //compositor.check_composite_tristrip = check_composite_traps; #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) compositor.composite_tristrip = composite_tristrip; #endif compositor.check_composite_glyphs = check_composite_glyphs; compositor.composite_glyphs = composite_glyphs; } return &compositor.base; } const cairo_compositor_t * _cairo_image_mask_compositor_get (void) { static cairo_mask_compositor_t compositor; if (compositor.base.delegate == NULL) { _cairo_mask_compositor_init (&compositor, _cairo_image_traps_compositor_get ()); compositor.acquire = acquire; compositor.release = release; compositor.set_clip_region = set_clip_region; compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; compositor.draw_image_boxes = draw_image_boxes; compositor.fill_rectangles = fill_rectangles; compositor.fill_boxes = fill_boxes; //compositor.check_composite = check_composite; compositor.composite = composite; //compositor.lerp = lerp; //compositor.check_composite_boxes = check_composite_boxes; compositor.composite_boxes = composite_boxes; compositor.check_composite_glyphs = check_composite_glyphs; compositor.composite_glyphs = composite_glyphs; } return &compositor.base; } #if PIXMAN_HAS_COMPOSITOR typedef struct _cairo_image_span_renderer { cairo_span_renderer_t base; pixman_image_compositor_t *compositor; pixman_image_t *src, *mask; float opacity; cairo_rectangle_int_t extents; } cairo_image_span_renderer_t; COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); static cairo_status_t _cairo_image_bounded_opaque_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) pixman_image_compositor_blt (r->compositor, spans[0].x, y, spans[1].x - spans[0].x, height, spans[0].coverage); spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { pixman_image_compositor_blt (r->compositor, spans[0].x, y, spans[1].x - spans[0].x, height, r->opacity * spans[0].coverage); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; assert (y + height <= r->extents.height); if (y > r->extents.y) { pixman_image_compositor_blt (r->compositor, r->extents.x, r->extents.y, r->extents.width, y - r->extents.y, 0); } if (num_spans == 0) { pixman_image_compositor_blt (r->compositor, r->extents.x, y, r->extents.width, height, 0); } else { if (spans[0].x != r->extents.x) { pixman_image_compositor_blt (r->compositor, r->extents.x, y, spans[0].x - r->extents.x, height, 0); } do { assert (spans[0].x < r->extents.x + r->extents.width); pixman_image_compositor_blt (r->compositor, spans[0].x, y, spans[1].x - spans[0].x, height, r->opacity * spans[0].coverage); spans++; } while (--num_spans > 1); if (spans[0].x != r->extents.x + r->extents.width) { assert (spans[0].x < r->extents.x + r->extents.width); pixman_image_compositor_blt (r->compositor, spans[0].x, y, r->extents.x + r->extents.width - spans[0].x, height, 0); } } r->extents.y = y + height; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_clipped_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; assert (num_spans); do { if (! spans[0].inverse) pixman_image_compositor_blt (r->compositor, spans[0].x, y, spans[1].x - spans[0].x, height, r->opacity * spans[0].coverage); spans++; } while (--num_spans > 1); r->extents.y = y + height; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_finish_unbounded_spans (void *abstract_renderer) { cairo_image_span_renderer_t *r = abstract_renderer; if (r->extents.y < r->extents.height) { pixman_image_compositor_blt (r->compositor, r->extents.x, r->extents.y, r->extents.width, r->extents.height - r->extents.y, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t span_renderer_init (cairo_abstract_span_renderer_t *_r, const cairo_composite_rectangles_t *composite, cairo_bool_t needs_clip) { cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; const cairo_pattern_t *source = &composite->source_pattern.base; cairo_operator_t op = composite->op; int src_x, src_y; int mask_x, mask_y; TRACE ((stderr, "%s\n", __FUNCTION__)); if (op == CAIRO_OPERATOR_CLEAR) { op = PIXMAN_OP_LERP_CLEAR; } else if (dst->base.is_clear && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) { op = PIXMAN_OP_SRC; } else if (op == CAIRO_OPERATOR_SOURCE) { op = PIXMAN_OP_LERP_SRC; } else { op = _pixman_operator (op); } r->compositor = NULL; r->mask = NULL; r->src = _pixman_image_for_pattern (dst, source, FALSE, &composite->unbounded, &composite->source_sample_area, &src_x, &src_y); if (unlikely (r->src == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); r->opacity = 1.0; if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { r->opacity = composite->mask_pattern.solid.color.alpha; } else { r->mask = _pixman_image_for_pattern (dst, &composite->mask_pattern.base, TRUE, &composite->unbounded, &composite->mask_sample_area, &mask_x, &mask_y); if (unlikely (r->mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* XXX Component-alpha? */ if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 && _cairo_pattern_is_opaque (source, &composite->source_sample_area)) { pixman_image_unref (r->src); r->src = r->mask; src_x = mask_x; src_y = mask_y; r->mask = NULL; } } if (composite->is_bounded) { if (r->opacity == 1.) r->base.render_rows = _cairo_image_bounded_opaque_spans; else r->base.render_rows = _cairo_image_bounded_spans; r->base.finish = NULL; } else { if (needs_clip) r->base.render_rows = _cairo_image_clipped_spans; else r->base.render_rows = _cairo_image_unbounded_spans; r->base.finish = _cairo_image_finish_unbounded_spans; r->extents = composite->unbounded; r->extents.height += r->extents.y; } r->compositor = pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image, composite->unbounded.x + src_x, composite->unbounded.y + src_y, composite->unbounded.x + mask_x, composite->unbounded.y + mask_y, composite->unbounded.x, composite->unbounded.y, composite->unbounded.width, composite->unbounded.height); if (unlikely (r->compositor == NULL)) return CAIRO_INT_STATUS_NOTHING_TO_DO; return CAIRO_STATUS_SUCCESS; } static void span_renderer_fini (cairo_abstract_span_renderer_t *_r, cairo_int_status_t status) { cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r; TRACE ((stderr, "%s\n", __FUNCTION__)); if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish) r->base.finish (r); if (r->compositor) pixman_image_compositor_destroy (r->compositor); if (r->src) pixman_image_unref (r->src); if (r->mask) pixman_image_unref (r->mask); } #else typedef struct _cairo_image_span_renderer { cairo_span_renderer_t base; const cairo_composite_rectangles_t *composite; float opacity; uint8_t op; int bpp; pixman_image_t *src, *mask; union { struct fill { int stride; uint8_t *data; uint32_t pixel; } fill; struct blit { int stride; uint8_t *data; int src_stride; uint8_t *src_data; } blit; struct composite { pixman_image_t *dst; int src_x, src_y; int mask_x, mask_y; int run_length; } composite; struct finish { cairo_rectangle_int_t extents; int src_x, src_y; int stride; uint8_t *data; } mask; } u; uint8_t _buf[0]; #define SZ_BUF (int)(sizeof (cairo_abstract_span_renderer_t) - sizeof (cairo_image_span_renderer_t)) } cairo_image_span_renderer_t; COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); static cairo_status_t _cairo_image_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *mask, *row; int len; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride; mask += spans[0].x - r->u.mask.extents.x; row = mask; do { len = spans[1].x - spans[0].x; if (spans[0].coverage) { *row++ = r->opacity * spans[0].coverage; if (--len) memset (row, row[-1], len); } row += len; spans++; } while (--num_spans > 1); len = row - mask; row = mask; while (--height) { mask += r->u.mask.stride; memcpy (mask, row, len); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_spans_and_zero (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *mask; int len; mask = r->u.mask.data; if (y > r->u.mask.extents.y) { len = (y - r->u.mask.extents.y) * r->u.mask.stride; memset (mask, 0, len); mask += len; } r->u.mask.extents.y = y + height; r->u.mask.data = mask + height * r->u.mask.stride; if (num_spans == 0) { memset (mask, 0, height * r->u.mask.stride); } else { uint8_t *row = mask; if (spans[0].x != r->u.mask.extents.x) { len = spans[0].x - r->u.mask.extents.x; memset (row, 0, len); row += len; } do { len = spans[1].x - spans[0].x; *row++ = r->opacity * spans[0].coverage; if (len > 1) { memset (row, row[-1], --len); row += len; } spans++; } while (--num_spans > 1); if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) { len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x; memset (row, 0, len); } row = mask; while (--height) { mask += r->u.mask.stride; memcpy (mask, row, r->u.mask.extents.width); } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_image_finish_spans_and_zero (void *abstract_renderer) { cairo_image_span_renderer_t *r = abstract_renderer; if (r->u.mask.extents.y < r->u.mask.extents.height) memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill8_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { if (spans[0].coverage) { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; if (len == 1) *d = r->u.fill.pixel; else memset(d, r->u.fill.pixel, len); } spans++; } while (--num_spans > 1); } else { do { if (spans[0].coverage) { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; if (len == 1) *d = r->u.fill.pixel; else memset(d, r->u.fill.pixel, len); yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill16_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { if (spans[0].coverage) { int len = spans[1].x - spans[0].x; uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2); while (len--) *d++ = r->u.fill.pixel; } spans++; } while (--num_spans > 1); } else { do { if (spans[0].coverage) { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2); while (len--) *d++ = r->u.fill.pixel; yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill32_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { if (spans[0].coverage) { int len = spans[1].x - spans[0].x; if (len > 32) { pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, spans[0].x, y, len, 1, r->u.fill.pixel); } else { uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); while (len--) *d++ = r->u.fill.pixel; } } spans++; } while (--num_spans > 1); } else { do { if (spans[0].coverage) { if (spans[1].x - spans[0].x > 16) { pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, spans[0].x, y, spans[1].x - spans[0].x, h, r->u.fill.pixel); } else { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); while (len--) *d++ = r->u.fill.pixel; yy++; } while (--hh); } } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } #if 0 static cairo_status_t _fill_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { pixman_fill ((uint32_t *) r->data, r->stride, r->bpp, spans[0].x, y, spans[1].x - spans[0].x, h, r->pixel); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } #endif static cairo_status_t _blit_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; int cpp; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; cpp = r->bpp/8; if (likely (h == 1)) { uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; do { if (spans[0].coverage) { void *s = src + spans[0].x*cpp; void *d = dst + spans[0].x*cpp; int len = (spans[1].x - spans[0].x) * cpp; switch (len) { case 1: *(uint8_t *)d = *(uint8_t *)s; break; case 2: *(uint16_t *)d = *(uint16_t *)s; break; case 4: *(uint32_t *)d = *(uint32_t *)s; break; #if HAVE_UINT64_T case 8: *(uint64_t *)d = *(uint64_t *)s; break; #endif default: memcpy(d, s, len); break; } } spans++; } while (--num_spans > 1); } else { do { if (spans[0].coverage) { int yy = y, hh = h; do { void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp; void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp; int len = (spans[1].x - spans[0].x) * cpp; switch (len) { case 1: *(uint8_t *)dst = *(uint8_t *)src; break; case 2: *(uint16_t *)dst = *(uint16_t *)src; break; case 4: *(uint32_t *)dst = *(uint32_t *)src; break; #if HAVE_UINT64_T case 8: *(uint64_t *)dst = *(uint64_t *)src; break; #endif default: memcpy(dst, src, len); break; } yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _mono_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (spans[0].coverage) { pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, spans[1].x - spans[0].x, h); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _mono_unbounded_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) { pixman_image_composite32 (PIXMAN_OP_CLEAR, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, r->composite->unbounded.x, y, r->composite->unbounded.width, h); r->u.composite.mask_y = y + h; return CAIRO_STATUS_SUCCESS; } if (y != r->u.composite.mask_y) { pixman_image_composite32 (PIXMAN_OP_CLEAR, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, r->composite->unbounded.x, r->u.composite.mask_y, r->composite->unbounded.width, y - r->u.composite.mask_y); } if (spans[0].x != r->composite->unbounded.x) { pixman_image_composite32 (PIXMAN_OP_CLEAR, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, r->composite->unbounded.x, y, spans[0].x - r->composite->unbounded.x, h); } do { int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR; pixman_image_composite32 (op, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, spans[1].x - spans[0].x, h); spans++; } while (--num_spans > 1); if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) { pixman_image_composite32 (PIXMAN_OP_CLEAR, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h); } r->u.composite.mask_y = y + h; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _mono_finish_unbounded_spans (void *abstract_renderer) { cairo_image_span_renderer_t *r = abstract_renderer; if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) { pixman_image_composite32 (PIXMAN_OP_CLEAR, r->src, NULL, r->u.composite.dst, r->composite->unbounded.x + r->u.composite.src_x, r->u.composite.mask_y + r->u.composite.src_y, 0, 0, r->composite->unbounded.x, r->u.composite.mask_y, r->composite->unbounded.width, r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t mono_renderer_init (cairo_image_span_renderer_t *r, const cairo_composite_rectangles_t *composite, cairo_antialias_t antialias, cairo_bool_t needs_clip) { cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; if (antialias != CAIRO_ANTIALIAS_NONE) return CAIRO_INT_STATUS_UNSUPPORTED; if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base)) return CAIRO_INT_STATUS_UNSUPPORTED; r->base.render_rows = NULL; if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_color_t *color; color = &composite->source_pattern.solid.color; if (composite->op == CAIRO_OPERATOR_CLEAR) color = CAIRO_COLOR_TRANSPARENT; if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) { /* Use plain C for the fill operations as the span length is * typically small, too small to payback the startup overheads of * using SSE2 etc. */ switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) { case 8: r->base.render_rows = _fill8_spans; break; case 16: r->base.render_rows = _fill16_spans; break; case 32: r->base.render_rows = _fill32_spans; break; default: break; } r->u.fill.data = dst->data; r->u.fill.stride = dst->stride; } } else if ((composite->op == CAIRO_OPERATOR_SOURCE || (composite->op == CAIRO_OPERATOR_OVER && (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) { cairo_image_surface_t *src = to_image_surface(composite->source_pattern.surface.surface); int tx, ty; if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, &tx, &ty) && composite->bounded.x + tx >= 0 && composite->bounded.y + ty >= 0 && composite->bounded.x + composite->bounded.width + tx <= src->width && composite->bounded.y + composite->bounded.height + ty <= src->height) { r->u.blit.stride = dst->stride; r->u.blit.data = dst->data; r->u.blit.src_stride = src->stride; r->u.blit.src_data = src->data + src->stride * ty + tx * 4; r->base.render_rows = _blit_spans; } } if (r->base.render_rows == NULL) { r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE, &composite->unbounded, &composite->source_sample_area, &r->u.composite.src_x, &r->u.composite.src_y); if (unlikely (r->src == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); r->u.composite.dst = to_pixman_image (composite->surface); r->op = _pixman_operator (composite->op); if (composite->is_bounded == 0) { r->base.render_rows = _mono_unbounded_spans; r->base.finish = _mono_finish_unbounded_spans; r->u.composite.mask_y = composite->unbounded.y; } else r->base.render_rows = _mono_spans; } r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format); return CAIRO_INT_STATUS_SUCCESS; } #define ONE_HALF 0x7f #define RB_MASK 0x00ff00ff #define RB_ONE_HALF 0x007f007f #define RB_MASK_PLUS_ONE 0x01000100 #define G_SHIFT 8 static inline uint32_t mul8x2_8 (uint32_t a, uint8_t b) { uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF; return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK; } static inline uint32_t add8x2_8x2 (uint32_t a, uint32_t b) { uint32_t t = a + b; t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); return t & RB_MASK; } static inline uint8_t mul8_8 (uint8_t a, uint8_t b) { uint16_t t = a * (uint16_t)b + ONE_HALF; return ((t >> G_SHIFT) + t) >> G_SHIFT; } static inline uint32_t lerp8x4 (uint32_t src, uint8_t a, uint32_t dst) { return (add8x2_8x2 (mul8x2_8 (src, a), mul8x2_8 (dst, ~a)) | add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a), mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT); } static cairo_status_t _fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { uint8_t *d = r->u.fill.data + r->u.fill.stride*y; do { uint8_t a = spans[0].coverage; if (a) { int len = spans[1].x - spans[0].x; if (a == 0xff) { memset(d + spans[0].x, r->u.fill.pixel, len); } else { uint8_t s = mul8_8(a, r->u.fill.pixel); uint8_t *dst = d + spans[0].x; a = ~a; while (len--) { uint8_t t = mul8_8(*dst, a); *dst++ = t + s; } } } spans++; } while (--num_spans > 1); } else { do { uint8_t a = spans[0].coverage; if (a) { int yy = y, hh = h; if (a == 0xff) { do { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; memset(d, r->u.fill.pixel, len); yy++; } while (--hh); } else { uint8_t s = mul8_8(a, r->u.fill.pixel); a = ~a; do { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; while (len--) { uint8_t t = mul8_8(*d, a); *d++ = t + s; } yy++; } while (--hh); } } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { uint8_t a = spans[0].coverage; if (a) { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); if (a == 0xff) { if (len > 31) { pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, spans[0].x, y, len, 1, r->u.fill.pixel); } else { uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); while (len--) *d++ = r->u.fill.pixel; } } else while (len--) { *d = lerp8x4 (r->u.fill.pixel, a, *d); d++; } } spans++; } while (--num_spans > 1); } else { do { uint8_t a = spans[0].coverage; if (a) { if (a == 0xff) { if (spans[1].x - spans[0].x > 16) { pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, spans[0].x, y, spans[1].x - spans[0].x, h, r->u.fill.pixel); } else { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); while (len--) *d++ = r->u.fill.pixel; yy++; } while (--hh); } } else { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); while (len--) { *d = lerp8x4 (r->u.fill.pixel, a, *d); d++; } yy++; } while (--hh); } } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill_a8_lerp_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; uint16_t ia = ~a; while (len--) { uint16_t t = *d*ia + p; *d++ = (t + (t>>8)) >> 8; } } spans++; } while (--num_spans > 1); } else { do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { int yy = y, hh = h; uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; uint16_t ia = ~a; do { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; while (len--) { uint16_t t = *d*ia + p; *d++ = (t + (t>>8)) >> 8; } yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); while (len--) { *d = lerp8x4 (r->u.fill.pixel, a, *d); d++; } } spans++; } while (--num_spans > 1); } else { do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { int yy = y, hh = h; do { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); while (len--) { *d = lerp8x4 (r->u.fill.pixel, a, *d); d++; } yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (likely(h == 1)) { uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { uint32_t *s = (uint32_t*)src + spans[0].x; uint32_t *d = (uint32_t*)dst + spans[0].x; int len = spans[1].x - spans[0].x; if (a == 0xff) { if (len == 1) *d = *s; else memcpy(d, s, len*4); } else { while (len--) { *d = lerp8x4 (*s, a, *d); s++, d++; } } } spans++; } while (--num_spans > 1); } else { do { uint8_t a = mul8_8 (spans[0].coverage, r->bpp); if (a) { int yy = y, hh = h; do { uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4); uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4); int len = spans[1].x - spans[0].x; if (a == 0xff) { if (len == 1) *d = *s; else memcpy(d, s, len * 4); } else { while (len--) { *d = lerp8x4 (*s, a, *d); s++, d++; } } yy++; } while (--hh); } spans++; } while (--num_spans > 1); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _inplace_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *mask; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; if (num_spans == 2 && spans[0].coverage == 0xff) { pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, spans[1].x - spans[0].x, h); return CAIRO_STATUS_SUCCESS; } mask = (uint8_t *)pixman_image_get_data (r->mask); x1 = x0 = spans[0].x; do { int len = spans[1].x - spans[0].x; *mask++ = spans[0].coverage; if (len > 1) { if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) { if (x1 != x0) { pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, x1 - x0, h); } pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, len, h); mask = (uint8_t *)pixman_image_get_data (r->mask); x0 = spans[1].x; } else if (spans[0].coverage == 0x0 && x1 - x0 > r->u.composite.run_length) { pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, x1 - x0, h); mask = (uint8_t *)pixman_image_get_data (r->mask); x0 = spans[1].x; }else { memset (mask, spans[0].coverage, --len); mask += len; } } x1 = spans[1].x; spans++; } while (--num_spans > 1); if (x1 != x0) { pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, x1 - x0, h); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _inplace_opacity_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *mask; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; mask = (uint8_t *)pixman_image_get_data (r->mask); x1 = x0 = spans[0].x; do { int len = spans[1].x - spans[0].x; uint8_t m = mul8_8(spans[0].coverage, r->bpp); *mask++ = m; if (len > 1) { if (m == 0 && x1 - x0 > r->u.composite.run_length) { pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, x1 - x0, h); mask = (uint8_t *)pixman_image_get_data (r->mask); x0 = spans[1].x; }else { memset (mask, m, --len); mask += len; } } x1 = spans[1].x; spans++; } while (--num_spans > 1); if (x1 != x0) { pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, x1 - x0, h); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _inplace_src_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *m; int x0; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; x0 = spans[0].x; m = r->_buf; do { int len = spans[1].x - spans[0].x; if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) { if (spans[0].x != x0) { #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #else pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, r->mask, NULL, r->u.composite.dst, 0, 0, 0, 0, x0, y, spans[0].x - x0, h); pixman_image_composite32 (PIXMAN_OP_ADD, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #endif } pixman_image_composite32 (PIXMAN_OP_SRC, r->src, NULL, r->u.composite.dst, spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, spans[0].x, y, spans[1].x - spans[0].x, h); m = r->_buf; x0 = spans[1].x; } else if (spans[0].coverage == 0x0) { if (spans[0].x != x0) { #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #else pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, r->mask, NULL, r->u.composite.dst, 0, 0, 0, 0, x0, y, spans[0].x - x0, h); pixman_image_composite32 (PIXMAN_OP_ADD, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #endif } m = r->_buf; x0 = spans[1].x; } else { *m++ = spans[0].coverage; if (len > 1) { memset (m, spans[0].coverage, --len); m += len; } } spans++; } while (--num_spans > 1); if (spans[0].x != x0) { #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #else pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, r->mask, NULL, r->u.composite.dst, 0, 0, 0, 0, x0, y, spans[0].x - x0, h); pixman_image_composite32 (PIXMAN_OP_ADD, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #endif } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _inplace_src_opacity_spans (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { cairo_image_span_renderer_t *r = abstract_renderer; uint8_t *mask; int x0; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; x0 = spans[0].x; mask = (uint8_t *)pixman_image_get_data (r->mask); do { int len = spans[1].x - spans[0].x; uint8_t m = mul8_8(spans[0].coverage, r->bpp); if (m == 0) { if (spans[0].x != x0) { #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #else pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, r->mask, NULL, r->u.composite.dst, 0, 0, 0, 0, x0, y, spans[0].x - x0, h); pixman_image_composite32 (PIXMAN_OP_ADD, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #endif } mask = (uint8_t *)pixman_image_get_data (r->mask); x0 = spans[1].x; } else { *mask++ = m; if (len > 1) { memset (mask, m, --len); mask += len; } } spans++; } while (--num_spans > 1); if (spans[0].x != x0) { #if PIXMAN_HAS_OP_LERP pixman_image_composite32 (PIXMAN_OP_LERP_SRC, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #else pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, r->mask, NULL, r->u.composite.dst, 0, 0, 0, 0, x0, y, spans[0].x - x0, h); pixman_image_composite32 (PIXMAN_OP_ADD, r->src, r->mask, r->u.composite.dst, x0 + r->u.composite.src_x, y + r->u.composite.src_y, 0, 0, x0, y, spans[0].x - x0, h); #endif } return CAIRO_STATUS_SUCCESS; } static void free_pixels (pixman_image_t *image, void *data) { free (data); } static cairo_int_status_t inplace_renderer_init (cairo_image_span_renderer_t *r, const cairo_composite_rectangles_t *composite, cairo_antialias_t antialias, cairo_bool_t needs_clip) { cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; uint8_t *buf; if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; r->base.render_rows = NULL; r->bpp = composite->mask_pattern.solid.color.alpha_short >> 8; if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_color_t *color; color = &composite->source_pattern.solid.color; if (composite->op == CAIRO_OPERATOR_CLEAR) color = CAIRO_COLOR_TRANSPARENT; if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) { /* Use plain C for the fill operations as the span length is * typically small, too small to payback the startup overheads of * using SSE2 etc. */ if (r->bpp == 0xff) { switch (dst->format) { case CAIRO_FORMAT_A8: r->base.render_rows = _fill_a8_lerp_opaque_spans; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: r->base.render_rows = _fill_xrgb32_lerp_opaque_spans; break; case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_INVALID: default: break; } } else { switch (dst->format) { case CAIRO_FORMAT_A8: r->base.render_rows = _fill_a8_lerp_spans; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: r->base.render_rows = _fill_xrgb32_lerp_spans; break; case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_INVALID: default: break; } } r->u.fill.data = dst->data; r->u.fill.stride = dst->stride; } } else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) && (composite->op == CAIRO_OPERATOR_SOURCE || (composite->op == CAIRO_OPERATOR_OVER && (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) { cairo_image_surface_t *src = to_image_surface(composite->source_pattern.surface.surface); int tx, ty; if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, &tx, &ty) && composite->bounded.x + tx >= 0 && composite->bounded.y + ty >= 0 && composite->bounded.x + composite->bounded.width + tx <= src->width && composite->bounded.y + composite->bounded.height + ty <= src->height) { assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32); r->u.blit.stride = dst->stride; r->u.blit.data = dst->data; r->u.blit.src_stride = src->stride; r->u.blit.src_data = src->data + src->stride * ty + tx * 4; r->base.render_rows = _blit_xrgb32_lerp_spans; } } if (r->base.render_rows == NULL) { const cairo_pattern_t *src = &composite->source_pattern.base; unsigned int width; if (composite->is_bounded == 0) return CAIRO_INT_STATUS_UNSUPPORTED; r->base.render_rows = r->bpp == 0xff ? _inplace_spans : _inplace_opacity_spans; width = (composite->bounded.width + 3) & ~3; r->u.composite.run_length = 8; if (src->type == CAIRO_PATTERN_TYPE_LINEAR || src->type == CAIRO_PATTERN_TYPE_RADIAL) r->u.composite.run_length = 256; if (dst->base.is_clear && (composite->op == CAIRO_OPERATOR_SOURCE || composite->op == CAIRO_OPERATOR_OVER || composite->op == CAIRO_OPERATOR_ADD)) { r->op = PIXMAN_OP_SRC; } else if (composite->op == CAIRO_OPERATOR_SOURCE) { r->base.render_rows = r->bpp == 0xff ? _inplace_src_spans : _inplace_src_opacity_spans; r->u.composite.mask_y = r->composite->unbounded.y; width = (composite->unbounded.width + 3) & ~3; } else if (composite->op == CAIRO_OPERATOR_CLEAR) { r->op = PIXMAN_OP_OUT_REVERSE; src = NULL; } else { r->op = _pixman_operator (composite->op); } r->src = _pixman_image_for_pattern (dst, src, FALSE, &composite->bounded, &composite->source_sample_area, &r->u.composite.src_x, &r->u.composite.src_y); if (unlikely (r->src == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* Create an effectively unbounded mask by repeating the single line */ buf = r->_buf; if (width > SZ_BUF) { buf = malloc (width); if (unlikely (buf == NULL)) { pixman_image_unref (r->src); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } r->mask = pixman_image_create_bits (PIXMAN_a8, width, composite->unbounded.height, (uint32_t *)buf, 0); if (unlikely (r->mask == NULL)) { pixman_image_unref (r->src); if (buf != r->_buf) free (buf); return _cairo_error(CAIRO_STATUS_NO_MEMORY); } if (buf != r->_buf) pixman_image_set_destroy_function (r->mask, free_pixels, buf); r->u.composite.dst = dst->pixman_image; } return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t span_renderer_init (cairo_abstract_span_renderer_t *_r, const cairo_composite_rectangles_t *composite, cairo_antialias_t antialias, cairo_bool_t needs_clip) { cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; const cairo_pattern_t *source = &composite->source_pattern.base; cairo_operator_t op = composite->op; cairo_int_status_t status; TRACE ((stderr, "%s: antialias=%d, needs_clip=%d\n", __FUNCTION__, antialias, needs_clip)); if (needs_clip) return CAIRO_INT_STATUS_UNSUPPORTED; r->composite = composite; r->mask = NULL; r->src = NULL; r->base.finish = NULL; status = mono_renderer_init (r, composite, antialias, needs_clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = inplace_renderer_init (r, composite, antialias, needs_clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; r->bpp = 0; if (op == CAIRO_OPERATOR_CLEAR) { #if PIXMAN_HAS_OP_LERP op = PIXMAN_OP_LERP_CLEAR; #else source = &_cairo_pattern_white.base; op = PIXMAN_OP_OUT_REVERSE; #endif } else if (dst->base.is_clear && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) { op = PIXMAN_OP_SRC; } else if (op == CAIRO_OPERATOR_SOURCE) { if (_cairo_pattern_is_opaque (&composite->source_pattern.base, &composite->source_sample_area)) { op = PIXMAN_OP_OVER; } else { #if PIXMAN_HAS_OP_LERP op = PIXMAN_OP_LERP_SRC; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } } else { op = _pixman_operator (op); } r->op = op; r->src = _pixman_image_for_pattern (dst, source, FALSE, &composite->unbounded, &composite->source_sample_area, &r->u.mask.src_x, &r->u.mask.src_y); if (unlikely (r->src == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); r->opacity = 1.0; if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { r->opacity = composite->mask_pattern.solid.color.alpha; } else { pixman_image_t *mask; int mask_x, mask_y; mask = _pixman_image_for_pattern (dst, &composite->mask_pattern.base, TRUE, &composite->unbounded, &composite->mask_sample_area, &mask_x, &mask_y); if (unlikely (mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* XXX Component-alpha? */ if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 && _cairo_pattern_is_opaque (source, &composite->source_sample_area)) { pixman_image_unref (r->src); r->src = mask; r->u.mask.src_x = mask_x; r->u.mask.src_y = mask_y; mask = NULL; } if (mask) { pixman_image_unref (mask); return CAIRO_INT_STATUS_UNSUPPORTED; } } r->u.mask.extents = composite->unbounded; r->u.mask.stride = (r->u.mask.extents.width + 3) & ~3; if (r->u.mask.extents.height * r->u.mask.stride > SZ_BUF) { r->mask = pixman_image_create_bits (PIXMAN_a8, r->u.mask.extents.width, r->u.mask.extents.height, NULL, 0); r->base.render_rows = _cairo_image_spans; r->base.finish = NULL; } else { r->mask = pixman_image_create_bits (PIXMAN_a8, r->u.mask.extents.width, r->u.mask.extents.height, (uint32_t *)r->_buf, r->u.mask.stride); r->base.render_rows = _cairo_image_spans_and_zero; r->base.finish = _cairo_image_finish_spans_and_zero; } if (unlikely (r->mask == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); r->u.mask.data = (uint8_t *) pixman_image_get_data (r->mask); r->u.mask.stride = pixman_image_get_stride (r->mask); r->u.mask.extents.height += r->u.mask.extents.y; return CAIRO_STATUS_SUCCESS; } static void span_renderer_fini (cairo_abstract_span_renderer_t *_r, cairo_int_status_t status) { cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r; TRACE ((stderr, "%s\n", __FUNCTION__)); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { if (r->base.finish) r->base.finish (r); } if (likely (status == CAIRO_INT_STATUS_SUCCESS && r->bpp == 0)) { const cairo_composite_rectangles_t *composite = r->composite; pixman_image_composite32 (r->op, r->src, r->mask, to_pixman_image (composite->surface), composite->unbounded.x + r->u.mask.src_x, composite->unbounded.y + r->u.mask.src_y, 0, 0, composite->unbounded.x, composite->unbounded.y, composite->unbounded.width, composite->unbounded.height); } if (r->src) pixman_image_unref (r->src); if (r->mask) pixman_image_unref (r->mask); } #endif const cairo_compositor_t * _cairo_image_spans_compositor_get (void) { static cairo_spans_compositor_t spans; static cairo_compositor_t shape; if (spans.base.delegate == NULL) { _cairo_shape_mask_compositor_init (&shape, _cairo_image_traps_compositor_get()); shape.glyphs = NULL; _cairo_spans_compositor_init (&spans, &shape); spans.flags = 0; #if PIXMAN_HAS_OP_LERP spans.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP; #endif //spans.acquire = acquire; //spans.release = release; spans.fill_boxes = fill_boxes; spans.draw_image_boxes = draw_image_boxes; //spans.copy_boxes = copy_boxes; spans.pattern_to_surface = _cairo_image_source_create_for_pattern; //spans.check_composite_boxes = check_composite_boxes; spans.composite_boxes = composite_boxes; //spans.check_span_renderer = check_span_renderer; spans.renderer_init = span_renderer_init; spans.renderer_fini = span_renderer_fini; } return &spans.base; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-info-private.h����������������������0000664�0000000�0000000�00000004310�12710376503�0027225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_IMAGE_INFO_PRIVATE_H #define CAIRO_IMAGE_INFO_PRIVATE_H #include "cairoint.h" typedef struct _cairo_image_info { int width; int height; int num_components; int bits_per_component; } cairo_image_info_t; cairo_private cairo_int_status_t _cairo_image_info_get_jpeg_info (cairo_image_info_t *info, const unsigned char *data, long length); cairo_private cairo_int_status_t _cairo_image_info_get_jpx_info (cairo_image_info_t *info, const unsigned char *data, unsigned long length); cairo_private cairo_int_status_t _cairo_image_info_get_png_info (cairo_image_info_t *info, const unsigned char *data, unsigned long length); #endif /* CAIRO_IMAGE_INFO_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-info.c������������������������������0000664�0000000�0000000�00000015617�12710376503�0025564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-info-private.h" static uint32_t _get_be32 (const unsigned char *p) { return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } /* JPEG (image/jpeg) * * http://www.w3.org/Graphics/JPEG/itu-t81.pdf */ /* Markers with no parameters. All other markers are followed by a two * byte length of the parameters. */ #define TEM 0x01 #define RST_begin 0xd0 #define RST_end 0xd7 #define SOI 0xd8 #define EOI 0xd9 /* Start of frame markers. */ #define SOF0 0xc0 #define SOF1 0xc1 #define SOF2 0xc2 #define SOF3 0xc3 #define SOF5 0xc5 #define SOF6 0xc6 #define SOF7 0xc7 #define SOF9 0xc9 #define SOF10 0xca #define SOF11 0xcb #define SOF13 0xcd #define SOF14 0xce #define SOF15 0xcf static const unsigned char * _jpeg_skip_segment (const unsigned char *p) { int len; p++; len = (p[0] << 8) | p[1]; return p + len; } static void _jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p) { info->width = (p[6] << 8) + p[7]; info->height = (p[4] << 8) + p[5]; info->num_components = p[8]; info->bits_per_component = p[3]; } cairo_int_status_t _cairo_image_info_get_jpeg_info (cairo_image_info_t *info, const unsigned char *data, long length) { const unsigned char *p = data; while (p + 1 < data + length) { if (*p != 0xff) return CAIRO_INT_STATUS_UNSUPPORTED; p++; switch (*p) { /* skip fill bytes */ case 0xff: p++; break; case TEM: case SOI: case EOI: p++; break; case SOF0: case SOF1: case SOF2: case SOF3: case SOF5: case SOF6: case SOF7: case SOF9: case SOF10: case SOF11: case SOF13: case SOF14: case SOF15: /* Start of frame found. Extract the image parameters. */ if (p + 8 > data + length) return CAIRO_INT_STATUS_UNSUPPORTED; _jpeg_extract_info (info, p); return CAIRO_STATUS_SUCCESS; default: if (*p >= RST_begin && *p <= RST_end) { p++; break; } if (p + 2 > data + length) return CAIRO_INT_STATUS_UNSUPPORTED; p = _jpeg_skip_segment (p); break; } } return CAIRO_STATUS_SUCCESS; } /* JPEG 2000 (image/jp2) * * http://www.jpeg.org/public/15444-1annexi.pdf */ #define JPX_FILETYPE 0x66747970 #define JPX_JP2_HEADER 0x6A703268 #define JPX_IMAGE_HEADER 0x69686472 static const unsigned char _jpx_signature[] = { 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a }; static const unsigned char * _jpx_next_box (const unsigned char *p) { return p + _get_be32 (p); } static const unsigned char * _jpx_get_box_contents (const unsigned char *p) { return p + 8; } static cairo_bool_t _jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) { uint32_t length; if (p + 8 < end) { length = _get_be32 (p); if (_get_be32 (p + 4) == type && p + length < end) return TRUE; } return FALSE; } static const unsigned char * _jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) { while (p < end) { if (_jpx_match_box (p, end, type)) return p; p = _jpx_next_box (p); } return NULL; } static void _jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) { info->height = _get_be32 (p); info->width = _get_be32 (p + 4); info->num_components = (p[8] << 8) + p[9]; info->bits_per_component = p[10]; } cairo_int_status_t _cairo_image_info_get_jpx_info (cairo_image_info_t *info, const unsigned char *data, unsigned long length) { const unsigned char *p = data; const unsigned char *end = data + length; /* First 12 bytes must be the JPEG 2000 signature box. */ if (length < ARRAY_LENGTH(_jpx_signature) || memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0) return CAIRO_INT_STATUS_UNSUPPORTED; p += ARRAY_LENGTH(_jpx_signature); /* Next box must be a File Type Box */ if (! _jpx_match_box (p, end, JPX_FILETYPE)) return CAIRO_INT_STATUS_UNSUPPORTED; p = _jpx_next_box (p); /* Locate the JP2 header box. */ p = _jpx_find_box (p, end, JPX_JP2_HEADER); if (!p) return CAIRO_INT_STATUS_UNSUPPORTED; /* Step into the JP2 header box. First box must be the Image * Header */ p = _jpx_get_box_contents (p); if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Get the image info */ p = _jpx_get_box_contents (p); _jpx_extract_info (p, info); return CAIRO_STATUS_SUCCESS; } /* PNG (image/png) * * http://www.w3.org/TR/2003/REC-PNG-20031110/ */ #define PNG_IHDR 0x49484452 static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; cairo_int_status_t _cairo_image_info_get_png_info (cairo_image_info_t *info, const unsigned char *data, unsigned long length) { const unsigned char *p = data; const unsigned char *end = data + length; if (length < 8 || memcmp (data, _png_magic, 8) != 0) return CAIRO_INT_STATUS_UNSUPPORTED; p += 8; /* The first chunk must be IDHR. IDHR has 13 bytes of data plus * the 12 bytes of overhead for the chunk. */ if (p + 13 + 12 > end) return CAIRO_INT_STATUS_UNSUPPORTED; p += 4; if (_get_be32 (p) != PNG_IHDR) return CAIRO_INT_STATUS_UNSUPPORTED; p += 4; info->width = _get_be32 (p); p += 4; info->height = _get_be32 (p); return CAIRO_STATUS_SUCCESS; } �����������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-source.c����������������������������0000664�0000000�0000000�00000077217�12710376503�0026135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2009,2010,2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* The purpose of this file/surface is to simply translate a pattern * to a pixman_image_t and thence to feed it back to the general * compositor interface. */ #include "cairoint.h" #include "cairo-image-surface-private.h" #include "cairo-compositor-private.h" #include "cairo-error-private.h" #include "cairo-pattern-inline.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-observer-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ #if CAIRO_NO_MUTEX #define PIXMAN_HAS_ATOMIC_OPS 1 #endif #if PIXMAN_HAS_ATOMIC_OPS static pixman_image_t *__pixman_transparent_image; static pixman_image_t *__pixman_black_image; static pixman_image_t *__pixman_white_image; static pixman_image_t * _pixman_transparent_image (void) { pixman_image_t *image; TRACE ((stderr, "%s\n", __FUNCTION__)); image = __pixman_transparent_image; if (unlikely (image == NULL)) { pixman_color_t color; color.red = 0x00; color.green = 0x00; color.blue = 0x00; color.alpha = 0x00; image = pixman_image_create_solid_fill (&color); if (unlikely (image == NULL)) return NULL; if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, NULL, image)) { pixman_image_ref (image); } } else { pixman_image_ref (image); } return image; } static pixman_image_t * _pixman_black_image (void) { pixman_image_t *image; TRACE ((stderr, "%s\n", __FUNCTION__)); image = __pixman_black_image; if (unlikely (image == NULL)) { pixman_color_t color; color.red = 0x00; color.green = 0x00; color.blue = 0x00; color.alpha = 0xffff; image = pixman_image_create_solid_fill (&color); if (unlikely (image == NULL)) return NULL; if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, NULL, image)) { pixman_image_ref (image); } } else { pixman_image_ref (image); } return image; } static pixman_image_t * _pixman_white_image (void) { pixman_image_t *image; TRACE ((stderr, "%s\n", __FUNCTION__)); image = __pixman_white_image; if (unlikely (image == NULL)) { pixman_color_t color; color.red = 0xffff; color.green = 0xffff; color.blue = 0xffff; color.alpha = 0xffff; image = pixman_image_create_solid_fill (&color); if (unlikely (image == NULL)) return NULL; if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, NULL, image)) { pixman_image_ref (image); } } else { pixman_image_ref (image); } return image; } static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } static struct { cairo_color_t color; pixman_image_t *image; } cache[16]; static int n_cached; #else /* !PIXMAN_HAS_ATOMIC_OPS */ static pixman_image_t * _pixman_transparent_image (void) { TRACE ((stderr, "%s\n", __FUNCTION__)); return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT); } static pixman_image_t * _pixman_black_image (void) { TRACE ((stderr, "%s\n", __FUNCTION__)); return _pixman_image_for_color (CAIRO_COLOR_BLACK); } static pixman_image_t * _pixman_white_image (void) { TRACE ((stderr, "%s\n", __FUNCTION__)); return _pixman_image_for_color (CAIRO_COLOR_WHITE); } #endif /* !PIXMAN_HAS_ATOMIC_OPS */ pixman_image_t * _pixman_image_for_color (const cairo_color_t *cairo_color) { pixman_color_t color; pixman_image_t *image; #if PIXMAN_HAS_ATOMIC_OPS int i; if (CAIRO_COLOR_IS_CLEAR (cairo_color)) return _pixman_transparent_image (); if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) { if (cairo_color->red_short <= 0x00ff && cairo_color->green_short <= 0x00ff && cairo_color->blue_short <= 0x00ff) { return _pixman_black_image (); } if (cairo_color->red_short >= 0xff00 && cairo_color->green_short >= 0xff00 && cairo_color->blue_short >= 0xff00) { return _pixman_white_image (); } } CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); for (i = 0; i < n_cached; i++) { if (_cairo_color_equal (&cache[i].color, cairo_color)) { image = pixman_image_ref (cache[i].image); goto UNLOCK; } } #endif color.red = cairo_color->red_short; color.green = cairo_color->green_short; color.blue = cairo_color->blue_short; color.alpha = cairo_color->alpha_short; image = pixman_image_create_solid_fill (&color); #if PIXMAN_HAS_ATOMIC_OPS if (image == NULL) goto UNLOCK; if (n_cached < ARRAY_LENGTH (cache)) { i = n_cached++; } else { i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); pixman_image_unref (cache[i].image); } cache[i].image = pixman_image_ref (image); cache[i].color = *cairo_color; UNLOCK: CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); #endif return image; } void _cairo_image_reset_static_data (void) { #if PIXMAN_HAS_ATOMIC_OPS while (n_cached) pixman_image_unref (cache[--n_cached].image); if (__pixman_transparent_image) { pixman_image_unref (__pixman_transparent_image); __pixman_transparent_image = NULL; } if (__pixman_black_image) { pixman_image_unref (__pixman_black_image); __pixman_black_image = NULL; } if (__pixman_white_image) { pixman_image_unref (__pixman_white_image); __pixman_white_image = NULL; } #endif } static pixman_image_t * _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *ix, int *iy) { pixman_image_t *pixman_image; pixman_gradient_stop_t pixman_stops_static[2]; pixman_gradient_stop_t *pixman_stops = pixman_stops_static; pixman_transform_t pixman_transform; cairo_matrix_t matrix; cairo_circle_double_t extremes[2]; pixman_point_fixed_t p1, p2; unsigned int i; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof(pixman_gradient_stop_t)); if (unlikely (pixman_stops == NULL)) return NULL; } for (i = 0; i < pattern->n_stops; i++) { pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); pixman_stops[i].color.red = pattern->stops[i].color.red_short; pixman_stops[i].color.green = pattern->stops[i].color.green_short; pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; } _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes); p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { pixman_image = pixman_image_create_linear_gradient (&p1, &p2, pixman_stops, pattern->n_stops); } else { pixman_fixed_t r1, r2; r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2, pixman_stops, pattern->n_stops); } if (pixman_stops != pixman_stops_static) free (pixman_stops); if (unlikely (pixman_image == NULL)) return NULL; *ix = *iy = 0; status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter, extents->x + extents->width/2., extents->y + extents->height/2., &pixman_transform, ix, iy); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) || ! pixman_image_set_transform (pixman_image, &pixman_transform)) { pixman_image_unref (pixman_image); return NULL; } } { pixman_repeat_t pixman_repeat; switch (pattern->base.extend) { default: case CAIRO_EXTEND_NONE: pixman_repeat = PIXMAN_REPEAT_NONE; break; case CAIRO_EXTEND_REPEAT: pixman_repeat = PIXMAN_REPEAT_NORMAL; break; case CAIRO_EXTEND_REFLECT: pixman_repeat = PIXMAN_REPEAT_REFLECT; break; case CAIRO_EXTEND_PAD: pixman_repeat = PIXMAN_REPEAT_PAD; break; } pixman_image_set_repeat (pixman_image, pixman_repeat); } return pixman_image; } static pixman_image_t * _pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *tx, int *ty) { pixman_image_t *image; int width, height; TRACE ((stderr, "%s\n", __FUNCTION__)); *tx = -extents->x; *ty = -extents->y; width = extents->width; height = extents->height; image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0); if (unlikely (image == NULL)) return NULL; _cairo_mesh_pattern_rasterize (pattern, pixman_image_get_data (image), width, height, pixman_image_get_stride (image), *tx, *ty); return image; } struct acquire_source_cleanup { cairo_surface_t *surface; cairo_image_surface_t *image; void *image_extra; }; static void _acquire_source_cleanup (pixman_image_t *pixman_image, void *closure) { struct acquire_source_cleanup *data = closure; _cairo_surface_release_source_image (data->surface, data->image, data->image_extra); free (data); } static void _defer_free_cleanup (pixman_image_t *pixman_image, void *closure) { cairo_surface_destroy (closure); } static uint16_t expand_channel (uint16_t v, uint32_t bits) { int offset = 16 - bits; while (offset > 0) { v |= v >> bits; offset -= bits; bits += bits; } return v; } static pixman_image_t * _pixel_to_solid (cairo_image_surface_t *image, int x, int y) { uint32_t pixel; pixman_color_t color; TRACE ((stderr, "%s\n", __FUNCTION__)); switch (image->format) { default: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return NULL; case CAIRO_FORMAT_A1: pixel = *(uint8_t *) (image->data + y * image->stride + x/8); return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image (); case CAIRO_FORMAT_A8: color.alpha = *(uint8_t *) (image->data + y * image->stride + x); color.alpha |= color.alpha << 8; if (color.alpha == 0) return _pixman_transparent_image (); if (color.alpha == 0xffff) return _pixman_black_image (); color.red = color.green = color.blue = 0; return pixman_image_create_solid_fill (&color); case CAIRO_FORMAT_RGB16_565: pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); if (pixel == 0) return _pixman_black_image (); if (pixel == 0xffff) return _pixman_white_image (); color.alpha = 0xffff; color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); color.blue = expand_channel ((pixel & 0x1f) << 11, 5); return pixman_image_create_solid_fill (&color); case CAIRO_FORMAT_RGB30: pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); pixel &= 0x3fffffff; /* ignore alpha bits */ if (pixel == 0) return _pixman_black_image (); if (pixel == 0x3fffffff) return _pixman_white_image (); /* convert 10bpc to 16bpc */ color.alpha = 0xffff; color.red = expand_channel((pixel >> 20) & 0x3fff, 10); color.green = expand_channel((pixel >> 10) & 0x3fff, 10); color.blue = expand_channel(pixel & 0x3fff, 10); return pixman_image_create_solid_fill (&color); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; if (color.alpha == 0) return _pixman_transparent_image (); if (pixel == 0xffffffff) return _pixman_white_image (); if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) return _pixman_black_image (); color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); return pixman_image_create_solid_fill (&color); } } static cairo_bool_t _pixman_image_set_properties (pixman_image_t *pixman_image, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *ix,int *iy) { pixman_transform_t pixman_transform; cairo_int_status_t status; status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix, pattern->filter, extents->x + extents->width/2., extents->y + extents->height/2., &pixman_transform, ix, iy); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { /* If the transform is an identity, we don't need to set it * and we can use any filtering, so choose the fastest one. */ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); } else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS || ! pixman_image_set_transform (pixman_image, &pixman_transform))) { return FALSE; } else { pixman_filter_t pixman_filter; switch (pattern->filter) { case CAIRO_FILTER_FAST: pixman_filter = PIXMAN_FILTER_FAST; break; case CAIRO_FILTER_GOOD: pixman_filter = PIXMAN_FILTER_GOOD; break; case CAIRO_FILTER_BEST: pixman_filter = PIXMAN_FILTER_BEST; break; case CAIRO_FILTER_NEAREST: pixman_filter = PIXMAN_FILTER_NEAREST; break; case CAIRO_FILTER_BILINEAR: pixman_filter = PIXMAN_FILTER_BILINEAR; break; case CAIRO_FILTER_GAUSSIAN: /* XXX: The GAUSSIAN value has no implementation in cairo * whatsoever, so it was really a mistake to have it in the * API. We could fix this by officially deprecating it, or * else inventing semantics and providing an actual * implementation for it. */ default: pixman_filter = PIXMAN_FILTER_BEST; } pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); } { pixman_repeat_t pixman_repeat; switch (pattern->extend) { default: case CAIRO_EXTEND_NONE: pixman_repeat = PIXMAN_REPEAT_NONE; break; case CAIRO_EXTEND_REPEAT: pixman_repeat = PIXMAN_REPEAT_NORMAL; break; case CAIRO_EXTEND_REFLECT: pixman_repeat = PIXMAN_REPEAT_REFLECT; break; case CAIRO_EXTEND_PAD: pixman_repeat = PIXMAN_REPEAT_PAD; break; } pixman_image_set_repeat (pixman_image, pixman_repeat); } if (pattern->has_component_alpha) pixman_image_set_component_alpha (pixman_image, TRUE); return TRUE; } struct proxy { cairo_surface_t base; cairo_surface_t *image; }; static cairo_status_t proxy_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { struct proxy *proxy = abstract_surface; return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); } static void proxy_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { struct proxy *proxy = abstract_surface; _cairo_surface_release_source_image (proxy->image, image, image_extra); } static cairo_status_t proxy_finish (void *abstract_surface) { return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t proxy_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, proxy_finish, NULL, NULL, /* create similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, proxy_acquire_source_image, proxy_release_source_image, }; static cairo_surface_t * attach_proxy (cairo_surface_t *source, cairo_surface_t *image) { struct proxy *proxy; proxy = malloc (sizeof (*proxy)); if (unlikely (proxy == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content); proxy->image = image; _cairo_surface_attach_snapshot (source, &proxy->base, NULL); return &proxy->base; } static void detach_proxy (cairo_surface_t *source, cairo_surface_t *proxy) { cairo_surface_finish (proxy); cairo_surface_destroy (proxy); } static cairo_surface_t * get_proxy (cairo_surface_t *proxy) { return ((struct proxy *)proxy)->image; } static pixman_image_t * _pixman_image_for_recording (cairo_image_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *ix, int *iy) { cairo_surface_t *source, *clone, *proxy; cairo_rectangle_int_t limit; pixman_image_t *pixman_image; cairo_status_t status; cairo_extend_t extend; cairo_matrix_t *m, matrix; int tx = 0, ty = 0; TRACE ((stderr, "%s\n", __FUNCTION__)); *ix = *iy = 0; source = _cairo_pattern_get_source (pattern, &limit); extend = pattern->base.extend; if (_cairo_rectangle_contains_rectangle (&limit, sample)) extend = CAIRO_EXTEND_NONE; if (extend == CAIRO_EXTEND_NONE) { if (! _cairo_rectangle_intersect (&limit, sample)) return _pixman_transparent_image (); if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { double x1, y1, x2, y2; matrix = pattern->base.matrix; status = cairo_matrix_invert (&matrix); assert (status == CAIRO_STATUS_SUCCESS); x1 = limit.x; y1 = limit.y; x2 = limit.x + limit.width; y2 = limit.y + limit.height; _cairo_matrix_transform_bounding_box (&matrix, &x1, &y1, &x2, &y2, NULL); limit.x = floor (x1); limit.y = floor (y1); limit.width = ceil (x2) - limit.x; limit.height = ceil (y2) - limit.y; } } tx = limit.x; ty = limit.y; /* XXX transformations! */ proxy = _cairo_surface_has_snapshot (source, &proxy_backend); if (proxy != NULL) { clone = cairo_surface_reference (get_proxy (proxy)); goto done; } if (is_mask) { clone = cairo_image_surface_create (CAIRO_FORMAT_A8, limit.width, limit.height); } else { if (dst->base.content == source->content) clone = cairo_image_surface_create (dst->format, limit.width, limit.height); else clone = _cairo_image_surface_create_with_content (source->content, limit.width, limit.height); } m = NULL; if (extend == CAIRO_EXTEND_NONE) { matrix = pattern->base.matrix; if (tx | ty) cairo_matrix_translate (&matrix, tx, ty); m = &matrix; } else { /* XXX extract scale factor for repeating patterns */ } /* Handle recursion by returning future reads from the current image */ proxy = attach_proxy (source, clone); status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL); detach_proxy (source, proxy); if (unlikely (status)) { cairo_surface_destroy (clone); return NULL; } done: pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); cairo_surface_destroy (clone); *ix = -limit.x; *iy = -limit.y; if (extend != CAIRO_EXTEND_NONE) { if (! _pixman_image_set_properties (pixman_image, &pattern->base, extents, ix, iy)) { pixman_image_unref (pixman_image); pixman_image= NULL; } } return pixman_image; } static pixman_image_t * _pixman_image_for_surface (cairo_image_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *ix, int *iy) { cairo_extend_t extend = pattern->base.extend; pixman_image_t *pixman_image; TRACE ((stderr, "%s\n", __FUNCTION__)); *ix = *iy = 0; pixman_image = NULL; if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return _pixman_image_for_recording(dst, pattern, is_mask, extents, sample, ix, iy); if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && (! is_mask || ! pattern->base.has_component_alpha || (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) { cairo_surface_t *defer_free = NULL; cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; cairo_surface_type_t type; if (_cairo_surface_is_snapshot (&source->base)) { defer_free = _cairo_surface_snapshot_get_target (&source->base); source = (cairo_image_surface_t *) defer_free; } type = source->base.backend->type; if (type == CAIRO_SURFACE_TYPE_IMAGE) { if (extend != CAIRO_EXTEND_NONE && sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= source->width && sample->y + sample->height <= source->height) { extend = CAIRO_EXTEND_NONE; } if (sample->width == 1 && sample->height == 1) { if (sample->x < 0 || sample->y < 0 || sample->x >= source->width || sample->y >= source->height) { if (extend == CAIRO_EXTEND_NONE) { cairo_surface_destroy (defer_free); return _pixman_transparent_image (); } } else { pixman_image = _pixel_to_solid (source, sample->x, sample->y); if (pixman_image) { cairo_surface_destroy (defer_free); return pixman_image; } } } #if PIXMAN_HAS_ATOMIC_OPS /* avoid allocating a 'pattern' image if we can reuse the original */ if (extend == CAIRO_EXTEND_NONE && _cairo_matrix_is_pixman_translation (&pattern->base.matrix, pattern->base.filter, ix, iy)) { cairo_surface_destroy (defer_free); return pixman_image_ref (source->pixman_image); } #endif pixman_image = pixman_image_create_bits (source->pixman_format, source->width, source->height, (uint32_t *) source->data, source->stride); if (unlikely (pixman_image == NULL)) { cairo_surface_destroy (defer_free); return NULL; } if (defer_free) { pixman_image_set_destroy_function (pixman_image, _defer_free_cleanup, defer_free); } } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub; cairo_bool_t is_contained = FALSE; sub = (cairo_surface_subsurface_t *) source; source = (cairo_image_surface_t *) sub->target; if (sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= sub->extents.width && sample->y + sample->height <= sub->extents.height) { is_contained = TRUE; } if (sample->width == 1 && sample->height == 1) { if (is_contained) { pixman_image = _pixel_to_solid (source, sub->extents.x + sample->x, sub->extents.y + sample->y); if (pixman_image) return pixman_image; } else { if (extend == CAIRO_EXTEND_NONE) return _pixman_transparent_image (); } } #if PIXMAN_HAS_ATOMIC_OPS *ix = sub->extents.x; *iy = sub->extents.y; if (is_contained && _cairo_matrix_is_pixman_translation (&pattern->base.matrix, pattern->base.filter, ix, iy)) { return pixman_image_ref (source->pixman_image); } #endif /* Avoid sub-byte offsets, force a copy in that case. */ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { if (is_contained) { void *data = source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride; pixman_image = pixman_image_create_bits (source->pixman_format, sub->extents.width, sub->extents.height, data, source->stride); if (unlikely (pixman_image == NULL)) return NULL; } else { /* XXX for a simple translation and EXTEND_NONE we can * fix up the pattern matrix instead. */ } } } } if (pixman_image == NULL) { struct acquire_source_cleanup *cleanup; cairo_image_surface_t *image; void *extra; cairo_status_t status; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); if (unlikely (status)) return NULL; pixman_image = pixman_image_create_bits (image->pixman_format, image->width, image->height, (uint32_t *) image->data, image->stride); if (unlikely (pixman_image == NULL)) { _cairo_surface_release_source_image (pattern->surface, image, extra); return NULL; } cleanup = malloc (sizeof (*cleanup)); if (unlikely (cleanup == NULL)) { _cairo_surface_release_source_image (pattern->surface, image, extra); pixman_image_unref (pixman_image); return NULL; } cleanup->surface = pattern->surface; cleanup->image = image; cleanup->image_extra = extra; pixman_image_set_destroy_function (pixman_image, _acquire_source_cleanup, cleanup); } if (! _pixman_image_set_properties (pixman_image, &pattern->base, extents, ix, iy)) { pixman_image_unref (pixman_image); pixman_image= NULL; } return pixman_image; } struct raster_source_cleanup { const cairo_pattern_t *pattern; cairo_surface_t *surface; cairo_image_surface_t *image; void *image_extra; }; static void _raster_source_cleanup (pixman_image_t *pixman_image, void *closure) { struct raster_source_cleanup *data = closure; _cairo_surface_release_source_image (data->surface, data->image, data->image_extra); _cairo_raster_source_pattern_release (data->pattern, data->surface); free (data); } static pixman_image_t * _pixman_image_for_raster (cairo_image_surface_t *dst, const cairo_raster_source_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *ix, int *iy) { pixman_image_t *pixman_image; struct raster_source_cleanup *cleanup; cairo_image_surface_t *image; void *extra; cairo_status_t status; cairo_surface_t *surface; TRACE ((stderr, "%s\n", __FUNCTION__)); *ix = *iy = 0; surface = _cairo_raster_source_pattern_acquire (&pattern->base, &dst->base, NULL); if (unlikely (surface == NULL || surface->status)) return NULL; status = _cairo_surface_acquire_source_image (surface, &image, &extra); if (unlikely (status)) { _cairo_raster_source_pattern_release (&pattern->base, surface); return NULL; } assert (image->width == pattern->extents.width); assert (image->height == pattern->extents.height); pixman_image = pixman_image_create_bits (image->pixman_format, image->width, image->height, (uint32_t *) image->data, image->stride); if (unlikely (pixman_image == NULL)) { _cairo_surface_release_source_image (surface, image, extra); _cairo_raster_source_pattern_release (&pattern->base, surface); return NULL; } cleanup = malloc (sizeof (*cleanup)); if (unlikely (cleanup == NULL)) { pixman_image_unref (pixman_image); _cairo_surface_release_source_image (surface, image, extra); _cairo_raster_source_pattern_release (&pattern->base, surface); return NULL; } cleanup->pattern = &pattern->base; cleanup->surface = surface; cleanup->image = image; cleanup->image_extra = extra; pixman_image_set_destroy_function (pixman_image, _raster_source_cleanup, cleanup); if (! _pixman_image_set_properties (pixman_image, &pattern->base, extents, ix, iy)) { pixman_image_unref (pixman_image); pixman_image= NULL; } return pixman_image; } pixman_image_t * _pixman_image_for_pattern (cairo_image_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *tx, int *ty) { *tx = *ty = 0; TRACE ((stderr, "%s\n", __FUNCTION__)); if (pattern == NULL) return _pixman_white_image (); switch (pattern->type) { default: ASSERT_NOT_REACHED; case CAIRO_PATTERN_TYPE_SOLID: return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color); case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_LINEAR: return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, extents, tx, ty); case CAIRO_PATTERN_TYPE_MESH: return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern, extents, tx, ty); case CAIRO_PATTERN_TYPE_SURFACE: return _pixman_image_for_surface (dst, (const cairo_surface_pattern_t *) pattern, is_mask, extents, sample, tx, ty); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _pixman_image_for_raster (dst, (const cairo_raster_source_pattern_t *) pattern, is_mask, extents, sample, tx, ty); } } static cairo_status_t _cairo_image_source_finish (void *abstract_surface) { cairo_image_source_t *source = abstract_surface; pixman_image_unref (source->pixman_image); return CAIRO_STATUS_SUCCESS; } const cairo_surface_backend_t _cairo_image_source_backend = { CAIRO_SURFACE_TYPE_IMAGE, _cairo_image_source_finish, NULL, /* read-only wrapper */ }; cairo_surface_t * _cairo_image_source_create_for_pattern (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_image_source_t *source; TRACE ((stderr, "%s\n", __FUNCTION__)); source = malloc (sizeof (cairo_image_source_t)); if (unlikely (source == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); source->pixman_image = _pixman_image_for_pattern ((cairo_image_surface_t *)dst, pattern, is_mask, extents, sample, src_x, src_y); if (unlikely (source->pixman_image == NULL)) { free (source); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } _cairo_surface_init (&source->base, &_cairo_image_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); source->is_opaque_solid = pattern == NULL || _cairo_pattern_is_opaque_solid (pattern); return &source->base; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-surface-inline.h��������������������0000664�0000000�0000000�00000005711�12710376503�0027534�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_IMAGE_SURFACE_INLINE_H #define CAIRO_IMAGE_SURFACE_INLINE_H #include "cairo-surface-private.h" #include "cairo-image-surface-private.h" CAIRO_BEGIN_DECLS static inline cairo_image_surface_t * _cairo_image_surface_create_in_error (cairo_status_t status) { return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); } static inline void _cairo_image_surface_set_parent (cairo_image_surface_t *image, cairo_surface_t *parent) { image->parent = parent; } static inline cairo_bool_t _cairo_image_surface_is_clone (cairo_image_surface_t *image) { return image->parent != NULL; } /** * _cairo_surface_is_image: * @surface: a #cairo_surface_t * * Checks if a surface is an #cairo_image_surface_t * * Return value: %TRUE if the surface is an image surface **/ static inline cairo_bool_t _cairo_surface_is_image (const cairo_surface_t *surface) { /* _cairo_surface_nil sets a NULL backend so be safe */ return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE; } /** * _cairo_surface_is_image_source: * @surface: a #cairo_surface_t * * Checks if a surface is an #cairo_image_source_t * * Return value: %TRUE if the surface is an image source **/ static inline cairo_bool_t _cairo_surface_is_image_source (const cairo_surface_t *surface) { return surface->backend == &_cairo_image_source_backend; } CAIRO_END_DECLS #endif /* CAIRO_IMAGE_SURFACE_INLINE_H */ �������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-surface-private.h�������������������0000664�0000000�0000000�00000017576�12710376503�0027744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H #define CAIRO_IMAGE_SURFACE_PRIVATE_H #include "cairo-surface-private.h" #include <pixman.h> CAIRO_BEGIN_DECLS /* The canonical image backend */ struct _cairo_image_surface { cairo_surface_t base; pixman_image_t *pixman_image; const cairo_compositor_t *compositor; /* Parenting is tricky wrt lifetime tracking... * * One use for tracking the parent of an image surface is for * create_similar_image() where we wish to create a device specific * surface but return an image surface to the user. In such a case, * the image may be owned by the device specific surface, its parent, * but the user lifetime tracking is then performed on the image. So * when the image is then finalized we call cairo_surface_destroy() * on the parent. However, for normal usage where the lifetime tracking * is done on the parent surface, we need to be careful to unhook * the image->parent pointer before finalizing the image. */ cairo_surface_t *parent; pixman_format_code_t pixman_format; cairo_format_t format; unsigned char *data; int width; int height; int stride; int depth; unsigned owns_data : 1; unsigned transparency : 2; unsigned color : 2; }; #define to_image_surface(S) ((cairo_image_surface_t *)(S)) /* A wrapper for holding pixman images returned by create_for_pattern */ typedef struct _cairo_image_source { cairo_surface_t base; pixman_image_t *pixman_image; unsigned is_opaque_solid : 1; } cairo_image_source_t; cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend; cairo_private extern const cairo_surface_backend_t _cairo_image_source_backend; cairo_private const cairo_compositor_t * _cairo_image_mask_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_image_traps_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_image_spans_compositor_get (void); #define _cairo_image_default_compositor_get _cairo_image_spans_compositor_get cairo_private cairo_int_status_t _cairo_image_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_image_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_image_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_image_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_image_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); cairo_private void _cairo_image_surface_init (cairo_image_surface_t *surface, pixman_image_t *pixman_image, pixman_format_code_t pixman_format); cairo_private cairo_surface_t * _cairo_image_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height); cairo_private cairo_image_surface_t * _cairo_image_surface_map_to_image (void *abstract_other, const cairo_rectangle_int_t *extents); cairo_private cairo_int_status_t _cairo_image_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image); cairo_private cairo_surface_t * _cairo_image_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_image_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra); cairo_private void _cairo_image_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra); cairo_private cairo_surface_t * _cairo_image_surface_snapshot (void *abstract_surface); cairo_private_no_warn cairo_bool_t _cairo_image_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); cairo_private void _cairo_image_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options); cairo_private cairo_surface_t * _cairo_image_source_create_for_pattern (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_private cairo_status_t _cairo_image_surface_finish (void *abstract_surface); cairo_private pixman_image_t * _pixman_image_for_color (const cairo_color_t *cairo_color); cairo_private pixman_image_t * _pixman_image_for_pattern (cairo_image_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *tx, int *ty); cairo_private void _pixman_image_add_traps (pixman_image_t *image, int dst_x, int dst_y, cairo_traps_t *traps); cairo_private void _pixman_image_add_tristrip (pixman_image_t *image, int dst_x, int dst_y, cairo_tristrip_t *strip); cairo_private cairo_image_surface_t * _cairo_image_surface_clone_subimage (cairo_surface_t *surface, const cairo_rectangle_int_t *extents); /* Similar to clone; but allow format conversion */ cairo_private cairo_image_surface_t * _cairo_image_surface_create_from_image (cairo_image_surface_t *other, pixman_format_code_t format, int x, int y, int width, int height, int stride); CAIRO_END_DECLS #endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-image-surface.c���������������������������0000664�0000000�0000000�00000114012�12710376503�0026246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2009,2010,2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-private.h" #include "cairo-pixman-private.h" #include "cairo-recording-surface-private.h" #include "cairo-region-private.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" /* Limit on the width / height of an image surface in pixels. This is * mainly determined by coordinates of things sent to pixman at the * moment being in 16.16 format. */ #define MAX_IMAGE_SIZE 32767 /** * SECTION:cairo-image * @Title: Image Surfaces * @Short_Description: Rendering to memory buffers * @See_Also: #cairo_surface_t * * Image surfaces provide the ability to render to memory buffers * either allocated by cairo or by the calling code. The supported * image formats are those defined in #cairo_format_t. **/ /** * CAIRO_HAS_IMAGE_SURFACE: * * Defined if the image surface backend is available. * The image surface backend is always built in. * This macro was added for completeness in cairo 1.8. * * Since: 1.8 **/ static cairo_bool_t _cairo_image_surface_is_size_valid (int width, int height) { return 0 <= width && width <= MAX_IMAGE_SIZE && 0 <= height && height <= MAX_IMAGE_SIZE; } cairo_format_t _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) { switch (pixman_format) { case PIXMAN_a8r8g8b8: return CAIRO_FORMAT_ARGB32; case PIXMAN_x2r10g10b10: return CAIRO_FORMAT_RGB30; case PIXMAN_x8r8g8b8: return CAIRO_FORMAT_RGB24; case PIXMAN_a8: return CAIRO_FORMAT_A8; case PIXMAN_a1: return CAIRO_FORMAT_A1; case PIXMAN_r5g6b5: return CAIRO_FORMAT_RGB16_565; #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8: #endif case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8: case PIXMAN_b8g8r8: case PIXMAN_b5g6r5: case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4: case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: case PIXMAN_g4: case PIXMAN_g1: case PIXMAN_yuy2: case PIXMAN_yv12: case PIXMAN_b8g8r8x8: case PIXMAN_b8g8r8a8: case PIXMAN_a2b10g10r10: case PIXMAN_x2b10g10r10: case PIXMAN_a2r10g10b10: #if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) case PIXMAN_x14r6g6b6: #endif default: return CAIRO_FORMAT_INVALID; } return CAIRO_FORMAT_INVALID; } cairo_content_t _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) { cairo_content_t content; content = 0; if (PIXMAN_FORMAT_RGB (pixman_format)) content |= CAIRO_CONTENT_COLOR; if (PIXMAN_FORMAT_A (pixman_format)) content |= CAIRO_CONTENT_ALPHA; return content; } void _cairo_image_surface_init (cairo_image_surface_t *surface, pixman_image_t *pixman_image, pixman_format_code_t pixman_format) { surface->parent = NULL; surface->pixman_image = pixman_image; surface->pixman_format = pixman_format; surface->format = _cairo_format_from_pixman_format (pixman_format); surface->data = (uint8_t *) pixman_image_get_data (pixman_image); surface->owns_data = FALSE; surface->transparency = CAIRO_IMAGE_UNKNOWN; surface->color = CAIRO_IMAGE_UNKNOWN_COLOR; surface->width = pixman_image_get_width (pixman_image); surface->height = pixman_image_get_height (pixman_image); surface->stride = pixman_image_get_stride (pixman_image); surface->depth = pixman_image_get_depth (pixman_image); surface->base.is_clear = surface->width == 0 || surface->height == 0; surface->compositor = _cairo_image_spans_compositor_get (); } cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, pixman_format_code_t pixman_format) { cairo_image_surface_t *surface; surface = malloc (sizeof (cairo_image_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_image_surface_backend, NULL, /* device */ _cairo_content_from_pixman_format (pixman_format)); _cairo_image_surface_init (surface, pixman_image, pixman_format); return &surface->base; } cairo_bool_t _pixman_format_from_masks (cairo_format_masks_t *masks, pixman_format_code_t *format_ret) { pixman_format_code_t format; int format_type; int a, r, g, b; cairo_format_masks_t format_masks; a = _cairo_popcount (masks->alpha_mask); r = _cairo_popcount (masks->red_mask); g = _cairo_popcount (masks->green_mask); b = _cairo_popcount (masks->blue_mask); if (masks->red_mask) { if (masks->red_mask > masks->blue_mask) format_type = PIXMAN_TYPE_ARGB; else format_type = PIXMAN_TYPE_ABGR; } else if (masks->alpha_mask) { format_type = PIXMAN_TYPE_A; } else { return FALSE; } format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b); if (! pixman_format_supported_destination (format)) return FALSE; /* Sanity check that we got out of PIXMAN_FORMAT exactly what we * expected. This avoid any problems from something bizarre like * alpha in the least-significant bits, or insane channel order, * or whatever. */ if (!_pixman_format_to_masks (format, &format_masks) || masks->bpp != format_masks.bpp || masks->red_mask != format_masks.red_mask || masks->green_mask != format_masks.green_mask || masks->blue_mask != format_masks.blue_mask) { return FALSE; } *format_ret = format; return TRUE; } /* A mask consisting of N bits set to 1. */ #define MASK(N) ((1UL << (N))-1) cairo_bool_t _pixman_format_to_masks (pixman_format_code_t format, cairo_format_masks_t *masks) { int a, r, g, b; masks->bpp = PIXMAN_FORMAT_BPP (format); /* Number of bits in each channel */ a = PIXMAN_FORMAT_A (format); r = PIXMAN_FORMAT_R (format); g = PIXMAN_FORMAT_G (format); b = PIXMAN_FORMAT_B (format); switch (PIXMAN_FORMAT_TYPE (format)) { case PIXMAN_TYPE_ARGB: masks->alpha_mask = MASK (a) << (r + g + b); masks->red_mask = MASK (r) << (g + b); masks->green_mask = MASK (g) << (b); masks->blue_mask = MASK (b); return TRUE; case PIXMAN_TYPE_ABGR: masks->alpha_mask = MASK (a) << (b + g + r); masks->blue_mask = MASK (b) << (g + r); masks->green_mask = MASK (g) << (r); masks->red_mask = MASK (r); return TRUE; #ifdef PIXMAN_TYPE_BGRA case PIXMAN_TYPE_BGRA: masks->blue_mask = MASK (b) << (masks->bpp - b); masks->green_mask = MASK (g) << (masks->bpp - b - g); masks->red_mask = MASK (r) << (masks->bpp - b - g - r); masks->alpha_mask = MASK (a); return TRUE; #endif case PIXMAN_TYPE_A: masks->alpha_mask = MASK (a); masks->red_mask = 0; masks->green_mask = 0; masks->blue_mask = 0; return TRUE; case PIXMAN_TYPE_OTHER: case PIXMAN_TYPE_COLOR: case PIXMAN_TYPE_GRAY: case PIXMAN_TYPE_YUY2: case PIXMAN_TYPE_YV12: default: masks->alpha_mask = 0; masks->red_mask = 0; masks->green_mask = 0; masks->blue_mask = 0; return FALSE; } } pixman_format_code_t _cairo_format_to_pixman_format_code (cairo_format_t format) { pixman_format_code_t ret; switch (format) { case CAIRO_FORMAT_A1: ret = PIXMAN_a1; break; case CAIRO_FORMAT_A8: ret = PIXMAN_a8; break; case CAIRO_FORMAT_RGB24: ret = PIXMAN_x8r8g8b8; break; case CAIRO_FORMAT_RGB30: ret = PIXMAN_x2r10g10b10; break; case CAIRO_FORMAT_RGB16_565: ret = PIXMAN_r5g6b5; break; case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_INVALID: default: ret = PIXMAN_a8r8g8b8; break; } return ret; } cairo_surface_t * _cairo_image_surface_create_with_pixman_format (unsigned char *data, pixman_format_code_t pixman_format, int width, int height, int stride) { cairo_surface_t *surface; pixman_image_t *pixman_image; if (! _cairo_image_surface_is_size_valid (width, height)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } pixman_image = pixman_image_create_bits (pixman_format, width, height, (uint32_t *) data, stride); if (unlikely (pixman_image == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface = _cairo_image_surface_create_for_pixman_image (pixman_image, pixman_format); if (unlikely (surface->status)) { pixman_image_unref (pixman_image); return surface; } /* we can not make any assumptions about the initial state of user data */ surface->is_clear = data == NULL; return surface; } /** * cairo_image_surface_create: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates an image surface of the specified format and * dimensions. Initially the surface contents are all * 0. (Specifically, within each pixel, each color or alpha channel * belonging to format will be 0. The contents of bits within a pixel, * but not belonging to the given format are undefined). * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, int height) { pixman_format_code_t pixman_format; if (! CAIRO_FORMAT_VALID (format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); pixman_format = _cairo_format_to_pixman_format_code (format); return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, width, height, -1); } slim_hidden_def (cairo_image_surface_create); cairo_surface_t * _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height) { return cairo_image_surface_create (_cairo_format_from_content (content), width, height); } /** * cairo_format_stride_for_width: * @format: A #cairo_format_t value * @width: The desired width of an image surface to be created. * * This function provides a stride value that will respect all * alignment requirements of the accelerated image-rendering code * within cairo. Typical usage will be of the form: * * <informalexample><programlisting> * int stride; * unsigned char *data; * #cairo_surface_t *surface; * * stride = cairo_format_stride_for_width (format, width); * data = malloc (stride * height); * surface = cairo_image_surface_create_for_data (data, format, * width, height, * stride); * </programlisting></informalexample> * * Return value: the appropriate stride to use given the desired * format and width, or -1 if either the format is invalid or the width * too large. * * Since: 1.6 **/ int cairo_format_stride_for_width (cairo_format_t format, int width) { int bpp; if (! CAIRO_FORMAT_VALID (format)) { _cairo_error_throw (CAIRO_STATUS_INVALID_FORMAT); return -1; } bpp = _cairo_format_bits_per_pixel (format); if ((unsigned) (width) >= (INT32_MAX - 7) / (unsigned) (bpp)) return -1; return CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); } slim_hidden_def (cairo_format_stride_for_width); /** * cairo_image_surface_create_for_data: * @data: a pointer to a buffer supplied by the application in which * to write contents. This pointer must be suitably aligned for any * kind of variable, (for example, a pointer returned by malloc). * @format: the format of pixels in the buffer * @width: the width of the image to be stored in the buffer * @height: the height of the image to be stored in the buffer * @stride: the number of bytes between the start of rows in the * buffer as allocated. This value should always be computed by * cairo_format_stride_for_width() before allocating the data * buffer. * * Creates an image surface for the provided pixel data. The output * buffer must be kept around until the #cairo_surface_t is destroyed * or cairo_surface_finish() is called on the surface. The initial * contents of @data will be used as the initial image contents; you * must explicitly clear the buffer, using, for example, * cairo_rectangle() and cairo_fill() if you want it cleared. * * Note that the stride may be larger than * width*bytes_per_pixel to provide proper alignment for each pixel * and row. This alignment is required to allow high-performance rendering * within cairo. The correct way to obtain a legal stride value is to * call cairo_format_stride_for_width() with the desired format and * maximum image width value, and then use the resulting stride value * to allocate the data and to create the image surface. See * cairo_format_stride_for_width() for example code. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface in the case of an error such as out of * memory or an invalid stride value. In case of invalid stride value * the error status of the returned surface will be * %CAIRO_STATUS_INVALID_STRIDE. You can use * cairo_surface_status() to check for this. * * See cairo_surface_set_user_data() for a means of attaching a * destroy-notification fallback to the surface if necessary. * * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride) { pixman_format_code_t pixman_format; int minstride; if (! CAIRO_FORMAT_VALID (format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); if (! _cairo_image_surface_is_size_valid (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); minstride = cairo_format_stride_for_width (format, width); if (stride < 0) { if (stride > -minstride) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); } } else { if (stride < minstride) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); } } pixman_format = _cairo_format_to_pixman_format_code (format); return _cairo_image_surface_create_with_pixman_format (data, pixman_format, width, height, stride); } slim_hidden_def (cairo_image_surface_create_for_data); /** * cairo_image_surface_get_data: * @surface: a #cairo_image_surface_t * * Get a pointer to the data of the image surface, for direct * inspection or modification. * * A call to cairo_surface_flush() is required before accessing the * pixel data to ensure that all pending drawing operations are * finished. A call to cairo_surface_mark_dirty() is required after * the data is modified. * * Return value: a pointer to the image data of this surface or %NULL * if @surface is not an image surface, or if cairo_surface_finish() * has been called. * * Since: 1.2 **/ unsigned char * cairo_image_surface_get_data (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return image_surface->data; } slim_hidden_def (cairo_image_surface_get_data); /** * cairo_image_surface_get_format: * @surface: a #cairo_image_surface_t * * Get the format of the surface. * * Return value: the format of the surface * * Since: 1.2 **/ cairo_format_t cairo_image_surface_get_format (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return CAIRO_FORMAT_INVALID; } return image_surface->format; } slim_hidden_def (cairo_image_surface_get_format); /** * cairo_image_surface_get_width: * @surface: a #cairo_image_surface_t * * Get the width of the image surface in pixels. * * Return value: the width of the surface in pixels. * * Since: 1.0 **/ int cairo_image_surface_get_width (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return image_surface->width; } slim_hidden_def (cairo_image_surface_get_width); /** * cairo_image_surface_get_height: * @surface: a #cairo_image_surface_t * * Get the height of the image surface in pixels. * * Return value: the height of the surface in pixels. * * Since: 1.0 **/ int cairo_image_surface_get_height (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return image_surface->height; } slim_hidden_def (cairo_image_surface_get_height); /** * cairo_image_surface_get_stride: * @surface: a #cairo_image_surface_t * * Get the stride of the image surface in bytes * * Return value: the stride of the image surface in bytes (or 0 if * @surface is not an image surface). The stride is the distance in * bytes from the beginning of one row of the image data to the * beginning of the next row. * * Since: 1.2 **/ int cairo_image_surface_get_stride (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; if (! _cairo_surface_is_image (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return image_surface->stride; } slim_hidden_def (cairo_image_surface_get_stride); cairo_format_t _cairo_format_from_content (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_COLOR: return CAIRO_FORMAT_RGB24; case CAIRO_CONTENT_ALPHA: return CAIRO_FORMAT_A8; case CAIRO_CONTENT_COLOR_ALPHA: return CAIRO_FORMAT_ARGB32; } ASSERT_NOT_REACHED; return CAIRO_FORMAT_INVALID; } cairo_content_t _cairo_content_from_format (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: return CAIRO_CONTENT_COLOR_ALPHA; case CAIRO_FORMAT_RGB30: return CAIRO_CONTENT_COLOR; case CAIRO_FORMAT_RGB24: return CAIRO_CONTENT_COLOR; case CAIRO_FORMAT_RGB16_565: return CAIRO_CONTENT_COLOR; case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: return CAIRO_CONTENT_ALPHA; case CAIRO_FORMAT_INVALID: break; } ASSERT_NOT_REACHED; return CAIRO_CONTENT_COLOR_ALPHA; } int _cairo_format_bits_per_pixel (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_RGB24: return 32; case CAIRO_FORMAT_RGB16_565: return 16; case CAIRO_FORMAT_A8: return 8; case CAIRO_FORMAT_A1: return 1; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; return 0; } } cairo_surface_t * _cairo_image_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_image_surface_t *other = abstract_other; TRACE ((stderr, "%s (other=%u)\n", __FUNCTION__, other->base.unique_id)); if (! _cairo_image_surface_is_size_valid (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (content == other->base.content) { return _cairo_image_surface_create_with_pixman_format (NULL, other->pixman_format, width, height, 0); } return _cairo_image_surface_create_with_content (content, width, height); } cairo_surface_t * _cairo_image_surface_snapshot (void *abstract_surface) { cairo_image_surface_t *image = abstract_surface; cairo_image_surface_t *clone; /* If we own the image, we can simply steal the memory for the snapshot */ if (image->owns_data && image->base._finishing) { clone = (cairo_image_surface_t *) _cairo_image_surface_create_for_pixman_image (image->pixman_image, image->pixman_format); if (unlikely (clone->base.status)) return &clone->base; image->pixman_image = NULL; image->owns_data = FALSE; clone->transparency = image->transparency; clone->color = image->color; clone->owns_data = TRUE; return &clone->base; } clone = (cairo_image_surface_t *) _cairo_image_surface_create_with_pixman_format (NULL, image->pixman_format, image->width, image->height, 0); if (unlikely (clone->base.status)) return &clone->base; if (clone->stride == image->stride) { memcpy (clone->data, image->data, clone->stride * clone->height); } else { pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, clone->pixman_image, 0, 0, 0, 0, 0, 0, image->width, image->height); } clone->base.is_clear = FALSE; return &clone->base; } cairo_image_surface_t * _cairo_image_surface_map_to_image (void *abstract_other, const cairo_rectangle_int_t *extents) { cairo_image_surface_t *other = abstract_other; cairo_surface_t *surface; uint8_t *data; data = other->data; data += extents->y * other->stride; data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8; surface = _cairo_image_surface_create_with_pixman_format (data, other->pixman_format, extents->width, extents->height, other->stride); cairo_surface_set_device_offset (surface, -extents->x, -extents->y); return (cairo_image_surface_t *) surface; } cairo_int_status_t _cairo_image_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return CAIRO_INT_STATUS_SUCCESS; } cairo_status_t _cairo_image_surface_finish (void *abstract_surface) { cairo_image_surface_t *surface = abstract_surface; if (surface->pixman_image) { pixman_image_unref (surface->pixman_image); surface->pixman_image = NULL; } if (surface->owns_data) { free (surface->data); surface->data = NULL; } if (surface->parent) { cairo_surface_t *parent = surface->parent; surface->parent = NULL; cairo_surface_destroy (parent); } return CAIRO_STATUS_SUCCESS; } void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) { surface->owns_data = TRUE; } cairo_surface_t * _cairo_image_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_image_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } cairo_status_t _cairo_image_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { *image_out = abstract_surface; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } void _cairo_image_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { } /* high level image interface */ cairo_bool_t _cairo_image_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_image_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } cairo_int_status_t _cairo_image_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_image_surface_t *surface = abstract_surface; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->base.unique_id)); return _cairo_compositor_paint (surface->compositor, &surface->base, op, source, clip); } cairo_int_status_t _cairo_image_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_image_surface_t *surface = abstract_surface; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->base.unique_id)); return _cairo_compositor_mask (surface->compositor, &surface->base, op, source, mask, clip); } cairo_int_status_t _cairo_image_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_image_surface_t *surface = abstract_surface; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->base.unique_id)); return _cairo_compositor_stroke (surface->compositor, &surface->base, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } cairo_int_status_t _cairo_image_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_image_surface_t *surface = abstract_surface; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->base.unique_id)); return _cairo_compositor_fill (surface->compositor, &surface->base, op, source, path, fill_rule, tolerance, antialias, clip); } cairo_int_status_t _cairo_image_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_image_surface_t *surface = abstract_surface; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->base.unique_id)); return _cairo_compositor_glyphs (surface->compositor, &surface->base, op, source, glyphs, num_glyphs, scaled_font, clip); } void _cairo_image_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } const cairo_surface_backend_t _cairo_image_surface_backend = { CAIRO_SURFACE_TYPE_IMAGE, _cairo_image_surface_finish, _cairo_default_context_create, _cairo_image_surface_create_similar, NULL, /* create similar image */ _cairo_image_surface_map_to_image, _cairo_image_surface_unmap_image, _cairo_image_surface_source, _cairo_image_surface_acquire_source_image, _cairo_image_surface_release_source_image, _cairo_image_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_image_surface_get_extents, _cairo_image_surface_get_font_options, NULL, /* flush */ NULL, _cairo_image_surface_paint, _cairo_image_surface_mask, _cairo_image_surface_stroke, _cairo_image_surface_fill, NULL, /* fill-stroke */ _cairo_image_surface_glyphs, }; /* A convenience function for when one needs to coerce an image * surface to an alternate format. */ cairo_image_surface_t * _cairo_image_surface_coerce (cairo_image_surface_t *surface) { return _cairo_image_surface_coerce_to_format (surface, _cairo_format_from_content (surface->base.content)); } /* A convenience function for when one needs to coerce an image * surface to an alternate format. */ cairo_image_surface_t * _cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, cairo_format_t format) { cairo_image_surface_t *clone; cairo_status_t status; status = surface->base.status; if (unlikely (status)) return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); if (surface->format == format) return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); clone = (cairo_image_surface_t *) cairo_image_surface_create (format, surface->width, surface->height); if (unlikely (clone->base.status)) return clone; pixman_image_composite32 (PIXMAN_OP_SRC, surface->pixman_image, NULL, clone->pixman_image, 0, 0, 0, 0, 0, 0, surface->width, surface->height); clone->base.is_clear = FALSE; clone->base.device_transform = surface->base.device_transform; clone->base.device_transform_inverse = surface->base.device_transform_inverse; return clone; } cairo_image_surface_t * _cairo_image_surface_create_from_image (cairo_image_surface_t *other, pixman_format_code_t format, int x, int y, int width, int height, int stride) { cairo_image_surface_t *surface; cairo_status_t status; pixman_image_t *image; void *mem = NULL; status = other->base.status; if (unlikely (status)) goto cleanup; if (stride) { mem = _cairo_malloc_ab (height, stride); if (unlikely (mem == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } } image = pixman_image_create_bits (format, width, height, mem, stride); if (unlikely (image == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup_mem; } surface = (cairo_image_surface_t *) _cairo_image_surface_create_for_pixman_image (image, format); if (unlikely (surface->base.status)) { status = surface->base.status; goto cleanup_image; } pixman_image_composite32 (PIXMAN_OP_SRC, other->pixman_image, NULL, image, x, y, 0, 0, 0, 0, width, height); surface->base.is_clear = FALSE; surface->owns_data = mem != NULL; return surface; cleanup_image: pixman_image_unref (image); cleanup_mem: free (mem); cleanup: return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); } cairo_image_transparency_t _cairo_image_analyze_transparency (cairo_image_surface_t *image) { int x, y; if (image->transparency != CAIRO_IMAGE_UNKNOWN) return image->transparency; if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) return image->transparency = CAIRO_IMAGE_IS_OPAQUE; if (image->base.is_clear) return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { if (image->format == CAIRO_FORMAT_A1) { return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else if (image->format == CAIRO_FORMAT_A8) { for (y = 0; y < image->height; y++) { uint8_t *alpha = (uint8_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, alpha++) { if (*alpha > 0 && *alpha < 255) return image->transparency = CAIRO_IMAGE_HAS_ALPHA; } } return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else { return image->transparency = CAIRO_IMAGE_HAS_ALPHA; } } if (image->format == CAIRO_FORMAT_RGB16_565) { image->transparency = CAIRO_IMAGE_IS_OPAQUE; return CAIRO_IMAGE_IS_OPAQUE; } if (image->format != CAIRO_FORMAT_ARGB32) return image->transparency = CAIRO_IMAGE_HAS_ALPHA; image->transparency = CAIRO_IMAGE_IS_OPAQUE; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, pixel++) { int a = (*pixel & 0xff000000) >> 24; if (a > 0 && a < 255) { return image->transparency = CAIRO_IMAGE_HAS_ALPHA; } else if (a == 0) { image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } } } return image->transparency; } cairo_image_color_t _cairo_image_analyze_color (cairo_image_surface_t *image) { int x, y; if (image->color != CAIRO_IMAGE_UNKNOWN_COLOR) return image->color; if (image->format == CAIRO_FORMAT_A1) return image->color = CAIRO_IMAGE_IS_MONOCHROME; if (image->format == CAIRO_FORMAT_A8) return image->color = CAIRO_IMAGE_IS_GRAYSCALE; if (image->format == CAIRO_FORMAT_ARGB32) { image->color = CAIRO_IMAGE_IS_MONOCHROME; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, pixel++) { int a = (*pixel & 0xff000000) >> 24; int r = (*pixel & 0x00ff0000) >> 16; int g = (*pixel & 0x0000ff00) >> 8; int b = (*pixel & 0x000000ff); if (a == 0) { r = g = b = 0; } else { r = (r * 255 + a / 2) / a; g = (g * 255 + a / 2) / a; b = (b * 255 + a / 2) / a; } if (!(r == g && g == b)) return image->color = CAIRO_IMAGE_IS_COLOR; else if (r > 0 && r < 255) image->color = CAIRO_IMAGE_IS_GRAYSCALE; } } return image->color; } if (image->format == CAIRO_FORMAT_RGB24) { image->color = CAIRO_IMAGE_IS_MONOCHROME; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, pixel++) { int r = (*pixel & 0x00ff0000) >> 16; int g = (*pixel & 0x0000ff00) >> 8; int b = (*pixel & 0x000000ff); if (!(r == g && g == b)) return image->color = CAIRO_IMAGE_IS_COLOR; else if (r > 0 && r < 255) image->color = CAIRO_IMAGE_IS_GRAYSCALE; } } return image->color; } return image->color = CAIRO_IMAGE_IS_COLOR; } cairo_image_surface_t * _cairo_image_surface_clone_subimage (cairo_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_surface_t *image; cairo_surface_pattern_t pattern; cairo_status_t status; image = cairo_surface_create_similar_image (surface, _cairo_format_from_content (surface->content), extents->width, extents->height); if (image->status) return to_image_surface (image); /* TODO: check me with non-identity device_transform. Should we * clone the scaling, too? */ cairo_surface_set_device_offset (image, -extents->x, -extents->y); _cairo_pattern_init_for_surface (&pattern, surface); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (image, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto error; /* We use the parent as a flag during map-to-image/umap-image that the * resultant image came from a fallback rather than as direct call * to the backend's map_to_image(). Whilst we use it as a simple flag, * we need to make sure the parent surface obeys the reference counting * semantics and is consistent for all callers. */ _cairo_image_surface_set_parent (to_image_surface (image), cairo_surface_reference (surface)); return to_image_surface (image); error: cairo_surface_destroy (image); return to_image_surface (_cairo_surface_create_in_error (status)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-list-inline.h�����������������������������0000664�0000000�0000000�00000014164�12710376503�0026001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> * */ #ifndef CAIRO_LIST_INLINE_H #define CAIRO_LIST_INLINE_H #include "cairo-list-private.h" #define cairo_list_entry(ptr, type, member) \ cairo_container_of(ptr, type, member) #define cairo_list_first_entry(ptr, type, member) \ cairo_list_entry((ptr)->next, type, member) #define cairo_list_last_entry(ptr, type, member) \ cairo_list_entry((ptr)->prev, type, member) #define cairo_list_foreach(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define cairo_list_foreach_entry(pos, type, head, member) \ for (pos = cairo_list_entry((head)->next, type, member);\ &pos->member != (head); \ pos = cairo_list_entry(pos->member.next, type, member)) #define cairo_list_foreach_entry_safe(pos, n, type, head, member) \ for (pos = cairo_list_entry ((head)->next, type, member),\ n = cairo_list_entry (pos->member.next, type, member);\ &pos->member != (head); \ pos = n, n = cairo_list_entry (n->member.next, type, member)) #define cairo_list_foreach_entry_reverse(pos, type, head, member) \ for (pos = cairo_list_entry((head)->prev, type, member);\ &pos->member != (head); \ pos = cairo_list_entry(pos->member.prev, type, member)) #define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \ for (pos = cairo_list_entry((head)->prev, type, member),\ n = cairo_list_entry (pos->member.prev, type, member);\ &pos->member != (head); \ pos = n, n = cairo_list_entry (n->member.prev, type, member)) #ifdef CAIRO_LIST_DEBUG static inline void _cairo_list_validate (const cairo_list_t *link) { assert (link->next->prev == link); assert (link->prev->next == link); } static inline void cairo_list_validate (const cairo_list_t *head) { cairo_list_t *link; cairo_list_foreach (link, head) _cairo_list_validate (link); } static inline cairo_bool_t cairo_list_is_empty (const cairo_list_t *head); static inline void cairo_list_validate_is_empty (const cairo_list_t *head) { assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev)); } #else #define _cairo_list_validate(link) #define cairo_list_validate(head) #define cairo_list_validate_is_empty(head) #endif static inline void cairo_list_init (cairo_list_t *entry) { entry->next = entry; entry->prev = entry; } static inline void __cairo_list_add (cairo_list_t *entry, cairo_list_t *prev, cairo_list_t *next) { next->prev = entry; entry->next = next; entry->prev = prev; prev->next = entry; } static inline void cairo_list_add (cairo_list_t *entry, cairo_list_t *head) { cairo_list_validate (head); cairo_list_validate_is_empty (entry); __cairo_list_add (entry, head, head->next); cairo_list_validate (head); } static inline void cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head) { cairo_list_validate (head); cairo_list_validate_is_empty (entry); __cairo_list_add (entry, head->prev, head); cairo_list_validate (head); } static inline void __cairo_list_del (cairo_list_t *prev, cairo_list_t *next) { next->prev = prev; prev->next = next; } static inline void _cairo_list_del (cairo_list_t *entry) { __cairo_list_del (entry->prev, entry->next); } static inline void cairo_list_del (cairo_list_t *entry) { _cairo_list_del (entry); cairo_list_init (entry); } static inline void cairo_list_move (cairo_list_t *entry, cairo_list_t *head) { cairo_list_validate (head); __cairo_list_del (entry->prev, entry->next); __cairo_list_add (entry, head, head->next); cairo_list_validate (head); } static inline void cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head) { cairo_list_validate (head); __cairo_list_del (entry->prev, entry->next); __cairo_list_add (entry, head->prev, head); cairo_list_validate (head); } static inline void cairo_list_swap (cairo_list_t *entry, cairo_list_t *other) { __cairo_list_add (entry, other->prev, other->next); cairo_list_init (other); } static inline cairo_bool_t cairo_list_is_first (const cairo_list_t *entry, const cairo_list_t *head) { cairo_list_validate (head); return entry->prev == head; } static inline cairo_bool_t cairo_list_is_last (const cairo_list_t *entry, const cairo_list_t *head) { cairo_list_validate (head); return entry->next == head; } static inline cairo_bool_t cairo_list_is_empty (const cairo_list_t *head) { cairo_list_validate (head); return head->next == head; } static inline cairo_bool_t cairo_list_is_singular (const cairo_list_t *head) { cairo_list_validate (head); return head->next == head || head->next == head->prev; } #endif /* CAIRO_LIST_INLINE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-list-private.h����������������������������0000664�0000000�0000000�00000003361�12710376503�0026172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> * */ #ifndef CAIRO_LIST_PRIVATE_H #define CAIRO_LIST_PRIVATE_H #include "cairo-compiler-private.h" /* Basic circular, doubly linked list implementation */ typedef struct _cairo_list { struct _cairo_list *next, *prev; } cairo_list_t; #endif /* CAIRO_LIST_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-lzw.c�������������������������������������0000664�0000000�0000000�00000027314�12710376503�0024362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" typedef struct _lzw_buf { cairo_status_t status; unsigned char *data; int data_size; int num_data; uint32_t pending; unsigned int pending_bits; } lzw_buf_t; /* An lzw_buf_t is a simple, growable chunk of memory for holding * variable-size objects of up to 16 bits each. * * Initialize an lzw_buf_t to the given size in bytes. * * To store objects into the lzw_buf_t, call _lzw_buf_store_bits and * when finished, call _lzw_buf_store_pending, (which flushes out the * last few bits that hadn't yet made a complete byte yet). * * Instead of returning failure from any functions, lzw_buf_t provides * a status value that the caller can query, (and should query at * least once when done with the object). The status value will be * either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY; */ static void _lzw_buf_init (lzw_buf_t *buf, int size) { if (size == 0) size = 16; buf->status = CAIRO_STATUS_SUCCESS; buf->data_size = size; buf->num_data = 0; buf->pending = 0; buf->pending_bits = 0; buf->data = malloc (size); if (unlikely (buf->data == NULL)) { buf->data_size = 0; buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return; } } /* Increase the buffer size by doubling. * * Returns %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY */ static cairo_status_t _lzw_buf_grow (lzw_buf_t *buf) { int new_size = buf->data_size * 2; unsigned char *new_data; if (buf->status) return buf->status; new_data = NULL; /* check for integer overflow */ if (new_size / 2 == buf->data_size) new_data = realloc (buf->data, new_size); if (unlikely (new_data == NULL)) { free (buf->data); buf->data_size = 0; buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return buf->status; } buf->data = new_data; buf->data_size = new_size; return CAIRO_STATUS_SUCCESS; } /* Store the lowest num_bits bits of values into buf. * * Note: The bits of value above size_in_bits must be 0, (so don't lie * about the size). * * See also _lzw_buf_store_pending which must be called after the last * call to _lzw_buf_store_bits. * * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. */ static void _lzw_buf_store_bits (lzw_buf_t *buf, uint16_t value, int num_bits) { cairo_status_t status; assert (value <= (1 << num_bits) - 1); if (buf->status) return; buf->pending = (buf->pending << num_bits) | value; buf->pending_bits += num_bits; while (buf->pending_bits >= 8) { if (buf->num_data >= buf->data_size) { status = _lzw_buf_grow (buf); if (unlikely (status)) return; } buf->data[buf->num_data++] = buf->pending >> (buf->pending_bits - 8); buf->pending_bits -= 8; } } /* Store the last remaining pending bits into the buffer. * * Note: This function must be called after the last call to * _lzw_buf_store_bits. * * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. */ static void _lzw_buf_store_pending (lzw_buf_t *buf) { cairo_status_t status; if (buf->status) return; if (buf->pending_bits == 0) return; assert (buf->pending_bits < 8); if (buf->num_data >= buf->data_size) { status = _lzw_buf_grow (buf); if (unlikely (status)) return; } buf->data[buf->num_data++] = buf->pending << (8 - buf->pending_bits); buf->pending_bits = 0; } /* LZW defines a few magic code values */ #define LZW_CODE_CLEAR_TABLE 256 #define LZW_CODE_EOD 257 #define LZW_CODE_FIRST 258 /* We pack three separate values into a symbol as follows: * * 12 bits (31 down to 20): CODE: code value used to represent this symbol * 12 bits (19 down to 8): PREV: previous code value in chain * 8 bits ( 7 down to 0): NEXT: next byte value in chain */ typedef uint32_t lzw_symbol_t; #define LZW_SYMBOL_SET(sym, prev, next) ((sym) = ((prev) << 8)|(next)) #define LZW_SYMBOL_SET_CODE(sym, code, prev, next) ((sym) = ((code << 20)|(prev) << 8)|(next)) #define LZW_SYMBOL_GET_CODE(sym) (((sym) >> 20)) #define LZW_SYMBOL_GET_PREV(sym) (((sym) >> 8) & 0x7ff) #define LZW_SYMBOL_GET_BYTE(sym) (((sym) >> 0) & 0x0ff) /* The PREV+NEXT fields can be seen as the key used to fetch values * from the hash table, while the code is the value fetched. */ #define LZW_SYMBOL_KEY_MASK 0x000fffff /* Since code values are only stored starting with 258 we can safely * use a zero value to represent free slots in the hash table. */ #define LZW_SYMBOL_FREE 0x00000000 /* These really aren't very free for modifying. First, the PostScript * specification sets the 9-12 bit range. Second, the encoding of * lzw_symbol_t above also relies on 2 of LZW_BITS_MAX plus one byte * fitting within 32 bits. * * But other than that, the LZW compression scheme could function with * more bits per code. */ #define LZW_BITS_MIN 9 #define LZW_BITS_MAX 12 #define LZW_BITS_BOUNDARY(bits) ((1<<(bits))-1) #define LZW_MAX_SYMBOLS (1<<LZW_BITS_MAX) #define LZW_SYMBOL_TABLE_SIZE 9013 #define LZW_SYMBOL_MOD1 LZW_SYMBOL_TABLE_SIZE #define LZW_SYMBOL_MOD2 9011 typedef struct _lzw_symbol_table { lzw_symbol_t table[LZW_SYMBOL_TABLE_SIZE]; } lzw_symbol_table_t; /* Initialize the hash table to entirely empty */ static void _lzw_symbol_table_init (lzw_symbol_table_t *table) { memset (table->table, 0, LZW_SYMBOL_TABLE_SIZE * sizeof (lzw_symbol_t)); } /* Lookup a symbol in the symbol table. The PREV and NEXT fields of * symbol form the key for the lookup. * * If successful, then this function returns %TRUE and slot_ret will be * left pointing at the result that will have the CODE field of * interest. * * If the lookup fails, then this function returns %FALSE and slot_ret * will be pointing at the location in the table to which a new CODE * value should be stored along with PREV and NEXT. */ static cairo_bool_t _lzw_symbol_table_lookup (lzw_symbol_table_t *table, lzw_symbol_t symbol, lzw_symbol_t **slot_ret) { /* The algorithm here is identical to that in cairo-hash.c. We * copy it here to allow for a rather more efficient * implementation due to several circumstances that do not apply * to the more general case: * * 1) We have a known bound on the total number of symbols, so we * have a fixed-size table without any copying when growing * * 2) We never delete any entries, so we don't need to * support/check for DEAD entries during lookup. * * 3) The object fits in 32 bits so we store each object in its * entirety within the table rather than storing objects * externally and putting pointers in the table, (which here * would just double the storage requirements and have negative * impacts on memory locality). */ int i, idx, step, hash = symbol & LZW_SYMBOL_KEY_MASK; lzw_symbol_t candidate; idx = hash % LZW_SYMBOL_MOD1; step = 0; *slot_ret = NULL; for (i = 0; i < LZW_SYMBOL_TABLE_SIZE; i++) { candidate = table->table[idx]; if (candidate == LZW_SYMBOL_FREE) { *slot_ret = &table->table[idx]; return FALSE; } else /* candidate is LIVE */ { if ((candidate & LZW_SYMBOL_KEY_MASK) == (symbol & LZW_SYMBOL_KEY_MASK)) { *slot_ret = &table->table[idx]; return TRUE; } } if (step == 0) { step = hash % LZW_SYMBOL_MOD2; if (step == 0) step = 1; } idx += step; if (idx >= LZW_SYMBOL_TABLE_SIZE) idx -= LZW_SYMBOL_TABLE_SIZE; } return FALSE; } /* Compress a bytestream using the LZW algorithm. * * This is an original implementation based on reading the * specification of the LZWDecode filter in the PostScript Language * Reference. The free parameters in the LZW algorithm are set to the * values mandated by PostScript, (symbols encoded with widths from 9 * to 12 bits). * * This function returns a pointer to a newly allocated buffer holding * the compressed data, or %NULL if an out-of-memory situation * occurs. * * Notice that any one of the _lzw_buf functions called here could * trigger an out-of-memory condition. But lzw_buf_t uses cairo's * shutdown-on-error idiom, so it's safe to continue to call into * lzw_buf without having to check for errors, (until a final check at * the end). */ unsigned char * _cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out) { int bytes_remaining = *size_in_out; lzw_buf_t buf; lzw_symbol_table_t table; lzw_symbol_t symbol, *slot = NULL; /* just to squelch a warning */ int code_next = LZW_CODE_FIRST; int code_bits = LZW_BITS_MIN; int prev, next = 0; /* just to squelch a warning */ if (*size_in_out == 0) return NULL; _lzw_buf_init (&buf, *size_in_out); _lzw_symbol_table_init (&table); /* The LZW header is a clear table code. */ _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits); while (1) { /* Find the longest existing code in the symbol table that * matches the current input, if any. */ prev = *data++; bytes_remaining--; if (bytes_remaining) { do { next = *data++; bytes_remaining--; LZW_SYMBOL_SET (symbol, prev, next); if (_lzw_symbol_table_lookup (&table, symbol, &slot)) prev = LZW_SYMBOL_GET_CODE (*slot); } while (bytes_remaining && *slot != LZW_SYMBOL_FREE); if (*slot == LZW_SYMBOL_FREE) { data--; bytes_remaining++; } } /* Write the code into the output. This is either a byte read * directly from the input, or a code from the last successful * lookup. */ _lzw_buf_store_bits (&buf, prev, code_bits); if (bytes_remaining == 0) break; LZW_SYMBOL_SET_CODE (*slot, code_next++, prev, next); if (code_next > LZW_BITS_BOUNDARY(code_bits)) { code_bits++; if (code_bits > LZW_BITS_MAX) { _lzw_symbol_table_init (&table); _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits - 1); code_bits = LZW_BITS_MIN; code_next = LZW_CODE_FIRST; } } } /* The LZW footer is an end-of-data code. */ _lzw_buf_store_bits (&buf, LZW_CODE_EOD, code_bits); _lzw_buf_store_pending (&buf); /* See if we ever ran out of memory while writing to buf. */ if (buf.status == CAIRO_STATUS_NO_MEMORY) { *size_in_out = 0; return NULL; } assert (buf.status == CAIRO_STATUS_SUCCESS); *size_in_out = buf.num_data; return buf.data; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-malloc-private.h��������������������������0000664�0000000�0000000�00000012341�12710376503�0026464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation * * Contributor(s): * Vladimir Vukicevic <vladimir@pobox.com> */ #ifndef CAIRO_MALLOC_PRIVATE_H #define CAIRO_MALLOC_PRIVATE_H #include "cairo-wideint-private.h" #include <stdlib.h> #if HAVE_MEMFAULT #include <memfault.h> #define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT() #else #define CAIRO_INJECT_FAULT() 0 #endif /** * _cairo_malloc: * @size: size in bytes * * Allocate @size memory using malloc(). * The memory should be freed using free(). * malloc is skipped, if 0 bytes are requested, and %NULL will be returned. * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or size is 0. **/ #define _cairo_malloc(size) \ ((size) ? malloc((unsigned) (size)) : NULL) /** * _cairo_malloc_ab: * @a: number of elements to allocate * @size: size of each element * * Allocates @a*@size memory using _cairo_malloc(), taking care to not * overflow when doing the multiplication. Behaves much like * calloc(), except that the returned memory is not set to zero. * The memory should be freed using free(). * * @size should be a constant so that the compiler can optimize * out a constant division. * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. **/ #define _cairo_malloc_ab(a, size) \ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ _cairo_malloc((unsigned) (a) * (unsigned) (size))) /** * _cairo_realloc_ab: * @ptr: original pointer to block of memory to be resized * @a: number of elements to allocate * @size: size of each element * * Reallocates @ptr a block of @a*@size memory using realloc(), taking * care to not overflow when doing the multiplication. The memory * should be freed using free(). * * @size should be a constant so that the compiler can optimize * out a constant division. * * Return value: A pointer to the newly allocated memory, or %NULL in * case of realloc() failure or overflow (whereupon the original block * of memory * is left untouched). **/ #define _cairo_realloc_ab(ptr, a, size) \ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ realloc(ptr, (unsigned) (a) * (unsigned) (size))) /** * _cairo_malloc_abc: * @a: first factor of number of elements to allocate * @b: second factor of number of elements to allocate * @size: size of each element * * Allocates @a*@b*@size memory using _cairo_malloc(), taking care to not * overflow when doing the multiplication. Behaves like * _cairo_malloc_ab(). The memory should be freed using free(). * * @size should be a constant so that the compiler can optimize * out a constant division. * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. **/ #define _cairo_malloc_abc(a, b, size) \ ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \ (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \ _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size))) /** * _cairo_malloc_ab_plus_c: * @a: number of elements to allocate * @size: size of each element * @c: additional size to allocate * * Allocates @a*@size+@c memory using _cairo_malloc(), taking care to not * overflow when doing the arithmetic. Behaves similar to * _cairo_malloc_ab(). The memory should be freed using free(). * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. **/ #define _cairo_malloc_ab_plus_c(a, size, c) \ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ (unsigned) (c) >= INT32_MAX - (unsigned) (a) * (unsigned) (size) ? NULL : \ _cairo_malloc((unsigned) (a) * (unsigned) (size) + (unsigned) (c))) #endif /* CAIRO_MALLOC_PRIVATE_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mask-compositor.c�������������������������0000664�0000000�0000000�00000124763�12710376503�0026703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ /* This compositor renders the shape to a mask using an image surface * then calls composite. */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-compositor-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-inline.h" #include "cairo-region-private.h" #include "cairo-surface-observer-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" typedef cairo_int_status_t (*draw_func_t) (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, const cairo_pattern_t *src, const cairo_rectangle_int_t *src_sample, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip); static void do_unaligned_row(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int y, int h, uint16_t coverage) { int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; if (x2 > x1) { if (! _cairo_fixed_is_integer (b->p1.x)) { blt(closure, x1, y, 1, h, coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); x1++; } if (x2 > x1) blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); if (! _cairo_fixed_is_integer (b->p2.x)) blt(closure, x2, y, 1, h, coverage * _cairo_fixed_fractional_part (b->p2.x)); } else blt(closure, x1, y, 1, h, coverage * (b->p2.x - b->p1.x)); } static void do_unaligned_box(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int ty) { int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; if (y2 > y1) { if (! _cairo_fixed_is_integer (b->p1.y)) { do_unaligned_row(blt, closure, b, tx, y1, 1, 256 - _cairo_fixed_fractional_part (b->p1.y)); y1++; } if (y2 > y1) do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); if (! _cairo_fixed_is_integer (b->p2.y)) do_unaligned_row(blt, closure, b, tx, y2, 1, _cairo_fixed_fractional_part (b->p2.y)); } else do_unaligned_row(blt, closure, b, tx, y1, 1, b->p2.y - b->p1.y); } struct blt_in { const cairo_mask_compositor_t *compositor; cairo_surface_t *dst; }; static void blt_in(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct blt_in *info = closure; cairo_color_t color; cairo_rectangle_int_t rect; if (coverage == 0xffff) return; rect.x = x; rect.y = y; rect.width = w; rect.height = h; _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff); info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN, &color, &rect, 1); } static cairo_surface_t * create_composite_mask (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, void *draw_closure, draw_func_t draw_func, draw_func_t mask_func, const cairo_composite_rectangles_t *extents) { cairo_surface_t *surface; cairo_int_status_t status; struct blt_in info; int i; surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA, extents->bounded.width, extents->bounded.height); if (unlikely (surface->status)) return surface; status = compositor->acquire (surface); if (unlikely (status)) { cairo_surface_destroy (surface); return _cairo_int_surface_create_in_error (status); } if (!surface->is_clear) { cairo_rectangle_int_t rect; rect.x = rect.y = 0; rect.width = extents->bounded.width; rect.height = extents->bounded.height; status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &rect, 1); if (unlikely (status)) goto error; } if (mask_func) { status = mask_func (compositor, surface, draw_closure, CAIRO_OPERATOR_SOURCE, NULL, NULL, extents->bounded.x, extents->bounded.y, &extents->bounded, extents->clip); if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) goto out; } /* Is it worth setting the clip region here? */ status = draw_func (compositor, surface, draw_closure, CAIRO_OPERATOR_ADD, NULL, NULL, extents->bounded.x, extents->bounded.y, &extents->bounded, NULL); if (unlikely (status)) goto error; info.compositor = compositor; info.dst = surface; for (i = 0; i < extents->clip->num_boxes; i++) { cairo_box_t *b = &extents->clip->boxes[i]; if (! _cairo_fixed_is_integer (b->p1.x) || ! _cairo_fixed_is_integer (b->p1.y) || ! _cairo_fixed_is_integer (b->p2.x) || ! _cairo_fixed_is_integer (b->p2.y)) { do_unaligned_box(blt_in, &info, b, extents->bounded.x, extents->bounded.y); } } if (extents->clip->path != NULL) { status = _cairo_clip_combine_with_surface (extents->clip, surface, extents->bounded.x, extents->bounded.y); if (unlikely (status)) goto error; } out: compositor->release (surface); surface->is_clear = FALSE; return surface; error: compositor->release (surface); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { cairo_surface_destroy (surface); surface = _cairo_int_surface_create_in_error (status); } return surface; } /* Handles compositing with a clip surface when the operator allows * us to combine the clip with the mask */ static cairo_status_t clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor, void *draw_closure, draw_func_t draw_func, draw_func_t mask_func, cairo_operator_t op, cairo_pattern_t *pattern, const cairo_composite_rectangles_t*extents) { cairo_surface_t *dst = extents->surface; cairo_surface_t *mask, *src; int src_x, src_y; mask = create_composite_mask (compositor, dst, draw_closure, draw_func, mask_func, extents); if (unlikely (mask->status)) return mask->status; if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) { src = compositor->pattern_to_surface (dst, &extents->source_pattern.base, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); if (unlikely (src->status)) { cairo_surface_destroy (mask); return src->status; } compositor->composite (dst, op, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); cairo_surface_destroy (src); } else { compositor->composite (dst, op, mask, NULL, 0, 0, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * get_clip_source (const cairo_mask_compositor_t *compositor, cairo_clip_t *clip, cairo_surface_t *dst, const cairo_rectangle_int_t *bounds, int *out_x, int *out_y) { cairo_surface_pattern_t pattern; cairo_rectangle_int_t r; cairo_surface_t *surface; surface = _cairo_clip_get_image (clip, dst, bounds); if (unlikely (surface->status)) return surface; _cairo_pattern_init_for_surface (&pattern, surface); pattern.base.filter = CAIRO_FILTER_NEAREST; cairo_surface_destroy (surface); r.x = r.y = 0; r.width = bounds->width; r.height = bounds->height; surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE, &r, &r, out_x, out_y); _cairo_pattern_fini (&pattern.base); *out_x += -bounds->x; *out_y += -bounds->y; return surface; } /* Handles compositing with a clip surface when we have to do the operation * in two pieces and combine them together. */ static cairo_status_t clip_and_composite_combine (const cairo_mask_compositor_t *compositor, void *draw_closure, draw_func_t draw_func, cairo_operator_t op, const cairo_pattern_t *pattern, const cairo_composite_rectangles_t*extents) { cairo_surface_t *dst = extents->surface; cairo_surface_t *tmp, *clip; cairo_status_t status; int clip_x, clip_y; tmp = _cairo_surface_create_similar_scratch (dst, dst->content, extents->bounded.width, extents->bounded.height); if (unlikely (tmp->status)) return tmp->status; compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL, extents->bounded.x, extents->bounded.y, 0, 0, 0, 0, extents->bounded.width, extents->bounded.height); status = draw_func (compositor, tmp, draw_closure, op, pattern, &extents->source_sample_area, extents->bounded.x, extents->bounded.y, &extents->bounded, NULL); if (unlikely (status)) goto cleanup; clip = get_clip_source (compositor, extents->clip, dst, &extents->bounded, &clip_x, &clip_y); if (unlikely ((status = clip->status))) goto cleanup; if (dst->is_clear) { compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip, 0, 0, clip_x, clip_y, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } else { /* Punch the clip out of the destination */ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL, clip_x, clip_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); /* Now add the two results together */ compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip, 0, 0, clip_x, clip_y, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } cairo_surface_destroy (clip); cleanup: cairo_surface_destroy (tmp); return status; } /* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) */ static cairo_status_t clip_and_composite_source (const cairo_mask_compositor_t *compositor, void *draw_closure, draw_func_t draw_func, draw_func_t mask_func, cairo_pattern_t *pattern, const cairo_composite_rectangles_t *extents) { cairo_surface_t *dst = extents->surface; cairo_surface_t *mask, *src; int src_x, src_y; /* Create a surface that is mask IN clip */ mask = create_composite_mask (compositor, dst, draw_closure, draw_func, mask_func, extents); if (unlikely (mask->status)) return mask->status; src = compositor->pattern_to_surface (dst, pattern, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); if (unlikely (src->status)) { cairo_surface_destroy (mask); return src->status; } if (dst->is_clear) { compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } else { /* Compute dest' = dest OUT (mask IN clip) */ compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, 0, 0, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); /* Now compute (src IN (mask IN clip)) ADD dest' */ compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } cairo_surface_destroy (src); cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t can_reduce_alpha_op (cairo_operator_t op) { int iop = op; switch (iop) { case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_ADD: return TRUE; default: return FALSE; } } static cairo_bool_t reduce_alpha_op (cairo_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern) { return dst->is_clear && dst->content == CAIRO_CONTENT_ALPHA && _cairo_pattern_is_opaque_solid (pattern) && can_reduce_alpha_op (op); } static cairo_status_t fixup_unbounded (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, const cairo_composite_rectangles_t *extents) { cairo_rectangle_int_t rects[4]; int n; if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } n = 0; if (extents->bounded.width == 0 || extents->bounded.height == 0) { rects[n].x = extents->unbounded.x; rects[n].width = extents->unbounded.width; rects[n].y = extents->unbounded.y; rects[n].height = extents->unbounded.height; n++; } else { /* top */ if (extents->bounded.y != extents->unbounded.y) { rects[n].x = extents->unbounded.x; rects[n].width = extents->unbounded.width; rects[n].y = extents->unbounded.y; rects[n].height = extents->bounded.y - extents->unbounded.y; n++; } /* left */ if (extents->bounded.x != extents->unbounded.x) { rects[n].x = extents->unbounded.x; rects[n].width = extents->bounded.x - extents->unbounded.x; rects[n].y = extents->bounded.y; rects[n].height = extents->bounded.height; n++; } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { rects[n].x = extents->bounded.x + extents->bounded.width; rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x; rects[n].y = extents->bounded.y; rects[n].height = extents->bounded.height; n++; } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { rects[n].x = extents->unbounded.x; rects[n].width = extents->unbounded.width; rects[n].y = extents->bounded.y + extents->bounded.height; rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y; n++; } } return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, rects, n); } static cairo_status_t fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, const cairo_composite_rectangles_t *extents) { cairo_surface_t *mask; int mask_x, mask_y; mask = get_clip_source (compositor, extents->clip, dst, &extents->unbounded, &mask_x, &mask_y); if (unlikely (mask->status)) return mask->status; /* top */ if (extents->bounded.y != extents->unbounded.y) { int x = extents->unbounded.x; int y = extents->unbounded.y; int width = extents->unbounded.width; int height = extents->bounded.y - y; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, x + mask_x, y + mask_y, 0, 0, x, y, width, height); } /* left */ if (extents->bounded.x != extents->unbounded.x) { int x = extents->unbounded.x; int y = extents->bounded.y; int width = extents->bounded.x - x; int height = extents->bounded.height; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, x + mask_x, y + mask_y, 0, 0, x, y, width, height); } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { int x = extents->bounded.x + extents->bounded.width; int y = extents->bounded.y; int width = extents->unbounded.x + extents->unbounded.width - x; int height = extents->bounded.height; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, x + mask_x, y + mask_y, 0, 0, x, y, width, height); } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { int x = extents->unbounded.x; int y = extents->bounded.y + extents->bounded.height; int width = extents->unbounded.width; int height = extents->unbounded.y + extents->unbounded.height - y; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, x + mask_x, y + mask_y, 0, 0, x, y, width, height); } cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } static cairo_status_t fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_boxes_t clear; cairo_region_t *clip_region; cairo_box_t box; cairo_status_t status; struct _cairo_boxes_chunk *chunk; int i; assert (boxes->is_pixel_aligned); clip_region = NULL; if (_cairo_clip_is_region (extents->clip) && (clip_region = _cairo_clip_get_region (extents->clip)) && cairo_region_contains_rectangle (clip_region, &extents->bounded) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; if (boxes->num_boxes <= 1 && clip_region == NULL) return fixup_unbounded (compositor, dst, extents); _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (clip_region == NULL) { cairo_boxes_t tmp; _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; } else { pixman_box32_t *pbox; pbox = pixman_region32_rectangles (&clip_region->rgn, &i); _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &chunk->base[i]); if (unlikely (status)) { _cairo_boxes_fini (&clear); return status; } } } status = _cairo_bentley_ottmann_tessellate_boxes (&clear, CAIRO_FILL_RULE_WINDING, &clear); } if (likely (status == CAIRO_STATUS_SUCCESS)) { status = compositor->fill_boxes (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); } _cairo_boxes_fini (&clear); return status; } enum { NEED_CLIP_REGION = 0x1, NEED_CLIP_SURFACE = 0x2, FORCE_CLIP_REGION = 0x4, }; static cairo_bool_t need_bounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = NEED_CLIP_REGION; if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_bool_t need_unbounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = 0; if (! extents->is_bounded) { flags |= NEED_CLIP_REGION; if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; } if (extents->clip->path != NULL) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_status_t clip_and_composite (const cairo_mask_compositor_t *compositor, draw_func_t draw_func, draw_func_t mask_func, void *draw_closure, cairo_composite_rectangles_t*extents, unsigned int need_clip) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; cairo_pattern_t *src = &extents->source_pattern.base; cairo_region_t *clip_region = NULL; cairo_status_t status; compositor->acquire (dst); if (need_clip & NEED_CLIP_REGION) { clip_region = _cairo_clip_get_region (extents->clip); if ((need_clip & FORCE_CLIP_REGION) == 0 && _cairo_composite_rectangles_can_reduce_clip (extents, extents->clip)) clip_region = NULL; if (clip_region != NULL) { status = compositor->set_clip_region (dst, clip_region); if (unlikely (status)) { compositor->release (dst); return status; } } } if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) { op = CAIRO_OPERATOR_ADD; src = NULL; } if (op == CAIRO_OPERATOR_SOURCE) { status = clip_and_composite_source (compositor, draw_closure, draw_func, mask_func, src, extents); } else { if (op == CAIRO_OPERATOR_CLEAR) { op = CAIRO_OPERATOR_DEST_OUT; src = NULL; } if (need_clip & NEED_CLIP_SURFACE) { if (extents->is_bounded) { status = clip_and_composite_with_mask (compositor, draw_closure, draw_func, mask_func, op, src, extents); } else { status = clip_and_composite_combine (compositor, draw_closure, draw_func, op, src, extents); } } else { status = draw_func (compositor, dst, draw_closure, op, src, &extents->source_sample_area, 0, 0, &extents->bounded, extents->clip); } } if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { if (need_clip & NEED_CLIP_SURFACE) status = fixup_unbounded_with_mask (compositor, dst, extents); else status = fixup_unbounded (compositor, dst, extents); } if (clip_region) compositor->set_clip_region (dst, NULL); compositor->release (dst); return status; } static cairo_int_status_t trim_extents_to_boxes (cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_box_t box; _cairo_boxes_extents (boxes, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_status_t upload_boxes (const cairo_mask_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; const cairo_pattern_t *source = &extents->source_pattern.base; cairo_surface_t *src; cairo_rectangle_int_t limit; cairo_int_status_t status; int tx, ty; src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit); if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Check that the data is entirely within the image */ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) return CAIRO_INT_STATUS_UNSUPPORTED; if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) return CAIRO_INT_STATUS_UNSUPPORTED; tx += limit.x; ty += limit.y; if (src->type == CAIRO_SURFACE_TYPE_IMAGE) status = compositor->draw_image_boxes (dst, (cairo_image_surface_t *)src, boxes, tx, ty); else status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, tx, ty); return status; } static cairo_status_t composite_boxes (const cairo_mask_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; const cairo_pattern_t *source = &extents->source_pattern.base; cairo_bool_t need_clip_mask = extents->clip->path != NULL; cairo_status_t status; if (need_clip_mask && (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = compositor->acquire (dst); if (unlikely (status)) return status; if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_color_t *color; color = &((cairo_solid_pattern_t *) source)->color; status = compositor->fill_boxes (dst, op, color, boxes); } else { cairo_surface_t *src, *mask = NULL; int src_x, src_y; int mask_x = 0, mask_y = 0; if (need_clip_mask) { mask = get_clip_source (compositor, extents->clip, dst, &extents->bounded, &mask_x, &mask_y); if (unlikely (mask->status)) return mask->status; if (op == CAIRO_OPERATOR_CLEAR) { source = NULL; op = CAIRO_OPERATOR_DEST_OUT; } } if (source || mask == NULL) { src = compositor->pattern_to_surface (dst, source, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); } else { src = mask; src_x = mask_x; src_y = mask_y; mask = NULL; } status = compositor->composite_boxes (dst, op, src, mask, src_x, src_y, mask_x, mask_y, 0, 0, boxes, &extents->bounded); cairo_surface_destroy (src); cairo_surface_destroy (mask); } if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) status = fixup_unbounded_boxes (compositor, extents, boxes); compositor->release (dst); return status; } static cairo_status_t clip_and_composite_boxes (const cairo_mask_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_int_status_t status; if (boxes->num_boxes == 0) { if (extents->is_bounded) return CAIRO_STATUS_SUCCESS; return fixup_unbounded_boxes (compositor, extents, boxes); } if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; status = trim_extents_to_boxes (extents, boxes); if (unlikely (status)) return status; if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && extents->clip->path == NULL && (extents->op == CAIRO_OPERATOR_SOURCE || (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD)))) { status = upload_boxes (compositor, extents, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } return composite_boxes (compositor, extents, boxes); } /* high-level compositor interface */ static cairo_int_status_t _cairo_mask_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; cairo_boxes_t boxes; cairo_int_status_t status; status = compositor->check_composite (extents); if (unlikely (status)) return status; _cairo_clip_steal_boxes (extents->clip, &boxes); status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_clip_unsteal_boxes (extents->clip, &boxes); return status; } struct composite_opacity_info { const cairo_mask_compositor_t *compositor; uint8_t op; cairo_surface_t *dst; cairo_surface_t *src; int src_x, src_y; double opacity; }; static void composite_opacity(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_opacity_info *info = closure; const cairo_mask_compositor_t *compositor = info->compositor; cairo_surface_t *mask; int mask_x, mask_y; cairo_color_t color; cairo_solid_pattern_t solid; _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage); _cairo_pattern_init_solid (&solid, &color); mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE, &_cairo_unbounded_rectangle, &_cairo_unbounded_rectangle, &mask_x, &mask_y); if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { if (info->src) { compositor->composite (info->dst, info->op, info->src, mask, x + info->src_x, y + info->src_y, mask_x, mask_y, x, y, w, h); } else { compositor->composite (info->dst, info->op, mask, NULL, mask_x, mask_y, 0, 0, x, y, w, h); } } cairo_surface_destroy (mask); } static cairo_int_status_t composite_opacity_boxes (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_rectangle_int_t *src_sample, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { const cairo_solid_pattern_t *mask_pattern = closure; struct composite_opacity_info info; int i; assert (clip); info.compositor = compositor; info.op = op; info.dst = dst; if (src_pattern != NULL) { info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE, extents, src_sample, &info.src_x, &info.src_y); if (unlikely (info.src->status)) return info.src->status; } else info.src = NULL; info.opacity = mask_pattern->color.alpha / (double) 0xffff; /* XXX for lots of boxes create a clip region for the fully opaque areas */ for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_opacity, &info, &clip->boxes[i], dst_x, dst_y); cairo_surface_destroy (info.src); return CAIRO_STATUS_SUCCESS; } struct composite_box_info { const cairo_mask_compositor_t *compositor; cairo_surface_t *dst; cairo_surface_t *src; int src_x, src_y; uint8_t op; }; static void composite_box(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_box_info *info = closure; const cairo_mask_compositor_t *compositor = info->compositor; if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) { cairo_surface_t *mask; cairo_color_t color; cairo_solid_pattern_t solid; int mask_x, mask_y; _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff); _cairo_pattern_init_solid (&solid, &color); mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE, &_cairo_unbounded_rectangle, &_cairo_unbounded_rectangle, &mask_x, &mask_y); if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { compositor->composite (info->dst, info->op, info->src, mask, x + info->src_x, y + info->src_y, mask_x, mask_y, x, y, w, h); } cairo_surface_destroy (mask); } else { compositor->composite (info->dst, info->op, info->src, NULL, x + info->src_x, y + info->src_y, 0, 0, x, y, w, h); } } static cairo_int_status_t composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_rectangle_int_t *src_sample, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { cairo_composite_rectangles_t *composite = closure; struct composite_box_info info; int i; assert (src_pattern == NULL); assert (op == CAIRO_OPERATOR_SOURCE); info.compositor = compositor; info.op = CAIRO_OPERATOR_SOURCE; info.dst = dst; info.src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE, extents, &composite->mask_sample_area, &info.src_x, &info.src_y); if (unlikely (info.src->status)) return info.src->status; info.src_x += dst_x; info.src_y += dst_y; for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); cairo_surface_destroy (info.src); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_mask (const cairo_mask_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_rectangle_int_t *src_sample, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { cairo_composite_rectangles_t *composite = closure; cairo_surface_t *src, *mask; int src_x, src_y; int mask_x, mask_y; if (src_pattern != NULL) { src = compositor->pattern_to_surface (dst, src_pattern, FALSE, extents, src_sample, &src_x, &src_y); if (unlikely (src->status)) return src->status; mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE, extents, &composite->mask_sample_area, &mask_x, &mask_y); if (unlikely (mask->status)) { cairo_surface_destroy (src); return mask->status; } compositor->composite (dst, op, src, mask, extents->x + src_x, extents->y + src_y, extents->x + mask_x, extents->y + mask_y, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); cairo_surface_destroy (mask); cairo_surface_destroy (src); } else { src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE, extents, &composite->mask_sample_area, &src_x, &src_y); if (unlikely (src->status)) return src->status; compositor->composite (dst, op, src, NULL, extents->x + src_x, extents->y + src_y, 0, 0, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); cairo_surface_destroy (src); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_mask_compositor_mask (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; status = compositor->check_composite (extents); if (unlikely (status)) return status; if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && extents->clip->path == NULL && _cairo_clip_is_region (extents->clip)) { status = clip_and_composite (compositor, composite_opacity_boxes, composite_opacity_boxes, &extents->mask_pattern.solid, extents, need_unbounded_clip (extents)); } else { status = clip_and_composite (compositor, composite_mask, extents->clip->path == NULL ? composite_mask_clip_boxes : NULL, extents, extents, need_bounded_clip (extents)); } return status; } static cairo_int_status_t _cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; status = compositor->check_composite (extents); if (unlikely (status)) return status; if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { mask = cairo_surface_create_similar_image (extents->surface, CAIRO_FORMAT_A8, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; status = _cairo_surface_offset_stroke (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, style, ctm, ctm_inverse, tolerance, antialias, extents->clip); if (unlikely (status)) { cairo_surface_destroy (mask); return status; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_surface_destroy (mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, extents->clip); _cairo_pattern_fini (&pattern.base); } return status; } static cairo_int_status_t _cairo_mask_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; status = compositor->check_composite (extents); if (unlikely (status)) return status; if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { mask = cairo_surface_create_similar_image (extents->surface, CAIRO_FORMAT_A8, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; status = _cairo_surface_offset_fill (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, fill_rule, tolerance, antialias, extents->clip); if (unlikely (status)) { cairo_surface_destroy (mask); return status; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_surface_destroy (mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, extents->clip); _cairo_pattern_fini (&pattern.base); } return status; } static cairo_int_status_t _cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status; status = compositor->check_composite (extents); if (unlikely (status)) return CAIRO_INT_STATUS_UNSUPPORTED; mask = cairo_surface_create_similar_image (extents->surface, CAIRO_FORMAT_A8, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; status = _cairo_surface_offset_glyphs (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, scaled_font, glyphs, num_glyphs, extents->clip); if (unlikely (status)) { cairo_surface_destroy (mask); return status; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_surface_destroy (mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, extents->clip); _cairo_pattern_fini (&pattern.base); return status; } void _cairo_mask_compositor_init (cairo_mask_compositor_t *compositor, const cairo_compositor_t *delegate) { compositor->base.delegate = delegate; compositor->base.paint = _cairo_mask_compositor_paint; compositor->base.mask = _cairo_mask_compositor_mask; compositor->base.fill = _cairo_mask_compositor_fill; compositor->base.stroke = _cairo_mask_compositor_stroke; compositor->base.glyphs = _cairo_mask_compositor_glyphs; } �������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-matrix.c����������������������������������0000664�0000000�0000000�00000101616�12710376503�0025050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" #include <float.h> #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) #define ISFINITE(x) isfinite (x) #else #define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ #endif /** * SECTION:cairo-matrix * @Title: cairo_matrix_t * @Short_Description: Generic matrix operations * @See_Also: #cairo_t * * #cairo_matrix_t is used throughout cairo to convert between different * coordinate spaces. A #cairo_matrix_t holds an affine transformation, * such as a scale, rotation, shear, or a combination of these. * The transformation of a point (<literal>x</literal>,<literal>y</literal>) * is given by: * * <programlisting> * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * </programlisting> * * The current transformation matrix of a #cairo_t, represented as a * #cairo_matrix_t, defines the transformation from user-space * coordinates to device-space coordinates. See cairo_get_matrix() and * cairo_set_matrix(). **/ static void _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); static void _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); /** * cairo_matrix_init_identity: * @matrix: a #cairo_matrix_t * * Modifies @matrix to be an identity transformation. * * Since: 1.0 **/ void cairo_matrix_init_identity (cairo_matrix_t *matrix) { cairo_matrix_init (matrix, 1, 0, 0, 1, 0, 0); } slim_hidden_def(cairo_matrix_init_identity); /** * cairo_matrix_init: * @matrix: a #cairo_matrix_t * @xx: xx component of the affine transformation * @yx: yx component of the affine transformation * @xy: xy component of the affine transformation * @yy: yy component of the affine transformation * @x0: X translation component of the affine transformation * @y0: Y translation component of the affine transformation * * Sets @matrix to be the affine transformation given by * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given * by: * <programlisting> * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * </programlisting> * * Since: 1.0 **/ void cairo_matrix_init (cairo_matrix_t *matrix, double xx, double yx, double xy, double yy, double x0, double y0) { matrix->xx = xx; matrix->yx = yx; matrix->xy = xy; matrix->yy = yy; matrix->x0 = x0; matrix->y0 = y0; } slim_hidden_def(cairo_matrix_init); /** * _cairo_matrix_get_affine: * @matrix: a #cairo_matrix_t * @xx: location to store xx component of matrix * @yx: location to store yx component of matrix * @xy: location to store xy component of matrix * @yy: location to store yy component of matrix * @x0: location to store x0 (X-translation component) of matrix, or %NULL * @y0: location to store y0 (Y-translation component) of matrix, or %NULL * * Gets the matrix values for the affine transformation that @matrix represents. * See cairo_matrix_init(). * * * This function is a leftover from the old public API, but is still * mildly useful as an internal means for getting at the matrix * members in a positional way. For example, when reassigning to some * external matrix type, or when renaming members to more meaningful * names (such as a,b,c,d,e,f) for particular manipulations. **/ void _cairo_matrix_get_affine (const cairo_matrix_t *matrix, double *xx, double *yx, double *xy, double *yy, double *x0, double *y0) { *xx = matrix->xx; *yx = matrix->yx; *xy = matrix->xy; *yy = matrix->yy; if (x0) *x0 = matrix->x0; if (y0) *y0 = matrix->y0; } /** * cairo_matrix_init_translate: * @matrix: a #cairo_matrix_t * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * * Initializes @matrix to a transformation that translates by @tx and * @ty in the X and Y dimensions, respectively. * * Since: 1.0 **/ void cairo_matrix_init_translate (cairo_matrix_t *matrix, double tx, double ty) { cairo_matrix_init (matrix, 1, 0, 0, 1, tx, ty); } slim_hidden_def(cairo_matrix_init_translate); /** * cairo_matrix_translate: * @matrix: a #cairo_matrix_t * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * * Applies a translation by @tx, @ty to the transformation in * @matrix. The effect of the new transformation is to first translate * the coordinates by @tx and @ty, then apply the original transformation * to the coordinates. * * Since: 1.0 **/ void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) { cairo_matrix_t tmp; cairo_matrix_init_translate (&tmp, tx, ty); cairo_matrix_multiply (matrix, &tmp, matrix); } slim_hidden_def (cairo_matrix_translate); /** * cairo_matrix_init_scale: * @matrix: a #cairo_matrix_t * @sx: scale factor in the X direction * @sy: scale factor in the Y direction * * Initializes @matrix to a transformation that scales by @sx and @sy * in the X and Y dimensions, respectively. * * Since: 1.0 **/ void cairo_matrix_init_scale (cairo_matrix_t *matrix, double sx, double sy) { cairo_matrix_init (matrix, sx, 0, 0, sy, 0, 0); } slim_hidden_def(cairo_matrix_init_scale); /** * cairo_matrix_scale: * @matrix: a #cairo_matrix_t * @sx: scale factor in the X direction * @sy: scale factor in the Y direction * * Applies scaling by @sx, @sy to the transformation in @matrix. The * effect of the new transformation is to first scale the coordinates * by @sx and @sy, then apply the original transformation to the coordinates. * * Since: 1.0 **/ void cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) { cairo_matrix_t tmp; cairo_matrix_init_scale (&tmp, sx, sy); cairo_matrix_multiply (matrix, &tmp, matrix); } slim_hidden_def(cairo_matrix_scale); /** * cairo_matrix_init_rotate: * @matrix: a #cairo_matrix_t * @radians: angle of rotation, in radians. The direction of rotation * is defined such that positive angles rotate in the direction from * the positive X axis toward the positive Y axis. With the default * axis orientation of cairo, positive angles rotate in a clockwise * direction. * * Initialized @matrix to a transformation that rotates by @radians. * * Since: 1.0 **/ void cairo_matrix_init_rotate (cairo_matrix_t *matrix, double radians) { double s; double c; s = sin (radians); c = cos (radians); cairo_matrix_init (matrix, c, s, -s, c, 0, 0); } slim_hidden_def(cairo_matrix_init_rotate); /** * cairo_matrix_rotate: * @matrix: a #cairo_matrix_t * @radians: angle of rotation, in radians. The direction of rotation * is defined such that positive angles rotate in the direction from * the positive X axis toward the positive Y axis. With the default * axis orientation of cairo, positive angles rotate in a clockwise * direction. * * Applies rotation by @radians to the transformation in * @matrix. The effect of the new transformation is to first rotate the * coordinates by @radians, then apply the original transformation * to the coordinates. * * Since: 1.0 **/ void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) { cairo_matrix_t tmp; cairo_matrix_init_rotate (&tmp, radians); cairo_matrix_multiply (matrix, &tmp, matrix); } /** * cairo_matrix_multiply: * @result: a #cairo_matrix_t in which to store the result * @a: a #cairo_matrix_t * @b: a #cairo_matrix_t * * Multiplies the affine transformations in @a and @b together * and stores the result in @result. The effect of the resulting * transformation is to first apply the transformation in @a to the * coordinates and then apply the transformation in @b to the * coordinates. * * It is allowable for @result to be identical to either @a or @b. * * Since: 1.0 **/ /* * XXX: The ordering of the arguments to this function corresponds * to [row_vector]*A*B. If we want to use column vectors instead, * then we need to switch the two arguments and fix up all * uses. */ void cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) { cairo_matrix_t r; r.xx = a->xx * b->xx + a->yx * b->xy; r.yx = a->xx * b->yx + a->yx * b->yy; r.xy = a->xy * b->xx + a->yy * b->xy; r.yy = a->xy * b->yx + a->yy * b->yy; r.x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; r.y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; *result = r; } slim_hidden_def(cairo_matrix_multiply); void _cairo_matrix_multiply (cairo_matrix_t *r, const cairo_matrix_t *a, const cairo_matrix_t *b) { r->xx = a->xx * b->xx + a->yx * b->xy; r->yx = a->xx * b->yx + a->yx * b->yy; r->xy = a->xy * b->xx + a->yy * b->xy; r->yy = a->xy * b->yx + a->yy * b->yy; r->x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; r->y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; } /** * cairo_matrix_transform_distance: * @matrix: a #cairo_matrix_t * @dx: X component of a distance vector. An in/out parameter * @dy: Y component of a distance vector. An in/out parameter * * Transforms the distance vector (@dx,@dy) by @matrix. This is * similar to cairo_matrix_transform_point() except that the translation * components of the transformation are ignored. The calculation of * the returned vector is as follows: * * <programlisting> * dx2 = dx1 * a + dy1 * c; * dy2 = dx1 * b + dy1 * d; * </programlisting> * * Affine transformations are position invariant, so the same vector * always transforms to the same vector. If (@x1,@y1) transforms * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. * * Since: 1.0 **/ void cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) { double new_x, new_y; new_x = (matrix->xx * *dx + matrix->xy * *dy); new_y = (matrix->yx * *dx + matrix->yy * *dy); *dx = new_x; *dy = new_y; } slim_hidden_def(cairo_matrix_transform_distance); /** * cairo_matrix_transform_point: * @matrix: a #cairo_matrix_t * @x: X position. An in/out parameter * @y: Y position. An in/out parameter * * Transforms the point (@x, @y) by @matrix. * * Since: 1.0 **/ void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y) { cairo_matrix_transform_distance (matrix, x, y); *x += matrix->x0; *y += matrix->y0; } slim_hidden_def(cairo_matrix_transform_point); void _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double *x1, double *y1, double *x2, double *y2, cairo_bool_t *is_tight) { int i; double quad_x[4], quad_y[4]; double min_x, max_x; double min_y, max_y; if (matrix->xy == 0. && matrix->yx == 0.) { /* non-rotation/skew matrix, just map the two extreme points */ if (matrix->xx != 1.) { quad_x[0] = *x1 * matrix->xx; quad_x[1] = *x2 * matrix->xx; if (quad_x[0] < quad_x[1]) { *x1 = quad_x[0]; *x2 = quad_x[1]; } else { *x1 = quad_x[1]; *x2 = quad_x[0]; } } if (matrix->x0 != 0.) { *x1 += matrix->x0; *x2 += matrix->x0; } if (matrix->yy != 1.) { quad_y[0] = *y1 * matrix->yy; quad_y[1] = *y2 * matrix->yy; if (quad_y[0] < quad_y[1]) { *y1 = quad_y[0]; *y2 = quad_y[1]; } else { *y1 = quad_y[1]; *y2 = quad_y[0]; } } if (matrix->y0 != 0.) { *y1 += matrix->y0; *y2 += matrix->y0; } if (is_tight) *is_tight = TRUE; return; } /* general matrix */ quad_x[0] = *x1; quad_y[0] = *y1; cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); quad_x[1] = *x2; quad_y[1] = *y1; cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]); quad_x[2] = *x1; quad_y[2] = *y2; cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]); quad_x[3] = *x2; quad_y[3] = *y2; cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]); min_x = max_x = quad_x[0]; min_y = max_y = quad_y[0]; for (i=1; i < 4; i++) { if (quad_x[i] < min_x) min_x = quad_x[i]; if (quad_x[i] > max_x) max_x = quad_x[i]; if (quad_y[i] < min_y) min_y = quad_y[i]; if (quad_y[i] > max_y) max_y = quad_y[i]; } *x1 = min_x; *y1 = min_y; *x2 = max_x; *y2 = max_y; if (is_tight) { /* it's tight if and only if the four corner points form an axis-aligned rectangle. And that's true if and only if we can derive corners 0 and 3 from corners 1 and 2 in one of two straightforward ways... We could use a tolerance here but for now we'll fall back to FALSE in the case of floating point error. */ *is_tight = (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] && quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) || (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] && quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]); } } cairo_private void _cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, cairo_box_t *bbox, cairo_bool_t *is_tight) { double x1, y1, x2, y2; _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2); _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight); _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2); } static void _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) { matrix->xx *= scalar; matrix->yx *= scalar; matrix->xy *= scalar; matrix->yy *= scalar; matrix->x0 *= scalar; matrix->y0 *= scalar; } /* This function isn't a correct adjoint in that the implicit 1 in the homogeneous result should actually be ad-bc instead. But, since this adjoint is only used in the computation of the inverse, which divides by det (A)=ad-bc anyway, everything works out in the end. */ static void _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) { /* adj (A) = transpose (C:cofactor (A,i,j)) */ double a, b, c, d, tx, ty; _cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); cairo_matrix_init (matrix, d, -b, -c, a, c*ty - d*tx, b*tx - a*ty); } /** * cairo_matrix_invert: * @matrix: a #cairo_matrix_t * * Changes @matrix to be the inverse of its original value. Not * all transformation matrices have inverses; if the matrix * collapses points together (it is <firstterm>degenerate</firstterm>), * then it has no inverse and this function will fail. * * Returns: If @matrix has an inverse, modifies @matrix to * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, * returns %CAIRO_STATUS_INVALID_MATRIX. * * Since: 1.0 **/ cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix) { double det; /* Simple scaling|translation matrices are quite common... */ if (matrix->xy == 0. && matrix->yx == 0.) { matrix->x0 = -matrix->x0; matrix->y0 = -matrix->y0; if (matrix->xx != 1.) { if (matrix->xx == 0.) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); matrix->xx = 1. / matrix->xx; matrix->x0 *= matrix->xx; } if (matrix->yy != 1.) { if (matrix->yy == 0.) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); matrix->yy = 1. / matrix->yy; matrix->y0 *= matrix->yy; } return CAIRO_STATUS_SUCCESS; } /* inv (A) = 1/det (A) * adj (A) */ det = _cairo_matrix_compute_determinant (matrix); if (! ISFINITE (det)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (det == 0) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); _cairo_matrix_compute_adjoint (matrix); _cairo_matrix_scalar_multiply (matrix, 1 / det); return CAIRO_STATUS_SUCCESS; } slim_hidden_def(cairo_matrix_invert); cairo_bool_t _cairo_matrix_is_invertible (const cairo_matrix_t *matrix) { double det; det = _cairo_matrix_compute_determinant (matrix); return ISFINITE (det) && det != 0.; } cairo_bool_t _cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) { return matrix->xx == 0. && matrix->xy == 0. && matrix->yx == 0. && matrix->yy == 0.; } double _cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) { double a, b, c, d; a = matrix->xx; b = matrix->yx; c = matrix->xy; d = matrix->yy; return a*d - b*c; } /** * _cairo_matrix_compute_basis_scale_factors: * @matrix: a matrix * @basis_scale: the scale factor in the direction of basis * @normal_scale: the scale factor in the direction normal to the basis * @x_basis: basis to use. X basis if true, Y basis otherwise. * * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1] * otherwise, and M is @matrix. * * Return value: the scale factor of @matrix on the height of the font, * or 1.0 if @matrix is %NULL. **/ cairo_status_t _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, double *basis_scale, double *normal_scale, cairo_bool_t x_basis) { double det; det = _cairo_matrix_compute_determinant (matrix); if (! ISFINITE (det)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); if (det == 0) { *basis_scale = *normal_scale = 0; } else { double x = x_basis != 0; double y = x == 0; double major, minor; cairo_matrix_transform_distance (matrix, &x, &y); major = hypot (x, y); /* * ignore mirroring */ if (det < 0) det = -det; if (major) minor = det / major; else minor = 0.0; if (x_basis) { *basis_scale = major; *normal_scale = minor; } else { *basis_scale = minor; *normal_scale = major; } } return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, int *itx, int *ity) { if (_cairo_matrix_is_translation (matrix)) { cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0); cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0); if (_cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed)) { if (itx) *itx = _cairo_fixed_integer_part (x0_fixed); if (ity) *ity = _cairo_fixed_integer_part (y0_fixed); return TRUE; } } return FALSE; } cairo_bool_t _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix) { if (matrix->xy == 0.0 && matrix->yx == 0.0) { if (! (matrix->xx == 1.0 || matrix->xx == -1.0)) return FALSE; if (! (matrix->yy == 1.0 || matrix->yy == -1.0)) return FALSE; } else if (matrix->xx == 0.0 && matrix->yy == 0.0) { if (! (matrix->xy == 1.0 || matrix->xy == -1.0)) return FALSE; if (! (matrix->yx == 1.0 || matrix->yx == -1.0)) return FALSE; } else return FALSE; return TRUE; } /* By pixel exact here, we mean a matrix that is composed only of * 90 degree rotations, flips, and integer translations and produces a 1:1 * mapping between source and destination pixels. If we transform an image * with a pixel-exact matrix, filtering is not useful. */ cairo_bool_t _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) { cairo_fixed_t x0_fixed, y0_fixed; if (! _cairo_matrix_has_unity_scale (matrix)) return FALSE; x0_fixed = _cairo_fixed_from_double (matrix->x0); y0_fixed = _cairo_fixed_from_double (matrix->y0); return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed); } /* A circle in user space is transformed into an ellipse in device space. The following is a derivation of a formula to calculate the length of the major axis for this ellipse; this is useful for error bounds calculations. Thanks to Walter Brisken <wbrisken@aoc.nrao.edu> for this derivation: 1. First some notation: All capital letters represent vectors in two dimensions. A prime ' represents a transformed coordinate. Matrices are written in underlined form, ie _R_. Lowercase letters represent scalar real values. 2. The question has been posed: What is the maximum expansion factor achieved by the linear transformation X' = X _R_ where _R_ is a real-valued 2x2 matrix with entries: _R_ = [a b] [c d] . In other words, what is the maximum radius, MAX[ |X'| ], reached for any X on the unit circle ( |X| = 1 ) ? 3. Some useful formulae (A) through (C) below are standard double-angle formulae. (D) is a lesser known result and is derived below: (A) sin²(θ) = (1 - cos(2*θ))/2 (B) cos²(θ) = (1 + cos(2*θ))/2 (C) sin(θ)*cos(θ) = sin(2*θ)/2 (D) MAX[a*cos(θ) + b*sin(θ)] = sqrt(a² + b²) Proof of (D): find the maximum of the function by setting the derivative to zero: -a*sin(θ)+b*cos(θ) = 0 From this it follows that tan(θ) = b/a and hence sin(θ) = b/sqrt(a² + b²) and cos(θ) = a/sqrt(a² + b²) Thus the maximum value is MAX[a*cos(θ) + b*sin(θ)] = (a² + b²)/sqrt(a² + b²) = sqrt(a² + b²) 4. Derivation of maximum expansion To find MAX[ |X'| ] we search brute force method using calculus. The unit circle on which X is constrained is to be parameterized by t: X(θ) = (cos(θ), sin(θ)) Thus X'(θ) = X(θ) * _R_ = (cos(θ), sin(θ)) * [a b] [c d] = (a*cos(θ) + c*sin(θ), b*cos(θ) + d*sin(θ)). Define r(θ) = |X'(θ)| Thus r²(θ) = (a*cos(θ) + c*sin(θ))² + (b*cos(θ) + d*sin(θ))² = (a² + b²)*cos²(θ) + (c² + d²)*sin²(θ) + 2*(a*c + b*d)*cos(θ)*sin(θ) Now apply the double angle formulae (A) to (C) from above: r²(θ) = (a² + b² + c² + d²)/2 + (a² + b² - c² - d²)*cos(2*θ)/2 + (a*c + b*d)*sin(2*θ) = f + g*cos(φ) + h*sin(φ) Where f = (a² + b² + c² + d²)/2 g = (a² + b² - c² - d²)/2 h = (a*c + d*d) φ = 2*θ It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]). Here we determine MAX[ r² ] using (D) from above: MAX[ r² ] = f + sqrt(g² + h²) And finally MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) ) Which is the solution to this problem. Walter Brisken 2004/10/08 (Note that the minor axis length is at the minimum of the above solution, which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)). For another derivation of the same result, using Singular Value Decomposition, see doc/tutorial/src/singular.c. */ /* determine the length of the major axis of a circle of the given radius after applying the transformation matrix. */ double _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, double radius) { double a, b, c, d, f, g, h, i, j; if (_cairo_matrix_has_unity_scale (matrix)) return radius; _cairo_matrix_get_affine (matrix, &a, &b, &c, &d, NULL, NULL); i = a*a + b*b; j = c*c + d*d; f = 0.5 * (i + j); g = 0.5 * (i - j); h = a*c + b*d; return radius * sqrt (f + hypot (g, h)); /* * we don't need the minor axis length, which is * double min = radius * sqrt (f - sqrt (g*g+h*h)); */ } static const pixman_transform_t pixman_identity_transform = {{ {1 << 16, 0, 0}, { 0, 1 << 16, 0}, { 0, 0, 1 << 16} }}; static cairo_status_t _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, pixman_transform_t *pixman_transform, double xc, double yc) { cairo_matrix_t inv; unsigned max_iterations; pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); pixman_transform->matrix[2][0] = 0; pixman_transform->matrix[2][1] = 0; pixman_transform->matrix[2][2] = 1 << 16; /* The conversion above breaks cairo's translation invariance: * a translation of (a, b) in device space translates to * a translation of (xx * a + xy * b, yx * a + yy * b) * for cairo, while pixman uses rounded versions of xx ... yy. * This error increases as a and b get larger. * * To compensate for this, we fix the point (xc, yc) in pattern * space and adjust pixman's transform to agree with cairo's at * that point. */ if (_cairo_matrix_has_unity_scale (matrix)) return CAIRO_STATUS_SUCCESS; if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT || fabs (matrix->xy) > PIXMAN_MAX_INT || fabs (matrix->x0) > PIXMAN_MAX_INT || fabs (matrix->yx) > PIXMAN_MAX_INT || fabs (matrix->yy) > PIXMAN_MAX_INT || fabs (matrix->y0) > PIXMAN_MAX_INT)) { return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); } /* Note: If we can't invert the transformation, skip the adjustment. */ inv = *matrix; if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; /* find the pattern space coordinate that maps to (xc, yc) */ max_iterations = 5; do { double x,y; pixman_vector_t vector; cairo_fixed_16_16_t dx, dy; vector.vector[0] = _cairo_fixed_16_16_from_double (xc); vector.vector[1] = _cairo_fixed_16_16_from_double (yc); vector.vector[2] = 1 << 16; /* If we can't transform the reference point, skip the adjustment. */ if (! pixman_transform_point_3d (pixman_transform, &vector)) return CAIRO_STATUS_SUCCESS; x = pixman_fixed_to_double (vector.vector[0]); y = pixman_fixed_to_double (vector.vector[1]); cairo_matrix_transform_point (&inv, &x, &y); /* Ideally, the vector should now be (xc, yc). * We can now compensate for the resulting error. */ x -= xc; y -= yc; cairo_matrix_transform_distance (matrix, &x, &y); dx = _cairo_fixed_16_16_from_double (x); dy = _cairo_fixed_16_16_from_double (y); pixman_transform->matrix[0][2] -= dx; pixman_transform->matrix[1][2] -= dy; if (dx == 0 && dy == 0) return CAIRO_STATUS_SUCCESS; } while (--max_iterations); /* We didn't find an exact match between cairo and pixman, but * the matrix should be mostly correct */ return CAIRO_STATUS_SUCCESS; } static inline double _pixman_nearest_sample (double d) { return ceil (d - .5); } /** * _cairo_matrix_is_pixman_translation: * @matrix: a matrix * @filter: the filter to be used on the pattern transformed by @matrix * @x_offset: the translation in the X direction * @y_offset: the translation in the Y direction * * Checks if @matrix translated by (x_offset, y_offset) can be * represented using just an offset (within the range pixman can * accept) and an identity matrix. * * Passing a non-zero value in x_offset/y_offset has the same effect * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and * setting x_offset and y_offset to 0. * * Upon return x_offset and y_offset contain the translation vector if * the return value is %TRUE. If the return value is %FALSE, they will * not be modified. * * Return value: %TRUE if @matrix can be represented as a pixman * translation, %FALSE otherwise. **/ cairo_bool_t _cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, cairo_filter_t filter, int *x_offset, int *y_offset) { double tx, ty; if (!_cairo_matrix_is_translation (matrix)) return FALSE; if (matrix->x0 == 0. && matrix->y0 == 0.) return TRUE; tx = matrix->x0 + *x_offset; ty = matrix->y0 + *y_offset; if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { tx = _pixman_nearest_sample (tx); ty = _pixman_nearest_sample (ty); } else if (tx != floor (tx) || ty != floor (ty)) { return FALSE; } if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT) return FALSE; *x_offset = _cairo_lround (tx); *y_offset = _cairo_lround (ty); return TRUE; } /** * _cairo_matrix_to_pixman_matrix_offset: * @matrix: a matrix * @filter: the filter to be used on the pattern transformed by @matrix * @xc: the X coordinate of the point to fix in pattern space * @yc: the Y coordinate of the point to fix in pattern space * @out_transform: the transformation which best approximates @matrix * @x_offset: the translation in the X direction * @y_offset: the translation in the Y direction * * This function tries to represent @matrix translated by (x_offset, * y_offset) as a %pixman_transform_t and an translation. * * Passing a non-zero value in x_offset/y_offset has the same effect * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and * setting x_offset and y_offset to 0. * * If it is possible to represent the matrix with an identity * %pixman_transform_t and a translation within the valid range for * pixman, this function will set @out_transform to be the identity, * @x_offset and @y_offset to be the translation vector and will * return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to * evenly divide the translational component of @matrix between * @out_transform and (@x_offset, @y_offset). * * Upon return x_offset and y_offset contain the translation vector. * * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform * is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not * possible to represent @matrix as a pixman_transform_t without * overflows, %CAIRO_STATUS_SUCCESS otherwise. **/ cairo_status_t _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, cairo_filter_t filter, double xc, double yc, pixman_transform_t *out_transform, int *x_offset, int *y_offset) { cairo_bool_t is_pixman_translation; is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix, filter, x_offset, y_offset); if (is_pixman_translation) { *out_transform = pixman_identity_transform; return CAIRO_INT_STATUS_NOTHING_TO_DO; } else { cairo_matrix_t m; m = *matrix; cairo_matrix_translate (&m, *x_offset, *y_offset); if (m.x0 != 0.0 || m.y0 != 0.0) { double tx, ty, norm; int i, j; /* pixman also limits the [xy]_offset to 16 bits so evenly * spread the bits between the two. * * To do this, find the solutions of: * |x| = |x*m.xx + y*m.xy + m.x0| * |y| = |x*m.yx + y*m.yy + m.y0| * * and select the one whose maximum norm is smallest. */ tx = m.x0; ty = m.y0; norm = MAX (fabs (tx), fabs (ty)); for (i = -1; i < 2; i+=2) { for (j = -1; j < 2; j+=2) { double x, y, den, new_norm; den = (m.xx + i) * (m.yy + j) - m.xy * m.yx; if (fabs (den) < DBL_EPSILON) continue; x = m.y0 * m.xy - m.x0 * (m.yy + j); y = m.x0 * m.yx - m.y0 * (m.xx + i); den = 1 / den; x *= den; y *= den; new_norm = MAX (fabs (x), fabs (y)); if (norm > new_norm) { norm = new_norm; tx = x; ty = y; } } } tx = floor (tx); ty = floor (ty); *x_offset = -tx; *y_offset = -ty; cairo_matrix_translate (&m, tx, ty); } else { *x_offset = 0; *y_offset = 0; } return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc); } } ������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mempool-private.h�������������������������0000664�0000000�0000000�00000005006�12710376503�0026665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributors(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_MEMPOOL_PRIVATE_H #define CAIRO_MEMPOOL_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include <stddef.h> /* for size_t */ CAIRO_BEGIN_DECLS typedef struct _cairo_mempool cairo_mempool_t; struct _cairo_mempool { char *base; struct _cairo_memblock { int bits; cairo_list_t link; } *blocks; cairo_list_t free[32]; unsigned char *map; unsigned int num_blocks; int min_bits; /* Minimum block size is 1 << min_bits */ int num_sizes; int max_free_bits; size_t free_bytes; size_t max_bytes; }; cairo_private cairo_status_t _cairo_mempool_init (cairo_mempool_t *pool, void *base, size_t bytes, int min_bits, int num_sizes); cairo_private void * _cairo_mempool_alloc (cairo_mempool_t *pi, size_t bytes); cairo_private void _cairo_mempool_free (cairo_mempool_t *pi, void *storage); cairo_private void _cairo_mempool_fini (cairo_mempool_t *pool); CAIRO_END_DECLS #endif /* CAIRO_MEMPOOL_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mempool.c���������������������������������0000664�0000000�0000000�00000023377�12710376503�0025223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipoolent may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributors(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-mempool-private.h" #include "cairo-list-inline.h" /* a simple buddy allocator for memory pools * XXX fragmentation? use Doug Lea's malloc? */ #define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7))) #define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7))) #define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7))) static void clear_bits (cairo_mempool_t *pool, size_t first, size_t last) { size_t i, n = last; size_t first_full = (first + 7) & ~7; size_t past_full = last & ~7; size_t bytes; if (n > first_full) n = first_full; for (i = first; i < n; i++) BITCLEAR (pool, i); if (past_full > first_full) { bytes = past_full - first_full; bytes = bytes >> 3; memset (pool->map + (first_full >> 3), 0, bytes); } if (past_full < n) past_full = n; for (i = past_full; i < last; i++) BITCLEAR (pool, i); } static void free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear) { struct _cairo_memblock *block; if (clear) clear_bits (pool, start, start + (1 << bits)); block = pool->blocks + start; block->bits = bits; cairo_list_add (&block->link, &pool->free[bits]); pool->free_bytes += 1 << (bits + pool->min_bits); if (bits > pool->max_free_bits) pool->max_free_bits = bits; } /* Add a chunk to the free list */ static void free_blocks (cairo_mempool_t *pool, size_t first, size_t last, cairo_bool_t clear) { size_t i, len; int bits = 0; for (i = first, len = 1; i < last; i += len) { /* To avoid cost quadratic in the number of different * blocks produced from this chunk of store, we have to * use the size of the previous block produced from this * chunk as the starting point to work out the size of the * next block we can produce. If you look at the binary * representation of the starting points of the blocks * produced, you can see that you first of all increase the * size of the blocks produced up to some maximum as the * address dealt with gets offsets added on which zap out * low order bits, then decrease as the low order bits of the * final block produced get added in. E.g. as you go from * 001 to 0111 you generate blocks * of size 001 at 001 taking you to 010 * of size 010 at 010 taking you to 100 * of size 010 at 100 taking you to 110 * of size 001 at 110 taking you to 111 * So the maximum total cost of the loops below this comment * is one trip from the lowest blocksize to the highest and * back again. */ while (bits < pool->num_sizes - 1) { size_t next_bits = bits + 1; size_t next_len = len << 1; if (i + next_bits > last) { /* off end of chunk to be freed */ break; } if (i & (next_len - 1)) /* block would not be on boundary */ break; bits = next_bits; len = next_len; } do { if (i + len <= last && /* off end of chunk to be freed */ (i & (len - 1)) == 0) /* block would not be on boundary */ break; bits--; len >>=1; } while (len); if (len == 0) break; free_bits (pool, i, bits, clear); } } static struct _cairo_memblock * get_buddy (cairo_mempool_t *pool, size_t offset, int bits) { struct _cairo_memblock *block; if (offset + (1 << bits) >= pool->num_blocks) return NULL; /* invalid */ if (BITTEST (pool, offset + (1 << bits) - 1)) return NULL; /* buddy is allocated */ block = pool->blocks + offset; if (block->bits != bits) return NULL; /* buddy is partially allocated */ return block; } static void merge_buddies (cairo_mempool_t *pool, struct _cairo_memblock *block, int max_bits) { size_t block_offset = block - pool->blocks; int bits = block->bits; while (bits < max_bits - 1) { /* while you can, merge two blocks and get a legal block size */ size_t buddy_offset = block_offset ^ (1 << bits); block = get_buddy (pool, buddy_offset, bits); if (block == NULL) break; cairo_list_del (&block->link); /* Merged block starts at buddy */ if (buddy_offset < block_offset) block_offset = buddy_offset; bits++; } block = pool->blocks + block_offset; block->bits = bits; cairo_list_add (&block->link, &pool->free[bits]); if (bits > pool->max_free_bits) pool->max_free_bits = bits; } /* attempt to merge all available buddies up to a particular size */ static int merge_bits (cairo_mempool_t *pool, int max_bits) { struct _cairo_memblock *block, *buddy, *next; int bits; for (bits = 0; bits < max_bits - 1; bits++) { cairo_list_foreach_entry_safe (block, next, struct _cairo_memblock, &pool->free[bits], link) { size_t buddy_offset = (block - pool->blocks) ^ (1 << bits); buddy = get_buddy (pool, buddy_offset, bits); if (buddy == NULL) continue; if (buddy == next) { next = cairo_container_of (buddy->link.next, struct _cairo_memblock, link); } cairo_list_del (&block->link); merge_buddies (pool, block, max_bits); } } return pool->max_free_bits; } /* find store for 1 << bits blocks */ static void * buddy_malloc (cairo_mempool_t *pool, int bits) { size_t past, offset; struct _cairo_memblock *block; int b; if (bits > pool->max_free_bits && bits > merge_bits (pool, bits)) return NULL; /* Find a list with blocks big enough on it */ block = NULL; for (b = bits; b <= pool->max_free_bits; b++) { if (! cairo_list_is_empty (&pool->free[b])) { block = cairo_list_first_entry (&pool->free[b], struct _cairo_memblock, link); break; } } assert (block != NULL); cairo_list_del (&block->link); while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) { if (--pool->max_free_bits == -1) break; } /* Mark end of allocated area */ offset = block - pool->blocks; past = offset + (1 << bits); BITSET (pool, past - 1); block->bits = bits; /* If we used a larger free block than we needed, free the rest */ pool->free_bytes -= 1 << (b + pool->min_bits); free_blocks (pool, past, offset + (1 << b), 0); return pool->base + ((block - pool->blocks) << pool->min_bits); } cairo_status_t _cairo_mempool_init (cairo_mempool_t *pool, void *base, size_t bytes, int min_bits, int num_sizes) { unsigned long tmp; int num_blocks; int i; /* Align the start to an integral chunk */ tmp = ((unsigned long) base) & ((1 << min_bits) - 1); if (tmp) { tmp = (1 << min_bits) - tmp; base = (char *)base + tmp; bytes -= tmp; } assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0); assert (num_sizes < ARRAY_LENGTH (pool->free)); pool->base = base; pool->free_bytes = 0; pool->max_bytes = bytes; pool->max_free_bits = -1; num_blocks = bytes >> min_bits; pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock)); if (pool->blocks == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pool->num_blocks = num_blocks; pool->min_bits = min_bits; pool->num_sizes = num_sizes; for (i = 0; i < ARRAY_LENGTH (pool->free); i++) cairo_list_init (&pool->free[i]); pool->map = malloc ((num_blocks + 7) >> 3); if (pool->map == NULL) { free (pool->blocks); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (pool->map, -1, (num_blocks + 7) >> 3); clear_bits (pool, 0, num_blocks); /* Now add all blocks to the free list */ free_blocks (pool, 0, num_blocks, 1); return CAIRO_STATUS_SUCCESS; } void * _cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes) { size_t size; int bits; size = 1 << pool->min_bits; for (bits = 0; size < bytes; bits++) size <<= 1; if (bits >= pool->num_sizes) return NULL; return buddy_malloc (pool, bits); } void _cairo_mempool_free (cairo_mempool_t *pool, void *storage) { size_t block_offset; struct _cairo_memblock *block; block_offset = ((char *)storage - pool->base) >> pool->min_bits; block = pool->blocks + block_offset; BITCLEAR (pool, block_offset + ((1 << block->bits) - 1)); pool->free_bytes += 1 << (block->bits + pool->min_bits); merge_buddies (pool, block, pool->num_sizes); } void _cairo_mempool_fini (cairo_mempool_t *pool) { free (pool->map); free (pool->blocks); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mesh-pattern-rasterizer.c�����������������0000664�0000000�0000000�00000072333�12710376503�0030346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright 2009 Andrea Canciani * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Andrea Canciani. * * Contributor(s): * Andrea Canciani <ranma42@gmail.com> */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-pattern-private.h" /* * Rasterizer for mesh patterns. * * This implementation is based on techniques derived from several * papers (available from ACM): * * - Lien, Shantz and Pratt "Adaptive Forward Differencing for * Rendering Curves and Surfaces" (discussion of the AFD technique, * bound of 1/sqrt(2) on step length without proof) * * - Popescu and Rosen, "Forward rasterization" (description of * forward rasterization, proof of the previous bound) * * - Klassen, "Integer Forward Differencing of Cubic Polynomials: * Analysis and Algorithms" * * - Klassen, "Exact Integer Hybrid Subdivision and Forward * Differencing of Cubics" (improving the bound on the minimum * number of steps) * * - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces * with Integer Adaptive Forward Differencing" (analysis of forward * differencing applied to Bezier patches) * * Notes: * - Poor performance expected in degenerate cases * * - Patches mostly outside the drawing area are drawn completely (and * clipped), wasting time * * - Both previous problems are greatly reduced by splitting until a * reasonably small size and clipping the new tiles: execution time * is quadratic in the convex-hull diameter instead than linear to * the painted area. Splitting the tiles doesn't change the painted * area but (usually) reduces the bounding box area (bbox area can * remain the same after splitting, but cannot grow) * * - The initial implementation used adaptive forward differencing, * but simple forward differencing scored better in benchmarks * * Idea: * * We do a sampling over the cubic patch with step du and dv (in the * two parameters) that guarantees that any point of our sampling will * be at most at 1/sqrt(2) from its adjacent points. In formulae * (assuming B is the patch): * * |B(u,v) - B(u+du,v)| < 1/sqrt(2) * |B(u,v) - B(u,v+dv)| < 1/sqrt(2) * * This means that every pixel covered by the patch will contain at * least one of the samples, thus forward rasterization can be * performed. Sketch of proof (from Popescu and Rosen): * * Let's take the P pixel we're interested into. If we assume it to be * square, its boundaries define 9 regions on the plane: * * 1|2|3 * -+-+- * 8|P|4 * -+-+- * 7|6|5 * * Let's check that the pixel P will contain at least one point * assuming that it is covered by the patch. * * Since the pixel is covered by the patch, its center will belong to * (at least) one of the quads: * * {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]} * * If P doesn't contain any of the corners of the quad: * * - if one of the corners is in 1,3,5 or 7, other two of them have to * be in 2,4,6 or 8, thus if the last corner is not in P, the length * of one of the edges will be > 1/sqrt(2) * * - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6 * and/or 8. If they are all in different regions, they can't * satisfy the distance constraint. If two of them are in the same * region (let's say 2), no point is in 6 and again it is impossible * to have the center of P in the quad respecting the distance * constraint (both these assertions can be checked by continuity * considering the length of the edges of a quad with the vertices * on the edges of P) * * Each of the cases led to a contradiction, so P contains at least * one of the corners of the quad. */ /* * Make sure that errors are less than 1 in fixed point math if you * change these values. * * The error is amplified by about steps^3/4 times. * The rasterizer always uses a number of steps that is a power of 2. * * 256 is the maximum allowed number of steps (to have error < 1) * using 8.24 for the differences. */ #define STEPS_MAX_V 256.0 #define STEPS_MAX_U 256.0 /* * If the patch/curve is only partially visible, split it to a finer * resolution to get higher chances to clip (part of) it. * * These values have not been computed, but simply obtained * empirically (by benchmarking some patches). They should never be * greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small * as 1 (depending on how much you want to spend time in splitting the * patch/curve when trying to save some rasterization time). */ #define STEPS_CLIP_V 64.0 #define STEPS_CLIP_U 64.0 /* Utils */ static inline double sqlen (cairo_point_double_t p0, cairo_point_double_t p1) { cairo_point_double_t delta; delta.x = p0.x - p1.x; delta.y = p0.y - p1.y; return delta.x * delta.x + delta.y * delta.y; } static inline int16_t _color_delta_to_shifted_short (int32_t from, int32_t to, int shift) { int32_t delta = to - from; /* We need to round toward zero, because otherwise adding the * delta 2^shift times can overflow */ if (delta >= 0) return delta >> shift; else return -((-delta) >> shift); } /* * Convert a number of steps to the equivalent shift. * * Input: the square of the minimum number of steps * * Output: the smallest integer x such that 2^x > steps */ static inline int sqsteps2shift (double steps_sq) { int r; frexp (MAX (1.0, steps_sq), &r); return (r + 1) >> 1; } /* * FD functions * * A Bezier curve is defined (with respect to a parameter t in * [0,1]) from its nodes (x,y,z,w) like this: * * B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3 * * To efficiently evaluate a Bezier curve, the rasterizer uses forward * differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it * is possible to convert them to forward differences form and walk * over the curve using fd_init (), fd_down () and fd_fwd (). * * f[0] is always the value of the Bezier curve for "current" t. */ /* * Initialize the coefficient for forward differences. * * Input: x,y,z,w are the 4 nodes of the Bezier curve * * Output: f[i] is the i-th difference of the curve * * f[0] is the value of the curve for t==0, i.e. f[0]==x. * * The initial step is 1; this means that each step increases t by 1 * (so fd_init () immediately followed by fd_fwd (f) n times makes * f[0] be the value of the curve for t==n). */ static inline void fd_init (double x, double y, double z, double w, double f[4]) { f[0] = x; f[1] = w - x; f[2] = 6. * (w - 2. * z + y); f[3] = 6. * (w - 3. * z + 3. * y - x); } /* * Halve the step of the coefficients for forward differences. * * Input: f[i] is the i-th difference of the curve * * Output: f[i] is the i-th difference of the curve with half the * original step * * f[0] is not affected, so the current t is not changed. * * The other coefficients are changed so that the step is half the * original step. This means that doing fd_fwd (f) n times with the * input f results in the same f[0] as doing fd_fwd (f) 2n times with * the output f. */ static inline void fd_down (double f[4]) { f[3] *= 0.125; f[2] = f[2] * 0.25 - f[3]; f[1] = (f[1] - f[2]) * 0.5; } /* * Perform one step of forward differences along the curve. * * Input: f[i] is the i-th difference of the curve * * Output: f[i] is the i-th difference of the curve after one step */ static inline void fd_fwd (double f[4]) { f[0] += f[1]; f[1] += f[2]; f[2] += f[3]; } /* * Transform to integer forward differences. * * Input: d[n] is the n-th difference (in double precision) * * Output: i[n] is the n-th difference (in fixed point precision) * * i[0] is 9.23 fixed point, other differences are 4.28 fixed point. */ static inline void fd_fixed (double d[4], int32_t i[4]) { i[0] = _cairo_fixed_16_16_from_double (256 * 2 * d[0]); i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]); i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]); i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]); } /* * Perform one step of integer forward differences along the curve. * * Input: f[n] is the n-th difference * * Output: f[n] is the n-th difference * * f[0] is 9.23 fixed point, other differences are 4.28 fixed point. */ static inline void fd_fixed_fwd (int32_t f[4]) { f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1); f[1] += f[2]; f[2] += f[3]; } /* * Compute the minimum number of steps that guarantee that walking * over a curve will leave no holes. * * Input: p[0..3] the nodes of the Bezier curve * * Returns: the square of the number of steps * * Idea: * * We want to make sure that at every step we move by less than * 1/sqrt(2). * * The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is * the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3, * so (since a Bezier curve is always bounded by its convex hull), we * can say that: * * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|) * * We can improve this by noticing that a quadratic Bezier (a,b,c) is * bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so * (substituting the previous values, using t=0.5 and simplifying): * * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) * * So, to guarantee a maximum step length of 1/sqrt(2) we must do: * * 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps */ static inline double bezier_steps_sq (cairo_point_double_t p[4]) { double tmp = sqlen (p[0], p[1]); tmp = MAX (tmp, sqlen (p[2], p[3])); tmp = MAX (tmp, sqlen (p[0], p[2]) * .25); tmp = MAX (tmp, sqlen (p[1], p[3]) * .25); return 18.0 * tmp; } /* * Split a 1D Bezier cubic using de Casteljau's algorithm. * * Input: x,y,z,w the nodes of the Bezier curve * * Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of * the first half and of the second half of the curve * * The output control nodes have to be distinct. */ static inline void split_bezier_1D (double x, double y, double z, double w, double *x0, double *y0, double *z0, double *w0, double *x1, double *y1, double *z1, double *w1) { double tmp; *x0 = x; *w1 = w; tmp = 0.5 * (y + z); *y0 = 0.5 * (x + y); *z1 = 0.5 * (z + w); *z0 = 0.5 * (*y0 + tmp); *y1 = 0.5 * (tmp + *z1); *w0 = *x1 = 0.5 * (*z0 + *y1); } /* * Split a Bezier curve using de Casteljau's algorithm. * * Input: p[0..3] the nodes of the Bezier curve * * Output: fst_half[0..3] and snd_half[0..3] are respectively the * nodes of the first and of the second half of the curve * * fst_half and snd_half must be different, but they can be the same as * nodes. */ static void split_bezier (cairo_point_double_t p[4], cairo_point_double_t fst_half[4], cairo_point_double_t snd_half[4]) { split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x, &fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x, &snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x); split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y, &fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y, &snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y); } typedef enum _intersection { INSIDE = -1, /* the interval is entirely contained in the reference interval */ OUTSIDE = 0, /* the interval has no intersection with the reference interval */ PARTIAL = 1 /* the interval intersects the reference interval (but is not fully inside it) */ } intersection_t; /* * Check if an interval if inside another. * * Input: a,b are the extrema of the first interval * c,d are the extrema of the second interval * * Returns: INSIDE iff [a,b) intersection [c,d) = [a,b) * OUTSIDE iff [a,b) intersection [c,d) = {} * PARTIAL otherwise * * The function assumes a < b and c < d * * Note: Bitwise-anding the results along each component gives the * expected result for [a,b) x [A,B) intersection [c,d) x [C,D). */ static inline int intersect_interval (double a, double b, double c, double d) { if (c <= a && b <= d) return INSIDE; else if (a >= d || b <= c) return OUTSIDE; else return PARTIAL; } /* * Set the color of a pixel. * * Input: data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * x, y are the coordinates of the pixel to be colored * r,g,b,a are the color components of the color to be set * * Output: the (x,y) pixel in data has the (r,g,b,a) color * * The input color components are not premultiplied, but the data * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, * premultiplied). * * If the pixel to be set is outside the image, this function does * nothing. */ static inline void draw_pixel (unsigned char *data, int width, int height, int stride, int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a) { if (likely (0 <= x && 0 <= y && x < width && y < height)) { uint32_t tr, tg, tb, ta; /* Premultiply and round */ ta = a; tr = r * ta + 0x8000; tg = g * ta + 0x8000; tb = b * ta + 0x8000; tr += tr >> 16; tg += tg >> 16; tb += tb >> 16; *((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) | ((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24); } } /* * Forward-rasterize a cubic curve using forward differences. * * Input: data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * ushift is log2(n) if n is the number of desired steps * dxu[i], dyu[i] are the x,y forward differences of the curve * r0,g0,b0,a0 are the color components of the start point * r3,g3,b3,a3 are the color components of the end point * * Output: data will be changed to have the requested curve drawn in * the specified colors * * The input color components are not premultiplied, but the data * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, * premultiplied). * * The function draws n+1 pixels, that is from the point at step 0 to * the point at step n, both included. This is the discrete equivalent * to drawing the curve for values of the interpolation parameter in * [0,1] (including both extremes). */ static inline void rasterize_bezier_curve (unsigned char *data, int width, int height, int stride, int ushift, double dxu[4], double dyu[4], uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0, uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3) { int32_t xu[4], yu[4]; int x0, y0, u, usteps = 1 << ushift; uint16_t r = r0, g = g0, b = b0, a = a0; int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift); int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift); int16_t db = _color_delta_to_shifted_short (b0, b3, ushift); int16_t da = _color_delta_to_shifted_short (a0, a3, ushift); fd_fixed (dxu, xu); fd_fixed (dyu, yu); /* * Use (dxu[0],dyu[0]) as origin for the forward differences. * * This makes it possible to handle much larger coordinates (the * ones that can be represented as cairo_fixed_t) */ x0 = _cairo_fixed_from_double (dxu[0]); y0 = _cairo_fixed_from_double (dyu[0]); xu[0] = 0; yu[0] = 0; for (u = 0; u <= usteps; ++u) { /* * This rasterizer assumes that pixels are integer aligned * squares, so a generic (x,y) point belongs to the pixel with * top-left coordinates (floor(x), floor(y)) */ int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1)); int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1)); draw_pixel (data, width, height, stride, x, y, r, g, b, a); fd_fixed_fwd (xu); fd_fixed_fwd (yu); r += dr; g += dg; b += db; a += da; } } /* * Clip, split and rasterize a Bezier curve. * * Input: data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * p[i] is the i-th node of the Bezier curve * c0[i] is the i-th color component at the start point * c3[i] is the i-th color component at the end point * * Output: data will be changed to have the requested curve drawn in * the specified colors * * The input color components are not premultiplied, but the data * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, * premultiplied). * * The color components are red, green, blue and alpha, in this order. * * The function guarantees that it will draw the curve with a step * small enough to never have a distance above 1/sqrt(2) between two * consecutive points (which is needed to ensure that no hole can * appear when using this function to rasterize a patch). */ static void draw_bezier_curve (unsigned char *data, int width, int height, int stride, cairo_point_double_t p[4], double c0[4], double c3[4]) { double top, bottom, left, right, steps_sq; int i, v; top = bottom = p[0].y; for (i = 1; i < 4; ++i) { top = MIN (top, p[i].y); bottom = MAX (bottom, p[i].y); } /* Check visibility */ v = intersect_interval (top, bottom, 0, height); if (v == OUTSIDE) return; left = right = p[0].x; for (i = 1; i < 4; ++i) { left = MIN (left, p[i].x); right = MAX (right, p[i].x); } v &= intersect_interval (left, right, 0, width); if (v == OUTSIDE) return; steps_sq = bezier_steps_sq (p); if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) { /* * The number of steps is greater than the threshold. This * means that either the error would become too big if we * directly rasterized it or that we can probably save some * time by splitting the curve and clipping part of it */ cairo_point_double_t first[4], second[4]; double midc[4]; split_bezier (p, first, second); midc[0] = (c0[0] + c3[0]) * 0.5; midc[1] = (c0[1] + c3[1]) * 0.5; midc[2] = (c0[2] + c3[2]) * 0.5; midc[3] = (c0[3] + c3[3]) * 0.5; draw_bezier_curve (data, width, height, stride, first, c0, midc); draw_bezier_curve (data, width, height, stride, second, midc, c3); } else { double xu[4], yu[4]; int ushift = sqsteps2shift (steps_sq), k; fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu); fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu); for (k = 0; k < ushift; ++k) { fd_down (xu); fd_down (yu); } rasterize_bezier_curve (data, width, height, stride, ushift, xu, yu, _cairo_color_double_to_short (c0[0]), _cairo_color_double_to_short (c0[1]), _cairo_color_double_to_short (c0[2]), _cairo_color_double_to_short (c0[3]), _cairo_color_double_to_short (c3[0]), _cairo_color_double_to_short (c3[1]), _cairo_color_double_to_short (c3[2]), _cairo_color_double_to_short (c3[3])); /* Draw the end point, to make sure that we didn't leave it * out because of rounding */ draw_pixel (data, width, height, stride, _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)), _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)), _cairo_color_double_to_short (c3[0]), _cairo_color_double_to_short (c3[1]), _cairo_color_double_to_short (c3[2]), _cairo_color_double_to_short (c3[3])); } } /* * Forward-rasterize a cubic Bezier patch using forward differences. * * Input: data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * vshift is log2(n) if n is the number of desired steps * p[i][j], p[i][j] are the the nodes of the Bezier patch * col[i][j] is the j-th color component of the i-th corner * * Output: data will be changed to have the requested patch drawn in * the specified colors * * The nodes of the patch are as follows: * * u\v 0 - > 1 * 0 p00 p01 p02 p03 * | p10 p11 p12 p13 * v p20 p21 p22 p23 * 1 p30 p31 p32 p33 * * i.e. u varies along the first component (rows), v varies along the * second one (columns). * * The color components are red, green, blue and alpha, in this order. * c[0..3] are the colors in p00, p30, p03, p33 respectively * * The input color components are not premultiplied, but the data * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, * premultiplied). * * If the patch folds over itself, the part with the highest v * parameter is considered above. If both have the same v, the one * with the highest u parameter is above. * * The function draws n+1 curves, that is from the curve at step 0 to * the curve at step n, both included. This is the discrete equivalent * to drawing the patch for values of the interpolation parameter in * [0,1] (including both extremes). */ static inline void rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift, cairo_point_double_t p[4][4], double col[4][4]) { double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4]; int vsteps, v, i, k; vsteps = 1 << vshift; /* * pv[i][0] is the function (represented using forward * differences) mapping v to the x coordinate of the i-th node of * the Bezier curve with parameter u. * (Likewise p[i][0] gives the y coordinate). * * This means that (pv[0][0][0],pv[0][1][0]), * (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and * (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for * the "current" v value (see the FD comments for more details). */ for (i = 0; i < 4; ++i) { fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]); fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]); for (k = 0; k < vshift; ++k) { fd_down (pv[i][0]); fd_down (pv[i][1]); } } for (i = 0; i < 4; ++i) { cstart[i] = col[0][i]; cend[i] = col[1][i]; dcstart[i] = (col[2][i] - col[0][i]) / vsteps; dcend[i] = (col[3][i] - col[1][i]) / vsteps; } for (v = 0; v <= vsteps; ++v) { cairo_point_double_t nodes[4]; for (i = 0; i < 4; ++i) { nodes[i].x = pv[i][0][0]; nodes[i].y = pv[i][1][0]; } draw_bezier_curve (data, width, height, stride, nodes, cstart, cend); for (i = 0; i < 4; ++i) { fd_fwd (pv[i][0]); fd_fwd (pv[i][1]); cstart[i] += dcstart[i]; cend[i] += dcend[i]; } } } /* * Clip, split and rasterize a Bezier cubic patch. * * Input: data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * p[i][j], p[i][j] are the nodes of the patch * col[i][j] is the j-th color component of the i-th corner * * Output: data will be changed to have the requested patch drawn in * the specified colors * * The nodes of the patch are as follows: * * u\v 0 - > 1 * 0 p00 p01 p02 p03 * | p10 p11 p12 p13 * v p20 p21 p22 p23 * 1 p30 p31 p32 p33 * * i.e. u varies along the first component (rows), v varies along the * second one (columns). * * The color components are red, green, blue and alpha, in this order. * c[0..3] are the colors in p00, p30, p03, p33 respectively * * The input color components are not premultiplied, but the data * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, * premultiplied). * * If the patch folds over itself, the part with the highest v * parameter is considered above. If both have the same v, the one * with the highest u parameter is above. * * The function guarantees that it will draw the patch with a step * small enough to never have a distance above 1/sqrt(2) between two * adjacent points (which guarantees that no hole can appear). * * This function can be used to rasterize a tile of PDF type 7 * shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html). */ static void draw_bezier_patch (unsigned char *data, int width, int height, int stride, cairo_point_double_t p[4][4], double c[4][4]) { double top, bottom, left, right, steps_sq; int i, j, v; top = bottom = p[0][0].y; for (i = 0; i < 4; ++i) { for (j= 0; j < 4; ++j) { top = MIN (top, p[i][j].y); bottom = MAX (bottom, p[i][j].y); } } v = intersect_interval (top, bottom, 0, height); if (v == OUTSIDE) return; left = right = p[0][0].x; for (i = 0; i < 4; ++i) { for (j= 0; j < 4; ++j) { left = MIN (left, p[i][j].x); right = MAX (right, p[i][j].x); } } v &= intersect_interval (left, right, 0, width); if (v == OUTSIDE) return; steps_sq = 0; for (i = 0; i < 4; ++i) steps_sq = MAX (steps_sq, bezier_steps_sq (p[i])); if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) { /* The number of steps is greater than the threshold. This * means that either the error would become too big if we * directly rasterized it or that we can probably save some * time by splitting the curve and clipping part of it. The * patch is only split in the v direction to guarantee that * rasterizing each part will overwrite parts with low v with * overlapping parts with higher v. */ cairo_point_double_t first[4][4], second[4][4]; double subc[4][4]; for (i = 0; i < 4; ++i) split_bezier (p[i], first[i], second[i]); for (i = 0; i < 4; ++i) { subc[0][i] = c[0][i]; subc[1][i] = c[1][i]; subc[2][i] = 0.5 * (c[0][i] + c[2][i]); subc[3][i] = 0.5 * (c[1][i] + c[3][i]); } draw_bezier_patch (data, width, height, stride, first, subc); for (i = 0; i < 4; ++i) { subc[0][i] = subc[2][i]; subc[1][i] = subc[3][i]; subc[2][i] = c[2][i]; subc[3][i] = c[3][i]; } draw_bezier_patch (data, width, height, stride, second, subc); } else { rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c); } } /* * Draw a tensor product shading pattern. * * Input: mesh is the mesh pattern * data is the base pointer of the image * width, height are the dimensions of the image * stride is the stride in bytes between adjacent rows * * Output: data will be changed to have the pattern drawn on it * * data is assumed to be clear and its content is assumed to be in * CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied). * * This function can be used to rasterize a PDF type 7 shading (see * http://www.adobe.com/devnet/pdf/pdf_reference.html). */ void _cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh, void *data, int width, int height, int stride, double x_offset, double y_offset) { cairo_point_double_t nodes[4][4]; double colors[4][4]; cairo_matrix_t p2u; unsigned int i, j, k, n; cairo_status_t status; const cairo_mesh_patch_t *patch; const cairo_color_t *c; assert (mesh->base.status == CAIRO_STATUS_SUCCESS); assert (mesh->current_patch == NULL); p2u = mesh->base.matrix; status = cairo_matrix_invert (&p2u); assert (status == CAIRO_STATUS_SUCCESS); n = _cairo_array_num_elements (&mesh->patches); patch = _cairo_array_index_const (&mesh->patches, 0); for (i = 0; i < n; i++) { for (j = 0; j < 4; j++) { for (k = 0; k < 4; k++) { nodes[j][k] = patch->points[j][k]; cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y); nodes[j][k].x += x_offset; nodes[j][k].y += y_offset; } } c = &patch->colors[0]; colors[0][0] = c->red; colors[0][1] = c->green; colors[0][2] = c->blue; colors[0][3] = c->alpha; c = &patch->colors[3]; colors[1][0] = c->red; colors[1][1] = c->green; colors[1][2] = c->blue; colors[1][3] = c->alpha; c = &patch->colors[1]; colors[2][0] = c->red; colors[2][1] = c->green; colors[2][2] = c->blue; colors[2][3] = c->alpha; c = &patch->colors[2]; colors[3][0] = c->red; colors[3][1] = c->green; colors[3][2] = c->blue; colors[3][3] = c->alpha; draw_bezier_patch (data, width, height, stride, nodes, colors); patch++; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-misc.c������������������������������������0000664�0000000�0000000�00000074400�12710376503�0024477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #include "cairo-error-private.h" COMPILE_TIME_ASSERT ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED); COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); /** * SECTION:cairo-status * @Title: Error handling * @Short_Description: Decoding cairo's status * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(), * cairo_font_face_status(), cairo_scaled_font_status(), * cairo_region_status() * * Cairo uses a single status type to represent all kinds of errors. A status * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value * of zero. All other status values represent an error. * * Cairo's error handling is designed to be easy to use and safe. All major * cairo objects <firstterm>retain</firstterm> an error status internally which * can be queried anytime by the users using cairo*_status() calls. In * the mean time, it is safe to call all cairo functions normally even if the * underlying object is in an error status. This means that no error handling * code is required before or after each individual cairo function call. **/ /* Public stuff */ /** * cairo_status_to_string: * @status: a cairo status * * Provides a human-readable description of a #cairo_status_t. * * Returns: a string representation of the status * * Since: 1.0 **/ const char * cairo_status_to_string (cairo_status_t status) { switch (status) { case CAIRO_STATUS_SUCCESS: return "no error has occurred"; case CAIRO_STATUS_NO_MEMORY: return "out of memory"; case CAIRO_STATUS_INVALID_RESTORE: return "cairo_restore() without matching cairo_save()"; case CAIRO_STATUS_INVALID_POP_GROUP: return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()"; case CAIRO_STATUS_NO_CURRENT_POINT: return "no current point defined"; case CAIRO_STATUS_INVALID_MATRIX: return "invalid matrix (not invertible)"; case CAIRO_STATUS_INVALID_STATUS: return "invalid value for an input cairo_status_t"; case CAIRO_STATUS_NULL_POINTER: return "NULL pointer"; case CAIRO_STATUS_INVALID_STRING: return "input string not valid UTF-8"; case CAIRO_STATUS_INVALID_PATH_DATA: return "input path data not valid"; case CAIRO_STATUS_READ_ERROR: return "error while reading from input stream"; case CAIRO_STATUS_WRITE_ERROR: return "error while writing to output stream"; case CAIRO_STATUS_SURFACE_FINISHED: return "the target surface has been finished"; case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: return "the surface type is not appropriate for the operation"; case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: return "the pattern type is not appropriate for the operation"; case CAIRO_STATUS_INVALID_CONTENT: return "invalid value for an input cairo_content_t"; case CAIRO_STATUS_INVALID_FORMAT: return "invalid value for an input cairo_format_t"; case CAIRO_STATUS_INVALID_VISUAL: return "invalid value for an input Visual*"; case CAIRO_STATUS_FILE_NOT_FOUND: return "file not found"; case CAIRO_STATUS_INVALID_DASH: return "invalid value for a dash setting"; case CAIRO_STATUS_INVALID_DSC_COMMENT: return "invalid value for a DSC comment"; case CAIRO_STATUS_INVALID_INDEX: return "invalid index passed to getter"; case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: return "clip region not representable in desired format"; case CAIRO_STATUS_TEMP_FILE_ERROR: return "error creating or writing to a temporary file"; case CAIRO_STATUS_INVALID_STRIDE: return "invalid value for stride"; case CAIRO_STATUS_FONT_TYPE_MISMATCH: return "the font type is not appropriate for the operation"; case CAIRO_STATUS_USER_FONT_IMMUTABLE: return "the user-font is immutable"; case CAIRO_STATUS_USER_FONT_ERROR: return "error occurred in a user-font callback function"; case CAIRO_STATUS_NEGATIVE_COUNT: return "negative number used where it is not allowed"; case CAIRO_STATUS_INVALID_CLUSTERS: return "input clusters do not represent the accompanying text and glyph arrays"; case CAIRO_STATUS_INVALID_SLANT: return "invalid value for an input cairo_font_slant_t"; case CAIRO_STATUS_INVALID_WEIGHT: return "invalid value for an input cairo_font_weight_t"; case CAIRO_STATUS_INVALID_SIZE: return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)"; case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: return "user-font method not implemented"; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: return "the device type is not appropriate for the operation"; case CAIRO_STATUS_DEVICE_ERROR: return "an operation to the device caused an unspecified error"; case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: return "invalid operation during mesh pattern construction"; case CAIRO_STATUS_DEVICE_FINISHED: return "the target device has been finished"; default: case CAIRO_STATUS_LAST_STATUS: return "<unknown error status>"; } } /** * cairo_glyph_allocate: * @num_glyphs: number of glyphs to allocate * * Allocates an array of #cairo_glyph_t's. * This function is only useful in implementations of * #cairo_user_scaled_font_text_to_glyphs_func_t where the user * needs to allocate an array of glyphs that cairo will free. * For all other uses, user can use their own allocation method * for glyphs. * * This function returns %NULL if @num_glyphs is not positive, * or if out of memory. That means, the %NULL return value * signals out-of-memory only if @num_glyphs was positive. * * Returns: the newly allocated array of glyphs that should be * freed using cairo_glyph_free() * * Since: 1.8 **/ cairo_glyph_t * cairo_glyph_allocate (int num_glyphs) { if (num_glyphs <= 0) return NULL; return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); } slim_hidden_def (cairo_glyph_allocate); /** * cairo_glyph_free: * @glyphs: array of glyphs to free, or %NULL * * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate(). * This function is only useful to free glyph array returned * by cairo_scaled_font_text_to_glyphs() where cairo returns * an array of glyphs that the user will free. * For all other uses, user can use their own allocation method * for glyphs. * * Since: 1.8 **/ void cairo_glyph_free (cairo_glyph_t *glyphs) { free (glyphs); } slim_hidden_def (cairo_glyph_free); /** * cairo_text_cluster_allocate: * @num_clusters: number of text_clusters to allocate * * Allocates an array of #cairo_text_cluster_t's. * This function is only useful in implementations of * #cairo_user_scaled_font_text_to_glyphs_func_t where the user * needs to allocate an array of text clusters that cairo will free. * For all other uses, user can use their own allocation method * for text clusters. * * This function returns %NULL if @num_clusters is not positive, * or if out of memory. That means, the %NULL return value * signals out-of-memory only if @num_clusters was positive. * * Returns: the newly allocated array of text clusters that should be * freed using cairo_text_cluster_free() * * Since: 1.8 **/ cairo_text_cluster_t * cairo_text_cluster_allocate (int num_clusters) { if (num_clusters <= 0) return NULL; return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); } slim_hidden_def (cairo_text_cluster_allocate); /** * cairo_text_cluster_free: * @clusters: array of text clusters to free, or %NULL * * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate(). * This function is only useful to free text cluster array returned * by cairo_scaled_font_text_to_glyphs() where cairo returns * an array of text clusters that the user will free. * For all other uses, user can use their own allocation method * for text clusters. * * Since: 1.8 **/ void cairo_text_cluster_free (cairo_text_cluster_t *clusters) { free (clusters); } slim_hidden_def (cairo_text_cluster_free); /* Private stuff */ /** * _cairo_validate_text_clusters: * @utf8: UTF-8 text * @utf8_len: length of @utf8 in bytes * @glyphs: array of glyphs * @num_glyphs: number of glyphs * @clusters: array of cluster mapping information * @num_clusters: number of clusters in the mapping * @cluster_flags: cluster flags * * Check that clusters cover the entire glyphs and utf8 arrays, * and that cluster boundaries are UTF-8 boundaries. * * Return value: %CAIRO_STATUS_SUCCESS upon success, or * %CAIRO_STATUS_INVALID_CLUSTERS on error. * The error is either invalid UTF-8 input, * or bad cluster mapping. **/ cairo_status_t _cairo_validate_text_clusters (const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags) { cairo_status_t status; unsigned int n_bytes = 0; unsigned int n_glyphs = 0; int i; for (i = 0; i < num_clusters; i++) { int cluster_bytes = clusters[i].num_bytes; int cluster_glyphs = clusters[i].num_glyphs; if (cluster_bytes < 0 || cluster_glyphs < 0) goto BAD; /* A cluster should cover at least one character or glyph. * I can't see any use for a 0,0 cluster. * I can't see an immediate use for a zero-text cluster * right now either, but they don't harm. * Zero-glyph clusters on the other hand are useful for * things like U+200C ZERO WIDTH NON-JOINER */ if (cluster_bytes == 0 && cluster_glyphs == 0) goto BAD; /* Since n_bytes and n_glyphs are unsigned, but the rest of * values involved are signed, we can detect overflow easily */ if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs) goto BAD; /* Make sure we've got valid UTF-8 for the cluster */ status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL); if (unlikely (status)) return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); n_bytes += cluster_bytes ; n_glyphs += cluster_glyphs; } if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) { BAD: return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); } return CAIRO_STATUS_SUCCESS; } /** * _cairo_operator_bounded_by_mask: * @op: a #cairo_operator_t * * A bounded operator is one where mask pixel * of zero results in no effect on the destination image. * * Unbounded operators often require special handling; if you, for * example, draw trapezoids with an unbounded operator, the effect * extends past the bounding box of the trapezoids. * * Return value: %TRUE if the operator is bounded by the mask operand **/ cairo_bool_t _cairo_operator_bounded_by_mask (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return TRUE; case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_ATOP: return FALSE; } ASSERT_NOT_REACHED; return FALSE; } /** * _cairo_operator_bounded_by_source: * @op: a #cairo_operator_t * * A bounded operator is one where source pixels of zero * (in all four components, r, g, b and a) effect no change * in the resulting destination image. * * Unbounded operators often require special handling; if you, for * example, copy a surface with the SOURCE operator, the effect * extends past the bounding box of the source surface. * * Return value: %TRUE if the operator is bounded by the source operand **/ cairo_bool_t _cairo_operator_bounded_by_source (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return TRUE; case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_ATOP: return FALSE; } ASSERT_NOT_REACHED; return FALSE; } uint32_t _cairo_operator_bounded_by_either (cairo_operator_t op) { switch (op) { default: ASSERT_NOT_REACHED; case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE; case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: return CAIRO_OPERATOR_BOUND_BY_MASK; case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_ATOP: return 0; } } #if DISABLE_SOME_FLOATING_POINT /* This function is identical to the C99 function lround(), except that it * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and * has a valid input range of (INT_MIN, INT_MAX] instead of * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems * than other commonly used methods for rounding (lround, round, rint, lrint * or float (d + 0.5)). * * The reason why this function is much faster on x86 than other * methods is due to the fact that it avoids the fldcw instruction. * This instruction incurs a large performance penalty on modern Intel * processors due to how it prevents efficient instruction pipelining. * * The reason why this function is much faster on FPU-less systems is for * an entirely different reason. All common rounding methods involve multiple * floating-point operations. Each one of these operations has to be * emulated in software, which adds up to be a large performance penalty. * This function doesn't perform any floating-point calculations, and thus * avoids this penalty. */ int _cairo_lround (double d) { uint32_t top, shift_amount, output; union { double d; uint64_t ui64; uint32_t ui32[2]; } u; u.d = d; /* If the integer word order doesn't match the float word order, we swap * the words of the input double. This is needed because we will be * treating the whole double as a 64-bit unsigned integer. Notice that we * use WORDS_BIGENDIAN to detect the integer word order, which isn't * exactly correct because WORDS_BIGENDIAN refers to byte order, not word * order. Thus, we are making the assumption that the byte order is the * same as the integer word order which, on the modern machines that we * care about, is OK. */ #if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \ (!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN)) { uint32_t temp = u.ui32[0]; u.ui32[0] = u.ui32[1]; u.ui32[1] = temp; } #endif #ifdef WORDS_BIGENDIAN #define MSW (0) /* Most Significant Word */ #define LSW (1) /* Least Significant Word */ #else #define MSW (1) #define LSW (0) #endif /* By shifting the most significant word of the input double to the * right 20 places, we get the very "top" of the double where the exponent * and sign bit lie. */ top = u.ui32[MSW] >> 20; /* Here, we calculate how much we have to shift the mantissa to normalize * it to an integer value. We extract the exponent "top" by masking out the * sign bit, then we calculate the shift amount by subtracting the exponent * from the bias. Notice that the correct bias for 64-bit doubles is * actually 1075, but we use 1053 instead for two reasons: * * 1) To perform rounding later on, we will first need the target * value in a 31.1 fixed-point format. Thus, the bias needs to be one * less: (1075 - 1: 1074). * * 2) To avoid shifting the mantissa as a full 64-bit integer (which is * costly on certain architectures), we break the shift into two parts. * First, the upper and lower parts of the mantissa are shifted * individually by a constant amount that all valid inputs will require * at the very least. This amount is chosen to be 21, because this will * allow the two parts of the mantissa to later be combined into a * single 32-bit representation, on which the remainder of the shift * will be performed. Thus, we decrease the bias by an additional 21: * (1074 - 21: 1053). */ shift_amount = 1053 - (top & 0x7FF); /* We are done with the exponent portion in "top", so here we shift it off * the end. */ top >>= 11; /* Before we perform any operations on the mantissa, we need to OR in * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask * off the sign bit nor the exponent bits because these higher bits won't * make a bit of difference in the rest of our calculations. */ u.ui32[MSW] |= 0x100000; /* If the input double is negative, we have to decrease the mantissa * by a hair. This is an important part of performing arithmetic rounding, * as negative numbers must round towards positive infinity in the * halfwase case of -x.5. Since "top" contains only the sign bit at this * point, we can just decrease the mantissa by the value of "top". */ u.ui64 -= top; /* By decrementing "top", we create a bitmask with a value of either * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive * and thus the unsigned subtraction underflowed) that we'll use later. */ top--; /* Here, we shift the mantissa by the constant value as described above. * We can emulate a 64-bit shift right by 21 through shifting the top 32 * bits left 11 places and ORing in the bottom 32 bits shifted 21 places * to the right. Both parts of the mantissa are now packed into a single * 32-bit integer. Although we severely truncate the lower part in the * process, we still have enough significant bits to perform the conversion * without error (for all valid inputs). */ output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21); /* Next, we perform the shift that converts the X.Y fixed-point number * currently found in "output" to the desired 31.1 fixed-point format * needed for the following rounding step. It is important to consider * all possible values for "shift_amount" at this point: * * - {shift_amount < 0} Since shift_amount is an unsigned integer, it * really can't have a value less than zero. But, if the shift_amount * calculation above caused underflow (which would happen with * input > INT_MAX or input <= INT_MIN) then shift_amount will now be * a very large number, and so this shift will result in complete * garbage. But that's OK, as the input was out of our range, so our * output is undefined. * * - {shift_amount > 31} If the magnitude of the input was very small * (i.e. |input| << 1.0), shift_amount will have a value greater than * 31. Thus, this shift will also result in garbage. After performing * the shift, we will zero-out "output" if this is the case. * * - {0 <= shift_amount < 32} In this case, the shift will properly convert * the mantissa into a 31.1 fixed-point number. */ output >>= shift_amount; /* This is where we perform rounding with the 31.1 fixed-point number. * Since what we're after is arithmetic rounding, we simply add the single * fractional bit into the integer part of "output", and just keep the * integer part. */ output = (output >> 1) + (output & 1); /* Here, we zero-out the result if the magnitude if the input was very small * (as explained in the section above). Notice that all input out of the * valid range is also caught by this condition, which means we produce 0 * for all invalid input, which is a nice side effect. * * The most straightforward way to do this would be: * * if (shift_amount > 31) * output = 0; * * But we can use a little trick to avoid the potential branch. The * expression (shift_amount > 31) will be either 1 or 0, which when * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow), * which can be used to conditionally mask away all the bits in "output" * (in the 0x0 case), effectively zeroing it out. Certain, compilers would * have done this for us automatically. */ output &= ((shift_amount > 31) - 1); /* If the input double was a negative number, then we have to negate our * output. The most straightforward way to do this would be: * * if (!top) * output = -output; * * as "top" at this point is either 0x0 (if the input was negative) or * 0xFFFFFFFF (if the input was positive). But, we can use a trick to * avoid the branch. Observe that the following snippet of code has the * same effect as the reference snippet above: * * if (!top) * output = 0 - output; * else * output = output - 0; * * Armed with the bitmask found in "top", we can condense the two statements * into the following: * * output = (output & top) - (output & ~top); * * where, in the case that the input double was negative, "top" will be 0, * and the statement will be equivalent to: * * output = (0) - (output); * * and if the input double was positive, "top" will be 0xFFFFFFFF, and the * statement will be equivalent to: * * output = (output) - (0); * * Which, as pointed out earlier, is equivalent to the original reference * snippet. */ output = (output & top) - (output & ~top); return output; #undef MSW #undef LSW } #endif /* Convert a 32-bit IEEE single precision floating point number to a * 'half' representation (s10.5) */ uint16_t _cairo_half_from_float (float f) { union { uint32_t ui; float f; } u; int s, e, m; u.f = f; s = (u.ui >> 16) & 0x00008000; e = ((u.ui >> 23) & 0x000000ff) - (127 - 15); m = u.ui & 0x007fffff; if (e <= 0) { if (e < -10) { /* underflow */ return 0; } m = (m | 0x00800000) >> (1 - e); /* round to nearest, round 0.5 up. */ if (m & 0x00001000) m += 0x00002000; return s | (m >> 13); } else if (e == 0xff - (127 - 15)) { if (m == 0) { /* infinity */ return s | 0x7c00; } else { /* nan */ m >>= 13; return s | 0x7c00 | m | (m == 0); } } else { /* round to nearest, round 0.5 up. */ if (m & 0x00001000) { m += 0x00002000; if (m & 0x00800000) { m = 0; e += 1; } } if (e > 30) { /* overflow -> infinity */ return s | 0x7c00; } return s | (e << 10) | (m >> 13); } } #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include <windows.h> #include <io.h> #if !_WIN32_WCE /* tmpfile() replacement for Windows. * * On Windows tmpfile() creates the file in the root directory. This * may fail due to unsufficient privileges. However, this isn't a * problem on Windows CE so we don't use it there. */ FILE * _cairo_win32_tmpfile (void) { DWORD path_len; WCHAR path_name[MAX_PATH + 1]; WCHAR file_name[MAX_PATH + 1]; HANDLE handle; int fd; FILE *fp; path_len = GetTempPathW (MAX_PATH, path_name); if (path_len <= 0 || path_len >= MAX_PATH) return NULL; if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0) return NULL; handle = CreateFileW (file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (handle == INVALID_HANDLE_VALUE) { DeleteFileW (file_name); return NULL; } fd = _open_osfhandle((intptr_t) handle, 0); if (fd < 0) { CloseHandle (handle); return NULL; } fp = fdopen(fd, "w+b"); if (fp == NULL) { _close(fd); return NULL; } return fp; } #endif /* !_WIN32_WCE */ #endif /* _WIN32 */ typedef struct _cairo_intern_string { cairo_hash_entry_t hash_entry; int len; char *string; } cairo_intern_string_t; static cairo_hash_table_t *_cairo_intern_string_ht; static unsigned long _intern_string_hash (const char *str, int len) { const signed char *p = (const signed char *) str; unsigned int h = *p; for (p += 1; --len; p++) h = (h << 5) - h + *p; return h; } static cairo_bool_t _intern_string_equal (const void *_a, const void *_b) { const cairo_intern_string_t *a = _a; const cairo_intern_string_t *b = _b; if (a->len != b->len) return FALSE; return memcmp (a->string, b->string, a->len) == 0; } cairo_status_t _cairo_intern_string (const char **str_inout, int len) { char *str = (char *) *str_inout; cairo_intern_string_t tmpl, *istring; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (len < 0) len = strlen (str); tmpl.hash_entry.hash = _intern_string_hash (str, len); tmpl.len = len; tmpl.string = (char *) str; CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); if (_cairo_intern_string_ht == NULL) { _cairo_intern_string_ht = _cairo_hash_table_create (_intern_string_equal); if (unlikely (_cairo_intern_string_ht == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } } istring = _cairo_hash_table_lookup (_cairo_intern_string_ht, &tmpl.hash_entry); if (istring == NULL) { istring = malloc (sizeof (cairo_intern_string_t) + len + 1); if (likely (istring != NULL)) { istring->hash_entry.hash = tmpl.hash_entry.hash; istring->len = tmpl.len; istring->string = (char *) (istring + 1); memcpy (istring->string, str, len); istring->string[len] = '\0'; status = _cairo_hash_table_insert (_cairo_intern_string_ht, &istring->hash_entry); if (unlikely (status)) { free (istring); goto BAIL; } } else { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } } *str_inout = istring->string; BAIL: CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); return status; } static void _intern_string_pluck (void *entry, void *closure) { _cairo_hash_table_remove (closure, entry); free (entry); } void _cairo_intern_string_reset_static_data (void) { CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); if (_cairo_intern_string_ht != NULL) { _cairo_hash_table_foreach (_cairo_intern_string_ht, _intern_string_pluck, _cairo_intern_string_ht); _cairo_hash_table_destroy(_cairo_intern_string_ht); _cairo_intern_string_ht = NULL; } CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mono-scan-converter.c���������������������0000664�0000000�0000000�00000034206�12710376503�0027443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* * Copyright (c) 2011 Intel Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "cairoint.h" #include "cairo-spans-private.h" #include "cairo-error-private.h" #include <stdlib.h> #include <string.h> #include <limits.h> struct quorem { int32_t quo; int32_t rem; }; struct edge { struct edge *next, *prev; int32_t height_left; int32_t dir; int32_t vertical; int32_t dy; struct quorem x; struct quorem dxdy; }; /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { /* The vertical clip extents. */ int32_t ymin, ymax; int num_edges; struct edge *edges; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64]; struct edge edges_embedded[32]; }; struct mono_scan_converter { struct polygon polygon[1]; /* Leftmost edge on the current scan line. */ struct edge head, tail; int is_vertical; cairo_half_open_span_t *spans; cairo_half_open_span_t spans_embedded[64]; int num_spans; /* Clip box. */ int32_t xmin, xmax; int32_t ymin, ymax; }; #define I(x) _cairo_fixed_integer_round_down(x) /* Compute the floored division a/b. Assumes / and % perform symmetric * division. */ inline static struct quorem floored_divrem(int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } /* Compute the floored division (x*a)/b. Assumes / and % perform symmetric * division. */ static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } static cairo_status_t polygon_init (struct polygon *polygon, int ymin, int ymax) { unsigned h = ymax - ymin + 1; polygon->y_buckets = polygon->y_buckets_embedded; if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (polygon->y_buckets, 0, h * sizeof (struct edge *)); polygon->y_buckets[h-1] = (void *)-1; polygon->ymin = ymin; polygon->ymax = ymax; return CAIRO_STATUS_SUCCESS; } static void polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); if (polygon->edges != polygon->edges_embedded) free (polygon->edges); } static void _polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e, int y) { struct edge **ptail = &polygon->y_buckets[y - polygon->ymin]; if (*ptail) (*ptail)->prev = e; e->next = *ptail; e->prev = NULL; *ptail = e; } inline static void polygon_add_edge (struct polygon *polygon, const cairo_edge_t *edge) { struct edge *e; cairo_fixed_t dx; cairo_fixed_t dy; int y, ytop, ybot; int ymin = polygon->ymin; int ymax = polygon->ymax; y = I(edge->top); ytop = MAX(y, ymin); y = I(edge->bottom); ybot = MIN(y, ymax); if (ybot <= ytop) return; e = polygon->edges + polygon->num_edges++; e->height_left = ybot - ytop; e->dir = edge->dir; dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; e->dy = 0; } else { e->vertical = FALSE; e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy); e->dy = dy; e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } e->x.rem -= dy; _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop); } static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { struct edge *head, **next, *prev; int32_t x; prev = head_a->prev; next = &head; if (head_a->x.quo <= head_b->x.quo) { head = head_a; } else { head = head_b; head_b->prev = prev; goto start_with_b; } do { x = head_b->x.quo; while (head_a != NULL && head_a->x.quo <= x) { prev = head_a; next = &head_a->next; head_a = head_a->next; } head_b->prev = prev; *next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x.quo; while (head_b != NULL && head_b->x.quo <= x) { prev = head_b; next = &head_b->next; head_b = head_b->next; } head_a->prev = prev; *next = head_a; if (head_b == NULL) return head; } while (1); } static struct edge * sort_edges (struct edge *list, unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; unsigned int i; head_other = list->next; if (head_other == NULL) { *head_out = list; return NULL; } remaining = head_other->next; if (list->x.quo <= head_other->x.quo) { *head_out = list; head_other->next = NULL; } else { *head_out = head_other; head_other->prev = list->prev; head_other->next = list; list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } return remaining; } static struct edge * merge_unsorted_edges (struct edge *head, struct edge *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); return merge_sorted_edges (head, unsorted); } inline static void active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges) { struct edge *e; for (e = edges; c->is_vertical && e; e = e->next) c->is_vertical = e->vertical; c->head.next = merge_unsorted_edges (c->head.next, edges); } inline static void add_span (struct mono_scan_converter *c, int x1, int x2) { int n; if (x1 < c->xmin) x1 = c->xmin; if (x2 > c->xmax) x2 = c->xmax; if (x2 <= x1) return; n = c->num_spans++; c->spans[n].x = x1; c->spans[n].coverage = 255; n = c->num_spans++; c->spans[n].x = x2; c->spans[n].coverage = 0; } inline static void row (struct mono_scan_converter *c, unsigned int mask) { struct edge *edge = c->head.next; int xstart = INT_MIN, prev_x = INT_MIN; int winding = 0; c->num_spans = 0; while (&c->tail != edge) { struct edge *next = edge->next; int xend = I(edge->x.quo); if (--edge->height_left) { if (!edge->vertical) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } } if (edge->x.quo < prev_x) { struct edge *pos = edge->prev; pos->next = next; next->prev = pos; do { pos = pos->prev; } while (edge->x.quo < pos->x.quo); pos->next->prev = edge; edge->next = pos->next; edge->prev = pos; pos->next = edge; } else prev_x = edge->x.quo; } else { edge->prev->next = next; next->prev = edge->prev; } winding += edge->dir; if ((winding & mask) == 0) { if (I(next->x.quo) > xend + 1) { add_span (c, xstart, xend); xstart = INT_MIN; } } else if (xstart == INT_MIN) xstart = xend; edge = next; } } inline static void dec (struct edge *e, int h) { e->height_left -= h; if (e->height_left == 0) { e->prev->next = e->next; e->next->prev = e->prev; } } static cairo_status_t _mono_scan_converter_init(struct mono_scan_converter *c, int xmin, int ymin, int xmax, int ymax) { cairo_status_t status; int max_num_spans; status = polygon_init (c->polygon, ymin, ymax); if (unlikely (status)) return status; max_num_spans = xmax - xmin + 1; if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) { c->spans = _cairo_malloc_ab (max_num_spans, sizeof (cairo_half_open_span_t)); if (unlikely (c->spans == NULL)) { polygon_fini (c->polygon); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } else c->spans = c->spans_embedded; c->xmin = xmin; c->xmax = xmax; c->ymin = ymin; c->ymax = ymax; c->head.vertical = 1; c->head.height_left = INT_MAX; c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN)); c->head.prev = NULL; c->head.next = &c->tail; c->tail.prev = &c->head; c->tail.next = NULL; c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX)); c->tail.height_left = INT_MAX; c->tail.vertical = 1; c->is_vertical = 1; return CAIRO_STATUS_SUCCESS; } static void _mono_scan_converter_fini(struct mono_scan_converter *self) { if (self->spans != self->spans_embedded) free (self->spans); polygon_fini(self->polygon); } static cairo_status_t mono_scan_converter_allocate_edges(struct mono_scan_converter *c, int num_edges) { c->polygon->num_edges = 0; c->polygon->edges = c->polygon->edges_embedded; if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) { c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge)); if (unlikely (c->polygon->edges == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } return CAIRO_STATUS_SUCCESS; } static void mono_scan_converter_add_edge (struct mono_scan_converter *c, const cairo_edge_t *edge) { polygon_add_edge (c->polygon, edge); } static void step_edges (struct mono_scan_converter *c, int count) { struct edge *edge; for (edge = c->head.next; edge != &c->tail; edge = edge->next) { edge->height_left -= count; if (! edge->height_left) { edge->prev->next = edge->next; edge->next->prev = edge->prev; } } } static cairo_status_t mono_scan_converter_render(struct mono_scan_converter *c, unsigned int winding_mask, cairo_span_renderer_t *renderer) { struct polygon *polygon = c->polygon; int i, j, h = c->ymax - c->ymin; cairo_status_t status; for (i = 0; i < h; i = j) { j = i + 1; if (polygon->y_buckets[i]) active_list_merge_edges (c, polygon->y_buckets[i]); if (c->is_vertical) { int min_height; struct edge *e; e = c->head.next; min_height = e->height_left; while (e != &c->tail) { if (e->height_left < min_height) min_height = e->height_left; e = e->next; } while (--min_height >= 1 && polygon->y_buckets[j] == NULL) j++; if (j != i + 1) step_edges (c, j - (i + 1)); } row (c, winding_mask); if (c->num_spans) { status = renderer->render_rows (renderer, c->ymin+i, j-i, c->spans, c->num_spans); if (unlikely (status)) return status; } /* XXX recompute after dropping edges? */ if (c->head.next == &c->tail) c->is_vertical = 1; } return CAIRO_STATUS_SUCCESS; } struct _cairo_mono_scan_converter { cairo_scan_converter_t base; struct mono_scan_converter converter[1]; cairo_fill_rule_t fill_rule; }; typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t; static void _cairo_mono_scan_converter_destroy (void *converter) { cairo_mono_scan_converter_t *self = converter; _mono_scan_converter_fini (self->converter); free(self); } cairo_status_t _cairo_mono_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon) { cairo_mono_scan_converter_t *self = converter; cairo_status_t status; int i; #if 0 FILE *file = fopen ("polygon.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); #endif status = mono_scan_converter_allocate_edges (self->converter, polygon->num_edges); if (unlikely (status)) return status; for (i = 0; i < polygon->num_edges; i++) mono_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_mono_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_mono_scan_converter_t *self = converter; return mono_scan_converter_render (self->converter, self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, renderer); } cairo_scan_converter_t * _cairo_mono_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule) { cairo_mono_scan_converter_t *self; cairo_status_t status; self = malloc (sizeof(struct _cairo_mono_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_mono_scan_converter_destroy; self->base.generate = _cairo_mono_scan_converter_generate; status = _mono_scan_converter_init (self->converter, xmin, ymin, xmax, ymax); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; return &self->base; bail: self->base.destroy(&self->base); bail_nomem: return _cairo_scan_converter_create_in_error (status); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mutex-impl-private.h����������������������0000664�0000000�0000000�00000024540�12710376503�0027322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005,2007 Red Hat, Inc. * Copyright © 2007 Mathias Hasselmann * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Mathias Hasselmann <mathias.hasselmann@gmx.de> * Behdad Esfahbod <behdad@behdad.org> */ #ifndef CAIRO_MUTEX_IMPL_PRIVATE_H #define CAIRO_MUTEX_IMPL_PRIVATE_H #include "cairo.h" #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_LOCKDEP #include <lockdep.h> #endif /* A fully qualified no-operation statement */ #define CAIRO_MUTEX_IMPL_NOOP do {/*no-op*/} while (0) /* And one that evaluates its argument once */ #define CAIRO_MUTEX_IMPL_NOOP1(expr) do { (void)(expr); } while (0) /* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the * result of __attribute__((warn_used_result)) functions. */ /* Cairo mutex implementation: * * Any new mutex implementation needs to do the following: * * - Condition on the right header or feature. Headers are * preferred as eg. you still can use win32 mutex implementation * on a win32 system even if you do not compile the win32 * surface/backend. * * - typedef #cairo_mutex_impl_t to the proper mutex type on your target * system. Note that you may or may not need to use a pointer, * depending on what kinds of initialization your mutex * implementation supports. No trailing semicolon needed. * You should be able to compile the following snippet (don't try * running it): * * <programlisting> * cairo_mutex_impl_t _cairo_some_mutex; * </programlisting> * * - #define %CAIRO_MUTEX_IMPL_<NAME> 1 with suitable name for your platform. You * can later use this symbol in cairo-system.c. * * - #define CAIRO_MUTEX_IMPL_LOCK(mutex) and CAIRO_MUTEX_IMPL_UNLOCK(mutex) to * proper statement to lock/unlock the mutex object passed in. * You can (and should) assume that the mutex is already * initialized, and is-not-already-locked/is-locked, * respectively. Use the "do { ... } while (0)" idiom if necessary. * No trailing semicolons are needed (in any macro you define here). * You should be able to compile the following snippet: * * <programlisting> * cairo_mutex_impl_t _cairo_some_mutex; * * if (1) * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); * else * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); * </programlisting> * * - #define %CAIRO_MUTEX_IMPL_NIL_INITIALIZER to something that can * initialize the #cairo_mutex_impl_t type you defined. Most of the * time one of 0, %NULL, or {} works. At this point * you should be able to compile the following snippet: * * <programlisting> * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; * * if (1) * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); * else * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); * </programlisting> * * - If the above code is not enough to initialize a mutex on * your platform, #define CAIRO_MUTEX_IMPL_INIT(mutex) to statement * to initialize the mutex (allocate resources, etc). Such that * you should be able to compile AND RUN the following snippet: * * <programlisting> * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; * * CAIRO_MUTEX_IMPL_INIT (_cairo_some_mutex); * * if (1) * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); * else * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); * </programlisting> * * - If you define CAIRO_MUTEX_IMPL_INIT(mutex), cairo will use it to * initialize all static mutex'es. If for any reason that should * not happen (eg. %CAIRO_MUTEX_IMPL_INIT is just a faster way than * what cairo does using %CAIRO_MUTEX_IMPL_NIL_INITIALIZER), then * <programlisting> * #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP * </programlisting> * * - If your system supports freeing a mutex object (deallocating * resources, etc), then #define CAIRO_MUTEX_IMPL_FINI(mutex) to do * that. * * - If you define CAIRO_MUTEX_IMPL_FINI(mutex), cairo will use it to * define a finalizer function to finalize all static mutex'es. * However, it's up to you to call CAIRO_MUTEX_IMPL_FINALIZE() at * proper places, eg. when the system is unloading the cairo library. * So, if for any reason finalizing static mutex'es is not needed * (eg. you never call CAIRO_MUTEX_IMPL_FINALIZE()), then * <programlisting> * #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP * </programlisting> * * - That is all. If for any reason you think the above API is * not enough to implement #cairo_mutex_impl_t on your system, please * stop and write to the cairo mailing list about it. DO NOT * poke around cairo-mutex-private.h for possible solutions. */ #if CAIRO_NO_MUTEX /* No mutexes */ typedef int cairo_mutex_impl_t; # define CAIRO_MUTEX_IMPL_NO 1 # define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP # define CAIRO_MUTEX_IMPL_LOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 # define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 typedef int cairo_recursive_mutex_impl_t; # define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) # define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER 0 #elif defined(_WIN32) /******************************************************/ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif # include <windows.h> typedef CRITICAL_SECTION cairo_mutex_impl_t; # define CAIRO_MUTEX_IMPL_WIN32 1 # define CAIRO_MUTEX_IMPL_LOCK(mutex) EnterCriticalSection (&(mutex)) # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) LeaveCriticalSection (&(mutex)) # define CAIRO_MUTEX_IMPL_INIT(mutex) InitializeCriticalSection (&(mutex)) # define CAIRO_MUTEX_IMPL_FINI(mutex) DeleteCriticalSection (&(mutex)) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER { NULL, 0, 0, NULL, NULL, 0 } #elif defined __OS2__ /******************************************************/ # define INCL_BASE # define INCL_PM # include <os2.h> typedef HMTX cairo_mutex_impl_t; # define CAIRO_MUTEX_IMPL_OS2 1 # define CAIRO_MUTEX_IMPL_LOCK(mutex) DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT) # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) DosReleaseMutexSem(mutex) # define CAIRO_MUTEX_IMPL_INIT(mutex) DosCreateMutexSem (NULL, &(mutex), 0L, FALSE) # define CAIRO_MUTEX_IMPL_FINI(mutex) DosCloseMutexSem (mutex) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 #elif CAIRO_HAS_BEOS_SURFACE /***********************************************/ typedef BLocker* cairo_mutex_impl_t; # define CAIRO_MUTEX_IMPL_BEOS 1 # define CAIRO_MUTEX_IMPL_LOCK(mutex) (mutex)->Lock() # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) (mutex)->Unlock() # define CAIRO_MUTEX_IMPL_INIT(mutex) (mutex) = new BLocker() # define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex) # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL #elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/ # include <pthread.h> typedef pthread_mutex_t cairo_mutex_impl_t; typedef pthread_mutex_t cairo_recursive_mutex_impl_t; # define CAIRO_MUTEX_IMPL_PTHREAD 1 #if HAVE_LOCKDEP /* expose all mutexes to the validator */ # define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) #endif # define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) #if HAVE_LOCKDEP # define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) # define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) #endif # define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) #if ! HAVE_LOCKDEP # define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP #endif # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER # define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 # define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) do { \ pthread_mutexattr_t attr; \ pthread_mutexattr_init (&attr); \ pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); \ pthread_mutex_init (&(mutex), &attr); \ pthread_mutexattr_destroy (&attr); \ } while (0) # define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP #else /**********************************************************************/ # error "XXX: No mutex implementation found. Cairo will not work with multiple threads. Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support." #endif /* By default mutex implementations are assumed to be recursive */ #if ! CAIRO_MUTEX_HAS_RECURSIVE_IMPL # define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 typedef cairo_mutex_impl_t cairo_recursive_mutex_impl_t; # define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) CAIRO_MUTEX_IMPL_INIT(mutex) # define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER #endif #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mutex-list-private.h����������������������0000664�0000000�0000000�00000005211�12710376503�0027326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2007 Mathias Hasselmann * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Mathias Hasselmann <mathias.hasselmann@gmx.de> */ #ifndef CAIRO_FEATURES_H /* This block is to just make this header file standalone */ #define CAIRO_MUTEX_DECLARE(mutex) #endif CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex) CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex) CAIRO_MUTEX_DECLARE (_cairo_glyph_cache_mutex) #if CAIRO_HAS_FT_FONT CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) #endif #if CAIRO_HAS_WIN32_FONT CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex) #endif #if CAIRO_HAS_XLIB_SURFACE CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) #endif #if CAIRO_HAS_XCB_SURFACE CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) #endif #if CAIRO_HAS_GL_SURFACE CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) #endif #if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) #endif #if CAIRO_HAS_DRM_SURFACE CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex) #endif /* Undefine, to err on unintended inclusion */ #undef CAIRO_MUTEX_DECLARE ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mutex-private.h���������������������������0000664�0000000�0000000�00000004657�12710376503�0026372�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005,2007 Red Hat, Inc. * Copyright © 2007 Mathias Hasselmann * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Mathias Hasselmann <mathias.hasselmann@gmx.de> * Behdad Esfahbod <behdad@behdad.org> */ #ifndef CAIRO_MUTEX_PRIVATE_H #define CAIRO_MUTEX_PRIVATE_H #include "cairo-mutex-type-private.h" CAIRO_BEGIN_DECLS #if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER cairo_private void _cairo_mutex_initialize (void); #endif #if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER cairo_private void _cairo_mutex_finalize (void); #endif /* only if using static initializer and/or finalizer define the boolean */ #if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER cairo_private extern cairo_bool_t _cairo_mutex_initialized; #endif /* Finally, extern the static mutexes and undef */ #define CAIRO_MUTEX_DECLARE(mutex) cairo_private extern cairo_mutex_t mutex; #include "cairo-mutex-list-private.h" #undef CAIRO_MUTEX_DECLARE CAIRO_END_DECLS #endif ���������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mutex-type-private.h����������������������0000664�0000000�0000000�00000014513�12710376503�0027341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005,2007 Red Hat, Inc. * Copyright © 2007 Mathias Hasselmann * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Mathias Hasselmann <mathias.hasselmann@gmx.de> * Behdad Esfahbod <behdad@behdad.org> */ #ifndef CAIRO_MUTEX_TYPE_PRIVATE_H #define CAIRO_MUTEX_TYPE_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-mutex-impl-private.h" /* Only the following four are mandatory at this point */ #ifndef CAIRO_MUTEX_IMPL_LOCK # error "CAIRO_MUTEX_IMPL_LOCK not defined. Check cairo-mutex-impl-private.h." #endif #ifndef CAIRO_MUTEX_IMPL_UNLOCK # error "CAIRO_MUTEX_IMPL_UNLOCK not defined. Check cairo-mutex-impl-private.h." #endif #ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER # error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined. Check cairo-mutex-impl-private.h." #endif #ifndef CAIRO_RECURSIVE_MUTEX_IMPL_INIT # error "CAIRO_RECURSIVE_MUTEX_IMPL_INIT not defined. Check cairo-mutex-impl-private.h." #endif /* make sure implementations don't fool us: we decide these ourself */ #undef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER #undef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER #ifdef CAIRO_MUTEX_IMPL_INIT /* If %CAIRO_MUTEX_IMPL_INIT is defined, we may need to initialize all * static mutex'es. */ # ifndef CAIRO_MUTEX_IMPL_INITIALIZE # define CAIRO_MUTEX_IMPL_INITIALIZE() do { \ if (!_cairo_mutex_initialized) \ _cairo_mutex_initialize (); \ } while(0) /* and make sure we implement the above */ # define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 1 # endif /* CAIRO_MUTEX_IMPL_INITIALIZE */ #else /* no CAIRO_MUTEX_IMPL_INIT */ /* Otherwise we probably don't need to initialize static mutex'es, */ # ifndef CAIRO_MUTEX_IMPL_INITIALIZE # define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP # endif /* CAIRO_MUTEX_IMPL_INITIALIZE */ /* and dynamic ones can be initialized using the static initializer. */ # define CAIRO_MUTEX_IMPL_INIT(mutex) do { \ cairo_mutex_t _tmp_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; \ memcpy (&(mutex), &_tmp_mutex, sizeof (_tmp_mutex)); \ } while (0) #endif /* CAIRO_MUTEX_IMPL_INIT */ #ifdef CAIRO_MUTEX_IMPL_FINI /* If %CAIRO_MUTEX_IMPL_FINI is defined, we may need to finalize all * static mutex'es. */ # ifndef CAIRO_MUTEX_IMPL_FINALIZE # define CAIRO_MUTEX_IMPL_FINALIZE() do { \ if (_cairo_mutex_initialized) \ _cairo_mutex_finalize (); \ } while(0) /* and make sure we implement the above */ # define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 1 # endif /* CAIRO_MUTEX_IMPL_FINALIZE */ #else /* no CAIRO_MUTEX_IMPL_FINI */ /* Otherwise we probably don't need to finalize static mutex'es, */ # ifndef CAIRO_MUTEX_IMPL_FINALIZE # define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP # endif /* CAIRO_MUTEX_IMPL_FINALIZE */ /* neither do the dynamic ones. */ # define CAIRO_MUTEX_IMPL_FINI(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) #endif /* CAIRO_MUTEX_IMPL_FINI */ #ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER #define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 0 #endif #ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER #define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 0 #endif /* Make sure everything we want is defined */ #ifndef CAIRO_MUTEX_IMPL_INITIALIZE # error "CAIRO_MUTEX_IMPL_INITIALIZE not defined" #endif #ifndef CAIRO_MUTEX_IMPL_FINALIZE # error "CAIRO_MUTEX_IMPL_FINALIZE not defined" #endif #ifndef CAIRO_MUTEX_IMPL_LOCK # error "CAIRO_MUTEX_IMPL_LOCK not defined" #endif #ifndef CAIRO_MUTEX_IMPL_UNLOCK # error "CAIRO_MUTEX_IMPL_UNLOCK not defined" #endif #ifndef CAIRO_MUTEX_IMPL_INIT # error "CAIRO_MUTEX_IMPL_INIT not defined" #endif #ifndef CAIRO_MUTEX_IMPL_FINI # error "CAIRO_MUTEX_IMPL_FINI not defined" #endif #ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER # error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined" #endif /* Public interface. */ /* By default it simply uses the implementation provided. * But we can provide for debugging features by overriding them */ #ifndef CAIRO_MUTEX_DEBUG typedef cairo_mutex_impl_t cairo_mutex_t; typedef cairo_recursive_mutex_impl_t cairo_recursive_mutex_t; #else # define cairo_mutex_t cairo_mutex_impl_t #endif #define CAIRO_MUTEX_INITIALIZE CAIRO_MUTEX_IMPL_INITIALIZE #define CAIRO_MUTEX_FINALIZE CAIRO_MUTEX_IMPL_FINALIZE #define CAIRO_MUTEX_LOCK CAIRO_MUTEX_IMPL_LOCK #define CAIRO_MUTEX_UNLOCK CAIRO_MUTEX_IMPL_UNLOCK #define CAIRO_MUTEX_INIT CAIRO_MUTEX_IMPL_INIT #define CAIRO_MUTEX_FINI CAIRO_MUTEX_IMPL_FINI #define CAIRO_MUTEX_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER #define CAIRO_RECURSIVE_MUTEX_INIT CAIRO_RECURSIVE_MUTEX_IMPL_INIT #define CAIRO_RECURSIVE_MUTEX_NIL_INITIALIZER CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER #ifndef CAIRO_MUTEX_IS_LOCKED # define CAIRO_MUTEX_IS_LOCKED(name) 1 #endif #ifndef CAIRO_MUTEX_IS_UNLOCKED # define CAIRO_MUTEX_IS_UNLOCKED(name) 1 #endif /* Debugging support */ #ifdef CAIRO_MUTEX_DEBUG /* TODO add mutex debugging facilities here (eg deadlock detection) */ #endif /* CAIRO_MUTEX_DEBUG */ #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-mutex.c�����������������������������������0000664�0000000�0000000�00000005204�12710376503�0024702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2007 Mathias Hasselmann * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Mathias Hasselmann <mathias.hasselmann@gmx.de> */ #include "cairoint.h" #include "cairo-mutex-private.h" #define CAIRO_MUTEX_DECLARE(mutex) cairo_mutex_t mutex = CAIRO_MUTEX_NIL_INITIALIZER; #include "cairo-mutex-list-private.h" #undef CAIRO_MUTEX_DECLARE #if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER # if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER # define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE FALSE # else # define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE TRUE # endif cairo_bool_t _cairo_mutex_initialized = _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE; # undef _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE #endif #if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER void _cairo_mutex_initialize (void) { if (_cairo_mutex_initialized) return; _cairo_mutex_initialized = TRUE; #define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_INIT (mutex); #include "cairo-mutex-list-private.h" #undef CAIRO_MUTEX_DECLARE } #endif #if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER void _cairo_mutex_finalize (void) { if (!_cairo_mutex_initialized) return; _cairo_mutex_initialized = FALSE; #define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex); #include "cairo-mutex-list-private.h" #undef CAIRO_MUTEX_DECLARE } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-no-compositor.c���������������������������0000664�0000000�0000000�00000007037�12710376503�0026356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" static cairo_int_status_t _cairo_no_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_no_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_no_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_no_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_no_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_NOTHING_TO_DO; } const cairo_compositor_t __cairo_no_compositor = { NULL, _cairo_no_compositor_paint, _cairo_no_compositor_mask, _cairo_no_compositor_stroke, _cairo_no_compositor_fill, _cairo_no_compositor_glyphs, }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-observer.c��������������������������������0000664�0000000�0000000�00000003527�12710376503�0025375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-list-inline.h" void _cairo_observers_notify (cairo_list_t *observers, void *arg) { cairo_observer_t *obs, *next; cairo_list_foreach_entry_safe (obs, next, cairo_observer_t, observers, link) { obs->callback (obs, arg); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-os2-private.h�����������������������������0000664�0000000�0000000�00000004643�12710376503�0025726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is * Doodle <doodle@scenergy.dfmk.hu> * * Contributor(s): * Peter Weilbacher <mozilla@Weilbacher.org> */ #ifndef CAIRO_OS2_PRIVATE_H #define CAIRO_OS2_PRIVATE_H #include "cairo-os2.h" #include "cairoint.h" typedef struct _cairo_os2_surface { cairo_surface_t base; /* Mutex semaphore to protect private fields from concurrent access */ HMTX hmtx_use_private_fields; /* Private fields: */ HPS hps_client_window; HWND hwnd_client_window; BITMAPINFO2 bitmap_info; unsigned char *pixels; cairo_image_surface_t *image_surface; int pixel_array_lend_count; HEV hev_pixel_array_came_back; RECTL rcl_dirty_area; cairo_bool_t dirty_area_present; /* General flags: */ cairo_bool_t blit_as_changes; cairo_bool_t use_24bpp; } cairo_os2_surface_t; #endif /* CAIRO_OS2_PRIVATE_H */ ���������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-os2-surface.c�����������������������������0000664�0000000�0000000�00000141255�12710376503�0025700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is * Doodle <doodle@scenergy.dfmk.hu> * * Contributor(s): * Peter Weilbacher <mozilla@Weilbacher.org> * Rich Walsh <dragtext@e-vertise.com> */ #include "cairoint.h" #include "cairo-os2-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-surface-fallback-private.h" #include "cairo-image-surface-private.h" #if CAIRO_HAS_FC_FONT #include <fontconfig/fontconfig.h> #endif #include <float.h> #ifdef BUILD_CAIRO_DLL # include "cairo-os2.h" # ifndef __WATCOMC__ # include <emx/startup.h> # endif #endif /* * Here comes the extra API for the OS/2 platform. Currently it consists * of two extra functions, the cairo_os2_init() and the * cairo_os2_fini(). Both of them are called automatically if * Cairo is compiled to be a DLL file, but you have to call them before * using the Cairo API if you link to Cairo statically! * * You'll also find the code in here which deals with DLL initialization * and termination, if the code is built to be a DLL. * (if BUILD_CAIRO_DLL is defined) */ /* Initialization counter: */ static int cairo_os2_initialization_count = 0; static inline void DisableFPUException (void) { unsigned short usCW; /* Some OS/2 PM API calls modify the FPU Control Word, * but forget to restore it. * * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions, * so to be sure, we disable Invalid Opcode FPU exception * before using FPU stuffs. */ usCW = _control87 (0, 0); usCW = usCW | EM_INVALID | 0x80; _control87 (usCW, MCW_EM | 0x80); } /** * cairo_os2_init: * * Initializes the Cairo library. This function is automatically called if * Cairo was compiled to be a DLL (however it's not a problem if it's called * multiple times). But if you link to Cairo statically, you have to call it * once to set up Cairo's internal structures and mutexes. * * Since: 1.4 **/ cairo_public void cairo_os2_init (void) { /* This may initialize some stuffs, like create mutex semaphores etc.. */ cairo_os2_initialization_count++; if (cairo_os2_initialization_count > 1) return; DisableFPUException (); #if CAIRO_HAS_FC_FONT /* Initialize FontConfig */ FcInit (); #endif CAIRO_MUTEX_INITIALIZE (); } /** * cairo_os2_fini: * * Uninitializes the Cairo library. This function is automatically called if * Cairo was compiled to be a DLL (however it's not a problem if it's called * multiple times). But if you link to Cairo statically, you have to call it * once to shut down Cairo, to let it free all the resources it has allocated. * * Since: 1.4 **/ cairo_public void cairo_os2_fini (void) { /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */ if (cairo_os2_initialization_count <= 0) return; cairo_os2_initialization_count--; if (cairo_os2_initialization_count > 0) return; DisableFPUException (); cairo_debug_reset_static_data (); #if CAIRO_HAS_FC_FONT # if HAVE_FCFINI /* Uninitialize FontConfig */ FcFini (); # endif #endif #ifdef __WATCOMC__ /* It can happen that the libraries we use have memory leaks, * so there are still memory chunks allocated at this point. * In these cases, Watcom might still have a bigger memory chunk, * called "the heap" allocated from the OS. * As we want to minimize the memory we lose from the point of * view of the OS, we call this function to shrink that heap * as much as possible. */ _heapshrink (); #else /* GCC has a heapmin function that approximately corresponds to * what the Watcom function does */ _heapmin (); #endif } /* * This function calls the allocation function depending on which * method was compiled into the library: it can be native allocation * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free). * Actually, for pixel buffers that we use this function for, cairo * uses _cairo_malloc_abc, so we use that here, too. And use the * change to check the size argument */ void *_buffer_alloc (size_t a, size_t b, const unsigned int size) { size_t nbytes; void *buffer = NULL; if (!a || !b || !size || a >= INT32_MAX / b || a*b >= INT32_MAX / size) { return NULL; } nbytes = a * b * size; #ifdef OS2_USE_PLATFORM_ALLOC /* Using OBJ_ANY on a machine that isn't configured for hi-mem * will cause ERROR_INVALID_PARAMETER. If this occurs, or this * build doesn't have hi-mem enabled, fall back to using lo-mem. */ #ifdef OS2_HIGH_MEMORY if (!DosAllocMem (&buffer, nbytes, OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) return buffer; #endif if (DosAllocMem (&buffer, nbytes, PAG_READ | PAG_WRITE | PAG_COMMIT)) return NULL; #else /* Clear the malloc'd buffer the way DosAllocMem() does. */ buffer = malloc (nbytes); if (buffer) { memset (buffer, 0, nbytes); } #endif return buffer; } /* * This function selects the free function depending on which * allocation method was compiled into the library */ void _buffer_free (void *buffer) { #ifdef OS2_USE_PLATFORM_ALLOC DosFreeMem (buffer); #else free (buffer); #endif } /* XXX * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and * the LibMain code moved to cairo-system.c. It should also call * cairo_debug_reset_static_data() instead of duplicating its logic... */ #ifdef BUILD_CAIRO_DLL /* The main DLL entry for DLL initialization and uninitialization */ /* Only include this code if we're about to build a DLL. */ #ifdef __WATCOMC__ unsigned _System LibMain (unsigned hmod, unsigned termination) #else unsigned long _System _DLL_InitTerm (unsigned long hModule, unsigned long termination) #endif { if (termination) { /* Unloading the DLL */ cairo_os2_fini (); #ifndef __WATCOMC__ /* Uninitialize RTL of GCC */ __ctordtorTerm (); _CRT_term (); #endif return 1; } else { /* Loading the DLL */ #ifndef __WATCOMC__ /* Initialize RTL of GCC */ if (_CRT_init () != 0) return 0; __ctordtorInit (); #endif cairo_os2_init (); return 1; } } #endif /* BUILD_CAIRO_DLL */ /* * The following part of the source file contains the code which might * be called the "core" of the OS/2 backend support. This contains the * OS/2 surface support functions and structures. */ /* Forward declaration */ static const cairo_surface_backend_t cairo_os2_surface_backend; /* Unpublished API: * GpiEnableYInversion = PMGPI.723 * GpiQueryYInversion = PMGPI.726 * BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); * LONG APIENTRY GpiQueryYInversion (HPS hps); */ BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); LONG APIENTRY GpiQueryYInversion (HPS hps); #ifdef __WATCOMC__ /* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */ LONG APIENTRY GpiDrawBits (HPS hps, PVOID pBits, PBITMAPINFO2 pbmiInfoTable, LONG lCount, PPOINTL aptlPoints, LONG lRop, ULONG flOptions); #endif static void _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, HPS hps_begin_paint, PRECTL prcl_begin_paint_rect) { POINTL aptlPoints[4]; LONG lOldYInversion; LONG rc = GPI_OK; /* Check the limits (may not be necessary) */ if (prcl_begin_paint_rect->xLeft < 0) prcl_begin_paint_rect->xLeft = 0; if (prcl_begin_paint_rect->yBottom < 0) prcl_begin_paint_rect->yBottom = 0; if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; /* Exit if the rectangle is empty */ if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) return; /* Set the Target & Source coordinates */ *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; /* Make the Target coordinates non-inclusive */ aptlPoints[1].x -= 1; aptlPoints[1].y -= 1; /* Enable Y Inversion for the HPS, so GpiDrawBits will * work with upside-top image, not with upside-down image! */ lOldYInversion = GpiQueryYInversion (hps_begin_paint); GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); /* Debug code to draw rectangle limits */ #if 0 { int x, y; unsigned char *pixels; pixels = surface->pixels; for (x = 0; x < surface->bitmap_info.cx; x++) { for (y = 0; y < surface->bitmap_info.cy; y++) { if ((x == 0) || (y == 0) || (x == y) || (x >= surface->bitmap_info.cx-1) || (y >= surface->bitmap_info.cy-1)) { pixels[y*surface->bitmap_info.cx*4+x*4] = 255; } } } } #endif if (!surface->use_24bpp) { rc = GpiDrawBits (hps_begin_paint, surface->pixels, &(surface->bitmap_info), 4, aptlPoints, ROP_SRCCOPY, BBO_IGNORE); if (rc != GPI_OK) surface->use_24bpp = TRUE; } if (surface->use_24bpp) { /* If GpiDrawBits () failed then this is most likely because the * display driver could not handle a 32bit bitmap. So we need to * - create a buffer that only contains 3 bytes per pixel * - change the bitmap info header to contain 24bit * - pass the new buffer to GpiDrawBits () again * - clean up the new buffer */ BITMAPINFO2 bmpinfo; unsigned char *pchPixBuf; unsigned char *pchTarget; ULONG *pulSource; ULONG ulX; ULONG ulY; ULONG ulPad; /* Set up the bitmap header, but this time for 24bit depth. */ bmpinfo = surface->bitmap_info; bmpinfo.cBitCount = 24; /* The start of each row has to be DWORD aligned. Calculate the * of number aligned bytes per row, the total size of the bitmap, * and the number of padding bytes at the end of each row. */ ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; bmpinfo.cbImage = ulX * bmpinfo.cy; ulPad = ulX - bmpinfo.cx * 3; /* Allocate temporary pixel buffer. If the rows don't need * padding, it has to be 1 byte larger than the size of the * bitmap or else the high-order byte from the last source * row will end up in unallocated memory. */ pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, bmpinfo.cbImage + (ulPad ? 0 : 1)); if (pchPixBuf) { /* Copy 4 bytes from the source but advance the target ptr only * 3 bytes, so the high-order alpha byte will be overwritten by * the next copy. At the end of each row, skip over the padding. */ pchTarget = pchPixBuf; pulSource = (ULONG*)surface->pixels; for (ulY = bmpinfo.cy; ulY; ulY--) { for (ulX = bmpinfo.cx; ulX; ulX--) { *((ULONG*)pchTarget) = *pulSource++; pchTarget += 3; } pchTarget += ulPad; } rc = GpiDrawBits (hps_begin_paint, pchPixBuf, &bmpinfo, 4, aptlPoints, ROP_SRCCOPY, BBO_IGNORE); if (rc != GPI_OK) surface->use_24bpp = FALSE; _buffer_free (pchPixBuf); } } /* Restore Y inversion */ GpiEnableYInversion (hps_begin_paint, lOldYInversion); } static void _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, HPS hps_begin_paint, PRECTL prcl_begin_paint_rect) { HPS hps; HDC hdc; SIZEL sizlTemp; HBITMAP hbmpTemp; BITMAPINFO2 bmi2Temp; POINTL aptlPoints[4]; int y; unsigned char *pchTemp; /* To copy pixels from screen to our buffer, we do the following steps: * * - Blit pixels from screen to a HBITMAP: * -- Create Memory Device Context * -- Create a PS into it * -- Create a HBITMAP * -- Select HBITMAP into memory PS * -- Blit dirty pixels from screen to HBITMAP * - Copy HBITMAP lines (pixels) into our buffer * - Free resources */ /* Create a memory device context */ hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE); if (!hdc) { return; } /* Create a memory PS */ sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft; sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom; hps = GpiCreatePS (0, hdc, &sizlTemp, PU_PELS | GPIT_NORMAL | GPIA_ASSOC); if (!hps) { DevCloseDC (hdc); return; } /* Create an uninitialized bitmap. */ /* Prepare BITMAPINFO2 structure for our buffer */ memset (&bmi2Temp, 0, sizeof (bmi2Temp)); bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2); bmi2Temp.cx = sizlTemp.cx; bmi2Temp.cy = sizlTemp.cy; bmi2Temp.cPlanes = 1; bmi2Temp.cBitCount = 32; hbmpTemp = GpiCreateBitmap (hps, (PBITMAPINFOHEADER2) &bmi2Temp, 0, NULL, NULL); if (!hbmpTemp) { GpiDestroyPS (hps); DevCloseDC (hdc); return; } /* Select the bitmap into the memory device context. */ GpiSetBitmap (hps, hbmpTemp); /* Target coordinates (Noninclusive) */ aptlPoints[0].x = 0; aptlPoints[0].y = 0; aptlPoints[1].x = sizlTemp.cx; aptlPoints[1].y = sizlTemp.cy; /* Source coordinates (Inclusive) */ aptlPoints[2].x = prcl_begin_paint_rect->xLeft; aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; aptlPoints[3].x = prcl_begin_paint_rect->xRight; aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop; /* Blit pixels from screen to bitmap */ GpiBitBlt (hps, hps_begin_paint, 4, aptlPoints, ROP_SRCCOPY, BBO_IGNORE); /* Now we have to extract the pixels from the bitmap. */ pchTemp = surface->pixels + (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 + prcl_begin_paint_rect->xLeft*4; for (y = 0; y < sizlTemp.cy; y++) { /* Get one line of pixels */ GpiQueryBitmapBits (hps, sizlTemp.cy - y - 1, /* lScanStart */ 1, /* lScans */ (PBYTE)pchTemp, &bmi2Temp); /* Go for next line */ pchTemp += surface->bitmap_info.cx*4; } /* Clean up resources */ GpiSetBitmap (hps, (HBITMAP) NULL); GpiDeleteBitmap (hbmpTemp); GpiDestroyPS (hps); DevCloseDC (hdc); } static cairo_status_t _cairo_os2_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Increase lend counter */ surface->pixel_array_lend_count++; *image_out = surface->image_surface; *image_extra = NULL; DosReleaseMutexSem (surface->hmtx_use_private_fields); return CAIRO_STATUS_SUCCESS; } static void _cairo_os2_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; /* Decrease Lend counter! */ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); if (surface->pixel_array_lend_count > 0) surface->pixel_array_lend_count--; DosPostEventSem (surface->hev_pixel_array_came_back); DosReleaseMutexSem (surface->hmtx_use_private_fields); } static cairo_image_surface_t * _cairo_os2_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Increase lend counter */ surface->pixel_array_lend_count++; DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); /* XXX: BROKEN! */ *image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface, extents); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_os2_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; /* So, we got back the image, and if all goes well, then * something has been changed inside the interest_rect. * So, we blit it to the screen! */ if (surface->blit_as_changes) { RECTL rclToBlit; /* Get mutex, we'll work with the pixel array! */ if (DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! */ return; } rclToBlit.xLeft = image->base.device_transform_inverse.x0; rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */ rclToBlit.yTop = image->base.device_transform_inverse.y0; rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */ if (surface->hwnd_client_window) { /* We know the HWND, so let's invalidate the window region, * so the application will redraw itself, using the * cairo_os2_surface_refresh_window () API from its own PM thread. * * This is the safe method, which should be preferred every time. */ rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop; rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop; WinInvalidateRect (surface->hwnd_client_window, &rclToBlit, FALSE); } else { /* We don't know the HWND, so try to blit the pixels from here! * Please note that it can be problematic if this is not the PM thread! * * It can cause internal PM stuffs to be screwed up, for some reason. * Please always tell the HWND to the surface using the * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () * from your WM_PAINT, if it's possible! */ _cairo_os2_surface_blit_pixels (surface, surface->hps_client_window, &rclToBlit); } DosReleaseMutexSem (surface->hmtx_use_private_fields); } /* Also decrease Lend counter! */ DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); if (surface->pixel_array_lend_count > 0) surface->pixel_array_lend_count--; DosPostEventSem (surface->hev_pixel_array_came_back); DosReleaseMutexSem (surface->hmtx_use_private_fields); } static cairo_bool_t _cairo_os2_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->bitmap_info.cx; rectangle->height = surface->bitmap_info.cy; return TRUE; } /** * cairo_os2_surface_create: * @hps_client_window: the presentation handle to bind the surface to * @width: the width of the surface * @height: the height of the surface * * Create a Cairo surface which is bound to a given presentation space (HPS). * The caller retains ownership of the HPS and must dispose of it after the * the surface has been destroyed. The surface will be created to have the * given size. By default every change to the surface will be made visible * immediately by blitting it into the window. This can be changed with * cairo_os2_surface_set_manual_window_refresh(). * Note that the surface will contain garbage when created, so the pixels * have to be initialized by hand first. You can use the Cairo functions to * fill it with black, or use cairo_surface_mark_dirty() to fill the surface * with pixels from the window/HPS. * * Return value: the newly created surface * * Since: 1.4 **/ cairo_surface_t * cairo_os2_surface_create (HPS hps_client_window, int width, int height) { cairo_os2_surface_t *local_os2_surface = 0; cairo_status_t status; int rc; /* Check the size of the window */ if ((width <= 0) || (height <= 0)) { status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); goto error_exit; } /* Allocate an OS/2 surface structure. */ local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); if (!local_os2_surface) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto error_exit; } memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); /* Allocate resources: mutex & event semaphores and the pixel buffer */ if (DosCreateMutexSem (NULL, &(local_os2_surface->hmtx_use_private_fields), 0, FALSE)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto error_exit; } if (DosCreateEventSem (NULL, &(local_os2_surface->hev_pixel_array_came_back), 0, FALSE)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto error_exit; } local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); if (!local_os2_surface->pixels) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto error_exit; } /* Create image surface from pixel array */ local_os2_surface->image_surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (local_os2_surface->pixels, CAIRO_FORMAT_ARGB32, width, /* Width */ height, /* Height */ width * 4); /* Rowstride */ status = local_os2_surface->image_surface->base.status; if (status) goto error_exit; /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. * Note: hps_client_window may be null if this was called by * cairo_os2_surface_create_for_window(). */ local_os2_surface->hps_client_window = hps_client_window; local_os2_surface->blit_as_changes = TRUE; /* Prepare BITMAPINFO2 structure for our buffer */ local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); local_os2_surface->bitmap_info.cx = width; local_os2_surface->bitmap_info.cy = height; local_os2_surface->bitmap_info.cPlanes = 1; local_os2_surface->bitmap_info.cBitCount = 32; /* Initialize base surface */ _cairo_surface_init (&local_os2_surface->base, &cairo_os2_surface_backend, NULL, /* device */ _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); /* Successful exit */ return (cairo_surface_t *)local_os2_surface; error_exit: /* This point will only be reached if an error occurred */ if (local_os2_surface) { if (local_os2_surface->pixels) _buffer_free (local_os2_surface->pixels); if (local_os2_surface->hev_pixel_array_came_back) DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); if (local_os2_surface->hmtx_use_private_fields) DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); free (local_os2_surface); } return _cairo_surface_create_in_error (status); } /** * cairo_os2_surface_create_for_window: * @hwnd_client_window: the window handle to bind the surface to * @width: the width of the surface * @height: the height of the surface * * Create a Cairo surface which is bound to a given window; the caller retains * ownership of the window. This is a convenience function for use with * windows that will only be updated when cairo_os2_surface_refresh_window() * is called (usually in response to a WM_PAINT message). It avoids the need * to create a persistent HPS for every window and assumes that one will be * supplied by the caller when a cairo function needs one. If it isn't, an * HPS will be created on-the-fly and released before the function which needs * it returns. * * Return value: the newly created surface * * Since: 1.10 **/ cairo_surface_t * cairo_os2_surface_create_for_window (HWND hwnd_client_window, int width, int height) { cairo_os2_surface_t *local_os2_surface; /* A window handle must be provided. */ if (!hwnd_client_window) { return _cairo_surface_create_in_error ( _cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* Create the surface. */ local_os2_surface = (cairo_os2_surface_t *) cairo_os2_surface_create (0, width, height); /* If successful, save the hwnd & turn off automatic repainting. */ if (!local_os2_surface->image_surface->base.status) { local_os2_surface->hwnd_client_window = hwnd_client_window; local_os2_surface->blit_as_changes = FALSE; } return (cairo_surface_t *)local_os2_surface; } /** * cairo_os2_surface_set_size: * @surface: the cairo surface to resize * @new_width: the new width of the surface * @new_height: the new height of the surface * @timeout: timeout value in milliseconds * * When the client window is resized, call this API to set the new size in the * underlying surface accordingly. This function will reallocate everything, * so you'll have to redraw everything in the surface after this call. * The surface will contain garbage after the resizing. So the notes of * cairo_os2_surface_create() apply here, too. * * The timeout value specifies how long the function should wait on other parts * of the program to release the buffers. It is necessary, because it can happen * that Cairo is just drawing something into the surface while we want to * destroy and recreate it. * * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, * %CAIRO_STATUS_INVALID_SIZE for invalid sizes * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the * timeout happened before all the buffers were released * * Since: 1.4 **/ int cairo_os2_surface_set_size (cairo_surface_t *surface, int new_width, int new_height, int timeout) { cairo_os2_surface_t *local_os2_surface; unsigned char *pchNewPixels; int rc; cairo_image_surface_t *pNewImageSurface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } if ((new_width <= 0) || (new_height <= 0)) { /* Invalid size! */ return _cairo_error (CAIRO_STATUS_INVALID_SIZE); } /* Allocate memory for new stuffs */ pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4); if (!pchNewPixels) { /* Not enough memory for the pixels! * Everything remains the same! */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Create image surface from new pixel array */ pNewImageSurface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (pchNewPixels, CAIRO_FORMAT_ARGB32, new_width, /* Width */ new_height, /* Height */ new_width * 4); /* Rowstride */ if (pNewImageSurface->base.status) { /* Could not create image surface! * Everything remains the same! */ _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Okay, new memory allocated, so it's time to swap old buffers * to new ones! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { /* Could not get mutex! * Everything remains the same! */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* We have to make sure that we won't destroy a surface which * is lent to some other code (Cairo is drawing into it)! */ while (local_os2_surface->pixel_array_lend_count > 0) { ULONG ulPostCount; DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount); DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); /* Wait for somebody to return the pixels! */ rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout); if (rc != NO_ERROR) { /* Either timeout or something wrong... Exit. */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Okay, grab mutex and check counter again! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! * Everything remains the same! */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } /* Destroy old image surface */ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); /* Destroy old pixel buffer */ _buffer_free (local_os2_surface->pixels); /* Set new image surface */ local_os2_surface->image_surface = pNewImageSurface; /* Set new pixel buffer */ local_os2_surface->pixels = pchNewPixels; /* Change bitmap2 structure */ local_os2_surface->bitmap_info.cx = new_width; local_os2_surface->bitmap_info.cy = new_height; DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); return CAIRO_STATUS_SUCCESS; } /** * cairo_os2_surface_refresh_window: * @surface: the cairo surface to refresh * @hps_begin_paint: the presentation handle of the window to refresh * @prcl_begin_paint_rect: the rectangle to redraw * * This function can be used to force a repaint of a given area of the client * window. It should usually be called from the WM_PAINT processing of the * window procedure. However, it can be called any time a given part of the * window has to be updated. * * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call * of the window procedure, but you can also get the HPS using WinGetPS, and you * can assemble your own update rectangle by hand. * If hps_begin_paint is %NULL, the function will use the HPS passed into * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function * will query the current window size and repaint the whole window. * * Cairo assumes that if you set the HWND to the surface using * cairo_os2_surface_set_hwnd(), this function will be called by the application * every time it gets a WM_PAINT for that HWND. If the HWND is set in the * surface, Cairo uses this function to handle dirty areas too. * * Since: 1.4 **/ void cairo_os2_surface_refresh_window (cairo_surface_t *surface, HPS hps_begin_paint, PRECTL prcl_begin_paint_rect) { cairo_os2_surface_t *local_os2_surface; RECTL rclTemp; HPS hpsTemp = 0; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return; } /* If an HPS wasn't provided, see if we can get one. */ if (!hps_begin_paint) { hps_begin_paint = local_os2_surface->hps_client_window; if (!hps_begin_paint) { if (local_os2_surface->hwnd_client_window) { hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window); hps_begin_paint = hpsTemp; } /* No HPS & no way to get one, so exit */ if (!hps_begin_paint) return; } } if (prcl_begin_paint_rect == NULL) { /* Update the whole window! */ rclTemp.xLeft = 0; rclTemp.xRight = local_os2_surface->bitmap_info.cx; rclTemp.yTop = local_os2_surface->bitmap_info.cy; rclTemp.yBottom = 0; } else { /* Use the rectangle we got passed as parameter! */ rclTemp.xLeft = prcl_begin_paint_rect->xLeft; rclTemp.xRight = prcl_begin_paint_rect->xRight; rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ; } /* Get mutex, we'll work with the pixel array! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! */ if (hpsTemp) WinReleasePS(hpsTemp); return; } if ((local_os2_surface->dirty_area_present) && (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) && (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) && (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) && (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom)) { /* Aha, this call was because of a dirty area, so in this case we * have to blit the pixels from the screen to the surface! */ local_os2_surface->dirty_area_present = FALSE; _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, hps_begin_paint, &rclTemp); } else { /* Okay, we have the surface, have the HPS * (might be from WinBeginPaint () or from WinGetPS () ) * Now blit there the stuffs! */ _cairo_os2_surface_blit_pixels (local_os2_surface, hps_begin_paint, &rclTemp); } DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); if (hpsTemp) WinReleasePS(hpsTemp); } static cairo_status_t _cairo_os2_surface_finish (void *abstract_surface) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) abstract_surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Destroy old image surface */ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); /* Destroy old pixel buffer */ _buffer_free (local_os2_surface->pixels); DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); /* The memory itself will be free'd by the cairo_surface_destroy () * who called us. */ return CAIRO_STATUS_SUCCESS; } /** * cairo_os2_surface_set_hwnd: * @surface: the cairo surface to associate with the window handle * @hwnd_client_window: the window handle of the client window * * Sets window handle for surface; the caller retains ownership of the window. * If Cairo wants to blit into the window because it is set to blit as the * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then * there are two ways it can choose: * If it knows the HWND of the surface, then it invalidates that area, so the * application will get a WM_PAINT message and it can call * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself * will use the HPS it got at surface creation time, and blit the pixels itself. * It's also a solution, but experience shows that if this happens from a non-PM * thread, then it can screw up PM internals. * * So, best solution is to set the HWND for the surface after the surface * creation, so every blit will be done from application's message processing * loop, which is the safest way to do. * * Since: 1.4 **/ void cairo_os2_surface_set_hwnd (cairo_surface_t *surface, HWND hwnd_client_window) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return; } if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! */ return; } local_os2_surface->hwnd_client_window = hwnd_client_window; DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); } /** * cairo_os2_surface_set_manual_window_refresh: * @surface: the cairo surface to set the refresh mode for * @manual_refresh: the switch for manual surface refresh * * This API can tell Cairo if it should show every change to this surface * immediately in the window or if it should be cached and will only be visible * once the user calls cairo_os2_surface_refresh_window() explicitly. If the * HWND was not set in the cairo surface, then the HPS will be used to blit the * graphics. Otherwise it will invalidate the given window region so the user * will get the WM_PAINT message to redraw that area of the window. * * So, if you're only interested in displaying the final result after several * drawing operations, you might get better performance if you put the surface * into manual refresh mode by passing a true value to this function. Then call * cairo_os2_surface_refresh() whenever desired. * * Since: 1.4 **/ void cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, cairo_bool_t manual_refresh) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return; } local_os2_surface->blit_as_changes = !manual_refresh; } /** * cairo_os2_surface_get_manual_window_refresh: * @surface: the cairo surface to query the refresh mode from * * This space left intentionally blank. * * Return value: current refresh mode of the surface (true by default) * * Since: 1.4 **/ cairo_bool_t cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return FALSE; } return !(local_os2_surface->blit_as_changes); } /** * cairo_os2_surface_get_hps: * @surface: the cairo surface to be querued * @hps: HPS currently associated with the surface (if any) * * This API retrieves the HPS associated with the surface. * * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, * %CAIRO_STATUS_NULL_POINTER if the hps argument is null * * Since: 1.10 **/ cairo_status_t cairo_os2_surface_get_hps (cairo_surface_t *surface, HPS *hps) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } if (!hps) { return _cairo_error (CAIRO_STATUS_NULL_POINTER); } *hps = local_os2_surface->hps_client_window; return CAIRO_STATUS_SUCCESS; } /** * cairo_os2_surface_set_hps: * @surface: the cairo surface to associate with the HPS * @hps: new HPS to be associated with the surface (the HPS may be null) * * This API replaces the HPS associated with the surface with a new one. * The caller retains ownership of the HPS and must dispose of it after * the surface has been destroyed or it has been replaced by another * call to this function. * * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, * * Since: 1.10 **/ cairo_status_t cairo_os2_surface_set_hps (cairo_surface_t *surface, HPS hps) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } local_os2_surface->hps_client_window = hps; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_os2_surface_mark_dirty_rectangle (void *surface, int x, int y, int width, int height) { cairo_os2_surface_t *local_os2_surface; RECTL rclToBlit; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } /* Get mutex, we'll work with the pixel array! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! */ return CAIRO_STATUS_NO_MEMORY; } /* Check for defaults */ if (width < 0) width = local_os2_surface->bitmap_info.cx; if (height < 0) height = local_os2_surface->bitmap_info.cy; if (local_os2_surface->hwnd_client_window) { /* We know the HWND, so let's invalidate the window region, * so the application will redraw itself, using the * cairo_os2_surface_refresh_window () API from its own PM thread. * From that function we'll note that it's not a redraw but a * dirty-rectangle deal stuff, so we'll handle the things from * there. * * This is the safe method, which should be preferred every time. */ rclToBlit.xLeft = x; rclToBlit.xRight = x + width; /* Noninclusive */ rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y); rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */ #if 0 if (local_os2_surface->dirty_area_present) { /* Yikes, there is already a dirty area which should be * cleaned up, but we'll overwrite it. Sorry. * TODO: Something clever should be done here. */ } #endif /* Set up dirty area reminder stuff */ memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL)); local_os2_surface->dirty_area_present = TRUE; /* Invalidate window area */ WinInvalidateRect (local_os2_surface->hwnd_client_window, &rclToBlit, FALSE); } else { /* We don't know the HWND, so try to blit the pixels from here! * Please note that it can be problematic if this is not the PM thread! * * It can cause internal PM stuffs to be scewed up, for some reason. * Please always tell the HWND to the surface using the * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () * from your WM_PAINT, if it's possible! */ rclToBlit.xLeft = x; rclToBlit.xRight = x + width; /* Noninclusive */ rclToBlit.yBottom = y; rclToBlit.yTop = y + height; /* Noninclusive */ /* Now get the pixels from the screen! */ _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, local_os2_surface->hps_client_window, &rclToBlit); } DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_os2_surface_backend = { CAIRO_SURFACE_TYPE_OS2, _cairo_os2_surface_finish, _cairo_default_context_create, NULL, /* create_similar */ NULL, /* create_similar_image */ _cairo_os2_surface_map_to_image, _cairo_os2_surface_unmap_image, _cairo_surface_default_source, _cairo_os2_surface_acquire_source_image, _cairo_os2_surface_release_source_image, NULL, /* snapshot */ _cairo_os2_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ _cairo_os2_surface_mark_dirty_rectangle, _cairo_surface_fallback_paint, _cairo_surface_fallback_mask, _cairo_surface_fallback_fill, _cairo_surface_fallback_stroke, NULL, /* fill/stroke */ _cairo_surface_fallback_glyphs, }; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-os2.h�������������������������������������0000664�0000000�0000000�00000007053�12710376503�0024254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is * Doodle <doodle@scenergy.dfmk.hu> * * Contributor(s): * Peter Weilbacher <mozilla@Weilbacher.org> * Rich Walsh <dragtext@e-vertise.com> */ #ifndef _CAIRO_OS2_H_ #define _CAIRO_OS2_H_ #define INCL_DOS #define INCL_DOSSEMAPHORES #define INCL_DOSERRORS #define INCL_WIN #define INCL_GPI #include "cairo.h" #include <os2.h> CAIRO_BEGIN_DECLS /* The OS/2 Specific Cairo API */ cairo_public void cairo_os2_init (void); cairo_public void cairo_os2_fini (void); #if CAIRO_HAS_OS2_SURFACE cairo_public cairo_surface_t * cairo_os2_surface_create (HPS hps_client_window, int width, int height); cairo_public cairo_surface_t * cairo_os2_surface_create_for_window (HWND hwnd_client_window, int width, int height); cairo_public void cairo_os2_surface_set_hwnd (cairo_surface_t *surface, HWND hwnd_client_window); cairo_public int cairo_os2_surface_set_size (cairo_surface_t *surface, int new_width, int new_height, int timeout); cairo_public void cairo_os2_surface_refresh_window (cairo_surface_t *surface, HPS hps_begin_paint, PRECTL prcl_begin_paint_rect); cairo_public void cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, cairo_bool_t manual_refresh); cairo_public cairo_bool_t cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface); cairo_public cairo_status_t cairo_os2_surface_get_hps (cairo_surface_t *surface, HPS *hps); cairo_public cairo_status_t cairo_os2_surface_set_hps (cairo_surface_t *surface, HPS hps); #else /* CAIRO_HAS_OS2_SURFACE */ # error Cairo was not compiled with support for the OS/2 backend #endif /* CAIRO_HAS_OS2_SURFACE */ CAIRO_END_DECLS #endif /* _CAIRO_OS2_H_ */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-output-stream-private.h�������������������0000664�0000000�0000000�00000015547�12710376503�0030061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Author(s): * Kristian Høgsberg <krh@redhat.com> */ #ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H #define CAIRO_OUTPUT_STREAM_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" #include <stdlib.h> #include <stdio.h> #include <stdarg.h> typedef cairo_status_t (*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, const unsigned char *data, unsigned int length); typedef cairo_status_t (*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); typedef cairo_status_t (*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); struct _cairo_output_stream { cairo_output_stream_write_func_t write_func; cairo_output_stream_flush_func_t flush_func; cairo_output_stream_close_func_t close_func; unsigned long position; cairo_status_t status; cairo_bool_t closed; }; extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; cairo_private void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func); cairo_private cairo_status_t _cairo_output_stream_fini (cairo_output_stream_t *stream); /* We already have the following declared in cairo.h: typedef cairo_status_t (*cairo_write_func_t) (void *closure, const unsigned char *data, unsigned int length); */ typedef cairo_status_t (*cairo_close_func_t) (void *closure); /* This function never returns %NULL. If an error occurs (NO_MEMORY) * while trying to create the output stream this function returns a * valid pointer to a nil output stream. * * Note that even with a nil surface, the close_func callback will be * called by a call to _cairo_output_stream_close or * _cairo_output_stream_destroy. */ cairo_private cairo_output_stream_t * _cairo_output_stream_create (cairo_write_func_t write_func, cairo_close_func_t close_func, void *closure); cairo_private cairo_output_stream_t * _cairo_output_stream_create_in_error (cairo_status_t status); /* Tries to flush any buffer maintained by the stream or its delegates. */ cairo_private cairo_status_t _cairo_output_stream_flush (cairo_output_stream_t *stream); /* Returns the final status value associated with this object, just * before its last gasp. This final status value will capture any * status failure returned by the stream's close_func as well. */ cairo_private cairo_status_t _cairo_output_stream_close (cairo_output_stream_t *stream); /* Returns the final status value associated with this object, just * before its last gasp. This final status value will capture any * status failure returned by the stream's close_func as well. */ cairo_private cairo_status_t _cairo_output_stream_destroy (cairo_output_stream_t *stream); cairo_private void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length); cairo_private void _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, const unsigned char *data, size_t length); cairo_private void _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap) CAIRO_PRINTF_FORMAT ( 2, 0); cairo_private void _cairo_output_stream_printf (cairo_output_stream_t *stream, const char *fmt, ...) CAIRO_PRINTF_FORMAT (2, 3); cairo_private long _cairo_output_stream_get_position (cairo_output_stream_t *stream); cairo_private cairo_status_t _cairo_output_stream_get_status (cairo_output_stream_t *stream); /* This function never returns %NULL. If an error occurs (NO_MEMORY or * WRITE_ERROR) while trying to create the output stream this function * returns a valid pointer to a nil output stream. * * Note: Even if a nil surface is returned, the caller should still * call _cairo_output_stream_destroy (or _cairo_output_stream_close at * least) in order to ensure that everything is properly cleaned up. */ cairo_private cairo_output_stream_t * _cairo_output_stream_create_for_filename (const char *filename); /* This function never returns %NULL. If an error occurs (NO_MEMORY or * WRITE_ERROR) while trying to create the output stream this function * returns a valid pointer to a nil output stream. * * The caller still "owns" file and is responsible for calling fclose * on it when finished. The stream will not do this itself. */ cairo_private cairo_output_stream_t * _cairo_output_stream_create_for_file (FILE *file); cairo_private cairo_output_stream_t * _cairo_memory_stream_create (void); cairo_private void _cairo_memory_stream_copy (cairo_output_stream_t *base, cairo_output_stream_t *dest); cairo_private int _cairo_memory_stream_length (cairo_output_stream_t *stream); cairo_private cairo_status_t _cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, unsigned char **data_out, unsigned long *length_out); cairo_private cairo_output_stream_t * _cairo_null_stream_create (void); /* cairo-base85-stream.c */ cairo_private cairo_output_stream_t * _cairo_base85_stream_create (cairo_output_stream_t *output); /* cairo-base64-stream.c */ cairo_private cairo_output_stream_t * _cairo_base64_stream_create (cairo_output_stream_t *output); /* cairo-deflate-stream.c */ cairo_private cairo_output_stream_t * _cairo_deflate_stream_create (cairo_output_stream_t *output); #endif /* CAIRO_OUTPUT_STREAM_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-output-stream.c���������������������������0000664�0000000�0000000�00000047264�12710376503�0026405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo-output-stream.c: Output stream abstraction * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Author(s): * Kristian Høgsberg <krh@redhat.com> */ #define _BSD_SOURCE /* for snprintf() */ #include "cairoint.h" #include "cairo-output-stream-private.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-compiler-private.h" #include <stdio.h> #include <locale.h> #include <errno.h> /* Numbers printed with %f are printed with this number of significant * digits after the decimal. */ #define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6 /* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS * bits of precision available after the decimal point. * * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal * digits after the decimal point required to preserve the available * precision. * * The conversion is: * * <programlisting> * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) ) * </programlisting> * * We can replace ceil(x) with (int)(x+1) since x will never be an * integer for any likely value of %CAIRO_FIXED_FRAC_BITS. */ #define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1)) void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func) { stream->write_func = write_func; stream->flush_func = flush_func; stream->close_func = close_func; stream->position = 0; stream->status = CAIRO_STATUS_SUCCESS; stream->closed = FALSE; } cairo_status_t _cairo_output_stream_fini (cairo_output_stream_t *stream) { return _cairo_output_stream_close (stream); } const cairo_output_stream_t _cairo_output_stream_nil = { NULL, /* write_func */ NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_NO_MEMORY, FALSE /* closed */ }; static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { NULL, /* write_func */ NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_WRITE_ERROR, FALSE /* closed */ }; typedef struct _cairo_output_stream_with_closure { cairo_output_stream_t base; cairo_write_func_t write_func; cairo_close_func_t close_func; void *closure; } cairo_output_stream_with_closure_t; static cairo_status_t closure_write (cairo_output_stream_t *stream, const unsigned char *data, unsigned int length) { cairo_output_stream_with_closure_t *stream_with_closure = (cairo_output_stream_with_closure_t *) stream; if (stream_with_closure->write_func == NULL) return CAIRO_STATUS_SUCCESS; return stream_with_closure->write_func (stream_with_closure->closure, data, length); } static cairo_status_t closure_close (cairo_output_stream_t *stream) { cairo_output_stream_with_closure_t *stream_with_closure = (cairo_output_stream_with_closure_t *) stream; if (stream_with_closure->close_func != NULL) return stream_with_closure->close_func (stream_with_closure->closure); else return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_output_stream_create (cairo_write_func_t write_func, cairo_close_func_t close_func, void *closure) { cairo_output_stream_with_closure_t *stream; stream = malloc (sizeof (cairo_output_stream_with_closure_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, closure_write, NULL, closure_close); stream->write_func = write_func; stream->close_func = close_func; stream->closure = closure; return &stream->base; } cairo_output_stream_t * _cairo_output_stream_create_in_error (cairo_status_t status) { cairo_output_stream_t *stream; /* check for the common ones */ if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_output_stream_t *) &_cairo_output_stream_nil; if (status == CAIRO_STATUS_WRITE_ERROR) return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; stream = malloc (sizeof (cairo_output_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (stream, NULL, NULL, NULL); stream->status = status; return stream; } cairo_status_t _cairo_output_stream_flush (cairo_output_stream_t *stream) { cairo_status_t status; if (stream->closed) return stream->status; if (stream == &_cairo_output_stream_nil || stream == &_cairo_output_stream_nil_write_error) { return stream->status; } if (stream->flush_func) { status = stream->flush_func (stream); /* Don't overwrite a pre-existing status failure. */ if (stream->status == CAIRO_STATUS_SUCCESS) stream->status = status; } return stream->status; } cairo_status_t _cairo_output_stream_close (cairo_output_stream_t *stream) { cairo_status_t status; if (stream->closed) return stream->status; if (stream == &_cairo_output_stream_nil || stream == &_cairo_output_stream_nil_write_error) { return stream->status; } if (stream->close_func) { status = stream->close_func (stream); /* Don't overwrite a pre-existing status failure. */ if (stream->status == CAIRO_STATUS_SUCCESS) stream->status = status; } stream->closed = TRUE; return stream->status; } cairo_status_t _cairo_output_stream_destroy (cairo_output_stream_t *stream) { cairo_status_t status; assert (stream != NULL); if (stream == &_cairo_output_stream_nil || stream == &_cairo_output_stream_nil_write_error) { return stream->status; } status = _cairo_output_stream_fini (stream); free (stream); return status; } void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length) { if (length == 0) return; if (stream->status) return; stream->status = stream->write_func (stream, data, length); stream->position += length; } void _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, const unsigned char *data, size_t length) { const char hex_chars[] = "0123456789abcdef"; char buffer[2]; unsigned int i, column; if (stream->status) return; for (i = 0, column = 0; i < length; i++, column++) { if (column == 38) { _cairo_output_stream_write (stream, "\n", 1); column = 0; } buffer[0] = hex_chars[(data[i] >> 4) & 0x0f]; buffer[1] = hex_chars[data[i] & 0x0f]; _cairo_output_stream_write (stream, buffer, 2); } } /* Format a double in a locale independent way and trim trailing * zeros. Based on code from Alex Larson <alexl@redhat.com>. * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html * * The code in the patch is copyright Red Hat, Inc under the LGPL, but * has been relicensed under the LGPL/MPL dual license for inclusion * into cairo (see COPYING). -- Kristian Høgsberg <krh@redhat.com> */ static void _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) { struct lconv *locale_data; const char *decimal_point; int decimal_point_len; char *p; int decimal_len; int num_zeros, decimal_digits; /* Omit the minus sign from negative zero. */ if (d == 0.0) d = 0.0; locale_data = localeconv (); decimal_point = locale_data->decimal_point; decimal_point_len = strlen (decimal_point); assert (decimal_point_len != 0); if (limited_precision) { snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d); } else { /* Using "%f" to print numbers less than 0.1 will result in * reduced precision due to the default 6 digits after the * decimal point. * * For numbers is < 0.1, we print with maximum precision and count * the number of zeros between the decimal point and the first * significant digit. We then print the number again with the * number of decimal places that gives us the required number of * significant digits. This ensures the number is correctly * rounded. */ if (fabs (d) >= 0.1) { snprintf (buffer, size, "%f", d); } else { snprintf (buffer, size, "%.18f", d); p = buffer; if (*p == '+' || *p == '-') p++; while (_cairo_isdigit (*p)) p++; if (strncmp (p, decimal_point, decimal_point_len) == 0) p += decimal_point_len; num_zeros = 0; while (*p++ == '0') num_zeros++; decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL; if (decimal_digits < 18) snprintf (buffer, size, "%.*f", decimal_digits, d); } } p = buffer; if (*p == '+' || *p == '-') p++; while (_cairo_isdigit (*p)) p++; if (strncmp (p, decimal_point, decimal_point_len) == 0) { *p = '.'; decimal_len = strlen (p + decimal_point_len); memmove (p + 1, p + decimal_point_len, decimal_len); p[1 + decimal_len] = 0; /* Remove trailing zeros and decimal point if possible. */ for (p = p + decimal_len; *p == '0'; p--) *p = 0; if (*p == '.') { *p = 0; p--; } } } enum { LENGTH_MODIFIER_LONG = 0x100 }; /* Here's a limited reimplementation of printf. The reason for doing * this is primarily to special case handling of doubles. We want * locale independent formatting of doubles and we want to trim * trailing zeros. This is handled by dtostr() above, and the code * below handles everything else by calling snprintf() to do the * formatting. This functionality is only for internal use and we * only implement the formats we actually use. */ void _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap) { #define SINGLE_FMT_BUFFER_SIZE 32 char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE]; int single_fmt_length; char *p; const char *f, *start; int length_modifier, width; cairo_bool_t var_width; if (stream->status) return; f = fmt; p = buffer; while (*f != '\0') { if (p == buffer + sizeof (buffer)) { _cairo_output_stream_write (stream, buffer, sizeof (buffer)); p = buffer; } if (*f != '%') { *p++ = *f++; continue; } start = f; f++; if (*f == '0') f++; var_width = FALSE; if (*f == '*') { var_width = TRUE; f++; } while (_cairo_isdigit (*f)) f++; length_modifier = 0; if (*f == 'l') { length_modifier = LENGTH_MODIFIER_LONG; f++; } /* The only format strings exist in the cairo implementation * itself. So there's an internal consistency problem if any * of them is larger than our format buffer size. */ single_fmt_length = f - start + 1; assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE); /* Reuse the format string for this conversion. */ memcpy (single_fmt, start, single_fmt_length); single_fmt[single_fmt_length] = '\0'; /* Flush contents of buffer before snprintf()'ing into it. */ _cairo_output_stream_write (stream, buffer, p - buffer); /* We group signed and unsigned together in this switch, the * only thing that matters here is the size of the arguments, * since we're just passing the data through to sprintf(). */ switch (*f | length_modifier) { case '%': buffer[0] = *f; buffer[1] = 0; break; case 'd': case 'u': case 'o': case 'x': case 'X': if (var_width) { width = va_arg (ap, int); snprintf (buffer, sizeof buffer, single_fmt, width, va_arg (ap, int)); } else { snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int)); } break; case 'd' | LENGTH_MODIFIER_LONG: case 'u' | LENGTH_MODIFIER_LONG: case 'o' | LENGTH_MODIFIER_LONG: case 'x' | LENGTH_MODIFIER_LONG: case 'X' | LENGTH_MODIFIER_LONG: if (var_width) { width = va_arg (ap, int); snprintf (buffer, sizeof buffer, single_fmt, width, va_arg (ap, long int)); } else { snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, long int)); } break; case 's': snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, const char *)); break; case 'f': _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE); break; case 'g': _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE); break; case 'c': buffer[0] = va_arg (ap, int); buffer[1] = 0; break; default: ASSERT_NOT_REACHED; } p = buffer + strlen (buffer); f++; } _cairo_output_stream_write (stream, buffer, p - buffer); } void _cairo_output_stream_printf (cairo_output_stream_t *stream, const char *fmt, ...) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (stream, fmt, ap); va_end (ap); } long _cairo_output_stream_get_position (cairo_output_stream_t *stream) { return stream->position; } cairo_status_t _cairo_output_stream_get_status (cairo_output_stream_t *stream) { return stream->status; } /* Maybe this should be a configure time option, so embedded targets * don't have to pull in stdio. */ typedef struct _stdio_stream { cairo_output_stream_t base; FILE *file; } stdio_stream_t; static cairo_status_t stdio_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { stdio_stream_t *stream = (stdio_stream_t *) base; if (fwrite (data, 1, length, stream->file) != length) return _cairo_error (CAIRO_STATUS_WRITE_ERROR); return CAIRO_STATUS_SUCCESS; } static cairo_status_t stdio_flush (cairo_output_stream_t *base) { stdio_stream_t *stream = (stdio_stream_t *) base; fflush (stream->file); if (ferror (stream->file)) return _cairo_error (CAIRO_STATUS_WRITE_ERROR); else return CAIRO_STATUS_SUCCESS; } static cairo_status_t stdio_close (cairo_output_stream_t *base) { cairo_status_t status; stdio_stream_t *stream = (stdio_stream_t *) base; status = stdio_flush (base); fclose (stream->file); return status; } cairo_output_stream_t * _cairo_output_stream_create_for_file (FILE *file) { stdio_stream_t *stream; if (file == NULL) { _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; } stream = malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush, stdio_flush); stream->file = file; return &stream->base; } cairo_output_stream_t * _cairo_output_stream_create_for_filename (const char *filename) { stdio_stream_t *stream; FILE *file; if (filename == NULL) return _cairo_null_stream_create (); file = fopen (filename, "wb"); if (file == NULL) { switch (errno) { case ENOMEM: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; default: _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; } } stream = malloc (sizeof *stream); if (unlikely (stream == NULL)) { fclose (file); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush, stdio_close); stream->file = file; return &stream->base; } typedef struct _memory_stream { cairo_output_stream_t base; cairo_array_t array; } memory_stream_t; static cairo_status_t memory_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { memory_stream_t *stream = (memory_stream_t *) base; return _cairo_array_append_multiple (&stream->array, data, length); } static cairo_status_t memory_close (cairo_output_stream_t *base) { memory_stream_t *stream = (memory_stream_t *) base; _cairo_array_fini (&stream->array); return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_memory_stream_create (void) { memory_stream_t *stream; stream = malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); _cairo_array_init (&stream->array, 1); return &stream->base; } cairo_status_t _cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, unsigned char **data_out, unsigned long *length_out) { memory_stream_t *stream; cairo_status_t status; status = abstract_stream->status; if (unlikely (status)) return _cairo_output_stream_destroy (abstract_stream); stream = (memory_stream_t *) abstract_stream; *length_out = _cairo_array_num_elements (&stream->array); *data_out = malloc (*length_out); if (unlikely (*data_out == NULL)) { status = _cairo_output_stream_destroy (abstract_stream); assert (status == CAIRO_STATUS_SUCCESS); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out); return _cairo_output_stream_destroy (abstract_stream); } void _cairo_memory_stream_copy (cairo_output_stream_t *base, cairo_output_stream_t *dest) { memory_stream_t *stream = (memory_stream_t *) base; if (dest->status) return; if (base->status) { dest->status = base->status; return; } _cairo_output_stream_write (dest, _cairo_array_index (&stream->array, 0), _cairo_array_num_elements (&stream->array)); } int _cairo_memory_stream_length (cairo_output_stream_t *base) { memory_stream_t *stream = (memory_stream_t *) base; return _cairo_array_num_elements (&stream->array); } static cairo_status_t null_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_null_stream_create (void) { cairo_output_stream_t *stream; stream = malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (stream, null_write, NULL, NULL); return stream; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-paginated-private.h�����������������������0000664�0000000�0000000�00000016065�12710376503�0027160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> */ #ifndef CAIRO_PAGINATED_H #define CAIRO_PAGINATED_H #include "cairoint.h" struct _cairo_paginated_surface_backend { /* Optional. Will be called once for each page. * * Note: With respect to the order of drawing operations as seen * by the target, this call will occur before any drawing * operations for the relevant page. However, with respect to the * function calls as made by the user, this call will be *after* * any drawing operations for the page, (that is, it will occur * during the user's call to cairo_show_page or cairo_copy_page). */ cairo_warn cairo_int_status_t (*start_page) (void *surface); /* Required. Will be called twice for each page, once with an * argument of CAIRO_PAGINATED_MODE_ANALYZE and once with * CAIRO_PAGINATED_MODE_RENDER. See more details in the * documentation for _cairo_paginated_surface_create below. */ void (*set_paginated_mode) (void *surface, cairo_paginated_mode_t mode); /* Optional. Specifies the smallest box that encloses all objects * on the page. Will be called at the end of the ANALYZE phase but * before the mode is changed to RENDER. */ cairo_warn cairo_int_status_t (*set_bounding_box) (void *surface, cairo_box_t *bbox); /* Optional. Indicates whether the page requires fallback images. * Will be called at the end of the ANALYZE phase but before the * mode is changed to RENDER. */ cairo_warn cairo_int_status_t (*set_fallback_images_required) (void *surface, cairo_bool_t fallbacks_required); cairo_bool_t (*supports_fine_grained_fallbacks) (void *surface); }; /* A #cairo_paginated_surface_t provides a very convenient wrapper that * is well-suited for doing the analysis common to most surfaces that * have paginated output, (that is, things directed at printers, or * for saving content in files such as PostScript or PDF files). * * To use the paginated surface, you'll first need to create your * 'real' surface using _cairo_surface_init() and the standard * #cairo_surface_backend_t. Then you also call * _cairo_paginated_surface_create which takes its own, much simpler, * #cairo_paginated_surface_backend_t. You are free to return the result * of _cairo_paginated_surface_create() from your public * cairo_<foo>_surface_create(). The paginated backend will be careful * to not let the user see that they really got a "wrapped" * surface. See test-paginated-surface.c for a fairly minimal example * of a paginated-using surface. That should be a reasonable example * to follow. * * What the paginated surface does is first save all drawing * operations for a page into a recording-surface. Then when the user calls * cairo_show_page(), the paginated surface performs the following * sequence of operations (using the backend functions passed to * cairo_paginated_surface_create()): * * 1. Calls start_page() (if not %NULL). At this point, it is appropriate * for the target to emit any page-specific header information into * its output. * * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE * * 3. Replays the recording-surface to the target surface, (with an * analysis surface inserted between which watches the return value * from each operation). This analysis stage is used to decide which * operations will require fallbacks. * * 4. Calls set_bounding_box() to provide the target surface with the * tight bounding box of the page. * * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER * * 6. Replays a subset of the recording-surface operations to the target surface * * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK * * 8. Replays the remaining operations to an image surface, sets an * appropriate clip on the target, then paints the resulting image * surface to the target. * * So, the target will see drawing operations during three separate * stages, (ANALYZE, RENDER and FALLBACK). During the ANALYZE phase * the target should not actually perform any rendering, (for example, * if performing output to a file, no output should be generated * during this stage). Instead the drawing functions simply need to * return %CAIRO_STATUS_SUCCESS or %CAIRO_INT_STATUS_UNSUPPORTED to * indicate whether rendering would be supported. And it should do * this as quickly as possible. The FALLBACK phase allows the surface * to distinguish fallback images from native rendering in case they * need to be handled as a special case. * * Note: The paginated surface layer assumes that the target surface * is "blank" by default at the beginning of each page, without any * need for an explicit erase operation, (as opposed to an image * surface, for example, which might have uninitialized content * originally). As such, it optimizes away CLEAR operations that * happen at the beginning of each page---the target surface will not * even see these operations. */ cairo_private cairo_surface_t * _cairo_paginated_surface_create (cairo_surface_t *target, cairo_content_t content, const cairo_paginated_surface_backend_t *backend); cairo_private cairo_surface_t * _cairo_paginated_surface_get_target (cairo_surface_t *surface); cairo_private cairo_surface_t * _cairo_paginated_surface_get_recording (cairo_surface_t *surface); cairo_private cairo_bool_t _cairo_surface_is_paginated (cairo_surface_t *surface); cairo_private cairo_status_t _cairo_paginated_surface_set_size (cairo_surface_t *surface, int width, int height); #endif /* CAIRO_PAGINATED_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-paginated-surface-private.h���������������0000664�0000000�0000000�00000004242�12710376503�0030600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> */ #ifndef CAIRO_PAGINATED_SURFACE_H #define CAIRO_PAGINATED_SURFACE_H #include "cairo.h" #include "cairo-surface-private.h" typedef struct _cairo_paginated_surface { cairo_surface_t base; /* The target surface to hold the final result. */ cairo_surface_t *target; cairo_content_t content; /* Paginated-surface specific functions for the target */ const cairo_paginated_surface_backend_t *backend; /* A cairo_recording_surface to record all operations. To be replayed * against target, and also against image surface as necessary for * fallbacks. */ cairo_surface_t *recording_surface; int page_num; } cairo_paginated_surface_t; #endif /* CAIRO_PAGINATED_SURFACE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-paginated-surface.c�����������������������0000664�0000000�0000000�00000052721�12710376503�0027130�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> * Keith Packard <keithp@keithp.com> * Adrian Johnson <ajohnson@redneon.com> */ /* The paginated surface layer exists to provide as much code sharing * as possible for the various paginated surface backends in cairo * (PostScript, PDF, etc.). See cairo-paginated-private.h for * more details on how it works and how to use it. */ #include "cairoint.h" #include "cairo-paginated-private.h" #include "cairo-paginated-surface-private.h" #include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-subsurface-inline.h" static const cairo_surface_backend_t cairo_paginated_surface_backend; static cairo_int_status_t _cairo_paginated_surface_show_page (void *abstract_surface); static cairo_surface_t * _cairo_paginated_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_rectangle_t rect; rect.x = rect.y = 0.; rect.width = width; rect.height = height; return cairo_recording_surface_create (content, &rect); } static cairo_surface_t * _create_recording_surface_for_target (cairo_surface_t *target, cairo_content_t content) { cairo_rectangle_int_t rect; if (_cairo_surface_get_extents (target, &rect)) { cairo_rectangle_t recording_extents; recording_extents.x = rect.x; recording_extents.y = rect.y; recording_extents.width = rect.width; recording_extents.height = rect.height; return cairo_recording_surface_create (content, &recording_extents); } else { return cairo_recording_surface_create (content, NULL); } } cairo_surface_t * _cairo_paginated_surface_create (cairo_surface_t *target, cairo_content_t content, const cairo_paginated_surface_backend_t *backend) { cairo_paginated_surface_t *surface; cairo_status_t status; surface = malloc (sizeof (cairo_paginated_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } _cairo_surface_init (&surface->base, &cairo_paginated_surface_backend, NULL, /* device */ content); /* Override surface->base.type with target's type so we don't leak * evidence of the paginated wrapper out to the user. */ surface->base.type = target->type; surface->target = cairo_surface_reference (target); surface->content = content; surface->backend = backend; surface->recording_surface = _create_recording_surface_for_target (target, content); status = surface->recording_surface->status; if (unlikely (status)) goto FAIL_CLEANUP_SURFACE; surface->page_num = 1; surface->base.is_clear = TRUE; return &surface->base; FAIL_CLEANUP_SURFACE: cairo_surface_destroy (target); free (surface); FAIL: return _cairo_surface_create_in_error (status); } cairo_bool_t _cairo_surface_is_paginated (cairo_surface_t *surface) { return surface->backend == &cairo_paginated_surface_backend; } cairo_surface_t * _cairo_paginated_surface_get_target (cairo_surface_t *surface) { cairo_paginated_surface_t *paginated_surface; assert (_cairo_surface_is_paginated (surface)); paginated_surface = (cairo_paginated_surface_t *) surface; return paginated_surface->target; } cairo_surface_t * _cairo_paginated_surface_get_recording (cairo_surface_t *surface) { cairo_paginated_surface_t *paginated_surface; assert (_cairo_surface_is_paginated (surface)); paginated_surface = (cairo_paginated_surface_t *) surface; return paginated_surface->recording_surface; } cairo_status_t _cairo_paginated_surface_set_size (cairo_surface_t *surface, int width, int height) { cairo_paginated_surface_t *paginated_surface; cairo_status_t status; cairo_rectangle_t recording_extents; assert (_cairo_surface_is_paginated (surface)); paginated_surface = (cairo_paginated_surface_t *) surface; recording_extents.x = 0; recording_extents.y = 0; recording_extents.width = width; recording_extents.height = height; cairo_surface_destroy (paginated_surface->recording_surface); paginated_surface->recording_surface = cairo_recording_surface_create (paginated_surface->content, &recording_extents); status = paginated_surface->recording_surface->status; if (unlikely (status)) return _cairo_surface_set_error (surface, status); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_paginated_surface_finish (void *abstract_surface) { cairo_paginated_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (! surface->base.is_clear || surface->page_num == 1) { /* Bypass some of the sanity checking in cairo-surface.c, as we * know that the surface is finished... */ status = _cairo_paginated_surface_show_page (surface); } /* XXX We want to propagate any errors from destroy(), but those are not * returned via the api. So we need to explicitly finish the target, * and check the status afterwards. However, we can only call finish() * on the target, if we own it. */ if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1) cairo_surface_finish (surface->target); if (status == CAIRO_STATUS_SUCCESS) status = cairo_surface_status (surface->target); cairo_surface_destroy (surface->target); cairo_surface_finish (surface->recording_surface); if (status == CAIRO_STATUS_SUCCESS) status = cairo_surface_status (surface->recording_surface); cairo_surface_destroy (surface->recording_surface); return status; } static cairo_surface_t * _cairo_paginated_surface_create_image_surface (void *abstract_surface, int width, int height) { cairo_paginated_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_font_options_t options; image = _cairo_image_surface_create_with_content (surface->content, width, height); cairo_surface_get_font_options (&surface->base, &options); _cairo_surface_set_font_options (image, &options); return image; } static cairo_surface_t * _cairo_paginated_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_get_source (surface->target, extents); } static cairo_status_t _cairo_paginated_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_paginated_surface_t *surface = abstract_surface; cairo_bool_t is_bounded; cairo_surface_t *image; cairo_status_t status; cairo_rectangle_int_t extents; is_bounded = _cairo_surface_get_extents (surface->target, &extents); if (! is_bounded) return CAIRO_INT_STATUS_UNSUPPORTED; image = _cairo_paginated_surface_create_image_surface (surface, extents.width, extents.height); status = _cairo_recording_surface_replay (surface->recording_surface, image); if (unlikely (status)) { cairo_surface_destroy (image); return status; } *image_out = (cairo_image_surface_t*) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_paginated_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_int_status_t _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_rectangle_int_t *rect) { double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; int x, y, width, height; cairo_status_t status; cairo_surface_t *image; cairo_surface_pattern_t pattern; cairo_clip_t *clip; x = rect->x; y = rect->y; width = rect->width; height = rect->height; image = _cairo_paginated_surface_create_image_surface (surface, ceil (width * x_scale), ceil (height * y_scale)); _cairo_surface_set_device_scale (image, x_scale, y_scale); /* set_device_offset just sets the x0/y0 components of the matrix; * so we have to do the scaling manually. */ cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); status = _cairo_recording_surface_replay (surface->recording_surface, image); if (unlikely (status)) goto CLEANUP_IMAGE; _cairo_pattern_init_for_surface (&pattern, image); cairo_matrix_init (&pattern.base.matrix, x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); /* the fallback should be rendered at native resolution, so disable * filtering (if possible) to avoid introducing potential artifacts. */ pattern.base.filter = CAIRO_FILTER_NEAREST; clip = _cairo_clip_intersect_rectangle (NULL, rect); status = _cairo_surface_paint (surface->target, CAIRO_OPERATOR_SOURCE, &pattern.base, clip); _cairo_clip_destroy (clip); _cairo_pattern_fini (&pattern.base); CLEANUP_IMAGE: cairo_surface_destroy (image); return status; } static cairo_int_status_t _paint_page (cairo_paginated_surface_t *surface) { cairo_surface_t *analysis; cairo_int_status_t status; cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; if (unlikely (surface->target->status)) return surface->target->status; analysis = _cairo_analysis_surface_create (surface->target); if (unlikely (analysis->status)) return _cairo_surface_set_error (surface->target, analysis->status); surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, analysis); if (status) goto FAIL; assert (analysis->status == CAIRO_STATUS_SUCCESS); if (surface->backend->set_bounding_box) { cairo_box_t bbox; _cairo_analysis_surface_get_bounding_box (analysis, &bbox); status = surface->backend->set_bounding_box (surface->target, &bbox); if (unlikely (status)) goto FAIL; } if (surface->backend->set_fallback_images_required) { cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis); status = surface->backend->set_fallback_images_required (surface->target, has_fallbacks); if (unlikely (status)) goto FAIL; } /* Finer grained fallbacks are currently only supported for some * surface types */ if (surface->backend->supports_fine_grained_fallbacks != NULL && surface->backend->supports_fine_grained_fallbacks (surface->target)) { has_supported = _cairo_analysis_surface_has_supported (analysis); has_page_fallback = FALSE; has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis); } else { if (_cairo_analysis_surface_has_unsupported (analysis)) { has_supported = FALSE; has_page_fallback = TRUE; } else { has_supported = TRUE; has_page_fallback = FALSE; } has_finegrained_fallback = FALSE; } if (has_supported) { surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER); status = _cairo_recording_surface_replay_region (surface->recording_surface, NULL, surface->target, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) goto FAIL; } if (has_page_fallback) { cairo_rectangle_int_t extents; cairo_bool_t is_bounded; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); is_bounded = _cairo_surface_get_extents (surface->target, &extents); if (! is_bounded) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; } status = _paint_fallback_image (surface, &extents); if (unlikely (status)) goto FAIL; } if (has_finegrained_fallback) { cairo_region_t *region; int num_rects, i; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); region = _cairo_analysis_surface_get_unsupported (analysis); num_rects = cairo_region_num_rectangles (region); for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); status = _paint_fallback_image (surface, &rect); if (unlikely (status)) goto FAIL; } } FAIL: cairo_surface_destroy (analysis); return _cairo_surface_set_error (surface->target, status); } static cairo_status_t _start_page (cairo_paginated_surface_t *surface) { if (surface->target->status) return surface->target->status; if (! surface->backend->start_page) return CAIRO_STATUS_SUCCESS; return _cairo_surface_set_error (surface->target, surface->backend->start_page (surface->target)); } static cairo_int_status_t _cairo_paginated_surface_copy_page (void *abstract_surface) { cairo_status_t status; cairo_paginated_surface_t *surface = abstract_surface; status = _start_page (surface); if (unlikely (status)) return status; status = _paint_page (surface); if (unlikely (status)) return status; surface->page_num++; /* XXX: It might make sense to add some support here for calling * cairo_surface_copy_page on the target surface. It would be an * optimization for the output, but the interaction with image * fallbacks gets tricky. For now, we just let the target see a * show_page and we implement the copying by simply not destroying * the recording-surface. */ cairo_surface_show_page (surface->target); return cairo_surface_status (surface->target); } static cairo_int_status_t _cairo_paginated_surface_show_page (void *abstract_surface) { cairo_status_t status; cairo_paginated_surface_t *surface = abstract_surface; status = _start_page (surface); if (unlikely (status)) return status; status = _paint_page (surface); if (unlikely (status)) return status; cairo_surface_show_page (surface->target); status = surface->target->status; if (unlikely (status)) return status; status = surface->recording_surface->status; if (unlikely (status)) return status; if (! surface->base.finished) { cairo_surface_destroy (surface->recording_surface); surface->recording_surface = _create_recording_surface_for_target (surface->target, surface->content); status = surface->recording_surface->status; if (unlikely (status)) return status; surface->page_num++; surface->base.is_clear = TRUE; } return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_paginated_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_get_extents (surface->target, rectangle); } static void _cairo_paginated_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_paginated_surface_t *surface = abstract_surface; cairo_surface_get_font_options (surface->target, options); } static cairo_int_status_t _cairo_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_paint (surface->recording_surface, op, source, clip); } static cairo_int_status_t _cairo_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip); } static cairo_int_status_t _cairo_paginated_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_stroke (surface->recording_surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_paginated_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_fill (surface->recording_surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_bool_t _cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) { cairo_paginated_surface_t *surface = abstract_surface; return cairo_surface_has_show_text_glyphs (surface->target); } static cairo_int_status_t _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); } static const char ** _cairo_paginated_surface_get_supported_mime_types (void *abstract_surface) { cairo_paginated_surface_t *surface = abstract_surface; if (surface->target->backend->get_supported_mime_types) return surface->target->backend->get_supported_mime_types (surface->target); return NULL; } static cairo_surface_t * _cairo_paginated_surface_snapshot (void *abstract_other) { cairo_paginated_surface_t *other = abstract_other; return other->recording_surface->backend->snapshot (other->recording_surface); } static cairo_t * _cairo_paginated_context_create (void *target) { cairo_paginated_surface_t *surface = target; if (_cairo_surface_is_subsurface (&surface->base)) surface = (cairo_paginated_surface_t *) _cairo_surface_subsurface_get_target (&surface->base); return surface->recording_surface->backend->create_context (target); } static const cairo_surface_backend_t cairo_paginated_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, _cairo_paginated_surface_finish, _cairo_paginated_context_create, _cairo_paginated_surface_create_similar, NULL, /* create simlar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_paginated_surface_source, _cairo_paginated_surface_acquire_source_image, _cairo_paginated_surface_release_source_image, _cairo_paginated_surface_snapshot, _cairo_paginated_surface_copy_page, _cairo_paginated_surface_show_page, _cairo_paginated_surface_get_extents, _cairo_paginated_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_paginated_surface_paint, _cairo_paginated_surface_mask, _cairo_paginated_surface_stroke, _cairo_paginated_surface_fill, NULL, /* fill_stroke */ NULL, /* show_glyphs */ _cairo_paginated_surface_has_show_text_glyphs, _cairo_paginated_surface_show_text_glyphs, _cairo_paginated_surface_get_supported_mime_types, }; �����������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-bounds.c�����������������������������0000664�0000000�0000000�00000013601�12710376503�0025764�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" typedef struct _cairo_path_bounder { cairo_point_t current_point; cairo_bool_t has_extents; cairo_box_t extents; } cairo_path_bounder_t; static cairo_status_t _cairo_path_bounder_move_to (void *closure, const cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; bounder->current_point = *point; if (likely (bounder->has_extents)) { _cairo_box_add_point (&bounder->extents, point); } else { bounder->has_extents = TRUE; _cairo_box_set (&bounder->extents, point, point); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_bounder_line_to (void *closure, const cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; bounder->current_point = *point; _cairo_box_add_point (&bounder->extents, point); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_bounder_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_path_bounder_t *bounder = closure; _cairo_box_add_curve_to (&bounder->extents, &bounder->current_point, b, c, d); bounder->current_point = *d; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_bounder_close_path (void *closure) { return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_path_bounder_extents (const cairo_path_fixed_t *path, cairo_box_t *extents) { cairo_path_bounder_t bounder; cairo_status_t status; bounder.has_extents = FALSE; status = _cairo_path_fixed_interpret (path, _cairo_path_bounder_move_to, _cairo_path_bounder_line_to, _cairo_path_bounder_curve_to, _cairo_path_bounder_close_path, &bounder); assert (!status); if (bounder.has_extents) *extents = bounder.extents; return bounder.has_extents; } void _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents) { _cairo_path_fixed_approximate_fill_extents (path, extents); } void _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents) { _cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents); } void _cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_rectangle_int_t *extents) { if (path->extents.p1.x < path->extents.p2.x && path->extents.p1.y < path->extents.p2.y) { _cairo_box_round_to_rectangle (&path->extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } } /* Adjusts the fill extents (above) by the device-space pen. */ void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, cairo_rectangle_int_t *extents) { if (path->has_extents) { cairo_box_t box_extents; double dx, dy; _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); box_extents = path->extents; box_extents.p1.x -= _cairo_fixed_from_double (dx); box_extents.p1.y -= _cairo_fixed_from_double (dy); box_extents.p2.x += _cairo_fixed_from_double (dx); box_extents.p2.y += _cairo_fixed_from_double (dy); _cairo_box_round_to_rectangle (&box_extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } } cairo_status_t _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_rectangle_int_t *extents) { cairo_polygon_t polygon; cairo_status_t status; _cairo_polygon_init (&polygon, NULL, 0); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &polygon); _cairo_box_round_to_rectangle (&polygon.extents, extents); _cairo_polygon_fini (&polygon); return status; } cairo_bool_t _cairo_path_fixed_extents (const cairo_path_fixed_t *path, cairo_box_t *box) { *box = path->extents; return path->has_extents; } �������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-fill.c�������������������������������0000664�0000000�0000000�00000022324�12710376503�0025422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-region-private.h" #include "cairo-traps-private.h" typedef struct cairo_filler { cairo_polygon_t *polygon; double tolerance; cairo_box_t limit; cairo_bool_t has_limits; cairo_point_t current_point; cairo_point_t last_move_to; } cairo_filler_t; static cairo_status_t _cairo_filler_line_to (void *closure, const cairo_point_t *point) { cairo_filler_t *filler = closure; cairo_status_t status; status = _cairo_polygon_add_external_edge (filler->polygon, &filler->current_point, point); filler->current_point = *point; return status; } static cairo_status_t _cairo_filler_close (void *closure) { cairo_filler_t *filler = closure; /* close the subpath */ return _cairo_filler_line_to (closure, &filler->last_move_to); } static cairo_status_t _cairo_filler_move_to (void *closure, const cairo_point_t *point) { cairo_filler_t *filler = closure; cairo_status_t status; /* close current subpath */ status = _cairo_filler_close (closure); if (unlikely (status)) return status; /* make sure that the closure represents a degenerate path */ filler->current_point = *point; filler->last_move_to = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_filler_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { cairo_filler_t *filler = closure; cairo_spline_t spline; if (filler->has_limits) { if (! _cairo_spline_intersects (&filler->current_point, p1, p2, p3, &filler->limit)) return _cairo_filler_line_to (filler, p3); } if (! _cairo_spline_init (&spline, (cairo_spline_add_point_func_t)_cairo_filler_line_to, filler, &filler->current_point, p1, p2, p3)) { return _cairo_filler_line_to (closure, p3); } return _cairo_spline_decompose (&spline, filler->tolerance); } cairo_status_t _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, double tolerance, cairo_polygon_t *polygon) { cairo_filler_t filler; cairo_status_t status; filler.polygon = polygon; filler.tolerance = tolerance; filler.has_limits = FALSE; if (polygon->num_limits) { filler.has_limits = TRUE; filler.limit = polygon->limit; } /* make sure that the closure represents a degenerate path */ filler.current_point.x = 0; filler.current_point.y = 0; filler.last_move_to = filler.current_point; status = _cairo_path_fixed_interpret (path, _cairo_filler_move_to, _cairo_filler_line_to, _cairo_filler_curve_to, _cairo_filler_close, &filler); if (unlikely (status)) return status; return _cairo_filler_close (&filler); } typedef struct cairo_filler_rectilinear_aligned { cairo_polygon_t *polygon; cairo_point_t current_point; cairo_point_t last_move_to; } cairo_filler_ra_t; static cairo_status_t _cairo_filler_ra_line_to (void *closure, const cairo_point_t *point) { cairo_filler_ra_t *filler = closure; cairo_status_t status; cairo_point_t p; p.x = _cairo_fixed_round_down (point->x); p.y = _cairo_fixed_round_down (point->y); status = _cairo_polygon_add_external_edge (filler->polygon, &filler->current_point, &p); filler->current_point = p; return status; } static cairo_status_t _cairo_filler_ra_close (void *closure) { cairo_filler_ra_t *filler = closure; return _cairo_filler_ra_line_to (closure, &filler->last_move_to); } static cairo_status_t _cairo_filler_ra_move_to (void *closure, const cairo_point_t *point) { cairo_filler_ra_t *filler = closure; cairo_status_t status; cairo_point_t p; /* close current subpath */ status = _cairo_filler_ra_close (closure); if (unlikely (status)) return status; p.x = _cairo_fixed_round_down (point->x); p.y = _cairo_fixed_round_down (point->y); /* make sure that the closure represents a degenerate path */ filler->current_point = p; filler->last_move_to = p; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path, cairo_antialias_t antialias, cairo_polygon_t *polygon) { cairo_filler_ra_t filler; cairo_status_t status; if (antialias != CAIRO_ANTIALIAS_NONE) return _cairo_path_fixed_fill_to_polygon (path, 0., polygon); filler.polygon = polygon; /* make sure that the closure represents a degenerate path */ filler.current_point.x = 0; filler.current_point.y = 0; filler.last_move_to = filler.current_point; status = _cairo_path_fixed_interpret_flat (path, _cairo_filler_ra_move_to, _cairo_filler_ra_line_to, _cairo_filler_ra_close, &filler, 0.); if (unlikely (status)) return status; return _cairo_filler_ra_close (&filler); } cairo_status_t _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_traps_t *traps) { cairo_polygon_t polygon; cairo_status_t status; if (_cairo_path_fixed_fill_is_empty (path)) return CAIRO_STATUS_SUCCESS; _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); if (unlikely (status || polygon.num_edges == 0)) goto CLEANUP; status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, fill_rule); CLEANUP: _cairo_polygon_fini (&polygon); return status; } static cairo_status_t _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_polygon_t polygon; cairo_status_t status; _cairo_polygon_init (&polygon, boxes->limits, boxes->num_limits); boxes->num_limits = 0; /* tolerance will be ignored as the path is rectilinear */ status = _cairo_path_fixed_fill_rectilinear_to_polygon (path, antialias, &polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon, fill_rule, boxes); } _cairo_polygon_fini (&polygon); return status; } cairo_status_t _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_path_fixed_iter_t iter; cairo_status_t status; cairo_box_t box; if (_cairo_path_fixed_is_box (path, &box)) return _cairo_boxes_add (boxes, antialias, &box); _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) continue; if (box.p1.y > box.p2.y) { cairo_fixed_t t; t = box.p1.y; box.p1.y = box.p2.y; box.p2.y = t; t = box.p1.x; box.p1.x = box.p2.x; box.p2.x = t; } status = _cairo_boxes_add (boxes, antialias, &box); if (unlikely (status)) return status; } if (_cairo_path_fixed_iter_at_end (&iter)) return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes); /* path is not rectangular, try extracting clipped rectilinear edges */ _cairo_boxes_clear (boxes); return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path, fill_rule, antialias, boxes); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-fixed-private.h����������������������0000664�0000000�0000000�00000014365�12710376503�0027256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_PATH_FIXED_PRIVATE_H #define CAIRO_PATH_FIXED_PRIVATE_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" #include "cairo-list-private.h" #define WATCH_PATH 0 #if WATCH_PATH #include <stdio.h> #endif enum cairo_path_op { CAIRO_PATH_OP_MOVE_TO = 0, CAIRO_PATH_OP_LINE_TO = 1, CAIRO_PATH_OP_CURVE_TO = 2, CAIRO_PATH_OP_CLOSE_PATH = 3 }; /* we want to make sure a single byte is used for the enum */ typedef char cairo_path_op_t; /* make _cairo_path_fixed fit into ~512 bytes -- about 50 items */ #define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \ / (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t))) #define cairo_path_head(path__) (&(path__)->buf.base) #define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__)) #define cairo_path_buf_next(pos__) \ cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link) #define cairo_path_buf_prev(pos__) \ cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link) #define cairo_path_foreach_buf_start(pos__, path__) \ pos__ = cairo_path_head (path__); do #define cairo_path_foreach_buf_end(pos__, path__) \ while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__)) typedef struct _cairo_path_buf { cairo_list_t link; unsigned int num_ops; unsigned int size_ops; unsigned int num_points; unsigned int size_points; cairo_path_op_t *op; cairo_point_t *points; } cairo_path_buf_t; typedef struct _cairo_path_buf_fixed { cairo_path_buf_t base; cairo_path_op_t op[CAIRO_PATH_BUF_SIZE]; cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE]; } cairo_path_buf_fixed_t; /* NOTES: has_curve_to => !stroke_is_rectilinear fill_is_rectilinear => stroke_is_rectilinear fill_is_empty => fill_is_rectilinear fill_maybe_region => fill_is_rectilinear */ struct _cairo_path_fixed { cairo_point_t last_move_point; cairo_point_t current_point; unsigned int has_current_point : 1; unsigned int needs_move_to : 1; unsigned int has_extents : 1; unsigned int has_curve_to : 1; unsigned int stroke_is_rectilinear : 1; unsigned int fill_is_rectilinear : 1; unsigned int fill_maybe_region : 1; unsigned int fill_is_empty : 1; cairo_box_t extents; cairo_path_buf_fixed_t buf; }; cairo_private void _cairo_path_fixed_translate (cairo_path_fixed_t *path, cairo_fixed_t offx, cairo_fixed_t offy); cairo_private cairo_status_t _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, cairo_fixed_t tx, cairo_fixed_t ty); cairo_private unsigned long _cairo_path_fixed_hash (const cairo_path_fixed_t *path); cairo_private unsigned long _cairo_path_fixed_size (const cairo_path_fixed_t *path); cairo_private cairo_bool_t _cairo_path_fixed_equal (const cairo_path_fixed_t *a, const cairo_path_fixed_t *b); typedef struct _cairo_path_fixed_iter { const cairo_path_buf_t *first; const cairo_path_buf_t *buf; unsigned int n_op; unsigned int n_point; } cairo_path_fixed_iter_t; cairo_private void _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, const cairo_path_fixed_t *path); cairo_private cairo_bool_t _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, cairo_box_t *box); cairo_private cairo_bool_t _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); static inline cairo_bool_t _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) { return path->fill_is_empty; } static inline cairo_bool_t _cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path) { if (! path->fill_is_rectilinear) return 0; if (! path->has_current_point || path->needs_move_to) return 1; /* check whether the implicit close preserves the rectilinear property */ return path->current_point.x == path->last_move_point.x || path->current_point.y == path->last_move_point.y; } static inline cairo_bool_t _cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path) { return path->stroke_is_rectilinear; } static inline cairo_bool_t _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path) { if (! path->fill_maybe_region) return 0; if (! path->has_current_point || path->needs_move_to) return 1; /* check whether the implicit close preserves the rectilinear property * (the integer point property is automatically preserved) */ return path->current_point.x == path->last_move_point.x || path->current_point.y == path->last_move_point.y; } cairo_private cairo_bool_t _cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path, cairo_box_t *box); cairo_private cairo_bool_t _cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path); #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-fixed.c������������������������������0000664�0000000�0000000�00000126770�12710376503�0025605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" static cairo_status_t _cairo_path_fixed_add (cairo_path_fixed_t *path, cairo_path_op_t op, const cairo_point_t *points, int num_points); static void _cairo_path_fixed_add_buf (cairo_path_fixed_t *path, cairo_path_buf_t *buf); static cairo_path_buf_t * _cairo_path_buf_create (int size_ops, int size_points); static void _cairo_path_buf_destroy (cairo_path_buf_t *buf); static void _cairo_path_buf_add_op (cairo_path_buf_t *buf, cairo_path_op_t op); static void _cairo_path_buf_add_points (cairo_path_buf_t *buf, const cairo_point_t *points, int num_points); void _cairo_path_fixed_init (cairo_path_fixed_t *path) { VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); cairo_list_init (&path->buf.base.link); path->buf.base.num_ops = 0; path->buf.base.num_points = 0; path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); path->buf.base.op = path->buf.op; path->buf.base.points = path->buf.points; path->current_point.x = 0; path->current_point.y = 0; path->last_move_point = path->current_point; path->has_current_point = FALSE; path->needs_move_to = TRUE; path->has_extents = FALSE; path->has_curve_to = FALSE; path->stroke_is_rectilinear = TRUE; path->fill_is_rectilinear = TRUE; path->fill_maybe_region = TRUE; path->fill_is_empty = TRUE; path->extents.p1.x = path->extents.p1.y = 0; path->extents.p2.x = path->extents.p2.y = 0; } cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, const cairo_path_fixed_t *other) { cairo_path_buf_t *buf, *other_buf; unsigned int num_points, num_ops; VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); cairo_list_init (&path->buf.base.link); path->buf.base.op = path->buf.op; path->buf.base.points = path->buf.points; path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); path->current_point = other->current_point; path->last_move_point = other->last_move_point; path->has_current_point = other->has_current_point; path->needs_move_to = other->needs_move_to; path->has_extents = other->has_extents; path->has_curve_to = other->has_curve_to; path->stroke_is_rectilinear = other->stroke_is_rectilinear; path->fill_is_rectilinear = other->fill_is_rectilinear; path->fill_maybe_region = other->fill_maybe_region; path->fill_is_empty = other->fill_is_empty; path->extents = other->extents; path->buf.base.num_ops = other->buf.base.num_ops; path->buf.base.num_points = other->buf.base.num_points; memcpy (path->buf.op, other->buf.base.op, other->buf.base.num_ops * sizeof (other->buf.op[0])); memcpy (path->buf.points, other->buf.points, other->buf.base.num_points * sizeof (other->buf.points[0])); num_points = num_ops = 0; for (other_buf = cairo_path_buf_next (cairo_path_head (other)); other_buf != cairo_path_head (other); other_buf = cairo_path_buf_next (other_buf)) { num_ops += other_buf->num_ops; num_points += other_buf->num_points; } if (num_ops) { buf = _cairo_path_buf_create (num_ops, num_points); if (unlikely (buf == NULL)) { _cairo_path_fixed_fini (path); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (other_buf = cairo_path_buf_next (cairo_path_head (other)); other_buf != cairo_path_head (other); other_buf = cairo_path_buf_next (other_buf)) { memcpy (buf->op + buf->num_ops, other_buf->op, other_buf->num_ops * sizeof (buf->op[0])); buf->num_ops += other_buf->num_ops; memcpy (buf->points + buf->num_points, other_buf->points, other_buf->num_points * sizeof (buf->points[0])); buf->num_points += other_buf->num_points; } _cairo_path_fixed_add_buf (path, buf); } return CAIRO_STATUS_SUCCESS; } unsigned long _cairo_path_fixed_hash (const cairo_path_fixed_t *path) { unsigned long hash = _CAIRO_HASH_INIT_VALUE; const cairo_path_buf_t *buf; unsigned int count; count = 0; cairo_path_foreach_buf_start (buf, path) { hash = _cairo_hash_bytes (hash, buf->op, buf->num_ops * sizeof (buf->op[0])); count += buf->num_ops; } cairo_path_foreach_buf_end (buf, path); hash = _cairo_hash_bytes (hash, &count, sizeof (count)); count = 0; cairo_path_foreach_buf_start (buf, path) { hash = _cairo_hash_bytes (hash, buf->points, buf->num_points * sizeof (buf->points[0])); count += buf->num_points; } cairo_path_foreach_buf_end (buf, path); hash = _cairo_hash_bytes (hash, &count, sizeof (count)); return hash; } unsigned long _cairo_path_fixed_size (const cairo_path_fixed_t *path) { const cairo_path_buf_t *buf; int num_points, num_ops; num_ops = num_points = 0; cairo_path_foreach_buf_start (buf, path) { num_ops += buf->num_ops; num_points += buf->num_points; } cairo_path_foreach_buf_end (buf, path); return num_ops * sizeof (buf->op[0]) + num_points * sizeof (buf->points[0]); } cairo_bool_t _cairo_path_fixed_equal (const cairo_path_fixed_t *a, const cairo_path_fixed_t *b) { const cairo_path_buf_t *buf_a, *buf_b; const cairo_path_op_t *ops_a, *ops_b; const cairo_point_t *points_a, *points_b; int num_points_a, num_ops_a; int num_points_b, num_ops_b; if (a == b) return TRUE; /* use the flags to quickly differentiate based on contents */ if (a->has_curve_to != b->has_curve_to) { return FALSE; } if (a->extents.p1.x != b->extents.p1.x || a->extents.p1.y != b->extents.p1.y || a->extents.p2.x != b->extents.p2.x || a->extents.p2.y != b->extents.p2.y) { return FALSE; } num_ops_a = num_points_a = 0; cairo_path_foreach_buf_start (buf_a, a) { num_ops_a += buf_a->num_ops; num_points_a += buf_a->num_points; } cairo_path_foreach_buf_end (buf_a, a); num_ops_b = num_points_b = 0; cairo_path_foreach_buf_start (buf_b, b) { num_ops_b += buf_b->num_ops; num_points_b += buf_b->num_points; } cairo_path_foreach_buf_end (buf_b, b); if (num_ops_a == 0 && num_ops_b == 0) return TRUE; if (num_ops_a != num_ops_b || num_points_a != num_points_b) return FALSE; buf_a = cairo_path_head (a); num_points_a = buf_a->num_points; num_ops_a = buf_a->num_ops; ops_a = buf_a->op; points_a = buf_a->points; buf_b = cairo_path_head (b); num_points_b = buf_b->num_points; num_ops_b = buf_b->num_ops; ops_b = buf_b->op; points_b = buf_b->points; while (TRUE) { int num_ops = MIN (num_ops_a, num_ops_b); int num_points = MIN (num_points_a, num_points_b); if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) return FALSE; if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) return FALSE; num_ops_a -= num_ops; ops_a += num_ops; num_points_a -= num_points; points_a += num_points; if (num_ops_a == 0 || num_points_a == 0) { if (num_ops_a || num_points_a) return FALSE; buf_a = cairo_path_buf_next (buf_a); if (buf_a == cairo_path_head (a)) break; num_points_a = buf_a->num_points; num_ops_a = buf_a->num_ops; ops_a = buf_a->op; points_a = buf_a->points; } num_ops_b -= num_ops; ops_b += num_ops; num_points_b -= num_points; points_b += num_points; if (num_ops_b == 0 || num_points_b == 0) { if (num_ops_b || num_points_b) return FALSE; buf_b = cairo_path_buf_next (buf_b); if (buf_b == cairo_path_head (b)) break; num_points_b = buf_b->num_points; num_ops_b = buf_b->num_ops; ops_b = buf_b->op; points_b = buf_b->points; } } return TRUE; } cairo_path_fixed_t * _cairo_path_fixed_create (void) { cairo_path_fixed_t *path; path = malloc (sizeof (cairo_path_fixed_t)); if (!path) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } _cairo_path_fixed_init (path); return path; } void _cairo_path_fixed_fini (cairo_path_fixed_t *path) { cairo_path_buf_t *buf; buf = cairo_path_buf_next (cairo_path_head (path)); while (buf != cairo_path_head (path)) { cairo_path_buf_t *this = buf; buf = cairo_path_buf_next (buf); _cairo_path_buf_destroy (this); } VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t))); } void _cairo_path_fixed_destroy (cairo_path_fixed_t *path) { _cairo_path_fixed_fini (path); free (path); } static cairo_path_op_t _cairo_path_fixed_last_op (cairo_path_fixed_t *path) { cairo_path_buf_t *buf; buf = cairo_path_tail (path); assert (buf->num_ops != 0); return buf->op[buf->num_ops - 1]; } static inline const cairo_point_t * _cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path) { cairo_path_buf_t *buf; buf = cairo_path_tail (path); if (likely (buf->num_points >= 2)) { return &buf->points[buf->num_points - 2]; } else { cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); assert (prev_buf->num_points >= 2 - buf->num_points); return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; } } static void _cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path) { cairo_path_buf_t *buf; assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO); buf = cairo_path_tail (path); buf->num_points--; buf->num_ops--; } cairo_status_t _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y) { _cairo_path_fixed_new_sub_path (path); path->has_current_point = TRUE; path->current_point.x = x; path->current_point.y = y; path->last_move_point = path->current_point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_fixed_move_to_apply (cairo_path_fixed_t *path) { if (likely (! path->needs_move_to)) return CAIRO_STATUS_SUCCESS; path->needs_move_to = FALSE; if (path->has_extents) { _cairo_box_add_point (&path->extents, &path->current_point); } else { _cairo_box_set (&path->extents, &path->current_point, &path->current_point); path->has_extents = TRUE; } if (path->fill_maybe_region) { path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) && _cairo_fixed_is_integer (path->current_point.y); } path->last_move_point = path->current_point; return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1); } void _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path) { if (! path->needs_move_to) { /* If the current subpath doesn't need_move_to, it contains at least one command */ if (path->fill_is_rectilinear) { /* Implicitly close for fill */ path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x || path->current_point.y == path->last_move_point.y; path->fill_maybe_region &= path->fill_is_rectilinear; } path->needs_move_to = TRUE; } path->has_current_point = FALSE; } cairo_status_t _cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, cairo_fixed_t dx, cairo_fixed_t dy) { if (unlikely (! path->has_current_point)) return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); return _cairo_path_fixed_move_to (path, path->current_point.x + dx, path->current_point.y + dy); } cairo_status_t _cairo_path_fixed_line_to (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y) { cairo_status_t status; cairo_point_t point; point.x = x; point.y = y; /* When there is not yet a current point, the line_to operation * becomes a move_to instead. Note: We have to do this by * explicitly calling into _cairo_path_fixed_move_to to ensure * that the last_move_point state is updated properly. */ if (! path->has_current_point) return _cairo_path_fixed_move_to (path, point.x, point.y); status = _cairo_path_fixed_move_to_apply (path); if (unlikely (status)) return status; /* If the previous op was but the initial MOVE_TO and this segment * is degenerate, then we can simply skip this point. Note that * a move-to followed by a degenerate line-to is a valid path for * stroking, but at all other times is simply a degenerate segment. */ if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { if (x == path->current_point.x && y == path->current_point.y) return CAIRO_STATUS_SUCCESS; } /* If the previous op was also a LINE_TO with the same gradient, * then just change its end-point rather than adding a new op. */ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) { const cairo_point_t *p; p = _cairo_path_fixed_penultimate_point (path); if (p->x == path->current_point.x && p->y == path->current_point.y) { /* previous line element was degenerate, replace */ _cairo_path_fixed_drop_line_to (path); } else { cairo_slope_t prev, self; _cairo_slope_init (&prev, p, &path->current_point); _cairo_slope_init (&self, &path->current_point, &point); if (_cairo_slope_equal (&prev, &self) && /* cannot trim anti-parallel segments whilst stroking */ ! _cairo_slope_backwards (&prev, &self)) { _cairo_path_fixed_drop_line_to (path); /* In this case the flags might be more restrictive than * what we actually need. * When changing the flags definition we should check if * changing the line_to point can affect them. */ } } } if (path->stroke_is_rectilinear) { path->stroke_is_rectilinear = path->current_point.x == x || path->current_point.y == y; path->fill_is_rectilinear &= path->stroke_is_rectilinear; path->fill_maybe_region &= path->fill_is_rectilinear; if (path->fill_maybe_region) { path->fill_maybe_region = _cairo_fixed_is_integer (x) && _cairo_fixed_is_integer (y); } if (path->fill_is_empty) { path->fill_is_empty = path->current_point.x == x && path->current_point.y == y; } } path->current_point = point; _cairo_box_add_point (&path->extents, &point); return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); } cairo_status_t _cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, cairo_fixed_t dx, cairo_fixed_t dy) { if (unlikely (! path->has_current_point)) return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); return _cairo_path_fixed_line_to (path, path->current_point.x + dx, path->current_point.y + dy); } cairo_status_t _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, cairo_fixed_t x0, cairo_fixed_t y0, cairo_fixed_t x1, cairo_fixed_t y1, cairo_fixed_t x2, cairo_fixed_t y2) { cairo_status_t status; cairo_point_t point[3]; /* If this curves does not move, replace it with a line-to. * This frequently happens with rounded-rectangles and r==0. */ if (path->current_point.x == x2 && path->current_point.y == y2) { if (x1 == x2 && x0 == x2 && y1 == y2 && y0 == y2) return _cairo_path_fixed_line_to (path, x2, y2); /* We may want to check for the absence of a cusp, in which case * we can also replace the curve-to with a line-to. */ } /* make sure subpaths are started properly */ if (! path->has_current_point) { status = _cairo_path_fixed_move_to (path, x0, y0); assert (status == CAIRO_STATUS_SUCCESS); } status = _cairo_path_fixed_move_to_apply (path); if (unlikely (status)) return status; /* If the previous op was a degenerate LINE_TO, drop it. */ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) { const cairo_point_t *p; p = _cairo_path_fixed_penultimate_point (path); if (p->x == path->current_point.x && p->y == path->current_point.y) { /* previous line element was degenerate, replace */ _cairo_path_fixed_drop_line_to (path); } } point[0].x = x0; point[0].y = y0; point[1].x = x1; point[1].y = y1; point[2].x = x2; point[2].y = y2; _cairo_box_add_curve_to (&path->extents, &path->current_point, &point[0], &point[1], &point[2]); path->current_point = point[2]; path->has_curve_to = TRUE; path->stroke_is_rectilinear = FALSE; path->fill_is_rectilinear = FALSE; path->fill_maybe_region = FALSE; path->fill_is_empty = FALSE; return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); } cairo_status_t _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, cairo_fixed_t dx0, cairo_fixed_t dy0, cairo_fixed_t dx1, cairo_fixed_t dy1, cairo_fixed_t dx2, cairo_fixed_t dy2) { if (unlikely (! path->has_current_point)) return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); return _cairo_path_fixed_curve_to (path, path->current_point.x + dx0, path->current_point.y + dy0, path->current_point.x + dx1, path->current_point.y + dy1, path->current_point.x + dx2, path->current_point.y + dy2); } cairo_status_t _cairo_path_fixed_close_path (cairo_path_fixed_t *path) { cairo_status_t status; if (! path->has_current_point) return CAIRO_STATUS_SUCCESS; /* * Add a line_to, to compute flags and solve any degeneracy. * It will be removed later (if it was actually added). */ status = _cairo_path_fixed_line_to (path, path->last_move_point.x, path->last_move_point.y); if (unlikely (status)) return status; /* * If the command used to close the path is a line_to, drop it. * We must check that last command is actually a line_to, * because the path could have been closed with a curve_to (and * the previous line_to not added as it would be degenerate). */ if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) _cairo_path_fixed_drop_line_to (path); path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */ return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); } cairo_bool_t _cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, cairo_fixed_t *x, cairo_fixed_t *y) { if (! path->has_current_point) return FALSE; *x = path->current_point.x; *y = path->current_point.y; return TRUE; } static cairo_status_t _cairo_path_fixed_add (cairo_path_fixed_t *path, cairo_path_op_t op, const cairo_point_t *points, int num_points) { cairo_path_buf_t *buf = cairo_path_tail (path); if (buf->num_ops + 1 > buf->size_ops || buf->num_points + num_points > buf->size_points) { buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_path_fixed_add_buf (path, buf); } if (WATCH_PATH) { const char *op_str[] = { "move-to", "line-to", "curve-to", "close-path", }; char buf[1024]; int len = 0; int i; len += snprintf (buf + len, sizeof (buf), "["); for (i = 0; i < num_points; i++) { if (i != 0) len += snprintf (buf + len, sizeof (buf), " "); len += snprintf (buf + len, sizeof (buf), "(%f, %f)", _cairo_fixed_to_double (points[i].x), _cairo_fixed_to_double (points[i].y)); } len += snprintf (buf + len, sizeof (buf), "]"); #define STRINGIFYFLAG(x) (path->x ? #x " " : "") fprintf (stderr, "_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n", op_str[(int) op], buf, STRINGIFYFLAG(has_current_point), STRINGIFYFLAG(needs_move_to), STRINGIFYFLAG(has_extents), STRINGIFYFLAG(has_curve_to), STRINGIFYFLAG(stroke_is_rectilinear), STRINGIFYFLAG(fill_is_rectilinear), STRINGIFYFLAG(fill_is_empty), STRINGIFYFLAG(fill_maybe_region) ); #undef STRINGIFYFLAG } _cairo_path_buf_add_op (buf, op); _cairo_path_buf_add_points (buf, points, num_points); return CAIRO_STATUS_SUCCESS; } static void _cairo_path_fixed_add_buf (cairo_path_fixed_t *path, cairo_path_buf_t *buf) { cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link); } COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1); static cairo_path_buf_t * _cairo_path_buf_create (int size_ops, int size_points) { cairo_path_buf_t *buf; /* adjust size_ops to ensure that buf->points is naturally aligned */ size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double)); buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t)); if (buf) { buf->num_ops = 0; buf->num_points = 0; buf->size_ops = size_ops; buf->size_points = size_points; buf->op = (cairo_path_op_t *) (buf + 1); buf->points = (cairo_point_t *) (buf->op + size_ops); } return buf; } static void _cairo_path_buf_destroy (cairo_path_buf_t *buf) { free (buf); } static void _cairo_path_buf_add_op (cairo_path_buf_t *buf, cairo_path_op_t op) { buf->op[buf->num_ops++] = op; } static void _cairo_path_buf_add_points (cairo_path_buf_t *buf, const cairo_point_t *points, int num_points) { if (num_points == 0) return; memcpy (buf->points + buf->num_points, points, sizeof (points[0]) * num_points); buf->num_points += num_points; } cairo_status_t _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_curve_to_func_t *curve_to, cairo_path_fixed_close_path_func_t *close_path, void *closure) { const cairo_path_buf_t *buf; cairo_status_t status; cairo_path_foreach_buf_start (buf, path) { const cairo_point_t *points = buf->points; unsigned int i; for (i = 0; i < buf->num_ops; i++) { switch (buf->op[i]) { case CAIRO_PATH_OP_MOVE_TO: status = (*move_to) (closure, &points[0]); points += 1; break; case CAIRO_PATH_OP_LINE_TO: status = (*line_to) (closure, &points[0]); points += 1; break; case CAIRO_PATH_OP_CURVE_TO: status = (*curve_to) (closure, &points[0], &points[1], &points[2]); points += 3; break; default: ASSERT_NOT_REACHED; case CAIRO_PATH_OP_CLOSE_PATH: status = (*close_path) (closure); break; } if (unlikely (status)) return status; } } cairo_path_foreach_buf_end (buf, path); return CAIRO_STATUS_SUCCESS; } typedef struct _cairo_path_fixed_append_closure { cairo_point_t offset; cairo_path_fixed_t *path; } cairo_path_fixed_append_closure_t; static cairo_status_t _append_move_to (void *abstract_closure, const cairo_point_t *point) { cairo_path_fixed_append_closure_t *closure = abstract_closure; return _cairo_path_fixed_move_to (closure->path, point->x + closure->offset.x, point->y + closure->offset.y); } static cairo_status_t _append_line_to (void *abstract_closure, const cairo_point_t *point) { cairo_path_fixed_append_closure_t *closure = abstract_closure; return _cairo_path_fixed_line_to (closure->path, point->x + closure->offset.x, point->y + closure->offset.y); } static cairo_status_t _append_curve_to (void *abstract_closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { cairo_path_fixed_append_closure_t *closure = abstract_closure; return _cairo_path_fixed_curve_to (closure->path, p0->x + closure->offset.x, p0->y + closure->offset.y, p1->x + closure->offset.x, p1->y + closure->offset.y, p2->x + closure->offset.x, p2->y + closure->offset.y); } static cairo_status_t _append_close_path (void *abstract_closure) { cairo_path_fixed_append_closure_t *closure = abstract_closure; return _cairo_path_fixed_close_path (closure->path); } cairo_status_t _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, cairo_fixed_t tx, cairo_fixed_t ty) { cairo_path_fixed_append_closure_t closure; closure.path = path; closure.offset.x = tx; closure.offset.y = ty; return _cairo_path_fixed_interpret (other, _append_move_to, _append_line_to, _append_curve_to, _append_close_path, &closure); } static void _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, cairo_fixed_t offx, cairo_fixed_t offy, cairo_fixed_t scalex, cairo_fixed_t scaley) { cairo_path_buf_t *buf; unsigned int i; if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) { _cairo_path_fixed_translate (path, offx, offy); return; } path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx; path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy; path->current_point.x = _cairo_fixed_mul (scalex, path->current_point.x) + offx; path->current_point.y = _cairo_fixed_mul (scaley, path->current_point.y) + offy; path->fill_maybe_region = TRUE; cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { if (scalex != CAIRO_FIXED_ONE) buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex); buf->points[i].x += offx; if (scaley != CAIRO_FIXED_ONE) buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley); buf->points[i].y += offy; if (path->fill_maybe_region) { path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) && _cairo_fixed_is_integer (buf->points[i].y); } } } cairo_path_foreach_buf_end (buf, path); path->fill_maybe_region &= path->fill_is_rectilinear; path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx; path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx; if (scalex < 0) { cairo_fixed_t t = path->extents.p1.x; path->extents.p1.x = path->extents.p2.x; path->extents.p2.x = t; } path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy; path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy; if (scaley < 0) { cairo_fixed_t t = path->extents.p1.y; path->extents.p1.y = path->extents.p2.y; path->extents.p2.y = t; } } void _cairo_path_fixed_translate (cairo_path_fixed_t *path, cairo_fixed_t offx, cairo_fixed_t offy) { cairo_path_buf_t *buf; unsigned int i; if (offx == 0 && offy == 0) return; path->last_move_point.x += offx; path->last_move_point.y += offy; path->current_point.x += offx; path->current_point.y += offy; path->fill_maybe_region = TRUE; cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { buf->points[i].x += offx; buf->points[i].y += offy; if (path->fill_maybe_region) { path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) && _cairo_fixed_is_integer (buf->points[i].y); } } } cairo_path_foreach_buf_end (buf, path); path->fill_maybe_region &= path->fill_is_rectilinear; path->extents.p1.x += offx; path->extents.p1.y += offy; path->extents.p2.x += offx; path->extents.p2.y += offy; } static inline void _cairo_path_fixed_transform_point (cairo_point_t *p, const cairo_matrix_t *matrix) { double dx, dy; dx = _cairo_fixed_to_double (p->x); dy = _cairo_fixed_to_double (p->y); cairo_matrix_transform_point (matrix, &dx, &dy); p->x = _cairo_fixed_from_double (dx); p->y = _cairo_fixed_from_double (dy); } /** * _cairo_path_fixed_transform: * @path: a #cairo_path_fixed_t to be transformed * @matrix: a #cairo_matrix_t * * Transform the fixed-point path according to the given matrix. * There is a fast path for the case where @matrix has no rotation * or shear. **/ void _cairo_path_fixed_transform (cairo_path_fixed_t *path, const cairo_matrix_t *matrix) { cairo_box_t extents; cairo_point_t point; cairo_path_buf_t *buf; unsigned int i; if (matrix->yx == 0.0 && matrix->xy == 0.0) { /* Fast path for the common case of scale+transform */ _cairo_path_fixed_offset_and_scale (path, _cairo_fixed_from_double (matrix->x0), _cairo_fixed_from_double (matrix->y0), _cairo_fixed_from_double (matrix->xx), _cairo_fixed_from_double (matrix->yy)); return; } _cairo_path_fixed_transform_point (&path->last_move_point, matrix); _cairo_path_fixed_transform_point (&path->current_point, matrix); buf = cairo_path_head (path); if (buf->num_points == 0) return; extents = path->extents; point = buf->points[0]; _cairo_path_fixed_transform_point (&point, matrix); _cairo_box_set (&path->extents, &point, &point); cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { _cairo_path_fixed_transform_point (&buf->points[i], matrix); _cairo_box_add_point (&path->extents, &buf->points[i]); } } cairo_path_foreach_buf_end (buf, path); if (path->has_curve_to) { cairo_bool_t is_tight; _cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight); if (!is_tight) { cairo_bool_t has_extents; has_extents = _cairo_path_bounder_extents (path, &extents); assert (has_extents); } path->extents = extents; } /* flags might become more strict than needed */ path->stroke_is_rectilinear = FALSE; path->fill_is_rectilinear = FALSE; path->fill_is_empty = FALSE; path->fill_maybe_region = FALSE; } /* Closure for path flattening */ typedef struct cairo_path_flattener { double tolerance; cairo_point_t current_point; cairo_path_fixed_move_to_func_t *move_to; cairo_path_fixed_line_to_func_t *line_to; cairo_path_fixed_close_path_func_t *close_path; void *closure; } cpf_t; static cairo_status_t _cpf_move_to (void *closure, const cairo_point_t *point) { cpf_t *cpf = closure; cpf->current_point = *point; return cpf->move_to (cpf->closure, point); } static cairo_status_t _cpf_line_to (void *closure, const cairo_point_t *point) { cpf_t *cpf = closure; cpf->current_point = *point; return cpf->line_to (cpf->closure, point); } static cairo_status_t _cpf_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { cpf_t *cpf = closure; cairo_spline_t spline; cairo_point_t *p0 = &cpf->current_point; if (! _cairo_spline_init (&spline, (cairo_spline_add_point_func_t)cpf->line_to, cpf->closure, p0, p1, p2, p3)) { return _cpf_line_to (closure, p3); } cpf->current_point = *p3; return _cairo_spline_decompose (&spline, cpf->tolerance); } static cairo_status_t _cpf_close_path (void *closure) { cpf_t *cpf = closure; return cpf->close_path (cpf->closure); } cairo_status_t _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_close_path_func_t *close_path, void *closure, double tolerance) { cpf_t flattener; if (! path->has_curve_to) { return _cairo_path_fixed_interpret (path, move_to, line_to, NULL, close_path, closure); } flattener.tolerance = tolerance; flattener.move_to = move_to; flattener.line_to = line_to; flattener.close_path = close_path; flattener.closure = closure; return _cairo_path_fixed_interpret (path, _cpf_move_to, _cpf_line_to, _cpf_curve_to, _cpf_close_path, &flattener); } static inline void _canonical_box (cairo_box_t *box, const cairo_point_t *p1, const cairo_point_t *p2) { if (p1->x <= p2->x) { box->p1.x = p1->x; box->p2.x = p2->x; } else { box->p1.x = p2->x; box->p2.x = p1->x; } if (p1->y <= p2->y) { box->p1.y = p1->y; box->p2.y = p2->y; } else { box->p1.y = p2->y; box->p2.y = p1->y; } } static inline cairo_bool_t _path_is_quad (const cairo_path_fixed_t *path) { const cairo_path_buf_t *buf = cairo_path_head (path); /* Do we have the right number of ops? */ if (buf->num_ops < 4 || buf->num_ops > 6) return FALSE; /* Check whether the ops are those that would be used for a rectangle */ if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO || buf->op[1] != CAIRO_PATH_OP_LINE_TO || buf->op[2] != CAIRO_PATH_OP_LINE_TO || buf->op[3] != CAIRO_PATH_OP_LINE_TO) { return FALSE; } /* we accept an implicit close for filled paths */ if (buf->num_ops > 4) { /* Now, there are choices. The rectangle might end with a LINE_TO * (to the original point), but this isn't required. If it * doesn't, then it must end with a CLOSE_PATH. */ if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { if (buf->points[4].x != buf->points[0].x || buf->points[4].y != buf->points[0].y) return FALSE; } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { return FALSE; } if (buf->num_ops == 6) { /* A trailing CLOSE_PATH or MOVE_TO is ok */ if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) return FALSE; } } return TRUE; } static inline cairo_bool_t _points_form_rect (const cairo_point_t *points) { if (points[0].y == points[1].y && points[1].x == points[2].x && points[2].y == points[3].y && points[3].x == points[0].x) return TRUE; if (points[0].x == points[1].x && points[1].y == points[2].y && points[2].x == points[3].x && points[3].y == points[0].y) return TRUE; return FALSE; } /* * Check whether the given path contains a single rectangle. */ cairo_bool_t _cairo_path_fixed_is_box (const cairo_path_fixed_t *path, cairo_box_t *box) { const cairo_path_buf_t *buf; if (! path->fill_is_rectilinear) return FALSE; if (! _path_is_quad (path)) return FALSE; buf = cairo_path_head (path); if (_points_form_rect (buf->points)) { _canonical_box (box, &buf->points[0], &buf->points[2]); return TRUE; } return FALSE; } /* Determine whether two lines A->B and C->D intersect based on the * algorithm described here: http://paulbourke.net/geometry/pointlineplane/ */ static inline cairo_bool_t _lines_intersect_or_are_coincident (cairo_point_t a, cairo_point_t b, cairo_point_t c, cairo_point_t d) { cairo_int64_t numerator_a, numerator_b, denominator; cairo_bool_t denominator_negative; denominator = _cairo_int64_sub (_cairo_int32x32_64_mul (d.y - c.y, b.x - a.x), _cairo_int32x32_64_mul (d.x - c.x, b.y - a.y)); numerator_a = _cairo_int64_sub (_cairo_int32x32_64_mul (d.x - c.x, a.y - c.y), _cairo_int32x32_64_mul (d.y - c.y, a.x - c.x)); numerator_b = _cairo_int64_sub (_cairo_int32x32_64_mul (b.x - a.x, a.y - c.y), _cairo_int32x32_64_mul (b.y - a.y, a.x - c.x)); if (_cairo_int64_is_zero (denominator)) { /* If the denominator and numerators are both zero, * the lines are coincident. */ if (_cairo_int64_is_zero (numerator_a) && _cairo_int64_is_zero (numerator_b)) return TRUE; /* Otherwise, a zero denominator indicates the lines are * parallel and never intersect. */ return FALSE; } /* The lines intersect if both quotients are between 0 and 1 (exclusive). */ /* We first test whether either quotient is a negative number. */ denominator_negative = _cairo_int64_negative (denominator); if (_cairo_int64_negative (numerator_a) ^ denominator_negative) return FALSE; if (_cairo_int64_negative (numerator_b) ^ denominator_negative) return FALSE; /* A zero quotient indicates an "intersection" at an endpoint, which * we aren't considering a true intersection. */ if (_cairo_int64_is_zero (numerator_a) || _cairo_int64_is_zero (numerator_b)) return FALSE; /* If the absolute value of the numerator is larger than or equal to the * denominator the result of the division would be greater than or equal * to one. */ if (! denominator_negative) { if (! _cairo_int64_lt (numerator_a, denominator) || ! _cairo_int64_lt (numerator_b, denominator)) return FALSE; } else { if (! _cairo_int64_lt (denominator, numerator_a) || ! _cairo_int64_lt (denominator, numerator_b)) return FALSE; } return TRUE; } cairo_bool_t _cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path) { const cairo_point_t *points; if (! _path_is_quad (path)) return FALSE; points = cairo_path_head (path)->points; if (_points_form_rect (points)) return TRUE; if (_lines_intersect_or_are_coincident (points[0], points[1], points[3], points[2])) return FALSE; if (_lines_intersect_or_are_coincident (points[0], points[3], points[1], points[2])) return FALSE; return TRUE; } cairo_bool_t _cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path, cairo_box_t *box) { const cairo_path_buf_t *buf = cairo_path_head (path); if (! path->fill_is_rectilinear) return FALSE; /* Do we have the right number of ops? */ if (buf->num_ops != 5) return FALSE; /* Check whether the ops are those that would be used for a rectangle */ if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO || buf->op[1] != CAIRO_PATH_OP_LINE_TO || buf->op[2] != CAIRO_PATH_OP_LINE_TO || buf->op[3] != CAIRO_PATH_OP_LINE_TO || buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { return FALSE; } /* Ok, we may have a box, if the points line up */ if (buf->points[0].y == buf->points[1].y && buf->points[1].x == buf->points[2].x && buf->points[2].y == buf->points[3].y && buf->points[3].x == buf->points[0].x) { _canonical_box (box, &buf->points[0], &buf->points[2]); return TRUE; } if (buf->points[0].x == buf->points[1].x && buf->points[1].y == buf->points[2].y && buf->points[2].x == buf->points[3].x && buf->points[3].y == buf->points[0].y) { _canonical_box (box, &buf->points[0], &buf->points[2]); return TRUE; } return FALSE; } /* * Check whether the given path contains a single rectangle * that is logically equivalent to: * <informalexample><programlisting> * cairo_move_to (cr, x, y); * cairo_rel_line_to (cr, width, 0); * cairo_rel_line_to (cr, 0, height); * cairo_rel_line_to (cr, -width, 0); * cairo_close_path (cr); * </programlisting></informalexample> */ cairo_bool_t _cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, cairo_box_t *box) { const cairo_path_buf_t *buf; if (! _cairo_path_fixed_is_box (path, box)) return FALSE; /* This check is valid because the current implementation of * _cairo_path_fixed_is_box () only accepts rectangles like: * move,line,line,line[,line|close[,close|move]]. */ buf = cairo_path_head (path); if (buf->num_ops > 4) return TRUE; return FALSE; } void _cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, const cairo_path_fixed_t *path) { iter->first = iter->buf = cairo_path_head (path); iter->n_op = 0; iter->n_point = 0; } static cairo_bool_t _cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) { if (++iter->n_op >= iter->buf->num_ops) { iter->buf = cairo_path_buf_next (iter->buf); if (iter->buf == iter->first) { iter->buf = NULL; return FALSE; } iter->n_op = 0; iter->n_point = 0; } return TRUE; } cairo_bool_t _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, cairo_box_t *box) { cairo_point_t points[5]; cairo_path_fixed_iter_t iter; if (_iter->buf == NULL) return FALSE; iter = *_iter; if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; /* Check whether the ops are those that would be used for a rectangle */ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) return FALSE; points[0] = iter.buf->points[iter.n_point++]; if (! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) return FALSE; points[1] = iter.buf->points[iter.n_point++]; if (! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; /* a horizontal/vertical closed line is also a degenerate rectangle */ switch (iter.buf->op[iter.n_op]) { case CAIRO_PATH_OP_CLOSE_PATH: _cairo_path_fixed_iter_next_op (&iter); case CAIRO_PATH_OP_MOVE_TO: /* implicit close */ box->p1 = box->p2 = points[0]; *_iter = iter; return TRUE; default: return FALSE; case CAIRO_PATH_OP_LINE_TO: break; } points[2] = iter.buf->points[iter.n_point++]; if (! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) return FALSE; points[3] = iter.buf->points[iter.n_point++]; /* Now, there are choices. The rectangle might end with a LINE_TO * (to the original point), but this isn't required. If it * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */ if (! _cairo_path_fixed_iter_next_op (&iter)) { /* implicit close due to fill */ } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) { points[4] = iter.buf->points[iter.n_point++]; if (points[4].x != points[0].x || points[4].y != points[0].y) return FALSE; _cairo_path_fixed_iter_next_op (&iter); } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH) { _cairo_path_fixed_iter_next_op (&iter); } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO) { /* implicit close-path due to new-sub-path */ } else { return FALSE; } /* Ok, we may have a box, if the points line up */ if (points[0].y == points[1].y && points[1].x == points[2].x && points[2].y == points[3].y && points[3].x == points[0].x) { box->p1 = points[0]; box->p2 = points[2]; *_iter = iter; return TRUE; } if (points[0].x == points[1].x && points[1].y == points[2].y && points[2].x == points[3].x && points[3].y == points[0].y) { box->p1 = points[1]; box->p2 = points[3]; *_iter = iter; return TRUE; } return FALSE; } cairo_bool_t _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) { if (iter->buf == NULL) return TRUE; return iter->n_op == iter->buf->num_ops; } ��������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-in-fill.c����������������������������0000664�0000000�0000000�00000016245�12710376503�0026033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-path-fixed-private.h" typedef struct cairo_in_fill { double tolerance; cairo_bool_t on_edge; int winding; cairo_fixed_t x, y; cairo_bool_t has_current_point; cairo_point_t current_point; cairo_point_t first_point; } cairo_in_fill_t; static void _cairo_in_fill_init (cairo_in_fill_t *in_fill, double tolerance, double x, double y) { in_fill->on_edge = FALSE; in_fill->winding = 0; in_fill->tolerance = tolerance; in_fill->x = _cairo_fixed_from_double (x); in_fill->y = _cairo_fixed_from_double (y); in_fill->has_current_point = FALSE; in_fill->current_point.x = 0; in_fill->current_point.y = 0; } static void _cairo_in_fill_fini (cairo_in_fill_t *in_fill) { } static int edge_compare_for_y_against_x (const cairo_point_t *p1, const cairo_point_t *p2, cairo_fixed_t y, cairo_fixed_t x) { cairo_fixed_t adx, ady; cairo_fixed_t dx, dy; cairo_int64_t L, R; adx = p2->x - p1->x; dx = x - p1->x; if (adx == 0) return -dx; if ((adx ^ dx) < 0) return adx; dy = y - p1->y; ady = p2->y - p1->y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); return _cairo_int64_cmp (L, R); } static void _cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, const cairo_point_t *p1, const cairo_point_t *p2) { int dir; if (in_fill->on_edge) return; /* count the number of edge crossing to -∞ */ dir = 1; if (p2->y < p1->y) { const cairo_point_t *tmp; tmp = p1; p1 = p2; p2 = tmp; dir = -1; } /* First check whether the query is on an edge */ if ((p1->x == in_fill->x && p1->y == in_fill->y) || (p2->x == in_fill->x && p2->y == in_fill->y) || (! (p2->y < in_fill->y || p1->y > in_fill->y || (p1->x > in_fill->x && p2->x > in_fill->x) || (p1->x < in_fill->x && p2->x < in_fill->x)) && edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0)) { in_fill->on_edge = TRUE; return; } /* edge is entirely above or below, note the shortening rule */ if (p2->y <= in_fill->y || p1->y > in_fill->y) return; /* edge lies wholly to the right */ if (p1->x >= in_fill->x && p2->x >= in_fill->x) return; if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0) { in_fill->winding += dir; } } static cairo_status_t _cairo_in_fill_move_to (void *closure, const cairo_point_t *point) { cairo_in_fill_t *in_fill = closure; /* implicit close path */ if (in_fill->has_current_point) { _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, &in_fill->first_point); } in_fill->first_point = *point; in_fill->current_point = *point; in_fill->has_current_point = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_in_fill_line_to (void *closure, const cairo_point_t *point) { cairo_in_fill_t *in_fill = closure; if (in_fill->has_current_point) _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point); in_fill->current_point = *point; in_fill->has_current_point = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_in_fill_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_in_fill_t *in_fill = closure; cairo_spline_t spline; cairo_fixed_t top, bot, left; /* first reject based on bbox */ bot = top = in_fill->current_point.y; if (b->y < top) top = b->y; if (b->y > bot) bot = b->y; if (c->y < top) top = c->y; if (c->y > bot) bot = c->y; if (d->y < top) top = d->y; if (d->y > bot) bot = d->y; if (bot < in_fill->y || top > in_fill->y) { in_fill->current_point = *d; return CAIRO_STATUS_SUCCESS; } left = in_fill->current_point.x; if (b->x < left) left = b->x; if (c->x < left) left = c->x; if (d->x < left) left = d->x; if (left > in_fill->x) { in_fill->current_point = *d; return CAIRO_STATUS_SUCCESS; } /* XXX Investigate direct inspection of the inflections? */ if (! _cairo_spline_init (&spline, (cairo_spline_add_point_func_t)_cairo_in_fill_line_to, in_fill, &in_fill->current_point, b, c, d)) { return CAIRO_STATUS_SUCCESS; } return _cairo_spline_decompose (&spline, in_fill->tolerance); } static cairo_status_t _cairo_in_fill_close_path (void *closure) { cairo_in_fill_t *in_fill = closure; if (in_fill->has_current_point) { _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, &in_fill->first_point); in_fill->has_current_point = FALSE; } return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, double x, double y) { cairo_in_fill_t in_fill; cairo_status_t status; cairo_bool_t is_inside; if (_cairo_path_fixed_fill_is_empty (path)) return FALSE; _cairo_in_fill_init (&in_fill, tolerance, x, y); status = _cairo_path_fixed_interpret (path, _cairo_in_fill_move_to, _cairo_in_fill_line_to, _cairo_in_fill_curve_to, _cairo_in_fill_close_path, &in_fill); assert (status == CAIRO_STATUS_SUCCESS); _cairo_in_fill_close_path (&in_fill); if (in_fill.on_edge) { is_inside = TRUE; } else switch (fill_rule) { case CAIRO_FILL_RULE_EVEN_ODD: is_inside = in_fill.winding & 1; break; case CAIRO_FILL_RULE_WINDING: is_inside = in_fill.winding != 0; break; default: ASSERT_NOT_REACHED; is_inside = FALSE; break; } _cairo_in_fill_fini (&in_fill); return is_inside; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-private.h����������������������������0000664�0000000�0000000�00000003776�12710376503�0026165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2006 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_PATH_PRIVATE_H #define CAIRO_PATH_PRIVATE_H #include "cairoint.h" cairo_private cairo_path_t * _cairo_path_create (cairo_path_fixed_t *path, cairo_t *cr); cairo_private cairo_path_t * _cairo_path_create_flat (cairo_path_fixed_t *path, cairo_t *cr); cairo_private cairo_path_t * _cairo_path_create_in_error (cairo_status_t status); cairo_private cairo_status_t _cairo_path_append_to_context (const cairo_path_t *path, cairo_t *cr); #endif /* CAIRO_PATH_DATA_PRIVATE_H */ ��Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-stroke-boxes.c�����������������������0000664�0000000�0000000�00000050001�12710376503�0027112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" #include "cairo-stroke-dash-private.h" typedef struct _segment_t { cairo_point_t p1, p2; unsigned flags; #define HORIZONTAL 0x1 #define FORWARDS 0x2 #define JOIN 0x4 } segment_t; typedef struct _cairo_rectilinear_stroker { const cairo_stroke_style_t *stroke_style; const cairo_matrix_t *ctm; cairo_antialias_t antialias; cairo_fixed_t half_line_x, half_line_y; cairo_boxes_t *boxes; cairo_point_t current_point; cairo_point_t first_point; cairo_bool_t open_sub_path; cairo_stroker_dash_t dash; cairo_bool_t has_bounds; cairo_box_t bounds; int num_segments; int segments_size; segment_t *segments; segment_t segments_embedded[8]; /* common case is a single rectangle */ } cairo_rectilinear_stroker_t; static void _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, const cairo_box_t *boxes, int num_boxes) { stroker->has_bounds = TRUE; _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); stroker->bounds.p1.x -= stroker->half_line_x; stroker->bounds.p2.x += stroker->half_line_x; stroker->bounds.p1.y -= stroker->half_line_y; stroker->bounds.p2.y += stroker->half_line_y; } static cairo_bool_t _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_antialias_t antialias, cairo_boxes_t *boxes) { /* This special-case rectilinear stroker only supports * miter-joined lines (not curves) and a translation-only matrix * (though it could probably be extended to support a matrix with * uniform, integer scaling). * * It also only supports horizontal and vertical line_to * elements. But we don't catch that here, but instead return * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any * non-rectilinear line_to is encountered. */ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) return FALSE; /* If the miter limit turns right angles into bevels, then we * can't use this optimization. Remember, the ratio is * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï€/4.0) or ⎷2, * which we round for safety. */ if (stroke_style->miter_limit < M_SQRT2) return FALSE; if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) { return FALSE; } if (! _cairo_matrix_is_scale (ctm)) return FALSE; stroker->stroke_style = stroke_style; stroker->ctm = ctm; stroker->antialias = antialias; stroker->half_line_x = _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0); stroker->half_line_y = _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0); stroker->open_sub_path = FALSE; stroker->segments = stroker->segments_embedded; stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); stroker->num_segments = 0; _cairo_stroker_dash_init (&stroker->dash, stroke_style); stroker->has_bounds = FALSE; stroker->boxes = boxes; return TRUE; } static void _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) { if (stroker->segments != stroker->segments_embedded) free (stroker->segments); } static cairo_status_t _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, const cairo_point_t *p1, const cairo_point_t *p2, unsigned flags) { if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (stroker->num_segments == stroker->segments_size) { int new_size = stroker->segments_size * 2; segment_t *new_segments; if (stroker->segments == stroker->segments_embedded) { new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); if (unlikely (new_segments == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (new_segments, stroker->segments, stroker->num_segments * sizeof (segment_t)); } else { new_segments = _cairo_realloc_ab (stroker->segments, new_size, sizeof (segment_t)); if (unlikely (new_segments == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } stroker->segments_size = new_size; stroker->segments = new_segments; } stroker->segments[stroker->num_segments].p1 = *p1; stroker->segments[stroker->num_segments].p2 = *p2; stroker->segments[stroker->num_segments].flags = flags; stroker->num_segments++; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) { cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_x = stroker->half_line_x; cairo_fixed_t half_line_y = stroker->half_line_y; cairo_status_t status; int i, j; /* For each segment we generate a single rectangle. * This rectangle is based on a perpendicular extension (by half the * line width) of the segment endpoints * after some adjustments of the * endpoints to account for caps and joins. */ for (i = 0; i < stroker->num_segments; i++) { cairo_bool_t lengthen_initial, lengthen_final; cairo_point_t *a, *b; cairo_box_t box; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; /* We adjust the initial point of the segment to extend the * rectangle to include the previous cap or join, (this * adjustment applies to all segments except for the first * segment of open, butt-capped paths). However, we must be * careful not to emit a miter join across a degenerate segment * which has been elided. * * Overlapping segments will be eliminated by the tessellation. * Ideally, we would not emit these self-intersections at all, * but that is tricky with segments shorter than half_line_width. */ j = i == 0 ? stroker->num_segments - 1 : i-1; lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; j = i == stroker->num_segments - 1 ? 0 : i+1; lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; if (stroker->open_sub_path) { if (i == 0) lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT; if (i == stroker->num_segments - 1) lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT; } /* Perform the adjustments of the endpoints. */ if (lengthen_initial | lengthen_final) { if (a->y == b->y) { if (a->x < b->x) { if (lengthen_initial) a->x -= half_line_x; if (lengthen_final) b->x += half_line_x; } else { if (lengthen_initial) a->x += half_line_x; if (lengthen_final) b->x -= half_line_x; } } else { if (a->y < b->y) { if (lengthen_initial) a->y -= half_line_y; if (lengthen_final) b->y += half_line_y; } else { if (lengthen_initial) a->y += half_line_y; if (lengthen_final) b->y -= half_line_y; } } } /* Form the rectangle by expanding by half the line width in * either perpendicular direction. */ if (a->y == b->y) { a->y -= half_line_y; b->y += half_line_y; } else { a->x -= half_line_x; b->x += half_line_x; } if (a->x < b->x) { box.p1.x = a->x; box.p2.x = b->x; } else { box.p1.x = b->x; box.p2.x = a->x; } if (a->y < b->y) { box.p1.y = a->y; box.p2.y = b->y; } else { box.p1.y = b->y; box.p2.y = a->y; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) { cairo_status_t status; cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_x = stroker->half_line_x; cairo_fixed_t half_line_y = stroker->half_line_y; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t is_horizontal; cairo_box_t box; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; is_horizontal = stroker->segments[i].flags & HORIZONTAL; /* Handle the joins for a potentially degenerate segment. */ if (line_cap == CAIRO_LINE_CAP_BUTT && stroker->segments[i].flags & JOIN && (i != stroker->num_segments - 1 || (! stroker->open_sub_path && stroker->dash.dash_starts_on))) { cairo_slope_t out_slope; int j = (i + 1) % stroker->num_segments; cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS); _cairo_slope_init (&out_slope, &stroker->segments[j].p1, &stroker->segments[j].p2); box.p2 = box.p1 = stroker->segments[i].p2; if (is_horizontal) { if (forwards) box.p2.x += half_line_x; else box.p1.x -= half_line_x; if (out_slope.dy > 0) box.p1.y -= half_line_y; else box.p2.y += half_line_y; } else { if (forwards) box.p2.y += half_line_y; else box.p1.y -= half_line_y; if (out_slope.dx > 0) box.p1.x -= half_line_x; else box.p2.x += half_line_x; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } /* Perform the adjustments of the endpoints. */ if (is_horizontal) { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->x <= b->x) { a->x -= half_line_x; b->x += half_line_x; } else { a->x += half_line_x; b->x -= half_line_x; } } a->y += half_line_y; b->y -= half_line_y; } else { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->y <= b->y) { a->y -= half_line_y; b->y += half_line_y; } else { a->y += half_line_y; b->y -= half_line_y; } } a->x += half_line_x; b->x -= half_line_x; } if (a->x == b->x && a->y == b->y) continue; if (a->x < b->x) { box.p1.x = a->x; box.p2.x = b->x; } else { box.p1.x = b->x; box.p2.x = a->x; } if (a->y < b->y) { box.p1.y = a->y; box.p2.y = b->y; } else { box.p1.y = b->y; box.p2.y = a->y; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectilinear_stroker_move_to (void *closure, const cairo_point_t *point) { cairo_rectilinear_stroker_t *stroker = closure; cairo_status_t status; if (stroker->dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); else status = _cairo_rectilinear_stroker_emit_segments (stroker); if (unlikely (status)) return status; /* reset the dash pattern for new sub paths */ _cairo_stroker_dash_start (&stroker->dash); stroker->current_point = *point; stroker->first_point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectilinear_stroker_line_to (void *closure, const cairo_point_t *b) { cairo_rectilinear_stroker_t *stroker = closure; cairo_point_t *a = &stroker->current_point; cairo_status_t status; /* We only support horizontal or vertical elements. */ assert (a->x == b->x || a->y == b->y); /* We don't draw anything for degenerate paths. */ if (a->x == b->x && a->y == b->y) return CAIRO_STATUS_SUCCESS; status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, (a->y == b->y) | JOIN); stroker->current_point = *b; stroker->open_sub_path = TRUE; return status; } static cairo_status_t _cairo_rectilinear_stroker_line_to_dashed (void *closure, const cairo_point_t *point) { cairo_rectilinear_stroker_t *stroker = closure; const cairo_point_t *a = &stroker->current_point; const cairo_point_t *b = point; cairo_bool_t fully_in_bounds; double sf, sign, remain; cairo_fixed_t mag; cairo_status_t status; cairo_line_t segment; cairo_bool_t dash_on = FALSE; unsigned is_horizontal; /* We don't draw anything for degenerate paths. */ if (a->x == b->x && a->y == b->y) return CAIRO_STATUS_SUCCESS; /* We only support horizontal or vertical elements. */ assert (a->x == b->x || a->y == b->y); fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->bounds, a) || ! _cairo_box_contains_point (&stroker->bounds, b))) { fully_in_bounds = FALSE; } is_horizontal = a->y == b->y; if (is_horizontal) { mag = b->x - a->x; sf = fabs (stroker->ctm->xx); } else { mag = b->y - a->y; sf = fabs (stroker->ctm->yy); } if (mag < 0) { remain = _cairo_fixed_to_double (-mag); sign = 1.; } else { remain = _cairo_fixed_to_double (mag); is_horizontal |= FORWARDS; sign = -1.; } segment.p2 = segment.p1 = *a; while (remain > 0.) { double step_length; step_length = MIN (sf * stroker->dash.dash_remain, remain); remain -= step_length; mag = _cairo_fixed_from_double (sign*remain); if (is_horizontal & 0x1) segment.p2.x = b->x + mag; else segment.p2.y = b->y + mag; if (stroker->dash.dash_on && (fully_in_bounds || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_rectilinear_stroker_add_segment (stroker, &segment.p1, &segment.p2, is_horizontal | (remain <= 0.) << 2); if (unlikely (status)) return status; dash_on = TRUE; } else { dash_on = FALSE; } _cairo_stroker_dash_step (&stroker->dash, step_length / sf); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! dash_on && (fully_in_bounds || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. */ status = _cairo_rectilinear_stroker_add_segment (stroker, &segment.p1, &segment.p1, is_horizontal | JOIN); if (unlikely (status)) return status; } stroker->current_point = *point; stroker->open_sub_path = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectilinear_stroker_close_path (void *closure) { cairo_rectilinear_stroker_t *stroker = closure; cairo_status_t status; /* We don't draw anything for degenerate paths. */ if (! stroker->open_sub_path) return CAIRO_STATUS_SUCCESS; if (stroker->dash.dashed) { status = _cairo_rectilinear_stroker_line_to_dashed (stroker, &stroker->first_point); } else { status = _cairo_rectilinear_stroker_line_to (stroker, &stroker->first_point); } if (unlikely (status)) return status; stroker->open_sub_path = FALSE; if (stroker->dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); else status = _cairo_rectilinear_stroker_emit_segments (stroker); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; cairo_box_t box; assert (_cairo_path_fixed_stroke_is_rectilinear (path)); if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, antialias, boxes)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! rectilinear_stroker.dash.dashed && _cairo_path_fixed_is_stroke_box (path, &box) && /* if the segments overlap we need to feed them into the tessellator */ box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x && box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y) { cairo_box_t b; /* top */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y - rectilinear_stroker.half_line_y; b.p2.y = box.p1.y + rectilinear_stroker.half_line_y; status = _cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* left (excluding top/bottom) */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p1.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = _cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* right (excluding top/bottom) */ b.p1.x = box.p2.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = _cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* bottom */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p2.y - rectilinear_stroker.half_line_y; b.p2.y = box.p2.y + rectilinear_stroker.half_line_y; status = _cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); goto done; } if (boxes->num_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, boxes->limits, boxes->num_limits); } status = _cairo_path_fixed_interpret (path, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); if (unlikely (status)) goto BAIL; /* As we incrementally tessellate, we do not eliminate self-intersections */ status = _cairo_bentley_ottmann_tessellate_boxes (boxes, CAIRO_FILL_RULE_WINDING, boxes); if (unlikely (status)) goto BAIL; done: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); return CAIRO_STATUS_SUCCESS; BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); _cairo_boxes_clear (boxes); return status; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-stroke-polygon.c���������������������0000664�0000000�0000000�00000111646�12710376503�0027476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-contour-inline.h" #include "cairo-contour-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" #define DEBUG 0 struct stroker { cairo_stroke_style_t style; #if DEBUG cairo_contour_t path; #endif struct stroke_contour { /* Note that these are not strictly contours as they may intersect */ cairo_contour_t contour; } cw, ccw; cairo_uint64_t contour_tolerance; cairo_polygon_t *polygon; const cairo_matrix_t *ctm; const cairo_matrix_t *ctm_inverse; double tolerance; double spline_cusp_tolerance; double half_line_width; cairo_bool_t ctm_det_positive; cairo_pen_t pen; cairo_point_t first_point; cairo_bool_t has_initial_sub_path; cairo_bool_t has_current_face; cairo_stroke_face_t current_face; cairo_bool_t has_first_face; cairo_stroke_face_t first_face; cairo_bool_t has_bounds; cairo_box_t bounds; }; static inline double normalize_slope (double *dx, double *dy); static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face); static cairo_uint64_t point_distance_sq (const cairo_point_t *p1, const cairo_point_t *p2) { int32_t dx = p1->x - p2->x; int32_t dy = p1->y - p2->y; return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); } static cairo_bool_t within_tolerance (const cairo_point_t *p1, const cairo_point_t *p2, cairo_uint64_t tolerance) { return FALSE; return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance); } static void contour_add_point (struct stroker *stroker, struct stroke_contour *c, const cairo_point_t *point) { if (! within_tolerance (point, _cairo_contour_last_point (&c->contour), stroker->contour_tolerance)) _cairo_contour_add_point (&c->contour, point); //*_cairo_contour_last_point (&c->contour) = *point; } static void translate_point (cairo_point_t *point, const cairo_point_t *offset) { point->x += offset->x; point->y += offset->y; } static int slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) { double c = (dx1 * dy2 - dx2 * dy1); if (c > 0) return 1; if (c < 0) return -1; return 0; } static inline int range_step (int i, int step, int max) { i += step; if (i < 0) i = max - 1; if (i >= max) i = 0; return i; } /* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static void add_fan (struct stroker *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, cairo_bool_t clockwise, struct stroke_contour *c) { cairo_pen_t *pen = &stroker->pen; int start, stop; if (stroker->has_bounds && ! _cairo_box_contains_point (&stroker->bounds, midpt)) return; assert (stroker->pen.num_vertices); if (clockwise) { _cairo_pen_find_active_cw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (++start == pen->num_vertices) start = 0; } } else { _cairo_pen_find_active_ccw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (start-- == 0) start += pen->num_vertices; } } } static int join_is_clockwise (const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; } static void inner_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { #if 0 cairo_point_t last; const cairo_point_t *p, *outpt; struct stroke_contour *inner; cairo_int64_t d_p, d_last; cairo_int64_t half_line_width; cairo_bool_t negate; /* XXX line segments shorter than line-width */ if (clockwise) { inner = &stroker->ccw; outpt = &out->ccw; negate = 1; } else { inner = &stroker->cw; outpt = &out->cw; negate = 0; } half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5; /* On the inside, the previous end-point is always * closer to the new face by definition. */ last = *_cairo_contour_last_point (&inner->contour); d_last = distance_from_face (out, &last, negate); _cairo_contour_remove_last_point (&inner->contour); prev: if (inner->contour.chain.num_points == 0) { contour_add_point (stroker, inner, outpt); return; } p = _cairo_contour_last_point (&inner->contour); d_p = distance_from_face (out, p, negate); if (_cairo_int64_lt (d_p, half_line_width) && !_cairo_int64_negative (distance_along_face (out, p))) { last = *p; d_last = d_p; _cairo_contour_remove_last_point (&inner->contour); goto prev; } compute_inner_joint (&last, d_last, p, d_p, half_line_width); contour_add_point (stroker, inner, &last); #else const cairo_point_t *outpt; struct stroke_contour *inner; if (clockwise) { inner = &stroker->ccw; outpt = &out->ccw; } else { inner = &stroker->cw; outpt = &out->cw; } contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, outpt); #endif } static void inner_close (struct stroker *stroker, const cairo_stroke_face_t *in, cairo_stroke_face_t *out) { #if 0 cairo_point_t last; const cairo_point_t *p, *outpt, *inpt; struct stroke_contour *inner; struct _cairo_contour_chain *chain; /* XXX line segments shorter than line-width */ if (join_is_clockwise (in, out)) { inner = &stroker->ccw; outpt = &in->ccw; inpt = &out->ccw; } else { inner = &stroker->cw; outpt = &in->cw; inpt = &out->cw; } if (inner->contour.chain.num_points == 0) { contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, inpt); *_cairo_contour_first_point (&inner->contour) = *_cairo_contour_last_point (&inner->contour); return; } line_width = stroker->style.line_width/2; line_width *= CAIRO_FIXED_ONE; d_last = sign * distance_from_face (out, outpt); last = *outpt; for (chain = &inner->contour.chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { p = &chain->points[i]; if ((d_p = sign * distance_from_face (in, p)) >= line_width && distance_from_edge (stroker, inpt, &last, p) < line_width) { goto out; } if (p->x != last.x || p->y != last.y) { last = *p; d_last = d_p; } } } out: if (d_p != d_last) { double dot = (line_width - d_last) / (d_p - d_last); last.x += dot * (p->x - last.x); last.y += dot * (p->y - last.y); } *_cairo_contour_last_point (&inner->contour) = last; for (chain = &inner->contour.chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { cairo_point_t *pp = &chain->points[i]; if (pp == p) return; *pp = last; } } #else const cairo_point_t *inpt; struct stroke_contour *inner; if (join_is_clockwise (in, out)) { inner = &stroker->ccw; inpt = &out->ccw; } else { inner = &stroker->cw; inpt = &out->cw; } contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, inpt); *_cairo_contour_first_point (&inner->contour) = *_cairo_contour_last_point (&inner->contour); #endif } static void outer_close (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; int clockwise; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } clockwise = join_is_clockwise (in, out); if (clockwise) { inpt = &in->cw; outpt = &out->cw; outer = &stroker->cw; } else { inpt = &in->ccw; outpt = &out->ccw; outer = &stroker->ccw; } if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) { *_cairo_contour_first_point (&outer->contour) = *_cairo_contour_last_point (&outer->contour); return; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ if ((in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) { add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, clockwise, outer); break; } case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 + in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->dev_slope.x; dy1 = in->dev_slope.y; /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->dev_slope.x; dy2 = out->dev_slope.y; /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); *_cairo_contour_last_point (&outer->contour) = p; *_cairo_contour_first_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } contour_add_point (stroker, outer, outpt); } static void outer_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } if (clockwise) { inpt = &in->cw; outpt = &out->cw; outer = &stroker->cw; } else { inpt = &in->ccw; outpt = &out->ccw; outer = &stroker->ccw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, clockwise, outer); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 + in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->dev_slope.x; dy1 = in->dev_slope.y; /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->dev_slope.x; dy2 = out->dev_slope.y; /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); *_cairo_contour_last_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } contour_add_point (stroker,outer, outpt); } static void add_cap (struct stroker *stroker, const cairo_stroke_face_t *f, struct stroke_contour *c) { switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; slope.dx = -f->dev_vector.dx; slope.dy = -f->dev_vector.dy; add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c); break; } case CAIRO_LINE_CAP_SQUARE: { cairo_slope_t fvector; cairo_point_t p; double dx, dy; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->half_line_width; dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); p.x = f->ccw.x + fvector.dx; p.y = f->ccw.y + fvector.dy; contour_add_point (stroker, c, &p); p.x = f->cw.x + fvector.dx; p.y = f->cw.y + fvector.dy; contour_add_point (stroker, c, &p); } case CAIRO_LINE_CAP_BUTT: default: break; } contour_add_point (stroker, c, &f->cw); } static void add_leading_cap (struct stroker *stroker, const cairo_stroke_face_t *face, struct stroke_contour *c) { cairo_stroke_face_t reversed; cairo_point_t t; reversed = *face; /* The initial cap needs an outward facing vector. Reverse everything */ reversed.usr_vector.x = -reversed.usr_vector.x; reversed.usr_vector.y = -reversed.usr_vector.y; reversed.dev_vector.dx = -reversed.dev_vector.dx; reversed.dev_vector.dy = -reversed.dev_vector.dy; t = reversed.cw; reversed.cw = reversed.ccw; reversed.ccw = t; add_cap (stroker, &reversed, c); } static void add_trailing_cap (struct stroker *stroker, const cairo_stroke_face_t *face, struct stroke_contour *c) { add_cap (stroker, face, c); } static inline double normalize_slope (double *dx, double *dy) { double dx0 = *dx, dy0 = *dy; double mag; assert (dx0 != 0.0 || dy0 != 0.0); if (dx0 == 0.0) { *dx = 0.0; if (dy0 > 0.0) { mag = dy0; *dy = 1.0; } else { mag = -dy0; *dy = -1.0; } } else if (dy0 == 0.0) { *dy = 0.0; if (dx0 > 0.0) { mag = dx0; *dx = 1.0; } else { mag = -dx0; *dx = -1.0; } } else { mag = hypot (dx0, dy0); *dx = dx0 / mag; *dy = dy0 / mag; } return mag; } static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; double slope_dx, slope_dy; slope_dx = _cairo_fixed_to_double (dev_slope->dx); slope_dy = _cairo_fixed_to_double (dev_slope->dy); face->length = normalize_slope (&slope_dx, &slope_dy); face->dev_slope.x = slope_dx; face->dev_slope.y = slope_dy; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { /* Normalize the matrix! */ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); normalize_slope (&slope_dx, &slope_dy); if (stroker->ctm_det_positive) { face_dx = - slope_dy * stroker->half_line_width; face_dy = slope_dx * stroker->half_line_width; } else { face_dx = slope_dy * stroker->half_line_width; face_dy = - slope_dx * stroker->half_line_width; } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); } else { face_dx = - slope_dy * stroker->half_line_width; face_dy = slope_dx * stroker->half_line_width; } offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; } static void add_caps (struct stroker *stroker) { /* check for a degenerative sub_path */ if (stroker->has_initial_sub_path && ! stroker->has_first_face && ! stroker->has_current_face && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; cairo_stroke_face_t face; /* arbitrarily choose first_point */ compute_face (&stroker->first_point, &slope, stroker, &face); add_leading_cap (stroker, &face, &stroker->ccw); add_trailing_cap (stroker, &face, &stroker->ccw); /* ensure the circle is complete */ _cairo_contour_add_point (&stroker->ccw.contour, _cairo_contour_first_point (&stroker->ccw.contour)); _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); _cairo_contour_reset (&stroker->ccw.contour); } else { if (stroker->has_current_face) add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw); #if DEBUG { FILE *file = fopen ("contours.txt", "a"); _cairo_debug_print_contour (file, &stroker->path); _cairo_debug_print_contour (file, &stroker->cw.contour); _cairo_debug_print_contour (file, &stroker->ccw.contour); fclose (file); _cairo_contour_reset (&stroker->path); } #endif _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); _cairo_contour_reset (&stroker->ccw.contour); if (stroker->has_first_face) { _cairo_contour_add_point (&stroker->ccw.contour, &stroker->first_face.cw); add_leading_cap (stroker, &stroker->first_face, &stroker->ccw); #if DEBUG { FILE *file = fopen ("contours.txt", "a"); _cairo_debug_print_contour (file, &stroker->ccw.contour); fclose (file); } #endif _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); _cairo_contour_reset (&stroker->ccw.contour); } _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); _cairo_contour_reset (&stroker->cw.contour); } } static cairo_status_t close_path (void *closure); static cairo_status_t move_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; /* Cap the start and end of the previous sub path as needed */ add_caps (stroker); stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; stroker->has_initial_sub_path = FALSE; stroker->first_point = *point; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif stroker->current_face.point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, &start.dev_vector); if (clockwise) { clockwise = clockwise < 0; /* Join with final face from previous segment */ if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, stroker->contour_tolerance) || ! within_tolerance (&stroker->current_face.cw, &start.cw, stroker->contour_tolerance)) { outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } } } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &start.cw); contour_add_point (stroker, &stroker->ccw, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; } static cairo_status_t spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { struct stroker *stroker = closure; cairo_stroke_face_t face; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif if ((tangent->dx | tangent->dy) == 0) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; cairo_point_t t; int clockwise; face = stroker->current_face; face.usr_vector.x = -face.usr_vector.x; face.usr_vector.y = -face.usr_vector.y; face.dev_vector.dx = -face.dev_vector.dx; face.dev_vector.dy = -face.dev_vector.dy; t = face.cw; face.cw = face.ccw; face.ccw = t; clockwise = join_is_clockwise (&stroker->current_face, &face); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; outer = &stroker->cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; outer = &stroker->ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, clockwise, outer); } else { compute_face (point, tangent, stroker, &face); if ((face.dev_slope.x * stroker->current_face.dev_slope.x + face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; int clockwise = join_is_clockwise (&stroker->current_face, &face); stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; outer = &stroker->cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; outer = &stroker->ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, clockwise, outer); } contour_add_point (stroker, &stroker->cw, &face.cw); contour_add_point (stroker, &stroker->ccw, &face.ccw); } stroker->current_face = face; return CAIRO_STATUS_SUCCESS; } static cairo_status_t curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { struct stroker *stroker = closure; cairo_spline_t spline; cairo_stroke_face_t face; if (stroker->has_bounds && ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, &stroker->bounds)) return line_to (closure, d); if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_face.point, b, c, d)) return line_to (closure, d); compute_face (&stroker->current_face.point, &spline.initial_slope, stroker, &face); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &face); /* Join with final face from previous segment */ outer_join (stroker, &stroker->current_face, &face, clockwise); inner_join (stroker, &stroker->current_face, &face, clockwise); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &face.cw); contour_add_point (stroker, &stroker->ccw, &face.ccw); } stroker->current_face = face; return _cairo_spline_decompose (&spline, stroker->tolerance); } static cairo_status_t close_path (void *closure) { struct stroker *stroker = closure; cairo_status_t status; status = line_to (stroker, &stroker->first_point); if (unlikely (status)) return status; if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ outer_close (stroker, &stroker->current_face, &stroker->first_face); inner_close (stroker, &stroker->current_face, &stroker->first_face); #if 0 *_cairo_contour_first_point (&stroker->ccw.contour) = *_cairo_contour_last_point (&stroker->ccw.contour); *_cairo_contour_first_point (&stroker->cw.contour) = *_cairo_contour_last_point (&stroker->cw.contour); #endif _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); #if DEBUG { FILE *file = fopen ("contours.txt", "a"); _cairo_debug_print_contour (file, &stroker->path); _cairo_debug_print_contour (file, &stroker->cw.contour); _cairo_debug_print_contour (file, &stroker->ccw.contour); fclose (file); _cairo_contour_reset (&stroker->path); } #endif _cairo_contour_reset (&stroker->cw.contour); _cairo_contour_reset (&stroker->ccw.contour); } else { /* Cap the start and end of the sub path as needed */ add_caps (stroker); } stroker->has_initial_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_polygon_t *polygon) { struct stroker stroker; cairo_status_t status; if (style->num_dashes) { return _cairo_path_fixed_stroke_dashed_to_polygon (path, style, ctm, ctm_inverse, tolerance, polygon); } stroker.has_bounds = polygon->num_limits; if (stroker.has_bounds) { /* Extend the bounds in each direction to account for the maximum area * we might generate trapezoids, to capture line segments that are * outside of the bounds but which might generate rendering that's * within bounds. */ double dx, dy; cairo_fixed_t fdx, fdy; int i; stroker.bounds = polygon->limits[0]; for (i = 1; i < polygon->num_limits; i++) _cairo_box_add_box (&stroker.bounds, &polygon->limits[i]); _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); stroker.bounds.p1.x -= fdx; stroker.bounds.p2.x += fdx; stroker.bounds.p1.y -= fdy; stroker.bounds.p2.y += fdy; } stroker.style = *style; stroker.ctm = ctm; stroker.ctm_inverse = ctm_inverse; stroker.tolerance = tolerance; stroker.half_line_width = style->line_width / 2.; /* To test whether we need to join two segments of a spline using * a round-join or a bevel-join, we can inspect the angle between the * two segments. If the difference between the chord distance * (half-line-width times the cosine of the bisection angle) and the * half-line-width itself is greater than tolerance then we need to * inject a point. */ stroker.spline_cusp_tolerance = 1 - tolerance / stroker.half_line_width; stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance; stroker.spline_cusp_tolerance *= 2; stroker.spline_cusp_tolerance -= 1; stroker.ctm_det_positive = _cairo_matrix_compute_determinant (ctm) >= 0.0; stroker.pen.num_vertices = 0; if (path->has_curve_to || style->line_join == CAIRO_LINE_JOIN_ROUND || style->line_cap == CAIRO_LINE_CAP_ROUND) { status = _cairo_pen_init (&stroker.pen, stroker.half_line_width, tolerance, ctm); if (unlikely (status)) return status; /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ if (stroker.pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; } stroker.has_current_face = FALSE; stroker.has_first_face = FALSE; stroker.has_initial_sub_path = FALSE; #if DEBUG remove ("contours.txt"); remove ("polygons.txt"); _cairo_contour_init (&stroker.path, 0); #endif _cairo_contour_init (&stroker.cw.contour, 1); _cairo_contour_init (&stroker.ccw.contour, -1); tolerance *= CAIRO_FIXED_ONE; tolerance *= tolerance; stroker.contour_tolerance = tolerance; stroker.polygon = polygon; status = _cairo_path_fixed_interpret (path, move_to, line_to, curve_to, close_path, &stroker); /* Cap the start and end of the final sub path as needed */ if (likely (status == CAIRO_STATUS_SUCCESS)) add_caps (&stroker); _cairo_contour_fini (&stroker.cw.contour); _cairo_contour_fini (&stroker.ccw.contour); if (stroker.pen.num_vertices) _cairo_pen_fini (&stroker.pen); #if DEBUG { FILE *file = fopen ("polygons.txt", "a"); _cairo_debug_print_polygon (file, polygon); fclose (file); } #endif return status; } ������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-stroke-traps.c�����������������������0000664�0000000�0000000�00000074620�12710376503�0027140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2013 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" #include "cairo-stroke-dash-private.h" #include "cairo-traps-private.h" #include <float.h> struct stroker { const cairo_stroke_style_t *style; const cairo_matrix_t *ctm; const cairo_matrix_t *ctm_inverse; double spline_cusp_tolerance; double half_line_width; double tolerance; double ctm_determinant; cairo_bool_t ctm_det_positive; cairo_line_join_t line_join; cairo_traps_t *traps; cairo_pen_t pen; cairo_point_t first_point; cairo_bool_t has_initial_sub_path; cairo_bool_t has_current_face; cairo_stroke_face_t current_face; cairo_bool_t has_first_face; cairo_stroke_face_t first_face; cairo_stroker_dash_t dash; cairo_bool_t has_bounds; cairo_box_t tight_bounds; cairo_box_t line_bounds; cairo_box_t join_bounds; }; static cairo_status_t stroker_init (struct stroker *stroker, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps) { cairo_status_t status; stroker->style = style; stroker->ctm = ctm; stroker->ctm_inverse = NULL; if (! _cairo_matrix_is_identity (ctm_inverse)) stroker->ctm_inverse = ctm_inverse; stroker->line_join = style->line_join; stroker->half_line_width = style->line_width / 2.0; stroker->tolerance = tolerance; stroker->traps = traps; /* To test whether we need to join two segments of a spline using * a round-join or a bevel-join, we can inspect the angle between the * two segments. If the difference between the chord distance * (half-line-width times the cosine of the bisection angle) and the * half-line-width itself is greater than tolerance then we need to * inject a point. */ stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width; stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance; stroker->spline_cusp_tolerance *= 2; stroker->spline_cusp_tolerance -= 1; stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; status = _cairo_pen_init (&stroker->pen, stroker->half_line_width, tolerance, ctm); if (unlikely (status)) return status; stroker->has_current_face = FALSE; stroker->has_first_face = FALSE; stroker->has_initial_sub_path = FALSE; _cairo_stroker_dash_init (&stroker->dash, style); stroker->has_bounds = traps->num_limits; if (stroker->has_bounds) { /* Extend the bounds in each direction to account for the maximum area * we might generate trapezoids, to capture line segments that are outside * of the bounds but which might generate rendering that's within bounds. */ double dx, dy; cairo_fixed_t fdx, fdy; stroker->tight_bounds = traps->bounds; _cairo_stroke_style_max_distance_from_path (stroker->style, path, stroker->ctm, &dx, &dy); _cairo_stroke_style_max_line_distance_from_path (stroker->style, path, stroker->ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); stroker->line_bounds = stroker->tight_bounds; stroker->line_bounds.p1.x -= fdx; stroker->line_bounds.p2.x += fdx; stroker->line_bounds.p1.y -= fdy; stroker->line_bounds.p2.y += fdy; _cairo_stroke_style_max_join_distance_from_path (stroker->style, path, stroker->ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); stroker->join_bounds = stroker->tight_bounds; stroker->join_bounds.p1.x -= fdx; stroker->join_bounds.p2.x += fdx; stroker->join_bounds.p1.y -= fdy; stroker->join_bounds.p2.y += fdy; } return CAIRO_STATUS_SUCCESS; } static void stroker_fini (struct stroker *stroker) { _cairo_pen_fini (&stroker->pen); } static void translate_point (cairo_point_t *point, cairo_point_t *offset) { point->x += offset->x; point->y += offset->y; } static int join_is_clockwise (const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; } static int slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) { double c = dx1 * dy2 - dx2 * dy1; if (c > 0) return 1; if (c < 0) return -1; return 0; } static cairo_bool_t stroker_intersects_join (const struct stroker *stroker, const cairo_point_t *in, const cairo_point_t *out) { cairo_line_t segment; if (! stroker->has_bounds) return TRUE; segment.p1 = *in; segment.p2 = *out; return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment); } static void join (struct stroker *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out) { int clockwise = join_is_clockwise (out, in); cairo_point_t *inpt, *outpt; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } if (clockwise) { inpt = &in->ccw; outpt = &out->ccw; } else { inpt = &in->cw; outpt = &out->cw; } if (! stroker_intersects_join (stroker, inpt, outpt)) return; switch (stroker->line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ if ((in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance) { int start, stop; cairo_point_t tri[3]; cairo_pen_t *pen = &stroker->pen; tri[0] = in->point; tri[1] = *inpt; if (clockwise) { _cairo_pen_find_active_ccw_vertices (pen, &in->dev_vector, &out->dev_vector, &start, &stop); while (start != stop) { tri[2] = in->point; translate_point (&tri[2], &pen->vertices[start].point); _cairo_traps_tessellate_triangle (stroker->traps, tri); tri[1] = tri[2]; if (start-- == 0) start += pen->num_vertices; } } else { _cairo_pen_find_active_cw_vertices (pen, &in->dev_vector, &out->dev_vector, &start, &stop); while (start != stop) { tri[2] = in->point; translate_point (&tri[2], &pen->vertices[start].point); _cairo_traps_tessellate_triangle (stroker->traps, tri); tri[1] = tri[2]; if (++start == pen->num_vertices) start = 0; } } tri[2] = *outpt; _cairo_traps_tessellate_triangle (stroker->traps, tri); break; } case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = (-in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.y); double ml = stroker->style->miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; cairo_point_t outer; cairo_point_t quad[4]; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { /* * Draw the quadrilateral */ outer.x = _cairo_fixed_from_double (mx); outer.y = _cairo_fixed_from_double (my); quad[0] = in->point; quad[1] = *inpt; quad[2] = outer; quad[3] = *outpt; _cairo_traps_tessellate_convex_quad (stroker->traps, quad); break; } } /* fall through ... */ } case CAIRO_LINE_JOIN_BEVEL: { cairo_point_t tri[3]; tri[0] = in->point; tri[1] = *inpt; tri[2] = *outpt; _cairo_traps_tessellate_triangle (stroker->traps, tri); break; } } } static void add_cap (struct stroker *stroker, cairo_stroke_face_t *f) { switch (stroker->style->line_cap) { case CAIRO_LINE_CAP_ROUND: { int start, stop; cairo_slope_t in_slope, out_slope; cairo_point_t tri[3]; cairo_pen_t *pen = &stroker->pen; in_slope = f->dev_vector; out_slope.dx = -in_slope.dx; out_slope.dy = -in_slope.dy; _cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope, &start, &stop); tri[0] = f->point; tri[1] = f->cw; while (start != stop) { tri[2] = f->point; translate_point (&tri[2], &pen->vertices[start].point); _cairo_traps_tessellate_triangle (stroker->traps, tri); tri[1] = tri[2]; if (++start == pen->num_vertices) start = 0; } tri[2] = f->ccw; _cairo_traps_tessellate_triangle (stroker->traps, tri); break; } case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; cairo_point_t quad[4]; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->half_line_width; dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); quad[0] = f->cw; quad[1].x = f->cw.x + fvector.dx; quad[1].y = f->cw.y + fvector.dy; quad[2].x = f->ccw.x + fvector.dx; quad[2].y = f->ccw.y + fvector.dy; quad[3] = f->ccw; _cairo_traps_tessellate_convex_quad (stroker->traps, quad); break; } case CAIRO_LINE_CAP_BUTT: default: break; } } static void add_leading_cap (struct stroker *stroker, cairo_stroke_face_t *face) { cairo_stroke_face_t reversed; cairo_point_t t; reversed = *face; /* The initial cap needs an outward facing vector. Reverse everything */ reversed.usr_vector.x = -reversed.usr_vector.x; reversed.usr_vector.y = -reversed.usr_vector.y; reversed.dev_vector.dx = -reversed.dev_vector.dx; reversed.dev_vector.dy = -reversed.dev_vector.dy; t = reversed.cw; reversed.cw = reversed.ccw; reversed.ccw = t; add_cap (stroker, &reversed); } static void add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face) { add_cap (stroker, face); } static inline double normalize_slope (double *dx, double *dy) { double dx0 = *dx, dy0 = *dy; if (dx0 == 0.0 && dy0 == 0.0) return 0; if (dx0 == 0.0) { *dx = 0.0; if (dy0 > 0.0) { *dy = 1.0; return dy0; } else { *dy = -1.0; return -dy0; } } else if (dy0 == 0.0) { *dy = 0.0; if (dx0 > 0.0) { *dx = 1.0; return dx0; } else { *dx = -1.0; return -dx0; } } else { double mag = hypot (dx0, dy0); *dx = dx0 / mag; *dy = dy0 / mag; return mag; } } static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; double slope_dx, slope_dy; slope_dx = _cairo_fixed_to_double (dev_slope->dx); slope_dy = _cairo_fixed_to_double (dev_slope->dy); face->length = normalize_slope (&slope_dx, &slope_dy); face->dev_slope.x = slope_dx; face->dev_slope.y = slope_dy; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (stroker->ctm_inverse) { cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); normalize_slope (&slope_dx, &slope_dy); if (stroker->ctm_det_positive) { face_dx = - slope_dy * stroker->half_line_width; face_dy = slope_dx * stroker->half_line_width; } else { face_dx = slope_dy * stroker->half_line_width; face_dy = - slope_dx * stroker->half_line_width; } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); } else { face_dx = - slope_dy * stroker->half_line_width; face_dy = slope_dx * stroker->half_line_width; } offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; } static void add_caps (struct stroker *stroker) { /* check for a degenerative sub_path */ if (stroker->has_initial_sub_path && !stroker->has_first_face && !stroker->has_current_face && stroker->style->line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; cairo_stroke_face_t face; /* arbitrarily choose first_point * first_point and current_point should be the same */ compute_face (&stroker->first_point, &slope, stroker, &face); add_leading_cap (stroker, &face); add_trailing_cap (stroker, &face); } if (stroker->has_first_face) add_leading_cap (stroker, &stroker->first_face); if (stroker->has_current_face) add_trailing_cap (stroker, &stroker->current_face); } static cairo_bool_t stroker_intersects_edge (const struct stroker *stroker, const cairo_stroke_face_t *start, const cairo_stroke_face_t *end) { cairo_box_t box; if (! stroker->has_bounds) return TRUE; if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw)) return TRUE; box.p2 = box.p1 = start->cw; if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw)) return TRUE; _cairo_box_add_point (&box, &start->ccw); if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw)) return TRUE; _cairo_box_add_point (&box, &end->cw); if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw)) return TRUE; _cairo_box_add_point (&box, &end->ccw); return (box.p2.x > stroker->tight_bounds.p1.x && box.p1.x < stroker->tight_bounds.p2.x && box.p2.y > stroker->tight_bounds.p1.y && box.p1.y < stroker->tight_bounds.p2.y); } static void add_sub_edge (struct stroker *stroker, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_slope_t *dev_slope, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { cairo_point_t rectangle[4]; compute_face (p1, dev_slope, stroker, start); *end = *start; end->point = *p2; rectangle[0].x = p2->x - p1->x; rectangle[0].y = p2->y - p1->y; translate_point (&end->ccw, &rectangle[0]); translate_point (&end->cw, &rectangle[0]); if (p1->x == p2->x && p1->y == p2->y) return; if (! stroker_intersects_edge (stroker, start, end)) return; rectangle[0] = start->cw; rectangle[1] = start->ccw; rectangle[2] = end->ccw; rectangle[3] = end->cw; _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); } static cairo_status_t move_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; /* Cap the start and end of the previous sub path as needed */ add_caps (stroker); stroker->first_point = *point; stroker->current_face.point = *point; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; stroker->has_initial_sub_path = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t move_to_dashed (void *closure, const cairo_point_t *point) { /* reset the dash pattern for new sub paths */ struct stroker *stroker = closure; _cairo_stroker_dash_start (&stroker->dash); return move_to (closure, point); } static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start, end; const cairo_point_t *p1 = &stroker->current_face.point; const cairo_point_t *p2 = point; cairo_slope_t dev_slope; stroker->has_initial_sub_path = TRUE; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, p2); add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end); if (stroker->has_current_face) { /* Join with final face from previous segment */ join (stroker, &stroker->current_face, &start); } else if (!stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->current_face = end; stroker->has_current_face = TRUE; return CAIRO_STATUS_SUCCESS; } /* * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t line_to_dashed (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; double mag, remain, step_length = 0; double slope_dx, slope_dy; double dx2, dy2; cairo_stroke_face_t sub_start, sub_end; const cairo_point_t *p1 = &stroker->current_face.point; const cairo_point_t *p2 = point; cairo_slope_t dev_slope; cairo_line_t segment; cairo_bool_t fully_in_bounds; stroker->has_initial_sub_path = stroker->dash.dash_starts_on; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->join_bounds, p1) || ! _cairo_box_contains_point (&stroker->join_bounds, p2))) { fully_in_bounds = FALSE; } _cairo_slope_init (&dev_slope, p1, p2); slope_dx = _cairo_fixed_to_double (p2->x - p1->x); slope_dy = _cairo_fixed_to_double (p2->y - p1->y); if (stroker->ctm_inverse) cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); mag = normalize_slope (&slope_dx, &slope_dy); if (mag <= DBL_EPSILON) return CAIRO_STATUS_SUCCESS; remain = mag; segment.p1 = *p1; while (remain) { step_length = MIN (stroker->dash.dash_remain, remain); remain -= step_length; dx2 = slope_dx * (mag - remain); dy2 = slope_dy * (mag - remain); cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; if (stroker->dash.dash_on && (fully_in_bounds || (! stroker->has_first_face && stroker->dash.dash_starts_on) || _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment))) { add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, &sub_start, &sub_end); if (stroker->has_current_face) { /* Join with final face from previous segment */ join (stroker, &stroker->current_face, &sub_start); stroker->has_current_face = FALSE; } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = sub_start; stroker->has_first_face = TRUE; } else { /* Cap dash start if not connecting to a previous segment */ add_leading_cap (stroker, &sub_start); } if (remain) { /* Cap dash end if not at end of segment */ add_trailing_cap (stroker, &sub_end); } else { stroker->current_face = sub_end; stroker->has_current_face = TRUE; } } else { if (stroker->has_current_face) { /* Cap final face from previous segment */ add_trailing_cap (stroker, &stroker->current_face); stroker->has_current_face = FALSE; } } _cairo_stroker_dash_step (&stroker->dash, step_length); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! stroker->has_current_face) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. * * Note: this will create a degenerate cap if this is not the last line * in the path. Whether this behaviour is desirable or not is debatable. * On one side these degenerate caps can not be reproduced with regular * path stroking. * On the other hand, Acroread 7 also produces the degenerate caps. */ compute_face (point, &dev_slope, stroker, &stroker->current_face); add_leading_cap (stroker, &stroker->current_face); stroker->has_current_face = TRUE; } else stroker->current_face.point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { struct stroker *stroker = closure; cairo_stroke_face_t face; if ((tangent->dx | tangent->dy) == 0) { cairo_point_t t; face = stroker->current_face; face.usr_vector.x = -face.usr_vector.x; face.usr_vector.y = -face.usr_vector.y; face.dev_slope.x = -face.dev_slope.x; face.dev_slope.y = -face.dev_slope.y; face.dev_vector.dx = -face.dev_vector.dx; face.dev_vector.dy = -face.dev_vector.dy; t = face.cw; face.cw = face.ccw; face.ccw = t; join (stroker, &stroker->current_face, &face); } else { cairo_point_t rectangle[4]; compute_face (&stroker->current_face.point, tangent, stroker, &face); join (stroker, &stroker->current_face, &face); rectangle[0] = face.cw; rectangle[1] = face.ccw; rectangle[2].x = point->x - face.point.x; rectangle[2].y = point->y - face.point.y; face.point = *point; translate_point (&face.ccw, &rectangle[2]); translate_point (&face.cw, &rectangle[2]); rectangle[2] = face.ccw; rectangle[3] = face.cw; _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); } stroker->current_face = face; return CAIRO_STATUS_SUCCESS; } static cairo_status_t curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { struct stroker *stroker = closure; cairo_line_join_t line_join_save; cairo_spline_t spline; cairo_stroke_face_t face; cairo_status_t status; if (stroker->has_bounds && ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, &stroker->line_bounds)) return line_to (closure, d); if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_face.point, b, c, d)) return line_to (closure, d); compute_face (&stroker->current_face.point, &spline.initial_slope, stroker, &face); if (stroker->has_current_face) { /* Join with final face from previous segment */ join (stroker, &stroker->current_face, &face); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; } stroker->current_face = face; /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->line_join; stroker->line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); stroker->line_join = line_join_save; return status; } static cairo_status_t curve_to_dashed (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { struct stroker *stroker = closure; cairo_spline_t spline; cairo_line_join_t line_join_save; cairo_spline_add_point_func_t func; cairo_status_t status; func = (cairo_spline_add_point_func_t)line_to_dashed; if (stroker->has_bounds && ! _cairo_spline_intersects (&stroker->current_face.point, b, c, b, &stroker->line_bounds)) return func (closure, d, NULL); if (! _cairo_spline_init (&spline, func, stroker, &stroker->current_face.point, b, c, d)) return func (closure, d, NULL); /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->line_join; stroker->line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); stroker->line_join = line_join_save; return status; } static cairo_status_t _close_path (struct stroker *stroker) { if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ join (stroker, &stroker->current_face, &stroker->first_face); } else { /* Cap the start and end of the sub path as needed */ add_caps (stroker); } stroker->has_initial_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t close_path (void *closure) { struct stroker *stroker = closure; cairo_status_t status; status = line_to (stroker, &stroker->first_point); if (unlikely (status)) return status; return _close_path (stroker); } static cairo_status_t close_path_dashed (void *closure) { struct stroker *stroker = closure; cairo_status_t status; status = line_to_dashed (stroker, &stroker->first_point); if (unlikely (status)) return status; return _close_path (stroker); } cairo_int_status_t _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps) { struct stroker stroker; cairo_status_t status; status = stroker_init (&stroker, path, style, ctm, ctm_inverse, tolerance, traps); if (unlikely (status)) return status; if (stroker.dash.dashed) status = _cairo_path_fixed_interpret (path, move_to_dashed, line_to_dashed, curve_to_dashed, close_path_dashed, &stroker); else status = _cairo_path_fixed_interpret (path, move_to, line_to, curve_to, close_path, &stroker); assert(status == CAIRO_STATUS_SUCCESS); add_caps (&stroker); stroker_fini (&stroker); return traps->status; } ����������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-stroke-tristrip.c��������������������0000664�0000000�0000000�00000072373�12710376503�0027672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" #include "cairo-tristrip-private.h" struct stroker { cairo_stroke_style_t style; cairo_tristrip_t *strip; const cairo_matrix_t *ctm; const cairo_matrix_t *ctm_inverse; double tolerance; cairo_bool_t ctm_det_positive; cairo_pen_t pen; cairo_bool_t has_sub_path; cairo_point_t first_point; cairo_bool_t has_current_face; cairo_stroke_face_t current_face; cairo_bool_t has_first_face; cairo_stroke_face_t first_face; cairo_box_t limit; cairo_bool_t has_limits; }; static inline double normalize_slope (double *dx, double *dy); static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face); static void translate_point (cairo_point_t *point, const cairo_point_t *offset) { point->x += offset->x; point->y += offset->y; } static int slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) { double c = (dx1 * dy2 - dx2 * dy1); if (c > 0) return 1; if (c < 0) return -1; return 0; } static inline int range_step (int i, int step, int max) { i += step; if (i < 0) i = max - 1; if (i >= max) i = 0; return i; } /* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static void add_fan (struct stroker *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, const cairo_point_t *inpt, const cairo_point_t *outpt, cairo_bool_t clockwise) { int start, stop, step, i, npoints; if (clockwise) { step = 1; start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, in_vector) < 0) start = range_step (start, 1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, out_vector) > 0) { stop = range_step (stop, -1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, in_vector) < 0) return; } npoints = stop - start; } else { step = -1; start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, in_vector) < 0) start = range_step (start, -1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, out_vector) > 0) { stop = range_step (stop, 1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, in_vector) < 0) return; } npoints = start - stop; } stop = range_step (stop, step, stroker->pen.num_vertices); if (npoints < 0) npoints += stroker->pen.num_vertices; if (npoints <= 1) return; for (i = start; i != stop; i = range_step (i, step, stroker->pen.num_vertices)) { cairo_point_t p = *midpt; translate_point (&p, &stroker->pen.vertices[i].point); //contour_add_point (stroker, c, &p); } } static int join_is_clockwise (const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; } static void inner_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { const cairo_point_t *outpt; if (clockwise) { outpt = &out->ccw; } else { outpt = &out->cw; } //contour_add_point (stroker, inner, &in->point); //contour_add_point (stroker, inner, outpt); } static void inner_close (struct stroker *stroker, const cairo_stroke_face_t *in, cairo_stroke_face_t *out) { const cairo_point_t *inpt; if (join_is_clockwise (in, out)) { inpt = &out->ccw; } else { inpt = &out->cw; } //contour_add_point (stroker, inner, &in->point); //contour_add_point (stroker, inner, inpt); //*_cairo_contour_first_point (&inner->contour) = //*_cairo_contour_last_point (&inner->contour); } static void outer_close (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { const cairo_point_t *inpt, *outpt; int clockwise; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } clockwise = join_is_clockwise (in, out); if (clockwise) { inpt = &in->cw; outpt = &out->cw; } else { inpt = &in->ccw; outpt = &out->ccw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, inpt, outpt, clockwise); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = -in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); //*_cairo_contour_last_point (&outer->contour) = p; //*_cairo_contour_first_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } //contour_add_point (stroker, outer, outpt); } static void outer_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { const cairo_point_t *inpt, *outpt; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } if (clockwise) { inpt = &in->cw; outpt = &out->cw; } else { inpt = &in->ccw; outpt = &out->ccw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, inpt, outpt, clockwise); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = -in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); //*_cairo_contour_last_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } //contour_add_point (stroker,outer, outpt); } static void add_cap (struct stroker *stroker, const cairo_stroke_face_t *f) { switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; slope.dx = -f->dev_vector.dx; slope.dy = -f->dev_vector.dy; add_fan (stroker, &f->dev_vector, &slope, &f->point, &f->ccw, &f->cw, FALSE); break; } case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; cairo_point_t quad[4]; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->style.line_width / 2.0; dy *= stroker->style.line_width / 2.0; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); quad[0] = f->ccw; quad[1].x = f->ccw.x + fvector.dx; quad[1].y = f->ccw.y + fvector.dy; quad[2].x = f->cw.x + fvector.dx; quad[2].y = f->cw.y + fvector.dy; quad[3] = f->cw; //contour_add_point (stroker, c, &quad[1]); //contour_add_point (stroker, c, &quad[2]); } case CAIRO_LINE_CAP_BUTT: default: break; } //contour_add_point (stroker, c, &f->cw); } static void add_leading_cap (struct stroker *stroker, const cairo_stroke_face_t *face) { cairo_stroke_face_t reversed; cairo_point_t t; reversed = *face; /* The initial cap needs an outward facing vector. Reverse everything */ reversed.usr_vector.x = -reversed.usr_vector.x; reversed.usr_vector.y = -reversed.usr_vector.y; reversed.dev_vector.dx = -reversed.dev_vector.dx; reversed.dev_vector.dy = -reversed.dev_vector.dy; t = reversed.cw; reversed.cw = reversed.ccw; reversed.ccw = t; add_cap (stroker, &reversed); } static void add_trailing_cap (struct stroker *stroker, const cairo_stroke_face_t *face) { add_cap (stroker, face); } static inline double normalize_slope (double *dx, double *dy) { double dx0 = *dx, dy0 = *dy; double mag; assert (dx0 != 0.0 || dy0 != 0.0); if (dx0 == 0.0) { *dx = 0.0; if (dy0 > 0.0) { mag = dy0; *dy = 1.0; } else { mag = -dy0; *dy = -1.0; } } else if (dy0 == 0.0) { *dy = 0.0; if (dx0 > 0.0) { mag = dx0; *dx = 1.0; } else { mag = -dx0; *dx = -1.0; } } else { mag = hypot (dx0, dy0); *dx = dx0 / mag; *dy = dy0 / mag; } return mag; } static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; double slope_dx, slope_dy; slope_dx = _cairo_fixed_to_double (dev_slope->dx); slope_dy = _cairo_fixed_to_double (dev_slope->dy); face->length = normalize_slope (&slope_dx, &slope_dy); face->dev_slope.x = slope_dx; face->dev_slope.y = slope_dy; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { /* Normalize the matrix! */ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); normalize_slope (&slope_dx, &slope_dy); if (stroker->ctm_det_positive) { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } else { face_dx = slope_dy * (stroker->style.line_width / 2.0); face_dy = - slope_dx * (stroker->style.line_width / 2.0); } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); } else { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; } static void add_caps (struct stroker *stroker) { /* check for a degenerative sub_path */ if (stroker->has_sub_path && ! stroker->has_first_face && ! stroker->has_current_face && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; cairo_stroke_face_t face; /* arbitrarily choose first_point */ compute_face (&stroker->first_point, &slope, stroker, &face); add_leading_cap (stroker, &face); add_trailing_cap (stroker, &face); /* ensure the circle is complete */ //_cairo_contour_add_point (&stroker->ccw.contour, //_cairo_contour_first_point (&stroker->ccw.contour)); } else { if (stroker->has_current_face) add_trailing_cap (stroker, &stroker->current_face); //_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); //_cairo_contour_reset (&stroker->ccw.contour); if (stroker->has_first_face) { //_cairo_contour_add_point (&stroker->ccw.contour, //&stroker->first_face.cw); add_leading_cap (stroker, &stroker->first_face); //_cairo_polygon_add_contour (stroker->polygon, //&stroker->ccw.contour); //_cairo_contour_reset (&stroker->ccw.contour); } } } static cairo_status_t move_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; /* Cap the start and end of the previous sub path as needed */ add_caps (stroker); stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; stroker->has_sub_path = FALSE; stroker->first_point = *point; stroker->current_face.point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &start); /* Join with final face from previous segment */ outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; _cairo_tristrip_move_to (stroker->strip, &start.cw); stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; _cairo_tristrip_add_point (stroker->strip, &start.cw); _cairo_tristrip_add_point (stroker->strip, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw); _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; } static cairo_status_t spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { struct stroker *stroker = closure; cairo_stroke_face_t face; if (tangent->dx == 0 && tangent->dy == 0) { const cairo_point_t *inpt, *outpt; cairo_point_t t; int clockwise; face = stroker->current_face; face.usr_vector.x = -face.usr_vector.x; face.usr_vector.y = -face.usr_vector.y; face.dev_vector.dx = -face.dev_vector.dx; face.dev_vector.dy = -face.dev_vector.dy; t = face.cw; face.cw = face.ccw; face.ccw = t; clockwise = join_is_clockwise (&stroker->current_face, &face); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, inpt, outpt, clockwise); } else { compute_face (point, tangent, stroker, &face); if (face.dev_slope.x * stroker->current_face.dev_slope.x + face.dev_slope.y * stroker->current_face.dev_slope.y < 0) { const cairo_point_t *inpt, *outpt; int clockwise = join_is_clockwise (&stroker->current_face, &face); stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; //contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; //contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, inpt, outpt, clockwise); } _cairo_tristrip_add_point (stroker->strip, &face.cw); _cairo_tristrip_add_point (stroker->strip, &face.ccw); } stroker->current_face = face; return CAIRO_STATUS_SUCCESS; } static cairo_status_t curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { struct stroker *stroker = closure; cairo_spline_t spline; cairo_stroke_face_t face; if (stroker->has_limits) { if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, &stroker->limit)) return line_to (closure, d); } if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_face.point, b, c, d)) return line_to (closure, d); compute_face (&stroker->current_face.point, &spline.initial_slope, stroker, &face); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &face); /* Join with final face from previous segment */ outer_join (stroker, &stroker->current_face, &face, clockwise); inner_join (stroker, &stroker->current_face, &face, clockwise); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = face; _cairo_tristrip_move_to (stroker->strip, &face.cw); stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; _cairo_tristrip_add_point (stroker->strip, &face.cw); _cairo_tristrip_add_point (stroker->strip, &face.ccw); } stroker->current_face = face; return _cairo_spline_decompose (&spline, stroker->tolerance); } static cairo_status_t close_path (void *closure) { struct stroker *stroker = closure; cairo_status_t status; status = line_to (stroker, &stroker->first_point); if (unlikely (status)) return status; if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ outer_close (stroker, &stroker->current_face, &stroker->first_face); inner_close (stroker, &stroker->current_face, &stroker->first_face); } else { /* Cap the start and end of the sub path as needed */ add_caps (stroker); } stroker->has_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path, const cairo_stroke_style_t*style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_tristrip_t *strip) { struct stroker stroker; cairo_int_status_t status; int i; if (style->num_dashes) return CAIRO_INT_STATUS_UNSUPPORTED; stroker.style = *style; stroker.ctm = ctm; stroker.ctm_inverse = ctm_inverse; stroker.tolerance = tolerance; stroker.ctm_det_positive = _cairo_matrix_compute_determinant (ctm) >= 0.0; status = _cairo_pen_init (&stroker.pen, style->line_width / 2.0, tolerance, ctm); if (unlikely (status)) return status; if (stroker.pen.num_vertices <= 1) return CAIRO_INT_STATUS_NOTHING_TO_DO; stroker.has_current_face = FALSE; stroker.has_first_face = FALSE; stroker.has_sub_path = FALSE; stroker.has_limits = strip->num_limits > 0; stroker.limit = strip->limits[0]; for (i = 1; i < strip->num_limits; i++) _cairo_box_add_box (&stroker.limit, &strip->limits[i]); stroker.strip = strip; status = _cairo_path_fixed_interpret (path, move_to, line_to, curve_to, close_path, &stroker); /* Cap the start and end of the final sub path as needed */ if (likely (status == CAIRO_INT_STATUS_SUCCESS)) add_caps (&stroker); _cairo_pen_fini (&stroker.pen); return status; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path-stroke.c�����������������������������0000664�0000000�0000000�00000120523�12710376503�0026003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #define _BSD_SOURCE /* for hypot() */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" #include "cairo-stroke-dash-private.h" #include "cairo-traps-private.h" typedef struct cairo_stroker { cairo_stroke_style_t style; const cairo_matrix_t *ctm; const cairo_matrix_t *ctm_inverse; double half_line_width; double tolerance; double spline_cusp_tolerance; double ctm_determinant; cairo_bool_t ctm_det_positive; void *closure; cairo_status_t (*add_external_edge) (void *closure, const cairo_point_t *p1, const cairo_point_t *p2); cairo_status_t (*add_triangle) (void *closure, const cairo_point_t triangle[3]); cairo_status_t (*add_triangle_fan) (void *closure, const cairo_point_t *midpt, const cairo_point_t *points, int npoints); cairo_status_t (*add_convex_quad) (void *closure, const cairo_point_t quad[4]); cairo_pen_t pen; cairo_point_t current_point; cairo_point_t first_point; cairo_bool_t has_initial_sub_path; cairo_bool_t has_current_face; cairo_stroke_face_t current_face; cairo_bool_t has_first_face; cairo_stroke_face_t first_face; cairo_stroker_dash_t dash; cairo_bool_t has_bounds; cairo_box_t bounds; } cairo_stroker_t; static void _cairo_stroker_limit (cairo_stroker_t *stroker, const cairo_path_fixed_t *path, const cairo_box_t *boxes, int num_boxes) { double dx, dy; cairo_fixed_t fdx, fdy; stroker->has_bounds = TRUE; _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); /* Extend the bounds in each direction to account for the maximum area * we might generate trapezoids, to capture line segments that are outside * of the bounds but which might generate rendering that's within bounds. */ _cairo_stroke_style_max_distance_from_path (&stroker->style, path, stroker->ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); stroker->bounds.p1.x -= fdx; stroker->bounds.p2.x += fdx; stroker->bounds.p1.y -= fdy; stroker->bounds.p2.y += fdy; } static cairo_status_t _cairo_stroker_init (cairo_stroker_t *stroker, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, const cairo_box_t *limits, int num_limits) { cairo_status_t status; stroker->style = *stroke_style; stroker->ctm = ctm; stroker->ctm_inverse = ctm_inverse; stroker->tolerance = tolerance; stroker->half_line_width = stroke_style->line_width / 2.0; /* To test whether we need to join two segments of a spline using * a round-join or a bevel-join, we can inspect the angle between the * two segments. If the difference between the chord distance * (half-line-width times the cosine of the bisection angle) and the * half-line-width itself is greater than tolerance then we need to * inject a point. */ stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width; stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance; stroker->spline_cusp_tolerance *= 2; stroker->spline_cusp_tolerance -= 1; stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; status = _cairo_pen_init (&stroker->pen, stroker->half_line_width, tolerance, ctm); if (unlikely (status)) return status; stroker->has_current_face = FALSE; stroker->has_first_face = FALSE; stroker->has_initial_sub_path = FALSE; _cairo_stroker_dash_init (&stroker->dash, stroke_style); stroker->add_external_edge = NULL; stroker->has_bounds = FALSE; if (num_limits) _cairo_stroker_limit (stroker, path, limits, num_limits); return CAIRO_STATUS_SUCCESS; } static void _cairo_stroker_fini (cairo_stroker_t *stroker) { _cairo_pen_fini (&stroker->pen); } static void _translate_point (cairo_point_t *point, const cairo_point_t *offset) { point->x += offset->x; point->y += offset->y; } static int _cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { cairo_slope_t in_slope, out_slope; _cairo_slope_init (&in_slope, &in->point, &in->cw); _cairo_slope_init (&out_slope, &out->point, &out->cw); return _cairo_slope_compare (&in_slope, &out_slope) < 0; } /** * _cairo_slope_compare_sgn: * * Return -1, 0 or 1 depending on the relative slopes of * two lines. **/ static int _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) { double c = (dx1 * dy2 - dx2 * dy1); if (c > 0) return 1; if (c < 0) return -1; return 0; } static inline int _range_step (int i, int step, int max) { i += step; if (i < 0) i = max - 1; if (i >= max) i = 0; return i; } /* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static cairo_status_t _tessellate_fan (cairo_stroker_t *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, const cairo_point_t *inpt, const cairo_point_t *outpt, cairo_bool_t clockwise) { cairo_point_t stack_points[64], *points = stack_points; cairo_pen_t *pen = &stroker->pen; int start, stop, num_points = 0; cairo_status_t status; if (stroker->has_bounds && ! _cairo_box_contains_point (&stroker->bounds, midpt)) goto BEVEL; assert (stroker->pen.num_vertices); if (clockwise) { _cairo_pen_find_active_ccw_vertices (pen, in_vector, out_vector, &start, &stop); if (stroker->add_external_edge) { cairo_point_t last; last = *inpt; while (start != stop) { cairo_point_t p = *midpt; _translate_point (&p, &pen->vertices[start].point); status = stroker->add_external_edge (stroker->closure, &last, &p); if (unlikely (status)) return status; last = p; if (start-- == 0) start += pen->num_vertices; } status = stroker->add_external_edge (stroker->closure, &last, outpt); } else { if (start == stop) goto BEVEL; num_points = stop - start; if (num_points < 0) num_points += pen->num_vertices; num_points += 2; if (num_points > ARRAY_LENGTH(stack_points)) { points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } points[0] = *inpt; num_points = 1; while (start != stop) { points[num_points] = *midpt; _translate_point (&points[num_points], &pen->vertices[start].point); num_points++; if (start-- == 0) start += pen->num_vertices; } points[num_points++] = *outpt; } } else { _cairo_pen_find_active_cw_vertices (pen, in_vector, out_vector, &start, &stop); if (stroker->add_external_edge) { cairo_point_t last; last = *inpt; while (start != stop) { cairo_point_t p = *midpt; _translate_point (&p, &pen->vertices[start].point); status = stroker->add_external_edge (stroker->closure, &p, &last); if (unlikely (status)) return status; last = p; if (++start == pen->num_vertices) start = 0; } status = stroker->add_external_edge (stroker->closure, outpt, &last); } else { if (start == stop) goto BEVEL; num_points = stop - start; if (num_points < 0) num_points += pen->num_vertices; num_points += 2; if (num_points > ARRAY_LENGTH(stack_points)) { points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } points[0] = *inpt; num_points = 1; while (start != stop) { points[num_points] = *midpt; _translate_point (&points[num_points], &pen->vertices[start].point); num_points++; if (++start == pen->num_vertices) start = 0; } points[num_points++] = *outpt; } } if (num_points) { status = stroker->add_triangle_fan (stroker->closure, midpt, points, num_points); } if (points != stack_points) free (points); return status; BEVEL: /* Ensure a leak free connection... */ if (stroker->add_external_edge != NULL) { if (clockwise) return stroker->add_external_edge (stroker->closure, inpt, outpt); else return stroker->add_external_edge (stroker->closure, outpt, inpt); } else { stack_points[0] = *midpt; stack_points[1] = *inpt; stack_points[2] = *outpt; return stroker->add_triangle (stroker->closure, stack_points); } } static cairo_status_t _cairo_stroker_join (cairo_stroker_t *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { int clockwise = _cairo_stroker_join_is_clockwise (out, in); const cairo_point_t *inpt, *outpt; cairo_point_t points[4]; cairo_status_t status; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return CAIRO_STATUS_SUCCESS; } if (clockwise) { if (stroker->add_external_edge != NULL) { status = stroker->add_external_edge (stroker->closure, &out->cw, &in->point); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &in->point, &in->cw); if (unlikely (status)) return status; } inpt = &in->ccw; outpt = &out->ccw; } else { if (stroker->add_external_edge != NULL) { status = stroker->add_external_edge (stroker->closure, &in->ccw, &in->point); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &in->point, &out->ccw); if (unlikely (status)) return status; } inpt = &in->cw; outpt = &out->cw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ return _tessellate_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, inpt, outpt, clockwise); case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = -in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { if (stroker->add_external_edge != NULL) { points[0].x = _cairo_fixed_from_double (mx); points[0].y = _cairo_fixed_from_double (my); if (clockwise) { status = stroker->add_external_edge (stroker->closure, inpt, &points[0]); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &points[0], outpt); if (unlikely (status)) return status; } else { status = stroker->add_external_edge (stroker->closure, outpt, &points[0]); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &points[0], inpt); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } else { points[0] = in->point; points[1] = *inpt; points[2].x = _cairo_fixed_from_double (mx); points[2].y = _cairo_fixed_from_double (my); points[3] = *outpt; return stroker->add_convex_quad (stroker->closure, points); } } } } /* fall through ... */ case CAIRO_LINE_JOIN_BEVEL: if (stroker->add_external_edge != NULL) { if (clockwise) { return stroker->add_external_edge (stroker->closure, inpt, outpt); } else { return stroker->add_external_edge (stroker->closure, outpt, inpt); } } else { points[0] = in->point; points[1] = *inpt; points[2] = *outpt; return stroker->add_triangle (stroker->closure, points); } } } static cairo_status_t _cairo_stroker_add_cap (cairo_stroker_t *stroker, const cairo_stroke_face_t *f) { switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; slope.dx = -f->dev_vector.dx; slope.dy = -f->dev_vector.dy; return _tessellate_fan (stroker, &f->dev_vector, &slope, &f->point, &f->cw, &f->ccw, FALSE); } case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; cairo_point_t quad[4]; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->half_line_width; dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); quad[0] = f->ccw; quad[1].x = f->ccw.x + fvector.dx; quad[1].y = f->ccw.y + fvector.dy; quad[2].x = f->cw.x + fvector.dx; quad[2].y = f->cw.y + fvector.dy; quad[3] = f->cw; if (stroker->add_external_edge != NULL) { cairo_status_t status; status = stroker->add_external_edge (stroker->closure, &quad[0], &quad[1]); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &quad[1], &quad[2]); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &quad[2], &quad[3]); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } else { return stroker->add_convex_quad (stroker->closure, quad); } } case CAIRO_LINE_CAP_BUTT: default: if (stroker->add_external_edge != NULL) { return stroker->add_external_edge (stroker->closure, &f->ccw, &f->cw); } else { return CAIRO_STATUS_SUCCESS; } } } static cairo_status_t _cairo_stroker_add_leading_cap (cairo_stroker_t *stroker, const cairo_stroke_face_t *face) { cairo_stroke_face_t reversed; cairo_point_t t; reversed = *face; /* The initial cap needs an outward facing vector. Reverse everything */ reversed.usr_vector.x = -reversed.usr_vector.x; reversed.usr_vector.y = -reversed.usr_vector.y; reversed.dev_vector.dx = -reversed.dev_vector.dx; reversed.dev_vector.dy = -reversed.dev_vector.dy; t = reversed.cw; reversed.cw = reversed.ccw; reversed.ccw = t; return _cairo_stroker_add_cap (stroker, &reversed); } static cairo_status_t _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, const cairo_stroke_face_t *face) { return _cairo_stroker_add_cap (stroker, face); } static inline cairo_bool_t _compute_normalized_device_slope (double *dx, double *dy, const cairo_matrix_t *ctm_inverse, double *mag_out) { double dx0 = *dx, dy0 = *dy; double mag; cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0); if (dx0 == 0.0 && dy0 == 0.0) { if (mag_out) *mag_out = 0.0; return FALSE; } if (dx0 == 0.0) { *dx = 0.0; if (dy0 > 0.0) { mag = dy0; *dy = 1.0; } else { mag = -dy0; *dy = -1.0; } } else if (dy0 == 0.0) { *dy = 0.0; if (dx0 > 0.0) { mag = dx0; *dx = 1.0; } else { mag = -dx0; *dx = -1.0; } } else { mag = hypot (dx0, dy0); *dx = dx0 / mag; *dy = dy0 / mag; } if (mag_out) *mag_out = mag; return TRUE; } static void _compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroker_t *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (stroker->ctm_det_positive) { face_dx = - slope_dy * stroker->half_line_width; face_dy = slope_dx * stroker->half_line_width; } else { face_dx = slope_dy * stroker->half_line_width; face_dy = - slope_dx * stroker->half_line_width; } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; _translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; _translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; } static cairo_status_t _cairo_stroker_add_caps (cairo_stroker_t *stroker) { cairo_status_t status; /* check for a degenerative sub_path */ if (stroker->has_initial_sub_path && ! stroker->has_first_face && ! stroker->has_current_face && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ double dx = 1.0, dy = 0.0; cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; cairo_stroke_face_t face; _compute_normalized_device_slope (&dx, &dy, stroker->ctm_inverse, NULL); /* arbitrarily choose first_point * first_point and current_point should be the same */ _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); status = _cairo_stroker_add_leading_cap (stroker, &face); if (unlikely (status)) return status; status = _cairo_stroker_add_trailing_cap (stroker, &face); if (unlikely (status)) return status; } if (stroker->has_first_face) { status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face); if (unlikely (status)) return status; } if (stroker->has_current_face) { status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, const cairo_point_t *p1, const cairo_point_t *p2, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); *end = *start; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; end->point = *p2; end->ccw.x += p2->x - p1->x; end->ccw.y += p2->y - p1->y; end->cw.x += p2->x - p1->x; end->cw.y += p2->y - p1->y; if (stroker->add_external_edge != NULL) { cairo_status_t status; status = stroker->add_external_edge (stroker->closure, &end->cw, &start->cw); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &start->ccw, &end->ccw); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } else { cairo_point_t quad[4]; quad[0] = start->cw; quad[1] = end->cw; quad[2] = end->ccw; quad[3] = start->ccw; return stroker->add_convex_quad (stroker->closure, quad); } } static cairo_status_t _cairo_stroker_move_to (void *closure, const cairo_point_t *point) { cairo_stroker_t *stroker = closure; cairo_status_t status; /* reset the dash pattern for new sub paths */ _cairo_stroker_dash_start (&stroker->dash); /* Cap the start and end of the previous sub path as needed */ status = _cairo_stroker_add_caps (stroker); if (unlikely (status)) return status; stroker->first_point = *point; stroker->current_point = *point; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; stroker->has_initial_sub_path = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_line_to (void *closure, const cairo_point_t *point) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; double slope_dx, slope_dy; cairo_status_t status; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); slope_dx = _cairo_fixed_to_double (point->x - p1->x); slope_dy = _cairo_fixed_to_double (point->y - p1->y); _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL); status = _cairo_stroker_add_sub_edge (stroker, p1, point, &dev_slope, slope_dx, slope_dy, &start, &end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (unlikely (status)) return status; } else if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->current_face = end; stroker->has_current_face = TRUE; stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t new_face; double slope_dx, slope_dy; cairo_point_t points[3]; cairo_point_t intersect_point; stroker->has_initial_sub_path = TRUE; if (stroker->current_point.x == point->x && stroker->current_point.y == point->y) return CAIRO_STATUS_SUCCESS; slope_dx = _cairo_fixed_to_double (tangent->dx); slope_dy = _cairo_fixed_to_double (tangent->dy); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) return CAIRO_STATUS_SUCCESS; _compute_face (point, tangent, slope_dx, slope_dy, stroker, &new_face); assert (stroker->has_current_face); if ((new_face.dev_slope.x * stroker->current_face.dev_slope.x + new_face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) { const cairo_point_t *inpt, *outpt; int clockwise = _cairo_stroker_join_is_clockwise (&new_face, &stroker->current_face); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &new_face.cw; } else { inpt = &stroker->current_face.ccw; outpt = &new_face.ccw; } _tessellate_fan (stroker, &stroker->current_face.dev_vector, &new_face.dev_vector, &stroker->current_face.point, inpt, outpt, clockwise); } if (_slow_segment_intersection (&stroker->current_face.cw, &stroker->current_face.ccw, &new_face.cw, &new_face.ccw, &intersect_point)) { points[0] = stroker->current_face.ccw; points[1] = new_face.ccw; points[2] = intersect_point; stroker->add_triangle (stroker->closure, points); points[0] = stroker->current_face.cw; points[1] = new_face.cw; stroker->add_triangle (stroker->closure, points); } else { points[0] = stroker->current_face.ccw; points[1] = stroker->current_face.cw; points[2] = new_face.cw; stroker->add_triangle (stroker->closure, points); points[0] = stroker->current_face.ccw; points[1] = new_face.cw; points[2] = new_face.ccw; stroker->add_triangle (stroker->closure, points); } stroker->current_face = new_face; stroker->has_current_face = TRUE; stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; } /* * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t _cairo_stroker_line_to_dashed (void *closure, const cairo_point_t *p2) { cairo_stroker_t *stroker = closure; double mag, remain, step_length = 0; double slope_dx, slope_dy; double dx2, dy2; cairo_stroke_face_t sub_start, sub_end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; cairo_line_t segment; cairo_bool_t fully_in_bounds; cairo_status_t status; stroker->has_initial_sub_path = stroker->dash.dash_starts_on; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->bounds, p1) || ! _cairo_box_contains_point (&stroker->bounds, p2))) { fully_in_bounds = FALSE; } _cairo_slope_init (&dev_slope, p1, p2); slope_dx = _cairo_fixed_to_double (p2->x - p1->x); slope_dy = _cairo_fixed_to_double (p2->y - p1->y); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, &mag)) { return CAIRO_STATUS_SUCCESS; } remain = mag; segment.p1 = *p1; while (remain) { step_length = MIN (stroker->dash.dash_remain, remain); remain -= step_length; dx2 = slope_dx * (mag - remain); dy2 = slope_dy * (mag - remain); cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; if (stroker->dash.dash_on && (fully_in_bounds || (! stroker->has_first_face && stroker->dash.dash_starts_on) || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_stroker_add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = sub_start; stroker->has_first_face = TRUE; } else { /* Cap dash start if not connecting to a previous segment */ status = _cairo_stroker_add_leading_cap (stroker, &sub_start); if (unlikely (status)) return status; } if (remain) { /* Cap dash end if not at end of segment */ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); if (unlikely (status)) return status; } else { stroker->current_face = sub_end; stroker->has_current_face = TRUE; } } else { if (stroker->has_current_face) { /* Cap final face from previous segment */ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } } _cairo_stroker_dash_step (&stroker->dash, step_length); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! stroker->has_current_face) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. * * Note: this will create a degenerate cap if this is not the last line * in the path. Whether this behaviour is desirable or not is debatable. * On one side these degenerate caps can not be reproduced with regular * path stroking. * On the other hand, Acroread 7 also produces the degenerate caps. */ _compute_face (p2, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face); status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = TRUE; } stroker->current_point = *p2; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_stroker_t *stroker = closure; cairo_spline_t spline; cairo_line_join_t line_join_save; cairo_stroke_face_t face; double slope_dx, slope_dy; cairo_spline_add_point_func_t line_to; cairo_spline_add_point_func_t spline_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : (cairo_spline_add_point_func_t) _cairo_stroker_line_to; /* spline_to is only capable of rendering non-degenerate splines. */ spline_to = stroker->dash.dashed ? (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : (cairo_spline_add_point_func_t) _cairo_stroker_spline_to; if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_point, b, c, d)) { cairo_slope_t fallback_slope; _cairo_slope_init (&fallback_slope, &stroker->current_point, d); return line_to (closure, d, &fallback_slope); } /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ if (stroker->pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; /* Compute the initial face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.initial_slope, slope_dx, slope_dy, stroker, &face); } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; } else if (! stroker->has_first_face) { stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->current_face = face; stroker->has_current_face = TRUE; } /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->style.line_join; stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); if (unlikely (status)) return status; /* And join the final face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.final_slope, slope_dx, slope_dy, stroker, &face); } status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; stroker->current_face = face; } stroker->style.line_join = line_join_save; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_stroker_close_path (void *closure) { cairo_stroker_t *stroker = closure; cairo_status_t status; if (stroker->dash.dashed) status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); else status = _cairo_stroker_line_to (stroker, &stroker->first_point); if (unlikely (status)) return status; if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); if (unlikely (status)) return status; } else { /* Cap the start and end of the sub path as needed */ status = _cairo_stroker_add_caps (stroker); if (unlikely (status)) return status; } stroker->has_initial_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_status_t (*add_triangle) (void *closure, const cairo_point_t triangle[3]), cairo_status_t (*add_triangle_fan) (void *closure, const cairo_point_t *midpt, const cairo_point_t *points, int npoints), cairo_status_t (*add_convex_quad) (void *closure, const cairo_point_t quad[4]), void *closure) { cairo_stroker_t stroker; cairo_status_t status; status = _cairo_stroker_init (&stroker, path, stroke_style, ctm, ctm_inverse, tolerance, NULL, 0); if (unlikely (status)) return status; stroker.add_triangle = add_triangle; stroker.add_triangle_fan = add_triangle_fan; stroker.add_convex_quad = add_convex_quad; stroker.closure = closure; status = _cairo_path_fixed_interpret (path, _cairo_stroker_move_to, stroker.dash.dashed ? _cairo_stroker_line_to_dashed : _cairo_stroker_line_to, _cairo_stroker_curve_to, _cairo_stroker_close_path, &stroker); if (unlikely (status)) goto BAIL; /* Cap the start and end of the final sub path as needed */ status = _cairo_stroker_add_caps (&stroker); BAIL: _cairo_stroker_fini (&stroker); return status; } cairo_status_t _cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_polygon_t *polygon) { cairo_stroker_t stroker; cairo_status_t status; status = _cairo_stroker_init (&stroker, path, stroke_style, ctm, ctm_inverse, tolerance, polygon->limits, polygon->num_limits); if (unlikely (status)) return status; stroker.add_external_edge = _cairo_polygon_add_external_edge, stroker.closure = polygon; status = _cairo_path_fixed_interpret (path, _cairo_stroker_move_to, stroker.dash.dashed ? _cairo_stroker_line_to_dashed : _cairo_stroker_line_to, _cairo_stroker_curve_to, _cairo_stroker_close_path, &stroker); if (unlikely (status)) goto BAIL; /* Cap the start and end of the final sub path as needed */ status = _cairo_stroker_add_caps (&stroker); BAIL: _cairo_stroker_fini (&stroker); return status; } cairo_int_status_t _cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps) { cairo_int_status_t status; cairo_polygon_t polygon; _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &polygon); if (unlikely (status)) goto BAIL; status = _cairo_polygon_status (&polygon); if (unlikely (status)) goto BAIL; status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, CAIRO_FILL_RULE_WINDING); BAIL: _cairo_polygon_fini (&polygon); return status; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-path.c������������������������������������0000664�0000000�0000000�00000027347�12710376503�0024510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2006 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #include "cairoint.h" #include "cairo-private.h" #include "cairo-backend-private.h" #include "cairo-error-private.h" #include "cairo-path-private.h" #include "cairo-path-fixed-private.h" /** * SECTION:cairo-paths * @Title: Paths * @Short_Description: Creating paths and manipulating path data * * Paths are the most basic drawing tools and are primarily used to implicitly * generate simple masks. **/ static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; /* Closure for path interpretation. */ typedef struct cairo_path_count { int count; } cpc_t; static cairo_status_t _cpc_move_to (void *closure, const cairo_point_t *point) { cpc_t *cpc = closure; cpc->count += 2; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpc_line_to (void *closure, const cairo_point_t *point) { cpc_t *cpc = closure; cpc->count += 2; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpc_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { cpc_t *cpc = closure; cpc->count += 4; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpc_close_path (void *closure) { cpc_t *cpc = closure; cpc->count += 1; return CAIRO_STATUS_SUCCESS; } static int _cairo_path_count (cairo_path_t *path, cairo_path_fixed_t *path_fixed, double tolerance, cairo_bool_t flatten) { cairo_status_t status; cpc_t cpc; cpc.count = 0; if (flatten) { status = _cairo_path_fixed_interpret_flat (path_fixed, _cpc_move_to, _cpc_line_to, _cpc_close_path, &cpc, tolerance); } else { status = _cairo_path_fixed_interpret (path_fixed, _cpc_move_to, _cpc_line_to, _cpc_curve_to, _cpc_close_path, &cpc); } if (unlikely (status)) return -1; return cpc.count; } /* Closure for path interpretation. */ typedef struct cairo_path_populate { cairo_path_data_t *data; cairo_t *cr; } cpp_t; static cairo_status_t _cpp_move_to (void *closure, const cairo_point_t *point) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; double x, y; x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); _cairo_backend_to_user (cpp->cr, &x, &y); data->header.type = CAIRO_PATH_MOVE_TO; data->header.length = 2; /* We index from 1 to leave room for data->header */ data[1].point.x = x; data[1].point.y = y; cpp->data += data->header.length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpp_line_to (void *closure, const cairo_point_t *point) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; double x, y; x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); _cairo_backend_to_user (cpp->cr, &x, &y); data->header.type = CAIRO_PATH_LINE_TO; data->header.length = 2; /* We index from 1 to leave room for data->header */ data[1].point.x = x; data[1].point.y = y; cpp->data += data->header.length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpp_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; double x1, y1; double x2, y2; double x3, y3; x1 = _cairo_fixed_to_double (p1->x); y1 = _cairo_fixed_to_double (p1->y); _cairo_backend_to_user (cpp->cr, &x1, &y1); x2 = _cairo_fixed_to_double (p2->x); y2 = _cairo_fixed_to_double (p2->y); _cairo_backend_to_user (cpp->cr, &x2, &y2); x3 = _cairo_fixed_to_double (p3->x); y3 = _cairo_fixed_to_double (p3->y); _cairo_backend_to_user (cpp->cr, &x3, &y3); data->header.type = CAIRO_PATH_CURVE_TO; data->header.length = 4; /* We index from 1 to leave room for data->header */ data[1].point.x = x1; data[1].point.y = y1; data[2].point.x = x2; data[2].point.y = y2; data[3].point.x = x3; data[3].point.y = y3; cpp->data += data->header.length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cpp_close_path (void *closure) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; data->header.type = CAIRO_PATH_CLOSE_PATH; data->header.length = 1; cpp->data += data->header.length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_populate (cairo_path_t *path, cairo_path_fixed_t *path_fixed, cairo_t *cr, cairo_bool_t flatten) { cairo_status_t status; cpp_t cpp; cpp.data = path->data; cpp.cr = cr; if (flatten) { status = _cairo_path_fixed_interpret_flat (path_fixed, _cpp_move_to, _cpp_line_to, _cpp_close_path, &cpp, cairo_get_tolerance (cr)); } else { status = _cairo_path_fixed_interpret (path_fixed, _cpp_move_to, _cpp_line_to, _cpp_curve_to, _cpp_close_path, &cpp); } if (unlikely (status)) return status; /* Sanity check the count */ assert (cpp.data - path->data == path->num_data); return CAIRO_STATUS_SUCCESS; } cairo_path_t * _cairo_path_create_in_error (cairo_status_t status) { cairo_path_t *path; /* special case NO_MEMORY so as to avoid allocations */ if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_path_t*) &_cairo_path_nil; path = malloc (sizeof (cairo_path_t)); if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } path->num_data = 0; path->data = NULL; path->status = status; return path; } static cairo_path_t * _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, cairo_t *cr, cairo_bool_t flatten) { cairo_path_t *path; path = malloc (sizeof (cairo_path_t)); if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } path->num_data = _cairo_path_count (path, path_fixed, cairo_get_tolerance (cr), flatten); if (path->num_data < 0) { free (path); return (cairo_path_t*) &_cairo_path_nil; } if (path->num_data) { path->data = _cairo_malloc_ab (path->num_data, sizeof (cairo_path_data_t)); if (unlikely (path->data == NULL)) { free (path); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } path->status = _cairo_path_populate (path, path_fixed, cr, flatten); } else { path->data = NULL; path->status = CAIRO_STATUS_SUCCESS; } return path; } /** * cairo_path_destroy: * @path: a path previously returned by either cairo_copy_path() or * cairo_copy_path_flat(). * * Immediately releases all memory associated with @path. After a call * to cairo_path_destroy() the @path pointer is no longer valid and * should not be used further. * * Note: cairo_path_destroy() should only be called with a * pointer to a #cairo_path_t returned by a cairo function. Any path * that is created manually (ie. outside of cairo) should be destroyed * manually as well. * * Since: 1.0 **/ void cairo_path_destroy (cairo_path_t *path) { if (path == NULL || path == &_cairo_path_nil) return; free (path->data); free (path); } slim_hidden_def (cairo_path_destroy); /** * _cairo_path_create: * @path: a fixed-point, device-space path to be converted and copied * @cr: the current graphics context * * Creates a user-space #cairo_path_t copy of the given device-space * @path. The @cr parameter provides the inverse CTM for the * conversion. * * Return value: the new copy of the path. If there is insufficient * memory a pointer to a special static nil #cairo_path_t will be * returned instead with status==%CAIRO_STATUS_NO_MEMORY and * data==%NULL. **/ cairo_path_t * _cairo_path_create (cairo_path_fixed_t *path, cairo_t *cr) { return _cairo_path_create_internal (path, cr, FALSE); } /** * _cairo_path_create_flat: * @path: a fixed-point, device-space path to be flattened, converted and copied * @cr: the current graphics context * * Creates a flattened, user-space #cairo_path_t copy of the given * device-space @path. The @cr parameter provide the inverse CTM * for the conversion, as well as the tolerance value to control the * accuracy of the flattening. * * Return value: the flattened copy of the path. If there is insufficient * memory a pointer to a special static nil #cairo_path_t will be * returned instead with status==%CAIRO_STATUS_NO_MEMORY and * data==%NULL. **/ cairo_path_t * _cairo_path_create_flat (cairo_path_fixed_t *path, cairo_t *cr) { return _cairo_path_create_internal (path, cr, TRUE); } /** * _cairo_path_append_to_context: * @path: the path data to be appended * @cr: a cairo context * * Append @path to the current path within @cr. * * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path * is invalid, and %CAIRO_STATUS_SUCCESS otherwise. **/ cairo_status_t _cairo_path_append_to_context (const cairo_path_t *path, cairo_t *cr) { const cairo_path_data_t *p, *end; end = &path->data[path->num_data]; for (p = &path->data[0]; p < end; p += p->header.length) { switch (p->header.type) { case CAIRO_PATH_MOVE_TO: if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); cairo_move_to (cr, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_LINE_TO: if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); cairo_line_to (cr, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_CURVE_TO: if (unlikely (p->header.length < 4)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); cairo_curve_to (cr, p[1].point.x, p[1].point.y, p[2].point.x, p[2].point.y, p[3].point.x, p[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: if (unlikely (p->header.length < 1)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); cairo_close_path (cr); break; default: return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); } if (unlikely (cr->status)) return cr->status; } return CAIRO_STATUS_SUCCESS; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pattern-inline.h��������������������������0000664�0000000�0000000�00000004250�12710376503�0026476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_PATTERN_INLINE_H #define CAIRO_PATTERN_INLINE_H #include "cairo-pattern-private.h" #include "cairo-list-inline.h" CAIRO_BEGIN_DECLS static inline void _cairo_pattern_add_observer (cairo_pattern_t *pattern, cairo_pattern_observer_t *observer, void (*func) (cairo_pattern_observer_t *, cairo_pattern_t *, unsigned int)) { observer->notify = func; cairo_list_add (&observer->link, &pattern->observers); } static inline cairo_surface_t * _cairo_pattern_get_source (const cairo_surface_pattern_t *pattern, cairo_rectangle_int_t *extents) { return _cairo_surface_get_source (pattern->surface, extents); } CAIRO_END_DECLS #endif /* CAIRO_PATTERN_INLINE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pattern-private.h�������������������������0000664�0000000�0000000�00000025330�12710376503�0026674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_PATTERN_PRIVATE_H #define CAIRO_PATTERN_PRIVATE_H #include "cairo-error-private.h" #include "cairo-types-private.h" #include "cairo-list-private.h" #include "cairo-surface-private.h" #include <stdio.h> /* FILE* */ CAIRO_BEGIN_DECLS typedef struct _cairo_pattern_observer cairo_pattern_observer_t; enum { CAIRO_PATTERN_NOTIFY_MATRIX = 0x1, CAIRO_PATTERN_NOTIFY_FILTER = 0x2, CAIRO_PATTERN_NOTIFY_EXTEND = 0x4, CAIRO_PATTERN_NOTIFY_OPACITY = 0x9, }; struct _cairo_pattern_observer { void (*notify) (cairo_pattern_observer_t *, cairo_pattern_t *pattern, unsigned int flags); cairo_list_t link; }; struct _cairo_pattern { cairo_reference_count_t ref_count; cairo_status_t status; cairo_user_data_array_t user_data; cairo_list_t observers; cairo_pattern_type_t type; cairo_filter_t filter; cairo_extend_t extend; cairo_bool_t has_component_alpha; cairo_matrix_t matrix; double opacity; }; struct _cairo_solid_pattern { cairo_pattern_t base; cairo_color_t color; }; typedef struct _cairo_surface_pattern { cairo_pattern_t base; cairo_surface_t *surface; } cairo_surface_pattern_t; typedef struct _cairo_gradient_stop { double offset; cairo_color_stop_t color; } cairo_gradient_stop_t; typedef struct _cairo_gradient_pattern { cairo_pattern_t base; unsigned int n_stops; unsigned int stops_size; cairo_gradient_stop_t *stops; cairo_gradient_stop_t stops_embedded[2]; } cairo_gradient_pattern_t; typedef struct _cairo_linear_pattern { cairo_gradient_pattern_t base; cairo_point_double_t pd1; cairo_point_double_t pd2; } cairo_linear_pattern_t; typedef struct _cairo_radial_pattern { cairo_gradient_pattern_t base; cairo_circle_double_t cd1; cairo_circle_double_t cd2; } cairo_radial_pattern_t; typedef union { cairo_gradient_pattern_t base; cairo_linear_pattern_t linear; cairo_radial_pattern_t radial; } cairo_gradient_pattern_union_t; /* * A mesh patch is a tensor-product patch (bicubic Bezier surface * patch). It has 16 control points. Each set of 4 points along the * sides of the 4x4 grid of control points is a Bezier curve that * defines one side of the patch. A color is assigned to each * corner. The inner 4 points provide additional control over the * shape and the color mapping. * * Cairo uses the same convention as the PDF Reference for numbering * the points and side of the patch. * * * Side 1 * * p[0][3] p[1][3] p[2][3] p[3][3] * Side 0 p[0][2] p[1][2] p[2][2] p[3][2] Side 2 * p[0][1] p[1][1] p[2][1] p[3][1] * p[0][0] p[1][0] p[2][0] p[3][0] * * Side 3 * * * Point Color * ------------------------- * points[0][0] colors[0] * points[0][3] colors[1] * points[3][3] colors[2] * points[3][0] colors[3] */ typedef struct _cairo_mesh_patch { cairo_point_double_t points[4][4]; cairo_color_t colors[4]; } cairo_mesh_patch_t; typedef struct _cairo_mesh_pattern { cairo_pattern_t base; cairo_array_t patches; cairo_mesh_patch_t *current_patch; int current_side; cairo_bool_t has_control_point[4]; cairo_bool_t has_color[4]; } cairo_mesh_pattern_t; typedef struct _cairo_raster_source_pattern { cairo_pattern_t base; cairo_content_t content; cairo_rectangle_int_t extents; cairo_raster_source_acquire_func_t acquire; cairo_raster_source_release_func_t release; cairo_raster_source_snapshot_func_t snapshot; cairo_raster_source_copy_func_t copy; cairo_raster_source_finish_func_t finish; /* an explicit pre-allocated member in preference to the general user-data */ void *user_data; } cairo_raster_source_pattern_t; typedef union { cairo_pattern_t base; cairo_solid_pattern_t solid; cairo_surface_pattern_t surface; cairo_gradient_pattern_union_t gradient; cairo_mesh_pattern_t mesh; cairo_raster_source_pattern_t raster_source; } cairo_pattern_union_t; /* cairo-pattern.c */ cairo_private cairo_pattern_t * _cairo_pattern_create_in_error (cairo_status_t status); cairo_private cairo_status_t _cairo_pattern_create_copy (cairo_pattern_t **pattern, const cairo_pattern_t *other); cairo_private void _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type); cairo_private cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other); cairo_private void _cairo_pattern_init_static_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other); cairo_private cairo_status_t _cairo_pattern_init_snapshot (cairo_pattern_t *pattern, const cairo_pattern_t *other); cairo_private void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, const cairo_color_t *color); cairo_private void _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, cairo_surface_t *surface); cairo_private void _cairo_pattern_fini (cairo_pattern_t *pattern); cairo_private cairo_pattern_t * _cairo_pattern_create_solid (const cairo_color_t *color); cairo_private void _cairo_pattern_transform (cairo_pattern_t *pattern, const cairo_matrix_t *ctm_inverse); cairo_private cairo_bool_t _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); cairo_private cairo_bool_t _cairo_pattern_is_opaque (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents); cairo_private cairo_bool_t _cairo_pattern_is_clear (const cairo_pattern_t *pattern); cairo_private cairo_bool_t _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents, cairo_color_t *color); cairo_private void _cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient, double max_value, cairo_matrix_t *out_matrix, cairo_circle_double_t out_circle[2]); cairo_private cairo_bool_t _cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial); cairo_private void _cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, double x0, double y0, double x1, double y1, double tolerance, double out_range[2]); cairo_private void _cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, double t, cairo_circle_double_t *out_circle); cairo_private void _cairo_pattern_alpha_range (const cairo_pattern_t *pattern, double *out_min, double *out_max); cairo_private cairo_bool_t _cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, double *out_xmin, double *out_ymin, double *out_xmax, double *out_ymax); cairo_private_no_warn cairo_filter_t _cairo_pattern_sampled_area (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_rectangle_int_t *sample); cairo_private void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents); cairo_private cairo_int_status_t _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents); cairo_private unsigned long _cairo_pattern_hash (const cairo_pattern_t *pattern); cairo_private unsigned long _cairo_linear_pattern_hash (unsigned long hash, const cairo_linear_pattern_t *linear); cairo_private unsigned long _cairo_radial_pattern_hash (unsigned long hash, const cairo_radial_pattern_t *radial); cairo_private cairo_bool_t _cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, const cairo_linear_pattern_t *b); cairo_private unsigned long _cairo_pattern_size (const cairo_pattern_t *pattern); cairo_private cairo_bool_t _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, const cairo_radial_pattern_t *b); cairo_private cairo_bool_t _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b); /* cairo-mesh-pattern-rasterizer.c */ cairo_private void _cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh, void *data, int width, int height, int stride, double x_offset, double y_offset); cairo_private cairo_surface_t * _cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern, cairo_surface_t *target, const cairo_rectangle_int_t *extents); cairo_private void _cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern, cairo_surface_t *surface); cairo_private cairo_status_t _cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern); cairo_private cairo_status_t _cairo_raster_source_pattern_init_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other); cairo_private void _cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern); cairo_private void _cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern); CAIRO_END_DECLS #endif /* CAIRO_PATTERN_PRIVATE */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pattern.c���������������������������������0000664�0000000�0000000�00000420220�12710376503�0025214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 David Reveman * Copyright © 2005 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of David * Reveman not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. David Reveman makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: David Reveman <davidr@novell.com> * Keith Packard <keithp@keithp.com> * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-snapshot-inline.h" #include <float.h> #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ /** * SECTION:cairo-pattern * @Title: cairo_pattern_t * @Short_Description: Sources for drawing * @See_Also: #cairo_t, #cairo_surface_t * * #cairo_pattern_t is the paint with which cairo draws. * The primary use of patterns is as the source for all cairo drawing * operations, although they can also be used as masks, that is, as the * brush too. * * A cairo pattern is created by using one of the many constructors, * of the form * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function> * or implicitly through * <function>cairo_set_source_<emphasis>type</emphasis>()</function> * functions. **/ static freed_pool_t freed_pattern_pool[5]; static const cairo_solid_pattern_t _cairo_pattern_nil = { { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ { 0, 0, 0, NULL }, /* user_data */ { NULL, NULL }, /* observers */ CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ } }; static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NULL_POINTER, /* status */ { 0, 0, 0, NULL }, /* user_data */ { NULL, NULL }, /* observers */ CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ } }; const cairo_solid_pattern_t _cairo_pattern_black = { { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ { NULL, NULL }, /* observers */ CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ }; const cairo_solid_pattern_t _cairo_pattern_clear = { { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ { NULL, NULL }, /* observers */ CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */ }; const cairo_solid_pattern_t _cairo_pattern_white = { { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ { NULL, NULL }, /* observers */ CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_NEAREST, /* filter */ CAIRO_EXTEND_REPEAT, /* extend */ FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ 1.0 /* opacity */ }, { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */ }; static void _cairo_pattern_notify_observers (cairo_pattern_t *pattern, unsigned int flags) { cairo_pattern_observer_t *pos; cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link) pos->notify (pos, pattern, flags); } /** * _cairo_pattern_set_error: * @pattern: a pattern * @status: a status value indicating an error * * Atomically sets pattern->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS. * * All assignments of an error status to pattern->status should happen * through _cairo_pattern_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the nil * objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. **/ static cairo_status_t _cairo_pattern_set_error (cairo_pattern_t *pattern, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS) return status; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (&pattern->status, status); return _cairo_error (status); } void _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) { #if HAVE_VALGRIND switch (type) { case CAIRO_PATTERN_TYPE_SOLID: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); break; case CAIRO_PATTERN_TYPE_MESH: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: break; } #endif pattern->type = type; pattern->status = CAIRO_STATUS_SUCCESS; /* Set the reference count to zero for on-stack patterns. * Callers needs to explicitly increment the count for heap allocations. */ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); if (type == CAIRO_PATTERN_TYPE_SURFACE || type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT; else pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT; pattern->filter = CAIRO_FILTER_DEFAULT; pattern->opacity = 1.0; pattern->has_component_alpha = FALSE; cairo_matrix_init_identity (&pattern->matrix); cairo_list_init (&pattern->observers); } static cairo_status_t _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, const cairo_gradient_pattern_t *other) { if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; *dst = *src; } else { cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; *dst = *src; } if (other->stops == other->stops_embedded) pattern->stops = pattern->stops_embedded; else if (other->stops) { pattern->stops = _cairo_malloc_ab (other->stops_size, sizeof (cairo_gradient_stop_t)); if (unlikely (pattern->stops == NULL)) { pattern->stops_size = 0; pattern->n_stops = 0; return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); } memcpy (pattern->stops, other->stops, other->n_stops * sizeof (cairo_gradient_stop_t)); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern, const cairo_mesh_pattern_t *other) { *pattern = *other; _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); return _cairo_array_append_multiple (&pattern->patches, _cairo_array_index_const (&other->patches, 0), _cairo_array_num_elements (&other->patches)); } cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other) { cairo_status_t status; if (other->status) return _cairo_pattern_set_error (pattern, other->status); switch (other->type) { case CAIRO_PATTERN_TYPE_SOLID: { cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); *dst = *src; } break; case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); *dst = *src; cairo_surface_reference (dst->surface); } break; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); } else { VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); } status = _cairo_gradient_pattern_init_copy (dst, src); if (unlikely (status)) return status; } break; case CAIRO_PATTERN_TYPE_MESH: { cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern; cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other; VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t))); status = _cairo_mesh_pattern_init_copy (dst, src); if (unlikely (status)) return status; } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { status = _cairo_raster_source_pattern_init_copy (pattern, other); if (unlikely (status)) return status; } break; } /* The reference count and user_data array are unique to the copy. */ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); return CAIRO_STATUS_SUCCESS; } void _cairo_pattern_init_static_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other) { int size; assert (other->status == CAIRO_STATUS_SUCCESS); switch (other->type) { default: ASSERT_NOT_REACHED; case CAIRO_PATTERN_TYPE_SOLID: size = sizeof (cairo_solid_pattern_t); break; case CAIRO_PATTERN_TYPE_SURFACE: size = sizeof (cairo_surface_pattern_t); break; case CAIRO_PATTERN_TYPE_LINEAR: size = sizeof (cairo_linear_pattern_t); break; case CAIRO_PATTERN_TYPE_RADIAL: size = sizeof (cairo_radial_pattern_t); break; case CAIRO_PATTERN_TYPE_MESH: size = sizeof (cairo_mesh_pattern_t); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: size = sizeof (cairo_raster_source_pattern_t); break; } memcpy (pattern, other, size); CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); } cairo_status_t _cairo_pattern_init_snapshot (cairo_pattern_t *pattern, const cairo_pattern_t *other) { cairo_status_t status; /* We don't bother doing any fancy copy-on-write implementation * for the pattern's data. It's generally quite tiny. */ status = _cairo_pattern_init_copy (pattern, other); if (unlikely (status)) return status; /* But we do let the surface snapshot stuff be as fancy as it * would like to be. */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; surface_pattern->surface = _cairo_surface_snapshot (surface); cairo_surface_destroy (surface); status = surface_pattern->surface->status; } else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) status = _cairo_raster_source_pattern_snapshot (pattern); return status; } void _cairo_pattern_fini (cairo_pattern_t *pattern) { _cairo_user_data_array_fini (&pattern->user_data); switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: break; case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; cairo_surface_destroy (surface_pattern->surface); } break; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; if (gradient->stops && gradient->stops != gradient->stops_embedded) free (gradient->stops); } break; case CAIRO_PATTERN_TYPE_MESH: { cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; _cairo_array_fini (&mesh->patches); } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: _cairo_raster_source_pattern_finish (pattern); break; } #if HAVE_VALGRIND switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); break; case CAIRO_PATTERN_TYPE_MESH: VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t)); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: break; } #endif } cairo_status_t _cairo_pattern_create_copy (cairo_pattern_t **pattern_out, const cairo_pattern_t *other) { cairo_pattern_t *pattern; cairo_status_t status; if (other->status) return other->status; switch (other->type) { case CAIRO_PATTERN_TYPE_SOLID: pattern = malloc (sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: pattern = malloc (sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: pattern = malloc (sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: pattern = malloc (sizeof (cairo_radial_pattern_t)); break; case CAIRO_PATTERN_TYPE_MESH: pattern = malloc (sizeof (cairo_mesh_pattern_t)); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: pattern = malloc (sizeof (cairo_raster_source_pattern_t)); break; default: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); } if (unlikely (pattern == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_pattern_init_copy (pattern, other); if (unlikely (status)) { free (pattern); return status; } CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); *pattern_out = pattern; return CAIRO_STATUS_SUCCESS; } void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, const cairo_color_t *color) { _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); pattern->color = *color; } void _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, cairo_surface_t *surface) { if (surface->status) { /* Force to solid to simplify the pattern_fini process. */ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); _cairo_pattern_set_error (&pattern->base, surface->status); return; } _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); pattern->surface = cairo_surface_reference (surface); } static void _cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, cairo_pattern_type_t type) { _cairo_pattern_init (&pattern->base, type); pattern->n_stops = 0; pattern->stops_size = 0; pattern->stops = NULL; } static void _cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, double x0, double y0, double x1, double y1) { _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR); pattern->pd1.x = x0; pattern->pd1.y = y0; pattern->pd2.x = x1; pattern->pd2.y = y1; } static void _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) { _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL); pattern->cd1.center.x = cx0; pattern->cd1.center.y = cy0; pattern->cd1.radius = fabs (radius0); pattern->cd2.center.x = cx1; pattern->cd2.center.y = cy1; pattern->cd2.radius = fabs (radius1); } cairo_pattern_t * _cairo_pattern_create_solid (const cairo_color_t *color) { cairo_solid_pattern_t *pattern; pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); if (unlikely (pattern == NULL)) { /* None cached, need to create a new pattern. */ pattern = malloc (sizeof (cairo_solid_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil; } } _cairo_pattern_init_solid (pattern, color); CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); return &pattern->base; } cairo_pattern_t * _cairo_pattern_create_in_error (cairo_status_t status) { cairo_pattern_t *pattern; if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_pattern_t *)&_cairo_pattern_nil.base; CAIRO_MUTEX_INITIALIZE (); pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); if (pattern->status == CAIRO_STATUS_SUCCESS) status = _cairo_pattern_set_error (pattern, status); return pattern; } /** * cairo_pattern_create_rgb: * @red: red component of the color * @green: green component of the color * @blue: blue component of the color * * Creates a new #cairo_pattern_t corresponding to an opaque color. The * color components are floating point numbers in the range 0 to 1. * If the values passed in are outside that range, they will be * clamped. * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_rgb (double red, double green, double blue) { return cairo_pattern_create_rgba (red, green, blue, 1.0); } slim_hidden_def (cairo_pattern_create_rgb); /** * cairo_pattern_create_rgba: * @red: red component of the color * @green: green component of the color * @blue: blue component of the color * @alpha: alpha component of the color * * Creates a new #cairo_pattern_t corresponding to a translucent color. * The color components are floating point numbers in the range 0 to * 1. If the values passed in are outside that range, they will be * clamped. * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_rgba (double red, double green, double blue, double alpha) { cairo_color_t color; red = _cairo_restrict_value (red, 0.0, 1.0); green = _cairo_restrict_value (green, 0.0, 1.0); blue = _cairo_restrict_value (blue, 0.0, 1.0); alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_color_init_rgba (&color, red, green, blue, alpha); CAIRO_MUTEX_INITIALIZE (); return _cairo_pattern_create_solid (&color); } slim_hidden_def (cairo_pattern_create_rgba); /** * cairo_pattern_create_for_surface: * @surface: the surface * * Create a new #cairo_pattern_t for the given surface. * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface) { cairo_surface_pattern_t *pattern; if (surface == NULL) { _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer; } if (surface->status) return _cairo_pattern_create_in_error (surface->status); pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); if (unlikely (pattern == NULL)) { pattern = malloc (sizeof (cairo_surface_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *)&_cairo_pattern_nil.base; } } CAIRO_MUTEX_INITIALIZE (); _cairo_pattern_init_for_surface (pattern, surface); CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); return &pattern->base; } slim_hidden_def (cairo_pattern_create_for_surface); /** * cairo_pattern_create_linear: * @x0: x coordinate of the start point * @y0: y coordinate of the start point * @x1: x coordinate of the end point * @y1: y coordinate of the end point * * Create a new linear gradient #cairo_pattern_t along the line defined * by (x0, y0) and (x1, y1). Before using the gradient pattern, a * number of color stops should be defined using * cairo_pattern_add_color_stop_rgb() or * cairo_pattern_add_color_stop_rgba(). * * Note: The coordinates here are in pattern space. For a new pattern, * pattern space is identical to user space, but the relationship * between the spaces can be changed with cairo_pattern_set_matrix(). * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1) { cairo_linear_pattern_t *pattern; pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); if (unlikely (pattern == NULL)) { pattern = malloc (sizeof (cairo_linear_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil.base; } } CAIRO_MUTEX_INITIALIZE (); _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); return &pattern->base.base; } /** * cairo_pattern_create_radial: * @cx0: x coordinate for the center of the start circle * @cy0: y coordinate for the center of the start circle * @radius0: radius of the start circle * @cx1: x coordinate for the center of the end circle * @cy1: y coordinate for the center of the end circle * @radius1: radius of the end circle * * Creates a new radial gradient #cairo_pattern_t between the two * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the * gradient pattern, a number of color stops should be defined using * cairo_pattern_add_color_stop_rgb() or * cairo_pattern_add_color_stop_rgba(). * * Note: The coordinates here are in pattern space. For a new pattern, * pattern space is identical to user space, but the relationship * between the spaces can be changed with cairo_pattern_set_matrix(). * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) { cairo_radial_pattern_t *pattern; pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); if (unlikely (pattern == NULL)) { pattern = malloc (sizeof (cairo_radial_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil.base; } } CAIRO_MUTEX_INITIALIZE (); _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); return &pattern->base.base; } /* This order is specified in the diagram in the documentation for * cairo_pattern_create_mesh() */ static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 }; static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 }; static const int mesh_control_point_i[4] = { 1, 1, 2, 2 }; static const int mesh_control_point_j[4] = { 1, 2, 2, 1 }; /** * cairo_pattern_create_mesh: * * Create a new mesh pattern. * * Mesh patterns are tensor-product patch meshes (type 7 shadings in * PDF). Mesh patterns may also be used to create other types of * shadings that are special cases of tensor-product patch meshes such * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded * triangle meshes (type 4 and 5 shadings in PDF). * * Mesh patterns consist of one or more tensor-product patches, which * should be defined before using the mesh pattern. Using a mesh * pattern with a partially defined patch as source or mask will put * the context in an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2, * 3) and by 4 additional control points (P0, P1, P2, P3) that provide * further control over the patch and complete the definition of the * tensor-product patch. The corner C0 is the first point of the * patch. * * Degenerate sides are permitted so straight lines may be used. A * zero length line on one side may be used to create 3 sided patches. * * <informalexample><screen> * C1 Side 1 C2 * +---------------+ * | | * | P1 P2 | * | | * Side 0 | | Side 2 * | | * | | * | P0 P3 | * | | * +---------------+ * C0 Side 3 C3 * </screen></informalexample> * * Each patch is constructed by first calling * cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to() * to specify the first point in the patch (C0). Then the sides are * specified with calls to cairo_mesh_pattern_curve_to() and * cairo_mesh_pattern_line_to(). * * The four additional control points (P0, P1, P2, P3) in a patch can * be specified with cairo_mesh_pattern_set_control_point(). * * At each corner of the patch (C0, C1, C2, C3) a color may be * specified with cairo_mesh_pattern_set_corner_color_rgb() or * cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color * is not explicitly specified defaults to transparent black. * * A Coons patch is a special case of the tensor-product patch where * the control points are implicitly defined by the sides of the * patch. The default value for any control point not specified is the * implicit value for a Coons patch, i.e. if no control points are * specified the patch is a Coons patch. * * A triangle is a special case of the tensor-product patch where the * control points are implicitly defined by the sides of the patch, * all the sides are lines and one of them has length 0, i.e. if the * patch is specified using just 3 lines, it is a triangle. If the * corners connected by the 0-length side have the same color, the * patch is a Gouraud-shaded triangle. * * Patches may be oriented differently to the above diagram. For * example the first point could be at the top left. The diagram only * shows the relationship between the sides, corners and control * points. Regardless of where the first point is located, when * specifying colors, corner 0 will always be the first point, corner * 1 the point between side 0 and side 1 etc. * * Calling cairo_mesh_pattern_end_patch() completes the current * patch. If less than 4 sides have been defined, the first missing * side is defined as a line from the current point to the first point * of the patch (C0) and the other sides are degenerate lines from C0 * to C0. The corners between the added sides will all be coincident * with C0 of the patch and their color will be set to be the same as * the color of C0. * * Additional patches may be added with additional calls to * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch(). * * <informalexample><programlisting> * cairo_pattern_t *pattern = cairo_pattern_create_mesh (); * * /* Add a Coons patch */ * cairo_mesh_pattern_begin_patch (pattern); * cairo_mesh_pattern_move_to (pattern, 0, 0); * cairo_mesh_pattern_curve_to (pattern, 30, -30, 60, 30, 100, 0); * cairo_mesh_pattern_curve_to (pattern, 60, 30, 130, 60, 100, 100); * cairo_mesh_pattern_curve_to (pattern, 60, 70, 30, 130, 0, 100); * cairo_mesh_pattern_curve_to (pattern, 30, 70, -30, 30, 0, 0); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0); * cairo_mesh_pattern_end_patch (pattern); * * /* Add a Gouraud-shaded triangle */ * cairo_mesh_pattern_begin_patch (pattern) * cairo_mesh_pattern_move_to (pattern, 100, 100); * cairo_mesh_pattern_line_to (pattern, 130, 130); * cairo_mesh_pattern_line_to (pattern, 130, 70); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); * cairo_mesh_pattern_end_patch (pattern) * </programlisting></informalexample> * * When two patches overlap, the last one that has been added is drawn * over the first one. * * When a patch folds over itself, points are sorted depending on * their parameter coordinates inside the patch. The v coordinate * ranges from 0 to 1 when moving from side 3 to side 1; the u * coordinate ranges from 0 to 1 when going from side 0 to side * 2. Points with higher v coordinate hide points with lower v * coordinate. When two points have the same v coordinate, the one * with higher u coordinate is above. This means that points nearer to * side 1 are above points nearer to side 3; when this is not * sufficient to decide which point is above (for example when both * points belong to side 1 or side 3) points nearer to side 2 are * above points nearer to side 0. * * For a complete definition of tensor-product patches, see the PDF * specification (ISO32000), which describes the parametrization in * detail. * * Note: The coordinates are always in pattern space. For a new * pattern, pattern space is identical to user space, but the * relationship between the spaces can be changed with * cairo_pattern_set_matrix(). * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the returned * object and should call cairo_pattern_destroy() when finished with * it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect the * status of a pattern use cairo_pattern_status(). * * Since: 1.12 **/ cairo_pattern_t * cairo_pattern_create_mesh (void) { cairo_mesh_pattern_t *pattern; pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]); if (unlikely (pattern == NULL)) { pattern = malloc (sizeof (cairo_mesh_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil.base; } } CAIRO_MUTEX_INITIALIZE (); _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH); _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); pattern->current_patch = NULL; CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); return &pattern->base; } /** * cairo_pattern_reference: * @pattern: a #cairo_pattern_t * * Increases the reference count on @pattern by one. This prevents * @pattern from being destroyed until a matching call to * cairo_pattern_destroy() is made. * * The number of references to a #cairo_pattern_t can be get using * cairo_pattern_get_reference_count(). * * Return value: the referenced #cairo_pattern_t. * * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_reference (cairo_pattern_t *pattern) { if (pattern == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return pattern; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); _cairo_reference_count_inc (&pattern->ref_count); return pattern; } slim_hidden_def (cairo_pattern_reference); /** * cairo_pattern_get_type: * @pattern: a #cairo_pattern_t * * This function returns the type a pattern. * See #cairo_pattern_type_t for available types. * * Return value: The type of @pattern. * * Since: 1.2 **/ cairo_pattern_type_t cairo_pattern_get_type (cairo_pattern_t *pattern) { return pattern->type; } /** * cairo_pattern_status: * @pattern: a #cairo_pattern_t * * Checks whether an error has previously occurred for this * pattern. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, * %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH, * or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.0 **/ cairo_status_t cairo_pattern_status (cairo_pattern_t *pattern) { return pattern->status; } /** * cairo_pattern_destroy: * @pattern: a #cairo_pattern_t * * Decreases the reference count on @pattern by one. If the result is * zero, then @pattern and all associated resources are freed. See * cairo_pattern_reference(). * * Since: 1.0 **/ void cairo_pattern_destroy (cairo_pattern_t *pattern) { cairo_pattern_type_t type; if (pattern == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) return; type = pattern->type; _cairo_pattern_fini (pattern); /* maintain a small cache of freed patterns */ if (type < ARRAY_LENGTH (freed_pattern_pool)) _freed_pool_put (&freed_pattern_pool[type], pattern); else free (pattern); } slim_hidden_def (cairo_pattern_destroy); /** * cairo_pattern_get_reference_count: * @pattern: a #cairo_pattern_t * * Returns the current reference count of @pattern. * * Return value: the current reference count of @pattern. If the * object is a nil object, 0 will be returned. * * Since: 1.4 **/ unsigned int cairo_pattern_get_reference_count (cairo_pattern_t *pattern) { if (pattern == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count); } /** * cairo_pattern_get_user_data: * @pattern: a #cairo_pattern_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @pattern using the * specified key. If no user data has been attached with the given * key this function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.4 **/ void * cairo_pattern_get_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key) { return _cairo_user_data_array_get_data (&pattern->user_data, key); } /** * cairo_pattern_set_user_data: * @pattern: a #cairo_pattern_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the #cairo_pattern_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @pattern. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.4 **/ cairo_status_t cairo_pattern_set_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return pattern->status; return _cairo_user_data_array_set_data (&pattern->user_data, key, user_data, destroy); } /** * cairo_mesh_pattern_begin_patch: * @pattern: a #cairo_pattern_t * * Begin a patch in a mesh pattern. * * After calling this function, the patch shape should be defined with * cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and * cairo_mesh_pattern_curve_to(). * * After defining the patch, cairo_mesh_pattern_end_patch() must be * called before using @pattern as a source or mask. * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a * current patch, it will be put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern) { cairo_mesh_pattern_t *mesh; cairo_status_t status; cairo_mesh_patch_t *current_patch; int i; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } status = _cairo_array_allocate (&mesh->patches, 1, (void **) ¤t_patch); if (unlikely (status)) { _cairo_pattern_set_error (pattern, status); return; } mesh->current_patch = current_patch; mesh->current_side = -2; /* no current point */ for (i = 0; i < 4; i++) mesh->has_control_point[i] = FALSE; for (i = 0; i < 4; i++) mesh->has_color[i] = FALSE; } static void _calc_control_point (cairo_mesh_patch_t *patch, int control_point) { /* The Coons patch is a special case of the Tensor Product patch * where the four control points are: * * P11 = S(1/3, 1/3) * P12 = S(1/3, 2/3) * P21 = S(2/3, 1/3) * P22 = S(2/3, 2/3) * * where S is the gradient surface. * * When one or more control points has not been specified * calculated the Coons patch control points are substituted. If * no control points are specified the gradient will be a Coons * patch. * * The equations below are defined in the ISO32000 standard. */ cairo_point_double_t *p[3][3]; int cp_i, cp_j, i, j; cp_i = mesh_control_point_i[control_point]; cp_j = mesh_control_point_j[control_point]; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j]; p[0][0]->x = (- 4 * p[1][1]->x + 6 * (p[1][0]->x + p[0][1]->x) - 2 * (p[1][2]->x + p[2][1]->x) + 3 * (p[2][0]->x + p[0][2]->x) - 1 * p[2][2]->x) * (1. / 9); p[0][0]->y = (- 4 * p[1][1]->y + 6 * (p[1][0]->y + p[0][1]->y) - 2 * (p[1][2]->y + p[2][1]->y) + 3 * (p[2][0]->y + p[0][2]->y) - 1 * p[2][2]->y) * (1. / 9); } /** * cairo_mesh_pattern_end_patch: * @pattern: a #cairo_pattern_t * * Indicates the end of the current patch in a mesh pattern. * * If the current patch has less than 4 sides, it is closed with a * straight line from the current point to the first point of the * patch as if cairo_mesh_pattern_line_to() was used. * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current * patch or the current patch has no current point, @pattern will be * put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern) { cairo_mesh_pattern_t *mesh; cairo_mesh_patch_t *current_patch; int i; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } mesh = (cairo_mesh_pattern_t *) pattern; current_patch = mesh->current_patch; if (unlikely (!current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (unlikely (mesh->current_side == -2)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } while (mesh->current_side < 3) { int corner_num; cairo_mesh_pattern_line_to (pattern, current_patch->points[0][0].x, current_patch->points[0][0].y); corner_num = mesh->current_side + 1; if (corner_num < 4 && ! mesh->has_color[corner_num]) { current_patch->colors[corner_num] = current_patch->colors[0]; mesh->has_color[corner_num] = TRUE; } } for (i = 0; i < 4; i++) { if (! mesh->has_control_point[i]) _calc_control_point (current_patch, i); } for (i = 0; i < 4; i++) { if (! mesh->has_color[i]) current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT; } mesh->current_patch = NULL; } /** * cairo_mesh_pattern_curve_to: * @pattern: a #cairo_pattern_t * @x1: the X coordinate of the first control point * @y1: the Y coordinate of the first control point * @x2: the X coordinate of the second control point * @y2: the Y coordinate of the second control point * @x3: the X coordinate of the end of the curve * @y3: the Y coordinate of the end of the curve * * Adds a cubic Bézier spline to the current patch from the current * point to position (@x3, @y3) in pattern-space coordinates, using * (@x1, @y1) and (@x2, @y2) as the control points. * * If the current patch has no current point before the call to * cairo_mesh_pattern_curve_to(), this function will behave as if * preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1, * @y1). * * After this call the current point will be (@x3, @y3). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current * patch or the current patch already has 4 sides, @pattern will be * put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, double x1, double y1, double x2, double y2, double x3, double y3) { cairo_mesh_pattern_t *mesh; int current_point, i, j; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (!mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (unlikely (mesh->current_side == 3)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (mesh->current_side == -2) cairo_mesh_pattern_move_to (pattern, x1, y1); assert (mesh->current_side >= -1); assert (pattern->status == CAIRO_STATUS_SUCCESS); mesh->current_side++; current_point = 3 * mesh->current_side; current_point++; i = mesh_path_point_i[current_point]; j = mesh_path_point_j[current_point]; mesh->current_patch->points[i][j].x = x1; mesh->current_patch->points[i][j].y = y1; current_point++; i = mesh_path_point_i[current_point]; j = mesh_path_point_j[current_point]; mesh->current_patch->points[i][j].x = x2; mesh->current_patch->points[i][j].y = y2; current_point++; if (current_point < 12) { i = mesh_path_point_i[current_point]; j = mesh_path_point_j[current_point]; mesh->current_patch->points[i][j].x = x3; mesh->current_patch->points[i][j].y = y3; } } slim_hidden_def (cairo_mesh_pattern_curve_to); /** * cairo_mesh_pattern_line_to: * @pattern: a #cairo_pattern_t * @x: the X coordinate of the end of the new line * @y: the Y coordinate of the end of the new line * * Adds a line to the current patch from the current point to position * (@x, @y) in pattern-space coordinates. * * If there is no current point before the call to * cairo_mesh_pattern_line_to() this function will behave as * cairo_mesh_pattern_move_to(@pattern, @x, @y). * * After this call the current point will be (@x, @y). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current * patch or the current patch already has 4 sides, @pattern will be * put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, double x, double y) { cairo_mesh_pattern_t *mesh; cairo_point_double_t last_point; int last_point_idx, i, j; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (!mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (unlikely (mesh->current_side == 3)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (mesh->current_side == -2) { cairo_mesh_pattern_move_to (pattern, x, y); return; } last_point_idx = 3 * (mesh->current_side + 1); i = mesh_path_point_i[last_point_idx]; j = mesh_path_point_j[last_point_idx]; last_point = mesh->current_patch->points[i][j]; cairo_mesh_pattern_curve_to (pattern, (2 * last_point.x + x) * (1. / 3), (2 * last_point.y + y) * (1. / 3), (last_point.x + 2 * x) * (1. / 3), (last_point.y + 2 * y) * (1. / 3), x, y); } slim_hidden_def (cairo_mesh_pattern_line_to); /** * cairo_mesh_pattern_move_to: * @pattern: a #cairo_pattern_t * @x: the X coordinate of the new position * @y: the Y coordinate of the new position * * Define the first point of the current patch in a mesh pattern. * * After this call the current point will be (@x, @y). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current * patch or the current patch already has at least one side, @pattern * will be put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_move_to (cairo_pattern_t *pattern, double x, double y) { cairo_mesh_pattern_t *mesh; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (!mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } if (unlikely (mesh->current_side >= 0)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } mesh->current_side = -1; mesh->current_patch->points[0][0].x = x; mesh->current_patch->points[0][0].y = y; } slim_hidden_def (cairo_mesh_pattern_move_to); /** * cairo_mesh_pattern_set_control_point: * @pattern: a #cairo_pattern_t * @point_num: the control point to set the position for * @x: the X coordinate of the control point * @y: the Y coordinate of the control point * * Set an internal control point of the current patch. * * Valid values for @point_num are from 0 to 3 and identify the * control points as explained in cairo_pattern_create_mesh(). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern, unsigned int point_num, double x, double y) { cairo_mesh_pattern_t *mesh; int i, j; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } if (unlikely (point_num > 3)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (!mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } i = mesh_control_point_i[point_num]; j = mesh_control_point_j[point_num]; mesh->current_patch->points[i][j].x = x; mesh->current_patch->points[i][j].y = y; mesh->has_control_point[point_num] = TRUE; } /* make room for at least one more color stop */ static cairo_status_t _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) { cairo_gradient_stop_t *new_stops; int old_size = pattern->stops_size; int embedded_size = ARRAY_LENGTH (pattern->stops_embedded); int new_size = 2 * MAX (old_size, 4); /* we have a local buffer at pattern->stops_embedded. try to fulfill the request * from there. */ if (old_size < embedded_size) { pattern->stops = pattern->stops_embedded; pattern->stops_size = embedded_size; return CAIRO_STATUS_SUCCESS; } if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); assert (pattern->n_stops <= pattern->stops_size); if (pattern->stops == pattern->stops_embedded) { new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t)); if (new_stops) memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t)); } else { new_stops = _cairo_realloc_ab (pattern->stops, new_size, sizeof (cairo_gradient_stop_t)); } if (unlikely (new_stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pattern->stops = new_stops; pattern->stops_size = new_size; return CAIRO_STATUS_SUCCESS; } static void _cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh, unsigned int corner_num, double red, double green, double blue, double alpha) { cairo_color_t *color; assert (mesh->current_patch); assert (corner_num <= 3); color = &mesh->current_patch->colors[corner_num]; color->red = red; color->green = green; color->blue = blue; color->alpha = alpha; color->red_short = _cairo_color_double_to_short (red); color->green_short = _cairo_color_double_to_short (green); color->blue_short = _cairo_color_double_to_short (blue); color->alpha_short = _cairo_color_double_to_short (alpha); mesh->has_color[corner_num] = TRUE; } /** * cairo_mesh_pattern_set_corner_color_rgb: * @pattern: a #cairo_pattern_t * @corner_num: the corner to set the color for * @red: red component of color * @green: green component of color * @blue: blue component of color * * Sets the color of a corner of the current patch in a mesh pattern. * * The color is specified in the same way as in cairo_set_source_rgb(). * * Valid values for @corner_num are from 0 to 3 and identify the * corners as explained in cairo_pattern_create_mesh(). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern, unsigned int corner_num, double red, double green, double blue) { cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0); } /** * cairo_mesh_pattern_set_corner_color_rgba: * @pattern: a #cairo_pattern_t * @corner_num: the corner to set the color for * @red: red component of color * @green: green component of color * @blue: blue component of color * @alpha: alpha component of color * * Sets the color of a corner of the current patch in a mesh pattern. * * The color is specified in the same way as in cairo_set_source_rgba(). * * Valid values for @corner_num are from 0 to 3 and identify the * corners as explained in cairo_pattern_create_mesh(). * * Note: If @pattern is not a mesh pattern then @pattern will be put * into an error status with a status of * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, * @pattern will be put into an error status with a status of * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. * * Since: 1.12 **/ void cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern, unsigned int corner_num, double red, double green, double blue, double alpha) { cairo_mesh_pattern_t *mesh; if (unlikely (pattern->status)) return; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } if (unlikely (corner_num > 3)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); return; } mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (!mesh->current_patch)) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); return; } red = _cairo_restrict_value (red, 0.0, 1.0); green = _cairo_restrict_value (green, 0.0, 1.0); blue = _cairo_restrict_value (blue, 0.0, 1.0); alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha); } slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba); static void _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, double offset, double red, double green, double blue, double alpha) { cairo_gradient_stop_t *stops; unsigned int i; if (pattern->n_stops >= pattern->stops_size) { cairo_status_t status = _cairo_pattern_gradient_grow (pattern); if (unlikely (status)) { status = _cairo_pattern_set_error (&pattern->base, status); return; } } stops = pattern->stops; for (i = 0; i < pattern->n_stops; i++) { if (offset < stops[i].offset) { memmove (&stops[i + 1], &stops[i], sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i)); break; } } stops[i].offset = offset; stops[i].color.red = red; stops[i].color.green = green; stops[i].color.blue = blue; stops[i].color.alpha = alpha; stops[i].color.red_short = _cairo_color_double_to_short (red); stops[i].color.green_short = _cairo_color_double_to_short (green); stops[i].color.blue_short = _cairo_color_double_to_short (blue); stops[i].color.alpha_short = _cairo_color_double_to_short (alpha); pattern->n_stops++; } /** * cairo_pattern_add_color_stop_rgb: * @pattern: a #cairo_pattern_t * @offset: an offset in the range [0.0 .. 1.0] * @red: red component of color * @green: green component of color * @blue: blue component of color * * Adds an opaque color stop to a gradient pattern. The offset * specifies the location along the gradient's control vector. For * example, a linear gradient's control vector is from (x0,y0) to * (x1,y1) while a radial gradient's control vector is from any point * on the start circle to the corresponding point on the end circle. * * The color is specified in the same way as in cairo_set_source_rgb(). * * If two (or more) stops are specified with identical offset values, * they will be sorted according to the order in which the stops are * added, (stops added earlier will compare less than stops added * later). This can be useful for reliably making sharp color * transitions instead of the typical blend. * * * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. * * Since: 1.0 **/ void cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, double red, double green, double blue) { cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0); } /** * cairo_pattern_add_color_stop_rgba: * @pattern: a #cairo_pattern_t * @offset: an offset in the range [0.0 .. 1.0] * @red: red component of color * @green: green component of color * @blue: blue component of color * @alpha: alpha component of color * * Adds a translucent color stop to a gradient pattern. The offset * specifies the location along the gradient's control vector. For * example, a linear gradient's control vector is from (x0,y0) to * (x1,y1) while a radial gradient's control vector is from any point * on the start circle to the corresponding point on the end circle. * * The color is specified in the same way as in cairo_set_source_rgba(). * * If two (or more) stops are specified with identical offset values, * they will be sorted according to the order in which the stops are * added, (stops added earlier will compare less than stops added * later). This can be useful for reliably making sharp color * transitions instead of the typical blend. * * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. * * Since: 1.0 **/ void cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, double red, double green, double blue, double alpha) { if (pattern->status) return; if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && pattern->type != CAIRO_PATTERN_TYPE_RADIAL) { _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); return; } offset = _cairo_restrict_value (offset, 0.0, 1.0); red = _cairo_restrict_value (red, 0.0, 1.0); green = _cairo_restrict_value (green, 0.0, 1.0); blue = _cairo_restrict_value (blue, 0.0, 1.0); alpha = _cairo_restrict_value (alpha, 0.0, 1.0); _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, offset, red, green, blue, alpha); } slim_hidden_def (cairo_pattern_add_color_stop_rgba); /** * cairo_pattern_set_matrix: * @pattern: a #cairo_pattern_t * @matrix: a #cairo_matrix_t * * Sets the pattern's transformation matrix to @matrix. This matrix is * a transformation from user space to pattern space. * * When a pattern is first created it always has the identity matrix * for its transformation matrix, which means that pattern space is * initially identical to user space. * * Important: Please note that the direction of this transformation * matrix is from user space to pattern space. This means that if you * imagine the flow from a pattern to user space (and on to device * space), then coordinates in that flow will be transformed by the * inverse of the pattern matrix. * * For example, if you want to make a pattern appear twice as large as * it does by default the correct code to use is: * * <informalexample><programlisting> * cairo_matrix_init_scale (&matrix, 0.5, 0.5); * cairo_pattern_set_matrix (pattern, &matrix); * </programlisting></informalexample> * * Meanwhile, using values of 2.0 rather than 0.5 in the code above * would cause the pattern to appear at half of its default size. * * Also, please note the discussion of the user-space locking * semantics of cairo_set_source(). * * Since: 1.0 **/ void cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix) { cairo_matrix_t inverse; cairo_status_t status; if (pattern->status) return; if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) return; pattern->matrix = *matrix; _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX); inverse = *matrix; status = cairo_matrix_invert (&inverse); if (unlikely (status)) status = _cairo_pattern_set_error (pattern, status); } slim_hidden_def (cairo_pattern_set_matrix); /** * cairo_pattern_get_matrix: * @pattern: a #cairo_pattern_t * @matrix: return value for the matrix * * Stores the pattern's transformation matrix into @matrix. * * Since: 1.0 **/ void cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { *matrix = pattern->matrix; } /** * cairo_pattern_set_filter: * @pattern: a #cairo_pattern_t * @filter: a #cairo_filter_t describing the filter to use for resizing * the pattern * * Sets the filter to be used for resizing when using this pattern. * See #cairo_filter_t for details on each filter. * * * Note that you might want to control filtering even when you do not * have an explicit #cairo_pattern_t object, (for example when using * cairo_set_source_surface()). In these cases, it is convenient to * use cairo_get_source() to get access to the pattern that cairo * creates implicitly. For example: * * <informalexample><programlisting> * cairo_set_source_surface (cr, image, x, y); * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); * </programlisting></informalexample> * * Since: 1.0 **/ void cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) { if (pattern->status) return; pattern->filter = filter; _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER); } /** * cairo_pattern_get_filter: * @pattern: a #cairo_pattern_t * * Gets the current filter for a pattern. See #cairo_filter_t * for details on each filter. * * Return value: the current filter used for resizing the pattern. * * Since: 1.0 **/ cairo_filter_t cairo_pattern_get_filter (cairo_pattern_t *pattern) { return pattern->filter; } /** * cairo_pattern_set_extend: * @pattern: a #cairo_pattern_t * @extend: a #cairo_extend_t describing how the area outside of the * pattern will be drawn * * Sets the mode to be used for drawing outside the area of a pattern. * See #cairo_extend_t for details on the semantics of each extend * strategy. * * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns * and %CAIRO_EXTEND_PAD for gradient patterns. * * Since: 1.0 **/ void cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) { if (pattern->status) return; pattern->extend = extend; _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND); } /** * cairo_pattern_get_extend: * @pattern: a #cairo_pattern_t * * Gets the current extend mode for a pattern. See #cairo_extend_t * for details on the semantics of each extend strategy. * * Return value: the current extend strategy used for drawing the * pattern. * * Since: 1.0 **/ cairo_extend_t cairo_pattern_get_extend (cairo_pattern_t *pattern) { return pattern->extend; } slim_hidden_def (cairo_pattern_get_extend); void _cairo_pattern_transform (cairo_pattern_t *pattern, const cairo_matrix_t *ctm_inverse) { if (pattern->status) return; cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); } static cairo_bool_t _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) { return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON && fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON; } static cairo_bool_t _radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial) { /* A radial pattern is considered degenerate if it can be * represented as a solid or clear pattern. This corresponds to * one of the two cases: * * 1) The radii are both very small: * |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON * * 2) The two circles have about the same radius and are very * close to each other (approximately a cylinder gradient that * doesn't move with the parameter): * |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON * * These checks are consistent with the assumptions used in * _cairo_radial_pattern_box_to_parameter (). */ return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON && (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON || MAX (fabs (radial->cd1.center.x - radial->cd2.center.x), fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON); } static void _cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear, double x0, double y0, double x1, double y1, double range[2]) { double t0, tdx, tdy; double p1x, p1y, pdx, pdy, invsqnorm; assert (! _linear_pattern_is_degenerate (linear)); /* * Linear gradients are othrogonal to the line passing through * their extremes. Because of convexity, the parameter range can * be computed as the convex hull (one the real line) of the * parameter values of the 4 corners of the box. * * The parameter value t for a point (x,y) can be computed as: * * t = (p2 - p1) . (x,y) / |p2 - p1|^2 * * t0 is the t value for the top left corner * tdx is the difference between left and right corners * tdy is the difference between top and bottom corners */ p1x = linear->pd1.x; p1y = linear->pd1.y; pdx = linear->pd2.x - p1x; pdy = linear->pd2.y - p1y; invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); pdx *= invsqnorm; pdy *= invsqnorm; t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy; tdx = (x1 - x0) * pdx; tdy = (y1 - y0) * pdy; /* * Because of the linearity of the t value, tdx can simply be * added the t0 to move along the top edge. After this, range[0] * and range[1] represent the parameter range for the top edge, so * extending it to include the whole box simply requires adding * tdy to the correct extreme. */ range[0] = range[1] = t0; if (tdx < 0) range[0] += tdx; else range[1] += tdx; if (tdy < 0) range[0] += tdy; else range[1] += tdy; } static cairo_bool_t _extend_range (double range[2], double value, cairo_bool_t valid) { if (!valid) range[0] = range[1] = value; else if (value < range[0]) range[0] = value; else if (value > range[1]) range[1] = value; return TRUE; } /* * _cairo_radial_pattern_focus_is_inside: * * Returns %TRUE if and only if the focus point exists and is * contained in one of the two extreme circles. This condition is * equivalent to one of the two extreme circles being completely * contained in the other one. * * Note: if the focus is on the border of one of the two circles (in * which case the circles are tangent in the focus point), it is not * considered as contained in the circle, hence this function returns * %FALSE. * */ cairo_bool_t _cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial) { double cx, cy, cr, dx, dy, dr; cx = radial->cd1.center.x; cy = radial->cd1.center.y; cr = radial->cd1.radius; dx = radial->cd2.center.x - cx; dy = radial->cd2.center.y - cy; dr = radial->cd2.radius - cr; return dx*dx + dy*dy < dr*dr; } static void _cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial, double x0, double y0, double x1, double y1, double tolerance, double range[2]) { double cx, cy, cr, dx, dy, dr; double a, x_focus, y_focus; double mindr, minx, miny, maxx, maxy; cairo_bool_t valid; assert (! _radial_pattern_is_degenerate (radial)); assert (x0 < x1); assert (y0 < y1); tolerance = MAX (tolerance, DBL_EPSILON); range[0] = range[1] = 0; valid = FALSE; x_focus = y_focus = 0; /* silence gcc */ cx = radial->cd1.center.x; cy = radial->cd1.center.y; cr = radial->cd1.radius; dx = radial->cd2.center.x - cx; dy = radial->cd2.center.y - cy; dr = radial->cd2.radius - cr; /* translate by -(cx, cy) to simplify computations */ x0 -= cx; y0 -= cy; x1 -= cx; y1 -= cy; /* enlarge boundaries slightly to avoid rounding problems in the * parameter range computation */ x0 -= DBL_EPSILON; y0 -= DBL_EPSILON; x1 += DBL_EPSILON; y1 += DBL_EPSILON; /* enlarge boundaries even more to avoid rounding problems when * testing if a point belongs to the box */ minx = x0 - DBL_EPSILON; miny = y0 - DBL_EPSILON; maxx = x1 + DBL_EPSILON; maxy = y1 + DBL_EPSILON; /* we dont' allow negative radiuses, so we will be checking that * t*dr >= mindr to consider t valid */ mindr = -(cr + DBL_EPSILON); /* * After the previous transformations, the start circle is * centered in the origin and has radius cr. A 1-unit change in * the t parameter corresponds to dx,dy,dr changes in the x,y,r of * the circle (center coordinates, radius). * * To compute the minimum range needed to correctly draw the * pattern, we start with an empty range and extend it to include * the circles touching the bounding box or within it. */ /* * Focus, the point where the circle has radius == 0. * * r = cr + t * dr = 0 * t = -cr / dr * * If the radius is constant (dr == 0) there is no focus (the * gradient represents a cylinder instead of a cone). */ if (fabs (dr) >= DBL_EPSILON) { double t_focus; t_focus = -cr / dr; x_focus = t_focus * dx; y_focus = t_focus * dy; if (minx <= x_focus && x_focus <= maxx && miny <= y_focus && y_focus <= maxy) { valid = _extend_range (range, t_focus, valid); } } /* * Circles externally tangent to box edges. * * All circles have center in (dx, dy) * t * * If the circle is tangent to the line defined by the edge of the * box, then at least one of the following holds true: * * (dx*t) + (cr + dr*t) == x0 (left edge) * (dx*t) - (cr + dr*t) == x1 (right edge) * (dy*t) + (cr + dr*t) == y0 (top edge) * (dy*t) - (cr + dr*t) == y1 (bottom edge) * * The solution is only valid if the tangent point is actually on * the edge, i.e. if its y coordinate is in [y0,y1] for left/right * edges and if its x coordinate is in [x0,x1] for top/bottom * edges. * * For the first equation: * * (dx + dr) * t = x0 - cr * t = (x0 - cr) / (dx + dr) * y = dy * t * * in the code this becomes: * * t_edge = (num) / (den) * v = (delta) * t_edge * * If the denominator in t is 0, the pattern is tangent to a line * parallel to the edge under examination. The corner-case where * the boundary line is the same as the edge is handled by the * focus point case and/or by the a==0 case. */ #define T_EDGE(num,den,delta,lower,upper) \ if (fabs (den) >= DBL_EPSILON) { \ double t_edge, v; \ \ t_edge = (num) / (den); \ v = t_edge * (delta); \ if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \ valid = _extend_range (range, t_edge, valid); \ } /* circles tangent (externally) to left/right/top/bottom edge */ T_EDGE (x0 - cr, dx + dr, dy, miny, maxy); T_EDGE (x1 + cr, dx - dr, dy, miny, maxy); T_EDGE (y0 - cr, dy + dr, dx, minx, maxx); T_EDGE (y1 + cr, dy - dr, dx, minx, maxx); #undef T_EDGE /* * Circles passing through a corner. * * A circle passing through the point (x,y) satisfies: * * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 * * If we set: * a = dx^2 + dy^2 - dr^2 * b = x*dx + y*dy + cr*dr * c = x^2 + y^2 - cr^2 * we have: * a*t^2 - 2*b*t + c == 0 */ a = dx * dx + dy * dy - dr * dr; if (fabs (a) < DBL_EPSILON * DBL_EPSILON) { double b, maxd2; /* Ensure that gradients with both a and dr small are * considered degenerate. * The floating point version of the degeneracy test implemented * in _radial_pattern_is_degenerate() is: * * 1) The circles are practically the same size: * |dr| < DBL_EPSILON * AND * 2a) The circles are both very small: * min (r0, r1) < DBL_EPSILON * OR * 2b) The circles are very close to each other: * max (|dx|, |dy|) < 2 * DBL_EPSILON * * Assuming that the gradient is not degenerate, we want to * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON. * * If the gradient is not degenerate yet it has |dr| < * DBL_EPSILON, (2b) is false, thus: * * max (|dx|, |dy|) >= 2*DBL_EPSILON * which implies: * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 * * From the definition of a, we get: * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2 * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2 * 3*DBL_EPSILON^2 < dr^2 * * which is inconsistent with the hypotheses, thus |dr| < * DBL_EPSILON is false or the gradient is degenerate. */ assert (fabs (dr) >= DBL_EPSILON); /* * If a == 0, all the circles are tangent to a line in the * focus point. If this line is within the box extents, we * should add the circle with infinite radius, but this would * make the range unbounded, so we add the smallest circle whose * distance to the desired (degenerate) circle within the * bounding box does not exceed tolerance. * * The equation of the line is b==0, i.e.: * x*dx + y*dy + cr*dr == 0 * * We compute the intersection of the line with the box and * keep the intersection with maximum square distance (maxd2) * from the focus point. * * In the code the intersection is represented in another * coordinate system, whose origin is the focus point and * which has a u,v axes, which are respectively orthogonal and * parallel to the edge being intersected. * * The intersection is valid only if it belongs to the box, * otherwise it is ignored. * * For example: * * y = y0 * x*dx + y0*dy + cr*dr == 0 * x = -(y0*dy + cr*dr) / dx * * which in (u,v) is: * u = y0 - y_focus * v = -(y0*dy + cr*dr) / dx - x_focus * * In the code: * u = (edge) - (u_origin) * v = -((edge) * (delta) + cr*dr) / (den) - v_focus */ #define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \ if (fabs (den) >= DBL_EPSILON) { \ double v; \ \ v = -((edge) * (delta) + cr * dr) / (den); \ if ((lower) <= v && v <= (upper)) { \ double u, d2; \ \ u = (edge) - (u_origin); \ v -= (v_origin); \ d2 = u*u + v*v; \ if (maxd2 < d2) \ maxd2 = d2; \ } \ } maxd2 = 0; /* degenerate circles (lines) passing through each edge */ T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus); T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus); T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus); T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus); #undef T_EDGE /* * The limit circle can be transformed rigidly to the y=0 line * and the circles tangent to it in (0,0) are: * * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0 * * y is the distance from the line, in our case tolerance; * x is the distance along the line, i.e. sqrt(maxd2), * so: * * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance) * t = (r - cr) / dr = * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr) */ if (maxd2 > 0) { double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr; t_limit /= 2 * tolerance * dr; valid = _extend_range (range, t_limit, valid); } /* * Nondegenerate, nonlimit circles passing through the corners. * * a == 0 && a*t^2 - 2*b*t + c == 0 * * t = c / (2*b) * * The b == 0 case has just been handled, so we only have to * compute this if b != 0. */ #define T_CORNER(x,y) \ b = (x) * dx + (y) * dy + cr * dr; \ if (fabs (b) >= DBL_EPSILON) { \ double t_corner; \ double x2 = (x) * (x); \ double y2 = (y) * (y); \ double cr2 = (cr) * (cr); \ double c = x2 + y2 - cr2; \ \ t_corner = 0.5 * c / b; \ if (t_corner * dr >= mindr) \ valid = _extend_range (range, t_corner, valid); \ } /* circles touching each corner */ T_CORNER (x0, y0); T_CORNER (x0, y1); T_CORNER (x1, y0); T_CORNER (x1, y1); #undef T_CORNER } else { double inva, b, c, d; inva = 1 / a; /* * Nondegenerate, nonlimit circles passing through the corners. * * a != 0 && a*t^2 - 2*b*t + c == 0 * * t = (b +- sqrt (b*b - a*c)) / a * * If the argument of sqrt() is negative, then no circle * passes through the corner. */ #define T_CORNER(x,y) \ b = (x) * dx + (y) * dy + cr * dr; \ c = (x) * (x) + (y) * (y) - cr * cr; \ d = b * b - a * c; \ if (d >= 0) { \ double t_corner; \ \ d = sqrt (d); \ t_corner = (b + d) * inva; \ if (t_corner * dr >= mindr) \ valid = _extend_range (range, t_corner, valid); \ t_corner = (b - d) * inva; \ if (t_corner * dr >= mindr) \ valid = _extend_range (range, t_corner, valid); \ } /* circles touching each corner */ T_CORNER (x0, y0); T_CORNER (x0, y1); T_CORNER (x1, y0); T_CORNER (x1, y1); #undef T_CORNER } } /** * _cairo_gradient_pattern_box_to_parameter: * * Compute a interpolation range sufficient to draw (within the given * tolerance) the gradient in the given box getting the same result as * using the (-inf, +inf) range. * * Assumes that the pattern is not degenerate. This can be guaranteed * by simplifying it to a solid clear if _cairo_pattern_is_clear or to * a solid color if _cairo_gradient_pattern_is_solid. * * The range isn't guaranteed to be minimal, but it tries to. **/ void _cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, double x0, double y0, double x1, double y1, double tolerance, double out_range[2]) { assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient, x0, y0, x1, y1, out_range); } else { _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient, x0, y0, x1, y1, tolerance, out_range); } } /** * _cairo_gradient_pattern_interpolate: * * Interpolate between the start and end objects of linear or radial * gradients. The interpolated object is stored in out_circle, with * the radius being zero in the linear gradient case. **/ void _cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, double t, cairo_circle_double_t *out_circle) { assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); #define lerp(a,b) (a)*(1-t) + (b)*t if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x); out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y); out_circle->radius = 0; } else { cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x); out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y); out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius); } #undef lerp } /** * _cairo_gradient_pattern_fit_to_range: * * Scale the extremes of a gradient to guarantee that the coordinates * and their deltas are within the range (-max_value, max_value). The * new extremes are stored in out_circle. * * The pattern matrix is scaled to guarantee that the aspect of the * gradient is the same and the result is stored in out_matrix. * **/ void _cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient, double max_value, cairo_matrix_t *out_matrix, cairo_circle_double_t out_circle[2]) { double dim; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; out_circle[0].center = linear->pd1; out_circle[0].radius = 0; out_circle[1].center = linear->pd2; out_circle[1].radius = 0; dim = fabs (linear->pd1.x); dim = MAX (dim, fabs (linear->pd1.y)); dim = MAX (dim, fabs (linear->pd2.x)); dim = MAX (dim, fabs (linear->pd2.y)); dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x)); dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y)); } else { cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; out_circle[0] = radial->cd1; out_circle[1] = radial->cd2; dim = fabs (radial->cd1.center.x); dim = MAX (dim, fabs (radial->cd1.center.y)); dim = MAX (dim, fabs (radial->cd1.radius)); dim = MAX (dim, fabs (radial->cd2.center.x)); dim = MAX (dim, fabs (radial->cd2.center.y)); dim = MAX (dim, fabs (radial->cd2.radius)); dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x)); dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y)); dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius)); } if (unlikely (dim > max_value)) { cairo_matrix_t scale; dim = max_value / dim; out_circle[0].center.x *= dim; out_circle[0].center.y *= dim; out_circle[0].radius *= dim; out_circle[1].center.x *= dim; out_circle[1].center.y *= dim; out_circle[1].radius *= dim; cairo_matrix_init_scale (&scale, dim, dim); cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale); } else { *out_matrix = gradient->base.matrix; } } static cairo_bool_t _gradient_is_clear (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents) { unsigned int i; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (gradient->n_stops == 0 || (gradient->base.extend == CAIRO_EXTEND_NONE && gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) return TRUE; if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) { /* degenerate radial gradients are clear */ if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient)) return TRUE; } else if (gradient->base.extend == CAIRO_EXTEND_NONE) { /* EXTEND_NONE degenerate linear gradients are clear */ if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient)) return TRUE; } /* Check if the extents intersect the drawn part of the pattern. */ if (extents != NULL && (gradient->base.extend == CAIRO_EXTEND_NONE || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL)) { double t[2]; _cairo_gradient_pattern_box_to_parameter (gradient, extents->x, extents->y, extents->x + extents->width, extents->y + extents->height, DBL_EPSILON, t); if (gradient->base.extend == CAIRO_EXTEND_NONE && (t[0] >= gradient->stops[gradient->n_stops - 1].offset || t[1] <= gradient->stops[0].offset)) { return TRUE; } if (t[0] == t[1]) return TRUE; } for (i = 0; i < gradient->n_stops; i++) if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color)) return FALSE; return TRUE; } static void _gradient_color_average (const cairo_gradient_pattern_t *gradient, cairo_color_t *color) { double delta0, delta1; double r, g, b, a; unsigned int i, start = 1, end; assert (gradient->n_stops > 0); assert (gradient->base.extend != CAIRO_EXTEND_NONE); if (gradient->n_stops == 1) { _cairo_color_init_rgba (color, gradient->stops[0].color.red, gradient->stops[0].color.green, gradient->stops[0].color.blue, gradient->stops[0].color.alpha); return; } end = gradient->n_stops - 1; switch (gradient->base.extend) { case CAIRO_EXTEND_REPEAT: /* * Sa, Sb and Sy, Sz are the first two and last two stops respectively. * The weight of the first and last stop can be computed as the area of * the following triangles (taken with height 1, since the whole [0-1] * will have total weight 1 this way): b*h/2 * * + + * / |\ / | \ * / | \ / | \ * / | \ / | \ * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~ * -1+Sz 0 Sa Sb Sy Sz 1 1+Sa * * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2 * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2 * Halving the result is done after summing up all the areas. */ delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset; delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset; break; case CAIRO_EXTEND_REFLECT: /* * Sa, Sb and Sy, Sz are the first two and last two stops respectively. * The weight of the first and last stop can be computed as the area of * the following trapezoids (taken with height 1, since the whole [0-1] * will have total weight 1 this way): (b+B)*h/2 * * +-------+ +---+ * | |\ / | | * | | \ / | | * | | \ / | | * +-------+---+~~~~~~~+-------+---+ * 0 Sa Sb Sy Sz 1 * * For the first stop: (Sa+Sb)/2 * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2 * Halving the result is done after summing up all the areas. */ delta0 = gradient->stops[0].offset + gradient->stops[1].offset; delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset; break; case CAIRO_EXTEND_PAD: /* PAD is computed as the average of the first and last stop: * - take both of them with weight 1 (they will be halved * after the whole sum has been computed). * - avoid summing any of the inner stops. */ delta0 = delta1 = 1.0; start = end; break; case CAIRO_EXTEND_NONE: default: ASSERT_NOT_REACHED; _cairo_color_init_rgba (color, 0, 0, 0, 0); return; } r = delta0 * gradient->stops[0].color.red; g = delta0 * gradient->stops[0].color.green; b = delta0 * gradient->stops[0].color.blue; a = delta0 * gradient->stops[0].color.alpha; for (i = start; i < end; ++i) { /* Inner stops weight is the same as the area of the triangle they influence * (which goes from the stop before to the stop after), again with height 1 * since the whole must sum up to 1: b*h/2 * Halving is done after the whole sum has been computed. */ double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset; r += delta * gradient->stops[i].color.red; g += delta * gradient->stops[i].color.green; b += delta * gradient->stops[i].color.blue; a += delta * gradient->stops[i].color.alpha; } r += delta1 * gradient->stops[end].color.red; g += delta1 * gradient->stops[end].color.green; b += delta1 * gradient->stops[end].color.blue; a += delta1 * gradient->stops[end].color.alpha; _cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5); } /** * _cairo_pattern_alpha_range: * * Convenience function to determine the minimum and maximum alpha in * the drawn part of a pattern (i.e. ignoring clear parts caused by * extend modes and/or pattern shape). * * If not NULL, out_min and out_max will be set respectively to the * minimum and maximum alpha value of the pattern. **/ void _cairo_pattern_alpha_range (const cairo_pattern_t *pattern, double *out_min, double *out_max) { double alpha_min, alpha_max; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; alpha_min = alpha_max = solid->color.alpha; break; } case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; unsigned int i; assert (gradient->n_stops >= 1); alpha_min = alpha_max = gradient->stops[0].color.alpha; for (i = 1; i < gradient->n_stops; i++) { if (alpha_min > gradient->stops[i].color.alpha) alpha_min = gradient->stops[i].color.alpha; else if (alpha_max < gradient->stops[i].color.alpha) alpha_max = gradient->stops[i].color.alpha; } break; } case CAIRO_PATTERN_TYPE_MESH: { const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern; const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches); assert (n >= 1); alpha_min = alpha_max = patch[0].colors[0].alpha; for (i = 0; i < n; i++) { for (j = 0; j < 4; j++) { if (patch[i].colors[j].alpha < alpha_min) alpha_min = patch[i].colors[j].alpha; else if (patch[i].colors[j].alpha > alpha_max) alpha_max = patch[i].colors[j].alpha; } } break; } default: ASSERT_NOT_REACHED; /* fall through */ case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: alpha_min = 0; alpha_max = 1; break; } if (out_min) *out_min = alpha_min; if (out_max) *out_max = alpha_max; } /** * _cairo_mesh_pattern_coord_box: * * Convenience function to determine the range of the coordinates of * the points used to define the patches of the mesh. * * This is guaranteed to contain the pattern extents, but might not be * tight, just like a Bezier curve is always inside the convex hull of * the control points. * * This function cannot be used while the mesh is being constructed. * * The function returns TRUE and sets the output parametes to define * the coodrinate range if the mesh pattern contains at least one * patch, otherwise it returns FALSE. **/ cairo_bool_t _cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, double *out_xmin, double *out_ymin, double *out_xmax, double *out_ymax) { const cairo_mesh_patch_t *patch; unsigned int num_patches, i, j, k; double x0, y0, x1, y1; assert (mesh->current_patch == NULL); num_patches = _cairo_array_num_elements (&mesh->patches); if (num_patches == 0) return FALSE; patch = _cairo_array_index_const (&mesh->patches, 0); x0 = x1 = patch->points[0][0].x; y0 = y1 = patch->points[0][0].y; for (i = 0; i < num_patches; i++) { for (j = 0; j < 4; j++) { for (k = 0; k < 4; k++) { x0 = MIN (x0, patch[i].points[j][k].x); y0 = MIN (y0, patch[i].points[j][k].y); x1 = MAX (x1, patch[i].points[j][k].x); y1 = MAX (y1, patch[i].points[j][k].y); } } } *out_xmin = x0; *out_ymin = y0; *out_xmax = x1; *out_ymax = y1; return TRUE; } /** * _cairo_gradient_pattern_is_solid: * * Convenience function to determine whether a gradient pattern is * a solid color within the given extents. In this case the color * argument is initialized to the color the pattern represents. * This functions doesn't handle completely transparent gradients, * thus it should be called only after _cairo_pattern_is_clear has * returned FALSE. * * Return value: %TRUE if the pattern is a solid color. **/ cairo_bool_t _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents, cairo_color_t *color) { unsigned int i; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); /* TODO: radial */ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; if (_linear_pattern_is_degenerate (linear)) { _gradient_color_average (gradient, color); return TRUE; } if (gradient->base.extend == CAIRO_EXTEND_NONE) { double t[2]; /* We already know that the pattern is not clear, thus if some * part of it is clear, the whole is not solid. */ if (extents == NULL) return FALSE; _cairo_linear_pattern_box_to_parameter (linear, extents->x, extents->y, extents->x + extents->width, extents->y + extents->height, t); if (t[0] < 0.0 || t[1] > 1.0) return FALSE; } } else return FALSE; for (i = 1; i < gradient->n_stops; i++) if (! _cairo_color_stop_equal (&gradient->stops[0].color, &gradient->stops[i].color)) return FALSE; _cairo_color_init_rgba (color, gradient->stops[0].color.red, gradient->stops[0].color.green, gradient->stops[0].color.blue, gradient->stops[0].color.alpha); return TRUE; } static cairo_bool_t _mesh_is_clear (const cairo_mesh_pattern_t *mesh) { double x1, y1, x2, y2; cairo_bool_t is_valid; is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2); if (!is_valid) return TRUE; if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON) return TRUE; return FALSE; } /** * _cairo_pattern_is_opaque_solid: * * Convenience function to determine whether a pattern is an opaque * (alpha==1.0) solid color pattern. This is done by testing whether * the pattern's alpha value when converted to a byte is 255, so if a * backend actually supported deep alpha channels this function might * not do the right thing. * * Return value: %TRUE if the pattern is an opaque, solid color. **/ cairo_bool_t _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) { cairo_solid_pattern_t *solid; if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return FALSE; solid = (cairo_solid_pattern_t *) pattern; return CAIRO_COLOR_IS_OPAQUE (&solid->color); } static cairo_bool_t _surface_is_opaque (const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *sample) { cairo_rectangle_int_t extents; if (pattern->surface->content & CAIRO_CONTENT_ALPHA) return FALSE; if (pattern->base.extend != CAIRO_EXTEND_NONE) return TRUE; if (! _cairo_surface_get_extents (pattern->surface, &extents)) return TRUE; if (sample == NULL) return FALSE; return _cairo_rectangle_contains_rectangle (&extents, sample); } static cairo_bool_t _raster_source_is_opaque (const cairo_raster_source_pattern_t *pattern, const cairo_rectangle_int_t *sample) { if (pattern->content & CAIRO_CONTENT_ALPHA) return FALSE; if (pattern->base.extend != CAIRO_EXTEND_NONE) return TRUE; if (sample == NULL) return FALSE; return _cairo_rectangle_contains_rectangle (&pattern->extents, sample); } static cairo_bool_t _surface_is_clear (const cairo_surface_pattern_t *pattern) { cairo_rectangle_int_t extents; if (_cairo_surface_get_extents (pattern->surface, &extents) && (extents.width == 0 || extents.height == 0)) return TRUE; return pattern->surface->is_clear && pattern->surface->content & CAIRO_CONTENT_ALPHA; } static cairo_bool_t _raster_source_is_clear (const cairo_raster_source_pattern_t *pattern) { return pattern->extents.width == 0 || pattern->extents.height == 0; } static cairo_bool_t _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *sample) { unsigned int i; assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); if (gradient->n_stops == 0 || (gradient->base.extend == CAIRO_EXTEND_NONE && gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) return FALSE; if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { if (gradient->base.extend == CAIRO_EXTEND_NONE) { double t[2]; cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; /* EXTEND_NONE degenerate radial gradients are clear */ if (_linear_pattern_is_degenerate (linear)) return FALSE; if (sample == NULL) return FALSE; _cairo_linear_pattern_box_to_parameter (linear, sample->x, sample->y, sample->x + sample->width, sample->y + sample->height, t); if (t[0] < 0.0 || t[1] > 1.0) return FALSE; } } else return FALSE; /* TODO: check actual intersection */ for (i = 0; i < gradient->n_stops; i++) if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) return FALSE; return TRUE; } /** * _cairo_pattern_is_opaque: * * Convenience function to determine whether a pattern is an opaque * pattern (of any type). The same caveats that apply to * _cairo_pattern_is_opaque_solid apply here as well. * * Return value: %TRUE if the pattern is a opaque. **/ cairo_bool_t _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, const cairo_rectangle_int_t *sample) { const cairo_pattern_union_t *pattern; if (abstract_pattern->has_component_alpha) return FALSE; pattern = (cairo_pattern_union_t *) abstract_pattern; switch (pattern->base.type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_pattern_is_opaque_solid (abstract_pattern); case CAIRO_PATTERN_TYPE_SURFACE: return _surface_is_opaque (&pattern->surface, sample); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _raster_source_is_opaque (&pattern->raster_source, sample); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: return _gradient_is_opaque (&pattern->gradient.base, sample); case CAIRO_PATTERN_TYPE_MESH: return FALSE; } ASSERT_NOT_REACHED; return FALSE; } cairo_bool_t _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) { const cairo_pattern_union_t *pattern; if (abstract_pattern->has_component_alpha) return FALSE; pattern = (cairo_pattern_union_t *) abstract_pattern; switch (abstract_pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color); case CAIRO_PATTERN_TYPE_SURFACE: return _surface_is_clear (&pattern->surface); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _raster_source_is_clear (&pattern->raster_source); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: return _gradient_is_clear (&pattern->gradient.base, NULL); case CAIRO_PATTERN_TYPE_MESH: return _mesh_is_clear (&pattern->mesh); } ASSERT_NOT_REACHED; return FALSE; } /** * _cairo_pattern_analyze_filter: * @pattern: surface pattern * @pad_out: location to store necessary padding in the source image, or %NULL * Returns: the optimized #cairo_filter_t to use with @pattern. * * Analyze the filter to determine how much extra needs to be sampled * from the source image to account for the filter radius and whether * we can optimize the filter to a simpler value. * * XXX: We don't actually have any way of querying the backend for * the filter radius, so we just guess base on what we know that * backends do currently (see bug #10508) **/ cairo_filter_t _cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, double *pad_out) { double pad; cairo_filter_t optimized_filter; switch (pattern->filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: /* If source pixels map 1:1 onto destination pixels, we do * not need to filter (and do not want to filter, since it * will cause blurriness) */ if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { pad = 0.; optimized_filter = CAIRO_FILTER_NEAREST; } else { /* 0.5 is enough for a bilinear filter. It's possible we * should defensively use more for CAIRO_FILTER_BEST, but * without a single example, it's hard to know how much * more would be defensive... */ pad = 0.5; optimized_filter = pattern->filter; } break; case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: default: pad = 0.; optimized_filter = pattern->filter; break; } if (pad_out) *pad_out = pad; return optimized_filter; } cairo_filter_t _cairo_pattern_sampled_area (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_rectangle_int_t *sample) { cairo_filter_t filter; double x1, x2, y1, y2; double pad; filter = _cairo_pattern_analyze_filter (pattern, &pad); if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) { *sample = *extents; return filter; } x1 = extents->x; y1 = extents->y; x2 = extents->x + (int) extents->width; y2 = extents->y + (int) extents->height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); if (x1 > CAIRO_RECT_INT_MIN) sample->x = floor (x1 - pad); else sample->x = CAIRO_RECT_INT_MIN; if (y1 > CAIRO_RECT_INT_MIN) sample->y = floor (y1 - pad); else sample->y = CAIRO_RECT_INT_MIN; if (x2 < CAIRO_RECT_INT_MAX) sample->width = ceil (x2 + pad); else sample->width = CAIRO_RECT_INT_MAX; if (y2 < CAIRO_RECT_INT_MAX) sample->height = ceil (y2 + pad); else sample->height = CAIRO_RECT_INT_MAX; sample->width -= sample->x; sample->height -= sample->y; return filter; } /** * _cairo_pattern_get_extents: * * Return the "target-space" extents of @pattern in @extents. * * For unbounded patterns, the @extents will be initialized with * "infinite" extents, (minimum and maximum fixed-point values). * * XXX: Currently, bounded gradient patterns will also return * "infinite" extents, though it would be possible to optimize these * with a little more work. **/ void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents) { double x1, y1, x2, y2; cairo_status_t status; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: goto UNBOUNDED; case CAIRO_PATTERN_TYPE_SURFACE: { cairo_rectangle_int_t surface_extents; const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; double pad; if (! _cairo_surface_get_extents (surface, &surface_extents)) goto UNBOUNDED; if (surface_extents.width == 0 || surface_extents.height == 0) goto EMPTY; if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; /* The filter can effectively enlarge the extents of the * pattern, so extend as necessary. */ _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); x1 = surface_extents.x - pad; y1 = surface_extents.y - pad; x2 = surface_extents.x + (int) surface_extents.width + pad; y2 = surface_extents.y + (int) surface_extents.height + pad; } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { const cairo_raster_source_pattern_t *raster = (const cairo_raster_source_pattern_t *) pattern; double pad; if (raster->extents.width == 0 || raster->extents.height == 0) goto EMPTY; if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; /* The filter can effectively enlarge the extents of the * pattern, so extend as necessary. */ _cairo_pattern_analyze_filter (pattern, &pad); x1 = raster->extents.x - pad; y1 = raster->extents.y - pad; x2 = raster->extents.x + (int) raster->extents.width + pad; y2 = raster->extents.y + (int) raster->extents.height + pad; } break; case CAIRO_PATTERN_TYPE_RADIAL: { const cairo_radial_pattern_t *radial = (const cairo_radial_pattern_t *) pattern; double cx1, cy1; double cx2, cy2; double r1, r2; if (_radial_pattern_is_degenerate (radial)) { /* cairo-gstate should have optimised degenerate * patterns to solid clear patterns, so we can ignore * them here. */ goto EMPTY; } /* TODO: in some cases (focus outside/on the circle) it is * half-bounded. */ if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; cx1 = radial->cd1.center.x; cy1 = radial->cd1.center.y; r1 = radial->cd1.radius; cx2 = radial->cd2.center.x; cy2 = radial->cd2.center.y; r2 = radial->cd2.radius; x1 = MIN (cx1 - r1, cx2 - r2); y1 = MIN (cy1 - r1, cy2 - r2); x2 = MAX (cx1 + r1, cx2 + r2); y2 = MAX (cy1 + r1, cy2 + r2); } break; case CAIRO_PATTERN_TYPE_LINEAR: { const cairo_linear_pattern_t *linear = (const cairo_linear_pattern_t *) pattern; if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; if (_linear_pattern_is_degenerate (linear)) { /* cairo-gstate should have optimised degenerate * patterns to solid ones, so we can again ignore * them here. */ goto EMPTY; } /* TODO: to get tight extents, use the matrix to transform * the pattern instead of transforming the extents later. */ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) goto UNBOUNDED; if (linear->pd1.x == linear->pd2.x) { x1 = -HUGE_VAL; x2 = HUGE_VAL; y1 = MIN (linear->pd1.y, linear->pd2.y); y2 = MAX (linear->pd1.y, linear->pd2.y); } else if (linear->pd1.y == linear->pd2.y) { x1 = MIN (linear->pd1.x, linear->pd2.x); x2 = MAX (linear->pd1.x, linear->pd2.x); y1 = -HUGE_VAL; y2 = HUGE_VAL; } else { goto UNBOUNDED; } } break; case CAIRO_PATTERN_TYPE_MESH: { const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern; double padx, pady; cairo_bool_t is_valid; is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2); if (!is_valid) goto EMPTY; padx = pady = 1.; cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady); padx = fabs (padx); pady = fabs (pady); x1 -= padx; y1 -= pady; x2 += padx; y2 += pady; } break; default: ASSERT_NOT_REACHED; } if (_cairo_matrix_is_translation (&pattern->matrix)) { x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0; y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0; } else { cairo_matrix_t imatrix; imatrix = pattern->matrix; status = cairo_matrix_invert (&imatrix); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); _cairo_matrix_transform_bounding_box (&imatrix, &x1, &y1, &x2, &y2, NULL); } x1 = floor (x1); if (x1 < CAIRO_RECT_INT_MIN) x1 = CAIRO_RECT_INT_MIN; y1 = floor (y1); if (y1 < CAIRO_RECT_INT_MIN) y1 = CAIRO_RECT_INT_MIN; x2 = ceil (x2); if (x2 > CAIRO_RECT_INT_MAX) x2 = CAIRO_RECT_INT_MAX; y2 = ceil (y2); if (y2 > CAIRO_RECT_INT_MAX) y2 = CAIRO_RECT_INT_MAX; extents->x = x1; extents->width = x2 - x1; extents->y = y1; extents->height = y2 - y1; return; UNBOUNDED: /* unbounded patterns -> 'infinite' extents */ _cairo_unbounded_rectangle_init (extents); return; EMPTY: extents->x = extents->y = 0; extents->width = extents->height = 0; return; } /** * _cairo_pattern_get_ink_extents: * * Return the "target-space" inked extents of @pattern in @extents. **/ cairo_int_status_t _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && pattern->extend == CAIRO_EXTEND_NONE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; surface = _cairo_surface_get_source (surface, NULL); if (_cairo_surface_is_recording (surface)) { cairo_matrix_t imatrix; cairo_box_t box; cairo_status_t status; imatrix = pattern->matrix; status = cairo_matrix_invert (&imatrix); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface, &box, &imatrix); if (unlikely (status)) return status; _cairo_box_round_to_rectangle (&box, extents); return CAIRO_STATUS_SUCCESS; } } _cairo_pattern_get_extents (pattern, extents); return CAIRO_STATUS_SUCCESS; } static unsigned long _cairo_solid_pattern_hash (unsigned long hash, const cairo_solid_pattern_t *solid) { hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); return hash; } static unsigned long _cairo_gradient_color_stops_hash (unsigned long hash, const cairo_gradient_pattern_t *gradient) { unsigned int n; hash = _cairo_hash_bytes (hash, &gradient->n_stops, sizeof (gradient->n_stops)); for (n = 0; n < gradient->n_stops; n++) { hash = _cairo_hash_bytes (hash, &gradient->stops[n].offset, sizeof (double)); hash = _cairo_hash_bytes (hash, &gradient->stops[n].color, sizeof (cairo_color_stop_t)); } return hash; } unsigned long _cairo_linear_pattern_hash (unsigned long hash, const cairo_linear_pattern_t *linear) { hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1)); hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2)); return _cairo_gradient_color_stops_hash (hash, &linear->base); } unsigned long _cairo_radial_pattern_hash (unsigned long hash, const cairo_radial_pattern_t *radial) { hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center)); hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius)); hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center)); hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius)); return _cairo_gradient_color_stops_hash (hash, &radial->base); } static unsigned long _cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh) { const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); unsigned int i, n = _cairo_array_num_elements (&mesh->patches); for (i = 0; i < n; i++) hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t)); return hash; } static unsigned long _cairo_surface_pattern_hash (unsigned long hash, const cairo_surface_pattern_t *surface) { hash ^= surface->surface->unique_id; return hash; } static unsigned long _cairo_raster_source_pattern_hash (unsigned long hash, const cairo_raster_source_pattern_t *raster) { hash ^= (uintptr_t)raster->user_data; return hash; } unsigned long _cairo_pattern_hash (const cairo_pattern_t *pattern) { unsigned long hash = _CAIRO_HASH_INIT_VALUE; if (pattern->status) return 0; hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { hash = _cairo_hash_bytes (hash, &pattern->matrix, sizeof (pattern->matrix)); hash = _cairo_hash_bytes (hash, &pattern->filter, sizeof (pattern->filter)); hash = _cairo_hash_bytes (hash, &pattern->extend, sizeof (pattern->extend)); hash = _cairo_hash_bytes (hash, &pattern->has_component_alpha, sizeof (pattern->has_component_alpha)); } switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_MESH: return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern); default: ASSERT_NOT_REACHED; return FALSE; } } static cairo_bool_t _cairo_solid_pattern_equal (const cairo_solid_pattern_t *a, const cairo_solid_pattern_t *b) { return _cairo_color_equal (&a->color, &b->color); } static cairo_bool_t _cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, const cairo_gradient_pattern_t *b) { unsigned int n; if (a->n_stops != b->n_stops) return FALSE; for (n = 0; n < a->n_stops; n++) { if (a->stops[n].offset != b->stops[n].offset) return FALSE; if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) return FALSE; } return TRUE; } cairo_bool_t _cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, const cairo_linear_pattern_t *b) { if (a->pd1.x != b->pd1.x) return FALSE; if (a->pd1.y != b->pd1.y) return FALSE; if (a->pd2.x != b->pd2.x) return FALSE; if (a->pd2.y != b->pd2.y) return FALSE; return _cairo_gradient_color_stops_equal (&a->base, &b->base); } cairo_bool_t _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, const cairo_radial_pattern_t *b) { if (a->cd1.center.x != b->cd1.center.x) return FALSE; if (a->cd1.center.y != b->cd1.center.y) return FALSE; if (a->cd1.radius != b->cd1.radius) return FALSE; if (a->cd2.center.x != b->cd2.center.x) return FALSE; if (a->cd2.center.y != b->cd2.center.y) return FALSE; if (a->cd2.radius != b->cd2.radius) return FALSE; return _cairo_gradient_color_stops_equal (&a->base, &b->base); } static cairo_bool_t _cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a, const cairo_mesh_pattern_t *b) { const cairo_mesh_patch_t *patch_a, *patch_b; unsigned int i, num_patches_a, num_patches_b; num_patches_a = _cairo_array_num_elements (&a->patches); num_patches_b = _cairo_array_num_elements (&b->patches); if (num_patches_a != num_patches_b) return FALSE; for (i = 0; i < num_patches_a; i++) { patch_a = _cairo_array_index_const (&a->patches, i); patch_b = _cairo_array_index_const (&a->patches, i); if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0) return FALSE; } return TRUE; } static cairo_bool_t _cairo_surface_pattern_equal (const cairo_surface_pattern_t *a, const cairo_surface_pattern_t *b) { return a->surface->unique_id == b->surface->unique_id; } static cairo_bool_t _cairo_raster_source_pattern_equal (const cairo_raster_source_pattern_t *a, const cairo_raster_source_pattern_t *b) { return a->user_data == b->user_data; } cairo_bool_t _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) { if (a->status || b->status) return FALSE; if (a == b) return TRUE; if (a->type != b->type) return FALSE; if (a->has_component_alpha != b->has_component_alpha) return FALSE; if (a->type != CAIRO_PATTERN_TYPE_SOLID) { if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) return FALSE; if (a->filter != b->filter) return FALSE; if (a->extend != b->extend) return FALSE; } switch (a->type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a, (cairo_solid_pattern_t *) b); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, (cairo_linear_pattern_t *) b); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, (cairo_radial_pattern_t *) b); case CAIRO_PATTERN_TYPE_MESH: return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a, (cairo_mesh_pattern_t *) b); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a, (cairo_surface_pattern_t *) b); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_raster_source_pattern_equal ((cairo_raster_source_pattern_t *) a, (cairo_raster_source_pattern_t *) b); default: ASSERT_NOT_REACHED; return FALSE; } } /** * cairo_pattern_get_rgba: * @pattern: a #cairo_pattern_t * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL * @blue: return value for blue component of color, or %NULL * @alpha: return value for alpha component of color, or %NULL * * Gets the solid color for a solid color pattern. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid * color pattern. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_rgba (cairo_pattern_t *pattern, double *red, double *green, double *blue, double *alpha) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; double r0, g0, b0, a0; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0); if (red) *red = r0; if (green) *green = g0; if (blue) *blue = b0; if (alpha) *alpha = a0; return CAIRO_STATUS_SUCCESS; } /** * cairo_pattern_get_surface: * @pattern: a #cairo_pattern_t * @surface: return value for surface of pattern, or %NULL * * Gets the surface of a surface pattern. The reference returned in * @surface is owned by the pattern; the caller should call * cairo_surface_reference() if the surface is to be retained. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface * pattern. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_surface (cairo_pattern_t *pattern, cairo_surface_t **surface) { cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (surface) *surface = spat->surface; return CAIRO_STATUS_SUCCESS; } /** * cairo_pattern_get_color_stop_rgba: * @pattern: a #cairo_pattern_t * @index: index of the stop to return data for * @offset: return value for the offset of the stop, or %NULL * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL * @blue: return value for blue component of color, or %NULL * @alpha: return value for alpha component of color, or %NULL * * Gets the color and offset information at the given @index for a * gradient pattern. Values of @index are 0 to 1 less than the number * returned by cairo_pattern_get_color_stop_count(). * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @index is not valid for the given pattern. If the pattern is * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is * returned. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, int index, double *offset, double *red, double *green, double *blue, double *alpha) { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (index < 0 || (unsigned int) index >= gradient->n_stops) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); if (offset) *offset = gradient->stops[index].offset; if (red) *red = gradient->stops[index].color.red; if (green) *green = gradient->stops[index].color.green; if (blue) *blue = gradient->stops[index].color.blue; if (alpha) *alpha = gradient->stops[index].color.alpha; return CAIRO_STATUS_SUCCESS; } /** * cairo_pattern_get_color_stop_count: * @pattern: a #cairo_pattern_t * @count: return value for the number of color stops, or %NULL * * Gets the number of color stops specified in the given gradient * pattern. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient * pattern. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, int *count) { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (count) *count = gradient->n_stops; return CAIRO_STATUS_SUCCESS; } /** * cairo_pattern_get_linear_points: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the first point, or %NULL * @y0: return value for the y coordinate of the first point, or %NULL * @x1: return value for the x coordinate of the second point, or %NULL * @y1: return value for the y coordinate of the second point, or %NULL * * Gets the gradient endpoints for a linear gradient. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear * gradient pattern. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_linear_points (cairo_pattern_t *pattern, double *x0, double *y0, double *x1, double *y1) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (x0) *x0 = linear->pd1.x; if (y0) *y0 = linear->pd1.y; if (x1) *x1 = linear->pd2.x; if (y1) *y1 = linear->pd2.y; return CAIRO_STATUS_SUCCESS; } /** * cairo_pattern_get_radial_circles: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the center of the first circle, or %NULL * @y0: return value for the y coordinate of the center of the first circle, or %NULL * @r0: return value for the radius of the first circle, or %NULL * @x1: return value for the x coordinate of the center of the second circle, or %NULL * @y1: return value for the y coordinate of the center of the second circle, or %NULL * @r1: return value for the radius of the second circle, or %NULL * * Gets the gradient endpoint circles for a radial gradient, each * specified as a center coordinate and a radius. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial * gradient pattern. * * Since: 1.4 **/ cairo_status_t cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, double *x0, double *y0, double *r0, double *x1, double *y1, double *r1) { cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (x0) *x0 = radial->cd1.center.x; if (y0) *y0 = radial->cd1.center.y; if (r0) *r0 = radial->cd1.radius; if (x1) *x1 = radial->cd2.center.x; if (y1) *y1 = radial->cd2.center.y; if (r1) *r1 = radial->cd2.radius; return CAIRO_STATUS_SUCCESS; } /** * cairo_mesh_pattern_get_patch_count: * @pattern: a #cairo_pattern_t * @count: return value for the number patches, or %NULL * * Gets the number of patches specified in the given mesh pattern. * * The number only includes patches which have been finished by * calling cairo_mesh_pattern_end_patch(). For example it will be 0 * during the definition of the first patch. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh * pattern. * * Since: 1.12 **/ cairo_status_t cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, unsigned int *count) { cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; if (unlikely (pattern->status)) return pattern->status; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (count) { *count = _cairo_array_num_elements (&mesh->patches); if (mesh->current_patch) *count -= 1; } return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_mesh_pattern_get_patch_count); /** * cairo_mesh_pattern_get_path: * @pattern: a #cairo_pattern_t * @patch_num: the patch number to return data for * * Gets path defining the patch @patch_num for a mesh * pattern. * * @patch_num can range 0 to 1 less than the number returned by * cairo_mesh_pattern_get_patch_count(). * * Return value: the path defining the patch, or a path with status * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not * valid for @pattern. If @pattern is not a mesh pattern, a path with * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned. * * Since: 1.12 **/ cairo_path_t * cairo_mesh_pattern_get_path (cairo_pattern_t *pattern, unsigned int patch_num) { cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; const cairo_mesh_patch_t *patch; cairo_path_t *path; cairo_path_data_t *data; unsigned int patch_count; int l, current_point; if (unlikely (pattern->status)) return _cairo_path_create_in_error (pattern->status); if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH)); patch_count = _cairo_array_num_elements (&mesh->patches); if (mesh->current_patch) patch_count--; if (unlikely (patch_num >= patch_count)) return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); patch = _cairo_array_index_const (&mesh->patches, patch_num); path = malloc (sizeof (cairo_path_t)); if (path == NULL) return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); path->num_data = 18; path->data = _cairo_malloc_ab (path->num_data, sizeof (cairo_path_data_t)); if (path->data == NULL) { free (path); return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } data = path->data; data[0].header.type = CAIRO_PATH_MOVE_TO; data[0].header.length = 2; data[1].point.x = patch->points[0][0].x; data[1].point.y = patch->points[0][0].y; data += data[0].header.length; current_point = 0; for (l = 0; l < 4; l++) { int i, j, k; data[0].header.type = CAIRO_PATH_CURVE_TO; data[0].header.length = 4; for (k = 1; k < 4; k++) { current_point = (current_point + 1) % 12; i = mesh_path_point_i[current_point]; j = mesh_path_point_j[current_point]; data[k].point.x = patch->points[i][j].x; data[k].point.y = patch->points[i][j].y; } data += data[0].header.length; } path->status = CAIRO_STATUS_SUCCESS; return path; } slim_hidden_def (cairo_mesh_pattern_get_path); /** * cairo_mesh_pattern_get_corner_color_rgba: * @pattern: a #cairo_pattern_t * @patch_num: the patch number to return data for * @corner_num: the corner number to return data for * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL * @blue: return value for blue component of color, or %NULL * @alpha: return value for alpha component of color, or %NULL * * Gets the color information in corner @corner_num of patch * @patch_num for a mesh pattern. * * @patch_num can range 0 to 1 less than the number returned by * cairo_mesh_pattern_get_patch_count(). * * Valid values for @corner_num are from 0 to 3 and identify the * corners as explained in cairo_pattern_create_mesh(). * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @patch_num or @corner_num is not valid for @pattern. If * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH * is returned. * * Since: 1.12 **/ cairo_status_t cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern, unsigned int patch_num, unsigned int corner_num, double *red, double *green, double *blue, double *alpha) { cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; unsigned int patch_count; const cairo_mesh_patch_t *patch; if (unlikely (pattern->status)) return pattern->status; if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (unlikely (corner_num > 3)) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); patch_count = _cairo_array_num_elements (&mesh->patches); if (mesh->current_patch) patch_count--; if (unlikely (patch_num >= patch_count)) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); patch = _cairo_array_index_const (&mesh->patches, patch_num); if (red) *red = patch->colors[corner_num].red; if (green) *green = patch->colors[corner_num].green; if (blue) *blue = patch->colors[corner_num].blue; if (alpha) *alpha = patch->colors[corner_num].alpha; return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba); /** * cairo_mesh_pattern_get_control_point: * @pattern: a #cairo_pattern_t * @patch_num: the patch number to return data for * @point_num: the control point number to return data for * @x: return value for the x coordinate of the control point, or %NULL * @y: return value for the y coordinate of the control point, or %NULL * * Gets the control point @point_num of patch @patch_num for a mesh * pattern. * * @patch_num can range 0 to 1 less than the number returned by * cairo_mesh_pattern_get_patch_count(). * * Valid values for @point_num are from 0 to 3 and identify the * control points as explained in cairo_pattern_create_mesh(). * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @patch_num or @point_num is not valid for @pattern. If @pattern * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is * returned. * * Since: 1.12 **/ cairo_status_t cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern, unsigned int patch_num, unsigned int point_num, double *x, double *y) { cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; const cairo_mesh_patch_t *patch; unsigned int patch_count; int i, j; if (pattern->status) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_MESH) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (point_num > 3) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); patch_count = _cairo_array_num_elements (&mesh->patches); if (mesh->current_patch) patch_count--; if (unlikely (patch_num >= patch_count)) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); patch = _cairo_array_index_const (&mesh->patches, patch_num); i = mesh_control_point_i[point_num]; j = mesh_control_point_j[point_num]; if (x) *x = patch->points[i][j].x; if (y) *y = patch->points[i][j].y; return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_mesh_pattern_get_control_point); void _cairo_pattern_reset_static_data (void) { int i; for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) _freed_pool_reset (&freed_pattern_pool[i]); } static void _cairo_debug_print_surface_pattern (FILE *file, const cairo_surface_pattern_t *pattern) { printf (" surface type: %d\n", pattern->surface->type); } static void _cairo_debug_print_raster_source_pattern (FILE *file, const cairo_raster_source_pattern_t *raster) { printf (" content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height); } static void _cairo_debug_print_linear_pattern (FILE *file, const cairo_linear_pattern_t *pattern) { } static void _cairo_debug_print_radial_pattern (FILE *file, const cairo_radial_pattern_t *pattern) { } static void _cairo_debug_print_mesh_pattern (FILE *file, const cairo_mesh_pattern_t *pattern) { } void _cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern) { const char *s; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break; case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break; case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break; case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break; case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: s = "raster"; break; default: s = "invalid"; ASSERT_NOT_REACHED; break; } fprintf (file, "pattern: %s\n", s); if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) return; switch (pattern->extend) { case CAIRO_EXTEND_NONE: s = "none"; break; case CAIRO_EXTEND_REPEAT: s = "repeat"; break; case CAIRO_EXTEND_REFLECT: s = "reflect"; break; case CAIRO_EXTEND_PAD: s = "pad"; break; default: s = "invalid"; ASSERT_NOT_REACHED; break; } fprintf (file, " extend: %s\n", s); switch (pattern->filter) { case CAIRO_FILTER_FAST: s = "fast"; break; case CAIRO_FILTER_GOOD: s = "good"; break; case CAIRO_FILTER_BEST: s = "best"; break; case CAIRO_FILTER_NEAREST: s = "nearest"; break; case CAIRO_FILTER_BILINEAR: s = "bilinear"; break; case CAIRO_FILTER_GAUSSIAN: s = "guassian"; break; default: s = "invalid"; ASSERT_NOT_REACHED; break; } fprintf (file, " filter: %s\n", s); fprintf (file, " matrix: [%g %g %g %g %g %g]\n", pattern->matrix.xx, pattern->matrix.yx, pattern->matrix.xy, pattern->matrix.yy, pattern->matrix.x0, pattern->matrix.y0); switch (pattern->type) { default: case CAIRO_PATTERN_TYPE_SOLID: break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: _cairo_debug_print_raster_source_pattern (file, (cairo_raster_source_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_SURFACE: _cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: _cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_RADIAL: _cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern); break; case CAIRO_PATTERN_TYPE_MESH: _cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern); break; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-operators-private.h�������������������0000664�0000000�0000000�00000014503�12710376503�0030004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2006 Red Hat, Inc * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_PDF_OPERATORS_H #define CAIRO_PDF_OPERATORS_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-types-private.h" /* The glyph buffer size is based on the expected maximum glyphs in a * line so that an entire line can be emitted in as one string. If the * glyphs in a line exceeds this size the only downside is the slight * overhead of emitting two strings. */ #define PDF_GLYPH_BUFFER_SIZE 200 typedef cairo_int_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id, unsigned int subset_id, void *closure); typedef struct _cairo_pdf_glyph { unsigned int glyph_index; double x_position; double x_advance; } cairo_pdf_glyph_t; typedef struct _cairo_pdf_operators { cairo_output_stream_t *stream; cairo_matrix_t cairo_to_pdf; cairo_scaled_font_subsets_t *font_subsets; cairo_pdf_operators_use_font_subset_t use_font_subset; void *use_font_subset_closure; cairo_bool_t use_actual_text; cairo_bool_t in_text_object; /* inside BT/ET pair */ /* PDF text state */ cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */ unsigned int font_id; unsigned int subset_id; cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */ cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */ cairo_matrix_t font_matrix_inverse; double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */ double cur_y; int hex_width; cairo_bool_t is_latin; int num_glyphs; double glyph_buf_x_pos; cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE]; /* PDF line style */ cairo_bool_t has_line_style; double line_width; cairo_line_cap_t line_cap; cairo_line_join_t line_join; double miter_limit; cairo_bool_t has_dashes; } cairo_pdf_operators_t; cairo_private void _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, cairo_matrix_t *cairo_to_pdf, cairo_scaled_font_subsets_t *font_subsets); cairo_private cairo_status_t _cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators); cairo_private void _cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, cairo_pdf_operators_use_font_subset_t use_font_subset, void *closure); cairo_private void _cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream); cairo_private void _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, cairo_matrix_t *cairo_to_pdf); cairo_private void _cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, cairo_bool_t enable); cairo_private cairo_status_t _cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators); cairo_private void _cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators); cairo_private cairo_int_status_t _cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule); cairo_private cairo_int_status_t _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, const cairo_stroke_style_t *style, double scale); cairo_private cairo_int_status_t _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse); cairo_private cairo_int_status_t _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule); cairo_private cairo_int_status_t _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse); cairo_private cairo_int_status_t _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font); #endif /* CAIRO_PDF_OPERATORS_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-operators.c���������������������������0000664�0000000�0000000�00000133764�12710376503�0026342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2006 Red Hat, Inc * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #if CAIRO_HAS_PDF_OPERATORS #include "cairo-error-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-path-fixed-private.h" #include "cairo-output-stream-private.h" #include "cairo-scaled-font-subsets-private.h" static cairo_status_t _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); void _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, cairo_matrix_t *cairo_to_pdf, cairo_scaled_font_subsets_t *font_subsets) { pdf_operators->stream = stream; pdf_operators->cairo_to_pdf = *cairo_to_pdf; pdf_operators->font_subsets = font_subsets; pdf_operators->use_font_subset = NULL; pdf_operators->use_font_subset_closure = NULL; pdf_operators->in_text_object = FALSE; pdf_operators->num_glyphs = 0; pdf_operators->has_line_style = FALSE; pdf_operators->use_actual_text = FALSE; } cairo_status_t _cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators) { return _cairo_pdf_operators_flush (pdf_operators); } void _cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, cairo_pdf_operators_use_font_subset_t use_font_subset, void *closure) { pdf_operators->use_font_subset = use_font_subset; pdf_operators->use_font_subset_closure = closure; } /* Change the output stream to a different stream. * _cairo_pdf_operators_flush() should always be called before calling * this function. */ void _cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream) { pdf_operators->stream = stream; pdf_operators->has_line_style = FALSE; } void _cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, cairo_matrix_t *cairo_to_pdf) { pdf_operators->cairo_to_pdf = *cairo_to_pdf; pdf_operators->has_line_style = FALSE; } cairo_private void _cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, cairo_bool_t enable) { pdf_operators->use_actual_text = enable; } /* Finish writing out any pending commands to the stream. This * function must be called by the surface before emitting anything * into the PDF stream. * * pdf_operators may leave the emitted PDF for some operations * unfinished in case subsequent operations can be merged. This * function will finish off any incomplete operation so the stream * will be in a state where the surface may emit its own PDF * operations (eg changing patterns). * */ cairo_status_t _cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) { cairo_status_t status = CAIRO_STATUS_SUCCESS; if (pdf_operators->in_text_object) status = _cairo_pdf_operators_end_text (pdf_operators); return status; } /* Reset the known graphics state of the PDF consumer. ie no * assumptions will be made about the state. The next time a * particular graphics state is required (eg line width) the state * operator is always emitted and then remembered for subsequent * operatations. * * This should be called when starting a new stream or after emitting * the 'Q' operator (where pdf-operators functions were called inside * the q/Q pair). */ void _cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) { pdf_operators->has_line_style = FALSE; } /* A word wrap stream can be used as a filter to do word wrapping on * top of an existing output stream. The word wrapping is quite * simple, using isspace to determine characters that separate * words. Any word that will cause the column count exceed the given * max_column will have a '\n' character emitted before it. * * The stream is careful to maintain integrity for words that cross * the boundary from one call to write to the next. * * Note: This stream does not guarantee that the output will never * exceed max_column. In particular, if a single word is larger than * max_column it will not be broken up. */ typedef enum _cairo_word_wrap_state { WRAP_STATE_DELIMITER, WRAP_STATE_WORD, WRAP_STATE_STRING, WRAP_STATE_HEXSTRING } cairo_word_wrap_state_t; typedef struct _word_wrap_stream { cairo_output_stream_t base; cairo_output_stream_t *output; int max_column; int column; cairo_word_wrap_state_t state; cairo_bool_t in_escape; int escape_digits; } word_wrap_stream_t; /* Emit word bytes up to the next delimiter character */ static int _word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream, const unsigned char *data, int length) { const unsigned char *s = data; int count = 0; while (length--) { if (_cairo_isspace (*s) || *s == '<' || *s == '(') { stream->state = WRAP_STATE_DELIMITER; break; } count++; stream->column++; s++; } if (count) _cairo_output_stream_write (stream->output, data, count); return count; } /* Emit hexstring bytes up to either the end of the ASCII hexstring or the number * of columns remaining. */ static int _word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream, const unsigned char *data, int length) { const unsigned char *s = data; int count = 0; cairo_bool_t newline = FALSE; while (length--) { count++; stream->column++; if (*s == '>') { stream->state = WRAP_STATE_DELIMITER; break; } if (stream->column > stream->max_column) { newline = TRUE; break; } s++; } if (count) _cairo_output_stream_write (stream->output, data, count); if (newline) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; } return count; } /* Count up to either the end of the string or the number of columns * remaining. */ static int _word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream, const unsigned char *data, int length) { const unsigned char *s = data; int count = 0; cairo_bool_t newline = FALSE; while (length--) { count++; stream->column++; if (!stream->in_escape) { if (*s == ')') { stream->state = WRAP_STATE_DELIMITER; break; } if (*s == '\\') { stream->in_escape = TRUE; stream->escape_digits = 0; } else if (stream->column > stream->max_column) { newline = TRUE; break; } } else { if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3) stream->in_escape = FALSE; } s++; } if (count) _cairo_output_stream_write (stream->output, data, count); if (newline) { _cairo_output_stream_printf (stream->output, "\\\n"); stream->column = 0; } return count; } static cairo_status_t _word_wrap_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { word_wrap_stream_t *stream = (word_wrap_stream_t *) base; int count; while (length) { switch (stream->state) { case WRAP_STATE_WORD: count = _word_wrap_stream_count_word_up_to (stream, data, length); break; case WRAP_STATE_HEXSTRING: count = _word_wrap_stream_count_hexstring_up_to (stream, data, length); break; case WRAP_STATE_STRING: count = _word_wrap_stream_count_string_up_to (stream, data, length); break; case WRAP_STATE_DELIMITER: count = 1; stream->column++; if (*data == '\n' || stream->column >= stream->max_column) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; } else if (*data == '<') { stream->state = WRAP_STATE_HEXSTRING; } else if (*data == '(') { stream->state = WRAP_STATE_STRING; } else if (!_cairo_isspace (*data)) { stream->state = WRAP_STATE_WORD; } if (*data != '\n') _cairo_output_stream_write (stream->output, data, 1); break; default: ASSERT_NOT_REACHED; count = length; break; } data += count; length -= count; } return _cairo_output_stream_get_status (stream->output); } static cairo_status_t _word_wrap_stream_close (cairo_output_stream_t *base) { word_wrap_stream_t *stream = (word_wrap_stream_t *) base; return _cairo_output_stream_get_status (stream->output); } static cairo_output_stream_t * _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) { word_wrap_stream_t *stream; if (output->status) return _cairo_output_stream_create_in_error (output->status); stream = malloc (sizeof (word_wrap_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _word_wrap_stream_write, NULL, _word_wrap_stream_close); stream->output = output; stream->max_column = max_column; stream->column = 0; stream->state = WRAP_STATE_DELIMITER; stream->in_escape = FALSE; stream->escape_digits = 0; return &stream->base; } typedef struct _pdf_path_info { cairo_output_stream_t *output; cairo_matrix_t *path_transform; cairo_line_cap_t line_cap; cairo_point_t last_move_to_point; cairo_bool_t has_sub_path; } pdf_path_info_t; static cairo_status_t _cairo_pdf_path_move_to (void *closure, const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); info->last_move_to_point = *point; info->has_sub_path = FALSE; cairo_matrix_transform_point (info->path_transform, &x, &y); _cairo_output_stream_printf (info->output, "%g %g m ", x, y); return _cairo_output_stream_get_status (info->output); } static cairo_status_t _cairo_pdf_path_line_to (void *closure, const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->line_cap != CAIRO_LINE_CAP_ROUND && ! info->has_sub_path && point->x == info->last_move_to_point.x && point->y == info->last_move_to_point.y) { return CAIRO_STATUS_SUCCESS; } info->has_sub_path = TRUE; cairo_matrix_transform_point (info->path_transform, &x, &y); _cairo_output_stream_printf (info->output, "%g %g l ", x, y); return _cairo_output_stream_get_status (info->output); } static cairo_status_t _cairo_pdf_path_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { pdf_path_info_t *info = closure; double bx = _cairo_fixed_to_double (b->x); double by = _cairo_fixed_to_double (b->y); double cx = _cairo_fixed_to_double (c->x); double cy = _cairo_fixed_to_double (c->y); double dx = _cairo_fixed_to_double (d->x); double dy = _cairo_fixed_to_double (d->y); info->has_sub_path = TRUE; cairo_matrix_transform_point (info->path_transform, &bx, &by); cairo_matrix_transform_point (info->path_transform, &cx, &cy); cairo_matrix_transform_point (info->path_transform, &dx, &dy); _cairo_output_stream_printf (info->output, "%g %g %g %g %g %g c ", bx, by, cx, cy, dx, dy); return _cairo_output_stream_get_status (info->output); } static cairo_status_t _cairo_pdf_path_close_path (void *closure) { pdf_path_info_t *info = closure; if (info->line_cap != CAIRO_LINE_CAP_ROUND && ! info->has_sub_path) { return CAIRO_STATUS_SUCCESS; } _cairo_output_stream_printf (info->output, "h\n"); return _cairo_output_stream_get_status (info->output); } static cairo_status_t _cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) { double x1 = _cairo_fixed_to_double (box->p1.x); double y1 = _cairo_fixed_to_double (box->p1.y); double x2 = _cairo_fixed_to_double (box->p2.x); double y2 = _cairo_fixed_to_double (box->p2.y); cairo_matrix_transform_point (info->path_transform, &x1, &y1); cairo_matrix_transform_point (info->path_transform, &x2, &y2); _cairo_output_stream_printf (info->output, "%g %g %g %g re ", x1, y1, x2 - x1, y2 - y1); return _cairo_output_stream_get_status (info->output); } /* The line cap value is needed to workaround the fact that PostScript * and PDF semantics for stroking degenerate sub-paths do not match * cairo semantics. (PostScript draws something for any line cap * value, while cairo draws something only for round caps). * * When using this function to emit a path to be filled, rather than * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that * the stroke workaround will not modify the path being emitted. */ static cairo_status_t _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t*path, cairo_matrix_t *path_transform, cairo_line_cap_t line_cap) { cairo_output_stream_t *word_wrap; cairo_status_t status, status2; pdf_path_info_t info; cairo_box_t box; word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); status = _cairo_output_stream_get_status (word_wrap); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap); info.output = word_wrap; info.path_transform = path_transform; info.line_cap = line_cap; if (_cairo_path_fixed_is_rectangle (path, &box)) { status = _cairo_pdf_path_rectangle (&info, &box); } else { status = _cairo_path_fixed_interpret (path, _cairo_pdf_path_move_to, _cairo_pdf_path_line_to, _cairo_pdf_path_curve_to, _cairo_pdf_path_close_path, &info); } status2 = _cairo_output_stream_destroy (word_wrap); if (status == CAIRO_STATUS_SUCCESS) status = status2; return status; } cairo_int_status_t _cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule) { const char *pdf_operator; cairo_status_t status; if (pdf_operators->in_text_object) { status = _cairo_pdf_operators_end_text (pdf_operators); if (unlikely (status)) return status; } if (! path->has_current_point) { /* construct an empty path */ _cairo_output_stream_printf (pdf_operators->stream, "0 0 m "); } else { status = _cairo_pdf_operators_emit_path (pdf_operators, path, &pdf_operators->cairo_to_pdf, CAIRO_LINE_CAP_ROUND); if (unlikely (status)) return status; } switch (fill_rule) { default: ASSERT_NOT_REACHED; case CAIRO_FILL_RULE_WINDING: pdf_operator = "W"; break; case CAIRO_FILL_RULE_EVEN_ODD: pdf_operator = "W*"; break; } _cairo_output_stream_printf (pdf_operators->stream, "%s n\n", pdf_operator); return _cairo_output_stream_get_status (pdf_operators->stream); } static int _cairo_pdf_line_cap (cairo_line_cap_t cap) { switch (cap) { case CAIRO_LINE_CAP_BUTT: return 0; case CAIRO_LINE_CAP_ROUND: return 1; case CAIRO_LINE_CAP_SQUARE: return 2; default: ASSERT_NOT_REACHED; return 0; } } static int _cairo_pdf_line_join (cairo_line_join_t join) { switch (join) { case CAIRO_LINE_JOIN_MITER: return 0; case CAIRO_LINE_JOIN_ROUND: return 1; case CAIRO_LINE_JOIN_BEVEL: return 2; default: ASSERT_NOT_REACHED; return 0; } } cairo_int_status_t _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, const cairo_stroke_style_t *style, double scale) { double *dash = style->dash; int num_dashes = style->num_dashes; double dash_offset = style->dash_offset; double line_width = style->line_width * scale; /* PostScript has "special needs" when it comes to zero-length * dash segments with butt caps. It apparently (at least * according to ghostscript) draws hairlines for this * case. That's not what the cairo semantics want, so we first * touch up the array to eliminate any 0.0 values that will * result in "on" segments. */ if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { int i; /* If there's an odd number of dash values they will each get * interpreted as both on and off. So we first explicitly * expand the array to remove the duplicate usage so that we * can modify some of the values. */ if (num_dashes % 2) { dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dash, style->dash, num_dashes * sizeof (double)); memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double)); num_dashes *= 2; } for (i = 0; i < num_dashes; i += 2) { if (dash[i] == 0.0) { /* Do not modify the dashes in-place, as we may need to also * replay this stroke to an image fallback. */ if (dash == style->dash) { dash = _cairo_malloc_ab (num_dashes, sizeof (double)); if (unlikely (dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dash, style->dash, num_dashes * sizeof (double)); } /* If we're at the front of the list, we first rotate * two elements from the end of the list to the front * of the list before folding away the 0.0. Or, if * there are only two dash elements, then there is * nothing at all to draw. */ if (i == 0) { double last_two[2]; if (num_dashes == 2) { free (dash); return CAIRO_INT_STATUS_NOTHING_TO_DO; } /* The cases of num_dashes == 0, 1, or 3 elements * cannot exist, so the rotation of 2 elements * will always be safe */ memcpy (last_two, dash + num_dashes - 2, sizeof (last_two)); memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double)); memcpy (dash, last_two, sizeof (last_two)); dash_offset += dash[0] + dash[1]; i = 2; } dash[i-1] += dash[i+1]; num_dashes -= 2; memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); /* If we might have just rotated, it's possible that * we rotated a 0.0 value to the front of the list. * Set i to -2 so it will get incremented to 0. */ if (i == 2) i = -2; } } } if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) { _cairo_output_stream_printf (pdf_operators->stream, "%f w\n", line_width); pdf_operators->line_width = line_width; } if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) { _cairo_output_stream_printf (pdf_operators->stream, "%d J\n", _cairo_pdf_line_cap (style->line_cap)); pdf_operators->line_cap = style->line_cap; } if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) { _cairo_output_stream_printf (pdf_operators->stream, "%d j\n", _cairo_pdf_line_join (style->line_join)); pdf_operators->line_join = style->line_join; } if (num_dashes) { int d; _cairo_output_stream_printf (pdf_operators->stream, "["); for (d = 0; d < num_dashes; d++) _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale); _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n", dash_offset * scale); pdf_operators->has_dashes = TRUE; } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) { _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n"); pdf_operators->has_dashes = FALSE; } if (dash != style->dash) free (dash); if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) { _cairo_output_stream_printf (pdf_operators->stream, "%f M ", style->miter_limit < 1.0 ? 1.0 : style->miter_limit); pdf_operators->miter_limit = style->miter_limit; } pdf_operators->has_line_style = TRUE; return _cairo_output_stream_get_status (pdf_operators->stream); } /* Scale the matrix so the largest absolute value of the non * translation components is 1.0. Return the scale required to restore * the matrix to the original values. * * eg the matrix [ 100 0 0 50 20 10 ] * * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] * and the scale returned is 100 */ static void _cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) { double s; s = fabs (m->xx); if (fabs (m->xy) > s) s = fabs (m->xy); if (fabs (m->yx) > s) s = fabs (m->yx); if (fabs (m->yy) > s) s = fabs (m->yy); *scale = s; s = 1.0/s; cairo_matrix_scale (m, s, s); } static cairo_int_status_t _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, const char *pdf_operator) { cairo_int_status_t status; cairo_matrix_t m, path_transform; cairo_bool_t has_ctm = TRUE; double scale = 1.0; if (pdf_operators->in_text_object) { status = _cairo_pdf_operators_end_text (pdf_operators); if (unlikely (status)) return status; } /* Optimize away the stroke ctm when it does not affect the * stroke. There are other ctm cases that could be optimized * however this is the most common. */ if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 && fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0) { has_ctm = FALSE; } /* The PDF CTM is transformed to the user space CTM when stroking * so the corect pen shape will be used. This also requires that * the path be transformed to user space when emitted. The * conversion of path coordinates to user space may cause rounding * errors. For example the device space point (1.234, 3.142) when * transformed to a user space CTM of [100 0 0 100 0 0] will be * emitted as (0.012, 0.031). * * To avoid the rounding problem we scale the user space CTM * matrix so that all the non translation components of the matrix * are <= 1. The line width and and dashes are scaled by the * inverse of the scale applied to the CTM. This maintains the * shape of the stroke pen while keeping the user space CTM within * the range that maximizes the precision of the emitted path. */ if (has_ctm) { m = *ctm; /* Zero out the translation since it does not affect the pen * shape however it may cause unnecessary digits to be emitted. */ m.x0 = 0.0; m.y0 = 0.0; _cairo_matrix_factor_out_scale (&m, &scale); path_transform = m; status = cairo_matrix_invert (&path_transform); if (unlikely (status)) return status; cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); } status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; if (has_ctm) { _cairo_output_stream_printf (pdf_operators->stream, "q %f %f %f %f %f %f cm\n", m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); } else { path_transform = pdf_operators->cairo_to_pdf; } status = _cairo_pdf_operators_emit_path (pdf_operators, path, &path_transform, style->line_cap); if (unlikely (status)) return status; _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); if (has_ctm) _cairo_output_stream_printf (pdf_operators->stream, " Q"); _cairo_output_stream_printf (pdf_operators->stream, "\n"); return _cairo_output_stream_get_status (pdf_operators->stream); } cairo_int_status_t _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse) { return _cairo_pdf_operators_emit_stroke (pdf_operators, path, style, ctm, ctm_inverse, "S"); } cairo_int_status_t _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule) { const char *pdf_operator; cairo_status_t status; if (pdf_operators->in_text_object) { status = _cairo_pdf_operators_end_text (pdf_operators); if (unlikely (status)) return status; } status = _cairo_pdf_operators_emit_path (pdf_operators, path, &pdf_operators->cairo_to_pdf, CAIRO_LINE_CAP_ROUND); if (unlikely (status)) return status; switch (fill_rule) { default: ASSERT_NOT_REACHED; case CAIRO_FILL_RULE_WINDING: pdf_operator = "f"; break; case CAIRO_FILL_RULE_EVEN_ODD: pdf_operator = "f*"; break; } _cairo_output_stream_printf (pdf_operators->stream, "%s\n", pdf_operator); return _cairo_output_stream_get_status (pdf_operators->stream); } cairo_int_status_t _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse) { const char *operator; switch (fill_rule) { default: ASSERT_NOT_REACHED; case CAIRO_FILL_RULE_WINDING: operator = "B"; break; case CAIRO_FILL_RULE_EVEN_ODD: operator = "B*"; break; } return _cairo_pdf_operators_emit_stroke (pdf_operators, path, style, ctm, ctm_inverse, operator); } static void _cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, unsigned int glyph) { if (pdf_operators->is_latin) { if (glyph == '(' || glyph == ')' || glyph == '\\') _cairo_output_stream_printf (stream, "\\%c", glyph); else if (glyph >= 0x20 && glyph <= 0x7e) _cairo_output_stream_printf (stream, "%c", glyph); else _cairo_output_stream_printf (stream, "\\%03o", glyph); } else { _cairo_output_stream_printf (stream, "%0*x", pdf_operators->hex_width, glyph); } } #define GLYPH_POSITION_TOLERANCE 0.001 /* Emit the string of glyphs using the 'Tj' operator. This requires * that the glyphs are positioned at their natural glyph advances. */ static cairo_status_t _cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream) { int i; _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { _cairo_pdf_operators_emit_glyph_index (pdf_operators, stream, pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); } /* Emit the string of glyphs using the 'TJ' operator. * * The TJ operator takes an array of strings of glyphs. Each string of * glyphs is displayed using the glyph advances of each glyph to * position the glyphs. A relative adjustment to the glyph advance may * be specified by including the adjustment between two strings. The * adjustment is in units of text space * -1000. */ static cairo_status_t _cairo_pdf_operators_emit_glyph_string_with_positioning ( cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream) { int i; _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) { double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; int rounded_delta; delta = -1000.0*delta; /* As the delta is in 1/1000 of a unit of text space, * rounding to an integer should still provide sufficient * precision. We round the delta before adding to Tm_x so * that we keep track of the accumulated rounding error in * the PDF interpreter and compensate for it when * calculating subsequent deltas. */ rounded_delta = _cairo_lround (delta); if (abs(rounded_delta) < 3) rounded_delta = 0; if (rounded_delta != 0) { if (pdf_operators->is_latin) { _cairo_output_stream_printf (stream, ")%d(", rounded_delta); } else { _cairo_output_stream_printf (stream, ">%d<", rounded_delta); } } /* Convert the rounded delta back to text * space before adding to the current text * position. */ delta = rounded_delta/-1000.0; pdf_operators->cur_x += delta; } _cairo_pdf_operators_emit_glyph_index (pdf_operators, stream, pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); } static cairo_status_t _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) { cairo_output_stream_t *word_wrap_stream; cairo_status_t status, status2; int i; double x; if (pdf_operators->num_glyphs == 0) return CAIRO_STATUS_SUCCESS; word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); status = _cairo_output_stream_get_status (word_wrap_stream); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap_stream); /* Check if glyph advance used to position every glyph */ x = pdf_operators->cur_x; for (i = 0; i < pdf_operators->num_glyphs; i++) { if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) break; x += pdf_operators->glyphs[i].x_advance; } if (i == pdf_operators->num_glyphs) { status = _cairo_pdf_operators_emit_glyph_string (pdf_operators, word_wrap_stream); } else { status = _cairo_pdf_operators_emit_glyph_string_with_positioning ( pdf_operators, word_wrap_stream); } pdf_operators->num_glyphs = 0; pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x; status2 = _cairo_output_stream_destroy (word_wrap_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; return status; } static cairo_status_t _cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, cairo_scaled_font_subsets_glyph_t *glyph, double x_position) { double x, y; x = glyph->x_advance; y = glyph->y_advance; if (glyph->is_scaled) cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x; pdf_operators->glyph_buf_x_pos += x; pdf_operators->num_glyphs++; if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE) return _cairo_pdf_operators_flush_glyphs (pdf_operators); return CAIRO_STATUS_SUCCESS; } /* Use 'Tm' operator to set the PDF text matrix. */ static cairo_status_t _cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, cairo_matrix_t *matrix) { cairo_matrix_t inverse; cairo_status_t status; /* We require the matrix to be invertable. */ inverse = *matrix; status = cairo_matrix_invert (&inverse); if (unlikely (status)) return status; pdf_operators->text_matrix = *matrix; pdf_operators->cur_x = 0; pdf_operators->cur_y = 0; pdf_operators->glyph_buf_x_pos = 0; _cairo_output_stream_printf (pdf_operators->stream, "%f %f %f %f %f %f Tm\n", pdf_operators->text_matrix.xx, pdf_operators->text_matrix.yx, pdf_operators->text_matrix.xy, pdf_operators->text_matrix.yy, pdf_operators->text_matrix.x0, pdf_operators->text_matrix.y0); pdf_operators->cairo_to_pdftext = *matrix; status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, &pdf_operators->cairo_to_pdf, &pdf_operators->cairo_to_pdftext); return _cairo_output_stream_get_status (pdf_operators->stream); } #define TEXT_MATRIX_TOLERANCE 1e-6 /* Set the translation components of the PDF text matrix to x, y. The * 'Td' operator is used to transform the text matrix. */ static cairo_status_t _cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, double x, double y) { cairo_matrix_t translate, inverse; cairo_status_t status; /* The Td operator transforms the text_matrix with: * * text_matrix' = T x text_matrix * * where T is a translation matrix with the translation components * set to the Td operands tx and ty. */ inverse = pdf_operators->text_matrix; status = cairo_matrix_invert (&inverse); assert (status == CAIRO_STATUS_SUCCESS); pdf_operators->text_matrix.x0 = x; pdf_operators->text_matrix.y0 = y; cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE) translate.x0 = 0.0; if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE) translate.y0 = 0.0; _cairo_output_stream_printf (pdf_operators->stream, "%f %f Td\n", translate.x0, translate.y0); pdf_operators->cur_x = 0; pdf_operators->cur_y = 0; pdf_operators->glyph_buf_x_pos = 0; pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix; status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, &pdf_operators->cairo_to_pdf, &pdf_operators->cairo_to_pdftext); return _cairo_output_stream_get_status (pdf_operators->stream); } /* Select the font using the 'Tf' operator. The font size is set to 1 * as we use the 'Tm' operator to set the font scale. */ static cairo_status_t _cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_status_t status; _cairo_output_stream_printf (pdf_operators->stream, "/f-%d-%d 1 Tf\n", subset_glyph->font_id, subset_glyph->subset_id); if (pdf_operators->use_font_subset) { status = pdf_operators->use_font_subset (subset_glyph->font_id, subset_glyph->subset_id, pdf_operators->use_font_subset_closure); if (unlikely (status)) return status; } pdf_operators->font_id = subset_glyph->font_id; pdf_operators->subset_id = subset_glyph->subset_id; pdf_operators->is_latin = subset_glyph->is_latin; if (subset_glyph->is_composite) pdf_operators->hex_width = 4; else pdf_operators->hex_width = 2; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) { _cairo_output_stream_printf (pdf_operators->stream, "BT\n"); pdf_operators->in_text_object = TRUE; pdf_operators->num_glyphs = 0; pdf_operators->glyph_buf_x_pos = 0; return _cairo_output_stream_get_status (pdf_operators->stream); } static cairo_status_t _cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) { cairo_status_t status; status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); pdf_operators->in_text_object = FALSE; return _cairo_output_stream_get_status (pdf_operators->stream); } /* Compare the scale components of two matrices. The translation * components are ignored. */ static cairo_bool_t _cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) { return (a->xx == b->xx && a->xy == b->xy && a->yx == b->yx && a->yy == b->yy); } static cairo_status_t _cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, const char *utf8, int utf8_len) { uint16_t *utf16; int utf16_len; cairo_status_t status; int i; _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <feff"); if (utf8_len) { status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len); if (unlikely (status)) return status; for (i = 0; i < utf16_len; i++) { _cairo_output_stream_printf (pdf_operators->stream, "%04x", (int) (utf16[i])); } free (utf16); } _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); return _cairo_output_stream_get_status (pdf_operators->stream); } static cairo_status_t _cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) { _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); return _cairo_output_stream_get_status (pdf_operators->stream); } static cairo_status_t _cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, cairo_glyph_t *glyph, cairo_scaled_font_subsets_glyph_t *subset_glyph) { double x, y; cairo_status_t status; if (pdf_operators->is_new_text_object || pdf_operators->font_id != subset_glyph->font_id || pdf_operators->subset_id != subset_glyph->subset_id) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); if (unlikely (status)) return status; pdf_operators->is_new_text_object = FALSE; } x = glyph->x; y = glyph->y; cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y); /* The TJ operator for displaying text strings can only set * the horizontal position of the glyphs. If the y position * (in text space) changes, use the Td operator to change the * current position to the next glyph. We also use the Td * operator to move the current position if the horizontal * position changes by more than 10 (in text space * units). This is becauses the horizontal glyph positioning * in the TJ operator is intended for kerning and there may be * PDF consumers that do not handle very large position * adjustments in TJ. */ if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 || fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; x = glyph->x; y = glyph->y; cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); if (unlikely (status)) return status; x = 0.0; y = 0.0; } status = _cairo_pdf_operators_add_glyph (pdf_operators, subset_glyph, x); return status; } /* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an * empty string. */ static cairo_int_status_t _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font) { cairo_scaled_font_subsets_glyph_t subset_glyph; cairo_glyph_t *cur_glyph; cairo_status_t status = CAIRO_STATUS_SUCCESS; int i; /* If the cluster maps 1 glyph to 1 or more unicode characters, we * first try _map_glyph() with the unicode string to see if it can * use toUnicode to map our glyph to the unicode. This will fail * if the glyph is already mapped to a different unicode string. * * We also go through this path if no unicode mapping was * supplied (utf8_len < 0). * * Mapping a glyph to a zero length unicode string requires the * use of ActualText. */ if (num_glyphs == 1 && utf8_len != 0) { status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, scaled_font, glyphs->index, utf8, utf8_len, &subset_glyph); if (unlikely (status)) return status; if (subset_glyph.utf8_is_mapped || utf8_len < 0) { status = _cairo_pdf_operators_emit_glyph (pdf_operators, glyphs, &subset_glyph); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } } if (pdf_operators->use_actual_text) { /* Fallback to using ActualText to map zero or more glyphs to a * unicode string. */ status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); if (unlikely (status)) return status; } cur_glyph = glyphs; /* XXX * If no glyphs, we should put *something* here for the text to be selectable. */ for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, scaled_font, cur_glyph->index, NULL, -1, &subset_glyph); if (unlikely (status)) return status; status = _cairo_pdf_operators_emit_glyph (pdf_operators, cur_glyph, &subset_glyph); if (unlikely (status)) return status; if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) cur_glyph--; else cur_glyph++; } if (pdf_operators->use_actual_text) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; status = _cairo_pdf_operators_end_actualtext (pdf_operators); } return status; } cairo_int_status_t _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font) { cairo_status_t status; int i; cairo_matrix_t text_matrix, invert_y_axis; double x, y; const char *cur_text; cairo_glyph_t *cur_glyph; pdf_operators->font_matrix_inverse = scaled_font->font_matrix; status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); if (status == CAIRO_STATUS_INVALID_MATRIX) return CAIRO_STATUS_SUCCESS; assert (status == CAIRO_STATUS_SUCCESS); pdf_operators->is_new_text_object = FALSE; if (pdf_operators->in_text_object == FALSE) { status = _cairo_pdf_operators_begin_text (pdf_operators); if (unlikely (status)) return status; /* Force Tm and Tf to be emitted when starting a new text * object.*/ pdf_operators->is_new_text_object = TRUE; } cairo_matrix_init_scale (&invert_y_axis, 1, -1); text_matrix = scaled_font->scale; /* Invert y axis in font space */ cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); /* Invert y axis in device space */ cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); if (pdf_operators->is_new_text_object || ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) { status = _cairo_pdf_operators_flush_glyphs (pdf_operators); if (unlikely (status)) return status; x = glyphs[0].x; y = glyphs[0].y; cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); text_matrix.x0 = x; text_matrix.y0 = y; status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); if (status == CAIRO_STATUS_INVALID_MATRIX) return CAIRO_STATUS_SUCCESS; if (unlikely (status)) return status; } if (num_clusters > 0) { cur_text = utf8; if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) cur_glyph = glyphs + num_glyphs; else cur_glyph = glyphs; for (i = 0; i < num_clusters; i++) { if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) cur_glyph -= clusters[i].num_glyphs; status = _cairo_pdf_operators_emit_cluster (pdf_operators, cur_text, clusters[i].num_bytes, cur_glyph, clusters[i].num_glyphs, cluster_flags, scaled_font); if (unlikely (status)) return status; cur_text += clusters[i].num_bytes; if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) cur_glyph += clusters[i].num_glyphs; } } else { for (i = 0; i < num_glyphs; i++) { status = _cairo_pdf_operators_emit_cluster (pdf_operators, NULL, -1, /* no unicode string available */ &glyphs[i], 1, FALSE, scaled_font); if (unlikely (status)) return status; } } return _cairo_output_stream_get_status (pdf_operators->stream); } #endif /* CAIRO_HAS_PDF_OPERATORS */ ������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-shading-private.h���������������������0000664�0000000�0000000�00000006552�12710376503�0027410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2009 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_PDF_SHADING_H #define CAIRO_PDF_SHADING_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" #include "cairo-pattern-private.h" typedef struct _cairo_pdf_shading { int shading_type; int bits_per_coordinate; int bits_per_component; int bits_per_flag; double *decode_array; int decode_array_length; unsigned char *data; unsigned long data_length; } cairo_pdf_shading_t; /** * _cairo_pdf_shading_init_color: * @shading: a #cairo_pdf_shading_t to initialize * @pattern: the #cairo_mesh_pattern_t to initialize from * * Generate the PDF shading dictionary data for the a PDF type 7 * shading from RGB part of the specified mesh pattern. * * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *pattern); /** * _cairo_pdf_shading_init_alpha: * @shading: a #cairo_pdf_shading_t to initialize * @pattern: the #cairo_mesh_pattern_t to initialize from * * Generate the PDF shading dictionary data for a PDF type 7 * shading from alpha part of the specified mesh pattern. * * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *pattern); /** * _cairo_pdf_shading_fini: * @shading: a #cairo_pdf_shading_t * * Free all resources associated with @shading. After this call, * @shading should not be used again without a subsequent call to * _cairo_pdf_shading_init() again first. **/ cairo_private void _cairo_pdf_shading_fini (cairo_pdf_shading_t *shading); #endif /* CAIRO_PDF_SHADING_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-shading.c�����������������������������0000664�0000000�0000000�00000017455�12710376503�0025737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2009 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson <ajohnson@redneon.com> */ #include "cairoint.h" #if CAIRO_HAS_PDF_OPERATORS #include "cairo-pdf-shading-private.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #include <float.h> static unsigned char * encode_coordinate (unsigned char *p, double c) { uint32_t f; f = c; *p++ = f >> 24; *p++ = (f >> 16) & 0xff; *p++ = (f >> 8) & 0xff; *p++ = f & 0xff; return p; } static unsigned char * encode_point (unsigned char *p, const cairo_point_double_t *point) { p = encode_coordinate (p, point->x); p = encode_coordinate (p, point->y); return p; } static unsigned char * encode_color_component (unsigned char *p, double color) { uint16_t c; c = _cairo_color_double_to_short (color); *p++ = c >> 8; *p++ = c & 0xff; return p; } static unsigned char * encode_color (unsigned char *p, const cairo_color_t *color) { p = encode_color_component (p, color->red); p = encode_color_component (p, color->green); p = encode_color_component (p, color->blue); return p; } static unsigned char * encode_alpha (unsigned char *p, const cairo_color_t *color) { p = encode_color_component (p, color->alpha); return p; } static cairo_status_t _cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *mesh, cairo_bool_t is_alpha) { unsigned int num_color_components, i; cairo_bool_t is_valid; if (is_alpha) num_color_components = 1; else num_color_components = 3; shading->decode_array_length = 4 + num_color_components * 2; shading->decode_array = _cairo_malloc_ab (shading->decode_array_length, sizeof (double)); if (unlikely (shading->decode_array == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); is_valid = _cairo_mesh_pattern_coord_box (mesh, &shading->decode_array[0], &shading->decode_array[2], &shading->decode_array[1], &shading->decode_array[3]); assert (is_valid); assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON); assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON); for (i = 0; i < num_color_components; i++) { shading->decode_array[4 + 2*i] = 0; shading->decode_array[5 + 2*i] = 1; } return CAIRO_STATUS_SUCCESS; } /* The ISO32000 specification mandates this order for the points which * define the patch. */ static const int pdf_points_order_i[16] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 }; static const int pdf_points_order_j[16] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 }; static cairo_status_t _cairo_pdf_shading_generate_data (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *mesh, cairo_bool_t is_alpha) { const cairo_mesh_patch_t *patch; double x_off, y_off, x_scale, y_scale; unsigned int num_patches; unsigned int num_color_components; unsigned char *p; unsigned int i, j; if (is_alpha) num_color_components = 1; else num_color_components = 3; num_patches = _cairo_array_num_elements (&mesh->patches); patch = _cairo_array_index_const (&mesh->patches, 0); /* Each patch requires: * * 1 flag - 1 byte * 16 points. Each point is 2 coordinates. Each coordinate is * stored in 4 bytes. * * 4 colors. Each color is stored in 2 bytes * num_color_components. */ shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components); shading->data = malloc (shading->data_length); if (unlikely (shading->data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); x_off = shading->decode_array[0]; y_off = shading->decode_array[2]; x_scale = UINT32_MAX / (shading->decode_array[1] - x_off); y_scale = UINT32_MAX / (shading->decode_array[3] - y_off); p = shading->data; for (i = 0; i < num_patches; i++) { /* edge flag */ *p++ = 0; /* 16 points */ for (j = 0; j < 16; j++) { cairo_point_double_t point; int pi, pj; pi = pdf_points_order_i[j]; pj = pdf_points_order_j[j]; point = patch[i].points[pi][pj]; /* Transform the point as specified in the decode array */ point.x -= x_off; point.y -= y_off; point.x *= x_scale; point.y *= y_scale; /* Make sure that rounding errors don't cause * wraparounds */ point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX); point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX); p = encode_point (p, &point); } /* 4 colors */ for (j = 0; j < 4; j++) { if (is_alpha) p = encode_alpha (p, &patch[i].colors[j]); else p = encode_color (p, &patch[i].colors[j]); } } assert (p == shading->data + shading->data_length); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_pdf_shading_init (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *mesh, cairo_bool_t is_alpha) { cairo_status_t status; assert (mesh->base.status == CAIRO_STATUS_SUCCESS); assert (mesh->current_patch == NULL); shading->shading_type = 7; /* * Coordinates from the minimum to the maximum value of the mesh * map to the [0..UINT32_MAX] range and are represented as * uint32_t values. * * Color components are represented as uint16_t (in a 0.16 fixed * point format, as in the rest of cairo). */ shading->bits_per_coordinate = 32; shading->bits_per_component = 16; shading->bits_per_flag = 8; shading->decode_array = NULL; shading->data = NULL; status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha); if (unlikely (status)) return status; return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha); } cairo_status_t _cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *pattern) { return _cairo_pdf_shading_init (shading, pattern, FALSE); } cairo_status_t _cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading, const cairo_mesh_pattern_t *pattern) { return _cairo_pdf_shading_init (shading, pattern, TRUE); } void _cairo_pdf_shading_fini (cairo_pdf_shading_t *shading) { free (shading->data); free (shading->decode_array); } #endif /* CAIRO_HAS_PDF_OPERATORS */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-surface-private.h���������������������0000664�0000000�0000000�00000014142�12710376503�0027415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2006 Red Hat, Inc * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_PDF_SURFACE_PRIVATE_H #define CAIRO_PDF_SURFACE_PRIVATE_H #include "cairo-pdf.h" #include "cairo-surface-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-path-fixed-private.h" typedef struct _cairo_pdf_resource { unsigned int id; } cairo_pdf_resource_t; #define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1) typedef struct _cairo_pdf_group_resources { cairo_bool_t operators[CAIRO_NUM_OPERATORS]; cairo_array_t alphas; cairo_array_t smasks; cairo_array_t patterns; cairo_array_t shadings; cairo_array_t xobjects; cairo_array_t fonts; } cairo_pdf_group_resources_t; typedef struct _cairo_pdf_source_surface_entry { cairo_hash_entry_t base; unsigned int id; unsigned char *unique_id; unsigned long unique_id_length; cairo_bool_t interpolate; cairo_bool_t stencil_mask; cairo_pdf_resource_t surface_res; int width; int height; cairo_rectangle_int_t extents; } cairo_pdf_source_surface_entry_t; typedef struct _cairo_pdf_source_surface { cairo_pattern_type_t type; cairo_surface_t *surface; cairo_pattern_t *raster_pattern; cairo_pdf_source_surface_entry_t *hash_entry; } cairo_pdf_source_surface_t; typedef struct _cairo_pdf_pattern { double width; double height; cairo_rectangle_int_t extents; cairo_pattern_t *pattern; cairo_pdf_resource_t pattern_res; cairo_pdf_resource_t gstate_res; cairo_bool_t is_shading; } cairo_pdf_pattern_t; typedef enum _cairo_pdf_operation { PDF_PAINT, PDF_MASK, PDF_FILL, PDF_STROKE, PDF_SHOW_GLYPHS } cairo_pdf_operation_t; typedef struct _cairo_pdf_smask_group { double width; double height; cairo_rectangle_int_t extents; cairo_pdf_resource_t group_res; cairo_pdf_operation_t operation; cairo_pattern_t *source; cairo_pdf_resource_t source_res; cairo_pattern_t *mask; cairo_path_fixed_t path; cairo_fill_rule_t fill_rule; cairo_stroke_style_t style; cairo_matrix_t ctm; cairo_matrix_t ctm_inverse; char *utf8; int utf8_len; cairo_glyph_t *glyphs; int num_glyphs; cairo_text_cluster_t *clusters; int num_clusters; cairo_bool_t cluster_flags; cairo_scaled_font_t *scaled_font; } cairo_pdf_smask_group_t; typedef struct _cairo_pdf_surface cairo_pdf_surface_t; struct _cairo_pdf_surface { cairo_surface_t base; /* Prefer the name "output" here to avoid confusion over the * structure within a PDF document known as a "stream". */ cairo_output_stream_t *output; double width; double height; cairo_matrix_t cairo_to_pdf; cairo_array_t objects; cairo_array_t pages; cairo_array_t rgb_linear_functions; cairo_array_t alpha_linear_functions; cairo_array_t page_patterns; cairo_array_t page_surfaces; cairo_hash_table_t *all_surfaces; cairo_array_t smask_groups; cairo_array_t knockout_group; cairo_scaled_font_subsets_t *font_subsets; cairo_array_t fonts; cairo_pdf_resource_t next_available_resource; cairo_pdf_resource_t pages_resource; cairo_pdf_version_t pdf_version; cairo_bool_t compress_content; cairo_pdf_resource_t content; cairo_pdf_resource_t content_resources; cairo_pdf_group_resources_t resources; cairo_bool_t has_fallback_images; cairo_bool_t header_emitted; struct { cairo_bool_t active; cairo_pdf_resource_t self; cairo_pdf_resource_t length; long start_offset; cairo_bool_t compressed; cairo_output_stream_t *old_output; } pdf_stream; struct { cairo_bool_t active; cairo_output_stream_t *stream; cairo_output_stream_t *mem_stream; cairo_output_stream_t *old_output; cairo_pdf_resource_t resource; cairo_box_double_t bbox; cairo_bool_t is_knockout; } group_stream; cairo_surface_clipper_t clipper; cairo_pdf_operators_t pdf_operators; cairo_paginated_mode_t paginated_mode; cairo_bool_t select_pattern_gstate_saved; cairo_bool_t force_fallbacks; cairo_operator_t current_operator; cairo_bool_t current_pattern_is_solid_color; cairo_bool_t current_color_is_stroke; double current_color_red; double current_color_green; double current_color_blue; double current_color_alpha; cairo_surface_t *paginated_surface; }; #endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf-surface.c�����������������������������0000664�0000000�0000000�00000672042�12710376503�0025751�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2006 Red Hat, Inc * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ #define _BSD_SOURCE /* for snprintf() */ #include "cairoint.h" #include "cairo-pdf.h" #include "cairo-pdf-surface-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-pdf-shading-private.h" #include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-image-info-private.h" #include "cairo-recording-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-type3-glyph-surface-private.h" #include <time.h> #include <zlib.h> /* Issues: * * - We embed an image in the stream each time it's composited. We * could add generation counters to surfaces and remember the stream * ID for a particular generation for a particular surface. * * - Backend specific meta data. */ /* * Page Structure of the Generated PDF: * * Each page requiring fallbacks images contains a knockout group at * the top level. The first operation of the knockout group paints a * group containing all the supported drawing operations. Fallback * images (if any) are painted in the knockout group. This ensures * that fallback images do not composite with any content under the * fallback images. * * Streams: * * This PDF surface has three types of streams: * - PDF Stream * - Content Stream * - Group Stream * * Calling _cairo_output_stream_printf (surface->output, ...) will * write to the currently open stream. * * PDF Stream: * A PDF Stream may be opened and closed with the following functions: * _cairo_pdf_surface_open stream () * _cairo_pdf_surface_close_stream () * * PDF Streams are written directly to the PDF file. They are used for * fonts, images and patterns. * * Content Stream: * The Content Stream is opened and closed with the following functions: * _cairo_pdf_surface_open_content_stream () * _cairo_pdf_surface_close_content_stream () * * The Content Stream contains the text and graphics operators. * * Group Stream: * A Group Stream may be opened and closed with the following functions: * _cairo_pdf_surface_open_group () * _cairo_pdf_surface_close_group () * * A Group Stream is a Form XObject. It is used for short sequences * of operators. As the content is very short the group is stored in * memory until it is closed. This allows some optimization such as * including the Resource dictionary and stream length inside the * XObject instead of using an indirect object. */ /** * SECTION:cairo-pdf * @Title: PDF Surfaces * @Short_Description: Rendering PDF documents * @See_Also: #cairo_surface_t * * The PDF surface is used to render cairo graphics to Adobe * PDF files and is a multi-page vector surface backend. **/ static cairo_bool_t _cairo_pdf_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); /** * CAIRO_HAS_PDF_SURFACE: * * Defined if the PDF surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.2 **/ static const cairo_pdf_version_t _cairo_pdf_versions[] = { CAIRO_PDF_VERSION_1_4, CAIRO_PDF_VERSION_1_5 }; #define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions) static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = { "PDF 1.4", "PDF 1.5" }; static const char *_cairo_pdf_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_UNIQUE_ID, NULL }; typedef struct _cairo_pdf_object { long offset; } cairo_pdf_object_t; typedef struct _cairo_pdf_font { unsigned int font_id; unsigned int subset_id; cairo_pdf_resource_t subset_resource; } cairo_pdf_font_t; typedef struct _cairo_pdf_rgb_linear_function { cairo_pdf_resource_t resource; double color1[3]; double color2[3]; } cairo_pdf_rgb_linear_function_t; typedef struct _cairo_pdf_alpha_linear_function { cairo_pdf_resource_t resource; double alpha1; double alpha2; } cairo_pdf_alpha_linear_function_t; static cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); static void _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure); static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res); static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, const char *fmt, ...) CAIRO_PRINTF_FORMAT(4, 5); static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); static cairo_pdf_resource_t _cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface); static cairo_pdf_resource_t _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); static long _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); static const cairo_surface_backend_t cairo_pdf_surface_backend; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; static cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t resource; cairo_int_status_t status; cairo_pdf_object_t object; object.offset = _cairo_output_stream_get_position (surface->output); status = _cairo_array_append (&surface->objects, &object); if (unlikely (status)) { resource.id = 0; return resource; } resource = surface->next_available_resource; surface->next_available_resource.id++; return resource; } static void _cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, cairo_pdf_resource_t resource) { cairo_pdf_object_t *object; object = _cairo_array_index (&surface->objects, resource.id - 1); object->offset = _cairo_output_stream_get_position (surface->output); } static void _cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, double width, double height) { surface->width = width; surface->height = height; cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_pdf); } static cairo_bool_t _path_covers_bbox (cairo_pdf_surface_t *surface, cairo_path_fixed_t *path) { cairo_box_t box; return _cairo_path_fixed_is_box (path, &box) && box.p1.x <= 0 && box.p1.y <= 0 && box.p2.x >= _cairo_fixed_from_double (surface->width) && box.p2.y >= _cairo_fixed_from_double (surface->height); } static cairo_status_t _cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_pdf_surface_t *surface = cairo_container_of (clipper, cairo_pdf_surface_t, clipper); cairo_int_status_t status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; if (path == NULL) { _cairo_output_stream_printf (surface->output, "Q q\n"); surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); return CAIRO_STATUS_SUCCESS; } if (_path_covers_bbox (surface, path)) return CAIRO_STATUS_SUCCESS; return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); } static cairo_surface_t * _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, double width, double height) { cairo_pdf_surface_t *surface; cairo_status_t status, status_ignored; surface = malloc (sizeof (cairo_pdf_surface_t)); if (unlikely (surface == NULL)) { /* destroy stream on behalf of caller */ status = _cairo_output_stream_destroy (output); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); surface->output = output; surface->width = width; surface->height = height; cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *)); _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); if (unlikely (surface->all_surfaces == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL0; } _cairo_pdf_group_resources_init (&surface->resources); surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); if (! surface->font_subsets) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL1; } _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); surface->next_available_resource.id = 1; surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL2; } surface->pdf_version = CAIRO_PDF_VERSION_1_5; surface->compress_content = TRUE; surface->pdf_stream.active = FALSE; surface->pdf_stream.old_output = NULL; surface->group_stream.active = FALSE; surface->group_stream.stream = NULL; surface->group_stream.mem_stream = NULL; surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->select_pattern_gstate_saved = FALSE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; surface->header_emitted = FALSE; _cairo_surface_clipper_init (&surface->clipper, _cairo_pdf_surface_clipper_intersect_clip_path); _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, &surface->cairo_to_pdf, surface->font_subsets); _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, _cairo_pdf_surface_add_font, surface); _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, &cairo_pdf_surface_paginated_backend); status = surface->paginated_surface->status; if (status == CAIRO_STATUS_SUCCESS) { /* paginated keeps the only reference to surface now, drop ours */ cairo_surface_destroy (&surface->base); return surface->paginated_surface; } BAIL2: _cairo_scaled_font_subsets_destroy (surface->font_subsets); BAIL1: _cairo_hash_table_destroy (surface->all_surfaces); BAIL0: _cairo_array_fini (&surface->objects); free (surface); /* destroy stream on behalf of caller */ status_ignored = _cairo_output_stream_destroy (output); return _cairo_surface_create_in_error (status); } /** * cairo_pdf_surface_create_for_stream: * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL * to indicate a no-op @write_func. With a no-op @write_func, * the surface may be queried or used as a source without * generating any temporary files. * @closure: the closure argument for @write_func * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a PDF surface of the specified size in points to be written * incrementally to the stream represented by @write_func and @closure. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points) { cairo_output_stream_t *output; output = _cairo_output_stream_create (write_func, NULL, closure); if (_cairo_output_stream_get_status (output)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); return _cairo_pdf_surface_create_for_stream_internal (output, width_in_points, height_in_points); } /** * cairo_pdf_surface_create: * @filename: a filename for the PDF output (must be writable), %NULL may be * used to specify no output. This will generate a PDF surface that * may be queried and used as a source, without generating a * temporary file. * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a PDF surface of the specified size in points to be written * to @filename. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points) { cairo_output_stream_t *output; output = _cairo_output_stream_create_for_filename (filename); if (_cairo_output_stream_get_status (output)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); return _cairo_pdf_surface_create_for_stream_internal (output, width_in_points, height_in_points); } static cairo_bool_t _cairo_surface_is_pdf (cairo_surface_t *surface) { return surface->backend == &cairo_pdf_surface_backend; } /* If the abstract_surface is a paginated surface, and that paginated * surface's target is a pdf_surface, then set pdf_surface to that * target. Otherwise return FALSE. */ static cairo_bool_t _extract_pdf_surface (cairo_surface_t *surface, cairo_pdf_surface_t **pdf_surface) { cairo_surface_t *target; cairo_status_t status_ignored; if (surface->status) return FALSE; if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_paginated (surface)) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } target = _cairo_paginated_surface_get_target (surface); if (target->status) { status_ignored = _cairo_surface_set_error (surface, target->status); return FALSE; } if (target->finished) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_pdf (target)) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } *pdf_surface = (cairo_pdf_surface_t *) target; return TRUE; } /** * cairo_pdf_surface_restrict_to_version: * @surface: a PDF #cairo_surface_t * @version: PDF version * * Restricts the generated PDF file to @version. See cairo_pdf_get_versions() * for a list of available version values that can be used here. * * This function should only be called before any drawing operations * have been performed on the given surface. The simplest way to do * this is to call this function immediately after creating the * surface. * * Since: 1.10 **/ void cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface, cairo_pdf_version_t version) { cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */ if (! _extract_pdf_surface (abstract_surface, &surface)) return; if (version < CAIRO_PDF_VERSION_LAST) surface->pdf_version = version; _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, version >= CAIRO_PDF_VERSION_1_5); } /** * cairo_pdf_get_versions: * @versions: supported version list * @num_versions: list length * * Used to retrieve the list of supported versions. See * cairo_pdf_surface_restrict_to_version(). * * Since: 1.10 **/ void cairo_pdf_get_versions (cairo_pdf_version_t const **versions, int *num_versions) { if (versions != NULL) *versions = _cairo_pdf_versions; if (num_versions != NULL) *num_versions = CAIRO_PDF_VERSION_LAST; } /** * cairo_pdf_version_to_string: * @version: a version id * * Get the string representation of the given @version id. This function * will return %NULL if @version isn't valid. See cairo_pdf_get_versions() * for a way to get the list of valid version ids. * * Return value: the string associated to given version. * * Since: 1.10 **/ const char * cairo_pdf_version_to_string (cairo_pdf_version_t version) { if (version >= CAIRO_PDF_VERSION_LAST) return NULL; return _cairo_pdf_version_strings[version]; } /** * cairo_pdf_surface_set_size: * @surface: a PDF #cairo_surface_t * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) * * Changes the size of a PDF surface for the current (and * subsequent) pages. * * This function should only be called before any drawing operations * have been performed on the current page. The simplest way to do * this is to call this function immediately after creating the * surface or immediately after completing a page with either * cairo_show_page() or cairo_copy_page(). * * Since: 1.2 **/ void cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points) { cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ cairo_status_t status; if (! _extract_pdf_surface (surface, &pdf_surface)) return; _cairo_pdf_surface_set_size_internal (pdf_surface, width_in_points, height_in_points); status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface, width_in_points, height_in_points); if (status) status = _cairo_surface_set_error (surface, status); } static void _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) { int i, size; cairo_pdf_pattern_t *pattern; cairo_pdf_source_surface_t *src_surface; cairo_pdf_smask_group_t *group; size = _cairo_array_num_elements (&surface->page_patterns); for (i = 0; i < size; i++) { pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i); cairo_pattern_destroy (pattern->pattern); } _cairo_array_truncate (&surface->page_patterns, 0); size = _cairo_array_num_elements (&surface->page_surfaces); for (i = 0; i < size; i++) { src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); cairo_surface_destroy (src_surface->surface); } _cairo_array_truncate (&surface->page_surfaces, 0); size = _cairo_array_num_elements (&surface->smask_groups); for (i = 0; i < size; i++) { _cairo_array_copy_element (&surface->smask_groups, i, &group); _cairo_pdf_smask_group_destroy (group); } _cairo_array_truncate (&surface->smask_groups, 0); _cairo_array_truncate (&surface->knockout_group, 0); } static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) { int i; for (i = 0; i < CAIRO_NUM_OPERATORS; i++) res->operators[i] = FALSE; _cairo_array_init (&res->alphas, sizeof (double)); _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->shadings, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t)); } static void _cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) { _cairo_array_fini (&res->alphas); _cairo_array_fini (&res->smasks); _cairo_array_fini (&res->patterns); _cairo_array_fini (&res->shadings); _cairo_array_fini (&res->xobjects); _cairo_array_fini (&res->fonts); } static void _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) { int i; for (i = 0; i < CAIRO_NUM_OPERATORS; i++) res->operators[i] = FALSE; _cairo_array_truncate (&res->alphas, 0); _cairo_array_truncate (&res->smasks, 0); _cairo_array_truncate (&res->patterns, 0); _cairo_array_truncate (&res->shadings, 0); _cairo_array_truncate (&res->xobjects, 0); _cairo_array_truncate (&res->fonts, 0); } static void _cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, cairo_operator_t op) { cairo_pdf_group_resources_t *res = &surface->resources; res->operators[op] = TRUE; } static cairo_int_status_t _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, int *index) { int num_alphas, i; double other; cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_alphas = _cairo_array_num_elements (&res->alphas); for (i = 0; i < num_alphas; i++) { _cairo_array_copy_element (&res->alphas, i, &other); if (alpha == other) { *index = i; return CAIRO_STATUS_SUCCESS; } } status = _cairo_array_append (&res->alphas, &alpha); if (unlikely (status)) return status; *index = _cairo_array_num_elements (&res->alphas) - 1; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface, cairo_pdf_resource_t smask) { return _cairo_array_append (&(surface->resources.smasks), &smask); } static cairo_int_status_t _cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, cairo_pdf_resource_t pattern) { return _cairo_array_append (&(surface->resources.patterns), &pattern); } static cairo_int_status_t _cairo_pdf_surface_add_shading (cairo_pdf_surface_t *surface, cairo_pdf_resource_t shading) { return _cairo_array_append (&(surface->resources.shadings), &shading); } static cairo_int_status_t _cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, cairo_pdf_resource_t xobject) { return _cairo_array_append (&(surface->resources.xobjects), &xobject); } static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure) { cairo_pdf_surface_t *surface = closure; cairo_pdf_font_t font; int num_fonts, i; cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_fonts = _cairo_array_num_elements (&res->fonts); for (i = 0; i < num_fonts; i++) { _cairo_array_copy_element (&res->fonts, i, &font); if (font.font_id == font_id && font.subset_id == subset_id) return CAIRO_STATUS_SUCCESS; } num_fonts = _cairo_array_num_elements (&surface->fonts); for (i = 0; i < num_fonts; i++) { _cairo_array_copy_element (&surface->fonts, i, &font); if (font.font_id == font_id && font.subset_id == subset_id) return _cairo_array_append (&res->fonts, &font); } font.font_id = font_id; font.subset_id = subset_id; font.subset_resource = _cairo_pdf_surface_new_object (surface); if (font.subset_resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_array_append (&surface->fonts, &font); if (unlikely (status)) return status; return _cairo_array_append (&res->fonts, &font); } static cairo_pdf_resource_t _cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, unsigned int font_id, unsigned int subset_id) { cairo_pdf_font_t font; int num_fonts, i; num_fonts = _cairo_array_num_elements (&surface->fonts); for (i = 0; i < num_fonts; i++) { _cairo_array_copy_element (&surface->fonts, i, &font); if (font.font_id == font_id && font.subset_id == subset_id) return font.subset_resource; } font.subset_resource.id = 0; return font.subset_resource; } static const char * _cairo_operator_to_pdf_blend_mode (cairo_operator_t op) { switch (op) { /* The extend blend mode operators */ case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; case CAIRO_OPERATOR_SCREEN: return "Screen"; case CAIRO_OPERATOR_OVERLAY: return "Overlay"; case CAIRO_OPERATOR_DARKEN: return "Darken"; case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; case CAIRO_OPERATOR_HSL_HUE: return "Hue"; case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; case CAIRO_OPERATOR_HSL_COLOR: return "Color"; case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; default: /* The original Porter-Duff set */ case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: return "Normal"; } } static void _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, cairo_pdf_group_resources_t *res) { int num_alphas, num_smasks, num_resources, i; double alpha; cairo_pdf_resource_t *smask, *pattern, *shading, *xobject; cairo_pdf_font_t *font; _cairo_output_stream_printf (surface->output, "<<\n"); num_alphas = _cairo_array_num_elements (&res->alphas); num_smasks = _cairo_array_num_elements (&res->smasks); if (num_alphas > 0 || num_smasks > 0) { _cairo_output_stream_printf (surface->output, " /ExtGState <<\n"); for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { if (res->operators[i]) { _cairo_output_stream_printf (surface->output, " /b%d << /BM /%s >>\n", i, _cairo_operator_to_pdf_blend_mode(i)); } } for (i = 0; i < num_alphas; i++) { _cairo_array_copy_element (&res->alphas, i, &alpha); _cairo_output_stream_printf (surface->output, " /a%d << /CA %f /ca %f >>\n", i, alpha, alpha); } for (i = 0; i < num_smasks; i++) { smask = _cairo_array_index (&res->smasks, i); _cairo_output_stream_printf (surface->output, " /s%d %d 0 R\n", smask->id, smask->id); } _cairo_output_stream_printf (surface->output, " >>\n"); } num_resources = _cairo_array_num_elements (&res->patterns); if (num_resources > 0) { _cairo_output_stream_printf (surface->output, " /Pattern <<"); for (i = 0; i < num_resources; i++) { pattern = _cairo_array_index (&res->patterns, i); _cairo_output_stream_printf (surface->output, " /p%d %d 0 R", pattern->id, pattern->id); } _cairo_output_stream_printf (surface->output, " >>\n"); } num_resources = _cairo_array_num_elements (&res->shadings); if (num_resources > 0) { _cairo_output_stream_printf (surface->output, " /Shading <<"); for (i = 0; i < num_resources; i++) { shading = _cairo_array_index (&res->shadings, i); _cairo_output_stream_printf (surface->output, " /sh%d %d 0 R", shading->id, shading->id); } _cairo_output_stream_printf (surface->output, " >>\n"); } num_resources = _cairo_array_num_elements (&res->xobjects); if (num_resources > 0) { _cairo_output_stream_printf (surface->output, " /XObject <<"); for (i = 0; i < num_resources; i++) { xobject = _cairo_array_index (&res->xobjects, i); _cairo_output_stream_printf (surface->output, " /x%d %d 0 R", xobject->id, xobject->id); } _cairo_output_stream_printf (surface->output, " >>\n"); } num_resources = _cairo_array_num_elements (&res->fonts); if (num_resources > 0) { _cairo_output_stream_printf (surface->output," /Font <<\n"); for (i = 0; i < num_resources; i++) { font = _cairo_array_index (&res->fonts, i); _cairo_output_stream_printf (surface->output, " /f-%d-%d %d 0 R\n", font->font_id, font->subset_id, font->subset_resource.id); } _cairo_output_stream_printf (surface->output, " >>\n"); } _cairo_output_stream_printf (surface->output, ">>\n"); } static cairo_pdf_smask_group_t * _cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_pdf_smask_group_t *group; group = calloc (1, sizeof (cairo_pdf_smask_group_t)); if (unlikely (group == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } group->group_res = _cairo_pdf_surface_new_object (surface); if (group->group_res.id == 0) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (group); return NULL; } group->width = surface->width; group->height = surface->height; if (extents != NULL) { group->extents = *extents; } else { group->extents.x = 0; group->extents.y = 0; group->extents.width = surface->width; group->extents.height = surface->height; } group->extents = *extents; return group; } static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) { if (group->operation == PDF_FILL || group->operation == PDF_STROKE) _cairo_path_fixed_fini (&group->path); if (group->source) cairo_pattern_destroy (group->source); if (group->mask) cairo_pattern_destroy (group->mask); free (group->utf8); free (group->glyphs); free (group->clusters); if (group->scaled_font) cairo_scaled_font_destroy (group->scaled_font); free (group); } static cairo_int_status_t _cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { return _cairo_array_append (&surface->smask_groups, &group); } static cairo_bool_t _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) { const cairo_pdf_source_surface_entry_t *a = key_a; const cairo_pdf_source_surface_entry_t *b = key_b; if (a->interpolate != b->interpolate) return FALSE; if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0); return (a->id == b->id); } static void _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) { if (key->unique_id && key->unique_id_length > 0) { key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, key->unique_id, key->unique_id_length); } else { key->base.hash = key->id; } } static cairo_int_status_t _cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_image_surface_t **image, void **image_extra) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra); } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { cairo_surface_t *surf; surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL); if (!surf) return CAIRO_INT_STATUS_UNSUPPORTED; assert (_cairo_surface_is_image (surf)); *image = (cairo_image_surface_t *) surf; } break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: default: ASSERT_NOT_REACHED; break; } return CAIRO_STATUS_SUCCESS; } static void _cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_image_surface_t *image, void *image_extra) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: _cairo_raster_source_pattern_release (pattern, &image->base); break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: default: ASSERT_NOT_REACHED; break; } } static cairo_int_status_t _get_jpx_image_info (cairo_surface_t *source, cairo_image_info_t *info, const unsigned char **mime_data, unsigned long *mime_data_length) { cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, mime_data, mime_data_length); if (*mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); } static cairo_int_status_t _get_jpeg_image_info (cairo_surface_t *source, cairo_image_info_t *info, const unsigned char **mime_data, unsigned long *mime_data_length) { cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, mime_data, mime_data_length); if (*mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); } static cairo_int_status_t _get_source_surface_size (cairo_surface_t *source, int *width, int *height, cairo_rectangle_int_t *extents) { cairo_int_status_t status; cairo_image_info_t info; const unsigned char *mime_data; unsigned long mime_data_length; if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; *extents = sub->extents; *width = extents->width; *height = extents->height; } else { cairo_surface_t *free_me = NULL; cairo_rectangle_int_t surf_extents; cairo_box_t box; cairo_bool_t bounded; if (_cairo_surface_is_snapshot (source)) free_me = source = _cairo_surface_snapshot_get_target (source); status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source, &box, NULL); if (unlikely (status)) { cairo_surface_destroy (free_me); return status; } bounded = _cairo_surface_get_extents (source, &surf_extents); cairo_surface_destroy (free_me); *width = surf_extents.width; *height = surf_extents.height; _cairo_box_round_to_rectangle (&box, extents); } return CAIRO_STATUS_SUCCESS; } extents->x = 0; extents->y = 0; status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *width = info.width; *height = info.height; extents->width = info.width; extents->height = info.height; return status; } status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *width = info.width; *height = info.height; extents->width = info.width; extents->height = info.height; return status; } if (! _cairo_surface_get_extents (source, extents)) return CAIRO_INT_STATUS_UNSUPPORTED; *width = extents->width; *height = extents->height; return CAIRO_STATUS_SUCCESS; } /** * _cairo_pdf_surface_add_source_surface: * @surface: the pdf surface * @source_surface: A #cairo_surface_t to use as the source surface * @source_pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source * @filter: filter type of the source pattern * @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask * @extents: extents of the operation that is using this source * @surface_res: return PDF resource number of the surface * @width: returns width of surface * @height: returns height of surface * @x_offset: x offset of surface * @t_offset: y offset of surface * @source_extents: returns extents of source (either ink extents or extents needed to cover @extents) * * Add surface or raster_source pattern to list of surfaces to be * written to the PDF file when the current page is finished. Returns * a PDF resource to reference the image. A hash table of all images * in the PDF files (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or surface * unique_id) to ensure surfaces with the same id are only written * once to the PDF file. * * Only one of @source_pattern or @source_surface is to be * specified. Set the other to NULL. **/ static cairo_int_status_t _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, cairo_surface_t *source_surface, const cairo_pattern_t *source_pattern, cairo_filter_t filter, cairo_bool_t stencil_mask, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *surface_res, int *width, int *height, double *x_offset, double *y_offset, cairo_rectangle_int_t *source_extents) { cairo_pdf_source_surface_t src_surface; cairo_pdf_source_surface_entry_t surface_key; cairo_pdf_source_surface_entry_t *surface_entry; cairo_int_status_t status; cairo_bool_t interpolate; unsigned char *unique_id = NULL; unsigned long unique_id_length = 0; cairo_image_surface_t *image; void *image_extra; switch (filter) { default: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: interpolate = TRUE; break; case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: interpolate = FALSE; break; } *x_offset = 0; *y_offset = 0; if (source_pattern) { if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern, &image, &image_extra); if (unlikely (status)) return status; source_surface = &image->base; cairo_surface_get_device_offset (source_surface, x_offset, y_offset); } else { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern; source_surface = surface_pattern->surface; } } surface_key.id = source_surface->unique_id; surface_key.interpolate = interpolate; cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID, (const unsigned char **) &surface_key.unique_id, &surface_key.unique_id_length); _cairo_pdf_source_surface_init_key (&surface_key); surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); if (surface_entry) { *surface_res = surface_entry->surface_res; *width = surface_entry->width; *height = surface_entry->height; *source_extents = surface_entry->extents; status = CAIRO_STATUS_SUCCESS; } else { status = _get_source_surface_size (source_surface, width, height, source_extents); if (unlikely(status)) goto release_source; if (surface_key.unique_id && surface_key.unique_id_length > 0) { unique_id = _cairo_malloc (surface_key.unique_id_length); if (unique_id == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto release_source; } unique_id_length = surface_key.unique_id_length; memcpy (unique_id, surface_key.unique_id, unique_id_length); } else { unique_id = NULL; unique_id_length = 0; } } release_source: if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); if (status || surface_entry) return status; surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); if (surface_entry == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } surface_entry->id = surface_key.id; surface_entry->interpolate = interpolate; surface_entry->stencil_mask = stencil_mask; surface_entry->unique_id_length = unique_id_length; surface_entry->unique_id = unique_id; surface_entry->width = *width; surface_entry->height = *height; surface_entry->extents = *source_extents; _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE; src_surface.surface = NULL; status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern); if (unlikely (status)) goto fail2; } else { src_surface.type = CAIRO_PATTERN_TYPE_SURFACE; src_surface.surface = cairo_surface_reference (source_surface); src_surface.raster_pattern = NULL; } surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); if (surface_entry->surface_res.id == 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } status = _cairo_array_append (&surface->page_surfaces, &src_surface); if (unlikely (status)) goto fail3; status = _cairo_hash_table_insert (surface->all_surfaces, &surface_entry->base); if (unlikely(status)) goto fail3; *surface_res = surface_entry->surface_res; return status; fail3: if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) cairo_pattern_destroy (src_surface.raster_pattern); else cairo_surface_destroy (src_surface.surface); fail2: free (surface_entry); fail1: free (unique_id); return status; } static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_bool_t is_shading, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { cairo_pdf_pattern_t pdf_pattern; cairo_int_status_t status; pdf_pattern.is_shading = is_shading; /* Solid colors are emitted into the content stream */ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { pattern_res->id = 0; gstate_res->id = 0; return CAIRO_INT_STATUS_SUCCESS; } status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); if (unlikely (status)) return status; pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); if (pdf_pattern.pattern_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pdf_pattern.gstate_res.id = 0; /* gradient patterns require an smask object to implement transparency */ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL || pattern->type == CAIRO_PATTERN_TYPE_MESH) { double min_alpha; _cairo_pattern_alpha_range (pattern, &min_alpha, NULL); if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) { pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); if (pdf_pattern.gstate_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } } pdf_pattern.width = surface->width; pdf_pattern.height = surface->height; if (extents != NULL) { pdf_pattern.extents = *extents; } else { pdf_pattern.extents.x = 0; pdf_pattern.extents.y = 0; pdf_pattern.extents.width = surface->width; pdf_pattern.extents.height = surface->height; } *pattern_res = pdf_pattern.pattern_res; *gstate_res = pdf_pattern.gstate_res; status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); if (unlikely (status)) { cairo_pattern_destroy (pdf_pattern.pattern); return status; } return CAIRO_INT_STATUS_SUCCESS; } /* Get BBox in PDF coordinates from extents in cairo coordinates */ static void _get_bbox_from_extents (double surface_height, const cairo_rectangle_int_t *extents, cairo_box_double_t *bbox) { bbox->p1.x = extents->x; bbox->p1.y = surface_height - (extents->y + extents->height); bbox->p2.x = extents->x + extents->width; bbox->p2.y = surface_height - extents->y; } static cairo_int_status_t _cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *shading_res, cairo_pdf_resource_t *gstate_res) { return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, extents, TRUE, shading_res, gstate_res); } static cairo_int_status_t _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *pattern_res, cairo_pdf_resource_t *gstate_res) { return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, pattern, extents, FALSE, pattern_res, gstate_res); } static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, const char *fmt, ...) { va_list ap; cairo_pdf_resource_t self, length; cairo_output_stream_t *output = NULL; if (resource) { self = *resource; _cairo_pdf_surface_update_object (surface, self); } else { self = _cairo_pdf_surface_new_object (surface); if (self.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } length = _cairo_pdf_surface_new_object (surface); if (length.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (compressed) { output = _cairo_deflate_stream_create (surface->output); if (_cairo_output_stream_get_status (output)) return _cairo_output_stream_destroy (output); } surface->pdf_stream.active = TRUE; surface->pdf_stream.self = self; surface->pdf_stream.length = length; surface->pdf_stream.compressed = compressed; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Length %d 0 R\n", surface->pdf_stream.self.id, surface->pdf_stream.length.id); if (compressed) _cairo_output_stream_printf (surface->output, " /Filter /FlateDecode\n"); if (fmt != NULL) { va_start (ap, fmt); _cairo_output_stream_vprintf (surface->output, fmt, ap); va_end (ap); } _cairo_output_stream_printf (surface->output, ">>\n" "stream\n"); surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output); if (compressed) { assert (surface->pdf_stream.old_output == NULL); surface->pdf_stream.old_output = surface->output; surface->output = output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); } return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) { cairo_int_status_t status; long length; if (! surface->pdf_stream.active) return CAIRO_INT_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (surface->pdf_stream.compressed) { cairo_int_status_t status2; status2 = _cairo_output_stream_destroy (surface->output); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = status2; surface->output = surface->pdf_stream.old_output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); surface->pdf_stream.old_output = NULL; } length = _cairo_output_stream_get_position (surface->output) - surface->pdf_stream.start_offset; _cairo_output_stream_printf (surface->output, "\n" "endstream\n" "endobj\n"); _cairo_pdf_surface_update_object (surface, surface->pdf_stream.length); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" " %ld\n" "endobj\n", surface->pdf_stream.length.id, length); surface->pdf_stream.active = FALSE; if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = _cairo_output_stream_get_status (surface->output); return status; } static void _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, cairo_output_stream_t *mem_stream, cairo_pdf_resource_t resource, cairo_pdf_group_resources_t *resources, cairo_bool_t is_knockout_group, const cairo_box_double_t *bbox) { _cairo_pdf_surface_update_object (surface, resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /XObject\n" " /Length %d\n", resource.id, _cairo_memory_stream_length (mem_stream)); if (surface->compress_content) { _cairo_output_stream_printf (surface->output, " /Filter /FlateDecode\n"); } _cairo_output_stream_printf (surface->output, " /Subtype /Form\n" " /BBox [ %f %f %f %f ]\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" " /I true\n" " /CS /DeviceRGB\n", bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y); if (is_knockout_group) _cairo_output_stream_printf (surface->output, " /K true\n"); _cairo_output_stream_printf (surface->output, " >>\n" " /Resources\n"); _cairo_pdf_surface_emit_group_resources (surface, resources); _cairo_output_stream_printf (surface->output, ">>\n" "stream\n"); _cairo_memory_stream_copy (mem_stream, surface->output); _cairo_output_stream_printf (surface->output, "endstream\n" "endobj\n"); } static cairo_int_status_t _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource) { cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); surface->group_stream.active = TRUE; surface->current_pattern_is_solid_color = FALSE; surface->current_operator = CAIRO_OPERATOR_OVER; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->group_stream.mem_stream = _cairo_memory_stream_create (); if (surface->compress_content) { surface->group_stream.stream = _cairo_deflate_stream_create (surface->group_stream.mem_stream); } else { surface->group_stream.stream = surface->group_stream.mem_stream; } status = _cairo_output_stream_get_status (surface->group_stream.stream); surface->group_stream.old_output = surface->output; surface->output = surface->group_stream.stream; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); _cairo_pdf_group_resources_clear (&surface->resources); if (resource) { surface->group_stream.resource = *resource; } else { surface->group_stream.resource = _cairo_pdf_surface_new_object (surface); if (surface->group_stream.resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } surface->group_stream.is_knockout = FALSE; surface->group_stream.bbox = *bbox; return status; } static cairo_int_status_t _cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox) { cairo_int_status_t status; status = _cairo_pdf_surface_open_group (surface, bbox, NULL); if (unlikely (status)) return status; surface->group_stream.is_knockout = TRUE; return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *group) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == TRUE); status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; if (surface->compress_content) { status = _cairo_output_stream_destroy (surface->group_stream.stream); surface->group_stream.stream = NULL; _cairo_output_stream_printf (surface->group_stream.mem_stream, "\n"); } surface->output = surface->group_stream.old_output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); surface->group_stream.active = FALSE; _cairo_pdf_surface_write_memory_stream (surface, surface->group_stream.mem_stream, surface->group_stream.resource, &surface->resources, surface->group_stream.is_knockout, &surface->group_stream.bbox); if (group) *group = surface->group_stream.resource; status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; surface->group_stream.mem_stream = NULL; surface->group_stream.stream = NULL; return status; } static cairo_int_status_t _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource, cairo_bool_t is_form) { cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); surface->content_resources = _cairo_pdf_surface_new_object (surface); if (surface->content_resources.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (is_form) { assert (bbox != NULL); status = _cairo_pdf_surface_open_stream (surface, resource, surface->compress_content, " /Type /XObject\n" " /Subtype /Form\n" " /BBox [ %f %f %f %f ]\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" " /I true\n" " /CS /DeviceRGB\n" " >>\n" " /Resources %d 0 R\n", bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y, surface->content_resources.id); } else { status = _cairo_pdf_surface_open_stream (surface, resource, surface->compress_content, NULL); } if (unlikely (status)) return status; surface->content = surface->pdf_stream.self; _cairo_output_stream_printf (surface->output, "q\n"); return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) { cairo_int_status_t status; assert (surface->pdf_stream.active == TRUE); assert (surface->group_stream.active == FALSE); status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) return status; _cairo_pdf_surface_update_object (surface, surface->content_resources); _cairo_output_stream_printf (surface->output, "%d 0 obj\n", surface->content_resources.id); _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); _cairo_output_stream_printf (surface->output, "endobj\n"); return _cairo_output_stream_get_status (surface->output); } static void _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) { cairo_pdf_source_surface_entry_t *surface_entry = entry; cairo_hash_table_t *patterns = closure; _cairo_hash_table_remove (patterns, &surface_entry->base); free (surface_entry->unique_id); free (surface_entry); } static cairo_status_t _cairo_pdf_surface_finish (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; long offset; cairo_pdf_resource_t info, catalog; cairo_status_t status, status2; status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) status = _cairo_pdf_surface_emit_font_subsets (surface); _cairo_pdf_surface_write_pages (surface); info = _cairo_pdf_surface_write_info (surface); if (info.id == 0 && status == CAIRO_STATUS_SUCCESS) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); catalog = _cairo_pdf_surface_write_catalog (surface); if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); offset = _cairo_pdf_surface_write_xref (surface); _cairo_output_stream_printf (surface->output, "trailer\n" "<< /Size %d\n" " /Root %d 0 R\n" " /Info %d 0 R\n" ">>\n", surface->next_available_resource.id, catalog.id, info.id); _cairo_output_stream_printf (surface->output, "startxref\n" "%ld\n" "%%%%EOF\n", offset); /* pdf_operators has already been flushed when the last stream was * closed so we should never be writing anything here - however, * the stream may itself be in an error state. */ status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); if (status == CAIRO_STATUS_SUCCESS) status = status2; /* close any active streams still open due to fatal errors */ status2 = _cairo_pdf_surface_close_stream (surface); if (status == CAIRO_STATUS_SUCCESS) status = status2; if (surface->group_stream.stream != NULL) { status2 = _cairo_output_stream_destroy (surface->group_stream.stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; } if (surface->group_stream.mem_stream != NULL) { status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; } if (surface->pdf_stream.active) surface->output = surface->pdf_stream.old_output; if (surface->group_stream.active) surface->output = surface->group_stream.old_output; /* and finish the pdf surface */ status2 = _cairo_output_stream_destroy (surface->output); if (status == CAIRO_STATUS_SUCCESS) status = status2; _cairo_pdf_surface_clear (surface); _cairo_pdf_group_resources_fini (&surface->resources); _cairo_array_fini (&surface->objects); _cairo_array_fini (&surface->pages); _cairo_array_fini (&surface->rgb_linear_functions); _cairo_array_fini (&surface->alpha_linear_functions); _cairo_array_fini (&surface->page_patterns); _cairo_array_fini (&surface->page_surfaces); _cairo_hash_table_foreach (surface->all_surfaces, _cairo_pdf_source_surface_entry_pluck, surface->all_surfaces); _cairo_hash_table_destroy (surface->all_surfaces); _cairo_array_fini (&surface->smask_groups); _cairo_array_fini (&surface->fonts); _cairo_array_fini (&surface->knockout_group); if (surface->font_subsets) { _cairo_scaled_font_subsets_destroy (surface->font_subsets); surface->font_subsets = NULL; } _cairo_surface_clipper_reset (&surface->clipper); return status; } static cairo_int_status_t _cairo_pdf_surface_start_page (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; /* Document header */ if (! surface->header_emitted) { const char *version; switch (surface->pdf_version) { case CAIRO_PDF_VERSION_1_4: version = "1.4"; break; default: case CAIRO_PDF_VERSION_1_5: version = "1.5"; break; } _cairo_output_stream_printf (surface->output, "%%PDF-%s\n", version); _cairo_output_stream_printf (surface->output, "%%%c%c%c%c\n", 181, 237, 174, 251); surface->header_emitted = TRUE; } _cairo_pdf_group_resources_clear (&surface->resources); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_bool_t has_fallbacks) { cairo_int_status_t status; cairo_pdf_surface_t *surface = abstract_surface; cairo_box_double_t bbox; surface->has_fallback_images = has_fallbacks; bbox.p1.x = 0; bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) { return TRUE; } static cairo_int_status_t _cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, cairo_pdf_resource_t *surface_res, int *width, int *height, double *x_offset, double *y_offset) { cairo_image_surface_t *image; cairo_surface_t *pad_image; void *image_extra; cairo_int_status_t status; int w, h; cairo_rectangle_int_t extents2; cairo_box_t box; cairo_rectangle_int_t rect; cairo_surface_pattern_t pad_pattern; status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, &image, &image_extra); if (unlikely (status)) return status; pad_image = &image->base; /* get the operation extents in pattern space */ _cairo_box_from_rectangle (&box, extents); _cairo_matrix_transform_bounding_box_fixed (&source->matrix, &box, NULL); _cairo_box_round_to_rectangle (&box, &rect); /* Check if image needs padding to fill extents */ w = image->width; h = image->height; if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || _cairo_fixed_integer_ceil(box.p1.y) < 0 || _cairo_fixed_integer_floor(box.p2.x) > w || _cairo_fixed_integer_floor(box.p2.y) > h) { pad_image = _cairo_image_surface_create_with_content (image->base.content, rect.width, rect.height); if (pad_image->status) { status = pad_image->status; goto BAIL; } _cairo_pattern_init_for_surface (&pad_pattern, &image->base); cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y); pad_pattern.base.extend = CAIRO_EXTEND_PAD; status = _cairo_surface_paint (pad_image, CAIRO_OPERATOR_SOURCE, &pad_pattern.base, NULL); _cairo_pattern_fini (&pad_pattern.base); if (unlikely (status)) goto BAIL; } status = _cairo_pdf_surface_add_source_surface (surface, pad_image, NULL, source->filter, FALSE, extents, surface_res, width, height, x_offset, y_offset, &extents2); if (unlikely (status)) goto BAIL; if (pad_image != &image->base) { /* If using a padded image, replace _add_source_surface * x/y_offset with padded image offset. Note: * _add_source_surface only sets a non zero x/y_offset for * RASTER_SOURCE patterns. _add_source_surface will always set * x/y_offset to 0 for surfaces so we can ignore the returned * offset and replace it with the offset required for the * padded image */ *x_offset = rect.x; *y_offset = rect.y; } BAIL: if (pad_image != &image->base) cairo_surface_destroy (pad_image); _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); return status; } /* Emit alpha channel from the image into the given data, providing * an id that can be used to reference the resulting SMask object. * * In the case that the alpha channel happens to be all opaque, then * no SMask object will be emitted and *id_ret will be set to 0. * * When stencil_mask is TRUE, stream_res is an an input specifying the * resource to use. When stencil_mask is FALSE, a new resource will be * created and returned in stream_res. */ static cairo_int_status_t _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, cairo_image_surface_t *image, cairo_bool_t stencil_mask, const char *interpolate, cairo_pdf_resource_t *stream_res) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; char *alpha; unsigned long alpha_size; uint32_t *pixel32; uint8_t *pixel8; int i, x, y, bit, a; cairo_image_transparency_t transparency; /* This is the only image format we support, which simplifies things. */ assert (image->format == CAIRO_FORMAT_ARGB32 || image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1 ); transparency = _cairo_image_analyze_transparency (image); if (stencil_mask) { assert (transparency == CAIRO_IMAGE_IS_OPAQUE || transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA); } else { if (transparency == CAIRO_IMAGE_IS_OPAQUE) return status; } if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA) { alpha_size = (image->width + 7) / 8 * image->height; alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); } else { alpha_size = image->height * image->width; alpha = _cairo_malloc_ab (image->height, image->width); } if (unlikely (alpha == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } i = 0; for (y = 0; y < image->height; y++) { if (image->format == CAIRO_FORMAT_A1) { pixel8 = (uint8_t *) (image->data + y * image->stride); for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { a = *pixel8; a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); alpha[i++] = a; } } else { pixel8 = (uint8_t *) (image->data + y * image->stride); pixel32 = (uint32_t *) (image->data + y * image->stride); bit = 7; for (x = 0; x < image->width; x++) { if (image->format == CAIRO_FORMAT_ARGB32) { a = (*pixel32 & 0xff000000) >> 24; pixel32++; } else { a = *pixel8; pixel8++; } if (transparency == CAIRO_IMAGE_HAS_ALPHA) { alpha[i++] = a; } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ if (bit == 7) alpha[i] = 0; if (a != 0) alpha[i] |= (1 << bit); bit--; if (bit < 0) { bit = 7; i++; } } } if (bit != 7) i++; } } if (stencil_mask) { status = _cairo_pdf_surface_open_stream (surface, stream_res, TRUE, " /Type /XObject\n" " /Subtype /Image\n" " /ImageMask true\n" " /Width %d\n" " /Height %d\n" " /Interpolate %s\n" " /BitsPerComponent 1\n" " /Decode [1 0]\n", image->width, image->height, interpolate); } else { stream_res->id = 0; status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, " /Type /XObject\n" " /Subtype /Image\n" " /Width %d\n" " /Height %d\n" " /ColorSpace /DeviceGray\n" " /Interpolate %s\n" " /BitsPerComponent %d\n", image->width, image->height, interpolate, transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1); } if (unlikely (status)) goto CLEANUP_ALPHA; if (!stencil_mask) *stream_res = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, alpha, alpha_size); status = _cairo_pdf_surface_close_stream (surface); CLEANUP_ALPHA: free (alpha); CLEANUP: return status; } /* Emit image data into the given surface, providing a resource that * can be used to reference the data in image_ret. */ static cairo_int_status_t _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, cairo_image_surface_t *image_surf, cairo_pdf_resource_t *image_res, cairo_filter_t filter, cairo_bool_t stencil_mask) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; char *data; unsigned long data_size; uint32_t *pixel; int i, x, y, bit; cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ cairo_bool_t need_smask; const char *interpolate = "true"; cairo_image_color_t color; cairo_image_surface_t *image; image = image_surf; if (image->format != CAIRO_FORMAT_RGB24 && image->format != CAIRO_FORMAT_ARGB32 && image->format != CAIRO_FORMAT_A8 && image->format != CAIRO_FORMAT_A1) { cairo_surface_t *surf; cairo_surface_pattern_t pattern; surf = _cairo_image_surface_create_with_content (image_surf->base.content, image_surf->width, image_surf->height); image = (cairo_image_surface_t *) surf; if (surf->status) { status = surf->status; goto CLEANUP; } _cairo_pattern_init_for_surface (&pattern, &image_surf->base); status = _cairo_surface_paint (surf, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto CLEANUP; } switch (filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: interpolate = "true"; break; case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: interpolate = "false"; break; } if (stencil_mask) return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res); color = _cairo_image_analyze_color (image); switch (color) { default: case CAIRO_IMAGE_UNKNOWN_COLOR: ASSERT_NOT_REACHED; case CAIRO_IMAGE_IS_COLOR: data_size = image->height * image->width * 3; data = _cairo_malloc_abc (image->width, image->height, 3); break; case CAIRO_IMAGE_IS_GRAYSCALE: data_size = image->height * image->width; data = _cairo_malloc_ab (image->width, image->height); break; case CAIRO_IMAGE_IS_MONOCHROME: data_size = (image->width + 7) / 8 * image->height; data = _cairo_malloc_ab ((image->width+7) / 8, image->height); break; } if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } i = 0; for (y = 0; y < image->height; y++) { pixel = (uint32_t *) (image->data + y * image->stride); bit = 7; for (x = 0; x < image->width; x++, pixel++) { int r, g, b; /* XXX: We're un-premultiplying alpha here. My reading of the PDF * specification suggests that we should be able to avoid having * to do this by filling in the SMask's Matte dictionary * appropriately, but my attempts to do that so far have * failed. */ if (image->format == CAIRO_FORMAT_ARGB32) { uint8_t a; a = (*pixel & 0xff000000) >> 24; if (a == 0) { r = g = b = 0; } else { r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; g = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; b = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; } } else if (image->format == CAIRO_FORMAT_RGB24) { r = (*pixel & 0x00ff0000) >> 16; g = (*pixel & 0x0000ff00) >> 8; b = (*pixel & 0x000000ff) >> 0; } else { r = g = b = 0; } switch (color) { case CAIRO_IMAGE_IS_COLOR: case CAIRO_IMAGE_UNKNOWN_COLOR: data[i++] = r; data[i++] = g; data[i++] = b; break; case CAIRO_IMAGE_IS_GRAYSCALE: data[i++] = r; break; case CAIRO_IMAGE_IS_MONOCHROME: if (bit == 7) data[i] = 0; if (r != 0) data[i] |= (1 << bit); bit--; if (bit < 0) { bit = 7; i++; } break; } } if (bit != 7) i++; } need_smask = FALSE; if (image->format == CAIRO_FORMAT_ARGB32 || image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1) { status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, interpolate, &smask); if (unlikely (status)) goto CLEANUP_RGB; if (smask.id) need_smask = TRUE; } #define IMAGE_DICTIONARY " /Type /XObject\n" \ " /Subtype /Image\n" \ " /Width %d\n" \ " /Height %d\n" \ " /ColorSpace %s\n" \ " /Interpolate %s\n" \ " /BitsPerComponent %d\n" if (need_smask) status = _cairo_pdf_surface_open_stream (surface, image_res, TRUE, IMAGE_DICTIONARY " /SMask %d 0 R\n", image->width, image->height, color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", interpolate, color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8, smask.id); else status = _cairo_pdf_surface_open_stream (surface, image_res, TRUE, IMAGE_DICTIONARY, image->width, image->height, color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", interpolate, color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8); if (unlikely (status)) goto CLEANUP_RGB; #undef IMAGE_DICTIONARY _cairo_output_stream_write (surface->output, data, data_size); status = _cairo_pdf_surface_close_stream (surface); CLEANUP_RGB: free (data); CLEANUP: if (image != image_surf) cairo_surface_destroy (&image->base); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, cairo_surface_t *source, cairo_pdf_resource_t res) { cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); if (status) return status; status = _cairo_pdf_surface_open_stream (surface, &res, FALSE, " /Type /XObject\n" " /Subtype /Image\n" " /Width %d\n" " /Height %d\n" " /Filter /JPXDecode\n", info.width, info.height); if (status) return status; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); status = _cairo_pdf_surface_close_stream (surface); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, cairo_surface_t *source, cairo_pdf_resource_t res) { cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; const char *colorspace; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (unlikely (source->status)) return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); if (unlikely (status)) return status; switch (info.num_components) { case 1: colorspace = "/DeviceGray"; break; case 3: colorspace = "/DeviceRGB"; break; case 4: colorspace = "/DeviceCMYK"; break; default: return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_pdf_surface_open_stream (surface, &res, FALSE, " /Type /XObject\n" " /Subtype /Image\n" " /Width %d\n" " /Height %d\n" " /ColorSpace %s\n" " /BitsPerComponent %d\n" " /Filter /DCTDecode\n", info.width, info.height, colorspace, info.bits_per_component); if (unlikely (status)) return status; _cairo_output_stream_write (surface->output, mime_data, mime_data_length); status = _cairo_pdf_surface_close_stream (surface); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *source) { cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra); } else { status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source->raster_pattern, &image, &image_extra); } if (unlikely (status)) return status; if (!source->hash_entry->stencil_mask) { status = _cairo_pdf_surface_emit_jpx_image (surface, &image->base, source->hash_entry->surface_res); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto release_source; status = _cairo_pdf_surface_emit_jpeg_image (surface, &image->base, source->hash_entry->surface_res); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto release_source; } status = _cairo_pdf_surface_emit_image (surface, image, &source->hash_entry->surface_res, source->hash_entry->interpolate, source->hash_entry->stencil_mask); release_source: if (source->type == CAIRO_PATTERN_TYPE_SURFACE) _cairo_surface_release_source_image (source->surface, image, image_extra); else _cairo_pdf_surface_release_source_image_from_pattern (surface, source->raster_pattern, image, image_extra); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *pdf_source) { double old_width, old_height; cairo_paginated_mode_t old_paginated_mode; cairo_surface_clipper_t old_clipper; cairo_box_double_t bbox; cairo_int_status_t status; int alpha = 0; cairo_surface_t *free_me = NULL; cairo_surface_t *source; const cairo_rectangle_int_t *extents; int width; int height; cairo_bool_t is_subsurface; assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); extents = &pdf_source->hash_entry->extents; width = pdf_source->hash_entry->width; height = pdf_source->hash_entry->height; is_subsurface = FALSE; source = pdf_source->surface; if (_cairo_surface_is_snapshot (source)) { free_me = source = _cairo_surface_snapshot_get_target (source); } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; source = sub->target; extents = &sub->extents; width = extents->width; height = extents->height; is_subsurface = TRUE; } old_width = surface->width; old_height = surface->height; old_paginated_mode = surface->paginated_mode; old_clipper = surface->clipper; _cairo_surface_clipper_init (&surface->clipper, _cairo_pdf_surface_clipper_intersect_clip_path); _cairo_pdf_surface_set_size_internal (surface, width, height); /* Patterns are emitted after fallback images. The paginated mode * needs to be set to _RENDER while the recording surface is replayed * back to this surface. */ surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; _cairo_pdf_group_resources_clear (&surface->resources); _get_bbox_from_extents (height, extents, &bbox); status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE); if (unlikely (status)) goto err; if (source->content == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) goto err; _cairo_output_stream_printf (surface->output, "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", alpha, surface->width, surface->height); } status = _cairo_recording_surface_replay_region (source, is_subsurface ? extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) goto err; status = _cairo_pdf_surface_close_content_stream (surface); _cairo_surface_clipper_reset (&surface->clipper); surface->clipper = old_clipper; _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); surface->paginated_mode = old_paginated_mode; err: cairo_surface_destroy (free_me); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, cairo_pdf_source_surface_t *src_surface) { if (src_surface->type == CAIRO_PATTERN_TYPE_SURFACE && src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return _cairo_pdf_surface_emit_recording_surface (surface, src_surface); return _cairo_pdf_surface_emit_image_surface (surface, src_surface); } static cairo_int_status_t _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_pattern_t *pattern = pdf_pattern->pattern; cairo_int_status_t status; cairo_pdf_resource_t pattern_resource = {0}; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_extend_t extend = cairo_pattern_get_extend (pattern); double xstep, ystep; cairo_rectangle_int_t pattern_extents; int pattern_width = 0; /* squelch bogus compiler warning */ int pattern_height = 0; /* squelch bogus compiler warning */ double x_offset; double y_offset; char draw_surface[200]; cairo_box_double_t bbox; if (pattern->extend == CAIRO_EXTEND_PAD) { status = _cairo_pdf_surface_add_padded_image_surface (surface, pattern, &pdf_pattern->extents, &pattern_resource, &pattern_width, &pattern_height, &x_offset, &y_offset); pattern_extents.x = 0; pattern_extents.y = 0; pattern_extents.width = pattern_width; pattern_extents.height = pattern_height; } else { status = _cairo_pdf_surface_add_source_surface (surface, NULL, pattern, pattern->filter, FALSE, &pdf_pattern->extents, &pattern_resource, &pattern_width, &pattern_height, &x_offset, &y_offset, &pattern_extents); } if (unlikely (status)) return status; switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { /* In PS/PDF, (as far as I can tell), all patterns are * repeating. So we support cairo's EXTEND_NONE semantics * by setting the repeat step size to a size large enough * to guarantee that no more than a single occurrence will * be visible. * * First, map the surface extents into pattern space (since * xstep and ystep are in pattern space). Then use an upper * bound on the length of the diagonal of the pattern image * and the surface as repeat size. This guarantees to never * repeat visibly. */ double x1 = 0.0, y1 = 0.0; double x2 = surface->width, y2 = surface->height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); /* Rather than computing precise bounds of the union, just * add the surface extents unconditionally. We only * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + pattern_width + pattern_height); } break; case CAIRO_EXTEND_REPEAT: xstep = pattern_width; ystep = pattern_height; break; case CAIRO_EXTEND_REFLECT: pattern_extents.x = 0; pattern_extents.y = 0; pattern_extents.width = pattern_width*2; pattern_extents.height = pattern_height*2; xstep = pattern_width*2; ystep = pattern_height*2; break; /* All the rest (if any) should have been analyzed away, so this * case should be unreachable. */ default: ASSERT_NOT_REACHED; xstep = 0; ystep = 0; } /* At this point, (that is, within the surface backend interface), * the pattern's matrix maps from cairo's device space to cairo's * pattern space, (both with their origin at the upper-left, and * cairo's pattern space of size width,height). * * Then, we must emit a PDF pattern object that maps from its own * pattern space, (which has a size that we establish in the BBox * dictionary entry), to the PDF page's *initial* space, (which * does not benefit from the Y-axis flipping matrix that we emit * on each page). So the PDF patterns matrix maps from a * (width,height) pattern space to a device space with the origin * in the lower-left corner. * * So to handle all of that, we start with an identity matrix for * the PDF pattern to device matrix. We translate it up by the * image height then flip it in the Y direction, (moving us from * the PDF origin to cairo's origin). We then multiply in the * inverse of the cairo pattern matrix, (since it maps from device * to pattern, while we're setting up pattern to device). Finally, * we translate back down by the image height and flip again to * end up at the lower-left origin that PDF expects. * * Additionally, within the stream that paints the pattern itself, * we are using a PDF image object that has a size of (1,1) so we * have to scale it up by the image width and height to fill our * pattern cell. */ cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); cairo_matrix_translate (&pdf_p2d, -x_offset, -y_offset); cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); _get_bbox_from_extents (pattern_height, &pattern_extents, &bbox); _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); status = _cairo_pdf_surface_open_stream (surface, &pdf_pattern->pattern_res, FALSE, " /PatternType 1\n" " /BBox [ %f %f %f %f ]\n" " /XStep %f\n" " /YStep %f\n" " /TilingType 1\n" " /PaintType 1\n" " /Matrix [ %f %f %f %f %f %f ]\n" " /Resources << /XObject << /x%d %d 0 R >> >>\n", bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y, xstep, ystep, pdf_p2d.xx, pdf_p2d.yx, pdf_p2d.xy, pdf_p2d.yy, pdf_p2d.x0, pdf_p2d.y0, pattern_resource.id, pattern_resource.id); if (unlikely (status)) return status; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { snprintf(draw_surface, sizeof (draw_surface), "/x%d Do\n", pattern_resource.id); } else { snprintf(draw_surface, sizeof (draw_surface), "q %d 0 0 %d 0 0 cm /x%d Do Q", pattern_width, pattern_height, pattern_resource.id); } if (extend == CAIRO_EXTEND_REFLECT) { _cairo_output_stream_printf (surface->output, "q 0 0 %d %d re W n %s Q\n" "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n" "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n" "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n", pattern_width, pattern_height, draw_surface, pattern_width*2, pattern_width, pattern_height, draw_surface, pattern_height*2, pattern_width, pattern_height, draw_surface, pattern_width*2, pattern_height*2, pattern_width, pattern_height, draw_surface); } else { _cairo_output_stream_printf (surface->output, " %s \n", draw_surface); } status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) return status; return _cairo_output_stream_get_status (surface->output); } typedef struct _cairo_pdf_color_stop { double offset; double color[4]; cairo_pdf_resource_t resource; } cairo_pdf_color_stop_t; static cairo_int_status_t cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, cairo_pdf_resource_t *function) { int num_elems, i; cairo_pdf_rgb_linear_function_t elem; cairo_pdf_resource_t res; cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); for (i = 0; i < num_elems; i++) { _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem); if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0) continue; if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0) continue; *function = elem.resource; return CAIRO_STATUS_SUCCESS; } res = _cairo_pdf_surface_new_object (surface); if (res.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /FunctionType 2\n" " /Domain [ 0 1 ]\n" " /C0 [ %f %f %f ]\n" " /C1 [ %f %f %f ]\n" " /N 1\n" ">>\n" "endobj\n", res.id, stop1->color[0], stop1->color[1], stop1->color[2], stop2->color[0], stop2->color[1], stop2->color[2]); elem.resource = res; memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3); memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3); status = _cairo_array_append (&surface->rgb_linear_functions, &elem); *function = res; return status; } static cairo_int_status_t cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, cairo_pdf_resource_t *function) { int num_elems, i; cairo_pdf_alpha_linear_function_t elem; cairo_pdf_resource_t res; cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); for (i = 0; i < num_elems; i++) { _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem); if (elem.alpha1 != stop1->color[3]) continue; if (elem.alpha2 != stop2->color[3]) continue; *function = elem.resource; return CAIRO_STATUS_SUCCESS; } res = _cairo_pdf_surface_new_object (surface); if (res.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /FunctionType 2\n" " /Domain [ 0 1 ]\n" " /C0 [ %f ]\n" " /C1 [ %f ]\n" " /N 1\n" ">>\n" "endobj\n", res.id, stop1->color[3], stop2->color[3]); elem.resource = res; elem.alpha1 = stop1->color[3]; elem.alpha2 = stop2->color[3]; status = _cairo_array_append (&surface->alpha_linear_functions, &elem); *function = res; return status; } static cairo_int_status_t _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, unsigned int n_stops, cairo_pdf_color_stop_t *stops, cairo_bool_t is_alpha, cairo_pdf_resource_t *function) { cairo_pdf_resource_t res; unsigned int i; cairo_int_status_t status; /* emit linear gradients between pairs of subsequent stops... */ for (i = 0; i < n_stops-1; i++) { if (is_alpha) { status = cairo_pdf_surface_emit_alpha_linear_function (surface, &stops[i], &stops[i+1], &stops[i].resource); if (unlikely (status)) return status; } else { status = cairo_pdf_surface_emit_rgb_linear_function (surface, &stops[i], &stops[i+1], &stops[i].resource); if (unlikely (status)) return status; } } /* ... and stitch them together */ res = _cairo_pdf_surface_new_object (surface); if (res.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /FunctionType 3\n" " /Domain [ %f %f ]\n", res.id, stops[0].offset, stops[n_stops - 1].offset); _cairo_output_stream_printf (surface->output, " /Functions [ "); for (i = 0; i < n_stops-1; i++) _cairo_output_stream_printf (surface->output, "%d 0 R ", stops[i].resource.id); _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, " /Bounds [ "); for (i = 1; i < n_stops-1; i++) _cairo_output_stream_printf (surface->output, "%f ", stops[i].offset); _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, " /Encode [ "); for (i = 1; i < n_stops; i++) _cairo_output_stream_printf (surface->output, "0 1 "); _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); *function = res; return _cairo_output_stream_get_status (surface->output); } static void calc_gradient_color (cairo_pdf_color_stop_t *new_stop, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2) { int i; double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); for (i = 0; i < 4; i++) new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); } #define COLOR_STOP_EPSILON 1e-6 static cairo_int_status_t _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *color_function, cairo_pdf_resource_t *alpha_function) { cairo_pdf_color_stop_t *allstops, *stops; unsigned int n_stops; unsigned int i; cairo_bool_t emit_alpha = FALSE; cairo_int_status_t status; color_function->id = 0; alpha_function->id = 0; allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); if (unlikely (allstops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); stops = &allstops[1]; n_stops = pattern->n_stops; for (i = 0; i < n_stops; i++) { stops[i].color[0] = pattern->stops[i].color.red; stops[i].color[1] = pattern->stops[i].color.green; stops[i].color[2] = pattern->stops[i].color.blue; stops[i].color[3] = pattern->stops[i].color.alpha; if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3])) emit_alpha = TRUE; stops[i].offset = pattern->stops[i].offset; } if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { if (stops[0].offset > COLOR_STOP_EPSILON) { if (pattern->base.extend == CAIRO_EXTEND_REFLECT) memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t)); else calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); stops = allstops; n_stops++; } stops[0].offset = 0.0; if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { memcpy (&stops[n_stops], &stops[n_stops - 1], sizeof (cairo_pdf_color_stop_t)); } else { calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); } n_stops++; } stops[n_stops-1].offset = 1.0; } if (stops[0].offset == stops[n_stops - 1].offset) { /* * The first and the last stops have the same offset, but we * don't want a function with an empty domain, because that * would provoke underdefined behaviour from rasterisers. * This can only happen with EXTEND_PAD, because EXTEND_NONE * is optimised into a clear pattern in cairo-gstate, and * REFLECT/REPEAT are always transformed to have the first * stop at t=0 and the last stop at t=1. Thus we want a step * function going from the first color to the last one. * * This can be accomplished by stitching three functions: * - a constant first color function, * - a step from the first color to the last color (with empty domain) * - a constant last color function */ cairo_pdf_color_stop_t pad_stops[4]; assert (pattern->base.extend == CAIRO_EXTEND_PAD); pad_stops[0] = pad_stops[1] = stops[0]; pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; pad_stops[0].offset = 0; pad_stops[3].offset = 1; status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, 4, pad_stops, FALSE, color_function); if (unlikely (status)) goto BAIL; if (emit_alpha) { status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, 4, pad_stops, TRUE, alpha_function); if (unlikely (status)) goto BAIL; } } else if (n_stops == 2) { /* no need for stitched function */ status = cairo_pdf_surface_emit_rgb_linear_function (surface, &stops[0], &stops[n_stops - 1], color_function); if (unlikely (status)) goto BAIL; if (emit_alpha) { status = cairo_pdf_surface_emit_alpha_linear_function (surface, &stops[0], &stops[n_stops - 1], alpha_function); if (unlikely (status)) goto BAIL; } } else { /* multiple stops: stitch. XXX possible optimization: regularly spaced * stops do not require stitching. XXX */ status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, n_stops, stops, FALSE, color_function); if (unlikely (status)) goto BAIL; if (emit_alpha) { status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, n_stops, stops, TRUE, alpha_function); if (unlikely (status)) goto BAIL; } } BAIL: free (allstops); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *function, int begin, int end) { cairo_pdf_resource_t res; int i; res = _cairo_pdf_surface_new_object (surface); if (res.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /FunctionType 3\n" " /Domain [ %d %d ]\n", res.id, begin, end); _cairo_output_stream_printf (surface->output, " /Functions [ "); for (i = begin; i < end; i++) _cairo_output_stream_printf (surface->output, "%d 0 R ", function->id); _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, " /Bounds [ "); for (i = begin + 1; i < end; i++) _cairo_output_stream_printf (surface->output, "%d ", i); _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, " /Encode [ "); for (i = begin; i < end; i++) { if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) { _cairo_output_stream_printf (surface->output, "1 0 "); } else { _cairo_output_stream_printf (surface->output, "0 1 "); } } _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); *function = res; return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern, cairo_pdf_resource_t gstate_resource, cairo_pdf_resource_t gradient_mask) { cairo_pdf_resource_t smask_resource; cairo_int_status_t status; char buf[100]; double x1, y1, x2, y2; if (pdf_pattern->is_shading) { snprintf(buf, sizeof(buf), " /Shading\n" " << /sh%d %d 0 R >>\n", gradient_mask.id, gradient_mask.id); } else { snprintf(buf, sizeof(buf), " /Pattern\n" " << /p%d %d 0 R >>\n", gradient_mask.id, gradient_mask.id); } if (pdf_pattern->is_shading) { cairo_box_t box; /* When emitting a shading operator we are in cairo pattern * coordinates. _cairo_pdf_surface_paint_gradient has set the * ctm to the pattern matrix (including the convertion from * pdf to cairo coordinates) */ _cairo_box_from_rectangle (&box, &pdf_pattern->extents); _cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2); _cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL); } else { cairo_box_double_t box; /* When emitting a shading pattern we are in pdf page * coordinates. The color and alpha shading patterns painted * in the XObject below contain the cairo pattern to pdf page * matrix in the /Matrix entry of the pattern. */ _get_bbox_from_extents (pdf_pattern->height, &pdf_pattern->extents, &box); x1 = box.p1.x; y1 = box.p1.y; x2 = box.p2.x; y2 = box.p2.y; } status = _cairo_pdf_surface_open_stream (surface, NULL, surface->compress_content, " /Type /XObject\n" " /Subtype /Form\n" " /FormType 1\n" " /BBox [ %f %f %f %f ]\n" " /Resources\n" " << /ExtGState\n" " << /a0 << /ca 1 /CA 1 >>" " >>\n" "%s" " >>\n" " /Group\n" " << /Type /Group\n" " /S /Transparency\n" " /I true\n" " /CS /DeviceGray\n" " >>\n", x1,y1,x2,y2, buf); if (unlikely (status)) return status; if (pdf_pattern->is_shading) { _cairo_output_stream_printf (surface->output, "/a0 gs /sh%d sh\n", gradient_mask.id); } else { _cairo_output_stream_printf (surface->output, "q\n" "/a0 gs\n" "/Pattern cs /p%d scn\n" "0 0 %f %f re\n" "f\n" "Q\n", gradient_mask.id, surface->width, surface->height); } status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) return status; smask_resource = _cairo_pdf_surface_new_object (surface); if (smask_resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Mask\n" " /S /Luminosity\n" " /G %d 0 R\n" ">>\n" "endobj\n", smask_resource.id, surface->pdf_stream.self.id); /* Create GState which uses the transparency group as an SMask. */ _cairo_pdf_surface_update_object (surface, gstate_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /ExtGState\n" " /SMask %d 0 R\n" " /ca 1\n" " /CA 1\n" " /AIS false\n" ">>\n" "endobj\n", gstate_resource.id, smask_resource.id); return _cairo_output_stream_get_status (surface->output); } static void _cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, const cairo_pdf_pattern_t *pdf_pattern, cairo_pdf_resource_t pattern_resource, const cairo_matrix_t *pat_to_pdf, const cairo_circle_double_t*start, const cairo_circle_double_t*end, const double *domain, const char *colorspace, cairo_pdf_resource_t color_function) { _cairo_output_stream_printf (surface->output, "%d 0 obj\n", pattern_resource.id); if (!pdf_pattern->is_shading) { _cairo_output_stream_printf (surface->output, "<< /Type /Pattern\n" " /PatternType 2\n" " /Matrix [ %f %f %f %f %f %f ]\n" " /Shading\n", pat_to_pdf->xx, pat_to_pdf->yx, pat_to_pdf->xy, pat_to_pdf->yy, pat_to_pdf->x0, pat_to_pdf->y0); } if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { _cairo_output_stream_printf (surface->output, " << /ShadingType 2\n" " /ColorSpace %s\n" " /Coords [ %f %f %f %f ]\n", colorspace, start->center.x, start->center.y, end->center.x, end->center.y); } else { _cairo_output_stream_printf (surface->output, " << /ShadingType 3\n" " /ColorSpace %s\n" " /Coords [ %f %f %f %f %f %f ]\n", colorspace, start->center.x, start->center.y, MAX (start->radius, 0), end->center.x, end->center.y, MAX (end->radius, 0)); } _cairo_output_stream_printf (surface->output, " /Domain [ %f %f ]\n", domain[0], domain[1]); if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (surface->output, " /Extend [ true true ]\n"); } else { _cairo_output_stream_printf (surface->output, " /Extend [ false false ]\n"); } _cairo_output_stream_printf (surface->output, " /Function %d 0 R\n" " >>\n", color_function.id); if (!pdf_pattern->is_shading) { _cairo_output_stream_printf (surface->output, ">>\n"); } _cairo_output_stream_printf (surface->output, "endobj\n"); } static cairo_int_status_t _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern; cairo_pdf_resource_t color_function, alpha_function; cairo_matrix_t pat_to_pdf; cairo_circle_double_t start, end; double domain[2]; cairo_int_status_t status; assert (pattern->n_stops != 0); status = _cairo_pdf_surface_emit_pattern_stops (surface, pattern, &color_function, &alpha_function); if (unlikely (status)) return status; pat_to_pdf = pattern->base.matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { double bounds_x1, bounds_x2, bounds_y1, bounds_y2; double x_scale, y_scale, tolerance; /* TODO: use tighter extents */ bounds_x1 = 0; bounds_y1 = 0; bounds_x2 = surface->width; bounds_y2 = surface->height; _cairo_matrix_transform_bounding_box (&pattern->base.matrix, &bounds_x1, &bounds_y1, &bounds_x2, &bounds_y2, NULL); x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); tolerance *= MIN (x_scale, y_scale); _cairo_gradient_pattern_box_to_parameter (pattern, bounds_x1, bounds_y1, bounds_x2, bounds_y2, tolerance, domain); } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { /* * If the first and the last stop offset are the same, then * the color function is a step function. * _cairo_ps_surface_emit_pattern_stops emits it as a stitched * function no matter how many stops the pattern has. The * domain of the stitched function will be [0 1] in this case. * * This is done to avoid emitting degenerate gradients for * EXTEND_PAD patterns having a step color function. */ domain[0] = 0.0; domain[1] = 1.0; assert (pattern->base.extend == CAIRO_EXTEND_PAD); } else { domain[0] = pattern->stops[0].offset; domain[1] = pattern->stops[pattern->n_stops - 1].offset; } /* PDF requires the first and last stop to be the same as the * extreme coordinates. For repeating patterns this moves the * extreme coordinates out to the begin/end of the repeating * function. For non repeating patterns this may move the extreme * coordinates in if there are not stops at offset 0 and 1. */ _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { int repeat_begin, repeat_end; repeat_begin = floor (domain[0]); repeat_end = ceil (domain[1]); status = _cairo_pdf_surface_emit_repeating_function (surface, pattern, &color_function, repeat_begin, repeat_end); if (unlikely (status)) return status; if (alpha_function.id != 0) { status = _cairo_pdf_surface_emit_repeating_function (surface, pattern, &alpha_function, repeat_begin, repeat_end); if (unlikely (status)) return status; } } else if (pattern->n_stops <= 2) { /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a * Type 2 function is used by itself without a stitching * function. Type 2 functions always have the domain [0 1] */ domain[0] = 0.0; domain[1] = 1.0; } _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); _cairo_pdf_surface_output_gradient (surface, pdf_pattern, pdf_pattern->pattern_res, &pat_to_pdf, &start, &end, domain, "/DeviceRGB", color_function); if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; assert (pdf_pattern->gstate_res.id != 0); /* Create pattern for SMask. */ mask_resource = _cairo_pdf_surface_new_object (surface); if (mask_resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_pdf_surface_output_gradient (surface, pdf_pattern, mask_resource, &pat_to_pdf, &start, &end, domain, "/DeviceGray", alpha_function); status = cairo_pdf_surface_emit_transparency_group (surface, pdf_pattern, pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) return status; } return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { cairo_matrix_t pat_to_pdf; cairo_int_status_t status; cairo_pattern_t *pattern = pdf_pattern->pattern; cairo_pdf_shading_t shading; int i; cairo_pdf_resource_t res; pat_to_pdf = pattern->matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); if (unlikely (status)) return status; res = _cairo_pdf_surface_new_object (surface); if (unlikely (res.id == 0)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /ShadingType %d\n" " /ColorSpace /DeviceRGB\n" " /BitsPerCoordinate %d\n" " /BitsPerComponent %d\n" " /BitsPerFlag %d\n" " /Decode [", res.id, shading.shading_type, shading.bits_per_coordinate, shading.bits_per_component, shading.bits_per_flag); for (i = 0; i < shading.decode_array_length; i++) _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); _cairo_output_stream_printf (surface->output, "]\n" " /Length %ld\n" ">>\n" "stream\n", shading.data_length); _cairo_output_stream_write (surface->output, shading.data, shading.data_length); _cairo_output_stream_printf (surface->output, "\nendstream\n" "endobj\n"); _cairo_pdf_shading_fini (&shading); _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" " /Matrix [ %f %f %f %f %f %f ]\n" " /Shading %d 0 R\n" ">>\n" "endobj\n", pdf_pattern->pattern_res.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, res.id); if (pdf_pattern->gstate_res.id != 0) { cairo_pdf_resource_t mask_resource; /* Create pattern for SMask. */ res = _cairo_pdf_surface_new_object (surface); if (unlikely (res.id == 0)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /ShadingType %d\n" " /ColorSpace /DeviceGray\n" " /BitsPerCoordinate %d\n" " /BitsPerComponent %d\n" " /BitsPerFlag %d\n" " /Decode [", res.id, shading.shading_type, shading.bits_per_coordinate, shading.bits_per_component, shading.bits_per_flag); for (i = 0; i < shading.decode_array_length; i++) _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); _cairo_output_stream_printf (surface->output, "]\n" " /Length %ld\n" ">>\n" "stream\n", shading.data_length); _cairo_output_stream_write (surface->output, shading.data, shading.data_length); _cairo_output_stream_printf (surface->output, "\nendstream\n" "endobj\n"); _cairo_pdf_shading_fini (&shading); mask_resource = _cairo_pdf_surface_new_object (surface); if (unlikely (mask_resource.id == 0)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" " /Matrix [ %f %f %f %f %f %f ]\n" " /Shading %d 0 R\n" ">>\n" "endobj\n", mask_resource.id, pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0, res.id); status = cairo_pdf_surface_emit_transparency_group (surface, pdf_pattern, pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) return status; } return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { double old_width, old_height; cairo_int_status_t status; old_width = surface->width; old_height = surface->height; _cairo_pdf_surface_set_size_internal (surface, pdf_pattern->width, pdf_pattern->height); switch (pdf_pattern->pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: ASSERT_NOT_REACHED; status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); break; case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern); break; case CAIRO_PATTERN_TYPE_MESH: status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern); break; default: ASSERT_NOT_REACHED; status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); break; } _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); return status; } static cairo_int_status_t _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, cairo_bool_t stencil_mask) { cairo_pdf_resource_t surface_res; int width, height; cairo_matrix_t cairo_p2d, pdf_p2d; cairo_int_status_t status; int alpha; cairo_rectangle_int_t extents2; double x_offset; double y_offset; if (source->extend == CAIRO_EXTEND_PAD && !(source->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) { status = _cairo_pdf_surface_add_padded_image_surface (surface, source, extents, &surface_res, &width, &height, &x_offset, &y_offset); } else { status = _cairo_pdf_surface_add_source_surface (surface, NULL, source, source->filter, stencil_mask, extents, &surface_res, &width, &height, &x_offset, &y_offset, &extents2); } if (unlikely (status)) return status; cairo_p2d = source->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); pdf_p2d = surface->cairo_to_pdf; cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); cairo_matrix_translate (&pdf_p2d, 0.0, height); cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); if (!(source->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) { cairo_matrix_scale (&pdf_p2d, width, height); } status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; if (! _cairo_matrix_is_identity (&pdf_p2d)) { _cairo_output_stream_printf (surface->output, "%f %f %f %f %f %f cm\n", pdf_p2d.xx, pdf_p2d.yx, pdf_p2d.xy, pdf_p2d.yy, pdf_p2d.x0, pdf_p2d.y0); } status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) return status; if (stencil_mask) { _cairo_output_stream_printf (surface->output, "/x%d Do\n", surface_res.id); } else { _cairo_output_stream_printf (surface->output, "/a%d gs /x%d Do\n", alpha, surface_res.id); } return _cairo_pdf_surface_add_xobject (surface, surface_res); } static cairo_int_status_t _cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents) { cairo_pdf_resource_t shading_res, gstate_res; cairo_matrix_t pat_to_pdf; cairo_int_status_t status; int alpha; status = _cairo_pdf_surface_add_pdf_shading (surface, source, extents, &shading_res, &gstate_res); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) return CAIRO_INT_STATUS_SUCCESS; if (unlikely (status)) return status; pat_to_pdf = source->matrix; status = cairo_matrix_invert (&pat_to_pdf); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; if (! _cairo_matrix_is_identity (&pat_to_pdf)) { _cairo_output_stream_printf (surface->output, "%f %f %f %f %f %f cm\n", pat_to_pdf.xx, pat_to_pdf.yx, pat_to_pdf.xy, pat_to_pdf.yy, pat_to_pdf.x0, pat_to_pdf.y0); } status = _cairo_pdf_surface_add_shading (surface, shading_res); if (unlikely (status)) return status; if (gstate_res.id != 0) { status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/s%d gs /sh%d sh\n", gstate_res.id, shading_res.id); } else { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/a%d gs /sh%d sh\n", alpha, shading_res.id); } return status; } static cairo_int_status_t _cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, cairo_bool_t mask) { switch (source->type) { case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_pdf_surface_paint_surface_pattern (surface, source, extents, mask); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_pdf_surface_paint_gradient (surface, source, extents); case CAIRO_PATTERN_TYPE_SOLID: default: ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; } } static cairo_bool_t _can_paint_pattern (const cairo_pattern_t *pattern) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return FALSE; case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return (pattern->extend == CAIRO_EXTEND_NONE || pattern->extend == CAIRO_EXTEND_PAD); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: return TRUE; case CAIRO_PATTERN_TYPE_MESH: return FALSE; default: ASSERT_NOT_REACHED; return FALSE; } } static cairo_int_status_t _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, cairo_operator_t op) { cairo_int_status_t status; if (op == surface->current_operator) return CAIRO_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/b%d gs\n", op); surface->current_operator = op; _cairo_pdf_surface_add_operator (surface, op); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_pdf_resource_t pattern_res, cairo_bool_t is_stroke) { cairo_int_status_t status; int alpha; const cairo_color_t *solid_color = NULL; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; solid_color = &solid->color; } if (solid_color != NULL) { if (surface->current_pattern_is_solid_color == FALSE || surface->current_color_red != solid_color->red || surface->current_color_green != solid_color->green || surface->current_color_blue != solid_color->blue || surface->current_color_is_stroke != is_stroke) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "%f %f %f ", solid_color->red, solid_color->green, solid_color->blue); if (is_stroke) _cairo_output_stream_printf (surface->output, "RG "); else _cairo_output_stream_printf (surface->output, "rg "); surface->current_color_red = solid_color->red; surface->current_color_green = solid_color->green; surface->current_color_blue = solid_color->blue; surface->current_color_is_stroke = is_stroke; } if (surface->current_pattern_is_solid_color == FALSE || surface->current_color_alpha != solid_color->alpha) { status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/a%d gs\n", alpha); surface->current_color_alpha = solid_color->alpha; } surface->current_pattern_is_solid_color = TRUE; } else { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) return status; status = _cairo_pdf_surface_add_pattern (surface, pattern_res); if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; /* fill-stroke calls select_pattern twice. Don't save if the * gstate is already saved. */ if (!surface->select_pattern_gstate_saved) _cairo_output_stream_printf (surface->output, "q "); if (is_stroke) { _cairo_output_stream_printf (surface->output, "/Pattern CS /p%d SCN ", pattern_res.id); } else { _cairo_output_stream_printf (surface->output, "/Pattern cs /p%d scn ", pattern_res.id); } _cairo_output_stream_printf (surface->output, "/a%d gs\n", alpha); surface->select_pattern_gstate_saved = TRUE; surface->current_pattern_is_solid_color = FALSE; } return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) { cairo_int_status_t status; if (surface->select_pattern_gstate_saved) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); surface->current_pattern_is_solid_color = FALSE; } surface->select_pattern_gstate_saved = FALSE; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_show_page (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; cairo_int_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) return status; _cairo_surface_clipper_reset (&surface->clipper); status = _cairo_pdf_surface_write_page (surface); if (unlikely (status)) return status; _cairo_pdf_surface_clear (surface); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_pdf_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_pdf_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; /* XXX: The conversion to integers here is pretty bogus, (not to * mention the arbitrary limitation of width to a short(!). We * may need to come up with a better interface for get_size. */ rectangle->width = ceil (surface->width); rectangle->height = ceil (surface->height); return TRUE; } static void _cairo_pdf_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static cairo_pdf_resource_t _cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t info; info = _cairo_pdf_surface_new_object (surface); if (info.id == 0) return info; _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Creator (cairo %s (http://cairographics.org))\n" " /Producer (cairo %s (http://cairographics.org))\n" ">>\n" "endobj\n", info.id, cairo_version_string (), cairo_version_string ()); return info; } static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page; int num_pages, i; _cairo_pdf_surface_update_object (surface, surface->pages_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pages\n" " /Kids [ ", surface->pages_resource.id); num_pages = _cairo_array_num_elements (&surface->pages); for (i = 0; i < num_pages; i++) { _cairo_array_copy_element (&surface->pages, i, &page); _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id); } _cairo_output_stream_printf (surface->output, "]\n"); _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages); /* TODO: Figure out which other defaults to be inherited by /Page * objects. */ _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); } static cairo_int_status_t _utf8_to_pdf_string (const char *utf8, char **str_out) { int i; int len; cairo_bool_t ascii; char *str; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; ascii = TRUE; len = strlen (utf8); for (i = 0; i < len; i++) { unsigned c = utf8[i]; if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') { ascii = FALSE; break; } } if (ascii) { str = malloc (len + 3); if (str == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); str[0] = '('; for (i = 0; i < len; i++) str[i+1] = utf8[i]; str[i+1] = ')'; str[i+2] = 0; } else { uint16_t *utf16 = NULL; int utf16_len = 0; status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); if (unlikely (status)) return status; str = malloc (utf16_len*4 + 7); if (str == NULL) { free (utf16); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } strcpy (str, "<FEFF"); for (i = 0; i < utf16_len; i++) snprintf (str + 4*i + 5, 5, "%04X", utf16[i]); strcat (str, ">"); free (utf16); } *str_out = str; return status; } static cairo_int_status_t _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, const char *utf8) { uint16_t *utf16 = NULL; int utf16_len = 0; cairo_int_status_t status; int i; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); if (unlikely (status)) return status; } _cairo_output_stream_printf (surface->output, "<"); if (utf16 == NULL || utf16_len == 0) { /* According to the "ToUnicode Mapping File Tutorial" * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf * * Glyphs that do not map to a Unicode code point must be * mapped to 0xfffd "REPLACEMENT CHARACTER". */ _cairo_output_stream_printf (surface->output, "fffd"); } else { for (i = 0; i < utf16_len; i++) _cairo_output_stream_printf (surface->output, "%04x", (int) (utf16[i])); } _cairo_output_stream_printf (surface->output, ">"); free (utf16); return CAIRO_STATUS_SUCCESS; } /* Bob Jenkins hash * * Public domain code from: * http://burtleburtle.net/bob/hash/doobs.html */ #define HASH_MIX(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } static uint32_t _hash_data (const unsigned char *data, int length, uint32_t initval) { uint32_t a, b, c, len; len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = initval; /* the previous hash value */ while (len >= 12) { a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); HASH_MIX (a,b,c); data += 12; len -= 12; } c += length; switch(len) { case 11: c+= ((uint32_t) data[10] << 24); case 10: c+= ((uint32_t) data[9] << 16); case 9 : c+= ((uint32_t) data[8] << 8); case 8 : b+= ((uint32_t) data[7] << 24); case 7 : b+= ((uint32_t) data[6] << 16); case 6 : b+= ((uint32_t) data[5] << 8); case 5 : b+= data[4]; case 4 : a+= ((uint32_t) data[3] << 24); case 3 : a+= ((uint32_t) data[2] << 16); case 2 : a+= ((uint32_t) data[1] << 8); case 1 : a+= data[0]; } HASH_MIX (a,b,c); return c; } static void _create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, const char *font_name, char *tag) { uint32_t hash; int i; long numerator; ldiv_t d; hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); hash = _hash_data ((unsigned char *) (font_subset->glyphs), font_subset->num_glyphs * sizeof(unsigned long), hash); numerator = abs (hash); for (i = 0; i < 6; i++) { d = ldiv (numerator, 26); numerator = d.quot; tag[i] = 'A' + d.rem; } tag[i] = 0; } static cairo_int_status_t _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_pdf_resource_t *stream) { unsigned int i, num_bfchar; cairo_int_status_t status; stream->id = 0; status = _cairo_pdf_surface_open_stream (surface, NULL, surface->compress_content, NULL); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/CIDInit /ProcSet findresource begin\n" "12 dict begin\n" "begincmap\n" "/CIDSystemInfo\n" "<< /Registry (Adobe)\n" " /Ordering (UCS)\n" " /Supplement 0\n" ">> def\n" "/CMapName /Adobe-Identity-UCS def\n" "/CMapType 2 def\n" "1 begincodespacerange\n"); if (font_subset->is_composite && !font_subset->is_latin) { _cairo_output_stream_printf (surface->output, "<0000> <ffff>\n"); } else { _cairo_output_stream_printf (surface->output, "<00> <ff>\n"); } _cairo_output_stream_printf (surface->output, "endcodespacerange\n"); if (font_subset->is_scaled) { /* Type 3 fonts include glyph 0 in the subset */ num_bfchar = font_subset->num_glyphs; /* The CMap specification has a limit of 100 characters per beginbfchar operator */ _cairo_output_stream_printf (surface->output, "%d beginbfchar\n", num_bfchar > 100 ? 100 : num_bfchar); for (i = 0; i < num_bfchar; i++) { if (i != 0 && i % 100 == 0) { _cairo_output_stream_printf (surface->output, "endbfchar\n" "%d beginbfchar\n", num_bfchar - i > 100 ? 100 : num_bfchar - i); } _cairo_output_stream_printf (surface->output, "<%02x> ", i); status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, font_subset->utf8[i]); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "\n"); } } else { /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ num_bfchar = font_subset->num_glyphs - 1; /* The CMap specification has a limit of 100 characters per beginbfchar operator */ _cairo_output_stream_printf (surface->output, "%d beginbfchar\n", num_bfchar > 100 ? 100 : num_bfchar); for (i = 0; i < num_bfchar; i++) { if (i != 0 && i % 100 == 0) { _cairo_output_stream_printf (surface->output, "endbfchar\n" "%d beginbfchar\n", num_bfchar - i > 100 ? 100 : num_bfchar - i); } if (font_subset->is_latin) _cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]); else if (font_subset->is_composite) _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); else _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, font_subset->utf8[i + 1]); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "\n"); } } _cairo_output_stream_printf (surface->output, "endbfchar\n"); _cairo_output_stream_printf (surface->output, "endcmap\n" "CMapName currentdict /CMap defineresource pop\n" "end\n" "end\n"); *stream = surface->pdf_stream.self; return _cairo_pdf_surface_close_stream (surface); } #define PDF_UNITS_PER_EM 1000 static cairo_int_status_t _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_cff_subset_t *subset) { cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; cairo_pdf_font_t font; unsigned int i, last_glyph; cairo_int_status_t status; char tag[10]; _create_font_subset_tag (font_subset, subset->ps_name, tag); subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, font_subset->subset_id); if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, font_subset->is_latin ? " /Subtype /Type1C\n" : " /Subtype /CIDFontType0C\n"); if (unlikely (status)) return status; stream = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, subset->data, subset->data_length); status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); if (_cairo_int_status_is_error (status)) return status; descriptor = _cairo_pdf_surface_new_object (surface); if (descriptor.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" " /FontName /%s+%s\n", descriptor.id, tag, subset->ps_name); if (subset->family_name_utf8) { char *pdf_str; status = _utf8_to_pdf_string (subset->family_name_utf8, &pdf_str); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, " /FontFamily %s\n", pdf_str); free (pdf_str); } _cairo_output_stream_printf (surface->output, " /Flags 4\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" " /Descent %ld\n" " /CapHeight %ld\n" " /StemV 80\n" " /StemH 80\n" " /FontFile3 %u 0 R\n" ">>\n" "endobj\n", (long)(subset->x_min*PDF_UNITS_PER_EM), (long)(subset->y_min*PDF_UNITS_PER_EM), (long)(subset->x_max*PDF_UNITS_PER_EM), (long)(subset->y_max*PDF_UNITS_PER_EM), (long)(subset->ascent*PDF_UNITS_PER_EM), (long)(subset->descent*PDF_UNITS_PER_EM), (long)(subset->y_max*PDF_UNITS_PER_EM), stream.id); if (font_subset->is_latin) { /* find last glyph used */ for (i = 255; i >= 32; i--) if (font_subset->latin_to_subset_glyph_index[i] > 0) break; last_glyph = i; _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type1\n" " /BaseFont /%s+%s\n" " /FirstChar 32\n" " /LastChar %d\n" " /FontDescriptor %d 0 R\n" " /Encoding /WinAnsiEncoding\n" " /Widths [", subset_resource.id, tag, subset->ps_name, last_glyph, descriptor.id); for (i = 32; i < last_glyph + 1; i++) { int glyph = font_subset->latin_to_subset_glyph_index[i]; if (glyph > 0) { _cairo_output_stream_printf (surface->output, " %ld", (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); } else { _cairo_output_stream_printf (surface->output, " 0"); } } _cairo_output_stream_printf (surface->output, " ]\n"); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); } else { cidfont_dict = _cairo_pdf_surface_new_object (surface); if (cidfont_dict.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /CIDFontType0\n" " /BaseFont /%s+%s\n" " /CIDSystemInfo\n" " << /Registry (Adobe)\n" " /Ordering (Identity)\n" " /Supplement 0\n" " >>\n" " /FontDescriptor %d 0 R\n" " /W [0 [", cidfont_dict.id, tag, subset->ps_name, descriptor.id); for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " %ld", (long)(subset->widths[i]*PDF_UNITS_PER_EM)); _cairo_output_stream_printf (surface->output, " ]]\n" ">>\n" "endobj\n"); _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type0\n" " /BaseFont /%s+%s\n" " /Encoding /Identity-H\n" " /DescendantFonts [ %d 0 R]\n", subset_resource.id, tag, subset->ps_name, cidfont_dict.id); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); } font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; font.subset_resource = subset_resource; status = _cairo_array_append (&surface->fonts, &font); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_subset_init (&subset, name, font_subset); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); _cairo_cff_subset_fini (&subset); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; /* CFF fallback subsetting does not work with 8-bit glyphs unless * they are a latin subset */ if (!font_subset->is_composite && !font_subset->is_latin) return CAIRO_INT_STATUS_UNSUPPORTED; snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_fallback_init (&subset, name, font_subset); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); _cairo_cff_fallback_fini (&subset); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_type1_subset_t *subset) { cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; cairo_pdf_font_t font; cairo_int_status_t status; unsigned long length; unsigned int i, last_glyph; char tag[10]; _create_font_subset_tag (font_subset, subset->base_font, tag); subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, font_subset->subset_id); if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; length = subset->header_length + subset->data_length + subset->trailer_length; status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, " /Length1 %lu\n" " /Length2 %lu\n" " /Length3 %lu\n", subset->header_length, subset->data_length, subset->trailer_length); if (unlikely (status)) return status; stream = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, subset->data, length); status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); if (_cairo_int_status_is_error (status)) return status; last_glyph = font_subset->num_glyphs - 1; if (font_subset->is_latin) { /* find last glyph used */ for (i = 255; i >= 32; i--) if (font_subset->latin_to_subset_glyph_index[i] > 0) break; last_glyph = i; } descriptor = _cairo_pdf_surface_new_object (surface); if (descriptor.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" " /FontName /%s+%s\n" " /Flags 4\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" " /Descent %ld\n" " /CapHeight %ld\n" " /StemV 80\n" " /StemH 80\n" " /FontFile %u 0 R\n" ">>\n" "endobj\n", descriptor.id, tag, subset->base_font, (long)(subset->x_min*PDF_UNITS_PER_EM), (long)(subset->y_min*PDF_UNITS_PER_EM), (long)(subset->x_max*PDF_UNITS_PER_EM), (long)(subset->y_max*PDF_UNITS_PER_EM), (long)(subset->ascent*PDF_UNITS_PER_EM), (long)(subset->descent*PDF_UNITS_PER_EM), (long)(subset->y_max*PDF_UNITS_PER_EM), stream.id); _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type1\n" " /BaseFont /%s+%s\n" " /FirstChar %d\n" " /LastChar %d\n" " /FontDescriptor %d 0 R\n", subset_resource.id, tag, subset->base_font, font_subset->is_latin ? 32 : 0, last_glyph, descriptor.id); if (font_subset->is_latin) _cairo_output_stream_printf (surface->output, " /Encoding /WinAnsiEncoding\n"); _cairo_output_stream_printf (surface->output, " /Widths ["); if (font_subset->is_latin) { for (i = 32; i < last_glyph + 1; i++) { int glyph = font_subset->latin_to_subset_glyph_index[i]; if (glyph > 0) { _cairo_output_stream_printf (surface->output, " %ld", (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); } else { _cairo_output_stream_printf (surface->output, " 0"); } } } else { for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " %ld", (long)(subset->widths[i]*PDF_UNITS_PER_EM)); } _cairo_output_stream_printf (surface->output, " ]\n"); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; font.subset_resource = subset_resource; return _cairo_array_append (&surface->fonts, &font); } static cairo_int_status_t _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; /* 16-bit glyphs not compatible with Type 1 fonts */ if (font_subset->is_composite && !font_subset->is_latin) return CAIRO_INT_STATUS_UNSUPPORTED; snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); _cairo_type1_subset_fini (&subset); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; /* 16-bit glyphs not compatible with Type 1 fonts */ if (font_subset->is_composite && !font_subset->is_latin) return CAIRO_INT_STATUS_UNSUPPORTED; snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); if (unlikely (status)) return status; status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); _cairo_type1_fallback_fini (&subset); return status; } static cairo_int_status_t _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; cairo_int_status_t status; cairo_pdf_font_t font; cairo_truetype_subset_t subset; unsigned int i, last_glyph; char tag[10]; subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, font_subset->subset_id); if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; status = _cairo_truetype_subset_init_pdf (&subset, font_subset); if (unlikely (status)) return status; _create_font_subset_tag (font_subset, subset.ps_name, tag); status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, " /Length1 %lu\n", subset.data_length); if (unlikely (status)) { _cairo_truetype_subset_fini (&subset); return status; } stream = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, subset.data, subset.data_length); status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) { _cairo_truetype_subset_fini (&subset); return status; } status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); if (_cairo_int_status_is_error (status)) { _cairo_truetype_subset_fini (&subset); return status; } descriptor = _cairo_pdf_surface_new_object (surface); if (descriptor.id == 0) { _cairo_truetype_subset_fini (&subset); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /FontDescriptor\n" " /FontName /%s+%s\n", descriptor.id, tag, subset.ps_name); if (subset.family_name_utf8) { char *pdf_str; status = _utf8_to_pdf_string (subset.family_name_utf8, &pdf_str); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, " /FontFamily %s\n", pdf_str); free (pdf_str); } _cairo_output_stream_printf (surface->output, " /Flags %d\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" " /Descent %ld\n" " /CapHeight %ld\n" " /StemV 80\n" " /StemH 80\n" " /FontFile2 %u 0 R\n" ">>\n" "endobj\n", font_subset->is_latin ? 32 : 4, (long)(subset.x_min*PDF_UNITS_PER_EM), (long)(subset.y_min*PDF_UNITS_PER_EM), (long)(subset.x_max*PDF_UNITS_PER_EM), (long)(subset.y_max*PDF_UNITS_PER_EM), (long)(subset.ascent*PDF_UNITS_PER_EM), (long)(subset.descent*PDF_UNITS_PER_EM), (long)(subset.y_max*PDF_UNITS_PER_EM), stream.id); if (font_subset->is_latin) { /* find last glyph used */ for (i = 255; i >= 32; i--) if (font_subset->latin_to_subset_glyph_index[i] > 0) break; last_glyph = i; _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /TrueType\n" " /BaseFont /%s+%s\n" " /FirstChar 32\n" " /LastChar %d\n" " /FontDescriptor %d 0 R\n" " /Encoding /WinAnsiEncoding\n" " /Widths [", subset_resource.id, tag, subset.ps_name, last_glyph, descriptor.id); for (i = 32; i < last_glyph + 1; i++) { int glyph = font_subset->latin_to_subset_glyph_index[i]; if (glyph > 0) { _cairo_output_stream_printf (surface->output, " %ld", (long)(subset.widths[glyph]*PDF_UNITS_PER_EM)); } else { _cairo_output_stream_printf (surface->output, " 0"); } } _cairo_output_stream_printf (surface->output, " ]\n"); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); } else { cidfont_dict = _cairo_pdf_surface_new_object (surface); if (cidfont_dict.id == 0) { _cairo_truetype_subset_fini (&subset); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /CIDFontType2\n" " /BaseFont /%s+%s\n" " /CIDSystemInfo\n" " << /Registry (Adobe)\n" " /Ordering (Identity)\n" " /Supplement 0\n" " >>\n" " /FontDescriptor %d 0 R\n" " /W [0 [", cidfont_dict.id, tag, subset.ps_name, descriptor.id); for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " %ld", (long)(subset.widths[i]*PDF_UNITS_PER_EM)); _cairo_output_stream_printf (surface->output, " ]]\n" ">>\n" "endobj\n"); _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type0\n" " /BaseFont /%s+%s\n" " /Encoding /Identity-H\n" " /DescendantFonts [ %d 0 R]\n", subset_resource.id, tag, subset.ps_name, cidfont_dict.id); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); } font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; font.subset_resource = subset_resource; status = _cairo_array_append (&surface->fonts, &font); _cairo_truetype_subset_fini (&subset); return status; } static cairo_int_status_t _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, cairo_output_stream_t *stream) { uint8_t *byte, output_byte; int row, col, num_cols; /* The only image type supported by Type 3 fonts are 1-bit image * masks */ assert (image->format == CAIRO_FORMAT_A1); _cairo_output_stream_printf (stream, "BI\n" "/IM true\n" "/W %d\n" "/H %d\n" "/BPC 1\n" "/D [1 0]\n", image->width, image->height); _cairo_output_stream_printf (stream, "ID "); num_cols = (image->width + 7) / 8; for (row = 0; row < image->height; row++) { byte = image->data + row * image->stride; for (col = 0; col < num_cols; col++) { output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); _cairo_output_stream_write (stream, &output_byte, 1); byte++; } } _cairo_output_stream_printf (stream, "\nEI\n"); return _cairo_output_stream_get_status (stream); } static cairo_int_status_t _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_pdf_surface_t *surface = closure; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_int_status_t status2; unsigned int i; cairo_surface_t *type3_surface; cairo_output_stream_t *null_stream; null_stream = _cairo_null_stream_create (); type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, null_stream, _cairo_pdf_emit_imagemask, surface->font_subsets); if (unlikely (type3_surface->status)) { status2 = _cairo_output_stream_destroy (null_stream); return type3_surface->status; } _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, font_subset->glyphs[i]); if (unlikely (status)) break; } cairo_surface_destroy (type3_surface); status2 = _cairo_output_stream_destroy (null_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; } static cairo_int_status_t _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; cairo_pdf_font_t font; double *widths; unsigned int i; cairo_box_t font_bbox = {{0,0},{0,0}}; cairo_box_t bbox = {{0,0},{0,0}}; cairo_surface_t *type3_surface; if (font_subset->num_glyphs == 0) return CAIRO_STATUS_SUCCESS; subset_resource = _cairo_pdf_surface_get_font_resource (surface, font_subset->font_id, font_subset->subset_id); if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); if (unlikely (glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); if (unlikely (widths == NULL)) { free (glyphs); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_pdf_group_resources_clear (&surface->resources); type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_pdf_emit_imagemask, surface->font_subsets); if (unlikely (type3_surface->status)) { free (glyphs); free (widths); return type3_surface->status; } _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_pdf_surface_open_stream (surface, NULL, surface->compress_content, NULL); if (unlikely (status)) break; glyphs[i] = surface->pdf_stream.self; status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, surface->output, font_subset->glyphs[i], &bbox, &widths[i]); if (unlikely (status)) break; status = _cairo_pdf_surface_close_stream (surface); if (unlikely (status)) break; if (i == 0) { font_bbox.p1.x = bbox.p1.x; font_bbox.p1.y = bbox.p1.y; font_bbox.p2.x = bbox.p2.x; font_bbox.p2.y = bbox.p2.y; } else { if (bbox.p1.x < font_bbox.p1.x) font_bbox.p1.x = bbox.p1.x; if (bbox.p1.y < font_bbox.p1.y) font_bbox.p1.y = bbox.p1.y; if (bbox.p2.x > font_bbox.p2.x) font_bbox.p2.x = bbox.p2.x; if (bbox.p2.y > font_bbox.p2.y) font_bbox.p2.y = bbox.p2.y; } } cairo_surface_destroy (type3_surface); if (unlikely (status)) { free (glyphs); free (widths); return status; } encoding = _cairo_pdf_surface_new_object (surface); if (encoding.id == 0) { free (glyphs); free (widths); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Encoding\n" " /Differences [0", encoding.id); for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " /%d", i); _cairo_output_stream_printf (surface->output, "]\n" ">>\n" "endobj\n"); char_procs = _cairo_pdf_surface_new_object (surface); if (char_procs.id == 0) { free (glyphs); free (widths); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<<\n", char_procs.id); for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " /%d %d 0 R\n", i, glyphs[i].id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); free (glyphs); status = _cairo_pdf_surface_emit_to_unicode_stream (surface, font_subset, &to_unicode_stream); if (_cairo_int_status_is_error (status)) { free (widths); return status; } _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Font\n" " /Subtype /Type3\n" " /FontBBox [%f %f %f %f]\n" " /FontMatrix [ 1 0 0 1 0 0 ]\n" " /Encoding %d 0 R\n" " /CharProcs %d 0 R\n" " /FirstChar 0\n" " /LastChar %d\n", subset_resource.id, _cairo_fixed_to_double (font_bbox.p1.x), - _cairo_fixed_to_double (font_bbox.p2.y), _cairo_fixed_to_double (font_bbox.p2.x), - _cairo_fixed_to_double (font_bbox.p1.y), encoding.id, char_procs.id, font_subset->num_glyphs - 1); _cairo_output_stream_printf (surface->output, " /Widths ["); for (i = 0; i < font_subset->num_glyphs; i++) _cairo_output_stream_printf (surface->output, " %f", widths[i]); _cairo_output_stream_printf (surface->output, "]\n"); free (widths); _cairo_output_stream_printf (surface->output, " /Resources\n"); _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); if (to_unicode_stream.id != 0) _cairo_output_stream_printf (surface->output, " /ToUnicode %d 0 R\n", to_unicode_stream.id); _cairo_output_stream_printf (surface->output, ">>\n" "endobj\n"); font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; font.subset_resource = subset_resource; return _cairo_array_append (&surface->fonts, &font); } static cairo_int_status_t _cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_pdf_surface_t *surface = closure; cairo_int_status_t status; status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_pdf_surface_t *surface = closure; cairo_int_status_t status; status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) { cairo_int_status_t status; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_pdf_surface_analyze_user_font_subset, surface); if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_pdf_surface_emit_unscaled_font_subset, surface); if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_pdf_surface_emit_scaled_font_subset, surface); if (unlikely (status)) goto BAIL; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_pdf_surface_emit_scaled_font_subset, surface); BAIL: _cairo_scaled_font_subsets_destroy (surface->font_subsets); surface->font_subsets = NULL; return status; } static cairo_pdf_resource_t _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t catalog; catalog = _cairo_pdf_surface_new_object (surface); if (catalog.id == 0) return catalog; _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Catalog\n" " /Pages %d 0 R\n" ">>\n" "endobj\n", catalog.id, surface->pages_resource.id); return catalog; } static long _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) { cairo_pdf_object_t *object; int num_objects, i; long offset; char buffer[11]; num_objects = _cairo_array_num_elements (&surface->objects); offset = _cairo_output_stream_get_position (surface->output); _cairo_output_stream_printf (surface->output, "xref\n" "%d %d\n", 0, num_objects + 1); _cairo_output_stream_printf (surface->output, "0000000000 65535 f \n"); for (i = 0; i < num_objects; i++) { object = _cairo_array_index (&surface->objects, i); snprintf (buffer, sizeof buffer, "%010ld", object->offset); _cairo_output_stream_printf (surface->output, "%s 00000 n \n", buffer); } return offset; } static cairo_int_status_t _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { cairo_pdf_resource_t mask_group; cairo_pdf_resource_t smask; cairo_pdf_smask_group_t *smask_group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_int_status_t status; cairo_box_double_t bbox; /* Create mask group */ _get_bbox_from_extents (group->height, &group->extents, &bbox); status = _cairo_pdf_surface_open_group (surface, &bbox, NULL); if (unlikely (status)) return status; if (_can_paint_pattern (group->mask)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, group->mask, &group->extents, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); } else { pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, &pattern_res, &gstate_res); if (unlikely (status)) return status; if (gstate_res.id != 0) { smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); smask_group->width = group->width; smask_group->height = group->height; smask_group->operation = PDF_PAINT; smask_group->source = cairo_pattern_reference (group->mask); smask_group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, smask_group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (smask_group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, smask_group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "%f %f %f %f re f\n", bbox.p1.x, bbox.p1.y, bbox.p2.x - bbox.p1.x, bbox.p2.y - bbox.p1.y); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) return status; } } status = _cairo_pdf_surface_close_group (surface, &mask_group); if (unlikely (status)) return status; /* Create source group */ status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res); if (unlikely (status)) return status; if (_can_paint_pattern (group->source)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, group->source, &group->extents, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); } else { pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, &pattern_res, &gstate_res); if (unlikely (status)) return status; if (gstate_res.id != 0) { smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); if (unlikely (smask_group == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); smask_group->operation = PDF_PAINT; smask_group->source = cairo_pattern_reference (group->source); smask_group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, smask_group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (smask_group); return status; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) return status; status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, smask_group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "%f %f %f %f re f\n", bbox.p1.x, bbox.p1.y, bbox.p2.x - bbox.p1.x, bbox.p2.y - bbox.p1.y); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) return status; } } status = _cairo_pdf_surface_close_group (surface, NULL); if (unlikely (status)) return status; /* Create an smask based on the alpha component of mask_group */ smask = _cairo_pdf_surface_new_object (surface); if (smask.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Mask\n" " /S /Alpha\n" " /G %d 0 R\n" ">>\n" "endobj\n", smask.id, mask_group.id); /* Create a GState that uses the smask */ _cairo_pdf_surface_update_object (surface, group->group_res); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /ExtGState\n" " /SMask %d 0 R\n" " /ca 1\n" " /CA 1\n" " /AIS false\n" ">>\n" "endobj\n", group->group_res.id, smask.id); return _cairo_output_stream_get_status (surface->output); } static cairo_int_status_t _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { double old_width, old_height; cairo_int_status_t status; cairo_box_double_t bbox; old_width = surface->width; old_height = surface->height; _cairo_pdf_surface_set_size_internal (surface, group->width, group->height); /* _mask is a special case that requires two groups - source * and mask as well as a smask and gstate dictionary */ if (group->operation == PDF_MASK) { status = _cairo_pdf_surface_write_mask_group (surface, group); goto RESTORE_SIZE; } _get_bbox_from_extents (group->height, &group->extents, &bbox); status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res); if (unlikely (status)) return status; status = _cairo_pdf_surface_select_pattern (surface, group->source, group->source_res, group->operation == PDF_STROKE); if (unlikely (status)) return status; switch (group->operation) { case PDF_PAINT: _cairo_output_stream_printf (surface->output, "0 0 %f %f re f\n", surface->width, surface->height); break; case PDF_MASK: ASSERT_NOT_REACHED; break; case PDF_FILL: status = _cairo_pdf_operators_fill (&surface->pdf_operators, &group->path, group->fill_rule); break; case PDF_STROKE: status = _cairo_pdf_operators_stroke (&surface->pdf_operators, &group->path, &group->style, &group->ctm, &group->ctm_inverse); break; case PDF_SHOW_GLYPHS: status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, group->utf8, group->utf8_len, group->glyphs, group->num_glyphs, group->clusters, group->num_clusters, group->cluster_flags, group->scaled_font); break; } if (unlikely (status)) return status; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) return status; status = _cairo_pdf_surface_close_group (surface, NULL); RESTORE_SIZE: _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); return status; } static cairo_int_status_t _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface) { cairo_pdf_pattern_t pattern; cairo_pdf_smask_group_t *group; cairo_pdf_source_surface_t src_surface; unsigned int pattern_index, group_index, surface_index; cairo_int_status_t status; /* Writing out PDF_MASK groups will cause additional smask groups * to be appended to surface->smask_groups. Additional patterns * may also be appended to surface->patterns. * * Writing recording surface patterns will cause additional patterns * and groups to be appended. */ pattern_index = 0; group_index = 0; surface_index = 0; while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || (group_index < _cairo_array_num_elements (&surface->smask_groups)) || (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) { for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { _cairo_array_copy_element (&surface->smask_groups, group_index, &group); status = _cairo_pdf_surface_write_smask_group (surface, group); if (unlikely (status)) return status; } for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); status = _cairo_pdf_surface_emit_pattern (surface, &pattern); if (unlikely (status)) return status; } for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); status = _cairo_pdf_surface_emit_surface (surface, &src_surface); if (unlikely (status)) return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page, knockout, res; cairo_int_status_t status; unsigned int i, len; _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { cairo_rectangle_int_t extents; cairo_box_double_t bbox; extents.x = 0; extents.y = 0; extents.width = ceil (surface->width); extents.height = ceil (surface->height); _get_bbox_from_extents (surface->height, &extents, &bbox); status = _cairo_pdf_surface_open_knockout_group (surface, &bbox); if (unlikely (status)) return status; len = _cairo_array_num_elements (&surface->knockout_group); for (i = 0; i < len; i++) { _cairo_array_copy_element (&surface->knockout_group, i, &res); _cairo_output_stream_printf (surface->output, "/x%d Do\n", res.id); status = _cairo_pdf_surface_add_xobject (surface, res); if (unlikely (status)) return status; } _cairo_output_stream_printf (surface->output, "/x%d Do\n", surface->content.id); status = _cairo_pdf_surface_add_xobject (surface, surface->content); if (unlikely (status)) return status; status = _cairo_pdf_surface_close_group (surface, &knockout); if (unlikely (status)) return status; _cairo_pdf_group_resources_clear (&surface->resources); status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "/x%d Do\n", knockout.id); status = _cairo_pdf_surface_add_xobject (surface, knockout); if (unlikely (status)) return status; status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) return status; } page = _cairo_pdf_surface_new_object (surface); if (page.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Page\n" " /Parent %d 0 R\n" " /MediaBox [ 0 0 %f %f ]\n" " /Contents %d 0 R\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" " /I true\n" " /CS /DeviceRGB\n" " >>\n" " /Resources %d 0 R\n" ">>\n" "endobj\n", page.id, surface->pages_resource.id, surface->width, surface->height, surface->content.id, surface->content_resources.id); status = _cairo_array_append (&surface->pages, &page); if (unlikely (status)) return status; status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, cairo_surface_pattern_t *pattern) { cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; cairo_image_transparency_t transparency; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (unlikely (status)) return status; if (image->base.status) return image->base.status; transparency = _cairo_image_analyze_transparency (image); if (transparency == CAIRO_IMAGE_IS_OPAQUE) status = CAIRO_STATUS_SUCCESS; else status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } static cairo_bool_t _surface_pattern_supported (cairo_surface_pattern_t *pattern) { cairo_extend_t extend; if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return TRUE; if (pattern->surface->backend->acquire_source_image == NULL) return FALSE; /* Does an ALPHA-only source surface even make sense? Maybe, but I * don't think it's worth the extra code to support it. */ /* XXX: Need to write this function here... if (pattern->surface->content == CAIRO_CONTENT_ALPHA) return FALSE; */ extend = cairo_pattern_get_extend (&pattern->base); switch (extend) { case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_REPEAT: case CAIRO_EXTEND_REFLECT: /* There's no point returning FALSE for EXTEND_PAD, as the image * surface does not currently implement it either */ case CAIRO_EXTEND_PAD: return TRUE; } ASSERT_NOT_REACHED; return FALSE; } static cairo_bool_t _pattern_supported (const cairo_pattern_t *pattern) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return TRUE; case CAIRO_PATTERN_TYPE_SURFACE: return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); default: ASSERT_NOT_REACHED; return FALSE; } } static cairo_bool_t _pdf_operator_supported (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return TRUE; default: case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: return FALSE; } } static cairo_int_status_t _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! _pattern_supported (pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; if (_pdf_operator_supported (op)) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { if (pattern->extend == CAIRO_EXTEND_PAD) { cairo_box_t box; cairo_rectangle_int_t rect; cairo_rectangle_int_t rec_extents; /* get the operation extents in pattern space */ _cairo_box_from_rectangle (&box, extents); _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); _cairo_box_round_to_rectangle (&box, &rect); /* Check if surface needs padding to fill extents */ if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) { if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x || _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y || _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width || _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) { return CAIRO_INT_STATUS_UNSUPPORTED; } } } return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } return CAIRO_STATUS_SUCCESS; } /* The SOURCE operator is supported if the pattern is opaque or if * there is nothing painted underneath. */ if (op == CAIRO_OPERATOR_SOURCE) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { if (_cairo_pattern_is_opaque (pattern, extents)) { return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } else { /* FIXME: The analysis surface does not yet have * the capability to analyze a non opaque recording * surface and mark it supported if there is * nothing underneath. For now recording surfaces of * type CONTENT_COLOR_ALPHA painted with * OPERATOR_SOURCE will result in a fallback * image. */ return CAIRO_INT_STATUS_UNSUPPORTED; } } else { return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface, surface_pattern); } } if (_cairo_pattern_is_opaque (pattern, extents)) return CAIRO_STATUS_SUCCESS; else return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_bool_t _cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) { cairo_box_double_t bbox; cairo_int_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) return status; status = _cairo_array_append (&surface->knockout_group, &surface->content); if (unlikely (status)) return status; _cairo_pdf_group_resources_clear (&surface->resources); bbox.p1.x = 0; bbox.p1.y = 0; bbox.p2.x = surface->width; bbox.p2.y = surface->height; return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE); } /* A PDF stencil mask is an A1 mask used with the current color */ static cairo_int_status_t _cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { cairo_int_status_t status; cairo_image_surface_t *image; void *image_extra; cairo_image_transparency_t transparency; cairo_pdf_resource_t pattern_res = {0}; if (! (source->type == CAIRO_PATTERN_TYPE_SOLID && (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) return CAIRO_INT_STATUS_UNSUPPORTED; if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, &image, &image_extra); if (unlikely (status)) return status; if (image->base.status) return image->base.status; transparency = _cairo_image_analyze_transparency (image); if (transparency != CAIRO_IMAGE_IS_OPAQUE && transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) return status; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_surface_pattern (surface, mask, extents, TRUE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->output, "Q\n"); status = _cairo_output_stream_get_status (surface->output); cleanup: _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); return status; } static cairo_int_status_t _cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface, cairo_composite_rectangles_t *composite) { cairo_clip_t *clip = composite->clip; if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) clip = NULL; if (clip == NULL) { if (_cairo_composite_rectangles_can_reduce_clip (composite, surface->clipper.clip)) return CAIRO_STATUS_SUCCESS; } return _cairo_surface_clipper_set_clip (&surface->clipper, clip); } static cairo_int_status_t _cairo_pdf_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_paint (&extents, &surface->base, op, source, clip); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; if (_can_paint_pattern (source)) { _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_surface_paint_pattern (surface, source, &extents.bounded, FALSE); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "Q\n"); _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); } pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } group->operation = PDF_PAINT; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "0 0 %f %f re f\n", surface->width, surface->height); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) goto cleanup; } _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_composite_rectangles_t extents; cairo_int_status_t status; cairo_rectangle_int_t r; cairo_box_t box; status = _cairo_composite_rectangles_init_for_mask (&extents, &surface->base, op, source, mask, clip); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { cairo_int_status_t source_status, mask_status; status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); if (_cairo_int_status_is_error (status)) goto cleanup; source_status = status; if (mask->has_component_alpha) { status = CAIRO_INT_STATUS_UNSUPPORTED; } else { status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); if (_cairo_int_status_is_error (status)) goto cleanup; } mask_status = status; _cairo_composite_rectangles_fini (&extents); return _cairo_analysis_surface_merge_status (source_status, mask_status); } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); /* get the accurate extents */ status = _cairo_pattern_get_ink_extents (source, &r); if (unlikely (status)) goto cleanup; /* XXX slight impedance mismatch */ _cairo_box_from_rectangle (&box, &r); status = _cairo_composite_rectangles_intersect_source_extents (&extents, &box); if (unlikely (status)) goto cleanup; status = _cairo_pattern_get_ink_extents (mask, &r); if (unlikely (status)) goto cleanup; _cairo_box_from_rectangle (&box, &r); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &box); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) goto cleanup; /* Check if we can use a stencil mask */ status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask, &extents.bounded); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto cleanup; group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } group->operation = PDF_MASK; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pattern_create_copy (&group->mask, mask); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->source_res = _cairo_pdf_surface_new_object (surface); if (group->source_res.id == 0) { _cairo_pdf_smask_group_destroy (group); status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, group->group_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->source_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", group->group_res.id, group->source_res.id); _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_pdf_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_stroke (&extents, &surface->base, op, source, path, style, ctm, clip); if (unlikely (status)) return status; /* use the more accurate extents */ if (extents.is_bounded) { cairo_rectangle_int_t mask; cairo_box_t box; status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, &mask); if (unlikely (status)) goto cleanup; _cairo_box_from_rectangle (&box, &mask); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &box); if (unlikely (status)) goto cleanup; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) goto cleanup; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } group->operation = PDF_STROKE; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->style = *style; group->ctm = *ctm; group->ctm_inverse = *ctm_inverse; status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_stroke (&surface->pdf_operators, path, style, ctm, ctm_inverse); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) goto cleanup; } _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_pdf_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; status = _cairo_composite_rectangles_init_for_fill (&extents, &surface->base, op, source, path, clip); if (unlikely (status)) return status; /* use the more accurate extents */ if (extents.is_bounded) { cairo_rectangle_int_t mask; cairo_box_t box; _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, &mask); _cairo_box_from_rectangle (&box, &mask); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &box); if (unlikely (status)) goto cleanup; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) goto cleanup; if (_can_paint_pattern (source)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_paint_pattern (surface, source, &extents.bounded, FALSE); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "Q\n"); status = _cairo_output_stream_get_status (surface->output); goto cleanup; } pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } group->operation = PDF_FILL; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->fill_rule = fill_rule; status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) goto cleanup; } _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; cairo_composite_rectangles_t extents; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis * for us. During render we may still encounter unsupported * combinations of fill/stroke patterns. However we can return * unsupported anytime to let the _fill and _stroke functions take * over. */ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return CAIRO_INT_STATUS_UNSUPPORTED; /* PDF rendering of fill-stroke is not the same as cairo when * either the fill or stroke is not opaque. */ if ( !_cairo_pattern_is_opaque (fill_source, NULL) || !_cairo_pattern_is_opaque (stroke_source, NULL)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (fill_op != stroke_op) return CAIRO_INT_STATUS_UNSUPPORTED; /* Compute the operation extents using the stroke which will naturally * be larger than the fill extents. */ status = _cairo_composite_rectangles_init_for_stroke (&extents, &surface->base, stroke_op, stroke_source, path, stroke_style, stroke_ctm, clip); if (unlikely (status)) return status; /* use the more accurate extents */ if (extents.is_bounded) { cairo_rectangle_int_t mask; cairo_box_t box; status = _cairo_path_fixed_stroke_extents (path, stroke_style, stroke_ctm, stroke_ctm_inverse, stroke_tolerance, &mask); if (unlikely (status)) goto cleanup; _cairo_box_from_rectangle (&box, &mask); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &box); if (unlikely (status)) goto cleanup; } status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, fill_op); if (unlikely (status)) goto cleanup; /* use the more accurate extents */ if (extents.is_bounded) { cairo_rectangle_int_t mask; cairo_box_t box; _cairo_path_fixed_fill_extents (path, fill_rule, fill_tolerance, &mask); _cairo_box_from_rectangle (&box, &mask); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &box); if (unlikely (status)) goto cleanup; } fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, &extents.bounded, &fill_pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; assert (gstate_res.id == 0); stroke_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, &extents.bounded, &stroke_pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; assert (gstate_res.id == 0); /* As PDF has separate graphics state for fill and stroke we can * select both at the same time */ status = _cairo_pdf_surface_select_pattern (surface, fill_source, fill_pattern_res, FALSE); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_pattern (surface, stroke_source, stroke_pattern_res, TRUE); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, path, fill_rule, stroke_style, stroke_ctm, stroke_ctm_inverse); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) goto cleanup; _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_bool_t _cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) { return TRUE; } static cairo_int_status_t _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; cairo_bool_t overlap; cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_glyphs (&extents, &surface->base, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup; pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, source, &extents.bounded, &pattern_res, &gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) goto cleanup; if (gstate_res.id != 0) { group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); if (unlikely (group == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } group->operation = PDF_SHOW_GLYPHS; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } group->source_res = pattern_res; if (utf8_len) { group->utf8 = malloc (utf8_len); if (unlikely (group->utf8 == NULL)) { _cairo_pdf_smask_group_destroy (group); status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } memcpy (group->utf8, utf8, utf8_len); } group->utf8_len = utf8_len; if (num_glyphs) { group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (group->glyphs == NULL)) { _cairo_pdf_smask_group_destroy (group); status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } group->num_glyphs = num_glyphs; if (num_clusters) { group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); if (unlikely (group->clusters == NULL)) { _cairo_pdf_smask_group_destroy (group); status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto cleanup; } memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); } group->num_clusters = num_clusters; group->scaled_font = cairo_scaled_font_reference (scaled_font); status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", gstate_res.id, group->group_res.id); } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) goto cleanup; /* Each call to show_glyphs() with a transclucent pattern must * be in a separate text object otherwise overlapping text * from separate calls to show_glyphs will not composite with * each other. */ if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup; } status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font); if (unlikely (status)) goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) goto cleanup; } _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); cleanup: _cairo_composite_rectangles_fini (&extents); return status; } static const char ** _cairo_pdf_surface_get_supported_mime_types (void *abstract_surface) { return _cairo_pdf_supported_mime_types; } static void _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_pdf_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; } static const cairo_surface_backend_t cairo_pdf_surface_backend = { CAIRO_SURFACE_TYPE_PDF, _cairo_pdf_surface_finish, _cairo_default_context_create, NULL, /* create similar: handled by wrapper */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, _cairo_pdf_surface_get_extents, _cairo_pdf_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ /* Here are the drawing functions */ _cairo_pdf_surface_paint, _cairo_pdf_surface_mask, _cairo_pdf_surface_stroke, _cairo_pdf_surface_fill, _cairo_pdf_surface_fill_stroke, NULL, /* show_glyphs */ _cairo_pdf_surface_has_show_text_glyphs, _cairo_pdf_surface_show_text_glyphs, _cairo_pdf_surface_get_supported_mime_types, }; static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend = { _cairo_pdf_surface_start_page, _cairo_pdf_surface_set_paginated_mode, NULL, /* set_bounding_box */ _cairo_pdf_surface_has_fallback_images, _cairo_pdf_surface_supports_fine_grained_fallbacks, }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pdf.h�������������������������������������0000664�0000000�0000000�00000006100�12710376503�0024312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_PDF_H #define CAIRO_PDF_H #include "cairo.h" #if CAIRO_HAS_PDF_SURFACE CAIRO_BEGIN_DECLS /** * cairo_pdf_version_t: * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. (Since 1.10) * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. (Since 1.10) * * #cairo_pdf_version_t is used to describe the version number of the PDF * specification that a generated PDF file will conform to. * * Since: 1.10 **/ typedef enum _cairo_pdf_version { CAIRO_PDF_VERSION_1_4, CAIRO_PDF_VERSION_1_5 } cairo_pdf_version_t; cairo_public cairo_surface_t * cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points); cairo_public cairo_surface_t * cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points); cairo_public void cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface, cairo_pdf_version_t version); cairo_public void cairo_pdf_get_versions (cairo_pdf_version_t const **versions, int *num_versions); cairo_public const char * cairo_pdf_version_to_string (cairo_pdf_version_t version); cairo_public void cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points); CAIRO_END_DECLS #else /* CAIRO_HAS_PDF_SURFACE */ # error Cairo was not compiled with support for the pdf backend #endif /* CAIRO_HAS_PDF_SURFACE */ #endif /* CAIRO_PDF_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pen.c�������������������������������������0000664�0000000�0000000�00000032711�12710376503�0024325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-slope-private.h" static void _cairo_pen_compute_slopes (cairo_pen_t *pen); cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, double tolerance, const cairo_matrix_t *ctm) { int i; int reflect; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); pen->radius = radius; pen->tolerance = tolerance; reflect = _cairo_matrix_compute_determinant (ctm) < 0.; pen->num_vertices = _cairo_pen_vertices_needed (tolerance, radius, ctm); if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { pen->vertices = _cairo_malloc_ab (pen->num_vertices, sizeof (cairo_pen_vertex_t)); if (unlikely (pen->vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { pen->vertices = pen->vertices_embedded; } /* * Compute pen coordinates. To generate the right ellipse, compute points around * a circle in user space and transform them to device space. To get a consistent * orientation in device space, flip the pen if the transformation matrix * is reflecting */ for (i=0; i < pen->num_vertices; i++) { cairo_pen_vertex_t *v = &pen->vertices[i]; double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy; if (reflect) theta = -theta; dx = radius * cos (theta); dy = radius * sin (theta); cairo_matrix_transform_distance (ctm, &dx, &dy); v->point.x = _cairo_fixed_from_double (dx); v->point.y = _cairo_fixed_from_double (dy); } _cairo_pen_compute_slopes (pen); return CAIRO_STATUS_SUCCESS; } void _cairo_pen_fini (cairo_pen_t *pen) { if (pen->vertices != pen->vertices_embedded) free (pen->vertices); VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t))); } cairo_status_t _cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) { VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); *pen = *other; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pen->vertices = pen->vertices_embedded; if (pen->num_vertices) { if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { pen->vertices = _cairo_malloc_ab (pen->num_vertices, sizeof (cairo_pen_vertex_t)); if (unlikely (pen->vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t)); } return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) { cairo_status_t status; int num_vertices; int i; if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); num_vertices = pen->num_vertices + num_points; if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || pen->vertices != pen->vertices_embedded) { cairo_pen_vertex_t *vertices; if (pen->vertices == pen->vertices_embedded) { vertices = _cairo_malloc_ab (num_vertices, sizeof (cairo_pen_vertex_t)); if (unlikely (vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (vertices, pen->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t)); } else { vertices = _cairo_realloc_ab (pen->vertices, num_vertices, sizeof (cairo_pen_vertex_t)); if (unlikely (vertices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pen->vertices = vertices; } pen->num_vertices = num_vertices; /* initialize new vertices */ for (i=0; i < num_points; i++) pen->vertices[pen->num_vertices-num_points+i].point = point[i]; status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); if (unlikely (status)) return status; _cairo_pen_compute_slopes (pen); return CAIRO_STATUS_SUCCESS; } /* The circular pen in user space is transformed into an ellipse in device space. We construct the pen by computing points along the circumference using equally spaced angles. We show that this approximation to the ellipse has maximum error at the major axis of the ellipse. Set M = major axis length m = minor axis length Align 'M' along the X axis and 'm' along the Y axis and draw an ellipse parameterized by angle 't': x = M cos t y = m sin t Perturb t by ± d and compute two new points (x+,y+), (x-,y-). The distance from the average of these two points to (x,y) represents the maximum error in approximating the ellipse with a polygon formed from vertices 2∆ radians apart. x+ = M cos (t+∆) y+ = m sin (t+∆) x- = M cos (t-∆) y- = m sin (t-∆) Now compute the approximation error, E: Ex = (x - (x+ + x-) / 2) Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + cos(t)cos(∆) - sin(t)sin(∆))/2) = M(cos(t) - cos(t)cos(∆)) = M cos(t) (1 - cos(∆)) Ey = y - (y+ - y-) / 2 = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + sin(t)cos(∆) - cos(t)sin(∆))/2) = m (sin(t) - sin(t)cos(∆)) = m sin(t) (1 - cos(∆)) E² = Ex² + Ey² = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² Find the extremum by differentiation wrt t and setting that to zero ∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) 0 = 2 cos (t) sin (t) 0 = sin (2t) t = nÏ€ Which is to say that the maximum and minimum errors occur on the axes of the ellipse at 0 and Ï€ radians: E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² = (1-cos(∆))² M² E²(Ï€) = (1-cos(∆))² m² maximum error = M (1-cos(∆)) minimum error = m (1-cos(∆)) We must make maximum error ≤ tolerance, so compute the ∆ needed: tolerance = M (1-cos(∆)) tolerance / M = 1 - cos (∆) cos(∆) = 1 - tolerance/M ∆ = acos (1 - tolerance / M); Remembering that ∆ is half of our angle between vertices, the number of vertices is then vertices = ceil(2Ï€/2∆). = ceil(Ï€/∆). Note that this also equation works for M == m (a circle) as it doesn't matter where on the circle the error is computed. */ int _cairo_pen_vertices_needed (double tolerance, double radius, const cairo_matrix_t *matrix) { /* * the pen is a circle that gets transformed to an ellipse by matrix. * compute major axis length for a pen with the specified radius. * we don't need the minor axis length. */ double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, radius); int num_vertices; if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */ num_vertices = 1; } else if (tolerance >= major_axis) { num_vertices = 4; } else { num_vertices = ceil (2*M_PI / acos (1 - tolerance / major_axis)); /* number of vertices must be even */ if (num_vertices % 2) num_vertices++; /* And we must always have at least 4 vertices. */ if (num_vertices < 4) num_vertices = 4; } return num_vertices; } static void _cairo_pen_compute_slopes (cairo_pen_t *pen) { int i, i_prev; cairo_pen_vertex_t *prev, *v, *next; for (i=0, i_prev = pen->num_vertices - 1; i < pen->num_vertices; i_prev = i++) { prev = &pen->vertices[i_prev]; v = &pen->vertices[i]; next = &pen->vertices[(i + 1) % pen->num_vertices]; _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); } } /* * Find active pen vertex for clockwise edge of stroke at the given slope. * * The strictness of the inequalities here is delicate. The issue is * that the slope_ccw member of one pen vertex will be equivalent to * the slope_cw member of the next pen vertex in a counterclockwise * order. However, for this function, we care strongly about which * vertex is returned. * * [I think the "care strongly" above has to do with ensuring that the * pen's "extra points" from the spline's initial and final slopes are * properly found when beginning the spline stroking.] */ int _cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope) { int i; for (i=0; i < pen->num_vertices; i++) { if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) break; } /* If the desired slope cannot be found between any of the pen * vertices, then we must have a degenerate pen, (such as a pen * that's been transformed to a line). In that case, we consider * the first pen vertex as the appropriate clockwise vertex. */ if (i == pen->num_vertices) i = 0; return i; } /* Find active pen vertex for counterclockwise edge of stroke at the given slope. * * Note: See the comments for _cairo_pen_find_active_cw_vertex_index * for some details about the strictness of the inequalities here. */ int _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope) { cairo_slope_t slope_reverse; int i; slope_reverse = *slope; slope_reverse.dx = -slope_reverse.dx; slope_reverse.dy = -slope_reverse.dy; for (i=pen->num_vertices-1; i >= 0; i--) { if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) break; } /* If the desired slope cannot be found between any of the pen * vertices, then we must have a degenerate pen, (such as a pen * that's been transformed to a line). In that case, we consider * the last pen vertex as the appropriate counterclockwise vertex. */ if (i < 0) i = pen->num_vertices - 1; return i; } void _cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, const cairo_slope_t *in, const cairo_slope_t *out, int *start, int *stop) { int lo = 0, hi = pen->num_vertices; int i; i = (lo + hi) >> 1; do { if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) lo = i; else hi = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) if (++i == pen->num_vertices) i = 0; *start = i; if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) { lo = i; hi = i + pen->num_vertices; i = (lo + hi) >> 1; do { int j = i; if (j >= pen->num_vertices) j -= pen->num_vertices; if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0) hi = i; else lo = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (i >= pen->num_vertices) i -= pen->num_vertices; } *stop = i; } void _cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen, const cairo_slope_t *in, const cairo_slope_t *out, int *start, int *stop) { int lo = 0, hi = pen->num_vertices; int i; i = (lo + hi) >> 1; do { if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) lo = i; else hi = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) if (++i == pen->num_vertices) i = 0; *start = i; if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) { lo = i; hi = i + pen->num_vertices; i = (lo + hi) >> 1; do { int j = i; if (j >= pen->num_vertices) j -= pen->num_vertices; if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0) hi = i; else lo = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (i >= pen->num_vertices) i -= pen->num_vertices; } *stop = i; } �������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-pixman-private.h��������������������������0000664�0000000�0000000�00000003677�12710376503�0026525�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright ©2013 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_PIXMAN_PRIVATE_H #define CAIRO_PIXMAN_PRIVATE_H #include "cairo-pixman-private.h" /* keep make check happy */ #include <pixman.h> #if PIXMAN_VERSION < PIXMAN_VERSION_ENCODE(0,22,0) #define pixman_image_composite32 pixman_image_composite #define pixman_image_get_component_alpha(i) 0 #define pixman_image_set_component_alpha(i, x) do { } while (0) #endif #endif �����������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-png.c�������������������������������������0000664�0000000�0000000�00000054310�12710376503�0024326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-output-stream-private.h" #include <stdio.h> #include <errno.h> #include <png.h> /** * SECTION:cairo-png * @Title: PNG Support * @Short_Description: Reading and writing PNG images * @See_Also: #cairo_surface_t * * The PNG functions allow reading PNG images into image surfaces, and writing * any surface to a PNG file. * * It is a toy API. It only offers very simple support for reading and * writing PNG files, which is sufficient for testing and * demonstration purposes. Applications which need more control over * the generated PNG file should access the pixel data directly, using * cairo_image_surface_get_data() or a backend-specific access * function, and process it with another library, e.g. gdk-pixbuf or * libpng. **/ /** * CAIRO_HAS_PNG_FUNCTIONS: * * Defined if the PNG functions are available. * This macro can be used to conditionally compile code using the cairo * PNG functions. * * Since: 1.0 **/ struct png_read_closure_t { cairo_read_func_t read_func; void *closure; cairo_output_stream_t *png_data; }; /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ static void unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *b = &data[i]; uint32_t pixel; uint8_t alpha; memcpy (&pixel, b, sizeof (uint32_t)); alpha = (pixel & 0xff000000) >> 24; if (alpha == 0) { b[0] = b[1] = b[2] = b[3] = 0; } else { b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; b[3] = alpha; } } } /* Converts native endian xRGB => RGBx bytes */ static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *b = &data[i]; uint32_t pixel; memcpy (&pixel, b, sizeof (uint32_t)); b[0] = (pixel & 0xff0000) >> 16; b[1] = (pixel & 0x00ff00) >> 8; b[2] = (pixel & 0x0000ff) >> 0; b[3] = 0; } } /* Use a couple of simple error callbacks that do not print anything to * stderr and rely on the user to check for errors via the #cairo_status_t * return. */ static void png_simple_error_callback (png_structp png, png_const_charp error_msg) { cairo_status_t *error = png_get_error_ptr (png); /* default to the most likely error */ if (*error == CAIRO_STATUS_SUCCESS) *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); #ifdef PNG_SETJMP_SUPPORTED longjmp (png_jmpbuf (png), 1); #endif /* if we get here, then we have to choice but to abort ... */ } static void png_simple_warning_callback (png_structp png, png_const_charp error_msg) { /* png does not expect to abort and will try to tidy up and continue * loading the image after a warning. So we also want to return the * (incorrect?) surface. * * We use our own warning callback to squelch any attempts by libpng * to write to stderr as we may not be in control of that output. */ } /* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn. * Otherwise, we will segfault if we are writing to a stream. */ static void png_simple_output_flush_fn (png_structp png_ptr) { } static cairo_status_t write_png (cairo_surface_t *surface, png_rw_ptr write_func, void *closure) { int i; cairo_int_status_t status; cairo_image_surface_t *image; cairo_image_surface_t * volatile clone; void *image_extra; png_struct *png; png_info *info; png_byte **volatile rows = NULL; png_color_16 white; int png_color_type; int bpc; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (status == CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); else if (unlikely (status)) return status; /* PNG complains about "Image width or height is zero in IHDR" */ if (image->width == 0 || image->height == 0) { status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); goto BAIL1; } /* Handle the various fallback formats (e.g. low bit-depth XServers) * by coercing them to a simpler format using pixman. */ clone = _cairo_image_surface_coerce (image); status = clone->base.status; if (unlikely (status)) goto BAIL1; rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*)); if (unlikely (rows == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL2; } for (i = 0; i < clone->height; i++) rows[i] = (png_byte *) clone->data + i * clone->stride; png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status, png_simple_error_callback, png_simple_warning_callback); if (unlikely (png == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL3; } info = png_create_info_struct (png); if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL4; } #ifdef PNG_SETJMP_SUPPORTED if (setjmp (png_jmpbuf (png))) goto BAIL4; #endif png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn); switch (clone->format) { case CAIRO_FORMAT_ARGB32: bpc = 8; if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE) png_color_type = PNG_COLOR_TYPE_RGB; else png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; case CAIRO_FORMAT_RGB30: bpc = 10; png_color_type = PNG_COLOR_TYPE_RGB; break; case CAIRO_FORMAT_RGB24: bpc = 8; png_color_type = PNG_COLOR_TYPE_RGB; break; case CAIRO_FORMAT_A8: bpc = 8; png_color_type = PNG_COLOR_TYPE_GRAY; break; case CAIRO_FORMAT_A1: bpc = 1; png_color_type = PNG_COLOR_TYPE_GRAY; #ifndef WORDS_BIGENDIAN png_set_packswap (png); #endif break; case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: default: status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); goto BAIL4; } png_set_IHDR (png, info, clone->width, clone->height, bpc, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); white.gray = (1 << bpc) - 1; white.red = white.blue = white.green = white.gray; png_set_bKGD (png, info, &white); if (0) { /* XXX extract meta-data from surface (i.e. creation date) */ png_time pt; png_convert_from_time_t (&pt, time (NULL)); png_set_tIME (png, info, &pt); } /* We have to call png_write_info() before setting up the write * transformation, since it stores data internally in 'png' * that is needed for the write transformation functions to work. */ png_write_info (png, info); if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_set_write_user_transform_fn (png, unpremultiply_data); } else if (png_color_type == PNG_COLOR_TYPE_RGB) { png_set_write_user_transform_fn (png, convert_data_to_bytes); png_set_filler (png, 0, PNG_FILLER_AFTER); } png_write_image (png, rows); png_write_end (png, info); BAIL4: png_destroy_write_struct (&png, &info); BAIL3: free (rows); BAIL2: cairo_surface_destroy (&clone->base); BAIL1: _cairo_surface_release_source_image (surface, image, image_extra); return status; } static void stdio_write_func (png_structp png, png_bytep data, png_size_t size) { FILE *fp; fp = png_get_io_ptr (png); while (size) { size_t ret = fwrite (data, 1, size, fp); size -= ret; data += ret; if (size && ferror (fp)) { cairo_status_t *error = png_get_error_ptr (png); if (*error == CAIRO_STATUS_SUCCESS) *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR); png_error (png, NULL); } } } /** * cairo_surface_write_to_png: * @surface: a #cairo_surface_t with pixel contents * @filename: the name of a file to write to * * Writes the contents of @surface to a new file @filename as a PNG * image. * * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not * be allocated for the operation or * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs * while attempting to write the file. * * Since: 1.0 **/ cairo_status_t cairo_surface_write_to_png (cairo_surface_t *surface, const char *filename) { FILE *fp; cairo_status_t status; if (surface->status) return surface->status; if (surface->finished) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); fp = fopen (filename, "wb"); if (fp == NULL) { switch (errno) { case ENOMEM: return _cairo_error (CAIRO_STATUS_NO_MEMORY); default: return _cairo_error (CAIRO_STATUS_WRITE_ERROR); } } status = write_png (surface, stdio_write_func, fp); if (fclose (fp) && status == CAIRO_STATUS_SUCCESS) status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); return status; } struct png_write_closure_t { cairo_write_func_t write_func; void *closure; }; static void stream_write_func (png_structp png, png_bytep data, png_size_t size) { cairo_status_t status; struct png_write_closure_t *png_closure; png_closure = png_get_io_ptr (png); status = png_closure->write_func (png_closure->closure, data, size); if (unlikely (status)) { cairo_status_t *error = png_get_error_ptr (png); if (*error == CAIRO_STATUS_SUCCESS) *error = status; png_error (png, NULL); } } /** * cairo_surface_write_to_png_stream: * @surface: a #cairo_surface_t with pixel contents * @write_func: a #cairo_write_func_t * @closure: closure data for the write function * * Writes the image surface to the write function. * * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if * memory could not be allocated for the operation, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have * pixel contents. * * Since: 1.0 **/ cairo_status_t cairo_surface_write_to_png_stream (cairo_surface_t *surface, cairo_write_func_t write_func, void *closure) { struct png_write_closure_t png_closure; if (surface->status) return surface->status; if (surface->finished) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); png_closure.write_func = write_func; png_closure.closure = closure; return write_png (surface, stream_write_func, &png_closure); } slim_hidden_def (cairo_surface_write_to_png_stream); static inline int multiply_alpha (int alpha, int color) { int temp = (alpha * color) + 0x80; return ((temp + (temp >> 8)) >> 8); } /* Premultiplies data and converts RGBA bytes => native endian */ static void premultiply_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *base = &data[i]; uint8_t alpha = base[3]; uint32_t p; if (alpha == 0) { p = 0; } else { uint8_t red = base[0]; uint8_t green = base[1]; uint8_t blue = base[2]; if (alpha != 0xff) { red = multiply_alpha (alpha, red); green = multiply_alpha (alpha, green); blue = multiply_alpha (alpha, blue); } p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); } memcpy (base, &p, sizeof (uint32_t)); } } /* Converts RGBx bytes to native endian xRGB */ static void convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *base = &data[i]; uint8_t red = base[0]; uint8_t green = base[1]; uint8_t blue = base[2]; uint32_t pixel; pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); memcpy (base, &pixel, sizeof (uint32_t)); } } static cairo_status_t stdio_read_func (void *closure, unsigned char *data, unsigned int size) { FILE *file = closure; while (size) { size_t ret; ret = fread (data, 1, size, file); size -= ret; data += ret; if (size && (feof (file) || ferror (file))) return _cairo_error (CAIRO_STATUS_READ_ERROR); } return CAIRO_STATUS_SUCCESS; } static void stream_read_func (png_structp png, png_bytep data, png_size_t size) { cairo_status_t status; struct png_read_closure_t *png_closure; png_closure = png_get_io_ptr (png); status = png_closure->read_func (png_closure->closure, data, size); if (unlikely (status)) { cairo_status_t *error = png_get_error_ptr (png); if (*error == CAIRO_STATUS_SUCCESS) *error = status; png_error (png, NULL); } _cairo_output_stream_write (png_closure->png_data, data, size); } static cairo_surface_t * read_png (struct png_read_closure_t *png_closure) { cairo_surface_t *surface; png_struct *png = NULL; png_info *info; png_byte *data = NULL; png_byte **row_pointers = NULL; png_uint_32 png_width, png_height; int depth, color_type, interlace, stride; unsigned int i; cairo_format_t format; cairo_status_t status; unsigned char *mime_data; unsigned long mime_data_length; png_closure->png_data = _cairo_memory_stream_create (); /* XXX: Perhaps we'll want some other error handlers? */ png = png_create_read_struct (PNG_LIBPNG_VER_STRING, &status, png_simple_error_callback, png_simple_warning_callback); if (unlikely (png == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } info = png_create_info_struct (png); if (unlikely (info == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } png_set_read_fn (png, png_closure, stream_read_func); status = CAIRO_STATUS_SUCCESS; #ifdef PNG_SETJMP_SUPPORTED if (setjmp (png_jmpbuf (png))) { surface = _cairo_surface_create_in_error (status); goto BAIL; } #endif png_read_info (png, info); png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); if (unlikely (status)) { /* catch any early warnings */ surface = _cairo_surface_create_in_error (status); goto BAIL; } /* convert palette/gray image to rgb */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb (png); /* expand gray bit depth if needed */ if (color_type == PNG_COLOR_TYPE_GRAY) { #if PNG_LIBPNG_VER >= 10209 png_set_expand_gray_1_2_4_to_8 (png); #else png_set_gray_1_2_4_to_8 (png); #endif } /* transform transparency to alpha */ if (png_get_valid (png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha (png); if (depth == 16) png_set_strip_16 (png); if (depth < 8) png_set_packing (png); /* convert grayscale to RGB */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb (png); } if (interlace != PNG_INTERLACE_NONE) png_set_interlace_handling (png); png_set_filler (png, 0xff, PNG_FILLER_AFTER); /* recheck header after setting EXPAND options */ png_read_update_info (png, info); png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); if (depth != 8 || ! (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR)); goto BAIL; } switch (color_type) { default: ASSERT_NOT_REACHED; /* fall-through just in case ;-) */ case PNG_COLOR_TYPE_RGB_ALPHA: format = CAIRO_FORMAT_ARGB32; png_set_read_user_transform_fn (png, premultiply_data); break; case PNG_COLOR_TYPE_RGB: format = CAIRO_FORMAT_RGB24; png_set_read_user_transform_fn (png, convert_bytes_to_data); break; } stride = cairo_format_stride_for_width (format, png_width); if (stride < 0) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); goto BAIL; } data = _cairo_malloc_ab (png_height, stride); if (unlikely (data == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } row_pointers = _cairo_malloc_ab (png_height, sizeof (char *)); if (unlikely (row_pointers == NULL)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto BAIL; } for (i = 0; i < png_height; i++) row_pointers[i] = &data[i * stride]; png_read_image (png, row_pointers); png_read_end (png, info); if (unlikely (status)) { /* catch any late warnings - probably hit an error already */ surface = _cairo_surface_create_in_error (status); goto BAIL; } surface = cairo_image_surface_create_for_data (data, format, png_width, png_height, stride); if (surface->status) goto BAIL; _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); data = NULL; _cairo_debug_check_image_surface_is_defined (surface); status = _cairo_memory_stream_destroy (png_closure->png_data, &mime_data, &mime_data_length); png_closure->png_data = NULL; if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); goto BAIL; } status = cairo_surface_set_mime_data (surface, CAIRO_MIME_TYPE_PNG, mime_data, mime_data_length, free, mime_data); if (unlikely (status)) { free (mime_data); cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); goto BAIL; } BAIL: free (row_pointers); free (data); if (png != NULL) png_destroy_read_struct (&png, &info, NULL); if (png_closure->png_data != NULL) { cairo_status_t status_ignored; status_ignored = _cairo_output_stream_destroy (png_closure->png_data); } return surface; } /** * cairo_image_surface_create_from_png: * @filename: name of PNG file to load * * Creates a new image surface and initializes the contents to the * given PNG file. * * Return value: a new #cairo_surface_t initialized with the contents * of the PNG file, or a "nil" surface if any error occurred. A nil * surface can be checked for with cairo_surface_status(surface) which * may return one of the following values: * * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_FILE_NOT_FOUND * %CAIRO_STATUS_READ_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion * using cairo_status(). * * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create_from_png (const char *filename) { struct png_read_closure_t png_closure; cairo_surface_t *surface; png_closure.closure = fopen (filename, "rb"); if (png_closure.closure == NULL) { cairo_status_t status; switch (errno) { case ENOMEM: status = _cairo_error (CAIRO_STATUS_NO_MEMORY); break; case ENOENT: status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); break; default: status = _cairo_error (CAIRO_STATUS_READ_ERROR); break; } return _cairo_surface_create_in_error (status); } png_closure.read_func = stdio_read_func; surface = read_png (&png_closure); fclose (png_closure.closure); return surface; } /** * cairo_image_surface_create_from_png_stream: * @read_func: function called to read the data of the file * @closure: data to pass to @read_func. * * Creates a new image surface from PNG data read incrementally * via the @read_func function. * * Return value: a new #cairo_surface_t initialized with the contents * of the PNG file or a "nil" surface if the data read is not a valid PNG image * or memory could not be allocated for the operation. A nil * surface can be checked for with cairo_surface_status(surface) which * may return one of the following values: * * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_READ_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion * using cairo_status(). * * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure) { struct png_read_closure_t png_closure; png_closure.read_func = read_func; png_closure.closure = closure; return read_png (&png_closure); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-polygon-intersect.c�����������������������0000664�0000000�0000000�00000124154�12710376503�0027233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-inline.h" typedef cairo_point_t cairo_bo_point32_t; typedef struct _cairo_bo_intersect_ordinate { int32_t ordinate; enum { EXACT, INEXACT } exactness; } cairo_bo_intersect_ordinate_t; typedef struct _cairo_bo_intersect_point { cairo_bo_intersect_ordinate_t x; cairo_bo_intersect_ordinate_t y; } cairo_bo_intersect_point_t; typedef struct _cairo_bo_edge cairo_bo_edge_t; typedef struct _cairo_bo_deferred { cairo_bo_edge_t *other; int32_t top; } cairo_bo_deferred_t; struct _cairo_bo_edge { int a_or_b; cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; cairo_bo_deferred_t deferred; }; /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef enum { CAIRO_BO_EVENT_TYPE_STOP, CAIRO_BO_EVENT_TYPE_INTERSECTION, CAIRO_BO_EVENT_TYPE_START } cairo_bo_event_type_t; typedef struct _cairo_bo_event { cairo_bo_event_type_t type; cairo_point_t point; } cairo_bo_event_t; typedef struct _cairo_bo_start_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t edge; } cairo_bo_start_event_t; typedef struct _cairo_bo_queue_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t *e1; cairo_bo_edge_t *e2; } cairo_bo_queue_event_t; typedef struct _pqueue { int size, max_size; cairo_bo_event_t **elements; cairo_bo_event_t *elements_embedded[1024]; } pqueue_t; typedef struct _cairo_bo_event_queue { cairo_freepool_t pool; pqueue_t pqueue; cairo_bo_event_t **start_events; } cairo_bo_event_queue_t; typedef struct _cairo_bo_sweep_line { cairo_bo_edge_t *head; int32_t current_y; cairo_bo_edge_t *current_edge; } cairo_bo_sweep_line_t; static cairo_fixed_t _line_compute_intersection_x_for_y (const cairo_line_t *line, cairo_fixed_t y) { cairo_fixed_t x, dy; if (y == line->p1.y) return line->p1.x; if (y == line->p2.y) return line->p2.x; x = line->p1.x; dy = line->p2.y - line->p1.y; if (dy != 0) { x += _cairo_fixed_mul_div_floor (y - line->p1.y, line->p2.x - line->p1.x, dy); } return x; } static inline int _cairo_bo_point32_compare (cairo_bo_point32_t const *a, cairo_bo_point32_t const *b) { int cmp; cmp = a->y - b->y; if (cmp) return cmp; return a->x - b->x; } /* Compare the slope of a to the slope of b, returning 1, 0, -1 if the * slope a is respectively greater than, equal to, or less than the * slope of b. * * For each edge, consider the direction vector formed from: * * top -> bottom * * which is: * * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) * * We then define the slope of each edge as dx/dy, (which is the * inverse of the slope typically used in math instruction). We never * compute a slope directly as the value approaches infinity, but we * can derive a slope comparison without division as follows, (where * the ? represents our compare operator). * * 1. slope(a) ? slope(b) * 2. adx/ady ? bdx/bdy * 3. (adx * bdy) ? (bdx * ady) * * Note that from step 2 to step 3 there is no change needed in the * sign of the result since both ady and bdy are guaranteed to be * greater than or equal to 0. * * When using this slope comparison to sort edges, some care is needed * when interpreting the results. Since the slope compare operates on * distance vectors from top to bottom it gives a correct left to * right sort for edges that have a common top point, (such as two * edges with start events at the same location). On the other hand, * the sense of the result will be exactly reversed for two edges that * have a common stop point. */ static inline int _slope_compare (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; /* Since the dy's are all positive by construction we can fast * path several common cases. */ /* First check for vertical lines. */ if (adx == 0) return -bdx; if (bdx == 0) return adx; /* Then where the two edges point in different directions wrt x. */ if ((adx ^ bdx) < 0) return adx; /* Finally we actually need to do the general comparison. */ { int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } } /* * We need to compare the x-coordinates of a pair of lines for a particular y, * without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 128 bit arithmetic. For certain, but common, * input we can reduce this down to a single 32 bit compare by inspecting the * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) * * See the similar discussion for _slope_compare(). */ static int edges_compare_x_for_y_general (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t dx; int32_t adx, ady; int32_t bdx, bdy; enum { HAVE_NONE = 0x0, HAVE_DX = 0x1, HAVE_ADX = 0x2, HAVE_DX_ADX = HAVE_DX | HAVE_ADX, HAVE_BDX = 0x4, HAVE_DX_BDX = HAVE_DX | HAVE_BDX, HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX } have_dx_adx_bdx = HAVE_ALL; /* don't bother solving for abscissa if the edges' bounding boxes * can be used to order them. */ { int32_t amin, amax; int32_t bmin, bmax; if (a->edge.line.p1.x < a->edge.line.p2.x) { amin = a->edge.line.p1.x; amax = a->edge.line.p2.x; } else { amin = a->edge.line.p2.x; amax = a->edge.line.p1.x; } if (b->edge.line.p1.x < b->edge.line.p2.x) { bmin = b->edge.line.p1.x; bmax = b->edge.line.p2.x; } else { bmin = b->edge.line.p2.x; bmax = b->edge.line.p1.x; } if (amax < bmin) return -1; if (amin > bmax) return +1; } ady = a->edge.line.p2.y - a->edge.line.p1.y; adx = a->edge.line.p2.x - a->edge.line.p1.x; if (adx == 0) have_dx_adx_bdx &= ~HAVE_ADX; bdy = b->edge.line.p2.y - b->edge.line.p1.y; bdx = b->edge.line.p2.x - b->edge.line.p1.x; if (bdx == 0) have_dx_adx_bdx &= ~HAVE_BDX; dx = a->edge.line.p1.x - b->edge.line.p1.x; if (dx == 0) have_dx_adx_bdx &= ~HAVE_DX; #define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) #define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) #define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) switch (have_dx_adx_bdx) { default: case HAVE_NONE: return 0; case HAVE_DX: /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ return dx; /* ady * bdy is positive definite */ case HAVE_ADX: /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ return adx; /* bdy * (y - a->top.y) is positive definite */ case HAVE_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy */ return -bdx; /* ady * (y - b->top.y) is positive definite */ case HAVE_ADX_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ if ((adx ^ bdx) < 0) { return adx; } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ cairo_int64_t adx_bdy, bdx_ady; /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ adx_bdy = _cairo_int32x32_64_mul (adx, bdy); bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } else return _cairo_int128_cmp (A, B); case HAVE_DX_ADX: /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ if ((-adx ^ dx) < 0) { return dx; } else { cairo_int64_t ady_dx, dy_adx; ady_dx = _cairo_int32x32_64_mul (ady, dx); dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); return _cairo_int64_cmp (ady_dx, dy_adx); } case HAVE_DX_BDX: /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ if ((bdx ^ dx) < 0) { return dx; } else { cairo_int64_t bdy_dx, dy_bdx; bdy_dx = _cairo_int32x32_64_mul (bdy, dx); dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); return _cairo_int64_cmp (bdy_dx, dy_bdx); } case HAVE_ALL: /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); } #undef B #undef A #undef L } /* * We need to compare the x-coordinate of a line for a particular y wrt to a * given x, without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ X * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. * * See the similar discussion for _slope_compare() and * edges_compare_x_for_y_general(). */ static int edge_compare_for_y_against_x (const cairo_bo_edge_t *a, int32_t y, int32_t x) { int32_t adx, ady; int32_t dx, dy; cairo_int64_t L, R; if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) return 1; if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) return -1; adx = a->edge.line.p2.x - a->edge.line.p1.x; dx = x - a->edge.line.p1.x; if (adx == 0) return -dx; if (dx == 0 || (adx ^ dx) < 0) return adx; dy = y - a->edge.line.p1.y; ady = a->edge.line.p2.y - a->edge.line.p1.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); return _cairo_int64_cmp (L, R); } static int edges_compare_x_for_y (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* If the sweep-line is currently on an end-point of a line, * then we know its precise x value (and considering that we often need to * compare events at end-points, this happens frequently enough to warrant * special casing). */ enum { HAVE_NEITHER = 0x0, HAVE_AX = 0x1, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; int32_t ax, bx; if (y == a->edge.line.p1.y) ax = a->edge.line.p1.x; else if (y == a->edge.line.p2.y) ax = a->edge.line.p2.x; else have_ax_bx &= ~HAVE_AX; if (y == b->edge.line.p1.y) bx = b->edge.line.p1.x; else if (y == b->edge.line.p2.y) bx = b->edge.line.p2.x; else have_ax_bx &= ~HAVE_BX; switch (have_ax_bx) { default: case HAVE_NEITHER: return edges_compare_x_for_y_general (a, b, y); case HAVE_AX: return -edge_compare_for_y_against_x (b, y, ax); case HAVE_BX: return edge_compare_for_y_against_x (a, y, bx); case HAVE_BOTH: return ax - bx; } } static inline int _line_equal (const cairo_line_t *a, const cairo_line_t *b) { return a->p1.x == b->p1.x && a->p1.y == b->p1.y && a->p2.x == b->p2.x && a->p2.y == b->p2.y; } static int _cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { int cmp; /* compare the edges if not identical */ if (! _line_equal (&a->edge.line, &b->edge.line)) { cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); if (cmp) return cmp; /* The two edges intersect exactly at y, so fall back on slope * comparison. We know that this compare_edges function will be * called only when starting a new edge, (not when stopping an * edge), so we don't have to worry about conditionally inverting * the sense of _slope_compare. */ cmp = _slope_compare (a, b); if (cmp) return cmp; } /* We've got two collinear edges now. */ return b->edge.bottom - a->edge.bottom; } static inline cairo_int64_t det32_64 (int32_t a, int32_t b, int32_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), _cairo_int32x32_64_mul (b, c)); } static inline cairo_int128_t det64x32_128 (cairo_int64_t a, int32_t b, cairo_int64_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), _cairo_int64x32_128_mul (c, b)); } /* Compute the intersection of two lines as defined by two edges. The * result is provided as a coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. */ static cairo_bool_t intersect_lines (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_intersect_point_t *intersection) { cairo_int64_t a_det, b_det; /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm begins. * What we're doing to mitigate this is to perform clamping in * cairo_bo_tessellate_polygon(). */ int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; cairo_int64_t den_det; cairo_int64_t R; cairo_quorem64_t qr; den_det = det32_64 (dx1, dy1, dx2, dy2); /* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by * avoiding the division? * * X = ax + t * adx = bx + s * bdx; * Y = ay + t * ady = by + s * bdy; * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) * => t * L = R * * Therefore we can reject any intersection (under the criteria for * valid intersection events) if: * L^R < 0 => t < 0, or * L<R => t > 1 * * (where top/bottom must at least extend to the line endpoints). * * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) */ R = det32_64 (dx2, dy2, b->edge.line.p1.x - a->edge.line.p1.x, b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } R = det32_64 (dy1, dx1, a->edge.line.p1.y - b->edge.line.p1.y, a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } /* We now know that the two lines should intersect within range. */ a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, a->edge.line.p2.x, a->edge.line.p2.y); b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, b->edge.line.p2.x, b->edge.line.p2.y); /* x = det (a_det, dx1, b_det, dx2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, b_det, dx2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->x.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->x.exactness = INEXACT; } #endif intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); /* y = det (a_det, dy1, b_det, dy2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, b_det, dy2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->y.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->y.exactness = INEXACT; } #endif intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); return TRUE; } static int _cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, int32_t b) { /* First compare the quotient */ if (a.ordinate > b) return +1; if (a.ordinate < b) return -1; /* With quotient identical, if remainder is 0 then compare equal */ /* Otherwise, the non-zero remainder makes a > b */ return INEXACT == a.exactness; } /* Does the given edge contain the given point. The point must already * be known to be contained within the line determined by the edge, * (most likely the point results from an intersection of this edge * with another). * * If we had exact arithmetic, then this function would simply be a * matter of examining whether the y value of the point lies within * the range of y values of the edge. But since intersection points * are not exact due to being rounded to the nearest integer within * the available precision, we must also examine the x value of the * point. * * The definition of "contains" here is that the given intersection * point will be seen by the sweep line after the start event for the * given edge and before the stop event for the edge. See the comments * in the implementation for more details. */ static cairo_bool_t _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, cairo_bo_intersect_point_t *point) { int cmp_top, cmp_bottom; /* XXX: When running the actual algorithm, we don't actually need to * compare against edge->top at all here, since any intersection above * top is eliminated early via a slope comparison. We're leaving these * here for now only for the sake of the quadratic-time intersection * finder which needs them. */ cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.top); cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.bottom); if (cmp_top < 0 || cmp_bottom > 0) { return FALSE; } if (cmp_top > 0 && cmp_bottom < 0) { return TRUE; } /* At this stage, the point lies on the same y value as either * edge->top or edge->bottom, so we have to examine the x value in * order to properly determine containment. */ /* If the y value of the point is the same as the y value of the * top of the edge, then the x value of the point must be greater * to be considered as inside the edge. Similarly, if the y value * of the point is the same as the y value of the bottom of the * edge, then the x value of the point must be less to be * considered as inside. */ if (cmp_top == 0) { cairo_fixed_t top_x; top_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.top); return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; } else { /* cmp_bottom == 0 */ cairo_fixed_t bot_x; bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.bottom); return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; } } /* Compute the intersection of two edges. The result is provided as a * coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the * intersection of the lines defined by the edges occurs outside of * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges * are exactly parallel. * * Note that when determining if a candidate intersection is "inside" * an edge, we consider both the infinitesimal shortening and the * infinitesimal tilt rules described by John Hobby. Specifically, if * the intersection is exactly the same as an edge point, it is * effectively outside (no intersection is returned). Also, if the * intersection point has the same */ static cairo_bool_t _cairo_bo_edge_intersect (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_point32_t *intersection) { cairo_bo_intersect_point_t quorem; if (! intersect_lines (a, b, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) return FALSE; /* Now that we've correctly compared the intersection point and * determined that it lies within the edge, then we know that we * no longer need any more bits of storage for the intersection * than we do for our edge coordinates. We also no longer need the * remainder from the division. */ intersection->x = quorem.x.ordinate; intersection->y = quorem.y.ordinate; return TRUE; } static inline int cairo_bo_event_compare (const cairo_bo_event_t *a, const cairo_bo_event_t *b) { int cmp; cmp = _cairo_bo_point32_compare (&a->point, &b->point); if (cmp) return cmp; cmp = a->type - b->type; if (cmp) return cmp; return a - b; } static inline void _pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; } static inline void _pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_status_t _pqueue_grow (pqueue_t *pq) { cairo_bo_event_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pq->elements = new_elements; return CAIRO_STATUS_SUCCESS; } static inline cairo_status_t _pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) { cairo_bo_event_t **elements; int i, parent; if (unlikely (pq->size + 1 == pq->max_size)) { cairo_status_t status; status = _pqueue_grow (pq); if (unlikely (status)) return status; } elements = pq->elements; for (i = ++pq->size; i != PQ_FIRST_ENTRY && cairo_bo_event_compare (event, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = event; return CAIRO_STATUS_SUCCESS; } static inline void _pqueue_pop (pqueue_t *pq) { cairo_bo_event_t **elements = pq->elements; cairo_bo_event_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && cairo_bo_event_compare (elements[child+1], elements[child]) < 0) { child++; } if (cairo_bo_event_compare (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline cairo_status_t _cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, cairo_bo_event_type_t type, cairo_bo_edge_t *e1, cairo_bo_edge_t *e2, const cairo_point_t *point) { cairo_bo_queue_event_t *event; event = _cairo_freepool_alloc (&queue->pool); if (unlikely (event == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event->type = type; event->e1 = e1; event->e2 = e2; event->point = *point; return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); } static void _cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, cairo_bo_event_t *event) { _cairo_freepool_free (&queue->pool, event); } static cairo_bo_event_t * _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) { cairo_bo_event_t *event, *cmp; event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; cmp = *event_queue->start_events; if (event == NULL || (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) { event = cmp; event_queue->start_events++; } else { _pqueue_pop (&event_queue->pqueue); } return event; } CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, cairo_bo_event_t *, cairo_bo_event_compare) static void _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_event_t **start_events, int num_events) { _cairo_bo_event_queue_sort (start_events, num_events); start_events[num_events] = NULL; event_queue->start_events = start_events; _cairo_freepool_init (&event_queue->pool, sizeof (cairo_bo_queue_event_t)); _pqueue_init (&event_queue->pqueue); event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; } static cairo_status_t event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *edge) { cairo_bo_point32_t point; point.y = edge->edge.bottom; point.x = _line_compute_intersection_x_for_y (&edge->edge.line, point.y); return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_STOP, edge, NULL, &point); } static void _cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) { _pqueue_fini (&event_queue->pqueue); _cairo_freepool_fini (&event_queue->pool); } static inline cairo_status_t event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { cairo_bo_point32_t intersection; if (_line_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS; /* The names "left" and "right" here are correct descriptions of * the order of the two edges within the active edge list. So if a * slope comparison also puts left less than right, then we know * that the intersection of these two segments has already * occurred before the current sweep line position. */ if (_slope_compare (left, right) <= 0) return CAIRO_STATUS_SUCCESS; if (! _cairo_bo_edge_intersect (left, right, &intersection)) return CAIRO_STATUS_SUCCESS; return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_INTERSECTION, left, right, &intersection); } static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) { sweep_line->head = NULL; sweep_line->current_y = INT32_MIN; sweep_line->current_edge = NULL; } static cairo_status_t sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (sweep_line->current_edge != NULL) { cairo_bo_edge_t *prev, *next; int cmp; cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, sweep_line->current_edge, edge); if (cmp < 0) { prev = sweep_line->current_edge; next = prev->next; while (next != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, next, edge) < 0) { prev = next, next = prev->next; } prev->next = edge; edge->prev = prev; edge->next = next; if (next != NULL) next->prev = edge; } else if (cmp > 0) { next = sweep_line->current_edge; prev = next->prev; while (prev != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, prev, edge) > 0) { next = prev, prev = next->prev; } next->prev = edge; edge->next = next; edge->prev = prev; if (prev != NULL) prev->next = edge; else sweep_line->head = edge; } else { prev = sweep_line->current_edge; edge->prev = prev; edge->next = prev->next; if (prev->next != NULL) prev->next->prev = edge; prev->next = edge; } } else { sweep_line->head = edge; } sweep_line->current_edge = edge; return CAIRO_STATUS_SUCCESS; } static void _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (edge->prev != NULL) edge->prev->next = edge->next; else sweep_line->head = edge->next; if (edge->next != NULL) edge->next->prev = edge->prev; if (sweep_line->current_edge == edge) sweep_line->current_edge = edge->prev ? edge->prev : edge->next; } static void _cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { if (left->prev != NULL) left->prev->next = right; else sweep_line->head = right; if (right->next != NULL) right->next->prev = left; left->next = right->next; right->next = left; right->prev = left->prev; left->prev = right; } static inline cairo_bool_t edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { if (_line_equal (&a->edge.line, &b->edge.line)) return TRUE; if (_slope_compare (a, b)) return FALSE; /* The choice of y is not truly arbitrary since we must guarantee that it * is greater than the start of either line. */ if (a->edge.line.p1.y == b->edge.line.p1.y) { return a->edge.line.p1.x == b->edge.line.p1.x; } else if (a->edge.line.p1.y < b->edge.line.p1.y) { return edge_compare_for_y_against_x (b, a->edge.line.p1.y, a->edge.line.p1.x) == 0; } else { return edge_compare_for_y_against_x (a, b->edge.line.p1.y, b->edge.line.p1.x) == 0; } } static void edges_end (cairo_bo_edge_t *left, int32_t bot, cairo_polygon_t *polygon) { cairo_bo_deferred_t *l = &left->deferred; cairo_bo_edge_t *right = l->other; assert(right->deferred.other == NULL); if (likely (l->top < bot)) { _cairo_polygon_add_line (polygon, &left->edge.line, l->top, bot, 1); _cairo_polygon_add_line (polygon, &right->edge.line, l->top, bot, -1); } l->other = NULL; } static inline void edges_start_or_continue (cairo_bo_edge_t *left, cairo_bo_edge_t *right, int top, cairo_polygon_t *polygon) { assert (right->deferred.other == NULL); if (left->deferred.other == right) return; if (left->deferred.other != NULL) { if (right != NULL && edges_colinear (left->deferred.other, right)) { cairo_bo_edge_t *old = left->deferred.other; /* continuation on right, extend right to cover both */ assert (old->deferred.other == NULL); assert (old->edge.line.p2.y > old->edge.line.p1.y); if (old->edge.line.p1.y < right->edge.line.p1.y) right->edge.line.p1 = old->edge.line.p1; if (old->edge.line.p2.y > right->edge.line.p2.y) right->edge.line.p2 = old->edge.line.p2; left->deferred.other = right; return; } edges_end (left, top, polygon); } if (right != NULL && ! edges_colinear (left, right)) { left->deferred.top = top; left->deferred.other = right; } } #define is_zero(w) ((w)[0] == 0 || (w)[1] == 0) static inline void active_edges (cairo_bo_edge_t *left, int32_t top, cairo_polygon_t *polygon) { cairo_bo_edge_t *right; int winding[2] = {0, 0}; /* Yes, this is naive. Consider this a placeholder. */ while (left != NULL) { assert (is_zero (winding)); do { winding[left->a_or_b] += left->edge.dir; if (! is_zero (winding)) break; if unlikely ((left->deferred.other)) edges_end (left, top, polygon); left = left->next; if (! left) return; } while (1); right = left->next; do { if unlikely ((right->deferred.other)) edges_end (right, top, polygon); winding[right->a_or_b] += right->edge.dir; if (is_zero (winding)) { if (right->next == NULL || ! edges_colinear (right, right->next)) break; } right = right->next; } while (1); edges_start_or_continue (left, right, top, polygon); left = right->next; } } static cairo_status_t intersection_sweep (cairo_bo_event_t **start_events, int num_events, cairo_polygon_t *polygon) { cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ cairo_bo_event_queue_t event_queue; cairo_bo_sweep_line_t sweep_line; cairo_bo_event_t *event; cairo_bo_edge_t *left, *right; cairo_bo_edge_t *e1, *e2; _cairo_bo_event_queue_init (&event_queue, start_events, num_events); _cairo_bo_sweep_line_init (&sweep_line); while ((event = _cairo_bo_event_dequeue (&event_queue))) { if (event->point.y != sweep_line.current_y) { active_edges (sweep_line.head, sweep_line.current_y, polygon); sweep_line.current_y = event->point.y; } switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: e1 = &((cairo_bo_start_event_t *) event)->edge; status = sweep_line_insert (&sweep_line, e1); if (unlikely (status)) goto unwind; status = event_queue_insert_stop (&event_queue, e1); if (unlikely (status)) goto unwind; left = e1->prev; right = e1->next; if (left != NULL) { status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); if (unlikely (status)) goto unwind; } if (right != NULL) { status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_STOP: e1 = ((cairo_bo_queue_event_t *) event)->e1; _cairo_bo_event_queue_delete (&event_queue, event); if (e1->deferred.other) edges_end (e1, sweep_line.current_y, polygon); left = e1->prev; right = e1->next; _cairo_bo_sweep_line_delete (&sweep_line, e1); if (left != NULL && right != NULL) { status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_INTERSECTION: e1 = ((cairo_bo_queue_event_t *) event)->e1; e2 = ((cairo_bo_queue_event_t *) event)->e2; _cairo_bo_event_queue_delete (&event_queue, event); /* skip this intersection if its edges are not adjacent */ if (e2 != e1->next) break; if (e1->deferred.other) edges_end (e1, sweep_line.current_y, polygon); if (e2->deferred.other) edges_end (e2, sweep_line.current_y, polygon); left = e1->prev; right = e2->next; _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); /* after the swap e2 is left of e1 */ if (left != NULL) { status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); if (unlikely (status)) goto unwind; } if (right != NULL) { status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; } } unwind: _cairo_bo_event_queue_fini (&event_queue); return status; } cairo_status_t _cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, cairo_polygon_t *b, int winding_b) { cairo_status_t status; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; int num_events; int i, j; /* XXX lazy */ if (winding_a != CAIRO_FILL_RULE_WINDING) { status = _cairo_polygon_reduce (a, winding_a); if (unlikely (status)) return status; } if (winding_b != CAIRO_FILL_RULE_WINDING) { status = _cairo_polygon_reduce (b, winding_b); if (unlikely (status)) return status; } if (unlikely (0 == a->num_edges)) return CAIRO_STATUS_SUCCESS; if (unlikely (0 == b->num_edges)) { a->num_edges = 0; return CAIRO_STATUS_SUCCESS; } events = stack_events; event_ptrs = stack_event_ptrs; num_events = a->num_edges + b->num_edges; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); } j = 0; for (i = 0; i < a->num_edges; i++) { event_ptrs[j] = (cairo_bo_event_t *) &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = a->edges[i].top; events[j].point.x = _line_compute_intersection_x_for_y (&a->edges[i].line, events[j].point.y); events[j].edge.a_or_b = 0; events[j].edge.edge = a->edges[i]; events[j].edge.deferred.other = NULL; events[j].edge.prev = NULL; events[j].edge.next = NULL; j++; } for (i = 0; i < b->num_edges; i++) { event_ptrs[j] = (cairo_bo_event_t *) &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = b->edges[i].top; events[j].point.x = _line_compute_intersection_x_for_y (&b->edges[i].line, events[j].point.y); events[j].edge.a_or_b = 1; events[j].edge.edge = b->edges[i]; events[j].edge.deferred.other = NULL; events[j].edge.prev = NULL; events[j].edge.next = NULL; j++; } assert (j == num_events); #if 0 { FILE *file = fopen ("clip_a.txt", "w"); _cairo_debug_print_polygon (file, a); fclose (file); } { FILE *file = fopen ("clip_b.txt", "w"); _cairo_debug_print_polygon (file, b); fclose (file); } #endif a->num_edges = 0; status = intersection_sweep (event_ptrs, num_events, a); if (events != stack_events) free (events); #if 0 { FILE *file = fopen ("clip_result.txt", "w"); _cairo_debug_print_polygon (file, a); fclose (file); } #endif return status; } cairo_status_t _cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon, cairo_fill_rule_t *winding, cairo_box_t *boxes, int num_boxes) { cairo_polygon_t b; cairo_status_t status; int n; if (num_boxes == 0) { polygon->num_edges = 0; return CAIRO_STATUS_SUCCESS; } for (n = 0; n < num_boxes; n++) { if (polygon->extents.p1.x >= boxes[n].p1.x && polygon->extents.p2.x <= boxes[n].p2.x && polygon->extents.p1.y >= boxes[n].p1.y && polygon->extents.p2.y <= boxes[n].p2.y) { return CAIRO_STATUS_SUCCESS; } } _cairo_polygon_init (&b, NULL, 0); for (n = 0; n < num_boxes; n++) { if (boxes[n].p2.x > polygon->extents.p1.x && boxes[n].p1.x < polygon->extents.p2.x && boxes[n].p2.y > polygon->extents.p1.y && boxes[n].p1.y < polygon->extents.p2.y) { cairo_point_t p1, p2; p1.y = boxes[n].p1.y; p2.y = boxes[n].p2.y; p2.x = p1.x = boxes[n].p1.x; _cairo_polygon_add_external_edge (&b, &p1, &p2); p2.x = p1.x = boxes[n].p2.x; _cairo_polygon_add_external_edge (&b, &p2, &p1); } } status = _cairo_polygon_intersect (polygon, *winding, &b, CAIRO_FILL_RULE_WINDING); _cairo_polygon_fini (&b); *winding = CAIRO_FILL_RULE_WINDING; return status; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-polygon-reduce.c��������������������������0000664�0000000�0000000�00000117662�12710376503�0026510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright © 2004 Carl Worth * Copyright © 2006 Red Hat, Inc. * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Carl Worth * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ /* Provide definitions for standalone compilation */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-inline.h" #define DEBUG_POLYGON 0 typedef cairo_point_t cairo_bo_point32_t; typedef struct _cairo_bo_intersect_ordinate { int32_t ordinate; enum { EXACT, INEXACT } exactness; } cairo_bo_intersect_ordinate_t; typedef struct _cairo_bo_intersect_point { cairo_bo_intersect_ordinate_t x; cairo_bo_intersect_ordinate_t y; } cairo_bo_intersect_point_t; typedef struct _cairo_bo_edge cairo_bo_edge_t; typedef struct _cairo_bo_deferred { cairo_bo_edge_t *right; int32_t top; } cairo_bo_deferred_t; struct _cairo_bo_edge { cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; cairo_bo_deferred_t deferred; }; /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef enum { CAIRO_BO_EVENT_TYPE_STOP, CAIRO_BO_EVENT_TYPE_INTERSECTION, CAIRO_BO_EVENT_TYPE_START } cairo_bo_event_type_t; typedef struct _cairo_bo_event { cairo_bo_event_type_t type; cairo_point_t point; } cairo_bo_event_t; typedef struct _cairo_bo_start_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t edge; } cairo_bo_start_event_t; typedef struct _cairo_bo_queue_event { cairo_bo_event_type_t type; cairo_point_t point; cairo_bo_edge_t *e1; cairo_bo_edge_t *e2; } cairo_bo_queue_event_t; typedef struct _pqueue { int size, max_size; cairo_bo_event_t **elements; cairo_bo_event_t *elements_embedded[1024]; } pqueue_t; typedef struct _cairo_bo_event_queue { cairo_freepool_t pool; pqueue_t pqueue; cairo_bo_event_t **start_events; } cairo_bo_event_queue_t; typedef struct _cairo_bo_sweep_line { cairo_bo_edge_t *head; int32_t current_y; cairo_bo_edge_t *current_edge; } cairo_bo_sweep_line_t; static cairo_fixed_t _line_compute_intersection_x_for_y (const cairo_line_t *line, cairo_fixed_t y) { cairo_fixed_t x, dy; if (y == line->p1.y) return line->p1.x; if (y == line->p2.y) return line->p2.x; x = line->p1.x; dy = line->p2.y - line->p1.y; if (dy != 0) { x += _cairo_fixed_mul_div_floor (y - line->p1.y, line->p2.x - line->p1.x, dy); } return x; } static inline int _cairo_bo_point32_compare (cairo_bo_point32_t const *a, cairo_bo_point32_t const *b) { int cmp; cmp = a->y - b->y; if (cmp) return cmp; return a->x - b->x; } /* Compare the slope of a to the slope of b, returning 1, 0, -1 if the * slope a is respectively greater than, equal to, or less than the * slope of b. * * For each edge, consider the direction vector formed from: * * top -> bottom * * which is: * * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) * * We then define the slope of each edge as dx/dy, (which is the * inverse of the slope typically used in math instruction). We never * compute a slope directly as the value approaches infinity, but we * can derive a slope comparison without division as follows, (where * the ? represents our compare operator). * * 1. slope(a) ? slope(b) * 2. adx/ady ? bdx/bdy * 3. (adx * bdy) ? (bdx * ady) * * Note that from step 2 to step 3 there is no change needed in the * sign of the result since both ady and bdy are guaranteed to be * greater than or equal to 0. * * When using this slope comparison to sort edges, some care is needed * when interpreting the results. Since the slope compare operates on * distance vectors from top to bottom it gives a correct left to * right sort for edges that have a common top point, (such as two * edges with start events at the same location). On the other hand, * the sense of the result will be exactly reversed for two edges that * have a common stop point. */ static inline int _slope_compare (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; /* Since the dy's are all positive by construction we can fast * path several common cases. */ /* First check for vertical lines. */ if (adx == 0) return -bdx; if (bdx == 0) return adx; /* Then where the two edges point in different directions wrt x. */ if ((adx ^ bdx) < 0) return adx; /* Finally we actually need to do the general comparison. */ { int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } } /* * We need to compare the x-coordinates of a pair of lines for a particular y, * without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 128 bit arithmetic. For certain, but common, * input we can reduce this down to a single 32 bit compare by inspecting the * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) * * See the similar discussion for _slope_compare(). */ static int edges_compare_x_for_y_general (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins. */ int32_t dx; int32_t adx, ady; int32_t bdx, bdy; enum { HAVE_NONE = 0x0, HAVE_DX = 0x1, HAVE_ADX = 0x2, HAVE_DX_ADX = HAVE_DX | HAVE_ADX, HAVE_BDX = 0x4, HAVE_DX_BDX = HAVE_DX | HAVE_BDX, HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX } have_dx_adx_bdx = HAVE_ALL; /* don't bother solving for abscissa if the edges' bounding boxes * can be used to order them. */ { int32_t amin, amax; int32_t bmin, bmax; if (a->edge.line.p1.x < a->edge.line.p2.x) { amin = a->edge.line.p1.x; amax = a->edge.line.p2.x; } else { amin = a->edge.line.p2.x; amax = a->edge.line.p1.x; } if (b->edge.line.p1.x < b->edge.line.p2.x) { bmin = b->edge.line.p1.x; bmax = b->edge.line.p2.x; } else { bmin = b->edge.line.p2.x; bmax = b->edge.line.p1.x; } if (amax < bmin) return -1; if (amin > bmax) return +1; } ady = a->edge.line.p2.y - a->edge.line.p1.y; adx = a->edge.line.p2.x - a->edge.line.p1.x; if (adx == 0) have_dx_adx_bdx &= ~HAVE_ADX; bdy = b->edge.line.p2.y - b->edge.line.p1.y; bdx = b->edge.line.p2.x - b->edge.line.p1.x; if (bdx == 0) have_dx_adx_bdx &= ~HAVE_BDX; dx = a->edge.line.p1.x - b->edge.line.p1.x; if (dx == 0) have_dx_adx_bdx &= ~HAVE_DX; #define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) #define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) #define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) switch (have_dx_adx_bdx) { default: case HAVE_NONE: return 0; case HAVE_DX: /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ return dx; /* ady * bdy is positive definite */ case HAVE_ADX: /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ return adx; /* bdy * (y - a->top.y) is positive definite */ case HAVE_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy */ return -bdx; /* ady * (y - b->top.y) is positive definite */ case HAVE_ADX_BDX: /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ if ((adx ^ bdx) < 0) { return adx; } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ cairo_int64_t adx_bdy, bdx_ady; /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ adx_bdy = _cairo_int32x32_64_mul (adx, bdy); bdx_ady = _cairo_int32x32_64_mul (bdx, ady); return _cairo_int64_cmp (adx_bdy, bdx_ady); } else return _cairo_int128_cmp (A, B); case HAVE_DX_ADX: /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ if ((-adx ^ dx) < 0) { return dx; } else { cairo_int64_t ady_dx, dy_adx; ady_dx = _cairo_int32x32_64_mul (ady, dx); dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); return _cairo_int64_cmp (ady_dx, dy_adx); } case HAVE_DX_BDX: /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ if ((bdx ^ dx) < 0) { return dx; } else { cairo_int64_t bdy_dx, dy_bdx; bdy_dx = _cairo_int32x32_64_mul (bdy, dx); dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); return _cairo_int64_cmp (bdy_dx, dy_bdx); } case HAVE_ALL: /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); } #undef B #undef A #undef L } /* * We need to compare the x-coordinate of a line for a particular y wrt to a * given x, without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ X * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. * * See the similar discussion for _slope_compare() and * edges_compare_x_for_y_general(). */ static int edge_compare_for_y_against_x (const cairo_bo_edge_t *a, int32_t y, int32_t x) { int32_t adx, ady; int32_t dx, dy; cairo_int64_t L, R; if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) return 1; if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) return -1; adx = a->edge.line.p2.x - a->edge.line.p1.x; dx = x - a->edge.line.p1.x; if (adx == 0) return -dx; if (dx == 0 || (adx ^ dx) < 0) return adx; dy = y - a->edge.line.p1.y; ady = a->edge.line.p2.y - a->edge.line.p1.y; L = _cairo_int32x32_64_mul (dy, adx); R = _cairo_int32x32_64_mul (dx, ady); return _cairo_int64_cmp (L, R); } static int edges_compare_x_for_y (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b, int32_t y) { /* If the sweep-line is currently on an end-point of a line, * then we know its precise x value (and considering that we often need to * compare events at end-points, this happens frequently enough to warrant * special casing). */ enum { HAVE_NEITHER = 0x0, HAVE_AX = 0x1, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; int32_t ax, bx; if (y == a->edge.line.p1.y) ax = a->edge.line.p1.x; else if (y == a->edge.line.p2.y) ax = a->edge.line.p2.x; else have_ax_bx &= ~HAVE_AX; if (y == b->edge.line.p1.y) bx = b->edge.line.p1.x; else if (y == b->edge.line.p2.y) bx = b->edge.line.p2.x; else have_ax_bx &= ~HAVE_BX; switch (have_ax_bx) { default: case HAVE_NEITHER: return edges_compare_x_for_y_general (a, b, y); case HAVE_AX: return -edge_compare_for_y_against_x (b, y, ax); case HAVE_BX: return edge_compare_for_y_against_x (a, y, bx); case HAVE_BOTH: return ax - bx; } } static inline int _line_equal (const cairo_line_t *a, const cairo_line_t *b) { return (a->p1.x == b->p1.x && a->p1.y == b->p1.y && a->p2.x == b->p2.x && a->p2.y == b->p2.y); } static int _cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { int cmp; /* compare the edges if not identical */ if (! _line_equal (&a->edge.line, &b->edge.line)) { cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); if (cmp) return cmp; /* The two edges intersect exactly at y, so fall back on slope * comparison. We know that this compare_edges function will be * called only when starting a new edge, (not when stopping an * edge), so we don't have to worry about conditionally inverting * the sense of _slope_compare. */ cmp = _slope_compare (a, b); if (cmp) return cmp; } /* We've got two collinear edges now. */ return b->edge.bottom - a->edge.bottom; } static inline cairo_int64_t det32_64 (int32_t a, int32_t b, int32_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), _cairo_int32x32_64_mul (b, c)); } static inline cairo_int128_t det64x32_128 (cairo_int64_t a, int32_t b, cairo_int64_t c, int32_t d) { /* det = a * d - b * c */ return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), _cairo_int64x32_128_mul (c, b)); } /* Compute the intersection of two lines as defined by two edges. The * result is provided as a coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. */ static cairo_bool_t intersect_lines (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_intersect_point_t *intersection) { cairo_int64_t a_det, b_det; /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm begins. * What we're doing to mitigate this is to perform clamping in * cairo_bo_tessellate_polygon(). */ int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; cairo_int64_t den_det; cairo_int64_t R; cairo_quorem64_t qr; den_det = det32_64 (dx1, dy1, dx2, dy2); /* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by * avoiding the division? * * X = ax + t * adx = bx + s * bdx; * Y = ay + t * ady = by + s * bdy; * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) * => t * L = R * * Therefore we can reject any intersection (under the criteria for * valid intersection events) if: * L^R < 0 => t < 0, or * L<R => t > 1 * * (where top/bottom must at least extend to the line endpoints). * * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) */ R = det32_64 (dx2, dy2, b->edge.line.p1.x - a->edge.line.p1.x, b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } R = det32_64 (dy1, dx1, a->edge.line.p1.y - b->edge.line.p1.y, a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_negative (den_det)) { if (_cairo_int64_ge (den_det, R)) return FALSE; } else { if (_cairo_int64_le (den_det, R)) return FALSE; } /* We now know that the two lines should intersect within range. */ a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, a->edge.line.p2.x, a->edge.line.p2.y); b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, b->edge.line.p2.x, b->edge.line.p2.y); /* x = det (a_det, dx1, b_det, dx2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, b_det, dx2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->x.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->x.exactness = INEXACT; } #endif intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); /* y = det (a_det, dy1, b_det, dy2) / den_det */ qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, b_det, dy2), den_det); if (_cairo_int64_eq (qr.rem, den_det)) return FALSE; #if 0 intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; #else intersection->y.exactness = EXACT; if (! _cairo_int64_is_zero (qr.rem)) { if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) qr.rem = _cairo_int64_negate (qr.rem); qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); if (_cairo_int64_ge (qr.rem, den_det)) { qr.quo = _cairo_int64_add (qr.quo, _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); } else intersection->y.exactness = INEXACT; } #endif intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); return TRUE; } static int _cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, int32_t b) { /* First compare the quotient */ if (a.ordinate > b) return +1; if (a.ordinate < b) return -1; /* With quotient identical, if remainder is 0 then compare equal */ /* Otherwise, the non-zero remainder makes a > b */ return INEXACT == a.exactness; } /* Does the given edge contain the given point. The point must already * be known to be contained within the line determined by the edge, * (most likely the point results from an intersection of this edge * with another). * * If we had exact arithmetic, then this function would simply be a * matter of examining whether the y value of the point lies within * the range of y values of the edge. But since intersection points * are not exact due to being rounded to the nearest integer within * the available precision, we must also examine the x value of the * point. * * The definition of "contains" here is that the given intersection * point will be seen by the sweep line after the start event for the * given edge and before the stop event for the edge. See the comments * in the implementation for more details. */ static cairo_bool_t _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, cairo_bo_intersect_point_t *point) { int cmp_top, cmp_bottom; /* XXX: When running the actual algorithm, we don't actually need to * compare against edge->top at all here, since any intersection above * top is eliminated early via a slope comparison. We're leaving these * here for now only for the sake of the quadratic-time intersection * finder which needs them. */ cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.top); cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->edge.bottom); if (cmp_top < 0 || cmp_bottom > 0) { return FALSE; } if (cmp_top > 0 && cmp_bottom < 0) { return TRUE; } /* At this stage, the point lies on the same y value as either * edge->top or edge->bottom, so we have to examine the x value in * order to properly determine containment. */ /* If the y value of the point is the same as the y value of the * top of the edge, then the x value of the point must be greater * to be considered as inside the edge. Similarly, if the y value * of the point is the same as the y value of the bottom of the * edge, then the x value of the point must be less to be * considered as inside. */ if (cmp_top == 0) { cairo_fixed_t top_x; top_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.top); return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; } else { /* cmp_bottom == 0 */ cairo_fixed_t bot_x; bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, edge->edge.bottom); return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; } } /* Compute the intersection of two edges. The result is provided as a * coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the * intersection of the lines defined by the edges occurs outside of * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges * are exactly parallel. * * Note that when determining if a candidate intersection is "inside" * an edge, we consider both the infinitesimal shortening and the * infinitesimal tilt rules described by John Hobby. Specifically, if * the intersection is exactly the same as an edge point, it is * effectively outside (no intersection is returned). Also, if the * intersection point has the same */ static cairo_bool_t _cairo_bo_edge_intersect (cairo_bo_edge_t *a, cairo_bo_edge_t *b, cairo_bo_point32_t *intersection) { cairo_bo_intersect_point_t quorem; if (! intersect_lines (a, b, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) return FALSE; if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) return FALSE; /* Now that we've correctly compared the intersection point and * determined that it lies within the edge, then we know that we * no longer need any more bits of storage for the intersection * than we do for our edge coordinates. We also no longer need the * remainder from the division. */ intersection->x = quorem.x.ordinate; intersection->y = quorem.y.ordinate; return TRUE; } static inline int cairo_bo_event_compare (const cairo_bo_event_t *a, const cairo_bo_event_t *b) { int cmp; cmp = _cairo_bo_point32_compare (&a->point, &b->point); if (cmp) return cmp; cmp = a->type - b->type; if (cmp) return cmp; return a - b; } static inline void _pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; } static inline void _pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_status_t _pqueue_grow (pqueue_t *pq) { cairo_bo_event_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (cairo_bo_event_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pq->elements = new_elements; return CAIRO_STATUS_SUCCESS; } static inline cairo_status_t _pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) { cairo_bo_event_t **elements; int i, parent; if (unlikely (pq->size + 1 == pq->max_size)) { cairo_status_t status; status = _pqueue_grow (pq); if (unlikely (status)) return status; } elements = pq->elements; for (i = ++pq->size; i != PQ_FIRST_ENTRY && cairo_bo_event_compare (event, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = event; return CAIRO_STATUS_SUCCESS; } static inline void _pqueue_pop (pqueue_t *pq) { cairo_bo_event_t **elements = pq->elements; cairo_bo_event_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && cairo_bo_event_compare (elements[child+1], elements[child]) < 0) { child++; } if (cairo_bo_event_compare (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline cairo_status_t _cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, cairo_bo_event_type_t type, cairo_bo_edge_t *e1, cairo_bo_edge_t *e2, const cairo_point_t *point) { cairo_bo_queue_event_t *event; event = _cairo_freepool_alloc (&queue->pool); if (unlikely (event == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event->type = type; event->e1 = e1; event->e2 = e2; event->point = *point; return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); } static void _cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, cairo_bo_event_t *event) { _cairo_freepool_free (&queue->pool, event); } static cairo_bo_event_t * _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) { cairo_bo_event_t *event, *cmp; event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; cmp = *event_queue->start_events; if (event == NULL || (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) { event = cmp; event_queue->start_events++; } else { _pqueue_pop (&event_queue->pqueue); } return event; } CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, cairo_bo_event_t *, cairo_bo_event_compare) static void _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_event_t **start_events, int num_events) { _cairo_bo_event_queue_sort (start_events, num_events); start_events[num_events] = NULL; event_queue->start_events = start_events; _cairo_freepool_init (&event_queue->pool, sizeof (cairo_bo_queue_event_t)); _pqueue_init (&event_queue->pqueue); event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; } static cairo_status_t _cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *edge) { cairo_bo_point32_t point; point.y = edge->edge.bottom; point.x = _line_compute_intersection_x_for_y (&edge->edge.line, point.y); return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_STOP, edge, NULL, &point); } static void _cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) { _pqueue_fini (&event_queue->pqueue); _cairo_freepool_fini (&event_queue->pool); } static inline cairo_status_t _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { cairo_bo_point32_t intersection; if (_line_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS; /* The names "left" and "right" here are correct descriptions of * the order of the two edges within the active edge list. So if a * slope comparison also puts left less than right, then we know * that the intersection of these two segments has already * occurred before the current sweep line position. */ if (_slope_compare (left, right) <= 0) return CAIRO_STATUS_SUCCESS; if (! _cairo_bo_edge_intersect (left, right, &intersection)) return CAIRO_STATUS_SUCCESS; return _cairo_bo_event_queue_insert (event_queue, CAIRO_BO_EVENT_TYPE_INTERSECTION, left, right, &intersection); } static void _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) { sweep_line->head = NULL; sweep_line->current_y = INT32_MIN; sweep_line->current_edge = NULL; } static cairo_status_t _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (sweep_line->current_edge != NULL) { cairo_bo_edge_t *prev, *next; int cmp; cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, sweep_line->current_edge, edge); if (cmp < 0) { prev = sweep_line->current_edge; next = prev->next; while (next != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, next, edge) < 0) { prev = next, next = prev->next; } prev->next = edge; edge->prev = prev; edge->next = next; if (next != NULL) next->prev = edge; } else if (cmp > 0) { next = sweep_line->current_edge; prev = next->prev; while (prev != NULL && _cairo_bo_sweep_line_compare_edges (sweep_line, prev, edge) > 0) { next = prev, prev = next->prev; } next->prev = edge; edge->next = next; edge->prev = prev; if (prev != NULL) prev->next = edge; else sweep_line->head = edge; } else { prev = sweep_line->current_edge; edge->prev = prev; edge->next = prev->next; if (prev->next != NULL) prev->next->prev = edge; prev->next = edge; } } else { sweep_line->head = edge; } sweep_line->current_edge = edge; return CAIRO_STATUS_SUCCESS; } static void _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { if (edge->prev != NULL) edge->prev->next = edge->next; else sweep_line->head = edge->next; if (edge->next != NULL) edge->next->prev = edge->prev; if (sweep_line->current_edge == edge) sweep_line->current_edge = edge->prev ? edge->prev : edge->next; } static void _cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *left, cairo_bo_edge_t *right) { if (left->prev != NULL) left->prev->next = right; else sweep_line->head = right; if (right->next != NULL) right->next->prev = left; left->next = right->next; right->next = left; right->prev = left->prev; left->prev = right; } static inline cairo_bool_t edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { if (_line_equal (&a->edge.line, &b->edge.line)) return TRUE; if (_slope_compare (a, b)) return FALSE; /* The choice of y is not truly arbitrary since we must guarantee that it * is greater than the start of either line. */ if (a->edge.line.p1.y == b->edge.line.p1.y) { return a->edge.line.p1.x == b->edge.line.p1.x; } else if (a->edge.line.p2.y == b->edge.line.p2.y) { return a->edge.line.p2.x == b->edge.line.p2.x; } else if (a->edge.line.p1.y < b->edge.line.p1.y) { return edge_compare_for_y_against_x (b, a->edge.line.p1.y, a->edge.line.p1.x) == 0; } else { return edge_compare_for_y_against_x (a, b->edge.line.p1.y, b->edge.line.p1.x) == 0; } } static void _cairo_bo_edge_end (cairo_bo_edge_t *left, int32_t bot, cairo_polygon_t *polygon) { cairo_bo_deferred_t *d = &left->deferred; if (likely (d->top < bot)) { _cairo_polygon_add_line (polygon, &left->edge.line, d->top, bot, 1); _cairo_polygon_add_line (polygon, &d->right->edge.line, d->top, bot, -1); } d->right = NULL; } static inline void _cairo_bo_edge_start_or_continue (cairo_bo_edge_t *left, cairo_bo_edge_t *right, int top, cairo_polygon_t *polygon) { if (left->deferred.right == right) return; if (left->deferred.right != NULL) { if (right != NULL && edges_colinear (left->deferred.right, right)) { /* continuation on right, so just swap edges */ left->deferred.right = right; return; } _cairo_bo_edge_end (left, top, polygon); } if (right != NULL && ! edges_colinear (left, right)) { left->deferred.top = top; left->deferred.right = right; } } static inline void _active_edges_to_polygon (cairo_bo_edge_t *left, int32_t top, cairo_fill_rule_t fill_rule, cairo_polygon_t *polygon) { cairo_bo_edge_t *right; unsigned int mask; if (fill_rule == CAIRO_FILL_RULE_WINDING) mask = ~0; else mask = 1; while (left != NULL) { int in_out = left->edge.dir; right = left->next; if (left->deferred.right == NULL) { while (right != NULL && right->deferred.right == NULL) right = right->next; if (right != NULL && edges_colinear (left, right)) { /* continuation on left */ left->deferred = right->deferred; right->deferred.right = NULL; } } right = left->next; while (right != NULL) { if (right->deferred.right != NULL) _cairo_bo_edge_end (right, top, polygon); in_out += right->edge.dir; if ((in_out & mask) == 0) { /* skip co-linear edges */ if (right->next == NULL || !edges_colinear (right, right->next)) break; } right = right->next; } _cairo_bo_edge_start_or_continue (left, right, top, polygon); left = right; if (left != NULL) left = left->next; } } static cairo_status_t _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, int num_events, cairo_fill_rule_t fill_rule, cairo_polygon_t *polygon) { cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ cairo_bo_event_queue_t event_queue; cairo_bo_sweep_line_t sweep_line; cairo_bo_event_t *event; cairo_bo_edge_t *left, *right; cairo_bo_edge_t *e1, *e2; _cairo_bo_event_queue_init (&event_queue, start_events, num_events); _cairo_bo_sweep_line_init (&sweep_line); while ((event = _cairo_bo_event_dequeue (&event_queue))) { if (event->point.y != sweep_line.current_y) { _active_edges_to_polygon (sweep_line.head, sweep_line.current_y, fill_rule, polygon); sweep_line.current_y = event->point.y; } switch (event->type) { case CAIRO_BO_EVENT_TYPE_START: e1 = &((cairo_bo_start_event_t *) event)->edge; status = _cairo_bo_sweep_line_insert (&sweep_line, e1); if (unlikely (status)) goto unwind; status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); if (unlikely (status)) goto unwind; left = e1->prev; right = e1->next; if (left != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); if (unlikely (status)) goto unwind; } if (right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_STOP: e1 = ((cairo_bo_queue_event_t *) event)->e1; _cairo_bo_event_queue_delete (&event_queue, event); left = e1->prev; right = e1->next; _cairo_bo_sweep_line_delete (&sweep_line, e1); if (e1->deferred.right != NULL) _cairo_bo_edge_end (e1, e1->edge.bottom, polygon); if (left != NULL && right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); if (unlikely (status)) goto unwind; } break; case CAIRO_BO_EVENT_TYPE_INTERSECTION: e1 = ((cairo_bo_queue_event_t *) event)->e1; e2 = ((cairo_bo_queue_event_t *) event)->e2; _cairo_bo_event_queue_delete (&event_queue, event); /* skip this intersection if its edges are not adjacent */ if (e2 != e1->next) break; left = e1->prev; right = e2->next; _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); /* after the swap e2 is left of e1 */ if (left != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); if (unlikely (status)) goto unwind; } if (right != NULL) { status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind; } break; } } unwind: _cairo_bo_event_queue_fini (&event_queue); return status; } cairo_status_t _cairo_polygon_reduce (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule) { cairo_status_t status; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; int num_limits; int num_events; int i; num_events = polygon->num_edges; if (unlikely (0 == num_events)) return CAIRO_STATUS_SUCCESS; if (DEBUG_POLYGON) { FILE *file = fopen ("reduce_in.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); } events = stack_events; event_ptrs = stack_event_ptrs; if (num_events > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (num_events, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + num_events); } for (i = 0; i < num_events; i++) { event_ptrs[i] = (cairo_bo_event_t *) &events[i]; events[i].type = CAIRO_BO_EVENT_TYPE_START; events[i].point.y = polygon->edges[i].top; events[i].point.x = _line_compute_intersection_x_for_y (&polygon->edges[i].line, events[i].point.y); events[i].edge.edge = polygon->edges[i]; events[i].edge.deferred.right = NULL; events[i].edge.prev = NULL; events[i].edge.next = NULL; } num_limits = polygon->num_limits; polygon->num_limits = 0; polygon->num_edges = 0; status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events, fill_rule, polygon); polygon->num_limits = num_limits; if (events != stack_events) free (events); if (DEBUG_POLYGON) { FILE *file = fopen ("reduce_out.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); } return status; } ������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-polygon.c���������������������������������0000664�0000000�0000000�00000040633�12710376503�0025234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-contour-private.h" #include "cairo-error-private.h" #define DEBUG_POLYGON 0 #if DEBUG_POLYGON && !NDEBUG static void assert_last_edge_is_valid(cairo_polygon_t *polygon, const cairo_box_t *limit) { cairo_edge_t *edge; cairo_fixed_t x; edge = &polygon->edges[polygon->num_edges-1]; assert (edge->bottom > edge->top); assert (edge->top >= limit->p1.y); assert (edge->bottom <= limit->p2.y); x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1, &edge->line.p2, edge->top); assert (x >= limit->p1.x); assert (x <= limit->p2.x); x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1, &edge->line.p2, edge->bottom); assert (x >= limit->p1.x); assert (x <= limit->p2.x); } #else #define assert_last_edge_is_valid(p, l) #endif static void _cairo_polygon_add_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, const cairo_point_t *p2, int dir); void _cairo_polygon_limit (cairo_polygon_t *polygon, const cairo_box_t *limits, int num_limits) { int n; polygon->limits = limits; polygon->num_limits = num_limits; if (polygon->num_limits) { polygon->limit = limits[0]; for (n = 1; n < num_limits; n++) { if (limits[n].p1.x < polygon->limit.p1.x) polygon->limit.p1.x = limits[n].p1.x; if (limits[n].p1.y < polygon->limit.p1.y) polygon->limit.p1.y = limits[n].p1.y; if (limits[n].p2.x > polygon->limit.p2.x) polygon->limit.p2.x = limits[n].p2.x; if (limits[n].p2.y > polygon->limit.p2.y) polygon->limit.p2.y = limits[n].p2.y; } } } void _cairo_polygon_limit_to_clip (cairo_polygon_t *polygon, const cairo_clip_t *clip) { if (clip) _cairo_polygon_limit (polygon, clip->boxes, clip->num_boxes); else _cairo_polygon_limit (polygon, 0, 0); } void _cairo_polygon_init (cairo_polygon_t *polygon, const cairo_box_t *limits, int num_limits) { VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); polygon->status = CAIRO_STATUS_SUCCESS; polygon->num_edges = 0; polygon->edges = polygon->edges_embedded; polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; _cairo_polygon_limit (polygon, limits, num_limits); } void _cairo_polygon_init_with_clip (cairo_polygon_t *polygon, const cairo_clip_t *clip) { if (clip) _cairo_polygon_init (polygon, clip->boxes, clip->num_boxes); else _cairo_polygon_init (polygon, 0, 0); } cairo_status_t _cairo_polygon_init_boxes (cairo_polygon_t *polygon, const cairo_boxes_t *boxes) { const struct _cairo_boxes_chunk *chunk; int i; VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); polygon->status = CAIRO_STATUS_SUCCESS; polygon->num_edges = 0; polygon->edges = polygon->edges_embedded; polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); if (boxes->num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) { polygon->edges_size = 2 * boxes->num_boxes; polygon->edges = _cairo_malloc_ab (polygon->edges_size, 2*sizeof(cairo_edge_t)); if (unlikely (polygon->edges == NULL)) return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; polygon->limits = NULL; polygon->num_limits = 0; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_point_t p1, p2; p1 = chunk->base[i].p1; p2.x = p1.x; p2.y = chunk->base[i].p2.y; _cairo_polygon_add_edge (polygon, &p1, &p2, 1); p1 = chunk->base[i].p2; p2.x = p1.x; p2.y = chunk->base[i].p1.y; _cairo_polygon_add_edge (polygon, &p1, &p2, 1); } } return polygon->status; } cairo_status_t _cairo_polygon_init_box_array (cairo_polygon_t *polygon, cairo_box_t *boxes, int num_boxes) { int i; VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); polygon->status = CAIRO_STATUS_SUCCESS; polygon->num_edges = 0; polygon->edges = polygon->edges_embedded; polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); if (num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) { polygon->edges_size = 2 * num_boxes; polygon->edges = _cairo_malloc_ab (polygon->edges_size, 2*sizeof(cairo_edge_t)); if (unlikely (polygon->edges == NULL)) return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; polygon->limits = NULL; polygon->num_limits = 0; for (i = 0; i < num_boxes; i++) { cairo_point_t p1, p2; p1 = boxes[i].p1; p2.x = p1.x; p2.y = boxes[i].p2.y; _cairo_polygon_add_edge (polygon, &p1, &p2, 1); p1 = boxes[i].p2; p2.x = p1.x; p2.y = boxes[i].p1.y; _cairo_polygon_add_edge (polygon, &p1, &p2, 1); } return polygon->status; } void _cairo_polygon_fini (cairo_polygon_t *polygon) { if (polygon->edges != polygon->edges_embedded) free (polygon->edges); VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t))); } /* make room for at least one more edge */ static cairo_bool_t _cairo_polygon_grow (cairo_polygon_t *polygon) { cairo_edge_t *new_edges; int old_size = polygon->edges_size; int new_size = 4 * old_size; if (CAIRO_INJECT_FAULT ()) { polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } if (polygon->edges == polygon->edges_embedded) { new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); if (new_edges != NULL) memcpy (new_edges, polygon->edges, old_size * sizeof (cairo_edge_t)); } else { new_edges = _cairo_realloc_ab (polygon->edges, new_size, sizeof (cairo_edge_t)); } if (unlikely (new_edges == NULL)) { polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } polygon->edges = new_edges; polygon->edges_size = new_size; return TRUE; } static void _add_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, const cairo_point_t *p2, int top, int bottom, int dir) { cairo_edge_t *edge; assert (top < bottom); if (unlikely (polygon->num_edges == polygon->edges_size)) { if (! _cairo_polygon_grow (polygon)) return; } edge = &polygon->edges[polygon->num_edges++]; edge->line.p1 = *p1; edge->line.p2 = *p2; edge->top = top; edge->bottom = bottom; edge->dir = dir; if (top < polygon->extents.p1.y) polygon->extents.p1.y = top; if (bottom > polygon->extents.p2.y) polygon->extents.p2.y = bottom; if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) { cairo_fixed_t x = p1->x; if (top != p1->y) x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top); if (x < polygon->extents.p1.x) polygon->extents.p1.x = x; if (x > polygon->extents.p2.x) polygon->extents.p2.x = x; } if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) { cairo_fixed_t x = p2->x; if (bottom != p2->y) x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom); if (x < polygon->extents.p1.x) polygon->extents.p1.x = x; if (x > polygon->extents.p2.x) polygon->extents.p2.x = x; } } static void _add_clipped_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, const cairo_point_t *p2, const int top, const int bottom, const int dir) { cairo_point_t bot_left, top_right; cairo_fixed_t top_y, bot_y; int n; for (n = 0; n < polygon->num_limits; n++) { const cairo_box_t *limits = &polygon->limits[n]; cairo_fixed_t pleft, pright; if (top >= limits->p2.y) continue; if (bottom <= limits->p1.y) continue; bot_left.x = limits->p1.x; bot_left.y = limits->p2.y; top_right.x = limits->p2.x; top_right.y = limits->p1.y; /* The useful region */ top_y = MAX (top, limits->p1.y); bot_y = MIN (bottom, limits->p2.y); /* The projection of the edge on the horizontal axis */ pleft = MIN (p1->x, p2->x); pright = MAX (p1->x, p2->x); if (limits->p1.x <= pleft && pright <= limits->p2.x) { /* Projection of the edge completely contained in the box: * clip vertically by restricting top and bottom */ _add_edge (polygon, p1, p2, top_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); } else if (pright <= limits->p1.x) { /* Projection of the edge to the left of the box: * replace with the left side of the box (clipped top/bottom) */ _add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); } else if (limits->p2.x <= pleft) { /* Projection of the edge to the right of the box: * replace with the right side of the box (clipped top/bottom) */ _add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); } else { /* The edge and the box intersect in a generic way */ cairo_fixed_t left_y, right_y; cairo_bool_t top_left_to_bottom_right; /* * The edge intersects the lines corresponding to the left * and right sides of the limit box at left_y and right_y, * but we need to add edges for the range from top_y to * bot_y. * * For both intersections, there are three cases: * * 1) It is outside the vertical range of the limit * box. In this case we can simply further clip the * edge we will be emitting (i.e. restrict its * top/bottom limits to those of the limit box). * * 2) It is inside the vertical range of the limit * box. In this case, we need to add the vertical edge * connecting the correct vertex to the intersection, * in order to preserve the winding count. * * 3) It is exactly on the box. In this case, do nothing. * * These operations restrict the active range (stored in * top_y/bot_y) so that the p1-p2 edge is completely * inside the box if it is clipped to this vertical range. */ top_left_to_bottom_right = (p1->x <= p2->x) == (p1->y <= p2->y); if (top_left_to_bottom_right) { if (pleft >= limits->p1.x) { left_y = top_y; } else { left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p1.x); if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x) left_y++; } left_y = MIN (left_y, bot_y); if (top_y < left_y) { _add_edge (polygon, &limits->p1, &bot_left, top_y, left_y, dir); assert_last_edge_is_valid (polygon, limits); top_y = left_y; } if (pright <= limits->p2.x) { right_y = bot_y; } else { right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p2.x); if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x) right_y--; } right_y = MAX (right_y, top_y); if (bot_y > right_y) { _add_edge (polygon, &top_right, &limits->p2, right_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); bot_y = right_y; } } else { if (pright <= limits->p2.x) { right_y = top_y; } else { right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p2.x); if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x) right_y++; } right_y = MIN (right_y, bot_y); if (top_y < right_y) { _add_edge (polygon, &top_right, &limits->p2, top_y, right_y, dir); assert_last_edge_is_valid (polygon, limits); top_y = right_y; } if (pleft >= limits->p1.x) { left_y = bot_y; } else { left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, limits->p1.x); if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x) left_y--; } left_y = MAX (left_y, top_y); if (bot_y > left_y) { _add_edge (polygon, &limits->p1, &bot_left, left_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); bot_y = left_y; } } if (top_y != bot_y) { _add_edge (polygon, p1, p2, top_y, bot_y, dir); assert_last_edge_is_valid (polygon, limits); } } } } static void _cairo_polygon_add_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, const cairo_point_t *p2, int dir) { /* drop horizontal edges */ if (p1->y == p2->y) return; if (p1->y > p2->y) { const cairo_point_t *t; t = p1, p1 = p2, p2 = t; dir = -dir; } if (polygon->num_limits) { if (p2->y <= polygon->limit.p1.y) return; if (p1->y >= polygon->limit.p2.y) return; _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir); } else _add_edge (polygon, p1, p2, p1->y, p2->y, dir); } cairo_status_t _cairo_polygon_add_external_edge (void *polygon, const cairo_point_t *p1, const cairo_point_t *p2) { _cairo_polygon_add_edge (polygon, p1, p2, 1); return _cairo_polygon_status (polygon); } cairo_status_t _cairo_polygon_add_line (cairo_polygon_t *polygon, const cairo_line_t *line, int top, int bottom, int dir) { /* drop horizontal edges */ if (line->p1.y == line->p2.y) return CAIRO_STATUS_SUCCESS; if (bottom <= top) return CAIRO_STATUS_SUCCESS; if (polygon->num_limits) { if (line->p2.y <= polygon->limit.p1.y) return CAIRO_STATUS_SUCCESS; if (line->p1.y >= polygon->limit.p2.y) return CAIRO_STATUS_SUCCESS; _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir); } else _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir); return polygon->status; } cairo_status_t _cairo_polygon_add_contour (cairo_polygon_t *polygon, const cairo_contour_t *contour) { const struct _cairo_contour_chain *chain; const cairo_point_t *prev = NULL; int i; if (contour->chain.num_points <= 1) return CAIRO_INT_STATUS_SUCCESS; prev = &contour->chain.points[0]; for (chain = &contour->chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { _cairo_polygon_add_edge (polygon, prev, &chain->points[i], contour->direction); prev = &chain->points[i]; } } return polygon->status; } void _cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy) { int n; dx = _cairo_fixed_from_int (dx); dy = _cairo_fixed_from_int (dy); polygon->extents.p1.x += dx; polygon->extents.p2.x += dx; polygon->extents.p1.y += dy; polygon->extents.p2.y += dy; for (n = 0; n < polygon->num_edges; n++) { cairo_edge_t *e = &polygon->edges[n]; e->top += dy; e->bottom += dy; e->line.p1.x += dx; e->line.p2.x += dx; e->line.p1.y += dy; e->line.p2.y += dy; } } �����������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-private.h���������������������������������0000664�0000000�0000000�00000004004�12710376503�0025214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth <cworth@redhat.com> */ #ifndef CAIRO_PRIVATE_H #define CAIRO_PRIVATE_H #include "cairo-types-private.h" #include "cairo-reference-count-private.h" CAIRO_BEGIN_DECLS struct _cairo { cairo_reference_count_t ref_count; cairo_status_t status; cairo_user_data_array_t user_data; const cairo_backend_t *backend; }; cairo_private cairo_t * _cairo_create_in_error (cairo_status_t status); cairo_private void _cairo_init (cairo_t *cr, const cairo_backend_t *backend); cairo_private void _cairo_fini (cairo_t *cr); CAIRO_END_DECLS #endif /* CAIRO_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ps-surface-private.h����������������������0000664�0000000�0000000�00000006552�12710376503�0027274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Keith Packard <keithp@keithp.com> */ #ifndef CAIRO_PS_SURFACE_PRIVATE_H #define CAIRO_PS_SURFACE_PRIVATE_H #include "cairo-ps.h" #include "cairo-surface-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include <time.h> typedef struct cairo_ps_surface { cairo_surface_t base; /* Here final_stream corresponds to the stream/file passed to * cairo_ps_surface_create surface is built. Meanwhile stream is a * temporary stream in which the file output is built, (so that * the header can be built and inserted into the target stream * before the contents of the temporary stream are copied). */ cairo_output_stream_t *final_stream; FILE *tmpfile; cairo_output_stream_t *stream; cairo_bool_t eps; cairo_content_t content; double width; double height; cairo_rectangle_int_t page_bbox; int bbox_x1, bbox_y1, bbox_x2, bbox_y2; cairo_matrix_t cairo_to_ps; cairo_bool_t use_string_datasource; cairo_bool_t current_pattern_is_solid_color; cairo_color_t current_color; int num_pages; cairo_paginated_mode_t paginated_mode; cairo_bool_t force_fallbacks; cairo_bool_t has_creation_date; time_t creation_date; cairo_scaled_font_subsets_t *font_subsets; cairo_list_t document_media; cairo_array_t dsc_header_comments; cairo_array_t dsc_setup_comments; cairo_array_t dsc_page_setup_comments; cairo_array_t *dsc_comment_target; cairo_ps_level_t ps_level; cairo_ps_level_t ps_level_used; cairo_surface_clipper_t clipper; cairo_pdf_operators_t pdf_operators; cairo_surface_t *paginated_surface; } cairo_ps_surface_t; #endif /* CAIRO_PS_SURFACE_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ps-surface.c������������������������������0000664�0000000�0000000�00000427250�12710376503�0025621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2005 Red Hat, Inc * Copyright © 2007,2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Keith Packard <keithp@keithp.com> * Adrian Johnson <ajohnson@redneon.com> */ /* * Design of the PS output: * * The PS output is harmonised with the PDF operations using PS procedures * to emulate the PDF operators. * * This has a number of advantages: * 1. A large chunk of code is shared between the PDF and PS backends. * See cairo-pdf-operators. * 2. Using gs to do PS -> PDF and PDF -> PS will always work well. */ #define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */ #include "cairoint.h" #include "cairo-ps.h" #include "cairo-ps-surface-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-pdf-shading-private.h" #include "cairo-array-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-list-inline.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-output-stream-private.h" #include "cairo-type3-glyph-surface-private.h" #include "cairo-image-info-private.h" #include <stdio.h> #include <ctype.h> #include <time.h> #include <zlib.h> #include <errno.h> #define DEBUG_PS 0 #if DEBUG_PS #define DEBUG_FALLBACK(s) \ fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s)) #else #define DEBUG_FALLBACK(s) #endif #ifndef HAVE_CTIME_R #define ctime_r(T, BUF) ctime (T) #endif /** * SECTION:cairo-ps * @Title: PostScript Surfaces * @Short_Description: Rendering PostScript documents * @See_Also: #cairo_surface_t * * The PostScript surface is used to render cairo graphics to Adobe * PostScript files and is a multi-page vector surface backend. **/ /** * CAIRO_HAS_PS_SURFACE: * * Defined if the PostScript surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.2 **/ typedef enum { CAIRO_PS_COMPRESS_NONE, CAIRO_PS_COMPRESS_LZW, CAIRO_PS_COMPRESS_DEFLATE } cairo_ps_compress_t; static const cairo_surface_backend_t cairo_ps_surface_backend; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; static cairo_bool_t _cairo_ps_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); static const cairo_ps_level_t _cairo_ps_levels[] = { CAIRO_PS_LEVEL_2, CAIRO_PS_LEVEL_3 }; #define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels) static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = { "PS Level 2", "PS Level 3" }; static const char *_cairo_ps_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, NULL }; typedef struct _cairo_page_standard_media { const char *name; int width; int height; } cairo_page_standard_media_t; static const cairo_page_standard_media_t _cairo_page_standard_media[] = { { "A0", 2384, 3371 }, { "A1", 1685, 2384 }, { "A2", 1190, 1684 }, { "A3", 842, 1190 }, { "A4", 595, 842 }, { "A5", 420, 595 }, { "B4", 729, 1032 }, { "B5", 516, 729 }, { "Letter", 612, 792 }, { "Tabloid", 792, 1224 }, { "Ledger", 1224, 792 }, { "Legal", 612, 1008 }, { "Statement", 396, 612 }, { "Executive", 540, 720 }, { "Folio", 612, 936 }, { "Quarto", 610, 780 }, { "10x14", 720, 1008 }, }; typedef struct _cairo_page_media { char *name; int width; int height; cairo_list_t link; } cairo_page_media_t; static void _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) { char ctime_buf[26]; time_t now; char **comments; int i, num_comments; int level; const char *eps_header = ""; cairo_bool_t has_bbox; if (surface->has_creation_date) now = surface->creation_date; else now = time (NULL); if (surface->ps_level_used == CAIRO_PS_LEVEL_2) level = 2; else level = 3; if (surface->eps) eps_header = " EPSF-3.0"; _cairo_output_stream_printf (surface->final_stream, "%%!PS-Adobe-3.0%s\n" "%%%%Creator: cairo %s (http://cairographics.org)\n" "%%%%CreationDate: %s" "%%%%Pages: %d\n", eps_header, cairo_version_string (), ctime_r (&now, ctime_buf), surface->num_pages); _cairo_output_stream_printf (surface->final_stream, "%%%%DocumentData: Clean7Bit\n" "%%%%LanguageLevel: %d\n", level); if (!cairo_list_is_empty (&surface->document_media)) { cairo_page_media_t *page; cairo_bool_t first = TRUE; cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { if (first) { _cairo_output_stream_printf (surface->final_stream, "%%%%DocumentMedia: "); first = FALSE; } else { _cairo_output_stream_printf (surface->final_stream, "%%%%+ "); } _cairo_output_stream_printf (surface->final_stream, "%s %d %d 0 () ()\n", page->name, page->width, page->height); } } has_bbox = FALSE; num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); comments = _cairo_array_index (&surface->dsc_header_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->final_stream, "%s\n", comments[i]); if (strncmp (comments[i], "%%BoundingBox:", 14) == 0) has_bbox = TRUE; free (comments[i]); comments[i] = NULL; } if (!has_bbox) { _cairo_output_stream_printf (surface->final_stream, "%%%%BoundingBox: %d %d %d %d\n", surface->bbox_x1, surface->bbox_y1, surface->bbox_x2, surface->bbox_y2); } _cairo_output_stream_printf (surface->final_stream, "%%%%EndComments\n"); _cairo_output_stream_printf (surface->final_stream, "%%%%BeginProlog\n"); if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, "save\n" "50 dict begin\n"); } else { _cairo_output_stream_printf (surface->final_stream, "/languagelevel where\n" "{ pop languagelevel } { 1 } ifelse\n" "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n" " (This print job requires a PostScript Language Level %d printer.) show\n" " showpage quit } if\n", level, level); } _cairo_output_stream_printf (surface->final_stream, "/q { gsave } bind def\n" "/Q { grestore } bind def\n" "/cm { 6 array astore concat } bind def\n" "/w { setlinewidth } bind def\n" "/J { setlinecap } bind def\n" "/j { setlinejoin } bind def\n" "/M { setmiterlimit } bind def\n" "/d { setdash } bind def\n" "/m { moveto } bind def\n" "/l { lineto } bind def\n" "/c { curveto } bind def\n" "/h { closepath } bind def\n" "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n" " 0 exch rlineto 0 rlineto closepath } bind def\n" "/S { stroke } bind def\n" "/f { fill } bind def\n" "/f* { eofill } bind def\n" "/n { newpath } bind def\n" "/W { clip } bind def\n" "/W* { eoclip } bind def\n" "/BT { } bind def\n" "/ET { } bind def\n" "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" " { globaldict begin /?pdfmark /pop load def /pdfmark\n" " /cleartomark load def end } ifelse\n" "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" "/EMC { mark /EMC pdfmark } bind def\n" "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n" "/Tj { show currentpoint cairo_store_point } bind def\n" "/TJ {\n" " {\n" " dup\n" " type /stringtype eq\n" " { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n" " } forall\n" " currentpoint cairo_store_point\n" "} bind def\n" "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n" " cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n" "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n" " { pop cairo_selectfont } if } bind def\n" "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n" " /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n" " /cairo_font where { pop cairo_selectfont } if } bind def\n" "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n" " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n" "/g { setgray } bind def\n" "/rg { setrgbcolor } bind def\n" "/d1 { setcachedevice } bind def\n"); _cairo_output_stream_printf (surface->final_stream, "%%%%EndProlog\n"); num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); if (num_comments) { _cairo_output_stream_printf (surface->final_stream, "%%%%BeginSetup\n"); comments = _cairo_array_index (&surface->dsc_setup_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->final_stream, "%s\n", comments[i]); free (comments[i]); comments[i] = NULL; } _cairo_output_stream_printf (surface->final_stream, "%%%%EndSetup\n"); } } static cairo_status_t _cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_type1_subset_t subset; cairo_status_t status; int length; char name[64]; snprintf (name, sizeof name, "f-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE); if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_type1_font_subset\n"); #endif length = subset.header_length + subset.data_length + subset.trailer_length; _cairo_output_stream_write (surface->final_stream, subset.data, length); _cairo_type1_subset_fini (&subset); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_type1_subset_t subset; cairo_status_t status; int length; char name[64]; snprintf (name, sizeof name, "f-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_hex (&subset, name, font_subset); if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_type1_font_fallback\n"); #endif length = subset.header_length + subset.data_length + subset.trailer_length; _cairo_output_stream_write (surface->final_stream, subset.data, length); _cairo_type1_fallback_fini (&subset); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_truetype_subset_t subset; cairo_status_t status; unsigned int i, begin, end; status = _cairo_truetype_subset_init_ps (&subset, font_subset); if (unlikely (status)) return status; /* FIXME: Figure out document structure convention for fonts */ #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_truetype_font_subset\n"); #endif _cairo_output_stream_printf (surface->final_stream, "11 dict begin\n" "/FontType 42 def\n" "/FontName /%s def\n" "/PaintType 0 def\n" "/FontMatrix [ 1 0 0 1 0 0 ] def\n" "/FontBBox [ 0 0 0 0 ] def\n" "/Encoding 256 array def\n" "0 1 255 { Encoding exch /.notdef put } for\n", subset.ps_name); /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ if (font_subset->is_latin) { for (i = 1; i < 256; i++) { if (font_subset->latin_to_subset_glyph_index[i] > 0) { if (font_subset->glyph_names != NULL) { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /%s put\n", i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]); } else { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]); } } } } else { for (i = 1; i < font_subset->num_glyphs; i++) { if (font_subset->glyph_names != NULL) { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /%s put\n", i, font_subset->glyph_names[i]); } else { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /g%d put\n", i, i); } } } _cairo_output_stream_printf (surface->final_stream, "/CharStrings %d dict dup begin\n" "/.notdef 0 def\n", font_subset->num_glyphs); for (i = 1; i < font_subset->num_glyphs; i++) { if (font_subset->glyph_names != NULL) { _cairo_output_stream_printf (surface->final_stream, "/%s %d def\n", font_subset->glyph_names[i], i); } else { _cairo_output_stream_printf (surface->final_stream, "/g%d %d def\n", i, i); } } _cairo_output_stream_printf (surface->final_stream, "end readonly def\n"); _cairo_output_stream_printf (surface->final_stream, "/sfnts [\n"); begin = 0; end = 0; for (i = 0; i < subset.num_string_offsets; i++) { end = subset.string_offsets[i]; _cairo_output_stream_printf (surface->final_stream,"<"); _cairo_output_stream_write_hex_string (surface->final_stream, subset.data + begin, end - begin); _cairo_output_stream_printf (surface->final_stream,"00>\n"); begin = end; } if (subset.data_length > end) { _cairo_output_stream_printf (surface->final_stream,"<"); _cairo_output_stream_write_hex_string (surface->final_stream, subset.data + end, subset.data_length - end); _cairo_output_stream_printf (surface->final_stream,"00>\n"); } _cairo_output_stream_printf (surface->final_stream, "] def\n" "/f-%d-%d currentdict end definefont pop\n", font_subset->font_id, font_subset->subset_id); _cairo_truetype_subset_fini (&subset); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_emit_imagemask (cairo_image_surface_t *image, cairo_output_stream_t *stream) { uint8_t *row, *byte; int rows, cols; /* The only image type supported by Type 3 fonts are 1-bit image * masks */ assert (image->format == CAIRO_FORMAT_A1); _cairo_output_stream_printf (stream, "<<\n" " /ImageType 1\n" " /Width %d\n" " /Height %d\n" " /ImageMatrix [%d 0 0 %d 0 %d]\n" " /Decode [1 0]\n" " /BitsPerComponent 1\n", image->width, image->height, image->width, -image->height, image->height); _cairo_output_stream_printf (stream, " /DataSource {<\n "); for (row = image->data, rows = image->height; rows; row += image->stride, rows--) { for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); _cairo_output_stream_printf (stream, "%02x ", output_byte); } _cairo_output_stream_printf (stream, "\n "); } _cairo_output_stream_printf (stream, ">}\n>>\n"); _cairo_output_stream_printf (stream, "imagemask\n"); return _cairo_output_stream_get_status (stream); } static cairo_int_status_t _cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_ps_surface_t *surface = closure; cairo_status_t status = CAIRO_STATUS_SUCCESS; unsigned int i; cairo_surface_t *type3_surface; type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_ps_emit_imagemask, surface->font_subsets); for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, font_subset->glyphs[i]); if (unlikely (status)) break; } cairo_surface_finish (type3_surface); cairo_surface_destroy (type3_surface); return status; } static cairo_status_t _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_status_t status; unsigned int i; cairo_box_t font_bbox = {{0,0},{0,0}}; cairo_box_t bbox = {{0,0},{0,0}}; cairo_surface_t *type3_surface; double width; if (font_subset->num_glyphs == 0) return CAIRO_STATUS_SUCCESS; #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_type3_font_subset\n"); #endif _cairo_output_stream_printf (surface->final_stream, "8 dict begin\n" "/FontType 3 def\n" "/FontMatrix [1 0 0 1 0 0] def\n" "/Encoding 256 array def\n" "0 1 255 { Encoding exch /.notdef put } for\n"); type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_ps_emit_imagemask, surface->font_subsets); status = type3_surface->status; if (unlikely (status)) return status; for (i = 0; i < font_subset->num_glyphs; i++) { if (font_subset->glyph_names != NULL) { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /%s put\n", i, font_subset->glyph_names[i]); } else { _cairo_output_stream_printf (surface->final_stream, "Encoding %d /g%d put\n", i, i); } } _cairo_output_stream_printf (surface->final_stream, "/Glyphs [\n"); for (i = 0; i < font_subset->num_glyphs; i++) { _cairo_output_stream_printf (surface->final_stream, " { %% %d\n", i); status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, surface->final_stream, font_subset->glyphs[i], &bbox, &width); if (unlikely (status)) break; _cairo_output_stream_printf (surface->final_stream, " }\n"); if (i == 0) { font_bbox.p1.x = bbox.p1.x; font_bbox.p1.y = bbox.p1.y; font_bbox.p2.x = bbox.p2.x; font_bbox.p2.y = bbox.p2.y; } else { if (bbox.p1.x < font_bbox.p1.x) font_bbox.p1.x = bbox.p1.x; if (bbox.p1.y < font_bbox.p1.y) font_bbox.p1.y = bbox.p1.y; if (bbox.p2.x > font_bbox.p2.x) font_bbox.p2.x = bbox.p2.x; if (bbox.p2.y > font_bbox.p2.y) font_bbox.p2.y = bbox.p2.y; } } cairo_surface_finish (type3_surface); cairo_surface_destroy (type3_surface); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->final_stream, "] def\n" "/FontBBox [%f %f %f %f] def\n" "/BuildChar {\n" " exch /Glyphs get\n" " exch get\n" " 10 dict begin exec end\n" "} bind def\n" "currentdict\n" "end\n" "/f-%d-%d exch definefont pop\n", _cairo_fixed_to_double (font_bbox.p1.x), - _cairo_fixed_to_double (font_bbox.p2.y), _cairo_fixed_to_double (font_bbox.p2.x), - _cairo_fixed_to_double (font_bbox.p1.y), font_subset->font_id, font_subset->subset_id); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_ps_surface_t *surface = closure; cairo_int_status_t status; status = _cairo_scaled_font_subset_create_glyph_names (font_subset); if (_cairo_int_status_is_error (status)) return status; status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_ps_surface_t *surface = closure; cairo_int_status_t status; status = _cairo_scaled_font_subset_create_glyph_names (font_subset); if (_cairo_int_status_is_error (status)) return status; status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) { cairo_status_t status; #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_font_subsets\n"); #endif status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_ps_surface_analyze_user_font_subset, surface); if (unlikely (status)) return status; status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, _cairo_ps_surface_emit_unscaled_font_subset, surface); if (unlikely (status)) return status; status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, _cairo_ps_surface_emit_scaled_font_subset, surface); if (unlikely (status)) return status; return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_ps_surface_emit_scaled_font_subset, surface); } static cairo_status_t _cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) { char buf[4096]; int n; if (ferror (surface->tmpfile) != 0) return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); rewind (surface->tmpfile); while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0) _cairo_output_stream_write (surface->final_stream, buf, n); if (ferror (surface->tmpfile) != 0) return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); return CAIRO_STATUS_SUCCESS; } static void _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) { _cairo_output_stream_printf (surface->final_stream, "%%%%Trailer\n"); if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, "end restore\n"); } _cairo_output_stream_printf (surface->final_stream, "%%%%EOF\n"); } static cairo_bool_t _path_covers_bbox (cairo_ps_surface_t *surface, cairo_path_fixed_t *path) { cairo_box_t box; if (_cairo_path_fixed_is_box (path, &box)) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (&box, &rect); /* skip trivial whole-page clips */ if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { if (rect.x == surface->page_bbox.x && rect.width == surface->page_bbox.width && rect.y == surface->page_bbox.y && rect.height == surface->page_bbox.height) { return TRUE; } } } return FALSE; } static cairo_status_t _cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_ps_surface_t *surface = cairo_container_of (clipper, cairo_ps_surface_t, clipper); cairo_output_stream_t *stream = surface->stream; cairo_status_t status; assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE); #if DEBUG_PS _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_intersect_clip_path\n"); #endif if (path == NULL) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (stream, "Q q\n"); surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); return CAIRO_STATUS_SUCCESS; } if (_path_covers_bbox (surface, path)) return CAIRO_STATUS_SUCCESS; return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); } /* PLRM specifies a tolerance of 5 points when matching page sizes */ static cairo_bool_t _ps_page_dimension_equal (int a, int b) { return (abs (a - b) < 5); } static const char * _cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) { int width, height, i; char buf[50]; cairo_page_media_t *page; const char *page_name; width = _cairo_lround (surface->width); height = _cairo_lround (surface->height); /* search previously used page sizes */ cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { if (_ps_page_dimension_equal (width, page->width) && _ps_page_dimension_equal (height, page->height)) return page->name; } /* search list of standard page sizes */ page_name = NULL; for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) { if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) && _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height)) { page_name = _cairo_page_standard_media[i].name; width = _cairo_page_standard_media[i].width; height = _cairo_page_standard_media[i].height; break; } } page = malloc (sizeof (cairo_page_media_t)); if (unlikely (page == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } if (page_name) { page->name = strdup (page_name); } else { snprintf (buf, sizeof (buf), "%dx%dmm", (int) _cairo_lround (surface->width * 25.4/72), (int) _cairo_lround (surface->height * 25.4/72)); page->name = strdup (buf); } if (unlikely (page->name == NULL)) { free (page); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } page->width = width; page->height = height; cairo_list_add_tail (&page->link, &surface->document_media); return page->name; } static cairo_surface_t * _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, double width, double height) { cairo_status_t status, status_ignored; cairo_ps_surface_t *surface; surface = malloc (sizeof (cairo_ps_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } _cairo_surface_init (&surface->base, &cairo_ps_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); surface->final_stream = stream; surface->tmpfile = tmpfile (); if (surface->tmpfile == NULL) { switch (errno) { case ENOMEM: status = _cairo_error (CAIRO_STATUS_NO_MEMORY); break; default: status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); break; } goto CLEANUP_SURFACE; } surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); status = _cairo_output_stream_get_status (surface->stream); if (unlikely (status)) goto CLEANUP_OUTPUT_STREAM; surface->font_subsets = _cairo_scaled_font_subsets_create_simple (); if (unlikely (surface->font_subsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_OUTPUT_STREAM; } _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); surface->has_creation_date = FALSE; surface->eps = FALSE; surface->ps_level = CAIRO_PS_LEVEL_3; surface->ps_level_used = CAIRO_PS_LEVEL_2; surface->width = width; surface->height = height; cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->content = CAIRO_CONTENT_COLOR_ALPHA; surface->use_string_datasource = FALSE; surface->current_pattern_is_solid_color = FALSE; surface->page_bbox.x = 0; surface->page_bbox.y = 0; surface->page_bbox.width = width; surface->page_bbox.height = height; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, &surface->cairo_to_ps, surface->font_subsets); surface->num_pages = 0; cairo_list_init (&surface->document_media); _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); surface->dsc_comment_target = &surface->dsc_header_comments; surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, &cairo_ps_surface_paginated_backend); status = surface->paginated_surface->status; if (status == CAIRO_STATUS_SUCCESS) { /* paginated keeps the only reference to surface now, drop ours */ cairo_surface_destroy (&surface->base); return surface->paginated_surface; } _cairo_scaled_font_subsets_destroy (surface->font_subsets); CLEANUP_OUTPUT_STREAM: status_ignored = _cairo_output_stream_destroy (surface->stream); fclose (surface->tmpfile); CLEANUP_SURFACE: free (surface); CLEANUP: /* destroy stream on behalf of caller */ status_ignored = _cairo_output_stream_destroy (stream); return _cairo_surface_create_in_error (status); } /** * cairo_ps_surface_create: * @filename: a filename for the PS output (must be writable), %NULL may be * used to specify no output. This will generate a PS surface that * may be queried and used as a source, without generating a * temporary file. * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a PostScript surface of the specified size in points to be * written to @filename. See cairo_ps_surface_create_for_stream() for * a more flexible mechanism for handling the PostScript output than * simply writing it to a named file. * * Note that the size of individual pages of the PostScript output can * vary. See cairo_ps_surface_set_size(). * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_ps_surface_create (const char *filename, double width_in_points, double height_in_points) { cairo_output_stream_t *stream; stream = _cairo_output_stream_create_for_filename (filename); if (_cairo_output_stream_get_status (stream)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); return _cairo_ps_surface_create_for_stream_internal (stream, width_in_points, height_in_points); } /** * cairo_ps_surface_create_for_stream: * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL * to indicate a no-op @write_func. With a no-op @write_func, * the surface may be queried or used as a source without * generating any temporary files. * @closure: the closure argument for @write_func * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a PostScript surface of the specified size in points to be * written incrementally to the stream represented by @write_func and * @closure. See cairo_ps_surface_create() for a more convenient way * to simply direct the PostScript output to a named file. * * Note that the size of individual pages of the PostScript * output can vary. See cairo_ps_surface_set_size(). * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points) { cairo_output_stream_t *stream; stream = _cairo_output_stream_create (write_func, NULL, closure); if (_cairo_output_stream_get_status (stream)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); return _cairo_ps_surface_create_for_stream_internal (stream, width_in_points, height_in_points); } static cairo_bool_t _cairo_surface_is_ps (cairo_surface_t *surface) { return surface->backend == &cairo_ps_surface_backend; } /* If the abstract_surface is a paginated surface, and that paginated * surface's target is a ps_surface, then set ps_surface to that * target. Otherwise return FALSE. */ static cairo_bool_t _extract_ps_surface (cairo_surface_t *surface, cairo_bool_t set_error_on_failure, cairo_ps_surface_t **ps_surface) { cairo_surface_t *target; if (surface->status) return FALSE; if (surface->finished) { if (set_error_on_failure) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_paginated (surface)) { if (set_error_on_failure) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } target = _cairo_paginated_surface_get_target (surface); if (target->status) { if (set_error_on_failure) _cairo_surface_set_error (surface, target->status); return FALSE; } if (target->finished) { if (set_error_on_failure) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_ps (target)) { if (set_error_on_failure) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } *ps_surface = (cairo_ps_surface_t *) target; return TRUE; } /** * cairo_ps_surface_restrict_to_level: * @surface: a PostScript #cairo_surface_t * @level: PostScript level * * Restricts the generated PostSript file to @level. See * cairo_ps_get_levels() for a list of available level values that * can be used here. * * This function should only be called before any drawing operations * have been performed on the given surface. The simplest way to do * this is to call this function immediately after creating the * surface. * * Since: 1.6 **/ void cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, cairo_ps_level_t level) { cairo_ps_surface_t *ps_surface = NULL; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; if (level < CAIRO_PS_LEVEL_LAST) ps_surface->ps_level = level; } /** * cairo_ps_get_levels: * @levels: supported level list * @num_levels: list length * * Used to retrieve the list of supported levels. See * cairo_ps_surface_restrict_to_level(). * * Since: 1.6 **/ void cairo_ps_get_levels (cairo_ps_level_t const **levels, int *num_levels) { if (levels != NULL) *levels = _cairo_ps_levels; if (num_levels != NULL) *num_levels = CAIRO_PS_LEVEL_LAST; } /** * cairo_ps_level_to_string: * @level: a level id * * Get the string representation of the given @level id. This function * will return %NULL if @level id isn't valid. See cairo_ps_get_levels() * for a way to get the list of valid level ids. * * Return value: the string associated to given level. * * Since: 1.6 **/ const char * cairo_ps_level_to_string (cairo_ps_level_t level) { if (level >= CAIRO_PS_LEVEL_LAST) return NULL; return _cairo_ps_level_strings[level]; } /** * cairo_ps_surface_set_eps: * @surface: a PostScript #cairo_surface_t * @eps: %TRUE to output EPS format PostScript * * If @eps is %TRUE, the PostScript surface will output Encapsulated * PostScript. * * This function should only be called before any drawing operations * have been performed on the current page. The simplest way to do * this is to call this function immediately after creating the * surface. An Encapsulated PostScript file should never contain more * than one page. * * Since: 1.6 **/ void cairo_ps_surface_set_eps (cairo_surface_t *surface, cairo_bool_t eps) { cairo_ps_surface_t *ps_surface = NULL; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; ps_surface->eps = eps; } /** * cairo_ps_surface_get_eps: * @surface: a PostScript #cairo_surface_t * * Check whether the PostScript surface will output Encapsulated PostScript. * * Return value: %TRUE if the surface will output Encapsulated PostScript. * * Since: 1.6 **/ cairo_public cairo_bool_t cairo_ps_surface_get_eps (cairo_surface_t *surface) { cairo_ps_surface_t *ps_surface = NULL; if (! _extract_ps_surface (surface, FALSE, &ps_surface)) return FALSE; return ps_surface->eps; } /** * cairo_ps_surface_set_size: * @surface: a PostScript #cairo_surface_t * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) * * Changes the size of a PostScript surface for the current (and * subsequent) pages. * * This function should only be called before any drawing operations * have been performed on the current page. The simplest way to do * this is to call this function immediately after creating the * surface or immediately after completing a page with either * cairo_show_page() or cairo_copy_page(). * * Since: 1.2 **/ void cairo_ps_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points) { cairo_ps_surface_t *ps_surface = NULL; cairo_status_t status; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; ps_surface->width = width_in_points; ps_surface->height = height_in_points; cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, &ps_surface->cairo_to_ps); status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, width_in_points, height_in_points); if (status) status = _cairo_surface_set_error (surface, status); } /** * cairo_ps_surface_dsc_comment: * @surface: a PostScript #cairo_surface_t * @comment: a comment string to be emitted into the PostScript output * * Emit a comment into the PostScript output for the given surface. * * The comment is expected to conform to the PostScript Language * Document Structuring Conventions (DSC). Please see that manual for * details on the available comments and their meanings. In * particular, the \%\%IncludeFeature comment allows a * device-independent means of controlling printer device features. So * the PostScript Printer Description Files Specification will also be * a useful reference. * * The comment string must begin with a percent character (\%) and the * total length of the string (including any initial percent * characters) must not exceed 255 characters. Violating either of * these conditions will place @surface into an error state. But * beyond these two conditions, this function will not enforce * conformance of the comment with any particular specification. * * The comment string should not have a trailing newline. * * The DSC specifies different sections in which particular comments * can appear. This function provides for comments to be emitted * within three sections: the header, the Setup section, and the * PageSetup section. Comments appearing in the first two sections * apply to the entire document while comments in the BeginPageSetup * section apply only to a single page. * * For comments to appear in the header section, this function should * be called after the surface is created, but before a call to * cairo_ps_surface_dsc_begin_setup(). * * For comments to appear in the Setup section, this function should * be called after a call to cairo_ps_surface_dsc_begin_setup() but * before a call to cairo_ps_surface_dsc_begin_page_setup(). * * For comments to appear in the PageSetup section, this function * should be called after a call to * cairo_ps_surface_dsc_begin_page_setup(). * * Note that it is only necessary to call * cairo_ps_surface_dsc_begin_page_setup() for the first page of any * surface. After a call to cairo_show_page() or cairo_copy_page() * comments are unambiguously directed to the PageSetup section of the * current page. But it doesn't hurt to call this function at the * beginning of every page as that consistency may make the calling * code simpler. * * As a final note, cairo automatically generates several comments on * its own. As such, applications must not manually generate any of * the following comments: * * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages, * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments. * * Setup section: \%\%BeginSetup, \%\%EndSetup * * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup. * * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF * * Here is an example sequence showing how this function might be used: * * <informalexample><programlisting> * cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); * ... * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") * ... * cairo_ps_surface_dsc_begin_setup (surface); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White"); * ... * cairo_ps_surface_dsc_begin_page_setup (surface); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue"); * ... draw to first page here .. * cairo_show_page (cr); * ... * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5"); * ... * </programlisting></informalexample> * * Since: 1.2 **/ void cairo_ps_surface_dsc_comment (cairo_surface_t *surface, const char *comment) { cairo_ps_surface_t *ps_surface = NULL; cairo_status_t status; char *comment_copy; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; /* A couple of sanity checks on the comment value. */ if (comment == NULL) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER); return; } if (comment[0] != '%' || strlen (comment) > 255) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT); return; } /* Then, copy the comment and store it in the appropriate array. */ comment_copy = strdup (comment); if (unlikely (comment_copy == NULL)) { status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); return; } status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); if (unlikely (status)) { free (comment_copy); status = _cairo_surface_set_error (surface, status); return; } } /** * cairo_ps_surface_dsc_begin_setup: * @surface: a PostScript #cairo_surface_t * * This function indicates that subsequent calls to * cairo_ps_surface_dsc_comment() should direct comments to the Setup * section of the PostScript output. * * This function should be called at most once per surface, and must * be called before any call to cairo_ps_surface_dsc_begin_page_setup() * and before any drawing is performed to the surface. * * See cairo_ps_surface_dsc_comment() for more details. * * Since: 1.2 **/ void cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface) { cairo_ps_surface_t *ps_surface = NULL; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments) ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments; } /** * cairo_ps_surface_dsc_begin_page_setup: * @surface: a PostScript #cairo_surface_t * * This function indicates that subsequent calls to * cairo_ps_surface_dsc_comment() should direct comments to the * PageSetup section of the PostScript output. * * This function call is only needed for the first page of a * surface. It should be called after any call to * cairo_ps_surface_dsc_begin_setup() and before any drawing is * performed to the surface. * * See cairo_ps_surface_dsc_comment() for more details. * * Since: 1.2 **/ void cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) { cairo_ps_surface_t *ps_surface = NULL; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments || ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments) { ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments; } } static cairo_status_t _cairo_ps_surface_finish (void *abstract_surface) { cairo_status_t status, status2; cairo_ps_surface_t *surface = abstract_surface; int i, num_comments; char **comments; status = surface->base.status; if (unlikely (status)) goto CLEANUP; _cairo_ps_surface_emit_header (surface); status = _cairo_ps_surface_emit_font_subsets (surface); if (unlikely (status)) goto CLEANUP; status = _cairo_ps_surface_emit_body (surface); if (unlikely (status)) goto CLEANUP; _cairo_ps_surface_emit_footer (surface); CLEANUP: _cairo_scaled_font_subsets_destroy (surface->font_subsets); status2 = _cairo_output_stream_destroy (surface->stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; fclose (surface->tmpfile); status2 = _cairo_output_stream_destroy (surface->final_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; while (! cairo_list_is_empty (&surface->document_media)) { cairo_page_media_t *page; page = cairo_list_first_entry (&surface->document_media, cairo_page_media_t, link); cairo_list_del (&page->link); free (page->name); free (page); } num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); comments = _cairo_array_index (&surface->dsc_header_comments, 0); for (i = 0; i < num_comments; i++) free (comments[i]); _cairo_array_fini (&surface->dsc_header_comments); num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); comments = _cairo_array_index (&surface->dsc_setup_comments, 0); for (i = 0; i < num_comments; i++) free (comments[i]); _cairo_array_fini (&surface->dsc_setup_comments); num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); for (i = 0; i < num_comments; i++) free (comments[i]); _cairo_array_fini (&surface->dsc_page_setup_comments); _cairo_surface_clipper_reset (&surface->clipper); return status; } static cairo_int_status_t _cairo_ps_surface_start_page (void *abstract_surface) { cairo_ps_surface_t *surface = abstract_surface; /* Increment before print so page numbers start at 1. */ surface->num_pages++; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_show_page (void *abstract_surface) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; if (surface->clipper.clip != NULL) _cairo_surface_clipper_reset (&surface->clipper); status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "Q Q\n" "showpage\n"); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t color_is_gray (double red, double green, double blue) { const double epsilon = 0.00001; return (fabs (red - green) < epsilon && fabs (red - blue) < epsilon); } /** * _cairo_ps_surface_acquire_source_surface_from_pattern: * @surface: the ps surface * @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source * @extents: extents of the operation that is using this source * @width: returns width of surface * @height: returns height of surface * @x_offset: returns x offset of surface * @y_offset: returns y offset of surface * @surface: returns surface of type image surface or recording surface * @image_extra: returns image extra for image type surface * * Acquire source surface or raster source pattern. **/ static cairo_status_t _cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *width, int *height, double *x_offset, double *y_offset, cairo_surface_t **source_surface, void **image_extra) { cairo_status_t status; cairo_image_surface_t *image; *x_offset = *y_offset = 0; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface; if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) { if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf; *width = sub->extents.width; *height = sub->extents.height; } else { cairo_surface_t *free_me = NULL; cairo_recording_surface_t *recording_surface; cairo_box_t bbox; cairo_rectangle_int_t extents; recording_surface = (cairo_recording_surface_t *) surf; if (_cairo_surface_is_snapshot (&recording_surface->base)) { free_me = _cairo_surface_snapshot_get_target (&recording_surface->base); recording_surface = (cairo_recording_surface_t *) free_me; } status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); cairo_surface_destroy (free_me); if (unlikely (status)) return status; _cairo_box_round_to_rectangle (&bbox, &extents); *width = extents.width; *height = extents.height; } *source_surface = surf; return CAIRO_STATUS_SUCCESS; } else { status = _cairo_surface_acquire_source_image (surf, &image, image_extra); if (unlikely (status)) return status; } } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { cairo_surface_t *surf; cairo_box_t box; cairo_rectangle_int_t rect; /* get the operation extents in pattern space */ _cairo_box_from_rectangle (&box, extents); _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); _cairo_box_round_to_rectangle (&box, &rect); surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, &rect); if (!surf) return CAIRO_INT_STATUS_UNSUPPORTED; assert (_cairo_surface_is_image (surf)); image = (cairo_image_surface_t *) surf; } break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: default: ASSERT_NOT_REACHED; break; } *width = image->width; *height = image->height; *source_surface = &image->base; return CAIRO_STATUS_SUCCESS; } static void _cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, cairo_surface_t *source, void *image_extra) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: { cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; if (surf_pat->surface->type != CAIRO_SURFACE_TYPE_RECORDING) { cairo_image_surface_t *image = (cairo_image_surface_t *) source; _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); } } break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: _cairo_raster_source_pattern_release (pattern, source); break; case CAIRO_PATTERN_TYPE_SOLID: case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: default: ASSERT_NOT_REACHED; break; } } /** * _cairo_ps_surface_create_padded_image_from_image: * @surface: the ps surface * @source: The source image * @extents: extents of the operation that is using this source * @width: returns width of padded image * @height: returns height of padded image * @x_offset: returns x offset of padded image * @y_offset: returns y offset of padded image * @image: returns the padded image or NULL if padding not required to fill @extents * * Creates a padded image if the source image does not fill the extents. **/ static cairo_status_t _cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t *surface, cairo_image_surface_t *source, const cairo_matrix_t *source_matrix, const cairo_rectangle_int_t *extents, int *width, int *height, double *x_offset, double *y_offset, cairo_image_surface_t **image) { cairo_box_t box; cairo_rectangle_int_t rect; cairo_surface_t *pad_image; cairo_surface_pattern_t pad_pattern; int w, h; cairo_int_status_t status; /* get the operation extents in pattern space */ _cairo_box_from_rectangle (&box, extents); _cairo_matrix_transform_bounding_box_fixed (source_matrix, &box, NULL); _cairo_box_round_to_rectangle (&box, &rect); /* Check if image needs padding to fill extents. */ w = source->width; h = source->height; if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || _cairo_fixed_integer_ceil(box.p1.y) < 0 || _cairo_fixed_integer_floor(box.p2.y) > w || _cairo_fixed_integer_floor(box.p2.y) > h) { pad_image = _cairo_image_surface_create_with_pixman_format (NULL, source->pixman_format, rect.width, rect.height, 0); if (pad_image->status) return pad_image->status; _cairo_pattern_init_for_surface (&pad_pattern, &source->base); cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y); pad_pattern.base.extend = CAIRO_EXTEND_PAD; status = _cairo_surface_paint (pad_image, CAIRO_OPERATOR_SOURCE, &pad_pattern.base, NULL); _cairo_pattern_fini (&pad_pattern.base); *image = (cairo_image_surface_t *) pad_image; *width = rect.width; *height = rect.height; *x_offset = rect.x; *y_offset = rect.y; } else { *image = NULL; status = CAIRO_STATUS_SUCCESS; } return status; } static cairo_int_status_t _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { int width, height; double x_offset, y_offset; cairo_surface_t *source; cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; cairo_image_transparency_t transparency; status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, &width, &height, &x_offset, &y_offset, &source, &image_extra); if (unlikely (status)) return status; image = (cairo_image_surface_t *) source; if (image->base.status) return image->base.status; transparency = _cairo_image_analyze_transparency (image); switch (transparency) { case CAIRO_IMAGE_IS_OPAQUE: status = CAIRO_STATUS_SUCCESS; break; case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: if (surface->ps_level == CAIRO_PS_LEVEL_2) { status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; } else { surface->ps_level_used = CAIRO_PS_LEVEL_3; status = CAIRO_STATUS_SUCCESS; } break; case CAIRO_IMAGE_HAS_ALPHA: status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; break; case CAIRO_IMAGE_UNKNOWN: ASSERT_NOT_REACHED; } _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); return status; } static cairo_bool_t surface_pattern_supported (const cairo_surface_pattern_t *pattern) { if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return TRUE; if (pattern->surface->backend->acquire_source_image == NULL) return FALSE; /* Does an ALPHA-only source surface even make sense? Maybe, but I * don't think it's worth the extra code to support it. */ /* XXX: Need to write this function here... content = pattern->surface->content; if (content == CAIRO_CONTENT_ALPHA) return FALSE; */ return TRUE; } static cairo_bool_t _gradient_pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) { double min_alpha, max_alpha; if (surface->ps_level == CAIRO_PS_LEVEL_2) return FALSE; /* Alpha gradients are only supported (by flattening the alpha) * if there is no variation in the alpha across the gradient. */ _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha); if (min_alpha != max_alpha) return FALSE; surface->ps_level_used = CAIRO_PS_LEVEL_3; return TRUE; } static cairo_bool_t pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return TRUE; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _gradient_pattern_supported (surface, pattern); case CAIRO_PATTERN_TYPE_SURFACE: return surface_pattern_supported ((cairo_surface_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return TRUE; default: ASSERT_NOT_REACHED; return FALSE; } } static cairo_bool_t mask_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { if (surface->ps_level == CAIRO_PS_LEVEL_2) return FALSE; if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { /* check if mask if opaque or bilevel alpha */ if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, mask, extents) == CAIRO_INT_STATUS_SUCCESS) { surface->ps_level_used = CAIRO_PS_LEVEL_3; return TRUE; } } } return FALSE; } static cairo_int_status_t _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { double min_alpha; if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! pattern_supported (surface, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Mask is only supported when the mask is an image with opaque or bilevel alpha. */ if (mask && !mask_supported (surface, mask, extents)) return CAIRO_INT_STATUS_UNSUPPORTED; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { if (pattern->extend == CAIRO_EXTEND_PAD) { cairo_box_t box; cairo_rectangle_int_t rect; cairo_rectangle_int_t rec_extents; /* get the operation extents in pattern space */ _cairo_box_from_rectangle (&box, extents); _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); _cairo_box_round_to_rectangle (&box, &rect); /* Check if surface needs padding to fill extents */ if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) { if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x || _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y || _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width || _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) { return CAIRO_INT_STATUS_UNSUPPORTED; } } } return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } if (op == CAIRO_OPERATOR_SOURCE) { if (mask) return CAIRO_INT_STATUS_UNSUPPORTED; else return CAIRO_STATUS_SUCCESS; } /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If * the pattern contains transparency, we return * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis * surface. If the analysis surface determines that there is * anything drawn under this operation, a fallback image will be * used. Otherwise the operation will be replayed during the * render stage and we blend the transparency into the white * background to convert the pattern to opaque. */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, pattern, extents); /* Patterns whose drawn part is opaque are directly supported; those whose drawn part is partially transparent can be supported by flattening the alpha. */ _cairo_pattern_alpha_range (pattern, &min_alpha, NULL); if (CAIRO_ALPHA_IS_OPAQUE (min_alpha)) return CAIRO_STATUS_SUCCESS; return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; } static cairo_bool_t _cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED; } /* The "standard" implementation limit for PostScript string sizes is * 65535 characters (see PostScript Language Reference, Appendix * B). We go one short of that because we sometimes need two * characters in a string to represent a single ASCII85 byte, (for the * escape sequences "\\", "\(", and "\)") and we must not split these * across two strings. So we'd be in trouble if we went right to the * limit and one of these escape sequences just happened to land at * the end. */ #define STRING_ARRAY_MAX_STRING_SIZE (65535-1) #define STRING_ARRAY_MAX_COLUMN 72 typedef struct _string_array_stream { cairo_output_stream_t base; cairo_output_stream_t *output; int column; int string_size; cairo_bool_t use_strings; } string_array_stream_t; static cairo_status_t _string_array_stream_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { string_array_stream_t *stream = (string_array_stream_t *) base; unsigned char c; const unsigned char backslash = '\\'; if (length == 0) return CAIRO_STATUS_SUCCESS; while (length--) { if (stream->string_size == 0 && stream->use_strings) { _cairo_output_stream_printf (stream->output, "("); stream->column++; } c = *data++; if (stream->use_strings) { switch (c) { case '\\': case '(': case ')': _cairo_output_stream_write (stream->output, &backslash, 1); stream->column++; stream->string_size++; break; } } /* Have to be careful to never split the final ~> sequence. */ if (c == '~') { _cairo_output_stream_write (stream->output, &c, 1); stream->column++; stream->string_size++; if (length-- == 0) break; c = *data++; } _cairo_output_stream_write (stream->output, &c, 1); stream->column++; stream->string_size++; if (stream->use_strings && stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) { _cairo_output_stream_printf (stream->output, ")\n"); stream->string_size = 0; stream->column = 0; } if (stream->column >= STRING_ARRAY_MAX_COLUMN) { _cairo_output_stream_printf (stream->output, "\n "); stream->string_size += 2; stream->column = 1; } } return _cairo_output_stream_get_status (stream->output); } static cairo_status_t _string_array_stream_close (cairo_output_stream_t *base) { cairo_status_t status; string_array_stream_t *stream = (string_array_stream_t *) base; if (stream->use_strings) _cairo_output_stream_printf (stream->output, ")\n"); status = _cairo_output_stream_get_status (stream->output); return status; } /* A string_array_stream wraps an existing output stream. It takes the * data provided to it and output one or more consecutive string * objects, each within the standard PostScript implementation limit * of 65k characters. * * The strings are each separated by a space character for easy * inclusion within an array object, (but the array delimiters are not * added by the string_array_stream). * * The string array stream is also careful to wrap the output within * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds * necessary escaping for special characters within a string, * (specifically '\', '(', and ')'). */ static cairo_output_stream_t * _string_array_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; stream = malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _string_array_stream_write, NULL, _string_array_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; stream->use_strings = TRUE; return &stream->base; } /* A base85_array_stream wraps an existing output stream. It wraps the * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output * is not enclosed in strings like string_array_stream. */ static cairo_output_stream_t * _base85_array_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; stream = malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, _string_array_stream_write, NULL, _string_array_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; stream->use_strings = FALSE; return &stream->base; } /* PS Output - this section handles output of the parts of the recording * surface we can render natively in PS. */ static cairo_status_t _cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, cairo_image_surface_t *image, cairo_image_surface_t **opaque_image) { cairo_surface_t *opaque; cairo_surface_pattern_t pattern; cairo_status_t status; opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, image->width, image->height); if (unlikely (opaque->status)) return opaque->status; if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_SOURCE, &_cairo_pattern_white.base, NULL); if (unlikely (status)) { cairo_surface_destroy (opaque); return status; } } _cairo_pattern_init_for_surface (&pattern, &image->base); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (opaque); return status; } *opaque_image = (cairo_image_surface_t *) opaque; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, const unsigned char *data, unsigned long length, cairo_ps_compress_t compress, cairo_bool_t use_strings) { cairo_output_stream_t *base85_stream, *string_array_stream, *deflate_stream; unsigned char *data_compressed; unsigned long data_compressed_size; cairo_status_t status, status2; if (use_strings) string_array_stream = _string_array_stream_create (surface->stream); else string_array_stream = _base85_array_stream_create (surface->stream); status = _cairo_output_stream_get_status (string_array_stream); if (unlikely (status)) return _cairo_output_stream_destroy (string_array_stream); base85_stream = _cairo_base85_stream_create (string_array_stream); status = _cairo_output_stream_get_status (base85_stream); if (unlikely (status)) { status2 = _cairo_output_stream_destroy (string_array_stream); return _cairo_output_stream_destroy (base85_stream); } switch (compress) { case CAIRO_PS_COMPRESS_NONE: _cairo_output_stream_write (base85_stream, data, length); break; case CAIRO_PS_COMPRESS_LZW: /* XXX: Should fix cairo-lzw to provide a stream-based interface * instead. */ data_compressed_size = length; data_compressed = _cairo_lzw_compress ((unsigned char*)data, &data_compressed_size); if (unlikely (data_compressed == NULL)) { status = _cairo_output_stream_destroy (string_array_stream); status = _cairo_output_stream_destroy (base85_stream); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } _cairo_output_stream_write (base85_stream, data_compressed, data_compressed_size); free (data_compressed); break; case CAIRO_PS_COMPRESS_DEFLATE: deflate_stream = _cairo_deflate_stream_create (base85_stream); if (_cairo_output_stream_get_status (deflate_stream)) { return _cairo_output_stream_destroy (deflate_stream); } _cairo_output_stream_write (deflate_stream, data, length); status = _cairo_output_stream_destroy (deflate_stream); if (unlikely (status)) { status2 = _cairo_output_stream_destroy (string_array_stream); status2 = _cairo_output_stream_destroy (base85_stream); return _cairo_output_stream_destroy (deflate_stream); } break; } status = _cairo_output_stream_destroy (base85_stream); /* Mark end of base85 data */ _cairo_output_stream_printf (string_array_stream, "~>"); status2 = _cairo_output_stream_destroy (string_array_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; return status; } static cairo_status_t _cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, cairo_image_surface_t *image_surf, cairo_operator_t op, cairo_filter_t filter, cairo_bool_t stencil_mask) { cairo_status_t status; unsigned char *data; unsigned long data_size; cairo_image_surface_t *ps_image; int x, y, i, a; cairo_image_transparency_t transparency; cairo_bool_t use_mask; uint32_t *pixel32; uint8_t *pixel8; int bit; cairo_image_color_t color; const char *interpolate; cairo_ps_compress_t compress; const char *compress_filter; cairo_image_surface_t *image; if (image_surf->base.status) return image_surf->base.status; image = image_surf; if (image->format != CAIRO_FORMAT_RGB24 && image->format != CAIRO_FORMAT_ARGB32 && image->format != CAIRO_FORMAT_A8 && image->format != CAIRO_FORMAT_A1) { cairo_surface_t *surf; cairo_surface_pattern_t pattern; surf = _cairo_image_surface_create_with_content (image_surf->base.content, image_surf->width, image_surf->height); image = (cairo_image_surface_t *) surf; if (surf->status) { status = surf->status; goto bail0; } _cairo_pattern_init_for_surface (&pattern, &image_surf->base); status = _cairo_surface_paint (surf, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto bail0; } ps_image = image; switch (filter) { default: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: interpolate = "true"; break; case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: interpolate = "false"; break; } if (stencil_mask) { use_mask = FALSE; color = CAIRO_IMAGE_IS_MONOCHROME; transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else { transparency = _cairo_image_analyze_transparency (image); /* PostScript can not represent the alpha channel, so we blend the current image over a white (or black for CONTENT_COLOR surfaces) RGB surface to eliminate it. */ if (op == CAIRO_OPERATOR_SOURCE || transparency == CAIRO_IMAGE_HAS_ALPHA || (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && surface->ps_level == CAIRO_PS_LEVEL_2)) { status = _cairo_ps_surface_flatten_image_transparency (surface, image, &ps_image); if (unlikely (status)) return status; use_mask = FALSE; } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) { use_mask = FALSE; } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */ use_mask = TRUE; } color = _cairo_image_analyze_color (ps_image); } /* Type 2 (mask and image interleaved) has the mask and image * samples interleaved by row. The mask row is first, one bit per * pixel with (bit 7 first). The row is padded to byte * boundaries. The image data is 3 bytes per pixel RGB format. */ switch (color) { default: case CAIRO_IMAGE_UNKNOWN_COLOR: ASSERT_NOT_REACHED; case CAIRO_IMAGE_IS_COLOR: data_size = ps_image->width * 3; break; case CAIRO_IMAGE_IS_GRAYSCALE: data_size = ps_image->width; break; case CAIRO_IMAGE_IS_MONOCHROME: data_size = (ps_image->width + 7)/8; break; } if (use_mask) data_size += (ps_image->width + 7)/8; data_size *= ps_image->height; data = malloc (data_size); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail1; } i = 0; for (y = 0; y < ps_image->height; y++) { if (stencil_mask || use_mask) { /* mask row */ if (ps_image->format == CAIRO_FORMAT_A1) { pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride); for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) { a = *pixel8; a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); data[i++] = a; } } else { pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride); pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride); bit = 7; for (x = 0; x < ps_image->width; x++) { if (ps_image->format == CAIRO_FORMAT_ARGB32) { a = (*pixel32 & 0xff000000) >> 24; pixel32++; } else { a = *pixel8; pixel8++; } if (transparency == CAIRO_IMAGE_HAS_ALPHA) { data[i++] = a; } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ if (bit == 7) data[i] = 0; if (a != 0) data[i] |= (1 << bit); bit--; if (bit < 0) { bit = 7; i++; } } } if (bit != 7) i++; } } if (stencil_mask) continue; /* image row*/ pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride); bit = 7; for (x = 0; x < ps_image->width; x++, pixel32++) { int r, g, b; if (ps_image->format == CAIRO_FORMAT_ARGB32) { /* At this point ARGB32 images are either opaque or * bilevel alpha so we don't need to unpremultiply. */ if (((*pixel32 & 0xff000000) >> 24) == 0) { r = g = b = 0; } else { r = (*pixel32 & 0x00ff0000) >> 16; g = (*pixel32 & 0x0000ff00) >> 8; b = (*pixel32 & 0x000000ff) >> 0; } } else if (ps_image->format == CAIRO_FORMAT_RGB24) { r = (*pixel32 & 0x00ff0000) >> 16; g = (*pixel32 & 0x0000ff00) >> 8; b = (*pixel32 & 0x000000ff) >> 0; } else { r = g = b = 0; } switch (color) { case CAIRO_IMAGE_IS_COLOR: case CAIRO_IMAGE_UNKNOWN_COLOR: data[i++] = r; data[i++] = g; data[i++] = b; break; case CAIRO_IMAGE_IS_GRAYSCALE: data[i++] = r; break; case CAIRO_IMAGE_IS_MONOCHROME: if (bit == 7) data[i] = 0; if (r != 0) data[i] |= (1 << bit); bit--; if (bit < 0) { bit = 7; i++; } break; } } if (bit != 7) i++; } if (surface->ps_level == CAIRO_PS_LEVEL_2) { compress = CAIRO_PS_COMPRESS_LZW; compress_filter = "LZWDecode"; } else { compress = CAIRO_PS_COMPRESS_DEFLATE; compress_filter = "FlateDecode"; surface->ps_level_used = CAIRO_PS_LEVEL_3; } if (surface->use_string_datasource) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, "/CairoImageData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, data, data_size, compress, TRUE); if (unlikely (status)) goto bail2; _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, "/CairoImageDataIndex 0 def\n"); } if (use_mask) { _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n" "5 dict dup begin\n" " /ImageType 3 def\n" " /InterleaveType 2 def\n" " /DataDict 8 dict def\n" " DataDict begin\n" " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" " /Interpolate %s def\n" " /BitsPerComponent %d def\n" " /Decode [ %s ] def\n", color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", ps_image->width, ps_image->height, interpolate, color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1"); if (surface->use_string_datasource) { _cairo_output_stream_printf (surface->stream, " /DataSource {\n" " CairoImageData CairoImageDataIndex get\n" " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" " CairoImageDataIndex CairoImageData length 1 sub gt\n" " { /CairoImageDataIndex 0 def } if\n" " } /ASCII85Decode filter /%s filter def\n", compress_filter); } else { _cairo_output_stream_printf (surface->stream, " /DataSource currentfile /ASCII85Decode filter /%s filter def\n", compress_filter); } _cairo_output_stream_printf (surface->stream, " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" " end\n" " /MaskDict 8 dict def\n" " MaskDict begin\n" " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" " /Interpolate %s def\n" " /BitsPerComponent 1 def\n" " /Decode [ 1 0 ] def\n" " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" " end\n" "end\n" "image\n", ps_image->height, ps_image->width, ps_image->height, interpolate, ps_image->height); } else { if (!stencil_mask) { _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n", color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray"); } _cairo_output_stream_printf (surface->stream, "8 dict dup begin\n" " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" " /Interpolate %s def\n" " /BitsPerComponent %d def\n" " /Decode [ %s ] def\n", ps_image->width, ps_image->height, interpolate, color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, stencil_mask ? "1 0" : color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1"); if (surface->use_string_datasource) { _cairo_output_stream_printf (surface->stream, " /DataSource {\n" " CairoImageData CairoImageDataIndex get\n" " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" " CairoImageDataIndex CairoImageData length 1 sub gt\n" " { /CairoImageDataIndex 0 def } if\n" " } /ASCII85Decode filter /%s filter def\n", compress_filter); } else { _cairo_output_stream_printf (surface->stream, " /DataSource currentfile /ASCII85Decode filter /%s filter def\n", compress_filter); } _cairo_output_stream_printf (surface->stream, " /Interpolate %s def\n" " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" "end\n" "%s\n", interpolate, ps_image->height, stencil_mask ? "imagemask" : "image"); } if (!surface->use_string_datasource) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, data, data_size, compress, FALSE); _cairo_output_stream_printf (surface->stream, "\n"); } else { status = CAIRO_STATUS_SUCCESS; } bail2: free (data); bail1: if (!use_mask && ps_image != image) cairo_surface_destroy (&ps_image->base); bail0: if (image != image_surf) cairo_surface_destroy (&image->base); return status; } static cairo_status_t _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, cairo_surface_t *source, int width, int height) { cairo_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; const char *colorspace; const char *decode; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (unlikely (source->status)) return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); if (unlikely (status)) return status; switch (info.num_components) { case 1: colorspace = "/DeviceGray"; decode = "0 1"; break; case 3: colorspace = "/DeviceRGB"; decode = "0 1 0 1 0 1"; break; case 4: colorspace = "/DeviceCMYK"; decode = "0 1 0 1 0 1 0 1"; break; default: return CAIRO_INT_STATUS_UNSUPPORTED; } if (surface->use_string_datasource) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, "/CairoImageData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, mime_data, mime_data_length, CAIRO_PS_COMPRESS_NONE, TRUE); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, "/CairoImageDataIndex 0 def\n"); } _cairo_output_stream_printf (surface->stream, "%s setcolorspace\n" "8 dict dup begin\n" " /ImageType 1 def\n" " /Width %d def\n" " /Height %d def\n" " /BitsPerComponent %d def\n" " /Decode [ %s ] def\n", colorspace, info.width, info.height, info.bits_per_component, decode); if (surface->use_string_datasource) { _cairo_output_stream_printf (surface->stream, " /DataSource {\n" " CairoImageData CairoImageDataIndex get\n" " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" " CairoImageDataIndex CairoImageData length 1 sub gt\n" " { /CairoImageDataIndex 0 def } if\n" " } /ASCII85Decode filter /DCTDecode filter def\n"); } else { _cairo_output_stream_printf (surface->stream, " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); } _cairo_output_stream_printf (surface->stream, " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" "end\n" "image\n", info.height); if (!surface->use_string_datasource) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, mime_data, mime_data_length, CAIRO_PS_COMPRESS_NONE, FALSE); } return status; } static cairo_status_t _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, cairo_surface_t *recording_surface) { double old_width, old_height; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; cairo_rectangle_int_t old_page_bbox; cairo_surface_t *free_me = NULL; cairo_surface_clipper_t old_clipper; cairo_box_t bbox; cairo_int_status_t status; old_content = surface->content; old_width = surface->width; old_height = surface->height; old_page_bbox = surface->page_bbox; old_cairo_to_ps = surface->cairo_to_ps; old_clipper = surface->clipper; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); if (_cairo_surface_is_snapshot (recording_surface)) free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, &bbox, NULL); if (unlikely (status)) goto err; #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", _cairo_fixed_to_double (bbox.p1.x), _cairo_fixed_to_double (bbox.p1.y), _cairo_fixed_to_double (bbox.p2.x), _cairo_fixed_to_double (bbox.p2.y)); #endif surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); _cairo_output_stream_printf (surface->stream, " q\n"); if (recording_surface->content == CAIRO_CONTENT_COLOR) { surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, " 0 g %d %d %d %d rectfill\n", surface->page_bbox.x, surface->page_bbox.y, surface->page_bbox.width, surface->page_bbox.height); } status = _cairo_recording_surface_replay_region (recording_surface, NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) goto err; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto err; _cairo_output_stream_printf (surface->stream, " Q\n"); _cairo_surface_clipper_reset (&surface->clipper); surface->clipper = old_clipper; surface->content = old_content; surface->width = old_width; surface->height = old_height; surface->page_bbox = old_page_bbox; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); err: cairo_surface_destroy (free_me); return status; } static cairo_int_status_t _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, cairo_surface_t *recording_surface, const cairo_rectangle_int_t *extents) { double old_width, old_height; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; cairo_rectangle_int_t old_page_bbox; cairo_surface_clipper_t old_clipper; cairo_surface_t *free_me = NULL; cairo_int_status_t status; old_content = surface->content; old_width = surface->width; old_height = surface->height; old_page_bbox = surface->page_bbox; old_cairo_to_ps = surface->cairo_to_ps; old_clipper = surface->clipper; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n", extents->x, extents->y, extents->width, extents->height); #endif surface->page_bbox.x = surface->page_bbox.y = 0; surface->page_bbox.width = surface->width = extents->width; surface->page_bbox.height = surface->height = extents->height; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); _cairo_output_stream_printf (surface->stream, " q\n"); if (_cairo_surface_is_snapshot (recording_surface)) free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); if (recording_surface->content == CAIRO_CONTENT_COLOR) { surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, " 0 g %d %d %d %d rectfill\n", surface->page_bbox.x, surface->page_bbox.y, surface->page_bbox.width, surface->page_bbox.height); } status = _cairo_recording_surface_replay_region (recording_surface, extents, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) goto err; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto err; _cairo_output_stream_printf (surface->stream, " Q\n"); _cairo_surface_clipper_reset (&surface->clipper); surface->clipper = old_clipper; surface->content = old_content; surface->width = old_width; surface->height = old_height; surface->page_bbox = old_page_bbox; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); err: cairo_surface_destroy (free_me); return status; } static void _cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface, const cairo_color_t *color, double *red, double *green, double *blue) { *red = color->red; *green = color->green; *blue = color->blue; if (! CAIRO_COLOR_IS_OPAQUE (color)) { *red *= color->alpha; *green *= color->alpha; *blue *= color->alpha; if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { double one_minus_alpha = 1. - color->alpha; *red += one_minus_alpha; *green += one_minus_alpha; *blue += one_minus_alpha; } } } static void _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, cairo_solid_pattern_t *pattern) { double red, green, blue; _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue); if (color_is_gray (red, green, blue)) _cairo_output_stream_printf (surface->stream, "%f g\n", red); else _cairo_output_stream_printf (surface->stream, "%f %f %f rg\n", red, green, blue); } static cairo_status_t _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, cairo_pattern_t *source_pattern, cairo_surface_t *source_surface, cairo_operator_t op, int width, int height, cairo_bool_t stencil_mask) { cairo_int_status_t status; if (source_surface->type == CAIRO_SURFACE_TYPE_RECORDING) { if (source_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source_surface; status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents); } else { status = _cairo_ps_surface_emit_recording_surface (surface, source_surface); } } else { cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface; if (source_pattern->extend != CAIRO_EXTEND_PAD) { status = _cairo_ps_surface_emit_jpeg_image (surface, source_surface, width, height); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } status = _cairo_ps_surface_emit_image (surface, image, op, source_pattern->filter, stencil_mask); } return status; } static void _path_fixed_init_rectangle (cairo_path_fixed_t *path, cairo_rectangle_int_t *rect) { cairo_status_t status; _cairo_path_fixed_init (path); status = _cairo_path_fixed_move_to (path, _cairo_fixed_from_int (rect->x), _cairo_fixed_from_int (rect->y)); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (rect->width), _cairo_fixed_from_int (0)); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (0), _cairo_fixed_from_int (rect->height)); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (-rect->width), _cairo_fixed_from_int (0)); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_path_fixed_close_path (path); assert (status == CAIRO_STATUS_SUCCESS); } static cairo_status_t _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op, cairo_bool_t stencil_mask) { cairo_status_t status; int width, height; cairo_matrix_t cairo_p2d, ps_p2d; cairo_path_fixed_t path; double x_offset, y_offset; cairo_surface_t *source; cairo_image_surface_t *image = NULL; void *image_extra; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, &width, &height, &x_offset, &y_offset, &source, &image_extra); if (unlikely (status)) return status; if (pattern->extend == CAIRO_EXTEND_PAD && pattern->type == CAIRO_PATTERN_TYPE_SURFACE && ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { cairo_image_surface_t *img; img = (cairo_image_surface_t *) source; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, &width, &height, &x_offset, &y_offset, &image); if (unlikely (status)) goto release_source; } _path_fixed_init_rectangle (&path, extents); status = _cairo_pdf_operators_clip (&surface->pdf_operators, &path, CAIRO_FILL_RULE_WINDING); _cairo_path_fixed_fini (&path); if (unlikely (status)) goto release_source; cairo_p2d = pattern->matrix; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { double x_scale = cairo_p2d.xx; double y_scale = cairo_p2d.yy; _cairo_output_stream_printf (surface->stream, "%% Fallback Image: x=%f y=%f w=%d h=%d ", -cairo_p2d.x0/x_scale, -cairo_p2d.y0/y_scale, (int)(width/x_scale), (int)(height/y_scale)); if (x_scale == y_scale) { _cairo_output_stream_printf (surface->stream, "res=%fppi ", x_scale*72); } else { _cairo_output_stream_printf (surface->stream, "res=%fx%fppi ", x_scale*72, y_scale*72); } _cairo_output_stream_printf (surface->stream, "size=%ld\n", (long)width*height*3); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, "%d g 0 0 %f %f rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, surface->width, surface->height); } } status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); cairo_matrix_translate (&ps_p2d, x_offset, y_offset); cairo_matrix_translate (&ps_p2d, 0.0, height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); if (! _cairo_matrix_is_identity (&ps_p2d)) { _cairo_output_stream_printf (surface->stream, "[ %f %f %f %f %f %f ] concat\n", ps_p2d.xx, ps_p2d.yx, ps_p2d.xy, ps_p2d.yy, ps_p2d.x0, ps_p2d.y0); } status = _cairo_ps_surface_emit_surface (surface, pattern, image ? &image->base : source, op, width, height, stencil_mask); release_source: if (image) cairo_surface_destroy (&image->base); _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); return status; } static cairo_status_t _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op) { cairo_status_t status; int pattern_width = 0; /* squelch bogus compiler warning */ int pattern_height = 0; /* squelch bogus compiler warning */ double xstep, ystep; cairo_matrix_t cairo_p2d, ps_p2d; cairo_bool_t old_use_string_datasource; double x_offset, y_offset; cairo_surface_t *source; cairo_image_surface_t *image = NULL; void *image_extra; cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, pattern, extents, &pattern_width, &pattern_height, &x_offset, &y_offset, &source, &image_extra); if (unlikely (status)) return status; if (pattern->extend == CAIRO_EXTEND_PAD) { cairo_image_surface_t *img; assert (source->type == CAIRO_SURFACE_TYPE_IMAGE); img = (cairo_image_surface_t *) source; status = _cairo_ps_surface_create_padded_image_from_image (surface, img, &pattern->matrix, extents, &pattern_width, &pattern_height, &x_offset, &y_offset, &image); if (unlikely (status)) goto release_source; } if (unlikely (status)) goto release_source; switch (pattern->extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { /* In PS/PDF, (as far as I can tell), all patterns are * repeating. So we support cairo's EXTEND_NONE semantics * by setting the repeat step size to a size large enough * to guarantee that no more than a single occurrence will * be visible. * * First, map the surface extents into pattern space (since * xstep and ystep are in pattern space). Then use an upper * bound on the length of the diagonal of the pattern image * and the surface as repeat size. This guarantees to never * repeat visibly. */ double x1 = 0.0, y1 = 0.0; double x2 = surface->width, y2 = surface->height; _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); /* Rather than computing precise bounds of the union, just * add the surface extents unconditionally. We only * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + pattern_width + pattern_height); break; } case CAIRO_EXTEND_REPEAT: xstep = pattern_width; ystep = pattern_height; break; case CAIRO_EXTEND_REFLECT: xstep = pattern_width*2; ystep = pattern_height*2; break; /* All the rest (if any) should have been analyzed away, so these * cases should be unreachable. */ default: ASSERT_NOT_REACHED; xstep = 0; ystep = 0; } _cairo_output_stream_printf (surface->stream, "/CairoPattern {\n"); old_use_string_datasource = surface->use_string_datasource; surface->use_string_datasource = TRUE; if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, "%d g 0 0 %f %f rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, xstep, ystep); } status = _cairo_ps_surface_emit_surface (surface, pattern, image ? &image->base : source, op, pattern_width, pattern_height, FALSE); if (unlikely (status)) goto release_source; surface->use_string_datasource = old_use_string_datasource; _cairo_output_stream_printf (surface->stream, "} bind def\n"); _cairo_output_stream_printf (surface->stream, "<< /PatternType 1\n" " /PaintType 1\n" " /TilingType 1\n"); _cairo_output_stream_printf (surface->stream, " /XStep %f /YStep %f\n", xstep, ystep); if (pattern->extend == CAIRO_EXTEND_REFLECT) { _cairo_output_stream_printf (surface->stream, " /BBox [0 0 %d %d]\n" " /PaintProc {\n" " CairoPattern\n" " [-1 0 0 1 %d 0] concat CairoPattern\n" " [ 1 0 0 -1 0 %d] concat CairoPattern\n" " [-1 0 0 1 %d 0] concat CairoPattern\n" " CairoPattern\n" " } bind\n", pattern_width*2, pattern_height*2, pattern_width*2, pattern_height*2, pattern_width*2); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, " /BBox [0 0 %f %f]\n", xstep, ystep); } else { _cairo_output_stream_printf (surface->stream, " /BBox [0 0 %d %d]\n", pattern_width, pattern_height); } _cairo_output_stream_printf (surface->stream, " /PaintProc { CairoPattern }\n"); } _cairo_output_stream_printf (surface->stream, ">>\n"); cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_init_identity (&ps_p2d); cairo_matrix_translate (&ps_p2d, 0.0, surface->height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); cairo_matrix_scale (&ps_p2d, 1.0, -1.0); _cairo_output_stream_printf (surface->stream, "[ %f %f %f %f %f %f ]\n", ps_p2d.xx, ps_p2d.yx, ps_p2d.xy, ps_p2d.yy, ps_p2d.x0, ps_p2d.y0); _cairo_output_stream_printf (surface->stream, "makepattern setpattern\n"); release_source: if (image) cairo_surface_destroy (&image->base); _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra); return status; } typedef struct _cairo_ps_color_stop { double offset; double color[4]; } cairo_ps_color_stop_t; static void _cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface, cairo_ps_color_stop_t *stop1, cairo_ps_color_stop_t *stop2) { _cairo_output_stream_printf (surface->stream, " << /FunctionType 2\n" " /Domain [ 0 1 ]\n" " /C0 [ %f %f %f ]\n" " /C1 [ %f %f %f ]\n" " /N 1\n" " >>\n", stop1->color[0], stop1->color[1], stop1->color[2], stop2->color[0], stop2->color[1], stop2->color[2]); } static void _cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface, unsigned int n_stops, cairo_ps_color_stop_t stops[]) { unsigned int i; _cairo_output_stream_printf (surface->stream, "<< /FunctionType 3\n" " /Domain [ 0 1 ]\n" " /Functions [\n"); for (i = 0; i < n_stops - 1; i++) _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]); _cairo_output_stream_printf (surface->stream, " ]\n"); _cairo_output_stream_printf (surface->stream, " /Bounds [ "); for (i = 1; i < n_stops-1; i++) _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset); _cairo_output_stream_printf (surface->stream, "]\n"); _cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n", n_stops - 1); _cairo_output_stream_printf (surface->stream, ">>\n"); } static void calc_gradient_color (cairo_ps_color_stop_t *new_stop, cairo_ps_color_stop_t *stop1, cairo_ps_color_stop_t *stop2) { int i; double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); for (i = 0; i < 4; i++) new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); } #define COLOR_STOP_EPSILON 1e-6 static cairo_status_t _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, cairo_gradient_pattern_t *pattern) { cairo_ps_color_stop_t *allstops, *stops; unsigned int i, n_stops; allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t)); if (unlikely (allstops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); stops = &allstops[1]; n_stops = pattern->n_stops; for (i = 0; i < n_stops; i++) { cairo_gradient_stop_t *stop = &pattern->stops[i]; stops[i].color[0] = stop->color.red; stops[i].color[1] = stop->color.green; stops[i].color[2] = stop->color.blue; stops[i].color[3] = stop->color.alpha; stops[i].offset = pattern->stops[i].offset; } if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { if (stops[0].offset > COLOR_STOP_EPSILON) { if (pattern->base.extend == CAIRO_EXTEND_REFLECT) memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t)); else calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); stops = allstops; n_stops++; } stops[0].offset = 0.0; if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { memcpy (&stops[n_stops], &stops[n_stops - 1], sizeof (cairo_ps_color_stop_t)); } else { calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); } n_stops++; } stops[n_stops-1].offset = 1.0; } for (i = 0; i < n_stops; i++) { double red, green, blue; cairo_color_t color; _cairo_color_init_rgba (&color, stops[i].color[0], stops[i].color[1], stops[i].color[2], stops[i].color[3]); _cairo_ps_surface_flatten_transparency (surface, &color, &red, &green, &blue); stops[i].color[0] = red; stops[i].color[1] = green; stops[i].color[2] = blue; } _cairo_output_stream_printf (surface->stream, "/CairoFunction\n"); if (stops[0].offset == stops[n_stops - 1].offset) { /* * The first and the last stops have the same offset, but we * don't want a function with an empty domain, because that * would provoke underdefined behaviour from rasterisers. * This can only happen with EXTEND_PAD, because EXTEND_NONE * is optimised into a clear pattern in cairo-gstate, and * REFLECT/REPEAT are always transformed to have the first * stop at t=0 and the last stop at t=1. Thus we want a step * function going from the first color to the last one. * * This can be accomplished by stitching three functions: * - a constant first color function, * - a step from the first color to the last color (with empty domain) * - a constant last color function */ cairo_ps_color_stop_t pad_stops[4]; assert (pattern->base.extend == CAIRO_EXTEND_PAD); pad_stops[0] = pad_stops[1] = stops[0]; pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; pad_stops[0].offset = 0; pad_stops[3].offset = 1; _cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops); } else if (n_stops == 2) { /* no need for stitched function */ _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]); } else { /* multiple stops: stitch. XXX possible optimization: regulary spaced * stops do not require stitching. XXX */ _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops); } _cairo_output_stream_printf (surface->stream, "def\n"); free (allstops); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface, cairo_gradient_pattern_t *pattern, int begin, int end) { _cairo_output_stream_printf (surface->stream, "/CairoFunction\n" "<< /FunctionType 3\n" " /Domain [ %d %d ]\n" " /Functions [ %d {CairoFunction} repeat ]\n" " /Bounds [ %d 1 %d {} for ]\n", begin, end, end - begin, begin + 1, end - 1); if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n", begin, end - 1); } else { _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n", begin, end - 1); } _cairo_output_stream_printf (surface->stream, ">> def\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_bool_t is_ps_pattern) { cairo_matrix_t pat_to_ps; cairo_circle_double_t start, end; double domain[2]; cairo_status_t status; assert (pattern->n_stops != 0); status = _cairo_ps_surface_emit_pattern_stops (surface, pattern); if (unlikely (status)) return status; pat_to_ps = pattern->base.matrix; status = cairo_matrix_invert (&pat_to_ps); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { double bounds_x1, bounds_x2, bounds_y1, bounds_y2; double x_scale, y_scale, tolerance; /* TODO: use tighter extents */ bounds_x1 = 0; bounds_y1 = 0; bounds_x2 = surface->width; bounds_y2 = surface->height; _cairo_matrix_transform_bounding_box (&pattern->base.matrix, &bounds_x1, &bounds_y1, &bounds_x2, &bounds_y2, NULL); x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); tolerance *= MIN (x_scale, y_scale); _cairo_gradient_pattern_box_to_parameter (pattern, bounds_x1, bounds_y1, bounds_x2, bounds_y2, tolerance, domain); } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { /* * If the first and the last stop offset are the same, then * the color function is a step function. * _cairo_ps_surface_emit_pattern_stops emits it as a stitched * function no matter how many stops the pattern has. The * domain of the stitched function will be [0 1] in this case. * * This is done to avoid emitting degenerate gradients for * EXTEND_PAD patterns having a step color function. */ domain[0] = 0.0; domain[1] = 1.0; assert (pattern->base.extend == CAIRO_EXTEND_PAD); } else { domain[0] = pattern->stops[0].offset; domain[1] = pattern->stops[pattern->n_stops - 1].offset; } /* PS requires the first and last stop to be the same as the * extreme coordinates. For repeating patterns this moves the * extreme coordinates out to the begin/end of the repeating * function. For non repeating patterns this may move the extreme * coordinates in if there are not stops at offset 0 and 1. */ _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); if (pattern->base.extend == CAIRO_EXTEND_REPEAT || pattern->base.extend == CAIRO_EXTEND_REFLECT) { int repeat_begin, repeat_end; repeat_begin = floor (domain[0]); repeat_end = ceil (domain[1]); status = _cairo_ps_surface_emit_repeating_function (surface, pattern, repeat_begin, repeat_end); if (unlikely (status)) return status; } else if (pattern->n_stops <= 2) { /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a * Type 2 function is used by itself without a stitching * function. Type 2 functions always have the domain [0 1] */ domain[0] = 0.0; domain[1] = 1.0; } if (is_ps_pattern) { _cairo_output_stream_printf (surface->stream, "<< /PatternType 2\n" " /Shading\n"); } if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { _cairo_output_stream_printf (surface->stream, " << /ShadingType 2\n" " /ColorSpace /DeviceRGB\n" " /Coords [ %f %f %f %f ]\n", start.center.x, start.center.y, end.center.x, end.center.y); } else { _cairo_output_stream_printf (surface->stream, " << /ShadingType 3\n" " /ColorSpace /DeviceRGB\n" " /Coords [ %f %f %f %f %f %f ]\n", start.center.x, start.center.y, MAX (start.radius, 0), end.center.x, end.center.y, MAX (end.radius, 0)); } if (pattern->base.extend != CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (surface->stream, " /Extend [ true true ]\n"); } else { _cairo_output_stream_printf (surface->stream, " /Extend [ false false ]\n"); } if (domain[0] == 0.0 && domain[1] == 1.0) { _cairo_output_stream_printf (surface->stream, " /Function CairoFunction\n"); } else { _cairo_output_stream_printf (surface->stream, " /Function <<\n" " /FunctionType 3\n" " /Domain [ 0 1 ]\n" " /Bounds [ ]\n" " /Encode [ %f %f ]\n" " /Functions [ CairoFunction ]\n" " >>\n", domain[0], domain[1]); } _cairo_output_stream_printf (surface->stream, " >>\n"); if (is_ps_pattern) { _cairo_output_stream_printf (surface->stream, ">>\n" "[ %f %f %f %f %f %f ]\n" "makepattern setpattern\n", pat_to_ps.xx, pat_to_ps.yx, pat_to_ps.xy, pat_to_ps.yy, pat_to_ps.x0, pat_to_ps.y0); } else { _cairo_output_stream_printf (surface->stream, "shfill\n"); } return status; } static cairo_status_t _cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t *surface, cairo_mesh_pattern_t *pattern, cairo_bool_t is_ps_pattern) { cairo_matrix_t pat_to_ps; cairo_status_t status; cairo_pdf_shading_t shading; int i; if (_cairo_array_num_elements (&pattern->patches) == 0) return CAIRO_INT_STATUS_NOTHING_TO_DO; pat_to_ps = pattern->base.matrix; status = cairo_matrix_invert (&pat_to_ps); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); status = _cairo_pdf_shading_init_color (&shading, pattern); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "currentfile\n" "/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n"); status = _cairo_ps_surface_emit_base85_string (surface, shading.data, shading.data_length, CAIRO_PS_COMPRESS_DEFLATE, FALSE); if (status) return status; _cairo_output_stream_printf (surface->stream, "\n" "/CairoData exch def\n"); if (is_ps_pattern) { _cairo_output_stream_printf (surface->stream, "<< /PatternType 2\n" " /Shading\n"); } _cairo_output_stream_printf (surface->stream, " << /ShadingType %d\n" " /ColorSpace /DeviceRGB\n" " /DataSource CairoData\n" " /BitsPerCoordinate %d\n" " /BitsPerComponent %d\n" " /BitsPerFlag %d\n" " /Decode [", shading.shading_type, shading.bits_per_coordinate, shading.bits_per_component, shading.bits_per_flag); for (i = 0; i < shading.decode_array_length; i++) _cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]); _cairo_output_stream_printf (surface->stream, "]\n" " >>\n"); if (is_ps_pattern) { _cairo_output_stream_printf (surface->stream, ">>\n" "[ %f %f %f %f %f %f ]\n", pat_to_ps.xx, pat_to_ps.yx, pat_to_ps.xy, pat_to_ps.yy, pat_to_ps.x0, pat_to_ps.y0); _cairo_output_stream_printf (surface->stream, "makepattern\n" "setpattern\n"); } else { _cairo_output_stream_printf (surface->stream, "shfill\n"); } _cairo_output_stream_printf (surface->stream, "currentdict /CairoData undef\n"); _cairo_pdf_shading_fini (&shading); return status; } static cairo_status_t _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op) { cairo_status_t status; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; if (surface->current_pattern_is_solid_color == FALSE || ! _cairo_color_equal (&surface->current_color, &solid->color)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); surface->current_pattern_is_solid_color = TRUE; surface->current_color = solid->color; } return CAIRO_STATUS_SUCCESS; } surface->current_pattern_is_solid_color = FALSE; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: status = _cairo_ps_surface_emit_surface_pattern (surface, (cairo_pattern_t *)pattern, extents, op); if (unlikely (status)) return status; break; case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_ps_surface_emit_gradient (surface, (cairo_gradient_pattern_t *) pattern, TRUE); if (unlikely (status)) return status; break; case CAIRO_PATTERN_TYPE_MESH: status = _cairo_ps_surface_emit_mesh_pattern (surface, (cairo_mesh_pattern_t *) pattern, TRUE); if (unlikely (status)) return status; break; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ps_surface_paint_gradient (cairo_ps_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents) { cairo_matrix_t pat_to_ps; cairo_status_t status; pat_to_ps = source->matrix; status = cairo_matrix_invert (&pat_to_ps); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); if (! _cairo_matrix_is_identity (&pat_to_ps)) { _cairo_output_stream_printf (surface->stream, "[%f %f %f %f %f %f] concat\n", pat_to_ps.xx, pat_to_ps.yx, pat_to_ps.xy, pat_to_ps.yy, pat_to_ps.x0, pat_to_ps.y0); } if (source->type == CAIRO_PATTERN_TYPE_MESH) { status = _cairo_ps_surface_emit_mesh_pattern (surface, (cairo_mesh_pattern_t *)source, FALSE); if (unlikely (status)) return status; } else { status = _cairo_ps_surface_emit_gradient (surface, (cairo_gradient_pattern_t *)source, FALSE); if (unlikely (status)) return status; } return status; } static cairo_status_t _cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, const cairo_pattern_t *source, cairo_rectangle_int_t *extents, cairo_operator_t op, cairo_bool_t stencil_mask) { switch (source->type) { case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _cairo_ps_surface_paint_surface (surface, (cairo_pattern_t *)source, extents, op, stencil_mask); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_ps_surface_paint_gradient (surface, source, extents); case CAIRO_PATTERN_TYPE_SOLID: default: ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; } } static cairo_bool_t _can_paint_pattern (const cairo_pattern_t *pattern) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return FALSE; case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return (pattern->extend == CAIRO_EXTEND_NONE || pattern->extend == CAIRO_EXTEND_PAD); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return TRUE; default: ASSERT_NOT_REACHED; return FALSE; } } static cairo_bool_t _cairo_ps_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_ps_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; /* XXX: The conversion to integers here is pretty bogus, (not to * mention the aribitray limitation of width to a short(!). We * may need to come up with a better interface for get_extents. */ rectangle->width = ceil (surface->width); rectangle->height = ceil (surface->height); return TRUE; } static void _cairo_ps_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static cairo_int_status_t _cairo_ps_surface_set_clip (cairo_ps_surface_t *surface, cairo_composite_rectangles_t *composite) { cairo_clip_t *clip = composite->clip; if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) clip = NULL; if (clip == NULL) { if (_cairo_composite_rectangles_can_reduce_clip (composite, surface->clipper.clip)) return CAIRO_STATUS_SUCCESS; } return _cairo_surface_clipper_set_clip (&surface->clipper, clip); } static cairo_int_status_t _cairo_ps_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; cairo_composite_rectangles_t extents; cairo_status_t status; status = _cairo_composite_rectangles_init_for_paint (&extents, &surface->base, op, source, clip); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); goto cleanup_composite; } assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_paint\n"); #endif status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup_composite; if (_can_paint_pattern (source)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (stream, "q\n"); status = _cairo_ps_surface_paint_pattern (surface, source, &extents.bounded, op, FALSE); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (stream, "Q\n"); } else { status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n", surface->width, surface->height); } cleanup_composite: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_ps_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; cairo_composite_rectangles_t extents; cairo_status_t status; status = _cairo_composite_rectangles_init_for_mask (&extents, &surface->base, op, source, mask, clip); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded); goto cleanup_composite; } assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_mask\n"); #endif status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (stream, "q\n"); status = _cairo_ps_surface_paint_pattern (surface, mask, &extents.bounded, op, TRUE); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (stream, "Q\n"); cleanup_composite: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_ps_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_stroke (&extents, &surface->base, op, source, path, style, ctm, clip); if (unlikely (status)) return status; /* use the more accurate extents */ { cairo_rectangle_int_t r; cairo_box_t b; status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, &r); if (unlikely (status)) goto cleanup_composite; _cairo_box_from_rectangle (&b, &r); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); if (unlikely (status)) goto cleanup_composite; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); goto cleanup_composite; } assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_stroke\n"); #endif status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) goto cleanup_composite; status = _cairo_pdf_operators_stroke (&surface->pdf_operators, path, style, ctm, ctm_inverse); cleanup_composite: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_int_status_t _cairo_ps_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_fill (&extents, &surface->base, op, source, path, clip); if (unlikely (status)) return status; /* use the more accurate extents */ { cairo_rectangle_int_t r; cairo_box_t b; _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, &r); _cairo_box_from_rectangle (&b, &r); status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); if (unlikely (status)) goto cleanup_composite; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); goto cleanup_composite; } assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_fill\n"); #endif status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup_composite; status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup_composite; if (_can_paint_pattern (source)) { _cairo_output_stream_printf (surface->stream, "q\n"); status = _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) goto cleanup_composite; status = _cairo_ps_surface_paint_pattern (surface, source, &extents.bounded, op, FALSE); if (unlikely (status)) goto cleanup_composite; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); } else { status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) goto cleanup_composite; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); } cleanup_composite: _cairo_composite_rectangles_fini (&extents); return status; } static cairo_bool_t _cairo_ps_surface_has_show_text_glyphs (void *abstract_surface) { return TRUE; } static cairo_int_status_t _cairo_ps_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_bool_t overlap; cairo_status_t status; status = _cairo_composite_rectangles_init_for_glyphs (&extents, &surface->base, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); goto cleanup_composite; } assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_show_glyphs\n"); #endif status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) goto cleanup_composite; status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font); cleanup_composite: _cairo_composite_rectangles_fini (&extents); return status; } static const char ** _cairo_ps_surface_get_supported_mime_types (void *abstract_surface) { return _cairo_ps_supported_mime_types; } static void _cairo_ps_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_ps_surface_t *surface = abstract_surface; cairo_status_t status; surface->paginated_mode = paginated_mode; if (surface->clipper.clip != NULL) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); _cairo_output_stream_printf (surface->stream, "Q q\n"); _cairo_surface_clipper_reset (&surface->clipper); } } static cairo_int_status_t _cairo_ps_surface_set_bounding_box (void *abstract_surface, cairo_box_t *bbox) { cairo_ps_surface_t *surface = abstract_surface; int i, num_comments; char **comments; int x1, y1, x2, y2; cairo_bool_t has_page_media, has_page_bbox; const char *page_media; x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); surface->page_bbox.x = x1; surface->page_bbox.y = y1; surface->page_bbox.width = x2 - x1; surface->page_bbox.height = y2 - y1; _cairo_output_stream_printf (surface->stream, "%%%%Page: %d %d\n", surface->num_pages, surface->num_pages); _cairo_output_stream_printf (surface->stream, "%%%%BeginPageSetup\n"); has_page_media = FALSE; has_page_bbox = FALSE; num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->stream, "%s\n", comments[i]); if (strncmp (comments[i], "%%PageMedia:", 11) == 0) has_page_media = TRUE; if (strncmp (comments[i], "%%PageBoundingBox:", 18) == 0) has_page_bbox = TRUE; free (comments[i]); comments[i] = NULL; } _cairo_array_truncate (&surface->dsc_page_setup_comments, 0); if (!has_page_media && !surface->eps) { page_media = _cairo_ps_surface_get_page_media (surface); if (unlikely (page_media == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_output_stream_printf (surface->stream, "%%%%PageMedia: %s\n", page_media); } if (!has_page_bbox) { _cairo_output_stream_printf (surface->stream, "%%%%PageBoundingBox: %d %d %d %d\n", x1, y1, x2, y2); } _cairo_output_stream_printf (surface->stream, "%%%%EndPageSetup\n" "q %d %d %d %d rectclip q\n", surface->page_bbox.x, surface->page_bbox.y, surface->page_bbox.width, surface->page_bbox.height); if (surface->num_pages == 1) { surface->bbox_x1 = x1; surface->bbox_y1 = y1; surface->bbox_x2 = x2; surface->bbox_y2 = y2; } else { if (x1 < surface->bbox_x1) surface->bbox_x1 = x1; if (y1 < surface->bbox_y1) surface->bbox_y1 = y1; if (x2 > surface->bbox_x2) surface->bbox_x2 = x2; if (y2 > surface->bbox_y2) surface->bbox_y2 = y2; } surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); return _cairo_output_stream_get_status (surface->stream); } static cairo_bool_t _cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) { return TRUE; } static const cairo_surface_backend_t cairo_ps_surface_backend = { CAIRO_SURFACE_TYPE_PS, _cairo_ps_surface_finish, _cairo_default_context_create, NULL, /* create similar: handled by wrapper */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* cairo_ps_surface_copy_page */ _cairo_ps_surface_show_page, _cairo_ps_surface_get_extents, _cairo_ps_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ /* Here are the drawing functions */ _cairo_ps_surface_paint, /* paint */ _cairo_ps_surface_mask, _cairo_ps_surface_stroke, _cairo_ps_surface_fill, NULL, /* fill-stroke */ NULL, /* show_glyphs */ _cairo_ps_surface_has_show_text_glyphs, _cairo_ps_surface_show_text_glyphs, _cairo_ps_surface_get_supported_mime_types, }; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = { _cairo_ps_surface_start_page, _cairo_ps_surface_set_paginated_mode, _cairo_ps_surface_set_bounding_box, NULL, /* _cairo_ps_surface_has_fallback_images, */ _cairo_ps_surface_supports_fine_grained_fallbacks, }; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-ps.h��������������������������������������0000664�0000000�0000000�00000007060�12710376503�0024171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_PS_H #define CAIRO_PS_H #include "cairo.h" #if CAIRO_HAS_PS_SURFACE #include <stdio.h> CAIRO_BEGIN_DECLS /* PS-surface functions */ /** * cairo_ps_level_t: * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. (Since 1.6) * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. (Since 1.6) * * #cairo_ps_level_t is used to describe the language level of the * PostScript Language Reference that a generated PostScript file will * conform to. * * Since: 1.6 **/ typedef enum _cairo_ps_level { CAIRO_PS_LEVEL_2, CAIRO_PS_LEVEL_3 } cairo_ps_level_t; cairo_public cairo_surface_t * cairo_ps_surface_create (const char *filename, double width_in_points, double height_in_points); cairo_public cairo_surface_t * cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points); cairo_public void cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, cairo_ps_level_t level); cairo_public void cairo_ps_get_levels (cairo_ps_level_t const **levels, int *num_levels); cairo_public const char * cairo_ps_level_to_string (cairo_ps_level_t level); cairo_public void cairo_ps_surface_set_eps (cairo_surface_t *surface, cairo_bool_t eps); cairo_public cairo_bool_t cairo_ps_surface_get_eps (cairo_surface_t *surface); cairo_public void cairo_ps_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points); cairo_public void cairo_ps_surface_dsc_comment (cairo_surface_t *surface, const char *comment); cairo_public void cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface); cairo_public void cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface); CAIRO_END_DECLS #else /* CAIRO_HAS_PS_SURFACE */ # error Cairo was not compiled with support for the ps backend #endif /* CAIRO_HAS_PS_SURFACE */ #endif /* CAIRO_PS_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-qt-surface.cpp����������������������������0000664�0000000�0000000�00000141533�12710376503�0026160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ /* Get INT16_MIN etc. as per C99 */ #define __STDC_LIMIT_MACROS #include "cairoint.h" #include "cairo-clip-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-region-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-types-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-fallback-private.h" #include "cairo-ft.h" #include "cairo-qt.h" #include <memory> #include <QtGui/QPainter> #include <QtGui/QPaintEngine> #include <QtGui/QPaintDevice> #include <QtGui/QImage> #include <QtGui/QPixmap> #include <QtGui/QBrush> #include <QtGui/QPen> #include <QWidget> #include <QtCore/QVarLengthArray> #if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0 extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count); #endif #include <sys/time.h> /* Enable workaround slow regional Qt paths */ #define ENABLE_FAST_FILL 0 #define ENABLE_FAST_CLIP 0 #if 0 #define D(x) x static const char * _opstr (cairo_operator_t op) { const char *ops[] = { "CLEAR", "SOURCE", "OVER", "IN", "OUT", "ATOP", "DEST", "DEST_OVER", "DEST_IN", "DEST_OUT", "DEST_ATOP", "XOR", "ADD", "SATURATE" }; if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE) return "(\?\?\?)"; return ops[op]; } #else #define D(x) do { } while(0) #endif #ifndef CAIRO_INT_STATUS_SUCCESS #define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS) #endif /* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */ #define DOT_LENGTH 1.0 #define DASH_LENGTH 3.0 struct cairo_qt_surface_t { cairo_surface_t base; cairo_bool_t supports_porter_duff; QPainter *p; /* The pixmap/image constructors will store their objects here */ QPixmap *pixmap; QImage *image; QRect window; cairo_surface_clipper_t clipper; cairo_surface_t *image_equiv; }; /* Will be true if we ever try to create a QPixmap and end * up with one without an alpha channel. */ static cairo_bool_t _qpixmaps_have_no_alpha = FALSE; /* * Helper methods */ static QPainter::CompositionMode _qpainter_compositionmode_from_cairo_op (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: return QPainter::CompositionMode_Clear; case CAIRO_OPERATOR_SOURCE: return QPainter::CompositionMode_Source; case CAIRO_OPERATOR_OVER: return QPainter::CompositionMode_SourceOver; case CAIRO_OPERATOR_IN: return QPainter::CompositionMode_SourceIn; case CAIRO_OPERATOR_OUT: return QPainter::CompositionMode_SourceOut; case CAIRO_OPERATOR_ATOP: return QPainter::CompositionMode_SourceAtop; case CAIRO_OPERATOR_DEST: return QPainter::CompositionMode_Destination; case CAIRO_OPERATOR_DEST_OVER: return QPainter::CompositionMode_DestinationOver; case CAIRO_OPERATOR_DEST_IN: return QPainter::CompositionMode_DestinationIn; case CAIRO_OPERATOR_DEST_OUT: return QPainter::CompositionMode_DestinationOut; case CAIRO_OPERATOR_DEST_ATOP: return QPainter::CompositionMode_DestinationAtop; case CAIRO_OPERATOR_XOR: return QPainter::CompositionMode_Xor; default: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: ASSERT_NOT_REACHED; } } static bool _op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op) { if (qs->p == NULL) return false; if (qs->supports_porter_duff) { switch (op) { case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: return TRUE; default: ASSERT_NOT_REACHED; case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: return FALSE; } } else { return op == CAIRO_OPERATOR_OVER; } } static cairo_format_t _cairo_format_from_qimage_format (QImage::Format fmt) { switch (fmt) { case QImage::Format_ARGB32_Premultiplied: return CAIRO_FORMAT_ARGB32; case QImage::Format_RGB32: return CAIRO_FORMAT_RGB24; case QImage::Format_Indexed8: // XXX not quite return CAIRO_FORMAT_A8; #ifdef WORDS_BIGENDIAN case QImage::Format_Mono: #else case QImage::Format_MonoLSB: #endif return CAIRO_FORMAT_A1; case QImage::Format_Invalid: #ifdef WORDS_BIGENDIAN case QImage::Format_MonoLSB: #else case QImage::Format_Mono: #endif case QImage::Format_ARGB32: case QImage::Format_RGB16: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_RGB666: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_RGB555: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_RGB888: case QImage::Format_RGB444: case QImage::Format_ARGB4444_Premultiplied: case QImage::NImageFormats: default: ASSERT_NOT_REACHED; return (cairo_format_t) -1; } } static QImage::Format _qimage_format_from_cairo_format (cairo_format_t fmt) { switch (fmt) { case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: return QImage::Format_ARGB32_Premultiplied; case CAIRO_FORMAT_RGB24: return QImage::Format_RGB32; case CAIRO_FORMAT_RGB16_565: return QImage::Format_RGB16; case CAIRO_FORMAT_A8: return QImage::Format_Indexed8; // XXX not quite case CAIRO_FORMAT_A1: #ifdef WORDS_BIGENDIAN return QImage::Format_Mono; // XXX think we need to choose between this and LSB #else return QImage::Format_MonoLSB; #endif } return QImage::Format_Mono; } static inline QMatrix _qmatrix_from_cairo_matrix (const cairo_matrix_t& m) { return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); } /* Path conversion */ typedef struct _qpainter_path_transform { QPainterPath path; const cairo_matrix_t *ctm_inverse; } qpainter_path_data; /* cairo path -> execute in context */ static cairo_status_t _cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (pdata->ctm_inverse) cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); pdata->path.moveTo(x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (pdata->ctm_inverse) cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); pdata->path.lineTo(x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); double y1 = _cairo_fixed_to_double (p1->y); double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); if (pdata->ctm_inverse) { cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0); cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1); cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2); } pdata->path.cubicTo (x0, y0, x1, y1, x2, y2); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_qpainterpath_close_path (void *closure) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); pdata->path.closeSubpath(); return CAIRO_STATUS_SUCCESS; } static inline QPainterPath path_to_qt (const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse = NULL) { qpainter_path_data data; cairo_status_t status; if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse)) ctm_inverse = NULL; data.ctm_inverse = ctm_inverse; status = _cairo_path_fixed_interpret (path, _cairo_path_to_qpainterpath_move_to, _cairo_path_to_qpainterpath_line_to, _cairo_path_to_qpainterpath_curve_to, _cairo_path_to_qpainterpath_close_path, &data); assert (status == CAIRO_STATUS_SUCCESS); return data.path; } static inline QPainterPath path_to_qt (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_matrix_t *ctm_inverse = NULL) { QPainterPath qpath = path_to_qt (path, ctm_inverse); qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ? Qt::WindingFill : Qt::OddEvenFill); return qpath; } /* * Surface backend methods */ static cairo_surface_t * _cairo_qt_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; bool use_pixmap; D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content)); use_pixmap = qs->image == NULL; if (use_pixmap) { switch (content) { case CAIRO_CONTENT_ALPHA: use_pixmap = FALSE; break; case CAIRO_CONTENT_COLOR: break; case CAIRO_CONTENT_COLOR_ALPHA: use_pixmap = ! _qpixmaps_have_no_alpha; break; } } if (use_pixmap) { cairo_surface_t *result = cairo_qt_surface_create_with_qpixmap (content, width, height); /* XXX result->content is always content. ??? */ if (result->content == content) { D(fprintf(stderr, "qpixmap content: %d\n", content)); return result; } _qpixmaps_have_no_alpha = TRUE; cairo_surface_destroy (result); } D(fprintf (stderr, "qimage\n")); return cairo_qt_surface_create_with_qimage (_cairo_format_from_content (content), width, height); } static cairo_status_t _cairo_qt_surface_finish (void *abstract_surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] finish\n", abstract_surface)); /* Only delete p if we created it */ if (qs->image || qs->pixmap) delete qs->p; else qs->p->restore (); if (qs->image_equiv) cairo_surface_destroy (qs->image_equiv); _cairo_surface_clipper_reset (&qs->clipper); if (qs->image) delete qs->image; if (qs->pixmap) delete qs->pixmap; return CAIRO_STATUS_SUCCESS; } static void _qimg_destroy (void *closure) { QImage *qimg = (QImage *) closure; delete qimg; } static cairo_status_t _cairo_qt_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface)); *image_extra = NULL; if (qs->image_equiv) { *image_out = (cairo_image_surface_t*) cairo_surface_reference (qs->image_equiv); return CAIRO_STATUS_SUCCESS; } if (qs->pixmap) { QImage *qimg = new QImage(qs->pixmap->toImage()); cairo_surface_t *image; cairo_status_t status; image = cairo_image_surface_create_for_data (qimg->bits(), _cairo_format_from_qimage_format (qimg->format()), qimg->width(), qimg->height(), qimg->bytesPerLine()); status = _cairo_user_data_array_set_data (&image->user_data, (const cairo_user_data_key_t *)&_qimg_destroy, qimg, _qimg_destroy); if (status) { cairo_surface_destroy (image); return status; } *image_out = (cairo_image_surface_t *) image; return CAIRO_STATUS_SUCCESS; } return _cairo_error (CAIRO_STATUS_NO_MEMORY); } static void _cairo_qt_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface)); cairo_surface_destroy (&image->base); } struct _qimage_surface { cairo_image_surface_t image; QImage *qimg; }; static cairo_surface_t * map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents) { struct _qimage_surface *surface; pixman_image_t *pixman_image; pixman_format_code_t pixman_format; uint8_t *data; if (qimg == NULL) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); switch (qimg->format()) { case QImage::Format_ARGB32_Premultiplied: pixman_format = PIXMAN_a8r8g8b8; break; case QImage::Format_RGB32: pixman_format = PIXMAN_x8r8g8b8; break; case QImage::Format_Indexed8: // XXX not quite pixman_format = PIXMAN_a8; break; #ifdef WORDS_BIGENDIAN case QImage::Format_Mono: #else case QImage::Format_MonoLSB: #endif pixman_format = PIXMAN_a1; break; case QImage::Format_Invalid: #ifdef WORDS_BIGENDIAN case QImage::Format_MonoLSB: #else case QImage::Format_Mono: #endif case QImage::Format_ARGB32: case QImage::Format_RGB16: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_RGB666: case QImage::Format_ARGB6666_Premultiplied: case QImage::Format_RGB555: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_RGB888: case QImage::Format_RGB444: case QImage::Format_ARGB4444_Premultiplied: case QImage::NImageFormats: default: delete qimg; return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); } data = qimg->bits(); data += extents->y * qimg->bytesPerLine(); data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8; pixman_image = pixman_image_create_bits (pixman_format, extents->width, extents->height, (uint32_t *)data, qimg->bytesPerLine()); if (pixman_image == NULL) { delete qimg; return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } surface = (struct _qimage_surface *) malloc (sizeof(*surface)); if (unlikely (surface == NULL)) { pixman_image_unref (pixman_image); delete qimg; return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } _cairo_image_surface_init (&surface->image, pixman_image, pixman_format); surface->qimg = qimg; cairo_surface_set_device_offset (&surface->image.base, -extents->x, -extents->y); return &surface->image.base; } static cairo_image_surface_t * _cairo_qt_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; QImage *qimg = NULL; D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface)); if (qs->image_equiv) return _cairo_image_surface_map_to_image (qs->image_equiv, extents); QPoint offset; if (qs->pixmap) { qimg = new QImage(qs->pixmap->toImage()); } else { // Try to figure out what kind of QPaintDevice we have, and // how we can grab an image from it QPaintDevice *pd = qs->p->device(); if (!pd) return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); QPaintDevice *rpd = QPainter::redirected(pd, &offset); if (rpd) pd = rpd; if (pd->devType() == QInternal::Image) { qimg = new QImage(((QImage*) pd)->copy()); } else if (pd->devType() == QInternal::Pixmap) { qimg = new QImage(((QPixmap*) pd)->toImage()); } else if (pd->devType() == QInternal::Widget) { qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage()); } } return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents); } static cairo_int_status_t _cairo_qt_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface)); if (!qs->image_equiv) { struct _qimage_surface *qimage = (struct _qimage_surface *)image; // XXX should I be using setBackgroundMode here instead of setCompositionMode? if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_Source); qs->p->drawImage ((int)qimage->image.base.device_transform.x0, (int)qimage->image.base.device_transform.y0, *qimage->qimg, (int)qimage->image.base.device_transform.x0, (int)qimage->image.base.device_transform.y0, (int)qimage->image.width, (int)qimage->image.height); if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); delete qimage->qimg; } cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return CAIRO_INT_STATUS_SUCCESS; } static cairo_bool_t _cairo_qt_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; extents->x = qs->window.x(); extents->y = qs->window.y(); extents->width = qs->window.width(); extents->height = qs->window.height(); return TRUE; } static cairo_status_t _cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_qt_surface_t *qs = cairo_container_of (clipper, cairo_qt_surface_t, clipper); if (path == NULL) { if (qs->pixmap || qs->image) { // we own p qs->p->setClipping (false); } else { qs->p->restore (); qs->p->save (); } } else { // XXX Antialiasing is ignored qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip); } return CAIRO_STATUS_SUCCESS; } static void _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, const cairo_region_t *clip_region) { _cairo_surface_clipper_reset (&qs->clipper); if (clip_region == NULL) { // How the clip path is reset depends on whether we own p or not if (qs->pixmap || qs->image) { // we own p qs->p->setClipping (false); } else { qs->p->restore (); qs->p->save (); } } else { QRegion qr; int num_rects = cairo_region_num_rectangles (clip_region); for (int i = 0; i < num_rects; ++i) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, i, &rect); QRect r(rect.x, rect.y, rect.width, rect.height); qr = qr.unite(r); } qs->p->setClipRegion (qr, Qt::IntersectClip); } } static cairo_int_status_t _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, const cairo_clip_t *clip) { cairo_int_status_t status; D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)")); if (clip == NULL) { _cairo_surface_clipper_reset (&qs->clipper); // How the clip path is reset depends on whether we own p or not if (qs->pixmap || qs->image) { // we own p qs->p->setClipping (false); } else { qs->p->restore (); qs->p->save (); } return CAIRO_INT_STATUS_SUCCESS; } #if ENABLE_FAST_CLIP // Qt will implicitly enable clipping, and will use ReplaceClip // instead of IntersectClip if clipping was disabled before // Note: Qt is really bad at dealing with clip paths. It doesn't // seem to usefully recognize rectangular paths, instead going down // extremely slow paths whenever a clip path is set. So, // we do a bunch of work here to try to get rectangles or regions // down to Qt for clipping. cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { // We weren't able to extract a region from the traps. // Just hand the path down to QPainter. status = (cairo_int_status_t) _cairo_surface_clipper_set_clip (&qs->clipper, clip); } else if (status == CAIRO_INT_STATUS_SUCCESS) { _cairo_qt_surface_set_clip_region (qs, clip_region); status = CAIRO_INT_STATUS_SUCCESS; } #else status = (cairo_int_status_t) _cairo_surface_clipper_set_clip (&qs->clipper, clip); #endif return status; } /* * Brush conversion */ struct PatternToBrushConverter { PatternToBrushConverter (const cairo_pattern_t *pattern) : mAcquiredImageParent(0), mAcquiredImage(0), mAcquiredImageExtra(0) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; QColor color; color.setRgbF(solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); mBrush = QBrush(color); } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; cairo_surface_t *surface = spattern->surface; if (surface->type == CAIRO_SURFACE_TYPE_QT) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; if (qs->image) { mBrush = QBrush(*qs->image); } else if (qs->pixmap) { mBrush = QBrush(*qs->pixmap); } else { // do something smart mBrush = QBrush(0xff0000ff); } } else { cairo_image_surface_t *isurf = NULL; if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { isurf = (cairo_image_surface_t*) surface; } else { void *image_extra; if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) { mAcquiredImageParent = surface; mAcquiredImage = isurf; mAcquiredImageExtra = image_extra; } else { isurf = NULL; } } if (isurf) { mBrush = QBrush (QImage ((const uchar *) isurf->data, isurf->width, isurf->height, isurf->stride, _qimage_format_from_cairo_format (isurf->format))); } else { mBrush = QBrush(0x0000ffff); } } } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { QGradient *grad; cairo_bool_t reverse_stops = FALSE; cairo_bool_t emulate_reflect = FALSE; double offset = 0.0; cairo_extend_t extend = pattern->extend; cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern; if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y, lpat->pd2.x, lpat->pd2.y); } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern; /* Based on the SVG surface code */ cairo_circle_double_t *c0, *c1; double x0, y0, r0, x1, y1, r1; if (rpat->cd1.radius < rpat->cd2.radius) { c0 = &rpat->cd1; c1 = &rpat->cd2; reverse_stops = FALSE; } else { c0 = &rpat->cd2; c1 = &rpat->cd1; reverse_stops = TRUE; } x0 = c0->center.x; y0 = c0->center.y; r0 = c0->radius; x1 = c1->center.x; y1 = c1->center.y; r1 = c1->radius; if (r0 == r1) { grad = new QRadialGradient (x1, y1, r1, x1, y1); } else { double fx = (r1 * x0 - r0 * x1) / (r1 - r0); double fy = (r1 * y0 - r0 * y1) / (r1 - r0); /* QPainter doesn't support the inner circle and use instead a gradient focal. * That means we need to emulate the cairo behaviour by processing the * cairo gradient stops. * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, * it's just a matter of stop position translation and calculation of * the corresponding SVG radial gradient focal. * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop * list that maps to the original cairo stop list. */ if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { double r_org = r1; double r, x, y; if (extend == CAIRO_EXTEND_REFLECT) { r1 = 2 * r1 - r0; emulate_reflect = TRUE; } offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; r = r1 - r0; /* New position of outer circle. */ x = r * (x1 - fx) / r_org + fx; y = r * (y1 - fy) / r_org + fy; x1 = x; y1 = y; r1 = r; r0 = 0.0; } else { offset = r0 / r1; } grad = new QRadialGradient (x1, y1, r1, fx, fy); if (extend == CAIRO_EXTEND_NONE && r0 != 0.0) grad->setColorAt (r0 / r1, Qt::transparent); } } switch (extend) { case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_PAD: grad->setSpread(QGradient::PadSpread); grad->setColorAt (0.0, Qt::transparent); grad->setColorAt (1.0, Qt::transparent); break; case CAIRO_EXTEND_REFLECT: grad->setSpread(QGradient::ReflectSpread); break; case CAIRO_EXTEND_REPEAT: grad->setSpread(QGradient::RepeatSpread); break; } for (unsigned int i = 0; i < gpat->n_stops; i++) { int index = i; if (reverse_stops) index = gpat->n_stops - i - 1; double offset = gpat->stops[i].offset; QColor color; color.setRgbF (gpat->stops[i].color.red, gpat->stops[i].color.green, gpat->stops[i].color.blue, gpat->stops[i].color.alpha); if (emulate_reflect) { offset = offset / 2.0; grad->setColorAt (1.0 - offset, color); } grad->setColorAt (offset, color); } mBrush = QBrush(*grad); delete grad; } if (mBrush.style() != Qt::NoBrush && pattern->type != CAIRO_PATTERN_TYPE_SOLID && ! _cairo_matrix_is_identity (&pattern->matrix)) { cairo_matrix_t pm = pattern->matrix; cairo_status_t status = cairo_matrix_invert (&pm); assert (status == CAIRO_STATUS_SUCCESS); mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm)); } } ~PatternToBrushConverter () { if (mAcquiredImageParent) _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra); } operator QBrush& () { return mBrush; } QBrush mBrush; private: cairo_surface_t *mAcquiredImageParent; cairo_image_surface_t *mAcquiredImage; void *mAcquiredImageExtra; }; struct PatternToPenConverter { PatternToPenConverter (const cairo_pattern_t *source, const cairo_stroke_style_t *style) : mBrushConverter(source) { Qt::PenJoinStyle join = Qt::MiterJoin; Qt::PenCapStyle cap = Qt::SquareCap; switch (style->line_cap) { case CAIRO_LINE_CAP_BUTT: cap = Qt::FlatCap; break; case CAIRO_LINE_CAP_ROUND: cap = Qt::RoundCap; break; case CAIRO_LINE_CAP_SQUARE: cap = Qt::SquareCap; break; } switch (style->line_join) { case CAIRO_LINE_JOIN_MITER: join = Qt::MiterJoin; break; case CAIRO_LINE_JOIN_ROUND: join = Qt::RoundJoin; break; case CAIRO_LINE_JOIN_BEVEL: join = Qt::BevelJoin; break; } mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join); mPen.setMiterLimit (style->miter_limit); if (style->dash && style->num_dashes) { Qt::PenStyle pstyle = Qt::NoPen; if (style->num_dashes == 2) { if ((style->dash[0] == style->line_width && style->dash[1] == style->line_width && style->line_width <= 2.0) || (style->dash[0] == 0.0 && style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap)) { pstyle = Qt::DotLine; } else if (style->dash[0] == style->line_width * DASH_LENGTH && style->dash[1] == style->line_width * DASH_LENGTH && cap == Qt::FlatCap) { pstyle = Qt::DashLine; } } if (pstyle != Qt::NoPen) { mPen.setStyle(pstyle); return; } unsigned int odd_dash = style->num_dashes % 2; QVector<qreal> dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes); for (unsigned int i = 0; i < odd_dash+1; i++) { for (unsigned int j = 0; j < style->num_dashes; j++) { // In Qt, the dash lengths are given in units of line width, whereas // in cairo, they are in user-space units. We'll always apply the CTM, // so all we have to do here is divide cairo's dash lengths by the line // width. dashes.append (style->dash[j] / style->line_width); } } mPen.setDashPattern(dashes); mPen.setDashOffset(style->dash_offset / style->line_width); } } ~PatternToPenConverter() { } operator QPen& () { return mPen; } QPen mPen; PatternToBrushConverter mBrushConverter; }; /* * Core drawing operations */ static bool _cairo_qt_fast_fill (cairo_qt_surface_t *qs, const cairo_pattern_t *source, const cairo_path_fixed_t *path = NULL, cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING, double tolerance = 0.0, cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE) { #if ENABLE_FAST_FILL QImage *qsSrc_image = NULL; QPixmap *qsSrc_pixmap = NULL; std::auto_ptr<QImage> qsSrc_image_d; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source; if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) { cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface; qsSrc_image = p->image; qsSrc_pixmap = p->pixmap; } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface; qsSrc_image = new QImage((const uchar*) p->data, p->width, p->height, p->stride, _qimage_format_from_cairo_format(p->format)); qsSrc_image_d.reset(qsSrc_image); } } if (!qsSrc_image && !qsSrc_pixmap) return false; // We can only drawTiledPixmap; there's no drawTiledImage if (! qsSrc_pixmap && (source->extend == CAIRO_EXTEND_REPEAT || source->extend == CAIRO_EXTEND_REFLECT)) { return false; } QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix); // We can draw this faster by clipping and calling drawImage/drawPixmap. // Use our own clipping function so that we can get the // region handling to end up with the fastest possible clip. // // XXX Antialiasing will fail pretty hard here, since we can't clip with AA // with QPainter. qs->p->save(); if (path) { cairo_int_status_t status; cairo_clip_t clip, old_clip = qs->clipper.clip; _cairo_clip_init_copy (&clip, &qs->clipper.clip); status = (cairo_int_status_t) _cairo_clip_clip (&clip, path, fill_rule, tolerance, antialias); if (unlikely (status)) { qs->p->restore(); return false; } status = _cairo_qt_surface_set_clip (qs, &clip); if (unlikely (status)) { qs->p->restore(); return false; } _cairo_clip_reset (&clip); qs->clipper.clip = old_clip; } qs->p->setWorldMatrix (sourceMatrix.inverted(), true); switch (source->extend) { case CAIRO_EXTEND_REPEAT: // XXX handle reflect by tiling 4 times first case CAIRO_EXTEND_REFLECT: { assert (qsSrc_pixmap); // Render the tiling to cover the entire destination window (because // it'll be clipped). Transform the window rect by the inverse // of the current world transform so that the device coordinates // end up as the right thing. QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window)); QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0)); qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin); } break; case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough default: if (qsSrc_image) qs->p->drawImage (0, 0, *qsSrc_image); else if (qsSrc_pixmap) qs->p->drawPixmap (0, 0, *qsSrc_pixmap); break; } qs->p->restore(); return true; #else return false; #endif } static cairo_int_status_t _cairo_qt_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; cairo_int_status_t status; D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op))); if (! _op_is_supported (qs, op)) return _cairo_surface_fallback_paint (abstract_surface, op, source, clip); status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (status)) return status; if (qs->supports_porter_duff) qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); if (! _cairo_qt_fast_fill (qs, source)) { PatternToBrushConverter brush (source); qs->p->fillRect (qs->window, brush); } if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_qt_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op))); if (! _op_is_supported (qs, op)) return _cairo_surface_fallback_fill (abstract_surface, op, source, path, fill_rule, tolerance, antialias, clip); cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (status)) return status; if (qs->supports_porter_duff) qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is // enabled //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); if (! _cairo_qt_fast_fill (qs, source, path, fill_rule, tolerance, antialias)) { PatternToBrushConverter brush(source); qs->p->fillPath (path_to_qt (path, fill_rule), brush); } if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_qt_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op))); if (! _op_is_supported (qs, op)) return _cairo_surface_fallback_stroke (abstract_surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (int_status)) return int_status; QMatrix savedMatrix = qs->p->worldMatrix(); if (qs->supports_porter_duff) qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true); // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is // enabled //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); PatternToPenConverter pen(source, style); qs->p->setPen(pen); qs->p->drawPath(path_to_qt (path, ctm_inverse)); qs->p->setPen(Qt::black); qs->p->setWorldMatrix (savedMatrix, false); if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_qt_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { #if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; // pick out the colour to use from the cairo source cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source; cairo_scaled_glyph_t* glyph; // documentation says you have to freeze the cache, but I don't believe it _cairo_scaled_font_freeze_cache(scaled_font); QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255); QVarLengthArray<QPointF> positions(num_glyphs); QVarLengthArray<unsigned int> glyphss(num_glyphs); FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font); const FT_Size_Metrics& ftMetrics = face->size->metrics; QFont font(face->family_name); font.setStyleStrategy(QFont::NoFontMerging); font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD); font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC); font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING); font.setPixelSize(ftMetrics.y_ppem); cairo_ft_scaled_font_unlock_face(scaled_font); qs->p->setFont(font); qs->p->setPen(tempColour); for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) { positions[currentGlyph].setX(glyphs[currentGlyph].x); positions[currentGlyph].setY(glyphs[currentGlyph].y); glyphss[currentGlyph] = glyphs[currentGlyph].index; } qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs); _cairo_scaled_font_thaw_cache(scaled_font); return CAIRO_INT_STATUS_SUCCESS; #else return _cairo_surface_fallback_glyphs (abstract_surface, op, source, glyphs, num_glyphs, scaled_font, clip); #endif } static cairo_int_status_t _cairo_qt_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op))); if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; cairo_int_status_t result; qs->p->setOpacity (solid_mask->color.alpha); result = _cairo_qt_surface_paint (abstract_surface, op, source, clip); qs->p->setOpacity (1.0); return result; } // otherwise skip for now return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip); } static cairo_status_t _cairo_qt_surface_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; if (qs->p && !(qs->image || qs->pixmap)) qs->p->save (); return CAIRO_STATUS_SUCCESS; } /* * Backend struct */ static const cairo_surface_backend_t cairo_qt_surface_backend = { CAIRO_SURFACE_TYPE_QT, _cairo_qt_surface_finish, _cairo_default_context_create, /* XXX */ _cairo_qt_surface_create_similar, NULL, /* similar image */ _cairo_qt_surface_map_to_image, _cairo_qt_surface_unmap_image, _cairo_surface_default_source, _cairo_qt_surface_acquire_source_image, _cairo_qt_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_qt_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ _cairo_qt_surface_mark_dirty, _cairo_qt_surface_paint, _cairo_qt_surface_mask, _cairo_qt_surface_stroke, _cairo_qt_surface_fill, NULL, /* fill_stroke */ _cairo_qt_surface_show_glyphs }; cairo_surface_t * cairo_qt_surface_create (QPainter *painter) { cairo_qt_surface_t *qs; qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (qs, 0, sizeof(cairo_qt_surface_t)); _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); qs->p = painter; if (qs->p->paintEngine()) qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); else qs->supports_porter_duff = FALSE; // Save so that we can always get back to the original state qs->p->save(); qs->window = painter->window(); D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n", qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), qs->supports_porter_duff)); return &qs->base; } cairo_surface_t * cairo_qt_surface_create_with_qimage (cairo_format_t format, int width, int height) { cairo_qt_surface_t *qs; qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (qs, 0, sizeof(cairo_qt_surface_t)); _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, NULL, /* device */ _cairo_content_from_format (format)); _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); QImage *image = new QImage (width, height, _qimage_format_from_cairo_format (format)); qs->image = image; if (!image->isNull()) { qs->p = new QPainter(image); qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); } qs->image_equiv = cairo_image_surface_create_for_data (image->bits(), format, width, height, image->bytesPerLine()); qs->window = QRect(0, 0, width, height); D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n", qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), qs->supports_porter_duff)); return &qs->base; } cairo_surface_t * cairo_qt_surface_create_with_qpixmap (cairo_content_t content, int width, int height) { cairo_qt_surface_t *qs; if ((content & CAIRO_CONTENT_COLOR) == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (qs, 0, sizeof(cairo_qt_surface_t)); QPixmap *pixmap = new QPixmap (width, height); if (pixmap == NULL) { free (qs); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } // By default, a QPixmap is opaque; however, if it's filled // with a color with a transparency component, it is converted // to a format that preserves transparency. if (content == CAIRO_CONTENT_COLOR_ALPHA) pixmap->fill(Qt::transparent); _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, NULL, /* device */ content); _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); qs->pixmap = pixmap; if (!pixmap->isNull()) { qs->p = new QPainter(pixmap); qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); } qs->window = QRect(0, 0, width, height); D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n", qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), qs->supports_porter_duff)); return &qs->base; } QPainter * cairo_qt_surface_get_qpainter (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; if (surface->type != CAIRO_SURFACE_TYPE_QT) return NULL; return qs->p; } QImage * cairo_qt_surface_get_qimage (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; if (surface->type != CAIRO_SURFACE_TYPE_QT) return NULL; return qs->image; } cairo_surface_t * cairo_qt_surface_get_image (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; if (surface->type != CAIRO_SURFACE_TYPE_QT) return NULL; return qs->image_equiv; } /* * TODO: * * - Figure out why QBrush isn't working with non-repeated images * * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT * - implement EXTEND_NONE (?? probably need to clip to the extents of the source) * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that) * * - stroke-image failure * * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN) * * - Implement gradient sources * * - Make create_similar smarter -- create QPixmaps in more circumstances * (e.g. if the pixmap can have alpha) * * - Implement show_glyphs() in terms of Qt * */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-qt.h��������������������������������������0000664�0000000�0000000�00000005321�12710376503�0024171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #ifndef CAIRO_QT_H #define CAIRO_QT_H #include "cairo.h" #if CAIRO_HAS_QT_SURFACE #include <QtGui/QImage> #include <QtGui/QPainter> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_qt_surface_create (QPainter *painter); cairo_public cairo_surface_t * cairo_qt_surface_create_with_qimage (cairo_format_t format, int width, int height); cairo_public cairo_surface_t * cairo_qt_surface_create_with_qpixmap (cairo_content_t content, int width, int height); cairo_public QPainter * cairo_qt_surface_get_qpainter (cairo_surface_t *surface); /* XXX needs hooking to generic surface layer, my vote is for cairo_public cairo_surface_t * cairo_surface_map_image (cairo_surface_t *surface); cairo_public void cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image); */ cairo_public cairo_surface_t * cairo_qt_surface_get_image (cairo_surface_t *surface); cairo_public QImage * cairo_qt_surface_get_qimage (cairo_surface_t *surface); CAIRO_END_DECLS #else /* CAIRO_HAS_QT_SURFACE */ # error Cairo was not compiled with support for the Qt backend #endif /* CAIRO_HAS_QT_SURFACE */ #endif /* CAIRO_QT_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz-font.c�����������������������������0000664�0000000�0000000�00000066341�12710376503�0026043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright � 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #include "cairoint.h" #include <dlfcn.h> #include "cairo-image-surface-private.h" #include "cairo-quartz.h" #include "cairo-quartz-private.h" #include "cairo-error-private.h" /** * SECTION:cairo-quartz-fonts * @Title: Quartz (CGFont) Fonts * @Short_Description: Font support via CGFont on OS X * @See_Also: #cairo_font_face_t * * The Quartz font backend is primarily used to render text on Apple * MacOS X systems. The CGFont API is used for the internal * implementation of the font backend methods. **/ /** * CAIRO_HAS_QUARTZ_FONT: * * Defined if the Quartz font backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.6 **/ static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL; /* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */ static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL; static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL; /* These aren't public before 10.5, and some have different names in 10.4 */ static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL; static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL; static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL; static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL; /* Not public, but present */ static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL; static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; /* Not public in the least bit */ // static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL; /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ typedef struct { int ascent; int descent; int leading; } quartz_CGFontMetrics; static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; /* Not public anymore in 64-bits nor in 10.7 */ static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; static void quartz_font_ensure_symbols(void) { if (_cairo_quartz_font_symbol_lookup_done) return; CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag"); /* Look for the 10.5 versions first */ CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); if (!CGFontGetGlyphBBoxesPtr) CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); if (!CGFontGetGlyphsForUnicharsPtr) CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); /* We just need one of these two */ CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); /* These have the same name in 10.4 and 10.5 */ CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); // CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && CGFontGetGlyphBBoxesPtr && CGFontGetGlyphsForUnicharsPtr && CGFontGetUnitsPerEmPtr && CGFontGetGlyphAdvancesPtr && //CGFontGetGlyphPathPtr && (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) _cairo_quartz_font_symbols_present = TRUE; _cairo_quartz_font_symbol_lookup_done = TRUE; } typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t; typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; struct _cairo_quartz_scaled_font { cairo_scaled_font_t base; }; struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; }; /* * font face backend */ static cairo_status_t _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face) { const char *family; char *full_name; CFStringRef cgFontName = NULL; CGFontRef cgFont = NULL; int loop; quartz_font_ensure_symbols(); if (! _cairo_quartz_font_symbols_present) return _cairo_error (CAIRO_STATUS_NO_MEMORY); family = toy_face->family; full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. /* handle CSS-ish faces */ if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) family = "Times"; else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) family = "Helvetica"; else if (!strcmp(family, "cursive")) family = "Apple Chancery"; else if (!strcmp(family, "fantasy")) family = "Papyrus"; else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) family = "Courier"; /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, * then drop the bold, then drop the slant, then drop both.. finally * just use "Helvetica". And if Helvetica doesn't exist, give up. */ for (loop = 0; loop < 5; loop++) { if (loop == 4) family = "Helvetica"; strcpy (full_name, family); if (loop < 3 && (loop & 1) == 0) { if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) strcat (full_name, " Bold"); } if (loop < 3 && (loop & 2) == 0) { if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) strcat (full_name, " Italic"); else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) strcat (full_name, " Oblique"); } if (CGFontCreateWithFontNamePtr) { cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); cgFont = CGFontCreateWithFontNamePtr (cgFontName); CFRelease (cgFontName); } else { cgFont = CGFontCreateWithNamePtr (full_name); } if (cgFont) break; } if (!cgFont) { /* Give up */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); CGFontRelease (cgFont); return CAIRO_STATUS_SUCCESS; } static void _cairo_quartz_font_face_destroy (void *abstract_face) { cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; CGFontRelease (font_face->cgFont); } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; static cairo_status_t _cairo_quartz_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font_out) { cairo_quartz_font_face_t *font_face = abstract_face; cairo_quartz_scaled_font_t *font = NULL; cairo_status_t status; cairo_font_extents_t fs_metrics; double ems; CGRect bbox; quartz_font_ensure_symbols(); if (!_cairo_quartz_font_symbols_present) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font = malloc(sizeof(cairo_quartz_scaled_font_t)); if (font == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memset (font, 0, sizeof(cairo_quartz_scaled_font_t)); status = _cairo_scaled_font_init (&font->base, &font_face->base, font_matrix, ctm, options, &_cairo_quartz_scaled_font_backend); if (status) goto FINISH; ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); /* initialize metrics */ if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (CGFontGetLeadingPtr (font_face->cgFont) / ems); bbox = CGFontGetFontBBoxPtr (font_face->cgFont); fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; fs_metrics.max_y_advance = 0.0; } else { CGGlyph wGlyph; UniChar u; quartz_CGFontMetrics *m; m = CGFontGetHMetricsPtr (font_face->cgFont); /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ if (!m) { status = _cairo_error(CAIRO_STATUS_NULL_POINTER); goto FINISH; } fs_metrics.ascent = (m->ascent / ems); fs_metrics.descent = - (m->descent / ems); fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); /* We kind of have to guess here; W's big, right? */ u = (UniChar) 'W'; CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; fs_metrics.max_y_advance = 0.0; } else { fs_metrics.max_x_advance = 0.0; fs_metrics.max_y_advance = 0.0; } } status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); FINISH: if (status != CAIRO_STATUS_SUCCESS) { free (font); } else { *font_out = (cairo_scaled_font_t*) font; } return status; } const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { CAIRO_FONT_TYPE_QUARTZ, _cairo_quartz_font_face_create_for_toy, _cairo_quartz_font_face_destroy, _cairo_quartz_font_face_scaled_font_create }; /** * cairo_quartz_font_face_create_for_cgfont: * @font: a #CGFontRef obtained through a method external to cairo. * * Creates a new font for the Quartz font backend based on a * #CGFontRef. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.6 **/ cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { cairo_quartz_font_face_t *font_face; quartz_font_ensure_symbols(); font_face = malloc (sizeof (cairo_quartz_font_face_t)); if (!font_face) { cairo_status_t ignore_status; ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } font_face->cgFont = CGFontRetain (font); _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); return &font_face->base; } /* * scaled font backend */ static cairo_quartz_font_face_t * _cairo_quartz_scaled_to_face (void *abstract_font) { cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font; cairo_font_face_t *font_face = sfont->base.font_face; assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ); return (cairo_quartz_font_face_t*) font_face; } static void _cairo_quartz_scaled_font_fini(void *abstract_font) { } #define INVALID_GLYPH 0x00 static inline CGGlyph _cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) { unsigned long index = _cairo_scaled_glyph_index (scaled_glyph); if (index > 0xffff) return INVALID_GLYPH; return (CGGlyph) index; } static cairo_int_status_t _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); int advance; CGRect bbox; double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); double xmin, ymin, xmax, ymax; if (glyph == INVALID_GLYPH) goto FAIL; if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) goto FAIL; /* broken fonts like Al Bayan return incorrect bounds for some null characters, see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ if (unlikely (bbox.origin.x == -32767 && bbox.origin.y == -32767 && bbox.size.width == 65534 && bbox.size.height == 65534)) { bbox.origin.x = bbox.origin.y = 0; bbox.size.width = bbox.size.height = 0; } bbox = CGRectMake (bbox.origin.x / emscale, bbox.origin.y / emscale, bbox.size.width / emscale, bbox.size.height / emscale); /* Should we want to always integer-align glyph extents, we can do so in this way */ #if 0 { CGAffineTransform textMatrix; textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, -font->base.scale.xy, font->base.scale.yy, 0.0f, 0.0f); bbox = CGRectApplyAffineTransform (bbox, textMatrix); bbox = CGRectIntegral (bbox); bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); } #endif #if 0 fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph, bbox.origin.x / emscale, bbox.origin.y / emscale, bbox.size.width / emscale, bbox.size.height / emscale); #endif xmin = CGRectGetMinX(bbox); ymin = CGRectGetMinY(bbox); xmax = CGRectGetMaxX(bbox); ymax = CGRectGetMaxY(bbox); extents.x_bearing = xmin; extents.y_bearing = - ymax; extents.width = xmax - xmin; extents.height = ymax - ymin; extents.x_advance = (double) advance / emscale; extents.y_advance = 0.0; #if 0 fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); #endif FAIL: _cairo_scaled_glyph_set_metrics (scaled_glyph, &font->base, &extents); return status; } static void _cairo_quartz_path_apply_func (void *info, const CGPathElement *el) { cairo_path_fixed_t *path = (cairo_path_fixed_t *) info; cairo_status_t status; switch (el->type) { case kCGPathElementMoveToPoint: status = _cairo_path_fixed_move_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y)); assert(!status); break; case kCGPathElementAddLineToPoint: status = _cairo_path_fixed_line_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y)); assert(!status); break; case kCGPathElementAddQuadCurveToPoint: { cairo_fixed_t fx, fy; double x, y; if (!_cairo_path_fixed_get_current_point (path, &fx, &fy)) fx = fy = 0; x = _cairo_fixed_to_double (fx); y = _cairo_fixed_to_double (fy); status = _cairo_path_fixed_curve_to (path, _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0), _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0), _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0), _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0), _cairo_fixed_from_double(el->points[1].x), _cairo_fixed_from_double(el->points[1].y)); } assert(!status); break; case kCGPathElementAddCurveToPoint: status = _cairo_path_fixed_curve_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y), _cairo_fixed_from_double(el->points[1].x), _cairo_fixed_from_double(el->points[1].y), _cairo_fixed_from_double(el->points[2].x), _cairo_fixed_from_double(el->points[2].y)); assert(!status); break; case kCGPathElementCloseSubpath: status = _cairo_path_fixed_close_path (path); assert(!status); break; } } static cairo_int_status_t _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; CTFontRef ctFont; cairo_path_fixed_t *path; if (glyph == INVALID_GLYPH) { _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); return CAIRO_STATUS_SUCCESS; } /* scale(1,-1) * font->base.scale */ textMatrix = CGAffineTransformMake (font->base.scale.xx, font->base.scale.yx, -font->base.scale.xy, -font->base.scale.yy, 0, 0); // glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 0.0, NULL, NULL); glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); CFRelease (ctFont); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; path = _cairo_path_fixed_create (); if (!path) { CGPathRelease (glyphPath); return _cairo_error(CAIRO_STATUS_NO_MEMORY); } CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func); CGPathRelease (glyphPath); _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); cairo_image_surface_t *surface = NULL; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); int advance; CGRect bbox; double width, height; double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); CGContextRef cgContext = NULL; CGAffineTransform textMatrix; CGRect glyphRect, glyphRectInt; CGPoint glyphOrigin; //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); /* Create blank 2x2 image if we don't have this character. * Maybe we should draw a better missing-glyph slug or something, * but this is ok for now. */ if (glyph == INVALID_GLYPH) { surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2); status = cairo_surface_status ((cairo_surface_t *) surface); if (status) return status; _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); return CAIRO_STATUS_SUCCESS; } if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) { return CAIRO_INT_STATUS_UNSUPPORTED; } /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, -font->base.scale.xy, font->base.scale.yy, 0, -0); glyphRect = CGRectMake (bbox.origin.x / emscale, bbox.origin.y / emscale, bbox.size.width / emscale, bbox.size.height / emscale); glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); /* Round the rectangle outwards, so that we don't have to deal * with non-integer-pixel origins or dimensions. */ glyphRectInt = CGRectIntegral (glyphRect); #if 0 fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); fprintf (stderr, "glyphRectInt: %f %f %f %f\n", glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height); #endif glyphOrigin = glyphRectInt.origin; //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); width = glyphRectInt.size.width; height = glyphRectInt.size.height; //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); if (surface->base.status) return surface->base.status; if (surface->width != 0 && surface->height != 0) { cgContext = CGBitmapContextCreate (surface->data, surface->width, surface->height, 8, surface->stride, NULL, kCGImageAlphaOnly); if (cgContext == NULL) { cairo_surface_destroy (&surface->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } CGContextSetFont (cgContext, font_face->cgFont); CGContextSetFontSize (cgContext, 1.0); CGContextSetTextMatrix (cgContext, textMatrix); switch (font->base.options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_BEST: CGContextSetShouldAntialias (cgContext, TRUE); CGContextSetShouldSmoothFonts (cgContext, TRUE); if (CGContextSetAllowsFontSmoothingPtr && !CGContextGetAllowsFontSmoothingPtr (cgContext)) CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE); break; case CAIRO_ANTIALIAS_NONE: CGContextSetShouldAntialias (cgContext, FALSE); break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_FAST: CGContextSetShouldAntialias (cgContext, TRUE); CGContextSetShouldSmoothFonts (cgContext, FALSE); break; case CAIRO_ANTIALIAS_DEFAULT: default: /* Don't do anything */ break; } CGContextSetAlpha (cgContext, 1.0); CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); CGContextRelease (cgContext); } cairo_surface_set_device_offset (&surface->base, - glyphOrigin.x, height + glyphOrigin.y); _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); return status; } static cairo_int_status_t _cairo_quartz_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS)) status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph); if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) status = _cairo_quartz_init_glyph_path (font, scaled_glyph); if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); return status; } static unsigned long _cairo_quartz_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); UniChar u = (UniChar) ucs4; CGGlyph glyph; CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1); return glyph; } static cairo_int_status_t _cairo_quartz_load_truetype_table (void *abstract_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length) { cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); CFDataRef data = NULL; if (likely (CGFontCopyTableForTagPtr)) data = CGFontCopyTableForTagPtr (font_face->cgFont, tag); if (!data) return CAIRO_INT_STATUS_UNSUPPORTED; if (buffer == NULL) { *length = CFDataGetLength (data); CFRelease (data); return CAIRO_STATUS_SUCCESS; } if (CFDataGetLength (data) < offset + (long) *length) { CFRelease (data); return CAIRO_INT_STATUS_UNSUPPORTED; } CFDataGetBytes (data, CFRangeMake (offset, *length), buffer); CFRelease (data); return CAIRO_STATUS_SUCCESS; } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { CAIRO_FONT_TYPE_QUARTZ, _cairo_quartz_scaled_font_fini, _cairo_quartz_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_quartz_ucs4_to_index, _cairo_quartz_load_truetype_table, NULL, /* map_glyphs_to_unicode */ }; /* * private methods that the quartz surface uses */ CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) { cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); return ffont->cgFont; } /* * compat with old ATSUI backend */ /** * cairo_quartz_font_face_create_for_atsu_font_id: * @font_id: an ATSUFontID for the font. * * Creates a new font for the Quartz font backend based on an * #ATSUFontID. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.6 **/ cairo_font_face_t * cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id) { quartz_font_ensure_symbols(); if (FMGetATSFontRefFromFontPtr != NULL) { ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id); CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); cairo_font_face_t *ff; ff = cairo_quartz_font_face_create_for_cgfont (cgFont); CGFontRelease (cgFont); return ff; } else { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } } /* This is the old name for the above function, exported for compat purposes */ cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id); cairo_font_face_t * cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id) { return cairo_quartz_font_face_create_for_atsu_font_id (font_id); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz-image-surface.c��������������������0000664�0000000�0000000�00000027364�12710376503�0027607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright � 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #include "cairoint.h" #include "cairo-image-surface-inline.h" #include "cairo-quartz-image.h" #include "cairo-quartz-private.h" #include "cairo-surface-backend-private.h" #include "cairo-error-private.h" #include "cairo-default-context-private.h" #define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY))) #define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH))) #define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE))) #define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) static void DataProviderReleaseCallback (void *info, const void *data, size_t size) { cairo_surface_t *surface = (cairo_surface_t *) info; cairo_surface_destroy (surface); } static cairo_surface_t * _cairo_quartz_image_surface_create_similar (void *asurface, cairo_content_t content, int width, int height) { cairo_surface_t *isurf = _cairo_image_surface_create_with_content (content, width, height); cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); cairo_surface_destroy (isurf); return result; } static cairo_surface_t * _cairo_quartz_image_surface_create_similar_image (void *asurface, cairo_format_t format, int width, int height) { cairo_surface_t *isurf = cairo_image_surface_create (format, width, height); cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); cairo_surface_destroy (isurf); return result; } static cairo_status_t _cairo_quartz_image_surface_finish (void *asurface) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; /* the imageSurface will be destroyed by the data provider's release callback */ CGImageRelease (surface->image); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_quartz_image_surface_acquire_source_image (void *asurface, cairo_image_surface_t **image_out, void **image_extra) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; *image_out = surface->imageSurface; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_image_surface_t * _cairo_quartz_image_surface_map_to_image (void *asurface, const cairo_rectangle_int_t *extents) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; return _cairo_surface_map_to_image (&surface->imageSurface->base, extents); } static cairo_int_status_t _cairo_quartz_image_surface_unmap_image (void *asurface, cairo_image_surface_t *image) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; return _cairo_surface_unmap_image (&surface->imageSurface->base, image); } static cairo_bool_t _cairo_quartz_image_surface_get_extents (void *asurface, cairo_rectangle_int_t *extents) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; extents->x = 0; extents->y = 0; extents->width = surface->width; extents->height = surface->height; return TRUE; } /* we assume some drawing happened to the image buffer; make sure it's * represented in the CGImage on flush() */ static cairo_status_t _cairo_quartz_image_surface_flush (void *asurface, unsigned flags) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; CGImageRef oldImage = surface->image; CGImageRef newImage = NULL; if (flags) return CAIRO_STATUS_SUCCESS; /* XXX only flush if the image has been modified. */ /* To be released by the ReleaseCallback */ cairo_surface_reference ((cairo_surface_t*) surface->imageSurface); newImage = CairoQuartzCreateCGImage (surface->imageSurface->format, surface->imageSurface->width, surface->imageSurface->height, surface->imageSurface->stride, surface->imageSurface->data, TRUE, NULL, DataProviderReleaseCallback, surface->imageSurface); surface->image = newImage; CGImageRelease (oldImage); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_quartz_image_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_quartz_image_surface_t *surface = abstract_surface; return _cairo_surface_paint (&surface->imageSurface->base, op, source, clip); } static cairo_int_status_t _cairo_quartz_image_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_quartz_image_surface_t *surface = abstract_surface; return _cairo_surface_mask (&surface->imageSurface->base, op, source, mask, clip); } static cairo_int_status_t _cairo_quartz_image_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_quartz_image_surface_t *surface = abstract_surface; return _cairo_surface_stroke (&surface->imageSurface->base, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_quartz_image_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_quartz_image_surface_t *surface = abstract_surface; return _cairo_surface_fill (&surface->imageSurface->base, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_quartz_image_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_quartz_image_surface_t *surface = abstract_surface; return _cairo_surface_show_text_glyphs (&surface->imageSurface->base, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); } static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, _cairo_quartz_image_surface_finish, _cairo_default_context_create, _cairo_quartz_image_surface_create_similar, _cairo_quartz_image_surface_create_similar_image, _cairo_quartz_image_surface_map_to_image, _cairo_quartz_image_surface_unmap_image, _cairo_surface_default_source, _cairo_quartz_image_surface_acquire_source_image, NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_quartz_image_surface_get_extents, NULL, /* get_font_options */ _cairo_quartz_image_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_quartz_image_surface_paint, _cairo_quartz_image_surface_mask, _cairo_quartz_image_surface_stroke, _cairo_quartz_image_surface_fill, NULL, /* fill-stroke */ _cairo_quartz_image_surface_glyphs, }; /** * cairo_quartz_image_surface_create: * @image_surface: a cairo image surface to wrap with a quartz image surface * * Creates a Quartz surface backed by a CGImageRef that references the * given image surface. The resulting surface can be rendered quickly * when used as a source when rendering to a #cairo_quartz_surface. If * the data in the image surface is ever updated, cairo_surface_flush() * must be called on the #cairo_quartz_image_surface to ensure that the * CGImageRef refers to the updated data. * * Return value: the newly created surface. * * Since: 1.6 **/ cairo_surface_t * cairo_quartz_image_surface_create (cairo_surface_t *surface) { cairo_quartz_image_surface_t *qisurf; CGImageRef image; cairo_image_surface_t *image_surface; int width, height, stride; cairo_format_t format; unsigned char *data; if (surface->status) return surface; if (! _cairo_surface_is_image (surface)) return SURFACE_ERROR_TYPE_MISMATCH; image_surface = (cairo_image_surface_t*) surface; width = image_surface->width; height = image_surface->height; stride = image_surface->stride; format = image_surface->format; data = image_surface->data; if (!_cairo_quartz_verify_surface_size(width, height)) return SURFACE_ERROR_INVALID_SIZE; if (width == 0 || height == 0) return SURFACE_ERROR_INVALID_SIZE; if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) return SURFACE_ERROR_INVALID_FORMAT; qisurf = malloc(sizeof(cairo_quartz_image_surface_t)); if (qisurf == NULL) return SURFACE_ERROR_NO_MEMORY; memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t)); /* In case the create_cgimage fails, this ref will * be released via the callback (which will be called in * case of failure.) */ cairo_surface_reference (surface); image = CairoQuartzCreateCGImage (format, width, height, stride, data, TRUE, NULL, DataProviderReleaseCallback, image_surface); if (!image) { free (qisurf); return SURFACE_ERROR_NO_MEMORY; } _cairo_surface_init (&qisurf->base, &cairo_quartz_image_surface_backend, NULL, /* device */ _cairo_content_from_format (format)); qisurf->width = width; qisurf->height = height; qisurf->image = image; qisurf->imageSurface = image_surface; return &qisurf->base; } cairo_surface_t * cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; if (asurface->type != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) return NULL; return (cairo_surface_t*) surface->imageSurface; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz-image.h����������������������������0000664�0000000�0000000�00000004052�12710376503�0026153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #ifndef CAIRO_QUARTZ_IMAGE_H #define CAIRO_QUARTZ_IMAGE_H #include "cairo.h" #if CAIRO_HAS_QUARTZ_IMAGE_SURFACE #include <Carbon/Carbon.h> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_quartz_image_surface_create (cairo_surface_t *image_surface); cairo_public cairo_surface_t * cairo_quartz_image_surface_get_image (cairo_surface_t *surface); CAIRO_END_DECLS #else /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ # error Cairo was not compiled with support for the quartz-image backend #endif /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ #endif /* CAIRO_QUARTZ_IMAGE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz-private.h��������������������������0000664�0000000�0000000�00000006211�12710376503�0026542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Calum Robinson * Copyright (C) 2006,2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Calum Robinson * * Contributor(s): * Calum Robinson <calumr@mac.com> * Vladimir Vukicevic <vladimir@mozilla.com> */ #ifndef CAIRO_QUARTZ_PRIVATE_H #define CAIRO_QUARTZ_PRIVATE_H #include "cairoint.h" #if CAIRO_HAS_QUARTZ_SURFACE #include "cairo-quartz.h" #include "cairo-surface-clipper-private.h" #ifdef CGFLOAT_DEFINED typedef CGFloat cairo_quartz_float_t; #else typedef float cairo_quartz_float_t; #endif typedef enum { DO_DIRECT, DO_SHADING, DO_IMAGE, DO_TILED_IMAGE } cairo_quartz_action_t; typedef struct cairo_quartz_surface { cairo_surface_t base; CGContextRef cgContext; CGAffineTransform cgContextBaseCTM; void *imageData; cairo_surface_t *imageSurfaceEquiv; cairo_surface_clipper_t clipper; cairo_rectangle_int_t extents; cairo_rectangle_int_t virtual_extents; } cairo_quartz_surface_t; typedef struct cairo_quartz_image_surface { cairo_surface_t base; int width, height; CGImageRef image; cairo_image_surface_t *imageSurface; } cairo_quartz_image_surface_t; cairo_private cairo_bool_t _cairo_quartz_verify_surface_size(int width, int height); cairo_private CGImageRef CairoQuartzCreateCGImage (cairo_format_t format, unsigned int width, unsigned int height, unsigned int stride, void *data, cairo_bool_t interpolate, CGColorSpaceRef colorSpaceOverride, CGDataProviderReleaseDataCallback releaseCallback, void *releaseInfo); cairo_private CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); #else # error Cairo was not compiled with support for the quartz backend #endif /* CAIRO_HAS_QUARTZ_SURFACE */ #endif /* CAIRO_QUARTZ_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz-surface.c��������������������������0000664�0000000�0000000�00000232374�12710376503�0026526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright � 2006, 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #define _GNU_SOURCE /* required for RTLD_DEFAULT */ #include "cairoint.h" #include "cairo-quartz-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-pattern-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-recording-surface-private.h" #include <dlfcn.h> #ifndef RTLD_DEFAULT #define RTLD_DEFAULT ((void *) 0) #endif #include <limits.h> #undef QUARTZ_DEBUG #ifdef QUARTZ_DEBUG #define ND(_x) fprintf _x #else #define ND(_x) do {} while(0) #endif #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) /** * SECTION:cairo-quartz * @Title: Quartz Surfaces * @Short_Description: Rendering to Quartz surfaces * @See_Also: #cairo_surface_t * * The Quartz surface is used to render cairo graphics targeting the * Apple OS X Quartz rendering system. **/ /** * CAIRO_HAS_QUARTZ_SURFACE: * * Defined if the Quartz surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.6 **/ #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 /* This method is private, but it exists. Its params are are exposed * as args to the NS* method, but not as CG. */ enum PrivateCGCompositeMode { kPrivateCGCompositeClear = 0, kPrivateCGCompositeCopy = 1, kPrivateCGCompositeSourceOver = 2, kPrivateCGCompositeSourceIn = 3, kPrivateCGCompositeSourceOut = 4, kPrivateCGCompositeSourceAtop = 5, kPrivateCGCompositeDestinationOver = 6, kPrivateCGCompositeDestinationIn = 7, kPrivateCGCompositeDestinationOut = 8, kPrivateCGCompositeDestinationAtop = 9, kPrivateCGCompositeXOR = 10, kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) }; typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); #endif /* Some of these are present in earlier versions of the OS than where * they are public; other are not public at all */ /* public since 10.5 */ static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; /* public since 10.6 */ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; /* not yet public */ static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* * Utility functions */ #ifdef QUARTZ_DEBUG static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); static void quartz_image_to_png (CGImageRef, char *dest); #endif static cairo_quartz_surface_t * _cairo_quartz_surface_create_internal (CGContextRef cgContext, cairo_content_t content, unsigned int width, unsigned int height); static cairo_bool_t _cairo_surface_is_quartz (const cairo_surface_t *surface); /* Load all extra symbols */ static void quartz_ensure_symbols (void) { if (likely (_cairo_quartz_symbol_lookup_done)) return; CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage"); CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath"); CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); _cairo_quartz_symbol_lookup_done = TRUE; } CGImageRef CairoQuartzCreateCGImage (cairo_format_t format, unsigned int width, unsigned int height, unsigned int stride, void *data, cairo_bool_t interpolate, CGColorSpaceRef colorSpaceOverride, CGDataProviderReleaseDataCallback releaseCallback, void *releaseInfo) { CGImageRef image = NULL; CGDataProviderRef dataProvider = NULL; CGColorSpaceRef colorSpace = colorSpaceOverride; CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host; int bitsPerComponent, bitsPerPixel; switch (format) { case CAIRO_FORMAT_ARGB32: if (colorSpace == NULL) colorSpace = CGColorSpaceCreateDeviceRGB (); bitinfo |= kCGImageAlphaPremultipliedFirst; bitsPerComponent = 8; bitsPerPixel = 32; break; case CAIRO_FORMAT_RGB24: if (colorSpace == NULL) colorSpace = CGColorSpaceCreateDeviceRGB (); bitinfo |= kCGImageAlphaNoneSkipFirst; bitsPerComponent = 8; bitsPerPixel = 32; break; case CAIRO_FORMAT_A8: bitsPerComponent = 8; bitsPerPixel = 8; break; case CAIRO_FORMAT_A1: #ifdef WORDS_BIGENDIAN bitsPerComponent = 1; bitsPerPixel = 1; break; #endif case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_INVALID: default: return NULL; } dataProvider = CGDataProviderCreateWithData (releaseInfo, data, height * stride, releaseCallback); if (unlikely (!dataProvider)) { // manually release if (releaseCallback) releaseCallback (releaseInfo, data, height * stride); goto FINISH; } if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) { cairo_quartz_float_t decode[] = {1.0, 0.0}; image = CGImageMaskCreate (width, height, bitsPerComponent, bitsPerPixel, stride, dataProvider, decode, interpolate); } else image = CGImageCreate (width, height, bitsPerComponent, bitsPerPixel, stride, colorSpace, bitinfo, dataProvider, NULL, interpolate, kCGRenderingIntentDefault); FINISH: CGDataProviderRelease (dataProvider); if (colorSpace != colorSpaceOverride) CGColorSpaceRelease (colorSpace); return image; } static inline cairo_bool_t _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) { if (unlikely (cgc == NULL)) return FALSE; if (likely (CGContextGetTypePtr)) { /* 4 is the type value of a bitmap context */ return CGContextGetTypePtr (cgc) == 4; } /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */ return CGBitmapContextGetBitsPerPixel (cgc) != 0; } /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */ #define CG_MAX_HEIGHT SHRT_MAX #define CG_MAX_WIDTH USHRT_MAX /* is the desired size of the surface within bounds? */ cairo_bool_t _cairo_quartz_verify_surface_size (int width, int height) { /* hmmm, allow width, height == 0 ? */ if (width < 0 || height < 0) return FALSE; if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) return FALSE; return TRUE; } /* * Cairo path -> Quartz path conversion helpers */ /* cairo path -> execute in context */ static cairo_status_t _cairo_path_to_quartz_context_move_to (void *closure, const cairo_point_t *point) { //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y))); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); CGContextMoveToPoint (closure, x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_quartz_context_line_to (void *closure, const cairo_point_t *point) { //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y))); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); CGContextAddLineToPoint (closure, x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_quartz_context_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n", // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y), // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y))); double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); double y1 = _cairo_fixed_to_double (p1->y); double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_quartz_context_close_path (void *closure) { //ND ((stderr, "closepath\n")); CGContextClosePath (closure); return CAIRO_STATUS_SUCCESS; } static void _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path, CGContextRef closure) { cairo_status_t status; CGContextBeginPath (closure); status = _cairo_path_fixed_interpret (path, _cairo_path_to_quartz_context_move_to, _cairo_path_to_quartz_context_line_to, _cairo_path_to_quartz_context_curve_to, _cairo_path_to_quartz_context_close_path, closure); assert (status == CAIRO_STATUS_SUCCESS); } /* * Misc helpers/callbacks */ #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 static PrivateCGCompositeMode _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: return kPrivateCGCompositeClear; case CAIRO_OPERATOR_SOURCE: return kPrivateCGCompositeCopy; case CAIRO_OPERATOR_OVER: return kPrivateCGCompositeSourceOver; case CAIRO_OPERATOR_IN: return kPrivateCGCompositeSourceIn; case CAIRO_OPERATOR_OUT: return kPrivateCGCompositeSourceOut; case CAIRO_OPERATOR_ATOP: return kPrivateCGCompositeSourceAtop; case CAIRO_OPERATOR_DEST_OVER: return kPrivateCGCompositeDestinationOver; case CAIRO_OPERATOR_DEST_IN: return kPrivateCGCompositeDestinationIn; case CAIRO_OPERATOR_DEST_OUT: return kPrivateCGCompositeDestinationOut; case CAIRO_OPERATOR_DEST_ATOP: return kPrivateCGCompositeDestinationAtop; case CAIRO_OPERATOR_XOR: return kPrivateCGCompositeXOR; case CAIRO_OPERATOR_ADD: return kPrivateCGCompositePlusLighter; case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: default: ASSERT_NOT_REACHED; } } #endif static CGBlendMode _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_MULTIPLY: return kCGBlendModeMultiply; case CAIRO_OPERATOR_SCREEN: return kCGBlendModeScreen; case CAIRO_OPERATOR_OVERLAY: return kCGBlendModeOverlay; case CAIRO_OPERATOR_DARKEN: return kCGBlendModeDarken; case CAIRO_OPERATOR_LIGHTEN: return kCGBlendModeLighten; case CAIRO_OPERATOR_COLOR_DODGE: return kCGBlendModeColorDodge; case CAIRO_OPERATOR_COLOR_BURN: return kCGBlendModeColorBurn; case CAIRO_OPERATOR_HARD_LIGHT: return kCGBlendModeHardLight; case CAIRO_OPERATOR_SOFT_LIGHT: return kCGBlendModeSoftLight; case CAIRO_OPERATOR_DIFFERENCE: return kCGBlendModeDifference; case CAIRO_OPERATOR_EXCLUSION: return kCGBlendModeExclusion; case CAIRO_OPERATOR_HSL_HUE: return kCGBlendModeHue; case CAIRO_OPERATOR_HSL_SATURATION: return kCGBlendModeSaturation; case CAIRO_OPERATOR_HSL_COLOR: return kCGBlendModeColor; case CAIRO_OPERATOR_HSL_LUMINOSITY: return kCGBlendModeLuminosity; #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 case CAIRO_OPERATOR_CLEAR: return kCGBlendModeClear; case CAIRO_OPERATOR_SOURCE: return kCGBlendModeCopy; case CAIRO_OPERATOR_OVER: return kCGBlendModeNormal; case CAIRO_OPERATOR_IN: return kCGBlendModeSourceIn; case CAIRO_OPERATOR_OUT: return kCGBlendModeSourceOut; case CAIRO_OPERATOR_ATOP: return kCGBlendModeSourceAtop; case CAIRO_OPERATOR_DEST_OVER: return kCGBlendModeDestinationOver; case CAIRO_OPERATOR_DEST_IN: return kCGBlendModeDestinationIn; case CAIRO_OPERATOR_DEST_OUT: return kCGBlendModeDestinationOut; case CAIRO_OPERATOR_DEST_ATOP: return kCGBlendModeDestinationAtop; case CAIRO_OPERATOR_XOR: return kCGBlendModeXOR; case CAIRO_OPERATOR_ADD: return kCGBlendModePlusLighter; #else case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_OUT: case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: #endif case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_SATURATE: default: ASSERT_NOT_REACHED; } } static cairo_int_status_t _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op) { CGBlendMode blendmode; assert (op != CAIRO_OPERATOR_DEST); /* Quartz doesn't support SATURATE at all. COLOR_DODGE and * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo * uses the definition from the Adobe Supplement. */ if (op == CAIRO_OPERATOR_SATURATE || op == CAIRO_OPERATOR_COLOR_DODGE || op == CAIRO_OPERATOR_COLOR_BURN) { return CAIRO_INT_STATUS_UNSUPPORTED; } #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 if (op <= CAIRO_OPERATOR_ADD) { PrivateCGCompositeMode compmode; compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op); CGContextSetCompositeOperation (context, compmode); return CAIRO_STATUS_SUCCESS; } #endif blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op); CGContextSetBlendMode (context, blendmode); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op) { ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op)); /* When the destination has no color components, we can avoid some * fallbacks, but we have to workaround operators which behave * differently in Quartz. */ if (surface->base.content == CAIRO_CONTENT_ALPHA) { assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */ if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_IN || op == CAIRO_OPERATOR_OUT || op == CAIRO_OPERATOR_DEST_IN || op == CAIRO_OPERATOR_DEST_ATOP || op == CAIRO_OPERATOR_XOR) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (op == CAIRO_OPERATOR_DEST_OVER) op = CAIRO_OPERATOR_OVER; else if (op == CAIRO_OPERATOR_SATURATE) op = CAIRO_OPERATOR_ADD; else if (op == CAIRO_OPERATOR_COLOR_DODGE) op = CAIRO_OPERATOR_OVER; else if (op == CAIRO_OPERATOR_COLOR_BURN) op = CAIRO_OPERATOR_OVER; } return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op); } static inline CGLineCap _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) { switch (ccap) { default: ASSERT_NOT_REACHED; case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; } } static inline CGLineJoin _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) { switch (cjoin) { default: ASSERT_NOT_REACHED; case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; } } static inline CGInterpolationQuality _cairo_quartz_filter_to_quartz (cairo_filter_t filter) { switch (filter) { case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_FAST: return kCGInterpolationNone; case CAIRO_FILTER_BEST: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: return kCGInterpolationDefault; default: ASSERT_NOT_REACHED; return kCGInterpolationDefault; } } static inline void _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, CGAffineTransform *dst) { dst->a = src->xx; dst->b = src->yx; dst->c = src->xy; dst->d = src->yy; dst->tx = src->x0; dst->ty = src->y0; } /* * Source -> Quartz setup and finish functions */ static void ComputeGradientValue (void *info, const cairo_quartz_float_t *in, cairo_quartz_float_t *out) { double fdist = *in; const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; unsigned int i; /* Put fdist back in the 0.0..1.0 range if we're doing * REPEAT/REFLECT */ if (grad->base.extend == CAIRO_EXTEND_REPEAT) { fdist = fdist - floor (fdist); } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) { fdist = fmod (fabs (fdist), 2.0); if (fdist > 1.0) fdist = 2.0 - fdist; } for (i = 0; i < grad->n_stops; i++) if (grad->stops[i].offset > fdist) break; if (i == 0 || i == grad->n_stops) { if (i == grad->n_stops) --i; out[0] = grad->stops[i].color.red; out[1] = grad->stops[i].color.green; out[2] = grad->stops[i].color.blue; out[3] = grad->stops[i].color.alpha; } else { cairo_quartz_float_t ax = grad->stops[i-1].offset; cairo_quartz_float_t bx = grad->stops[i].offset - ax; cairo_quartz_float_t bp = (fdist - ax)/bx; cairo_quartz_float_t ap = 1.0 - bp; out[0] = grad->stops[i-1].color.red * ap + grad->stops[i].color.red * bp; out[1] = grad->stops[i-1].color.green * ap + grad->stops[i].color.green * bp; out[2] = grad->stops[i-1].color.blue * ap + grad->stops[i].color.blue * bp; out[3] = grad->stops[i-1].color.alpha * ap + grad->stops[i].color.alpha * bp; } } static const cairo_quartz_float_t gradient_output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f }; static const CGFunctionCallbacks gradient_callbacks = { 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy }; /* Quartz computes a small number of samples of the gradient color * function. On MacOS X 10.5 it apparently computes only 1024 * samples. */ #define MAX_GRADIENT_RANGE 1024 static CGFunctionRef CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents, cairo_circle_double_t *start, cairo_circle_double_t *end) { cairo_pattern_t *pat; cairo_quartz_float_t input_value_range[2]; if (gradient->base.extend != CAIRO_EXTEND_NONE) { double bounds_x1, bounds_x2, bounds_y1, bounds_y2; double t[2], tolerance; tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix)); tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1); bounds_x1 = extents->x; bounds_y1 = extents->y; bounds_x2 = extents->x + extents->width; bounds_y2 = extents->y + extents->height; _cairo_matrix_transform_bounding_box (&gradient->base.matrix, &bounds_x1, &bounds_y1, &bounds_x2, &bounds_y2, NULL); _cairo_gradient_pattern_box_to_parameter (gradient, bounds_x1, bounds_y1, bounds_x2, bounds_y2, tolerance, t); if (gradient->base.extend == CAIRO_EXTEND_PAD) { t[0] = MAX (t[0], -0.5); t[1] = MIN (t[1], 1.5); } else if (t[1] - t[0] > MAX_GRADIENT_RANGE) return NULL; /* set the input range for the function -- the function knows how to map values outside of 0.0 .. 1.0 to the correct color */ input_value_range[0] = t[0]; input_value_range[1] = t[1]; } else { input_value_range[0] = 0; input_value_range[1] = 1; } _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start); _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end); if (_cairo_pattern_create_copy (&pat, &gradient->base)) return NULL; return CGFunctionCreate (pat, 1, input_value_range, 4, gradient_output_value_ranges, &gradient_callbacks); } /* Obtain a CGImageRef from a #cairo_surface_t * */ typedef struct { cairo_surface_t *surface; cairo_image_surface_t *image_out; void *image_extra; } quartz_source_image_t; static void DataProviderReleaseCallback (void *info, const void *data, size_t size) { quartz_source_image_t *source_img = info; _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra); free (source_img); } static cairo_status_t _cairo_surface_to_cgimage (cairo_surface_t *source, cairo_rectangle_int_t *extents, cairo_format_t format, cairo_matrix_t *matrix, const cairo_clip_t *clip, CGImageRef *image_out) { cairo_status_t status; quartz_source_image_t *source_img; cairo_image_surface_t *image_surface; if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source; *image_out = CGImageRetain (surface->image); return CAIRO_STATUS_SUCCESS; } if (_cairo_surface_is_quartz (source)) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; if (IS_EMPTY (surface)) { *image_out = NULL; return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { *image_out = CGBitmapContextCreateImage (surface->cgContext); if (*image_out) return CAIRO_STATUS_SUCCESS; } } source_img = malloc (sizeof (quartz_source_image_t)); if (unlikely (source_img == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); source_img->surface = source; if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { image_surface = (cairo_image_surface_t *) cairo_image_surface_create (format, extents->width, extents->height); if (unlikely (image_surface->base.status)) { status = image_surface->base.status; cairo_surface_destroy (&image_surface->base); free (source_img); return status; } status = _cairo_recording_surface_replay_with_clip (source, matrix, &image_surface->base, NULL); if (unlikely (status)) { cairo_surface_destroy (&image_surface->base); free (source_img); return status; } source_img->image_out = image_surface; source_img->image_extra = NULL; cairo_matrix_init_identity (matrix); } else { status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra); if (unlikely (status)) { free (source_img); return status; } } if (source_img->image_out->width == 0 || source_img->image_out->height == 0) { *image_out = NULL; DataProviderReleaseCallback (source_img, source_img->image_out->data, source_img->image_out->height * source_img->image_out->stride); } else { *image_out = CairoQuartzCreateCGImage (source_img->image_out->format, source_img->image_out->width, source_img->image_out->height, source_img->image_out->stride, source_img->image_out->data, TRUE, NULL, DataProviderReleaseCallback, source_img); /* TODO: differentiate memory error and unsupported surface type */ if (unlikely (*image_out == NULL)) status = CAIRO_INT_STATUS_UNSUPPORTED; } return status; } /* Generic #cairo_pattern_t -> CGPattern function */ typedef struct { CGImageRef image; CGRect imageBounds; cairo_bool_t do_reflect; } SurfacePatternDrawInfo; static void SurfacePatternDrawFunc (void *ainfo, CGContextRef context) { SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; CGContextTranslateCTM (context, 0, info->imageBounds.size.height); CGContextScaleCTM (context, 1, -1); CGContextDrawImage (context, info->imageBounds, info->image); if (info->do_reflect) { /* draw 3 more copies of the image, flipped. * DrawImage draws the image according to the current Y-direction into the rectangle given * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left * of the base image position, and the Y axis is extending upwards. */ /* Make the y axis extend downwards, and draw a flipped image below */ CGContextScaleCTM (context, 1, -1); CGContextDrawImage (context, info->imageBounds, info->image); /* Shift over to the right, and flip vertically (translation is 2x, * since we'll be flipping and thus rendering the rectangle "backwards" */ CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0); CGContextScaleCTM (context, -1, 1); CGContextDrawImage (context, info->imageBounds, info->image); /* Then unflip the Y-axis again, and draw the image above the point. */ CGContextScaleCTM (context, 1, -1); CGContextDrawImage (context, info->imageBounds, info->image); } } static void SurfacePatternReleaseInfoFunc (void *ainfo) { SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; CGImageRelease (info->image); free (info); } static cairo_int_status_t _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest, const cairo_pattern_t *apattern, const cairo_clip_t *clip, CGPatternRef *cgpat) { cairo_surface_pattern_t *spattern; cairo_surface_t *pat_surf; cairo_rectangle_int_t extents; cairo_format_t format = _cairo_format_from_content (dest->base.content); CGImageRef image; CGRect pbounds; CGAffineTransform ptransform, stransform; CGPatternCallbacks cb = { 0, SurfacePatternDrawFunc, SurfacePatternReleaseInfoFunc }; SurfacePatternDrawInfo *info; cairo_quartz_float_t rw, rh; cairo_status_t status; cairo_bool_t is_bounded; cairo_matrix_t m; /* SURFACE is the only type we'll handle here */ assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE); spattern = (cairo_surface_pattern_t *) apattern; pat_surf = spattern->surface; if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) { is_bounded = _cairo_surface_get_extents (pat_surf, &extents); assert (is_bounded); } else _cairo_surface_get_extents (&dest->base, &extents); m = spattern->base.matrix; status = _cairo_surface_to_cgimage (pat_surf, &extents, format, &m, clip, &image); if (unlikely (status)) return status; info = malloc (sizeof (SurfacePatternDrawInfo)); if (unlikely (!info)) return CAIRO_STATUS_NO_MEMORY; /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure * that the data will stick around for this image when the printer gets to it. * Otherwise, the underlying data store may disappear from under us! * * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces, * since the Quartz surfaces have a higher chance of sticking around. If the * source is a quartz image surface, then it's set up to retain a ref to the * image surface that it's backed by. */ info->image = image; info->imageBounds = CGRectMake (0, 0, extents.width, extents.height); info->do_reflect = FALSE; pbounds.origin.x = 0; pbounds.origin.y = 0; if (spattern->base.extend == CAIRO_EXTEND_REFLECT) { pbounds.size.width = 2.0 * extents.width; pbounds.size.height = 2.0 * extents.height; info->do_reflect = TRUE; } else { pbounds.size.width = extents.width; pbounds.size.height = extents.height; } rw = pbounds.size.width; rh = pbounds.size.height; cairo_matrix_invert (&m); _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform); /* The pattern matrix is relative to the bottom left, again; the * incoming cairo pattern matrix is relative to the upper left. * So we take the pattern matrix and the original context matrix, * which gives us the correct base translation/y flip. */ ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM); #ifdef QUARTZ_DEBUG ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); CGAffineTransform xform = CGContextGetCTM (dest->cgContext); ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d)); #endif *cgpat = CGPatternCreate (info, pbounds, ptransform, rw, rh, kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ TRUE, &cb); return CAIRO_STATUS_SUCCESS; } /* State used during a drawing operation. */ typedef struct { /* The destination of the mask */ CGContextRef cgMaskContext; /* The destination of the drawing of the source */ CGContextRef cgDrawContext; /* The filter to be used when drawing the source */ CGInterpolationQuality filter; /* Action type */ cairo_quartz_action_t action; /* Destination rect */ CGRect rect; /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */ CGAffineTransform transform; /* Used with DO_IMAGE and DO_TILED_IMAGE */ CGImageRef image; /* Used with DO_SHADING */ CGShadingRef shading; /* Temporary destination for unbounded operations */ CGLayerRef layer; CGRect clipRect; } cairo_quartz_drawing_state_t; /* Quartz does not support repeating radients. We handle repeating gradients by manually extending the gradient and repeating color stops. We need to minimize the number of repetitions since Quartz seems to sample our color function across the entire range, even if part of that range is not needed for the visible area of the gradient, and it samples with some fixed resolution, so if the gradient range is too large it samples with very low resolution and the gradient is very coarse. _cairo_quartz_create_gradient_function computes the number of repetitions needed based on the extents. */ static cairo_int_status_t _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents) { cairo_matrix_t mat; cairo_circle_double_t start, end; CGFunctionRef gradFunc; CGColorSpaceRef rgb; bool extend = gradient->base.extend != CAIRO_EXTEND_NONE; assert (gradient->n_stops > 0); mat = gradient->base.matrix; cairo_matrix_invert (&mat); _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); gradFunc = CairoQuartzCreateGradientFunction (gradient, extents, &start, &end); if (unlikely (gradFunc == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; rgb = CGColorSpaceCreateDeviceRGB (); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { state->shading = CGShadingCreateAxial (rgb, CGPointMake (start.center.x, start.center.y), CGPointMake (end.center.x, end.center.y), gradFunc, extend, extend); } else { state->shading = CGShadingCreateRadial (rgb, CGPointMake (start.center.x, start.center.y), MAX (start.radius, 0), CGPointMake (end.center.x, end.center.y), MAX (end.radius, 0), gradFunc, extend, extend); } CGColorSpaceRelease (rgb); CGFunctionRelease (gradFunc); state->action = DO_SHADING; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, cairo_composite_rectangles_t *composite) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface; cairo_operator_t op = composite->op; const cairo_pattern_t *source = &composite->source_pattern.base; const cairo_clip_t *clip = composite->clip; cairo_bool_t needs_temp; cairo_status_t status; cairo_format_t format = _cairo_format_from_content (composite->surface->content); state->layer = NULL; state->image = NULL; state->shading = NULL; state->cgDrawContext = NULL; state->cgMaskContext = NULL; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; status = _cairo_quartz_surface_set_cairo_operator (surface, op); if (unlikely (status)) return status; /* Save before we change the pattern, colorspace, etc. so that * we can restore and make sure that quartz releases our * pattern (which may be stack allocated) */ CGContextSaveGState (surface->cgContext); state->clipRect = CGContextGetClipBoundingBox (surface->cgContext); state->clipRect = CGRectIntegral (state->clipRect); state->rect = state->clipRect; state->cgMaskContext = surface->cgContext; state->cgDrawContext = state->cgMaskContext; state->filter = _cairo_quartz_filter_to_quartz (source->filter); if (op == CAIRO_OPERATOR_CLEAR) { CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); state->action = DO_DIRECT; return CAIRO_STATUS_SUCCESS; } /* * To implement mask unbounded operations Quartz needs a temporary * surface which will be composited entirely (ignoring the mask). * To implement source unbounded operations Quartz needs a * temporary surface which allows extending the source to a size * covering the whole mask, but there are some optimization * opportunities: * * - CLEAR completely ignores the source, thus we can just use a * solid color fill. * * - SOURCE can be implemented by drawing the source and clearing * outside of the source as long as the two regions have no * intersection. This happens when the source is a pixel-aligned * rectangle. If the source is at least as big as the * intersection between the clip rectangle and the mask * rectangle, no clear operation is needed. */ needs_temp = ! _cairo_operator_bounded_by_mask (op); if (needs_temp) { state->layer = CGLayerCreateWithContext (surface->cgContext, state->clipRect.size, NULL); state->cgDrawContext = CGLayerGetContext (state->layer); state->cgMaskContext = state->cgDrawContext; CGContextTranslateCTM (state->cgDrawContext, -state->clipRect.origin.x, -state->clipRect.origin.y); } if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; CGContextSetRGBStrokeColor (state->cgDrawContext, solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); CGContextSetRGBFillColor (state->cgDrawContext, solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); state->action = DO_DIRECT; return CAIRO_STATUS_SUCCESS; } if (source->type == CAIRO_PATTERN_TYPE_LINEAR || source->type == CAIRO_PATTERN_TYPE_RADIAL) { const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source; cairo_rectangle_int_t extents; extents = surface->virtual_extents; extents.x -= surface->base.device_transform.x0; extents.y -= surface->base.device_transform.y0; _cairo_rectangle_union (&extents, &surface->extents); return _cairo_quartz_setup_gradient_source (state, gpat, &extents); } if (source->type == CAIRO_PATTERN_TYPE_SURFACE && (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) { const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; cairo_surface_t *pat_surf = spat->surface; CGImageRef img; cairo_matrix_t m = spat->base.matrix; cairo_rectangle_int_t extents; CGAffineTransform xform; CGRect srcRect; cairo_fixed_t fw, fh; cairo_bool_t is_bounded; _cairo_surface_get_extents (composite->surface, &extents); status = _cairo_surface_to_cgimage (pat_surf, &extents, format, &m, clip, &img); if (unlikely (status)) return status; state->image = img; if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) { m.x0 = -ceil (m.x0 - 0.5); m.y0 = -ceil (m.y0 - 0.5); } else { cairo_matrix_invert (&m); } _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) { is_bounded = _cairo_surface_get_extents (pat_surf, &extents); assert (is_bounded); } srcRect = CGRectMake (0, 0, extents.width, extents.height); if (source->extend == CAIRO_EXTEND_NONE) { int x, y; if (op == CAIRO_OPERATOR_SOURCE && (pat_surf->content == CAIRO_CONTENT_ALPHA || ! _cairo_matrix_is_integer_translation (&m, &x, &y))) { state->layer = CGLayerCreateWithContext (surface->cgContext, state->clipRect.size, NULL); state->cgDrawContext = CGLayerGetContext (state->layer); CGContextTranslateCTM (state->cgDrawContext, -state->clipRect.origin.x, -state->clipRect.origin.y); } CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); state->rect = srcRect; state->action = DO_IMAGE; return CAIRO_STATUS_SUCCESS; } CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); /* Quartz seems to tile images at pixel-aligned regions only -- this * leads to seams if the image doesn't end up scaling to fill the * space exactly. The CGPattern tiling approach doesn't have this * problem. Check if we're going to fill up the space (within some * epsilon), and if not, fall back to the CGPattern type. */ xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext), state->transform); srcRect = CGRectApplyAffineTransform (srcRect, xform); fw = _cairo_fixed_from_double (srcRect.size.width); fh = _cairo_fixed_from_double (srcRect.size.height); if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON && (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON) { /* We're good to use DrawTiledImage, but ensure that * the math works out */ srcRect.size.width = round (srcRect.size.width); srcRect.size.height = round (srcRect.size.height); xform = CGAffineTransformInvert (xform); srcRect = CGRectApplyAffineTransform (srcRect, xform); state->rect = srcRect; state->action = DO_TILED_IMAGE; return CAIRO_STATUS_SUCCESS; } /* Fall through to generic SURFACE case */ } if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_quartz_float_t patternAlpha = 1.0f; CGColorSpaceRef patternSpace; CGPatternRef pattern = NULL; cairo_int_status_t status; status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern); if (unlikely (status)) return status; patternSpace = CGColorSpaceCreatePattern (NULL); CGContextSetFillColorSpace (state->cgDrawContext, patternSpace); CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha); CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace); CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha); CGColorSpaceRelease (patternSpace); /* Quartz likes to munge the pattern phase (as yet unexplained * why); force it to 0,0 as we've already baked in the correct * pattern translation into the pattern matrix */ CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0)); CGPatternRelease (pattern); state->action = DO_DIRECT; return CAIRO_STATUS_SUCCESS; } return CAIRO_INT_STATUS_UNSUPPORTED; } static void _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state, cairo_composite_rectangles_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface; if (state->layer) { CGContextDrawLayerInRect (surface->cgContext, state->clipRect, state->layer); CGLayerRelease (state->layer); } if (state->cgMaskContext) CGContextRestoreGState (surface->cgContext); if (state->image) CGImageRelease (state->image); if (state->shading) CGShadingRelease (state->shading); } static void _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state, cairo_operator_t op) { CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone); CGContextSetInterpolationQuality(state->cgDrawContext, state->filter); if (state->action == DO_DIRECT) { CGContextFillRect (state->cgDrawContext, state->rect); return; } CGContextConcatCTM (state->cgDrawContext, state->transform); if (state->action == DO_SHADING) { CGContextDrawShading (state->cgDrawContext, state->shading); return; } CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); CGContextScaleCTM (state->cgDrawContext, 1, -1); if (state->action == DO_IMAGE) { CGContextDrawImage (state->cgDrawContext, state->rect, state->image); if (op == CAIRO_OPERATOR_SOURCE && state->cgDrawContext == state->cgMaskContext) { CGContextBeginPath (state->cgDrawContext); CGContextAddRect (state->cgDrawContext, state->rect); CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); CGContextScaleCTM (state->cgDrawContext, 1, -1); CGContextConcatCTM (state->cgDrawContext, CGAffineTransformInvert (state->transform)); CGContextAddRect (state->cgDrawContext, state->clipRect); CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0); CGContextEOFillPath (state->cgDrawContext); } } else { CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image); } } static cairo_image_surface_t * _cairo_quartz_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; unsigned int stride, bitinfo, bpp, color_comps; CGColorSpaceRef colorspace; void *imageData; cairo_format_t format; if (surface->imageSurfaceEquiv) return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents); if (IS_EMPTY (surface)) return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); // let's hope they don't add YUV under us colorspace = CGBitmapContextGetColorSpace (surface->cgContext); color_comps = CGColorSpaceGetNumberOfComponents (colorspace); /* XXX TODO: We can handle many more data formats by * converting to pixman_format_t */ if (bpp == 32 && color_comps == 3 && (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) { format = CAIRO_FORMAT_ARGB32; } else if (bpp == 32 && color_comps == 3 && (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) { format = CAIRO_FORMAT_RGB24; } else if (bpp == 8 && color_comps == 1) { format = CAIRO_FORMAT_A1; } else { return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } imageData = CGBitmapContextGetData (surface->cgContext); stride = CGBitmapContextGetBytesPerRow (surface->cgContext); return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData, format, extents->width, extents->height, stride); } static cairo_int_status_t _cairo_quartz_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; if (surface->imageSurfaceEquiv) return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image); cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return CAIRO_STATUS_SUCCESS; } /* * Cairo surface backend implementations */ static cairo_status_t _cairo_quartz_surface_finish (void *abstract_surface) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS; /* Restore our saved gstate that we use to reset clipping */ CGContextRestoreGState (surface->cgContext); _cairo_surface_clipper_reset (&surface->clipper); CGContextRelease (surface->cgContext); surface->cgContext = NULL; if (surface->imageSurfaceEquiv) { cairo_surface_destroy (surface->imageSurfaceEquiv); surface->imageSurfaceEquiv = NULL; } free (surface->imageData); surface->imageData = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_quartz_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface)); *image_extra = NULL; *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents); if (unlikely (cairo_surface_status(&(*image_out)->base))) { cairo_surface_destroy (&(*image_out)->base); *image_out = NULL; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } return CAIRO_STATUS_SUCCESS; } static void _cairo_quartz_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { _cairo_quartz_surface_unmap_image (abstract_surface, image); } static cairo_surface_t * _cairo_quartz_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_quartz_surface_t *surface, *similar_quartz; cairo_surface_t *similar; cairo_format_t format; if (content == CAIRO_CONTENT_COLOR_ALPHA) format = CAIRO_FORMAT_ARGB32; else if (content == CAIRO_CONTENT_COLOR) format = CAIRO_FORMAT_RGB24; else if (content == CAIRO_CONTENT_ALPHA) format = CAIRO_FORMAT_A8; else return NULL; // verify width and height of surface if (!_cairo_quartz_verify_surface_size (width, height)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } similar = cairo_quartz_surface_create (format, width, height); if (unlikely (similar->status)) return similar; surface = (cairo_quartz_surface_t *) abstract_surface; similar_quartz = (cairo_quartz_surface_t *) similar; similar_quartz->virtual_extents = surface->virtual_extents; return similar; } static cairo_bool_t _cairo_quartz_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; *extents = surface->extents; return TRUE; } static cairo_int_status_t _cairo_quartz_cg_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_quartz_drawing_state_t state; cairo_int_status_t rv; ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", extents->surface, extents->op, extents->source_pattern.base.type)); rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; _cairo_quartz_draw_source (&state, extents->op); BAIL: _cairo_quartz_teardown_state (&state, extents); ND ((stderr, "-- paint\n")); return rv; } static cairo_int_status_t _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents, cairo_surface_t *mask_surf, const cairo_matrix_t *mask_mat, CGInterpolationQuality filter) { CGRect rect; CGImageRef img; cairo_status_t status; CGAffineTransform mask_matrix; cairo_quartz_drawing_state_t state; cairo_format_t format = _cairo_format_from_content (extents->surface->content); cairo_rectangle_int_t dest_extents; cairo_matrix_t m = *mask_mat; _cairo_surface_get_extents (extents->surface, &dest_extents); status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format, &m, extents->clip, &img); if (unlikely (status)) return status; status = _cairo_quartz_setup_state (&state, extents); if (unlikely (status)) goto BAIL; rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix); /* ClipToMask is essentially drawing an image, so we need to flip the CTM * to get the image to appear oriented the right way */ CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix)); CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height); CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); state.filter = filter; CGContextSetInterpolationQuality (state.cgMaskContext, filter); CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone); CGContextClipToMask (state.cgMaskContext, rect, img); CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height); CGContextConcatCTM (state.cgMaskContext, mask_matrix); _cairo_quartz_draw_source (&state, extents->op); BAIL: _cairo_quartz_teardown_state (&state, extents); CGImageRelease (img); return status; } static cairo_int_status_t _cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface, cairo_composite_rectangles_t *extents) { cairo_quartz_drawing_state_t state; double alpha = extents->mask_pattern.solid.color.alpha; cairo_status_t status; status = _cairo_quartz_setup_state (&state, extents); if (unlikely (status)) return status; CGContextSetAlpha (surface->cgContext, alpha); _cairo_quartz_draw_source (&state, extents->op); _cairo_quartz_teardown_state (&state, extents); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_quartz_cg_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface; const cairo_pattern_t *source = &extents->source_pattern.base; const cairo_pattern_t *mask = &extents->mask_pattern.base; cairo_surface_t *mask_surf; cairo_matrix_t matrix; cairo_status_t status; cairo_bool_t need_temp; CGInterpolationQuality filter; ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", extents->surface, extents->op, extents->source_pattern.base.type, extents->mask_pattern.base.type)); if (mask->type == CAIRO_PATTERN_TYPE_SOLID) return _cairo_quartz_cg_mask_with_solid (surface, extents); need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE || mask->extend != CAIRO_EXTEND_NONE); filter = _cairo_quartz_filter_to_quartz (source->filter); if (! need_temp) { mask_surf = extents->mask_pattern.surface.surface; /* When an opaque surface used as a mask in Quartz, its * luminosity is used as the alpha value, so we con only use * surfaces with alpha without creating a temporary mask. */ need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA); } if (! need_temp) { CGInterpolationQuality mask_filter; cairo_bool_t simple_transform; matrix = mask->matrix; mask_filter = _cairo_quartz_filter_to_quartz (mask->filter); if (mask_filter == kCGInterpolationNone) { simple_transform = _cairo_matrix_is_translation (&matrix); if (simple_transform) { matrix.x0 = ceil (matrix.x0 - 0.5); matrix.y0 = ceil (matrix.y0 - 0.5); } } else { simple_transform = _cairo_matrix_is_integer_translation (&matrix, NULL, NULL); } /* Quartz only allows one interpolation to be set for mask and * source, so we can skip the temp surface only if the source * filtering makes the mask look correct. */ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) need_temp = ! (simple_transform || filter == mask_filter); else filter = mask_filter; } if (need_temp) { /* Render the mask to a surface */ mask_surf = _cairo_quartz_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, surface->extents.width, surface->extents.height); status = mask_surf->status; if (unlikely (status)) goto BAIL; /* mask_surf is clear, so use OVER instead of SOURCE to avoid a * temporary layer or fallback to cairo-image. */ status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL); if (unlikely (status)) goto BAIL; cairo_matrix_init_identity (&matrix); } status = _cairo_quartz_cg_mask_with_surface (extents, mask_surf, &matrix, filter); BAIL: if (need_temp) cairo_surface_destroy (mask_surf); return status; } static cairo_int_status_t _cairo_quartz_cg_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", extents->surface, extents->op, extents->source_pattern.base.type)); rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE)); _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); if (state.action == DO_DIRECT) { assert (state.cgDrawContext == state.cgMaskContext); if (fill_rule == CAIRO_FILL_RULE_WINDING) CGContextFillPath (state.cgMaskContext); else CGContextEOFillPath (state.cgMaskContext); } else { if (fill_rule == CAIRO_FILL_RULE_WINDING) CGContextClip (state.cgMaskContext); else CGContextEOClip (state.cgMaskContext); _cairo_quartz_draw_source (&state, extents->op); } BAIL: _cairo_quartz_teardown_state (&state, extents); ND ((stderr, "-- fill\n")); return rv; } static cairo_int_status_t _cairo_quartz_cg_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; CGAffineTransform strokeTransform, invStrokeTransform; ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", extents->surface, extents->op, extents->source_pattern.base.type)); rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; // Turning antialiasing off used to cause misrendering with // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases. CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE)); CGContextSetLineWidth (state.cgMaskContext, style->line_width); CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit); if (style->dash && style->num_dashes) { cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)]; cairo_quartz_float_t *fdash = sdash; unsigned int max_dashes = style->num_dashes; unsigned int k; if (style->num_dashes%2) max_dashes *= 2; if (max_dashes > ARRAY_LENGTH (sdash)) fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t)); if (unlikely (fdash == NULL)) { rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } for (k = 0; k < max_dashes; k++) fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes]; CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes); if (fdash != sdash) free (fdash); } else CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0); _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); CGContextConcatCTM (state.cgMaskContext, strokeTransform); if (state.action == DO_DIRECT) { assert (state.cgDrawContext == state.cgMaskContext); CGContextStrokePath (state.cgMaskContext); } else { CGContextReplacePathWithStrokedPath (state.cgMaskContext); CGContextClip (state.cgMaskContext); _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform); CGContextConcatCTM (state.cgMaskContext, invStrokeTransform); _cairo_quartz_draw_source (&state, extents->op); } BAIL: _cairo_quartz_teardown_state (&state, extents); ND ((stderr, "-- stroke\n")); return rv; } #if CAIRO_HAS_QUARTZ_FONT static cairo_int_status_t _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { CGAffineTransform textTransform, invTextTransform; CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGGlyph *cg_glyphs = &glyphs_static[0]; CGSize *cg_advances = &cg_advances_static[0]; COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; cairo_quartz_float_t xprev, yprev; int i; CGFontRef cgfref = NULL; cairo_bool_t didForceFontSmoothing = FALSE; if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) return CAIRO_INT_STATUS_UNSUPPORTED; rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; if (state.action == DO_DIRECT) { assert (state.cgDrawContext == state.cgMaskContext); CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill); } else { CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip); } /* this doesn't addref */ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); CGContextSetFont (state.cgMaskContext, cgfref); CGContextSetFontSize (state.cgMaskContext, 1.0); switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_BEST: CGContextSetShouldAntialias (state.cgMaskContext, TRUE); CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); if (CGContextSetAllowsFontSmoothingPtr && !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) { didForceFontSmoothing = TRUE; CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE); } break; case CAIRO_ANTIALIAS_NONE: CGContextSetShouldAntialias (state.cgMaskContext, FALSE); break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_FAST: CGContextSetShouldAntialias (state.cgMaskContext, TRUE); CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE); break; case CAIRO_ANTIALIAS_DEFAULT: /* Don't do anything */ break; } if (num_glyphs > ARRAY_LENGTH (glyphs_static)) { cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize)); if (unlikely (cg_glyphs == NULL)) { rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } cg_advances = (CGSize*) (cg_glyphs + num_glyphs); } /* scale(1,-1) * scaled_font->scale */ textTransform = CGAffineTransformMake (scaled_font->scale.xx, scaled_font->scale.yx, -scaled_font->scale.xy, -scaled_font->scale.yy, 0.0, 0.0); /* scaled_font->scale_inverse * scale(1,-1) */ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx, -scaled_font->scale_inverse.yx, scaled_font->scale_inverse.xy, -scaled_font->scale_inverse.yy, 0.0, 0.0); CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0); CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity); /* Convert our glyph positions to glyph advances. We need n-1 advances, * since the advance at index 0 is applied after glyph 0. */ xprev = glyphs[0].x; yprev = glyphs[0].y; cg_glyphs[0] = glyphs[0].index; for (i = 1; i < num_glyphs; i++) { cairo_quartz_float_t xf = glyphs[i].x; cairo_quartz_float_t yf = glyphs[i].y; cg_glyphs[i] = glyphs[i].index; cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform); xprev = xf; yprev = yf; } /* Translate to the first glyph's position before drawing */ CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); CGContextConcatCTM (state.cgMaskContext, textTransform); CGContextShowGlyphsWithAdvances (state.cgMaskContext, cg_glyphs, cg_advances, num_glyphs); CGContextConcatCTM (state.cgMaskContext, invTextTransform); CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); if (state.action != DO_DIRECT) _cairo_quartz_draw_source (&state, extents->op); BAIL: if (didForceFontSmoothing) CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); _cairo_quartz_teardown_state (&state, extents); if (cg_glyphs != glyphs_static) free (cg_glyphs); return rv; } #endif /* CAIRO_HAS_QUARTZ_FONT */ static const cairo_compositor_t _cairo_quartz_cg_compositor = { &_cairo_fallback_compositor, _cairo_quartz_cg_paint, _cairo_quartz_cg_mask, _cairo_quartz_cg_stroke, _cairo_quartz_cg_fill, #if CAIRO_HAS_QUARTZ_FONT _cairo_quartz_cg_glyphs, #else NULL, #endif }; static cairo_int_status_t _cairo_quartz_surface_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { return _cairo_compositor_paint (&_cairo_quartz_cg_compositor, surface, op, source, clip); } static cairo_int_status_t _cairo_quartz_surface_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { return _cairo_compositor_mask (&_cairo_quartz_cg_compositor, surface, op, source, mask, clip); } static cairo_int_status_t _cairo_quartz_surface_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_fill (&_cairo_quartz_cg_compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_quartz_surface_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor, surface, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_quartz_surface_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); } static cairo_status_t _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_quartz_surface_t *surface = cairo_container_of (clipper, cairo_quartz_surface_t, clipper); ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path)); if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS; if (path == NULL) { /* If we're being asked to reset the clip, we can only do it * by restoring the gstate to our previous saved one, and * saving it again. * * Note that this assumes that ALL quartz surface creation * functions will do a SaveGState first; we do this in create_internal. */ CGContextRestoreGState (surface->cgContext); CGContextSaveGState (surface->cgContext); } else { CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext); if (fill_rule == CAIRO_FILL_RULE_WINDING) CGContextClip (surface->cgContext); else CGContextEOClip (surface->cgContext); } ND ((stderr, "-- intersect_clip_path\n")); return CAIRO_STATUS_SUCCESS; } // XXXtodo implement show_page; need to figure out how to handle begin/end static const struct _cairo_surface_backend cairo_quartz_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ, _cairo_quartz_surface_finish, _cairo_default_context_create, _cairo_quartz_surface_create_similar, NULL, /* similar image */ _cairo_quartz_surface_map_to_image, _cairo_quartz_surface_unmap_image, _cairo_surface_default_source, _cairo_quartz_surface_acquire_source_image, _cairo_quartz_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_quartz_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_quartz_surface_paint, _cairo_quartz_surface_mask, _cairo_quartz_surface_stroke, _cairo_quartz_surface_fill, NULL, /* fill-stroke */ _cairo_quartz_surface_glyphs, }; cairo_quartz_surface_t * _cairo_quartz_surface_create_internal (CGContextRef cgContext, cairo_content_t content, unsigned int width, unsigned int height) { cairo_quartz_surface_t *surface; quartz_ensure_symbols (); /* Init the base surface */ surface = malloc (sizeof (cairo_quartz_surface_t)); if (unlikely (surface == NULL)) return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (surface, 0, sizeof (cairo_quartz_surface_t)); _cairo_surface_init (&surface->base, &cairo_quartz_surface_backend, NULL, /* device */ content); _cairo_surface_clipper_init (&surface->clipper, _cairo_quartz_surface_clipper_intersect_clip_path); /* Save our extents */ surface->extents.x = surface->extents.y = 0; surface->extents.width = width; surface->extents.height = height; surface->virtual_extents = surface->extents; if (IS_EMPTY (surface)) { surface->cgContext = NULL; surface->cgContextBaseCTM = CGAffineTransformIdentity; surface->imageData = NULL; surface->base.is_clear = TRUE; return surface; } /* Save so we can always get back to a known-good CGContext -- this is * required for proper behaviour of intersect_clip_path(NULL) */ CGContextSaveGState (cgContext); surface->cgContext = cgContext; surface->cgContextBaseCTM = CGContextGetCTM (cgContext); surface->imageData = NULL; surface->imageSurfaceEquiv = NULL; return surface; } /** * cairo_quartz_surface_create_for_cg_context: * @cgContext: the existing CGContext for which to create the surface * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a Quartz surface that wraps the given CGContext. The * CGContext is assumed to be in the standard Cairo coordinate space * (that is, with the origin at the upper left and the Y axis * increasing downward). If the CGContext is in the Quartz coordinate * space (with the origin at the bottom left), then it should be * flipped before this function is called. The flip can be accomplished * using a translate and a scale; for example: * * <informalexample><programlisting> * CGContextTranslateCTM (cgContext, 0.0, height); * CGContextScaleCTM (cgContext, 1.0, -1.0); * </programlisting></informalexample> * * All Cairo operations are implemented in terms of Quartz operations, * as long as Quartz-compatible elements are used (such as Quartz fonts). * * Return value: the newly created Cairo surface. * * Since: 1.6 **/ cairo_surface_t * cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, unsigned int width, unsigned int height) { cairo_quartz_surface_t *surf; surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, width, height); if (likely (!surf->base.status)) CGContextRetain (cgContext); return &surf->base; } /** * cairo_quartz_surface_create: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a Quartz surface backed by a CGBitmap. The surface is * created using the Device RGB (or Device Gray, for A8) color space. * All Cairo operations, including those that require software * rendering, will succeed on this surface. * * Return value: the newly created surface. * * Since: 1.6 **/ cairo_surface_t * cairo_quartz_surface_create (cairo_format_t format, unsigned int width, unsigned int height) { cairo_quartz_surface_t *surf; CGContextRef cgc; CGColorSpaceRef cgColorspace; CGBitmapInfo bitinfo; void *imageData; int stride; int bitsPerComponent; if (!_cairo_quartz_verify_surface_size (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (width == 0 || height == 0) { return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), width, height)->base; } if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) { cgColorspace = CGColorSpaceCreateDeviceRGB (); bitinfo = kCGBitmapByteOrder32Host; if (format == CAIRO_FORMAT_ARGB32) bitinfo |= kCGImageAlphaPremultipliedFirst; else bitinfo |= kCGImageAlphaNoneSkipFirst; bitsPerComponent = 8; stride = width * 4; } else if (format == CAIRO_FORMAT_A8) { cgColorspace = NULL; stride = width; bitinfo = kCGImageAlphaOnly; bitsPerComponent = 8; } else if (format == CAIRO_FORMAT_A1) { /* I don't think we can usefully support this, as defined by * cairo_format_t -- these are 1-bit pixels stored in 32-bit * quantities. */ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } else { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } /* The Apple docs say that for best performance, the stride and the data * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, * so we don't have to anything special on allocation. */ stride = (stride + 15) & ~15; imageData = _cairo_malloc_ab (height, stride); if (unlikely (!imageData)) { CGColorSpaceRelease (cgColorspace); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* zero the memory to match the image surface behaviour */ memset (imageData, 0, height * stride); cgc = CGBitmapContextCreate (imageData, width, height, bitsPerComponent, stride, cgColorspace, bitinfo); CGColorSpaceRelease (cgColorspace); if (!cgc) { free (imageData); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* flip the Y axis */ CGContextTranslateCTM (cgc, 0.0, height); CGContextScaleCTM (cgc, 1.0, -1.0); surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), width, height); if (surf->base.status) { CGContextRelease (cgc); free (imageData); // create_internal will have set an error return &surf->base; } surf->base.is_clear = TRUE; surf->imageData = imageData; surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); return &surf->base; } /** * cairo_quartz_surface_get_cg_context: * @surface: the Cairo Quartz surface * * Returns the CGContextRef that the given Quartz surface is backed * by. * * A call to cairo_surface_flush() is required before using the * CGContextRef to ensure that all pending drawing operations are * finished and to restore any temporary modification cairo has made * to its state. A call to cairo_surface_mark_dirty() is required * after the state or the content of the CGContextRef has been * modified. * * Return value: the CGContextRef for the given surface. * * Since: 1.6 **/ CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) { if (surface && _cairo_surface_is_quartz (surface)) { cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; return quartz->cgContext; } else return NULL; } static cairo_bool_t _cairo_surface_is_quartz (const cairo_surface_t *surface) { return surface->backend == &cairo_quartz_surface_backend; } /* Debug stuff */ #ifdef QUARTZ_DEBUG #include <Movies.h> void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest) { Handle dataRef = NULL; OSType dataRefType; CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII); GraphicsExportComponent grex = 0; unsigned long sizeWritten; ComponentResult result; // create the data reference result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType); if (NULL != dataRef && noErr == result) { // get the PNG exporter result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG, &grex); if (grex) { // tell the exporter where to find its source image result = GraphicsExportSetInputCGImage (grex, inImageRef); if (noErr == result) { // tell the exporter where to save the exporter image result = GraphicsExportSetOutputDataReference (grex, dataRef, dataRefType); if (noErr == result) { // write the PNG file result = GraphicsExportDoExport (grex, &sizeWritten); } } // remember to close the component CloseComponent (grex); } // remember to dispose of the data reference handle DisposeHandle (dataRef); } } void quartz_image_to_png (CGImageRef imgref, char *dest) { static int sctr = 0; char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png"; if (dest == NULL) { fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr); sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr); sctr++; dest = sptr; } ExportCGImageToPNGFile (imgref, dest); } void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest) { static int sctr = 0; char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png"; if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq); return; } if (dest == NULL) { fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr); sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr); sctr++; dest = sptr; } CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext); if (imgref == NULL) { fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq); return; } ExportCGImageToPNGFile (imgref, dest); CGImageRelease (imgref); } #endif /* QUARTZ_DEBUG */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-quartz.h����������������������������������0000664�0000000�0000000�00000005130�12710376503�0025071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2006, 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com> */ #ifndef CAIRO_QUARTZ_H #define CAIRO_QUARTZ_H #include "cairo.h" #if CAIRO_HAS_QUARTZ_SURFACE #include <ApplicationServices/ApplicationServices.h> CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_quartz_surface_create (cairo_format_t format, unsigned int width, unsigned int height); cairo_public cairo_surface_t * cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, unsigned int width, unsigned int height); cairo_public CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); #if CAIRO_HAS_QUARTZ_FONT /* * Quartz font support */ cairo_public cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font); cairo_public cairo_font_face_t * cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id); #endif /* CAIRO_HAS_QUARTZ_FONT */ CAIRO_END_DECLS #else # error Cairo was not compiled with support for the quartz backend #endif /* CAIRO_HAS_QUARTZ_SURFACE */ #endif /* CAIRO_QUARTZ_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-raster-source-pattern.c�������������������0000664�0000000�0000000�00000031216�12710376503�0030013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-pattern-private.h" /** * SECTION:cairo-raster-source * @Title: Raster Sources * @Short_Description: Supplying arbitrary image data * @See_Also: #cairo_pattern_t * * The raster source provides the ability to supply arbitrary pixel data * whilst rendering. The pixels are queried at the time of rasterisation * by means of user callback functions, allowing for the ultimate * flexibility. For example, in handling compressed image sources, you * may keep a MRU cache of decompressed images and decompress sources on the * fly and discard old ones to conserve memory. * * For the raster source to be effective, you must at least specify * the acquire and release callbacks which are used to retrieve the pixel * data for the region of interest and demark when it can be freed afterwards. * Other callbacks are provided for when the pattern is copied temporarily * during rasterisation, or more permanently as a snapshot in order to keep * the pixel data available for printing. * * Since: 1.12 **/ cairo_surface_t * _cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern, cairo_surface_t *target, const cairo_rectangle_int_t *extents) { cairo_raster_source_pattern_t *pattern = (cairo_raster_source_pattern_t *) abstract_pattern; if (pattern->acquire == NULL) return NULL; if (extents == NULL) extents = &pattern->extents; return pattern->acquire (&pattern->base, pattern->user_data, target, extents); } void _cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern, cairo_surface_t *surface) { cairo_raster_source_pattern_t *pattern = (cairo_raster_source_pattern_t *) abstract_pattern; if (pattern->release == NULL) return; pattern->release (&pattern->base, pattern->user_data, surface); } cairo_status_t _cairo_raster_source_pattern_init_copy (cairo_pattern_t *abstract_pattern, const cairo_pattern_t *other) { cairo_raster_source_pattern_t *pattern = (cairo_raster_source_pattern_t *) abstract_pattern; cairo_status_t status; VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_raster_source_pattern_t))); memcpy(pattern, other, sizeof (cairo_raster_source_pattern_t)); status = CAIRO_STATUS_SUCCESS; if (pattern->copy) status = pattern->copy (&pattern->base, pattern->user_data, other); return status; } cairo_status_t _cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern = (cairo_raster_source_pattern_t *) abstract_pattern; if (pattern->snapshot == NULL) return CAIRO_STATUS_SUCCESS; return pattern->snapshot (&pattern->base, pattern->user_data); } void _cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern = (cairo_raster_source_pattern_t *) abstract_pattern; if (pattern->finish == NULL) return; pattern->finish (&pattern->base, pattern->user_data); } /* Public interface */ /** * cairo_pattern_create_raster_source: * @user_data: the user data to be passed to all callbacks * @content: content type for the pixel data that will be returned. Knowing * the content type ahead of time is used for analysing the operation and * picking the appropriate rendering path. * @width: maximum size of the sample area * @height: maximum size of the sample area * * Creates a new user pattern for providing pixel data. * * Use the setter functions to associate callbacks with the returned * pattern. The only mandatory callback is acquire. * * Return value: a newly created #cairo_pattern_t. Free with * cairo_pattern_destroy() when you are done using it. * * Since: 1.12 **/ cairo_pattern_t * cairo_pattern_create_raster_source (void *user_data, cairo_content_t content, int width, int height) { cairo_raster_source_pattern_t *pattern; CAIRO_MUTEX_INITIALIZE (); if (width < 0 || height < 0) return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_SIZE); if (! CAIRO_CONTENT_VALID (content)) return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_CONTENT); pattern = calloc (1, sizeof (*pattern)); if (unlikely (pattern == NULL)) return _cairo_pattern_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_RASTER_SOURCE); CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); pattern->content = content; pattern->extents.x = 0; pattern->extents.y = 0; pattern->extents.width = width; pattern->extents.height = height; pattern->user_data = user_data; return &pattern->base; } /** * cairo_raster_source_pattern_set_callback_data: * @pattern: the pattern to update * @data: the user data to be passed to all callbacks * * Updates the user data that is provided to all callbacks. * * Since: 1.12 **/ void cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *abstract_pattern, void *data) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; pattern->user_data = data; } /** * cairo_raster_source_pattern_get_callback_data: * @pattern: the pattern to update * * Queries the current user data. * * Return value: the current user-data passed to each callback * * Since: 1.12 **/ void * cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return NULL; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; return pattern->user_data; } /** * cairo_raster_source_pattern_set_acquire: * @pattern: the pattern to update * @acquire: acquire callback * @release: release callback * * Specifies the callbacks used to generate the image surface for a rendering * operation (acquire) and the function used to cleanup that surface afterwards. * * The @acquire callback should create a surface (preferably an image * surface created to match the target using * cairo_surface_create_similar_image()) that defines at least the region * of interest specified by extents. The surface is allowed to be the entire * sample area, but if it does contain a subsection of the sample area, * the surface extents should be provided by setting the device offset (along * with its width and height) using cairo_surface_set_device_offset(). * * Since: 1.12 **/ void cairo_raster_source_pattern_set_acquire (cairo_pattern_t *abstract_pattern, cairo_raster_source_acquire_func_t acquire, cairo_raster_source_release_func_t release) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; pattern->acquire = acquire; pattern->release = release; } /** * cairo_raster_source_pattern_get_acquire: * @pattern: the pattern to query * @acquire: return value for the current acquire callback * @release: return value for the current release callback * * Queries the current acquire and release callbacks. * * Since: 1.12 **/ void cairo_raster_source_pattern_get_acquire (cairo_pattern_t *abstract_pattern, cairo_raster_source_acquire_func_t *acquire, cairo_raster_source_release_func_t *release) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; if (acquire) *acquire = pattern->acquire; if (release) *release = pattern->release; } /** * cairo_raster_source_pattern_set_snapshot: * @pattern: the pattern to update * @snapshot: snapshot callback * * Sets the callback that will be used whenever a snapshot is taken of the * pattern, that is whenever the current contents of the pattern should be * preserved for later use. This is typically invoked whilst printing. * * Since: 1.12 **/ void cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *abstract_pattern, cairo_raster_source_snapshot_func_t snapshot) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; pattern->snapshot = snapshot; } /** * cairo_raster_source_pattern_get_snapshot: * @pattern: the pattern to query * * Queries the current snapshot callback. * * Return value: the current snapshot callback * * Since: 1.12 **/ cairo_raster_source_snapshot_func_t cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return NULL; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; return pattern->snapshot; } /** * cairo_raster_source_pattern_set_copy: * @pattern: the pattern to update * @copy: the copy callback * * Updates the copy callback which is used whenever a temporary copy of the * pattern is taken. * * Since: 1.12 **/ void cairo_raster_source_pattern_set_copy (cairo_pattern_t *abstract_pattern, cairo_raster_source_copy_func_t copy) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; pattern->copy = copy; } /** * cairo_raster_source_pattern_get_copy: * @pattern: the pattern to query * * Queries the current copy callback. * * Return value: the current copy callback * * Since: 1.12 **/ cairo_raster_source_copy_func_t cairo_raster_source_pattern_get_copy (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return NULL; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; return pattern->copy; } /** * cairo_raster_source_pattern_set_finish: * @pattern: the pattern to update * @finish: the finish callback * * Updates the finish callback which is used whenever a pattern (or a copy * thereof) will no longer be used. * * Since: 1.12 **/ void cairo_raster_source_pattern_set_finish (cairo_pattern_t *abstract_pattern, cairo_raster_source_finish_func_t finish) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; pattern->finish = finish; } /** * cairo_raster_source_pattern_get_finish: * @pattern: the pattern to query * * Queries the current finish callback. * * Return value: the current finish callback * * Since: 1.12 **/ cairo_raster_source_finish_func_t cairo_raster_source_pattern_get_finish (cairo_pattern_t *abstract_pattern) { cairo_raster_source_pattern_t *pattern; if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) return NULL; pattern = (cairo_raster_source_pattern_t *) abstract_pattern; return pattern->finish; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-recording-surface-inline.h����������������0000664�0000000�0000000�00000004520�12710376503�0030423�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_RECORDING_SURFACE_INLINE_H #define CAIRO_RECORDING_SURFACE_INLINE_H #include "cairo-recording-surface-private.h" static inline cairo_bool_t _cairo_recording_surface_get_bounds (cairo_surface_t *surface, cairo_rectangle_t *extents) { cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface; if (recording->unbounded) return FALSE; *extents = recording->extents_pixels; return TRUE; } /** * _cairo_surface_is_recording: * @surface: a #cairo_surface_t * * Checks if a surface is a #cairo_recording_surface_t * * Return value: %TRUE if the surface is a recording surface **/ static inline cairo_bool_t _cairo_surface_is_recording (const cairo_surface_t *surface) { return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING; } #endif /* CAIRO_RECORDING_SURFACE_INLINE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-recording-surface-private.h���������������0000664�0000000�0000000�00000013664�12710376503�0030630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Adrian Johnson <ajohnson@redneon.com> */ #ifndef CAIRO_RECORDING_SURFACE_H #define CAIRO_RECORDING_SURFACE_H #include "cairoint.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-backend-private.h" typedef enum { /* The 5 basic drawing operations. */ CAIRO_COMMAND_PAINT, CAIRO_COMMAND_MASK, CAIRO_COMMAND_STROKE, CAIRO_COMMAND_FILL, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, } cairo_command_type_t; typedef enum { CAIRO_RECORDING_REGION_ALL, CAIRO_RECORDING_REGION_NATIVE, CAIRO_RECORDING_REGION_IMAGE_FALLBACK } cairo_recording_region_type_t; typedef struct _cairo_command_header { cairo_command_type_t type; cairo_recording_region_type_t region; cairo_operator_t op; cairo_rectangle_int_t extents; cairo_clip_t *clip; int index; struct _cairo_command_header *chain; } cairo_command_header_t; typedef struct _cairo_command_paint { cairo_command_header_t header; cairo_pattern_union_t source; } cairo_command_paint_t; typedef struct _cairo_command_mask { cairo_command_header_t header; cairo_pattern_union_t source; cairo_pattern_union_t mask; } cairo_command_mask_t; typedef struct _cairo_command_stroke { cairo_command_header_t header; cairo_pattern_union_t source; cairo_path_fixed_t path; cairo_stroke_style_t style; cairo_matrix_t ctm; cairo_matrix_t ctm_inverse; double tolerance; cairo_antialias_t antialias; } cairo_command_stroke_t; typedef struct _cairo_command_fill { cairo_command_header_t header; cairo_pattern_union_t source; cairo_path_fixed_t path; cairo_fill_rule_t fill_rule; double tolerance; cairo_antialias_t antialias; } cairo_command_fill_t; typedef struct _cairo_command_show_text_glyphs { cairo_command_header_t header; cairo_pattern_union_t source; char *utf8; int utf8_len; cairo_glyph_t *glyphs; unsigned int num_glyphs; cairo_text_cluster_t *clusters; int num_clusters; cairo_text_cluster_flags_t cluster_flags; cairo_scaled_font_t *scaled_font; } cairo_command_show_text_glyphs_t; typedef union _cairo_command { cairo_command_header_t header; cairo_command_paint_t paint; cairo_command_mask_t mask; cairo_command_stroke_t stroke; cairo_command_fill_t fill; cairo_command_show_text_glyphs_t show_text_glyphs; } cairo_command_t; typedef struct _cairo_recording_surface { cairo_surface_t base; /* A recording-surface is logically unbounded, but when used as a * source we need to render it to an image, so we need a size at * which to create that image. */ cairo_rectangle_t extents_pixels; cairo_rectangle_int_t extents; cairo_bool_t unbounded; cairo_array_t commands; unsigned int *indices; unsigned int num_indices; cairo_bool_t optimize_clears; struct bbtree { cairo_box_t extents; struct bbtree *left, *right; cairo_command_header_t *chain; } bbtree; } cairo_recording_surface_t; slim_hidden_proto (cairo_recording_surface_create); cairo_private cairo_int_status_t _cairo_recording_surface_get_path (cairo_surface_t *surface, cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, long unsigned index, cairo_surface_t *target); cairo_private cairo_status_t _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target); cairo_private cairo_status_t _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, const cairo_matrix_t *surface_transform, cairo_surface_t *target, const cairo_clip_t *target_clip); cairo_private cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, cairo_surface_t *target); cairo_private cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, const cairo_rectangle_int_t *surface_extents, cairo_surface_t *target, cairo_recording_region_type_t region); cairo_private cairo_status_t _cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, cairo_box_t *bbox, const cairo_matrix_t *transform); cairo_private cairo_status_t _cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, cairo_box_t *bbox, const cairo_matrix_t *transform); #endif /* CAIRO_RECORDING_SURFACE_H */ ����������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-recording-surface.c�����������������������0000664�0000000�0000000�00000167674�12710376503�0027166�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * Copyright © 2007 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg <krh@redhat.com> * Carl Worth <cworth@cworth.org> * Adrian Johnson <ajohnson@redneon.com> */ /** * SECTION:cairo-recording * @Title: Recording Surfaces * @Short_Description: Records all drawing operations * @See_Also: #cairo_surface_t * * A recording surface is a surface that records all drawing operations at * the highest level of the surface backend interface, (that is, the * level of paint, mask, stroke, fill, and show_text_glyphs). The recording * surface can then be "replayed" against any target surface by using it * as a source surface. * * If you want to replay a surface so that the results in target will be * identical to the results that would have been obtained if the original * operations applied to the recording surface had instead been applied to the * target surface, you can use code like this: * <informalexample><programlisting> * cairo_t *cr; * * cr = cairo_create (target); * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); * cairo_paint (cr); * cairo_destroy (cr); * </programlisting></informalexample> * * A recording surface is logically unbounded, i.e. it has no implicit constraint * on the size of the drawing surface. However, in practice this is rarely * useful as you wish to replay against a particular target surface with * known bounds. For this case, it is more efficient to specify the target * extents to the recording surface upon creation. * * The recording phase of the recording surface is careful to snapshot all * necessary objects (paths, patterns, etc.), in order to achieve * accurate replay. The efficiency of the recording surface could be * improved by improving the implementation of snapshot for the * various objects. For example, it would be nice to have a * copy-on-write implementation for _cairo_surface_snapshot. **/ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-clip-private.h" #include "cairo-combsort-inline.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-wrapper-private.h" #include "cairo-traps-private.h" typedef enum { CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_CREATE_REGIONS } cairo_recording_replay_type_t; static const cairo_surface_backend_t cairo_recording_surface_backend; /** * CAIRO_HAS_RECORDING_SURFACE: * * Defined if the recording surface backend is available. * The recording surface backend is always built in. * This macro was added for completeness in cairo 1.10. * * Since: 1.10 **/ /* Currently all recording surfaces do have a size which should be passed * in as the maximum size of any target surface against which the * recording-surface will ever be replayed. * * XXX: The naming of "pixels" in the size here is a misnomer. It's * actually a size in whatever device-space units are desired (again, * according to the intended replay target). */ static int bbtree_left_or_right (struct bbtree *bbt, const cairo_box_t *box) { int left, right; if (bbt->left) { cairo_box_t *e = &bbt->left->extents; cairo_box_t b; b.p1.x = MIN (e->p1.x, box->p1.x); b.p1.y = MIN (e->p1.y, box->p1.y); b.p2.x = MAX (e->p2.x, box->p2.x); b.p2.y = MAX (e->p2.y, box->p2.y); left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y); left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y); } else left = 0; if (bbt->right) { cairo_box_t *e = &bbt->right->extents; cairo_box_t b; b.p1.x = MIN (e->p1.x, box->p1.x); b.p1.y = MIN (e->p1.y, box->p1.y); b.p2.x = MAX (e->p2.x, box->p2.x); b.p2.y = MAX (e->p2.y, box->p2.y); right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y); right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y); } else right = 0; return left <= right; } #define INVALID_CHAIN ((cairo_command_header_t *)-1) static struct bbtree * bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain) { struct bbtree *bbt = malloc (sizeof (*bbt)); if (bbt == NULL) return NULL; bbt->extents = *box; bbt->left = bbt->right = NULL; bbt->chain = chain; return bbt; } static void bbtree_init (struct bbtree *bbt, cairo_command_header_t *header) { _cairo_box_from_rectangle (&bbt->extents, &header->extents); bbt->chain = header; } static cairo_status_t bbtree_add (struct bbtree *bbt, cairo_command_header_t *header, const cairo_box_t *box) { if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y || box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y) { if (bbt->chain) { if (bbtree_left_or_right (bbt, &bbt->extents)) { if (bbt->left == NULL) { bbt->left = bbtree_new (&bbt->extents, bbt->chain); if (unlikely (bbt->left == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else bbtree_add (bbt->left, bbt->chain, &bbt->extents); } else { if (bbt->right == NULL) { bbt->right = bbtree_new (&bbt->extents, bbt->chain); if (unlikely (bbt->right == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else bbtree_add (bbt->right, bbt->chain, &bbt->extents); } bbt->chain = NULL; } bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x); bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y); bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x); bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y); } if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y && box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y) { cairo_command_header_t *last = header; while (last->chain) /* expected to be infrequent */ last = last->chain; last->chain = bbt->chain; bbt->chain = header; return CAIRO_STATUS_SUCCESS; } if (bbtree_left_or_right (bbt, box)) { if (bbt->left == NULL) { bbt->left = bbtree_new (box, header); if (unlikely (bbt->left == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else return bbtree_add (bbt->left, header, box); } else { if (bbt->right == NULL) { bbt->right = bbtree_new (box, header); if (unlikely (bbt->right == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else return bbtree_add (bbt->right, header, box); } return CAIRO_STATUS_SUCCESS; } static void bbtree_del (struct bbtree *bbt) { if (bbt->left) bbtree_del (bbt->left); if (bbt->right) bbtree_del (bbt->right); free (bbt); } static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b) { return a->p1.x >= b->p2.x || a->p1.y >= b->p2.y || a->p2.x <= b->p1.x || a->p2.y <= b->p1.y; } static void bbtree_foreach_mark_visible (struct bbtree *bbt, const cairo_box_t *box, unsigned int **indices) { cairo_command_header_t *chain; for (chain = bbt->chain; chain; chain = chain->chain) *(*indices)++ = chain->index; if (bbt->left && ! box_outside (box, &bbt->left->extents)) bbtree_foreach_mark_visible (bbt->left, box, indices); if (bbt->right && ! box_outside (box, &bbt->right->extents)) bbtree_foreach_mark_visible (bbt->right, box, indices); } static inline int intcmp (const unsigned int a, const unsigned int b) { return a - b; } CAIRO_COMBSORT_DECLARE (sort_indices, unsigned int, intcmp) static inline int sizecmp (unsigned int a, unsigned int b, cairo_command_header_t **elements) { const cairo_rectangle_int_t *r; r = &elements[a]->extents; a = r->width * r->height; r = &elements[b]->extents; b = r->width * r->height; return b - a; } CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, unsigned int, sizecmp) static void _cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface) { cairo_command_t **elements; int i, num_elements; if (surface->bbtree.chain == INVALID_CHAIN) return; if (surface->bbtree.left) { bbtree_del (surface->bbtree.left); surface->bbtree.left = NULL; } if (surface->bbtree.right) { bbtree_del (surface->bbtree.right); surface->bbtree.right = NULL; } elements = _cairo_array_index (&surface->commands, 0); num_elements = surface->commands.num_elements; for (i = 0; i < num_elements; i++) elements[i]->header.chain = NULL; surface->bbtree.chain = INVALID_CHAIN; } static cairo_status_t _cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface) { cairo_command_t **elements = _cairo_array_index (&surface->commands, 0); unsigned int *indices; cairo_status_t status; unsigned int i, count; count = surface->commands.num_elements; if (count > surface->num_indices) { free (surface->indices); surface->indices = _cairo_malloc_ab (count, sizeof (int)); if (unlikely (surface->indices == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); surface->num_indices = count; } indices = surface->indices; for (i = 0; i < count; i++) indices[i] = i; sort_commands (indices, count, elements); bbtree_init (&surface->bbtree, &elements[indices[0]]->header); for (i = 1; i < count; i++) { cairo_command_header_t *header = &elements[indices[i]]->header; cairo_box_t box; _cairo_box_from_rectangle (&box, &header->extents); status = bbtree_add (&surface->bbtree, header, &box); if (unlikely (status)) goto cleanup; } return CAIRO_STATUS_SUCCESS; cleanup: bbtree_del (&surface->bbtree); return status; } /** * cairo_recording_surface_create: * @content: the content of the recording surface * @extents: the extents to record in pixels, can be %NULL to record * unbounded operations. * * Creates a recording-surface which can be used to record all drawing operations * at the highest level (that is, the level of paint, mask, stroke, fill * and show_text_glyphs). The recording surface can then be "replayed" against * any target surface by using it as a source to drawing operations. * * The recording phase of the recording surface is careful to snapshot all * necessary objects (paths, patterns, etc.), in order to achieve * accurate replay. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * Since: 1.10 **/ cairo_surface_t * cairo_recording_surface_create (cairo_content_t content, const cairo_rectangle_t *extents) { cairo_recording_surface_t *surface; surface = malloc (sizeof (cairo_recording_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_recording_surface_backend, NULL, /* device */ content); surface->unbounded = TRUE; /* unbounded -> 'infinite' extents */ if (extents != NULL) { surface->extents_pixels = *extents; /* XXX check for overflow */ surface->extents.x = floor (extents->x); surface->extents.y = floor (extents->y); surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x; surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y; surface->unbounded = FALSE; } _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); surface->base.is_clear = TRUE; surface->bbtree.left = surface->bbtree.right = NULL; surface->bbtree.chain = INVALID_CHAIN; surface->indices = NULL; surface->num_indices = 0; surface->optimize_clears = TRUE; return &surface->base; } slim_hidden_def (cairo_recording_surface_create); static cairo_surface_t * _cairo_recording_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_rectangle_t extents; extents.x = extents.y = 0; extents.width = width; extents.height = height; return cairo_recording_surface_create (content, &extents); } static cairo_status_t _cairo_recording_surface_finish (void *abstract_surface) { cairo_recording_surface_t *surface = abstract_surface; cairo_command_t **elements; int i, num_elements; num_elements = surface->commands.num_elements; elements = _cairo_array_index (&surface->commands, 0); for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[i]; switch (command->header.type) { case CAIRO_COMMAND_PAINT: _cairo_pattern_fini (&command->paint.source.base); break; case CAIRO_COMMAND_MASK: _cairo_pattern_fini (&command->mask.source.base); _cairo_pattern_fini (&command->mask.mask.base); break; case CAIRO_COMMAND_STROKE: _cairo_pattern_fini (&command->stroke.source.base); _cairo_path_fixed_fini (&command->stroke.path); _cairo_stroke_style_fini (&command->stroke.style); break; case CAIRO_COMMAND_FILL: _cairo_pattern_fini (&command->fill.source.base); _cairo_path_fixed_fini (&command->fill.path); break; case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: _cairo_pattern_fini (&command->show_text_glyphs.source.base); free (command->show_text_glyphs.utf8); free (command->show_text_glyphs.glyphs); free (command->show_text_glyphs.clusters); cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); break; default: ASSERT_NOT_REACHED; } _cairo_clip_destroy (command->header.clip); free (command); } _cairo_array_fini (&surface->commands); if (surface->bbtree.left) bbtree_del (surface->bbtree.left); if (surface->bbtree.right) bbtree_del (surface->bbtree.right); free (surface->indices); return CAIRO_STATUS_SUCCESS; } struct proxy { cairo_surface_t base; cairo_surface_t *image; }; static cairo_status_t proxy_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { struct proxy *proxy = abstract_surface; return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); } static void proxy_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { struct proxy *proxy = abstract_surface; _cairo_surface_release_source_image (proxy->image, image, image_extra); } static cairo_status_t proxy_finish (void *abstract_surface) { return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t proxy_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, proxy_finish, NULL, NULL, /* create similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, proxy_acquire_source_image, proxy_release_source_image, }; static cairo_surface_t * attach_proxy (cairo_surface_t *source, cairo_surface_t *image) { struct proxy *proxy; proxy = malloc (sizeof (*proxy)); if (unlikely (proxy == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content); proxy->image = image; _cairo_surface_attach_snapshot (source, &proxy->base, NULL); return &proxy->base; } static void detach_proxy (cairo_surface_t *source, cairo_surface_t *proxy) { cairo_surface_finish (proxy); cairo_surface_destroy (proxy); } static cairo_surface_t * get_proxy (cairo_surface_t *proxy) { return ((struct proxy *)proxy)->image; } static cairo_status_t _cairo_recording_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_recording_surface_t *surface = abstract_surface; cairo_surface_t *image, *proxy; cairo_status_t status; proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend); if (proxy != NULL) { *image_out = (cairo_image_surface_t *) cairo_surface_reference (get_proxy (proxy)); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } assert (! surface->unbounded); image = _cairo_image_surface_create_with_content (surface->base.content, surface->extents.width, surface->extents.height); if (unlikely (image->status)) return image->status; /* Handle recursion by returning future reads from the current image */ proxy = attach_proxy (abstract_surface, image); status = _cairo_recording_surface_replay (&surface->base, image); detach_proxy (abstract_surface, proxy); if (unlikely (status)) { cairo_surface_destroy (image); return status; } *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_recording_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _command_init (cairo_recording_surface_t *surface, cairo_command_header_t *command, cairo_command_type_t type, cairo_operator_t op, cairo_composite_rectangles_t *composite) { cairo_status_t status = CAIRO_STATUS_SUCCESS; command->type = type; command->op = op; command->region = CAIRO_RECORDING_REGION_ALL; command->extents = composite->unbounded; command->chain = NULL; command->index = surface->commands.num_elements; /* steal the clip */ command->clip = NULL; if (! _cairo_composite_rectangles_can_reduce_clip (composite, composite->clip)) { command->clip = composite->clip; composite->clip = NULL; } return status; } static void _cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface) { cairo_surface_flush (&surface->base); } static cairo_status_t _cairo_recording_surface_commit (cairo_recording_surface_t *surface, cairo_command_header_t *command) { _cairo_recording_surface_break_self_copy_loop (surface); return _cairo_array_append (&surface->commands, &command); } static void _cairo_recording_surface_reset (cairo_recording_surface_t *surface) { /* Reset the commands and temporaries */ _cairo_recording_surface_finish (surface); surface->bbtree.left = surface->bbtree.right = NULL; surface->bbtree.chain = INVALID_CHAIN; surface->indices = NULL; surface->num_indices = 0; _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); } static cairo_bool_t is_identity_recording_pattern (const cairo_pattern_t *pattern) { cairo_surface_t *surface; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return FALSE; if (!_cairo_matrix_is_identity(&pattern->matrix)) return FALSE; surface = ((cairo_surface_pattern_t *)pattern)->surface; return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING; } static cairo_int_status_t _cairo_recording_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; cairo_command_paint_t *command; cairo_composite_rectangles_t composite; TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { if (surface->optimize_clears) { _cairo_recording_surface_reset (surface); return CAIRO_STATUS_SUCCESS; } } if (clip == NULL && surface->optimize_clears && (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source))))) { _cairo_recording_surface_reset (surface); if (is_identity_recording_pattern (source)) { cairo_surface_t *src = ((cairo_surface_pattern_t *)source)->surface; return _cairo_recording_surface_replay (src, &surface->base); } } status = _cairo_composite_rectangles_init_for_paint (&composite, &surface->base, op, source, clip); if (unlikely (status)) return status; command = malloc (sizeof (cairo_command_paint_t)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_COMPOSITE; } status = _command_init (surface, &command->header, CAIRO_COMMAND_PAINT, op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->source.base, source); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_SOURCE; _cairo_recording_surface_destroy_bbtree (surface); _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: _cairo_clip_destroy (command->header.clip); free (command); CLEANUP_COMPOSITE: _cairo_composite_rectangles_fini (&composite); return status; } static cairo_int_status_t _cairo_recording_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; cairo_command_mask_t *command; cairo_composite_rectangles_t composite; TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); status = _cairo_composite_rectangles_init_for_mask (&composite, &surface->base, op, source, mask, clip); if (unlikely (status)) return status; command = malloc (sizeof (cairo_command_mask_t)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_COMPOSITE; } status = _command_init (surface, &command->header, CAIRO_COMMAND_MASK, op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->source.base, source); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->mask.base, mask); if (unlikely (status)) goto CLEANUP_SOURCE; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_MASK; _cairo_recording_surface_destroy_bbtree (surface); _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_MASK: _cairo_pattern_fini (&command->mask.base); CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: _cairo_clip_destroy (command->header.clip); free (command); CLEANUP_COMPOSITE: _cairo_composite_rectangles_fini (&composite); return status; } static cairo_int_status_t _cairo_recording_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; cairo_command_stroke_t *command; cairo_composite_rectangles_t composite; TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); status = _cairo_composite_rectangles_init_for_stroke (&composite, &surface->base, op, source, path, style, ctm, clip); if (unlikely (status)) return status; command = malloc (sizeof (cairo_command_stroke_t)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_COMPOSITE; } status = _command_init (surface, &command->header, CAIRO_COMMAND_STROKE, op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->source.base, source); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_path_fixed_init_copy (&command->path, path); if (unlikely (status)) goto CLEANUP_SOURCE; status = _cairo_stroke_style_init_copy (&command->style, style); if (unlikely (status)) goto CLEANUP_PATH; command->ctm = *ctm; command->ctm_inverse = *ctm_inverse; command->tolerance = tolerance; command->antialias = antialias; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_STYLE; _cairo_recording_surface_destroy_bbtree (surface); _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_STYLE: _cairo_stroke_style_fini (&command->style); CLEANUP_PATH: _cairo_path_fixed_fini (&command->path); CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: _cairo_clip_destroy (command->header.clip); free (command); CLEANUP_COMPOSITE: _cairo_composite_rectangles_fini (&composite); return status; } static cairo_int_status_t _cairo_recording_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; cairo_command_fill_t *command; cairo_composite_rectangles_t composite; TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); status = _cairo_composite_rectangles_init_for_fill (&composite, &surface->base, op, source, path, clip); if (unlikely (status)) return status; command = malloc (sizeof (cairo_command_fill_t)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_COMPOSITE; } status =_command_init (surface, &command->header, CAIRO_COMMAND_FILL, op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->source.base, source); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_path_fixed_init_copy (&command->path, path); if (unlikely (status)) goto CLEANUP_SOURCE; command->fill_rule = fill_rule; command->tolerance = tolerance; command->antialias = antialias; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_PATH; _cairo_recording_surface_destroy_bbtree (surface); _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_PATH: _cairo_path_fixed_fini (&command->path); CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: _cairo_clip_destroy (command->header.clip); free (command); CLEANUP_COMPOSITE: _cairo_composite_rectangles_fini (&composite); return status; } static cairo_bool_t _cairo_recording_surface_has_show_text_glyphs (void *abstract_surface) { return TRUE; } static cairo_int_status_t _cairo_recording_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; cairo_command_show_text_glyphs_t *command; cairo_composite_rectangles_t composite; TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); status = _cairo_composite_rectangles_init_for_glyphs (&composite, &surface->base, op, source, scaled_font, glyphs, num_glyphs, clip, NULL); if (unlikely (status)) return status; command = malloc (sizeof (cairo_command_show_text_glyphs_t)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_COMPOSITE; } status = _command_init (surface, &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; status = _cairo_pattern_init_snapshot (&command->source.base, source); if (unlikely (status)) goto CLEANUP_COMMAND; command->utf8 = NULL; command->utf8_len = utf8_len; command->glyphs = NULL; command->num_glyphs = num_glyphs; command->clusters = NULL; command->num_clusters = num_clusters; if (utf8_len) { command->utf8 = malloc (utf8_len); if (unlikely (command->utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } memcpy (command->utf8, utf8, utf8_len); } if (num_glyphs) { command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); if (unlikely (command->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); } if (num_clusters) { command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); if (unlikely (command->clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; } memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); } command->cluster_flags = cluster_flags; command->scaled_font = cairo_scaled_font_reference (scaled_font); status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_SCALED_FONT; _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_SCALED_FONT: cairo_scaled_font_destroy (command->scaled_font); CLEANUP_ARRAYS: free (command->utf8); free (command->glyphs); free (command->clusters); _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: _cairo_clip_destroy (command->header.clip); free (command); CLEANUP_COMPOSITE: _cairo_composite_rectangles_fini (&composite); return status; } static void _command_init_copy (cairo_recording_surface_t *surface, cairo_command_header_t *dst, const cairo_command_header_t *src) { dst->type = src->type; dst->op = src->op; dst->region = CAIRO_RECORDING_REGION_ALL; dst->extents = src->extents; dst->chain = NULL; dst->index = surface->commands.num_elements; dst->clip = _cairo_clip_copy (src->clip); } static cairo_status_t _cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface, const cairo_command_t *src) { cairo_command_paint_t *command; cairo_status_t status; command = malloc (sizeof (*command)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err; } _command_init_copy (surface, &command->header, &src->header); status = _cairo_pattern_init_copy (&command->source.base, &src->paint.source.base); if (unlikely (status)) goto err_command; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto err_source; return CAIRO_STATUS_SUCCESS; err_source: _cairo_pattern_fini (&command->source.base); err_command: free(command); err: return status; } static cairo_status_t _cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface, const cairo_command_t *src) { cairo_command_mask_t *command; cairo_status_t status; command = malloc (sizeof (*command)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err; } _command_init_copy (surface, &command->header, &src->header); status = _cairo_pattern_init_copy (&command->source.base, &src->mask.source.base); if (unlikely (status)) goto err_command; status = _cairo_pattern_init_copy (&command->mask.base, &src->mask.source.base); if (unlikely (status)) goto err_source; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto err_mask; return CAIRO_STATUS_SUCCESS; err_mask: _cairo_pattern_fini (&command->mask.base); err_source: _cairo_pattern_fini (&command->source.base); err_command: free(command); err: return status; } static cairo_status_t _cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface, const cairo_command_t *src) { cairo_command_stroke_t *command; cairo_status_t status; command = malloc (sizeof (*command)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err; } _command_init_copy (surface, &command->header, &src->header); status = _cairo_pattern_init_copy (&command->source.base, &src->stroke.source.base); if (unlikely (status)) goto err_command; status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path); if (unlikely (status)) goto err_source; status = _cairo_stroke_style_init_copy (&command->style, &src->stroke.style); if (unlikely (status)) goto err_path; command->ctm = src->stroke.ctm; command->ctm_inverse = src->stroke.ctm_inverse; command->tolerance = src->stroke.tolerance; command->antialias = src->stroke.antialias; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto err_style; return CAIRO_STATUS_SUCCESS; err_style: _cairo_stroke_style_fini (&command->style); err_path: _cairo_path_fixed_fini (&command->path); err_source: _cairo_pattern_fini (&command->source.base); err_command: free(command); err: return status; } static cairo_status_t _cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface, const cairo_command_t *src) { cairo_command_fill_t *command; cairo_status_t status; command = malloc (sizeof (*command)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err; } _command_init_copy (surface, &command->header, &src->header); status = _cairo_pattern_init_copy (&command->source.base, &src->fill.source.base); if (unlikely (status)) goto err_command; status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path); if (unlikely (status)) goto err_source; command->fill_rule = src->fill.fill_rule; command->tolerance = src->fill.tolerance; command->antialias = src->fill.antialias; status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto err_path; return CAIRO_STATUS_SUCCESS; err_path: _cairo_path_fixed_fini (&command->path); err_source: _cairo_pattern_fini (&command->source.base); err_command: free(command); err: return status; } static cairo_status_t _cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface, const cairo_command_t *src) { cairo_command_show_text_glyphs_t *command; cairo_status_t status; command = malloc (sizeof (*command)); if (unlikely (command == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err; } _command_init_copy (surface, &command->header, &src->header); status = _cairo_pattern_init_copy (&command->source.base, &src->show_text_glyphs.source.base); if (unlikely (status)) goto err_command; command->utf8 = NULL; command->utf8_len = src->show_text_glyphs.utf8_len; command->glyphs = NULL; command->num_glyphs = src->show_text_glyphs.num_glyphs; command->clusters = NULL; command->num_clusters = src->show_text_glyphs.num_clusters; if (command->utf8_len) { command->utf8 = malloc (command->utf8_len); if (unlikely (command->utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err_arrays; } memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len); } if (command->num_glyphs) { command->glyphs = _cairo_malloc_ab (command->num_glyphs, sizeof (command->glyphs[0])); if (unlikely (command->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err_arrays; } memcpy (command->glyphs, src->show_text_glyphs.glyphs, sizeof (command->glyphs[0]) * command->num_glyphs); } if (command->num_clusters) { command->clusters = _cairo_malloc_ab (command->num_clusters, sizeof (command->clusters[0])); if (unlikely (command->clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto err_arrays; } memcpy (command->clusters, src->show_text_glyphs.clusters, sizeof (command->clusters[0]) * command->num_clusters); } command->cluster_flags = src->show_text_glyphs.cluster_flags; command->scaled_font = cairo_scaled_font_reference (src->show_text_glyphs.scaled_font); status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto err_arrays; return CAIRO_STATUS_SUCCESS; err_arrays: free (command->utf8); free (command->glyphs); free (command->clusters); _cairo_pattern_fini (&command->source.base); err_command: free(command); err: return status; } static cairo_status_t _cairo_recording_surface_copy (cairo_recording_surface_t *dst, cairo_recording_surface_t *src) { cairo_command_t **elements; int i, num_elements; cairo_status_t status; elements = _cairo_array_index (&src->commands, 0); num_elements = src->commands.num_elements; for (i = 0; i < num_elements; i++) { const cairo_command_t *command = elements[i]; switch (command->header.type) { case CAIRO_COMMAND_PAINT: status = _cairo_recording_surface_copy__paint (dst, command); break; case CAIRO_COMMAND_MASK: status = _cairo_recording_surface_copy__mask (dst, command); break; case CAIRO_COMMAND_STROKE: status = _cairo_recording_surface_copy__stroke (dst, command); break; case CAIRO_COMMAND_FILL: status = _cairo_recording_surface_copy__fill (dst, command); break; case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: status = _cairo_recording_surface_copy__glyphs (dst, command); break; default: ASSERT_NOT_REACHED; } if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } /** * _cairo_recording_surface_snapshot: * @surface: a #cairo_surface_t which must be a recording surface * * Make an immutable copy of @surface. It is an error to call a * surface-modifying function on the result of this function. * * The caller owns the return value and should call * cairo_surface_destroy() when finished with it. This function will not * return %NULL, but will return a nil surface instead. * * Return value: The snapshot surface. **/ static cairo_surface_t * _cairo_recording_surface_snapshot (void *abstract_other) { cairo_recording_surface_t *other = abstract_other; cairo_recording_surface_t *surface; cairo_status_t status; surface = malloc (sizeof (cairo_recording_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_recording_surface_backend, NULL, /* device */ other->base.content); surface->extents_pixels = other->extents_pixels; surface->extents = other->extents; surface->unbounded = other->unbounded; surface->base.is_clear = other->base.is_clear; surface->bbtree.left = surface->bbtree.right = NULL; surface->bbtree.chain = INVALID_CHAIN; surface->indices = NULL; surface->num_indices = 0; surface->optimize_clears = TRUE; _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); status = _cairo_recording_surface_copy (surface, other); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } return &surface->base; } static cairo_bool_t _cairo_recording_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_recording_surface_t *surface = abstract_surface; if (surface->unbounded) return FALSE; *rectangle = surface->extents; return TRUE; } static const cairo_surface_backend_t cairo_recording_surface_backend = { CAIRO_SURFACE_TYPE_RECORDING, _cairo_recording_surface_finish, _cairo_default_context_create, _cairo_recording_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, _cairo_recording_surface_acquire_source_image, _cairo_recording_surface_release_source_image, _cairo_recording_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_recording_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ /* Here are the 5 basic drawing operations, (which are in some * sense the only things that cairo_recording_surface should need to * implement). However, we implement the more generic show_text_glyphs * instead of show_glyphs. One or the other is eough. */ _cairo_recording_surface_paint, _cairo_recording_surface_mask, _cairo_recording_surface_stroke, _cairo_recording_surface_fill, NULL, /* fill-stroke */ NULL, _cairo_recording_surface_has_show_text_glyphs, _cairo_recording_surface_show_text_glyphs, }; cairo_int_status_t _cairo_recording_surface_get_path (cairo_surface_t *abstract_surface, cairo_path_fixed_t *path) { cairo_recording_surface_t *surface; cairo_command_t **elements; int i, num_elements; cairo_int_status_t status; if (unlikely (abstract_surface->status)) return abstract_surface->status; surface = (cairo_recording_surface_t *) abstract_surface; status = CAIRO_STATUS_SUCCESS; num_elements = surface->commands.num_elements; elements = _cairo_array_index (&surface->commands, 0); for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[i]; switch (command->header.type) { case CAIRO_COMMAND_PAINT: case CAIRO_COMMAND_MASK: status = CAIRO_INT_STATUS_UNSUPPORTED; break; case CAIRO_COMMAND_STROKE: { cairo_traps_t traps; _cairo_traps_init (&traps); /* XXX call cairo_stroke_to_path() when that is implemented */ status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path, &command->stroke.style, &command->stroke.ctm, &command->stroke.ctm_inverse, command->stroke.tolerance, &traps); if (status == CAIRO_INT_STATUS_SUCCESS) status = _cairo_traps_path (&traps, path); _cairo_traps_fini (&traps); break; } case CAIRO_COMMAND_FILL: { status = _cairo_path_fixed_append (path, &command->fill.path, 0, 0); break; } case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: { status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, path); break; } default: ASSERT_NOT_REACHED; } if (unlikely (status)) break; } return status; } static int _cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface, const cairo_rectangle_int_t *extents) { unsigned int num_visible, *indices; cairo_box_t box; if (surface->commands.num_elements == 0) return 0; _cairo_box_from_rectangle (&box, extents); if (surface->bbtree.chain == INVALID_CHAIN) _cairo_recording_surface_create_bbtree (surface); indices = surface->indices; bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices); num_visible = indices - surface->indices; if (num_visible > 1) sort_indices (surface->indices, num_visible); return num_visible; } static cairo_status_t _cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, const cairo_rectangle_int_t *surface_extents, const cairo_matrix_t *surface_transform, cairo_surface_t *target, const cairo_clip_t *target_clip, cairo_recording_replay_type_t type, cairo_recording_region_type_t region) { cairo_surface_wrapper_t wrapper; cairo_command_t **elements; cairo_bool_t replay_all = type == CAIRO_RECORDING_REPLAY && region == CAIRO_RECORDING_REGION_ALL; cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_rectangle_int_t extents; cairo_bool_t use_indices = FALSE; const cairo_rectangle_int_t *r; unsigned int i, num_elements; if (unlikely (surface->base.status)) return surface->base.status; if (unlikely (target->status)) return target->status; if (unlikely (surface->base.finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); if (surface->base.is_clear) return CAIRO_STATUS_SUCCESS; assert (_cairo_surface_is_recording (&surface->base)); _cairo_surface_wrapper_init (&wrapper, target); if (surface_extents) _cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents); r = &_cairo_unbounded_rectangle; if (! surface->unbounded) { _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents); r = &surface->extents; } _cairo_surface_wrapper_set_inverse_transform (&wrapper, surface_transform); _cairo_surface_wrapper_set_clip (&wrapper, target_clip); /* Compute the extents of the target clip in recorded device space */ if (! _cairo_surface_wrapper_get_target_extents (&wrapper, &extents)) goto done; num_elements = surface->commands.num_elements; elements = _cairo_array_index (&surface->commands, 0); if (extents.width < r->width || extents.height < r->height) { num_elements = _cairo_recording_surface_get_visible_commands (surface, &extents); use_indices = num_elements != surface->commands.num_elements; } for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[use_indices ? surface->indices[i] : i]; if (! replay_all && command->header.region != region) continue; if (! _cairo_rectangle_intersects (&extents, &command->header.extents)) continue; switch (command->header.type) { case CAIRO_COMMAND_PAINT: status = _cairo_surface_wrapper_paint (&wrapper, command->header.op, &command->paint.source.base, command->header.clip); break; case CAIRO_COMMAND_MASK: status = _cairo_surface_wrapper_mask (&wrapper, command->header.op, &command->mask.source.base, &command->mask.mask.base, command->header.clip); break; case CAIRO_COMMAND_STROKE: status = _cairo_surface_wrapper_stroke (&wrapper, command->header.op, &command->stroke.source.base, &command->stroke.path, &command->stroke.style, &command->stroke.ctm, &command->stroke.ctm_inverse, command->stroke.tolerance, command->stroke.antialias, command->header.clip); break; case CAIRO_COMMAND_FILL: status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) { cairo_command_t *stroke_command; stroke_command = NULL; if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) stroke_command = elements[i + 1]; if (stroke_command != NULL && type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) { if (stroke_command->header.region != region) stroke_command = NULL; } if (stroke_command != NULL && stroke_command->header.type == CAIRO_COMMAND_STROKE && _cairo_path_fixed_equal (&command->fill.path, &stroke_command->stroke.path) && _cairo_clip_equal (command->header.clip, stroke_command->header.clip)) { status = _cairo_surface_wrapper_fill_stroke (&wrapper, command->header.op, &command->fill.source.base, command->fill.fill_rule, command->fill.tolerance, command->fill.antialias, &command->fill.path, stroke_command->header.op, &stroke_command->stroke.source.base, &stroke_command->stroke.style, &stroke_command->stroke.ctm, &stroke_command->stroke.ctm_inverse, stroke_command->stroke.tolerance, stroke_command->stroke.antialias, command->header.clip); i++; } } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_surface_wrapper_fill (&wrapper, command->header.op, &command->fill.source.base, &command->fill.path, command->fill.fill_rule, command->fill.tolerance, command->fill.antialias, command->header.clip); } break; case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, command->header.op, &command->show_text_glyphs.source.base, command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, command->show_text_glyphs.cluster_flags, command->show_text_glyphs.scaled_font, command->header.clip); break; default: ASSERT_NOT_REACHED; } if (type == CAIRO_RECORDING_CREATE_REGIONS) { if (status == CAIRO_INT_STATUS_SUCCESS) { command->header.region = CAIRO_RECORDING_REGION_NATIVE; } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; status = CAIRO_INT_STATUS_SUCCESS; } else { assert (_cairo_int_status_is_error (status)); } } if (unlikely (status)) break; } done: _cairo_surface_wrapper_fini (&wrapper); return _cairo_surface_set_error (&surface->base, status); } cairo_status_t _cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, long unsigned index, cairo_surface_t *target) { cairo_surface_wrapper_t wrapper; cairo_command_t **elements, *command; cairo_int_status_t status; if (unlikely (surface->base.status)) return surface->base.status; if (unlikely (target->status)) return target->status; if (unlikely (surface->base.finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); assert (_cairo_surface_is_recording (&surface->base)); /* XXX * Use a surface wrapper because we may want to do transformed * replay in the future. */ _cairo_surface_wrapper_init (&wrapper, target); if (index > surface->commands.num_elements) return _cairo_error (CAIRO_STATUS_READ_ERROR); elements = _cairo_array_index (&surface->commands, 0); command = elements[index]; switch (command->header.type) { case CAIRO_COMMAND_PAINT: status = _cairo_surface_wrapper_paint (&wrapper, command->header.op, &command->paint.source.base, command->header.clip); break; case CAIRO_COMMAND_MASK: status = _cairo_surface_wrapper_mask (&wrapper, command->header.op, &command->mask.source.base, &command->mask.mask.base, command->header.clip); break; case CAIRO_COMMAND_STROKE: status = _cairo_surface_wrapper_stroke (&wrapper, command->header.op, &command->stroke.source.base, &command->stroke.path, &command->stroke.style, &command->stroke.ctm, &command->stroke.ctm_inverse, command->stroke.tolerance, command->stroke.antialias, command->header.clip); break; case CAIRO_COMMAND_FILL: status = _cairo_surface_wrapper_fill (&wrapper, command->header.op, &command->fill.source.base, &command->fill.path, command->fill.fill_rule, command->fill.tolerance, command->fill.antialias, command->header.clip); break; case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, command->header.op, &command->show_text_glyphs.source.base, command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, command->show_text_glyphs.cluster_flags, command->show_text_glyphs.scaled_font, command->header.clip); break; default: ASSERT_NOT_REACHED; } _cairo_surface_wrapper_fini (&wrapper); return _cairo_surface_set_error (&surface->base, status); } /** * _cairo_recording_surface_replay: * @surface: the #cairo_recording_surface_t * @target: a target #cairo_surface_t onto which to replay the operations * @width_pixels: width of the surface, in pixels * @height_pixels: height of the surface, in pixels * * A recording surface can be "replayed" against any target surface, * after which the results in target will be identical to the results * that would have been obtained if the original operations applied to * the recording surface had instead been applied to the target surface. **/ cairo_status_t _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL, target, NULL, CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_REGION_ALL); } cairo_status_t _cairo_recording_surface_replay_with_clip (cairo_surface_t *surface, const cairo_matrix_t *surface_transform, cairo_surface_t *target, const cairo_clip_t *target_clip) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform, target, target_clip, CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_REGION_ALL); } /* Replay recording to surface. When the return status of each operation is * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation * will be stored in the recording surface. Any other status will abort the * replay and return the status. */ cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, cairo_surface_t *target) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL, target, NULL, CAIRO_RECORDING_CREATE_REGIONS, CAIRO_RECORDING_REGION_ALL); } cairo_status_t _cairo_recording_surface_replay_region (cairo_surface_t *surface, const cairo_rectangle_int_t *surface_extents, cairo_surface_t *target, cairo_recording_region_type_t region) { return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, surface_extents, NULL, target, NULL, CAIRO_RECORDING_REPLAY, region); } static cairo_status_t _recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, cairo_box_t *bbox, const cairo_matrix_t *transform) { cairo_surface_t *null_surface; cairo_surface_t *analysis_surface; cairo_status_t status; null_surface = _cairo_null_surface_create (surface->base.content); analysis_surface = _cairo_analysis_surface_create (null_surface); cairo_surface_destroy (null_surface); status = analysis_surface->status; if (unlikely (status)) return status; if (transform != NULL) _cairo_analysis_surface_set_ctm (analysis_surface, transform); status = _cairo_recording_surface_replay (&surface->base, analysis_surface); _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); cairo_surface_destroy (analysis_surface); return status; } /** * cairo_recording_surface_ink_extents: * @surface: a #cairo_recording_surface_t * @x0: the x-coordinate of the top-left of the ink bounding box * @y0: the y-coordinate of the top-left of the ink bounding box * @width: the width of the ink bounding box * @height: the height of the ink bounding box * * Measures the extents of the operations stored within the recording-surface. * This is useful to compute the required size of an image surface (or * equivalent) into which to replay the full sequence of drawing operations. * * Since: 1.10 **/ void cairo_recording_surface_ink_extents (cairo_surface_t *surface, double *x0, double *y0, double *width, double *height) { cairo_status_t status; cairo_box_t bbox; memset (&bbox, 0, sizeof (bbox)); if (surface->status || ! _cairo_surface_is_recording (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); goto DONE; } status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface, &bbox, NULL); if (unlikely (status)) status = _cairo_surface_set_error (surface, status); DONE: if (x0) *x0 = _cairo_fixed_to_double (bbox.p1.x); if (y0) *y0 = _cairo_fixed_to_double (bbox.p1.y); if (width) *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); if (height) *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); } cairo_status_t _cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, cairo_box_t *bbox, const cairo_matrix_t *transform) { if (! surface->unbounded) { _cairo_box_from_rectangle (bbox, &surface->extents); if (transform != NULL) _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); return CAIRO_STATUS_SUCCESS; } return _recording_surface_get_ink_bbox (surface, bbox, transform); } cairo_status_t _cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, cairo_box_t *bbox, const cairo_matrix_t *transform) { return _recording_surface_get_ink_bbox (surface, bbox, transform); } /** * cairo_recording_surface_get_extents: * @surface: a #cairo_recording_surface_t * @extents: the #cairo_rectangle_t to be assigned the extents * * Get the extents of the recording-surface. * * Return value: %TRUE if the surface is bounded, of recording type, and * not in an error state, otherwise %FALSE * * Since: 1.12 **/ cairo_bool_t cairo_recording_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_t *extents) { cairo_recording_surface_t *record; if (surface->status || ! _cairo_surface_is_recording (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return FALSE; } record = (cairo_recording_surface_t *)surface; if (record->unbounded) return FALSE; *extents = record->extents_pixels; return TRUE; } ��������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-rectangle.c�������������������������������0000664�0000000�0000000�00000021373�12710376503�0025511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2006 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-box-inline.h" const cairo_rectangle_int_t _cairo_empty_rectangle = { 0, 0, 0, 0 }; const cairo_rectangle_int_t _cairo_unbounded_rectangle = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN, }; cairo_private void _cairo_box_from_doubles (cairo_box_t *box, double *x1, double *y1, double *x2, double *y2) { box->p1.x = _cairo_fixed_from_double (*x1); box->p1.y = _cairo_fixed_from_double (*y1); box->p2.x = _cairo_fixed_from_double (*x2); box->p2.y = _cairo_fixed_from_double (*y2); } cairo_private void _cairo_box_to_doubles (const cairo_box_t *box, double *x1, double *y1, double *x2, double *y2) { *x1 = _cairo_fixed_to_double (box->p1.x); *y1 = _cairo_fixed_to_double (box->p1.y); *x2 = _cairo_fixed_to_double (box->p2.x); *y2 = _cairo_fixed_to_double (box->p2.y); } void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rect) { box->p1.x = _cairo_fixed_from_int (rect->x); box->p1.y = _cairo_fixed_from_int (rect->y); box->p2.x = _cairo_fixed_from_int (rect->x + rect->width); box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); } void _cairo_boxes_get_extents (const cairo_box_t *boxes, int num_boxes, cairo_box_t *extents) { assert (num_boxes > 0); *extents = *boxes; while (--num_boxes) _cairo_box_add_box (extents, ++boxes); } /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right * corners, expressed in fixed point numbers. A #cairo_rectangle_int_t is * also a rectangular area, but represented by the upper left corner * and the width and the height, as integer numbers. * * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by * increasing the area to the nearest integer coordinates. We should * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and * this function could be renamed to the more reasonable * _cairo_rectangle_fixed_round. */ void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle) { rectangle->x = _cairo_fixed_integer_floor (box->p1.x); rectangle->y = _cairo_fixed_integer_floor (box->p1.y); rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; } cairo_bool_t _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src) { int x1, y1, x2, y2; x1 = MAX (dst->x, src->x); y1 = MAX (dst->y, src->y); /* Beware the unsigned promotion, fortunately we have bits to spare * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX */ x2 = MIN (dst->x + (int) dst->width, src->x + (int) src->width); y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height); if (x1 >= x2 || y1 >= y2) { dst->x = 0; dst->y = 0; dst->width = 0; dst->height = 0; return FALSE; } else { dst->x = x1; dst->y = y1; dst->width = x2 - x1; dst->height = y2 - y1; return TRUE; } } /* Extends the dst rectangle to also contain src. * If one of the rectangles is empty, the result is undefined */ void _cairo_rectangle_union (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src) { int x1, y1, x2, y2; x1 = MIN (dst->x, src->x); y1 = MIN (dst->y, src->y); /* Beware the unsigned promotion, fortunately we have bits to spare * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX */ x2 = MAX (dst->x + (int) dst->width, src->x + (int) src->width); y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height); dst->x = x1; dst->y = y1; dst->width = x2 - x1; dst->height = y2 - y1; } #define P1x (line->p1.x) #define P1y (line->p1.y) #define P2x (line->p2.x) #define P2y (line->p2.y) #define B1x (box->p1.x) #define B1y (box->p1.y) #define B2x (box->p2.x) #define B2y (box->p2.y) /* * Check whether any part of line intersects box. This function essentially * computes whether the ray starting at line->p1 in the direction of line->p2 * intersects the box before it reaches p2. Normally, this is done * by dividing by the lengths of the line projected onto each axis. Because * we're in fixed point, this function does a bit more work to avoid having to * do the division -- we don't care about the actual intersection point, so * it's of no interest to us. */ cairo_bool_t _cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line) { cairo_fixed_t t1=0, t2=0, t3=0, t4=0; cairo_int64_t t1y, t2y, t3x, t4x; cairo_fixed_t xlen, ylen; if (_cairo_box_contains_point (box, &line->p1) || _cairo_box_contains_point (box, &line->p2)) return TRUE; xlen = P2x - P1x; ylen = P2y - P1y; if (xlen) { if (xlen > 0) { t1 = B1x - P1x; t2 = B2x - P1x; } else { t1 = P1x - B2x; t2 = P1x - B1x; xlen = - xlen; } if ((t1 < 0 || t1 > xlen) && (t2 < 0 || t2 > xlen)) return FALSE; } else { /* Fully vertical line -- check that X is in bounds */ if (P1x < B1x || P1x > B2x) return FALSE; } if (ylen) { if (ylen > 0) { t3 = B1y - P1y; t4 = B2y - P1y; } else { t3 = P1y - B2y; t4 = P1y - B1y; ylen = - ylen; } if ((t3 < 0 || t3 > ylen) && (t4 < 0 || t4 > ylen)) return FALSE; } else { /* Fully horizontal line -- check Y */ if (P1y < B1y || P1y > B2y) return FALSE; } /* If we had a horizontal or vertical line, then it's already been checked */ if (P1x == P2x || P1y == P2y) return TRUE; /* Check overlap. Note that t1 < t2 and t3 < t4 here. */ t1y = _cairo_int32x32_64_mul (t1, ylen); t2y = _cairo_int32x32_64_mul (t2, ylen); t3x = _cairo_int32x32_64_mul (t3, xlen); t4x = _cairo_int32x32_64_mul (t4, xlen); if (_cairo_int64_lt(t1y, t4x) && _cairo_int64_lt(t3x, t2y)) return TRUE; return FALSE; } static cairo_status_t _cairo_box_add_spline_point (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { _cairo_box_add_point (closure, point); return CAIRO_STATUS_SUCCESS; } /* assumes a has been previously added */ void _cairo_box_add_curve_to (cairo_box_t *extents, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { _cairo_box_add_point (extents, d); if (!_cairo_box_contains_point (extents, b) || !_cairo_box_contains_point (extents, c)) { cairo_status_t status; status = _cairo_spline_bound (_cairo_box_add_spline_point, extents, a, b, c, d); assert (status == CAIRO_STATUS_SUCCESS); } } void _cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti, const cairo_rectangle_t *rectf) { recti->x = floor (rectf->x); recti->y = floor (rectf->y); recti->width = ceil (rectf->x + rectf->width) - floor (rectf->x); recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-rectangular-scan-converter.c��������������0000664�0000000�0000000�00000047155�12710376503�0031011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-combsort-inline.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-list-private.h" #include "cairo-spans-private.h" #include <setjmp.h> typedef struct _rectangle { struct _rectangle *next, *prev; cairo_fixed_t left, right; cairo_fixed_t top, bottom; int32_t top_y, bottom_y; int dir; } rectangle_t; #define UNROLL3(x) x x x /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) typedef struct _pqueue { int size, max_size; rectangle_t **elements; rectangle_t *elements_embedded[1024]; } pqueue_t; typedef struct { rectangle_t **start; pqueue_t stop; rectangle_t head, tail; rectangle_t *insert_cursor; int32_t current_y; int32_t xmin, xmax; struct coverage { struct cell { struct cell *prev, *next; int x, covered, uncovered; } head, tail, *cursor; unsigned int count; cairo_freepool_t pool; } coverage; cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; cairo_half_open_span_t *spans; unsigned int num_spans; unsigned int size_spans; jmp_buf jmpbuf; } sweep_line_t; static inline int rectangle_compare_start (const rectangle_t *a, const rectangle_t *b) { int cmp; cmp = a->top_y - b->top_y; if (cmp) return cmp; return a->left - b->left; } static inline int rectangle_compare_stop (const rectangle_t *a, const rectangle_t *b) { return a->bottom_y - b->bottom_y; } static inline void pqueue_init (pqueue_t *pq) { pq->max_size = ARRAY_LENGTH (pq->elements_embedded); pq->size = 0; pq->elements = pq->elements_embedded; pq->elements[PQ_FIRST_ENTRY] = NULL; } static inline void pqueue_fini (pqueue_t *pq) { if (pq->elements != pq->elements_embedded) free (pq->elements); } static cairo_bool_t pqueue_grow (pqueue_t *pq) { rectangle_t **new_elements; pq->max_size *= 2; if (pq->elements == pq->elements_embedded) { new_elements = _cairo_malloc_ab (pq->max_size, sizeof (rectangle_t *)); if (unlikely (new_elements == NULL)) return FALSE; memcpy (new_elements, pq->elements_embedded, sizeof (pq->elements_embedded)); } else { new_elements = _cairo_realloc_ab (pq->elements, pq->max_size, sizeof (rectangle_t *)); if (unlikely (new_elements == NULL)) return FALSE; } pq->elements = new_elements; return TRUE; } static inline void pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) { rectangle_t **elements; int i, parent; if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) { if (unlikely (! pqueue_grow (&sweep->stop))) longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } elements = sweep->stop.elements; for (i = ++sweep->stop.size; i != PQ_FIRST_ENTRY && rectangle_compare_stop (rectangle, elements[parent = PQ_PARENT_INDEX (i)]) < 0; i = parent) { elements[i] = elements[parent]; } elements[i] = rectangle; } static inline void pqueue_pop (pqueue_t *pq) { rectangle_t **elements = pq->elements; rectangle_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && rectangle_compare_stop (elements[child+1], elements[child]) < 0) { child++; } if (rectangle_compare_stop (elements[child], tail) >= 0) break; elements[i] = elements[child]; } elements[i] = tail; } static inline rectangle_t * peek_stop (sweep_line_t *sweep) { return sweep->stop.elements[PQ_FIRST_ENTRY]; } CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start) static void sweep_line_init (sweep_line_t *sweep) { sweep->head.left = INT_MIN; sweep->head.next = &sweep->tail; sweep->tail.left = INT_MAX; sweep->tail.prev = &sweep->head; sweep->insert_cursor = &sweep->tail; _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell)); sweep->spans = sweep->spans_stack; sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack); sweep->coverage.head.prev = NULL; sweep->coverage.head.x = INT_MIN; sweep->coverage.tail.next = NULL; sweep->coverage.tail.x = INT_MAX; pqueue_init (&sweep->stop); } static void sweep_line_fini (sweep_line_t *sweep) { _cairo_freepool_fini (&sweep->coverage.pool); pqueue_fini (&sweep->stop); if (sweep->spans != sweep->spans_stack) free (sweep->spans); } static inline void add_cell (sweep_line_t *sweep, int x, int covered, int uncovered) { struct cell *cell; cell = sweep->coverage.cursor; if (cell->x > x) { do { UNROLL3({ if (cell->prev->x < x) break; cell = cell->prev; }) } while (TRUE); } else { if (cell->x == x) goto found; do { UNROLL3({ cell = cell->next; if (cell->x >= x) break; }) } while (TRUE); } if (x != cell->x) { struct cell *c; sweep->coverage.count++; c = _cairo_freepool_alloc (&sweep->coverage.pool); if (unlikely (c == NULL)) { longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); } cell->prev->next = c; c->prev = cell->prev; c->next = cell; cell->prev = c; c->x = x; c->covered = 0; c->uncovered = 0; cell = c; } found: cell->covered += covered; cell->uncovered += uncovered; sweep->coverage.cursor = cell; } static inline void _active_edges_to_spans (sweep_line_t *sweep) { int32_t y = sweep->current_y; rectangle_t *rectangle; int coverage, prev_coverage; int prev_x; struct cell *cell; sweep->num_spans = 0; if (sweep->head.next == &sweep->tail) return; sweep->coverage.head.next = &sweep->coverage.tail; sweep->coverage.tail.prev = &sweep->coverage.head; sweep->coverage.cursor = &sweep->coverage.tail; sweep->coverage.count = 0; /* XXX cell coverage only changes when a rectangle appears or * disappears. Try only modifying coverage at such times. */ for (rectangle = sweep->head.next; rectangle != &sweep->tail; rectangle = rectangle->next) { int height; int frac, i; if (y == rectangle->bottom_y) { height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK; if (height == 0) continue; } else height = CAIRO_FIXED_ONE; if (y == rectangle->top_y) height -= rectangle->top & CAIRO_FIXED_FRAC_MASK; height *= rectangle->dir; i = _cairo_fixed_integer_part (rectangle->left), frac = _cairo_fixed_fractional_part (rectangle->left); add_cell (sweep, i, (CAIRO_FIXED_ONE-frac) * height, frac * height); i = _cairo_fixed_integer_part (rectangle->right), frac = _cairo_fixed_fractional_part (rectangle->right); add_cell (sweep, i, -(CAIRO_FIXED_ONE-frac) * height, -frac * height); } if (2*sweep->coverage.count >= sweep->size_spans) { unsigned size; size = sweep->size_spans; while (size <= 2*sweep->coverage.count) size <<= 1; if (sweep->spans != sweep->spans_stack) free (sweep->spans); sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t)); if (unlikely (sweep->spans == NULL)) longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); sweep->size_spans = size; } prev_coverage = coverage = 0; prev_x = INT_MIN; for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) { if (cell->x != prev_x && coverage != prev_coverage) { int n = sweep->num_spans++; int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = prev_x; sweep->spans[n].inverse = 0; sweep->spans[n].coverage = c - (c >> 8); prev_coverage = coverage; } coverage += cell->covered; if (coverage != prev_coverage) { int n = sweep->num_spans++; int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = cell->x; sweep->spans[n].inverse = 0; sweep->spans[n].coverage = c - (c >> 8); prev_coverage = coverage; } coverage += cell->uncovered; prev_x = cell->x + 1; } _cairo_freepool_reset (&sweep->coverage.pool); if (sweep->num_spans) { if (prev_x <= sweep->xmax) { int n = sweep->num_spans++; int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = prev_x; sweep->spans[n].inverse = 0; sweep->spans[n].coverage = c - (c >> 8); } if (coverage && prev_x < sweep->xmax) { int n = sweep->num_spans++; sweep->spans[n].x = sweep->xmax; sweep->spans[n].inverse = 1; sweep->spans[n].coverage = 0; } } } static inline void sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle) { if (sweep->insert_cursor == rectangle) sweep->insert_cursor = rectangle->next; rectangle->prev->next = rectangle->next; rectangle->next->prev = rectangle->prev; pqueue_pop (&sweep->stop); } static inline void sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) { rectangle_t *pos; pos = sweep->insert_cursor; if (pos->left != rectangle->left) { if (pos->left > rectangle->left) { do { UNROLL3({ if (pos->prev->left < rectangle->left) break; pos = pos->prev; }) } while (TRUE); } else { do { UNROLL3({ pos = pos->next; if (pos->left >= rectangle->left) break; }); } while (TRUE); } } pos->prev->next = rectangle; rectangle->prev = pos->prev; rectangle->next = pos; pos->prev = rectangle; sweep->insert_cursor = rectangle; pqueue_push (sweep, rectangle); } static void render_rows (sweep_line_t *sweep_line, cairo_span_renderer_t *renderer, int height) { cairo_status_t status; _active_edges_to_spans (sweep_line); status = renderer->render_rows (renderer, sweep_line->current_y, height, sweep_line->spans, sweep_line->num_spans); if (unlikely (status)) longjmp (sweep_line->jmpbuf, status); } static cairo_status_t generate (cairo_rectangular_scan_converter_t *self, cairo_span_renderer_t *renderer, rectangle_t **rectangles) { sweep_line_t sweep_line; rectangle_t *start, *stop; cairo_status_t status; sweep_line_init (&sweep_line); sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x); sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x); sweep_line.start = rectangles; if ((status = setjmp (sweep_line.jmpbuf))) goto out; sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y); start = *sweep_line.start++; do { if (start->top_y != sweep_line.current_y) { render_rows (&sweep_line, renderer, start->top_y - sweep_line.current_y); sweep_line.current_y = start->top_y; } do { sweep_line_insert (&sweep_line, start); start = *sweep_line.start++; if (start == NULL) goto end; if (start->top_y != sweep_line.current_y) break; } while (TRUE); render_rows (&sweep_line, renderer, 1); stop = peek_stop (&sweep_line); while (stop->bottom_y == sweep_line.current_y) { sweep_line_delete (&sweep_line, stop); stop = peek_stop (&sweep_line); if (stop == NULL) break; } sweep_line.current_y++; while (stop != NULL && stop->bottom_y < start->top_y) { if (stop->bottom_y != sweep_line.current_y) { render_rows (&sweep_line, renderer, stop->bottom_y - sweep_line.current_y); sweep_line.current_y = stop->bottom_y; } render_rows (&sweep_line, renderer, 1); do { sweep_line_delete (&sweep_line, stop); stop = peek_stop (&sweep_line); } while (stop != NULL && stop->bottom_y == sweep_line.current_y); sweep_line.current_y++; } } while (TRUE); end: render_rows (&sweep_line, renderer, 1); stop = peek_stop (&sweep_line); while (stop->bottom_y == sweep_line.current_y) { sweep_line_delete (&sweep_line, stop); stop = peek_stop (&sweep_line); if (stop == NULL) goto out; } while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) { if (stop->bottom_y != sweep_line.current_y) { render_rows (&sweep_line, renderer, stop->bottom_y - sweep_line.current_y); sweep_line.current_y = stop->bottom_y; } render_rows (&sweep_line, renderer, 1); do { sweep_line_delete (&sweep_line, stop); stop = peek_stop (&sweep_line); if (stop == NULL) goto out; } while (stop->bottom_y == sweep_line.current_y); } out: sweep_line_fini (&sweep_line); return status; } static void generate_row(cairo_span_renderer_t *renderer, const rectangle_t *r, int y, int h, uint16_t coverage) { cairo_half_open_span_t spans[4]; unsigned int num_spans = 0; int x1 = _cairo_fixed_integer_part (r->left); int x2 = _cairo_fixed_integer_part (r->right); if (x2 > x1) { if (! _cairo_fixed_is_integer (r->left)) { spans[num_spans].x = x1; spans[num_spans].coverage = coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8; num_spans++; x1++; } if (x2 > x1) { spans[num_spans].x = x1; spans[num_spans].coverage = coverage - (coverage >> 8); num_spans++; } if (! _cairo_fixed_is_integer (r->right)) { spans[num_spans].x = x2++; spans[num_spans].coverage = coverage * _cairo_fixed_fractional_part (r->right) >> 8; num_spans++; } } else { spans[num_spans].x = x2++; spans[num_spans].coverage = coverage * (r->right - r->left) >> 8; num_spans++; } spans[num_spans].x = x2; spans[num_spans].coverage = 0; num_spans++; renderer->render_rows (renderer, y, h, spans, num_spans); } static cairo_status_t generate_box (cairo_rectangular_scan_converter_t *self, cairo_span_renderer_t *renderer) { const rectangle_t *r = self->chunks.base; int y1 = _cairo_fixed_integer_part (r->top); int y2 = _cairo_fixed_integer_part (r->bottom); if (y2 > y1) { if (! _cairo_fixed_is_integer (r->top)) { generate_row(renderer, r, y1, 1, 256 - _cairo_fixed_fractional_part (r->top)); y1++; } if (y2 > y1) generate_row(renderer, r, y1, y2-y1, 256); if (! _cairo_fixed_is_integer (r->bottom)) generate_row(renderer, r, y2, 1, _cairo_fixed_fractional_part (r->bottom)); } else generate_row(renderer, r, y1, 1, r->bottom - r->top); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_rectangular_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_rectangular_scan_converter_t *self = converter; rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)]; rectangle_t **rectangles; struct _cairo_rectangular_scan_converter_chunk *chunk; cairo_status_t status; int i, j; if (unlikely (self->num_rectangles == 0)) { return renderer->render_rows (renderer, _cairo_fixed_integer_part (self->extents.p1.y), _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y), NULL, 0); } if (self->num_rectangles == 1) return generate_box (self, renderer); rectangles = rectangles_stack; if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) { rectangles = _cairo_malloc_ab (self->num_rectangles + 1, sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } j = 0; for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { rectangle_t *rectangle; rectangle = chunk->base; for (i = 0; i < chunk->count; i++) rectangles[j++] = &rectangle[i]; } rectangle_sort (rectangles, j); rectangles[j] = NULL; status = generate (self, renderer, rectangles); if (rectangles != rectangles_stack) free (rectangles); return status; } static rectangle_t * _allocate_rectangle (cairo_rectangular_scan_converter_t *self) { rectangle_t *rectangle; struct _cairo_rectangular_scan_converter_chunk *chunk; chunk = self->tail; if (chunk->count == chunk->size) { int size; size = chunk->size * 2; chunk->next = _cairo_malloc_ab_plus_c (size, sizeof (rectangle_t), sizeof (struct _cairo_rectangular_scan_converter_chunk)); if (unlikely (chunk->next == NULL)) return NULL; chunk = chunk->next; chunk->next = NULL; chunk->count = 0; chunk->size = size; chunk->base = chunk + 1; self->tail = chunk; } rectangle = chunk->base; return rectangle + chunk->count++; } cairo_status_t _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, const cairo_box_t *box, int dir) { rectangle_t *rectangle; rectangle = _allocate_rectangle (self); if (unlikely (rectangle == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); rectangle->dir = dir; rectangle->left = MAX (box->p1.x, self->extents.p1.x); rectangle->right = MIN (box->p2.x, self->extents.p2.x); if (unlikely (rectangle->right <= rectangle->left)) { self->tail->count--; return CAIRO_STATUS_SUCCESS; } rectangle->top = MAX (box->p1.y, self->extents.p1.y); rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top); rectangle->bottom = MIN (box->p2.y, self->extents.p2.y); rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom); if (likely (rectangle->bottom > rectangle->top)) self->num_rectangles++; else self->tail->count--; return CAIRO_STATUS_SUCCESS; } static void _cairo_rectangular_scan_converter_destroy (void *converter) { cairo_rectangular_scan_converter_t *self = converter; struct _cairo_rectangular_scan_converter_chunk *chunk, *next; for (chunk = self->chunks.next; chunk != NULL; chunk = next) { next = chunk->next; free (chunk); } } void _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, const cairo_rectangle_int_t *extents) { self->base.destroy = _cairo_rectangular_scan_converter_destroy; self->base.generate = _cairo_rectangular_scan_converter_generate; _cairo_box_from_rectangle (&self->extents, extents); self->chunks.base = self->buf; self->chunks.next = NULL; self->chunks.count = 0; self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t); self->tail = &self->chunks; self->num_rectangles = 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-reference-count-private.h�����������������0000664�0000000�0000000�00000005007�12710376503�0030302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H #define CAIRO_REFRENCE_COUNT_PRIVATE_H #include "cairo-atomic-private.h" /* Encapsulate operations on the object's reference count */ typedef struct { cairo_atomic_int_t ref_count; } cairo_reference_count_t; #define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count) #define _cairo_reference_count_dec(RC) _cairo_atomic_int_dec (&(RC)->ref_count) #define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count) #define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE)) #define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count) #define CAIRO_REFERENCE_COUNT_INVALID_VALUE ((cairo_atomic_int_t) -1) #define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE} #define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE) #define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0) #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-region-private.h��������������������������0000664�0000000�0000000�00000004777�12710376503�0026516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor <otaylor@redhat.com> * Vladimir Vukicevic <vladimir@pobox.com> * Søren Sandmann <sandmann@daimi.au.dk> */ #ifndef CAIRO_REGION_PRIVATE_H #define CAIRO_REGION_PRIVATE_H #include "cairo-types-private.h" #include "cairo-reference-count-private.h" #include <pixman.h> CAIRO_BEGIN_DECLS struct _cairo_region { cairo_reference_count_t ref_count; cairo_status_t status; pixman_region32_t rgn; }; cairo_private cairo_region_t * _cairo_region_create_in_error (cairo_status_t status); cairo_private void _cairo_region_init (cairo_region_t *region); cairo_private void _cairo_region_init_rectangle (cairo_region_t *region, const cairo_rectangle_int_t *rectangle); cairo_private void _cairo_region_fini (cairo_region_t *region); cairo_private cairo_region_t * _cairo_region_create_from_boxes (const cairo_box_t *boxes, int count); cairo_private cairo_box_t * _cairo_region_get_boxes (const cairo_region_t *region, int *nbox); CAIRO_END_DECLS #endif /* CAIRO_REGION_PRIVATE_H */ �Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-region.c����������������������������������0000664�0000000�0000000�00000063607�12710376503�0025036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor <otaylor@redhat.com> * Vladimir Vukicevic <vladimir@pobox.com> * Søren Sandmann <sandmann@daimi.au.dk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-region-private.h" /* XXX need to update pixman headers to be const as appropriate */ #define CONST_CAST (pixman_region32_t *) /** * SECTION:cairo-region * @Title: Regions * @Short_Description: Representing a pixel-aligned area * * Regions are a simple graphical data type representing an area of * integer-aligned rectangles. They are often used on raster surfaces * to track areas of interest, such as change or clip areas. **/ static const cairo_region_t _cairo_region_nil = { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ }; cairo_region_t * _cairo_region_create_in_error (cairo_status_t status) { switch (status) { case CAIRO_STATUS_NO_MEMORY: return (cairo_region_t *) &_cairo_region_nil; case CAIRO_STATUS_SUCCESS: case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; /* fall-through */ case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: case CAIRO_STATUS_INVALID_STATUS: case CAIRO_STATUS_INVALID_CONTENT: case CAIRO_STATUS_INVALID_FORMAT: case CAIRO_STATUS_INVALID_VISUAL: case CAIRO_STATUS_READ_ERROR: case CAIRO_STATUS_WRITE_ERROR: case CAIRO_STATUS_FILE_NOT_FOUND: case CAIRO_STATUS_TEMP_FILE_ERROR: case CAIRO_STATUS_INVALID_STRIDE: case CAIRO_STATUS_INVALID_SIZE: case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: case CAIRO_STATUS_DEVICE_ERROR: case CAIRO_STATUS_INVALID_RESTORE: case CAIRO_STATUS_INVALID_POP_GROUP: case CAIRO_STATUS_NO_CURRENT_POINT: case CAIRO_STATUS_INVALID_MATRIX: case CAIRO_STATUS_NULL_POINTER: case CAIRO_STATUS_INVALID_STRING: case CAIRO_STATUS_INVALID_PATH_DATA: case CAIRO_STATUS_SURFACE_FINISHED: case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: case CAIRO_STATUS_INVALID_DASH: case CAIRO_STATUS_INVALID_DSC_COMMENT: case CAIRO_STATUS_INVALID_INDEX: case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: case CAIRO_STATUS_FONT_TYPE_MISMATCH: case CAIRO_STATUS_USER_FONT_IMMUTABLE: case CAIRO_STATUS_USER_FONT_ERROR: case CAIRO_STATUS_NEGATIVE_COUNT: case CAIRO_STATUS_INVALID_CLUSTERS: case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_region_t *) &_cairo_region_nil; } } /** * _cairo_region_set_error: * @region: a region * @status: a status value indicating an error * * Atomically sets region->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal * status values. * * All assignments of an error status to region->status should happen * through _cairo_region_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the * nil objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. * * Return value: the error status. **/ static cairo_status_t _cairo_region_set_error (cairo_region_t *region, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS) return CAIRO_STATUS_SUCCESS; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (®ion->status, status); return _cairo_error (status); } void _cairo_region_init (cairo_region_t *region) { VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); region->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); pixman_region32_init (®ion->rgn); } void _cairo_region_init_rectangle (cairo_region_t *region, const cairo_rectangle_int_t *rectangle) { VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); region->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); pixman_region32_init_rect (®ion->rgn, rectangle->x, rectangle->y, rectangle->width, rectangle->height); } void _cairo_region_fini (cairo_region_t *region) { assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); pixman_region32_fini (®ion->rgn); VG (VALGRIND_MAKE_MEM_NOACCESS (region, sizeof (cairo_region_t))); } /** * cairo_region_create: * * Allocates a new empty region object. * * Return value: A newly allocated #cairo_region_t. Free with * cairo_region_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_region_status(). * * Since: 1.10 **/ cairo_region_t * cairo_region_create (void) { cairo_region_t *region; region = _cairo_malloc (sizeof (cairo_region_t)); if (region == NULL) return (cairo_region_t *) &_cairo_region_nil; region->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); pixman_region32_init (®ion->rgn); return region; } slim_hidden_def (cairo_region_create); /** * cairo_region_create_rectangles: * @rects: an array of @count rectangles * @count: number of rectangles * * Allocates a new region object containing the union of all given @rects. * * Return value: A newly allocated #cairo_region_t. Free with * cairo_region_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_region_status(). * * Since: 1.10 **/ cairo_region_t * cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, int count) { pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; pixman_box32_t *pboxes = stack_pboxes; cairo_region_t *region; int i; region = _cairo_malloc (sizeof (cairo_region_t)); if (unlikely (region == NULL)) return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); region->status = CAIRO_STATUS_SUCCESS; if (count == 1) { pixman_region32_init_rect (®ion->rgn, rects->x, rects->y, rects->width, rects->height); return region; } if (count > ARRAY_LENGTH (stack_pboxes)) { pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); if (unlikely (pboxes == NULL)) { free (region); return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } for (i = 0; i < count; i++) { pboxes[i].x1 = rects[i].x; pboxes[i].y1 = rects[i].y; pboxes[i].x2 = rects[i].x + rects[i].width; pboxes[i].y2 = rects[i].y + rects[i].height; } i = pixman_region32_init_rects (®ion->rgn, pboxes, count); if (pboxes != stack_pboxes) free (pboxes); if (unlikely (i == 0)) { free (region); return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } return region; } slim_hidden_def (cairo_region_create_rectangles); cairo_region_t * _cairo_region_create_from_boxes (const cairo_box_t *boxes, int count) { cairo_region_t *region; region = _cairo_malloc (sizeof (cairo_region_t)); if (unlikely (region == NULL)) return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); region->status = CAIRO_STATUS_SUCCESS; if (! pixman_region32_init_rects (®ion->rgn, (pixman_box32_t *)boxes, count)) { free (region); return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } return region; } cairo_box_t * _cairo_region_get_boxes (const cairo_region_t *region, int *nbox) { if (region->status) { nbox = 0; return NULL; } return (cairo_box_t *) pixman_region32_rectangles (CONST_CAST ®ion->rgn, nbox); } /** * cairo_region_create_rectangle: * @rectangle: a #cairo_rectangle_int_t * * Allocates a new region object containing @rectangle. * * Return value: A newly allocated #cairo_region_t. Free with * cairo_region_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_region_status(). * * Since: 1.10 **/ cairo_region_t * cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) { cairo_region_t *region; region = _cairo_malloc (sizeof (cairo_region_t)); if (unlikely (region == NULL)) return (cairo_region_t *) &_cairo_region_nil; region->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); pixman_region32_init_rect (®ion->rgn, rectangle->x, rectangle->y, rectangle->width, rectangle->height); return region; } slim_hidden_def (cairo_region_create_rectangle); /** * cairo_region_copy: * @original: a #cairo_region_t * * Allocates a new region object copying the area from @original. * * Return value: A newly allocated #cairo_region_t. Free with * cairo_region_destroy(). This function always returns a * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_region_status(). * * Since: 1.10 **/ cairo_region_t * cairo_region_copy (const cairo_region_t *original) { cairo_region_t *copy; if (original != NULL && original->status) return (cairo_region_t *) &_cairo_region_nil; copy = cairo_region_create (); if (unlikely (copy->status)) return copy; if (original != NULL && ! pixman_region32_copy (©->rgn, CONST_CAST &original->rgn)) { cairo_region_destroy (copy); return (cairo_region_t *) &_cairo_region_nil; } return copy; } slim_hidden_def (cairo_region_copy); /** * cairo_region_reference: * @region: a #cairo_region_t * * Increases the reference count on @region by one. This prevents * @region from being destroyed until a matching call to * cairo_region_destroy() is made. * * Return value: the referenced #cairo_region_t. * * Since: 1.10 **/ cairo_region_t * cairo_region_reference (cairo_region_t *region) { if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) return NULL; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); _cairo_reference_count_inc (®ion->ref_count); return region; } slim_hidden_def (cairo_region_reference); /** * cairo_region_destroy: * @region: a #cairo_region_t * * Destroys a #cairo_region_t object created with * cairo_region_create(), cairo_region_copy(), or * or cairo_region_create_rectangle(). * * Since: 1.10 **/ void cairo_region_destroy (cairo_region_t *region) { if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); if (! _cairo_reference_count_dec_and_test (®ion->ref_count)) return; _cairo_region_fini (region); free (region); } slim_hidden_def (cairo_region_destroy); /** * cairo_region_num_rectangles: * @region: a #cairo_region_t * * Returns the number of rectangles contained in @region. * * Return value: The number of rectangles contained in @region. * * Since: 1.10 **/ int cairo_region_num_rectangles (const cairo_region_t *region) { if (region->status) return 0; return pixman_region32_n_rects (CONST_CAST ®ion->rgn); } slim_hidden_def (cairo_region_num_rectangles); /** * cairo_region_get_rectangle: * @region: a #cairo_region_t * @nth: a number indicating which rectangle should be returned * @rectangle: return location for a #cairo_rectangle_int_t * * Stores the @nth rectangle from the region in @rectangle. * * Since: 1.10 **/ void cairo_region_get_rectangle (const cairo_region_t *region, int nth, cairo_rectangle_int_t *rectangle) { pixman_box32_t *pbox; if (region->status) { rectangle->x = rectangle->y = 0; rectangle->width = rectangle->height = 0; return; } pbox = pixman_region32_rectangles (CONST_CAST ®ion->rgn, NULL) + nth; rectangle->x = pbox->x1; rectangle->y = pbox->y1; rectangle->width = pbox->x2 - pbox->x1; rectangle->height = pbox->y2 - pbox->y1; } slim_hidden_def (cairo_region_get_rectangle); /** * cairo_region_get_extents: * @region: a #cairo_region_t * @extents: rectangle into which to store the extents * * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t * * Since: 1.10 **/ void cairo_region_get_extents (const cairo_region_t *region, cairo_rectangle_int_t *extents) { pixman_box32_t *pextents; if (region->status) { extents->x = extents->y = 0; extents->width = extents->height = 0; return; } pextents = pixman_region32_extents (CONST_CAST ®ion->rgn); extents->x = pextents->x1; extents->y = pextents->y1; extents->width = pextents->x2 - pextents->x1; extents->height = pextents->y2 - pextents->y1; } slim_hidden_def (cairo_region_get_extents); /** * cairo_region_status: * @region: a #cairo_region_t * * Checks whether an error has previous occurred for this * region object. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_status (const cairo_region_t *region) { return region->status; } slim_hidden_def (cairo_region_status); /** * cairo_region_subtract: * @dst: a #cairo_region_t * @other: another #cairo_region_t * * Subtracts @other from @dst and places the result in @dst * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other) { if (dst->status) return dst->status; if (other->status) return _cairo_region_set_error (dst, other->status); if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) { return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); } return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_region_subtract); /** * cairo_region_subtract_rectangle: * @dst: a #cairo_region_t * @rectangle: a #cairo_rectangle_int_t * * Subtracts @rectangle from @dst and places the result in @dst * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_subtract_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle) { cairo_status_t status = CAIRO_STATUS_SUCCESS; pixman_region32_t region; if (dst->status) return dst->status; pixman_region32_init_rect (®ion, rectangle->x, rectangle->y, rectangle->width, rectangle->height); if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); pixman_region32_fini (®ion); return status; } slim_hidden_def (cairo_region_subtract_rectangle); /** * cairo_region_intersect: * @dst: a #cairo_region_t * @other: another #cairo_region_t * * Computes the intersection of @dst with @other and places the result in @dst * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other) { if (dst->status) return dst->status; if (other->status) return _cairo_region_set_error (dst, other->status); if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_region_intersect); /** * cairo_region_intersect_rectangle: * @dst: a #cairo_region_t * @rectangle: a #cairo_rectangle_int_t * * Computes the intersection of @dst with @rectangle and places the * result in @dst * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_intersect_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle) { cairo_status_t status = CAIRO_STATUS_SUCCESS; pixman_region32_t region; if (dst->status) return dst->status; pixman_region32_init_rect (®ion, rectangle->x, rectangle->y, rectangle->width, rectangle->height); if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); pixman_region32_fini (®ion); return status; } slim_hidden_def (cairo_region_intersect_rectangle); /** * cairo_region_union: * @dst: a #cairo_region_t * @other: another #cairo_region_t * * Computes the union of @dst with @other and places the result in @dst * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_union (cairo_region_t *dst, const cairo_region_t *other) { if (dst->status) return dst->status; if (other->status) return _cairo_region_set_error (dst, other->status); if (! pixman_region32_union (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_region_union); /** * cairo_region_union_rectangle: * @dst: a #cairo_region_t * @rectangle: a #cairo_rectangle_int_t * * Computes the union of @dst with @rectangle and places the result in @dst. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_union_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle) { cairo_status_t status = CAIRO_STATUS_SUCCESS; pixman_region32_t region; if (dst->status) return dst->status; pixman_region32_init_rect (®ion, rectangle->x, rectangle->y, rectangle->width, rectangle->height); if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); pixman_region32_fini (®ion); return status; } slim_hidden_def (cairo_region_union_rectangle); /** * cairo_region_xor: * @dst: a #cairo_region_t * @other: another #cairo_region_t * * Computes the exclusive difference of @dst with @other and places the * result in @dst. That is, @dst will be set to contain all areas that * are either in @dst or in @other, but not in both. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other) { cairo_status_t status = CAIRO_STATUS_SUCCESS; pixman_region32_t tmp; if (dst->status) return dst->status; if (other->status) return _cairo_region_set_error (dst, other->status); pixman_region32_init (&tmp); /* XXX: get an xor function into pixman */ if (! pixman_region32_subtract (&tmp, CONST_CAST &other->rgn, &dst->rgn) || ! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn) || ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); pixman_region32_fini (&tmp); return status; } slim_hidden_def (cairo_region_xor); /** * cairo_region_xor_rectangle: * @dst: a #cairo_region_t * @rectangle: a #cairo_rectangle_int_t * * Computes the exclusive difference of @dst with @rectangle and places the * result in @dst. That is, @dst will be set to contain all areas that are * either in @dst or in @rectangle, but not in both. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY * * Since: 1.10 **/ cairo_status_t cairo_region_xor_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle) { cairo_status_t status = CAIRO_STATUS_SUCCESS; pixman_region32_t region, tmp; if (dst->status) return dst->status; pixman_region32_init_rect (®ion, rectangle->x, rectangle->y, rectangle->width, rectangle->height); pixman_region32_init (&tmp); /* XXX: get an xor function into pixman */ if (! pixman_region32_subtract (&tmp, ®ion, &dst->rgn) || ! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion) || ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); pixman_region32_fini (&tmp); pixman_region32_fini (®ion); return status; } slim_hidden_def (cairo_region_xor_rectangle); /** * cairo_region_is_empty: * @region: a #cairo_region_t * * Checks whether @region is empty. * * Return value: %TRUE if @region is empty, %FALSE if it isn't. * * Since: 1.10 **/ cairo_bool_t cairo_region_is_empty (const cairo_region_t *region) { if (region->status) return TRUE; return ! pixman_region32_not_empty (CONST_CAST ®ion->rgn); } slim_hidden_def (cairo_region_is_empty); /** * cairo_region_translate: * @region: a #cairo_region_t * @dx: Amount to translate in the x direction * @dy: Amount to translate in the y direction * * Translates @region by (@dx, @dy). * * Since: 1.10 **/ void cairo_region_translate (cairo_region_t *region, int dx, int dy) { if (region->status) return; pixman_region32_translate (®ion->rgn, dx, dy); } slim_hidden_def (cairo_region_translate); /** * cairo_region_overlap_t: * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region. (Since 1.10) * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region. (Since 1.10) * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and * partially outside the region. (Since 1.10) * * Used as the return value for cairo_region_contains_rectangle(). * * Since: 1.10 **/ /** * cairo_region_contains_rectangle: * @region: a #cairo_region_t * @rectangle: a #cairo_rectangle_int_t * * Checks whether @rectangle is inside, outside or partially contained * in @region * * Return value: * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. * * Since: 1.10 **/ cairo_region_overlap_t cairo_region_contains_rectangle (const cairo_region_t *region, const cairo_rectangle_int_t *rectangle) { pixman_box32_t pbox; pixman_region_overlap_t poverlap; if (region->status) return CAIRO_REGION_OVERLAP_OUT; pbox.x1 = rectangle->x; pbox.y1 = rectangle->y; pbox.x2 = rectangle->x + rectangle->width; pbox.y2 = rectangle->y + rectangle->height; poverlap = pixman_region32_contains_rectangle (CONST_CAST ®ion->rgn, &pbox); switch (poverlap) { default: case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; } } slim_hidden_def (cairo_region_contains_rectangle); /** * cairo_region_contains_point: * @region: a #cairo_region_t * @x: the x coordinate of a point * @y: the y coordinate of a point * * Checks whether (@x, @y) is contained in @region. * * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. * * Since: 1.10 **/ cairo_bool_t cairo_region_contains_point (const cairo_region_t *region, int x, int y) { pixman_box32_t box; if (region->status) return FALSE; return pixman_region32_contains_point (CONST_CAST ®ion->rgn, x, y, &box); } slim_hidden_def (cairo_region_contains_point); /** * cairo_region_equal: * @a: a #cairo_region_t or %NULL * @b: a #cairo_region_t or %NULL * * Compares whether region_a is equivalent to region_b. %NULL as an argument * is equal to itself, but not to any non-%NULL region. * * Return value: %TRUE if both regions contained the same coverage, * %FALSE if it is not or any region is in an error status. * * Since: 1.10 **/ cairo_bool_t cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b) { /* error objects are never equal */ if ((a != NULL && a->status) || (b != NULL && b->status)) return FALSE; if (a == b) return TRUE; if (a == NULL || b == NULL) return FALSE; return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn); } slim_hidden_def (cairo_region_equal); �������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-rtree-private.h���������������������������0000664�0000000�0000000�00000010134�12710376503�0026334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> * */ #ifndef CAIRO_RTREE_PRIVATE_H #define CAIRO_RTREE_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-types-private.h" #include "cairo-freelist-private.h" #include "cairo-list-inline.h" enum { CAIRO_RTREE_NODE_AVAILABLE, CAIRO_RTREE_NODE_DIVIDED, CAIRO_RTREE_NODE_OCCUPIED, }; typedef struct _cairo_rtree_node { struct _cairo_rtree_node *children[4], *parent; cairo_list_t link; uint16_t pinned; uint16_t state; uint16_t x, y; uint16_t width, height; } cairo_rtree_node_t; typedef struct _cairo_rtree { cairo_rtree_node_t root; int min_size; cairo_list_t pinned; cairo_list_t available; cairo_list_t evictable; void (*destroy) (cairo_rtree_node_t *); cairo_freepool_t node_freepool; } cairo_rtree_t; cairo_private cairo_rtree_node_t * _cairo_rtree_node_create (cairo_rtree_t *rtree, cairo_rtree_node_t *parent, int x, int y, int width, int height); cairo_private cairo_status_t _cairo_rtree_node_insert (cairo_rtree_t *rtree, cairo_rtree_node_t *node, int width, int height, cairo_rtree_node_t **out); cairo_private void _cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node); cairo_private void _cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node); cairo_private void _cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node); cairo_private void _cairo_rtree_init (cairo_rtree_t *rtree, int width, int height, int min_size, int node_size, void (*destroy)(cairo_rtree_node_t *)); cairo_private cairo_int_status_t _cairo_rtree_insert (cairo_rtree_t *rtree, int width, int height, cairo_rtree_node_t **out); cairo_private cairo_int_status_t _cairo_rtree_evict_random (cairo_rtree_t *rtree, int width, int height, cairo_rtree_node_t **out); cairo_private void _cairo_rtree_foreach (cairo_rtree_t *rtree, void (*func)(cairo_rtree_node_t *, void *data), void *data); static inline void * _cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node) { assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); if (! node->pinned) { cairo_list_move (&node->link, &rtree->pinned); node->pinned = 1; } return node; } cairo_private void _cairo_rtree_unpin (cairo_rtree_t *rtree); cairo_private void _cairo_rtree_reset (cairo_rtree_t *rtree); cairo_private void _cairo_rtree_fini (cairo_rtree_t *rtree); #endif /* CAIRO_RTREE_PRIVATE_H */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-rtree.c�����������������������������������0000664�0000000�0000000�00000024250�12710376503�0024663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> * */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-rtree-private.h" cairo_rtree_node_t * _cairo_rtree_node_create (cairo_rtree_t *rtree, cairo_rtree_node_t *parent, int x, int y, int width, int height) { cairo_rtree_node_t *node; node = _cairo_freepool_alloc (&rtree->node_freepool); if (node == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } node->children[0] = NULL; node->parent = parent; node->state = CAIRO_RTREE_NODE_AVAILABLE; node->pinned = FALSE; node->x = x; node->y = y; node->width = width; node->height = height; cairo_list_add (&node->link, &rtree->available); return node; } void _cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) { int i; cairo_list_del (&node->link); if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { rtree->destroy (node); } else { for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, node->children[i]); } _cairo_freepool_free (&rtree->node_freepool, node); } void _cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node) { int i; do { assert (node->state == CAIRO_RTREE_NODE_DIVIDED); for (i = 0; i < 4 && node->children[i] != NULL; i++) if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE) return; for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, node->children[i]); node->children[0] = NULL; node->state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_move (&node->link, &rtree->available); } while ((node = node->parent) != NULL); } cairo_status_t _cairo_rtree_node_insert (cairo_rtree_t *rtree, cairo_rtree_node_t *node, int width, int height, cairo_rtree_node_t **out) { int w, h, i; assert (node->state == CAIRO_RTREE_NODE_AVAILABLE); assert (node->pinned == FALSE); if (node->width - width > rtree->min_size || node->height - height > rtree->min_size) { w = node->width - width; h = node->height - height; i = 0; node->children[i] = _cairo_rtree_node_create (rtree, node, node->x, node->y, width, height); if (unlikely (node->children[i] == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); i++; if (w > rtree->min_size) { node->children[i] = _cairo_rtree_node_create (rtree, node, node->x + width, node->y, w, height); if (unlikely (node->children[i] == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); i++; } if (h > rtree->min_size) { node->children[i] = _cairo_rtree_node_create (rtree, node, node->x, node->y + height, width, h); if (unlikely (node->children[i] == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); i++; if (w > rtree->min_size) { node->children[i] = _cairo_rtree_node_create (rtree, node, node->x + width, node->y + height, w, h); if (unlikely (node->children[i] == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); i++; } } if (i < 4) node->children[i] = NULL; node->state = CAIRO_RTREE_NODE_DIVIDED; cairo_list_move (&node->link, &rtree->evictable); node = node->children[0]; } node->state = CAIRO_RTREE_NODE_OCCUPIED; cairo_list_move (&node->link, &rtree->evictable); *out = node; return CAIRO_STATUS_SUCCESS; } void _cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node) { assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); assert (node->pinned == FALSE); rtree->destroy (node); node->state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_move (&node->link, &rtree->available); _cairo_rtree_node_collapse (rtree, node->parent); } cairo_int_status_t _cairo_rtree_insert (cairo_rtree_t *rtree, int width, int height, cairo_rtree_node_t **out) { cairo_rtree_node_t *node; cairo_list_foreach_entry (node, cairo_rtree_node_t, &rtree->available, link) { if (node->width >= width && node->height >= height) return _cairo_rtree_node_insert (rtree, node, width, height, out); } return CAIRO_INT_STATUS_UNSUPPORTED; } static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } cairo_int_status_t _cairo_rtree_evict_random (cairo_rtree_t *rtree, int width, int height, cairo_rtree_node_t **out) { cairo_int_status_t ret = CAIRO_INT_STATUS_UNSUPPORTED; cairo_rtree_node_t *node, *next; cairo_list_t tmp_pinned; int i, cnt; cairo_list_init (&tmp_pinned); /* propagate pinned from children to root */ cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t, &rtree->pinned, link) { node = node->parent; while (node && ! node->pinned) { node->pinned = 1; cairo_list_move (&node->link, &tmp_pinned); node = node->parent; } } cnt = 0; cairo_list_foreach_entry (node, cairo_rtree_node_t, &rtree->evictable, link) { if (node->width >= width && node->height >= height) cnt++; } if (cnt == 0) goto out; cnt = hars_petruska_f54_1_random () % cnt; cairo_list_foreach_entry (node, cairo_rtree_node_t, &rtree->evictable, link) { if (node->width >= width && node->height >= height && cnt-- == 0) { if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { rtree->destroy (node); } else { for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, node->children[i]); node->children[0] = NULL; } node->state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_move (&node->link, &rtree->available); *out = node; ret = CAIRO_STATUS_SUCCESS; break; } } out: while (! cairo_list_is_empty (&tmp_pinned)) { node = cairo_list_first_entry (&tmp_pinned, cairo_rtree_node_t, link); node->pinned = 0; cairo_list_move (&node->link, &rtree->evictable); } return ret; } void _cairo_rtree_unpin (cairo_rtree_t *rtree) { while (! cairo_list_is_empty (&rtree->pinned)) { cairo_rtree_node_t *node = cairo_list_first_entry (&rtree->pinned, cairo_rtree_node_t, link); node->pinned = 0; cairo_list_move (&node->link, &rtree->evictable); } } void _cairo_rtree_init (cairo_rtree_t *rtree, int width, int height, int min_size, int node_size, void (*destroy) (cairo_rtree_node_t *)) { assert (node_size >= (int) sizeof (cairo_rtree_node_t)); _cairo_freepool_init (&rtree->node_freepool, node_size); cairo_list_init (&rtree->available); cairo_list_init (&rtree->pinned); cairo_list_init (&rtree->evictable); rtree->min_size = min_size; rtree->destroy = destroy; memset (&rtree->root, 0, sizeof (rtree->root)); rtree->root.width = width; rtree->root.height = height; rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_add (&rtree->root.link, &rtree->available); } void _cairo_rtree_reset (cairo_rtree_t *rtree) { int i; if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { rtree->destroy (&rtree->root); } else { for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); rtree->root.children[0] = NULL; } cairo_list_init (&rtree->available); cairo_list_init (&rtree->evictable); cairo_list_init (&rtree->pinned); rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; rtree->root.pinned = FALSE; cairo_list_add (&rtree->root.link, &rtree->available); } static void _cairo_rtree_node_foreach (cairo_rtree_node_t *node, void (*func)(cairo_rtree_node_t *, void *data), void *data) { int i; for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_foreach(node->children[i], func, data); func(node, data); } void _cairo_rtree_foreach (cairo_rtree_t *rtree, void (*func)(cairo_rtree_node_t *, void *data), void *data) { int i; if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { func(&rtree->root, data); } else { for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) _cairo_rtree_node_foreach (rtree->root.children[i], func, data); } } void _cairo_rtree_fini (cairo_rtree_t *rtree) { int i; if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { rtree->destroy (&rtree->root); } else { for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); } _cairo_freepool_fini (&rtree->node_freepool); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-scaled-font-private.h���������������������0000664�0000000�0000000�00000015105�12710376503�0027415�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SCALED_FONT_PRIVATE_H #define CAIRO_SCALED_FONT_PRIVATE_H #include "cairo.h" #include "cairo-types-private.h" #include "cairo-list-private.h" #include "cairo-mutex-type-private.h" #include "cairo-reference-count-private.h" CAIRO_BEGIN_DECLS typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t; struct _cairo_scaled_font { /* For most cairo objects, the rule for multiple threads is that * the user is responsible for any locking if the same object is * manipulated from multiple threads simultaneously. * * However, with the caching that cairo does for scaled fonts, a * user can easily end up with the same cairo_scaled_font object * being manipulated from multiple threads without the user ever * being aware of this, (and in fact, unable to control it). * * So, as a special exception, the cairo implementation takes care * of all locking needed for cairo_scaled_font_t. Most of what is * in the scaled font is immutable, (which is what allows for the * sharing in the first place). The things that are modified and * the locks protecting them are as follows: * * 1. The reference count (scaled_font->ref_count) * * Modifications to the reference count are protected by the * _cairo_scaled_font_map_mutex. This is because the reference * count of a scaled font is intimately related with the font * map itself, (and the magic holdovers array). * * 2. The cache of glyphs (scaled_font->glyphs) * 3. The backend private data (scaled_font->surface_backend, * scaled_font->surface_private) * * Modifications to these fields are protected with locks on * scaled_font->mutex in the generic scaled_font code. */ cairo_hash_entry_t hash_entry; /* useful bits for _cairo_scaled_font_nil */ cairo_status_t status; cairo_reference_count_t ref_count; cairo_user_data_array_t user_data; cairo_font_face_t *original_font_face; /* may be NULL */ /* hash key members */ cairo_font_face_t *font_face; /* may be NULL */ cairo_matrix_t font_matrix; /* font space => user space */ cairo_matrix_t ctm; /* user space => device space */ cairo_font_options_t options; unsigned int placeholder : 1; /* protected by fontmap mutex */ unsigned int holdover : 1; unsigned int finished : 1; /* "live" scaled_font members */ cairo_matrix_t scale; /* font space => device space */ cairo_matrix_t scale_inverse; /* device space => font space */ double max_scale; /* maximum x/y expansion of scale */ cairo_font_extents_t extents; /* user space */ cairo_font_extents_t fs_extents; /* font space */ /* The mutex protects modification to all subsequent fields. */ cairo_mutex_t mutex; cairo_hash_table_t *glyphs; cairo_list_t glyph_pages; cairo_bool_t cache_frozen; cairo_bool_t global_cache_frozen; cairo_list_t dev_privates; /* font backend managing this scaled font */ const cairo_scaled_font_backend_t *backend; cairo_list_t link; }; struct _cairo_scaled_font_private { cairo_list_t link; const void *key; void (*destroy) (cairo_scaled_font_private_t *, cairo_scaled_font_t *); }; struct _cairo_scaled_glyph { cairo_hash_entry_t hash_entry; cairo_text_extents_t metrics; /* user-space metrics */ cairo_text_extents_t fs_metrics; /* font-space metrics */ cairo_box_t bbox; /* device-space bounds */ int16_t x_advance; /* device-space rounded X advance */ int16_t y_advance; /* device-space rounded Y advance */ unsigned int has_info; cairo_image_surface_t *surface; /* device-space image */ cairo_path_fixed_t *path; /* device-space outline */ cairo_surface_t *recording_surface; /* device-space recording-surface */ const void *dev_private_key; void *dev_private; cairo_list_t dev_privates; }; struct _cairo_scaled_glyph_private { cairo_list_t link; const void *key; void (*destroy) (cairo_scaled_glyph_private_t *, cairo_scaled_glyph_t *, cairo_scaled_font_t *); }; cairo_private cairo_scaled_font_private_t * _cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font, const void *key); cairo_private void _cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font, cairo_scaled_font_private_t *priv, const void *key, void (*destroy) (cairo_scaled_font_private_t *, cairo_scaled_font_t *)); cairo_private cairo_scaled_glyph_private_t * _cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph, const void *key); cairo_private void _cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_private_t *priv, const void *key, void (*destroy) (cairo_scaled_glyph_private_t *, cairo_scaled_glyph_t *, cairo_scaled_font_t *)); CAIRO_END_DECLS #endif /* CAIRO_SCALED_FONT_PRIVATE_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-scaled-font-subsets-private.h�������������0000664�0000000�0000000�00000070037�12710376503�0031110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H #define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET typedef struct _cairo_scaled_font_subsets_glyph { unsigned int font_id; unsigned int subset_id; unsigned int subset_glyph_index; cairo_bool_t is_scaled; cairo_bool_t is_composite; cairo_bool_t is_latin; double x_advance; double y_advance; cairo_bool_t utf8_is_mapped; uint32_t unicode; } cairo_scaled_font_subsets_glyph_t; /** * _cairo_scaled_font_subsets_create_scaled: * * Create a new #cairo_scaled_font_subsets_t object which can be used * to create subsets of any number of #cairo_scaled_font_t * objects. This allows the (arbitrarily large and sparse) glyph * indices of a #cairo_scaled_font_t to be mapped to one or more font * subsets with glyph indices packed into the range * [0 .. max_glyphs_per_subset). * * Return value: a pointer to the newly creates font subsets. The * caller owns this object and should call * _cairo_scaled_font_subsets_destroy() when done with it. **/ cairo_private cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_scaled (void); /** * _cairo_scaled_font_subsets_create_simple: * * Create a new #cairo_scaled_font_subsets_t object which can be used * to create font subsets suitable for embedding as Postscript or PDF * simple fonts. * * Glyphs with an outline path available will be mapped to one font * subset for each font face. Glyphs from bitmap fonts will mapped to * separate font subsets for each #cairo_scaled_font_t object. * * The maximum number of glyphs per subset is 256. Each subset * reserves the first glyph for the .notdef glyph. * * Return value: a pointer to the newly creates font subsets. The * caller owns this object and should call * _cairo_scaled_font_subsets_destroy() when done with it. **/ cairo_private cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_simple (void); /** * _cairo_scaled_font_subsets_create_composite: * * Create a new #cairo_scaled_font_subsets_t object which can be used * to create font subsets suitable for embedding as Postscript or PDF * composite fonts. * * Glyphs with an outline path available will be mapped to one font * subset for each font face. Each unscaled subset has a maximum of * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs. * * Glyphs from bitmap fonts will mapped to separate font subsets for * each #cairo_scaled_font_t object. Each unscaled subset has a maximum * of 256 glyphs. * * Each subset reserves the first glyph for the .notdef glyph. * * Return value: a pointer to the newly creates font subsets. The * caller owns this object and should call * _cairo_scaled_font_subsets_destroy() when done with it. **/ cairo_private cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_composite (void); /** * _cairo_scaled_font_subsets_destroy: * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed * * Destroys @font_subsets and all resources associated with it. **/ cairo_private void _cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets); /** * _cairo_scaled_font_subsets_enable_latin_subset: * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed * @use_latin: a #cairo_bool_t indicating if a latin subset is to be used * * If enabled, all CP1252 characters will be placed in a separate * 8-bit latin subset. **/ cairo_private void _cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets, cairo_bool_t use_latin); /** * _cairo_scaled_font_subsets_map_glyph: * @font_subsets: a #cairo_scaled_font_subsets_t * @scaled_font: the font of the glyph to be mapped * @scaled_font_glyph_index: the index of the glyph to be mapped * @utf8: a string of text encoded in UTF-8 * @utf8_len: length of @utf8 in bytes * @subset_glyph_ret: return structure containing subset font and glyph id * * Map a glyph from a #cairo_scaled_font to a new index within a * subset of that font. The mapping performed is from the tuple: * * (scaled_font, scaled_font_glyph_index) * * to the tuple: * * (font_id, subset_id, subset_glyph_index) * * This mapping is 1:1. If the input tuple has previously mapped, the * the output tuple previously returned will be returned again. * * Otherwise, the return tuple will be constructed as follows: * * 1) There is a 1:1 correspondence between the input scaled_font * value and the output font_id value. If no mapping has been * previously performed with the scaled_font value then the * smallest unused font_id value will be returned. * * 2) Within the set of output tuples of the same font_id value the * smallest value of subset_id will be returned such that * subset_glyph_index does not exceed max_glyphs_per_subset (as * passed to _cairo_scaled_font_subsets_create()) and that the * resulting tuple is unique. * * 3) The smallest value of subset_glyph_index is returned such that * the resulting tuple is unique. * * The net result is that any #cairo_scaled_font_t will be represented * by one or more font subsets. Each subset is effectively a tuple of * (scaled_font, font_id, subset_id) and within each subset there * exists a mapping of scaled_glyph_font_index to subset_glyph_index. * * This final description of a font subset is the same representation * used by #cairo_scaled_font_subset_t as provided by * _cairo_scaled_font_subsets_foreach. * * @utf8 and @utf8_len specify a string of unicode characters that the * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in * @subset_glyph_ret is %TRUE, the font subsetting will (where index to * unicode mapping is supported) ensure that @scaled_font_glyph_index * maps to @utf8. If @utf8_is_mapped is %FALSE, * @scaled_font_glyph_index has already been mapped to a different * unicode string. * * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are: * * @font_id: The font ID of the mapped glyph * @subset_id : The subset ID of the mapped glyph within the @font_id * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font * subset is created for each font scale used. If false, the outline of the mapped glyph * is available. One font subset for each font face is created. * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain * the x and y advance for the mapped glyph in device space. * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for * the the mapped glyph from an unhinted 1 point font. * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph() * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already * mapped to a different utf8 string. * @unicode: the unicode character mapped to this glyph by the font backend. * * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero * value indicating an error. Possible errors include * %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, const char * utf8, int utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph_ret); typedef cairo_int_status_t (*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset, void *closure); /** * _cairo_scaled_font_subsets_foreach_scaled: * @font_subsets: a #cairo_scaled_font_subsets_t * @font_subset_callback: a function to be called for each font subset * @closure: closure data for the callback function * * Iterate over each unique scaled font subset as created by calls to * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by * unique pairs of (font_id, subset_id) as returned by * _cairo_scaled_font_subsets_map_glyph(). * * For each subset, @font_subset_callback will be called and will be * provided with both a #cairo_scaled_font_subset_t object containing * all the glyphs in the subset as well as the value of @closure. * * The #cairo_scaled_font_subset_t object contains the scaled_font, * the font_id, and the subset_id corresponding to all glyphs * belonging to the subset. In addition, it contains an array providing * a mapping between subset glyph indices and the original scaled font * glyph indices. * * The index of the array corresponds to subset_glyph_index values * returned by _cairo_scaled_font_subsets_map_glyph() while the * values of the array correspond to the scaled_font_glyph_index * values passed as input to the same function. * * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero * value indicating an error. Possible errors include * %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure); /** * _cairo_scaled_font_subsets_foreach_unscaled: * @font_subsets: a #cairo_scaled_font_subsets_t * @font_subset_callback: a function to be called for each font subset * @closure: closure data for the callback function * * Iterate over each unique unscaled font subset as created by calls to * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by * unique pairs of (font_id, subset_id) as returned by * _cairo_scaled_font_subsets_map_glyph(). * * For each subset, @font_subset_callback will be called and will be * provided with both a #cairo_scaled_font_subset_t object containing * all the glyphs in the subset as well as the value of @closure. * * The #cairo_scaled_font_subset_t object contains the scaled_font, * the font_id, and the subset_id corresponding to all glyphs * belonging to the subset. In addition, it contains an array providing * a mapping between subset glyph indices and the original scaled font * glyph indices. * * The index of the array corresponds to subset_glyph_index values * returned by _cairo_scaled_font_subsets_map_glyph() while the * values of the array correspond to the scaled_font_glyph_index * values passed as input to the same function. * * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero * value indicating an error. Possible errors include * %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure); /** * _cairo_scaled_font_subsets_foreach_user: * @font_subsets: a #cairo_scaled_font_subsets_t * @font_subset_callback: a function to be called for each font subset * @closure: closure data for the callback function * * Iterate over each unique scaled font subset as created by calls to * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by * unique pairs of (font_id, subset_id) as returned by * _cairo_scaled_font_subsets_map_glyph(). * * For each subset, @font_subset_callback will be called and will be * provided with both a #cairo_scaled_font_subset_t object containing * all the glyphs in the subset as well as the value of @closure. * * The #cairo_scaled_font_subset_t object contains the scaled_font, * the font_id, and the subset_id corresponding to all glyphs * belonging to the subset. In addition, it contains an array providing * a mapping between subset glyph indices and the original scaled font * glyph indices. * * The index of the array corresponds to subset_glyph_index values * returned by _cairo_scaled_font_subsets_map_glyph() while the * values of the array correspond to the scaled_font_glyph_index * values passed as input to the same function. * * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero * value indicating an error. Possible errors include * %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure); /** * _cairo_scaled_font_subset_create_glyph_names: * @font_subsets: a #cairo_scaled_font_subsets_t * * Create an array of strings containing the glyph name for each glyph * in @font_subsets. The array as store in font_subsets->glyph_names. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support * mapping the glyph indices to unicode characters. Possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_int_status_t _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset); typedef struct _cairo_cff_subset { char *family_name_utf8; char *ps_name; double *widths; double x_min, y_min, x_max, y_max; double ascent, descent; char *data; unsigned long data_length; } cairo_cff_subset_t; /** * _cairo_cff_subset_init: * @cff_subset: a #cairo_cff_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a * cff file corresponding to @font_subset and initialize * @cff_subset with information about the subset and the cff * data. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a * cff file, or an non-zero value indicating an error. Possible * errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, const char *name, cairo_scaled_font_subset_t *font_subset); /** * _cairo_cff_subset_fini: * @cff_subset: a #cairo_cff_subset_t * * Free all resources associated with @cff_subset. After this * call, @cff_subset should not be used again without a * subsequent call to _cairo_cff_subset_init() again first. **/ cairo_private void _cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset); /** * _cairo_cff_scaled_font_is_cid_cff: * @scaled_font: a #cairo_scaled_font_t * * Return %TRUE if @scaled_font is a CID CFF font, otherwise return %FALSE. **/ cairo_private cairo_bool_t _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font); /** * _cairo_cff_fallback_init: * @cff_subset: a #cairo_cff_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a cff * file corresponding to @font_subset and initialize @cff_subset * with information about the subset and the cff data. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a * cff file, or an non-zero value indicating an error. Possible * errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, const char *name, cairo_scaled_font_subset_t *font_subset); /** * _cairo_cff_fallback_fini: * @cff_subset: a #cairo_cff_subset_t * * Free all resources associated with @cff_subset. After this * call, @cff_subset should not be used again without a * subsequent call to _cairo_cff_subset_init() again first. **/ cairo_private void _cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); typedef struct _cairo_truetype_subset { char *family_name_utf8; char *ps_name; double *widths; double x_min, y_min, x_max, y_max; double ascent, descent; unsigned char *data; unsigned long data_length; unsigned long *string_offsets; unsigned long num_string_offsets; } cairo_truetype_subset_t; /** * _cairo_truetype_subset_init_ps: * @truetype_subset: a #cairo_truetype_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a * truetype file corresponding to @font_subset and initialize * @truetype_subset with information about the subset and the truetype * data. The generated font will be suitable for embedding in * PostScript. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a * truetype file, or an non-zero value indicating an error. Possible * errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset); /** * _cairo_truetype_subset_init_pdf: * @truetype_subset: a #cairo_truetype_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a * truetype file corresponding to @font_subset and initialize * @truetype_subset with information about the subset and the truetype * data. The generated font will be suitable for embedding in * PDF. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a * truetype file, or an non-zero value indicating an error. Possible * errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset); /** * _cairo_truetype_subset_fini: * @truetype_subset: a #cairo_truetype_subset_t * * Free all resources associated with @truetype_subset. After this * call, @truetype_subset should not be used again without a * subsequent call to _cairo_truetype_subset_init() again first. **/ cairo_private void _cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset); cairo_private const char * _cairo_ps_standard_encoding_to_glyphname (int glyph); cairo_private int _cairo_unicode_to_winansi (unsigned long unicode); cairo_private const char * _cairo_winansi_to_glyphname (int glyph); typedef struct _cairo_type1_subset { char *base_font; double *widths; double x_min, y_min, x_max, y_max; double ascent, descent; char *data; unsigned long header_length; unsigned long data_length; unsigned long trailer_length; } cairo_type1_subset_t; /** * _cairo_type1_subset_init: * @type1_subset: a #cairo_type1_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * @hex_encode: if true the encrypted portion of the font is hex encoded * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a type1 * file corresponding to @font_subset and initialize @type1_subset * with information about the subset and the type1 data. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 * file, or an non-zero value indicating an error. Possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_type1_subset_init (cairo_type1_subset_t *type_subset, const char *name, cairo_scaled_font_subset_t *font_subset, cairo_bool_t hex_encode); /** * _cairo_type1_subset_fini: * @type1_subset: a #cairo_type1_subset_t * * Free all resources associated with @type1_subset. After this call, * @type1_subset should not be used again without a subsequent call to * _cairo_truetype_type1_init() again first. **/ cairo_private void _cairo_type1_subset_fini (cairo_type1_subset_t *subset); /** * _cairo_type1_scaled_font_is_type1: * @scaled_font: a #cairo_scaled_font_t * * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE. **/ cairo_private cairo_bool_t _cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); /** * _cairo_type1_fallback_init_binary: * @type1_subset: a #cairo_type1_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a type1 * file corresponding to @font_subset and initialize @type1_subset * with information about the subset and the type1 data. The encrypted * part of the font is binary encoded. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 * file, or an non-zero value indicating an error. Possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset, const char *name, cairo_scaled_font_subset_t *font_subset); /** * _cairo_type1_fallback_init_hex: * @type1_subset: a #cairo_type1_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate a type1 * file corresponding to @font_subset and initialize @type1_subset * with information about the subset and the type1 data. The encrypted * part of the font is hex encoded. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 * file, or an non-zero value indicating an error. Possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_type1_fallback_init_hex (cairo_type1_subset_t *type_subset, const char *name, cairo_scaled_font_subset_t *font_subset); /** * _cairo_type1_fallback_fini: * @type1_subset: a #cairo_type1_subset_t * * Free all resources associated with @type1_subset. After this call, * @type1_subset should not be used again without a subsequent call to * _cairo_truetype_type1_init() again first. **/ cairo_private void _cairo_type1_fallback_fini (cairo_type1_subset_t *subset); typedef struct _cairo_type2_charstrings { int *widths; long x_min, y_min, x_max, y_max; long ascent, descent; cairo_array_t charstrings; } cairo_type2_charstrings_t; /** * _cairo_type2_charstrings_init: * @type2_subset: a #cairo_type2_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) generate type2 * charstrings to @font_subset and initialize @type2_subset * with information about the subset. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2 * charstrings, or an non-zero value indicating an error. Possible errors * include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t _cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings, cairo_scaled_font_subset_t *font_subset); /** * _cairo_type2_charstrings_fini: * @subset: a #cairo_type2_charstrings_t * * Free all resources associated with @type2_charstring. After this call, * @type2_charstring should not be used again without a subsequent call to * _cairo_type2_charstring_init() again first. **/ cairo_private void _cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings); /** * _cairo_truetype_index_to_ucs4: * @scaled_font: the #cairo_scaled_font_t * @index: the glyph index * @ucs4: return value for the unicode value of the glyph * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) assign * the unicode character of the glyph to @ucs4. * * If mapping glyph indices to unicode is supported but the unicode * value of the specified glyph is not available, @ucs4 is set to -1. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode * is not supported. Possible errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_int_status_t _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, unsigned long index, uint32_t *ucs4); /** * _cairo_truetype_read_font_name: * @scaled_font: the #cairo_scaled_font_t * @ps_name: returns the PostScript name of the font * or %NULL if the name could not be found. * @font_name: returns the font name or %NULL if the name could not be found. * * If possible (depending on the format of the underlying * #cairo_scaled_font_t and the font backend in use) read the * PostScript and Font names from a TrueType/OpenType font. * * The font name is the full name of the font eg "DejaVu Sans Bold". * The PostScript name is a shortened name with spaces removed * suitable for use as the font name in a PS or PDF file eg * "DejaVuSans-Bold". * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType * or the name table is not present. Possible errors include * %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_int_status_t _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, char **ps_name, char **font_name); /** * _cairo_truetype_get_style: * @scaled_font: the #cairo_scaled_font_t * @weight: returns the font weight from the OS/2 table * @bold: returns true if font is bold * @italic: returns true if font is italic * * If the font is a truetype/opentype font with an OS/2 table, get the * weight, bold, and italic data from the OS/2 table. The weight * values have the same meaning as the lfWeight field of the Windows * LOGFONT structure. Refer to the TrueType Specification for * definition of the weight values. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType * or the OS/2 table is not present. **/ cairo_private cairo_int_status_t _cairo_truetype_get_style (cairo_scaled_font_t *scaled_font, int *weight, cairo_bool_t *bold, cairo_bool_t *italic); #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-scaled-font-subsets.c���������������������0000664�0000000�0000000�00000115572�12710376503�0027437�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California * Copyright © 2005 Red Hat, Inc * Copyright © 2006 Keith Packard * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> * Keith Packard <keithp@keithp.com> * Adrian Johnson <ajohnson@redneon.com> */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-scaled-font-subsets-private.h" #include "cairo-user-font-private.h" #define MAX_GLYPHS_PER_SIMPLE_FONT 256 #define MAX_GLYPHS_PER_COMPOSITE_FONT 65536 typedef enum { CAIRO_SUBSETS_SCALED, CAIRO_SUBSETS_SIMPLE, CAIRO_SUBSETS_COMPOSITE } cairo_subsets_type_t; typedef enum { CAIRO_SUBSETS_FOREACH_UNSCALED, CAIRO_SUBSETS_FOREACH_SCALED, CAIRO_SUBSETS_FOREACH_USER } cairo_subsets_foreach_type_t; typedef struct _cairo_sub_font { cairo_hash_entry_t base; cairo_bool_t is_scaled; cairo_bool_t is_composite; cairo_bool_t is_user; cairo_bool_t use_latin_subset; cairo_scaled_font_subsets_t *parent; cairo_scaled_font_t *scaled_font; unsigned int font_id; int current_subset; int num_glyphs_in_current_subset; int num_glyphs_in_latin_subset; int max_glyphs_per_subset; char latin_char_map[256]; cairo_hash_table_t *sub_font_glyphs; struct _cairo_sub_font *next; } cairo_sub_font_t; struct _cairo_scaled_font_subsets { cairo_subsets_type_t type; cairo_bool_t use_latin_subset; int max_glyphs_per_unscaled_subset_used; cairo_hash_table_t *unscaled_sub_fonts; cairo_sub_font_t *unscaled_sub_fonts_list; cairo_sub_font_t *unscaled_sub_fonts_list_end; int max_glyphs_per_scaled_subset_used; cairo_hash_table_t *scaled_sub_fonts; cairo_sub_font_t *scaled_sub_fonts_list; cairo_sub_font_t *scaled_sub_fonts_list_end; int num_sub_fonts; }; typedef struct _cairo_sub_font_glyph { cairo_hash_entry_t base; unsigned int subset_id; unsigned int subset_glyph_index; double x_advance; double y_advance; cairo_bool_t is_latin; int latin_character; cairo_bool_t is_mapped; uint32_t unicode; char *utf8; int utf8_len; } cairo_sub_font_glyph_t; typedef struct _cairo_sub_font_collection { unsigned long *glyphs; /* scaled_font_glyph_index */ char **utf8; unsigned int glyphs_size; int *to_latin_char; unsigned long *latin_to_subset_glyph_index; unsigned int max_glyph; unsigned int num_glyphs; unsigned int subset_id; cairo_status_t status; cairo_scaled_font_subset_callback_func_t font_subset_callback; void *font_subset_callback_closure; } cairo_sub_font_collection_t; typedef struct _cairo_string_entry { cairo_hash_entry_t base; char *string; } cairo_string_entry_t; static cairo_status_t _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, const char * utf8, int utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph); static void _cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, unsigned long scaled_font_glyph_index) { sub_font_glyph->base.hash = scaled_font_glyph_index; } static cairo_sub_font_glyph_t * _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, unsigned int subset_id, unsigned int subset_glyph_index, double x_advance, double y_advance, int latin_character, uint32_t unicode, char *utf8, int utf8_len) { cairo_sub_font_glyph_t *sub_font_glyph; sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); if (unlikely (sub_font_glyph == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index); sub_font_glyph->subset_id = subset_id; sub_font_glyph->subset_glyph_index = subset_glyph_index; sub_font_glyph->x_advance = x_advance; sub_font_glyph->y_advance = y_advance; sub_font_glyph->is_latin = (latin_character >= 0); sub_font_glyph->latin_character = latin_character; sub_font_glyph->is_mapped = FALSE; sub_font_glyph->unicode = unicode; sub_font_glyph->utf8 = utf8; sub_font_glyph->utf8_len = utf8_len; return sub_font_glyph; } static void _cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) { free (sub_font_glyph->utf8); free (sub_font_glyph); } static void _cairo_sub_font_glyph_pluck (void *entry, void *closure) { cairo_sub_font_glyph_t *sub_font_glyph = entry; cairo_hash_table_t *sub_font_glyphs = closure; _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base); _cairo_sub_font_glyph_destroy (sub_font_glyph); } static void _cairo_sub_font_glyph_collect (void *entry, void *closure) { cairo_sub_font_glyph_t *sub_font_glyph = entry; cairo_sub_font_collection_t *collection = closure; unsigned long scaled_font_glyph_index; unsigned int subset_glyph_index; if (sub_font_glyph->subset_id != collection->subset_id) return; scaled_font_glyph_index = sub_font_glyph->base.hash; subset_glyph_index = sub_font_glyph->subset_glyph_index; /* Ensure we don't exceed the allocated bounds. */ assert (subset_glyph_index < collection->glyphs_size); collection->glyphs[subset_glyph_index] = scaled_font_glyph_index; collection->utf8[subset_glyph_index] = sub_font_glyph->utf8; collection->to_latin_char[subset_glyph_index] = sub_font_glyph->latin_character; if (sub_font_glyph->is_latin) collection->latin_to_subset_glyph_index[sub_font_glyph->latin_character] = subset_glyph_index; if (subset_glyph_index > collection->max_glyph) collection->max_glyph = subset_glyph_index; collection->num_glyphs++; } static cairo_bool_t _cairo_sub_fonts_equal (const void *key_a, const void *key_b) { const cairo_sub_font_t *sub_font_a = key_a; const cairo_sub_font_t *sub_font_b = key_b; cairo_scaled_font_t *a = sub_font_a->scaled_font; cairo_scaled_font_t *b = sub_font_b->scaled_font; if (sub_font_a->is_scaled) return a == b; else return a->font_face == b->font_face || a->original_font_face == b->original_font_face; } static void _cairo_sub_font_init_key (cairo_sub_font_t *sub_font, cairo_scaled_font_t *scaled_font) { if (sub_font->is_scaled) { sub_font->base.hash = (unsigned long) scaled_font; sub_font->scaled_font = scaled_font; } else { sub_font->base.hash = (unsigned long) scaled_font->font_face; sub_font->scaled_font = scaled_font; } } static cairo_status_t _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, cairo_scaled_font_t *scaled_font, unsigned int font_id, int max_glyphs_per_subset, cairo_bool_t is_scaled, cairo_bool_t is_composite, cairo_sub_font_t **sub_font_out) { cairo_sub_font_t *sub_font; int i; sub_font = malloc (sizeof (cairo_sub_font_t)); if (unlikely (sub_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); sub_font->is_scaled = is_scaled; sub_font->is_composite = is_composite; sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face); _cairo_sub_font_init_key (sub_font, scaled_font); sub_font->parent = parent; sub_font->scaled_font = scaled_font; sub_font->font_id = font_id; sub_font->use_latin_subset = parent->use_latin_subset; /* latin subsets of Type 3 and CID CFF fonts are not supported */ if (sub_font->is_user || sub_font->is_scaled || _cairo_cff_scaled_font_is_cid_cff (scaled_font) ) { sub_font->use_latin_subset = FALSE; } if (sub_font->use_latin_subset) sub_font->current_subset = 1; /* reserve subset 0 for latin glyphs */ else sub_font->current_subset = 0; sub_font->num_glyphs_in_current_subset = 0; sub_font->num_glyphs_in_latin_subset = 0; sub_font->max_glyphs_per_subset = max_glyphs_per_subset; for (i = 0; i < 256; i++) sub_font->latin_char_map[i] = FALSE; sub_font->sub_font_glyphs = _cairo_hash_table_create (NULL); if (unlikely (sub_font->sub_font_glyphs == NULL)) { free (sub_font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } sub_font->next = NULL; *sub_font_out = sub_font; return CAIRO_STATUS_SUCCESS; } static void _cairo_sub_font_destroy (cairo_sub_font_t *sub_font) { _cairo_hash_table_foreach (sub_font->sub_font_glyphs, _cairo_sub_font_glyph_pluck, sub_font->sub_font_glyphs); _cairo_hash_table_destroy (sub_font->sub_font_glyphs); cairo_scaled_font_destroy (sub_font->scaled_font); free (sub_font); } static void _cairo_sub_font_pluck (void *entry, void *closure) { cairo_sub_font_t *sub_font = entry; cairo_hash_table_t *sub_fonts = closure; _cairo_hash_table_remove (sub_fonts, &sub_font->base); _cairo_sub_font_destroy (sub_font); } /* Characters 0x80 to 0x9f in the winansi encoding. * All other characters in the range 0x00 to 0xff map 1:1 to unicode */ static unsigned int _winansi_0x80_to_0x9f[] = { 0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017d, 0x0000, 0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x017e, 0x0178 }; int _cairo_unicode_to_winansi (unsigned long uni) { int i; /* exclude the extra "hyphen" at 0xad to avoid duplicate glyphnames */ if ((uni >= 0x20 && uni <= 0x7e) || (uni >= 0xa1 && uni <= 0xff && uni != 0xad) || uni == 0) return uni; for (i = 0; i < 32; i++) if (_winansi_0x80_to_0x9f[i] == uni) return i + 0x80; return -1; } static cairo_status_t _cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, uint32_t *unicode_out, char **utf8_out, int *utf8_len_out) { uint32_t unicode; char buf[8]; int len; cairo_status_t status; /* Do a reverse lookup on the glyph index. unicode is -1 if the * index could not be mapped to a unicode character. */ unicode = -1; status = _cairo_truetype_index_to_ucs4 (scaled_font, scaled_font_glyph_index, &unicode); if (_cairo_status_is_error (status)) return status; if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) { status = scaled_font->backend->index_to_ucs4 (scaled_font, scaled_font_glyph_index, &unicode); if (unlikely (status)) return status; } *unicode_out = unicode; *utf8_out = NULL; *utf8_len_out = 0; if (unicode != (uint32_t) -1) { len = _cairo_ucs4_to_utf8 (unicode, buf); if (len > 0) { *utf8_out = malloc (len + 1); if (unlikely (*utf8_out == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (*utf8_out, buf, len); (*utf8_out)[len] = 0; *utf8_len_out = len; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, const char *utf8, int utf8_len, cairo_bool_t *is_mapped) { *is_mapped = FALSE; if (utf8_len < 0) return CAIRO_STATUS_SUCCESS; if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') utf8_len--; if (utf8 != NULL && utf8_len != 0) { if (sub_font_glyph->utf8 != NULL) { if (utf8_len == sub_font_glyph->utf8_len && memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) { /* Requested utf8 mapping matches the existing mapping */ *is_mapped = TRUE; } } else { /* No existing mapping. Use the requested mapping */ sub_font_glyph->utf8 = malloc (utf8_len + 1); if (unlikely (sub_font_glyph->utf8 == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (sub_font_glyph->utf8, utf8, utf8_len); sub_font_glyph->utf8[utf8_len] = 0; sub_font_glyph->utf8_len = utf8_len; *is_mapped = TRUE; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, const char *utf8, int utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_glyph_t key, *sub_font_glyph; cairo_int_status_t status; _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base); if (sub_font_glyph != NULL) { subset_glyph->font_id = sub_font->font_id; subset_glyph->subset_id = sub_font_glyph->subset_id; if (sub_font_glyph->is_latin) subset_glyph->subset_glyph_index = sub_font_glyph->latin_character; else subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; subset_glyph->is_scaled = sub_font->is_scaled; subset_glyph->is_composite = sub_font->is_composite; subset_glyph->is_latin = sub_font_glyph->is_latin; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len, &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; return status; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _cairo_sub_font_add_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, cairo_bool_t is_latin, int latin_character, uint32_t unicode, char *utf8, int utf8_len, cairo_sub_font_glyph_t **sub_font_glyph_out) { cairo_scaled_glyph_t *scaled_glyph; cairo_sub_font_glyph_t *sub_font_glyph; int *num_glyphs_in_subset_ptr; double x_advance; double y_advance; cairo_int_status_t status; _cairo_scaled_font_freeze_cache (sub_font->scaled_font); status = _cairo_scaled_glyph_lookup (sub_font->scaled_font, scaled_font_glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); if (unlikely (status)) { _cairo_scaled_font_thaw_cache (sub_font->scaled_font); return status; } x_advance = scaled_glyph->metrics.x_advance; y_advance = scaled_glyph->metrics.y_advance; _cairo_scaled_font_thaw_cache (sub_font->scaled_font); if (!is_latin && sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) { sub_font->current_subset++; sub_font->num_glyphs_in_current_subset = 0; } if (is_latin) num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_latin_subset; else num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_current_subset; /* Reserve first glyph in subset for the .notdef glyph except for * Type 3 fonts */ if (*num_glyphs_in_subset_ptr == 0 && scaled_font_glyph_index != 0 && ! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) { status = _cairo_sub_font_add_glyph (sub_font, 0, is_latin, 0, 0, NULL, -1, &sub_font_glyph); if (unlikely (status)) return status; } sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, is_latin ? 0 : sub_font->current_subset, *num_glyphs_in_subset_ptr, x_advance, y_advance, is_latin ? latin_character : -1, unicode, utf8, utf8_len); if (unlikely (sub_font_glyph == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); if (unlikely (status)) { _cairo_sub_font_glyph_destroy (sub_font_glyph); return status; } (*num_glyphs_in_subset_ptr)++; if (sub_font->is_scaled) { if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_scaled_subset_used) sub_font->parent->max_glyphs_per_scaled_subset_used = *num_glyphs_in_subset_ptr; } else { if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_unscaled_subset_used) sub_font->parent->max_glyphs_per_unscaled_subset_used = *num_glyphs_in_subset_ptr; } *sub_font_glyph_out = sub_font_glyph; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, const char *text_utf8, int text_utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_glyph_t key, *sub_font_glyph; cairo_status_t status; _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base); if (sub_font_glyph == NULL) { uint32_t font_unicode; char *font_utf8; int font_utf8_len; cairo_bool_t is_latin; int latin_character; status = _cairo_sub_font_glyph_lookup_unicode (sub_font->scaled_font, scaled_font_glyph_index, &font_unicode, &font_utf8, &font_utf8_len); if (unlikely(status)) return status; /* If the supplied utf8 is a valid single character, use it * instead of the font lookup */ if (text_utf8 != NULL && text_utf8_len > 0) { uint32_t *ucs4; int ucs4_len; status = _cairo_utf8_to_ucs4 (text_utf8, text_utf8_len, &ucs4, &ucs4_len); if (status == CAIRO_STATUS_SUCCESS) { if (ucs4_len == 1) { font_unicode = ucs4[0]; free (font_utf8); font_utf8 = malloc (text_utf8_len + 1); if (font_utf8 == NULL) { free (ucs4); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (font_utf8, text_utf8, text_utf8_len); font_utf8[text_utf8_len] = 0; font_utf8_len = text_utf8_len; } free (ucs4); } } /* If glyph is in the winansi encoding and font is not a user * font, put glyph in the latin subset. If glyph is .notdef * the latin subset is preferred but only if the latin subset * already contains at least one glyph. We don't want to * create a separate subset just for the .notdef glyph. */ is_latin = FALSE; latin_character = -1; if (sub_font->use_latin_subset && (! _cairo_font_face_is_user (sub_font->scaled_font->font_face))) { latin_character = _cairo_unicode_to_winansi (font_unicode); if (latin_character > 0 || (latin_character == 0 && sub_font->num_glyphs_in_latin_subset > 0)) { if (!sub_font->latin_char_map[latin_character]) { sub_font->latin_char_map[latin_character] = TRUE; is_latin = TRUE; } } } status = _cairo_sub_font_add_glyph (sub_font, scaled_font_glyph_index, is_latin, latin_character, font_unicode, font_utf8, font_utf8_len, &sub_font_glyph); if (unlikely(status)) return status; } subset_glyph->font_id = sub_font->font_id; subset_glyph->subset_id = sub_font_glyph->subset_id; if (sub_font_glyph->is_latin) subset_glyph->subset_glyph_index = sub_font_glyph->latin_character; else subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; subset_glyph->is_scaled = sub_font->is_scaled; subset_glyph->is_composite = sub_font->is_composite; subset_glyph->is_latin = sub_font_glyph->is_latin; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, text_utf8, text_utf8_len, &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; return status; } static void _cairo_sub_font_collect (void *entry, void *closure) { cairo_sub_font_t *sub_font = entry; cairo_sub_font_collection_t *collection = closure; cairo_scaled_font_subset_t subset; int i; unsigned int j; if (collection->status) return; collection->status = sub_font->scaled_font->status; if (collection->status) return; for (i = 0; i <= sub_font->current_subset; i++) { collection->subset_id = i; collection->num_glyphs = 0; collection->max_glyph = 0; memset (collection->latin_to_subset_glyph_index, 0, 256*sizeof(unsigned long)); _cairo_hash_table_foreach (sub_font->sub_font_glyphs, _cairo_sub_font_glyph_collect, collection); if (collection->status) break; if (collection->num_glyphs == 0) continue; /* Ensure the resulting array has no uninitialized holes */ assert (collection->num_glyphs == collection->max_glyph + 1); subset.scaled_font = sub_font->scaled_font; subset.is_composite = sub_font->is_composite; subset.is_scaled = sub_font->is_scaled; subset.font_id = sub_font->font_id; subset.subset_id = i; subset.glyphs = collection->glyphs; subset.utf8 = collection->utf8; subset.num_glyphs = collection->num_glyphs; subset.glyph_names = NULL; subset.is_latin = FALSE; if (sub_font->use_latin_subset && i == 0) { subset.is_latin = TRUE; subset.to_latin_char = collection->to_latin_char; subset.latin_to_subset_glyph_index = collection->latin_to_subset_glyph_index; } else { subset.to_latin_char = NULL; subset.latin_to_subset_glyph_index = NULL; } collection->status = (collection->font_subset_callback) (&subset, collection->font_subset_callback_closure); if (subset.glyph_names != NULL) { for (j = 0; j < collection->num_glyphs; j++) free (subset.glyph_names[j]); free (subset.glyph_names); } if (collection->status) break; } } static cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) { cairo_scaled_font_subsets_t *subsets; subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); if (unlikely (subsets == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } subsets->type = type; subsets->use_latin_subset = FALSE; subsets->max_glyphs_per_unscaled_subset_used = 0; subsets->max_glyphs_per_scaled_subset_used = 0; subsets->num_sub_fonts = 0; subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); if (! subsets->unscaled_sub_fonts) { free (subsets); return NULL; } subsets->unscaled_sub_fonts_list = NULL; subsets->unscaled_sub_fonts_list_end = NULL; subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); if (! subsets->scaled_sub_fonts) { _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); free (subsets); return NULL; } subsets->scaled_sub_fonts_list = NULL; subsets->scaled_sub_fonts_list_end = NULL; return subsets; } cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_scaled (void) { return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED); } cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_simple (void) { return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE); } cairo_scaled_font_subsets_t * _cairo_scaled_font_subsets_create_composite (void) { return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE); } void _cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) { _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts); _cairo_hash_table_destroy (subsets->scaled_sub_fonts); _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts); _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); free (subsets); } void _cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets, cairo_bool_t use_latin) { font_subsets->use_latin_subset = use_latin; } cairo_status_t _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, const char * utf8, int utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_t key, *sub_font; cairo_scaled_glyph_t *scaled_glyph; cairo_font_face_t *font_face; cairo_matrix_t identity; cairo_font_options_t font_options; cairo_scaled_font_t *unscaled_font; cairo_int_status_t status; int max_glyphs; cairo_bool_t type1_font; /* Lookup glyph in unscaled subsets */ if (subsets->type != CAIRO_SUBSETS_SCALED) { key.is_scaled = FALSE; _cairo_sub_font_init_key (&key, scaled_font); sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, &key.base); if (sub_font != NULL) { status = _cairo_sub_font_lookup_glyph (sub_font, scaled_font_glyph_index, utf8, utf8_len, subset_glyph); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } } /* Lookup glyph in scaled subsets */ key.is_scaled = TRUE; _cairo_sub_font_init_key (&key, scaled_font); sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, &key.base); if (sub_font != NULL) { status = _cairo_sub_font_lookup_glyph (sub_font, scaled_font_glyph_index, utf8, utf8_len, subset_glyph); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } /* Glyph not found. Determine whether the glyph is outline or * bitmap and add to the appropriate subset. * * glyph_index 0 (the .notdef glyph) is a special case. Some fonts * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates * empty glyphs in this case so we can put the glyph in a unscaled * subset. */ if (scaled_font_glyph_index == 0 || _cairo_font_face_is_user (scaled_font->font_face)) { status = CAIRO_STATUS_SUCCESS; } else { _cairo_scaled_font_freeze_cache (scaled_font); status = _cairo_scaled_glyph_lookup (scaled_font, scaled_font_glyph_index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); _cairo_scaled_font_thaw_cache (scaled_font); } if (_cairo_int_status_is_error (status)) return status; if (status == CAIRO_INT_STATUS_SUCCESS && subsets->type != CAIRO_SUBSETS_SCALED && ! _cairo_font_face_is_user (scaled_font->font_face)) { /* Path available. Add to unscaled subset. */ key.is_scaled = FALSE; _cairo_sub_font_init_key (&key, scaled_font); sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, &key.base); if (sub_font == NULL) { font_face = cairo_scaled_font_get_font_face (scaled_font); cairo_matrix_init_identity (&identity); _cairo_font_options_init_default (&font_options); cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); unscaled_font = cairo_scaled_font_create (font_face, &identity, &identity, &font_options); if (unlikely (unscaled_font->status)) return unscaled_font->status; subset_glyph->is_scaled = FALSE; type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font); if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) { max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT; subset_glyph->is_composite = TRUE; } else { max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; subset_glyph->is_composite = FALSE; } status = _cairo_sub_font_create (subsets, unscaled_font, subsets->num_sub_fonts, max_glyphs, subset_glyph->is_scaled, subset_glyph->is_composite, &sub_font); if (unlikely (status)) { cairo_scaled_font_destroy (unscaled_font); return status; } status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts, &sub_font->base); if (unlikely (status)) { _cairo_sub_font_destroy (sub_font); return status; } if (!subsets->unscaled_sub_fonts_list) subsets->unscaled_sub_fonts_list = sub_font; else subsets->unscaled_sub_fonts_list_end->next = sub_font; subsets->unscaled_sub_fonts_list_end = sub_font; subsets->num_sub_fonts++; } } else { /* No path available. Add to scaled subset. */ key.is_scaled = TRUE; _cairo_sub_font_init_key (&key, scaled_font); sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, &key.base); if (sub_font == NULL) { subset_glyph->is_scaled = TRUE; subset_glyph->is_composite = FALSE; if (subsets->type == CAIRO_SUBSETS_SCALED) max_glyphs = INT_MAX; else max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; status = _cairo_sub_font_create (subsets, cairo_scaled_font_reference (scaled_font), subsets->num_sub_fonts, max_glyphs, subset_glyph->is_scaled, subset_glyph->is_composite, &sub_font); if (unlikely (status)) { cairo_scaled_font_destroy (scaled_font); return status; } status = _cairo_hash_table_insert (subsets->scaled_sub_fonts, &sub_font->base); if (unlikely (status)) { _cairo_sub_font_destroy (sub_font); return status; } if (!subsets->scaled_sub_fonts_list) subsets->scaled_sub_fonts_list = sub_font; else subsets->scaled_sub_fonts_list_end->next = sub_font; subsets->scaled_sub_fonts_list_end = sub_font; subsets->num_sub_fonts++; } } return _cairo_sub_font_map_glyph (sub_font, scaled_font_glyph_index, utf8, utf8_len, subset_glyph); } static cairo_status_t _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure, cairo_subsets_foreach_type_t type) { cairo_sub_font_collection_t collection; cairo_sub_font_t *sub_font; cairo_bool_t is_scaled, is_user; is_scaled = FALSE; is_user = FALSE; if (type == CAIRO_SUBSETS_FOREACH_USER) is_user = TRUE; if (type == CAIRO_SUBSETS_FOREACH_SCALED || type == CAIRO_SUBSETS_FOREACH_USER) { is_scaled = TRUE; } if (is_scaled) collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; else collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used; if (! collection.glyphs_size) return CAIRO_STATUS_SUCCESS; collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); collection.to_latin_char = _cairo_malloc_ab (collection.glyphs_size, sizeof(int)); collection.latin_to_subset_glyph_index = _cairo_malloc_ab (256, sizeof(unsigned long)); if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL || collection.to_latin_char == NULL || collection.latin_to_subset_glyph_index == NULL)) { free (collection.glyphs); free (collection.utf8); free (collection.to_latin_char); free (collection.latin_to_subset_glyph_index); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } collection.font_subset_callback = font_subset_callback; collection.font_subset_callback_closure = closure; collection.status = CAIRO_STATUS_SUCCESS; if (is_scaled) sub_font = font_subsets->scaled_sub_fonts_list; else sub_font = font_subsets->unscaled_sub_fonts_list; while (sub_font) { if (sub_font->is_user == is_user) _cairo_sub_font_collect (sub_font, &collection); sub_font = sub_font->next; } free (collection.utf8); free (collection.glyphs); free (collection.to_latin_char); free (collection.latin_to_subset_glyph_index); return collection.status; } cairo_status_t _cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure) { return _cairo_scaled_font_subsets_foreach_internal (font_subsets, font_subset_callback, closure, CAIRO_SUBSETS_FOREACH_SCALED); } cairo_status_t _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure) { return _cairo_scaled_font_subsets_foreach_internal (font_subsets, font_subset_callback, closure, CAIRO_SUBSETS_FOREACH_UNSCALED); } cairo_status_t _cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_subset_callback_func_t font_subset_callback, void *closure) { return _cairo_scaled_font_subsets_foreach_internal (font_subsets, font_subset_callback, closure, CAIRO_SUBSETS_FOREACH_USER); } static cairo_bool_t _cairo_string_equal (const void *key_a, const void *key_b) { const cairo_string_entry_t *a = key_a; const cairo_string_entry_t *b = key_b; if (strcmp (a->string, b->string) == 0) return TRUE; else return FALSE; } static void _cairo_string_init_key (cairo_string_entry_t *key, char *s) { unsigned long sum = 0; unsigned int i; for (i = 0; i < strlen(s); i++) sum += s[i]; key->base.hash = sum; key->string = s; } static cairo_status_t create_string_entry (char *s, cairo_string_entry_t **entry) { *entry = malloc (sizeof (cairo_string_entry_t)); if (unlikely (*entry == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_string_init_key (*entry, s); return CAIRO_STATUS_SUCCESS; } static void _pluck_entry (void *entry, void *closure) { _cairo_hash_table_remove (closure, entry); free (entry); } cairo_int_status_t _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset) { unsigned int i; cairo_hash_table_t *names; cairo_string_entry_t key, *entry; char buf[30]; char *utf8; uint16_t *utf16; int utf16_len; cairo_status_t status = CAIRO_STATUS_SUCCESS; names = _cairo_hash_table_create (_cairo_string_equal); if (unlikely (names == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *)); if (unlikely (subset->glyph_names == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } i = 0; if (! subset->is_scaled) { subset->glyph_names[0] = strdup (".notdef"); if (unlikely (subset->glyph_names[0] == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } status = create_string_entry (subset->glyph_names[0], &entry); if (unlikely (status)) goto CLEANUP_HASH; status = _cairo_hash_table_insert (names, &entry->base); if (unlikely (status)) { free (entry); goto CLEANUP_HASH; } i++; } for (; i < subset->num_glyphs; i++) { utf8 = subset->utf8[i]; utf16 = NULL; utf16_len = 0; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); if (unlikely (status)) goto CLEANUP_HASH; } if (utf16_len == 1) { int ch = _cairo_unicode_to_winansi (utf16[0]); if (ch > 0 && _cairo_winansi_to_glyphname (ch)) strncpy (buf, _cairo_winansi_to_glyphname (ch), sizeof (buf)); else snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); _cairo_string_init_key (&key, buf); entry = _cairo_hash_table_lookup (names, &key.base); if (entry != NULL) snprintf (buf, sizeof (buf), "g%d", i); } else { snprintf (buf, sizeof (buf), "g%d", i); } free (utf16); subset->glyph_names[i] = strdup (buf); if (unlikely (subset->glyph_names[i] == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_HASH; } status = create_string_entry (subset->glyph_names[i], &entry); if (unlikely (status)) goto CLEANUP_HASH; status = _cairo_hash_table_insert (names, &entry->base); if (unlikely (status)) { free (entry); goto CLEANUP_HASH; } } CLEANUP_HASH: _cairo_hash_table_foreach (names, _pluck_entry, names); _cairo_hash_table_destroy (names); if (likely (status == CAIRO_STATUS_SUCCESS)) return CAIRO_STATUS_SUCCESS; if (subset->glyph_names != NULL) { for (i = 0; i < subset->num_glyphs; i++) { free (subset->glyph_names[i]); } free (subset->glyph_names); subset->glyph_names = NULL; } return status; } #endif /* CAIRO_HAS_FONT_SUBSET */ ��������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-scaled-font.c�����������������������������0000664�0000000�0000000�00000300703�12710376503�0025741�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* * Copyright © 2005 Keith Packard * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith Packard <keithp@keithp.com> * Carl D. Worth <cworth@cworth.org> * Graydon Hoare <graydon@redhat.com> * Owen Taylor <otaylor@redhat.com> * Behdad Esfahbod <behdad@behdad.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-pattern-private.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-backend-private.h" #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) #define ISFINITE(x) isfinite (x) #else #define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ #endif /** * SECTION:cairo-scaled-font * @Title: cairo_scaled_font_t * @Short_Description: Font face at particular size and options * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t * * #cairo_scaled_font_t represents a realization of a font face at a particular * size and transformation and a certain set of font options. **/ static uint32_t _cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font); /* Global Glyph Cache * * We maintain a global pool of glyphs split between all active fonts. This * allows a heavily used individual font to cache more glyphs than we could * manage if we used per-font glyph caches, but at the same time maintains * fairness across all fonts and provides a cap on the maximum number of * global glyphs. * * The glyphs are allocated in pages, which are capped in the global pool. * Using pages means we can reduce the frequency at which we have to probe the * global pool and ameliorates the memory allocation pressure. */ /* XXX: This number is arbitrary---we've never done any measurement of this. */ #define MAX_GLYPH_PAGES_CACHED 512 static cairo_cache_t cairo_scaled_glyph_page_cache; #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 struct _cairo_scaled_glyph_page { cairo_cache_entry_t cache_entry; cairo_list_t link; unsigned int num_glyphs; cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE]; }; /* * Notes: * * To store rasterizations of glyphs, we use an image surface and the * device offset to represent the glyph origin. * * A device_transform converts from device space (a conceptual space) to * surface space. For simple cases of translation only, it's called a * device_offset and is public API (cairo_surface_[gs]et_device_offset()). * A possibly better name for those functions could have been * cairo_surface_[gs]et_origin(). So, that's what they do: they set where * the device-space origin (0,0) is in the surface. If the origin is inside * the surface, device_offset values are positive. It may look like this: * * Device space: * (-x,-y) <-- negative numbers * +----------------+ * | . | * | . | * |......(0,0) <---|-- device-space origin * | | * | | * +----------------+ * (width-x,height-y) * * Surface space: * (0,0) <-- surface-space origin * +---------------+ * | . | * | . | * |......(x,y) <--|-- device_offset * | | * | | * +---------------+ * (width,height) * * In other words: device_offset is the coordinates of the device-space * origin relative to the top-left of the surface. * * We use device offsets in a couple of places: * * - Public API: To let toolkits like Gtk+ give user a surface that * only represents part of the final destination (say, the expose * area), but has the same device space as the destination. In these * cases device_offset is typically negative. Example: * * application window * +---------------+ * | . | * | (x,y). | * |......+---+ | * | | | <--|-- expose area * | +---+ | * +---------------+ * * In this case, the user of cairo API can set the device_space on * the expose area to (-x,-y) to move the device space origin to that * of the application window, such that drawing in the expose area * surface and painting it in the application window has the same * effect as drawing in the application window directly. Gtk+ has * been using this feature. * * - Glyph surfaces: In most font rendering systems, glyph surfaces * have an origin at (0,0) and a bounding box that is typically * represented as (x_bearing,y_bearing,width,height). Depending on * which way y progresses in the system, y_bearing may typically be * negative (for systems similar to cairo, with origin at top left), * or be positive (in systems like PDF with origin at bottom left). * No matter which is the case, it is important to note that * (x_bearing,y_bearing) is the coordinates of top-left of the glyph * relative to the glyph origin. That is, for example: * * Scaled-glyph space: * * (x_bearing,y_bearing) <-- negative numbers * +----------------+ * | . | * | . | * |......(0,0) <---|-- glyph origin * | | * | | * +----------------+ * (width+x_bearing,height+y_bearing) * * Note the similarity of the origin to the device space. That is * exactly how we use the device_offset to represent scaled glyphs: * to use the device-space origin as the glyph origin. * * Now compare the scaled-glyph space to device-space and surface-space * and convince yourself that: * * (x_bearing,y_bearing) = (-x,-y) = - device_offset * * That's right. If you are not convinced yet, contrast the definition * of the two: * * "(x_bearing,y_bearing) is the coordinates of top-left of the * glyph relative to the glyph origin." * * "In other words: device_offset is the coordinates of the * device-space origin relative to the top-left of the surface." * * and note that glyph origin = device-space origin. */ static void _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font); static void _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { while (! cairo_list_is_empty (&scaled_glyph->dev_privates)) { cairo_scaled_glyph_private_t *private = cairo_list_first_entry (&scaled_glyph->dev_privates, cairo_scaled_glyph_private_t, link); private->destroy (private, scaled_glyph, scaled_font); } _cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph); if (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); if (scaled_glyph->path != NULL) _cairo_path_fixed_destroy (scaled_glyph->path); if (scaled_glyph->recording_surface != NULL) { cairo_surface_finish (scaled_glyph->recording_surface); cairo_surface_destroy (scaled_glyph->recording_surface); } } #define ZOMBIE 0 static const cairo_scaled_font_t _cairo_scaled_font_nil = { { ZOMBIE }, /* hash_entry */ CAIRO_STATUS_NO_MEMORY, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL, /* original_font_face */ NULL, /* font_face */ { 1., 0., 0., 1., 0, 0}, /* font_matrix */ { 1., 0., 0., 1., 0, 0}, /* ctm */ { CAIRO_ANTIALIAS_DEFAULT, /* options */ CAIRO_SUBPIXEL_ORDER_DEFAULT, CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_METRICS_DEFAULT} , FALSE, /* placeholder */ FALSE, /* holdover */ TRUE, /* finished */ { 1., 0., 0., 1., 0, 0}, /* scale */ { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ 1., /* max_scale */ { 0., 0., 0., 0., 0. }, /* extents */ { 0., 0., 0., 0., 0. }, /* fs_extents */ CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ NULL, /* glyphs */ { NULL, NULL }, /* pages */ FALSE, /* cache_frozen */ FALSE, /* global_cache_frozen */ { NULL, NULL }, /* privates */ NULL /* backend */ }; /** * _cairo_scaled_font_set_error: * @scaled_font: a scaled_font * @status: a status value indicating an error * * Atomically sets scaled_font->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS. * * All assignments of an error status to scaled_font->status should happen * through _cairo_scaled_font_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the nil * objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. * * Return value: the error status. **/ cairo_status_t _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS) return status; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (&scaled_font->status, status); return _cairo_error (status); } /** * cairo_scaled_font_get_type: * @scaled_font: a #cairo_scaled_font_t * * This function returns the type of the backend used to create * a scaled font. See #cairo_font_type_t for available types. * However, this function never returns %CAIRO_FONT_TYPE_TOY. * * Return value: The type of @scaled_font. * * Since: 1.2 **/ cairo_font_type_t cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return CAIRO_FONT_TYPE_TOY; return scaled_font->backend->type; } /** * cairo_scaled_font_status: * @scaled_font: a #cairo_scaled_font_t * * Checks whether an error has previously occurred for this * scaled_font. * * Return value: %CAIRO_STATUS_SUCCESS or another error such as * %CAIRO_STATUS_NO_MEMORY. * * Since: 1.0 **/ cairo_status_t cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) { return scaled_font->status; } slim_hidden_def (cairo_scaled_font_status); /* Here we keep a unique mapping from * font_face/matrix/ctm/font_options => #cairo_scaled_font_t. * * Here are the things that we want to map: * * a) All otherwise referenced #cairo_scaled_font_t's * b) Some number of not otherwise referenced #cairo_scaled_font_t's * * The implementation uses a hash table which covers (a) * completely. Then, for (b) we have an array of otherwise * unreferenced fonts (holdovers) which are expired in * least-recently-used order. * * The cairo_scaled_font_create() code gets to treat this like a regular * hash table. All of the magic for the little holdover cache is in * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). */ /* This defines the size of the holdover array ... that is, the number * of scaled fonts we keep around even when not otherwise referenced */ #define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256 typedef struct _cairo_scaled_font_map { cairo_scaled_font_t *mru_scaled_font; cairo_hash_table_t *hash_table; cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; int num_holdovers; } cairo_scaled_font_map_t; static cairo_scaled_font_map_t *cairo_scaled_font_map; static int _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); static cairo_scaled_font_map_t * _cairo_scaled_font_map_lock (void) { CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); if (cairo_scaled_font_map == NULL) { cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); if (unlikely (cairo_scaled_font_map == NULL)) goto CLEANUP_MUTEX_LOCK; cairo_scaled_font_map->mru_scaled_font = NULL; cairo_scaled_font_map->hash_table = _cairo_hash_table_create (_cairo_scaled_font_keys_equal); if (unlikely (cairo_scaled_font_map->hash_table == NULL)) goto CLEANUP_SCALED_FONT_MAP; cairo_scaled_font_map->num_holdovers = 0; } return cairo_scaled_font_map; CLEANUP_SCALED_FONT_MAP: free (cairo_scaled_font_map); cairo_scaled_font_map = NULL; CLEANUP_MUTEX_LOCK: CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } static void _cairo_scaled_font_map_unlock (void) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); } void _cairo_scaled_font_map_destroy (void) { cairo_scaled_font_map_t *font_map; cairo_scaled_font_t *scaled_font; CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); font_map = cairo_scaled_font_map; if (unlikely (font_map == NULL)) { goto CLEANUP_MUTEX_LOCK; } scaled_font = font_map->mru_scaled_font; if (scaled_font != NULL) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); cairo_scaled_font_destroy (scaled_font); CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); } /* remove scaled_fonts starting from the end so that font_map->holdovers * is always in a consistent state when we release the mutex. */ while (font_map->num_holdovers) { scaled_font = font_map->holdovers[font_map->num_holdovers-1]; assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); _cairo_hash_table_remove (font_map->hash_table, &scaled_font->hash_entry); font_map->num_holdovers--; /* This releases the font_map lock to avoid the possibility of a * recursive deadlock when the scaled font destroy closure gets * called */ _cairo_scaled_font_fini (scaled_font); free (scaled_font); } _cairo_hash_table_destroy (font_map->hash_table); free (cairo_scaled_font_map); cairo_scaled_font_map = NULL; CLEANUP_MUTEX_LOCK: CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); } static void _cairo_scaled_glyph_page_destroy (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_page_t *page) { unsigned int n; assert (!scaled_font->cache_frozen); assert (!scaled_font->global_cache_frozen); for (n = 0; n < page->num_glyphs; n++) { _cairo_hash_table_remove (scaled_font->glyphs, &page->glyphs[n].hash_entry); _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]); } cairo_list_del (&page->link); free (page); } static void _cairo_scaled_glyph_page_pluck (void *closure) { cairo_scaled_glyph_page_t *page = closure; cairo_scaled_font_t *scaled_font; assert (! cairo_list_is_empty (&page->link)); scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; CAIRO_MUTEX_LOCK (scaled_font->mutex); _cairo_scaled_glyph_page_destroy (scaled_font, page); CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } /* If a scaled font wants to unlock the font map while still being * created (needed for user-fonts), we need to take extra care not * ending up with multiple identical scaled fonts being created. * * What we do is, we create a fake identical scaled font, and mark * it as placeholder, lock its mutex, and insert that in the fontmap * hash table. This makes other code trying to create an identical * scaled font to just wait and retry. * * The reason we have to create a fake scaled font instead of just using * scaled_font is for lifecycle management: we need to (or rather, * other code needs to) reference the scaled_font in the hash table. * We can't do that on the input scaled_font as it may be freed by * font backend upon error. */ cairo_status_t _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font) { cairo_status_t status; cairo_scaled_font_t *placeholder_scaled_font; assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex)); status = scaled_font->status; if (unlikely (status)) return status; placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t)); if (unlikely (placeholder_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* full initialization is wasteful, but who cares... */ status = _cairo_scaled_font_init (placeholder_scaled_font, scaled_font->font_face, &scaled_font->font_matrix, &scaled_font->ctm, &scaled_font->options, NULL); if (unlikely (status)) goto FREE_PLACEHOLDER; placeholder_scaled_font->placeholder = TRUE; placeholder_scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash (placeholder_scaled_font); status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table, &placeholder_scaled_font->hash_entry); if (unlikely (status)) goto FINI_PLACEHOLDER; CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); return CAIRO_STATUS_SUCCESS; FINI_PLACEHOLDER: _cairo_scaled_font_fini_internal (placeholder_scaled_font); FREE_PLACEHOLDER: free (placeholder_scaled_font); return _cairo_scaled_font_set_error (scaled_font, status); } void _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font) { cairo_scaled_font_t *placeholder_scaled_font; CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); /* temporary hash value to match the placeholder */ scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash (scaled_font); placeholder_scaled_font = _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, &scaled_font->hash_entry); assert (placeholder_scaled_font != NULL); assert (placeholder_scaled_font->placeholder); assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex)); _cairo_hash_table_remove (cairo_scaled_font_map->hash_table, &placeholder_scaled_font->hash_entry); CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); cairo_scaled_font_destroy (placeholder_scaled_font); CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); } static void _cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font) { /* reference the place holder so it doesn't go away */ cairo_scaled_font_reference (placeholder_scaled_font); /* now unlock the fontmap mutex so creation has a chance to finish */ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); /* wait on placeholder mutex until we are awaken */ CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); /* ok, creation done. just clean up and back out */ CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); cairo_scaled_font_destroy (placeholder_scaled_font); CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); } /* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) * * Not necessarily better than a lot of other hashes, but should be OK, and * well tested with binary data. */ #define FNV_32_PRIME ((uint32_t)0x01000193) #define FNV1_32_INIT ((uint32_t)0x811c9dc5) static uint32_t _hash_matrix_fnv (const cairo_matrix_t *matrix, uint32_t hval) { const uint8_t *buffer = (const uint8_t *) matrix; int len = sizeof (cairo_matrix_t); do { hval *= FNV_32_PRIME; hval ^= *buffer++; } while (--len); return hval; } static uint32_t _hash_mix_bits (uint32_t hash) { hash += hash << 12; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return hash; } static uint32_t _cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font) { uint32_t hash = FNV1_32_INIT; /* We do a bytewise hash on the font matrices */ hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); hash = _hash_matrix_fnv (&scaled_font->ctm, hash); hash = _hash_mix_bits (hash); hash ^= (unsigned long) scaled_font->original_font_face; hash ^= cairo_font_options_hash (&scaled_font->options); /* final mixing of bits */ hash = _hash_mix_bits (hash); assert (hash != ZOMBIE); return hash; } static void _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { scaled_font->status = CAIRO_STATUS_SUCCESS; scaled_font->placeholder = FALSE; scaled_font->font_face = font_face; scaled_font->original_font_face = font_face; scaled_font->font_matrix = *font_matrix; scaled_font->ctm = *ctm; /* ignore translation values in the ctm */ scaled_font->ctm.x0 = 0.; scaled_font->ctm.y0 = 0.; _cairo_font_options_init_copy (&scaled_font->options, options); scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash (scaled_font); } static cairo_bool_t _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b) { const cairo_scaled_font_t *key_a = abstract_key_a; const cairo_scaled_font_t *key_b = abstract_key_b; return key_a->original_font_face == key_b->original_font_face && memcmp ((unsigned char *)(&key_a->font_matrix.xx), (unsigned char *)(&key_b->font_matrix.xx), sizeof(cairo_matrix_t)) == 0 && memcmp ((unsigned char *)(&key_a->ctm.xx), (unsigned char *)(&key_b->ctm.xx), sizeof(cairo_matrix_t)) == 0 && cairo_font_options_equal (&key_a->options, &key_b->options); } static cairo_bool_t _cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, const cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { return scaled_font->original_font_face == font_face && memcmp ((unsigned char *)(&scaled_font->font_matrix.xx), (unsigned char *)(&font_matrix->xx), sizeof(cairo_matrix_t)) == 0 && memcmp ((unsigned char *)(&scaled_font->ctm.xx), (unsigned char *)(&ctm->xx), sizeof(cairo_matrix_t)) == 0 && cairo_font_options_equal (&scaled_font->options, options); } /* * Basic #cairo_scaled_font_t object management */ cairo_status_t _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, const cairo_scaled_font_backend_t *backend) { cairo_status_t status; status = cairo_font_options_status ((cairo_font_options_t *) options); if (unlikely (status)) return status; scaled_font->status = CAIRO_STATUS_SUCCESS; scaled_font->placeholder = FALSE; scaled_font->font_face = font_face; scaled_font->original_font_face = font_face; scaled_font->font_matrix = *font_matrix; scaled_font->ctm = *ctm; /* ignore translation values in the ctm */ scaled_font->ctm.x0 = 0.; scaled_font->ctm.y0 = 0.; _cairo_font_options_init_copy (&scaled_font->options, options); cairo_matrix_multiply (&scaled_font->scale, &scaled_font->font_matrix, &scaled_font->ctm); scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy), fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy)); scaled_font->scale_inverse = scaled_font->scale; status = cairo_matrix_invert (&scaled_font->scale_inverse); if (unlikely (status)) { /* If the font scale matrix is rank 0, just using an all-zero inverse matrix * makes everything work correctly. This make font size 0 work without * producing an error. * * FIXME: If the scale is rank 1, we still go into error mode. But then * again, that's what we do everywhere in cairo. * * Also, the check for == 0. below may be too harsh... */ if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) { cairo_matrix_init (&scaled_font->scale_inverse, 0, 0, 0, 0, -scaled_font->scale.x0, -scaled_font->scale.y0); } else return status; } scaled_font->glyphs = _cairo_hash_table_create (NULL); if (unlikely (scaled_font->glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_list_init (&scaled_font->glyph_pages); scaled_font->cache_frozen = FALSE; scaled_font->global_cache_frozen = FALSE; scaled_font->holdover = FALSE; scaled_font->finished = FALSE; CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1); _cairo_user_data_array_init (&scaled_font->user_data); cairo_font_face_reference (font_face); scaled_font->original_font_face = NULL; CAIRO_MUTEX_INIT (scaled_font->mutex); cairo_list_init (&scaled_font->dev_privates); scaled_font->backend = backend; cairo_list_init (&scaled_font->link); return CAIRO_STATUS_SUCCESS; } void _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) { /* ensure we do not modify an error object */ assert (scaled_font->status == CAIRO_STATUS_SUCCESS); CAIRO_MUTEX_LOCK (scaled_font->mutex); scaled_font->cache_frozen = TRUE; } void _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) { assert (scaled_font->cache_frozen); if (scaled_font->global_cache_frozen) { CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); _cairo_cache_thaw (&cairo_scaled_glyph_page_cache); CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); scaled_font->global_cache_frozen = FALSE; } scaled_font->cache_frozen = FALSE; CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } void _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) { CAIRO_MUTEX_LOCK (scaled_font->mutex); assert (! scaled_font->cache_frozen); assert (! scaled_font->global_cache_frozen); CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { cairo_scaled_glyph_page_t *page = cairo_list_first_entry (&scaled_font->glyph_pages, cairo_scaled_glyph_page_t, link); cairo_scaled_glyph_page_cache.size -= page->cache_entry.size; _cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table, (cairo_hash_entry_t *) &page->cache_entry); _cairo_scaled_glyph_page_destroy (scaled_font, page); } CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } cairo_status_t _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *fs_metrics) { cairo_status_t status; double font_scale_x, font_scale_y; scaled_font->fs_extents = *fs_metrics; status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, &font_scale_x, &font_scale_y, 1); if (unlikely (status)) return status; /* * The font responded in unscaled units, scale by the font * matrix scale factors to get to user space */ scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y; scaled_font->extents.descent = fs_metrics->descent * font_scale_y; scaled_font->extents.height = fs_metrics->height * font_scale_y; scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x; scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y; return CAIRO_STATUS_SUCCESS; } static void _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) { assert (! scaled_font->cache_frozen); assert (! scaled_font->global_cache_frozen); scaled_font->finished = TRUE; _cairo_scaled_font_reset_cache (scaled_font); _cairo_hash_table_destroy (scaled_font->glyphs); cairo_font_face_destroy (scaled_font->font_face); cairo_font_face_destroy (scaled_font->original_font_face); CAIRO_MUTEX_FINI (scaled_font->mutex); while (! cairo_list_is_empty (&scaled_font->dev_privates)) { cairo_scaled_font_private_t *private = cairo_list_first_entry (&scaled_font->dev_privates, cairo_scaled_font_private_t, link); private->destroy (private, scaled_font); } if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL) scaled_font->backend->fini (scaled_font); _cairo_user_data_array_fini (&scaled_font->user_data); } void _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) { /* Release the lock to avoid the possibility of a recursive * deadlock when the scaled font destroy closure gets called. */ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); _cairo_scaled_font_fini_internal (scaled_font); CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); } void _cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font, cairo_scaled_font_private_t *private, const void *key, void (*destroy) (cairo_scaled_font_private_t *, cairo_scaled_font_t *)) { private->key = key; private->destroy = destroy; cairo_list_add (&private->link, &scaled_font->dev_privates); } cairo_scaled_font_private_t * _cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font, const void *key) { cairo_scaled_font_private_t *priv; cairo_list_foreach_entry (priv, cairo_scaled_font_private_t, &scaled_font->dev_privates, link) { if (priv->key == key) { if (priv->link.prev != &scaled_font->dev_privates) cairo_list_move (&priv->link, &scaled_font->dev_privates); return priv; } } return NULL; } void _cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_private_t *private, const void *key, void (*destroy) (cairo_scaled_glyph_private_t *, cairo_scaled_glyph_t *, cairo_scaled_font_t *)) { private->key = key; private->destroy = destroy; cairo_list_add (&private->link, &scaled_glyph->dev_privates); } cairo_scaled_glyph_private_t * _cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph, const void *key) { cairo_scaled_glyph_private_t *priv; cairo_list_foreach_entry (priv, cairo_scaled_glyph_private_t, &scaled_glyph->dev_privates, link) { if (priv->key == key) { if (priv->link.prev != &scaled_glyph->dev_privates) cairo_list_move (&priv->link, &scaled_glyph->dev_privates); return priv; } } return NULL; } /** * cairo_scaled_font_create: * @font_face: a #cairo_font_face_t * @font_matrix: font space to user space transformation matrix for the * font. In the simplest case of a N point font, this matrix is * just a scale by N, but it can also be used to shear the font * or stretch it unequally along the two axes. See * cairo_set_font_matrix(). * @ctm: user to device transformation matrix with which the font will * be used. * @options: options to use when getting metrics for the font and * rendering with it. * * Creates a #cairo_scaled_font_t object from a font face and matrices that * describe the size of the font and the environment in which it will * be used. * * Return value: a newly created #cairo_scaled_font_t. Destroy with * cairo_scaled_font_destroy() * * Since: 1.0 **/ cairo_scaled_font_t * cairo_scaled_font_create (cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { cairo_status_t status; cairo_scaled_font_map_t *font_map; cairo_font_face_t *original_font_face = font_face; cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL; double det; status = font_face->status; if (unlikely (status)) return _cairo_scaled_font_create_in_error (status); det = _cairo_matrix_compute_determinant (font_matrix); if (! ISFINITE (det)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); det = _cairo_matrix_compute_determinant (ctm); if (! ISFINITE (det)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); status = cairo_font_options_status ((cairo_font_options_t *) options); if (unlikely (status)) return _cairo_scaled_font_create_in_error (status); /* Note that degenerate ctm or font_matrix *are* allowed. * We want to support a font size of 0. */ font_map = _cairo_scaled_font_map_lock (); if (unlikely (font_map == NULL)) return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); scaled_font = font_map->mru_scaled_font; if (scaled_font != NULL && _cairo_scaled_font_matches (scaled_font, font_face, font_matrix, ctm, options)) { assert (scaled_font->hash_entry.hash != ZOMBIE); assert (! scaled_font->placeholder); if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still * held. */ _cairo_reference_count_inc (&scaled_font->ref_count); _cairo_scaled_font_map_unlock (); return scaled_font; } /* the font has been put into an error status - abandon the cache */ _cairo_hash_table_remove (font_map->hash_table, &scaled_font->hash_entry); scaled_font->hash_entry.hash = ZOMBIE; dead = scaled_font; font_map->mru_scaled_font = NULL; } _cairo_scaled_font_init_key (&key, font_face, font_matrix, ctm, options); while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, &key.hash_entry))) { if (! scaled_font->placeholder) break; /* If the scaled font is being created (happens for user-font), * just wait until it's done, then retry */ _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font); } if (scaled_font != NULL) { /* If the original reference count is 0, then this font must have * been found in font_map->holdovers, (which means this caching is * actually working). So now we remove it from the holdovers * array, unless we caught the font in the middle of destruction. */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { if (scaled_font->holdover) { int i; for (i = 0; i < font_map->num_holdovers; i++) { if (font_map->holdovers[i] == scaled_font) { font_map->num_holdovers--; memmove (&font_map->holdovers[i], &font_map->holdovers[i+1], (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); break; } } scaled_font->holdover = FALSE; } /* reset any error status */ scaled_font->status = CAIRO_STATUS_SUCCESS; } if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { /* We increment the reference count manually here, (rather * than calling into cairo_scaled_font_reference), since we * must modify the reference count while our lock is still * held. */ old = font_map->mru_scaled_font; font_map->mru_scaled_font = scaled_font; /* increment reference count for the mru cache */ _cairo_reference_count_inc (&scaled_font->ref_count); /* and increment for the returned reference */ _cairo_reference_count_inc (&scaled_font->ref_count); _cairo_scaled_font_map_unlock (); cairo_scaled_font_destroy (old); if (font_face != original_font_face) cairo_font_face_destroy (font_face); return scaled_font; } /* the font has been put into an error status - abandon the cache */ _cairo_hash_table_remove (font_map->hash_table, &scaled_font->hash_entry); scaled_font->hash_entry.hash = ZOMBIE; } /* Otherwise create it and insert it into the hash table. */ if (font_face->backend->get_implementation != NULL) { font_face = font_face->backend->get_implementation (font_face, font_matrix, ctm, options); if (unlikely (font_face->status)) { _cairo_scaled_font_map_unlock (); return _cairo_scaled_font_create_in_error (font_face->status); } } status = font_face->backend->scaled_font_create (font_face, font_matrix, ctm, options, &scaled_font); /* Did we leave the backend in an error state? */ if (unlikely (status)) { _cairo_scaled_font_map_unlock (); if (font_face != original_font_face) cairo_font_face_destroy (font_face); if (dead != NULL) cairo_scaled_font_destroy (dead); status = _cairo_font_face_set_error (font_face, status); return _cairo_scaled_font_create_in_error (status); } /* Or did we encounter an error whilst constructing the scaled font? */ if (unlikely (scaled_font->status)) { _cairo_scaled_font_map_unlock (); if (font_face != original_font_face) cairo_font_face_destroy (font_face); if (dead != NULL) cairo_scaled_font_destroy (dead); return scaled_font; } /* Our caching above is defeated if the backend switches fonts on us - * e.g. old incarnations of toy-font-face and lazily resolved * ft-font-faces */ assert (scaled_font->font_face == font_face); assert (! scaled_font->cache_frozen); assert (! scaled_font->global_cache_frozen); scaled_font->original_font_face = cairo_font_face_reference (original_font_face); scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash(scaled_font); status = _cairo_hash_table_insert (font_map->hash_table, &scaled_font->hash_entry); if (likely (status == CAIRO_STATUS_SUCCESS)) { old = font_map->mru_scaled_font; font_map->mru_scaled_font = scaled_font; _cairo_reference_count_inc (&scaled_font->ref_count); } _cairo_scaled_font_map_unlock (); cairo_scaled_font_destroy (old); if (font_face != original_font_face) cairo_font_face_destroy (font_face); if (dead != NULL) cairo_scaled_font_destroy (dead); if (unlikely (status)) { /* We can't call _cairo_scaled_font_destroy here since it expects * that the font has already been successfully inserted into the * hash table. */ _cairo_scaled_font_fini_internal (scaled_font); free (scaled_font); return _cairo_scaled_font_create_in_error (status); } return scaled_font; } slim_hidden_def (cairo_scaled_font_create); static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1]; /* XXX This should disappear in favour of a common pool of error objects. */ cairo_scaled_font_t * _cairo_scaled_font_create_in_error (cairo_status_t status) { cairo_scaled_font_t *scaled_font; assert (status != CAIRO_STATUS_SUCCESS); if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); scaled_font = _cairo_scaled_font_nil_objects[status]; if (unlikely (scaled_font == NULL)) { scaled_font = malloc (sizeof (cairo_scaled_font_t)); if (unlikely (scaled_font == NULL)) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; } *scaled_font = _cairo_scaled_font_nil; scaled_font->status = status; _cairo_scaled_font_nil_objects[status] = scaled_font; } CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); return scaled_font; } void _cairo_scaled_font_reset_static_data (void) { int status; CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); for (status = CAIRO_STATUS_SUCCESS; status <= CAIRO_STATUS_LAST_STATUS; status++) { free (_cairo_scaled_font_nil_objects[status]); _cairo_scaled_font_nil_objects[status] = NULL; } CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); if (cairo_scaled_glyph_page_cache.hash_table != NULL) { _cairo_cache_fini (&cairo_scaled_glyph_page_cache); cairo_scaled_glyph_page_cache.hash_table = NULL; } CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); } /** * cairo_scaled_font_reference: * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case * this function does nothing) * * Increases the reference count on @scaled_font by one. This prevents * @scaled_font from being destroyed until a matching call to * cairo_scaled_font_destroy() is made. * * The number of references to a #cairo_scaled_font_t can be get using * cairo_scaled_font_get_reference_count(). * * Returns: the referenced #cairo_scaled_font_t * * Since: 1.0 **/ cairo_scaled_font_t * cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) { if (scaled_font == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return scaled_font; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); _cairo_reference_count_inc (&scaled_font->ref_count); return scaled_font; } slim_hidden_def (cairo_scaled_font_reference); /** * cairo_scaled_font_destroy: * @scaled_font: a #cairo_scaled_font_t * * Decreases the reference count on @font by one. If the result * is zero, then @font and all associated resources are freed. * See cairo_scaled_font_reference(). * * Since: 1.0 **/ void cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) { cairo_scaled_font_t *lru = NULL; cairo_scaled_font_map_t *font_map; assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex)); if (scaled_font == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) return; assert (! scaled_font->cache_frozen); assert (! scaled_font->global_cache_frozen); font_map = _cairo_scaled_font_map_lock (); assert (font_map != NULL); /* Another thread may have resurrected the font whilst we waited */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { if (! scaled_font->placeholder && scaled_font->hash_entry.hash != ZOMBIE) { /* Another thread may have already inserted us into the holdovers */ if (scaled_font->holdover) goto unlock; /* Rather than immediately destroying this object, we put it into * the font_map->holdovers array in case it will get used again * soon (and is why we must hold the lock over the atomic op on * the reference count). To make room for it, we do actually * destroy the least-recently-used holdover. */ if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { lru = font_map->holdovers[0]; assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count)); _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry); font_map->num_holdovers--; memmove (&font_map->holdovers[0], &font_map->holdovers[1], font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); } font_map->holdovers[font_map->num_holdovers++] = scaled_font; scaled_font->holdover = TRUE; } else lru = scaled_font; } unlock: _cairo_scaled_font_map_unlock (); /* If we pulled an item from the holdovers array, (while the font * map lock was held, of course), then there is no way that anyone * else could have acquired a reference to it. So we can now * safely call fini on it without any lock held. This is desirable * as we never want to call into any backend function with a lock * held. */ if (lru != NULL) { _cairo_scaled_font_fini_internal (lru); free (lru); } } slim_hidden_def (cairo_scaled_font_destroy); /** * cairo_scaled_font_get_reference_count: * @scaled_font: a #cairo_scaled_font_t * * Returns the current reference count of @scaled_font. * * Return value: the current reference count of @scaled_font. If the * object is a nil object, 0 will be returned. * * Since: 1.4 **/ unsigned int cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font) { if (scaled_font == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count); } /** * cairo_scaled_font_get_user_data: * @scaled_font: a #cairo_scaled_font_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @scaled_font using the * specified key. If no user data has been attached with the given * key this function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.4 **/ void * cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key) { return _cairo_user_data_array_get_data (&scaled_font->user_data, key); } slim_hidden_def (cairo_scaled_font_get_user_data); /** * cairo_scaled_font_set_user_data: * @scaled_font: a #cairo_scaled_font_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the #cairo_scaled_font_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @scaled_font. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.4 **/ cairo_status_t cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) return scaled_font->status; return _cairo_user_data_array_set_data (&scaled_font->user_data, key, user_data, destroy); } slim_hidden_def (cairo_scaled_font_set_user_data); /* Public font API follows. */ /** * cairo_scaled_font_extents: * @scaled_font: a #cairo_scaled_font_t * @extents: a #cairo_font_extents_t which to store the retrieved extents. * * Gets the metrics for a #cairo_scaled_font_t. * * Since: 1.0 **/ void cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *extents) { if (scaled_font->status) { extents->ascent = 0.0; extents->descent = 0.0; extents->height = 0.0; extents->max_x_advance = 0.0; extents->max_y_advance = 0.0; return; } *extents = scaled_font->extents; } slim_hidden_def (cairo_scaled_font_extents); /** * cairo_scaled_font_text_extents: * @scaled_font: a #cairo_scaled_font_t * @utf8: a NUL-terminated string of text, encoded in UTF-8 * @extents: a #cairo_text_extents_t which to store the retrieved extents. * * Gets the extents for a string of text. The extents describe a * user-space rectangle that encloses the "inked" portion of the text * drawn at the origin (0,0) (as it would be drawn by cairo_show_text() * if the cairo graphics state were set to the same font_face, * font_matrix, ctm, and font_options as @scaled_font). Additionally, * the x_advance and y_advance values indicate the amount by which the * current point would be advanced by cairo_show_text(). * * Note that whitespace characters do not directly contribute to the * size of the rectangle (extents.width and extents.height). They do * contribute indirectly by changing the position of non-whitespace * characters. In particular, trailing whitespace characters are * likely to not affect the size of the rectangle, though they will * affect the x_advance and y_advance values. * * Since: 1.2 **/ void cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, const char *utf8, cairo_text_extents_t *extents) { cairo_status_t status; cairo_glyph_t *glyphs = NULL; int num_glyphs; if (scaled_font->status) goto ZERO_EXTENTS; if (utf8 == NULL) goto ZERO_EXTENTS; status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., utf8, -1, &glyphs, &num_glyphs, NULL, NULL, NULL); if (unlikely (status)) { status = _cairo_scaled_font_set_error (scaled_font, status); goto ZERO_EXTENTS; } cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents); free (glyphs); return; ZERO_EXTENTS: extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; extents->x_advance = 0.0; extents->y_advance = 0.0; } /** * cairo_scaled_font_glyph_extents: * @scaled_font: a #cairo_scaled_font_t * @glyphs: an array of glyph IDs with X and Y offsets. * @num_glyphs: the number of glyphs in the @glyphs array * @extents: a #cairo_text_extents_t which to store the retrieved extents. * * Gets the extents for an array of glyphs. The extents describe a * user-space rectangle that encloses the "inked" portion of the * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo * graphics state were set to the same font_face, font_matrix, ctm, * and font_options as @scaled_font). Additionally, the x_advance and * y_advance values indicate the amount by which the current point * would be advanced by cairo_show_glyphs(). * * Note that whitespace glyphs do not contribute to the size of the * rectangle (extents.width and extents.height). * * Since: 1.0 **/ void cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) { cairo_status_t status; int i; double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; cairo_bool_t visible = FALSE; cairo_scaled_glyph_t *scaled_glyph = NULL; extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; extents->x_advance = 0.0; extents->y_advance = 0.0; if (unlikely (scaled_font->status)) goto ZERO_EXTENTS; if (num_glyphs == 0) goto ZERO_EXTENTS; if (unlikely (num_glyphs < 0)) { _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT); /* XXX Can't propagate error */ goto ZERO_EXTENTS; } if (unlikely (glyphs == NULL)) { _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); /* XXX Can't propagate error */ goto ZERO_EXTENTS; } _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { double left, top, right, bottom; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) { status = _cairo_scaled_font_set_error (scaled_font, status); goto UNLOCK; } /* "Ink" extents should skip "invisible" glyphs */ if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0) continue; left = scaled_glyph->metrics.x_bearing + glyphs[i].x; right = left + scaled_glyph->metrics.width; top = scaled_glyph->metrics.y_bearing + glyphs[i].y; bottom = top + scaled_glyph->metrics.height; if (!visible) { visible = TRUE; min_x = left; max_x = right; min_y = top; max_y = bottom; } else { if (left < min_x) min_x = left; if (right > max_x) max_x = right; if (top < min_y) min_y = top; if (bottom > max_y) max_y = bottom; } } if (visible) { extents->x_bearing = min_x - glyphs[0].x; extents->y_bearing = min_y - glyphs[0].y; extents->width = max_x - min_x; extents->height = max_y - min_y; } else { extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; } if (num_glyphs) { double x0, y0, x1, y1; x0 = glyphs[0].x; y0 = glyphs[0].y; /* scaled_glyph contains the glyph for num_glyphs - 1 already. */ x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance; y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance; extents->x_advance = x1 - x0; extents->y_advance = y1 - y0; } else { extents->x_advance = 0.0; extents->y_advance = 0.0; } UNLOCK: _cairo_scaled_font_thaw_cache (scaled_font); return; ZERO_EXTENTS: extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; extents->x_advance = 0.0; extents->y_advance = 0.0; } slim_hidden_def (cairo_scaled_font_glyph_extents); #define GLYPH_LUT_SIZE 64 static cairo_status_t cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t *scaled_font, double x, double y, const char *utf8, cairo_glyph_t *glyphs, cairo_text_cluster_t **clusters, int num_chars) { struct glyph_lut_elt { unsigned long index; double x_advance; double y_advance; } glyph_lut[GLYPH_LUT_SIZE]; uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE]; cairo_status_t status; const char *p; int i; for (i = 0; i < GLYPH_LUT_SIZE; i++) glyph_lut_unicode[i] = ~0U; p = utf8; for (i = 0; i < num_chars; i++) { int idx, num_bytes; uint32_t unicode; cairo_scaled_glyph_t *scaled_glyph; struct glyph_lut_elt *glyph_slot; num_bytes = _cairo_utf8_get_char_validated (p, &unicode); p += num_bytes; glyphs[i].x = x; glyphs[i].y = y; idx = unicode % ARRAY_LENGTH (glyph_lut); glyph_slot = &glyph_lut[idx]; if (glyph_lut_unicode[idx] == unicode) { glyphs[i].index = glyph_slot->index; x += glyph_slot->x_advance; y += glyph_slot->y_advance; } else { unsigned long g; g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); status = _cairo_scaled_glyph_lookup (scaled_font, g, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) return status; x += scaled_glyph->metrics.x_advance; y += scaled_glyph->metrics.y_advance; glyph_lut_unicode[idx] = unicode; glyph_slot->index = g; glyph_slot->x_advance = scaled_glyph->metrics.x_advance; glyph_slot->y_advance = scaled_glyph->metrics.y_advance; glyphs[i].index = g; } if (clusters) { (*clusters)[i].num_bytes = num_bytes; (*clusters)[i].num_glyphs = 1; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t *scaled_font, double x, double y, const char *utf8, cairo_glyph_t *glyphs, cairo_text_cluster_t **clusters, int num_chars) { const char *p; int i; p = utf8; for (i = 0; i < num_chars; i++) { unsigned long g; int num_bytes; uint32_t unicode; cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; num_bytes = _cairo_utf8_get_char_validated (p, &unicode); p += num_bytes; glyphs[i].x = x; glyphs[i].y = y; g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); /* * No advance needed for a single character string. So, let's speed up * one-character strings by skipping glyph lookup. */ if (num_chars > 1) { status = _cairo_scaled_glyph_lookup (scaled_font, g, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) return status; x += scaled_glyph->metrics.x_advance; y += scaled_glyph->metrics.y_advance; } glyphs[i].index = g; if (clusters) { (*clusters)[i].num_bytes = num_bytes; (*clusters)[i].num_glyphs = 1; } } return CAIRO_STATUS_SUCCESS; } /** * cairo_scaled_font_text_to_glyphs: * @x: X position to place first glyph * @y: Y position to place first glyph * @scaled_font: a #cairo_scaled_font_t * @utf8: a string of text encoded in UTF-8 * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated * @glyphs: pointer to array of glyphs to fill * @num_glyphs: pointer to number of glyphs * @clusters: pointer to array of cluster mapping information to fill, or %NULL * @num_clusters: pointer to number of clusters, or %NULL * @cluster_flags: pointer to location to store cluster flags corresponding to the * output @clusters, or %NULL * * Converts UTF-8 text to an array of glyphs, optionally with cluster * mapping, that can be used to render later using @scaled_font. * * If @glyphs initially points to a non-%NULL value, that array is used * as a glyph buffer, and @num_glyphs should point to the number of glyph * entries available there. If the provided glyph array is too short for * the conversion, a new glyph array is allocated using cairo_glyph_allocate() * and placed in @glyphs. Upon return, @num_glyphs always contains the * number of generated glyphs. If the value @glyphs points to has changed * after the call, the user is responsible for freeing the allocated glyph * array using cairo_glyph_free(). This may happen even if the provided * array was large enough. * * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL, * and cluster mapping will be computed. * The semantics of how cluster array allocation works is similar to the glyph * array. That is, * if @clusters initially points to a non-%NULL value, that array is used * as a cluster buffer, and @num_clusters should point to the number of cluster * entries available there. If the provided cluster array is too short for * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate() * and placed in @clusters. Upon return, @num_clusters always contains the * number of generated clusters. If the value @clusters points at has changed * after the call, the user is responsible for freeing the allocated cluster * array using cairo_text_cluster_free(). This may happen even if the provided * array was large enough. * * In the simplest case, @glyphs and @clusters can point to %NULL initially * and a suitable array will be allocated. In code: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t *glyphs = NULL; * int num_glyphs; * cairo_text_cluster_t *clusters = NULL; * int num_clusters; * cairo_text_cluster_flags_t cluster_flags; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * &clusters, &num_clusters, &cluster_flags); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, * glyphs, num_glyphs, * clusters, num_clusters, cluster_flags); * * cairo_glyph_free (glyphs); * cairo_text_cluster_free (clusters); * } * </programlisting></informalexample> * * If no cluster mapping is needed: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t *glyphs = NULL; * int num_glyphs; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * NULL, NULL, * NULL); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_glyphs (cr, glyphs, num_glyphs); * cairo_glyph_free (glyphs); * } * </programlisting></informalexample> * * If stack-based glyph and cluster arrays are to be used for small * arrays: * <informalexample><programlisting> * cairo_status_t status; * * cairo_glyph_t stack_glyphs[40]; * cairo_glyph_t *glyphs = stack_glyphs; * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]); * cairo_text_cluster_t stack_clusters[40]; * cairo_text_cluster_t *clusters = stack_clusters; * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]); * cairo_text_cluster_flags_t cluster_flags; * * status = cairo_scaled_font_text_to_glyphs (scaled_font, * x, y, * utf8, utf8_len, * &glyphs, &num_glyphs, * &clusters, &num_clusters, &cluster_flags); * * if (status == CAIRO_STATUS_SUCCESS) { * cairo_show_text_glyphs (cr, * utf8, utf8_len, * glyphs, num_glyphs, * clusters, num_clusters, cluster_flags); * * if (glyphs != stack_glyphs) * cairo_glyph_free (glyphs); * if (clusters != stack_clusters) * cairo_text_cluster_free (clusters); * } * </programlisting></informalexample> * * For details of how @clusters, @num_clusters, and @cluster_flags map input * UTF-8 text to the output glyphs see cairo_show_text_glyphs(). * * The output values can be readily passed to cairo_show_text_glyphs() * cairo_show_glyphs(), or related functions, assuming that the exact * same @scaled_font is used for the operation. * * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status * if the input values are wrong or if conversion failed. If the input * values are correct but the conversion failed, the error status is also * set on @scaled_font. * * Since: 1.8 **/ #define CACHING_THRESHOLD 16 cairo_status_t cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, double x, double y, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *cluster_flags) { int num_chars = 0; cairo_int_status_t status; cairo_glyph_t *orig_glyphs; cairo_text_cluster_t *orig_clusters; status = scaled_font->status; if (unlikely (status)) return status; /* A slew of sanity checks */ /* glyphs and num_glyphs can't be NULL */ if (glyphs == NULL || num_glyphs == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto BAIL; } /* Special case for NULL and -1 */ if (utf8 == NULL && utf8_len == -1) utf8_len = 0; /* No NULLs for non-NULLs! */ if ((utf8_len && utf8 == NULL) || (clusters && num_clusters == NULL) || (clusters && cluster_flags == NULL)) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto BAIL; } /* A -1 for utf8_len means NUL-terminated */ if (utf8_len == -1) utf8_len = strlen (utf8); /* A NULL *glyphs means no prealloced glyphs array */ if (glyphs && *glyphs == NULL) *num_glyphs = 0; /* A NULL *clusters means no prealloced clusters array */ if (clusters && *clusters == NULL) *num_clusters = 0; if (!clusters && num_clusters) { num_clusters = NULL; } if (cluster_flags) { *cluster_flags = FALSE; } if (!clusters && cluster_flags) { cluster_flags = NULL; } /* Apart from that, no negatives */ if (utf8_len < 0 || *num_glyphs < 0 || (num_clusters && *num_clusters < 0)) { status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto BAIL; } if (utf8_len == 0) { status = CAIRO_STATUS_SUCCESS; goto BAIL; } /* validate input so backend does not have to */ status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars); if (unlikely (status)) goto BAIL; _cairo_scaled_font_freeze_cache (scaled_font); orig_glyphs = *glyphs; orig_clusters = clusters ? *clusters : NULL; if (scaled_font->backend->text_to_glyphs) { status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { if (status == CAIRO_INT_STATUS_SUCCESS) { /* The checks here are crude; we only should do them in * user-font backend, but they don't hurt here. This stuff * can be hard to get right. */ if (*num_glyphs < 0) { status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } if (num_glyphs && *glyphs == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } if (clusters) { if (*num_clusters < 0) { status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } if (num_clusters && *clusters == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } /* Don't trust the backend, validate clusters! */ status = _cairo_validate_text_clusters (utf8, utf8_len, *glyphs, *num_glyphs, *clusters, *num_clusters, *cluster_flags); } } goto DONE; } } if (*num_glyphs < num_chars) { *glyphs = cairo_glyph_allocate (num_chars); if (unlikely (*glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } } *num_glyphs = num_chars; if (clusters) { if (*num_clusters < num_chars) { *clusters = cairo_text_cluster_allocate (num_chars); if (unlikely (*clusters == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto DONE; } } *num_clusters = num_chars; } if (num_chars > CACHING_THRESHOLD) status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font, x, y, utf8, *glyphs, clusters, num_chars); else status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font, x, y, utf8, *glyphs, clusters, num_chars); DONE: /* error that should be logged on scaled_font happened */ _cairo_scaled_font_thaw_cache (scaled_font); if (unlikely (status)) { *num_glyphs = 0; if (*glyphs != orig_glyphs) { cairo_glyph_free (*glyphs); *glyphs = orig_glyphs; } if (clusters) { *num_clusters = 0; if (*clusters != orig_clusters) { cairo_text_cluster_free (*clusters); *clusters = orig_clusters; } } } return _cairo_scaled_font_set_error (scaled_font, status); BAIL: /* error with input arguments */ if (num_glyphs) *num_glyphs = 0; if (num_clusters) *num_clusters = 0; return status; } slim_hidden_def (cairo_scaled_font_text_to_glyphs); static inline cairo_bool_t _range_contains_glyph (const cairo_box_t *extents, cairo_fixed_t left, cairo_fixed_t top, cairo_fixed_t right, cairo_fixed_t bottom) { if (left == right || top == bottom) return FALSE; return right > extents->p1.x && left < extents->p2.x && bottom > extents->p1.y && top < extents->p2.y; } static cairo_status_t _cairo_scaled_font_single_glyph_device_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyph, cairo_rectangle_int_t *extents) { cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; _cairo_scaled_font_freeze_cache (scaled_font); status = _cairo_scaled_glyph_lookup (scaled_font, glyph->index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_bool_t round_xy = _cairo_font_options_get_round_glyph_positions (&scaled_font->options) == CAIRO_ROUND_GLYPH_POS_ON; cairo_box_t box; cairo_fixed_t v; if (round_xy) v = _cairo_fixed_from_int (_cairo_lround (glyph->x)); else v = _cairo_fixed_from_double (glyph->x); box.p1.x = v + scaled_glyph->bbox.p1.x; box.p2.x = v + scaled_glyph->bbox.p2.x; if (round_xy) v = _cairo_fixed_from_int (_cairo_lround (glyph->y)); else v = _cairo_fixed_from_double (glyph->y); box.p1.y = v + scaled_glyph->bbox.p1.y; box.p2.y = v + scaled_glyph->bbox.p2.y; _cairo_box_round_to_rectangle (&box, extents); } _cairo_scaled_font_thaw_cache (scaled_font); return status; } /* * Compute a device-space bounding box for the glyphs. */ cairo_status_t _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_rectangle_int_t *extents, cairo_bool_t *overlap_out) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }}; cairo_scaled_glyph_t *glyph_cache[64]; cairo_bool_t overlap = overlap_out ? FALSE : TRUE; cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options); int i; if (unlikely (scaled_font->status)) return scaled_font->status; if (num_glyphs == 1) { if (overlap_out) *overlap_out = FALSE; return _cairo_scaled_font_single_glyph_device_extents (scaled_font, glyphs, extents); } _cairo_scaled_font_freeze_cache (scaled_font); memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; cairo_fixed_t x, y, x1, y1, x2, y2; int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) break; glyph_cache[cache_index] = scaled_glyph; } if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x)); else x = _cairo_fixed_from_double (glyphs[i].x); x1 = x + scaled_glyph->bbox.p1.x; x2 = x + scaled_glyph->bbox.p2.x; if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y)); else y = _cairo_fixed_from_double (glyphs[i].y); y1 = y + scaled_glyph->bbox.p1.y; y2 = y + scaled_glyph->bbox.p2.y; if (overlap == FALSE) overlap = _range_contains_glyph (&box, x1, y1, x2, y2); if (x1 < box.p1.x) box.p1.x = x1; if (x2 > box.p2.x) box.p2.x = x2; if (y1 < box.p1.y) box.p1.y = y1; if (y2 > box.p2.y) box.p2.y = y2; } _cairo_scaled_font_thaw_cache (scaled_font); if (unlikely (status)) return _cairo_scaled_font_set_error (scaled_font, status); if (box.p1.x < box.p2.x) { _cairo_box_round_to_rectangle (&box, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } if (overlap_out != NULL) *overlap_out = overlap; return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_rectangle_int_t *extents) { double x0, x1, y0, y1, pad; int i; /* If any of the factors are suspect (i.e. the font is broken), bail */ if (scaled_font->fs_extents.max_x_advance == 0 || scaled_font->fs_extents.height == 0 || scaled_font->max_scale == 0) { return FALSE; } assert (num_glyphs); x0 = x1 = glyphs[0].x; y0 = y1 = glyphs[0].y; for (i = 1; i < num_glyphs; i++) { double g; g = glyphs[i].x; if (g < x0) x0 = g; if (g > x1) x1 = g; g = glyphs[i].y; if (g < y0) y0 = g; if (g > y1) y1 = g; } pad = MAX(scaled_font->fs_extents.max_x_advance, scaled_font->fs_extents.height); pad *= scaled_font->max_scale; extents->x = floor (x0 - pad); extents->width = ceil (x1 + pad) - extents->x; extents->y = floor (y0 - pad); extents->height = ceil (y1 + pad) - extents->y; return TRUE; } #if 0 /* XXX win32 */ cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, int dest_x, int dest_y, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, cairo_region_t *clip_region) { cairo_int_status_t status; cairo_surface_t *mask = NULL; cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */ cairo_surface_pattern_t mask_pattern; int i; /* These operators aren't interpreted the same way by the backends; * they are implemented in terms of other operators in cairo-gstate.c */ assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); if (scaled_font->status) return scaled_font->status; if (!num_glyphs) return CAIRO_STATUS_SUCCESS; if (scaled_font->backend->show_glyphs != NULL) { int remaining_glyphs = num_glyphs; status = scaled_font->backend->show_glyphs (scaled_font, op, pattern, surface, source_x, source_y, dest_x, dest_y, width, height, glyphs, num_glyphs, clip_region, &remaining_glyphs); glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) status = CAIRO_INT_STATUS_SUCCESS; if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_scaled_font_set_error (scaled_font, status); } /* Font display routine either does not exist or failed. */ _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { int x, y; cairo_image_surface_t *glyph_surface; cairo_scaled_glyph_t *scaled_glyph; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto CLEANUP_MASK; glyph_surface = scaled_glyph->surface; /* To start, create the mask using the format from the first * glyph. Later we'll deal with different formats. */ if (mask == NULL) { mask_format = glyph_surface->format; mask = cairo_image_surface_create (mask_format, width, height); status = mask->status; if (unlikely (status)) goto CLEANUP_MASK; } /* If we have glyphs of different formats, we "upgrade" the mask * to the wider of the formats. */ if (glyph_surface->format != mask_format && _cairo_format_bits_per_pixel (mask_format) < _cairo_format_bits_per_pixel (glyph_surface->format) ) { cairo_surface_t *new_mask; switch (glyph_surface->format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: mask_format = glyph_surface->format; break; case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; mask_format = CAIRO_FORMAT_ARGB32; break; } new_mask = cairo_image_surface_create (mask_format, width, height); status = new_mask->status; if (unlikely (status)) { cairo_surface_destroy (new_mask); goto CLEANUP_MASK; } _cairo_pattern_init_for_surface (&mask_pattern, mask); /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is * never any component alpha here. */ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, &mask_pattern.base, new_mask, 0, 0, 0, 0, 0, 0, width, height, NULL); _cairo_pattern_fini (&mask_pattern.base); if (unlikely (status)) { cairo_surface_destroy (new_mask); goto CLEANUP_MASK; } cairo_surface_destroy (mask); mask = new_mask; } if (glyph_surface->width && glyph_surface->height) { cairo_surface_pattern_t glyph_pattern; /* round glyph locations to the nearest pixel */ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ x = _cairo_lround (glyphs[i].x - glyph_surface->base.device_transform.x0); y = _cairo_lround (glyphs[i].y - glyph_surface->base.device_transform.y0); _cairo_pattern_init_for_surface (&glyph_pattern, &glyph_surface->base); if (mask_format == CAIRO_FORMAT_ARGB32) glyph_pattern.base.has_component_alpha = TRUE; status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, &glyph_pattern.base, mask, 0, 0, 0, 0, x - dest_x, y - dest_y, glyph_surface->width, glyph_surface->height, NULL); _cairo_pattern_fini (&glyph_pattern.base); if (unlikely (status)) goto CLEANUP_MASK; } } _cairo_pattern_init_for_surface (&mask_pattern, mask); if (mask_format == CAIRO_FORMAT_ARGB32) mask_pattern.base.has_component_alpha = TRUE; status = _cairo_surface_composite (op, pattern, &mask_pattern.base, surface, source_x, source_y, 0, 0, dest_x, dest_y, width, height, clip_region); _cairo_pattern_fini (&mask_pattern.base); CLEANUP_MASK: _cairo_scaled_font_thaw_cache (scaled_font); if (mask != NULL) cairo_surface_destroy (mask); return _cairo_scaled_font_set_error (scaled_font, status); } #endif /* Add a single-device-unit rectangle to a path. */ static cairo_status_t _add_unit_rectangle_to_path (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y) { cairo_status_t status; status = _cairo_path_fixed_move_to (path, x, y); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (1), _cairo_fixed_from_int (0)); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (0), _cairo_fixed_from_int (1)); if (unlikely (status)) return status; status = _cairo_path_fixed_rel_line_to (path, _cairo_fixed_from_int (-1), _cairo_fixed_from_int (0)); if (unlikely (status)) return status; return _cairo_path_fixed_close_path (path); } /** * _trace_mask_to_path: * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8) * @path: An initialized path to hold the result * * Given a mask surface, (an alpha image), fill out the provided path * so that when filled it would result in something that approximates * the mask. * * Note: The current tracing code here is extremely primitive. It * operates only on an A1 surface, (converting an A8 surface to A1 if * necessary), and performs the tracing by drawing a little square * around each pixel that is on in the mask. We do not pretend that * this is a high-quality result. But we are leaving it up to someone * who cares enough about getting a better result to implement * something more sophisticated. **/ static cairo_status_t _trace_mask_to_path (cairo_image_surface_t *mask, cairo_path_fixed_t *path, double tx, double ty) { const uint8_t *row; int rows, cols, bytes_per_row; int x, y, bit; double xoff, yoff; cairo_fixed_t x0, y0; cairo_fixed_t px, py; cairo_status_t status; mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1); status = mask->base.status; if (unlikely (status)) return status; cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); x0 = _cairo_fixed_from_double (tx - xoff); y0 = _cairo_fixed_from_double (ty - yoff); bytes_per_row = (mask->width + 7) / 8; row = mask->data; for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { const uint8_t *byte_ptr = row; x = 0; py = _cairo_fixed_from_int (y); for (cols = bytes_per_row; cols--; ) { uint8_t byte = *byte_ptr++; if (byte == 0) { x += 8; continue; } byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { if (byte & bit) { px = _cairo_fixed_from_int (x); status = _add_unit_rectangle_to_path (path, px + x0, py + y0); if (unlikely (status)) goto BAIL; } } } } BAIL: cairo_surface_destroy (&mask->base); return status; } cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path) { cairo_int_status_t status; int i; status = scaled_font->status; if (unlikely (status)) return status; _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_path_fixed_append (path, scaled_glyph->path, _cairo_fixed_from_double (glyphs[i].x), _cairo_fixed_from_double (glyphs[i].y)); } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* If the font is incapable of providing a path, then we'll * have to trace our own from a surface. */ status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) goto BAIL; status = _trace_mask_to_path (scaled_glyph->surface, path, glyphs[i].x, glyphs[i].y); } if (unlikely (status)) goto BAIL; } BAIL: _cairo_scaled_font_thaw_cache (scaled_font); return _cairo_scaled_font_set_error (scaled_font, status); } /** * _cairo_scaled_glyph_set_metrics: * @scaled_glyph: a #cairo_scaled_glyph_t * @scaled_font: a #cairo_scaled_font_t * @fs_metrics: a #cairo_text_extents_t in font space * * _cairo_scaled_glyph_set_metrics() stores user space metrics * for the specified glyph given font space metrics. It is * called by the font backend when initializing a glyph with * %CAIRO_SCALED_GLYPH_INFO_METRICS. **/ void _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_text_extents_t *fs_metrics) { cairo_bool_t first = TRUE; double hm, wm; double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0; double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; double device_x_advance, device_y_advance; scaled_glyph->fs_metrics = *fs_metrics; for (hm = 0.0; hm <= 1.0; hm += 1.0) for (wm = 0.0; wm <= 1.0; wm += 1.0) { double x, y; /* Transform this corner to user space */ x = fs_metrics->x_bearing + fs_metrics->width * wm; y = fs_metrics->y_bearing + fs_metrics->height * hm; cairo_matrix_transform_point (&scaled_font->font_matrix, &x, &y); if (first) { min_user_x = max_user_x = x; min_user_y = max_user_y = y; } else { if (x < min_user_x) min_user_x = x; if (x > max_user_x) max_user_x = x; if (y < min_user_y) min_user_y = y; if (y > max_user_y) max_user_y = y; } /* Transform this corner to device space from glyph origin */ x = fs_metrics->x_bearing + fs_metrics->width * wm; y = fs_metrics->y_bearing + fs_metrics->height * hm; cairo_matrix_transform_distance (&scaled_font->scale, &x, &y); if (first) { min_device_x = max_device_x = x; min_device_y = max_device_y = y; } else { if (x < min_device_x) min_device_x = x; if (x > max_device_x) max_device_x = x; if (y < min_device_y) min_device_y = y; if (y > max_device_y) max_device_y = y; } first = FALSE; } scaled_glyph->metrics.x_bearing = min_user_x; scaled_glyph->metrics.y_bearing = min_user_y; scaled_glyph->metrics.width = max_user_x - min_user_x; scaled_glyph->metrics.height = max_user_y - min_user_y; scaled_glyph->metrics.x_advance = fs_metrics->x_advance; scaled_glyph->metrics.y_advance = fs_metrics->y_advance; cairo_matrix_transform_distance (&scaled_font->font_matrix, &scaled_glyph->metrics.x_advance, &scaled_glyph->metrics.y_advance); device_x_advance = fs_metrics->x_advance; device_y_advance = fs_metrics->y_advance; cairo_matrix_transform_distance (&scaled_font->scale, &device_x_advance, &device_y_advance); scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x); scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y); scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x); scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y); scaled_glyph->x_advance = _cairo_lround (device_x_advance); scaled_glyph->y_advance = _cairo_lround (device_y_advance); scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS; } void _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_image_surface_t *surface) { if (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); /* sanity check the backend glyph contents */ _cairo_debug_check_image_surface_is_defined (&surface->base); scaled_glyph->surface = surface; if (surface != NULL) scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; else scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE; } void _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_path_fixed_t *path) { if (scaled_glyph->path != NULL) _cairo_path_fixed_destroy (scaled_glyph->path); scaled_glyph->path = path; if (path != NULL) scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH; else scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH; } void _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_surface_t *recording_surface) { if (scaled_glyph->recording_surface != NULL) { cairo_surface_finish (scaled_glyph->recording_surface); cairo_surface_destroy (scaled_glyph->recording_surface); } scaled_glyph->recording_surface = recording_surface; if (recording_surface != NULL) scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; else scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; } static cairo_bool_t _cairo_scaled_glyph_page_can_remove (const void *closure) { const cairo_scaled_glyph_page_t *page = closure; const cairo_scaled_font_t *scaled_font; scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; return scaled_font->cache_frozen == 0; } static cairo_status_t _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t **scaled_glyph) { cairo_scaled_glyph_page_t *page; cairo_status_t status; assert (scaled_font->cache_frozen); /* only the first page in the list may contain available slots */ if (! cairo_list_is_empty (&scaled_font->glyph_pages)) { page = cairo_list_last_entry (&scaled_font->glyph_pages, cairo_scaled_glyph_page_t, link); if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) { *scaled_glyph = &page->glyphs[page->num_glyphs++]; return CAIRO_STATUS_SUCCESS; } } page = malloc (sizeof (cairo_scaled_glyph_page_t)); if (unlikely (page == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); page->cache_entry.hash = (unsigned long) scaled_font; page->cache_entry.size = 1; /* XXX occupancy weighting? */ page->num_glyphs = 0; CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); if (scaled_font->global_cache_frozen == FALSE) { if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) { status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, NULL, _cairo_scaled_glyph_page_can_remove, _cairo_scaled_glyph_page_pluck, MAX_GLYPH_PAGES_CACHED); if (unlikely (status)) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); free (page); return status; } } _cairo_cache_freeze (&cairo_scaled_glyph_page_cache); scaled_font->global_cache_frozen = TRUE; } status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache, &page->cache_entry); CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); if (unlikely (status)) { free (page); return status; } cairo_list_add_tail (&page->link, &scaled_font->glyph_pages); *scaled_glyph = &page->glyphs[page->num_glyphs++]; return CAIRO_STATUS_SUCCESS; } static void _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_scaled_glyph_page_t *page; assert (! cairo_list_is_empty (&scaled_font->glyph_pages)); page = cairo_list_last_entry (&scaled_font->glyph_pages, cairo_scaled_glyph_page_t, link); assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]); _cairo_scaled_glyph_fini (scaled_font, scaled_glyph); if (--page->num_glyphs == 0) { CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); /* Temporarily disconnect callback to avoid recursive locking */ cairo_scaled_glyph_page_cache.entry_destroy = NULL; _cairo_cache_remove (&cairo_scaled_glyph_page_cache, &page->cache_entry); _cairo_scaled_glyph_page_destroy (scaled_font, page); cairo_scaled_glyph_page_cache.entry_destroy = _cairo_scaled_glyph_page_pluck; CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); } } /** * _cairo_scaled_glyph_lookup: * @scaled_font: a #cairo_scaled_font_t * @index: the glyph to create * @info: a #cairo_scaled_glyph_info_t marking which portions of * the glyph should be filled in. * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph * is returned. * * If the desired info is not available, (for example, when trying to * get INFO_PATH with a bitmapped font), this function will return * %CAIRO_INT_STATUS_UNSUPPORTED. * * Note: This function must be called with the scaled font frozen, and it must * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled * font was not frozen, then there is no guarantee that the glyph would not be * evicted before you tried to access it.) See * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache(). * * Returns: a glyph with the requested portions filled in. Glyph * lookup is cached and glyph will be automatically freed along * with the scaled_font so no explicit free is required. * @info can be one or more of: * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space **/ cairo_int_status_t _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, unsigned long index, cairo_scaled_glyph_info_t info, cairo_scaled_glyph_t **scaled_glyph_ret) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_scaled_glyph_info_t need_info; *scaled_glyph_ret = NULL; if (unlikely (scaled_font->status)) return scaled_font->status; assert (CAIRO_MUTEX_IS_LOCKED(scaled_font->mutex)); assert (scaled_font->cache_frozen); if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* * Check cache for glyph */ scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs, (cairo_hash_entry_t *) &index); if (scaled_glyph == NULL) { status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph); if (unlikely (status)) goto err; memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t)); _cairo_scaled_glyph_set_index (scaled_glyph, index); cairo_list_init (&scaled_glyph->dev_privates); /* ask backend to initialize metrics and shape fields */ status = scaled_font->backend->scaled_glyph_init (scaled_font, scaled_glyph, info | CAIRO_SCALED_GLYPH_INFO_METRICS); if (unlikely (status)) { _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); goto err; } status = _cairo_hash_table_insert (scaled_font->glyphs, &scaled_glyph->hash_entry); if (unlikely (status)) { _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); goto err; } } /* * Check and see if the glyph, as provided, * already has the requested data and amend it if not */ need_info = info & ~scaled_glyph->has_info; if (need_info) { status = scaled_font->backend->scaled_glyph_init (scaled_font, scaled_glyph, need_info); if (unlikely (status)) goto err; /* Don't trust the scaled_glyph_init() return value, the font * backend may not even know about some of the info. For example, * no backend other than the user-fonts knows about recording-surface * glyph info. */ if (info & ~scaled_glyph->has_info) return CAIRO_INT_STATUS_UNSUPPORTED; } *scaled_glyph_ret = scaled_glyph; return CAIRO_STATUS_SUCCESS; err: /* It's not an error for the backend to not support the info we want. */ if (status != CAIRO_INT_STATUS_UNSUPPORTED) status = _cairo_scaled_font_set_error (scaled_font, status); return status; } double _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) { return scaled_font->max_scale; } /** * cairo_scaled_font_get_font_face: * @scaled_font: a #cairo_scaled_font_t * * Gets the font face that this scaled font uses. This might be the * font face passed to cairo_scaled_font_create(), but this does not * hold true for all possible cases. * * Return value: The #cairo_font_face_t with which @scaled_font was * created. This object is owned by cairo. To keep a reference to it, * you must call cairo_scaled_font_reference(). * * Since: 1.2 **/ cairo_font_face_t * cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font) { if (scaled_font->status) return (cairo_font_face_t*) &_cairo_font_face_nil; if (scaled_font->original_font_face != NULL) return scaled_font->original_font_face; return scaled_font->font_face; } slim_hidden_def (cairo_scaled_font_get_font_face); /** * cairo_scaled_font_get_font_matrix: * @scaled_font: a #cairo_scaled_font_t * @font_matrix: return value for the matrix * * Stores the font matrix with which @scaled_font was created into * @matrix. * * Since: 1.2 **/ void cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, cairo_matrix_t *font_matrix) { if (scaled_font->status) { cairo_matrix_init_identity (font_matrix); return; } *font_matrix = scaled_font->font_matrix; } slim_hidden_def (cairo_scaled_font_get_font_matrix); /** * cairo_scaled_font_get_ctm: * @scaled_font: a #cairo_scaled_font_t * @ctm: return value for the CTM * * Stores the CTM with which @scaled_font was created into @ctm. * Note that the translation offsets (x0, y0) of the CTM are ignored * by cairo_scaled_font_create(). So, the matrix this * function returns always has 0,0 as x0,y0. * * Since: 1.2 **/ void cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, cairo_matrix_t *ctm) { if (scaled_font->status) { cairo_matrix_init_identity (ctm); return; } *ctm = scaled_font->ctm; } slim_hidden_def (cairo_scaled_font_get_ctm); /** * cairo_scaled_font_get_scale_matrix: * @scaled_font: a #cairo_scaled_font_t * @scale_matrix: return value for the matrix * * Stores the scale matrix of @scaled_font into @matrix. * The scale matrix is product of the font matrix and the ctm * associated with the scaled font, and hence is the matrix mapping from * font space to device space. * * Since: 1.8 **/ void cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, cairo_matrix_t *scale_matrix) { if (scaled_font->status) { cairo_matrix_init_identity (scale_matrix); return; } *scale_matrix = scaled_font->scale; } /** * cairo_scaled_font_get_font_options: * @scaled_font: a #cairo_scaled_font_t * @options: return value for the font options * * Stores the font options with which @scaled_font was created into * @options. * * Since: 1.2 **/ void cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, cairo_font_options_t *options) { if (cairo_font_options_status (options)) return; if (scaled_font->status) { _cairo_font_options_init_default (options); return; } _cairo_font_options_init_copy (options, &scaled_font->options); } slim_hidden_def (cairo_scaled_font_get_font_options); �������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-script-private.h��������������������������0000664�0000000�0000000�00000004006�12710376503�0026520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SCRIPT_PRIVATE_H #define CAIRO_SCRIPT_PRIVATE_H #include "cairo.h" #include "cairo-script.h" #include "cairo-compiler-private.h" #include "cairo-output-stream-private.h" #include "cairo-types-private.h" CAIRO_BEGIN_DECLS cairo_private cairo_device_t * _cairo_script_context_create_internal (cairo_output_stream_t *stream); cairo_private void _cairo_script_context_attach_snapshots (cairo_device_t *device, cairo_bool_t enable); slim_hidden_proto (cairo_script_surface_create); CAIRO_END_DECLS #endif /* CAIRO_SCRIPT_PRIVATE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-script-surface.c��������������������������0000664�0000000�0000000�00000322037�12710376503�0026500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ /* The script surface is one that records all operations performed on * it in the form of a procedural script, similar in fashion to * PostScript but using Cairo's imaging model. In essence, this is * equivalent to the recording-surface, but as there is no impedance mismatch * between Cairo and CairoScript, we can generate output immediately * without having to copy and hold the data in memory. */ /** * SECTION:cairo-script * @Title: Script Surfaces * @Short_Description: Rendering to replayable scripts * @See_Also: #cairo_surface_t * * The script surface provides the ability to render to a native * script that matches the cairo drawing model. The scripts can * be replayed using tools under the util/cairo-script directoriy, * or with cairo-perf-trace. **/ /** * CAIRO_HAS_SCRIPT_SURFACE: * * Defined if the script surface backend is available. * The script surface backend is always built in since 1.12. * * Since: 1.12 **/ #include "cairoint.h" #include "cairo-script.h" #include "cairo-script-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-default-context-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-image-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-pattern-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-wrapper-private.h" #if CAIRO_HAS_FT_FONT #include "cairo-ft-private.h" #endif #include <ctype.h> #ifdef WORDS_BIGENDIAN #define to_be32(x) x #else #define to_be32(x) bswap_32(x) #endif #define _cairo_output_stream_puts(S, STR) \ _cairo_output_stream_write ((S), (STR), strlen (STR)) #define static cairo_warn static typedef struct _cairo_script_context cairo_script_context_t; typedef struct _cairo_script_surface cairo_script_surface_t; typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; typedef struct _cairo_script_font cairo_script_font_t; typedef struct _operand { enum { SURFACE, DEFERRED, } type; cairo_list_t link; } operand_t; struct deferred_finish { cairo_list_t link; operand_t operand; }; struct _cairo_script_context { cairo_device_t base; int active; int attach_snapshots; cairo_bool_t owns_stream; cairo_output_stream_t *stream; cairo_script_mode_t mode; struct _bitmap { unsigned long min; unsigned long count; unsigned int map[64]; struct _bitmap *next; } surface_id, font_id; cairo_list_t operands; cairo_list_t deferred; cairo_list_t fonts; cairo_list_t defines; }; struct _cairo_script_font { cairo_scaled_font_private_t base; cairo_bool_t has_sfnt; unsigned long id; unsigned long subset_glyph_index; cairo_list_t link; cairo_scaled_font_t *parent; }; struct _cairo_script_implicit_context { cairo_operator_t current_operator; cairo_fill_rule_t current_fill_rule; double current_tolerance; cairo_antialias_t current_antialias; cairo_stroke_style_t current_style; cairo_pattern_union_t current_source; cairo_matrix_t current_ctm; cairo_matrix_t current_stroke_matrix; cairo_matrix_t current_font_matrix; cairo_font_options_t current_font_options; cairo_scaled_font_t *current_scaled_font; cairo_path_fixed_t current_path; cairo_bool_t has_clip; }; struct _cairo_script_surface { cairo_surface_t base; cairo_surface_wrapper_t wrapper; cairo_surface_clipper_t clipper; operand_t operand; cairo_bool_t emitted; cairo_bool_t defined; cairo_bool_t active; double width, height; /* implicit flattened context */ cairo_script_implicit_context_t cr; }; static const cairo_surface_backend_t _cairo_script_surface_backend; static cairo_script_surface_t * _cairo_script_surface_create_internal (cairo_script_context_t *ctx, cairo_content_t content, cairo_rectangle_t *extents, cairo_surface_t *passthrough); static void _cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, cairo_scaled_font_t *scaled_font); static void _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); static void _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); static void _bitmap_release_id (struct _bitmap *b, unsigned long token) { struct _bitmap **prev = NULL; do { if (token < b->min + sizeof (b->map) * CHAR_BIT) { unsigned int bit, elem; token -= b->min; elem = token / (sizeof (b->map[0]) * CHAR_BIT); bit = token % (sizeof (b->map[0]) * CHAR_BIT); b->map[elem] &= ~(1 << bit); if (! --b->count && prev) { *prev = b->next; free (b); } return; } prev = &b->next; b = b->next; } while (b != NULL); } static cairo_status_t _bitmap_next_id (struct _bitmap *b, unsigned long *id) { struct _bitmap *bb, **prev = NULL; unsigned long min = 0; do { if (b->min != min) break; if (b->count < sizeof (b->map) * CHAR_BIT) { unsigned int n, m, bit; for (n = 0; n < ARRAY_LENGTH (b->map); n++) { if (b->map[n] == (unsigned int) -1) continue; for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) { if ((b->map[n] & bit) == 0) { b->map[n] |= bit; b->count++; *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; return CAIRO_STATUS_SUCCESS; } } } } min += sizeof (b->map) * CHAR_BIT; prev = &b->next; b = b->next; } while (b != NULL); bb = malloc (sizeof (struct _bitmap)); if (unlikely (bb == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); *prev = bb; bb->next = b; bb->min = min; bb->count = 1; bb->map[0] = 0x1; memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); *id = min; return CAIRO_STATUS_SUCCESS; } static void _bitmap_fini (struct _bitmap *b) { while (b != NULL) { struct _bitmap *next = b->next; free (b); b = next; } } static const char * _direction_to_string (cairo_bool_t backward) { static const char *names[] = { "FORWARD", "BACKWARD" }; assert (backward < ARRAY_LENGTH (names)); return names[backward]; } static const char * _operator_to_string (cairo_operator_t op) { static const char *names[] = { "CLEAR", /* CAIRO_OPERATOR_CLEAR */ "SOURCE", /* CAIRO_OPERATOR_SOURCE */ "OVER", /* CAIRO_OPERATOR_OVER */ "IN", /* CAIRO_OPERATOR_IN */ "OUT", /* CAIRO_OPERATOR_OUT */ "ATOP", /* CAIRO_OPERATOR_ATOP */ "DEST", /* CAIRO_OPERATOR_DEST */ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ "XOR", /* CAIRO_OPERATOR_XOR */ "ADD", /* CAIRO_OPERATOR_ADD */ "SATURATE", /* CAIRO_OPERATOR_SATURATE */ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ "SCREEN", /* CAIRO_OPERATOR_SCREEN */ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ "DARKEN", /* CAIRO_OPERATOR_DARKEN */ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ }; assert (op < ARRAY_LENGTH (names)); return names[op]; } static const char * _extend_to_string (cairo_extend_t extend) { static const char *names[] = { "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ }; assert (extend < ARRAY_LENGTH (names)); return names[extend]; } static const char * _filter_to_string (cairo_filter_t filter) { static const char *names[] = { "FILTER_FAST", /* CAIRO_FILTER_FAST */ "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ "FILTER_BEST", /* CAIRO_FILTER_BEST */ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ }; assert (filter < ARRAY_LENGTH (names)); return names[filter]; } static const char * _fill_rule_to_string (cairo_fill_rule_t rule) { static const char *names[] = { "WINDING", /* CAIRO_FILL_RULE_WINDING */ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ }; assert (rule < ARRAY_LENGTH (names)); return names[rule]; } static const char * _antialias_to_string (cairo_antialias_t antialias) { static const char *names[] = { "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ "ANTIALIAS_SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ "ANTIALIAS_FAST", /* CAIRO_ANTIALIAS_FAST */ "ANTIALIAS_GOOD", /* CAIRO_ANTIALIAS_GOOD */ "ANTIALIAS_BEST" /* CAIRO_ANTIALIAS_BEST */ }; assert (antialias < ARRAY_LENGTH (names)); return names[antialias]; } static const char * _line_cap_to_string (cairo_line_cap_t line_cap) { static const char *names[] = { "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ }; assert (line_cap < ARRAY_LENGTH (names)); return names[line_cap]; } static const char * _line_join_to_string (cairo_line_join_t line_join) { static const char *names[] = { "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ }; assert (line_join < ARRAY_LENGTH (names)); return names[line_join]; } static inline cairo_script_context_t * to_context (cairo_script_surface_t *surface) { return (cairo_script_context_t *) surface->base.device; } static cairo_bool_t target_is_active (cairo_script_surface_t *surface) { return cairo_list_is_first (&surface->operand.link, &to_context (surface)->operands); } static void target_push (cairo_script_surface_t *surface) { cairo_list_move (&surface->operand.link, &to_context (surface)->operands); } static int target_depth (cairo_script_surface_t *surface) { cairo_list_t *link; int depth = 0; cairo_list_foreach (link, &to_context (surface)->operands) { if (link == &surface->operand.link) break; depth++; } return depth; } static void _get_target (cairo_script_surface_t *surface) { cairo_script_context_t *ctx = to_context (surface); if (target_is_active (surface)) { _cairo_output_stream_puts (ctx->stream, "dup "); return; } if (surface->defined) { _cairo_output_stream_printf (ctx->stream, "s%u ", surface->base.unique_id); } else { int depth = target_depth (surface); assert (! cairo_list_is_empty (&surface->operand.link)); assert (! target_is_active (surface)); if (ctx->active) { _cairo_output_stream_printf (ctx->stream, "%d index ", depth); _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); } else { if (depth == 1) { _cairo_output_stream_puts (ctx->stream, "exch "); } else { _cairo_output_stream_printf (ctx->stream, "%d -1 roll ", depth); } target_push (surface); _cairo_output_stream_puts (ctx->stream, "dup "); } } } static const char * _content_to_string (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_ALPHA: return "ALPHA"; case CAIRO_CONTENT_COLOR: return "COLOR"; default: case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; } } static cairo_status_t _emit_surface (cairo_script_surface_t *surface) { cairo_script_context_t *ctx = to_context (surface); _cairo_output_stream_printf (ctx->stream, "<< /content //%s", _content_to_string (surface->base.content)); if (surface->width != -1 && surface->height != -1) { _cairo_output_stream_printf (ctx->stream, " /width %f /height %f", surface->width, surface->height); } if (surface->base.x_fallback_resolution != CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || surface->base.y_fallback_resolution != CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) { _cairo_output_stream_printf (ctx->stream, " /fallback-resolution [%f %f]", surface->base.x_fallback_resolution, surface->base.y_fallback_resolution); } if (surface->base.device_transform.x0 != 0. || surface->base.device_transform.y0 != 0.) { /* XXX device offset is encoded into the pattern matrices etc. */ if (0) { _cairo_output_stream_printf (ctx->stream, " /device-offset [%f %f]", surface->base.device_transform.x0, surface->base.device_transform.y0); } } _cairo_output_stream_puts (ctx->stream, " >> surface context\n"); surface->emitted = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_context (cairo_script_surface_t *surface) { cairo_script_context_t *ctx = to_context (surface); if (target_is_active (surface)) return CAIRO_STATUS_SUCCESS; while (! cairo_list_is_empty (&ctx->operands)) { operand_t *op; cairo_script_surface_t *old; op = cairo_list_first_entry (&ctx->operands, operand_t, link); if (op->type == DEFERRED) break; old = cairo_container_of (op, cairo_script_surface_t, operand); if (old == surface) break; if (old->active) break; if (! old->defined) { assert (old->emitted); _cairo_output_stream_printf (ctx->stream, "/target get /s%u exch def pop\n", old->base.unique_id); old->defined = TRUE; } else { _cairo_output_stream_puts (ctx->stream, "pop\n"); } cairo_list_del (&old->operand.link); } if (target_is_active (surface)) return CAIRO_STATUS_SUCCESS; if (! surface->emitted) { cairo_status_t status; status = _emit_surface (surface); if (unlikely (status)) return status; } else if (cairo_list_is_empty (&surface->operand.link)) { assert (surface->defined); _cairo_output_stream_printf (ctx->stream, "s%u context\n", surface->base.unique_id); _cairo_script_implicit_context_reset (&surface->cr); _cairo_surface_clipper_reset (&surface->clipper); } else { int depth = target_depth (surface); if (depth == 1) { _cairo_output_stream_puts (ctx->stream, "exch\n"); } else { _cairo_output_stream_printf (ctx->stream, "%d -1 roll\n", depth); } } target_push (surface); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_operator (cairo_script_surface_t *surface, cairo_operator_t op) { assert (target_is_active (surface)); if (surface->cr.current_operator == op) return CAIRO_STATUS_SUCCESS; surface->cr.current_operator = op; _cairo_output_stream_printf (to_context (surface)->stream, "//%s set-operator\n", _operator_to_string (op)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_fill_rule (cairo_script_surface_t *surface, cairo_fill_rule_t fill_rule) { assert (target_is_active (surface)); if (surface->cr.current_fill_rule == fill_rule) return CAIRO_STATUS_SUCCESS; surface->cr.current_fill_rule = fill_rule; _cairo_output_stream_printf (to_context (surface)->stream, "//%s set-fill-rule\n", _fill_rule_to_string (fill_rule)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_tolerance (cairo_script_surface_t *surface, double tolerance, cairo_bool_t force) { assert (target_is_active (surface)); if ((! force || fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && surface->cr.current_tolerance == tolerance) { return CAIRO_STATUS_SUCCESS; } surface->cr.current_tolerance = tolerance; _cairo_output_stream_printf (to_context (surface)->stream, "%f set-tolerance\n", tolerance); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_antialias (cairo_script_surface_t *surface, cairo_antialias_t antialias) { assert (target_is_active (surface)); if (surface->cr.current_antialias == antialias) return CAIRO_STATUS_SUCCESS; surface->cr.current_antialias = antialias; _cairo_output_stream_printf (to_context (surface)->stream, "//%s set-antialias\n", _antialias_to_string (antialias)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_line_width (cairo_script_surface_t *surface, double line_width, cairo_bool_t force) { assert (target_is_active (surface)); if ((! force || fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && surface->cr.current_style.line_width == line_width) { return CAIRO_STATUS_SUCCESS; } surface->cr.current_style.line_width = line_width; _cairo_output_stream_printf (to_context (surface)->stream, "%f set-line-width\n", line_width); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_line_cap (cairo_script_surface_t *surface, cairo_line_cap_t line_cap) { assert (target_is_active (surface)); if (surface->cr.current_style.line_cap == line_cap) return CAIRO_STATUS_SUCCESS; surface->cr.current_style.line_cap = line_cap; _cairo_output_stream_printf (to_context (surface)->stream, "//%s set-line-cap\n", _line_cap_to_string (line_cap)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_line_join (cairo_script_surface_t *surface, cairo_line_join_t line_join) { assert (target_is_active (surface)); if (surface->cr.current_style.line_join == line_join) return CAIRO_STATUS_SUCCESS; surface->cr.current_style.line_join = line_join; _cairo_output_stream_printf (to_context (surface)->stream, "//%s set-line-join\n", _line_join_to_string (line_join)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_miter_limit (cairo_script_surface_t *surface, double miter_limit, cairo_bool_t force) { assert (target_is_active (surface)); if ((! force || fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && surface->cr.current_style.miter_limit == miter_limit) { return CAIRO_STATUS_SUCCESS; } surface->cr.current_style.miter_limit = miter_limit; _cairo_output_stream_printf (to_context (surface)->stream, "%f set-miter-limit\n", miter_limit); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _dashes_equal (const double *a, const double *b, int num_dashes) { while (num_dashes--) { if (fabs (*a - *b) > 1e-5) return FALSE; a++, b++; } return TRUE; } static cairo_status_t _emit_dash (cairo_script_surface_t *surface, const double *dash, unsigned int num_dashes, double offset, cairo_bool_t force) { unsigned int n; assert (target_is_active (surface)); if (force && num_dashes == 0 && surface->cr.current_style.num_dashes == 0) { return CAIRO_STATUS_SUCCESS; } if (! force && (surface->cr.current_style.num_dashes == num_dashes && (num_dashes == 0 || (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) { return CAIRO_STATUS_SUCCESS; } if (num_dashes) { surface->cr.current_style.dash = _cairo_realloc_ab (surface->cr.current_style.dash, num_dashes, sizeof (double)); if (unlikely (surface->cr.current_style.dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (surface->cr.current_style.dash, dash, sizeof (double) * num_dashes); } else { free (surface->cr.current_style.dash); surface->cr.current_style.dash = NULL; } surface->cr.current_style.num_dashes = num_dashes; surface->cr.current_style.dash_offset = offset; _cairo_output_stream_puts (to_context (surface)->stream, "["); for (n = 0; n < num_dashes; n++) { _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]); if (n < num_dashes-1) _cairo_output_stream_puts (to_context (surface)->stream, " "); } _cairo_output_stream_printf (to_context (surface)->stream, "] %f set-dash\n", offset); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_stroke_style (cairo_script_surface_t *surface, const cairo_stroke_style_t *style, cairo_bool_t force) { cairo_status_t status; assert (target_is_active (surface)); status = _emit_line_width (surface, style->line_width, force); if (unlikely (status)) return status; status = _emit_line_cap (surface, style->line_cap); if (unlikely (status)) return status; status = _emit_line_join (surface, style->line_join); if (unlikely (status)) return status; status = _emit_miter_limit (surface, style->miter_limit, force); if (unlikely (status)) return status; status = _emit_dash (surface, style->dash, style->num_dashes, style->dash_offset, force); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static const char * _format_to_string (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: return "ARGB32"; case CAIRO_FORMAT_RGB30: return "RGB30"; case CAIRO_FORMAT_RGB24: return "RGB24"; case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; case CAIRO_FORMAT_A8: return "A8"; case CAIRO_FORMAT_A1: return "A1"; case CAIRO_FORMAT_INVALID: return "INVALID"; } ASSERT_NOT_REACHED; return "INVALID"; } static cairo_status_t _emit_solid_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; cairo_script_context_t *ctx = to_context (surface); if (! CAIRO_COLOR_IS_OPAQUE (&solid->color)) { if (! (surface->base.content & CAIRO_CONTENT_COLOR) || ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) { _cairo_output_stream_printf (ctx->stream, "%f a", solid->color.alpha); } else { _cairo_output_stream_printf (ctx->stream, "%f %f %f %f rgba", solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); } } else { if (solid->color.red_short == solid->color.green_short && solid->color.red_short == solid->color.blue_short) { _cairo_output_stream_printf (ctx->stream, "%f g", solid->color.red); } else { _cairo_output_stream_printf (ctx->stream, "%f %f %f rgb", solid->color.red, solid->color.green, solid->color.blue); } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, cairo_output_stream_t *output) { unsigned int n; for (n = 0; n < gradient->n_stops; n++) { _cairo_output_stream_printf (output, "\n %f %f %f %f %f add-color-stop", gradient->stops[n].offset, gradient->stops[n].color.red, gradient->stops[n].color.green, gradient->stops[n].color.blue, gradient->stops[n].color.alpha); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_linear_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); cairo_linear_pattern_t *linear; linear = (cairo_linear_pattern_t *) pattern; _cairo_output_stream_printf (ctx->stream, "%f %f %f %f linear", linear->pd1.x, linear->pd1.y, linear->pd2.x, linear->pd2.y); return _emit_gradient_color_stops (&linear->base, ctx->stream); } static cairo_status_t _emit_radial_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); cairo_radial_pattern_t *radial; radial = (cairo_radial_pattern_t *) pattern; _cairo_output_stream_printf (ctx->stream, "%f %f %f %f %f %f radial", radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius, radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius); return _emit_gradient_color_stops (&radial->base, ctx->stream); } static cairo_status_t _emit_mesh_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); cairo_pattern_t *mesh; cairo_status_t status; unsigned int i, n; mesh = (cairo_pattern_t *) pattern; status = cairo_mesh_pattern_get_patch_count (mesh, &n); if (unlikely (status)) return status; _cairo_output_stream_printf (ctx->stream, "mesh"); for (i = 0; i < n; i++) { cairo_path_t *path; cairo_path_data_t *data; int j; _cairo_output_stream_printf (ctx->stream, "\n begin-patch"); path = cairo_mesh_pattern_get_path (mesh, i); if (unlikely (path->status)) return path->status; for (j = 0; j < path->num_data; j+=data[0].header.length) { data = &path->data[j]; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: _cairo_output_stream_printf (ctx->stream, "\n %f %f m", data[1].point.x, data[1].point.y); break; case CAIRO_PATH_LINE_TO: _cairo_output_stream_printf (ctx->stream, "\n %f %f l", data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: _cairo_output_stream_printf (ctx->stream, "\n %f %f %f %f %f %f c", data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: break; } } cairo_path_destroy (path); for (j = 0; j < 4; j++) { double x, y; status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y); if (unlikely (status)) return status; _cairo_output_stream_printf (ctx->stream, "\n %d %f %f set-control-point", j, x, y); } for (j = 0; j < 4; j++) { double r, g, b, a; status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a); if (unlikely (status)) return status; _cairo_output_stream_printf (ctx->stream, "\n %d %f %f %f %f set-corner-color", j, r, g, b, a); } _cairo_output_stream_printf (ctx->stream, "\n end-patch"); } return CAIRO_STATUS_SUCCESS; } struct script_snapshot { cairo_surface_t base; }; static cairo_status_t script_snapshot_finish (void *abstract_surface) { return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t script_snapshot_backend = { CAIRO_SURFACE_TYPE_SCRIPT, script_snapshot_finish, }; static void detach_snapshot (cairo_surface_t *abstract_surface) { cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface; cairo_script_context_t *ctx = to_context (surface); _cairo_output_stream_printf (ctx->stream, "/s%d undef\n", surface->base.unique_id); } static void attach_snapshot (cairo_script_context_t *ctx, cairo_surface_t *source) { struct script_snapshot *surface; if (! ctx->attach_snapshots) return; surface = malloc (sizeof (*surface)); if (unlikely (surface == NULL)) return; _cairo_surface_init (&surface->base, &script_snapshot_backend, &ctx->base, source->content); _cairo_output_stream_printf (ctx->stream, "dup /s%d exch def ", surface->base.unique_id); _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot); cairo_surface_destroy (&surface->base); } static cairo_status_t _emit_recording_surface_pattern (cairo_script_surface_t *surface, cairo_recording_surface_t *source) { cairo_script_implicit_context_t old_cr; cairo_script_context_t *ctx = to_context (surface); cairo_script_surface_t *similar; cairo_surface_t *snapshot; cairo_rectangle_t r, *extents; cairo_status_t status; snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend); if (snapshot) { _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id); return CAIRO_INT_STATUS_SUCCESS; } extents = NULL; if (_cairo_recording_surface_get_bounds (&source->base, &r)) extents = &r; similar = _cairo_script_surface_create_internal (ctx, source->base.content, extents, NULL); if (unlikely (similar->base.status)) return similar->base.status; similar->base.is_clear = TRUE; _cairo_output_stream_printf (ctx->stream, "//%s ", _content_to_string (source->base.content)); if (extents) { _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]", extents->x, extents->y, extents->width, extents->height); } else _cairo_output_stream_puts (ctx->stream, "[]"); _cairo_output_stream_puts (ctx->stream, " record\n"); attach_snapshot (ctx, &source->base); _cairo_output_stream_puts (ctx->stream, "dup context\n"); target_push (similar); similar->emitted = TRUE; old_cr = surface->cr; _cairo_script_implicit_context_init (&surface->cr); status = _cairo_recording_surface_replay (&source->base, &similar->base); surface->cr = old_cr; if (unlikely (status)) { cairo_surface_destroy (&similar->base); return status; } cairo_list_del (&similar->operand.link); assert (target_is_active (surface)); _cairo_output_stream_puts (ctx->stream, "pop "); cairo_surface_destroy (&similar->base); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_script_surface_pattern (cairo_script_surface_t *surface, cairo_script_surface_t *source) { _get_target (source); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _write_image_surface (cairo_output_stream_t *output, const cairo_image_surface_t *image) { int stride, row, width; uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; uint8_t *rowdata; uint8_t *data; stride = image->stride; width = image->width; data = image->data; #if WORDS_BIGENDIAN switch (image->format) { case CAIRO_FORMAT_A1: for (row = image->height; row--; ) { _cairo_output_stream_write (output, data, (width+7)/8); data += stride; } break; case CAIRO_FORMAT_A8: for (row = image->height; row--; ) { _cairo_output_stream_write (output, data, width); data += stride; } break; case CAIRO_FORMAT_RGB16_565: for (row = image->height; row--; ) { _cairo_output_stream_write (output, data, 2*width); data += stride; } break; case CAIRO_FORMAT_RGB24: for (row = image->height; row--; ) { int col; rowdata = data; for (col = width; col--; ) { _cairo_output_stream_write (output, rowdata, 3); rowdata+=4; } data += stride; } break; case CAIRO_FORMAT_ARGB32: for (row = image->height; row--; ) { _cairo_output_stream_write (output, data, 4*width); data += stride; } break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; break; } #else if (stride > ARRAY_LENGTH (row_stack)) { rowdata = malloc (stride); if (unlikely (rowdata == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else rowdata = row_stack; switch (image->format) { case CAIRO_FORMAT_A1: for (row = image->height; row--; ) { int col; for (col = 0; col < (width + 7)/8; col++) rowdata[col] = CAIRO_BITSWAP8 (data[col]); _cairo_output_stream_write (output, rowdata, (width+7)/8); data += stride; } break; case CAIRO_FORMAT_A8: for (row = image->height; row--; ) { _cairo_output_stream_write (output, data, width); data += stride; } break; case CAIRO_FORMAT_RGB16_565: for (row = image->height; row--; ) { uint16_t *src = (uint16_t *) data; uint16_t *dst = (uint16_t *) rowdata; int col; for (col = 0; col < width; col++) dst[col] = bswap_16 (src[col]); _cairo_output_stream_write (output, rowdata, 2*width); data += stride; } break; case CAIRO_FORMAT_RGB24: for (row = image->height; row--; ) { uint8_t *src = data; int col; for (col = 0; col < width; col++) { rowdata[3*col+2] = *src++; rowdata[3*col+1] = *src++; rowdata[3*col+0] = *src++; src++; } _cairo_output_stream_write (output, rowdata, 3*width); data += stride; } break; case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: for (row = image->height; row--; ) { uint32_t *src = (uint32_t *) data; uint32_t *dst = (uint32_t *) rowdata; int col; for (col = 0; col < width; col++) dst[col] = bswap_32 (src[col]); _cairo_output_stream_write (output, rowdata, 4*width); data += stride; } break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; break; } if (rowdata != row_stack) free (rowdata); #endif return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _emit_png_surface (cairo_script_surface_t *surface, cairo_image_surface_t *image) { cairo_script_context_t *ctx = to_context (surface); cairo_output_stream_t *base85_stream; cairo_status_t status; const uint8_t *mime_data; unsigned long mime_data_length; cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_printf (ctx->stream, "<< " "/width %d " "/height %d " "/format //%s " "/mime-type (image/png) " "/source <~", image->width, image->height, _format_to_string (image->format)); base85_stream = _cairo_base85_stream_create (ctx->stream); _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) return status; _cairo_output_stream_puts (ctx->stream, "~> >> image "); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _emit_image_surface (cairo_script_surface_t *surface, cairo_image_surface_t *image) { cairo_script_context_t *ctx = to_context (surface); cairo_output_stream_t *base85_stream; cairo_output_stream_t *zlib_stream; cairo_int_status_t status, status2; cairo_surface_t *snapshot; const uint8_t *mime_data; unsigned long mime_data_length; snapshot = _cairo_surface_has_snapshot (&image->base, &script_snapshot_backend); if (snapshot) { _cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id); return CAIRO_INT_STATUS_SUCCESS; } status = _emit_png_surface (surface, image); if (_cairo_int_status_is_error (status)) { return status; } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_image_surface_t *clone; uint32_t len; if (image->format == CAIRO_FORMAT_INVALID) { clone = _cairo_image_surface_coerce (image); } else { clone = (cairo_image_surface_t *) cairo_surface_reference (&image->base); } _cairo_output_stream_printf (ctx->stream, "<< " "/width %d " "/height %d " "/format //%s " "/source ", clone->width, clone->height, _format_to_string (clone->format)); switch (clone->format) { case CAIRO_FORMAT_A1: len = (clone->width + 7)/8; break; case CAIRO_FORMAT_A8: len = clone->width; break; case CAIRO_FORMAT_RGB16_565: len = clone->width * 2; break; case CAIRO_FORMAT_RGB24: len = clone->width * 3; break; case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: len = clone->width * 4; break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; len = 0; break; } len *= clone->height; if (len > 24) { _cairo_output_stream_puts (ctx->stream, "<|"); base85_stream = _cairo_base85_stream_create (ctx->stream); len = to_be32 (len); _cairo_output_stream_write (base85_stream, &len, sizeof (len)); zlib_stream = _cairo_deflate_stream_create (base85_stream); status = _write_image_surface (zlib_stream, clone); status2 = _cairo_output_stream_destroy (zlib_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (base85_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; if (unlikely (status)) return status; } else { _cairo_output_stream_puts (ctx->stream, "<~"); base85_stream = _cairo_base85_stream_create (ctx->stream); status = _write_image_surface (base85_stream, clone); status2 = _cairo_output_stream_destroy (base85_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; if (unlikely (status)) return status; } _cairo_output_stream_puts (ctx->stream, "~> >> image "); cairo_surface_destroy (&clone->base); } cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (mime_data != NULL) { _cairo_output_stream_printf (ctx->stream, "\n (%s) <~", CAIRO_MIME_TYPE_JPEG); base85_stream = _cairo_base85_stream_create (ctx->stream); _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) return status; _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); } cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, &mime_data, &mime_data_length); if (mime_data != NULL) { _cairo_output_stream_printf (ctx->stream, "\n (%s) <~", CAIRO_MIME_TYPE_JP2); base85_stream = _cairo_base85_stream_create (ctx->stream); _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) return status; _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); } return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_image_surface_pattern (cairo_script_surface_t *surface, cairo_surface_t *source) { cairo_image_surface_t *image; cairo_status_t status; void *extra; status = _cairo_surface_acquire_source_image (source, &image, &extra); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _emit_image_surface (surface, image); _cairo_surface_release_source_image (source, image, extra); } return status; } static cairo_int_status_t _emit_subsurface_pattern (cairo_script_surface_t *surface, cairo_surface_subsurface_t *sub) { cairo_surface_t *source = sub->target; cairo_int_status_t status; switch ((int) source->backend->type) { case CAIRO_SURFACE_TYPE_RECORDING: status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); break; case CAIRO_SURFACE_TYPE_SCRIPT: status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); break; default: status = _emit_image_surface_pattern (surface, source); break; } if (unlikely (status)) return status; _cairo_output_stream_printf (to_context (surface)->stream, "%d %d %d %d subsurface ", sub->extents.x, sub->extents.y, sub->extents.width, sub->extents.height); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_surface_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); cairo_surface_pattern_t *surface_pattern; cairo_surface_t *source, *snapshot, *free_me = NULL; cairo_surface_t *take_snapshot = NULL; cairo_int_status_t status; surface_pattern = (cairo_surface_pattern_t *) pattern; source = surface_pattern->surface; if (_cairo_surface_is_snapshot (source)) { snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend); if (snapshot) { _cairo_output_stream_printf (ctx->stream, "s%d pattern ", snapshot->unique_id); return CAIRO_INT_STATUS_SUCCESS; } if (_cairo_surface_snapshot_is_reused (source)) take_snapshot = source; free_me = source = _cairo_surface_snapshot_get_target (source); } switch ((int) source->backend->type) { case CAIRO_SURFACE_TYPE_RECORDING: status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); break; case CAIRO_SURFACE_TYPE_SCRIPT: status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); break; case CAIRO_SURFACE_TYPE_SUBSURFACE: status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source); break; default: status = _emit_image_surface_pattern (surface, source); break; } cairo_surface_destroy (free_me); if (unlikely (status)) return status; if (take_snapshot) attach_snapshot (ctx, take_snapshot); _cairo_output_stream_puts (ctx->stream, "pattern"); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_raster_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_surface_t *source; cairo_int_status_t status; source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL); if (unlikely (source == NULL)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } if (unlikely (source->status)) return source->status; status = _emit_image_surface_pattern (surface, source); _cairo_raster_source_pattern_release (pattern, source); if (unlikely (status)) return status; _cairo_output_stream_puts (to_context(surface)->stream, "pattern"); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); cairo_int_status_t status; cairo_bool_t is_default_extend; cairo_bool_t need_newline = TRUE; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: /* solid colors do not need filter/extend/matrix */ return _emit_solid_pattern (surface, pattern); case CAIRO_PATTERN_TYPE_LINEAR: status = _emit_linear_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; break; case CAIRO_PATTERN_TYPE_RADIAL: status = _emit_radial_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; break; case CAIRO_PATTERN_TYPE_MESH: status = _emit_mesh_pattern (surface, pattern); is_default_extend = TRUE; break; case CAIRO_PATTERN_TYPE_SURFACE: status = _emit_surface_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: status = _emit_raster_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; break; default: ASSERT_NOT_REACHED; status = CAIRO_INT_STATUS_UNSUPPORTED; } if (unlikely (status)) return status; if (! _cairo_matrix_is_identity (&pattern->matrix)) { if (need_newline) { _cairo_output_stream_puts (ctx->stream, "\n "); need_newline = FALSE; } _cairo_output_stream_printf (ctx->stream, " [%f %f %f %f %f %f] set-matrix\n ", pattern->matrix.xx, pattern->matrix.yx, pattern->matrix.xy, pattern->matrix.yy, pattern->matrix.x0, pattern->matrix.y0); } /* XXX need to discriminate the user explicitly setting the default */ if (pattern->filter != CAIRO_FILTER_DEFAULT) { if (need_newline) { _cairo_output_stream_puts (ctx->stream, "\n "); need_newline = FALSE; } _cairo_output_stream_printf (ctx->stream, " //%s set-filter\n ", _filter_to_string (pattern->filter)); } if (! is_default_extend ){ if (need_newline) { _cairo_output_stream_puts (ctx->stream, "\n "); need_newline = FALSE; } _cairo_output_stream_printf (ctx->stream, " //%s set-extend\n ", _extend_to_string (pattern->extend)); } if (need_newline) _cairo_output_stream_puts (ctx->stream, "\n "); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_identity (cairo_script_surface_t *surface, cairo_bool_t *matrix_updated) { assert (target_is_active (surface)); if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) return CAIRO_INT_STATUS_SUCCESS; _cairo_output_stream_puts (to_context (surface)->stream, "identity set-matrix\n"); *matrix_updated = TRUE; cairo_matrix_init_identity (&surface->cr.current_ctm); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _emit_source (cairo_script_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source) { cairo_bool_t matrix_updated = FALSE; cairo_int_status_t status; assert (target_is_active (surface)); if (op == CAIRO_OPERATOR_CLEAR) { /* the source is ignored, so don't change it */ return CAIRO_INT_STATUS_SUCCESS; } if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) return CAIRO_INT_STATUS_SUCCESS; _cairo_pattern_fini (&surface->cr.current_source.base); status = _cairo_pattern_init_copy (&surface->cr.current_source.base, source); if (unlikely (status)) return status; status = _emit_identity (surface, &matrix_updated); if (unlikely (status)) return status; status = _emit_pattern (surface, source); if (unlikely (status)) return status; assert (target_is_active (surface)); _cairo_output_stream_puts (to_context (surface)->stream, " set-source\n"); return CAIRO_INT_STATUS_SUCCESS; } static cairo_status_t _path_move_to (void *closure, const cairo_point_t *point) { _cairo_output_stream_printf (closure, " %f %f m", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _path_line_to (void *closure, const cairo_point_t *point) { _cairo_output_stream_printf (closure, " %f %f l", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _path_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { _cairo_output_stream_printf (closure, " %f %f %f %f %f %f c", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y), _cairo_fixed_to_double (p3->x), _cairo_fixed_to_double (p3->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _path_close (void *closure) { _cairo_output_stream_printf (closure, " h"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_path_boxes (cairo_script_surface_t *surface, const cairo_path_fixed_t *path) { cairo_script_context_t *ctx = to_context (surface); cairo_path_fixed_iter_t iter; cairo_status_t status; struct _cairo_boxes_chunk *chunk; cairo_boxes_t boxes; cairo_box_t box; int i; _cairo_boxes_init (&boxes); _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) continue; status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box); if (unlikely (status)) { _cairo_boxes_fini (&boxes); return status; } } if (! _cairo_path_fixed_iter_at_end (&iter)) { _cairo_boxes_fini (&boxes); return FALSE; } for (chunk = &boxes.chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { const cairo_box_t *b = &chunk->base[i]; double x1 = _cairo_fixed_to_double (b->p1.x); double y1 = _cairo_fixed_to_double (b->p1.y); double x2 = _cairo_fixed_to_double (b->p2.x); double y2 = _cairo_fixed_to_double (b->p2.y); _cairo_output_stream_printf (ctx->stream, "\n %f %f %f %f rectangle", x1, y1, x2 - x1, y2 - y1); } } _cairo_boxes_fini (&boxes); return status; } static cairo_status_t _emit_path (cairo_script_surface_t *surface, const cairo_path_fixed_t *path, cairo_bool_t is_fill) { cairo_script_context_t *ctx = to_context (surface); cairo_box_t box; cairo_int_status_t status; assert (target_is_active (surface)); assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) return CAIRO_STATUS_SUCCESS; _cairo_path_fixed_fini (&surface->cr.current_path); _cairo_output_stream_puts (ctx->stream, "n"); if (path == NULL) { _cairo_path_fixed_init (&surface->cr.current_path); _cairo_output_stream_puts (ctx->stream, "\n"); return CAIRO_STATUS_SUCCESS; } status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); if (unlikely (status)) return status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_is_rectangle (path, &box)) { double x1 = _cairo_fixed_to_double (box.p1.x); double y1 = _cairo_fixed_to_double (box.p1.y); double x2 = _cairo_fixed_to_double (box.p2.x); double y2 = _cairo_fixed_to_double (box.p2.y); assert (x1 > -9999); _cairo_output_stream_printf (ctx->stream, " %f %f %f %f rectangle", x1, y1, x2 - x1, y2 - y1); status = CAIRO_INT_STATUS_SUCCESS; } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) { status = _emit_path_boxes (surface, path); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_path_fixed_interpret (path, _path_move_to, _path_line_to, _path_curve_to, _path_close, ctx->stream); } _cairo_output_stream_puts (ctx->stream, "\n"); return status; } static cairo_bool_t _scaling_matrix_equal (const cairo_matrix_t *a, const cairo_matrix_t *b) { return fabs (a->xx - b->xx) < 1e-5 && fabs (a->xy - b->xy) < 1e-5 && fabs (a->yx - b->yx) < 1e-5 && fabs (a->yy - b->yy) < 1e-5; } static cairo_status_t _emit_scaling_matrix (cairo_script_surface_t *surface, const cairo_matrix_t *ctm, cairo_bool_t *matrix_updated) { cairo_script_context_t *ctx = to_context (surface); cairo_bool_t was_identity; assert (target_is_active (surface)); if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) return CAIRO_STATUS_SUCCESS; was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); *matrix_updated = TRUE; surface->cr.current_ctm = *ctm; surface->cr.current_ctm.x0 = 0.; surface->cr.current_ctm.y0 = 0.; if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { _cairo_output_stream_puts (ctx->stream, "identity set-matrix\n"); } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { _cairo_output_stream_printf (ctx->stream, "%f %f scale\n", ctm->xx, ctm->yy); } else { _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f 0 0] set-matrix\n", ctm->xx, ctm->yx, ctm->xy, ctm->yy); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_font_matrix (cairo_script_surface_t *surface, const cairo_matrix_t *font_matrix) { cairo_script_context_t *ctx = to_context (surface); assert (target_is_active (surface)); if (memcmp (&surface->cr.current_font_matrix, font_matrix, sizeof (cairo_matrix_t)) == 0) { return CAIRO_STATUS_SUCCESS; } surface->cr.current_font_matrix = *font_matrix; if (_cairo_matrix_is_identity (font_matrix)) { _cairo_output_stream_puts (ctx->stream, "identity set-font-matrix\n"); } else { _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f %f %f] set-font-matrix\n", font_matrix->xx, font_matrix->yx, font_matrix->xy, font_matrix->yy, font_matrix->x0, font_matrix->y0); } return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_script_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_script_surface_t *surface, *other = abstract_surface; cairo_surface_t *passthrough = NULL; cairo_script_context_t *ctx; cairo_rectangle_t extents; cairo_status_t status; ctx = to_context (other); status = cairo_device_acquire (&ctx->base); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (! other->emitted) { status = _emit_surface (other); if (unlikely (status)) { cairo_device_release (&ctx->base); return _cairo_surface_create_in_error (status); } target_push (other); } if (_cairo_surface_wrapper_is_active (&other->wrapper)) { passthrough = _cairo_surface_wrapper_create_similar (&other->wrapper, content, width, height); if (unlikely (passthrough->status)) { cairo_device_release (&ctx->base); return passthrough; } } extents.x = extents.y = 0; extents.width = width; extents.height = height; surface = _cairo_script_surface_create_internal (ctx, content, &extents, passthrough); cairo_surface_destroy (passthrough); if (unlikely (surface->base.status)) { cairo_device_release (&ctx->base); return &surface->base; } _get_target (other); _cairo_output_stream_printf (ctx->stream, "%u %u //%s similar dup /s%u exch def context\n", width, height, _content_to_string (content), surface->base.unique_id); surface->emitted = TRUE; surface->defined = TRUE; surface->base.is_clear = TRUE; target_push (surface); cairo_device_release (&ctx->base); return &surface->base; } static cairo_status_t _device_flush (void *abstract_device) { cairo_script_context_t *ctx = abstract_device; return _cairo_output_stream_flush (ctx->stream); } static void _device_destroy (void *abstract_device) { cairo_script_context_t *ctx = abstract_device; cairo_status_t status; while (! cairo_list_is_empty (&ctx->fonts)) { cairo_script_font_t *font; font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link); cairo_list_del (&font->base.link); cairo_list_del (&font->link); free (font); } _bitmap_fini (ctx->surface_id.next); _bitmap_fini (ctx->font_id.next); if (ctx->owns_stream) status = _cairo_output_stream_destroy (ctx->stream); free (ctx); } static cairo_surface_t * _cairo_script_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_script_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } static cairo_status_t _cairo_script_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_script_surface_t *surface = abstract_surface; if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, image_out, image_extra); } return CAIRO_INT_STATUS_UNSUPPORTED; } static void _cairo_script_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_script_surface_t *surface = abstract_surface; assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); _cairo_surface_wrapper_release_source_image (&surface->wrapper, image, image_extra); } static cairo_status_t _cairo_script_surface_finish (void *abstract_surface) { cairo_script_surface_t *surface = abstract_surface; cairo_script_context_t *ctx = to_context (surface); cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; _cairo_surface_wrapper_fini (&surface->wrapper); free (surface->cr.current_style.dash); surface->cr.current_style.dash = NULL; _cairo_pattern_fini (&surface->cr.current_source.base); _cairo_path_fixed_fini (&surface->cr.current_path); _cairo_surface_clipper_reset (&surface->clipper); status = cairo_device_acquire (&ctx->base); if (unlikely (status)) return status; if (surface->emitted) { assert (! surface->active); if (! cairo_list_is_empty (&surface->operand.link)) { if (! ctx->active) { if (target_is_active (surface)) { _cairo_output_stream_printf (ctx->stream, "pop\n"); } else { int depth = target_depth (surface); if (depth == 1) { _cairo_output_stream_printf (ctx->stream, "exch pop\n"); } else { _cairo_output_stream_printf (ctx->stream, "%d -1 roll pop\n", depth); } } cairo_list_del (&surface->operand.link); } else { struct deferred_finish *link = malloc (sizeof (*link)); if (link == NULL) { status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); if (status == CAIRO_STATUS_SUCCESS) status = status2; cairo_list_del (&surface->operand.link); } else { link->operand.type = DEFERRED; cairo_list_swap (&link->operand.link, &surface->operand.link); cairo_list_add (&link->link, &ctx->deferred); } } } if (surface->defined) { _cairo_output_stream_printf (ctx->stream, "/s%u undef\n", surface->base.unique_id); } } if (status == CAIRO_STATUS_SUCCESS) status = _cairo_output_stream_flush (to_context (surface)->stream); cairo_device_release (&ctx->base); return status; } static cairo_int_status_t _cairo_script_surface_copy_page (void *abstract_surface) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; status = cairo_device_acquire (surface->base.device); if (unlikely (status)) return status; status = _emit_context (surface); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n"); BAIL: cairo_device_release (surface->base.device); return status; } static cairo_int_status_t _cairo_script_surface_show_page (void *abstract_surface) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; status = cairo_device_acquire (surface->base.device); if (unlikely (status)) return status; status = _emit_context (surface); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n"); BAIL: cairo_device_release (surface->base.device); return status; } static cairo_status_t _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_script_surface_t *surface = cairo_container_of (clipper, cairo_script_surface_t, clipper); cairo_script_context_t *ctx = to_context (surface); cairo_bool_t matrix_updated = FALSE; cairo_status_t status; cairo_box_t box; status = _emit_context (surface); if (unlikely (status)) return status; if (path == NULL) { if (surface->cr.has_clip) { _cairo_output_stream_puts (ctx->stream, "reset-clip\n"); surface->cr.has_clip = FALSE; } return CAIRO_STATUS_SUCCESS; } /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && _cairo_path_fixed_is_box (path, &box)) { if (box.p1.x <= 0 && box.p1.y <= 0 && box.p2.x >= _cairo_fixed_from_double (surface->width) && box.p2.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } status = _emit_identity (surface, &matrix_updated); if (unlikely (status)) return status; status = _emit_fill_rule (surface, fill_rule); if (unlikely (status)) return status; if (path->has_curve_to) { status = _emit_tolerance (surface, tolerance, matrix_updated); if (unlikely (status)) return status; } if (! _cairo_path_fixed_fill_maybe_region (path)) { status = _emit_antialias (surface, antialias); if (unlikely (status)) return status; } status = _emit_path (surface, path, TRUE); if (unlikely (status)) return status; _cairo_output_stream_puts (ctx->stream, "clip+\n"); surface->cr.has_clip = TRUE; return CAIRO_STATUS_SUCCESS; } static cairo_status_t active (cairo_script_surface_t *surface) { cairo_status_t status; status = cairo_device_acquire (surface->base.device); if (unlikely (status)) return status; if (surface->active++ == 0) to_context (surface)->active++; return CAIRO_STATUS_SUCCESS; } static void inactive (cairo_script_surface_t *surface) { cairo_script_context_t *ctx = to_context (surface); cairo_list_t sorted; assert (surface->active > 0); if (--surface->active) goto DONE; assert (ctx->active > 0); if (--ctx->active) goto DONE; cairo_list_init (&sorted); while (! cairo_list_is_empty (&ctx->deferred)) { struct deferred_finish *df; cairo_list_t *operand; int depth; df = cairo_list_first_entry (&ctx->deferred, struct deferred_finish, link); depth = 0; cairo_list_foreach (operand, &ctx->operands) { if (operand == &df->operand.link) break; depth++; } df->operand.type = depth; if (cairo_list_is_empty (&sorted)) { cairo_list_move (&df->link, &sorted); } else { struct deferred_finish *pos; cairo_list_foreach_entry (pos, struct deferred_finish, &sorted, link) { if (df->operand.type < pos->operand.type) break; } cairo_list_move_tail (&df->link, &pos->link); } } while (! cairo_list_is_empty (&sorted)) { struct deferred_finish *df; cairo_list_t *operand; int depth; df = cairo_list_first_entry (&sorted, struct deferred_finish, link); depth = 0; cairo_list_foreach (operand, &ctx->operands) { if (operand == &df->operand.link) break; depth++; } if (depth == 0) { _cairo_output_stream_printf (ctx->stream, "pop\n"); } else if (depth == 1) { _cairo_output_stream_printf (ctx->stream, "exch pop\n"); } else { _cairo_output_stream_printf (ctx->stream, "%d -1 roll pop\n", depth); } cairo_list_del (&df->operand.link); cairo_list_del (&df->link); free (df); } DONE: cairo_device_release (surface->base.device); } static cairo_int_status_t _cairo_script_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; status = active (surface); if (unlikely (status)) return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) goto BAIL; status = _emit_context (surface); if (unlikely (status)) goto BAIL; status = _emit_source (surface, op, source); if (unlikely (status)) goto BAIL; status = _emit_operator (surface, op); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (to_context (surface)->stream, "paint\n"); inactive (surface); if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_paint (&surface->wrapper, op, source, clip); } return CAIRO_STATUS_SUCCESS; BAIL: inactive (surface); return status; } static cairo_int_status_t _cairo_script_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; status = active (surface); if (unlikely (status)) return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) goto BAIL; status = _emit_context (surface); if (unlikely (status)) goto BAIL; status = _emit_source (surface, op, source); if (unlikely (status)) goto BAIL; status = _emit_operator (surface, op); if (unlikely (status)) goto BAIL; if (_cairo_pattern_equal (source, mask)) { _cairo_output_stream_puts (to_context (surface)->stream, "/source get"); } else { status = _emit_pattern (surface, mask); if (unlikely (status)) goto BAIL; } assert (surface->cr.current_operator == op); _cairo_output_stream_puts (to_context (surface)->stream, " mask\n"); inactive (surface); if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_mask (&surface->wrapper, op, source, mask, clip); } return CAIRO_STATUS_SUCCESS; BAIL: inactive (surface); return status; } static cairo_int_status_t _cairo_script_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_bool_t matrix_updated = FALSE; cairo_status_t status; status = active (surface); if (unlikely (status)) return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) goto BAIL; status = _emit_context (surface); if (unlikely (status)) goto BAIL; status = _emit_identity (surface, &matrix_updated); if (unlikely (status)) goto BAIL; status = _emit_path (surface, path, FALSE); if (unlikely (status)) goto BAIL; status = _emit_source (surface, op, source); if (unlikely (status)) goto BAIL; status = _emit_scaling_matrix (surface, ctm, &matrix_updated); if (unlikely (status)) goto BAIL; status = _emit_operator (surface, op); if (unlikely (status)) goto BAIL; if (_scaling_matrix_equal (&surface->cr.current_ctm, &surface->cr.current_stroke_matrix)) { matrix_updated = FALSE; } else { matrix_updated = TRUE; surface->cr.current_stroke_matrix = surface->cr.current_ctm; } status = _emit_stroke_style (surface, style, matrix_updated); if (unlikely (status)) goto BAIL; status = _emit_tolerance (surface, tolerance, matrix_updated); if (unlikely (status)) goto BAIL; status = _emit_antialias (surface, antialias); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n"); inactive (surface); if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_stroke (&surface->wrapper, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } return CAIRO_STATUS_SUCCESS; BAIL: inactive (surface); return status; } static cairo_int_status_t _cairo_script_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_bool_t matrix_updated = FALSE; cairo_status_t status; cairo_box_t box; status = active (surface); if (unlikely (status)) return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) goto BAIL; status = _emit_context (surface); if (unlikely (status)) goto BAIL; status = _emit_identity (surface, &matrix_updated); if (unlikely (status)) goto BAIL; status = _emit_source (surface, op, source); if (unlikely (status)) goto BAIL; if (! _cairo_path_fixed_is_box (path, &box)) { status = _emit_fill_rule (surface, fill_rule); if (unlikely (status)) goto BAIL; } if (path->has_curve_to) { status = _emit_tolerance (surface, tolerance, matrix_updated); if (unlikely (status)) goto BAIL; } if (! _cairo_path_fixed_fill_maybe_region (path)) { status = _emit_antialias (surface, antialias); if (unlikely (status)) goto BAIL; } status = _emit_path (surface, path, TRUE); if (unlikely (status)) goto BAIL; status = _emit_operator (surface, op); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n"); inactive (surface); if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_fill (&surface->wrapper, op, source, path, fill_rule, tolerance, antialias, clip); } return CAIRO_STATUS_SUCCESS; BAIL: inactive (surface); return status; } static cairo_surface_t * _cairo_script_surface_snapshot (void *abstract_surface) { cairo_script_surface_t *surface = abstract_surface; if (_cairo_surface_wrapper_is_active (&surface->wrapper)) return _cairo_surface_wrapper_snapshot (&surface->wrapper); return NULL; } static cairo_bool_t _cairo_script_surface_has_show_text_glyphs (void *abstract_surface) { return TRUE; } static const char * _subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) { static const char *names[] = { "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ }; return names[subpixel_order]; } static const char * _hint_style_to_string (cairo_hint_style_t hint_style) { static const char *names[] = { "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ }; return names[hint_style]; } static const char * _hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) { static const char *names[] = { "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ }; return names[hint_metrics]; } static cairo_status_t _emit_font_options (cairo_script_surface_t *surface, cairo_font_options_t *font_options) { cairo_script_context_t *ctx = to_context (surface); if (cairo_font_options_equal (&surface->cr.current_font_options, font_options)) { return CAIRO_STATUS_SUCCESS; } _cairo_output_stream_printf (ctx->stream, "<<"); if (font_options->antialias != surface->cr.current_font_options.antialias) { _cairo_output_stream_printf (ctx->stream, " /antialias //%s", _antialias_to_string (font_options->antialias)); } if (font_options->subpixel_order != surface->cr.current_font_options.subpixel_order) { _cairo_output_stream_printf (ctx->stream, " /subpixel-order //%s", _subpixel_order_to_string (font_options->subpixel_order)); } if (font_options->hint_style != surface->cr.current_font_options.hint_style) { _cairo_output_stream_printf (ctx->stream, " /hint-style //%s", _hint_style_to_string (font_options->hint_style)); } if (font_options->hint_metrics != surface->cr.current_font_options.hint_metrics) { _cairo_output_stream_printf (ctx->stream, " /hint-metrics //%s", _hint_metrics_to_string (font_options->hint_metrics)); } _cairo_output_stream_printf (ctx->stream, " >> set-font-options\n"); surface->cr.current_font_options = *font_options; return CAIRO_STATUS_SUCCESS; } static void _cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, cairo_scaled_font_t *scaled_font) { cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private; cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key; cairo_status_t status; status = cairo_device_acquire (&ctx->base); if (likely (status == CAIRO_STATUS_SUCCESS)) { _cairo_output_stream_printf (ctx->stream, "/f%lu undef /sf%lu undef\n", priv->id, priv->id); _bitmap_release_id (&ctx->font_id, priv->id); cairo_device_release (&ctx->base); } cairo_list_del (&priv->link); cairo_list_del (&priv->base.link); free (priv); } static cairo_script_font_t * _cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font) { return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx); } static long unsigned _cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font) { return _cairo_script_font_get (ctx, font)->id; } static cairo_status_t _emit_type42_font (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font) { cairo_script_context_t *ctx = to_context (surface); const cairo_scaled_font_backend_t *backend; cairo_output_stream_t *base85_stream; cairo_output_stream_t *zlib_stream; cairo_status_t status, status2; unsigned long size; unsigned int load_flags; uint32_t len; uint8_t *buf; backend = scaled_font->backend; if (backend->load_truetype_table == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); if (unlikely (status)) return status; buf = malloc (size); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); if (unlikely (status)) { free (buf); return status; } #if CAIRO_HAS_FT_FONT load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); #else load_flags = 0; #endif _cairo_output_stream_printf (ctx->stream, "<< " "/type 42 " "/index 0 " "/flags %d " "/source <|", load_flags); base85_stream = _cairo_base85_stream_create (ctx->stream); len = to_be32 (size); _cairo_output_stream_write (base85_stream, &len, sizeof (len)); zlib_stream = _cairo_deflate_stream_create (base85_stream); _cairo_output_stream_write (zlib_stream, buf, size); free (buf); status2 = _cairo_output_stream_destroy (zlib_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (base85_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; _cairo_output_stream_printf (ctx->stream, "~> >> font dup /f%lu exch def set-font-face", _cairo_script_font_id (ctx, scaled_font)); return status; } static cairo_status_t _emit_scaled_font_init (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_script_font_t **font_out) { cairo_script_context_t *ctx = to_context (surface); cairo_script_font_t *font_private; cairo_int_status_t status; font_private = malloc (sizeof (cairo_script_font_t)); if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx, _cairo_script_scaled_font_fini); font_private->parent = scaled_font; font_private->subset_glyph_index = 0; font_private->has_sfnt = TRUE; cairo_list_add (&font_private->link, &ctx->fonts); status = _bitmap_next_id (&ctx->font_id, &font_private->id); if (unlikely (status)) { free (font_private); return status; } status = _emit_context (surface); if (unlikely (status)) { free (font_private); return status; } status = _emit_type42_font (surface, scaled_font); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { *font_out = font_private; return status; } font_private->has_sfnt = FALSE; _cairo_output_stream_printf (ctx->stream, "dict\n" " /type 3 set\n" " /metrics [%f %f %f %f %f] set\n" " /glyphs array set\n" " font dup /f%lu exch def set-font-face", scaled_font->fs_extents.ascent, scaled_font->fs_extents.descent, scaled_font->fs_extents.height, scaled_font->fs_extents.max_x_advance, scaled_font->fs_extents.max_y_advance, font_private->id); *font_out = font_private; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_scaled_font (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font) { cairo_script_context_t *ctx = to_context (surface); cairo_matrix_t matrix; cairo_font_options_t options; cairo_bool_t matrix_updated = FALSE; cairo_status_t status; cairo_script_font_t *font_private; cairo_scaled_font_get_ctm (scaled_font, &matrix); status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); if (unlikely (status)) return status; if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) return CAIRO_STATUS_SUCCESS; surface->cr.current_scaled_font = scaled_font; font_private = _cairo_script_font_get (ctx, scaled_font); if (font_private == NULL) { cairo_scaled_font_get_font_matrix (scaled_font, &matrix); status = _emit_font_matrix (surface, &matrix); if (unlikely (status)) return status; cairo_scaled_font_get_font_options (scaled_font, &options); status = _emit_font_options (surface, &options); if (unlikely (status)) return status; status = _emit_scaled_font_init (surface, scaled_font, &font_private); if (unlikely (status)) return status; assert (target_is_active (surface)); _cairo_output_stream_printf (ctx->stream, " /scaled-font get /sf%lu exch def\n", font_private->id); } else { _cairo_output_stream_printf (ctx->stream, "sf%lu set-scaled-font\n", font_private->id); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_scaled_glyph_vector (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_script_font_t *font_private, cairo_scaled_glyph_t *scaled_glyph) { cairo_script_context_t *ctx = to_context (surface); cairo_script_implicit_context_t old_cr; cairo_status_t status; unsigned long index; index = ++font_private->subset_glyph_index; scaled_glyph->dev_private_key = ctx; scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" " /metrics [%f %f %f %f %f %f]\n" " /render {\n", index, scaled_glyph->fs_metrics.x_bearing, scaled_glyph->fs_metrics.y_bearing, scaled_glyph->fs_metrics.width, scaled_glyph->fs_metrics.height, scaled_glyph->fs_metrics.x_advance, scaled_glyph->fs_metrics.y_advance); if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f %f %f] transform\n", scaled_font->scale_inverse.xx, scaled_font->scale_inverse.yx, scaled_font->scale_inverse.xy, scaled_font->scale_inverse.yy, scaled_font->scale_inverse.x0, scaled_font->scale_inverse.y0); } old_cr = surface->cr; _cairo_script_implicit_context_init (&surface->cr); status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, &surface->base); surface->cr = old_cr; _cairo_output_stream_puts (ctx->stream, "} >> set\n"); return status; } static cairo_status_t _emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_script_font_t *font_private, cairo_scaled_glyph_t *scaled_glyph) { cairo_script_context_t *ctx = to_context (surface); cairo_status_t status; unsigned long index; index = ++font_private->subset_glyph_index; scaled_glyph->dev_private_key = ctx; scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" " /metrics [%f %f %f %f %f %f]\n" " /render {\n" "%f %f translate\n", index, scaled_glyph->fs_metrics.x_bearing, scaled_glyph->fs_metrics.y_bearing, scaled_glyph->fs_metrics.width, scaled_glyph->fs_metrics.height, scaled_glyph->fs_metrics.x_advance, scaled_glyph->fs_metrics.y_advance, scaled_glyph->fs_metrics.x_bearing, scaled_glyph->fs_metrics.y_bearing); status = _emit_image_surface (surface, scaled_glyph->surface); if (unlikely (status)) return status; _cairo_output_stream_puts (ctx->stream, "pattern "); if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { _cairo_output_stream_printf (ctx->stream, "\n [%f %f %f %f %f %f] set-matrix\n", scaled_font->font_matrix.xx, scaled_font->font_matrix.yx, scaled_font->font_matrix.xy, scaled_font->font_matrix.yy, scaled_font->font_matrix.x0, scaled_font->font_matrix.y0); } _cairo_output_stream_puts (ctx->stream, "mask\n} >> set\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_scaled_glyph_prologue (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font) { cairo_script_context_t *ctx = to_context (surface); _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n", _cairo_script_font_id (ctx, scaled_font)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _emit_scaled_glyphs (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, unsigned int num_glyphs) { cairo_script_context_t *ctx = to_context (surface); cairo_script_font_t *font_private; cairo_status_t status; unsigned int n; cairo_bool_t have_glyph_prologue = FALSE; if (num_glyphs == 0) return CAIRO_STATUS_SUCCESS; font_private = _cairo_script_font_get (ctx, scaled_font); if (font_private->has_sfnt) return CAIRO_STATUS_SUCCESS; _cairo_scaled_font_freeze_cache (scaled_font); for (n = 0; n < num_glyphs; n++) { cairo_scaled_glyph_t *scaled_glyph; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) break; if (scaled_glyph->dev_private_key == ctx) continue; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); if (_cairo_status_is_error (status)) break; if (status == CAIRO_STATUS_SUCCESS) { if (! have_glyph_prologue) { status = _emit_scaled_glyph_prologue (surface, scaled_font); if (unlikely (status)) break; have_glyph_prologue = TRUE; } status = _emit_scaled_glyph_vector (surface, scaled_font, font_private, scaled_glyph); if (unlikely (status)) break; continue; } status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (_cairo_status_is_error (status)) break; if (status == CAIRO_STATUS_SUCCESS) { if (! have_glyph_prologue) { status = _emit_scaled_glyph_prologue (surface, scaled_font); if (unlikely (status)) break; have_glyph_prologue = TRUE; } status = _emit_scaled_glyph_bitmap (surface, scaled_font, font_private, scaled_glyph); if (unlikely (status)) break; continue; } } _cairo_scaled_font_thaw_cache (scaled_font); if (have_glyph_prologue) { _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n"); } return status; } static void to_octal (int value, char *buf, size_t size) { do { buf[--size] = '0' + (value & 7); value >>= 3; } while (size); } static void _emit_string_literal (cairo_script_surface_t *surface, const char *utf8, int len) { cairo_script_context_t *ctx = to_context (surface); char c; const char *end; _cairo_output_stream_puts (ctx->stream, "("); if (utf8 == NULL) { end = utf8; } else { if (len < 0) len = strlen (utf8); end = utf8 + len; } while (utf8 < end) { switch ((c = *utf8++)) { case '\n': c = 'n'; goto ESCAPED_CHAR; case '\r': c = 'r'; goto ESCAPED_CHAR; case '\t': c = 't'; goto ESCAPED_CHAR; case '\b': c = 'b'; goto ESCAPED_CHAR; case '\f': c = 'f'; goto ESCAPED_CHAR; case '\\': case '(': case ')': ESCAPED_CHAR: _cairo_output_stream_printf (ctx->stream, "\\%c", c); break; default: if (isprint (c) || isspace (c)) { _cairo_output_stream_printf (ctx->stream, "%c", c); } else { char buf[4] = { '\\' }; to_octal (c, buf+1, 3); _cairo_output_stream_write (ctx->stream, buf, 4); } break; } } _cairo_output_stream_puts (ctx->stream, ")"); } static cairo_int_status_t _cairo_script_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t backward, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_script_context_t *ctx = to_context (surface); cairo_script_font_t *font_private; cairo_scaled_glyph_t *scaled_glyph; cairo_matrix_t matrix; cairo_status_t status; double x, y, ix, iy; int n; cairo_output_stream_t *base85_stream = NULL; status = active (surface); if (unlikely (status)) return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) goto BAIL; status = _emit_context (surface); if (unlikely (status)) goto BAIL; status = _emit_source (surface, op, source); if (unlikely (status)) goto BAIL; status = _emit_scaled_font (surface, scaled_font); if (unlikely (status)) goto BAIL; status = _emit_operator (surface, op); if (unlikely (status)) goto BAIL; status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); if (unlikely (status)) goto BAIL; /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ /* [cx cy [glyphs]] show_glyphs */ if (utf8 != NULL && clusters != NULL) { _emit_string_literal (surface, utf8, utf8_len); _cairo_output_stream_puts (ctx->stream, " "); } matrix = surface->cr.current_ctm; status = cairo_matrix_invert (&matrix); assert (status == CAIRO_STATUS_SUCCESS); ix = x = glyphs[0].x; iy = y = glyphs[0].y; cairo_matrix_transform_point (&matrix, &ix, &iy); ix -= scaled_font->font_matrix.x0; iy -= scaled_font->font_matrix.y0; _cairo_scaled_font_freeze_cache (scaled_font); font_private = _cairo_script_font_get (ctx, scaled_font); _cairo_output_stream_printf (ctx->stream, "[%f %f ", ix, iy); for (n = 0; n < num_glyphs; n++) { if (font_private->has_sfnt) { if (glyphs[n].index > 256) break; } else { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) { _cairo_scaled_font_thaw_cache (scaled_font); goto BAIL; } if ((long unsigned) scaled_glyph->dev_private > 256) break; } } if (n == num_glyphs) { _cairo_output_stream_puts (ctx->stream, "<~"); base85_stream = _cairo_base85_stream_create (ctx->stream); } else _cairo_output_stream_puts (ctx->stream, "["); for (n = 0; n < num_glyphs; n++) { double dx, dy; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) { _cairo_scaled_font_thaw_cache (scaled_font); goto BAIL; } if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { if (fabs (glyphs[n].y - y) < 1e-5) { if (base85_stream != NULL) { status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) { base85_stream = NULL; break; } _cairo_output_stream_printf (ctx->stream, "~> %f <~", glyphs[n].x - x); base85_stream = _cairo_base85_stream_create (ctx->stream); } else { _cairo_output_stream_printf (ctx->stream, " ] %f [ ", glyphs[n].x - x); } x = glyphs[n].x; } else { ix = x = glyphs[n].x; iy = y = glyphs[n].y; cairo_matrix_transform_point (&matrix, &ix, &iy); ix -= scaled_font->font_matrix.x0; iy -= scaled_font->font_matrix.y0; if (base85_stream != NULL) { status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) { base85_stream = NULL; break; } _cairo_output_stream_printf (ctx->stream, "~> %f %f <~", ix, iy); base85_stream = _cairo_base85_stream_create (ctx->stream); } else { _cairo_output_stream_printf (ctx->stream, " ] %f %f [ ", ix, iy); } } } if (base85_stream != NULL) { uint8_t c; if (font_private->has_sfnt) c = glyphs[n].index; else c = (uint8_t) (long unsigned) scaled_glyph->dev_private; _cairo_output_stream_write (base85_stream, &c, 1); } else { if (font_private->has_sfnt) _cairo_output_stream_printf (ctx->stream, " %lu", glyphs[n].index); else _cairo_output_stream_printf (ctx->stream, " %lu", (long unsigned) scaled_glyph->dev_private); } dx = scaled_glyph->metrics.x_advance; dy = scaled_glyph->metrics.y_advance; cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); x += dx; y += dy; } _cairo_scaled_font_thaw_cache (scaled_font); if (base85_stream != NULL) { cairo_status_t status2; status2 = _cairo_output_stream_destroy (base85_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; _cairo_output_stream_printf (ctx->stream, "~>"); } else { _cairo_output_stream_puts (ctx->stream, " ]"); } if (unlikely (status)) return status; if (utf8 != NULL && clusters != NULL) { for (n = 0; n < num_clusters; n++) { if (clusters[n].num_bytes > UCHAR_MAX || clusters[n].num_glyphs > UCHAR_MAX) { break; } } if (n < num_clusters) { _cairo_output_stream_puts (ctx->stream, "] [ "); for (n = 0; n < num_clusters; n++) { _cairo_output_stream_printf (ctx->stream, "%d %d ", clusters[n].num_bytes, clusters[n].num_glyphs); } _cairo_output_stream_puts (ctx->stream, "]"); } else { _cairo_output_stream_puts (ctx->stream, "] <~"); base85_stream = _cairo_base85_stream_create (ctx->stream); for (n = 0; n < num_clusters; n++) { uint8_t c[2]; c[0] = clusters[n].num_bytes; c[1] = clusters[n].num_glyphs; _cairo_output_stream_write (base85_stream, c, 2); } status = _cairo_output_stream_destroy (base85_stream); if (unlikely (status)) goto BAIL; _cairo_output_stream_puts (ctx->stream, "~>"); } _cairo_output_stream_printf (ctx->stream, " //%s show-text-glyphs\n", _direction_to_string (backward)); } else { _cairo_output_stream_puts (ctx->stream, "] show-glyphs\n"); } inactive (surface); if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, backward, scaled_font, clip); } return CAIRO_STATUS_SUCCESS; BAIL: inactive (surface); return status; } static cairo_bool_t _cairo_script_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_script_surface_t *surface = abstract_surface; if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { return _cairo_surface_wrapper_get_extents (&surface->wrapper, rectangle); } if (surface->width < 0 || surface->height < 0) return FALSE; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static const cairo_surface_backend_t _cairo_script_surface_backend = { CAIRO_SURFACE_TYPE_SCRIPT, _cairo_script_surface_finish, _cairo_default_context_create, _cairo_script_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_script_surface_source, _cairo_script_surface_acquire_source_image, _cairo_script_surface_release_source_image, _cairo_script_surface_snapshot, _cairo_script_surface_copy_page, _cairo_script_surface_show_page, _cairo_script_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_script_surface_paint, _cairo_script_surface_mask, _cairo_script_surface_stroke, _cairo_script_surface_fill, NULL, /* fill/stroke */ NULL, /* glyphs */ _cairo_script_surface_has_show_text_glyphs, _cairo_script_surface_show_text_glyphs }; static void _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) { cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; _cairo_stroke_style_init (&cr->current_style); _cairo_pattern_init_solid (&cr->current_source.solid, CAIRO_COLOR_BLACK); _cairo_path_fixed_init (&cr->current_path); cairo_matrix_init_identity (&cr->current_ctm); cairo_matrix_init_identity (&cr->current_stroke_matrix); cairo_matrix_init_identity (&cr->current_font_matrix); _cairo_font_options_init_default (&cr->current_font_options); cr->current_scaled_font = NULL; cr->has_clip = FALSE; } static void _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) { free (cr->current_style.dash); cr->current_style.dash = NULL; _cairo_pattern_fini (&cr->current_source.base); _cairo_path_fixed_fini (&cr->current_path); _cairo_script_implicit_context_init (cr); } static cairo_script_surface_t * _cairo_script_surface_create_internal (cairo_script_context_t *ctx, cairo_content_t content, cairo_rectangle_t *extents, cairo_surface_t *passthrough) { cairo_script_surface_t *surface; if (unlikely (ctx == NULL)) return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); surface = malloc (sizeof (cairo_script_surface_t)); if (unlikely (surface == NULL)) return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_script_surface_backend, &ctx->base, content); _cairo_surface_wrapper_init (&surface->wrapper, passthrough); _cairo_surface_clipper_init (&surface->clipper, _cairo_script_surface_clipper_intersect_clip_path); surface->width = surface->height = -1; if (extents) { surface->width = extents->width; surface->height = extents->height; cairo_surface_set_device_offset (&surface->base, -extents->x, -extents->y); } surface->emitted = FALSE; surface->defined = FALSE; surface->active = FALSE; surface->operand.type = SURFACE; cairo_list_init (&surface->operand.link); _cairo_script_implicit_context_init (&surface->cr); return surface; } static const cairo_device_backend_t _cairo_script_device_backend = { CAIRO_DEVICE_TYPE_SCRIPT, NULL, NULL, /* lock, unlock */ _device_flush, /* flush */ NULL, /* finish */ _device_destroy }; cairo_device_t * _cairo_script_context_create_internal (cairo_output_stream_t *stream) { cairo_script_context_t *ctx; ctx = malloc (sizeof (cairo_script_context_t)); if (unlikely (ctx == NULL)) return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); memset (ctx, 0, sizeof (cairo_script_context_t)); _cairo_device_init (&ctx->base, &_cairo_script_device_backend); cairo_list_init (&ctx->operands); cairo_list_init (&ctx->deferred); ctx->stream = stream; ctx->mode = CAIRO_SCRIPT_MODE_ASCII; cairo_list_init (&ctx->fonts); cairo_list_init (&ctx->defines); ctx->attach_snapshots = TRUE; return &ctx->base; } void _cairo_script_context_attach_snapshots (cairo_device_t *device, cairo_bool_t enable) { cairo_script_context_t *ctx; ctx = (cairo_script_context_t *) device; ctx->attach_snapshots = enable; } static cairo_device_t * _cairo_script_context_create (cairo_output_stream_t *stream) { cairo_script_context_t *ctx; ctx = (cairo_script_context_t *) _cairo_script_context_create_internal (stream); if (unlikely (ctx->base.status)) return &ctx->base; ctx->owns_stream = TRUE; _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); return &ctx->base; } /** * cairo_script_create: * @filename: the name (path) of the file to write the script to * * Creates a output device for emitting the script, used when * creating the individual surfaces. * * Return value: a pointer to the newly created device. The caller * owns the surface and should call cairo_device_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" device if an error such as out of memory * occurs. You can use cairo_device_status() to check for this. * * Since: 1.12 **/ cairo_device_t * cairo_script_create (const char *filename) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create_for_filename (filename); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_script_context_create (stream); } /** * cairo_script_create_for_stream: * @write_func: callback function passed the bytes written to the script * @closure: user data to be passed to the callback * * Creates a output device for emitting the script, used when * creating the individual surfaces. * * Return value: a pointer to the newly created device. The caller * owns the surface and should call cairo_device_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" device if an error such as out of memory * occurs. You can use cairo_device_status() to check for this. * * Since: 1.12 **/ cairo_device_t * cairo_script_create_for_stream (cairo_write_func_t write_func, void *closure) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create (write_func, NULL, closure); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_script_context_create (stream); } /** * cairo_script_write_comment: * @script: the script (output device) * @comment: the string to emit * @len:the length of the sting to write, or -1 to use strlen() * * Emit a string verbatim into the script. * * Since: 1.12 **/ void cairo_script_write_comment (cairo_device_t *script, const char *comment, int len) { cairo_script_context_t *context = (cairo_script_context_t *) script; if (len < 0) len = strlen (comment); _cairo_output_stream_puts (context->stream, "% "); _cairo_output_stream_write (context->stream, comment, len); _cairo_output_stream_puts (context->stream, "\n"); } /** * cairo_script_set_mode: * @script: The script (output device) * @mode: the new mode * * Change the output mode of the script * * Since: 1.12 **/ void cairo_script_set_mode (cairo_device_t *script, cairo_script_mode_t mode) { cairo_script_context_t *context = (cairo_script_context_t *) script; context->mode = mode; } /** * cairo_script_get_mode: * @script: The script (output device) to query * * Queries the script for its current output mode. * * Return value: the current output mode of the script * * Since: 1.12 **/ cairo_script_mode_t cairo_script_get_mode (cairo_device_t *script) { cairo_script_context_t *context = (cairo_script_context_t *) script; return context->mode; } /** * cairo_script_surface_create: * @script: the script (output device) * @content: the content of the surface * @width: width in pixels * @height: height in pixels * * Create a new surface that will emit its rendering through @script * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_script_surface_create (cairo_device_t *script, cairo_content_t content, double width, double height) { cairo_rectangle_t *extents, r; if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (script->status)) return _cairo_surface_create_in_error (script->status); extents = NULL; if (width > 0 && height > 0) { r.x = r.y = 0; r.width = width; r.height = height; extents = &r; } return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, content, extents, NULL)->base; } slim_hidden_def (cairo_script_surface_create); /** * cairo_script_surface_create_for_target: * @script: the script (output device) * @target: a target surface to wrap * * Create a pxoy surface that will render to @target and record * the operations to @device. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_script_surface_create_for_target (cairo_device_t *script, cairo_surface_t *target) { cairo_rectangle_int_t extents; cairo_rectangle_t rect, *r; if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (script->status)) return _cairo_surface_create_in_error (script->status); if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); r = NULL; if (_cairo_surface_get_extents (target, &extents)) { rect.x = rect.y = 0; rect.width = extents.width; rect.height = extents.height; r= ▭ } return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, target->content, r, target)->base; } /** * cairo_script_from_recording_surface: * @script: the script (output device) * @recording_surface: the recording surface to replay * * Converts the record operations in @recording_surface into a script. * * Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code. * * Since: 1.12 **/ cairo_status_t cairo_script_from_recording_surface (cairo_device_t *script, cairo_surface_t *recording_surface) { cairo_rectangle_t r, *extents; cairo_surface_t *surface; cairo_status_t status; if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (script->status)) return _cairo_error (script->status); if (unlikely (recording_surface->status)) return recording_surface->status; if (unlikely (! _cairo_surface_is_recording (recording_surface))) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); extents = NULL; if (_cairo_recording_surface_get_bounds (recording_surface, &r)) extents = &r; surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, recording_surface->content, extents, NULL)->base; if (unlikely (surface->status)) return surface->status; status = _cairo_recording_surface_replay (recording_surface, surface); cairo_surface_destroy (surface); return status; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-script.h����������������������������������0000664�0000000�0000000�00000006000�12710376503�0025044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2008 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SCRIPT_H #define CAIRO_SCRIPT_H #include "cairo.h" #if CAIRO_HAS_SCRIPT_SURFACE CAIRO_BEGIN_DECLS /** * cairo_script_mode_t: * @CAIRO_SCRIPT_MODE_ASCII: the output will be in readable text (default). (Since 1.12) * @CAIRO_SCRIPT_MODE_BINARY: the output will use byte codes. (Since 1.12) * * A set of script output variants. * * Since: 1.12 **/ typedef enum { CAIRO_SCRIPT_MODE_ASCII, CAIRO_SCRIPT_MODE_BINARY } cairo_script_mode_t; cairo_public cairo_device_t * cairo_script_create (const char *filename); cairo_public cairo_device_t * cairo_script_create_for_stream (cairo_write_func_t write_func, void *closure); cairo_public void cairo_script_write_comment (cairo_device_t *script, const char *comment, int len); cairo_public void cairo_script_set_mode (cairo_device_t *script, cairo_script_mode_t mode); cairo_public cairo_script_mode_t cairo_script_get_mode (cairo_device_t *script); cairo_public cairo_surface_t * cairo_script_surface_create (cairo_device_t *script, cairo_content_t content, double width, double height); cairo_public cairo_surface_t * cairo_script_surface_create_for_target (cairo_device_t *script, cairo_surface_t *target); cairo_public cairo_status_t cairo_script_from_recording_surface (cairo_device_t *script, cairo_surface_t *recording_surface); CAIRO_END_DECLS #else /*CAIRO_HAS_SCRIPT_SURFACE*/ # error Cairo was not compiled with support for the CairoScript backend #endif /*CAIRO_HAS_SCRIPT_SURFACE*/ #endif /*CAIRO_SCRIPT_H*/ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-shape-mask-compositor.c�������������������0000664�0000000�0000000�00000023627�12710376503�0027776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" #include "cairo-clip-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-private.h" #include "cairo-surface-offset-private.h" static cairo_int_status_t _cairo_shape_mask_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status; cairo_clip_t *clip; if (! extents->is_bounded) return CAIRO_INT_STATUS_UNSUPPORTED; TRACE ((stderr, "%s\n", __FUNCTION__)); mask = _cairo_surface_create_similar_scratch (extents->surface, CAIRO_CONTENT_ALPHA, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; clip = extents->clip; if (! _cairo_clip_is_region (clip)) clip = _cairo_clip_copy_region (clip); if (! mask->is_clear) { status = _cairo_surface_offset_paint (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, clip); if (unlikely (status)) goto error; } status = _cairo_surface_offset_stroke (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (unlikely (status)) goto error; if (clip != extents->clip) { status = _cairo_clip_combine_with_surface (extents->clip, mask, extents->bounded.x, extents->bounded.y); if (unlikely (status)) goto error; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; if (extents->op == CAIRO_OPERATOR_SOURCE) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_DEST_OUT, &_cairo_pattern_white.base, &pattern.base, clip); if ((status == CAIRO_INT_STATUS_SUCCESS)) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, &pattern.base, clip); } } else { status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, clip); } _cairo_pattern_fini (&pattern.base); error: cairo_surface_destroy (mask); if (clip != extents->clip) _cairo_clip_destroy (clip); return status; } static cairo_int_status_t _cairo_shape_mask_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status; cairo_clip_t *clip; TRACE ((stderr, "%s\n", __FUNCTION__)); if (! extents->is_bounded) return CAIRO_INT_STATUS_UNSUPPORTED; mask = _cairo_surface_create_similar_scratch (extents->surface, CAIRO_CONTENT_ALPHA, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; clip = extents->clip; if (! _cairo_clip_is_region (clip)) clip = _cairo_clip_copy_region (clip); if (! mask->is_clear) { status = _cairo_surface_offset_paint (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, clip); if (unlikely (status)) goto error; } status = _cairo_surface_offset_fill (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, fill_rule, tolerance, antialias, clip); if (unlikely (status)) goto error; if (clip != extents->clip) { status = _cairo_clip_combine_with_surface (extents->clip, mask, extents->bounded.x, extents->bounded.y); if (unlikely (status)) goto error; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; if (extents->op == CAIRO_OPERATOR_SOURCE) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_DEST_OUT, &_cairo_pattern_white.base, &pattern.base, clip); if ((status == CAIRO_INT_STATUS_SUCCESS)) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, &pattern.base, clip); } } else { status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, clip); } _cairo_pattern_fini (&pattern.base); error: if (clip != extents->clip) _cairo_clip_destroy (clip); cairo_surface_destroy (mask); return status; } static cairo_int_status_t _cairo_shape_mask_compositor_glyphs (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_surface_t *mask; cairo_surface_pattern_t pattern; cairo_int_status_t status; cairo_clip_t *clip; if (! extents->is_bounded) return CAIRO_INT_STATUS_UNSUPPORTED; TRACE ((stderr, "%s\n", __FUNCTION__)); mask = _cairo_surface_create_similar_scratch (extents->surface, CAIRO_CONTENT_ALPHA, extents->bounded.width, extents->bounded.height); if (unlikely (mask->status)) return mask->status; clip = extents->clip; if (! _cairo_clip_is_region (clip)) clip = _cairo_clip_copy_region (clip); if (! mask->is_clear) { status = _cairo_surface_offset_paint (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, clip); if (unlikely (status)) goto error; } status = _cairo_surface_offset_glyphs (mask, extents->bounded.x, extents->bounded.y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, scaled_font, glyphs, num_glyphs, clip); if (unlikely (status)) goto error; if (clip != extents->clip) { status = _cairo_clip_combine_with_surface (extents->clip, mask, extents->bounded.x, extents->bounded.y); if (unlikely (status)) goto error; } _cairo_pattern_init_for_surface (&pattern, mask); cairo_matrix_init_translate (&pattern.base.matrix, -extents->bounded.x, -extents->bounded.y); pattern.base.filter = CAIRO_FILTER_NEAREST; pattern.base.extend = CAIRO_EXTEND_NONE; if (extents->op == CAIRO_OPERATOR_SOURCE) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_DEST_OUT, &_cairo_pattern_white.base, &pattern.base, clip); if ((status == CAIRO_INT_STATUS_SUCCESS)) { status = _cairo_surface_mask (extents->surface, CAIRO_OPERATOR_ADD, &extents->source_pattern.base, &pattern.base, clip); } } else { status = _cairo_surface_mask (extents->surface, extents->op, &extents->source_pattern.base, &pattern.base, clip); } _cairo_pattern_fini (&pattern.base); error: if (clip != extents->clip) _cairo_clip_destroy (clip); cairo_surface_destroy (mask); return status; } void _cairo_shape_mask_compositor_init (cairo_compositor_t *compositor, const cairo_compositor_t *delegate) { compositor->delegate = delegate; compositor->paint = NULL; compositor->mask = NULL; compositor->fill = _cairo_shape_mask_compositor_fill; compositor->stroke = _cairo_shape_mask_compositor_stroke; compositor->glyphs = _cairo_shape_mask_compositor_glyphs; } ���������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-skia.h������������������������������������0000664�0000000�0000000�00000004003�12710376503�0024470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SKIA_H #define CAIRO_SKIA_H #include "cairo.h" #if CAIRO_HAS_SKIA_SURFACE CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_skia_surface_create (cairo_format_t format, int width, int height); cairo_public cairo_surface_t * cairo_skia_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride); CAIRO_END_DECLS #else # error Cairo was not compiled with support for the Skia backend #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-slope-private.h���������������������������0000664�0000000�0000000�00000004701�12710376503�0026340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef _CAIRO_SLOPE_PRIVATE_H #define _CAIRO_SLOPE_PRIVATE_H #include "cairo-types-private.h" #include "cairo-fixed-private.h" static inline void _cairo_slope_init (cairo_slope_t *slope, const cairo_point_t *a, const cairo_point_t *b) { slope->dx = b->x - a->x; slope->dy = b->y - a->y; } static inline cairo_bool_t _cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b) { return _cairo_int64_eq (_cairo_int32x32_64_mul (a->dy, b->dx), _cairo_int32x32_64_mul (b->dy, a->dx)); } static inline cairo_bool_t _cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b) { return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx), _cairo_int32x32_64_mul (a->dy, b->dy))); } cairo_private int _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) cairo_pure; #endif /* _CAIRO_SLOPE_PRIVATE_H */ ���������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-slope.c�����������������������������������0000664�0000000�0000000�00000007052�12710376503�0024665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-slope-private.h" /* Compare two slopes. Slope angles begin at 0 in the direction of the positive X axis and increase in the direction of the positive Y axis. This function always compares the slope vectors based on the smaller angular difference between them, (that is based on an angular difference that is strictly less than pi). To break ties when comparing slope vectors with an angular difference of exactly pi, the vector with a positive dx (or positive dy if dx's are zero) is considered to be more positive than the other. Also, all slope vectors with both dx==0 and dy==0 are considered equal and more positive than any non-zero vector. < 0 => a less positive than b == 0 => a equal to b > 0 => a more positive than b */ int _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) { cairo_int64_t ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx); cairo_int64_t bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx); int cmp; cmp = _cairo_int64_cmp (ady_bdx, bdy_adx); if (cmp) return cmp; /* special-case zero vectors. the intended logic here is: * zero vectors all compare equal, and more positive than any * non-zero vector. */ if (a->dx == 0 && a->dy == 0 && b->dx == 0 && b->dy ==0) return 0; if (a->dx == 0 && a->dy == 0) return 1; if (b->dx == 0 && b->dy ==0) return -1; /* Finally, we're looking at two vectors that are either equal or * that differ by exactly pi. We can identify the "differ by pi" * case by looking for a change in sign in either dx or dy between * a and b. * * And in these cases, we eliminate the ambiguity by reducing the angle * of b by an infinitesimally small amount, (that is, 'a' will * always be considered less than 'b'). */ if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) { if (a->dx > 0 || (a->dx == 0 && a->dy > 0)) return -1; else return +1; } /* Finally, for identical slopes, we obviously return 0. */ return 0; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-spans-compositor-private.h����������������0000664�0000000�0000000�00000007342�12710376503�0030542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SPANS_COMPOSITOR_PRIVATE_H #define CAIRO_SPANS_COMPOSITOR_PRIVATE_H #include "cairo-compositor-private.h" #include "cairo-types-private.h" #include "cairo-spans-private.h" CAIRO_BEGIN_DECLS typedef struct _cairo_abstract_span_renderer { cairo_span_renderer_t base; char data[4096]; } cairo_abstract_span_renderer_t; struct cairo_spans_compositor { cairo_compositor_t base; unsigned int flags; #define CAIRO_SPANS_COMPOSITOR_HAS_LERP 0x1 /* pixel-aligned fast paths */ cairo_int_status_t (*fill_boxes) (void *surface, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes); cairo_int_status_t (*draw_image_boxes) (void *surface, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy); cairo_int_status_t (*copy_boxes) (void *surface, cairo_surface_t *src, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents, int dx, int dy); cairo_surface_t * (*pattern_to_surface) (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_int_status_t (*composite_boxes) (void *surface, cairo_operator_t op, cairo_surface_t *source, cairo_surface_t *mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents); /* general shape masks using a span renderer */ cairo_int_status_t (*renderer_init) (cairo_abstract_span_renderer_t *renderer, const cairo_composite_rectangles_t *extents, cairo_antialias_t antialias, cairo_bool_t needs_clip); void (*renderer_fini) (cairo_abstract_span_renderer_t *renderer, cairo_int_status_t status); }; cairo_private void _cairo_spans_compositor_init (cairo_spans_compositor_t *compositor, const cairo_compositor_t *delegate); CAIRO_END_DECLS #endif /* CAIRO_SPANS_COMPOSITOR_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-spans-compositor.c������������������������0000664�0000000�0000000�00000107045�12710376503�0027066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-image-surface-private.h" #include "cairo-paginated-private.h" #include "cairo-pattern-inline.h" #include "cairo-region-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-spans-compositor-private.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-observer-private.h" typedef struct { cairo_polygon_t *polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; } composite_spans_info_t; static cairo_int_status_t composite_polygon (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); static cairo_int_status_t composite_boxes (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes); static cairo_int_status_t clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); static cairo_surface_t * get_clip_surface (const cairo_spans_compositor_t *compositor, cairo_surface_t *dst, const cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_composite_rectangles_t composite; cairo_surface_t *surface; cairo_box_t box; cairo_polygon_t polygon; const cairo_clip_path_t *clip_path; cairo_antialias_t antialias; cairo_fill_rule_t fill_rule; cairo_int_status_t status; assert (clip->path); surface = _cairo_surface_create_similar_solid (dst, CAIRO_CONTENT_ALPHA, extents->width, extents->height, CAIRO_COLOR_TRANSPARENT); _cairo_box_from_rectangle (&box, extents); _cairo_polygon_init (&polygon, &box, 1); clip_path = clip->path; status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, &polygon); if (unlikely (status)) goto cleanup_polygon; polygon.num_limits = 0; antialias = clip_path->antialias; fill_rule = clip_path->fill_rule; if (clip->boxes) { cairo_polygon_t intersect; cairo_boxes_t tmp; _cairo_boxes_init_for_array (&tmp, clip->boxes, clip->num_boxes); status= _cairo_polygon_init_boxes (&intersect, &tmp); if (unlikely (status)) goto cleanup_polygon; status = _cairo_polygon_intersect (&polygon, fill_rule, &intersect, CAIRO_FILL_RULE_WINDING); _cairo_polygon_fini (&intersect); if (unlikely (status)) goto cleanup_polygon; fill_rule = CAIRO_FILL_RULE_WINDING; } polygon.limits = NULL; polygon.num_limits = 0; clip_path = clip_path->prev; while (clip_path) { if (clip_path->antialias == antialias) { cairo_polygon_t next; _cairo_polygon_init (&next, NULL, 0); status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, &next); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = _cairo_polygon_intersect (&polygon, fill_rule, &next, clip_path->fill_rule); _cairo_polygon_fini (&next); if (unlikely (status)) goto cleanup_polygon; fill_rule = CAIRO_FILL_RULE_WINDING; } clip_path = clip_path->prev; } _cairo_polygon_translate (&polygon, -extents->x, -extents->y); status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, &polygon, NULL); if (unlikely (status)) goto cleanup_polygon; status = composite_polygon (compositor, &composite, &polygon, fill_rule, antialias); _cairo_composite_rectangles_fini (&composite); _cairo_polygon_fini (&polygon); if (unlikely (status)) goto error; _cairo_polygon_init (&polygon, &box, 1); clip_path = clip->path; antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT; clip_path = clip_path->prev; while (clip_path) { if (clip_path->antialias == antialias) { if (polygon.num_edges == 0) { status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, &polygon); fill_rule = clip_path->fill_rule; polygon.limits = NULL; polygon.num_limits = 0; } else { cairo_polygon_t next; _cairo_polygon_init (&next, NULL, 0); status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, clip_path->tolerance, &next); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = _cairo_polygon_intersect (&polygon, fill_rule, &next, clip_path->fill_rule); _cairo_polygon_fini (&next); fill_rule = CAIRO_FILL_RULE_WINDING; } if (unlikely (status)) goto error; } clip_path = clip_path->prev; } if (polygon.num_edges) { _cairo_polygon_translate (&polygon, -extents->x, -extents->y); status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, CAIRO_OPERATOR_IN, &_cairo_pattern_white.base, &polygon, NULL); if (unlikely (status)) goto cleanup_polygon; status = composite_polygon (compositor, &composite, &polygon, fill_rule, antialias); _cairo_composite_rectangles_fini (&composite); _cairo_polygon_fini (&polygon); if (unlikely (status)) goto error; } return surface; cleanup_polygon: _cairo_polygon_fini (&polygon); error: cairo_surface_destroy (surface); return _cairo_int_surface_create_in_error (status); } static cairo_int_status_t fixup_unbounded_mask (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_composite_rectangles_t composite; cairo_surface_t *clip; cairo_int_status_t status; TRACE((stderr, "%s\n", __FUNCTION__)); clip = get_clip_surface (compositor, extents->surface, extents->clip, &extents->unbounded); if (unlikely (clip->status)) { if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; return clip->status; } status = _cairo_composite_rectangles_init_for_boxes (&composite, extents->surface, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, boxes, NULL); if (unlikely (status)) goto cleanup_clip; _cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip); composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST; composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE; status = composite_boxes (compositor, &composite, boxes); _cairo_pattern_fini (&composite.mask_pattern.base); _cairo_composite_rectangles_fini (&composite); cleanup_clip: cairo_surface_destroy (clip); return status; } static cairo_int_status_t fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_polygon_t polygon, intersect; cairo_composite_rectangles_t composite; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_int_status_t status; TRACE((stderr, "%s\n", __FUNCTION__)); /* Can we treat the clip as a regular clear-polygon and use it to fill? */ status = _cairo_clip_get_polygon (extents->clip, &polygon, &fill_rule, &antialias); if (status == CAIRO_INT_STATUS_UNSUPPORTED) return status; status= _cairo_polygon_init_boxes (&intersect, boxes); if (unlikely (status)) goto cleanup_polygon; status = _cairo_polygon_intersect (&polygon, fill_rule, &intersect, CAIRO_FILL_RULE_WINDING); _cairo_polygon_fini (&intersect); if (unlikely (status)) goto cleanup_polygon; status = _cairo_composite_rectangles_init_for_polygon (&composite, extents->surface, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, &polygon, NULL); if (unlikely (status)) goto cleanup_polygon; status = composite_polygon (compositor, &composite, &polygon, fill_rule, antialias); _cairo_composite_rectangles_fini (&composite); cleanup_polygon: _cairo_polygon_fini (&polygon); return status; } static cairo_int_status_t fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_boxes_t tmp, clear; cairo_box_t box; cairo_int_status_t status; assert (boxes->is_pixel_aligned); TRACE ((stderr, "%s\n", __FUNCTION__)); if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } /* subtract the drawn boxes from the unbounded area */ _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (boxes->num_boxes) { _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; if (unlikely (status)) goto error; } else { box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); } /* If we have a clip polygon, we need to intersect with that as well */ if (extents->clip->path) { status = fixup_unbounded_polygon (compositor, extents, &clear); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = fixup_unbounded_mask (compositor, extents, &clear); } else { /* Otherwise just intersect with the clip boxes */ if (extents->clip->num_boxes) { _cairo_boxes_init_for_array (&tmp, extents->clip->boxes, extents->clip->num_boxes); status = _cairo_boxes_intersect (&clear, &tmp, &clear); if (unlikely (status)) goto error; } if (clear.is_pixel_aligned) { status = compositor->fill_boxes (extents->surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); } else { cairo_composite_rectangles_t composite; status = _cairo_composite_rectangles_init_for_boxes (&composite, extents->surface, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, &clear, NULL); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = composite_boxes (compositor, &composite, &clear); _cairo_composite_rectangles_fini (&composite); } } } error: _cairo_boxes_fini (&clear); return status; } static cairo_surface_t * unwrap_source (const cairo_pattern_t *pattern) { cairo_rectangle_int_t limit; return _cairo_pattern_get_source ((cairo_surface_pattern_t *)pattern, &limit); } static cairo_bool_t is_recording_pattern (const cairo_pattern_t *pattern) { cairo_surface_t *surface; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return FALSE; surface = ((const cairo_surface_pattern_t *) pattern)->surface; return _cairo_surface_is_recording (surface); } static cairo_bool_t recording_pattern_contains_sample (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample) { cairo_recording_surface_t *surface; if (! is_recording_pattern (pattern)) return FALSE; if (pattern->extend == CAIRO_EXTEND_NONE) return TRUE; surface = (cairo_recording_surface_t *) unwrap_source (pattern); if (surface->unbounded) return TRUE; return _cairo_rectangle_contains_rectangle (&surface->extents, sample); } static cairo_bool_t op_reduces_to_source (const cairo_composite_rectangles_t *extents, cairo_bool_t no_mask) { if (extents->op == CAIRO_OPERATOR_SOURCE) return TRUE; if (extents->surface->is_clear) return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD; if (no_mask && extents->op == CAIRO_OPERATOR_OVER) return _cairo_pattern_is_opaque (&extents->source_pattern.base, &extents->source_sample_area); return FALSE; } static cairo_status_t upload_boxes (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; const cairo_surface_pattern_t *source = &extents->source_pattern.surface; cairo_surface_t *src; cairo_rectangle_int_t limit; cairo_int_status_t status; int tx, ty; TRACE ((stderr, "%s\n", __FUNCTION__)); src = _cairo_pattern_get_source(source, &limit); if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->base.matrix, &tx, &ty)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Check that the data is entirely within the image */ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) return CAIRO_INT_STATUS_UNSUPPORTED; if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) return CAIRO_INT_STATUS_UNSUPPORTED; tx += limit.x; ty += limit.y; if (src->type == CAIRO_SURFACE_TYPE_IMAGE) status = compositor->draw_image_boxes (dst, (cairo_image_surface_t *)src, boxes, tx, ty); else status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, tx, ty); return status; } static cairo_bool_t _clip_is_region (const cairo_clip_t *clip) { int i; if (clip->is_region) return TRUE; if (clip->path) return FALSE; for (i = 0; i < clip->num_boxes; i++) { const cairo_box_t *b = &clip->boxes[i]; if (!_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y)) return FALSE; } return TRUE; } static cairo_int_status_t composite_aligned_boxes (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; const cairo_pattern_t *source = &extents->source_pattern.base; cairo_int_status_t status; cairo_bool_t need_clip_mask = ! _clip_is_region (extents->clip); cairo_bool_t op_is_source; cairo_bool_t no_mask; cairo_bool_t inplace; TRACE ((stderr, "%s: need_clip_mask=%d, is-bounded=%d\n", __FUNCTION__, need_clip_mask, extents->is_bounded)); if (need_clip_mask && ! extents->is_bounded) { TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); return CAIRO_INT_STATUS_UNSUPPORTED; } no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && CAIRO_COLOR_IS_OPAQUE (&extents->mask_pattern.solid.color); op_is_source = op_reduces_to_source (extents, no_mask); inplace = ! need_clip_mask && op_is_source && no_mask; TRACE ((stderr, "%s: op-is-source=%d [op=%d], no-mask=%d, inplace=%d\n", __FUNCTION__, op_is_source, op, no_mask, inplace)); if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) { /* SOURCE with a mask is actually a LERP in cairo semantics */ if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0) { TRACE ((stderr, "%s: unsupported lerp\n", __FUNCTION__)); return CAIRO_INT_STATUS_UNSUPPORTED; } } /* Are we just copying a recording surface? */ if (inplace && recording_pattern_contains_sample (&extents->source_pattern.base, &extents->source_sample_area)) { cairo_clip_t *recording_clip; const cairo_pattern_t *source = &extents->source_pattern.base; /* XXX could also do tiling repeat modes... */ /* first clear the area about to be overwritten */ if (! dst->is_clear) { status = compositor->fill_boxes (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, boxes); if (unlikely (status)) return status; dst->is_clear = TRUE; } recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (unwrap_source (source), &source->matrix, dst, recording_clip); _cairo_clip_destroy (recording_clip); return status; } status = CAIRO_INT_STATUS_UNSUPPORTED; if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_color_t *color; color = &((cairo_solid_pattern_t *) source)->color; if (op_is_source) op = CAIRO_OPERATOR_SOURCE; status = compositor->fill_boxes (dst, op, color, boxes); } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) { status = upload_boxes (compositor, extents, boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_surface_t *src; cairo_surface_t *mask = NULL; int src_x, src_y; int mask_x = 0, mask_y = 0; /* All typical cases will have been resolved before now... */ if (need_clip_mask) { mask = get_clip_surface (compositor, dst, extents->clip, &extents->bounded); if (unlikely (mask->status)) return mask->status; mask_x = -extents->bounded.x; mask_y = -extents->bounded.y; } /* XXX but this is still ugly */ if (! no_mask) { src = compositor->pattern_to_surface (dst, &extents->mask_pattern.base, TRUE, &extents->bounded, &extents->mask_sample_area, &src_x, &src_y); if (unlikely (src->status)) { cairo_surface_destroy (mask); return src->status; } if (mask != NULL) { status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN, src, NULL, src_x, src_y, 0, 0, mask_x, mask_y, boxes, &extents->bounded); cairo_surface_destroy (src); } else { mask = src; mask_x = src_x; mask_y = src_y; } } src = compositor->pattern_to_surface (dst, source, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); if (likely (src->status == CAIRO_STATUS_SUCCESS)) { status = compositor->composite_boxes (dst, op, src, mask, src_x, src_y, mask_x, mask_y, 0, 0, boxes, &extents->bounded); cairo_surface_destroy (src); } else status = src->status; cairo_surface_destroy (mask); } if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded) status = fixup_unbounded_boxes (compositor, extents, boxes); return status; } static cairo_bool_t composite_needs_clip (const cairo_composite_rectangles_t *composite, const cairo_box_t *extents) { return !_cairo_clip_contains_box (composite->clip, extents); } static cairo_int_status_t composite_boxes (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_abstract_span_renderer_t renderer; cairo_rectangular_scan_converter_t converter; const struct _cairo_boxes_chunk *chunk; cairo_int_status_t status; cairo_box_t box; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_box_from_rectangle (&box, &extents->unbounded); if (composite_needs_clip (extents, &box)) { TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_rectangular_scan_converter_init (&converter, &extents->unbounded); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; int i; for (i = 0; i < chunk->count; i++) { status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); if (unlikely (status)) goto cleanup_converter; } } status = compositor->renderer_init (&renderer, extents, CAIRO_ANTIALIAS_DEFAULT, FALSE); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = converter.base.generate (&converter.base, &renderer.base); compositor->renderer_fini (&renderer, status); cleanup_converter: converter.base.destroy (&converter.base); return status; } static cairo_int_status_t composite_polygon (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_abstract_span_renderer_t renderer; cairo_scan_converter_t *converter; cairo_bool_t needs_clip; cairo_int_status_t status; if (extents->is_bounded) needs_clip = extents->clip->path != NULL; else needs_clip = !_clip_is_region (extents->clip) || extents->clip->num_boxes > 1; TRACE ((stderr, "%s - needs_clip=%d\n", __FUNCTION__, needs_clip)); if (needs_clip) { TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); return CAIRO_INT_STATUS_UNSUPPORTED; converter = _cairo_clip_tor_scan_converter_create (extents->clip, polygon, fill_rule, antialias); } else { const cairo_rectangle_int_t *r = &extents->unbounded; if (antialias == CAIRO_ANTIALIAS_FAST) { converter = _cairo_tor22_scan_converter_create (r->x, r->y, r->x + r->width, r->y + r->height, fill_rule, antialias); status = _cairo_tor22_scan_converter_add_polygon (converter, polygon); } else if (antialias == CAIRO_ANTIALIAS_NONE) { converter = _cairo_mono_scan_converter_create (r->x, r->y, r->x + r->width, r->y + r->height, fill_rule); status = _cairo_mono_scan_converter_add_polygon (converter, polygon); } else { converter = _cairo_tor_scan_converter_create (r->x, r->y, r->x + r->width, r->y + r->height, fill_rule, antialias); status = _cairo_tor_scan_converter_add_polygon (converter, polygon); } } if (unlikely (status)) goto cleanup_converter; status = compositor->renderer_init (&renderer, extents, antialias, needs_clip); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = converter->generate (converter, &renderer.base); compositor->renderer_fini (&renderer, status); cleanup_converter: converter->destroy (converter); return status; } static cairo_int_status_t trim_extents_to_boxes (cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_box_t box; _cairo_boxes_extents (boxes, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_int_status_t trim_extents_to_polygon (cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon) { return _cairo_composite_rectangles_intersect_mask_extents (extents, &polygon->extents); } static cairo_int_status_t clip_and_composite_boxes (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_int_status_t status; cairo_polygon_t polygon; TRACE ((stderr, "%s\n", __FUNCTION__)); status = trim_extents_to_boxes (extents, boxes); if (unlikely (status)) return status; if (boxes->num_boxes == 0) { if (extents->is_bounded) return CAIRO_STATUS_SUCCESS; return fixup_unbounded_boxes (compositor, extents, boxes); } /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ if (extents->clip->path != NULL && extents->is_bounded) { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_clip_t *clip; clip = _cairo_clip_copy (extents->clip); clip = _cairo_clip_intersect_boxes (clip, boxes); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); _cairo_clip_path_destroy (clip->path); clip->path = NULL; if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *saved_clip = extents->clip; extents->clip = clip; status = clip_and_composite_polygon (compositor, extents, &polygon, fill_rule, antialias); clip = extents->clip; extents->clip = saved_clip; _cairo_polygon_fini (&polygon); } _cairo_clip_destroy (clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } if (boxes->is_pixel_aligned) { status = composite_aligned_boxes (compositor, extents, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } status = composite_boxes (compositor, extents, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_polygon_init_boxes (&polygon, boxes); if (unlikely (status)) return status; status = composite_polygon (compositor, extents, &polygon, CAIRO_FILL_RULE_WINDING, CAIRO_ANTIALIAS_DEFAULT); _cairo_polygon_fini (&polygon); return status; } static cairo_int_status_t clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); /* XXX simply uses polygon limits.point extemities, tessellation? */ status = trim_extents_to_polygon (extents, polygon); if (unlikely (status)) return status; if (_cairo_polygon_is_empty (polygon)) { cairo_boxes_t boxes; if (extents->is_bounded) return CAIRO_STATUS_SUCCESS; _cairo_boxes_init (&boxes); extents->bounded.width = extents->bounded.height = 0; return fixup_unbounded_boxes (compositor, extents, &boxes); } if (extents->is_bounded && extents->clip->path) { cairo_polygon_t clipper; cairo_antialias_t clip_antialias; cairo_fill_rule_t clip_fill_rule; TRACE((stderr, "%s - combining shape with clip polygon\n", __FUNCTION__)); status = _cairo_clip_get_polygon (extents->clip, &clipper, &clip_fill_rule, &clip_antialias); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *old_clip; if (clip_antialias == antialias) { status = _cairo_polygon_intersect (polygon, fill_rule, &clipper, clip_fill_rule); _cairo_polygon_fini (&clipper); if (unlikely (status)) return status; old_clip = extents->clip; extents->clip = _cairo_clip_copy_region (extents->clip); _cairo_clip_destroy (old_clip); status = trim_extents_to_polygon (extents, polygon); if (unlikely (status)) return status; fill_rule = CAIRO_FILL_RULE_WINDING; } else { _cairo_polygon_fini (&clipper); } } } return composite_polygon (compositor, extents, polygon, fill_rule, antialias); } /* high-level compositor interface */ static cairo_int_status_t _cairo_spans_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; cairo_boxes_t boxes; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_clip_steal_boxes (extents->clip, &boxes); status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_clip_unsteal_boxes (extents->clip, &boxes); return status; } static cairo_int_status_t _cairo_spans_compositor_mask (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; cairo_int_status_t status; cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_clip_steal_boxes (extents->clip, &boxes); status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_clip_unsteal_boxes (extents->clip, &boxes); return status; } static cairo_int_status_t _cairo_spans_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); TRACE_ (_cairo_debug_print_path (stderr, path)); TRACE_ (_cairo_debug_print_clip (stderr, extents->clip)); status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) _cairo_boxes_limit (&boxes, extents->clip->boxes, extents->clip->num_boxes); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING; if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, &extents->mask)) { if (extents->clip->num_boxes == 1) { _cairo_polygon_init (&polygon, extents->clip->boxes, 1); } else { cairo_box_t limits; _cairo_box_from_rectangle (&limits, &extents->unbounded); _cairo_polygon_init (&polygon, &limits, 1); } } else { _cairo_polygon_init (&polygon, NULL, 0); } status = _cairo_path_fixed_stroke_to_polygon (path, style, ctm, ctm_inverse, tolerance, &polygon); TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); polygon.num_limits = 0; if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, extents->clip->boxes, extents->clip->num_boxes); } if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *saved_clip = extents->clip; if (extents->is_bounded) { extents->clip = _cairo_clip_copy_path (extents->clip); extents->clip = _cairo_clip_intersect_box(extents->clip, &polygon.extents); } status = clip_and_composite_polygon (compositor, extents, &polygon, fill_rule, antialias); if (extents->is_bounded) { _cairo_clip_destroy (extents->clip); extents->clip = saved_clip; } } _cairo_polygon_fini (&polygon); } return status; } static cairo_int_status_t _cairo_spans_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; cairo_int_status_t status; TRACE((stderr, "%s op=%d, antialias=%d\n", __FUNCTION__, extents->op, antialias)); status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; TRACE((stderr, "%s - rectilinear\n", __FUNCTION__)); _cairo_boxes_init (&boxes); if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) _cairo_boxes_limit (&boxes, extents->clip->boxes, extents->clip->num_boxes); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_polygon_t polygon; TRACE((stderr, "%s - polygon\n", __FUNCTION__)); if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, &extents->mask)) { TRACE((stderr, "%s - clipping to bounds\n", __FUNCTION__)); if (extents->clip->num_boxes == 1) { _cairo_polygon_init (&polygon, extents->clip->boxes, 1); } else { cairo_box_t limits; _cairo_box_from_rectangle (&limits, &extents->unbounded); _cairo_polygon_init (&polygon, &limits, 1); } } else { _cairo_polygon_init (&polygon, NULL, 0); } status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); polygon.num_limits = 0; if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { TRACE((stderr, "%s - polygon intersect with %d clip boxes\n", __FUNCTION__, extents->clip->num_boxes)); status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, extents->clip->boxes, extents->clip->num_boxes); } TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *saved_clip = extents->clip; if (extents->is_bounded) { TRACE((stderr, "%s - polygon discard clip boxes\n", __FUNCTION__)); extents->clip = _cairo_clip_copy_path (extents->clip); extents->clip = _cairo_clip_intersect_box(extents->clip, &polygon.extents); } status = clip_and_composite_polygon (compositor, extents, &polygon, fill_rule, antialias); if (extents->is_bounded) { _cairo_clip_destroy (extents->clip); extents->clip = saved_clip; } } _cairo_polygon_fini (&polygon); TRACE((stderr, "%s - polygon status=%d\n", __FUNCTION__, status)); } return status; } void _cairo_spans_compositor_init (cairo_spans_compositor_t *compositor, const cairo_compositor_t *delegate) { compositor->base.delegate = delegate; compositor->base.paint = _cairo_spans_compositor_paint; compositor->base.mask = _cairo_spans_compositor_mask; compositor->base.fill = _cairo_spans_compositor_fill; compositor->base.stroke = _cairo_spans_compositor_stroke; compositor->base.glyphs = NULL; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-spans-private.h���������������������������0000664�0000000�0000000�00000015733�12710376503�0026351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2008 M Joonas Pihlaja * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CAIRO_SPANS_PRIVATE_H #define CAIRO_SPANS_PRIVATE_H #include "cairo-types-private.h" #include "cairo-compiler-private.h" /* Number of bits of precision used for alpha. */ #define CAIRO_SPANS_UNIT_COVERAGE_BITS 8 #define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1) /* A structure representing an open-ended horizontal span of constant * pixel coverage. */ typedef struct _cairo_half_open_span { int32_t x; /* The inclusive x-coordinate of the start of the span. */ uint8_t coverage; /* The pixel coverage for the pixels to the right. */ uint8_t inverse; /* between regular mask and clip */ } cairo_half_open_span_t; /* Span renderer interface. Instances of renderers are provided by * surfaces if they want to composite spans instead of trapezoids. */ typedef struct _cairo_span_renderer cairo_span_renderer_t; struct _cairo_span_renderer { /* Private status variable. */ cairo_status_t status; /* Called to destroy the renderer. */ cairo_destroy_func_t destroy; /* Render the spans on row y of the destination by whatever compositing * method is required. */ cairo_warn cairo_status_t (*render_rows) (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *coverages, unsigned num_coverages); /* Called after all rows have been rendered to perform whatever * final rendering step is required. This function is called just * once before the renderer is destroyed. */ cairo_status_t (*finish) (void *abstract_renderer); }; /* Scan converter interface. */ typedef struct _cairo_scan_converter cairo_scan_converter_t; struct _cairo_scan_converter { /* Destroy this scan converter. */ cairo_destroy_func_t destroy; /* Generates coverage spans for rows for the added edges and calls * the renderer function for each row. After generating spans the * only valid thing to do with the converter is to destroy it. */ cairo_status_t (*generate) (void *abstract_converter, cairo_span_renderer_t *renderer); /* Private status. Read with _cairo_scan_converter_status(). */ cairo_status_t status; }; /* Scan converter constructors. */ cairo_private cairo_scan_converter_t * _cairo_tor_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); cairo_private cairo_status_t _cairo_tor_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon); cairo_private cairo_scan_converter_t * _cairo_tor22_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); cairo_private cairo_status_t _cairo_tor22_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon); cairo_private cairo_scan_converter_t * _cairo_mono_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_mono_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon); cairo_private cairo_scan_converter_t * _cairo_clip_tor_scan_converter_create (cairo_clip_t *clip, cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias); typedef struct _cairo_rectangular_scan_converter { cairo_scan_converter_t base; cairo_box_t extents; struct _cairo_rectangular_scan_converter_chunk { struct _cairo_rectangular_scan_converter_chunk *next; void *base; int count; int size; } chunks, *tail; char buf[CAIRO_STACK_BUFFER_SIZE]; int num_rectangles; } cairo_rectangular_scan_converter_t; cairo_private void _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, const cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, const cairo_box_t *box, int dir); typedef struct _cairo_botor_scan_converter { cairo_scan_converter_t base; cairo_box_t extents; cairo_fill_rule_t fill_rule; int xmin, xmax; struct _cairo_botor_scan_converter_chunk { struct _cairo_botor_scan_converter_chunk *next; void *base; int count; int size; } chunks, *tail; char buf[CAIRO_STACK_BUFFER_SIZE]; int num_edges; } cairo_botor_scan_converter_t; cairo_private void _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, const cairo_box_t *extents, cairo_fill_rule_t fill_rule); /* cairo-spans.c: */ cairo_private cairo_scan_converter_t * _cairo_scan_converter_create_in_error (cairo_status_t error); cairo_private cairo_status_t _cairo_scan_converter_status (void *abstract_converter); cairo_private cairo_status_t _cairo_scan_converter_set_error (void *abstract_converter, cairo_status_t error); cairo_private cairo_span_renderer_t * _cairo_span_renderer_create_in_error (cairo_status_t error); cairo_private cairo_status_t _cairo_span_renderer_status (void *abstract_renderer); /* Set the renderer into an error state. This sets all the method * pointers except ->destroy() of the renderer to no-op * implementations that just return the error status. */ cairo_private cairo_status_t _cairo_span_renderer_set_error (void *abstract_renderer, cairo_status_t error); cairo_private cairo_status_t _cairo_surface_composite_polygon (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, const cairo_composite_rectangles_t *rects, cairo_polygon_t *polygon, cairo_region_t *clip_region); #endif /* CAIRO_SPANS_PRIVATE_H */ �������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-spans.c�����������������������������������0000664�0000000�0000000�00000021023�12710376503�0024661�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2008 M Joonas Pihlaja * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-fixed-private.h" #include "cairo-types-private.h" static void _cairo_nil_destroy (void *abstract) { (void) abstract; } static cairo_status_t _cairo_nil_scan_converter_generate (void *abstract_converter, cairo_span_renderer_t *renderer) { (void) abstract_converter; (void) renderer; return _cairo_scan_converter_status (abstract_converter); } cairo_status_t _cairo_scan_converter_status (void *abstract_converter) { cairo_scan_converter_t *converter = abstract_converter; return converter->status; } cairo_status_t _cairo_scan_converter_set_error (void *abstract_converter, cairo_status_t error) { cairo_scan_converter_t *converter = abstract_converter; if (error == CAIRO_STATUS_SUCCESS) ASSERT_NOT_REACHED; if (converter->status == CAIRO_STATUS_SUCCESS) { converter->generate = _cairo_nil_scan_converter_generate; converter->status = error; } return converter->status; } static void _cairo_nil_scan_converter_init (cairo_scan_converter_t *converter, cairo_status_t status) { converter->destroy = _cairo_nil_destroy; converter->status = CAIRO_STATUS_SUCCESS; status = _cairo_scan_converter_set_error (converter, status); } cairo_scan_converter_t * _cairo_scan_converter_create_in_error (cairo_status_t status) { #define RETURN_NIL {\ static cairo_scan_converter_t nil;\ _cairo_nil_scan_converter_init (&nil, status);\ return &nil;\ } switch (status) { case CAIRO_STATUS_SUCCESS: case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; break; case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; case CAIRO_STATUS_READ_ERROR: RETURN_NIL; case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; default: break; } status = CAIRO_STATUS_NO_MEMORY; RETURN_NIL; #undef RETURN_NIL } static cairo_status_t _cairo_nil_span_renderer_render_rows ( void *abstract_renderer, int y, int height, const cairo_half_open_span_t *coverages, unsigned num_coverages) { (void) y; (void) height; (void) coverages; (void) num_coverages; return _cairo_span_renderer_status (abstract_renderer); } static cairo_status_t _cairo_nil_span_renderer_finish (void *abstract_renderer) { return _cairo_span_renderer_status (abstract_renderer); } cairo_status_t _cairo_span_renderer_status (void *abstract_renderer) { cairo_span_renderer_t *renderer = abstract_renderer; return renderer->status; } cairo_status_t _cairo_span_renderer_set_error ( void *abstract_renderer, cairo_status_t error) { cairo_span_renderer_t *renderer = abstract_renderer; if (error == CAIRO_STATUS_SUCCESS) { ASSERT_NOT_REACHED; } if (renderer->status == CAIRO_STATUS_SUCCESS) { renderer->render_rows = _cairo_nil_span_renderer_render_rows; renderer->finish = _cairo_nil_span_renderer_finish; renderer->status = error; } return renderer->status; } static void _cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer, cairo_status_t status) { renderer->destroy = _cairo_nil_destroy; renderer->status = CAIRO_STATUS_SUCCESS; status = _cairo_span_renderer_set_error (renderer, status); } cairo_span_renderer_t * _cairo_span_renderer_create_in_error (cairo_status_t status) { #define RETURN_NIL {\ static cairo_span_renderer_t nil;\ _cairo_nil_span_renderer_init (&nil, status);\ return &nil;\ } switch (status) { case CAIRO_STATUS_SUCCESS: case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; break; case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; case CAIRO_STATUS_READ_ERROR: RETURN_NIL; case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; default: break; } status = CAIRO_STATUS_NO_MEMORY; RETURN_NIL; #undef RETURN_NIL } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-spline.c����������������������������������0000664�0000000�0000000�00000027420�12710376503�0025036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-slope-private.h" cairo_bool_t _cairo_spline_intersects (const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d, const cairo_box_t *box) { cairo_box_t bounds; if (_cairo_box_contains_point (box, a) || _cairo_box_contains_point (box, b) || _cairo_box_contains_point (box, c) || _cairo_box_contains_point (box, d)) { return TRUE; } bounds.p2 = bounds.p1 = *a; _cairo_box_add_point (&bounds, b); _cairo_box_add_point (&bounds, c); _cairo_box_add_point (&bounds, d); if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) { return FALSE; } #if 0 /* worth refining? */ bounds.p2 = bounds.p1 = *a; _cairo_box_add_curve_to (&bounds, b, c, d); if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) { return FALSE; } #endif return TRUE; } cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { /* If both tangents are zero, this is just a straight line */ if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y) return FALSE; spline->add_point_func = add_point_func; spline->closure = closure; spline->knots.a = *a; spline->knots.b = *b; spline->knots.c = *c; spline->knots.d = *d; if (a->x != b->x || a->y != b->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); else if (a->x != c->x || a->y != c->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); else if (a->x != d->x || a->y != d->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); else return FALSE; if (c->x != d->x || c->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); else if (b->x != d->x || b->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); else return FALSE; /* just treat this as a straight-line from a -> d */ /* XXX if the initial, final and vector are all equal, this is just a line */ return TRUE; } static cairo_status_t _cairo_spline_add_point (cairo_spline_t *spline, const cairo_point_t *point, const cairo_point_t *knot) { cairo_point_t *prev; cairo_slope_t slope; prev = &spline->last_point; if (prev->x == point->x && prev->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&slope, point, knot); spline->last_point = *point; return spline->add_point_func (spline->closure, point, &slope); } static void _lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result) { result->x = a->x + ((b->x - a->x) >> 1); result->y = a->y + ((b->y - a->y) >> 1); } static void _de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2) { cairo_point_t ab, bc, cd; cairo_point_t abbc, bccd; cairo_point_t final; _lerp_half (&s1->a, &s1->b, &ab); _lerp_half (&s1->b, &s1->c, &bc); _lerp_half (&s1->c, &s1->d, &cd); _lerp_half (&ab, &bc, &abbc); _lerp_half (&bc, &cd, &bccd); _lerp_half (&abbc, &bccd, &final); s2->a = final; s2->b = bccd; s2->c = cd; s2->d = s1->d; s1->b = ab; s1->c = abbc; s1->d = final; } /* Return an upper bound on the error (squared) that could result from * approximating a spline as a line segment connecting the two endpoints. */ static double _cairo_spline_error_squared (const cairo_spline_knots_t *knots) { double bdx, bdy, berr; double cdx, cdy, cerr; /* We are going to compute the distance (squared) between each of the the b * and c control points and the segment a-b. The maximum of these two * distances will be our approximation error. */ bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x); cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { /* Intersection point (px): * px = p1 + u(p2 - p1) * (p - px) ∙ (p2 - p1) = 0 * Thus: * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; */ double dx, dy, u, v; dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); dy = _cairo_fixed_to_double (knots->d.y - knots->a.y); v = dx * dx + dy * dy; u = bdx * dx + bdy * dy; if (u <= 0) { /* bdx -= 0; * bdy -= 0; */ } else if (u >= v) { bdx -= dx; bdy -= dy; } else { bdx -= u/v * dx; bdy -= u/v * dy; } u = cdx * dx + cdy * dy; if (u <= 0) { /* cdx -= 0; * cdy -= 0; */ } else if (u >= v) { cdx -= dx; cdy -= dy; } else { cdx -= u/v * dx; cdy -= u/v * dy; } } berr = bdx * bdx + bdy * bdy; cerr = cdx * cdx + cdy * cdy; if (berr > cerr) return berr; else return cerr; } static cairo_status_t _cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result) { cairo_spline_knots_t s2; cairo_status_t status; if (_cairo_spline_error_squared (s1) < tolerance_squared) return _cairo_spline_add_point (result, &s1->a, &s1->b); _de_casteljau (s1, &s2); status = _cairo_spline_decompose_into (s1, tolerance_squared, result); if (unlikely (status)) return status; return _cairo_spline_decompose_into (&s2, tolerance_squared, result); } cairo_status_t _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) { cairo_spline_knots_t s1; cairo_status_t status; s1 = spline->knots; spline->last_point = s1.a; status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); if (unlikely (status)) return status; return spline->add_point_func (spline->closure, &spline->knots.d, &spline->final_slope); } /* Note: this function is only good for computing bounds in device space. */ cairo_status_t _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { double x0, x1, x2, x3; double y0, y1, y2, y3; double a, b, c; double t[4]; int t_num = 0, i; cairo_status_t status; x0 = _cairo_fixed_to_double (p0->x); y0 = _cairo_fixed_to_double (p0->y); x1 = _cairo_fixed_to_double (p1->x); y1 = _cairo_fixed_to_double (p1->y); x2 = _cairo_fixed_to_double (p2->x); y2 = _cairo_fixed_to_double (p2->y); x3 = _cairo_fixed_to_double (p3->x); y3 = _cairo_fixed_to_double (p3->y); /* The spline can be written as a polynomial of the four points: * * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 * * for 0≤t≤1. Now, the X and Y components of the spline follow the * same polynomial but with x and y replaced for p. To find the * bounds of the spline, we just need to find the X and Y bounds. * To find the bound, we take the derivative and equal it to zero, * and solve to find the t's that give the extreme points. * * Here is the derivative of the curve, sorted on t: * * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 * * Let: * * a = -p0+3p1-3p2+p3 * b = p0-2p1+p2 * c = -p0+p1 * * Gives: * * a.t² + 2b.t + c = 0 * * With: * * delta = b*b - a*c * * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if * delta is positive, and at -b/a if delta is zero. */ #define ADD(t0) \ { \ double _t0 = (t0); \ if (0 < _t0 && _t0 < 1) \ t[t_num++] = _t0; \ } #define FIND_EXTREMES(a,b,c) \ { \ if (a == 0) { \ if (b != 0) \ ADD (-c / (2*b)); \ } else { \ double b2 = b * b; \ double delta = b2 - a * c; \ if (delta > 0) { \ cairo_bool_t feasible; \ double _2ab = 2 * a * b; \ /* We are only interested in solutions t that satisfy 0<t<1 \ * here. We do some checks to avoid sqrt if the solutions \ * are not in that range. The checks can be derived from: \ * \ * 0 < (-b±√delta)/a < 1 \ */ \ if (_2ab >= 0) \ feasible = delta > b2 && delta < a*a + b2 + _2ab; \ else if (-b / a >= 1) \ feasible = delta < b2 && delta > a*a + b2 + _2ab; \ else \ feasible = delta < b2 || delta < a*a + b2 + _2ab; \ \ if (unlikely (feasible)) { \ double sqrt_delta = sqrt (delta); \ ADD ((-b - sqrt_delta) / a); \ ADD ((-b + sqrt_delta) / a); \ } \ } else if (delta == 0) { \ ADD (-b / a); \ } \ } \ } /* Find X extremes */ a = -x0 + 3*x1 - 3*x2 + x3; b = x0 - 2*x1 + x2; c = -x0 + x1; FIND_EXTREMES (a, b, c); /* Find Y extremes */ a = -y0 + 3*y1 - 3*y2 + y3; b = y0 - 2*y1 + y2; c = -y0 + y1; FIND_EXTREMES (a, b, c); status = add_point_func (closure, p0, NULL); if (unlikely (status)) return status; for (i = 0; i < t_num; i++) { cairo_point_t p; double x, y; double t_1_0, t_0_1; double t_2_0, t_0_2; double t_3_0, t_2_1_3, t_1_2_3, t_0_3; t_1_0 = t[i]; /* t */ t_0_1 = 1 - t_1_0; /* (1 - t) */ t_2_0 = t_1_0 * t_1_0; /* t * t */ t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ t_3_0 = t_2_0 * t_1_0; /* t * t * t */ t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ /* Bezier polynomial */ x = x0 * t_0_3 + x1 * t_1_2_3 + x2 * t_2_1_3 + x3 * t_3_0; y = y0 * t_0_3 + y1 * t_1_2_3 + y2 * t_2_1_3 + y3 * t_3_0; p.x = _cairo_fixed_from_double (x); p.y = _cairo_fixed_from_double (y); status = add_point_func (closure, &p, NULL); if (unlikely (status)) return status; } return add_point_func (closure, p3, NULL); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-stroke-dash-private.h���������������������0000664�0000000�0000000�00000004525�12710376503�0027446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_STROKE_DASH_PRIVATE_H #define CAIRO_STROKE_DASH_PRIVATE_H #include "cairoint.h" CAIRO_BEGIN_DECLS typedef struct _cairo_stroker_dash { cairo_bool_t dashed; unsigned int dash_index; cairo_bool_t dash_on; cairo_bool_t dash_starts_on; double dash_remain; double dash_offset; const double *dashes; unsigned int num_dashes; } cairo_stroker_dash_t; cairo_private void _cairo_stroker_dash_init (cairo_stroker_dash_t *dash, const cairo_stroke_style_t *style); cairo_private void _cairo_stroker_dash_start (cairo_stroker_dash_t *dash); cairo_private void _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step); CAIRO_END_DECLS #endif /* CAIRO_STROKE_DASH_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-stroke-dash.c�����������������������������0000664�0000000�0000000�00000005735�12710376503�0025775�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-stroke-dash-private.h" void _cairo_stroker_dash_start (cairo_stroker_dash_t *dash) { double offset; cairo_bool_t on = TRUE; unsigned int i = 0; if (! dash->dashed) return; offset = dash->dash_offset; /* We stop searching for a starting point as soon as the offset reaches zero. Otherwise when an initial dash segment shrinks to zero it will be skipped over. */ while (offset > 0.0 && offset >= dash->dashes[i]) { offset -= dash->dashes[i]; on = !on; if (++i == dash->num_dashes) i = 0; } dash->dash_index = i; dash->dash_on = dash->dash_starts_on = on; dash->dash_remain = dash->dashes[i] - offset; } void _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) { dash->dash_remain -= step; if (dash->dash_remain < CAIRO_FIXED_ERROR_DOUBLE) { if (++dash->dash_index == dash->num_dashes) dash->dash_index = 0; dash->dash_on = ! dash->dash_on; dash->dash_remain += dash->dashes[dash->dash_index]; } } void _cairo_stroker_dash_init (cairo_stroker_dash_t *dash, const cairo_stroke_style_t *style) { dash->dashed = style->dash != NULL; if (! dash->dashed) return; dash->dashes = style->dash; dash->num_dashes = style->num_dashes; dash->dash_offset = style->dash_offset; _cairo_stroker_dash_start (dash); } �����������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-stroke-style.c����������������������������0000664�0000000�0000000�00000027337�12710376503�0026220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-error-private.h" void _cairo_stroke_style_init (cairo_stroke_style_t *style) { VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; style->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; style->dash = NULL; style->num_dashes = 0; style->dash_offset = 0.0; } cairo_status_t _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, const cairo_stroke_style_t *other) { if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); style->line_width = other->line_width; style->line_cap = other->line_cap; style->line_join = other->line_join; style->miter_limit = other->miter_limit; style->num_dashes = other->num_dashes; if (other->dash == NULL) { style->dash = NULL; } else { style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double)); if (unlikely (style->dash == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (style->dash, other->dash, style->num_dashes * sizeof (double)); } style->dash_offset = other->dash_offset; return CAIRO_STATUS_SUCCESS; } void _cairo_stroke_style_fini (cairo_stroke_style_t *style) { free (style->dash); style->dash = NULL; style->num_dashes = 0; VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t))); } /* * For a stroke in the given style, compute the maximum distance * from the path that vertices could be generated. In the case * of rotation in the ctm, the distance will not be exact. */ void _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy) { double style_expansion = 0.5; if (style->line_cap == CAIRO_LINE_CAP_SQUARE) style_expansion = M_SQRT1_2; if (style->line_join == CAIRO_LINE_JOIN_MITER && ! path->stroke_is_rectilinear && style_expansion < M_SQRT2 * style->miter_limit) { style_expansion = M_SQRT2 * style->miter_limit; } style_expansion *= style->line_width; if (_cairo_matrix_has_unity_scale (ctm)) { *dx = *dy = style_expansion; } else { *dx = style_expansion * hypot (ctm->xx, ctm->xy); *dy = style_expansion * hypot (ctm->yy, ctm->yx); } } void _cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy) { double style_expansion = 0.5 * style->line_width; if (_cairo_matrix_has_unity_scale (ctm)) { *dx = *dy = style_expansion; } else { *dx = style_expansion * hypot (ctm->xx, ctm->xy); *dy = style_expansion * hypot (ctm->yy, ctm->yx); } } void _cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy) { double style_expansion = 0.5; if (style->line_join == CAIRO_LINE_JOIN_MITER && ! path->stroke_is_rectilinear && style_expansion < M_SQRT2 * style->miter_limit) { style_expansion = M_SQRT2 * style->miter_limit; } style_expansion *= style->line_width; if (_cairo_matrix_has_unity_scale (ctm)) { *dx = *dy = style_expansion; } else { *dx = style_expansion * hypot (ctm->xx, ctm->xy); *dy = style_expansion * hypot (ctm->yy, ctm->yx); } } /* * Computes the period of a dashed stroke style. * Returns 0 for non-dashed styles. */ double _cairo_stroke_style_dash_period (const cairo_stroke_style_t *style) { double period; unsigned int i; period = 0.0; for (i = 0; i < style->num_dashes; i++) period += style->dash[i]; if (style->num_dashes & 1) period *= 2.0; return period; } /* * Coefficient of the linear approximation (minimizing square difference) * of the surface covered by round caps * * This can be computed in the following way: * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is: * f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2) * The square difference to a generic linear approximation (c*d) in the range (0,w) would be: * integrate ((f(w,d) - c*d)^2, d, 0, w) * To minimize this difference it is sufficient to find a solution of the differential with * respect to c: * solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c) * Which leads to c = 9/32*pi*w * Since we're not interested in the true area, but just in a coverage extimate, * we always divide the real area by the line width (w). * The same computation for square caps would be * f(w,d) = 2 * integrate(w/2, x, -d/2, d/2) * c = 1*w * but in this case it would not be an approximation, since f is already linear in d. */ #define ROUND_MINSQ_APPROXIMATION (9*M_PI/32) /* * Computes the length of the "on" part of a dashed stroke style, * taking into account also line caps. * Returns 0 for non-dashed styles. */ double _cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) { double stroked, cap_scale; unsigned int i; switch (style->line_cap) { default: ASSERT_NOT_REACHED; case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; } stroked = 0.0; if (style->num_dashes & 1) { /* Each dash element is used both as on and as off. The order in which they are summed is * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */ for (i = 0; i < style->num_dashes; i++) stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width); } else { /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus * their coverage is approximated based on the area covered by the caps of adjacent on dases. */ for (i = 0; i + 1 < style->num_dashes; i += 2) stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); } return stroked; } /* * Verifies if _cairo_stroke_style_dash_approximate should be used to generate * an approximation of the dash pattern in the specified style, when used for * stroking a path with the given CTM and tolerance. * Always %FALSE for non-dashed styles. */ cairo_bool_t _cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double tolerance) { double period; if (! style->num_dashes) return FALSE; period = _cairo_stroke_style_dash_period (style); return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance; } /* * Create a 2-dashes approximation of a dashed style, by making the "on" and "off" * parts respect the original ratio. */ void _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double tolerance, double *dash_offset, double *dashes, unsigned int *num_dashes) { double coverage, scale, offset; cairo_bool_t on = TRUE; unsigned int i = 0; coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style); coverage = MIN (coverage, 1.0); scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0); /* We stop searching for a starting point as soon as the * offset reaches zero. Otherwise when an initial dash * segment shrinks to zero it will be skipped over. */ offset = style->dash_offset; while (offset > 0.0 && offset >= style->dash[i]) { offset -= style->dash[i]; on = !on; if (++i == style->num_dashes) i = 0; } *num_dashes = 2; /* * We want to create a new dash pattern with the same relative coverage, * but composed of just 2 elements with total length equal to scale. * Based on the formula in _cairo_stroke_style_dash_stroked: * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width) * = MIN (dashes[0] + cap_scale * (scale - dashes[0]), * dashes[0] + cap_scale * line_width) = * = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale, * dashes[0] + cap_scale * line_width) * * Solving both cases we get: * dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale) * when scale - dashes[0] <= line_width * dashes[0] = scale * coverage - cap_scale * line_width * when scale - dashes[0] > line_width. * * Comparing the two cases we get: * second > first * second > scale * (coverage - cap_scale) / (1 - cap_scale) * second - cap_scale * second - scale * coverage + scale * cap_scale > 0 * (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0 * - line_width - second + scale > 0 * scale - second > line_width * which is the condition for the second solution to be the valid one. * So when second > first, the second solution is the correct one (i.e. * the solution is always MAX (first, second). */ switch (style->line_cap) { default: ASSERT_NOT_REACHED; dashes[0] = 0.0; break; case CAIRO_LINE_CAP_BUTT: /* Simplified formula (substituting 0 for cap_scale): */ dashes[0] = scale * coverage; break; case CAIRO_LINE_CAP_ROUND: dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION), scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width); break; case CAIRO_LINE_CAP_SQUARE: /* * Special attention is needed to handle the case cap_scale == 1 (since the first solution * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using * 0 as first solution always leads to the correct solution. */ dashes[0] = MAX(0.0, scale * coverage - style->line_width); break; } dashes[1] = scale - dashes[0]; *dash_offset = on ? 0.0 : dashes[0]; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-supported-features.h����������������������0000664�0000000�0000000�00000001730�12710376503�0027406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Generated by configure. Do not edit. */ #ifndef CAIRO_SUPPORTED_FEATURES_H #define CAIRO_SUPPORTED_FEATURES_H /* This is a dummy header, to trick gtk-doc only */ #define CAIRO_HAS_XLIB_SURFACE 1 #define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 #define CAIRO_HAS_XCB_SURFACE 1 #define CAIRO_HAS_XCB_SHM_FUNCTIONS 1 #define CAIRO_HAS_QUARTZ_SURFACE 1 #define CAIRO_HAS_QUARTZ_FONT 1 #define CAIRO_HAS_WIN32_SURFACE 1 #define CAIRO_HAS_WIN32_FONT 1 #define CAIRO_HAS_PNG_FUNCTIONS 1 #define CAIRO_HAS_EGL_FUNCTIONS 1 #define CAIRO_HAS_GLX_FUNCTIONS 1 #define CAIRO_HAS_WGL_FUNCTIONS 1 #define CAIRO_HAS_SCRIPT_SURFACE 1 #define CAIRO_HAS_FT_FONT 1 #define CAIRO_HAS_FC_FONT 1 #define CAIRO_HAS_PS_SURFACE 1 #define CAIRO_HAS_PDF_SURFACE 1 #define CAIRO_HAS_SVG_SURFACE 1 #define CAIRO_HAS_IMAGE_SURFACE 1 #define CAIRO_HAS_MIME_SURFACE 1 #define CAIRO_HAS_RECORDING_SURFACE 1 #define CAIRO_HAS_OBSERVER_SURFACE 1 #define CAIRO_HAS_USER_FONT 1 #define CAIRO_HAS_GOBJECT_FUNCTIONS 1 #endif ����������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-backend-private.h�����������������0000664�0000000�0000000�00000015726�12710376503�0030244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SURFACE_BACKEND_PRIVATE_H #define CAIRO_SURFACE_BACKEND_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" CAIRO_BEGIN_DECLS struct _cairo_surface_backend { cairo_surface_type_t type; cairo_warn cairo_status_t (*finish) (void *surface); cairo_t * (*create_context) (void *surface); cairo_surface_t * (*create_similar) (void *surface, cairo_content_t content, int width, int height); cairo_surface_t * (*create_similar_image) (void *surface, cairo_format_t format, int width, int height); cairo_image_surface_t * (*map_to_image) (void *surface, const cairo_rectangle_int_t *extents); cairo_int_status_t (*unmap_image) (void *surface, cairo_image_surface_t *image); cairo_surface_t * (*source) (void *abstract_surface, cairo_rectangle_int_t *extents); cairo_warn cairo_status_t (*acquire_source_image) (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra); cairo_warn void (*release_source_image) (void *abstract_surface, cairo_image_surface_t *image_out, void *image_extra); cairo_surface_t * (*snapshot) (void *surface); cairo_warn cairo_int_status_t (*copy_page) (void *surface); cairo_warn cairo_int_status_t (*show_page) (void *surface); /* Get the extents of the current surface. For many surface types * this will be as simple as { x=0, y=0, width=surface->width, * height=surface->height}. * * If this function is not implemented, or if it returns * FALSE the surface is considered to be * boundless and infinite bounds are used for it. */ cairo_bool_t (*get_extents) (void *surface, cairo_rectangle_int_t *extents); void (*get_font_options) (void *surface, cairo_font_options_t *options); cairo_warn cairo_status_t (*flush) (void *surface, unsigned flags); cairo_warn cairo_status_t (*mark_dirty_rectangle) (void *surface, int x, int y, int width, int height); cairo_warn cairo_int_status_t (*paint) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*mask) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*stroke) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*fill_stroke) (void *surface, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip); cairo_warn cairo_int_status_t (*show_glyphs) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); cairo_bool_t (*has_show_text_glyphs) (void *surface); cairo_warn cairo_int_status_t (*show_text_glyphs) (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); const char ** (*get_supported_mime_types) (void *surface); }; cairo_private cairo_status_t _cairo_surface_default_acquire_source_image (void *surface, cairo_image_surface_t **image_out, void **image_extra); cairo_private void _cairo_surface_default_release_source_image (void *surface, cairo_image_surface_t *image, void *image_extra); cairo_private cairo_surface_t * _cairo_surface_default_source (void *surface, cairo_rectangle_int_t *extents); CAIRO_END_DECLS #endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */ ������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-clipper-private.h�����������������0000664�0000000�0000000�00000004742�12710376503�0030307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.u> */ #ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H #define CAIRO_SURFACE_CLIPPER_PRIVATE_H #include "cairo-types-private.h" #include "cairo-clip-private.h" CAIRO_BEGIN_DECLS typedef struct _cairo_surface_clipper cairo_surface_clipper_t; typedef cairo_status_t (*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *, cairo_path_fixed_t *, cairo_fill_rule_t, double, cairo_antialias_t); struct _cairo_surface_clipper { cairo_clip_t *clip; cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path; }; cairo_private cairo_status_t _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, const cairo_clip_t *clip); cairo_private void _cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, cairo_surface_clipper_intersect_clip_path_func_t intersect); cairo_private void _cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper); CAIRO_END_DECLS #endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */ ������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-clipper.c�������������������������0000664�0000000�0000000�00000012744�12710376503�0026633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-surface-clipper-private.h" /* A collection of routines to facilitate vector surface clipping */ /* XXX Eliminate repeated paths and nested clips */ static cairo_status_t _cairo_path_fixed_add_box (cairo_path_fixed_t *path, const cairo_box_t *box) { cairo_status_t status; status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); if (unlikely (status)) return status; return _cairo_path_fixed_close_path (path); } static cairo_status_t _cairo_surface_clipper_intersect_clip_boxes (cairo_surface_clipper_t *clipper, const cairo_clip_t *clip) { cairo_path_fixed_t path; cairo_status_t status; int i; if (clip->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* Reconstruct the path for the clip boxes. * XXX maybe a new clipper callback? */ _cairo_path_fixed_init (&path); for (i = 0; i < clip->num_boxes; i++) { status = _cairo_path_fixed_add_box (&path, &clip->boxes[i]); if (unlikely (status)) { _cairo_path_fixed_fini (&path); return status; } } status = clipper->intersect_clip_path (clipper, &path, CAIRO_FILL_RULE_WINDING, 0., CAIRO_ANTIALIAS_DEFAULT); _cairo_path_fixed_fini (&path); return status; } static cairo_status_t _cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, cairo_clip_path_t *clip_path, cairo_clip_path_t *end) { cairo_status_t status; if (clip_path->prev != end) { status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, clip_path->prev, end); if (unlikely (status)) return status; } return clipper->intersect_clip_path (clipper, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias); } cairo_status_t _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, const cairo_clip_t *clip) { cairo_status_t status; cairo_bool_t incremental = FALSE; if (_cairo_clip_equal (clip, clipper->clip)) return CAIRO_STATUS_SUCCESS; /* all clipped out state should never propagate this far */ assert (!_cairo_clip_is_all_clipped (clip)); /* XXX Is this an incremental clip? */ if (clipper->clip && clip && clip->num_boxes == clipper->clip->num_boxes && memcmp (clip->boxes, clipper->clip->boxes, sizeof (cairo_box_t) * clip->num_boxes) == 0) { cairo_clip_path_t *clip_path = clip->path; while (clip_path != NULL && clip_path != clipper->clip->path) clip_path = clip_path->prev; if (clip_path) { incremental = TRUE; status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, clip->path, clipper->clip->path); } } _cairo_clip_destroy (clipper->clip); clipper->clip = _cairo_clip_copy (clip); if (incremental) return status; status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); if (unlikely (status)) return status; if (clip == NULL) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_clipper_intersect_clip_boxes (clipper, clip); if (unlikely (status)) return status; if (clip->path != NULL) { status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, clip->path, NULL); } return status; } void _cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, cairo_surface_clipper_intersect_clip_path_func_t func) { clipper->clip = NULL; clipper->intersect_clip_path = func; } void _cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) { _cairo_clip_destroy (clipper->clip); clipper->clip = NULL; } ����������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-fallback-private.h����������������0000664�0000000�0000000�00000006524�12710376503�0030410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H #define CAIRO_SURFACE_FALLBACK_PRIVATE_H #include "cairoint.h" CAIRO_BEGIN_DECLS cairo_private cairo_int_status_t _cairo_surface_fallback_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_surface_fallback_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_surface_fallback_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t*style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_surface_fallback_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_surface_fallback_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); CAIRO_END_DECLS #endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-fallback.c������������������������0000664�0000000�0000000�00000007507�12710376503�0026735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Joonas Pihlaja <jpihlaja@cc.helsinki.fi> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-compositor-private.h" #include "cairo-surface-fallback-private.h" cairo_int_status_t _cairo_surface_fallback_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { return _cairo_compositor_paint (&_cairo_fallback_compositor, surface, op, source, clip); } cairo_int_status_t _cairo_surface_fallback_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { return _cairo_compositor_mask (&_cairo_fallback_compositor, surface, op, source, mask, clip); } cairo_int_status_t _cairo_surface_fallback_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, const cairo_stroke_style_t*style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_stroke (&_cairo_fallback_compositor, surface, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip); } cairo_int_status_t _cairo_surface_fallback_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { return _cairo_compositor_fill (&_cairo_fallback_compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); } cairo_int_status_t _cairo_surface_fallback_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { return _cairo_compositor_glyphs (&_cairo_fallback_compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-inline.h��������������������������0000664�0000000�0000000�00000004212�12710376503�0026447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SURFACE_INLINE_H #define CAIRO_SURFACE_INLINE_H #include "cairo-surface-private.h" static inline cairo_status_t __cairo_surface_flush (cairo_surface_t *surface, unsigned flags) { cairo_status_t status = CAIRO_STATUS_SUCCESS; if (surface->backend->flush) status = surface->backend->flush (surface, flags); return status; } static inline cairo_surface_t * _cairo_surface_reference (cairo_surface_t *surface) { if (!CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) _cairo_reference_count_inc (&surface->ref_count); return surface; } #endif /* CAIRO_SURFACE_INLINE_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-observer-inline.h�����������������0000664�0000000�0000000�00000004233�12710376503�0030277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_OBSERVER_INLINE_H #define CAIRO_SURFACE_OBSERVER_INLINE_H #include "cairo-surface-observer-private.h" static inline cairo_surface_t * _cairo_surface_observer_get_target (cairo_surface_t *surface) { return ((cairo_surface_observer_t *) surface)->target; } static inline cairo_bool_t _cairo_surface_is_observer (cairo_surface_t *surface) { return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER; } static inline cairo_bool_t _cairo_device_is_observer (cairo_device_t *device) { return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER; } #endif /* CAIRO_SURFACE_OBSERVER_INLINE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-observer-private.h����������������0000664�0000000�0000000�00000012367�12710376503�0030502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H #define CAIRO_SURFACE_OBSERVER_PRIVATE_H #include "cairoint.h" #include "cairo-device-private.h" #include "cairo-list-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-private.h" #include "cairo-surface-backend-private.h" #include "cairo-time-private.h" struct stat { double min, max, sum, sum_sq; unsigned count; }; #define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1) #define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1) #define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1) #define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1) #define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1) struct extents { struct stat area; unsigned int bounded, unbounded; }; struct pattern { unsigned int type[7]; /* native/record/other surface/gradients */ }; struct path { unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */ }; struct clip { unsigned int type[6]; /* none, region, boxes, single path, polygon, general */ }; typedef struct _cairo_observation cairo_observation_t; typedef struct _cairo_observation_record cairo_observation_record_t; typedef struct _cairo_device_observer cairo_device_observer_t; struct _cairo_observation_record { cairo_content_t target_content; int target_width; int target_height; int index; cairo_operator_t op; int source; int mask; int num_glyphs; int path; int fill_rule; double tolerance; int antialias; int clip; cairo_time_t elapsed; }; struct _cairo_observation { int num_surfaces; int num_contexts; int num_sources_acquired; /* XXX put interesting stats here! */ struct paint { cairo_time_t elapsed; unsigned int count; struct extents extents; unsigned int operators[NUM_OPERATORS]; struct pattern source; struct clip clip; unsigned int noop; cairo_observation_record_t slowest; } paint; struct mask { cairo_time_t elapsed; unsigned int count; struct extents extents; unsigned int operators[NUM_OPERATORS]; struct pattern source; struct pattern mask; struct clip clip; unsigned int noop; cairo_observation_record_t slowest; } mask; struct fill { cairo_time_t elapsed; unsigned int count; struct extents extents; unsigned int operators[NUM_OPERATORS]; struct pattern source; struct path path; unsigned int antialias[NUM_ANTIALIAS]; unsigned int fill_rule[NUM_FILL_RULE]; struct clip clip; unsigned int noop; cairo_observation_record_t slowest; } fill; struct stroke { cairo_time_t elapsed; unsigned int count; struct extents extents; unsigned int operators[NUM_OPERATORS]; unsigned int caps[NUM_CAPS]; unsigned int joins[NUM_CAPS]; unsigned int antialias[NUM_ANTIALIAS]; struct pattern source; struct path path; struct stat line_width; struct clip clip; unsigned int noop; cairo_observation_record_t slowest; } stroke; struct glyphs { cairo_time_t elapsed; unsigned int count; struct extents extents; unsigned int operators[NUM_OPERATORS]; struct pattern source; struct clip clip; unsigned int noop; cairo_observation_record_t slowest; } glyphs; cairo_array_t timings; cairo_recording_surface_t *record; }; struct _cairo_device_observer { cairo_device_t base; cairo_device_t *target; cairo_observation_t log; }; struct callback_list { cairo_list_t link; cairo_surface_observer_callback_t func; void *data; }; struct _cairo_surface_observer { cairo_surface_t base; cairo_surface_t *target; cairo_observation_t log; cairo_list_t paint_callbacks; cairo_list_t mask_callbacks; cairo_list_t fill_callbacks; cairo_list_t stroke_callbacks; cairo_list_t glyphs_callbacks; cairo_list_t flush_callbacks; cairo_list_t finish_callbacks; }; #endif /* CAIRO_SURFACE_OBSERVER_PRIVATE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-observer.c������������������������0000664�0000000�0000000�00000166004�12710376503�0027023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-surface-observer-private.h" #include "cairo-surface-observer-inline.h" #include "cairo-array-private.h" #include "cairo-combsort-inline.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-pattern-private.h" #include "cairo-output-stream-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-subsurface-inline.h" #include "cairo-reference-count-private.h" #if CAIRO_HAS_SCRIPT_SURFACE #include "cairo-script-private.h" #endif static const cairo_surface_backend_t _cairo_surface_observer_backend; /* observation/stats */ static void init_stats (struct stat *s) { s->min = HUGE_VAL; s->max = -HUGE_VAL; } static void init_extents (struct extents *e) { init_stats (&e->area); } static void init_pattern (struct pattern *p) { } static void init_path (struct path *p) { } static void init_clip (struct clip *c) { } static void init_paint (struct paint *p) { init_extents (&p->extents); init_pattern (&p->source); init_clip (&p->clip); } static void init_mask (struct mask *m) { init_extents (&m->extents); init_pattern (&m->source); init_pattern (&m->mask); init_clip (&m->clip); } static void init_fill (struct fill *f) { init_extents (&f->extents); init_pattern (&f->source); init_path (&f->path); init_clip (&f->clip); } static void init_stroke (struct stroke *s) { init_extents (&s->extents); init_pattern (&s->source); init_path (&s->path); init_clip (&s->clip); } static void init_glyphs (struct glyphs *g) { init_extents (&g->extents); init_pattern (&g->source); init_clip (&g->clip); } static cairo_status_t log_init (cairo_observation_t *log, cairo_bool_t record) { memset (log, 0, sizeof(*log)); init_paint (&log->paint); init_mask (&log->mask); init_fill (&log->fill); init_stroke (&log->stroke); init_glyphs (&log->glyphs); _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t)); if (record) { log->record = (cairo_recording_surface_t *) cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); if (unlikely (log->record->base.status)) return log->record->base.status; log->record->optimize_clears = FALSE; } return CAIRO_STATUS_SUCCESS; } static void log_fini (cairo_observation_t *log) { _cairo_array_fini (&log->timings); cairo_surface_destroy (&log->record->base); } static cairo_surface_t* get_pattern_surface (const cairo_pattern_t *pattern) { return ((cairo_surface_pattern_t *)pattern)->surface; } static int classify_pattern (const cairo_pattern_t *pattern, const cairo_surface_t *target) { int classify; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: if (get_pattern_surface (pattern)->type == target->type) classify = 0; else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING) classify = 1; else classify = 2; break; default: case CAIRO_PATTERN_TYPE_SOLID: classify = 3; break; case CAIRO_PATTERN_TYPE_LINEAR: classify = 4; break; case CAIRO_PATTERN_TYPE_RADIAL: classify = 5; break; case CAIRO_PATTERN_TYPE_MESH: classify = 6; break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: classify = 7; break; } return classify; } static void add_pattern (struct pattern *stats, const cairo_pattern_t *pattern, const cairo_surface_t *target) { stats->type[classify_pattern(pattern, target)]++; } static int classify_path (const cairo_path_fixed_t *path, cairo_bool_t is_fill) { int classify; /* XXX improve for stroke */ classify = -1; if (is_fill) { if (path->fill_is_empty) classify = 0; else if (_cairo_path_fixed_fill_is_rectilinear (path)) classify = path->fill_maybe_region ? 1 : 2; } else { if (_cairo_path_fixed_stroke_is_rectilinear (path)) classify = 2; } if (classify == -1) classify = 3 + (path->has_curve_to != 0); return classify; } static void add_path (struct path *stats, const cairo_path_fixed_t *path, cairo_bool_t is_fill) { stats->type[classify_path(path, is_fill)]++; } static int classify_clip (const cairo_clip_t *clip) { int classify; if (clip == NULL) classify = 0; else if (_cairo_clip_is_region (clip)) classify = 1; else if (clip->path == NULL) classify = 2; else if (clip->path->prev == NULL) classify = 3; else if (_cairo_clip_is_polygon (clip)) classify = 4; else classify = 5; return classify; } static void add_clip (struct clip *stats, const cairo_clip_t *clip) { stats->type[classify_clip (clip)]++; } static void stats_add (struct stat *s, double v) { if (v < s->min) s->min = v; if (v > s->max) s->max = v; s->sum += v; s->sum_sq += v*v; s->count++; } static void add_extents (struct extents *stats, const cairo_composite_rectangles_t *extents) { const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded; stats_add (&stats->area, r->width * r->height); stats->bounded += extents->is_bounded != 0; stats->unbounded += extents->is_bounded == 0; } /* device interface */ static void _cairo_device_observer_lock (void *_device) { cairo_device_observer_t *device = (cairo_device_observer_t *) _device; cairo_status_t ignored; /* cairo_device_acquire() can fail for nil and finished * devices. We don't care about observing them. */ ignored = cairo_device_acquire (device->target); } static void _cairo_device_observer_unlock (void *_device) { cairo_device_observer_t *device = (cairo_device_observer_t *) _device; cairo_device_release (device->target); } static cairo_status_t _cairo_device_observer_flush (void *_device) { cairo_device_observer_t *device = (cairo_device_observer_t *) _device; if (device->target == NULL) return CAIRO_STATUS_SUCCESS; cairo_device_flush (device->target); return device->target->status; } static void _cairo_device_observer_finish (void *_device) { cairo_device_observer_t *device = (cairo_device_observer_t *) _device; log_fini (&device->log); cairo_device_finish (device->target); } static void _cairo_device_observer_destroy (void *_device) { cairo_device_observer_t *device = (cairo_device_observer_t *) _device; cairo_device_destroy (device->target); free (device); } static const cairo_device_backend_t _cairo_device_observer_backend = { CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER, _cairo_device_observer_lock, _cairo_device_observer_unlock, _cairo_device_observer_flush, _cairo_device_observer_finish, _cairo_device_observer_destroy, }; static cairo_device_t * _cairo_device_create_observer_internal (cairo_device_t *target, cairo_bool_t record) { cairo_device_observer_t *device; cairo_status_t status; device = malloc (sizeof (cairo_device_observer_t)); if (unlikely (device == NULL)) return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_device_init (&device->base, &_cairo_device_observer_backend); status = log_init (&device->log, record); if (unlikely (status)) { free (device); return _cairo_device_create_in_error (status); } device->target = cairo_device_reference (target); return &device->base; } /* surface interface */ static cairo_device_observer_t * to_device (cairo_surface_observer_t *suface) { return (cairo_device_observer_t *)suface->base.device; } static cairo_surface_t * _cairo_surface_create_observer_internal (cairo_device_t *device, cairo_surface_t *target) { cairo_surface_observer_t *surface; cairo_status_t status; surface = malloc (sizeof (cairo_surface_observer_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_surface_observer_backend, device, target->content); status = log_init (&surface->log, ((cairo_device_observer_t *)device)->log.record != NULL); if (unlikely (status)) { free (surface); return _cairo_surface_create_in_error (status); } surface->target = cairo_surface_reference (target); surface->base.type = surface->target->type; surface->base.is_clear = surface->target->is_clear; cairo_list_init (&surface->paint_callbacks); cairo_list_init (&surface->mask_callbacks); cairo_list_init (&surface->fill_callbacks); cairo_list_init (&surface->stroke_callbacks); cairo_list_init (&surface->glyphs_callbacks); cairo_list_init (&surface->flush_callbacks); cairo_list_init (&surface->finish_callbacks); surface->log.num_surfaces++; to_device (surface)->log.num_surfaces++; return &surface->base; } static inline void do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head) { struct callback_list *cb; cairo_list_foreach_entry (cb, struct callback_list, head, link) cb->func (&surface->base, surface->target, cb->data); } static cairo_status_t _cairo_surface_observer_finish (void *abstract_surface) { cairo_surface_observer_t *surface = abstract_surface; do_callbacks (surface, &surface->finish_callbacks); cairo_surface_destroy (surface->target); log_fini (&surface->log); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_surface_observer_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_surface_observer_t *other = abstract_other; cairo_surface_t *target, *surface; target = NULL; if (other->target->backend->create_similar) target = other->target->backend->create_similar (other->target, content, width, height); if (target == NULL) target = _cairo_image_surface_create_with_content (content, width, height); surface = _cairo_surface_create_observer_internal (other->base.device, target); cairo_surface_destroy (target); return surface; } static cairo_surface_t * _cairo_surface_observer_create_similar_image (void *other, cairo_format_t format, int width, int height) { cairo_surface_observer_t *surface = other; if (surface->target->backend->create_similar_image) return surface->target->backend->create_similar_image (surface->target, format, width, height); return NULL; } static cairo_image_surface_t * _cairo_surface_observer_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_surface_observer_t *surface = abstract_surface; return _cairo_surface_map_to_image (surface->target, extents); } static cairo_int_status_t _cairo_surface_observer_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_surface_observer_t *surface = abstract_surface; return _cairo_surface_unmap_image (surface->target, image); } static void record_target (cairo_observation_record_t *r, cairo_surface_t *target) { cairo_rectangle_int_t extents; r->target_content = target->content; if (_cairo_surface_get_extents (target, &extents)) { r->target_width = extents.width; r->target_height = extents.height; } else { r->target_width = -1; r->target_height = -1; } } static cairo_observation_record_t * record_paint (cairo_observation_record_t *r, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip, cairo_time_t elapsed) { record_target (r, target); r->op = op; r->source = classify_pattern (source, target); r->mask = -1; r->num_glyphs = -1; r->path = -1; r->fill_rule = -1; r->tolerance = -1; r->antialias = -1; r->clip = classify_clip (clip); r->elapsed = elapsed; return r; } static cairo_observation_record_t * record_mask (cairo_observation_record_t *r, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip, cairo_time_t elapsed) { record_target (r, target); r->op = op; r->source = classify_pattern (source, target); r->mask = classify_pattern (mask, target); r->num_glyphs = -1; r->path = -1; r->fill_rule = -1; r->tolerance = -1; r->antialias = -1; r->clip = classify_clip (clip); r->elapsed = elapsed; return r; } static cairo_observation_record_t * record_fill (cairo_observation_record_t *r, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip, cairo_time_t elapsed) { record_target (r, target); r->op = op; r->source = classify_pattern (source, target); r->mask = -1; r->num_glyphs = -1; r->path = classify_path (path, TRUE); r->fill_rule = fill_rule; r->tolerance = tolerance; r->antialias = antialias; r->clip = classify_clip (clip); r->elapsed = elapsed; return r; } static cairo_observation_record_t * record_stroke (cairo_observation_record_t *r, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip, cairo_time_t elapsed) { record_target (r, target); r->op = op; r->source = classify_pattern (source, target); r->mask = -1; r->num_glyphs = -1; r->path = classify_path (path, FALSE); r->fill_rule = -1; r->tolerance = tolerance; r->antialias = antialias; r->clip = classify_clip (clip); r->elapsed = elapsed; return r; } static cairo_observation_record_t * record_glyphs (cairo_observation_record_t *r, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip, cairo_time_t elapsed) { record_target (r, target); r->op = op; r->source = classify_pattern (source, target); r->mask = -1; r->path = -1; r->num_glyphs = num_glyphs; r->fill_rule = -1; r->tolerance = -1; r->antialias = -1; r->clip = classify_clip (clip); r->elapsed = elapsed; return r; } static void add_record (cairo_observation_t *log, cairo_observation_record_t *r) { cairo_int_status_t status; r->index = log->record ? log->record->commands.num_elements : 0; status = _cairo_array_append (&log->timings, r); assert (status == CAIRO_INT_STATUS_SUCCESS); } static void sync (cairo_surface_t *target, int x, int y) { cairo_rectangle_int_t extents; extents.x = x; extents.y = y; extents.width = 1; extents.height = 1; _cairo_surface_unmap_image (target, _cairo_surface_map_to_image (target, &extents)); } static void midpt (const cairo_composite_rectangles_t *extents, int *x, int *y) { *x = extents->bounded.x + extents->bounded.width / 2; *y = extents->bounded.y + extents->bounded.height / 2; } static void add_record_paint (cairo_observation_t *log, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip, cairo_time_t elapsed) { cairo_observation_record_t record; cairo_int_status_t status; add_record (log, record_paint (&record, target, op, source, clip, elapsed)); /* We have to bypass the high-level surface layer in case it tries to be * too smart and discard operations; we need to record exactly what just * happened on the target. */ if (log->record) { status = log->record->base.backend->paint (&log->record->base, op, source, clip); assert (status == CAIRO_INT_STATUS_SUCCESS); } if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed)) log->paint.slowest = record; log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed); } static cairo_int_status_t _cairo_surface_observer_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; cairo_int_status_t status; cairo_time_t t; int x, y; /* XXX device locking */ surface->log.paint.count++; surface->log.paint.operators[op]++; add_pattern (&surface->log.paint.source, source, surface->target); add_clip (&surface->log.paint.clip, clip); device->log.paint.count++; device->log.paint.operators[op]++; add_pattern (&device->log.paint.source, source, surface->target); add_clip (&device->log.paint.clip, clip); status = _cairo_composite_rectangles_init_for_paint (&composite, surface->target, op, source, clip); if (unlikely (status)) { surface->log.paint.noop++; device->log.paint.noop++; return status; } midpt (&composite, &x, &y); add_extents (&surface->log.paint.extents, &composite); add_extents (&device->log.paint.extents, &composite); _cairo_composite_rectangles_fini (&composite); t = _cairo_time_get (); status = _cairo_surface_paint (surface->target, op, source, clip); if (unlikely (status)) return status; sync (surface->target, x, y); t = _cairo_time_get_delta (t); add_record_paint (&surface->log, surface->target, op, source, clip, t); add_record_paint (&device->log, surface->target, op, source, clip, t); do_callbacks (surface, &surface->paint_callbacks); return CAIRO_STATUS_SUCCESS; } static void add_record_mask (cairo_observation_t *log, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip, cairo_time_t elapsed) { cairo_observation_record_t record; cairo_int_status_t status; add_record (log, record_mask (&record, target, op, source, mask, clip, elapsed)); if (log->record) { status = log->record->base.backend->mask (&log->record->base, op, source, mask, clip); assert (status == CAIRO_INT_STATUS_SUCCESS); } if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed)) log->mask.slowest = record; log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed); } static cairo_int_status_t _cairo_surface_observer_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; cairo_int_status_t status; cairo_time_t t; int x, y; surface->log.mask.count++; surface->log.mask.operators[op]++; add_pattern (&surface->log.mask.source, source, surface->target); add_pattern (&surface->log.mask.mask, mask, surface->target); add_clip (&surface->log.mask.clip, clip); device->log.mask.count++; device->log.mask.operators[op]++; add_pattern (&device->log.mask.source, source, surface->target); add_pattern (&device->log.mask.mask, mask, surface->target); add_clip (&device->log.mask.clip, clip); status = _cairo_composite_rectangles_init_for_mask (&composite, surface->target, op, source, mask, clip); if (unlikely (status)) { surface->log.mask.noop++; device->log.mask.noop++; return status; } midpt (&composite, &x, &y); add_extents (&surface->log.mask.extents, &composite); add_extents (&device->log.mask.extents, &composite); _cairo_composite_rectangles_fini (&composite); t = _cairo_time_get (); status = _cairo_surface_mask (surface->target, op, source, mask, clip); if (unlikely (status)) return status; sync (surface->target, x, y); t = _cairo_time_get_delta (t); add_record_mask (&surface->log, surface->target, op, source, mask, clip, t); add_record_mask (&device->log, surface->target, op, source, mask, clip, t); do_callbacks (surface, &surface->mask_callbacks); return CAIRO_STATUS_SUCCESS; } static void add_record_fill (cairo_observation_t *log, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip, cairo_time_t elapsed) { cairo_observation_record_t record; cairo_int_status_t status; add_record (log, record_fill (&record, target, op, source, path, fill_rule, tolerance, antialias, clip, elapsed)); if (log->record) { status = log->record->base.backend->fill (&log->record->base, op, source, path, fill_rule, tolerance, antialias, clip); assert (status == CAIRO_INT_STATUS_SUCCESS); } if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed)) log->fill.slowest = record; log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed); } static cairo_int_status_t _cairo_surface_observer_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; cairo_int_status_t status; cairo_time_t t; int x, y; surface->log.fill.count++; surface->log.fill.operators[op]++; surface->log.fill.fill_rule[fill_rule]++; surface->log.fill.antialias[antialias]++; add_pattern (&surface->log.fill.source, source, surface->target); add_path (&surface->log.fill.path, path, TRUE); add_clip (&surface->log.fill.clip, clip); device->log.fill.count++; device->log.fill.operators[op]++; device->log.fill.fill_rule[fill_rule]++; device->log.fill.antialias[antialias]++; add_pattern (&device->log.fill.source, source, surface->target); add_path (&device->log.fill.path, path, TRUE); add_clip (&device->log.fill.clip, clip); status = _cairo_composite_rectangles_init_for_fill (&composite, surface->target, op, source, path, clip); if (unlikely (status)) { surface->log.fill.noop++; device->log.fill.noop++; return status; } midpt (&composite, &x, &y); add_extents (&surface->log.fill.extents, &composite); add_extents (&device->log.fill.extents, &composite); _cairo_composite_rectangles_fini (&composite); t = _cairo_time_get (); status = _cairo_surface_fill (surface->target, op, source, path, fill_rule, tolerance, antialias, clip); if (unlikely (status)) return status; sync (surface->target, x, y); t = _cairo_time_get_delta (t); add_record_fill (&surface->log, surface->target, op, source, path, fill_rule, tolerance, antialias, clip, t); add_record_fill (&device->log, surface->target, op, source, path, fill_rule, tolerance, antialias, clip, t); do_callbacks (surface, &surface->fill_callbacks); return CAIRO_STATUS_SUCCESS; } static void add_record_stroke (cairo_observation_t *log, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip, cairo_time_t elapsed) { cairo_observation_record_t record; cairo_int_status_t status; add_record (log, record_stroke (&record, target, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip, elapsed)); if (log->record) { status = log->record->base.backend->stroke (&log->record->base, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip); assert (status == CAIRO_INT_STATUS_SUCCESS); } if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed)) log->stroke.slowest = record; log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed); } static cairo_int_status_t _cairo_surface_observer_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; cairo_int_status_t status; cairo_time_t t; int x, y; surface->log.stroke.count++; surface->log.stroke.operators[op]++; surface->log.stroke.antialias[antialias]++; surface->log.stroke.caps[style->line_cap]++; surface->log.stroke.joins[style->line_join]++; add_pattern (&surface->log.stroke.source, source, surface->target); add_path (&surface->log.stroke.path, path, FALSE); add_clip (&surface->log.stroke.clip, clip); device->log.stroke.count++; device->log.stroke.operators[op]++; device->log.stroke.antialias[antialias]++; device->log.stroke.caps[style->line_cap]++; device->log.stroke.joins[style->line_join]++; add_pattern (&device->log.stroke.source, source, surface->target); add_path (&device->log.stroke.path, path, FALSE); add_clip (&device->log.stroke.clip, clip); status = _cairo_composite_rectangles_init_for_stroke (&composite, surface->target, op, source, path, style, ctm, clip); if (unlikely (status)) { surface->log.stroke.noop++; device->log.stroke.noop++; return status; } midpt (&composite, &x, &y); add_extents (&surface->log.stroke.extents, &composite); add_extents (&device->log.stroke.extents, &composite); _cairo_composite_rectangles_fini (&composite); t = _cairo_time_get (); status = _cairo_surface_stroke (surface->target, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (unlikely (status)) return status; sync (surface->target, x, y); t = _cairo_time_get_delta (t); add_record_stroke (&surface->log, surface->target, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip, t); add_record_stroke (&device->log, surface->target, op, source, path, style, ctm,ctm_inverse, tolerance, antialias, clip, t); do_callbacks (surface, &surface->stroke_callbacks); return CAIRO_STATUS_SUCCESS; } static void add_record_glyphs (cairo_observation_t *log, cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t*source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip, cairo_time_t elapsed) { cairo_observation_record_t record; cairo_int_status_t status; add_record (log, record_glyphs (&record, target, op, source, glyphs, num_glyphs, scaled_font, clip, elapsed)); if (log->record) { status = log->record->base.backend->show_text_glyphs (&log->record->base, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); assert (status == CAIRO_INT_STATUS_SUCCESS); } if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed)) log->glyphs.slowest = record; log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed); } static cairo_int_status_t _cairo_surface_observer_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; cairo_int_status_t status; cairo_glyph_t *dev_glyphs; cairo_time_t t; int x, y; surface->log.glyphs.count++; surface->log.glyphs.operators[op]++; add_pattern (&surface->log.glyphs.source, source, surface->target); add_clip (&surface->log.glyphs.clip, clip); device->log.glyphs.count++; device->log.glyphs.operators[op]++; add_pattern (&device->log.glyphs.source, source, surface->target); add_clip (&device->log.glyphs.clip, clip); status = _cairo_composite_rectangles_init_for_glyphs (&composite, surface->target, op, source, scaled_font, glyphs, num_glyphs, clip, NULL); if (unlikely (status)) { surface->log.glyphs.noop++; device->log.glyphs.noop++; return status; } midpt (&composite, &x, &y); add_extents (&surface->log.glyphs.extents, &composite); add_extents (&device->log.glyphs.extents, &composite); _cairo_composite_rectangles_fini (&composite); /* XXX We have to copy the glyphs, because the backend is allowed to * modify! */ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); t = _cairo_time_get (); status = _cairo_surface_show_text_glyphs (surface->target, op, source, NULL, 0, dev_glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); free (dev_glyphs); if (unlikely (status)) return status; sync (surface->target, x, y); t = _cairo_time_get_delta (t); add_record_glyphs (&surface->log, surface->target, op, source, glyphs, num_glyphs, scaled_font, clip, t); add_record_glyphs (&device->log, surface->target, op, source, glyphs, num_glyphs, scaled_font, clip, t); do_callbacks (surface, &surface->glyphs_callbacks); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_surface_observer_flush (void *abstract_surface, unsigned flags) { cairo_surface_observer_t *surface = abstract_surface; do_callbacks (surface, &surface->flush_callbacks); return _cairo_surface_flush (surface->target, flags); } static cairo_status_t _cairo_surface_observer_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { cairo_surface_observer_t *surface = abstract_surface; cairo_status_t status; printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height); status = CAIRO_STATUS_SUCCESS; if (surface->target->backend->mark_dirty_rectangle) status = surface->target->backend->mark_dirty_rectangle (surface->target, x,y, width,height); return status; } static cairo_int_status_t _cairo_surface_observer_copy_page (void *abstract_surface) { cairo_surface_observer_t *surface = abstract_surface; cairo_status_t status; status = CAIRO_STATUS_SUCCESS; if (surface->target->backend->copy_page) status = surface->target->backend->copy_page (surface->target); return status; } static cairo_int_status_t _cairo_surface_observer_show_page (void *abstract_surface) { cairo_surface_observer_t *surface = abstract_surface; cairo_status_t status; status = CAIRO_STATUS_SUCCESS; if (surface->target->backend->show_page) status = surface->target->backend->show_page (surface->target); return status; } static cairo_bool_t _cairo_surface_observer_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_observer_t *surface = abstract_surface; return _cairo_surface_get_extents (surface->target, extents); } static void _cairo_surface_observer_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_surface_observer_t *surface = abstract_surface; if (surface->target->backend->get_font_options != NULL) surface->target->backend->get_font_options (surface->target, options); } static cairo_surface_t * _cairo_surface_observer_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_observer_t *surface = abstract_surface; return _cairo_surface_get_source (surface->target, extents); } static cairo_status_t _cairo_surface_observer_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_surface_observer_t *surface = abstract_surface; surface->log.num_sources_acquired++; to_device (surface)->log.num_sources_acquired++; return _cairo_surface_acquire_source_image (surface->target, image_out, image_extra); } static void _cairo_surface_observer_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_observer_t *surface = abstract_surface; _cairo_surface_release_source_image (surface->target, image, image_extra); } static cairo_surface_t * _cairo_surface_observer_snapshot (void *abstract_surface) { cairo_surface_observer_t *surface = abstract_surface; /* XXX hook onto the snapshot so that we measure number of reads */ if (surface->target->backend->snapshot) return surface->target->backend->snapshot (surface->target); return NULL; } static cairo_t * _cairo_surface_observer_create_context(void *target) { cairo_surface_observer_t *surface = target; if (_cairo_surface_is_subsurface (&surface->base)) surface = (cairo_surface_observer_t *) _cairo_surface_subsurface_get_target (&surface->base); surface->log.num_contexts++; to_device (surface)->log.num_contexts++; return surface->target->backend->create_context (target); } static const cairo_surface_backend_t _cairo_surface_observer_backend = { CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER, _cairo_surface_observer_finish, _cairo_surface_observer_create_context, _cairo_surface_observer_create_similar, _cairo_surface_observer_create_similar_image, _cairo_surface_observer_map_to_image, _cairo_surface_observer_unmap_image, _cairo_surface_observer_source, _cairo_surface_observer_acquire_source_image, _cairo_surface_observer_release_source_image, _cairo_surface_observer_snapshot, _cairo_surface_observer_copy_page, _cairo_surface_observer_show_page, _cairo_surface_observer_get_extents, _cairo_surface_observer_get_font_options, _cairo_surface_observer_flush, _cairo_surface_observer_mark_dirty, _cairo_surface_observer_paint, _cairo_surface_observer_mask, _cairo_surface_observer_stroke, _cairo_surface_observer_fill, NULL, /* fill-stroke */ _cairo_surface_observer_glyphs, }; /** * cairo_surface_create_observer: * @target: an existing surface for which the observer will watch * * Create a new surface that exists solely to watch another is doing. In * the process it will log operations and times, which are fast, which are * slow, which are frequent, etc. * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * Since: 1.12 **/ cairo_surface_t * cairo_surface_create_observer (cairo_surface_t *target, cairo_surface_observer_mode_t mode) { cairo_device_t *device; cairo_surface_t *surface; cairo_bool_t record; if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS; device = _cairo_device_create_observer_internal (target->device, record); if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); surface = _cairo_surface_create_observer_internal (device, target); cairo_device_destroy (device); return surface; } static cairo_status_t _cairo_surface_observer_add_callback (cairo_list_t *head, cairo_surface_observer_callback_t func, void *data) { struct callback_list *cb; cb = malloc (sizeof (*cb)); if (unlikely (cb == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_list_add (&cb->link, head); cb->func = func; cb->data = data; return CAIRO_STATUS_SUCCESS; } cairo_status_t cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->paint_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->mask_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->fill_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->stroke_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->flush_callbacks, func, data); } cairo_status_t cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return abstract_surface->status; if (! _cairo_surface_is_observer (abstract_surface)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *)abstract_surface; return _cairo_surface_observer_add_callback (&surface->finish_callbacks, func, data); } static void print_extents (cairo_output_stream_t *stream, const struct extents *e) { _cairo_output_stream_printf (stream, " extents: total %g, avg %g [unbounded %d]\n", e->area.sum, e->area.sum / e->area.count, e->unbounded); } static inline int ordercmp (int a, int b, const unsigned int *array) { /* high to low */ return array[b] - array[a]; } CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp) static void print_array (cairo_output_stream_t *stream, const unsigned int *array, const char **names, int count) { int order[64]; int i, j; assert (count < ARRAY_LENGTH (order)); for (i = j = 0; i < count; i++) { if (array[i] != 0) order[j++] = i; } sort_order (order, j, (void *)array); for (i = 0; i < j; i++) _cairo_output_stream_printf (stream, " %d %s%s", array[order[i]], names[order[i]], i < j -1 ? "," : ""); } static const char *operator_names[] = { "CLEAR", /* CAIRO_OPERATOR_CLEAR */ "SOURCE", /* CAIRO_OPERATOR_SOURCE */ "OVER", /* CAIRO_OPERATOR_OVER */ "IN", /* CAIRO_OPERATOR_IN */ "OUT", /* CAIRO_OPERATOR_OUT */ "ATOP", /* CAIRO_OPERATOR_ATOP */ "DEST", /* CAIRO_OPERATOR_DEST */ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ "XOR", /* CAIRO_OPERATOR_XOR */ "ADD", /* CAIRO_OPERATOR_ADD */ "SATURATE", /* CAIRO_OPERATOR_SATURATE */ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ "SCREEN", /* CAIRO_OPERATOR_SCREEN */ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ "DARKEN", /* CAIRO_OPERATOR_DARKEN */ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ }; static void print_operators (cairo_output_stream_t *stream, unsigned int *array) { _cairo_output_stream_printf (stream, " op:"); print_array (stream, array, operator_names, NUM_OPERATORS); _cairo_output_stream_printf (stream, "\n"); } static const char *fill_rule_names[] = { "non-zero", "even-odd", }; static void print_fill_rule (cairo_output_stream_t *stream, unsigned int *array) { _cairo_output_stream_printf (stream, " fill rule:"); print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names)); _cairo_output_stream_printf (stream, "\n"); } static const char *cap_names[] = { "butt", /* CAIRO_LINE_CAP_BUTT */ "round", /* CAIRO_LINE_CAP_ROUND */ "square" /* CAIRO_LINE_CAP_SQUARE */ }; static void print_line_caps (cairo_output_stream_t *stream, unsigned int *array) { _cairo_output_stream_printf (stream, " caps:"); print_array (stream, array, cap_names, NUM_CAPS); _cairo_output_stream_printf (stream, "\n"); } static const char *join_names[] = { "miter", /* CAIRO_LINE_JOIN_MITER */ "round", /* CAIRO_LINE_JOIN_ROUND */ "bevel", /* CAIRO_LINE_JOIN_BEVEL */ }; static void print_line_joins (cairo_output_stream_t *stream, unsigned int *array) { _cairo_output_stream_printf (stream, " joins:"); print_array (stream, array, join_names, NUM_JOINS); _cairo_output_stream_printf (stream, "\n"); } static const char *antialias_names[] = { "default", "none", "gray", "subpixel", "fast", "good", "best" }; static void print_antialias (cairo_output_stream_t *stream, unsigned int *array) { _cairo_output_stream_printf (stream, " antialias:"); print_array (stream, array, antialias_names, NUM_ANTIALIAS); _cairo_output_stream_printf (stream, "\n"); } static const char *pattern_names[] = { "native", "record", "other surface", "solid", "linear", "radial", "mesh", "raster" }; static void print_pattern (cairo_output_stream_t *stream, const char *name, const struct pattern *p) { _cairo_output_stream_printf (stream, " %s:", name); print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names)); _cairo_output_stream_printf (stream, "\n"); } static const char *path_names[] = { "empty", "pixel-aligned", "rectliinear", "straight", "curved", }; static void print_path (cairo_output_stream_t *stream, const struct path *p) { _cairo_output_stream_printf (stream, " path:"); print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names)); _cairo_output_stream_printf (stream, "\n"); } static const char *clip_names[] = { "none", "region", "boxes", "single path", "polygon", "general", }; static void print_clip (cairo_output_stream_t *stream, const struct clip *c) { _cairo_output_stream_printf (stream, " clip:"); print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names)); _cairo_output_stream_printf (stream, "\n"); } static void print_record (cairo_output_stream_t *stream, cairo_observation_record_t *r) { _cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]); _cairo_output_stream_printf (stream, " source: %s\n", pattern_names[r->source]); if (r->mask != -1) _cairo_output_stream_printf (stream, " mask: %s\n", pattern_names[r->mask]); if (r->num_glyphs != -1) _cairo_output_stream_printf (stream, " num_glyphs: %d\n", r->num_glyphs); if (r->path != -1) _cairo_output_stream_printf (stream, " path: %s\n", path_names[r->path]); if (r->fill_rule != -1) _cairo_output_stream_printf (stream, " fill rule: %s\n", fill_rule_names[r->fill_rule]); if (r->antialias != -1) _cairo_output_stream_printf (stream, " antialias: %s\n", antialias_names[r->antialias]); _cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]); _cairo_output_stream_printf (stream, " elapsed: %f ns\n", _cairo_time_to_ns (r->elapsed)); } static double percent (cairo_time_t a, cairo_time_t b) { /* Fake %.1f */ return _cairo_round (_cairo_time_to_s (a) * 1000 / _cairo_time_to_s (b)) / 10; } static cairo_bool_t replay_record (cairo_observation_t *log, cairo_observation_record_t *r, cairo_device_t *script) { #if CAIRO_HAS_SCRIPT_SURFACE cairo_surface_t *surface; cairo_int_status_t status; if (log->record == NULL || script == NULL) return FALSE; surface = cairo_script_surface_create (script, r->target_content, r->target_width, r->target_height); status = _cairo_recording_surface_replay_one (log->record, r->index, surface); cairo_surface_destroy (surface); assert (status == CAIRO_INT_STATUS_SUCCESS); return TRUE; #else return FALSE; #endif } static cairo_time_t _cairo_observation_total_elapsed (cairo_observation_t *log) { cairo_time_t total; total = log->paint.elapsed; total = _cairo_time_add (total, log->mask.elapsed); total = _cairo_time_add (total, log->fill.elapsed); total = _cairo_time_add (total, log->stroke.elapsed); total = _cairo_time_add (total, log->glyphs.elapsed); return total; } static void _cairo_observation_print (cairo_output_stream_t *stream, cairo_observation_t *log) { cairo_device_t *script; cairo_time_t total; #if CAIRO_HAS_SCRIPT_SURFACE script = _cairo_script_context_create_internal (stream); _cairo_script_context_attach_snapshots (script, FALSE); #else script = NULL; #endif total = _cairo_observation_total_elapsed (log); _cairo_output_stream_printf (stream, "elapsed: %f\n", _cairo_time_to_ns (total)); _cairo_output_stream_printf (stream, "surfaces: %d\n", log->num_surfaces); _cairo_output_stream_printf (stream, "contexts: %d\n", log->num_contexts); _cairo_output_stream_printf (stream, "sources acquired: %d\n", log->num_sources_acquired); _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n", log->paint.count, log->paint.noop, _cairo_time_to_ns (log->paint.elapsed), percent (log->paint.elapsed, total)); if (log->paint.count) { print_extents (stream, &log->paint.extents); print_operators (stream, log->paint.operators); print_pattern (stream, "source", &log->paint.source); print_clip (stream, &log->paint.clip); _cairo_output_stream_printf (stream, "slowest paint: %f%%\n", percent (log->paint.slowest.elapsed, log->paint.elapsed)); print_record (stream, &log->paint.slowest); _cairo_output_stream_printf (stream, "\n"); if (replay_record (log, &log->paint.slowest, script)) _cairo_output_stream_printf (stream, "\n\n"); } _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n", log->mask.count, log->mask.noop, _cairo_time_to_ns (log->mask.elapsed), percent (log->mask.elapsed, total)); if (log->mask.count) { print_extents (stream, &log->mask.extents); print_operators (stream, log->mask.operators); print_pattern (stream, "source", &log->mask.source); print_pattern (stream, "mask", &log->mask.mask); print_clip (stream, &log->mask.clip); _cairo_output_stream_printf (stream, "slowest mask: %f%%\n", percent (log->mask.slowest.elapsed, log->mask.elapsed)); print_record (stream, &log->mask.slowest); _cairo_output_stream_printf (stream, "\n"); if (replay_record (log, &log->mask.slowest, script)) _cairo_output_stream_printf (stream, "\n\n"); } _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n", log->fill.count, log->fill.noop, _cairo_time_to_ns (log->fill.elapsed), percent (log->fill.elapsed, total)); if (log->fill.count) { print_extents (stream, &log->fill.extents); print_operators (stream, log->fill.operators); print_pattern (stream, "source", &log->fill.source); print_path (stream, &log->fill.path); print_fill_rule (stream, log->fill.fill_rule); print_antialias (stream, log->fill.antialias); print_clip (stream, &log->fill.clip); _cairo_output_stream_printf (stream, "slowest fill: %f%%\n", percent (log->fill.slowest.elapsed, log->fill.elapsed)); print_record (stream, &log->fill.slowest); _cairo_output_stream_printf (stream, "\n"); if (replay_record (log, &log->fill.slowest, script)) _cairo_output_stream_printf (stream, "\n\n"); } _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n", log->stroke.count, log->stroke.noop, _cairo_time_to_ns (log->stroke.elapsed), percent (log->stroke.elapsed, total)); if (log->stroke.count) { print_extents (stream, &log->stroke.extents); print_operators (stream, log->stroke.operators); print_pattern (stream, "source", &log->stroke.source); print_path (stream, &log->stroke.path); print_antialias (stream, log->stroke.antialias); print_line_caps (stream, log->stroke.caps); print_line_joins (stream, log->stroke.joins); print_clip (stream, &log->stroke.clip); _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n", percent (log->stroke.slowest.elapsed, log->stroke.elapsed)); print_record (stream, &log->stroke.slowest); _cairo_output_stream_printf (stream, "\n"); if (replay_record (log, &log->stroke.slowest, script)) _cairo_output_stream_printf (stream, "\n\n"); } _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n", log->glyphs.count, log->glyphs.noop, _cairo_time_to_ns (log->glyphs.elapsed), percent (log->glyphs.elapsed, total)); if (log->glyphs.count) { print_extents (stream, &log->glyphs.extents); print_operators (stream, log->glyphs.operators); print_pattern (stream, "source", &log->glyphs.source); print_clip (stream, &log->glyphs.clip); _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n", percent (log->glyphs.slowest.elapsed, log->glyphs.elapsed)); print_record (stream, &log->glyphs.slowest); _cairo_output_stream_printf (stream, "\n"); if (replay_record (log, &log->glyphs.slowest, script)) _cairo_output_stream_printf (stream, "\n\n"); } cairo_device_destroy (script); } cairo_status_t cairo_surface_observer_print (cairo_surface_t *abstract_surface, cairo_write_func_t write_func, void *closure) { cairo_output_stream_t *stream; cairo_surface_observer_t *surface; if (unlikely (abstract_surface->status)) return abstract_surface->status; if (unlikely (! _cairo_surface_is_observer (abstract_surface))) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); surface = (cairo_surface_observer_t *) abstract_surface; stream = _cairo_output_stream_create (write_func, NULL, closure); _cairo_observation_print (stream, &surface->log); return _cairo_output_stream_destroy (stream); } double cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface) { cairo_surface_observer_t *surface; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) return -1; if (! _cairo_surface_is_observer (abstract_surface)) return -1; surface = (cairo_surface_observer_t *) abstract_surface; return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log)); } cairo_status_t cairo_device_observer_print (cairo_device_t *abstract_device, cairo_write_func_t write_func, void *closure) { cairo_output_stream_t *stream; cairo_device_observer_t *device; if (unlikely (abstract_device->status)) return abstract_device->status; if (unlikely (! _cairo_device_is_observer (abstract_device))) return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); device = (cairo_device_observer_t *) abstract_device; stream = _cairo_output_stream_create (write_func, NULL, closure); _cairo_observation_print (stream, &device->log); return _cairo_output_stream_destroy (stream); } double cairo_device_observer_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log)); } double cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (device->log.paint.elapsed); } double cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (device->log.mask.elapsed); } double cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (device->log.fill.elapsed); } double cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (device->log.stroke.elapsed); } double cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device) { cairo_device_observer_t *device; if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) return -1; if (! _cairo_device_is_observer (abstract_device)) return -1; device = (cairo_device_observer_t *) abstract_device; return _cairo_time_to_ns (device->log.glyphs.elapsed); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-offset-private.h������������������0000664�0000000�0000000�00000006461�12710376503�0030137�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.u> */ #ifndef CAIRO_SURFACE_OFFSET_PRIVATE_H #define CAIRO_SURFACE_OFFSET_PRIVATE_H #include "cairo-types-private.h" CAIRO_BEGIN_DECLS cairo_private cairo_status_t _cairo_surface_offset_paint (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_mask (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_stroke (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_fill (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t*source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_glyphs (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, const cairo_clip_t *clip); #endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-offset.c��������������������������0000664�0000000�0000000�00000020745�12710376503�0026463�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * Copyright © 2007 Adrian Johnson * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-error-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-offset-private.h" /* A collection of routines to facilitate drawing to an alternate surface. */ static void _copy_transformed_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original, const cairo_matrix_t *ctm_inverse) { _cairo_pattern_init_static_copy (pattern, original); if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); } cairo_status_t _cairo_surface_offset_paint (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; if (unlikely (target->status)) return target->status; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } status = _cairo_surface_paint (target, op, source, dev_clip); if (dev_clip != clip) _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_offset_mask (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; cairo_pattern_union_t mask_copy; if (unlikely (target->status)) return target->status; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); _copy_transformed_pattern (&mask_copy.base, mask, &m); source = &source_copy.base; mask = &mask_copy.base; } status = _cairo_surface_mask (target, op, source, mask, dev_clip); if (dev_clip != clip) _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_offset_stroke (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t*stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_matrix_t dev_ctm = *ctm; cairo_matrix_t dev_ctm_inverse = *ctm_inverse; cairo_pattern_union_t source_copy; cairo_status_t status; if (unlikely (surface->status)) return surface->status; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; _cairo_path_fixed_translate (&path_copy, _cairo_fixed_from_int (-x), _cairo_fixed_from_int (-y)); dev_path = &path_copy; cairo_matrix_init_translate (&m, -x, -y); cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); } status = _cairo_surface_stroke (surface, op, source, dev_path, stroke_style, &dev_ctm, &dev_ctm_inverse, tolerance, antialias, dev_clip); FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); if (dev_clip != clip) _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_offset_fill (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t*source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; if (unlikely (surface->status)) return surface->status; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; _cairo_path_fixed_translate (&path_copy, _cairo_fixed_from_int (-x), _cairo_fixed_from_int (-y)); dev_path = &path_copy; cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } status = _cairo_surface_fill (surface, op, source, dev_path, fill_rule, tolerance, antialias, dev_clip); FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); if (dev_clip != clip) _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_offset_glyphs (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; cairo_glyph_t *dev_glyphs; int i; if (unlikely (surface->status)) return surface->status; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (dev_glyphs == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); if (x | y) { cairo_matrix_t m; dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; for (i = 0; i < num_glyphs; i++) { dev_glyphs[i].x -= x; dev_glyphs[i].y -= y; } } status = _cairo_surface_show_text_glyphs (surface, op, source, NULL, 0, dev_glyphs, num_glyphs, NULL, 0, 0, scaled_font, dev_clip); if (dev_clip != clip) _cairo_clip_destroy (dev_clip); free (dev_glyphs); return status; } ���������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-private.h�������������������������0000664�0000000�0000000�00000007770�12710376503�0026657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_SURFACE_PRIVATE_H #define CAIRO_SURFACE_PRIVATE_H #include "cairo.h" #include "cairo-types-private.h" #include "cairo-list-private.h" #include "cairo-reference-count-private.h" #include "cairo-clip-private.h" #include "cairo-surface-backend-private.h" typedef void (*cairo_surface_func_t) (cairo_surface_t *); struct _cairo_surface { const cairo_surface_backend_t *backend; cairo_device_t *device; /* We allow surfaces to override the backend->type by shoving something * else into surface->type. This is for "wrapper" surfaces that want to * hide their internal type from the user-level API. */ cairo_surface_type_t type; cairo_content_t content; cairo_reference_count_t ref_count; cairo_status_t status; unsigned int unique_id; unsigned int serial; cairo_damage_t *damage; unsigned _finishing : 1; unsigned finished : 1; unsigned is_clear : 1; unsigned has_font_options : 1; unsigned owns_device : 1; cairo_user_data_array_t user_data; cairo_user_data_array_t mime_data; cairo_matrix_t device_transform; cairo_matrix_t device_transform_inverse; cairo_list_t device_transform_observers; /* The actual resolution of the device, in dots per inch. */ double x_resolution; double y_resolution; /* The resolution that should be used when generating image-based * fallback; generally only used by the analysis/paginated * surfaces */ double x_fallback_resolution; double y_fallback_resolution; /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ cairo_surface_t *snapshot_of; cairo_surface_func_t snapshot_detach; /* current snapshots of this surface*/ cairo_list_t snapshots; /* place upon snapshot list */ cairo_list_t snapshot; /* * Surface font options, falling back to backend's default options, * and set using _cairo_surface_set_font_options(), and propagated by * cairo_surface_create_similar(). */ cairo_font_options_t font_options; }; cairo_private cairo_surface_t * _cairo_surface_create_in_error (cairo_status_t status); cairo_private cairo_surface_t * _cairo_int_surface_create_in_error (cairo_int_status_t status); cairo_private cairo_surface_t * _cairo_surface_get_source (cairo_surface_t *surface, cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_surface_flush (cairo_surface_t *surface, unsigned flags); #endif /* CAIRO_SURFACE_PRIVATE_H */ ��������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-snapshot-inline.h�����������������0000664�0000000�0000000�00000004601�12710376503�0030306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_SNAPSHOT_INLINE_H #define CAIRO_SURFACE_SNAPSHOT_INLINE_H #include "cairo-surface-snapshot-private.h" #include "cairo-surface-inline.h" static inline cairo_bool_t _cairo_surface_snapshot_is_reused (cairo_surface_t *surface) { return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2; } static inline cairo_surface_t * _cairo_surface_snapshot_get_target (cairo_surface_t *surface) { cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; cairo_surface_t *target; CAIRO_MUTEX_LOCK (snapshot->mutex); target = _cairo_surface_reference (snapshot->target); CAIRO_MUTEX_UNLOCK (snapshot->mutex); return target; } static inline cairo_bool_t _cairo_surface_is_snapshot (cairo_surface_t *surface) { return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT; } #endif /* CAIRO_SURFACE_SNAPSHOT_INLINE_H */ �������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-snapshot-private.h����������������0000664�0000000�0000000�00000003555�12710376503�0030511�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H #define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H #include "cairo-mutex-private.h" #include "cairo-surface-private.h" #include "cairo-surface-backend-private.h" struct _cairo_surface_snapshot { cairo_surface_t base; cairo_mutex_t mutex; cairo_surface_t *target; cairo_surface_t *clone; }; #endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-snapshot.c������������������������0000664�0000000�0000000�00000022342�12710376503�0027027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-snapshot-inline.h" static cairo_status_t _cairo_surface_snapshot_finish (void *abstract_surface) { cairo_surface_snapshot_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; TRACE ((stderr, "%s\n", __FUNCTION__)); if (surface->clone != NULL) { cairo_surface_finish (surface->clone); status = surface->clone->status; cairo_surface_destroy (surface->clone); } CAIRO_MUTEX_FINI (surface->mutex); return status; } static cairo_status_t _cairo_surface_snapshot_flush (void *abstract_surface, unsigned flags) { cairo_surface_snapshot_t *surface = abstract_surface; cairo_surface_t *target; cairo_status_t status; target = _cairo_surface_snapshot_get_target (&surface->base); status = _cairo_surface_flush (target, flags); cairo_surface_destroy (target); return status; } static cairo_surface_t * _cairo_surface_snapshot_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_snapshot_t *surface = abstract_surface; return _cairo_surface_get_source (surface->target, extents); /* XXX racy */ } struct snapshot_extra { cairo_surface_t *target; void *extra; }; static cairo_status_t _cairo_surface_snapshot_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **extra_out) { cairo_surface_snapshot_t *surface = abstract_surface; struct snapshot_extra *extra; cairo_status_t status; extra = malloc (sizeof (*extra)); if (unlikely (extra == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); extra->target = _cairo_surface_snapshot_get_target (&surface->base); status = _cairo_surface_acquire_source_image (extra->target, image_out, &extra->extra); if (unlikely (status)) { cairo_surface_destroy (extra->target); free (extra); } *extra_out = extra; return status; } static void _cairo_surface_snapshot_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *_extra) { struct snapshot_extra *extra = _extra; _cairo_surface_release_source_image (extra->target, image, extra->extra); cairo_surface_destroy (extra->target); free (extra); } static cairo_bool_t _cairo_surface_snapshot_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_snapshot_t *surface = abstract_surface; cairo_surface_t *target; cairo_bool_t bounded; target = _cairo_surface_snapshot_get_target (&surface->base); bounded = _cairo_surface_get_extents (target, extents); cairo_surface_destroy (target); return bounded; } static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT, _cairo_surface_snapshot_finish, NULL, NULL, /* create similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_snapshot_source, _cairo_surface_snapshot_acquire_source_image, _cairo_surface_snapshot_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_surface_snapshot_get_extents, NULL, /* get-font-options */ _cairo_surface_snapshot_flush, }; static void _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) { cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; cairo_image_surface_t *image; cairo_surface_t *clone; void *extra; cairo_status_t status; TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, snapshot->target->unique_id)); /* We need to make an image copy of the original surface since the * snapshot may exceed the lifetime of the original device, i.e. * when we later need to use the snapshot the data may have already * been lost. */ CAIRO_MUTEX_LOCK (snapshot->mutex); if (snapshot->target->backend->snapshot != NULL) { clone = snapshot->target->backend->snapshot (snapshot->target); if (clone != NULL) { assert (clone->status || ! _cairo_surface_is_snapshot (clone)); goto done; } } /* XXX copy to a similar surface, leave acquisition till later? * We should probably leave such decisions to the backend in case we * rely upon devices/connections like Xlib. */ status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra); if (unlikely (status)) { snapshot->target = _cairo_surface_create_in_error (status); status = _cairo_surface_set_error (surface, status); goto unlock; } clone = image->base.backend->snapshot (&image->base); _cairo_surface_release_source_image (snapshot->target, image, extra); done: status = _cairo_surface_set_error (surface, clone->status); snapshot->target = snapshot->clone = clone; snapshot->base.type = clone->type; unlock: CAIRO_MUTEX_UNLOCK (snapshot->mutex); } /** * _cairo_surface_snapshot: * @surface: a #cairo_surface_t * * Make an immutable reference to @surface. It is an error to call a * surface-modifying function on the result of this function. The * resulting 'snapshot' is a lazily copied-on-write surface i.e. it * remains a reference to the original surface until that surface is * written to again, at which time a copy is made of the original surface * and the snapshot then points to that instead. Multiple snapshots of the * same unmodified surface point to the same copy. * * The caller owns the return value and should call * cairo_surface_destroy() when finished with it. This function will not * return %NULL, but will return a nil surface instead. * * Return value: The snapshot surface. Note that the return surface * may not necessarily be of the same type as @surface. **/ cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface) { cairo_surface_snapshot_t *snapshot; cairo_status_t status; TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->unique_id)); if (unlikely (surface->status)) return _cairo_surface_create_in_error (surface->status); if (unlikely (surface->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (surface->snapshot_of != NULL) return cairo_surface_reference (surface); if (_cairo_surface_is_snapshot (surface)) return cairo_surface_reference (surface); snapshot = (cairo_surface_snapshot_t *) _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend); if (snapshot != NULL) return cairo_surface_reference (&snapshot->base); snapshot = malloc (sizeof (cairo_surface_snapshot_t)); if (unlikely (snapshot == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); _cairo_surface_init (&snapshot->base, &_cairo_surface_snapshot_backend, NULL, /* device */ surface->content); snapshot->base.type = surface->type; CAIRO_MUTEX_INIT (snapshot->mutex); snapshot->target = surface; snapshot->clone = NULL; status = _cairo_surface_copy_mime_data (&snapshot->base, surface); if (unlikely (status)) { cairo_surface_destroy (&snapshot->base); return _cairo_surface_create_in_error (status); } snapshot->base.device_transform = surface->device_transform; snapshot->base.device_transform_inverse = surface->device_transform_inverse; _cairo_surface_attach_snapshot (surface, &snapshot->base, _cairo_surface_snapshot_copy_on_write); return &snapshot->base; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-subsurface-inline.h���������������0000664�0000000�0000000�00000004743�12710376503�0030620�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_SUBSURFACE_INLINE_H #define CAIRO_SURFACE_SUBSURFACE_INLINE_H #include "cairo-surface-subsurface-private.h" static inline cairo_surface_t * _cairo_surface_subsurface_get_target (cairo_surface_t *surface) { return ((cairo_surface_subsurface_t *) surface)->target; } static inline void _cairo_surface_subsurface_offset (cairo_surface_t *surface, int *x, int *y) { cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; *x += ss->extents.x; *y += ss->extents.y; } static inline cairo_surface_t * _cairo_surface_subsurface_get_target_with_offset (cairo_surface_t *surface, int *x, int *y) { cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; *x += ss->extents.x; *y += ss->extents.y; return ss->target; } static inline cairo_bool_t _cairo_surface_is_subsurface (cairo_surface_t *surface) { return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE; } #endif /* CAIRO_SURFACE_SUBSURFACE_INLINE_H */ �����������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-subsurface-private.h��������������0000664�0000000�0000000�00000003731�12710376503�0031010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H #define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H #include "cairo-surface-private.h" #include "cairo-surface-backend-private.h" struct _cairo_surface_subsurface { cairo_surface_t base; cairo_rectangle_int_t extents; cairo_surface_t *target; cairo_surface_t *snapshot; }; cairo_private void _cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface, cairo_surface_t *snapshot); #endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */ ���������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-subsurface.c����������������������0000664�0000000�0000000�00000044734�12710376503�0027343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Intel Corporation. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" static const cairo_surface_backend_t _cairo_surface_subsurface_backend; static cairo_status_t _cairo_surface_subsurface_finish (void *abstract_surface) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_surface_destroy (surface->target); cairo_surface_destroy (surface->snapshot); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_surface_subsurface_create_similar (void *other, cairo_content_t content, int width, int height) { cairo_surface_subsurface_t *surface = other; if (surface->target->backend->create_similar == NULL) return NULL; return surface->target->backend->create_similar (surface->target, content, width, height); } static cairo_surface_t * _cairo_surface_subsurface_create_similar_image (void *other, cairo_format_t format, int width, int height) { cairo_surface_subsurface_t *surface = other; if (surface->target->backend->create_similar_image == NULL) return NULL; return surface->target->backend->create_similar_image (surface->target, format, width, height); } static cairo_image_surface_t * _cairo_surface_subsurface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t target_extents; target_extents.x = extents->x + surface->extents.x; target_extents.y = extents->y + surface->extents.y; target_extents.width = extents->width; target_extents.height = extents->height; return _cairo_surface_map_to_image (surface->target, &target_extents); } static cairo_int_status_t _cairo_surface_subsurface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_surface_subsurface_t *surface = abstract_surface; return _cairo_surface_unmap_image (surface->target, image); } static cairo_int_status_t _cairo_surface_subsurface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; cairo_clip_t *target_clip; target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_paint (surface->target, -surface->extents.x, -surface->extents.y, op, source, target_clip); _cairo_clip_destroy (target_clip); return status; } static cairo_int_status_t _cairo_surface_subsurface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; cairo_clip_t *target_clip; target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_mask (surface->target, -surface->extents.x, -surface->extents.y, op, source, mask, target_clip); _cairo_clip_destroy (target_clip); return status; } static cairo_int_status_t _cairo_surface_subsurface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; cairo_clip_t *target_clip; target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_fill (surface->target, -surface->extents.x, -surface->extents.y, op, source, path, fill_rule, tolerance, antialias, target_clip); _cairo_clip_destroy (target_clip); return status; } static cairo_int_status_t _cairo_surface_subsurface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; cairo_clip_t *target_clip; target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_stroke (surface->target, -surface->extents.x, -surface->extents.y, op, source, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, target_clip); _cairo_clip_destroy (target_clip); return status; } static cairo_int_status_t _cairo_surface_subsurface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; cairo_clip_t *target_clip; target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_glyphs (surface->target, -surface->extents.x, -surface->extents.y, op, source, scaled_font, glyphs, num_glyphs, target_clip); _cairo_clip_destroy (target_clip); return status; } static cairo_status_t _cairo_surface_subsurface_flush (void *abstract_surface, unsigned flags) { cairo_surface_subsurface_t *surface = abstract_surface; return _cairo_surface_flush (surface->target, flags); } static cairo_status_t _cairo_surface_subsurface_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_status_t status; status = CAIRO_STATUS_SUCCESS; if (surface->target->backend->mark_dirty_rectangle != NULL) { cairo_rectangle_int_t rect, extents; rect.x = x; rect.y = y; rect.width = width; rect.height = height; extents.x = extents.y = 0; extents.width = surface->extents.width; extents.height = surface->extents.height; if (_cairo_rectangle_intersect (&rect, &extents)) { status = surface->target->backend->mark_dirty_rectangle (surface->target, rect.x + surface->extents.x, rect.y + surface->extents.y, rect.width, rect.height); } } return status; } static cairo_bool_t _cairo_surface_subsurface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_subsurface_t *surface = abstract_surface; extents->x = 0; extents->y = 0; extents->width = surface->extents.width; extents->height = surface->extents.height; return TRUE; } static void _cairo_surface_subsurface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_surface_subsurface_t *surface = abstract_surface; if (surface->target->backend->get_font_options != NULL) surface->target->backend->get_font_options (surface->target, options); } static cairo_surface_t * _cairo_surface_subsurface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_surface_t *source; source = _cairo_surface_get_source (surface->target, extents); if (extents) *extents = surface->extents; return source; } static cairo_status_t _cairo_surface_subsurface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **extra_out) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_surface_pattern_t pattern; cairo_surface_t *image; cairo_status_t status; image = _cairo_image_surface_create_with_content (surface->base.content, surface->extents.width, surface->extents.height); if (unlikely (image->status)) return image->status; _cairo_pattern_init_for_surface (&pattern, surface->target); cairo_matrix_init_translate (&pattern.base.matrix, surface->extents.x, surface->extents.y); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (image, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (image); return status; } *image_out = (cairo_image_surface_t *)image; *extra_out = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_surface_subsurface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *abstract_extra) { cairo_surface_destroy (&image->base); } static cairo_surface_t * _cairo_surface_subsurface_snapshot (void *abstract_surface) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_surface_pattern_t pattern; cairo_surface_t *clone; cairo_status_t status; TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->target->unique_id)); clone = _cairo_surface_create_similar_scratch (surface->target, surface->target->content, surface->extents.width, surface->extents.height); if (unlikely (clone->status)) return clone; _cairo_pattern_init_for_surface (&pattern, surface->target); cairo_matrix_init_translate (&pattern.base.matrix, surface->extents.x, surface->extents.y); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (clone, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (clone); clone = _cairo_surface_create_in_error (status); } return clone; } static cairo_t * _cairo_surface_subsurface_create_context(void *target) { cairo_surface_subsurface_t *surface = target; return surface->target->backend->create_context (&surface->base); } static const cairo_surface_backend_t _cairo_surface_subsurface_backend = { CAIRO_SURFACE_TYPE_SUBSURFACE, _cairo_surface_subsurface_finish, _cairo_surface_subsurface_create_context, _cairo_surface_subsurface_create_similar, _cairo_surface_subsurface_create_similar_image, _cairo_surface_subsurface_map_to_image, _cairo_surface_subsurface_unmap_image, _cairo_surface_subsurface_source, _cairo_surface_subsurface_acquire_source_image, _cairo_surface_subsurface_release_source_image, _cairo_surface_subsurface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_surface_subsurface_get_extents, _cairo_surface_subsurface_get_font_options, _cairo_surface_subsurface_flush, _cairo_surface_subsurface_mark_dirty, _cairo_surface_subsurface_paint, _cairo_surface_subsurface_mask, _cairo_surface_subsurface_stroke, _cairo_surface_subsurface_fill, NULL, /* fill/stroke */ _cairo_surface_subsurface_glyphs, }; /** * cairo_surface_create_for_rectangle: * @target: an existing surface for which the sub-surface will point to * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units) * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units) * @width: width of the sub-surface (in device-space units) * @height: height of the sub-surface (in device-space units) * * Create a new surface that is a rectangle within the target surface. * All operations drawn to this surface are then clipped and translated * onto the target surface. Nothing drawn via this sub-surface outside of * its bounds is drawn onto the target surface, making this a useful method * for passing constrained child surfaces to library routines that draw * directly onto the parent surface, i.e. with no further backend allocations, * double buffering or copies. * * <note><para>The semantics of subsurfaces have not been finalized yet * unless the rectangle is in full device units, is contained within * the extents of the target surface, and the target or subsurface's * device transforms are not changed.</para></note> * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * Since: 1.10 **/ cairo_surface_t * cairo_surface_create_for_rectangle (cairo_surface_t *target, double x, double y, double width, double height) { cairo_surface_subsurface_t *surface; if (unlikely (width < 0 || height < 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); surface = malloc (sizeof (cairo_surface_subsurface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); assert (_cairo_matrix_is_translation (&target->device_transform)); x += target->device_transform.x0; y += target->device_transform.y0; _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ target->content); /* XXX forced integer alignment */ surface->extents.x = ceil (x); surface->extents.y = ceil (y); surface->extents.width = floor (x + width) - surface->extents.x; surface->extents.height = floor (y + height) - surface->extents.y; if ((surface->extents.width | surface->extents.height) < 0) surface->extents.width = surface->extents.height = 0; if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { /* Maintain subsurfaces as 1-depth */ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target; surface->extents.x += sub->extents.x; surface->extents.y += sub->extents.y; target = sub->target; } surface->target = cairo_surface_reference (target); surface->base.type = surface->target->type; surface->snapshot = NULL; return &surface->base; } cairo_surface_t * _cairo_surface_create_for_rectangle_int (cairo_surface_t *target, const cairo_rectangle_int_t *extents) { cairo_surface_subsurface_t *surface; if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE); surface = malloc (sizeof (cairo_surface_subsurface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); assert (_cairo_matrix_is_translation (&target->device_transform)); _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ target->content); surface->extents = *extents; surface->extents.x += target->device_transform.x0; surface->extents.y += target->device_transform.y0; surface->target = cairo_surface_reference (target); surface->base.type = surface->target->type; surface->snapshot = NULL; return &surface->base; } /* XXX observe mark-dirty */ static void _cairo_surface_subsurface_detach_snapshot (cairo_surface_t *surface) { cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, ss->target->unique_id)); cairo_surface_destroy (ss->snapshot); ss->snapshot = NULL; } void _cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface, cairo_surface_t *snapshot) { cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; TRACE ((stderr, "%s: target=%d, snapshot=%d\n", __FUNCTION__, ss->target->unique_id, snapshot->unique_id)); /* FIXME: attaching the subsurface as a snapshot to its target creates * a reference cycle. Let's make this call as a no-op until that bug * is fixed. */ return; if (ss->snapshot) _cairo_surface_detach_snapshot (ss->snapshot); ss->snapshot = cairo_surface_reference (snapshot); _cairo_surface_attach_snapshot (ss->target, &ss->base, _cairo_surface_subsurface_detach_snapshot); } ������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-wrapper-private.h�����������������0000664�0000000�0000000�00000015064�12710376503�0030330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.u> */ #ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H #define CAIRO_SURFACE_WRAPPER_PRIVATE_H #include "cairoint.h" #include "cairo-types-private.h" #include "cairo-surface-backend-private.h" CAIRO_BEGIN_DECLS struct _cairo_surface_wrapper { cairo_surface_t *target; cairo_matrix_t transform; cairo_bool_t has_extents; cairo_rectangle_int_t extents; const cairo_clip_t *clip; cairo_bool_t needs_transform; }; cairo_private void _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, cairo_surface_t *target); cairo_private void _cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper, const cairo_rectangle_int_t *extents); cairo_private void _cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper, const cairo_matrix_t *transform); cairo_private void _cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, const cairo_clip_t *clip); cairo_private void _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); static inline cairo_bool_t _cairo_surface_wrapper_has_fill_stroke (cairo_surface_wrapper_t *wrapper) { return wrapper->target->backend->fill_stroke != NULL; } cairo_private cairo_status_t _cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t **image_out, void **image_extra); cairo_private void _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t *image, void *image_extra); cairo_private cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, cairo_content_t content, int width, int height); cairo_private cairo_bool_t _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, cairo_rectangle_int_t *extents); cairo_private void _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, cairo_font_options_t *options); cairo_private cairo_surface_t * _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper); cairo_private cairo_bool_t _cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper); static inline cairo_bool_t _cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) { return wrapper->target != (cairo_surface_t *) 0; } cairo_private cairo_bool_t _cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, cairo_rectangle_int_t *extents); CAIRO_END_DECLS #endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface-wrapper.c�������������������������0000664�0000000�0000000�00000047617�12710376503�0026664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * Copyright © 2007 Adrian Johnson * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" #include "cairo-clip-inline.h" #include "cairo-error-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-wrapper-private.h" /* A collection of routines to facilitate surface wrapping */ static void _copy_transformed_pattern (cairo_pattern_t *pattern, const cairo_pattern_t *original, const cairo_matrix_t *ctm_inverse) { _cairo_pattern_init_static_copy (pattern, original); if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); } cairo_status_t _cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t **image_out, void **image_extra) { if (unlikely (wrapper->target->status)) return wrapper->target->status; return _cairo_surface_acquire_source_image (wrapper->target, image_out, image_extra); } void _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t *image, void *image_extra) { _cairo_surface_release_source_image (wrapper->target, image, image_extra); } static void _cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *m) { cairo_matrix_init_identity (m); if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y)) cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y); if (! _cairo_matrix_is_identity (&wrapper->transform)) cairo_matrix_multiply (m, &wrapper->transform, m); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) cairo_matrix_multiply (m, &wrapper->target->device_transform, m); } static void _cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper, cairo_matrix_t *m) { cairo_matrix_init_identity (m); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse)) cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m); if (! _cairo_matrix_is_identity (&wrapper->transform)) { cairo_matrix_t inv; cairo_status_t status; inv = wrapper->transform; status = cairo_matrix_invert (&inv); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (m, &inv, m); } if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y)) cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y); } static cairo_clip_t * _cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper, const cairo_clip_t *clip) { cairo_clip_t *copy; copy = _cairo_clip_copy (clip); if (wrapper->has_extents) { copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents); } copy = _cairo_clip_transform (copy, &wrapper->transform); if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) copy = _cairo_clip_transform (copy, &wrapper->target->device_transform); if (wrapper->clip) copy = _cairo_clip_intersect_clip (copy, wrapper->clip); return copy; } cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; cairo_pattern_union_t source_copy; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (wrapper->needs_transform) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } status = _cairo_surface_paint (wrapper->target, op, source, dev_clip); _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; cairo_pattern_union_t source_copy; cairo_pattern_union_t mask_copy; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (wrapper->needs_transform) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; _copy_transformed_pattern (&mask_copy.base, mask, &m); mask = &mask_copy.base; } status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip); _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; cairo_clip_t *dev_clip; cairo_matrix_t dev_ctm = *ctm; cairo_matrix_t dev_ctm_inverse = *ctm_inverse; cairo_pattern_union_t source_copy; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (wrapper->needs_transform) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } status = _cairo_surface_stroke (wrapper->target, op, source, dev_path, stroke_style, &dev_ctm, &dev_ctm_inverse, tolerance, antialias, dev_clip); FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path; cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; cairo_clip_t *dev_clip; cairo_pattern_union_t stroke_source_copy; cairo_pattern_union_t fill_source_copy; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (wrapper->needs_transform) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m); stroke_source = &stroke_source_copy.base; _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); fill_source = &fill_source_copy.base; } status = _cairo_surface_fill_stroke (wrapper->target, fill_op, fill_source, fill_rule, fill_tolerance, fill_antialias, dev_path, stroke_op, stroke_source, stroke_style, &dev_ctm, &dev_ctm_inverse, stroke_tolerance, stroke_antialias, dev_clip); FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_status_t status; cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; cairo_pattern_union_t source_copy; cairo_clip_t *dev_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; if (wrapper->needs_transform) { cairo_matrix_t m; _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } status = _cairo_surface_fill (wrapper->target, op, source, dev_path, fill_rule, tolerance, antialias, dev_clip); FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); _cairo_clip_destroy (dev_clip); return status; } cairo_status_t _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_status_t status; cairo_clip_t *dev_clip; cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)]; cairo_glyph_t *dev_glyphs = stack_glyphs; cairo_scaled_font_t *dev_scaled_font = scaled_font; cairo_pattern_union_t source_copy; cairo_font_options_t options; if (unlikely (wrapper->target->status)) return wrapper->target->status; dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); if (_cairo_clip_is_all_clipped (dev_clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; cairo_surface_get_font_options (wrapper->target, &options); cairo_font_options_merge (&options, &scaled_font->options); if (wrapper->needs_transform) { cairo_matrix_t m; int i; _cairo_surface_wrapper_get_transform (wrapper, &m); if (! _cairo_matrix_is_translation (&wrapper->transform)) { cairo_matrix_t ctm; /* XXX No device-transform? A bug in the tangle of layers? */ _cairo_matrix_multiply (&ctm, &wrapper->transform, &scaled_font->ctm); dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &ctm, &options); } if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } for (i = 0; i < num_glyphs; i++) { dev_glyphs[i] = glyphs[i]; cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); } status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } else { if (! cairo_font_options_equal (&options, &scaled_font->options)) { dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &scaled_font->ctm, &options); } /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed * to modify the glyph array that's passed in. We must always * copy the array before handing it to the backend. */ if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (dev_glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FINISH; } } memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, utf8, utf8_len, dev_glyphs, num_glyphs, clusters, num_clusters, cluster_flags, dev_scaled_font, dev_clip); FINISH: _cairo_clip_destroy (dev_clip); if (dev_glyphs != stack_glyphs) free (dev_glyphs); if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); return status; } cairo_surface_t * _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, cairo_content_t content, int width, int height) { return _cairo_surface_create_similar_scratch (wrapper->target, content, width, height); } cairo_bool_t _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, cairo_rectangle_int_t *extents) { if (wrapper->has_extents) { if (_cairo_surface_get_extents (wrapper->target, extents)) _cairo_rectangle_intersect (extents, &wrapper->extents); else *extents = wrapper->extents; return TRUE; } else { return _cairo_surface_get_extents (wrapper->target, extents); } } static cairo_bool_t _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) { return (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) || ! _cairo_matrix_is_identity (&wrapper->transform) || ! _cairo_matrix_is_identity (&wrapper->target->device_transform); } void _cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper, const cairo_rectangle_int_t *extents) { if (! wrapper->has_extents) { wrapper->extents = *extents; wrapper->has_extents = TRUE; } else _cairo_rectangle_intersect (&wrapper->extents, extents); wrapper->needs_transform = _cairo_surface_wrapper_needs_device_transform (wrapper); } void _cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper, const cairo_matrix_t *transform) { cairo_status_t status; if (transform == NULL || _cairo_matrix_is_identity (transform)) { cairo_matrix_init_identity (&wrapper->transform); wrapper->needs_transform = _cairo_surface_wrapper_needs_device_transform (wrapper); } else { wrapper->transform = *transform; status = cairo_matrix_invert (&wrapper->transform); /* should always be invertible unless given pathological input */ assert (status == CAIRO_STATUS_SUCCESS); wrapper->needs_transform = TRUE; } } void _cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, const cairo_clip_t *clip) { wrapper->clip = clip; } void _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, cairo_font_options_t *options) { cairo_surface_get_font_options (wrapper->target, options); } cairo_surface_t * _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) { if (wrapper->target->backend->snapshot) return wrapper->target->backend->snapshot (wrapper->target); return NULL; } cairo_bool_t _cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper) { return cairo_surface_has_show_text_glyphs (wrapper->target); } void _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, cairo_surface_t *target) { wrapper->target = cairo_surface_reference (target); cairo_matrix_init_identity (&wrapper->transform); wrapper->has_extents = FALSE; wrapper->extents.x = wrapper->extents.y = 0; wrapper->clip = NULL; wrapper->needs_transform = FALSE; if (target) { wrapper->needs_transform = ! _cairo_matrix_is_identity (&target->device_transform); } } void _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) { cairo_surface_destroy (wrapper->target); } cairo_bool_t _cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, cairo_rectangle_int_t *extents) { cairo_rectangle_int_t clip; cairo_bool_t has_clip; has_clip = _cairo_surface_get_extents (wrapper->target, &clip); if (wrapper->clip) { if (has_clip) { if (! _cairo_rectangle_intersect (&clip, _cairo_clip_get_extents (wrapper->clip))) return FALSE; } else { has_clip = TRUE; clip = *_cairo_clip_get_extents (wrapper->clip); } } if (has_clip && wrapper->needs_transform) { cairo_matrix_t m; double x1, y1, x2, y2; _cairo_surface_wrapper_get_inverse_transform (wrapper, &m); x1 = clip.x; y1 = clip.y; x2 = clip.x + clip.width; y2 = clip.y + clip.height; _cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL); clip.x = floor (x1); clip.y = floor (y1); clip.width = ceil (x2) - clip.x; clip.height = ceil (y2) - clip.y; } if (has_clip) { if (wrapper->has_extents) { *extents = wrapper->extents; return _cairo_rectangle_intersect (extents, &clip); } else { *extents = clip; return TRUE; } } else if (wrapper->has_extents) { *extents = wrapper->extents; return TRUE; } else { _cairo_unbounded_rectangle_init (extents); return TRUE; } } �����������������������������������������������������������������������������������������������������������������Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-surface.c���������������������������������0000664�0000000�0000000�00000241312�12710376503�0025172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-damage-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-image-surface-inline.h" #include "cairo-recording-surface-private.h" #include "cairo-region-private.h" #include "cairo-surface-inline.h" #include "cairo-tee-surface-private.h" /** * SECTION:cairo-surface * @Title: cairo_surface_t * @Short_Description: Base class for surfaces * @See_Also: #cairo_t, #cairo_pattern_t * * #cairo_surface_t is the abstract type representing all different drawing * targets that cairo can render to. The actual drawings are * performed using a cairo <firstterm>context</firstterm>. * * A cairo surface is created by using <firstterm>backend</firstterm>-specific * constructors, typically of the form * <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>. * * Most surface types allow accessing the surface without using Cairo * functions. If you do this, keep in mind that it is mandatory that you call * cairo_surface_flush() before reading from or writing to the surface and that * you must use cairo_surface_mark_dirty() after modifying it. * <example> * <title>Directly modifying an image surface * * void * modify_image_surface (cairo_surface_t *surface) * { * unsigned char *data; * int width, height, stride; * * // flush to ensure all writing to the image was done * cairo_surface_flush (surface); * * // modify the image * data = cairo_image_surface_get_data (surface); * width = cairo_image_surface_get_width (surface); * height = cairo_image_surface_get_height (surface); * stride = cairo_image_surface_get_stride (surface); * modify_image_data (data, width, height, stride); * * // mark the image dirty so Cairo clears its caches. * cairo_surface_mark_dirty (surface); * } * * * Note that for other surface types it might be necessary to acquire the * surface's device first. See cairo_device_acquire() for a discussion of * devices. **/ #define DEFINE_NIL_SURFACE(status, name) \ const cairo_surface_t name = { \ NULL, /* backend */ \ NULL, /* device */ \ CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ CAIRO_CONTENT_COLOR, /* content */ \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ 0, /* unique id */ \ 0, /* serial */ \ NULL, /* damage */ \ FALSE, /* _finishing */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ FALSE, /* has_font_options */ \ FALSE, /* owns_device */ \ { 0, 0, 0, NULL, }, /* user_data */ \ { 0, 0, 0, NULL, }, /* mime_data */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ { NULL, NULL }, /* device_transform_observers */ \ 0.0, /* x_resolution */ \ 0.0, /* y_resolution */ \ 0.0, /* x_fallback_resolution */ \ 0.0, /* y_fallback_resolution */ \ NULL, /* snapshot_of */ \ NULL, /* snapshot_detach */ \ { NULL, NULL }, /* snapshots */ \ { NULL, NULL }, /* snapshot */ \ { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \ CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \ CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \ } /* font_options */ \ } /* XXX error object! */ static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil); static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual); static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found); static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error); static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error); static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch); static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error); static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported); static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do); static void _cairo_surface_finish_snapshots (cairo_surface_t *surface); static void _cairo_surface_finish (cairo_surface_t *surface); /** * _cairo_surface_set_error: * @surface: a surface * @status: a status value indicating an error * * Atomically sets surface->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal * status values. * * All assignments of an error status to surface->status should happen * through _cairo_surface_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the * nil objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. * * Return value: the error status. **/ cairo_int_status_t _cairo_surface_set_error (cairo_surface_t *surface, cairo_int_status_t status) { /* NOTHING_TO_DO is magic. We use it to break out of the inner-most * surface function, but anything higher just sees "success". */ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) status = CAIRO_INT_STATUS_SUCCESS; if (status == CAIRO_INT_STATUS_SUCCESS || status >= (int)CAIRO_INT_STATUS_LAST_STATUS) return status; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (&surface->status, (cairo_status_t)status); return _cairo_error (status); } /** * cairo_surface_get_type: * @surface: a #cairo_surface_t * * This function returns the type of the backend used to create * a surface. See #cairo_surface_type_t for available types. * * Return value: The type of @surface. * * Since: 1.2 **/ cairo_surface_type_t cairo_surface_get_type (cairo_surface_t *surface) { /* We don't use surface->backend->type here so that some of the * special "wrapper" surfaces such as cairo_paginated_surface_t * can override surface->type with the type of the "child" * surface. */ return surface->type; } /** * cairo_surface_get_content: * @surface: a #cairo_surface_t * * This function returns the content type of @surface which indicates * whether the surface contains color and/or alpha information. See * #cairo_content_t. * * Return value: The content type of @surface. * * Since: 1.2 **/ cairo_content_t cairo_surface_get_content (cairo_surface_t *surface) { return surface->content; } /** * cairo_surface_status: * @surface: a #cairo_surface_t * * Checks whether an error has previously occurred for this * surface. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER, * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR, * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or * %CAIRO_STATUS_INVALID_VISUAL. * * Since: 1.0 **/ cairo_status_t cairo_surface_status (cairo_surface_t *surface) { return surface->status; } slim_hidden_def (cairo_surface_status); static unsigned int _cairo_surface_allocate_unique_id (void) { static cairo_atomic_int_t unique_id; #if CAIRO_NO_MUTEX if (++unique_id == 0) unique_id = 1; return unique_id; #else cairo_atomic_int_t old, id; do { old = _cairo_atomic_uint_get (&unique_id); id = old + 1; if (id == 0) id = 1; } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id)); return id; #endif } /** * cairo_surface_get_device: * @surface: a #cairo_surface_t * * This function returns the device for a @surface. * See #cairo_device_t. * * Return value: The device for @surface or %NULL if the surface does * not have an associated device. * * Since: 1.10 **/ cairo_device_t * cairo_surface_get_device (cairo_surface_t *surface) { if (unlikely (surface->status)) return _cairo_device_create_in_error (surface->status); return surface->device; } static cairo_bool_t _cairo_surface_has_snapshots (cairo_surface_t *surface) { return ! cairo_list_is_empty (&surface->snapshots); } static cairo_bool_t _cairo_surface_has_mime_data (cairo_surface_t *surface) { return surface->mime_data.num_elements != 0; } static void _cairo_surface_detach_mime_data (cairo_surface_t *surface) { if (! _cairo_surface_has_mime_data (surface)) return; _cairo_user_data_array_fini (&surface->mime_data); _cairo_user_data_array_init (&surface->mime_data); } static void _cairo_surface_detach_snapshots (cairo_surface_t *surface) { while (_cairo_surface_has_snapshots (surface)) { _cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots, cairo_surface_t, snapshot)); } } void _cairo_surface_detach_snapshot (cairo_surface_t *snapshot) { assert (snapshot->snapshot_of != NULL); snapshot->snapshot_of = NULL; cairo_list_del (&snapshot->snapshot); if (snapshot->snapshot_detach != NULL) snapshot->snapshot_detach (snapshot); cairo_surface_destroy (snapshot); } void _cairo_surface_attach_snapshot (cairo_surface_t *surface, cairo_surface_t *snapshot, cairo_surface_func_t detach_func) { assert (surface != snapshot); assert (snapshot->snapshot_of != surface); cairo_surface_reference (snapshot); if (snapshot->snapshot_of != NULL) _cairo_surface_detach_snapshot (snapshot); snapshot->snapshot_of = surface; snapshot->snapshot_detach = detach_func; cairo_list_add (&snapshot->snapshot, &surface->snapshots); assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot); } cairo_surface_t * _cairo_surface_has_snapshot (cairo_surface_t *surface, const cairo_surface_backend_t *backend) { cairo_surface_t *snapshot; cairo_list_foreach_entry (snapshot, cairo_surface_t, &surface->snapshots, snapshot) { if (snapshot->backend == backend) return snapshot; } return NULL; } cairo_status_t _cairo_surface_begin_modification (cairo_surface_t *surface) { assert (surface->status == CAIRO_STATUS_SUCCESS); assert (! surface->finished); return _cairo_surface_flush (surface, 1); } void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, cairo_device_t *device, cairo_content_t content) { CAIRO_MUTEX_INITIALIZE (); surface->backend = backend; surface->device = cairo_device_reference (device); surface->content = content; surface->type = backend->type; CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); surface->status = CAIRO_STATUS_SUCCESS; surface->unique_id = _cairo_surface_allocate_unique_id (); surface->finished = FALSE; surface->_finishing = FALSE; surface->is_clear = FALSE; surface->serial = 0; surface->damage = NULL; surface->owns_device = (device != NULL); _cairo_user_data_array_init (&surface->user_data); _cairo_user_data_array_init (&surface->mime_data); cairo_matrix_init_identity (&surface->device_transform); cairo_matrix_init_identity (&surface->device_transform_inverse); cairo_list_init (&surface->device_transform_observers); surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; cairo_list_init (&surface->snapshots); surface->snapshot_of = NULL; surface->has_font_options = FALSE; } static void _cairo_surface_copy_similar_properties (cairo_surface_t *surface, cairo_surface_t *other) { if (other->has_font_options || other->backend != surface->backend) { cairo_font_options_t options; cairo_surface_get_font_options (other, &options); _cairo_surface_set_font_options (surface, &options); } cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, other->y_fallback_resolution); } cairo_surface_t * _cairo_surface_create_similar_scratch (cairo_surface_t *other, cairo_content_t content, int width, int height) { cairo_surface_t *surface; if (unlikely (other->status)) return _cairo_surface_create_in_error (other->status); surface = NULL; if (other->backend->create_similar) surface = other->backend->create_similar (other, content, width, height); if (surface == NULL) surface = cairo_surface_create_similar_image (other, _cairo_format_from_content (content), width, height); if (unlikely (surface->status)) return surface; _cairo_surface_copy_similar_properties (surface, other); return surface; } /** * cairo_surface_create_similar: * @other: an existing surface used to select the backend of the new surface * @content: the content for the new surface * @width: width of the new surface, (in device-space units) * @height: height of the new surface (in device-space units) * * Create a new surface that is as compatible as possible with an * existing surface. For example the new surface will have the same * fallback resolution and font options as @other. Generally, the new * surface will also use the same backend as @other, unless that is * not possible for some reason. The type of the returned surface may * be examined with cairo_surface_get_type(). * * Initially the surface contents are all 0 (transparent if contents * have transparency, black otherwise.) * * Use cairo_surface_create_similar_image() if you need an image surface * which can be painted quickly to the target surface. * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * Since: 1.0 **/ cairo_surface_t * cairo_surface_create_similar (cairo_surface_t *other, cairo_content_t content, int width, int height) { cairo_surface_t *surface; if (unlikely (other->status)) return _cairo_surface_create_in_error (other->status); if (unlikely (other->finished)) return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); if (unlikely (width < 0 || height < 0)) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); if (unlikely (! CAIRO_CONTENT_VALID (content))) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT); surface = _cairo_surface_create_similar_solid (other, content, width, height, CAIRO_COLOR_TRANSPARENT); assert (surface->is_clear); return surface; } /** * cairo_surface_create_similar_image: * @other: an existing surface used to select the preference of the new surface * @format: the format for the new surface * @width: width of the new surface, (in device-space units) * @height: height of the new surface (in device-space units) * * Create a new image surface that is as compatible as possible for uploading * to and the use in conjunction with an existing surface. However, this surface * can still be used like any normal image surface. * * Initially the surface contents are all 0 (transparent if contents * have transparency, black otherwise.) * * Use cairo_surface_create_similar() if you don't need an image surface. * * Return value: a pointer to the newly allocated image surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * Since: 1.12 **/ cairo_surface_t * cairo_surface_create_similar_image (cairo_surface_t *other, cairo_format_t format, int width, int height) { cairo_surface_t *image; if (unlikely (other->status)) return _cairo_surface_create_in_error (other->status); if (unlikely (other->finished)) return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); if (unlikely (width < 0 || height < 0)) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); if (unlikely (! CAIRO_FORMAT_VALID (format))) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); image = NULL; if (other->backend->create_similar_image) image = other->backend->create_similar_image (other, format, width, height); if (image == NULL) image = cairo_image_surface_create (format, width, height); assert (image->is_clear); return image; } slim_hidden_def (cairo_surface_create_similar_image); /** * _cairo_surface_map_to_image: * @surface: an existing surface used to extract the image from * @extents: limit the extraction to an rectangular region * * Returns an image surface that is the most efficient mechanism for * modifying the backing store of the target surface. The region * retrieved is limited to @extents. * * Note, the use of the original surface as a target or source whilst * it is mapped is undefined. The result of mapping the surface * multiple times is undefined. Calling cairo_surface_destroy() or * cairo_surface_finish() on the resulting image surface results in * undefined behavior. Changing the device transform of the image * surface or of @surface before the image surface is unmapped results * in undefined behavior. * * Assumes that @surface is valid (CAIRO_STATUS_SUCCESS, * non-finished). * * Return value: a pointer to the newly allocated image surface. The * caller must use _cairo_surface_unmap_image() to destroy this image * surface. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. * * The returned image might have a %CAIRO_FORMAT_INVALID format. **/ cairo_image_surface_t * _cairo_surface_map_to_image (cairo_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_image_surface_t *image = NULL; assert (extents != NULL); /* TODO: require map_to_image != NULL */ if (surface->backend->map_to_image) image = surface->backend->map_to_image (surface, extents); if (image == NULL) image = _cairo_image_surface_clone_subimage (surface, extents); return image; } /** * _cairo_surface_unmap_image: * @surface: the surface passed to _cairo_surface_map_to_image(). * @image: the currently mapped image * * Unmaps the image surface as returned from * _cairo_surface_map_to_image(). * * The content of the image will be uploaded to the target surface. * Afterwards, the image is destroyed. * * Using an image surface which wasn't returned by * _cairo_surface_map_to_image() results in undefined behavior. * * An image surface in error status can be passed to * _cairo_surface_unmap_image(). * * Return value: the unmap status. * * Even if the unmap status is not successful, @image is destroyed. **/ cairo_int_status_t _cairo_surface_unmap_image (cairo_surface_t *surface, cairo_image_surface_t *image) { cairo_surface_pattern_t pattern; cairo_rectangle_int_t extents; cairo_clip_t *clip; cairo_int_status_t status; /* map_to_image can return error surfaces */ if (unlikely (image->base.status)) { status = image->base.status; goto destroy; } /* If the image is untouched just skip the update */ if (image->base.serial == 0) { status = CAIRO_STATUS_SUCCESS; goto destroy; } /* TODO: require unmap_image != NULL */ if (surface->backend->unmap_image && ! _cairo_image_surface_is_clone (image)) { status = surface->backend->unmap_image (surface, image); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } _cairo_pattern_init_for_surface (&pattern, &image->base); pattern.base.filter = CAIRO_FILTER_NEAREST; /* We have to apply the translate from map_to_image's extents.x and .y */ cairo_matrix_init_translate (&pattern.base.matrix, image->base.device_transform.x0, image->base.device_transform.y0); /* And we also have to clip the operation to the image's extents */ extents.x = image->base.device_transform_inverse.x0; extents.y = image->base.device_transform_inverse.y0; extents.width = image->width; extents.height = image->height; clip = _cairo_clip_intersect_rectangle (NULL, &extents); status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE, &pattern.base, clip); _cairo_pattern_fini (&pattern.base); _cairo_clip_destroy (clip); destroy: cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return status; } /** * cairo_surface_map_to_image: * @surface: an existing surface used to extract the image from * @extents: limit the extraction to an rectangular region * * Returns an image surface that is the most efficient mechanism for * modifying the backing store of the target surface. The region retrieved * may be limited to the @extents or %NULL for the whole surface * * Note, the use of the original surface as a target or source whilst * it is mapped is undefined. The result of mapping the surface * multiple times is undefined. Calling cairo_surface_destroy() or * cairo_surface_finish() on the resulting image surface results in * undefined behavior. Changing the device transform of the image * surface or of @surface before the image surface is unmapped results * in undefined behavior. * * Return value: a pointer to the newly allocated image surface. The caller * must use cairo_surface_unmap_image() to destroy this image surface. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. If the returned pointer does not have an * error status, it is guaranteed to be an image surface whose format * is not %CAIRO_FORMAT_INVALID. * * Since: 1.12 **/ cairo_surface_t * cairo_surface_map_to_image (cairo_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_rectangle_int_t rect; cairo_image_surface_t *image; cairo_status_t status; if (unlikely (surface->status)) return _cairo_surface_create_in_error (surface->status); if (unlikely (surface->finished)) return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); if (extents == NULL) { if (unlikely (! surface->backend->get_extents (surface, &rect))) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); extents = ▭ } else { cairo_rectangle_int_t surface_extents; /* If this surface is bounded, we can't map parts * that are outside of it. */ if (likely (surface->backend->get_extents (surface, &surface_extents))) { if (unlikely (! _cairo_rectangle_contains_rectangle (&surface_extents, extents))) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); } } image = _cairo_surface_map_to_image (surface, extents); status = image->base.status; if (unlikely (status)) { cairo_surface_destroy (&image->base); return _cairo_surface_create_in_error (status); } if (image->format == CAIRO_FORMAT_INVALID) { cairo_surface_destroy (&image->base); image = _cairo_image_surface_clone_subimage (surface, extents); } return &image->base; } /** * cairo_surface_unmap_image: * @surface: the surface passed to cairo_surface_map_to_image(). * @image: the currently mapped image * * Unmaps the image surface as returned from #cairo_surface_map_to_image(). * * The content of the image will be uploaded to the target surface. * Afterwards, the image is destroyed. * * Using an image surface which wasn't returned by cairo_surface_map_to_image() * results in undefined behavior. * * Since: 1.12 **/ void cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; if (unlikely (surface->status)) { status = surface->status; goto error; } if (unlikely (surface->finished)) { status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); goto error; } if (unlikely (image->status)) { status = image->status; goto error; } if (unlikely (image->finished)) { status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); goto error; } if (unlikely (! _cairo_surface_is_image (image))) { status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); goto error; } status = _cairo_surface_unmap_image (surface, (cairo_image_surface_t *) image); if (unlikely (status)) _cairo_surface_set_error (surface, status); return; error: _cairo_surface_set_error (surface, status); cairo_surface_finish (image); cairo_surface_destroy (image); } cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_content_t content, int width, int height, const cairo_color_t *color) { cairo_status_t status; cairo_surface_t *surface; cairo_solid_pattern_t pattern; surface = _cairo_surface_create_similar_scratch (other, content, width, height); if (unlikely (surface->status)) return surface; _cairo_pattern_init_solid (&pattern, color); status = _cairo_surface_paint (surface, color == CAIRO_COLOR_TRANSPARENT ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } return surface; } /** * cairo_surface_reference: * @surface: a #cairo_surface_t * * Increases the reference count on @surface by one. This prevents * @surface from being destroyed until a matching call to * cairo_surface_destroy() is made. * * The number of references to a #cairo_surface_t can be get using * cairo_surface_get_reference_count(). * * Return value: the referenced #cairo_surface_t. * * Since: 1.0 **/ cairo_surface_t * cairo_surface_reference (cairo_surface_t *surface) { if (surface == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return surface; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); _cairo_reference_count_inc (&surface->ref_count); return surface; } slim_hidden_def (cairo_surface_reference); /** * cairo_surface_destroy: * @surface: a #cairo_surface_t * * Decreases the reference count on @surface by one. If the result is * zero, then @surface and all associated resources are freed. See * cairo_surface_reference(). * * Since: 1.0 **/ void cairo_surface_destroy (cairo_surface_t *surface) { if (surface == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); if (! _cairo_reference_count_dec_and_test (&surface->ref_count)) return; assert (surface->snapshot_of == NULL); if (! surface->finished) { _cairo_surface_finish_snapshots (surface); /* We may have been referenced by a snapshot prior to have * detaching it with the copy-on-write. */ if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count)) return; _cairo_surface_finish (surface); } if (surface->damage) _cairo_damage_destroy (surface->damage); _cairo_user_data_array_fini (&surface->user_data); _cairo_user_data_array_fini (&surface->mime_data); if (surface->owns_device) cairo_device_destroy (surface->device); assert (surface->snapshot_of == NULL); assert (! _cairo_surface_has_snapshots (surface)); /* paranoid check that nobody took a reference whilst finishing */ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); free (surface); } slim_hidden_def(cairo_surface_destroy); /** * cairo_surface_get_reference_count: * @surface: a #cairo_surface_t * * Returns the current reference count of @surface. * * Return value: the current reference count of @surface. If the * object is a nil object, 0 will be returned. * * Since: 1.4 **/ unsigned int cairo_surface_get_reference_count (cairo_surface_t *surface) { if (surface == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count); } static void _cairo_surface_finish_snapshots (cairo_surface_t *surface) { cairo_status_t status; /* update the snapshots *before* we declare the surface as finished */ surface->_finishing = TRUE; status = _cairo_surface_flush (surface, 0); (void) status; } static void _cairo_surface_finish (cairo_surface_t *surface) { cairo_status_t status; surface->finished = TRUE; /* call finish even if in error mode */ if (surface->backend->finish) { status = surface->backend->finish (surface); if (unlikely (status)) _cairo_surface_set_error (surface, status); } assert (surface->snapshot_of == NULL); assert (!_cairo_surface_has_snapshots (surface)); } /** * cairo_surface_finish: * @surface: the #cairo_surface_t to finish * * This function finishes the surface and drops all references to * external resources. For example, for the Xlib backend it means * that cairo will no longer access the drawable, which can be freed. * After calling cairo_surface_finish() the only valid operations on a * surface are getting and setting user, referencing and * destroying, and flushing and finishing it. * Further drawing to the surface will not affect the * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED * error. * * When the last call to cairo_surface_destroy() decreases the * reference count to zero, cairo will call cairo_surface_finish() if * it hasn't been called already, before freeing the resources * associated with the surface. * * Since: 1.0 **/ void cairo_surface_finish (cairo_surface_t *surface) { if (surface == NULL) return; if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return; if (surface->finished) return; /* We have to be careful when decoupling potential reference cycles */ cairo_surface_reference (surface); _cairo_surface_finish_snapshots (surface); /* XXX need to block and wait for snapshot references */ _cairo_surface_finish (surface); cairo_surface_destroy (surface); } slim_hidden_def (cairo_surface_finish); /** * _cairo_surface_release_device_reference: * @surface: a #cairo_surface_t * * This function makes @surface release the reference to its device. The * function is intended to be used for avoiding cycling references for * surfaces that are owned by their device, for example cache surfaces. * Note that the @surface will still assume that the device is available. * So it is the caller's responsibility to ensure the device stays around * until the @surface is destroyed. Just calling cairo_surface_finish() is * not enough. **/ void _cairo_surface_release_device_reference (cairo_surface_t *surface) { assert (surface->owns_device); cairo_device_destroy (surface->device); surface->owns_device = FALSE; } /** * cairo_surface_get_user_data: * @surface: a #cairo_surface_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @surface using the specified * key. If no user data has been attached with the given key this * function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.0 **/ void * cairo_surface_get_user_data (cairo_surface_t *surface, const cairo_user_data_key_t *key) { /* Prevent reads of the array during teardown */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) return NULL; return _cairo_user_data_array_get_data (&surface->user_data, key); } /** * cairo_surface_set_user_data: * @surface: a #cairo_surface_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the surface * @destroy: a #cairo_destroy_func_t which will be called when the * surface is destroyed or when new user data is attached using the * same key. * * Attach user data to @surface. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.0 **/ cairo_status_t cairo_surface_set_user_data (cairo_surface_t *surface, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return surface->status; if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); return _cairo_user_data_array_set_data (&surface->user_data, key, user_data, destroy); } /** * cairo_surface_get_mime_data: * @surface: a #cairo_surface_t * @mime_type: the mime type of the image data * @data: the image data to attached to the surface * @length: the length of the image data * * Return mime data previously attached to @surface using the * specified mime type. If no data has been attached with the given * mime type, @data is set %NULL. * * Since: 1.10 **/ void cairo_surface_get_mime_data (cairo_surface_t *surface, const char *mime_type, const unsigned char **data, unsigned long *length) { cairo_user_data_slot_t *slots; int i, num_slots; *data = NULL; *length = 0; /* Prevent reads of the array during teardown */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) return; /* The number of mime-types attached to a surface is usually small, * typically zero. Therefore it is quicker to do a strcmp() against * each key than it is to intern the string (i.e. compute a hash, * search the hash table, and do a final strcmp). */ num_slots = surface->mime_data.num_elements; slots = _cairo_array_index (&surface->mime_data, 0); for (i = 0; i < num_slots; i++) { if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) { cairo_mime_data_t *mime_data = slots[i].user_data; *data = mime_data->data; *length = mime_data->length; return; } } } slim_hidden_def (cairo_surface_get_mime_data); static void _cairo_mime_data_destroy (void *ptr) { cairo_mime_data_t *mime_data = ptr; if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count)) return; if (mime_data->destroy && mime_data->closure) mime_data->destroy (mime_data->closure); free (mime_data); } /** * CAIRO_MIME_TYPE_JP2: * * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1). * * Since: 1.10 **/ /** * CAIRO_MIME_TYPE_JPEG: * * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1). * * Since: 1.10 **/ /** * CAIRO_MIME_TYPE_PNG: * * The Portable Network Graphics image file format (ISO/IEC 15948). * * Since: 1.10 **/ /** * CAIRO_MIME_TYPE_URI: * * URI for an image file (unofficial MIME type). * * Since: 1.10 **/ /** * CAIRO_MIME_TYPE_UNIQUE_ID: * * Unique identifier for a surface (cairo specific MIME type). * * Since: 1.12 **/ /** * cairo_surface_set_mime_data: * @surface: a #cairo_surface_t * @mime_type: the MIME type of the image data * @data: the image data to attach to the surface * @length: the length of the image data * @destroy: a #cairo_destroy_func_t which will be called when the * surface is destroyed or when new image data is attached using the * same mime type. * @closure: the data to be passed to the @destroy notifier * * Attach an image in the format @mime_type to @surface. To remove * the data from a surface, call this function with same mime type * and %NULL for @data. * * The attached image (or filename) data can later be used by backends * which support it (currently: PDF, PS, SVG and Win32 Printing * surfaces) to emit this data instead of making a snapshot of the * @surface. This approach tends to be faster and requires less * memory and disk space. * * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG, * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI, * %CAIRO_MIME_TYPE_UNIQUE_ID. * * See corresponding backend surface docs for details about which MIME * types it can handle. Caution: the associated MIME data will be * discarded if you draw on the surface afterwards. Use this function * with care. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.10 **/ cairo_status_t cairo_surface_set_mime_data (cairo_surface_t *surface, const char *mime_type, const unsigned char *data, unsigned long length, cairo_destroy_func_t destroy, void *closure) { cairo_status_t status; cairo_mime_data_t *mime_data; if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return surface->status; if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); status = _cairo_intern_string (&mime_type, -1); if (unlikely (status)) return _cairo_surface_set_error (surface, status); if (data != NULL) { mime_data = malloc (sizeof (cairo_mime_data_t)); if (unlikely (mime_data == NULL)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY)); CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1); mime_data->data = (unsigned char *) data; mime_data->length = length; mime_data->destroy = destroy; mime_data->closure = closure; } else mime_data = NULL; status = _cairo_user_data_array_set_data (&surface->mime_data, (cairo_user_data_key_t *) mime_type, mime_data, _cairo_mime_data_destroy); if (unlikely (status)) { free (mime_data); return _cairo_surface_set_error (surface, status); } return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_surface_set_mime_data); /** * cairo_surface_supports_mime_type: * @surface: a #cairo_surface_t * @mime_type: the mime type * * Return whether @surface supports @mime_type. * * Return value: %TRUE if @surface supports * @mime_type, %FALSE otherwise * * Since: 1.12 **/ cairo_bool_t cairo_surface_supports_mime_type (cairo_surface_t *surface, const char *mime_type) { const char **types; if (unlikely (surface->status)) return FALSE; if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (surface->backend->get_supported_mime_types) { types = surface->backend->get_supported_mime_types (surface); if (types) { while (*types) { if (strcmp (*types, mime_type) == 0) return TRUE; types++; } } } return FALSE; } slim_hidden_def (cairo_surface_supports_mime_type); static void _cairo_mime_data_reference (const void *key, void *elt, void *closure) { cairo_mime_data_t *mime_data = elt; _cairo_reference_count_inc (&mime_data->ref_count); } cairo_status_t _cairo_surface_copy_mime_data (cairo_surface_t *dst, cairo_surface_t *src) { cairo_status_t status; if (dst->status) return dst->status; if (src->status) return _cairo_surface_set_error (dst, src->status); /* first copy the mime-data, discarding any already set on dst */ status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data); if (unlikely (status)) return _cairo_surface_set_error (dst, status); /* now increment the reference counters for the copies */ _cairo_user_data_array_foreach (&dst->mime_data, _cairo_mime_data_reference, NULL); return CAIRO_STATUS_SUCCESS; } /** * _cairo_surface_set_font_options: * @surface: a #cairo_surface_t * @options: a #cairo_font_options_t object that contains the * options to use for this surface instead of backend's default * font options. * * Sets the default font rendering options for the surface. * This is useful to correctly propagate default font options when * falling back to an image surface in a backend implementation. * This affects the options returned in cairo_surface_get_font_options(). * * If @options is %NULL the surface options are reset to those of * the backend default. **/ void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options) { if (surface->status) return; assert (surface->snapshot_of == NULL); if (surface->finished) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (options) { surface->has_font_options = TRUE; _cairo_font_options_init_copy (&surface->font_options, options); } else { surface->has_font_options = FALSE; } } /** * cairo_surface_get_font_options: * @surface: a #cairo_surface_t * @options: a #cairo_font_options_t object into which to store * the retrieved options. All existing values are overwritten * * Retrieves the default font rendering options for the surface. * This allows display surfaces to report the correct subpixel order * for rendering on them, print surfaces to disable hinting of * metrics and so forth. The result can then be used with * cairo_scaled_font_create(). * * Since: 1.0 **/ void cairo_surface_get_font_options (cairo_surface_t *surface, cairo_font_options_t *options) { if (cairo_font_options_status (options)) return; if (surface->status) { _cairo_font_options_init_default (options); return; } if (! surface->has_font_options) { surface->has_font_options = TRUE; _cairo_font_options_init_default (&surface->font_options); if (!surface->finished && surface->backend->get_font_options) { surface->backend->get_font_options (surface, &surface->font_options); } } _cairo_font_options_init_copy (options, &surface->font_options); } slim_hidden_def (cairo_surface_get_font_options); cairo_status_t _cairo_surface_flush (cairo_surface_t *surface, unsigned flags) { /* update the current snapshots *before* the user updates the surface */ _cairo_surface_detach_snapshots (surface); if (surface->snapshot_of != NULL) _cairo_surface_detach_snapshot (surface); _cairo_surface_detach_mime_data (surface); return __cairo_surface_flush (surface, flags); } /** * cairo_surface_flush: * @surface: a #cairo_surface_t * * Do any pending drawing for the surface and also restore any * temporary modifications cairo has made to the surface's * state. This function must be called before switching from * drawing on the surface with cairo to drawing on it directly * with native APIs. If the surface doesn't support direct access, * then this function does nothing. * * Since: 1.0 **/ void cairo_surface_flush (cairo_surface_t *surface) { cairo_status_t status; if (surface->status) return; if (surface->finished) return; status = _cairo_surface_flush (surface, 0); if (unlikely (status)) _cairo_surface_set_error (surface, status); } slim_hidden_def (cairo_surface_flush); /** * cairo_surface_mark_dirty: * @surface: a #cairo_surface_t * * Tells cairo that drawing has been done to surface using means other * than cairo, and that cairo should reread any cached areas. Note * that you must call cairo_surface_flush() before doing such drawing. * * Since: 1.0 **/ void cairo_surface_mark_dirty (cairo_surface_t *surface) { cairo_rectangle_int_t extents; if (unlikely (surface->status)) return; if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } _cairo_surface_get_extents (surface, &extents); cairo_surface_mark_dirty_rectangle (surface, extents.x, extents.y, extents.width, extents.height); } slim_hidden_def (cairo_surface_mark_dirty); /** * cairo_surface_mark_dirty_rectangle: * @surface: a #cairo_surface_t * @x: X coordinate of dirty rectangle * @y: Y coordinate of dirty rectangle * @width: width of dirty rectangle * @height: height of dirty rectangle * * Like cairo_surface_mark_dirty(), but drawing has been done only to * the specified rectangle, so that cairo can retain cached contents * for other parts of the surface. * * Any cached clip set on the surface will be reset by this function, * to make sure that future cairo calls have the clip set that they * expect. * * Since: 1.0 **/ void cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, int x, int y, int width, int height) { cairo_status_t status; if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } /* The application *should* have called cairo_surface_flush() before * modifying the surface independently of cairo (and thus having to * call mark_dirty()). */ assert (! _cairo_surface_has_snapshots (surface)); assert (! _cairo_surface_has_mime_data (surface)); surface->is_clear = FALSE; surface->serial++; if (surface->damage) { cairo_box_t box; box.p1.x = x; box.p1.y = y; box.p2.x = x + width; box.p2.y = y + height; surface->damage = _cairo_damage_add_box (surface->damage, &box); } if (surface->backend->mark_dirty_rectangle != NULL) { /* XXX: FRAGILE: We're ignoring the scaling component of * device_transform here. I don't know what the right thing to * do would actually be if there were some scaling here, but * we avoid this since device_transfom scaling is not exported * publicly and mark_dirty is not used internally. */ status = surface->backend->mark_dirty_rectangle (surface, x + surface->device_transform.x0, y + surface->device_transform.y0, width, height); if (unlikely (status)) _cairo_surface_set_error (surface, status); } } slim_hidden_def (cairo_surface_mark_dirty_rectangle); /** * _cairo_surface_set_device_scale: * @surface: a #cairo_surface_t * @sx: a scale factor in the X direction * @sy: a scale factor in the Y direction * * Private function for setting an extra scale factor to affect all * drawing to a surface. This is used, for example, when replaying a * recording surface to an image fallback intended for an eventual * vector-oriented backend. Since the recording surface will record * coordinates in one backend space, but the image fallback uses a * different backend space, (differing by the fallback resolution * scale factors), we need a scale factor correction. * * Caution: Not all places we use device transform correctly handle * both a translate and a scale. An audit would be nice. **/ void _cairo_surface_set_device_scale (cairo_surface_t *surface, double sx, double sy) { cairo_status_t status; if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } status = _cairo_surface_begin_modification (surface); if (unlikely (status)) { _cairo_surface_set_error (surface, status); return; } surface->device_transform.xx = sx; surface->device_transform.yy = sy; surface->device_transform.xy = 0.0; surface->device_transform.yx = 0.0; surface->device_transform_inverse = surface->device_transform; status = cairo_matrix_invert (&surface->device_transform_inverse); /* should always be invertible unless given pathological input */ assert (status == CAIRO_STATUS_SUCCESS); _cairo_observers_notify (&surface->device_transform_observers, surface); } /** * cairo_surface_set_device_offset: * @surface: a #cairo_surface_t * @x_offset: the offset in the X direction, in device units * @y_offset: the offset in the Y direction, in device units * * Sets an offset that is added to the device coordinates determined * by the CTM when drawing to @surface. One use case for this function * is when we want to create a #cairo_surface_t that redirects drawing * for a portion of an onscreen surface to an offscreen surface in a * way that is completely invisible to the user of the cairo * API. Setting a transformation via cairo_translate() isn't * sufficient to do this, since functions like * cairo_device_to_user() will expose the hidden offset. * * Note that the offset affects drawing to the surface as well as * using the surface in a source pattern. * * Since: 1.0 **/ void cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, double y_offset) { cairo_status_t status; if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } status = _cairo_surface_begin_modification (surface); if (unlikely (status)) { _cairo_surface_set_error (surface, status); return; } surface->device_transform.x0 = x_offset; surface->device_transform.y0 = y_offset; surface->device_transform_inverse = surface->device_transform; status = cairo_matrix_invert (&surface->device_transform_inverse); /* should always be invertible unless given pathological input */ assert (status == CAIRO_STATUS_SUCCESS); _cairo_observers_notify (&surface->device_transform_observers, surface); } slim_hidden_def (cairo_surface_set_device_offset); /** * cairo_surface_get_device_offset: * @surface: a #cairo_surface_t * @x_offset: the offset in the X direction, in device units * @y_offset: the offset in the Y direction, in device units * * This function returns the previous device offset set by * cairo_surface_set_device_offset(). * * Since: 1.2 **/ void cairo_surface_get_device_offset (cairo_surface_t *surface, double *x_offset, double *y_offset) { if (x_offset) *x_offset = surface->device_transform.x0; if (y_offset) *y_offset = surface->device_transform.y0; } slim_hidden_def (cairo_surface_get_device_offset); /** * cairo_surface_set_fallback_resolution: * @surface: a #cairo_surface_t * @x_pixels_per_inch: horizontal setting for pixels per inch * @y_pixels_per_inch: vertical setting for pixels per inch * * Set the horizontal and vertical resolution for image fallbacks. * * When certain operations aren't supported natively by a backend, * cairo will fallback by rendering operations to an image and then * overlaying that image onto the output. For backends that are * natively vector-oriented, this function can be used to set the * resolution used for these image fallbacks, (larger values will * result in more detailed images, but also larger file sizes). * * Some examples of natively vector-oriented backends are the ps, pdf, * and svg backends. * * For backends that are natively raster-oriented, image fallbacks are * still possible, but they are always performed at the native * device resolution. So this function has no effect on those * backends. * * Note: The fallback resolution only takes effect at the time of * completing a page (with cairo_show_page() or cairo_copy_page()) so * there is currently no way to have more than one fallback resolution * in effect on a single page. * * The default fallback resoultion is 300 pixels per inch in both * dimensions. * * Since: 1.2 **/ void cairo_surface_set_fallback_resolution (cairo_surface_t *surface, double x_pixels_per_inch, double y_pixels_per_inch) { cairo_status_t status; if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) { /* XXX Could delay raising the error until we fallback, but throwing * the error here means that we can catch the real culprit. */ _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); return; } status = _cairo_surface_begin_modification (surface); if (unlikely (status)) { _cairo_surface_set_error (surface, status); return; } surface->x_fallback_resolution = x_pixels_per_inch; surface->y_fallback_resolution = y_pixels_per_inch; } slim_hidden_def (cairo_surface_set_fallback_resolution); /** * cairo_surface_get_fallback_resolution: * @surface: a #cairo_surface_t * @x_pixels_per_inch: horizontal pixels per inch * @y_pixels_per_inch: vertical pixels per inch * * This function returns the previous fallback resolution set by * cairo_surface_set_fallback_resolution(), or default fallback * resolution if never set. * * Since: 1.8 **/ void cairo_surface_get_fallback_resolution (cairo_surface_t *surface, double *x_pixels_per_inch, double *y_pixels_per_inch) { if (x_pixels_per_inch) *x_pixels_per_inch = surface->x_fallback_resolution; if (y_pixels_per_inch) *y_pixels_per_inch = surface->y_fallback_resolution; } cairo_bool_t _cairo_surface_has_device_transform (cairo_surface_t *surface) { return ! _cairo_matrix_is_identity (&surface->device_transform); } /** * _cairo_surface_acquire_source_image: * @surface: a #cairo_surface_t * @image_out: location to store a pointer to an image surface that * has identical contents to @surface. This surface could be @surface * itself, a surface held internal to @surface, or it could be a new * surface with a copy of the relevant portion of @surface. * @image_extra: location to store image specific backend data * * Gets an image surface to use when drawing as a fallback when drawing with * @surface as a source. _cairo_surface_release_source_image() must be called * when finished. * * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out. * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified * surface. Or %CAIRO_STATUS_NO_MEMORY. **/ cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_status_t status; if (unlikely (surface->status)) return surface->status; assert (!surface->finished); if (surface->backend->acquire_source_image == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = surface->backend->acquire_source_image (surface, image_out, image_extra); if (unlikely (status)) return _cairo_surface_set_error (surface, status); _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_surface_default_acquire_source_image (void *_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_surface_t *surface = _surface; cairo_rectangle_int_t extents; if (unlikely (! surface->backend->get_extents (surface, &extents))) return _cairo_error (CAIRO_STATUS_INVALID_SIZE); *image_out = _cairo_surface_map_to_image (surface, &extents); *image_extra = NULL; return (*image_out)->base.status; } /** * _cairo_surface_release_source_image: * @surface: a #cairo_surface_t * @image_extra: same as return from the matching _cairo_surface_acquire_source_image() * * Releases any resources obtained with _cairo_surface_acquire_source_image() **/ void _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra) { assert (!surface->finished); if (surface->backend->release_source_image) surface->backend->release_source_image (surface, image, image_extra); } void _cairo_surface_default_release_source_image (void *surface, cairo_image_surface_t *image, void *image_extra) { cairo_status_t ignored; ignored = _cairo_surface_unmap_image (surface, image); (void)ignored; } cairo_surface_t * _cairo_surface_get_source (cairo_surface_t *surface, cairo_rectangle_int_t *extents) { assert (surface->backend->source); return surface->backend->source (surface, extents); } cairo_surface_t * _cairo_surface_default_source (void *surface, cairo_rectangle_int_t *extents) { if (extents) _cairo_surface_get_extents(surface, extents); return surface; } static cairo_status_t _pattern_has_error (const cairo_pattern_t *pattern) { const cairo_surface_pattern_t *spattern; if (unlikely (pattern->status)) return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_STATUS_SUCCESS; spattern = (const cairo_surface_pattern_t *) pattern; if (unlikely (spattern->surface->status)) return spattern->surface->status; if (unlikely (spattern->surface->finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t nothing_to_do (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source) { if (_cairo_pattern_is_clear (source)) { if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) return TRUE; if (op == CAIRO_OPERATOR_SOURCE) op = CAIRO_OPERATOR_CLEAR; } if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) return TRUE; if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0) return TRUE; return FALSE; } cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; status = _pattern_has_error (source); if (unlikely (status)) return status; if (nothing_to_do (surface, op, source)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; status = surface->backend->paint (surface, op, source, clip); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL; surface->serial++; } return _cairo_surface_set_error (surface, status); } cairo_status_t _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; /* If the mask is blank, this is just an expensive no-op */ if (_cairo_pattern_is_clear (mask) && _cairo_operator_bounded_by_mask (op)) { return CAIRO_STATUS_SUCCESS; } status = _pattern_has_error (source); if (unlikely (status)) return status; status = _pattern_has_error (mask); if (unlikely (status)) return status; if (nothing_to_do (surface, op, source)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; status = surface->backend->mask (surface, op, source, mask, clip); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; surface->serial++; } return _cairo_surface_set_error (surface, status); } cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (surface->is_clear && fill_op == CAIRO_OPERATOR_CLEAR && stroke_op == CAIRO_OPERATOR_CLEAR) { return CAIRO_STATUS_SUCCESS; } status = _pattern_has_error (fill_source); if (unlikely (status)) return status; status = _pattern_has_error (stroke_source); if (unlikely (status)) return status; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; if (surface->backend->fill_stroke) { cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; status = surface->backend->fill_stroke (surface, fill_op, fill_source, fill_rule, fill_tolerance, fill_antialias, path, stroke_op, stroke_source, stroke_style, &dev_ctm, &dev_ctm_inverse, stroke_tolerance, stroke_antialias, clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto FINISH; } status = _cairo_surface_fill (surface, fill_op, fill_source, path, fill_rule, fill_tolerance, fill_antialias, clip); if (unlikely (status)) goto FINISH; status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, stroke_style, stroke_ctm, stroke_ctm_inverse, stroke_tolerance, stroke_antialias, clip); if (unlikely (status)) goto FINISH; FINISH: if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; surface->serial++; } return _cairo_surface_set_error (surface, status); } cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; status = _pattern_has_error (source); if (unlikely (status)) return status; if (nothing_to_do (surface, op, source)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; status = surface->backend->stroke (surface, op, source, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, clip); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; surface->serial++; } return _cairo_surface_set_error (surface, status); } cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; status = _pattern_has_error (source); if (unlikely (status)) return status; if (nothing_to_do (surface, op, source)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; status = surface->backend->fill (surface, op, source, path, fill_rule, tolerance, antialias, clip); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; surface->serial++; } return _cairo_surface_set_error (surface, status); } /** * cairo_surface_copy_page: * @surface: a #cairo_surface_t * * Emits the current page for backends that support multiple pages, * but doesn't clear it, so that the contents of the current page will * be retained for the next page. Use cairo_surface_show_page() if you * want to get an empty page after the emission. * * There is a convenience function for this that takes a #cairo_t, * namely cairo_copy_page(). * * Since: 1.6 **/ void cairo_surface_copy_page (cairo_surface_t *surface) { if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } /* It's fine if some backends don't implement copy_page */ if (surface->backend->copy_page == NULL) return; _cairo_surface_set_error (surface, surface->backend->copy_page (surface)); } slim_hidden_def (cairo_surface_copy_page); /** * cairo_surface_show_page: * @surface: a #cairo_Surface_t * * Emits and clears the current page for backends that support multiple * pages. Use cairo_surface_copy_page() if you don't want to clear the page. * * There is a convenience function for this that takes a #cairo_t, * namely cairo_show_page(). * * Since: 1.6 **/ void cairo_surface_show_page (cairo_surface_t *surface) { cairo_status_t status; if (unlikely (surface->status)) return; if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } status = _cairo_surface_begin_modification (surface); if (unlikely (status)) { _cairo_surface_set_error (surface, status); return; } /* It's fine if some backends don't implement show_page */ if (surface->backend->show_page == NULL) return; _cairo_surface_set_error (surface, surface->backend->show_page (surface)); } slim_hidden_def (cairo_surface_show_page); /** * _cairo_surface_get_extents: * @surface: the #cairo_surface_t to fetch extents for * * This function returns a bounding box for the surface. The surface * bounds are defined as a region beyond which no rendering will * possibly be recorded, in other words, it is the maximum extent of * potentially usable coordinates. * * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface * might be conceived as unbounded, but we force the user to provide a * maximum size at the time of surface_create. So get_extents uses * that size. * * Note: The coordinates returned are in "backend" space rather than * "surface" space. That is, they are relative to the true (0,0) * origin rather than the device_transform origin. This might seem a * bit inconsistent with other #cairo_surface_t interfaces, but all * current callers are within the surface layer where backend space is * desired. * * This behavior would have to be changed is we ever exported a public * variant of this function. **/ cairo_bool_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_int_t *extents) { cairo_bool_t bounded; if (unlikely (surface->status)) goto zero_extents; if (unlikely (surface->finished)) { _cairo_surface_set_error(surface, CAIRO_STATUS_SURFACE_FINISHED); goto zero_extents; } bounded = FALSE; if (surface->backend->get_extents != NULL) bounded = surface->backend->get_extents (surface, extents); if (! bounded) _cairo_unbounded_rectangle_init (extents); return bounded; zero_extents: extents->x = extents->y = 0; extents->width = extents->height = 0; return TRUE; } /** * cairo_surface_has_show_text_glyphs: * @surface: a #cairo_surface_t * * Returns whether the surface supports * sophisticated cairo_show_text_glyphs() operations. That is, * whether it actually uses the provided text and cluster data * to a cairo_show_text_glyphs() call. * * Note: Even if this function returns %FALSE, a * cairo_show_text_glyphs() operation targeted at @surface will * still succeed. It just will * act like a cairo_show_glyphs() operation. Users can use this * function to avoid computing UTF-8 text and cluster mapping if the * target surface does not use it. * * Return value: %TRUE if @surface supports * cairo_show_text_glyphs(), %FALSE otherwise * * Since: 1.8 **/ cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) { if (unlikely (surface->status)) return FALSE; if (unlikely (surface->finished)) { _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return FALSE; } if (surface->backend->has_show_text_glyphs) return surface->backend->has_show_text_glyphs (surface); else return surface->backend->show_text_glyphs != NULL; } slim_hidden_def (cairo_surface_has_show_text_glyphs); /* Note: the backends may modify the contents of the glyph array as long as * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to * avoid copying the array again and again, and edit it in-place. * Backends are in fact free to use the array as a generic buffer as they * see fit. * * For show_glyphs backend method, and NOT for show_text_glyphs method, * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify * that they have successfully rendered some of the glyphs (from the beginning * of the array), but not all. If they don't touch remaining_glyphs, it * defaults to all glyphs. * * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale. */ cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_int_status_t status; cairo_scaled_font_t *dev_scaled_font = scaled_font; TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (num_glyphs == 0 && utf8_len == 0) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; status = _pattern_has_error (source); if (unlikely (status)) return status; if (nothing_to_do (surface, op, source)) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_begin_modification (surface); if (unlikely (status)) return status; if (_cairo_surface_has_device_transform (surface) && ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL)) { cairo_font_options_t font_options; cairo_matrix_t dev_ctm, font_matrix; cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); cairo_scaled_font_get_ctm (scaled_font, &dev_ctm); cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform); cairo_scaled_font_get_font_options (scaled_font, &font_options); dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font), &font_matrix, &dev_ctm, &font_options); } status = cairo_scaled_font_status (dev_scaled_font); if (unlikely (status)) return _cairo_surface_set_error (surface, status); status = CAIRO_INT_STATUS_UNSUPPORTED; /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and * show_text_glyphs. Keep in synch. */ if (clusters) { /* A real show_text_glyphs call. Try show_text_glyphs backend * method first */ if (surface->backend->show_text_glyphs != NULL) { status = surface->backend->show_text_glyphs (surface, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, dev_scaled_font, clip); } if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->backend->show_glyphs) { status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, dev_scaled_font, clip); } } else { /* A mere show_glyphs call. Try show_glyphs backend method first */ if (surface->backend->show_glyphs != NULL) { status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, dev_scaled_font, clip); } else if (surface->backend->show_text_glyphs != NULL) { /* Intentionally only try show_text_glyphs method for show_glyphs * calls if backend does not have show_glyphs. If backend has * both methods implemented, we don't fallback from show_glyphs to * show_text_glyphs, and hence the backend can assume in its * show_text_glyphs call that clusters is not NULL (which also * implies that UTF-8 is not NULL, unless the text is * zero-length). */ status = surface->backend->show_text_glyphs (surface, op, source, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, dev_scaled_font, clip); } } if (dev_scaled_font != scaled_font) cairo_scaled_font_destroy (dev_scaled_font); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; surface->serial++; } return _cairo_surface_set_error (surface, status); } /** * _cairo_surface_set_resolution: * @surface: the surface * @x_res: x resolution, in dpi * @y_res: y resolution, in dpi * * Set the actual surface resolution of @surface to the given x and y DPI. * Mainly used for correctly computing the scale factor when fallback * rendering needs to take place in the paginated surface. **/ void _cairo_surface_set_resolution (cairo_surface_t *surface, double x_res, double y_res) { if (surface->status) return; surface->x_resolution = x_res; surface->y_resolution = y_res; } cairo_surface_t * _cairo_surface_create_in_error (cairo_status_t status) { assert (status < CAIRO_STATUS_LAST_STATUS); switch (status) { case CAIRO_STATUS_NO_MEMORY: return (cairo_surface_t *) &_cairo_surface_nil; case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch; case CAIRO_STATUS_INVALID_STATUS: return (cairo_surface_t *) &_cairo_surface_nil_invalid_status; case CAIRO_STATUS_INVALID_CONTENT: return (cairo_surface_t *) &_cairo_surface_nil_invalid_content; case CAIRO_STATUS_INVALID_FORMAT: return (cairo_surface_t *) &_cairo_surface_nil_invalid_format; case CAIRO_STATUS_INVALID_VISUAL: return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual; case CAIRO_STATUS_READ_ERROR: return (cairo_surface_t *) &_cairo_surface_nil_read_error; case CAIRO_STATUS_WRITE_ERROR: return (cairo_surface_t *) &_cairo_surface_nil_write_error; case CAIRO_STATUS_FILE_NOT_FOUND: return (cairo_surface_t *) &_cairo_surface_nil_file_not_found; case CAIRO_STATUS_TEMP_FILE_ERROR: return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error; case CAIRO_STATUS_INVALID_STRIDE: return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; case CAIRO_STATUS_INVALID_SIZE: return (cairo_surface_t *) &_cairo_surface_nil_invalid_size; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch; case CAIRO_STATUS_DEVICE_ERROR: return (cairo_surface_t *) &_cairo_surface_nil_device_error; case CAIRO_STATUS_SUCCESS: case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; /* fall-through */ case CAIRO_STATUS_INVALID_RESTORE: case CAIRO_STATUS_INVALID_POP_GROUP: case CAIRO_STATUS_NO_CURRENT_POINT: case CAIRO_STATUS_INVALID_MATRIX: case CAIRO_STATUS_NULL_POINTER: case CAIRO_STATUS_INVALID_STRING: case CAIRO_STATUS_INVALID_PATH_DATA: case CAIRO_STATUS_SURFACE_FINISHED: case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: case CAIRO_STATUS_INVALID_DASH: case CAIRO_STATUS_INVALID_DSC_COMMENT: case CAIRO_STATUS_INVALID_INDEX: case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: case CAIRO_STATUS_FONT_TYPE_MISMATCH: case CAIRO_STATUS_USER_FONT_IMMUTABLE: case CAIRO_STATUS_USER_FONT_ERROR: case CAIRO_STATUS_NEGATIVE_COUNT: case CAIRO_STATUS_INVALID_CLUSTERS: case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: case CAIRO_STATUS_DEVICE_FINISHED: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; } } cairo_surface_t * _cairo_int_surface_create_in_error (cairo_int_status_t status) { if (status < CAIRO_INT_STATUS_LAST_STATUS) return _cairo_surface_create_in_error (status); switch ((int)status) { case CAIRO_INT_STATUS_UNSUPPORTED: return (cairo_surface_t *) &_cairo_surface_nil_unsupported; case CAIRO_INT_STATUS_NOTHING_TO_DO: return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do; default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; } } /* LocalWords: rasterized */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-svg-surface-private.h000066400000000000000000000046301271037650300274440ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2005-2006 Emmanuel Pacaud * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg * Emmanuel Pacaud * Carl Worth */ #ifndef CAIRO_SVG_SURFACE_PRIVATE_H #define CAIRO_SVG_SURFACE_PRIVATE_H #include "cairo-svg.h" #include "cairo-surface-private.h" #include "cairo-surface-clipper-private.h" typedef struct cairo_svg_document cairo_svg_document_t; typedef struct cairo_svg_surface { cairo_surface_t base; cairo_content_t content; double width; double height; cairo_svg_document_t *document; cairo_output_stream_t *xml_node; cairo_array_t page_set; cairo_surface_clipper_t clipper; unsigned int clip_level; unsigned int base_clip; cairo_bool_t is_base_clip_emitted; cairo_paginated_mode_t paginated_mode; cairo_bool_t force_fallbacks; } cairo_svg_surface_t; #endif /* CAIRO_SVG_SURFACE_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-svg-surface.c000066400000000000000000002550641271037650300260000ustar00rootroot00000000000000/* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * Copyright © 2005-2007 Emmanuel Pacaud * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Kristian Høgsberg * Emmanuel Pacaud * Carl Worth */ #define _BSD_SOURCE /* for snprintf() */ #include "cairoint.h" #include "cairo-svg.h" #include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-info-private.h" #include "cairo-image-surface-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-output-stream-private.h" #include "cairo-path-fixed-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-svg-surface-private.h" /** * SECTION:cairo-svg * @Title: SVG Surfaces * @Short_Description: Rendering SVG documents * @See_Also: #cairo_surface_t * * The SVG surface is used to render cairo graphics to * SVG files and is a multi-page vector surface backend. **/ /** * CAIRO_HAS_SVG_SURFACE: * * Defined if the SVG surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.2 **/ typedef struct cairo_svg_page cairo_svg_page_t; static const int invalid_pattern_id = -1; static const cairo_svg_version_t _cairo_svg_versions[] = { CAIRO_SVG_VERSION_1_1, CAIRO_SVG_VERSION_1_2 }; #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) static const char *_cairo_svg_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_PNG, CAIRO_MIME_TYPE_URI, NULL }; static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse); static cairo_bool_t _cairo_svg_version_has_page_set_support (cairo_svg_version_t version) { return version > CAIRO_SVG_VERSION_1_1; } static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] = { "SVG 1.1", "SVG 1.2" }; static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] = { "1.1", "1.2" }; struct cairo_svg_page { unsigned int surface_id; unsigned int clip_level; cairo_output_stream_t *xml_node; }; struct cairo_svg_document { cairo_output_stream_t *output_stream; unsigned long refcount; cairo_surface_t *owner; cairo_bool_t finished; double width; double height; cairo_output_stream_t *xml_node_defs; cairo_output_stream_t *xml_node_glyphs; unsigned int linear_pattern_id; unsigned int radial_pattern_id; unsigned int pattern_id; unsigned int filter_id; unsigned int clip_id; unsigned int mask_id; cairo_bool_t alpha_filter; cairo_svg_version_t svg_version; cairo_scaled_font_subsets_t *font_subsets; }; static cairo_status_t _cairo_svg_document_create (cairo_output_stream_t *stream, double width, double height, cairo_svg_version_t version, cairo_svg_document_t **document_out); static cairo_status_t _cairo_svg_document_destroy (cairo_svg_document_t *document); static cairo_status_t _cairo_svg_document_finish (cairo_svg_document_t *document); static cairo_svg_document_t * _cairo_svg_document_reference (cairo_svg_document_t *document); static unsigned int _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document); static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_content_t content, double width, double height); static cairo_surface_t * _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, double width, double height, cairo_svg_version_t version); static const cairo_surface_backend_t cairo_svg_surface_backend; static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend; /** * cairo_svg_surface_create_for_stream: * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL * to indicate a no-op @write_func. With a no-op @write_func, * the surface may be queried or used as a source without * generating any temporary files. * @closure: the closure argument for @write_func * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a SVG surface of the specified size in points to be written * incrementally to the stream represented by @write_func and @closure. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width, double height) { cairo_output_stream_t *stream; stream = _cairo_output_stream_create (write_func, NULL, closure); if (_cairo_output_stream_get_status (stream)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); } /** * cairo_svg_surface_create: * @filename: a filename for the SVG output (must be writable), %NULL may be * used to specify no output. This will generate a SVG surface that * may be queried and used as a source, without generating a * temporary file. * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) * * Creates a SVG surface of the specified size in points to be written * to @filename. * * The SVG surface backend recognizes the following MIME types for the * data attached to a surface (see cairo_surface_set_mime_data()) when * it is used as a source pattern for drawing on this surface: * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend * emits a href with the content of MIME data instead of a surface * snapshot (PNG, Base64-encoded) in the corresponding image tag. * * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined * first. If present, the URI is emitted as is: assuring the * correctness of URI is left to the client code. * * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is * Base64-encoded and emitted. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 **/ cairo_surface_t * cairo_svg_surface_create (const char *filename, double width, double height) { cairo_output_stream_t *stream; stream = _cairo_output_stream_create_for_filename (filename); if (_cairo_output_stream_get_status (stream)) return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); } static cairo_bool_t _cairo_surface_is_svg (cairo_surface_t *surface) { return surface->backend == &cairo_svg_surface_backend; } /* If the abstract_surface is a paginated surface, and that paginated * surface's target is a svg_surface, then set svg_surface to that * target. Otherwise return FALSE. */ static cairo_bool_t _extract_svg_surface (cairo_surface_t *surface, cairo_svg_surface_t **svg_surface) { cairo_surface_t *target; cairo_status_t status_ignored; if (surface->status) return FALSE; if (surface->finished) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_paginated (surface)) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } target = _cairo_paginated_surface_get_target (surface); if (target->status) { status_ignored = _cairo_surface_set_error (surface, target->status); return FALSE; } if (target->finished) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_svg (target)) { status_ignored = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } *svg_surface = (cairo_svg_surface_t *) target; return TRUE; } /** * cairo_svg_surface_restrict_to_version: * @surface: a SVG #cairo_surface_t * @version: SVG version * * Restricts the generated SVG file to @version. See cairo_svg_get_versions() * for a list of available version values that can be used here. * * This function should only be called before any drawing operations * have been performed on the given surface. The simplest way to do * this is to call this function immediately after creating the * surface. * * Since: 1.2 **/ void cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, cairo_svg_version_t version) { cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ if (! _extract_svg_surface (abstract_surface, &surface)) return; if (version < CAIRO_SVG_VERSION_LAST) surface->document->svg_version = version; } /** * cairo_svg_get_versions: * @versions: supported version list * @num_versions: list length * * Used to retrieve the list of supported versions. See * cairo_svg_surface_restrict_to_version(). * * Since: 1.2 **/ void cairo_svg_get_versions (cairo_svg_version_t const **versions, int *num_versions) { if (versions != NULL) *versions = _cairo_svg_versions; if (num_versions != NULL) *num_versions = CAIRO_SVG_VERSION_LAST; } /** * cairo_svg_version_to_string: * @version: a version id * * Get the string representation of the given @version id. This function * will return %NULL if @version isn't valid. See cairo_svg_get_versions() * for a way to get the list of valid version ids. * * Return value: the string associated to given version. * * Since: 1.2 **/ const char * cairo_svg_version_to_string (cairo_svg_version_t version) { if (version >= CAIRO_SVG_VERSION_LAST) return NULL; return _cairo_svg_version_strings[version]; } static cairo_bool_t _cliprect_covers_surface (cairo_svg_surface_t *surface, cairo_path_fixed_t *path) { cairo_box_t box; if (_cairo_path_fixed_is_box (path, &box)) { if (box.p1.x <= 0 && box.p1.y <= 0 && _cairo_fixed_to_double (box.p2.x) >= surface->width && _cairo_fixed_to_double (box.p2.y) >= surface->height) { return TRUE; } } return FALSE; } static cairo_status_t _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_svg_surface_t *surface = cairo_container_of (clipper, cairo_svg_surface_t, clipper); cairo_svg_document_t *document = surface->document; unsigned int i; if (path == NULL) { for (i = 0; i < surface->clip_level; i++) _cairo_output_stream_printf (surface->xml_node, "\n"); surface->clip_level = 0; return CAIRO_STATUS_SUCCESS; } /* skip trivial whole-page clips */ if (_cliprect_covers_surface (surface, path)) return CAIRO_STATUS_SUCCESS; _cairo_output_stream_printf (document->xml_node_defs, "\n" " clip_id); _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); _cairo_output_stream_printf (document->xml_node_defs, "/>\n" "\n"); _cairo_output_stream_printf (surface->xml_node, "\n", document->clip_id, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd" : "nonzero"); document->clip_id++; surface->clip_level++; return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_content_t content, double width, double height) { cairo_svg_surface_t *surface; cairo_surface_t *paginated; cairo_status_t status, status_ignored; surface = malloc (sizeof (cairo_svg_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_svg_surface_backend, NULL, /* device */ content); surface->width = width; surface->height = height; surface->document = _cairo_svg_document_reference (document); surface->clip_level = 0; _cairo_surface_clipper_init (&surface->clipper, _cairo_svg_surface_clipper_intersect_clip_path); surface->base_clip = document->clip_id++; surface->is_base_clip_emitted = FALSE; surface->xml_node = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (surface->xml_node); if (unlikely (status)) goto CLEANUP; _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); if (content == CAIRO_CONTENT_COLOR) { _cairo_output_stream_printf (surface->xml_node, "\n", width, height); status = _cairo_output_stream_get_status (surface->xml_node); if (unlikely (status)) goto CLEANUP; } surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->content = content; paginated = _cairo_paginated_surface_create (&surface->base, surface->content, &cairo_svg_surface_paginated_backend); status = paginated->status; if (status == CAIRO_STATUS_SUCCESS) { /* paginated keeps the only reference to surface now, drop ours */ cairo_surface_destroy (&surface->base); return paginated; } /* ignore status as we are on the error path */ CLEANUP: status_ignored = _cairo_output_stream_destroy (surface->xml_node); status_ignored = _cairo_svg_document_destroy (document); free (surface); return _cairo_surface_create_in_error (status); } static cairo_surface_t * _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, double width, double height, cairo_svg_version_t version) { cairo_svg_document_t *document = NULL; /* silence compiler */ cairo_surface_t *surface; cairo_status_t status; status = _cairo_svg_document_create (stream, width, height, version, &document); if (unlikely (status)) { surface = _cairo_surface_create_in_error (status); /* consume the output stream on behalf of caller */ status = _cairo_output_stream_destroy (stream); return surface; } surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, width, height); if (surface->status) { status = _cairo_svg_document_destroy (document); return surface; } document->owner = surface; status = _cairo_svg_document_destroy (document); /* the ref count should be 2 at this point */ assert (status == CAIRO_STATUS_SUCCESS); return surface; } static cairo_svg_page_t * _cairo_svg_surface_store_page (cairo_svg_surface_t *surface) { cairo_svg_page_t page; cairo_output_stream_t *stream; cairo_int_status_t status; unsigned int i; stream = _cairo_memory_stream_create (); if (_cairo_output_stream_get_status (stream)) { status = _cairo_output_stream_destroy (stream); return NULL; } page.surface_id = surface->base.unique_id; page.clip_level = surface->clip_level; page.xml_node = surface->xml_node; if (_cairo_array_append (&surface->page_set, &page)) { status = _cairo_output_stream_destroy (stream); return NULL; } surface->xml_node = stream; surface->clip_level = 0; for (i = 0; i < page.clip_level; i++) _cairo_output_stream_printf (page.xml_node, "\n"); _cairo_surface_clipper_reset (&surface->clipper); return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); } static cairo_int_status_t _cairo_svg_surface_copy_page (void *abstract_surface) { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_page_t *page; page = _cairo_svg_surface_store_page (surface); if (unlikely (page == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_memory_stream_copy (page->xml_node, surface->xml_node); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_show_page (void *abstract_surface) { cairo_svg_surface_t *surface = abstract_surface; if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static void _cairo_svg_surface_emit_transform (cairo_output_stream_t *output, char const *attribute_str, const cairo_matrix_t *object_matrix, const cairo_matrix_t *parent_matrix) { cairo_matrix_t matrix = *object_matrix; if (parent_matrix != NULL) cairo_matrix_multiply (&matrix, &matrix, parent_matrix); if (!_cairo_matrix_is_identity (&matrix)) _cairo_output_stream_printf (output, "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"", attribute_str, matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0); } typedef struct { cairo_output_stream_t *output; const cairo_matrix_t *ctm_inverse; } svg_path_info_t; static cairo_status_t _cairo_svg_path_move_to (void *closure, const cairo_point_t *point) { svg_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); _cairo_output_stream_printf (info->output, "M %f %f ", x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_path_line_to (void *closure, const cairo_point_t *point) { svg_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->ctm_inverse) cairo_matrix_transform_point (info->ctm_inverse, &x, &y); _cairo_output_stream_printf (info->output, "L %f %f ", x, y); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_path_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { svg_path_info_t *info = closure; double bx = _cairo_fixed_to_double (b->x); double by = _cairo_fixed_to_double (b->y); double cx = _cairo_fixed_to_double (c->x); double cy = _cairo_fixed_to_double (c->y); double dx = _cairo_fixed_to_double (d->x); double dy = _cairo_fixed_to_double (d->y); if (info->ctm_inverse) { cairo_matrix_transform_point (info->ctm_inverse, &bx, &by); cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy); cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); } _cairo_output_stream_printf (info->output, "C %f %f %f %f %f %f ", bx, by, cx, cy, dx, dy); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_path_close_path (void *closure) { svg_path_info_t *info = closure; _cairo_output_stream_printf (info->output, "Z "); return CAIRO_STATUS_SUCCESS; } static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse) { cairo_status_t status; svg_path_info_t info; _cairo_output_stream_printf (output, "d=\""); info.output = output; info.ctm_inverse = ctm_inverse; status = _cairo_path_fixed_interpret (path, _cairo_svg_path_move_to, _cairo_svg_path_line_to, _cairo_svg_path_curve_to, _cairo_svg_path_close_path, &info); assert (status == CAIRO_STATUS_SUCCESS); _cairo_output_stream_printf (output, "\""); } static cairo_int_status_t _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, unsigned long glyph_index) { cairo_scaled_glyph_t *scaled_glyph; cairo_int_status_t status; status = _cairo_scaled_glyph_lookup (scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, scaled_glyph->path, NULL); _cairo_output_stream_printf (document->xml_node_glyphs, "/>\n"); return status; } static cairo_int_status_t _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, unsigned long glyph_index) { cairo_scaled_glyph_t *scaled_glyph; cairo_image_surface_t *image; cairo_status_t status; uint8_t *row, *byte; int rows, cols; int x, y, bit; status = _cairo_scaled_glyph_lookup (scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) return status; image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface, CAIRO_FORMAT_A1); status = image->base.status; if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, " transform", &image->base.device_transform_inverse, NULL); _cairo_output_stream_printf (document->xml_node_glyphs, ">/n"); for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) { for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); for (bit = 7; bit >= 0 && x < image->width; bit--, x++) { if (output_byte & (1 << bit)) { _cairo_output_stream_printf (document->xml_node_glyphs, "\n", x, y); } } } } _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); cairo_surface_destroy (&image->base); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, unsigned int font_id, unsigned int subset_glyph_index) { cairo_int_status_t status; _cairo_output_stream_printf (document->xml_node_glyphs, "\n", font_id, subset_glyph_index); status = _cairo_svg_document_emit_outline_glyph_data (document, scaled_font, scaled_font_glyph_index); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = _cairo_svg_document_emit_bitmap_glyph_data (document, scaled_font, scaled_font_glyph_index); if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_svg_document_t *document = closure; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; unsigned int i; _cairo_scaled_font_freeze_cache (font_subset->scaled_font); for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_svg_document_emit_glyph (document, font_subset->scaled_font, font_subset->glyphs[i], font_subset->font_id, i); if (unlikely (status)) break; } _cairo_scaled_font_thaw_cache (font_subset->scaled_font); return status; } static cairo_status_t _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) { cairo_status_t status; status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, _cairo_svg_document_emit_font_subset, document); if (unlikely (status)) goto FAIL; status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, _cairo_svg_document_emit_font_subset, document); FAIL: _cairo_scaled_font_subsets_destroy (document->font_subsets); document->font_subsets = NULL; return status; } static char const * _cairo_svg_surface_operators[] = { "clear", "src", "src-over", "src-in", "src-out", "src-atop", "dst", "dst-over", "dst-in", "dst-out", "dst-atop", "xor", "plus", "color-dodge", /* FIXME: saturate ? */ "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion" }; static cairo_bool_t _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface, cairo_operator_t op) { /* guard against newly added operators */ if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators)) return CAIRO_INT_STATUS_UNSUPPORTED; /* allow operators being NULL if they are unsupported */ if (_cairo_svg_surface_operators[op] == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern) { cairo_svg_document_t *document = surface->document; if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (pattern->type == CAIRO_PATTERN_TYPE_MESH) return CAIRO_INT_STATUS_UNSUPPORTED; /* SVG doesn't support extend reflect for image pattern */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && pattern->extend == CAIRO_EXTEND_REFLECT) return CAIRO_INT_STATUS_UNSUPPORTED; if (document->svg_version >= CAIRO_SVG_VERSION_1_2) return _cairo_svg_surface_analyze_operator (surface, op); if (op == CAIRO_OPERATOR_OVER) return CAIRO_STATUS_SUCCESS; /* The SOURCE operator is only supported if there is nothing * painted underneath. */ if (op == CAIRO_OPERATOR_SOURCE) return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern) { return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _cairo_svg_surface_finish (void *abstract_surface) { cairo_status_t status, status2; cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; cairo_svg_page_t *page; unsigned int i; if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) status = _cairo_svg_document_finish (document); else status = CAIRO_STATUS_SUCCESS; if (surface->xml_node != NULL) { status2 = _cairo_output_stream_destroy (surface->xml_node); if (status == CAIRO_STATUS_SUCCESS) status = status2; } for (i = 0; i < surface->page_set.num_elements; i++) { page = _cairo_array_index (&surface->page_set, i); status2 = _cairo_output_stream_destroy (page->xml_node); if (status == CAIRO_STATUS_SUCCESS) status = status2; } _cairo_array_fini (&surface->page_set); _cairo_surface_clipper_reset (&surface->clipper); status2 = _cairo_svg_document_destroy (document); if (status == CAIRO_STATUS_SUCCESS) status = status2; return status; } static void _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) { if (document->alpha_filter) return; _cairo_output_stream_printf (document->xml_node_defs, "\n" " \n" "\n"); document->alpha_filter = TRUE; } typedef struct { cairo_output_stream_t *output; unsigned int in_mem; unsigned int trailing; unsigned char src[3]; } base64_write_closure_t; static char const base64_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static cairo_status_t base64_write_func (void *closure, const unsigned char *data, unsigned int length) { base64_write_closure_t *info = (base64_write_closure_t *) closure; unsigned int i; unsigned char *src; src = info->src; if (info->in_mem + length < 3) { for (i = 0; i < length; i++) { src[i + info->in_mem] = *data++; } info->in_mem += length; return CAIRO_STATUS_SUCCESS; } do { unsigned char dst[4]; for (i = info->in_mem; i < 3; i++) { src[i] = *data++; length--; } info->in_mem = 0; dst[0] = base64_table[src[0] >> 2]; dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; dst[3] = base64_table[src[2] & 0xfc >> 2]; /* Special case for the last missing bits */ switch (info->trailing) { case 2: dst[2] = '='; case 1: dst[3] = '='; default: break; } _cairo_output_stream_write (info->output, dst, 4); } while (length >= 3); for (i = 0; i < length; i++) { src[i] = *data++; } info->in_mem = length; return _cairo_output_stream_get_status (info->output); } static cairo_int_status_t _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, cairo_output_stream_t *output) { const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t image_info; base64_write_closure_t info; cairo_status_t status; cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); if (unlikely (status)) return status; _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); info.output = output; info.in_mem = 0; info.trailing = 0; status = base64_write_func (&info, mime_data, mime_data_length); if (unlikely (status)) return status; if (info.in_mem > 0) { memset (info.src + info.in_mem, 0, 3 - info.in_mem); info.trailing = 3 - info.in_mem; info.in_mem = 3; status = base64_write_func (&info, NULL, 0); } return status; } static cairo_int_status_t _cairo_surface_base64_encode_png (cairo_surface_t *surface, cairo_output_stream_t *output) { const unsigned char *mime_data; unsigned long mime_data_length; base64_write_closure_t info; cairo_status_t status; cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, &mime_data, &mime_data_length); if (unlikely (surface->status)) return surface->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_printf (output, "data:image/png;base64,"); info.output = output; info.in_mem = 0; info.trailing = 0; status = base64_write_func (&info, mime_data, mime_data_length); if (unlikely (status)) return status; if (info.in_mem > 0) { memset (info.src + info.in_mem, 0, 3 - info.in_mem); info.trailing = 3 - info.in_mem; info.in_mem = 3; status = base64_write_func (&info, NULL, 0); } return status; } static cairo_int_status_t _cairo_surface_base64_encode (cairo_surface_t *surface, cairo_output_stream_t *output) { cairo_int_status_t status; base64_write_closure_t info; status = _cairo_surface_base64_encode_jpeg (surface, output); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; status = _cairo_surface_base64_encode_png (surface, output); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; info.output = output; info.in_mem = 0; info.trailing = 0; _cairo_output_stream_printf (info.output, "data:image/png;base64,"); status = cairo_surface_write_to_png_stream (surface, base64_write_func, (void *) &info); if (unlikely (status)) return status; if (info.in_mem > 0) { memset (info.src + info.in_mem, 0, 3 - info.in_mem); info.trailing = 3 - info.in_mem; info.in_mem = 3; status = base64_write_func (&info, NULL, 0); } return status; } static void _cairo_svg_surface_emit_operator (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op) { if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && op != CAIRO_OPERATOR_OVER) { _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]); if (!_cairo_operator_bounded_by_source (op)) _cairo_output_stream_printf (output, " clip-to-self=\"true\""); } } static void _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op) { if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && op != CAIRO_OPERATOR_OVER) { _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]); if (!_cairo_operator_bounded_by_source (op)) _cairo_output_stream_printf (output, "clip-to-self:true;"); } } /** * _cairo_svg_surface_emit_attr_value: * * Write the value to output the stream as a sequence of characters, * while escaping those which have special meaning in the XML * attribute's value context: & and ". **/ static void _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, const unsigned char *value, unsigned int length) { const unsigned char *p; const unsigned char *q; unsigned int i; /* we'll accumulate non-special chars in [q, p) range */ p = value; q = p; for (i = 0; i < length; i++, p++) { if (*p == '&' || *p == '"') { /* flush what's left before special char */ if (p != q) { _cairo_output_stream_write (stream, q, p - q); q = p + 1; } if (*p == '&') _cairo_output_stream_printf (stream, "&"); else // p == '"' _cairo_output_stream_printf (stream, """); } } /* flush the trailing chars if any */ if (p != q) _cairo_output_stream_write (stream, q, p - q); } static cairo_status_t _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, cairo_surface_t *surface) { cairo_rectangle_int_t extents; cairo_bool_t is_bounded; cairo_status_t status; const unsigned char *uri; unsigned long uri_len; if (_cairo_user_data_array_get_data (&surface->user_data, (cairo_user_data_key_t *) document)) { return CAIRO_STATUS_SUCCESS; } is_bounded = _cairo_surface_get_extents (surface, &extents); assert (is_bounded); _cairo_output_stream_printf (document->xml_node_defs, "unique_id, extents.width, extents.height); _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\""); cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI, &uri, &uri_len); if (uri != NULL) { _cairo_svg_surface_emit_attr_value (document->xml_node_defs, uri, uri_len); } else { status = _cairo_surface_base64_encode (surface, document->xml_node_defs); if (unlikely (status)) return status; } _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n"); /* and tag it */ return _cairo_user_data_array_set_data (&surface->user_data, (cairo_user_data_key_t *) document, document, NULL); } static cairo_status_t _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *svg_surface, cairo_operator_t op, cairo_surface_pattern_t *pattern, int pattern_id, const cairo_matrix_t *parent_matrix, const char *extra_attributes) { cairo_status_t status; cairo_matrix_t p2u; p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_svg_surface_emit_surface (svg_surface->document, pattern->surface); if (unlikely (status)) return status; if (pattern_id != invalid_pattern_id) { cairo_rectangle_int_t extents; cairo_bool_t is_bounded; is_bounded = _cairo_surface_get_extents (pattern->surface, &extents); assert (is_bounded); _cairo_output_stream_printf (output, "\n "); } _cairo_output_stream_printf (output, "surface->unique_id); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); if (pattern_id == invalid_pattern_id) { _cairo_svg_surface_emit_operator (output, svg_surface, op); _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix); } _cairo_output_stream_printf (output, "/>\n"); if (pattern_id != invalid_pattern_id) _cairo_output_stream_printf (output, "\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, cairo_recording_surface_t *source) { cairo_status_t status; cairo_surface_t *paginated_surface; cairo_svg_surface_t *svg_surface; cairo_array_t *page_set; cairo_output_stream_t *contents; if (_cairo_user_data_array_get_data (&source->base.user_data, (cairo_user_data_key_t *) document)) { return CAIRO_STATUS_SUCCESS; } paginated_surface = _cairo_svg_surface_create_for_document (document, source->base.content, source->extents_pixels.width, source->extents_pixels.height); if (unlikely (paginated_surface->status)) return paginated_surface->status; svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface); cairo_surface_set_fallback_resolution (paginated_surface, document->owner->x_fallback_resolution, document->owner->y_fallback_resolution); cairo_surface_set_device_offset (&svg_surface->base, -source->extents_pixels.x, -source->extents_pixels.y); status = _cairo_recording_surface_replay (&source->base, paginated_surface); if (unlikely (status)) { cairo_surface_destroy (paginated_surface); return status; } cairo_surface_show_page (paginated_surface); status = cairo_surface_status (paginated_surface); if (unlikely (status)) { cairo_surface_destroy (paginated_surface); return status; } if (! svg_surface->is_base_clip_emitted) { svg_surface->is_base_clip_emitted = TRUE; _cairo_output_stream_printf (document->xml_node_defs, "\n" " \n" "\n", svg_surface->base_clip, svg_surface->width, svg_surface->height); } if (source->base.content == CAIRO_CONTENT_ALPHA) { _cairo_svg_surface_emit_alpha_filter (document); _cairo_output_stream_printf (document->xml_node_defs, "\n", source->base.unique_id, svg_surface->base_clip); } else { _cairo_output_stream_printf (document->xml_node_defs, "\n", source->base.unique_id, svg_surface->base_clip); } contents = svg_surface->xml_node; page_set = &svg_surface->page_set; if (_cairo_memory_stream_length (contents) > 0) { if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) { cairo_surface_destroy (paginated_surface); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } if (page_set->num_elements > 0) { cairo_svg_page_t *page; page = _cairo_array_index (page_set, page_set->num_elements - 1); _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs); } _cairo_output_stream_printf (document->xml_node_defs, "\n"); status = cairo_surface_status (paginated_surface); cairo_surface_destroy (paginated_surface); if (unlikely (status)) return status; /* and tag it */ return _cairo_user_data_array_set_data (&source->base.user_data, (cairo_user_data_key_t *) document, document, NULL); } static cairo_recording_surface_t * to_recording_surface (const cairo_surface_pattern_t *pattern) { cairo_surface_t *surface = pattern->surface; if (_cairo_surface_is_paginated (surface)) surface = _cairo_paginated_surface_get_recording (surface); if (_cairo_surface_is_snapshot (surface)) surface = _cairo_surface_snapshot_get_target (surface); return (cairo_recording_surface_t *) surface; } static cairo_status_t _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, cairo_surface_pattern_t *pattern, int pattern_id, const cairo_matrix_t *parent_matrix, const char *extra_attributes) { cairo_svg_document_t *document = surface->document; cairo_recording_surface_t *recording_surface; cairo_matrix_t p2u; cairo_status_t status; p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); recording_surface = to_recording_surface (pattern); status = _cairo_svg_surface_emit_recording_surface (document, recording_surface); if (unlikely (status)) return status; if (pattern_id != invalid_pattern_id) { _cairo_output_stream_printf (output, "extents.width, recording_surface->extents.height); _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix); _cairo_output_stream_printf (output, ">\n"); } _cairo_output_stream_printf (output, "base.unique_id); if (pattern_id == invalid_pattern_id) { _cairo_svg_surface_emit_operator (output, surface, op); _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix); } if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); _cairo_output_stream_printf (output, "/>\n"); if (pattern_id != invalid_pattern_id) _cairo_output_stream_printf (output, "\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, cairo_surface_pattern_t *pattern, int pattern_id, const cairo_matrix_t *parent_matrix, const char *extra_attributes) { if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { return _cairo_svg_surface_emit_composite_recording_pattern (output, surface, op, pattern, pattern_id, parent_matrix, extra_attributes); } return _cairo_svg_surface_emit_composite_surface_pattern (output, surface, op, pattern, pattern_id, parent_matrix, extra_attributes); } static cairo_status_t _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface, cairo_solid_pattern_t *pattern, cairo_output_stream_t *style, cairo_bool_t is_stroke) { _cairo_output_stream_printf (style, is_stroke ? "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;": "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;", pattern->color.red * 100.0, pattern->color.green * 100.0, pattern->color.blue * 100.0, pattern->color.alpha); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, cairo_surface_pattern_t *pattern, cairo_output_stream_t *style, cairo_bool_t is_stroke, const cairo_matrix_t *parent_matrix) { cairo_svg_document_t *document = surface->document; cairo_status_t status; int pattern_id; pattern_id = document->pattern_id++; status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs, surface, CAIRO_OPERATOR_SOURCE, pattern, pattern_id, parent_matrix, NULL); if (unlikely (status)) return status; _cairo_output_stream_printf (style, "%s:url(#pattern%d);", is_stroke ? "stroke" : "fill", pattern_id); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, cairo_gradient_pattern_t const *pattern, double start_offset, cairo_bool_t reverse_stops, cairo_bool_t emulate_reflect) { cairo_gradient_stop_t *stops; double offset; unsigned int n_stops; unsigned int i; if (pattern->n_stops < 1) return CAIRO_STATUS_SUCCESS; if (pattern->n_stops == 1) { _cairo_output_stream_printf (output, "\n", pattern->stops[0].offset, pattern->stops[0].color.red * 100.0, pattern->stops[0].color.green * 100.0, pattern->stops[0].color.blue * 100.0, pattern->stops[0].color.alpha); return CAIRO_STATUS_SUCCESS; } if (emulate_reflect || reverse_stops) { n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); if (unlikely (stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < pattern->n_stops; i++) { if (reverse_stops) { stops[i] = pattern->stops[pattern->n_stops - i - 1]; stops[i].offset = 1.0 - stops[i].offset; } else stops[i] = pattern->stops[i]; if (emulate_reflect) { stops[i].offset /= 2; if (i > 0 && i < (pattern->n_stops - 1)) { if (reverse_stops) { stops[i + pattern->n_stops - 1] = pattern->stops[i]; stops[i + pattern->n_stops - 1].offset = 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset; } else { stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1]; stops[i + pattern->n_stops - 1].offset = 1 - 0.5 * stops[i + pattern->n_stops - 1].offset; } } } } } else { n_stops = pattern->n_stops; stops = pattern->stops; } if (start_offset >= 0.0) for (i = 0; i < n_stops; i++) { offset = start_offset + (1 - start_offset ) * stops[i].offset; _cairo_output_stream_printf (output, "\n", offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, stops[i].color.blue * 100.0, stops[i].color.alpha); } else { cairo_bool_t found = FALSE; unsigned int offset_index; cairo_color_stop_t offset_color_start, offset_color_stop; for (i = 0; i < n_stops; i++) { if (stops[i].offset >= -start_offset) { if (i > 0) { if (stops[i].offset != stops[i-1].offset) { double x0, x1; cairo_color_stop_t *color0, *color1; x0 = stops[i-1].offset; x1 = stops[i].offset; color0 = &stops[i-1].color; color1 = &stops[i].color; offset_color_start.red = color0->red + (color1->red - color0->red) * (-start_offset - x0) / (x1 - x0); offset_color_start.green = color0->green + (color1->green - color0->green) * (-start_offset - x0) / (x1 - x0); offset_color_start.blue = color0->blue + (color1->blue - color0->blue) * (-start_offset - x0) / (x1 - x0); offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha) * (-start_offset - x0) / (x1 - x0); offset_color_stop = offset_color_start; } else { offset_color_stop = stops[i-1].color; offset_color_start = stops[i].color; } } else offset_color_stop = offset_color_start = stops[i].color; offset_index = i; found = TRUE; break; } } if (!found) { offset_index = n_stops - 1; offset_color_stop = offset_color_start = stops[offset_index].color; } _cairo_output_stream_printf (output, "\n", offset_color_start.red * 100.0, offset_color_start.green * 100.0, offset_color_start.blue * 100.0, offset_color_start.alpha); for (i = offset_index; i < n_stops; i++) { _cairo_output_stream_printf (output, "\n", stops[i].offset + start_offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, stops[i].color.blue * 100.0, stops[i].color.alpha); } for (i = 0; i < offset_index; i++) { _cairo_output_stream_printf (output, "\n", 1.0 + stops[i].offset + start_offset, stops[i].color.red * 100.0, stops[i].color.green * 100.0, stops[i].color.blue * 100.0, stops[i].color.alpha); } _cairo_output_stream_printf (output, "\n", offset_color_stop.red * 100.0, offset_color_stop.green * 100.0, offset_color_stop.blue * 100.0, offset_color_stop.alpha); } if (reverse_stops || emulate_reflect) free (stops); return CAIRO_STATUS_SUCCESS; } static void _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output, cairo_pattern_t *pattern) { switch (pattern->extend) { case CAIRO_EXTEND_REPEAT: _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); break; case CAIRO_EXTEND_REFLECT: _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); break; case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_PAD: break; } } static cairo_status_t _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, cairo_linear_pattern_t *pattern, cairo_output_stream_t *style, cairo_bool_t is_stroke, const cairo_matrix_t *parent_matrix) { cairo_svg_document_t *document = surface->document; cairo_matrix_t p2u; cairo_status_t status; p2u = pattern->base.base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); _cairo_output_stream_printf (document->xml_node_defs, "linear_pattern_id, pattern->pd1.x, pattern->pd1.y, pattern->pd2.x, pattern->pd2.y); _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base), _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); _cairo_output_stream_printf (document->xml_node_defs, ">\n"); status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, &pattern->base, 0.0, FALSE, FALSE); if (unlikely (status)) return status; _cairo_output_stream_printf (document->xml_node_defs, "\n"); _cairo_output_stream_printf (style, "%s:url(#linear%d);", is_stroke ? "stroke" : "fill", document->linear_pattern_id); document->linear_pattern_id++; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, cairo_radial_pattern_t *pattern, cairo_output_stream_t *style, cairo_bool_t is_stroke, const cairo_matrix_t *parent_matrix) { cairo_svg_document_t *document = surface->document; cairo_matrix_t p2u; cairo_extend_t extend; double x0, y0, x1, y1, r0, r1; double fx, fy; cairo_bool_t reverse_stops; cairo_status_t status; cairo_circle_double_t *c0, *c1; extend = pattern->base.base.extend; if (pattern->cd1.radius < pattern->cd2.radius) { c0 = &pattern->cd1; c1 = &pattern->cd2; reverse_stops = FALSE; } else { c0 = &pattern->cd2; c1 = &pattern->cd1; reverse_stops = TRUE; } x0 = c0->center.x; y0 = c0->center.y; r0 = c0->radius; x1 = c1->center.x; y1 = c1->center.y; r1 = c1->radius; p2u = pattern->base.base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); if (r0 == r1) { unsigned int n_stops = pattern->base.n_stops; _cairo_output_stream_printf (document->xml_node_defs, "radial_pattern_id, x1, y1, x1, y1, r1); _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); _cairo_output_stream_printf (document->xml_node_defs, ">\n"); if (extend == CAIRO_EXTEND_NONE || n_stops < 1) _cairo_output_stream_printf (document->xml_node_defs, "\n"); else { _cairo_output_stream_printf (document->xml_node_defs, "\n", pattern->base.stops[0].color.red * 100.0, pattern->base.stops[0].color.green * 100.0, pattern->base.stops[0].color.blue * 100.0, pattern->base.stops[0].color.alpha); if (n_stops > 1) _cairo_output_stream_printf (document->xml_node_defs, "\n", pattern->base.stops[n_stops - 1].color.red * 100.0, pattern->base.stops[n_stops - 1].color.green * 100.0, pattern->base.stops[n_stops - 1].color.blue * 100.0, pattern->base.stops[n_stops - 1].color.alpha); } } else { double offset, r, x, y; cairo_bool_t emulate_reflect = FALSE; fx = (r1 * x0 - r0 * x1) / (r1 - r0); fy = (r1 * y0 - r0 * y1) / (r1 - r0); /* SVG doesn't support the inner circle and use instead a gradient focal. * That means we need to emulate the cairo behaviour by processing the * cairo gradient stops. * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, * it's just a matter of stop position translation and calculation of * the corresponding SVG radial gradient focal. * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop * list that maps to the original cairo stop list. */ if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { double r_org = r1; if (extend == CAIRO_EXTEND_REFLECT) { r1 = 2 * r1 - r0; emulate_reflect = TRUE; } offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; r = r1 - r0; /* New position of outer circle. */ x = r * (x1 - fx) / r_org + fx; y = r * (y1 - fy) / r_org + fy; x1 = x; y1 = y; r1 = r; r0 = 0.0; } else { offset = r0 / r1; } _cairo_output_stream_printf (document->xml_node_defs, "radial_pattern_id, x1, y1, fx, fy, r1); if (emulate_reflect) _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" "); else _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base); _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); _cairo_output_stream_printf (document->xml_node_defs, ">\n"); /* To support cairo's EXTEND_NONE, (for which SVG has no similar * notion), we add transparent color stops on either end of the * user-provided stops. */ if (extend == CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (document->xml_node_defs, "\n"); if (r0 != 0.0) _cairo_output_stream_printf (document->xml_node_defs, "\n", r0 / r1); } status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, &pattern->base, offset, reverse_stops, emulate_reflect); if (unlikely (status)) return status; if (pattern->base.base.extend == CAIRO_EXTEND_NONE) _cairo_output_stream_printf (document->xml_node_defs, "\n"); } _cairo_output_stream_printf (document->xml_node_defs, "\n"); _cairo_output_stream_printf (style, "%s:url(#radial%d);", is_stroke ? "stroke" : "fill", document->radial_pattern_id); document->radial_pattern_id++; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, const cairo_pattern_t *pattern, cairo_output_stream_t *output, cairo_bool_t is_stroke, const cairo_matrix_t *parent_matrix) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, output, is_stroke); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, output, is_stroke, parent_matrix); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, output, is_stroke, parent_matrix); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, output, is_stroke, parent_matrix); case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: ASSERT_NOT_REACHED; } return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); } static cairo_status_t _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_fill_rule_t fill_rule, const cairo_matrix_t *parent_matrix) { _cairo_output_stream_printf (output, "fill-rule:%s;", fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd" : "nonzero"); _cairo_svg_surface_emit_operator_for_style (output, surface, op); return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); } static cairo_status_t _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *parent_matrix) { cairo_status_t status; const char *line_cap, *line_join; unsigned int i; switch (stroke_style->line_cap) { case CAIRO_LINE_CAP_BUTT: line_cap = "butt"; break; case CAIRO_LINE_CAP_ROUND: line_cap = "round"; break; case CAIRO_LINE_CAP_SQUARE: line_cap = "square"; break; default: ASSERT_NOT_REACHED; } switch (stroke_style->line_join) { case CAIRO_LINE_JOIN_MITER: line_join = "miter"; break; case CAIRO_LINE_JOIN_ROUND: line_join = "round"; break; case CAIRO_LINE_JOIN_BEVEL: line_join = "bevel"; break; default: ASSERT_NOT_REACHED; } _cairo_output_stream_printf (output, "stroke-width:%f;" "stroke-linecap:%s;" "stroke-linejoin:%s;", stroke_style->line_width, line_cap, line_join); status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); if (unlikely (status)) return status; _cairo_svg_surface_emit_operator_for_style (output, surface, op); if (stroke_style->num_dashes > 0) { _cairo_output_stream_printf (output, "stroke-dasharray:"); for (i = 0; i < stroke_style->num_dashes; i++) { _cairo_output_stream_printf (output, "%f", stroke_style->dash[i]); if (i + 1 < stroke_style->num_dashes) _cairo_output_stream_printf (output, ","); else _cairo_output_stream_printf (output, ";"); } if (stroke_style->dash_offset != 0.0) { _cairo_output_stream_printf (output, "stroke-dashoffset:%f;", stroke_style->dash_offset); } } _cairo_output_stream_printf (output, "stroke-miterlimit:%f;", stroke_style->miter_limit); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_fill_stroke (void *abstract_surface, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, fill_op, fill_source, fill_rule, stroke_ctm_inverse); if (unlikely (status)) return status; status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, stroke_source, stroke_style, stroke_ctm_inverse); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_svg_surface_analyze_operation (surface, op, source); assert (_cairo_svg_surface_operation_supported (surface, op, source)); status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, source, fill_rule, NULL); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_svg_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_svg_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; /* XXX: The conversion to integers here is pretty bogus, (not to * mention the arbitrary limitation of width to a short(!). We * may need to come up with a better interface for get_size. */ rectangle->width = ceil (surface->width); rectangle->height = ceil (surface->height); return TRUE; } static cairo_status_t _cairo_svg_surface_emit_paint (cairo_output_stream_t *output, cairo_svg_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask_source, const char *extra_attributes) { cairo_status_t status; if (source->type == CAIRO_PATTERN_TYPE_SURFACE && source->extend == CAIRO_EXTEND_NONE) return _cairo_svg_surface_emit_composite_pattern (output, surface, op, (cairo_surface_pattern_t *) source, invalid_pattern_id, mask_source ? &mask_source->matrix :NULL, extra_attributes); _cairo_output_stream_printf (output, "width, surface->height); _cairo_svg_surface_emit_operator_for_style (output, surface, op); status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); if (unlikely (status)) return status; _cairo_output_stream_printf (output, "stroke:none;\""); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); _cairo_output_stream_printf (output, "/>\n"); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; /* Emulation of clear and source operators, when no clipping region * is defined. We just delete existing content of surface root node, * and exit early if operator is clear. */ if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && clip == NULL) { switch (surface->paginated_mode) { case CAIRO_PAGINATED_MODE_FALLBACK: ASSERT_NOT_REACHED; case CAIRO_PAGINATED_MODE_ANALYZE: return CAIRO_STATUS_SUCCESS; case CAIRO_PAGINATED_MODE_RENDER: status = _cairo_output_stream_destroy (surface->xml_node); if (unlikely (status)) { surface->xml_node = NULL; return status; } surface->xml_node = _cairo_memory_stream_create (); if (_cairo_output_stream_get_status (surface->xml_node)) { status = _cairo_output_stream_destroy (surface->xml_node); surface->xml_node = NULL; return status; } if (op == CAIRO_OPERATOR_CLEAR) { if (surface->content == CAIRO_CONTENT_COLOR) { _cairo_output_stream_printf (surface->xml_node, "\n", surface->width, surface->height); } return CAIRO_STATUS_SUCCESS; } break; } } else { if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_svg_surface_analyze_operation (surface, op, source); assert (_cairo_svg_surface_operation_supported (surface, op, source)); } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; return _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, NULL); } static cairo_int_status_t _cairo_svg_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; cairo_output_stream_t *mask_stream; char buffer[64]; cairo_bool_t discard_filter = FALSE; unsigned int mask_id; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { cairo_status_t source_status, mask_status; source_status = _cairo_svg_surface_analyze_operation (surface, op, source); if (_cairo_status_is_error (source_status)) return source_status; if (mask->has_component_alpha) { mask_status = CAIRO_INT_STATUS_UNSUPPORTED; } else { mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); if (_cairo_status_is_error (mask_status)) return mask_status; } return _cairo_analysis_surface_merge_status (source_status, mask_status); } assert (_cairo_svg_surface_operation_supported (surface, op, source)); assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; cairo_content_t content = surface_pattern->surface->content; if (content == CAIRO_CONTENT_ALPHA) discard_filter = TRUE; } if (!discard_filter) _cairo_svg_surface_emit_alpha_filter (document); /* _cairo_svg_surface_emit_paint() will output a pattern definition to * document->xml_node_defs so we need to write the mask element to * a temporary stream and then copy that to xml_node_defs. */ mask_stream = _cairo_memory_stream_create (); if (_cairo_output_stream_get_status (mask_stream)) return _cairo_output_stream_destroy (mask_stream); mask_id = _cairo_svg_document_allocate_mask_id (document); _cairo_output_stream_printf (mask_stream, "\n" "%s", mask_id, discard_filter ? "" : " \n"); status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); if (unlikely (status)) { cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); return status; (void) ignore; } _cairo_output_stream_printf (mask_stream, "%s" "\n", discard_filter ? "" : " \n"); _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); status = _cairo_output_stream_destroy (mask_stream); if (unlikely (status)) return status; snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", mask_id); status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_stroke (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_dst; cairo_status_t status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_svg_surface_analyze_operation (surface, op, source); assert (_cairo_svg_surface_operation_supported (surface, op, source)); status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, source, stroke_style, ctm_inverse); if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, "\" "); _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); _cairo_output_stream_printf (surface->xml_node, "/>\n"); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; cairo_path_fixed_t path; cairo_int_status_t status; cairo_scaled_font_subsets_glyph_t subset_glyph; int i; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_svg_surface_analyze_operation (surface, op, pattern); assert (_cairo_svg_surface_operation_supported (surface, op, pattern)); if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; /* FIXME it's probably possible to apply a pattern of a gradient to * a group of symbols, but I don't know how yet. Gradients or patterns * are translated by x and y properties of use element. */ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) goto FALLBACK; _cairo_output_stream_printf (surface->xml_node, "xml_node, FALSE, NULL); if (unlikely (status)) return status; _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); _cairo_output_stream_printf (surface->xml_node, "\">\n"); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, scaled_font, glyphs[i].index, NULL, 0, &subset_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { _cairo_output_stream_printf (surface->xml_node, "\n"); glyphs += i; num_glyphs -= i; goto FALLBACK; } if (unlikely (status)) return status; _cairo_output_stream_printf (surface->xml_node, " \n", subset_glyph.font_id, subset_glyph.subset_glyph_index, glyphs[i].x, glyphs[i].y); } _cairo_output_stream_printf (surface->xml_node, "\n"); return CAIRO_STATUS_SUCCESS; FALLBACK: _cairo_path_fixed_init (&path); status = _cairo_scaled_font_glyph_path (scaled_font, (cairo_glyph_t *) glyphs, num_glyphs, &path); if (unlikely (status)) { _cairo_path_fixed_fini (&path); return status; } status = _cairo_svg_surface_fill (abstract_surface, op, pattern, &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL, clip); _cairo_path_fixed_fini (&path); return status; } static void _cairo_svg_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static const char ** _cairo_svg_surface_get_supported_mime_types (void *abstract_surface) { return _cairo_svg_supported_mime_types; } static const cairo_surface_backend_t cairo_svg_surface_backend = { CAIRO_SURFACE_TYPE_SVG, _cairo_svg_surface_finish, _cairo_default_context_create, NULL, /* create_similar: handled by wrapper */ NULL, /* create_similar_image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ _cairo_svg_surface_copy_page, _cairo_svg_surface_show_page, _cairo_svg_surface_get_extents, _cairo_svg_surface_get_font_options, NULL, /* flush */ NULL, /* mark dirty rectangle */ _cairo_svg_surface_paint, _cairo_svg_surface_mask, _cairo_svg_surface_stroke, _cairo_svg_surface_fill, _cairo_svg_surface_fill_stroke, _cairo_svg_surface_show_glyphs, NULL, /* has_show_text_glyphs */ NULL, /* show_text_glyphs */ _cairo_svg_surface_get_supported_mime_types, }; static cairo_status_t _cairo_svg_document_create (cairo_output_stream_t *output_stream, double width, double height, cairo_svg_version_t version, cairo_svg_document_t **document_out) { cairo_svg_document_t *document; cairo_status_t status, status_ignored; if (output_stream->status) return output_stream->status; document = malloc (sizeof (cairo_svg_document_t)); if (unlikely (document == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* The use of defs for font glyphs imposes no per-subset limit. */ document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); if (unlikely (document->font_subsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DOCUMENT; } document->output_stream = output_stream; document->refcount = 1; document->owner = NULL; document->finished = FALSE; document->width = width; document->height = height; document->linear_pattern_id = 0; document->radial_pattern_id = 0; document->pattern_id = 0; document->filter_id = 0; document->clip_id = 0; document->mask_id = 0; document->xml_node_defs = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (document->xml_node_defs); if (unlikely (status)) goto CLEANUP_NODE_DEFS; document->xml_node_glyphs = _cairo_memory_stream_create (); status = _cairo_output_stream_get_status (document->xml_node_glyphs); if (unlikely (status)) goto CLEANUP_NODE_GLYPHS; document->alpha_filter = FALSE; document->svg_version = version; *document_out = document; return CAIRO_STATUS_SUCCESS; CLEANUP_NODE_GLYPHS: status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs); CLEANUP_NODE_DEFS: status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); _cairo_scaled_font_subsets_destroy (document->font_subsets); CLEANUP_DOCUMENT: free (document); return status; } static cairo_svg_document_t * _cairo_svg_document_reference (cairo_svg_document_t *document) { document->refcount++; return document; } static unsigned int _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document) { return document->mask_id++; } static cairo_status_t _cairo_svg_document_destroy (cairo_svg_document_t *document) { cairo_status_t status; document->refcount--; if (document->refcount > 0) return CAIRO_STATUS_SUCCESS; status = _cairo_svg_document_finish (document); free (document); return status; } static cairo_status_t _cairo_svg_document_finish (cairo_svg_document_t *document) { cairo_status_t status, status2; cairo_output_stream_t *output = document->output_stream; cairo_svg_page_t *page; unsigned int i; if (document->finished) return CAIRO_STATUS_SUCCESS; /* * Should we add DOCTYPE? * * Google says no. * * http://tech.groups.yahoo.com/group/svg-developers/message/48562: * There's a bunch of issues, but just to pick a few: * - they'll give false positives. * - they'll give false negatives. * - they're namespace-unaware. * - they don't wildcard. * So when they say OK they really haven't checked anything, when * they say NOT OK they might be on crack, and like all * namespace-unaware things they're a dead branch of the XML tree. * * http://jwatt.org/svg/authoring/: * Unfortunately the SVG DTDs are a source of so many issues that the * SVG WG has decided not to write one for the upcoming SVG 1.2 * standard. In fact SVG WG members are even telling people not to use * a DOCTYPE declaration in SVG 1.0 and 1.1 documents. */ _cairo_output_stream_printf (output, "\n" "\n", document->width, document->height, document->width, document->height, _cairo_svg_internal_version_strings [document->svg_version]); status = _cairo_svg_document_emit_font_subsets (document); if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 || _cairo_memory_stream_length (document->xml_node_defs) > 0) { _cairo_output_stream_printf (output, "\n"); if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { _cairo_output_stream_printf (output, "\n"); _cairo_memory_stream_copy (document->xml_node_glyphs, output); _cairo_output_stream_printf (output, "\n"); } _cairo_memory_stream_copy (document->xml_node_defs, output); _cairo_output_stream_printf (output, "\n"); } if (document->owner != NULL) { cairo_svg_surface_t *surface; surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); if (surface->xml_node != NULL && _cairo_memory_stream_length (surface->xml_node) > 0) { if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { if (status == CAIRO_STATUS_SUCCESS) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } } if (surface->page_set.num_elements > 1 && _cairo_svg_version_has_page_set_support (document->svg_version)) { _cairo_output_stream_printf (output, "\n"); for (i = 0; i < surface->page_set.num_elements; i++) { page = _cairo_array_index (&surface->page_set, i); _cairo_output_stream_printf (output, "\n"); _cairo_output_stream_printf (output, "\n", page->surface_id); _cairo_memory_stream_copy (page->xml_node, output); _cairo_output_stream_printf (output, "\n\n"); } _cairo_output_stream_printf (output, "\n"); } else if (surface->page_set.num_elements > 0) { page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); _cairo_output_stream_printf (output, "\n", page->surface_id); _cairo_memory_stream_copy (page->xml_node, output); _cairo_output_stream_printf (output, "\n"); } } _cairo_output_stream_printf (output, "\n"); status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); if (status == CAIRO_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (document->xml_node_defs); if (status == CAIRO_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (output); if (status == CAIRO_STATUS_SUCCESS) status = status2; document->finished = TRUE; return status; } static void _cairo_svg_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_svg_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; } static cairo_bool_t _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) { cairo_svg_surface_t *surface = abstract_surface; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) { status = _cairo_svg_surface_analyze_operator (surface, CAIRO_OPERATOR_SOURCE); } return status == CAIRO_INT_STATUS_SUCCESS; } static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { NULL /*_cairo_svg_surface_start_page*/, _cairo_svg_surface_set_paginated_mode, NULL, /* _cairo_svg_surface_set_bounding_box */ NULL, /* _cairo_svg_surface_set_fallback_images_required */ _cairo_svg_surface_supports_fine_grained_fallbacks, }; Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-svg.h000066400000000000000000000054071271037650300243510ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * cairo-svg.h * * Copyright © 2005 Emmanuel Pacaud * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #ifndef CAIRO_SVG_H #define CAIRO_SVG_H #include "cairo.h" #if CAIRO_HAS_SVG_SURFACE CAIRO_BEGIN_DECLS /** * cairo_svg_version_t: * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. (Since 1.2) * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. (Since 1.2) * * #cairo_svg_version_t is used to describe the version number of the SVG * specification that a generated SVG file will conform to. * * Since: 1.2 **/ typedef enum _cairo_svg_version { CAIRO_SVG_VERSION_1_1, CAIRO_SVG_VERSION_1_2 } cairo_svg_version_t; cairo_public cairo_surface_t * cairo_svg_surface_create (const char *filename, double width_in_points, double height_in_points); cairo_public cairo_surface_t * cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points); cairo_public void cairo_svg_surface_restrict_to_version (cairo_surface_t *surface, cairo_svg_version_t version); cairo_public void cairo_svg_get_versions (cairo_svg_version_t const **versions, int *num_versions); cairo_public const char * cairo_svg_version_to_string (cairo_svg_version_t version); CAIRO_END_DECLS #else /* CAIRO_HAS_SVG_SURFACE */ # error Cairo was not compiled with support for the svg backend #endif /* CAIRO_HAS_SVG_SURFACE */ #endif /* CAIRO_SVG_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tee-surface-private.h000066400000000000000000000034521271037650300274230ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson */ #ifndef CAIRO_TEE_SURFACE_PRIVATE_H #define CAIRO_TEE_SURFACE_PRIVATE_H #include "cairoint.h" cairo_private cairo_surface_t * _cairo_tee_surface_find_match (void *abstract_surface, const cairo_surface_backend_t *backend, cairo_content_t content); #endif /* CAIRO_TEE_SURFACE_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tee-surface.c000066400000000000000000000431411271037650300257450ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth * Chris Wilson */ /* This surface supports redirecting all its input to multiple surfaces. */ #include "cairoint.h" #include "cairo-tee.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-tee-surface-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-wrapper-private.h" #include "cairo-array-private.h" #include "cairo-image-surface-inline.h" typedef struct _cairo_tee_surface { cairo_surface_t base; cairo_surface_wrapper_t master; cairo_array_t slaves; } cairo_tee_surface_t; slim_hidden_proto (cairo_tee_surface_create); slim_hidden_proto (cairo_tee_surface_add); static cairo_surface_t * _cairo_tee_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_tee_surface_t *other = abstract_surface; cairo_surface_t *similar; cairo_surface_t *surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; similar = _cairo_surface_wrapper_create_similar (&other->master, content, width, height); surface = cairo_tee_surface_create (similar); cairo_surface_destroy (similar); if (unlikely (surface->status)) return surface; num_slaves = _cairo_array_num_elements (&other->slaves); slaves = _cairo_array_index (&other->slaves, 0); for (n = 0; n < num_slaves; n++) { similar = _cairo_surface_wrapper_create_similar (&slaves[n], content, width, height); cairo_tee_surface_add (surface, similar); cairo_surface_destroy (similar); } if (unlikely (surface->status)) { cairo_status_t status = surface->status; cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } return surface; } static cairo_status_t _cairo_tee_surface_finish (void *abstract_surface) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; _cairo_surface_wrapper_fini (&surface->master); num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) _cairo_surface_wrapper_fini (&slaves[n]); _cairo_array_fini (&surface->slaves); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_tee_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_tee_surface_t *surface = abstract_surface; return _cairo_surface_get_source (surface->master.target, extents); } static cairo_status_t _cairo_tee_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int num_slaves, n; /* we prefer to use a real image surface if available */ if (_cairo_surface_is_image (surface->master.target)) { return _cairo_surface_wrapper_acquire_source_image (&surface->master, image_out, image_extra); } num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { if (_cairo_surface_is_image (slaves[n].target)) { return _cairo_surface_wrapper_acquire_source_image (&slaves[n], image_out, image_extra); } } return _cairo_surface_wrapper_acquire_source_image (&surface->master, image_out, image_extra); } static void _cairo_tee_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_tee_surface_t *surface = abstract_surface; _cairo_surface_wrapper_release_source_image (&surface->master, image, image_extra); } static cairo_surface_t * _cairo_tee_surface_snapshot (void *abstract_surface) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int num_slaves, n; /* we prefer to use a recording surface for our snapshots */ if (_cairo_surface_is_recording (surface->master.target)) return _cairo_surface_wrapper_snapshot (&surface->master); num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { if (_cairo_surface_is_recording (slaves[n].target)) return _cairo_surface_wrapper_snapshot (&slaves[n]); } return _cairo_surface_wrapper_snapshot (&surface->master); } static cairo_bool_t _cairo_tee_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_tee_surface_t *surface = abstract_surface; return _cairo_surface_wrapper_get_extents (&surface->master, rectangle); } static void _cairo_tee_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_tee_surface_t *surface = abstract_surface; _cairo_surface_wrapper_get_font_options (&surface->master, options); } static cairo_int_status_t _cairo_tee_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; cairo_int_status_t status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); if (unlikely (status)) return status; } return _cairo_surface_wrapper_paint (&surface->master, op, source, clip); } static cairo_int_status_t _cairo_tee_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; cairo_int_status_t status; int n, num_slaves; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_mask (&slaves[n], op, source, mask, clip); if (unlikely (status)) return status; } return _cairo_surface_wrapper_mask (&surface->master, op, source, mask, clip); } static cairo_int_status_t _cairo_tee_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; cairo_int_status_t status; int n, num_slaves; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_stroke (&slaves[n], op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); if (unlikely (status)) return status; } return _cairo_surface_wrapper_stroke (&surface->master, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_tee_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; cairo_int_status_t status; int n, num_slaves; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { status = _cairo_surface_wrapper_fill (&slaves[n], op, source, path, fill_rule, tolerance, antialias, clip); if (unlikely (status)) return status; } return _cairo_surface_wrapper_fill (&surface->master, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_bool_t _cairo_tee_surface_has_show_text_glyphs (void *abstract_surface) { return TRUE; } static cairo_int_status_t _cairo_tee_surface_show_text_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; cairo_int_status_t status; int n, num_slaves; cairo_glyph_t *glyphs_copy; /* XXX: This copying is ugly. */ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (glyphs_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, source, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); if (unlikely (status)) goto CLEANUP; } memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, source, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); CLEANUP: free (glyphs_copy); return status; } static const cairo_surface_backend_t cairo_tee_surface_backend = { CAIRO_SURFACE_TYPE_TEE, _cairo_tee_surface_finish, _cairo_default_context_create, /* XXX */ _cairo_tee_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_tee_surface_source, _cairo_tee_surface_acquire_source_image, _cairo_tee_surface_release_source_image, _cairo_tee_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_tee_surface_get_extents, _cairo_tee_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_tee_surface_paint, _cairo_tee_surface_mask, _cairo_tee_surface_stroke, _cairo_tee_surface_fill, NULL, /* fill_stroke */ NULL, /* show_glyphs */ _cairo_tee_surface_has_show_text_glyphs, _cairo_tee_surface_show_text_glyphs }; cairo_surface_t * cairo_tee_surface_create (cairo_surface_t *master) { cairo_tee_surface_t *surface; if (unlikely (master->status)) return _cairo_surface_create_in_error (master->status); surface = malloc (sizeof (cairo_tee_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_tee_surface_backend, master->device, master->content); _cairo_surface_wrapper_init (&surface->master, master); _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); return &surface->base; } slim_hidden_def (cairo_tee_surface_create); void cairo_tee_surface_add (cairo_surface_t *abstract_surface, cairo_surface_t *target) { cairo_tee_surface_t *surface; cairo_surface_wrapper_t slave; cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->backend != &cairo_tee_surface_backend) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (unlikely (target->status)) { status = _cairo_surface_set_error (abstract_surface, target->status); return; } surface = (cairo_tee_surface_t *) abstract_surface; _cairo_surface_wrapper_init (&slave, target); status = _cairo_array_append (&surface->slaves, &slave); if (unlikely (status)) { _cairo_surface_wrapper_fini (&slave); status = _cairo_surface_set_error (&surface->base, status); } } slim_hidden_def (cairo_tee_surface_add); void cairo_tee_surface_remove (cairo_surface_t *abstract_surface, cairo_surface_t *target) { cairo_tee_surface_t *surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->backend != &cairo_tee_surface_backend) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } surface = (cairo_tee_surface_t *) abstract_surface; if (target == surface->master.target) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_INDEX)); return; } num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { if (slaves[n].target == target) break; } if (n == num_slaves) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_INDEX)); return; } _cairo_surface_wrapper_fini (&slaves[n]); for (n++; n < num_slaves; n++) slaves[n-1] = slaves[n]; surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */ } cairo_surface_t * cairo_tee_surface_index (cairo_surface_t *abstract_surface, unsigned int index) { cairo_tee_surface_t *surface; if (unlikely (abstract_surface->status)) return _cairo_surface_create_in_error (abstract_surface->status); if (unlikely (abstract_surface->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (abstract_surface->backend != &cairo_tee_surface_backend) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); surface = (cairo_tee_surface_t *) abstract_surface; if (index == 0) { return surface->master.target; } else { cairo_surface_wrapper_t *slave; index--; if (index >= _cairo_array_num_elements (&surface->slaves)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); slave = _cairo_array_index (&surface->slaves, index); return slave->target; } } cairo_surface_t * _cairo_tee_surface_find_match (void *abstract_surface, const cairo_surface_backend_t *backend, cairo_content_t content) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int num_slaves, n; /* exact match first */ if (surface->master.target->backend == backend && surface->master.target->content == content) { return surface->master.target; } num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { if (slaves[n].target->backend == backend && slaves[n].target->content == content) { return slaves[n].target; } } /* matching backend? */ if (surface->master.target->backend == backend) return surface->master.target; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { if (slaves[n].target->backend == backend) return slaves[n].target; } return NULL; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tee.h000066400000000000000000000041751271037650300243300ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson */ #ifndef CAIRO_TEE_H #define CAIRO_TEE_H #include "cairo.h" #if CAIRO_HAS_TEE_SURFACE CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_tee_surface_create (cairo_surface_t *master); cairo_public void cairo_tee_surface_add (cairo_surface_t *surface, cairo_surface_t *target); cairo_public void cairo_tee_surface_remove (cairo_surface_t *surface, cairo_surface_t *target); cairo_public cairo_surface_t * cairo_tee_surface_index (cairo_surface_t *surface, unsigned int index); CAIRO_END_DECLS #else /*CAIRO_HAS_TEE_SURFACE*/ # error Cairo was not compiled with support for the TEE backend #endif /*CAIRO_HAS_TEE_SURFACE*/ #endif /*CAIRO_TEE_H*/ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-time-private.h000066400000000000000000000051531271037650300261560ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright (C) 2011 Andrea Canciani * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of the * copyright holders not be used in advertising or publicity * pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Authors: Andrea Canciani * */ #ifndef CAIRO_TIME_PRIVATE_H #define CAIRO_TIME_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-wideint-private.h" /* Make the base type signed for easier arithmetic */ typedef cairo_int64_t cairo_time_t; #define _cairo_time_add _cairo_int64_add #define _cairo_time_sub _cairo_int64_sub #define _cairo_time_gt _cairo_int64_gt #define _cairo_time_lt _cairo_int64_lt #define _cairo_time_to_double _cairo_int64_to_double #define _cairo_time_from_double _cairo_double_to_int64 cairo_private int _cairo_time_cmp (const void *a, const void *b); cairo_private double _cairo_time_to_s (cairo_time_t t); cairo_private cairo_time_t _cairo_time_from_s (double t); cairo_private cairo_time_t _cairo_time_get (void); static cairo_always_inline cairo_time_t _cairo_time_get_delta (cairo_time_t t) { cairo_time_t now; now = _cairo_time_get (); return _cairo_time_sub (now, t); } static cairo_always_inline double _cairo_time_to_ns (cairo_time_t t) { return 1.e9 * _cairo_time_to_s (t); } static cairo_always_inline cairo_time_t _cairo_time_max (cairo_time_t a, cairo_time_t b) { if (_cairo_int64_gt (a, b)) return a; else return b; } static cairo_always_inline cairo_time_t _cairo_time_min (cairo_time_t a, cairo_time_t b) { if (_cairo_int64_lt (a, b)) return a; else return b; } #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-time.c000066400000000000000000000113331271037650300244760ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright (c) 2007 Netlabs * Copyright (c) 2006 Mozilla Corporation * Copyright (c) 2006 Red Hat, Inc. * Copyright (c) 2011 Andrea Canciani * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * the authors not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. The authors make no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: Peter Weilbacher * Vladimir Vukicevic * Carl Worth * Andrea Canciani */ #include "cairoint.h" #include "cairo-time-private.h" #if HAVE_CLOCK_GETTIME #if defined(CLOCK_MONOTONIC_RAW) #define CAIRO_CLOCK CLOCK_MONOTONIC_RAW #elif defined(CLOCK_MONOTONIC) #define CAIRO_CLOCK CLOCK_MONOTONIC #endif #endif #if defined(__APPLE__) #include static cairo_always_inline double _cairo_time_1s (void) { mach_timebase_info_data_t freq; mach_timebase_info (&freq); return 1000000000. * freq.denom / freq.numer; } cairo_time_t _cairo_time_get (void) { return mach_absolute_time (); } #elif defined(__OS2__) #define INCL_BASE #include static cairo_always_inline double _cairo_time_1s (void) { ULONG freq; DosTmrQueryFreq (&freq); return freq; } cairo_time_t _cairo_time_get (void) { QWORD t; cairo_int64_t r; DosTmrQueryTime (&t); r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32); r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo)); return r; } #elif _WIN32 #define WIN32_LEAN_AND_MEAN #include static cairo_always_inline double _cairo_time_1s (void) { LARGE_INTEGER freq; QueryPerformanceFrequency (&freq); return freq.QuadPart; } #ifndef HAVE_UINT64_T static cairo_always_inline cairo_time_t _cairo_time_from_large_integer (LARGE_INTEGER t) { cairo_int64_t r; r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.HighPart), 32); r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.LowPart)); return r; } #else static cairo_always_inline cairo_time_t _cairo_time_from_large_integer (LARGE_INTEGER t) { return t.QuadPart; } #endif cairo_time_t _cairo_time_get (void) { LARGE_INTEGER t; QueryPerformanceCounter (&t); return _cairo_time_from_large_integer(t); } #elif defined(CAIRO_CLOCK) #include static cairo_always_inline double _cairo_time_1s (void) { return 1000000000; } cairo_time_t _cairo_time_get (void) { struct timespec t; cairo_time_t r; clock_gettime (CAIRO_CLOCK, &t); r = _cairo_double_to_int64 (_cairo_time_1s ()); r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec)); r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_nsec)); return r; } #else #include static cairo_always_inline double _cairo_time_1s (void) { return 1000000; } cairo_time_t _cairo_time_get (void) { struct timeval t; cairo_time_t r; gettimeofday (&t, NULL); r = _cairo_double_to_int64 (_cairo_time_1s ()); r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec)); r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_usec)); return r; } #endif int _cairo_time_cmp (const void *a, const void *b) { const cairo_time_t *ta = a, *tb = b; return _cairo_int64_cmp (*ta, *tb); } static double _cairo_time_ticks_per_sec (void) { static double ticks = 0; if (unlikely (ticks == 0)) ticks = _cairo_time_1s (); return ticks; } static double _cairo_time_s_per_tick (void) { static double s = 0; if (unlikely (s == 0)) s = 1. / _cairo_time_ticks_per_sec (); return s; } double _cairo_time_to_s (cairo_time_t t) { return _cairo_int64_to_double (t) * _cairo_time_s_per_tick (); } cairo_time_t _cairo_time_from_s (double t) { return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ()); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tor-scan-converter.c000066400000000000000000001443211271037650300272770ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* glitter-paths - polygon scan converter * * Copyright (c) 2008 M Joonas Pihlaja * Copyright (c) 2007 David Turner * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* This is the Glitter paths scan converter incorporated into cairo. * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 * of * * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths */ /* Glitter-paths is a stand alone polygon rasteriser derived from * David Turner's reimplementation of Tor Anderssons's 15x17 * supersampling rasteriser from the Apparition graphics library. The * main new feature here is cheaply choosing per-scan line between * doing fully analytical coverage computation for an entire row at a * time vs. using a supersampling approach. * * David Turner's code can be found at * * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 * * In particular this file incorporates large parts of ftgrays_tor10.h * from raster-comparison-20070813.tar.bz2 */ /* Overview * * A scan converter's basic purpose to take polygon edges and convert * them into an RLE compressed A8 mask. This one works in two phases: * gathering edges and generating spans. * * 1) As the user feeds the scan converter edges they are vertically * clipped and bucketted into a _polygon_ data structure. The edges * are also snapped from the user's coordinates to the subpixel grid * coordinates used during scan conversion. * * user * | * | edges * V * polygon buckets * * 2) Generating spans works by performing a vertical sweep of pixel * rows from top to bottom and maintaining an _active_list_ of edges * that intersect the row. From the active list the fill rule * determines which edges are the left and right edges of the start of * each span, and their contribution is then accumulated into a pixel * coverage list (_cell_list_) as coverage deltas. Once the coverage * deltas of all edges are known we can form spans of constant pixel * coverage by summing the deltas during a traversal of the cell list. * At the end of a pixel row the cell list is sent to a coverage * blitter for rendering to some target surface. * * The pixel coverages are computed by either supersampling the row * and box filtering a mono rasterisation, or by computing the exact * coverages of edges in the active list. The supersampling method is * used whenever some edge starts or stops within the row or there are * edge intersections in the row. * * polygon bucket for \ * current pixel row | * | | * | activate new edges | Repeat GRID_Y times if we * V \ are supersampling this row, * active list / or just once if we're computing * | | analytical coverage. * | coverage deltas | * V | * pixel coverage list / * | * V * coverage blitter */ #include "cairoint.h" #include "cairo-spans-private.h" #include "cairo-error-private.h" #include #include #include #include /*------------------------------------------------------------------------- * cairo specific config */ #define I static /* Prefer cairo's status type. */ #define GLITTER_HAVE_STATUS_T 1 #define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS #define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY typedef cairo_status_t glitter_status_t; /* The input coordinate scale and the rasterisation grid scales. */ #define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS #define GRID_X_BITS CAIRO_FIXED_FRAC_BITS #define GRID_Y 15 /* Set glitter up to use a cairo span renderer to do the coverage * blitting. */ struct pool; struct cell_list; /*------------------------------------------------------------------------- * glitter-paths.h */ /* "Input scaled" numbers are fixed precision reals with multiplier * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as * pixel scaled numbers. These get converted to the internal grid * scaled numbers as soon as possible. Internal overflow is possible * if GRID_X/Y inside glitter-paths.c is larger than * 1< #include #include /* All polygon coordinates are snapped onto a subsample grid. "Grid * scaled" numbers are fixed precision reals with multiplier GRID_X or * GRID_Y. */ typedef int grid_scaled_t; typedef int grid_scaled_x_t; typedef int grid_scaled_y_t; /* Default x/y scale factors. * You can either define GRID_X/Y_BITS to get a power-of-two scale * or define GRID_X/Y separately. */ #if !defined(GRID_X) && !defined(GRID_X_BITS) # define GRID_X_BITS 8 #endif #if !defined(GRID_Y) && !defined(GRID_Y_BITS) # define GRID_Y 15 #endif /* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ #ifdef GRID_X_BITS # define GRID_X (1 << GRID_X_BITS) #endif #ifdef GRID_Y_BITS # define GRID_Y (1 << GRID_Y_BITS) #endif /* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into * integer and fractional parts. The integer part is floored. */ #if defined(GRID_X_TO_INT_FRAC) /* do nothing */ #elif defined(GRID_X_BITS) # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) #else # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) #endif #define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ (i) = (t) / (m); \ (f) = (t) % (m); \ if ((f) < 0) { \ --(i); \ (f) += (m); \ } \ } while (0) #define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ (f) = (t) & ((1 << (b)) - 1); \ (i) = (t) >> (b); \ } while (0) /* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want * to be able to represent exactly areas of subpixel trapezoids whose * vertices are given in grid scaled coordinates. The scale factor * comes from needing to accurately represent the area 0.5*dx*dy of a * triangle with base dx and height dy in grid scaled numbers. */ #define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ /* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ #if GRID_XY == 510 # define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) #elif GRID_XY == 255 # define GRID_AREA_TO_ALPHA(c) (c) #elif GRID_XY == 64 # define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) #elif GRID_XY == 128 # define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) #elif GRID_XY == 256 # define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) #elif GRID_XY == 15 # define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) #elif GRID_XY == 2*256*15 # define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) #else # define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) #endif #define UNROLL3(x) x x x struct quorem { int32_t quo; int32_t rem; }; /* Header for a chunk of memory in a memory pool. */ struct _pool_chunk { /* # bytes used in this chunk. */ size_t size; /* # bytes total in this chunk */ size_t capacity; /* Pointer to the previous chunk or %NULL if this is the sentinel * chunk in the pool header. */ struct _pool_chunk *prev_chunk; /* Actual data starts here. Well aligned for pointers. */ }; /* A memory pool. This is supposed to be embedded on the stack or * within some other structure. It may optionally be followed by an * embedded array from which requests are fulfilled until * malloc needs to be called to allocate a first real chunk. */ struct pool { /* Chunk we're allocating from. */ struct _pool_chunk *current; jmp_buf *jmp; /* Free list of previously allocated chunks. All have >= default * capacity. */ struct _pool_chunk *first_free; /* The default capacity of a chunk. */ size_t default_capacity; /* Header for the sentinel chunk. Directly following the pool * struct should be some space for embedded elements from which * the sentinel chunk allocates from. */ struct _pool_chunk sentinel[1]; }; /* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ struct edge *next, *prev; /* Number of subsample rows remaining to scan convert of this * edge. */ grid_scaled_y_t height_left; /* Original sign of the edge: +1 for downwards, -1 for upwards * edges. */ int dir; int vertical; /* Current x coordinate while the edge is on the active * list. Initialised to the x coordinate of the top of the * edge. The quotient is in grid_scaled_x_t units and the * remainder is mod dy in grid_scaled_y_t units.*/ struct quorem x; /* Advance of the current x when moving down a subsample line. */ struct quorem dxdy; /* Advance of the current x when moving down a full pixel * row. Only initialised when the height of the edge is large * enough that there's a chance the edge could be stepped by a * full row's worth of subsample rows at a time. */ struct quorem dxdy_full; /* The clipped y of the top of the edge. */ grid_scaled_y_t ytop; /* y2-y1 after orienting the edge downwards. */ grid_scaled_y_t dy; }; #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y) /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { /* The vertical clip extents. */ grid_scaled_y_t ymin, ymax; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64]; struct { struct pool base[1]; struct edge embedded[32]; } edge_pool; }; /* A cell records the effect on pixel coverage of polygon edges * passing through a pixel. It contains two accumulators of pixel * coverage. * * Consider the effects of a polygon edge on the coverage of a pixel * it intersects and that of the following one. The coverage of the * following pixel is the height of the edge multiplied by the width * of the pixel, and the coverage of the pixel itself is the area of * the trapezoid formed by the edge and the right side of the pixel. * * +-----------------------+-----------------------+ * | | | * | | | * |_______________________|_______________________| * | \...................|.......................|\ * | \..................|.......................| | * | \.................|.......................| | * | \....covered.....|.......................| | * | \....area.......|.......................| } covered height * | \..............|.......................| | * |uncovered\.............|.......................| | * | area \............|.......................| | * |___________\...........|.......................|/ * | | | * | | | * | | | * +-----------------------+-----------------------+ * * Since the coverage of the following pixel will always be a multiple * of the width of the pixel, we can store the height of the covered * area instead. The coverage of the pixel itself is the total * coverage minus the area of the uncovered area to the left of the * edge. As it's faster to compute the uncovered area we only store * that and subtract it from the total coverage later when forming * spans to blit. * * The heights and areas are signed, with left edges of the polygon * having positive sign and right edges having negative sign. When * two edges intersect they swap their left/rightness so their * contribution above and below the intersection point must be * computed separately. */ struct cell { struct cell *next; int x; int16_t uncovered_area; int16_t covered_height; }; /* A cell list represents the scan line sparsely as cells ordered by * ascending x. It is geared towards scanning the cells in order * using an internal cursor. */ struct cell_list { /* Sentinel nodes */ struct cell head, tail; /* Cursor state for iterating through the cell list. */ struct cell *cursor, *rewind; /* Cells in the cell list are owned by the cell list and are * allocated from this pool. */ struct { struct pool base[1]; struct cell embedded[32]; } cell_pool; }; struct cell_pair { struct cell *cell1; struct cell *cell2; }; /* The active list contains edges in the current scan line ordered by * the x-coordinate of the intercept of the edge and the scan line. */ struct active_list { /* Leftmost edge on the current scan line. */ struct edge head, tail; /* A lower bound on the height of the active edges is used to * estimate how soon some active edge ends. We can't advance the * scan conversion by a full pixel row if an edge ends somewhere * within it. */ grid_scaled_y_t min_height; int is_vertical; }; struct glitter_scan_converter { struct polygon polygon[1]; struct active_list active[1]; struct cell_list coverages[1]; cairo_half_open_span_t *spans; cairo_half_open_span_t spans_embedded[64]; /* Clip box. */ grid_scaled_x_t xmin, xmax; grid_scaled_y_t ymin, ymax; }; /* Compute the floored division a/b. Assumes / and % perform symmetric * division. */ inline static struct quorem floored_divrem(int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } /* Compute the floored division (x*a)/b. Assumes / and % perform symmetric * division. */ static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } static struct _pool_chunk * _pool_chunk_init( struct _pool_chunk *p, struct _pool_chunk *prev_chunk, size_t capacity) { p->prev_chunk = prev_chunk; p->size = 0; p->capacity = capacity; return p; } static struct _pool_chunk * _pool_chunk_create(struct pool *pool, size_t size) { struct _pool_chunk *p; p = malloc(size + sizeof(struct _pool_chunk)); if (unlikely (NULL == p)) longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); return _pool_chunk_init(p, pool->current, size); } static void pool_init(struct pool *pool, jmp_buf *jmp, size_t default_capacity, size_t embedded_capacity) { pool->jmp = jmp; pool->current = pool->sentinel; pool->first_free = NULL; pool->default_capacity = default_capacity; _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); } static void pool_fini(struct pool *pool) { struct _pool_chunk *p = pool->current; do { while (NULL != p) { struct _pool_chunk *prev = p->prev_chunk; if (p != pool->sentinel) free(p); p = prev; } p = pool->first_free; pool->first_free = NULL; } while (NULL != p); } /* Satisfy an allocation by first allocating a new large enough chunk * and adding it to the head of the pool's chunk list. This function * is called as a fallback if pool_alloc() couldn't do a quick * allocation from the current chunk in the pool. */ static void * _pool_alloc_from_new_chunk( struct pool *pool, size_t size) { struct _pool_chunk *chunk; void *obj; size_t capacity; /* If the allocation is smaller than the default chunk size then * try getting a chunk off the free list. Force alloc of a new * chunk for large requests. */ capacity = size; chunk = NULL; if (size < pool->default_capacity) { capacity = pool->default_capacity; chunk = pool->first_free; if (chunk) { pool->first_free = chunk->prev_chunk; _pool_chunk_init(chunk, pool->current, chunk->capacity); } } if (NULL == chunk) chunk = _pool_chunk_create (pool, capacity); pool->current = chunk; obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } /* Allocate size bytes from the pool. The first allocated address * returned from a pool is aligned to sizeof(void*). Subsequent * addresses will maintain alignment as long as multiples of void* are * allocated. Returns the address of a new memory area or %NULL on * allocation failures. The pool retains ownership of the returned * memory. */ inline static void * pool_alloc (struct pool *pool, size_t size) { struct _pool_chunk *chunk = pool->current; if (size <= chunk->capacity - chunk->size) { void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } else { return _pool_alloc_from_new_chunk(pool, size); } } /* Relinquish all pool_alloced memory back to the pool. */ static void pool_reset (struct pool *pool) { /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; if (chunk != pool->sentinel) { while (chunk->prev_chunk != pool->sentinel) { chunk = chunk->prev_chunk; } chunk->prev_chunk = pool->first_free; pool->first_free = pool->current; } /* Reset the sentinel as the current chunk. */ pool->current = pool->sentinel; pool->sentinel->size = 0; } /* Rewinds the cell list's cursor to the beginning. After rewinding * we're good to cell_list_find() the cell any x coordinate. */ inline static void cell_list_rewind (struct cell_list *cells) { cells->cursor = &cells->head; } inline static void cell_list_maybe_rewind (struct cell_list *cells, int x) { if (x < cells->cursor->x) { cells->cursor = cells->rewind; if (x < cells->cursor->x) cells->cursor = &cells->head; } } inline static void cell_list_set_rewind (struct cell_list *cells) { cells->rewind = cells->cursor; } static void cell_list_init(struct cell_list *cells, jmp_buf *jmp) { pool_init(cells->cell_pool.base, jmp, 256*sizeof(struct cell), sizeof(cells->cell_pool.embedded)); cells->tail.next = NULL; cells->tail.x = INT_MAX; cells->head.x = INT_MIN; cells->head.next = &cells->tail; cell_list_rewind (cells); } static void cell_list_fini(struct cell_list *cells) { pool_fini (cells->cell_pool.base); } /* Empty the cell list. This is called at the start of every pixel * row. */ inline static void cell_list_reset (struct cell_list *cells) { cell_list_rewind (cells); cells->head.next = &cells->tail; pool_reset (cells->cell_pool.base); } inline static struct cell * cell_list_alloc (struct cell_list *cells, struct cell *tail, int x) { struct cell *cell; cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); cell->next = tail->next; tail->next = cell; cell->x = x; *(uint32_t *)&cell->uncovered_area = 0; return cell; } /* Find a cell at the given x-coordinate. Returns %NULL if a new cell * needed to be allocated but couldn't be. Cells must be found with * non-decreasing x-coordinate until the cell list is rewound using * cell_list_rewind(). Ownership of the returned cell is retained by * the cell list. */ inline static struct cell * cell_list_find (struct cell_list *cells, int x) { struct cell *tail = cells->cursor; if (tail->x == x) return tail; while (1) { UNROLL3({ if (tail->next->x > x) break; tail = tail->next; }); } if (tail->x != x) tail = cell_list_alloc (cells, tail, x); return cells->cursor = tail; } /* Find two cells at x1 and x2. This is exactly equivalent * to * * pair.cell1 = cell_list_find(cells, x1); * pair.cell2 = cell_list_find(cells, x2); * * except with less function call overhead. */ inline static struct cell_pair cell_list_find_pair(struct cell_list *cells, int x1, int x2) { struct cell_pair pair; pair.cell1 = cells->cursor; while (1) { UNROLL3({ if (pair.cell1->next->x > x1) break; pair.cell1 = pair.cell1->next; }); } if (pair.cell1->x != x1) pair.cell1 = cell_list_alloc (cells, pair.cell1, x1); pair.cell2 = pair.cell1; while (1) { UNROLL3({ if (pair.cell2->next->x > x2) break; pair.cell2 = pair.cell2->next; }); } if (pair.cell2->x != x2) pair.cell2 = cell_list_alloc (cells, pair.cell2, x2); cells->cursor = pair.cell2; return pair; } /* Add a subpixel span covering [x1, x2) to the coverage cells. */ inline static void cell_list_add_subspan(struct cell_list *cells, grid_scaled_x_t x1, grid_scaled_x_t x2) { int ix1, fx1; int ix2, fx2; if (x1 == x2) return; GRID_X_TO_INT_FRAC(x1, ix1, fx1); GRID_X_TO_INT_FRAC(x2, ix2, fx2); if (ix1 != ix2) { struct cell_pair p; p = cell_list_find_pair(cells, ix1, ix2); p.cell1->uncovered_area += 2*fx1; ++p.cell1->covered_height; p.cell2->uncovered_area -= 2*fx2; --p.cell2->covered_height; } else { struct cell *cell = cell_list_find(cells, ix1); cell->uncovered_area += 2*(fx1-fx2); } } /* Adds the analytical coverage of an edge crossing the current pixel * row to the coverage cells and advances the edge's x position to the * following row. * * This function is only called when we know that during this pixel row: * * 1) The relative order of all edges on the active list doesn't * change. In particular, no edges intersect within this row to pixel * precision. * * 2) No new edges start in this row. * * 3) No existing edges end mid-row. * * This function depends on being called with all edges from the * active list in the order they appear on the list (i.e. with * non-decreasing x-coordinate.) */ static void cell_list_render_edge(struct cell_list *cells, struct edge *edge, int sign) { grid_scaled_y_t y1, y2, dy; grid_scaled_x_t dx; int ix1, ix2; grid_scaled_x_t fx1, fx2; struct quorem x1 = edge->x; struct quorem x2 = x1; if (! edge->vertical) { x2.quo += edge->dxdy_full.quo; x2.rem += edge->dxdy_full.rem; if (x2.rem >= 0) { ++x2.quo; x2.rem -= edge->dy; } edge->x = x2; } GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); /* Edge is entirely within a column? */ if (ix1 == ix2) { /* We always know that ix1 is >= the cell list cursor in this * case due to the no-intersections precondition. */ struct cell *cell = cell_list_find(cells, ix1); cell->covered_height += sign*GRID_Y; cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; return; } /* Orient the edge left-to-right. */ dx = x2.quo - x1.quo; if (dx >= 0) { y1 = 0; y2 = GRID_Y; } else { int tmp; tmp = ix1; ix1 = ix2; ix2 = tmp; tmp = fx1; fx1 = fx2; fx2 = tmp; dx = -dx; sign = -sign; y1 = GRID_Y; y2 = 0; } dy = y2 - y1; /* Add coverage for all pixels [ix1,ix2] on this row crossed * by the edge. */ { struct cell_pair pair; struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); /* When rendering a previous edge on the active list we may * advance the cell list cursor past the leftmost pixel of the * current edge even though the two edges don't intersect. * e.g. consider two edges going down and rightwards: * * --\_+---\_+-----+-----+---- * \_ \_ | | * | \_ | \_ | | * | \_| \_| | * | \_ \_ | * ----+-----+-\---+-\---+---- * * The left edge touches cells past the starting cell of the * right edge. Fortunately such cases are rare. * * The rewinding is never necessary if the current edge stays * within a single column because we've checked before calling * this function that the active list order won't change. */ cell_list_maybe_rewind(cells, ix1); pair = cell_list_find_pair(cells, ix1, ix1+1); pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); pair.cell1->covered_height += sign*y.quo; y.quo += y1; if (ix1+1 < ix2) { struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); struct cell *cell = pair.cell2; ++ix1; do { grid_scaled_y_t y_skip = dydx_full.quo; y.rem += dydx_full.rem; if (y.rem >= dx) { ++y_skip; y.rem -= dx; } y.quo += y_skip; y_skip *= sign; cell->uncovered_area += y_skip*GRID_X; cell->covered_height += y_skip; ++ix1; cell = cell_list_find(cells, ix1); } while (ix1 != ix2); pair.cell2 = cell; } pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; pair.cell2->covered_height += sign*(y2 - y.quo); } } static void polygon_init (struct polygon *polygon, jmp_buf *jmp) { polygon->ymin = polygon->ymax = 0; polygon->y_buckets = polygon->y_buckets_embedded; pool_init (polygon->edge_pool.base, jmp, 8192 - sizeof (struct _pool_chunk), sizeof (polygon->edge_pool.embedded)); } static void polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); pool_fini (polygon->edge_pool.base); } /* Empties the polygon of all edges. The polygon is then prepared to * receive new edges and clip them to the vertical range * [ymin,ymax). */ static glitter_status_t polygon_reset (struct polygon *polygon, grid_scaled_y_t ymin, grid_scaled_y_t ymax) { unsigned h = ymax - ymin; unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin); pool_reset(polygon->edge_pool.base); if (unlikely (h > 0x7FFFFFFFU - GRID_Y)) goto bail_no_mem; /* even if you could, you wouldn't want to. */ if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); polygon->y_buckets = polygon->y_buckets_embedded; if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (num_buckets, sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) goto bail_no_mem; } memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); polygon->ymin = ymin; polygon->ymax = ymax; return GLITTER_STATUS_SUCCESS; bail_no_mem: polygon->ymin = 0; polygon->ymax = 0; return GLITTER_STATUS_NO_MEMORY; } static void _polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e) { unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); struct edge **ptail = &polygon->y_buckets[ix]; e->next = *ptail; *ptail = e; } inline static void polygon_add_edge (struct polygon *polygon, const cairo_edge_t *edge) { struct edge *e; grid_scaled_x_t dx; grid_scaled_y_t dy; grid_scaled_y_t ytop, ybot; grid_scaled_y_t ymin = polygon->ymin; grid_scaled_y_t ymax = polygon->ymax; if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) return; e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; e->dy = dy; e->dir = edge->dir; ytop = edge->top >= ymin ? edge->top : ymin; ybot = edge->bottom <= ymax ? edge->bottom : ymax; e->ytop = ytop; e->height_left = ybot - ytop; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } else { e->vertical = FALSE; e->dxdy = floored_divrem (dx, dy); if (ytop == edge->line.p1.y) { e->x.quo = edge->line.p1.x; e->x.rem = 0; } else { e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } if (e->height_left >= GRID_Y) { e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); } else { e->dxdy_full.quo = 0; e->dxdy_full.rem = 0; } } _polygon_insert_edge_into_its_y_bucket (polygon, e); e->x.rem -= dy; /* Bias the remainder for faster * edge advancement. */ } static void active_list_reset (struct active_list *active) { active->head.vertical = 1; active->head.height_left = INT_MAX; active->head.x.quo = INT_MIN; active->head.prev = NULL; active->head.next = &active->tail; active->tail.prev = &active->head; active->tail.next = NULL; active->tail.x.quo = INT_MAX; active->tail.height_left = INT_MAX; active->tail.vertical = 1; active->min_height = 0; active->is_vertical = 1; } static void active_list_init(struct active_list *active) { active_list_reset(active); } /* * Merge two sorted edge lists. * Input: * - head_a: The head of the first list. * - head_b: The head of the second list; head_b cannot be NULL. * Output: * Returns the head of the merged list. * * Implementation notes: * To make it fast (in particular, to reduce to an insertion sort whenever * one of the two input lists only has a single element) we iterate through * a list until its head becomes greater than the head of the other list, * then we switch their roles. As soon as one of the two lists is empty, we * just attach the other one to the current list and exit. * Writes to memory are only needed to "switch" lists (as it also requires * attaching to the output list the list which we will be iterating next) and * to attach the last non-empty list. */ static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { struct edge *head, **next, *prev; int32_t x; prev = head_a->prev; next = &head; if (head_a->x.quo <= head_b->x.quo) { head = head_a; } else { head = head_b; head_b->prev = prev; goto start_with_b; } do { x = head_b->x.quo; while (head_a != NULL && head_a->x.quo <= x) { prev = head_a; next = &head_a->next; head_a = head_a->next; } head_b->prev = prev; *next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x.quo; while (head_b != NULL && head_b->x.quo <= x) { prev = head_b; next = &head_b->next; head_b = head_b->next; } head_a->prev = prev; *next = head_a; if (head_b == NULL) return head; } while (1); } /* * Sort (part of) a list. * Input: * - list: The list to be sorted; list cannot be NULL. * - limit: Recursion limit. * Output: * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the * input list; if the input list has fewer elements, head_out be a sorted list * containing all the elements of the input list. * Returns the head of the list of unprocessed elements (NULL if the sorted list contains * all the elements of the input list). * * Implementation notes: * Special case single element list, unroll/inline the sorting of the first two elements. * Some tail recursion is used since we iterate on the bottom-up solution of the problem * (we start with a small sorted list and keep merging other lists of the same size to it). */ static struct edge * sort_edges (struct edge *list, unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; unsigned int i; head_other = list->next; if (head_other == NULL) { *head_out = list; return NULL; } remaining = head_other->next; if (list->x.quo <= head_other->x.quo) { *head_out = list; head_other->next = NULL; } else { *head_out = head_other; head_other->prev = list->prev; head_other->next = list; list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } return remaining; } static struct edge * merge_unsorted_edges (struct edge *head, struct edge *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); return merge_sorted_edges (head, unsorted); } /* Test if the edges on the active list can be safely advanced by a * full row without intersections or any edges ending. */ inline static int can_do_full_row (struct active_list *active) { const struct edge *e; int prev_x = INT_MIN; /* Recomputes the minimum height of all edges on the active * list if we have been dropping edges. */ if (active->min_height <= 0) { int min_height = INT_MAX; int is_vertical = 1; e = active->head.next; while (NULL != e) { if (e->height_left < min_height) min_height = e->height_left; is_vertical &= e->vertical; e = e->next; } active->is_vertical = is_vertical; active->min_height = min_height; } if (active->min_height < GRID_Y) return 0; /* Check for intersections as no edges end during the next row. */ for (e = active->head.next; e != &active->tail; e = e->next) { struct quorem x = e->x; if (! e->vertical) { x.quo += e->dxdy_full.quo; x.rem += e->dxdy_full.rem; if (x.rem >= 0) ++x.quo; } if (x.quo < prev_x) return 0; prev_x = x.quo; } return 1; } /* Merges edges on the given subpixel row from the polygon to the * active_list. */ inline static void active_list_merge_edges_from_bucket(struct active_list *active, struct edge *edges) { active->head.next = merge_unsorted_edges (active->head.next, edges); } inline static void polygon_fill_buckets (struct active_list *active, struct edge *edge, int y, struct edge **buckets) { grid_scaled_y_t min_height = active->min_height; int is_vertical = active->is_vertical; while (edge) { struct edge *next = edge->next; int suby = edge->ytop - y; if (buckets[suby]) buckets[suby]->prev = edge; edge->next = buckets[suby]; edge->prev = NULL; buckets[suby] = edge; if (edge->height_left < min_height) min_height = edge->height_left; is_vertical &= edge->vertical; edge = next; } active->is_vertical = is_vertical; active->min_height = min_height; } inline static void sub_row (struct active_list *active, struct cell_list *coverages, unsigned int mask) { struct edge *edge = active->head.next; int xstart = INT_MIN, prev_x = INT_MIN; int winding = 0; cell_list_rewind (coverages); while (&active->tail != edge) { struct edge *next = edge->next; int xend = edge->x.quo; if (--edge->height_left) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } if (edge->x.quo < prev_x) { struct edge *pos = edge->prev; pos->next = next; next->prev = pos; do { pos = pos->prev; } while (edge->x.quo < pos->x.quo); pos->next->prev = edge; edge->next = pos->next; edge->prev = pos; pos->next = edge; } else prev_x = edge->x.quo; active->min_height = -1; } else { edge->prev->next = next; next->prev = edge->prev; } winding += edge->dir; if ((winding & mask) == 0) { if (next->x.quo != xend) { cell_list_add_subspan (coverages, xstart, xend); xstart = INT_MIN; } } else if (xstart == INT_MIN) xstart = xend; edge = next; } } inline static void dec (struct active_list *a, struct edge *e, int h) { e->height_left -= h; if (e->height_left == 0) { e->prev->next = e->next; e->next->prev = e->prev; a->min_height = -1; } } inline static void full_step (struct edge *e) { if (! e->vertical) { e->x.quo += e->dxdy_full.quo; e->x.rem += e->dxdy_full.rem; if (e->x.rem >= 0) { ++e->x.quo; e->x.rem -= e->dy; } } } static void full_row (struct active_list *active, struct cell_list *coverages, unsigned int mask) { struct edge *left = active->head.next; while (&active->tail != left) { struct edge *right; int winding; dec (active, left, GRID_Y); winding = left->dir; right = left->next; do { dec (active, right, GRID_Y); winding += right->dir; if ((winding & mask) == 0 && right->next->x.quo != right->x.quo) break; full_step (right); right = right->next; } while (1); cell_list_set_rewind (coverages); cell_list_render_edge (coverages, left, +1); cell_list_render_edge (coverages, right, -1); left = right->next; } } static void _glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) { polygon_init(converter->polygon, jmp); active_list_init(converter->active); cell_list_init(converter->coverages, jmp); converter->xmin=0; converter->ymin=0; converter->xmax=0; converter->ymax=0; } static void _glitter_scan_converter_fini(glitter_scan_converter_t *self) { if (self->spans != self->spans_embedded) free (self->spans); polygon_fini(self->polygon); cell_list_fini(self->coverages); self->xmin=0; self->ymin=0; self->xmax=0; self->ymax=0; } static grid_scaled_t int_to_grid_scaled(int i, int scale) { /* Clamp to max/min representable scaled number. */ if (i >= 0) { if (i >= INT_MAX/scale) i = INT_MAX/scale; } else { if (i <= INT_MIN/scale) i = INT_MIN/scale; } return i*scale; } #define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) #define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) I glitter_status_t glitter_scan_converter_reset( glitter_scan_converter_t *converter, int xmin, int ymin, int xmax, int ymax) { glitter_status_t status; int max_num_spans; converter->xmin = 0; converter->xmax = 0; converter->ymin = 0; converter->ymax = 0; max_num_spans = xmax - xmin + 1; if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) { converter->spans = _cairo_malloc_ab (max_num_spans, sizeof (cairo_half_open_span_t)); if (unlikely (converter->spans == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else converter->spans = converter->spans_embedded; xmin = int_to_grid_scaled_x(xmin); ymin = int_to_grid_scaled_y(ymin); xmax = int_to_grid_scaled_x(xmax); ymax = int_to_grid_scaled_y(ymax); active_list_reset(converter->active); cell_list_reset(converter->coverages); status = polygon_reset(converter->polygon, ymin, ymax); if (status) return status; converter->xmin = xmin; converter->xmax = xmax; converter->ymin = ymin; converter->ymax = ymax; return GLITTER_STATUS_SUCCESS; } /* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) * These macros convert an input coordinate in the client's * device space to the rasterisation grid. */ /* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use * shifts if possible, and something saneish if not. */ #if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) #else # define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) #endif #if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) #else # define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) #endif #define INPUT_TO_GRID_general(in, out, grid_scale) do { \ long long tmp__ = (long long)(grid_scale) * (in); \ tmp__ >>= GLITTER_INPUT_BITS; \ (out) = tmp__; \ } while (0) /* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan * converter. The coordinates represent pixel positions scaled by * 2**GLITTER_PIXEL_BITS. If this function fails then the scan * converter should be reset or destroyed. Dir must be +1 or -1, * with the latter reversing the orientation of the edge. */ I void glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, const cairo_edge_t *edge) { cairo_edge_t e; INPUT_TO_GRID_Y (edge->top, e.top); INPUT_TO_GRID_Y (edge->bottom, e.bottom); if (e.top >= e.bottom) return; /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); if (e.line.p1.y == e.line.p2.y) e.line.p2.y++; /* little fudge to prevent a div-by-zero */ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); e.dir = edge->dir; polygon_add_edge (converter->polygon, &e); } static void step_edges (struct active_list *active, int count) { struct edge *edge; count *= GRID_Y; for (edge = active->head.next; edge != &active->tail; edge = edge->next) { edge->height_left -= count; if (! edge->height_left) { edge->prev->next = edge->next; edge->next->prev = edge->prev; active->min_height = -1; } } } static glitter_status_t blit_a8 (struct cell_list *cells, cairo_span_renderer_t *renderer, cairo_half_open_span_t *spans, int y, int height, int xmin, int xmax) { struct cell *cell = cells->head.next; int prev_x = xmin, last_x = -1; int16_t cover = 0, last_cover = 0; unsigned num_spans; if (cell == &cells->tail) return CAIRO_STATUS_SUCCESS; /* Skip cells to the left of the clip region. */ while (cell->x < xmin) { cover += cell->covered_height; cell = cell->next; } cover *= GRID_X*2; /* Form the spans from the coverages and areas. */ num_spans = 0; for (; cell->x < xmax; cell = cell->next) { int x = cell->x; int16_t area; if (x > prev_x && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); last_cover = cover; last_x = prev_x; ++num_spans; } cover += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; if (area != last_cover) { spans[num_spans].x = x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); last_cover = area; last_x = x; ++num_spans; } prev_x = x+1; } if (prev_x <= xmax && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); last_cover = cover; last_x = prev_x; ++num_spans; } if (last_x < xmax && last_cover) { spans[num_spans].x = xmax; spans[num_spans].coverage = 0; ++num_spans; } /* Dump them into the renderer. */ return renderer->render_rows (renderer, y, height, spans, num_spans); } #define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0) static glitter_status_t blit_a1 (struct cell_list *cells, cairo_span_renderer_t *renderer, cairo_half_open_span_t *spans, int y, int height, int xmin, int xmax) { struct cell *cell = cells->head.next; int prev_x = xmin, last_x = -1; int16_t cover = 0; uint8_t coverage, last_cover = 0; unsigned num_spans; if (cell == &cells->tail) return CAIRO_STATUS_SUCCESS; /* Skip cells to the left of the clip region. */ while (cell->x < xmin) { cover += cell->covered_height; cell = cell->next; } cover *= GRID_X*2; /* Form the spans from the coverages and areas. */ num_spans = 0; for (; cell->x < xmax; cell = cell->next) { int x = cell->x; int16_t area; coverage = GRID_AREA_TO_A1 (cover); if (x > prev_x && coverage != last_cover) { last_x = spans[num_spans].x = prev_x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } cover += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; coverage = GRID_AREA_TO_A1 (area); if (coverage != last_cover) { last_x = spans[num_spans].x = x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } prev_x = x+1; } coverage = GRID_AREA_TO_A1 (cover); if (prev_x <= xmax && coverage != last_cover) { last_x = spans[num_spans].x = prev_x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } if (last_x < xmax && last_cover) { spans[num_spans].x = xmax; spans[num_spans].coverage = 0; ++num_spans; } if (num_spans == 1) return CAIRO_STATUS_SUCCESS; /* Dump them into the renderer. */ return renderer->render_rows (renderer, y, height, spans, num_spans); } I void glitter_scan_converter_render(glitter_scan_converter_t *converter, unsigned int winding_mask, int antialias, cairo_span_renderer_t *renderer) { int i, j; int ymax_i = converter->ymax / GRID_Y; int ymin_i = converter->ymin / GRID_Y; int xmin_i, xmax_i; int h = ymax_i - ymin_i; struct polygon *polygon = converter->polygon; struct cell_list *coverages = converter->coverages; struct active_list *active = converter->active; struct edge *buckets[GRID_Y] = { 0 }; xmin_i = converter->xmin / GRID_X; xmax_i = converter->xmax / GRID_X; if (xmin_i >= xmax_i) return; /* Render each pixel row. */ for (i = 0; i < h; i = j) { int do_full_row = 0; j = i + 1; /* Determine if we can ignore this row or use the full pixel * stepper. */ if (! polygon->y_buckets[i]) { if (active->head.next == &active->tail) { active->min_height = INT_MAX; active->is_vertical = 1; for (; j < h && ! polygon->y_buckets[j]; j++) ; continue; } do_full_row = can_do_full_row (active); } if (do_full_row) { /* Step by a full pixel row's worth. */ full_row (active, coverages, winding_mask); if (active->is_vertical) { while (j < h && polygon->y_buckets[j] == NULL && active->min_height >= 2*GRID_Y) { active->min_height -= GRID_Y; j++; } if (j != i + 1) step_edges (active, j - (i + 1)); } } else { int sub; polygon_fill_buckets (active, polygon->y_buckets[i], (i+ymin_i)*GRID_Y, buckets); /* Subsample this row. */ for (sub = 0; sub < GRID_Y; sub++) { if (buckets[sub]) { active_list_merge_edges_from_bucket (active, buckets[sub]); buckets[sub] = NULL; } sub_row (active, coverages, winding_mask); } } if (antialias) blit_a8 (coverages, renderer, converter->spans, i+ymin_i, j-i, xmin_i, xmax_i); else blit_a1 (coverages, renderer, converter->spans, i+ymin_i, j-i, xmin_i, xmax_i); cell_list_reset (coverages); active->min_height -= GRID_Y; } } struct _cairo_tor_scan_converter { cairo_scan_converter_t base; glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; jmp_buf jmp; }; typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; static void _cairo_tor_scan_converter_destroy (void *converter) { cairo_tor_scan_converter_t *self = converter; if (self == NULL) { return; } _glitter_scan_converter_fini (self->converter); free(self); } cairo_status_t _cairo_tor_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon) { cairo_tor_scan_converter_t *self = converter; int i; #if 0 FILE *file = fopen ("polygon.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); #endif for (i = 0; i < polygon->num_edges; i++) glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_tor_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_tor_scan_converter_t *self = converter; cairo_status_t status; if ((status = setjmp (self->jmp))) return _cairo_scan_converter_set_error (self, _cairo_error (status)); glitter_scan_converter_render (self->converter, self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, self->antialias != CAIRO_ANTIALIAS_NONE, renderer); return CAIRO_STATUS_SUCCESS; } cairo_scan_converter_t * _cairo_tor_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_tor_scan_converter_t *self; cairo_status_t status; self = malloc (sizeof(struct _cairo_tor_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_tor_scan_converter_destroy; self->base.generate = _cairo_tor_scan_converter_generate; _glitter_scan_converter_init (self->converter, &self->jmp); status = glitter_scan_converter_reset (self->converter, xmin, ymin, xmax, ymax); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; self->antialias = antialias; return &self->base; bail: self->base.destroy(&self->base); bail_nomem: return _cairo_scan_converter_create_in_error (status); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tor22-scan-converter.c000066400000000000000000001354501271037650300274460ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* glitter-paths - polygon scan converter * * Copyright (c) 2008 M Joonas Pihlaja * Copyright (c) 2007 David Turner * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* This is the Glitter paths scan converter incorporated into cairo. * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 * of * * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths */ /* Glitter-paths is a stand alone polygon rasteriser derived from * David Turner's reimplementation of Tor Anderssons's 15x17 * supersampling rasteriser from the Apparition graphics library. The * main new feature here is cheaply choosing per-scan line between * doing fully analytical coverage computation for an entire row at a * time vs. using a supersampling approach. * * David Turner's code can be found at * * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 * * In particular this file incorporates large parts of ftgrays_tor10.h * from raster-comparison-20070813.tar.bz2 */ /* Overview * * A scan converter's basic purpose to take polygon edges and convert * them into an RLE compressed A8 mask. This one works in two phases: * gathering edges and generating spans. * * 1) As the user feeds the scan converter edges they are vertically * clipped and bucketted into a _polygon_ data structure. The edges * are also snapped from the user's coordinates to the subpixel grid * coordinates used during scan conversion. * * user * | * | edges * V * polygon buckets * * 2) Generating spans works by performing a vertical sweep of pixel * rows from top to bottom and maintaining an _active_list_ of edges * that intersect the row. From the active list the fill rule * determines which edges are the left and right edges of the start of * each span, and their contribution is then accumulated into a pixel * coverage list (_cell_list_) as coverage deltas. Once the coverage * deltas of all edges are known we can form spans of constant pixel * coverage by summing the deltas during a traversal of the cell list. * At the end of a pixel row the cell list is sent to a coverage * blitter for rendering to some target surface. * * The pixel coverages are computed by either supersampling the row * and box filtering a mono rasterisation, or by computing the exact * coverages of edges in the active list. The supersampling method is * used whenever some edge starts or stops within the row or there are * edge intersections in the row. * * polygon bucket for \ * current pixel row | * | | * | activate new edges | Repeat GRID_Y times if we * V \ are supersampling this row, * active list / or just once if we're computing * | | analytical coverage. * | coverage deltas | * V | * pixel coverage list / * | * V * coverage blitter */ #include "cairoint.h" #include "cairo-spans-private.h" #include "cairo-error-private.h" #include #include #include #include /*------------------------------------------------------------------------- * cairo specific config */ #define I static /* Prefer cairo's status type. */ #define GLITTER_HAVE_STATUS_T 1 #define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS #define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY typedef cairo_status_t glitter_status_t; /* The input coordinate scale and the rasterisation grid scales. */ #define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS //#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS //#define GRID_Y 15 #define GRID_X_BITS 2 #define GRID_Y_BITS 2 /* Set glitter up to use a cairo span renderer to do the coverage * blitting. */ struct pool; struct cell_list; /*------------------------------------------------------------------------- * glitter-paths.h */ /* "Input scaled" numbers are fixed precision reals with multiplier * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as * pixel scaled numbers. These get converted to the internal grid * scaled numbers as soon as possible. Internal overflow is possible * if GRID_X/Y inside glitter-paths.c is larger than * 1< #include #include /* All polygon coordinates are snapped onto a subsample grid. "Grid * scaled" numbers are fixed precision reals with multiplier GRID_X or * GRID_Y. */ typedef int grid_scaled_t; typedef int grid_scaled_x_t; typedef int grid_scaled_y_t; /* Default x/y scale factors. * You can either define GRID_X/Y_BITS to get a power-of-two scale * or define GRID_X/Y separately. */ #if !defined(GRID_X) && !defined(GRID_X_BITS) # define GRID_X_BITS 8 #endif #if !defined(GRID_Y) && !defined(GRID_Y_BITS) # define GRID_Y 15 #endif /* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ #ifdef GRID_X_BITS # define GRID_X (1 << GRID_X_BITS) #endif #ifdef GRID_Y_BITS # define GRID_Y (1 << GRID_Y_BITS) #endif /* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into * integer and fractional parts. The integer part is floored. */ #if defined(GRID_X_TO_INT_FRAC) /* do nothing */ #elif defined(GRID_X_BITS) # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) #else # define GRID_X_TO_INT_FRAC(x, i, f) \ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) #endif #define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ (i) = (t) / (m); \ (f) = (t) % (m); \ if ((f) < 0) { \ --(i); \ (f) += (m); \ } \ } while (0) #define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ (f) = (t) & ((1 << (b)) - 1); \ (i) = (t) >> (b); \ } while (0) /* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want * to be able to represent exactly areas of subpixel trapezoids whose * vertices are given in grid scaled coordinates. The scale factor * comes from needing to accurately represent the area 0.5*dx*dy of a * triangle with base dx and height dy in grid scaled numbers. */ #define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ /* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ #if GRID_XY == 510 # define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) #elif GRID_XY == 255 # define GRID_AREA_TO_ALPHA(c) (c) #elif GRID_XY == 64 # define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) #elif GRID_XY == 32 # define GRID_AREA_TO_ALPHA(c) (((c) << 3) | -(((c) & 0x20) >> 5)) #elif GRID_XY == 128 # define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) #elif GRID_XY == 256 # define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) #elif GRID_XY == 15 # define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) #elif GRID_XY == 2*256*15 # define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) #else # define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) #endif #define UNROLL3(x) x x x struct quorem { int32_t quo; int32_t rem; }; /* Header for a chunk of memory in a memory pool. */ struct _pool_chunk { /* # bytes used in this chunk. */ size_t size; /* # bytes total in this chunk */ size_t capacity; /* Pointer to the previous chunk or %NULL if this is the sentinel * chunk in the pool header. */ struct _pool_chunk *prev_chunk; /* Actual data starts here. Well aligned for pointers. */ }; /* A memory pool. This is supposed to be embedded on the stack or * within some other structure. It may optionally be followed by an * embedded array from which requests are fulfilled until * malloc needs to be called to allocate a first real chunk. */ struct pool { /* Chunk we're allocating from. */ struct _pool_chunk *current; jmp_buf *jmp; /* Free list of previously allocated chunks. All have >= default * capacity. */ struct _pool_chunk *first_free; /* The default capacity of a chunk. */ size_t default_capacity; /* Header for the sentinel chunk. Directly following the pool * struct should be some space for embedded elements from which * the sentinel chunk allocates from. */ struct _pool_chunk sentinel[1]; }; /* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ struct edge *next, *prev; /* Number of subsample rows remaining to scan convert of this * edge. */ grid_scaled_y_t height_left; /* Original sign of the edge: +1 for downwards, -1 for upwards * edges. */ int dir; int vertical; /* Current x coordinate while the edge is on the active * list. Initialised to the x coordinate of the top of the * edge. The quotient is in grid_scaled_x_t units and the * remainder is mod dy in grid_scaled_y_t units.*/ struct quorem x; /* Advance of the current x when moving down a subsample line. */ struct quorem dxdy; /* The clipped y of the top of the edge. */ grid_scaled_y_t ytop; /* y2-y1 after orienting the edge downwards. */ grid_scaled_y_t dy; }; #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y) /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { /* The vertical clip extents. */ grid_scaled_y_t ymin, ymax; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64]; struct { struct pool base[1]; struct edge embedded[32]; } edge_pool; }; /* A cell records the effect on pixel coverage of polygon edges * passing through a pixel. It contains two accumulators of pixel * coverage. * * Consider the effects of a polygon edge on the coverage of a pixel * it intersects and that of the following one. The coverage of the * following pixel is the height of the edge multiplied by the width * of the pixel, and the coverage of the pixel itself is the area of * the trapezoid formed by the edge and the right side of the pixel. * * +-----------------------+-----------------------+ * | | | * | | | * |_______________________|_______________________| * | \...................|.......................|\ * | \..................|.......................| | * | \.................|.......................| | * | \....covered.....|.......................| | * | \....area.......|.......................| } covered height * | \..............|.......................| | * |uncovered\.............|.......................| | * | area \............|.......................| | * |___________\...........|.......................|/ * | | | * | | | * | | | * +-----------------------+-----------------------+ * * Since the coverage of the following pixel will always be a multiple * of the width of the pixel, we can store the height of the covered * area instead. The coverage of the pixel itself is the total * coverage minus the area of the uncovered area to the left of the * edge. As it's faster to compute the uncovered area we only store * that and subtract it from the total coverage later when forming * spans to blit. * * The heights and areas are signed, with left edges of the polygon * having positive sign and right edges having negative sign. When * two edges intersect they swap their left/rightness so their * contribution above and below the intersection point must be * computed separately. */ struct cell { struct cell *next; int x; int16_t uncovered_area; int16_t covered_height; }; /* A cell list represents the scan line sparsely as cells ordered by * ascending x. It is geared towards scanning the cells in order * using an internal cursor. */ struct cell_list { /* Sentinel nodes */ struct cell head, tail; /* Cursor state for iterating through the cell list. */ struct cell *cursor, *rewind; /* Cells in the cell list are owned by the cell list and are * allocated from this pool. */ struct { struct pool base[1]; struct cell embedded[32]; } cell_pool; }; struct cell_pair { struct cell *cell1; struct cell *cell2; }; /* The active list contains edges in the current scan line ordered by * the x-coordinate of the intercept of the edge and the scan line. */ struct active_list { /* Leftmost edge on the current scan line. */ struct edge head, tail; /* A lower bound on the height of the active edges is used to * estimate how soon some active edge ends. We can't advance the * scan conversion by a full pixel row if an edge ends somewhere * within it. */ grid_scaled_y_t min_height; int is_vertical; }; struct glitter_scan_converter { struct polygon polygon[1]; struct active_list active[1]; struct cell_list coverages[1]; cairo_half_open_span_t *spans; cairo_half_open_span_t spans_embedded[64]; /* Clip box. */ grid_scaled_x_t xmin, xmax; grid_scaled_y_t ymin, ymax; }; /* Compute the floored division a/b. Assumes / and % perform symmetric * division. */ inline static struct quorem floored_divrem(int a, int b) { struct quorem qr; qr.quo = a/b; qr.rem = a%b; if ((a^b)<0 && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } /* Compute the floored division (x*a)/b. Assumes / and % perform symmetric * division. */ static struct quorem floored_muldivrem(int x, int a, int b) { struct quorem qr; long long xa = (long long)x*a; qr.quo = xa/b; qr.rem = xa%b; if ((xa>=0) != (b>=0) && qr.rem) { qr.quo -= 1; qr.rem += b; } return qr; } static struct _pool_chunk * _pool_chunk_init( struct _pool_chunk *p, struct _pool_chunk *prev_chunk, size_t capacity) { p->prev_chunk = prev_chunk; p->size = 0; p->capacity = capacity; return p; } static struct _pool_chunk * _pool_chunk_create(struct pool *pool, size_t size) { struct _pool_chunk *p; p = malloc(size + sizeof(struct _pool_chunk)); if (unlikely (NULL == p)) longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); return _pool_chunk_init(p, pool->current, size); } static void pool_init(struct pool *pool, jmp_buf *jmp, size_t default_capacity, size_t embedded_capacity) { pool->jmp = jmp; pool->current = pool->sentinel; pool->first_free = NULL; pool->default_capacity = default_capacity; _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); } static void pool_fini(struct pool *pool) { struct _pool_chunk *p = pool->current; do { while (NULL != p) { struct _pool_chunk *prev = p->prev_chunk; if (p != pool->sentinel) free(p); p = prev; } p = pool->first_free; pool->first_free = NULL; } while (NULL != p); } /* Satisfy an allocation by first allocating a new large enough chunk * and adding it to the head of the pool's chunk list. This function * is called as a fallback if pool_alloc() couldn't do a quick * allocation from the current chunk in the pool. */ static void * _pool_alloc_from_new_chunk( struct pool *pool, size_t size) { struct _pool_chunk *chunk; void *obj; size_t capacity; /* If the allocation is smaller than the default chunk size then * try getting a chunk off the free list. Force alloc of a new * chunk for large requests. */ capacity = size; chunk = NULL; if (size < pool->default_capacity) { capacity = pool->default_capacity; chunk = pool->first_free; if (chunk) { pool->first_free = chunk->prev_chunk; _pool_chunk_init(chunk, pool->current, chunk->capacity); } } if (NULL == chunk) chunk = _pool_chunk_create (pool, capacity); pool->current = chunk; obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } /* Allocate size bytes from the pool. The first allocated address * returned from a pool is aligned to sizeof(void*). Subsequent * addresses will maintain alignment as long as multiples of void* are * allocated. Returns the address of a new memory area or %NULL on * allocation failures. The pool retains ownership of the returned * memory. */ inline static void * pool_alloc (struct pool *pool, size_t size) { struct _pool_chunk *chunk = pool->current; if (size <= chunk->capacity - chunk->size) { void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); chunk->size += size; return obj; } else { return _pool_alloc_from_new_chunk(pool, size); } } /* Relinquish all pool_alloced memory back to the pool. */ static void pool_reset (struct pool *pool) { /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; if (chunk != pool->sentinel) { while (chunk->prev_chunk != pool->sentinel) { chunk = chunk->prev_chunk; } chunk->prev_chunk = pool->first_free; pool->first_free = pool->current; } /* Reset the sentinel as the current chunk. */ pool->current = pool->sentinel; pool->sentinel->size = 0; } /* Rewinds the cell list's cursor to the beginning. After rewinding * we're good to cell_list_find() the cell any x coordinate. */ inline static void cell_list_rewind (struct cell_list *cells) { cells->cursor = &cells->head; } inline static void cell_list_maybe_rewind (struct cell_list *cells, int x) { if (x < cells->cursor->x) { cells->cursor = cells->rewind; if (x < cells->cursor->x) cells->cursor = &cells->head; } } inline static void cell_list_set_rewind (struct cell_list *cells) { cells->rewind = cells->cursor; } static void cell_list_init(struct cell_list *cells, jmp_buf *jmp) { pool_init(cells->cell_pool.base, jmp, 256*sizeof(struct cell), sizeof(cells->cell_pool.embedded)); cells->tail.next = NULL; cells->tail.x = INT_MAX; cells->head.x = INT_MIN; cells->head.next = &cells->tail; cell_list_rewind (cells); } static void cell_list_fini(struct cell_list *cells) { pool_fini (cells->cell_pool.base); } /* Empty the cell list. This is called at the start of every pixel * row. */ inline static void cell_list_reset (struct cell_list *cells) { cell_list_rewind (cells); cells->head.next = &cells->tail; pool_reset (cells->cell_pool.base); } inline static struct cell * cell_list_alloc (struct cell_list *cells, struct cell *tail, int x) { struct cell *cell; cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); cell->next = tail->next; tail->next = cell; cell->x = x; *(uint32_t *)&cell->uncovered_area = 0; return cell; } /* Find a cell at the given x-coordinate. Returns %NULL if a new cell * needed to be allocated but couldn't be. Cells must be found with * non-decreasing x-coordinate until the cell list is rewound using * cell_list_rewind(). Ownership of the returned cell is retained by * the cell list. */ inline static struct cell * cell_list_find (struct cell_list *cells, int x) { struct cell *tail = cells->cursor; if (tail->x == x) return tail; while (1) { UNROLL3({ if (tail->next->x > x) break; tail = tail->next; }); } if (tail->x != x) tail = cell_list_alloc (cells, tail, x); return cells->cursor = tail; } /* Find two cells at x1 and x2. This is exactly equivalent * to * * pair.cell1 = cell_list_find(cells, x1); * pair.cell2 = cell_list_find(cells, x2); * * except with less function call overhead. */ inline static struct cell_pair cell_list_find_pair(struct cell_list *cells, int x1, int x2) { struct cell_pair pair; pair.cell1 = cells->cursor; while (1) { UNROLL3({ if (pair.cell1->next->x > x1) break; pair.cell1 = pair.cell1->next; }); } if (pair.cell1->x != x1) pair.cell1 = cell_list_alloc (cells, pair.cell1, x1); pair.cell2 = pair.cell1; while (1) { UNROLL3({ if (pair.cell2->next->x > x2) break; pair.cell2 = pair.cell2->next; }); } if (pair.cell2->x != x2) pair.cell2 = cell_list_alloc (cells, pair.cell2, x2); cells->cursor = pair.cell2; return pair; } /* Add a subpixel span covering [x1, x2) to the coverage cells. */ inline static void cell_list_add_subspan(struct cell_list *cells, grid_scaled_x_t x1, grid_scaled_x_t x2) { int ix1, fx1; int ix2, fx2; if (x1 == x2) return; GRID_X_TO_INT_FRAC(x1, ix1, fx1); GRID_X_TO_INT_FRAC(x2, ix2, fx2); if (ix1 != ix2) { struct cell_pair p; p = cell_list_find_pair(cells, ix1, ix2); p.cell1->uncovered_area += 2*fx1; ++p.cell1->covered_height; p.cell2->uncovered_area -= 2*fx2; --p.cell2->covered_height; } else { struct cell *cell = cell_list_find(cells, ix1); cell->uncovered_area += 2*(fx1-fx2); } } /* Adds the analytical coverage of an edge crossing the current pixel * row to the coverage cells and advances the edge's x position to the * following row. * * This function is only called when we know that during this pixel row: * * 1) The relative order of all edges on the active list doesn't * change. In particular, no edges intersect within this row to pixel * precision. * * 2) No new edges start in this row. * * 3) No existing edges end mid-row. * * This function depends on being called with all edges from the * active list in the order they appear on the list (i.e. with * non-decreasing x-coordinate.) */ static void cell_list_render_edge(struct cell_list *cells, struct edge *edge, int sign) { grid_scaled_x_t fx; struct cell *cell; int ix; GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx); /* We always know that ix1 is >= the cell list cursor in this * case due to the no-intersections precondition. */ cell = cell_list_find(cells, ix); cell->covered_height += sign*GRID_Y; cell->uncovered_area += sign*2*fx*GRID_Y; } static void polygon_init (struct polygon *polygon, jmp_buf *jmp) { polygon->ymin = polygon->ymax = 0; polygon->y_buckets = polygon->y_buckets_embedded; pool_init (polygon->edge_pool.base, jmp, 8192 - sizeof (struct _pool_chunk), sizeof (polygon->edge_pool.embedded)); } static void polygon_fini (struct polygon *polygon) { if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); pool_fini (polygon->edge_pool.base); } /* Empties the polygon of all edges. The polygon is then prepared to * receive new edges and clip them to the vertical range * [ymin,ymax). */ static glitter_status_t polygon_reset (struct polygon *polygon, grid_scaled_y_t ymin, grid_scaled_y_t ymax) { unsigned h = ymax - ymin; unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin); pool_reset(polygon->edge_pool.base); if (unlikely (h > 0x7FFFFFFFU - GRID_Y)) goto bail_no_mem; /* even if you could, you wouldn't want to. */ if (polygon->y_buckets != polygon->y_buckets_embedded) free (polygon->y_buckets); polygon->y_buckets = polygon->y_buckets_embedded; if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (num_buckets, sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) goto bail_no_mem; } memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); polygon->ymin = ymin; polygon->ymax = ymax; return GLITTER_STATUS_SUCCESS; bail_no_mem: polygon->ymin = 0; polygon->ymax = 0; return GLITTER_STATUS_NO_MEMORY; } static void _polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, struct edge *e) { unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); struct edge **ptail = &polygon->y_buckets[ix]; e->next = *ptail; *ptail = e; } inline static void polygon_add_edge (struct polygon *polygon, const cairo_edge_t *edge) { struct edge *e; grid_scaled_x_t dx; grid_scaled_y_t dy; grid_scaled_y_t ytop, ybot; grid_scaled_y_t ymin = polygon->ymin; grid_scaled_y_t ymax = polygon->ymax; if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) return; e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); dx = edge->line.p2.x - edge->line.p1.x; dy = edge->line.p2.y - edge->line.p1.y; e->dy = dy; e->dir = edge->dir; ytop = edge->top >= ymin ? edge->top : ymin; ybot = edge->bottom <= ymax ? edge->bottom : ymax; e->ytop = ytop; e->height_left = ybot - ytop; if (dx == 0) { e->vertical = TRUE; e->x.quo = edge->line.p1.x; e->x.rem = 0; e->dxdy.quo = 0; e->dxdy.rem = 0; } else { e->vertical = FALSE; e->dxdy = floored_divrem (dx, dy); if (ytop == edge->line.p1.y) { e->x.quo = edge->line.p1.x; e->x.rem = 0; } else { e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); e->x.quo += edge->line.p1.x; } } _polygon_insert_edge_into_its_y_bucket (polygon, e); e->x.rem -= dy; /* Bias the remainder for faster * edge advancement. */ } static void active_list_reset (struct active_list *active) { active->head.vertical = 1; active->head.height_left = INT_MAX; active->head.x.quo = INT_MIN; active->head.prev = NULL; active->head.next = &active->tail; active->tail.prev = &active->head; active->tail.next = NULL; active->tail.x.quo = INT_MAX; active->tail.height_left = INT_MAX; active->tail.vertical = 1; active->min_height = 0; active->is_vertical = 1; } static void active_list_init(struct active_list *active) { active_list_reset(active); } /* * Merge two sorted edge lists. * Input: * - head_a: The head of the first list. * - head_b: The head of the second list; head_b cannot be NULL. * Output: * Returns the head of the merged list. * * Implementation notes: * To make it fast (in particular, to reduce to an insertion sort whenever * one of the two input lists only has a single element) we iterate through * a list until its head becomes greater than the head of the other list, * then we switch their roles. As soon as one of the two lists is empty, we * just attach the other one to the current list and exit. * Writes to memory are only needed to "switch" lists (as it also requires * attaching to the output list the list which we will be iterating next) and * to attach the last non-empty list. */ static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { struct edge *head, **next, *prev; int32_t x; prev = head_a->prev; next = &head; if (head_a->x.quo <= head_b->x.quo) { head = head_a; } else { head = head_b; head_b->prev = prev; goto start_with_b; } do { x = head_b->x.quo; while (head_a != NULL && head_a->x.quo <= x) { prev = head_a; next = &head_a->next; head_a = head_a->next; } head_b->prev = prev; *next = head_b; if (head_a == NULL) return head; start_with_b: x = head_a->x.quo; while (head_b != NULL && head_b->x.quo <= x) { prev = head_b; next = &head_b->next; head_b = head_b->next; } head_a->prev = prev; *next = head_a; if (head_b == NULL) return head; } while (1); } /* * Sort (part of) a list. * Input: * - list: The list to be sorted; list cannot be NULL. * - limit: Recursion limit. * Output: * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the * input list; if the input list has fewer elements, head_out be a sorted list * containing all the elements of the input list. * Returns the head of the list of unprocessed elements (NULL if the sorted list contains * all the elements of the input list). * * Implementation notes: * Special case single element list, unroll/inline the sorting of the first two elements. * Some tail recursion is used since we iterate on the bottom-up solution of the problem * (we start with a small sorted list and keep merging other lists of the same size to it). */ static struct edge * sort_edges (struct edge *list, unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; unsigned int i; head_other = list->next; if (head_other == NULL) { *head_out = list; return NULL; } remaining = head_other->next; if (list->x.quo <= head_other->x.quo) { *head_out = list; head_other->next = NULL; } else { *head_out = head_other; head_other->prev = list->prev; head_other->next = list; list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } return remaining; } static struct edge * merge_unsorted_edges (struct edge *head, struct edge *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); return merge_sorted_edges (head, unsorted); } /* Test if the edges on the active list can be safely advanced by a * full row without intersections or any edges ending. */ inline static int can_do_full_row (struct active_list *active) { const struct edge *e; /* Recomputes the minimum height of all edges on the active * list if we have been dropping edges. */ if (active->min_height <= 0) { int min_height = INT_MAX; int is_vertical = 1; e = active->head.next; while (NULL != e) { if (e->height_left < min_height) min_height = e->height_left; is_vertical &= e->vertical; e = e->next; } active->is_vertical = is_vertical; active->min_height = min_height; } if (active->min_height < GRID_Y) return 0; return active->is_vertical; } /* Merges edges on the given subpixel row from the polygon to the * active_list. */ inline static void active_list_merge_edges_from_bucket(struct active_list *active, struct edge *edges) { active->head.next = merge_unsorted_edges (active->head.next, edges); } inline static void polygon_fill_buckets (struct active_list *active, struct edge *edge, int y, struct edge **buckets) { grid_scaled_y_t min_height = active->min_height; int is_vertical = active->is_vertical; while (edge) { struct edge *next = edge->next; int suby = edge->ytop - y; if (buckets[suby]) buckets[suby]->prev = edge; edge->next = buckets[suby]; edge->prev = NULL; buckets[suby] = edge; if (edge->height_left < min_height) min_height = edge->height_left; is_vertical &= edge->vertical; edge = next; } active->is_vertical = is_vertical; active->min_height = min_height; } inline static void sub_row (struct active_list *active, struct cell_list *coverages, unsigned int mask) { struct edge *edge = active->head.next; int xstart = INT_MIN, prev_x = INT_MIN; int winding = 0; cell_list_rewind (coverages); while (&active->tail != edge) { struct edge *next = edge->next; int xend = edge->x.quo; if (--edge->height_left) { edge->x.quo += edge->dxdy.quo; edge->x.rem += edge->dxdy.rem; if (edge->x.rem >= 0) { ++edge->x.quo; edge->x.rem -= edge->dy; } if (edge->x.quo < prev_x) { struct edge *pos = edge->prev; pos->next = next; next->prev = pos; do { pos = pos->prev; } while (edge->x.quo < pos->x.quo); pos->next->prev = edge; edge->next = pos->next; edge->prev = pos; pos->next = edge; } else prev_x = edge->x.quo; } else { edge->prev->next = next; next->prev = edge->prev; } winding += edge->dir; if ((winding & mask) == 0) { if (next->x.quo != xend) { cell_list_add_subspan (coverages, xstart, xend); xstart = INT_MIN; } } else if (xstart == INT_MIN) xstart = xend; edge = next; } } inline static void dec (struct edge *e, int h) { e->height_left -= h; if (e->height_left == 0) { e->prev->next = e->next; e->next->prev = e->prev; } } static void full_row (struct active_list *active, struct cell_list *coverages, unsigned int mask) { struct edge *left = active->head.next; while (&active->tail != left) { struct edge *right; int winding; dec (left, GRID_Y); winding = left->dir; right = left->next; do { dec (right, GRID_Y); winding += right->dir; if ((winding & mask) == 0 && right->next->x.quo != right->x.quo) break; right = right->next; } while (1); cell_list_set_rewind (coverages); cell_list_render_edge (coverages, left, +1); cell_list_render_edge (coverages, right, -1); left = right->next; } } static void _glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) { polygon_init(converter->polygon, jmp); active_list_init(converter->active); cell_list_init(converter->coverages, jmp); converter->xmin=0; converter->ymin=0; converter->xmax=0; converter->ymax=0; } static void _glitter_scan_converter_fini(glitter_scan_converter_t *self) { if (self->spans != self->spans_embedded) free (self->spans); polygon_fini(self->polygon); cell_list_fini(self->coverages); self->xmin=0; self->ymin=0; self->xmax=0; self->ymax=0; } static grid_scaled_t int_to_grid_scaled(int i, int scale) { /* Clamp to max/min representable scaled number. */ if (i >= 0) { if (i >= INT_MAX/scale) i = INT_MAX/scale; } else { if (i <= INT_MIN/scale) i = INT_MIN/scale; } return i*scale; } #define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) #define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) I glitter_status_t glitter_scan_converter_reset( glitter_scan_converter_t *converter, int xmin, int ymin, int xmax, int ymax) { glitter_status_t status; converter->xmin = 0; converter->xmax = 0; converter->ymin = 0; converter->ymax = 0; if (xmax - xmin > ARRAY_LENGTH(converter->spans_embedded)) { converter->spans = _cairo_malloc_ab (xmax - xmin, sizeof (cairo_half_open_span_t)); if (unlikely (converter->spans == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else converter->spans = converter->spans_embedded; xmin = int_to_grid_scaled_x(xmin); ymin = int_to_grid_scaled_y(ymin); xmax = int_to_grid_scaled_x(xmax); ymax = int_to_grid_scaled_y(ymax); active_list_reset(converter->active); cell_list_reset(converter->coverages); status = polygon_reset(converter->polygon, ymin, ymax); if (status) return status; converter->xmin = xmin; converter->xmax = xmax; converter->ymin = ymin; converter->ymax = ymax; return GLITTER_STATUS_SUCCESS; } /* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) * These macros convert an input coordinate in the client's * device space to the rasterisation grid. */ /* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use * shifts if possible, and something saneish if not. */ #if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) #else # define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) #endif #if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS # define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) #else # define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) #endif #define INPUT_TO_GRID_general(in, out, grid_scale) do { \ long long tmp__ = (long long)(grid_scale) * (in); \ tmp__ >>= GLITTER_INPUT_BITS; \ (out) = tmp__; \ } while (0) /* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan * converter. The coordinates represent pixel positions scaled by * 2**GLITTER_PIXEL_BITS. If this function fails then the scan * converter should be reset or destroyed. Dir must be +1 or -1, * with the latter reversing the orientation of the edge. */ I void glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, const cairo_edge_t *edge) { cairo_edge_t e; INPUT_TO_GRID_Y (edge->top, e.top); INPUT_TO_GRID_Y (edge->bottom, e.bottom); if (e.top >= e.bottom) return; /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); if (e.line.p1.y == e.line.p2.y) e.line.p2.y++; /* Fudge to prevent div-by-zero */ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); e.dir = edge->dir; polygon_add_edge (converter->polygon, &e); } static void step_edges (struct active_list *active, int count) { struct edge *edge; count *= GRID_Y; for (edge = active->head.next; edge != &active->tail; edge = edge->next) { edge->height_left -= count; if (! edge->height_left) { edge->prev->next = edge->next; edge->next->prev = edge->prev; } } } static glitter_status_t blit_a8 (struct cell_list *cells, cairo_span_renderer_t *renderer, cairo_half_open_span_t *spans, int y, int height, int xmin, int xmax) { struct cell *cell = cells->head.next; int prev_x = xmin, last_x = -1; int16_t cover = 0, last_cover = 0; unsigned num_spans; if (cell == &cells->tail) return CAIRO_STATUS_SUCCESS; /* Skip cells to the left of the clip region. */ while (cell->x < xmin) { cover += cell->covered_height; cell = cell->next; } cover *= GRID_X*2; /* Form the spans from the coverages and areas. */ num_spans = 0; for (; cell->x < xmax; cell = cell->next) { int x = cell->x; int16_t area; if (x > prev_x && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); last_cover = cover; last_x = prev_x; ++num_spans; } cover += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; if (area != last_cover) { spans[num_spans].x = x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); last_cover = area; last_x = x; ++num_spans; } prev_x = x+1; } if (prev_x <= xmax && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); last_cover = cover; last_x = prev_x; ++num_spans; } if (last_x < xmax && last_cover) { spans[num_spans].x = xmax; spans[num_spans].coverage = 0; ++num_spans; } /* Dump them into the renderer. */ return renderer->render_rows (renderer, y, height, spans, num_spans); } #define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0) static glitter_status_t blit_a1 (struct cell_list *cells, cairo_span_renderer_t *renderer, cairo_half_open_span_t *spans, int y, int height, int xmin, int xmax) { struct cell *cell = cells->head.next; int prev_x = xmin, last_x = -1; int16_t cover = 0; uint8_t coverage, last_cover = 0; unsigned num_spans; if (cell == &cells->tail) return CAIRO_STATUS_SUCCESS; /* Skip cells to the left of the clip region. */ while (cell->x < xmin) { cover += cell->covered_height; cell = cell->next; } cover *= GRID_X*2; /* Form the spans from the coverages and areas. */ num_spans = 0; for (; cell->x < xmax; cell = cell->next) { int x = cell->x; int16_t area; coverage = GRID_AREA_TO_A1 (cover); if (x > prev_x && coverage != last_cover) { last_x = spans[num_spans].x = prev_x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } cover += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; coverage = GRID_AREA_TO_A1 (area); if (coverage != last_cover) { last_x = spans[num_spans].x = x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } prev_x = x+1; } coverage = GRID_AREA_TO_A1 (cover); if (prev_x <= xmax && coverage != last_cover) { last_x = spans[num_spans].x = prev_x; last_cover = spans[num_spans].coverage = coverage; ++num_spans; } if (last_x < xmax && last_cover) { spans[num_spans].x = xmax; spans[num_spans].coverage = 0; ++num_spans; } if (num_spans == 1) return CAIRO_STATUS_SUCCESS; /* Dump them into the renderer. */ return renderer->render_rows (renderer, y, height, spans, num_spans); } I void glitter_scan_converter_render(glitter_scan_converter_t *converter, unsigned int winding_mask, int antialias, cairo_span_renderer_t *renderer) { int i, j; int ymax_i = converter->ymax / GRID_Y; int ymin_i = converter->ymin / GRID_Y; int xmin_i, xmax_i; int h = ymax_i - ymin_i; struct polygon *polygon = converter->polygon; struct cell_list *coverages = converter->coverages; struct active_list *active = converter->active; struct edge *buckets[GRID_Y] = { 0 }; xmin_i = converter->xmin / GRID_X; xmax_i = converter->xmax / GRID_X; if (xmin_i >= xmax_i) return; /* Render each pixel row. */ for (i = 0; i < h; i = j) { int do_full_row = 0; j = i + 1; /* Determine if we can ignore this row or use the full pixel * stepper. */ if (! polygon->y_buckets[i]) { if (active->head.next == &active->tail) { active->min_height = INT_MAX; active->is_vertical = 1; for (; j < h && ! polygon->y_buckets[j]; j++) ; continue; } do_full_row = can_do_full_row (active); } if (do_full_row) { /* Step by a full pixel row's worth. */ full_row (active, coverages, winding_mask); if (active->is_vertical) { while (j < h && polygon->y_buckets[j] == NULL && active->min_height >= 2*GRID_Y) { active->min_height -= GRID_Y; j++; } if (j != i + 1) step_edges (active, j - (i + 1)); } } else { int sub; polygon_fill_buckets (active, polygon->y_buckets[i], (i+ymin_i)*GRID_Y, buckets); /* Subsample this row. */ for (sub = 0; sub < GRID_Y; sub++) { if (buckets[sub]) { active_list_merge_edges_from_bucket (active, buckets[sub]); buckets[sub] = NULL; } sub_row (active, coverages, winding_mask); } } if (antialias) blit_a8 (coverages, renderer, converter->spans, i+ymin_i, j-i, xmin_i, xmax_i); else blit_a1 (coverages, renderer, converter->spans, i+ymin_i, j-i, xmin_i, xmax_i); cell_list_reset (coverages); active->min_height -= GRID_Y; } } struct _cairo_tor22_scan_converter { cairo_scan_converter_t base; glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; jmp_buf jmp; }; typedef struct _cairo_tor22_scan_converter cairo_tor22_scan_converter_t; static void _cairo_tor22_scan_converter_destroy (void *converter) { cairo_tor22_scan_converter_t *self = converter; if (self == NULL) { return; } _glitter_scan_converter_fini (self->converter); free(self); } cairo_status_t _cairo_tor22_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon) { cairo_tor22_scan_converter_t *self = converter; int i; #if 0 FILE *file = fopen ("polygon.txt", "w"); _cairo_debug_print_polygon (file, polygon); fclose (file); #endif for (i = 0; i < polygon->num_edges; i++) glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_tor22_scan_converter_generate (void *converter, cairo_span_renderer_t *renderer) { cairo_tor22_scan_converter_t *self = converter; cairo_status_t status; if ((status = setjmp (self->jmp))) return _cairo_scan_converter_set_error (self, _cairo_error (status)); glitter_scan_converter_render (self->converter, self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, self->antialias != CAIRO_ANTIALIAS_NONE, renderer); return CAIRO_STATUS_SUCCESS; } cairo_scan_converter_t * _cairo_tor22_scan_converter_create (int xmin, int ymin, int xmax, int ymax, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias) { cairo_tor22_scan_converter_t *self; cairo_status_t status; self = malloc (sizeof(struct _cairo_tor22_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_tor22_scan_converter_destroy; self->base.generate = _cairo_tor22_scan_converter_generate; _glitter_scan_converter_init (self->converter, &self->jmp); status = glitter_scan_converter_reset (self->converter, xmin, ymin, xmax, ymax); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; self->antialias = antialias; return &self->base; bail: self->base.destroy(&self->base); bail_nomem: return _cairo_scan_converter_create_in_error (status); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-toy-font-face.c000066400000000000000000000373311271037650300262210ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005,2008 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Graydon Hoare * Owen Taylor * Behdad Esfahbod */ #define _BSD_SOURCE /* for strdup() */ #include "cairoint.h" #include "cairo-error-private.h" static const cairo_font_face_t _cairo_font_face_null_pointer = { { 0 }, /* hash_entry */ CAIRO_STATUS_NULL_POINTER, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL }; static const cairo_font_face_t _cairo_font_face_invalid_string = { { 0 }, /* hash_entry */ CAIRO_STATUS_INVALID_STRING, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL }; static const cairo_font_face_t _cairo_font_face_invalid_slant = { { 0 }, /* hash_entry */ CAIRO_STATUS_INVALID_SLANT, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL }; static const cairo_font_face_t _cairo_font_face_invalid_weight = { { 0 }, /* hash_entry */ CAIRO_STATUS_INVALID_WEIGHT, /* status */ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ { 0, 0, 0, NULL }, /* user_data */ NULL }; static const cairo_font_face_backend_t _cairo_toy_font_face_backend; static int _cairo_toy_font_face_keys_equal (const void *key_a, const void *key_b); /* We maintain a hash table from family/weight/slant => * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of * this mapping is to provide unique #cairo_font_face_t values so that * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t * works. Once the corresponding #cairo_font_face_t objects fall out of * downstream caches, we don't need them in this hash table anymore. * * Modifications to this hash table are protected by * _cairo_toy_font_face_mutex. */ static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; static cairo_hash_table_t * _cairo_toy_font_face_hash_table_lock (void) { CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); if (cairo_toy_font_face_hash_table == NULL) { cairo_toy_font_face_hash_table = _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); if (cairo_toy_font_face_hash_table == NULL) { CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); return NULL; } } return cairo_toy_font_face_hash_table; } static void _cairo_toy_font_face_hash_table_unlock (void) { CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); } /** * _cairo_toy_font_face_init_key: * * Initialize those portions of #cairo_toy_font_face_t needed to use * it as a hash table key, including the hash code buried away in * font_face->base.hash_entry. No memory allocation is performed here * so that no fini call is needed. We do this to make it easier to use * an automatic #cairo_toy_font_face_t variable as a key. **/ static void _cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { unsigned long hash; key->family = family; key->owns_family = FALSE; key->slant = slant; key->weight = weight; /* 1607 and 1451 are just a couple of arbitrary primes. */ hash = _cairo_hash_string (family); hash += ((unsigned long) slant) * 1607; hash += ((unsigned long) weight) * 1451; key->base.hash_entry.hash = hash; } static cairo_status_t _cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face, cairo_font_face_t **impl_font_face) { const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (font_face->base.status)) return font_face->base.status; if (backend->create_for_toy != NULL && 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT, strlen (CAIRO_USER_FONT_FAMILY_DEFAULT))) { status = backend->create_for_toy (font_face, impl_font_face); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { backend = &_cairo_user_font_face_backend; status = backend->create_for_toy (font_face, impl_font_face); } return status; } static cairo_status_t _cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { char *family_copy; cairo_status_t status; family_copy = strdup (family); if (unlikely (family_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight); font_face->owns_family = TRUE; _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); status = _cairo_toy_font_face_create_impl_face (font_face, &font_face->impl_face); if (unlikely (status)) { free (family_copy); return status; } return CAIRO_STATUS_SUCCESS; } static void _cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) { /* We assert here that we own font_face->family before casting * away the const qualifer. */ assert (font_face->owns_family); free ((char*) font_face->family); if (font_face->impl_face) cairo_font_face_destroy (font_face->impl_face); } static int _cairo_toy_font_face_keys_equal (const void *key_a, const void *key_b) { const cairo_toy_font_face_t *face_a = key_a; const cairo_toy_font_face_t *face_b = key_b; return (strcmp (face_a->family, face_b->family) == 0 && face_a->slant == face_b->slant && face_a->weight == face_b->weight); } /** * cairo_toy_font_face_create: * @family: a font family name, encoded in UTF-8 * @slant: the slant for the font * @weight: the weight for the font * * Creates a font face from a triplet of family, slant, and weight. * These font faces are used in implementation of the the #cairo_t "toy" * font API. * * If @family is the zero-length string "", the platform-specific default * family is assumed. The default family then can be queried using * cairo_toy_font_face_get_family(). * * The cairo_select_font_face() function uses this to create font faces. * See that function for limitations and other details of toy font faces. * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.8 **/ cairo_font_face_t * cairo_toy_font_face_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { cairo_status_t status; cairo_toy_font_face_t key, *font_face; cairo_hash_table_t *hash_table; if (family == NULL) return (cairo_font_face_t*) &_cairo_font_face_null_pointer; /* Make sure we've got valid UTF-8 for the family */ status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); if (unlikely (status)) { if (status == CAIRO_STATUS_INVALID_STRING) return (cairo_font_face_t*) &_cairo_font_face_invalid_string; return (cairo_font_face_t*) &_cairo_font_face_nil; } switch (slant) { case CAIRO_FONT_SLANT_NORMAL: case CAIRO_FONT_SLANT_ITALIC: case CAIRO_FONT_SLANT_OBLIQUE: break; default: return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; } switch (weight) { case CAIRO_FONT_WEIGHT_NORMAL: case CAIRO_FONT_WEIGHT_BOLD: break; default: return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; } if (*family == '\0') family = CAIRO_FONT_FAMILY_DEFAULT; hash_table = _cairo_toy_font_face_hash_table_lock (); if (unlikely (hash_table == NULL)) goto UNWIND; _cairo_toy_font_face_init_key (&key, family, slant, weight); /* Return existing font_face if it exists in the hash table. */ font_face = _cairo_hash_table_lookup (hash_table, &key.base.hash_entry); if (font_face != NULL) { if (font_face->base.status == CAIRO_STATUS_SUCCESS) { cairo_font_face_reference (&font_face->base); _cairo_toy_font_face_hash_table_unlock (); return &font_face->base; } /* remove the bad font from the hash table */ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); } /* Otherwise create it and insert into hash table. */ font_face = malloc (sizeof (cairo_toy_font_face_t)); if (unlikely (font_face == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_HASH_TABLE_LOCK; } status = _cairo_toy_font_face_init (font_face, family, slant, weight); if (unlikely (status)) goto UNWIND_FONT_FACE_MALLOC; assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); if (unlikely (status)) goto UNWIND_FONT_FACE_INIT; _cairo_toy_font_face_hash_table_unlock (); return &font_face->base; UNWIND_FONT_FACE_INIT: _cairo_toy_font_face_fini (font_face); UNWIND_FONT_FACE_MALLOC: free (font_face); UNWIND_HASH_TABLE_LOCK: _cairo_toy_font_face_hash_table_unlock (); UNWIND: return (cairo_font_face_t*) &_cairo_font_face_nil; } slim_hidden_def (cairo_toy_font_face_create); static void _cairo_toy_font_face_destroy (void *abstract_face) { cairo_toy_font_face_t *font_face = abstract_face; cairo_hash_table_t *hash_table; hash_table = _cairo_toy_font_face_hash_table_lock (); /* All created objects must have been mapped in the hash table. */ assert (hash_table != NULL); if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { /* somebody recreated the font whilst we waited for the lock */ _cairo_toy_font_face_hash_table_unlock (); return; } /* Font faces in SUCCESS status are guaranteed to be in the * hashtable. Font faces in an error status are removed from the * hashtable if they are found during a lookup, thus they should * only be removed if they are in the hashtable. */ if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) || _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face) _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); _cairo_toy_font_face_hash_table_unlock (); _cairo_toy_font_face_fini (font_face); } static cairo_status_t _cairo_toy_font_face_scaled_font_create (void *abstract_font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font) { cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face; ASSERT_NOT_REACHED; return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH); } static cairo_font_face_t * _cairo_toy_font_face_get_implementation (void *abstract_font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { cairo_toy_font_face_t *font_face = abstract_font_face; if (font_face->impl_face) { cairo_font_face_t *impl = font_face->impl_face; if (impl->backend->get_implementation != NULL) { return impl->backend->get_implementation (impl, font_matrix, ctm, options); } return cairo_font_face_reference (impl); } return abstract_font_face; } static cairo_bool_t _cairo_font_face_is_toy (cairo_font_face_t *font_face) { return font_face->backend == &_cairo_toy_font_face_backend; } /** * cairo_toy_font_face_get_family: * @font_face: A toy font face * * Gets the familly name of a toy font. * * Return value: The family name. This string is owned by the font face * and remains valid as long as the font face is alive (referenced). * * Since: 1.8 **/ const char * cairo_toy_font_face_get_family (cairo_font_face_t *font_face) { cairo_toy_font_face_t *toy_font_face; if (font_face->status) return CAIRO_FONT_FAMILY_DEFAULT; toy_font_face = (cairo_toy_font_face_t *) font_face; if (! _cairo_font_face_is_toy (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return CAIRO_FONT_FAMILY_DEFAULT; } assert (toy_font_face->owns_family); return toy_font_face->family; } /** * cairo_toy_font_face_get_slant: * @font_face: A toy font face * * Gets the slant a toy font. * * Return value: The slant value * * Since: 1.8 **/ cairo_font_slant_t cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) { cairo_toy_font_face_t *toy_font_face; if (font_face->status) return CAIRO_FONT_SLANT_DEFAULT; toy_font_face = (cairo_toy_font_face_t *) font_face; if (! _cairo_font_face_is_toy (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return CAIRO_FONT_SLANT_DEFAULT; } return toy_font_face->slant; } slim_hidden_def (cairo_toy_font_face_get_slant); /** * cairo_toy_font_face_get_weight: * @font_face: A toy font face * * Gets the weight a toy font. * * Return value: The weight value * * Since: 1.8 **/ cairo_font_weight_t cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) { cairo_toy_font_face_t *toy_font_face; if (font_face->status) return CAIRO_FONT_WEIGHT_DEFAULT; toy_font_face = (cairo_toy_font_face_t *) font_face; if (! _cairo_font_face_is_toy (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return CAIRO_FONT_WEIGHT_DEFAULT; } return toy_font_face->weight; } slim_hidden_def (cairo_toy_font_face_get_weight); static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { CAIRO_FONT_TYPE_TOY, NULL, /* create_for_toy */ _cairo_toy_font_face_destroy, _cairo_toy_font_face_scaled_font_create, _cairo_toy_font_face_get_implementation }; void _cairo_toy_font_face_reset_static_data (void) { cairo_hash_table_t *hash_table; /* We manually acquire the lock rather than calling * cairo_toy_font_face_hash_table_lock simply to avoid * creating the table only to destroy it again. */ CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); hash_table = cairo_toy_font_face_hash_table; cairo_toy_font_face_hash_table = NULL; CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); if (hash_table != NULL) _cairo_hash_table_destroy (hash_table); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-traps-compositor.c000066400000000000000000002024251271037650300270710ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Joonas Pihlaja * Chris Wilson */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-inline.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-observer-private.h" #include "cairo-region-private.h" #include "cairo-spans-private.h" #include "cairo-traps-private.h" #include "cairo-tristrip-private.h" typedef cairo_int_status_t (*draw_func_t) (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip); static void do_unaligned_row(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int y, int h, uint16_t coverage) { int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; if (x2 > x1) { if (! _cairo_fixed_is_integer (b->p1.x)) { blt(closure, x1, y, 1, h, coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); x1++; } if (x2 > x1) blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); if (! _cairo_fixed_is_integer (b->p2.x)) blt(closure, x2, y, 1, h, coverage * _cairo_fixed_fractional_part (b->p2.x)); } else blt(closure, x1, y, 1, h, coverage * (b->p2.x - b->p1.x)); } static void do_unaligned_box(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int ty) { int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; if (y2 > y1) { if (! _cairo_fixed_is_integer (b->p1.y)) { do_unaligned_row(blt, closure, b, tx, y1, 1, 256 - _cairo_fixed_fractional_part (b->p1.y)); y1++; } if (y2 > y1) do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); if (! _cairo_fixed_is_integer (b->p2.y)) do_unaligned_row(blt, closure, b, tx, y2, 1, _cairo_fixed_fractional_part (b->p2.y)); } else do_unaligned_row(blt, closure, b, tx, y1, 1, b->p2.y - b->p1.y); } struct blt_in { const cairo_traps_compositor_t *compositor; cairo_surface_t *dst; cairo_boxes_t boxes; }; static void blt_in(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct blt_in *info = closure; cairo_color_t color; if (CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) return; _cairo_box_from_integers (&info->boxes.chunks.base[0], x, y, w, h); _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff); info->compositor->fill_boxes (info->dst, CAIRO_OPERATOR_IN, &color, &info->boxes); } static void add_rect_with_offset (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2, int dx, int dy) { cairo_box_t box; cairo_int_status_t status; box.p1.x = _cairo_fixed_from_int (x1 - dx); box.p1.y = _cairo_fixed_from_int (y1 - dy); box.p2.x = _cairo_fixed_from_int (x2 - dx); box.p2.y = _cairo_fixed_from_int (y2 - dy); status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); } static cairo_int_status_t combine_clip_as_traps (const cairo_traps_compositor_t *compositor, cairo_surface_t *mask, const cairo_clip_t *clip, const cairo_rectangle_int_t *extents) { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_traps_t traps; cairo_surface_t *src; cairo_box_t box; cairo_rectangle_int_t fixup; int src_x, src_y; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); if (status) return status; _cairo_traps_init (&traps); status = _cairo_bentley_ottmann_tessellate_polygon (&traps, &polygon, fill_rule); _cairo_polygon_fini (&polygon); if (unlikely (status)) return status; src = compositor->pattern_to_surface (mask, NULL, FALSE, extents, NULL, &src_x, &src_y); if (unlikely (src->status)) { _cairo_traps_fini (&traps); return src->status; } status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, src, src_x, src_y, extents->x, extents->y, extents, antialias, &traps); _cairo_traps_extents (&traps, &box); _cairo_box_round_to_rectangle (&box, &fixup); _cairo_traps_fini (&traps); cairo_surface_destroy (src); if (unlikely (status)) return status; if (! _cairo_rectangle_intersect (&fixup, extents)) return CAIRO_STATUS_SUCCESS; if (fixup.width < extents->width || fixup.height < extents->height) { cairo_boxes_t clear; _cairo_boxes_init (&clear); /* top */ if (fixup.y != extents->y) { add_rect_with_offset (&clear, extents->x, extents->y, extents->x + extents->width, fixup.y, extents->x, extents->y); } /* left */ if (fixup.x != extents->x) { add_rect_with_offset (&clear, extents->x, fixup.y, fixup.x, fixup.y + fixup.height, extents->x, extents->y); } /* right */ if (fixup.x + fixup.width != extents->x + extents->width) { add_rect_with_offset (&clear, fixup.x + fixup.width, fixup.y, extents->x + extents->width, fixup.y + fixup.height, extents->x, extents->y); } /* bottom */ if (fixup.y + fixup.height != extents->y + extents->height) { add_rect_with_offset (&clear, extents->x, fixup.y + fixup.height, extents->x + extents->width, extents->y + extents->height, extents->x, extents->y); } status = compositor->fill_boxes (mask, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); _cairo_boxes_fini (&clear); } return status; } static cairo_status_t __clip_to_surface (const cairo_traps_compositor_t *compositor, const cairo_composite_rectangles_t *composite, const cairo_rectangle_int_t *extents, cairo_surface_t **surface) { cairo_surface_t *mask; cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_traps_t traps; cairo_boxes_t clear; cairo_surface_t *src; int src_x, src_y; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_clip_get_polygon (composite->clip, &polygon, &fill_rule, &antialias); if (status) return status; _cairo_traps_init (&traps); status = _cairo_bentley_ottmann_tessellate_polygon (&traps, &polygon, fill_rule); _cairo_polygon_fini (&polygon); if (unlikely (status)) return status; mask = _cairo_surface_create_similar_scratch (composite->surface, CAIRO_CONTENT_ALPHA, extents->width, extents->height); if (unlikely (mask->status)) { _cairo_traps_fini (&traps); return status; } src = compositor->pattern_to_surface (mask, NULL, FALSE, extents, NULL, &src_x, &src_y); if (unlikely (status = src->status)) goto error; status = compositor->acquire (mask); if (unlikely (status)) goto error; _cairo_boxes_init_from_rectangle (&clear, 0, 0, extents->width, extents->height); status = compositor->fill_boxes (mask, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); if (unlikely (status)) goto error_release; status = compositor->composite_traps (mask, CAIRO_OPERATOR_ADD, src, src_x, src_y, extents->x, extents->y, extents, antialias, &traps); if (unlikely (status)) goto error_release; compositor->release (mask); *surface = mask; out: cairo_surface_destroy (src); _cairo_traps_fini (&traps); return status; error_release: compositor->release (mask); error: cairo_surface_destroy (mask); goto out; } static cairo_surface_t * traps_get_clip_surface (const cairo_traps_compositor_t *compositor, const cairo_composite_rectangles_t *composite, const cairo_rectangle_int_t *extents) { cairo_surface_t *surface = NULL; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = __clip_to_surface (compositor, composite, extents, &surface); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { surface = _cairo_surface_create_similar_solid (composite->surface, CAIRO_CONTENT_ALPHA, extents->width, extents->height, CAIRO_COLOR_WHITE); if (unlikely (surface->status)) return surface; status = _cairo_clip_combine_with_surface (composite->clip, surface, extents->x, extents->y); } if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } return surface; } static void blt_unaligned_boxes(const cairo_traps_compositor_t *compositor, cairo_surface_t *surface, int dx, int dy, cairo_box_t *boxes, int num_boxes) { struct blt_in info; int i; info.compositor = compositor; info.dst = surface; _cairo_boxes_init (&info.boxes); info.boxes.num_boxes = 1; for (i = 0; i < num_boxes; i++) { cairo_box_t *b = &boxes[i]; if (! _cairo_fixed_is_integer (b->p1.x) || ! _cairo_fixed_is_integer (b->p1.y) || ! _cairo_fixed_is_integer (b->p2.x) || ! _cairo_fixed_is_integer (b->p2.y)) { do_unaligned_box(blt_in, &info, b, dx, dy); } } } static cairo_surface_t * create_composite_mask (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *draw_closure, draw_func_t draw_func, draw_func_t mask_func, const cairo_composite_rectangles_t *extents) { cairo_surface_t *surface, *src; cairo_int_status_t status; int src_x, src_y; TRACE ((stderr, "%s\n", __FUNCTION__)); surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA, extents->bounded.width, extents->bounded.height); if (unlikely (surface->status)) return surface; src = compositor->pattern_to_surface (surface, &_cairo_pattern_white.base, FALSE, &extents->bounded, &extents->bounded, &src_x, &src_y); if (unlikely (src->status)) { cairo_surface_destroy (surface); return src; } status = compositor->acquire (surface); if (unlikely (status)) { cairo_surface_destroy (src); cairo_surface_destroy (surface); return _cairo_surface_create_in_error (status); } if (!surface->is_clear) { cairo_boxes_t clear; _cairo_boxes_init_from_rectangle (&clear, 0, 0, extents->bounded.width, extents->bounded.height); status = compositor->fill_boxes (surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); if (unlikely (status)) goto error; surface->is_clear = TRUE; } if (mask_func) { status = mask_func (compositor, surface, draw_closure, CAIRO_OPERATOR_SOURCE, src, src_x, src_y, extents->bounded.x, extents->bounded.y, &extents->bounded, extents->clip); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { surface->is_clear = FALSE; goto out; } if (unlikely (status != CAIRO_INT_STATUS_UNSUPPORTED)) goto error; } /* Is it worth setting the clip region here? */ status = draw_func (compositor, surface, draw_closure, CAIRO_OPERATOR_ADD, src, src_x, src_y, extents->bounded.x, extents->bounded.y, &extents->bounded, NULL); if (unlikely (status)) goto error; surface->is_clear = FALSE; if (extents->clip->path != NULL) { status = combine_clip_as_traps (compositor, surface, extents->clip, &extents->bounded); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_clip_combine_with_surface (extents->clip, surface, extents->bounded.x, extents->bounded.y); } if (unlikely (status)) goto error; } else if (extents->clip->boxes) { blt_unaligned_boxes(compositor, surface, extents->bounded.x, extents->bounded.y, extents->clip->boxes, extents->clip->num_boxes); } out: compositor->release (surface); cairo_surface_destroy (src); return surface; error: compositor->release (surface); if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } cairo_surface_destroy (src); return surface; } /* Handles compositing with a clip surface when the operator allows * us to combine the clip with the mask */ static cairo_status_t clip_and_composite_with_mask (const cairo_traps_compositor_t *compositor, const cairo_composite_rectangles_t*extents, draw_func_t draw_func, draw_func_t mask_func, void *draw_closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y) { cairo_surface_t *dst = extents->surface; cairo_surface_t *mask; TRACE ((stderr, "%s\n", __FUNCTION__)); mask = create_composite_mask (compositor, dst, draw_closure, draw_func, mask_func, extents); if (unlikely (mask->status)) return mask->status; if (mask->is_clear) goto skip; if (src != NULL || dst->content != CAIRO_CONTENT_ALPHA) { compositor->composite (dst, op, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } else { compositor->composite (dst, op, mask, NULL, 0, 0, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } skip: cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } /* Handles compositing with a clip surface when we have to do the operation * in two pieces and combine them together. */ static cairo_status_t clip_and_composite_combine (const cairo_traps_compositor_t *compositor, const cairo_composite_rectangles_t*extents, draw_func_t draw_func, void *draw_closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y) { cairo_surface_t *dst = extents->surface; cairo_surface_t *tmp, *clip; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); tmp = _cairo_surface_create_similar_scratch (dst, dst->content, extents->bounded.width, extents->bounded.height); if (unlikely (tmp->status)) return tmp->status; status = compositor->acquire (tmp); if (unlikely (status)) { cairo_surface_destroy (tmp); return status; } compositor->composite (tmp, dst->is_clear ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, dst, NULL, extents->bounded.x, extents->bounded.y, 0, 0, 0, 0, extents->bounded.width, extents->bounded.height); status = draw_func (compositor, tmp, draw_closure, op, src, src_x, src_y, extents->bounded.x, extents->bounded.y, &extents->bounded, NULL); if (unlikely (status)) goto cleanup; clip = traps_get_clip_surface (compositor, extents, &extents->bounded); if (unlikely ((status = clip->status))) goto cleanup; if (dst->is_clear) { compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip, 0, 0, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } else { compositor->lerp (dst, tmp, clip, 0, 0, 0,0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } cairo_surface_destroy (clip); cleanup: compositor->release (tmp); cairo_surface_destroy (tmp); return status; } /* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) */ static cairo_status_t clip_and_composite_source (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, draw_func_t draw_func, draw_func_t mask_func, void *draw_closure, cairo_surface_t *src, int src_x, int src_y, const cairo_composite_rectangles_t *extents) { cairo_surface_t *mask; TRACE ((stderr, "%s\n", __FUNCTION__)); /* Create a surface that is mask IN clip */ mask = create_composite_mask (compositor, dst, draw_closure, draw_func, mask_func, extents); if (unlikely (mask->status)) return mask->status; if (mask->is_clear) goto skip; if (dst->is_clear) { compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } else { compositor->lerp (dst, src, mask, extents->bounded.x + src_x, extents->bounded.y + src_y, 0, 0, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } skip: cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t can_reduce_alpha_op (cairo_operator_t op) { int iop = op; switch (iop) { case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_ADD: return TRUE; default: return FALSE; } } static cairo_bool_t reduce_alpha_op (cairo_composite_rectangles_t *extents) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; const cairo_pattern_t *pattern = &extents->source_pattern.base; return dst->is_clear && dst->content == CAIRO_CONTENT_ALPHA && _cairo_pattern_is_opaque_solid (pattern) && can_reduce_alpha_op (op); } static cairo_status_t fixup_unbounded_with_mask (const cairo_traps_compositor_t *compositor, const cairo_composite_rectangles_t *extents) { cairo_surface_t *dst = extents->surface; cairo_surface_t *mask; TRACE ((stderr, "%s\n", __FUNCTION__)); /* XXX can we avoid querying the clip surface again? */ mask = traps_get_clip_surface (compositor, extents, &extents->unbounded); if (unlikely (mask->status)) return mask->status; /* top */ if (extents->bounded.y != extents->unbounded.y) { int x = extents->unbounded.x; int y = extents->unbounded.y; int width = extents->unbounded.width; int height = extents->bounded.y - y; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, 0, 0, 0, 0, x, y, width, height); } /* left */ if (extents->bounded.x != extents->unbounded.x) { int x = extents->unbounded.x; int y = extents->bounded.y; int width = extents->bounded.x - x; int height = extents->bounded.height; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, 0, y - extents->unbounded.y, 0, 0, x, y, width, height); } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { int x = extents->bounded.x + extents->bounded.width; int y = extents->bounded.y; int width = extents->unbounded.x + extents->unbounded.width - x; int height = extents->bounded.height; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, x - extents->unbounded.x, y - extents->unbounded.y, 0, 0, x, y, width, height); } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { int x = extents->unbounded.x; int y = extents->bounded.y + extents->bounded.height; int width = extents->unbounded.width; int height = extents->unbounded.y + extents->unbounded.height - y; compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, 0, y - extents->unbounded.y, 0, 0, x, y, width, height); } cairo_surface_destroy (mask); return CAIRO_STATUS_SUCCESS; } static void add_rect (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2) { cairo_box_t box; cairo_int_status_t status; box.p1.x = _cairo_fixed_from_int (x1); box.p1.y = _cairo_fixed_from_int (y1); box.p2.x = _cairo_fixed_from_int (x2); box.p2.y = _cairo_fixed_from_int (y2); status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); } static cairo_status_t fixup_unbounded (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_boxes_t clear, tmp; cairo_box_t box; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } assert (extents->clip->path == NULL); /* subtract the drawn boxes from the unbounded area */ _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (boxes == NULL) { if (extents->bounded.width == 0 || extents->bounded.height == 0) { goto empty; } else { /* top */ if (extents->bounded.y != extents->unbounded.y) { add_rect (&clear, extents->unbounded.x, extents->unbounded.y, extents->unbounded.x + extents->unbounded.width, extents->bounded.y); } /* left */ if (extents->bounded.x != extents->unbounded.x) { add_rect (&clear, extents->unbounded.x, extents->bounded.y, extents->bounded.x, extents->bounded.y + extents->bounded.height); } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { add_rect (&clear, extents->bounded.x + extents->bounded.width, extents->bounded.y, extents->unbounded.x + extents->unbounded.width, extents->bounded.y + extents->bounded.height); } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { add_rect (&clear, extents->unbounded.x, extents->bounded.y + extents->bounded.height, extents->unbounded.x + extents->unbounded.width, extents->unbounded.y + extents->unbounded.height); } } } else if (boxes->num_boxes) { _cairo_boxes_init (&tmp); assert (boxes->is_pixel_aligned); status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; if (unlikely (status)) goto error; } else { empty: box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); } /* Now intersect with the clip boxes */ if (extents->clip->num_boxes) { _cairo_boxes_init_for_array (&tmp, extents->clip->boxes, extents->clip->num_boxes); status = _cairo_boxes_intersect (&clear, &tmp, &clear); if (unlikely (status)) goto error; } status = compositor->fill_boxes (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); error: _cairo_boxes_fini (&clear); return status; } enum { NEED_CLIP_REGION = 0x1, NEED_CLIP_SURFACE = 0x2, FORCE_CLIP_REGION = 0x4, }; static cairo_bool_t need_bounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = 0; if (extents->clip->num_boxes > 1 || extents->mask.width > extents->unbounded.width || extents->mask.height > extents->unbounded.height) { flags |= NEED_CLIP_REGION; } if (extents->clip->num_boxes > 1 || extents->mask.width > extents->bounded.width || extents->mask.height > extents->bounded.height) { flags |= FORCE_CLIP_REGION; } if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_bool_t need_unbounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = 0; if (! extents->is_bounded) { flags |= NEED_CLIP_REGION; if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; } if (extents->clip->path != NULL) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_status_t clip_and_composite (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, draw_func_t draw_func, draw_func_t mask_func, void *draw_closure, unsigned int need_clip) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; cairo_pattern_t *source = &extents->source_pattern.base; cairo_surface_t *src; int src_x, src_y; cairo_region_t *clip_region = NULL; cairo_status_t status = CAIRO_STATUS_SUCCESS; TRACE ((stderr, "%s\n", __FUNCTION__)); if (reduce_alpha_op (extents)) { op = CAIRO_OPERATOR_ADD; source = NULL; } if (op == CAIRO_OPERATOR_CLEAR) { op = CAIRO_OPERATOR_DEST_OUT; source = NULL; } compositor->acquire (dst); if (need_clip & NEED_CLIP_REGION) { const cairo_rectangle_int_t *limit; if ((need_clip & FORCE_CLIP_REGION) == 0) limit = &extents->unbounded; else limit = &extents->destination; clip_region = _cairo_clip_get_region (extents->clip); if (clip_region != NULL && cairo_region_contains_rectangle (clip_region, limit) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; if (clip_region != NULL) { status = compositor->set_clip_region (dst, clip_region); if (unlikely (status)) { compositor->release (dst); return status; } } } if (extents->bounded.width == 0 || extents->bounded.height == 0) goto skip; src = compositor->pattern_to_surface (dst, source, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); if (unlikely (status = src->status)) goto error; if (op == CAIRO_OPERATOR_SOURCE) { status = clip_and_composite_source (compositor, dst, draw_func, mask_func, draw_closure, src, src_x, src_y, extents); } else { if (need_clip & NEED_CLIP_SURFACE) { if (extents->is_bounded) { status = clip_and_composite_with_mask (compositor, extents, draw_func, mask_func, draw_closure, op, src, src_x, src_y); } else { status = clip_and_composite_combine (compositor, extents, draw_func, draw_closure, op, src, src_x, src_y); } } else { status = draw_func (compositor, dst, draw_closure, op, src, src_x, src_y, 0, 0, &extents->bounded, extents->clip); } } cairo_surface_destroy (src); skip: if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { if (need_clip & NEED_CLIP_SURFACE) status = fixup_unbounded_with_mask (compositor, extents); else status = fixup_unbounded (compositor, extents, NULL); } error: if (clip_region) compositor->set_clip_region (dst, NULL); compositor->release (dst); return status; } /* meta-ops */ typedef struct { cairo_traps_t traps; cairo_antialias_t antialias; } composite_traps_info_t; static cairo_int_status_t composite_traps (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { composite_traps_info_t *info = closure; TRACE ((stderr, "%s\n", __FUNCTION__)); return compositor->composite_traps (dst, op, src, src_x - dst_x, src_y - dst_y, dst_x, dst_y, extents, info->antialias, &info->traps); } typedef struct { cairo_tristrip_t strip; cairo_antialias_t antialias; } composite_tristrip_info_t; static cairo_int_status_t composite_tristrip (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { composite_tristrip_info_t *info = closure; TRACE ((stderr, "%s\n", __FUNCTION__)); return compositor->composite_tristrip (dst, op, src, src_x - dst_x, src_y - dst_y, dst_x, dst_y, extents, info->antialias, &info->strip); } static cairo_bool_t is_recording_pattern (const cairo_pattern_t *pattern) { cairo_surface_t *surface; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return FALSE; surface = ((const cairo_surface_pattern_t *) pattern)->surface; surface = _cairo_surface_get_source (surface, NULL); return _cairo_surface_is_recording (surface); } static cairo_surface_t * recording_pattern_get_surface (const cairo_pattern_t *pattern) { cairo_surface_t *surface; surface = ((const cairo_surface_pattern_t *) pattern)->surface; return _cairo_surface_get_source (surface, NULL); } static cairo_bool_t recording_pattern_contains_sample (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *sample) { cairo_recording_surface_t *surface; if (! is_recording_pattern (pattern)) return FALSE; if (pattern->extend == CAIRO_EXTEND_NONE) return TRUE; surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern); if (surface->unbounded) return TRUE; return _cairo_rectangle_contains_rectangle (&surface->extents, sample); } static cairo_bool_t op_reduces_to_source (cairo_composite_rectangles_t *extents) { if (extents->op == CAIRO_OPERATOR_SOURCE) return TRUE; if (extents->surface->is_clear) return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD; return FALSE; } static cairo_status_t composite_aligned_boxes (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; cairo_operator_t op = extents->op; cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (extents->clip); cairo_bool_t op_is_source; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (need_clip_mask && (! extents->is_bounded || extents->op == CAIRO_OPERATOR_SOURCE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } op_is_source = op_reduces_to_source (extents); /* Are we just copying a recording surface? */ if (! need_clip_mask && op_is_source && recording_pattern_contains_sample (&extents->source_pattern.base, &extents->source_sample_area)) { cairo_clip_t *recording_clip; cairo_pattern_t *source = &extents->source_pattern.base; /* XXX could also do tiling repeat modes... */ /* first clear the area about to be overwritten */ if (! dst->is_clear) { status = compositor->acquire (dst); if (unlikely (status)) return status; status = compositor->fill_boxes (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, boxes); compositor->release (dst); if (unlikely (status)) return status; } recording_clip = _cairo_clip_from_boxes (boxes); status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source), &source->matrix, dst, recording_clip); _cairo_clip_destroy (recording_clip); return status; } status = compositor->acquire (dst); if (unlikely (status)) return status; if (! need_clip_mask && (op == CAIRO_OPERATOR_CLEAR || extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID)) { const cairo_color_t *color; if (op == CAIRO_OPERATOR_CLEAR) { color = CAIRO_COLOR_TRANSPARENT; } else { color = &((cairo_solid_pattern_t *) &extents->source_pattern)->color; if (op_is_source) op = CAIRO_OPERATOR_SOURCE; } status = compositor->fill_boxes (dst, op, color, boxes); } else { cairo_surface_t *src, *mask = NULL; cairo_pattern_t *source = &extents->source_pattern.base; int src_x, src_y; int mask_x = 0, mask_y = 0; if (need_clip_mask) { mask = traps_get_clip_surface (compositor, extents, &extents->bounded); if (unlikely (mask->status)) return mask->status; mask_x = -extents->bounded.x; mask_y = -extents->bounded.y; if (op == CAIRO_OPERATOR_CLEAR) { source = NULL; op = CAIRO_OPERATOR_DEST_OUT; } } else if (op_is_source) op = CAIRO_OPERATOR_SOURCE; src = compositor->pattern_to_surface (dst, source, FALSE, &extents->bounded, &extents->source_sample_area, &src_x, &src_y); if (likely (src->status == CAIRO_STATUS_SUCCESS)) { status = compositor->composite_boxes (dst, op, src, mask, src_x, src_y, mask_x, mask_y, 0, 0, boxes, &extents->bounded); cairo_surface_destroy (src); } else status = src->status; cairo_surface_destroy (mask); } if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) status = fixup_unbounded (compositor, extents, boxes); compositor->release (dst); return status; } static cairo_status_t upload_boxes (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_surface_t *dst = extents->surface; const cairo_pattern_t *source = &extents->source_pattern.base; cairo_surface_t *src; cairo_rectangle_int_t limit; cairo_int_status_t status; int tx, ty; TRACE ((stderr, "%s\n", __FUNCTION__)); src = _cairo_pattern_get_source((cairo_surface_pattern_t *)source, &limit); if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Check that the data is entirely within the image */ if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) return CAIRO_INT_STATUS_UNSUPPORTED; if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) return CAIRO_INT_STATUS_UNSUPPORTED; tx += limit.x; ty += limit.y; if (src->type == CAIRO_SURFACE_TYPE_IMAGE) status = compositor->draw_image_boxes (dst, (cairo_image_surface_t *)src, boxes, tx, ty); else status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, tx, ty); return status; } static cairo_int_status_t trim_extents_to_traps (cairo_composite_rectangles_t *extents, cairo_traps_t *traps) { cairo_box_t box; _cairo_traps_extents (traps, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_int_status_t trim_extents_to_tristrip (cairo_composite_rectangles_t *extents, cairo_tristrip_t *strip) { cairo_box_t box; _cairo_tristrip_extents (strip, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_int_status_t trim_extents_to_boxes (cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_box_t box; _cairo_boxes_extents (boxes, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_int_status_t boxes_for_traps (cairo_boxes_t *boxes, cairo_traps_t *traps, cairo_antialias_t antialias) { int i; /* first check that the traps are rectilinear */ if (antialias == CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { const cairo_trapezoid_t *t = &traps->traps[i]; if (_cairo_fixed_integer_round_down (t->left.p1.x) != _cairo_fixed_integer_round_down (t->left.p2.x) || _cairo_fixed_integer_round_down (t->right.p1.x) != _cairo_fixed_integer_round_down (t->right.p2.x)) { return CAIRO_INT_STATUS_UNSUPPORTED; } } } else { for (i = 0; i < traps->num_traps; i++) { const cairo_trapezoid_t *t = &traps->traps[i]; if (t->left.p1.x != t->left.p2.x || t->right.p1.x != t->right.p2.x) return CAIRO_INT_STATUS_UNSUPPORTED; } } _cairo_boxes_init (boxes); boxes->num_boxes = traps->num_traps; boxes->chunks.base = (cairo_box_t *) traps->traps; boxes->chunks.count = traps->num_traps; boxes->chunks.size = traps->num_traps; if (antialias != CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; boxes->chunks.base[i].p1.x = x1; boxes->chunks.base[i].p1.y = y1; boxes->chunks.base[i].p2.x = x2; boxes->chunks.base[i].p2.y = y2; if (boxes->is_pixel_aligned) { boxes->is_pixel_aligned = _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); } } } else { boxes->is_pixel_aligned = TRUE; for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; /* round down here to match Pixman's behavior when using traps. */ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); } } return CAIRO_INT_STATUS_SUCCESS; } static cairo_status_t clip_and_composite_boxes (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes); static cairo_status_t clip_and_composite_polygon (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_polygon_t *polygon, cairo_antialias_t antialias, cairo_fill_rule_t fill_rule, cairo_bool_t curvy) { composite_traps_info_t traps; cairo_surface_t *dst = extents->surface; cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip); cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (polygon->num_edges == 0) { status = CAIRO_INT_STATUS_SUCCESS; if (! extents->is_bounded) { cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); if (clip_region && cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; if (clip_region != NULL) { status = compositor->set_clip_region (dst, clip_region); if (unlikely (status)) return status; } if (clip_surface) status = fixup_unbounded_with_mask (compositor, extents); else status = fixup_unbounded (compositor, extents, NULL); if (clip_region != NULL) compositor->set_clip_region (dst, NULL); } return status; } if (extents->clip->path != NULL && extents->is_bounded) { cairo_polygon_t clipper; cairo_fill_rule_t clipper_fill_rule; cairo_antialias_t clipper_antialias; status = _cairo_clip_get_polygon (extents->clip, &clipper, &clipper_fill_rule, &clipper_antialias); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { if (clipper_antialias == antialias) { status = _cairo_polygon_intersect (polygon, fill_rule, &clipper, clipper_fill_rule); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip); _cairo_clip_destroy (extents->clip); extents->clip = clip; fill_rule = CAIRO_FILL_RULE_WINDING; } _cairo_polygon_fini (&clipper); } } } if (antialias == CAIRO_ANTIALIAS_NONE && curvy) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); status = _cairo_rasterise_polygon_to_boxes (polygon, fill_rule, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { assert (boxes.is_pixel_aligned); status = clip_and_composite_boxes (compositor, extents, &boxes); } _cairo_boxes_fini (&boxes); if ((status != CAIRO_INT_STATUS_UNSUPPORTED)) return status; } _cairo_traps_init (&traps.traps); if (antialias == CAIRO_ANTIALIAS_NONE && curvy) { status = _cairo_rasterise_polygon_to_traps (polygon, fill_rule, antialias, &traps.traps); } else { status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule); } if (unlikely (status)) goto CLEANUP_TRAPS; status = trim_extents_to_traps (extents, &traps.traps); if (unlikely (status)) goto CLEANUP_TRAPS; /* Use a fast path if the trapezoids consist of a set of boxes. */ status = CAIRO_INT_STATUS_UNSUPPORTED; if (1) { cairo_boxes_t boxes; status = boxes_for_traps (&boxes, &traps.traps, antialias); if (status == CAIRO_INT_STATUS_SUCCESS) { status = clip_and_composite_boxes (compositor, extents, &boxes); /* XXX need to reconstruct the traps! */ assert (status != CAIRO_INT_STATUS_UNSUPPORTED); } } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* Otherwise render the trapezoids to a mask and composite in the usual * fashion. */ unsigned int flags = 0; /* For unbounded operations, the X11 server will estimate the * affected rectangle and apply the operation to that. However, * there are cases where this is an overestimate (e.g. the * clip-fill-{eo,nz}-unbounded test). * * The clip will trim that overestimate to our expectations. */ if (! extents->is_bounded) flags |= FORCE_CLIP_REGION; traps.antialias = antialias; status = clip_and_composite (compositor, extents, composite_traps, NULL, &traps, need_unbounded_clip (extents) | flags); } CLEANUP_TRAPS: _cairo_traps_fini (&traps.traps); return status; } struct composite_opacity_info { const cairo_traps_compositor_t *compositor; uint8_t op; cairo_surface_t *dst; cairo_surface_t *src; int src_x, src_y; double opacity; }; static void composite_opacity(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_opacity_info *info = closure; const cairo_traps_compositor_t *compositor = info->compositor; cairo_surface_t *mask; int mask_x, mask_y; cairo_color_t color; cairo_solid_pattern_t solid; _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage); _cairo_pattern_init_solid (&solid, &color); mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE, &_cairo_unbounded_rectangle, &_cairo_unbounded_rectangle, &mask_x, &mask_y); if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { if (info->src) { compositor->composite (info->dst, info->op, info->src, mask, x + info->src_x, y + info->src_y, mask_x, mask_y, x, y, w, h); } else { compositor->composite (info->dst, info->op, mask, NULL, mask_x, mask_y, 0, 0, x, y, w, h); } } cairo_surface_destroy (mask); } static cairo_int_status_t composite_opacity_boxes (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { const cairo_solid_pattern_t *mask = closure; struct composite_opacity_info info; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); info.compositor = compositor; info.op = op; info.dst = dst; info.src = src; info.src_x = src_x; info.src_y = src_y; info.opacity = mask->color.alpha / (double) 0xffff; /* XXX for lots of boxes create a clip region for the fully opaque areas */ for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_opacity, &info, &clip->boxes[i], dst_x, dst_y); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_boxes (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { cairo_traps_t traps; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_traps_init_boxes (&traps, closure); if (unlikely (status)) return status; status = compositor->composite_traps (dst, op, src, src_x - dst_x, src_y - dst_y, dst_x, dst_y, extents, CAIRO_ANTIALIAS_DEFAULT, &traps); _cairo_traps_fini (&traps); return status; } static cairo_status_t clip_and_composite_boxes (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (boxes->num_boxes == 0 && extents->is_bounded) return CAIRO_STATUS_SUCCESS; status = trim_extents_to_boxes (extents, boxes); if (unlikely (status)) return status; if (boxes->is_pixel_aligned && extents->clip->path == NULL && extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && (op_reduces_to_source (extents) || (extents->op == CAIRO_OPERATOR_OVER && (extents->source_pattern.surface.surface->content & CAIRO_CONTENT_ALPHA) == 0))) { status = upload_boxes (compositor, extents, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ if (extents->clip->path != NULL && extents->is_bounded) { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_clip_t *clip; clip = _cairo_clip_copy (extents->clip); clip = _cairo_clip_intersect_boxes (clip, boxes); if (_cairo_clip_is_all_clipped (clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); _cairo_clip_path_destroy (clip->path); clip->path = NULL; if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *saved_clip = extents->clip; extents->clip = clip; status = clip_and_composite_polygon (compositor, extents, &polygon, antialias, fill_rule, FALSE); clip = extents->clip; extents->clip = saved_clip; _cairo_polygon_fini (&polygon); } _cairo_clip_destroy (clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } /* Use a fast path if the boxes are pixel aligned (or nearly aligned!) */ if (boxes->is_pixel_aligned) { status = composite_aligned_boxes (compositor, extents, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } return clip_and_composite (compositor, extents, composite_boxes, NULL, boxes, need_unbounded_clip (extents)); } static cairo_int_status_t composite_traps_as_boxes (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, composite_traps_info_t *info) { cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes)) return CAIRO_INT_STATUS_UNSUPPORTED; return clip_and_composite_boxes (compositor, extents, &boxes); } static cairo_int_status_t clip_and_composite_traps (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, composite_traps_info_t *info, unsigned flags) { cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = trim_extents_to_traps (extents, &info->traps); if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) return status; status = CAIRO_INT_STATUS_UNSUPPORTED; if ((flags & FORCE_CLIP_REGION) == 0) status = composite_traps_as_boxes (compositor, extents, info); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* For unbounded operations, the X11 server will estimate the * affected rectangle and apply the operation to that. However, * there are cases where this is an overestimate (e.g. the * clip-fill-{eo,nz}-unbounded test). * * The clip will trim that overestimate to our expectations. */ if (! extents->is_bounded) flags |= FORCE_CLIP_REGION; status = clip_and_composite (compositor, extents, composite_traps, NULL, info, need_unbounded_clip (extents) | flags); } return status; } static cairo_int_status_t clip_and_composite_tristrip (const cairo_traps_compositor_t *compositor, cairo_composite_rectangles_t *extents, composite_tristrip_info_t *info) { cairo_int_status_t status; unsigned int flags = 0; TRACE ((stderr, "%s\n", __FUNCTION__)); status = trim_extents_to_tristrip (extents, &info->strip); if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) return status; if (! extents->is_bounded) flags |= FORCE_CLIP_REGION; status = clip_and_composite (compositor, extents, composite_tristrip, NULL, info, need_unbounded_clip (extents) | flags); return status; } struct composite_mask { cairo_surface_t *mask; int mask_x, mask_y; }; static cairo_int_status_t composite_mask (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { struct composite_mask *data = closure; TRACE ((stderr, "%s\n", __FUNCTION__)); if (src != NULL) { compositor->composite (dst, op, src, data->mask, extents->x + src_x, extents->y + src_y, extents->x + data->mask_x, extents->y + data->mask_y, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); } else { compositor->composite (dst, op, data->mask, NULL, extents->x + data->mask_x, extents->y + data->mask_y, 0, 0, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); } return CAIRO_STATUS_SUCCESS; } struct composite_box_info { const cairo_traps_compositor_t *compositor; cairo_surface_t *dst; cairo_surface_t *src; int src_x, src_y; uint8_t op; }; static void composite_box(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_box_info *info = closure; const cairo_traps_compositor_t *compositor = info->compositor; TRACE ((stderr, "%s\n", __FUNCTION__)); if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) { cairo_surface_t *mask; cairo_color_t color; cairo_solid_pattern_t solid; int mask_x, mask_y; _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff); _cairo_pattern_init_solid (&solid, &color); mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE, &_cairo_unbounded_rectangle, &_cairo_unbounded_rectangle, &mask_x, &mask_y); if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { compositor->composite (info->dst, info->op, info->src, mask, x + info->src_x, y + info->src_y, mask_x, mask_y, x, y, w, h); } cairo_surface_destroy (mask); } else { compositor->composite (info->dst, info->op, info->src, NULL, x + info->src_x, y + info->src_y, 0, 0, x, y, w, h); } } static cairo_int_status_t composite_mask_clip_boxes (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { struct composite_mask *data = closure; struct composite_box_info info; int i; TRACE ((stderr, "%s\n", __FUNCTION__)); info.compositor = compositor; info.op = CAIRO_OPERATOR_SOURCE; info.dst = dst; info.src = data->mask; info.src_x = data->mask_x; info.src_y = data->mask_y; info.src_x += dst_x; info.src_y += dst_y; for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_mask_clip (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { struct composite_mask *data = closure; cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; composite_traps_info_t info; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &info.antialias); if (unlikely (status)) return status; _cairo_traps_init (&info.traps); status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps, &polygon, fill_rule); _cairo_polygon_fini (&polygon); if (unlikely (status)) return status; status = composite_traps (compositor, dst, &info, CAIRO_OPERATOR_SOURCE, data->mask, data->mask_x + dst_x, data->mask_y + dst_y, dst_x, dst_y, extents, NULL); _cairo_traps_fini (&info.traps); return status; } /* high-level compositor interface */ static cairo_int_status_t _cairo_traps_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor; cairo_boxes_t boxes; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = compositor->check_composite (extents); if (unlikely (status)) return status; _cairo_clip_steal_boxes (extents->clip, &boxes); status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_clip_unsteal_boxes (extents->clip, &boxes); return status; } static cairo_int_status_t _cairo_traps_compositor_mask (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = compositor->check_composite (extents); if (unlikely (status)) return status; if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && extents->clip->path == NULL) { status = clip_and_composite (compositor, extents, composite_opacity_boxes, composite_opacity_boxes, &extents->mask_pattern, need_unbounded_clip (extents)); } else { struct composite_mask data; data.mask = compositor->pattern_to_surface (extents->surface, &extents->mask_pattern.base, TRUE, &extents->bounded, &extents->mask_sample_area, &data.mask_x, &data.mask_y); if (unlikely (data.mask->status)) return data.mask->status; status = clip_and_composite (compositor, extents, composite_mask, extents->clip->path ? composite_mask_clip : composite_mask_clip_boxes, &data, need_bounded_clip (extents)); cairo_surface_destroy (data.mask); } return status; } static cairo_int_status_t _cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = compositor->check_composite (extents); if (unlikely (status)) return status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED && 0 && _cairo_clip_is_region (extents->clip)) /* XXX */ { composite_tristrip_info_t info; info.antialias = antialias; _cairo_tristrip_init_with_clip (&info.strip, extents->clip); status = _cairo_path_fixed_stroke_to_tristrip (path, style, ctm, ctm_inverse, tolerance, &info.strip); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_tristrip (compositor, extents, &info); _cairo_tristrip_fini (&info.strip); } if (status == CAIRO_INT_STATUS_UNSUPPORTED && path->has_curve_to && antialias == CAIRO_ANTIALIAS_NONE) { cairo_polygon_t polygon; _cairo_polygon_init_with_clip (&polygon, extents->clip); status = _cairo_path_fixed_stroke_to_polygon (path, style, ctm, ctm_inverse, tolerance, &polygon); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_polygon (compositor, extents, &polygon, CAIRO_ANTIALIAS_NONE, CAIRO_FILL_RULE_WINDING, TRUE); _cairo_polygon_fini (&polygon); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_int_status_t (*func) (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps); composite_traps_info_t info; unsigned flags; if (antialias == CAIRO_ANTIALIAS_BEST || antialias == CAIRO_ANTIALIAS_GOOD) { func = _cairo_path_fixed_stroke_polygon_to_traps; flags = 0; } else { func = _cairo_path_fixed_stroke_to_traps; flags = need_bounded_clip (extents) & ~NEED_CLIP_SURFACE; } info.antialias = antialias; _cairo_traps_init_with_clip (&info.traps, extents->clip); status = func (path, style, ctm, ctm_inverse, tolerance, &info.traps); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_traps (compositor, extents, &info, flags); _cairo_traps_fini (&info.traps); } return status; } static cairo_int_status_t _cairo_traps_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = compositor->check_composite (extents); if (unlikely (status)) return status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = clip_and_composite_boxes (compositor, extents, &boxes); _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_polygon_t polygon; #if 0 if (extents->mask.width > extents->unbounded.width || extents->mask.height > extents->unbounded.height) { cairo_box_t limits; _cairo_box_from_rectangle (&limits, &extents->unbounded); _cairo_polygon_init (&polygon, &limits, 1); } else { _cairo_polygon_init (&polygon, NULL, 0); } status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, extents->clip->boxes, extents->clip->num_boxes); } #else _cairo_polygon_init_with_clip (&polygon, extents->clip); status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); #endif if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = clip_and_composite_polygon (compositor, extents, &polygon, antialias, fill_rule, path->has_curve_to); } _cairo_polygon_fini (&polygon); } return status; } static cairo_int_status_t composite_glyphs (const cairo_traps_compositor_t *compositor, cairo_surface_t *dst, void *closure, cairo_operator_t op, cairo_surface_t *src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { cairo_composite_glyphs_info_t *info = closure; TRACE ((stderr, "%s\n", __FUNCTION__)); if (op == CAIRO_OPERATOR_ADD && (dst->content & CAIRO_CONTENT_COLOR) == 0) info->use_mask = 0; return compositor->composite_glyphs (dst, op, src, src_x, src_y, dst_x, dst_y, info); } static cairo_int_status_t _cairo_traps_compositor_glyphs (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); status = compositor->check_composite (extents); if (unlikely (status)) return status; _cairo_scaled_font_freeze_cache (scaled_font); status = compositor->check_composite_glyphs (extents, scaled_font, glyphs, &num_glyphs); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_composite_glyphs_info_t info; info.font = scaled_font; info.glyphs = glyphs; info.num_glyphs = num_glyphs; info.use_mask = overlap || ! extents->is_bounded; info.extents = extents->bounded; status = clip_and_composite (compositor, extents, composite_glyphs, NULL, &info, need_bounded_clip (extents) | FORCE_CLIP_REGION); } _cairo_scaled_font_thaw_cache (scaled_font); return status; } void _cairo_traps_compositor_init (cairo_traps_compositor_t *compositor, const cairo_compositor_t *delegate) { compositor->base.delegate = delegate; compositor->base.paint = _cairo_traps_compositor_paint; compositor->base.mask = _cairo_traps_compositor_mask; compositor->base.fill = _cairo_traps_compositor_fill; compositor->base.stroke = _cairo_traps_compositor_stroke; compositor->base.glyphs = _cairo_traps_compositor_glyphs; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-traps-private.h000066400000000000000000000103121271037650300263420ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_TRAPS_PRIVATE_H #define CAIRO_TRAPS_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-types-private.h" CAIRO_BEGIN_DECLS struct _cairo_traps { cairo_status_t status; cairo_box_t bounds; const cairo_box_t *limits; int num_limits; unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ unsigned int has_intersections : 1; unsigned int is_rectilinear : 1; unsigned int is_rectangular : 1; int num_traps; int traps_size; cairo_trapezoid_t *traps; cairo_trapezoid_t traps_embedded[16]; }; /* cairo-traps.c */ cairo_private void _cairo_traps_init (cairo_traps_t *traps); cairo_private void _cairo_traps_init_with_clip (cairo_traps_t *traps, const cairo_clip_t *clip); cairo_private void _cairo_traps_limit (cairo_traps_t *traps, const cairo_box_t *boxes, int num_boxes); cairo_private cairo_status_t _cairo_traps_init_boxes (cairo_traps_t *traps, const cairo_boxes_t *boxes); cairo_private void _cairo_traps_clear (cairo_traps_t *traps); cairo_private void _cairo_traps_fini (cairo_traps_t *traps); #define _cairo_traps_status(T) (T)->status cairo_private void _cairo_traps_translate (cairo_traps_t *traps, int x, int y); cairo_private void _cairo_traps_tessellate_triangle (cairo_traps_t *traps, const cairo_point_t t[3]); cairo_private void _cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, const cairo_point_t q[4]); cairo_private cairo_status_t _cairo_traps_tessellate_rectangle (cairo_traps_t *traps, const cairo_point_t *top_left, const cairo_point_t *bottom_right); cairo_private void _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, cairo_line_t *left, cairo_line_t *right); cairo_private int _cairo_traps_contain (const cairo_traps_t *traps, double x, double y); cairo_private void _cairo_traps_extents (const cairo_traps_t *traps, cairo_box_t *extents); cairo_private cairo_int_status_t _cairo_traps_extract_region (cairo_traps_t *traps, cairo_antialias_t antialias, cairo_region_t **region); cairo_private cairo_bool_t _cairo_traps_to_boxes (cairo_traps_t *traps, cairo_antialias_t antialias, cairo_boxes_t *boxes); cairo_private cairo_status_t _cairo_traps_path (const cairo_traps_t *traps, cairo_path_fixed_t *path); cairo_private cairo_int_status_t _cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_traps_t *traps); CAIRO_END_DECLS #endif /* CAIRO_TRAPS_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-traps.c000066400000000000000000000772761271037650300247130ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* * Copyright © 2002 Keith Packard * Copyright © 2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith R. Packard * Carl D. Worth * * 2002-07-15: Converted from XRenderCompositeDoublePoly to #cairo_trap_t. Carl D. Worth */ #include "cairoint.h" #include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-region-private.h" #include "cairo-slope-private.h" #include "cairo-traps-private.h" #include "cairo-spans-private.h" /* private functions */ void _cairo_traps_init (cairo_traps_t *traps) { VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); traps->status = CAIRO_STATUS_SUCCESS; traps->maybe_region = 1; traps->is_rectilinear = 0; traps->is_rectangular = 0; traps->num_traps = 0; traps->traps_size = ARRAY_LENGTH (traps->traps_embedded); traps->traps = traps->traps_embedded; traps->num_limits = 0; traps->has_intersections = FALSE; } void _cairo_traps_limit (cairo_traps_t *traps, const cairo_box_t *limits, int num_limits) { int i; traps->limits = limits; traps->num_limits = num_limits; traps->bounds = limits[0]; for (i = 1; i < num_limits; i++) _cairo_box_add_box (&traps->bounds, &limits[i]); } void _cairo_traps_init_with_clip (cairo_traps_t *traps, const cairo_clip_t *clip) { _cairo_traps_init (traps); if (clip) _cairo_traps_limit (traps, clip->boxes, clip->num_boxes); } void _cairo_traps_clear (cairo_traps_t *traps) { traps->status = CAIRO_STATUS_SUCCESS; traps->maybe_region = 1; traps->is_rectilinear = 0; traps->is_rectangular = 0; traps->num_traps = 0; traps->has_intersections = FALSE; } void _cairo_traps_fini (cairo_traps_t *traps) { if (traps->traps != traps->traps_embedded) free (traps->traps); VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); } /* make room for at least one more trap */ static cairo_bool_t _cairo_traps_grow (cairo_traps_t *traps) { cairo_trapezoid_t *new_traps; int new_size = 4 * traps->traps_size; if (CAIRO_INJECT_FAULT ()) { traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } if (traps->traps == traps->traps_embedded) { new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); if (new_traps != NULL) memcpy (new_traps, traps->traps, sizeof (traps->traps_embedded)); } else { new_traps = _cairo_realloc_ab (traps->traps, new_size, sizeof (cairo_trapezoid_t)); } if (unlikely (new_traps == NULL)) { traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } traps->traps = new_traps; traps->traps_size = new_size; return TRUE; } void _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, cairo_line_t *left, cairo_line_t *right) { cairo_trapezoid_t *trap; if (unlikely (traps->num_traps == traps->traps_size)) { if (unlikely (! _cairo_traps_grow (traps))) return; } trap = &traps->traps[traps->num_traps++]; trap->top = top; trap->bottom = bottom; trap->left = *left; trap->right = *right; } static void _cairo_traps_add_clipped_trap (cairo_traps_t *traps, cairo_fixed_t _top, cairo_fixed_t _bottom, cairo_line_t *_left, cairo_line_t *_right) { /* Note: With the goofy trapezoid specification, (where an * arbitrary two points on the lines can specified for the left * and right edges), these limit checks would not work in * general. For example, one can imagine a trapezoid entirely * within the limits, but with two points used to specify the left * edge entirely to the right of the limits. Fortunately, for our * purposes, cairo will never generate such a crazy * trapezoid. Instead, cairo always uses for its points the * extreme positions of the edge that are visible on at least some * trapezoid. With this constraint, it's impossible for both * points to be outside the limits while the relevant edge is * entirely inside the limits. */ if (traps->num_limits) { const cairo_box_t *b = &traps->bounds; cairo_fixed_t top = _top, bottom = _bottom; cairo_line_t left = *_left, right = *_right; /* Trivially reject if trapezoid is entirely to the right or * to the left of the limits. */ if (left.p1.x >= b->p2.x && left.p2.x >= b->p2.x) return; if (right.p1.x <= b->p1.x && right.p2.x <= b->p1.x) return; /* And reject if the trapezoid is entirely above or below */ if (top >= b->p2.y || bottom <= b->p1.y) return; /* Otherwise, clip the trapezoid to the limits. We only clip * where an edge is entirely outside the limits. If we wanted * to be more clever, we could handle cases where a trapezoid * edge intersects the edge of the limits, but that would * require slicing this trapezoid into multiple trapezoids, * and I'm not sure the effort would be worth it. */ if (top < b->p1.y) top = b->p1.y; if (bottom > b->p2.y) bottom = b->p2.y; if (left.p1.x <= b->p1.x && left.p2.x <= b->p1.x) left.p1.x = left.p2.x = b->p1.x; if (right.p1.x >= b->p2.x && right.p2.x >= b->p2.x) right.p1.x = right.p2.x = b->p2.x; /* Trivial discards for empty trapezoids that are likely to * be produced by our tessellators (most notably convex_quad * when given a simple rectangle). */ if (top >= bottom) return; /* cheap colinearity check */ if (right.p1.x <= left.p1.x && right.p1.y == left.p1.y && right.p2.x <= left.p2.x && right.p2.y == left.p2.y) return; _cairo_traps_add_trap (traps, top, bottom, &left, &right); } else _cairo_traps_add_trap (traps, _top, _bottom, _left, _right); } static int _compare_point_fixed_by_y (const void *av, const void *bv) { const cairo_point_t *a = av, *b = bv; int ret = a->y - b->y; if (ret == 0) ret = a->x - b->x; return ret; } void _cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, const cairo_point_t q[4]) { int a, b, c, d; int i; cairo_slope_t ab, ad; cairo_bool_t b_left_of_d; cairo_line_t left; cairo_line_t right; /* Choose a as a point with minimal y */ a = 0; for (i = 1; i < 4; i++) if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0) a = i; /* b and d are adjacent to a, while c is opposite */ b = (a + 1) % 4; c = (a + 2) % 4; d = (a + 3) % 4; /* Choose between b and d so that b.y is less than d.y */ if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) { b = (a + 3) % 4; d = (a + 1) % 4; } /* Without freedom left to choose anything else, we have four * cases to tessellate. * * First, we have to determine the Y-axis sort of the four * vertices, (either abcd or abdc). After that we need to detemine * which edges will be "left" and which will be "right" in the * resulting trapezoids. This can be determined by computing a * slope comparison of ab and ad to determine if b is left of d or * not. * * Note that "left of" here is in the sense of which edges should * be the left vs. right edges of the trapezoid. In particular, b * left of d does *not* mean that b.x is less than d.x. * * This should hopefully be made clear in the lame ASCII art * below. Since the same slope comparison is used in all cases, we * compute it before testing for the Y-value sort. */ /* Note: If a == b then the ab slope doesn't give us any * information. In that case, we can replace it with the ac (or * equivalenly the bc) slope which gives us exactly the same * information we need. At worst the names of the identifiers ab * and b_left_of_d are inaccurate in this case, (would be ac, and * c_left_of_d). */ if (q[a].x == q[b].x && q[a].y == q[b].y) _cairo_slope_init (&ab, &q[a], &q[c]); else _cairo_slope_init (&ab, &q[a], &q[b]); _cairo_slope_init (&ad, &q[a], &q[d]); b_left_of_d = _cairo_slope_compare (&ab, &ad) > 0; if (q[c].y <= q[d].y) { if (b_left_of_d) { /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad)) * * top bot left right * _a a a * / / /| |\ a.y b.y ab ad * b / b | b \ * / / | | \ \ b.y c.y bc ad * c / c | c \ * | / \| \ \ c.y d.y cd ad * d d d */ left.p1 = q[a]; left.p2 = q[b]; right.p1 = q[a]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); left.p1 = q[b]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); left.p1 = q[c]; left.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); } else { /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad)) * * a a a_ * /| |\ \ \ a.y b.y ad ab * / b | b \ b * / / | | \ \ b.y c.y ad bc * / c | c \ c * / / |/ \ | c.y d.y ad cd * d d d */ left.p1 = q[a]; left.p2 = q[d]; right.p1 = q[a]; right.p2 = q[b]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); right.p1 = q[b]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); right.p1 = q[c]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); } } else { if (b_left_of_d) { /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad)) * * a a a * // / \ |\ a.y b.y ab ad * /b/ b \ b \ * / / \ \ \ \ b.y d.y bc ad * /d/ \ d \ d * // \ / \| d.y c.y bc dc * c c c */ left.p1 = q[a]; left.p2 = q[b]; right.p1 = q[a]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); left.p1 = q[b]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); right.p1 = q[d]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); } else { /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad)) * * a a a * /| / \ \\ a.y b.y ad ab * / b / b \b\ * / / / / \ \ b.y d.y ad bc * d / d / \d\ * |/ \ / \\ d.y c.y dc bc * c c c */ left.p1 = q[a]; left.p2 = q[d]; right.p1 = q[a]; right.p2 = q[b]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); right.p1 = q[b]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); left.p1 = q[d]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); } } } /* A triangle is simply a degenerate case of a convex * quadrilateral. We would not benefit from having any distinct * implementation of triangle vs. quadrilateral tessellation here. */ void _cairo_traps_tessellate_triangle (cairo_traps_t *traps, const cairo_point_t t[3]) { cairo_point_t quad[4]; quad[0] = t[0]; quad[1] = t[0]; quad[2] = t[1]; quad[3] = t[2]; _cairo_traps_tessellate_convex_quad (traps, quad); } /** * _cairo_traps_init_boxes: * @traps: a #cairo_traps_t * @box: an array box that will each be converted to a single trapezoid * to store in @traps. * * Initializes a #cairo_traps_t to contain an array of rectangular * trapezoids. **/ cairo_status_t _cairo_traps_init_boxes (cairo_traps_t *traps, const cairo_boxes_t *boxes) { cairo_trapezoid_t *trap; const struct _cairo_boxes_chunk *chunk; _cairo_traps_init (traps); while (traps->traps_size < boxes->num_boxes) { if (unlikely (! _cairo_traps_grow (traps))) { _cairo_traps_fini (traps); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } traps->num_traps = boxes->num_boxes; traps->is_rectilinear = TRUE; traps->is_rectangular = TRUE; traps->maybe_region = boxes->is_pixel_aligned; trap = &traps->traps[0]; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box; int i; box = chunk->base; for (i = 0; i < chunk->count; i++) { trap->top = box->p1.y; trap->bottom = box->p2.y; trap->left.p1 = box->p1; trap->left.p2.x = box->p1.x; trap->left.p2.y = box->p2.y; trap->right.p1.x = box->p2.x; trap->right.p1.y = box->p1.y; trap->right.p2 = box->p2; box++, trap++; } } return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_traps_tessellate_rectangle (cairo_traps_t *traps, const cairo_point_t *top_left, const cairo_point_t *bottom_right) { cairo_line_t left; cairo_line_t right; cairo_fixed_t top, bottom; if (top_left->y == bottom_right->y) return CAIRO_STATUS_SUCCESS; if (top_left->x == bottom_right->x) return CAIRO_STATUS_SUCCESS; left.p1.x = left.p2.x = top_left->x; left.p1.y = right.p1.y = top_left->y; right.p1.x = right.p2.x = bottom_right->x; left.p2.y = right.p2.y = bottom_right->y; top = top_left->y; bottom = bottom_right->y; if (traps->num_limits) { cairo_bool_t reversed; int n; if (top >= traps->bounds.p2.y || bottom <= traps->bounds.p1.y) return CAIRO_STATUS_SUCCESS; /* support counter-clockwise winding for rectangular tessellation */ reversed = top_left->x > bottom_right->x; if (reversed) { right.p1.x = right.p2.x = top_left->x; left.p1.x = left.p2.x = bottom_right->x; } if (left.p1.x >= traps->bounds.p2.x || right.p1.x <= traps->bounds.p1.x) return CAIRO_STATUS_SUCCESS; for (n = 0; n < traps->num_limits; n++) { const cairo_box_t *limits = &traps->limits[n]; cairo_line_t _left, _right; cairo_fixed_t _top, _bottom; if (top >= limits->p2.y) continue; if (bottom <= limits->p1.y) continue; /* Trivially reject if trapezoid is entirely to the right or * to the left of the limits. */ if (left.p1.x >= limits->p2.x) continue; if (right.p1.x <= limits->p1.x) continue; /* Otherwise, clip the trapezoid to the limits. */ _top = top; if (_top < limits->p1.y) _top = limits->p1.y; _bottom = bottom; if (_bottom > limits->p2.y) _bottom = limits->p2.y; if (_bottom <= _top) continue; _left = left; if (_left.p1.x < limits->p1.x) { _left.p1.x = limits->p1.x; _left.p1.y = limits->p1.y; _left.p2.x = limits->p1.x; _left.p2.y = limits->p2.y; } _right = right; if (_right.p1.x > limits->p2.x) { _right.p1.x = limits->p2.x; _right.p1.y = limits->p1.y; _right.p2.x = limits->p2.x; _right.p2.y = limits->p2.y; } if (left.p1.x >= right.p1.x) continue; if (reversed) _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left); else _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right); } } else { _cairo_traps_add_trap (traps, top, bottom, &left, &right); } return traps->status; } void _cairo_traps_translate (cairo_traps_t *traps, int x, int y) { cairo_fixed_t xoff, yoff; cairo_trapezoid_t *t; int i; /* Ugh. The cairo_composite/(Render) interface doesn't allow an offset for the trapezoids. Need to manually shift all the coordinates to align with the offset origin of the intermediate surface. */ xoff = _cairo_fixed_from_int (x); yoff = _cairo_fixed_from_int (y); for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { t->top += yoff; t->bottom += yoff; t->left.p1.x += xoff; t->left.p1.y += yoff; t->left.p2.x += xoff; t->left.p2.y += yoff; t->right.p1.x += xoff; t->right.p1.y += yoff; t->right.p2.x += xoff; t->right.p2.y += yoff; } } void _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, cairo_trapezoid_t *src_traps, int num_traps, double tx, double ty, double sx, double sy) { int i; cairo_fixed_t xoff = _cairo_fixed_from_double (tx); cairo_fixed_t yoff = _cairo_fixed_from_double (ty); if (sx == 1.0 && sy == 1.0) { for (i = 0; i < num_traps; i++) { offset_traps[i].top = src_traps[i].top + yoff; offset_traps[i].bottom = src_traps[i].bottom + yoff; offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff; offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff; offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff; offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff; offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff; offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff; offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff; offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff; } } else { cairo_fixed_t xsc = _cairo_fixed_from_double (sx); cairo_fixed_t ysc = _cairo_fixed_from_double (sy); for (i = 0; i < num_traps; i++) { offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc); offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc); offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc); offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc); offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc); offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc); offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc); offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc); offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc); offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc); } } } static cairo_bool_t _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) { cairo_slope_t slope_left, slope_pt, slope_right; if (t->top > pt->y) return FALSE; if (t->bottom < pt->y) return FALSE; _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); _cairo_slope_init (&slope_pt, &t->left.p1, pt); if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) return FALSE; _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); _cairo_slope_init (&slope_pt, &t->right.p1, pt); if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) return FALSE; return TRUE; } cairo_bool_t _cairo_traps_contain (const cairo_traps_t *traps, double x, double y) { int i; cairo_point_t point; point.x = _cairo_fixed_from_double (x); point.y = _cairo_fixed_from_double (y); for (i = 0; i < traps->num_traps; i++) { if (_cairo_trap_contains (&traps->traps[i], &point)) return TRUE; } return FALSE; } static cairo_fixed_t _line_compute_intersection_x_for_y (const cairo_line_t *line, cairo_fixed_t y) { return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y); } void _cairo_traps_extents (const cairo_traps_t *traps, cairo_box_t *extents) { int i; if (traps->num_traps == 0) { extents->p1.x = extents->p1.y = 0; extents->p2.x = extents->p2.y = 0; return; } extents->p1.x = extents->p1.y = INT32_MAX; extents->p2.x = extents->p2.y = INT32_MIN; for (i = 0; i < traps->num_traps; i++) { const cairo_trapezoid_t *trap = &traps->traps[i]; if (trap->top < extents->p1.y) extents->p1.y = trap->top; if (trap->bottom > extents->p2.y) extents->p2.y = trap->bottom; if (trap->left.p1.x < extents->p1.x) { cairo_fixed_t x = trap->left.p1.x; if (trap->top != trap->left.p1.y) { x = _line_compute_intersection_x_for_y (&trap->left, trap->top); if (x < extents->p1.x) extents->p1.x = x; } else extents->p1.x = x; } if (trap->left.p2.x < extents->p1.x) { cairo_fixed_t x = trap->left.p2.x; if (trap->bottom != trap->left.p2.y) { x = _line_compute_intersection_x_for_y (&trap->left, trap->bottom); if (x < extents->p1.x) extents->p1.x = x; } else extents->p1.x = x; } if (trap->right.p1.x > extents->p2.x) { cairo_fixed_t x = trap->right.p1.x; if (trap->top != trap->right.p1.y) { x = _line_compute_intersection_x_for_y (&trap->right, trap->top); if (x > extents->p2.x) extents->p2.x = x; } else extents->p2.x = x; } if (trap->right.p2.x > extents->p2.x) { cairo_fixed_t x = trap->right.p2.x; if (trap->bottom != trap->right.p2.y) { x = _line_compute_intersection_x_for_y (&trap->right, trap->bottom); if (x > extents->p2.x) extents->p2.x = x; } else extents->p2.x = x; } } } static cairo_bool_t _mono_edge_is_vertical (const cairo_line_t *line) { return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); } static cairo_bool_t _traps_are_pixel_aligned (cairo_traps_t *traps, cairo_antialias_t antialias) { int i; if (antialias == CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { if (! _mono_edge_is_vertical (&traps->traps[i].left) || ! _mono_edge_is_vertical (&traps->traps[i].right)) { traps->maybe_region = FALSE; return FALSE; } } } else { for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || ! _cairo_fixed_is_integer (traps->traps[i].top) || ! _cairo_fixed_is_integer (traps->traps[i].bottom) || ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) { traps->maybe_region = FALSE; return FALSE; } } } return TRUE; } /** * _cairo_traps_extract_region: * @traps: a #cairo_traps_t * @region: a #cairo_region_t * * Determines if a set of trapezoids are exactly representable as a * cairo region. If so, the passed-in region is initialized to * the area representing the given traps. It should be finalized * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED * is returned. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED * or %CAIRO_STATUS_NO_MEMORY **/ cairo_int_status_t _cairo_traps_extract_region (cairo_traps_t *traps, cairo_antialias_t antialias, cairo_region_t **region) { cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *rects = stack_rects; cairo_int_status_t status; int i, rect_count; /* we only treat this a hint... */ if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _traps_are_pixel_aligned (traps, antialias)) { traps->maybe_region = FALSE; return CAIRO_INT_STATUS_UNSUPPORTED; } if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } rect_count = 0; for (i = 0; i < traps->num_traps; i++) { int x1, y1, x2, y2; if (antialias == CAIRO_ANTIALIAS_NONE) { x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x); y1 = _cairo_fixed_integer_round_down (traps->traps[i].top); x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x); y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom); } else { x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); y1 = _cairo_fixed_integer_part (traps->traps[i].top); x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); } if (x2 > x1 && y2 > y1) { rects[rect_count].x = x1; rects[rect_count].y = y1; rects[rect_count].width = x2 - x1; rects[rect_count].height = y2 - y1; rect_count++; } } *region = cairo_region_create_rectangles (rects, rect_count); status = (*region)->status; if (rects != stack_rects) free (rects); return status; } cairo_bool_t _cairo_traps_to_boxes (cairo_traps_t *traps, cairo_antialias_t antialias, cairo_boxes_t *boxes) { int i; for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || traps->traps[i].right.p1.x != traps->traps[i].right.p2.x) return FALSE; } _cairo_boxes_init (boxes); boxes->num_boxes = traps->num_traps; boxes->chunks.base = (cairo_box_t *) traps->traps; boxes->chunks.count = traps->num_traps; boxes->chunks.size = traps->num_traps; if (antialias != CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; boxes->chunks.base[i].p1.x = x1; boxes->chunks.base[i].p1.y = y1; boxes->chunks.base[i].p2.x = x2; boxes->chunks.base[i].p2.y = y2; if (boxes->is_pixel_aligned) { boxes->is_pixel_aligned = _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); } } } else { boxes->is_pixel_aligned = TRUE; for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; /* round down here to match Pixman's behavior when using traps. */ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); } } return TRUE; } /* moves trap points such that they become the actual corners of the trapezoid */ static void _sanitize_trap (cairo_trapezoid_t *t) { cairo_trapezoid_t s = *t; #define FIX(lr, tb, p) \ if (t->lr.p.y != t->tb) { \ t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ t->lr.p.y = s.tb; \ } FIX (left, top, p1); FIX (left, bottom, p2); FIX (right, top, p1); FIX (right, bottom, p2); } cairo_private cairo_status_t _cairo_traps_path (const cairo_traps_t *traps, cairo_path_fixed_t *path) { int i; for (i = 0; i < traps->num_traps; i++) { cairo_status_t status; cairo_trapezoid_t trap = traps->traps[i]; if (trap.top == trap.bottom) continue; _sanitize_trap (&trap); status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom); if (unlikely (status)) return status; status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom); if (unlikely (status)) return status; status = _cairo_path_fixed_close_path (path); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } void _cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps) { cairo_box_t extents; int n; #if 0 if (traps->has_limits) { printf ("%s: limits=(%d, %d, %d, %d)\n", filename, traps->limits.p1.x, traps->limits.p1.y, traps->limits.p2.x, traps->limits.p2.y); } #endif _cairo_traps_extents (traps, &extents); fprintf (file, "extents=(%d, %d, %d, %d)\n", extents.p1.x, extents.p1.y, extents.p2.x, extents.p2.y); for (n = 0; n < traps->num_traps; n++) { fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", traps->traps[n].top, traps->traps[n].bottom, traps->traps[n].left.p1.x, traps->traps[n].left.p1.y, traps->traps[n].left.p2.x, traps->traps[n].left.p2.y, traps->traps[n].right.p1.x, traps->traps[n].right.p1.y, traps->traps[n].right.p2.x, traps->traps[n].right.p2.y); } } struct cairo_trap_renderer { cairo_span_renderer_t base; cairo_traps_t *traps; }; static cairo_status_t span_to_traps (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { struct cairo_trap_renderer *r = abstract_renderer; cairo_fixed_t top, bot; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; top = _cairo_fixed_from_int (y); bot = _cairo_fixed_from_int (y + h); do { if (spans[0].coverage) { cairo_fixed_t x0 = _cairo_fixed_from_int(spans[0].x); cairo_fixed_t x1 = _cairo_fixed_from_int(spans[1].x); cairo_line_t left = { { x0, top }, { x0, bot } }, right = { { x1, top }, { x1, bot } }; _cairo_traps_add_trap (r->traps, top, bot, &left, &right); } spans++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_traps_t *traps) { struct cairo_trap_renderer renderer; cairo_scan_converter_t *converter; cairo_int_status_t status; cairo_rectangle_int_t r; TRACE ((stderr, "%s: fill_rule=%d, antialias=%d\n", __FUNCTION__, fill_rule, antialias)); assert(antialias == CAIRO_ANTIALIAS_NONE); renderer.traps = traps; renderer.base.render_rows = span_to_traps; _cairo_box_round_to_rectangle (&polygon->extents, &r); converter = _cairo_mono_scan_converter_create (r.x, r.y, r.x + r.width, r.y + r.height, fill_rule); status = _cairo_mono_scan_converter_add_polygon (converter, polygon); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = converter->generate (converter, &renderer.base); converter->destroy (converter); return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tristrip-private.h000066400000000000000000000054371271037650300271050ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson */ #ifndef CAIRO_TRISTRIP_PRIVATE_H #define CAIRO_TRISTRIP_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-error-private.h" #include "cairo-types-private.h" CAIRO_BEGIN_DECLS struct _cairo_tristrip { cairo_status_t status; /* XXX clipping */ const cairo_box_t *limits; int num_limits; int num_points; int size_points; cairo_point_t *points; cairo_point_t points_embedded[64]; }; cairo_private void _cairo_tristrip_init (cairo_tristrip_t *strip); cairo_private void _cairo_tristrip_limit (cairo_tristrip_t *strip, const cairo_box_t *limits, int num_limits); cairo_private void _cairo_tristrip_init_with_clip (cairo_tristrip_t *strip, const cairo_clip_t *clip); cairo_private void _cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y); cairo_private void _cairo_tristrip_move_to (cairo_tristrip_t *strip, const cairo_point_t *point); cairo_private void _cairo_tristrip_add_point (cairo_tristrip_t *strip, const cairo_point_t *point); cairo_private void _cairo_tristrip_extents (const cairo_tristrip_t *strip, cairo_box_t *extents); cairo_private void _cairo_tristrip_fini (cairo_tristrip_t *strip); #define _cairo_tristrip_status(T) ((T)->status) CAIRO_END_DECLS #endif /* CAIRO_TRISTRIP_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-tristrip.c000066400000000000000000000116561271037650300254300ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-tristrip-private.h" void _cairo_tristrip_init (cairo_tristrip_t *strip) { VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t))); strip->status = CAIRO_STATUS_SUCCESS; strip->num_limits = 0; strip->num_points = 0; strip->size_points = ARRAY_LENGTH (strip->points_embedded); strip->points = strip->points_embedded; } void _cairo_tristrip_fini (cairo_tristrip_t *strip) { if (strip->points != strip->points_embedded) free (strip->points); VG (VALGRIND_MAKE_MEM_NOACCESS (strip, sizeof (cairo_tristrip_t))); } void _cairo_tristrip_limit (cairo_tristrip_t *strip, const cairo_box_t *limits, int num_limits) { strip->limits = limits; strip->num_limits = num_limits; } void _cairo_tristrip_init_with_clip (cairo_tristrip_t *strip, const cairo_clip_t *clip) { _cairo_tristrip_init (strip); if (clip) _cairo_tristrip_limit (strip, clip->boxes, clip->num_boxes); } /* make room for at least one more trap */ static cairo_bool_t _cairo_tristrip_grow (cairo_tristrip_t *strip) { cairo_point_t *points; int new_size = 4 * strip->size_points; if (CAIRO_INJECT_FAULT ()) { strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } if (strip->points == strip->points_embedded) { points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t)); if (points != NULL) memcpy (points, strip->points, sizeof (strip->points_embedded)); } else { points = _cairo_realloc_ab (strip->points, new_size, sizeof (cairo_trapezoid_t)); } if (unlikely (points == NULL)) { strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return FALSE; } strip->points = points; strip->size_points = new_size; return TRUE; } void _cairo_tristrip_add_point (cairo_tristrip_t *strip, const cairo_point_t *p) { if (unlikely (strip->num_points == strip->size_points)) { if (unlikely (! _cairo_tristrip_grow (strip))) return; } strip->points[strip->num_points++] = *p; } /* Insert degenerate triangles to advance to the given point. The * next point inserted must also be @p. */ void _cairo_tristrip_move_to (cairo_tristrip_t *strip, const cairo_point_t *p) { if (strip->num_points == 0) return; _cairo_tristrip_add_point (strip, &strip->points[strip->num_points-1]); _cairo_tristrip_add_point (strip, p); #if 0 /* and one more for luck! (to preserve cw/ccw ordering) */ _cairo_tristrip_add_point (strip, p); #endif } void _cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y) { cairo_fixed_t xoff, yoff; cairo_point_t *p; int i; xoff = _cairo_fixed_from_int (x); yoff = _cairo_fixed_from_int (y); for (i = 0, p = strip->points; i < strip->num_points; i++, p++) { p->x += xoff; p->y += yoff; } } void _cairo_tristrip_extents (const cairo_tristrip_t *strip, cairo_box_t *extents) { int i; if (strip->num_points == 0) { extents->p1.x = extents->p1.y = 0; extents->p2.x = extents->p2.y = 0; return; } extents->p2 = extents->p1 = strip->points[0]; for (i = 1; i < strip->num_points; i++) { const cairo_point_t *p = &strip->points[i]; if (p->x < extents->p1.x) extents->p1.x = p->x; else if (p->x > extents->p2.x) extents->p2.x = p->x; if (p->y < extents->p1.y) extents->p1.y = p->y; else if (p->y > extents->p2.y) extents->p2.y = p->y; } } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-truetype-subset-private.h000066400000000000000000000154231271037650300304050ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg * Adrian Johnson */ #ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H #define CAIRO_TRUETYPE_SUBSET_PRIVATE_H #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET /* The structs defined here should strictly follow the TrueType * specification and not be padded. We use only 16-bit integer * in their definition to guarantee that. The fields of type * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit * parts, and 64-bit members are broken into four. * * The test truetype-tables in the test suite makes sure that * these tables have the right size. Please update that test * if you add new tables/structs that should be packed. */ #define MAKE_TT_TAG(a, b, c, d) (a<<24 | b<<16 | c<<8 | d) #define TT_TAG_CFF MAKE_TT_TAG('C','F','F',' ') #define TT_TAG_cmap MAKE_TT_TAG('c','m','a','p') #define TT_TAG_cvt MAKE_TT_TAG('c','v','t',' ') #define TT_TAG_fpgm MAKE_TT_TAG('f','p','g','m') #define TT_TAG_glyf MAKE_TT_TAG('g','l','y','f') #define TT_TAG_head MAKE_TT_TAG('h','e','a','d') #define TT_TAG_hhea MAKE_TT_TAG('h','h','e','a') #define TT_TAG_hmtx MAKE_TT_TAG('h','m','t','x') #define TT_TAG_loca MAKE_TT_TAG('l','o','c','a') #define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p') #define TT_TAG_name MAKE_TT_TAG('n','a','m','e') #define TT_TAG_OS2 MAKE_TT_TAG('O','S','/','2') #define TT_TAG_post MAKE_TT_TAG('p','o','s','t') #define TT_TAG_prep MAKE_TT_TAG('p','r','e','p') /* All tt_* structs are big-endian */ typedef struct _tt_cmap_index { uint16_t platform; uint16_t encoding; uint32_t offset; } tt_cmap_index_t; typedef struct _tt_cmap { uint16_t version; uint16_t num_tables; tt_cmap_index_t index[1]; } tt_cmap_t; typedef struct _segment_map { uint16_t format; uint16_t length; uint16_t version; uint16_t segCountX2; uint16_t searchRange; uint16_t entrySelector; uint16_t rangeShift; uint16_t endCount[1]; } tt_segment_map_t; typedef struct _tt_head { int16_t version_1; int16_t version_2; int16_t revision_1; int16_t revision_2; uint16_t checksum_1; uint16_t checksum_2; uint16_t magic_1; uint16_t magic_2; uint16_t flags; uint16_t units_per_em; int16_t created_1; int16_t created_2; int16_t created_3; int16_t created_4; int16_t modified_1; int16_t modified_2; int16_t modified_3; int16_t modified_4; int16_t x_min; /* FWORD */ int16_t y_min; /* FWORD */ int16_t x_max; /* FWORD */ int16_t y_max; /* FWORD */ uint16_t mac_style; uint16_t lowest_rec_pppem; int16_t font_direction_hint; int16_t index_to_loc_format; int16_t glyph_data_format; } tt_head_t; typedef struct _tt_hhea { int16_t version_1; int16_t version_2; int16_t ascender; /* FWORD */ int16_t descender; /* FWORD */ int16_t line_gap; /* FWORD */ uint16_t advance_max_width; /* UFWORD */ int16_t min_left_side_bearing; /* FWORD */ int16_t min_right_side_bearing; /* FWORD */ int16_t x_max_extent; /* FWORD */ int16_t caret_slope_rise; int16_t caret_slope_run; int16_t reserved[5]; int16_t metric_data_format; uint16_t num_hmetrics; } tt_hhea_t; typedef struct _tt_maxp { int16_t version_1; int16_t version_2; uint16_t num_glyphs; uint16_t max_points; uint16_t max_contours; uint16_t max_composite_points; uint16_t max_composite_contours; uint16_t max_zones; uint16_t max_twilight_points; uint16_t max_storage; uint16_t max_function_defs; uint16_t max_instruction_defs; uint16_t max_stack_elements; uint16_t max_size_of_instructions; uint16_t max_component_elements; uint16_t max_component_depth; } tt_maxp_t; typedef struct _tt_name_record { uint16_t platform; uint16_t encoding; uint16_t language; uint16_t name; uint16_t length; uint16_t offset; } tt_name_record_t; typedef struct _tt_name { uint16_t format; uint16_t num_records; uint16_t strings_offset; tt_name_record_t records[1]; } tt_name_t; /* bitmask for fsSelection field */ #define TT_FS_SELECTION_ITALIC 1 #define TT_FS_SELECTION_BOLD 32 /* _unused fields are defined in TT spec but not used by cairo */ typedef struct _tt_os2 { uint16_t _unused1[2]; uint16_t usWeightClass; uint16_t _unused2[28]; uint16_t fsSelection; uint16_t _unused3[11]; } tt_os2_t; /* composite_glyph_t flags */ #define TT_ARG_1_AND_2_ARE_WORDS 0x0001 #define TT_WE_HAVE_A_SCALE 0x0008 #define TT_MORE_COMPONENTS 0x0020 #define TT_WE_HAVE_AN_X_AND_Y_SCALE 0x0040 #define TT_WE_HAVE_A_TWO_BY_TWO 0x0080 typedef struct _tt_composite_glyph { uint16_t flags; uint16_t index; uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ } tt_composite_glyph_t; typedef struct _tt_glyph_data { int16_t num_contours; int8_t data[8]; tt_composite_glyph_t glyph; } tt_glyph_data_t; #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-truetype-subset.c000066400000000000000000001431541271037650300267330ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg * Adrian Johnson */ /* * Useful links: * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html * http://www.microsoft.com/typography/specs/default.htm */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-scaled-font-subsets-private.h" #include "cairo-truetype-subset-private.h" typedef struct subset_glyph subset_glyph_t; struct subset_glyph { int parent_index; unsigned long location; }; typedef struct _cairo_truetype_font cairo_truetype_font_t; typedef struct table table_t; struct table { unsigned long tag; cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag); int pos; /* position in the font directory */ }; struct _cairo_truetype_font { cairo_scaled_font_subset_t *scaled_font_subset; table_t truetype_tables[10]; int num_tables; struct { char *font_name; char *ps_name; unsigned int num_glyphs; int *widths; long x_min, y_min, x_max, y_max; long ascent, descent; int units_per_em; } base; subset_glyph_t *glyphs; const cairo_scaled_font_backend_t *backend; int num_glyphs_in_face; int checksum_index; cairo_array_t output; cairo_array_t string_offsets; unsigned long last_offset; unsigned long last_boundary; int *parent_to_subset; cairo_status_t status; cairo_bool_t is_pdf; }; /* * Test that the structs we define for TrueType tables have the * correct size, ie. they are not padded. */ #define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) check (tt_head_t, 54); check (tt_hhea_t, 36); check (tt_maxp_t, 32); check (tt_name_record_t, 12); check (tt_name_t, 18); check (tt_name_t, 18); check (tt_composite_glyph_t, 16); check (tt_glyph_data_t, 26); #undef check static cairo_status_t cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, unsigned short *out); #define SFNT_VERSION 0x00010000 #define SFNT_STRING_MAX_LENGTH 65535 static cairo_status_t _cairo_truetype_font_set_error (cairo_truetype_font_t *font, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS || status == (int)CAIRO_INT_STATUS_UNSUPPORTED) return status; _cairo_status_set_error (&font->status, status); return _cairo_error (status); } static cairo_status_t _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t is_pdf, cairo_truetype_font_t **font_return) { cairo_status_t status; cairo_truetype_font_t *font; const cairo_scaled_font_backend_t *backend; tt_head_t head; tt_hhea_t hhea; tt_maxp_t maxp; unsigned long size; backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; /* FIXME: We should either support subsetting vertical fonts, or fail on * vertical. Currently font_options_t doesn't have vertical flag, but * it should be added in the future. For now, the freetype backend * returns UNSUPPORTED in load_truetype_table if the font is vertical. * * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font)) * return CAIRO_INT_STATUS_UNSUPPORTED; */ /* We need to use a fallback font generated from the synthesized outlines. */ if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; size = sizeof (tt_head_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); if (unlikely (status)) return status; size = sizeof (tt_maxp_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_maxp, 0, (unsigned char *) &maxp, &size); if (unlikely (status)) return status; size = sizeof (tt_hhea_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); if (unlikely (status)) return status; font = malloc (sizeof (cairo_truetype_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); font->scaled_font_subset = scaled_font_subset; font->last_offset = 0; font->last_boundary = 0; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); if (unlikely (status)) goto fail1; font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); if (unlikely (font->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); if (unlikely (font->parent_to_subset == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } font->is_pdf = is_pdf; font->base.num_glyphs = 0; font->base.x_min = (int16_t) be16_to_cpu (head.x_min); font->base.y_min = (int16_t) be16_to_cpu (head.y_min); font->base.x_max = (int16_t) be16_to_cpu (head.x_max); font->base.y_max = (int16_t) be16_to_cpu (head.y_max); font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender); font->base.descent = (int16_t) be16_to_cpu (hhea.descender); font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em); if (font->base.units_per_em == 0) font->base.units_per_em = 2048; font->base.ps_name = NULL; font->base.font_name = NULL; status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, &font->base.ps_name, &font->base.font_name); if (_cairo_status_is_error (status)) goto fail3; /* If the PS name is not found, create a CairoFont-x-y name. */ if (font->base.ps_name == NULL) { font->base.ps_name = malloc (30); if (unlikely (font->base.ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); } font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); if (unlikely (font->base.widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); status = _cairo_array_grow_by (&font->string_offsets, 10); if (unlikely (status)) goto fail5; font->status = CAIRO_STATUS_SUCCESS; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_array_fini (&font->string_offsets); free (font->base.widths); fail4: free (font->base.ps_name); fail3: free (font->parent_to_subset); free (font->base.font_name); fail2: free (font->glyphs); fail1: _cairo_array_fini (&font->output); free (font); return status; } static void cairo_truetype_font_destroy (cairo_truetype_font_t *font) { _cairo_array_fini (&font->string_offsets); free (font->base.widths); free (font->base.ps_name); free (font->base.font_name); free (font->parent_to_subset); free (font->glyphs); _cairo_array_fini (&font->output); free (font); } static cairo_status_t cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, size_t length, unsigned char **buffer) { cairo_status_t status; if (font->status) return font->status; status = _cairo_array_allocate (&font->output, length, (void **) buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static void cairo_truetype_font_write (cairo_truetype_font_t *font, const void *data, size_t length) { cairo_status_t status; if (font->status) return; status = _cairo_array_append_multiple (&font->output, data, length); if (unlikely (status)) status = _cairo_truetype_font_set_error (font, status); } static void cairo_truetype_font_write_be16 (cairo_truetype_font_t *font, uint16_t value) { uint16_t be16_value; if (font->status) return; be16_value = cpu_to_be16 (value); cairo_truetype_font_write (font, &be16_value, sizeof be16_value); } static void cairo_truetype_font_write_be32 (cairo_truetype_font_t *font, uint32_t value) { uint32_t be32_value; if (font->status) return; be32_value = cpu_to_be32 (value); cairo_truetype_font_write (font, &be32_value, sizeof be32_value); } static cairo_status_t cairo_truetype_font_align_output (cairo_truetype_font_t *font, unsigned long *aligned) { int length, pad; unsigned char *padding; length = _cairo_array_num_elements (&font->output); *aligned = (length + 3) & ~3; pad = *aligned - length; if (pad) { cairo_status_t status; status = cairo_truetype_font_allocate_write_buffer (font, pad, &padding); if (unlikely (status)) return status; memset (padding, 0, pad); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, unsigned long boundary) { cairo_status_t status; if (font->status) return font->status; if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH) { status = _cairo_array_append (&font->string_offsets, &font->last_boundary); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->last_offset = font->last_boundary; } font->last_boundary = boundary; return CAIRO_STATUS_SUCCESS; } typedef struct _cmap_unicode_range { unsigned int start; unsigned int end; } cmap_unicode_range_t; static cmap_unicode_range_t winansi_unicode_ranges[] = { { 0x0020, 0x007f }, { 0x00a0, 0x00ff }, { 0x0152, 0x0153 }, { 0x0160, 0x0161 }, { 0x0178, 0x0178 }, { 0x017d, 0x017e }, { 0x0192, 0x0192 }, { 0x02c6, 0x02c6 }, { 0x02dc, 0x02dc }, { 0x2013, 0x2026 }, { 0x2030, 0x2030 }, { 0x2039, 0x203a }, { 0x20ac, 0x20ac }, { 0x2122, 0x2122 }, }; static cairo_status_t cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font, unsigned long tag) { int i; unsigned int j; int range_offset; int num_ranges; int entry_selector; int length; num_ranges = ARRAY_LENGTH (winansi_unicode_ranges); length = 16 + (num_ranges + 1)*8; for (i = 0; i < num_ranges; i++) length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2; entry_selector = 0; while ((1 << entry_selector) <= (num_ranges + 1)) entry_selector++; entry_selector--; cairo_truetype_font_write_be16 (font, 0); /* Table version */ cairo_truetype_font_write_be16 (font, 1); /* Num tables */ cairo_truetype_font_write_be16 (font, 3); /* Platform */ cairo_truetype_font_write_be16 (font, 1); /* Encoding */ cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */ /* Output a format 4 encoding table for the winansi encoding */ cairo_truetype_font_write_be16 (font, 4); /* Format */ cairo_truetype_font_write_be16 (font, length); /* Length */ cairo_truetype_font_write_be16 (font, 0); /* Version */ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2); /* 2*segcount */ cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1))); /* searchrange */ cairo_truetype_font_write_be16 (font, entry_selector); /* entry selector */ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1))); /* rangeshift */ for (i = 0; i < num_ranges; i++) cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */ cairo_truetype_font_write_be16 (font, 0xffff); /* end count[] */ cairo_truetype_font_write_be16 (font, 0); /* reserved */ for (i = 0; i < num_ranges; i++) cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start); /* startCode[] */ cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[] */ for (i = 0; i < num_ranges; i++) cairo_truetype_font_write_be16 (font, 0x0000); /* delta[] */ cairo_truetype_font_write_be16 (font, 1); /* delta[] */ range_offset = num_ranges*2 + 2; for (i = 0; i < num_ranges; i++) { cairo_truetype_font_write_be16 (font, range_offset); /* rangeOffset[] */ range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2; } cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[] */ for (i = 0; i < num_ranges; i++) { for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) { int ch = _cairo_unicode_to_winansi (j); int glyph; if (ch > 0) glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch]; else glyph = 0; cairo_truetype_font_write_be16 (font, glyph); } } return font->status; } static cairo_status_t cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, unsigned long tag) { cairo_status_t status; unsigned char *buffer; unsigned long size; if (font->status) return font->status; size = 0; status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, unsigned char *buffer, unsigned long size) { tt_glyph_data_t *glyph_data; tt_composite_glyph_t *composite_glyph; int num_args; int has_more_components; unsigned short flags; unsigned short index; cairo_status_t status; unsigned char *end = buffer + size; if (font->status) return font->status; glyph_data = (tt_glyph_data_t *) buffer; if ((unsigned char *)(&glyph_data->data) >= end) return CAIRO_INT_STATUS_UNSUPPORTED; if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0) return CAIRO_STATUS_SUCCESS; composite_glyph = &glyph_data->glyph; do { if ((unsigned char *)(&composite_glyph->args[1]) > end) return CAIRO_INT_STATUS_UNSUPPORTED; flags = be16_to_cpu (composite_glyph->flags); has_more_components = flags & TT_MORE_COMPONENTS; status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); if (unlikely (status)) return status; composite_glyph->index = cpu_to_be16 (index); num_args = 1; if (flags & TT_ARG_1_AND_2_ARE_WORDS) num_args += 1; if (flags & TT_WE_HAVE_A_SCALE) num_args += 1; else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) num_args += 2; else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) num_args += 4; composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); } while (has_more_components); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned long start_offset, index, size, next; tt_head_t header; unsigned long begin, end; unsigned char *buffer; unsigned int i; union { unsigned char *bytes; uint16_t *short_offsets; uint32_t *long_offsets; } u; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_head_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); else size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); u.bytes = malloc (size); if (unlikely (u.bytes == NULL)) return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_loca, 0, u.bytes, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); start_offset = _cairo_array_num_elements (&font->output); for (i = 0; i < font->base.num_glyphs; i++) { index = font->glyphs[i].parent_index; if (be16_to_cpu (header.index_to_loc_format) == 0) { begin = be16_to_cpu (u.short_offsets[index]) * 2; end = be16_to_cpu (u.short_offsets[index + 1]) * 2; } else { begin = be32_to_cpu (u.long_offsets[index]); end = be32_to_cpu (u.long_offsets[index + 1]); } /* quick sanity check... */ if (end < begin) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; } size = end - begin; status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_check_boundary (font, next); if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) goto FAIL; if (size != 0) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_glyf, begin, buffer, &size); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); if (unlikely (status)) goto FAIL; } } status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; status = font->status; FAIL: free (u.bytes); return _cairo_truetype_font_set_error (font, status); } static cairo_status_t cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned char *buffer; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = 0; status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->checksum_index = _cairo_array_num_elements (&font->output) + 8; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); /* set checkSumAdjustment to 0 for table checksum calculation */ *(uint32_t *)(buffer + 8) = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag) { tt_hhea_t *hhea; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_hhea_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) hhea, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned long size; unsigned long long_entry_size; unsigned long short_entry_size; short *p; unsigned int i; tt_hhea_t hhea; int num_hmetrics; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_hhea_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); num_hmetrics = be16_to_cpu(hhea.num_hmetrics); for (i = 0; i < font->base.num_glyphs; i++) { long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); status = cairo_truetype_font_allocate_write_buffer (font, long_entry_size, (unsigned char **) &p); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (font->glyphs[i].parent_index < num_hmetrics) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, font->glyphs[i].parent_index * long_entry_size, (unsigned char *) p, &long_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } else { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, (unsigned char *) p, &short_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, num_hmetrics * long_entry_size + (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, (unsigned char *) (p + 1), &short_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } font->base.widths[i] = be16_to_cpu (p[0]); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned int i; tt_head_t header; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof(tt_head_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) { for (i = 0; i < font->base.num_glyphs + 1; i++) cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2); } else { for (i = 0; i < font->base.num_glyphs + 1; i++) cairo_truetype_font_write_be32 (font, font->glyphs[i].location); } return font->status; } static cairo_status_t cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, unsigned long tag) { tt_maxp_t *maxp; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_maxp_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) maxp, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) { cairo_status_t status; unsigned char *table_buffer; size_t table_buffer_length; unsigned short search_range, entry_selector, range_shift; if (font->status) return font->status; search_range = 1; entry_selector = 0; while (search_range * 2 <= font->num_tables) { search_range *= 2; entry_selector++; } search_range *= 16; range_shift = font->num_tables * 16 - search_range; cairo_truetype_font_write_be32 (font, SFNT_VERSION); cairo_truetype_font_write_be16 (font, font->num_tables); cairo_truetype_font_write_be16 (font, search_range); cairo_truetype_font_write_be16 (font, entry_selector); cairo_truetype_font_write_be16 (font, range_shift); /* Allocate space for the table directory. Each directory entry * will be filled in by cairo_truetype_font_update_entry() after * the table is written. */ table_buffer_length = font->num_tables * 16; status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, &table_buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static uint32_t cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font, unsigned long start, unsigned long end) { uint32_t *padded_end; uint32_t *p; uint32_t checksum; char *data; checksum = 0; data = _cairo_array_index (&font->output, 0); p = (uint32_t *) (data + start); padded_end = (uint32_t *) (data + ((end + 3) & ~3)); while (p < padded_end) checksum += be32_to_cpu(*p++); return checksum; } static void cairo_truetype_font_update_entry (cairo_truetype_font_t *font, int index, unsigned long tag, unsigned long start, unsigned long end) { uint32_t *entry; entry = _cairo_array_index (&font->output, 12 + 16 * index); entry[0] = cpu_to_be32 ((uint32_t)tag); entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end)); entry[2] = cpu_to_be32 ((uint32_t)start); entry[3] = cpu_to_be32 ((uint32_t)(end - start)); } static cairo_status_t cairo_truetype_font_generate (cairo_truetype_font_t *font, const char **data, unsigned long *length, const unsigned long **string_offsets, unsigned long *num_strings) { cairo_status_t status; unsigned long start, end, next; uint32_t checksum, *checksum_location; int i; if (font->status) return font->status; status = cairo_truetype_font_write_offset_table (font); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_align_output (font, &start); if (unlikely (status)) goto FAIL; end = 0; for (i = 0; i < font->num_tables; i++) { status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); if (unlikely (status)) goto FAIL; end = _cairo_array_num_elements (&font->output); status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, font->truetype_tables[i].tag, start, end); status = cairo_truetype_font_check_boundary (font, next); if (unlikely (status)) goto FAIL; start = next; } checksum = 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end); checksum_location = _cairo_array_index (&font->output, font->checksum_index); *checksum_location = cpu_to_be32 (checksum); *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); *num_strings = _cairo_array_num_elements (&font->string_offsets); if (*num_strings != 0) *string_offsets = _cairo_array_index (&font->string_offsets, 0); else *string_offsets = NULL; FAIL: return _cairo_truetype_font_set_error (font, status); } static cairo_status_t cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, unsigned short *out) { if (glyph >= font->num_glyphs_in_face) return CAIRO_INT_STATUS_UNSUPPORTED; if (font->parent_to_subset[glyph] == 0) { font->parent_to_subset[glyph] = font->base.num_glyphs; font->glyphs[font->base.num_glyphs].parent_index = glyph; font->base.num_glyphs++; } *out = font->parent_to_subset[glyph]; return CAIRO_STATUS_SUCCESS; } static void cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, unsigned long tag, cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag), int pos) { font->truetype_tables[font->num_tables].tag = tag; font->truetype_tables[font->num_tables].write = write; font->truetype_tables[font->num_tables].pos = pos; font->num_tables++; } /* cairo_truetype_font_create_truetype_table_list() builds the list of * truetype tables to be embedded in the subsetted font. Each call to * cairo_truetype_font_add_truetype_table() adds a table, the callback * for generating the table, and the position in the table directory * to the truetype_tables array. * * As we write out the glyf table we remap composite glyphs. * Remapping composite glyphs will reference the sub glyphs the * composite glyph is made up of. The "glyf" table callback needs to * be called first so we have all the glyphs in the subset before * going further. * * The order in which tables are added to the truetype_table array * using cairo_truetype_font_add_truetype_table() specifies the order * in which the callback functions will be called. * * The tables in the table directory must be listed in alphabetical * order. The "cvt", "fpgm", and "prep" are optional tables. They * will only be embedded in the subset if they exist in the source * font. "cmap" is only embedded for latin fonts. The pos parameter of * cairo_truetype_font_add_truetype_table() specifies the position of * the table in the table directory. */ static void cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) { cairo_bool_t has_cvt = FALSE; cairo_bool_t has_fpgm = FALSE; cairo_bool_t has_prep = FALSE; unsigned long size; int pos; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_cvt, 0, NULL, &size) == CAIRO_INT_STATUS_SUCCESS) has_cvt = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_fpgm, 0, NULL, &size) == CAIRO_INT_STATUS_SUCCESS) has_fpgm = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_prep, 0, NULL, &size) == CAIRO_INT_STATUS_SUCCESS) has_prep = TRUE; font->num_tables = 0; pos = 0; if (font->is_pdf && font->scaled_font_subset->is_latin) pos++; if (has_cvt) pos++; if (has_fpgm) pos++; cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); pos = 0; if (font->is_pdf && font->scaled_font_subset->is_latin) cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); if (has_cvt) cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++); if (has_fpgm) cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++); pos++; cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++); if (has_prep) cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); } static cairo_status_t cairo_truetype_subset_init_internal (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset, cairo_bool_t is_pdf) { cairo_truetype_font_t *font = NULL; cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned long offsets_length; unsigned int i; const unsigned long *string_offsets = NULL; unsigned long num_strings = 0; status = _cairo_truetype_font_create (font_subset, is_pdf, &font); if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); if (unlikely (status)) goto fail1; } cairo_truetype_font_create_truetype_table_list (font); status = cairo_truetype_font_generate (font, &data, &length, &string_offsets, &num_strings); if (unlikely (status)) goto fail1; truetype_subset->ps_name = strdup (font->base.ps_name); if (unlikely (truetype_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } if (font->base.font_name != NULL) { truetype_subset->family_name_utf8 = strdup (font->base.font_name); if (unlikely (truetype_subset->family_name_utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { truetype_subset->family_name_utf8 = NULL; } /* The widths array returned must contain only widths for the * glyphs in font_subset. Any subglyphs appended after * font_subset->num_glyphs are omitted. */ truetype_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (truetype_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em; truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em; truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em; truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em; truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em; truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; if (length) { truetype_subset->data = malloc (length); if (unlikely (truetype_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (truetype_subset->data, data, length); } else truetype_subset->data = NULL; truetype_subset->data_length = length; if (num_strings) { offsets_length = num_strings * sizeof (unsigned long); truetype_subset->string_offsets = malloc (offsets_length); if (unlikely (truetype_subset->string_offsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail5; } memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); truetype_subset->num_string_offsets = num_strings; } else { truetype_subset->string_offsets = NULL; truetype_subset->num_string_offsets = 0; } cairo_truetype_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail5: free (truetype_subset->data); fail4: free (truetype_subset->widths); fail3: free (truetype_subset->family_name_utf8); fail2: free (truetype_subset->ps_name); fail1: cairo_truetype_font_destroy (font); return status; } cairo_status_t _cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset) { return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE); } cairo_status_t _cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset) { return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE); } void _cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) { free (subset->ps_name); free (subset->family_name_utf8); free (subset->widths); free (subset->data); free (subset->string_offsets); } static cairo_int_status_t _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, unsigned long table_offset, unsigned long index, uint32_t *ucs4) { cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_segment_map_t *map; char buf[4]; unsigned int num_segments, i; unsigned long size; uint16_t *start_code; uint16_t *end_code; uint16_t *delta; uint16_t *range_offset; uint16_t c; backend = scaled_font->backend; size = 4; status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) &buf, &size); if (unlikely (status)) return status; /* All table formats have the same first two words */ map = (tt_segment_map_t *) buf; if (be16_to_cpu (map->format) != 4) return CAIRO_INT_STATUS_UNSUPPORTED; size = be16_to_cpu (map->length); map = malloc (size); if (unlikely (map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) map, &size); if (unlikely (status)) goto fail; num_segments = be16_to_cpu (map->segCountX2)/2; /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of * uint16_t each num_segments long. */ if (size < (8 + 4*num_segments)*sizeof(uint16_t)) return CAIRO_INT_STATUS_UNSUPPORTED; end_code = map->endCount; start_code = &(end_code[num_segments + 1]); delta = &(start_code[num_segments]); range_offset = &(delta[num_segments]); /* search for glyph in segments with rangeOffset=0 */ for (i = 0; i < num_segments; i++) { c = index - be16_to_cpu (delta[i]); if (range_offset[i] == 0 && c >= be16_to_cpu (start_code[i]) && c <= be16_to_cpu (end_code[i])) { *ucs4 = c; goto found; } } /* search for glyph in segments with rangeOffset=1 */ for (i = 0; i < num_segments; i++) { if (range_offset[i] != 0) { uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; uint16_t g_id_be = cpu_to_be16 (index); int j; if (range_size > 0) { if ((char*)glyph_ids + 2*range_size > (char*)map + size) return CAIRO_INT_STATUS_UNSUPPORTED; for (j = 0; j < range_size; j++) { if (glyph_ids[j] == g_id_be) { *ucs4 = be16_to_cpu (start_code[i]) + j; goto found; } } } } } /* glyph not found */ *ucs4 = -1; found: status = CAIRO_STATUS_SUCCESS; fail: free (map); return status; } cairo_int_status_t _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, unsigned long index, uint32_t *ucs4) { cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; const cairo_scaled_font_backend_t *backend; tt_cmap_t *cmap; char buf[4]; int num_tables, i; unsigned long size; backend = scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; size = 4; status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, (unsigned char *) &buf, &size); if (unlikely (status)) return status; cmap = (tt_cmap_t *) buf; num_tables = be16_to_cpu (cmap->num_tables); size = 4 + num_tables*sizeof(tt_cmap_index_t); cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); if (unlikely (cmap == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, (unsigned char *) cmap, &size); if (unlikely (status)) goto cleanup; /* Find a table with Unicode mapping */ for (i = 0; i < num_tables; i++) { if (be16_to_cpu (cmap->index[i].platform) == 3 && be16_to_cpu (cmap->index[i].encoding) == 1) { status = _cairo_truetype_reverse_cmap (scaled_font, be32_to_cpu (cmap->index[i].offset), index, ucs4); if (status != CAIRO_INT_STATUS_UNSUPPORTED) break; } } cleanup: free (cmap); return status; } static cairo_status_t find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out) { tt_name_record_t *record; int i, len; char *str; char *p; cairo_bool_t has_tag; cairo_status_t status; str = NULL; for (i = 0; i < be16_to_cpu (name->num_records); i++) { record = &(name->records[i]); if (be16_to_cpu (record->name) == name_id && be16_to_cpu (record->platform) == platform && be16_to_cpu (record->encoding) == encoding && (language == -1 || be16_to_cpu (record->language) == language)) { str = malloc (be16_to_cpu (record->length) + 1); if (str == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); len = be16_to_cpu (record->length); memcpy (str, ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), len); str[be16_to_cpu (record->length)] = 0; break; } } if (str == NULL) { *str_out = NULL; return CAIRO_STATUS_SUCCESS; } if (platform == 3) { /* Win platform, unicode encoding */ /* convert to utf8 */ int size = 0; char *utf8; uint16_t *u = (uint16_t *) str; int u_len = len/2; for (i = 0; i < u_len; i++) size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL); utf8 = malloc (size + 1); if (utf8 == NULL) { status =_cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } p = utf8; for (i = 0; i < u_len; i++) p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p); *p = 0; free (str); str = utf8; } else if (platform == 1) { /* Mac platform, Mac Roman encoding */ /* Replace characters above 127 with underscores. We could use * a lookup table to convert to unicode but since most fonts * include a unicode name this is just a rarely used fallback. */ for (i = 0; i < len; i++) { if ((unsigned char)str[i] > 127) str[i] = '_'; } } /* If font name is prefixed with a PDF subset tag, strip it off. */ p = str; len = strlen (str); has_tag = FALSE; if (len > 7 && p[6] == '+') { has_tag = TRUE; for (i = 0; i < 6; i++) { if (p[i] < 'A' || p[i] > 'Z') { has_tag = FALSE; break; } } } if (has_tag) { p = malloc (len - 6); if (unlikely (p == NULL)) { status =_cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } memcpy (p, str + 7, len - 7); p[len-7] = 0; free (str); str = p; } *str_out = str; return CAIRO_STATUS_SUCCESS; fail: free (str); return status; } cairo_int_status_t _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, char **ps_name_out, char **font_name_out) { cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_name_t *name; unsigned long size; char *ps_name = NULL; char *family_name = NULL; backend = scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, TT_TAG_name, 0, NULL, &size); if (status) return status; name = malloc (size); if (name == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_name, 0, (unsigned char *) name, &size); if (status) goto fail; /* Find PS Name (name_id = 6). OT spec says PS name must be one of * the following two encodings */ status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */ if (unlikely(status)) goto fail; if (!ps_name) { status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */ if (unlikely(status)) goto fail; } /* Find Family name (name_id = 1) */ status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */ if (unlikely(status)) goto fail; if (!family_name) { status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */ if (unlikely(status)) goto fail; } if (!family_name) { status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */ if (unlikely(status)) goto fail; } if (!family_name) { status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */ if (unlikely(status)) goto fail; } free (name); /* Ensure PS name is a valid PDF/PS name object. In PDF names are * treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded * as '#' followed by 2 hex digits that encode the byte. By also * encoding the characters in the reserved string we ensure the * name is also PS compatible. */ if (ps_name) { static const char *reserved = "()<>[]{}/%#\\"; char buf[128]; /* max name length is 127 bytes */ char *src = ps_name; char *dst = buf; while (*src && dst < buf + 127) { unsigned char c = *src; if (c < 0x21 || c > 0x7e || strchr (reserved, c)) { if (dst + 4 > buf + 127) break; snprintf (dst, 4, "#%02X", c); src++; dst += 3; } else { *dst++ = *src++; } } *dst = 0; free (ps_name); ps_name = strdup (buf); if (ps_name == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } } *ps_name_out = ps_name; *font_name_out = family_name; return CAIRO_STATUS_SUCCESS; fail: free (name); free (ps_name); free (family_name); *ps_name_out = NULL; *font_name_out = NULL; return status; } cairo_int_status_t _cairo_truetype_get_style (cairo_scaled_font_t *scaled_font, int *weight, cairo_bool_t *bold, cairo_bool_t *italic) { cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_os2_t os2; unsigned long size; uint16_t selection; backend = scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, TT_TAG_OS2, 0, NULL, &size); if (status) return status; if (size < sizeof(os2)) return CAIRO_INT_STATUS_UNSUPPORTED; size = sizeof (os2); status = backend->load_truetype_table (scaled_font, TT_TAG_OS2, 0, (unsigned char *) &os2, &size); if (status) return status; *weight = be16_to_cpu (os2.usWeightClass); selection = be16_to_cpu (os2.fsSelection); *bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE; *italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE; return CAIRO_STATUS_SUCCESS; } #endif /* CAIRO_HAS_FONT_SUBSET */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type1-fallback.c000066400000000000000000000731001271037650300263370ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Adrian Johnson */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-type1-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-path-fixed-private.h" #include "cairo-output-stream-private.h" typedef enum { CAIRO_CHARSTRING_TYPE1, CAIRO_CHARSTRING_TYPE2 } cairo_charstring_type_t; typedef struct _cairo_type1_font { int *widths; cairo_scaled_font_subset_t *scaled_font_subset; cairo_scaled_font_t *type1_scaled_font; cairo_array_t contents; double x_min, y_min, x_max, y_max; const char *data; unsigned long header_size; unsigned long data_size; unsigned long trailer_size; int bbox_position; int bbox_max_chars; cairo_output_stream_t *output; unsigned short eexec_key; cairo_bool_t hex_encode; int hex_column; } cairo_type1_font_t; static cairo_status_t cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_type1_font_t **subset_return, cairo_bool_t hex_encode) { cairo_type1_font_t *font; cairo_font_face_t *font_face; cairo_matrix_t font_matrix; cairo_matrix_t ctm; cairo_font_options_t font_options; cairo_status_t status; font = calloc (1, sizeof (cairo_type1_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { free (font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } font->scaled_font_subset = scaled_font_subset; font->hex_encode = hex_encode; font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font); cairo_matrix_init_scale (&font_matrix, 1000, -1000); cairo_matrix_init_identity (&ctm); _cairo_font_options_init_default (&font_options); cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); font->type1_scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, &font_options); status = font->type1_scaled_font->status; if (unlikely (status)) goto fail; _cairo_array_init (&font->contents, sizeof (unsigned char)); font->output = NULL; *subset_return = font; return CAIRO_STATUS_SUCCESS; fail: free (font->widths); free (font); return status; } /* Charstring commands. If the high byte is 0 the command is encoded * with a single byte. */ #define CHARSTRING_sbw 0x0c07 #define CHARSTRING_rmoveto 0x0015 #define CHARSTRING_rlineto 0x0005 #define CHARSTRING_rcurveto 0x0008 #define CHARSTRING_closepath 0x0009 #define CHARSTRING_endchar 0x000e /* Before calling this function, the caller must allocate sufficient * space in data (see _cairo_array_grow_by). The maximum number of * bytes that will be used is 2. */ static void charstring_encode_command (cairo_array_t *data, int command) { cairo_status_t status; unsigned int orig_size; unsigned char buf[5]; unsigned char *p = buf; if (command & 0xff00) *p++ = command >> 8; *p++ = command & 0x00ff; /* Ensure the array doesn't grow, which allows this function to * have no possibility of failure. */ orig_size = _cairo_array_size (data); status = _cairo_array_append_multiple (data, buf, p - buf); assert (status == CAIRO_STATUS_SUCCESS); assert (_cairo_array_size (data) == orig_size); } /* Before calling this function, the caller must allocate sufficient * space in data (see _cairo_array_grow_by). The maximum number of * bytes that will be used is 5. */ static void charstring_encode_integer (cairo_array_t *data, int i, cairo_charstring_type_t type) { cairo_status_t status; unsigned int orig_size; unsigned char buf[10]; unsigned char *p = buf; if (i >= -107 && i <= 107) { *p++ = i + 139; } else if (i >= 108 && i <= 1131) { i -= 108; *p++ = (i >> 8)+ 247; *p++ = i & 0xff; } else if (i >= -1131 && i <= -108) { i = -i - 108; *p++ = (i >> 8)+ 251; *p++ = i & 0xff; } else { if (type == CAIRO_CHARSTRING_TYPE1) { *p++ = 0xff; *p++ = i >> 24; *p++ = (i >> 16) & 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; } else { *p++ = 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; *p++ = 0; *p++ = 0; } } /* Ensure the array doesn't grow, which allows this function to * have no possibility of failure. */ orig_size = _cairo_array_size (data); status = _cairo_array_append_multiple (data, buf, p - buf); assert (status == CAIRO_STATUS_SUCCESS); assert (_cairo_array_size (data) == orig_size); } typedef struct _ps_path_info { cairo_array_t *data; int current_x, current_y; cairo_charstring_type_t type; } t1_path_info_t; static cairo_status_t _charstring_move_to (void *closure, const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; charstring_encode_integer (path_info->data, dx, path_info->type); charstring_encode_integer (path_info->data, dy, path_info->type); path_info->current_x += dx; path_info->current_y += dy; charstring_encode_command (path_info->data, CHARSTRING_rmoveto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_line_to (void *closure, const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; charstring_encode_integer (path_info->data, dx, path_info->type); charstring_encode_integer (path_info->data, dy, path_info->type); path_info->current_x += dx; path_info->current_y += dy; charstring_encode_command (path_info->data, CHARSTRING_rlineto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_curve_to (void *closure, const cairo_point_t *point1, const cairo_point_t *point2, const cairo_point_t *point3) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx1, dy1, dx2, dy2, dx3, dy3; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 32); if (unlikely (status)) return status; dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y; dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1; dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1; dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2; dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2; charstring_encode_integer (path_info->data, dx1, path_info->type); charstring_encode_integer (path_info->data, dy1, path_info->type); charstring_encode_integer (path_info->data, dx2, path_info->type); charstring_encode_integer (path_info->data, dy2, path_info->type); charstring_encode_integer (path_info->data, dx3, path_info->type); charstring_encode_integer (path_info->data, dy3, path_info->type); path_info->current_x += dx1 + dx2 + dx3; path_info->current_y += dy1 + dy2 + dy3; charstring_encode_command (path_info->data, CHARSTRING_rcurveto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_close_path (void *closure) { cairo_status_t status; t1_path_info_t *path_info = (t1_path_info_t *) closure; if (path_info->type == CAIRO_CHARSTRING_TYPE2) return CAIRO_STATUS_SUCCESS; status = _cairo_array_grow_by (path_info->data, 2); if (unlikely (status)) return status; charstring_encode_command (path_info->data, CHARSTRING_closepath); return CAIRO_STATUS_SUCCESS; } static void charstring_encrypt (cairo_array_t *data) { unsigned char *d, *end; uint16_t c, p, r; r = CAIRO_TYPE1_CHARSTRING_KEY; d = (unsigned char *) _cairo_array_index (data, 0); end = d + _cairo_array_num_elements (data); while (d < end) { p = *d; c = p ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *d++ = c; } } static cairo_int_status_t cairo_type1_font_create_charstring (cairo_type1_font_t *font, int subset_index, int glyph_index, cairo_charstring_type_t type, cairo_array_t *data) { cairo_int_status_t status; cairo_scaled_glyph_t *scaled_glyph; t1_path_info_t path_info; cairo_text_extents_t *metrics; cairo_bool_t emit_path = TRUE; /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */ status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); /* It is ok for the .notdef glyph to not have a path available. We * just need the metrics to emit an empty glyph. */ if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) { emit_path = FALSE; status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); } if (unlikely (status)) return status; metrics = &scaled_glyph->metrics; if (subset_index == 0) { font->x_min = metrics->x_bearing; font->y_min = metrics->y_bearing; font->x_max = metrics->x_bearing + metrics->width; font->y_max = metrics->y_bearing + metrics->height; } else { if (metrics->x_bearing < font->x_min) font->x_min = metrics->x_bearing; if (metrics->y_bearing < font->y_min) font->y_min = metrics->y_bearing; if (metrics->x_bearing + metrics->width > font->x_max) font->x_max = metrics->x_bearing + metrics->width; if (metrics->y_bearing + metrics->height > font->y_max) font->y_max = metrics->y_bearing + metrics->height; } font->widths[subset_index] = metrics->x_advance; status = _cairo_array_grow_by (data, 30); if (unlikely (status)) return status; if (type == CAIRO_CHARSTRING_TYPE1) { charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type); charstring_encode_command (data, CHARSTRING_sbw); path_info.current_x = (int) scaled_glyph->metrics.x_bearing; path_info.current_y = (int) scaled_glyph->metrics.y_bearing; } else { charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); path_info.current_x = 0; path_info.current_y = 0; } path_info.data = data; path_info.type = type; if (emit_path) { status = _cairo_path_fixed_interpret (scaled_glyph->path, _charstring_move_to, _charstring_line_to, _charstring_curve_to, _charstring_close_path, &path_info); if (unlikely (status)) return status; } status = _cairo_array_grow_by (data, 1); if (unlikely (status)) return status; charstring_encode_command (path_info.data, CHARSTRING_endchar); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_write_charstrings (cairo_type1_font_t *font, cairo_output_stream_t *encrypted_output) { cairo_status_t status; unsigned char zeros[] = { 0, 0, 0, 0 }; cairo_array_t data; unsigned int i; int length; _cairo_array_init (&data, sizeof (unsigned char)); status = _cairo_array_grow_by (&data, 1024); if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, "2 index /CharStrings %d dict dup begin\n", font->scaled_font_subset->num_glyphs + 1); _cairo_scaled_font_freeze_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { _cairo_array_truncate (&data, 0); /* four "random" bytes required by encryption algorithm */ status = _cairo_array_append_multiple (&data, zeros, 4); if (unlikely (status)) break; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE1, &data); if (unlikely (status)) break; charstring_encrypt (&data); length = _cairo_array_num_elements (&data); if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (encrypted_output, "/%s %d RD ", font->scaled_font_subset->glyph_names[i], length); } else if (i == 0) { _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length); } else { _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length); } _cairo_output_stream_write (encrypted_output, _cairo_array_index (&data, 0), length); _cairo_output_stream_printf (encrypted_output, " ND\n"); } _cairo_scaled_font_thaw_cache (font->type1_scaled_font); fail: _cairo_array_fini (&data); return status; } static void cairo_type1_font_write_header (cairo_type1_font_t *font, const char *name) { unsigned int i; const char spaces[50] = " "; _cairo_output_stream_printf (font->output, "%%!FontType1-1.1 %s 1.0\n" "11 dict begin\n" "/FontName /%s def\n" "/PaintType 0 def\n" "/FontType 1 def\n" "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n", name, name); /* We don't know the bbox values until after the charstrings have * been generated. Reserve some space and fill in the bbox * later. */ /* Worst case for four signed ints with spaces between each number */ font->bbox_max_chars = 50; _cairo_output_stream_printf (font->output, "/FontBBox {"); font->bbox_position = _cairo_output_stream_get_position (font->output); _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars); _cairo_output_stream_printf (font->output, "} readonly def\n" "/Encoding 256 array\n" "0 1 255 {1 index exch /.notdef put} for\n"); if (font->scaled_font_subset->is_latin) { for (i = 1; i < 256; i++) { int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i]; if (subset_glyph > 0) { if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, font->scaled_font_subset->glyph_names[subset_glyph]); } else { _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph); } } } } else { for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, font->scaled_font_subset->glyph_names[i]); } else { _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); } } } _cairo_output_stream_printf (font->output, "readonly def\n" "currentdict end\n" "currentfile eexec\n"); } static cairo_status_t cairo_type1_write_stream_encrypted (void *closure, const unsigned char *data, unsigned int length) { const unsigned char *in, *end; uint16_t c, p; static const char hex_digits[16] = "0123456789abcdef"; char digits[3]; cairo_type1_font_t *font = closure; in = (const unsigned char *) data; end = (const unsigned char *) data + length; while (in < end) { p = *in++; c = p ^ (font->eexec_key >> 8); font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; if (font->hex_encode) { digits[0] = hex_digits[c >> 4]; digits[1] = hex_digits[c & 0x0f]; digits[2] = '\n'; font->hex_column += 2; if (font->hex_column == 78) { _cairo_output_stream_write (font->output, digits, 3); font->hex_column = 0; } else { _cairo_output_stream_write (font->output, digits, 2); } } else { digits[0] = c; _cairo_output_stream_write (font->output, digits, 1); } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_write_private_dict (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; cairo_status_t status2; cairo_output_stream_t *encrypted_output; font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; font->hex_column = 0; encrypted_output = _cairo_output_stream_create ( cairo_type1_write_stream_encrypted, NULL, font); if (_cairo_output_stream_get_status (encrypted_output)) return _cairo_output_stream_destroy (encrypted_output); /* Note: the first four spaces at the start of this private dict * are the four "random" bytes of plaintext required by the * encryption algorithm */ _cairo_output_stream_printf (encrypted_output, " dup /Private 9 dict dup begin\n" "/RD {string currentfile exch readstring pop}" " bind executeonly def\n" "/ND {noaccess def} executeonly def\n" "/NP {noaccess put} executeonly def\n" "/BlueValues [] def\n" "/MinFeature {16 16} def\n" "/lenIV 4 def\n" "/password 5839 def\n"); status = cairo_type1_font_write_charstrings (font, encrypted_output); if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, "end\n" "end\n" "readonly put\n" "noaccess put\n" "dup /FontName get exch definefont pop\n" "mark currentfile closefile\n"); fail: status2 = _cairo_output_stream_destroy (encrypted_output); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; } static void cairo_type1_font_write_trailer(cairo_type1_font_t *font) { int i; static const char zeros[65] = "0000000000000000000000000000000000000000000000000000000000000000\n"; for (i = 0; i < 8; i++) _cairo_output_stream_write (font->output, zeros, sizeof zeros); _cairo_output_stream_printf (font->output, "cleartomark\n"); } static cairo_status_t cairo_type1_write_stream (void *closure, const unsigned char *data, unsigned int length) { cairo_type1_font_t *font = closure; return _cairo_array_append_multiple (&font->contents, data, length); } static cairo_int_status_t cairo_type1_font_write (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; cairo_type1_font_write_header (font, name); font->header_size = _cairo_output_stream_get_position (font->output); status = cairo_type1_font_write_private_dict (font, name); if (unlikely (status)) return status; font->data_size = _cairo_output_stream_get_position (font->output) - font->header_size; cairo_type1_font_write_trailer (font); font->trailer_size = _cairo_output_stream_get_position (font->output) - font->header_size - font->data_size; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; status = _cairo_array_grow_by (&font->contents, 4096); if (unlikely (status)) return status; font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); if (_cairo_output_stream_get_status (font->output)) return _cairo_output_stream_destroy (font->output); status = cairo_type1_font_write (font, name); if (unlikely (status)) return status; font->data = _cairo_array_index (&font->contents, 0); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_destroy (cairo_type1_font_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; free (font->widths); cairo_scaled_font_destroy (font->type1_scaled_font); _cairo_array_fini (&font->contents); if (font->output) status = _cairo_output_stream_destroy (font->output); free (font); return status; } static cairo_status_t _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { cairo_type1_font_t *font; cairo_status_t status; unsigned long length; unsigned int i, len; status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); if (unlikely (status)) return status; status = cairo_type1_font_generate (font, name); if (unlikely (status)) goto fail1; type1_subset->base_font = strdup (name); if (unlikely (type1_subset->base_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (type1_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) type1_subset->widths[i] = (double)font->widths[i]/1000; type1_subset->x_min = (double)font->x_min/1000; type1_subset->y_min = (double)font->y_min/1000; type1_subset->x_max = (double)font->x_max/1000; type1_subset->y_max = (double)font->y_max/1000; type1_subset->ascent = (double)font->y_max/1000; type1_subset->descent = (double)font->y_min/1000; length = font->header_size + font->data_size + font->trailer_size; type1_subset->data = malloc (length); if (unlikely (type1_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } memcpy (type1_subset->data, _cairo_array_index (&font->contents, 0), length); len = snprintf(type1_subset->data + font->bbox_position, font->bbox_max_chars, "%d %d %d %d", (int)font->x_min, (int)font->y_min, (int)font->x_max, (int)font->y_max); type1_subset->data[font->bbox_position + len] = ' '; type1_subset->header_length = font->header_size; type1_subset->data_length = font->data_size; type1_subset->trailer_length = font->trailer_size; return cairo_type1_font_destroy (font); fail3: free (type1_subset->widths); fail2: free (type1_subset->base_font); fail1: /* status is already set, ignore further errors */ cairo_type1_font_destroy (font); return status; } cairo_status_t _cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset) { return _cairo_type1_fallback_init_internal (type1_subset, name, scaled_font_subset, FALSE); } cairo_status_t _cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset) { return _cairo_type1_fallback_init_internal (type1_subset, name, scaled_font_subset, TRUE); } void _cairo_type1_fallback_fini (cairo_type1_subset_t *subset) { free (subset->base_font); free (subset->widths); free (subset->data); } cairo_status_t _cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, cairo_scaled_font_subset_t *scaled_font_subset) { cairo_type1_font_t *font; cairo_status_t status; unsigned int i; cairo_array_t charstring; status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); if (unlikely (status)) return status; _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); if (unlikely (type2_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } _cairo_scaled_font_freeze_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { _cairo_array_init (&charstring, sizeof (unsigned char)); status = _cairo_array_grow_by (&charstring, 32); if (unlikely (status)) goto fail2; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE2, &charstring); if (unlikely (status)) goto fail2; status = _cairo_array_append (&type2_subset->charstrings, &charstring); if (unlikely (status)) goto fail2; } _cairo_scaled_font_thaw_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) type2_subset->widths[i] = font->widths[i]; type2_subset->x_min = (int) font->x_min; type2_subset->y_min = (int) font->y_min; type2_subset->x_max = (int) font->x_max; type2_subset->y_max = (int) font->y_max; type2_subset->ascent = (int) font->y_max; type2_subset->descent = (int) font->y_min; return cairo_type1_font_destroy (font); fail2: _cairo_scaled_font_thaw_cache (font->type1_scaled_font); _cairo_array_fini (&charstring); _cairo_type2_charstrings_fini (type2_subset); fail1: cairo_type1_font_destroy (font); return status; } void _cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset) { unsigned int i, num_charstrings; cairo_array_t *charstring; num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings); for (i = 0; i < num_charstrings; i++) { charstring = _cairo_array_index (&type2_subset->charstrings, i); _cairo_array_fini (charstring); } _cairo_array_fini (&type2_subset->charstrings); free (type2_subset->widths); } #endif /* CAIRO_HAS_FONT_SUBSET */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type1-glyph-names.c000066400000000000000000000410621271037650300270260ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg */ #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-type1-private.h" #include "cairo-scaled-font-subsets-private.h" #if 0 /* * The three tables that follow are generated using this perl code: */ @ps_standard_encoding = ( # 0 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 16 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 32 "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", # 48 "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", # 64 "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", # 80 "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", # 96 "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", # 112 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", NULL, # 128 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 144 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 160 NULL, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft","guilsinglright","fi", "fl", # 176 NULL, "endash", "dagger", "daggerdbl", "periodcentered",NULL, "paragraph", "bullet", "quotesinglbase","quotedblbase","quotedblright","guillemotright", "ellipsis", "perthousand", NULL, "questiondown", # 192 NULL, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", "dieresis", NULL, "ring", "cedilla", NULL, "hungarumlaut", "ogonek", "caron", # 208 "emdash", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 224 NULL, "AE", NULL, "ordfeminine", NULL, NULL, NULL, NULL, "Lslash", "Oslash", "OE", "ordmasculine", NULL, NULL, NULL, NULL, # 240 NULL, "ae", NULL, NULL, NULL, "dotlessi", NULL, NULL, "lslash", "oslash", "oe", "germandbls", NULL, NULL, NULL, NULL ); @winansi_encoding = ( # 0 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 16 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, # 32 "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", # 48 "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", # 64 "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", # 80 "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", # 96 "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", # 112 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", NULL, # 128 "Euro", NULL, "quotesinglbase","florin", "quotedblbase", "ellipsis", "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", "guilsinglleft", "OE", NULL, "Zcaron", NULL, # 144 NULL, "quoteleft", "quoteright", "quotedblleft", "quotedblright","bullet", "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", "oe", NULL, "zcaron", "Ydieresis", # 160 NULL, "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", # 173 is also "hyphen" but we leave this NULL to avoid duplicate names "logicalnot", NULL, "registered", "macron", # 176 "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters","questiondown", # 192 "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", # 208 "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", # 224 "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", # 240 "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis" ); sub print_offsets { $s = qq(); for $sym (@_) { if (! ($sym eq NULL)) { $ss = qq( $hash{$sym}/*$sym*/,); } else { $ss = qq( 0,); } if (length($s) + length($ss) > 78) { print qq( $s\n); $s = ""; } $s .= $ss; } print qq( $s\n); } @combined = (@ps_standard_encoding, @winansi_encoding); print "static const char glyph_name_symbol[] = {\n"; %hash = (); $s = qq( "\\0"); $offset = 1; for $sym (@combined) { if (! ($sym eq NULL)) { if (! exists $hash{$sym}) { $hash{$sym} = $offset; $offset += length($sym) + 1; $ss = qq( "$sym\\0"); if (length($s) + length($ss) > 78) { print qq( $s\n); $s = ""; } $s .= $ss; } } } print qq( $s\n); print "};\n\n"; print "static const int16_t ps_standard_encoding_offset[256] = {\n"; print_offsets(@ps_standard_encoding); print "};\n"; print "static const int16_t winansi_encoding_offset[256] = {\n"; print_offsets(@winansi_encoding); print "};\n"; exit; #endif static const char glyph_name_symbol[] = { "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0" "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0" "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0" "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0" "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0" "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0" "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0" "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0" "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0" "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0" "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0" "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0" "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0" "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0" "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0" "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0" "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0" "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0" "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0" "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0" "oslash\0" "oe\0" "germandbls\0" "Euro\0" "Scaron\0" "Zcaron\0" "trademark\0" "scaron\0" "zcaron\0" "Ydieresis\0" "brokenbar\0" "copyright\0" "logicalnot\0" "registered\0" "degree\0" "plusminus\0" "twosuperior\0" "threesuperior\0" "mu\0" "onesuperior\0" "onequarter\0" "onehalf\0" "threequarters\0" "Agrave\0" "Aacute\0" "Acircumflex\0" "Atilde\0" "Adieresis\0" "Aring\0" "Ccedilla\0" "Egrave\0" "Eacute\0" "Ecircumflex\0" "Edieresis\0" "Igrave\0" "Iacute\0" "Icircumflex\0" "Idieresis\0" "Eth\0" "Ntilde\0" "Ograve\0" "Oacute\0" "Ocircumflex\0" "Otilde\0" "Odieresis\0" "multiply\0" "Ugrave\0" "Uacute\0" "Ucircumflex\0" "Udieresis\0" "Yacute\0" "Thorn\0" "agrave\0" "aacute\0" "acircumflex\0" "atilde\0" "adieresis\0" "aring\0" "ccedilla\0" "egrave\0" "eacute\0" "ecircumflex\0" "edieresis\0" "igrave\0" "iacute\0" "icircumflex\0" "idieresis\0" "eth\0" "ntilde\0" "ograve\0" "oacute\0" "ocircumflex\0" "otilde\0" "odieresis\0" "divide\0" "ugrave\0" "uacute\0" "ucircumflex\0" "udieresis\0" "yacute\0" "thorn\0" "ydieresis\0" }; static const int16_t ps_standard_encoding_offset[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/, 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/, 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/, 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/, 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/, 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/, 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/, 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/, 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0, 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/, 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/, 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/, 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/, 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0, 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/, 900/*germandbls*/, 0, 0, 0, 0, }; static const int16_t winansi_encoding_offset[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 498/*quotesingle*/, 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 719/*grave*/, 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 911/*Euro*/, 0, 628/*quotesinglbase*/, 474/*florin*/, 643/*quotedblbase*/, 685/*ellipsis*/, 579/*dagger*/, 586/*daggerdbl*/, 731/*circumflex*/, 694/*perthousand*/, 916/*Scaron*/, 537/*guilsinglleft*/, 855/*OE*/, 0, 923/*Zcaron*/, 0, 0, 338/*quoteleft*/, 59/*quoteright*/, 510/*quotedblleft*/, 656/*quotedblright*/, 621/*bullet*/, 572/*endash*/, 819/*emdash*/, 742/*tilde*/, 930/*trademark*/, 940/*scaron*/, 551/*guilsinglright*/, 897/*oe*/, 0, 947/*zcaron*/, 954/*Ydieresis*/, 0, 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 489/*currency*/, 470/*yen*/, 964/*brokenbar*/, 481/*section*/, 771/*dieresis*/, 974/*copyright*/, 829/*ordfeminine*/, 523/*guillemotleft*/, 984/*logicalnot*/, 0, 995/*registered*/, 748/*macron*/, 1006/*degree*/, 1013/*plusminus*/, 1023/*twosuperior*/, 1035/*threesuperior*/, 725/*acute*/, 1049/*mu*/, 611/*paragraph*/, 596/*periodcentered*/, 785/*cedilla*/, 1052/*onesuperior*/, 858/*ordmasculine*/, 670/*guillemotright*/, 1064/*onequarter*/, 1075/*onehalf*/, 1083/*threequarters*/, 706/*questiondown*/, 1097/*Agrave*/, 1104/*Aacute*/, 1111/*Acircumflex*/, 1123/*Atilde*/, 1130/*Adieresis*/, 1140/*Aring*/, 826/*AE*/, 1146/*Ccedilla*/, 1155/*Egrave*/, 1162/*Eacute*/, 1169/*Ecircumflex*/, 1181/*Edieresis*/, 1191/*Igrave*/, 1198/*Iacute*/, 1205/*Icircumflex*/, 1217/*Idieresis*/, 1227/*Eth*/, 1231/*Ntilde*/, 1238/*Ograve*/, 1245/*Oacute*/, 1252/*Ocircumflex*/, 1264/*Otilde*/, 1271/*Odieresis*/, 1281/*multiply*/, 848/*Oslash*/, 1290/*Ugrave*/, 1297/*Uacute*/, 1304/*Ucircumflex*/, 1316/*Udieresis*/, 1326/*Yacute*/, 1333/*Thorn*/, 900/*germandbls*/, 1339/*agrave*/, 1346/*aacute*/, 1353/*acircumflex*/, 1365/*atilde*/, 1372/*adieresis*/, 1382/*aring*/, 871/*ae*/, 1388/*ccedilla*/, 1397/*egrave*/, 1404/*eacute*/, 1411/*ecircumflex*/, 1423/*edieresis*/, 1433/*igrave*/, 1440/*iacute*/, 1447/*icircumflex*/, 1459/*idieresis*/, 1469/*eth*/, 1473/*ntilde*/, 1480/*ograve*/, 1487/*oacute*/, 1494/*ocircumflex*/, 1506/*otilde*/, 1513/*odieresis*/, 1523/*divide*/, 890/*oslash*/, 1530/*ugrave*/, 1537/*uacute*/, 1544/*ucircumflex*/, 1556/*udieresis*/, 1566/*yacute*/, 1573/*thorn*/, 1579/*ydieresis*/, }; const char * _cairo_ps_standard_encoding_to_glyphname (int glyph) { if (ps_standard_encoding_offset[glyph]) return glyph_name_symbol + ps_standard_encoding_offset[glyph]; else return NULL; } const char * _cairo_winansi_to_glyphname (int glyph) { if (winansi_encoding_offset[glyph]) return glyph_name_symbol + winansi_encoding_offset[glyph]; else return NULL; } #endif /* CAIRO_HAS_FONT_SUBSET */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type1-private.h000066400000000000000000000036611271037650300262640ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Adrian Johnson */ #ifndef CAIRO_TYPE1_PRIVATE_H #define CAIRO_TYPE1_PRIVATE_H #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET /* Magic constants for the type1 eexec encryption */ #define CAIRO_TYPE1_ENCRYPT_C1 ((unsigned short) 52845) #define CAIRO_TYPE1_ENCRYPT_C2 ((unsigned short) 22719) #define CAIRO_TYPE1_PRIVATE_DICT_KEY ((unsigned short) 55665) #define CAIRO_TYPE1_CHARSTRING_KEY ((unsigned short) 4330) #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_TYPE1_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type1-subset.c000066400000000000000000001524241271037650300261140ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg */ /* * Useful links: * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-type1-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-output-stream-private.h" #include #include #define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */ typedef struct { int subset_index; double width; const char *encrypted_charstring; int encrypted_charstring_length; } glyph_data_t; typedef struct _cairo_type1_font_subset { cairo_scaled_font_subset_t *scaled_font_subset; struct { unsigned int font_id; char *base_font; unsigned int num_glyphs; double x_min, y_min, x_max, y_max; double ascent, descent; double units_per_em; const char *data; unsigned long header_size; unsigned long data_size; unsigned long trailer_size; } base; int num_glyphs; /* The glyphs and glyph_names arrays are indexed by the order of * the Charstrings in the font. This is not necessarily the same * order as the glyph index. The index_to_glyph_name() font backend * function is used to map the glyph index to the glyph order in * the Charstrings. */ glyph_data_t *glyphs; char **glyph_names; cairo_array_t glyphs_array; cairo_array_t glyph_names_array; int num_subrs; cairo_bool_t subset_subrs; struct { const char *subr_string; int subr_length; const char *np; int np_length; cairo_bool_t used; } *subrs; /* Indexed by subset_index this maps to the glyph order in the * glyph_names and glyphs arrays. Has font->num_golyphs * elements. */ int *subset_index_to_glyphs; cairo_output_stream_t *output; cairo_array_t contents; const char *rd, *nd, *np; int lenIV; char *type1_data; unsigned int type1_length; char *type1_end; char *header_segment; int header_segment_size; char *eexec_segment; int eexec_segment_size; cairo_bool_t eexec_segment_is_ascii; char *cleartext; char *cleartext_end; int header_size; unsigned short eexec_key; cairo_bool_t hex_encode; int hex_column; struct { double stack[TYPE1_STACKSIZE]; int sp; } build_stack; struct { int stack[TYPE1_STACKSIZE]; int sp; } ps_stack; } cairo_type1_font_subset_t; static cairo_status_t _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { memset (font, 0, sizeof (*font)); font->scaled_font_subset = scaled_font_subset; _cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t)); _cairo_array_init (&font->glyph_names_array, sizeof (char *)); font->subset_index_to_glyphs = NULL; font->base.num_glyphs = 0; font->num_subrs = 0; font->subset_subrs = TRUE; font->subrs = NULL; font->hex_encode = hex_encode; font->num_glyphs = 0; _cairo_array_init (&font->contents, sizeof (char)); return CAIRO_STATUS_SUCCESS; } static void cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph) { if (font->glyphs[glyph].subset_index >= 0) return; font->glyphs[glyph].subset_index = font->num_glyphs; font->subset_index_to_glyphs[font->num_glyphs] = glyph; font->num_glyphs++; } static cairo_bool_t is_ps_delimiter(int c) { static const char delimiters[] = "()[]{}<>/% \t\r\n"; return strchr (delimiters, c) != NULL; } static const char * find_token (const char *buffer, const char *end, const char *token) { int i, length; /* FIXME: find substring really must be find_token */ if (buffer == NULL) return NULL; length = strlen (token); for (i = 0; buffer + i < end - length + 1; i++) if (memcmp (buffer + i, token, length) == 0) if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) && (buffer + i == end - length || is_ps_delimiter(buffer[i + length]))) return buffer + i; return NULL; } static cairo_status_t cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) { unsigned char *p; const char *eexec_token; int size, i; p = (unsigned char *) font->type1_data; font->type1_end = font->type1_data + font->type1_length; if (p[0] == 0x80 && p[1] == 0x01) { font->header_segment_size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); font->header_segment = (char *) p + 6; p += 6 + font->header_segment_size; font->eexec_segment_size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); font->eexec_segment = (char *) p + 6; font->eexec_segment_is_ascii = (p[1] == 1); p += 6 + font->eexec_segment_size; while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) { size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); p += 6 + size; } font->type1_end = (char *) p; } else { eexec_token = find_token ((char *) p, font->type1_end, "eexec"); if (eexec_token == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n"); font->header_segment = (char *) p; font->eexec_segment_size = font->type1_length - font->header_segment_size; font->eexec_segment = (char *) p + font->header_segment_size; font->eexec_segment_is_ascii = TRUE; for (i = 0; i < 4; i++) { if (!isxdigit(font->eexec_segment[i])) font->eexec_segment_is_ascii = FALSE; } } return CAIRO_STATUS_SUCCESS; } /* Search for the definition of key and erase it by overwriting with spaces. * This function is looks for definitions of the form: * * /key1 1234 def * /key2 [12 34 56] def * * ie a key defined as an integer or array of integers. * */ static void cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, const char *key) { const char *start, *p, *segment_end; segment_end = font->header_segment + font->header_segment_size; start = font->header_segment; do { start = find_token (start, segment_end, key); if (start) { p = start + strlen(key); /* skip integers or array of integers */ while (p < segment_end && (_cairo_isspace(*p) || _cairo_isdigit(*p) || *p == '[' || *p == ']')) { p++; } if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) { /* erase definition of the key */ memset((char *) start, ' ', p + 3 - start); } start += strlen(key); } } while (start); } static cairo_status_t cairo_type1_font_subset_get_matrix (cairo_type1_font_subset_t *font, const char *name, double *a, double *b, double *c, double *d) { const char *start, *end, *segment_end; int ret, s_max, i, j; char *s; struct lconv *locale_data; const char *decimal_point; int decimal_point_len; locale_data = localeconv (); decimal_point = locale_data->decimal_point; decimal_point_len = strlen (decimal_point); assert (decimal_point_len != 0); segment_end = font->header_segment + font->header_segment_size; start = find_token (font->header_segment, segment_end, name); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; s_max = end - start + 5*decimal_point_len + 1; s = malloc (s_max); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); i = 0; j = 0; while (i < end - start && j < s_max - decimal_point_len) { if (start[i] == '.') { strncpy(s + j, decimal_point, decimal_point_len); i++; j += decimal_point_len; } else { s[j++] = start[i++]; } } s[j] = 0; start = strpbrk (s, "{["); if (!start) { free (s); return CAIRO_INT_STATUS_UNSUPPORTED; } start++; ret = 0; if (*start) ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d); free (s); if (ret != 4) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font) { cairo_status_t status; double x_min, y_min, x_max, y_max; double xx, yx, xy, yy; status = cairo_type1_font_subset_get_matrix (font, "/FontBBox", &x_min, &y_min, &x_max, &y_max); if (unlikely (status)) return status; status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix", &xx, &yx, &xy, &yy); if (unlikely (status)) return status; if (yy == 0.0) return CAIRO_INT_STATUS_UNSUPPORTED; /* Freetype uses 1/yy to get units per EM */ font->base.units_per_em = 1.0/yy; font->base.x_min = x_min / font->base.units_per_em; font->base.y_min = y_min / font->base.units_per_em; font->base.x_max = x_max / font->base.units_per_em; font->base.y_max = y_max / font->base.units_per_em; font->base.ascent = font->base.y_max; font->base.descent = font->base.y_min; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font) { const char *start, *end, *segment_end; char *s; int i; segment_end = font->header_segment + font->header_segment_size; start = find_token (font->header_segment, segment_end, "/FontName"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; start += strlen ("/FontName"); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; s = malloc (end - start + 1); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (s, start, end - start); s[end - start] = 0; start = strchr (s, '/'); if (!start++ || !start) { free (s); return CAIRO_INT_STATUS_UNSUPPORTED; } /* If font name is prefixed with a subset tag, strip it off. */ if (strlen(start) > 7 && start[6] == '+') { for (i = 0; i < 6; i++) { if (start[i] < 'A' || start[i] > 'Z') break; } if (i == 6) start += 7; } font->base.base_font = strdup (start); free (s); if (unlikely (font->base.base_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); s = font->base.base_font; while (*s && !is_ps_delimiter(*s)) s++; *s = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, const char *name) { const char *start, *end, *segment_end; unsigned int i; /* FIXME: * This function assumes that /FontName always appears * before /Encoding. This appears to always be the case with Type1 * fonts. * * The more recently added code for removing the UniqueID and XUID * keys can not make any assumptions about the position of the * keys in the dictionary so it is implemented by overwriting the * key definition with spaces before we start copying the font to * the output. * * This code should be rewritten to not make any assumptions about * the order of dictionary keys. This will allow UniqueID to be * stripped out instead of leaving a bunch of spaces in the * output. */ cairo_type1_font_erase_dict_key (font, "/UniqueID"); cairo_type1_font_erase_dict_key (font, "/XUID"); segment_end = font->header_segment + font->header_segment_size; /* Type 1 fonts created by Fontforge have some PostScript code at * the start of the font that skips the font if the printer has a * cached copy of the font with the same unique id. This breaks * our subsetted font so we disable it by searching for the * PostScript operator "known" when used to check for the * "/UniqueID" dictionary key. We append " pop false " after it to * pop the result of this check off the stack and replace it with * "false" to make the PostScript code think "/UniqueID" does not * exist. */ end = font->header_segment; start = find_token (font->header_segment, segment_end, "/UniqueID"); if (start) { start += 9; while (start < segment_end && _cairo_isspace (*start)) start++; if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { _cairo_output_stream_write (font->output, font->header_segment, start + 5 - font->header_segment); _cairo_output_stream_printf (font->output, " pop false "); end = start + 5; } } start = find_token (end, segment_end, "/FontName"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, start - end); _cairo_output_stream_printf (font->output, "/FontName /%s def", name); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end += 3; start = find_token (end, segment_end, "/Encoding"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, start - end); _cairo_output_stream_printf (font->output, "/Encoding 256 array\n" "0 1 255 {1 index exch /.notdef put} for\n"); if (font->scaled_font_subset->is_latin) { for (i = 1; i < 256; i++) { int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i]; if (subset_glyph > 0) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, _cairo_winansi_to_glyphname (i)); } } } else { for (i = 0; i < font->base.num_glyphs; i++) { if (font->glyphs[i].subset_index <= 0) continue; _cairo_output_stream_printf (font->output, "dup %d /%s put\n", font->glyphs[i].subset_index, font->glyph_names[i]); } } _cairo_output_stream_printf (font->output, "readonly def"); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end += 3; /* There are some buggy fonts that contain more than one /Encoding */ if (find_token (end, segment_end, "/Encoding")) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, segment_end - end); return font->output->status; } static int hex_to_int (int ch) { if (ch <= '9') return ch - '0'; else if (ch <= 'F') return ch - 'A' + 10; else return ch - 'a' + 10; } static cairo_status_t cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font, const char *data, unsigned int length) { const unsigned char *in, *end; int c, p; static const char hex_digits[16] = "0123456789abcdef"; char digits[3]; in = (const unsigned char *) data; end = (const unsigned char *) data + length; while (in < end) { p = *in++; c = p ^ (font->eexec_key >> 8); font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; if (font->hex_encode) { digits[0] = hex_digits[c >> 4]; digits[1] = hex_digits[c & 0x0f]; digits[2] = '\n'; font->hex_column += 2; if (font->hex_column == 78) { _cairo_output_stream_write (font->output, digits, 3); font->hex_column = 0; } else { _cairo_output_stream_write (font->output, digits, 2); } } else { digits[0] = c; _cairo_output_stream_write (font->output, digits, 1); } } return font->output->status; } static cairo_status_t cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) { unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY; unsigned char *in, *end; char *out; int c, p; int i; in = (unsigned char *) font->eexec_segment; end = (unsigned char *) in + font->eexec_segment_size; font->cleartext = malloc (font->eexec_segment_size + 1); if (unlikely (font->cleartext == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); out = font->cleartext; while (in < end) { if (font->eexec_segment_is_ascii) { c = *in++; if (_cairo_isspace (c)) continue; c = (hex_to_int (c) << 4) | hex_to_int (*in++); } else { c = *in++; } p = c ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *out++ = p; } font->cleartext_end = out; /* Overwrite random bytes with spaces. * * The first 4 bytes of the cleartext are the random bytes * required by the encryption algorithm. When encrypting the * cleartext, the first ciphertext byte must not be a white space * character and the first 4 bytes must not be an ASCII Hex * character. Some fonts do not check that their randomly chosen * bytes results in ciphertext that complies with this * restriction. This may cause problems for some PDF consumers. By * replacing the random bytes with spaces, the first four bytes of * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies * with this restriction. Using spaces also means we don't have to * skip over the random bytes when parsing the cleartext. */ for (i = 0; i < 4 && i < font->eexec_segment_size; i++) font->cleartext[i] = ' '; /* Ensure strtol() can not scan past the end of the cleartext */ font->cleartext[font->eexec_segment_size] = 0; return CAIRO_STATUS_SUCCESS; } static const char * skip_token (const char *p, const char *end) { while (p < end && _cairo_isspace(*p)) p++; while (p < end && !_cairo_isspace(*p)) p++; if (p == end) return NULL; return p; } static void cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) { unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY; int c, p, i; for (i = 0; i < size; i++) { c = *in++; p = c ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *out++ = p; } } static const unsigned char * cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) { if (*p <= 246) { *integer = *p++ - 139; } else if (*p <= 250) { *integer = (p[0] - 247) * 256 + p[1] + 108; p += 2; } else if (*p <= 254) { *integer = -(p[0] - 251) * 256 - p[1] - 108; p += 2; } else { *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; p += 5; } return p; } static cairo_status_t use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index) { const char *glyph_name; unsigned int i; if (index < 0 || index > 255) return CAIRO_STATUS_SUCCESS; glyph_name = _cairo_ps_standard_encoding_to_glyphname (index); if (glyph_name == NULL) return CAIRO_STATUS_SUCCESS; for (i = 0; i < font->base.num_glyphs; i++) { if (font->glyph_names[i] && strcmp (font->glyph_names[i], glyph_name) == 0) { cairo_type1_font_subset_use_glyph (font, i); return CAIRO_STATUS_SUCCESS; } } return CAIRO_INT_STATUS_UNSUPPORTED; } #define TYPE1_CHARSTRING_COMMAND_HSTEM 0x01 #define TYPE1_CHARSTRING_COMMAND_VSTEM 0x03 #define TYPE1_CHARSTRING_COMMAND_VMOVETO 0x04 #define TYPE1_CHARSTRING_COMMAND_RLINETO 0x05 #define TYPE1_CHARSTRING_COMMAND_HLINETO 0x06 #define TYPE1_CHARSTRING_COMMAND_VLINETO 0x07 #define TYPE1_CHARSTRING_COMMAND_RRCURVETO 0x08 #define TYPE1_CHARSTRING_COMMAND_CLOSEPATH 0x09 #define TYPE1_CHARSTRING_COMMAND_CALLSUBR 0x0a #define TYPE1_CHARSTRING_COMMAND_RETURN 0x0b #define TYPE1_CHARSTRING_COMMAND_ESCAPE 0x0c #define TYPE1_CHARSTRING_COMMAND_HSBW 0x0d #define TYPE1_CHARSTRING_COMMAND_ENDCHAR 0x0e #define TYPE1_CHARSTRING_COMMAND_RMOVETO 0x15 #define TYPE1_CHARSTRING_COMMAND_HMOVETO 0x16 #define TYPE1_CHARSTRING_COMMAND_VHCURVETO 0x1e #define TYPE1_CHARSTRING_COMMAND_HVCURVETO 0x1f #define TYPE1_CHARSTRING_COMMAND_DOTSECTION 0x0c00 #define TYPE1_CHARSTRING_COMMAND_VSTEM3 0x0c01 #define TYPE1_CHARSTRING_COMMAND_HSTEM3 0x0c02 #define TYPE1_CHARSTRING_COMMAND_SEAC 0x0c06 #define TYPE1_CHARSTRING_COMMAND_SBW 0x0c07 #define TYPE1_CHARSTRING_COMMAND_DIV 0x0c0c #define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR 0x0c10 #define TYPE1_CHARSTRING_COMMAND_POP 0x0c11 #define TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT 0x0c21 /* Parse the charstring, including recursing into subroutines. Find * the glyph width, subroutines called, and glyphs required by the * SEAC operator. */ static cairo_status_t cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font, int glyph, const char *encrypted_charstring, int encrypted_charstring_length) { cairo_status_t status; unsigned char *charstring; const unsigned char *end; const unsigned char *p; int command; charstring = malloc (encrypted_charstring_length); if (unlikely (charstring == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) encrypted_charstring, encrypted_charstring_length, charstring); end = charstring + encrypted_charstring_length; p = charstring + font->lenIV; status = CAIRO_STATUS_SUCCESS; while (p < end) { if (*p < 32) { command = *p++; switch (command) { case TYPE1_CHARSTRING_COMMAND_HSTEM: case TYPE1_CHARSTRING_COMMAND_VSTEM: case TYPE1_CHARSTRING_COMMAND_VMOVETO: case TYPE1_CHARSTRING_COMMAND_RLINETO: case TYPE1_CHARSTRING_COMMAND_HLINETO: case TYPE1_CHARSTRING_COMMAND_VLINETO: case TYPE1_CHARSTRING_COMMAND_RRCURVETO: case TYPE1_CHARSTRING_COMMAND_CLOSEPATH: case TYPE1_CHARSTRING_COMMAND_RMOVETO: case TYPE1_CHARSTRING_COMMAND_HMOVETO: case TYPE1_CHARSTRING_COMMAND_VHCURVETO: case TYPE1_CHARSTRING_COMMAND_HVCURVETO: case TYPE1_CHARSTRING_COMMAND_RETURN: case TYPE1_CHARSTRING_COMMAND_ENDCHAR: default: /* stack clearing operator */ font->build_stack.sp = 0; break; case TYPE1_CHARSTRING_COMMAND_CALLSUBR: if (font->subset_subrs && font->build_stack.sp > 0) { double int_val; if (modf(font->build_stack.stack[--font->build_stack.sp], &int_val) == 0.0) { int subr_num = int_val; if (subr_num >= 0 && subr_num < font->num_subrs) { font->subrs[subr_num].used = TRUE; status = cairo_type1_font_subset_parse_charstring ( font, glyph, font->subrs[subr_num].subr_string, font->subrs[subr_num].subr_length); break; } } } font->subset_subrs = FALSE; break; case TYPE1_CHARSTRING_COMMAND_HSBW: if (font->build_stack.sp < 2) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em; font->build_stack.sp = 0; break; case TYPE1_CHARSTRING_COMMAND_ESCAPE: command = command << 8 | *p++; switch (command) { case TYPE1_CHARSTRING_COMMAND_DOTSECTION: case TYPE1_CHARSTRING_COMMAND_VSTEM3: case TYPE1_CHARSTRING_COMMAND_HSTEM3: case TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT: default: /* stack clearing operator */ font->build_stack.sp = 0; break; case TYPE1_CHARSTRING_COMMAND_SEAC: /* The seac command takes five integer arguments. The * last two are glyph indices into the PS standard * encoding give the names of the glyphs that this * glyph is composed from. All we need to do is to * make sure those glyphs are present in the subset * under their standard names. */ if (font->build_stack.sp < 5) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } status = use_standard_encoding_glyph (font, font->build_stack.stack[3]); if (unlikely (status)) goto cleanup; status = use_standard_encoding_glyph (font, font->build_stack.stack[4]); if (unlikely (status)) goto cleanup; font->build_stack.sp = 0; break; case TYPE1_CHARSTRING_COMMAND_SBW: if (font->build_stack.sp < 4) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em; font->build_stack.sp = 0; break; case TYPE1_CHARSTRING_COMMAND_DIV: if (font->build_stack.sp < 2) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } else { double num1 = font->build_stack.stack[font->build_stack.sp - 2]; double num2 = font->build_stack.stack[font->build_stack.sp - 1]; font->build_stack.sp--; if (num2 == 0.0) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } font->build_stack.stack[font->build_stack.sp - 1] = num1/num2; } break; case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR: if (font->build_stack.sp < 1) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } font->build_stack.sp--; font->ps_stack.sp = 0; while (font->build_stack.sp) font->ps_stack.stack[font->ps_stack.sp++] = font->build_stack.stack[--font->build_stack.sp]; break; case TYPE1_CHARSTRING_COMMAND_POP: if (font->ps_stack.sp < 1) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } /* T1 spec states that if the interpreter does not * support executing the callothersub, the results * must be taken from the callothersub arguments. */ font->build_stack.stack[font->build_stack.sp++] = font->ps_stack.stack[--font->ps_stack.sp]; break; } break; } } else { /* integer argument */ if (font->build_stack.sp < TYPE1_STACKSIZE) { int val; p = cairo_type1_font_subset_decode_integer (p, &val); font->build_stack.stack[font->build_stack.sp++] = val; } else { status = CAIRO_INT_STATUS_UNSUPPORTED; goto cleanup; } } } cleanup: free (charstring); return status; } static cairo_status_t cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font, int subr_number, const char *encrypted_charstring, int encrypted_charstring_length, const char *np, int np_length) { font->subrs[subr_number].subr_string = encrypted_charstring; font->subrs[subr_number].subr_length = encrypted_charstring_length; font->subrs[subr_number].np = np; font->subrs[subr_number].np_length = np_length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t write_used_subrs (cairo_type1_font_subset_t *font, int subr_number, const char *subr_string, int subr_string_length, const char *np, int np_length) { cairo_status_t status; char buffer[256]; int length; if (!font->subrs[subr_number].used) return CAIRO_STATUS_SUCCESS; length = snprintf (buffer, sizeof buffer, "dup %d %d %s ", subr_number, subr_string_length, font->rd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_encrypted (font, subr_string, subr_string_length); if (unlikely (status)) return status; if (np) { status = cairo_type1_font_subset_write_encrypted (font, np, np_length); } else { length = snprintf (buffer, sizeof buffer, "%s\n", font->np); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); } if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font, int subr_number, const char *subr_string, int subr_string_length, const char *np, int np_length); static cairo_status_t cairo_type1_font_for_each_subr (cairo_type1_font_subset_t *font, const char *array_start, const char *cleartext_end, subr_func_t func, const char **array_end) { const char *p, *subr_string; char *end; int subr_num, subr_length; const char *np; int np_length; cairo_status_t status; /* We're looking at "dup" at the start of the first subroutine. The subroutines * definitions are on the form: * * dup 5 23 RD <23 binary bytes> NP * * or alternatively using -| and |- instead of RD and ND. * The first number is the subroutine number. */ p = array_start; while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) { p = skip_token (p, cleartext_end); /* get subr number */ subr_num = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; if (subr_num < 0 || subr_num >= font->num_subrs) return CAIRO_INT_STATUS_UNSUPPORTED; /* get subr length */ p = end; subr_length = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Skip past -| or RD to binary data. There is exactly one space * between the -| or RD token and the encrypted data, thus '+ 1'. */ subr_string = skip_token (end, cleartext_end) + 1; np = NULL; np_length = 0; /* Skip binary data and | or NP token. */ p = skip_token (subr_string + subr_length, cleartext_end); while (p < cleartext_end && _cairo_isspace(*p)) p++; /* Some fonts have "noaccess put" instead of "NP" */ if (p + 3 < cleartext_end && strncmp (p, "put", 3) == 0) { p = skip_token (p, cleartext_end); while (p < cleartext_end && _cairo_isspace(*p)) p++; np = subr_string + subr_length; np_length = p - np; } status = func (font, subr_num, subr_string, subr_length, np, np_length); if (unlikely (status)) return status; } *array_end = (char *) p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *encrypted_charstring, int encrypted_charstring_length) { char *s; glyph_data_t glyph; cairo_status_t status; s = malloc (name_length + 1); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (s, name, name_length); s[name_length] = 0; status = _cairo_array_append (&font->glyph_names_array, &s); if (unlikely (status)) return status; glyph.subset_index = -1; glyph.width = 0; glyph.encrypted_charstring = encrypted_charstring; glyph.encrypted_charstring_length = encrypted_charstring_length; status = _cairo_array_append (&font->glyphs_array, &glyph); return status; } static cairo_status_t write_used_glyphs (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length) { cairo_status_t status; char buffer[256]; int length; int subset_id; int ch; const char *wa_name; if (font->glyphs[glyph_number].subset_index < 0) return CAIRO_STATUS_SUCCESS; if (font->scaled_font_subset->is_latin) { /* When using the WinAnsi encoding in PDF, the /Encoding array * is ignored and instead glyphs are keyed by glyph names. To * ensure correct rendering we replace the glyph name in the * font with the standard name. **/ subset_id = font->glyphs[glyph_number].subset_index; if (subset_id > 0) { ch = font->scaled_font_subset->to_latin_char[subset_id]; wa_name = _cairo_winansi_to_glyphname (ch); /* If this subset contains any seac glyphs, additional non * winansi glyphs (wa_name = NULL) may be included in the * subset. In this case the original name is used. */ if (wa_name) { name = wa_name; name_length = strlen(name); } } } length = snprintf (buffer, sizeof buffer, "/%.*s %d %s ", name_length, name, charstring_length, font->rd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_encrypted (font, charstring, charstring_length); if (unlikely (status)) return status; length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length); static cairo_status_t cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, const char *dict_start, const char *dict_end, glyph_func_t func, const char **dict_out) { int charstring_length, name_length; const char *p, *charstring, *name; char *end; cairo_status_t status; int glyph_count; /* We're looking at '/' in the name of the first glyph. The glyph * definitions are on the form: * * /name 23 RD <23 binary bytes> ND * * or alternatively using -| and |- instead of RD and ND. * * We parse the glyph name and see if it is in the subset. If it * is, we call the specified callback with the glyph name and * glyph data, otherwise we just skip it. We need to parse * through a glyph definition; we can't just find the next '/', * since the binary data could contain a '/'. */ p = dict_start; glyph_count = 0; while (*p == '/') { name = p + 1; p = skip_token (p, dict_end); name_length = p - name; charstring_length = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Skip past -| or RD to binary data. There is exactly one space * between the -| or RD token and the encrypted data, thus '+ 1'. */ charstring = skip_token (end, dict_end) + 1; /* Skip binary data and |- or ND token. */ p = skip_token (charstring + charstring_length, dict_end); while (p < dict_end && _cairo_isspace(*p)) p++; /* In case any of the skip_token() calls above reached EOF, p will * be equal to dict_end. */ if (p == dict_end) return CAIRO_INT_STATUS_UNSUPPORTED; status = func (font, glyph_count++, name, name_length, charstring, charstring_length); if (unlikely (status)) return status; } *dict_out = p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, const char *name) { cairo_status_t status; const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end; const char *lenIV_start, *lenIV_end, *closefile_token; char buffer[32], *lenIV_str, *subr_count_end, *glyph_count_end; int ret, lenIV, length; const cairo_scaled_font_backend_t *backend; unsigned int i; int glyph, j; /* The private dict holds hint information, common subroutines and * the actual glyph definitions (charstrings). * * What we do here is scan directly to the /Subrs token, which * marks the beginning of the subroutines. We read in all the * subroutines, then move on to the /CharString token, which marks * the beginning of the glyph definitions, and read in the charstrings. * * The charstrings are parsed to extract glyph widths, work out * which subroutines are called, and to see if any extra glyphs * need to be included due to the use of the seac glyph combining * operator. * * Finally, the private dict is copied to the subset font minus the * subroutines and charstrings not required. */ /* Determine lenIV, the number of random characters at the start of each encrypted charstring. The default is 4, but this can be overridden in the private dict. */ font->lenIV = 4; if ((lenIV_start = find_token (font->cleartext, font->cleartext_end, "/lenIV")) != NULL) { lenIV_start += 6; lenIV_end = find_token (lenIV_start, font->cleartext_end, "def"); if (lenIV_end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; lenIV_str = malloc (lenIV_end - lenIV_start + 1); if (unlikely (lenIV_str == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (lenIV_str, lenIV_start, lenIV_end - lenIV_start); lenIV_str[lenIV_end - lenIV_start] = 0; ret = sscanf(lenIV_str, "%d", &lenIV); free(lenIV_str); if (unlikely (ret <= 0)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Apparently some fonts signal unencrypted charstrings with a negative lenIV, though this is not part of the Type 1 Font Format specification. See, e.g. http://lists.gnu.org/archive/html/freetype-devel/2000-06/msg00064.html. */ if (unlikely (lenIV < 0)) return CAIRO_INT_STATUS_UNSUPPORTED; font->lenIV = lenIV; } /* Find start of Subrs */ subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs"); if (subrs == NULL) { font->subset_subrs = FALSE; p = font->cleartext; array_start = NULL; goto skip_subrs; } /* Scan past /Subrs and get the array size. */ p = subrs + strlen ("/Subrs"); font->num_subrs = strtol (p, &subr_count_end, 10); if (subr_count_end == p) return CAIRO_INT_STATUS_UNSUPPORTED; if (font->num_subrs <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0])); if (unlikely (font->subrs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* look for "dup" which marks the beginning of the first subr */ array_start = find_token (subr_count_end, font->cleartext_end, "dup"); if (subrs == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* Read in the subroutines */ status = cairo_type1_font_for_each_subr (font, array_start, font->cleartext_end, cairo_type1_font_subset_build_subr_list, &array_end); if (unlikely(status)) return status; p = array_end; skip_subrs: /* Find start of CharStrings */ charstrings = find_token (p, font->cleartext_end, "/CharStrings"); if (charstrings == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* Scan past /CharStrings and the integer following it. */ p = charstrings + strlen ("/CharStrings"); strtol (p, &glyph_count_end, 10); if (p == glyph_count_end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Look for a '/' which marks the beginning of the first glyph * definition. */ for (p = glyph_count_end; p < font->cleartext_end; p++) if (*p == '/') break; if (p == font->cleartext_end) return CAIRO_INT_STATUS_UNSUPPORTED; dict_start = p; /* Now that we have the private dictionary broken down in * sections, do the first pass through the glyph definitions to * build a list of glyph names and charstrings. */ status = cairo_type1_font_subset_for_each_glyph (font, dict_start, font->cleartext_end, cairo_type1_font_subset_build_glyph_list, &dict_end); if (unlikely(status)) return status; font->glyphs = _cairo_array_index (&font->glyphs_array, 0); font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0); font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array); font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]); if (unlikely (font->subset_index_to_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); backend = font->scaled_font_subset->scaled_font->backend; if (!backend->index_to_glyph_name) return CAIRO_INT_STATUS_UNSUPPORTED; /* Find the glyph number corresponding to each glyph in the subset * and mark it as in use */ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { unsigned long index; status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font, font->glyph_names, font->base.num_glyphs, font->scaled_font_subset->glyphs[i], &index); if (unlikely(status)) return status; cairo_type1_font_subset_use_glyph (font, index); } /* Go through the charstring of each glyph in use, get the glyph * width and figure out which extra glyphs may be required by the * seac operator (which may cause font->num_glyphs to increase * while this loop is executing). Also subset the Subrs. */ for (j = 0; j < font->num_glyphs; j++) { glyph = font->subset_index_to_glyphs[j]; font->build_stack.sp = 0; font->ps_stack.sp = 0; status = cairo_type1_font_subset_parse_charstring (font, glyph, font->glyphs[glyph].encrypted_charstring, font->glyphs[glyph].encrypted_charstring_length); if (unlikely (status)) return status; } /* Always include the first five subroutines in case the Flex/hint mechanism is * being used. */ for (j = 0; j < MIN (font->num_subrs, 5); j++) { font->subrs[j].used = TRUE; } closefile_token = find_token (dict_end, font->cleartext_end, "closefile"); if (closefile_token == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* We're ready to start outputting. First write the header, * i.e. the public part of the font dict.*/ status = cairo_type1_font_subset_write_header (font, name); if (unlikely (status)) return status; font->base.header_size = _cairo_output_stream_get_position (font->output); /* Start outputting the private dict */ if (font->subset_subrs) { /* First output everything up to the start of the Subrs array. */ status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, array_start - font->cleartext); if (unlikely (status)) return status; /* Write out the subr definitions for each of the glyphs in * the subset. */ status = cairo_type1_font_for_each_subr (font, array_start, font->cleartext_end, write_used_subrs, &p); if (unlikely (status)) return status; } else { p = font->cleartext; } /* If subr subsetting, output everything from end of subrs to * start of /CharStrings token. If not subr subsetting, output * everything start of private dict to start of /CharStrings * token. */ status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p); if (unlikely (status)) return status; /* Write out new charstring count */ length = snprintf (buffer, sizeof buffer, "/CharStrings %d", font->num_glyphs); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; /* Write out text between the charstring count and the first * charstring definition */ status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, dict_start - glyph_count_end); if (unlikely (status)) return status; /* Write out the charstring definitions for each of the glyphs in * the subset. */ status = cairo_type1_font_subset_for_each_glyph (font, dict_start, font->cleartext_end, write_used_glyphs, &p); if (unlikely (status)) return status; /* Output what's left between the end of the glyph definitions and * the end of the private dict to the output. */ status = cairo_type1_font_subset_write_encrypted (font, p, closefile_token - p + strlen ("closefile") + 1); if (unlikely (status)) return status; if (font->hex_encode) _cairo_output_stream_write (font->output, "\n", 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) { const char *cleartomark_token; int i; static const char zeros[65] = "0000000000000000000000000000000000000000000000000000000000000000\n"; for (i = 0; i < 8; i++) _cairo_output_stream_write (font->output, zeros, sizeof zeros); cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); if (cleartomark_token) { /* Some fonts have conditional save/restore around the entire * font dict, so we need to retain whatever postscript code * that may come after 'cleartomark'. */ _cairo_output_stream_write (font->output, cleartomark_token, font->type1_end - cleartomark_token); if (*(font->type1_end - 1) != '\n') _cairo_output_stream_printf (font->output, "\n"); } else if (!font->eexec_segment_is_ascii) { /* Fonts embedded in PDF may omit the fixed-content portion * that includes the 'cleartomark' operator. Type 1 in PDF is * always binary. */ _cairo_output_stream_printf (font->output, "cleartomark\n"); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } /* some fonts do not have a newline at the end of the last line */ _cairo_output_stream_printf (font->output, "\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t type1_font_write (void *closure, const unsigned char *data, unsigned int length) { cairo_type1_font_subset_t *font = closure; return _cairo_array_append_multiple (&font->contents, data, length); } static cairo_status_t cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, const char *name) { cairo_status_t status; status = cairo_type1_font_subset_find_segments (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_decrypt_eexec_segment (font); if (unlikely (status)) return status; /* Determine which glyph definition delimiters to use. */ if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) { font->rd = "-|"; font->nd = "|-"; font->np = "|"; } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) { font->rd = "RD"; font->nd = "ND"; font->np = "NP"; } else { /* Don't know *what* kind of font this is... */ return CAIRO_INT_STATUS_UNSUPPORTED; } font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; font->hex_column = 0; status = cairo_type1_font_subset_get_bbox (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_get_fontname (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_private_dict (font, name); if (unlikely (status)) return status; font->base.data_size = _cairo_output_stream_get_position (font->output) - font->base.header_size; status = cairo_type1_font_subset_write_trailer (font); if (unlikely (status)) return status; font->base.trailer_size = _cairo_output_stream_get_position (font->output) - font->base.header_size - font->base.data_size; return CAIRO_STATUS_SUCCESS; } static cairo_bool_t check_fontdata_is_type1 (const unsigned char *data, long length) { /* Test for Type 1 Binary (PFB) */ if (length > 2 && data[0] == 0x80 && data[1] == 0x01) return TRUE; /* Test for Type 1 1 ASCII (PFA) */ if (length > 2 && data[0] == '%' && data[1] == '!') return TRUE; return FALSE; } static cairo_status_t cairo_type1_font_subset_generate (void *abstract_font, const char *name) { cairo_type1_font_subset_t *font = abstract_font; cairo_scaled_font_t *scaled_font; cairo_status_t status; unsigned long data_length; scaled_font = font->scaled_font_subset->scaled_font; if (!scaled_font->backend->load_type1_data) return CAIRO_INT_STATUS_UNSUPPORTED; status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length); if (status) return CAIRO_INT_STATUS_UNSUPPORTED; font->type1_length = data_length; font->type1_data = malloc (font->type1_length); if (unlikely (font->type1_data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = scaled_font->backend->load_type1_data (scaled_font, 0, (unsigned char *) font->type1_data, &data_length); if (unlikely (status)) return status; if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_array_grow_by (&font->contents, 4096); if (unlikely (status)) return status; font->output = _cairo_output_stream_create (type1_font_write, NULL, font); if (unlikely ((status = font->output->status))) return status; status = cairo_type1_font_subset_write (font, name); if (unlikely (status)) return status; font->base.data = _cairo_array_index (&font->contents, 0); return status; } static cairo_status_t _cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; unsigned int i; /* If the subset generation failed, some of the pointers below may * be NULL depending on at which point the error occurred. */ _cairo_array_fini (&font->contents); free (font->type1_data); for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) { char **s; s = _cairo_array_index (&font->glyph_names_array, i); free (*s); } _cairo_array_fini (&font->glyph_names_array); _cairo_array_fini (&font->glyphs_array); free (font->subrs); if (font->output != NULL) status = _cairo_output_stream_destroy (font->output); free (font->base.base_font); free (font->subset_index_to_glyphs); free (font->cleartext); return status; } cairo_status_t _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { cairo_type1_font_subset_t font; cairo_status_t status; unsigned long length; unsigned int i; char buf[30]; /* We need to use a fallback font generated from the synthesized outlines. */ if (scaled_font_subset->scaled_font->backend->is_synthetic && scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode); if (unlikely (status)) return status; status = cairo_type1_font_subset_generate (&font, name); if (unlikely (status)) goto fail1; if (font.base.base_font) { type1_subset->base_font = strdup (font.base.base_font); } else { snprintf(buf, sizeof (buf), "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); type1_subset->base_font = strdup (buf); } if (unlikely (type1_subset->base_font == NULL)) goto fail1; type1_subset->widths = calloc (sizeof (double), font.num_glyphs); if (unlikely (type1_subset->widths == NULL)) goto fail2; for (i = 0; i < font.base.num_glyphs; i++) { if (font.glyphs[i].subset_index < 0) continue; type1_subset->widths[font.glyphs[i].subset_index] = font.glyphs[i].width; } type1_subset->x_min = font.base.x_min; type1_subset->y_min = font.base.y_min; type1_subset->x_max = font.base.x_max; type1_subset->y_max = font.base.y_max; type1_subset->ascent = font.base.ascent; type1_subset->descent = font.base.descent; length = font.base.header_size + font.base.data_size + font.base.trailer_size; type1_subset->data = malloc (length); if (unlikely (type1_subset->data == NULL)) goto fail3; memcpy (type1_subset->data, _cairo_array_index (&font.contents, 0), length); type1_subset->header_length = font.base.header_size; type1_subset->data_length = font.base.data_size; type1_subset->trailer_length = font.base.trailer_size; return _cairo_type1_font_subset_fini (&font); fail3: free (type1_subset->widths); fail2: free (type1_subset->base_font); fail1: _cairo_type1_font_subset_fini (&font); return status; } void _cairo_type1_subset_fini (cairo_type1_subset_t *subset) { free (subset->base_font); free (subset->widths); free (subset->data); } cairo_bool_t _cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) { cairo_status_t status; unsigned long length; unsigned char buf[64]; if (!scaled_font->backend->load_type1_data) return FALSE; status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length); if (status) return FALSE; /* We only need a few bytes to test for Type 1 */ if (length > sizeof (buf)) length = sizeof (buf); status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length); if (status) return FALSE; return check_fontdata_is_type1 (buf, length); } #endif /* CAIRO_HAS_FONT_SUBSET */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type3-glyph-surface-private.h000066400000000000000000000063171271037650300310360ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson */ #ifndef CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H #define CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-surface-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" typedef cairo_int_status_t (*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image, cairo_output_stream_t *stream); typedef struct cairo_type3_glyph_surface { cairo_surface_t base; cairo_scaled_font_t *scaled_font; cairo_output_stream_t *stream; cairo_pdf_operators_t pdf_operators; cairo_matrix_t cairo_to_pdf; cairo_type3_glyph_surface_emit_image_t emit_image; cairo_surface_clipper_t clipper; } cairo_type3_glyph_surface_t; cairo_private cairo_surface_t * _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, cairo_scaled_font_subsets_t *font_subsets); cairo_private void _cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, cairo_pdf_operators_use_font_subset_t use_font_subset, void *closure); cairo_private cairo_status_t _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, unsigned long glyph_index); cairo_private cairo_status_t _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, cairo_output_stream_t *stream, unsigned long glyph_index, cairo_box_t *bbox, double *width); #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-type3-glyph-surface.c000066400000000000000000000430071271037650300273560ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson */ #include "cairoint.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-type3-glyph-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-clipper-private.h" static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; static cairo_status_t _cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper, cairo_type3_glyph_surface_t, clipper); if (path == NULL) { _cairo_output_stream_printf (surface->stream, "Q q\n"); return CAIRO_STATUS_SUCCESS; } return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); } cairo_surface_t * _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, cairo_scaled_font_subsets_t *font_subsets) { cairo_type3_glyph_surface_t *surface; cairo_matrix_t invert_y_axis; if (unlikely (stream != NULL && stream->status)) return _cairo_surface_create_in_error (stream->status); surface = malloc (sizeof (cairo_type3_glyph_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_type3_glyph_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); surface->scaled_font = scaled_font; surface->stream = stream; surface->emit_image = emit_image; /* Setup the transform from the user-font device space to Type 3 * font space. The Type 3 font space is defined by the FontMatrix * entry in the Type 3 dictionary. In the PDF backend this is an * identity matrix. */ surface->cairo_to_pdf = scaled_font->scale_inverse; cairo_matrix_init_scale (&invert_y_axis, 1, -1); cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis); _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, &surface->cairo_to_pdf, font_subsets); _cairo_surface_clipper_init (&surface->clipper, _cairo_type3_glyph_surface_clipper_intersect_clip_path); return &surface->base; } static cairo_status_t _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, cairo_image_surface_t *image, cairo_matrix_t *image_matrix) { cairo_status_t status; /* The only image type supported by Type 3 fonts are 1-bit masks */ image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1); status = image->base.status; if (unlikely (status)) return status; _cairo_output_stream_printf (surface->stream, "q %f %f %f %f %f %f cm\n", image_matrix->xx, image_matrix->xy, image_matrix->yx, image_matrix->yy, image_matrix->x0, image_matrix->y0); status = surface->emit_image (image, surface->stream); cairo_surface_destroy (&image->base); _cairo_output_stream_printf (surface->stream, "Q\n"); return status; } static cairo_status_t _cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface, cairo_image_surface_t *image, const cairo_matrix_t *pattern_matrix) { cairo_matrix_t mat, upside_down; cairo_status_t status; if (image->width == 0 || image->height == 0) return CAIRO_STATUS_SUCCESS; mat = *pattern_matrix; /* Get the pattern space to user space matrix */ status = cairo_matrix_invert (&mat); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); /* Make this a pattern space to Type 3 font space matrix */ cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf); /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by * 1 image upside down to convert to flip the Y-axis going from * cairo to PDF. Then scale the image up to the required size. */ cairo_matrix_scale (&mat, image->width, image->height); cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1); cairo_matrix_multiply (&mat, &upside_down, &mat); return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); } static cairo_status_t _cairo_type3_glyph_surface_finish (void *abstract_surface) { cairo_type3_glyph_surface_t *surface = abstract_surface; return _cairo_pdf_operators_fini (&surface->pdf_operators); } static cairo_int_status_t _cairo_type3_glyph_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; const cairo_surface_pattern_t *pattern; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_IMAGE_FALLBACK; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; pattern = (const cairo_surface_pattern_t *) source; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (unlikely (status)) goto fail; status = _cairo_type3_glyph_surface_emit_image_pattern (surface, image, &pattern->base.matrix); fail: _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } static cairo_int_status_t _cairo_type3_glyph_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { return _cairo_type3_glyph_surface_paint (abstract_surface, op, mask, clip); } static cairo_int_status_t _cairo_type3_glyph_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; return _cairo_pdf_operators_stroke (&surface->pdf_operators, path, style, ctm, ctm_inverse); } static cairo_int_status_t _cairo_type3_glyph_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; return _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); } static cairo_int_status_t _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_scaled_font_t *font; cairo_matrix_t new_ctm, invert_y_axis; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; cairo_matrix_init_scale (&invert_y_axis, 1, -1); cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm); cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm); font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &new_ctm, &scaled_font->options); if (unlikely (font->status)) return font->status; status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, NULL, 0, glyphs, num_glyphs, NULL, 0, FALSE, font); cairo_scaled_font_destroy (font); return status; } static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, _cairo_type3_glyph_surface_finish, _cairo_default_context_create, /* XXX usable through a context? */ NULL, /* create similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* copy page */ NULL, /* show page */ NULL, /* _cairo_type3_glyph_surface_get_extents */ NULL, /* _cairo_type3_glyph_surface_get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_type3_glyph_surface_paint, _cairo_type3_glyph_surface_mask, _cairo_type3_glyph_surface_stroke, _cairo_type3_glyph_surface_fill, NULL, /* fill-stroke */ _cairo_type3_glyph_surface_show_glyphs, }; static void _cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface, cairo_output_stream_t *stream) { surface->stream = stream; _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream); } static cairo_status_t _cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface, unsigned long glyph_index) { cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; cairo_image_surface_t *image; cairo_matrix_t mat; double x, y; status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) return status; image = scaled_glyph->surface; if (image->width == 0 || image->height == 0) return CAIRO_STATUS_SUCCESS; x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); mat.xx = image->width; mat.xy = 0; mat.yx = 0; mat.yy = image->height; mat.x0 = x; mat.y0 = y; cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); mat.y0 *= -1; return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); } void _cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, cairo_pdf_operators_use_font_subset_t use_font_subset, void *closure) { cairo_type3_glyph_surface_t *surface = abstract_surface; if (unlikely (surface->base.status)) return; _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, use_font_subset, closure); } cairo_status_t _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, unsigned long glyph_index) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_scaled_glyph_t *scaled_glyph; cairo_int_status_t status, status2; cairo_output_stream_t *null_stream; if (unlikely (surface->base.status)) return surface->base.status; null_stream = _cairo_null_stream_create (); if (unlikely (null_stream->status)) return null_stream->status; _cairo_type3_glyph_surface_set_stream (surface, null_stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); if (_cairo_int_status_is_error (status)) goto cleanup; if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = CAIRO_INT_STATUS_SUCCESS; goto cleanup; } status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, &surface->base); if (unlikely (status)) goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = CAIRO_INT_STATUS_SUCCESS; cleanup: _cairo_scaled_font_thaw_cache (surface->scaled_font); status2 = _cairo_output_stream_destroy (null_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; } cairo_status_t _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, cairo_output_stream_t *stream, unsigned long glyph_index, cairo_box_t *bbox, double *width) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_scaled_glyph_t *scaled_glyph; cairo_int_status_t status, status2; double x_advance, y_advance; cairo_matrix_t font_matrix_inverse; if (unlikely (surface->base.status)) return surface->base.status; _cairo_type3_glyph_surface_set_stream (surface, stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (status == CAIRO_INT_STATUS_SUCCESS) status = CAIRO_INT_STATUS_IMAGE_FALLBACK; } if (_cairo_int_status_is_error (status)) { _cairo_scaled_font_thaw_cache (surface->scaled_font); return status; } x_advance = scaled_glyph->metrics.x_advance; y_advance = scaled_glyph->metrics.y_advance; font_matrix_inverse = surface->scaled_font->font_matrix; status2 = cairo_matrix_invert (&font_matrix_inverse); /* The invertability of font_matrix is tested in * pdf_operators_show_glyphs before any glyphs are mapped to the * subset. */ assert (status2 == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); *width = x_advance; *bbox = scaled_glyph->bbox; _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse, bbox, NULL); _cairo_output_stream_printf (surface->stream, "%f 0 %f %f %f %f d1\n", x_advance, _cairo_fixed_to_double (bbox->p1.x), - _cairo_fixed_to_double (bbox->p2.y), _cairo_fixed_to_double (bbox->p2.x), - _cairo_fixed_to_double (bbox->p1.y)); if (status == CAIRO_INT_STATUS_SUCCESS) { cairo_output_stream_t *mem_stream; mem_stream = _cairo_memory_stream_create (); status = mem_stream->status; if (unlikely (status)) goto FAIL; _cairo_type3_glyph_surface_set_stream (surface, mem_stream); _cairo_output_stream_printf (surface->stream, "q\n"); status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, &surface->base); status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_type3_glyph_surface_set_stream (surface, stream); if (status == CAIRO_INT_STATUS_SUCCESS) _cairo_memory_stream_copy (mem_stream, stream); status2 = _cairo_output_stream_destroy (mem_stream); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; } if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); FAIL: _cairo_scaled_font_thaw_cache (surface->scaled_font); return status; } #endif /* CAIRO_HAS_FONT_SUBSET */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-types-private.h000066400000000000000000000325101271037650300263610ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_TYPES_PRIVATE_H #define CAIRO_TYPES_PRIVATE_H #include "cairo.h" #include "cairo-fixed-type-private.h" #include "cairo-list-private.h" #include "cairo-reference-count-private.h" CAIRO_BEGIN_DECLS /** * SECTION:cairo-types * @Title: Types * @Short_Description: Generic data types * * This section lists generic data types used in the cairo API. **/ typedef struct _cairo_array cairo_array_t; typedef struct _cairo_backend cairo_backend_t; typedef struct _cairo_boxes_t cairo_boxes_t; typedef struct _cairo_cache cairo_cache_t; typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t; typedef struct _cairo_clip cairo_clip_t; typedef struct _cairo_clip_path cairo_clip_path_t; typedef struct _cairo_color cairo_color_t; typedef struct _cairo_color_stop cairo_color_stop_t; typedef struct _cairo_contour cairo_contour_t; typedef struct _cairo_contour_chain cairo_contour_chain_t; typedef struct _cairo_contour_iter cairo_contour_iter_t; typedef struct _cairo_damage cairo_damage_t; typedef struct _cairo_device_backend cairo_device_backend_t; typedef struct _cairo_font_face_backend cairo_font_face_backend_t; typedef struct _cairo_gstate cairo_gstate_t; typedef struct _cairo_gstate_backend cairo_gstate_backend_t; typedef struct _cairo_glyph_text_info cairo_glyph_text_info_t; typedef struct _cairo_hash_entry cairo_hash_entry_t; typedef struct _cairo_hash_table cairo_hash_table_t; typedef struct _cairo_image_surface cairo_image_surface_t; typedef struct _cairo_mime_data cairo_mime_data_t; typedef struct _cairo_observer cairo_observer_t; typedef struct _cairo_output_stream cairo_output_stream_t; typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; typedef struct _cairo_path_fixed cairo_path_fixed_t; typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; typedef struct _cairo_solid_pattern cairo_solid_pattern_t; typedef struct _cairo_surface_attributes cairo_surface_attributes_t; typedef struct _cairo_surface_backend cairo_surface_backend_t; typedef struct _cairo_surface_observer cairo_surface_observer_t; typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t; typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t; typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t; typedef struct _cairo_traps cairo_traps_t; typedef struct _cairo_tristrip cairo_tristrip_t; typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t; typedef cairo_array_t cairo_user_data_array_t; typedef struct _cairo_scaled_font_private cairo_scaled_font_private_t; typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_scaled_glyph cairo_scaled_glyph_t; typedef struct _cairo_scaled_glyph_private cairo_scaled_glyph_private_t; typedef struct cairo_compositor cairo_compositor_t; typedef struct cairo_fallback_compositor cairo_fallback_compositor_t; typedef struct cairo_mask_compositor cairo_mask_compositor_t; typedef struct cairo_traps_compositor cairo_traps_compositor_t; typedef struct cairo_spans_compositor cairo_spans_compositor_t; struct _cairo_observer { cairo_list_t link; void (*callback) (cairo_observer_t *self, void *arg); }; /** * cairo_hash_entry_t: * * A #cairo_hash_entry_t contains both a key and a value for * #cairo_hash_table_t. User-derived types for #cairo_hash_entry_t must * be type-compatible with this structure (eg. they must have an * unsigned long as the first parameter. The easiest way to get this * is to use: * * typedef _my_entry { * cairo_hash_entry_t base; * ... Remainder of key and value fields here .. * } my_entry_t; * * which then allows a pointer to my_entry_t to be passed to any of * the #cairo_hash_table_t functions as follows without requiring a cast: * * _cairo_hash_table_insert (hash_table, &my_entry->base); * * IMPORTANT: The caller is responsible for initializing * my_entry->base.hash with a hash code derived from the key. The * essential property of the hash code is that keys_equal must never * return %TRUE for two keys that have different hashes. The best hash * code will reduce the frequency of two keys with the same code for * which keys_equal returns %FALSE. * * Which parts of the entry make up the "key" and which part make up * the value are entirely up to the caller, (as determined by the * computation going into base.hash as well as the keys_equal * function). A few of the #cairo_hash_table_t functions accept an entry * which will be used exclusively as a "key", (indicated by a * parameter name of key). In these cases, the value-related fields of * the entry need not be initialized if so desired. **/ struct _cairo_hash_entry { unsigned long hash; }; struct _cairo_array { unsigned int size; unsigned int num_elements; unsigned int element_size; char *elements; }; /** * cairo_lcd_filter_t: * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for * font backend and target device * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel * * The LCD filter specifies the low-pass filter applied to LCD-optimized * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. * * Note: This API was temporarily made available in the public * interface during the 1.7.x development series, but was made private * before 1.8. **/ typedef enum _cairo_lcd_filter { CAIRO_LCD_FILTER_DEFAULT, CAIRO_LCD_FILTER_NONE, CAIRO_LCD_FILTER_INTRA_PIXEL, CAIRO_LCD_FILTER_FIR3, CAIRO_LCD_FILTER_FIR5 } cairo_lcd_filter_t; typedef enum _cairo_round_glyph_positions { CAIRO_ROUND_GLYPH_POS_DEFAULT, CAIRO_ROUND_GLYPH_POS_ON, CAIRO_ROUND_GLYPH_POS_OFF } cairo_round_glyph_positions_t; struct _cairo_font_options { cairo_antialias_t antialias; cairo_subpixel_order_t subpixel_order; cairo_lcd_filter_t lcd_filter; cairo_hint_style_t hint_style; cairo_hint_metrics_t hint_metrics; cairo_round_glyph_positions_t round_glyph_positions; }; struct _cairo_glyph_text_info { const char *utf8; int utf8_len; const cairo_text_cluster_t *clusters; int num_clusters; cairo_text_cluster_flags_t cluster_flags; }; /* XXX: Right now, the _cairo_color structure puts unpremultiplied color in the doubles and premultiplied color in the shorts. Yes, this is crazy insane, (but at least we don't export this madness). I'm still working on a cleaner API, but in the meantime, at least this does prevent precision loss in color when changing alpha. */ struct _cairo_color { double red; double green; double blue; double alpha; unsigned short red_short; unsigned short green_short; unsigned short blue_short; unsigned short alpha_short; }; struct _cairo_color_stop { /* unpremultiplied */ double red; double green; double blue; double alpha; /* unpremultipled, for convenience */ uint16_t red_short; uint16_t green_short; uint16_t blue_short; uint16_t alpha_short; }; typedef enum _cairo_paginated_mode { CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */ CAIRO_PAGINATED_MODE_RENDER, /* render page contents */ CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ } cairo_paginated_mode_t; typedef enum _cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000, CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER, CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, CAIRO_INTERNAL_SURFACE_TYPE_NULL, CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH } cairo_internal_surface_type_t; typedef enum _cairo_internal_device_type { CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000, } cairo_device_surface_type_t; #define CAIRO_HAS_TEST_PAGINATED_SURFACE 1 typedef struct _cairo_slope { cairo_fixed_t dx; cairo_fixed_t dy; } cairo_slope_t, cairo_distance_t; typedef struct _cairo_point_double { double x; double y; } cairo_point_double_t; typedef struct _cairo_circle_double { cairo_point_double_t center; double radius; } cairo_circle_double_t; typedef struct _cairo_distance_double { double dx; double dy; } cairo_distance_double_t; typedef struct _cairo_box_double { cairo_point_double_t p1; cairo_point_double_t p2; } cairo_box_double_t; typedef struct _cairo_line { cairo_point_t p1; cairo_point_t p2; } cairo_line_t, cairo_box_t; typedef struct _cairo_trapezoid { cairo_fixed_t top, bottom; cairo_line_t left, right; } cairo_trapezoid_t; typedef struct _cairo_point_int { int x, y; } cairo_point_int_t; #define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) #define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) typedef enum _cairo_direction { CAIRO_DIRECTION_FORWARD, CAIRO_DIRECTION_REVERSE } cairo_direction_t; typedef struct _cairo_edge { cairo_line_t line; int top, bottom; int dir; } cairo_edge_t; typedef struct _cairo_polygon { cairo_status_t status; cairo_box_t extents; cairo_box_t limit; const cairo_box_t *limits; int num_limits; int num_edges; int edges_size; cairo_edge_t *edges; cairo_edge_t edges_embedded[32]; } cairo_polygon_t; typedef cairo_warn cairo_status_t (*cairo_spline_add_point_func_t) (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent); typedef struct _cairo_spline_knots { cairo_point_t a, b, c, d; } cairo_spline_knots_t; typedef struct _cairo_spline { cairo_spline_add_point_func_t add_point_func; void *closure; cairo_spline_knots_t knots; cairo_slope_t initial_slope; cairo_slope_t final_slope; cairo_bool_t has_point; cairo_point_t last_point; } cairo_spline_t; typedef struct _cairo_pen_vertex { cairo_point_t point; cairo_slope_t slope_ccw; cairo_slope_t slope_cw; } cairo_pen_vertex_t; typedef struct _cairo_pen { double radius; double tolerance; int num_vertices; cairo_pen_vertex_t *vertices; cairo_pen_vertex_t vertices_embedded[32]; } cairo_pen_t; typedef struct _cairo_stroke_style { double line_width; cairo_line_cap_t line_cap; cairo_line_join_t line_join; double miter_limit; double *dash; unsigned int num_dashes; double dash_offset; } cairo_stroke_style_t; typedef struct _cairo_format_masks { int bpp; unsigned long alpha_mask; unsigned long red_mask; unsigned long green_mask; unsigned long blue_mask; } cairo_format_masks_t; typedef enum { CAIRO_STOCK_WHITE, CAIRO_STOCK_BLACK, CAIRO_STOCK_TRANSPARENT, CAIRO_STOCK_NUM_COLORS, } cairo_stock_t; typedef enum _cairo_image_transparency { CAIRO_IMAGE_IS_OPAQUE, CAIRO_IMAGE_HAS_BILEVEL_ALPHA, CAIRO_IMAGE_HAS_ALPHA, CAIRO_IMAGE_UNKNOWN } cairo_image_transparency_t; typedef enum _cairo_image_color { CAIRO_IMAGE_IS_COLOR, CAIRO_IMAGE_IS_GRAYSCALE, CAIRO_IMAGE_IS_MONOCHROME, CAIRO_IMAGE_UNKNOWN_COLOR } cairo_image_color_t; struct _cairo_mime_data { cairo_reference_count_t ref_count; unsigned char *data; unsigned long length; cairo_destroy_func_t destroy; void *closure; }; /* * A #cairo_unscaled_font_t is just an opaque handle we use in the * glyph cache. */ typedef struct _cairo_unscaled_font { cairo_hash_entry_t hash_entry; cairo_reference_count_t ref_count; const cairo_unscaled_font_backend_t *backend; } cairo_unscaled_font_t; CAIRO_END_DECLS #endif /* CAIRO_TYPES_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-unicode.c000066400000000000000000000267131271037650300251760ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * The code in this file is derived from GLib's gutf8.c and * ultimately from libunicode. It is relicensed under the * dual LGPL/MPL with permission of the original authors. * * Copyright © 1999 Tom Tromey * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Tom Tromey. * and Red Hat, Inc. * * Contributor(s): * Owen Taylor */ #include "cairoint.h" #include "cairo-error-private.h" #define UTF8_COMPUTE(Char, Mask, Len) \ if (Char < 128) \ { \ Len = 1; \ Mask = 0x7f; \ } \ else if ((Char & 0xe0) == 0xc0) \ { \ Len = 2; \ Mask = 0x1f; \ } \ else if ((Char & 0xf0) == 0xe0) \ { \ Len = 3; \ Mask = 0x0f; \ } \ else if ((Char & 0xf8) == 0xf0) \ { \ Len = 4; \ Mask = 0x07; \ } \ else if ((Char & 0xfc) == 0xf8) \ { \ Len = 5; \ Mask = 0x03; \ } \ else if ((Char & 0xfe) == 0xfc) \ { \ Len = 6; \ Mask = 0x01; \ } \ else \ Len = -1; #define UTF8_LENGTH(Char) \ ((Char) < 0x80 ? 1 : \ ((Char) < 0x800 ? 2 : \ ((Char) < 0x10000 ? 3 : \ ((Char) < 0x200000 ? 4 : \ ((Char) < 0x4000000 ? 5 : 6))))) #define UTF8_GET(Result, Chars, Count, Mask, Len) \ (Result) = (Chars)[0] & (Mask); \ for ((Count) = 1; (Count) < (Len); ++(Count)) \ { \ if (((Chars)[(Count)] & 0xc0) != 0x80) \ { \ (Result) = -1; \ break; \ } \ (Result) <<= 6; \ (Result) |= ((Chars)[(Count)] & 0x3f); \ } #define UNICODE_VALID(Char) \ ((Char) < 0x110000 && \ (((Char) & 0xFFFFF800) != 0xD800) && \ ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ ((Char) & 0xFFFE) != 0xFFFE) static const char utf8_skip_data[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 }; #define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)]) /* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. * If @p does not point to a valid UTF-8 encoded character, results are * undefined. **/ static uint32_t _utf8_get_char (const unsigned char *p) { int i, mask = 0, len; uint32_t result; unsigned char c = (unsigned char) *p; UTF8_COMPUTE (c, mask, len); if (len == -1) return (uint32_t)-1; UTF8_GET (result, p, i, mask, len); return result; } /* Like _utf8_get_char, but take a maximum length * and return (uint32_t)-2 on incomplete trailing character */ static uint32_t _utf8_get_char_extended (const unsigned char *p, long max_len) { int i, len; uint32_t wc = (unsigned char) *p; if (wc < 0x80) { return wc; } else if (wc < 0xc0) { return (uint32_t)-1; } else if (wc < 0xe0) { len = 2; wc &= 0x1f; } else if (wc < 0xf0) { len = 3; wc &= 0x0f; } else if (wc < 0xf8) { len = 4; wc &= 0x07; } else if (wc < 0xfc) { len = 5; wc &= 0x03; } else if (wc < 0xfe) { len = 6; wc &= 0x01; } else { return (uint32_t)-1; } if (max_len >= 0 && len > max_len) { for (i = 1; i < max_len; i++) { if ((((unsigned char *)p)[i] & 0xc0) != 0x80) return (uint32_t)-1; } return (uint32_t)-2; } for (i = 1; i < len; ++i) { uint32_t ch = ((unsigned char *)p)[i]; if ((ch & 0xc0) != 0x80) { if (ch) return (uint32_t)-1; else return (uint32_t)-2; } wc <<= 6; wc |= (ch & 0x3f); } if (UTF8_LENGTH(wc) != len) return (uint32_t)-1; return wc; } /** * _cairo_utf8_get_char_validated: * @p: a UTF-8 string * @unicode: location to store one Unicode character * * Decodes the first character of a valid UTF-8 string, and returns * the number of bytes consumed. * * Note that the string should be valid. Do not use this without * validating the string first. * * Returns: the number of bytes forming the character returned. **/ int _cairo_utf8_get_char_validated (const char *p, uint32_t *unicode) { int i, mask = 0, len; uint32_t result; unsigned char c = (unsigned char) *p; UTF8_COMPUTE (c, mask, len); if (len == -1) { if (unicode) *unicode = (uint32_t)-1; return 1; } UTF8_GET (result, p, i, mask, len); if (unicode) *unicode = result; return len; } /** * _cairo_utf8_to_ucs4: * @str: an UTF-8 string * @len: length of @str in bytes, or -1 if it is nul-terminated. * If @len is supplied and the string has an embedded nul * byte, only the portion before the nul byte is converted. * @result: location to store a pointer to a newly allocated UTF-32 * string (always native endian), or %NULL. Free with free(). A 0 * word will be written after the last character. * @items_written: location to store number of 32-bit words * written. (Not including the trailing 0) * * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode * with 1 32-bit word per character. The string is validated to * consist entirely of valid Unicode characters. * * Return value: %CAIRO_STATUS_SUCCESS if the entire string was * successfully converted. %CAIRO_STATUS_INVALID_STRING if an * invalid sequence was found. **/ cairo_status_t _cairo_utf8_to_ucs4 (const char *str, int len, uint32_t **result, int *items_written) { uint32_t *str32 = NULL; int n_chars, i; const unsigned char *in; const unsigned char * const ustr = (const unsigned char *) str; in = ustr; n_chars = 0; while ((len < 0 || ustr + len - in > 0) && *in) { uint32_t wc = _utf8_get_char_extended (in, ustr + len - in); if (wc & 0x80000000 || !UNICODE_VALID (wc)) return _cairo_error (CAIRO_STATUS_INVALID_STRING); n_chars++; if (n_chars == INT_MAX) return _cairo_error (CAIRO_STATUS_INVALID_STRING); in = UTF8_NEXT_CHAR (in); } if (result) { str32 = _cairo_malloc_ab (n_chars + 1, sizeof (uint32_t)); if (!str32) return _cairo_error (CAIRO_STATUS_NO_MEMORY); in = ustr; for (i=0; i < n_chars; i++) { str32[i] = _utf8_get_char (in); in = UTF8_NEXT_CHAR (in); } str32[i] = 0; *result = str32; } if (items_written) *items_written = n_chars; return CAIRO_STATUS_SUCCESS; } /** * _cairo_ucs4_to_utf8: * @unicode: a UCS-4 character * @utf8: buffer to write utf8 string into. Must have at least 4 bytes * space available. Or %NULL. * * This space left intentionally blank. * * Return value: Number of bytes in the utf8 string or 0 if an invalid * unicode character **/ int _cairo_ucs4_to_utf8 (uint32_t unicode, char *utf8) { int bytes; char *p; if (unicode < 0x80) { if (utf8) *utf8 = unicode; return 1; } else if (unicode < 0x800) { bytes = 2; } else if (unicode < 0x10000) { bytes = 3; } else if (unicode < 0x200000) { bytes = 4; } else { return 0; } if (!utf8) return bytes; p = utf8 + bytes; while (p > utf8) { *--p = 0x80 | (unicode & 0x3f); unicode >>= 6; } *p |= 0xf0 << (4 - bytes); return bytes; } #if CAIRO_HAS_UTF8_TO_UTF16 /** * _cairo_utf8_to_utf16: * @str: an UTF-8 string * @len: length of @str in bytes, or -1 if it is nul-terminated. * If @len is supplied and the string has an embedded nul * byte, only the portion before the nul byte is converted. * @result: location to store a pointer to a newly allocated UTF-16 * string (always native endian). Free with free(). A 0 * word will be written after the last character. * @items_written: location to store number of 16-bit words * written. (Not including the trailing 0) * * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode * where characters are represented either as a single 16-bit word, or * as a pair of 16-bit "surrogates". The string is validated to * consist entirely of valid Unicode characters. * * Return value: %CAIRO_STATUS_SUCCESS if the entire string was * successfully converted. %CAIRO_STATUS_INVALID_STRING if an * an invalid sequence was found. **/ cairo_status_t _cairo_utf8_to_utf16 (const char *str, int len, uint16_t **result, int *items_written) { uint16_t *str16 = NULL; int n16, i; const unsigned char *in; const unsigned char * const ustr = (const unsigned char *) str; in = ustr; n16 = 0; while ((len < 0 || ustr + len - in > 0) && *in) { uint32_t wc = _utf8_get_char_extended (in, ustr + len - in); if (wc & 0x80000000 || !UNICODE_VALID (wc)) return _cairo_error (CAIRO_STATUS_INVALID_STRING); if (wc < 0x10000) n16 += 1; else n16 += 2; if (n16 == INT_MAX - 1 || n16 == INT_MAX) return _cairo_error (CAIRO_STATUS_INVALID_STRING); in = UTF8_NEXT_CHAR (in); } str16 = _cairo_malloc_ab (n16 + 1, sizeof (uint16_t)); if (!str16) return _cairo_error (CAIRO_STATUS_NO_MEMORY); in = ustr; for (i = 0; i < n16;) { uint32_t wc = _utf8_get_char (in); if (wc < 0x10000) { str16[i++] = wc; } else { str16[i++] = (wc - 0x10000) / 0x400 + 0xd800; str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00; } in = UTF8_NEXT_CHAR (in); } str16[i] = 0; *result = str16; if (items_written) *items_written = n16; return CAIRO_STATUS_SUCCESS; } #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-user-font-private.h000066400000000000000000000034071271037650300271420ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2006, 2008 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg * Behdad Esfahbod */ #ifndef CAIRO_USER_FONT_PRIVATE_H #define CAIRO_USER_FONT_PRIVATE_H #include "cairo.h" #include "cairo-compiler-private.h" cairo_private cairo_bool_t _cairo_font_face_is_user (cairo_font_face_t *font_face); #endif /* CAIRO_USER_FONT_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-user-font.c000066400000000000000000000630411271037650300254650ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2006, 2008 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg * Behdad Esfahbod */ #include "cairoint.h" #include "cairo-user-font-private.h" #include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-error-private.h" /** * SECTION:cairo-user-fonts * @Title:User Fonts * @Short_Description: Font support with font data provided by the user * * The user-font feature allows the cairo user to provide drawings for glyphs * in a font. This is most useful in implementing fonts in non-standard * formats, like SVG fonts and Flash fonts, but can also be used by games and * other application to draw "funky" fonts. **/ /** * CAIRO_HAS_USER_FONT: * * Defined if the user font backend is available. * This macro can be used to conditionally compile backend-specific code. * The user font backend is always built in versions of cairo that support * this feature (1.8 and later). * * Since: 1.8 **/ typedef struct _cairo_user_scaled_font_methods { cairo_user_scaled_font_init_func_t init; cairo_user_scaled_font_render_glyph_func_t render_glyph; cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; } cairo_user_scaled_font_methods_t; typedef struct _cairo_user_font_face { cairo_font_face_t base; /* Set to true after first scaled font is created. At that point, * the scaled_font_methods cannot change anymore. */ cairo_bool_t immutable; cairo_user_scaled_font_methods_t scaled_font_methods; } cairo_user_font_face_t; typedef struct _cairo_user_scaled_font { cairo_scaled_font_t base; cairo_text_extents_t default_glyph_extents; /* space to compute extents in, and factors to convert back to user space */ cairo_matrix_t extent_scale; double extent_x_scale; double extent_y_scale; /* multiplier for metrics hinting */ double snap_x_scale; double snap_y_scale; } cairo_user_scaled_font_t; /* #cairo_user_scaled_font_t */ static cairo_surface_t * _cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font) { cairo_content_t content; content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_ALPHA; return cairo_recording_surface_create (content, NULL); } static cairo_t * _cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, cairo_surface_t *recording_surface) { cairo_t *cr; cr = cairo_create (recording_surface); if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { cairo_matrix_t scale; scale = scaled_font->base.scale; scale.x0 = scale.y0 = 0.; cairo_set_matrix (cr, &scale); } cairo_set_font_size (cr, 1.0); cairo_set_font_options (cr, &scaled_font->base.options); cairo_set_source_rgb (cr, 1., 1., 1.); return cr; } static cairo_int_status_t _cairo_user_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_user_scaled_font_t *scaled_font = abstract_font; cairo_surface_t *recording_surface = scaled_glyph->recording_surface; if (!scaled_glyph->recording_surface) { cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; cairo_text_extents_t extents = scaled_font->default_glyph_extents; cairo_t *cr; if (!face->scaled_font_methods.render_glyph) return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font); /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface); status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); if (status == CAIRO_INT_STATUS_SUCCESS) status = cairo_status (cr); cairo_destroy (cr); if (unlikely (status)) { cairo_surface_destroy (recording_surface); return status; } } _cairo_scaled_glyph_set_recording_surface (scaled_glyph, &scaled_font->base, recording_surface); /* set metrics */ if (extents.width == 0.) { cairo_box_t bbox; double x1, y1, x2, y2; double x_scale, y_scale; /* Compute extents.x/y/width/height from recording_surface, * in font space. */ status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, &bbox, &scaled_font->extent_scale); if (unlikely (status)) return status; _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); x_scale = scaled_font->extent_x_scale; y_scale = scaled_font->extent_y_scale; extents.x_bearing = x1 * x_scale; extents.y_bearing = y1 * y_scale; extents.width = (x2 - x1) * x_scale; extents.height = (y2 - y1) * y_scale; } if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale; extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale; } _cairo_scaled_glyph_set_metrics (scaled_glyph, &scaled_font->base, &extents); } if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { cairo_surface_t *surface; cairo_format_t format; int width, height; /* TODO * extend the glyph cache to support argb glyphs. * need to figure out the semantics and interaction with subpixel * rendering first. */ width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); switch (scaled_font->base.options.antialias) { default: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; case CAIRO_ANTIALIAS_BEST: case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; } surface = cairo_image_surface_create (format, width, height); cairo_surface_set_device_offset (surface, - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); status = _cairo_recording_surface_replay (recording_surface, surface); if (unlikely (status)) { cairo_surface_destroy(surface); return status; } _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *) surface); } if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { cairo_path_fixed_t *path = _cairo_path_fixed_create (); if (!path) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_recording_surface_get_path (recording_surface, path); if (unlikely (status)) { _cairo_path_fixed_destroy (path); return status; } _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); } return status; } static unsigned long _cairo_user_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_user_scaled_font_t *scaled_font = abstract_font; cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; unsigned long glyph = 0; if (face->scaled_font_methods.unicode_to_glyph) { cairo_status_t status; status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, ucs4, &glyph); if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) goto not_implemented; if (status != CAIRO_STATUS_SUCCESS) { status = _cairo_scaled_font_set_error (&scaled_font->base, status); glyph = 0; } } else { not_implemented: glyph = ucs4; } return glyph; } static cairo_int_status_t _cairo_user_text_to_glyphs (void *abstract_font, double x, double y, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *cluster_flags) { cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; cairo_user_scaled_font_t *scaled_font = abstract_font; cairo_user_font_face_t *face = (cairo_user_font_face_t *) scaled_font->base.font_face; if (face->scaled_font_methods.text_to_glyphs) { int i; cairo_glyph_t *orig_glyphs = *glyphs; int orig_num_glyphs = *num_glyphs; status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); if (status != CAIRO_INT_STATUS_SUCCESS && status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED) return status; if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) { if (orig_glyphs != *glyphs) { cairo_glyph_free (*glyphs); *glyphs = orig_glyphs; } *num_glyphs = orig_num_glyphs; return CAIRO_INT_STATUS_UNSUPPORTED; } /* Convert from font space to user space and add x,y */ for (i = 0; i < *num_glyphs; i++) { double gx = (*glyphs)[i].x; double gy = (*glyphs)[i].y; cairo_matrix_transform_point (&scaled_font->base.font_matrix, &gx, &gy); (*glyphs)[i].x = gx + x; (*glyphs)[i].y = gy + y; } } return status; } static cairo_status_t _cairo_user_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font); static cairo_status_t _cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face) { return _cairo_font_face_twin_create_for_toy (toy_face, font_face); } static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { CAIRO_FONT_TYPE_USER, NULL, /* scaled_font_fini */ _cairo_user_scaled_glyph_init, _cairo_user_text_to_glyphs, _cairo_user_ucs4_to_index, NULL, /* show_glyphs */ NULL, /* load_truetype_table */ NULL /* index_to_ucs4 */ }; /* #cairo_user_font_face_t */ static cairo_status_t _cairo_user_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_user_font_face_t *font_face = abstract_face; cairo_user_scaled_font_t *user_scaled_font = NULL; cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.}; font_face->immutable = TRUE; user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); if (unlikely (user_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = _cairo_scaled_font_init (&user_scaled_font->base, &font_face->base, font_matrix, ctm, options, &_cairo_user_scaled_font_backend); if (unlikely (status)) { free (user_scaled_font); return status; } /* XXX metrics hinting? */ /* compute a normalized version of font scale matrix to compute * extents in. This is to minimize error caused by the cairo_fixed_t * representation. */ { double fixed_scale, x_scale, y_scale; user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse; status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale, &x_scale, &y_scale, 1); if (status == CAIRO_STATUS_SUCCESS) { if (x_scale == 0) x_scale = 1.; if (y_scale == 0) y_scale = 1.; user_scaled_font->snap_x_scale = x_scale; user_scaled_font->snap_y_scale = y_scale; /* since glyphs are pretty much 1.0x1.0, we can reduce error by * scaling to a larger square. say, 1024.x1024. */ fixed_scale = 1024.; x_scale /= fixed_scale; y_scale /= fixed_scale; cairo_matrix_scale (&user_scaled_font->extent_scale, 1. / x_scale, 1. / y_scale); user_scaled_font->extent_x_scale = x_scale; user_scaled_font->extent_y_scale = y_scale; } } if (status == CAIRO_STATUS_SUCCESS && font_face->scaled_font_methods.init != NULL) { /* Lock the scaled_font mutex such that user doesn't accidentally try * to use it just yet. */ CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex); /* Give away fontmap lock such that user-font can use other fonts */ status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base); if (status == CAIRO_STATUS_SUCCESS) { cairo_surface_t *recording_surface; cairo_t *cr; recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font); cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface); cairo_surface_destroy (recording_surface); status = font_face->scaled_font_methods.init (&user_scaled_font->base, cr, &font_extents); if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) status = CAIRO_STATUS_SUCCESS; if (status == CAIRO_STATUS_SUCCESS) status = cairo_status (cr); cairo_destroy (cr); _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base); } CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex); } if (status == CAIRO_STATUS_SUCCESS) status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents); if (status != CAIRO_STATUS_SUCCESS) { _cairo_scaled_font_fini (&user_scaled_font->base); free (user_scaled_font); } else { user_scaled_font->default_glyph_extents.x_bearing = 0.; user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent; user_scaled_font->default_glyph_extents.width = 0.; user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent; user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance; user_scaled_font->default_glyph_extents.y_advance = 0.; *scaled_font = &user_scaled_font->base; } return status; } const cairo_font_face_backend_t _cairo_user_font_face_backend = { CAIRO_FONT_TYPE_USER, _cairo_user_font_face_create_for_toy, NULL, /* destroy */ _cairo_user_font_face_scaled_font_create }; cairo_bool_t _cairo_font_face_is_user (cairo_font_face_t *font_face) { return font_face->backend == &_cairo_user_font_face_backend; } /* Implement the public interface */ /** * cairo_user_font_face_create: * * Creates a new user font-face. * * Use the setter functions to associate callbacks with the returned * user font. The only mandatory callback is render_glyph. * * After the font-face is created, the user can attach arbitrary data * (the actual font data) to it using cairo_font_face_set_user_data() * and access it from the user-font callbacks by using * cairo_scaled_font_get_font_face() followed by * cairo_font_face_get_user_data(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.8 **/ cairo_font_face_t * cairo_user_font_face_create (void) { cairo_user_font_face_t *font_face; font_face = malloc (sizeof (cairo_user_font_face_t)); if (!font_face) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); font_face->immutable = FALSE; memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); return &font_face->base; } slim_hidden_def(cairo_user_font_face_create); /* User-font method setters */ /** * cairo_user_font_face_set_init_func: * @font_face: A user font face * @init_func: The init callback, or %NULL * * Sets the scaled-font initialization function of a user-font. * See #cairo_user_scaled_font_init_func_t for details of how the callback * works. * * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE * error will occur. A user font-face is immutable as soon as a scaled-font * is created from it. * * Since: 1.8 **/ void cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, cairo_user_scaled_font_init_func_t init_func) { cairo_user_font_face_t *user_font_face; if (font_face->status) return; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return; } user_font_face = (cairo_user_font_face_t *) font_face; if (user_font_face->immutable) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) return; } user_font_face->scaled_font_methods.init = init_func; } slim_hidden_def(cairo_user_font_face_set_init_func); /** * cairo_user_font_face_set_render_glyph_func: * @font_face: A user font face * @render_glyph_func: The render_glyph callback, or %NULL * * Sets the glyph rendering function of a user-font. * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback * works. * * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE * error will occur. A user font-face is immutable as soon as a scaled-font * is created from it. * * The render_glyph callback is the only mandatory callback of a user-font. * If the callback is %NULL and a glyph is tried to be rendered using * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. * * Since: 1.8 **/ void cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, cairo_user_scaled_font_render_glyph_func_t render_glyph_func) { cairo_user_font_face_t *user_font_face; if (font_face->status) return; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return; } user_font_face = (cairo_user_font_face_t *) font_face; if (user_font_face->immutable) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) return; } user_font_face->scaled_font_methods.render_glyph = render_glyph_func; } slim_hidden_def(cairo_user_font_face_set_render_glyph_func); /** * cairo_user_font_face_set_text_to_glyphs_func: * @font_face: A user font face * @text_to_glyphs_func: The text_to_glyphs callback, or %NULL * * Sets th text-to-glyphs conversion function of a user-font. * See #cairo_user_scaled_font_text_to_glyphs_func_t for details of how the callback * works. * * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE * error will occur. A user font-face is immutable as soon as a scaled-font * is created from it. * * Since: 1.8 **/ void cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func) { cairo_user_font_face_t *user_font_face; if (font_face->status) return; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return; } user_font_face = (cairo_user_font_face_t *) font_face; if (user_font_face->immutable) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) return; } user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func; } /** * cairo_user_font_face_set_unicode_to_glyph_func: * @font_face: A user font face * @unicode_to_glyph_func: The unicode_to_glyph callback, or %NULL * * Sets the unicode-to-glyph conversion function of a user-font. * See #cairo_user_scaled_font_unicode_to_glyph_func_t for details of how the callback * works. * * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE * error will occur. A user font-face is immutable as soon as a scaled-font * is created from it. * * Since: 1.8 **/ void cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func) { cairo_user_font_face_t *user_font_face; if (font_face->status) return; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return; } user_font_face = (cairo_user_font_face_t *) font_face; if (user_font_face->immutable) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) return; } user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func; } slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func); /* User-font method getters */ /** * cairo_user_font_face_get_init_func: * @font_face: A user font face * * Gets the scaled-font initialization function of a user-font. * * Return value: The init callback of @font_face * or %NULL if none set or an error has occurred. * * Since: 1.8 **/ cairo_user_scaled_font_init_func_t cairo_user_font_face_get_init_func (cairo_font_face_t *font_face) { cairo_user_font_face_t *user_font_face; if (font_face->status) return NULL; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return NULL; } user_font_face = (cairo_user_font_face_t *) font_face; return user_font_face->scaled_font_methods.init; } /** * cairo_user_font_face_get_render_glyph_func: * @font_face: A user font face * * Gets the glyph rendering function of a user-font. * * Return value: The render_glyph callback of @font_face * or %NULL if none set or an error has occurred. * * Since: 1.8 **/ cairo_user_scaled_font_render_glyph_func_t cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face) { cairo_user_font_face_t *user_font_face; if (font_face->status) return NULL; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return NULL; } user_font_face = (cairo_user_font_face_t *) font_face; return user_font_face->scaled_font_methods.render_glyph; } /** * cairo_user_font_face_get_text_to_glyphs_func: * @font_face: A user font face * * Gets the text-to-glyphs conversion function of a user-font. * * Return value: The text_to_glyphs callback of @font_face * or %NULL if none set or an error occurred. * * Since: 1.8 **/ cairo_user_scaled_font_text_to_glyphs_func_t cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face) { cairo_user_font_face_t *user_font_face; if (font_face->status) return NULL; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return NULL; } user_font_face = (cairo_user_font_face_t *) font_face; return user_font_face->scaled_font_methods.text_to_glyphs; } /** * cairo_user_font_face_get_unicode_to_glyph_func: * @font_face: A user font face * * Gets the unicode-to-glyph conversion function of a user-font. * * Return value: The unicode_to_glyph callback of @font_face * or %NULL if none set or an error occurred. * * Since: 1.8 **/ cairo_user_scaled_font_unicode_to_glyph_func_t cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) { cairo_user_font_face_t *user_font_face; if (font_face->status) return NULL; if (! _cairo_font_face_is_user (font_face)) { if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) return NULL; } user_font_face = (cairo_user_font_face_t *) font_face; return user_font_face->scaled_font_methods.unicode_to_glyph; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-version.c000066400000000000000000000202421271037650300252240ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #define CAIRO_VERSION_H 1 #include "cairoint.h" /* get the "real" version info instead of dummy cairo-version.h */ #undef CAIRO_VERSION_H #include "../cairo-version.h" /** * SECTION:cairo-version * @Title: Version Information * @Short_Description: Compile-time and run-time version checks. * * Cairo has a three-part version number scheme. In this scheme, we use * even vs. odd numbers to distinguish fixed points in the software * vs. in-progress development, (such as from git instead of a tar file, * or as a "snapshot" tar file as opposed to a "release" tar file). * * * _____ Major. Always 1, until we invent a new scheme. * / ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git) * | / _ Micro. Even/Odd = Tar-file/git * | | / * 1.0.0 * * * Here are a few examples of versions that one might see. * * Releases * -------- * 1.0.0 - A major release * 1.0.2 - A subsequent maintenance release * 1.2.0 - Another major release *   * Snapshots * --------- * 1.1.2 - A snapshot (working toward the 1.2.0 release) *   * In-progress development (eg. from git) * -------------------------------------- * 1.0.1 - Development on a maintenance branch (toward 1.0.2 release) * 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release) * * * * Compatibility * * The API/ABI compatibility guarantees for various versions are as * follows. First, let's assume some cairo-using application code that is * successfully using the API/ABI "from" one version of cairo. Then let's * ask the question whether this same code can be moved "to" the API/ABI * of another version of cairo. * * Moving from a release to any later version (release, snapshot, * development) is always guaranteed to provide compatibility. * * Moving from a snapshot to any later version is not guaranteed to * provide compatibility, since snapshots may introduce new API that ends * up being removed before the next release. * * Moving from an in-development version (odd micro component) to any * later version is not guaranteed to provide compatibility. In fact, * there's not even a guarantee that the code will even continue to work * with the same in-development version number. This is because these * numbers don't correspond to any fixed state of the software, but * rather the many states between snapshots and releases. * * * * Examining the version * * Cairo provides the ability to examine the version at either * compile-time or run-time and in both a human-readable form as well as * an encoded form suitable for direct comparison. Cairo also provides the * macro CAIRO_VERSION_ENCODE() to perform the encoding. * * * Compile-time * ------------ * CAIRO_VERSION_STRING Human-readable * CAIRO_VERSION Encoded, suitable for comparison *   * Run-time * -------- * cairo_version_string() Human-readable * cairo_version() Encoded, suitable for comparison * * * For example, checking that the cairo version is greater than or equal * to 1.0.0 could be achieved at compile-time or run-time as follows: * * * ##if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 0, 0) * printf ("Compiling with suitable cairo version: %s\n", %CAIRO_VERSION_STRING); * ##endif * * if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 0, 0)) * printf ("Running with suitable cairo version: %s\n", cairo_version_string ()); * * * **/ /** * CAIRO_VERSION: * * The version of cairo available at compile-time, encoded using * CAIRO_VERSION_ENCODE(). * * Since: 1.0 **/ /** * CAIRO_VERSION_MAJOR: * * The major component of the version of cairo available at compile-time. * * Since: 1.0 **/ /** * CAIRO_VERSION_MINOR: * * The minor component of the version of cairo available at compile-time. * * Since: 1.0 **/ /** * CAIRO_VERSION_MICRO: * * The micro component of the version of cairo available at compile-time. * * Since: 1.0 **/ /** * CAIRO_VERSION_STRING: * * A human-readable string literal containing the version of cairo available * at compile-time, in the form of "X.Y.Z". * * Since: 1.8 **/ /** * CAIRO_VERSION_ENCODE: * @major: the major component of the version number * @minor: the minor component of the version number * @micro: the micro component of the version number * * This macro encodes the given cairo version into an integer. The numbers * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro. * Two encoded version numbers can be compared as integers. The encoding ensures * that later versions compare greater than earlier versions. * * Returns: the encoded version. * * Since: 1.0 **/ /** * CAIRO_VERSION_STRINGIZE: * @major: the major component of the version number * @minor: the minor component of the version number * @micro: the micro component of the version number * * This macro encodes the given cairo version into an string. The numbers * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro. * The parameters to this macro must expand to numerical literals. * * Returns: a string literal containing the version. * * Since: 1.8 **/ /** * cairo_version: * * Returns the version of the cairo library encoded in a single * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that * later versions compare greater than earlier versions. * * A run-time comparison to check that cairo's version is greater than * or equal to version X.Y.Z could be performed as follows: * * * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...} * * * See also cairo_version_string() as well as the compile-time * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING. * * Return value: the encoded version. * * Since: 1.0 **/ int cairo_version (void) { return CAIRO_VERSION; } /** * cairo_version_string: * * Returns the version of the cairo library as a human-readable string * of the form "X.Y.Z". * * See also cairo_version() as well as the compile-time equivalents * %CAIRO_VERSION_STRING and %CAIRO_VERSION. * * Return value: a string containing the version. * * Since: 1.0 **/ const char* cairo_version_string (void) { return CAIRO_VERSION_STRING; } slim_hidden_def (cairo_version_string); Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-version.h000066400000000000000000000011741271037650300252340ustar00rootroot00000000000000/* This is a dummy file. * The actual version info is in toplevel cairo-version.h. * The purpose of this file is to make most of the source files NOT depend * on the real cairo-version.h, and as a result, changing library version * would not cause a complete rebuild of all object files (just a relink). * This is useful when bisecting. */ #ifndef CAIRO_VERSION_H #define CAIRO_VERSION_H #define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD #define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD #define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-vg-surface.c000066400000000000000000001414671271037650300256160ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2008 Opened Hand Ltd. * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.og/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Pierre Tardy * Øyvind KolÃ¥s * Vladimi Vukicevic (stubbed out base backend) * Chris Wilson */ #include "cairoint.h" #include "cairo-vg.h" #include "cairo-cache-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-path-fixed-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-surface-clipper-private.h" #include #include //#define OPENVG_DEBUG /* * Work that needs to be done: * - Glyph cache / proper font support * * - First-class paths * Paths are expensive for OpenVG, reuse paths whenever possible. * So add a path cache, and first class paths! */ typedef struct _cairo_vg_surface cairo_vg_surface_t; /* XXX need GL specific context control. :( */ struct _cairo_vg_context { cairo_status_t status; cairo_reference_count_t ref_count; unsigned long target_id; VGPaint paint; cairo_vg_surface_t *source; double alpha; cairo_cache_t snapshot_cache; void *display; void *context; cairo_status_t (*create_target) (cairo_vg_context_t *, cairo_vg_surface_t *); cairo_status_t (*set_target) (cairo_vg_context_t *, cairo_vg_surface_t *); void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *); }; struct _cairo_vg_surface { cairo_surface_t base; cairo_vg_context_t *context; VGImage image; VGImageFormat format; int width; int height; cairo_bool_t own_image; cairo_cache_entry_t snapshot_cache_entry; cairo_surface_clipper_t clipper; unsigned long target_id; }; static const cairo_surface_backend_t cairo_vg_surface_backend; slim_hidden_proto (cairo_vg_surface_create); static cairo_surface_t * _vg_surface_create_internal (cairo_vg_context_t *context, VGImage image, VGImageFormat format, int width, int height); static cairo_vg_context_t * _vg_context_reference (cairo_vg_context_t *context) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); _cairo_reference_count_inc (&context->ref_count); return context; } static cairo_vg_context_t * _vg_context_lock (cairo_vg_context_t *context) { /* XXX if we need to add locking, then it has to be recursive */ return context; } static cairo_int_status_t _vg_context_set_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { cairo_status_t status; if (surface->target_id == 0) { status = context->create_target (context, surface); if (unlikely (status)) return status; } if (context->target_id == surface->target_id) return CAIRO_STATUS_SUCCESS; context->target_id = surface->target_id; return context->set_target (context, surface); } static void _vg_context_destroy_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { if (surface->target_id == 0) return; if (context->target_id == surface->target_id) context->set_target (context, NULL); context->destroy_target (context, surface); } static cairo_bool_t _vg_snapshot_cache_can_remove (const void *entry) { return TRUE; } static void _vg_snapshot_cache_remove (void *cache_entry) { cairo_vg_surface_t *surface = cairo_container_of (cache_entry, cairo_vg_surface_t, snapshot_cache_entry); surface->snapshot_cache_entry.hash = 0; cairo_surface_destroy (&surface->base); } static cairo_status_t _vg_context_init (cairo_vg_context_t *context) { cairo_status_t status; context->status = CAIRO_STATUS_SUCCESS; CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1); status = _cairo_cache_init (&context->snapshot_cache, NULL, _vg_snapshot_cache_can_remove, _vg_snapshot_cache_remove, 16*1024*1024); if (unlikely (status)) return status; context->target_id = 0; context->source = NULL; context->alpha = 1.0; context->paint = vgCreatePaint (); vgLoadIdentity (); return CAIRO_STATUS_SUCCESS; } static void _vg_context_destroy (cairo_vg_context_t *context) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); if (! _cairo_reference_count_dec_and_test (&context->ref_count)) return; if (context->paint != VG_INVALID_HANDLE) vgDestroyPaint (context->paint); _cairo_cache_fini (&context->snapshot_cache); free (context); } static void _vg_context_unlock (cairo_vg_context_t *context) { } #ifdef OPENVG_DEBUG static void check_vg_errors(const char*function,int line) { int err = vgGetError(); if (err != VG_NO_ERROR){ printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err); assert(err == VG_NO_ERROR); } } #define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__) #else #define CHECK_VG_ERRORS() do{}while(0) #endif //OPENVG_DEBUG static pixman_format_code_t _vg_format_to_pixman (VGImageFormat format, cairo_bool_t *needs_premult_fixup) { *needs_premult_fixup = FALSE; switch (format) { /* RGB{A,X} channel ordering */ case VG_sRGBX_8888: return PIXMAN_r8g8b8x8; case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8; case VG_sRGBA_8888_PRE: return PIXMAN_r8g8b8a8; case VG_sRGB_565: return PIXMAN_r5g6b5; case VG_sRGBA_5551: return 0; case VG_sRGBA_4444: return 0; case VG_sL_8: return PIXMAN_g8; case VG_lRGBX_8888: return 0; case VG_lRGBA_8888: return 0; case VG_lRGBA_8888_PRE: return 0; case VG_lL_8: return 0; case VG_A_8: return PIXMAN_a8; case VG_BW_1: return PIXMAN_a1; case VG_A_1: return PIXMAN_a1; case VG_A_4: return PIXMAN_a4; /* {A,X}RGB channel ordering */ case VG_sXRGB_8888: return PIXMAN_x8r8g8b8; case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8; case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8; case VG_sARGB_1555: return 0; case VG_sARGB_4444: return 0; case VG_lXRGB_8888: return 0; case VG_lARGB_8888: return 0; case VG_lARGB_8888_PRE: return 0; /* BGR{A,X} channel ordering */ case VG_sBGRX_8888: return PIXMAN_b8g8r8x8; case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8; case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8; case VG_sBGR_565: return PIXMAN_b5g6r5; case VG_sBGRA_5551: return 0; case VG_sBGRA_4444: return 0; case VG_lBGRX_8888: return 0; case VG_lBGRA_8888: return 0; case VG_lBGRA_8888_PRE: return 0; /* {A,X}BGR channel ordering */ case VG_sXBGR_8888: return PIXMAN_x8b8g8r8; case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8; case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8; case VG_sABGR_1555: return 0; case VG_sABGR_4444: return 0; case VG_lXBGR_8888: return 0; case VG_lABGR_8888: return 0; case VG_lABGR_8888_PRE: return 0; default: return 0; } } static pixman_format_code_t _vg_format_to_content (VGImageFormat format) { /* XXX could use more simple bit tests */ switch (format) { /* RGB{A,X} channel ordering */ case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR; case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sRGB_565: return CAIRO_CONTENT_COLOR; case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sL_8: return CAIRO_CONTENT_ALPHA; case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR; case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lL_8: return CAIRO_CONTENT_ALPHA; case VG_A_8: return CAIRO_CONTENT_ALPHA; case VG_A_4: return CAIRO_CONTENT_ALPHA; case VG_A_1: return CAIRO_CONTENT_ALPHA; case VG_BW_1: return CAIRO_CONTENT_ALPHA; /* {A,X}RGB channel ordering */ case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR; case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR; case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; /* BGR{A,X} channel ordering */ case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR; case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sBGR_565: return CAIRO_CONTENT_COLOR; case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR; case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; /* {A,X}BGR channel ordering */ case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR; case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA; case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR; case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; default: return 0; } } static VGImageFormat _vg_format_from_pixman (pixman_format_code_t format) { /* XXX _PRE needs fixup */ switch ((int) format) { case PIXMAN_r5g6b5: return VG_sRGB_565; case PIXMAN_g8: return VG_sL_8; case PIXMAN_a8: return VG_A_8; case PIXMAN_a1: return VG_BW_1; case PIXMAN_x8r8g8b8: return VG_sXRGB_8888; case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE case PIXMAN_b8g8r8x8: return VG_sBGRX_8888; case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE case PIXMAN_b5g6r5: return VG_sBGR_565; case PIXMAN_x8b8g8r8: return VG_sXBGR_8888; case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE default: return 0; } } static VGImageFormat _vg_format_for_content (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_ALPHA: return VG_A_8; case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888; default: ASSERT_NOT_REACHED; case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE } } static cairo_surface_t * _vg_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_vg_surface_t *surface = abstract_surface; if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || height > vgGeti (VG_MAX_IMAGE_HEIGHT)) { return NULL; } return cairo_vg_surface_create (surface->context, content, width, height); } static cairo_status_t _vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_vg_surface_t *surface = cairo_container_of (clipper, cairo_vg_surface_t, clipper); cairo_vg_surface_t *mask; cairo_status_t status; if (path == NULL) { vgMask (VG_INVALID_HANDLE, VG_FILL_MASK, 0, 0, surface->width, surface->height); vgSeti (VG_MASKING, VG_FALSE); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } mask = (cairo_vg_surface_t *) _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, surface->width, surface->height); if (unlikely (mask == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (mask->base.status)) return mask->base.status; status = _cairo_surface_fill (&mask->base, CAIRO_OPERATOR_SOURCE, &_cairo_pattern_white.base, path, fill_rule, tolerance, antialias, NULL); if (status) { cairo_surface_destroy (&mask->base); return status; } vgSeti (VG_MASKING, VG_TRUE); vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height); cairo_surface_destroy (&mask->base); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _vg_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_vg_surface_t *surface = abstract_surface; extents->x = 0; extents->y = 0; extents->width = surface->width; extents->height = surface->height; return TRUE; } #define MAX_SEG 16 /* max number of knots to upload in a batch */ typedef struct _vg_path { VGPath path; const cairo_matrix_t *ctm_inverse; VGubyte gseg[MAX_SEG]; VGfloat gdata[MAX_SEG*3*2]; int dcount; int scount; } vg_path_t; static cairo_status_t _vg_move_to (void *closure, const cairo_point_t *point) { vg_path_t *path = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (path->ctm_inverse) cairo_matrix_transform_point (path->ctm_inverse, &x, &y); path->gseg[path->scount++] = VG_MOVE_TO; path->gdata[path->dcount++] = x; path->gdata[path->dcount++] = y; if (path->scount >= MAX_SEG-1) { vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); path->scount = 0; path->dcount = 0; } CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _vg_line_to (void *closure, const cairo_point_t *point) { vg_path_t *path = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (path->ctm_inverse) cairo_matrix_transform_point (path->ctm_inverse, &x, &y); path->gseg[path->scount++] = VG_LINE_TO; path->gdata[path->dcount++] = x; path->gdata[path->dcount++] = y; if (path->scount >= MAX_SEG-1) { vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); path->scount = 0; path->dcount = 0; } CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _vg_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { vg_path_t *path = closure; double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); double y1 = _cairo_fixed_to_double (p1->y); double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); if (path->ctm_inverse) { cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0); cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1); cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2); } path->gseg[path->scount++] = VG_CUBIC_TO; path->gdata[path->dcount++] = x0; path->gdata[path->dcount++] = y0; path->gdata[path->dcount++] = x1; path->gdata[path->dcount++] = y1; path->gdata[path->dcount++] = x2; path->gdata[path->dcount++] = y2; if (path->scount >= MAX_SEG-1) { vgAppendPathData(path->path, path->scount, path->gseg, path->gdata); path->scount = 0; path->dcount = 0; } CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _vg_close_path (void *closure) { vg_path_t *path = closure; path->gseg[path->scount++] = VG_CLOSE_PATH; if (path->scount >= MAX_SEG-1) { vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); path->scount = 0; path->dcount = 0; } CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static void _vg_path_from_cairo (vg_path_t *vg_path, const cairo_path_fixed_t *path) { cairo_status_t status; vg_path->scount = 0; vg_path->dcount = 0; status = _cairo_path_fixed_interpret (path, _vg_move_to, _vg_line_to, _vg_curve_to, _vg_close_path, vg_path); assert (status == CAIRO_STATUS_SUCCESS); vgAppendPathData (vg_path->path, vg_path->scount, vg_path->gseg, vg_path->gdata); CHECK_VG_ERRORS(); } static cairo_bool_t _vg_is_supported_operator (cairo_operator_t op) { switch ((int) op) { case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_IN: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_ADD: return TRUE; default: return FALSE; } } static VGBlendMode _vg_operator (cairo_operator_t op) { switch ((int) op) { case CAIRO_OPERATOR_SOURCE: return VG_BLEND_SRC; case CAIRO_OPERATOR_OVER: return VG_BLEND_SRC_OVER; case CAIRO_OPERATOR_IN: return VG_BLEND_SRC_IN; case CAIRO_OPERATOR_DEST_OVER: return VG_BLEND_DST_OVER; case CAIRO_OPERATOR_DEST_IN: return VG_BLEND_DST_IN; case CAIRO_OPERATOR_ADD: return VG_BLEND_ADDITIVE; default: ASSERT_NOT_REACHED; return VG_BLEND_SRC_OVER; } } static VGFillRule _vg_fill_rule_from_cairo (cairo_fill_rule_t rule) { switch (rule) { case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD; case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO; } ASSERT_NOT_REACHED; return VG_NON_ZERO; } static VGRenderingQuality _vg_rendering_quality_from_cairo (cairo_antialias_t aa) { switch (aa) { case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_BEST: return VG_RENDERING_QUALITY_BETTER; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_FAST: return VG_RENDERING_QUALITY_FASTER; case CAIRO_ANTIALIAS_NONE: return VG_RENDERING_QUALITY_NONANTIALIASED; } ASSERT_NOT_REACHED; return VG_RENDERING_QUALITY_BETTER; } static VGCapStyle _vg_line_cap_from_cairo (cairo_line_cap_t cap) { switch (cap) { case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; } ASSERT_NOT_REACHED; return VG_CAP_BUTT; } static VGJoinStyle _vg_line_join_from_cairo (cairo_line_join_t join) { switch (join) { case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER; case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND; case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; } ASSERT_NOT_REACHED; return VG_JOIN_MITER; } static void _vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src) { dst[0] = /* sx */ src->xx; dst[1] = /* shy */ src->yx; dst[2] = /* w0 */ 0; dst[3] = /* shx */ src->xy; dst[4] = /* sy */ src->yy; dst[5] = /* w1 */ 0; dst[6] = /* tx */ src->x0; dst[7] = /* ty */ src->y0; dst[8] = /* w2 */ 0; } static cairo_status_t _vg_setup_gradient_stops (cairo_vg_context_t *context, const cairo_gradient_pattern_t *pattern) { VGint numstops = pattern->n_stops; VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)]; int i; if (numstops*5 < ARRAY_LENGTH (stack_stops)) { stops = stack_stops; } else { stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat)); if (unlikely (stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < numstops; i++) { stops[i*5 + 0] = pattern->stops[i].offset; stops[i*5 + 1] = pattern->stops[i].color.red; stops[i*5 + 2] = pattern->stops[i].color.green; stops[i*5 + 3] = pattern->stops[i].color.blue; stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha; } vgSetParameterfv (context->paint, VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops); if (stops != stack_stops) free (stops); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static void _vg_set_source_matrix (const cairo_pattern_t *pat) { cairo_matrix_t mat; cairo_status_t status; VGfloat vmat[9]; mat = pat->matrix; status = cairo_matrix_invert (&mat); assert (status == CAIRO_STATUS_SUCCESS); _vg_matrix_from_cairo (vmat, &mat); vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); vgLoadMatrix (vmat); vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); vgLoadMatrix (vmat); vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); CHECK_VG_ERRORS(); } static cairo_status_t _vg_setup_linear_source (cairo_vg_context_t *context, const cairo_linear_pattern_t *lpat) { VGfloat linear[4]; linear[0] = lpat->pd1.x; linear[1] = lpat->pd1.y; linear[2] = lpat->pd2.x; linear[3] = lpat->pd2.y; vgSetParameteri (context->paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); vgSetParameterfv (context->paint, VG_PAINT_LINEAR_GRADIENT, 4, linear); _vg_set_source_matrix (&lpat->base.base); CHECK_VG_ERRORS(); return _vg_setup_gradient_stops (context, &lpat->base); } static cairo_status_t _vg_setup_radial_source (cairo_vg_context_t *context, const cairo_radial_pattern_t *rpat) { VGfloat radial[5]; radial[0] = rpat->cd1.center.x; radial[1] = rpat->cd1.center.y; radial[2] = rpat->cd2.center.x; radial[3] = rpat->cd2.center.y; radial[4] = rpat->cd2.radius; vgSetParameteri (context->paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); vgSetParameterfv (context->paint, VG_PAINT_RADIAL_GRADIENT, 5, radial); _vg_set_source_matrix (&rpat->base.base); /* FIXME: copy/adapt fixes from SVG backend to add inner radius */ CHECK_VG_ERRORS(); return _vg_setup_gradient_stops (context, &rpat->base); } static cairo_status_t _vg_setup_solid_source (cairo_vg_context_t *context, const cairo_solid_pattern_t *spat) { VGfloat color[] = { spat->color.red, spat->color.green, spat->color.blue, spat->color.alpha * context->alpha }; vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_vg_surface_t * _vg_clone_recording_surface (cairo_vg_context_t *context, cairo_surface_t *surface) { VGImage vg_image; VGImageFormat format; cairo_status_t status; cairo_rectangle_int_t extents; cairo_vg_surface_t *clone; status = _cairo_surface_get_extents (surface, &extents); if (status) return NULL; if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) { return NULL; } format = _vg_format_for_content (surface->content); /* NONALIASED, FASTER, BETTER */ vg_image = vgCreateImage (format, extents.width, extents.height, VG_IMAGE_QUALITY_FASTER); clone = (cairo_vg_surface_t *) _vg_surface_create_internal (context, vg_image, format, extents.width, extents.height); cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y); status = _cairo_recording_surface_replay (surface, &clone->base); if (unlikely (status)) { cairo_surface_destroy (&clone->base); return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); } return clone; } static cairo_vg_surface_t * _vg_clone_image_surface (cairo_vg_context_t *context, cairo_surface_t *surface) { cairo_image_surface_t *image; void *image_extra; cairo_status_t status; VGImage vg_image; VGImageFormat format; cairo_rectangle_int_t extents; cairo_vg_surface_t *clone; if (surface->backend->acquire_source_image == NULL) return NULL; status = _cairo_surface_get_extents (surface, &extents); if (status) return NULL; if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) { return NULL; } status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (unlikely (status)) return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); format = _vg_format_from_pixman (image->pixman_format); if (format == 0) format = _vg_format_for_content (image->base.content); /* NONALIASED, FASTER, BETTER */ vg_image = vgCreateImage (format, image->width, image->height, VG_IMAGE_QUALITY_FASTER); clone = (cairo_vg_surface_t *) _vg_surface_create_internal (context, vg_image, format, image->width, image->height); if (unlikely (clone->base.status)) return clone; vgImageSubData (clone->image, image->data, image->stride, format, 0, 0, image->width, image->height); _cairo_surface_release_source_image (surface, image, image_extra); return clone; } static void _vg_surface_remove_from_cache (cairo_surface_t *abstract_surface) { cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface; if (surface->snapshot_cache_entry.hash) { cairo_vg_context_t *context; context = _vg_context_lock (surface->context); _cairo_cache_remove (&context->snapshot_cache, &surface->snapshot_cache_entry); _vg_context_unlock (context); surface->snapshot_cache_entry.hash = 0; } } static cairo_status_t _vg_setup_surface_source (cairo_vg_context_t *context, const cairo_surface_pattern_t *spat) { cairo_surface_t *snapshot; cairo_vg_surface_t *clone; cairo_status_t status; snapshot = _cairo_surface_has_snapshot (spat->surface, &cairo_vg_surface_backend); if (snapshot != NULL) { clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot); goto DONE; } if (_cairo_surface_is_recording (spat->surface)) clone = _vg_clone_recording_surface (context, spat->surface); else clone = _vg_clone_image_surface (context, spat->surface); if (clone == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (clone->base.status)) return clone->base.status; clone->snapshot_cache_entry.hash = clone->base.unique_id; status = _cairo_cache_insert (&context->snapshot_cache, &clone->snapshot_cache_entry); if (unlikely (status)) { clone->snapshot_cache_entry.hash = 0; cairo_surface_destroy (&clone->base); return status; } _cairo_surface_attach_snapshot (spat->surface, &clone->base, _vg_surface_remove_from_cache); DONE: cairo_surface_destroy (&context->source->base); context->source = clone; vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); switch (spat->base.extend) { case CAIRO_EXTEND_PAD: vgSetParameteri (context->paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_PAD); break; case CAIRO_EXTEND_NONE: vgSetParameteri (context->paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_FILL); { VGfloat color[] = {0,0,0,0}; vgSetfv (VG_TILE_FILL_COLOR, 4, color); } break; case CAIRO_EXTEND_REPEAT: vgSetParameteri (context->paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT); break; default: ASSERT_NOT_REACHED; case CAIRO_EXTEND_REFLECT: vgSetParameteri (context->paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REFLECT); break; } vgPaintPattern (context->paint, context->source->image); _vg_set_source_matrix (&spat->base); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_status_t setup_source (cairo_vg_context_t *context, const cairo_pattern_t *source) { switch (source->type) { case CAIRO_PATTERN_TYPE_SOLID: return _vg_setup_solid_source (context, (cairo_solid_pattern_t *) source); case CAIRO_PATTERN_TYPE_LINEAR: return _vg_setup_linear_source (context, (cairo_linear_pattern_t *) source); case CAIRO_PATTERN_TYPE_RADIAL: return _vg_setup_radial_source (context, (cairo_radial_pattern_t *) source); case CAIRO_PATTERN_TYPE_SURFACE: return _vg_setup_surface_source (context, (cairo_surface_pattern_t *) source); default: ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } } static cairo_int_status_t _vg_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; cairo_status_t status; VGfloat state[9]; VGfloat strokeTransform[9]; vg_path_t vg_path; if (! _vg_is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; context = _vg_context_lock (surface->context); status = _vg_context_set_target (context, surface); if (status) { _vg_context_unlock (context); return status; } status = setup_source (context, source); if (status) { _vg_context_unlock (context); return status; } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) { _vg_context_unlock (context); return status; } vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); vgGetMatrix (state); _vg_matrix_from_cairo (strokeTransform, ctm); vgMultMatrix (strokeTransform); vg_path.ctm_inverse = ctm_inverse; _vg_path_from_cairo (&vg_path, path); /* XXX DASH_PATTERN, DASH_PHASE */ vgSetf (VG_STROKE_LINE_WIDTH, style->line_width); vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit); vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join)); vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap)); vgSeti (VG_BLEND_MODE, _vg_operator (op)); vgSetPaint (context->paint, VG_STROKE_PATH); vgDrawPath (vg_path.path, VG_STROKE_PATH); vgDestroyPath (vg_path.path); vgLoadMatrix (state); CHECK_VG_ERRORS(); _vg_context_unlock (context); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _vg_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; cairo_status_t status; vg_path_t vg_path; if (op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (! _vg_is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; context = _vg_context_lock (surface->context); status = _vg_context_set_target (context, surface); if (status) { _vg_context_unlock (context); return status; } status = setup_source (context, source); if (status) { _vg_context_unlock (context); return status; } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) { _vg_context_unlock (context); return status; } vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1, 0, 0, 0, VG_PATH_CAPABILITY_ALL); vg_path.ctm_inverse = NULL; _vg_path_from_cairo (&vg_path, path); /* XXX tolerance */ vgSeti (VG_BLEND_MODE, _vg_operator (op)); vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule)); vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias)); vgSetPaint (context->paint, VG_FILL_PATH); vgDrawPath (vg_path.path, VG_FILL_PATH); vgDestroyPath (vg_path.path); _vg_context_unlock (context); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _vg_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; cairo_status_t status; if (op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; if (! _vg_is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; context = _vg_context_lock (surface->context); status = _vg_context_set_target (context, surface); if (status) { _vg_context_unlock (context); return status; } status = setup_source (context, source); if (status) { _vg_context_unlock (context); return status; } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) { _vg_context_unlock (context); return status; } vgSeti (VG_BLEND_MODE, _vg_operator (op)); vgSetPaint (context->paint, VG_FILL_PATH); { /* creating a rectangular path that should cover the extent */ VGubyte segs[] = { VG_MOVE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_LINE_TO_ABS, VG_CLOSE_PATH }; VGfloat data[] = { 0, 0, surface->width, 0, surface->width, surface->height, 0, surface->height }; VGPath fullext; fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1,0,0,0, VG_PATH_CAPABILITY_ALL); vgAppendPathData (fullext, sizeof(segs), segs, data); vgDrawPath (fullext, VG_FILL_PATH); vgDestroyPath (fullext); } _vg_context_unlock (context); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _vg_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_status_t status; if (! _vg_is_supported_operator (op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Handle paint-with-alpha to do fades cheaply */ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; cairo_vg_context_t *context = _vg_context_lock (surface->context); double alpha = context->alpha; context->alpha = solid->color.alpha; status = _vg_surface_paint (abstract_surface, op, source, clip); context->alpha = alpha; _vg_context_unlock (context); return status; } return CAIRO_INT_STATUS_UNSUPPORTED; } static void _vg_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static cairo_int_status_t _vg_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_path_fixed_t path; if (num_glyphs <= 0) return CAIRO_STATUS_SUCCESS; _cairo_path_fixed_init (&path); /* XXX Glyph cache! OpenVG font support in 1.1? */ status = _cairo_scaled_font_glyph_path (scaled_font, glyphs, num_glyphs, &path); if (unlikely (status)) goto BAIL; status = _vg_surface_fill (abstract_surface, op, source, &path, CAIRO_FILL_RULE_WINDING, CAIRO_GSTATE_TOLERANCE_DEFAULT, CAIRO_ANTIALIAS_DEFAULT, clip); BAIL: _cairo_path_fixed_fini (&path); return status; } static inline int multiply_alpha (int alpha, int color) { int temp = alpha * color + 0x80; return (temp + (temp >> 8)) >> 8; } static void premultiply_argb (uint8_t *data, int width, int height, int stride) { int i; while (height --) { uint32_t *row = (uint32_t *) data; for (i = 0; i < width; i++) { uint32_t p = row[i]; uint8_t alpha; alpha = p >> 24; if (alpha == 0) { row[i] = 0; } else if (alpha != 0xff) { uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff); uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff); uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff); row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); } } data += stride; } } static cairo_int_status_t _vg_get_image (cairo_vg_surface_t *surface, int x, int y, int width, int height, cairo_image_surface_t **image_out) { cairo_image_surface_t *image; pixman_image_t *pixman_image; pixman_format_code_t pixman_format; cairo_bool_t needs_premultiply; pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply); if (pixman_format == 0) return CAIRO_INT_STATUS_UNSUPPORTED; pixman_image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); if (unlikely (pixman_image == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); vgFinish (); CHECK_VG_ERRORS(); vgGetImageSubData (surface->image, pixman_image_get_data (pixman_image), pixman_image_get_stride (pixman_image), surface->format, x, y, width, height); image = (cairo_image_surface_t *) _cairo_image_surface_create_for_pixman_image (pixman_image, pixman_format); if (unlikely (image->base.status)) { pixman_image_unref (pixman_image); return image->base.status; } if (needs_premultiply) premultiply_argb (image->data, width, height, image->stride); *image_out = image; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _vg_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_vg_surface_t *surface = abstract_surface; CHECK_VG_ERRORS(); *image_extra = NULL; return _vg_get_image (surface, 0, 0, surface->width, surface->height, image_out); } static void _vg_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_status_t _vg_surface_finish (void *abstract_surface) { cairo_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context = _vg_context_lock (surface->context); if (surface->snapshot_cache_entry.hash) { _cairo_cache_remove (&context->snapshot_cache, &surface->snapshot_cache_entry); surface->snapshot_cache_entry.hash = 0; } _cairo_surface_clipper_reset (&surface->clipper); if (surface->own_image) vgDestroyImage (surface->image); _vg_context_destroy_target (context, surface); _vg_context_unlock (context); _vg_context_destroy (context); CHECK_VG_ERRORS(); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_vg_surface_backend = { CAIRO_SURFACE_TYPE_VG, _vg_surface_finish, _cairo_default_context_create, /* XXX */ _vg_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, _vg_surface_acquire_source_image, _vg_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _vg_surface_get_extents, _vg_surface_get_font_options, /* get_font_options */ NULL, /* flush */ NULL, /* mark dirty */ _vg_surface_paint, _vg_surface_mask, _vg_surface_stroke, _vg_surface_fill, NULL, /* fill-stroke */ _vg_surface_show_glyphs, }; static cairo_surface_t * _vg_surface_create_internal (cairo_vg_context_t *context, VGImage image, VGImageFormat format, int width, int height) { cairo_vg_surface_t *surface; surface = malloc (sizeof (cairo_vg_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->context = _vg_context_reference (context); surface->image = image; surface->format = format; _cairo_surface_init (&surface->base, &cairo_vg_surface_backend, NULL, /* device */ _vg_format_to_content (format)); surface->width = width; surface->height = height; _cairo_surface_clipper_init (&surface->clipper, _vg_surface_clipper_intersect_clip_path); surface->snapshot_cache_entry.hash = 0; surface->target_id = 0; CHECK_VG_ERRORS(); return &surface->base; } cairo_surface_t * cairo_vg_surface_create_for_image (cairo_vg_context_t *context, VGImage image, VGImageFormat format, int width, int height) { cairo_bool_t premult; if (context->status) return _cairo_surface_create_in_error (context->status); if (image == VG_INVALID_HANDLE) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); if (_vg_format_to_pixman (format, &premult) == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); return _vg_surface_create_internal (context, image, format, width, height); } cairo_surface_t * cairo_vg_surface_create (cairo_vg_context_t *context, cairo_content_t content, int width, int height) { VGImage image; VGImageFormat format; cairo_surface_t *surface; if (context->status) return _cairo_surface_create_in_error (context->status); if (! CAIRO_CONTENT_VALID (content)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || height > vgGeti (VG_MAX_IMAGE_HEIGHT)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } format = _vg_format_for_content (content); image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER); if (image == VG_INVALID_HANDLE) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface = _vg_surface_create_internal (context, image, format, width, height); if (unlikely (surface->status)) return surface; ((cairo_vg_surface_t *) surface)->own_image = TRUE; return surface; } slim_hidden_def (cairo_vg_surface_create); VGImage cairo_vg_surface_get_image (cairo_surface_t *abstract_surface) { cairo_vg_surface_t *surface; if (abstract_surface->backend != &cairo_vg_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return VG_INVALID_HANDLE; } surface = (cairo_vg_surface_t *) abstract_surface; return surface->image; } int cairo_vg_surface_get_width (cairo_surface_t *abstract_surface) { cairo_vg_surface_t *surface; if (abstract_surface->backend != &cairo_vg_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } surface = (cairo_vg_surface_t *) abstract_surface; return surface->width; } int cairo_vg_surface_get_height (cairo_surface_t *abstract_surface) { cairo_vg_surface_t *surface; if (abstract_surface->backend != &cairo_vg_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } surface = (cairo_vg_surface_t *) abstract_surface; return surface->height; } VGImageFormat cairo_vg_surface_get_format (cairo_surface_t *abstract_surface) { cairo_vg_surface_t *surface; if (abstract_surface->backend != &cairo_vg_surface_backend) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } surface = (cairo_vg_surface_t *) abstract_surface; return surface->format; } /* GL specific context support :-( * * OpenVG like cairo defers creation of surface (and the necessary * paraphernalia to the application. */ static const cairo_vg_context_t _vg_context_nil = { CAIRO_STATUS_NO_MEMORY, CAIRO_REFERENCE_COUNT_INVALID }; static const cairo_vg_context_t _vg_context_nil_invalid_visual = { CAIRO_STATUS_INVALID_VISUAL, CAIRO_REFERENCE_COUNT_INVALID }; #if CAIRO_HAS_GLX_FUNCTIONS #include static cairo_status_t glx_create_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { /* XXX hmm, magic required for creating an FBO points to VGImage! */ return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t glx_set_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { #if 0 glXMakeContextCurrent (context->display, (GLXDrawable) surface->target_id, (GLXDrawable) surface->target_id, context->context); #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } static void glx_destroy_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { } cairo_vg_context_t * cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx) { cairo_vg_context_t *context; cairo_status_t status; context = malloc (sizeof (*context)); if (unlikely (context == NULL)) return (cairo_vg_context_t *) &_vg_context_nil; context->display = dpy; context->context = ctx; context->create_target = glx_create_target; context->set_target = glx_set_target; context->destroy_target = glx_destroy_target; status = _vg_context_init (context); if (unlikely (status)) { free (context); return (cairo_vg_context_t *) &_vg_context_nil; } return context; } #endif #if CAIRO_HAS_EGL_FUNCTIONS static cairo_status_t egl_create_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { EGLSurface *egl_surface; #define RED 1 #define GREEN 3 #define BLUE 5 #define ALPHA 7 int attribs[] = { EGL_RED_SIZE, 0, EGL_GREEN_SIZE, 0, EGL_BLUE_SIZE, 0, EGL_ALPHA_SIZE, 0, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, EGL_NONE }; pixman_format_code_t pixman_format; EGLConfig config; int num_configs = 0; cairo_bool_t needs_premultiply; pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply); if (pixman_format == 0) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX no control over pixel ordering! */ attribs[RED] = PIXMAN_FORMAT_R (pixman_format); attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format); attribs[BLUE] = PIXMAN_FORMAT_B (pixman_format); attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format); if (! eglChooseConfig (context->display, attribs, &config, 1, &num_configs) || num_configs != 1) { fprintf(stderr, "Error: eglChooseConfig() failed.\n"); return CAIRO_INT_STATUS_UNSUPPORTED; } egl_surface = eglCreatePbufferFromClientBuffer (context->display, EGL_OPENVG_IMAGE, (EGLClientBuffer) surface->image, config, NULL); surface->target_id = (unsigned long) egl_surface; return CAIRO_STATUS_SUCCESS; } static cairo_status_t egl_set_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { if (! eglMakeCurrent (context->display, (EGLSurface *) surface->target_id, (EGLSurface *) surface->target_id, context->context)) { return _cairo_error (CAIRO_STATUS_NO_MEMORY); } return CAIRO_STATUS_SUCCESS; } static void egl_destroy_target (cairo_vg_context_t *context, cairo_vg_surface_t *surface) { eglDestroySurface (context->display, (EGLSurface *) surface->target_id); } cairo_vg_context_t * cairo_vg_context_create_for_egl (EGLDisplay egl_display, EGLContext egl_context) { cairo_vg_context_t *context; cairo_status_t status; context = malloc (sizeof (*context)); if (unlikely (context == NULL)) return (cairo_vg_context_t *) &_vg_context_nil; status = _vg_context_init (context); if (unlikely (status)) { free (context); return (cairo_vg_context_t *) &_vg_context_nil; } context->display = egl_display; context->context = egl_context; context->create_target = egl_create_target; context->set_target = egl_set_target; context->destroy_target = egl_destroy_target; return context; } #endif cairo_status_t cairo_vg_context_status (cairo_vg_context_t *context) { return context->status; } void cairo_vg_context_destroy (cairo_vg_context_t *context) { if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count)) return; _vg_context_destroy (context); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-vg.h000066400000000000000000000062651271037650300241710ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2007 * Mozilla Corporation * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): * Vladimir Vukicevic * Chris Wilson */ #ifndef CAIRO_VG_H #define CAIRO_VG_H #include "cairo.h" #if CAIRO_HAS_VG_SURFACE #include CAIRO_BEGIN_DECLS typedef struct _cairo_vg_context cairo_vg_context_t; #if CAIRO_HAS_GLX_FUNCTIONS typedef struct __GLXcontextRec *GLXContext; typedef struct _XDisplay Display; cairo_public cairo_vg_context_t * cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx); #endif #if CAIRO_HAS_EGL_FUNCTIONS #include cairo_public cairo_vg_context_t * cairo_vg_context_create_for_egl (EGLDisplay egl_display, EGLContext egl_context); #endif cairo_public cairo_status_t cairo_vg_context_status (cairo_vg_context_t *context); cairo_public void cairo_vg_context_destroy (cairo_vg_context_t *context); cairo_public cairo_surface_t * cairo_vg_surface_create (cairo_vg_context_t *context, cairo_content_t content, int width, int height); cairo_public cairo_surface_t * cairo_vg_surface_create_for_image (cairo_vg_context_t *context, VGImage image, VGImageFormat format, int width, int height); cairo_public VGImage cairo_vg_surface_get_image (cairo_surface_t *abstract_surface); cairo_public VGImageFormat cairo_vg_surface_get_format (cairo_surface_t *abstract_surface); cairo_public int cairo_vg_surface_get_height (cairo_surface_t *abstract_surface); cairo_public int cairo_vg_surface_get_width (cairo_surface_t *abstract_surface); CAIRO_END_DECLS #else /* CAIRO_HAS_VG_SURFACE*/ # error Cairo was not compiled with support for the OpenVG backend #endif /* CAIRO_HAS_VG_SURFACE*/ #endif /* CAIRO_VG_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-wgl-context.c000066400000000000000000000160401271037650300260130ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Eric Anholt * Copyright © 2009 Chris Wilson * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl Worth * Chris Wilson * Zoxc */ #include "cairoint.h" #include "cairo-gl-private.h" #include "cairo-error-private.h" #define WIN32_LEAN_AND_MEAN #include typedef struct _cairo_wgl_context { cairo_gl_context_t base; HDC dummy_dc; HWND dummy_wnd; HGLRC rc; HDC prev_dc; HGLRC prev_rc; } cairo_wgl_context_t; typedef struct _cairo_wgl_surface { cairo_gl_surface_t base; HDC dc; } cairo_wgl_surface_t; static void _wgl_acquire (void *abstract_ctx) { cairo_wgl_context_t *ctx = abstract_ctx; HDC current_dc; ctx->prev_dc = wglGetCurrentDC (); ctx->prev_rc = wglGetCurrentContext (); if (ctx->base.current_target == NULL || _cairo_gl_surface_is_texture (ctx->base.current_target)) { current_dc = ctx->dummy_dc; } else { cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) ctx->base.current_target; current_dc = surface->dc; } if (ctx->prev_dc != current_dc || (ctx->prev_rc != ctx->rc && current_dc != ctx->dummy_dc)) { wglMakeCurrent (current_dc, ctx->rc); } } static void _wgl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_wgl_context_t *ctx = abstract_ctx; cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface; /* Set the window as the target of our context. */ wglMakeCurrent (surface->dc, ctx->rc); } static void _wgl_release (void *abstract_ctx) { cairo_wgl_context_t *ctx = abstract_ctx; if (ctx->prev_dc != wglGetCurrentDC () || ctx->prev_rc != wglGetCurrentContext ()) { wglMakeCurrent (ctx->prev_dc, ctx->prev_rc); } } static void _wgl_swap_buffers (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) { cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface; SwapBuffers (surface->dc); } static void _wgl_destroy (void *abstract_ctx) { cairo_wgl_context_t *ctx = abstract_ctx; if (ctx->dummy_dc != 0) { wglMakeCurrent (ctx->dummy_dc, 0); ReleaseDC (ctx->dummy_wnd, ctx->dummy_dc); DestroyWindow (ctx->dummy_wnd); } } static cairo_status_t _wgl_dummy_ctx (cairo_wgl_context_t *ctx) { WNDCLASSEXA wincl; PIXELFORMATDESCRIPTOR pfd; int format; HDC dc; ZeroMemory (&wincl, sizeof (WNDCLASSEXA)); wincl.cbSize = sizeof (WNDCLASSEXA); wincl.hInstance = GetModuleHandle (0); wincl.lpszClassName = "cairo_wgl_context_dummy"; wincl.lpfnWndProc = DefWindowProcA; wincl.style = CS_OWNDC; RegisterClassExA (&wincl); ctx->dummy_wnd = CreateWindowA ("cairo_wgl_context_dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); ctx->dummy_dc = GetDC (ctx->dummy_wnd); ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE; format = ChoosePixelFormat (ctx->dummy_dc, &pfd); SetPixelFormat (ctx->dummy_dc, format, &pfd); wglMakeCurrent(ctx->dummy_dc, ctx->rc); return CAIRO_STATUS_SUCCESS; } cairo_device_t * cairo_wgl_device_create (HGLRC rc) { cairo_wgl_context_t *ctx; cairo_status_t status; ctx = calloc (1, sizeof (cairo_wgl_context_t)); if (unlikely (ctx == NULL)) return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); ctx->rc = rc; ctx->prev_dc = 0; ctx->prev_rc = 0; status = _wgl_dummy_ctx (ctx); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } ctx->base.acquire = _wgl_acquire; ctx->base.release = _wgl_release; ctx->base.make_current = _wgl_make_current; ctx->base.swap_buffers = _wgl_swap_buffers; ctx->base.destroy = _wgl_destroy; status = _cairo_gl_dispatch_init (&ctx->base.dispatch, (cairo_gl_get_proc_addr_func_t) wglGetProcAddress); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } status = _cairo_gl_context_init (&ctx->base); if (unlikely (status)) { free (ctx); return _cairo_gl_context_create_in_error (status); } ctx->base.release (ctx); return &ctx->base.base; } HGLRC cairo_wgl_device_get_context (cairo_device_t *device) { cairo_wgl_context_t *ctx; if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); return NULL; } ctx = (cairo_wgl_context_t *) device; return ctx->rc; } cairo_surface_t * cairo_gl_surface_create_for_dc (cairo_device_t *device, HDC dc, int width, int height) { cairo_wgl_surface_t *surface; if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); if (device->backend->type != CAIRO_DEVICE_TYPE_GL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); if (width <= 0 || height <= 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); surface = calloc (1, sizeof (cairo_wgl_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_gl_surface_init (device, &surface->base, CAIRO_CONTENT_COLOR_ALPHA, width, height); surface->dc = dc; return &surface->base.base; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-wideint-private.h000066400000000000000000000341331271037650300266630ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Keith Packard * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith R. Packard * */ #ifndef CAIRO_WIDEINT_H #define CAIRO_WIDEINT_H #include "cairo-wideint-type-private.h" #include "cairo-compiler-private.h" /* * 64-bit datatypes. Two separate implementations, one using * built-in 64-bit signed/unsigned types another implemented * as a pair of 32-bit ints */ #define I cairo_private cairo_const #if !HAVE_UINT64_T cairo_uquorem64_t I _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); cairo_uint64_t I _cairo_double_to_uint64 (double i); double I _cairo_uint64_to_double (cairo_uint64_t i); cairo_int64_t I _cairo_double_to_int64 (double i); double I _cairo_int64_to_double (cairo_uint64_t i); cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i); #define _cairo_uint64_to_uint32(a) ((a).lo) cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b); cairo_uint64_t I _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b); cairo_uint64_t I _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b); cairo_uint64_t I _cairo_uint32x32_64_mul (uint32_t a, uint32_t b); cairo_uint64_t I _cairo_uint64_lsl (cairo_uint64_t a, int shift); cairo_uint64_t I _cairo_uint64_rsl (cairo_uint64_t a, int shift); cairo_uint64_t I _cairo_uint64_rsa (cairo_uint64_t a, int shift); int I _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b); int I _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b); int I _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b); cairo_uint64_t I _cairo_uint64_negate (cairo_uint64_t a); #define _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0) #define _cairo_uint64_negative(a) (((int32_t) ((a).hi)) < 0) cairo_uint64_t I _cairo_uint64_not (cairo_uint64_t a); #define _cairo_uint64_to_int64(i) (i) #define _cairo_int64_to_uint64(i) (i) cairo_int64_t I _cairo_int32_to_int64(int32_t i); #define _cairo_int64_to_int32(a) ((int32_t) _cairo_uint64_to_uint32(a)) #define _cairo_int64_add(a,b) _cairo_uint64_add (a,b) #define _cairo_int64_sub(a,b) _cairo_uint64_sub (a,b) #define _cairo_int64_mul(a,b) _cairo_uint64_mul (a,b) cairo_int64_t I _cairo_int32x32_64_mul (int32_t a, int32_t b); int I _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b); int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); #define _cairo_int64_is_zero(a) _cairo_uint64_is_zero (a) #define _cairo_int64_eq(a,b) _cairo_uint64_eq (a,b) #define _cairo_int64_lsl(a,b) _cairo_uint64_lsl (a,b) #define _cairo_int64_rsl(a,b) _cairo_uint64_rsl (a,b) #define _cairo_int64_rsa(a,b) _cairo_uint64_rsa (a,b) #define _cairo_int64_negate(a) _cairo_uint64_negate(a) #define _cairo_int64_negative(a) (((int32_t) ((a).hi)) < 0) #define _cairo_int64_not(a) _cairo_uint64_not(a) #else static inline cairo_uquorem64_t _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) { cairo_uquorem64_t qr; qr.quo = num / den; qr.rem = num % den; return qr; } /* * These need to be functions or gcc will complain when used on the * result of a function: * * warning: cast from function call of type ‘#cairo_uint64_t’ to * non-matching type ‘double’ */ static cairo_always_inline cairo_const cairo_uint64_t _cairo_double_to_uint64 (double i) { return i; } static cairo_always_inline cairo_const double _cairo_uint64_to_double (cairo_uint64_t i) { return i; } static cairo_always_inline cairo_int64_t I _cairo_double_to_int64 (double i) { return i; } static cairo_always_inline double I _cairo_int64_to_double (cairo_int64_t i) { return i; } #define _cairo_uint32_to_uint64(i) ((uint64_t) (i)) #define _cairo_uint64_to_uint32(i) ((uint32_t) (i)) #define _cairo_uint64_add(a,b) ((a) + (b)) #define _cairo_uint64_sub(a,b) ((a) - (b)) #define _cairo_uint64_mul(a,b) ((a) * (b)) #define _cairo_uint32x32_64_mul(a,b) ((uint64_t) (a) * (b)) #define _cairo_uint64_lsl(a,b) ((a) << (b)) #define _cairo_uint64_rsl(a,b) ((uint64_t) (a) >> (b)) #define _cairo_uint64_rsa(a,b) ((uint64_t) ((int64_t) (a) >> (b))) #define _cairo_uint64_lt(a,b) ((a) < (b)) #define _cairo_uint64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) #define _cairo_uint64_is_zero(a) ((a) == 0) #define _cairo_uint64_eq(a,b) ((a) == (b)) #define _cairo_uint64_negate(a) ((uint64_t) -((int64_t) (a))) #define _cairo_uint64_negative(a) ((int64_t) (a) < 0) #define _cairo_uint64_not(a) (~(a)) #define _cairo_uint64_to_int64(i) ((int64_t) (i)) #define _cairo_int64_to_uint64(i) ((uint64_t) (i)) #define _cairo_int32_to_int64(i) ((int64_t) (i)) #define _cairo_int64_to_int32(i) ((int32_t) (i)) #define _cairo_int64_add(a,b) ((a) + (b)) #define _cairo_int64_sub(a,b) ((a) - (b)) #define _cairo_int64_mul(a,b) ((a) * (b)) #define _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b)) #define _cairo_int64_lt(a,b) ((a) < (b)) #define _cairo_int64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) #define _cairo_int64_is_zero(a) ((a) == 0) #define _cairo_int64_eq(a,b) ((a) == (b)) #define _cairo_int64_lsl(a,b) ((a) << (b)) #define _cairo_int64_rsl(a,b) ((int64_t) ((uint64_t) (a) >> (b))) #define _cairo_int64_rsa(a,b) ((int64_t) (a) >> (b)) #define _cairo_int64_negate(a) (-(a)) #define _cairo_int64_negative(a) ((a) < 0) #define _cairo_int64_not(a) (~(a)) #endif /* * 64-bit comparisons derived from lt or eq */ #define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b)) #define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b)) #define _cairo_uint64_ge(a,b) (!_cairo_uint64_lt(a,b)) #define _cairo_uint64_gt(a,b) _cairo_uint64_lt(b,a) #define _cairo_int64_le(a,b) (!_cairo_int64_gt(a,b)) #define _cairo_int64_ne(a,b) (!_cairo_int64_eq(a,b)) #define _cairo_int64_ge(a,b) (!_cairo_int64_lt(a,b)) #define _cairo_int64_gt(a,b) _cairo_int64_lt(b,a) /* * As the C implementation always computes both, create * a function which returns both for the 'native' type as well */ static inline cairo_quorem64_t _cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) { int num_neg = _cairo_int64_negative (num); int den_neg = _cairo_int64_negative (den); cairo_uquorem64_t uqr; cairo_quorem64_t qr; if (num_neg) num = _cairo_int64_negate (num); if (den_neg) den = _cairo_int64_negate (den); uqr = _cairo_uint64_divrem (num, den); if (num_neg) qr.rem = _cairo_int64_negate (uqr.rem); else qr.rem = uqr.rem; if (num_neg != den_neg) qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); else qr.quo = (cairo_int64_t) uqr.quo; return qr; } static inline int32_t _cairo_int64_32_div (cairo_int64_t num, int32_t den) { #if !HAVE_UINT64_T return _cairo_int64_to_int32 (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo); #else return num / den; #endif } /* * 128-bit datatypes. Again, provide two implementations in * case the machine has a native 128-bit datatype. GCC supports int128_t * on ia64 */ #if !HAVE_UINT128_T cairo_uint128_t I _cairo_uint32_to_uint128 (uint32_t i); cairo_uint128_t I _cairo_uint64_to_uint128 (cairo_uint64_t i); #define _cairo_uint128_to_uint64(a) ((a).lo) #define _cairo_uint128_to_uint32(a) _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a)) cairo_uint128_t I _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b); cairo_uint128_t I _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b); cairo_uint128_t I _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b); cairo_uint128_t I _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b); cairo_uint128_t I _cairo_uint128_lsl (cairo_uint128_t a, int shift); cairo_uint128_t I _cairo_uint128_rsl (cairo_uint128_t a, int shift); cairo_uint128_t I _cairo_uint128_rsa (cairo_uint128_t a, int shift); int I _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b); int I _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b); int I _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b); #define _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo)) cairo_uint128_t I _cairo_uint128_negate (cairo_uint128_t a); #define _cairo_uint128_negative(a) (_cairo_uint64_negative(a.hi)) cairo_uint128_t I _cairo_uint128_not (cairo_uint128_t a); #define _cairo_uint128_to_int128(i) (i) #define _cairo_int128_to_uint128(i) (i) cairo_int128_t I _cairo_int32_to_int128 (int32_t i); cairo_int128_t I _cairo_int64_to_int128 (cairo_int64_t i); #define _cairo_int128_to_int64(a) ((cairo_int64_t) (a).lo) #define _cairo_int128_to_int32(a) _cairo_int64_to_int32(_cairo_int128_to_int64(a)) #define _cairo_int128_add(a,b) _cairo_uint128_add(a,b) #define _cairo_int128_sub(a,b) _cairo_uint128_sub(a,b) #define _cairo_int128_mul(a,b) _cairo_uint128_mul(a,b) cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b); #define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) #define _cairo_int128_lsl(a,b) _cairo_uint128_lsl(a,b) #define _cairo_int128_rsl(a,b) _cairo_uint128_rsl(a,b) #define _cairo_int128_rsa(a,b) _cairo_uint128_rsa(a,b) int I _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b); int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b); #define _cairo_int128_is_zero(a) _cairo_uint128_is_zero (a) #define _cairo_int128_eq(a,b) _cairo_uint128_eq (a,b) #define _cairo_int128_negate(a) _cairo_uint128_negate(a) #define _cairo_int128_negative(a) (_cairo_uint128_negative(a)) #define _cairo_int128_not(a) _cairo_uint128_not(a) #else /* !HAVE_UINT128_T */ #define _cairo_uint32_to_uint128(i) ((uint128_t) (i)) #define _cairo_uint64_to_uint128(i) ((uint128_t) (i)) #define _cairo_uint128_to_uint64(i) ((uint64_t) (i)) #define _cairo_uint128_to_uint32(i) ((uint32_t) (i)) #define _cairo_uint128_add(a,b) ((a) + (b)) #define _cairo_uint128_sub(a,b) ((a) - (b)) #define _cairo_uint128_mul(a,b) ((a) * (b)) #define _cairo_uint64x64_128_mul(a,b) ((uint128_t) (a) * (b)) #define _cairo_uint128_lsl(a,b) ((a) << (b)) #define _cairo_uint128_rsl(a,b) ((uint128_t) (a) >> (b)) #define _cairo_uint128_rsa(a,b) ((uint128_t) ((int128_t) (a) >> (b))) #define _cairo_uint128_lt(a,b) ((a) < (b)) #define _cairo_uint128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) #define _cairo_uint128_is_zero(a) ((a) == 0) #define _cairo_uint128_eq(a,b) ((a) == (b)) #define _cairo_uint128_negate(a) ((uint128_t) -((int128_t) (a))) #define _cairo_uint128_negative(a) ((int128_t) (a) < 0) #define _cairo_uint128_not(a) (~(a)) #define _cairo_uint128_to_int128(i) ((int128_t) (i)) #define _cairo_int128_to_uint128(i) ((uint128_t) (i)) #define _cairo_int32_to_int128(i) ((int128_t) (i)) #define _cairo_int64_to_int128(i) ((int128_t) (i)) #define _cairo_int128_to_int64(i) ((int64_t) (i)) #define _cairo_int128_to_int32(i) ((int32_t) (i)) #define _cairo_int128_add(a,b) ((a) + (b)) #define _cairo_int128_sub(a,b) ((a) - (b)) #define _cairo_int128_mul(a,b) ((a) * (b)) #define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b)) #define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) #define _cairo_int128_lt(a,b) ((a) < (b)) #define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) #define _cairo_int128_is_zero(a) ((a) == 0) #define _cairo_int128_eq(a,b) ((a) == (b)) #define _cairo_int128_lsl(a,b) ((a) << (b)) #define _cairo_int128_rsl(a,b) ((int128_t) ((uint128_t) (a) >> (b))) #define _cairo_int128_rsa(a,b) ((int128_t) (a) >> (b)) #define _cairo_int128_negate(a) (-(a)) #define _cairo_int128_negative(a) ((a) < 0) #define _cairo_int128_not(a) (~(a)) #endif /* HAVE_UINT128_T */ cairo_uquorem128_t I _cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den); cairo_quorem128_t I _cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den); cairo_uquorem64_t I _cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, cairo_uint64_t den); cairo_quorem64_t I _cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den); #define _cairo_uint128_le(a,b) (!_cairo_uint128_gt(a,b)) #define _cairo_uint128_ne(a,b) (!_cairo_uint128_eq(a,b)) #define _cairo_uint128_ge(a,b) (!_cairo_uint128_lt(a,b)) #define _cairo_uint128_gt(a,b) _cairo_uint128_lt(b,a) #define _cairo_int128_le(a,b) (!_cairo_int128_gt(a,b)) #define _cairo_int128_ne(a,b) (!_cairo_int128_eq(a,b)) #define _cairo_int128_ge(a,b) (!_cairo_int128_lt(a,b)) #define _cairo_int128_gt(a,b) _cairo_int128_lt(b,a) #undef I #endif /* CAIRO_WIDEINT_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-wideint-type-private.h000066400000000000000000000075731271037650300276520ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Keith Packard * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith R. Packard * */ #ifndef CAIRO_WIDEINT_TYPE_H #define CAIRO_WIDEINT_TYPE_H #include "cairo.h" #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_STDINT_H # include #elif HAVE_INTTYPES_H # include #elif HAVE_SYS_INT_TYPES_H # include #elif defined(_MSC_VER) typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; # ifndef HAVE_UINT64_T # define HAVE_UINT64_T 1 # endif #else #error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.) #endif #ifndef INT16_MIN # define INT16_MIN (-32767-1) #endif #ifndef INT16_MAX # define INT16_MAX (32767) #endif #ifndef UINT16_MAX # define UINT16_MAX (65535) #endif #ifndef INT32_MIN # define INT32_MIN (-2147483647-1) #endif #ifndef INT32_MAX # define INT32_MAX (2147483647) #endif #ifndef UINT32_MAX # define UINT32_MAX (4294967295U) #endif #if HAVE_BYTESWAP_H # include #endif #ifndef bswap_16 # define bswap_16(p) \ (((((uint16_t)(p)) & 0x00ff) << 8) | \ (((uint16_t)(p)) >> 8)); #endif #ifndef bswap_32 # define bswap_32(p) \ (((((uint32_t)(p)) & 0x000000ff) << 24) | \ ((((uint32_t)(p)) & 0x0000ff00) << 8) | \ ((((uint32_t)(p)) & 0x00ff0000) >> 8) | \ ((((uint32_t)(p))) >> 24)); #endif #if !HAVE_UINT64_T typedef struct _cairo_uint64 { uint32_t lo, hi; } cairo_uint64_t, cairo_int64_t; #else typedef uint64_t cairo_uint64_t; typedef int64_t cairo_int64_t; #endif typedef struct _cairo_uquorem64 { cairo_uint64_t quo; cairo_uint64_t rem; } cairo_uquorem64_t; typedef struct _cairo_quorem64 { cairo_int64_t quo; cairo_int64_t rem; } cairo_quorem64_t; /* gcc has a non-standard name. */ #if HAVE___UINT128_T && !HAVE_UINT128_T typedef __uint128_t uint128_t; typedef __int128_t int128_t; #define HAVE_UINT128_T 1 #endif #if !HAVE_UINT128_T typedef struct cairo_uint128 { cairo_uint64_t lo, hi; } cairo_uint128_t, cairo_int128_t; #else typedef uint128_t cairo_uint128_t; typedef int128_t cairo_int128_t; #endif typedef struct _cairo_uquorem128 { cairo_uint128_t quo; cairo_uint128_t rem; } cairo_uquorem128_t; typedef struct _cairo_quorem128 { cairo_int128_t quo; cairo_int128_t rem; } cairo_quorem128_t; #endif /* CAIRO_WIDEINT_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-wideint.c000066400000000000000000000463631271037650300252160ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2004 Keith Packard * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Keith Packard * * Contributor(s): * Keith R. Packard */ #include "cairoint.h" #if HAVE_UINT64_T #define uint64_lo32(i) ((i) & 0xffffffff) #define uint64_hi32(i) ((i) >> 32) #define uint64_lo(i) ((i) & 0xffffffff) #define uint64_hi(i) ((i) >> 32) #define uint64_shift32(i) ((i) << 32) #define uint64_carry32 (((uint64_t) 1) << 32) #define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) #else #define uint64_lo32(i) ((i).lo) #define uint64_hi32(i) ((i).hi) static cairo_uint64_t uint64_lo (cairo_uint64_t i) { cairo_uint64_t s; s.lo = i.lo; s.hi = 0; return s; } static cairo_uint64_t uint64_hi (cairo_uint64_t i) { cairo_uint64_t s; s.lo = i.hi; s.hi = 0; return s; } static cairo_uint64_t uint64_shift32 (cairo_uint64_t i) { cairo_uint64_t s; s.lo = 0; s.hi = i.lo; return s; } static const cairo_uint64_t uint64_carry32 = { 0, 1 }; cairo_uint64_t _cairo_double_to_uint64 (double i) { cairo_uint64_t q; q.hi = i * (1. / 4294967296.); q.lo = i - q.hi * 4294967296.; return q; } double _cairo_uint64_to_double (cairo_uint64_t i) { return i.hi * 4294967296. + i.lo; } cairo_int64_t _cairo_double_to_int64 (double i) { cairo_uint64_t q; q.hi = i * (1. / INT32_MAX); q.lo = i - q.hi * (double)INT32_MAX; return q; } double _cairo_int64_to_double (cairo_int64_t i) { return i.hi * INT32_MAX + i.lo; } cairo_uint64_t _cairo_uint32_to_uint64 (uint32_t i) { cairo_uint64_t q; q.lo = i; q.hi = 0; return q; } cairo_int64_t _cairo_int32_to_int64 (int32_t i) { cairo_uint64_t q; q.lo = i; q.hi = i < 0 ? -1 : 0; return q; } static cairo_uint64_t _cairo_uint32s_to_uint64 (uint32_t h, uint32_t l) { cairo_uint64_t q; q.lo = l; q.hi = h; return q; } cairo_uint64_t _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b) { cairo_uint64_t s; s.hi = a.hi + b.hi; s.lo = a.lo + b.lo; if (s.lo < a.lo) s.hi++; return s; } cairo_uint64_t _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b) { cairo_uint64_t s; s.hi = a.hi - b.hi; s.lo = a.lo - b.lo; if (s.lo > a.lo) s.hi--; return s; } #define uint32_lo(i) ((i) & 0xffff) #define uint32_hi(i) ((i) >> 16) #define uint32_carry16 ((1) << 16) cairo_uint64_t _cairo_uint32x32_64_mul (uint32_t a, uint32_t b) { cairo_uint64_t s; uint16_t ah, al, bh, bl; uint32_t r0, r1, r2, r3; al = uint32_lo (a); ah = uint32_hi (a); bl = uint32_lo (b); bh = uint32_hi (b); r0 = (uint32_t) al * bl; r1 = (uint32_t) al * bh; r2 = (uint32_t) ah * bl; r3 = (uint32_t) ah * bh; r1 += uint32_hi(r0); /* no carry possible */ r1 += r2; /* but this can carry */ if (r1 < r2) /* check */ r3 += uint32_carry16; s.hi = r3 + uint32_hi(r1); s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0); return s; } cairo_int64_t _cairo_int32x32_64_mul (int32_t a, int32_t b) { cairo_int64_t s; s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b); if (a < 0) s.hi -= b; if (b < 0) s.hi -= a; return s; } cairo_uint64_t _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b) { cairo_uint64_t s; s = _cairo_uint32x32_64_mul (a.lo, b.lo); s.hi += a.lo * b.hi + a.hi * b.lo; return s; } cairo_uint64_t _cairo_uint64_lsl (cairo_uint64_t a, int shift) { if (shift >= 32) { a.hi = a.lo; a.lo = 0; shift -= 32; } if (shift) { a.hi = a.hi << shift | a.lo >> (32 - shift); a.lo = a.lo << shift; } return a; } cairo_uint64_t _cairo_uint64_rsl (cairo_uint64_t a, int shift) { if (shift >= 32) { a.lo = a.hi; a.hi = 0; shift -= 32; } if (shift) { a.lo = a.lo >> shift | a.hi << (32 - shift); a.hi = a.hi >> shift; } return a; } #define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n))) cairo_int64_t _cairo_uint64_rsa (cairo_int64_t a, int shift) { if (shift >= 32) { a.lo = a.hi; a.hi = _cairo_uint32_rsa (a.hi, 31); shift -= 32; } if (shift) { a.lo = a.lo >> shift | a.hi << (32 - shift); a.hi = _cairo_uint32_rsa (a.hi, shift); } return a; } int _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b) { return (a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo)); } int _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b) { return a.hi == b.hi && a.lo == b.lo; } int _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b) { if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) return 1; if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) return 0; return _cairo_uint64_lt (a, b); } int _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b) { if (a.hi < b.hi) return -1; else if (a.hi > b.hi) return 1; else if (a.lo < b.lo) return -1; else if (a.lo > b.lo) return 1; else return 0; } int _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b) { if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) return -1; if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) return 1; return _cairo_uint64_cmp (a, b); } cairo_uint64_t _cairo_uint64_not (cairo_uint64_t a) { a.lo = ~a.lo; a.hi = ~a.hi; return a; } cairo_uint64_t _cairo_uint64_negate (cairo_uint64_t a) { a.lo = ~a.lo; a.hi = ~a.hi; if (++a.lo == 0) ++a.hi; return a; } /* * Simple bit-at-a-time divide. */ cairo_uquorem64_t _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) { cairo_uquorem64_t qr; cairo_uint64_t bit; cairo_uint64_t quo; bit = _cairo_uint32_to_uint64 (1); /* normalize to make den >= num, but not overflow */ while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0) { bit = _cairo_uint64_lsl (bit, 1); den = _cairo_uint64_lsl (den, 1); } quo = _cairo_uint32_to_uint64 (0); /* generate quotient, one bit at a time */ while (bit.hi | bit.lo) { if (_cairo_uint64_le (den, num)) { num = _cairo_uint64_sub (num, den); quo = _cairo_uint64_add (quo, bit); } bit = _cairo_uint64_rsl (bit, 1); den = _cairo_uint64_rsl (den, 1); } qr.quo = quo; qr.rem = num; return qr; } #endif /* !HAVE_UINT64_T */ #if HAVE_UINT128_T cairo_uquorem128_t _cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) { cairo_uquorem128_t qr; qr.quo = num / den; qr.rem = num % den; return qr; } #else cairo_uint128_t _cairo_uint32_to_uint128 (uint32_t i) { cairo_uint128_t q; q.lo = _cairo_uint32_to_uint64 (i); q.hi = _cairo_uint32_to_uint64 (0); return q; } cairo_int128_t _cairo_int32_to_int128 (int32_t i) { cairo_int128_t q; q.lo = _cairo_int32_to_int64 (i); q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0); return q; } cairo_uint128_t _cairo_uint64_to_uint128 (cairo_uint64_t i) { cairo_uint128_t q; q.lo = i; q.hi = _cairo_uint32_to_uint64 (0); return q; } cairo_int128_t _cairo_int64_to_int128 (cairo_int64_t i) { cairo_int128_t q; q.lo = i; q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0); return q; } cairo_uint128_t _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b) { cairo_uint128_t s; s.hi = _cairo_uint64_add (a.hi, b.hi); s.lo = _cairo_uint64_add (a.lo, b.lo); if (_cairo_uint64_lt (s.lo, a.lo)) s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1)); return s; } cairo_uint128_t _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) { cairo_uint128_t s; s.hi = _cairo_uint64_sub (a.hi, b.hi); s.lo = _cairo_uint64_sub (a.lo, b.lo); if (_cairo_uint64_gt (s.lo, a.lo)) s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1)); return s; } cairo_uint128_t _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) { cairo_uint128_t s; uint32_t ah, al, bh, bl; cairo_uint64_t r0, r1, r2, r3; al = uint64_lo32 (a); ah = uint64_hi32 (a); bl = uint64_lo32 (b); bh = uint64_hi32 (b); r0 = _cairo_uint32x32_64_mul (al, bl); r1 = _cairo_uint32x32_64_mul (al, bh); r2 = _cairo_uint32x32_64_mul (ah, bl); r3 = _cairo_uint32x32_64_mul (ah, bh); r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */ r1 = _cairo_uint64_add (r1, r2); /* but this can carry */ if (_cairo_uint64_lt (r1, r2)) /* check */ r3 = _cairo_uint64_add (r3, uint64_carry32); s.hi = _cairo_uint64_add (r3, uint64_hi(r1)); s.lo = _cairo_uint64_add (uint64_shift32 (r1), uint64_lo (r0)); return s; } cairo_int128_t _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b) { cairo_int128_t s; s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a), _cairo_int64_to_uint64(b)); if (_cairo_int64_negative (a)) s.hi = _cairo_uint64_sub (s.hi, _cairo_int64_to_uint64 (b)); if (_cairo_int64_negative (b)) s.hi = _cairo_uint64_sub (s.hi, _cairo_int64_to_uint64 (a)); return s; } cairo_uint128_t _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b) { cairo_uint128_t s; s = _cairo_uint64x64_128_mul (a.lo, b.lo); s.hi = _cairo_uint64_add (s.hi, _cairo_uint64_mul (a.lo, b.hi)); s.hi = _cairo_uint64_add (s.hi, _cairo_uint64_mul (a.hi, b.lo)); return s; } cairo_uint128_t _cairo_uint128_lsl (cairo_uint128_t a, int shift) { if (shift >= 64) { a.hi = a.lo; a.lo = _cairo_uint32_to_uint64 (0); shift -= 64; } if (shift) { a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift), _cairo_uint64_rsl (a.lo, (64 - shift))); a.lo = _cairo_uint64_lsl (a.lo, shift); } return a; } cairo_uint128_t _cairo_uint128_rsl (cairo_uint128_t a, int shift) { if (shift >= 64) { a.lo = a.hi; a.hi = _cairo_uint32_to_uint64 (0); shift -= 64; } if (shift) { a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), _cairo_uint64_lsl (a.hi, (64 - shift))); a.hi = _cairo_uint64_rsl (a.hi, shift); } return a; } cairo_uint128_t _cairo_uint128_rsa (cairo_int128_t a, int shift) { if (shift >= 64) { a.lo = a.hi; a.hi = _cairo_uint64_rsa (a.hi, 64-1); shift -= 64; } if (shift) { a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), _cairo_uint64_lsl (a.hi, (64 - shift))); a.hi = _cairo_uint64_rsa (a.hi, shift); } return a; } int _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b) { return (_cairo_uint64_lt (a.hi, b.hi) || (_cairo_uint64_eq (a.hi, b.hi) && _cairo_uint64_lt (a.lo, b.lo))); } int _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b) { if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) return 1; if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) return 0; return _cairo_uint128_lt (a, b); } int _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b) { int cmp; cmp = _cairo_uint64_cmp (a.hi, b.hi); if (cmp) return cmp; return _cairo_uint64_cmp (a.lo, b.lo); } int _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b) { if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) return -1; if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) return 1; return _cairo_uint128_cmp (a, b); } int _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b) { return (_cairo_uint64_eq (a.hi, b.hi) && _cairo_uint64_eq (a.lo, b.lo)); } #if HAVE_UINT64_T #define _cairo_msbset64(q) (q & ((uint64_t) 1 << 63)) #else #define _cairo_msbset64(q) (q.hi & ((uint32_t) 1 << 31)) #endif cairo_uquorem128_t _cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) { cairo_uquorem128_t qr; cairo_uint128_t bit; cairo_uint128_t quo; bit = _cairo_uint32_to_uint128 (1); /* normalize to make den >= num, but not overflow */ while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi)) { bit = _cairo_uint128_lsl (bit, 1); den = _cairo_uint128_lsl (den, 1); } quo = _cairo_uint32_to_uint128 (0); /* generate quotient, one bit at a time */ while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0))) { if (_cairo_uint128_le (den, num)) { num = _cairo_uint128_sub (num, den); quo = _cairo_uint128_add (quo, bit); } bit = _cairo_uint128_rsl (bit, 1); den = _cairo_uint128_rsl (den, 1); } qr.quo = quo; qr.rem = num; return qr; } cairo_int128_t _cairo_int128_negate (cairo_int128_t a) { a.lo = _cairo_uint64_not (a.lo); a.hi = _cairo_uint64_not (a.hi); return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1)); } cairo_int128_t _cairo_int128_not (cairo_int128_t a) { a.lo = _cairo_uint64_not (a.lo); a.hi = _cairo_uint64_not (a.hi); return a; } #endif /* !HAVE_UINT128_T */ cairo_quorem128_t _cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) { int num_neg = _cairo_int128_negative (num); int den_neg = _cairo_int128_negative (den); cairo_uquorem128_t uqr; cairo_quorem128_t qr; if (num_neg) num = _cairo_int128_negate (num); if (den_neg) den = _cairo_int128_negate (den); uqr = _cairo_uint128_divrem (num, den); if (num_neg) qr.rem = _cairo_int128_negate (uqr.rem); else qr.rem = uqr.rem; if (num_neg != den_neg) qr.quo = _cairo_int128_negate (uqr.quo); else qr.quo = uqr.quo; return qr; } /** * _cairo_uint_96by64_32x64_divrem: * * Compute a 32 bit quotient and 64 bit remainder of a 96 bit unsigned * dividend and 64 bit divisor. If the quotient doesn't fit into 32 * bits then the returned remainder is equal to the divisor, and the * quotient is the largest representable 64 bit integer. It is an * error to call this function with the high 32 bits of @num being * non-zero. **/ cairo_uquorem64_t _cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, cairo_uint64_t den) { cairo_uquorem64_t result; cairo_uint64_t B = _cairo_uint32s_to_uint64 (1, 0); /* These are the high 64 bits of the *96* bit numerator. We're * going to represent the numerator as xB + y, where x is a 64, * and y is a 32 bit number. */ cairo_uint64_t x = _cairo_uint128_to_uint64 (_cairo_uint128_rsl(num, 32)); /* Initialise the result to indicate overflow. */ result.quo = _cairo_uint32s_to_uint64 (-1U, -1U); result.rem = den; /* Don't bother if the quotient is going to overflow. */ if (_cairo_uint64_ge (x, den)) { return /* overflow */ result; } if (_cairo_uint64_lt (x, B)) { /* When the final quotient is known to fit in 32 bits, then * num < 2^64 if and only if den < 2^32. */ return _cairo_uint64_divrem (_cairo_uint128_to_uint64 (num), den); } else { /* Denominator is >= 2^32. the numerator is >= 2^64, and the * division won't overflow: need two divrems. Write the * numerator and denominator as * * num = xB + y x : 64 bits, y : 32 bits * den = uB + v u, v : 32 bits */ uint32_t y = _cairo_uint128_to_uint32 (num); uint32_t u = uint64_hi32 (den); uint32_t v = _cairo_uint64_to_uint32 (den); /* Compute a lower bound approximate quotient of num/den * from x/(u+1). Then we have * * x = q(u+1) + r ; q : 32 bits, r <= u : 32 bits. * * xB + y = q(u+1)B + (rB+y) * = q(uB + B + v - v) + (rB+y) * = q(uB + v) + qB - qv + (rB+y) * = q(uB + v) + q(B-v) + (rB+y) * * The true quotient of num/den then is q plus the * contribution of q(B-v) + (rB+y). The main contribution * comes from the term q(B-v), with the term (rB+y) only * contributing at most one part. * * The term q(B-v) must fit into 64 bits, since q fits into 32 * bits on account of being a lower bound to the true * quotient, and as B-v <= 2^32, we may safely use a single * 64/64 bit division to find its contribution. */ cairo_uquorem64_t quorem; cairo_uint64_t remainder; /* will contain final remainder */ uint32_t quotient; /* will contain final quotient. */ uint32_t q; uint32_t r; /* Approximate quotient by dividing the high 64 bits of num by * u+1. Watch out for overflow of u+1. */ if (u+1) { quorem = _cairo_uint64_divrem (x, _cairo_uint32_to_uint64 (u+1)); q = _cairo_uint64_to_uint32 (quorem.quo); r = _cairo_uint64_to_uint32 (quorem.rem); } else { q = uint64_hi32 (x); r = _cairo_uint64_to_uint32 (x); } quotient = q; /* Add the main term's contribution to quotient. Note B-v = * -v as an uint32 (unless v = 0) */ if (v) quorem = _cairo_uint64_divrem (_cairo_uint32x32_64_mul (q, -v), den); else quorem = _cairo_uint64_divrem (_cairo_uint32s_to_uint64 (q, 0), den); quotient += _cairo_uint64_to_uint32 (quorem.quo); /* Add the contribution of the subterm and start computing the * true remainder. */ remainder = _cairo_uint32s_to_uint64 (r, y); if (_cairo_uint64_ge (remainder, den)) { remainder = _cairo_uint64_sub (remainder, den); quotient++; } /* Add the contribution of the main term's remainder. The * funky test here checks that remainder + main_rem >= den, * taking into account overflow of the addition. */ remainder = _cairo_uint64_add (remainder, quorem.rem); if (_cairo_uint64_ge (remainder, den) || _cairo_uint64_lt (remainder, quorem.rem)) { remainder = _cairo_uint64_sub (remainder, den); quotient++; } result.quo = _cairo_uint32_to_uint64 (quotient); result.rem = remainder; } return result; } cairo_quorem64_t _cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den) { int num_neg = _cairo_int128_negative (num); int den_neg = _cairo_int64_negative (den); cairo_uint64_t nonneg_den; cairo_uquorem64_t uqr; cairo_quorem64_t qr; if (num_neg) num = _cairo_int128_negate (num); if (den_neg) nonneg_den = _cairo_int64_negate (den); else nonneg_den = den; uqr = _cairo_uint_96by64_32x64_divrem (num, nonneg_den); if (_cairo_uint64_eq (uqr.rem, nonneg_den)) { /* bail on overflow. */ qr.quo = _cairo_uint32s_to_uint64 (0x7FFFFFFF, -1U); qr.rem = den; return qr; } if (num_neg) qr.rem = _cairo_int64_negate (uqr.rem); else qr.rem = uqr.rem; if (num_neg != den_neg) qr.quo = _cairo_int64_negate (uqr.quo); else qr.quo = uqr.quo; return qr; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-win32.h000066400000000000000000000070741271037650300245160ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor */ #ifndef _CAIRO_WIN32_H_ #define _CAIRO_WIN32_H_ #include "cairo.h" #if CAIRO_HAS_WIN32_SURFACE #include CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_win32_surface_create (HDC hdc); cairo_public cairo_surface_t * cairo_win32_printing_surface_create (HDC hdc); cairo_public cairo_surface_t * cairo_win32_surface_create_with_ddb (HDC hdc, cairo_format_t format, int width, int height); cairo_public cairo_surface_t * cairo_win32_surface_create_with_dib (cairo_format_t format, int width, int height); cairo_public HDC cairo_win32_surface_get_dc (cairo_surface_t *surface); cairo_public cairo_surface_t * cairo_win32_surface_get_image (cairo_surface_t *surface); #if CAIRO_HAS_WIN32_FONT /* * Win32 font support */ cairo_public cairo_font_face_t * cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); cairo_public cairo_font_face_t * cairo_win32_font_face_create_for_hfont (HFONT font); cairo_public cairo_font_face_t * cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font); cairo_public cairo_status_t cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, HDC hdc); cairo_public void cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font); cairo_public double cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font); cairo_public void cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, cairo_matrix_t *logical_to_device); cairo_public void cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, cairo_matrix_t *device_to_logical); #endif /* CAIRO_HAS_WIN32_FONT */ CAIRO_END_DECLS #else /* CAIRO_HAS_WIN32_SURFACE */ # error Cairo was not compiled with support for the win32 backend #endif /* CAIRO_HAS_WIN32_SURFACE */ #endif /* _CAIRO_WIN32_H_ */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-connection-core.c000066400000000000000000000207171271037650300274050ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-xcb-private.h" #include xcb_pixmap_t _cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height) { xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection); assert (width > 0); assert (height > 0); xcb_create_pixmap (connection->xcb_connection, depth, pixmap, drawable, width, height); return pixmap; } void _cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, xcb_pixmap_t pixmap) { xcb_free_pixmap (connection->xcb_connection, pixmap); _cairo_xcb_connection_put_xid (connection, pixmap); } xcb_gcontext_t _cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, xcb_drawable_t drawable, uint32_t value_mask, uint32_t *values) { xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection); xcb_create_gc (connection->xcb_connection, gc, drawable, value_mask, values); return gc; } void _cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, xcb_gcontext_t gc) { xcb_free_gc (connection->xcb_connection, gc); _cairo_xcb_connection_put_xid (connection, gc); } void _cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, xcb_gcontext_t gc, uint32_t value_mask, uint32_t *values) { xcb_change_gc (connection->xcb_connection, gc, value_mask, values); } void _cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, xcb_drawable_t src, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height) { xcb_copy_area (connection->xcb_connection, src, dst, gc, src_x, src_y, dst_x, dst_y, width, height); } void _cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint32_t num_rectangles, xcb_rectangle_t *rectangles) { xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc, num_rectangles, rectangles); } void _cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t depth, uint32_t stride, void *data) { const uint32_t req_size = 18; uint32_t length = height * stride; uint32_t len = (req_size + length) >> 2; if (len < connection->maximum_request_length) { xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, dst, gc, width, height, dst_x, dst_y, 0, depth, length, data); } else { int rows = (connection->maximum_request_length - req_size - 4) / stride; if (rows > 0) { do { if (rows > height) rows = height; length = rows * stride; xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data); height -= rows; dst_y += rows; data = (char *) data + length; } while (height); } else { ASSERT_NOT_REACHED; } } } static void _cairo_xcb_connection_do_put_subimage (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, uint16_t cpp, int stride, int16_t dst_x, int16_t dst_y, uint8_t depth, void *_data) { xcb_protocol_request_t xcb_req = { 0 /* count */, 0 /* ext */, XCB_PUT_IMAGE /* opcode */, 1 /* isvoid (doesn't cause a reply) */ }; xcb_put_image_request_t req; struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)]; struct iovec *vec = vec_stack; uint32_t len = 0; uint8_t *data = _data; int n = 3; /* Two extra entries are needed for xcb, two for us */ int entries_needed = height + 2 + 2; req.format = XCB_IMAGE_FORMAT_Z_PIXMAP; req.drawable = dst; req.gc = gc; req.width = width; req.height = height; req.dst_x = dst_x; req.dst_y = dst_y; req.left_pad = 0; req.depth = depth; req.pad0[0] = 0; req.pad0[1] = 0; if (entries_needed > ARRAY_LENGTH (vec_stack)) { vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec)); if (unlikely (vec == NULL)) { /* XXX loop over ARRAY_LENGTH (vec_stack) */ return; } } data += src_y * stride + src_x * cpp; /* vec[1] will be used in XCB if it has to use BigRequests or insert a sync, * vec[0] is used if the internal queue needs to be flushed. */ vec[2].iov_base = (char *) &req; vec[2].iov_len = sizeof (req); /* Now comes the actual data */ while (height--) { vec[n].iov_base = data; vec[n].iov_len = cpp * width; len += cpp * width; data += stride; n++; } /* And again some padding */ vec[n].iov_base = 0; vec[n].iov_len = -len & 3; n++; /* For efficiency reasons, this functions writes the request "directly" to * the xcb connection to avoid having to copy the data around. */ assert (n == entries_needed); xcb_req.count = n - 2; xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req); if (vec != vec_stack) free (vec); } void _cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, uint16_t cpp, int stride, int16_t dst_x, int16_t dst_y, uint8_t depth, void *_data) { const uint32_t req_size = sizeof(xcb_put_image_request_t); uint32_t length = height * cpp * width; uint32_t len = (req_size + length) >> 2; if (len < connection->maximum_request_length) { _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y, width, height, cpp, stride, dst_x, dst_y, depth, _data); } else { int rows = (connection->maximum_request_length - req_size - 4) / (cpp * width); if (rows > 0) { do { if (rows > height) rows = height; length = rows * cpp * width; _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y, width, rows, cpp, stride, dst_x, dst_y, depth, _data); height -= rows; dst_y += rows; _data = (char *) _data + stride * rows; } while (height); } else { ASSERT_NOT_REACHED; } } } cairo_status_t _cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, xcb_drawable_t src, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, xcb_get_image_reply_t **reply) { xcb_generic_error_t *error; *reply = xcb_get_image_reply (connection->xcb_connection, xcb_get_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, src, src_x, src_y, width, height, (uint32_t) -1), &error); if (error) { free (error); free (*reply); *reply = NULL; } return CAIRO_STATUS_SUCCESS; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-connection-render.c000066400000000000000000000261741271037650300277370ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-xcb-private.h" #include void _cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_drawable_t drawable, xcb_render_pictformat_t format, uint32_t value_mask, uint32_t *value_list) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_create_picture (connection->xcb_connection, picture, drawable, format, value_mask, value_list); } void _cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, uint32_t value_mask, uint32_t *value_list) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_change_picture (connection->xcb_connection, picture, value_mask, value_list); } void _cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, int16_t clip_x_origin, int16_t clip_y_origin, uint32_t rectangles_len, xcb_rectangle_t *rectangles) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_set_picture_clip_rectangles (connection->xcb_connection, picture, clip_x_origin, clip_y_origin, rectangles_len, rectangles); } void _cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_free_picture (connection->xcb_connection, picture); _cairo_xcb_connection_put_xid (connection, picture); } void _cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t mask, xcb_render_picture_t dst, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE); xcb_render_composite (connection->xcb_connection, op, src, mask, dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); } void _cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, int16_t src_x, int16_t src_y, uint32_t traps_len, xcb_render_trapezoid_t *traps) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS); xcb_render_trapezoids (connection->xcb_connection, op, src, dst, mask_format, src_x, src_y, traps_len, traps); } void _cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, xcb_render_glyphset_t id, xcb_render_pictformat_t format) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_create_glyph_set (connection->xcb_connection, id, format); } void _cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_free_glyph_set (connection->xcb_connection, glyphset); _cairo_xcb_connection_put_xid (connection, glyphset); } void _cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset, uint32_t num_glyphs, uint32_t *glyphs_id, xcb_render_glyphinfo_t *glyphs, uint32_t data_len, uint8_t *data) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_add_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs_id, glyphs, data_len, data); } void _cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset, uint32_t num_glyphs, xcb_render_glyph_t *glyphs) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_free_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs); } void _cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_composite_glyphs_8 (connection->xcb_connection, op, src, dst, mask_format, glyphset, src_x, src_y, glyphcmds_len, glyphcmds); } void _cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_composite_glyphs_16 (connection->xcb_connection, op, src, dst, mask_format, glyphset, src_x, src_y, glyphcmds_len, glyphcmds); } void _cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds) { assert (connection->flags & CAIRO_XCB_HAS_RENDER); xcb_render_composite_glyphs_32 (connection->xcb_connection, op, src, dst, mask_format, glyphset, src_x, src_y, glyphcmds_len, glyphcmds); } void _cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t dst, xcb_render_color_t color, uint32_t num_rects, xcb_rectangle_t *rects) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES); xcb_render_fill_rectangles (connection->xcb_connection, op, dst, color, num_rects, rects); } void _cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_transform_t *transform) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM); xcb_render_set_picture_transform (connection->xcb_connection, picture, *transform); } void _cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, uint16_t filter_len, char *filter) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILTERS); xcb_render_set_picture_filter (connection->xcb_connection, picture, filter_len, filter, 0, NULL); } void _cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_color_t color) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); xcb_render_create_solid_fill (connection->xcb_connection, picture, color); } void _cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_pointfix_t p1, xcb_render_pointfix_t p2, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); xcb_render_create_linear_gradient (connection->xcb_connection, picture, p1, p2, num_stops, stops, colors); } void _cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_pointfix_t inner, xcb_render_pointfix_t outer, xcb_render_fixed_t inner_radius, xcb_render_fixed_t outer_radius, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); xcb_render_create_radial_gradient (connection->xcb_connection, picture, inner, outer, inner_radius, outer_radius, num_stops, stops, colors); } void _cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_pointfix_t center, xcb_render_fixed_t angle, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors) { assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); xcb_render_create_conical_gradient (connection->xcb_connection, picture, center, angle, num_stops, stops, colors); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-connection-shm.c000066400000000000000000000071611271037650300272420ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #if CAIRO_HAS_XCB_SHM_FUNCTIONS #include "cairo-xcb-private.h" #include #include uint32_t _cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, uint32_t id, cairo_bool_t readonly) { uint32_t segment = _cairo_xcb_connection_get_xid (connection); assert (connection->flags & CAIRO_XCB_HAS_SHM); xcb_shm_attach (connection->xcb_connection, segment, id, readonly); return segment; } void _cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint16_t total_width, uint16_t total_height, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t depth, uint32_t shm, uint32_t offset) { assert (connection->flags & CAIRO_XCB_HAS_SHM); xcb_shm_put_image (connection->xcb_connection, dst, gc, total_width, total_height, src_x, src_y, width, height, dst_x, dst_y, depth, XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shm, offset); } cairo_status_t _cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, xcb_drawable_t src, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, uint32_t shmseg, uint32_t offset) { xcb_shm_get_image_reply_t *reply; xcb_generic_error_t *error; assert (connection->flags & CAIRO_XCB_HAS_SHM); reply = xcb_shm_get_image_reply (connection->xcb_connection, xcb_shm_get_image (connection->xcb_connection, src, src_x, src_y, width, height, (uint32_t) -1, XCB_IMAGE_FORMAT_Z_PIXMAP, shmseg, offset), &error); free (reply); if (error) { /* an error here should be impossible */ free (error); return _cairo_error (CAIRO_STATUS_READ_ERROR); } return CAIRO_STATUS_SUCCESS; } void _cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, uint32_t segment) { assert (connection->flags & CAIRO_XCB_HAS_SHM); xcb_shm_detach (connection->xcb_connection, segment); _cairo_xcb_connection_put_xid (connection, segment); } #endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-connection.c000066400000000000000000000703511271037650300264560ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Authors: * Chris Wilson */ #include "cairoint.h" #include "cairo-xcb-private.h" #include "cairo-hash-private.h" #include "cairo-freelist-private.h" #include "cairo-list-inline.h" #include #include #include #if CAIRO_HAS_XCB_SHM_FUNCTIONS #include #include #include #endif typedef struct _cairo_xcb_xrender_format { cairo_hash_entry_t key; xcb_render_pictformat_t xrender_format; } cairo_xcb_xrender_format_t; typedef struct _cairo_xcb_xid { cairo_list_t link; uint32_t xid; } cairo_xcb_xid_t; #define XCB_RENDER_AT_LEAST(V, major, minor) \ (((V)->major_version > major) || \ (((V)->major_version == major) && ((V)->minor_version >= minor))) #define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) #define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) #define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) #define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1) #define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) #define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) #define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) #define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) #define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) #define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) #define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) #define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) #define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) #define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) #define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11) static cairo_list_t connections; static cairo_status_t _cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection, const xcb_render_query_pict_formats_reply_t *formats) { xcb_render_pictscreen_iterator_t screens; xcb_render_pictdepth_iterator_t depths; xcb_render_pictvisual_iterator_t visuals; for (screens = xcb_render_query_pict_formats_screens_iterator (formats); screens.rem; xcb_render_pictscreen_next (&screens)) { for (depths = xcb_render_pictscreen_depths_iterator (screens.data); depths.rem; xcb_render_pictdepth_next (&depths)) { for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data); visuals.rem; xcb_render_pictvisual_next (&visuals)) { cairo_xcb_xrender_format_t *f; cairo_status_t status; f = malloc (sizeof (cairo_xcb_xrender_format_t)); if (unlikely (f == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); f->key.hash = visuals.data->visual; f->xrender_format = visuals.data->format; status = _cairo_hash_table_insert (connection->visual_to_xrender_format, &f->key); if (unlikely (status)) return status; } } } return CAIRO_STATUS_SUCCESS; } #if 0 static xcb_format_t * find_format_for_depth (const xcb_setup_t *setup, uint8_t depth) { xcb_format_t *fmt = xcb_setup_pixmap_formats (setup); xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup); for (; fmt != fmtend; ++fmt) if (fmt->depth == depth) return fmt; return 0; } #endif static cairo_status_t _cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection, const xcb_render_query_pict_formats_reply_t *formats) { xcb_render_pictforminfo_iterator_t i; cairo_status_t status; for (i = xcb_render_query_pict_formats_formats_iterator (formats); i.rem; xcb_render_pictforminfo_next (&i)) { cairo_format_masks_t masks; pixman_format_code_t pixman_format; if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT) continue; masks.alpha_mask = (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift; masks.red_mask = (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift; masks.green_mask = (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift; masks.blue_mask = (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift; masks.bpp = i.data->depth; if (_pixman_format_from_masks (&masks, &pixman_format)) { cairo_hash_entry_t key; key.hash = pixman_format; if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) { cairo_xcb_xrender_format_t *f; f = malloc (sizeof (cairo_xcb_xrender_format_t)); if (unlikely (f == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); f->key.hash = pixman_format; f->xrender_format = i.data->id; status = _cairo_hash_table_insert (connection->xrender_formats, &f->key); if (unlikely (status)) return status; #if 0 printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n", i.data->id, masks.alpha_mask, masks.red_mask, masks.green_mask, masks.blue_mask, masks.bpp, pixman_format, PIXMAN_FORMAT_DEPTH(pixman_format), PIXMAN_FORMAT_BPP(pixman_format)); #endif } } } status = _cairo_xcb_connection_find_visual_formats (connection, formats); if (unlikely (status)) return status; connection->standard_formats[CAIRO_FORMAT_A1] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1); connection->standard_formats[CAIRO_FORMAT_A8] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8); connection->standard_formats[CAIRO_FORMAT_RGB24] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_FORMAT (24, PIXMAN_TYPE_ARGB, 0, 8, 8, 8)); if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) { connection->standard_formats[CAIRO_FORMAT_RGB24] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR, 0, 8, 8, 8)); } connection->standard_formats[CAIRO_FORMAT_ARGB32] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8); if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) { connection->standard_formats[CAIRO_FORMAT_ARGB32] = _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8); } return CAIRO_STATUS_SUCCESS; } /* * We require support for depth 1, 8, 24 and 32 pixmaps */ #define DEPTH_MASK(d) (1 << ((d) - 1)) #define REQUIRED_DEPTHS (DEPTH_MASK(1) | \ DEPTH_MASK(8) | \ DEPTH_MASK(24) | \ DEPTH_MASK(32)) static cairo_bool_t pixmap_depths_usable (cairo_xcb_connection_t *connection, uint32_t missing, xcb_drawable_t root) { xcb_connection_t *c = connection->xcb_connection; xcb_void_cookie_t create_cookie[32]; xcb_pixmap_t pixmap; cairo_bool_t success = TRUE; int depth, i, j; pixmap = _cairo_xcb_connection_get_xid (connection); for (depth = 1, i = 0; depth <= 32; depth++) { if (missing & DEPTH_MASK(depth)) { create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1); xcb_free_pixmap (c, pixmap); if (!create_cookie[i].sequence) { success = FALSE; break; } i++; } } for (j = 0; j < i; j++) { xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]); success &= create_error == NULL; free (create_error); } _cairo_xcb_connection_put_xid (connection, pixmap); return success; } static cairo_bool_t has_required_depths (cairo_xcb_connection_t *connection) { xcb_screen_iterator_t screens; for (screens = xcb_setup_roots_iterator (connection->root); screens.rem; xcb_screen_next (&screens)) { xcb_depth_iterator_t depths; uint32_t missing = REQUIRED_DEPTHS; for (depths = xcb_screen_allowed_depths_iterator (screens.data); depths.rem; xcb_depth_next (&depths)) { missing &= ~DEPTH_MASK (depths.data->depth); } if (missing == 0) continue; /* * Ok, this is ugly. It should be sufficient at this * point to just return false, but Xinerama is broken at * this point and only advertises depths which have an * associated visual. Of course, the other depths still * work, but the only way to find out is to try them. */ if (! pixmap_depths_usable (connection, missing, screens.data->root)) return FALSE; } return TRUE; } static xcb_render_query_version_reply_t * _render_restrict_env(xcb_render_query_version_reply_t *version) { const char *env; if (version == NULL) return NULL; env = getenv ("CAIRO_DEBUG"); if (env != NULL) env = strstr (env, "xrender-version="); if (env != NULL) { int max_render_major, max_render_minor; env += sizeof ("xrender-version=") - 1; if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) max_render_major = max_render_minor = -1; if (max_render_major < 0 || max_render_minor < 0) { free (version); return NULL; } if (max_render_major < (int) version->major_version || (max_render_major == (int) version->major_version && max_render_minor < (int) version->minor_version)) { version->major_version = max_render_major; version->minor_version = max_render_minor; } } return version; } static cairo_status_t _cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection) { xcb_connection_t *c = connection->xcb_connection; xcb_render_query_version_cookie_t version_cookie; xcb_render_query_pict_formats_cookie_t formats_cookie; xcb_render_query_version_reply_t *version; xcb_render_query_pict_formats_reply_t *formats; cairo_status_t status; cairo_bool_t present; version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION); formats_cookie = xcb_render_query_pict_formats (c); present = has_required_depths (connection); version = xcb_render_query_version_reply (c, version_cookie, 0); formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0); version = _render_restrict_env (version); if (! present || version == NULL || formats == NULL) { free (version); free (formats); return CAIRO_STATUS_SUCCESS; } /* always true if the extension is present (i.e. >= 0.0) */ connection->flags |= CAIRO_XCB_HAS_RENDER; connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE; connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS; if (XCB_RENDER_HAS_FILL_RECTANGLES (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; if (XCB_RENDER_HAS_TRAPEZOIDS (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; if (XCB_RENDER_HAS_FILTERS (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS; if (XCB_RENDER_HAS_PDF_OPERATORS (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; if (XCB_RENDER_HAS_EXTENDED_REPEAT (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; if (XCB_RENDER_HAS_GRADIENTS (version)) connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS; free (version); status = _cairo_xcb_connection_parse_xrender_formats (connection, formats); free (formats); return status; } #if 0 static void _cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection) { xcb_connection_t *c = connection->xcb_connection; xcb_cairo_query_version_reply_t *version; version = xcb_cairo_query_version_reply (c, xcb_cairo_query_version (c, 0, 0), 0); free (version); } #endif #if CAIRO_HAS_XCB_SHM_FUNCTIONS static cairo_bool_t can_use_shm (cairo_xcb_connection_t *connection) { cairo_bool_t success = TRUE; xcb_connection_t *c = connection->xcb_connection; xcb_void_cookie_t cookie[2]; xcb_generic_error_t *error; int shmid; uint32_t shmseg; void *ptr; shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); if (shmid == -1) return FALSE; ptr = shmat (shmid, NULL, 0); if (ptr == (char *) -1) { shmctl (shmid, IPC_RMID, NULL); return FALSE; } shmseg = _cairo_xcb_connection_get_xid (connection); cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE); cookie[1] = xcb_shm_detach_checked (c, shmseg); _cairo_xcb_connection_put_xid (connection, shmseg); error = xcb_request_check (c, cookie[0]); if (error != NULL) success = FALSE; error = xcb_request_check (c, cookie[1]); if (error != NULL) success = FALSE; shmctl (shmid, IPC_RMID, NULL); shmdt (ptr); return success; } static void _cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection) { xcb_connection_t *c = connection->xcb_connection; xcb_shm_query_version_reply_t *version; version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0); if (version == NULL) return; free (version); if (can_use_shm (connection)) connection->flags |= CAIRO_XCB_HAS_SHM; } #endif static cairo_status_t _device_flush (void *device) { cairo_xcb_connection_t *connection = device; cairo_status_t status; status = cairo_device_acquire (&connection->device); if (unlikely (status)) return status; #if CAIRO_HAS_XCB_SHM_FUNCTIONS _cairo_xcb_connection_shm_mem_pools_flush (connection); #endif xcb_flush (connection->xcb_connection); cairo_device_release (&connection->device); return CAIRO_STATUS_SUCCESS; } static void _pluck_xrender_format (void *entry, void *closure) { _cairo_hash_table_remove (closure, entry); free (entry); } static void _device_finish (void *device) { cairo_xcb_connection_t *connection = device; cairo_bool_t was_cached = FALSE; if (! cairo_list_is_empty (&connection->link)) { CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); cairo_list_del (&connection->link); CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); was_cached = TRUE; } while (! cairo_list_is_empty (&connection->fonts)) { cairo_xcb_font_t *font; font = cairo_list_first_entry (&connection->fonts, cairo_xcb_font_t, link); _cairo_xcb_font_close (font); } while (! cairo_list_is_empty (&connection->screens)) { cairo_xcb_screen_t *screen; screen = cairo_list_first_entry (&connection->screens, cairo_xcb_screen_t, link); _cairo_xcb_screen_finish (screen); } #if CAIRO_HAS_XCB_SHM_FUNCTIONS /* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had * a fallback image, we might have done a SHM PutImage. */ _cairo_xcb_connection_shm_mem_pools_flush (connection); #endif if (was_cached) cairo_device_destroy (device); } static void _device_destroy (void *device) { cairo_xcb_connection_t *connection = device; _cairo_hash_table_foreach (connection->xrender_formats, _pluck_xrender_format, connection->xrender_formats); _cairo_hash_table_destroy (connection->xrender_formats); _cairo_hash_table_foreach (connection->visual_to_xrender_format, _pluck_xrender_format, connection->visual_to_xrender_format); _cairo_hash_table_destroy (connection->visual_to_xrender_format); #if CAIRO_HAS_XCB_SHM_FUNCTIONS _cairo_xcb_connection_shm_mem_pools_fini (connection); #endif _cairo_freepool_fini (&connection->shm_info_freelist); _cairo_freepool_fini (&connection->xid_pool); CAIRO_MUTEX_FINI (connection->shm_mutex); CAIRO_MUTEX_FINI (connection->screens_mutex); free (connection); } static const cairo_device_backend_t _cairo_xcb_device_backend = { CAIRO_DEVICE_TYPE_XCB, NULL, NULL, /* lock, unlock */ _device_flush, _device_finish, _device_destroy, }; cairo_xcb_connection_t * _cairo_xcb_connection_get (xcb_connection_t *xcb_connection) { cairo_xcb_connection_t *connection; const xcb_query_extension_reply_t *ext; cairo_status_t status; CAIRO_MUTEX_INITIALIZE (); CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); if (connections.next == NULL) { /* XXX _cairo_init () */ cairo_list_init (&connections); } cairo_list_foreach_entry (connection, cairo_xcb_connection_t, &connections, link) { if (connection->xcb_connection == xcb_connection) { /* Maintain MRU order. */ if (connections.next != &connection->link) cairo_list_move (&connection->link, &connections); goto unlock; } } connection = malloc (sizeof (cairo_xcb_connection_t)); if (unlikely (connection == NULL)) goto unlock; _cairo_device_init (&connection->device, &_cairo_xcb_device_backend); connection->xcb_connection = xcb_connection; cairo_list_init (&connection->fonts); cairo_list_init (&connection->screens); cairo_list_init (&connection->link); connection->xrender_formats = _cairo_hash_table_create (NULL); if (connection->xrender_formats == NULL) { CAIRO_MUTEX_FINI (connection->device.mutex); free (connection); connection = NULL; goto unlock; } connection->visual_to_xrender_format = _cairo_hash_table_create (NULL); if (connection->visual_to_xrender_format == NULL) { _cairo_hash_table_destroy (connection->xrender_formats); CAIRO_MUTEX_FINI (connection->device.mutex); free (connection); connection = NULL; goto unlock; } cairo_list_init (&connection->free_xids); _cairo_freepool_init (&connection->xid_pool, sizeof (cairo_xcb_xid_t)); cairo_list_init (&connection->shm_pools); cairo_list_init (&connection->shm_pending); _cairo_freepool_init (&connection->shm_info_freelist, sizeof (cairo_xcb_shm_info_t)); connection->maximum_request_length = xcb_get_maximum_request_length (xcb_connection); CAIRO_MUTEX_INIT (connection->shm_mutex); CAIRO_MUTEX_INIT (connection->screens_mutex); CAIRO_MUTEX_LOCK (connection->device.mutex); connection->flags = 0; connection->force_precision = -1; xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id); xcb_prefetch_extension_data (xcb_connection, &xcb_render_id); #if CAIRO_HAS_XCB_SHM_FUNCTIONS xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id); #endif #if 0 xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id); #endif xcb_prefetch_maximum_request_length (xcb_connection); connection->root = xcb_get_setup (xcb_connection); connection->render = NULL; ext = xcb_get_extension_data (xcb_connection, &xcb_render_id); if (ext != NULL && ext->present) { status = _cairo_xcb_connection_query_render (connection); if (unlikely (status)) { CAIRO_MUTEX_UNLOCK (connection->device.mutex); _cairo_xcb_connection_destroy (connection); connection = NULL; goto unlock; } connection->render = ext; } #if 0 ext = xcb_get_extension_data (connection, &xcb_cairo_id); if (ext != NULL && ext->present) _cairo_xcb_connection_query_cairo (connection); #endif connection->shm = NULL; #if CAIRO_HAS_XCB_SHM_FUNCTIONS ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id); if (ext != NULL && ext->present) { _cairo_xcb_connection_query_shm (connection); connection->shm = ext; } #endif connection->original_flags = connection->flags; CAIRO_MUTEX_UNLOCK (connection->device.mutex); cairo_list_add (&connection->link, &connections); unlock: CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); return connection; } xcb_render_pictformat_t _cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format) { cairo_hash_entry_t key; cairo_xcb_xrender_format_t *format; key.hash = pixman_format; format = _cairo_hash_table_lookup (connection->xrender_formats, &key); return format ? format->xrender_format : XCB_NONE; } xcb_render_pictformat_t _cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, const xcb_visualid_t visual) { cairo_hash_entry_t key; cairo_xcb_xrender_format_t *format; key.hash = visual; format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key); return format ? format->xrender_format : XCB_NONE; } void _cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, uint32_t xid) { cairo_xcb_xid_t *cache; assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex)); cache = _cairo_freepool_alloc (&connection->xid_pool); if (likely (cache != NULL)) { cache->xid = xid; cairo_list_add (&cache->link, &connection->free_xids); } } uint32_t _cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection) { uint32_t xid; assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex)); if (! cairo_list_is_empty (&connection->free_xids)) { cairo_xcb_xid_t *cache; cache = cairo_list_first_entry (&connection->free_xids, cairo_xcb_xid_t, link); xid = cache->xid; cairo_list_del (&cache->link); _cairo_freepool_free (&connection->xid_pool, cache); } else { xid = xcb_generate_id (connection->xcb_connection); } return xid; } /** * cairo_xcb_device_get_connection: * @device: a #cairo_device_t for the XCB backend * * Get the connection for the XCB device. * * Returns: the #xcb_connection_t for the connection * * Since: 1.12 **/ xcb_connection_t * cairo_xcb_device_get_connection (cairo_device_t *device) { if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) return NULL; return ((cairo_xcb_connection_t *)device)->xcb_connection; } /* public (debug) interface */ /** * cairo_xcb_device_debug_cap_xshm_version: * @device: a #cairo_device_t for the XCB backend * @major_version: major version to restrict to * @minor_version: minor version to restrict to * * Restricts all future XCB surfaces for this devices to the specified version * of the SHM extension. This function exists solely for debugging purpose. * It let's you find out how cairo would behave with an older version of * the SHM extension. * * Use the special values -1 and -1 for disabling the SHM extension. * * Since: 1.12 **/ void cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, int major_version, int minor_version) { cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return; } /* First reset all the SHM flags to their original value. This works * because we only ever clear bits after the connection was created. */ connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK); /* clear any flags that are inappropriate for the desired version */ if (major_version < 0 && minor_version < 0) { connection->flags &= ~(CAIRO_XCB_HAS_SHM); } } /** * cairo_xcb_device_debug_cap_xrender_version: * @device: a #cairo_device_t for the XCB backend * @major_version: major version to restrict to * @minor_version: minor version to restrict to * * Restricts all future XCB surfaces for this devices to the specified version * of the RENDER extension. This function exists solely for debugging purpose. * It let's you find out how cairo would behave with an older version of * the RENDER extension. * * Use the special values -1 and -1 for disabling the RENDER extension. * * Since: 1.12 **/ void cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, int major_version, int minor_version) { cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return; } /* First reset all the RENDER flags to their original value. This works * because we only ever clear bits after the connection was created. */ connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK); /* clear any flags that are inappropriate for the desired version */ if (major_version < 0 && minor_version < 0) { connection->flags &= ~(CAIRO_XCB_HAS_RENDER | CAIRO_XCB_RENDER_HAS_COMPOSITE | CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM | CAIRO_XCB_RENDER_HAS_FILTERS | CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | CAIRO_XCB_RENDER_HAS_GRADIENTS); } else { xcb_render_query_version_reply_t version; version.major_version = major_version; version.minor_version = minor_version; if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; if (! XCB_RENDER_HAS_TRAPEZOIDS (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; if (! XCB_RENDER_HAS_FILTERS (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS; if (! XCB_RENDER_HAS_PDF_OPERATORS (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; if (! XCB_RENDER_HAS_GRADIENTS (&version)) connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS; } } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_device_debug_cap_xrender_version); #endif /** * cairo_xcb_device_debug_set_precision: * @device: a #cairo_device_t for the XCB backend * @precision: the precision to use * * Render supports two modes of precision when rendering trapezoids. Set * the precision to the desired mode. * * Since: 1.12 **/ void cairo_xcb_device_debug_set_precision (cairo_device_t *device, int precision) { if (device == NULL || device->status) return; if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return; } ((cairo_xcb_connection_t *) device)->force_precision = precision; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_device_debug_set_precision); #endif /** * cairo_xcb_device_debug_get_precision: * @device: a #cairo_device_t for the XCB backend * * Get the Xrender precision mode. * * Returns: the render precision mode * * Since: 1.12 **/ int cairo_xcb_device_debug_get_precision (cairo_device_t *device) { if (device == NULL || device->status) return -1; if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return -1; } return ((cairo_xcb_connection_t *) device)->force_precision; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_device_debug_get_precision); #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-private.h000066400000000000000000000577011271037650300260020ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributors(s): * Chris Wilson */ #ifndef CAIRO_XCB_PRIVATE_H #define CAIRO_XCB_PRIVATE_H #include "cairo-xcb.h" #include "cairoint.h" #include "cairo-cache-private.h" #include "cairo-compiler-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-list-private.h" #include "cairo-mutex-private.h" #include "cairo-pattern-private.h" #include "cairo-reference-count-private.h" #include "cairo-scaled-font-private.h" #include "cairo-spans-private.h" #include "cairo-surface-private.h" #include #include #include #include #define XLIB_COORD_MAX 32767 /* maximum number of cached GC's */ #define GC_CACHE_SIZE 4 #define CAIRO_XCB_RENDER_AT_LEAST(major, minor) \ ((XCB_RENDER_MAJOR_VERSION > major) || \ ((XCB_RENDER_MAJOR_VERSION == major) && (XCB_RENDER_MINOR_VERSION >= minor))) typedef struct _cairo_xcb_connection cairo_xcb_connection_t; typedef struct _cairo_xcb_font cairo_xcb_font_t; typedef struct _cairo_xcb_screen cairo_xcb_screen_t; typedef struct _cairo_xcb_surface cairo_xcb_surface_t; typedef struct _cairo_xcb_picture cairo_xcb_picture_t; typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t; typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t; struct _cairo_xcb_shm_info { cairo_xcb_connection_t *connection; uint32_t shm; uint32_t offset; size_t size; void *mem; cairo_xcb_shm_mem_pool_t *pool; xcb_get_input_focus_cookie_t sync; cairo_list_t pending; }; struct _cairo_xcb_surface { cairo_surface_t base; cairo_image_surface_t *fallback; cairo_boxes_t fallback_damage; cairo_xcb_connection_t *connection; cairo_xcb_screen_t *screen; xcb_drawable_t drawable; cairo_bool_t owns_pixmap; cairo_bool_t deferred_clear; cairo_color_t deferred_clear_color; int width; int height; int depth; xcb_render_picture_t picture; xcb_render_pictformat_t xrender_format; pixman_format_code_t pixman_format; uint32_t precision; cairo_list_t link; }; struct _cairo_xcb_picture { cairo_surface_t base; cairo_xcb_screen_t *screen; xcb_render_picture_t picture; xcb_render_pictformat_t xrender_format; pixman_format_code_t pixman_format; int width, height; cairo_extend_t extend; cairo_filter_t filter; cairo_bool_t has_component_alpha; xcb_render_transform_t transform; int x0, y0; int x, y; cairo_list_t link; }; #if CAIRO_HAS_XLIB_XCB_FUNCTIONS typedef struct _cairo_xlib_xcb_surface { cairo_surface_t base; cairo_xcb_surface_t *xcb; /* original settings for query */ void *display; void *screen; void *visual; void *format; } cairo_xlib_xcb_surface_t; #endif enum { GLYPHSET_INDEX_ARGB32, GLYPHSET_INDEX_A8, GLYPHSET_INDEX_A1, NUM_GLYPHSETS }; typedef struct _cairo_xcb_font_glyphset_free_glyphs { xcb_render_glyphset_t glyphset; int glyph_count; xcb_render_glyph_t glyph_indices[128]; } cairo_xcb_font_glyphset_free_glyphs_t; typedef struct _cairo_xcb_font_glyphset_info { xcb_render_glyphset_t glyphset; cairo_format_t format; xcb_render_pictformat_t xrender_format; cairo_xcb_font_glyphset_free_glyphs_t *pending_free_glyphs; } cairo_xcb_font_glyphset_info_t; struct _cairo_xcb_font { cairo_scaled_font_private_t base; cairo_scaled_font_t *scaled_font; cairo_xcb_connection_t *connection; cairo_xcb_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; cairo_list_t link; }; struct _cairo_xcb_screen { cairo_xcb_connection_t *connection; xcb_screen_t *xcb_screen; xcb_gcontext_t gc[GC_CACHE_SIZE]; uint8_t gc_depths[GC_CACHE_SIZE]; cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS]; struct { cairo_surface_t *picture; cairo_color_t color; } solid_cache[16]; int solid_cache_size; cairo_cache_t linear_pattern_cache; cairo_cache_t radial_pattern_cache; cairo_freelist_t pattern_cache_entry_freelist; cairo_list_t link; cairo_list_t surfaces; cairo_list_t pictures; }; struct _cairo_xcb_connection { cairo_device_t device; xcb_connection_t *xcb_connection; xcb_render_pictformat_t standard_formats[5]; cairo_hash_table_t *xrender_formats; cairo_hash_table_t *visual_to_xrender_format; unsigned int maximum_request_length; unsigned int flags; unsigned int original_flags; int force_precision; const xcb_setup_t *root; const xcb_query_extension_reply_t *render; const xcb_query_extension_reply_t *shm; cairo_list_t free_xids; cairo_freepool_t xid_pool; cairo_mutex_t shm_mutex; cairo_list_t shm_pools; cairo_list_t shm_pending; cairo_freepool_t shm_info_freelist; cairo_mutex_t screens_mutex; cairo_list_t screens; cairo_list_t fonts; cairo_list_t link; }; enum { CAIRO_XCB_HAS_RENDER = 0x0001, CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES = 0x0002, CAIRO_XCB_RENDER_HAS_COMPOSITE = 0x0004, CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS = 0x0008, CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0010, CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0020, CAIRO_XCB_RENDER_HAS_FILTERS = 0x0040, CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0080, CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0100, CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0200, CAIRO_XCB_HAS_SHM = 0x80000000, CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER | CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | CAIRO_XCB_RENDER_HAS_COMPOSITE | CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM | CAIRO_XCB_RENDER_HAS_FILTERS | CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | CAIRO_XCB_RENDER_HAS_GRADIENTS, CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM }; #define CAIRO_XCB_SHM_SMALL_IMAGE 8192 cairo_private extern const cairo_surface_backend_t _cairo_xcb_surface_backend; cairo_private cairo_xcb_connection_t * _cairo_xcb_connection_get (xcb_connection_t *connection); static inline cairo_xcb_connection_t * _cairo_xcb_connection_reference (cairo_xcb_connection_t *connection) { return (cairo_xcb_connection_t *) cairo_device_reference (&connection->device); } cairo_private xcb_render_pictformat_t _cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format); cairo_private xcb_render_pictformat_t _cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, const xcb_visualid_t visual); static inline cairo_status_t cairo_warn _cairo_xcb_connection_acquire (cairo_xcb_connection_t *connection) { return cairo_device_acquire (&connection->device); } cairo_private uint32_t _cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection); cairo_private void _cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, uint32_t xid); static inline void _cairo_xcb_connection_release (cairo_xcb_connection_t *connection) { cairo_device_release (&connection->device); } static inline void _cairo_xcb_connection_destroy (cairo_xcb_connection_t *connection) { cairo_device_destroy (&connection->device); } cairo_private cairo_int_status_t _cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display, size_t size, cairo_bool_t might_reuse, cairo_xcb_shm_info_t **shm_info_out); cairo_private void _cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info); cairo_private void _cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection); cairo_private void _cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection); cairo_private void _cairo_xcb_font_close (cairo_xcb_font_t *font); cairo_private cairo_xcb_screen_t * _cairo_xcb_screen_get (xcb_connection_t *connection, xcb_screen_t *screen); cairo_private void _cairo_xcb_screen_finish (cairo_xcb_screen_t *screen); cairo_private xcb_gcontext_t _cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, int depth); cairo_private void _cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc); cairo_private cairo_status_t _cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, const cairo_linear_pattern_t *linear, cairo_surface_t *picture); cairo_private cairo_surface_t * _cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, const cairo_linear_pattern_t *linear); cairo_private cairo_status_t _cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, const cairo_radial_pattern_t *radial, cairo_surface_t *picture); cairo_private cairo_surface_t * _cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, const cairo_radial_pattern_t *radial); cairo_private cairo_surface_t * _cairo_xcb_surface_create_similar_image (void *abstrct_other, cairo_format_t format, int width, int height); cairo_private cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height); cairo_private cairo_surface_t * _cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, cairo_bool_t owns_pixmap, pixman_format_code_t pixman_format, xcb_render_pictformat_t xrender_format, int width, int height); cairo_private_no_warn cairo_bool_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents); cairo_private cairo_int_status_t _cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents); cairo_private cairo_int_status_t _cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents); cairo_private cairo_int_status_t _cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias); cairo_private cairo_int_status_t _cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias); cairo_private cairo_int_status_t _cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap); cairo_private void _cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); cairo_private void _cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font); cairo_private cairo_status_t _cairo_xcb_surface_clear (cairo_xcb_surface_t *dst); cairo_private cairo_status_t _cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, const cairo_pattern_t *src_pattern, const cairo_rectangle_int_t *extents, const cairo_boxes_t *boxes); cairo_private cairo_status_t _cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, const cairo_color_t *color, cairo_boxes_t *boxes); cairo_private xcb_pixmap_t _cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height); cairo_private void _cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, xcb_pixmap_t pixmap); cairo_private xcb_gcontext_t _cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, xcb_drawable_t drawable, uint32_t value_mask, uint32_t *values); cairo_private void _cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, xcb_gcontext_t gc); cairo_private void _cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, xcb_gcontext_t gc, uint32_t value_mask, uint32_t *values); cairo_private void _cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, xcb_drawable_t src, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height); cairo_private void _cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t depth, uint32_t length, void *data); cairo_private void _cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, uint16_t cpp, int stride, int16_t dst_x, int16_t dst_y, uint8_t depth, void *data); cairo_private cairo_status_t _cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, xcb_drawable_t src, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, xcb_get_image_reply_t **reply); cairo_private void _cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint32_t num_rectangles, xcb_rectangle_t *rectangles); cairo_private cairo_status_t _cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_image_surface_t **image_out, cairo_xcb_shm_info_t **shm_info_out); #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_private uint32_t _cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, uint32_t id, cairo_bool_t readonly); cairo_private void _cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint16_t total_width, uint16_t total_height, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t depth, uint32_t shm, uint32_t offset); cairo_private cairo_status_t _cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, xcb_drawable_t src, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, uint32_t shmseg, uint32_t offset); cairo_private void _cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, uint32_t segment); #else static inline void _cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, xcb_drawable_t dst, xcb_gcontext_t gc, uint16_t total_width, uint16_t total_height, int16_t src_x, int16_t src_y, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t depth, uint32_t shm, uint32_t offset) { ASSERT_NOT_REACHED; } #endif cairo_private void _cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_drawable_t drawable, xcb_render_pictformat_t format, uint32_t value_mask, uint32_t *value_list); cairo_private void _cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, uint32_t value_mask, uint32_t *value_list); cairo_private void _cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, int16_t clip_x_origin, int16_t clip_y_origin, uint32_t rectangles_len, xcb_rectangle_t *rectangles); cairo_private void _cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, xcb_render_picture_t picture); cairo_private void _cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t mask, xcb_render_picture_t dst, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dst_x, int16_t dst_y, uint16_t width, uint16_t height); cairo_private void _cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, int16_t src_x, int16_t src_y, uint32_t traps_len, xcb_render_trapezoid_t *traps); cairo_private void _cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, xcb_render_glyphset_t id, xcb_render_pictformat_t format); cairo_private void _cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset); cairo_private void _cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset, uint32_t num_glyphs, uint32_t *glyphs_id, xcb_render_glyphinfo_t *glyphs, uint32_t data_len, uint8_t *data); cairo_private void _cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, xcb_render_glyphset_t glyphset, uint32_t num_glyphs, xcb_render_glyph_t *glyphs); cairo_private void _cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds); cairo_private void _cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds); cairo_private void _cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t glyphcmds_len, uint8_t *glyphcmds); cairo_private void _cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t dst, xcb_render_color_t color, uint32_t num_rects, xcb_rectangle_t *rects); cairo_private void _cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_transform_t *transform); cairo_private void _cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, uint16_t filter_len, char *filter); cairo_private void _cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_color_t color); cairo_private void _cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_pointfix_t p1, xcb_render_pointfix_t p2, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors); cairo_private void _cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, xcb_render_picture_t picture, xcb_render_pointfix_t inner, xcb_render_pointfix_t outer, xcb_render_fixed_t inner_radius, xcb_render_fixed_t outer_radius, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors); cairo_private void _cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *c, xcb_render_picture_t picture, xcb_render_pointfix_t center, xcb_render_fixed_t angle, uint32_t num_stops, xcb_render_fixed_t *stops, xcb_render_color_t *colors); #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_proto (cairo_xcb_surface_create); slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); slim_hidden_proto (cairo_xcb_surface_set_size); slim_hidden_proto (cairo_xcb_surface_set_drawable); slim_hidden_proto (cairo_xcb_device_debug_get_precision); slim_hidden_proto_no_warn (cairo_xcb_device_debug_set_precision); slim_hidden_proto_no_warn (cairo_xcb_device_debug_cap_xrender_version); #endif #endif /* CAIRO_XCB_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-screen.c000066400000000000000000000245531271037650300256010ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2008 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Authors: * Chris Wilson */ #include "cairoint.h" #include "cairo-xcb-private.h" #include "cairo-list-inline.h" struct pattern_cache_entry { cairo_cache_entry_t key; cairo_xcb_screen_t *screen; cairo_pattern_union_t pattern; cairo_surface_t *picture; }; void _cairo_xcb_screen_finish (cairo_xcb_screen_t *screen) { int i; CAIRO_MUTEX_LOCK (screen->connection->screens_mutex); cairo_list_del (&screen->link); CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex); while (! cairo_list_is_empty (&screen->surfaces)) { cairo_surface_t *surface; surface = &cairo_list_first_entry (&screen->surfaces, cairo_xcb_surface_t, link)->base; cairo_surface_finish (surface); } while (! cairo_list_is_empty (&screen->pictures)) { cairo_surface_t *surface; surface = &cairo_list_first_entry (&screen->pictures, cairo_xcb_picture_t, link)->base; cairo_surface_finish (surface); } for (i = 0; i < screen->solid_cache_size; i++) cairo_surface_destroy (screen->solid_cache[i].picture); for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) cairo_surface_destroy (screen->stock_colors[i]); for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { if (screen->gc_depths[i] != 0) _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]); } _cairo_cache_fini (&screen->linear_pattern_cache); _cairo_cache_fini (&screen->radial_pattern_cache); _cairo_freelist_fini (&screen->pattern_cache_entry_freelist); free (screen); } static cairo_bool_t _linear_pattern_cache_entry_equal (const void *A, const void *B) { const struct pattern_cache_entry *a = A, *b = B; return _cairo_linear_pattern_equal (&a->pattern.gradient.linear, &b->pattern.gradient.linear); } static cairo_bool_t _radial_pattern_cache_entry_equal (const void *A, const void *B) { const struct pattern_cache_entry *a = A, *b = B; return _cairo_radial_pattern_equal (&a->pattern.gradient.radial, &b->pattern.gradient.radial); } static void _pattern_cache_entry_destroy (void *closure) { struct pattern_cache_entry *entry = closure; _cairo_pattern_fini (&entry->pattern.base); cairo_surface_destroy (entry->picture); _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry); } cairo_xcb_screen_t * _cairo_xcb_screen_get (xcb_connection_t *xcb_connection, xcb_screen_t *xcb_screen) { cairo_xcb_connection_t *connection; cairo_xcb_screen_t *screen; cairo_status_t status; int i; connection = _cairo_xcb_connection_get (xcb_connection); if (unlikely (connection == NULL)) return NULL; CAIRO_MUTEX_LOCK (connection->screens_mutex); cairo_list_foreach_entry (screen, cairo_xcb_screen_t, &connection->screens, link) { if (screen->xcb_screen == xcb_screen) { /* Maintain list in MRU order */ if (&screen->link != connection->screens.next) cairo_list_move (&screen->link, &connection->screens); goto unlock; } } screen = malloc (sizeof (cairo_xcb_screen_t)); if (unlikely (screen == NULL)) goto unlock; screen->connection = connection; screen->xcb_screen = xcb_screen; _cairo_freelist_init (&screen->pattern_cache_entry_freelist, sizeof (struct pattern_cache_entry)); cairo_list_init (&screen->link); cairo_list_init (&screen->surfaces); cairo_list_init (&screen->pictures); memset (screen->gc_depths, 0, sizeof (screen->gc_depths)); memset (screen->gc, 0, sizeof (screen->gc)); screen->solid_cache_size = 0; for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) screen->stock_colors[i] = NULL; status = _cairo_cache_init (&screen->linear_pattern_cache, _linear_pattern_cache_entry_equal, NULL, _pattern_cache_entry_destroy, 16); if (unlikely (status)) goto error_screen; status = _cairo_cache_init (&screen->radial_pattern_cache, _radial_pattern_cache_entry_equal, NULL, _pattern_cache_entry_destroy, 4); if (unlikely (status)) goto error_linear; cairo_list_add (&screen->link, &connection->screens); unlock: CAIRO_MUTEX_UNLOCK (connection->screens_mutex); return screen; error_linear: _cairo_cache_fini (&screen->linear_pattern_cache); error_screen: CAIRO_MUTEX_UNLOCK (connection->screens_mutex); free (screen); return NULL; } static xcb_gcontext_t _create_gc (cairo_xcb_screen_t *screen, xcb_drawable_t drawable) { uint32_t values[] = { 0 }; return _cairo_xcb_connection_create_gc (screen->connection, drawable, XCB_GC_GRAPHICS_EXPOSURES, values); } xcb_gcontext_t _cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, int depth) { int i; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { if (screen->gc_depths[i] == depth) { screen->gc_depths[i] = 0; return screen->gc[i]; } } return _create_gc (screen, drawable); } void _cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc) { int i; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { if (screen->gc_depths[i] == 0) break; } if (i == ARRAY_LENGTH (screen->gc)) { /* perform random substitution to ensure fair caching over depths */ i = rand () % ARRAY_LENGTH (screen->gc); _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]); } screen->gc[i] = gc; screen->gc_depths[i] = depth; } cairo_status_t _cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, const cairo_linear_pattern_t *linear, cairo_surface_t *picture) { struct pattern_cache_entry *entry; cairo_status_t status; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); if (unlikely (entry == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); entry->key.size = 1; status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base); if (unlikely (status)) { _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); return status; } entry->picture = cairo_surface_reference (picture); entry->screen = screen; status = _cairo_cache_insert (&screen->linear_pattern_cache, &entry->key); if (unlikely (status)) { cairo_surface_destroy (picture); _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); return status; } return CAIRO_STATUS_SUCCESS; } cairo_surface_t * _cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, const cairo_linear_pattern_t *linear) { cairo_surface_t *picture = NULL; struct pattern_cache_entry tmpl; struct pattern_cache_entry *entry; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base); entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key); if (entry != NULL) picture = cairo_surface_reference (entry->picture); return picture; } cairo_status_t _cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, const cairo_radial_pattern_t *radial, cairo_surface_t *picture) { struct pattern_cache_entry *entry; cairo_status_t status; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); if (unlikely (entry == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); entry->key.size = 1; status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base); if (unlikely (status)) { _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); return status; } entry->picture = cairo_surface_reference (picture); entry->screen = screen; status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key); if (unlikely (status)) { cairo_surface_destroy (picture); _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); return status; } return CAIRO_STATUS_SUCCESS; } cairo_surface_t * _cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, const cairo_radial_pattern_t *radial) { cairo_surface_t *picture = NULL; struct pattern_cache_entry tmpl; struct pattern_cache_entry *entry; assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base); entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key); if (entry != NULL) picture = cairo_surface_reference (entry->picture); return picture; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-shm.c000066400000000000000000000240001271037650300250740ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributors(s): * Chris Wilson */ #include "cairoint.h" #if CAIRO_HAS_XCB_SHM_FUNCTIONS #include "cairo-xcb-private.h" #include "cairo-list-inline.h" #include "cairo-mempool-private.h" #include #include #include #include #define CAIRO_MAX_SHM_MEMORY (16*1024*1024) /* a simple buddy allocator for memory pools * XXX fragmentation? use Doug Lea's malloc? */ typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t; typedef enum { PENDING_WAIT, PENDING_POLL } shm_wait_type_t; struct _cairo_xcb_shm_mem_pool { int shmid; uint32_t shmseg; void *shm; cairo_mempool_t mem; cairo_list_t link; }; static void _cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool) { cairo_list_del (&pool->link); shmdt (pool->shm); _cairo_mempool_fini (&pool->mem); free (pool); } static void _cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info) { cairo_xcb_connection_t *connection = shm_info->connection; assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex)); _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem); _cairo_freepool_free (&connection->shm_info_freelist, shm_info); /* scan for old, unused pools - hold at least one in reserve */ if (! cairo_list_is_singular (&connection->shm_pools)) { cairo_xcb_shm_mem_pool_t *pool, *next; cairo_list_t head; cairo_list_init (&head); cairo_list_move (connection->shm_pools.next, &head); cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, &connection->shm_pools, link) { if (pool->mem.free_bytes == pool->mem.max_bytes) { _cairo_xcb_connection_shm_detach (connection, pool->shmseg); _cairo_xcb_shm_mem_pool_destroy (pool); } } cairo_list_move (head.next, &connection->shm_pools); } } static void _cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait) { cairo_xcb_shm_info_t *info, *next; xcb_get_input_focus_reply_t *reply; assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex)); cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t, &connection->shm_pending, pending) { switch (wait) { case PENDING_WAIT: reply = xcb_wait_for_reply (connection->xcb_connection, info->sync.sequence, NULL); break; case PENDING_POLL: if (! xcb_poll_for_reply (connection->xcb_connection, info->sync.sequence, (void **) &reply, NULL)) /* We cannot be sure the server finished with this image yet, so * try again later. All other shm info are guaranteed to have a * larger sequence number and thus don't have to be checked. */ return; break; default: /* silence Clang static analyzer warning */ ASSERT_NOT_REACHED; reply = NULL; } free (reply); cairo_list_del (&info->pending); _cairo_xcb_shm_info_finalize (info); } } cairo_int_status_t _cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection, size_t size, cairo_bool_t might_reuse, cairo_xcb_shm_info_t **shm_info_out) { cairo_xcb_shm_info_t *shm_info; cairo_xcb_shm_mem_pool_t *pool, *next; size_t bytes, maxbits = 16, minbits = 8; size_t shm_allocated = 0; void *mem = NULL; cairo_status_t status; assert (connection->flags & CAIRO_XCB_HAS_SHM); CAIRO_MUTEX_LOCK (connection->shm_mutex); _cairo_xcb_shm_process_pending (connection, PENDING_POLL); if (might_reuse) { cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t, &connection->shm_pending, pending) { if (shm_info->size >= size) { cairo_list_del (&shm_info->pending); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence); shm_info->sync.sequence = XCB_NONE; *shm_info_out = shm_info; return CAIRO_STATUS_SUCCESS; } } } cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, &connection->shm_pools, link) { if (pool->mem.free_bytes > size) { mem = _cairo_mempool_alloc (&pool->mem, size); if (mem != NULL) { /* keep the active pools towards the front */ cairo_list_move (&pool->link, &connection->shm_pools); goto allocate_shm_info; } } /* scan for old, unused pools */ if (pool->mem.free_bytes == pool->mem.max_bytes) { _cairo_xcb_connection_shm_detach (connection, pool->shmseg); _cairo_xcb_shm_mem_pool_destroy (pool); } else { shm_allocated += pool->mem.max_bytes; } } if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) { CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t)); if (unlikely (pool == NULL)) { CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } bytes = 1 << maxbits; while (bytes <= size) bytes <<= 1, maxbits++; bytes <<= 3; do { pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); if (pool->shmid != -1) break; /* If the allocation failed because we asked for too much memory, we try * again with a smaller request, as long as our allocation still fits. */ bytes >>= 1; if (errno != EINVAL || bytes < size) break; } while (TRUE); if (pool->shmid == -1) { int err = errno; if (! (err == EINVAL || err == ENOMEM)) connection->flags &= ~CAIRO_XCB_HAS_SHM; free (pool); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return CAIRO_INT_STATUS_UNSUPPORTED; } pool->shm = shmat (pool->shmid, NULL, 0); if (unlikely (pool->shm == (char *) -1)) { shmctl (pool->shmid, IPC_RMID, NULL); free (pool); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } status = _cairo_mempool_init (&pool->mem, pool->shm, bytes, minbits, maxbits - minbits + 1); if (unlikely (status)) { shmdt (pool->shm); free (pool); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return status; } pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE); shmctl (pool->shmid, IPC_RMID, NULL); cairo_list_add (&pool->link, &connection->shm_pools); mem = _cairo_mempool_alloc (&pool->mem, size); allocate_shm_info: shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist); if (unlikely (shm_info == NULL)) { _cairo_mempool_free (&pool->mem, mem); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } shm_info->connection = connection; shm_info->pool = pool; shm_info->shm = pool->shmseg; shm_info->size = size; shm_info->offset = (char *) mem - (char *) pool->shm; shm_info->mem = mem; shm_info->sync.sequence = XCB_NONE; /* scan for old, unused pools */ cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, &connection->shm_pools, link) { if (pool->mem.free_bytes == pool->mem.max_bytes) { _cairo_xcb_connection_shm_detach (connection, pool->shmseg); _cairo_xcb_shm_mem_pool_destroy (pool); } } CAIRO_MUTEX_UNLOCK (connection->shm_mutex); *shm_info_out = shm_info; return CAIRO_STATUS_SUCCESS; } void _cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info) { cairo_xcb_connection_t *connection = shm_info->connection; /* We can only return shm_info->mem to the allocator when we can be sure * that the X server no longer reads from it. Since the X server processes * requests in order, we send a GetInputFocus here. * _cairo_xcb_shm_process_pending () will later check if the reply for that * request was received and then actually mark this memory area as free. */ CAIRO_MUTEX_LOCK (connection->shm_mutex); assert (shm_info->sync.sequence == XCB_NONE); shm_info->sync = xcb_get_input_focus (connection->xcb_connection); cairo_list_init (&shm_info->pending); cairo_list_add_tail (&shm_info->pending, &connection->shm_pending); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); } void _cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection) { CAIRO_MUTEX_LOCK (connection->shm_mutex); _cairo_xcb_shm_process_pending (connection, PENDING_WAIT); CAIRO_MUTEX_UNLOCK (connection->shm_mutex); } void _cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection) { assert (cairo_list_is_empty (&connection->shm_pending)); while (! cairo_list_is_empty (&connection->shm_pools)) { _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools, cairo_xcb_shm_mem_pool_t, link)); } } #endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-surface-core.c000066400000000000000000000432641271037650300267000ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-boxes-private.h" #include "cairo-xcb-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-backend-private.h" /* XXX dithering */ typedef struct _cairo_xcb_pixmap { cairo_surface_t base; cairo_xcb_connection_t *connection; cairo_xcb_screen_t *screen; cairo_surface_t *owner; xcb_pixmap_t pixmap; int width; int height; int depth; int x0, y0; cairo_bool_t repeat; } cairo_xcb_pixmap_t; static cairo_status_t _cairo_xcb_pixmap_finish (void *abstract_surface) { cairo_xcb_pixmap_t *surface = abstract_surface; cairo_status_t status; if (surface->owner != NULL) { cairo_surface_destroy (surface->owner); } else { status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; _cairo_xcb_connection_free_pixmap (surface->connection, surface->pixmap); _cairo_xcb_connection_release (surface->connection); } return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = { CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_pixmap_finish, }; static cairo_xcb_pixmap_t * _cairo_xcb_pixmap_create (cairo_xcb_surface_t *target, int width, int height) { cairo_xcb_pixmap_t *surface; surface = malloc (sizeof (cairo_xcb_pixmap_t)); if (unlikely (surface == NULL)) return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_pixmap_backend, NULL, target->base.content); surface->connection = target->connection; surface->screen = target->screen; surface->owner = NULL; surface->width = width; surface->height = height; surface->depth = target->depth; surface->x0 = surface->y0 = 0; surface->repeat = FALSE; surface->pixmap = _cairo_xcb_connection_create_pixmap (surface->connection, surface->depth, target->drawable, width, height); return surface; } static cairo_xcb_pixmap_t * _cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target) { cairo_xcb_pixmap_t *surface; surface = malloc (sizeof (cairo_xcb_pixmap_t)); if (unlikely (surface == NULL)) return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_pixmap_backend, NULL, target->base.content); surface->connection = target->connection; surface->screen = target->screen; surface->pixmap = target->drawable; surface->owner = cairo_surface_reference (&target->base); surface->width = target->width; surface->height = target->height; surface->depth = target->depth; surface->x0 = surface->y0 = 0; surface->repeat = FALSE; return surface; } #if CAIRO_HAS_XCB_SHM_FUNCTIONS static cairo_status_t _cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_image_surface_t **image_out, cairo_xcb_shm_info_t **shm_info_out) { cairo_surface_t *image = NULL; cairo_xcb_shm_info_t *shm_info = NULL; cairo_status_t status; size_t size, stride; if (! (connection->flags & CAIRO_XCB_HAS_SHM)) return CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) return CAIRO_INT_STATUS_UNSUPPORTED; stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); size = stride * height; if (size <= CAIRO_XCB_SHM_SMALL_IMAGE) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_xcb_connection_allocate_shm_info (connection, size, FALSE, &shm_info); if (unlikely (status)) return status; image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, pixman_format, width, height, stride); status = image->status; if (unlikely (status)) { _cairo_xcb_shm_info_destroy (shm_info); return status; } status = _cairo_user_data_array_set_data (&image->user_data, (const cairo_user_data_key_t *) connection, shm_info, (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); if (unlikely (status)) { cairo_surface_destroy (image); _cairo_xcb_shm_info_destroy (shm_info); return status; } *image_out = (cairo_image_surface_t *) image; *shm_info_out = shm_info; return CAIRO_STATUS_SUCCESS; } #else static cairo_status_t _cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_image_surface_t **image_out, cairo_xcb_shm_info_t **shm_info_out) { return CAIRO_INT_STATUS_UNSUPPORTED; } #endif cairo_status_t _cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_image_surface_t **image_out, cairo_xcb_shm_info_t **shm_info_out) { cairo_surface_t *image = NULL; cairo_xcb_shm_info_t *shm_info = NULL; cairo_status_t status; status = _cairo_xcb_shm_image_create_shm (connection, pixman_format, width, height, image_out, shm_info_out); if (status != CAIRO_STATUS_SUCCESS) { image = _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, width, height, 0); status = image->status; if (unlikely (status)) return status; *image_out = (cairo_image_surface_t *) image; *shm_info_out = shm_info; } return CAIRO_STATUS_SUCCESS; } static cairo_xcb_pixmap_t * _pixmap_from_image (cairo_xcb_surface_t *target, xcb_render_pictformat_t format, cairo_image_surface_t *image, cairo_xcb_shm_info_t *shm_info) { xcb_gcontext_t gc; cairo_xcb_pixmap_t *pixmap; pixmap = _cairo_xcb_pixmap_create (target, image->width, image->height); if (unlikely (pixmap->base.status)) return pixmap; gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth); if (shm_info != NULL) { _cairo_xcb_connection_shm_put_image (target->connection, pixmap->pixmap, gc, image->width, image->height, 0, 0, image->width, image->height, 0, 0, image->depth, shm_info->shm, shm_info->offset); } else { int len; /* Do we need to trim the image? */ len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)); if (len == image->stride) { _cairo_xcb_connection_put_image (target->connection, pixmap->pixmap, gc, image->width, image->height, 0, 0, image->depth, image->stride, image->data); } else { _cairo_xcb_connection_put_subimage (target->connection, pixmap->pixmap, gc, 0, 0, image->width, image->height, PIXMAN_FORMAT_BPP (image->pixman_format) / 8, image->stride, 0, 0, image->depth, image->data); } } _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); return pixmap; } static cairo_xcb_pixmap_t * _render_to_pixmap (cairo_xcb_surface_t *target, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_image_surface_t *image; cairo_xcb_shm_info_t *shm_info; cairo_pattern_union_t copy; cairo_status_t status; cairo_xcb_pixmap_t *pixmap; status = _cairo_xcb_shm_image_create (target->screen->connection, target->pixman_format, extents->width, extents->height, &image, &shm_info); if (unlikely (status)) return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); _cairo_pattern_init_static_copy (©.base, pattern); cairo_matrix_translate (©.base.matrix, -extents->x, -extents->y); status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, ©.base, NULL); if (unlikely (status)) { cairo_surface_destroy (&image->base); return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); } pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info); cairo_surface_destroy (&image->base); if (unlikely (pixmap->base.status)) return pixmap; pixmap->x0 = -extents->x; pixmap->y0 = -extents->y; return pixmap; } static cairo_xcb_pixmap_t * _copy_to_pixmap (cairo_xcb_surface_t *source) { cairo_xcb_pixmap_t *pixmap; /* If the source may be a window, we need to copy it and its children * via a temporary pixmap so that we can IncludeInferiors on the source * and use ClipByChildren on the destination. */ if (source->owns_pixmap) { pixmap = _cairo_xcb_pixmap_copy (source); if (unlikely (pixmap->base.status)) return pixmap; } else { uint32_t values[1]; xcb_gcontext_t gc; pixmap = _cairo_xcb_pixmap_create (source, source->width, source->height); if (unlikely (pixmap->base.status)) return pixmap; gc = _cairo_xcb_screen_get_gc (source->screen, pixmap->pixmap, pixmap->depth); values[0] = TRUE; _cairo_xcb_connection_change_gc (pixmap->connection, gc, XCB_GC_SUBWINDOW_MODE, values); _cairo_xcb_connection_copy_area (pixmap->connection, source->drawable, pixmap->pixmap, gc, 0, 0, 0, 0, source->width, source->height); values[0] = FALSE; _cairo_xcb_connection_change_gc (pixmap->connection, gc, XCB_GC_SUBWINDOW_MODE, values); _cairo_xcb_screen_put_gc (source->screen, pixmap->depth, gc); } return pixmap; } static cairo_xcb_pixmap_t * _cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, int tx, int ty) { cairo_surface_t *source; cairo_xcb_pixmap_t *pixmap; source = pattern->surface; pixmap = (cairo_xcb_pixmap_t *) _cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend); if (pixmap != NULL && pixmap->screen == target->screen) return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base); if (source->type == CAIRO_SURFACE_TYPE_XCB && ((cairo_xcb_surface_t *) source)->screen == target->screen) { cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source; if (xcb_source->depth == target->depth) pixmap = _copy_to_pixmap (xcb_source); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS else if (source->type == CAIRO_SURFACE_TYPE_XLIB && ((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) { cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb; if (xcb_source->depth == target->depth) pixmap = _copy_to_pixmap (xcb_source); } #endif if (pixmap == NULL) { cairo_rectangle_int_t rect; if (! _cairo_surface_get_extents (source, &rect)) { rect.x = rect.y = 0; rect.width = target->width; rect.height = target->height; } pixmap = _render_to_pixmap (target, &pattern->base, &rect); } if (unlikely (pixmap->base.status)) return pixmap; _cairo_surface_attach_snapshot (source, &pixmap->base, NULL); if (pattern->base.extend != CAIRO_EXTEND_NONE) { if (extents->x < 0 || extents->y < 0 || extents->x + extents->width > pixmap->width || extents->y + extents->height > pixmap->height) { pixmap->repeat = TRUE; } } pixmap->x0 += tx; pixmap->y0 += ty; return pixmap; } static cairo_xcb_pixmap_t * _cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { int tx, ty; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SURFACE: /* Core can only perform a native, unscaled blit, but can handle tiles */ if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) { switch (pattern->extend) { case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_REPEAT: return _cairo_xcb_surface_pixmap (target, (cairo_surface_pattern_t *) pattern, extents, tx, ty); default: case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_REFLECT: break; } } /* fallthrough */ case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _render_to_pixmap (target, pattern, extents); default: case CAIRO_PATTERN_TYPE_SOLID: ASSERT_NOT_REACHED; return NULL; } } cairo_status_t _cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, const cairo_pattern_t *src_pattern, const cairo_rectangle_int_t *extents, const cairo_boxes_t *boxes) { cairo_xcb_pixmap_t *src; const struct _cairo_boxes_chunk *chunk; xcb_gcontext_t gc; cairo_status_t status; status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) return status; src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents); status = src->base.status; if (unlikely (status)) goto CLEANUP_CONNECTION; assert (src->depth == dst->depth); gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth); if (src->repeat) { uint32_t mask = XCB_GC_FILL_STYLE | XCB_GC_TILE | XCB_GC_TILE_STIPPLE_ORIGIN_X | XCB_GC_TILE_STIPPLE_ORIGIN_Y; uint32_t values[] = { XCB_FILL_STYLE_TILED, src->pixmap, - src->x0, - src->y0, }; xcb_rectangle_t *xcb_rects; _cairo_xcb_connection_change_gc (dst->connection, gc, mask, values); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { int i; xcb_rects = (xcb_rectangle_t *) chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); xcb_rects[i].x = x1; xcb_rects[i].y = y1; xcb_rects[i].width = x2 - x1; xcb_rects[i].height = y2 - y1; } _cairo_xcb_connection_poly_fill_rectangle (dst->connection, dst->drawable, gc, chunk->count, xcb_rects); } values[0] = 0; _cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values); } else { for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { int i; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); _cairo_xcb_connection_copy_area (dst->connection, src->pixmap, dst->drawable, gc, src->x0 + x1, src->y0 + y1, x1, y1, x2 - x2, y2 - x2); } } } _cairo_xcb_screen_put_gc (dst->screen, src->depth, gc); cairo_surface_destroy (&src->base); CLEANUP_CONNECTION: _cairo_xcb_connection_release (dst->connection); return status; } cairo_status_t _cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, const cairo_color_t *color, cairo_boxes_t *boxes) { struct _cairo_boxes_chunk *chunk; xcb_gcontext_t gc; cairo_status_t status; status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) return status; gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); #if 0 xcb_pixmap_t source; source = _dither_source (dst, color); XSetTSOrigin (surface->dpy, gc, 0, 0); XSetTile (surface->dpy, gc, source); #endif for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { xcb_rectangle_t *xcb_rects; int i; xcb_rects = (xcb_rectangle_t *) chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); xcb_rects[i].x = x1; xcb_rects[i].y = y1; xcb_rects[i].width = x2 - x1; xcb_rects[i].height = y2 - y1; } _cairo_xcb_connection_poly_fill_rectangle (dst->connection, dst->drawable, gc, chunk->count, xcb_rects); } _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); _cairo_xcb_connection_release (dst->connection); return CAIRO_STATUS_SUCCESS; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-surface-render.c000066400000000000000000004302411271037650300272220ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-xcb-private.h" #include "cairo-boxes-private.h" #include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-region-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-traps-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-inline.h" #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ static cairo_status_t _clip_and_composite_boxes (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, cairo_composite_rectangles_t *extents); static inline cairo_xcb_connection_t * _picture_to_connection (cairo_xcb_picture_t *picture) { return (cairo_xcb_connection_t *) picture->base.device; } static void _cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface); static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } static cairo_status_t _cairo_xcb_picture_finish (void *abstract_surface) { cairo_xcb_picture_t *surface = abstract_surface; cairo_xcb_connection_t *connection = _picture_to_connection (surface); cairo_status_t status; status = _cairo_xcb_connection_acquire (connection); cairo_list_del (&surface->link); if (unlikely (status)) return status; _cairo_xcb_connection_render_free_picture (connection, surface->picture); _cairo_xcb_connection_release (connection); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t _cairo_xcb_picture_backend = { CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_picture_finish, }; static const struct xcb_render_transform_t identity_transform = { 1 << 16, 0, 0, 0, 1 << 16, 0, 0, 0, 1 << 16, }; static cairo_xcb_picture_t * _cairo_xcb_picture_create (cairo_xcb_screen_t *screen, pixman_format_code_t pixman_format, xcb_render_pictformat_t xrender_format, int width, int height) { cairo_xcb_picture_t *surface; surface = malloc (sizeof (cairo_xcb_picture_t)); if (unlikely (surface == NULL)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_picture_backend, &screen->connection->device, _cairo_content_from_pixman_format (pixman_format)); cairo_list_add (&surface->link, &screen->pictures); surface->screen = screen; surface->picture = _cairo_xcb_connection_get_xid (screen->connection); surface->pixman_format = pixman_format; surface->xrender_format = xrender_format; surface->x0 = surface->y0 = 0; surface->x = surface->y = 0; surface->width = width; surface->height = height; surface->transform = identity_transform; surface->extend = CAIRO_EXTEND_NONE; surface->filter = CAIRO_FILTER_NEAREST; surface->has_component_alpha = FALSE; return surface; } static inline cairo_bool_t _operator_is_supported (uint32_t flags, cairo_operator_t op) { if (op <= CAIRO_OPERATOR_SATURATE) return TRUE; /* Can we use PDF operators? */ #if CAIRO_XCB_RENDER_AT_LEAST(0, 11) if (op <= CAIRO_OPERATOR_HSL_LUMINOSITY) return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; #endif return FALSE; } static int _render_operator (cairo_operator_t op) { #define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y switch (op) { C(CLEAR, CLEAR); C(SOURCE, SRC); C(OVER, OVER); C(IN, IN); C(OUT, OUT); C(ATOP, ATOP); C(DEST, DST); C(DEST_OVER, OVER_REVERSE); C(DEST_IN, IN_REVERSE); C(DEST_OUT, OUT_REVERSE); C(DEST_ATOP, ATOP_REVERSE); C(XOR, XOR); C(ADD, ADD); C(SATURATE, SATURATE); /* PDF operators were added in RENDER 0.11, check if the xcb headers have * the defines, else fall through to the default case. */ #if CAIRO_XCB_RENDER_AT_LEAST(0, 11) #define BLEND(x,y) C(x,y) #else #define BLEND(x,y) case CAIRO_OPERATOR_##x: #endif BLEND(MULTIPLY, MULTIPLY); BLEND(SCREEN, SCREEN); BLEND(OVERLAY, OVERLAY); BLEND(DARKEN, DARKEN); BLEND(LIGHTEN, LIGHTEN); BLEND(COLOR_DODGE, COLOR_DODGE); BLEND(COLOR_BURN, COLOR_BURN); BLEND(HARD_LIGHT, HARD_LIGHT); BLEND(SOFT_LIGHT, SOFT_LIGHT); BLEND(DIFFERENCE, DIFFERENCE); BLEND(EXCLUSION, EXCLUSION); BLEND(HSL_HUE, HSL_HUE); BLEND(HSL_SATURATION, HSL_SATURATION); BLEND(HSL_COLOR, HSL_COLOR); BLEND(HSL_LUMINOSITY, HSL_LUMINOSITY); default: ASSERT_NOT_REACHED; return XCB_RENDER_PICT_OP_OVER; } } static cairo_status_t _cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface, cairo_region_t *region) { xcb_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; xcb_rectangle_t *rects = stack_rects; int i, num_rects; num_rects = cairo_region_num_rectangles (region); if (num_rects > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (num_rects, sizeof (xcb_rectangle_t)); if (unlikely (rects == NULL)) { return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].x = rect.x; rects[i].y = rect.y; rects[i].width = rect.width; rects[i].height = rect.height; } _cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection, surface->picture, 0, 0, num_rects, rects); if (rects != stack_rects) free (rects); return CAIRO_STATUS_SUCCESS; } static void _cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface) { uint32_t values[] = { XCB_NONE }; _cairo_xcb_connection_render_change_picture (surface->connection, surface->picture, XCB_RENDER_CP_CLIP_MASK, values); } static void _cairo_xcb_surface_set_precision (cairo_xcb_surface_t *surface, cairo_antialias_t antialias) { cairo_xcb_connection_t *connection = surface->connection; uint32_t precision; if (connection->force_precision != -1) precision = connection->force_precision; else switch (antialias) { default: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_NONE: case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GOOD: precision = XCB_RENDER_POLY_MODE_IMPRECISE; break; case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_BEST: precision = XCB_RENDER_POLY_MODE_PRECISE; break; } if (surface->precision != precision) { _cairo_xcb_connection_render_change_picture (connection, surface->picture, XCB_RENDER_CP_POLY_MODE, &precision); surface->precision = precision; } } static void _cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface) { assert (surface->fallback == NULL); if (surface->picture == XCB_NONE) { uint32_t values[1]; uint32_t flags = 0; if (surface->precision != XCB_RENDER_POLY_MODE_PRECISE) { flags |= XCB_RENDER_CP_POLY_MODE; values[0] = surface->precision; } surface->picture = _cairo_xcb_connection_get_xid (surface->connection); _cairo_xcb_connection_render_create_picture (surface->connection, surface->picture, surface->drawable, surface->xrender_format, flags, values); } } static cairo_xcb_picture_t * _picture_from_image (cairo_xcb_surface_t *target, xcb_render_pictformat_t format, cairo_image_surface_t *image, cairo_xcb_shm_info_t *shm_info) { xcb_pixmap_t pixmap; xcb_gcontext_t gc; cairo_xcb_picture_t *picture; pixmap = _cairo_xcb_connection_create_pixmap (target->connection, image->depth, target->drawable, image->width, image->height); gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth); if (shm_info != NULL) { _cairo_xcb_connection_shm_put_image (target->connection, pixmap, gc, image->width, image->height, 0, 0, image->width, image->height, 0, 0, image->depth, shm_info->shm, shm_info->offset); } else { int len; /* Do we need to trim the image? */ len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)); if (len == image->stride) { _cairo_xcb_connection_put_image (target->connection, pixmap, gc, image->width, image->height, 0, 0, image->depth, image->stride, image->data); } else { _cairo_xcb_connection_put_subimage (target->connection, pixmap, gc, 0, 0, image->width, image->height, PIXMAN_FORMAT_BPP (image->pixman_format) / 8, image->stride, 0, 0, image->depth, image->data); } } _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); picture = _cairo_xcb_picture_create (target->screen, image->pixman_format, format, image->width, image->height); if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) { _cairo_xcb_connection_render_create_picture (target->connection, picture->picture, pixmap, format, 0, 0); } _cairo_xcb_connection_free_pixmap (target->connection, pixmap); return picture; } static cairo_bool_t _pattern_is_supported (uint32_t flags, const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) return TRUE; if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) { if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0) return FALSE; } switch (pattern->extend) { default: ASSERT_NOT_REACHED; case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_REPEAT: break; case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_REFLECT: if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0) return FALSE; } if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_filter_t filter; filter = pattern->filter; if (_cairo_matrix_has_unity_scale (&pattern->matrix) && _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) { filter = CAIRO_FILTER_NEAREST; } if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) { if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0) return FALSE; } } else { /* gradient */ if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0) return FALSE; /* The RENDER specification says that the inner circle has to be * completely contained inside the outer one. */ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL && ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern)) { return FALSE; } } return pattern->type != CAIRO_PATTERN_TYPE_MESH; } static void _cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, const cairo_matrix_t *matrix, cairo_filter_t filter, double xc, double yc) { xcb_render_transform_t transform; pixman_transform_t *pixman_transform; cairo_int_status_t ignored; /* Casting between pixman_transform_t and xcb_render_transform_t is safe * because they happen to be the exact same type. */ pixman_transform = (pixman_transform_t *) &transform; picture->x = picture->x0; picture->y = picture->y0; ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, pixman_transform, &picture->x, &picture->y); (void) ignored; if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) { _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture), picture->picture, &transform); picture->transform = transform; } } static void _cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture, cairo_filter_t filter) { const char *render_filter; int len; if (picture->filter == filter) return; switch (filter) { case CAIRO_FILTER_FAST: render_filter = "fast"; len = strlen ("fast"); break; case CAIRO_FILTER_GOOD: render_filter = "good"; len = strlen ("good"); break; case CAIRO_FILTER_BEST: render_filter = "best"; len = strlen ("best"); break; case CAIRO_FILTER_NEAREST: render_filter = "nearest"; len = strlen ("nearest"); break; case CAIRO_FILTER_BILINEAR: render_filter = "bilinear"; len = strlen ("bilinear"); break; default: ASSERT_NOT_REACHED; case CAIRO_FILTER_GAUSSIAN: render_filter = "best"; len = strlen ("best"); break; } _cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture), picture->picture, len, (char *) render_filter); picture->filter = filter; } static void _cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture, cairo_extend_t extend) { uint32_t pa[1]; if (picture->extend == extend) return; switch (extend) { default: ASSERT_NOT_REACHED; case CAIRO_EXTEND_NONE: pa[0] = XCB_RENDER_REPEAT_NONE; break; case CAIRO_EXTEND_REPEAT: pa[0] = XCB_RENDER_REPEAT_NORMAL; break; case CAIRO_EXTEND_REFLECT: pa[0] = XCB_RENDER_REPEAT_REFLECT; break; case CAIRO_EXTEND_PAD: pa[0] = XCB_RENDER_REPEAT_PAD; break; } _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture), picture->picture, XCB_RENDER_CP_REPEAT, pa); picture->extend = extend; } static void _cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture, cairo_bool_t ca) { uint32_t pa[1]; if (picture->has_component_alpha == ca) return; pa[0] = ca; _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture), picture->picture, XCB_RENDER_CP_COMPONENT_ALPHA, pa); picture->has_component_alpha = ca; } static cairo_xcb_picture_t * _solid_picture (cairo_xcb_surface_t *target, const cairo_color_t *color) { xcb_render_color_t xcb_color; xcb_render_pictformat_t xrender_format; cairo_xcb_picture_t *picture; xcb_color.red = color->red_short; xcb_color.green = color->green_short; xcb_color.blue = color->blue_short; xcb_color.alpha = color->alpha_short; xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; picture = _cairo_xcb_picture_create (target->screen, PIXMAN_a8r8g8b8, xrender_format, -1, -1); if (unlikely (picture->base.status)) return picture; if (target->connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) { _cairo_xcb_connection_render_create_solid_fill (target->connection, picture->picture, xcb_color); } else { xcb_pixmap_t pixmap; uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL }; pixmap = _cairo_xcb_connection_create_pixmap (target->connection, 32, target->drawable, 1, 1); _cairo_xcb_connection_render_create_picture (target->connection, picture->picture, pixmap, xrender_format, XCB_RENDER_CP_REPEAT, values); if (target->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { xcb_rectangle_t rect; rect.x = rect.y = 0; rect.width = rect.height = 1; _cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture), XCB_RENDER_PICT_OP_SRC, picture->picture, xcb_color, 1, &rect); } else { xcb_gcontext_t gc; uint32_t pixel; gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32); /* XXX byte ordering? */ pixel = ((color->alpha_short >> 8) << 24) | ((color->red_short >> 8) << 16) | ((color->green_short >> 8) << 8) | ((color->blue_short >> 8) << 0); _cairo_xcb_connection_put_image (target->connection, pixmap, gc, 1, 1, 0, 0, 32, 4, &pixel); _cairo_xcb_screen_put_gc (target->screen, 32, gc); } _cairo_xcb_connection_free_pixmap (target->connection, pixmap); } return picture; } static cairo_xcb_picture_t * _cairo_xcb_transparent_picture (cairo_xcb_surface_t *target) { cairo_xcb_picture_t *picture; picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT]; if (picture == NULL) { picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT); target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base; } return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); } static cairo_xcb_picture_t * _cairo_xcb_black_picture (cairo_xcb_surface_t *target) { cairo_xcb_picture_t *picture; picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK]; if (picture == NULL) { picture = _solid_picture (target, CAIRO_COLOR_BLACK); target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base; } return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); } static cairo_xcb_picture_t * _cairo_xcb_white_picture (cairo_xcb_surface_t *target) { cairo_xcb_picture_t *picture; picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE]; if (picture == NULL) { picture = _solid_picture (target, CAIRO_COLOR_WHITE); target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base; } return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); } static cairo_xcb_picture_t * _cairo_xcb_solid_picture (cairo_xcb_surface_t *target, const cairo_solid_pattern_t *pattern) { cairo_xcb_picture_t *picture; cairo_xcb_screen_t *screen; int i, n_cached; if (pattern->color.alpha_short <= 0x00ff) return _cairo_xcb_transparent_picture (target); if (pattern->color.alpha_short >= 0xff00) { if (pattern->color.red_short <= 0x00ff && pattern->color.green_short <= 0x00ff && pattern->color.blue_short <= 0x00ff) { return _cairo_xcb_black_picture (target); } if (pattern->color.red_short >= 0xff00 && pattern->color.green_short >= 0xff00 && pattern->color.blue_short >= 0xff00) { return _cairo_xcb_white_picture (target); } } screen = target->screen; n_cached = screen->solid_cache_size; for (i = 0; i < n_cached; i++) { if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) { return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture); } } picture = _solid_picture (target, &pattern->color); if (unlikely (picture->base.status)) return picture; if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) { i = screen->solid_cache_size++; } else { i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache); cairo_surface_destroy (screen->solid_cache[i].picture); } screen->solid_cache[i].picture = cairo_surface_reference (&picture->base); screen->solid_cache[i].color = pattern->color; return picture; } static cairo_xcb_picture_t * _render_to_picture (cairo_xcb_surface_t *target, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_image_surface_t *image; cairo_xcb_shm_info_t *shm_info; cairo_pattern_union_t copy; cairo_status_t status; cairo_xcb_picture_t *picture; pixman_format_code_t pixman_format; xcb_render_pictformat_t xrender_format; /* XXX handle extend modes via tiling? */ /* XXX alpha-only masks? */ pixman_format = PIXMAN_a8r8g8b8; xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; status = _cairo_xcb_shm_image_create (target->screen->connection, pixman_format, extents->width, extents->height, &image, &shm_info); if (unlikely (status)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); _cairo_pattern_init_static_copy (©.base, pattern); cairo_matrix_translate (©.base.matrix, extents->x, extents->y); status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, ©.base, NULL); if (unlikely (status)) { cairo_surface_destroy (&image->base); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); } picture = _picture_from_image (target, xrender_format, image, shm_info); cairo_surface_destroy (&image->base); if (unlikely (picture->base.status)) return picture; _cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha); picture->x = -extents->x; picture->y = -extents->y; return picture; } static xcb_render_fixed_t * _gradient_to_xcb (const cairo_gradient_pattern_t *gradient, unsigned int *n_stops, char *buf, unsigned int buflen) { xcb_render_fixed_t *stops; xcb_render_color_t *colors; unsigned int i; assert (gradient->n_stops > 0); *n_stops = MAX (gradient->n_stops, 2); if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen) { stops = (xcb_render_fixed_t *) buf; } else { stops = _cairo_malloc_ab (*n_stops, sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)); if (unlikely (stops == NULL)) return NULL; } colors = (xcb_render_color_t *) (stops + *n_stops); for (i = 0; i < gradient->n_stops; i++) { stops[i] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); colors[i].red = gradient->stops[i].color.red_short; colors[i].green = gradient->stops[i].color.green_short; colors[i].blue = gradient->stops[i].color.blue_short; colors[i].alpha = gradient->stops[i].color.alpha_short; } /* RENDER does not support gradients with less than 2 stops. If a * gradient has only a single stop, duplicate it to make RENDER * happy. */ if (gradient->n_stops == 1) { stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset); colors[1].red = gradient->stops[0].color.red_short; colors[1].green = gradient->stops[0].color.green_short; colors[1].blue = gradient->stops[0].color.blue_short; colors[1].alpha = gradient->stops[0].color.alpha_short; } return stops; } static cairo_xcb_picture_t * _cairo_xcb_linear_picture (cairo_xcb_surface_t *target, const cairo_linear_pattern_t *pattern, const cairo_rectangle_int_t *extents) { char buf[CAIRO_STACK_BUFFER_SIZE]; xcb_render_fixed_t *stops; xcb_render_color_t *colors; xcb_render_pointfix_t p1, p2; cairo_matrix_t matrix; cairo_circle_double_t extremes[2]; cairo_xcb_picture_t *picture; cairo_status_t status; unsigned int n_stops; _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); picture = (cairo_xcb_picture_t *) _cairo_xcb_screen_lookup_linear_picture (target->screen, pattern); if (picture != NULL) goto setup_picture; stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf)); if (unlikely (stops == NULL)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); picture = _cairo_xcb_picture_create (target->screen, target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], PIXMAN_a8r8g8b8, -1, -1); if (unlikely (picture->base.status)) { if (stops != (xcb_render_fixed_t *) buf) free (stops); return picture; } picture->filter = CAIRO_FILTER_DEFAULT; colors = (xcb_render_color_t *) (stops + n_stops); p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); _cairo_xcb_connection_render_create_linear_gradient (target->connection, picture->picture, p1, p2, n_stops, stops, colors); if (stops != (xcb_render_fixed_t *) buf) free (stops); status = _cairo_xcb_screen_store_linear_picture (target->screen, pattern, &picture->base); if (unlikely (status)) { cairo_surface_destroy (&picture->base); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); } setup_picture: _cairo_xcb_picture_set_matrix (picture, &matrix, pattern->base.base.filter, extents->x + extents->width/2., extents->y + extents->height/2.); _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); _cairo_xcb_picture_set_component_alpha (picture, pattern->base.base.has_component_alpha); return picture; } static cairo_xcb_picture_t * _cairo_xcb_radial_picture (cairo_xcb_surface_t *target, const cairo_radial_pattern_t *pattern, const cairo_rectangle_int_t *extents) { char buf[CAIRO_STACK_BUFFER_SIZE]; xcb_render_fixed_t *stops; xcb_render_color_t *colors; xcb_render_pointfix_t p1, p2; xcb_render_fixed_t r1, r2; cairo_matrix_t matrix; cairo_circle_double_t extremes[2]; cairo_xcb_picture_t *picture; cairo_status_t status; unsigned int n_stops; _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); picture = (cairo_xcb_picture_t *) _cairo_xcb_screen_lookup_radial_picture (target->screen, pattern); if (picture != NULL) goto setup_picture; stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf)); if (unlikely (stops == NULL)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); picture = _cairo_xcb_picture_create (target->screen, target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], PIXMAN_a8r8g8b8, -1, -1); if (unlikely (picture->base.status)) { if (stops != (xcb_render_fixed_t *) buf) free (stops); return picture; } picture->filter = CAIRO_FILTER_DEFAULT; colors = (xcb_render_color_t *) (stops + n_stops); p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); _cairo_xcb_connection_render_create_radial_gradient (target->connection, picture->picture, p1, p2, r1, r2, n_stops, stops, colors); if (stops != (xcb_render_fixed_t *) buf) free (stops); status = _cairo_xcb_screen_store_radial_picture (target->screen, pattern, &picture->base); if (unlikely (status)) { cairo_surface_destroy (&picture->base); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); } setup_picture: _cairo_xcb_picture_set_matrix (picture, &matrix, pattern->base.base.filter, extents->x + extents->width/2., extents->y + extents->height/2.); _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); _cairo_xcb_picture_set_component_alpha (picture, pattern->base.base.has_component_alpha); return picture; } static cairo_xcb_picture_t * _copy_to_picture (cairo_xcb_surface_t *source) { cairo_xcb_picture_t *picture; uint32_t values[] = { 0, 1 }; if (source->deferred_clear) { cairo_status_t status = _cairo_xcb_surface_clear (source); if (unlikely (status)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); } picture = _cairo_xcb_picture_create (source->screen, source->xrender_format, source->pixman_format, source->width, source->height); if (unlikely (picture->base.status)) return picture; _cairo_xcb_connection_render_create_picture (source->connection, picture->picture, source->drawable, source->xrender_format, XCB_RENDER_CP_GRAPHICS_EXPOSURE | XCB_RENDER_CP_SUBWINDOW_MODE, values); return picture; } static void _cairo_xcb_surface_setup_surface_picture(cairo_xcb_picture_t *picture, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_filter_t filter; filter = pattern->base.filter; if (filter != CAIRO_FILTER_NEAREST && _cairo_matrix_has_unity_scale (&pattern->base.matrix) && _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) && _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0))) { filter = CAIRO_FILTER_NEAREST; } _cairo_xcb_picture_set_filter (picture, filter); _cairo_xcb_picture_set_matrix (picture, &pattern->base.matrix, filter, extents->x + extents->width/2., extents->y + extents->height/2.); _cairo_xcb_picture_set_extend (picture, pattern->base.extend); _cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha); } static cairo_xcb_picture_t * record_to_picture (cairo_surface_t *target, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_surface_pattern_t tmp_pattern; cairo_xcb_picture_t *picture; cairo_status_t status; cairo_matrix_t matrix; cairo_surface_t *tmp; cairo_surface_t *source; cairo_rectangle_int_t limit; cairo_extend_t extend; /* XXX: The following was once more or less copied from cairo-xlibs-ource.c, * record_source() and recording_pattern_get_surface(), can we share a * single version? */ /* First get the 'real' recording surface and figure out the size for tmp */ source = _cairo_pattern_get_source (pattern, &limit); assert (_cairo_surface_is_recording (source)); if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { double x1, y1, x2, y2; matrix = pattern->base.matrix; status = cairo_matrix_invert (&matrix); assert (status == CAIRO_STATUS_SUCCESS); x1 = limit.x; y1 = limit.y; x2 = limit.x + limit.width; y2 = limit.y + limit.height; _cairo_matrix_transform_bounding_box (&matrix, &x1, &y1, &x2, &y2, NULL); limit.x = floor (x1); limit.y = floor (y1); limit.width = ceil (x2) - limit.x; limit.height = ceil (y2) - limit.y; } extend = pattern->base.extend; if (_cairo_rectangle_contains_rectangle (&limit, extents)) extend = CAIRO_EXTEND_NONE; if (extend == CAIRO_EXTEND_NONE && ! _cairo_rectangle_intersect (&limit, extents)) return _cairo_xcb_transparent_picture ((cairo_xcb_surface_t *) target); /* Now draw the recording surface to an xcb surface */ tmp = _cairo_surface_create_similar_solid (target, source->content, limit.width, limit.height, CAIRO_COLOR_TRANSPARENT); if (tmp->status != CAIRO_STATUS_SUCCESS) { return (cairo_xcb_picture_t *) tmp; } cairo_matrix_init_translate (&matrix, limit.x, limit.y); cairo_matrix_multiply (&matrix, &matrix, &pattern->base.matrix); status = _cairo_recording_surface_replay_with_clip (source, &matrix, tmp, NULL); if (unlikely (status)) { cairo_surface_destroy (tmp); return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); } /* Now that we have drawn this to an xcb surface, try again with that */ _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base); tmp_pattern.surface = tmp; cairo_matrix_init_translate (&tmp_pattern.base.matrix, -limit.x, -limit.y); picture = _copy_to_picture ((cairo_xcb_surface_t *) tmp); if (picture->base.status == CAIRO_STATUS_SUCCESS) _cairo_xcb_surface_setup_surface_picture (picture, &tmp_pattern, extents); cairo_surface_destroy (tmp); return picture; } static cairo_xcb_picture_t * _cairo_xcb_surface_picture (cairo_xcb_surface_t *target, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_surface_t *source = pattern->surface; cairo_xcb_picture_t *picture; picture = (cairo_xcb_picture_t *) _cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend); if (picture != NULL) { if (picture->screen == target->screen) { picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); _cairo_xcb_surface_setup_surface_picture (picture, pattern, extents); return picture; } picture = NULL; } if (source->type == CAIRO_SURFACE_TYPE_XCB) { if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) { cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) source; if (xcb->screen == target->screen && xcb->fallback == NULL) { picture = _copy_to_picture ((cairo_xcb_surface_t *) source); if (unlikely (picture->base.status)) return picture; } } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target; /* XXX repeat interval with source clipping? */ if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) { xcb_rectangle_t rect; picture = _copy_to_picture (xcb); if (unlikely (picture->base.status)) return picture; rect.x = sub->extents.x; rect.y = sub->extents.y; rect.width = sub->extents.width; rect.height = sub->extents.height; _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, picture->picture, 0, 0, 1, &rect); picture->x0 = rect.x; picture->y0 = rect.y; picture->width = rect.width; picture->height = rect.height; } } else if (_cairo_surface_is_snapshot (source)) { cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target; if (xcb->screen == target->screen && xcb->fallback == NULL) { picture = _copy_to_picture (xcb); if (unlikely (picture->base.status)) return picture; } } } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS else if (source->type == CAIRO_SURFACE_TYPE_XLIB) { if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) { cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) source)->xcb; if (xcb->screen == target->screen && xcb->fallback == NULL) { picture = _copy_to_picture (xcb); if (unlikely (picture->base.status)) return picture; } } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb; if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) { xcb_rectangle_t rect; picture = _copy_to_picture (xcb); if (unlikely (picture->base.status)) return picture; rect.x = sub->extents.x; rect.y = sub->extents.y; rect.width = sub->extents.width; rect.height = sub->extents.height; _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, picture->picture, 0, 0, 1, &rect); picture->x0 = rect.x; picture->y0 = rect.y; picture->width = rect.width; picture->height = rect.height; } } else if (_cairo_surface_is_snapshot (source)) { cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb; if (xcb->screen == target->screen && xcb->fallback == NULL) { picture = _copy_to_picture (xcb); if (unlikely (picture->base.status)) return picture; } } } #endif #if CAIRO_HAS_GL_FUNCTIONS else if (source->type == CAIRO_SURFACE_TYPE_GL) { /* pixmap from texture */ } #endif else if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { /* We have to skip the call to attach_snapshot() because we possibly * only drew part of the recording surface. * TODO: When can we safely attach a snapshot? */ return record_to_picture(&target->base, pattern, extents); } if (picture == NULL) { cairo_image_surface_t *image; void *image_extra; cairo_status_t status; status = _cairo_surface_acquire_source_image (source, &image, &image_extra); if (unlikely (status)) return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); if (image->format != CAIRO_FORMAT_INVALID) { xcb_render_pictformat_t format; format = target->screen->connection->standard_formats[image->format]; picture = _picture_from_image (target, format, image, NULL); _cairo_surface_release_source_image (source, image, image_extra); } else { cairo_image_surface_t *conv; xcb_render_pictformat_t render_format; /* XXX XRenderPutImage! */ conv = _cairo_image_surface_coerce (image); _cairo_surface_release_source_image (source, image, image_extra); if (unlikely (conv->base.status)) return (cairo_xcb_picture_t *) conv; render_format = target->screen->connection->standard_formats[conv->format]; picture = _picture_from_image (target, render_format, conv, NULL); cairo_surface_destroy (&conv->base); } if (unlikely (picture->base.status)) return picture; } _cairo_surface_attach_snapshot (source, &picture->base, NULL); _cairo_xcb_surface_setup_surface_picture (picture, pattern, extents); return picture; } static cairo_xcb_picture_t * _cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { if (pattern == NULL) return _cairo_xcb_white_picture (target); if (! _pattern_is_supported (target->connection->flags, pattern)) return _render_to_picture (target, pattern, extents); switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_xcb_linear_picture (target, (cairo_linear_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_xcb_radial_picture (target, (cairo_radial_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_xcb_surface_picture (target, (cairo_surface_pattern_t *) pattern, extents); default: ASSERT_NOT_REACHED; case CAIRO_PATTERN_TYPE_MESH: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return _render_to_picture (target, pattern, extents); } } COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t)); static cairo_status_t _render_fill_boxes (void *abstract_dst, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_xcb_surface_t *dst = abstract_dst; xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))]; xcb_rectangle_t *xrects = stack_xrects; xcb_render_color_t render_color; int render_op = _render_operator (op); struct _cairo_boxes_chunk *chunk; int max_count; render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; max_count = 0; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { if (chunk->count > max_count) max_count = chunk->count; } if (max_count > ARRAY_LENGTH (stack_xrects)) { xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t)); if (unlikely (xrects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { int i, j; for (i = j = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y); if (x2 > x1 && y2 > y1) { xrects[j].x = x1; xrects[j].y = y1; xrects[j].width = x2 - x1; xrects[j].height = y2 - y1; j++; } } if (j) { _cairo_xcb_connection_render_fill_rectangles (dst->connection, render_op, dst->picture, render_color, j, xrects); } } if (xrects != stack_xrects) free (xrects); return CAIRO_STATUS_SUCCESS; } /* pixel aligned, non-overlapping boxes */ static cairo_int_status_t _render_composite_boxes (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, const cairo_pattern_t *mask_pattern, const cairo_rectangle_int_t *extents, const cairo_boxes_t *boxes) { cairo_xcb_picture_t *src, *mask; const struct _cairo_boxes_chunk *chunk; xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; xcb_rectangle_t *clip_boxes; cairo_rectangle_int_t stack_extents; cairo_status_t status; int num_boxes; int render_op; render_op = _render_operator (op); if (src_pattern == NULL) { src_pattern = mask_pattern; mask_pattern = NULL; } /* amalgamate into a single Composite call by setting a clip region */ clip_boxes = stack_boxes; if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) { clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t)); if (unlikely (clip_boxes == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); status = src->base.status; if (unlikely (status)) goto cleanup_boxes; num_boxes = 0; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; int i; for (i = 0; i < chunk->count; i++) { int x = _cairo_fixed_integer_round_down (box[i].p1.x); int y = _cairo_fixed_integer_round_down (box[i].p1.y); int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x; int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y; if (width && height) { clip_boxes[num_boxes].x = x; clip_boxes[num_boxes].y = y; clip_boxes[num_boxes].width = width; clip_boxes[num_boxes].height = height; num_boxes++; } } } if (num_boxes) { if (num_boxes > 1) { _cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection, dst->picture, 0, 0, num_boxes, clip_boxes); } else { stack_extents.x = clip_boxes[0].x; stack_extents.y = clip_boxes[0].y; stack_extents.width = clip_boxes[0].width; stack_extents.height = clip_boxes[0].height; extents = &stack_extents; } if (mask_pattern != NULL) { mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); status = mask->base.status; if (unlikely (status)) goto cleanup_clip; _cairo_xcb_connection_render_composite (dst->connection, render_op, src->picture, mask->picture, dst->picture, src->x + extents->x, src->y + extents->y, mask->x + extents->x, mask->y + extents->y, extents->x, extents->y, extents->width, extents->height); cairo_surface_destroy (&mask->base); } else { _cairo_xcb_connection_render_composite (dst->connection, render_op, src->picture, XCB_NONE, dst->picture, src->x + extents->x, src->y + extents->y, 0, 0, extents->x, extents->y, extents->width, extents->height); } cleanup_clip: if (num_boxes > 1) _cairo_xcb_surface_clear_clip_region (dst); } cairo_surface_destroy (&src->base); cleanup_boxes: if (clip_boxes != stack_boxes) free (clip_boxes); return status; } #define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) #define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) static cairo_bool_t _line_exceeds_16_16 (const cairo_line_t *line) { return line->p1.x <= CAIRO_FIXED_16_16_MIN || line->p1.x >= CAIRO_FIXED_16_16_MAX || line->p2.x <= CAIRO_FIXED_16_16_MIN || line->p2.x >= CAIRO_FIXED_16_16_MAX || line->p1.y <= CAIRO_FIXED_16_16_MIN || line->p1.y >= CAIRO_FIXED_16_16_MAX || line->p2.y <= CAIRO_FIXED_16_16_MIN || line->p2.y >= CAIRO_FIXED_16_16_MAX; } static void _project_line_x_onto_16_16 (const cairo_line_t *line, cairo_fixed_t top, cairo_fixed_t bottom, xcb_render_linefix_t *out) { cairo_point_double_t p1, p2; double m; p1.x = _cairo_fixed_to_double (line->p1.x); p1.y = _cairo_fixed_to_double (line->p1.y); p2.x = _cairo_fixed_to_double (line->p2.x); p2.y = _cairo_fixed_to_double (line->p2.y); m = (p2.x - p1.x) / (p2.y - p1.y); out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); } typedef struct { cairo_traps_t traps; cairo_antialias_t antialias; } composite_traps_info_t; COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t)); static cairo_int_status_t _composite_traps (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { composite_traps_info_t *info = closure; const cairo_traps_t *traps = &info->traps; cairo_xcb_picture_t *src; cairo_format_t format; xcb_render_pictformat_t xrender_format; xcb_render_trapezoid_t *xtraps; int render_reference_x, render_reference_y; cairo_status_t status; int i; if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); if (unlikely (src->base.status)) return src->base.status; if (info->antialias == CAIRO_ANTIALIAS_NONE) format = CAIRO_FORMAT_A1; else format = CAIRO_FORMAT_A8; xrender_format = dst->screen->connection->standard_formats[format]; xtraps = (xcb_render_trapezoid_t *) traps->traps; for (i = 0; i < traps->num_traps; i++) { cairo_trapezoid_t t = traps->traps[i]; /* top/bottom will be clamped to surface bounds */ xtraps[i].top = _cairo_fixed_to_16_16 (t.top); xtraps[i].top -= dst_y << 16; xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom); xtraps[i].bottom -= dst_y << 16; /* However, all the other coordinates will have been left untouched so * as not to introduce numerical error. Recompute them if they * exceed the 16.16 limits. */ if (unlikely (_line_exceeds_16_16 (&t.left))) { _project_line_x_onto_16_16 (&t.left, t.top, t.bottom, &xtraps[i].left); xtraps[i].left.p1.y = xtraps[i].top; xtraps[i].left.p2.y = xtraps[i].bottom; } else { xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x); xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y); xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x); xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y); } xtraps[i].left.p1.x -= dst_x << 16; xtraps[i].left.p1.y -= dst_y << 16; xtraps[i].left.p2.x -= dst_x << 16; xtraps[i].left.p2.y -= dst_y << 16; if (unlikely (_line_exceeds_16_16 (&t.right))) { _project_line_x_onto_16_16 (&t.right, t.top, t.bottom, &xtraps[i].right); xtraps[i].right.p1.y = xtraps[i].top; xtraps[i].right.p2.y = xtraps[i].bottom; } else { xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x); xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y); xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x); xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y); } xtraps[i].right.p1.x -= dst_x << 16; xtraps[i].right.p1.y -= dst_y << 16; xtraps[i].right.p2.x -= dst_x << 16; xtraps[i].right.p2.y -= dst_y << 16; } if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { render_reference_x = xtraps[0].left.p1.x >> 16; render_reference_y = xtraps[0].left.p1.y >> 16; } else { render_reference_x = xtraps[0].left.p2.x >> 16; render_reference_y = xtraps[0].left.p2.y >> 16; } render_reference_x += src->x + dst_x; render_reference_y += src->y + dst_y; _cairo_xcb_surface_set_precision (dst, info->antialias); _cairo_xcb_connection_render_trapezoids (dst->connection, _render_operator (op), src->picture, dst->picture, xrender_format, render_reference_x, render_reference_y, traps->num_traps, xtraps); cairo_surface_destroy (&src->base); return CAIRO_STATUS_SUCCESS; } /* low-level composite driver */ static cairo_xcb_surface_t * get_clip_surface (const cairo_clip_t *clip, cairo_xcb_surface_t *target, int *tx, int *ty) { cairo_surface_t *surface; cairo_status_t status; surface = _cairo_surface_create_similar_solid (&target->base, CAIRO_CONTENT_ALPHA, clip->extents.width, clip->extents.height, CAIRO_COLOR_WHITE); if (unlikely (surface->status)) return (cairo_xcb_surface_t *) surface; assert (surface->backend == &_cairo_xcb_surface_backend); status = _cairo_clip_combine_with_surface (clip, surface, clip->extents.x, clip->extents.y); if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } *tx = clip->extents.x; *ty = clip->extents.y; return (cairo_xcb_surface_t *) surface; } typedef cairo_int_status_t (*xcb_draw_func_t) (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip); static void do_unaligned_row(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int y, int h, uint16_t coverage) { int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; if (x2 > x1) { if (! _cairo_fixed_is_integer (b->p1.x)) { blt(closure, x1, y, 1, h, coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); x1++; } if (x2 > x1) blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); if (! _cairo_fixed_is_integer (b->p2.x)) blt(closure, x2, y, 1, h, coverage * _cairo_fixed_fractional_part (b->p2.x)); } else blt(closure, x1, y, 1, h, coverage * (b->p2.x - b->p1.x)); } static void do_unaligned_box(void (*blt)(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage), void *closure, const cairo_box_t *b, int tx, int ty) { int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; if (y2 > y1) { if (! _cairo_fixed_is_integer (b->p1.y)) { do_unaligned_row(blt, closure, b, tx, y1, 1, 256 - _cairo_fixed_fractional_part (b->p1.y)); y1++; } if (y2 > y1) do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); if (! _cairo_fixed_is_integer (b->p2.y)) do_unaligned_row(blt, closure, b, tx, y2, 1, _cairo_fixed_fractional_part (b->p2.y)); } else do_unaligned_row(blt, closure, b, tx, y1, 1, b->p2.y - b->p1.y); } static void blt_in(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { cairo_xcb_surface_t *mask = closure; xcb_render_color_t color; xcb_rectangle_t rect; if (coverage == 0xffff) return; color.red = color.green = color.blue = 0; color.alpha = coverage; rect.x = x; rect.y = y; rect.width = w; rect.height = h; _cairo_xcb_connection_render_fill_rectangles (mask->connection, XCB_RENDER_PICT_OP_IN, mask->picture, color, 1, &rect); } static cairo_xcb_surface_t * _create_composite_mask (cairo_clip_t *clip, xcb_draw_func_t draw_func, xcb_draw_func_t mask_func, void *draw_closure, cairo_xcb_surface_t *dst, const cairo_rectangle_int_t*extents) { cairo_xcb_surface_t *surface; cairo_bool_t need_clip_combine; cairo_int_status_t status; surface = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA, extents->width, extents->height); if (unlikely (surface->base.status)) return surface; _cairo_xcb_surface_ensure_picture (surface); surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT; surface->deferred_clear = TRUE; surface->base.is_clear = TRUE; if (mask_func) { status = mask_func (draw_closure, surface, CAIRO_OPERATOR_ADD, NULL, extents->x, extents->y, extents, clip); if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) return surface; } /* Is it worth setting the clip region here? */ status = draw_func (draw_closure, surface, CAIRO_OPERATOR_ADD, NULL, extents->x, extents->y, extents, NULL); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); } if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { int i; for (i = 0; i < clip->num_boxes; i++) { cairo_box_t *b = &clip->boxes[i]; if (! _cairo_fixed_is_integer (b->p1.x) || ! _cairo_fixed_is_integer (b->p1.y) || ! _cairo_fixed_is_integer (b->p2.x) || ! _cairo_fixed_is_integer (b->p2.y)) { do_unaligned_box(blt_in, surface, b, extents->x, extents->y); } } need_clip_combine = clip->path != NULL; } else need_clip_combine = ! _cairo_clip_is_region (clip); if (need_clip_combine) { status = _cairo_clip_combine_with_surface (clip, &surface->base, extents->x, extents->y); if (unlikely (status)) { cairo_surface_destroy (&surface->base); return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); } } return surface; } /* Handles compositing with a clip surface when the operator allows * us to combine the clip with the mask */ static cairo_status_t _clip_and_composite_with_mask (cairo_clip_t *clip, cairo_operator_t op, const cairo_pattern_t *pattern, xcb_draw_func_t draw_func, xcb_draw_func_t mask_func, void *draw_closure, cairo_xcb_surface_t *dst, const cairo_rectangle_int_t*extents) { cairo_xcb_surface_t *mask; cairo_xcb_picture_t *src; mask = _create_composite_mask (clip, draw_func, mask_func, draw_closure, dst, extents); if (unlikely (mask->base.status)) return mask->base.status; if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) { src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); if (unlikely (src->base.status)) { cairo_surface_destroy (&mask->base); return src->base.status; } _cairo_xcb_connection_render_composite (dst->connection, _render_operator (op), src->picture, mask->picture, dst->picture, extents->x + src->x, extents->y + src->y, 0, 0, extents->x, extents->y, extents->width, extents->height); cairo_surface_destroy (&src->base); } else { _cairo_xcb_connection_render_composite (dst->connection, _render_operator (op), mask->picture, XCB_NONE, dst->picture, 0, 0, 0, 0, extents->x, extents->y, extents->width, extents->height); } cairo_surface_destroy (&mask->base); return CAIRO_STATUS_SUCCESS; } /* Handles compositing with a clip surface when we have to do the operation * in two pieces and combine them together. */ static cairo_status_t _clip_and_composite_combine (cairo_clip_t *clip, cairo_operator_t op, const cairo_pattern_t *pattern, xcb_draw_func_t draw_func, void *draw_closure, cairo_xcb_surface_t *dst, const cairo_rectangle_int_t*extents) { cairo_xcb_surface_t *tmp; cairo_xcb_surface_t *clip_surface; int clip_x = 0, clip_y = 0; xcb_render_picture_t clip_picture; cairo_status_t status; tmp = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_similar (dst, dst->base.content, extents->width, extents->height); if (unlikely (tmp->base.status)) return tmp->base.status; /* create_similar() could have done a fallback to an image surface */ assert (tmp->base.backend == &_cairo_xcb_surface_backend); _cairo_xcb_surface_ensure_picture (tmp); if (pattern == NULL) { status = (*draw_func) (draw_closure, tmp, CAIRO_OPERATOR_ADD, NULL, extents->x, extents->y, extents, NULL); } else { /* Initialize the temporary surface from the destination surface */ if (! dst->base.is_clear || (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0) { /* XCopyArea may actually be quicker here. * A good driver should translate if appropriate. */ _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_SRC, dst->picture, XCB_NONE, tmp->picture, extents->x, extents->y, 0, 0, 0, 0, extents->width, extents->height); } else { xcb_render_color_t clear; xcb_rectangle_t xrect; clear.red = clear.green = clear.blue = clear.alpha = 0; xrect.x = xrect.y = 0; xrect.width = extents->width; xrect.height = extents->height; _cairo_xcb_connection_render_fill_rectangles (dst->connection, XCB_RENDER_PICT_OP_CLEAR, dst->picture, clear, 1, &xrect); } status = (*draw_func) (draw_closure, tmp, op, pattern, extents->x, extents->y, extents, NULL); } if (unlikely (status)) goto CLEANUP_SURFACE; clip_surface = get_clip_surface (clip, dst, &clip_x, &clip_y); status = clip_surface->base.status; if (unlikely (status)) goto CLEANUP_SURFACE; assert (clip_surface->base.backend == &_cairo_xcb_surface_backend); clip_picture = clip_surface->picture; assert (clip_picture != XCB_NONE); if (dst->base.is_clear) { _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_SRC, tmp->picture, clip_picture, dst->picture, 0, 0, 0, 0, extents->x, extents->y, extents->width, extents->height); } else { /* Punch the clip out of the destination */ _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, clip_picture, XCB_NONE, dst->picture, extents->x - clip_x, extents->y - clip_y, 0, 0, extents->x, extents->y, extents->width, extents->height); /* Now add the two results together */ _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_ADD, tmp->picture, clip_picture, dst->picture, 0, 0, extents->x - clip_x, extents->y - clip_y, extents->x, extents->y, extents->width, extents->height); } cairo_surface_destroy (&clip_surface->base); CLEANUP_SURFACE: cairo_surface_destroy (&tmp->base); return status; } /* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) */ static cairo_status_t _clip_and_composite_source (cairo_clip_t *clip, const cairo_pattern_t *pattern, xcb_draw_func_t draw_func, xcb_draw_func_t mask_func, void *draw_closure, cairo_xcb_surface_t *dst, const cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *mask; cairo_xcb_picture_t *src; /* Create a surface that is mask IN clip */ mask = _create_composite_mask (clip, draw_func, mask_func, draw_closure, dst, extents); if (unlikely (mask->base.status)) return mask->base.status; src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); if (unlikely (src->base.status)) { cairo_surface_destroy (&mask->base); return src->base.status; } if (dst->base.is_clear) { _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_SRC, src->picture, mask->picture, dst->picture, extents->x + src->x, extents->y + src->y, 0, 0, extents->x, extents->y, extents->width, extents->height); } else { /* Compute dest' = dest OUT (mask IN clip) */ _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, mask->picture, XCB_NONE, dst->picture, 0, 0, 0, 0, extents->x, extents->y, extents->width, extents->height); /* Now compute (src IN (mask IN clip)) ADD dest' */ _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_ADD, src->picture, mask->picture, dst->picture, extents->x + src->x, extents->y + src->y, 0, 0, extents->x, extents->y, extents->width, extents->height); } cairo_surface_destroy (&src->base); cairo_surface_destroy (&mask->base); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t can_reduce_alpha_op (cairo_operator_t op) { int iop = op; switch (iop) { case CAIRO_OPERATOR_OVER: case CAIRO_OPERATOR_SOURCE: case CAIRO_OPERATOR_ADD: return TRUE; default: return FALSE; } } static cairo_bool_t reduce_alpha_op (cairo_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern) { return dst->is_clear && dst->content == CAIRO_CONTENT_ALPHA && _cairo_pattern_is_opaque_solid (pattern) && can_reduce_alpha_op (op); } static cairo_status_t _cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst, const cairo_composite_rectangles_t *rects) { xcb_rectangle_t xrects[4]; int n; if (rects->bounded.width == rects->unbounded.width && rects->bounded.height == rects->unbounded.height) { return CAIRO_STATUS_SUCCESS; } n = 0; if (rects->bounded.width == 0 || rects->bounded.height == 0) { xrects[n].x = rects->unbounded.x; xrects[n].width = rects->unbounded.width; xrects[n].y = rects->unbounded.y; xrects[n].height = rects->unbounded.height; n++; } else { /* top */ if (rects->bounded.y != rects->unbounded.y) { xrects[n].x = rects->unbounded.x; xrects[n].width = rects->unbounded.width; xrects[n].y = rects->unbounded.y; xrects[n].height = rects->bounded.y - rects->unbounded.y; n++; } /* left */ if (rects->bounded.x != rects->unbounded.x) { xrects[n].x = rects->unbounded.x; xrects[n].width = rects->bounded.x - rects->unbounded.x; xrects[n].y = rects->bounded.y; xrects[n].height = rects->bounded.height; n++; } /* right */ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { xrects[n].x = rects->bounded.x + rects->bounded.width; xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x; xrects[n].y = rects->bounded.y; xrects[n].height = rects->bounded.height; n++; } /* bottom */ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { xrects[n].x = rects->unbounded.x; xrects[n].width = rects->unbounded.width; xrects[n].y = rects->bounded.y + rects->bounded.height; xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y; n++; } } if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { xcb_render_color_t color; color.red = 0; color.green = 0; color.blue = 0; color.alpha = 0; _cairo_xcb_connection_render_fill_rectangles (dst->connection, XCB_RENDER_PICT_OP_CLEAR, dst->picture, color, n, xrects); } else { int i; cairo_xcb_picture_t *src; src = _cairo_xcb_transparent_picture (dst); if (unlikely (src->base.status)) return src->base.status; for (i = 0; i < n; i++) { _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_CLEAR, src->picture, XCB_NONE, dst->picture, 0, 0, 0, 0, xrects[i].x, xrects[i].y, xrects[i].width, xrects[i].height); } cairo_surface_destroy (&src->base); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst, const cairo_composite_rectangles_t *rects, cairo_clip_t *clip) { cairo_xcb_surface_t *mask; int mask_x = 0, mask_y = 0; mask = get_clip_surface (clip, dst, &mask_x, &mask_y); if (unlikely (mask->base.status)) return mask->base.status; /* top */ if (rects->bounded.y != rects->unbounded.y) { int x = rects->unbounded.x; int y = rects->unbounded.y; int width = rects->unbounded.width; int height = rects->bounded.y - y; _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, mask->picture, XCB_NONE, dst->picture, x - mask_x, y - mask_y, 0, 0, x, y, width, height); } /* left */ if (rects->bounded.x != rects->unbounded.x) { int x = rects->unbounded.x; int y = rects->bounded.y; int width = rects->bounded.x - x; int height = rects->bounded.height; _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, mask->picture, XCB_NONE, dst->picture, x - mask_x, y - mask_y, 0, 0, x, y, width, height); } /* right */ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { int x = rects->bounded.x + rects->bounded.width; int y = rects->bounded.y; int width = rects->unbounded.x + rects->unbounded.width - x; int height = rects->bounded.height; _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, mask->picture, XCB_NONE, dst->picture, x - mask_x, y - mask_y, 0, 0, x, y, width, height); } /* bottom */ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { int x = rects->unbounded.x; int y = rects->bounded.y + rects->bounded.height; int width = rects->unbounded.width; int height = rects->unbounded.y + rects->unbounded.height - y; _cairo_xcb_connection_render_composite (dst->connection, XCB_RENDER_PICT_OP_OUT_REVERSE, mask->picture, XCB_NONE, dst->picture, x - mask_x, y - mask_y, 0, 0, x, y, width, height); } cairo_surface_destroy (&mask->base); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip, cairo_boxes_t *boxes) { cairo_boxes_t clear; cairo_box_t box; cairo_status_t status; struct _cairo_boxes_chunk *chunk; int i; if (boxes->num_boxes <= 1 && clip == NULL) return _cairo_xcb_surface_fixup_unbounded (dst, extents); _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (clip == NULL) { cairo_boxes_t tmp; _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; } else { _cairo_boxes_init_with_clip (&clear, clip); status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &chunk->base[i]); if (unlikely (status)) { _cairo_boxes_fini (&clear); return status; } } } status = _cairo_bentley_ottmann_tessellate_boxes (&clear, CAIRO_FILL_RULE_WINDING, &clear); } if (likely (status == CAIRO_STATUS_SUCCESS)) { if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) status = _render_fill_boxes (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); else status = _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, &clear); } _cairo_boxes_fini (&clear); return status; } cairo_status_t _cairo_xcb_surface_clear (cairo_xcb_surface_t *dst) { xcb_gcontext_t gc; xcb_rectangle_t rect; cairo_status_t status; status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) return status; rect.x = rect.y = 0; rect.width = dst->width; rect.height = dst->height; if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { xcb_render_color_t color; uint8_t op; color.red = dst->deferred_clear_color.red_short; color.green = dst->deferred_clear_color.green_short; color.blue = dst->deferred_clear_color.blue_short; color.alpha = dst->deferred_clear_color.alpha_short; if (color.alpha == 0) op = XCB_RENDER_PICT_OP_CLEAR; else op = XCB_RENDER_PICT_OP_SRC; _cairo_xcb_surface_ensure_picture (dst); _cairo_xcb_connection_render_fill_rectangles (dst->connection, op, dst->picture, color, 1, &rect); } else { gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); /* XXX color */ _cairo_xcb_connection_poly_fill_rectangle (dst->connection, dst->drawable, gc, 1, &rect); _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); } _cairo_xcb_connection_release (dst->connection); dst->deferred_clear = FALSE; return CAIRO_STATUS_SUCCESS; } enum { NEED_CLIP_REGION = 0x1, NEED_CLIP_SURFACE = 0x2, FORCE_CLIP_REGION = 0x4, }; static cairo_bool_t need_bounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = NEED_CLIP_REGION; if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_bool_t need_unbounded_clip (cairo_composite_rectangles_t *extents) { unsigned int flags = 0; if (! extents->is_bounded) { flags |= NEED_CLIP_REGION; if (! _cairo_clip_is_region (extents->clip)) flags |= NEED_CLIP_SURFACE; } if (extents->clip->path != NULL) flags |= NEED_CLIP_SURFACE; return flags; } static cairo_status_t _clip_and_composite (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, xcb_draw_func_t draw_func, xcb_draw_func_t mask_func, void *draw_closure, cairo_composite_rectangles_t*extents, unsigned int need_clip) { cairo_region_t *clip_region = NULL; cairo_status_t status; status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) return status; if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) { _cairo_xcb_connection_release (dst->connection); return status; } } _cairo_xcb_surface_ensure_picture (dst); if (need_clip & NEED_CLIP_REGION) { clip_region = _cairo_clip_get_region (extents->clip); if ((need_clip & FORCE_CLIP_REGION) == 0 && clip_region != NULL && cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; if (clip_region != NULL) { status = _cairo_xcb_surface_set_clip_region (dst, clip_region); if (unlikely (status)) { _cairo_xcb_connection_release (dst->connection); return status; } } } if (reduce_alpha_op (&dst->base, op, src)) { op = CAIRO_OPERATOR_ADD; src = NULL; } if (extents->bounded.width != 0 && extents->bounded.height != 0) { if (op == CAIRO_OPERATOR_SOURCE) { status = _clip_and_composite_source (extents->clip, src, draw_func, mask_func, draw_closure, dst, &extents->bounded); } else { if (op == CAIRO_OPERATOR_CLEAR) { op = CAIRO_OPERATOR_DEST_OUT; src = NULL; } if (need_clip & NEED_CLIP_SURFACE) { if (extents->is_bounded) { status = _clip_and_composite_with_mask (extents->clip, op, src, draw_func, mask_func, draw_closure, dst, &extents->bounded); } else { status = _clip_and_composite_combine (extents->clip, op, src, draw_func, draw_closure, dst, &extents->bounded); } } else { status = draw_func (draw_closure, dst, op, src, 0, 0, &extents->bounded, extents->clip); } } } if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { if (need_clip & NEED_CLIP_SURFACE) status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip); else status = _cairo_xcb_surface_fixup_unbounded (dst, extents); } if (clip_region) _cairo_xcb_surface_clear_clip_region (dst); _cairo_xcb_connection_release (dst->connection); return status; } static cairo_status_t _core_boxes (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, const cairo_composite_rectangles_t *extents) { if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_clip_is_region (extents->clip)) return CAIRO_INT_STATUS_UNSUPPORTED; if (op == CAIRO_OPERATOR_CLEAR) return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes); if (op == CAIRO_OPERATOR_OVER) { if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded)) op = CAIRO_OPERATOR_SOURCE; } if (op != CAIRO_OPERATOR_SOURCE) return CAIRO_INT_STATUS_UNSUPPORTED; if (src->type == CAIRO_PATTERN_TYPE_SOLID) { return _cairo_xcb_surface_core_fill_boxes (dst, &((cairo_solid_pattern_t *) src)->color, boxes); } return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes); } static cairo_status_t _composite_boxes (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, const cairo_composite_rectangles_t *extents) { cairo_clip_t *clip = extents->clip; cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (clip); cairo_status_t status; /* If the boxes are not pixel-aligned, we will need to compute a real mask */ if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (need_clip_mask && (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) return status; _cairo_xcb_surface_ensure_picture (dst); if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask && (op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID)) { const cairo_color_t *color; if (op == CAIRO_OPERATOR_CLEAR) color = CAIRO_COLOR_TRANSPARENT; else color = &((cairo_solid_pattern_t *) src)->color; status = _render_fill_boxes (dst, op, color, boxes); } else { cairo_surface_pattern_t mask; if (need_clip_mask) { cairo_xcb_surface_t *clip_surface; int clip_x = 0, clip_y = 0; clip_surface = get_clip_surface (extents->clip, dst, &clip_x, &clip_y); if (unlikely (clip_surface->base.status)) return clip_surface->base.status; _cairo_pattern_init_for_surface (&mask, &clip_surface->base); mask.base.filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&mask.base.matrix, -clip_x, -clip_y); cairo_surface_destroy (&clip_surface->base); if (op == CAIRO_OPERATOR_CLEAR) { src = NULL; op = CAIRO_OPERATOR_DEST_OUT; } } status = _render_composite_boxes (dst, op, src, need_clip_mask ? &mask.base : NULL, &extents->bounded, boxes); if (need_clip_mask) _cairo_pattern_fini (&mask.base); } if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { status = _cairo_xcb_surface_fixup_unbounded_boxes (dst, extents, clip, boxes); } _cairo_xcb_connection_release (dst->connection); return status; } static cairo_bool_t cairo_boxes_for_each_box (cairo_boxes_t *boxes, cairo_bool_t (*func) (cairo_box_t *box, void *data), void *data) { struct _cairo_boxes_chunk *chunk; int i; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) if (! func (&chunk->base[i], data)) return FALSE; } return TRUE; } struct _image_contains_box { int width, height; int tx, ty; }; static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure) { struct _image_contains_box *data = closure; /* The box is pixel-aligned so the truncation is safe. */ return _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 && _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 && _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width && _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height; } struct _image_upload_box { cairo_xcb_surface_t *surface; cairo_image_surface_t *image; xcb_gcontext_t gc; int tx, ty; }; static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) { const struct _image_upload_box *iub = closure; /* The box is pixel-aligned so the truncation is safe. */ int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format); int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); if (len == iub->image->stride) { _cairo_xcb_connection_put_image (iub->surface->connection, iub->surface->drawable, iub->gc, width, height, x, y, iub->image->depth, iub->image->stride, iub->image->data + (y + iub->ty) * iub->image->stride + (x + iub->tx) * bpp/8); } else { _cairo_xcb_connection_put_subimage (iub->surface->connection, iub->surface->drawable, iub->gc, x + iub->tx, y + iub->ty, width, height, bpp / 8, iub->image->stride, x, y, iub->image->depth, iub->image->data); } return TRUE; } static cairo_status_t _upload_image_inplace (cairo_xcb_surface_t *surface, const cairo_pattern_t *source, cairo_boxes_t *boxes) { const cairo_surface_pattern_t *pattern; struct _image_contains_box icb; struct _image_upload_box iub; cairo_image_surface_t *image; cairo_status_t status; int tx, ty; if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; pattern = (const cairo_surface_pattern_t *) source; if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) return CAIRO_INT_STATUS_UNSUPPORTED; /* Have we already upload this image to a pixmap? */ { cairo_xcb_picture_t *snapshot; snapshot = (cairo_xcb_picture_t *) _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend); if (snapshot != NULL) { if (snapshot->screen == surface->screen) return CAIRO_INT_STATUS_UNSUPPORTED; } } image = (cairo_image_surface_t *) pattern->surface; if (image->format == CAIRO_FORMAT_INVALID) return CAIRO_INT_STATUS_UNSUPPORTED; if (image->depth != surface->depth) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) return CAIRO_INT_STATUS_UNSUPPORTED; /* Check that the data is entirely within the image */ icb.width = image->width; icb.height = image->height; icb.tx = tx; icb.ty = ty; if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb)) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->deferred_clear) { status = _cairo_xcb_surface_clear (surface); if (unlikely (status)) return status; } status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; iub.surface = surface; iub.image = image; iub.gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, image->depth); iub.tx = tx; iub.ty = ty; cairo_boxes_for_each_box (boxes, image_upload_box, &iub); _cairo_xcb_screen_put_gc (surface->screen, image->depth, iub.gc); _cairo_xcb_connection_release (surface->connection); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t trim_extents_to_traps (cairo_composite_rectangles_t *extents, cairo_traps_t *traps) { cairo_box_t box; /* X trims the affected area to the extents of the trapezoids, so * we need to compensate when fixing up the unbounded area. */ _cairo_traps_extents (traps, &box); return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); } static cairo_bool_t _mono_edge_is_vertical (const cairo_line_t *line) { return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); } static cairo_bool_t _traps_are_pixel_aligned (cairo_traps_t *traps, cairo_antialias_t antialias) { int i; if (antialias == CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { if (! _mono_edge_is_vertical (&traps->traps[i].left) || ! _mono_edge_is_vertical (&traps->traps[i].right)) { traps->maybe_region = FALSE; return FALSE; } } } else { for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || ! _cairo_fixed_is_integer (traps->traps[i].top) || ! _cairo_fixed_is_integer (traps->traps[i].bottom) || ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) { traps->maybe_region = FALSE; return FALSE; } } } return TRUE; } static void _boxes_for_traps (cairo_boxes_t *boxes, cairo_traps_t *traps, cairo_antialias_t antialias) { int i; _cairo_boxes_init (boxes); boxes->num_boxes = traps->num_traps; boxes->chunks.base = (cairo_box_t *) traps->traps; boxes->chunks.count = traps->num_traps; boxes->chunks.size = traps->num_traps; if (antialias != CAIRO_ANTIALIAS_NONE) { for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; boxes->chunks.base[i].p1.x = x1; boxes->chunks.base[i].p1.y = y1; boxes->chunks.base[i].p2.x = x2; boxes->chunks.base[i].p2.y = y2; if (boxes->is_pixel_aligned) { boxes->is_pixel_aligned = _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); } } } else { boxes->is_pixel_aligned = TRUE; for (i = 0; i < traps->num_traps; i++) { /* Note the traps and boxes alias so we need to take the local copies first. */ cairo_fixed_t x1 = traps->traps[i].left.p1.x; cairo_fixed_t x2 = traps->traps[i].right.p1.x; cairo_fixed_t y1 = traps->traps[i].top; cairo_fixed_t y2 = traps->traps[i].bottom; /* round down here to match Pixman's behavior when using traps. */ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); } } } static cairo_status_t _composite_polygon (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_polygon_t *polygon, cairo_antialias_t antialias, cairo_fill_rule_t fill_rule, cairo_composite_rectangles_t *extents) { composite_traps_info_t traps; cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip); cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); cairo_status_t status; if (polygon->num_edges == 0) { status = CAIRO_STATUS_SUCCESS; if (! extents->is_bounded) { if (cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; if (clip_surface == FALSE) { if (clip_region != NULL) { status = _cairo_xcb_surface_set_clip_region (dst, clip_region); if (unlikely (status)) return status; } status = _cairo_xcb_surface_fixup_unbounded (dst, extents); if (clip_region != NULL) _cairo_xcb_surface_clear_clip_region (dst); } else { status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip); } } return status; } if (extents->clip->path != NULL && extents->is_bounded) { cairo_polygon_t clipper; cairo_fill_rule_t clipper_fill_rule; cairo_antialias_t clipper_antialias; status = _cairo_clip_get_polygon (extents->clip, &clipper, &clipper_fill_rule, &clipper_antialias); if (likely (status == CAIRO_STATUS_SUCCESS)) { if (clipper_antialias == antialias) { status = _cairo_polygon_intersect (polygon, fill_rule, &clipper, clipper_fill_rule); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip); _cairo_clip_destroy (extents->clip); extents->clip = clip; fill_rule = CAIRO_FILL_RULE_WINDING; } _cairo_polygon_fini (&clipper); } } } _cairo_traps_init (&traps.traps); status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule); if (unlikely (status)) goto CLEANUP_TRAPS; if (traps.traps.has_intersections) { if (traps.traps.is_rectangular) status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); else if (traps.traps.is_rectilinear) status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); else status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); if (unlikely (status)) goto CLEANUP_TRAPS; } /* Use a fast path if the trapezoids consist of a simple region, * but we can only do this if we do not have a clip surface, or can * substitute the mask with the clip. */ if (traps.traps.maybe_region && _traps_are_pixel_aligned (&traps.traps, antialias) && (! clip_surface || (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) { cairo_boxes_t boxes; _boxes_for_traps (&boxes, &traps.traps, antialias); status = _clip_and_composite_boxes (dst, op, source, &boxes, extents); } else { /* Otherwise render the trapezoids to a mask and composite in the usual * fashion. */ traps.antialias = antialias; status = trim_extents_to_traps (extents, &traps.traps); if (likely (status == CAIRO_STATUS_SUCCESS)) { unsigned int flags = 0; /* For unbounded operations, the X11 server will estimate the * affected rectangle and apply the operation to that. However, * there are cases where this is an overestimate (e.g. the * clip-fill-{eo,nz}-unbounded test). * * The clip will trim that overestimate to our expectations. */ if (! extents->is_bounded) flags |= FORCE_CLIP_REGION; status = _clip_and_composite (dst, op, source, _composite_traps, NULL, &traps, extents, need_unbounded_clip (extents) | flags); } } CLEANUP_TRAPS: _cairo_traps_fini (&traps.traps); return status; } static cairo_status_t _clip_and_composite_boxes (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, cairo_composite_rectangles_t *extents) { composite_traps_info_t info; cairo_int_status_t status; if (boxes->num_boxes == 0 && extents->is_bounded) return CAIRO_STATUS_SUCCESS; if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) && (op == CAIRO_OPERATOR_SOURCE || (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)))) { if (boxes->num_boxes == 1 && extents->bounded.width == dst->width && extents->bounded.height == dst->height) { op = CAIRO_OPERATOR_SOURCE; dst->deferred_clear = FALSE; } status = _upload_image_inplace (dst, src, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS && extents->clip->path != NULL && extents->is_bounded) { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; cairo_clip_t *clip; clip = _cairo_clip_copy (extents->clip); clip = _cairo_clip_intersect_boxes (clip, boxes); status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias); _cairo_clip_path_destroy (clip->path); clip->path = NULL; if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { cairo_clip_t *saved_clip = extents->clip; extents->clip = clip; status = _composite_polygon (dst, op, src, &polygon, antialias, fill_rule, extents); if (extents->clip != clip) clip = NULL; extents->clip = saved_clip; _cairo_polygon_fini (&polygon); } if (clip) _cairo_clip_destroy (clip); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) && op == CAIRO_OPERATOR_SOURCE) { status = _upload_image_inplace (dst, src, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) return _core_boxes (dst, op, src, boxes, extents); /* Use a fast path if the boxes are pixel aligned */ status = _composite_boxes (dst, op, src, boxes, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; /* Otherwise render via a mask and composite in the usual fashion. */ status = _cairo_traps_init_boxes (&info.traps, boxes); if (unlikely (status)) return status; info.antialias = CAIRO_ANTIALIAS_DEFAULT; status = trim_extents_to_traps (extents, &info.traps); if (status == CAIRO_INT_STATUS_SUCCESS) { status = _clip_and_composite (dst, op, src, _composite_traps, NULL, &info, extents, need_unbounded_clip (extents)); } _cairo_traps_fini (&info.traps); return status; } static cairo_int_status_t _composite_mask (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { const cairo_pattern_t *mask_pattern = closure; cairo_xcb_picture_t *src, *mask = NULL; cairo_status_t status; if (dst->base.is_clear) { if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) op = CAIRO_OPERATOR_SOURCE; } if (op == CAIRO_OPERATOR_SOURCE && clip == NULL) dst->deferred_clear = FALSE; if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } if (src_pattern != NULL) { src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); if (unlikely (src->base.status)) return src->base.status; mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); if (unlikely (mask->base.status)) { cairo_surface_destroy (&src->base); return mask->base.status; } _cairo_xcb_connection_render_composite (dst->connection, _render_operator (op), src->picture, mask->picture, dst->picture, extents->x + src->x, extents->y + src->y, extents->x + mask->x, extents->y + mask->y, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); cairo_surface_destroy (&mask->base); cairo_surface_destroy (&src->base); } else { src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); if (unlikely (src->base.status)) return src->base.status; _cairo_xcb_connection_render_composite (dst->connection, _render_operator (op), src->picture, XCB_NONE, dst->picture, extents->x + src->x, extents->y + src->y, 0, 0, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); cairo_surface_destroy (&src->base); } return CAIRO_STATUS_SUCCESS; } struct composite_box_info { cairo_xcb_surface_t *dst; cairo_xcb_picture_t *src; uint8_t op; }; static void composite_box(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_box_info *info = closure; if (coverage < 0xff00) { cairo_xcb_picture_t *mask; cairo_color_t color; color.red_short = color.green_short = color.blue_short = 0; color.alpha_short = coverage; mask = _solid_picture (info->dst, &color); if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { _cairo_xcb_connection_render_composite (info->dst->connection, info->op, info->src->picture, mask->picture, info->dst->picture, x + info->src->x, y + info->src->y, 0, 0, x, y, w, h); } cairo_surface_destroy (&mask->base); } else { _cairo_xcb_connection_render_composite (info->dst->connection, info->op, info->src->picture, XCB_NONE, info->dst->picture, x + info->src->x, y + info->src->y, 0, 0, x, y, w, h); } } static cairo_int_status_t _composite_mask_clip_boxes (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { struct composite_box_info info; cairo_status_t status; int i; assert (src_pattern == NULL); assert (op == CAIRO_OPERATOR_ADD); assert (dst->base.is_clear); if (clip->num_boxes > 1) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } info.op = XCB_RENDER_PICT_OP_SRC; info.dst = dst; info.src = _cairo_xcb_picture_for_pattern (dst, closure, extents); if (unlikely (info.src->base.status)) return info.src->base.status; info.src->x += dst_x; info.src->y += dst_y; for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); cairo_surface_destroy (&info.src->base); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _composite_mask_clip (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { const cairo_pattern_t *mask_pattern = closure; cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; composite_traps_info_t info; cairo_status_t status; assert (src_pattern == NULL); assert (op == CAIRO_OPERATOR_ADD); assert (dst->base.is_clear); status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &info.antialias); if (unlikely (status)) return status; _cairo_traps_init (&info.traps); status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps, &polygon, fill_rule); _cairo_polygon_fini (&polygon); if (unlikely (status)) return status; if (info.traps.has_intersections) { if (info.traps.is_rectangular) status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING); else if (info.traps.is_rectilinear) status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING); else status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING); if (unlikely (status)) { _cairo_traps_fini (&info.traps); return status; } } dst->deferred_clear = FALSE; /* assert(trap extents == extents); */ status = _composite_traps (&info, dst, CAIRO_OPERATOR_SOURCE, mask_pattern, dst_x, dst_y, extents, NULL); _cairo_traps_fini (&info.traps); return status; } struct composite_opacity_info { uint8_t op; cairo_xcb_surface_t *dst; cairo_xcb_picture_t *src; double opacity; }; static void composite_opacity(void *closure, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t coverage) { struct composite_opacity_info *info = closure; cairo_xcb_picture_t *mask; cairo_color_t color; color.red_short = color.green_short = color.blue_short = 0; color.alpha_short = info->opacity * coverage; mask = _solid_picture (info->dst, &color); if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { if (info->src) { _cairo_xcb_connection_render_composite (info->dst->connection, info->op, info->src->picture, mask->picture, info->dst->picture, x + info->src->x, y + info->src->y, 0, 0, x, y, w, h); } else { _cairo_xcb_connection_render_composite (info->dst->connection, info->op, mask->picture, XCB_NONE, info->dst->picture, 0, 0, 0, 0, x, y, w, h); } } cairo_surface_destroy (&mask->base); } static cairo_int_status_t _composite_opacity_boxes (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src_pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { const cairo_solid_pattern_t *mask_pattern = closure; struct composite_opacity_info info; cairo_status_t status; int i; if (dst->base.is_clear) { if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) op = CAIRO_OPERATOR_SOURCE; } if (op == CAIRO_OPERATOR_SOURCE && (clip == NULL || (clip->extents.width >= extents->width && clip->extents.height >= extents->height))) dst->deferred_clear = FALSE; if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } info.op = _render_operator (op); info.dst = dst; if (src_pattern != NULL) { info.src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); if (unlikely (info.src->base.status)) return info.src->base.status; } else info.src = NULL; info.opacity = mask_pattern->color.alpha; /* XXX for lots of boxes create a clip region for the fully opaque areas */ if (clip) { for (i = 0; i < clip->num_boxes; i++) do_unaligned_box(composite_opacity, &info, &clip->boxes[i], dst_x, dst_y); } else { composite_opacity(&info, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height, 0xffff); } cairo_surface_destroy (&info.src->base); return CAIRO_STATUS_SUCCESS; } /* high level rasteriser -> compositor */ cairo_int_status_t _cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; cairo_operator_t op = composite->op; cairo_pattern_t *source = &composite->source_pattern.base; cairo_boxes_t boxes; cairo_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) return CAIRO_INT_STATUS_UNSUPPORTED; if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (composite->clip == NULL && source->type == CAIRO_PATTERN_TYPE_SOLID && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR || (surface->base.is_clear && (op == CAIRO_OPERATOR_ADD || op == CAIRO_OPERATOR_OVER)))) { surface->deferred_clear = TRUE; surface->deferred_clear_color = composite->source_pattern.solid.color; return CAIRO_STATUS_SUCCESS; } _cairo_clip_steal_boxes(composite->clip, &boxes); status = _clip_and_composite_boxes (surface, op, source, &boxes, composite); _cairo_clip_unsteal_boxes (composite->clip, &boxes); return status; } cairo_int_status_t _cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; cairo_operator_t op = composite->op; cairo_pattern_t *source = &composite->source_pattern.base; cairo_pattern_t *mask = &composite->mask_pattern.base; cairo_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) return CAIRO_INT_STATUS_UNSUPPORTED; if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; if (mask->type == CAIRO_PATTERN_TYPE_SOLID && composite->clip->path == NULL && ! _cairo_clip_is_region (composite->clip)) { status = _clip_and_composite (surface, op, source, _composite_opacity_boxes, _composite_opacity_boxes, (void *) mask, composite, need_unbounded_clip (composite)); } else { xcb_draw_func_t mask_func = NULL; if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) mask_func = composite->clip->path ? _composite_mask_clip : _composite_mask_clip_boxes; status = _clip_and_composite (surface, op, source, _composite_mask, mask_func, (void *) mask, composite, need_bounded_clip (composite)); } return status; } static cairo_int_status_t _cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_composite_rectangles_t *extents) { cairo_polygon_t polygon; cairo_status_t status; _cairo_polygon_init_with_clip (&polygon, extents->clip); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _composite_polygon (dst, op, source, &polygon, antialias, CAIRO_FILL_RULE_WINDING, extents); } _cairo_polygon_fini (&polygon); return status; } static cairo_status_t _cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_composite_rectangles_t *extents) { cairo_surface_t *image; cairo_status_t status; cairo_clip_t *clip; int x, y; x = extents->bounded.x; y = extents->bounded.y; image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8, extents->bounded.width, extents->bounded.height); if (unlikely (image->status)) return image->status; clip = _cairo_clip_copy_region (extents->clip); status = _cairo_surface_offset_stroke (image, x, y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, clip); _cairo_clip_destroy (clip); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_surface_pattern_t mask; _cairo_pattern_init_for_surface (&mask, image); mask.base.filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&mask.base.matrix, -x, -y); status = _clip_and_composite (dst, op, source, _composite_mask, NULL, &mask.base, extents, need_bounded_clip (extents)); _cairo_pattern_fini (&mask.base); } cairo_surface_finish (image); cairo_surface_destroy (image); return status; } cairo_int_status_t _cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; cairo_operator_t op = composite->op; cairo_pattern_t *source = &composite->source_pattern.base; cairo_int_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) return CAIRO_INT_STATUS_UNSUPPORTED; if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, composite->clip); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (surface, op, source, &boxes, composite); } _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) { status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, composite); } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, composite); } else { ASSERT_NOT_REACHED; } } return status; } static cairo_status_t _cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t*source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_composite_rectangles_t *extents) { cairo_polygon_t polygon; cairo_status_t status; _cairo_polygon_init_with_clip (&polygon, extents->clip); status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _composite_polygon (dst, op, source, &polygon, antialias, fill_rule, extents); } _cairo_polygon_fini (&polygon); return status; } static cairo_status_t _cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_composite_rectangles_t *extents) { cairo_surface_t *image; cairo_status_t status; cairo_clip_t *clip; int x, y; x = extents->bounded.x; y = extents->bounded.y; image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8, extents->bounded.width, extents->bounded.height); if (unlikely (image->status)) return image->status; clip = _cairo_clip_copy_region (extents->clip); status = _cairo_surface_offset_fill (image, x, y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, path, fill_rule, tolerance, antialias, clip); _cairo_clip_destroy (clip); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_surface_pattern_t mask; _cairo_pattern_init_for_surface (&mask, image); mask.base.filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&mask.base.matrix, -x, -y); status = _clip_and_composite (dst, op, source, _composite_mask, NULL, &mask.base, extents, need_bounded_clip (extents)); _cairo_pattern_fini (&mask.base); } cairo_surface_finish (image); cairo_surface_destroy (image); return status; } cairo_int_status_t _cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; cairo_operator_t op = composite->op; cairo_pattern_t *source = &composite->source_pattern.base; cairo_int_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) return CAIRO_INT_STATUS_UNSUPPORTED; if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, composite->clip); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (surface, op, source, &boxes, composite); } _cairo_boxes_fini (&boxes); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) { status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path, fill_rule, tolerance, antialias, composite); } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path, fill_rule, tolerance, antialias, composite); } else { ASSERT_NOT_REACHED; } } return status; } static cairo_status_t _cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_composite_rectangles_t *extents) { cairo_surface_t *image; cairo_content_t content; cairo_status_t status; cairo_clip_t *clip; int x, y; content = CAIRO_CONTENT_ALPHA; if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) content = CAIRO_CONTENT_COLOR_ALPHA; x = extents->bounded.x; y = extents->bounded.y; image = _cairo_xcb_surface_create_similar_image (dst, _cairo_format_from_content (content), extents->bounded.width, extents->bounded.height); if (unlikely (image->status)) return image->status; clip = _cairo_clip_copy_region (extents->clip); status = _cairo_surface_offset_glyphs (image, x, y, CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, scaled_font, glyphs, num_glyphs, clip); _cairo_clip_destroy (clip); if (likely (status == CAIRO_STATUS_SUCCESS)) { cairo_surface_pattern_t mask; _cairo_pattern_init_for_surface (&mask, image); mask.base.filter = CAIRO_FILTER_NEAREST; if (content & CAIRO_CONTENT_COLOR) mask.base.has_component_alpha = TRUE; cairo_matrix_init_translate (&mask.base.matrix, -x, -y); status = _clip_and_composite (dst, op, source, _composite_mask, NULL, &mask.base, extents, need_bounded_clip (extents)); _cairo_pattern_fini (&mask.base); } cairo_surface_finish (image); cairo_surface_destroy (image); return status; } /* Build a struct of the same size of #cairo_glyph_t that can be used both as * an input glyph with double coordinates, and as "working" glyph with * integer from-current-point offsets. */ typedef union { cairo_glyph_t d; unsigned long index; struct { unsigned long index; int x; int y; } i; } cairo_xcb_glyph_t; /* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */ COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t)); typedef struct { cairo_scaled_font_t *font; cairo_xcb_glyph_t *glyphs; int num_glyphs; cairo_bool_t use_mask; } composite_glyphs_info_t; static cairo_status_t _can_composite_glyphs (cairo_xcb_surface_t *dst, cairo_rectangle_int_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int *num_glyphs) { #define GLYPH_CACHE_SIZE 64 cairo_box_t bbox_cache[GLYPH_CACHE_SIZE]; unsigned long glyph_cache[GLYPH_CACHE_SIZE]; #undef GLYPH_CACHE_SIZE cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_glyph_t *glyphs_end, *valid_glyphs; const int max_glyph_size = dst->connection->maximum_request_length - 64; /* We must initialize the cache with values that cannot match the * "hash" to guarantee that when compared for the first time they * will result in a mismatch. The hash function is simply modulus, * so we cannot use 0 in glyph_cache[0], but we can use it in all * other array cells. */ memset (glyph_cache, 0, sizeof (glyph_cache)); glyph_cache[0] = 1; /* Scan for oversized glyphs or glyphs outside the representable * range and fallback in that case, discard glyphs outside of the * image. */ valid_glyphs = glyphs; for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) { double x1, y1, x2, y2; cairo_scaled_glyph_t *glyph; cairo_box_t *bbox; int width, height, len; int g; g = glyphs->index % ARRAY_LENGTH (glyph_cache); if (glyph_cache[g] != glyphs->index) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs->index, CAIRO_SCALED_GLYPH_INFO_METRICS, &glyph); if (unlikely (status)) break; glyph_cache[g] = glyphs->index; bbox_cache[g] = glyph->bbox; } bbox = &bbox_cache[g]; /* Drop glyphs outside the clipping */ x1 = _cairo_fixed_to_double (bbox->p1.x); y1 = _cairo_fixed_to_double (bbox->p1.y); y2 = _cairo_fixed_to_double (bbox->p2.y); x2 = _cairo_fixed_to_double (bbox->p2.x); if (unlikely (glyphs->x + x2 <= extents->x || glyphs->y + y2 <= extents->y || glyphs->x + x1 >= extents->x + extents->width || glyphs->y + y1 >= extents->y + extents->height)) { (*num_glyphs)--; continue; } /* XRenderAddGlyph does not handle a glyph surface larger than * the extended maximum XRequest size. */ width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x); height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y); len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height; if (unlikely (len >= max_glyph_size)) { status = CAIRO_INT_STATUS_UNSUPPORTED; break; } /* The glyph coordinates must be representable in an int16_t. * When possible, they will be expressed as an offset from the * previous glyph, otherwise they will be an offset from the * operation extents or from the surface origin. If the last * two options are not valid, fallback. */ if (unlikely (glyphs->x > INT16_MAX || glyphs->y > INT16_MAX || glyphs->x - extents->x < INT16_MIN || glyphs->y - extents->y < INT16_MIN)) { status = CAIRO_INT_STATUS_UNSUPPORTED; break; } if (unlikely (valid_glyphs != glyphs)) *valid_glyphs = *glyphs; valid_glyphs++; } if (unlikely (valid_glyphs != glyphs)) { for (; glyphs != glyphs_end; glyphs++) { *valid_glyphs = *glyphs; valid_glyphs++; } } return status; } /* Start a new element for the first glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs * (Xrender limits each element to 252 glyphs, we limit them to 128) * * These same conditions need to be mirrored between * _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks */ #define _start_new_glyph_elt(count, glyph) \ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) /* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have * enough room for padding */ typedef struct { uint8_t len; uint8_t pad1; uint16_t pad2; int16_t deltax; int16_t deltay; } x_glyph_elt_t; #define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4) static void _cairo_xcb_font_destroy (cairo_xcb_font_t *font) { int i; for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xcb_font_glyphset_info_t *info; info = &font->glyphset_info[i]; free (info->pending_free_glyphs); } cairo_list_del (&font->base.link); cairo_list_del (&font->link); _cairo_xcb_connection_destroy (font->connection); free (font); } static void _cairo_xcb_font_fini (cairo_scaled_font_private_t *abstract_private, cairo_scaled_font_t *scaled_font) { cairo_xcb_font_t *font_private = (cairo_xcb_font_t *)abstract_private; cairo_xcb_connection_t *connection; cairo_bool_t have_connection; cairo_status_t status; int i; connection = font_private->connection; status = _cairo_xcb_connection_acquire (connection); have_connection = status == CAIRO_STATUS_SUCCESS; for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xcb_font_glyphset_info_t *info; info = &font_private->glyphset_info[i]; if (info->glyphset && status == CAIRO_STATUS_SUCCESS) { _cairo_xcb_connection_render_free_glyph_set (connection, info->glyphset); } } if (have_connection) _cairo_xcb_connection_release (connection); _cairo_xcb_font_destroy (font_private); } static cairo_xcb_font_t * _cairo_xcb_font_create (cairo_xcb_connection_t *connection, cairo_scaled_font_t *font) { cairo_xcb_font_t *priv; int i; priv = malloc (sizeof (cairo_xcb_font_t)); if (unlikely (priv == NULL)) return NULL; _cairo_scaled_font_attach_private (font, &priv->base, connection, _cairo_xcb_font_fini); priv->scaled_font = font; priv->connection = _cairo_xcb_connection_reference (connection); cairo_list_add (&priv->link, &connection->fonts); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xcb_font_glyphset_info_t *info = &priv->glyphset_info[i]; switch (i) { case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break; case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break; case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break; default: ASSERT_NOT_REACHED; break; } info->xrender_format = 0; info->glyphset = XCB_NONE; info->pending_free_glyphs = NULL; } return priv; } void _cairo_xcb_font_close (cairo_xcb_font_t *font) { cairo_scaled_font_t *scaled_font; scaled_font = font->scaled_font; //scaled_font->surface_private = NULL; _cairo_scaled_font_reset_cache (scaled_font); _cairo_xcb_font_destroy (font); } static void _cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection, cairo_xcb_font_glyphset_free_glyphs_t *to_free) { _cairo_xcb_connection_render_free_glyphs (connection, to_free->glyphset, to_free->glyph_count, to_free->glyph_indices); } static int _cairo_xcb_get_glyphset_index_for_format (cairo_format_t format) { if (format == CAIRO_FORMAT_A8) return GLYPHSET_INDEX_A8; if (format == CAIRO_FORMAT_A1) return GLYPHSET_INDEX_A1; assert (format == CAIRO_FORMAT_ARGB32); return GLYPHSET_INDEX_ARGB32; } static inline cairo_xcb_font_t * _cairo_xcb_font_get (const cairo_xcb_connection_t *c, cairo_scaled_font_t *font) { return (cairo_xcb_font_t *)_cairo_scaled_font_find_private (font, c); } static cairo_xcb_font_glyphset_info_t * _cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_xcb_connection_t *c, cairo_scaled_font_t *font, cairo_format_t format) { cairo_xcb_font_t *priv; cairo_xcb_font_glyphset_info_t *info; int glyphset_index; glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format); priv = _cairo_xcb_font_get (c, font); if (priv == NULL) { priv = _cairo_xcb_font_create (c, font); if (priv == NULL) return NULL; } info = &priv->glyphset_info[glyphset_index]; if (info->glyphset == XCB_NONE) { info->glyphset = _cairo_xcb_connection_get_xid (c); info->xrender_format = c->standard_formats[info->format]; _cairo_xcb_connection_render_create_glyph_set (c, info->glyphset, info->xrender_format); } return info; } static cairo_bool_t _cairo_xcb_glyphset_info_has_pending_free_glyph ( cairo_xcb_font_glyphset_info_t *info, unsigned long glyph_index) { if (info->pending_free_glyphs != NULL) { cairo_xcb_font_glyphset_free_glyphs_t *to_free; int i; to_free = info->pending_free_glyphs; for (i = 0; i < to_free->glyph_count; i++) { if (to_free->glyph_indices[i] == glyph_index) { to_free->glyph_count--; memmove (&to_free->glyph_indices[i], &to_free->glyph_indices[i+1], (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); return TRUE; } } } return FALSE; } typedef struct { cairo_scaled_glyph_private_t base; cairo_xcb_font_glyphset_info_t *glyphset; } cairo_xcb_glyph_private_t; static cairo_xcb_font_glyphset_info_t * _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (cairo_xcb_connection_t *c, cairo_scaled_font_t *font, unsigned long glyph_index, cairo_image_surface_t *surface) { cairo_xcb_font_t *priv; int i; priv = _cairo_xcb_font_get (c, font); if (priv == NULL) return NULL; if (surface != NULL) { i = _cairo_xcb_get_glyphset_index_for_format (surface->format); if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( &priv->glyphset_info[i], glyph_index)) { return &priv->glyphset_info[i]; } } else { for (i = 0; i < NUM_GLYPHSETS; i++) { if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( &priv->glyphset_info[i], glyph_index)) { return &priv->glyphset_info[i]; } } } return NULL; } static void _cairo_xcb_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, cairo_scaled_glyph_t *glyph, cairo_scaled_font_t *font) { cairo_xcb_glyph_private_t *priv = (cairo_xcb_glyph_private_t *)glyph_private; if (! font->finished) { cairo_xcb_font_glyphset_info_t *info = priv->glyphset; cairo_xcb_font_glyphset_free_glyphs_t *to_free; cairo_xcb_font_t *font_private; font_private = _cairo_xcb_font_get (glyph_private->key, font); assert (font_private); to_free = info->pending_free_glyphs; if (to_free != NULL && to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) { _cairo_xcb_render_free_glyphs (font_private->connection, to_free); to_free = info->pending_free_glyphs = NULL; } if (to_free == NULL) { to_free = malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t)); if (unlikely (to_free == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return; /* XXX cannot propagate failure */ } to_free->glyphset = info->glyphset; to_free->glyph_count = 0; info->pending_free_glyphs = to_free; } to_free->glyph_indices[to_free->glyph_count++] = _cairo_scaled_glyph_index (glyph); } cairo_list_del (&glyph_private->link); free (glyph_private); } static cairo_status_t _cairo_xcb_glyph_attach (cairo_xcb_connection_t *c, cairo_scaled_glyph_t *glyph, cairo_xcb_font_glyphset_info_t *info) { cairo_xcb_glyph_private_t *priv; priv = malloc (sizeof (*priv)); if (unlikely (priv == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_scaled_glyph_attach_private (glyph, &priv->base, c, _cairo_xcb_glyph_fini); priv->glyphset = info; glyph->dev_private = info; glyph->dev_private_key = c; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, cairo_scaled_font_t *font, cairo_scaled_glyph_t **scaled_glyph_out) { xcb_render_glyphinfo_t glyph_info; uint32_t glyph_index; uint8_t *data; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out; cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_bool_t already_had_glyph_surface; cairo_xcb_font_glyphset_info_t *info; glyph_index = _cairo_scaled_glyph_index (scaled_glyph); /* check to see if we have a pending XRenderFreeGlyph for this glyph */ info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (connection, font, glyph_index, glyph_surface); if (info != NULL) return _cairo_xcb_glyph_attach (connection, scaled_glyph, info); if (glyph_surface == NULL) { status = _cairo_scaled_glyph_lookup (font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, scaled_glyph_out); if (unlikely (status)) return status; scaled_glyph = *scaled_glyph_out; glyph_surface = scaled_glyph->surface; already_had_glyph_surface = FALSE; } else { already_had_glyph_surface = TRUE; } info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (connection, font, glyph_surface->format); if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } #if 0 /* If the glyph surface has zero height or width, we create * a clear 1x1 surface, to avoid various X server bugs. */ if (glyph_surface->width == 0 || glyph_surface->height == 0) { cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (info->format, 1, 1); status = tmp_surface->status; if (unlikely (status)) goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; glyph_surface = (cairo_image_surface_t *) tmp_surface; } #endif /* If the glyph format does not match the font format, then we * create a temporary surface for the glyph image with the font's * format. */ if (glyph_surface->format != info->format) { glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface, info->format); status = glyph_surface->base.status; if (unlikely (status)) goto BAIL; } /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); glyph_info.width = glyph_surface->width; glyph_info.height = glyph_surface->height; glyph_info.x_off = scaled_glyph->x_advance; glyph_info.y_off = scaled_glyph->y_advance; data = glyph_surface->data; /* flip formats around */ switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) { case GLYPHSET_INDEX_A1: /* local bitmaps are always stored with bit == byte */ if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { int c = glyph_surface->stride * glyph_surface->height; const uint8_t *d; uint8_t *new, *n; new = malloc (c); if (unlikely (new == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; d = data; do { uint8_t b = *d++; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *n++ = b; } while (--c); data = new; } break; case GLYPHSET_INDEX_A8: break; case GLYPHSET_INDEX_ARGB32: if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { unsigned int c = glyph_surface->stride * glyph_surface->height / 4; const uint32_t *d; uint32_t *new, *n; new = malloc (4 * c); if (unlikely (new == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; d = (uint32_t *) data; do { *n++ = bswap_32 (*d); d++; } while (--c); data = (uint8_t *) new; } break; default: ASSERT_NOT_REACHED; break; } /* XXX assume X server wants pixman padding. Xft assumes this as well */ _cairo_xcb_connection_render_add_glyphs (connection, info->glyphset, 1, &glyph_index, &glyph_info, glyph_surface->stride * glyph_surface->height, data); if (data != glyph_surface->data) free (data); status = _cairo_xcb_glyph_attach (connection, scaled_glyph, info); BAIL: if (glyph_surface != scaled_glyph->surface) cairo_surface_destroy (&glyph_surface->base); /* If the scaled glyph didn't already have a surface attached * to it, release the created surface now that we have it * uploaded to the X server. If the surface has already been * there (e.g. because image backend requested it), leave it in * the cache */ if (! already_had_glyph_surface) _cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL); return status; } typedef void (*cairo_xcb_render_composite_text_func_t) (cairo_xcb_connection_t *connection, uint8_t op, xcb_render_picture_t src, xcb_render_picture_t dst, xcb_render_pictformat_t mask_format, xcb_render_glyphset_t glyphset, int16_t src_x, int16_t src_y, uint32_t len, uint8_t *cmd); static cairo_status_t _emit_glyphs_chunk (cairo_xcb_surface_t *dst, cairo_operator_t op, cairo_xcb_picture_t *src, /* info for this chunk */ cairo_xcb_glyph_t *glyphs, int num_glyphs, int width, int estimated_req_size, cairo_xcb_font_glyphset_info_t *info, xcb_render_pictformat_t mask_format) { cairo_xcb_render_composite_text_func_t composite_text_func; uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE]; uint8_t *buf = stack_buf; x_glyph_elt_t *elt = NULL; /* silence compiler */ uint32_t len; int i; if (estimated_req_size > ARRAY_LENGTH (stack_buf)) { buf = malloc (estimated_req_size); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } len = 0; for (i = 0; i < num_glyphs; i++) { if (_start_new_glyph_elt (i, &glyphs[i])) { if (len & 3) len += 4 - (len & 3); elt = (x_glyph_elt_t *) (buf + len); elt->len = 0; elt->deltax = glyphs[i].i.x; elt->deltay = glyphs[i].i.y; len += sizeof (x_glyph_elt_t); } switch (width) { case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break; case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break; default: case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break; } len += width; elt->len++; } if (len & 3) len += 4 - (len & 3); switch (width) { case 1: composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8; break; case 2: composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16; break; default: case 4: composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32; break; } composite_text_func (dst->connection, _render_operator (op), src->picture, dst->picture, mask_format, info->glyphset, src->x + glyphs[0].i.x, src->y + glyphs[0].i.y, len, buf); if (buf != stack_buf) free (buf); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _composite_glyphs (void *closure, cairo_xcb_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_clip_t *clip) { composite_glyphs_info_t *info = closure; cairo_scaled_glyph_t *glyph_cache[64]; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_fixed_t x = 0, y = 0; cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; const unsigned int max_request_size = dst->connection->maximum_request_length - 64; cairo_xcb_picture_t *src; unsigned long max_index = 0; int width = 1; unsigned int request_size = 0; int i; if (dst->deferred_clear) { status = _cairo_xcb_surface_clear (dst); if (unlikely (status)) return status; } src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); if (unlikely (src->base.status)) return src->base.status; memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *glyph; unsigned long glyph_index = info->glyphs[i].index; int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); int old_width = width; int this_x, this_y; glyph = glyph_cache[cache_index]; if (glyph == NULL || _cairo_scaled_glyph_index (glyph) != glyph_index) { status = _cairo_scaled_glyph_lookup (info->font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &glyph); if (unlikely (status)) { cairo_surface_destroy (&src->base); return status; } /* Send unseen glyphs to the server */ if (glyph->dev_private_key != dst->connection) { status = _cairo_xcb_surface_add_glyph (dst->connection, info->font, &glyph); if (unlikely (status)) { cairo_surface_destroy (&src->base); return status; } } glyph_cache[cache_index] = glyph; } this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x; this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y; this_glyphset_info = glyph->dev_private; if (glyphset_info == NULL) glyphset_info = this_glyphset_info; /* Update max glyph index */ if (glyph_index > max_index) { max_index = glyph_index; if (max_index >= 65536) width = 4; else if (max_index >= 256) width = 2; if (width != old_width) request_size += (width - old_width) * i; } /* If we will pass the max request size by adding this glyph, * flush current glyphs. Note that we account for a * possible element being added below. * * Also flush if changing glyphsets, as Xrender limits one mask * format per request, so we can either break up, or use a * wide-enough mask format. We do the former. One reason to * prefer the latter is the fact that Xserver ADDs all glyphs * to the mask first, and then composes that to final surface, * though it's not a big deal. * * If the glyph has a coordinate which cannot be represented * as a 16-bit offset from the previous glyph, flush the * current chunk. The current glyph will be the first one in * the next chunk, thus its coordinates will be an offset from * the destination origin. This offset is guaranteed to be * representable as 16-bit offset in _can_composite_glyphs(). */ if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t || this_x - x > INT16_MAX || this_x - x < INT16_MIN || this_y - y > INT16_MAX || this_y - y < INT16_MIN || this_glyphset_info != glyphset_info) { status = _emit_glyphs_chunk (dst, op, src, info->glyphs, i, old_width, request_size, glyphset_info, info->use_mask ? glyphset_info->xrender_format : 0); if (unlikely (status)) { cairo_surface_destroy (&src->base); return status; } info->glyphs += i; info->num_glyphs -= i; i = 0; max_index = info->glyphs[0].index; width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; request_size = 0; x = y = 0; glyphset_info = this_glyphset_info; } /* Convert absolute glyph position to relative-to-current-point * position */ info->glyphs[i].i.x = this_x - x; info->glyphs[i].i.y = this_y - y; /* Start a new element for the first glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs. * * These same conditions are mirrored in _emit_glyphs_chunk(). */ if (_start_new_glyph_elt (i, &info->glyphs[i])) request_size += _cairo_sz_x_glyph_elt_t; /* adjust current-position */ x = this_x + glyph->x_advance; y = this_y + glyph->y_advance; request_size += width; } if (i) { status = _emit_glyphs_chunk (dst, op, src, info->glyphs, i, width, request_size, glyphset_info, info->use_mask ? glyphset_info->xrender_format : 0); } cairo_surface_destroy (&src->base); return status; } cairo_int_status_t _cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; cairo_operator_t op = composite->op; cairo_pattern_t *source = &composite->source_pattern.base; cairo_int_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) return CAIRO_INT_STATUS_UNSUPPORTED; if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) { _cairo_scaled_font_freeze_cache (scaled_font); status = _can_composite_glyphs (surface, &composite->bounded, scaled_font, glyphs, &num_glyphs); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { composite_glyphs_info_t info; unsigned flags = 0; info.font = scaled_font; info.glyphs = (cairo_xcb_glyph_t *) glyphs; info.num_glyphs = num_glyphs; info.use_mask = overlap || ! composite->is_bounded || ! _cairo_clip_is_region(composite->clip); if (composite->mask.width > composite->unbounded.width || composite->mask.height > composite->unbounded.height) { /* Glyphs are tricky since we do not directly control the * geometry and their inked extents depend on the * individual glyph-surface size. We must set a clip region * so that the X server can trim the glyphs appropriately. */ flags |= FORCE_CLIP_REGION; } status = _clip_and_composite (surface, op, source, _composite_glyphs, NULL, &info, composite, need_bounded_clip (composite) | flags); } _cairo_scaled_font_thaw_cache (scaled_font); } if (status == CAIRO_INT_STATUS_UNSUPPORTED) { assert (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE); status = _cairo_xcb_surface_render_glyphs_via_mask (surface, op, source, scaled_font, glyphs, num_glyphs, composite); } return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb-surface.c000066400000000000000000001332751271037650300257540ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Behdad Esfahbod * Carl D. Worth * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #include "cairo-xcb.h" #include "cairo-xcb-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-inline.h" #include "cairo-list-inline.h" #include "cairo-surface-backend-private.h" #include "cairo-compositor-private.h" #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_proto (cairo_xcb_surface_create); slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); #endif /** * SECTION:cairo-xcb * @Title: XCB Surfaces * @Short_Description: X Window System rendering using the XCB library * @See_Also: #cairo_surface_t * * The XCB surface is used to render cairo graphics to X Window System * windows and pixmaps using the XCB library. * * Note that the XCB surface automatically takes advantage of the X render * extension if it is available. **/ /** * CAIRO_HAS_XCB_SURFACE: * * Defined if the xcb surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.12 **/ cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_xcb_surface_t *other = abstract_other; cairo_xcb_surface_t *surface; cairo_xcb_connection_t *connection; xcb_pixmap_t pixmap; cairo_status_t status; if (unlikely(width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0)) return cairo_image_surface_create (_cairo_format_from_content (content), width, height); if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0) return _cairo_xcb_surface_create_similar_image (other, _cairo_format_from_content (content), width, height); connection = other->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (content == other->base.content) { pixmap = _cairo_xcb_connection_create_pixmap (connection, other->depth, other->drawable, width, height); surface = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_internal (other->screen, pixmap, TRUE, other->pixman_format, other->xrender_format, width, height); } else { cairo_format_t format; pixman_format_code_t pixman_format; /* XXX find a compatible xrender format */ switch (content) { case CAIRO_CONTENT_ALPHA: pixman_format = PIXMAN_a8; format = CAIRO_FORMAT_A8; break; case CAIRO_CONTENT_COLOR: pixman_format = PIXMAN_x8r8g8b8; format = CAIRO_FORMAT_RGB24; break; default: ASSERT_NOT_REACHED; case CAIRO_CONTENT_COLOR_ALPHA: pixman_format = PIXMAN_a8r8g8b8; format = CAIRO_FORMAT_ARGB32; break; } pixmap = _cairo_xcb_connection_create_pixmap (connection, PIXMAN_FORMAT_DEPTH (pixman_format), other->drawable, width, height); surface = (cairo_xcb_surface_t *) _cairo_xcb_surface_create_internal (other->screen, pixmap, TRUE, pixman_format, connection->standard_formats[format], width, height); } if (unlikely (surface->base.status)) _cairo_xcb_connection_free_pixmap (connection, pixmap); _cairo_xcb_connection_release (connection); return &surface->base; } cairo_surface_t * _cairo_xcb_surface_create_similar_image (void *abstract_other, cairo_format_t format, int width, int height) { cairo_xcb_surface_t *other = abstract_other; cairo_xcb_connection_t *connection = other->connection; cairo_xcb_shm_info_t *shm_info; cairo_image_surface_t *image; cairo_status_t status; pixman_format_code_t pixman_format; if (unlikely(width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0)) return NULL; pixman_format = _cairo_format_to_pixman_format_code (format); status = _cairo_xcb_shm_image_create (connection, pixman_format, width, height, &image, &shm_info); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (! image->base.is_clear) { memset (image->data, 0, image->stride * image->height); image->base.is_clear = TRUE; } return &image->base; } static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; if (surface->fallback != NULL) { cairo_surface_finish (&surface->fallback->base); cairo_surface_destroy (&surface->fallback->base); } _cairo_boxes_fini (&surface->fallback_damage); cairo_list_del (&surface->link); status = _cairo_xcb_connection_acquire (surface->connection); if (status == CAIRO_STATUS_SUCCESS) { if (surface->picture != XCB_NONE) { _cairo_xcb_connection_render_free_picture (surface->connection, surface->picture); } if (surface->owns_pixmap) _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); _cairo_xcb_connection_release (surface->connection); } _cairo_xcb_connection_destroy (surface->connection); return status; } static void _destroy_image (pixman_image_t *image, void *data) { free (data); } #if CAIRO_HAS_XCB_SHM_FUNCTIONS static cairo_surface_t * _cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection, pixman_format_code_t pixman_format, int width, int height, cairo_bool_t might_reuse, cairo_xcb_shm_info_t **shm_info_out) { cairo_surface_t *image; cairo_xcb_shm_info_t *shm_info; cairo_int_status_t status; size_t stride; *shm_info_out = NULL; stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); status = _cairo_xcb_connection_allocate_shm_info (connection, stride * height, might_reuse, &shm_info); if (unlikely (status)) { if (status == CAIRO_INT_STATUS_UNSUPPORTED) return NULL; return _cairo_surface_create_in_error (status); } image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, pixman_format, width, height, stride); if (unlikely (image->status)) { _cairo_xcb_shm_info_destroy (shm_info); return image; } status = _cairo_user_data_array_set_data (&image->user_data, (const cairo_user_data_key_t *) connection, shm_info, (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); if (unlikely (status)) { cairo_surface_destroy (image); _cairo_xcb_shm_info_destroy (shm_info); return _cairo_surface_create_in_error (status); } *shm_info_out = shm_info; return image; } #endif static cairo_surface_t * _get_shm_image (cairo_xcb_surface_t *surface, int x, int y, int width, int height) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; cairo_surface_t *image; cairo_status_t status; if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0) return NULL; image = _cairo_xcb_surface_create_shm_image (surface->connection, surface->pixman_format, width, height, TRUE, &shm_info); if (unlikely (image == NULL || image->status)) goto done; status = _cairo_xcb_connection_shm_get_image (surface->connection, surface->drawable, x, y, width, height, shm_info->shm, shm_info->offset); if (unlikely (status)) { cairo_surface_destroy (image); image = _cairo_surface_create_in_error (status); } done: return image; #else return NULL; #endif } static cairo_surface_t * _get_image (cairo_xcb_surface_t *surface, cairo_bool_t use_shm, int x, int y, int width, int height) { cairo_surface_t *image; cairo_xcb_connection_t *connection; xcb_get_image_reply_t *reply; cairo_int_status_t status; assert (surface->fallback == NULL); assert (x >= 0); assert (y >= 0); assert (x + width <= surface->width); assert (y + height <= surface->height); if (surface->deferred_clear) { image = _cairo_image_surface_create_with_pixman_format (NULL, surface->pixman_format, width, height, 0); if (surface->deferred_clear_color.alpha_short > 0x00ff) { cairo_solid_pattern_t solid; _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color); status = _cairo_surface_paint (image, CAIRO_OPERATOR_SOURCE, &solid.base, NULL); if (unlikely (status)) { cairo_surface_destroy (image); image = _cairo_surface_create_in_error (status); } } return image; } connection = surface->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (use_shm) { image = _get_shm_image (surface, x, y, width, height); if (image) { if (image->status == CAIRO_STATUS_SUCCESS) { _cairo_xcb_connection_release (connection); return image; } cairo_surface_destroy (image); } } status = _cairo_xcb_connection_get_image (connection, surface->drawable, x, y, width, height, &reply); if (unlikely (status)) goto FAIL; if (reply == NULL && ! surface->owns_pixmap) { /* xcb_get_image_t from a window is dangerous because it can * produce errors if the window is unmapped or partially * outside the screen. We could check for errors and * retry, but to keep things simple, we just create a * temporary pixmap * * If we hit this fallback too often, we should remember so and * skip the round-trip from the above GetImage request, * similar to what cairo-xlib does. */ xcb_pixmap_t pixmap; xcb_gcontext_t gc; gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); pixmap = _cairo_xcb_connection_create_pixmap (connection, surface->depth, surface->drawable, width, height); /* XXX IncludeInferiors? */ _cairo_xcb_connection_copy_area (connection, surface->drawable, pixmap, gc, x, y, 0, 0, width, height); _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); status = _cairo_xcb_connection_get_image (connection, pixmap, 0, 0, width, height, &reply); _cairo_xcb_connection_free_pixmap (connection, pixmap); if (unlikely (status)) goto FAIL; } if (unlikely (reply == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; } /* XXX byte swap */ /* XXX format conversion */ assert (reply->depth == surface->depth); image = _cairo_image_surface_create_with_pixman_format (xcb_get_image_data (reply), surface->pixman_format, width, height, CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (surface->pixman_format))); status = image->status; if (unlikely (status)) { free (reply); goto FAIL; } /* XXX */ pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply); _cairo_xcb_connection_release (connection); return image; FAIL: _cairo_xcb_connection_release (connection); return _cairo_surface_create_in_error (status); } static cairo_surface_t * _cairo_xcb_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } static cairo_status_t _cairo_xcb_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xcb_surface_t *surface = abstract_surface; cairo_surface_t *image; if (surface->fallback != NULL) { image = cairo_surface_reference (&surface->fallback->base); goto DONE; } image = _cairo_surface_has_snapshot (&surface->base, &_cairo_image_surface_backend); if (image != NULL) { image = cairo_surface_reference (image); goto DONE; } image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height); if (unlikely (image->status)) return image->status; _cairo_surface_attach_snapshot (&surface->base, image, NULL); DONE: *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } cairo_bool_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; return TRUE; } static void _cairo_xcb_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { /* XXX copy from xlib */ _cairo_font_options_init_default (options); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } static cairo_status_t _put_shm_image (cairo_xcb_surface_t *surface, xcb_gcontext_t gc, cairo_image_surface_t *image) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; shm_info = _cairo_user_data_array_get_data (&image->base.user_data, (const cairo_user_data_key_t *) surface->connection); if (shm_info == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_xcb_connection_shm_put_image (surface->connection, surface->drawable, gc, surface->width, surface->height, 0, 0, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0, image->depth, shm_info->shm, shm_info->offset); return CAIRO_STATUS_SUCCESS; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } static cairo_status_t _put_image (cairo_xcb_surface_t *surface, cairo_image_surface_t *image) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; /* XXX track damaged region? */ status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; if (image->pixman_format == surface->pixman_format) { xcb_gcontext_t gc; assert (image->depth == surface->depth); assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); status = _put_shm_image (surface, gc, image); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { _cairo_xcb_connection_put_image (surface->connection, surface->drawable, gc, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0, image->depth, image->stride, image->data); status = CAIRO_STATUS_SUCCESS; } _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); } else { ASSERT_NOT_REACHED; } _cairo_xcb_connection_release (surface->connection); return status; } static cairo_int_status_t _put_shm_image_boxes (cairo_xcb_surface_t *surface, cairo_image_surface_t *image, xcb_gcontext_t gc, cairo_boxes_t *boxes) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS cairo_xcb_shm_info_t *shm_info; shm_info = _cairo_user_data_array_get_data (&image->base.user_data, (const cairo_user_data_key_t *) surface->connection); if (shm_info != NULL) { struct _cairo_boxes_chunk *chunk; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { int i; for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); _cairo_xcb_connection_shm_put_image (surface->connection, surface->drawable, gc, surface->width, surface->height, x, y, width, height, x, y, image->depth, shm_info->shm, shm_info->offset); } } } return CAIRO_INT_STATUS_SUCCESS; #endif return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t _put_image_boxes (cairo_xcb_surface_t *surface, cairo_image_surface_t *image, cairo_boxes_t *boxes) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; xcb_gcontext_t gc; if (boxes->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* XXX track damaged region? */ status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return status; assert (image->pixman_format == surface->pixman_format); assert (image->depth == surface->depth); assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, surface->depth); status = _put_shm_image_boxes (surface, image, gc, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { struct _cairo_boxes_chunk *chunk; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { int i; for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x = _cairo_fixed_integer_part (b->p1.x); int y = _cairo_fixed_integer_part (b->p1.y); int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); _cairo_xcb_connection_put_image (surface->connection, surface->drawable, gc, width, height, x, y, image->depth, image->stride, image->data + x * PIXMAN_FORMAT_BPP (image->pixman_format) / 8 + y * image->stride); } } status = CAIRO_STATUS_SUCCESS; } _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); _cairo_xcb_connection_release (surface->connection); return status; } static cairo_status_t _cairo_xcb_surface_flush (void *abstract_surface, unsigned flags) { cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (likely (surface->fallback == NULL)) { status = CAIRO_STATUS_SUCCESS; if (! surface->base.finished && surface->deferred_clear) status = _cairo_xcb_surface_clear (surface); return status; } status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS && (! surface->base._finishing || ! surface->owns_pixmap)) { status = cairo_surface_status (&surface->fallback->base); if (status == CAIRO_STATUS_SUCCESS) status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage, CAIRO_FILL_RULE_WINDING, &surface->fallback_damage); if (status == CAIRO_STATUS_SUCCESS) status = _put_image_boxes (surface, surface->fallback, &surface->fallback_damage); if (status == CAIRO_STATUS_SUCCESS && ! surface->base._finishing) { _cairo_surface_attach_snapshot (&surface->base, &surface->fallback->base, cairo_surface_finish); } } _cairo_boxes_clear (&surface->fallback_damage); cairo_surface_destroy (&surface->fallback->base); surface->fallback = NULL; return status; } static cairo_image_surface_t * _cairo_xcb_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_status_t status; if (surface->fallback) return _cairo_surface_map_to_image (&surface->fallback->base, extents); image = _get_image (surface, TRUE, extents->x, extents->y, extents->width, extents->height); status = cairo_surface_status (image); if (unlikely (status)) { cairo_surface_destroy(image); return _cairo_image_surface_create_in_error (status); } /* Do we have a deferred clear and this image surface does NOT cover the * whole xcb surface? Have to apply the clear in that case, else * uploading the image will handle the problem for us. */ if (surface->deferred_clear && ! (extents->width == surface->width && extents->height == surface->height)) { status = _cairo_xcb_surface_clear (surface); if (unlikely (status)) { cairo_surface_destroy(image); return _cairo_image_surface_create_in_error (status); } } surface->deferred_clear = FALSE; cairo_surface_set_device_offset (image, -extents->x, -extents->y); return (cairo_image_surface_t *) image; } static cairo_int_status_t _cairo_xcb_surface_unmap (void *abstract_surface, cairo_image_surface_t *image) { cairo_xcb_surface_t *surface = abstract_surface; cairo_int_status_t status; if (surface->fallback) return _cairo_surface_unmap_image (&surface->fallback->base, image); status = _put_image (abstract_surface, image); cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return status; } static cairo_surface_t * _cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface, cairo_composite_rectangles_t *composite) { cairo_image_surface_t *image; cairo_status_t status; status = _cairo_composite_rectangles_add_to_damage (composite, &surface->fallback_damage); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (surface->fallback) return &surface->fallback->base; image = (cairo_image_surface_t *) _get_image (surface, TRUE, 0, 0, surface->width, surface->height); /* If there was a deferred clear, _get_image applied it */ if (image->base.status == CAIRO_STATUS_SUCCESS) { surface->deferred_clear = FALSE; surface->fallback = image; } return &surface->fallback->base; } static cairo_int_status_t _cairo_xcb_fallback_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_paint (fallback, extents->op, &extents->source_pattern.base, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_mask (fallback, extents->op, &extents->source_pattern.base, &extents->mask_pattern.base, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_stroke (fallback, extents->op, &extents->source_pattern.base, path, style, ctm, ctm_inverse, tolerance, antialias, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_fill (fallback, extents->op, &extents->source_pattern.base, path, fill_rule, tolerance, antialias, extents->clip); } static cairo_int_status_t _cairo_xcb_fallback_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); return _cairo_surface_show_text_glyphs (fallback, extents->op, &extents->source_pattern.base, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, extents->clip); } static const cairo_compositor_t _cairo_xcb_fallback_compositor = { &__cairo_no_compositor, _cairo_xcb_fallback_compositor_paint, _cairo_xcb_fallback_compositor_mask, _cairo_xcb_fallback_compositor_stroke, _cairo_xcb_fallback_compositor_fill, _cairo_xcb_fallback_compositor_glyphs, }; static const cairo_compositor_t _cairo_xcb_render_compositor = { &_cairo_xcb_fallback_compositor, _cairo_xcb_render_compositor_paint, _cairo_xcb_render_compositor_mask, _cairo_xcb_render_compositor_stroke, _cairo_xcb_render_compositor_fill, _cairo_xcb_render_compositor_glyphs, }; static inline const cairo_compositor_t * get_compositor (cairo_surface_t **s) { cairo_xcb_surface_t *surface = (cairo_xcb_surface_t * )*s; if (surface->fallback) { *s = &surface->fallback->base; return ((cairo_image_surface_t *) *s)->compositor; } return &_cairo_xcb_render_compositor; } static cairo_int_status_t _cairo_xcb_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); return _cairo_compositor_paint (compositor, surface, op, source, clip); } static cairo_int_status_t _cairo_xcb_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); return _cairo_compositor_mask (compositor, surface, op, source, mask, clip); } static cairo_int_status_t _cairo_xcb_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); return _cairo_compositor_stroke (compositor, surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xcb_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); return _cairo_compositor_fill (compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xcb_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_surface_t *surface = abstract_surface; const cairo_compositor_t *compositor = get_compositor (&surface); return _cairo_compositor_glyphs (compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); } const cairo_surface_backend_t _cairo_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XCB, _cairo_xcb_surface_finish, _cairo_default_context_create, _cairo_xcb_surface_create_similar, _cairo_xcb_surface_create_similar_image, _cairo_xcb_surface_map_to_image, _cairo_xcb_surface_unmap, _cairo_xcb_surface_source, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xcb_surface_get_extents, _cairo_xcb_surface_get_font_options, _cairo_xcb_surface_flush, NULL, _cairo_xcb_surface_paint, _cairo_xcb_surface_mask, _cairo_xcb_surface_stroke, _cairo_xcb_surface_fill, NULL, /* fill-stroke */ _cairo_xcb_surface_glyphs, }; cairo_surface_t * _cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, cairo_bool_t owns_pixmap, pixman_format_code_t pixman_format, xcb_render_pictformat_t xrender_format, int width, int height) { cairo_xcb_surface_t *surface; surface = malloc (sizeof (cairo_xcb_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_surface_backend, &screen->connection->device, _cairo_content_from_pixman_format (pixman_format)); surface->connection = _cairo_xcb_connection_reference (screen->connection); surface->screen = screen; cairo_list_add (&surface->link, &screen->surfaces); surface->drawable = drawable; surface->owns_pixmap = owns_pixmap; surface->deferred_clear = FALSE; surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT; surface->width = width; surface->height = height; surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); surface->picture = XCB_NONE; if (screen->connection->force_precision != -1) surface->precision = screen->connection->force_precision; else surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE; surface->pixman_format = pixman_format; surface->xrender_format = xrender_format; surface->fallback = NULL; _cairo_boxes_init (&surface->fallback_damage); return &surface->base; } static xcb_screen_t * _cairo_xcb_screen_from_visual (xcb_connection_t *connection, xcb_visualtype_t *visual, int *depth) { xcb_depth_iterator_t d; xcb_screen_iterator_t s; s = xcb_setup_roots_iterator (xcb_get_setup (connection)); for (; s.rem; xcb_screen_next (&s)) { if (s.data->root_visual == visual->visual_id) { *depth = s.data->root_depth; return s.data; } d = xcb_screen_allowed_depths_iterator(s.data); for (; d.rem; xcb_depth_next (&d)) { xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); for (; v.rem; xcb_visualtype_next (&v)) { if (v.data->visual_id == visual->visual_id) { *depth = d.data->depth; return s.data; } } } } return NULL; } /** * cairo_xcb_surface_create: * @connection: an XCB connection * @drawable: an XCB drawable * @visual: the visual to use for drawing to @drawable. The depth * of the visual must match the depth of the drawable. * Currently, only TrueColor visuals are fully supported. * @width: the current width of @drawable * @height: the current height of @drawable * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided visual. * * Note: If @drawable is a Window, then the function * cairo_xcb_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create (xcb_connection_t *connection, xcb_drawable_t drawable, xcb_visualtype_t *visual, int width, int height) { cairo_xcb_screen_t *screen; xcb_screen_t *xcb_screen; cairo_format_masks_t image_masks; pixman_format_code_t pixman_format; xcb_render_pictformat_t xrender_format; int depth; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); xcb_screen = _cairo_xcb_screen_from_visual (connection, visual, &depth); if (unlikely (xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); image_masks.alpha_mask = 0; image_masks.red_mask = visual->red_mask; image_masks.green_mask = visual->green_mask; image_masks.blue_mask = visual->blue_mask; if (depth == 32) /* XXX visuals have no alpha! */ image_masks.alpha_mask = 0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask); if (depth > 16) image_masks.bpp = 32; else if (depth > 8) image_masks.bpp = 16; else if (depth > 1) image_masks.bpp = 8; else image_masks.bpp = 1; if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); screen = _cairo_xcb_screen_get (connection, xcb_screen); if (unlikely (screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); xrender_format = _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection, visual->visual_id); return _cairo_xcb_surface_create_internal (screen, drawable, FALSE, pixman_format, xrender_format, width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create); #endif /** * cairo_xcb_surface_create_for_bitmap: * @connection: an XCB connection * @screen: the XCB screen associated with @bitmap * @bitmap: an XCB drawable (a Pixmap with depth 1) * @width: the current width of @bitmap * @height: the current height of @bitmap * * Creates an XCB surface that draws to the given bitmap. * This will be drawn to as a %CAIRO_FORMAT_A1 object. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, xcb_screen_t *screen, xcb_pixmap_t bitmap, int width, int height) { cairo_xcb_screen_t *cairo_xcb_screen; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); return _cairo_xcb_surface_create_internal (cairo_xcb_screen, bitmap, FALSE, PIXMAN_a1, cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1], width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create_for_bitmap); #endif /** * cairo_xcb_surface_create_with_xrender_format: * @connection: an XCB connection * @drawable: an XCB drawable * @screen: the XCB screen associated with @drawable * @format: the picture format to use for drawing to @drawable. The * depth of @format mush match the depth of the drawable. * @width: the current width of @drawable * @height: the current height of @drawable * * Creates an XCB surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided picture format. * * Note: If @drawable is a Window, then the function * cairo_xcb_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. * * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if an error such as out of memory * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.12 **/ cairo_surface_t * cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, xcb_screen_t *screen, xcb_drawable_t drawable, xcb_render_pictforminfo_t *format, int width, int height) { cairo_xcb_screen_t *cairo_xcb_screen; cairo_format_masks_t image_masks; pixman_format_code_t pixman_format; if (xcb_connection_has_error (connection)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (unlikely (width <= 0 || height <= 0)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); image_masks.alpha_mask = (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift; image_masks.red_mask = (unsigned long) format->direct.red_mask << format->direct.red_shift; image_masks.green_mask = (unsigned long) format->direct.green_mask << format->direct.green_shift; image_masks.blue_mask = (unsigned long) format->direct.blue_mask << format->direct.blue_shift; #if 0 image_masks.bpp = format->depth; #else if (format->depth > 16) image_masks.bpp = 32; else if (format->depth > 8) image_masks.bpp = 16; else if (format->depth > 1) image_masks.bpp = 8; else image_masks.bpp = 1; #endif if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); return _cairo_xcb_surface_create_internal (cairo_xcb_screen, drawable, FALSE, pixman_format, format->id, width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); #endif /* This does the necessary fixup when a surface's drawable or size changed. */ static void _drawable_changed (cairo_xcb_surface_t *surface) { _cairo_surface_set_error (&surface->base, _cairo_surface_begin_modification (&surface->base)); _cairo_boxes_clear (&surface->fallback_damage); cairo_surface_destroy (&surface->fallback->base); surface->deferred_clear = FALSE; surface->fallback = NULL; } /** * cairo_xcb_surface_set_size: * @surface: a #cairo_surface_t for the XCB backend * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new size of the XCB drawable underlying the * surface. For a surface created for a window (rather than a pixmap), * this function must be called each time the size of the window * changes. (For a subwindow, you are normally resizing the window * yourself, but for a toplevel window, it is necessary to listen for * ConfigureNotify events.) * * A pixmap can never change size, so it is never necessary to call * this function on a surface created for a pixmap. * * If cairo_surface_flush() wasn't called, some pending operations * might be discarded. * * Since: 1.12 **/ void cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_xcb_surface_t *surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } surface = (cairo_xcb_surface_t *) abstract_surface; _drawable_changed(surface); surface->width = width; surface->height = height; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_set_size); #endif /** * cairo_xcb_surface_set_drawable: * @surface: a #cairo_surface_t for the XCB backend * @drawable: the new drawable of the surface * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new drawable and size of the XCB drawable underlying the * surface. * * If cairo_surface_flush() wasn't called, some pending operations * might be discarded. * * Since: 1.12 **/ void cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface, xcb_drawable_t drawable, int width, int height) { cairo_xcb_surface_t *surface; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } surface = (cairo_xcb_surface_t *) abstract_surface; /* XXX: and what about this case? */ if (surface->owns_pixmap) return; _drawable_changed (surface); if (surface->drawable != drawable) { cairo_status_t status; status = _cairo_xcb_connection_acquire (surface->connection); if (unlikely (status)) return; if (surface->picture != XCB_NONE) { _cairo_xcb_connection_render_free_picture (surface->connection, surface->picture); surface->picture = XCB_NONE; } _cairo_xcb_connection_release (surface->connection); surface->drawable = drawable; } surface->width = width; surface->height = height; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_set_drawable); #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xcb.h000066400000000000000000000072771271037650300243350ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Chris Wilson */ #ifndef CAIRO_XCB_H #define CAIRO_XCB_H #include "cairo.h" #if CAIRO_HAS_XCB_SURFACE #include #include CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_xcb_surface_create (xcb_connection_t *connection, xcb_drawable_t drawable, xcb_visualtype_t *visual, int width, int height); cairo_public cairo_surface_t * cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, xcb_screen_t *screen, xcb_pixmap_t bitmap, int width, int height); cairo_public cairo_surface_t * cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, xcb_screen_t *screen, xcb_drawable_t drawable, xcb_render_pictforminfo_t *format, int width, int height); cairo_public void cairo_xcb_surface_set_size (cairo_surface_t *surface, int width, int height); cairo_public void cairo_xcb_surface_set_drawable (cairo_surface_t *surface, xcb_drawable_t drawable, int width, int height); cairo_public xcb_connection_t * cairo_xcb_device_get_connection (cairo_device_t *device); /* debug interface */ cairo_public void cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, int major_version, int minor_version); cairo_public void cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, int major_version, int minor_version); /* * @precision: -1 implies automatically choose based on antialiasing mode, * any other value overrides and sets the corresponding PolyMode. */ cairo_public void cairo_xcb_device_debug_set_precision (cairo_device_t *device, int precision); cairo_public int cairo_xcb_device_debug_get_precision (cairo_device_t *device); CAIRO_END_DECLS #else /* CAIRO_HAS_XCB_SURFACE */ # error Cairo was not compiled with support for the xcb backend #endif /* CAIRO_HAS_XCB_SURFACE */ #endif /* CAIRO_XCB_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-core-compositor.c000066400000000000000000000455251271037650300276320ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ /* The original X drawing API was very restrictive in what it could handle, * pixel-aligned fill/blits are all that map into Cairo's drawing model. */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-boxes-private.h" #include "cairo-clip-inline.h" #include "cairo-compositor-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-private.h" #include "cairo-region-private.h" #include "cairo-surface-offset-private.h" /* the low-level interface */ static cairo_int_status_t acquire (void *abstract_dst) { cairo_xlib_surface_t *dst = abstract_dst; return _cairo_xlib_display_acquire (dst->base.device, &dst->display); } static cairo_int_status_t release (void *abstract_dst) { cairo_xlib_surface_t *dst = abstract_dst; cairo_device_release (&dst->display->base); dst->display = NULL; return CAIRO_STATUS_SUCCESS; } struct _fill_box { Display *dpy; Drawable drawable; GC gc; //cairo_surface_t *dither = NULL; }; static cairo_bool_t fill_box (cairo_box_t *box, void *closure) { struct _fill_box *data = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); XFillRectangle (data->dpy, data->drawable, data->gc, x, y, width, height); return TRUE; } static void _characterize_field (uint32_t mask, int *width, int *shift) { *width = _cairo_popcount (mask); /* The final '& 31' is to force a 0 mask to result in 0 shift. */ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; } static uint32_t color_to_pixel (cairo_xlib_surface_t *dst, const cairo_color_t *color) { uint32_t rgba = 0; int width, shift; _characterize_field (dst->a_mask, &width, &shift); rgba |= color->alpha_short >> (16 - width) << shift; _characterize_field (dst->r_mask, &width, &shift); rgba |= color->red_short >> (16 - width) << shift; _characterize_field (dst->g_mask, &width, &shift); rgba |= color->green_short >> (16 - width) << shift; _characterize_field (dst->b_mask, &width, &shift); rgba |= color->blue_short >> (16 - width) << shift; return rgba; } static cairo_int_status_t _fill_box_init (struct _fill_box *fb, cairo_xlib_surface_t *dst, const cairo_color_t *color) { cairo_int_status_t status; status = _cairo_xlib_surface_get_gc (dst->display, dst, &fb->gc); if (unlikely (status)) return status; fb->dpy = dst->display->display; fb->drawable = dst->drawable; if (dst->visual && dst->visual->class != TrueColor && 0) { #if 0 cairo_solid_pattern_t solid; cairo_surface_attributes_t attrs; _cairo_pattern_init_solid (&solid, color); status = _cairo_pattern_acquire_surface (&solid.base, &dst->base, 0, 0, ARRAY_LENGTH (dither_pattern[0]), ARRAY_LENGTH (dither_pattern), CAIRO_PATTERN_ACQUIRE_NONE, &dither, &attrs); if (unlikely (status)) { _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc); return status; } XSetTSOrigin (fb->dpy, fb->gc, - (dst->base.device_transform.x0 + attrs.x_offset), - (dst->base.device_transform.y0 + attrs.y_offset)); XSetTile (fb->dpy, fb->gc, ((cairo_xlib_surface_t *) dither)->drawable); #endif } else { XGCValues gcv; gcv.foreground = color_to_pixel (dst, color); gcv.fill_style = FillSolid; XChangeGC (fb->dpy, fb->gc, GCFillStyle | GCForeground, &gcv); } return CAIRO_INT_STATUS_SUCCESS; } static void _fill_box_fini (struct _fill_box *fb, cairo_xlib_surface_t *dst) { _cairo_xlib_surface_put_gc (dst->display, dst, fb->gc); //cairo_surface_destroy (fb->dither); } cairo_int_status_t _cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_int_status_t status; struct _fill_box fb; status = _fill_box_init (&fb, dst, color); if (unlikely (status)) return status; _cairo_boxes_for_each_box (boxes, fill_box, &fb); _fill_box_fini (&fb, dst); return CAIRO_STATUS_SUCCESS; } cairo_int_status_t _cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst, const cairo_color_t *color, int num_rects, cairo_rectangle_int_t *rects) { cairo_int_status_t status; struct _fill_box fb; int i; status = _fill_box_init (&fb, dst, color); if (unlikely (status)) return status; for (i = 0; i < num_rects; i++) XFillRectangle (fb.dpy, fb.drawable, fb.gc, rects[i].x, rects[i].y, rects[i].width, rects[i].height); _fill_box_fini (&fb, dst); return CAIRO_STATUS_SUCCESS; } struct _fallback_box { cairo_xlib_surface_t *dst; cairo_format_t format; const cairo_pattern_t *pattern; }; static cairo_bool_t fallback_box (cairo_box_t *box, void *closure) { struct _fallback_box *data = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); cairo_surface_t *image; cairo_status_t status; /* XXX for EXTEND_NONE and if the box is wholly outside we can just fill */ image = cairo_surface_create_similar_image (&data->dst->base, data->format, width, height); status = _cairo_surface_offset_paint (image, x, y, CAIRO_OPERATOR_SOURCE, data->pattern, NULL); if (status == CAIRO_STATUS_SUCCESS) { status = _cairo_xlib_surface_draw_image (data->dst, (cairo_image_surface_t *)image, 0, 0, width, height, x, y); } cairo_surface_destroy (image); return status == CAIRO_STATUS_SUCCESS; } static cairo_int_status_t fallback_boxes (cairo_xlib_surface_t *dst, const cairo_pattern_t *pattern, cairo_boxes_t *boxes) { struct _fallback_box fb; /* XXX create_similar_image using pixman_format? */ switch (dst->depth) { case 8: fb.format = CAIRO_FORMAT_A8; break; case 16: fb.format = CAIRO_FORMAT_RGB16_565; break; case 24: fb.format = CAIRO_FORMAT_RGB24; break; case 30: fb.format = CAIRO_FORMAT_RGB30; break; case 32: fb.format = CAIRO_FORMAT_ARGB32; break; default: return CAIRO_INT_STATUS_UNSUPPORTED; } fb.dst = dst; fb.pattern = pattern; if (! _cairo_boxes_for_each_box (boxes, fallback_box, &fb)) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t render_boxes (cairo_xlib_surface_t *dst, const cairo_pattern_t *pattern, cairo_boxes_t *boxes) { double pad; if (_cairo_pattern_analyze_filter (pattern, &pad) != CAIRO_FILTER_NEAREST) return fallback_boxes (dst, pattern, boxes); switch (pattern->extend) { default: case CAIRO_EXTEND_NONE: case CAIRO_EXTEND_REFLECT: case CAIRO_EXTEND_PAD: return fallback_boxes (dst, pattern, boxes); case CAIRO_EXTEND_REPEAT: /* XXX Use tiling */ return fallback_boxes (dst, pattern, boxes); } } /* the mid-level: converts boxes into drawing operations */ struct _box_data { Display *dpy; cairo_xlib_surface_t *dst; cairo_surface_t *src; GC gc; int tx, ty; int width, height; }; static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure) { struct _box_data *data = closure; /* The box is pixel-aligned so the truncation is safe. */ return _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 && _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 && _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width && _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height; } static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) { const struct _box_data *iub = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); return _cairo_xlib_surface_draw_image (iub->dst, (cairo_image_surface_t *)iub->src, x + iub->tx, y + iub->ty, width, height, x, y) == CAIRO_STATUS_SUCCESS; } static cairo_bool_t surface_matches_image_format (cairo_xlib_surface_t *surface, cairo_image_surface_t *image) { cairo_format_masks_t format; return (_pixman_format_to_masks (image->pixman_format, &format) && (format.alpha_mask == surface->a_mask || surface->a_mask == 0) && (format.red_mask == surface->r_mask || surface->r_mask == 0) && (format.green_mask == surface->g_mask || surface->g_mask == 0) && (format.blue_mask == surface->b_mask || surface->b_mask == 0)); } static cairo_status_t upload_image_inplace (cairo_xlib_surface_t *dst, const cairo_pattern_t *source, cairo_boxes_t *boxes) { const cairo_surface_pattern_t *pattern; struct _box_data iub; cairo_image_surface_t *image; if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; pattern = (const cairo_surface_pattern_t *) source; if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) return CAIRO_INT_STATUS_UNSUPPORTED; image = (cairo_image_surface_t *) pattern->surface; if (image->format == CAIRO_FORMAT_INVALID) return CAIRO_INT_STATUS_UNSUPPORTED; if (image->depth != dst->depth) return CAIRO_INT_STATUS_UNSUPPORTED; if (! surface_matches_image_format (dst, image)) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX subsurface */ if (! _cairo_matrix_is_integer_translation (&source->matrix, &iub.tx, &iub.ty)) return CAIRO_INT_STATUS_UNSUPPORTED; iub.dst = dst; iub.src = &image->base; iub.width = image->width; iub.height = image->height; /* First check that the data is entirely within the image */ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &iub)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_boxes_for_each_box (boxes, image_upload_box, &iub)) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_bool_t copy_box (cairo_box_t *box, void *closure) { const struct _box_data *cb = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); XCopyArea (cb->dpy, ((cairo_xlib_surface_t *)cb->src)->drawable, cb->dst->drawable, cb->gc, x + cb->tx, y + cb->ty, width, height, x, y); return TRUE; } static cairo_status_t copy_boxes (cairo_xlib_surface_t *dst, const cairo_pattern_t *source, cairo_boxes_t *boxes) { const cairo_surface_pattern_t *pattern; struct _box_data cb; cairo_xlib_surface_t *src; cairo_status_t status; if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; /* XXX subsurface */ pattern = (const cairo_surface_pattern_t *) source; if (pattern->surface->backend->type != CAIRO_SURFACE_TYPE_XLIB) return CAIRO_INT_STATUS_UNSUPPORTED; src = (cairo_xlib_surface_t *) pattern->surface; if (src->depth != dst->depth) return CAIRO_INT_STATUS_UNSUPPORTED; /* We can only have a single control for subwindow_mode on the * GC. If we have a Window destination, we need to set ClipByChildren, * but if we have a Window source, we need IncludeInferiors. If we have * both a Window destination and source, we must fallback. There is * no convenient way to detect if a drawable is a Pixmap or Window, * therefore we can only rely on those surfaces that we created * ourselves to be Pixmaps, and treat everything else as a potential * Window. */ if (! src->owns_pixmap && ! dst->owns_pixmap) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_xlib_surface_same_screen (dst, src)) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &cb.tx, &cb.ty)) return CAIRO_INT_STATUS_UNSUPPORTED; cb.dpy = dst->display->display; cb.dst = dst; cb.src = &src->base; cb.width = src->width; cb.height = src->height; /* First check that the data is entirely within the image */ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_xlib_surface_get_gc (dst->display, dst, &cb.gc); if (unlikely (status)) return status; if (! src->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = IncludeInferiors; XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); } status = CAIRO_STATUS_SUCCESS; if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) status = CAIRO_INT_STATUS_UNSUPPORTED; if (! src->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = ClipByChildren; XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); } _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc); return status; } static cairo_status_t draw_boxes (cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface; cairo_operator_t op = extents->op; const cairo_pattern_t *src = &extents->source_pattern.base; cairo_int_status_t status; if (boxes->num_boxes == 0 && extents->is_bounded) return CAIRO_STATUS_SUCCESS; if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (op == CAIRO_OPERATOR_CLEAR) op = CAIRO_OPERATOR_SOURCE; if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (src, &extents->bounded)) op = CAIRO_OPERATOR_SOURCE; if (dst->base.is_clear && op == CAIRO_OPERATOR_OVER) op = CAIRO_OPERATOR_SOURCE; if (op != CAIRO_OPERATOR_SOURCE) return CAIRO_INT_STATUS_UNSUPPORTED; status = acquire (dst); if (unlikely (status)) return status; if (src->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_xlib_core_fill_boxes (dst, &((cairo_solid_pattern_t *) src)->color, boxes); } else { status = upload_image_inplace (dst, src, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = copy_boxes (dst, src, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = render_boxes (dst, src, boxes); } release (dst); return status; } /* high-level compositor interface */ static cairo_int_status_t _cairo_xlib_core_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_clip_is_region (extents->clip)) { cairo_boxes_t boxes; _cairo_clip_steal_boxes (extents->clip, &boxes); status = draw_boxes (extents, &boxes); _cairo_clip_unsteal_boxes (extents->clip, &boxes); } return status; } static cairo_int_status_t _cairo_xlib_core_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (extents->clip->path == NULL && _cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = draw_boxes (extents, &boxes); _cairo_boxes_fini (&boxes); } return status; } static cairo_int_status_t _cairo_xlib_core_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (extents->clip->path == NULL && _cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init_with_clip (&boxes, extents->clip); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = draw_boxes (extents, &boxes); _cairo_boxes_fini (&boxes); } return status; } const cairo_compositor_t * _cairo_xlib_core_compositor_get (void) { static cairo_compositor_t compositor; if (compositor.delegate == NULL) { compositor.delegate = _cairo_xlib_fallback_compositor_get (); compositor.paint = _cairo_xlib_core_compositor_paint; compositor.mask = NULL; compositor.fill = _cairo_xlib_core_compositor_fill; compositor.stroke = _cairo_xlib_core_compositor_stroke; compositor.glyphs = NULL; /* XXX PolyGlyph? */ } return &compositor; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-display.c000066400000000000000000000472741271037650300261560ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-freelist-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include /* For XESetCloseDisplay */ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); static cairo_xlib_display_t *_cairo_xlib_display_list; static int _noop_error_handler (Display *display, XErrorEvent *event) { return False; /* return value is ignored */ } static void _cairo_xlib_display_finish (void *abstract_display) { cairo_xlib_display_t *display = abstract_display; Display *dpy = display->display; _cairo_xlib_display_fini_shm (display); if (! cairo_device_acquire (&display->base)) { cairo_xlib_error_func_t old_handler; /* protect the notifies from triggering XErrors */ XSync (dpy, False); old_handler = XSetErrorHandler (_noop_error_handler); while (! cairo_list_is_empty (&display->fonts)) { _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts, cairo_xlib_font_t, link)); } while (! cairo_list_is_empty (&display->screens)) { _cairo_xlib_screen_destroy (display, cairo_list_first_entry (&display->screens, cairo_xlib_screen_t, link)); } XSync (dpy, False); XSetErrorHandler (old_handler); cairo_device_release (&display->base); } } static void _cairo_xlib_display_destroy (void *abstract_display) { cairo_xlib_display_t *display = abstract_display; free (display); } static int _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) { cairo_xlib_display_t *display, **prev, *next; CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); for (display = _cairo_xlib_display_list; display; display = display->next) if (display->display == dpy) break; CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); if (display == NULL) return 0; cairo_device_finish (&display->base); /* * Unhook from the global list */ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); prev = &_cairo_xlib_display_list; for (display = _cairo_xlib_display_list; display; display = next) { next = display->next; if (display->display == dpy) { *prev = next; break; } else prev = &display->next; } CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); display->display = NULL; /* catch any later invalid access */ cairo_device_destroy (&display->base); /* Return value in accordance with requirements of * XESetCloseDisplay */ return 0; } static const cairo_device_backend_t _cairo_xlib_device_backend = { CAIRO_DEVICE_TYPE_XLIB, NULL, NULL, NULL, /* flush */ _cairo_xlib_display_finish, _cairo_xlib_display_destroy, }; static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display) { #if 1 if (display->render_major > 0 || display->render_minor >= 4) display->compositor = _cairo_xlib_traps_compositor_get (); else if (display->render_major > 0 || display->render_minor >= 0) display->compositor = _cairo_xlib_mask_compositor_get (); else display->compositor = _cairo_xlib_core_compositor_get (); #else display->compositor = _cairo_xlib_fallback_compositor_get (); #endif } /** * _cairo_xlib_device_create: * @dpy: the display to create the device for * * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. * * Returns: the device belonging to @dpy **/ cairo_device_t * _cairo_xlib_device_create (Display *dpy) { cairo_xlib_display_t *display; cairo_xlib_display_t **prev; cairo_device_t *device; XExtCodes *codes; const char *env; CAIRO_MUTEX_INITIALIZE (); /* There is an apparent deadlock between this mutex and the * mutex for the display, but it's actually safe. For the * app to call XCloseDisplay() while any other thread is * inside this function would be an error in the logic * app, and the CloseDisplay hook is the only other place we * acquire this mutex. */ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next) { if (display->display == dpy) { /* * MRU the list */ if (prev != &_cairo_xlib_display_list) { *prev = display->next; display->next = _cairo_xlib_display_list; _cairo_xlib_display_list = display; } device = cairo_device_reference (&display->base); goto UNLOCK; } } display = malloc (sizeof (cairo_xlib_display_t)); if (unlikely (display == NULL)) { device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); goto UNLOCK; } _cairo_device_init (&display->base, &_cairo_xlib_device_backend); display->display = dpy; cairo_list_init (&display->screens); cairo_list_init (&display->fonts); display->closed = FALSE; /* Xlib calls out to the extension close_display hooks in LIFO * order. So we have to ensure that all extensions that we depend * on in our close_display hook are properly initialized before we * add our hook. For now, that means Render, so we call into its * QueryVersion function to ensure it gets initialized. */ display->render_major = display->render_minor = -1; XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); env = getenv ("CAIRO_DEBUG"); if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) { int max_render_major, max_render_minor; env += sizeof ("xrender-version=") - 1; if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) max_render_major = max_render_minor = -1; if (max_render_major < display->render_major || (max_render_major == display->render_major && max_render_minor < display->render_minor)) { display->render_major = max_render_major; display->render_minor = max_render_minor; } } _cairo_xlib_display_select_compositor (display); display->white = NULL; memset (display->alpha, 0, sizeof (display->alpha)); memset (display->solid, 0, sizeof (display->solid)); memset (display->solid_cache, 0, sizeof (display->solid_cache)); memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache)); memset (display->cached_xrender_formats, 0, sizeof (display->cached_xrender_formats)); display->force_precision = -1; _cairo_xlib_display_init_shm (display); /* Prior to Render 0.10, there is no protocol support for gradients and * we call function stubs instead, which would silently consume the drawing. */ #if RENDER_MAJOR == 0 && RENDER_MINOR < 10 display->buggy_gradients = TRUE; #else display->buggy_gradients = FALSE; #endif display->buggy_pad_reflect = FALSE; display->buggy_repeat = FALSE; /* This buggy_repeat condition is very complicated because there * are multiple X server code bases (with multiple versioning * schemes within a code base), and multiple bugs. * * The X servers: * * 1. The Vendor=="XFree86" code base with release numbers such * as 4.7.0 (VendorRelease==40700000). * * 2. The Vendor=="X.Org" code base (a descendant of the * XFree86 code base). It originally had things like * VendorRelease==60700000 for release 6.7.0 but then changed * its versioning scheme so that, for example, * VendorRelease==10400000 for the 1.4.0 X server within the * X.Org 7.3 release. * * The bugs: * * 1. The original bug that led to the buggy_repeat * workaround. This was a bug that Owen Taylor investigated, * understood well, and characterized against carious X * servers. Confirmed X servers with this bug include: * * "XFree86" <= 40500000 * "X.Org" <= 60802000 (only with old numbering >= 60700000) * * 2. A separate bug resulting in a crash of the X server when * using cairo's extend-reflect test case, (which, surprisingly * enough was not passing RepeatReflect to the X server, but * instead using RepeatNormal in a workaround). Nobody to date * has understood the bug well, but it appears to be gone as of * the X.Org 1.4.0 server. This bug is coincidentally avoided * by using the same buggy_repeat workaround. Confirmed X * servers with this bug include: * * "X.org" == 60900000 (old versioning scheme) * "X.org" < 10400000 (new numbering scheme) * * For the old-versioning-scheme X servers we don't know * exactly when second the bug started, but since bug 1 is * present through 6.8.2 and bug 2 is present in 6.9.0 it seems * safest to just blacklist all old-versioning-scheme X servers, * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. */ if (_cairo_xlib_vendor_is_xorg (dpy)) { if (VendorRelease (dpy) >= 60700000) { if (VendorRelease (dpy) < 70000000) display->buggy_repeat = TRUE; /* We know that gradients simply do not work in early Xorg servers */ if (VendorRelease (dpy) < 70200000) display->buggy_gradients = TRUE; /* And the extended repeat modes were not fixed until much later */ display->buggy_pad_reflect = TRUE; } else { if (VendorRelease (dpy) < 10400000) display->buggy_repeat = TRUE; /* Too many bugs in the early drivers */ if (VendorRelease (dpy) < 10699000) display->buggy_pad_reflect = TRUE; } } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { if (VendorRelease (dpy) <= 40500000) display->buggy_repeat = TRUE; display->buggy_gradients = TRUE; display->buggy_pad_reflect = TRUE; } codes = XAddExtension (dpy); if (unlikely (codes == NULL)) { device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); free (display); goto UNLOCK; } XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); cairo_device_reference (&display->base); /* add one for the CloseDisplay */ display->next = _cairo_xlib_display_list; _cairo_xlib_display_list = display; device = &display->base; UNLOCK: CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); return device; } cairo_status_t _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) { cairo_status_t status; status = cairo_device_acquire (device); if (status) return status; *display = (cairo_xlib_display_t *) device; return CAIRO_STATUS_SUCCESS; } XRenderPictFormat * _cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display, pixman_format_code_t format) { Display *dpy = display->display; XRenderPictFormat tmpl; int mask; #define MASK(x) ((1<<(x))-1) tmpl.depth = PIXMAN_FORMAT_DEPTH(format); mask = PictFormatType | PictFormatDepth; switch (PIXMAN_FORMAT_TYPE(format)) { case PIXMAN_TYPE_ARGB: tmpl.type = PictTypeDirect; tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); if (PIXMAN_FORMAT_A(format)) tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) + PIXMAN_FORMAT_G(format) + PIXMAN_FORMAT_B(format)); tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); tmpl.direct.red = (PIXMAN_FORMAT_G(format) + PIXMAN_FORMAT_B(format)); tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); tmpl.direct.green = PIXMAN_FORMAT_B(format); tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); tmpl.direct.blue = 0; mask |= PictFormatRed | PictFormatRedMask; mask |= PictFormatGreen | PictFormatGreenMask; mask |= PictFormatBlue | PictFormatBlueMask; mask |= PictFormatAlpha | PictFormatAlphaMask; break; case PIXMAN_TYPE_ABGR: tmpl.type = PictTypeDirect; tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); if (tmpl.direct.alphaMask) tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) + PIXMAN_FORMAT_G(format) + PIXMAN_FORMAT_R(format)); tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); tmpl.direct.blue = (PIXMAN_FORMAT_G(format) + PIXMAN_FORMAT_R(format)); tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); tmpl.direct.green = PIXMAN_FORMAT_R(format); tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); tmpl.direct.red = 0; mask |= PictFormatRed | PictFormatRedMask; mask |= PictFormatGreen | PictFormatGreenMask; mask |= PictFormatBlue | PictFormatBlueMask; mask |= PictFormatAlpha | PictFormatAlphaMask; break; case PIXMAN_TYPE_BGRA: tmpl.type = PictTypeDirect; tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format)); tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - PIXMAN_FORMAT_G(format)); tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format)); tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); tmpl.direct.alpha = 0; mask |= PictFormatRed | PictFormatRedMask; mask |= PictFormatGreen | PictFormatGreenMask; mask |= PictFormatBlue | PictFormatBlueMask; mask |= PictFormatAlpha | PictFormatAlphaMask; break; case PIXMAN_TYPE_A: tmpl.type = PictTypeDirect; tmpl.direct.alpha = 0; tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); mask |= PictFormatAlpha | PictFormatAlphaMask; break; case PIXMAN_TYPE_COLOR: case PIXMAN_TYPE_GRAY: /* XXX Find matching visual/colormap */ tmpl.type = PictTypeIndexed; //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid; //mask |= PictFormatColormap; return NULL; } #undef MASK /* XXX caching? */ return XRenderFindFormat(dpy, mask, &tmpl, 0); } XRenderPictFormat * _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, cairo_format_t format) { XRenderPictFormat *xrender_format; #if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER xrender_format = display->cached_xrender_formats[format]; if (likely (xrender_format != NULL)) return xrender_format; #endif xrender_format = display->cached_xrender_formats[format]; if (xrender_format == NULL) { int pict_format = PictStandardNUM; switch (format) { case CAIRO_FORMAT_A1: pict_format = PictStandardA1; break; case CAIRO_FORMAT_A8: pict_format = PictStandardA8; break; case CAIRO_FORMAT_RGB24: pict_format = PictStandardRGB24; break; case CAIRO_FORMAT_RGB16_565: xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, PIXMAN_r5g6b5); break; case CAIRO_FORMAT_RGB30: xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, PIXMAN_x2r10g10b10); break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: pict_format = PictStandardARGB32; break; } if (pict_format != PictStandardNUM) xrender_format = XRenderFindStandardFormat (display->display, pict_format); display->cached_xrender_formats[format] = xrender_format; } return xrender_format; } cairo_xlib_screen_t * _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, Screen *screen) { cairo_xlib_screen_t *info; cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) { if (info->screen == screen) { if (display->screens.next != &info->link) cairo_list_move (&info->link, &display->screens); return info; } } return NULL; } cairo_bool_t _cairo_xlib_display_has_repeat (cairo_device_t *device) { return ! ((cairo_xlib_display_t *) device)->buggy_repeat; } cairo_bool_t _cairo_xlib_display_has_reflect (cairo_device_t *device) { return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect; } cairo_bool_t _cairo_xlib_display_has_gradients (cairo_device_t *device) { return ! ((cairo_xlib_display_t *) device)->buggy_gradients; } /** * cairo_xlib_device_debug_cap_xrender_version: * @device: a #cairo_device_t for the Xlib backend * @major_version: major version to restrict to * @minor_version: minor version to restrict to * * Restricts all future Xlib surfaces for this devices to the specified version * of the RENDER extension. This function exists solely for debugging purpose. * It let's you find out how cairo would behave with an older version of * the RENDER extension. * * Use the special values -1 and -1 for disabling the RENDER extension. * * Since: 1.12 **/ void cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, int major_version, int minor_version) { cairo_xlib_display_t *display = (cairo_xlib_display_t *) device; if (device == NULL || device->status) return; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) return; if (major_version < display->render_major || (major_version == display->render_major && minor_version < display->render_minor)) { display->render_major = major_version; display->render_minor = minor_version; } _cairo_xlib_display_select_compositor (display); } /** * cairo_xlib_device_debug_set_precision: * @device: a #cairo_device_t for the Xlib backend * @precision: the precision to use * * Render supports two modes of precision when rendering trapezoids. Set * the precision to the desired mode. * * Since: 1.12 **/ void cairo_xlib_device_debug_set_precision (cairo_device_t *device, int precision) { if (device == NULL || device->status) return; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return; } ((cairo_xlib_display_t *) device)->force_precision = precision; } /** * cairo_xlib_device_debug_get_precision: * @device: a #cairo_device_t for the Xlib backend * * Get the Xrender precision mode. * * Returns: the render precision mode * * Since: 1.12 **/ int cairo_xlib_device_debug_get_precision (cairo_device_t *device) { if (device == NULL || device->status) return -1; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return -1; } return ((cairo_xlib_display_t *) device)->force_precision; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-fallback-compositor.c000066400000000000000000000166761271037650300304460ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-compositor-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-offset-private.h" static const cairo_compositor_t * _get_compositor (cairo_surface_t *surface) { return ((cairo_image_surface_t *)surface)->compositor; } static cairo_bool_t unclipped (cairo_xlib_surface_t *xlib, cairo_clip_t *clip) { cairo_rectangle_int_t r; r.x = r.y = 0; r.width = xlib->width; r.height = xlib->height; return _cairo_clip_contains_rectangle (clip, &r); } static cairo_int_status_t _cairo_xlib_shm_compositor_paint (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; cairo_int_status_t status; cairo_surface_t *shm; cairo_bool_t overwrite; TRACE ((stderr, "%s\n", __FUNCTION__)); overwrite = extents->op <= CAIRO_OPERATOR_SOURCE && unclipped (xlib, extents->clip); shm = _cairo_xlib_surface_get_shm (xlib, overwrite); if (shm == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_paint (_get_compositor (shm), shm, extents->op, &extents->source_pattern.base, extents->clip); if (unlikely (status)) return status; xlib->base.is_clear = extents->op == CAIRO_OPERATOR_CLEAR && unclipped (xlib, extents->clip); xlib->base.serial++; xlib->fallback++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_xlib_shm_compositor_mask (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents) { cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; cairo_int_status_t status; cairo_surface_t *shm; TRACE ((stderr, "%s\n", __FUNCTION__)); shm = _cairo_xlib_surface_get_shm (xlib, FALSE); if (shm == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_mask (_get_compositor (shm), shm, extents->op, &extents->source_pattern.base, &extents->mask_pattern.base, extents->clip); if (unlikely (status)) return status; xlib->base.is_clear = FALSE; xlib->base.serial++; xlib->fallback++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_xlib_shm_compositor_stroke (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; cairo_int_status_t status; cairo_surface_t *shm; TRACE ((stderr, "%s\n", __FUNCTION__)); shm = _cairo_xlib_surface_get_shm (xlib, FALSE); if (shm == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_stroke (_get_compositor (shm), shm, extents->op, &extents->source_pattern.base, path, style, ctm, ctm_inverse, tolerance, antialias, extents->clip); if (unlikely (status)) return status; xlib->base.is_clear = FALSE; xlib->base.serial++; xlib->fallback++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_xlib_shm_compositor_fill (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; cairo_int_status_t status; cairo_surface_t *shm; TRACE ((stderr, "%s\n", __FUNCTION__)); shm = _cairo_xlib_surface_get_shm (xlib, FALSE); if (shm == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_fill (_get_compositor (shm), shm, extents->op, &extents->source_pattern.base, path, fill_rule, tolerance, antialias, extents->clip); if (unlikely (status)) return status; xlib->base.is_clear = FALSE; xlib->base.serial++; xlib->fallback++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static cairo_int_status_t _cairo_xlib_shm_compositor_glyphs (const cairo_compositor_t *_compositor, cairo_composite_rectangles_t *extents, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; cairo_int_status_t status; cairo_surface_t *shm; TRACE ((stderr, "%s\n", __FUNCTION__)); shm = _cairo_xlib_surface_get_shm (xlib, FALSE); if (shm == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_compositor_glyphs (_get_compositor (shm), shm, extents->op, &extents->source_pattern.base, glyphs, num_glyphs, scaled_font, extents->clip); if (unlikely (status)) return status; xlib->base.is_clear = FALSE; xlib->base.serial++; xlib->fallback++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } static const cairo_compositor_t _cairo_xlib_shm_compositor = { &_cairo_fallback_compositor, _cairo_xlib_shm_compositor_paint, _cairo_xlib_shm_compositor_mask, _cairo_xlib_shm_compositor_stroke, _cairo_xlib_shm_compositor_fill, _cairo_xlib_shm_compositor_glyphs, }; const cairo_compositor_t * _cairo_xlib_fallback_compositor_get (void) { return &_cairo_xlib_shm_compositor; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-private.h000066400000000000000000000342311271037650300261550ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributors(s): * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #ifndef CAIRO_XLIB_PRIVATE_H #define CAIRO_XLIB_PRIVATE_H #include "cairo-xlib.h" #include "cairo-xlib-xrender-private.h" #include "cairo-compiler-private.h" #include "cairo-device-private.h" #include "cairo-freelist-type-private.h" #include "cairo-list-private.h" #include "cairo-reference-count-private.h" #include "cairo-types-private.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-private.h" #include #include typedef struct _cairo_xlib_display cairo_xlib_display_t; typedef struct _cairo_xlib_shm_display cairo_xlib_shm_display_t; typedef struct _cairo_xlib_screen cairo_xlib_screen_t; typedef struct _cairo_xlib_source cairo_xlib_source_t; typedef struct _cairo_xlib_proxy cairo_xlib_proxy_t; typedef struct _cairo_xlib_surface cairo_xlib_surface_t; /* size of color cube */ #define CUBE_SIZE 6 /* size of gray ramp */ #define RAMP_SIZE 16 /* maximum number of cached GC's */ #define GC_CACHE_SIZE 4 struct _cairo_xlib_display { cairo_device_t base; cairo_xlib_display_t *next; Display *display; cairo_list_t screens; cairo_list_t fonts; cairo_xlib_shm_display_t *shm; const cairo_compositor_t *compositor; int render_major; int render_minor; XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1]; int force_precision; cairo_surface_t *white; cairo_surface_t *alpha[256]; cairo_surface_t *solid[32]; uint32_t solid_cache[32]; /* low 16 are opaque, high 16 transparent */ struct { uint32_t color; int index; } last_solid_cache[2]; /* TRUE if the server has a bug with repeating pictures * * https://bugs.freedesktop.org/show_bug.cgi?id=3566 * * We can't test for this because it depends on whether the * picture is in video memory or not. * * We also use this variable as a guard against a second * independent bug with transformed repeating pictures: * * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html * * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so * we can reuse the test for now. */ unsigned int buggy_gradients : 1; unsigned int buggy_pad_reflect : 1; unsigned int buggy_repeat : 1; unsigned int closed :1; }; typedef struct _cairo_xlib_visual_info { cairo_list_t link; VisualID visualid; struct { uint8_t a, r, g, b; } colors[256]; uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE]; uint8_t field8_to_cube[256]; int8_t dither8_to_cube[256]; uint8_t gray8_to_pseudocolor[256]; } cairo_xlib_visual_info_t; struct _cairo_xlib_screen { cairo_list_t link; cairo_device_t *device; Screen *screen; cairo_list_t surfaces; cairo_bool_t has_font_options; cairo_font_options_t font_options; GC gc[GC_CACHE_SIZE]; uint8_t gc_depths[GC_CACHE_SIZE]; cairo_list_t visuals; }; enum { GLYPHSET_INDEX_ARGB32, GLYPHSET_INDEX_A8, GLYPHSET_INDEX_A1, NUM_GLYPHSETS }; typedef struct _cairo_xlib_font_glyphset { GlyphSet glyphset; cairo_format_t format; XRenderPictFormat *xrender_format; struct _cairo_xlib_font_glyphset_free_glyphs { int count; unsigned long indices[128]; } to_free; } cairo_xlib_font_glyphset_t; typedef struct _cairo_xlib_font { cairo_scaled_font_private_t base; cairo_scaled_font_t *font; cairo_device_t *device; cairo_list_t link; cairo_xlib_font_glyphset_t glyphset[NUM_GLYPHSETS]; } cairo_xlib_font_t; struct _cairo_xlib_surface { cairo_surface_t base; Picture picture; Drawable drawable; const cairo_compositor_t *compositor; cairo_surface_t *shm; int fallback; cairo_xlib_display_t *display; cairo_xlib_screen_t *screen; cairo_list_t link; Display *dpy; /* only valid between acquire/release */ cairo_bool_t owns_pixmap; Visual *visual; int use_pixmap; int width; int height; int depth; int precision; XRenderPictFormat *xrender_format; /* XXX pixman_format instead of masks? */ uint32_t a_mask; uint32_t r_mask; uint32_t g_mask; uint32_t b_mask; struct _cairo_xlib_source { cairo_surface_t base; Picture picture; Pixmap pixmap; Display *dpy; unsigned int filter:3; unsigned int extend:3; unsigned int has_matrix:1; unsigned int has_component_alpha:1; } embedded_source; }; struct _cairo_xlib_proxy { struct _cairo_xlib_source source; cairo_surface_t *owner; }; inline static cairo_bool_t _cairo_xlib_vendor_is_xorg (Display *dpy) { const char *const vendor = ServerVendor (dpy); return strstr (vendor, "X.Org") || strstr (vendor, "Xorg"); } cairo_private cairo_status_t _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, GC *gc); cairo_private cairo_device_t * _cairo_xlib_device_create (Display *display); cairo_private void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display); cairo_private void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display); cairo_private cairo_xlib_screen_t * _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, Screen *screen); cairo_private cairo_status_t _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display); cairo_private cairo_bool_t _cairo_xlib_display_has_repeat (cairo_device_t *device); cairo_private cairo_bool_t _cairo_xlib_display_has_reflect (cairo_device_t *device); cairo_private cairo_bool_t _cairo_xlib_display_has_gradients (cairo_device_t *device); cairo_private void _cairo_xlib_display_set_precision(cairo_device_t *device, int precision); cairo_private XRenderPictFormat * _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, cairo_format_t format); cairo_private XRenderPictFormat * _cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display, pixman_format_code_t format); cairo_private cairo_status_t _cairo_xlib_screen_get (Display *dpy, Screen *screen, cairo_xlib_screen_t **out); cairo_private void _cairo_xlib_screen_destroy (cairo_xlib_display_t *display, cairo_xlib_screen_t *info); cairo_private GC _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, int depth, Drawable drawable); cairo_private void _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, int depth, GC gc); cairo_private cairo_font_options_t * _cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info); cairo_private cairo_status_t _cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, Visual *visual, cairo_xlib_visual_info_t **out); cairo_private cairo_status_t _cairo_xlib_visual_info_create (Display *dpy, int screen, VisualID visualid, cairo_xlib_visual_info_t **out); cairo_private void _cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info); cairo_private const cairo_compositor_t * _cairo_xlib_core_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_xlib_fallback_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_xlib_mask_compositor_get (void); cairo_private const cairo_compositor_t * _cairo_xlib_traps_compositor_get (void); cairo_private void _cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface); cairo_private void _cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface, cairo_antialias_t antialias); cairo_private cairo_int_status_t _cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, cairo_surface_attributes_t *attributes, double xc, double yc); cairo_private cairo_status_t _cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, int src_x, int src_y, int width, int height, int dst_x, int dst_y); cairo_private cairo_surface_t * _cairo_xlib_source_create_for_pattern (cairo_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y); cairo_private void _cairo_xlib_font_close (cairo_xlib_font_t *font); #define CAIRO_RENDER_AT_LEAST(surface, major, minor) \ (((surface)->render_major > major) || \ (((surface)->render_major == major) && ((surface)->render_minor >= minor))) #define CAIRO_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_RENDER_HAS_COMPOSITE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 1) #define CAIRO_RENDER_HAS_DISJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2) #define CAIRO_RENDER_HAS_CONJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2) #define CAIRO_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_RENDER_HAS_TRIANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_RENDER_HAS_TRISTRIP(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_RENDER_HAS_TRIFAN(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_RENDER_HAS_FILTERS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6) #define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10) #define CAIRO_RENDER_HAS_GRADIENTS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10) #define CAIRO_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 11) #define CAIRO_RENDER_SUPPORTS_OPERATOR(surface, op) \ ((op) <= CAIRO_OPERATOR_SATURATE || \ (CAIRO_RENDER_HAS_PDF_OPERATORS(surface) && \ (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) /* * Return whether two xlib surfaces share the same * screen. Both core and Render drawing require this * when using multiple drawables in an operation. */ static inline cairo_bool_t _cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { return dst->screen == src->screen; } cairo_private cairo_int_status_t _cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst, const cairo_color_t *color, cairo_boxes_t *boxes); cairo_private cairo_int_status_t _cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst, const cairo_color_t *color, int num_rects, cairo_rectangle_int_t *rects); static inline void _cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, GC gc) { _cairo_xlib_screen_put_gc (display, surface->screen, surface->depth, gc); } cairo_private cairo_surface_t * _cairo_xlib_surface_create_similar_shm (void *surface, cairo_format_t format, int width, int height); cairo_private cairo_surface_t * _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, cairo_bool_t overwrite); cairo_private cairo_int_status_t _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface); cairo_private cairo_surface_t * _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, pixman_format_code_t format, int width, int height); cairo_private cairo_surface_t * _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, pixman_format_code_t format, int width, int height); cairo_private void _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, XImage *ximage); cairo_private void * _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface); cairo_private void _cairo_xlib_shm_surface_mark_active (cairo_surface_t *shm); cairo_private cairo_bool_t _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface); cairo_private cairo_bool_t _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface); cairo_private Pixmap _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface); cairo_private XRenderPictFormat * _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface); cairo_private pixman_format_code_t _pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface); #endif /* CAIRO_XLIB_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-render-compositor.c000066400000000000000000001637621271037650300301650ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-compositor-private.h" #include "cairo-damage-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-pattern-private.h" #include "cairo-pixman-private.h" #include "cairo-traps-private.h" #include "cairo-tristrip-private.h" static cairo_int_status_t check_composite (const cairo_composite_rectangles_t *extents) { cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display; if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t acquire (void *abstract_dst) { cairo_xlib_surface_t *dst = abstract_dst; cairo_int_status_t status; status = _cairo_xlib_display_acquire (dst->base.device, &dst->display); if (unlikely (status)) return status; dst->dpy = dst->display->display; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t release (void *abstract_dst) { cairo_xlib_surface_t *dst = abstract_dst; cairo_device_release (&dst->display->base); dst->dpy = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t set_clip_region (void *_surface, cairo_region_t *region) { cairo_xlib_surface_t *surface = _surface; _cairo_xlib_surface_ensure_picture (surface); if (region != NULL) { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))]; XRectangle *rects = stack_rects; int n_rects, i; n_rects = cairo_region_num_rectangles (region); if (n_rects > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].x = rect.x; rects[i].y = rect.y; rects[i].width = rect.width; rects[i].height = rect.height; } XRenderSetPictureClipRectangles (surface->dpy, surface->picture, 0, 0, rects, n_rects); if (rects != stack_rects) free (rects); } else { XRenderPictureAttributes pa; pa.clip_mask = None; XRenderChangePicture (surface->dpy, surface->picture, CPClipMask, &pa); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t copy_image_boxes (void *_dst, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy) { cairo_xlib_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; cairo_int_status_t status; Pixmap src; GC gc; int i, j; assert (image->depth == dst->depth); status = acquire (dst); if (unlikely (status)) return status; status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); if (unlikely (status)) { release (dst); return status; } src = _cairo_xlib_shm_surface_get_pixmap (&image->base); if (boxes->num_boxes == 1) { int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); _cairo_xlib_shm_surface_mark_active (&image->base); XCopyArea (dst->dpy, src, dst->drawable, gc, x1 + dx, y1 + dy, x2 - x1, y2 - y1, x1, y1); } else { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *rects = stack_rects; if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } j = 0; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); if (x2 > x1 && y2 > y1) { rects[j].x = x1; rects[j].y = y1; rects[j].width = x2 - x1; rects[j].height = y2 - y1; j++; } } } XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); _cairo_xlib_shm_surface_mark_active (&image->base); XCopyArea (dst->dpy, src, dst->drawable, gc, 0, 0, image->width, image->height, -dx, -dy); XSetClipMask (dst->dpy, gc, None); if (rects != stack_rects) free (rects); } _cairo_xlib_surface_put_gc (dst->display, dst, gc); release (dst); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t boxes_cover_surface (cairo_boxes_t *boxes, cairo_xlib_surface_t *surface) { cairo_box_t *b; if (boxes->num_boxes != 1) return FALSE; b = &boxes->chunks.base[0]; if (_cairo_fixed_integer_part (b->p1.x) > 0 || _cairo_fixed_integer_part (b->p1.y) > 0) return FALSE; if (_cairo_fixed_integer_part (b->p2.x) < surface->width || _cairo_fixed_integer_part (b->p2.y) < surface->height) return FALSE; return TRUE; } static cairo_int_status_t draw_image_boxes (void *_dst, cairo_image_surface_t *image, cairo_boxes_t *boxes, int dx, int dy) { cairo_xlib_surface_t *dst = _dst; struct _cairo_boxes_chunk *chunk; cairo_image_surface_t *shm = NULL; cairo_int_status_t status; int i; if (image->base.device == dst->base.device) { if (image->depth != dst->depth) return CAIRO_INT_STATUS_UNSUPPORTED; if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) return copy_image_boxes (dst, image, boxes, dx, dy); goto draw_image_boxes; } if (boxes_cover_surface (boxes, dst)) shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE); if (shm) { for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; cairo_rectangle_int_t r; r.x = _cairo_fixed_integer_part (b->p1.x); r.y = _cairo_fixed_integer_part (b->p1.y); r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; if (shm->pixman_format != image->pixman_format || ! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, image->stride / sizeof (uint32_t), shm->stride / sizeof (uint32_t), PIXMAN_FORMAT_BPP (image->pixman_format), PIXMAN_FORMAT_BPP (shm->pixman_format), r.x + dx, r.y + dy, r.x, r.y, r.width, r.height)) { pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, shm->pixman_image, r.x + dx, r.y + dy, 0, 0, r.x, r.y, r.width, r.height); } shm->base.damage = _cairo_damage_add_rectangle (shm->base.damage, &r); } } dst->base.is_clear = FALSE; dst->fallback++; dst->base.serial++; return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (image->depth == dst->depth && ((cairo_xlib_display_t *)dst->display)->shm) { cairo_box_t extents; cairo_rectangle_int_t r; _cairo_boxes_extents (boxes, &extents); _cairo_box_round_to_rectangle (&extents, &r); shm = (cairo_image_surface_t *) _cairo_xlib_surface_create_shm (dst, image->pixman_format, r.width, r.height); if (shm) { int tx = -r.x, ty = -r.y; assert (shm->pixman_format == image->pixman_format); for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; r.x = _cairo_fixed_integer_part (b->p1.x); r.y = _cairo_fixed_integer_part (b->p1.y); r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, image->stride / sizeof (uint32_t), shm->stride / sizeof (uint32_t), PIXMAN_FORMAT_BPP (image->pixman_format), PIXMAN_FORMAT_BPP (shm->pixman_format), r.x + dx, r.y + dy, r.x + tx, r.y + ty, r.width, r.height)) { pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, shm->pixman_image, r.x + dx, r.y + dy, 0, 0, r.x + tx, r.y + ty, r.width, r.height); } } } dx = tx; dy = ty; image = shm; if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) { status = copy_image_boxes (dst, image, boxes, dx, dy); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto out; } } } draw_image_boxes: status = CAIRO_STATUS_SUCCESS; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { cairo_box_t *b = &chunk->base[i]; int x1 = _cairo_fixed_integer_part (b->p1.x); int y1 = _cairo_fixed_integer_part (b->p1.y); int x2 = _cairo_fixed_integer_part (b->p2.x); int y2 = _cairo_fixed_integer_part (b->p2.y); if (_cairo_xlib_surface_draw_image (dst, image, x1 + dx, y1 + dy, x2 - x1, y2 - y1, x1, y1)) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto out; } } } out: cairo_surface_destroy (&shm->base); return status; } static cairo_int_status_t copy_boxes (void *_dst, cairo_surface_t *_src, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents, int dx, int dy) { cairo_xlib_surface_t *dst = _dst; cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src; struct _cairo_boxes_chunk *chunk; cairo_int_status_t status; GC gc; Drawable d; int i, j; if (! _cairo_xlib_surface_same_screen (dst, src)) return CAIRO_INT_STATUS_UNSUPPORTED; if (dst->depth != src->depth) return CAIRO_INT_STATUS_UNSUPPORTED; status = acquire (dst); if (unlikely (status)) return status; status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); if (unlikely (status)) { release (dst); return status; } if (src->fallback && src->shm->damage->dirty) { assert (src != dst); d = _cairo_xlib_shm_surface_get_pixmap (src->shm); assert (d != 0); } else { if (! src->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = IncludeInferiors; XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); } d = src->drawable; } if (boxes->num_boxes == 1) { int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); XCopyArea (dst->dpy, d, dst->drawable, gc, x1 + dx, y1 + dy, x2 - x1, y2 - y1, x1, y1); } else { /* We can only have a single control for subwindow_mode on the * GC. If we have a Window destination, we need to set ClipByChildren, * but if we have a Window source, we need IncludeInferiors. If we have * both a Window destination and source, we must fallback. There is * no convenient way to detect if a drawable is a Pixmap or Window, * therefore we can only rely on those surfaces that we created * ourselves to be Pixmaps, and treat everything else as a potential * Window. */ if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) { for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); XCopyArea (dst->dpy, d, dst->drawable, gc, x1 + dx, y1 + dy, x2 - x1, y2 - y1, x1, y1); } } } else { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *rects = stack_rects; if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } j = 0; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); rects[j].x = x1; rects[j].y = y1; rects[j].width = x2 - x1; rects[j].height = y2 - y1; j++; } } assert (j == boxes->num_boxes); XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); XCopyArea (dst->dpy, d, dst->drawable, gc, extents->x + dx, extents->y + dy, extents->width, extents->height, extents->x, extents->y); XSetClipMask (dst->dpy, gc, None); if (rects != stack_rects) free (rects); } } if (src->fallback && src->shm->damage->dirty) { _cairo_xlib_shm_surface_mark_active (src->shm); } else if (! src->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = ClipByChildren; XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); } _cairo_xlib_surface_put_gc (dst->display, dst, gc); release (dst); return CAIRO_STATUS_SUCCESS; } static int _render_operator (cairo_operator_t op) { switch (op) { case CAIRO_OPERATOR_CLEAR: return PictOpClear; case CAIRO_OPERATOR_SOURCE: return PictOpSrc; case CAIRO_OPERATOR_OVER: return PictOpOver; case CAIRO_OPERATOR_IN: return PictOpIn; case CAIRO_OPERATOR_OUT: return PictOpOut; case CAIRO_OPERATOR_ATOP: return PictOpAtop; case CAIRO_OPERATOR_DEST: return PictOpDst; case CAIRO_OPERATOR_DEST_OVER: return PictOpOverReverse; case CAIRO_OPERATOR_DEST_IN: return PictOpInReverse; case CAIRO_OPERATOR_DEST_OUT: return PictOpOutReverse; case CAIRO_OPERATOR_DEST_ATOP: return PictOpAtopReverse; case CAIRO_OPERATOR_XOR: return PictOpXor; case CAIRO_OPERATOR_ADD: return PictOpAdd; case CAIRO_OPERATOR_SATURATE: return PictOpSaturate; case CAIRO_OPERATOR_MULTIPLY: return PictOpMultiply; case CAIRO_OPERATOR_SCREEN: return PictOpScreen; case CAIRO_OPERATOR_OVERLAY: return PictOpOverlay; case CAIRO_OPERATOR_DARKEN: return PictOpDarken; case CAIRO_OPERATOR_LIGHTEN: return PictOpLighten; case CAIRO_OPERATOR_COLOR_DODGE: return PictOpColorDodge; case CAIRO_OPERATOR_COLOR_BURN: return PictOpColorBurn; case CAIRO_OPERATOR_HARD_LIGHT: return PictOpHardLight; case CAIRO_OPERATOR_SOFT_LIGHT: return PictOpSoftLight; case CAIRO_OPERATOR_DIFFERENCE: return PictOpDifference; case CAIRO_OPERATOR_EXCLUSION: return PictOpExclusion; case CAIRO_OPERATOR_HSL_HUE: return PictOpHSLHue; case CAIRO_OPERATOR_HSL_SATURATION: return PictOpHSLSaturation; case CAIRO_OPERATOR_HSL_COLOR: return PictOpHSLColor; case CAIRO_OPERATOR_HSL_LUMINOSITY: return PictOpHSLLuminosity; default: ASSERT_NOT_REACHED; return PictOpOver; } } static cairo_bool_t fill_reduces_to_source (cairo_operator_t op, const cairo_color_t *color, cairo_xlib_surface_t *dst) { if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) { if (op == CAIRO_OPERATOR_OVER) return TRUE; if (op == CAIRO_OPERATOR_ADD) return (dst->base.content & CAIRO_CONTENT_COLOR) == 0; } return FALSE; } static cairo_int_status_t fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int num_rects) { cairo_xlib_surface_t *dst = abstract_surface; XRenderColor render_color; int i; //X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); if (fill_reduces_to_source (op, color, dst)) op = CAIRO_OPERATOR_SOURCE; if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (op == CAIRO_OPERATOR_SOURCE) status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects); return status; } render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; _cairo_xlib_surface_ensure_picture (dst); if (num_rects == 1) { /* Take advantage of the protocol compaction that libXrender performs * to amalgamate sequences of XRenderFillRectangle(). */ XRenderFillRectangle (dst->dpy, _render_operator (op), dst->picture, &render_color, rects->x, rects->y, rects->width, rects->height); } else { XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *xrects = stack_xrects; if (num_rects > ARRAY_LENGTH (stack_xrects)) { xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); if (unlikely (xrects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < num_rects; i++) { xrects[i].x = rects[i].x; xrects[i].y = rects[i].y; xrects[i].width = rects[i].width; xrects[i].height = rects[i].height; } XRenderFillRectangles (dst->dpy, _render_operator (op), dst->picture, &render_color, xrects, num_rects); if (xrects != stack_xrects) free (xrects); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t fill_boxes (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_boxes_t *boxes) { cairo_xlib_surface_t *dst = abstract_surface; XRenderColor render_color; if (fill_reduces_to_source (op, color, dst)) op = CAIRO_OPERATOR_SOURCE; if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (op == CAIRO_OPERATOR_SOURCE) status = _cairo_xlib_core_fill_boxes (dst, color, boxes); return status; } render_color.red = color->red_short; render_color.green = color->green_short; render_color.blue = color->blue_short; render_color.alpha = color->alpha_short; _cairo_xlib_surface_ensure_picture (dst); if (boxes->num_boxes == 1) { int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); /* Take advantage of the protocol compaction that libXrender performs * to amalgamate sequences of XRenderFillRectangle(). */ XRenderFillRectangle (dst->dpy, _render_operator (op), dst->picture, &render_color, x1, y1, x2 - x1, y2 - y1); } else { XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *xrects = stack_xrects; struct _cairo_boxes_chunk *chunk; int i, j; if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) { xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); if (unlikely (xrects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } j = 0; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); xrects[j].x = x1; xrects[j].y = y1; xrects[j].width = x2 - x1; xrects[j].height = y2 - y1; j++; } } XRenderFillRectangles (dst->dpy, _render_operator (op), dst->picture, &render_color, xrects, j); if (xrects != stack_xrects) free (xrects); } return CAIRO_STATUS_SUCCESS; } #if 0 check_composite () operation = _categorize_composite_operation (dst, op, src_pattern, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported operation"); //X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); operation = _recategorize_composite_operation (dst, op, src, &src_attr, mask_pattern != NULL); if (operation == DO_UNSUPPORTED) { status = UNSUPPORTED ("unsupported operation"); goto BAIL; } #endif static cairo_int_status_t composite (void *abstract_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; op = _render_operator (op); _cairo_xlib_surface_ensure_picture (dst); if (abstract_mask) { cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; XRenderComposite (dst->dpy, op, src->picture, mask->picture, dst->picture, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); } else { XRenderComposite (dst->dpy, op, src->picture, 0, dst->picture, src_x, src_y, 0, 0, dst_x, dst_y, width, height); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t lerp (void *abstract_dst, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height) { cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; _cairo_xlib_surface_ensure_picture (dst); XRenderComposite (dst->dpy, PictOpOutReverse, mask->picture, None, dst->picture, mask_x, mask_y, 0, 0, dst_x, dst_y, width, height); XRenderComposite (dst->dpy, PictOpAdd, src->picture, mask->picture, dst->picture, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_boxes (void *abstract_dst, cairo_operator_t op, cairo_surface_t *abstract_src, cairo_surface_t *abstract_mask, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, cairo_boxes_t *boxes, const cairo_rectangle_int_t *extents) { cairo_xlib_surface_t *dst = abstract_dst; Picture src = ((cairo_xlib_source_t *)abstract_src)->picture; Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0; XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; XRectangle *rects = stack_rects; struct _cairo_boxes_chunk *chunk; int i, j; op = _render_operator (op); _cairo_xlib_surface_ensure_picture (dst); if (boxes->num_boxes == 1) { int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); XRenderComposite (dst->dpy, op, src, mask, dst->picture, x1 + src_x, y1 + src_y, x1 + mask_x, y1 + mask_y, x1 - dst_x, y1 - dst_y, x2 - x1, y2 - y1); return CAIRO_STATUS_SUCCESS; } if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); if (unlikely (rects == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } j = 0; for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); rects[j].x = x1 - dst_x; rects[j].y = y1 - dst_y; rects[j].width = x2 - x1; rects[j].height = y2 - y1; j++; } } assert (j == boxes->num_boxes); XRenderSetPictureClipRectangles (dst->dpy, dst->picture, 0, 0, rects, j); if (rects != stack_rects) free (rects); XRenderComposite (dst->dpy, op, src, mask, dst->picture, extents->x + src_x, extents->y + src_y, extents->x + mask_x, extents->y + mask_y, extents->x - dst_x, extents->y - dst_y, extents->width, extents->height); set_clip_region (dst, NULL); return CAIRO_STATUS_SUCCESS; } /* font rendering */ void _cairo_xlib_font_close (cairo_xlib_font_t *priv) { cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key; int i; /* XXX All I really want is to do is zap my glyphs... */ _cairo_scaled_font_reset_cache (priv->font); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_t *info; info = &priv->glyphset[i]; if (info->glyphset) XRenderFreeGlyphSet (display->display, info->glyphset); } /* XXX locking */ cairo_list_del (&priv->link); cairo_list_del (&priv->base.link); free (priv); } static void _cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private, cairo_scaled_font_t *font) { cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private; cairo_status_t status; cairo_xlib_display_t *display; int i; cairo_list_del (&priv->base.link); cairo_list_del (&priv->link); status = _cairo_xlib_display_acquire (priv->device, &display); if (status) goto BAIL; for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_t *info; info = &priv->glyphset[i]; if (info->glyphset) XRenderFreeGlyphSet (display->display, info->glyphset); } cairo_device_release (&display->base); BAIL: cairo_device_destroy (&display->base); free (priv); } static cairo_xlib_font_t * _cairo_xlib_font_create (cairo_xlib_display_t *display, cairo_scaled_font_t *font) { cairo_xlib_font_t *priv; int i; priv = malloc (sizeof (cairo_xlib_font_t)); if (unlikely (priv == NULL)) return NULL; _cairo_scaled_font_attach_private (font, &priv->base, display, _cairo_xlib_font_fini); priv->device = cairo_device_reference (&display->base); priv->font = font; cairo_list_add (&priv->link, &display->fonts); for (i = 0; i < NUM_GLYPHSETS; i++) { cairo_xlib_font_glyphset_t *info = &priv->glyphset[i]; switch (i) { case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break; case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break; case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break; default: ASSERT_NOT_REACHED; break; } info->xrender_format = NULL; info->glyphset = None; info->to_free.count = 0; } return priv; } static int _cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) { if (format == CAIRO_FORMAT_A8) return GLYPHSET_INDEX_A8; if (format == CAIRO_FORMAT_A1) return GLYPHSET_INDEX_A1; assert (format == CAIRO_FORMAT_ARGB32); return GLYPHSET_INDEX_ARGB32; } static inline cairo_xlib_font_t * _cairo_xlib_font_get (const cairo_xlib_display_t *display, cairo_scaled_font_t *font) { return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display); } typedef struct { cairo_scaled_glyph_private_t base; cairo_xlib_font_glyphset_t *glyphset; } cairo_xlib_glyph_private_t; static void _cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, cairo_scaled_glyph_t *glyph, cairo_scaled_font_t *font) { cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private; if (! font->finished) { cairo_xlib_font_t *font_private; struct _cairo_xlib_font_glyphset_free_glyphs *to_free; cairo_xlib_font_glyphset_t *info; font_private = _cairo_xlib_font_get (glyph_private->key, font); assert (font_private); info = priv->glyphset; to_free = &info->to_free; if (to_free->count == ARRAY_LENGTH (to_free->indices)) { cairo_xlib_display_t *display; if (_cairo_xlib_display_acquire (font_private->device, &display) == CAIRO_STATUS_SUCCESS) { XRenderFreeGlyphs (display->display, info->glyphset, to_free->indices, to_free->count); cairo_device_release (&display->base); } to_free->count = 0; } to_free->indices[to_free->count++] = _cairo_scaled_glyph_index (glyph); } cairo_list_del (&glyph_private->link); free (glyph_private); } static cairo_status_t _cairo_xlib_glyph_attach (cairo_xlib_display_t *display, cairo_scaled_glyph_t *glyph, cairo_xlib_font_glyphset_t *info) { cairo_xlib_glyph_private_t *priv; priv = malloc (sizeof (*priv)); if (unlikely (priv == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_scaled_glyph_attach_private (glyph, &priv->base, display, _cairo_xlib_glyph_fini); priv->glyphset = info; glyph->dev_private = info; glyph->dev_private_key = display; return CAIRO_STATUS_SUCCESS; } static cairo_xlib_font_glyphset_t * _cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display, cairo_scaled_font_t *font, cairo_format_t format) { cairo_xlib_font_t *priv; cairo_xlib_font_glyphset_t *info; int glyphset_index; glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); priv = _cairo_xlib_font_get (display, font); if (priv == NULL) { priv = _cairo_xlib_font_create (display, font); if (priv == NULL) return NULL; } info = &priv->glyphset[glyphset_index]; if (info->glyphset == None) { info->xrender_format = _cairo_xlib_display_get_xrender_format (display, info->format); info->glyphset = XRenderCreateGlyphSet (display->display, info->xrender_format); } return info; } static cairo_bool_t has_pending_free_glyph (cairo_xlib_font_glyphset_t *info, unsigned long glyph_index) { struct _cairo_xlib_font_glyphset_free_glyphs *to_free; int i; to_free = &info->to_free; for (i = 0; i < to_free->count; i++) { if (to_free->indices[i] == glyph_index) { to_free->count--; memmove (&to_free->indices[i], &to_free->indices[i+1], (to_free->count - i) * sizeof (to_free->indices[0])); return TRUE; } } return FALSE; } static cairo_xlib_font_glyphset_t * find_pending_free_glyph (cairo_xlib_display_t *display, cairo_scaled_font_t *font, unsigned long glyph_index, cairo_image_surface_t *surface) { cairo_xlib_font_t *priv; int i; priv = _cairo_xlib_font_get (display, font); if (priv == NULL) return NULL; if (surface != NULL) { i = _cairo_xlib_get_glyphset_index_for_format (surface->format); if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) return &priv->glyphset[i]; } else { for (i = 0; i < NUM_GLYPHSETS; i++) { if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) return &priv->glyphset[i]; } } return NULL; } static cairo_status_t _cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, cairo_scaled_font_t *font, cairo_scaled_glyph_t **pscaled_glyph) { XGlyphInfo glyph_info; unsigned long glyph_index; unsigned char *data; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *glyph = *pscaled_glyph; cairo_image_surface_t *glyph_surface = glyph->surface; cairo_bool_t already_had_glyph_surface; cairo_xlib_font_glyphset_t *info; glyph_index = _cairo_scaled_glyph_index (glyph); /* check to see if we have a pending XRenderFreeGlyph for this glyph */ info = find_pending_free_glyph (display, font, glyph_index, glyph_surface); if (info != NULL) return _cairo_xlib_glyph_attach (display, glyph, info); if (glyph_surface == NULL) { status = _cairo_scaled_glyph_lookup (font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, pscaled_glyph); if (unlikely (status)) return status; glyph = *pscaled_glyph; glyph_surface = glyph->surface; already_had_glyph_surface = FALSE; } else { already_had_glyph_surface = TRUE; } info = _cairo_xlib_font_get_glyphset_info_for_format (display, font, glyph_surface->format); #if 0 /* If the glyph surface has zero height or width, we create * a clear 1x1 surface, to avoid various X server bugs. */ if (glyph_surface->width == 0 || glyph_surface->height == 0) { cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (info->format, 1, 1); status = tmp_surface->status; if (unlikely (status)) goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; glyph_surface = (cairo_image_surface_t *) tmp_surface; } #endif /* If the glyph format does not match the font format, then we * create a temporary surface for the glyph image with the font's * format. */ if (glyph_surface->format != info->format) { cairo_surface_pattern_t pattern; cairo_surface_t *tmp_surface; tmp_surface = cairo_image_surface_create (info->format, glyph_surface->width, glyph_surface->height); status = tmp_surface->status; if (unlikely (status)) goto BAIL; tmp_surface->device_transform = glyph_surface->base.device_transform; tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); status = _cairo_surface_paint (tmp_surface, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); glyph_surface = (cairo_image_surface_t *) tmp_surface; if (unlikely (status)) goto BAIL; } /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); glyph_info.width = glyph_surface->width; glyph_info.height = glyph_surface->height; glyph_info.xOff = glyph->x_advance; glyph_info.yOff = glyph->y_advance; data = glyph_surface->data; /* flip formats around */ switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) { case GLYPHSET_INDEX_A1: /* local bitmaps are always stored with bit == byte */ if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) { int c = glyph_surface->stride * glyph_surface->height; unsigned char *d; unsigned char *new, *n; new = malloc (c); if (!new) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; d = data; do { char b = *d++; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *n++ = b; } while (--c); data = new; } break; case GLYPHSET_INDEX_A8: break; case GLYPHSET_INDEX_ARGB32: if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) { unsigned int c = glyph_surface->stride * glyph_surface->height / 4; const uint32_t *d; uint32_t *new, *n; new = malloc (4 * c); if (unlikely (new == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } n = new; d = (uint32_t *) data; do { *n++ = bswap_32 (*d); d++; } while (--c); data = (uint8_t *) new; } break; default: ASSERT_NOT_REACHED; break; } /* XXX assume X server wants pixman padding. Xft assumes this as well */ XRenderAddGlyphs (display->display, info->glyphset, &glyph_index, &glyph_info, 1, (char *) data, glyph_surface->stride * glyph_surface->height); if (data != glyph_surface->data) free (data); status = _cairo_xlib_glyph_attach (display, glyph, info); BAIL: if (glyph_surface != glyph->surface) cairo_surface_destroy (&glyph_surface->base); /* if the scaled glyph didn't already have a surface attached * to it, release the created surface now that we have it * uploaded to the X server. If the surface has already been * there (eg. because image backend requested it), leave it in * the cache */ if (!already_had_glyph_surface) _cairo_scaled_glyph_set_surface (glyph, font, NULL); return status; } typedef void (*cairo_xrender_composite_text_func_t) (Display *dpy, int op, Picture src, Picture dst, _Xconst XRenderPictFormat *maskFormat, int xSrc, int ySrc, int xDst, int yDst, _Xconst XGlyphElt8 *elts, int nelt); /* Build a struct of the same size of #cairo_glyph_t that can be used both as * an input glyph with double coordinates, and as "working" glyph with * integer from-current-point offsets. */ typedef union { cairo_glyph_t d; unsigned long index; struct { unsigned long index; int x; int y; } i; } cairo_xlib_glyph_t; /* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); /* Start a new element for the first glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs * (Xrender limits each element to 252 glyphs, we limit them to 128) * * These same conditions need to be mirrored between * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks */ #define _start_new_glyph_elt(count, glyph) \ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) static cairo_status_t _emit_glyphs_chunk (cairo_xlib_display_t *display, cairo_xlib_surface_t *dst, int dst_x, int dst_y, cairo_xlib_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *font, cairo_bool_t use_mask, cairo_operator_t op, cairo_xlib_source_t *src, int src_x, int src_y, /* info for this chunk */ int num_elts, int width, cairo_xlib_font_glyphset_t *info) { /* Which XRenderCompositeText function to use */ cairo_xrender_composite_text_func_t composite_text_func; int size; /* Element buffer stuff */ XGlyphElt8 *elts; XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; /* Reuse the input glyph array for output char generation */ char *char8 = (char *) glyphs; unsigned short *char16 = (unsigned short *) glyphs; unsigned int *char32 = (unsigned int *) glyphs; int i; int nelt; /* Element index */ int n; /* Num output glyphs in current element */ int j; /* Num output glyphs so far */ switch (width) { case 1: /* don't cast the 8-variant, to catch possible mismatches */ composite_text_func = XRenderCompositeText8; size = sizeof (char); break; case 2: composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; size = sizeof (unsigned short); break; default: case 4: composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; size = sizeof (unsigned int); } /* Allocate element array */ if (num_elts <= ARRAY_LENGTH (stack_elts)) { elts = stack_elts; } else { elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); if (unlikely (elts == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Fill them in */ nelt = 0; n = 0; j = 0; for (i = 0; i < num_glyphs; i++) { /* Start a new element for first output glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs. * * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() */ if (_start_new_glyph_elt (j, &glyphs[i])) { if (j) { elts[nelt].nchars = n; nelt++; n = 0; } elts[nelt].chars = char8 + size * j; elts[nelt].glyphset = info->glyphset; elts[nelt].xOff = glyphs[i].i.x; elts[nelt].yOff = glyphs[i].i.y; } switch (width) { case 1: char8 [j] = (char) glyphs[i].index; break; case 2: char16[j] = (unsigned short) glyphs[i].index; break; default: case 4: char32[j] = (unsigned int) glyphs[i].index; break; } n++; j++; } if (n) { elts[nelt].nchars = n; nelt++; } /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the * expected number of xGlyphElts. */ assert (nelt == num_elts); composite_text_func (display->display, op, src->picture, dst->picture, use_mask ? info->xrender_format : NULL, src_x + elts[0].xOff + dst_x, src_y + elts[0].yOff + dst_y, elts[0].xOff, elts[0].yOff, (XGlyphElt8 *) elts, nelt); if (elts != stack_elts) free (elts); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t check_composite_glyphs (const cairo_composite_rectangles_t *extents, cairo_scaled_font_t *font, cairo_glyph_t *glyphs, int *num_glyphs) { cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface; cairo_xlib_display_t *display = dst->display; int max_request_size, size; TRACE ((stderr, "%s\n", __FUNCTION__)); if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) return CAIRO_INT_STATUS_UNSUPPORTED; /* The glyph coordinates must be representable in an int16_t. * When possible, they will be expressed as an offset from the * previous glyph, otherwise they will be an offset from the * surface origin. If we can't guarantee this to be possible, * fallback. */ if (extents->bounded.x + extents->bounded.width > INT16_MAX || extents->bounded.y + extents->bounded.height> INT16_MAX || extents->bounded.x < INT16_MIN || extents->bounded.y < INT16_MIN) { return CAIRO_INT_STATUS_UNSUPPORTED; } /* Approximate the size of the largest glyph and fallback if we can not * upload it to the xserver. */ size = ceil (font->max_scale); size = 4 * size * size; max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) : XMaxRequestSize (display->display)) * 4 - sz_xRenderAddGlyphsReq - sz_xGlyphInfo - 8; if (size >= max_request_size) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } /* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have * enough room for padding */ #define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) static cairo_int_status_t composite_glyphs (void *surface, cairo_operator_t op, cairo_surface_t *_src, int src_x, int src_y, int dst_x, int dst_y, cairo_composite_glyphs_info_t *info) { cairo_xlib_surface_t *dst = surface; cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs; cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src; cairo_xlib_display_t *display = dst->display; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_scaled_glyph_t *glyph; cairo_fixed_t x = dst_x, y = dst_y; cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info; unsigned long max_index = 0; int width = 1; int num_elts = 0; int num_out_glyphs = 0; int num_glyphs = info->num_glyphs; int max_request_size = XMaxRequestSize (display->display) * 4 - MAX (sz_xRenderCompositeGlyphs8Req, MAX(sz_xRenderCompositeGlyphs16Req, sz_xRenderCompositeGlyphs32Req)); int request_size = 0; int i; op = _render_operator (op), _cairo_xlib_surface_ensure_picture (dst); for (i = 0; i < num_glyphs; i++) { int this_x, this_y; int old_width; status = _cairo_scaled_glyph_lookup (info->font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &glyph); if (unlikely (status)) return status; this_x = _cairo_lround (glyphs[i].d.x); this_y = _cairo_lround (glyphs[i].d.y); /* Send unsent glyphs to the server */ if (glyph->dev_private_key != display) { status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph); if (unlikely (status)) return status; } this_glyphset_info = glyph->dev_private; if (!glyphset) glyphset = this_glyphset_info; /* The invariant here is that we can always flush the glyphs * accumulated before this one, using old_width, and they * would fit in the request. */ old_width = width; /* Update max glyph index */ if (glyphs[i].index > max_index) { max_index = glyphs[i].index; if (max_index >= 65536) width = 4; else if (max_index >= 256) width = 2; if (width != old_width) request_size += (width - old_width) * num_out_glyphs; } /* If we will pass the max request size by adding this glyph, * flush current glyphs. Note that we account for a * possible element being added below. * * Also flush if changing glyphsets, as Xrender limits one mask * format per request, so we can either break up, or use a * wide-enough mask format. We do the former. One reason to * prefer the latter is the fact that Xserver ADDs all glyphs * to the mask first, and then composes that to final surface, * though it's not a big deal. * * If the glyph has a coordinate which cannot be represented * as a 16-bit offset from the previous glyph, flush the * current chunk. The current glyph will be the first one in * the next chunk, thus its coordinates will be an offset from * the destination origin. This offset is guaranteed to be * representable as 16-bit offset (otherwise we would have * fallen back). */ if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || this_x - x > INT16_MAX || this_x - x < INT16_MIN || this_y - y > INT16_MAX || this_y - y < INT16_MIN || (this_glyphset_info != glyphset)) { status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, glyphs, i, info->font, info->use_mask, op, src, src_x, src_y, num_elts, old_width, glyphset); if (unlikely (status)) return status; glyphs += i; num_glyphs -= i; i = 0; max_index = glyphs[i].index; width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; request_size = 0; num_elts = 0; num_out_glyphs = 0; x = y = 0; glyphset = this_glyphset_info; } /* Convert absolute glyph position to relative-to-current-point * position */ glyphs[i].i.x = this_x - x; glyphs[i].i.y = this_y - y; /* Start a new element for the first glyph, * or for any glyph that has unexpected position, * or if current element has too many glyphs. * * These same conditions are mirrored in _emit_glyphs_chunk(). */ if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { num_elts++; request_size += _cairo_sz_xGlyphElt; } /* adjust current-position */ x = this_x + glyph->x_advance; y = this_y + glyph->y_advance; num_out_glyphs++; request_size += width; } if (num_elts) { status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, glyphs, i, info->font, info->use_mask, op, src, src_x, src_y, num_elts, width, glyphset); } return status; } const cairo_compositor_t * _cairo_xlib_mask_compositor_get (void) { static cairo_mask_compositor_t compositor; if (compositor.base.delegate == NULL) { _cairo_mask_compositor_init (&compositor, _cairo_xlib_fallback_compositor_get ()); compositor.acquire = acquire; compositor.release = release; compositor.set_clip_region = set_clip_region; compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; compositor.draw_image_boxes = draw_image_boxes; compositor.fill_rectangles = fill_rectangles; compositor.fill_boxes = fill_boxes; compositor.copy_boxes = copy_boxes; compositor.check_composite = check_composite; compositor.composite = composite; //compositor.check_composite_boxes = check_composite_boxes; compositor.composite_boxes = composite_boxes; compositor.check_composite_glyphs = check_composite_glyphs; compositor.composite_glyphs = composite_glyphs; } return &compositor.base; } #define CAIRO_FIXED_16_16_MIN -32768 #define CAIRO_FIXED_16_16_MAX 32767 static cairo_bool_t line_exceeds_16_16 (const cairo_line_t *line) { return line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); } static void project_line_x_onto_16_16 (const cairo_line_t *line, cairo_fixed_t top, cairo_fixed_t bottom, XLineFixed *out) { cairo_point_double_t p1, p2; double m; p1.x = _cairo_fixed_to_double (line->p1.x); p1.y = _cairo_fixed_to_double (line->p1.y); p2.x = _cairo_fixed_to_double (line->p2.x); p2.y = _cairo_fixed_to_double (line->p2.y); m = (p2.x - p1.x) / (p2.y - p1.y); out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); } #if 0 static cairo_int_status_T check_composite_trapezoids () { operation = _categorize_composite_operation (dst, op, pattern, TRUE); if (operation == DO_UNSUPPORTED) return UNSUPPORTED ("unsupported operation"); operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE); if (operation == DO_UNSUPPORTED) { status = UNSUPPORTED ("unsupported operation"); goto BAIL; } } #endif static cairo_int_status_t composite_traps (void *abstract_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_traps_t *traps) { cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_display_t *display = dst->display; cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; XRenderPictFormat *pict_format; XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; XTrapezoid *xtraps = xtraps_stack; int dx, dy; int i; //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); if (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) { op = CAIRO_OPERATOR_SOURCE; } pict_format = _cairo_xlib_display_get_xrender_format (display, antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) { xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid)); if (unlikely (xtraps == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } dx = -dst_x << 16; dy = -dst_y << 16; for (i = 0; i < traps->num_traps; i++) { cairo_trapezoid_t *t = &traps->traps[i]; /* top/bottom will be clamped to surface bounds */ xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy; xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy; /* However, all the other coordinates will have been left untouched so * as not to introduce numerical error. Recompute them if they * exceed the 16.16 limits. */ if (unlikely (line_exceeds_16_16 (&t->left))) { project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &xtraps[i].left); xtraps[i].left.p1.x += dx; xtraps[i].left.p2.x += dx; xtraps[i].left.p1.y = xtraps[i].top; xtraps[i].left.p2.y = xtraps[i].bottom; } else { xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx; xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy; xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx; xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy; } if (unlikely (line_exceeds_16_16 (&t->right))) { project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &xtraps[i].right); xtraps[i].right.p1.x += dx; xtraps[i].right.p2.x += dx; xtraps[i].right.p1.y = xtraps[i].top; xtraps[i].right.p2.y = xtraps[i].bottom; } else { xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx; xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy; xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx; xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy; } } if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); } else { src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); } src_x += dst_x; src_y += dst_y; _cairo_xlib_surface_ensure_picture (dst); _cairo_xlib_surface_set_precision (dst, antialias); XRenderCompositeTrapezoids (dst->dpy, _render_operator (op), src->picture, dst->picture, pict_format, src_x, src_y, xtraps, traps->num_traps); if (xtraps != xtraps_stack) free (xtraps); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t composite_tristrip (void *abstract_dst, cairo_operator_t op, cairo_surface_t *abstract_src, int src_x, int src_y, int dst_x, int dst_y, const cairo_rectangle_int_t *extents, cairo_antialias_t antialias, cairo_tristrip_t *strip) { cairo_xlib_surface_t *dst = abstract_dst; cairo_xlib_display_t *display = dst->display; cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; XRenderPictFormat *pict_format; XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)]; XPointFixed *points = points_stack; int dx, dy; int i; //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); pict_format = _cairo_xlib_display_get_xrender_format (display, antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); if (strip->num_points > ARRAY_LENGTH (points_stack)) { points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } dx = -dst_x << 16; dy = -dst_y << 16; for (i = 0; i < strip->num_points; i++) { cairo_point_t *p = &strip->points[i]; points[i].x = _cairo_fixed_to_16_16(p->x) + dx; points[i].y = _cairo_fixed_to_16_16(p->y) + dy; } src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x; src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y; _cairo_xlib_surface_ensure_picture (dst); _cairo_xlib_surface_set_precision (dst, antialias); XRenderCompositeTriStrip (dst->dpy, _render_operator (op), src->picture, dst->picture, pict_format, src_x, src_y, points, strip->num_points); if (points != points_stack) free (points); return CAIRO_STATUS_SUCCESS; } const cairo_compositor_t * _cairo_xlib_traps_compositor_get (void) { static cairo_traps_compositor_t compositor; if (compositor.base.delegate == NULL) { _cairo_traps_compositor_init (&compositor, _cairo_xlib_mask_compositor_get ()); compositor.acquire = acquire; compositor.release = release; compositor.set_clip_region = set_clip_region; compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; compositor.draw_image_boxes = draw_image_boxes; compositor.copy_boxes = copy_boxes; compositor.fill_boxes = fill_boxes; compositor.check_composite = check_composite; compositor.composite = composite; compositor.lerp = lerp; //compositor.check_composite_boxes = check_composite_boxes; compositor.composite_boxes = composite_boxes; //compositor.check_composite_traps = check_composite_traps; compositor.composite_traps = composite_traps; //compositor.check_composite_tristrip = check_composite_tristrip; compositor.composite_tristrip = composite_tristrip; compositor.check_composite_glyphs = check_composite_glyphs; compositor.composite_glyphs = composite_glyphs; } return &compositor.base; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-screen.c000066400000000000000000000302341271037650300257540ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Partially on code from xftdpy.c * * Copyright © 2000 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" #include "cairo-fontconfig-private.h" static int parse_boolean (const char *v) { char c0, c1; c0 = *v; if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') return 1; if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') return 0; if (c0 == 'o') { c1 = v[1]; if (c1 == 'n' || c1 == 'N') return 1; if (c1 == 'f' || c1 == 'F') return 0; } return -1; } static cairo_bool_t get_boolean_default (Display *dpy, const char *option, cairo_bool_t *value) { char *v; int i; v = XGetDefault (dpy, "Xft", option); if (v) { i = parse_boolean (v); if (i >= 0) { *value = i; return TRUE; } } return FALSE; } static cairo_bool_t get_integer_default (Display *dpy, const char *option, int *value) { char *v, *e; v = XGetDefault (dpy, "Xft", option); if (v) { #if CAIRO_HAS_FC_FONT if (FcNameConstant ((FcChar8 *) v, value)) return TRUE; #endif *value = strtol (v, &e, 0); if (e != v) return TRUE; } return FALSE; } static void _cairo_xlib_init_screen_font_options (Display *dpy, cairo_xlib_screen_t *info) { cairo_bool_t xft_hinting; cairo_bool_t xft_antialias; int xft_hintstyle; int xft_rgba; int xft_lcdfilter; cairo_antialias_t antialias; cairo_subpixel_order_t subpixel_order; cairo_lcd_filter_t lcd_filter; cairo_hint_style_t hint_style; if (!get_boolean_default (dpy, "antialias", &xft_antialias)) xft_antialias = TRUE; if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) { /* -1 is an non-existant Fontconfig constant used to differentiate * the case when no lcdfilter property is available. */ xft_lcdfilter = -1; } if (!get_boolean_default (dpy, "hinting", &xft_hinting)) xft_hinting = TRUE; if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle)) xft_hintstyle = FC_HINT_FULL; if (!get_integer_default (dpy, "rgba", &xft_rgba)) { cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device; xft_rgba = FC_RGBA_UNKNOWN; #if RENDER_MAJOR > 0 || RENDER_MINOR >= 6 if (display->render_major > 0 || display->render_minor >= 6) { int render_order = XRenderQuerySubpixelOrder (dpy, XScreenNumberOfScreen (info->screen)); switch (render_order) { default: case SubPixelUnknown: xft_rgba = FC_RGBA_UNKNOWN; break; case SubPixelHorizontalRGB: xft_rgba = FC_RGBA_RGB; break; case SubPixelHorizontalBGR: xft_rgba = FC_RGBA_BGR; break; case SubPixelVerticalRGB: xft_rgba = FC_RGBA_VRGB; break; case SubPixelVerticalBGR: xft_rgba = FC_RGBA_VBGR; break; case SubPixelNone: xft_rgba = FC_RGBA_NONE; break; } } #endif } if (xft_hinting) { switch (xft_hintstyle) { case FC_HINT_NONE: hint_style = CAIRO_HINT_STYLE_NONE; break; case FC_HINT_SLIGHT: hint_style = CAIRO_HINT_STYLE_SLIGHT; break; case FC_HINT_MEDIUM: hint_style = CAIRO_HINT_STYLE_MEDIUM; break; case FC_HINT_FULL: hint_style = CAIRO_HINT_STYLE_FULL; break; default: hint_style = CAIRO_HINT_STYLE_DEFAULT; } } else { hint_style = CAIRO_HINT_STYLE_NONE; } switch (xft_rgba) { case FC_RGBA_RGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; break; case FC_RGBA_BGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; break; case FC_RGBA_VRGB: subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; break; case FC_RGBA_VBGR: subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; break; case FC_RGBA_UNKNOWN: case FC_RGBA_NONE: default: subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; } switch (xft_lcdfilter) { case FC_LCD_NONE: lcd_filter = CAIRO_LCD_FILTER_NONE; break; case FC_LCD_DEFAULT: lcd_filter = CAIRO_LCD_FILTER_FIR5; break; case FC_LCD_LIGHT: lcd_filter = CAIRO_LCD_FILTER_FIR3; break; case FC_LCD_LEGACY: lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; break; default: lcd_filter = CAIRO_LCD_FILTER_DEFAULT; break; } if (xft_antialias) { if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) antialias = CAIRO_ANTIALIAS_GRAY; else antialias = CAIRO_ANTIALIAS_SUBPIXEL; } else { antialias = CAIRO_ANTIALIAS_NONE; } cairo_font_options_set_hint_style (&info->font_options, hint_style); cairo_font_options_set_antialias (&info->font_options, antialias); cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order); _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter); cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); } void _cairo_xlib_screen_destroy (cairo_xlib_display_t *display, cairo_xlib_screen_t *info) { Display *dpy; int i; dpy = display->display; while (! cairo_list_is_empty (&info->surfaces)) { cairo_xlib_surface_t *surface; surface = cairo_list_first_entry (&info->surfaces, cairo_xlib_surface_t, link); cairo_surface_finish (&surface->base); } for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if (info->gc_depths[i] != 0) { XFreeGC (dpy, info->gc[i]); info->gc_depths[i] = 0; } } while (! cairo_list_is_empty (&info->visuals)) { _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, cairo_xlib_visual_info_t, link)); } cairo_list_del (&info->link); free (info); } cairo_status_t _cairo_xlib_screen_get (Display *dpy, Screen *screen, cairo_xlib_screen_t **out) { cairo_xlib_display_t *display; cairo_device_t *device; cairo_xlib_screen_t *info; cairo_status_t status; device = _cairo_xlib_device_create (dpy); status = device->status; if (unlikely (status)) goto CLEANUP_DEVICE; status = _cairo_xlib_display_acquire (device, &display); if (unlikely (status)) goto CLEANUP_DEVICE; info = _cairo_xlib_display_get_screen (display, screen); if (info != NULL) { *out = info; goto CLEANUP_DISPLAY; } info = malloc (sizeof (cairo_xlib_screen_t)); if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DISPLAY; } info->device = device; info->screen = screen; info->has_font_options = FALSE; memset (info->gc_depths, 0, sizeof (info->gc_depths)); memset (info->gc, 0, sizeof (info->gc)); cairo_list_init (&info->surfaces); cairo_list_init (&info->visuals); cairo_list_add (&info->link, &display->screens); *out = info; CLEANUP_DISPLAY: cairo_device_release (&display->base); CLEANUP_DEVICE: cairo_device_destroy (device); return status; } GC _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, int depth, Drawable drawable) { GC gc = NULL; int i; for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if (info->gc_depths[i] == depth) { info->gc_depths[i] = 0; gc = info->gc[i]; break; } } if (gc == NULL) { XGCValues gcv; gcv.graphics_exposures = False; gcv.fill_style = FillTiled; gc = XCreateGC (display->display, drawable, GCGraphicsExposures | GCFillStyle, &gcv); } return gc; } void _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, int depth, GC gc) { int i; for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { if (info->gc_depths[i] == 0) break; } if (i == ARRAY_LENGTH (info->gc)) { /* perform random substitution to ensure fair caching over depths */ i = rand () % ARRAY_LENGTH (info->gc); XFreeGC(display->display, info->gc[i]); } info->gc[i] = gc; info->gc_depths[i] = depth; } cairo_status_t _cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, Visual *v, cairo_xlib_visual_info_t **out) { cairo_xlib_visual_info_t *visual; cairo_status_t status; cairo_list_foreach_entry (visual, cairo_xlib_visual_info_t, &info->visuals, link) { if (visual->visualid == v->visualid) { *out = visual; return CAIRO_STATUS_SUCCESS; } } status = _cairo_xlib_visual_info_create (display->display, XScreenNumberOfScreen (info->screen), v->visualid, &visual); if (unlikely (status)) return status; cairo_list_add (&visual->link, &info->visuals); *out = visual; return CAIRO_STATUS_SUCCESS; } cairo_font_options_t * _cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) { if (! info->has_font_options) { _cairo_font_options_init_default (&info->font_options); _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON); if (info->screen != NULL) { cairo_xlib_display_t *display; if (! _cairo_xlib_display_acquire (info->device, &display)) { _cairo_xlib_init_screen_font_options (display->display, info); cairo_device_release (&display->base); } } info->has_font_options = TRUE; } return &info->font_options; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-source.c000066400000000000000000001054771271037650300260110ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-inline.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-observer-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-inline.h" #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ static cairo_xlib_surface_t * unwrap_source (const cairo_surface_pattern_t *pattern) { cairo_rectangle_int_t limits; return (cairo_xlib_surface_t *)_cairo_pattern_get_source (pattern, &limits); } static cairo_status_t _cairo_xlib_source_finish (void *abstract_surface) { cairo_xlib_source_t *source = abstract_surface; XRenderFreePicture (source->dpy, source->picture); if (source->pixmap) XFreePixmap (source->dpy, source->pixmap); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xlib_source_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_source_finish, NULL, /* read-only wrapper */ }; static cairo_status_t _cairo_xlib_proxy_finish (void *abstract_surface) { cairo_xlib_proxy_t *proxy = abstract_surface; _cairo_xlib_shm_surface_mark_active (proxy->owner); XRenderFreePicture (proxy->source.dpy, proxy->source.picture); if (proxy->source.pixmap) XFreePixmap (proxy->source.dpy, proxy->source.pixmap); cairo_surface_destroy (proxy->owner); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xlib_proxy_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_proxy_finish, NULL, /* read-only wrapper */ }; static cairo_surface_t * source (cairo_xlib_surface_t *dst, Picture picture, Pixmap pixmap) { cairo_xlib_source_t *source; if (picture == None) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); source = malloc (sizeof (*source)); if (unlikely (source == NULL)) { XRenderFreePicture (dst->display->display, picture); if (pixmap) XFreePixmap (dst->display->display, pixmap); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } _cairo_surface_init (&source->base, &cairo_xlib_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); /* The source exists only within an operation */ source->picture = picture; source->pixmap = pixmap; source->dpy = dst->display->display; return &source->base; } static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } static const XTransform identity = { { { 1 << 16, 0x00000, 0x00000 }, { 0x00000, 1 << 16, 0x00000 }, { 0x00000, 0x00000, 1 << 16 }, } }; static cairo_bool_t picture_set_matrix (cairo_xlib_display_t *display, Picture picture, const cairo_matrix_t *matrix, cairo_filter_t filter, double xc, double yc, int *x_offset, int *y_offset) { XTransform xtransform; pixman_transform_t *pixman_transform; cairo_int_status_t status; /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ pixman_transform = (pixman_transform_t *) &xtransform; status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, pixman_transform, x_offset, y_offset); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return TRUE; if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) return FALSE; if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) return TRUE; /* a late check in case we perturb the matrix too far */ if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) return FALSE; XRenderSetPictureTransform (display->display, picture, &xtransform); return TRUE; } static cairo_status_t picture_set_filter (Display *dpy, Picture picture, cairo_filter_t filter) { const char *render_filter; switch (filter) { case CAIRO_FILTER_FAST: render_filter = FilterFast; break; case CAIRO_FILTER_GOOD: render_filter = FilterGood; break; case CAIRO_FILTER_BEST: render_filter = FilterBest; break; case CAIRO_FILTER_NEAREST: render_filter = FilterNearest; break; case CAIRO_FILTER_BILINEAR: render_filter = FilterBilinear; break; case CAIRO_FILTER_GAUSSIAN: /* XXX: The GAUSSIAN value has no implementation in cairo * whatsoever, so it was really a mistake to have it in the * API. We could fix this by officially deprecating it, or * else inventing semantics and providing an actual * implementation for it. */ default: render_filter = FilterBest; break; } XRenderSetPictureFilter (dpy, picture, (char *) render_filter, NULL, 0); return CAIRO_STATUS_SUCCESS; } static int extend_to_repeat (cairo_extend_t extend) { switch (extend) { default: ASSERT_NOT_REACHED; case CAIRO_EXTEND_NONE: return RepeatNone; case CAIRO_EXTEND_REPEAT: return RepeatNormal; case CAIRO_EXTEND_REFLECT: return RepeatReflect; case CAIRO_EXTEND_PAD: return RepeatPad; } } static cairo_bool_t picture_set_properties (cairo_xlib_display_t *display, Picture picture, const cairo_pattern_t *pattern, const cairo_matrix_t *matrix, const cairo_rectangle_int_t *extents, int *x_off, int *y_off) { XRenderPictureAttributes pa; int mask = 0; if (! picture_set_matrix (display, picture, matrix, pattern->filter, extents->x + extents->width / 2, extents->y + extents->height / 2, x_off, y_off)) return FALSE; picture_set_filter (display->display, picture, pattern->filter); if (pattern->has_component_alpha) { pa.component_alpha = 1; mask |= CPComponentAlpha; } if (pattern->extend != CAIRO_EXTEND_NONE) { pa.repeat = extend_to_repeat (pattern->extend); mask |= CPRepeat; } if (mask) XRenderChangePicture (display->display, picture, mask, &pa); return TRUE; } static cairo_surface_t * render_pattern (cairo_xlib_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, int *src_x, int *src_y) { Display *dpy = dst->display->display; cairo_xlib_surface_t *src; cairo_image_surface_t *image; cairo_status_t status; cairo_rectangle_int_t map_extents; src = (cairo_xlib_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA, extents->width, extents->height); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } map_extents = *extents; map_extents.x = map_extents.y = 0; image = _cairo_surface_map_to_image (&src->base, &map_extents); status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y, CAIRO_OPERATOR_SOURCE, pattern, NULL); status = _cairo_surface_unmap_image (&src->base, image); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } status = _cairo_xlib_surface_put_shm (src); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } src->picture = XRenderCreatePicture (dpy, src->drawable, src->xrender_format, 0, NULL); *src_x = -extents->x; *src_y = -extents->y; return &src->base; } static cairo_surface_t * gradient_source (cairo_xlib_surface_t *dst, const cairo_gradient_pattern_t *gradient, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, int *src_x, int *src_y) { cairo_xlib_display_t *display = dst->display; cairo_matrix_t matrix = gradient->base.matrix; char buf[CAIRO_STACK_BUFFER_SIZE]; cairo_circle_double_t extremes[2]; XFixed *stops; XRenderColor *colors; Picture picture; unsigned int i, n_stops; /* The RENDER specification says that the inner circle has * to be completely contained inside the outer one. */ if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL && ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient)) return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); assert (gradient->n_stops > 0); n_stops = MAX (gradient->n_stops, 2); if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) { stops = (XFixed *) buf; } else { stops = _cairo_malloc_ab (n_stops, sizeof (XFixed) + sizeof (XRenderColor)); if (unlikely (stops == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } colors = (XRenderColor *) (stops + n_stops); for (i = 0; i < gradient->n_stops; i++) { stops[i] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); colors[i].red = gradient->stops[i].color.red_short; colors[i].green = gradient->stops[i].color.green_short; colors[i].blue = gradient->stops[i].color.blue_short; colors[i].alpha = gradient->stops[i].color.alpha_short; } /* RENDER does not support gradients with less than 2 * stops. If a gradient has only a single stop, duplicate * it to make RENDER happy. */ if (gradient->n_stops == 1) { stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset); colors[1].red = gradient->stops[0].color.red_short; colors[1].green = gradient->stops[0].color.green_short; colors[1].blue = gradient->stops[0].color.blue_short; colors[1].alpha = gradient->stops[0].color.alpha_short; } #if 0 /* For some weird reason the X server is sometimes getting * CreateGradient requests with bad length. So far I've only seen * XRenderCreateLinearGradient request with 4 stops sometime end up * with length field matching 0 stops at the server side. I've * looked at the libXrender code and I can't see anything that * could cause this behavior. However, for some reason having a * XSync call here seems to avoid the issue so I'll keep it here * until it's solved. */ XSync (display->display, False); #endif _cairo_gradient_pattern_fit_to_range (gradient, PIXMAN_MAX_INT >> 1, &matrix, extremes); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { XLinearGradient grad; grad.p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); grad.p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); grad.p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); grad.p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); picture = XRenderCreateLinearGradient (display->display, &grad, stops, colors, n_stops); } else { XRadialGradient grad; grad.inner.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); grad.inner.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); grad.inner.radius = _cairo_fixed_16_16_from_double (extremes[0].radius); grad.outer.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); grad.outer.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); grad.outer.radius = _cairo_fixed_16_16_from_double (extremes[1].radius); picture = XRenderCreateRadialGradient (display->display, &grad, stops, colors, n_stops); } if (stops != (XFixed *) buf) free (stops); *src_x = *src_y = 0; if (! picture_set_properties (display, picture, &gradient->base, &gradient->base.matrix, extents, src_x, src_y)) { XRenderFreePicture (display->display, picture); return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); } return source (dst, picture, None); } static cairo_surface_t * color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { Display *dpy = dst->display->display; XRenderColor xcolor; Picture picture; Pixmap pixmap = None; xcolor.red = color->red_short; xcolor.green = color->green_short; xcolor.blue = color->blue_short; xcolor.alpha = color->alpha_short; if (CAIRO_RENDER_HAS_GRADIENTS(dst->display)) { picture = XRenderCreateSolidFill (dpy, &xcolor); } else { XRenderPictureAttributes pa; int mask = 0; pa.repeat = RepeatNormal; mask |= CPRepeat; pixmap = XCreatePixmap (dpy, dst->drawable, 1, 1, 32); picture = XRenderCreatePicture (dpy, pixmap, _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_ARGB32), mask, &pa); if (CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { XRectangle r = { 0, 0, 1, 1}; XRenderFillRectangles (dpy, PictOpSrc, picture, &xcolor, &r, 1); } else { XGCValues gcv; GC gc; gc = _cairo_xlib_screen_get_gc (dst->display, dst->screen, 32, pixmap); if (unlikely (gc == NULL)) { XFreePixmap (dpy, pixmap); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } gcv.foreground = 0; gcv.foreground |= color->alpha_short >> 8 << 24; gcv.foreground |= color->red_short >> 8 << 16; gcv.foreground |= color->green_short >> 8 << 8; gcv.foreground |= color->blue_short >> 8 << 0; gcv.fill_style = FillSolid; XChangeGC (dpy, gc, GCFillStyle | GCForeground, &gcv); XFillRectangle (dpy, pixmap, gc, 0, 0, 1, 1); _cairo_xlib_screen_put_gc (dst->display, dst->screen, 32, gc); } } return source (dst, picture, pixmap); } static cairo_surface_t * alpha_source (cairo_xlib_surface_t *dst, uint8_t alpha) { cairo_xlib_display_t *display = dst->display; if (display->alpha[alpha] == NULL) { cairo_color_t color; color.red_short = color.green_short = color.blue_short = 0; color.alpha_short = alpha << 8 | alpha; display->alpha[alpha] = color_source (dst, &color); } return cairo_surface_reference (display->alpha[alpha]); } static cairo_surface_t * white_source (cairo_xlib_surface_t *dst) { cairo_xlib_display_t *display = dst->display; if (display->white == NULL) display->white = color_source (dst, CAIRO_COLOR_WHITE); return cairo_surface_reference (display->white); } static cairo_surface_t * opaque_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { cairo_xlib_display_t *display = dst->display; uint32_t pixel = 0xff000000 | color->red_short >> 8 << 16 | color->green_short >> 8 << 8 | color->blue_short >> 8 << 0; int i; if (display->last_solid_cache[0].color == pixel) return cairo_surface_reference (display->solid[display->last_solid_cache[0].index]); for (i = 0; i < 16; i++) { if (display->solid_cache[i] == pixel) goto done; } i = hars_petruska_f54_1_random () % 16; cairo_surface_destroy (display->solid[i]); display->solid[i] = color_source (dst, color); display->solid_cache[i] = pixel; done: display->last_solid_cache[0].color = pixel; display->last_solid_cache[0].index = i; return cairo_surface_reference (display->solid[i]); } static cairo_surface_t * transparent_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { cairo_xlib_display_t *display = dst->display; uint32_t pixel = color->alpha_short >> 8 << 24 | color->red_short >> 8 << 16 | color->green_short >> 8 << 8 | color->blue_short >> 8 << 0; int i; if (display->last_solid_cache[1].color == pixel) { assert (display->solid[display->last_solid_cache[1].index]); return cairo_surface_reference (display->solid[display->last_solid_cache[1].index]); } for (i = 16; i < 32; i++) { if (display->solid_cache[i] == pixel) goto done; } i = 16 + (hars_petruska_f54_1_random () % 16); cairo_surface_destroy (display->solid[i]); display->solid[i] = color_source (dst, color); display->solid_cache[i] = pixel; done: display->last_solid_cache[1].color = pixel; display->last_solid_cache[1].index = i; assert (display->solid[i]); return cairo_surface_reference (display->solid[i]); } static cairo_surface_t * solid_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { if ((color->red_short | color->green_short | color->blue_short) <= 0xff) return alpha_source (dst, color->alpha_short >> 8); if (CAIRO_ALPHA_SHORT_IS_OPAQUE (color->alpha_short)) { if (color->red_short >= 0xff00 && color->green_short >= 0xff00 && color->blue_short >= 0xff00) return white_source (dst); return opaque_source (dst, color); } else return transparent_source (dst, color); } static cairo_xlib_source_t *init_source (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { Display *dpy = dst->display->display; cairo_xlib_source_t *source = &src->embedded_source; /* As these are frequent and meant to be fast, we track pictures for * native surface and minimise update requests. */ if (source->picture == None) { XRenderPictureAttributes pa; _cairo_surface_init (&source->base, &cairo_xlib_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); pa.subwindow_mode = IncludeInferiors; source->picture = XRenderCreatePicture (dpy, src->drawable, src->xrender_format, CPSubwindowMode, &pa); source->has_component_alpha = 0; source->has_matrix = 0; source->filter = CAIRO_FILTER_NEAREST; source->extend = CAIRO_EXTEND_NONE; } return (cairo_xlib_source_t *) cairo_surface_reference (&source->base); } static cairo_surface_t * embedded_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *src_x, int *src_y, cairo_xlib_source_t *source) { Display *dpy = dst->display->display; cairo_int_status_t status; XTransform xtransform; XRenderPictureAttributes pa; unsigned mask = 0; status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, pattern->base.filter, extents->x + extents->width / 2, extents->y + extents->height / 2, (pixman_transform_t *)&xtransform, src_x, src_y); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { if (source->has_matrix) { source->has_matrix = 0; memcpy (&xtransform, &identity, sizeof (identity)); status = CAIRO_INT_STATUS_SUCCESS; } } else source->has_matrix = 1; if (status == CAIRO_INT_STATUS_SUCCESS) XRenderSetPictureTransform (dpy, source->picture, &xtransform); if (source->filter != pattern->base.filter) { picture_set_filter (dpy, source->picture, pattern->base.filter); source->filter = pattern->base.filter; } if (source->has_component_alpha != pattern->base.has_component_alpha) { pa.component_alpha = pattern->base.has_component_alpha; mask |= CPComponentAlpha; source->has_component_alpha = pattern->base.has_component_alpha; } if (source->extend != pattern->base.extend) { pa.repeat = extend_to_repeat (pattern->base.extend); mask |= CPRepeat; source->extend = pattern->base.extend; } if (mask) XRenderChangePicture (dpy, source->picture, mask, &pa); return &source->base; } static cairo_surface_t * subsurface_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_surface_subsurface_t *sub; cairo_xlib_surface_t *src; cairo_xlib_source_t *source; Display *dpy = dst->display->display; cairo_int_status_t status; cairo_surface_pattern_t local_pattern; XTransform xtransform; XRenderPictureAttributes pa; unsigned mask = 0; sub = (cairo_surface_subsurface_t *) pattern->surface; if (sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= sub->extents.width && sample->y + sample->height <= sub->extents.height) { src = (cairo_xlib_surface_t *) sub->target; status = _cairo_surface_flush (&src->base, 0); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (pattern->base.filter == CAIRO_FILTER_NEAREST && _cairo_matrix_is_translation (&pattern->base.matrix)) { *src_x += pattern->base.matrix.x0 + sub->extents.x; *src_y += pattern->base.matrix.y0 + sub->extents.y; _cairo_xlib_surface_ensure_picture (src); return cairo_surface_reference (&src->base); } else { cairo_surface_pattern_t local_pattern = *pattern; local_pattern.base.matrix.x0 += sub->extents.x; local_pattern.base.matrix.y0 += sub->extents.y; local_pattern.base.extend = CAIRO_EXTEND_NONE; return embedded_source (dst, &local_pattern, extents, src_x, src_y, init_source (dst, src)); } } if (sub->snapshot && sub->snapshot->type == CAIRO_SURFACE_TYPE_XLIB) { src = (cairo_xlib_surface_t *) cairo_surface_reference (sub->snapshot); source = &src->embedded_source; } else { src = (cairo_xlib_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, sub->base.content, sub->extents.width, sub->extents.height); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } _cairo_pattern_init_for_surface (&local_pattern, sub->target); cairo_matrix_init_translate (&local_pattern.base.matrix, sub->extents.x, sub->extents.y); local_pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (&src->base, CAIRO_OPERATOR_SOURCE, &local_pattern.base, NULL); _cairo_pattern_fini (&local_pattern.base); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } _cairo_xlib_surface_ensure_picture (src); _cairo_surface_subsurface_set_snapshot (&sub->base, &src->base); source = &src->embedded_source; source->has_component_alpha = 0; source->has_matrix = 0; source->filter = CAIRO_FILTER_NEAREST; source->extend = CAIRO_EXTEND_NONE; } status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, pattern->base.filter, extents->x + extents->width / 2, extents->y + extents->height / 2, (pixman_transform_t *)&xtransform, src_x, src_y); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { if (source->has_matrix) { source->has_matrix = 0; memcpy (&xtransform, &identity, sizeof (identity)); status = CAIRO_INT_STATUS_SUCCESS; } } else source->has_matrix = 1; if (status == CAIRO_INT_STATUS_SUCCESS) XRenderSetPictureTransform (dpy, src->picture, &xtransform); if (source->filter != pattern->base.filter) { picture_set_filter (dpy, src->picture, pattern->base.filter); source->filter = pattern->base.filter; } if (source->has_component_alpha != pattern->base.has_component_alpha) { pa.component_alpha = pattern->base.has_component_alpha; mask |= CPComponentAlpha; source->has_component_alpha = pattern->base.has_component_alpha; } if (source->extend != pattern->base.extend) { pa.repeat = extend_to_repeat (pattern->base.extend); mask |= CPRepeat; source->extend = pattern->base.extend; } if (mask) XRenderChangePicture (dpy, src->picture, mask, &pa); return &src->base; } static cairo_surface_t * native_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *src; cairo_int_status_t status; if (_cairo_surface_is_subsurface (pattern->surface)) return subsurface_source (dst, pattern, is_mask, extents, sample, src_x, src_y); src = unwrap_source (pattern); status = _cairo_surface_flush (&src->base, 0); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (pattern->base.filter == CAIRO_FILTER_NEAREST && sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= src->width && sample->y + sample->height <= src->height && _cairo_matrix_is_translation (&pattern->base.matrix)) { *src_x += pattern->base.matrix.x0; *src_y += pattern->base.matrix.y0; _cairo_xlib_surface_ensure_picture (src); return cairo_surface_reference (&src->base); } return embedded_source (dst, pattern, extents, src_x, src_y, init_source (dst, src)); } static cairo_surface_t * recording_pattern_get_surface (const cairo_pattern_t *pattern) { cairo_surface_t *surface; surface = ((const cairo_surface_pattern_t *) pattern)->surface; if (_cairo_surface_is_paginated (surface)) surface = _cairo_paginated_surface_get_recording (surface); if (_cairo_surface_is_snapshot (surface)) surface = _cairo_surface_snapshot_get_target (surface); return surface; } static cairo_surface_t * record_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *src; cairo_matrix_t matrix, m; cairo_status_t status; cairo_rectangle_int_t upload, limit; upload = *sample; if (_cairo_surface_get_extents (pattern->surface, &limit) && ! _cairo_rectangle_intersect (&upload, &limit)) { if (pattern->base.extend == CAIRO_EXTEND_NONE) return alpha_source (dst, 0); upload = limit; } src = (cairo_xlib_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, pattern->surface->content, upload.width, upload.height); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } cairo_matrix_init_translate (&matrix, upload.x, upload.y); status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (&pattern->base), &matrix, &src->base, NULL); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } matrix = pattern->base.matrix; if (upload.x | upload.y) { cairo_matrix_init_translate (&m, -upload.x, -upload.y); cairo_matrix_multiply (&matrix, &matrix, &m); } _cairo_xlib_surface_ensure_picture (src); if (! picture_set_properties (src->display, src->picture, &pattern->base, &matrix, extents, src_x, src_y)) { cairo_surface_destroy (&src->base); return render_pattern (dst, &pattern->base, is_mask, extents, src_x, src_y); } return &src->base; } static cairo_surface_t * surface_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_surface_t *src; cairo_xlib_surface_t *xsrc; cairo_surface_pattern_t local_pattern; cairo_status_t status; cairo_rectangle_int_t upload, limit; src = pattern->surface; if (src->type == CAIRO_SURFACE_TYPE_IMAGE && src->device == dst->base.device && _cairo_xlib_shm_surface_get_pixmap (src)) { cairo_xlib_proxy_t *proxy; proxy = malloc (sizeof(*proxy)); if (unlikely (proxy == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&proxy->source.base, &cairo_xlib_proxy_backend, dst->base.device, src->content); proxy->source.dpy = dst->display->display; proxy->source.picture = XRenderCreatePicture (proxy->source.dpy, _cairo_xlib_shm_surface_get_pixmap (src), _cairo_xlib_shm_surface_get_xrender_format (src), 0, NULL); proxy->source.pixmap = None; proxy->source.has_component_alpha = 0; proxy->source.has_matrix = 0; proxy->source.filter = CAIRO_FILTER_NEAREST; proxy->source.extend = CAIRO_EXTEND_NONE; proxy->owner = cairo_surface_reference (src); return embedded_source (dst, pattern, extents, src_x, src_y, &proxy->source); } upload = *sample; if (_cairo_surface_get_extents (pattern->surface, &limit)) { if (pattern->base.extend == CAIRO_EXTEND_NONE) { if (! _cairo_rectangle_intersect (&upload, &limit)) return alpha_source (dst, 0); } else if (pattern->base.extend == CAIRO_EXTEND_PAD) { if (! _cairo_rectangle_intersect (&upload, &limit)) upload = limit; } else { if (upload.x < limit.x || upload.x + upload.width > limit.x + limit.width || upload.y < limit.y || upload.y + upload.height > limit.y + limit.height) { upload = limit; } } } xsrc = (cairo_xlib_surface_t *) _cairo_surface_create_similar_scratch (&dst->base, src->content, upload.width, upload.height); if (xsrc->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (src); cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } if (_cairo_surface_is_image (src)) { status = _cairo_xlib_surface_draw_image (xsrc, (cairo_image_surface_t *)src, upload.x, upload.y, upload.width, upload.height, 0, 0); } else { cairo_image_surface_t *image; cairo_rectangle_int_t map_extents = { 0,0, upload.width,upload.height }; image = _cairo_surface_map_to_image (&xsrc->base, &map_extents); _cairo_pattern_init_for_surface (&local_pattern, pattern->surface); cairo_matrix_init_translate (&local_pattern.base.matrix, upload.x, upload.y); status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, &local_pattern.base, NULL); _cairo_pattern_fini (&local_pattern.base); status = _cairo_surface_unmap_image (&xsrc->base, image); if (unlikely (status)) { cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (status); } status = _cairo_xlib_surface_put_shm (xsrc); if (unlikely (status)) { cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (status); } } _cairo_pattern_init_static_copy (&local_pattern.base, &pattern->base); if (upload.x | upload.y) { cairo_matrix_t m; cairo_matrix_init_translate (&m, -upload.x, -upload.y); cairo_matrix_multiply (&local_pattern.base.matrix, &local_pattern.base.matrix, &m); } *src_x = *src_y = 0; _cairo_xlib_surface_ensure_picture (xsrc); if (! picture_set_properties (xsrc->display, xsrc->picture, &local_pattern.base, &local_pattern.base.matrix, extents, src_x, src_y)) { cairo_surface_destroy (&xsrc->base); return render_pattern (dst, &pattern->base, is_mask, extents, src_x, src_y); } return &xsrc->base; } static cairo_bool_t pattern_is_supported (cairo_xlib_display_t *display, const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_MESH) return FALSE; if (display->buggy_pad_reflect) { if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_PAD) return FALSE; } if (display->buggy_gradients) { if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) return FALSE; } if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) { if (!_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) return FALSE; } if (! CAIRO_RENDER_HAS_FILTERS (display)) { /* No filters implies no transforms, so we optimise away BILINEAR */ } return TRUE; } cairo_surface_t * _cairo_xlib_source_create_for_pattern (cairo_surface_t *_dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)_dst; *src_x = *src_y = 0; if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) { if (pattern == NULL) pattern = &_cairo_pattern_white.base; return solid_source (dst, &((cairo_solid_pattern_t *)pattern)->color); } if (pattern_is_supported (dst->display, pattern)) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *)pattern; if (spattern->surface->type == CAIRO_SURFACE_TYPE_XLIB && _cairo_xlib_surface_same_screen (dst, unwrap_source (spattern))) return native_source (dst, spattern, is_mask, extents, sample, src_x, src_y); if (spattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return record_source (dst, spattern, is_mask, extents, sample, src_x, src_y); return surface_source (dst, spattern, is_mask, extents, sample, src_x, src_y); } if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_gradient_pattern_t *gpattern = (cairo_gradient_pattern_t *)pattern; return gradient_source (dst, gpattern, is_mask, extents, src_x, src_y); } } return render_pattern (dst, pattern, is_mask, extents, src_x, src_y); } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-surface-private.h000066400000000000000000000033011271037650300275750ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. */ #ifndef CAIRO_XLIB_SURFACE_PRIVATE_H #define CAIRO_XLIB_SURFACE_PRIVATE_H #include "cairo-xlib.h" #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-surface-private.h" #include "cairo-surface-backend-private.h" #endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-surface-shm.c000066400000000000000000001105171271037650300267150ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H) void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {} cairo_surface_t * _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, cairo_bool_t overwrite) { return NULL; } cairo_int_status_t _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) { assert (!surface->fallback); return CAIRO_INT_STATUS_SUCCESS; } cairo_surface_t * _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, pixman_format_code_t format, int width, int height) { return NULL; } cairo_surface_t * _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, pixman_format_code_t format, int width, int height) { return NULL; } cairo_surface_t * _cairo_xlib_surface_create_similar_shm (void *other, cairo_format_t format, int width, int height) { return cairo_image_surface_create (format, width, height); } void _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) { ASSERT_NOT_REACHED; } void _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, XImage *ximage) { ASSERT_NOT_REACHED; } void * _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) { ASSERT_NOT_REACHED; return NULL; } Pixmap _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface) { ASSERT_NOT_REACHED; return 0; } XRenderPictFormat * _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface) { ASSERT_NOT_REACHED; return NULL; } cairo_bool_t _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface) { ASSERT_NOT_REACHED; return FALSE; } cairo_bool_t _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) { ASSERT_NOT_REACHED; return TRUE; } void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {} #else #include "cairo-damage-private.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-mempool-private.h" #include #include #include #if HAVE_X11_EXTENSIONS_SHMPROTO_H #include #elif HAVE_X11_EXTENSIONS_SHMSTR_H #include #endif #include #include #define MIN_PIXMAP_SIZE 4096 #define MIN_BITS 8 #define MIN_SIZE (1<<(MIN_BITS-1)) typedef struct _cairo_xlib_shm cairo_xlib_shm_t; typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t; typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t; struct _cairo_xlib_shm { cairo_mempool_t mem; XShmSegmentInfo shm; unsigned long attached; cairo_list_t link; }; struct _cairo_xlib_shm_info { unsigned long last_request; void *mem; size_t size; cairo_xlib_shm_t *pool; }; struct _cairo_xlib_shm_surface { cairo_image_surface_t image; cairo_list_t link; cairo_xlib_shm_info_t *info; Pixmap pixmap; unsigned long active; int idle; }; /* the parent is always given by index/2 */ #define PQ_PARENT_INDEX(i) ((i) >> 1) #define PQ_FIRST_ENTRY 1 /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) #define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY]) struct pqueue { int size, max_size; cairo_xlib_shm_info_t **elements; }; struct _cairo_xlib_shm_display { int has_pixmaps; int opcode; int event; Window window; unsigned long last_request; unsigned long last_event; cairo_list_t surfaces; cairo_list_t pool; struct pqueue info; }; static inline cairo_bool_t seqno_passed (unsigned long a, unsigned long b) { return (long)(b - a) >= 0; } static inline cairo_bool_t seqno_before (unsigned long a, unsigned long b) { return (long)(b - a) > 0; } static inline cairo_bool_t seqno_after (unsigned long a, unsigned long b) { return (long)(a - b) > 0; } static inline cairo_status_t _pqueue_init (struct pqueue *pq) { pq->max_size = 32; pq->size = 0; pq->elements = _cairo_malloc_ab (pq->max_size, sizeof (cairo_xlib_shm_info_t *)); if (unlikely (pq->elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); PQ_TOP(pq) = NULL; return CAIRO_STATUS_SUCCESS; } static inline void _pqueue_fini (struct pqueue *pq) { free (pq->elements); } static cairo_status_t _pqueue_grow (struct pqueue *pq) { cairo_xlib_shm_info_t **new_elements; new_elements = _cairo_realloc_ab (pq->elements, 2 * pq->max_size, sizeof (cairo_xlib_shm_info_t *)); if (unlikely (new_elements == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pq->elements = new_elements; pq->max_size *= 2; return CAIRO_STATUS_SUCCESS; } static void _pqueue_shrink (struct pqueue *pq, int min_size) { cairo_xlib_shm_info_t **new_elements; if (min_size > pq->max_size) return; new_elements = _cairo_realloc_ab (pq->elements, min_size, sizeof (cairo_xlib_shm_info_t *)); if (unlikely (new_elements == NULL)) return; pq->elements = new_elements; pq->max_size = min_size; } static inline cairo_status_t _pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info) { cairo_xlib_shm_info_t **elements; int i, parent; if (unlikely (pq->size + 1 == pq->max_size)) { cairo_status_t status; status = _pqueue_grow (pq); if (unlikely (status)) return status; } elements = pq->elements; for (i = ++pq->size; i != PQ_FIRST_ENTRY && info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request; i = parent) { elements[i] = elements[parent]; } elements[i] = info; return CAIRO_STATUS_SUCCESS; } static inline void _pqueue_pop (struct pqueue *pq) { cairo_xlib_shm_info_t **elements = pq->elements; cairo_xlib_shm_info_t *tail; int child, i; tail = elements[pq->size--]; if (pq->size == 0) { elements[PQ_FIRST_ENTRY] = NULL; _pqueue_shrink (pq, 32); return; } for (i = PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; i = child) { if (child != pq->size && elements[child+1]->last_request < elements[child]->last_request) { child++; } if (elements[child]->last_request >= tail->last_request) break; elements[i] = elements[child]; } elements[i] = tail; } static cairo_bool_t _x_error_occurred; static int _check_error_handler (Display *display, XErrorEvent *event) { _x_error_occurred = TRUE; return False; /* ignored */ } static cairo_bool_t can_use_shm (Display *dpy, int *has_pixmap) { XShmSegmentInfo shm; int (*old_handler) (Display *display, XErrorEvent *event); Status success; int major, minor; if (! XShmQueryExtension (dpy)) return FALSE; XShmQueryVersion (dpy, &major, &minor, has_pixmap); shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); if (shm.shmid == -1) return FALSE; shm.readOnly = FALSE; shm.shmaddr = shmat (shm.shmid, NULL, 0); if (shm.shmaddr == (char *) -1) { shmctl (shm.shmid, IPC_RMID, NULL); return FALSE; } assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex)); _x_error_occurred = FALSE; XLockDisplay (dpy); XSync (dpy, False); old_handler = XSetErrorHandler (_check_error_handler); success = XShmAttach (dpy, &shm); if (success) XShmDetach (dpy, &shm); XSync (dpy, False); XSetErrorHandler (old_handler); XUnlockDisplay (dpy); shmctl (shm.shmid, IPC_RMID, NULL); shmdt (shm.shmaddr); return success && ! _x_error_occurred; } static inline Display * peek_display (cairo_device_t *device) { return ((cairo_xlib_display_t *)device)->display; } static inline unsigned long peek_processed (cairo_device_t *device) { return LastKnownRequestProcessed (peek_display(device)); } static void _cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display, cairo_xlib_shm_t *pool) { shmdt (pool->shm.shmaddr); if (display->display) /* may be called after CloseDisplay */ XShmDetach (display->display, &pool->shm); _cairo_mempool_fini (&pool->mem); cairo_list_del (&pool->link); free (pool); } static void send_event(cairo_xlib_display_t *display, cairo_xlib_shm_info_t *info, unsigned long seqno) { XShmCompletionEvent ev; if (! seqno_after (seqno, display->shm->last_event)) return; ev.type = display->shm->event; ev.send_event = 1; /* XXX or lie? */ ev.serial = NextRequest (display->display); ev.drawable = display->shm->window; ev.major_code = display->shm->opcode; ev.minor_code = X_ShmPutImage; ev.shmseg = info->pool->shm.shmid; ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr; XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev); display->shm->last_event = ev.serial; } static void sync (cairo_xlib_display_t *display) { cairo_xlib_shm_info_t *info; struct pqueue *pq = &display->shm->info; XSync (display->display, False); while ((info = PQ_TOP(pq))) { _cairo_mempool_free (&info->pool->mem, info->mem); _pqueue_pop (&display->shm->info); free (info); } } static void _cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display) { cairo_xlib_shm_info_t *info; Display *dpy = display->display; struct pqueue *pq = &display->shm->info; unsigned long processed; if (PQ_TOP(pq) == NULL) return; XEventsQueued (dpy, QueuedAfterReading); processed = LastKnownRequestProcessed (dpy); info = PQ_TOP(pq); do { if (! seqno_passed (info->last_request, processed)) { send_event (display, info, display->shm->last_request); return; } _cairo_mempool_free (&info->pool->mem, info->mem); _pqueue_pop (&display->shm->info); free (info); } while ((info = PQ_TOP(pq))); } static cairo_xlib_shm_t * _cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size, void **ptr, unsigned long *last_request) { cairo_xlib_shm_info_t *info; struct pqueue *pq = &display->shm->info; if (PQ_TOP(pq) == NULL) return NULL; info = PQ_TOP(pq); do { cairo_xlib_shm_t *pool = info->pool; *last_request = info->last_request; _pqueue_pop (&display->shm->info); _cairo_mempool_free (&pool->mem, info->mem); free (info); if (pool->mem.free_bytes >= size) { void *mem = _cairo_mempool_alloc (&pool->mem, size); if (mem != NULL) { *ptr = mem; return pool; } } } while ((info = PQ_TOP(pq))); return NULL; } static cairo_xlib_shm_t * _cairo_xlib_shm_pool_find (cairo_xlib_display_t *display, size_t size, void **ptr) { cairo_xlib_shm_t *pool; cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) { if (pool->mem.free_bytes >= size) { void *mem = _cairo_mempool_alloc (&pool->mem, size); if (mem != NULL) { *ptr = mem; return pool; } } } return NULL; } static void _cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display) { cairo_xlib_shm_t *pool, *next; unsigned long processed; processed = LastKnownRequestProcessed (display->display); cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t, &display->shm->pool, link) { if (! seqno_passed (pool->attached, processed)) break; if (pool->mem.free_bytes == pool->mem.max_bytes) _cairo_xlib_display_shm_pool_destroy (display, pool); } } static cairo_xlib_shm_t * _cairo_xlib_shm_pool_create(cairo_xlib_display_t *display, size_t size, void **ptr) { Display *dpy = display->display; cairo_xlib_shm_t *pool; size_t bytes, maxbits = 16, minbits = MIN_BITS; Status success; pool = malloc (sizeof (cairo_xlib_shm_t)); if (pool == NULL) return NULL; bytes = 1 << maxbits; while (bytes <= size) bytes <<= 1, maxbits++; bytes <<= 3; minbits += (maxbits - 16) / 2; pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); while (pool->shm.shmid == -1 && bytes >= 2*size) { bytes >>= 1; pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); } if (pool->shm.shmid == -1) goto cleanup; pool->shm.readOnly = FALSE; pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0); if (pool->shm.shmaddr == (char *) -1) { shmctl (pool->shm.shmid, IPC_RMID, NULL); goto cleanup; } pool->attached = NextRequest (dpy); success = XShmAttach (dpy, &pool->shm); #if !IPC_RMID_DEFERRED_RELEASE XSync (dpy, FALSE); #endif shmctl (pool->shm.shmid, IPC_RMID, NULL); if (! success) goto cleanup_shm; if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes, minbits, maxbits - minbits + 1)) goto cleanup_detach; cairo_list_add (&pool->link, &display->shm->pool); *ptr = _cairo_mempool_alloc (&pool->mem, size); assert (*ptr != NULL); return pool; cleanup_detach: XShmDetach (dpy, &pool->shm); cleanup_shm: shmdt (pool->shm.shmaddr); cleanup: free (pool); return NULL; } static cairo_xlib_shm_info_t * _cairo_xlib_shm_info_create (cairo_xlib_display_t *display, size_t size, cairo_bool_t will_sync) { cairo_xlib_shm_info_t *info; cairo_xlib_shm_t *pool; unsigned long last_request = 0; void *mem = NULL; _cairo_xlib_shm_info_cleanup (display); pool = _cairo_xlib_shm_pool_find (display, size, &mem); _cairo_xlib_shm_pool_cleanup (display); if (pool == NULL && will_sync) pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request); if (pool == NULL) pool = _cairo_xlib_shm_pool_create (display, size, &mem); if (pool == NULL) return NULL; assert (mem != NULL); info = malloc (sizeof (*info)); if (info == NULL) { _cairo_mempool_free (&pool->mem, mem); return NULL; } info->pool = pool; info->mem = mem; info->size = size; info->last_request = last_request; return info; } static cairo_status_t _cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags) { cairo_xlib_shm_surface_t *shm = abstract_surface; cairo_xlib_display_t *display; Display *dpy; cairo_status_t status; if (shm->active == 0) return CAIRO_STATUS_SUCCESS; if (shm->image.base._finishing) return CAIRO_STATUS_SUCCESS; if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) { shm->active = 0; return CAIRO_STATUS_SUCCESS; } status = _cairo_xlib_display_acquire (shm->image.base.device, &display); if (unlikely (status)) return status; send_event (display, shm->info, shm->active); dpy = display->display; XEventsQueued (dpy, QueuedAfterReading); while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) { LockDisplay(dpy); _XReadEvents(dpy); UnlockDisplay(dpy); } cairo_device_release (&display->base); shm->active = 0; return CAIRO_STATUS_SUCCESS; } static inline cairo_bool_t active (cairo_xlib_shm_surface_t *shm, Display *dpy) { return (shm->active && ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))); } static cairo_status_t _cairo_xlib_shm_surface_finish (void *abstract_surface) { cairo_xlib_shm_surface_t *shm = abstract_surface; cairo_xlib_display_t *display; cairo_status_t status; if (shm->image.base.damage) { _cairo_damage_destroy (shm->image.base.damage); shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); } status = _cairo_xlib_display_acquire (shm->image.base.device, &display); if (unlikely (status)) return status; if (shm->pixmap) XFreePixmap (display->display, shm->pixmap); if (active (shm, display->display)) { shm->info->last_request = shm->active; _pqueue_push (&display->shm->info, shm->info); if (seqno_before (display->shm->last_request, shm->active)) display->shm->last_request = shm->active; } else { _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem); free (shm->info); _cairo_xlib_shm_pool_cleanup (display); } cairo_list_del (&shm->link); cairo_device_release (&display->base); return _cairo_image_surface_finish (abstract_surface); } static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = { CAIRO_SURFACE_TYPE_IMAGE, _cairo_xlib_shm_surface_finish, _cairo_default_context_create, _cairo_image_surface_create_similar, NULL, /* create similar image */ _cairo_image_surface_map_to_image, _cairo_image_surface_unmap_image, _cairo_image_surface_source, _cairo_image_surface_acquire_source_image, _cairo_image_surface_release_source_image, _cairo_image_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_image_surface_get_extents, _cairo_image_surface_get_font_options, _cairo_xlib_shm_surface_flush, NULL, _cairo_image_surface_paint, _cairo_image_surface_mask, _cairo_image_surface_stroke, _cairo_image_surface_fill, NULL, /* fill-stroke */ _cairo_image_surface_glyphs, }; static cairo_bool_t has_shm (cairo_xlib_surface_t *surface) { cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device; return display->shm != NULL; } static int has_shm_pixmaps (cairo_xlib_surface_t *surface) { cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device; if (!display->shm) return 0; return display->shm->has_pixmaps; } static cairo_xlib_shm_surface_t * _cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other, pixman_format_code_t format, int width, int height, cairo_bool_t will_sync, int create_pixmap) { cairo_xlib_shm_surface_t *shm; cairo_xlib_display_t *display; pixman_image_t *image; int stride, size; stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format)); size = stride * height; if (size < MIN_SIZE) return NULL; shm = malloc (sizeof (*shm)); if (unlikely (shm == NULL)) return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&shm->image.base, &cairo_xlib_shm_surface_backend, other->base.device, _cairo_content_from_pixman_format (format)); if (_cairo_xlib_display_acquire (other->base.device, &display)) goto cleanup_shm; shm->info = _cairo_xlib_shm_info_create (display, size, will_sync); if (shm->info == NULL) goto cleanup_display; image = pixman_image_create_bits (format, width, height, (uint32_t *) shm->info->mem, stride); if (image == NULL) goto cleanup_info; _cairo_image_surface_init (&shm->image, image, format); shm->pixmap = 0; if (create_pixmap && size >= create_pixmap) { shm->pixmap = XShmCreatePixmap (display->display, other->drawable, shm->info->mem, &shm->info->pool->shm, shm->image.width, shm->image.height, shm->image.depth); } shm->active = shm->info->last_request; shm->idle = -5; assert (shm->active == 0 || will_sync); cairo_list_add (&shm->link, &display->shm->surfaces); cairo_device_release (&display->base); return shm; cleanup_info: _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem); free(shm->info); cleanup_display: cairo_device_release (&display->base); cleanup_shm: free (shm); return NULL; } static void _cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm; cairo_xlib_display_t *display; cairo_damage_t *damage; GC gc; damage = _cairo_damage_reduce (surface->base.damage); surface->base.damage = _cairo_damage_create(); if (_cairo_xlib_display_acquire (surface->base.device, &display)) goto cleanup_damage; if (_cairo_xlib_surface_get_gc (display, surface, &gc)) goto cleanup_display; if (! surface->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = IncludeInferiors; XChangeGC (display->display, gc, GCSubwindowMode, &gcv); } if (damage->region) { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))]; XRectangle *rects = stack_rects; cairo_rectangle_int_t r; int n_rects, i; n_rects = cairo_region_num_rectangles (damage->region); if (n_rects == 0) { } else if (n_rects == 1) { cairo_region_get_rectangle (damage->region, 0, &r); XCopyArea (display->display, surface->drawable, shm->pixmap, gc, r.x, r.y, r.width, r.height, r.x, r.y); } else { if (n_rects > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) { rects = stack_rects; n_rects = ARRAY_LENGTH (stack_rects); } } for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (damage->region, i, &r); rects[i].x = r.x; rects[i].y = r.y; rects[i].width = r.width; rects[i].height = r.height; } XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded); XCopyArea (display->display, surface->drawable, shm->pixmap, gc, 0, 0, shm->image.width, shm->image.height, 0, 0); if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) XSetClipMask (display->display, gc, None); } } else { XCopyArea (display->display, surface->drawable, shm->pixmap, gc, 0, 0, shm->image.width, shm->image.height, 0, 0); } if (! surface->owns_pixmap) { XGCValues gcv; gcv.subwindow_mode = ClipByChildren; XChangeGC (display->display, gc, GCSubwindowMode, &gcv); } sync (display); shm->active = 0; shm->idle--; _cairo_xlib_surface_put_gc (display, surface, gc); cleanup_display: cairo_device_release (&display->base); cleanup_damage: _cairo_damage_destroy (damage); } static void _cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm; assert (shm->active == 0); _cairo_damage_destroy (surface->base.damage); surface->base.damage = _cairo_damage_create(); memset (shm->image.data, 0, shm->image.stride * shm->image.height); shm->image.base.is_clear = TRUE; } static void inc_idle (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface; shm->idle++; } static void dec_idle (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface; shm->idle--; } cairo_surface_t * _cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, cairo_bool_t overwrite) { if (surface->fallback) { assert (surface->base.damage); assert (surface->shm); assert (surface->shm->damage); goto done; } if (surface->shm == NULL) { pixman_format_code_t pixman_format; cairo_bool_t will_sync; if (! has_shm_pixmaps (surface)) return NULL; if ((surface->width | surface->height) < 32) return NULL; pixman_format = _pixman_format_for_xlib_surface (surface); if (pixman_format == 0) return NULL; will_sync = !surface->base.is_clear && !overwrite; surface->shm = &_cairo_xlib_shm_surface_create (surface, pixman_format, surface->width, surface->height, will_sync, 1)->image.base; if (surface->shm == NULL) return NULL; assert (surface->base.damage == NULL); if (surface->base.serial || !surface->owns_pixmap) { cairo_rectangle_int_t rect; rect.x = rect.y = 0; rect.width = surface->width; rect.height = surface->height; surface->base.damage = _cairo_damage_add_rectangle (NULL, &rect); } else surface->base.damage = _cairo_damage_create (); surface->shm->damage = _cairo_damage_create (); } if (overwrite) { _cairo_damage_destroy (surface->base.damage); surface->base.damage = _cairo_damage_create (); } if (!surface->base.is_clear && surface->base.damage->dirty) _cairo_xlib_surface_update_shm (surface); _cairo_xlib_shm_surface_flush (surface->shm, 1); if (surface->base.is_clear && surface->base.damage->dirty) _cairo_xlib_surface_clear_shm (surface); done: dec_idle(surface->shm); return surface->shm; } cairo_int_status_t _cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) { cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; if (!surface->fallback) { if (surface->shm) inc_idle (surface->shm); return CAIRO_INT_STATUS_SUCCESS; } if (surface->shm->damage->dirty) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm; cairo_xlib_display_t *display; cairo_damage_t *damage; GC gc; status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return status; damage = _cairo_damage_reduce (shm->image.base.damage); shm->image.base.damage = _cairo_damage_create (); TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, damage->region ? cairo_region_num_rectangles (damage->region) : 0)); if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) { XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))]; XRectangle *rects = stack_rects; cairo_rectangle_int_t r; int n_rects, i; n_rects = cairo_region_num_rectangles (damage->region); if (n_rects == 0) goto out; status = _cairo_xlib_surface_get_gc (display, surface, &gc); if (unlikely (status)) goto out; if (n_rects == 1) { cairo_region_get_rectangle (damage->region, 0, &r); _cairo_xlib_shm_surface_mark_active (surface->shm); XCopyArea (display->display, shm->pixmap, surface->drawable, gc, r.x, r.y, r.width, r.height, r.x, r.y); } else { if (n_rects > ARRAY_LENGTH (stack_rects)) { rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_xlib_surface_put_gc (display, surface, gc); goto out; } } for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (damage->region, i, &r); rects[i].x = r.x; rects[i].y = r.y; rects[i].width = r.width; rects[i].height = r.height; } XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded); _cairo_xlib_shm_surface_mark_active (surface->shm); XCopyArea (display->display, shm->pixmap, surface->drawable, gc, 0, 0, shm->image.width, shm->image.height, 0, 0); if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) XSetClipMask (display->display, gc, None); } _cairo_xlib_surface_put_gc (display, surface, gc); } out: _cairo_damage_destroy (damage); cairo_device_release (&display->base); } return status; } cairo_surface_t * _cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, pixman_format_code_t format, int width, int height) { cairo_surface_t *surface; surface = NULL; if (has_shm (other)) surface = &_cairo_xlib_shm_surface_create (other, format, width, height, FALSE, has_shm_pixmaps (other))->image.base; return surface; } cairo_surface_t * _cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, pixman_format_code_t format, int width, int height) { if (! has_shm(surface)) return NULL; return &_cairo_xlib_shm_surface_create (surface, format, width, height, FALSE, 0)->image.base; } cairo_surface_t * _cairo_xlib_surface_create_similar_shm (void *other, cairo_format_t format, int width, int height) { cairo_surface_t *surface; surface = _cairo_xlib_surface_create_shm (other, _cairo_format_to_pixman_format_code (format), width, height); if (surface) { if (! surface->is_clear) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; assert (shm->active == 0); memset (shm->image.data, 0, shm->image.stride * shm->image.height); shm->image.base.is_clear = TRUE; } } else surface = cairo_image_surface_create (format, width, height); return surface; } void _cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm; cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device; shm->active = NextRequest (display->display); } void _cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, XImage *ximage) { cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; cairo_format_masks_t image_masks; int ret; ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks); assert (ret); ximage->width = shm->image.width; ximage->height = shm->image.height; ximage->format = ZPixmap; ximage->data = (char *) shm->image.data; ximage->obdata = (char *)&shm->info->pool->shm; ximage->byte_order = native_byte_order; ximage->bitmap_unit = 32; /* always for libpixman */ ximage->bitmap_bit_order = native_byte_order; ximage->bitmap_pad = 32; /* always for libpixman */ ximage->depth = shm->image.depth; ximage->bytes_per_line = shm->image.stride; ximage->bits_per_pixel = image_masks.bpp; ximage->red_mask = image_masks.red_mask; ximage->green_mask = image_masks.green_mask; ximage->blue_mask = image_masks.blue_mask; ximage->xoffset = 0; ret = XInitImage (ximage); assert (ret != 0); } void * _cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) { cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device; cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; display->shm->last_event = shm->active = NextRequest (display->display); return &shm->info->pool->shm; } Pixmap _cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm; shm = (cairo_xlib_shm_surface_t *) surface; return shm->pixmap; } XRenderPictFormat * _cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm; shm = (cairo_xlib_shm_surface_t *) surface; if (shm->image.format != CAIRO_FORMAT_INVALID) return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device, shm->image.format); return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device, shm->image.pixman_format); } cairo_bool_t _cairo_xlib_shm_surface_is_active (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm; shm = (cairo_xlib_shm_surface_t *) surface; if (shm->active == 0) return FALSE; if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) { shm->active = 0; return FALSE; } return TRUE; } cairo_bool_t _cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) { cairo_xlib_shm_surface_t *shm; shm = (cairo_xlib_shm_surface_t *) surface; return shm->idle > 0; } #define XORG_VERSION_ENCODE(major,minor,patch,snap) \ (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap) static cairo_bool_t has_broken_send_shm_event (cairo_xlib_display_t *display, cairo_xlib_shm_display_t *shm) { Display *dpy = display->display; int (*old_handler) (Display *display, XErrorEvent *event); XShmCompletionEvent ev; XShmSegmentInfo info; info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); if (info.shmid == -1) return TRUE; info.readOnly = FALSE; info.shmaddr = shmat (info.shmid, NULL, 0); if (info.shmaddr == (char *) -1) { shmctl (info.shmid, IPC_RMID, NULL); return TRUE; } ev.type = shm->event; ev.send_event = 1; ev.serial = 1; ev.drawable = shm->window; ev.major_code = shm->opcode; ev.minor_code = X_ShmPutImage; ev.shmseg = info.shmid; ev.offset = 0; assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex)); _x_error_occurred = FALSE; XLockDisplay (dpy); XSync (dpy, False); old_handler = XSetErrorHandler (_check_error_handler); XShmAttach (dpy, &info); XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev); XShmDetach (dpy, &info); XSync (dpy, False); XSetErrorHandler (old_handler); XUnlockDisplay (dpy); shmctl (info.shmid, IPC_RMID, NULL); shmdt (info.shmaddr); return _x_error_occurred; } static cairo_bool_t xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display, cairo_xlib_shm_display_t *shm) { Display *dpy = display->display; /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent, * the Xserver may crash if it does not take care when processing * the event type. For instance versions of Xorg prior to 1.11.1 * exhibited this bug, and was fixed by: * * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39 * Author: Sam Spilsbury * Date: Wed Sep 14 09:58:34 2011 +0800 * * Remove the SendEvent bit (0x80) before doing range checks on event type. */ if (_cairo_xlib_vendor_is_xorg (dpy) && VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1)) return TRUE; /* For everyone else check that no error is generated */ return has_broken_send_shm_event (display, shm); } void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) { cairo_xlib_shm_display_t *shm; XSetWindowAttributes attr; XExtCodes *codes; int has_pixmap, scr; display->shm = NULL; if (!can_use_shm (display->display, &has_pixmap)) return; shm = malloc (sizeof (*shm)); if (unlikely (shm == NULL)) return; codes = XInitExtension (display->display, SHMNAME); if (codes == NULL) { free (shm); return; } shm->opcode = codes ->major_opcode; shm->event = codes->first_event; if (unlikely (_pqueue_init (&shm->info))) { free (shm); return; } scr = DefaultScreen (display->display); attr.override_redirect = 1; shm->window = XCreateWindow (display->display, DefaultRootWindow (display->display), -1, -1, 1, 1, 0, DefaultDepth (display->display, scr), InputOutput, DefaultVisual (display->display, scr), CWOverrideRedirect, &attr); shm->last_event = 0; shm->last_request = 0; if (xorg_has_buggy_send_shm_completion_event(display, shm)) has_pixmap = 0; shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0; cairo_list_init (&shm->pool); cairo_list_init (&shm->surfaces); display->shm = shm; } void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) { cairo_xlib_shm_display_t *shm = display->shm; if (shm == NULL) return; while (!cairo_list_is_empty (&shm->surfaces)) cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces, cairo_xlib_shm_surface_t, link)->image.base); _pqueue_fini (&shm->info); while (!cairo_list_is_empty (&shm->pool)) { cairo_xlib_shm_t *pool; pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link); _cairo_xlib_display_shm_pool_destroy (display, pool); } if (display->display) XDestroyWindow (display->display, shm->window); free (shm); display->shm = NULL; } #endif #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-surface.c000066400000000000000000002025421271037650300261300ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ /* Heed well the words of Owen Taylor: * "Any patch that works around a render bug, or claims to, without a * specific reference to the bug filed in bugzilla.freedesktop.org will * never pass approval." */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-compositor-private.h" #include "cairo-clip-private.h" #include "cairo-damage-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-list-inline.h" #include "cairo-pattern-private.h" #include "cairo-pixman-private.h" #include "cairo-region-private.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" #include /* for XDestroyImage */ #include #include #include #define XLIB_COORD_MAX 32767 #define DEBUG 0 #if DEBUG #define UNSUPPORTED(reason) \ fprintf (stderr, \ "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ __FUNCTION__, __LINE__, reason), \ CAIRO_INT_STATUS_UNSUPPORTED #else #define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED #endif #if DEBUG #include static void CAIRO_PRINTF_FORMAT (2, 3) _x_bread_crumb (Display *dpy, const char *fmt, ...) { xReq *req; char buf[2048]; unsigned int len, len_dwords; va_list ap; va_start (ap, fmt); len = vsnprintf (buf, sizeof (buf), fmt, ap); va_end (ap); buf[len++] = '\0'; while (len & 3) buf[len++] = '\0'; LockDisplay (dpy); GetEmptyReq (NoOperation, req); len_dwords = len >> 2; SetReqLen (req, len_dwords, len_dwords); Data (dpy, buf, len); UnlockDisplay (dpy); SyncHandle (); } #define X_DEBUG(x) _x_bread_crumb x #else #define X_DEBUG(x) #endif /** * SECTION:cairo-xlib * @Title: XLib Surfaces * @Short_Description: X Window System rendering using XLib * @See_Also: #cairo_surface_t * * The XLib surface is used to render cairo graphics to X Window System * windows and pixmaps using the XLib library. * * Note that the XLib surface automatically takes advantage of X render extension * if it is available. **/ /** * CAIRO_HAS_XLIB_SURFACE: * * Defined if the Xlib surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.0 **/ /** * SECTION:cairo-xlib-xrender * @Title: XLib-XRender Backend * @Short_Description: X Window System rendering using XLib and the X Render extension * @See_Also: #cairo_surface_t * * The XLib surface is used to render cairo graphics to X Window System * windows and pixmaps using the XLib and Xrender libraries. * * Note that the XLib surface automatically takes advantage of X Render extension * if it is available. **/ /** * CAIRO_HAS_XLIB_XRENDER_SURFACE: * * Defined if the XLib/XRender surface functions are available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.6 **/ /* Xlib doesn't define a typedef, so define one ourselves */ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); static cairo_surface_t * _cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, Drawable drawable, Visual *visual, XRenderPictFormat *xrender_format, int width, int height, int depth); static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface); /* * Instead of taking two round trips for each blending request, * assume that if a particular drawable fails GetImage that it will * fail for a "while"; use temporary pixmaps to avoid the errors */ #define CAIRO_ASSUME_PIXMAP 20 static const XTransform identity = { { { 1 << 16, 0x00000, 0x00000 }, { 0x00000, 1 << 16, 0x00000 }, { 0x00000, 0x00000, 1 << 16 }, } }; static Visual * _visual_for_xrender_format(Screen *screen, XRenderPictFormat *xrender_format) { int d, v; /* XXX Consider searching through the list of known cairo_visual_t for * the reverse mapping. */ for (d = 0; d < screen->ndepths; d++) { Depth *d_info = &screen->depths[d]; if (d_info->depth != xrender_format->depth) continue; for (v = 0; v < d_info->nvisuals; v++) { Visual *visual = &d_info->visuals[v]; switch (visual->class) { case TrueColor: if (xrender_format->type != PictTypeDirect) continue; break; case DirectColor: /* Prefer TrueColor to DirectColor. * (XRenderFindVisualFormat considers both TrueColor and DirectColor * Visuals to match the same PictFormat.) */ continue; case StaticGray: case GrayScale: case StaticColor: case PseudoColor: if (xrender_format->type != PictTypeIndexed) continue; break; } if (xrender_format == XRenderFindVisualFormat (DisplayOfScreen(screen), visual)) return visual; } } return NULL; } static cairo_content_t _xrender_format_to_content (XRenderPictFormat *xrender_format) { cairo_content_t content; /* This only happens when using a non-Render server. Let's punt * and say there's no alpha here. */ if (xrender_format == NULL) return CAIRO_CONTENT_COLOR; content = 0; if (xrender_format->direct.alphaMask) content |= CAIRO_CONTENT_ALPHA; if (xrender_format->direct.redMask | xrender_format->direct.greenMask | xrender_format->direct.blueMask) content |= CAIRO_CONTENT_COLOR; return content; } static cairo_surface_t * _cairo_xlib_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_xlib_surface_t *src = abstract_src; XRenderPictFormat *xrender_format; cairo_xlib_surface_t *surface; cairo_xlib_display_t *display; Pixmap pix; if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return NULL; if (width == 0 || height == 0) return NULL; if (_cairo_xlib_display_acquire (src->base.device, &display)) return NULL; /* If we never found an XRenderFormat or if it isn't compatible * with the content being requested, then we fallback to just * constructing a cairo_format_t instead, (which will fairly * arbitrarily pick a visual/depth for the similar surface. */ xrender_format = NULL; if (src->xrender_format && _xrender_format_to_content (src->xrender_format) == content) { xrender_format = src->xrender_format; } if (xrender_format == NULL) { xrender_format = _cairo_xlib_display_get_xrender_format (display, _cairo_format_from_content (content)); } if (xrender_format) { Visual *visual; /* We've got a compatible XRenderFormat now, which means the * similar surface will match the existing surface as closely in * visual/depth etc. as possible. */ pix = XCreatePixmap (display->display, src->drawable, width, height, xrender_format->depth); if (xrender_format == src->xrender_format) visual = src->visual; else visual = _visual_for_xrender_format(src->screen->screen, xrender_format); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (src->screen, pix, visual, xrender_format, width, height, xrender_format->depth); } else { Screen *screen = src->screen->screen; int depth; /* No compatible XRenderFormat, see if we can make an ordinary pixmap, * so that we can still accelerate blits with XCopyArea(). */ if (content != CAIRO_CONTENT_COLOR) { cairo_device_release (&display->base); return NULL; } depth = DefaultDepthOfScreen (screen); pix = XCreatePixmap (display->display, RootWindowOfScreen (screen), width <= 0 ? 1 : width, height <= 0 ? 1 : height, depth); surface = (cairo_xlib_surface_t *) _cairo_xlib_surface_create_internal (src->screen, pix, DefaultVisualOfScreen (screen), NULL, width, height, depth); } if (likely (surface->base.status == CAIRO_STATUS_SUCCESS)) surface->owns_pixmap = TRUE; else XFreePixmap (display->display, pix); cairo_device_release (&display->base); return &surface->base; } static void _cairo_xlib_surface_discard_shm (cairo_xlib_surface_t *surface) { if (surface->shm == NULL) return; /* Force the flush for an external surface */ if (!surface->owns_pixmap) cairo_surface_flush (surface->shm); cairo_surface_finish (surface->shm); cairo_surface_destroy (surface->shm); surface->shm = NULL; _cairo_damage_destroy (surface->base.damage); surface->base.damage = NULL; surface->fallback = 0; } static cairo_status_t _cairo_xlib_surface_finish (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; cairo_status_t status; cairo_xlib_display_t *display; cairo_list_del (&surface->link); status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return status; X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); if (surface->embedded_source.picture) XRenderFreePicture (display->display, surface->embedded_source.picture); if (surface->picture) XRenderFreePicture (display->display, surface->picture); _cairo_xlib_surface_discard_shm (surface); if (surface->owns_pixmap) XFreePixmap (display->display, surface->drawable); cairo_device_release (&display->base); return status; } cairo_status_t _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, GC *gc) { *gc = _cairo_xlib_screen_get_gc (display, surface->screen, surface->depth, surface->drawable); if (unlikely (*gc == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static int _noop_error_handler (Display *display, XErrorEvent *event) { return False; /* return value is ignored */ } static void _swap_ximage_2bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint16_t *p = (uint16_t *) line; for (i = ximage->width; i; i--) { *p = bswap_16 (*p); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_3bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint8_t *p = (uint8_t *) line; for (i = ximage->width; i; i--) { uint8_t tmp; tmp = p[2]; p[2] = p[0]; p[0] = tmp; p += 3; } line += ximage->bytes_per_line; } } static void _swap_ximage_4bytes (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint32_t *p = (uint32_t *) line; for (i = ximage->width; i; i--) { *p = bswap_32 (*p); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_nibbles (XImage *ximage) { int i, j; char *line = ximage->data; for (j = ximage->height; j; j--) { uint8_t *p = (uint8_t *) line; for (i = (ximage->width + 1) / 2; i; i--) { *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf); p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_bits (XImage *ximage) { int i, j; char *line = ximage->data; int unit = ximage->bitmap_unit; int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8; for (j = ximage->height; j; j--) { char *p = line; for (i = line_bytes; i; i--) { char b = *p; b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); *p = b; p++; } line += ximage->bytes_per_line; } } static void _swap_ximage_to_native (XImage *ximage) { int unit_bytes = 0; int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; if (ximage->bits_per_pixel == 1 && ximage->bitmap_bit_order != native_byte_order) { _swap_ximage_bits (ximage); if (ximage->bitmap_bit_order == ximage->byte_order) return; } if (ximage->byte_order == native_byte_order) return; switch (ximage->bits_per_pixel) { case 1: unit_bytes = ximage->bitmap_unit / 8; break; case 4: _swap_ximage_nibbles (ximage); /* fall-through */ case 8: case 16: case 20: case 24: case 28: case 30: case 32: unit_bytes = (ximage->bits_per_pixel + 7) / 8; break; default: /* This could be hit on some rare but possible cases. */ ASSERT_NOT_REACHED; } switch (unit_bytes) { case 1: break; case 2: _swap_ximage_2bytes (ximage); break; case 3: _swap_ximage_3bytes (ximage); break; case 4: _swap_ximage_4bytes (ximage); break; default: ASSERT_NOT_REACHED; } } /* Given a mask, (with a single sequence of contiguous 1 bits), return * the number of 1 bits in 'width' and the number of 0 bits to its * right in 'shift'. */ static void _characterize_field (uint32_t mask, int *width, int *shift) { *width = _cairo_popcount (mask); /* The final '& 31' is to force a 0 mask to result in 0 shift. */ *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; } /* Convert a field of 'width' bits to 'new_width' bits with correct * rounding. */ static inline uint32_t _resize_field (uint32_t field, int width, int new_width) { if (width == 0) return 0; if (width >= new_width) { return field >> (width - new_width); } else { uint32_t result = field << (new_width - width); while (width < new_width) { result |= result >> width; width <<= 1; } return result; } } static inline uint32_t _adjust_field (uint32_t field, int adjustment) { return MIN (255, MAX(0, (int)field + adjustment)); } /* Given a shifted field value, (described by 'width' and 'shift), * resize it 8-bits and return that value. * * Note that the original field value must not have any non-field bits * set. */ static inline uint32_t _field_to_8 (uint32_t field, int width, int shift) { return _resize_field (field >> shift, width, 8); } static inline uint32_t _field_to_8_undither (uint32_t field, int width, int shift, int dither_adjustment) { return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width); } /* Given an 8-bit value, convert it to a field of 'width', shift it up * to 'shift, and return it. */ static inline uint32_t _field_from_8 (uint32_t field, int width, int shift) { return _resize_field (field, 8, width) << shift; } static inline uint32_t _field_from_8_dither (uint32_t field, int width, int shift, int8_t dither_adjustment) { return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift); } static inline uint32_t _pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info, uint32_t r, uint32_t g, uint32_t b, int8_t dither_adjustment) { if (r == g && g == b) { dither_adjustment /= RAMP_SIZE; return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)]; } else { dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128]; return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]] [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]] [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]]; } } static inline uint32_t _pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, uint32_t pixel) { uint32_t r, g, b; pixel &= 0xff; r = visual_info->colors[pixel].r; g = visual_info->colors[pixel].g; b = visual_info->colors[pixel].b; return (r << 16) | (g << 8) | (b ); } /* should range from -128 to 127 */ #define X 16 static const int8_t dither_pattern[4][4] = { {-8*X, +0*X, -6*X, +2*X}, {+4*X, -4*X, +6*X, -2*X}, {-5*X, +4*X, -7*X, +1*X}, {+7*X, -1*X, +5*X, -3*X} }; #undef X static int bits_per_pixel(cairo_xlib_surface_t *surface) { if (surface->depth > 16) return 32; else if (surface->depth > 8) return 16; else if (surface->depth > 1) return 8; else return 1; } pixman_format_code_t _pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface) { cairo_format_masks_t masks; pixman_format_code_t format; masks.bpp = bits_per_pixel (surface); masks.alpha_mask = surface->a_mask; masks.red_mask = surface->r_mask; masks.green_mask = surface->g_mask; masks.blue_mask = surface->b_mask; if (! _pixman_format_from_masks (&masks, &format)) return 0; return format; } static cairo_surface_t * _get_image_surface (cairo_xlib_surface_t *surface, const cairo_rectangle_int_t *extents, int try_shm) { cairo_int_status_t status; cairo_image_surface_t *image = NULL; XImage *ximage; pixman_format_code_t pixman_format; cairo_xlib_display_t *display; assert (extents->x >= 0); assert (extents->y >= 0); assert (extents->x + extents->width <= surface->width); assert (extents->y + extents->height <= surface->height); if (surface->base.is_clear || (surface->base.serial == 0 && surface->owns_pixmap)) { pixman_format = _pixman_format_for_xlib_surface (surface); if (pixman_format) { return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, extents->width, extents->height, 0); } } if (surface->shm) { cairo_image_surface_t *src = (cairo_image_surface_t *) surface->shm; cairo_surface_t *dst; cairo_surface_pattern_t pattern; dst = cairo_image_surface_create (src->format, extents->width, extents->height); if (unlikely (dst->status)) return dst; _cairo_pattern_init_for_surface (&pattern, &src->base); cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); status = _cairo_surface_paint (dst, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (dst); dst = _cairo_surface_create_in_error (status); } return dst; } status = _cairo_xlib_display_acquire (surface->base.device, &display); if (status) return _cairo_surface_create_in_error (status); pixman_format = _pixman_format_for_xlib_surface (surface); if (try_shm && pixman_format) { image = (cairo_image_surface_t *) _cairo_xlib_surface_create_shm__image (surface, pixman_format, extents->width, extents->height); if (image && image->base.status == CAIRO_STATUS_SUCCESS) { cairo_xlib_error_func_t old_handler; XImage shm_image; Bool success; _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image); old_handler = XSetErrorHandler (_noop_error_handler); success = XShmGetImage (display->display, surface->drawable, &shm_image, extents->x, extents->y, AllPlanes); XSetErrorHandler (old_handler); if (success) { cairo_device_release (&display->base); return &image->base; } cairo_surface_destroy (&image->base); } } if (surface->use_pixmap == 0) { cairo_xlib_error_func_t old_handler; old_handler = XSetErrorHandler (_noop_error_handler); ximage = XGetImage (display->display, surface->drawable, extents->x, extents->y, extents->width, extents->height, AllPlanes, ZPixmap); XSetErrorHandler (old_handler); /* If we get an error, the surface must have been a window, * so retry with the safe code path. */ if (!ximage) surface->use_pixmap = CAIRO_ASSUME_PIXMAP; } else { surface->use_pixmap--; ximage = NULL; } if (ximage == NULL) { /* XGetImage from a window is dangerous because it can * produce errors if the window is unmapped or partially * outside the screen. We could check for errors and * retry, but to keep things simple, we just create a * temporary pixmap */ Pixmap pixmap; GC gc; status = _cairo_xlib_surface_get_gc (display, surface, &gc); if (unlikely (status)) goto BAIL; pixmap = XCreatePixmap (display->display, surface->drawable, extents->width, extents->height, surface->depth); if (pixmap) { XGCValues gcv; gcv.subwindow_mode = IncludeInferiors; XChangeGC (display->display, gc, GCSubwindowMode, &gcv); XCopyArea (display->display, surface->drawable, pixmap, gc, extents->x, extents->y, extents->width, extents->height, 0, 0); gcv.subwindow_mode = ClipByChildren; XChangeGC (display->display, gc, GCSubwindowMode, &gcv); ximage = XGetImage (display->display, pixmap, 0, 0, extents->width, extents->height, AllPlanes, ZPixmap); XFreePixmap (display->display, pixmap); } _cairo_xlib_surface_put_gc (display, surface, gc); if (ximage == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } } _swap_ximage_to_native (ximage); /* We can't use pixman to simply write to image if: * (a) the pixels are not appropriately aligned, * (b) pixman does not the pixel format, or * (c) if the image is palettized and we need to convert. */ if (pixman_format && ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && (surface->visual == NULL || surface->visual->class == TrueColor)) { image = (cairo_image_surface_t*) _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, pixman_format, ximage->width, ximage->height, ximage->bytes_per_line); status = image->base.status; if (unlikely (status)) goto BAIL; /* Let the surface take ownership of the data */ _cairo_image_surface_assume_ownership_of_data (image); ximage->data = NULL; } else { /* The visual we are dealing with is not supported by the * standard pixman formats. So we must first convert the data * to a supported format. */ cairo_format_t format; unsigned char *data; uint32_t *row; uint32_t in_pixel, out_pixel; unsigned int rowstride; uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0; int a_width=0, r_width=0, g_width=0, b_width=0; int a_shift=0, r_shift=0, g_shift=0, b_shift=0; int x, y, x0, y0, x_off, y_off; cairo_xlib_visual_info_t *visual_info = NULL; if (surface->visual == NULL || surface->visual->class == TrueColor) { cairo_bool_t has_alpha; cairo_bool_t has_color; has_alpha = surface->a_mask; has_color = (surface->r_mask || surface->g_mask || surface->b_mask); if (has_color) { if (has_alpha) { format = CAIRO_FORMAT_ARGB32; } else { format = CAIRO_FORMAT_RGB24; } } else { /* XXX: Using CAIRO_FORMAT_A8 here would be more * efficient, but would require slightly different code in * the image conversion to put the alpha channel values * into the right place. */ format = CAIRO_FORMAT_ARGB32; } a_mask = surface->a_mask; r_mask = surface->r_mask; g_mask = surface->g_mask; b_mask = surface->b_mask; _characterize_field (a_mask, &a_width, &a_shift); _characterize_field (r_mask, &r_width, &r_shift); _characterize_field (g_mask, &g_width, &g_shift); _characterize_field (b_mask, &b_width, &b_shift); } else { format = CAIRO_FORMAT_RGB24; status = _cairo_xlib_screen_get_visual_info (display, surface->screen, surface->visual, &visual_info); if (unlikely (status)) goto BAIL; } image = (cairo_image_surface_t *) cairo_image_surface_create (format, ximage->width, ximage->height); status = image->base.status; if (unlikely (status)) goto BAIL; data = cairo_image_surface_get_data (&image->base); rowstride = cairo_image_surface_get_stride (&image->base) >> 2; row = (uint32_t *) data; x0 = extents->x + surface->base.device_transform.x0; y0 = extents->y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); y < ximage->height; y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { const int8_t *dither_row = dither_pattern[y_off]; for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); x < ximage->width; x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { int dither_adjustment = dither_row[x_off]; in_pixel = XGetPixel (ximage, x, y); if (visual_info == NULL) { out_pixel = ( _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 | _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment)); } else { /* Undithering pseudocolor does not look better */ out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel); } row[x] = out_pixel; } row += rowstride; } cairo_surface_mark_dirty (&image->base); } BAIL: if (ximage) XDestroyImage (ximage); cairo_device_release (&display->base); if (unlikely (status)) { cairo_surface_destroy (&image->base); return _cairo_surface_create_in_error (status); } return &image->base; } void _cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface, cairo_antialias_t antialias) { cairo_xlib_display_t *display = surface->display; int precision; if (display->force_precision != -1) precision = display->force_precision; else switch (antialias) { default: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_NONE: case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GOOD: precision = PolyModeImprecise; break; case CAIRO_ANTIALIAS_BEST: case CAIRO_ANTIALIAS_SUBPIXEL: precision = PolyModePrecise; break; } if (surface->precision != precision) { XRenderPictureAttributes pa; pa.poly_mode = precision; XRenderChangePicture (display->display, surface->picture, CPPolyMode, &pa); surface->precision = precision; } } void _cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface) { cairo_xlib_display_t *display = surface->display; XRenderPictureAttributes pa; int mask = 0; if (surface->picture) return; if (display->force_precision != -1) pa.poly_mode = display->force_precision; else pa.poly_mode = PolyModeImprecise; if (pa.poly_mode) mask |= CPPolyMode; surface->precision = pa.poly_mode; surface->picture = XRenderCreatePicture (display->display, surface->drawable, surface->xrender_format, mask, &pa); } cairo_status_t _cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface, cairo_image_surface_t *image, int src_x, int src_y, int width, int height, int dst_x, int dst_y) { cairo_xlib_display_t *display; XImage ximage; cairo_format_masks_t image_masks; int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; cairo_surface_t *shm_image = NULL; pixman_image_t *pixman_image = NULL; cairo_status_t status; cairo_bool_t own_data = FALSE; cairo_bool_t is_rgb_image; GC gc; ximage.width = image->width; ximage.height = image->height; ximage.format = ZPixmap; ximage.byte_order = native_byte_order; ximage.bitmap_unit = 32; /* always for libpixman */ ximage.bitmap_bit_order = native_byte_order; ximage.bitmap_pad = 32; /* always for libpixman */ ximage.depth = surface->depth; ximage.red_mask = surface->r_mask; ximage.green_mask = surface->g_mask; ximage.blue_mask = surface->b_mask; ximage.xoffset = 0; ximage.obdata = NULL; status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return status; is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks); if (is_rgb_image && (image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) { int ret; ximage.bits_per_pixel = image_masks.bpp; ximage.bytes_per_line = image->stride; ximage.data = (char *)image->data; if (image->base.device != surface->base.device) { /* If PutImage will break the image up into chunks, prefer to * send it all in one pass with ShmPutImage. For larger images, * it is further advantageous to reduce the number of copies, * albeit at the expense of more SHM bookkeeping. */ int max_request_size = XExtendedMaxRequestSize (display->display); if (max_request_size == 0) max_request_size = XMaxRequestSize (display->display); if (max_request_size > 8192) max_request_size = 8192; if (width * height * 4 > max_request_size) { shm_image = _cairo_xlib_surface_create_shm__image (surface, image->pixman_format, width, height); if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) { cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image; pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, clone->pixman_image, src_x, src_y, 0, 0, 0, 0, width, height); ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image); ximage.data = (char *)clone->data; ximage.bytes_per_line = clone->stride; ximage.width = width; ximage.height = height; src_x = src_y = 0; } } } else ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base); ret = XInitImage (&ximage); assert (ret != 0); } else if (surface->visual == NULL || surface->visual->class == TrueColor) { pixman_format_code_t intermediate_format; int ret; image_masks.alpha_mask = surface->a_mask; image_masks.red_mask = surface->r_mask; image_masks.green_mask = surface->g_mask; image_masks.blue_mask = surface->b_mask; image_masks.bpp = bits_per_pixel (surface); ret = _pixman_format_from_masks (&image_masks, &intermediate_format); assert (ret); shm_image = _cairo_xlib_surface_create_shm__image (surface, intermediate_format, width, height); if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) { cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image; pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, clone->pixman_image, src_x, src_y, 0, 0, 0, 0, width, height); ximage.data = (char *) clone->data; ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base); ximage.bytes_per_line = clone->stride; } else { pixman_image = pixman_image_create_bits (intermediate_format, width, height, NULL, 0); if (pixman_image == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, pixman_image, src_x, src_y, 0, 0, 0, 0, width, height); ximage.data = (char *) pixman_image_get_data (pixman_image); ximage.bytes_per_line = pixman_image_get_stride (pixman_image); } ximage.width = width; ximage.height = height; ximage.bits_per_pixel = image_masks.bpp; ret = XInitImage (&ximage); assert (ret != 0); src_x = src_y = 0; } else { unsigned int stride, rowstride; int x, y, x0, y0, x_off, y_off; uint32_t in_pixel, out_pixel, *row; int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0; int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0; int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0; int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0; cairo_xlib_visual_info_t *visual_info = NULL; cairo_bool_t true_color; int ret; ximage.bits_per_pixel = bits_per_pixel(surface); stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, ximage.bits_per_pixel); ximage.bytes_per_line = stride; ximage.data = _cairo_malloc_ab (stride, ximage.height); if (unlikely (ximage.data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } own_data = TRUE; ret = XInitImage (&ximage); assert (ret != 0); _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift); _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift); _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift); _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift); true_color = surface->visual == NULL || surface->visual->class == TrueColor; if (true_color) { _characterize_field (surface->a_mask, &o_a_width, &o_a_shift); _characterize_field (surface->r_mask, &o_r_width, &o_r_shift); _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); } else { status = _cairo_xlib_screen_get_visual_info (display, surface->screen, surface->visual, &visual_info); if (unlikely (status)) goto BAIL; } rowstride = image->stride >> 2; row = (uint32_t *) image->data; x0 = dst_x + surface->base.device_transform.x0; y0 = dst_y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); y < ximage.height; y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { const int8_t *dither_row = dither_pattern[y_off]; for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); x < ximage.width; x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { int dither_adjustment = dither_row[x_off]; int a, r, g, b; if (image_masks.bpp == 1) in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); else if (image_masks.bpp <= 8) in_pixel = ((uint8_t*)row)[x]; else if (image_masks.bpp <= 16) in_pixel = ((uint16_t*)row)[x]; else if (image_masks.bpp <= 24) #ifdef WORDS_BIGENDIAN in_pixel = ((uint8_t*)row)[3 * x] << 16 | ((uint8_t*)row)[3 * x + 1] << 8 | ((uint8_t*)row)[3 * x + 2]; #else in_pixel = ((uint8_t*)row)[3 * x] | ((uint8_t*)row)[3 * x + 1] << 8 | ((uint8_t*)row)[3 * x + 2] << 16; #endif else in_pixel = row[x]; /* If the incoming image has no alpha channel, then the input * is opaque and the output should have the maximum alpha value. * For all other channels, their absence implies 0. */ if (image_masks.alpha_mask == 0x0) a = 0xff; else a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift); r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift); g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift); b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift); if (true_color) { out_pixel = _field_from_8 (a, o_a_width, o_a_shift) | _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) | _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) | _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment); } else { out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment); } XPutPixel (&ximage, x, y, out_pixel); } row += rowstride; } } status = _cairo_xlib_surface_get_gc (display, surface, &gc); if (unlikely (status)) goto BAIL; if (ximage.obdata) XShmPutImage (display->display, surface->drawable, gc, &ximage, src_x, src_y, dst_x, dst_y, width, height, True); else XPutImage (display->display, surface->drawable, gc, &ximage, src_x, src_y, dst_x, dst_y, width, height); _cairo_xlib_surface_put_gc (display, surface, gc); BAIL: cairo_device_release (&display->base); if (own_data) free (ximage.data); if (shm_image) cairo_surface_destroy (shm_image); if (pixman_image) pixman_image_unref (pixman_image); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_xlib_surface_source(void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xlib_surface_t *surface = abstract_surface; if (extents) { extents->x = extents->y = 0; extents->width = surface->width; extents->height = surface->height; } return &surface->base; } static cairo_status_t _cairo_xlib_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xlib_surface_t *surface = abstract_surface; cairo_rectangle_int_t extents; *image_extra = NULL; *image_out = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (abstract_surface, FALSE); if (*image_out) return (*image_out)->base.status; extents.x = extents.y = 0; extents.width = surface->width; extents.height = surface->height; *image_out = (cairo_image_surface_t*) _get_image_surface (surface, &extents, TRUE); return (*image_out)->base.status; } static cairo_surface_t * _cairo_xlib_surface_snapshot (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; cairo_rectangle_int_t extents; extents.x = extents.y = 0; extents.width = surface->width; extents.height = surface->height; return _get_image_surface (surface, &extents, FALSE); } static void _cairo_xlib_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_xlib_surface_t *surface = abstract_surface; if (&image->base == surface->shm) return; cairo_surface_destroy (&image->base); } static cairo_image_surface_t * _cairo_xlib_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_xlib_surface_t *surface = abstract_surface; cairo_surface_t *image; image = _cairo_xlib_surface_get_shm (abstract_surface, FALSE); if (image) { assert (surface->base.damage); surface->fallback++; return _cairo_image_surface_map_to_image (image, extents); } image = _get_image_surface (abstract_surface, extents, TRUE); cairo_surface_set_device_offset (image, -extents->x, -extents->y); return (cairo_image_surface_t *) image; } static cairo_int_status_t _cairo_xlib_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_xlib_surface_t *surface = abstract_surface; cairo_int_status_t status; if (surface->shm) { cairo_rectangle_int_t r; assert (surface->fallback); assert (surface->base.damage); r.x = image->base.device_transform_inverse.x0; r.y = image->base.device_transform_inverse.y0; r.width = image->width; r.height = image->height; TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n", __FUNCTION__, r.x, r.y, r.width, r.height)); surface->shm->damage = _cairo_damage_add_rectangle (surface->shm->damage, &r); return _cairo_image_surface_unmap_image (surface->shm, image); } status = _cairo_xlib_surface_draw_image (abstract_surface, image, 0, 0, image->width, image->height, image->base.device_transform_inverse.x0, image->base.device_transform_inverse.y0); cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); return status; } static cairo_status_t _cairo_xlib_surface_flush (void *abstract_surface, unsigned flags) { cairo_xlib_surface_t *surface = abstract_surface; cairo_int_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; status = _cairo_xlib_surface_put_shm (surface); if (unlikely (status)) return status; surface->fallback >>= 1; if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm)) _cairo_xlib_surface_discard_shm (surface); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_xlib_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_xlib_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static void _cairo_xlib_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_xlib_surface_t *surface = abstract_surface; *options = *_cairo_xlib_screen_get_font_options (surface->screen); } static inline cairo_int_status_t get_compositor (cairo_xlib_surface_t **surface, const cairo_compositor_t **compositor) { cairo_xlib_surface_t *s = *surface; cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;; if (s->fallback) { assert (s->base.damage != NULL); assert (s->shm != NULL); assert (s->shm->damage != NULL); if (! _cairo_xlib_shm_surface_is_active (s->shm)) { *surface = (cairo_xlib_surface_t *) s->shm; *compositor = ((cairo_image_surface_t *) s->shm)->compositor; s->fallback++; } else { status = _cairo_xlib_surface_put_shm (s); s->fallback = 0; *compositor = s->compositor; } } else *compositor = s->compositor; return status; } static cairo_int_status_t _cairo_xlib_surface_paint (void *_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_xlib_surface_t *surface = _surface; const cairo_compositor_t *compositor; cairo_int_status_t status; status = get_compositor (&surface, &compositor); if (unlikely (status)) return status; return _cairo_compositor_paint (compositor, &surface->base, op, source, clip); } static cairo_int_status_t _cairo_xlib_surface_mask (void *_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_xlib_surface_t *surface = _surface; const cairo_compositor_t *compositor; cairo_int_status_t status; status = get_compositor (&surface, &compositor); if (unlikely (status)) return status; return _cairo_compositor_mask (compositor, &surface->base, op, source, mask, clip); } static cairo_int_status_t _cairo_xlib_surface_stroke (void *_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xlib_surface_t *surface = _surface; const cairo_compositor_t *compositor; cairo_int_status_t status; status = get_compositor (&surface, &compositor); if (unlikely (status)) return status; return _cairo_compositor_stroke (compositor, &surface->base, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xlib_surface_fill (void *_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xlib_surface_t *surface = _surface; const cairo_compositor_t *compositor; cairo_int_status_t status; status = get_compositor (&surface, &compositor); if (unlikely (status)) return status; return _cairo_compositor_fill (compositor, &surface->base, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xlib_surface_glyphs (void *_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_xlib_surface_t *surface = _surface; const cairo_compositor_t *compositor; cairo_int_status_t status; status = get_compositor (&surface, &compositor); if (unlikely (status)) return status; return _cairo_compositor_glyphs (compositor, &surface->base, op, source, glyphs, num_glyphs, scaled_font, clip); } static const cairo_surface_backend_t cairo_xlib_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_surface_finish, _cairo_default_context_create, _cairo_xlib_surface_create_similar, _cairo_xlib_surface_create_similar_shm, _cairo_xlib_surface_map_to_image, _cairo_xlib_surface_unmap_image, _cairo_xlib_surface_source, _cairo_xlib_surface_acquire_source_image, _cairo_xlib_surface_release_source_image, _cairo_xlib_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_xlib_surface_get_extents, _cairo_xlib_surface_get_font_options, _cairo_xlib_surface_flush, NULL, /* mark_dirty_rectangle */ _cairo_xlib_surface_paint, _cairo_xlib_surface_mask, _cairo_xlib_surface_stroke, _cairo_xlib_surface_fill, NULL, /* fill-stroke */ _cairo_xlib_surface_glyphs, }; /** * _cairo_surface_is_xlib: * @surface: a #cairo_surface_t * * Checks if a surface is a #cairo_xlib_surface_t * * Return value: True if the surface is an xlib surface **/ static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface) { return surface->backend == &cairo_xlib_surface_backend; } static cairo_surface_t * _cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, Drawable drawable, Visual *visual, XRenderPictFormat *xrender_format, int width, int height, int depth) { cairo_xlib_surface_t *surface; cairo_xlib_display_t *display; cairo_status_t status; if (depth == 0) { if (xrender_format) { depth = xrender_format->depth; /* XXX find matching visual for core/dithering fallbacks? */ } else if (visual) { Screen *scr = screen->screen; if (visual == DefaultVisualOfScreen (scr)) { depth = DefaultDepthOfScreen (scr); } else { int j, k; /* This is ugly, but we have to walk over all visuals * for the display to find the correct depth. */ depth = 0; for (j = 0; j < scr->ndepths; j++) { Depth *d = &scr->depths[j]; for (k = 0; k < d->nvisuals; k++) { if (&d->visuals[k] == visual) { depth = d->depth; goto found; } } } } } if (depth == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); found: ; } surface = malloc (sizeof (cairo_xlib_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); status = _cairo_xlib_display_acquire (screen->device, &display); if (unlikely (status)) { free (surface); return _cairo_surface_create_in_error (_cairo_error (status)); } surface->display = display; if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) { if (!xrender_format) { if (visual) { xrender_format = XRenderFindVisualFormat (display->display, visual); } else if (depth == 1) { xrender_format = _cairo_xlib_display_get_xrender_format (display, CAIRO_FORMAT_A1); } } } cairo_device_release (&display->base); _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend, screen->device, _xrender_format_to_content (xrender_format)); surface->screen = screen; surface->compositor = display->compositor; surface->shm = NULL; surface->fallback = 0; surface->drawable = drawable; surface->owns_pixmap = FALSE; surface->use_pixmap = 0; surface->width = width; surface->height = height; surface->picture = None; surface->precision = PolyModePrecise; surface->embedded_source.picture = None; surface->visual = visual; surface->xrender_format = xrender_format; surface->depth = depth; /* * Compute the pixel format masks from either a XrenderFormat or * else from a visual; failing that we assume the drawable is an * alpha-only pixmap as it could only have been created that way * through the cairo_xlib_surface_create_for_bitmap function. */ if (xrender_format) { surface->a_mask = (unsigned long) surface->xrender_format->direct.alphaMask << surface->xrender_format->direct.alpha; surface->r_mask = (unsigned long) surface->xrender_format->direct.redMask << surface->xrender_format->direct.red; surface->g_mask = (unsigned long) surface->xrender_format->direct.greenMask << surface->xrender_format->direct.green; surface->b_mask = (unsigned long) surface->xrender_format->direct.blueMask << surface->xrender_format->direct.blue; } else if (visual) { surface->a_mask = 0; surface->r_mask = visual->red_mask; surface->g_mask = visual->green_mask; surface->b_mask = visual->blue_mask; } else { if (depth < 32) surface->a_mask = (1 << depth) - 1; else surface->a_mask = 0xffffffff; surface->r_mask = 0; surface->g_mask = 0; surface->b_mask = 0; } cairo_list_add (&surface->link, &screen->surfaces); return &surface->base; } static Screen * _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { int s, d, v; for (s = 0; s < ScreenCount (dpy); s++) { Screen *screen; screen = ScreenOfDisplay (dpy, s); if (visual == DefaultVisualOfScreen (screen)) return screen; for (d = 0; d < screen->ndepths; d++) { Depth *depth; depth = &screen->depths[d]; for (v = 0; v < depth->nvisuals; v++) if (visual == &depth->visuals[v]) return screen; } } return NULL; } static cairo_bool_t valid_size (int width, int height) { /* Note: the minimum surface size allowed in the X protocol is 1x1. * However, as we historically did not check the minimum size we * allowed applications to lie and set the correct size later (one hopes). * To preserve compatability we must allow applications to use * 0x0 surfaces. */ return (width >= 0 && width <= XLIB_COORD_MAX && height >= 0 && height <= XLIB_COORD_MAX); } /** * cairo_xlib_surface_create: * @dpy: an X Display * @drawable: an X Drawable, (a Pixmap or a Window) * @visual: the visual to use for drawing to @drawable. The depth * of the visual must match the depth of the drawable. * Currently, only TrueColor visuals are fully supported. * @width: the current width of @drawable. * @height: the current height of @drawable. * * Creates an Xlib surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided visual. * * Note: If @drawable is a Window, then the function * cairo_xlib_surface_set_size() must be called whenever the size of the * window changes. * * When @drawable is a Window containing child windows then drawing to * the created surface will be clipped by those child windows. When * the created surface is used as a source, the contents of the * children will be included. * * Return value: the newly created surface * * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create (Display *dpy, Drawable drawable, Visual *visual, int width, int height) { Screen *scr; cairo_xlib_screen_t *screen; cairo_status_t status; if (! valid_size (width, height)) { /* you're lying, and you know it! */ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } scr = _cairo_xlib_screen_from_visual (dpy, visual); if (scr == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) return _cairo_surface_create_in_error (status); X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); return _cairo_xlib_surface_create_internal (screen, drawable, visual, NULL, width, height, 0); } /** * cairo_xlib_surface_create_for_bitmap: * @dpy: an X Display * @bitmap: an X Drawable, (a depth-1 Pixmap) * @screen: the X Screen associated with @bitmap * @width: the current width of @bitmap. * @height: the current height of @bitmap. * * Creates an Xlib surface that draws to the given bitmap. * This will be drawn to as a %CAIRO_FORMAT_A1 object. * * Return value: the newly created surface * * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, Screen *scr, int width, int height) { cairo_xlib_screen_t *screen; cairo_status_t status; if (! valid_size (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) return _cairo_surface_create_in_error (status); X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); return _cairo_xlib_surface_create_internal (screen, bitmap, NULL, NULL, width, height, 1); } #if CAIRO_HAS_XLIB_XRENDER_SURFACE /** * cairo_xlib_surface_create_with_xrender_format: * @dpy: an X Display * @drawable: an X Drawable, (a Pixmap or a Window) * @screen: the X Screen associated with @drawable * @format: the picture format to use for drawing to @drawable. The depth * of @format must match the depth of the drawable. * @width: the current width of @drawable. * @height: the current height of @drawable. * * Creates an Xlib surface that draws to the given drawable. * The way that colors are represented in the drawable is specified * by the provided picture format. * * Note: If @drawable is a Window, then the function * cairo_xlib_surface_set_size() must be called whenever the size of the * window changes. * * Return value: the newly created surface * * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, Screen *scr, XRenderPictFormat *format, int width, int height) { cairo_xlib_screen_t *screen; cairo_status_t status; if (! valid_size (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) return _cairo_surface_create_in_error (status); X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); return _cairo_xlib_surface_create_internal (screen, drawable, _visual_for_xrender_format (scr, format), format, width, height, 0); } /** * cairo_xlib_surface_get_xrender_format: * @surface: an xlib surface * * Gets the X Render picture format that @surface uses for rendering with the * X Render extension. If the surface was created by * cairo_xlib_surface_create_with_xrender_format() originally, the return * value is the format passed to that constructor. * * Return value: the XRenderPictFormat* associated with @surface, * or %NULL if the surface is not an xlib surface * or if the X Render extension is not available. * * Since: 1.6 **/ XRenderPictFormat * cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) { cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; /* Throw an error for a non-xlib surface */ if (! _cairo_surface_is_xlib (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return xlib_surface->xrender_format; } #endif /** * cairo_xlib_surface_set_size: * @surface: a #cairo_surface_t for the XLib backend * @width: the new width of the surface * @height: the new height of the surface * * Informs cairo of the new size of the X Drawable underlying the * surface. For a surface created for a Window (rather than a Pixmap), * this function must be called each time the size of the window * changes. (For a subwindow, you are normally resizing the window * yourself, but for a toplevel window, it is necessary to listen for * ConfigureNotify events.) * * A Pixmap can never change size, so it is never necessary to call * this function on a surface created for a Pixmap. * * Since: 1.0 **/ void cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (surface->width == width && surface->height == height) return; if (! valid_size (width, height)) { _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } status = _cairo_surface_flush (abstract_surface, 0); if (unlikely (status)) { _cairo_surface_set_error (abstract_surface, status); return; } _cairo_xlib_surface_discard_shm (surface); surface->width = width; surface->height = height; } /** * cairo_xlib_surface_set_drawable: * @surface: a #cairo_surface_t for the XLib backend * @drawable: the new drawable for the surface * @width: the width of the new drawable * @height: the height of the new drawable * * Informs cairo of a new X Drawable underlying the * surface. The drawable must match the display, screen * and format of the existing drawable or the application * will get X protocol errors and will probably terminate. * No checks are done by this function to ensure this * compatibility. * * Since: 1.0 **/ void cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, Drawable drawable, int width, int height) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_xlib (abstract_surface)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } if (! valid_size (width, height)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } /* XXX: and what about this case? */ if (surface->owns_pixmap) return; status = _cairo_surface_flush (abstract_surface, 0); if (unlikely (status)) { _cairo_surface_set_error (abstract_surface, status); return; } if (surface->drawable != drawable) { cairo_xlib_display_t *display; status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return; X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); if (surface->picture != None) { XRenderFreePicture (display->display, surface->picture); if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } surface->picture = None; } cairo_device_release (&display->base); surface->drawable = drawable; } if (surface->width != width || surface->height != height) { _cairo_xlib_surface_discard_shm (surface); surface->width = width; surface->height = height; } } /** * cairo_xlib_surface_get_display: * @surface: a #cairo_xlib_surface_t * * Get the X Display for the underlying X Drawable. * * Return value: the display. * * Since: 1.2 **/ Display * cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) { if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return ((cairo_xlib_display_t *) abstract_surface->device)->display; } /** * cairo_xlib_surface_get_drawable: * @surface: a #cairo_xlib_surface_t * * Get the underlying X Drawable used for the surface. * * Return value: the drawable. * * Since: 1.2 **/ Drawable cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->drawable; } /** * cairo_xlib_surface_get_screen: * @surface: a #cairo_xlib_surface_t * * Get the X Screen for the underlying X Drawable. * * Return value: the screen. * * Since: 1.2 **/ Screen * cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->screen->screen; } /** * cairo_xlib_surface_get_visual: * @surface: a #cairo_xlib_surface_t * * Gets the X Visual associated with @surface, suitable for use with the * underlying X Drawable. If @surface was created by * cairo_xlib_surface_create(), the return value is the Visual passed to that * constructor. * * Return value: the Visual or %NULL if there is no appropriate Visual for * @surface. * * Since: 1.2 **/ Visual * cairo_xlib_surface_get_visual (cairo_surface_t *surface) { cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; if (! _cairo_surface_is_xlib (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return xlib_surface->visual; } /** * cairo_xlib_surface_get_depth: * @surface: a #cairo_xlib_surface_t * * Get the number of bits used to represent each pixel value. * * Return value: the depth of the surface in bits. * * Since: 1.2 **/ int cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->depth; } /** * cairo_xlib_surface_get_width: * @surface: a #cairo_xlib_surface_t * * Get the width of the X Drawable underlying the surface in pixels. * * Return value: the width of the surface in pixels. * * Since: 1.2 **/ int cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->width; } /** * cairo_xlib_surface_get_height: * @surface: a #cairo_xlib_surface_t * * Get the height of the X Drawable underlying the surface in pixels. * * Return value: the height of the surface in pixels. * * Since: 1.2 **/ int cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) { cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; if (! _cairo_surface_is_xlib (abstract_surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->height; } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-visual.c000066400000000000000000000144021271037650300257770ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-error-private.h" #include "cairo-list-inline.h" /* A perceptual distance metric between two colors. No sqrt needed * since the square of the distance is still a valid metric. */ /* XXX: This is currently using linear distance in RGB space which is * decidedly not perceptually linear. If someone cared a lot about the * quality, they might choose something else here. Then again, they * might also choose not to use a PseudoColor visual... */ static inline int _color_distance (unsigned short r1, unsigned short g1, unsigned short b1, unsigned short r2, unsigned short g2, unsigned short b2) { r1 >>= 8; g1 >>= 8; b1 >>= 8; r2 >>= 8; g2 >>= 8; b2 >>= 8; return ((r2 - r1) * (r2 - r1) + (g2 - g1) * (g2 - g1) + (b2 - b1) * (b2 - b1)); } cairo_status_t _cairo_xlib_visual_info_create (Display *dpy, int screen, VisualID visualid, cairo_xlib_visual_info_t **out) { cairo_xlib_visual_info_t *info; Colormap colormap = DefaultColormap (dpy, screen); XColor color; int gray, red, green, blue; int i, j, distance, min_distance = 0; XColor colors[256]; unsigned short cube_index_to_short[CUBE_SIZE]; unsigned short ramp_index_to_short[RAMP_SIZE]; unsigned char gray_to_pseudocolor[RAMP_SIZE]; for (i = 0; i < CUBE_SIZE; i++) cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1); for (i = 0; i < RAMP_SIZE; i++) ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); info = malloc (sizeof (cairo_xlib_visual_info_t)); if (unlikely (info == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_list_init (&info->link); info->visualid = visualid; /* Allocate a gray ramp and a color cube. * Give up as soon as failures start. */ for (gray = 0; gray < RAMP_SIZE; gray++) { color.red = color.green = color.blue = ramp_index_to_short[gray]; if (! XAllocColor (dpy, colormap, &color)) goto DONE_ALLOCATE; } /* XXX: Could do this in a more clever order to have the best * possible results from early failure. Could also choose a cube * uniformly distributed in a better space than RGB. */ for (red = 0; red < CUBE_SIZE; red++) { for (green = 0; green < CUBE_SIZE; green++) { for (blue = 0; blue < CUBE_SIZE; blue++) { color.red = cube_index_to_short[red]; color.green = cube_index_to_short[green]; color.blue = cube_index_to_short[blue]; color.pixel = 0; color.flags = 0; color.pad = 0; if (! XAllocColor (dpy, colormap, &color)) goto DONE_ALLOCATE; } } } DONE_ALLOCATE: for (i = 0; i < ARRAY_LENGTH (colors); i++) colors[i].pixel = i; XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors)); /* Search for nearest colors within allocated colormap. */ for (gray = 0; gray < RAMP_SIZE; gray++) { for (i = 0; i < 256; i++) { distance = _color_distance (ramp_index_to_short[gray], ramp_index_to_short[gray], ramp_index_to_short[gray], colors[i].red, colors[i].green, colors[i].blue); if (i == 0 || distance < min_distance) { gray_to_pseudocolor[gray] = colors[i].pixel; min_distance = distance; if (!min_distance) break; } } } for (red = 0; red < CUBE_SIZE; red++) { for (green = 0; green < CUBE_SIZE; green++) { for (blue = 0; blue < CUBE_SIZE; blue++) { for (i = 0; i < 256; i++) { distance = _color_distance (cube_index_to_short[red], cube_index_to_short[green], cube_index_to_short[blue], colors[i].red, colors[i].green, colors[i].blue); if (i == 0 || distance < min_distance) { info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel; min_distance = distance; if (!min_distance) break; } } } } } for (i = 0, j = 0; i < 256; i++) { if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i))) j++; info->field8_to_cube[i] = j; info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1); } for (i = 0, j = 0; i < 256; i++) { if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i))) j++; info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j]; } for (i = 0; i < 256; i++) { info->colors[i].a = 0xff; info->colors[i].r = colors[i].red >> 8; info->colors[i].g = colors[i].green >> 8; info->colors[i].b = colors[i].blue >> 8; } *out = info; return CAIRO_STATUS_SUCCESS; } void _cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info) { /* No need for XFreeColors() whilst using DefaultColormap */ _cairo_list_del (&info->link); free (info); } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-xcb-surface.c000066400000000000000000000610351271037650300267020ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Chris Wilson */ #include "cairoint.h" #if CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib.h" #include "cairo-xcb.h" #include "cairo-xcb-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-default-context-private.h" #include "cairo-list-inline.h" #include "cairo-image-surface-private.h" #include "cairo-surface-backend-private.h" #include #include /* For XESetCloseDisplay */ struct cairo_xlib_xcb_display_t { cairo_device_t base; Display *dpy; cairo_device_t *xcb_device; XExtCodes *codes; cairo_list_t link; }; typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t; /* List of all #cairo_xlib_xcb_display_t alive, * protected by _cairo_xlib_display_mutex */ static cairo_list_t displays; static cairo_surface_t * _cairo_xlib_xcb_surface_create (void *dpy, void *scr, void *visual, void *format, cairo_surface_t *xcb); static cairo_surface_t * _cairo_xlib_xcb_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { cairo_xlib_xcb_surface_t *other = abstract_other; cairo_surface_t *xcb; xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height); if (unlikely (xcb == NULL || xcb->status)) return xcb; return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb); } static cairo_status_t _cairo_xlib_xcb_surface_finish (void *abstract_surface) { cairo_xlib_xcb_surface_t *surface = abstract_surface; cairo_status_t status; cairo_surface_finish (&surface->xcb->base); status = surface->xcb->base.status; cairo_surface_destroy (&surface->xcb->base); surface->xcb = NULL; return status; } static cairo_surface_t * _cairo_xlib_xcb_surface_create_similar_image (void *abstract_other, cairo_format_t format, int width, int height) { cairo_xlib_xcb_surface_t *surface = abstract_other; return cairo_surface_create_similar_image (&surface->xcb->base, format, width, height); } static cairo_image_surface_t * _cairo_xlib_xcb_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_map_to_image (&surface->xcb->base, extents); } static cairo_int_status_t _cairo_xlib_xcb_surface_unmap (void *abstract_surface, cairo_image_surface_t *image) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_unmap_image (&surface->xcb->base, image); } static cairo_surface_t * _cairo_xlib_xcb_surface_source (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_get_source (&surface->xcb->base, extents); } static cairo_status_t _cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_acquire_source_image (&surface->xcb->base, image_out, image_extra); } static void _cairo_xlib_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image_out, void *image_extra) { cairo_xlib_xcb_surface_t *surface = abstract_surface; _cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra); } static cairo_bool_t _cairo_xlib_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_get_extents (&surface->xcb->base, extents); } static void _cairo_xlib_xcb_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { cairo_xlib_xcb_surface_t *surface = abstract_surface; cairo_surface_get_font_options (&surface->xcb->base, options); } static cairo_int_status_t _cairo_xlib_xcb_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_paint (&surface->xcb->base, op, source, clip); } static cairo_int_status_t _cairo_xlib_xcb_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_mask (&surface->xcb->base, op, source, mask, clip); } static cairo_int_status_t _cairo_xlib_xcb_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_stroke (&surface->xcb->base, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xlib_xcb_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_fill (&surface->xcb->base, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_xlib_xcb_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_xlib_xcb_surface_t *surface = abstract_surface; return _cairo_surface_show_text_glyphs (&surface->xcb->base, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); } static cairo_status_t _cairo_xlib_xcb_surface_flush (void *abstract_surface, unsigned flags) { cairo_xlib_xcb_surface_t *surface = abstract_surface; /* We have to call cairo_surface_flush() to make sure snapshots are detached */ return _cairo_surface_flush (&surface->xcb->base, flags); } static cairo_status_t _cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { cairo_xlib_xcb_surface_t *surface = abstract_surface; cairo_surface_mark_dirty_rectangle (&surface->xcb->base, x, y, width, height); return cairo_surface_status (&surface->xcb->base); } static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_xcb_surface_finish, _cairo_default_context_create, /* XXX */ _cairo_xlib_xcb_surface_create_similar, _cairo_xlib_xcb_surface_create_similar_image, _cairo_xlib_xcb_surface_map_to_image, _cairo_xlib_xcb_surface_unmap, _cairo_xlib_xcb_surface_source, _cairo_xlib_xcb_surface_acquire_source_image, _cairo_xlib_xcb_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_xlib_xcb_surface_get_extents, _cairo_xlib_xcb_surface_get_font_options, _cairo_xlib_xcb_surface_flush, _cairo_xlib_xcb_surface_mark_dirty, _cairo_xlib_xcb_surface_paint, _cairo_xlib_xcb_surface_mask, _cairo_xlib_xcb_surface_stroke, _cairo_xlib_xcb_surface_fill, NULL, /* fill_stroke */ _cairo_xlib_xcb_surface_glyphs, }; static void _cairo_xlib_xcb_display_finish (void *abstract_display) { cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display; CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); cairo_list_del (&display->link); CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); cairo_device_destroy (display->xcb_device); display->xcb_device = NULL; XESetCloseDisplay (display->dpy, display->codes->extension, NULL); /* Drop the reference from _cairo_xlib_xcb_device_create */ cairo_device_destroy (&display->base); } static int _cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes) { cairo_xlib_xcb_display_t *display; CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); cairo_list_foreach_entry (display, cairo_xlib_xcb_display_t, &displays, link) { if (display->dpy == dpy) { /* _cairo_xlib_xcb_display_finish will lock the mutex again * -> deadlock (This mutex isn't recursive) */ cairo_device_reference (&display->base); CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); /* Make sure the xcb and xlib-xcb devices are finished */ cairo_device_finish (display->xcb_device); cairo_device_finish (&display->base); cairo_device_destroy (&display->base); return 0; } } CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); return 0; } static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = { CAIRO_DEVICE_TYPE_XLIB, NULL, NULL, NULL, /* flush */ _cairo_xlib_xcb_display_finish, free, /* destroy */ }; static cairo_device_t * _cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device) { cairo_xlib_xcb_display_t *display = NULL; cairo_device_t *device; if (xcb_device == NULL) return NULL; CAIRO_MUTEX_INITIALIZE (); CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); if (displays.next == NULL) { cairo_list_init (&displays); } cairo_list_foreach_entry (display, cairo_xlib_xcb_display_t, &displays, link) { if (display->dpy == dpy) { /* Maintain MRU order. */ if (displays.next != &display->link) cairo_list_move (&display->link, &displays); /* Grab a reference for our caller */ device = cairo_device_reference (&display->base); assert (display->xcb_device == xcb_device); goto unlock; } } display = malloc (sizeof (cairo_xlib_xcb_display_t)); if (unlikely (display == NULL)) { device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); goto unlock; } display->codes = XAddExtension (dpy); if (unlikely (display->codes == NULL)) { device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); free (display); goto unlock; } _cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend); XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display); /* Add a reference for _cairo_xlib_xcb_display_finish. This basically means * that the device's reference count never drops to zero * as long as our Display* is alive. We need this because there is no * "XDelExtension" to undo XAddExtension and having lots of registered * extensions slows down libX11. */ cairo_device_reference (&display->base); display->dpy = dpy; display->xcb_device = cairo_device_reference(xcb_device); cairo_list_add (&display->link, &displays); device = &display->base; unlock: CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); return device; } static cairo_surface_t * _cairo_xlib_xcb_surface_create (void *dpy, void *scr, void *visual, void *format, cairo_surface_t *xcb) { cairo_xlib_xcb_surface_t *surface; if (unlikely (xcb->status)) return xcb; surface = malloc (sizeof (*surface)); if (unlikely (surface == NULL)) { cairo_surface_destroy (xcb); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } _cairo_surface_init (&surface->base, &_cairo_xlib_xcb_surface_backend, _cairo_xlib_xcb_device_create (dpy, xcb->device), xcb->content); /* _cairo_surface_init() got another reference to the device, drop ours */ cairo_device_destroy (surface->base.device); surface->display = dpy; surface->screen = scr; surface->visual = visual; surface->format = format; surface->xcb = (cairo_xcb_surface_t *) xcb; return &surface->base; } static Screen * _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { int s, d, v; for (s = 0; s < ScreenCount (dpy); s++) { Screen *screen; screen = ScreenOfDisplay (dpy, s); if (visual == DefaultVisualOfScreen (screen)) return screen; for (d = 0; d < screen->ndepths; d++) { Depth *depth; depth = &screen->depths[d]; for (v = 0; v < depth->nvisuals; v++) if (visual == &depth->visuals[v]) return screen; } } return NULL; } cairo_surface_t * cairo_xlib_surface_create (Display *dpy, Drawable drawable, Visual *visual, int width, int height) { Screen *scr; xcb_visualtype_t xcb_visual; scr = _cairo_xlib_screen_from_visual (dpy, visual); if (scr == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); xcb_visual.visual_id = visual->visualid; #if defined(__cplusplus) || defined(c_plusplus) xcb_visual._class = visual->c_class; #else xcb_visual._class = visual->class; #endif xcb_visual.bits_per_rgb_value = visual->bits_per_rgb; xcb_visual.colormap_entries = visual->map_entries; xcb_visual.red_mask = visual->red_mask; xcb_visual.green_mask = visual->green_mask; xcb_visual.blue_mask = visual->blue_mask; return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL, cairo_xcb_surface_create (XGetXCBConnection (dpy), drawable, &xcb_visual, width, height)); } cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, Screen *scr, int width, int height) { return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL, cairo_xcb_surface_create_for_bitmap (XGetXCBConnection (dpy), (xcb_screen_t *) scr, bitmap, width, height)); } #if CAIRO_HAS_XLIB_XRENDER_SURFACE static xcb_screen_t * _cairo_xcb_screen_from_root (xcb_connection_t *connection, xcb_window_t id) { xcb_screen_iterator_t s; s = xcb_setup_roots_iterator (xcb_get_setup (connection)); for (; s.rem; xcb_screen_next (&s)) { if (s.data->root == id) return s.data; } return NULL; } cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, Screen *scr, XRenderPictFormat *format, int width, int height) { xcb_render_pictforminfo_t xcb_format; xcb_connection_t *connection; xcb_screen_t *screen; xcb_format.id = format->id; xcb_format.type = format->type; xcb_format.depth = format->depth; xcb_format.direct.red_shift = format->direct.red; xcb_format.direct.red_mask = format->direct.redMask; xcb_format.direct.green_shift = format->direct.green; xcb_format.direct.green_mask = format->direct.greenMask; xcb_format.direct.blue_shift = format->direct.blue; xcb_format.direct.blue_mask = format->direct.blueMask; xcb_format.direct.alpha_shift = format->direct.alpha; xcb_format.direct.alpha_mask = format->direct.alphaMask; xcb_format.colormap = format->colormap; connection = XGetXCBConnection (dpy); screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root); return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format, cairo_xcb_surface_create_with_xrender_format (connection, screen, drawable, &xcb_format, width, height)); } XRenderPictFormat * cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) { cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface; /* Throw an error for a non-xlib surface */ if (surface->type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return xlib_surface->format; } #endif void cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, int width, int height) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { status = _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } cairo_xcb_surface_set_size (&surface->xcb->base, width, height); if (unlikely (surface->xcb->base.status)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (surface->xcb->base.status)); } } void cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, Drawable drawable, int width, int height) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface; cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { status = _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } cairo_xcb_surface_set_drawable (&surface->xcb->base, drawable, width, height); if (unlikely (surface->xcb->base.status)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (surface->xcb->base.status)); } } Display * cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->display; } Drawable cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (unlikely (abstract_surface->finished)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); return 0; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } /* This can happen when e.g. create_similar falls back to an image surface * because we don't have the RENDER extension. */ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->xcb->drawable; } Screen * cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->screen; } Visual * cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; } return surface->visual; } int cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (unlikely (abstract_surface->finished)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); return 0; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } /* This can happen when e.g. create_similar falls back to an image surface * because we don't have the RENDER extension. */ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->xcb->depth; } int cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (unlikely (abstract_surface->finished)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); return 0; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } /* This can happen when e.g. create_similar falls back to an image surface * because we don't have the RENDER extension. */ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->xcb->width; } int cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) { cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; if (unlikely (abstract_surface->finished)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); return 0; } if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } /* This can happen when e.g. create_similar falls back to an image surface * because we don't have the RENDER extension. */ if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->xcb->height; } void cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, int major, int minor) { cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; if (device == NULL || device->status) return; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) return; cairo_xcb_device_debug_cap_xrender_version (display->xcb_device, major, minor); } void cairo_xlib_device_debug_set_precision (cairo_device_t *device, int precision) { cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; if (device == NULL || device->status) return; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return; } cairo_xcb_device_debug_set_precision (display->xcb_device, precision); } int cairo_xlib_device_debug_get_precision (cairo_device_t *device) { cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; if (device == NULL || device->status) return -1; if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { cairo_status_t status; status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); (void) status; return -1; } return cairo_xcb_device_debug_get_precision (display->xcb_device); } #endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-xrender-private.h000066400000000000000000000676441271037650300276400ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. */ #ifndef CAIRO_XLIB_XRENDER_PRIVATE_H #define CAIRO_XLIB_XRENDER_PRIVATE_H #include "cairo-features.h" #include "cairo-compiler-private.h" #include /* These prototypes are used when defining interfaces missing from the * render headers. As it happens, it is the case that all libxrender * functions take a pointer as first argument. */ __attribute__((__unused__)) static void _void_consume (void *p, ...) { } __attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } __attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } __attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } #if CAIRO_HAS_XLIB_XRENDER_SURFACE #include "cairo-xlib-xrender.h" #include #include /* We require Render >= 0.6. The following defines were only added in * 0.10. Make sure they are defined. */ /* Filters included in 0.10 */ #ifndef FilterConvolution #define FilterConvolution "convolution" #endif /* Extended repeat attributes included in 0.10 */ #ifndef RepeatNone #define RepeatNone 0 #define RepeatNormal 1 #define RepeatPad 2 #define RepeatReflect 3 #endif #ifndef PictOptBlendMinimum /* * Operators only available in version 0.11 */ #define PictOpBlendMinimum 0x30 #define PictOpMultiply 0x30 #define PictOpScreen 0x31 #define PictOpOverlay 0x32 #define PictOpDarken 0x33 #define PictOpLighten 0x34 #define PictOpColorDodge 0x35 #define PictOpColorBurn 0x36 #define PictOpHardLight 0x37 #define PictOpSoftLight 0x38 #define PictOpDifference 0x39 #define PictOpExclusion 0x3a #define PictOpHSLHue 0x3b #define PictOpHSLSaturation 0x3c #define PictOpHSLColor 0x3d #define PictOpHSLLuminosity 0x3e #define PictOpBlendMaximum 0x3e #endif #if !HAVE_XRENDERCREATELINEARGRADIENT #define XRenderCreateLinearGradient _int_consume typedef struct _XLinearGradient { XPointFixed p1; XPointFixed p2; } XLinearGradient; #endif #if !HAVE_XRENDERCREATERADIALGRADIENT #define XRenderCreateRadialGradient _int_consume typedef struct _XCircle { XFixed x; XFixed y; XFixed radius; } XCircle; typedef struct _XRadialGradient { XCircle inner; XCircle outer; } XRadialGradient; #endif #if !HAVE_XRENDERCREATECONICALGRADIENT #define XRenderCreateConicalGradient _int_consume typedef struct _XConicalGradient { XPointFixed center; XFixed angle; /* in degrees */ } XConicalGradient; #endif #else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ /* Provide dummy symbols and macros to get it compile and take the fallback * route, just like as if Xrender is not available in the server at run-time. */ /* Functions */ #define XRenderQueryExtension _int_consume #define XRenderQueryVersion _int_consume #define XRenderQueryFormats _int_consume #define XRenderQuerySubpixelOrder _int_consume #define XRenderSetSubpixelOrder _int_consume #define XRenderFindVisualFormat _voidp_consume #define XRenderFindFormat _voidp_consume #define XRenderFindStandardFormat _voidp_consume #define XRenderQueryPictIndexValues _voidp_consume #define XRenderCreatePicture _int_consume #define XRenderChangePicture _void_consume #define XRenderSetPictureClipRectangles _void_consume #define XRenderSetPictureClipRegion _void_consume #define XRenderSetPictureTransform _void_consume #define XRenderFreePicture _void_consume_free #define XRenderComposite _void_consume #define XRenderCreateGlyphSet _int_consume #define XRenderReferenceGlyphSet _int_consume #define XRenderFreeGlyphSet _void_consume_free #define XRenderAddGlyphs _void_consume #define XRenderFreeGlyphs _void_consume #define XRenderCompositeString8 _void_consume #define XRenderCompositeString16 _void_consume #define XRenderCompositeString32 _void_consume #define XRenderCompositeText8 (cairo_xrender_composite_text_func_t) _void_consume #define XRenderCompositeText16 _void_consume #define XRenderCompositeText32 _void_consume #define XRenderFillRectangle _void_consume #define XRenderFillRectangles _void_consume #define XRenderCompositeTrapezoids _void_consume #define XRenderCompositeTriangles _void_consume #define XRenderCompositeTriStrip _void_consume #define XRenderCompositeTriFan _void_consume #define XRenderCompositeDoublePoly _void_consume #define XRenderParseColor _int_consume #define XRenderCreateCursor _int_consume #define XRenderQueryFilters _voidp_consume #define XRenderSetPictureFilter _void_consume #define XRenderCreateAnimCursor _int_consume #define XRenderAddTraps _void_consume #define XRenderCreateSolidFill _int_consume #define XRenderCreateLinearGradient _int_consume #define XRenderCreateRadialGradient _int_consume #define XRenderCreateConicalGradient _int_consume #define cairo_xlib_surface_create_with_xrender_format _voidp_consume /* The rest of this file is copied from various Xrender header files, with * the following copyright/license information: * * Copyright © 2000 SuSE, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ /* Copied from X11/extensions/render.h */ typedef unsigned long Glyph; typedef unsigned long GlyphSet; typedef unsigned long Picture; typedef unsigned long PictFormat; #define BadPictFormat 0 #define BadPicture 1 #define BadPictOp 2 #define BadGlyphSet 3 #define BadGlyph 4 #define RenderNumberErrors (BadGlyph+1) #define PictTypeIndexed 0 #define PictTypeDirect 1 #define PictOpMinimum 0 #define PictOpClear 0 #define PictOpSrc 1 #define PictOpDst 2 #define PictOpOver 3 #define PictOpOverReverse 4 #define PictOpIn 5 #define PictOpInReverse 6 #define PictOpOut 7 #define PictOpOutReverse 8 #define PictOpAtop 9 #define PictOpAtopReverse 10 #define PictOpXor 11 #define PictOpAdd 12 #define PictOpSaturate 13 #define PictOpMaximum 13 /* * Operators only available in version 0.2 */ #define PictOpDisjointMinimum 0x10 #define PictOpDisjointClear 0x10 #define PictOpDisjointSrc 0x11 #define PictOpDisjointDst 0x12 #define PictOpDisjointOver 0x13 #define PictOpDisjointOverReverse 0x14 #define PictOpDisjointIn 0x15 #define PictOpDisjointInReverse 0x16 #define PictOpDisjointOut 0x17 #define PictOpDisjointOutReverse 0x18 #define PictOpDisjointAtop 0x19 #define PictOpDisjointAtopReverse 0x1a #define PictOpDisjointXor 0x1b #define PictOpDisjointMaximum 0x1b #define PictOpConjointMinimum 0x20 #define PictOpConjointClear 0x20 #define PictOpConjointSrc 0x21 #define PictOpConjointDst 0x22 #define PictOpConjointOver 0x23 #define PictOpConjointOverReverse 0x24 #define PictOpConjointIn 0x25 #define PictOpConjointInReverse 0x26 #define PictOpConjointOut 0x27 #define PictOpConjointOutReverse 0x28 #define PictOpConjointAtop 0x29 #define PictOpConjointAtopReverse 0x2a #define PictOpConjointXor 0x2b #define PictOpConjointMaximum 0x2b /* * Operators only available in version 0.11 */ #define PictOpBlendMinimum 0x30 #define PictOpMultiply 0x30 #define PictOpScreen 0x31 #define PictOpOverlay 0x32 #define PictOpDarken 0x33 #define PictOpLighten 0x34 #define PictOpColorDodge 0x35 #define PictOpColorBurn 0x36 #define PictOpHardLight 0x37 #define PictOpSoftLight 0x38 #define PictOpDifference 0x39 #define PictOpExclusion 0x3a #define PictOpHSLHue 0x3b #define PictOpHSLSaturation 0x3c #define PictOpHSLColor 0x3d #define PictOpHSLLuminosity 0x3e #define PictOpBlendMaximum 0x3e #define PolyEdgeSharp 0 #define PolyEdgeSmooth 1 #define PolyModePrecise 0 #define PolyModeImprecise 1 #define CPRepeat (1 << 0) #define CPAlphaMap (1 << 1) #define CPAlphaXOrigin (1 << 2) #define CPAlphaYOrigin (1 << 3) #define CPClipXOrigin (1 << 4) #define CPClipYOrigin (1 << 5) #define CPClipMask (1 << 6) #define CPGraphicsExposure (1 << 7) #define CPSubwindowMode (1 << 8) #define CPPolyEdge (1 << 9) #define CPPolyMode (1 << 10) #define CPDither (1 << 11) #define CPComponentAlpha (1 << 12) #define CPLastBit 12 /* Filters included in 0.6 */ #define FilterNearest "nearest" #define FilterBilinear "bilinear" /* Filters included in 0.10 */ #define FilterConvolution "convolution" #define FilterFast "fast" #define FilterGood "good" #define FilterBest "best" #define FilterAliasNone -1 /* Subpixel orders included in 0.6 */ #define SubPixelUnknown 0 #define SubPixelHorizontalRGB 1 #define SubPixelHorizontalBGR 2 #define SubPixelVerticalRGB 3 #define SubPixelVerticalBGR 4 #define SubPixelNone 5 /* Extended repeat attributes included in 0.10 */ #define RepeatNone 0 #define RepeatNormal 1 #define RepeatPad 2 #define RepeatReflect 3 /* Copied from X11/extensions/Xrender.h */ typedef struct { short red; short redMask; short green; short greenMask; short blue; short blueMask; short alpha; short alphaMask; } XRenderDirectFormat; typedef struct { PictFormat id; int type; int depth; XRenderDirectFormat direct; Colormap colormap; } XRenderPictFormat; #define PictFormatID (1 << 0) #define PictFormatType (1 << 1) #define PictFormatDepth (1 << 2) #define PictFormatRed (1 << 3) #define PictFormatRedMask (1 << 4) #define PictFormatGreen (1 << 5) #define PictFormatGreenMask (1 << 6) #define PictFormatBlue (1 << 7) #define PictFormatBlueMask (1 << 8) #define PictFormatAlpha (1 << 9) #define PictFormatAlphaMask (1 << 10) #define PictFormatColormap (1 << 11) typedef struct _XRenderPictureAttributes { int repeat; Picture alpha_map; int alpha_x_origin; int alpha_y_origin; int clip_x_origin; int clip_y_origin; Pixmap clip_mask; Bool graphics_exposures; int subwindow_mode; int poly_edge; int poly_mode; Atom dither; Bool component_alpha; } XRenderPictureAttributes; typedef struct { unsigned short red; unsigned short green; unsigned short blue; unsigned short alpha; } XRenderColor; typedef struct _XGlyphInfo { unsigned short width; unsigned short height; short x; short y; short xOff; short yOff; } XGlyphInfo; typedef struct _XGlyphElt8 { GlyphSet glyphset; _Xconst char *chars; int nchars; int xOff; int yOff; } XGlyphElt8; typedef struct _XGlyphElt16 { GlyphSet glyphset; _Xconst unsigned short *chars; int nchars; int xOff; int yOff; } XGlyphElt16; typedef struct _XGlyphElt32 { GlyphSet glyphset; _Xconst unsigned int *chars; int nchars; int xOff; int yOff; } XGlyphElt32; typedef double XDouble; typedef struct _XPointDouble { XDouble x, y; } XPointDouble; #define XDoubleToFixed(f) ((XFixed) ((f) * 65536)) #define XFixedToDouble(f) (((XDouble) (f)) / 65536) typedef int XFixed; typedef struct _XPointFixed { XFixed x, y; } XPointFixed; typedef struct _XLineFixed { XPointFixed p1, p2; } XLineFixed; typedef struct _XTriangle { XPointFixed p1, p2, p3; } XTriangle; typedef struct _XCircle { XFixed x; XFixed y; XFixed radius; } XCircle; typedef struct _XTrapezoid { XFixed top, bottom; XLineFixed left, right; } XTrapezoid; typedef struct _XTransform { XFixed matrix[3][3]; } XTransform; typedef struct _XFilters { int nfilter; char **filter; int nalias; short *alias; } XFilters; typedef struct _XIndexValue { unsigned long pixel; unsigned short red, green, blue, alpha; } XIndexValue; typedef struct _XAnimCursor { Cursor cursor; unsigned long delay; } XAnimCursor; typedef struct _XSpanFix { XFixed left, right, y; } XSpanFix; typedef struct _XTrap { XSpanFix top, bottom; } XTrap; typedef struct _XLinearGradient { XPointFixed p1; XPointFixed p2; } XLinearGradient; typedef struct _XRadialGradient { XCircle inner; XCircle outer; } XRadialGradient; typedef struct _XConicalGradient { XPointFixed center; XFixed angle; /* in degrees */ } XConicalGradient; #define PictStandardARGB32 0 #define PictStandardRGB24 1 #define PictStandardA8 2 #define PictStandardA4 3 #define PictStandardA1 4 #define PictStandardNUM 5 /* Copied from X11/extensions/renderproto.h */ #include #define Window CARD32 #define Drawable CARD32 #define Font CARD32 #define Pixmap CARD32 #define Cursor CARD32 #define Colormap CARD32 #define GContext CARD32 #define Atom CARD32 #define VisualID CARD32 #define Time CARD32 #define KeyCode CARD8 #define KeySym CARD32 #define Picture CARD32 #define PictFormat CARD32 #define Fixed INT32 #define Glyphset CARD32 #define Glyph CARD32 /* * data structures */ typedef struct { CARD16 red B16; CARD16 redMask B16; CARD16 green B16; CARD16 greenMask B16; CARD16 blue B16; CARD16 blueMask B16; CARD16 alpha B16; CARD16 alphaMask B16; } xDirectFormat; #define sz_xDirectFormat 16 typedef struct { PictFormat id B32; CARD8 type; CARD8 depth; CARD16 pad1 B16; xDirectFormat direct; Colormap colormap; } xPictFormInfo; #define sz_xPictFormInfo 28 typedef struct { VisualID visual; PictFormat format; } xPictVisual; #define sz_xPictVisual 8 typedef struct { CARD8 depth; CARD8 pad1; CARD16 nPictVisuals B16; CARD32 pad2 B32; } xPictDepth; #define sz_xPictDepth 8 typedef struct { CARD32 nDepth B32; PictFormat fallback B32; } xPictScreen; #define sz_xPictScreen 8 typedef struct { CARD32 pixel B32; CARD16 red B16; CARD16 green B16; CARD16 blue B16; CARD16 alpha B16; } xIndexValue; #define sz_xIndexValue 12 typedef struct { CARD16 red B16; CARD16 green B16; CARD16 blue B16; CARD16 alpha B16; } xRenderColor; #define sz_xRenderColor 8 typedef struct { Fixed x B32; Fixed y B32; } xPointFixed; #define sz_xPointFixed 8 typedef struct { xPointFixed p1; xPointFixed p2; } xLineFixed; #define sz_xLineFixed 16 typedef struct { xPointFixed p1, p2, p3; } xTriangle; #define sz_xTriangle 24 typedef struct { Fixed top B32; Fixed bottom B32; xLineFixed left; xLineFixed right; } xTrapezoid; #define sz_xTrapezoid 40 typedef struct { CARD16 width B16; CARD16 height B16; INT16 x B16; INT16 y B16; INT16 xOff B16; INT16 yOff B16; } xGlyphInfo; #define sz_xGlyphInfo 12 typedef struct { CARD8 len; CARD8 pad1; CARD16 pad2; INT16 deltax; INT16 deltay; } xGlyphElt; #define sz_xGlyphElt 8 typedef struct { Fixed l, r, y; } xSpanFix; #define sz_xSpanFix 12 typedef struct { xSpanFix top, bot; } xTrap; #define sz_xTrap 24 /* * requests and replies */ typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD32 majorVersion B32; CARD32 minorVersion B32; } xRenderQueryVersionReq; #define sz_xRenderQueryVersionReq 12 typedef struct { BYTE type; /* X_Reply */ BYTE pad1; CARD16 sequenceNumber B16; CARD32 length B32; CARD32 majorVersion B32; CARD32 minorVersion B32; CARD32 pad2 B32; CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; } xRenderQueryVersionReply; #define sz_xRenderQueryVersionReply 32 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; } xRenderQueryPictFormatsReq; #define sz_xRenderQueryPictFormatsReq 4 typedef struct { BYTE type; /* X_Reply */ BYTE pad1; CARD16 sequenceNumber B16; CARD32 length B32; CARD32 numFormats B32; CARD32 numScreens B32; CARD32 numDepths B32; CARD32 numVisuals B32; CARD32 numSubpixel B32; /* Version 0.6 */ CARD32 pad5 B32; } xRenderQueryPictFormatsReply; #define sz_xRenderQueryPictFormatsReply 32 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; PictFormat format B32; } xRenderQueryPictIndexValuesReq; #define sz_xRenderQueryPictIndexValuesReq 8 typedef struct { BYTE type; /* X_Reply */ BYTE pad1; CARD16 sequenceNumber B16; CARD32 length B32; CARD32 numIndexValues; CARD32 pad2 B32; CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32; } xRenderQueryPictIndexValuesReply; #define sz_xRenderQueryPictIndexValuesReply 32 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture pid B32; Drawable drawable B32; PictFormat format B32; CARD32 mask B32; } xRenderCreatePictureReq; #define sz_xRenderCreatePictureReq 20 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture B32; CARD32 mask B32; } xRenderChangePictureReq; #define sz_xRenderChangePictureReq 12 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture B32; INT16 xOrigin B16; INT16 yOrigin B16; } xRenderSetPictureClipRectanglesReq; #define sz_xRenderSetPictureClipRectanglesReq 12 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture B32; } xRenderFreePictureReq; #define sz_xRenderFreePictureReq 8 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture mask B32; Picture dst B32; INT16 xSrc B16; INT16 ySrc B16; INT16 xMask B16; INT16 yMask B16; INT16 xDst B16; INT16 yDst B16; CARD16 width B16; CARD16 height B16; } xRenderCompositeReq; #define sz_xRenderCompositeReq 36 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture src B32; Picture dst B32; CARD32 colorScale B32; CARD32 alphaScale B32; INT16 xSrc B16; INT16 ySrc B16; INT16 xDst B16; INT16 yDst B16; CARD16 width B16; CARD16 height B16; } xRenderScaleReq; #define sz_xRenderScaleReq 32 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture dst B32; PictFormat maskFormat B32; INT16 xSrc B16; INT16 ySrc B16; } xRenderTrapezoidsReq; #define sz_xRenderTrapezoidsReq 24 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture dst B32; PictFormat maskFormat B32; INT16 xSrc B16; INT16 ySrc B16; } xRenderTrianglesReq; #define sz_xRenderTrianglesReq 24 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture dst B32; PictFormat maskFormat B32; INT16 xSrc B16; INT16 ySrc B16; } xRenderTriStripReq; #define sz_xRenderTriStripReq 24 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture dst B32; PictFormat maskFormat B32; INT16 xSrc B16; INT16 ySrc B16; } xRenderTriFanReq; #define sz_xRenderTriFanReq 24 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Glyphset gsid B32; PictFormat format B32; } xRenderCreateGlyphSetReq; #define sz_xRenderCreateGlyphSetReq 12 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Glyphset gsid B32; Glyphset existing B32; } xRenderReferenceGlyphSetReq; #define sz_xRenderReferenceGlyphSetReq 24 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Glyphset glyphset B32; } xRenderFreeGlyphSetReq; #define sz_xRenderFreeGlyphSetReq 8 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Glyphset glyphset B32; CARD32 nglyphs; } xRenderAddGlyphsReq; #define sz_xRenderAddGlyphsReq 12 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Glyphset glyphset B32; } xRenderFreeGlyphsReq; #define sz_xRenderFreeGlyphsReq 8 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture src B32; Picture dst B32; PictFormat maskFormat B32; Glyphset glyphset B32; INT16 xSrc B16; INT16 ySrc B16; } xRenderCompositeGlyphsReq, xRenderCompositeGlyphs8Req, xRenderCompositeGlyphs16Req, xRenderCompositeGlyphs32Req; #define sz_xRenderCompositeGlyphs8Req 28 #define sz_xRenderCompositeGlyphs16Req 28 #define sz_xRenderCompositeGlyphs32Req 28 /* 0.1 and higher */ typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; CARD8 op; CARD8 pad1; CARD16 pad2 B16; Picture dst B32; xRenderColor color; } xRenderFillRectanglesReq; #define sz_xRenderFillRectanglesReq 20 /* 0.5 and higher */ typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Cursor cid B32; Picture src B32; CARD16 x B16; CARD16 y B16; } xRenderCreateCursorReq; #define sz_xRenderCreateCursorReq 16 /* 0.6 and higher */ /* * This can't use an array because 32-bit values may be in bitfields */ typedef struct { Fixed matrix11 B32; Fixed matrix12 B32; Fixed matrix13 B32; Fixed matrix21 B32; Fixed matrix22 B32; Fixed matrix23 B32; Fixed matrix31 B32; Fixed matrix32 B32; Fixed matrix33 B32; } xRenderTransform; #define sz_xRenderTransform 36 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture B32; xRenderTransform transform; } xRenderSetPictureTransformReq; #define sz_xRenderSetPictureTransformReq 44 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Drawable drawable B32; } xRenderQueryFiltersReq; #define sz_xRenderQueryFiltersReq 8 typedef struct { BYTE type; /* X_Reply */ BYTE pad1; CARD16 sequenceNumber B16; CARD32 length B32; CARD32 numAliases B32; /* LISTofCARD16 */ CARD32 numFilters B32; /* LISTofSTRING8 */ CARD32 pad2 B32; CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; } xRenderQueryFiltersReply; #define sz_xRenderQueryFiltersReply 32 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture B32; CARD16 nbytes B16; /* number of bytes in name */ CARD16 pad B16; } xRenderSetPictureFilterReq; #define sz_xRenderSetPictureFilterReq 12 /* 0.8 and higher */ typedef struct { Cursor cursor B32; CARD32 delay B32; } xAnimCursorElt; #define sz_xAnimCursorElt 8 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Cursor cid B32; } xRenderCreateAnimCursorReq; #define sz_xRenderCreateAnimCursorReq 8 /* 0.9 and higher */ typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture picture; INT16 xOff B16; INT16 yOff B16; } xRenderAddTrapsReq; #define sz_xRenderAddTrapsReq 12 /* 0.10 and higher */ typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture pid B32; xRenderColor color; } xRenderCreateSolidFillReq; #define sz_xRenderCreateSolidFillReq 16 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture pid B32; xPointFixed p1; xPointFixed p2; CARD32 nStops; } xRenderCreateLinearGradientReq; #define sz_xRenderCreateLinearGradientReq 28 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture pid B32; xPointFixed inner; xPointFixed outer; Fixed inner_radius; Fixed outer_radius; CARD32 nStops; } xRenderCreateRadialGradientReq; #define sz_xRenderCreateRadialGradientReq 36 typedef struct { CARD8 reqType; CARD8 renderReqType; CARD16 length B16; Picture pid B32; xPointFixed center; Fixed angle; /* in degrees */ CARD32 nStops; } xRenderCreateConicalGradientReq; #define sz_xRenderCreateConicalGradientReq 24 #undef Window #undef Drawable #undef Font #undef Pixmap #undef Cursor #undef Colormap #undef GContext #undef Atom #undef VisualID #undef Time #undef KeyCode #undef KeySym #undef Picture #undef PictFormat #undef Fixed #undef Glyphset #undef Glyph #endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ #endif /* CAIRO_XLIB_XRENDER_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib-xrender.h000066400000000000000000000046041271037650300261530ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_XLIB_XRENDER_H #define CAIRO_XLIB_XRENDER_H #include "cairo.h" #if CAIRO_HAS_XLIB_XRENDER_SURFACE #include #include CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, Drawable drawable, Screen *screen, XRenderPictFormat *format, int width, int height); cairo_public XRenderPictFormat * cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface); CAIRO_END_DECLS #else /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ # error Cairo was not compiled with support for the xlib XRender backend #endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ #endif /* CAIRO_XLIB_XRENDER_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xlib.h000066400000000000000000000067711271037650300245150ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_XLIB_H #define CAIRO_XLIB_H #include "cairo.h" #if CAIRO_HAS_XLIB_SURFACE #include CAIRO_BEGIN_DECLS cairo_public cairo_surface_t * cairo_xlib_surface_create (Display *dpy, Drawable drawable, Visual *visual, int width, int height); cairo_public cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, Pixmap bitmap, Screen *screen, int width, int height); cairo_public void cairo_xlib_surface_set_size (cairo_surface_t *surface, int width, int height); cairo_public void cairo_xlib_surface_set_drawable (cairo_surface_t *surface, Drawable drawable, int width, int height); cairo_public Display * cairo_xlib_surface_get_display (cairo_surface_t *surface); cairo_public Drawable cairo_xlib_surface_get_drawable (cairo_surface_t *surface); cairo_public Screen * cairo_xlib_surface_get_screen (cairo_surface_t *surface); cairo_public Visual * cairo_xlib_surface_get_visual (cairo_surface_t *surface); cairo_public int cairo_xlib_surface_get_depth (cairo_surface_t *surface); cairo_public int cairo_xlib_surface_get_width (cairo_surface_t *surface); cairo_public int cairo_xlib_surface_get_height (cairo_surface_t *surface); /* debug interface */ cairo_public void cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, int major_version, int minor_version); /* * @precision: -1 implies automatically choose based on antialiasing mode, * any other value overrides and sets the corresponding PolyMode. */ cairo_public void cairo_xlib_device_debug_set_precision (cairo_device_t *device, int precision); cairo_public int cairo_xlib_device_debug_get_precision (cairo_device_t *device); CAIRO_END_DECLS #else /* CAIRO_HAS_XLIB_SURFACE */ # error Cairo was not compiled with support for the xlib backend #endif /* CAIRO_HAS_XLIB_SURFACE */ #endif /* CAIRO_XLIB_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xml-surface.c000066400000000000000000001010261271037650300257650ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributor(s): * Chris Wilson */ /* This surface is intended to produce a verbose, hierarchical, DAG XML file * representing a single surface. It is intended to be used by debuggers, * such as cairo-sphinx, or by application test-suites that what a log of * operations. */ #include "cairoint.h" #include "cairo-xml.h" #include "cairo-clip-private.h" #include "cairo-device-private.h" #include "cairo-default-context-private.h" #include "cairo-image-surface-private.h" #include "cairo-error-private.h" #include "cairo-output-stream-private.h" #include "cairo-recording-surface-inline.h" #define static cairo_warn static typedef struct _cairo_xml_surface cairo_xml_surface_t; typedef struct _cairo_xml { cairo_device_t base; cairo_output_stream_t *stream; int indent; } cairo_xml_t; struct _cairo_xml_surface { cairo_surface_t base; double width, height; }; slim_hidden_proto (cairo_xml_for_recording_surface); static const cairo_surface_backend_t _cairo_xml_surface_backend; static const char * _operator_to_string (cairo_operator_t op) { static const char *names[] = { "CLEAR", /* CAIRO_OPERATOR_CLEAR */ "SOURCE", /* CAIRO_OPERATOR_SOURCE */ "OVER", /* CAIRO_OPERATOR_OVER */ "IN", /* CAIRO_OPERATOR_IN */ "OUT", /* CAIRO_OPERATOR_OUT */ "ATOP", /* CAIRO_OPERATOR_ATOP */ "DEST", /* CAIRO_OPERATOR_DEST */ "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ "XOR", /* CAIRO_OPERATOR_XOR */ "ADD", /* CAIRO_OPERATOR_ADD */ "SATURATE", /* CAIRO_OPERATOR_SATURATE */ "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ "SCREEN", /* CAIRO_OPERATOR_SCREEN */ "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ "DARKEN", /* CAIRO_OPERATOR_DARKEN */ "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ }; assert (op < ARRAY_LENGTH (names)); return names[op]; } static const char * _extend_to_string (cairo_extend_t extend) { static const char *names[] = { "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ }; assert (extend < ARRAY_LENGTH (names)); return names[extend]; } static const char * _filter_to_string (cairo_filter_t filter) { static const char *names[] = { "FILTER_FAST", /* CAIRO_FILTER_FAST */ "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ "FILTER_BEST", /* CAIRO_FILTER_BEST */ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ }; assert (filter < ARRAY_LENGTH (names)); return names[filter]; } static const char * _fill_rule_to_string (cairo_fill_rule_t rule) { static const char *names[] = { "WINDING", /* CAIRO_FILL_RULE_WINDING */ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ }; assert (rule < ARRAY_LENGTH (names)); return names[rule]; } static const char * _antialias_to_string (cairo_antialias_t antialias) { static const char *names[] = { "DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ "NONE", /* CAIRO_ANTIALIAS_NONE */ "GRAY", /* CAIRO_ANTIALIAS_GRAY */ "SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ "FAST", /* CAIRO_ANTIALIAS_FAST */ "GOOD", /* CAIRO_ANTIALIAS_GOOD */ "BEST", /* CAIRO_ANTIALIAS_BEST */ }; assert (antialias < ARRAY_LENGTH (names)); return names[antialias]; } static const char * _line_cap_to_string (cairo_line_cap_t line_cap) { static const char *names[] = { "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ }; assert (line_cap < ARRAY_LENGTH (names)); return names[line_cap]; } static const char * _line_join_to_string (cairo_line_join_t line_join) { static const char *names[] = { "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ }; assert (line_join < ARRAY_LENGTH (names)); return names[line_join]; } static const char * _content_to_string (cairo_content_t content) { switch (content) { case CAIRO_CONTENT_ALPHA: return "ALPHA"; case CAIRO_CONTENT_COLOR: return "COLOR"; default: case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; } } static const char * _format_to_string (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: return "ARGB32"; case CAIRO_FORMAT_RGB30: return "RGB30"; case CAIRO_FORMAT_RGB24: return "RGB24"; case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; case CAIRO_FORMAT_A8: return "A8"; case CAIRO_FORMAT_A1: return "A1"; case CAIRO_FORMAT_INVALID: return "INVALID"; } ASSERT_NOT_REACHED; return "INVALID"; } static cairo_status_t _device_flush (void *abstract_device) { cairo_xml_t *xml = abstract_device; cairo_status_t status; status = _cairo_output_stream_flush (xml->stream); return status; } static void _device_destroy (void *abstract_device) { cairo_xml_t *xml = abstract_device; cairo_status_t status; status = _cairo_output_stream_destroy (xml->stream); free (xml); } static const cairo_device_backend_t _cairo_xml_device_backend = { CAIRO_DEVICE_TYPE_XML, NULL, NULL, /* lock, unlock */ _device_flush, NULL, /* finish */ _device_destroy }; static cairo_device_t * _cairo_xml_create_internal (cairo_output_stream_t *stream) { cairo_xml_t *xml; xml = malloc (sizeof (cairo_xml_t)); if (unlikely (xml == NULL)) return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); memset (xml, 0, sizeof (cairo_xml_t)); _cairo_device_init (&xml->base, &_cairo_xml_device_backend); xml->indent = 0; xml->stream = stream; return &xml->base; } static void _cairo_xml_indent (cairo_xml_t *xml, int indent) { xml->indent += indent; assert (xml->indent >= 0); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) { va_list ap; char indent[80]; int len; len = MIN (xml->indent, ARRAY_LENGTH (indent)); memset (indent, ' ', len); _cairo_output_stream_write (xml->stream, indent, len); va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); _cairo_output_stream_write (xml->stream, "\n", 1); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) { char indent[80]; int len; len = MIN (xml->indent, ARRAY_LENGTH (indent)); memset (indent, ' ', len); _cairo_output_stream_write (xml->stream, indent, len); if (fmt != NULL) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } static void CAIRO_PRINTF_FORMAT (2, 3) _cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) { if (fmt != NULL) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (xml->stream, fmt, ap); va_end (ap); } _cairo_output_stream_write (xml->stream, "\n", 1); } static cairo_surface_t * _cairo_xml_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_rectangle_t extents; extents.x = extents.y = 0; extents.width = width; extents.height = height; return cairo_recording_surface_create (content, &extents); } static cairo_bool_t _cairo_xml_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_xml_surface_t *surface = abstract_surface; if (surface->width < 0 || surface->height < 0) return FALSE; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } static cairo_status_t _cairo_xml_move_to (void *closure, const cairo_point_t *p1) { _cairo_xml_printf_continue (closure, " %f %f m", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_line_to (void *closure, const cairo_point_t *p1) { _cairo_xml_printf_continue (closure, " %f %f l", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y), _cairo_fixed_to_double (p3->x), _cairo_fixed_to_double (p3->y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_close_path (void *closure) { _cairo_xml_printf_continue (closure, " h"); return CAIRO_STATUS_SUCCESS; } static void _cairo_xml_emit_path (cairo_xml_t *xml, const cairo_path_fixed_t *path) { cairo_status_t status; _cairo_xml_printf_start (xml, ""); status = _cairo_path_fixed_interpret (path, _cairo_xml_move_to, _cairo_xml_line_to, _cairo_xml_curve_to, _cairo_xml_close_path, xml); assert (status == CAIRO_STATUS_SUCCESS); _cairo_xml_printf_end (xml, ""); } static void _cairo_xml_emit_string (cairo_xml_t *xml, const char *node, const char *data) { _cairo_xml_printf (xml, "<%s>%s", node, data, node); } static void _cairo_xml_emit_double (cairo_xml_t *xml, const char *node, double data) { _cairo_xml_printf (xml, "<%s>%f", node, data, node); } static cairo_xml_t * to_xml (cairo_xml_surface_t *surface) { return (cairo_xml_t *) surface->base.device; } static cairo_status_t _cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface, cairo_clip_t *clip) { cairo_box_t *box; cairo_status_t status; cairo_xml_t *xml; int n; if (clip->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) { box = &clip->boxes[0]; if (box->p1.x <= 0 && box->p1.y <= 0 && box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) && box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } xml = to_xml (surface); _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); for (n = 0; n < clip->num_boxes; n++) { box = &clip->boxes[n]; _cairo_xml_printf_start (xml, "%f %f m", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_end (xml, " h"); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); _cairo_xml_emit_double (xml, "tolerance", 1.0); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (CAIRO_ANTIALIAS_NONE)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (CAIRO_FILL_RULE_WINDING)); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, cairo_clip_path_t *clip_path) { cairo_box_t box; cairo_status_t status; cairo_xml_t *xml; if (clip_path == NULL) return CAIRO_STATUS_SUCCESS; status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); if (unlikely (status)) return status; /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && _cairo_path_fixed_is_box (&clip_path->path, &box)) { if (box.p1.x <= 0 && box.p1.y <= 0 && box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } xml = to_xml (surface); _cairo_xml_printf_start (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_path (xml, &clip_path->path); _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (clip_path->antialias)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (clip_path->fill_rule)); _cairo_xml_indent (xml, -2); _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, const cairo_clip_t *clip) { cairo_status_t status; if (clip == NULL) return CAIRO_STATUS_SUCCESS; status = _cairo_xml_surface_emit_clip_boxes (surface, clip); if (unlikely (status)) return status; return _cairo_xml_surface_emit_clip_path (surface, clip->path); } static cairo_status_t _cairo_xml_emit_solid (cairo_xml_t *xml, const cairo_solid_pattern_t *solid) { _cairo_xml_printf (xml, "%f %f %f %f", solid->color.red, solid->color.green, solid->color.blue, solid->color.alpha); return CAIRO_STATUS_SUCCESS; } static void _cairo_xml_emit_matrix (cairo_xml_t *xml, const cairo_matrix_t *matrix) { if (! _cairo_matrix_is_identity (matrix)) { _cairo_xml_printf (xml, "%f %f %f %f %f %f", matrix->xx, matrix->yx, matrix->xy, matrix->yy, matrix->x0, matrix->y0); } } static void _cairo_xml_emit_gradient (cairo_xml_t *xml, const cairo_gradient_pattern_t *gradient) { unsigned int i; for (i = 0; i < gradient->n_stops; i++) { _cairo_xml_printf (xml, "%f %f %f %f %f", gradient->stops[i].offset, gradient->stops[i].color.red, gradient->stops[i].color.green, gradient->stops[i].color.blue, gradient->stops[i].color.alpha); } } static cairo_status_t _cairo_xml_emit_linear (cairo_xml_t *xml, const cairo_linear_pattern_t *linear) { _cairo_xml_printf (xml, "", linear->pd1.x, linear->pd1.y, linear->pd2.x, linear->pd2.y); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &linear->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_radial (cairo_xml_t *xml, const cairo_radial_pattern_t *radial) { _cairo_xml_printf (xml, "", radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius, radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &radial->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _write_func (void *closure, const unsigned char *data, unsigned len) { _cairo_output_stream_write (closure, data, len); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_image (cairo_xml_t *xml, cairo_image_surface_t *image) { cairo_output_stream_t *stream; cairo_status_t status; _cairo_xml_printf_start (xml, "", image->width, image->height, _format_to_string (image->format)); stream = _cairo_base64_stream_create (xml->stream); status = cairo_surface_write_to_png_stream (&image->base, _write_func, stream); assert (status == CAIRO_STATUS_SUCCESS); status = _cairo_output_stream_destroy (stream); if (unlikely (status)) return status; _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_surface (cairo_xml_t *xml, const cairo_surface_pattern_t *pattern) { cairo_surface_t *source = pattern->surface; cairo_status_t status; if (_cairo_surface_is_recording (source)) { status = cairo_xml_for_recording_surface (&xml->base, source); } else { cairo_image_surface_t *image; void *image_extra; status = _cairo_surface_acquire_source_image (source, &image, &image_extra); if (unlikely (status)) return status; status = _cairo_xml_emit_image (xml, image); _cairo_surface_release_source_image (source, image, image_extra); } return status; } static cairo_status_t _cairo_xml_emit_pattern (cairo_xml_t *xml, const char *source_or_mask, const cairo_pattern_t *pattern) { cairo_status_t status; _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); _cairo_xml_indent (xml, 2); switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_RADIAL: status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); break; case CAIRO_PATTERN_TYPE_SURFACE: status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); break; default: ASSERT_NOT_REACHED; status = CAIRO_INT_STATUS_UNSUPPORTED; break; } if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { _cairo_xml_emit_matrix (xml, &pattern->matrix); _cairo_xml_printf (xml, "%s", _extend_to_string (pattern->extend)); _cairo_xml_printf (xml, "%s", _filter_to_string (pattern->filter)); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "", source_or_mask); return status; } static cairo_int_status_t _cairo_xml_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "mask", mask); if (unlikely (status)) return status; _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); _cairo_xml_emit_double (xml, "line-width", style->line_width); _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; if (style->num_dashes) { unsigned int i; _cairo_xml_printf_start (xml, "", style->dash_offset); for (i = 0; i < style->num_dashes; i++) _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); _cairo_xml_printf_end (xml, ""); } _cairo_xml_emit_path (xml, path); _cairo_xml_emit_double (xml, "tolerance", tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); _cairo_xml_emit_matrix (xml, ctm); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_xml_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; _cairo_xml_emit_path (xml, path); _cairo_xml_emit_double (xml, "tolerance", tolerance); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } #if CAIRO_HAS_FT_FONT #include "cairo-ft-private.h" static cairo_status_t _cairo_xml_emit_type42_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font) { const cairo_scaled_font_backend_t *backend; cairo_output_stream_t *base64_stream; cairo_output_stream_t *zlib_stream; cairo_status_t status, status2; unsigned long size; uint32_t len; uint8_t *buf; backend = scaled_font->backend; if (backend->load_truetype_table == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); if (unlikely (status)) return status; buf = malloc (size); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); if (unlikely (status)) { free (buf); return status; } _cairo_xml_printf_start (xml, "", _cairo_ft_scaled_font_get_load_flags (scaled_font)); base64_stream = _cairo_base64_stream_create (xml->stream); len = size; _cairo_output_stream_write (base64_stream, &len, sizeof (len)); zlib_stream = _cairo_deflate_stream_create (base64_stream); _cairo_output_stream_write (zlib_stream, buf, size); free (buf); status2 = _cairo_output_stream_destroy (zlib_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (base64_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; _cairo_xml_printf_end (xml, ""); return status; } #else static cairo_status_t _cairo_xml_emit_type42_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font) { return CAIRO_INT_STATUS_UNSUPPORTED; } #endif static cairo_status_t _cairo_xml_emit_type3_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs) { _cairo_xml_printf_start (xml, ""); _cairo_xml_printf_end (xml, ""); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_xml_emit_scaled_font (cairo_xml_t *xml, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs) { cairo_int_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); status = _cairo_xml_emit_type42_font (xml, scaled_font); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_xml_emit_type3_font (xml, scaled_font, glyphs, num_glyphs); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return status; } static cairo_int_status_t _cairo_xml_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); cairo_status_t status; int i; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); status = _cairo_xml_surface_emit_clip (surface, clip); if (unlikely (status)) return status; status = _cairo_xml_emit_pattern (xml, "source", source); if (unlikely (status)) return status; status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); if (unlikely (status)) return status; for (i = 0; i < num_glyphs; i++) { _cairo_xml_printf (xml, "%f %f", glyphs[i].index, glyphs[i].x, glyphs[i].y); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t _cairo_xml_surface_backend = { CAIRO_SURFACE_TYPE_XML, NULL, _cairo_default_context_create, _cairo_xml_surface_create_similar, NULL, /* create_similar_image */ NULL, /* map_to_image */ NULL, /* unmap_image */ _cairo_surface_default_source, NULL, /* acquire source image */ NULL, /* release source image */ NULL, /* snapshot */ NULL, /* copy page */ NULL, /* show page */ _cairo_xml_surface_get_extents, NULL, /* get_font_options */ NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_xml_surface_paint, _cairo_xml_surface_mask, _cairo_xml_surface_stroke, _cairo_xml_surface_fill, NULL, /* fill_stroke */ _cairo_xml_surface_glyphs, }; static cairo_surface_t * _cairo_xml_surface_create_internal (cairo_device_t *device, cairo_content_t content, double width, double height) { cairo_xml_surface_t *surface; surface = malloc (sizeof (cairo_xml_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xml_surface_backend, device, content); surface->width = width; surface->height = height; return &surface->base; } cairo_device_t * cairo_xml_create (const char *filename) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create_for_filename (filename); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_xml_create_internal (stream); } cairo_device_t * cairo_xml_create_for_stream (cairo_write_func_t write_func, void *closure) { cairo_output_stream_t *stream; cairo_status_t status; stream = _cairo_output_stream_create (write_func, NULL, closure); if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); return _cairo_xml_create_internal (stream); } cairo_surface_t * cairo_xml_surface_create (cairo_device_t *device, cairo_content_t content, double width, double height) { if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (device->status)) return _cairo_surface_create_in_error (device->status); return _cairo_xml_surface_create_internal (device, content, width, height); } cairo_status_t cairo_xml_for_recording_surface (cairo_device_t *device, cairo_surface_t *recording_surface) { cairo_box_t bbox; cairo_rectangle_int_t extents; cairo_surface_t *surface; cairo_xml_t *xml; cairo_status_t status; if (unlikely (device->status)) return device->status; if (unlikely (recording_surface->status)) return recording_surface->status; if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); if (unlikely (! _cairo_surface_is_recording (recording_surface))) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, &bbox, NULL); if (unlikely (status)) return status; _cairo_box_round_to_rectangle (&bbox, &extents); surface = _cairo_xml_surface_create_internal (device, recording_surface->content, extents.width, extents.height); if (unlikely (surface->status)) return surface->status; xml = (cairo_xml_t *) device; _cairo_xml_printf (xml, "", _content_to_string (recording_surface->content), extents.width, extents.height); _cairo_xml_indent (xml, 2); cairo_surface_set_device_offset (surface, -extents.x, -extents.y); status = _cairo_recording_surface_replay (recording_surface, surface); cairo_surface_destroy (surface); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); return status; } slim_hidden_def (cairo_xml_for_recording_surface); Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo-xml.h000066400000000000000000000042601271037650300243460ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): * Chris Wilson */ #ifndef CAIRO_XML_H #define CAIRO_XML_H #include "cairo.h" #if CAIRO_HAS_XML_SURFACE CAIRO_BEGIN_DECLS cairo_public cairo_device_t * cairo_xml_create (const char *filename); cairo_public cairo_device_t * cairo_xml_create_for_stream (cairo_write_func_t write_func, void *closure); cairo_public cairo_surface_t * cairo_xml_surface_create (cairo_device_t *xml, cairo_content_t content, double width, double height); cairo_public cairo_status_t cairo_xml_for_recording_surface (cairo_device_t *xml, cairo_surface_t *surface); CAIRO_END_DECLS #else /*CAIRO_HAS_XML_SURFACE*/ # error Cairo was not compiled with support for the XML backend #endif /*CAIRO_HAS_XML_SURFACE*/ #endif /*CAIRO_XML_H*/ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo.c000066400000000000000000003343141271037650300235510ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Chris Wilson */ #include "cairoint.h" #include "cairo-private.h" #include "cairo-backend-private.h" #include "cairo-error-private.h" #include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-private.h" #include "cairo-surface-backend-private.h" #include /** * SECTION:cairo * @Title: cairo_t * @Short_Description: The cairo drawing context * @See_Also: #cairo_surface_t * * #cairo_t is the main object used when drawing with cairo. To * draw with cairo, you create a #cairo_t, set the target surface, * and drawing options for the #cairo_t, create shapes with * functions like cairo_move_to() and cairo_line_to(), and then * draw shapes with cairo_stroke() or cairo_fill(). * * #cairo_t's can be pushed to a stack via cairo_save(). * They may then safely be changed, without losing the current state. * Use cairo_restore() to restore to the saved state. **/ /** * SECTION:cairo-text * @Title: text * @Short_Description: Rendering text and glyphs * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(), * cairo_glyph_path() * * The functions with text in their name form cairo's * toy text API. The toy API takes UTF-8 encoded * text and is limited in its functionality to rendering simple * left-to-right text with no advanced features. That means for example * that most complex scripts like Hebrew, Arabic, and Indic scripts are * out of question. No kerning or correct positioning of diacritical marks * either. The font selection is pretty limited too and doesn't handle the * case that the selected font does not cover the characters in the text. * This set of functions are really that, a toy text API, for testing and * demonstration purposes. Any serious application should avoid them. * * The functions with glyphs in their name form cairo's * low-level text API. The low-level API relies on * the user to convert text to a set of glyph indexes and positions. This * is a very hard problem and is best handled by external libraries, like * the pangocairo that is part of the Pango text layout and rendering library. * Pango is available from http://www.pango.org/. **/ /** * SECTION:cairo-transforms * @Title: Transformations * @Short_Description: Manipulating the current transformation matrix * @See_Also: #cairo_matrix_t * * The current transformation matrix, ctm, is a * two-dimensional affine transformation that maps all coordinates and other * drawing instruments from the user space into the * surface's canonical coordinate system, also known as the device * space. **/ #define DEFINE_NIL_CONTEXT(status) \ { \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ { 0, 0, 0, NULL }, /* user_data */ \ NULL \ } static const cairo_t _cairo_nil[] = { DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_MEMORY), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_RESTORE), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_POP_GROUP), DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_CURRENT_POINT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MATRIX), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STATUS), DEFINE_NIL_CONTEXT (CAIRO_STATUS_NULL_POINTER), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRING), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_PATH_DATA), DEFINE_NIL_CONTEXT (CAIRO_STATUS_READ_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_WRITE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_FINISHED), DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_TYPE_MISMATCH), DEFINE_NIL_CONTEXT (CAIRO_STATUS_PATTERN_TYPE_MISMATCH), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CONTENT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_FORMAT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_VISUAL), DEFINE_NIL_CONTEXT (CAIRO_STATUS_FILE_NOT_FOUND), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DASH), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DSC_COMMENT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_INDEX), DEFINE_NIL_CONTEXT (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE), DEFINE_NIL_CONTEXT (CAIRO_STATUS_TEMP_FILE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRIDE), DEFINE_NIL_CONTEXT (CAIRO_STATUS_FONT_TYPE_MISMATCH), DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_IMMUTABLE), DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_NEGATIVE_COUNT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CLUSTERS), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SLANT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_WEIGHT), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SIZE), DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED), DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH), DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION), DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED) }; COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1); /** * _cairo_set_error: * @cr: a cairo context * @status: a status value indicating an error * * Atomically sets cr->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS. * * All assignments of an error status to cr->status should happen * through _cairo_set_error(). Note that due to the nature of the atomic * operation, it is not safe to call this function on the nil objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error. **/ static void _cairo_set_error (cairo_t *cr, cairo_status_t status) { /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ _cairo_status_set_error (&cr->status, _cairo_error (status)); } cairo_t * _cairo_create_in_error (cairo_status_t status) { cairo_t *cr; assert (status != CAIRO_STATUS_SUCCESS); cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY]; assert (status == cr->status); return cr; } /** * cairo_create: * @target: target surface for the context * * Creates a new #cairo_t with all graphics state parameters set to * default values and with @target as a target surface. The target * surface should be constructed with a backend-specific function such * as cairo_image_surface_create() (or any other * cairo_backend_surface_create() * variant). * * This function references @target, so you can immediately * call cairo_surface_destroy() on it if you don't need to * maintain a separate reference to it. * * Return value: a newly allocated #cairo_t with a reference * count of 1. The initial reference count should be released * with cairo_destroy() when you are done using the #cairo_t. * This function never returns %NULL. If memory cannot be * allocated, a special #cairo_t object will be returned on * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If * you attempt to target a surface which does not support * writing (such as #cairo_mime_surface_t) then a * %CAIRO_STATUS_WRITE_ERROR will be raised. You can use this * object normally, but no drawing will be done. * * Since: 1.0 **/ cairo_t * cairo_create (cairo_surface_t *target) { if (unlikely (target == NULL)) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); if (unlikely (target->status)) return _cairo_create_in_error (target->status); if (target->backend->create_context == NULL) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); return target->backend->create_context (target); } slim_hidden_def (cairo_create); void _cairo_init (cairo_t *cr, const cairo_backend_t *backend) { CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1); cr->status = CAIRO_STATUS_SUCCESS; _cairo_user_data_array_init (&cr->user_data); cr->backend = backend; } /** * cairo_reference: * @cr: a #cairo_t * * Increases the reference count on @cr by one. This prevents * @cr from being destroyed until a matching call to cairo_destroy() * is made. * * The number of references to a #cairo_t can be get using * cairo_get_reference_count(). * * Return value: the referenced #cairo_t. * * Since: 1.0 **/ cairo_t * cairo_reference (cairo_t *cr) { if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return cr; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); _cairo_reference_count_inc (&cr->ref_count); return cr; } void _cairo_fini (cairo_t *cr) { _cairo_user_data_array_fini (&cr->user_data); } /** * cairo_destroy: * @cr: a #cairo_t * * Decreases the reference count on @cr by one. If the result * is zero, then @cr and all associated resources are freed. * See cairo_reference(). * * Since: 1.0 **/ void cairo_destroy (cairo_t *cr) { if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) return; cr->backend->destroy (cr); } slim_hidden_def (cairo_destroy); /** * cairo_get_user_data: * @cr: a #cairo_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @cr using the specified * key. If no user data has been attached with the given key this * function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.4 **/ void * cairo_get_user_data (cairo_t *cr, const cairo_user_data_key_t *key) { return _cairo_user_data_array_get_data (&cr->user_data, key); } /** * cairo_set_user_data: * @cr: a #cairo_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the #cairo_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @cr. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.4 **/ cairo_status_t cairo_set_user_data (cairo_t *cr, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy) { if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return cr->status; return _cairo_user_data_array_set_data (&cr->user_data, key, user_data, destroy); } /** * cairo_get_reference_count: * @cr: a #cairo_t * * Returns the current reference count of @cr. * * Return value: the current reference count of @cr. If the * object is a nil object, 0 will be returned. * * Since: 1.4 **/ unsigned int cairo_get_reference_count (cairo_t *cr) { if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count); } /** * cairo_save: * @cr: a #cairo_t * * Makes a copy of the current state of @cr and saves it * on an internal stack of saved states for @cr. When * cairo_restore() is called, @cr will be restored to * the saved state. Multiple calls to cairo_save() and * cairo_restore() can be nested; each call to cairo_restore() * restores the state from the matching paired cairo_save(). * * It isn't necessary to clear all saved states before * a #cairo_t is freed. If the reference count of a #cairo_t * drops to zero in response to a call to cairo_destroy(), * any saved states will be freed along with the #cairo_t. * * Since: 1.0 **/ void cairo_save (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->save (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_save); /** * cairo_restore: * @cr: a #cairo_t * * Restores @cr to the state saved by a preceding call to * cairo_save() and removes that state from the stack of * saved states. * * Since: 1.0 **/ void cairo_restore (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->restore (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_restore); /** * cairo_push_group: * @cr: a cairo context * * Temporarily redirects drawing to an intermediate surface known as a * group. The redirection lasts until the group is completed by a call * to cairo_pop_group() or cairo_pop_group_to_source(). These calls * provide the result of any drawing to the group as a pattern, * (either as an explicit object, or set as the source pattern). * * This group functionality can be convenient for performing * intermediate compositing. One common use of a group is to render * objects as opaque within the group, (so that they occlude each * other), and then blend the result with translucence onto the * destination. * * Groups can be nested arbitrarily deep by making balanced calls to * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new * target group onto/from a stack. * * The cairo_push_group() function calls cairo_save() so that any * changes to the graphics state will not be visible outside the * group, (the pop_group functions call cairo_restore()). * * By default the intermediate group will have a content type of * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for * the group by using cairo_push_group_with_content() instead. * * As an example, here is how one might fill and stroke a path with * translucence, but without any portion of the fill being visible * under the stroke: * * * cairo_push_group (cr); * cairo_set_source (cr, fill_pattern); * cairo_fill_preserve (cr); * cairo_set_source (cr, stroke_pattern); * cairo_stroke (cr); * cairo_pop_group_to_source (cr); * cairo_paint_with_alpha (cr, alpha); * * * Since: 1.2 **/ void cairo_push_group (cairo_t *cr) { cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); } /** * cairo_push_group_with_content: * @cr: a cairo context * @content: a #cairo_content_t indicating the type of group that * will be created * * Temporarily redirects drawing to an intermediate surface known as a * group. The redirection lasts until the group is completed by a call * to cairo_pop_group() or cairo_pop_group_to_source(). These calls * provide the result of any drawing to the group as a pattern, * (either as an explicit object, or set as the source pattern). * * The group will have a content type of @content. The ability to * control this content type is the only distinction between this * function and cairo_push_group() which you should see for a more * detailed description of group rendering. * * Since: 1.2 **/ void cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->push_group (cr, content); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_push_group_with_content); /** * cairo_pop_group: * @cr: a cairo context * * Terminates the redirection begun by a call to cairo_push_group() or * cairo_push_group_with_content() and returns a new pattern * containing the results of all drawing operations performed to the * group. * * The cairo_pop_group() function calls cairo_restore(), (balancing a * call to cairo_save() by the push_group function), so that any * changes to the graphics state will not be visible outside the * group. * * Return value: a newly created (surface) pattern containing the * results of all drawing operations performed to the group. The * caller owns the returned object and should call * cairo_pattern_destroy() when finished with it. * * Since: 1.2 **/ cairo_pattern_t * cairo_pop_group (cairo_t *cr) { cairo_pattern_t *group_pattern; if (unlikely (cr->status)) return _cairo_pattern_create_in_error (cr->status); group_pattern = cr->backend->pop_group (cr); if (unlikely (group_pattern->status)) _cairo_set_error (cr, group_pattern->status); return group_pattern; } slim_hidden_def(cairo_pop_group); /** * cairo_pop_group_to_source: * @cr: a cairo context * * Terminates the redirection begun by a call to cairo_push_group() or * cairo_push_group_with_content() and installs the resulting pattern * as the source pattern in the given cairo context. * * The behavior of this function is equivalent to the sequence of * operations: * * * cairo_pattern_t *group = cairo_pop_group (cr); * cairo_set_source (cr, group); * cairo_pattern_destroy (group); * * * but is more convenient as their is no need for a variable to store * the short-lived pointer to the pattern. * * The cairo_pop_group() function calls cairo_restore(), (balancing a * call to cairo_save() by the push_group function), so that any * changes to the graphics state will not be visible outside the * group. * * Since: 1.2 **/ void cairo_pop_group_to_source (cairo_t *cr) { cairo_pattern_t *group_pattern; group_pattern = cairo_pop_group (cr); cairo_set_source (cr, group_pattern); cairo_pattern_destroy (group_pattern); } /** * cairo_set_operator: * @cr: a #cairo_t * @op: a compositing operator, specified as a #cairo_operator_t * * Sets the compositing operator to be used for all drawing * operations. See #cairo_operator_t for details on the semantics of * each available compositing operator. * * The default operator is %CAIRO_OPERATOR_OVER. * * Since: 1.0 **/ void cairo_set_operator (cairo_t *cr, cairo_operator_t op) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_operator (cr, op); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_operator); #if 0 /** * cairo_set_opacity: * @cr: a #cairo_t * @opacity: the level of opacity to use when compositing * * Sets the compositing opacity to be used for all drawing * operations. The effect is to fade out the operations * using the alpha value. * * The default opacity is 1. * * Since: TBD **/ void cairo_set_opacity (cairo_t *cr, double opacity) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_opacity (cr, opacity); if (unlikely (status)) _cairo_set_error (cr, status); } #endif /** * cairo_set_source_rgb: * @cr: a cairo context * @red: red component of color * @green: green component of color * @blue: blue component of color * * Sets the source pattern within @cr to an opaque color. This opaque * color will then be used for any subsequent drawing operation until * a new source pattern is set. * * The color components are floating point numbers in the range 0 to * 1. If the values passed in are outside that range, they will be * clamped. * * The default source pattern is opaque black, (that is, it is * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)). * * Since: 1.0 **/ void cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_source_rgba (cr, red, green, blue, 1.); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source_rgb); /** * cairo_set_source_rgba: * @cr: a cairo context * @red: red component of color * @green: green component of color * @blue: blue component of color * @alpha: alpha component of color * * Sets the source pattern within @cr to a translucent color. This * color will then be used for any subsequent drawing operation until * a new source pattern is set. * * The color and alpha components are floating point numbers in the * range 0 to 1. If the values passed in are outside that range, they * will be clamped. * * The default source pattern is opaque black, (that is, it is * equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)). * * Since: 1.0 **/ void cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_source_rgba (cr, red, green, blue, alpha); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_set_source_surface: * @cr: a cairo context * @surface: a surface to be used to set the source pattern * @x: User-space X coordinate for surface origin * @y: User-space Y coordinate for surface origin * * This is a convenience function for creating a pattern from @surface * and setting it as the source in @cr with cairo_set_source(). * * The @x and @y parameters give the user-space coordinate at which * the surface origin should appear. (The surface origin is its * upper-left corner before any transformation has been applied.) The * @x and @y parameters are negated and then set as translation values * in the pattern matrix. * * Other than the initial translation pattern matrix, as described * above, all other pattern attributes, (such as its extend mode), are * set to the default values as in cairo_pattern_create_for_surface(). * The resulting pattern can be queried with cairo_get_source() so * that these attributes can be modified if desired, (eg. to create a * repeating pattern with cairo_pattern_set_extend()). * * Since: 1.0 **/ void cairo_set_source_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y) { cairo_status_t status; if (unlikely (cr->status)) return; if (unlikely (surface == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } status = cr->backend->set_source_surface (cr, surface, x, y); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source_surface); /** * cairo_set_source: * @cr: a cairo context * @source: a #cairo_pattern_t to be used as the source for * subsequent drawing operations. * * Sets the source pattern within @cr to @source. This pattern * will then be used for any subsequent drawing operation until a new * source pattern is set. * * Note: The pattern's transformation matrix will be locked to the * user space in effect at the time of cairo_set_source(). This means * that further modifications of the current transformation matrix * will not affect the source pattern. See cairo_pattern_set_matrix(). * * The default source pattern is a solid pattern that is opaque black, * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, * 0.0)). * * Since: 1.0 **/ void cairo_set_source (cairo_t *cr, cairo_pattern_t *source) { cairo_status_t status; if (unlikely (cr->status)) return; if (unlikely (source == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } if (unlikely (source->status)) { _cairo_set_error (cr, source->status); return; } status = cr->backend->set_source (cr, source); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source); /** * cairo_get_source: * @cr: a cairo context * * Gets the current source pattern for @cr. * * Return value: the current source pattern. This object is owned by * cairo. To keep a reference to it, you must call * cairo_pattern_reference(). * * Since: 1.0 **/ cairo_pattern_t * cairo_get_source (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_pattern_create_in_error (cr->status); return cr->backend->get_source (cr); } /** * cairo_set_tolerance: * @cr: a #cairo_t * @tolerance: the tolerance, in device units (typically pixels) * * Sets the tolerance used when converting paths into trapezoids. * Curved segments of the path will be subdivided until the maximum * deviation between the original path and the polygonal approximation * is less than @tolerance. The default value is 0.1. A larger * value will give better performance, a smaller value, better * appearance. (Reducing the value from the default value of 0.1 * is unlikely to improve appearance significantly.) The accuracy of paths * within Cairo is limited by the precision of its internal arithmetic, and * the prescribed @tolerance is restricted to the smallest * representable internal value. * * Since: 1.0 **/ void cairo_set_tolerance (cairo_t *cr, double tolerance) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_tolerance (cr, tolerance); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_tolerance); /** * cairo_set_antialias: * @cr: a #cairo_t * @antialias: the new antialiasing mode * * Set the antialiasing mode of the rasterizer used for drawing shapes. * This value is a hint, and a particular backend may or may not support * a particular value. At the current time, no backend supports * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes. * * Note that this option does not affect text rendering, instead see * cairo_font_options_set_antialias(). * * Since: 1.0 **/ void cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_antialias (cr, antialias); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_set_fill_rule: * @cr: a #cairo_t * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t * * Set the current fill rule within the cairo context. The fill rule * is used to determine which regions are inside or outside a complex * (potentially self-intersecting) path. The current fill rule affects * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details * on the semantics of each available fill rule. * * The default fill rule is %CAIRO_FILL_RULE_WINDING. * * Since: 1.0 **/ void cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_fill_rule (cr, fill_rule); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_set_line_width: * @cr: a #cairo_t * @width: a line width * * Sets the current line width within the cairo context. The line * width value specifies the diameter of a pen that is circular in * user space, (though device-space pen may be an ellipse in general * due to scaling/shear/rotation of the CTM). * * Note: When the description above refers to user space and CTM it * refers to the user space and CTM in effect at the time of the * stroking operation, not the user space and CTM in effect at the * time of the call to cairo_set_line_width(). The simplest usage * makes both of these spaces identical. That is, if there is no * change to the CTM between a call to cairo_set_line_width() and the * stroking operation, then one can just pass user-space values to * cairo_set_line_width() and ignore this note. * * As with the other stroke parameters, the current line width is * examined by cairo_stroke(), cairo_stroke_extents(), and * cairo_stroke_to_path(), but does not have any effect during path * construction. * * The default line width value is 2.0. * * Since: 1.0 **/ void cairo_set_line_width (cairo_t *cr, double width) { cairo_status_t status; if (unlikely (cr->status)) return; if (width < 0.) width = 0.; status = cr->backend->set_line_width (cr, width); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_width); /** * cairo_set_line_cap: * @cr: a cairo context * @line_cap: a line cap style * * Sets the current line cap style within the cairo context. See * #cairo_line_cap_t for details about how the available line cap * styles are drawn. * * As with the other stroke parameters, the current line cap style is * examined by cairo_stroke(), cairo_stroke_extents(), and * cairo_stroke_to_path(), but does not have any effect during path * construction. * * The default line cap style is %CAIRO_LINE_CAP_BUTT. * * Since: 1.0 **/ void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_line_cap (cr, line_cap); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_cap); /** * cairo_set_line_join: * @cr: a cairo context * @line_join: a line join style * * Sets the current line join style within the cairo context. See * #cairo_line_join_t for details about how the available line join * styles are drawn. * * As with the other stroke parameters, the current line join style is * examined by cairo_stroke(), cairo_stroke_extents(), and * cairo_stroke_to_path(), but does not have any effect during path * construction. * * The default line join style is %CAIRO_LINE_JOIN_MITER. * * Since: 1.0 **/ void cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_line_join (cr, line_join); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_line_join); /** * cairo_set_dash: * @cr: a cairo context * @dashes: an array specifying alternate lengths of on and off stroke portions * @num_dashes: the length of the dashes array * @offset: an offset into the dash pattern at which the stroke should start * * Sets the dash pattern to be used by cairo_stroke(). A dash pattern * is specified by @dashes, an array of positive values. Each value * provides the length of alternate "on" and "off" portions of the * stroke. The @offset specifies an offset into the pattern at which * the stroke begins. * * Each "on" segment will have caps applied as if the segment were a * separate sub-path. In particular, it is valid to use an "on" length * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order * to distributed dots or squares along a path. * * Note: The length values are in user-space units as evaluated at the * time of stroking. This is not necessarily the same as the user * space at the time of cairo_set_dash(). * * If @num_dashes is 0 dashing is disabled. * * If @num_dashes is 1 a symmetric pattern is assumed with alternating * on and off portions of the size specified by the single value in * @dashes. * * If any value in @dashes is negative, or if all values are 0, then * @cr will be put into an error state with a status of * %CAIRO_STATUS_INVALID_DASH. * * Since: 1.0 **/ void cairo_set_dash (cairo_t *cr, const double *dashes, int num_dashes, double offset) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_dash (cr, dashes, num_dashes, offset); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_get_dash_count: * @cr: a #cairo_t * * This function returns the length of the dash array in @cr (0 if dashing * is not currently in effect). * * See also cairo_set_dash() and cairo_get_dash(). * * Return value: the length of the dash array, or 0 if no dash array set. * * Since: 1.4 **/ int cairo_get_dash_count (cairo_t *cr) { int num_dashes; if (unlikely (cr->status)) return 0; cr->backend->get_dash (cr, NULL, &num_dashes, NULL); return num_dashes; } /** * cairo_get_dash: * @cr: a #cairo_t * @dashes: return value for the dash array, or %NULL * @offset: return value for the current dash offset, or %NULL * * Gets the current dash array. If not %NULL, @dashes should be big * enough to hold at least the number of values returned by * cairo_get_dash_count(). * * Since: 1.4 **/ void cairo_get_dash (cairo_t *cr, double *dashes, double *offset) { if (unlikely (cr->status)) return; cr->backend->get_dash (cr, dashes, NULL, offset); } /** * cairo_set_miter_limit: * @cr: a cairo context * @limit: miter limit to set * * Sets the current miter limit within the cairo context. * * If the current line join style is set to %CAIRO_LINE_JOIN_MITER * (see cairo_set_line_join()), the miter limit is used to determine * whether the lines should be joined with a bevel instead of a miter. * Cairo divides the length of the miter by the line width. * If the result is greater than the miter limit, the style is * converted to a bevel. * * As with the other stroke parameters, the current line miter limit is * examined by cairo_stroke(), cairo_stroke_extents(), and * cairo_stroke_to_path(), but does not have any effect during path * construction. * * The default miter limit value is 10.0, which will convert joins * with interior angles less than 11 degrees to bevels instead of * miters. For reference, a miter limit of 2.0 makes the miter cutoff * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 * degrees. * * A miter limit for a desired angle can be computed as: miter limit = * 1/sin(angle/2) * * Since: 1.0 **/ void cairo_set_miter_limit (cairo_t *cr, double limit) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_miter_limit (cr, limit); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_translate: * @cr: a cairo context * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * * Modifies the current transformation matrix (CTM) by translating the * user-space origin by (@tx, @ty). This offset is interpreted as a * user-space coordinate according to the CTM in place before the new * call to cairo_translate(). In other words, the translation of the * user-space origin takes place after any existing transformation. * * Since: 1.0 **/ void cairo_translate (cairo_t *cr, double tx, double ty) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->translate (cr, tx, ty); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_translate); /** * cairo_scale: * @cr: a cairo context * @sx: scale factor for the X dimension * @sy: scale factor for the Y dimension * * Modifies the current transformation matrix (CTM) by scaling the X * and Y user-space axes by @sx and @sy respectively. The scaling of * the axes takes place after any existing transformation of user * space. * * Since: 1.0 **/ void cairo_scale (cairo_t *cr, double sx, double sy) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->scale (cr, sx, sy); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_scale); /** * cairo_rotate: * @cr: a cairo context * @angle: angle (in radians) by which the user-space axes will be * rotated * * Modifies the current transformation matrix (CTM) by rotating the * user-space axes by @angle radians. The rotation of the axes takes * places after any existing transformation of user space. The * rotation direction for positive angles is from the positive X axis * toward the positive Y axis. * * Since: 1.0 **/ void cairo_rotate (cairo_t *cr, double angle) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rotate (cr, angle); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_transform: * @cr: a cairo context * @matrix: a transformation to be applied to the user-space axes * * Modifies the current transformation matrix (CTM) by applying * @matrix as an additional transformation. The new transformation of * user space takes place after any existing transformation. * * Since: 1.0 **/ void cairo_transform (cairo_t *cr, const cairo_matrix_t *matrix) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->transform (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_transform); /** * cairo_set_matrix: * @cr: a cairo context * @matrix: a transformation matrix from user space to device space * * Modifies the current transformation matrix (CTM) by setting it * equal to @matrix. * * Since: 1.0 **/ void cairo_set_matrix (cairo_t *cr, const cairo_matrix_t *matrix) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_matrix (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_matrix); /** * cairo_identity_matrix: * @cr: a cairo context * * Resets the current transformation matrix (CTM) by setting it equal * to the identity matrix. That is, the user-space and device-space * axes will be aligned and one user-space unit will transform to one * device-space unit. * * Since: 1.0 **/ void cairo_identity_matrix (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_identity_matrix (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_user_to_device: * @cr: a cairo context * @x: X value of coordinate (in/out parameter) * @y: Y value of coordinate (in/out parameter) * * Transform a coordinate from user space to device space by * multiplying the given point by the current transformation matrix * (CTM). * * Since: 1.0 **/ void cairo_user_to_device (cairo_t *cr, double *x, double *y) { if (unlikely (cr->status)) return; cr->backend->user_to_device (cr, x, y); } slim_hidden_def (cairo_user_to_device); /** * cairo_user_to_device_distance: * @cr: a cairo context * @dx: X component of a distance vector (in/out parameter) * @dy: Y component of a distance vector (in/out parameter) * * Transform a distance vector from user space to device space. This * function is similar to cairo_user_to_device() except that the * translation components of the CTM will be ignored when transforming * (@dx,@dy). * * Since: 1.0 **/ void cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) { if (unlikely (cr->status)) return; cr->backend->user_to_device_distance (cr, dx, dy); } slim_hidden_def (cairo_user_to_device_distance); /** * cairo_device_to_user: * @cr: a cairo * @x: X value of coordinate (in/out parameter) * @y: Y value of coordinate (in/out parameter) * * Transform a coordinate from device space to user space by * multiplying the given point by the inverse of the current * transformation matrix (CTM). * * Since: 1.0 **/ void cairo_device_to_user (cairo_t *cr, double *x, double *y) { if (unlikely (cr->status)) return; cr->backend->device_to_user (cr, x, y); } slim_hidden_def (cairo_device_to_user); /** * cairo_device_to_user_distance: * @cr: a cairo context * @dx: X component of a distance vector (in/out parameter) * @dy: Y component of a distance vector (in/out parameter) * * Transform a distance vector from device space to user space. This * function is similar to cairo_device_to_user() except that the * translation components of the inverse CTM will be ignored when * transforming (@dx,@dy). * * Since: 1.0 **/ void cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) { if (unlikely (cr->status)) return; cr->backend->device_to_user_distance (cr, dx, dy); } /** * cairo_new_path: * @cr: a cairo context * * Clears the current path. After this call there will be no path and * no current point. * * Since: 1.0 **/ void cairo_new_path (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->new_path (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_new_path); /** * cairo_new_sub_path: * @cr: a cairo context * * Begin a new sub-path. Note that the existing path is not * affected. After this call there will be no current point. * * In many cases, this call is not needed since new sub-paths are * frequently started with cairo_move_to(). * * A call to cairo_new_sub_path() is particularly useful when * beginning a new sub-path with one of the cairo_arc() calls. This * makes things easier as it is no longer necessary to manually * compute the arc's initial coordinates for a call to * cairo_move_to(). * * Since: 1.2 **/ void cairo_new_sub_path (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->new_sub_path (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_move_to: * @cr: a cairo context * @x: the X coordinate of the new position * @y: the Y coordinate of the new position * * Begin a new sub-path. After this call the current point will be (@x, * @y). * * Since: 1.0 **/ void cairo_move_to (cairo_t *cr, double x, double y) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->move_to (cr, x, y); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_move_to); /** * cairo_line_to: * @cr: a cairo context * @x: the X coordinate of the end of the new line * @y: the Y coordinate of the end of the new line * * Adds a line to the path from the current point to position (@x, @y) * in user-space coordinates. After this call the current point * will be (@x, @y). * * If there is no current point before the call to cairo_line_to() * this function will behave as cairo_move_to(@cr, @x, @y). * * Since: 1.0 **/ void cairo_line_to (cairo_t *cr, double x, double y) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->line_to (cr, x, y); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_line_to); /** * cairo_curve_to: * @cr: a cairo context * @x1: the X coordinate of the first control point * @y1: the Y coordinate of the first control point * @x2: the X coordinate of the second control point * @y2: the Y coordinate of the second control point * @x3: the X coordinate of the end of the curve * @y3: the Y coordinate of the end of the curve * * Adds a cubic Bézier spline to the path from the current point to * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and * (@x2, @y2) as the control points. After this call the current point * will be (@x3, @y3). * * If there is no current point before the call to cairo_curve_to() * this function will behave as if preceded by a call to * cairo_move_to(@cr, @x1, @y1). * * Since: 1.0 **/ void cairo_curve_to (cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->curve_to (cr, x1, y1, x2, y2, x3, y3); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_curve_to); /** * cairo_arc: * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc * @angle1: the start angle, in radians * @angle2: the end angle, in radians * * Adds a circular arc of the given @radius to the current path. The * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in * the direction of increasing angles to end at @angle2. If @angle2 is * less than @angle1 it will be progressively increased by * 2*M_PI until it is greater than @angle1. * * If there is a current point, an initial line segment will be added * to the path to connect the current point to the beginning of the * arc. If this initial line is undesired, it can be avoided by * calling cairo_new_sub_path() before calling cairo_arc(). * * Angles are measured in radians. An angle of 0.0 is in the direction * of the positive X axis (in user space). An angle of * M_PI/2.0 radians (90 degrees) is in the * direction of the positive Y axis (in user space). Angles increase * in the direction from the positive X axis toward the positive Y * axis. So with the default transformation matrix, angles increase in * a clockwise direction. * * (To convert from degrees to radians, use degrees * (M_PI / * 180.).) * * This function gives the arc in the direction of increasing angles; * see cairo_arc_negative() to get the arc in the direction of * decreasing angles. * * The arc is circular in user space. To achieve an elliptical arc, * you can scale the current transformation matrix by different * amounts in the X and Y directions. For example, to draw an ellipse * in the box given by @x, @y, @width, @height: * * * cairo_save (cr); * cairo_translate (cr, x + width / 2., y + height / 2.); * cairo_scale (cr, width / 2., height / 2.); * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); * cairo_restore (cr); * * * Since: 1.0 **/ void cairo_arc (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2) { cairo_status_t status; if (unlikely (cr->status)) return; if (angle2 < angle1) { /* increase angle2 by multiples of full circle until it * satisfies angle2 >= angle1 */ angle2 = fmod (angle2 - angle1, 2 * M_PI); if (angle2 < 0) angle2 += 2 * M_PI; angle2 += angle1; } status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_arc_negative: * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc * @radius: the radius of the arc * @angle1: the start angle, in radians * @angle2: the end angle, in radians * * Adds a circular arc of the given @radius to the current path. The * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in * the direction of decreasing angles to end at @angle2. If @angle2 is * greater than @angle1 it will be progressively decreased by * 2*M_PI until it is less than @angle1. * * See cairo_arc() for more details. This function differs only in the * direction of the arc between the two angles. * * Since: 1.0 **/ void cairo_arc_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2) { cairo_status_t status; if (unlikely (cr->status)) return; if (angle2 > angle1) { /* decrease angle2 by multiples of full circle until it * satisfies angle2 <= angle1 */ angle2 = fmod (angle2 - angle1, 2 * M_PI); if (angle2 > 0) angle2 -= 2 * M_PI; angle2 += angle1; } status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE); if (unlikely (status)) _cairo_set_error (cr, status); } /* XXX: NYI void cairo_arc_to (cairo_t *cr, double x1, double y1, double x2, double y2, double radius) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->arc_to (cr, x1, y1, x2, y2, radius); if (unlikely (status)) _cairo_set_error (cr, status); } void cairo_rel_arc_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double radius) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rel_arc_to (cr, dx1, dy1, dx2, dy2, radius); if (unlikely (status)) _cairo_set_error (cr, status); } */ /** * cairo_rel_move_to: * @cr: a cairo context * @dx: the X offset * @dy: the Y offset * * Begin a new sub-path. After this call the current point will offset * by (@x, @y). * * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy) * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @dy). * * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. * * Since: 1.0 **/ void cairo_rel_move_to (cairo_t *cr, double dx, double dy) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rel_move_to (cr, dx, dy); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_rel_line_to: * @cr: a cairo context * @dx: the X offset to the end of the new line * @dy: the Y offset to the end of the new line * * Relative-coordinate version of cairo_line_to(). Adds a line to the * path from the current point to a point that is offset from the * current point by (@dx, @dy) in user space. After this call the * current point will be offset by (@dx, @dy). * * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy) * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @dy). * * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. * * Since: 1.0 **/ void cairo_rel_line_to (cairo_t *cr, double dx, double dy) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rel_line_to (cr, dx, dy); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_rel_line_to); /** * cairo_rel_curve_to: * @cr: a cairo context * @dx1: the X offset to the first control point * @dy1: the Y offset to the first control point * @dx2: the X offset to the second control point * @dy2: the Y offset to the second control point * @dx3: the X offset to the end of the curve * @dy3: the Y offset to the end of the curve * * Relative-coordinate version of cairo_curve_to(). All offsets are * relative to the current point. Adds a cubic Bézier spline to the * path from the current point to a point offset from the current * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and * (@dx2, @dy2) as the control points. After this call the current * point will be offset by (@dx3, @dy3). * * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1, * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3). * * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. * * Since: 1.0 **/ void cairo_rel_curve_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rel_curve_to (cr, dx1, dy1, dx2, dy2, dx3, dy3); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_rectangle: * @cr: a cairo context * @x: the X coordinate of the top left corner of the rectangle * @y: the Y coordinate to the top left corner of the rectangle * @width: the width of the rectangle * @height: the height of the rectangle * * Adds a closed sub-path rectangle of the given size to the current * path at position (@x, @y) in user-space coordinates. * * This function is logically equivalent to: * * cairo_move_to (cr, x, y); * cairo_rel_line_to (cr, width, 0); * cairo_rel_line_to (cr, 0, height); * cairo_rel_line_to (cr, -width, 0); * cairo_close_path (cr); * * * Since: 1.0 **/ void cairo_rectangle (cairo_t *cr, double x, double y, double width, double height) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->rectangle (cr, x, y, width, height); if (unlikely (status)) _cairo_set_error (cr, status); } #if 0 /* XXX: NYI */ void cairo_stroke_to_path (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */ status = _cairo_gstate_stroke_path (cr->gstate); if (unlikely (status)) _cairo_set_error (cr, status); } #endif /** * cairo_close_path: * @cr: a cairo context * * Adds a line segment to the path from the current point to the * beginning of the current sub-path, (the most recent point passed to * cairo_move_to()), and closes this sub-path. After this call the * current point will be at the joined endpoint of the sub-path. * * The behavior of cairo_close_path() is distinct from simply calling * cairo_line_to() with the equivalent coordinate in the case of * stroking. When a closed sub-path is stroked, there are no caps on * the ends of the sub-path. Instead, there is a line join connecting * the final and initial segments of the sub-path. * * If there is no current point before the call to cairo_close_path(), * this function will have no effect. * * Note: As of cairo version 1.2.4 any call to cairo_close_path() will * place an explicit MOVE_TO element into the path immediately after * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for * example). This can simplify path processing in some cases as it may * not be necessary to save the "last move_to point" during processing * as the MOVE_TO immediately after the CLOSE_PATH will provide that * point. * * Since: 1.0 **/ void cairo_close_path (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->close_path (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_close_path); /** * cairo_path_extents: * @cr: a cairo context * @x1: left of the resulting extents * @y1: top of the resulting extents * @x2: right of the resulting extents * @y2: bottom of the resulting extents * * Computes a bounding box in user-space coordinates covering the * points on the current path. If the current path is empty, returns * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule, * surface dimensions and clipping are not taken into account. * * Contrast with cairo_fill_extents() and cairo_stroke_extents() which * return the extents of only the area that would be "inked" by * the corresponding drawing operations. * * The result of cairo_path_extents() is defined as equivalent to the * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the * line width approaches 0.0, (but never reaching the empty-rectangle * returned by cairo_stroke_extents() for a line width of 0.0). * * Specifically, this means that zero-area sub-paths such as * cairo_move_to();cairo_line_to() segments, (even degenerate cases * where the coordinates to both calls are identical), will be * considered as contributing to the extents. However, a lone * cairo_move_to() will not contribute to the results of * cairo_path_extents(). * * Since: 1.6 **/ void cairo_path_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { if (unlikely (cr->status)) { if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; return; } cr->backend->path_extents (cr, x1, y1, x2, y2); } /** * cairo_paint: * @cr: a cairo context * * A drawing operator that paints the current source everywhere within * the current clip region. * * Since: 1.0 **/ void cairo_paint (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->paint (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_paint); /** * cairo_paint_with_alpha: * @cr: a cairo context * @alpha: alpha value, between 0 (transparent) and 1 (opaque) * * A drawing operator that paints the current source everywhere within * the current clip region using a mask of constant alpha value * @alpha. The effect is similar to cairo_paint(), but the drawing * is faded out using the alpha value. * * Since: 1.0 **/ void cairo_paint_with_alpha (cairo_t *cr, double alpha) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->paint_with_alpha (cr, alpha); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_mask: * @cr: a cairo context * @pattern: a #cairo_pattern_t * * A drawing operator that paints the current source * using the alpha channel of @pattern as a mask. (Opaque * areas of @pattern are painted with the source, transparent * areas are not painted.) * * Since: 1.0 **/ void cairo_mask (cairo_t *cr, cairo_pattern_t *pattern) { cairo_status_t status; if (unlikely (cr->status)) return; if (unlikely (pattern == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } if (unlikely (pattern->status)) { _cairo_set_error (cr, pattern->status); return; } status = cr->backend->mask (cr, pattern); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_mask); /** * cairo_mask_surface: * @cr: a cairo context * @surface: a #cairo_surface_t * @surface_x: X coordinate at which to place the origin of @surface * @surface_y: Y coordinate at which to place the origin of @surface * * A drawing operator that paints the current source * using the alpha channel of @surface as a mask. (Opaque * areas of @surface are painted with the source, transparent * areas are not painted.) * * Since: 1.0 **/ void cairo_mask_surface (cairo_t *cr, cairo_surface_t *surface, double surface_x, double surface_y) { cairo_pattern_t *pattern; cairo_matrix_t matrix; if (unlikely (cr->status)) return; pattern = cairo_pattern_create_for_surface (surface); cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); cairo_pattern_set_matrix (pattern, &matrix); cairo_mask (cr, pattern); cairo_pattern_destroy (pattern); } /** * cairo_stroke: * @cr: a cairo context * * A drawing operator that strokes the current path according to the * current line width, line join, line cap, and dash settings. After * cairo_stroke(), the current path will be cleared from the cairo * context. See cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). * * Note: Degenerate segments and sub-paths are treated specially and * provide a useful result. These can result in two different * situations: * * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these * segments will be drawn as circular dots or squares respectively. In * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares * is determined by the direction of the underlying path. * * 2. A sub-path created by cairo_move_to() followed by either a * cairo_close_path() or one or more calls to cairo_line_to() to the * same coordinate as the cairo_move_to(). If the cap style is * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate * sub-path will not be drawn at all, (since the correct orientation * is indeterminate). * * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything * to be drawn in the case of either degenerate segments or sub-paths. * * Since: 1.0 **/ void cairo_stroke (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->stroke (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_stroke); /** * cairo_stroke_preserve: * @cr: a cairo context * * A drawing operator that strokes the current path according to the * current line width, line join, line cap, and dash settings. Unlike * cairo_stroke(), cairo_stroke_preserve() preserves the path within the * cairo context. * * See cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). * * Since: 1.0 **/ void cairo_stroke_preserve (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->stroke_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_stroke_preserve); /** * cairo_fill: * @cr: a cairo context * * A drawing operator that fills the current path according to the * current fill rule, (each sub-path is implicitly closed before being * filled). After cairo_fill(), the current path will be cleared from * the cairo context. See cairo_set_fill_rule() and * cairo_fill_preserve(). * * Since: 1.0 **/ void cairo_fill (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->fill (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_fill_preserve: * @cr: a cairo context * * A drawing operator that fills the current path according to the * current fill rule, (each sub-path is implicitly closed before being * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the * path within the cairo context. * * See cairo_set_fill_rule() and cairo_fill(). * * Since: 1.0 **/ void cairo_fill_preserve (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->fill_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_fill_preserve); /** * cairo_copy_page: * @cr: a cairo context * * Emits the current page for backends that support multiple pages, but * doesn't clear it, so, the contents of the current page will be retained * for the next page too. Use cairo_show_page() if you want to get an * empty page after the emission. * * This is a convenience function that simply calls * cairo_surface_copy_page() on @cr's target. * * Since: 1.0 **/ void cairo_copy_page (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->copy_page (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_show_page: * @cr: a cairo context * * Emits and clears the current page for backends that support multiple * pages. Use cairo_copy_page() if you don't want to clear the page. * * This is a convenience function that simply calls * cairo_surface_show_page() on @cr's target. * * Since: 1.0 **/ void cairo_show_page (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->show_page (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_in_stroke: * @cr: a cairo context * @x: X coordinate of the point to test * @y: Y coordinate of the point to test * * Tests whether the given point is inside the area that would be * affected by a cairo_stroke() operation given the current path and * stroking parameters. Surface dimensions and clipping are not taken * into account. * * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). * * Return value: A non-zero value if the point is inside, or zero if * outside. * * Since: 1.0 **/ cairo_bool_t cairo_in_stroke (cairo_t *cr, double x, double y) { cairo_status_t status; cairo_bool_t inside = FALSE; if (unlikely (cr->status)) return FALSE; status = cr->backend->in_stroke (cr, x, y, &inside); if (unlikely (status)) _cairo_set_error (cr, status); return inside; } /** * cairo_in_fill: * @cr: a cairo context * @x: X coordinate of the point to test * @y: Y coordinate of the point to test * * Tests whether the given point is inside the area that would be * affected by a cairo_fill() operation given the current path and * filling parameters. Surface dimensions and clipping are not taken * into account. * * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). * * Return value: A non-zero value if the point is inside, or zero if * outside. * * Since: 1.0 **/ cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y) { cairo_status_t status; cairo_bool_t inside = FALSE; if (unlikely (cr->status)) return FALSE; status = cr->backend->in_fill (cr, x, y, &inside); if (unlikely (status)) _cairo_set_error (cr, status); return inside; } /** * cairo_stroke_extents: * @cr: a cairo context * @x1: left of the resulting extents * @y1: top of the resulting extents * @x2: right of the resulting extents * @y2: bottom of the resulting extents * * Computes a bounding box in user coordinates covering the area that * would be affected, (the "inked" area), by a cairo_stroke() * operation given the current path and stroke parameters. * If the current path is empty, returns an empty rectangle ((0,0), (0,0)). * Surface dimensions and clipping are not taken into account. * * Note that if the line width is set to exactly zero, then * cairo_stroke_extents() will return an empty rectangle. Contrast with * cairo_path_extents() which can be used to compute the non-empty * bounds as the line width approaches zero. * * Note that cairo_stroke_extents() must necessarily do more work to * compute the precise inked areas in light of the stroke parameters, * so cairo_path_extents() may be more desirable for sake of * performance if non-inked path extents are desired. * * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). * * Since: 1.0 **/ void cairo_stroke_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; if (unlikely (cr->status)) { if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; return; } status = cr->backend->stroke_extents (cr, x1, y1, x2, y2); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_fill_extents: * @cr: a cairo context * @x1: left of the resulting extents * @y1: top of the resulting extents * @x2: right of the resulting extents * @y2: bottom of the resulting extents * * Computes a bounding box in user coordinates covering the area that * would be affected, (the "inked" area), by a cairo_fill() operation * given the current path and fill parameters. If the current path is * empty, returns an empty rectangle ((0,0), (0,0)). Surface * dimensions and clipping are not taken into account. * * Contrast with cairo_path_extents(), which is similar, but returns * non-zero extents for some paths with no inked area, (such as a * simple line segment). * * Note that cairo_fill_extents() must necessarily do more work to * compute the precise inked areas in light of the fill rule, so * cairo_path_extents() may be more desirable for sake of performance * if the non-inked path extents are desired. * * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). * * Since: 1.0 **/ void cairo_fill_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; if (unlikely (cr->status)) { if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; return; } status = cr->backend->fill_extents (cr, x1, y1, x2, y2); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_clip: * @cr: a cairo context * * Establishes a new clip region by intersecting the current clip * region with the current path as it would be filled by cairo_fill() * and according to the current fill rule (see cairo_set_fill_rule()). * * After cairo_clip(), the current path will be cleared from the cairo * context. * * The current clip region affects all drawing operations by * effectively masking out any changes to the surface that are outside * the current clip region. * * Calling cairo_clip() can only make the clip region smaller, never * larger. But the current clip is part of the graphics state, so a * temporary restriction of the clip region can be achieved by * calling cairo_clip() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). * * Since: 1.0 **/ void cairo_clip (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->clip (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_clip_preserve: * @cr: a cairo context * * Establishes a new clip region by intersecting the current clip * region with the current path as it would be filled by cairo_fill() * and according to the current fill rule (see cairo_set_fill_rule()). * * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within * the cairo context. * * The current clip region affects all drawing operations by * effectively masking out any changes to the surface that are outside * the current clip region. * * Calling cairo_clip_preserve() can only make the clip region smaller, never * larger. But the current clip is part of the graphics state, so a * temporary restriction of the clip region can be achieved by * calling cairo_clip_preserve() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). * * Since: 1.0 **/ void cairo_clip_preserve (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->clip_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def(cairo_clip_preserve); /** * cairo_reset_clip: * @cr: a cairo context * * Reset the current clip region to its original, unrestricted * state. That is, set the clip region to an infinitely large shape * containing the target surface. Equivalently, if infinity is too * hard to grasp, one can imagine the clip region being reset to the * exact bounds of the target surface. * * Note that code meant to be reusable should not call * cairo_reset_clip() as it will cause results unexpected by * higher-level code which calls cairo_clip(). Consider using * cairo_save() and cairo_restore() around cairo_clip() as a more * robust means of temporarily restricting the clip region. * * Since: 1.0 **/ void cairo_reset_clip (cairo_t *cr) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->reset_clip (cr); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_clip_extents: * @cr: a cairo context * @x1: left of the resulting extents * @y1: top of the resulting extents * @x2: right of the resulting extents * @y2: bottom of the resulting extents * * Computes a bounding box in user coordinates covering the area inside the * current clip. * * Since: 1.4 **/ void cairo_clip_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; if (x1) *x1 = 0.0; if (y1) *y1 = 0.0; if (x2) *x2 = 0.0; if (y2) *y2 = 0.0; if (unlikely (cr->status)) return; status = cr->backend->clip_extents (cr, x1, y1, x2, y2); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_in_clip: * @cr: a cairo context * @x: X coordinate of the point to test * @y: Y coordinate of the point to test * * Tests whether the given point is inside the area that would be * visible through the current clip, i.e. the area that would be filled by * a cairo_paint() operation. * * See cairo_clip(), and cairo_clip_preserve(). * * Return value: A non-zero value if the point is inside, or zero if * outside. * * Since: 1.10 **/ cairo_bool_t cairo_in_clip (cairo_t *cr, double x, double y) { cairo_status_t status; cairo_bool_t inside = FALSE; if (unlikely (cr->status)) return FALSE; status = cr->backend->in_clip (cr, x, y, &inside); if (unlikely (status)) _cairo_set_error (cr, status); return inside; } /** * cairo_copy_clip_rectangle_list: * @cr: a cairo context * * Gets the current clip region as a list of rectangles in user coordinates. * Never returns %NULL. * * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to * indicate that the clip region cannot be represented as a list of * user-space rectangles. The status may have other values to indicate * other errors. * * Returns: the current clip region as a list of rectangles in user coordinates, * which should be destroyed using cairo_rectangle_list_destroy(). * * Since: 1.4 **/ cairo_rectangle_list_t * cairo_copy_clip_rectangle_list (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_rectangle_list_create_in_error (cr->status); return cr->backend->clip_copy_rectangle_list (cr); } /** * cairo_select_font_face: * @cr: a #cairo_t * @family: a font family name, encoded in UTF-8 * @slant: the slant for the font * @weight: the weight for the font * * Note: The cairo_select_font_face() function call is part of what * the cairo designers call the "toy" text API. It is convenient for * short demos and simple programs, but it is not expected to be * adequate for serious text-using applications. * * Selects a family and style of font from a simplified description as * a family name, slant and weight. Cairo provides no operation to * list available family names on the system (this is a "toy", * remember), but the standard CSS2 generic family names, ("serif", * "sans-serif", "cursive", "fantasy", "monospace"), are likely to * work as expected. * * If @family starts with the string "@cairo:", or if no native font * backends are compiled in, cairo will use an internal font family. * The internal font family recognizes many modifiers in the @family * string, most notably, it recognizes the string "monospace". That is, * the family name "@cairo:monospace" will use the monospace version of * the internal font family. * * For "real" font selection, see the font-backend-specific * font_face_create functions for the font backend you are using. (For * example, if you are using the freetype-based cairo-ft font backend, * see cairo_ft_font_face_create_for_ft_face() or * cairo_ft_font_face_create_for_pattern().) The resulting font face * could then be used with cairo_scaled_font_create() and * cairo_set_scaled_font(). * * Similarly, when using the "real" font support, you can call * directly into the underlying font system, (such as fontconfig or * freetype), for operations such as listing available fonts, etc. * * It is expected that most applications will need to use a more * comprehensive font handling and text layout library, (for example, * pango), in conjunction with cairo. * * If text is drawn without a call to cairo_select_font_face(), (nor * cairo_set_font_face() nor cairo_set_scaled_font()), the default * family is platform-specific, but is essentially "sans-serif". * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is * %CAIRO_FONT_WEIGHT_NORMAL. * * This function is equivalent to a call to cairo_toy_font_face_create() * followed by cairo_set_font_face(). * * Since: 1.0 **/ void cairo_select_font_face (cairo_t *cr, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { cairo_font_face_t *font_face; cairo_status_t status; if (unlikely (cr->status)) return; font_face = cairo_toy_font_face_create (family, slant, weight); if (unlikely (font_face->status)) { _cairo_set_error (cr, font_face->status); return; } status = cr->backend->set_font_face (cr, font_face); cairo_font_face_destroy (font_face); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_font_extents: * @cr: a #cairo_t * @extents: a #cairo_font_extents_t object into which the results * will be stored. * * Gets the font extents for the currently selected font. * * Since: 1.0 **/ void cairo_font_extents (cairo_t *cr, cairo_font_extents_t *extents) { cairo_status_t status; extents->ascent = 0.0; extents->descent = 0.0; extents->height = 0.0; extents->max_x_advance = 0.0; extents->max_y_advance = 0.0; if (unlikely (cr->status)) return; status = cr->backend->font_extents (cr, extents); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_set_font_face: * @cr: a #cairo_t * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font * * Replaces the current #cairo_font_face_t object in the #cairo_t with * @font_face. The replaced font face in the #cairo_t will be * destroyed if there are no other references to it. * * Since: 1.0 **/ void cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_font_face (cr, font_face); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_get_font_face: * @cr: a #cairo_t * * Gets the current font face for a #cairo_t. * * Return value: the current font face. This object is owned by * cairo. To keep a reference to it, you must call * cairo_font_face_reference(). * * This function never returns %NULL. If memory cannot be allocated, a * special "nil" #cairo_font_face_t object will be returned on which * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using * this nil object will cause its error state to propagate to other * objects it is passed to, (for example, calling * cairo_set_font_face() with a nil font will trigger an error that * will shutdown the #cairo_t object). * * Since: 1.0 **/ cairo_font_face_t * cairo_get_font_face (cairo_t *cr) { if (unlikely (cr->status)) return (cairo_font_face_t*) &_cairo_font_face_nil; return cr->backend->get_font_face (cr); } /** * cairo_set_font_size: * @cr: a #cairo_t * @size: the new font size, in user space units * * Sets the current font matrix to a scale by a factor of @size, replacing * any font matrix previously set with cairo_set_font_size() or * cairo_set_font_matrix(). This results in a font size of @size user space * units. (More precisely, this matrix will result in the font's * em-square being a @size by @size square in user space.) * * If text is drawn without a call to cairo_set_font_size(), (nor * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default * font size is 10.0. * * Since: 1.0 **/ void cairo_set_font_size (cairo_t *cr, double size) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_font_size (cr, size); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_size); /** * cairo_set_font_matrix: * @cr: a #cairo_t * @matrix: a #cairo_matrix_t describing a transform to be applied to * the current font. * * Sets the current font matrix to @matrix. The font matrix gives a * transformation from the design space of the font (in this space, * the em-square is 1 unit by 1 unit) to user space. Normally, a * simple scale is used (see cairo_set_font_size()), but a more * complex font matrix can be used to shear the font * or stretch it unequally along the two axes * * Since: 1.0 **/ void cairo_set_font_matrix (cairo_t *cr, const cairo_matrix_t *matrix) { cairo_status_t status; if (unlikely (cr->status)) return; status = cr->backend->set_font_matrix (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_matrix); /** * cairo_get_font_matrix: * @cr: a #cairo_t * @matrix: return value for the matrix * * Stores the current font matrix into @matrix. See * cairo_set_font_matrix(). * * Since: 1.0 **/ void cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) { if (unlikely (cr->status)) { cairo_matrix_init_identity (matrix); return; } cr->backend->get_font_matrix (cr, matrix); } /** * cairo_set_font_options: * @cr: a #cairo_t * @options: font options to use * * Sets a set of custom font rendering options for the #cairo_t. * Rendering options are derived by merging these options with the * options derived from underlying surface; if the value in @options * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value * from the surface is used. * * Since: 1.0 **/ void cairo_set_font_options (cairo_t *cr, const cairo_font_options_t *options) { cairo_status_t status; if (unlikely (cr->status)) return; status = cairo_font_options_status ((cairo_font_options_t *) options); if (unlikely (status)) { _cairo_set_error (cr, status); return; } status = cr->backend->set_font_options (cr, options); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_options); /** * cairo_get_font_options: * @cr: a #cairo_t * @options: a #cairo_font_options_t object into which to store * the retrieved options. All existing values are overwritten * * Retrieves font rendering options set via #cairo_set_font_options. * Note that the returned options do not include any options derived * from the underlying surface; they are literally the options * passed to cairo_set_font_options(). * * Since: 1.0 **/ void cairo_get_font_options (cairo_t *cr, cairo_font_options_t *options) { /* check that we aren't trying to overwrite the nil object */ if (cairo_font_options_status (options)) return; if (unlikely (cr->status)) { _cairo_font_options_init_default (options); return; } cr->backend->get_font_options (cr, options); } /** * cairo_set_scaled_font: * @cr: a #cairo_t * @scaled_font: a #cairo_scaled_font_t * * Replaces the current font face, font matrix, and font options in * the #cairo_t with those of the #cairo_scaled_font_t. Except for * some translation, the current CTM of the #cairo_t should be the * same as that of the #cairo_scaled_font_t, which can be accessed * using cairo_scaled_font_get_ctm(). * * Since: 1.2 **/ void cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font) { cairo_status_t status; if (unlikely (cr->status)) return; if ((scaled_font == NULL)) { _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER)); return; } status = scaled_font->status; if (unlikely (status)) { _cairo_set_error (cr, status); return; } status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_get_scaled_font: * @cr: a #cairo_t * * Gets the current scaled font for a #cairo_t. * * Return value: the current scaled font. This object is owned by * cairo. To keep a reference to it, you must call * cairo_scaled_font_reference(). * * This function never returns %NULL. If memory cannot be allocated, a * special "nil" #cairo_scaled_font_t object will be returned on which * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using * this nil object will cause its error state to propagate to other * objects it is passed to, (for example, calling * cairo_set_scaled_font() with a nil font will trigger an error that * will shutdown the #cairo_t object). * * Since: 1.4 **/ cairo_scaled_font_t * cairo_get_scaled_font (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_scaled_font_create_in_error (cr->status); return cr->backend->get_scaled_font (cr); } slim_hidden_def (cairo_get_scaled_font); /** * cairo_text_extents: * @cr: a #cairo_t * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL * @extents: a #cairo_text_extents_t object into which the results * will be stored * * Gets the extents for a string of text. The extents describe a * user-space rectangle that encloses the "inked" portion of the text, * (as it would be drawn by cairo_show_text()). Additionally, the * x_advance and y_advance values indicate the amount by which the * current point would be advanced by cairo_show_text(). * * Note that whitespace characters do not directly contribute to the * size of the rectangle (extents.width and extents.height). They do * contribute indirectly by changing the position of non-whitespace * characters. In particular, trailing whitespace characters are * likely to not affect the size of the rectangle, though they will * affect the x_advance and y_advance values. * * Since: 1.0 **/ void cairo_text_extents (cairo_t *cr, const char *utf8, cairo_text_extents_t *extents) { cairo_status_t status; cairo_scaled_font_t *scaled_font; cairo_glyph_t *glyphs = NULL; int num_glyphs = 0; double x, y; extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; extents->x_advance = 0.0; extents->y_advance = 0.0; if (unlikely (cr->status)) return; if (utf8 == NULL) return; scaled_font = cairo_get_scaled_font (cr); if (unlikely (scaled_font->status)) { _cairo_set_error (cr, scaled_font->status); return; } cairo_get_current_point (cr, &x, &y); status = cairo_scaled_font_text_to_glyphs (scaled_font, x, y, utf8, -1, &glyphs, &num_glyphs, NULL, NULL, NULL); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents); } cairo_glyph_free (glyphs); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_glyph_extents: * @cr: a #cairo_t * @glyphs: an array of #cairo_glyph_t objects * @num_glyphs: the number of elements in @glyphs * @extents: a #cairo_text_extents_t object into which the results * will be stored * * Gets the extents for an array of glyphs. The extents describe a * user-space rectangle that encloses the "inked" portion of the * glyphs, (as they would be drawn by cairo_show_glyphs()). * Additionally, the x_advance and y_advance values indicate the * amount by which the current point would be advanced by * cairo_show_glyphs(). * * Note that whitespace glyphs do not contribute to the size of the * rectangle (extents.width and extents.height). * * Since: 1.0 **/ void cairo_glyph_extents (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) { cairo_status_t status; extents->x_bearing = 0.0; extents->y_bearing = 0.0; extents->width = 0.0; extents->height = 0.0; extents->x_advance = 0.0; extents->y_advance = 0.0; if (unlikely (cr->status)) return; if (num_glyphs == 0) return; if (unlikely (num_glyphs < 0)) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } if (unlikely (glyphs == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_show_text: * @cr: a cairo context * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL * * A drawing operator that generates the shape from a string of UTF-8 * characters, rendered according to the current font_face, font_size * (font_matrix), and font_options. * * This function first computes a set of glyphs for the string of * text. The first glyph is placed so that its origin is at the * current point. The origin of each subsequent glyph is offset from * that of the previous glyph by the advance values of the previous * glyph. * * After this call the current point is moved to the origin of where * the next glyph would be placed in this same progression. That is, * the current point will be at the origin of the final glyph offset * by its advance values. This allows for easy display of a single * logical string with multiple calls to cairo_show_text(). * * Note: The cairo_show_text() function call is part of what the cairo * designers call the "toy" text API. It is convenient for short demos * and simple programs, but it is not expected to be adequate for * serious text-using applications. See cairo_show_glyphs() for the * "real" text display API in cairo. * * Since: 1.0 **/ void cairo_show_text (cairo_t *cr, const char *utf8) { cairo_text_extents_t extents; cairo_status_t status; cairo_glyph_t *glyphs, *last_glyph; cairo_text_cluster_t *clusters; int utf8_len, num_glyphs, num_clusters; cairo_text_cluster_flags_t cluster_flags; double x, y; cairo_bool_t has_show_text_glyphs; cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; cairo_scaled_font_t *scaled_font; cairo_glyph_text_info_t info, *i; if (unlikely (cr->status)) return; if (utf8 == NULL) return; scaled_font = cairo_get_scaled_font (cr); if (unlikely (scaled_font->status)) { _cairo_set_error (cr, scaled_font->status); return; } utf8_len = strlen (utf8); has_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (cr)); glyphs = stack_glyphs; num_glyphs = ARRAY_LENGTH (stack_glyphs); if (has_show_text_glyphs) { clusters = stack_clusters; num_clusters = ARRAY_LENGTH (stack_clusters); } else { clusters = NULL; num_clusters = 0; } cairo_get_current_point (cr, &x, &y); status = cairo_scaled_font_text_to_glyphs (scaled_font, x, y, utf8, utf8_len, &glyphs, &num_glyphs, has_show_text_glyphs ? &clusters : NULL, &num_clusters, &cluster_flags); if (unlikely (status)) goto BAIL; if (num_glyphs == 0) return; i = NULL; if (has_show_text_glyphs) { info.utf8 = utf8; info.utf8_len = utf8_len; info.clusters = clusters; info.num_clusters = num_clusters; info.cluster_flags = cluster_flags; i = &info; } status = cr->backend->glyphs (cr, glyphs, num_glyphs, i); if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents); if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; y = last_glyph->y + extents.y_advance; cr->backend->move_to (cr, x, y); BAIL: if (glyphs != stack_glyphs) cairo_glyph_free (glyphs); if (clusters != stack_clusters) cairo_text_cluster_free (clusters); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_show_glyphs: * @cr: a cairo context * @glyphs: array of glyphs to show * @num_glyphs: number of glyphs to show * * A drawing operator that generates the shape from an array of glyphs, * rendered according to the current font face, font size * (font matrix), and font options. * * Since: 1.0 **/ void cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) { cairo_status_t status; if (unlikely (cr->status)) return; if (num_glyphs == 0) return; if (num_glyphs < 0) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } if (glyphs == NULL) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_show_text_glyphs: * @cr: a cairo context * @utf8: a string of text encoded in UTF-8 * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated * @glyphs: array of glyphs to show * @num_glyphs: number of glyphs to show * @clusters: array of cluster mapping information * @num_clusters: number of clusters in the mapping * @cluster_flags: cluster mapping flags * * This operation has rendering effects similar to cairo_show_glyphs() * but, if the target surface supports it, uses the provided text and * cluster mapping to embed the text for the glyphs shown in the output. * If the target does not support the extended attributes, this function * acts like the basic cairo_show_glyphs() as if it had been passed * @glyphs and @num_glyphs. * * The mapping between @utf8 and @glyphs is provided by an array of * clusters. Each cluster covers a number of * text bytes and glyphs, and neighboring clusters cover neighboring * areas of @utf8 and @glyphs. The clusters should collectively cover @utf8 * and @glyphs in entirety. * * The first cluster always covers bytes from the beginning of @utf8. * If @cluster_flags do not have the %CAIRO_TEXT_CLUSTER_FLAG_BACKWARD * set, the first cluster also covers the beginning * of @glyphs, otherwise it covers the end of the @glyphs array and * following clusters move backward. * * See #cairo_text_cluster_t for constraints on valid clusters. * * Since: 1.8 **/ void cairo_show_text_glyphs (cairo_t *cr, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags) { cairo_status_t status; if (unlikely (cr->status)) return; /* A slew of sanity checks */ /* Special case for NULL and -1 */ if (utf8 == NULL && utf8_len == -1) utf8_len = 0; /* No NULLs for non-zeros */ if ((num_glyphs && glyphs == NULL) || (utf8_len && utf8 == NULL) || (num_clusters && clusters == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } /* A -1 for utf8_len means NUL-terminated */ if (utf8_len == -1) utf8_len = strlen (utf8); /* Apart from that, no negatives */ if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } if (num_glyphs == 0 && utf8_len == 0) return; if (utf8) { /* Make sure clusters cover the entire glyphs and utf8 arrays, * and that cluster boundaries are UTF-8 boundaries. */ status = _cairo_validate_text_clusters (utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); if (status == CAIRO_STATUS_INVALID_CLUSTERS) { /* Either got invalid UTF-8 text, or cluster mapping is bad. * Differentiate those. */ cairo_status_t status2; status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL); if (status2) status = status2; } else { cairo_glyph_text_info_t info; info.utf8 = utf8; info.utf8_len = utf8_len; info.clusters = clusters; info.num_clusters = num_clusters; info.cluster_flags = cluster_flags; status = cr->backend->glyphs (cr, glyphs, num_glyphs, &info); } } else { status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL); } if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_text_path: * @cr: a cairo context * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL * * Adds closed paths for text to the current path. The generated * path if filled, achieves an effect similar to that of * cairo_show_text(). * * Text conversion and positioning is done similar to cairo_show_text(). * * Like cairo_show_text(), After this call the current point is * moved to the origin of where the next glyph would be placed in * this same progression. That is, the current point will be at * the origin of the final glyph offset by its advance values. * This allows for chaining multiple calls to to cairo_text_path() * without having to set current point in between. * * Note: The cairo_text_path() function call is part of what the cairo * designers call the "toy" text API. It is convenient for short demos * and simple programs, but it is not expected to be adequate for * serious text-using applications. See cairo_glyph_path() for the * "real" text path API in cairo. * * Since: 1.0 **/ void cairo_text_path (cairo_t *cr, const char *utf8) { cairo_status_t status; cairo_text_extents_t extents; cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_glyph_t *glyphs, *last_glyph; cairo_scaled_font_t *scaled_font; int num_glyphs; double x, y; if (unlikely (cr->status)) return; if (utf8 == NULL) return; glyphs = stack_glyphs; num_glyphs = ARRAY_LENGTH (stack_glyphs); scaled_font = cairo_get_scaled_font (cr); if (unlikely (scaled_font->status)) { _cairo_set_error (cr, scaled_font->status); return; } cairo_get_current_point (cr, &x, &y); status = cairo_scaled_font_text_to_glyphs (scaled_font, x, y, utf8, -1, &glyphs, &num_glyphs, NULL, NULL, NULL); if (num_glyphs == 0) return; status = cr->backend->glyph_path (cr, glyphs, num_glyphs); if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents); if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; y = last_glyph->y + extents.y_advance; cr->backend->move_to (cr, x, y); BAIL: if (glyphs != stack_glyphs) cairo_glyph_free (glyphs); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_glyph_path: * @cr: a cairo context * @glyphs: array of glyphs to show * @num_glyphs: number of glyphs to show * * Adds closed paths for the glyphs to the current path. The generated * path if filled, achieves an effect similar to that of * cairo_show_glyphs(). * * Since: 1.0 **/ void cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) { cairo_status_t status; if (unlikely (cr->status)) return; if (num_glyphs == 0) return; if (unlikely (num_glyphs < 0)) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } if (unlikely (glyphs == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } status = cr->backend->glyph_path (cr, glyphs, num_glyphs); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_get_operator: * @cr: a cairo context * * Gets the current compositing operator for a cairo context. * * Return value: the current compositing operator. * * Since: 1.0 **/ cairo_operator_t cairo_get_operator (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_OPERATOR_DEFAULT; return cr->backend->get_operator (cr); } #if 0 /** * cairo_get_opacity: * @cr: a cairo context * * Gets the current compositing opacity for a cairo context. * * Return value: the current compositing opacity. * * Since: TBD **/ double cairo_get_opacity (cairo_t *cr) { if (unlikely (cr->status)) return 1.; return cr->backend->get_opacity (cr); } #endif /** * cairo_get_tolerance: * @cr: a cairo context * * Gets the current tolerance value, as set by cairo_set_tolerance(). * * Return value: the current tolerance value. * * Since: 1.0 **/ double cairo_get_tolerance (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_TOLERANCE_DEFAULT; return cr->backend->get_tolerance (cr); } slim_hidden_def (cairo_get_tolerance); /** * cairo_get_antialias: * @cr: a cairo context * * Gets the current shape antialiasing mode, as set by * cairo_set_antialias(). * * Return value: the current shape antialiasing mode. * * Since: 1.0 **/ cairo_antialias_t cairo_get_antialias (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_ANTIALIAS_DEFAULT; return cr->backend->get_antialias (cr); } /** * cairo_has_current_point: * @cr: a cairo context * * Returns whether a current point is defined on the current path. * See cairo_get_current_point() for details on the current point. * * Return value: whether a current point is defined. * * Since: 1.6 **/ cairo_bool_t cairo_has_current_point (cairo_t *cr) { if (unlikely (cr->status)) return FALSE; return cr->backend->has_current_point (cr); } /** * cairo_get_current_point: * @cr: a cairo context * @x: return value for X coordinate of the current point * @y: return value for Y coordinate of the current point * * Gets the current point of the current path, which is * conceptually the final point reached by the path so far. * * The current point is returned in the user-space coordinate * system. If there is no defined current point or if @cr is in an * error status, @x and @y will both be set to 0.0. It is possible to * check this in advance with cairo_has_current_point(). * * Most path construction functions alter the current point. See the * following for details on how they affect the current point: * cairo_new_path(), cairo_new_sub_path(), * cairo_append_path(), cairo_close_path(), * cairo_move_to(), cairo_line_to(), cairo_curve_to(), * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(), * cairo_arc(), cairo_arc_negative(), cairo_rectangle(), * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path(). * * Some functions use and alter the current point but do not * otherwise change current path: * cairo_show_text(). * * Some functions unset the current path and as a result, current point: * cairo_fill(), cairo_stroke(). * * Since: 1.0 **/ void cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret) { double x, y; x = y = 0; if (cr->status == CAIRO_STATUS_SUCCESS && cr->backend->has_current_point (cr)) { cr->backend->get_current_point (cr, &x, &y); } if (x_ret) *x_ret = x; if (y_ret) *y_ret = y; } slim_hidden_def(cairo_get_current_point); /** * cairo_get_fill_rule: * @cr: a cairo context * * Gets the current fill rule, as set by cairo_set_fill_rule(). * * Return value: the current fill rule. * * Since: 1.0 **/ cairo_fill_rule_t cairo_get_fill_rule (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_FILL_RULE_DEFAULT; return cr->backend->get_fill_rule (cr); } /** * cairo_get_line_width: * @cr: a cairo context * * This function returns the current line width value exactly as set by * cairo_set_line_width(). Note that the value is unchanged even if * the CTM has changed between the calls to cairo_set_line_width() and * cairo_get_line_width(). * * Return value: the current line width. * * Since: 1.0 **/ double cairo_get_line_width (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_WIDTH_DEFAULT; return cr->backend->get_line_width (cr); } slim_hidden_def (cairo_get_line_width); /** * cairo_get_line_cap: * @cr: a cairo context * * Gets the current line cap style, as set by cairo_set_line_cap(). * * Return value: the current line cap style. * * Since: 1.0 **/ cairo_line_cap_t cairo_get_line_cap (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_CAP_DEFAULT; return cr->backend->get_line_cap (cr); } /** * cairo_get_line_join: * @cr: a cairo context * * Gets the current line join style, as set by cairo_set_line_join(). * * Return value: the current line join style. * * Since: 1.0 **/ cairo_line_join_t cairo_get_line_join (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_JOIN_DEFAULT; return cr->backend->get_line_join (cr); } /** * cairo_get_miter_limit: * @cr: a cairo context * * Gets the current miter limit, as set by cairo_set_miter_limit(). * * Return value: the current miter limit. * * Since: 1.0 **/ double cairo_get_miter_limit (cairo_t *cr) { if (unlikely (cr->status)) return CAIRO_GSTATE_MITER_LIMIT_DEFAULT; return cr->backend->get_miter_limit (cr); } /** * cairo_get_matrix: * @cr: a cairo context * @matrix: return value for the matrix * * Stores the current transformation matrix (CTM) into @matrix. * * Since: 1.0 **/ void cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) { if (unlikely (cr->status)) { cairo_matrix_init_identity (matrix); return; } cr->backend->get_matrix (cr, matrix); } slim_hidden_def (cairo_get_matrix); /** * cairo_get_target: * @cr: a cairo context * * Gets the target surface for the cairo context as passed to * cairo_create(). * * This function will always return a valid pointer, but the result * can be a "nil" surface if @cr is already in an error state, * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). * A nil surface is indicated by cairo_surface_status() * != %CAIRO_STATUS_SUCCESS. * * Return value: the target surface. This object is owned by cairo. To * keep a reference to it, you must call cairo_surface_reference(). * * Since: 1.0 **/ cairo_surface_t * cairo_get_target (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_surface_create_in_error (cr->status); return cr->backend->get_original_target (cr); } slim_hidden_def (cairo_get_target); /** * cairo_get_group_target: * @cr: a cairo context * * Gets the current destination surface for the context. This is either * the original target surface as passed to cairo_create() or the target * surface for the current group as started by the most recent call to * cairo_push_group() or cairo_push_group_with_content(). * * This function will always return a valid pointer, but the result * can be a "nil" surface if @cr is already in an error state, * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). * A nil surface is indicated by cairo_surface_status() * != %CAIRO_STATUS_SUCCESS. * * Return value: the target surface. This object is owned by cairo. To * keep a reference to it, you must call cairo_surface_reference(). * * Since: 1.2 **/ cairo_surface_t * cairo_get_group_target (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_surface_create_in_error (cr->status); return cr->backend->get_current_target (cr); } /** * cairo_copy_path: * @cr: a cairo context * * Creates a copy of the current path and returns it to the user as a * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate * over the returned data structure. * * This function will always return a valid pointer, but the result * will have no data (data==%NULL and * num_data==0), if either of the following * conditions hold: * * * If there is insufficient memory to copy the path. In this * case path->status will be set to * %CAIRO_STATUS_NO_MEMORY. * If @cr is already in an error state. In this case * path->status will contain the same status that * would be returned by cairo_status(). * * * Return value: the copy of the current path. The caller owns the * returned object and should call cairo_path_destroy() when finished * with it. * * Since: 1.0 **/ cairo_path_t * cairo_copy_path (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_path_create_in_error (cr->status); return cr->backend->copy_path (cr); } /** * cairo_copy_path_flat: * @cr: a cairo context * * Gets a flattened copy of the current path and returns it to the * user as a #cairo_path_t. See #cairo_path_data_t for hints on * how to iterate over the returned data structure. * * This function is like cairo_copy_path() except that any curves * in the path will be approximated with piecewise-linear * approximations, (accurate to within the current tolerance * value). That is, the result is guaranteed to not have any elements * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a * series of %CAIRO_PATH_LINE_TO elements. * * This function will always return a valid pointer, but the result * will have no data (data==%NULL and * num_data==0), if either of the following * conditions hold: * * * If there is insufficient memory to copy the path. In this * case path->status will be set to * %CAIRO_STATUS_NO_MEMORY. * If @cr is already in an error state. In this case * path->status will contain the same status that * would be returned by cairo_status(). * * * Return value: the copy of the current path. The caller owns the * returned object and should call cairo_path_destroy() when finished * with it. * * Since: 1.0 **/ cairo_path_t * cairo_copy_path_flat (cairo_t *cr) { if (unlikely (cr->status)) return _cairo_path_create_in_error (cr->status); return cr->backend->copy_path_flat (cr); } /** * cairo_append_path: * @cr: a cairo context * @path: path to be appended * * Append the @path onto the current path. The @path may be either the * return value from one of cairo_copy_path() or * cairo_copy_path_flat() or it may be constructed manually. See * #cairo_path_t for details on how the path data structure should be * initialized, and note that path->status must be * initialized to %CAIRO_STATUS_SUCCESS. * * Since: 1.0 **/ void cairo_append_path (cairo_t *cr, const cairo_path_t *path) { cairo_status_t status; if (unlikely (cr->status)) return; if (unlikely (path == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } if (unlikely (path->status)) { if (path->status > CAIRO_STATUS_SUCCESS && path->status <= CAIRO_STATUS_LAST_STATUS) _cairo_set_error (cr, path->status); else _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS); return; } if (path->num_data == 0) return; if (unlikely (path->data == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } status = cr->backend->append_path (cr, path); if (unlikely (status)) _cairo_set_error (cr, status); } /** * cairo_status: * @cr: a cairo context * * Checks whether an error has previously occurred for this context. * * Returns: the current status of this context, see #cairo_status_t * * Since: 1.0 **/ cairo_status_t cairo_status (cairo_t *cr) { return cr->status; } slim_hidden_def (cairo_status); Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairo.h000066400000000000000000003235651271037650300235640ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_H #define CAIRO_H #include "cairo-version.h" #include "cairo-features.h" #include "cairo-deprecated.h" #ifdef __cplusplus # define CAIRO_BEGIN_DECLS extern "C" { # define CAIRO_END_DECLS } #else # define CAIRO_BEGIN_DECLS # define CAIRO_END_DECLS #endif #ifndef cairo_public # if defined (_MSC_VER) && ! defined (CAIRO_WIN32_STATIC_BUILD) # define cairo_public __declspec(dllimport) # else # define cairo_public # endif #endif CAIRO_BEGIN_DECLS #define CAIRO_VERSION_ENCODE(major, minor, micro) ( \ ((major) * 10000) \ + ((minor) * 100) \ + ((micro) * 1)) #define CAIRO_VERSION CAIRO_VERSION_ENCODE( \ CAIRO_VERSION_MAJOR, \ CAIRO_VERSION_MINOR, \ CAIRO_VERSION_MICRO) #define CAIRO_VERSION_STRINGIZE_(major, minor, micro) \ #major"."#minor"."#micro #define CAIRO_VERSION_STRINGIZE(major, minor, micro) \ CAIRO_VERSION_STRINGIZE_(major, minor, micro) #define CAIRO_VERSION_STRING CAIRO_VERSION_STRINGIZE( \ CAIRO_VERSION_MAJOR, \ CAIRO_VERSION_MINOR, \ CAIRO_VERSION_MICRO) cairo_public int cairo_version (void); cairo_public const char* cairo_version_string (void); /** * cairo_bool_t: * * #cairo_bool_t is used for boolean values. Returns of type * #cairo_bool_t will always be either 0 or 1, but testing against * these values explicitly is not encouraged; just use the * value as a boolean condition. * * * if (cairo_in_stroke (cr, x, y)) { * /* do something */ * } * * * Since: 1.0 **/ typedef int cairo_bool_t; /** * cairo_t: * * A #cairo_t contains the current state of the rendering device, * including coordinates of yet to be drawn shapes. * * Cairo contexts, as #cairo_t objects are named, are central to * cairo and all drawing with cairo is always done to a #cairo_t * object. * * Memory management of #cairo_t is done with * cairo_reference() and cairo_destroy(). * * Since: 1.0 **/ typedef struct _cairo cairo_t; /** * cairo_surface_t: * * A #cairo_surface_t represents an image, either as the destination * of a drawing operation or as source when drawing onto another * surface. To draw to a #cairo_surface_t, create a cairo context * with the surface as the target, using cairo_create(). * * There are different subtypes of #cairo_surface_t for * different drawing backends; for example, cairo_image_surface_create() * creates a bitmap image in memory. * The type of a surface can be queried with cairo_surface_get_type(). * * The initial contents of a surface after creation depend upon the manner * of its creation. If cairo creates the surface and backing storage for * the user, it will be initially cleared; for example, * cairo_image_surface_create() and cairo_surface_create_similar(). * Alternatively, if the user passes in a reference to some backing storage * and asks cairo to wrap that in a #cairo_surface_t, then the contents are * not modified; for example, cairo_image_surface_create_for_data() and * cairo_xlib_surface_create(). * * Memory management of #cairo_surface_t is done with * cairo_surface_reference() and cairo_surface_destroy(). * * Since: 1.0 **/ typedef struct _cairo_surface cairo_surface_t; /** * cairo_device_t: * * A #cairo_device_t represents the driver interface for drawing * operations to a #cairo_surface_t. There are different subtypes of * #cairo_device_t for different drawing backends; for example, * cairo_egl_device_create() creates a device that wraps an EGL display and * context. * * The type of a device can be queried with cairo_device_get_type(). * * Memory management of #cairo_device_t is done with * cairo_device_reference() and cairo_device_destroy(). * * Since: 1.10 **/ typedef struct _cairo_device cairo_device_t; /** * cairo_matrix_t: * @xx: xx component of the affine transformation * @yx: yx component of the affine transformation * @xy: xy component of the affine transformation * @yy: yy component of the affine transformation * @x0: X translation component of the affine transformation * @y0: Y translation component of the affine transformation * * A #cairo_matrix_t holds an affine transformation, such as a scale, * rotation, shear, or a combination of those. The transformation of * a point (x, y) is given by: * * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * * * Since: 1.0 **/ typedef struct _cairo_matrix { double xx; double yx; double xy; double yy; double x0; double y0; } cairo_matrix_t; /** * cairo_pattern_t: * * A #cairo_pattern_t represents a source when drawing onto a * surface. There are different subtypes of #cairo_pattern_t, * for different types of sources; for example, * cairo_pattern_create_rgb() creates a pattern for a solid * opaque color. * * Other than various * cairo_pattern_create_type() * functions, some of the pattern types can be implicitly created using various * cairo_set_source_type() functions; * for example cairo_set_source_rgb(). * * The type of a pattern can be queried with cairo_pattern_get_type(). * * Memory management of #cairo_pattern_t is done with * cairo_pattern_reference() and cairo_pattern_destroy(). * * Since: 1.0 **/ typedef struct _cairo_pattern cairo_pattern_t; /** * cairo_destroy_func_t: * @data: The data element being destroyed. * * #cairo_destroy_func_t the type of function which is called when a * data element is destroyed. It is passed the pointer to the data * element and should free any memory and resources allocated for it. * * Since: 1.0 **/ typedef void (*cairo_destroy_func_t) (void *data); /** * cairo_user_data_key_t: * @unused: not used; ignore. * * #cairo_user_data_key_t is used for attaching user data to cairo * data structures. The actual contents of the struct is never used, * and there is no need to initialize the object; only the unique * address of a #cairo_data_key_t object is used. Typically, you * would just use the address of a static #cairo_data_key_t object. * * Since: 1.0 **/ typedef struct _cairo_user_data_key { int unused; } cairo_user_data_key_t; /** * cairo_status_t: * @CAIRO_STATUS_SUCCESS: no error has occurred (Since 1.0) * @CAIRO_STATUS_NO_MEMORY: out of memory (Since 1.0) * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() (Since 1.0) * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() (Since 1.0) * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined (Since 1.0) * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) (Since 1.0) * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t (Since 1.0) * @CAIRO_STATUS_NULL_POINTER: %NULL pointer (Since 1.0) * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 (Since 1.0) * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid (Since 1.0) * @CAIRO_STATUS_READ_ERROR: error while reading from input stream (Since 1.0) * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream (Since 1.0) * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished (Since 1.0) * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation (Since 1.0) * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation (Since 1.0) * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t (Since 1.0) * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t (Since 1.0) * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* (Since 1.0) * @CAIRO_STATUS_FILE_NOT_FOUND: file not found (Since 1.0) * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting (Since 1.0) * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2) * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4) * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4) * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6) * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6) * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8) * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8) * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8) * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8) * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8) * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8) * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10) * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10) * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10) * @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern * construction operation was used outside of a * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch() * pair (Since 1.12) * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12) * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of * status values defined in this enumeration. When using this value, note * that the version of cairo at run-time may have additional status values * defined than the value of this symbol at compile-time. (Since 1.10) * * #cairo_status_t is used to indicate errors that can occur when * using Cairo. In some cases it is returned directly by functions. * but when using #cairo_t, the last error, if any, is stored in * the context and can be retrieved with cairo_status(). * * New entries may be added in future versions. Use cairo_status_to_string() * to get a human-readable representation of an error message. * * Since: 1.0 **/ typedef enum _cairo_status { CAIRO_STATUS_SUCCESS = 0, CAIRO_STATUS_NO_MEMORY, CAIRO_STATUS_INVALID_RESTORE, CAIRO_STATUS_INVALID_POP_GROUP, CAIRO_STATUS_NO_CURRENT_POINT, CAIRO_STATUS_INVALID_MATRIX, CAIRO_STATUS_INVALID_STATUS, CAIRO_STATUS_NULL_POINTER, CAIRO_STATUS_INVALID_STRING, CAIRO_STATUS_INVALID_PATH_DATA, CAIRO_STATUS_READ_ERROR, CAIRO_STATUS_WRITE_ERROR, CAIRO_STATUS_SURFACE_FINISHED, CAIRO_STATUS_SURFACE_TYPE_MISMATCH, CAIRO_STATUS_PATTERN_TYPE_MISMATCH, CAIRO_STATUS_INVALID_CONTENT, CAIRO_STATUS_INVALID_FORMAT, CAIRO_STATUS_INVALID_VISUAL, CAIRO_STATUS_FILE_NOT_FOUND, CAIRO_STATUS_INVALID_DASH, CAIRO_STATUS_INVALID_DSC_COMMENT, CAIRO_STATUS_INVALID_INDEX, CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, CAIRO_STATUS_TEMP_FILE_ERROR, CAIRO_STATUS_INVALID_STRIDE, CAIRO_STATUS_FONT_TYPE_MISMATCH, CAIRO_STATUS_USER_FONT_IMMUTABLE, CAIRO_STATUS_USER_FONT_ERROR, CAIRO_STATUS_NEGATIVE_COUNT, CAIRO_STATUS_INVALID_CLUSTERS, CAIRO_STATUS_INVALID_SLANT, CAIRO_STATUS_INVALID_WEIGHT, CAIRO_STATUS_INVALID_SIZE, CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, CAIRO_STATUS_DEVICE_TYPE_MISMATCH, CAIRO_STATUS_DEVICE_ERROR, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, CAIRO_STATUS_DEVICE_FINISHED, CAIRO_STATUS_LAST_STATUS } cairo_status_t; /** * cairo_content_t: * @CAIRO_CONTENT_COLOR: The surface will hold color content only. (Since 1.0) * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. (Since 1.0) * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. (Since 1.0) * * #cairo_content_t is used to describe the content that a surface will * contain, whether color information, alpha information (translucence * vs. opacity), or both. * * Note: The large values here are designed to keep #cairo_content_t * values distinct from #cairo_format_t values so that the * implementation can detect the error if users confuse the two types. * * Since: 1.0 **/ typedef enum _cairo_content { CAIRO_CONTENT_COLOR = 0x1000, CAIRO_CONTENT_ALPHA = 0x2000, CAIRO_CONTENT_COLOR_ALPHA = 0x3000 } cairo_content_t; /** * cairo_format_t: * @CAIRO_FORMAT_INVALID: no such format exists or is supported. * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with * alpha in the upper 8 bits, then red, then green, then blue. * The 32-bit quantities are stored native-endian. Pre-multiplied * alpha is used. (That is, 50% transparent red is 0x80800000, * not 0x80ff0000.) (Since 1.0) * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with * the upper 8 bits unused. Red, Green, and Blue are stored * in the remaining 24 bits in that order. (Since 1.0) * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding * an alpha value. (Since 1.0) * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding * an alpha value. Pixels are packed together into 32-bit * quantities. The ordering of the bits matches the * endianess of the platform. On a big-endian machine, the * first pixel is in the uppermost bit, on a little-endian * machine the first pixel is in the least-significant bit. (Since 1.0) * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity * with red in the upper 5 bits, then green in the middle * 6 bits, and blue in the lower 5 bits. (Since 1.2) * @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc. (Since 1.12) * * #cairo_format_t is used to identify the memory format of * image data. * * New entries may be added in future versions. * * Since: 1.0 **/ typedef enum _cairo_format { CAIRO_FORMAT_INVALID = -1, CAIRO_FORMAT_ARGB32 = 0, CAIRO_FORMAT_RGB24 = 1, CAIRO_FORMAT_A8 = 2, CAIRO_FORMAT_A1 = 3, CAIRO_FORMAT_RGB16_565 = 4, CAIRO_FORMAT_RGB30 = 5 } cairo_format_t; /** * cairo_write_func_t: * @closure: the output closure * @data: the buffer containing the data to write * @length: the amount of data to write * * #cairo_write_func_t is the type of function which is called when a * backend needs to write data to an output stream. It is passed the * closure which was specified by the user at the time the write * function was registered, the data to write and the length of the * data in bytes. The write function should return * %CAIRO_STATUS_SUCCESS if all the data was successfully written, * %CAIRO_STATUS_WRITE_ERROR otherwise. * * Returns: the status code of the write operation * * Since: 1.0 **/ typedef cairo_status_t (*cairo_write_func_t) (void *closure, const unsigned char *data, unsigned int length); /** * cairo_read_func_t: * @closure: the input closure * @data: the buffer into which to read the data * @length: the amount of data to read * * #cairo_read_func_t is the type of function which is called when a * backend needs to read data from an input stream. It is passed the * closure which was specified by the user at the time the read * function was registered, the buffer to read the data into and the * length of the data in bytes. The read function should return * %CAIRO_STATUS_SUCCESS if all the data was successfully read, * %CAIRO_STATUS_READ_ERROR otherwise. * * Returns: the status code of the read operation * * Since: 1.0 **/ typedef cairo_status_t (*cairo_read_func_t) (void *closure, unsigned char *data, unsigned int length); /** * cairo_rectangle_int_t: * @x: X coordinate of the left side of the rectangle * @y: Y coordinate of the the top side of the rectangle * @width: width of the rectangle * @height: height of the rectangle * * A data structure for holding a rectangle with integer coordinates. * * Since: 1.10 **/ typedef struct _cairo_rectangle_int { int x, y; int width, height; } cairo_rectangle_int_t; /* Functions for manipulating state objects */ cairo_public cairo_t * cairo_create (cairo_surface_t *target); cairo_public cairo_t * cairo_reference (cairo_t *cr); cairo_public void cairo_destroy (cairo_t *cr); cairo_public unsigned int cairo_get_reference_count (cairo_t *cr); cairo_public void * cairo_get_user_data (cairo_t *cr, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_set_user_data (cairo_t *cr, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); cairo_public void cairo_save (cairo_t *cr); cairo_public void cairo_restore (cairo_t *cr); cairo_public void cairo_push_group (cairo_t *cr); cairo_public void cairo_push_group_with_content (cairo_t *cr, cairo_content_t content); cairo_public cairo_pattern_t * cairo_pop_group (cairo_t *cr); cairo_public void cairo_pop_group_to_source (cairo_t *cr); /* Modify state */ /** * cairo_operator_t: * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) (Since 1.0) * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) (Since 1.0) * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer * (bounded) (Since 1.0) * @CAIRO_OPERATOR_IN: draw source where there was destination content * (unbounded) (Since 1.0) * @CAIRO_OPERATOR_OUT: draw source where there was no destination * content (unbounded) (Since 1.0) * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and * only there (Since 1.0) * @CAIRO_OPERATOR_DEST: ignore the source (Since 1.0) * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source (Since 1.0) * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was * source content (unbounded) (Since 1.0) * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no * source content (Since 1.0) * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content * and only there (unbounded) (Since 1.0) * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only * one of them (Since 1.0) * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated (Since 1.0) * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are * disjoint geometries (Since 1.0) * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied. * This causes the result to be at least as dark as the darker inputs. (Since 1.10) * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and * multiplied. This causes the result to be at least as light as the lighter * inputs. (Since 1.10) * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the * lightness of the destination color. (Since 1.10) * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it * is darker, otherwise keeps the source. (Since 1.10) * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it * is lighter, otherwise keeps the source. (Since 1.10) * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect * the source color. (Since 1.10) * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect * the source color. (Since 1.10) * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependent on source * color. (Since 1.10) * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependent on source * color. (Since 1.10) * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and * destination color. (Since 1.10) * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but * with lower contrast. (Since 1.10) * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source * and the saturation and luminosity of the target. (Since 1.10) * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation * of the source and the hue and luminosity of the target. Painting with * this mode onto a gray area produces no change. (Since 1.10) * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation * of the source and the luminosity of the target. This preserves the gray * levels of the target and is useful for coloring monochrome images or * tinting color images. (Since 1.10) * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of * the source and the hue and saturation of the target. This produces an * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. (Since 1.10) * * #cairo_operator_t is used to set the compositing operator for all cairo * drawing operations. * * The default operator is %CAIRO_OPERATOR_OVER. * * The operators marked as unbounded modify their * destination even outside of the mask layer (that is, their effect is not * bound by the mask layer). However, their effect can still be limited by * way of clipping. * * To keep things simple, the operator descriptions here * document the behavior for when both source and destination are either fully * transparent or fully opaque. The actual implementation works for * translucent layers too. * For a more detailed explanation of the effects of each operator, including * the mathematical definitions, see * http://cairographics.org/operators/. * * Since: 1.0 **/ typedef enum _cairo_operator { CAIRO_OPERATOR_CLEAR, CAIRO_OPERATOR_SOURCE, CAIRO_OPERATOR_OVER, CAIRO_OPERATOR_IN, CAIRO_OPERATOR_OUT, CAIRO_OPERATOR_ATOP, CAIRO_OPERATOR_DEST, CAIRO_OPERATOR_DEST_OVER, CAIRO_OPERATOR_DEST_IN, CAIRO_OPERATOR_DEST_OUT, CAIRO_OPERATOR_DEST_ATOP, CAIRO_OPERATOR_XOR, CAIRO_OPERATOR_ADD, CAIRO_OPERATOR_SATURATE, CAIRO_OPERATOR_MULTIPLY, CAIRO_OPERATOR_SCREEN, CAIRO_OPERATOR_OVERLAY, CAIRO_OPERATOR_DARKEN, CAIRO_OPERATOR_LIGHTEN, CAIRO_OPERATOR_COLOR_DODGE, CAIRO_OPERATOR_COLOR_BURN, CAIRO_OPERATOR_HARD_LIGHT, CAIRO_OPERATOR_SOFT_LIGHT, CAIRO_OPERATOR_DIFFERENCE, CAIRO_OPERATOR_EXCLUSION, CAIRO_OPERATOR_HSL_HUE, CAIRO_OPERATOR_HSL_SATURATION, CAIRO_OPERATOR_HSL_COLOR, CAIRO_OPERATOR_HSL_LUMINOSITY } cairo_operator_t; cairo_public void cairo_set_operator (cairo_t *cr, cairo_operator_t op); cairo_public void cairo_set_source (cairo_t *cr, cairo_pattern_t *source); cairo_public void cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); cairo_public void cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha); cairo_public void cairo_set_source_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y); cairo_public void cairo_set_tolerance (cairo_t *cr, double tolerance); /** * cairo_antialias_t: * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for * the subsystem and target device, since 1.0 * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask, since 1.0 * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using * shades of gray for black text on a white background, for example), since 1.0 * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking * advantage of the order of subpixel elements on devices * such as LCD panels, since 1.0 * @CAIRO_ANTIALIAS_FAST: Hint that the backend should perform some * antialiasing but prefer speed over quality, since 1.12 * @CAIRO_ANTIALIAS_GOOD: The backend should balance quality against * performance, since 1.12 * @CAIRO_ANTIALIAS_BEST: Hint that the backend should render at the highest * quality, sacrificing speed if necessary, since 1.12 * * Specifies the type of antialiasing to do when rendering text or shapes. * * As it is not necessarily clear from the above what advantages a particular * antialias method provides, since 1.12, there is also a set of hints: * @CAIRO_ANTIALIAS_FAST: Allow the backend to degrade raster quality for speed * @CAIRO_ANTIALIAS_GOOD: A balance between speed and quality * @CAIRO_ANTIALIAS_BEST: A high-fidelity, but potentially slow, raster mode * * These make no guarantee on how the backend will perform its rasterisation * (if it even rasterises!), nor that they have any differing effect other * than to enable some form of antialiasing. In the case of glyph rendering, * @CAIRO_ANTIALIAS_FAST and @CAIRO_ANTIALIAS_GOOD will be mapped to * @CAIRO_ANTIALIAS_GRAY, with @CAIRO_ANTALIAS_BEST being equivalent to * @CAIRO_ANTIALIAS_SUBPIXEL. * * The interpretation of @CAIRO_ANTIALIAS_DEFAULT is left entirely up to * the backend, typically this will be similar to @CAIRO_ANTIALIAS_GOOD. * * Since: 1.0 **/ typedef enum _cairo_antialias { CAIRO_ANTIALIAS_DEFAULT, /* method */ CAIRO_ANTIALIAS_NONE, CAIRO_ANTIALIAS_GRAY, CAIRO_ANTIALIAS_SUBPIXEL, /* hints */ CAIRO_ANTIALIAS_FAST, CAIRO_ANTIALIAS_GOOD, CAIRO_ANTIALIAS_BEST } cairo_antialias_t; cairo_public void cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); /** * cairo_fill_rule_t: * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from * left-to-right, counts +1. If the path crosses the ray * from right to left, counts -1. (Left and right are determined * from the perspective of looking along the ray from the starting * point.) If the total count is non-zero, the point will be filled. (Since 1.0) * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of * intersections, without regard to the orientation of the contour. If * the total number of intersections is odd, the point will be * filled. (Since 1.0) * * #cairo_fill_rule_t is used to select how paths are filled. For both * fill rules, whether or not a point is included in the fill is * determined by taking a ray from that point to infinity and looking * at intersections with the path. The ray can be in any direction, * as long as it doesn't pass through the end point of a segment * or have a tricky intersection such as intersecting tangent to the path. * (Note that filling is not actually implemented in this way. This * is just a description of the rule that is applied.) * * The default fill rule is %CAIRO_FILL_RULE_WINDING. * * New entries may be added in future versions. * * Since: 1.0 **/ typedef enum _cairo_fill_rule { CAIRO_FILL_RULE_WINDING, CAIRO_FILL_RULE_EVEN_ODD } cairo_fill_rule_t; cairo_public void cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); cairo_public void cairo_set_line_width (cairo_t *cr, double width); /** * cairo_line_cap_t: * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0) * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point (Since 1.0) * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point (Since 1.0) * * Specifies how to render the endpoints of the path when stroking. * * The default line cap style is %CAIRO_LINE_CAP_BUTT. * * Since: 1.0 **/ typedef enum _cairo_line_cap { CAIRO_LINE_CAP_BUTT, CAIRO_LINE_CAP_ROUND, CAIRO_LINE_CAP_SQUARE } cairo_line_cap_t; cairo_public void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); /** * cairo_line_join_t: * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see * cairo_set_miter_limit() (Since 1.0) * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the * joint point (Since 1.0) * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half * the line width from the joint point (Since 1.0) * * Specifies how to render the junction of two lines when stroking. * * The default line join style is %CAIRO_LINE_JOIN_MITER. * * Since: 1.0 **/ typedef enum _cairo_line_join { CAIRO_LINE_JOIN_MITER, CAIRO_LINE_JOIN_ROUND, CAIRO_LINE_JOIN_BEVEL } cairo_line_join_t; cairo_public void cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join); cairo_public void cairo_set_dash (cairo_t *cr, const double *dashes, int num_dashes, double offset); cairo_public void cairo_set_miter_limit (cairo_t *cr, double limit); cairo_public void cairo_translate (cairo_t *cr, double tx, double ty); cairo_public void cairo_scale (cairo_t *cr, double sx, double sy); cairo_public void cairo_rotate (cairo_t *cr, double angle); cairo_public void cairo_transform (cairo_t *cr, const cairo_matrix_t *matrix); cairo_public void cairo_set_matrix (cairo_t *cr, const cairo_matrix_t *matrix); cairo_public void cairo_identity_matrix (cairo_t *cr); cairo_public void cairo_user_to_device (cairo_t *cr, double *x, double *y); cairo_public void cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy); cairo_public void cairo_device_to_user (cairo_t *cr, double *x, double *y); cairo_public void cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy); /* Path creation functions */ cairo_public void cairo_new_path (cairo_t *cr); cairo_public void cairo_move_to (cairo_t *cr, double x, double y); cairo_public void cairo_new_sub_path (cairo_t *cr); cairo_public void cairo_line_to (cairo_t *cr, double x, double y); cairo_public void cairo_curve_to (cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3); cairo_public void cairo_arc (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); cairo_public void cairo_arc_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); /* XXX: NYI cairo_public void cairo_arc_to (cairo_t *cr, double x1, double y1, double x2, double y2, double radius); */ cairo_public void cairo_rel_move_to (cairo_t *cr, double dx, double dy); cairo_public void cairo_rel_line_to (cairo_t *cr, double dx, double dy); cairo_public void cairo_rel_curve_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3); cairo_public void cairo_rectangle (cairo_t *cr, double x, double y, double width, double height); /* XXX: NYI cairo_public void cairo_stroke_to_path (cairo_t *cr); */ cairo_public void cairo_close_path (cairo_t *cr); cairo_public void cairo_path_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2); /* Painting functions */ cairo_public void cairo_paint (cairo_t *cr); cairo_public void cairo_paint_with_alpha (cairo_t *cr, double alpha); cairo_public void cairo_mask (cairo_t *cr, cairo_pattern_t *pattern); cairo_public void cairo_mask_surface (cairo_t *cr, cairo_surface_t *surface, double surface_x, double surface_y); cairo_public void cairo_stroke (cairo_t *cr); cairo_public void cairo_stroke_preserve (cairo_t *cr); cairo_public void cairo_fill (cairo_t *cr); cairo_public void cairo_fill_preserve (cairo_t *cr); cairo_public void cairo_copy_page (cairo_t *cr); cairo_public void cairo_show_page (cairo_t *cr); /* Insideness testing */ cairo_public cairo_bool_t cairo_in_stroke (cairo_t *cr, double x, double y); cairo_public cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y); cairo_public cairo_bool_t cairo_in_clip (cairo_t *cr, double x, double y); /* Rectangular extents */ cairo_public void cairo_stroke_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2); cairo_public void cairo_fill_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2); /* Clipping */ cairo_public void cairo_reset_clip (cairo_t *cr); cairo_public void cairo_clip (cairo_t *cr); cairo_public void cairo_clip_preserve (cairo_t *cr); cairo_public void cairo_clip_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2); /** * cairo_rectangle_t: * @x: X coordinate of the left side of the rectangle * @y: Y coordinate of the the top side of the rectangle * @width: width of the rectangle * @height: height of the rectangle * * A data structure for holding a rectangle. * * Since: 1.4 **/ typedef struct _cairo_rectangle { double x, y, width, height; } cairo_rectangle_t; /** * cairo_rectangle_list_t: * @status: Error status of the rectangle list * @rectangles: Array containing the rectangles * @num_rectangles: Number of rectangles in this list * * A data structure for holding a dynamically allocated * array of rectangles. * * Since: 1.4 **/ typedef struct _cairo_rectangle_list { cairo_status_t status; cairo_rectangle_t *rectangles; int num_rectangles; } cairo_rectangle_list_t; cairo_public cairo_rectangle_list_t * cairo_copy_clip_rectangle_list (cairo_t *cr); cairo_public void cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); /* Font/Text functions */ /** * cairo_scaled_font_t: * * A #cairo_scaled_font_t is a font scaled to a particular size and device * resolution. A #cairo_scaled_font_t is most useful for low-level font * usage where a library or application wants to cache a reference * to a scaled font to speed up the computation of metrics. * * There are various types of scaled fonts, depending on the * font backend they use. The type of a * scaled font can be queried using cairo_scaled_font_get_type(). * * Memory management of #cairo_scaled_font_t is done with * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). * * Since: 1.0 **/ typedef struct _cairo_scaled_font cairo_scaled_font_t; /** * cairo_font_face_t: * * A #cairo_font_face_t specifies all aspects of a font other * than the size or font matrix (a font matrix is used to distort * a font by sheering it or scaling it unequally in the two * directions) . A font face can be set on a #cairo_t by using * cairo_set_font_face(); the size and font matrix are set with * cairo_set_font_size() and cairo_set_font_matrix(). * * There are various types of font faces, depending on the * font backend they use. The type of a * font face can be queried using cairo_font_face_get_type(). * * Memory management of #cairo_font_face_t is done with * cairo_font_face_reference() and cairo_font_face_destroy(). * * Since: 1.0 **/ typedef struct _cairo_font_face cairo_font_face_t; /** * cairo_glyph_t: * @index: glyph index in the font. The exact interpretation of the * glyph index depends on the font technology being used. * @x: the offset in the X direction between the origin used for * drawing or measuring the string and the origin of this glyph. * @y: the offset in the Y direction between the origin used for * drawing or measuring the string and the origin of this glyph. * * The #cairo_glyph_t structure holds information about a single glyph * when drawing or measuring text. A font is (in simple terms) a * collection of shapes used to draw text. A glyph is one of these * shapes. There can be multiple glyphs for a single character * (alternates to be used in different contexts, for example), or a * glyph can be a ligature of multiple * characters. Cairo doesn't expose any way of converting input text * into glyphs, so in order to use the Cairo interfaces that take * arrays of glyphs, you must directly access the appropriate * underlying font system. * * Note that the offsets given by @x and @y are not cumulative. When * drawing or measuring text, each glyph is individually positioned * with respect to the overall origin * * Since: 1.0 **/ typedef struct { unsigned long index; double x; double y; } cairo_glyph_t; cairo_public cairo_glyph_t * cairo_glyph_allocate (int num_glyphs); cairo_public void cairo_glyph_free (cairo_glyph_t *glyphs); /** * cairo_text_cluster_t: * @num_bytes: the number of bytes of UTF-8 text covered by cluster * @num_glyphs: the number of glyphs covered by cluster * * The #cairo_text_cluster_t structure holds information about a single * text cluster. A text cluster is a minimal * mapping of some glyphs corresponding to some UTF-8 text. * * For a cluster to be valid, both @num_bytes and @num_glyphs should * be non-negative, and at least one should be non-zero. * Note that clusters with zero glyphs are not as well supported as * normal clusters. For example, PDF rendering applications typically * ignore those clusters when PDF text is being selected. * * See cairo_show_text_glyphs() for how clusters are used in advanced * text operations. * * Since: 1.8 **/ typedef struct { int num_bytes; int num_glyphs; } cairo_text_cluster_t; cairo_public cairo_text_cluster_t * cairo_text_cluster_allocate (int num_clusters); cairo_public void cairo_text_cluster_free (cairo_text_cluster_t *clusters); /** * cairo_text_cluster_flags_t: * @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array * map to glyphs in the glyph array from end to start. (Since 1.8) * * Specifies properties of a text cluster mapping. * * Since: 1.8 **/ typedef enum _cairo_text_cluster_flags { CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001 } cairo_text_cluster_flags_t; /** * cairo_text_extents_t: * @x_bearing: the horizontal distance from the origin to the * leftmost part of the glyphs as drawn. Positive if the * glyphs lie entirely to the right of the origin. * @y_bearing: the vertical distance from the origin to the * topmost part of the glyphs as drawn. Positive only if the * glyphs lie completely below the origin; will usually be * negative. * @width: width of the glyphs as drawn * @height: height of the glyphs as drawn * @x_advance:distance to advance in the X direction * after drawing these glyphs * @y_advance: distance to advance in the Y direction * after drawing these glyphs. Will typically be zero except * for vertical text layout as found in East-Asian languages. * * The #cairo_text_extents_t structure stores the extents of a single * glyph or a string of glyphs in user-space coordinates. Because text * extents are in user-space coordinates, they are mostly, but not * entirely, independent of the current transformation matrix. If you call * cairo_scale(cr, 2.0, 2.0), text will * be drawn twice as big, but the reported text extents will not be * doubled. They will change slightly due to hinting (so you can't * assume that metrics are independent of the transformation matrix), * but otherwise will remain unchanged. * * Since: 1.0 **/ typedef struct { double x_bearing; double y_bearing; double width; double height; double x_advance; double y_advance; } cairo_text_extents_t; /** * cairo_font_extents_t: * @ascent: the distance that the font extends above the baseline. * Note that this is not always exactly equal to the maximum * of the extents of all the glyphs in the font, but rather * is picked to express the font designer's intent as to * how the font should align with elements above it. * @descent: the distance that the font extends below the baseline. * This value is positive for typical fonts that include * portions below the baseline. Note that this is not always * exactly equal to the maximum of the extents of all the * glyphs in the font, but rather is picked to express the * font designer's intent as to how the font should * align with elements below it. * @height: the recommended vertical distance between baselines when * setting consecutive lines of text with the font. This * is greater than @ascent+@descent by a * quantity known as the line spacing * or external leading. When space * is at a premium, most fonts can be set with only * a distance of @ascent+@descent between lines. * @max_x_advance: the maximum distance in the X direction that * the origin is advanced for any glyph in the font. * @max_y_advance: the maximum distance in the Y direction that * the origin is advanced for any glyph in the font. * This will be zero for normal fonts used for horizontal * writing. (The scripts of East Asia are sometimes written * vertically.) * * The #cairo_font_extents_t structure stores metric information for * a font. Values are given in the current user-space coordinate * system. * * Because font metrics are in user-space coordinates, they are * mostly, but not entirely, independent of the current transformation * matrix. If you call cairo_scale(cr, 2.0, 2.0), * text will be drawn twice as big, but the reported text extents will * not be doubled. They will change slightly due to hinting (so you * can't assume that metrics are independent of the transformation * matrix), but otherwise will remain unchanged. * * Since: 1.0 **/ typedef struct { double ascent; double descent; double height; double max_x_advance; double max_y_advance; } cairo_font_extents_t; /** * cairo_font_slant_t: * @CAIRO_FONT_SLANT_NORMAL: Upright font style, since 1.0 * @CAIRO_FONT_SLANT_ITALIC: Italic font style, since 1.0 * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style, since 1.0 * * Specifies variants of a font face based on their slant. * * Since: 1.0 **/ typedef enum _cairo_font_slant { CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_SLANT_OBLIQUE } cairo_font_slant_t; /** * cairo_font_weight_t: * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight, since 1.0 * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight, since 1.0 * * Specifies variants of a font face based on their weight. * * Since: 1.0 **/ typedef enum _cairo_font_weight { CAIRO_FONT_WEIGHT_NORMAL, CAIRO_FONT_WEIGHT_BOLD } cairo_font_weight_t; /** * cairo_subpixel_order_t: * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for * for the target device, since 1.0 * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally * with red at the left, since 1.0 * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally * with blue at the left, since 1.0 * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically * with red at the top, since 1.0 * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically * with blue at the top, since 1.0 * * The subpixel order specifies the order of color elements within * each pixel on the display device when rendering with an * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. * * Since: 1.0 **/ typedef enum _cairo_subpixel_order { CAIRO_SUBPIXEL_ORDER_DEFAULT, CAIRO_SUBPIXEL_ORDER_RGB, CAIRO_SUBPIXEL_ORDER_BGR, CAIRO_SUBPIXEL_ORDER_VRGB, CAIRO_SUBPIXEL_ORDER_VBGR } cairo_subpixel_order_t; /** * cairo_hint_style_t: * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for * font backend and target device, since 1.0 * @CAIRO_HINT_STYLE_NONE: Do not hint outlines, since 1.0 * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve * contrast while retaining good fidelity to the original * shapes, since 1.0 * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength * giving a compromise between fidelity to the original shapes * and contrast, since 1.0 * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast, since 1.0 * * Specifies the type of hinting to do on font outlines. Hinting * is the process of fitting outlines to the pixel grid in order * to improve the appearance of the result. Since hinting outlines * involves distorting them, it also reduces the faithfulness * to the original outline shapes. Not all of the outline hinting * styles are supported by all font backends. * * New entries may be added in future versions. * * Since: 1.0 **/ typedef enum _cairo_hint_style { CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_STYLE_NONE, CAIRO_HINT_STYLE_SLIGHT, CAIRO_HINT_STYLE_MEDIUM, CAIRO_HINT_STYLE_FULL } cairo_hint_style_t; /** * cairo_hint_metrics_t: * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default * manner for the font backend and target device, since 1.0 * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics, since 1.0 * @CAIRO_HINT_METRICS_ON: Hint font metrics, since 1.0 * * Specifies whether to hint font metrics; hinting font metrics * means quantizing them so that they are integer values in * device space. Doing this improves the consistency of * letter and line spacing, however it also means that text * will be laid out differently at different zoom factors. * * Since: 1.0 **/ typedef enum _cairo_hint_metrics { CAIRO_HINT_METRICS_DEFAULT, CAIRO_HINT_METRICS_OFF, CAIRO_HINT_METRICS_ON } cairo_hint_metrics_t; /** * cairo_font_options_t: * * An opaque structure holding all options that are used when * rendering fonts. * * Individual features of a #cairo_font_options_t can be set or * accessed using functions named * cairo_font_options_set_feature_name() and * cairo_font_options_get_feature_name(), like * cairo_font_options_set_antialias() and * cairo_font_options_get_antialias(). * * New features may be added to a #cairo_font_options_t in the * future. For this reason, cairo_font_options_copy(), * cairo_font_options_equal(), cairo_font_options_merge(), and * cairo_font_options_hash() should be used to copy, check * for equality, merge, or compute a hash value of * #cairo_font_options_t objects. * * Since: 1.0 **/ typedef struct _cairo_font_options cairo_font_options_t; cairo_public cairo_font_options_t * cairo_font_options_create (void); cairo_public cairo_font_options_t * cairo_font_options_copy (const cairo_font_options_t *original); cairo_public void cairo_font_options_destroy (cairo_font_options_t *options); cairo_public cairo_status_t cairo_font_options_status (cairo_font_options_t *options); cairo_public void cairo_font_options_merge (cairo_font_options_t *options, const cairo_font_options_t *other); cairo_public cairo_bool_t cairo_font_options_equal (const cairo_font_options_t *options, const cairo_font_options_t *other); cairo_public unsigned long cairo_font_options_hash (const cairo_font_options_t *options); cairo_public void cairo_font_options_set_antialias (cairo_font_options_t *options, cairo_antialias_t antialias); cairo_public cairo_antialias_t cairo_font_options_get_antialias (const cairo_font_options_t *options); cairo_public void cairo_font_options_set_subpixel_order (cairo_font_options_t *options, cairo_subpixel_order_t subpixel_order); cairo_public cairo_subpixel_order_t cairo_font_options_get_subpixel_order (const cairo_font_options_t *options); cairo_public void cairo_font_options_set_hint_style (cairo_font_options_t *options, cairo_hint_style_t hint_style); cairo_public cairo_hint_style_t cairo_font_options_get_hint_style (const cairo_font_options_t *options); cairo_public void cairo_font_options_set_hint_metrics (cairo_font_options_t *options, cairo_hint_metrics_t hint_metrics); cairo_public cairo_hint_metrics_t cairo_font_options_get_hint_metrics (const cairo_font_options_t *options); /* This interface is for dealing with text as text, not caring about the font object inside the the cairo_t. */ cairo_public void cairo_select_font_face (cairo_t *cr, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); cairo_public void cairo_set_font_size (cairo_t *cr, double size); cairo_public void cairo_set_font_matrix (cairo_t *cr, const cairo_matrix_t *matrix); cairo_public void cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix); cairo_public void cairo_set_font_options (cairo_t *cr, const cairo_font_options_t *options); cairo_public void cairo_get_font_options (cairo_t *cr, cairo_font_options_t *options); cairo_public void cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); cairo_public cairo_font_face_t * cairo_get_font_face (cairo_t *cr); cairo_public void cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font); cairo_public cairo_scaled_font_t * cairo_get_scaled_font (cairo_t *cr); cairo_public void cairo_show_text (cairo_t *cr, const char *utf8); cairo_public void cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); cairo_public void cairo_show_text_glyphs (cairo_t *cr, const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags); cairo_public void cairo_text_path (cairo_t *cr, const char *utf8); cairo_public void cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); cairo_public void cairo_text_extents (cairo_t *cr, const char *utf8, cairo_text_extents_t *extents); cairo_public void cairo_glyph_extents (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); cairo_public void cairo_font_extents (cairo_t *cr, cairo_font_extents_t *extents); /* Generic identifier for a font style */ cairo_public cairo_font_face_t * cairo_font_face_reference (cairo_font_face_t *font_face); cairo_public void cairo_font_face_destroy (cairo_font_face_t *font_face); cairo_public unsigned int cairo_font_face_get_reference_count (cairo_font_face_t *font_face); cairo_public cairo_status_t cairo_font_face_status (cairo_font_face_t *font_face); /** * cairo_font_type_t: * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api (Since: 1.2) * @CAIRO_FONT_TYPE_FT: The font is of type FreeType (Since: 1.2) * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 (Since: 1.2) * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6, in 1.2 and * 1.4 it was named CAIRO_FONT_TYPE_ATSUI) * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) * * #cairo_font_type_t is used to describe the type of a given font * face or scaled font. The font types are also known as "font * backends" within cairo. * * The type of a font face is determined by the function used to * create it, which will generally be of the form * cairo_type_font_face_create(). * The font face type can be queried with cairo_font_face_get_type() * * The various #cairo_font_face_t functions can be used with a font face * of any type. * * The type of a scaled font is determined by the type of the font * face passed to cairo_scaled_font_create(). The scaled font type can * be queried with cairo_scaled_font_get_type() * * The various #cairo_scaled_font_t functions can be used with scaled * fonts of any type, but some font backends also provide * type-specific functions that must only be called with a scaled font * of the appropriate type. These functions have names that begin with * cairo_type_scaled_font() * such as cairo_ft_scaled_font_lock_face(). * * The behavior of calling a type-specific function with a scaled font * of the wrong type is undefined. * * New entries may be added in future versions. * * Since: 1.2 **/ typedef enum _cairo_font_type { CAIRO_FONT_TYPE_TOY, CAIRO_FONT_TYPE_FT, CAIRO_FONT_TYPE_WIN32, CAIRO_FONT_TYPE_QUARTZ, CAIRO_FONT_TYPE_USER } cairo_font_type_t; cairo_public cairo_font_type_t cairo_font_face_get_type (cairo_font_face_t *font_face); cairo_public void * cairo_font_face_get_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_font_face_set_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); /* Portable interface to general font features. */ cairo_public cairo_scaled_font_t * cairo_scaled_font_create (cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options); cairo_public cairo_scaled_font_t * cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font); cairo_public void cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); cairo_public unsigned int cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font); cairo_public cairo_status_t cairo_scaled_font_status (cairo_scaled_font_t *scaled_font); cairo_public cairo_font_type_t cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font); cairo_public void * cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); cairo_public void cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *extents); cairo_public void cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, const char *utf8, cairo_text_extents_t *extents); cairo_public void cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); cairo_public cairo_status_t cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, double x, double y, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *cluster_flags); cairo_public cairo_font_face_t * cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font); cairo_public void cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, cairo_matrix_t *font_matrix); cairo_public void cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, cairo_matrix_t *ctm); cairo_public void cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, cairo_matrix_t *scale_matrix); cairo_public void cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, cairo_font_options_t *options); /* Toy fonts */ cairo_public cairo_font_face_t * cairo_toy_font_face_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); cairo_public const char * cairo_toy_font_face_get_family (cairo_font_face_t *font_face); cairo_public cairo_font_slant_t cairo_toy_font_face_get_slant (cairo_font_face_t *font_face); cairo_public cairo_font_weight_t cairo_toy_font_face_get_weight (cairo_font_face_t *font_face); /* User fonts */ cairo_public cairo_font_face_t * cairo_user_font_face_create (void); /* User-font method signatures */ /** * cairo_user_scaled_font_init_func_t: * @scaled_font: the scaled-font being created * @cr: a cairo context, in font space * @extents: font extents to fill in, in font space * * #cairo_user_scaled_font_init_func_t is the type of function which is * called when a scaled-font needs to be created for a user font-face. * * The cairo context @cr is not used by the caller, but is prepared in font * space, similar to what the cairo contexts passed to the render_glyph * method will look like. The callback can use this context for extents * computation for example. After the callback is called, @cr is checked * for any error status. * * The @extents argument is where the user font sets the font extents for * @scaled_font. It is in font space, which means that for most cases its * ascent and descent members should add to 1.0. @extents is preset to * hold a value of 1.0 for ascent, height, and max_x_advance, and 0.0 for * descent and max_y_advance members. * * The callback is optional. If not set, default font extents as described * in the previous paragraph will be used. * * Note that @scaled_font is not fully initialized at this * point and trying to use it for text operations in the callback will result * in deadlock. * * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error. * * Since: 1.8 **/ typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_font_extents_t *extents); /** * cairo_user_scaled_font_render_glyph_func_t: * @scaled_font: user scaled-font * @glyph: glyph code to render * @cr: cairo context to draw to, in font space * @extents: glyph extents to fill in, in font space * * #cairo_user_scaled_font_render_glyph_func_t is the type of function which * is called when a user scaled-font needs to render a glyph. * * The callback is mandatory, and expected to draw the glyph with code @glyph to * the cairo context @cr. @cr is prepared such that the glyph drawing is done in * font space. That is, the matrix set on @cr is the scale matrix of @scaled_font, * The @extents argument is where the user font sets the font extents for * @scaled_font. However, if user prefers to draw in user space, they can * achieve that by changing the matrix on @cr. All cairo rendering operations * to @cr are permitted, however, the result is undefined if any source other * than the default source on @cr is used. That means, glyph bitmaps should * be rendered using cairo_mask() instead of cairo_paint(). * * Other non-default settings on @cr include a font size of 1.0 (given that * it is set up to be in font space), and font options corresponding to * @scaled_font. * * The @extents argument is preset to have x_bearing, * width, and y_advance of zero, * y_bearing set to -font_extents.ascent, * height to font_extents.ascent+font_extents.descent, * and x_advance to font_extents.max_x_advance. * The only field user needs to set in majority of cases is * x_advance. * If the width field is zero upon the callback returning * (which is its preset value), the glyph extents are automatically computed * based on the drawings done to @cr. This is in most cases exactly what the * desired behavior is. However, if for any reason the callback sets the * extents, it must be ink extents, and include the extents of all drawing * done to @cr in the callback. * * Returns: %CAIRO_STATUS_SUCCESS upon success, or * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *extents); /** * cairo_user_scaled_font_text_to_glyphs_func_t: * @scaled_font: the scaled-font being created * @utf8: a string of text encoded in UTF-8 * @utf8_len: length of @utf8 in bytes * @glyphs: pointer to array of glyphs to fill, in font space * @num_glyphs: pointer to number of glyphs * @clusters: pointer to array of cluster mapping information to fill, or %NULL * @num_clusters: pointer to number of clusters * @cluster_flags: pointer to location to store cluster flags corresponding to the * output @clusters * * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which * is called to convert input text to an array of glyphs. This is used by the * cairo_show_text() operation. * * Using this callback the user-font has full control on glyphs and their * positions. That means, it allows for features like ligatures and kerning, * as well as complex shaping required for scripts like * Arabic and Indic. * * The @num_glyphs argument is preset to the number of glyph entries available * in the @glyphs buffer. If the @glyphs buffer is %NULL, the value of * @num_glyphs will be zero. If the provided glyph array is too short for * the conversion (or for convenience), a new glyph array may be allocated * using cairo_glyph_allocate() and placed in @glyphs. Upon return, * @num_glyphs should contain the number of generated glyphs. If the value * @glyphs points at has changed after the call, the caller will free the * allocated glyph array using cairo_glyph_free(). The caller will also free * the original value of @glyphs, so the callback shouldn't do so. * The callback should populate the glyph indices and positions (in font space) * assuming that the text is to be shown at the origin. * * If @clusters is not %NULL, @num_clusters and @cluster_flags are also * non-%NULL, and cluster mapping should be computed. The semantics of how * cluster array allocation works is similar to the glyph array. That is, * if @clusters initially points to a non-%NULL value, that array may be used * as a cluster buffer, and @num_clusters points to the number of cluster * entries available there. If the provided cluster array is too short for * the conversion (or for convenience), a new cluster array may be allocated * using cairo_text_cluster_allocate() and placed in @clusters. In this case, * the original value of @clusters will still be freed by the caller. Upon * return, @num_clusters should contain the number of generated clusters. * If the value @clusters points at has changed after the call, the caller * will free the allocated cluster array using cairo_text_cluster_free(). * * The callback is optional. If @num_glyphs is negative upon * the callback returning or if the return value * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t. * * Note: While cairo does not impose any limitation on glyph indices, * some applications may assume that a glyph index fits in a 16-bit * unsigned integer. As such, it is advised that user-fonts keep their * glyphs in the 0 to 65535 range. Furthermore, some applications may * assume that glyph 0 is a special glyph-not-found glyph. User-fonts * are advised to use glyph 0 for such purposes and do not use that * glyph value for other purposes. * * Returns: %CAIRO_STATUS_SUCCESS upon success, * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *cluster_flags); /** * cairo_user_scaled_font_unicode_to_glyph_func_t: * @scaled_font: the scaled-font being created * @unicode: input unicode character code-point * @glyph_index: output glyph index * * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which * is called to convert an input Unicode character to a single glyph. * This is used by the cairo_show_text() operation. * * This callback is used to provide the same functionality as the * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t) * but has much less control on the output, * in exchange for increased ease of use. The inherent assumption to using * this callback is that each character maps to one glyph, and that the * mapping is context independent. It also assumes that glyphs are positioned * according to their advance width. These mean no ligatures, kerning, or * complex scripts can be implemented using this callback. * * The callback is optional, and only used if text_to_glyphs callback is not * set or fails to return glyphs. If this callback is not set or if it returns * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode * code-points to glyph indices is assumed. * * Note: While cairo does not impose any limitation on glyph indices, * some applications may assume that a glyph index fits in a 16-bit * unsigned integer. As such, it is advised that user-fonts keep their * glyphs in the 0 to 65535 range. Furthermore, some applications may * assume that glyph 0 is a special glyph-not-found glyph. User-fonts * are advised to use glyph 0 for such purposes and do not use that * glyph value for other purposes. * * Returns: %CAIRO_STATUS_SUCCESS upon success, * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. * * Since: 1.8 **/ typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font, unsigned long unicode, unsigned long *glyph_index); /* User-font method setters */ cairo_public void cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, cairo_user_scaled_font_init_func_t init_func); cairo_public void cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, cairo_user_scaled_font_render_glyph_func_t render_glyph_func); cairo_public void cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); cairo_public void cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func); /* User-font method getters */ cairo_public cairo_user_scaled_font_init_func_t cairo_user_font_face_get_init_func (cairo_font_face_t *font_face); cairo_public cairo_user_scaled_font_render_glyph_func_t cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face); cairo_public cairo_user_scaled_font_text_to_glyphs_func_t cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); /* Query functions */ cairo_public cairo_operator_t cairo_get_operator (cairo_t *cr); cairo_public cairo_pattern_t * cairo_get_source (cairo_t *cr); cairo_public double cairo_get_tolerance (cairo_t *cr); cairo_public cairo_antialias_t cairo_get_antialias (cairo_t *cr); cairo_public cairo_bool_t cairo_has_current_point (cairo_t *cr); cairo_public void cairo_get_current_point (cairo_t *cr, double *x, double *y); cairo_public cairo_fill_rule_t cairo_get_fill_rule (cairo_t *cr); cairo_public double cairo_get_line_width (cairo_t *cr); cairo_public cairo_line_cap_t cairo_get_line_cap (cairo_t *cr); cairo_public cairo_line_join_t cairo_get_line_join (cairo_t *cr); cairo_public double cairo_get_miter_limit (cairo_t *cr); cairo_public int cairo_get_dash_count (cairo_t *cr); cairo_public void cairo_get_dash (cairo_t *cr, double *dashes, double *offset); cairo_public void cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix); cairo_public cairo_surface_t * cairo_get_target (cairo_t *cr); cairo_public cairo_surface_t * cairo_get_group_target (cairo_t *cr); /** * cairo_path_data_type_t: * @CAIRO_PATH_MOVE_TO: A move-to operation, since 1.0 * @CAIRO_PATH_LINE_TO: A line-to operation, since 1.0 * @CAIRO_PATH_CURVE_TO: A curve-to operation, since 1.0 * @CAIRO_PATH_CLOSE_PATH: A close-path operation, since 1.0 * * #cairo_path_data_t is used to describe the type of one portion * of a path when represented as a #cairo_path_t. * See #cairo_path_data_t for details. * * Since: 1.0 **/ typedef enum _cairo_path_data_type { CAIRO_PATH_MOVE_TO, CAIRO_PATH_LINE_TO, CAIRO_PATH_CURVE_TO, CAIRO_PATH_CLOSE_PATH } cairo_path_data_type_t; /** * cairo_path_data_t: * * #cairo_path_data_t is used to represent the path data inside a * #cairo_path_t. * * The data structure is designed to try to balance the demands of * efficiency and ease-of-use. A path is represented as an array of * #cairo_path_data_t, which is a union of headers and points. * * Each portion of the path is represented by one or more elements in * the array, (one header followed by 0 or more points). The length * value of the header is the number of array elements for the current * portion including the header, (ie. length == 1 + # of points), and * where the number of points for each element type is as follows: * * * %CAIRO_PATH_MOVE_TO: 1 point * %CAIRO_PATH_LINE_TO: 1 point * %CAIRO_PATH_CURVE_TO: 3 points * %CAIRO_PATH_CLOSE_PATH: 0 points * * * The semantics and ordering of the coordinate values are consistent * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and * cairo_close_path(). * * Here is sample code for iterating through a #cairo_path_t: * * * int i; * cairo_path_t *path; * cairo_path_data_t *data; *   * path = cairo_copy_path (cr); *   * for (i=0; i < path->num_data; i += path->data[i].header.length) { * data = &path->data[i]; * switch (data->header.type) { * case CAIRO_PATH_MOVE_TO: * do_move_to_things (data[1].point.x, data[1].point.y); * break; * case CAIRO_PATH_LINE_TO: * do_line_to_things (data[1].point.x, data[1].point.y); * break; * case CAIRO_PATH_CURVE_TO: * do_curve_to_things (data[1].point.x, data[1].point.y, * data[2].point.x, data[2].point.y, * data[3].point.x, data[3].point.y); * break; * case CAIRO_PATH_CLOSE_PATH: * do_close_path_things (); * break; * } * } * cairo_path_destroy (path); * * * As of cairo 1.4, cairo does not mind if there are more elements in * a portion of the path than needed. Such elements can be used by * users of the cairo API to hold extra values in the path data * structure. For this reason, it is recommended that applications * always use data->header.length to * iterate over the path data, instead of hardcoding the number of * elements for each element type. * * Since: 1.0 **/ typedef union _cairo_path_data_t cairo_path_data_t; union _cairo_path_data_t { struct { cairo_path_data_type_t type; int length; } header; struct { double x, y; } point; }; /** * cairo_path_t: * @status: the current error status * @data: the elements in the path * @num_data: the number of elements in the data array * * A data structure for holding a path. This data structure serves as * the return value for cairo_copy_path() and * cairo_copy_path_flat() as well the input value for * cairo_append_path(). * * See #cairo_path_data_t for hints on how to iterate over the * actual data within the path. * * The num_data member gives the number of elements in the data * array. This number is larger than the number of independent path * portions (defined in #cairo_path_data_type_t), since the data * includes both headers and coordinates for each portion. * * Since: 1.0 **/ typedef struct cairo_path { cairo_status_t status; cairo_path_data_t *data; int num_data; } cairo_path_t; cairo_public cairo_path_t * cairo_copy_path (cairo_t *cr); cairo_public cairo_path_t * cairo_copy_path_flat (cairo_t *cr); cairo_public void cairo_append_path (cairo_t *cr, const cairo_path_t *path); cairo_public void cairo_path_destroy (cairo_path_t *path); /* Error status queries */ cairo_public cairo_status_t cairo_status (cairo_t *cr); cairo_public const char * cairo_status_to_string (cairo_status_t status); /* Backend device manipulation */ cairo_public cairo_device_t * cairo_device_reference (cairo_device_t *device); /** * cairo_device_type_t: * @CAIRO_DEVICE_TYPE_DRM: The device is of type Direct Render Manager, since 1.10 * @CAIRO_DEVICE_TYPE_GL: The device is of type OpenGL, since 1.10 * @CAIRO_DEVICE_TYPE_SCRIPT: The device is of type script, since 1.10 * @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb, since 1.10 * @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib, since 1.10 * @CAIRO_DEVICE_TYPE_XML: The device is of type XML, since 1.10 * @CAIRO_DEVICE_TYPE_COGL: The device is of type cogl, since 1.12 * @CAIRO_DEVICE_TYPE_WIN32: The device is of type win32, since 1.12 * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid, since 1.10 * * #cairo_device_type_t is used to describe the type of a given * device. The devices types are also known as "backends" within cairo. * * The device type can be queried with cairo_device_get_type() * * The various #cairo_device_t functions can be used with devices of * any type, but some backends also provide type-specific functions * that must only be called with a device of the appropriate * type. These functions have names that begin with * cairo_type_device such as * cairo_xcb_device_debug_cap_xrender_version(). * * The behavior of calling a type-specific function with a device of * the wrong type is undefined. * * New entries may be added in future versions. * * Since: 1.10 **/ typedef enum _cairo_device_type { CAIRO_DEVICE_TYPE_DRM, CAIRO_DEVICE_TYPE_GL, CAIRO_DEVICE_TYPE_SCRIPT, CAIRO_DEVICE_TYPE_XCB, CAIRO_DEVICE_TYPE_XLIB, CAIRO_DEVICE_TYPE_XML, CAIRO_DEVICE_TYPE_COGL, CAIRO_DEVICE_TYPE_WIN32, CAIRO_DEVICE_TYPE_INVALID = -1 } cairo_device_type_t; cairo_public cairo_device_type_t cairo_device_get_type (cairo_device_t *device); cairo_public cairo_status_t cairo_device_status (cairo_device_t *device); cairo_public cairo_status_t cairo_device_acquire (cairo_device_t *device); cairo_public void cairo_device_release (cairo_device_t *device); cairo_public void cairo_device_flush (cairo_device_t *device); cairo_public void cairo_device_finish (cairo_device_t *device); cairo_public void cairo_device_destroy (cairo_device_t *device); cairo_public unsigned int cairo_device_get_reference_count (cairo_device_t *device); cairo_public void * cairo_device_get_user_data (cairo_device_t *device, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_device_set_user_data (cairo_device_t *device, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); /* Surface manipulation */ cairo_public cairo_surface_t * cairo_surface_create_similar (cairo_surface_t *other, cairo_content_t content, int width, int height); cairo_public cairo_surface_t * cairo_surface_create_similar_image (cairo_surface_t *other, cairo_format_t format, int width, int height); cairo_public cairo_surface_t * cairo_surface_map_to_image (cairo_surface_t *surface, const cairo_rectangle_int_t *extents); cairo_public void cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image); cairo_public cairo_surface_t * cairo_surface_create_for_rectangle (cairo_surface_t *target, double x, double y, double width, double height); typedef enum { CAIRO_SURFACE_OBSERVER_NORMAL = 0, CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS = 0x1 } cairo_surface_observer_mode_t; cairo_public cairo_surface_t * cairo_surface_create_observer (cairo_surface_t *target, cairo_surface_observer_mode_t mode); typedef void (*cairo_surface_observer_callback_t) (cairo_surface_t *observer, cairo_surface_t *target, void *data); cairo_public cairo_status_t cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface, cairo_surface_observer_callback_t func, void *data); cairo_public cairo_status_t cairo_surface_observer_print (cairo_surface_t *surface, cairo_write_func_t write_func, void *closure); cairo_public double cairo_surface_observer_elapsed (cairo_surface_t *surface); cairo_public cairo_status_t cairo_device_observer_print (cairo_device_t *device, cairo_write_func_t write_func, void *closure); cairo_public double cairo_device_observer_elapsed (cairo_device_t *device); cairo_public double cairo_device_observer_paint_elapsed (cairo_device_t *device); cairo_public double cairo_device_observer_mask_elapsed (cairo_device_t *device); cairo_public double cairo_device_observer_fill_elapsed (cairo_device_t *device); cairo_public double cairo_device_observer_stroke_elapsed (cairo_device_t *device); cairo_public double cairo_device_observer_glyphs_elapsed (cairo_device_t *device); cairo_public cairo_surface_t * cairo_surface_reference (cairo_surface_t *surface); cairo_public void cairo_surface_finish (cairo_surface_t *surface); cairo_public void cairo_surface_destroy (cairo_surface_t *surface); cairo_public cairo_device_t * cairo_surface_get_device (cairo_surface_t *surface); cairo_public unsigned int cairo_surface_get_reference_count (cairo_surface_t *surface); cairo_public cairo_status_t cairo_surface_status (cairo_surface_t *surface); /** * cairo_surface_type_t: * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image, since 1.2 * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf, since 1.2 * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps, since 1.2 * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib, since 1.2 * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb, since 1.2 * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz, since 1.2 * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz, since 1.2 * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32, since 1.2 * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos, since 1.2 * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb, since 1.2 * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg, since 1.2 * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2, since 1.4 * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface, since 1.6 * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image, since 1.6 * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10 * @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10 * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 * @CAIRO_SURFACE_TYPE_TEE: The surface is of type 'tee' (a multiplexing surface), since 1.10 * @CAIRO_SURFACE_TYPE_XML: The surface is of type XML (for debugging), since 1.10 * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10 * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with * cairo_surface_create_for_rectangle(), since 1.10 * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12 * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface * backends" within cairo. * * The type of a surface is determined by the function used to create * it, which will generally be of the form * cairo_type_surface_create(), * (though see cairo_surface_create_similar() as well). * * The surface type can be queried with cairo_surface_get_type() * * The various #cairo_surface_t functions can be used with surfaces of * any type, but some backends also provide type-specific functions * that must only be called with a surface of the appropriate * type. These functions have names that begin with * cairo_type_surface such as cairo_image_surface_get_width(). * * The behavior of calling a type-specific function with a surface of * the wrong type is undefined. * * New entries may be added in future versions. * * Since: 1.2 **/ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_IMAGE, CAIRO_SURFACE_TYPE_PDF, CAIRO_SURFACE_TYPE_PS, CAIRO_SURFACE_TYPE_XLIB, CAIRO_SURFACE_TYPE_XCB, CAIRO_SURFACE_TYPE_GLITZ, CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_SURFACE_TYPE_WIN32, CAIRO_SURFACE_TYPE_BEOS, CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_SURFACE_TYPE_SVG, CAIRO_SURFACE_TYPE_OS2, CAIRO_SURFACE_TYPE_WIN32_PRINTING, CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, CAIRO_SURFACE_TYPE_SCRIPT, CAIRO_SURFACE_TYPE_QT, CAIRO_SURFACE_TYPE_RECORDING, CAIRO_SURFACE_TYPE_VG, CAIRO_SURFACE_TYPE_GL, CAIRO_SURFACE_TYPE_DRM, CAIRO_SURFACE_TYPE_TEE, CAIRO_SURFACE_TYPE_XML, CAIRO_SURFACE_TYPE_SKIA, CAIRO_SURFACE_TYPE_SUBSURFACE, CAIRO_SURFACE_TYPE_COGL } cairo_surface_type_t; cairo_public cairo_surface_type_t cairo_surface_get_type (cairo_surface_t *surface); cairo_public cairo_content_t cairo_surface_get_content (cairo_surface_t *surface); #if CAIRO_HAS_PNG_FUNCTIONS cairo_public cairo_status_t cairo_surface_write_to_png (cairo_surface_t *surface, const char *filename); cairo_public cairo_status_t cairo_surface_write_to_png_stream (cairo_surface_t *surface, cairo_write_func_t write_func, void *closure); #endif cairo_public void * cairo_surface_get_user_data (cairo_surface_t *surface, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_surface_set_user_data (cairo_surface_t *surface, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); #define CAIRO_MIME_TYPE_JPEG "image/jpeg" #define CAIRO_MIME_TYPE_PNG "image/png" #define CAIRO_MIME_TYPE_JP2 "image/jp2" #define CAIRO_MIME_TYPE_URI "text/x-uri" #define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid" cairo_public void cairo_surface_get_mime_data (cairo_surface_t *surface, const char *mime_type, const unsigned char **data, unsigned long *length); cairo_public cairo_status_t cairo_surface_set_mime_data (cairo_surface_t *surface, const char *mime_type, const unsigned char *data, unsigned long length, cairo_destroy_func_t destroy, void *closure); cairo_public cairo_bool_t cairo_surface_supports_mime_type (cairo_surface_t *surface, const char *mime_type); cairo_public void cairo_surface_get_font_options (cairo_surface_t *surface, cairo_font_options_t *options); cairo_public void cairo_surface_flush (cairo_surface_t *surface); cairo_public void cairo_surface_mark_dirty (cairo_surface_t *surface); cairo_public void cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, int x, int y, int width, int height); cairo_public void cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, double y_offset); cairo_public void cairo_surface_get_device_offset (cairo_surface_t *surface, double *x_offset, double *y_offset); cairo_public void cairo_surface_set_fallback_resolution (cairo_surface_t *surface, double x_pixels_per_inch, double y_pixels_per_inch); cairo_public void cairo_surface_get_fallback_resolution (cairo_surface_t *surface, double *x_pixels_per_inch, double *y_pixels_per_inch); cairo_public void cairo_surface_copy_page (cairo_surface_t *surface); cairo_public void cairo_surface_show_page (cairo_surface_t *surface); cairo_public cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); /* Image-surface functions */ cairo_public cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, int height); cairo_public int cairo_format_stride_for_width (cairo_format_t format, int width); cairo_public cairo_surface_t * cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride); cairo_public unsigned char * cairo_image_surface_get_data (cairo_surface_t *surface); cairo_public cairo_format_t cairo_image_surface_get_format (cairo_surface_t *surface); cairo_public int cairo_image_surface_get_width (cairo_surface_t *surface); cairo_public int cairo_image_surface_get_height (cairo_surface_t *surface); cairo_public int cairo_image_surface_get_stride (cairo_surface_t *surface); #if CAIRO_HAS_PNG_FUNCTIONS cairo_public cairo_surface_t * cairo_image_surface_create_from_png (const char *filename); cairo_public cairo_surface_t * cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure); #endif /* Recording-surface functions */ cairo_public cairo_surface_t * cairo_recording_surface_create (cairo_content_t content, const cairo_rectangle_t *extents); cairo_public void cairo_recording_surface_ink_extents (cairo_surface_t *surface, double *x0, double *y0, double *width, double *height); cairo_public cairo_bool_t cairo_recording_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_t *extents); /* raster-source pattern (callback) functions */ /** * cairo_raster_source_acquire_func_t: * @pattern: the pattern being rendered from * @callback_data: the user data supplied during creation * @target: the rendering target surface * @extents: rectangular region of interest in pixels in sample space * * #cairo_raster_source_acquire_func_t is the type of function which is * called when a pattern is being rendered from. It should create a surface * that provides the pixel data for the region of interest as defined by * extents, though the surface itself does not have to be limited to that * area. For convenience the surface should probably be of image type, * created with cairo_surface_create_similar_image() for the target (which * enables the number of copies to be reduced during transfer to the * device). Another option, might be to return a similar surface to the * target for explicit handling by the application of a set of cached sources * on the device. The region of sample data provided should be defined using * cairo_surface_set_device_offset() to specify the top-left corner of the * sample data (along with width and height of the surface). * * Returns: a #cairo_surface_t * * Since: 1.12 **/ typedef cairo_surface_t * (*cairo_raster_source_acquire_func_t) (cairo_pattern_t *pattern, void *callback_data, cairo_surface_t *target, const cairo_rectangle_int_t *extents); /** * cairo_raster_source_release_func_t: * @pattern: the pattern being rendered from * @callback_data: the user data supplied during creation * @surface: the surface created during acquire * * #cairo_raster_source_release_func_t is the type of function which is * called when the pixel data is no longer being access by the pattern * for the rendering operation. Typically this function will simply * destroy the surface created during acquire. * * Since: 1.12 **/ typedef void (*cairo_raster_source_release_func_t) (cairo_pattern_t *pattern, void *callback_data, cairo_surface_t *surface); /** * cairo_raster_source_snapshot_func_t: * @pattern: the pattern being rendered from * @callback_data: the user data supplied during creation * * #cairo_raster_source_snapshot_func_t is the type of function which is * called when the pixel data needs to be preserved for later use * during printing. This pattern will be accessed again later, and it * is expected to provide the pixel data that was current at the time * of snapshotting. * * Return value: CAIRO_STATUS_SUCCESS on success, or one of the * #cairo_status_t error codes for failure. * * Since: 1.12 **/ typedef cairo_status_t (*cairo_raster_source_snapshot_func_t) (cairo_pattern_t *pattern, void *callback_data); /** * cairo_raster_source_copy_func_t: * @pattern: the #cairo_pattern_t that was copied to * @callback_data: the user data supplied during creation * @other: the #cairo_pattern_t being used as the source for the copy * * #cairo_raster_source_copy_func_t is the type of function which is * called when the pattern gets copied as a normal part of rendering. * * Return value: CAIRO_STATUS_SUCCESS on success, or one of the * #cairo_status_t error codes for failure. * * Since: 1.12 **/ typedef cairo_status_t (*cairo_raster_source_copy_func_t) (cairo_pattern_t *pattern, void *callback_data, const cairo_pattern_t *other); /** * cairo_raster_source_finish_func_t: * @pattern: the pattern being rendered from * @callback_data: the user data supplied during creation * * #cairo_raster_source_finish_func_t is the type of function which is * called when the pattern (or a copy thereof) is no longer required. * * Since: 1.12 **/ typedef void (*cairo_raster_source_finish_func_t) (cairo_pattern_t *pattern, void *callback_data); cairo_public cairo_pattern_t * cairo_pattern_create_raster_source (void *user_data, cairo_content_t content, int width, int height); cairo_public void cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *pattern, void *data); cairo_public void * cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *pattern); cairo_public void cairo_raster_source_pattern_set_acquire (cairo_pattern_t *pattern, cairo_raster_source_acquire_func_t acquire, cairo_raster_source_release_func_t release); cairo_public void cairo_raster_source_pattern_get_acquire (cairo_pattern_t *pattern, cairo_raster_source_acquire_func_t *acquire, cairo_raster_source_release_func_t *release); cairo_public void cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *pattern, cairo_raster_source_snapshot_func_t snapshot); cairo_public cairo_raster_source_snapshot_func_t cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *pattern); cairo_public void cairo_raster_source_pattern_set_copy (cairo_pattern_t *pattern, cairo_raster_source_copy_func_t copy); cairo_public cairo_raster_source_copy_func_t cairo_raster_source_pattern_get_copy (cairo_pattern_t *pattern); cairo_public void cairo_raster_source_pattern_set_finish (cairo_pattern_t *pattern, cairo_raster_source_finish_func_t finish); cairo_public cairo_raster_source_finish_func_t cairo_raster_source_pattern_get_finish (cairo_pattern_t *pattern); /* Pattern creation functions */ cairo_public cairo_pattern_t * cairo_pattern_create_rgb (double red, double green, double blue); cairo_public cairo_pattern_t * cairo_pattern_create_rgba (double red, double green, double blue, double alpha); cairo_public cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface); cairo_public cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1); cairo_public cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1); cairo_public cairo_pattern_t * cairo_pattern_create_mesh (void); cairo_public cairo_pattern_t * cairo_pattern_reference (cairo_pattern_t *pattern); cairo_public void cairo_pattern_destroy (cairo_pattern_t *pattern); cairo_public unsigned int cairo_pattern_get_reference_count (cairo_pattern_t *pattern); cairo_public cairo_status_t cairo_pattern_status (cairo_pattern_t *pattern); cairo_public void * cairo_pattern_get_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key); cairo_public cairo_status_t cairo_pattern_set_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); /** * cairo_pattern_type_t: * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) * color. It may be opaque or translucent, since 1.2. * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image), since 1.2. * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient, since 1.2. * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient, since 1.2. * @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh, since 1.12. * @CAIRO_PATTERN_TYPE_RASTER_SOURCE: The pattern is a user pattern providing raster data, since 1.12. * * #cairo_pattern_type_t is used to describe the type of a given pattern. * * The type of a pattern is determined by the function used to create * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() * functions create SOLID patterns. The remaining * cairo_pattern_create functions map to pattern types in obvious * ways. * * The pattern type can be queried with cairo_pattern_get_type() * * Most #cairo_pattern_t functions can be called with a pattern of any * type, (though trying to change the extend or filter for a solid * pattern will have no effect). A notable exception is * cairo_pattern_add_color_stop_rgb() and * cairo_pattern_add_color_stop_rgba() which must only be called with * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern * will be shutdown and put into an error state. * * New entries may be added in future versions. * * Since: 1.2 **/ typedef enum _cairo_pattern_type { CAIRO_PATTERN_TYPE_SOLID, CAIRO_PATTERN_TYPE_SURFACE, CAIRO_PATTERN_TYPE_LINEAR, CAIRO_PATTERN_TYPE_RADIAL, CAIRO_PATTERN_TYPE_MESH, CAIRO_PATTERN_TYPE_RASTER_SOURCE } cairo_pattern_type_t; cairo_public cairo_pattern_type_t cairo_pattern_get_type (cairo_pattern_t *pattern); cairo_public void cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, double red, double green, double blue); cairo_public void cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, double red, double green, double blue, double alpha); cairo_public void cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern); cairo_public void cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern); cairo_public void cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, double x1, double y1, double x2, double y2, double x3, double y3); cairo_public void cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, double x, double y); cairo_public void cairo_mesh_pattern_move_to (cairo_pattern_t *pattern, double x, double y); cairo_public void cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern, unsigned int point_num, double x, double y); cairo_public void cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern, unsigned int corner_num, double red, double green, double blue); cairo_public void cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern, unsigned int corner_num, double red, double green, double blue, double alpha); cairo_public void cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix); cairo_public void cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); /** * cairo_extend_t: * @CAIRO_EXTEND_NONE: pixels outside of the source pattern * are fully transparent (Since 1.0) * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating (Since 1.0) * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting * at the edges (Since 1.0; but only implemented for surface patterns since 1.6) * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy * the closest pixel from the source (Since 1.2; but only * implemented for surface patterns since 1.6) * * #cairo_extend_t is used to describe how pattern color/alpha will be * determined for areas "outside" the pattern's natural area, (for * example, outside the surface bounds or outside the gradient * geometry). * * Mesh patterns are not affected by the extend mode. * * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns * and %CAIRO_EXTEND_PAD for gradient patterns. * * New entries may be added in future versions. * * Since: 1.0 **/ typedef enum _cairo_extend { CAIRO_EXTEND_NONE, CAIRO_EXTEND_REPEAT, CAIRO_EXTEND_REFLECT, CAIRO_EXTEND_PAD } cairo_extend_t; cairo_public void cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); cairo_public cairo_extend_t cairo_pattern_get_extend (cairo_pattern_t *pattern); /** * cairo_filter_t: * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar * to %CAIRO_FILTER_NEAREST (Since 1.0) * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality * similar to %CAIRO_FILTER_BILINEAR (Since 1.0) * @CAIRO_FILTER_BEST: The highest-quality available, performance may * not be suitable for interactive use. (Since 1.0) * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering (Since 1.0) * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions (Since 1.0) * @CAIRO_FILTER_GAUSSIAN: This filter value is currently * unimplemented, and should not be used in current code. (Since 1.0) * * #cairo_filter_t is used to indicate what filtering should be * applied when reading pixel values from patterns. See * cairo_pattern_set_filter() for indicating the desired filter to be * used with a particular pattern. * * Since: 1.0 **/ typedef enum _cairo_filter { CAIRO_FILTER_FAST, CAIRO_FILTER_GOOD, CAIRO_FILTER_BEST, CAIRO_FILTER_NEAREST, CAIRO_FILTER_BILINEAR, CAIRO_FILTER_GAUSSIAN } cairo_filter_t; cairo_public void cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); cairo_public cairo_filter_t cairo_pattern_get_filter (cairo_pattern_t *pattern); cairo_public cairo_status_t cairo_pattern_get_rgba (cairo_pattern_t *pattern, double *red, double *green, double *blue, double *alpha); cairo_public cairo_status_t cairo_pattern_get_surface (cairo_pattern_t *pattern, cairo_surface_t **surface); cairo_public cairo_status_t cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, int index, double *offset, double *red, double *green, double *blue, double *alpha); cairo_public cairo_status_t cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, int *count); cairo_public cairo_status_t cairo_pattern_get_linear_points (cairo_pattern_t *pattern, double *x0, double *y0, double *x1, double *y1); cairo_public cairo_status_t cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, double *x0, double *y0, double *r0, double *x1, double *y1, double *r1); cairo_public cairo_status_t cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, unsigned int *count); cairo_public cairo_path_t * cairo_mesh_pattern_get_path (cairo_pattern_t *pattern, unsigned int patch_num); cairo_public cairo_status_t cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern, unsigned int patch_num, unsigned int corner_num, double *red, double *green, double *blue, double *alpha); cairo_public cairo_status_t cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern, unsigned int patch_num, unsigned int point_num, double *x, double *y); /* Matrix functions */ cairo_public void cairo_matrix_init (cairo_matrix_t *matrix, double xx, double yx, double xy, double yy, double x0, double y0); cairo_public void cairo_matrix_init_identity (cairo_matrix_t *matrix); cairo_public void cairo_matrix_init_translate (cairo_matrix_t *matrix, double tx, double ty); cairo_public void cairo_matrix_init_scale (cairo_matrix_t *matrix, double sx, double sy); cairo_public void cairo_matrix_init_rotate (cairo_matrix_t *matrix, double radians); cairo_public void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); cairo_public void cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); cairo_public void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); cairo_public cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix); cairo_public void cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b); cairo_public void cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy); cairo_public void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y); /* Region functions */ /** * cairo_region_t: * * A #cairo_region_t represents a set of integer-aligned rectangles. * * It allows set-theoretical operations like cairo_region_union() and * cairo_region_intersect() to be performed on them. * * Memory management of #cairo_region_t is done with * cairo_region_reference() and cairo_region_destroy(). * * Since: 1.10 **/ typedef struct _cairo_region cairo_region_t; typedef enum _cairo_region_overlap { CAIRO_REGION_OVERLAP_IN, /* completely inside region */ CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ CAIRO_REGION_OVERLAP_PART /* partly inside region */ } cairo_region_overlap_t; cairo_public cairo_region_t * cairo_region_create (void); cairo_public cairo_region_t * cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); cairo_public cairo_region_t * cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, int count); cairo_public cairo_region_t * cairo_region_copy (const cairo_region_t *original); cairo_public cairo_region_t * cairo_region_reference (cairo_region_t *region); cairo_public void cairo_region_destroy (cairo_region_t *region); cairo_public cairo_bool_t cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b); cairo_public cairo_status_t cairo_region_status (const cairo_region_t *region); cairo_public void cairo_region_get_extents (const cairo_region_t *region, cairo_rectangle_int_t *extents); cairo_public int cairo_region_num_rectangles (const cairo_region_t *region); cairo_public void cairo_region_get_rectangle (const cairo_region_t *region, int nth, cairo_rectangle_int_t *rectangle); cairo_public cairo_bool_t cairo_region_is_empty (const cairo_region_t *region); cairo_public cairo_region_overlap_t cairo_region_contains_rectangle (const cairo_region_t *region, const cairo_rectangle_int_t *rectangle); cairo_public cairo_bool_t cairo_region_contains_point (const cairo_region_t *region, int x, int y); cairo_public void cairo_region_translate (cairo_region_t *region, int dx, int dy); cairo_public cairo_status_t cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other); cairo_public cairo_status_t cairo_region_subtract_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle); cairo_public cairo_status_t cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other); cairo_public cairo_status_t cairo_region_intersect_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle); cairo_public cairo_status_t cairo_region_union (cairo_region_t *dst, const cairo_region_t *other); cairo_public cairo_status_t cairo_region_union_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle); cairo_public cairo_status_t cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other); cairo_public cairo_status_t cairo_region_xor_rectangle (cairo_region_t *dst, const cairo_rectangle_int_t *rectangle); /* Functions to be used while debugging (not intended for use in production code) */ cairo_public void cairo_debug_reset_static_data (void); CAIRO_END_DECLS #endif /* CAIRO_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/cairoint.h000066400000000000000000002026021271037650300242630ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth */ /* * These definitions are solely for use by the implementation of cairo * and constitute no kind of standard. If you need any of these * functions, please drop me a note. Either the library needs new * functionality, or there's a way to do what you need using the * existing published interfaces. cworth@cworth.org */ #ifndef _CAIROINT_H_ #define _CAIROINT_H_ #if HAVE_CONFIG_H #include "config.h" #endif #ifdef _MSC_VER #define cairo_public __declspec(dllexport) #endif #include #include #include #include #include #ifdef _MSC_VER #define _USE_MATH_DEFINES #endif #include #include #include #include "cairo.h" #include #include "cairo-compiler-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_PDF_SURFACE || \ CAIRO_HAS_PS_SURFACE || \ CAIRO_HAS_SCRIPT_SURFACE || \ CAIRO_HAS_XML_SURFACE #define CAIRO_HAS_DEFLATE_STREAM 1 #endif #if CAIRO_HAS_PS_SURFACE || \ CAIRO_HAS_PDF_SURFACE || \ CAIRO_HAS_SVG_SURFACE || \ CAIRO_HAS_WIN32_SURFACE #define CAIRO_HAS_FONT_SUBSET 1 #endif #if CAIRO_HAS_PS_SURFACE || \ CAIRO_HAS_PDF_SURFACE || \ CAIRO_HAS_FONT_SUBSET #define CAIRO_HAS_PDF_OPERATORS 1 #endif CAIRO_BEGIN_DECLS #if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ cairo_private FILE * _cairo_win32_tmpfile (void); #define tmpfile() _cairo_win32_tmpfile() #endif #undef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #undef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 #endif #ifndef M_SQRT1_2 #define M_SQRT1_2 0.707106781186547524400844362104849039 #endif #undef ARRAY_LENGTH #define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0]))) #undef STRINGIFY #undef STRINGIFY_ARG #define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) #define STRINGIFY_ARG(contents) #contents #if defined (__GNUC__) #define cairo_container_of(ptr, type, member) ({ \ const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ (type *) ((char *) mptr__ - offsetof (type, member)); \ }) #else #define cairo_container_of(ptr, type, member) \ ((type *)((char *) (ptr) - (char *) &((type *)0)->member)) #endif #define ASSERT_NOT_REACHED \ do { \ assert (!"reached"); \ } while (0) #define COMPILE_TIME_ASSERT1(condition, line) \ typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1] #define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line) #define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__) #define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff)) #define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff) #define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff)) #define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00) #define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0) #define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short) #define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short) /* Reverse the bits in a byte with 7 operations (no 64-bit): * Devised by Sean Anderson, July 13, 2001. * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */ #define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) /* Return the number of 1 bits in mask. * * GCC 3.4 supports a "population count" builtin, which on many targets is * implemented with a single instruction. There is a fallback definition * in libgcc in case a target does not have one, which should be just as * good as the open-coded solution below, (which is "HACKMEM 169"). */ static inline int cairo_const _cairo_popcount (uint32_t mask) { #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) return __builtin_popcount (mask); #else register int y; y = (mask >> 1) &033333333333; y = mask - y - ((y >>1) & 033333333333); return (((y + (y >> 3)) & 030707070707) % 077); #endif } static cairo_always_inline cairo_bool_t _cairo_is_little_endian (void) { static const int i = 1; return *((char *) &i) == 0x01; } #ifdef WORDS_BIGENDIAN #define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c) #else #define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c) #endif #ifdef WORDS_BIGENDIAN #define cpu_to_be16(v) (v) #define be16_to_cpu(v) (v) #define cpu_to_be32(v) (v) #define be32_to_cpu(v) (v) #else static inline uint16_t cairo_const cpu_to_be16(uint16_t v) { return (v << 8) | (v >> 8); } static inline uint16_t cairo_const be16_to_cpu(uint16_t v) { return cpu_to_be16 (v); } static inline uint32_t cairo_const cpu_to_be32(uint32_t v) { return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); } static inline uint32_t cairo_const be32_to_cpu(uint32_t v) { return cpu_to_be32 (v); } #endif /* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales. */ static inline int cairo_const _cairo_isspace (int c) { return (c == 0x20 || (c >= 0x09 && c <= 0x0d)); } static inline int cairo_const _cairo_isdigit (int c) { return (c >= '0' && c <= '9'); } #include "cairo-types-private.h" #include "cairo-cache-private.h" #include "cairo-reference-count-private.h" #include "cairo-spans-private.h" #include "cairo-surface-private.h" cairo_private void _cairo_box_from_doubles (cairo_box_t *box, double *x1, double *y1, double *x2, double *y2); cairo_private void _cairo_box_to_doubles (const cairo_box_t *box, double *x1, double *y1, double *x2, double *y2); cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); cairo_private void _cairo_box_add_curve_to (cairo_box_t *extents, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d); cairo_private void _cairo_boxes_get_extents (const cairo_box_t *boxes, int num_boxes, cairo_box_t *extents); cairo_private extern const cairo_rectangle_int_t _cairo_empty_rectangle; cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle; static inline void _cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect) { *rect = _cairo_unbounded_rectangle; } cairo_private_no_warn cairo_bool_t _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src); static inline cairo_bool_t _cairo_rectangle_intersects (const cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src) { return !(src->x >= dst->x + (int) dst->width || src->x + (int) src->width <= dst->x || src->y >= dst->y + (int) dst->height || src->y + (int) src->height <= dst->y); } static inline cairo_bool_t _cairo_rectangle_contains_rectangle (const cairo_rectangle_int_t *a, const cairo_rectangle_int_t *b) { return (a->x <= b->x && a->x + (int) a->width >= b->x + (int) b->width && a->y <= b->y && a->y + (int) a->height >= b->y + (int) b->height); } cairo_private void _cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti, const cairo_rectangle_t *rectf); /* Extends the dst rectangle to also contain src. * If one of the rectangles is empty, the result is undefined */ cairo_private void _cairo_rectangle_union (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src); cairo_private cairo_bool_t _cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line) cairo_pure; cairo_private cairo_bool_t _cairo_spline_intersects (const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d, const cairo_box_t *box) cairo_pure; typedef struct { const cairo_user_data_key_t *key; void *user_data; cairo_destroy_func_t destroy; } cairo_user_data_slot_t; cairo_private void _cairo_user_data_array_init (cairo_user_data_array_t *array); cairo_private void _cairo_user_data_array_fini (cairo_user_data_array_t *array); cairo_private void * _cairo_user_data_array_get_data (cairo_user_data_array_t *array, const cairo_user_data_key_t *key); cairo_private cairo_status_t _cairo_user_data_array_set_data (cairo_user_data_array_t *array, const cairo_user_data_key_t *key, void *user_data, cairo_destroy_func_t destroy); cairo_private cairo_status_t _cairo_user_data_array_copy (cairo_user_data_array_t *dst, const cairo_user_data_array_t *src); cairo_private void _cairo_user_data_array_foreach (cairo_user_data_array_t *array, void (*func) (const void *key, void *elt, void *closure), void *closure); #define _CAIRO_HASH_INIT_VALUE 5381 cairo_private unsigned long _cairo_hash_string (const char *c); cairo_private unsigned long _cairo_hash_bytes (unsigned long hash, const void *bytes, unsigned int length); #define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash) #define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) #include "cairo-scaled-font-private.h" struct _cairo_font_face { /* hash_entry must be first */ cairo_hash_entry_t hash_entry; cairo_status_t status; cairo_reference_count_t ref_count; cairo_user_data_array_t user_data; const cairo_font_face_backend_t *backend; }; cairo_private void _cairo_default_context_reset_static_data (void); cairo_private void _cairo_toy_font_face_reset_static_data (void); cairo_private void _cairo_ft_font_reset_static_data (void); cairo_private void _cairo_win32_font_reset_static_data (void); #if CAIRO_HAS_COGL_SURFACE void _cairo_cogl_context_reset_static_data (void); #endif /* the font backend interface */ struct _cairo_unscaled_font_backend { void (*destroy) (void *unscaled_font); }; /* #cairo_toy_font_face_t - simple family/slant/weight font faces used for * the built-in font API */ typedef struct _cairo_toy_font_face { cairo_font_face_t base; const char *family; cairo_bool_t owns_family; cairo_font_slant_t slant; cairo_font_weight_t weight; cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */ } cairo_toy_font_face_t; typedef enum _cairo_scaled_glyph_info { CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2), CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3) } cairo_scaled_glyph_info_t; typedef struct _cairo_scaled_font_subset { cairo_scaled_font_t *scaled_font; unsigned int font_id; unsigned int subset_id; /* Index of glyphs array is subset_glyph_index. * Value of glyphs array is scaled_font_glyph_index. */ unsigned long *glyphs; char **utf8; char **glyph_names; int *to_latin_char; unsigned long *latin_to_subset_glyph_index; unsigned int num_glyphs; cairo_bool_t is_composite; cairo_bool_t is_scaled; cairo_bool_t is_latin; } cairo_scaled_font_subset_t; struct _cairo_scaled_font_backend { cairo_font_type_t type; void (*fini) (void *scaled_font); cairo_warn cairo_int_status_t (*scaled_glyph_init) (void *scaled_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info); /* A backend only needs to implement this or ucs4_to_index(), not * both. This allows the backend to do something more sophisticated * then just converting characters one by one. */ cairo_warn cairo_int_status_t (*text_to_glyphs) (void *scaled_font, double x, double y, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *cluster_flags); unsigned long (*ucs4_to_index) (void *scaled_font, uint32_t ucs4); /* Read data from a sfnt font table. * @scaled_font: font * @tag: 4 byte table name specifying the table to read. * @offset: offset into the table * @buffer: buffer to write data into. Caller must ensure there is sufficient space. * If NULL, return the size of the table in @length. * @length: If @buffer is NULL, the size of the table will be returned in @length. * If @buffer is not null, @length specifies the number of bytes to read. * * If less than @length bytes are available to read this function * returns CAIRO_INT_STATUS_UNSUPPORTED. Note that requesting more * bytes than are available in the table may continue reading data * from the following table and return success. If this is * undesirable the caller should first query the table size. If an * error occurs the output value of @length is undefined. * * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a sfnt style font or table not found. */ cairo_warn cairo_int_status_t (*load_truetype_table)(void *scaled_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length); /* ucs4 is set to -1 if the unicode character could not be found * for the glyph */ cairo_warn cairo_int_status_t (*index_to_ucs4)(void *scaled_font, unsigned long index, uint32_t *ucs4); cairo_warn cairo_bool_t (*is_synthetic)(void *scaled_font); /* For type 1 fonts, return the glyph name for a given glyph index. * A glyph index and list of glyph names in the Type 1 fonts is provided. * The function returns the index of the glyph in the list of glyph names. * @scaled_font: font * @glyph_names: the names of each glyph in the Type 1 font in the * order they appear in the CharStrings array * @num_glyph_names: the number of names in the glyph_names array * @glyph_index: the given glyph index * @glyph_array_index: (index into glyph_names) the glyph name corresponding * to the glyph_index */ cairo_warn cairo_int_status_t (*index_to_glyph_name)(void *scaled_font, char **glyph_names, int num_glyph_names, unsigned long glyph_index, unsigned long *glyph_array_index); /* Read data from a PostScript font. * @scaled_font: font * @offset: offset into the table * @buffer: buffer to write data into. Caller must ensure there is sufficient space. * If NULL, return the size of the table in @length. * @length: If @buffer is NULL, the size of the table will be returned in @length. * If @buffer is not null, @length specifies the number of bytes to read. * * If less than @length bytes are available to read this function * returns CAIRO_INT_STATUS_UNSUPPORTED. If an error occurs the * output value of @length is undefined. * * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a Type 1 font. */ cairo_warn cairo_int_status_t (*load_type1_data) (void *scaled_font, long offset, unsigned char *buffer, unsigned long *length); }; struct _cairo_font_face_backend { cairo_font_type_t type; cairo_warn cairo_status_t (*create_for_toy) (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face); /* The destroy() function is allowed to resurrect the font face * by re-referencing. This is needed for the FreeType backend. */ void (*destroy) (void *font_face); cairo_warn cairo_status_t (*scaled_font_create) (void *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **scaled_font); cairo_font_face_t * (*get_implementation) (void *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options); }; extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; /* concrete font backends */ #if CAIRO_HAS_FT_FONT extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend; #endif #if CAIRO_HAS_WIN32_FONT extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend; #endif #if CAIRO_HAS_QUARTZ_FONT extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; #endif #define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear; extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white; struct _cairo_surface_attributes { cairo_matrix_t matrix; cairo_extend_t extend; cairo_filter_t filter; cairo_bool_t has_component_alpha; int x_offset; int y_offset; void *extra; }; #define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL #define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL #define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" #define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" #define CAIRO_FT_FONT_FAMILY_DEFAULT "" #define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" #if CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend #elif CAIRO_HAS_QUARTZ_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend #elif CAIRO_HAS_FT_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend #else #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend #endif #define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER #define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1 #define CAIRO_GSTATE_FILL_RULE_DEFAULT CAIRO_FILL_RULE_WINDING #define CAIRO_GSTATE_LINE_WIDTH_DEFAULT 2.0 #define CAIRO_GSTATE_LINE_CAP_DEFAULT CAIRO_LINE_CAP_BUTT #define CAIRO_GSTATE_LINE_JOIN_DEFAULT CAIRO_LINE_JOIN_MITER #define CAIRO_GSTATE_MITER_LIMIT_DEFAULT 10.0 #define CAIRO_GSTATE_DEFAULT_FONT_SIZE 10.0 #define CAIRO_SURFACE_RESOLUTION_DEFAULT 72.0 #define CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT 300.0 typedef struct _cairo_stroke_face { cairo_point_t ccw; cairo_point_t point; cairo_point_t cw; cairo_slope_t dev_vector; cairo_point_double_t dev_slope; cairo_point_double_t usr_vector; double length; } cairo_stroke_face_t; /* cairo.c */ static inline double cairo_const _cairo_restrict_value (double value, double min, double max) { if (value < min) return min; else if (value > max) return max; else return value; } /* C99 round() rounds to the nearest integral value with halfway cases rounded * away from 0. _cairo_round rounds halfway cases toward positive infinity. * This matches the rounding behaviour of _cairo_lround. */ static inline double cairo_const _cairo_round (double r) { return floor (r + .5); } #if DISABLE_SOME_FLOATING_POINT cairo_private int _cairo_lround (double d) cairo_const; #else static inline int cairo_const _cairo_lround (double r) { return _cairo_round (r); } #endif cairo_private uint16_t _cairo_half_from_float (float f) cairo_const; cairo_private cairo_bool_t _cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const; cairo_private cairo_bool_t _cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const; enum { CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1, CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2, }; cairo_private uint32_t _cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const; /* cairo-color.c */ cairo_private const cairo_color_t * _cairo_stock_color (cairo_stock_t stock) cairo_pure; #define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) #define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) #define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) cairo_private uint16_t _cairo_color_double_to_short (double d) cairo_const; cairo_private void _cairo_color_init_rgba (cairo_color_t *color, double red, double green, double blue, double alpha); cairo_private void _cairo_color_multiply_alpha (cairo_color_t *color, double alpha); cairo_private void _cairo_color_get_rgba (cairo_color_t *color, double *red, double *green, double *blue, double *alpha); cairo_private void _cairo_color_get_rgba_premultiplied (cairo_color_t *color, double *red, double *green, double *blue, double *alpha); cairo_private cairo_bool_t _cairo_color_equal (const cairo_color_t *color_a, const cairo_color_t *color_b) cairo_pure; cairo_private cairo_bool_t _cairo_color_stop_equal (const cairo_color_stop_t *color_a, const cairo_color_stop_t *color_b) cairo_pure; cairo_private cairo_content_t _cairo_color_get_content (const cairo_color_t *color) cairo_pure; /* cairo-font-face.c */ extern const cairo_private cairo_font_face_t _cairo_font_face_nil; cairo_private void _cairo_font_face_init (cairo_font_face_t *font_face, const cairo_font_face_backend_t *backend); cairo_private cairo_status_t _cairo_font_face_set_error (cairo_font_face_t *font_face, cairo_status_t status); cairo_private void _cairo_unscaled_font_init (cairo_unscaled_font_t *font, const cairo_unscaled_font_backend_t *backend); cairo_private_no_warn cairo_unscaled_font_t * _cairo_unscaled_font_reference (cairo_unscaled_font_t *font); cairo_private void _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); /* cairo-font-face-twin.c */ cairo_private cairo_font_face_t * _cairo_font_face_twin_create_fallback (void); cairo_private cairo_status_t _cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face); /* cairo-font-face-twin-data.c */ extern const cairo_private int8_t _cairo_twin_outlines[]; extern const cairo_private uint16_t _cairo_twin_charmap[128]; /* cairo-font-options.c */ cairo_private void _cairo_font_options_init_default (cairo_font_options_t *options); cairo_private void _cairo_font_options_init_copy (cairo_font_options_t *options, const cairo_font_options_t *other); cairo_private void _cairo_font_options_set_lcd_filter (cairo_font_options_t *options, cairo_lcd_filter_t lcd_filter); cairo_private cairo_lcd_filter_t _cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); cairo_private void _cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, cairo_round_glyph_positions_t round); cairo_private cairo_round_glyph_positions_t _cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options); /* cairo-hull.c */ cairo_private cairo_status_t _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); /* cairo-lzw.c */ cairo_private unsigned char * _cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out); /* cairo-misc.c */ cairo_private cairo_status_t _cairo_validate_text_clusters (const char *utf8, int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags); cairo_private cairo_status_t _cairo_intern_string (const char **str_inout, int len); cairo_private void _cairo_intern_string_reset_static_data (void); /* cairo-path-fixed.c */ cairo_private cairo_path_fixed_t * _cairo_path_fixed_create (void); cairo_private void _cairo_path_fixed_init (cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, const cairo_path_fixed_t *other); cairo_private void _cairo_path_fixed_fini (cairo_path_fixed_t *path); cairo_private void _cairo_path_fixed_destroy (cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y); cairo_private void _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path); cairo_private cairo_status_t _cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, cairo_fixed_t dx, cairo_fixed_t dy); cairo_private cairo_status_t _cairo_path_fixed_line_to (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y); cairo_private cairo_status_t _cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, cairo_fixed_t dx, cairo_fixed_t dy); cairo_private cairo_status_t _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, cairo_fixed_t x0, cairo_fixed_t y0, cairo_fixed_t x1, cairo_fixed_t y1, cairo_fixed_t x2, cairo_fixed_t y2); cairo_private cairo_status_t _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, cairo_fixed_t dx0, cairo_fixed_t dy0, cairo_fixed_t dx1, cairo_fixed_t dy1, cairo_fixed_t dx2, cairo_fixed_t dy2); cairo_private cairo_status_t _cairo_path_fixed_close_path (cairo_path_fixed_t *path); cairo_private cairo_bool_t _cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, cairo_fixed_t *x, cairo_fixed_t *y); typedef cairo_status_t (cairo_path_fixed_move_to_func_t) (void *closure, const cairo_point_t *point); typedef cairo_status_t (cairo_path_fixed_line_to_func_t) (void *closure, const cairo_point_t *point); typedef cairo_status_t (cairo_path_fixed_curve_to_func_t) (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2); typedef cairo_status_t (cairo_path_fixed_close_path_func_t) (void *closure); cairo_private cairo_status_t _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_curve_to_func_t *curve_to, cairo_path_fixed_close_path_func_t *close_path, void *closure); cairo_private cairo_status_t _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_close_path_func_t *close_path, void *closure, double tolerance); cairo_private cairo_bool_t _cairo_path_bounder_extents (const cairo_path_fixed_t *path, cairo_box_t *box); cairo_private cairo_bool_t _cairo_path_fixed_extents (const cairo_path_fixed_t *path, cairo_box_t *box); cairo_private void _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents); cairo_private void _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents); cairo_private void _cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_rectangle_int_t *extents); cairo_private void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_rectangle_int_t *extents); cairo_private void _cairo_path_fixed_transform (cairo_path_fixed_t *path, const cairo_matrix_t *matrix); cairo_private cairo_bool_t _cairo_path_fixed_is_box (const cairo_path_fixed_t *path, cairo_box_t *box); cairo_private cairo_bool_t _cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, cairo_box_t *box); /* cairo-path-in-fill.c */ cairo_private cairo_bool_t _cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, double x, double y); /* cairo-path-fill.c */ cairo_private cairo_status_t _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, double tolerance, cairo_polygon_t *polygon); cairo_private cairo_status_t _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path, cairo_antialias_t antialias, cairo_polygon_t *polygon); cairo_private cairo_status_t _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_boxes_t *boxes); cairo_private cairo_region_t * _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_traps_t *traps); /* cairo-path-stroke.c */ cairo_private cairo_status_t _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_polygon_t *polygon); cairo_private cairo_int_status_t _cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path, const cairo_stroke_style_t*style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_tristrip_t *strip); cairo_private cairo_status_t _cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_polygon_t *polygon); cairo_private cairo_int_status_t _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_antialias_t antialias, cairo_boxes_t *boxes); cairo_private cairo_int_status_t _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps); cairo_private cairo_int_status_t _cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_traps_t *traps); cairo_private cairo_status_t _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_status_t (*add_triangle) (void *closure, const cairo_point_t triangle[3]), cairo_status_t (*add_triangle_fan) (void *closure, const cairo_point_t *midpt, const cairo_point_t *points, int npoints), cairo_status_t (*add_quad) (void *closure, const cairo_point_t quad[4]), void *closure); /* cairo-scaled-font.c */ cairo_private void _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font); cairo_private void _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font); cairo_private void _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font); cairo_private cairo_status_t _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, cairo_status_t status); cairo_private cairo_scaled_font_t * _cairo_scaled_font_create_in_error (cairo_status_t status); cairo_private void _cairo_scaled_font_reset_static_data (void); cairo_private cairo_status_t _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font); cairo_private void _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font); cairo_private cairo_status_t _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, const cairo_scaled_font_backend_t *backend); cairo_private cairo_status_t _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *fs_metrics); /* This should only be called on an error path by a scaled_font constructor */ cairo_private void _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); cairo_private cairo_status_t _cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, cairo_font_extents_t *extents); cairo_private cairo_status_t _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_rectangle_int_t *extents, cairo_bool_t *overlap); cairo_private cairo_bool_t _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_rectangle_int_t *extents); cairo_private cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, const cairo_pattern_t *source, cairo_surface_t *surface, int source_x, int source_y, int dest_x, int dest_y, unsigned int width, unsigned int height, cairo_glyph_t *glyphs, int num_glyphs, cairo_region_t *clip_region); cairo_private cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path); cairo_private void _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_text_extents_t *fs_metrics); cairo_private void _cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_image_surface_t *surface); cairo_private void _cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_path_fixed_t *path); cairo_private void _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_surface_t *recording_surface); cairo_private cairo_int_status_t _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, unsigned long index, cairo_scaled_glyph_info_t info, cairo_scaled_glyph_t **scaled_glyph_ret); cairo_private double _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font); cairo_private void _cairo_scaled_font_map_destroy (void); /* cairo-stroke-style.c */ cairo_private void _cairo_stroke_style_init (cairo_stroke_style_t *style); cairo_private cairo_status_t _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, const cairo_stroke_style_t *other); cairo_private void _cairo_stroke_style_fini (cairo_stroke_style_t *style); cairo_private void _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy); cairo_private void _cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy); cairo_private void _cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style, const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy); cairo_private double _cairo_stroke_style_dash_period (const cairo_stroke_style_t *style); cairo_private double _cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style); cairo_private cairo_bool_t _cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double tolerance); cairo_private void _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, double tolerance, double *dash_offset, double *dashes, unsigned int *num_dashes); /* cairo-surface.c */ cairo_private cairo_status_t _cairo_surface_copy_mime_data (cairo_surface_t *dst, cairo_surface_t *src); cairo_private_no_warn cairo_int_status_t _cairo_surface_set_error (cairo_surface_t *surface, cairo_int_status_t status); cairo_private void _cairo_surface_set_resolution (cairo_surface_t *surface, double x_res, double y_res); cairo_private cairo_surface_t * _cairo_surface_create_similar_scratch (cairo_surface_t *other, cairo_content_t content, int width, int height); cairo_private cairo_surface_t * _cairo_surface_create_for_rectangle_int (cairo_surface_t *target, const cairo_rectangle_int_t *extents); cairo_private cairo_surface_t * _cairo_surface_create_similar_solid (cairo_surface_t *other, cairo_content_t content, int width, int height, const cairo_color_t *color); cairo_private void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, cairo_device_t *device, cairo_content_t content); cairo_private void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options); cairo_private cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip); cairo_private cairo_image_surface_t * _cairo_surface_map_to_image (cairo_surface_t *surface, const cairo_rectangle_int_t *extents); cairo_private cairo_int_status_t _cairo_surface_unmap_image (cairo_surface_t *surface, cairo_image_surface_t *image); cairo_private cairo_status_t _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, cairo_operator_t fill_op, const cairo_pattern_t *fill_source, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, cairo_path_fixed_t *path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const char *utf8, int utf8_len, cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, cairo_image_surface_t **image_out, void **image_extra); cairo_private void _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra); cairo_private cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface); cairo_private void _cairo_surface_attach_snapshot (cairo_surface_t *surface, cairo_surface_t *snapshot, cairo_surface_func_t detach_func); cairo_private cairo_surface_t * _cairo_surface_has_snapshot (cairo_surface_t *surface, const cairo_surface_backend_t *backend); cairo_private void _cairo_surface_detach_snapshot (cairo_surface_t *snapshot); cairo_private cairo_status_t _cairo_surface_begin_modification (cairo_surface_t *surface); cairo_private_no_warn cairo_bool_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_int_t *extents); cairo_private void _cairo_surface_set_device_scale (cairo_surface_t *surface, double sx, double sy); cairo_private cairo_bool_t _cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; cairo_private void _cairo_surface_release_device_reference (cairo_surface_t *surface); /* cairo-image-surface.c */ /* XXX: In cairo 1.2.0 we added a new %CAIRO_FORMAT_RGB16_565 but * neglected to adjust this macro. The net effect is that it's * impossible to externally create an image surface with this * format. This is perhaps a good thing since we also neglected to fix * up things like cairo_surface_write_to_png() for the new format * (-Wswitch-enum will tell you where). Is it obvious that format was * added in haste? * * The reason for the new format was to allow the xlib backend to be * used on X servers with a 565 visual. So the new format did its job * for that, even without being considered "valid" for the sake of * things like cairo_image_surface_create(). * * Since 1.2.0 we ran into the same situtation with X servers with BGR * visuals. This time we invented #cairo_internal_format_t instead, * (see it for more discussion). * * The punchline is that %CAIRO_FORMAT_VALID must not conside any * internal format to be valid. Also we need to decide if the * RGB16_565 should be moved to instead be an internal format. If so, * this macro need not change for it. (We probably will need to leave * an RGB16_565 value in the header files for the sake of code that * might have that value in it.) * * If we do decide to start fully supporting RGB16_565 as an external * format, then %CAIRO_FORMAT_VALID needs to be adjusted to include * it. But that should not happen before all necessary code is fixed * to support it (at least cairo_surface_write_to_png() and a few spots * in cairo-xlib-surface.c--again see -Wswitch-enum). */ #define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ (format) <= CAIRO_FORMAT_RGB30) /* pixman-required stride alignment in bytes. */ #define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) #define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \ ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT) #define CAIRO_CONTENT_VALID(content) ((content) && \ (((content) & ~(CAIRO_CONTENT_COLOR | \ CAIRO_CONTENT_ALPHA | \ CAIRO_CONTENT_COLOR_ALPHA))\ == 0)) cairo_private int _cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; cairo_private cairo_format_t _cairo_format_from_content (cairo_content_t content) cairo_const; cairo_private cairo_format_t _cairo_format_from_pixman_format (pixman_format_code_t pixman_format); cairo_private cairo_content_t _cairo_content_from_format (cairo_format_t format) cairo_const; cairo_private cairo_content_t _cairo_content_from_pixman_format (pixman_format_code_t pixman_format); cairo_private cairo_surface_t * _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, pixman_format_code_t pixman_format); cairo_private pixman_format_code_t _cairo_format_to_pixman_format_code (cairo_format_t format); cairo_private cairo_bool_t _pixman_format_from_masks (cairo_format_masks_t *masks, pixman_format_code_t *format_ret); cairo_private cairo_bool_t _pixman_format_to_masks (pixman_format_code_t pixman_format, cairo_format_masks_t *masks); cairo_private void _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); cairo_private void _cairo_image_reset_static_data (void); cairo_private cairo_surface_t * _cairo_image_surface_create_with_pixman_format (unsigned char *data, pixman_format_code_t pixman_format, int width, int height, int stride); cairo_private cairo_surface_t * _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height); cairo_private void _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); cairo_private cairo_image_surface_t * _cairo_image_surface_coerce (cairo_image_surface_t *surface); cairo_private cairo_image_surface_t * _cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, cairo_format_t format); cairo_private cairo_image_transparency_t _cairo_image_analyze_transparency (cairo_image_surface_t *image); cairo_private cairo_image_color_t _cairo_image_analyze_color (cairo_image_surface_t *image); /* cairo-pen.c */ cairo_private int _cairo_pen_vertices_needed (double tolerance, double radius, const cairo_matrix_t *matrix); cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, double tolerance, const cairo_matrix_t *ctm); cairo_private void _cairo_pen_init_empty (cairo_pen_t *pen); cairo_private cairo_status_t _cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other); cairo_private void _cairo_pen_fini (cairo_pen_t *pen); cairo_private cairo_status_t _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points); cairo_private int _cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope); cairo_private int _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope); cairo_private void _cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, const cairo_slope_t *in, const cairo_slope_t *out, int *start, int *stop); cairo_private void _cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen, const cairo_slope_t *in, const cairo_slope_t *out, int *start, int *stop); /* cairo-polygon.c */ cairo_private void _cairo_polygon_init (cairo_polygon_t *polygon, const cairo_box_t *boxes, int num_boxes); cairo_private void _cairo_polygon_init_with_clip (cairo_polygon_t *polygon, const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_polygon_init_boxes (cairo_polygon_t *polygon, const cairo_boxes_t *boxes); cairo_private cairo_status_t _cairo_polygon_init_box_array (cairo_polygon_t *polygon, cairo_box_t *boxes, int num_boxes); cairo_private void _cairo_polygon_limit (cairo_polygon_t *polygon, const cairo_box_t *limits, int num_limits); cairo_private void _cairo_polygon_limit_to_clip (cairo_polygon_t *polygon, const cairo_clip_t *clip); cairo_private void _cairo_polygon_fini (cairo_polygon_t *polygon); cairo_private cairo_status_t _cairo_polygon_add_line (cairo_polygon_t *polygon, const cairo_line_t *line, int top, int bottom, int dir); cairo_private cairo_status_t _cairo_polygon_add_external_edge (void *polygon, const cairo_point_t *p1, const cairo_point_t *p2); cairo_private cairo_status_t _cairo_polygon_add_contour (cairo_polygon_t *polygon, const cairo_contour_t *contour); cairo_private void _cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy); cairo_private cairo_status_t _cairo_polygon_reduce (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, cairo_polygon_t *b, int winding_b); cairo_private cairo_status_t _cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon, cairo_fill_rule_t *winding, cairo_box_t *boxes, int num_boxes); static inline cairo_bool_t _cairo_polygon_is_empty (const cairo_polygon_t *polygon) { return polygon->num_edges == 0 || polygon->extents.p2.x <= polygon->extents.p1.x; } #define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status /* cairo-spline.c */ cairo_private cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d); cairo_private cairo_status_t _cairo_spline_decompose (cairo_spline_t *spline, double tolerance); cairo_private cairo_status_t _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3); /* cairo-matrix.c */ cairo_private void _cairo_matrix_get_affine (const cairo_matrix_t *matrix, double *xx, double *yx, double *xy, double *yy, double *x0, double *y0); cairo_private void _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, double *x1, double *y1, double *x2, double *y2, cairo_bool_t *is_tight); cairo_private void _cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, cairo_box_t *bbox, cairo_bool_t *is_tight); cairo_private cairo_bool_t _cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure; cairo_private cairo_bool_t _cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure; cairo_private double _cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure; cairo_private cairo_status_t _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, double *sx, double *sy, int x_major); static inline cairo_bool_t _cairo_matrix_is_identity (const cairo_matrix_t *matrix) { return (matrix->xx == 1.0 && matrix->yx == 0.0 && matrix->xy == 0.0 && matrix->yy == 1.0 && matrix->x0 == 0.0 && matrix->y0 == 0.0); } static inline cairo_bool_t _cairo_matrix_is_translation (const cairo_matrix_t *matrix) { return (matrix->xx == 1.0 && matrix->yx == 0.0 && matrix->xy == 0.0 && matrix->yy == 1.0); } static inline cairo_bool_t _cairo_matrix_is_scale (const cairo_matrix_t *matrix) { return matrix->yx == 0.0 && matrix->xy == 0.0; } cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, int *itx, int *ity); cairo_private cairo_bool_t _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix); cairo_private cairo_bool_t _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; cairo_private double _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, double radius) cairo_pure; cairo_private cairo_bool_t _cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, cairo_filter_t filter, int *out_x_offset, int *out_y_offset); cairo_private cairo_status_t _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, cairo_filter_t filter, double xc, double yc, pixman_transform_t *out_transform, int *out_x_offset, int *out_y_offset); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_fill_rule_t fill_rule, cairo_boxes_t *out); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes); cairo_private void _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, cairo_trapezoid_t *src_traps, int num_traps, double tx, double ty, double sx, double sy); #if CAIRO_HAS_DRM_SURFACE cairo_private void _cairo_drm_device_reset_static_data (void); #endif cairo_private void _cairo_clip_reset_static_data (void); cairo_private void _cairo_pattern_reset_static_data (void); /* cairo-unicode.c */ cairo_private int _cairo_utf8_get_char_validated (const char *p, uint32_t *unicode); cairo_private cairo_status_t _cairo_utf8_to_ucs4 (const char *str, int len, uint32_t **result, int *items_written); cairo_private int _cairo_ucs4_to_utf8 (uint32_t unicode, char *utf8); #if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS # define CAIRO_HAS_UTF8_TO_UTF16 1 #endif #if CAIRO_HAS_UTF8_TO_UTF16 cairo_private cairo_status_t _cairo_utf8_to_utf16 (const char *str, int len, uint16_t **result, int *items_written); #endif cairo_private void _cairo_matrix_multiply (cairo_matrix_t *r, const cairo_matrix_t *a, const cairo_matrix_t *b); /* cairo-observer.c */ cairo_private void _cairo_observers_notify (cairo_list_t *observers, void *arg); /* Avoid unnecessary PLT entries. */ slim_hidden_proto (cairo_clip_preserve); slim_hidden_proto (cairo_close_path); slim_hidden_proto (cairo_create); slim_hidden_proto (cairo_curve_to); slim_hidden_proto (cairo_destroy); slim_hidden_proto (cairo_fill_preserve); slim_hidden_proto (cairo_font_face_destroy); slim_hidden_proto (cairo_font_face_get_user_data); slim_hidden_proto_no_warn (cairo_font_face_reference); slim_hidden_proto (cairo_font_face_set_user_data); slim_hidden_proto (cairo_font_options_equal); slim_hidden_proto (cairo_font_options_hash); slim_hidden_proto (cairo_font_options_merge); slim_hidden_proto (cairo_font_options_set_antialias); slim_hidden_proto (cairo_font_options_set_hint_metrics); slim_hidden_proto (cairo_font_options_set_hint_style); slim_hidden_proto (cairo_font_options_set_subpixel_order); slim_hidden_proto (cairo_font_options_status); slim_hidden_proto (cairo_format_stride_for_width); slim_hidden_proto (cairo_get_current_point); slim_hidden_proto (cairo_get_line_width); slim_hidden_proto (cairo_get_matrix); slim_hidden_proto (cairo_get_scaled_font); slim_hidden_proto (cairo_get_target); slim_hidden_proto (cairo_get_tolerance); slim_hidden_proto (cairo_glyph_allocate); slim_hidden_proto (cairo_glyph_free); slim_hidden_proto (cairo_image_surface_create); slim_hidden_proto (cairo_image_surface_create_for_data); slim_hidden_proto (cairo_image_surface_get_data); slim_hidden_proto (cairo_image_surface_get_format); slim_hidden_proto (cairo_image_surface_get_height); slim_hidden_proto (cairo_image_surface_get_stride); slim_hidden_proto (cairo_image_surface_get_width); slim_hidden_proto (cairo_line_to); slim_hidden_proto (cairo_mask); slim_hidden_proto (cairo_matrix_init); slim_hidden_proto (cairo_matrix_init_identity); slim_hidden_proto (cairo_matrix_init_rotate); slim_hidden_proto (cairo_matrix_init_scale); slim_hidden_proto (cairo_matrix_init_translate); slim_hidden_proto (cairo_matrix_invert); slim_hidden_proto (cairo_matrix_multiply); slim_hidden_proto (cairo_matrix_scale); slim_hidden_proto (cairo_matrix_transform_distance); slim_hidden_proto (cairo_matrix_transform_point); slim_hidden_proto (cairo_matrix_translate); slim_hidden_proto (cairo_move_to); slim_hidden_proto (cairo_new_path); slim_hidden_proto (cairo_paint); slim_hidden_proto (cairo_pattern_add_color_stop_rgba); slim_hidden_proto (cairo_pattern_create_for_surface); slim_hidden_proto (cairo_pattern_create_rgb); slim_hidden_proto (cairo_pattern_create_rgba); slim_hidden_proto (cairo_pattern_destroy); slim_hidden_proto (cairo_pattern_get_extend); slim_hidden_proto (cairo_mesh_pattern_curve_to); slim_hidden_proto (cairo_mesh_pattern_get_control_point); slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba); slim_hidden_proto (cairo_mesh_pattern_get_patch_count); slim_hidden_proto (cairo_mesh_pattern_get_path); slim_hidden_proto (cairo_mesh_pattern_line_to); slim_hidden_proto (cairo_mesh_pattern_move_to); slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba); slim_hidden_proto_no_warn (cairo_pattern_reference); slim_hidden_proto (cairo_pattern_set_matrix); slim_hidden_proto (cairo_pop_group); slim_hidden_proto (cairo_push_group_with_content); slim_hidden_proto_no_warn (cairo_path_destroy); slim_hidden_proto (cairo_recording_surface_create); slim_hidden_proto (cairo_rel_line_to); slim_hidden_proto (cairo_restore); slim_hidden_proto (cairo_save); slim_hidden_proto (cairo_scale); slim_hidden_proto (cairo_scaled_font_create); slim_hidden_proto (cairo_scaled_font_destroy); slim_hidden_proto (cairo_scaled_font_extents); slim_hidden_proto (cairo_scaled_font_get_ctm); slim_hidden_proto (cairo_scaled_font_get_font_face); slim_hidden_proto (cairo_scaled_font_get_font_matrix); slim_hidden_proto (cairo_scaled_font_get_font_options); slim_hidden_proto (cairo_scaled_font_glyph_extents); slim_hidden_proto_no_warn (cairo_scaled_font_reference); slim_hidden_proto (cairo_scaled_font_status); slim_hidden_proto (cairo_scaled_font_get_user_data); slim_hidden_proto (cairo_scaled_font_set_user_data); slim_hidden_proto (cairo_scaled_font_text_to_glyphs); slim_hidden_proto (cairo_set_font_matrix); slim_hidden_proto (cairo_set_font_options); slim_hidden_proto (cairo_set_font_size); slim_hidden_proto (cairo_set_line_cap); slim_hidden_proto (cairo_set_line_join); slim_hidden_proto (cairo_set_line_width); slim_hidden_proto (cairo_set_matrix); slim_hidden_proto (cairo_set_operator); slim_hidden_proto (cairo_set_source); slim_hidden_proto (cairo_set_source_rgb); slim_hidden_proto (cairo_set_source_surface); slim_hidden_proto (cairo_set_tolerance); slim_hidden_proto (cairo_status); slim_hidden_proto (cairo_stroke); slim_hidden_proto (cairo_stroke_preserve); slim_hidden_proto (cairo_surface_copy_page); slim_hidden_proto (cairo_surface_create_similar_image); slim_hidden_proto (cairo_surface_destroy); slim_hidden_proto (cairo_surface_finish); slim_hidden_proto (cairo_surface_flush); slim_hidden_proto (cairo_surface_get_device_offset); slim_hidden_proto (cairo_surface_get_font_options); slim_hidden_proto (cairo_surface_get_mime_data); slim_hidden_proto (cairo_surface_has_show_text_glyphs); slim_hidden_proto (cairo_surface_mark_dirty); slim_hidden_proto (cairo_surface_mark_dirty_rectangle); slim_hidden_proto_no_warn (cairo_surface_reference); slim_hidden_proto (cairo_surface_set_device_offset); slim_hidden_proto (cairo_surface_set_fallback_resolution); slim_hidden_proto (cairo_surface_set_mime_data); slim_hidden_proto (cairo_surface_show_page); slim_hidden_proto (cairo_surface_status); slim_hidden_proto (cairo_surface_supports_mime_type); slim_hidden_proto (cairo_text_cluster_allocate); slim_hidden_proto (cairo_text_cluster_free); slim_hidden_proto (cairo_toy_font_face_create); slim_hidden_proto (cairo_toy_font_face_get_slant); slim_hidden_proto (cairo_toy_font_face_get_weight); slim_hidden_proto (cairo_translate); slim_hidden_proto (cairo_transform); slim_hidden_proto (cairo_user_font_face_create); slim_hidden_proto (cairo_user_font_face_set_init_func); slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); slim_hidden_proto (cairo_device_to_user); slim_hidden_proto (cairo_user_to_device); slim_hidden_proto (cairo_user_to_device_distance); slim_hidden_proto (cairo_version_string); slim_hidden_proto (cairo_region_create); slim_hidden_proto (cairo_region_create_rectangle); slim_hidden_proto (cairo_region_create_rectangles); slim_hidden_proto (cairo_region_copy); slim_hidden_proto (cairo_region_reference); slim_hidden_proto (cairo_region_destroy); slim_hidden_proto (cairo_region_equal); slim_hidden_proto (cairo_region_status); slim_hidden_proto (cairo_region_get_extents); slim_hidden_proto (cairo_region_num_rectangles); slim_hidden_proto (cairo_region_get_rectangle); slim_hidden_proto (cairo_region_is_empty); slim_hidden_proto (cairo_region_contains_rectangle); slim_hidden_proto (cairo_region_contains_point); slim_hidden_proto (cairo_region_translate); slim_hidden_proto (cairo_region_subtract); slim_hidden_proto (cairo_region_subtract_rectangle); slim_hidden_proto (cairo_region_intersect); slim_hidden_proto (cairo_region_intersect_rectangle); slim_hidden_proto (cairo_region_union); slim_hidden_proto (cairo_region_union_rectangle); slim_hidden_proto (cairo_region_xor); slim_hidden_proto (cairo_region_xor_rectangle); #if CAIRO_HAS_PNG_FUNCTIONS slim_hidden_proto (cairo_surface_write_to_png_stream); #endif cairo_private_no_warn cairo_filter_t _cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, double *pad_out); CAIRO_END_DECLS #include "cairo-mutex-private.h" #include "cairo-fixed-private.h" #include "cairo-wideint-private.h" #include "cairo-malloc-private.h" #include "cairo-hash-private.h" #if HAVE_VALGRIND #include #define VG(x) x cairo_private void _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); #else #define VG(x) #define _cairo_debug_check_image_surface_is_defined(X) #endif cairo_private void _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); cairo_private void _cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon); cairo_private void _cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps); cairo_private void _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip); #if 0 #define TRACE(x) fprintf (stderr, "%s: ", __FILE__), fprintf x #define TRACE_(x) x #else #define TRACE(x) #define TRACE_(x) #endif #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/000077500000000000000000000000001271037650300230625ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-bo.c000066400000000000000000000067021271037650300255060ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-ioctl-private.h" #include "cairo-error-private.h" #include #include #define ERR_DEBUG(x) x struct drm_gem_close { /** Handle of the object to be closed. */ uint32_t handle; uint32_t pad; }; struct drm_gem_flink { /** Handle for the object being named */ uint32_t handle; /** Returned global name */ uint32_t name; }; struct drm_gem_open { /** Name of object being opened */ uint32_t name; /** Returned handle for the object */ uint32_t handle; /** Returned size of the object */ uint64_t size; }; #define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) #define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) #define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) cairo_status_t _cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo, uint32_t name) { struct drm_gem_open open; int ret; open.name = name; open.handle = 0; open.size = 0; do { ret = ioctl (dev->fd, DRM_IOCTL_GEM_OPEN, &open); } while (ret == -1 && errno == EINTR); if (ret == -1) { ERR_DEBUG((fprintf (stderr, "Failed to open bo for name %d: %s\n", name, strerror (errno)))); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } bo->name = name; bo->size = open.size; bo->handle = open.handle; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_drm_bo_flink (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo) { struct drm_gem_flink flink; int ret; memset (&flink, 0, sizeof (flink)); flink.handle = bo->handle; ret = ioctl (dev->fd, DRM_IOCTL_GEM_FLINK, &flink); if (ret == -1) { ERR_DEBUG((fprintf (stderr, "Failed to flink bo: %s\n", strerror (errno)))); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } bo->name = flink.name; return CAIRO_STATUS_SUCCESS; } void _cairo_drm_bo_close (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo) { struct drm_gem_close close; int ret; close.handle = bo->handle; do { ret = ioctl (dev->fd, DRM_IOCTL_GEM_CLOSE, &close); } while (ret == -1 && errno == EINTR); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-gallium-surface.c000066400000000000000000000536621271037650300301750ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * Copyright © 2009 Eric Anholt * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include #include #include #include #include #include #include typedef struct _gallium_surface gallium_surface_t; typedef struct _gallium_device gallium_device_t; struct _gallium_device { cairo_drm_device_t drm; void *dlhandle; struct drm_api *api; struct pipe_screen *screen; struct pipe_context *pipe; int max_size; }; struct _gallium_surface { cairo_drm_surface_t drm; enum pipe_format pipe_format; struct pipe_resource *texture; struct pipe_transfer *map_transfer; cairo_surface_t *fallback; }; static cairo_surface_t * gallium_surface_create_internal (gallium_device_t *device, enum pipe_format format, int width, int height); static inline gallium_device_t * gallium_device (gallium_surface_t *surface) { return (gallium_device_t *) surface->drm.base.device; } static cairo_format_t _cairo_format_from_pipe_format (enum pipe_format format) { switch ((int) format) { case PIPE_FORMAT_A8_UNORM: return CAIRO_FORMAT_A8; case PIPE_FORMAT_A8R8G8B8_UNORM: return CAIRO_FORMAT_ARGB32; default: return CAIRO_FORMAT_INVALID; } } static enum pipe_format pipe_format_from_format (cairo_format_t format) { switch ((int) format) { case CAIRO_FORMAT_A8: return PIPE_FORMAT_A8_UNORM; case CAIRO_FORMAT_ARGB32: return PIPE_FORMAT_A8R8G8B8_UNORM; default: return (enum pipe_format) -1; } } static enum pipe_format pipe_format_from_content (cairo_content_t content) { if (content == CAIRO_CONTENT_ALPHA) return PIPE_FORMAT_A8_UNORM; else return PIPE_FORMAT_A8R8G8B8_UNORM; } static cairo_bool_t format_is_supported_destination (gallium_device_t *device, enum pipe_format format) { if (format == (enum pipe_format) -1) return FALSE; return device->screen->is_format_supported (device->screen, format, 0, PIPE_BIND_RENDER_TARGET, 0); } #if 0 static cairo_bool_t format_is_supported_source (gallium_device_t *device, enum pipe_format format) { return device->screen->is_format_supported (device->screen, format, 0, PIPE_BIND_SAMPLER_VIEW, 0); } #endif static cairo_surface_t * gallium_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { gallium_surface_t *other = abstract_src; gallium_device_t *device = gallium_device (other); enum pipe_format pipe_format; cairo_surface_t *surface = NULL; cairo_status_t status; status = cairo_device_acquire (&device->drm.base); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (MAX (width, height) > device->max_size) goto RELEASE; if (content == other->drm.base.content) pipe_format = other->pipe_format; else pipe_format = pipe_format_from_content (content); if (! format_is_supported_destination (device, pipe_format)) goto RELEASE; surface = gallium_surface_create_internal (device, pipe_format, width, height); RELEASE: cairo_device_release (&device->drm.base); return surface; } static cairo_status_t gallium_surface_finish (void *abstract_surface) { gallium_surface_t *surface = abstract_surface; gallium_device_t *device = gallium_device (surface); cairo_status_t status; status = cairo_device_acquire (&device->drm.base); if (likely (status == CAIRO_STATUS_SUCCESS)) { pipe_resource_reference (&surface->texture, NULL); cairo_device_release (&device->drm.base); } return _cairo_drm_surface_finish (&surface->drm); } static cairo_surface_t * gallium_surface_map_to_image (gallium_surface_t *surface) { gallium_device_t *device = gallium_device (surface); cairo_status_t status; void *ptr = NULL; status = cairo_device_acquire (&device->drm.base); if (unlikely (status)) return _cairo_surface_create_in_error (status); surface->map_transfer = pipe_get_transfer (device->pipe, surface->texture, 0, 0, 0, PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_READ_WRITE, 0, 0, surface->drm.width, surface->drm.height); if (likely (surface->map_transfer != NULL)) ptr = device->pipe->transfer_map (device->pipe, surface->map_transfer); cairo_device_release (&device->drm.base); if (unlikely (ptr == NULL)) { if (surface->map_transfer != NULL) { device->pipe->transfer_destroy (device->pipe, surface->map_transfer); surface->map_transfer = NULL; } return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } return cairo_image_surface_create_for_data (ptr, surface->drm.format, surface->drm.width, surface->drm.height, surface->map_transfer->stride); } static cairo_status_t gallium_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { gallium_surface_t *surface = abstract_surface; gallium_device_t *device = gallium_device (surface); cairo_format_t format; cairo_surface_t *image; cairo_status_t status; struct pipe_transfer *transfer; void *ptr; if (surface->fallback != NULL) { *image_out = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } if (unlikely (surface->drm.width == 0 || surface->drm.height == 0)) { image = cairo_image_surface_create (surface->drm.format, 0, 0); if (unlikely (image->status)) return image->status; *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } format = _cairo_format_from_pipe_format (surface->pipe_format); if (format == CAIRO_FORMAT_INVALID) return CAIRO_INT_STATUS_UNSUPPORTED; status = cairo_device_acquire (&device->drm.base); if (unlikely (status)) return status; transfer = pipe_get_transfer (device->pipe, surface->texture, 0, 0, 0, PIPE_TRANSFER_READ, 0, 0, surface->drm.width, surface->drm.height); ptr = device->pipe->transfer_map (device->pipe, transfer); cairo_device_release (&device->drm.base); image = cairo_image_surface_create_for_data (ptr, format, surface->drm.width, surface->drm.height, surface->drm.stride); if (unlikely (image->status)) return image->status; *image_out = (cairo_image_surface_t *) image; *image_extra = transfer; return CAIRO_STATUS_SUCCESS; } static void gallium_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); if (image_extra != NULL) { gallium_device_t *device = gallium_device (abstract_surface); device->pipe->transfer_unmap (device->pipe, image_extra); device->pipe->transfer_destroy (device->pipe, image_extra); } } static cairo_status_t gallium_surface_flush (void *abstract_surface, unsigned flags) { gallium_surface_t *surface = abstract_surface; gallium_device_t *device = gallium_device (surface); cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->fallback == NULL) { device->pipe->flush (device->pipe, PIPE_FLUSH_RENDER_CACHE, NULL); return CAIRO_STATUS_SUCCESS; } /* kill any outstanding maps */ cairo_surface_finish (surface->fallback); status = cairo_device_acquire (&device->drm.base); if (likely (status == CAIRO_STATUS_SUCCESS)) { device->pipe->transfer_unmap (device->pipe, surface->map_transfer); device->pipe->transfer_destroy (device->pipe, surface->map_transfer); surface->map_transfer = NULL; cairo_device_release (&device->drm.base); } status = cairo_surface_status (surface->fallback); cairo_surface_destroy (surface->fallback); surface->fallback = NULL; return status; } static cairo_int_status_t gallium_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip) { gallium_surface_t *surface = abstract_surface; if (surface->fallback == NULL) { /* XXX insert magic */ surface->fallback = gallium_surface_map_to_image (surface); } return _cairo_surface_paint (surface->fallback, op, source, clip); } static cairo_int_status_t gallium_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_clip_t *clip) { gallium_surface_t *surface = abstract_surface; if (surface->fallback == NULL) { /* XXX insert magic */ surface->fallback = gallium_surface_map_to_image (surface); } return _cairo_surface_mask (surface->fallback, op, source, mask, clip); } static cairo_int_status_t gallium_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { gallium_surface_t *surface = abstract_surface; if (surface->fallback == NULL) { /* XXX insert magic */ surface->fallback = gallium_surface_map_to_image (surface); } return _cairo_surface_stroke (surface->fallback, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t gallium_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { gallium_surface_t *surface = abstract_surface; if (surface->fallback == NULL) { /* XXX insert magic */ surface->fallback = gallium_surface_map_to_image (surface); } return _cairo_surface_fill (surface->fallback, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t gallium_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { gallium_surface_t *surface = abstract_surface; *num_remaining = 0; if (surface->fallback == NULL) { /* XXX insert magic */ surface->fallback = gallium_surface_map_to_image (surface); } return _cairo_surface_show_text_glyphs (surface->fallback, op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); } static const cairo_surface_backend_t gallium_surface_backend = { CAIRO_SURFACE_TYPE_DRM, _cairo_default_context_create, gallium_surface_create_similar, gallium_surface_finish, NULL, gallium_surface_acquire_source_image, gallium_surface_release_source_image, NULL, //gallium_surface_acquire_dest_image, NULL, //gallium_surface_release_dest_image, NULL, //gallium_surface_clone_similar, NULL, //gallium_surface_composite, NULL, //gallium_surface_fill_rectangles, NULL, //gallium_surface_composite_trapezoids, NULL, //gallium_surface_create_span_renderer, NULL, //gallium_surface_check_span_renderer, NULL, /* copy_page */ NULL, /* show_page */ _cairo_drm_surface_get_extents, NULL, /* old_show_glyphs */ _cairo_drm_surface_get_font_options, gallium_surface_flush, NULL, /* mark_dirty_rectangle */ NULL, //gallium_surface_scaled_font_fini, NULL, //gallium_surface_scaled_glyph_fini, gallium_surface_paint, gallium_surface_mask, gallium_surface_stroke, gallium_surface_fill, gallium_surface_glyphs, NULL, /* snapshot */ NULL, /* is_similar */ NULL, /* reset */ }; static int gallium_format_stride_for_width (enum pipe_format format, int width) { int stride; stride = 1024; /* XXX fugly */ while (stride < width) stride *= 2; if (format == PIPE_FORMAT_A8R8G8B8_UNORM) stride *= 4; return stride; } static cairo_drm_bo_t * _gallium_fake_bo_create (uint32_t size, uint32_t name) { cairo_drm_bo_t *bo; /* XXX integrate with winsys handle */ bo = malloc (sizeof (cairo_drm_bo_t)); CAIRO_REFERENCE_COUNT_INIT (&bo->ref_count, 1); bo->name = name; bo->handle = 0; bo->size = size; return bo; } static void _gallium_fake_bo_release (void *dev, void *bo) { free (bo); } static cairo_surface_t * gallium_surface_create_internal (gallium_device_t *device, enum pipe_format pipe_format, int width, int height) { gallium_surface_t *surface; struct pipe_resource template; cairo_status_t status; cairo_format_t format; int stride, size; surface = malloc (sizeof (gallium_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); format = _cairo_format_from_pipe_format (pipe_format); _cairo_surface_init (&surface->drm.base, &gallium_surface_backend, &device->drm.base, _cairo_content_from_format (format)); _cairo_drm_surface_init (&surface->drm, format, width, height); stride = gallium_format_stride_for_width (pipe_format, width); size = stride * height; surface->drm.stride = stride; surface->drm.bo = _gallium_fake_bo_create (size, 0); memset(&template, 0, sizeof(template)); template.target = PIPE_TEXTURE_2D; template.format = pipe_format; template.width0 = width; template.height0 = height; template.depth0 = 1; template.last_level = 0; template.bind = PIPE_BIND_RENDER_TARGET; surface->texture = device->screen->resource_create (device->screen, &template); if (unlikely (surface->texture == NULL)) { status = _cairo_drm_surface_finish (&surface->drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } surface->pipe_format = pipe_format; surface->texture = NULL; return &surface->drm.base; } static cairo_surface_t * gallium_surface_create (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height) { gallium_device_t *device = (gallium_device_t *) base_dev; cairo_surface_t *surface; enum pipe_format pipe_format; cairo_status_t status; status = cairo_device_acquire (&device->drm.base); if (MAX (width, height) > device->max_size) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); goto RELEASE; } pipe_format = pipe_format_from_format (format); if (! format_is_supported_destination (device, pipe_format)) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); goto RELEASE; } surface = gallium_surface_create_internal (device, pipe_format, width, height); RELEASE: cairo_device_release (&device->drm.base); return surface; } #if 0 static cairo_surface_t * gallium_surface_create_for_name (cairo_drm_device_t *base_dev, unsigned int name, cairo_format_t format, int width, int height, int stride) { gallium_device_t *device; gallium_surface_t *surface; cairo_status_t status; cairo_content_t content; surface = malloc (sizeof (gallium_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); case CAIRO_FORMAT_A8: surface->pipe_format = PIPE_FORMAT_A8_UNORM; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: surface->pipe_format = PIPE_FORMAT_A8R8G8B8_UNORM; break; } status = cairo_device_acquire (&device->drm.base); if (MAX (width, height) > device->max_size) { cairo_device_release (&device->drm.base); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } if (! format_is_supported_destination (device, surface->pipe_format)) { cairo_device_release (&device->drm.base); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } content = _cairo_content_from_format (format); _cairo_surface_init (&surface->drm.base, &gallium_surface_backend, content); _cairo_drm_surface_init (&surface->drm, base_dev); surface->drm.bo = _gallium_fake_bo_create (height * stride, name); surface->drm.width = width; surface->drm.height = height; surface->drm.stride = stride; #if 0 /* XXX screen->create_from_handle */ surface->buffer = device->api->buffer_from_handle (device->api, device->screen, "cairo-gallium alien", name); if (unlikely (surface->buffer == NULL)) { status = _cairo_drm_surface_finish (&surface->drm); cairo_device_release (&device->drm.base); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } #endif surface->texture = NULL; surface->fallback = NULL; cairo_device_release (&device->drm.base); return &surface->drm.base; } static cairo_int_status_t gallium_surface_flink (void *abstract_surface) { gallium_surface_t *surface = abstract_surface; gallium_device_t *device; cairo_status_t status = CAIRO_STATUS_SUCCESS; status = cairo_device_acquire (&device->drm.base); if (! device->api->global_handle_from_buffer (device->api, device->screen, surface->buffer, &surface->drm.bo->name)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); } cairo_device_release (&device->drm.base); return status; } #endif static void gallium_device_destroy (void *abstract_device) { gallium_device_t *device = abstract_device; device->pipe->destroy (device->pipe); device->screen->destroy (device->screen); device->api->destroy (device->api); dlclose (device->dlhandle); free (device); } cairo_drm_device_t * _cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id) { gallium_device_t *device; cairo_status_t status; void *handle; const char *libdir; char buf[4096]; struct drm_api *(*ctor) (void); /* XXX need search path + probe */ libdir = getenv ("CAIRO_GALLIUM_LIBDIR"); if (libdir == NULL) libdir = "/usr/lib/dri"; buf[snprintf (buf, sizeof (buf)-1, "%s/i915_dri.so", libdir)] = '\0'; handle = dlopen (buf, RTLD_LAZY); if (handle == NULL) return NULL; ctor = dlsym (handle, "drm_api_create"); if (ctor == NULL) { dlclose (handle); return NULL; } device = malloc (sizeof (gallium_device_t)); if (device == NULL) { dlclose (handle); return _cairo_drm_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } device->dlhandle = handle; device->drm.surface.create = gallium_surface_create; device->drm.surface.create_for_name = NULL; //device->drm.surface.create_for_name = gallium_surface_create_for_name; device->drm.surface.enable_scan_out = NULL; //device->drm.surface.flink = gallium_surface_flink; device->drm.surface.flink = NULL; device->drm.device.flush = NULL; device->drm.device.throttle = NULL; device->drm.device.destroy = gallium_device_destroy; device->drm.bo.release = _gallium_fake_bo_release; device->api = ctor (); if (device->api == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } device->screen = device->api->create_screen (device->api, fd, NULL); if (device->screen == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_API; } device->max_size = 1 << device->screen->get_param (device->screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS); device->pipe = device->screen->context_create (device->screen, device); if (device->pipe == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_SCREEN; } return _cairo_drm_device_init (&device->drm, fd, dev, 0, 0, device->max_size); CLEANUP_SCREEN: device->screen->destroy (device->screen); CLEANUP_API: device->api->destroy (device->api); CLEANUP: free (device); dlclose (handle); return _cairo_drm_device_create_in_error (status); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i915-glyphs.c000066400000000000000000000366211271037650300271040ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-drm-i915-private.h" #include "cairo-error-private.h" #include "cairo-rtree-private.h" static void i915_emit_glyph_rectangle_zero (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { float *v; /* Each vertex is: * 2 vertex coordinates */ v = i915_add_rectangle (device); *v++ = x2; *v++ = y2; *v++ = x1; *v++ = y2; *v++ = x1; *v++ = y1; } static void i915_emit_glyph_rectangle_constant (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { float *v; /* Each vertex is: * 2 vertex coordinates * 2 glyph texture coordinates */ v = i915_add_rectangle (device); /* bottom right */ *v++ = x2; *v++ = y2; *v++ = glyph->texcoord[0]; /* bottom left */ *v++ = x1; *v++ = y2; *v++ = glyph->texcoord[1]; /* top left */ *v++ = x1; *v++ = y1; *v++ = glyph->texcoord[2]; } static void i915_emit_glyph_rectangle_general (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { double s, t; float *v; /* Each vertex is: * 2 vertex coordinates * [0-2] source texture coordinates * 2 glyph texture coordinates */ v = i915_add_rectangle (device); /* bottom right */ *v++ = x2; *v++ = y2; s = x2, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[0]; /* bottom left */ *v++ = x1; *v++ = y2; s = x1, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[1]; /* top left */ *v++ = x1; *v++ = y1; s = x1, t = y2; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } *v++ = glyph->texcoord[2]; } typedef void (*i915_emit_glyph_rectangle_func_t) (i915_device_t *device, i915_shader_t *shader, int x1, int y1, int x2, int y2, intel_glyph_t *glyph); static cairo_status_t i915_surface_mask_internal (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, i915_surface_t *mask, cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { i915_device_t *device; i915_shader_t shader; cairo_region_t *clip_region = NULL; cairo_status_t status; i915_shader_init (&shader, dst, op, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, source, &extents->bounded); if (unlikely (status)) return status; shader.mask.type.vertex = VS_TEXTURE_16; shader.mask.type.pattern = PATTERN_TEXTURE; shader.mask.type.fragment = FS_TEXTURE; shader.mask.base.content = mask->intel.drm.base.content; shader.mask.base.texfmt = TEXCOORDFMT_2D_16; shader.mask.base.n_samplers = 1; shader.mask.base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); shader.mask.base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_translate (&shader.mask.base.matrix, -extents->bounded.x, -extents->bounded.y); cairo_matrix_scale (&shader.mask.base.matrix, 1. / mask->intel.drm.width, 1. / mask->intel.drm.height); shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo)); shader.mask.base.offset[0] = 0; shader.mask.base.map[0] = mask->map0; shader.mask.base.map[1] = mask->map1; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i915_shader_set_clip (&shader, clip); } status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SHADER; device = i915_device (dst); status = i915_shader_commit (&shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; if (clip_region != NULL) { unsigned int n, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (n = 0; n < num_rectangles; n++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, n, &rect); shader.add_rectangle (&shader, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); } } else { shader.add_rectangle (&shader, extents->bounded.x, extents->bounded.y, extents->bounded.x + extents->bounded.width, extents->bounded.y + extents->bounded.height); } if (! extents->is_bounded) status = i915_fixup_unbounded (dst, extents, clip); CLEANUP_DEVICE: cairo_device_release (&device->intel.base.base); CLEANUP_SHADER: i915_shader_fini (&shader); return status; } cairo_int_status_t i915_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { i915_surface_t *surface = abstract_surface; i915_surface_t *mask = NULL; i915_device_t *device; i915_shader_t shader; cairo_composite_rectangles_t extents; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_bool_t overlap; cairo_region_t *clip_region = NULL; intel_bo_t *last_bo = NULL; i915_emit_glyph_rectangle_func_t emit_func; cairo_scaled_glyph_t *glyph_cache[64]; cairo_status_t status; int mask_x = 0, mask_y = 0; int i = 0; *num_remaining = 0; status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface->intel.drm.width, surface->intel.drm.height, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (_cairo_clip_contains_rectangle (clip, &extents.mask)) clip = NULL; if (clip != NULL && extents.is_bounded) { clip = _cairo_clip_init_copy (&local_clip, clip); status = _cairo_clip_rectangle (clip, &extents.bounded); if (unlikely (status)) return status; have_clip = TRUE; } if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (unlikely (_cairo_status_is_error (status) || status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } } if (i915_surface_needs_tiling (surface)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } if (overlap || ! extents.is_bounded) { cairo_format_t format; format = CAIRO_FORMAT_A8; if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) format = CAIRO_FORMAT_ARGB32; mask = (i915_surface_t *) i915_surface_create_internal (&i915_device (surface)->intel.base, format, extents.bounded.width, extents.bounded.height, I915_TILING_DEFAULT, TRUE); if (unlikely (mask->intel.drm.base.status)) return mask->intel.drm.base.status; status = i915_surface_clear (mask); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_white.base, &extents.bounded); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } mask_x = -extents.bounded.x; mask_y = -extents.bounded.y; } else { i915_shader_init (&shader, surface, op, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, source, &extents.bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i915_shader_set_clip (&shader, clip); } } shader.mask.type.fragment = FS_TEXTURE; shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ shader.mask.base.texfmt = TEXCOORDFMT_2D_16; shader.mask.base.n_samplers = 1; shader.mask.base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); shader.mask.base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); switch (shader.source.type.vertex) { case VS_ZERO: emit_func = i915_emit_glyph_rectangle_zero; break; case VS_CONSTANT: emit_func = i915_emit_glyph_rectangle_constant; break; default: case VS_LINEAR: case VS_TEXTURE: case VS_TEXTURE_16: emit_func = i915_emit_glyph_rectangle_general; break; } status = cairo_device_acquire (surface->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SHADER; device = i915_device (surface); _cairo_scaled_font_freeze_cache (scaled_font); if (scaled_font->surface_private == NULL) { scaled_font->surface_private = device; scaled_font->surface_backend = surface->intel.drm.base.backend; cairo_list_add (&scaled_font->link, &device->intel.fonts); } memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; int x, y, x1, x2, y1, y2; int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); intel_glyph_t *glyph; scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) goto FINISH; glyph_cache[cache_index] = scaled_glyph; } if (unlikely (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)) { continue; } /* XXX glyph images are snapped to pixel locations */ x = _cairo_lround (glyphs[i].x); y = _cairo_lround (glyphs[i].y); x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); if (x2 < extents.bounded.x || y2 < extents.bounded.y || x1 > extents.bounded.x + extents.bounded.width || y1 > extents.bounded.y + extents.bounded.height) { continue; } if (scaled_glyph->surface_private == NULL) { status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { status = CAIRO_STATUS_SUCCESS; continue; } if (unlikely (status)) goto FINISH; } glyph = intel_glyph_pin (scaled_glyph->surface_private); if (glyph->cache->buffer.bo != last_bo) { intel_buffer_cache_t *cache = glyph->cache; shader.mask.base.bo = cache->buffer.bo; shader.mask.base.offset[0] = cache->buffer.offset; shader.mask.base.map[0] = cache->buffer.map0; shader.mask.base.map[1] = cache->buffer.map1; shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ status = i915_shader_commit (&shader, device); if (unlikely (status)) goto FINISH; last_bo = cache->buffer.bo; } x2 = x1 + glyph->width; y2 = y1 + glyph->height; if (mask_x) x1 += mask_x, x2 += mask_x; if (mask_y) y1 += mask_y, y2 += mask_y; /* XXX clip glyph */ emit_func (device, &shader, x1, y1, x2, y2, glyph); } status = CAIRO_STATUS_SUCCESS; FINISH: _cairo_scaled_font_thaw_cache (scaled_font); cairo_device_release (surface->intel.drm.base.device); CLEANUP_SHADER: i915_shader_fini (&shader); if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { cairo_path_fixed_t path; _cairo_path_fixed_init (&path); status = _cairo_scaled_font_glyph_path (scaled_font, glyphs + i, num_glyphs - i, &path); if (mask_x | mask_y) { _cairo_path_fixed_translate (&path, _cairo_fixed_from_int (mask_x), _cairo_fixed_from_int (mask_y)); } if (likely (status == CAIRO_STATUS_SUCCESS)) { status = surface->intel.drm.base.backend->fill (shader.target, shader.op, mask != NULL ? &_cairo_pattern_white.base : source, &path, CAIRO_FILL_RULE_WINDING, 0, scaled_font->options.antialias, clip); } _cairo_path_fixed_fini (&path); } if (mask != NULL) { if (likely (status == CAIRO_STATUS_SUCCESS)) { status = i915_surface_mask_internal (surface, op, source, mask, clip, &extents); } cairo_surface_finish (&mask->intel.drm.base); cairo_surface_destroy (&mask->intel.drm.base); } if (have_clip) _cairo_clip_fini (&local_clip); return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i915-private.h000066400000000000000000001171531271037650300272550ustar00rootroot00000000000000/* * Copyright © 2006, 2009 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Eric Anholt * Chris Wilson */ #ifndef CAIRO_DRM_I915_PRIVATE_H #define CAIRO_DRM_I915_PRIVATE_H #include "cairo-types-private.h" #include "cairo-drm-private.h" #include "cairo-drm-intel-private.h" #include "cairo-drm-intel-command-private.h" #include "cairo-drm-intel-ioctl-private.h" #include "cairo-freelist-private.h" #include #define I915_VERBOSE 1 #define I915_MAX_TEX_INDIRECT 4 #define I915_MAX_TEX_INSN 32 #define I915_MAX_ALU_INSN 64 #define I915_MAX_DECL_INSN 27 #define I915_MAX_TEMPORARY 16 /* Each instruction is 3 dwords long, though most don't require all * this space. Maximum of 123 instructions. Smaller maxes per insn * type. */ #define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16)) #define REG_TYPE_R 0 /* temporary regs, no need to * dcl, must be written before * read -- Preserved between * phases. */ #define REG_TYPE_T 1 /* Interpolated values, must be * dcl'ed before use. * * 0..7: texture coord, * 8: diffuse spec, * 9: specular color, * 10: fog parameter in w. */ #define REG_TYPE_CONST 2 /* Restriction: only one const * can be referenced per * instruction, though it may be * selected for multiple inputs. * Constants not initialized * default to zero. */ #define REG_TYPE_S 3 /* sampler */ #define REG_TYPE_OC 4 /* output color (rgba) */ #define REG_TYPE_OD 5 /* output depth (w), xyz are * temporaries. If not written, * interpolated depth is used? */ #define REG_TYPE_U 6 /* unpreserved temporaries */ #define REG_TYPE_MASK 0x7 #define REG_TYPE_SHIFT 4 #define REG_NR_MASK 0xf /* REG_TYPE_T: */ #define T_TEX0 0 #define T_TEX1 1 #define T_TEX2 2 #define T_TEX3 3 #define T_TEX4 4 #define T_TEX5 5 #define T_TEX6 6 #define T_TEX7 7 #define T_DIFFUSE 8 #define T_SPECULAR 9 #define T_FOG_W 10 /* interpolated fog is in W coord */ /* Arithmetic instructions */ /* .replicate_swizzle == selection and replication of a particular * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww */ #define A0_NOP (0x0<<24) /* no operation */ #define A0_ADD (0x1<<24) /* dst = src0 + src1 */ #define A0_MOV (0x2<<24) /* dst = src0 */ #define A0_MUL (0x3<<24) /* dst = src0 * src1 */ #define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */ #define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */ #define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */ #define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */ #define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */ #define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */ #define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */ #define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */ #define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */ #define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */ #define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */ #define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */ #define A0_FLR (0x10<<24) /* dst = floor(src0) */ #define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */ #define A0_TRC (0x12<<24) /* dst = int(src0) */ #define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */ #define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */ #define A0_DEST_SATURATE (1<<22) #define A0_DEST_TYPE_SHIFT 19 /* Allow: R, OC, OD, U */ #define A0_DEST_NR_SHIFT 14 /* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ #define A0_DEST_CHANNEL_X (1<<10) #define A0_DEST_CHANNEL_Y (2<<10) #define A0_DEST_CHANNEL_Z (4<<10) #define A0_DEST_CHANNEL_W (8<<10) #define A0_DEST_CHANNEL_ALL (0xf<<10) #define A0_DEST_CHANNEL_SHIFT 10 #define A0_SRC0_TYPE_SHIFT 7 #define A0_SRC0_NR_SHIFT 2 #define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y) #define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z) #define SRC_X 0 #define SRC_Y 1 #define SRC_Z 2 #define SRC_W 3 #define SRC_ZERO 4 #define SRC_ONE 5 #define A1_SRC0_CHANNEL_X_NEGATE (1<<31) #define A1_SRC0_CHANNEL_X_SHIFT 28 #define A1_SRC0_CHANNEL_Y_NEGATE (1<<27) #define A1_SRC0_CHANNEL_Y_SHIFT 24 #define A1_SRC0_CHANNEL_Z_NEGATE (1<<23) #define A1_SRC0_CHANNEL_Z_SHIFT 20 #define A1_SRC0_CHANNEL_W_NEGATE (1<<19) #define A1_SRC0_CHANNEL_W_SHIFT 16 #define A1_SRC1_TYPE_SHIFT 13 #define A1_SRC1_NR_SHIFT 8 #define A1_SRC1_CHANNEL_X_NEGATE (1<<7) #define A1_SRC1_CHANNEL_X_SHIFT 4 #define A1_SRC1_CHANNEL_Y_NEGATE (1<<3) #define A1_SRC1_CHANNEL_Y_SHIFT 0 #define A2_SRC1_CHANNEL_Z_NEGATE (1<<31) #define A2_SRC1_CHANNEL_Z_SHIFT 28 #define A2_SRC1_CHANNEL_W_NEGATE (1<<27) #define A2_SRC1_CHANNEL_W_SHIFT 24 #define A2_SRC2_TYPE_SHIFT 21 #define A2_SRC2_NR_SHIFT 16 #define A2_SRC2_CHANNEL_X_NEGATE (1<<15) #define A2_SRC2_CHANNEL_X_SHIFT 12 #define A2_SRC2_CHANNEL_Y_NEGATE (1<<11) #define A2_SRC2_CHANNEL_Y_SHIFT 8 #define A2_SRC2_CHANNEL_Z_NEGATE (1<<7) #define A2_SRC2_CHANNEL_Z_SHIFT 4 #define A2_SRC2_CHANNEL_W_NEGATE (1<<3) #define A2_SRC2_CHANNEL_W_SHIFT 0 /* Texture instructions */ #define T0_TEXLD (0x15<<24) /* Sample texture using predeclared * sampler and address, and output * filtered texel data to destination * register */ #define T0_TEXLDP (0x16<<24) /* Same as texld but performs a * perspective divide of the texture * coordinate .xyz values by .w before * sampling. */ #define T0_TEXLDB (0x17<<24) /* Same as texld but biases the * computed LOD by w. Only S4.6 two's * comp is used. This implies that a * float to fixed conversion is * done. */ #define T0_TEXKILL (0x18<<24) /* Does not perform a sampling * operation. Simply kills the pixel * if any channel of the address * register is < 0.0. */ #define T0_DEST_TYPE_SHIFT 19 /* Allow: R, OC, OD, U */ /* Note: U (unpreserved) regs do not retain their values between * phases (cannot be used for feedback) * * Note: oC and OD registers can only be used as the destination of a * texture instruction once per phase (this is an implementation * restriction). */ #define T0_DEST_NR_SHIFT 14 /* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ #define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */ #define T0_SAMPLER_NR_MASK (0xf<<0) #define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */ /* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */ #define T1_ADDRESS_REG_NR_SHIFT 17 #define T2_MBZ 0 /* Declaration instructions */ #define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib) * register or an s (sampler) * register. */ #define D0_SAMPLE_TYPE_SHIFT 22 #define D0_SAMPLE_TYPE_2D (0x0<<22) #define D0_SAMPLE_TYPE_CUBE (0x1<<22) #define D0_SAMPLE_TYPE_VOLUME (0x2<<22) #define D0_SAMPLE_TYPE_MASK (0x3<<22) #define D0_TYPE_SHIFT 19 /* Allow: T, S */ #define D0_NR_SHIFT 14 /* Allow T: 0..10, S: 0..15 */ #define D0_CHANNEL_X (1<<10) #define D0_CHANNEL_Y (2<<10) #define D0_CHANNEL_Z (4<<10) #define D0_CHANNEL_W (8<<10) #define D0_CHANNEL_ALL (0xf<<10) #define D0_CHANNEL_NONE (0<<10) #define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y) #define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z) /* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse * or specular declarations. * * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw) * * Must be zero for S (sampler) dcls */ #define D1_MBZ 0 #define D2_MBZ 0 /* MASK_* are the unshifted bitmasks of the destination mask in arithmetic * operations */ #define MASK_X 0x1 #define MASK_Y 0x2 #define MASK_Z 0x4 #define MASK_W 0x8 #define MASK_XYZ (MASK_X | MASK_Y | MASK_Z) #define MASK_XYZW (MASK_XYZ | MASK_W) #define MASK_SATURATE 0x10 /* Temporary, undeclared regs. Preserved between phases */ #define FS_R0 ((REG_TYPE_R << REG_TYPE_SHIFT) | 0) #define FS_R1 ((REG_TYPE_R << REG_TYPE_SHIFT) | 1) #define FS_R2 ((REG_TYPE_R << REG_TYPE_SHIFT) | 2) #define FS_R3 ((REG_TYPE_R << REG_TYPE_SHIFT) | 3) /* Texture coordinate regs. Must be declared. */ #define FS_T0 ((REG_TYPE_T << REG_TYPE_SHIFT) | 0) #define FS_T1 ((REG_TYPE_T << REG_TYPE_SHIFT) | 1) #define FS_T2 ((REG_TYPE_T << REG_TYPE_SHIFT) | 2) #define FS_T3 ((REG_TYPE_T << REG_TYPE_SHIFT) | 3) #define FS_T4 ((REG_TYPE_T << REG_TYPE_SHIFT) | 4) #define FS_T5 ((REG_TYPE_T << REG_TYPE_SHIFT) | 5) #define FS_T6 ((REG_TYPE_T << REG_TYPE_SHIFT) | 6) #define FS_T7 ((REG_TYPE_T << REG_TYPE_SHIFT) | 7) #define FS_T8 ((REG_TYPE_T << REG_TYPE_SHIFT) | 8) #define FS_T9 ((REG_TYPE_T << REG_TYPE_SHIFT) | 9) #define FS_T10 ((REG_TYPE_T << REG_TYPE_SHIFT) | 10) /* Constant values */ #define FS_C0 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 0) #define FS_C1 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 1) #define FS_C2 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 2) #define FS_C3 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 3) #define FS_C4 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 4) #define FS_C5 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 5) #define FS_C6 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 6) #define FS_C7 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 7) /* Sampler regs */ #define FS_S0 ((REG_TYPE_S << REG_TYPE_SHIFT) | 0) #define FS_S1 ((REG_TYPE_S << REG_TYPE_SHIFT) | 1) #define FS_S2 ((REG_TYPE_S << REG_TYPE_SHIFT) | 2) #define FS_S3 ((REG_TYPE_S << REG_TYPE_SHIFT) | 3) /* Output color */ #define FS_OC ((REG_TYPE_OC << REG_TYPE_SHIFT) | 0) /* Output depth */ #define FS_OD ((REG_TYPE_OD << REG_TYPE_SHIFT) | 0) /* Unpreserved temporary regs */ #define FS_U0 ((REG_TYPE_U << REG_TYPE_SHIFT) | 0) #define FS_U1 ((REG_TYPE_U << REG_TYPE_SHIFT) | 1) #define FS_U2 ((REG_TYPE_U << REG_TYPE_SHIFT) | 2) #define FS_U3 ((REG_TYPE_U << REG_TYPE_SHIFT) | 3) #define X_CHANNEL_SHIFT (REG_TYPE_SHIFT + 3) #define Y_CHANNEL_SHIFT (X_CHANNEL_SHIFT + 4) #define Z_CHANNEL_SHIFT (Y_CHANNEL_SHIFT + 4) #define W_CHANNEL_SHIFT (Z_CHANNEL_SHIFT + 4) #define REG_CHANNEL_MASK 0xf #define REG_NR(reg) ((reg) & REG_NR_MASK) #define REG_TYPE(reg) (((reg) >> REG_TYPE_SHIFT) & REG_TYPE_MASK) #define REG_X(reg) (((reg) >> X_CHANNEL_SHIFT) & REG_CHANNEL_MASK) #define REG_Y(reg) (((reg) >> Y_CHANNEL_SHIFT) & REG_CHANNEL_MASK) #define REG_Z(reg) (((reg) >> Z_CHANNEL_SHIFT) & REG_CHANNEL_MASK) #define REG_W(reg) (((reg) >> W_CHANNEL_SHIFT) & REG_CHANNEL_MASK) enum i915_fs_channel { X_CHANNEL_VAL = 0, Y_CHANNEL_VAL, Z_CHANNEL_VAL, W_CHANNEL_VAL, ZERO_CHANNEL_VAL, ONE_CHANNEL_VAL, NEG_X_CHANNEL_VAL = X_CHANNEL_VAL | 0x8, NEG_Y_CHANNEL_VAL = Y_CHANNEL_VAL | 0x8, NEG_Z_CHANNEL_VAL = Z_CHANNEL_VAL | 0x8, NEG_W_CHANNEL_VAL = W_CHANNEL_VAL | 0x8, NEG_ONE_CHANNEL_VAL = ONE_CHANNEL_VAL | 0x8 }; #define i915_fs_operand(reg, x, y, z, w) \ (reg) | \ (x##_CHANNEL_VAL << X_CHANNEL_SHIFT) | \ (y##_CHANNEL_VAL << Y_CHANNEL_SHIFT) | \ (z##_CHANNEL_VAL << Z_CHANNEL_SHIFT) | \ (w##_CHANNEL_VAL << W_CHANNEL_SHIFT) /* * Construct an operand description for using a register with no swizzling */ #define i915_fs_operand_reg(reg) \ i915_fs_operand(reg, X, Y, Z, W) #define i915_fs_operand_reg_negate(reg) \ i915_fs_operand(reg, NEG_X, NEG_Y, NEG_Z, NEG_W) /* * Returns an operand containing (0.0, 0.0, 0.0, 0.0). */ #define i915_fs_operand_zero() i915_fs_operand(FS_R0, ZERO, ZERO, ZERO, ZERO) /* * Returns an unused operand */ #define i915_fs_operand_none() i915_fs_operand_zero() /* * Returns an operand containing (1.0, 1.0, 1.0, 1.0). */ #define i915_fs_operand_one() i915_fs_operand(FS_R0, ONE, ONE, ONE, ONE) #define i915_get_hardware_channel_val(val, shift, negate) \ (((val & 0x7) << shift) | ((val & 0x8) ? negate : 0)) /* * Outputs a fragment shader command to declare a sampler or texture register. */ #define i915_fs_dcl(reg) \ do { \ OUT_DWORD (D0_DCL | \ (REG_TYPE(reg) << D0_TYPE_SHIFT) | \ (REG_NR(reg) << D0_NR_SHIFT) | \ ((REG_TYPE(reg) != REG_TYPE_S) ? D0_CHANNEL_ALL : 0)); \ OUT_DWORD (0); \ OUT_DWORD (0); \ } while (0) #define i915_fs_texld(dest_reg, sampler_reg, address_reg) \ do { \ OUT_DWORD (T0_TEXLD | \ (REG_TYPE(dest_reg) << T0_DEST_TYPE_SHIFT) | \ (REG_NR(dest_reg) << T0_DEST_NR_SHIFT) | \ (REG_NR(sampler_reg) << T0_SAMPLER_NR_SHIFT)); \ OUT_DWORD((REG_TYPE(address_reg) << T1_ADDRESS_REG_TYPE_SHIFT) | \ (REG_NR(address_reg) << T1_ADDRESS_REG_NR_SHIFT)); \ OUT_DWORD (0); \ } while (0) #define i915_fs_arith_masked(op, dest_reg, dest_mask, operand0, operand1, operand2) \ _i915_fs_arith_masked(A0_##op, dest_reg, dest_mask, operand0, operand1, operand2) #define i915_fs_arith(op, dest_reg, operand0, operand1, operand2) \ _i915_fs_arith(A0_##op, dest_reg, operand0, operand1, operand2) #define _i915_fs_arith_masked(cmd, dest_reg, dest_mask, operand0, operand1, operand2) \ do { \ /* Set up destination register and write mask */ \ OUT_DWORD (cmd | \ (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \ (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \ (((dest_mask) & ~MASK_SATURATE) << A0_DEST_CHANNEL_SHIFT) | \ (((dest_mask) & MASK_SATURATE) ? A0_DEST_SATURATE : 0) | \ /* Set up operand 0 */ \ (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \ (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \ OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \ A1_SRC0_CHANNEL_X_SHIFT, \ A1_SRC0_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand0), \ A1_SRC0_CHANNEL_Y_SHIFT, \ A1_SRC0_CHANNEL_Y_NEGATE) | \ i915_get_hardware_channel_val(REG_Z(operand0), \ A1_SRC0_CHANNEL_Z_SHIFT, \ A1_SRC0_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand0), \ A1_SRC0_CHANNEL_W_SHIFT, \ A1_SRC0_CHANNEL_W_NEGATE) | \ /* Set up operand 1 */ \ (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \ (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \ i915_get_hardware_channel_val(REG_X(operand1), \ A1_SRC1_CHANNEL_X_SHIFT, \ A1_SRC1_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand1), \ A1_SRC1_CHANNEL_Y_SHIFT, \ A1_SRC1_CHANNEL_Y_NEGATE)); \ OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \ A2_SRC1_CHANNEL_Z_SHIFT, \ A2_SRC1_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand1), \ A2_SRC1_CHANNEL_W_SHIFT, \ A2_SRC1_CHANNEL_W_NEGATE) | \ /* Set up operand 2 */ \ (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \ (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \ i915_get_hardware_channel_val(REG_X(operand2), \ A2_SRC2_CHANNEL_X_SHIFT, \ A2_SRC2_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand2), \ A2_SRC2_CHANNEL_Y_SHIFT, \ A2_SRC2_CHANNEL_Y_NEGATE) | \ i915_get_hardware_channel_val(REG_Z(operand2), \ A2_SRC2_CHANNEL_Z_SHIFT, \ A2_SRC2_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand2), \ A2_SRC2_CHANNEL_W_SHIFT, \ A2_SRC2_CHANNEL_W_NEGATE)); \ } while (0) #define _i915_fs_arith(cmd, dest_reg, operand0, operand1, operand2) do {\ /* Set up destination register and write mask */ \ OUT_DWORD (cmd | \ (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \ (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \ (A0_DEST_CHANNEL_ALL) | \ /* Set up operand 0 */ \ (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \ (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \ OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \ A1_SRC0_CHANNEL_X_SHIFT, \ A1_SRC0_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand0), \ A1_SRC0_CHANNEL_Y_SHIFT, \ A1_SRC0_CHANNEL_Y_NEGATE) | \ i915_get_hardware_channel_val(REG_Z(operand0), \ A1_SRC0_CHANNEL_Z_SHIFT, \ A1_SRC0_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand0), \ A1_SRC0_CHANNEL_W_SHIFT, \ A1_SRC0_CHANNEL_W_NEGATE) | \ /* Set up operand 1 */ \ (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \ (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \ i915_get_hardware_channel_val(REG_X(operand1), \ A1_SRC1_CHANNEL_X_SHIFT, \ A1_SRC1_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand1), \ A1_SRC1_CHANNEL_Y_SHIFT, \ A1_SRC1_CHANNEL_Y_NEGATE)); \ OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \ A2_SRC1_CHANNEL_Z_SHIFT, \ A2_SRC1_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand1), \ A2_SRC1_CHANNEL_W_SHIFT, \ A2_SRC1_CHANNEL_W_NEGATE) | \ /* Set up operand 2 */ \ (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \ (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \ i915_get_hardware_channel_val(REG_X(operand2), \ A2_SRC2_CHANNEL_X_SHIFT, \ A2_SRC2_CHANNEL_X_NEGATE) | \ i915_get_hardware_channel_val(REG_Y(operand2), \ A2_SRC2_CHANNEL_Y_SHIFT, \ A2_SRC2_CHANNEL_Y_NEGATE) | \ i915_get_hardware_channel_val(REG_Z(operand2), \ A2_SRC2_CHANNEL_Z_SHIFT, \ A2_SRC2_CHANNEL_Z_NEGATE) | \ i915_get_hardware_channel_val(REG_W(operand2), \ A2_SRC2_CHANNEL_W_SHIFT, \ A2_SRC2_CHANNEL_W_NEGATE)); \ } while (0) #define i915_fs_mov(dest_reg, operand0) \ i915_fs_arith(MOV, dest_reg, \ operand0, \ i915_fs_operand_none(), \ i915_fs_operand_none()) #define i915_fs_mov_masked(dest_reg, dest_mask, operand0) \ i915_fs_arith_masked (MOV, dest_reg, dest_mask, \ operand0, \ i915_fs_operand_none(), \ i915_fs_operand_none()) #define i915_fs_frc(dest_reg, operand0) \ i915_fs_arith (FRC, dest_reg, \ operand0, \ i915_fs_operand_none(), \ i915_fs_operand_none()) /* Add operand0 and operand1 and put the result in dest_reg */ #define i915_fs_add(dest_reg, operand0, operand1) \ i915_fs_arith (ADD, dest_reg, \ operand0, operand1, \ i915_fs_operand_none()) /* Multiply operand0 and operand1 and put the result in dest_reg */ #define i915_fs_mul(dest_reg, operand0, operand1) \ i915_fs_arith (MUL, dest_reg, \ operand0, operand1, \ i915_fs_operand_none()) /* Computes 1/sqrt(operand0.replicate_swizzle) puts the result in dest_reg */ #define i915_fs_rsq(dest_reg, dest_mask, operand0) \ do { \ if (dest_mask) { \ i915_fs_arith_masked (RSQ, dest_reg, dest_mask, \ operand0, \ i915_fs_operand_none (), \ i915_fs_operand_none ()); \ } else { \ i915_fs_arith (RSQ, dest_reg, \ operand0, \ i915_fs_operand_none (), \ i915_fs_operand_none ()); \ } \ } while (0) /* Puts the minimum of operand0 and operand1 in dest_reg */ #define i915_fs_min(dest_reg, operand0, operand1) \ i915_fs_arith (MIN, dest_reg, \ operand0, operand1, \ i915_fs_operand_none()) /* Puts the maximum of operand0 and operand1 in dest_reg */ #define i915_fs_max(dest_reg, operand0, operand1) \ i915_fs_arith (MAX, dest_reg, \ operand0, operand1, \ i915_fs_operand_none()) #define i915_fs_cmp(dest_reg, operand0, operand1, operand2) \ i915_fs_arith (CMP, dest_reg, operand0, operand1, operand2) /* Perform operand0 * operand1 + operand2 and put the result in dest_reg */ #define i915_fs_mad(dest_reg, dest_mask, op0, op1, op2) \ do { \ if (dest_mask) { \ i915_fs_arith_masked (MAD, dest_reg, dest_mask, op0, op1, op2); \ } else { \ i915_fs_arith (MAD, dest_reg, op0, op1, op2); \ } \ } while (0) #define i915_fs_dp2add(dest_reg, dest_mask, op0, op1, op2) \ do { \ if (dest_mask) { \ i915_fs_arith_masked (DP2ADD, dest_reg, dest_mask, op0, op1, op2); \ } else { \ i915_fs_arith (DP2ADD, dest_reg, op0, op1, op2); \ } \ } while (0) /* * Perform a 3-component dot-product of operand0 and operand1 and put the * resulting scalar in the channels of dest_reg specified by the dest_mask. */ #define i915_fs_dp3(dest_reg, dest_mask, op0, op1) \ do { \ if (dest_mask) { \ i915_fs_arith_masked (DP3, dest_reg, dest_mask, \ op0, op1,\ i915_fs_operand_none()); \ } else { \ i915_fs_arith (DP3, dest_reg, op0, op1,\ i915_fs_operand_none()); \ } \ } while (0) static inline uint32_t cairo_const i915_fs_operand_pure_alpha (int pure) { if (pure & (1 << 3)) return i915_fs_operand_one (); else return i915_fs_operand_zero (); } #define I915_TILING_DEFAULT I915_TILING_Y #define I915_BO_CACHE_BUCKETS 13 /* cache surfaces up to 16 MiB */ typedef struct i915_surface i915_surface_t; typedef struct i915_device i915_device_t; typedef struct i915_shader i915_shader_t; typedef void (*i915_add_rectangle_func_t) (const i915_shader_t *shader, int x, int y, int w, int h); #define IMAGE_CACHE_WIDTH 1024 #define IMAGE_CACHE_HEIGHT 1024 typedef struct i915_image_private { cairo_rtree_node_t node; intel_buffer_cache_t *container; } i915_image_private_t; #define I915_BATCH_SIZE (128*1024) #define I915_VBO_SIZE (512*1024) #define I915_MAX_RELOCS 2048 enum { I915_DEBUG_EXEC = 0x1, I915_DEBUG_SYNC = 0x2, I915_DEBUG_BATCH = 0x4, I915_DEBUG_BUFFER = 0x8, I915_DEBUG_BUFFER_CACHE = 0x10, I915_DEBUG_BUFFER_ALLOC = 0x20, I915_DEBUG_GLYPHS = 0x40, I915_DEBUG_MAP = 0x80, I915_DEBUG_THROTTLE = 0x100, }; struct i915_device { intel_device_t intel; cairo_bool_t debug; i915_shader_t *shader; /* note: only valid during geometry emission */ struct i915_batch { intel_bo_t *target_bo[I915_MAX_RELOCS]; size_t gtt_avail_size; size_t est_gtt_size; size_t total_gtt_size; uint16_t fences; uint16_t fences_avail; uint16_t reloc_count; uint16_t exec_count; uint16_t used; struct drm_i915_gem_exec_object2 exec[I915_MAX_RELOCS]; struct drm_i915_gem_relocation_entry reloc[I915_MAX_RELOCS]; } batch; uint32_t vbo; uint32_t vbo_offset; uint32_t vbo_used; uint32_t vbo_max_index; uint32_t vertex_index; uint32_t vertex_count; uint32_t floats_per_vertex; uint32_t rectangle_size; intel_bo_t *last_vbo; uint32_t last_vbo_offset; uint32_t last_vbo_space; i915_surface_t *current_target; uint32_t current_size; uint32_t current_diffuse; uint32_t current_colorbuf; uint32_t *current_source; uint32_t *current_mask; uint32_t *current_clip; uint32_t current_program; uint32_t current_texcoords; uint32_t current_blend; uint32_t current_constants[8*4]; uint32_t current_n_constants; uint32_t current_samplers[2*4]; uint32_t current_maps[4*4]; uint32_t current_n_samplers; uint32_t current_n_maps; uint32_t last_source_fragment; uint32_t clear_alpha; cairo_list_t image_caches[2]; uint32_t batch_header[13]; uint32_t batch_base[I915_BATCH_SIZE / sizeof (uint32_t)]; uint8_t vbo_base[I915_VBO_SIZE]; }; enum { CURRENT_SOURCE = 0x1, CURRENT_MASK = 0x2, CURRENT_CLIP = 0x4 }; typedef enum { VS_ZERO, VS_CONSTANT, VS_LINEAR, VS_TEXTURE, VS_TEXTURE_16, } i915_vertex_shader_t; typedef enum { FS_ZERO, FS_ONE, FS_PURE, FS_CONSTANT, FS_DIFFUSE, FS_LINEAR, FS_RADIAL, FS_TEXTURE, FS_YUV, FS_SPANS, } i915_fragment_shader_t; #define FS_DETAILS_SHIFT 4 typedef enum { PATTERN_BASE, PATTERN_CONSTANT, PATTERN_LINEAR, PATTERN_RADIAL, PATTERN_TEXTURE, } i915_shader_channel_t; struct i915_surface { intel_surface_t intel; uint32_t map0, map1; uint32_t colorbuf; cairo_bool_t deferred_clear; uint32_t offset; uint32_t is_current_texture; i915_image_private_t *cache; intel_bo_t *stencil; uint32_t stencil_stride; uint32_t stencil_offset; }; typedef enum { NONE = 0, YUV_I420, /* XXX */ YUV_YV12, YUV_YUY2, YUV_UYVY, } i915_packed_pixel_t; /* read-only container */ #define I915_PACKED_PIXEL_SURFACE_TYPE 0x1000 typedef struct i915_packed_pixel_surface { cairo_surface_t base; i915_packed_pixel_t pixel; i915_device_t *device; intel_bo_t *bo; uint32_t is_current_texture; uint32_t offset[4]; uint32_t stride[4]; uint32_t width[4]; uint32_t height[4]; uint32_t map0[4], map1[4]; } i915_packed_pixel_surface_t; struct i915_shader { i915_device_t *device; i915_surface_t *target; cairo_operator_t op; uint32_t blend; float opacity; cairo_content_t content; cairo_bool_t committed; cairo_bool_t need_combine; i915_add_rectangle_func_t add_rectangle; union i915_shader_channel { struct { i915_vertex_shader_t vertex; i915_fragment_shader_t fragment; i915_shader_channel_t pattern; } type; struct i915_shader_base { i915_vertex_shader_t vertex; i915_fragment_shader_t fragment; i915_shader_channel_t pattern; uint32_t texfmt; cairo_content_t content; uint32_t mode; intel_bo_t *bo; uint32_t n_samplers; uint32_t offset[4]; uint32_t map[2*4]; uint32_t sampler[2]; cairo_matrix_t matrix; } base; struct i915_shader_solid { struct i915_shader_base base; cairo_color_t color; int pure; } solid; struct i915_shader_linear { struct i915_shader_base base; struct { float red, green, blue, alpha; } color0, color1; float dx, dy, offset; } linear; struct i915_shader_radial { struct i915_shader_base base; float constants[8]; } radial; struct i915_shader_surface { struct i915_shader_base base; i915_packed_pixel_t pixel; } surface; } source, mask, clip, dst; jmp_buf unwind; }; enum i915_shader_linear_mode { /* XXX REFLECT */ LINEAR_TEXTURE, LINEAR_NONE, LINEAR_REPEAT, LINEAR_PAD, }; enum i915_shader_radial_mode { RADIAL_ONE, RADIAL_TWO }; typedef cairo_status_t (*i915_spans_func_t) (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents); cairo_private cairo_status_t i915_clip_and_composite_spans (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, i915_spans_func_t draw_func, void *draw_closure, const cairo_composite_rectangles_t*extents, cairo_clip_t *clip, double opacity); cairo_private cairo_surface_t * i915_surface_create_internal (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height, uint32_t tiling, cairo_bool_t gpu_target); cairo_private i915_surface_t * i915_surface_create_from_cacheable_image_internal (i915_device_t *device, cairo_image_surface_t *image); cairo_private void i915_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); cairo_private cairo_int_status_t i915_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining); static inline int cairo_const i915_tiling_height (uint32_t tiling, int height) { switch (tiling) { default: case I915_TILING_NONE: return (height + 1) & -2; case I915_TILING_X: return (height + 7) & -8; case I915_TILING_Y: return (height + 31) & -32; } } static inline uint32_t cairo_const i915_tiling_stride (int format, uint32_t stride) { uint32_t tile_width; /* use 64B alignment so that the buffer may be used as a scanout */ if (format == I915_TILING_NONE) return (stride + 63) & -64; tile_width = 512; /* XXX Currently the kernel enforces a tile_width of 512 for TILING_Y. the docs are a bit confused on that front once we enable it on 915 we'll find out what the tile width size should be in the fence setup it could be that 915 has y tiling but that the minimum width is 512 or something yeah it's probably 128 on 915 also it's just that we haven't tested but I wasn't thinking that the tile widths were the same only that in order to fence y tiles on 915 you needed pitch to be a multiple of 4 y tiles (or something like that) tile_width = format == I915_TILING_Y ? 128 : 512; */ /* needs a pot tile width */ while (tile_width < stride) tile_width <<= 1; return tile_width; } static inline uint32_t cairo_const i915_tiling_size (uint32_t tiling, uint32_t size) { uint32_t fence; if (tiling == I915_TILING_NONE) return (size + 4095) & -4096; fence = 1024 * 1024; /* 1 MiB */ while (fence < size) fence <<= 1; return fence; } static inline cairo_bool_t cairo_const i915_texture_filter_is_nearest (cairo_filter_t filter) { switch (filter) { case CAIRO_FILTER_BEST: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: return FALSE; default: case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: return TRUE; } } static inline uint32_t cairo_const i915_texture_filter (cairo_filter_t filter) { switch (filter) { case CAIRO_FILTER_BEST: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: return (FILTER_LINEAR << SS2_MAG_FILTER_SHIFT) | (FILTER_LINEAR << SS2_MIN_FILTER_SHIFT); default: case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: return (FILTER_NEAREST << SS2_MAG_FILTER_SHIFT) | (FILTER_NEAREST << SS2_MIN_FILTER_SHIFT); } } static inline uint32_t cairo_const i915_texture_extend (cairo_extend_t extend) { switch (extend) { default: case CAIRO_EXTEND_NONE: return (TEXCOORDMODE_CLAMP_BORDER << SS3_TCX_ADDR_MODE_SHIFT) | (TEXCOORDMODE_CLAMP_BORDER << SS3_TCY_ADDR_MODE_SHIFT); case CAIRO_EXTEND_REPEAT: return (TEXCOORDMODE_WRAP << SS3_TCX_ADDR_MODE_SHIFT) | (TEXCOORDMODE_WRAP << SS3_TCY_ADDR_MODE_SHIFT); case CAIRO_EXTEND_PAD: return (TEXCOORDMODE_CLAMP_EDGE << SS3_TCX_ADDR_MODE_SHIFT) | (TEXCOORDMODE_CLAMP_EDGE << SS3_TCY_ADDR_MODE_SHIFT); case CAIRO_EXTEND_REFLECT: return (TEXCOORDMODE_MIRROR << SS3_TCX_ADDR_MODE_SHIFT) | (TEXCOORDMODE_MIRROR << SS3_TCY_ADDR_MODE_SHIFT); } } static inline uint32_t cairo_const BUF_tiling (uint32_t tiling) { switch (tiling) { default: case I915_TILING_NONE: return 0; case I915_TILING_X: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_X; case I915_TILING_Y: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_Y; } } #define OUT_DWORD(dword) i915_batch_emit_dword (device, dword) #define OUT_RELOC(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, FALSE) #define OUT_RELOC_FENCED(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, TRUE) #define FS_LOCALS \ uint32_t *_shader_start #define FS_BEGIN() \ do { \ _shader_start = BATCH_PTR (device); \ OUT_DWORD (_3DSTATE_PIXEL_SHADER_PROGRAM); \ } while (0) #define FS_END() \ do { \ *_shader_start |= BATCH_PTR (device) - _shader_start - 2; \ } while (0); static inline int32_t i915_batch_space (i915_device_t *device) { /* leave room for RECTLIST(4) + MI_BUFFER_END + MI_NOOP */ return sizeof (device->batch_base) - (device->batch.used << 2) - 32; } static inline cairo_bool_t i915_check_aperture_size (const i915_device_t *device, int relocs, size_t est_size, size_t size) { return device->batch.reloc_count + relocs < I915_MAX_RELOCS - 2 && device->batch.est_gtt_size + est_size <= device->batch.gtt_avail_size && device->batch.total_gtt_size + size <= device->intel.gtt_avail_size; } static inline cairo_bool_t i915_check_aperture (const i915_device_t *device, intel_bo_t **bo_array, int count) { uint32_t relocs = 0, est_size = 0, size = 0; while (count--) { const intel_bo_t *bo = *bo_array++; if (bo->exec == NULL) { relocs++; size += bo->base.size; if (!bo->busy) est_size += bo->base.size; } } return i915_check_aperture_size (device, relocs, est_size, size); } static inline cairo_bool_t i915_check_aperture_and_fences (const i915_device_t *device, intel_bo_t **bo_array, int count) { uint32_t relocs = 0, est_size = 0, size = 0; uint32_t fences = 0; while (count--) { const intel_bo_t *bo = *bo_array++; if (bo->exec == NULL) { relocs++; size += bo->base.size; if (!bo->busy) est_size += bo->base.size; if (bo->tiling != I915_TILING_NONE) fences++; } else if (bo->tiling != I915_TILING_NONE) { if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) fences++; } } return i915_check_aperture_size (device, relocs, est_size, size) && device->batch.fences + fences <= device->batch.fences_avail; } #define BATCH_PTR(device) &(device)->batch_base[(device)->batch.used] static inline void i915_batch_emit_dword (i915_device_t *device, uint32_t dword) { device->batch_base[device->batch.used++] = dword; } cairo_private void i915_batch_add_reloc (i915_device_t *device, uint32_t pos, intel_bo_t *bo, uint32_t offset, uint32_t read_domains, uint32_t write_domain, cairo_bool_t needs_fence); static inline void i915_batch_fill_reloc (i915_device_t *device, uint32_t pos, intel_bo_t *bo, uint32_t offset, uint32_t read_domains, uint32_t write_domain) { i915_batch_add_reloc (device, pos, bo, offset, read_domains, write_domain, FALSE); device->batch_base[pos] = bo->offset + offset; } static inline void i915_batch_emit_reloc (i915_device_t *device, intel_bo_t *bo, uint32_t offset, uint32_t read_domains, uint32_t write_domain, cairo_bool_t needs_fence) { i915_batch_add_reloc (device, device->batch.used, bo, offset, read_domains, write_domain, needs_fence); i915_batch_emit_dword (device, bo->offset + offset); } cairo_private void i915_vbo_flush (i915_device_t *device); cairo_private void i915_vbo_finish (i915_device_t *device); cairo_private cairo_status_t i915_batch_flush (i915_device_t *device); static inline float * i915_add_rectangle (i915_device_t *device) { float *vertices; uint32_t size; assert (device->floats_per_vertex); assert (device->rectangle_size == 3*device->floats_per_vertex*sizeof(float)); size = device->rectangle_size; if (unlikely (device->vbo_offset + size > I915_VBO_SIZE)) i915_vbo_finish (device); vertices = (float *) (device->vbo_base + device->vbo_offset); device->vbo_used = device->vbo_offset += size; device->vertex_count += 3; return vertices; } static inline i915_device_t * i915_device (i915_surface_t *surface) { return (i915_device_t *) surface->intel.drm.base.device; } cairo_private cairo_status_t i915_surface_clear (i915_surface_t *dst); cairo_private void i915_set_dst (i915_device_t *device, i915_surface_t *dst); cairo_private void i915_shader_init (i915_shader_t *shader, i915_surface_t *dst, cairo_operator_t op, double opacity); cairo_private cairo_status_t i915_shader_acquire_pattern (i915_shader_t *shader, union i915_shader_channel *src, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents); cairo_private void i915_shader_set_clip (i915_shader_t *shader, cairo_clip_t *clip); cairo_private int i915_shader_num_texcoords (const i915_shader_t *shader); static inline double cairo_const i915_shader_linear_texcoord (const struct i915_shader_linear *l, double src_x, double src_y) { return l->dx * src_x + l->dy * src_y + l->offset; } cairo_private cairo_status_t i915_shader_commit (i915_shader_t *shader, i915_device_t *device); cairo_private void i915_shader_fini (i915_shader_t *shader); cairo_private cairo_status_t i915_fixup_unbounded (i915_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip); static inline cairo_bool_t i915_surface_needs_tiling (i915_surface_t *dst) { return dst->intel.drm.width > 2048 || dst->intel.drm.height > 2048; } cairo_private cairo_status_t i915_surface_copy_subimage (i915_device_t *device, i915_surface_t *src, const cairo_rectangle_int_t *extents, cairo_bool_t flush, i915_surface_t **clone_out); static inline uint32_t pack_float (float f) { union { float f; uint32_t ui; } t; t.f = f; return t.ui; } static inline cairo_status_t i915_surface_fallback_flush (i915_surface_t *surface) { cairo_status_t status; if (unlikely (surface->intel.drm.fallback != NULL)) return intel_surface_flush (&surface->intel, 0); status = CAIRO_STATUS_SUCCESS; if (unlikely (surface->deferred_clear)) status = i915_surface_clear (surface); return status; } #endif /* CAIRO_DRM_I915_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i915-shader.c000066400000000000000000002405541271037650300270460ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-drm-i915-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-snapshot-private.h" #if 0 static cairo_status_t i915_packed_pixel_surface_finish (void *abstract_surface) { i915_packed_pixel_surface_t *surface = abstract_surface; i915_device_t *device; device = i915_device_acquire (&surface->device->intel.base); intel_bo_destroy (&device->intel, surface->bo); if (surface->is_current_texture) { if (surface->is_current_texture & CURRENT_SOURCE) device->current_source = NULL; if (surface->is_current_texture & CURRENT_MASK) device->current_mask = NULL; device->current_n_samplers = 0; } i915_device_release (device); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t i915_packed_pixel_surface_backend = { I915_PACKED_PIXEL_SURFACE_TYPE, i915_packed_pixel_surface_finish, }; static cairo_surface_t * i915_packed_pixel_surface_create (i915_device_t *device, i915_packed_pixel_t pixel, const uint8_t *data, uint32_t length, uint32_t width, uint32_t height) { i915_packed_pixel_surface_t *surface; cairo_content_t content; uint32_t tiling, size; uint32_t stride, half_stride; uint32_t i; if (width > 2048 || height > 2048) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); surface = malloc (sizeof (i915_packed_pixel_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); tiling = I915_TILING_NONE; /* XXX */ half_stride = stride = i915_tiling_stride (tiling, width/2); if (stride < width) stride *= 2 ; height = i915_tiling_height (tiling, height); switch (surface->pixel = pixel) { case YUV_I420: content = CAIRO_CONTENT_COLOR; surface->offset[0] = 0; surface->width[0] = width; surface->height[0] = height; surface->stride[0] = stride; surface->map0[0] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); surface->map0[0] |= ((height - 1) << MS3_HEIGHT_SHIFT) | ((width - 1) << MS3_WIDTH_SHIFT); surface->map1[0] = (stride / 4 - 1) << MS4_PITCH_SHIFT; surface->offset[1] = stride * height; surface->width[1] = width / 2; surface->height[1] = height / 2; surface->stride[1] = half_stride; surface->map0[1] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); surface->map0[1] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) | ((width/2 - 1) << MS3_WIDTH_SHIFT); surface->map1[1] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT; if (width < half_stride) { surface->offset[2] = stride * height + half_stride / 2; size = stride * height + half_stride * height / 2; } else { surface->offset[2] = stride * height + half_stride * height / 2; size = stride * height + half_stride * height; } surface->width[2] = width / 2; surface->height[2] = height / 2; surface->stride[2] = half_stride; surface->map0[2] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); surface->map0[2] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) | ((width/2 - 1) << MS3_WIDTH_SHIFT); surface->map1[2] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT; break; case NONE: case YUV_YV12: case YUV_YUY2: case YUV_UYVY: ASSERT_NOT_REACHED; break; } _cairo_surface_init (&surface->base, &i915_packed_pixel_surface_backend, content); surface->bo = intel_bo_create (&device->intel, size, FALSE); assert (surface->bo->tiling == I915_TILING_NONE); if (unlikely (surface->bo == NULL)) { free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } if (tiling == I915_TILING_NONE) { intel_bo_t *bo = surface->bo; uint32_t dst; int uv; dst = surface->offset[0]; if (width == stride) { size = stride * height; intel_bo_write (&device->intel, bo, dst, size, data); data += size; } else { for (i = 0; i < height; i++) { intel_bo_write (&device->intel, bo, dst, width, data); dst += stride; data += width; } } for (uv = 1; uv <= 2; uv++) { dst = surface->offset[uv]; if (width / 2 == half_stride) { size = half_stride * height / 2; intel_bo_write (&device->intel, bo, dst, size, data); data += size; } else { size = width / 2; for (i = 0; i < height / 2; i++) { intel_bo_write (&device->intel, bo, dst, size, data); dst += half_stride; data += size; } } } } else { uint8_t *dst, *base; base = intel_bo_map (&device->intel, surface->bo); dst = base + surface->offset[0]; if (width == stride) { size = stride * height; memcpy (dst, data, size); data += size; } else { for (i = 0; i < height; i++) { memcpy (dst, data, width); dst += stride; data += width; } } dst = base + surface->offset[1]; if (width / 2 == half_stride) { size = half_stride * height / 2; memcpy (dst, data, size); data += size; } else { size = width / 2; for (i = 0; i < height / 2; i++) { memcpy (dst, data, size); dst += half_stride; data += size; } } dst = base + surface->offset[2]; if (width / 2 == half_stride) { size = half_stride * height / 2; memcpy (dst, data, size); data += size; } else { size = width / 2; for (i = 0; i < height / 2; i++) { memcpy (dst, data, size); dst += half_stride; data += size; } } } surface->device = device; surface->is_current_texture = 0; return &surface->base; } static cairo_int_status_t i915_clone_yuv (i915_surface_t *surface, cairo_surface_t *source, int width, int height, cairo_surface_t **clone_out) { const uint8_t *mime_data = NULL; unsigned int mime_data_length; cairo_surface_t *clone; cairo_surface_get_mime_data (source, "video/x-raw-yuv/i420", &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; clone = i915_packed_pixel_surface_create ((i915_device_t *) surface->base.device, YUV_I420, mime_data, mime_data_length, width, height); if (clone == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; if (unlikely (clone->status)) return clone->status; *clone_out = clone; return CAIRO_STATUS_SUCCESS; } #endif /* Max instruction count: 4 */ static void i915_shader_linear_color (i915_device_t *device, enum i915_shader_linear_mode mode, int in, int c0, int c1, int out) { int tmp = FS_U0; switch (mode) { case LINEAR_TEXTURE: ASSERT_NOT_REACHED; case LINEAR_NONE: tmp = in; break; case LINEAR_REPEAT: i915_fs_frc (tmp, i915_fs_operand (in, X, X, X, X)); break; #if 0 case LINEAR_REFLECT: /* XXX needs an extra constant: C2 [0.5, 2.0, x, x] */ i915_fs_mul (tmp, in, 0.5); i915_fs_frc (tmp, i915_fs_operand_reg (tmp)); i915_fs_mul (tmp, tmp, 2.0); i915_fs_add (tmp, i915_fs_operand_one (), i915_fs_operand_reg_negate (tmp)); i915_fs_cmp (tmp, i915_fs_operand_reg (tmp), i915_fs_operand_reg (tmp), i915_fs_operand_reg_negate (tmp)); i915_fs_add (tmp, i915_fs_operand_one (), i915_fs_operand_reg_negate (tmp)); #endif case LINEAR_PAD: i915_fs_max (tmp, i915_fs_operand_zero (), i915_fs_operand (in, X, X, X, X)); i915_fs_min (tmp, i915_fs_operand_one (), i915_fs_operand_reg (tmp)); break; } /* interpolate */ i915_fs_mad (out, 0, i915_fs_operand (tmp, NEG_X, NEG_X, NEG_X, NEG_X), i915_fs_operand_reg (c0), i915_fs_operand_reg (c0)); i915_fs_mad (out, 0, i915_fs_operand (tmp, X, X, X, X), i915_fs_operand_reg (c1), i915_fs_operand_reg (out)); } static void i915_shader_radial_init (struct i915_shader_radial *r, const cairo_radial_pattern_t *radial) { double dx, dy, dr, r1; dx = radial->cd2.center.x - radial->cd1.center.x; dy = radial->cd2.center.y - radial->cd1.center.y; dr = radial->cd2.radius - radial->cd1.radius; r1 = radial->cd1.radius; if (radial->cd2.center.x == radial->cd1.center.x && radial->cd2.center.y == radial->cd1.center.y) { /* XXX dr == 0, meaningless with anything other than PAD */ r->constants[0] = radial->cd1.center.x / dr; r->constants[1] = radial->cd1.center.y / dr; r->constants[2] = 1. / dr; r->constants[3] = -r1 / dr; r->constants[4] = 0; r->constants[5] = 0; r->constants[6] = 0; r->constants[7] = 0; r->base.mode = RADIAL_ONE; } else { r->constants[0] = -radial->cd1.center.x; r->constants[1] = -radial->cd1.center.y; r->constants[2] = r1; r->constants[3] = -4 * (dx*dx + dy*dy - dr*dr); r->constants[4] = -2 * dx; r->constants[5] = -2 * dy; r->constants[6] = -2 * r1 * dr; r->constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr)); r->base.mode = RADIAL_TWO; } r->base.matrix = radial->base.base.matrix; } /* Max instruction count: 10 */ static void i915_shader_radial_coord (i915_device_t *device, enum i915_shader_radial_mode mode, int in, int g0, int g1, int out) { switch (mode) { case RADIAL_ONE: /* pdx = (x - c1x) / dr, pdy = (y - c1y) / dr; r² = pdx*pdx + pdy*pdy t = r²/sqrt(r²) - r1/dr; */ i915_fs_mad (FS_U0, MASK_X | MASK_Y, i915_fs_operand (in, X, Y, ZERO, ZERO), i915_fs_operand (g0, Z, Z, ZERO, ZERO), i915_fs_operand (g0, NEG_X, NEG_Y, ZERO, ZERO)); i915_fs_dp2add (FS_U0, MASK_X, i915_fs_operand (FS_U0, X, Y, ZERO, ZERO), i915_fs_operand (FS_U0, X, Y, ZERO, ZERO), i915_fs_operand_zero ()); i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U0, X, X, X, X)); i915_fs_mad (out, MASK_X, i915_fs_operand (FS_U0, X, ZERO, ZERO, ZERO), i915_fs_operand (out, X, ZERO, ZERO, ZERO), i915_fs_operand (g0, W, ZERO, ZERO, ZERO)); break; case RADIAL_TWO: /* pdx = x - c1x, pdy = y - c1y; A = dx² + dy² - dr² B = -2*(pdx*dx + pdy*dy + r1*dr); C = pdx² + pdy² - r1²; det = B*B - 4*A*C; t = (-B + sqrt (det)) / (2 * A) */ /* u0.x = pdx, u0.y = pdy, u[0].z = r1; */ i915_fs_add (FS_U0, i915_fs_operand (in, X, Y, ZERO, ZERO), i915_fs_operand (g0, X, Y, Z, ZERO)); /* u0.x = pdx, u0.y = pdy, u[0].z = r1, u[0].w = B; */ i915_fs_dp3 (FS_U0, MASK_W, i915_fs_operand (FS_U0, X, Y, ONE, ZERO), i915_fs_operand (g1, X, Y, Z, ZERO)); /* u1.x = pdx² + pdy² - r1²; [C] */ i915_fs_dp3 (FS_U1, MASK_X, i915_fs_operand (FS_U0, X, Y, Z, ZERO), i915_fs_operand (FS_U0, X, Y, NEG_Z, ZERO)); /* u1.x = C, u1.y = B, u1.z=-4*A; */ i915_fs_mov_masked (FS_U1, MASK_Y, i915_fs_operand (FS_U0, W, W, W, W)); i915_fs_mov_masked (FS_U1, MASK_Z, i915_fs_operand (g0, W, W, W, W)); /* u1.x = B² - 4*A*C */ i915_fs_dp2add (FS_U1, MASK_X, i915_fs_operand (FS_U1, X, Y, ZERO, ZERO), i915_fs_operand (FS_U1, Z, Y, ZERO, ZERO), i915_fs_operand_zero ()); /* out.x = -B + sqrt (B² - 4*A*C), * out.y = -B - sqrt (B² - 4*A*C), */ i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U1, X, X, X, X)); i915_fs_mad (out, MASK_X | MASK_Y, i915_fs_operand (out, X, X, ZERO, ZERO), i915_fs_operand (FS_U1, X, NEG_X, ZERO, ZERO), i915_fs_operand (FS_U0, NEG_W, NEG_W, ZERO, ZERO)); /* out.x = (-B + sqrt (B² - 4*A*C)) / (2 * A), * out.y = (-B - sqrt (B² - 4*A*C)) / (2 * A) */ i915_fs_mul (out, i915_fs_operand (out, X, Y, ZERO, ZERO), i915_fs_operand (g1, W, W, ZERO, ZERO)); /* if (A > 0) * out = (-B + sqrt (B² - 4*A*C)) / (2 * A), * else * out = (-B - sqrt (B² - 4*A*C)) / (2 * A) */ i915_fs_cmp (out, i915_fs_operand (g1, W, ZERO, ZERO, ZERO), i915_fs_operand (out, X, ZERO, ZERO, ZERO), i915_fs_operand (out, Y, ZERO, ZERO, ZERO)); break; } } /* Max instruction count: 7 */ static inline void i915_shader_yuv_color (i915_device_t *device, int y, int u, int v, int c0, int c1, int c2, int out) { i915_fs_mov_masked (FS_U0, MASK_X, i915_fs_operand_reg (y)); i915_fs_mov_masked (FS_U0, MASK_Y, i915_fs_operand_reg (u)); i915_fs_mov_masked (FS_U0, MASK_Z, i915_fs_operand_reg (v)); i915_fs_add (FS_U0, i915_fs_operand_reg (FS_U0), i915_fs_operand_reg (c0)); i915_fs_dp3 (out, MASK_X, i915_fs_operand_reg (FS_U0), i915_fs_operand (c1, X, ZERO, Y, ZERO)); i915_fs_dp3 (out, MASK_Z, i915_fs_operand_reg (FS_U0), i915_fs_operand (c1, Z, W, ZERO, ZERO)); i915_fs_dp3 (out, MASK_Y, i915_fs_operand_reg (FS_U0), i915_fs_operand_reg (c2)); } static inline uint32_t i915_shader_channel_key (const union i915_shader_channel *channel) { return (channel->type.fragment & 0x0f) | (channel->base.mode << FS_DETAILS_SHIFT); } static uint32_t i915_shader_channel_get_num_tex_coords (const union i915_shader_channel *channel) { switch (channel->type.fragment) { default: case FS_ZERO: case FS_ONE: case FS_CONSTANT: case FS_PURE: case FS_DIFFUSE: return 0; case FS_LINEAR: case FS_RADIAL: case FS_TEXTURE: case FS_SPANS: case FS_YUV: return 1; } } static uint32_t i915_shader_get_num_tex_coords (const i915_shader_t *shader) { uint32_t num_tex_coords; num_tex_coords = 0; num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->source); num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->mask); num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->clip); num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->dst); return num_tex_coords; } #define i915_fs_operand_impure(reg, channel, pure) \ (reg | \ (((pure & (1 << 0)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \ (((pure & (1 << 1)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \ (((pure & (1 << 2)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \ (((pure & (1 << 3)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT)) #define i915_fs_operand_pure(pure) \ (FS_R0 | \ (((pure & (1 << 0)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \ (((pure & (1 << 1)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \ (((pure & (1 << 2)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \ (((pure & (1 << 3)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT)) static void i915_set_shader_program (i915_device_t *device, const i915_shader_t *shader) { uint32_t num_tex_coords; uint32_t num_samplers; uint32_t n; uint32_t texture_offset = 0; uint32_t constant_offset = 0; uint32_t sampler_offset = 0; uint32_t source_reg; uint32_t source_pure; uint32_t mask_reg; uint32_t out_reg; uint32_t dest_reg; FS_LOCALS; n = (i915_shader_channel_key (&shader->source) << 0) | (i915_shader_channel_key (&shader->mask) << 8) | (i915_shader_channel_key (&shader->clip) << 16) | (shader->op << 24) | ((shader->opacity < 1.) << 30) | (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31); if (n == device->current_program) return; device->current_program = n; FS_BEGIN (); if (shader->source.type.fragment == FS_ZERO) { if (shader->clip.type.fragment == FS_TEXTURE) { /* XXX need_combine */ assert (shader->mask.type.fragment == (i915_fragment_shader_t) -1); i915_fs_dcl (FS_T0); i915_fs_texld (FS_U0, FS_S0, FS_T0); if ((shader->content & CAIRO_CONTENT_COLOR) == 0) i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, W, W, W, W)); else i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, ZERO, ZERO, ZERO, W)); } else { i915_fs_mov (FS_OC, i915_fs_operand_zero ()); } FS_END (); return; } num_tex_coords = i915_shader_get_num_tex_coords (shader); for (n = 0; n < num_tex_coords; n++) i915_fs_dcl (FS_T0 + n); num_samplers = shader->source.base.n_samplers + shader->mask.base.n_samplers + shader->clip.base.n_samplers + shader->dst.base.n_samplers; for (n = 0; n < num_samplers; n++) i915_fs_dcl (FS_S0 + n); source_reg = ~0; source_pure = 0; out_reg = FS_R0; if (! shader->need_combine && shader->mask.type.fragment == (i915_fragment_shader_t) -1 && shader->clip.type.fragment != FS_TEXTURE && shader->content != CAIRO_CONTENT_ALPHA) { out_reg = FS_OC; } switch (shader->source.type.fragment) { default: case FS_ZERO: case FS_SPANS: ASSERT_NOT_REACHED; case FS_PURE: source_pure = shader->source.solid.pure; case FS_ONE: break; case FS_CONSTANT: source_reg = FS_C0; constant_offset += 1; break; case FS_DIFFUSE: i915_fs_dcl (FS_T8); source_reg = FS_T8; break; case FS_LINEAR: i915_shader_linear_color (device, shader->source.base.mode, FS_T0, /* input */ FS_C0, FS_C1, /* colour ramp */ FS_U3); /* unpremultiplied output */ /* XXX can we defer premultiplication? */ i915_fs_mul (out_reg, i915_fs_operand_reg (FS_U3), i915_fs_operand (FS_U3, W, W, W, ONE)); constant_offset += 2; texture_offset += 1; source_reg = out_reg; break; case FS_RADIAL: i915_shader_radial_coord (device, shader->source.base.mode, FS_T0, /* input */ FS_C0, FS_C1, /* gradient constants */ FS_R0); /* coordinate */ i915_fs_texld (out_reg, FS_S0, FS_R0); constant_offset += 2; texture_offset += 1; sampler_offset += 1; source_reg = out_reg; break; case FS_TEXTURE: i915_fs_texld (out_reg, FS_S0, FS_T0); texture_offset += 1; sampler_offset += 1; source_reg = out_reg; break; case FS_YUV: /* Load samplers to temporaries. */ i915_fs_texld (FS_R0, FS_S0, FS_T0); i915_fs_texld (FS_R1, FS_S1, FS_T0); i915_fs_texld (FS_R2, FS_S2, FS_T0); i915_shader_yuv_color (device, FS_R0, FS_R1, FS_R2, /* y, u, v */ FS_C0, FS_C1, FS_C2, /* coefficients */ out_reg); constant_offset += 3; texture_offset += 1; sampler_offset += 3; source_reg = out_reg; break; } mask_reg = ~0; switch (shader->mask.type.fragment) { case FS_PURE: case FS_ZERO: case FS_YUV: case FS_DIFFUSE: ASSERT_NOT_REACHED; case FS_ONE: default: break; case FS_SPANS: mask_reg = FS_T0 + texture_offset; texture_offset += 1; break; case FS_CONSTANT: mask_reg = FS_C0 + constant_offset; constant_offset += 1; break; case FS_LINEAR: i915_shader_linear_color (device, shader->mask.base.mode, FS_T0 + texture_offset, /* input */ FS_C0 + constant_offset, FS_C0 + constant_offset + 1, /* colour ramp */ FS_R1); /* unpremultiplied output */ constant_offset += 2; texture_offset += 1; mask_reg = FS_R1; break; case FS_RADIAL: i915_shader_radial_coord (device, shader->mask.base.mode, FS_T0 + texture_offset, /* input */ FS_C0 + constant_offset, FS_C0 + constant_offset + 1, /* gradient constants */ FS_R1); /* coordinate */ i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_R1); constant_offset += 2; texture_offset += 1; sampler_offset += 1; mask_reg = FS_R1; break; case FS_TEXTURE: i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset); texture_offset += 1; sampler_offset += 1; mask_reg = FS_R1; break; } if (mask_reg != ~0U) { if (! shader->need_combine && shader->clip.type.fragment != FS_TEXTURE && (shader->content != CAIRO_CONTENT_ALPHA || source_reg == ~0U)) { out_reg = FS_OC; } if (source_reg == ~0U) { if (source_pure) { if (shader->mask.type.fragment == FS_SPANS) { if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { if (source_pure & (1 << 3)) i915_fs_mov (out_reg, i915_fs_operand (mask_reg, X, X, X, X)); else i915_fs_mov (out_reg, i915_fs_operand_zero ()); } else { i915_fs_mov (out_reg, i915_fs_operand_impure (mask_reg, X, source_pure)); } } else { /* XXX ComponentAlpha i915_fs_mov (out_reg, i915_fs_operand_pure (mask_reg, shader->source.solid.pure)); */ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { if (source_pure & (1 << 3)) i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W)); else i915_fs_mov (out_reg, i915_fs_operand_zero ()); } else { i915_fs_mov (out_reg, i915_fs_operand_impure (mask_reg, W, source_pure)); } } source_reg = out_reg; } else if (shader->mask.type.fragment == FS_SPANS) { i915_fs_mov (out_reg, i915_fs_operand (mask_reg, X, X, X, X)); source_reg = out_reg; } else { source_reg = mask_reg; } } else { if (shader->mask.type.fragment == FS_SPANS) { if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { i915_fs_mul (out_reg, i915_fs_operand (source_reg, W, W, W, W), i915_fs_operand (mask_reg, X, X, X, X)); } else { i915_fs_mul (out_reg, i915_fs_operand_reg (source_reg), i915_fs_operand (mask_reg, X, X, X, X)); } } else { /* XXX ComponentAlpha i915_fs_mul (FS_R0, i915_fs_operand_reg (source_reg), i915_fs_operand_reg (mask_reg)); */ if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { i915_fs_mul (out_reg, i915_fs_operand (source_reg, W, W, W, W), i915_fs_operand (mask_reg, W, W, W, W)); } else { i915_fs_mul (out_reg, i915_fs_operand_reg (source_reg), i915_fs_operand (mask_reg, W, W, W, W)); } } source_reg = out_reg; } } if (shader->opacity < 1.) { i915_fs_mul (source_reg, i915_fs_operand_reg (source_reg), i915_fs_operand_reg (FS_C0 + constant_offset)); constant_offset++; } /* need to preserve order of src, mask, clip, dst */ mask_reg = ~0; if (shader->clip.type.fragment == FS_TEXTURE) { i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset); texture_offset += 1; sampler_offset += 1; mask_reg = FS_R1; } if (shader->need_combine) { assert (shader->dst.type.fragment == FS_TEXTURE); i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset); texture_offset += 1; sampler_offset += 1; dest_reg = FS_R2; switch (shader->op) { case CAIRO_OPERATOR_CLEAR: case CAIRO_OPERATOR_SOURCE: ASSERT_NOT_REACHED; case CAIRO_OPERATOR_OVER: if (source_reg == ~0U) { /* XXX shader->source.type.fragment == FS_PURE */ dest_reg = FS_OC; } else { i915_fs_add (FS_U0, i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W), i915_fs_operand_one ()); i915_fs_mul (FS_U0, i915_fs_operand_reg (FS_U0), dest_reg); i915_fs_add (FS_R3, i915_fs_operand_reg (source_reg), i915_fs_operand_reg (FS_U0)); source_reg = FS_R3; } break; case CAIRO_OPERATOR_IN: if (source_reg == ~0U) { /* XXX shader->source.type.fragment == FS_PURE */ source_reg = dest_reg; } else { i915_fs_mul (FS_R3, i915_fs_operand_reg (source_reg), dest_reg); source_reg = FS_R3; } break; case CAIRO_OPERATOR_OUT: if (source_reg == ~0U) { /* XXX shader->source.type.fragment == FS_PURE */ i915_fs_mov (FS_R3, i915_fs_operand_zero ()); source_reg = FS_R3; } else { i915_fs_add (FS_U0, i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W), i915_fs_operand_one ()); i915_fs_mul (FS_R3, i915_fs_operand_reg (FS_U0), dest_reg); source_reg = FS_R3; } break; case CAIRO_OPERATOR_ATOP: case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_DEST_OVER: case CAIRO_OPERATOR_DEST_IN: case CAIRO_OPERATOR_DEST_OUT: case CAIRO_OPERATOR_DEST_ATOP: case CAIRO_OPERATOR_XOR: case CAIRO_OPERATOR_ADD: case CAIRO_OPERATOR_SATURATE: case CAIRO_OPERATOR_MULTIPLY: case CAIRO_OPERATOR_SCREEN: case CAIRO_OPERATOR_OVERLAY: case CAIRO_OPERATOR_DARKEN: case CAIRO_OPERATOR_LIGHTEN: case CAIRO_OPERATOR_COLOR_DODGE: case CAIRO_OPERATOR_COLOR_BURN: case CAIRO_OPERATOR_HARD_LIGHT: case CAIRO_OPERATOR_SOFT_LIGHT: case CAIRO_OPERATOR_DIFFERENCE: case CAIRO_OPERATOR_EXCLUSION: case CAIRO_OPERATOR_HSL_HUE: case CAIRO_OPERATOR_HSL_SATURATION: case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: ASSERT_NOT_REACHED; break; } } if (shader->clip.type.fragment == FS_TEXTURE) { assert (mask_reg != ~0U); if (! shader->need_combine) { /* (source IN clip) */ if (source_reg == ~0U) { if (source_pure == 0) { source_reg = mask_reg; } else { out_reg = FS_OC; if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { if (source_pure & (1 << 3)) i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W)); else i915_fs_mov (out_reg, i915_fs_operand_zero ()); } else { i915_fs_mov (out_reg, i915_fs_operand_impure (mask_reg, W, source_pure)); } source_reg = out_reg; } } else if (mask_reg) { out_reg = FS_OC; if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { i915_fs_mul (out_reg, i915_fs_operand (source_reg, W, W, W, W), i915_fs_operand (mask_reg, W, W, W, W)); } else { i915_fs_mul (out_reg, i915_fs_operand_reg (source_reg), i915_fs_operand (mask_reg, W, W, W, W)); } source_reg = out_reg; } } else { /* (source OP dest) LERP_clip dest */ if (source_reg == ~0U) { if (source_pure == 0) { i915_fs_mov (FS_R3, i915_fs_operand (mask_reg, W, W, W, W)); } else { i915_fs_mov (FS_R3, i915_fs_operand_impure (mask_reg, W, source_pure)); } } else { i915_fs_mul (FS_R3, i915_fs_operand_reg (source_reg), i915_fs_operand (mask_reg, W, W, W, W)); } i915_fs_add (mask_reg, i915_fs_operand_one (), i915_fs_operand (mask_reg, NEG_W, NEG_W, NEG_W, NEG_W)); if (dest_reg != FS_OC) { if (dest_reg == ~0U) { assert (shader->dst.type.fragment == FS_TEXTURE); i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset); texture_offset += 1; sampler_offset += 1; dest_reg = FS_R2; } i915_fs_mul (FS_U1, i915_fs_operand_reg (dest_reg), i915_fs_operand_reg (mask_reg)); mask_reg = FS_U1; } source_reg = FS_OC; if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { i915_fs_add (source_reg, i915_fs_operand (FS_R3, W, W, W, W), i915_fs_operand (mask_reg, W, W, W, W)); } else { i915_fs_add (source_reg, i915_fs_operand_reg (FS_R3), i915_fs_operand_reg (mask_reg)); } } } if (source_reg != FS_OC) { if (source_reg == ~0U) { if (source_pure) { if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { if (source_pure & (1 << 3)) i915_fs_mov (FS_OC, i915_fs_operand_one ()); else i915_fs_mov (FS_OC, i915_fs_operand_zero ()); } else i915_fs_mov (FS_OC, i915_fs_operand_pure (source_pure)); } else { i915_fs_mov (FS_OC, i915_fs_operand_one ()); } } else if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { i915_fs_mov (FS_OC, i915_fs_operand (source_reg, W, W, W, W)); } else { i915_fs_mov (FS_OC, i915_fs_operand_reg (source_reg)); } } FS_END (); } static cairo_bool_t i915_shader_linear_init (struct i915_shader_linear *l, const cairo_linear_pattern_t *linear) { double x0, y0, sf; double dx, dy, offset; dx = linear->pd2.x - linear->pd1.x; dy = linear->pd2.y - linear->pd1.y; sf = dx * dx + dy * dy; if (sf <= 1e-5) return FALSE; dx /= sf; dy /= sf; x0 = linear->pd1.x; y0 = linear->pd1.y; offset = dx*x0 + dy*y0; if (_cairo_matrix_is_identity (&linear->base.base.matrix)) { l->dx = dx; l->dy = dy; l->offset = -offset; } else { cairo_matrix_t m; cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0); cairo_matrix_multiply (&m, &linear->base.base.matrix, &m); l->dx = m.xx; l->dy = m.xy; l->offset = m.x0; } return TRUE; } static cairo_bool_t i915_shader_linear_contains_rectangle (struct i915_shader_linear *l, const cairo_rectangle_int_t *extents) { double v; v = i915_shader_linear_texcoord (l, extents->x, extents->y); if (v < 0.) return FALSE; if (v > 1.) return FALSE; v = i915_shader_linear_texcoord (l, extents->x + extents->width, extents->y); if (v < 0.) return FALSE; if (v > 1.) return FALSE; v = i915_shader_linear_texcoord (l, extents->x, extents->y + extents->height); if (v < 0.) return FALSE; if (v > 1.) return FALSE; v = i915_shader_linear_texcoord (l, extents->x + extents->width, extents->y + extents->height); if (v < 0.) return FALSE; if (v > 1.) return FALSE; return TRUE; } #define is_pure(C,mask) (((mask) == 0) || (C) <= 0x00ff || (C) >= 0xff00) #define is_one(C,mask) (((mask) != 0) && (C) >= 0xff00) #define is_zero(C,mask) (((mask) != 0) && (C) <= 0x00ff) static cairo_status_t i915_shader_acquire_solid (i915_shader_t *shader, union i915_shader_channel *src, const cairo_solid_pattern_t *solid, const cairo_rectangle_int_t *extents) { cairo_content_t content; content = CAIRO_CONTENT_COLOR_ALPHA; src->solid.color = solid->color; if (content == 0 || solid->color.alpha_short <= 0x00ff) { src->base.content = CAIRO_CONTENT_ALPHA; src->type.fragment = FS_ZERO; } else if ((((content & CAIRO_CONTENT_COLOR) == 0) || (solid->color.red_short >= 0xff00 && solid->color.green_short >= 0xff00 && solid->color.blue_short >= 0xff00)) && ((content & CAIRO_CONTENT_ALPHA) == 0 || solid->color.alpha_short >= 0xff00)) { src->base.content = CAIRO_CONTENT_ALPHA; src->type.fragment = FS_ONE; } else if (is_pure (solid->color.red_short, content & CAIRO_CONTENT_COLOR) && is_pure (solid->color.green_short, content & CAIRO_CONTENT_COLOR) && is_pure (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) && is_pure (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) { src->solid.pure = 0; src->solid.pure |= is_one (solid->color.red_short, content & CAIRO_CONTENT_COLOR) << 0; src->solid.pure |= is_one (solid->color.green_short, content & CAIRO_CONTENT_COLOR) << 1; src->solid.pure |= is_one (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) << 2; src->solid.pure |= (! is_zero (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) << 3; if (src->solid.pure == 0) { src->base.content = CAIRO_CONTENT_ALPHA; src->type.fragment = FS_ZERO; } else if (src->solid.pure == 0x7) { src->base.content = CAIRO_CONTENT_ALPHA; src->type.fragment = FS_ONE; } else { src->base.content = content; src->type.fragment = FS_PURE; src->base.mode = src->solid.pure; } } else { src->base.content = content; src->type.fragment = src == &shader->source ? FS_DIFFUSE : FS_CONSTANT; } src->type.vertex = src->type.fragment == FS_ZERO ? VS_ZERO : VS_CONSTANT; src->type.pattern = PATTERN_CONSTANT; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_shader_acquire_linear (i915_shader_t *shader, union i915_shader_channel *src, const cairo_linear_pattern_t *linear, const cairo_rectangle_int_t *extents) { cairo_bool_t mode = LINEAR_TEXTURE; cairo_status_t status; if (i915_shader_linear_init (&src->linear, linear) && linear->base.n_stops == 2 && linear->base.stops[0].offset == 0.0 && linear->base.stops[1].offset == 1.0) { if (i915_shader_linear_contains_rectangle (&src->linear, extents)) { /* XXX can also lerp if contained within offset range */ mode = LINEAR_NONE; } else switch (linear->base.base.extend) { case CAIRO_EXTEND_REPEAT: mode = LINEAR_REPEAT; break; case CAIRO_EXTEND_PAD: mode = LINEAR_PAD; break; case CAIRO_EXTEND_NONE: break; case CAIRO_EXTEND_REFLECT: break; default: ASSERT_NOT_REACHED; break; } } src->type.vertex = VS_LINEAR; src->type.pattern = PATTERN_LINEAR; src->base.texfmt = TEXCOORDFMT_1D; src->base.content = CAIRO_CONTENT_COLOR_ALPHA; src->base.mode = mode; if (mode == LINEAR_TEXTURE) { intel_buffer_t buffer; status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device, &linear->base, &buffer); if (unlikely (status)) return status; src->type.fragment = FS_TEXTURE; src->base.bo = intel_bo_reference (buffer.bo); src->base.n_samplers = 1; src->base.offset[0] = buffer.offset; src->base.map[0] = buffer.map0; src->base.map[1] = buffer.map1; src->base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_BILINEAR); src->base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (linear->base.base.extend); } else { src->type.fragment = FS_LINEAR; src->linear.color0.red = linear->base.stops[0].color.red; src->linear.color0.green = linear->base.stops[0].color.green; src->linear.color0.blue = linear->base.stops[0].color.blue; src->linear.color0.alpha = linear->base.stops[0].color.alpha; src->linear.color1.red = linear->base.stops[1].color.red; src->linear.color1.green = linear->base.stops[1].color.green; src->linear.color1.blue = linear->base.stops[1].color.blue; src->linear.color1.alpha = linear->base.stops[1].color.alpha; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_shader_acquire_radial (i915_shader_t *shader, union i915_shader_channel *src, const cairo_radial_pattern_t *radial, const cairo_rectangle_int_t *extents) { intel_buffer_t buffer; cairo_status_t status; status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device, &radial->base, &buffer); if (unlikely (status)) return status; i915_shader_radial_init (&src->radial, radial); src->type.vertex = VS_TEXTURE; src->type.fragment = FS_RADIAL; src->type.pattern = PATTERN_RADIAL; src->base.texfmt = TEXCOORDFMT_2D; src->base.content = CAIRO_CONTENT_COLOR_ALPHA; src->base.bo = intel_bo_reference (buffer.bo); src->base.n_samplers = 1; src->base.offset[0] = buffer.offset; src->base.map[0] = buffer.map0; src->base.map[1] = buffer.map1; src->base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_BILINEAR); src->base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (radial->base.base.extend); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_surface_clone (i915_device_t *device, cairo_image_surface_t *image, i915_surface_t **clone_out) { i915_surface_t *clone; cairo_status_t status; #if 0 clone = i915_surface_create_from_cacheable_image_internal (device, image); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; #else cairo_format_t format; format = image->format; if (format == CAIRO_FORMAT_A1) format = CAIRO_FORMAT_A8; clone = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, format, image->width, image->height, I915_TILING_DEFAULT, FALSE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; status = intel_bo_put_image (&device->intel, to_intel_bo (clone->intel.drm.bo), image, 0, 0, image->width, image->height, 0, 0); if (unlikely (status)) return status; #endif *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_surface_clone_subimage (i915_device_t *device, cairo_image_surface_t *image, const cairo_rectangle_int_t *extents, i915_surface_t **clone_out) { i915_surface_t *clone; cairo_status_t status; cairo_format_t format; format = image->format; if (format == CAIRO_FORMAT_A1) format = CAIRO_FORMAT_A8; clone = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, format, extents->width, extents->height, I915_TILING_NONE, FALSE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; status = intel_bo_put_image (&device->intel, to_intel_bo (clone->intel.drm.bo), image, extents->x, extents->y, extents->width, extents->height, 0, 0); if (unlikely (status)) return status; *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_surface_render_pattern (i915_device_t *device, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, i915_surface_t **clone_out) { i915_surface_t *clone; cairo_surface_t *image; cairo_status_t status; void *ptr; clone = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, _cairo_format_from_content (pattern->surface->content), extents->width, extents->height, I915_TILING_NONE, FALSE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; ptr = intel_bo_map (&device->intel, to_intel_bo (clone->intel.drm.bo)); if (unlikely (ptr == NULL)) { cairo_surface_destroy (&clone->intel.drm.base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } image = cairo_image_surface_create_for_data (ptr, clone->intel.drm.format, clone->intel.drm.width, clone->intel.drm.height, clone->intel.drm.stride); if (unlikely (image->status)) { cairo_surface_destroy (&clone->intel.drm.base); return image->status; } status = _cairo_surface_offset_paint (image, extents->x, extents->y, CAIRO_OPERATOR_SOURCE, &pattern->base, NULL); cairo_surface_destroy (image); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_shader_acquire_solid_surface (i915_shader_t *shader, union i915_shader_channel *src, cairo_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_surface_pattern_t pattern; cairo_surface_t *pixel; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; uint32_t argb; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (unlikely (status)) return status; /* extract the pixel as argb32 */ pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); _cairo_pattern_init_for_surface (&pattern, &image->base); cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); _cairo_surface_release_source_image (surface, image, image_extra); if (unlikely (status)) { cairo_surface_destroy (pixel); return status; } image = (cairo_image_surface_t *) pixel; argb = *(uint32_t *) image->data; cairo_surface_destroy (pixel); if (argb >> 24 == 0) { _cairo_color_init_rgba (&src->solid.color, 0, 0, 0, 0); } else { uint8_t alpha = argb >> 24; _cairo_color_init_rgba (&src->solid.color, ((((argb >> 16) & 0xff) * 255 + alpha / 2) / alpha) / 255., ((((argb >> 8) & 0xff) * 255 + alpha / 2) / alpha) / 255., ((((argb >> 0) & 0xff) * 255 + alpha / 2) / alpha) / 255., alpha / 255.); } src->base.content = CAIRO_CONTENT_COLOR_ALPHA; src->type.fragment = FS_CONSTANT; src->type.vertex = VS_CONSTANT; src->type.pattern = PATTERN_CONSTANT; return CAIRO_STATUS_SUCCESS; } static cairo_filter_t sampled_area (const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, cairo_rectangle_int_t *sample) { cairo_rectangle_int_t surface_extents; cairo_filter_t filter; double x1, x2, y1, y2; double pad; x1 = extents->x; y1 = extents->y; x2 = extents->x + (int) extents->width; y2 = extents->y + (int) extents->height; if (_cairo_matrix_is_translation (&pattern->base.matrix)) { x1 += pattern->base.matrix.x0; x2 += pattern->base.matrix.x0; y1 += pattern->base.matrix.y0; y2 += pattern->base.matrix.y0; } else { _cairo_matrix_transform_bounding_box (&pattern->base.matrix, &x1, &y1, &x2, &y2, NULL); } filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); sample->x = floor (x1 - pad); sample->y = floor (y1 - pad); sample->width = ceil (x2 + pad) - sample->x; sample->height = ceil (y2 + pad) - sample->y; if (_cairo_surface_get_extents (pattern->surface, &surface_extents)) _cairo_rectangle_intersect (sample, &surface_extents); return filter; } static cairo_status_t i915_shader_acquire_surface (i915_shader_t *shader, union i915_shader_channel *src, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents) { int surface_width, surface_height; cairo_surface_t *surface, *drm; cairo_extend_t extend; cairo_filter_t filter; cairo_matrix_t m; int src_x = 0, src_y = 0; cairo_surface_t *free_me = NULL; cairo_status_t status; cairo_rectangle_int_t sample; assert (src->type.fragment == (i915_fragment_shader_t) -1); drm = surface = pattern->surface; extend = pattern->base.extend; src->base.matrix = pattern->base.matrix; filter = sampled_area (pattern, extents, &sample); if (surface->type == CAIRO_SURFACE_TYPE_DRM) { if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { drm = ((cairo_surface_subsurface_t *) surface)->target; } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { drm = ((cairo_surface_snapshot_t *) surface)->target; } } if (drm->type == CAIRO_SURFACE_TYPE_DRM) { i915_surface_t *s = (i915_surface_t *) drm; if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { if (s->intel.drm.base.device == shader->target->intel.drm.base.device && s != shader->target) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface; int x; status = i915_surface_fallback_flush (s); if (unlikely (status)) return status; /* XXX blt subimage and cache snapshot */ if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) { /* XXX pipelined flush of RENDER/TEXTURE cache */ } src->type.fragment = FS_TEXTURE; src->surface.pixel = NONE; surface_width = sub->extents.width; surface_height = sub->extents.height; src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); src->base.n_samplers = 1; x = sub->extents.x; if (s->intel.drm.format != CAIRO_FORMAT_A8) x *= 4; /* XXX tiling restrictions upon offset? */ src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x; src->base.map[0] = s->map0; src->base.map[0] &= ~((2047 << MS3_HEIGHT_SHIFT) | (2047 << MS3_WIDTH_SHIFT)); src->base.map[0] |= ((sub->extents.height - 1) << MS3_HEIGHT_SHIFT) | ((sub->extents.width - 1) << MS3_WIDTH_SHIFT); src->base.map[1] = (s->intel.drm.stride / 4 - 1) << MS4_PITCH_SHIFT; } } else { /* XXX if s == shader->dst allow if FILTER_NEAREST, EXTEND_NONE? */ if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { status = i915_surface_fallback_flush (s); if (unlikely (status)) return status; if (s == shader->target || i915_surface_needs_tiling (s)) { status = i915_surface_copy_subimage (i915_device (shader->target), s, &sample, TRUE, &s); if (unlikely (status)) return status; free_me = drm = &s->intel.drm.base; } src->type.fragment = FS_TEXTURE; src->surface.pixel = NONE; surface_width = s->intel.drm.width; surface_height = s->intel.drm.height; src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); src->base.n_samplers = 1; src->base.offset[0] = s->offset; src->base.map[0] = s->map0; src->base.map[1] = s->map1; } } } if (src->type.fragment == (i915_fragment_shader_t) -1) { i915_surface_t *s; if (extents->width == 1 && extents->height == 1) { return i915_shader_acquire_solid_surface (shader, src, surface, extents); } s = (i915_surface_t *) _cairo_surface_has_snapshot (surface, shader->target->intel.drm.base.backend); if (s == NULL) { cairo_status_t status; #if 0 /* XXX hackity hack hack */ status = i915_clone_yuv (surface, src, image->width, image->height, clone_out); #endif if (sample.width > 2048 || sample.height > 2048) { status = i915_surface_render_pattern (i915_device (shader->target), pattern, extents, &s); if (unlikely (status)) return status; extend = CAIRO_EXTEND_NONE; filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&src->base.matrix, -extents->x, -extents->y); } else { cairo_image_surface_t *image; void *image_extra; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (unlikely (status)) return status; if (image->width < 2048 && image->height < 2048 && sample.width >= image->width / 4 && sample.height >= image->height /4) { status = i915_surface_clone (i915_device (shader->target), image, &s); if (likely (status == CAIRO_STATUS_SUCCESS)) { _cairo_surface_attach_snapshot (surface, &s->intel.drm.base, intel_surface_detach_snapshot); status = intel_snapshot_cache_insert (&i915_device (shader->target)->intel, &s->intel); if (unlikely (status)) { cairo_surface_finish (&s->intel.drm.base); cairo_surface_destroy (&s->intel.drm.base); } } } else { status = i915_surface_clone_subimage (i915_device (shader->target), image, &sample, &s); src_x = -extents->x; src_y = -extents->y; } _cairo_surface_release_source_image (surface, image, image_extra); if (unlikely (status)) return status; } free_me = &s->intel.drm.base; } src->type.fragment = FS_TEXTURE; src->surface.pixel = NONE; src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); src->base.n_samplers = 1; src->base.offset[0] = s->offset; src->base.map[0] = s->map0; src->base.map[1] = s->map1; drm = &s->intel.drm.base; surface_width = s->intel.drm.width; surface_height = s->intel.drm.height; } /* XXX transform nx1 or 1xn surfaces to 1D */ src->type.pattern = PATTERN_TEXTURE; if (extend != CAIRO_EXTEND_NONE && sample.x >= 0 && sample.y >= 0 && sample.x + sample.width <= surface_width && sample.y + sample.height <= surface_height) { extend = CAIRO_EXTEND_NONE; } if (extend == CAIRO_EXTEND_NONE) { src->type.vertex = VS_TEXTURE_16; src->base.texfmt = TEXCOORDFMT_2D_16; } else { src->type.vertex = VS_TEXTURE; src->base.texfmt = TEXCOORDFMT_2D; } src->base.content = drm->content; src->base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (filter); src->base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (extend); /* tweak the src matrix to map from dst to texture coordinates */ if (src_x | src_y) cairo_matrix_translate (&src->base.matrix, src_x, src_x); cairo_matrix_init_scale (&m, 1. / surface_width, 1. / surface_height); cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m); if (free_me != NULL) cairo_surface_destroy (free_me); return CAIRO_STATUS_SUCCESS; } cairo_status_t i915_shader_acquire_pattern (i915_shader_t *shader, union i915_shader_channel *src, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return i915_shader_acquire_solid (shader, src, (cairo_solid_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_LINEAR: return i915_shader_acquire_linear (shader, src, (cairo_linear_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_RADIAL: return i915_shader_acquire_radial (shader, src, (cairo_radial_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_SURFACE: return i915_shader_acquire_surface (shader, src, (cairo_surface_pattern_t *) pattern, extents); default: ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; } } static uint32_t i915_get_blend (cairo_operator_t op, i915_surface_t *dst) { #define SBLEND(X) ((BLENDFACT_##X) << S6_CBUF_SRC_BLEND_FACT_SHIFT) #define DBLEND(X) ((BLENDFACT_##X) << S6_CBUF_DST_BLEND_FACT_SHIFT) static const struct blendinfo { cairo_bool_t dst_alpha; uint32_t src_blend; uint32_t dst_blend; enum { BOUNDED, SIMPLE, XRENDER, } kind; } i915_blend_op[] = { {0, SBLEND (ZERO), DBLEND (ZERO), BOUNDED}, /* Clear */ {0, SBLEND (ONE), DBLEND (ZERO), BOUNDED}, /* Src */ {0, SBLEND (ONE), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Over */ {1, SBLEND (DST_ALPHA), DBLEND (ZERO), XRENDER}, /* In */ {1, SBLEND (INV_DST_ALPHA), DBLEND (ZERO), XRENDER}, /* Out */ {1, SBLEND (DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Atop */ {0, SBLEND (ZERO), DBLEND (ONE), SIMPLE}, /* Dst */ {1, SBLEND (INV_DST_ALPHA), DBLEND (ONE), SIMPLE}, /* OverReverse */ {0, SBLEND (ZERO), DBLEND (SRC_ALPHA), XRENDER}, /* InReverse */ {0, SBLEND (ZERO), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* OutReverse */ {1, SBLEND (INV_DST_ALPHA), DBLEND (SRC_ALPHA), XRENDER}, /* AtopReverse */ {1, SBLEND (INV_DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Xor */ {0, SBLEND (ONE), DBLEND (ONE), SIMPLE}, /* Add */ //{0, 0, SBLEND (SRC_ALPHA_SATURATE), DBLEND (ONE), SIMPLE}, /* XXX Saturate */ }; uint32_t sblend, dblend; if (op >= ARRAY_LENGTH (i915_blend_op)) return 0; if (i915_blend_op[op].kind == BOUNDED) return 0; sblend = i915_blend_op[op].src_blend; dblend = i915_blend_op[op].dst_blend; /* If there's no dst alpha channel, adjust the blend op so that we'll treat * it as always 1. */ if ((dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA) == 0 && i915_blend_op[op].dst_alpha) { if (sblend == SBLEND (DST_ALPHA)) sblend = SBLEND (ONE); else if (sblend == SBLEND (INV_DST_ALPHA)) sblend = SBLEND (ZERO); } /* i915 engine reads 8bit color buffer into green channel in cases like color buffer blending etc., and also writes back green channel. So with dst_alpha blend we should use color factor. See spec on "8-bit rendering" */ if (dst->intel.drm.format == CAIRO_FORMAT_A8 && i915_blend_op[op].dst_alpha) { if (sblend == SBLEND (DST_ALPHA)) sblend = SBLEND (DST_COLR); else if (sblend == SBLEND (INV_DST_ALPHA)) sblend = SBLEND (INV_DST_COLR); } return sblend | dblend; #undef SBLEND #undef DBLEND } static void i915_shader_channel_init (union i915_shader_channel *channel) { channel->type.vertex = (i915_vertex_shader_t) -1; channel->type.fragment = (i915_fragment_shader_t) -1; channel->type.pattern = (i915_shader_channel_t) -1; channel->base.texfmt = TEXCOORDFMT_NOT_PRESENT; channel->base.bo = NULL; channel->base.n_samplers = 0; channel->base.mode = 0; } static void i915_shader_channel_fini (i915_device_t *device, union i915_shader_channel *channel) { switch (channel->type.pattern) { case PATTERN_TEXTURE: case PATTERN_BASE: case PATTERN_LINEAR: case PATTERN_RADIAL: if (channel->base.bo != NULL) intel_bo_destroy (&device->intel, channel->base.bo); break; default: case PATTERN_CONSTANT: break; } } static void i915_shader_channel_reset (i915_device_t *device, union i915_shader_channel *channel) { i915_shader_channel_fini (device, channel); i915_shader_channel_init (channel); } void i915_shader_init (i915_shader_t *shader, i915_surface_t *dst, cairo_operator_t op, double opacity) { shader->committed = FALSE; shader->device = i915_device (dst); shader->target = dst; shader->op = op; shader->opacity = opacity; shader->blend = i915_get_blend (op, dst); shader->need_combine = FALSE; shader->content = dst->intel.drm.base.content; i915_shader_channel_init (&shader->source); i915_shader_channel_init (&shader->mask); i915_shader_channel_init (&shader->clip); i915_shader_channel_init (&shader->dst); } static void i915_set_shader_samplers (i915_device_t *device, const i915_shader_t *shader) { uint32_t n_samplers, n_maps, n; uint32_t samplers[2*4]; uint32_t maps[4*4]; uint32_t mask, s, m; n_maps = shader->source.base.n_samplers + shader->mask.base.n_samplers + shader->clip.base.n_samplers + shader->dst.base.n_samplers; assert (n_maps <= 4); if (n_maps == 0) return; n_samplers = !! shader->source.base.bo + !! shader->mask.base.bo + !! shader->clip.base.bo + !! shader->dst.base.bo; mask = (1 << n_maps) - 1; /* We check for repeated setting of sample state mainly to catch * continuation of text strings across multiple show-glyphs. */ s = m = 0; if (shader->source.base.bo != NULL) { samplers[s++] = shader->source.base.sampler[0]; samplers[s++] = shader->source.base.sampler[1]; maps[m++] = shader->source.base.bo->base.handle; for (n = 0; n < shader->source.base.n_samplers; n++) { maps[m++] = shader->source.base.offset[n]; maps[m++] = shader->source.base.map[2*n+0]; maps[m++] = shader->source.base.map[2*n+1]; } } if (shader->mask.base.bo != NULL) { samplers[s++] = shader->mask.base.sampler[0]; samplers[s++] = shader->mask.base.sampler[1]; maps[m++] = shader->mask.base.bo->base.handle; for (n = 0; n < shader->mask.base.n_samplers; n++) { maps[m++] = shader->mask.base.offset[n]; maps[m++] = shader->mask.base.map[2*n+0]; maps[m++] = shader->mask.base.map[2*n+1]; } } if (shader->clip.base.bo != NULL) { samplers[s++] = shader->clip.base.sampler[0]; samplers[s++] = shader->clip.base.sampler[1]; maps[m++] = shader->clip.base.bo->base.handle; for (n = 0; n < shader->clip.base.n_samplers; n++) { maps[m++] = shader->clip.base.offset[n]; maps[m++] = shader->clip.base.map[2*n+0]; maps[m++] = shader->clip.base.map[2*n+1]; } } if (shader->dst.base.bo != NULL) { samplers[s++] = shader->dst.base.sampler[0]; samplers[s++] = shader->dst.base.sampler[1]; maps[m++] = shader->dst.base.bo->base.handle; for (n = 0; n < shader->dst.base.n_samplers; n++) { maps[m++] = shader->dst.base.offset[n]; maps[m++] = shader->dst.base.map[2*n+0]; maps[m++] = shader->dst.base.map[2*n+1]; } } if (n_maps > device->current_n_maps || memcmp (device->current_maps, maps, m * sizeof (uint32_t))) { memcpy (device->current_maps, maps, m * sizeof (uint32_t)); device->current_n_maps = n_maps; if (device->current_source != NULL) *device->current_source = 0; if (device->current_mask != NULL) *device->current_mask = 0; if (device->current_clip != NULL) *device->current_clip = 0; #if 0 if (shader->source.type.pattern == PATTERN_TEXTURE) { switch ((int) shader->source.surface.surface->type) { case CAIRO_SURFACE_TYPE_DRM: { i915_surface_t *surface = (i915_surface_t *) shader->source.surface.surface; device->current_source = &surface->is_current_texture; surface->is_current_texture |= CURRENT_SOURCE; break; } case I915_PACKED_PIXEL_SURFACE_TYPE: { i915_packed_pixel_surface_t *surface = (i915_packed_pixel_surface_t *) shader->source.surface.surface; device->current_source = &surface->is_current_texture; surface->is_current_texture |= CURRENT_SOURCE; break; } default: device->current_source = NULL; break; } } else device->current_source = NULL; if (shader->mask.type.pattern == PATTERN_TEXTURE) { switch ((int) shader->mask.surface.surface->type) { case CAIRO_SURFACE_TYPE_DRM: { i915_surface_t *surface = (i915_surface_t *) shader->mask.surface.surface; device->current_mask = &surface->is_current_texture; surface->is_current_texture |= CURRENT_MASK; break; } case I915_PACKED_PIXEL_SURFACE_TYPE: { i915_packed_pixel_surface_t *surface = (i915_packed_pixel_surface_t *) shader->mask.surface.surface; device->current_mask = &surface->is_current_texture; surface->is_current_texture |= CURRENT_MASK; break; } default: device->current_mask = NULL; break; } } else device->current_mask = NULL; #endif OUT_DWORD (_3DSTATE_MAP_STATE | (3 * n_maps)); OUT_DWORD (mask); for (n = 0; n < shader->source.base.n_samplers; n++) { i915_batch_emit_reloc (device, shader->source.base.bo, shader->source.base.offset[n], I915_GEM_DOMAIN_SAMPLER, 0, FALSE); OUT_DWORD (shader->source.base.map[2*n+0]); OUT_DWORD (shader->source.base.map[2*n+1]); } for (n = 0; n < shader->mask.base.n_samplers; n++) { i915_batch_emit_reloc (device, shader->mask.base.bo, shader->mask.base.offset[n], I915_GEM_DOMAIN_SAMPLER, 0, FALSE); OUT_DWORD (shader->mask.base.map[2*n+0]); OUT_DWORD (shader->mask.base.map[2*n+1]); } for (n = 0; n < shader->clip.base.n_samplers; n++) { i915_batch_emit_reloc (device, shader->clip.base.bo, shader->clip.base.offset[n], I915_GEM_DOMAIN_SAMPLER, 0, FALSE); OUT_DWORD (shader->clip.base.map[2*n+0]); OUT_DWORD (shader->clip.base.map[2*n+1]); } for (n = 0; n < shader->dst.base.n_samplers; n++) { i915_batch_emit_reloc (device, shader->dst.base.bo, shader->dst.base.offset[n], I915_GEM_DOMAIN_SAMPLER, 0, FALSE); OUT_DWORD (shader->dst.base.map[2*n+0]); OUT_DWORD (shader->dst.base.map[2*n+1]); } } if (n_samplers > device->current_n_samplers || memcmp (device->current_samplers, samplers, s * sizeof (uint32_t))) { device->current_n_samplers = s; memcpy (device->current_samplers, samplers, s * sizeof (uint32_t)); OUT_DWORD (_3DSTATE_SAMPLER_STATE | (3 * n_maps)); OUT_DWORD (mask); s = 0; for (n = 0; n < shader->source.base.n_samplers; n++) { OUT_DWORD (shader->source.base.sampler[0]); OUT_DWORD (shader->source.base.sampler[1] | (s << SS3_TEXTUREMAP_INDEX_SHIFT)); OUT_DWORD (0x0); s++; } for (n = 0; n < shader->mask.base.n_samplers; n++) { OUT_DWORD (shader->mask.base.sampler[0]); OUT_DWORD (shader->mask.base.sampler[1] | (s << SS3_TEXTUREMAP_INDEX_SHIFT)); OUT_DWORD (0x0); s++; } for (n = 0; n < shader->clip.base.n_samplers; n++) { OUT_DWORD (shader->clip.base.sampler[0]); OUT_DWORD (shader->clip.base.sampler[1] | (s << SS3_TEXTUREMAP_INDEX_SHIFT)); OUT_DWORD (0x0); s++; } for (n = 0; n < shader->dst.base.n_samplers; n++) { OUT_DWORD (shader->dst.base.sampler[0]); OUT_DWORD (shader->dst.base.sampler[1] | (s << SS3_TEXTUREMAP_INDEX_SHIFT)); OUT_DWORD (0x0); s++; } } } static uint32_t i915_shader_get_texcoords (const i915_shader_t *shader) { uint32_t texcoords; uint32_t tu; texcoords = S2_TEXCOORD_NONE; tu = 0; if (shader->source.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); texcoords |= S2_TEXCOORD_FMT (tu, shader->source.base.texfmt); tu++; } if (shader->mask.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); texcoords |= S2_TEXCOORD_FMT (tu, shader->mask.base.texfmt); tu++; } if (shader->clip.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); texcoords |= S2_TEXCOORD_FMT (tu, shader->clip.base.texfmt); tu++; } if (shader->dst.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); texcoords |= S2_TEXCOORD_FMT (tu, shader->dst.base.texfmt); tu++; } return texcoords; } static void i915_set_shader_mode (i915_device_t *device, const i915_shader_t *shader) { uint32_t texcoords; uint32_t mask, cnt; texcoords = i915_shader_get_texcoords (shader); mask = cnt = 0; if (device->current_texcoords != texcoords) mask |= I1_LOAD_S (2), cnt++; if (device->current_blend != shader->blend) mask |= I1_LOAD_S (6), cnt++; if (cnt == 0) return; OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | mask | (cnt-1)); if (device->current_texcoords != texcoords) { OUT_DWORD (texcoords); device->current_texcoords = texcoords; } if (device->current_blend != shader->blend) { if (shader->blend) { OUT_DWORD (S6_CBUF_BLEND_ENABLE | S6_COLOR_WRITE_ENABLE | (BLENDFUNC_ADD << S6_CBUF_BLEND_FUNC_SHIFT) | shader->blend); } else { OUT_DWORD (S6_COLOR_WRITE_ENABLE); } device->current_blend = shader->blend; } } static void i915_set_constants (i915_device_t *device, const uint32_t *constants, uint32_t n_constants) { uint32_t n; OUT_DWORD (_3DSTATE_PIXEL_SHADER_CONSTANTS | n_constants); OUT_DWORD ((1 << (n_constants >> 2)) - 1); for (n = 0; n < n_constants; n++) OUT_DWORD (constants[n]); device->current_n_constants = n_constants; memcpy (device->current_constants, constants, n_constants*4); } static uint32_t pack_constants (const union i915_shader_channel *channel, uint32_t *constants) { uint32_t count = 0, n; switch (channel->type.fragment) { case FS_ZERO: case FS_ONE: case FS_PURE: case FS_DIFFUSE: break; case FS_CONSTANT: constants[count++] = pack_float (channel->solid.color.red); constants[count++] = pack_float (channel->solid.color.green); constants[count++] = pack_float (channel->solid.color.blue); constants[count++] = pack_float (channel->solid.color.alpha); break; case FS_LINEAR: constants[count++] = pack_float (channel->linear.color0.red); constants[count++] = pack_float (channel->linear.color0.green); constants[count++] = pack_float (channel->linear.color0.blue); constants[count++] = pack_float (channel->linear.color0.alpha); constants[count++] = pack_float (channel->linear.color1.red); constants[count++] = pack_float (channel->linear.color1.green); constants[count++] = pack_float (channel->linear.color1.blue); constants[count++] = pack_float (channel->linear.color1.alpha); break; case FS_RADIAL: for (n = 0; n < ARRAY_LENGTH (channel->radial.constants); n++) constants[count++] = pack_float (channel->radial.constants[n]); break; case FS_TEXTURE: case FS_YUV: case FS_SPANS: break; } return count; } static void i915_set_shader_constants (i915_device_t *device, const i915_shader_t *shader) { uint32_t constants[4*4*3+4]; unsigned n_constants; n_constants = 0; if (shader->source.type.fragment == FS_DIFFUSE) { uint32_t diffuse; diffuse = ((shader->source.solid.color.alpha_short >> 8) << 24) | ((shader->source.solid.color.red_short >> 8) << 16) | ((shader->source.solid.color.green_short >> 8) << 8) | ((shader->source.solid.color.blue_short >> 8) << 0); if (diffuse != device->current_diffuse) { OUT_DWORD (_3DSTATE_DFLT_DIFFUSE_CMD); OUT_DWORD (diffuse); device->current_diffuse = diffuse; } } else { n_constants += pack_constants (&shader->source, constants + n_constants); } n_constants += pack_constants (&shader->mask, constants + n_constants); if (shader->opacity < 1.) { constants[n_constants+0] = constants[n_constants+1] = constants[n_constants+2] = constants[n_constants+3] = pack_float (shader->opacity); n_constants += 4; } if (n_constants != 0 && (device->current_n_constants != n_constants || memcmp (device->current_constants, constants, n_constants*4))) { i915_set_constants (device, constants, n_constants); } } static cairo_bool_t i915_shader_needs_update (const i915_shader_t *shader, const i915_device_t *device) { uint32_t count, n; uint32_t buf[64]; if (device->current_target != shader->target) return TRUE; count = !! shader->source.base.bo + !! shader->mask.base.bo + !! shader->clip.base.bo + !! shader->dst.base.bo; if (count > device->current_n_samplers) return TRUE; count = shader->source.base.n_samplers + shader->mask.base.n_samplers + shader->clip.base.n_samplers + shader->dst.base.n_samplers; if (count > device->current_n_maps) return TRUE; if (count) { count = 0; if (shader->source.base.bo != NULL) { buf[count++] = shader->source.base.sampler[0]; buf[count++] = shader->source.base.sampler[1]; } if (shader->mask.base.bo != NULL) { buf[count++] = shader->mask.base.sampler[0]; buf[count++] = shader->mask.base.sampler[1]; } if (shader->clip.base.bo != NULL) { buf[count++] = shader->clip.base.sampler[0]; buf[count++] = shader->clip.base.sampler[1]; } if (shader->dst.base.bo != NULL) { buf[count++] = shader->dst.base.sampler[0]; buf[count++] = shader->dst.base.sampler[1]; } if (memcmp (device->current_samplers, buf, count * sizeof (uint32_t))) return TRUE; count = 0; if (shader->source.base.bo != NULL) { buf[count++] = shader->source.base.bo->base.handle; for (n = 0; n < shader->source.base.n_samplers; n++) { buf[count++] = shader->source.base.offset[n]; buf[count++] = shader->source.base.map[2*n+0]; buf[count++] = shader->source.base.map[2*n+1]; } } if (shader->mask.base.bo != NULL) { buf[count++] = shader->mask.base.bo->base.handle; for (n = 0; n < shader->mask.base.n_samplers; n++) { buf[count++] = shader->mask.base.offset[n]; buf[count++] = shader->mask.base.map[2*n+0]; buf[count++] = shader->mask.base.map[2*n+1]; } } if (shader->clip.base.bo != NULL) { buf[count++] = shader->clip.base.bo->base.handle; for (n = 0; n < shader->clip.base.n_samplers; n++) { buf[count++] = shader->clip.base.offset[n]; buf[count++] = shader->clip.base.map[2*n+0]; buf[count++] = shader->clip.base.map[2*n+1]; } } if (shader->dst.base.bo != NULL) { buf[count++] = shader->dst.base.bo->base.handle; for (n = 0; n < shader->dst.base.n_samplers; n++) { buf[count++] = shader->dst.base.offset[n]; buf[count++] = shader->dst.base.map[2*n+0]; buf[count++] = shader->dst.base.map[2*n+1]; } } if (memcmp (device->current_maps, buf, count * sizeof (uint32_t))) return TRUE; } if (i915_shader_get_texcoords (shader) != device->current_texcoords) return TRUE; if (device->current_blend != shader->blend) return TRUE; count = 0; if (shader->source.type.fragment == FS_DIFFUSE) { uint32_t diffuse; diffuse = ((shader->source.solid.color.alpha_short >> 8) << 24) | ((shader->source.solid.color.red_short >> 8) << 16) | ((shader->source.solid.color.green_short >> 8) << 8) | ((shader->source.solid.color.blue_short >> 8) << 0); if (diffuse != device->current_diffuse) return TRUE; } else { count += pack_constants (&shader->source, buf + count); } count += pack_constants (&shader->mask, buf + count); if (count && (device->current_n_constants != count || memcmp (device->current_constants, buf, count*4))) { return TRUE; } n = (i915_shader_channel_key (&shader->source) << 0) | (i915_shader_channel_key (&shader->mask) << 8) | (i915_shader_channel_key (&shader->clip) << 16) | (shader->op << 24) | ((shader->opacity < 1.) << 30) | (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31); return n != device->current_program; } void i915_set_dst (i915_device_t *device, i915_surface_t *dst) { uint32_t size; if (device->current_target != dst) { intel_bo_t *bo; bo = to_intel_bo (dst->intel.drm.bo); assert (bo != NULL); OUT_DWORD (_3DSTATE_BUF_INFO_CMD); OUT_DWORD (BUF_3D_ID_COLOR_BACK | BUF_tiling (bo->tiling) | BUF_3D_PITCH (dst->intel.drm.stride)); OUT_RELOC (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); device->current_target = dst; } if (dst->colorbuf != device->current_colorbuf) { OUT_DWORD (_3DSTATE_DST_BUF_VARS_CMD); OUT_DWORD (dst->colorbuf); device->current_colorbuf = dst->colorbuf; } size = DRAW_YMAX (dst->intel.drm.height) | DRAW_XMAX (dst->intel.drm.width); if (size != device->current_size) { OUT_DWORD (_3DSTATE_DRAW_RECT_CMD); OUT_DWORD (0); /* dither */ OUT_DWORD (0); /* top-left */ OUT_DWORD (size); OUT_DWORD (0); /* origin */ device->current_size = size; } } static void i915_set_shader_target (i915_device_t *device, const i915_shader_t *shader) { i915_set_dst (device, shader->target); } int i915_shader_num_texcoords (const i915_shader_t *shader) { int cnt = 0; switch (shader->source.base.texfmt) { default: ASSERT_NOT_REACHED; case TEXCOORDFMT_NOT_PRESENT: break; case TEXCOORDFMT_2D: cnt += 2; break; case TEXCOORDFMT_3D: cnt += 3; break; case TEXCOORDFMT_4D: cnt += 4; break; case TEXCOORDFMT_1D: cnt += 1; break; case TEXCOORDFMT_2D_16: cnt += 1; break; } switch (shader->mask.base.texfmt) { default: ASSERT_NOT_REACHED; case TEXCOORDFMT_NOT_PRESENT: break; case TEXCOORDFMT_2D: cnt += 2; break; case TEXCOORDFMT_3D: cnt += 3; break; case TEXCOORDFMT_4D: cnt += 4; break; case TEXCOORDFMT_1D: cnt += 1; break; case TEXCOORDFMT_2D_16: cnt += 1; break; } switch (shader->clip.base.texfmt) { default: ASSERT_NOT_REACHED; case TEXCOORDFMT_NOT_PRESENT: break; case TEXCOORDFMT_2D: cnt += 2; break; case TEXCOORDFMT_3D: cnt += 3; break; case TEXCOORDFMT_4D: cnt += 4; break; case TEXCOORDFMT_1D: cnt += 1; break; case TEXCOORDFMT_2D_16: cnt += 1; break; } switch (shader->dst.base.texfmt) { default: ASSERT_NOT_REACHED; case TEXCOORDFMT_NOT_PRESENT: break; case TEXCOORDFMT_2D: cnt += 2; break; case TEXCOORDFMT_3D: cnt += 3; break; case TEXCOORDFMT_4D: cnt += 4; break; case TEXCOORDFMT_1D: cnt += 1; break; case TEXCOORDFMT_2D_16: cnt += 1; break; } return cnt; } void i915_shader_fini (i915_shader_t *shader) { i915_device_t *device = i915_device (shader->target); i915_shader_channel_fini (device, &shader->source); i915_shader_channel_fini (device, &shader->mask); i915_shader_channel_fini (device, &shader->clip); } void i915_shader_set_clip (i915_shader_t *shader, cairo_clip_t *clip) { cairo_surface_t *clip_surface; int clip_x, clip_y; union i915_shader_channel *channel; i915_surface_t *s; clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y); assert (clip_surface->status == CAIRO_STATUS_SUCCESS); assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM); channel = &shader->clip; channel->type.vertex = VS_TEXTURE_16; channel->base.texfmt = TEXCOORDFMT_2D_16; channel->base.content = CAIRO_CONTENT_ALPHA; channel->type.fragment = FS_TEXTURE; channel->surface.pixel = NONE; s = (i915_surface_t *) clip_surface; channel->base.bo = to_intel_bo (s->intel.drm.bo); channel->base.n_samplers = 1; channel->base.offset[0] = s->offset; channel->base.map[0] = s->map0; channel->base.map[1] = s->map1; channel->base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); channel->base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_scale (&shader->clip.base.matrix, 1. / s->intel.drm.width, 1. / s->intel.drm.height); cairo_matrix_translate (&shader->clip.base.matrix, -clip_x, -clip_y); } static cairo_status_t i915_shader_check_aperture (i915_shader_t *shader, i915_device_t *device) { cairo_status_t status; intel_bo_t *bo_array[4]; uint32_t n = 0; if (shader->target != device->current_target) bo_array[n++] = to_intel_bo (shader->target->intel.drm.bo); if (shader->source.base.bo != NULL) bo_array[n++] = shader->source.base.bo; if (shader->mask.base.bo != NULL) bo_array[n++] = shader->mask.base.bo; if (shader->clip.base.bo != NULL) bo_array[n++] = shader->clip.base.bo; if (n == 0 || i915_check_aperture (device, bo_array, n)) return CAIRO_STATUS_SUCCESS; status = i915_batch_flush (device); if (unlikely (status)) return status; assert (i915_check_aperture (device, bo_array, n)); return CAIRO_STATUS_SUCCESS; } static void i915_shader_combine_mask (i915_shader_t *shader, i915_device_t *device) { if (shader->mask.type.fragment == (i915_fragment_shader_t) -1 || shader->mask.type.fragment == FS_CONSTANT) { return; } if (shader->mask.type.fragment == FS_PURE) { if (shader->mask.solid.pure & (1<<3)) { shader->mask.type.fragment = FS_ONE; } else { shader->mask.type.fragment = FS_ZERO; } } if (shader->mask.type.fragment == FS_ONE || (shader->mask.base.content & CAIRO_CONTENT_ALPHA) == 0) { i915_shader_channel_reset (device, &shader->mask); } if (shader->mask.type.fragment == FS_ZERO) { i915_shader_channel_fini (device, &shader->source); shader->source.type.fragment = FS_ZERO; shader->source.type.vertex = VS_ZERO; shader->source.base.texfmt = TEXCOORDFMT_NOT_PRESENT; shader->source.base.mode = 0; shader->source.base.n_samplers = 0; } if (shader->source.type.fragment == FS_ZERO) { i915_shader_channel_reset (device, &shader->mask); i915_shader_channel_reset (device, &shader->clip); } } static void i915_shader_setup_dst (i915_shader_t *shader) { union i915_shader_channel *channel; i915_surface_t *s; /* We need to manual blending if we have a clip surface and an unbounded op, * or an extended blend mode. */ if (shader->need_combine || (shader->op < CAIRO_OPERATOR_SATURATE && (shader->clip.type.fragment == (i915_fragment_shader_t) -1 || _cairo_operator_bounded_by_mask (shader->op)))) { return; } shader->need_combine = TRUE; channel = &shader->dst; channel->type.vertex = VS_TEXTURE_16; channel->base.texfmt = TEXCOORDFMT_2D_16; channel->base.content = shader->content; channel->type.fragment = FS_TEXTURE; channel->surface.pixel = NONE; s = shader->target; channel->base.bo = to_intel_bo (s->intel.drm.bo); channel->base.n_samplers = 1; channel->base.offset[0] = s->offset; channel->base.map[0] = s->map0; channel->base.map[1] = s->map1; channel->base.sampler[0] = (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | i915_texture_filter (CAIRO_FILTER_NEAREST); channel->base.sampler[1] = SS3_NORMALIZED_COORDS | i915_texture_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_scale (&shader->dst.base.matrix, 1. / s->intel.drm.width, 1. / s->intel.drm.height); } static void i915_shader_combine_source (i915_shader_t *shader, i915_device_t *device) { if (device->last_source_fragment == shader->source.type.fragment) return; if (device->last_source_fragment == FS_DIFFUSE) { switch (shader->source.type.fragment) { case FS_ONE: case FS_PURE: case FS_CONSTANT: case FS_DIFFUSE: shader->source.type.fragment = FS_DIFFUSE; shader->source.base.mode = 0; break; case FS_ZERO: case FS_LINEAR: case FS_RADIAL: case FS_TEXTURE: case FS_YUV: case FS_SPANS: default: break; } } device->last_source_fragment = shader->source.type.fragment; } static inline float * i915_composite_vertex (float *v, const i915_shader_t *shader, double x, double y) { double s, t; /* Each vertex is: * 2 vertex coordinates * [0-2] source texture coordinates * [0-2] mask texture coordinates */ *v++ = x; *v++ = y; switch (shader->source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->source.linear, x, y); break; case VS_TEXTURE: s = x, t = y; cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: s = x, t = y; cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } switch (shader->mask.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *v++ = i915_shader_linear_texcoord (&shader->mask.linear, x, y); break; case VS_TEXTURE: s = x, t = y; cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t); *v++ = s; *v++ = t; break; case VS_TEXTURE_16: s = x, t = y; cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t); *v++ = texcoord_2d_16 (s, t); break; } return v; } static inline void i915_shader_add_rectangle_general (const i915_shader_t *shader, int x, int y, int w, int h) { float *vertices; vertices = i915_add_rectangle (shader->device); vertices = i915_composite_vertex (vertices, shader, x + w, y + h); vertices = i915_composite_vertex (vertices, shader, x, y + h); vertices = i915_composite_vertex (vertices, shader, x, y); /* XXX overflow! */ } void i915_vbo_flush (i915_device_t *device) { assert (device->floats_per_vertex); assert (device->vertex_count); if (device->vbo == 0) { OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); device->vbo = device->batch.used++; device->vbo_max_index = device->batch.used; OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); } OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | device->vertex_count); OUT_DWORD (device->vertex_index); device->vertex_index += device->vertex_count; device->vertex_count = 0; } cairo_status_t i915_shader_commit (i915_shader_t *shader, i915_device_t *device) { unsigned floats_per_vertex; cairo_status_t status; assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); if (! shader->committed) { device->shader = shader; i915_shader_combine_mask (shader, device); i915_shader_combine_source (shader, device); i915_shader_setup_dst (shader); shader->add_rectangle = i915_shader_add_rectangle_general; if ((status = setjmp (shader->unwind))) return status; shader->committed = TRUE; } if (i915_shader_needs_update (shader, device)) { if (i915_batch_space (device) < 256) { status = i915_batch_flush (device); if (unlikely (status)) return status; } if (device->vertex_count) i915_vbo_flush (device); status = i915_shader_check_aperture (shader, device); if (unlikely (status)) return status; update_shader: i915_set_shader_target (device, shader); i915_set_shader_mode (device, shader); i915_set_shader_samplers (device, shader); i915_set_shader_constants (device, shader); i915_set_shader_program (device, shader); } floats_per_vertex = 2 + i915_shader_num_texcoords (shader); if (device->floats_per_vertex == floats_per_vertex) return CAIRO_STATUS_SUCCESS; if (i915_batch_space (device) < 8) { status = i915_batch_flush (device); if (unlikely (status)) return status; goto update_shader; } if (device->vertex_count) i915_vbo_flush (device); if (device->vbo) { device->batch_base[device->vbo_max_index] |= device->vertex_index; OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (1) | 0); device->vbo_max_index = device->batch.used; OUT_DWORD ((floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); } device->floats_per_vertex = floats_per_vertex; device->rectangle_size = floats_per_vertex * 3 * sizeof (float); device->vertex_index = (device->vbo_used + 4*floats_per_vertex - 1) / (4 * floats_per_vertex); device->vbo_offset = 4 * device->vertex_index * floats_per_vertex; return CAIRO_STATUS_SUCCESS; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i915-spans.c000066400000000000000000000471041271037650300267200ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-drm-i915-private.h" /* Operates in either immediate or retained mode. * When given a clip region we record the sequence of vbo and then * replay them for each clip rectangle, otherwise we simply emit * the vbo straight into the command stream. */ typedef struct _i915_spans i915_spans_t; typedef float * (*i915_get_rectangle_func_t) (i915_spans_t *spans); typedef void (*i915_span_func_t) (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha); struct _i915_spans { cairo_span_renderer_t renderer; i915_device_t *device; int xmin, xmax; cairo_bool_t is_bounded; const cairo_rectangle_int_t *extents; i915_get_rectangle_func_t get_rectangle; i915_span_func_t span; i915_shader_t shader; cairo_region_t *clip_region; cairo_bool_t need_clip_surface; struct vbo { struct vbo *next; intel_bo_t *bo; unsigned int count; } head, *tail; unsigned int vbo_offset; float *vbo_base; }; static float * i915_emit_rectangle (i915_spans_t *spans) { return i915_add_rectangle (spans->device); } static float * i915_accumulate_rectangle (i915_spans_t *spans) { float *vertices; uint32_t size; size = spans->device->rectangle_size; if (unlikely (spans->vbo_offset + size > I915_VBO_SIZE)) { struct vbo *vbo; vbo = malloc (sizeof (struct vbo)); if (unlikely (vbo == NULL)) { /* throw error! */ } spans->tail->next = vbo; spans->tail = vbo; vbo->next = NULL; vbo->bo = intel_bo_create (&spans->device->intel, I915_VBO_SIZE, I915_VBO_SIZE, FALSE, I915_TILING_NONE, 0); vbo->count = 0; spans->vbo_offset = 0; spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo); } vertices = spans->vbo_base + spans->vbo_offset; spans->vbo_offset += size; spans->tail->count += 3; return vertices; } static void i915_span_zero (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; *vertices++ = x0; *vertices++ = y1; *vertices++ = x0; *vertices++ = y0; } static void i915_span_constant (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y0; *vertices++ = a; } static void i915_span_linear (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); *vertices++ = a; } static void i915_span_texture (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; *vertices++ = a; } static void i915_span_texture16 (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; double s, t; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y1; s = x1, t = y0; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; *vertices++ = x0; *vertices++ = y0; s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); *vertices++ = a; } static void i915_span_generic (i915_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { double s, t; float *vertices; float a = alpha / 255.; /* Each vertex is: * 2 vertex coordinates * [0-2] source texture coordinates * 1 alpha value. * [0,2] clip mask coordinates */ vertices = spans->get_rectangle (spans); /* bottom right */ *vertices++ = x1; *vertices++ = y1; s = x1, t = y1; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x1, t = y1; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } /* bottom left */ *vertices++ = x0; *vertices++ = y1; s = x0, t = y1; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x0, t = y1; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x0, t = y1; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } /* top left */ *vertices++ = x0; *vertices++ = y0; s = x0, t = y0; switch (spans->shader.source.type.vertex) { case VS_ZERO: case VS_CONSTANT: break; case VS_LINEAR: *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); break; case VS_TEXTURE: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = s; *vertices++ = t; break; case VS_TEXTURE_16: cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); break; } *vertices++ = a; if (spans->need_clip_surface) { s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } if (spans->shader.need_combine) { s = x0, t = y0; cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); *vertices++ = texcoord_2d_16 (s, t); } } static cairo_status_t i915_zero_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { while (num_spans && half[0].coverage < 128) half++, num_spans--; if (num_spans == 0) break; x0 = x1 = half[0].x; while (num_spans--) { half++; x1 = half[0].x; if (half[0].coverage < 128) break; } i915_span_zero (spans, x0, x1, y, y + height, 0); } while (num_spans); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_zero_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; int x0, x1; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { while (num_spans && half[0].coverage == 0) half++, num_spans--; if (num_spans == 0) break; x0 = x1 = half[0].x; while (num_spans--) { half++; x1 = half[0].x; if (half[0].coverage == 0) break; } i915_span_zero (spans, x0, x1, y, y + height, 0); } while (num_spans); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_bounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage >= 128) { spans->span (spans, half[0].x, half[1].x, y, y + height, 255); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage) { spans->span (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) { spans->span (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { spans->span (spans, spans->xmin, half[0].x, y, y + height, 0); } do { spans->span (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { spans->span (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_unbounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i915_spans_t *spans = abstract_renderer; if (num_spans == 0) { spans->span (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { spans->span (spans, spans->xmin, half[0].x, y, y + height, 0); } do { int alpha = 0; if (half[0].coverage >= 128) alpha = 255; spans->span (spans, half[0].x, half[1].x, y, y + height, alpha); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { spans->span (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_spans_init (i915_spans_t *spans, i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, cairo_clip_t *clip, double opacity, const cairo_composite_rectangles_t *extents) { cairo_status_t status; spans->device = (i915_device_t *) dst->intel.drm.base.device; spans->is_bounded = extents->is_bounded; if (extents->is_bounded) { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i915_bounded_spans_mono; else spans->renderer.render_rows = i915_bounded_spans; spans->extents = &extents->bounded; } else { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i915_unbounded_spans_mono; else spans->renderer.render_rows = i915_unbounded_spans; spans->extents = &extents->unbounded; } spans->xmin = spans->extents->x; spans->xmax = spans->extents->x + spans->extents->width; spans->clip_region = NULL; spans->need_clip_surface = FALSE; if (clip != NULL) { cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; spans->clip_region = clip_region; spans->need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; } spans->head.next = NULL; spans->head.bo = NULL; spans->head.count = 0; spans->tail = &spans->head; if (spans->clip_region == NULL) { spans->get_rectangle = i915_emit_rectangle; } else { assert (! extents->is_bounded); spans->get_rectangle = i915_accumulate_rectangle; spans->head.bo = intel_bo_create (&spans->device->intel, I915_VBO_SIZE, I915_VBO_SIZE, FALSE, I915_TILING_NONE, 0); if (unlikely (spans->head.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo); } spans->vbo_offset = 0; i915_shader_init (&spans->shader, dst, op, opacity); if (spans->need_clip_surface) i915_shader_set_clip (&spans->shader, clip); status = i915_shader_acquire_pattern (&spans->shader, &spans->shader.source, pattern, &extents->bounded); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static void i915_spans_fini (i915_spans_t *spans) { i915_shader_fini (&spans->shader); if (spans->head.bo != NULL) { struct vbo *vbo, *next; intel_bo_destroy (&spans->device->intel, spans->head.bo); for (vbo = spans->head.next; vbo != NULL; vbo = next) { next = vbo->next; intel_bo_destroy (&spans->device->intel, vbo->bo); free (vbo); } } } cairo_status_t i915_clip_and_composite_spans (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, i915_spans_func_t draw_func, void *draw_closure, const cairo_composite_rectangles_t*extents, cairo_clip_t *clip, double opacity) { i915_spans_t spans; i915_device_t *device; cairo_status_t status; struct vbo *vbo; if (i915_surface_needs_tiling (dst)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_white.base; op = CAIRO_OPERATOR_DEST_OUT; } status = i915_spans_init (&spans, dst, op, pattern, antialias, clip, opacity, extents); if (unlikely (status)) return status; spans.shader.mask.base.texfmt = TEXCOORDFMT_1D; spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA; spans.shader.mask.type.fragment = FS_SPANS; status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SPANS; if (dst->deferred_clear) { status = i915_surface_clear (dst); if (unlikely (status)) goto CLEANUP_SPANS; } device = i915_device (dst); status = i915_shader_commit (&spans.shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; if (! spans.shader.need_combine && ! spans.need_clip_surface) { switch (spans.shader.source.type.vertex) { case VS_ZERO: spans.span = i915_span_zero; if (extents->is_bounded) { if (antialias == CAIRO_ANTIALIAS_NONE) spans.renderer.render_rows = i915_zero_spans_mono; else spans.renderer.render_rows = i915_zero_spans; } break; case VS_CONSTANT: spans.span = i915_span_constant; break; case VS_LINEAR: spans.span = i915_span_linear; break; case VS_TEXTURE: spans.span = i915_span_texture; break; case VS_TEXTURE_16: spans.span = i915_span_texture16; break; default: spans.span = i915_span_generic; break; } } else { spans.span = i915_span_generic; } status = draw_func (draw_closure, &spans.renderer, spans.extents); if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) { i915_vbo_finish (device); OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | ENABLE_SCISSOR_RECT); for (vbo = &spans.head; vbo != NULL; vbo = vbo->next) { int i, num_rectangles; /* XXX require_space & batch_flush */ OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); i915_batch_emit_reloc (device, vbo->bo, 0, I915_GEM_DOMAIN_VERTEX, 0, FALSE); OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT) | vbo->count); num_rectangles = cairo_region_num_rectangles (spans.clip_region); for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (spans.clip_region, i, &rect); OUT_DWORD (_3DSTATE_SCISSOR_RECT_0_CMD); OUT_DWORD (SCISSOR_RECT_0_XMIN (rect.x) | SCISSOR_RECT_0_YMIN (rect.y)); OUT_DWORD (SCISSOR_RECT_0_XMAX (rect.x + rect.width) | SCISSOR_RECT_0_YMAX (rect.y + rect.height)); OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | vbo->count); OUT_DWORD (0); } } OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT); } CLEANUP_DEVICE: cairo_device_release (dst->intel.drm.base.device); CLEANUP_SPANS: i915_spans_fini (&spans); return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i915-surface.c000066400000000000000000002457411271037650300272330ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * ************************************************************************** * This work was initially based upon xf86-video-intel/src/i915_render.c: * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Wang Zhenyu * Eric Anholt * * ************************************************************************** * and also upon libdrm/intel/intel_bufmgr_gem.c: * Copyright © 2007 Red Hat Inc. * Copyright © 2007 Intel Corporation * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * Authors: Thomas Hellström * Keith Whitwell * Eric Anholt * Dave Airlie */ /* XXX * * - Per thread context? Would it actually avoid many locks? * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-ioctl-private.h" #include "cairo-drm-intel-private.h" #include "cairo-drm-intel-command-private.h" #include "cairo-drm-intel-ioctl-private.h" #include "cairo-drm-i915-private.h" #include "cairo-boxes-private.h" #include "cairo-cache-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-list-private.h" #include "cairo-path-fixed-private.h" #include "cairo-region-private.h" #include "cairo-surface-offset-private.h" #include #include #include static const uint32_t i915_batch_setup[] = { /* Disable line anti-aliasing */ _3DSTATE_AA_CMD, /* Disable independent alpha blend */ _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD | IAB_MODIFY_ENABLE | IAB_MODIFY_FUNC | (BLENDFUNC_ADD << IAB_FUNC_SHIFT) | IAB_MODIFY_SRC_FACTOR | (BLENDFACT_ONE << IAB_SRC_FACTOR_SHIFT) | IAB_MODIFY_DST_FACTOR | (BLENDFACT_ZERO << IAB_DST_FACTOR_SHIFT), /* Disable texture crossbar */ _3DSTATE_COORD_SET_BINDINGS | CSB_TCB (0, 0) | CSB_TCB (1, 1) | CSB_TCB (2, 2) | CSB_TCB (3, 3) | CSB_TCB (4, 4) | CSB_TCB (5, 5) | CSB_TCB (6, 6) | CSB_TCB (7, 7), _3DSTATE_MODES_4_CMD | ENABLE_LOGIC_OP_FUNC | LOGIC_OP_FUNC (LOGICOP_COPY), _3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (2) | I1_LOAD_S (3) | I1_LOAD_S (4) | I1_LOAD_S (5) | I1_LOAD_S (6) | 4, S2_TEXCOORD_NONE, 0, /* Disable texture coordinate wrap-shortest */ (1 << S4_POINT_WIDTH_SHIFT) | S4_LINE_WIDTH_ONE | S4_FLATSHADE_ALPHA | S4_FLATSHADE_FOG | S4_FLATSHADE_SPECULAR | S4_FLATSHADE_COLOR | S4_CULLMODE_NONE | S4_VFMT_XY, 0, /* Disable stencil buffer */ S6_COLOR_WRITE_ENABLE, _3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT, /* disable indirect state */ _3DSTATE_LOAD_INDIRECT, 0, }; static const cairo_surface_backend_t i915_surface_backend; static cairo_surface_t * i915_surface_create_from_cacheable_image (cairo_drm_device_t *base_dev, cairo_surface_t *source); static cairo_status_t i915_bo_exec (i915_device_t *device, intel_bo_t *bo, uint32_t offset) { struct drm_i915_gem_execbuffer2 execbuf; int ret, cnt, i; /* Add the batch buffer to the validation list. */ cnt = device->batch.exec_count; if (cnt > 0 && bo->base.handle == device->batch.exec[cnt-1].handle) i = cnt - 1; else i = device->batch.exec_count++; device->batch.exec[i].handle = bo->base.handle; device->batch.exec[i].relocation_count = device->batch.reloc_count; device->batch.exec[i].relocs_ptr = (uintptr_t) device->batch.reloc; device->batch.exec[i].alignment = 0; device->batch.exec[i].offset = 0; device->batch.exec[i].flags = 0; device->batch.exec[i].rsvd1 = 0; device->batch.exec[i].rsvd2 = 0; execbuf.buffers_ptr = (uintptr_t) device->batch.exec; execbuf.buffer_count = device->batch.exec_count; execbuf.batch_start_offset = offset; execbuf.batch_len = (device->batch.used << 2) + sizeof (device->batch_header); execbuf.DR1 = 0; execbuf.DR4 = 0; execbuf.num_cliprects = 0; execbuf.cliprects_ptr = 0; execbuf.flags = 0; execbuf.rsvd1 = 0; execbuf.rsvd2 = 0; do { ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); } while (ret != 0 && errno == EINTR); if (device->debug & I915_DEBUG_SYNC && ret == 0) ret = ! intel_bo_wait (&device->intel, bo); if (0 && ret) { int n, m; fprintf (stderr, "Batch submission failed: %d\n", errno); fprintf (stderr, " relocation entries: %d/%d\n", device->batch.reloc_count, I915_MAX_RELOCS); fprintf (stderr, " gtt size: (%zd/%zd), (%zd/%zd)\n", device->batch.est_gtt_size, device->batch.gtt_avail_size, device->batch.total_gtt_size, device->intel.gtt_avail_size); fprintf (stderr, " buffers:\n"); for (n = 0; n < device->batch.exec_count; n++) { fprintf (stderr, " exec[%d] = %d, %d/%d bytes, gtt = %qx\n", n, device->batch.exec[n].handle, n == device->batch.exec_count - 1 ? bo->base.size : device->batch.target_bo[n]->base.size, n == device->batch.exec_count - 1 ? bo->full_size : device->batch.target_bo[n]->full_size, device->batch.exec[n].offset); } for (n = 0; n < device->batch.reloc_count; n++) { for (m = 0; m < device->batch.exec_count; m++) if (device->batch.exec[m].handle == device->batch.reloc[n].target_handle) break; fprintf (stderr, " reloc[%d] = %d @ %qx -> %qx + %qx\n", n, device->batch.reloc[n].target_handle, device->batch.reloc[n].offset, (unsigned long long) device->batch.exec[m].offset, (unsigned long long) device->batch.reloc[n].delta); device->batch_base[(device->batch.reloc[n].offset - sizeof (device->batch_header)) / 4] = device->batch.exec[m].offset + device->batch.reloc[n].delta; } intel_dump_batchbuffer (device->batch_header, execbuf.batch_len, device->intel.base.chip_id); } assert (ret == 0); VG (VALGRIND_MAKE_MEM_DEFINED (device->batch.exec, sizeof (device->batch.exec[0]) * i)); bo->offset = device->batch.exec[i].offset; bo->busy = TRUE; if (bo->virtual) intel_bo_unmap (bo); bo->cpu = FALSE; while (cnt--) { intel_bo_t *bo = device->batch.target_bo[cnt]; bo->offset = device->batch.exec[cnt].offset; bo->exec = NULL; bo->busy = TRUE; bo->batch_read_domains = 0; bo->batch_write_domain = 0; cairo_list_del (&bo->cache_list); if (bo->virtual) intel_bo_unmap (bo); bo->cpu = FALSE; intel_bo_destroy (&device->intel, bo); } assert (cairo_list_is_empty (&device->intel.bo_in_flight)); device->batch.exec_count = 0; device->batch.reloc_count = 0; device->batch.fences = 0; device->batch.est_gtt_size = I915_BATCH_SIZE; device->batch.total_gtt_size = I915_BATCH_SIZE; return ret == 0 ? CAIRO_STATUS_SUCCESS : _cairo_error (CAIRO_STATUS_NO_MEMORY); } void i915_batch_add_reloc (i915_device_t *device, uint32_t pos, intel_bo_t *bo, uint32_t offset, uint32_t read_domains, uint32_t write_domain, cairo_bool_t needs_fence) { int index; assert (offset < bo->base.size); if (bo->exec == NULL) { device->batch.total_gtt_size += bo->base.size; if (! bo->busy) device->batch.est_gtt_size += bo->base.size; assert (device->batch.exec_count < ARRAY_LENGTH (device->batch.exec)); index = device->batch.exec_count++; device->batch.exec[index].handle = bo->base.handle; device->batch.exec[index].relocation_count = 0; device->batch.exec[index].relocs_ptr = 0; device->batch.exec[index].alignment = 0; device->batch.exec[index].offset = 0; device->batch.exec[index].flags = 0; device->batch.exec[index].rsvd1 = 0; device->batch.exec[index].rsvd2 = 0; device->batch.target_bo[index] = intel_bo_reference (bo); bo->exec = &device->batch.exec[index]; } if (bo->tiling != I915_TILING_NONE) { uint32_t alignment; #if 0 /* We presume that we will want to use a fence with X tiled objects... */ if (needs_fence || bo->tiling == I915_TILING_X) alignment = bo->full_size; else alignment = 2*((bo->stride + 4095) & -4096); #else alignment = bo->full_size; #endif if (bo->exec->alignment < alignment) bo->exec->alignment = alignment; if (needs_fence && (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) { bo->exec->flags |= EXEC_OBJECT_NEEDS_FENCE; device->batch.fences++; intel_bo_set_tiling (&device->intel, bo); } } assert (device->batch.reloc_count < ARRAY_LENGTH (device->batch.reloc)); index = device->batch.reloc_count++; device->batch.reloc[index].offset = (pos << 2) + sizeof (device->batch_header); device->batch.reloc[index].delta = offset; device->batch.reloc[index].target_handle = bo->base.handle; device->batch.reloc[index].read_domains = read_domains; device->batch.reloc[index].write_domain = write_domain; device->batch.reloc[index].presumed_offset = bo->offset; assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain); bo->batch_read_domains |= read_domains; bo->batch_write_domain |= write_domain; } void i915_vbo_finish (i915_device_t *device) { intel_bo_t *vbo; assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); assert (device->vbo_used); if (device->vertex_count) { if (device->vbo == 0) { OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); device->vbo = device->batch.used++; device->vbo_max_index = device->batch.used; OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); } OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | device->vertex_count); OUT_DWORD (device->vertex_index); } if (device->last_vbo != NULL) { intel_bo_in_flight_add (&device->intel, device->last_vbo); intel_bo_destroy (&device->intel, device->last_vbo); } device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count; /* will include a few bytes of inter-array padding */ vbo = intel_bo_create (&device->intel, device->vbo_used, device->vbo_used, FALSE, I915_TILING_NONE, 0); i915_batch_fill_reloc (device, device->vbo, vbo, 0, I915_GEM_DOMAIN_VERTEX, 0); intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base); device->last_vbo = vbo; device->last_vbo_offset = (device->vbo_used+7)&-8; device->last_vbo_space = vbo->base.size - device->last_vbo_offset; device->vbo = 0; device->vbo_used = device->vbo_offset = 0; device->vertex_index = device->vertex_count = 0; if (! i915_check_aperture_size (device, 1, I915_VBO_SIZE, I915_VBO_SIZE)) { cairo_status_t status; status = i915_batch_flush (device); if (unlikely (status)) longjmp (device->shader->unwind, status); status = i915_shader_commit (device->shader, device); if (unlikely (status)) longjmp (device->shader->unwind, status); } } /* XXX improve state tracker/difference and flush state on vertex emission */ static void i915_device_reset (i915_device_t *device) { if (device->current_source != NULL) *device->current_source = 0; if (device->current_mask != NULL) *device->current_mask = 0; if (device->current_clip != NULL) *device->current_clip = 0; device->current_target = NULL; device->current_size = 0; device->current_source = NULL; device->current_mask = NULL; device->current_clip = NULL; device->current_texcoords = ~0; device->current_blend = 0; device->current_n_constants = 0; device->current_n_samplers = 0; device->current_n_maps = 0; device->current_colorbuf = 0; device->current_diffuse = 0; device->current_program = ~0; device->clear_alpha = ~0; device->last_source_fragment = ~0; } static void i915_batch_cleanup (i915_device_t *device) { int i; for (i = 0; i < device->batch.exec_count; i++) { intel_bo_t *bo = device->batch.target_bo[i]; bo->exec = NULL; bo->batch_read_domains = 0; bo->batch_write_domain = 0; cairo_list_del (&bo->cache_list); intel_bo_destroy (&device->intel, bo); } device->batch.exec_count = 0; device->batch.reloc_count = 0; } static void i915_batch_vbo_finish (i915_device_t *device) { assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); if (device->vbo || i915_batch_space (device) < (int32_t) device->vbo_used) { intel_bo_t *vbo; if (device->vertex_count) { if (device->vbo == 0) { OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); device->vbo = device->batch.used++; device->vbo_max_index = device->batch.used; OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); } OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | device->vertex_count); OUT_DWORD (device->vertex_index); } if (device->last_vbo != NULL) intel_bo_destroy (&device->intel, device->last_vbo); device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count; /* will include a few bytes of inter-array padding */ vbo = intel_bo_create (&device->intel, device->vbo_used, device->vbo_used, FALSE, I915_TILING_NONE, 0); i915_batch_fill_reloc (device, device->vbo, vbo, 0, I915_GEM_DOMAIN_VERTEX, 0); intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base); device->last_vbo = vbo; device->last_vbo_offset = (device->vbo_used+7)&-8; device->last_vbo_space = vbo->base.size - device->last_vbo_offset; device->vbo = 0; } else { /* Only a single rectlist in this batch, and no active vertex buffer. */ OUT_DWORD (PRIM3D_RECTLIST | (device->vbo_used / 4 - 1)); memcpy (BATCH_PTR (device), device->vbo_base, device->vbo_used); device->batch.used += device->vbo_used >> 2; } device->vbo_used = device->vbo_offset = 0; device->vertex_index = device->vertex_count = 0; } cairo_status_t i915_batch_flush (i915_device_t *device) { intel_bo_t *batch; cairo_status_t status; uint32_t length, offset; int n; assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); if (device->vbo_used) i915_batch_vbo_finish (device); if (device->batch.used == 0) return CAIRO_STATUS_SUCCESS; i915_batch_emit_dword (device, MI_BATCH_BUFFER_END); if ((device->batch.used & 1) != ((sizeof (device->batch_header)>>2) & 1)) i915_batch_emit_dword (device, MI_NOOP); length = (device->batch.used << 2) + sizeof (device->batch_header); /* NB: it is faster to copy the data then map/unmap the batch, * presumably because we frequently only use a small part of the buffer. */ batch = NULL; if (device->last_vbo) { if (length <= device->last_vbo_space) { batch = device->last_vbo; offset = device->last_vbo_offset; /* fixup the relocations */ for (n = 0; n < device->batch.reloc_count; n++) device->batch.reloc[n].offset += offset; } else intel_bo_destroy (&device->intel, device->last_vbo); device->last_vbo = NULL; } if (batch == NULL) { batch = intel_bo_create (&device->intel, length, length, FALSE, I915_TILING_NONE, 0); if (unlikely (batch == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); i915_batch_cleanup (device); goto BAIL; } offset = 0; } intel_bo_write (&device->intel, batch, offset, length, device->batch_header); status = i915_bo_exec (device, batch, offset); intel_bo_destroy (&device->intel, batch); BAIL: device->batch.used = 0; intel_glyph_cache_unpin (&device->intel); intel_snapshot_cache_thaw (&device->intel); i915_device_reset (device); return status; } #if 0 static float * i915_add_rectangles (i915_device_t *device, int num_rects, int *count) { float *vertices; uint32_t size; int cnt; assert (device->floats_per_vertex); size = device->rectangle_size; if (unlikely (device->vbo_offset + size > I915_VBO_SIZE)) i915_vbo_finish (device); vertices = (float *) (device->vbo_base + device->vbo_offset); cnt = (I915_VBO_SIZE - device->vbo_offset) / size; if (cnt > num_rects) cnt = num_rects; device->vbo_used = device->vbo_offset += size * cnt; device->vertex_count += 3 * cnt; *count = cnt; return vertices; } #endif static cairo_surface_t * i915_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { i915_surface_t *other; cairo_format_t format; uint32_t tiling = I915_TILING_DEFAULT; other = abstract_other; if (content == other->intel.drm.base.content) format = other->intel.drm.format; else format = _cairo_format_from_content (content); if (width * _cairo_format_bits_per_pixel (format) > 8 * 32*1024 || height > 64*1024) return NULL; /* we presume that a similar surface will be used for blitting */ if (i915_surface_needs_tiling (other)) tiling = I915_TILING_X; return i915_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device, format, width, height, tiling, TRUE); } static cairo_status_t i915_surface_finish (void *abstract_surface) { i915_surface_t *surface = abstract_surface; i915_device_t *device = i915_device (surface); if (surface->stencil != NULL) { intel_bo_in_flight_add (&device->intel, surface->stencil); intel_bo_destroy (&device->intel, surface->stencil); } if (surface->is_current_texture) { if (surface->is_current_texture & CURRENT_SOURCE) device->current_source = NULL; if (surface->is_current_texture & CURRENT_MASK) device->current_mask = NULL; if (surface->is_current_texture & CURRENT_CLIP) device->current_clip = NULL; device->current_n_samplers = 0; } if (surface == device->current_target) device->current_target = NULL; if (surface->cache != NULL) { i915_image_private_t *node = surface->cache; intel_buffer_cache_t *cache = node->container; if (--cache->ref_count == 0) { intel_bo_in_flight_add (&device->intel, cache->buffer.bo); intel_bo_destroy (&device->intel, cache->buffer.bo); _cairo_rtree_fini (&cache->rtree); cairo_list_del (&cache->link); free (cache); } else { node->node.state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_move (&node->node.link, &cache->rtree.available); _cairo_rtree_node_collapse (&cache->rtree, node->node.parent); } } return intel_surface_finish (&surface->intel); } static cairo_status_t i915_surface_batch_flush (i915_surface_t *surface) { cairo_status_t status; intel_bo_t *bo; assert (surface->intel.drm.fallback == NULL); bo = to_intel_bo (surface->intel.drm.bo); if (bo == NULL || bo->batch_write_domain == 0) return CAIRO_STATUS_SUCCESS; status = cairo_device_acquire (surface->intel.drm.base.device); if (unlikely (status)) return status; status = i915_batch_flush (i915_device (surface)); cairo_device_release (surface->intel.drm.base.device); return status; } static cairo_status_t i915_surface_flush (void *abstract_surface, unsigned flags) { i915_surface_t *surface = abstract_surface; cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->intel.drm.fallback == NULL) { if (surface->intel.drm.base.finished) { /* Forgo flushing on finish as the user cannot access the surface directly. */ return CAIRO_STATUS_SUCCESS; } if (surface->deferred_clear) { status = i915_surface_clear (surface); if (unlikely (status)) return status; } return i915_surface_batch_flush (surface); } return intel_surface_flush (abstract_surface, flags); } /* rasterisation */ static cairo_status_t _composite_boxes_spans (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents) { cairo_boxes_t *boxes = closure; cairo_rectangular_scan_converter_t converter; struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i; _cairo_rectangular_scan_converter_init (&converter, extents); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); if (unlikely (status)) goto CLEANUP; } } status = converter.base.generate (&converter.base, renderer); CLEANUP: converter.base.destroy (&converter.base); return status; } cairo_status_t i915_fixup_unbounded (i915_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip) { i915_shader_t shader; i915_device_t *device; cairo_status_t status; if (clip != NULL) { cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); assert (clip_region == NULL); if (status != CAIRO_INT_STATUS_UNSUPPORTED) clip = NULL; } else { if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } } if (clip != NULL) { i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.); i915_shader_set_clip (&shader, clip); status = i915_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_white.base, &extents->unbounded); assert (status == CAIRO_STATUS_SUCCESS); } else { i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_clear.base, &extents->unbounded); assert (status == CAIRO_STATUS_SUCCESS); } device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return status; status = i915_shader_commit (&shader, device); if (unlikely (status)) goto BAIL; if (extents->bounded.width == 0 || extents->bounded.height == 0) { shader.add_rectangle (&shader, extents->unbounded.x, extents->unbounded.y, extents->unbounded.width, extents->unbounded.height); } else { /* top */ if (extents->bounded.y != extents->unbounded.y) { shader.add_rectangle (&shader, extents->unbounded.x, extents->unbounded.y, extents->unbounded.width, extents->bounded.y - extents->unbounded.y); } /* left */ if (extents->bounded.x != extents->unbounded.x) { shader.add_rectangle (&shader, extents->unbounded.x, extents->bounded.y, extents->bounded.x - extents->unbounded.x, extents->bounded.height); } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { shader.add_rectangle (&shader, extents->bounded.x + extents->bounded.width, extents->bounded.y, extents->unbounded.x + extents->unbounded.width - (extents->bounded.x + extents->bounded.width), extents->bounded.height); } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { shader.add_rectangle (&shader, extents->unbounded.x, extents->bounded.y + extents->bounded.height, extents->unbounded.width, extents->unbounded.y + extents->unbounded.height - (extents->bounded.y + extents->bounded.height)); } } i915_shader_fini (&shader); BAIL: cairo_device_release (&device->intel.base.base); return status; } static cairo_status_t i915_fixup_unbounded_boxes (i915_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip, cairo_boxes_t *boxes) { cairo_boxes_t clear; cairo_box_t box; cairo_region_t *clip_region = NULL; cairo_status_t status; struct _cairo_boxes_chunk *chunk; int i; if (boxes->num_boxes <= 1) return i915_fixup_unbounded (dst, extents, clip); _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (status != CAIRO_INT_STATUS_UNSUPPORTED) clip = NULL; } if (clip_region == NULL) { cairo_boxes_t tmp; _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, &box); assert (status == CAIRO_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; } else { pixman_box32_t *pbox; pbox = pixman_region32_rectangles (&clip_region->rgn, &i); _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); status = _cairo_boxes_add (&clear, &box); assert (status == CAIRO_STATUS_SUCCESS); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_boxes_add (&clear, &chunk->base[i]); if (unlikely (status)) { _cairo_boxes_fini (&clear); return status; } } } status = _cairo_bentley_ottmann_tessellate_boxes (&clear, CAIRO_FILL_RULE_WINDING, &clear); } if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) { i915_shader_t shader; i915_device_t *device; if (clip != NULL) { i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.); i915_shader_set_clip (&shader, clip); status = i915_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_white.base, &extents->unbounded); assert (status == CAIRO_STATUS_SUCCESS); } else { i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_clear.base, &extents->unbounded); assert (status == CAIRO_STATUS_SUCCESS); } device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i915_shader_commit (&shader, device); if (unlikely (status)) goto err_device; for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); } } err_device: cairo_device_release (&device->intel.base.base); err_shader: i915_shader_fini (&shader); } _cairo_boxes_fini (&clear); return status; } static cairo_bool_t i915_can_blt (i915_surface_t *dst, const cairo_pattern_t *pattern) { const cairo_surface_pattern_t *spattern; i915_surface_t *src; spattern = (const cairo_surface_pattern_t *) pattern; src = (i915_surface_t *) spattern->surface; if (src->intel.drm.base.device != dst->intel.drm.base.device) return FALSE; if (! i915_surface_needs_tiling (dst)) return FALSE; if (! _cairo_matrix_is_translation (&pattern->matrix)) return FALSE; if (! (pattern->filter == CAIRO_FILTER_NEAREST || pattern->filter == CAIRO_FILTER_FAST)) { if (! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.x0)) || ! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.y0))) { return FALSE; } } return _cairo_format_bits_per_pixel (src->intel.drm.format) == _cairo_format_bits_per_pixel (dst->intel.drm.format); } static cairo_status_t i915_blt (i915_surface_t *src, i915_surface_t *dst, int src_x, int src_y, int width, int height, int dst_x, int dst_y, cairo_bool_t flush) { i915_device_t *device; intel_bo_t *bo_array[2]; cairo_status_t status; int br13, cmd; bo_array[0] = to_intel_bo (dst->intel.drm.bo); bo_array[1] = to_intel_bo (src->intel.drm.bo); status = i915_surface_fallback_flush (src); if (unlikely (status)) return status; device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return status; if (! i915_check_aperture_and_fences (device, bo_array, 2) || i915_batch_space (device) < 9) { status = i915_batch_flush (device); if (unlikely (status)) goto CLEANUP; } cmd = XY_SRC_COPY_BLT_CMD; br13 = (0xCC << 16) | dst->intel.drm.stride; switch (dst->intel.drm.format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: ASSERT_NOT_REACHED; case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_RGB16_565: br13 |= BR13_565; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: br13 |= BR13_8888; cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; break; } OUT_DWORD (cmd); OUT_DWORD (br13); OUT_DWORD ((dst_y << 16) | dst_x); OUT_DWORD (((dst_y + height - 1) << 16) | (dst_x + width - 1)); OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); OUT_DWORD ((src_y << 16) | src_x); OUT_DWORD (src->intel.drm.stride); OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0); /* require explicit RenderCache flush for 2D -> 3D sampler? */ if (flush) OUT_DWORD (MI_FLUSH); CLEANUP: cairo_device_release (&device->intel.base.base); return CAIRO_STATUS_SUCCESS; } cairo_status_t i915_surface_copy_subimage (i915_device_t *device, i915_surface_t *src, const cairo_rectangle_int_t *extents, cairo_bool_t flush, i915_surface_t **clone_out) { i915_surface_t *clone; cairo_status_t status; clone = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, src->intel.drm.format, extents->width, extents->height, I915_TILING_X, TRUE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; status = i915_blt (src, clone, extents->x, extents->y, extents->width, extents->height, 0, 0, flush); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i915_clear_boxes (i915_surface_t *dst, const cairo_boxes_t *boxes) { i915_device_t *device = i915_device (dst); const struct _cairo_boxes_chunk *chunk; cairo_status_t status; intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) }; int cmd, br13, clear = 0, i; cmd = XY_COLOR_BLT_CMD; br13 = (0xCC << 16) | dst->intel.drm.stride; switch (dst->intel.drm.format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: ASSERT_NOT_REACHED; case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_RGB16_565: br13 |= BR13_565; break; case CAIRO_FORMAT_RGB24: clear = 0xff000000; case CAIRO_FORMAT_ARGB32: br13 |= BR13_8888; cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; break; } status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return status; if (! i915_check_aperture_and_fences (device, bo_array, 1) || i915_batch_space (device) < 6 * boxes->num_boxes) { status = i915_batch_flush (device); if (unlikely (status)) goto RELEASE; } if (device->vertex_count) i915_vbo_flush (device); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int y2 = _cairo_fixed_integer_round (box[i].p2.y); if (x2 <= x1 || y2 <= y1) continue; OUT_DWORD (cmd); OUT_DWORD (br13); OUT_DWORD ((y1 << 16) | x1); OUT_DWORD (((y2 - 1) << 16) | (x2 - 1)); OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); OUT_DWORD (clear); } } RELEASE: cairo_device_release (&device->intel.base.base); return status; } static cairo_status_t i915_surface_extract_X_from_Y (i915_device_t *device, i915_surface_t *src, const cairo_rectangle_int_t *extents, i915_surface_t **clone_out) { i915_surface_t *clone; i915_shader_t shader; cairo_surface_pattern_t pattern; cairo_rectangle_int_t rect; cairo_status_t status; status = i915_surface_fallback_flush (src); if (unlikely (status)) return status; clone = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, src->intel.drm.format, extents->width, extents->height, I915_TILING_X, TRUE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; i915_shader_init (&shader, clone, CAIRO_OPERATOR_SOURCE, 1.); _cairo_pattern_init_for_surface (&pattern, &src->intel.drm.base); pattern.base.filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); rect.x = rect.y = 0; rect.width = extents->width; rect.height = extents->height; status = i915_shader_acquire_pattern (&shader, &shader.source, &pattern.base, &rect); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) goto err_shader; status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i915_shader_commit (&shader, device); if (unlikely (status)) goto err_device; shader.add_rectangle (&shader, 0, 0, extents->width, extents->height); cairo_device_release (&device->intel.base.base); i915_shader_fini (&shader); *clone_out = clone; return CAIRO_STATUS_SUCCESS; err_device: cairo_device_release (&device->intel.base.base); err_shader: i915_shader_fini (&shader); cairo_surface_destroy (&clone->intel.drm.base); return status; } static cairo_status_t i915_blt_boxes (i915_surface_t *dst, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents, const cairo_boxes_t *boxes) { const cairo_surface_pattern_t *spattern; i915_device_t *device; i915_surface_t *src; cairo_surface_t *free_me = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int br13, cmd, tx, ty; intel_bo_t *bo_array[2]; int i; if (! i915_can_blt (dst, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; spattern = (const cairo_surface_pattern_t *) pattern; src = (i915_surface_t *) spattern->surface; if (src->intel.drm.base.is_clear) return i915_clear_boxes (dst, boxes); if (pattern->extend != CAIRO_EXTEND_NONE && (extents->x + tx < 0 || extents->y + ty < 0 || extents->x + tx + extents->width > src->intel.drm.width || extents->y + ty + extents->height > src->intel.drm.height)) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = i915_surface_fallback_flush (src); if (unlikely (status)) return status; tx = _cairo_lround (pattern->matrix.x0); ty = _cairo_lround (pattern->matrix.y0); device = i915_device (dst); if (to_intel_bo (src->intel.drm.bo)->tiling == I915_TILING_Y) { cairo_rectangle_int_t extents; _cairo_boxes_extents (boxes, &extents); extents.x += tx; extents.y += ty; status = i915_surface_extract_X_from_Y (device, src, &extents, &src); if (unlikely (status)) return status; free_me = &src->intel.drm.base; tx = -extents.x; ty = -extents.y; } bo_array[0] = to_intel_bo (dst->intel.drm.bo); bo_array[1] = to_intel_bo (src->intel.drm.bo); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto CLEANUP_SURFACE; if (! i915_check_aperture_and_fences (device, bo_array, 2) || i915_batch_space (device) < 8 * boxes->num_boxes) { status = i915_batch_flush (device); if (unlikely (status)) goto CLEANUP_DEVICE; } cmd = XY_SRC_COPY_BLT_CMD; br13 = (0xCC << 16) | dst->intel.drm.stride; switch (dst->intel.drm.format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: ASSERT_NOT_REACHED; case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_RGB16_565: br13 |= BR13_565; break; case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: br13 |= BR13_8888; cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; break; } for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int y2 = _cairo_fixed_integer_round (box[i].p2.y); if (x1 + tx < 0) x1 = -tx; if (x2 + tx > src->intel.drm.width) x2 = src->intel.drm.width - tx; if (y1 + ty < 0) y1 = -ty; if (y2 + ty > src->intel.drm.height) y2 = src->intel.drm.height - ty; if (x2 <= x1 || y2 <= y1) continue; if (x2 < 0 || y2 < 0) continue; if (x1 >= dst->intel.drm.width || y2 >= dst->intel.drm.height) continue; OUT_DWORD (cmd); OUT_DWORD (br13); OUT_DWORD ((y1 << 16) | x1); OUT_DWORD (((y2 - 1) << 16) | (x2 - 1)); OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); OUT_DWORD (((y1 + ty) << 16) | (x1 + tx)); OUT_DWORD (src->intel.drm.stride); OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0); } } /* XXX fixup blank portions */ CLEANUP_DEVICE: cairo_device_release (&device->intel.base.base); CLEANUP_SURFACE: cairo_surface_destroy (free_me); return status; } static cairo_status_t _upload_image_inplace (i915_surface_t *surface, const cairo_pattern_t *source, const cairo_rectangle_int_t *extents, const cairo_boxes_t *boxes) { i915_device_t *device; const cairo_surface_pattern_t *pattern; cairo_image_surface_t *image; const struct _cairo_boxes_chunk *chunk; intel_bo_t *bo; int tx, ty, i; if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; pattern = (const cairo_surface_pattern_t *) source; if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) return CAIRO_INT_STATUS_UNSUPPORTED; image = (cairo_image_surface_t *) pattern->surface; if (source->extend != CAIRO_EXTEND_NONE && (extents->x + tx < 0 || extents->y + ty < 0 || extents->x + tx + extents->width > image->width || extents->y + ty + extents->height > image->height)) { return CAIRO_INT_STATUS_UNSUPPORTED; } device = i915_device (surface); bo = to_intel_bo (surface->intel.drm.bo); if (bo->exec != NULL || ! intel_bo_is_inactive (&device->intel, bo)) { intel_bo_t *new_bo; cairo_bool_t need_clear = FALSE; if (boxes->num_boxes != 1 || extents->width < surface->intel.drm.width || extents->height < surface->intel.drm.height) { if (! surface->intel.drm.base.is_clear) return CAIRO_INT_STATUS_UNSUPPORTED; need_clear = TRUE; } new_bo = intel_bo_create (&device->intel, bo->full_size, bo->base.size, FALSE, bo->tiling, bo->stride); if (unlikely (new_bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); intel_bo_in_flight_add (&device->intel, bo); intel_bo_destroy (&device->intel, bo); bo = new_bo; surface->intel.drm.bo = &bo->base; if (need_clear) { memset (intel_bo_map (&device->intel, bo), 0, bo->stride * surface->intel.drm.height); } } if (image->format == surface->intel.drm.format) { for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int y2 = _cairo_fixed_integer_round (box[i].p2.y); cairo_status_t status; if (x1 + tx < 0) x1 = -tx; if (x2 + tx > image->width) x2 = image->width - tx; if (y1 + ty < 0) y1 = -ty; if (y2 + ty > image->height) y2 = image->height - ty; if (x2 <= x1 || y2 <= y1) continue; if (x2 < 0 || y2 < 0) continue; if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height) continue; status = intel_bo_put_image (&device->intel, bo, image, x1 + tx, y1 + ty, x2 - x1, y2 - y1, x1, y1); if (unlikely (status)) return status; } } } else { pixman_image_t *dst; void *ptr; ptr = intel_bo_map (&device->intel, bo); if (unlikely (ptr == NULL)) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); dst = pixman_image_create_bits (_cairo_format_to_pixman_format_code (surface->intel.drm.format), surface->intel.drm.width, surface->intel.drm.height, ptr, surface->intel.drm.stride); if (unlikely (dst == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int y2 = _cairo_fixed_integer_round (box[i].p2.y); if (x1 + tx < 0) x1 = -tx; if (x2 + tx > image->width) x2 = image->width - tx; if (y1 + ty < 0) y1 = -ty; if (y2 + ty > image->height) y2 = image->height - ty; if (x2 <= x1 || y2 <= y1) continue; if (x2 < 0 || y2 < 0) continue; if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height) continue; pixman_image_composite32 (PIXMAN_OP_SRC, image->pixman_image, NULL, dst, x1 + tx, y1 + ty, 0, 0, x1, y1, x2 - x1, y2 - y1); } } pixman_image_unref (dst); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _composite_boxes (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_boxes_t *boxes, cairo_antialias_t antialias, cairo_clip_t *clip, double opacity, const cairo_composite_rectangles_t *extents) { cairo_bool_t need_clip_surface = FALSE; cairo_region_t *clip_region = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; i915_shader_t shader; i915_device_t *device; int i; /* If the boxes are not pixel-aligned, we will need to compute a real mask */ if (antialias != CAIRO_ANTIALIAS_NONE) { if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; } if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && opacity == 1.) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { status = i915_blt_boxes (dst, pattern, &extents->bounded, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _upload_image_inplace (dst, pattern, &extents->bounded, boxes); } if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } } if (i915_surface_needs_tiling (dst)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } i915_shader_init (&shader, dst, op, opacity); status = i915_shader_acquire_pattern (&shader, &shader.source, pattern, &extents->bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; if (need_clip_surface) i915_shader_set_clip (&shader, clip); } device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i915_shader_commit (&shader, device); if (unlikely (status)) goto err_device; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y2 = _cairo_fixed_integer_round (box[i].p2.y); if (x2 > x1 && y2 > y1) shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); } } if (! extents->is_bounded) status = i915_fixup_unbounded_boxes (dst, extents, clip, boxes); err_device: cairo_device_release (&device->intel.base.base); err_shader: i915_shader_fini (&shader); return status; } cairo_status_t i915_surface_clear (i915_surface_t *dst) { i915_device_t *device; cairo_status_t status; intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) }; device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return status; if (i915_surface_needs_tiling (dst)) { int cmd, br13, clear = 0; if (! i915_check_aperture_and_fences (device, bo_array, 1) || i915_batch_space (device) < 6) { status = i915_batch_flush (device); if (unlikely (status)) { cairo_device_release (&device->intel.base.base); return status; } } if (device->vertex_count) i915_vbo_flush (device); cmd = XY_COLOR_BLT_CMD; br13 = (0xCC << 16) | dst->intel.drm.stride; switch (dst->intel.drm.format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: ASSERT_NOT_REACHED; case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_RGB16_565: br13 |= BR13_565; break; case CAIRO_FORMAT_RGB24: clear = 0xff000000; case CAIRO_FORMAT_ARGB32: br13 |= BR13_8888; cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; break; } OUT_DWORD (cmd); OUT_DWORD (br13); OUT_DWORD (0); OUT_DWORD (((dst->intel.drm.height - 1) << 16) | (dst->intel.drm.width - 1)); OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); OUT_DWORD (clear); } else { if (! i915_check_aperture (device, bo_array, 1) || i915_batch_space (device) < 24) { status = i915_batch_flush (device); if (unlikely (status)) { cairo_device_release (&device->intel.base.base); return status; } } if (device->vertex_count) i915_vbo_flush (device); i915_set_dst (device, dst); /* set clear parameters */ if (device->clear_alpha != (dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA)) { device->clear_alpha = dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA; OUT_DWORD (_3DSTATE_CLEAR_PARAMETERS); OUT_DWORD (CLEARPARAM_CLEAR_RECT | CLEARPARAM_WRITE_COLOR); /* ZONE_INIT color */ if (device->clear_alpha) /* XXX depends on pixel format, 16bit needs replication, 8bit? */ OUT_DWORD (0x00000000); else OUT_DWORD (0xff000000); OUT_DWORD (0); /* ZONE_INIT depth */ /* CLEAR_RECT color */ if (device->clear_alpha) OUT_DWORD (0x00000000); else OUT_DWORD (0xff000000); OUT_DWORD (0); /* CLEAR_RECT depth */ OUT_DWORD (0); /* CLEAR_RECT stencil */ } OUT_DWORD (PRIM3D_CLEAR_RECT | 5); OUT_DWORD (pack_float (dst->intel.drm.width)); OUT_DWORD (pack_float (dst->intel.drm.height)); OUT_DWORD (0); OUT_DWORD (pack_float (dst->intel.drm.height)); OUT_DWORD (0); OUT_DWORD (0); } cairo_device_release (&device->intel.base.base); dst->deferred_clear = FALSE; return status; } static cairo_status_t _clip_and_composite_boxes (i915_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, cairo_antialias_t antialias, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip, double opacity) { cairo_status_t status; if (boxes->num_boxes == 0) { if (extents->is_bounded) return CAIRO_STATUS_SUCCESS; return i915_fixup_unbounded (dst, extents, clip); } if (clip == NULL && (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->intel.drm.base.is_clear)) && opacity == 1. && boxes->num_boxes == 1 && extents->bounded.width == dst->intel.drm.width && extents->bounded.height == dst->intel.drm.height) { op = CAIRO_OPERATOR_SOURCE; dst->deferred_clear = FALSE; status = _upload_image_inplace (dst, src, &extents->bounded, boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; } if (dst->deferred_clear) { status = i915_surface_clear (dst); if (unlikely (status)) return status; } /* Use a fast path if the boxes are pixel aligned */ status = _composite_boxes (dst, op, src, boxes, antialias, clip, opacity, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; /* Otherwise render the boxes via an implicit mask and composite in the usual * fashion. */ return i915_clip_and_composite_spans (dst, op, src, antialias, _composite_boxes_spans, boxes, extents, clip, opacity); } static cairo_clip_path_t * _clip_get_solitary_path (cairo_clip_t *clip) { cairo_clip_path_t *iter = clip->path; cairo_clip_path_t *path = NULL; do { if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) { if (path != NULL) return FALSE; path = iter; } iter = iter->prev; } while (iter != NULL); return path; } typedef struct { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; } composite_polygon_info_t; static cairo_status_t _composite_polygon_spans (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents) { composite_polygon_info_t *info = closure; cairo_botor_scan_converter_t converter; cairo_status_t status; cairo_box_t box; box.p1.x = _cairo_fixed_from_int (extents->x); box.p1.y = _cairo_fixed_from_int (extents->y); box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); status = converter.base.add_polygon (&converter.base, &info->polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) status = converter.base.generate (&converter.base, renderer); converter.base.destroy (&converter.base); return status; } static cairo_int_status_t i915_surface_fill_with_alpha (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip, double opacity) { i915_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; composite_polygon_info_t info; cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_status_t status; status = _cairo_composite_rectangles_init_for_fill (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, path, clip); if (unlikely (status)) return status; if (_cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (extents.is_bounded && clip != NULL) { cairo_clip_path_t *clip_path; if (((clip_path = _clip_get_solitary_path (clip)) != NULL) && _cairo_path_fixed_equal (&clip_path->path, path)) { clip = NULL; } } if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } assert (! _cairo_path_fixed_fill_is_empty (path)); if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, &boxes); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (dst, op, source, &boxes, antialias, &extents, clip, opacity); } _cairo_boxes_fini (&boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto CLEANUP_BOXES; } _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon); if (unlikely (status)) goto CLEANUP_POLYGON; if (extents.is_bounded) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) goto CLEANUP_POLYGON; } if (info.polygon.num_edges == 0) { if (! extents.is_bounded) status = i915_fixup_unbounded (dst, &extents, clip); goto CLEANUP_POLYGON; } info.fill_rule = fill_rule; info.antialias = antialias; status = i915_clip_and_composite_spans (dst, op, source, antialias, _composite_polygon_spans, &info, &extents, clip, opacity); CLEANUP_POLYGON: _cairo_polygon_fini (&info.polygon); CLEANUP_BOXES: if (clip_boxes != boxes_stack) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i915_surface_paint_with_alpha (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip, double opacity) { i915_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_clip_path_t *clip_path; cairo_boxes_t boxes; int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); cairo_box_t *clip_boxes = boxes.boxes_embedded; cairo_status_t status; status = _cairo_composite_rectangles_init_for_paint (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, clip); if (unlikely (status)) return status; if (_cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } /* If the clip cannot be reduced to a set of boxes, we will need to * use a clipmask. Paint is special as it is the only operation that * does not implicitly use a mask, so we may be able to reduce this * operation to a fill... */ if (clip != NULL && extents.is_bounded && (clip_path = _clip_get_solitary_path (clip)) != NULL) { status = i915_surface_fill_with_alpha (dst, op, source, &clip_path->path, clip_path->fill_rule, clip_path->tolerance, clip_path->antialias, NULL, opacity); } else { _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); status = _clip_and_composite_boxes (dst, op, source, &boxes, CAIRO_ANTIALIAS_DEFAULT, &extents, clip, opacity); } if (clip_boxes != boxes.boxes_embedded) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i915_surface_paint (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip) { i915_surface_t *dst = abstract_dst; /* XXX unsupported operators? use pixel shader blending, eventually */ if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { dst->deferred_clear = TRUE; return CAIRO_STATUS_SUCCESS; } return i915_surface_paint_with_alpha (dst, op, source, clip, 1.); } static cairo_int_status_t i915_surface_mask (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_clip_t *clip) { i915_surface_t *dst = abstract_dst; i915_device_t *device; cairo_composite_rectangles_t extents; i915_shader_t shader; cairo_clip_t local_clip; cairo_region_t *clip_region = NULL; cairo_bool_t need_clip_surface = FALSE; cairo_bool_t have_clip = FALSE; cairo_status_t status; if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; return i915_surface_paint_with_alpha (dst, op, source, clip, solid->color.alpha); } status = _cairo_composite_rectangles_init_for_mask (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, mask, clip); if (unlikely (status)) return status; if (_cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL && extents.is_bounded) { clip = _cairo_clip_init_copy (&local_clip, clip); status = _cairo_clip_rectangle (clip, &extents.bounded); if (unlikely (status)) { _cairo_clip_fini (&local_clip); return status; } have_clip = TRUE; } i915_shader_init (&shader, dst, op, 1.); status = i915_shader_acquire_pattern (&shader, &shader.source, source, &extents.bounded); if (unlikely (status)) goto err_shader; status = i915_shader_acquire_pattern (&shader, &shader.mask, mask, &extents.bounded); if (unlikely (status)) goto err_shader; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); if (unlikely (_cairo_status_is_error (status) || status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { goto err_shader; } need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; if (need_clip_surface) i915_shader_set_clip (&shader, clip); if (clip_region != NULL) { cairo_rectangle_int_t rect; cairo_bool_t is_empty; status = CAIRO_STATUS_SUCCESS; cairo_region_get_extents (clip_region, &rect); is_empty = ! _cairo_rectangle_intersect (&extents.unbounded, &rect); if (unlikely (is_empty)) goto err_shader; is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &rect); if (unlikely (is_empty && extents.is_bounded)) goto err_shader; if (cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; } } if (i915_surface_needs_tiling (dst)) { ASSERT_NOT_REACHED; return CAIRO_INT_STATUS_UNSUPPORTED; } device = i915_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; if (dst->deferred_clear) { status = i915_surface_clear (dst); if (unlikely (status)) goto err_shader; } status = i915_shader_commit (&shader, device); if (unlikely (status)) goto err_device; if (clip_region != NULL) { unsigned int n, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (n = 0; n < num_rectangles; n++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, n, &rect); shader.add_rectangle (&shader, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); } } else { shader.add_rectangle (&shader, extents.bounded.x, extents.bounded.y, extents.bounded.x + extents.bounded.width, extents.bounded.y + extents.bounded.height); } if (! extents.is_bounded) status = i915_fixup_unbounded (dst, &extents, clip); err_device: cairo_device_release (&device->intel.base.base); err_shader: i915_shader_fini (&shader); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i915_surface_stroke (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { i915_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; composite_polygon_info_t info; cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_status_t status; status = _cairo_composite_rectangles_init_for_stroke (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, path, stroke_style, ctm, clip); if (unlikely (status)) return status; if (_cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, stroke_style, ctm, &boxes); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (dst, op, source, &boxes, antialias, &extents, clip, 1.); } _cairo_boxes_fini (&boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto CLEANUP_BOXES; } _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &info.polygon); if (unlikely (status)) goto CLEANUP_POLYGON; if (extents.is_bounded) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) goto CLEANUP_POLYGON; } if (info.polygon.num_edges == 0) { if (! extents.is_bounded) status = i915_fixup_unbounded (dst, &extents, clip); goto CLEANUP_POLYGON; } info.fill_rule = CAIRO_FILL_RULE_WINDING; info.antialias = antialias; status = i915_clip_and_composite_spans (dst, op, source, antialias, _composite_polygon_spans, &info, &extents, clip, 1.); CLEANUP_POLYGON: _cairo_polygon_fini (&info.polygon); CLEANUP_BOXES: if (clip_boxes != boxes_stack) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i915_surface_fill (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t*source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { return i915_surface_fill_with_alpha (abstract_dst, op, source, path, fill_rule, tolerance, antialias, clip, 1.); } static const cairo_surface_backend_t i915_surface_backend = { CAIRO_SURFACE_TYPE_DRM, _cairo_default_context_create, i915_surface_create_similar, i915_surface_finish, NULL, intel_surface_acquire_source_image, intel_surface_release_source_image, NULL, NULL, NULL, NULL, /* composite */ NULL, /* fill */ NULL, /* trapezoids */ NULL, /* span */ NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_drm_surface_get_extents, NULL, /* old-glyphs */ _cairo_drm_surface_get_font_options, i915_surface_flush, NULL, /* mark_dirty */ intel_scaled_font_fini, intel_scaled_glyph_fini, i915_surface_paint, i915_surface_mask, i915_surface_stroke, i915_surface_fill, i915_surface_glyphs, }; static void i915_surface_init (i915_surface_t *surface, cairo_drm_device_t *device, cairo_format_t format, int width, int height) { intel_surface_init (&surface->intel, &i915_surface_backend, device, format, width, height); switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: surface->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER; break; case CAIRO_FORMAT_RGB24: surface->map0 = MAPSURF_32BIT | MT_32BIT_XRGB8888; surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER; break; case CAIRO_FORMAT_RGB16_565: surface->map0 = MAPSURF_16BIT | MT_16BIT_RGB565; surface->colorbuf = COLR_BUF_RGB565; break; case CAIRO_FORMAT_A8: surface->map0 = MAPSURF_8BIT | MT_8BIT_A8; surface->colorbuf = COLR_BUF_8BIT | DEPTH_FRMT_24_FIXED_8_OTHER; break; } surface->colorbuf |= DSTORG_HORT_BIAS (0x8) | DSTORG_VERT_BIAS (0x8); surface->map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | ((width - 1) << MS3_WIDTH_SHIFT); surface->map1 = 0; surface->is_current_texture = 0; surface->deferred_clear = FALSE; surface->offset = 0; surface->stencil = NULL; surface->cache = NULL; } cairo_surface_t * i915_surface_create_internal (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height, uint32_t tiling, cairo_bool_t gpu_target) { i915_surface_t *surface; cairo_status_t status_ignored; surface = malloc (sizeof (i915_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); i915_surface_init (surface, base_dev, format, width, height); if (width && height) { uint32_t size, stride; intel_bo_t *bo; width = (width + 3) & -4; stride = cairo_format_stride_for_width (surface->intel.drm.format, width); /* check for tiny surfaces for which tiling is irrelevant */ if (height * stride <= 4096) tiling = I915_TILING_NONE; if (tiling != I915_TILING_NONE && stride <= 512) tiling = I915_TILING_NONE; if (tiling != I915_TILING_NONE) { if (height <= 8) tiling = I915_TILING_NONE; else if (height <= 16) tiling = I915_TILING_X; } /* large surfaces we need to blt, so force TILING_X */ if (height > 2048) tiling = I915_TILING_X; /* but there is a maximum limit to the tiling pitch */ if (tiling != I915_TILING_NONE && stride > 8192) tiling = I915_TILING_NONE; stride = i915_tiling_stride (tiling, stride); assert (stride >= (uint32_t) cairo_format_stride_for_width (surface->intel.drm.format, width)); assert (tiling == I915_TILING_NONE || stride <= 8192); height = i915_tiling_height (tiling, height); if (height > 64*1024) { free (surface); cairo_device_destroy (&base_dev->base); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } size = stride * height; bo = intel_bo_create (to_intel_device (&base_dev->base), i915_tiling_size (tiling, size), size, gpu_target, tiling, stride); if (bo == NULL) { status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } assert (bo->base.size >= size); surface->intel.drm.bo = &bo->base; surface->intel.drm.stride = stride; surface->map0 |= MS3_tiling (tiling); surface->map1 = (stride/4 - 1) << MS4_PITCH_SHIFT; } return &surface->intel.drm.base; } static cairo_surface_t * i915_surface_create (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height) { switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_INVALID: default: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } return i915_surface_create_internal (base_dev, format, width, height, I915_TILING_DEFAULT, TRUE); } static cairo_surface_t * i915_surface_create_for_name (cairo_drm_device_t *base_dev, unsigned int name, cairo_format_t format, int width, int height, int stride) { i915_surface_t *surface; /* Vol I, p134: size restrictions for textures */ /* Vol I, p129: destination surface stride must be a multiple of 32 bytes */ if (stride < cairo_format_stride_for_width (format, (width + 3) & -4) || stride & 31) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); } switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; } surface = malloc (sizeof (i915_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); i915_surface_init (surface, base_dev, format, width, height); if (width && height) { surface->intel.drm.stride = stride; surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT; surface->intel.drm.bo = &intel_bo_create_for_name (to_intel_device (&base_dev->base), name)->base; if (unlikely (surface->intel.drm.bo == NULL)) { free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } to_intel_bo (surface->intel.drm.bo)->stride = stride; surface->map0 |= MS3_tiling (to_intel_bo (surface->intel.drm.bo)->tiling); } return &surface->intel.drm.base; } static cairo_status_t i915_buffer_cache_init (intel_buffer_cache_t *cache, i915_device_t *device, cairo_format_t format, int width, int height) { const uint32_t tiling = I915_TILING_DEFAULT; uint32_t stride, size; assert ((width & 3) == 0); assert ((height & 1) == 0); cache->buffer.width = width; cache->buffer.height = height; switch (format) { case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_RGB16_565: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; stride = width * 4; break; case CAIRO_FORMAT_A8: cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8; stride = width; break; } assert ((stride & 7) == 0); assert (i915_tiling_stride (tiling, stride) == stride); assert (i915_tiling_height (tiling, height) == height); size = height * stride; assert (i915_tiling_size (tiling, size) == size); cache->buffer.bo = intel_bo_create (&device->intel, size, size, FALSE, tiling, stride); if (unlikely (cache->buffer.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cache->buffer.stride = cache->buffer.bo->stride; cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | ((width - 1) << MS3_WIDTH_SHIFT); cache->buffer.map0 |= MS3_tiling (tiling); cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT; cache->ref_count = 0; cairo_list_init (&cache->link); return CAIRO_STATUS_SUCCESS; } i915_surface_t * i915_surface_create_from_cacheable_image_internal (i915_device_t *device, cairo_image_surface_t *image) { i915_surface_t *surface; cairo_status_t status; cairo_list_t *caches; intel_buffer_cache_t *cache; cairo_rtree_node_t *node; cairo_format_t format; int width, height, bpp; format = image->format; if (format == CAIRO_FORMAT_A1) format = CAIRO_FORMAT_A8; width = image->width; height = image->height; if (width > IMAGE_CACHE_WIDTH/2 || height > IMAGE_CACHE_HEIGHT/2) { surface = (i915_surface_t *) i915_surface_create_internal (&device->intel.base, format, width, height, I915_TILING_NONE, FALSE); if (unlikely (surface->intel.drm.base.status)) return surface; status = intel_bo_put_image (&device->intel, to_intel_bo (surface->intel.drm.bo), image, 0, 0, width, height, 0, 0); if (unlikely (status)) { cairo_surface_destroy (&surface->intel.drm.base); return (i915_surface_t *) _cairo_surface_create_in_error (status); } return surface; } status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return (i915_surface_t *) _cairo_surface_create_in_error (status); switch (image->format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_RGB16_565: caches = &device->image_caches[0]; format = CAIRO_FORMAT_ARGB32; bpp = 4; break; case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: caches = &device->image_caches[1]; format = CAIRO_FORMAT_A8; bpp = 1; break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); goto CLEANUP_DEVICE; } node = NULL; cairo_list_foreach_entry (cache, intel_buffer_cache_t, caches, link) { if (! intel_bo_is_inactive (&device->intel, cache->buffer.bo)) continue; status = _cairo_rtree_insert (&cache->rtree, width, height, &node); if (unlikely (_cairo_status_is_error (status))) goto CLEANUP_DEVICE; if (status == CAIRO_STATUS_SUCCESS) break; } if (node == NULL) { cache = malloc (sizeof (intel_buffer_cache_t)); if (unlikely (cache == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DEVICE; } status = i915_buffer_cache_init (cache, device, format, IMAGE_CACHE_WIDTH, IMAGE_CACHE_HEIGHT); if (unlikely (status)) { free (cache); goto CLEANUP_DEVICE; } _cairo_rtree_init (&cache->rtree, IMAGE_CACHE_WIDTH, IMAGE_CACHE_HEIGHT, 4, sizeof (i915_image_private_t)); status = _cairo_rtree_insert (&cache->rtree, width, height, &node); assert (status == CAIRO_STATUS_SUCCESS); cairo_list_init (&cache->link); } cairo_list_move (&cache->link, caches); ((i915_image_private_t *) node)->container = cache; status = intel_bo_put_image (&device->intel, cache->buffer.bo, image, 0, 0, width, height, node->x, node->y); if (unlikely (status)) goto CLEANUP_CACHE; surface = malloc (sizeof (i915_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_CACHE; } i915_surface_init (surface, &device->intel.base, format, width, height); surface->intel.drm.stride = cache->buffer.stride; surface->map0 |= MS3_tiling (cache->buffer.bo->tiling); surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT; surface->intel.drm.bo = &intel_bo_reference (cache->buffer.bo)->base; surface->offset = node->y * cache->buffer.stride + bpp * node->x; surface->cache = (i915_image_private_t *) node; cache->ref_count++; cairo_device_release (&device->intel.base.base); return surface; CLEANUP_CACHE: _cairo_rtree_node_destroy (&cache->rtree, node); if (cache->ref_count == 0) { intel_bo_destroy (&device->intel, cache->buffer.bo); _cairo_rtree_fini (&cache->rtree); cairo_list_del (&cache->link); free (cache); } CLEANUP_DEVICE: cairo_device_release (&device->intel.base.base); return (i915_surface_t *) _cairo_surface_create_in_error (status); } static cairo_surface_t * i915_surface_create_from_cacheable_image (cairo_drm_device_t *device, cairo_surface_t *source) { i915_surface_t *surface; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; status = _cairo_surface_acquire_source_image (source, &image, &image_extra); if (unlikely (status)) return _cairo_surface_create_in_error (status); surface = i915_surface_create_from_cacheable_image_internal ((i915_device_t *) device, image); _cairo_surface_release_source_image (source, image, image_extra); return &surface->intel.drm.base; } static cairo_status_t i915_surface_enable_scan_out (void *abstract_surface) { i915_surface_t *surface = abstract_surface; intel_bo_t *bo; cairo_status_t status; if (unlikely (surface->intel.drm.bo == NULL)) return _cairo_error (CAIRO_STATUS_INVALID_SIZE); bo = to_intel_bo (surface->intel.drm.bo); if (bo->tiling == I915_TILING_Y) { status = i915_surface_batch_flush (surface); if (unlikely (status)) return status; bo->tiling = I915_TILING_X; surface->map0 &= ~MS3_tiling (I915_TILING_Y); surface->map0 |= MS3_tiling (I915_TILING_X); } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t i915_device_flush (cairo_drm_device_t *device) { cairo_status_t status; if (unlikely (device->base.finished)) return CAIRO_STATUS_SUCCESS; status = cairo_device_acquire (&device->base); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = i915_batch_flush ((i915_device_t *) device); cairo_device_release (&device->base); } return status; } static cairo_int_status_t i915_device_throttle (cairo_drm_device_t *device) { cairo_status_t status; status = cairo_device_acquire (&device->base); if (unlikely (status)) return status; status = i915_batch_flush ((i915_device_t *) device); intel_throttle ((intel_device_t *) device); cairo_device_release (&device->base); return status; } static void i915_device_destroy (void *data) { i915_device_t *device = data; if (device->last_vbo) intel_bo_destroy (&device->intel, device->last_vbo); i915_batch_cleanup (device); intel_device_fini (&device->intel); free (device); } COMPILE_TIME_ASSERT (sizeof (i915_batch_setup) == sizeof (((i915_device_t *)0)->batch_header)); COMPILE_TIME_ASSERT (offsetof (i915_device_t, batch_base) == offsetof (i915_device_t, batch_header) + sizeof (i915_batch_setup)); cairo_drm_device_t * _cairo_drm_i915_device_create (int fd, dev_t dev_id, int vendor_id, int chip_id) { i915_device_t *device; cairo_status_t status; uint64_t gtt_size; int n; if (! intel_info (fd, >t_size)) return NULL; device = malloc (sizeof (i915_device_t)); if (device == NULL) return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); status = intel_device_init (&device->intel, fd); if (unlikely (status)) { free (device); return (cairo_drm_device_t *) _cairo_device_create_in_error (status); } device->debug = 0; if (getenv ("CAIRO_DEBUG_DRM") != NULL) device->debug = I915_DEBUG_SYNC; n = intel_get (fd, I915_PARAM_NUM_FENCES_AVAIL); if (n == 0) n = 8; device->batch.fences_avail = n - 2; /* conservative */ device->batch.gtt_avail_size = device->intel.gtt_avail_size / 4; device->batch.est_gtt_size = I915_BATCH_SIZE; device->batch.total_gtt_size = I915_BATCH_SIZE; device->batch.exec_count = 0; device->batch.reloc_count = 0; device->batch.used = 0; device->batch.fences = 0; memcpy (device->batch_header, i915_batch_setup, sizeof (i915_batch_setup)); device->vbo = 0; device->vbo_offset = 0; device->vbo_used = 0; device->vertex_index = 0; device->vertex_count = 0; device->last_vbo = NULL; for (n = 0; n < ARRAY_LENGTH (device->image_caches); n++) cairo_list_init (&device->image_caches[n]); device->intel.base.surface.create = i915_surface_create; device->intel.base.surface.create_for_name = i915_surface_create_for_name; device->intel.base.surface.create_from_cacheable_image = i915_surface_create_from_cacheable_image; device->intel.base.surface.flink = _cairo_drm_surface_flink; device->intel.base.surface.enable_scan_out = i915_surface_enable_scan_out; device->intel.base.surface.map_to_image = intel_surface_map_to_image; device->intel.base.device.flush = i915_device_flush; device->intel.base.device.throttle = i915_device_throttle; device->intel.base.device.destroy = i915_device_destroy; device->floats_per_vertex = 0; device->current_source = NULL; device->current_mask = NULL; device->current_clip = NULL; i915_device_reset (device); return _cairo_drm_device_init (&device->intel.base, fd, dev_id, vendor_id, chip_id, 16*1024); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i965-glyphs.c000066400000000000000000000345741271037650300271160ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-drm-i965-private.h" #include "cairo-error-private.h" #include "cairo-rtree-private.h" typedef struct _i965_glyphs i965_glyphs_t; typedef float * (*i965_get_rectangle_func_t) (i965_glyphs_t *glyphs); struct _i965_glyphs { i965_get_rectangle_func_t get_rectangle; i965_shader_t shader; struct i965_vbo head, *tail; unsigned int vbo_offset; float *vbo_base; }; static float * i965_glyphs_emit_rectangle (i965_glyphs_t *glyphs) { return i965_add_rectangle (glyphs->shader.device); } static float * i965_glyphs_accumulate_rectangle (i965_glyphs_t *glyphs) { float *vertices; uint32_t size; size = glyphs->shader.device->rectangle_size; if (unlikely (glyphs->vbo_offset + size > I965_VERTEX_SIZE)) { struct i965_vbo *vbo; vbo = malloc (sizeof (struct i965_vbo)); if (unlikely (vbo == NULL)) { /* throw error! */ } glyphs->tail->next = vbo; glyphs->tail = vbo; vbo->next = NULL; vbo->bo = intel_bo_create (&glyphs->shader.device->intel, I965_VERTEX_SIZE, I965_VERTEX_SIZE, FALSE, I915_TILING_NONE, 0); vbo->count = 0; glyphs->vbo_offset = 0; glyphs->vbo_base = intel_bo_map (&glyphs->shader.device->intel, vbo->bo); } vertices = glyphs->vbo_base + glyphs->vbo_offset; glyphs->vbo_offset += size; glyphs->tail->count += 3; return vertices; } static void i965_add_glyph_rectangle (i965_glyphs_t *glyphs, int x1, int y1, int x2, int y2, intel_glyph_t *glyph) { float *v; /* Each vertex is: * 2 vertex coordinates * 1 glyph texture coordinate */ v = glyphs->get_rectangle (glyphs); /* bottom right */ *v++ = x2; *v++ = y2; *v++ = glyph->texcoord[0]; /* bottom left */ *v++ = x1; *v++ = y2; *v++ = glyph->texcoord[1]; /* top left */ *v++ = x1; *v++ = y1; *v++ = glyph->texcoord[2]; } static cairo_status_t i965_surface_mask_internal (i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *source, i965_surface_t *mask, cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { i965_device_t *device; i965_shader_t shader; cairo_region_t *clip_region = NULL; cairo_status_t status; i965_shader_init (&shader, dst, op); status = i965_shader_acquire_pattern (&shader, &shader.source, source, &extents->bounded); if (unlikely (status)) return status; shader.mask.type.vertex = VS_NONE; shader.mask.type.fragment = FS_SURFACE; shader.mask.base.content = mask->intel.drm.base.content; shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST); shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_translate (&shader.mask.base.matrix, -extents->bounded.x, -extents->bounded.y); cairo_matrix_scale (&shader.mask.base.matrix, 1. / mask->intel.drm.width, 1. / mask->intel.drm.height); shader.mask.base.bo = to_intel_bo (mask->intel.drm.bo); shader.mask.base.format = mask->intel.drm.format; shader.mask.base.width = mask->intel.drm.width; shader.mask.base.height = mask->intel.drm.height; shader.mask.base.stride = mask->intel.drm.stride; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&shader, clip); } status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SHADER; device = i965_device (dst); status = i965_shader_commit (&shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; if (clip_region != NULL) { unsigned int n, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (n = 0; n < num_rectangles; n++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, n, &rect); i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } } else { i965_shader_add_rectangle (&shader, extents->bounded.x, extents->bounded.y, extents->bounded.width, extents->bounded.height); } if (! extents->is_bounded) status = i965_fixup_unbounded (dst, extents, clip); CLEANUP_DEVICE: cairo_device_release (&device->intel.base.base); CLEANUP_SHADER: i965_shader_fini (&shader); return status; } cairo_int_status_t i965_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *g, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { i965_surface_t *surface = abstract_surface; i965_surface_t *mask = NULL; i965_device_t *device; i965_glyphs_t glyphs; cairo_composite_rectangles_t extents; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_bool_t overlap; cairo_region_t *clip_region = NULL; intel_bo_t *last_bo = NULL; cairo_scaled_glyph_t *glyph_cache[64]; cairo_status_t status; int mask_x = 0, mask_y = 0; int i = 0; *num_remaining = 0; status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface->intel.drm.width, surface->intel.drm.height, op, source, scaled_font, g, num_glyphs, clip, &overlap); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_rectangle (clip, &extents.mask)) clip = NULL; if (clip != NULL && extents.is_bounded) { clip = _cairo_clip_init_copy (&local_clip, clip); status = _cairo_clip_rectangle (clip, &extents.bounded); if (unlikely (status)) return status; have_clip = TRUE; } if (overlap || ! extents.is_bounded) { cairo_format_t format; format = CAIRO_FORMAT_A8; if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) format = CAIRO_FORMAT_ARGB32; mask = (i965_surface_t *) i965_surface_create_internal (&i965_device (surface)->intel.base, format, extents.bounded.width, extents.bounded.height, I965_TILING_DEFAULT, TRUE); if (unlikely (mask->intel.drm.base.status)) return mask->intel.drm.base.status; status = _cairo_surface_paint (&mask->intel.drm.base, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, NULL); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } i965_shader_init (&glyphs.shader, mask, CAIRO_OPERATOR_ADD); status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, &_cairo_pattern_white.base, &extents.bounded); if (unlikely (status)) { cairo_surface_destroy (&mask->intel.drm.base); return status; } mask_x = -extents.bounded.x; mask_y = -extents.bounded.y; } else { i965_shader_init (&glyphs.shader, surface, op); status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, source, &extents.bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&glyphs.shader, clip); } } glyphs.head.next = NULL; glyphs.head.bo = NULL; glyphs.head.count = 0; glyphs.tail = &glyphs.head; device = i965_device (surface); if (mask != NULL || clip_region == NULL) { glyphs.get_rectangle = i965_glyphs_emit_rectangle; } else { glyphs.get_rectangle = i965_glyphs_accumulate_rectangle; glyphs.head.bo = intel_bo_create (&device->intel, I965_VERTEX_SIZE, I965_VERTEX_SIZE, FALSE, I915_TILING_NONE, 0); if (unlikely (glyphs.head.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); glyphs.vbo_base = intel_bo_map (&device->intel, glyphs.head.bo); } glyphs.vbo_offset = 0; status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto CLEANUP_GLYPHS; _cairo_scaled_font_freeze_cache (scaled_font); //private = _cairo_scaled_font_get_device (scaled_font, device); if (scaled_font->surface_private == NULL) { scaled_font->surface_private = device; scaled_font->surface_backend = surface->intel.drm.base.backend; cairo_list_add (&scaled_font->link, &device->intel.fonts); } memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; int x, y, x1, x2, y1, y2; int cache_index = g[i].index % ARRAY_LENGTH (glyph_cache); intel_glyph_t *glyph; scaled_glyph = glyph_cache[cache_index]; if (scaled_glyph == NULL || _cairo_scaled_glyph_index (scaled_glyph) != g[i].index) { status = _cairo_scaled_glyph_lookup (scaled_font, g[i].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) goto FINISH; glyph_cache[cache_index] = scaled_glyph; } if (unlikely (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0)) { continue; } /* XXX glyph images are snapped to pixel locations */ x = _cairo_lround (g[i].x); y = _cairo_lround (g[i].y); x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); if (x2 < extents.bounded.x || y2 < extents.bounded.y || x1 > extents.bounded.x + extents.bounded.width || y1 > extents.bounded.y + extents.bounded.height) { continue; } if (scaled_glyph->surface_private == NULL) { status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph); if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { status = CAIRO_STATUS_SUCCESS; continue; } if (unlikely (status)) goto FINISH; } glyph = intel_glyph_pin (scaled_glyph->surface_private); if (glyph->cache->buffer.bo != last_bo) { intel_buffer_cache_t *cache = glyph->cache; glyphs.shader.mask.type.vertex = VS_GLYPHS; glyphs.shader.mask.type.fragment = FS_GLYPHS; glyphs.shader.mask.type.pattern = PATTERN_BASE; glyphs.shader.mask.base.bo = cache->buffer.bo; glyphs.shader.mask.base.format = cache->buffer.format; glyphs.shader.mask.base.width = cache->buffer.width; glyphs.shader.mask.base.height = cache->buffer.height; glyphs.shader.mask.base.stride = cache->buffer.stride; glyphs.shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST); glyphs.shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE); glyphs.shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ glyphs.shader.committed = FALSE; status = i965_shader_commit (&glyphs.shader, device); if (unlikely (status)) goto FINISH; last_bo = cache->buffer.bo; } x2 = x1 + glyph->width; y2 = y1 + glyph->height; if (mask_x) x1 += mask_x, x2 += mask_x; if (mask_y) y1 += mask_y, y2 += mask_y; i965_add_glyph_rectangle (&glyphs, x1, y1, x2, y2, glyph); } if (mask != NULL && clip_region != NULL) i965_clipped_vertices (device, &glyphs.head, clip_region); status = CAIRO_STATUS_SUCCESS; FINISH: _cairo_scaled_font_thaw_cache (scaled_font); cairo_device_release (surface->intel.drm.base.device); CLEANUP_GLYPHS: i965_shader_fini (&glyphs.shader); if (glyphs.head.bo != NULL) { struct i965_vbo *vbo, *next; intel_bo_destroy (&device->intel, glyphs.head.bo); for (vbo = glyphs.head.next; vbo != NULL; vbo = next) { next = vbo->next; intel_bo_destroy (&device->intel, vbo->bo); free (vbo); } } if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { cairo_path_fixed_t path; _cairo_path_fixed_init (&path); status = _cairo_scaled_font_glyph_path (scaled_font, g + i, num_glyphs - i, &path); if (mask_x | mask_y) { _cairo_path_fixed_translate (&path, _cairo_fixed_from_int (mask_x), _cairo_fixed_from_int (mask_y)); } if (likely (status == CAIRO_STATUS_SUCCESS)) { status = surface->intel.drm.base.backend->fill (glyphs.shader.target, glyphs.shader.op, mask != NULL ? &_cairo_pattern_white.base : source, &path, CAIRO_FILL_RULE_WINDING, 0, scaled_font->options.antialias, clip); } _cairo_path_fixed_fini (&path); } if (mask != NULL) { if (likely (status == CAIRO_STATUS_SUCCESS)) { status = i965_surface_mask_internal (surface, op, source, mask, clip, &extents); } cairo_surface_finish (&mask->intel.drm.base); cairo_surface_destroy (&mask->intel.drm.base); } if (have_clip) _cairo_clip_fini (&local_clip); return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i965-private.h000066400000000000000000000505601271037650300272600ustar00rootroot00000000000000#ifndef CAIRO_DRM_I965_PRIVATE_H #define CAIRO_DRM_I965_PRIVATE_H #include "cairo-drm-intel-private.h" #include "cairo-hash-private.h" #include "cairo-freelist-private.h" #include "cairo-drm-intel-brw-defines.h" #include #define BRW_MI_GLOBAL_SNAPSHOT_RESET (1 << 3) /* * New regs for broadwater -- we need to split this file up sensibly somehow. */ #define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \ ((Pipeline) << 27) | \ ((Opcode) << 24) | \ ((Subopcode) << 16)) #define BRW_URB_FENCE BRW_3D(0, 0, 0) #define BRW_CS_URB_STATE BRW_3D(0, 0, 1) #define BRW_CONSTANT_BUFFER BRW_3D(0, 0, 2) #define BRW_STATE_PREFETCH BRW_3D(0, 0, 3) #define BRW_STATE_BASE_ADDRESS BRW_3D(0, 1, 1) #define BRW_STATE_SIP BRW_3D(0, 1, 2) #define BRW_PIPELINE_SELECT BRW_3D(0, 1, 4) #define NEW_PIPELINE_SELECT BRW_3D(1, 1, 4) #define BRW_MEDIA_STATE_POINTERS BRW_3D(2, 0, 0) #define BRW_MEDIA_OBJECT BRW_3D(2, 1, 0) #define BRW_3DSTATE_PIPELINED_POINTERS BRW_3D(3, 0, 0) #define BRW_3DSTATE_BINDING_TABLE_POINTERS BRW_3D(3, 0, 1) #define BRW_3DSTATE_VERTEX_BUFFERS BRW_3D(3, 0, 8) #define BRW_3DSTATE_VERTEX_ELEMENTS BRW_3D(3, 0, 9) #define BRW_3DSTATE_INDEX_BUFFER BRW_3D(3, 0, 0xa) #define BRW_3DSTATE_VF_STATISTICS BRW_3D(3, 0, 0xb) #define BRW_3DSTATE_DRAWING_RECTANGLE BRW_3D(3, 1, 0) #define BRW_3DSTATE_CONSTANT_COLOR BRW_3D(3, 1, 1) #define BRW_3DSTATE_SAMPLER_PALETTE_LOAD BRW_3D(3, 1, 2) #define BRW_3DSTATE_CHROMA_KEY BRW_3D(3, 1, 4) #define BRW_3DSTATE_DEPTH_BUFFER BRW_3D(3, 1, 5) #define BRW_3DSTATE_POLY_STIPPLE_OFFSET BRW_3D(3, 1, 6) #define BRW_3DSTATE_POLY_STIPPLE_PATTERN BRW_3D(3, 1, 7) #define BRW_3DSTATE_LINE_STIPPLE BRW_3D(3, 1, 8) #define BRW_3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP BRW_3D(3, 1, 9) /* These two are BLC and CTG only, not BW or CL */ #define BRW_3DSTATE_AA_LINE_PARAMS BRW_3D(3, 1, 0xa) #define BRW_3DSTATE_GS_SVB_INDEX BRW_3D(3, 1, 0xb) #define BRW_PIPE_CONTROL BRW_3D(3, 2, 0) #define BRW_3DPRIMITIVE BRW_3D(3, 3, 0) #define PIPELINE_SELECT_3D 0 #define PIPELINE_SELECT_MEDIA 1 #define UF0_CS_REALLOC (1 << 13) #define UF0_VFE_REALLOC (1 << 12) #define UF0_SF_REALLOC (1 << 11) #define UF0_CLIP_REALLOC (1 << 10) #define UF0_GS_REALLOC (1 << 9) #define UF0_VS_REALLOC (1 << 8) #define UF1_CLIP_FENCE_SHIFT 20 #define UF1_GS_FENCE_SHIFT 10 #define UF1_VS_FENCE_SHIFT 0 #define UF2_CS_FENCE_SHIFT 20 #define UF2_VFE_FENCE_SHIFT 10 #define UF2_SF_FENCE_SHIFT 0 /* for BRW_STATE_BASE_ADDRESS */ #define BASE_ADDRESS_MODIFY (1 << 0) /* for BRW_3DSTATE_PIPELINED_POINTERS */ #define BRW_GS_DISABLE 0 #define BRW_GS_ENABLE 1 #define BRW_CLIP_DISABLE 0 #define BRW_CLIP_ENABLE 1 /* for BRW_PIPE_CONTROL */ #define BRW_PIPE_CONTROL_NOWRITE (0 << 14) #define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14) #define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14) #define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14) #define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13) #define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12) #define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11) #define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8) #define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2) #define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2) /* VERTEX_BUFFER_STATE Structure */ #define VB0_BUFFER_INDEX_SHIFT 27 #define VB0_VERTEXDATA (0 << 26) #define VB0_INSTANCEDATA (1 << 26) #define VB0_BUFFER_PITCH_SHIFT 0 /* VERTEX_ELEMENT_STATE Structure */ #define VE0_VERTEX_BUFFER_INDEX_SHIFT 27 #define VE0_VALID (1 << 26) #define VE0_FORMAT_SHIFT 16 #define VE0_OFFSET_SHIFT 0 #define VE1_VFCOMPONENT_0_SHIFT 28 #define VE1_VFCOMPONENT_1_SHIFT 24 #define VE1_VFCOMPONENT_2_SHIFT 20 #define VE1_VFCOMPONENT_3_SHIFT 16 #define VE1_DESTINATION_ELEMENT_OFFSET_SHIFT 0 /* 3DPRIMITIVE bits */ #define BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL (0 << 15) #define BRW_3DPRIMITIVE_VERTEX_RANDOM (1 << 15) /* Primitive types are in brw_defines.h */ #define BRW_3DPRIMITIVE_TOPOLOGY_SHIFT 10 #define BRW_SVG_CTL 0x7400 #define BRW_SVG_CTL_GS_BA (0 << 8) #define BRW_SVG_CTL_SS_BA (1 << 8) #define BRW_SVG_CTL_IO_BA (2 << 8) #define BRW_SVG_CTL_GS_AUB (3 << 8) #define BRW_SVG_CTL_IO_AUB (4 << 8) #define BRW_SVG_CTL_SIP (5 << 8) #define BRW_SVG_RDATA 0x7404 #define BRW_SVG_WORK_CTL 0x7408 #define BRW_VF_CTL 0x7500 #define BRW_VF_CTL_SNAPSHOT_COMPLETE (1 << 31) #define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_THREADID (0 << 8) #define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_VF_DEBUG (1 << 8) #define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_SEQUENCE (0 << 4) #define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_INDEX (1 << 4) #define BRW_VF_CTL_SKIP_INITIAL_PRIMITIVES (1 << 3) #define BRW_VF_CTL_MAX_PRIMITIVES_LIMIT_ENABLE (1 << 2) #define BRW_VF_CTL_VERTEX_RANGE_LIMIT_ENABLE (1 << 1) #define BRW_VF_CTL_SNAPSHOT_ENABLE (1 << 0) #define BRW_VF_STRG_VAL 0x7504 #define BRW_VF_STR_VL_OVR 0x7508 #define BRW_VF_VC_OVR 0x750c #define BRW_VF_STR_PSKIP 0x7510 #define BRW_VF_MAX_PRIM 0x7514 #define BRW_VF_RDATA 0x7518 #define BRW_VS_CTL 0x7600 #define BRW_VS_CTL_SNAPSHOT_COMPLETE (1 << 31) #define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_0 (0 << 8) #define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_1 (1 << 8) #define BRW_VS_CTL_SNAPSHOT_MUX_VALID_COUNT (2 << 8) #define BRW_VS_CTL_SNAPSHOT_MUX_VS_KERNEL_POINTER (3 << 8) #define BRW_VS_CTL_SNAPSHOT_ALL_THREADS (1 << 2) #define BRW_VS_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) #define BRW_VS_CTL_SNAPSHOT_ENABLE (1 << 0) #define BRW_VS_STRG_VAL 0x7604 #define BRW_VS_RDATA 0x7608 #define BRW_SF_CTL 0x7b00 #define BRW_SF_CTL_SNAPSHOT_COMPLETE (1 << 31) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_FF_ID (0 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_REL_COUNT (1 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_FF_ID (2 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_REL_COUNT (3 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_FF_ID (4 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_REL_COUNT (5 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_COUNT (6 << 8) #define BRW_SF_CTL_SNAPSHOT_MUX_SF_KERNEL_POINTER (7 << 8) #define BRW_SF_CTL_MIN_MAX_PRIMITIVE_RANGE_ENABLE (1 << 4) #define BRW_SF_CTL_DEBUG_CLIP_RECTANGLE_ENABLE (1 << 3) #define BRW_SF_CTL_SNAPSHOT_ALL_THREADS (1 << 2) #define BRW_SF_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) #define BRW_SF_CTL_SNAPSHOT_ENABLE (1 << 0) #define BRW_SF_STRG_VAL 0x7b04 #define BRW_SF_RDATA 0x7b18 #define BRW_WIZ_CTL 0x7c00 #define BRW_WIZ_CTL_SNAPSHOT_COMPLETE (1 << 31) #define BRW_WIZ_CTL_SUBSPAN_INSTANCE_SHIFT 16 #define BRW_WIZ_CTL_SNAPSHOT_MUX_WIZ_KERNEL_POINTER (0 << 8) #define BRW_WIZ_CTL_SNAPSHOT_MUX_SUBSPAN_INSTANCE (1 << 8) #define BRW_WIZ_CTL_SNAPSHOT_MUX_PRIMITIVE_SEQUENCE (2 << 8) #define BRW_WIZ_CTL_SINGLE_SUBSPAN_DISPATCH (1 << 6) #define BRW_WIZ_CTL_IGNORE_COLOR_SCOREBOARD_STALLS (1 << 5) #define BRW_WIZ_CTL_ENABLE_SUBSPAN_INSTANCE_COMPARE (1 << 4) #define BRW_WIZ_CTL_USE_UPSTREAM_SNAPSHOT_FLAG (1 << 3) #define BRW_WIZ_CTL_SNAPSHOT_ALL_THREADS (1 << 2) #define BRW_WIZ_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) #define BRW_WIZ_CTL_SNAPSHOT_ENABLE (1 << 0) #define BRW_WIZ_STRG_VAL 0x7c04 #define BRW_WIZ_RDATA 0x7c18 #define BRW_TS_CTL 0x7e00 #define BRW_TS_CTL_SNAPSHOT_COMPLETE (1 << 31) #define BRW_TS_CTL_SNAPSHOT_MESSAGE_ERROR (0 << 8) #define BRW_TS_CTL_SNAPSHOT_INTERFACE_DESCRIPTOR (3 << 8) #define BRW_TS_CTL_SNAPSHOT_ALL_CHILD_THREADS (1 << 2) #define BRW_TS_CTL_SNAPSHOT_ALL_ROOT_THREADS (1 << 1) #define BRW_TS_CTL_SNAPSHOT_ENABLE (1 << 0) #define BRW_TS_STRG_VAL 0x7e04 #define BRW_TS_RDATA 0x7e08 #define BRW_TD_CTL 0x8000 #define BRW_TD_CTL_MUX_SHIFT 8 #define BRW_TD_CTL_EXTERNAL_HALT_R0_DEBUG_MATCH (1 << 7) #define BRW_TD_CTL_FORCE_EXTERNAL_HALT (1 << 6) #define BRW_TD_CTL_EXCEPTION_MASK_OVERRIDE (1 << 5) #define BRW_TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE (1 << 4) #define BRW_TD_CTL_BREAKPOINT_ENABLE (1 << 2) #define BRW_TD_CTL2 0x8004 #define BRW_TD_CTL2_ILLEGAL_OPCODE_EXCEPTION_OVERRIDE (1 << 28) #define BRW_TD_CTL2_MASKSTACK_EXCEPTION_OVERRIDE (1 << 26) #define BRW_TD_CTL2_SOFTWARE_EXCEPTION_OVERRIDE (1 << 25) #define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_SHIFT 16 #define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_ENABLE (1 << 8) #define BRW_TD_CTL2_THREAD_SPAWNER_EXECUTION_MASK_ENABLE (1 << 7) #define BRW_TD_CTL2_WIZ_EXECUTION_MASK_ENABLE (1 << 6) #define BRW_TD_CTL2_SF_EXECUTION_MASK_ENABLE (1 << 5) #define BRW_TD_CTL2_CLIPPER_EXECUTION_MASK_ENABLE (1 << 4) #define BRW_TD_CTL2_GS_EXECUTION_MASK_ENABLE (1 << 3) #define BRW_TD_CTL2_VS_EXECUTION_MASK_ENABLE (1 << 0) #define BRW_TD_VF_VS_EMSK 0x8008 #define BRW_TD_GS_EMSK 0x800c #define BRW_TD_CLIP_EMSK 0x8010 #define BRW_TD_SF_EMSK 0x8014 #define BRW_TD_WIZ_EMSK 0x8018 #define BRW_TD_0_6_EHTRG_VAL 0x801c #define BRW_TD_0_7_EHTRG_VAL 0x8020 #define BRW_TD_0_6_EHTRG_MSK 0x8024 #define BRW_TD_0_7_EHTRG_MSK 0x8028 #define BRW_TD_RDATA 0x802c #define BRW_TD_TS_EMSK 0x8030 #define BRW_EU_CTL 0x8800 #define BRW_EU_CTL_SELECT_SHIFT 16 #define BRW_EU_CTL_DATA_MUX_SHIFT 8 #define BRW_EU_ATT_0 0x8810 #define BRW_EU_ATT_1 0x8814 #define BRW_EU_ATT_DATA_0 0x8820 #define BRW_EU_ATT_DATA_1 0x8824 #define BRW_EU_ATT_CLR_0 0x8830 #define BRW_EU_ATT_CLR_1 0x8834 #define BRW_EU_RDATA 0x8840 typedef struct i965_device i965_device_t; typedef struct i965_surface i965_surface_t; typedef struct i965_shader i965_shader_t; typedef struct i965_stream i965_stream_t; struct i965_sf_state { cairo_hash_entry_t entry; uint32_t offset; }; cairo_private cairo_bool_t i965_sf_state_equal (const void *, const void *); struct i965_cc_state { cairo_hash_entry_t entry; uint32_t offset; }; cairo_private cairo_bool_t i965_cc_state_equal (const void *, const void *); struct i965_wm_kernel { cairo_hash_entry_t entry; uint32_t offset; }; struct i965_wm_state { cairo_hash_entry_t entry; uint32_t kernel; uint32_t sampler; uint32_t offset; }; cairo_private cairo_bool_t i965_wm_state_equal (const void *, const void *); struct i965_wm_binding { cairo_hash_entry_t entry; uint32_t table[4]; int size; uint32_t offset; }; cairo_private cairo_bool_t i965_wm_binding_equal (const void *, const void *); struct i965_sampler { cairo_hash_entry_t entry; uint32_t offset; }; struct i965_vbo { struct i965_vbo *next; intel_bo_t *bo; unsigned int count; }; struct i965_surface { intel_surface_t intel; uint32_t stream; uint32_t offset; }; struct i965_pending_relocation { uint32_t offset; uint32_t read_domains; uint32_t write_domain; uint32_t delta; }; struct i965_stream { uint32_t used; uint32_t committed; uint32_t size; uint8_t *data; uint32_t serial; int num_pending_relocations; int max_pending_relocations; struct i965_pending_relocation *pending_relocations; int num_relocations; int max_relocations; struct drm_i915_gem_relocation_entry *relocations; }; #define I965_BATCH_SIZE (16 * 4096) #define I965_GENERAL_SIZE (16 * 4096) #define I965_SURFACE_SIZE (32 * 4096) #define I965_VERTEX_SIZE (128 * 4096) #define I965_TILING_DEFAULT I915_TILING_Y struct i965_device { intel_device_t intel; cairo_bool_t is_g4x; i965_shader_t *shader; /* note: only valid during geometry emission */ /* track state changes */ struct i965_sf_state sf_state; struct i965_cc_state cc_state; struct i965_wm_state wm_state; struct i965_wm_binding wm_binding; i965_surface_t *target; uint32_t target_offset; intel_bo_t *source; uint32_t source_offset; intel_bo_t *mask; uint32_t mask_offset; intel_bo_t *clip; uint32_t clip_offset; uint32_t draw_rectangle; uint32_t vs_offset; uint32_t border_color_offset; cairo_hash_table_t *sf_states; cairo_hash_table_t *cc_states; cairo_hash_table_t *wm_kernels; cairo_hash_table_t *wm_states; cairo_hash_table_t *wm_bindings; cairo_hash_table_t *samplers; intel_bo_t *general_state; cairo_freelist_t sf_freelist; cairo_freelist_t cc_freelist; cairo_freelist_t wm_kernel_freelist; cairo_freelist_t wm_state_freelist; cairo_freelist_t wm_binding_freelist; cairo_freelist_t sampler_freelist; uint32_t vertex_type; uint32_t vertex_size; uint32_t rectangle_size; uint32_t last_vertex_size; float *constants; /* 4 x matrix + 2 x source */ unsigned constants_size; cairo_bool_t have_urb_fences; i965_stream_t batch; uint8_t batch_base[I965_BATCH_SIZE]; struct drm_i915_gem_relocation_entry batch_relocations[2048]; i965_stream_t surface; uint8_t surface_base[I965_SURFACE_SIZE]; struct i965_pending_relocation surface_pending_relocations[1]; struct drm_i915_gem_relocation_entry surface_relocations[1024]; i965_stream_t general; uint8_t general_base[I965_GENERAL_SIZE]; struct i965_pending_relocation general_pending_relocations[1]; i965_stream_t vertex; uint8_t vertex_base[I965_VERTEX_SIZE]; struct i965_pending_relocation vertex_pending_relocations[512]; struct { size_t gtt_size; intel_bo_t *bo[1024]; int count; struct drm_i915_gem_exec_object2 exec[1024]; } exec; cairo_list_t flush; }; typedef enum { VS_NONE = 0, VS_GLYPHS, VS_SPANS, } i965_vertex_shader_t; typedef enum { FS_NONE = 0, FS_CONSTANT, FS_LINEAR, FS_RADIAL, FS_SURFACE, FS_GLYPHS, FS_SPANS, } i965_fragment_shader_t; typedef enum { PATTERN_BASE, PATTERN_SOLID, PATTERN_LINEAR, PATTERN_RADIAL, PATTERN_SURFACE, } i965_shader_channel_t; #define PATTERN_NONE (i965_shader_channel_t)-1 struct i965_shader { i965_device_t *device; i965_surface_t *target; cairo_operator_t op; cairo_bool_t committed; cairo_bool_t need_combine; float constants[4*8 + 2*8]; /* 4 x matrix + 2 x source */ unsigned constants_size; union i965_shader_channel { struct { i965_vertex_shader_t vertex; i965_fragment_shader_t fragment; i965_shader_channel_t pattern; } type; struct i965_shader_base { i965_vertex_shader_t vertex; i965_fragment_shader_t fragment; i965_shader_channel_t pattern; uint32_t mode; float constants[8]; unsigned constants_size; intel_bo_t *bo; cairo_format_t format; cairo_content_t content; int width, height, stride; int filter, extend; cairo_matrix_t matrix; cairo_bool_t has_component_alpha; } base; struct i965_shader_solid { struct i965_shader_base base; } solid; struct i965_shader_linear { struct i965_shader_base base; } linear; struct i965_shader_radial { struct i965_shader_base base; } radial; struct i965_shader_surface { struct i965_shader_base base; cairo_surface_t *surface; } surface; } source, mask, clip, dst; jmp_buf unwind; }; enum i965_shader_linear_mode { /* XXX REFLECT */ LINEAR_TEXTURE, LINEAR_NONE, LINEAR_REPEAT, LINEAR_PAD, }; enum i965_shader_radial_mode { RADIAL_ONE, RADIAL_TWO }; typedef cairo_status_t (*i965_spans_func_t) (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents); static inline i965_device_t * i965_device (i965_surface_t *surface) { return (i965_device_t *) surface->intel.drm.base.device; } cairo_private void i965_emit_relocation (i965_device_t *device, i965_stream_t *stream, intel_bo_t *target, uint32_t target_offset, uint32_t read_domains, uint32_t write_domain, uint32_t offset); static cairo_always_inline uint32_t i965_stream_emit (i965_stream_t *stream, const void *data, size_t size) { uint32_t offset; offset = stream->used; assert (offset + size <= stream->size); memcpy (stream->data + offset, data, size); stream->used += size; return offset; } static cairo_always_inline void i965_stream_align (i965_stream_t *stream, uint32_t size) { stream->used = (stream->used + size - 1) & -size; } static cairo_always_inline void * i965_stream_alloc (i965_stream_t *stream, uint32_t align, uint32_t size) { void *ptr; if (align) i965_stream_align (stream, align); assert (stream->used + size <= stream->size); ptr = stream->data + stream->used; stream->used += size; return ptr; } static cairo_always_inline uint32_t i965_stream_offsetof (i965_stream_t *stream, const void *ptr) { return (char *) ptr - (char *) stream->data; } cairo_private void i965_stream_commit (i965_device_t *device, i965_stream_t *stream); cairo_private void i965_general_state_reset (i965_device_t *device); static inline void i965_batch_emit_dword (i965_device_t *device, uint32_t dword) { *(uint32_t *) (device->batch.data + device->batch.used) = dword; device->batch.used += 4; } #define OUT_BATCH(dword) i965_batch_emit_dword(device, dword) cairo_private void i965_clipped_vertices (i965_device_t *device, struct i965_vbo *vbo, cairo_region_t *clip_region); cairo_private void i965_flush_vertices (i965_device_t *device); cairo_private void i965_finish_vertices (i965_device_t *device); static inline float * i965_add_rectangle (i965_device_t *device) { float *vertices; uint32_t size; size = device->rectangle_size; if (unlikely (device->vertex.used + size > device->vertex.size)) i965_finish_vertices (device); vertices = (float *) (device->vertex.data + device->vertex.used); device->vertex.used += size; return vertices; } static inline void i965_shader_add_rectangle (const i965_shader_t *shader, int x, int y, int w, int h) { float *v; v= i965_add_rectangle (shader->device); /* bottom-right */ *v++ = x + w; *v++ = y + h; /* bottom-left */ *v++ = x; *v++ = y + h; /* top-left */ *v++ = x; *v++ = y; } cairo_private cairo_surface_t * i965_surface_create_internal (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height, uint32_t tiling, cairo_bool_t gpu_target); cairo_private cairo_status_t i965_clip_and_composite_spans (i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, i965_spans_func_t draw_func, void *draw_closure, const cairo_composite_rectangles_t*extents, cairo_clip_t *clip); cairo_private cairo_int_status_t i965_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining); cairo_private void i965_shader_init (i965_shader_t *shader, i965_surface_t *dst, cairo_operator_t op); cairo_private cairo_status_t i965_shader_acquire_pattern (i965_shader_t *shader, union i965_shader_channel *src, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents); cairo_private void i965_shader_set_clip (i965_shader_t *shader, cairo_clip_t *clip); cairo_private cairo_status_t i965_shader_commit (i965_shader_t *shader, i965_device_t *device); cairo_private void i965_shader_fini (i965_shader_t *shader); cairo_private cairo_status_t i965_device_flush (i965_device_t *device); cairo_private cairo_status_t i965_fixup_unbounded (i965_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip); static inline int i965_filter (cairo_filter_t filter) { switch (filter) { default: case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: return BRW_MAPFILTER_NEAREST; case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: return BRW_MAPFILTER_LINEAR; } } static inline int i965_extend (cairo_extend_t extend) { switch (extend) { default: case CAIRO_EXTEND_NONE: return BRW_TEXCOORDMODE_CLAMP_BORDER; case CAIRO_EXTEND_REPEAT: return BRW_TEXCOORDMODE_WRAP; case CAIRO_EXTEND_PAD: return BRW_TEXCOORDMODE_CLAMP; case CAIRO_EXTEND_REFLECT: return BRW_TEXCOORDMODE_MIRROR; } } #endif /* CAIRO_DRM_I965_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i965-shader.c000066400000000000000000002375301271037650300270530ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Kristian Høgsberg * Copyright © 2009 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * Contributor(s): * Chris Wilson * Kristian Høgsberg */ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-drm-i965-private.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-drm-intel-brw-eu.h" /* Theory of shaders: * * 3 types of rectangular inputs: * (a) standard composite: x,y, use source, mask matrices to compute texcoords * (b) spans: x,y, alpha, use source matrix * (c) glyphs: x,y, s,t, use source matrix * * 5 types of pixel shaders: * (a) Solid colour * (b) Linear gradient (via 1D texture, with precomputed tex) * (c) Radial gradient (per-pixel s computation, 1D texture) * (d) Spans (mask only): apply opacity * (e) Texture (includes glyphs). * * Clip masks are limited to 2D textures only. */ /* XXX dual source blending for LERP + ComponentAlpha!!! */ #define BRW_GRF_BLOCKS(nreg) ((nreg + 15) / 16 - 1) #define SF_KERNEL_NUM_GRF 1 #define SF_MAX_THREADS 24 #define PS_MAX_THREADS_CTG 50 #define PS_MAX_THREADS_BRW 32 #define URB_CS_ENTRY_SIZE 3 /* We need 4 matrices + 2 sources */ #define URB_CS_ENTRIES 4 /* 4x sets of CONSTANT_BUFFER */ #define URB_VS_ENTRY_SIZE 1 #define URB_VS_ENTRIES 8 #define URB_GS_ENTRY_SIZE 0 #define URB_GS_ENTRIES 0 #define URB_CLIP_ENTRY_SIZE 0 #define URB_CLIP_ENTRIES 0 #define URB_SF_ENTRY_SIZE 1 #define URB_SF_ENTRIES (SF_MAX_THREADS + 1) static void i965_pipelined_flush (i965_device_t *device) { intel_bo_t *bo, *next; if (device->batch.used == 0) return; OUT_BATCH (BRW_PIPE_CONTROL | BRW_PIPE_CONTROL_NOWRITE | BRW_PIPE_CONTROL_WC_FLUSH | 2); OUT_BATCH(0); /* Destination address */ OUT_BATCH(0); /* Immediate data low DW */ OUT_BATCH(0); /* Immediate data high DW */ cairo_list_foreach_entry_safe (bo, next, intel_bo_t, &device->flush, link) { bo->batch_write_domain = 0; cairo_list_init (&bo->link); } cairo_list_init (&device->flush); } static cairo_status_t i965_shader_acquire_solid (i965_shader_t *shader, union i965_shader_channel *src, const cairo_solid_pattern_t *solid, const cairo_rectangle_int_t *extents) { src->type.fragment = FS_CONSTANT; src->type.vertex = VS_NONE; src->type.pattern = PATTERN_SOLID; src->base.content = _cairo_color_get_content (&solid->color); src->base.constants[0] = solid->color.red * solid->color.alpha; src->base.constants[1] = solid->color.green * solid->color.alpha; src->base.constants[2] = solid->color.blue * solid->color.alpha; src->base.constants[3] = solid->color.alpha; src->base.constants_size = 4; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_shader_acquire_linear (i965_shader_t *shader, union i965_shader_channel *src, const cairo_linear_pattern_t *linear, const cairo_rectangle_int_t *extents) { intel_buffer_t buffer; cairo_status_t status; double x0, y0, sf; double dx, dy, offset; status = intel_gradient_render (&i965_device (shader->target)->intel, &linear->base, &buffer); if (unlikely (status)) return status; src->type.vertex = VS_NONE; src->type.pattern = PATTERN_LINEAR; src->type.fragment = FS_LINEAR; src->base.bo = buffer.bo; src->base.content = CAIRO_CONTENT_COLOR_ALPHA; src->base.format = buffer.format; src->base.width = buffer.width; src->base.height = buffer.height; src->base.stride = buffer.stride; src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR); src->base.extend = i965_extend (linear->base.base.extend); dx = linear->pd2.x - linear->pd1.x; dy = linear->pd2.y - linear->pd1.y; sf = 1. / (dx * dx + dy * dy); dx *= sf; dy *= sf; x0 = linear->pd1.x; y0 = linear->pd1.y; offset = dx*x0 + dy*y0; if (_cairo_matrix_is_identity (&linear->base.base.matrix)) { src->base.matrix.xx = dx; src->base.matrix.xy = dy; src->base.matrix.x0 = -offset; } else { cairo_matrix_t m; cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0); cairo_matrix_multiply (&src->base.matrix, &linear->base.base.matrix, &m); } src->base.matrix.yx = 0.; src->base.matrix.yy = 1.; src->base.matrix.y0 = 0.; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_shader_acquire_radial (i965_shader_t *shader, union i965_shader_channel *src, const cairo_radial_pattern_t *radial, const cairo_rectangle_int_t *extents) { intel_buffer_t buffer; cairo_status_t status; double dx, dy, dr, r1; status = intel_gradient_render (&i965_device (shader->target)->intel, &radial->base, &buffer); if (unlikely (status)) return status; src->type.vertex = VS_NONE; src->type.pattern = PATTERN_RADIAL; src->type.fragment = FS_RADIAL; src->base.bo = buffer.bo; src->base.content = CAIRO_CONTENT_COLOR_ALPHA; src->base.format = buffer.format; src->base.width = buffer.width; src->base.height = buffer.height; src->base.stride = buffer.stride; src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR); src->base.extend = i965_extend (radial->base.base.extend); dx = radial->cd2.center.x - radial->cd1.center.x; dy = radial->cd2.center.y - radial->cd1.center.y; dr = radial->cd2.radius - radial->cd1.radius; r1 = radial->cd1.radius; if (FALSE && (radial->cd2.center.x == radial->cd1.center.x && radial->cd2.center.y == radial->cd1.center.y)) { /* XXX dr == 0, meaningless with anything other than PAD */ src->base.constants[0] = radial->cd1.center.x / dr; src->base.constants[1] = radial->cd1.center.y / dr; src->base.constants[2] = 1. / dr; src->base.constants[3] = -r1 / dr; src->base.constants_size = 4; src->base.mode = RADIAL_ONE; } else { src->base.constants[0] = -radial->cd1.center.x; src->base.constants[1] = -radial->cd1.center.y; src->base.constants[2] = r1; src->base.constants[3] = -4 * (dx*dx + dy*dy - dr*dr); src->base.constants[4] = -2 * dx; src->base.constants[5] = -2 * dy; src->base.constants[6] = -2 * r1 * dr; src->base.constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr)); src->base.constants_size = 8; src->base.mode = RADIAL_TWO; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_surface_clone (i965_device_t *device, cairo_image_surface_t *image, i965_surface_t **clone_out) { i965_surface_t *clone; cairo_status_t status; clone = (i965_surface_t *) i965_surface_create_internal (&device->intel.base, image->base.content, image->width, image->height, I965_TILING_DEFAULT, FALSE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; status = intel_bo_put_image (&device->intel, to_intel_bo (clone->intel.drm.bo), image, 0, 0, image->width, image->height, 0, 0); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } status = intel_snapshot_cache_insert (&device->intel, &clone->intel); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } _cairo_surface_attach_snapshot (&image->base, &clone->intel.drm.base, intel_surface_detach_snapshot); *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_surface_clone_subimage (i965_device_t *device, cairo_image_surface_t *image, const cairo_rectangle_int_t *extents, i965_surface_t **clone_out) { i965_surface_t *clone; cairo_status_t status; clone = (i965_surface_t *) i965_surface_create_internal (&device->intel.base, image->base.content, extents->width, extents->height, I965_TILING_DEFAULT, FALSE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; status = intel_bo_put_image (to_intel_device (clone->intel.drm.base.device), to_intel_bo (clone->intel.drm.bo), image, extents->x, extents->y, extents->width, extents->height, 0, 0); if (unlikely (status)) return status; *clone_out = clone; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_shader_acquire_solid_surface (i965_shader_t *shader, union i965_shader_channel *src, cairo_surface_t *surface, const cairo_rectangle_int_t *extents) { cairo_image_surface_t *image; void *image_extra; cairo_status_t status; uint32_t argb; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (unlikely (status)) return status; if (image->format != CAIRO_FORMAT_ARGB32) { cairo_surface_t *pixel; cairo_surface_pattern_t pattern; /* extract the pixel as argb32 */ pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); _cairo_pattern_init_for_surface (&pattern, &image->base); cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { _cairo_surface_release_source_image (surface, image, image_extra); cairo_surface_destroy (pixel); return status; } argb = *(uint32_t *) ((cairo_image_surface_t *) pixel)->data; cairo_surface_destroy (pixel); } else { argb = ((uint32_t *) (image->data + extents->y * image->stride))[extents->x]; } _cairo_surface_release_source_image (surface, image, image_extra); if (argb >> 24 == 0) argb = 0; src->base.constants[0] = ((argb >> 16) & 0xff) / 255.; src->base.constants[1] = ((argb >> 8) & 0xff) / 255.; src->base.constants[2] = ((argb >> 0) & 0xff) / 255.; src->base.constants[3] = ((argb >> 24) & 0xff) / 255.; src->base.constants_size = 4; src->base.content = CAIRO_CONTENT_COLOR_ALPHA; if (CAIRO_ALPHA_IS_OPAQUE(src->base.constants[3])) src->base.content &= ~CAIRO_CONTENT_ALPHA; src->type.fragment = FS_CONSTANT; src->type.vertex = VS_NONE; src->type.pattern = PATTERN_SOLID; return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_shader_acquire_surface (i965_shader_t *shader, union i965_shader_channel *src, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents) { cairo_surface_t *surface, *drm; cairo_matrix_t m; cairo_status_t status; int src_x = 0, src_y = 0; assert (src->type.fragment == FS_NONE); drm = surface = pattern->surface; if (surface->type == CAIRO_SURFACE_TYPE_DRM) { if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { drm = ((cairo_surface_subsurface_t *) surface)->target; } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { drm = ((cairo_surface_snapshot_t *) surface)->target; } } src->type.pattern = PATTERN_SURFACE; src->surface.surface = NULL; if (drm->type == CAIRO_SURFACE_TYPE_DRM) { i965_surface_t *s = (i965_surface_t *) drm; if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface; if (s != shader->target) { int x; if (s->intel.drm.fallback != NULL) { status = intel_surface_flush (s, 0); if (unlikely (status)) return status; } if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) i965_pipelined_flush (i965_device (s)); src->type.fragment = FS_SURFACE; src->base.bo = to_intel_bo (s->intel.drm.bo); src->base.format = s->intel.drm.format; src->base.content = s->intel.drm.base.content; src->base.width = sub->extents.width; src->base.height = sub->extents.height; src->base.stride = s->intel.drm.stride; x = sub->extents.x; if (s->intel.drm.format != CAIRO_FORMAT_A8) x *= 4; /* XXX tiling restrictions upon offset? */ //src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x; } else { i965_surface_t *clone; cairo_surface_pattern_t pattern; clone = (i965_surface_t *) i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, s->intel.drm.base.content, sub->extents.width, sub->extents.height, I965_TILING_DEFAULT, TRUE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base); pattern.base.filter = CAIRO_FILTER_NEAREST; cairo_matrix_init_translate (&pattern.base.matrix, sub->extents.x, sub->extents.y); status = _cairo_surface_paint (&clone->intel.drm.base, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } i965_pipelined_flush (i965_device (s)); src->type.fragment = FS_SURFACE; src->base.bo = to_intel_bo (clone->intel.drm.bo); src->base.format = clone->intel.drm.format; src->base.content = clone->intel.drm.base.content; src->base.width = clone->intel.drm.width; src->base.height = clone->intel.drm.height; src->base.stride = clone->intel.drm.stride; src->surface.surface = &clone->intel.drm.base; } src_x = sub->extents.x; src_y = sub->extents.y; } } else { if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { if (s != shader->target) { if (s->intel.drm.fallback != NULL) { status = intel_surface_flush (s, 0); if (unlikely (status)) return status; } if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) i965_pipelined_flush (i965_device (s)); src->type.fragment = FS_SURFACE; src->base.bo = to_intel_bo (s->intel.drm.bo); src->base.format = s->intel.drm.format; src->base.content = s->intel.drm.base.content; src->base.width = s->intel.drm.width; src->base.height = s->intel.drm.height; src->base.stride = s->intel.drm.stride; } else { i965_surface_t *clone; cairo_surface_pattern_t pattern; clone = (i965_surface_t *) i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, s->intel.drm.base.content, s->intel.drm.width, s->intel.drm.height, I965_TILING_DEFAULT, TRUE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (&clone->intel.drm.base, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (&clone->intel.drm.base); return status; } i965_pipelined_flush (i965_device (s)); src->type.fragment = FS_SURFACE; src->base.bo = to_intel_bo (clone->intel.drm.bo); src->base.format = clone->intel.drm.format; src->base.content = clone->intel.drm.base.content; src->base.width = clone->intel.drm.width; src->base.height = clone->intel.drm.height; src->base.stride = clone->intel.drm.stride; src->surface.surface = &clone->intel.drm.base; } } } } if (src->type.fragment == FS_NONE) { i965_surface_t *s; if (extents->width == 1 && extents->height == 1) { return i965_shader_acquire_solid_surface (shader, src, surface, extents); } s = (i965_surface_t *) _cairo_surface_has_snapshot (surface, shader->target->intel.drm.base.backend); if (s != NULL) { i965_device_t *device = i965_device (shader->target); intel_bo_t *bo = to_intel_bo (s->intel.drm.bo); if (bo->purgeable && ! intel_bo_madvise (&device->intel, bo, I915_MADV_WILLNEED)) { _cairo_surface_detach_snapshot (&s->intel.drm.base); s = NULL; } if (s != NULL) cairo_surface_reference (&s->intel.drm.base); } if (s == NULL) { cairo_image_surface_t *image; void *image_extra; cairo_status_t status; status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (unlikely (status)) return status; if (image->width < 8192 && image->height < 8192) { status = i965_surface_clone (i965_device (shader->target), image, &s); } else { status = i965_surface_clone_subimage (i965_device (shader->target), image, extents, &s); src_x = -extents->x; src_y = -extents->y; } _cairo_surface_release_source_image (surface, image, image_extra); if (unlikely (status)) return status; /* XXX? */ //intel_bo_mark_purgeable (to_intel_bo (s->intel.drm.bo), TRUE); } src->type.fragment = FS_SURFACE; src->base.bo = to_intel_bo (s->intel.drm.bo); src->base.content = s->intel.drm.base.content; src->base.format = s->intel.drm.format; src->base.width = s->intel.drm.width; src->base.height = s->intel.drm.height; src->base.stride = s->intel.drm.stride; src->surface.surface = &s->intel.drm.base; drm = &s->intel.drm.base; } /* XXX transform nx1 or 1xn surfaces to 1D? */ src->type.vertex = VS_NONE; src->base.extend = i965_extend (pattern->base.extend); if (pattern->base.extend == CAIRO_EXTEND_NONE && extents->x >= 0 && extents->y >= 0 && extents->x + extents->width <= src->base.width && extents->y + extents->height <= src->base.height) { /* Convert a wholly contained NONE to a REFLECT as the contiguous sampler * cannot not handle CLAMP_BORDER textures. */ src->base.extend = i965_extend (CAIRO_EXTEND_REFLECT); /* XXX also need to check |u,v| < 3 */ } src->base.filter = i965_filter (pattern->base.filter); if (_cairo_matrix_is_pixel_exact (&pattern->base.matrix)) src->base.filter = i965_filter (CAIRO_FILTER_NEAREST); /* tweak the src matrix to map from dst to texture coordinates */ src->base.matrix = pattern->base.matrix; if (src_x | src_y) cairo_matrix_translate (&src->base.matrix, src_x, src_x); cairo_matrix_init_scale (&m, 1. / src->base.width, 1. / src->base.height); cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m); return CAIRO_STATUS_SUCCESS; } cairo_status_t i965_shader_acquire_pattern (i965_shader_t *shader, union i965_shader_channel *src, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents) { switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return i965_shader_acquire_solid (shader, src, (cairo_solid_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_LINEAR: return i965_shader_acquire_linear (shader, src, (cairo_linear_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_RADIAL: return i965_shader_acquire_radial (shader, src, (cairo_radial_pattern_t *) pattern, extents); case CAIRO_PATTERN_TYPE_SURFACE: return i965_shader_acquire_surface (shader, src, (cairo_surface_pattern_t *) pattern, extents); default: ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS; } } static void i965_shader_channel_init (union i965_shader_channel *channel) { channel->type.vertex = VS_NONE; channel->type.fragment = FS_NONE; channel->type.pattern = PATTERN_NONE; channel->base.mode = 0; channel->base.bo = NULL; channel->base.filter = i965_extend (CAIRO_FILTER_NEAREST); channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); channel->base.has_component_alpha = 0; channel->base.constants_size = 0; } void i965_shader_init (i965_shader_t *shader, i965_surface_t *dst, cairo_operator_t op) { shader->committed = FALSE; shader->device = i965_device (dst); shader->target = dst; shader->op = op; shader->constants_size = 0; shader->need_combine = FALSE; i965_shader_channel_init (&shader->source); i965_shader_channel_init (&shader->mask); i965_shader_channel_init (&shader->clip); i965_shader_channel_init (&shader->dst); } void i965_shader_fini (i965_shader_t *shader) { if (shader->source.type.pattern == PATTERN_SURFACE) cairo_surface_destroy (shader->source.surface.surface); if (shader->mask.type.pattern == PATTERN_SURFACE) cairo_surface_destroy (shader->mask.surface.surface); if (shader->clip.type.pattern == PATTERN_SURFACE) cairo_surface_destroy (shader->clip.surface.surface); if (shader->dst.type.pattern == PATTERN_SURFACE) cairo_surface_destroy (shader->dst.surface.surface); } void i965_shader_set_clip (i965_shader_t *shader, cairo_clip_t *clip) { cairo_surface_t *clip_surface; int clip_x, clip_y; union i965_shader_channel *channel; i965_surface_t *s; clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y); assert (clip_surface->status == CAIRO_STATUS_SUCCESS); assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM); s = (i965_surface_t *) clip_surface; if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) i965_pipelined_flush (i965_device (s)); channel = &shader->clip; channel->type.pattern = PATTERN_BASE; channel->type.vertex = VS_NONE; channel->type.fragment = FS_SURFACE; channel->base.bo = to_intel_bo (s->intel.drm.bo); channel->base.content = CAIRO_CONTENT_ALPHA; channel->base.format = CAIRO_FORMAT_A8; channel->base.width = s->intel.drm.width; channel->base.height = s->intel.drm.height; channel->base.stride = s->intel.drm.stride; channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST); cairo_matrix_init_scale (&shader->clip.base.matrix, 1. / s->intel.drm.width, 1. / s->intel.drm.height); cairo_matrix_translate (&shader->clip.base.matrix, -clip_x, -clip_y); } static cairo_bool_t i965_shader_check_aperture (i965_shader_t *shader, i965_device_t *device) { uint32_t size = device->exec.gtt_size; if (shader->target != device->target) { const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); if (bo->exec == NULL) size += bo->base.size; } if (shader->source.base.bo != NULL && shader->source.base.bo != device->source) { const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); if (bo->exec == NULL) size += bo->base.size; } if (shader->mask.base.bo != NULL && shader->mask.base.bo != device->mask) { const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); if (bo->exec == NULL) size += bo->base.size; } if (shader->clip.base.bo != NULL && shader->clip.base.bo != device->clip) { const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); if (bo->exec == NULL) size += bo->base.size; } return size <= device->intel.gtt_avail_size; } static cairo_status_t i965_shader_setup_dst (i965_shader_t *shader) { union i965_shader_channel *channel; i965_surface_t *s, *clone; /* We need to manual blending if we have a clip surface and an unbounded op, * or an extended blend mode. */ if (shader->need_combine || (shader->op < CAIRO_OPERATOR_SATURATE && (shader->clip.type.fragment == FS_NONE || _cairo_operator_bounded_by_mask (shader->op)))) { return CAIRO_STATUS_SUCCESS; } shader->need_combine = TRUE; s = shader->target; /* we need to allocate a new render target and use the original as a source */ clone = (i965_surface_t *) i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, s->intel.drm.base.content, s->intel.drm.width, s->intel.drm.height, I965_TILING_DEFAULT, TRUE); if (unlikely (clone->intel.drm.base.status)) return clone->intel.drm.base.status; if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) i965_pipelined_flush (i965_device (s)); channel = &shader->dst; channel->type.vertex = VS_NONE; channel->type.fragment = FS_SURFACE; channel->type.pattern = PATTERN_SURFACE; /* swap buffer objects */ channel->base.bo = to_intel_bo (s->intel.drm.bo); s->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo; ((cairo_drm_surface_t *) clone)->bo = &channel->base.bo->base; channel->base.content = s->intel.drm.base.content; channel->base.format = s->intel.drm.format; channel->base.width = s->intel.drm.width; channel->base.height = s->intel.drm.height; channel->base.stride = s->intel.drm.stride; channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST); channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); cairo_matrix_init_scale (&channel->base.matrix, 1. / s->intel.drm.width, 1. / s->intel.drm.height); channel->surface.surface = &clone->intel.drm.base; s->intel.drm.base.content = clone->intel.drm.base.content; s->intel.drm.format = clone->intel.drm.format; assert (s->intel.drm.width == clone->intel.drm.width); assert (s->intel.drm.height == clone->intel.drm.height); s->intel.drm.stride = clone->intel.drm.stride; return CAIRO_STATUS_SUCCESS; } static inline void constant_add_float (i965_shader_t *shader, float v) { shader->constants[shader->constants_size++] = v; } static inline void i965_shader_copy_channel_constants (i965_shader_t *shader, const union i965_shader_channel *channel) { if (channel->base.constants_size) { assert (shader->constants_size + channel->base.constants_size < ARRAY_LENGTH (shader->constants)); memcpy (shader->constants + shader->constants_size, channel->base.constants, sizeof (float) * channel->base.constants_size); shader->constants_size += channel->base.constants_size; } } static void i965_shader_setup_channel_constants (i965_shader_t *shader, const union i965_shader_channel *channel) { switch (channel->type.fragment) { case FS_NONE: case FS_CONSTANT: /* no plane equations */ break; case FS_LINEAR: constant_add_float (shader, channel->base.matrix.xx); constant_add_float (shader, channel->base.matrix.xy); constant_add_float (shader, 0); constant_add_float (shader, channel->base.matrix.x0); break; case FS_RADIAL: case FS_SURFACE: constant_add_float (shader, channel->base.matrix.xx); constant_add_float (shader, channel->base.matrix.xy); constant_add_float (shader, 0); constant_add_float (shader, channel->base.matrix.x0); constant_add_float (shader, channel->base.matrix.yx); constant_add_float (shader, channel->base.matrix.yy); constant_add_float (shader, 0); constant_add_float (shader, channel->base.matrix.y0); break; case FS_SPANS: case FS_GLYPHS: /* use pue from SF */ break; } i965_shader_copy_channel_constants (shader, channel); } static void i965_shader_setup_constants (i965_shader_t *shader) { i965_shader_setup_channel_constants (shader, &shader->source); i965_shader_setup_channel_constants (shader, &shader->mask); i965_shader_setup_channel_constants (shader, &shader->clip); i965_shader_setup_channel_constants (shader, &shader->dst); assert (shader->constants_size < ARRAY_LENGTH (shader->constants)); } /* * Highest-valued BLENDFACTOR used in i965_blend_op. * * This leaves out BRW_BLENDFACTOR_INV_DST_COLOR, * BRW_BLENDFACTOR_INV_CONST_{COLOR,ALPHA}, * BRW_BLENDFACTOR_INV_SRC1_{COLOR,ALPHA} */ #define BRW_BLENDFACTOR_COUNT (BRW_BLENDFACTOR_INV_DST_ALPHA + 1) static void i965_shader_get_blend_cntl (const i965_shader_t *shader, uint32_t *sblend, uint32_t *dblend) { static const struct blendinfo { cairo_bool_t dst_alpha; cairo_bool_t src_alpha; uint32_t src_blend; uint32_t dst_blend; } i965_blend_op[] = { /* CAIRO_OPERATOR_CLEAR treat as SOURCE with transparent */ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO}, /* CAIRO_OPERATOR_SOURCE */ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO}, /* CAIRO_OPERATOR_OVER */ {0, 1, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_INV_SRC_ALPHA}, /* CAIRO_OPERATOR_IN */ {1, 0, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_ZERO}, /* CAIRO_OPERATOR_OUT */ {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ZERO}, /* CAIRO_OPERATOR_ATOP */ {1, 1, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA}, /* CAIRO_OPERATOR_DEST */ {0, 0, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_ONE}, /* CAIRO_OPERATOR_DEST_OVER */ {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ONE}, /* CAIRO_OPERATOR_DEST_IN */ {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_SRC_ALPHA}, /* CAIRO_OPERATOR_DEST_OUT */ {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_INV_SRC_ALPHA}, /* CAIRO_OPERATOR_DEST_ATOP */ {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_SRC_ALPHA}, /* CAIRO_OPERATOR_XOR */ {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA}, /* CAIRO_OPERATOR_ADD */ {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ONE}, }; const struct blendinfo *op = &i965_blend_op[shader->op]; *sblend = op->src_blend; *dblend = op->dst_blend; /* If there's no dst alpha channel, adjust the blend op so that we'll treat * it as always 1. */ if (shader->target->intel.drm.base.content == CAIRO_CONTENT_COLOR && op->dst_alpha) { if (*sblend == BRW_BLENDFACTOR_DST_ALPHA) *sblend = BRW_BLENDFACTOR_ONE; else if (*sblend == BRW_BLENDFACTOR_INV_DST_ALPHA) *sblend = BRW_BLENDFACTOR_ZERO; } } static void emit_wm_subpans_to_pixels (struct brw_compile *compile, int tmp) { /* Inputs: * R1.5 x/y of upper-left pixel of subspan 3 * R1.4 x/y of upper-left pixel of subspan 2 * R1.3 x/y of upper-left pixel of subspan 1 * R1.2 x/y of upper-left pixel of subspan 0 * * Outputs: * M1,2: u * M3,4: v * * upper left, upper right, lower left, lower right. */ /* compute pixel locations for each subspan */ brw_set_compression_control (compile, BRW_COMPRESSION_NONE); brw_ADD (compile, brw_vec8_grf (tmp), brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 4, BRW_REGISTER_TYPE_UW, BRW_VERTICAL_STRIDE_2, BRW_WIDTH_4, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_NOOP, WRITEMASK_XYZW), brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE)); brw_ADD (compile, brw_vec8_grf (tmp+1), brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 8, BRW_REGISTER_TYPE_UW, BRW_VERTICAL_STRIDE_2, BRW_WIDTH_4, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_NOOP, WRITEMASK_XYZW), brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE)); brw_ADD (compile, brw_vec8_grf (tmp+2), brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 5, BRW_REGISTER_TYPE_UW, BRW_VERTICAL_STRIDE_2, BRW_WIDTH_4, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_NOOP, WRITEMASK_XYZW), brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE)); brw_ADD (compile, brw_vec8_grf (tmp+3), brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 9, BRW_REGISTER_TYPE_UW, BRW_VERTICAL_STRIDE_2, BRW_WIDTH_4, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_NOOP, WRITEMASK_XYZW), brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE)); brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED); } static void emit_wm_affine (struct brw_compile *compile, int tmp, int reg, int msg) { emit_wm_subpans_to_pixels (compile, tmp); brw_LINE (compile, brw_null_reg (), brw_vec1_grf (reg, 0), brw_vec8_grf (tmp)); brw_MAC (compile, brw_message_reg (msg + 1), brw_vec1_grf (reg, 1), brw_vec8_grf (tmp+2)); brw_LINE (compile, brw_null_reg (), brw_vec1_grf (reg, 4), brw_vec8_grf (tmp)); brw_MAC (compile, brw_message_reg (msg + 3), brw_vec1_grf (reg, 5), brw_vec8_grf (tmp+2)); } static void emit_wm_glyph (struct brw_compile *compile, int tmp, int vue, int msg) { emit_wm_subpans_to_pixels (compile, tmp); brw_MUL (compile, brw_null_reg (), brw_vec8_grf (tmp), brw_imm_f (1./1024)); brw_ADD (compile, brw_message_reg (msg + 1), brw_acc_reg (), brw_vec1_grf (vue, 0)); brw_MUL (compile, brw_null_reg (), brw_vec8_grf (tmp + 2), brw_imm_f (1./1024)); brw_ADD (compile, brw_message_reg (msg + 3), brw_acc_reg (), brw_vec1_grf (vue, 1)); } static void emit_wm_load_constant (struct brw_compile *compile, int reg, struct brw_reg *result) { int n; for (n = 0; n < 4; n++) { result[n] = result[n+4] = brw_reg (BRW_GENERAL_REGISTER_FILE, reg, n, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_0, BRW_WIDTH_1, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_XXXX, WRITEMASK_XYZW); } } static void emit_wm_load_opacity (struct brw_compile *compile, int reg, struct brw_reg *result) { result[0] = result[1] = result[2] = result[3] = result[4] = result[5] = result[6] = result[7] = brw_reg (BRW_GENERAL_REGISTER_FILE, reg, 0, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_0, BRW_WIDTH_1, BRW_HORIZONTAL_STRIDE_1, BRW_SWIZZLE_XXXX, WRITEMASK_XYZW); } static void emit_wm_load_linear (struct brw_compile *compile, int tmp, int reg, int msg) { emit_wm_subpans_to_pixels (compile, tmp); brw_LINE (compile, brw_null_reg(), brw_vec1_grf (reg, 0), brw_vec8_grf (tmp)); brw_MAC (compile, brw_message_reg(msg + 1), brw_vec1_grf (reg, 1), brw_vec8_grf (tmp + 2)); } static void emit_wm_load_radial (struct brw_compile *compile, int reg, int msg) { struct brw_reg c1x = brw_vec1_grf (reg, 0); struct brw_reg c1y = brw_vec1_grf (reg, 1); struct brw_reg minus_r_sq = brw_vec1_grf (reg, 3); struct brw_reg cdx = brw_vec1_grf (reg, 4); struct brw_reg cdy = brw_vec1_grf (reg, 5); struct brw_reg neg_4a = brw_vec1_grf (reg + 1, 0); struct brw_reg inv_2a = brw_vec1_grf (reg + 1, 1); struct brw_reg tmp_x = brw_uw16_grf (30, 0); struct brw_reg tmp_y = brw_uw16_grf (28, 0); struct brw_reg det = brw_vec8_grf (22); struct brw_reg b = brw_vec8_grf (20); struct brw_reg c = brw_vec8_grf (18); struct brw_reg pdx = brw_vec8_grf (16); struct brw_reg pdy = brw_vec8_grf (14); struct brw_reg t = brw_message_reg (msg + 1); /* cdx = (câ‚‚x - câ‚x) * cdy = (câ‚‚y - câ‚y) * dr = râ‚‚-râ‚ * pdx = px - câ‚x * pdy = py - câ‚y * * A = cdx² + cdy² - dr² * B = -2·(pdx·cdx + pdy·cdy + râ‚·dr) * C = pdx² + pdy² - r₲ * * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A */ brw_ADD (compile, pdx, vec8 (tmp_x), negate (c1x)); brw_ADD (compile, pdy, vec8 (tmp_y), negate (c1y)); brw_LINE (compile, brw_null_reg (), cdx, pdx); brw_MAC (compile, b, cdy, pdy); brw_MUL (compile, brw_null_reg (), pdx, pdx); brw_MAC (compile, c, pdy, pdy); brw_ADD (compile, c, c, minus_r_sq); brw_MUL (compile, brw_null_reg (), b, b); brw_MAC (compile, det, neg_4a, c); /* XXX use rsqrt like i915?, it's faster and we need to mac anyway */ brw_math (compile, det, BRW_MATH_FUNCTION_SQRT, BRW_MATH_SATURATE_NONE, 2, det, BRW_MATH_DATA_VECTOR, BRW_MATH_PRECISION_FULL); /* XXX cmp, +- */ brw_ADD (compile, det, negate (det), negate (b)); brw_ADD (compile, det, det, negate (b)); brw_MUL (compile, t, det, inv_2a); } static int emit_wm_sample (struct brw_compile *compile, union i965_shader_channel *channel, int sampler, int msg_base, int msg_len, int dst, struct brw_reg *result) { int response_len, mask; if (channel->base.content == CAIRO_CONTENT_ALPHA) { mask = 0x7000; response_len = 2; result[0] = result[1] = result[2] = result[3] = brw_vec8_grf (dst); result[4] = result[5] = result[6] = result[7] = brw_vec8_grf (dst + 1); } else { mask = 0; response_len = 8; result[0] = brw_vec8_grf (dst + 0); result[1] = brw_vec8_grf (dst + 2); result[2] = brw_vec8_grf (dst + 4); result[3] = brw_vec8_grf (dst + 6); result[4] = brw_vec8_grf (dst + 1); result[5] = brw_vec8_grf (dst + 3); result[6] = brw_vec8_grf (dst + 5); result[7] = brw_vec8_grf (dst + 7); } brw_set_compression_control (compile, BRW_COMPRESSION_NONE); brw_set_mask_control (compile, BRW_MASK_DISABLE); brw_MOV (compile, get_element_ud (brw_vec8_grf (0), 2), brw_imm_ud (mask)); brw_set_mask_control (compile, BRW_MASK_ENABLE); brw_SAMPLE (compile, brw_uw16_grf (dst, 0), msg_base, brw_uw8_grf (0, 0), sampler + 1, /* binding table */ sampler, WRITEMASK_XYZW, BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE, response_len, msg_len, 0 /* eot */); brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED); return response_len; } #define MAX_MSG_REGISTER 16 static void emit_wm_load_channel (struct brw_compile *compile, union i965_shader_channel *channel, int *vue, int *cue, int *msg, int *sampler, int *grf, struct brw_reg *result) { switch (channel->type.fragment) { case FS_NONE: break; case FS_CONSTANT: emit_wm_load_constant (compile, *cue, result); *cue += 1; break; case FS_RADIAL: emit_wm_load_radial (compile, *cue, *msg); *cue += 2; if (*msg + 3 > MAX_MSG_REGISTER) *msg = 1; *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result); *sampler += 1; *msg += 3; break; case FS_LINEAR: emit_wm_load_linear (compile, *grf, *cue, *msg); *cue += 1; if (*msg + 3 > MAX_MSG_REGISTER) *msg = 1; *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result); *sampler += 1; *msg += 3; break; case FS_SURFACE: emit_wm_affine (compile, *grf, *cue, *msg); *cue += 2; if (*msg + 5 > MAX_MSG_REGISTER) *msg = 1; *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result); *sampler += 1; *msg += 5; break; case FS_SPANS: emit_wm_load_opacity (compile, *vue, result); *vue += 1; break; case FS_GLYPHS: emit_wm_glyph (compile, *grf, *vue, *msg); *vue += 1; if (*msg + 5 > MAX_MSG_REGISTER) *msg = 1; *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result); *sampler += 1; *msg += 5; break; } } static unsigned long i965_wm_kernel_hash (const i965_shader_t *shader) { unsigned long hash; hash = (shader->source.type.fragment & 0xff) | (shader->mask.type.fragment & 0xff) << 8 | (shader->clip.type.fragment & 0xff) << 16; if (shader->need_combine) hash |= (1 + shader->op) << 24; return hash; } static void i965_wm_kernel_init (struct i965_wm_kernel *key, const i965_shader_t *shader) { key->entry.hash = i965_wm_kernel_hash (shader); } static uint32_t i965_shader_const_urb_length (i965_shader_t *shader) { const int lengths[] = { 0, 1, 1, 4, 2, 0, 0 }; int count = 0; /* 128-bit/16-byte increments */ count += lengths[shader->source.type.fragment]; count += lengths[shader->mask.type.fragment]; count += lengths[shader->clip.type.fragment]; count += lengths[shader->dst.type.fragment]; return (count + 1) / 2; /* 256-bit/32-byte increments */ } static uint32_t i965_shader_pue_length (i965_shader_t *shader) { return 1 + (shader->mask.type.vertex != VS_NONE); } static uint32_t create_wm_kernel (i965_device_t *device, i965_shader_t *shader, int *num_reg) { struct brw_compile compile; struct brw_reg source[8], mask[8], clip[8], dst[8]; const uint32_t *program; uint32_t size; int msg, cue, vue, grf, sampler; int i; struct i965_wm_kernel key, *cache; cairo_status_t status; uint32_t offset; i965_wm_kernel_init (&key, shader); cache = _cairo_hash_table_lookup (device->wm_kernels, &key.entry); if (cache != NULL) return cache->offset; brw_compile_init (&compile, device->is_g4x); if (key.entry.hash == FS_CONSTANT && to_intel_bo (shader->target->intel.drm.bo)->tiling) { struct brw_instruction *insn; assert (i965_shader_const_urb_length (shader) == 1); brw_MOV (&compile, brw_message4_reg (2), brw_vec4_grf (2, 0)); grf = 3; brw_push_insn_state (&compile); brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */ brw_MOV (&compile, retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD), retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD)); brw_pop_insn_state (&compile); insn = brw_next_instruction (&compile, BRW_OPCODE_SEND); insn->header.predicate_control = 0; insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.destreg__conditonalmod = 0; brw_instruction_set_destination (insn, retype (vec16 (brw_acc_reg ()), BRW_REGISTER_TYPE_UW)); brw_instruction_set_source0 (insn, retype (brw_vec8_grf (0), BRW_REGISTER_TYPE_UW)); brw_instruction_set_dp_write_message (insn, 0, BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED, /* msg_control */ BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */ 3, 1, /* pixel scoreboard */ 0, TRUE); } else { msg = 1; cue = 2; vue = cue + i965_shader_const_urb_length (shader); grf = vue + i965_shader_pue_length (shader); sampler = 0; brw_set_compression_control (&compile, BRW_COMPRESSION_COMPRESSED); emit_wm_load_channel (&compile, &shader->source, &vue, &cue, &msg, &sampler, &grf, source); emit_wm_load_channel (&compile, &shader->mask, &vue, &cue, &msg, &sampler, &grf, mask); emit_wm_load_channel (&compile, &shader->clip, &vue, &cue, &msg, &sampler, &grf, clip); emit_wm_load_channel (&compile, &shader->dst, &vue, &cue, &msg, &sampler, &grf, dst); brw_set_compression_control (&compile, BRW_COMPRESSION_NONE); if (shader->need_combine) { if (shader->mask.type.fragment != FS_NONE && shader->clip.type.fragment != FS_NONE) { for (i = 0; i < 8; i++) brw_MUL (&compile, mask[i], mask[i], clip[i]); } /* XXX LERP ! */ for (i = 0; i < 8; i++) brw_MOV (&compile, brw_message_reg (2 + i), source[i]); } else { if (shader->mask.type.fragment != FS_NONE) { if (shader->clip.type.fragment != FS_NONE) { for (i = 0; i < 8; i++) brw_MUL (&compile, mask[i], mask[i], clip[i]); } for (i = 0; i < 8; i++) brw_MUL (&compile, brw_message_reg (2 + i), source[i], mask[i]); } else { if (shader->clip.type.fragment != FS_NONE) { for (i = 0; i < 8; i++) brw_MUL (&compile, brw_message_reg (2 + i), source[i], clip[i]); } else { for (i = 0; i < 8; i++) brw_MOV (&compile, brw_message_reg (2 + i), source[i]); } } } brw_push_insn_state (&compile); brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */ brw_MOV (&compile, retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD), retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD)); brw_pop_insn_state (&compile); brw_fb_WRITE (&compile, retype (vec16 (brw_acc_reg ()), BRW_REGISTER_TYPE_UW), 0, /* base reg */ retype (brw_vec8_grf (0), BRW_REGISTER_TYPE_UW), 0, /* binding table index */ 2 + 8, /* msg length */ 0, /* response length */ TRUE); /* EOT */ } program = brw_get_program (&compile, &size); *num_reg = grf; i965_stream_align (&device->general, 64); offset = i965_stream_emit (&device->general, program, size); cache = _cairo_freelist_alloc (&device->wm_kernel_freelist); if (likely (cache != NULL)) { i965_wm_kernel_init (cache, shader); cache->offset = offset; status = _cairo_hash_table_insert (device->wm_kernels, &cache->entry); if (unlikely (status)) _cairo_freelist_free (&device->wm_kernel_freelist, cache); } return offset; } static uint32_t create_sf_kernel (i965_device_t *device, i965_shader_t *shader) { struct brw_compile compile; const uint32_t *program; uint32_t size; int msg_len; brw_compile_init (&compile, device->is_g4x); switch (shader->mask.type.vertex) { default: case VS_NONE: /* use curb plane eq in WM */ msg_len = 1; break; case VS_SPANS: /* just a constant opacity */ brw_MOV (&compile, brw_message4_reg (1), brw_vec4_grf (3, 0)); msg_len = 2; break; case VS_GLYPHS: /* an offset+sf into the glyph cache */ brw_MOV (&compile, brw_acc_reg (), brw_vec2_grf (3, 0)); brw_MAC (&compile, brw_message4_reg (1), negate (brw_vec2_grf (1, 4)), brw_imm_f (1./1024)); msg_len = 2; break; } brw_urb_WRITE (&compile, brw_null_reg (), 0, brw_vec8_grf (0), /* r0, will be copied to m0 */ 0, /* allocate */ 1, /* used */ msg_len, 0, /* response len */ 1, /* eot */ 1, /* writes complete */ 0, /* offset */ BRW_URB_SWIZZLE_NONE); program = brw_get_program (&compile, &size); i965_stream_align (&device->general, 64); return i965_stream_emit (&device->general, program, size); } static uint32_t i965_sf_kernel (const i965_shader_t *shader) { return shader->mask.type.vertex; } static void i965_sf_state_init (struct i965_sf_state *key, const i965_shader_t *shader) { key->entry.hash = i965_sf_kernel (shader); } cairo_bool_t i965_sf_state_equal (const void *A, const void *B) { const cairo_hash_entry_t *a = A, *b = B; return a->hash == b->hash; } /* * Sets up the SF state pointing at an SF kernel. * * The SF kernel does coord interp: for each attribute, * calculate dA/dx and dA/dy. Hand these interpolation coefficients * back to SF which then hands pixels off to WM. */ static uint32_t gen4_create_sf_state (i965_device_t *device, i965_shader_t *shader) { struct brw_sf_unit_state *state; struct i965_sf_state key, *cache; cairo_status_t status; uint32_t offset; i965_sf_state_init (&key, shader); if (i965_sf_state_equal (&key, &device->sf_state)) return device->sf_state.offset; cache = _cairo_hash_table_lookup (device->sf_states, &key.entry); if (cache != NULL) { offset = cache->offset; goto DONE; } offset = create_sf_kernel (device, shader); state = i965_stream_alloc (&device->general, 32, sizeof (*state)); memset (state, 0, sizeof (*state)); state->thread0.grf_reg_count = BRW_GRF_BLOCKS (3); assert ((offset & 63) == 0); state->thread0.kernel_start_pointer = offset >> 6; state->sf1.single_program_flow = 1; state->thread3.urb_entry_read_length = 1; /* 1 URB per vertex */ state->thread3.urb_entry_read_offset = 1; state->thread3.dispatch_grf_start_reg = 3; state->thread4.max_threads = SF_MAX_THREADS - 1; state->thread4.urb_entry_allocation_size = URB_SF_ENTRY_SIZE - 1; state->thread4.nr_urb_entries = URB_SF_ENTRIES; state->sf6.dest_org_vbias = 0x8; state->sf6.dest_org_hbias = 0x8; offset = i965_stream_offsetof (&device->general, state); cache = _cairo_freelist_alloc (&device->sf_freelist); if (likely (cache != NULL)) { i965_sf_state_init (cache, shader); cache->offset = offset; status = _cairo_hash_table_insert (device->sf_states, &cache->entry); if (unlikely (status)) _cairo_freelist_free (&device->sf_freelist, cache); } DONE: i965_sf_state_init (&device->sf_state, shader); device->sf_state.offset = offset; return offset; } static unsigned long i965_shader_sampler_hash (const i965_shader_t *shader) { unsigned long hash = 0; unsigned int offset = 0; if (shader->source.base.bo != NULL) { hash |= (shader->source.base.filter << offset) | (shader->source.base.extend << (offset + 4)); offset += 8; } if (shader->mask.base.bo != NULL) { hash |= (shader->mask.base.filter << offset) | (shader->mask.base.extend << (offset + 4)); offset += 8; } if (shader->clip.base.bo != NULL) { hash |= (shader->clip.base.filter << offset) | (shader->clip.base.extend << (offset + 4)); offset += 8; } if (shader->dst.base.bo != NULL) { hash |= (shader->dst.base.filter << offset) | (shader->dst.base.extend << (offset + 4)); offset += 8; } return hash; } static void i965_sampler_init (struct i965_sampler *key, const i965_shader_t *shader) { key->entry.hash = i965_shader_sampler_hash (shader); } static void emit_sampler_channel (i965_device_t *device, const union i965_shader_channel *channel, uint32_t border_color) { struct brw_sampler_state *state; state = i965_stream_alloc (&device->general, 0, sizeof (*state)); memset (state, 0, sizeof (*state)); state->ss0.lod_preclamp = 1; /* GL mode */ state->ss0.border_color_mode = BRW_BORDER_COLOR_MODE_LEGACY; state->ss0.min_filter = channel->base.filter; state->ss0.mag_filter = channel->base.filter; state->ss1.r_wrap_mode = channel->base.extend; state->ss1.s_wrap_mode = channel->base.extend; state->ss1.t_wrap_mode = channel->base.extend; assert ((border_color & 31) == 0); state->ss2.border_color_pointer = border_color >> 5; } static uint32_t emit_sampler_state_table (i965_device_t *device, i965_shader_t *shader) { struct i965_sampler key, *cache; cairo_status_t status; uint32_t offset; if (device->border_color_offset == (uint32_t) -1) { struct brw_sampler_legacy_border_color *border_color; border_color = i965_stream_alloc (&device->general, 32, sizeof (*border_color)); border_color->color[0] = 0; /* R */ border_color->color[1] = 0; /* G */ border_color->color[2] = 0; /* B */ border_color->color[3] = 0; /* A */ device->border_color_offset = i965_stream_offsetof (&device->general, border_color); } else { i965_sampler_init (&key, shader); cache = _cairo_hash_table_lookup (device->samplers, &key.entry); if (cache != NULL) return cache->offset; } i965_stream_align (&device->general, 32); offset = device->general.used; if (shader->source.base.bo != NULL) { emit_sampler_channel (device, &shader->source, device->border_color_offset); } if (shader->mask.base.bo != NULL) { emit_sampler_channel (device, &shader->mask, device->border_color_offset); } if (shader->clip.base.bo != NULL) { emit_sampler_channel (device, &shader->clip, device->border_color_offset); } if (shader->dst.base.bo != NULL) { emit_sampler_channel (device, &shader->dst, device->border_color_offset); } cache = _cairo_freelist_alloc (&device->sampler_freelist); if (likely (cache != NULL)) { i965_sampler_init (cache, shader); cache->offset = offset; status = _cairo_hash_table_insert (device->samplers, &cache->entry); if (unlikely (status)) _cairo_freelist_free (&device->sampler_freelist, cache); } return offset; } static void i965_cc_state_init (struct i965_cc_state *key, const i965_shader_t *shader) { uint32_t src_blend, dst_blend; if (shader->need_combine) src_blend = dst_blend = 0; else i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend); key->entry.hash = src_blend | ((dst_blend & 0xffff) << 16); } cairo_bool_t i965_cc_state_equal (const void *A, const void *B) { const cairo_hash_entry_t *a = A, *b = B; return a->hash == b->hash; } static uint32_t cc_state_emit (i965_device_t *device, i965_shader_t *shader) { struct brw_cc_unit_state *state; struct i965_cc_state key, *cache; cairo_status_t status; uint32_t src_blend, dst_blend; uint32_t offset; i965_cc_state_init (&key, shader); if (i965_cc_state_equal (&key, &device->cc_state)) return device->cc_state.offset; cache = _cairo_hash_table_lookup (device->cc_states, &key.entry); if (cache != NULL) { offset = cache->offset; goto DONE; } if (shader->need_combine) src_blend = dst_blend = 0; else i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend); state = i965_stream_alloc (&device->general, 64, sizeof (*state)); memset (state, 0, sizeof (*state)); /* XXX Note errata, need to flush render cache when blend_enable 0 -> 1 */ /* XXX 2 source blend */ state->cc3.blend_enable = ! shader->need_combine; state->cc5.ia_blend_function = BRW_BLENDFUNCTION_ADD; state->cc5.ia_src_blend_factor = src_blend; state->cc5.ia_dest_blend_factor = dst_blend; state->cc6.blend_function = BRW_BLENDFUNCTION_ADD; state->cc6.clamp_post_alpha_blend = 1; state->cc6.clamp_pre_alpha_blend = 1; state->cc6.src_blend_factor = src_blend; state->cc6.dest_blend_factor = dst_blend; offset = i965_stream_offsetof (&device->general, state); cache = _cairo_freelist_alloc (&device->cc_freelist); if (likely (cache != NULL)) { i965_cc_state_init (cache, shader); cache->offset = offset; status = _cairo_hash_table_insert (device->cc_states, &cache->entry); if (unlikely (status)) _cairo_freelist_free (&device->cc_freelist, cache); } DONE: i965_cc_state_init (&device->cc_state, shader); device->cc_state.offset = offset; return offset; } static void i965_wm_state_init (struct i965_wm_state *key, const i965_shader_t *shader) { key->kernel = i965_wm_kernel_hash (shader); key->sampler = i965_shader_sampler_hash (shader); key->entry.hash = key->kernel ^ ((key->sampler) << 16 | (key->sampler >> 16)); } cairo_bool_t i965_wm_state_equal (const void *A, const void *B) { const struct i965_wm_state *a = A, *b = B; if (a->entry.hash != b->entry.hash) return FALSE; return a->kernel == b->kernel && a->sampler == b->sampler; } static int i965_shader_binding_table_count (i965_shader_t *shader) { int count; count = 1; if (shader->source.type.fragment != FS_CONSTANT) count++; switch (shader->mask.type.fragment) { case FS_NONE: case FS_CONSTANT: case FS_SPANS: break; case FS_LINEAR: case FS_RADIAL: case FS_SURFACE: case FS_GLYPHS: count++; } if (shader->clip.type.fragment == FS_SURFACE) count++; if (shader->dst.type.fragment == FS_SURFACE) count++; return count; } static uint32_t gen4_create_wm_state (i965_device_t *device, i965_shader_t *shader) { struct brw_wm_unit_state *state; uint32_t sampler; uint32_t kernel; struct i965_wm_state key, *cache; cairo_status_t status; int num_reg; i965_wm_state_init (&key, shader); if (i965_wm_state_equal (&key, &device->wm_state)) return device->wm_state.offset; cache = _cairo_hash_table_lookup (device->wm_states, &key.entry); if (cache != NULL) { device->wm_state = *cache; return cache->offset; } kernel = create_wm_kernel (device, shader, &num_reg); sampler = emit_sampler_state_table (device, shader); state = i965_stream_alloc (&device->general, 32, sizeof (*state)); memset (state, 0, sizeof (*state)); state->thread0.grf_reg_count = BRW_GRF_BLOCKS (num_reg); assert ((kernel & 63) == 0); state->thread0.kernel_start_pointer = kernel >> 6; state->thread3.dispatch_grf_start_reg = 2; state->wm4.sampler_count = 1; /* 1-4 samplers used */ assert ((sampler & 31) == 0); state->wm4.sampler_state_pointer = sampler >> 5; if (device->is_g4x) state->wm5.max_threads = PS_MAX_THREADS_CTG - 1; else state->wm5.max_threads = PS_MAX_THREADS_BRW - 1; state->wm5.thread_dispatch_enable = 1; if (device->is_g4x) { /* XXX contiguous 32 pixel dispatch */ } state->wm5.enable_16_pix = 1; /* 8 pixel dispatch and friends */ //state->wm5.early_depth_test = 1; state->thread1.binding_table_entry_count = i965_shader_binding_table_count(shader); state->thread3.urb_entry_read_length = i965_shader_pue_length (shader); state->thread3.const_urb_entry_read_length = i965_shader_const_urb_length (shader); key.offset = i965_stream_offsetof (&device->general, state); cache = _cairo_freelist_alloc (&device->wm_state_freelist); if (likely (cache != NULL)) { *cache = key; status = _cairo_hash_table_insert (device->wm_states, &cache->entry); if (unlikely (status)) _cairo_freelist_free (&device->wm_state_freelist, cache); } device->wm_state = key; return key.offset; } static uint32_t vs_unit_state_emit (i965_device_t *device) { if (device->vs_offset == (uint32_t) -1) { struct brw_vs_unit_state *state; /* Set up the vertex shader to be disabled (passthrough) */ state = i965_stream_alloc (&device->general, 32, sizeof (*state)); memset (state, 0, sizeof (*state)); state->thread4.nr_urb_entries = URB_VS_ENTRIES; state->thread4.urb_entry_allocation_size = URB_VS_ENTRY_SIZE - 1; state->vs6.vert_cache_disable = 1; device->vs_offset = i965_stream_offsetof (&device->general, state); } return device->vs_offset; } static uint32_t i965_get_card_format (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: return BRW_SURFACEFORMAT_B8G8R8A8_UNORM; case CAIRO_FORMAT_RGB24: return BRW_SURFACEFORMAT_B8G8R8X8_UNORM; case CAIRO_FORMAT_RGB16_565: return BRW_SURFACEFORMAT_B5G6R5_UNORM; case CAIRO_FORMAT_A8: return BRW_SURFACEFORMAT_A8_UNORM; case CAIRO_FORMAT_A1: case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; return 0; } } static uint32_t i965_get_dest_format (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: return BRW_SURFACEFORMAT_B8G8R8A8_UNORM; case CAIRO_FORMAT_RGB16_565: return BRW_SURFACEFORMAT_B5G6R5_UNORM; case CAIRO_FORMAT_A8: return BRW_SURFACEFORMAT_A8_UNORM; case CAIRO_FORMAT_A1: case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; return 0; } } /* XXX silly inline due to compiler bug... */ static inline void i965_stream_add_pending_relocation (i965_stream_t *stream, uint32_t target_offset, uint32_t read_domains, uint32_t write_domain, uint32_t delta) { int n; n = stream->num_pending_relocations++; assert (n < stream->max_pending_relocations); stream->pending_relocations[n].offset = target_offset; stream->pending_relocations[n].read_domains = read_domains; stream->pending_relocations[n].write_domain = write_domain; stream->pending_relocations[n].delta = delta; } static uint32_t emit_surface_state (i965_device_t *device, cairo_bool_t is_target, intel_bo_t *bo, cairo_format_t format, int width, int height, int stride, int type) { struct brw_surface_state *state; uint32_t write_domain, read_domains; uint32_t offset; state = i965_stream_alloc (&device->surface, 32, sizeof (*state)); memset (state, 0, sizeof (*state)); state->ss0.surface_type = type; if (is_target) state->ss0.surface_format = i965_get_dest_format (format); else state->ss0.surface_format = i965_get_card_format (format); state->ss0.data_return_format = BRW_SURFACERETURNFORMAT_FLOAT32; state->ss0.color_blend = 1; if (is_target && device->is_g4x) state->ss0.render_cache_read_mode = 1; state->ss1.base_addr = bo->offset; state->ss2.height = height - 1; state->ss2.width = width - 1; state->ss3.pitch = stride - 1; state->ss3.tile_walk = bo->tiling == I915_TILING_Y; state->ss3.tiled_surface = bo->tiling != I915_TILING_NONE; if (is_target) { read_domains = I915_GEM_DOMAIN_RENDER; write_domain = I915_GEM_DOMAIN_RENDER; } else { read_domains = I915_GEM_DOMAIN_SAMPLER; write_domain = 0; } offset = i965_stream_offsetof (&device->surface, state); i965_emit_relocation (device, &device->surface, bo, 0, read_domains, write_domain, offset + offsetof (struct brw_surface_state, ss1.base_addr)); return offset; } static uint32_t emit_surface_state_for_shader (i965_device_t *device, const union i965_shader_channel *channel) { int type = BRW_SURFACE_2D; assert (channel->type.fragment != FS_NONE); assert (channel->type.fragment != FS_CONSTANT); if (channel->type.fragment != FS_SURFACE) type = BRW_SURFACE_1D; return emit_surface_state (device, FALSE, channel->base.bo, channel->base.format, channel->base.width, channel->base.height, channel->base.stride, type); } cairo_bool_t i965_wm_binding_equal (const void *A, const void *B) { const struct i965_wm_binding *a = A, *b = B; if (a->entry.hash != b->entry.hash) return FALSE; if (a->size != b->size) return FALSE; return memcmp (a->table, b->table, sizeof (uint32_t) * a->size) == 0; } static void i965_wm_binding_init (struct i965_wm_binding *state, const uint32_t *table, int size) { int n; state->entry.hash = size; state->size = size; for (n = 0; n < size; n++) { state->table[n] = table[n]; state->entry.hash ^= (table[n] << (8 * n)) | (table[n] >> (32 - (8*n))); } } static uint32_t emit_binding_table (i965_device_t *device, i965_shader_t *shader) { intel_bo_t *bo; struct i965_wm_binding key, *cache; uint32_t *table; int n = 0; table = i965_stream_alloc (&device->surface, 32, 5 * sizeof (uint32_t)); if (shader->target->stream != device->surface.serial) { shader->target->stream = device->surface.serial; shader->target->offset = emit_surface_state (device, TRUE, to_intel_bo (shader->target->intel.drm.bo), shader->target->intel.drm.format, shader->target->intel.drm.width, shader->target->intel.drm.height, shader->target->intel.drm.stride, BRW_SURFACE_2D); } table[n++] = shader->target->offset; bo = shader->source.base.bo; if (bo != NULL) { if (bo->opaque0 != device->surface.serial) { bo->opaque0 = device->surface.serial; bo->opaque1 = emit_surface_state_for_shader (device, &shader->source); } table[n++] = bo->opaque1; } bo = shader->mask.base.bo; if (bo != NULL) { if (bo->opaque0 != device->surface.serial) { bo->opaque0 = device->surface.serial; bo->opaque1 = emit_surface_state_for_shader (device, &shader->mask); } table[n++] = bo->opaque1; } bo = shader->clip.base.bo; if (bo != NULL) { if (bo->opaque0 != device->surface.serial) { bo->opaque0 = device->surface.serial; bo->opaque1 = emit_surface_state_for_shader (device, &shader->clip); } table[n++] = bo->opaque1; } bo = shader->dst.base.bo; if (bo != NULL) { if (bo->opaque0 != device->surface.serial) { bo->opaque0 = device->surface.serial; bo->opaque1 = emit_surface_state_for_shader (device, &shader->dst); } table[n++] = bo->opaque1; } i965_wm_binding_init (&key, table, n); key.offset = i965_stream_offsetof (&device->surface, table); if (i965_wm_binding_equal (&key, &device->wm_binding)) { device->surface.used = key.offset; return device->wm_binding.offset; } cache = _cairo_hash_table_lookup (device->wm_bindings, &key.entry); if (cache != NULL) { device->surface.used = key.offset; key.offset = cache->offset; } device->wm_binding = key; return key.offset; } static void i965_emit_invariants (i965_device_t *device) { OUT_BATCH (BRW_CS_URB_STATE | 0); OUT_BATCH (((URB_CS_ENTRY_SIZE-1) << 4) | (URB_CS_ENTRIES << 0)); } static void i965_emit_urb_fences (i965_device_t *device) { int urb_vs_start, urb_vs_size; int urb_gs_start, urb_gs_size; int urb_clip_start, urb_clip_size; int urb_sf_start, urb_sf_size; int urb_cs_start, urb_cs_size; if (device->have_urb_fences) return; /* URB fence */ urb_vs_start = 0; urb_vs_size = URB_VS_ENTRIES * URB_VS_ENTRY_SIZE; urb_gs_start = urb_vs_start + urb_vs_size; urb_gs_size = URB_GS_ENTRIES * URB_GS_ENTRY_SIZE; urb_clip_start = urb_gs_start + urb_gs_size; urb_clip_size = URB_CLIP_ENTRIES * URB_CLIP_ENTRY_SIZE; urb_sf_start = urb_clip_start + urb_clip_size; urb_sf_size = URB_SF_ENTRIES * URB_SF_ENTRY_SIZE; urb_cs_start = urb_sf_start + urb_sf_size; urb_cs_size = URB_CS_ENTRIES * URB_CS_ENTRY_SIZE; /* erratum: URB_FENCE must not cross a 64-byte cache-line */ while ((device->batch.used & 63) > 64-12) OUT_BATCH (MI_NOOP); OUT_BATCH (BRW_URB_FENCE | UF0_CS_REALLOC | UF0_SF_REALLOC | UF0_CLIP_REALLOC | UF0_GS_REALLOC | UF0_VS_REALLOC | 1); OUT_BATCH (((urb_clip_start + urb_clip_size) << UF1_CLIP_FENCE_SHIFT) | ((urb_gs_start + urb_gs_size) << UF1_GS_FENCE_SHIFT) | ((urb_vs_start + urb_vs_size) << UF1_VS_FENCE_SHIFT)); OUT_BATCH (((urb_cs_start + urb_cs_size) << UF2_CS_FENCE_SHIFT) | ((urb_sf_start + urb_sf_size) << UF2_SF_FENCE_SHIFT)); device->have_urb_fences = TRUE; device->constants_size = 0; } static void i965_emit_base (i965_device_t *device) { OUT_BATCH (BRW_STATE_BASE_ADDRESS | 4); if (likely (device->general.num_pending_relocations == 0)) { i965_stream_add_pending_relocation (&device->general, device->batch.used, I915_GEM_DOMAIN_INSTRUCTION, 0, BASE_ADDRESS_MODIFY); } OUT_BATCH (0); /* pending relocation */ if (likely (device->surface.num_pending_relocations == 0)) { i965_stream_add_pending_relocation (&device->surface, device->batch.used, I915_GEM_DOMAIN_INSTRUCTION, 0, BASE_ADDRESS_MODIFY); } OUT_BATCH (0); /* pending relocation */ OUT_BATCH (0 | BASE_ADDRESS_MODIFY); /* general state max addr, disabled */ OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY); /* media object state max addr, disabled */ OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY); } static void i965_emit_vertex_element (i965_device_t *device, i965_shader_t *shader) { uint32_t offset; uint32_t type; int nelem; type = 0; nelem = 1; if (shader->mask.type.vertex == VS_SPANS || shader->mask.type.vertex == VS_GLYPHS) { type = shader->mask.type.vertex; nelem++; } if (type == device->vertex_type) return; device->vertex_type = type; offset = 0; OUT_BATCH (BRW_3DSTATE_VERTEX_ELEMENTS | ((2 * nelem) - 1)); OUT_BATCH ((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID | (BRW_SURFACEFORMAT_R32G32_FLOAT << VE0_FORMAT_SHIFT) | (offset << VE0_OFFSET_SHIFT)); OUT_BATCH ((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) | (BRW_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT) | (BRW_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT) | (4 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); offset += 8; assert (shader->source.type.vertex == VS_NONE); switch (shader->mask.type.vertex) { default: case VS_NONE: break; case VS_SPANS: OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID | (BRW_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT) | (offset << VE0_OFFSET_SHIFT)); OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_1_SHIFT) | (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) | (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) | (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); offset += 4; break; case VS_GLYPHS: OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | VE0_VALID | (BRW_SURFACEFORMAT_R16G16_FLOAT << VE0_FORMAT_SHIFT) | (offset << VE0_OFFSET_SHIFT)); OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) | (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) | (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) | (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); offset += 4; break; } assert (shader->clip.type.vertex == VS_NONE); assert (shader->dst.type.vertex == VS_NONE); device->vertex_size = offset; i965_stream_align (&device->vertex, device->vertex_size); device->vertex.committed = device->vertex.used; device->rectangle_size = 3 * offset; } static cairo_bool_t i965_shader_needs_surface_update (const i965_shader_t *shader, const i965_device_t *device) { return device->target != shader->target || shader->target->stream == 0 || (shader->source.base.bo != NULL && device->source != shader->source.base.bo) || (shader->mask.base.bo != NULL && device->mask != shader->mask.base.bo) || (shader->clip.base.bo != NULL && device->clip != shader->clip.base.bo); } static cairo_bool_t i965_shader_needs_constants_update (const i965_shader_t *shader, const i965_device_t *device) { if (shader->constants_size == 0) return FALSE; if (device->constants_size != shader->constants_size) return TRUE; return memcmp (device->constants, shader->constants, sizeof (float) * shader->constants_size); } static cairo_bool_t i965_shader_needs_state_update (const i965_shader_t *shader, const i965_device_t *device) { union { struct i965_sf_state sf; struct i965_wm_state wm; struct i965_cc_state cc; } state; i965_sf_state_init (&state.sf, shader); if (! i965_sf_state_equal (&state.sf, &device->sf_state)) return TRUE; i965_wm_state_init (&state.wm, shader); if (! i965_wm_state_equal (&state.wm, &device->wm_state)) return TRUE; i965_cc_state_init (&state.cc, shader); if (! i965_cc_state_equal (&state.cc, &device->cc_state)) return TRUE; return FALSE; } static void i965_emit_composite (i965_device_t *device, i965_shader_t *shader) { uint32_t draw_rectangle; if (i965_shader_needs_surface_update (shader, device)) { uint32_t offset; offset = emit_binding_table (device, shader); /* Only the PS uses the binding table */ OUT_BATCH (BRW_3DSTATE_BINDING_TABLE_POINTERS | 4); OUT_BATCH (0); /* vs */ OUT_BATCH (0); /* gs */ OUT_BATCH (0); /* clip */ OUT_BATCH (0); /* sf */ OUT_BATCH (offset); device->target = shader->target; device->source = shader->source.base.bo; device->mask = shader->mask.base.bo; device->clip = shader->clip.base.bo; } /* The drawing rectangle clipping is always on. Set it to values that * shouldn't do any clipping. */ draw_rectangle = DRAW_YMAX (shader->target->intel.drm.height) | DRAW_XMAX (shader->target->intel.drm.width); if (draw_rectangle != device->draw_rectangle) { OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); OUT_BATCH (0x00000000); /* ymin, xmin */ OUT_BATCH (draw_rectangle); OUT_BATCH (0x00000000); /* yorigin, xorigin */ device->draw_rectangle = draw_rectangle; } /* skip the depth buffer */ /* skip the polygon stipple */ /* skip the polygon stipple offset */ /* skip the line stipple */ /* Set the pointers to the 3d pipeline state */ if (i965_shader_needs_state_update (shader, device)) { OUT_BATCH (BRW_3DSTATE_PIPELINED_POINTERS | 5); OUT_BATCH (vs_unit_state_emit (device)); OUT_BATCH (BRW_GS_DISABLE); OUT_BATCH (BRW_CLIP_DISABLE); OUT_BATCH (gen4_create_sf_state (device, shader)); OUT_BATCH (gen4_create_wm_state (device, shader)); OUT_BATCH (cc_state_emit (device, shader)); /* Once the units are initialized, we need to setup the fences */ i965_emit_urb_fences (device); } if (i965_shader_needs_constants_update (shader, device)) { uint32_t size = (sizeof (float) * shader->constants_size + 63) & -64; /* XXX reuse clear/black/white * ht! */ /* XXX CONSTANT_BUFFER Address Offset Disable? INSTPM? */ assert (size <= 64 * URB_CS_ENTRY_SIZE); assert (((sizeof (float) * shader->constants_size + 31) & -32) == 32 * i965_shader_const_urb_length (shader)); device->constants = i965_stream_alloc (&device->surface, 64, size); memcpy (device->constants, shader->constants, size); device->constants_size = shader->constants_size; OUT_BATCH (BRW_CONSTANT_BUFFER | (1 << 8)); OUT_BATCH (i965_stream_offsetof (&device->surface, device->constants) + size / 64 - 1); } i965_emit_vertex_element (device, shader); } void i965_flush_vertices (i965_device_t *device) { int vertex_count, vertex_start; if (device->vertex.used == device->vertex.committed) return; assert (device->vertex.used > device->vertex.committed); vertex_start = device->vertex.committed / device->vertex_size; vertex_count = (device->vertex.used - device->vertex.committed) / device->vertex_size; assert (vertex_count); if (device->vertex_size != device->last_vertex_size) { i965_stream_add_pending_relocation (&device->vertex, device->batch.used + 8, I915_GEM_DOMAIN_VERTEX, 0, 0); OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3); OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) | VB0_VERTEXDATA | (device->vertex_size << VB0_BUFFER_PITCH_SHIFT)); OUT_BATCH (0); /* pending relocation */ OUT_BATCH (0); OUT_BATCH (0); device->last_vertex_size = device->vertex_size; } OUT_BATCH (BRW_3DPRIMITIVE | BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | (0 << 9) | 4); OUT_BATCH (vertex_count); /* vertex count per instance */ OUT_BATCH (vertex_start); /* start vertex offset */ OUT_BATCH (1); /* single instance */ OUT_BATCH (0); OUT_BATCH (0); device->vertex.committed = device->vertex.used; } void i965_finish_vertices (i965_device_t *device) { cairo_status_t status; i965_flush_vertices (device); i965_stream_commit (device, &device->vertex); if (! i965_shader_check_aperture (device->shader, device)) { status = i965_device_flush (device); if (unlikely (status)) longjmp (device->shader->unwind, status); status = i965_shader_commit (device->shader, device); assert (status == CAIRO_STATUS_SUCCESS); } device->last_vertex_size = 0; } static cairo_bool_t i965_shader_needs_update (const i965_shader_t *shader, const i965_device_t *device) { if (i965_shader_needs_surface_update (shader, device)) return TRUE; if (i965_shader_needs_constants_update (shader, device)) return TRUE; return i965_shader_needs_state_update (shader, device); } static void i965_shader_reduce (i965_shader_t *shader, const i965_device_t *device) { if (shader->op == CAIRO_OPERATOR_OVER && (i965_wm_kernel_hash (shader) & ~0xff) == 0 && (shader->source.base.content & CAIRO_CONTENT_ALPHA) == 0) { shader->op = CAIRO_OPERATOR_SOURCE; } } cairo_status_t i965_shader_commit (i965_shader_t *shader, i965_device_t *device) { cairo_status_t status; if (! shader->committed) { device->shader = shader; status = i965_shader_setup_dst (shader); if (unlikely (status)) return status; i965_shader_setup_constants (shader); i965_shader_reduce (shader, device); if ((status = setjmp (shader->unwind))) return status; shader->committed = TRUE; } if (! i965_shader_needs_update (shader, device)) return CAIRO_STATUS_SUCCESS; /* XXX too many guestimates about likely maximum sizes */ recheck: if (device->batch.used + 128 > device->batch.size || ! i965_shader_check_aperture (shader, device)) { status = i965_device_flush (device); if (unlikely (status)) longjmp (shader->unwind, status); } i965_flush_vertices (device); if (unlikely (device->surface.used + 128 > device->surface.size || device->surface.num_relocations + 4 > device->surface.max_relocations)) { i965_stream_commit (device, &device->surface); goto recheck; } if (unlikely (device->general.used + 512 > device->general.size)) { i965_stream_commit (device, &device->general); i965_general_state_reset (device); goto recheck; } if (unlikely (device->batch.used == 0)) i965_emit_invariants (device); if (unlikely (device->surface.num_pending_relocations == 0 || device->general.num_pending_relocations == 0)) { i965_emit_base (device); } i965_emit_composite (device, shader); return CAIRO_STATUS_SUCCESS; } void i965_clipped_vertices (i965_device_t *device, struct i965_vbo *vbo, cairo_region_t *clip_region) { int i, num_rectangles, size; cairo_status_t status; if (vbo->count == 0) return; num_rectangles = cairo_region_num_rectangles (clip_region); assert (num_rectangles); if (vbo->next || vbo->count * device->vertex_size + device->vertex.used > device->vertex.size) { i965_finish_vertices (device); size = device->rectangle_size; do { for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, i, &rect); if (unlikely (device->vertex.used + size > device->vertex.size || device->batch.used + 64 > device->batch.size || ! i965_shader_check_aperture (device->shader, device))) { status = i965_device_flush (device); if (unlikely (status)) longjmp (device->shader->unwind, status); status = i965_shader_commit (device->shader, device); assert (status == CAIRO_STATUS_SUCCESS); } i965_emit_relocation (device, &device->batch, vbo->bo, 0, I915_GEM_DOMAIN_VERTEX, 0, device->batch.used + 8); OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3); OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) | VB0_VERTEXDATA | (device->vertex_size << VB0_BUFFER_PITCH_SHIFT)); OUT_BATCH (vbo->bo->offset); OUT_BATCH (0); OUT_BATCH (0); /* XXX scissor? */ OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x)); OUT_BATCH (DRAW_YMAX (rect.y + rect.height) | DRAW_XMAX (rect.x + rect.width)); OUT_BATCH (0x00000000); /* yorigin, xorigin */ OUT_BATCH (BRW_3DPRIMITIVE | BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | (0 << 9) | 4); OUT_BATCH (vbo->count); /* vertex count per instance */ OUT_BATCH (0); /* start vertex offset */ OUT_BATCH (1); /* single instance */ OUT_BATCH (0); OUT_BATCH (0); } } while ((vbo = vbo->next) != NULL); assert (device->last_vertex_size == 0); } else { int vertex_start, vertex_count; void *ptr; vertex_start = device->vertex.committed / device->vertex_size; vertex_count = vbo->count; size = vertex_count * device->vertex_size; ptr = intel_bo_map (&device->intel, vbo->bo); memcpy (device->vertex.data + device->vertex.used, ptr, size); device->vertex.committed = device->vertex.used += size; for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, i, &rect); /* XXX scissor? */ OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x)); OUT_BATCH (DRAW_YMAX (rect.y + rect.height) | DRAW_XMAX (rect.x + rect.width)); OUT_BATCH (0x00000000); /* yorigin, xorigin */ OUT_BATCH (BRW_3DPRIMITIVE | BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | (0 << 9) | 4); OUT_BATCH (vertex_count); /* vertex count per instance */ OUT_BATCH (vertex_start); /* start vertex offset */ OUT_BATCH (1); /* single instance */ OUT_BATCH (0); OUT_BATCH (0); } } device->draw_rectangle = 0; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i965-spans.c000066400000000000000000000243071271037650300267250ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Chris Wilson */ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-drm-i965-private.h" /* Operates in either immediate or retained mode. * When given a clip region we record the sequence of vbo and then * replay them for each clip rectangle, otherwise we simply emit * the vbo straight into the command stream. */ typedef struct _i965_spans i965_spans_t; typedef float * (*i965_get_rectangle_func_t) (i965_spans_t *spans); struct _i965_spans { cairo_span_renderer_t renderer; i965_device_t *device; int xmin, xmax; cairo_bool_t is_bounded; const cairo_rectangle_int_t *extents; i965_get_rectangle_func_t get_rectangle; i965_shader_t shader; cairo_region_t *clip_region; struct i965_vbo head, *tail; unsigned int vbo_offset; float *vbo_base; }; static float * i965_spans_emit_rectangle (i965_spans_t *spans) { return i965_add_rectangle (spans->device); } static float * i965_spans_accumulate_rectangle (i965_spans_t *spans) { float *vertices; uint32_t size; size = spans->device->rectangle_size; if (unlikely (spans->vbo_offset + size > I965_VERTEX_SIZE)) { struct i965_vbo *vbo; vbo = malloc (sizeof (struct i965_vbo)); if (unlikely (vbo == NULL)) { /* throw error! */ } spans->tail->next = vbo; spans->tail = vbo; vbo->next = NULL; vbo->bo = intel_bo_create (&spans->device->intel, I965_VERTEX_SIZE, I965_VERTEX_SIZE, FALSE, I915_TILING_NONE, 0); vbo->count = 0; spans->vbo_offset = 0; spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo); } vertices = spans->vbo_base + spans->vbo_offset; spans->vbo_offset += size; spans->tail->count += 3; return vertices; } static void i965_span_rectangle (i965_spans_t *spans, int x0, int x1, int y0, int y1, int alpha) { float *vertices; float a = alpha / 255.; vertices = spans->get_rectangle (spans); *vertices++ = x1; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y1; *vertices++ = a; *vertices++ = x0; *vertices++ = y0; *vertices++ = a; } static cairo_status_t i965_bounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i965_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage >= 128) { i965_span_rectangle (spans, half[0].x, half[1].x, y, y + height, 255); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_bounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i965_spans_t *spans = abstract_renderer; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; do { if (half[0].coverage) { i965_span_rectangle (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); } half++; } while (--num_spans > 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_unbounded_spans (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i965_spans_t *spans = abstract_renderer; if (num_spans == 0) { i965_span_rectangle (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { i965_span_rectangle (spans, spans->xmin, half[0].x, y, y + height, 0); } do { i965_span_rectangle (spans, half[0].x, half[1].x, y, y + height, half[0].coverage); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { i965_span_rectangle (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_unbounded_spans_mono (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *half, unsigned num_spans) { i965_spans_t *spans = abstract_renderer; if (num_spans == 0) { i965_span_rectangle (spans, spans->xmin, spans->xmax, y, y + height, 0); return CAIRO_STATUS_SUCCESS; } if (half[0].x != spans->xmin) { i965_span_rectangle (spans, spans->xmin, half[0].x, y, y + height, 0); } do { int alpha = 0; if (half[0].coverage >= 128) alpha = 255; i965_span_rectangle (spans, half[0].x, half[1].x, y, y + height, alpha); half++; } while (--num_spans > 1); if (half[0].x != spans->xmax) { i965_span_rectangle (spans, half[0].x, spans->xmax, y, y + height, 0); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t i965_spans_init (i965_spans_t *spans, i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { cairo_status_t status; spans->device = i965_device (dst); i965_shader_init (&spans->shader, dst, op); spans->is_bounded = extents->is_bounded; if (extents->is_bounded) { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i965_bounded_spans_mono; else spans->renderer.render_rows = i965_bounded_spans; spans->extents = &extents->bounded; } else { if (antialias == CAIRO_ANTIALIAS_NONE) spans->renderer.render_rows = i965_unbounded_spans_mono; else spans->renderer.render_rows = i965_unbounded_spans; spans->extents = &extents->unbounded; } spans->xmin = spans->extents->x; spans->xmax = spans->extents->x + spans->extents->width; spans->clip_region = NULL; if (clip != NULL) { cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) clip_region = NULL; spans->clip_region = clip_region; if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&spans->shader, clip); } spans->head.next = NULL; spans->head.bo = NULL; spans->head.count = 0; spans->tail = &spans->head; if (spans->clip_region == NULL) { spans->get_rectangle = i965_spans_emit_rectangle; } else { spans->get_rectangle = i965_spans_accumulate_rectangle; spans->head.bo = intel_bo_create (&spans->device->intel, I965_VERTEX_SIZE, I965_VERTEX_SIZE, FALSE, I915_TILING_NONE, 0); if (unlikely (spans->head.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo); } spans->vbo_offset = 0; return i965_shader_acquire_pattern (&spans->shader, &spans->shader.source, pattern, &extents->bounded); } static void i965_spans_fini (i965_spans_t *spans) { i965_shader_fini (&spans->shader); if (spans->head.bo != NULL) { struct i965_vbo *vbo, *next; intel_bo_destroy (&spans->device->intel, spans->head.bo); for (vbo = spans->head.next; vbo != NULL; vbo = next) { next = vbo->next; intel_bo_destroy (&spans->device->intel, vbo->bo); free (vbo); } } } cairo_status_t i965_clip_and_composite_spans (i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_antialias_t antialias, i965_spans_func_t draw_func, void *draw_closure, const cairo_composite_rectangles_t*extents, cairo_clip_t *clip) { i965_spans_t spans; i965_device_t *device; cairo_status_t status; if (op == CAIRO_OPERATOR_CLEAR) { pattern = &_cairo_pattern_white.base; op = CAIRO_OPERATOR_DEST_OUT; } status = i965_spans_init (&spans, dst, op, pattern, antialias, clip, extents); if (unlikely (status)) return status; spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA; spans.shader.mask.type.fragment = FS_SPANS; spans.shader.mask.type.vertex = VS_SPANS; spans.shader.mask.type.pattern = PATTERN_BASE; status = cairo_device_acquire (dst->intel.drm.base.device); if (unlikely (status)) goto CLEANUP_SPANS; device = i965_device (dst); status = i965_shader_commit (&spans.shader, device); if (unlikely (status)) goto CLEANUP_DEVICE; status = draw_func (draw_closure, &spans.renderer, spans.extents); if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) i965_clipped_vertices (device, &spans.head, spans.clip_region); CLEANUP_DEVICE: cairo_device_release (dst->intel.drm.base.device); CLEANUP_SPANS: i965_spans_fini (&spans); return status; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-i965-surface.c000066400000000000000000001522231271037650300272300ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Kristian Høgsberg * Copyright © 2009 Chris Wilson * Copyright © 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Kristian Høgsberg. * * Based on the xf86-intel-driver i965 render acceleration code, * authored by: * Wang Zhenyu * Eric Anholt * Carl Worth * Keith Packard */ /* XXX * * FIXME: Use brw_PLN for [DevCTG-B+] * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-ioctl-private.h" #include "cairo-drm-intel-private.h" #include "cairo-drm-intel-command-private.h" #include "cairo-drm-intel-ioctl-private.h" #include "cairo-drm-i965-private.h" #include "cairo-boxes-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-region-private.h" #include "cairo-surface-offset-private.h" #include #include #define I965_MAX_SIZE 8192 static const cairo_surface_backend_t i965_surface_backend; static void i965_stream_init (i965_stream_t *stream, uint8_t *data, uint32_t size, struct i965_pending_relocation *pending, int max_pending, struct drm_i915_gem_relocation_entry *relocations, int max_relocations) { stream->used = stream->committed = 0; stream->data = data; stream->size = size; stream->serial = 1; stream->num_pending_relocations = 0; stream->max_pending_relocations = max_pending; stream->pending_relocations = pending; stream->num_relocations = 0; stream->max_relocations = max_relocations; stream->relocations = relocations; } static void i965_add_relocation (i965_device_t *device, intel_bo_t *bo, uint32_t read_domains, uint32_t write_domain) { if (bo->exec == NULL) { int i; device->exec.gtt_size += bo->base.size; i = device->exec.count++; assert (i < ARRAY_LENGTH (device->exec.exec)); device->exec.exec[i].handle = bo->base.handle; device->exec.exec[i].relocation_count = 0; device->exec.exec[i].relocs_ptr = 0; device->exec.exec[i].alignment = 0; device->exec.exec[i].offset = 0; device->exec.exec[i].flags = 0; device->exec.exec[i].rsvd1 = 0; device->exec.exec[i].rsvd2 = 0; device->exec.bo[i] = intel_bo_reference (bo); bo->exec = &device->exec.exec[i]; } if (cairo_list_is_empty (&bo->link)) cairo_list_add_tail (&device->flush, &bo->link); assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain); bo->batch_read_domains |= read_domains; bo->batch_write_domain |= write_domain; } void i965_emit_relocation (i965_device_t *device, i965_stream_t *stream, intel_bo_t *target, uint32_t target_offset, uint32_t read_domains, uint32_t write_domain, uint32_t offset) { int n; assert (target_offset < target->base.size); i965_add_relocation (device, target, read_domains, write_domain); n = stream->num_relocations++; assert (n < stream->max_relocations); stream->relocations[n].offset = offset; stream->relocations[n].delta = target_offset; stream->relocations[n].target_handle = target->base.handle; stream->relocations[n].read_domains = read_domains; stream->relocations[n].write_domain = write_domain; stream->relocations[n].presumed_offset = target->offset; } static void i965_stream_reset (i965_stream_t *stream) { stream->used = stream->committed = 0; stream->num_relocations = 0; stream->num_pending_relocations = 0; if (++stream->serial == 0) stream->serial = 1; } void i965_stream_commit (i965_device_t *device, i965_stream_t *stream) { intel_bo_t *bo; int n; assert (stream->used); bo = intel_bo_create (&device->intel, stream->used, stream->used, FALSE, I915_TILING_NONE, 0); /* apply pending relocations */ for (n = 0; n < stream->num_pending_relocations; n++) { struct i965_pending_relocation *p = &stream->pending_relocations[n]; i965_emit_relocation (device, &device->batch, bo, p->delta, p->read_domains, p->write_domain, p->offset); if (bo->offset) *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta; } intel_bo_write (&device->intel, bo, 0, stream->used, stream->data); if (stream->num_relocations) { assert (bo->exec != NULL); bo->exec->relocs_ptr = (uintptr_t) stream->relocations; bo->exec->relocation_count = stream->num_relocations; } intel_bo_destroy (&device->intel, bo); i965_stream_reset (stream); } static void sf_states_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->sf_states, entry); _cairo_freelist_free (&device->sf_freelist, entry); } static void cc_offsets_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->cc_states, entry); _cairo_freelist_free (&device->cc_freelist, entry); } static void wm_kernels_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->wm_kernels, entry); _cairo_freelist_free (&device->wm_kernel_freelist, entry); } static void wm_states_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->wm_states, entry); _cairo_freelist_free (&device->wm_state_freelist, entry); } static void wm_bindings_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->wm_bindings, entry); _cairo_freelist_free (&device->wm_binding_freelist, entry); } static void samplers_pluck (void *entry, void *closure) { i965_device_t *device = closure; _cairo_hash_table_remove (device->samplers, entry); _cairo_freelist_free (&device->sampler_freelist, entry); } void i965_general_state_reset (i965_device_t *device) { _cairo_hash_table_foreach (device->sf_states, sf_states_pluck, device); _cairo_hash_table_foreach (device->cc_states, cc_offsets_pluck, device); _cairo_hash_table_foreach (device->wm_kernels, wm_kernels_pluck, device); _cairo_hash_table_foreach (device->wm_states, wm_states_pluck, device); _cairo_hash_table_foreach (device->wm_bindings, wm_bindings_pluck, device); _cairo_hash_table_foreach (device->samplers, samplers_pluck, device); device->vs_offset = (uint32_t) -1; device->border_color_offset = (uint32_t) -1; if (device->general_state != NULL) { intel_bo_destroy (&device->intel, device->general_state); device->general_state = NULL; } } static void i965_device_reset (i965_device_t *device) { device->exec.count = 0; device->exec.gtt_size = I965_VERTEX_SIZE + I965_SURFACE_SIZE + I965_GENERAL_SIZE + I965_BATCH_SIZE; device->sf_state.entry.hash = (uint32_t) -1; device->wm_state.entry.hash = (uint32_t) -1; device->wm_binding.entry.hash = (uint32_t) -1; device->cc_state.entry.hash = (uint32_t) -1; device->target = NULL; device->source = NULL; device->mask = NULL; device->clip = NULL; device->draw_rectangle = (uint32_t) -1; device->vertex_type = (uint32_t) -1; device->vertex_size = 0; device->rectangle_size = 0; device->last_vertex_size = 0; device->constants = NULL; device->constants_size = 0; device->have_urb_fences = FALSE; } static cairo_status_t i965_exec (i965_device_t *device, uint32_t offset) { struct drm_i915_gem_execbuffer2 execbuf; cairo_status_t status = CAIRO_STATUS_SUCCESS; int ret, i; execbuf.buffers_ptr = (uintptr_t) device->exec.exec; execbuf.buffer_count = device->exec.count; execbuf.batch_start_offset = offset; execbuf.batch_len = device->batch.used; execbuf.DR1 = 0; execbuf.DR4 = 0; execbuf.num_cliprects = 0; execbuf.cliprects_ptr = 0; execbuf.flags = I915_GEM_3D_PIPELINE; execbuf.rsvd1 = 0; execbuf.rsvd2 = 0; #if 0 printf ("exec: offset=%d, length=%d, buffers=%d\n", offset, device->batch.used, device->exec.count); intel_dump_batchbuffer ((uint32_t *) device->batch.data, device->batch.used, device->intel.base.chip_id); #endif ret = 0; do { ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); } while (ret != 0 && errno == EINTR); if (unlikely (ret)) { if (errno == ENOMEM) status = _cairo_error (CAIRO_STATUS_NO_MEMORY); else status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); fprintf (stderr, "Batch submission failed: %d\n", errno); fprintf (stderr, " gtt size: %zd/%zd\n", device->exec.gtt_size, device->intel.gtt_avail_size); fprintf (stderr, " %d buffers:\n", device->exec.count); for (i = 0; i < device->exec.count; i++) { fprintf (stderr, " exec[%d] = %d\n", i, device->exec.bo[i]->base.size); } intel_dump_batchbuffer ((uint32_t *) device->batch.data, device->batch.used, device->intel.base.chip_id); } /* XXX any write target within the batch should now be in error */ for (i = 0; i < device->exec.count; i++) { intel_bo_t *bo = device->exec.bo[i]; cairo_bool_t ret; bo->offset = device->exec.exec[i].offset; bo->exec = NULL; bo->batch_read_domains = 0; bo->batch_write_domain = 0; if (bo->virtual) intel_bo_unmap (bo); bo->cpu = FALSE; if (bo->purgeable) ret = intel_bo_madvise (&device->intel, bo, I915_MADV_DONTNEED); /* ignore immediate notification of purging */ cairo_list_del (&bo->cache_list); cairo_list_init (&bo->link); intel_bo_destroy (&device->intel, bo); } cairo_list_init (&device->flush); device->exec.count = 0; return status; } static inline uint32_t next_bo_size (uint32_t v) { v = (v + 8191) / 8192; v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v * 8192; } static void _copy_to_bo_and_apply_relocations (i965_device_t *device, intel_bo_t *bo, i965_stream_t *stream, uint32_t offset) { int n; intel_bo_write (&device->intel, bo, offset, stream->used, stream->data); for (n = 0; n < stream->num_pending_relocations; n++) { struct i965_pending_relocation *p = &stream->pending_relocations[n]; i965_emit_relocation (device, &device->batch, bo, p->delta + offset, p->read_domains, p->write_domain, p->offset); if (bo->offset) { *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta + offset; } } } cairo_status_t i965_device_flush (i965_device_t *device) { cairo_status_t status; uint32_t aligned, max; intel_bo_t *bo; int n; if (device->batch.used == 0) return CAIRO_STATUS_SUCCESS; i965_flush_vertices (device); OUT_BATCH (MI_BATCH_BUFFER_END); /* Emit a padding dword if we aren't going to be quad-word aligned. */ if (device->batch.used & 4) OUT_BATCH (MI_NOOP); #if 0 printf ("device flush: vertex=%d, constant=%d, surface=%d, general=%d, batch=%d\n", device->vertex.used, device->constant.used, device->surface.used, device->general.used, device->batch.used); #endif /* can we pack the surface state into the tail of the general state? */ if (device->general.used == device->general.committed) { if (device->general.used) { assert (device->general.num_pending_relocations == 1); assert (device->general_state != NULL); i965_emit_relocation (device, &device->batch, device->general_state, device->general.pending_relocations[0].delta, device->general.pending_relocations[0].read_domains, device->general.pending_relocations[0].write_domain, device->general.pending_relocations[0].offset); if (device->general_state->offset) { *(uint32_t *) (device->batch.data + device->general.pending_relocations[0].offset) = device->general_state->offset + device->general.pending_relocations[0].delta; } } } else { assert (device->general.num_pending_relocations == 1); if (device->general_state != NULL) { intel_bo_destroy (&device->intel, device->general_state); device->general_state = NULL; } bo = intel_bo_create (&device->intel, device->general.used, device->general.used, FALSE, I915_TILING_NONE, 0); if (unlikely (bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); aligned = (device->general.used + 31) & -32; if (device->surface.used && aligned + device->surface.used <= bo->base.size) { _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); if (device->surface.num_relocations) { for (n = 0; n < device->surface.num_relocations; n++) device->surface.relocations[n].offset += aligned; assert (bo->exec != NULL); bo->exec->relocs_ptr = (uintptr_t) device->surface.relocations; bo->exec->relocation_count = device->surface.num_relocations; } i965_stream_reset (&device->surface); } else { _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); } /* Note we don't reset the general state, just mark what data we've committed. */ device->general.committed = device->general.used; device->general_state = bo; } device->general.num_pending_relocations = 0; /* Combine vertex+constant+surface+batch streams? */ max = aligned = device->vertex.used; if (device->surface.used) { aligned = (aligned + 63) & -64; aligned += device->surface.used; if (device->surface.used > max) max = device->surface.used; } aligned = (aligned + 63) & -64; aligned += device->batch.used; if (device->batch.used > max) max = device->batch.used; if (aligned <= next_bo_size (max)) { int batch_num_relocations; if (aligned <= 8192) max = aligned; bo = intel_bo_create (&device->intel, max, max, FALSE, I915_TILING_NONE, 0); if (unlikely (bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); assert (aligned <= bo->base.size); if (device->vertex.used) _copy_to_bo_and_apply_relocations (device, bo, &device->vertex, 0); aligned = device->vertex.used; batch_num_relocations = device->batch.num_relocations; if (device->surface.used) { aligned = (aligned + 63) & -64; _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); batch_num_relocations = device->batch.num_relocations; if (device->surface.num_relocations) { assert (device->batch.num_relocations + device->surface.num_relocations < device->batch.max_relocations); memcpy (device->batch.relocations + device->batch.num_relocations, device->surface.relocations, sizeof (device->surface.relocations[0]) * device->surface.num_relocations); for (n = 0; n < device->surface.num_relocations; n++) device->batch.relocations[device->batch.num_relocations + n].offset += aligned; device->batch.num_relocations += device->surface.num_relocations; } aligned += device->surface.used; } aligned = (aligned + 63) & -64; intel_bo_write (&device->intel, bo, aligned, device->batch.used, device->batch.data); for (n = 0; n < batch_num_relocations; n++) device->batch.relocations[n].offset += aligned; if (device->exec.bo[device->exec.count-1] == bo) { assert (bo->exec == &device->exec.exec[device->exec.count-1]); bo->exec->relocation_count = device->batch.num_relocations; bo->exec->relocs_ptr = (uintptr_t) device->batch.relocations; intel_bo_destroy (&device->intel, bo); } else { assert (bo->exec == NULL); n = device->exec.count++; device->exec.exec[n].handle = bo->base.handle; device->exec.exec[n].relocation_count = device->batch.num_relocations; device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; device->exec.exec[n].alignment = 0; device->exec.exec[n].offset = 0; device->exec.exec[n].flags = 0; device->exec.exec[n].rsvd1 = 0; device->exec.exec[n].rsvd2 = 0; /* transfer ownership to the exec */ device->exec.bo[n] = bo; } } else { i965_stream_commit (device, &device->vertex); if (device->surface.used) i965_stream_commit (device, &device->surface); bo = intel_bo_create (&device->intel, device->batch.used, device->batch.used, FALSE, I915_TILING_NONE, 0); if (unlikely (bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); intel_bo_write (&device->intel, bo, 0, device->batch.used, device->batch.data); n = device->exec.count++; device->exec.exec[n].handle = bo->base.handle; device->exec.exec[n].relocation_count = device->batch.num_relocations; device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; device->exec.exec[n].alignment = 0; device->exec.exec[n].offset = 0; device->exec.exec[n].flags = 0; device->exec.exec[n].rsvd1 = 0; device->exec.exec[n].rsvd2 = 0; /* transfer ownership to the exec */ device->exec.bo[n] = bo; aligned = 0; } status = i965_exec (device, aligned); i965_stream_reset (&device->vertex); i965_stream_reset (&device->surface); i965_stream_reset (&device->batch); intel_glyph_cache_unpin (&device->intel); intel_snapshot_cache_thaw (&device->intel); i965_device_reset (device); return status; } static cairo_surface_t * i965_surface_create_similar (void *abstract_other, cairo_content_t content, int width, int height) { i965_surface_t *other; cairo_format_t format; if (width > 8192 || height > 8192) return NULL; other = abstract_other; if (content == other->intel.drm.base.content) format = other->intel.drm.format; else format = _cairo_format_from_content (content); return i965_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device, format, width, height, I965_TILING_DEFAULT, TRUE); } static cairo_status_t i965_surface_finish (void *abstract_surface) { i965_surface_t *surface = abstract_surface; return intel_surface_finish (&surface->intel); } static cairo_status_t i965_surface_flush (void *abstract_surface, unsigned flags) { i965_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->intel.drm.fallback != NULL) return intel_surface_flush (abstract_surface); /* Forgo flushing on finish as the user cannot access the surface directly. */ if (! surface->intel.drm.base.finished && to_intel_bo (surface->intel.drm.bo)->exec != NULL) { status = cairo_device_acquire (surface->intel.drm.base.device); if (likely (status == CAIRO_STATUS_SUCCESS)) { i965_device_t *device; device = i965_device (surface); status = i965_device_flush (device); cairo_device_release (&device->intel.base.base); } } return status; } /* rasterisation */ static cairo_status_t _composite_boxes_spans (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents) { cairo_boxes_t *boxes = closure; cairo_rectangular_scan_converter_t converter; struct _cairo_boxes_chunk *chunk; cairo_status_t status; _cairo_rectangular_scan_converter_init (&converter, extents); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; int i; for (i = 0; i < chunk->count; i++) { status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); if (unlikely (status)) goto CLEANUP; } } status = converter.base.generate (&converter.base, renderer); CLEANUP: converter.base.destroy (&converter.base); return status; } cairo_status_t i965_fixup_unbounded (i965_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip) { i965_shader_t shader; i965_device_t *device; cairo_status_t status; i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); if (clip != NULL) { cairo_region_t *clip_region = NULL; status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); assert (clip_region == NULL); if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&shader, clip); } else { if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } } status = i965_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_clear.base, &extents->unbounded); if (unlikely (status)) { i965_shader_fini (&shader); return status; } device = i965_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) return status; status = i965_shader_commit (&shader, device); if (unlikely (status)) { goto BAIL; } if (extents->bounded.width == 0 || extents->bounded.height == 0) { i965_shader_add_rectangle (&shader, extents->unbounded.x, extents->unbounded.y, extents->unbounded.width, extents->unbounded.height); } else { /* top */ if (extents->bounded.y != extents->unbounded.y) { cairo_rectangle_int_t rect; rect.x = extents->unbounded.x; rect.y = extents->unbounded.y; rect.width = extents->unbounded.width; rect.height = extents->bounded.y - rect.y; i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } /* left */ if (extents->bounded.x != extents->unbounded.x) { cairo_rectangle_int_t rect; rect.x = extents->unbounded.x; rect.y = extents->bounded.y; rect.width = extents->bounded.x - extents->unbounded.x; rect.height = extents->bounded.height; i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } /* right */ if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { cairo_rectangle_int_t rect; rect.x = extents->bounded.x + extents->bounded.width; rect.y = extents->bounded.y; rect.width = extents->unbounded.x + extents->unbounded.width - rect.x; rect.height = extents->bounded.height; i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } /* bottom */ if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { cairo_rectangle_int_t rect; rect.x = extents->unbounded.x; rect.y = extents->bounded.y + extents->bounded.height; rect.width = extents->unbounded.width; rect.height = extents->unbounded.y + extents->unbounded.height - rect.y; i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } } i965_shader_fini (&shader); BAIL: cairo_device_release (&device->intel.base.base); return status; } static cairo_status_t i965_fixup_unbounded_boxes (i965_surface_t *dst, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip, cairo_boxes_t *boxes) { cairo_boxes_t clear; cairo_box_t box; cairo_region_t *clip_region = NULL; cairo_status_t status; struct _cairo_boxes_chunk *chunk; i965_shader_t shader; int i; if (boxes->num_boxes <= 1) return i965_fixup_unbounded (dst, extents, clip); i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); if (status == CAIRO_INT_STATUS_UNSUPPORTED) i965_shader_set_clip (&shader, clip); } status = i965_shader_acquire_pattern (&shader, &shader.source, &_cairo_pattern_clear.base, &extents->unbounded); if (unlikely (status)) { i965_shader_fini (&shader); return status; } _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (clip_region == NULL) { cairo_boxes_t tmp; _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, &box); assert (status == CAIRO_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; } else { pixman_box32_t *pbox; pbox = pixman_region32_rectangles (&clip_region->rgn, &i); _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); status = _cairo_boxes_add (&clear, &box); assert (status == CAIRO_STATUS_SUCCESS); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { status = _cairo_boxes_add (&clear, &chunk->base[i]); if (unlikely (status)) { _cairo_boxes_fini (&clear); return status; } } } status = _cairo_bentley_ottmann_tessellate_boxes (&clear, CAIRO_FILL_RULE_WINDING, &clear); } if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) { i965_device_t *device; device = i965_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i965_shader_commit (&shader, device); if (unlikely (status)) goto err_device; for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x); int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y); int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x); int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y); i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); } } err_device: cairo_device_release (&device->intel.base.base); err_shader: i965_shader_fini (&shader); } _cairo_boxes_fini (&clear); return status; } static cairo_status_t _composite_boxes (i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *pattern, cairo_boxes_t *boxes, cairo_antialias_t antialias, cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { cairo_bool_t need_clip_surface = FALSE; cairo_region_t *clip_region = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; i965_shader_t shader; i965_device_t *device; int i; /* If the boxes are not pixel-aligned, we will need to compute a real mask */ if (antialias != CAIRO_ANTIALIAS_NONE) { if (! boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; } i965_shader_init (&shader, dst, op); status = i965_shader_acquire_pattern (&shader, &shader.source, pattern, &extents->bounded); if (unlikely (status)) return status; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; if (need_clip_surface) i965_shader_set_clip (&shader, clip); } device = i965_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i965_shader_commit (&shader, i965_device (dst)); if (unlikely (status)) goto err_device; for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int x1 = _cairo_fixed_integer_round (box[i].p1.x); int y1 = _cairo_fixed_integer_round (box[i].p1.y); int x2 = _cairo_fixed_integer_round (box[i].p2.x); int y2 = _cairo_fixed_integer_round (box[i].p2.y); if (x2 > x1 && y2 > y1) i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); } } if (! extents->is_bounded) status = i965_fixup_unbounded_boxes (dst, extents, clip, boxes); err_device: cairo_device_release (&device->intel.base.base); err_shader: i965_shader_fini (&shader); return status; } static cairo_status_t _clip_and_composite_boxes (i965_surface_t *dst, cairo_operator_t op, const cairo_pattern_t *src, cairo_boxes_t *boxes, cairo_antialias_t antialias, const cairo_composite_rectangles_t *extents, cairo_clip_t *clip) { cairo_status_t status; if (boxes->num_boxes == 0) { if (extents->is_bounded) return CAIRO_STATUS_SUCCESS; return i965_fixup_unbounded (dst, extents, clip); } /* Use a fast path if the boxes are pixel aligned */ status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; /* Otherwise render the boxes via an implicit mask and composite in the usual * fashion. */ return i965_clip_and_composite_spans (dst, op, src, antialias, _composite_boxes_spans, boxes, extents, clip); } static cairo_int_status_t i965_surface_paint (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip) { i965_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; cairo_boxes_t boxes; cairo_box_t *clip_boxes = boxes.boxes_embedded; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); cairo_status_t status; /* XXX unsupported operators? use pixel shader blending, eventually */ status = _cairo_composite_rectangles_init_for_paint (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, clip); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); status = _clip_and_composite_boxes (dst, op, source, &boxes, CAIRO_ANTIALIAS_DEFAULT, &extents, clip); if (clip_boxes != boxes.boxes_embedded) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i965_surface_mask (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_clip_t *clip) { i965_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; i965_shader_t shader; i965_device_t *device; cairo_clip_t local_clip; cairo_region_t *clip_region = NULL; cairo_bool_t need_clip_surface = FALSE; cairo_bool_t have_clip = FALSE; cairo_status_t status; status = _cairo_composite_rectangles_init_for_mask (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, mask, clip); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL && extents.is_bounded) { clip = _cairo_clip_init_copy (&local_clip, clip); status = _cairo_clip_rectangle (clip, &extents.bounded); if (unlikely (status)) { _cairo_clip_fini (&local_clip); return status; } have_clip = TRUE; } i965_shader_init (&shader, dst, op); status = i965_shader_acquire_pattern (&shader, &shader.source, source, &extents.bounded); if (unlikely (status)) goto err_shader; status = i965_shader_acquire_pattern (&shader, &shader.mask, mask, &extents.bounded); if (unlikely (status)) goto err_shader; if (clip != NULL) { status = _cairo_clip_get_region (clip, &clip_region); assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; if (need_clip_surface) i965_shader_set_clip (&shader, clip); } device = i965_device (dst); status = cairo_device_acquire (&device->intel.base.base); if (unlikely (status)) goto err_shader; status = i965_shader_commit (&shader, device); if (unlikely (status)) goto err_device; if (clip_region != NULL) { unsigned int n, num_rectangles; num_rectangles = cairo_region_num_rectangles (clip_region); for (n = 0; n < num_rectangles; n++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (clip_region, n, &rect); i965_shader_add_rectangle (&shader, rect.x, rect.y, rect.width, rect.height); } } else { i965_shader_add_rectangle (&shader, extents.bounded.x, extents.bounded.y, extents.bounded.width, extents.bounded.height); } if (! extents.is_bounded) status = i965_fixup_unbounded (dst, &extents, clip); err_device: cairo_device_release (&device->intel.base.base); err_shader: i965_shader_fini (&shader); if (have_clip) _cairo_clip_fini (&local_clip); return status; } typedef struct { cairo_polygon_t polygon; cairo_fill_rule_t fill_rule; cairo_antialias_t antialias; } composite_polygon_info_t; static cairo_status_t _composite_polygon_spans (void *closure, cairo_span_renderer_t *renderer, const cairo_rectangle_int_t *extents) { composite_polygon_info_t *info = closure; cairo_botor_scan_converter_t converter; cairo_status_t status; cairo_box_t box; box.p1.x = _cairo_fixed_from_int (extents->x); box.p1.y = _cairo_fixed_from_int (extents->y); box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); status = converter.base.add_polygon (&converter.base, &info->polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) status = converter.base.generate (&converter.base, renderer); converter.base.destroy (&converter.base); return status; } static cairo_int_status_t i965_surface_stroke (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { i965_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; composite_polygon_info_t info; cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; cairo_status_t status; status = _cairo_composite_rectangles_init_for_stroke (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, path, stroke_style, ctm, clip); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } if (_cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, stroke_style, ctm, &boxes); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (dst, op, source, &boxes, antialias, &extents, clip); } _cairo_boxes_fini (&boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto CLEANUP_BOXES; } _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &info.polygon); if (unlikely (status)) goto CLEANUP_POLYGON; if (extents.is_bounded) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) goto CLEANUP_POLYGON; } if (info.polygon.num_edges == 0) { if (! extents.is_bounded) status = i965_fixup_unbounded (dst, &extents, clip); } else { info.fill_rule = CAIRO_FILL_RULE_WINDING; info.antialias = antialias; status = i965_clip_and_composite_spans (dst, op, source, antialias, _composite_polygon_spans, &info, &extents, clip); } CLEANUP_POLYGON: _cairo_polygon_fini (&info.polygon); CLEANUP_BOXES: if (clip_boxes != boxes_stack) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static cairo_int_status_t i965_surface_fill (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t*source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { i965_surface_t *dst = abstract_dst; cairo_composite_rectangles_t extents; composite_polygon_info_t info; cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; cairo_clip_t local_clip; cairo_bool_t have_clip = FALSE; int num_boxes = ARRAY_LENGTH (boxes_stack); cairo_status_t status; status = _cairo_composite_rectangles_init_for_fill (&extents, dst->intel.drm.width, dst->intel.drm.height, op, source, path, clip); if (unlikely (status)) return status; if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) clip = NULL; if (clip != NULL) { clip = _cairo_clip_init_copy (&local_clip, clip); have_clip = TRUE; } status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); if (unlikely (status)) { if (have_clip) _cairo_clip_fini (&local_clip); return status; } assert (! _cairo_path_fixed_fill_is_empty (path)); if (_cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; _cairo_boxes_init (&boxes); _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, &boxes); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _clip_and_composite_boxes (dst, op, source, &boxes, antialias, &extents, clip); } _cairo_boxes_fini (&boxes); if (status != CAIRO_INT_STATUS_UNSUPPORTED) goto CLEANUP_BOXES; } _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon); if (unlikely (status)) goto CLEANUP_POLYGON; if (extents.is_bounded) { cairo_rectangle_int_t rect; _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) goto CLEANUP_POLYGON; } if (info.polygon.num_edges == 0) { if (! extents.is_bounded) status = i965_fixup_unbounded (dst, &extents, clip); } else { info.fill_rule = fill_rule; info.antialias = antialias; status = i965_clip_and_composite_spans (dst, op, source, antialias, _composite_polygon_spans, &info, &extents, clip); } CLEANUP_POLYGON: _cairo_polygon_fini (&info.polygon); CLEANUP_BOXES: if (clip_boxes != boxes_stack) free (clip_boxes); if (have_clip) _cairo_clip_fini (&local_clip); return status; } static const cairo_surface_backend_t i965_surface_backend = { CAIRO_SURFACE_TYPE_DRM, _cairo_default_context_create, i965_surface_create_similar, i965_surface_finish, NULL, intel_surface_acquire_source_image, intel_surface_release_source_image, NULL, NULL, NULL, NULL, /* composite */ NULL, /* fill */ NULL, /* trapezoids */ NULL, /* span */ NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_drm_surface_get_extents, NULL, /* old-glyphs */ _cairo_drm_surface_get_font_options, i965_surface_flush, NULL, /* mark_dirty */ intel_scaled_font_fini, intel_scaled_glyph_fini, i965_surface_paint, i965_surface_mask, i965_surface_stroke, i965_surface_fill, i965_surface_glyphs, }; static void i965_surface_init (i965_surface_t *surface, cairo_drm_device_t *device, cairo_format_t format, int width, int height) { intel_surface_init (&surface->intel, &i965_surface_backend, device, format, width, height); surface->stream = 0; } static inline int cairo_const i965_tiling_stride (uint32_t tiling, int stride) { if (tiling == I915_TILING_NONE) return stride; return (stride + 127) & -128; } static inline int cairo_const i965_tiling_height (uint32_t tiling, int height) { switch (tiling) { default: case I915_TILING_NONE: return (height + 1) & -2; case I915_TILING_X: return (height + 7) & -8; case I915_TILING_Y: return (height + 31) & -32; } } cairo_surface_t * i965_surface_create_internal (cairo_drm_device_t *base_dev, cairo_format_t format, int width, int height, uint32_t tiling, cairo_bool_t gpu_target) { i965_surface_t *surface; cairo_status_t status_ignored; surface = malloc (sizeof (i965_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); i965_surface_init (surface, base_dev, format, width, height); if (width && height) { uint32_t size, stride; intel_bo_t *bo; width = (width + 3) & -4; stride = cairo_format_stride_for_width (surface->intel.drm.format, width); stride = (stride + 63) & ~63; stride = i965_tiling_stride (tiling, stride); surface->intel.drm.stride = stride; height = i965_tiling_height (tiling, height); assert (height <= I965_MAX_SIZE); size = stride * height; bo = intel_bo_create (to_intel_device (&base_dev->base), size, size, gpu_target, tiling, stride); if (bo == NULL) { status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } bo->tiling = tiling; bo->stride = stride; surface->intel.drm.bo = &bo->base; assert (bo->base.size >= (size_t) stride*height); } return &surface->intel.drm.base; } static cairo_surface_t * i965_surface_create (cairo_drm_device_t *device, cairo_format_t format, int width, int height) { switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_INVALID: default: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } return i965_surface_create_internal (device, format, width, height, I965_TILING_DEFAULT, TRUE); } static cairo_surface_t * i965_surface_create_for_name (cairo_drm_device_t *base_dev, unsigned int name, cairo_format_t format, int width, int height, int stride) { i965_device_t *device; i965_surface_t *surface; cairo_status_t status_ignored; int min_stride; min_stride = cairo_format_stride_for_width (format, (width + 3) & -4); if (stride < min_stride || stride & 63) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); if (format == CAIRO_FORMAT_A1) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; case CAIRO_FORMAT_INVALID: default: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } surface = malloc (sizeof (i965_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); i965_surface_init (surface, base_dev, format, width, height); device = (i965_device_t *) base_dev; surface->intel.drm.bo = &intel_bo_create_for_name (&device->intel, name)->base; if (unlikely (surface->intel.drm.bo == NULL)) { status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } surface->intel.drm.stride = stride; return &surface->intel.drm.base; } static cairo_status_t i965_surface_enable_scan_out (void *abstract_surface) { i965_surface_t *surface = abstract_surface; intel_bo_t *bo; if (unlikely (surface->intel.drm.bo == NULL)) return _cairo_error (CAIRO_STATUS_INVALID_SIZE); bo = to_intel_bo (surface->intel.drm.bo); if (bo->tiling != I915_TILING_X) { i965_device_t *device = i965_device (surface); cairo_surface_pattern_t pattern; cairo_surface_t *clone; cairo_status_t status; clone = i965_surface_create_internal (&device->intel.base, surface->intel.drm.base.content, surface->intel.drm.width, surface->intel.drm.height, I915_TILING_X, TRUE); if (unlikely (clone->status)) return clone->status; /* 2D blit? */ _cairo_pattern_init_for_surface (&pattern, &surface->intel.drm.base); pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (clone, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); _cairo_pattern_fini (&pattern.base); if (unlikely (status)) { cairo_surface_destroy (clone); return status; } /* swap buffer objects */ surface->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo; ((cairo_drm_surface_t *) clone)->bo = &bo->base; bo = to_intel_bo (surface->intel.drm.bo); cairo_surface_destroy (clone); } if (unlikely (bo->tiling == I915_TILING_Y)) return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */ return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _i965_device_flush (cairo_drm_device_t *device) { cairo_status_t status; if (unlikely (device->base.finished)) return CAIRO_STATUS_SUCCESS; status = cairo_device_acquire (&device->base); if (likely (status == CAIRO_STATUS_SUCCESS)) status = i965_device_flush ((i965_device_t *) device); cairo_device_release (&device->base); return status; } static cairo_int_status_t _i965_device_throttle (cairo_drm_device_t *device) { cairo_status_t status; status = cairo_device_acquire (&device->base); if (unlikely (status)) return status; status = i965_device_flush ((i965_device_t *) device); intel_throttle ((intel_device_t *) device); cairo_device_release (&device->base); return status; } static void _i965_device_destroy (void *base) { i965_device_t *device = base; i965_device_reset (device); i965_general_state_reset (device); _cairo_hash_table_destroy (device->sf_states); _cairo_hash_table_destroy (device->samplers); _cairo_hash_table_destroy (device->cc_states); _cairo_hash_table_destroy (device->wm_kernels); _cairo_hash_table_destroy (device->wm_states); _cairo_hash_table_destroy (device->wm_bindings); _cairo_freelist_fini (&device->sf_freelist); _cairo_freelist_fini (&device->cc_freelist); _cairo_freelist_fini (&device->wm_kernel_freelist); _cairo_freelist_fini (&device->wm_state_freelist); _cairo_freelist_fini (&device->wm_binding_freelist); _cairo_freelist_fini (&device->sampler_freelist); intel_device_fini (&device->intel); free (device); } static cairo_bool_t hash_equal (const void *A, const void *B) { const cairo_hash_entry_t *a = A, *b = B; return a->hash == b->hash; } cairo_drm_device_t * _cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id) { i965_device_t *device; uint64_t gtt_size; cairo_status_t status; if (! intel_info (fd, >t_size)) return NULL; device = malloc (sizeof (i965_device_t)); if (unlikely (device == NULL)) return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); status = intel_device_init (&device->intel, fd); if (unlikely (status)) goto CLEANUP; device->is_g4x = IS_G4X (chip_id); //device->is_g5x = IS_G5X (chip_id); device->intel.base.surface.create = i965_surface_create; device->intel.base.surface.create_for_name = i965_surface_create_for_name; device->intel.base.surface.create_from_cacheable_image = NULL; device->intel.base.surface.enable_scan_out = i965_surface_enable_scan_out; device->intel.base.device.flush = _i965_device_flush; device->intel.base.device.throttle = _i965_device_throttle; device->intel.base.device.destroy = _i965_device_destroy; device->sf_states = _cairo_hash_table_create (i965_sf_state_equal); if (unlikely (device->sf_states == NULL)) goto CLEANUP_INTEL; _cairo_freelist_init (&device->sf_freelist, sizeof (struct i965_sf_state)); device->cc_states = _cairo_hash_table_create (i965_cc_state_equal); if (unlikely (device->cc_states == NULL)) goto CLEANUP_SF; _cairo_freelist_init (&device->cc_freelist, sizeof (struct i965_cc_state)); device->wm_kernels = _cairo_hash_table_create (hash_equal); if (unlikely (device->wm_kernels == NULL)) goto CLEANUP_CC; _cairo_freelist_init (&device->wm_kernel_freelist, sizeof (struct i965_wm_kernel)); device->wm_states = _cairo_hash_table_create (i965_wm_state_equal); if (unlikely (device->wm_states == NULL)) goto CLEANUP_WM_KERNEL; _cairo_freelist_init (&device->wm_state_freelist, sizeof (struct i965_wm_state)); device->wm_bindings = _cairo_hash_table_create (i965_wm_binding_equal); if (unlikely (device->wm_bindings == NULL)) goto CLEANUP_WM_STATE; _cairo_freelist_init (&device->wm_binding_freelist, sizeof (struct i965_wm_binding)); device->samplers = _cairo_hash_table_create (hash_equal); if (unlikely (device->samplers == NULL)) goto CLEANUP_WM_BINDING; _cairo_freelist_init (&device->sampler_freelist, sizeof (struct i965_sampler)); i965_stream_init (&device->batch, device->batch_base, sizeof (device->batch_base), NULL, 0, device->batch_relocations, ARRAY_LENGTH (device->batch_relocations)); i965_stream_init (&device->surface, device->surface_base, sizeof (device->surface_base), device->surface_pending_relocations, ARRAY_LENGTH (device->surface_pending_relocations), device->surface_relocations, ARRAY_LENGTH (device->surface_relocations)); i965_stream_init (&device->general, device->general_base, sizeof (device->general_base), device->general_pending_relocations, ARRAY_LENGTH (device->general_pending_relocations), NULL, 0); i965_stream_init (&device->vertex, device->vertex_base, sizeof (device->vertex_base), device->vertex_pending_relocations, ARRAY_LENGTH (device->vertex_pending_relocations), NULL, 0); cairo_list_init (&device->flush); i965_device_reset (device); device->vs_offset = (uint32_t) -1; device->border_color_offset = (uint32_t) -1; device->general_state = NULL; return _cairo_drm_device_init (&device->intel.base, fd, dev, vendor_id, chip_id, I965_MAX_SIZE); CLEANUP_WM_BINDING: _cairo_hash_table_destroy (device->wm_bindings); CLEANUP_WM_STATE: _cairo_hash_table_destroy (device->wm_states); CLEANUP_WM_KERNEL: _cairo_hash_table_destroy (device->wm_kernels); CLEANUP_CC: _cairo_hash_table_destroy (device->cc_states); CLEANUP_SF: _cairo_hash_table_destroy (device->sf_states); CLEANUP_INTEL: intel_device_fini (&device->intel); CLEANUP: free (device); return (cairo_drm_device_t *) _cairo_device_create_in_error (status); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-defines.h000066400000000000000000001022661271037650300304330ustar00rootroot00000000000000/************************************************************************** * * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #ifndef CAIRO_DRM_INTEL_BRW_DEFINES_H #define CAIRO_DRM_INTEL_BRW_DEFINES_H /* 3D state: */ #define _3DOP_3DSTATE_PIPELINED 0x0 #define _3DOP_3DSTATE_NONPIPELINED 0x1 #define _3DOP_3DCONTROL 0x2 #define _3DOP_3DPRIMITIVE 0x3 #define _3DSTATE_PIPELINED_POINTERS 0x00 #define _3DSTATE_BINDING_TABLE_POINTERS 0x01 #define _3DSTATE_VERTEX_BUFFERS 0x08 #define _3DSTATE_VERTEX_ELEMENTS 0x09 #define _3DSTATE_INDEX_BUFFER 0x0A #define _3DSTATE_VF_STATISTICS 0x0B #define _3DSTATE_DRAWING_RECTANGLE 0x00 #define _3DSTATE_CONSTANT_COLOR 0x01 #define _3DSTATE_SAMPLER_PALETTE_LOAD 0x02 #define _3DSTATE_CHROMA_KEY 0x04 #define _3DSTATE_DEPTH_BUFFER 0x05 #define _3DSTATE_POLY_STIPPLE_OFFSET 0x06 #define _3DSTATE_POLY_STIPPLE_PATTERN 0x07 #define _3DSTATE_LINE_STIPPLE 0x08 #define _3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP 0x09 #define _3DCONTROL 0x00 #define _3DPRIMITIVE 0x00 #define PIPE_CONTROL_NOWRITE 0x00 #define PIPE_CONTROL_WRITEIMMEDIATE 0x01 #define PIPE_CONTROL_WRITEDEPTH 0x02 #define PIPE_CONTROL_WRITETIMESTAMP 0x03 #define PIPE_CONTROL_GTTWRITE_PROCESS_LOCAL 0x00 #define PIPE_CONTROL_GTTWRITE_GLOBAL 0x01 #define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \ ((Pipeline) << 27) | \ ((Opcode) << 24) | \ ((Subopcode) << 16)) #define BRW_PIPE_CONTROL BRW_3D(3, 2, 0) #define BRW_PIPE_CONTROL_NOWRITE (0 << 14) #define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14) #define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14) #define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14) #define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13) #define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12) #define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11) #define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8) #define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2) #define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2) #define _3DPRIM_POINTLIST 0x01 #define _3DPRIM_LINELIST 0x02 #define _3DPRIM_LINESTRIP 0x03 #define _3DPRIM_TRILIST 0x04 #define _3DPRIM_TRISTRIP 0x05 #define _3DPRIM_TRIFAN 0x06 #define _3DPRIM_QUADLIST 0x07 #define _3DPRIM_QUADSTRIP 0x08 #define _3DPRIM_LINELIST_ADJ 0x09 #define _3DPRIM_LINESTRIP_ADJ 0x0A #define _3DPRIM_TRILIST_ADJ 0x0B #define _3DPRIM_TRISTRIP_ADJ 0x0C #define _3DPRIM_TRISTRIP_REVERSE 0x0D #define _3DPRIM_POLYGON 0x0E #define _3DPRIM_RECTLIST 0x0F #define _3DPRIM_LINELOOP 0x10 #define _3DPRIM_POINTLIST_BF 0x11 #define _3DPRIM_LINESTRIP_CONT 0x12 #define _3DPRIM_LINESTRIP_BF 0x13 #define _3DPRIM_LINESTRIP_CONT_BF 0x14 #define _3DPRIM_TRIFAN_NOSTIPPLE 0x15 #define _3DPRIM_VERTEXBUFFER_ACCESS_SEQUENTIAL 0 #define _3DPRIM_VERTEXBUFFER_ACCESS_RANDOM 1 #define BRW_ANISORATIO_2 0 #define BRW_ANISORATIO_4 1 #define BRW_ANISORATIO_6 2 #define BRW_ANISORATIO_8 3 #define BRW_ANISORATIO_10 4 #define BRW_ANISORATIO_12 5 #define BRW_ANISORATIO_14 6 #define BRW_ANISORATIO_16 7 #define BRW_BLENDFACTOR_ONE 0x1 #define BRW_BLENDFACTOR_SRC_COLOR 0x2 #define BRW_BLENDFACTOR_SRC_ALPHA 0x3 #define BRW_BLENDFACTOR_DST_ALPHA 0x4 #define BRW_BLENDFACTOR_DST_COLOR 0x5 #define BRW_BLENDFACTOR_SRC_ALPHA_SATURATE 0x6 #define BRW_BLENDFACTOR_CONST_COLOR 0x7 #define BRW_BLENDFACTOR_CONST_ALPHA 0x8 #define BRW_BLENDFACTOR_SRC1_COLOR 0x9 #define BRW_BLENDFACTOR_SRC1_ALPHA 0x0A #define BRW_BLENDFACTOR_ZERO 0x11 #define BRW_BLENDFACTOR_INV_SRC_COLOR 0x12 #define BRW_BLENDFACTOR_INV_SRC_ALPHA 0x13 #define BRW_BLENDFACTOR_INV_DST_ALPHA 0x14 #define BRW_BLENDFACTOR_INV_DST_COLOR 0x15 #define BRW_BLENDFACTOR_INV_CONST_COLOR 0x17 #define BRW_BLENDFACTOR_INV_CONST_ALPHA 0x18 #define BRW_BLENDFACTOR_INV_SRC1_COLOR 0x19 #define BRW_BLENDFACTOR_INV_SRC1_ALPHA 0x1A #define BRW_BLENDFUNCTION_ADD 0 #define BRW_BLENDFUNCTION_SUBTRACT 1 #define BRW_BLENDFUNCTION_REVERSE_SUBTRACT 2 #define BRW_BLENDFUNCTION_MIN 3 #define BRW_BLENDFUNCTION_MAX 4 #define BRW_ALPHATEST_FORMAT_UNORM8 0 #define BRW_ALPHATEST_FORMAT_FLOAT32 1 #define BRW_CHROMAKEY_KILL_ON_ANY_MATCH 0 #define BRW_CHROMAKEY_REPLACE_BLACK 1 #define BRW_CLIP_API_OGL 0 #define BRW_CLIP_API_DX 1 #define BRW_CLIPMODE_NORMAL 0 #define BRW_CLIPMODE_CLIP_ALL 1 #define BRW_CLIPMODE_CLIP_NON_REJECTED 2 #define BRW_CLIPMODE_REJECT_ALL 3 #define BRW_CLIPMODE_ACCEPT_ALL 4 #define BRW_CLIP_NDCSPACE 0 #define BRW_CLIP_SCREENSPACE 1 #define BRW_COMPAREFUNCTION_ALWAYS 0 #define BRW_COMPAREFUNCTION_NEVER 1 #define BRW_COMPAREFUNCTION_LESS 2 #define BRW_COMPAREFUNCTION_EQUAL 3 #define BRW_COMPAREFUNCTION_LEQUAL 4 #define BRW_COMPAREFUNCTION_GREATER 5 #define BRW_COMPAREFUNCTION_NOTEQUAL 6 #define BRW_COMPAREFUNCTION_GEQUAL 7 #define BRW_COVERAGE_PIXELS_HALF 0 #define BRW_COVERAGE_PIXELS_1 1 #define BRW_COVERAGE_PIXELS_2 2 #define BRW_COVERAGE_PIXELS_4 3 #define BRW_CULLMODE_BOTH 0 #define BRW_CULLMODE_NONE 1 #define BRW_CULLMODE_FRONT 2 #define BRW_CULLMODE_BACK 3 #define BRW_DEFAULTCOLOR_R8G8B8A8_UNORM 0 #define BRW_DEFAULTCOLOR_R32G32B32A32_FLOAT 1 #define BRW_DEPTHFORMAT_D32_FLOAT_S8X24_UINT 0 #define BRW_DEPTHFORMAT_D32_FLOAT 1 #define BRW_DEPTHFORMAT_D24_UNORM_S8_UINT 2 #define BRW_DEPTHFORMAT_D16_UNORM 5 #define BRW_FLOATING_POINT_IEEE_754 0 #define BRW_FLOATING_POINT_NON_IEEE_754 1 #define BRW_FRONTWINDING_CW 0 #define BRW_FRONTWINDING_CCW 1 #define BRW_INDEX_BYTE 0 #define BRW_INDEX_WORD 1 #define BRW_INDEX_DWORD 2 #define BRW_LOGICOPFUNCTION_CLEAR 0 #define BRW_LOGICOPFUNCTION_NOR 1 #define BRW_LOGICOPFUNCTION_AND_INVERTED 2 #define BRW_LOGICOPFUNCTION_COPY_INVERTED 3 #define BRW_LOGICOPFUNCTION_AND_REVERSE 4 #define BRW_LOGICOPFUNCTION_INVERT 5 #define BRW_LOGICOPFUNCTION_XOR 6 #define BRW_LOGICOPFUNCTION_NAND 7 #define BRW_LOGICOPFUNCTION_AND 8 #define BRW_LOGICOPFUNCTION_EQUIV 9 #define BRW_LOGICOPFUNCTION_NOOP 10 #define BRW_LOGICOPFUNCTION_OR_INVERTED 11 #define BRW_LOGICOPFUNCTION_COPY 12 #define BRW_LOGICOPFUNCTION_OR_REVERSE 13 #define BRW_LOGICOPFUNCTION_OR 14 #define BRW_LOGICOPFUNCTION_SET 15 #define BRW_MAPFILTER_NEAREST 0x0 #define BRW_MAPFILTER_LINEAR 0x1 #define BRW_MAPFILTER_ANISOTROPIC 0x2 #define BRW_MIPFILTER_NONE 0 #define BRW_MIPFILTER_NEAREST 1 #define BRW_MIPFILTER_LINEAR 3 #define BRW_POLYGON_FRONT_FACING 0 #define BRW_POLYGON_BACK_FACING 1 #define BRW_PREFILTER_ALWAYS 0x0 #define BRW_PREFILTER_NEVER 0x1 #define BRW_PREFILTER_LESS 0x2 #define BRW_PREFILTER_EQUAL 0x3 #define BRW_PREFILTER_LEQUAL 0x4 #define BRW_PREFILTER_GREATER 0x5 #define BRW_PREFILTER_NOTEQUAL 0x6 #define BRW_PREFILTER_GEQUAL 0x7 #define BRW_PROVOKING_VERTEX_0 0 #define BRW_PROVOKING_VERTEX_1 1 #define BRW_PROVOKING_VERTEX_2 2 #define BRW_RASTRULE_UPPER_LEFT 0 #define BRW_RASTRULE_UPPER_RIGHT 1 #define BRW_RENDERTARGET_CLAMPRANGE_UNORM 0 #define BRW_RENDERTARGET_CLAMPRANGE_SNORM 1 #define BRW_RENDERTARGET_CLAMPRANGE_FORMAT 2 #define BRW_STENCILOP_KEEP 0 #define BRW_STENCILOP_ZERO 1 #define BRW_STENCILOP_REPLACE 2 #define BRW_STENCILOP_INCRSAT 3 #define BRW_STENCILOP_DECRSAT 4 #define BRW_STENCILOP_INCR 5 #define BRW_STENCILOP_DECR 6 #define BRW_STENCILOP_INVERT 7 #define BRW_SURFACE_MIPMAPLAYOUT_BELOW 0 #define BRW_SURFACE_MIPMAPLAYOUT_RIGHT 1 #define BRW_SURFACEFORMAT_R32G32B32A32_FLOAT 0x000 #define BRW_SURFACEFORMAT_R32G32B32A32_SINT 0x001 #define BRW_SURFACEFORMAT_R32G32B32A32_UINT 0x002 #define BRW_SURFACEFORMAT_R32G32B32A32_UNORM 0x003 #define BRW_SURFACEFORMAT_R32G32B32A32_SNORM 0x004 #define BRW_SURFACEFORMAT_R64G64_FLOAT 0x005 #define BRW_SURFACEFORMAT_R32G32B32X32_FLOAT 0x006 #define BRW_SURFACEFORMAT_R32G32B32A32_SSCALED 0x007 #define BRW_SURFACEFORMAT_R32G32B32A32_USCALED 0x008 #define BRW_SURFACEFORMAT_R32G32B32_FLOAT 0x040 #define BRW_SURFACEFORMAT_R32G32B32_SINT 0x041 #define BRW_SURFACEFORMAT_R32G32B32_UINT 0x042 #define BRW_SURFACEFORMAT_R32G32B32_UNORM 0x043 #define BRW_SURFACEFORMAT_R32G32B32_SNORM 0x044 #define BRW_SURFACEFORMAT_R32G32B32_SSCALED 0x045 #define BRW_SURFACEFORMAT_R32G32B32_USCALED 0x046 #define BRW_SURFACEFORMAT_R16G16B16A16_UNORM 0x080 #define BRW_SURFACEFORMAT_R16G16B16A16_SNORM 0x081 #define BRW_SURFACEFORMAT_R16G16B16A16_SINT 0x082 #define BRW_SURFACEFORMAT_R16G16B16A16_UINT 0x083 #define BRW_SURFACEFORMAT_R16G16B16A16_FLOAT 0x084 #define BRW_SURFACEFORMAT_R32G32_FLOAT 0x085 #define BRW_SURFACEFORMAT_R32G32_SINT 0x086 #define BRW_SURFACEFORMAT_R32G32_UINT 0x087 #define BRW_SURFACEFORMAT_R32_FLOAT_X8X24_TYPELESS 0x088 #define BRW_SURFACEFORMAT_X32_TYPELESS_G8X24_UINT 0x089 #define BRW_SURFACEFORMAT_L32A32_FLOAT 0x08A #define BRW_SURFACEFORMAT_R32G32_UNORM 0x08B #define BRW_SURFACEFORMAT_R32G32_SNORM 0x08C #define BRW_SURFACEFORMAT_R64_FLOAT 0x08D #define BRW_SURFACEFORMAT_R16G16B16X16_UNORM 0x08E #define BRW_SURFACEFORMAT_R16G16B16X16_FLOAT 0x08F #define BRW_SURFACEFORMAT_A32X32_FLOAT 0x090 #define BRW_SURFACEFORMAT_L32X32_FLOAT 0x091 #define BRW_SURFACEFORMAT_I32X32_FLOAT 0x092 #define BRW_SURFACEFORMAT_R16G16B16A16_SSCALED 0x093 #define BRW_SURFACEFORMAT_R16G16B16A16_USCALED 0x094 #define BRW_SURFACEFORMAT_R32G32_SSCALED 0x095 #define BRW_SURFACEFORMAT_R32G32_USCALED 0x096 #define BRW_SURFACEFORMAT_B8G8R8A8_UNORM 0x0C0 #define BRW_SURFACEFORMAT_B8G8R8A8_UNORM_SRGB 0x0C1 #define BRW_SURFACEFORMAT_R10G10B10A2_UNORM 0x0C2 #define BRW_SURFACEFORMAT_R10G10B10A2_UNORM_SRGB 0x0C3 #define BRW_SURFACEFORMAT_R10G10B10A2_UINT 0x0C4 #define BRW_SURFACEFORMAT_R10G10B10_SNORM_A2_UNORM 0x0C5 #define BRW_SURFACEFORMAT_R8G8B8A8_UNORM 0x0C7 #define BRW_SURFACEFORMAT_R8G8B8A8_UNORM_SRGB 0x0C8 #define BRW_SURFACEFORMAT_R8G8B8A8_SNORM 0x0C9 #define BRW_SURFACEFORMAT_R8G8B8A8_SINT 0x0CA #define BRW_SURFACEFORMAT_R8G8B8A8_UINT 0x0CB #define BRW_SURFACEFORMAT_R16G16_UNORM 0x0CC #define BRW_SURFACEFORMAT_R16G16_SNORM 0x0CD #define BRW_SURFACEFORMAT_R16G16_SINT 0x0CE #define BRW_SURFACEFORMAT_R16G16_UINT 0x0CF #define BRW_SURFACEFORMAT_R16G16_FLOAT 0x0D0 #define BRW_SURFACEFORMAT_B10G10R10A2_UNORM 0x0D1 #define BRW_SURFACEFORMAT_B10G10R10A2_UNORM_SRGB 0x0D2 #define BRW_SURFACEFORMAT_R11G11B10_FLOAT 0x0D3 #define BRW_SURFACEFORMAT_R32_SINT 0x0D6 #define BRW_SURFACEFORMAT_R32_UINT 0x0D7 #define BRW_SURFACEFORMAT_R32_FLOAT 0x0D8 #define BRW_SURFACEFORMAT_R24_UNORM_X8_TYPELESS 0x0D9 #define BRW_SURFACEFORMAT_X24_TYPELESS_G8_UINT 0x0DA #define BRW_SURFACEFORMAT_L16A16_UNORM 0x0DF #define BRW_SURFACEFORMAT_I24X8_UNORM 0x0E0 #define BRW_SURFACEFORMAT_L24X8_UNORM 0x0E1 #define BRW_SURFACEFORMAT_A24X8_UNORM 0x0E2 #define BRW_SURFACEFORMAT_I32_FLOAT 0x0E3 #define BRW_SURFACEFORMAT_L32_FLOAT 0x0E4 #define BRW_SURFACEFORMAT_A32_FLOAT 0x0E5 #define BRW_SURFACEFORMAT_B8G8R8X8_UNORM 0x0E9 #define BRW_SURFACEFORMAT_B8G8R8X8_UNORM_SRGB 0x0EA #define BRW_SURFACEFORMAT_R8G8B8X8_UNORM 0x0EB #define BRW_SURFACEFORMAT_R8G8B8X8_UNORM_SRGB 0x0EC #define BRW_SURFACEFORMAT_R9G9B9E5_SHAREDEXP 0x0ED #define BRW_SURFACEFORMAT_B10G10R10X2_UNORM 0x0EE #define BRW_SURFACEFORMAT_L16A16_FLOAT 0x0F0 #define BRW_SURFACEFORMAT_R32_UNORM 0x0F1 #define BRW_SURFACEFORMAT_R32_SNORM 0x0F2 #define BRW_SURFACEFORMAT_R10G10B10X2_USCALED 0x0F3 #define BRW_SURFACEFORMAT_R8G8B8A8_SSCALED 0x0F4 #define BRW_SURFACEFORMAT_R8G8B8A8_USCALED 0x0F5 #define BRW_SURFACEFORMAT_R16G16_SSCALED 0x0F6 #define BRW_SURFACEFORMAT_R16G16_USCALED 0x0F7 #define BRW_SURFACEFORMAT_R32_SSCALED 0x0F8 #define BRW_SURFACEFORMAT_R32_USCALED 0x0F9 #define BRW_SURFACEFORMAT_B5G6R5_UNORM 0x100 #define BRW_SURFACEFORMAT_B5G6R5_UNORM_SRGB 0x101 #define BRW_SURFACEFORMAT_B5G5R5A1_UNORM 0x102 #define BRW_SURFACEFORMAT_B5G5R5A1_UNORM_SRGB 0x103 #define BRW_SURFACEFORMAT_B4G4R4A4_UNORM 0x104 #define BRW_SURFACEFORMAT_B4G4R4A4_UNORM_SRGB 0x105 #define BRW_SURFACEFORMAT_R8G8_UNORM 0x106 #define BRW_SURFACEFORMAT_R8G8_SNORM 0x107 #define BRW_SURFACEFORMAT_R8G8_SINT 0x108 #define BRW_SURFACEFORMAT_R8G8_UINT 0x109 #define BRW_SURFACEFORMAT_R16_UNORM 0x10A #define BRW_SURFACEFORMAT_R16_SNORM 0x10B #define BRW_SURFACEFORMAT_R16_SINT 0x10C #define BRW_SURFACEFORMAT_R16_UINT 0x10D #define BRW_SURFACEFORMAT_R16_FLOAT 0x10E #define BRW_SURFACEFORMAT_I16_UNORM 0x111 #define BRW_SURFACEFORMAT_L16_UNORM 0x112 #define BRW_SURFACEFORMAT_A16_UNORM 0x113 #define BRW_SURFACEFORMAT_L8A8_UNORM 0x114 #define BRW_SURFACEFORMAT_I16_FLOAT 0x115 #define BRW_SURFACEFORMAT_L16_FLOAT 0x116 #define BRW_SURFACEFORMAT_A16_FLOAT 0x117 #define BRW_SURFACEFORMAT_R5G5_SNORM_B6_UNORM 0x119 #define BRW_SURFACEFORMAT_B5G5R5X1_UNORM 0x11A #define BRW_SURFACEFORMAT_B5G5R5X1_UNORM_SRGB 0x11B #define BRW_SURFACEFORMAT_R8G8_SSCALED 0x11C #define BRW_SURFACEFORMAT_R8G8_USCALED 0x11D #define BRW_SURFACEFORMAT_R16_SSCALED 0x11E #define BRW_SURFACEFORMAT_R16_USCALED 0x11F #define BRW_SURFACEFORMAT_R8_UNORM 0x140 #define BRW_SURFACEFORMAT_R8_SNORM 0x141 #define BRW_SURFACEFORMAT_R8_SINT 0x142 #define BRW_SURFACEFORMAT_R8_UINT 0x143 #define BRW_SURFACEFORMAT_A8_UNORM 0x144 #define BRW_SURFACEFORMAT_I8_UNORM 0x145 #define BRW_SURFACEFORMAT_L8_UNORM 0x146 #define BRW_SURFACEFORMAT_P4A4_UNORM 0x147 #define BRW_SURFACEFORMAT_A4P4_UNORM 0x148 #define BRW_SURFACEFORMAT_R8_SSCALED 0x149 #define BRW_SURFACEFORMAT_R8_USCALED 0x14A #define BRW_SURFACEFORMAT_R1_UINT 0x181 #define BRW_SURFACEFORMAT_YCRCB_NORMAL 0x182 #define BRW_SURFACEFORMAT_YCRCB_SWAPUVY 0x183 #define BRW_SURFACEFORMAT_BC1_UNORM 0x186 #define BRW_SURFACEFORMAT_BC2_UNORM 0x187 #define BRW_SURFACEFORMAT_BC3_UNORM 0x188 #define BRW_SURFACEFORMAT_BC4_UNORM 0x189 #define BRW_SURFACEFORMAT_BC5_UNORM 0x18A #define BRW_SURFACEFORMAT_BC1_UNORM_SRGB 0x18B #define BRW_SURFACEFORMAT_BC2_UNORM_SRGB 0x18C #define BRW_SURFACEFORMAT_BC3_UNORM_SRGB 0x18D #define BRW_SURFACEFORMAT_MONO8 0x18E #define BRW_SURFACEFORMAT_YCRCB_SWAPUV 0x18F #define BRW_SURFACEFORMAT_YCRCB_SWAPY 0x190 #define BRW_SURFACEFORMAT_DXT1_RGB 0x191 #define BRW_SURFACEFORMAT_FXT1 0x192 #define BRW_SURFACEFORMAT_R8G8B8_UNORM 0x193 #define BRW_SURFACEFORMAT_R8G8B8_SNORM 0x194 #define BRW_SURFACEFORMAT_R8G8B8_SSCALED 0x195 #define BRW_SURFACEFORMAT_R8G8B8_USCALED 0x196 #define BRW_SURFACEFORMAT_R64G64B64A64_FLOAT 0x197 #define BRW_SURFACEFORMAT_R64G64B64_FLOAT 0x198 #define BRW_SURFACEFORMAT_BC4_SNORM 0x199 #define BRW_SURFACEFORMAT_BC5_SNORM 0x19A #define BRW_SURFACEFORMAT_R16G16B16_UNORM 0x19C #define BRW_SURFACEFORMAT_R16G16B16_SNORM 0x19D #define BRW_SURFACEFORMAT_R16G16B16_SSCALED 0x19E #define BRW_SURFACEFORMAT_R16G16B16_USCALED 0x19F #define BRW_SURFACERETURNFORMAT_FLOAT32 0 #define BRW_SURFACERETURNFORMAT_S1 1 #define BRW_SURFACE_1D 0 #define BRW_SURFACE_2D 1 #define BRW_SURFACE_3D 2 #define BRW_SURFACE_CUBE 3 #define BRW_SURFACE_BUFFER 4 #define BRW_SURFACE_NULL 7 #define BRW_BORDER_COLOR_MODE_DEFAULT 0 #define BRW_BORDER_COLOR_MODE_LEGACY 1 #define BRW_TEXCOORDMODE_WRAP 0 #define BRW_TEXCOORDMODE_MIRROR 1 #define BRW_TEXCOORDMODE_CLAMP 2 #define BRW_TEXCOORDMODE_CUBE 3 #define BRW_TEXCOORDMODE_CLAMP_BORDER 4 #define BRW_TEXCOORDMODE_MIRROR_ONCE 5 #define BRW_THREAD_PRIORITY_NORMAL 0 #define BRW_THREAD_PRIORITY_HIGH 1 #define BRW_TILEWALK_XMAJOR 0 #define BRW_TILEWALK_YMAJOR 1 #define BRW_VERTEX_SUBPIXEL_PRECISION_8BITS 0 #define BRW_VERTEX_SUBPIXEL_PRECISION_4BITS 1 #define BRW_VERTEXBUFFER_ACCESS_VERTEXDATA 0 #define BRW_VERTEXBUFFER_ACCESS_INSTANCEDATA 1 #define BRW_VFCOMPONENT_NOSTORE 0 #define BRW_VFCOMPONENT_STORE_SRC 1 #define BRW_VFCOMPONENT_STORE_0 2 #define BRW_VFCOMPONENT_STORE_1_FLT 3 #define BRW_VFCOMPONENT_STORE_1_INT 4 #define BRW_VFCOMPONENT_STORE_VID 5 #define BRW_VFCOMPONENT_STORE_IID 6 #define BRW_VFCOMPONENT_STORE_PID 7 /* Execution Unit (EU) defines */ #define BRW_ALIGN_1 0 #define BRW_ALIGN_16 1 #define BRW_ADDRESS_DIRECT 0 #define BRW_ADDRESS_REGISTER_INDIRECT_REGISTER 1 #define BRW_CHANNEL_X 0 #define BRW_CHANNEL_Y 1 #define BRW_CHANNEL_Z 2 #define BRW_CHANNEL_W 3 #define BRW_COMPRESSION_NONE 0 #define BRW_COMPRESSION_2NDHALF 1 #define BRW_COMPRESSION_COMPRESSED 2 #define BRW_CONDITIONAL_NONE 0 #define BRW_CONDITIONAL_Z 1 #define BRW_CONDITIONAL_NZ 2 #define BRW_CONDITIONAL_EQ 1 /* Z */ #define BRW_CONDITIONAL_NEQ 2 /* NZ */ #define BRW_CONDITIONAL_G 3 #define BRW_CONDITIONAL_GE 4 #define BRW_CONDITIONAL_L 5 #define BRW_CONDITIONAL_LE 6 #define BRW_CONDITIONAL_C 7 #define BRW_CONDITIONAL_O 8 #define BRW_DEBUG_NONE 0 #define BRW_DEBUG_BREAKPOINT 1 #define BRW_DEPENDENCY_NORMAL 0 #define BRW_DEPENDENCY_NOTCLEARED 1 #define BRW_DEPENDENCY_NOTCHECKED 2 #define BRW_DEPENDENCY_DISABLE 3 #define BRW_EXECUTE_1 0 #define BRW_EXECUTE_2 1 #define BRW_EXECUTE_4 2 #define BRW_EXECUTE_8 3 #define BRW_EXECUTE_16 4 #define BRW_EXECUTE_32 5 #define BRW_HORIZONTAL_STRIDE_0 0 #define BRW_HORIZONTAL_STRIDE_1 1 #define BRW_HORIZONTAL_STRIDE_2 2 #define BRW_HORIZONTAL_STRIDE_4 3 #define BRW_INSTRUCTION_NORMAL 0 #define BRW_INSTRUCTION_SATURATE 1 #define BRW_MASK_ENABLE 0 #define BRW_MASK_DISABLE 1 #define BRW_OPCODE_MOV 1 #define BRW_OPCODE_SEL 2 #define BRW_OPCODE_NOT 4 #define BRW_OPCODE_AND 5 #define BRW_OPCODE_OR 6 #define BRW_OPCODE_XOR 7 #define BRW_OPCODE_SHR 8 #define BRW_OPCODE_SHL 9 #define BRW_OPCODE_RSR 10 #define BRW_OPCODE_RSL 11 #define BRW_OPCODE_ASR 12 #define BRW_OPCODE_CMP 16 #define BRW_OPCODE_JMPI 32 #define BRW_OPCODE_IF 34 #define BRW_OPCODE_IFF 35 #define BRW_OPCODE_ELSE 36 #define BRW_OPCODE_ENDIF 37 #define BRW_OPCODE_DO 38 #define BRW_OPCODE_WHILE 39 #define BRW_OPCODE_BREAK 40 #define BRW_OPCODE_CONTINUE 41 #define BRW_OPCODE_HALT 42 #define BRW_OPCODE_MSAVE 44 #define BRW_OPCODE_MRESTORE 45 #define BRW_OPCODE_PUSH 46 #define BRW_OPCODE_POP 47 #define BRW_OPCODE_WAIT 48 #define BRW_OPCODE_SEND 49 #define BRW_OPCODE_ADD 64 #define BRW_OPCODE_MUL 65 #define BRW_OPCODE_AVG 66 #define BRW_OPCODE_FRC 67 #define BRW_OPCODE_RNDU 68 #define BRW_OPCODE_RNDD 69 #define BRW_OPCODE_RNDE 70 #define BRW_OPCODE_RNDZ 71 #define BRW_OPCODE_MAC 72 #define BRW_OPCODE_MACH 73 #define BRW_OPCODE_LZD 74 #define BRW_OPCODE_SAD2 80 #define BRW_OPCODE_SADA2 81 #define BRW_OPCODE_DP4 84 #define BRW_OPCODE_DPH 85 #define BRW_OPCODE_DP3 86 #define BRW_OPCODE_DP2 87 #define BRW_OPCODE_DPA2 88 #define BRW_OPCODE_LINE 89 #define BRW_OPCODE_NOP 126 #define BRW_PREDICATE_NONE 0 #define BRW_PREDICATE_NORMAL 1 #define BRW_PREDICATE_ALIGN1_ANYV 2 #define BRW_PREDICATE_ALIGN1_ALLV 3 #define BRW_PREDICATE_ALIGN1_ANY2H 4 #define BRW_PREDICATE_ALIGN1_ALL2H 5 #define BRW_PREDICATE_ALIGN1_ANY4H 6 #define BRW_PREDICATE_ALIGN1_ALL4H 7 #define BRW_PREDICATE_ALIGN1_ANY8H 8 #define BRW_PREDICATE_ALIGN1_ALL8H 9 #define BRW_PREDICATE_ALIGN1_ANY16H 10 #define BRW_PREDICATE_ALIGN1_ALL16H 11 #define BRW_PREDICATE_ALIGN16_REPLICATE_X 2 #define BRW_PREDICATE_ALIGN16_REPLICATE_Y 3 #define BRW_PREDICATE_ALIGN16_REPLICATE_Z 4 #define BRW_PREDICATE_ALIGN16_REPLICATE_W 5 #define BRW_PREDICATE_ALIGN16_ANY4H 6 #define BRW_PREDICATE_ALIGN16_ALL4H 7 #define BRW_ARCHITECTURE_REGISTER_FILE 0 #define BRW_GENERAL_REGISTER_FILE 1 #define BRW_MESSAGE_REGISTER_FILE 2 #define BRW_IMMEDIATE_VALUE 3 #define BRW_REGISTER_TYPE_UD 0 #define BRW_REGISTER_TYPE_D 1 #define BRW_REGISTER_TYPE_UW 2 #define BRW_REGISTER_TYPE_W 3 #define BRW_REGISTER_TYPE_UB 4 #define BRW_REGISTER_TYPE_B 5 #define BRW_REGISTER_TYPE_VF 5 /* packed float vector, immediates only? */ #define BRW_REGISTER_TYPE_HF 6 #define BRW_REGISTER_TYPE_V 6 /* packed int vector, immediates only, uword dest only */ #define BRW_REGISTER_TYPE_F 7 #define BRW_ARF_NULL 0x00 #define BRW_ARF_ADDRESS 0x10 #define BRW_ARF_ACCUMULATOR 0x20 #define BRW_ARF_FLAG 0x30 #define BRW_ARF_MASK 0x40 #define BRW_ARF_MASK_STACK 0x50 #define BRW_ARF_MASK_STACK_DEPTH 0x60 #define BRW_ARF_STATE 0x70 #define BRW_ARF_CONTROL 0x80 #define BRW_ARF_NOTIFICATION_COUNT 0x90 #define BRW_ARF_IP 0xA0 #define BRW_AMASK 0 #define BRW_IMASK 1 #define BRW_LMASK 2 #define BRW_CMASK 3 #define BRW_THREAD_NORMAL 0 #define BRW_THREAD_ATOMIC 1 #define BRW_THREAD_SWITCH 2 #define BRW_VERTICAL_STRIDE_0 0 #define BRW_VERTICAL_STRIDE_1 1 #define BRW_VERTICAL_STRIDE_2 2 #define BRW_VERTICAL_STRIDE_4 3 #define BRW_VERTICAL_STRIDE_8 4 #define BRW_VERTICAL_STRIDE_16 5 #define BRW_VERTICAL_STRIDE_32 6 #define BRW_VERTICAL_STRIDE_64 7 #define BRW_VERTICAL_STRIDE_128 8 #define BRW_VERTICAL_STRIDE_256 9 #define BRW_VERTICAL_STRIDE_ONE_DIMENSIONAL 0xF #define BRW_WIDTH_1 0 #define BRW_WIDTH_2 1 #define BRW_WIDTH_4 2 #define BRW_WIDTH_8 3 #define BRW_WIDTH_16 4 #define BRW_STATELESS_BUFFER_BOUNDARY_1K 0 #define BRW_STATELESS_BUFFER_BOUNDARY_2K 1 #define BRW_STATELESS_BUFFER_BOUNDARY_4K 2 #define BRW_STATELESS_BUFFER_BOUNDARY_8K 3 #define BRW_STATELESS_BUFFER_BOUNDARY_16K 4 #define BRW_STATELESS_BUFFER_BOUNDARY_32K 5 #define BRW_STATELESS_BUFFER_BOUNDARY_64K 6 #define BRW_STATELESS_BUFFER_BOUNDARY_128K 7 #define BRW_STATELESS_BUFFER_BOUNDARY_256K 8 #define BRW_STATELESS_BUFFER_BOUNDARY_512K 9 #define BRW_STATELESS_BUFFER_BOUNDARY_1M 10 #define BRW_STATELESS_BUFFER_BOUNDARY_2M 11 #define BRW_POLYGON_FACING_FRONT 0 #define BRW_POLYGON_FACING_BACK 1 #define BRW_MESSAGE_TARGET_NULL 0 #define BRW_MESSAGE_TARGET_MATH 1 #define BRW_MESSAGE_TARGET_SAMPLER 2 #define BRW_MESSAGE_TARGET_GATEWAY 3 #define BRW_MESSAGE_TARGET_DATAPORT_READ 4 #define BRW_MESSAGE_TARGET_DATAPORT_WRITE 5 #define BRW_MESSAGE_TARGET_URB 6 #define BRW_MESSAGE_TARGET_THREAD_SPAWNER 7 #define BRW_SAMPLER_RETURN_FORMAT_FLOAT32 0 #define BRW_SAMPLER_RETURN_FORMAT_UINT32 2 #define BRW_SAMPLER_RETURN_FORMAT_SINT32 3 #define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE 0 #define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE 0 #define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_BIAS 0 #define BRW_SAMPLER_MESSAGE_SIMD8_KILLPIX 1 #define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_LOD 1 #define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_LOD 1 #define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_GRADIENTS 2 #define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE_GRADIENTS 2 #define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_COMPARE 0 #define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_COMPARE 2 #define BRW_SAMPLER_MESSAGE_SIMD4X2_RESINFO 2 #define BRW_SAMPLER_MESSAGE_SIMD8_RESINFO 2 #define BRW_SAMPLER_MESSAGE_SIMD16_RESINFO 2 #define BRW_SAMPLER_MESSAGE_SIMD4X2_LD 3 #define BRW_SAMPLER_MESSAGE_SIMD8_LD 3 #define BRW_SAMPLER_MESSAGE_SIMD16_LD 3 #define BRW_DATAPORT_OWORD_BLOCK_1_OWORDLOW 0 #define BRW_DATAPORT_OWORD_BLOCK_1_OWORDHIGH 1 #define BRW_DATAPORT_OWORD_BLOCK_2_OWORDS 2 #define BRW_DATAPORT_OWORD_BLOCK_4_OWORDS 3 #define BRW_DATAPORT_OWORD_BLOCK_8_OWORDS 4 #define BRW_DATAPORT_OWORD_DUAL_BLOCK_1OWORD 0 #define BRW_DATAPORT_OWORD_DUAL_BLOCK_4OWORDS 2 #define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_8DWORDS 2 #define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_16DWORDS 3 #define BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ 0 #define BRW_DATAPORT_READ_MESSAGE_OWORD_DUAL_BLOCK_READ 1 #define BRW_DATAPORT_READ_MESSAGE_DWORD_BLOCK_READ 2 #define BRW_DATAPORT_READ_MESSAGE_DWORD_SCATTERED_READ 3 #define BRW_DATAPORT_READ_TARGET_DATA_CACHE 0 #define BRW_DATAPORT_READ_TARGET_RENDER_CACHE 1 #define BRW_DATAPORT_READ_TARGET_SAMPLER_CACHE 2 #define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE 0 #define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED 1 #define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN01 2 #define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN23 3 #define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_SINGLE_SOURCE_SUBSPAN01 4 #define BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE 0 #define BRW_DATAPORT_WRITE_MESSAGE_OWORD_DUAL_BLOCK_WRITE 1 #define BRW_DATAPORT_WRITE_MESSAGE_DWORD_BLOCK_WRITE 2 #define BRW_DATAPORT_WRITE_MESSAGE_DWORD_SCATTERED_WRITE 3 #define BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE 4 #define BRW_DATAPORT_WRITE_MESSAGE_STREAMED_VERTEX_BUFFER_WRITE 5 #define BRW_DATAPORT_WRITE_MESSAGE_FLUSH_RENDER_CACHE 7 #define BRW_MATH_FUNCTION_INV 1 #define BRW_MATH_FUNCTION_LOG 2 #define BRW_MATH_FUNCTION_EXP 3 #define BRW_MATH_FUNCTION_SQRT 4 #define BRW_MATH_FUNCTION_RSQ 5 #define BRW_MATH_FUNCTION_SIN 6 /* was 7 */ #define BRW_MATH_FUNCTION_COS 7 /* was 8 */ #define BRW_MATH_FUNCTION_SINCOS 8 /* was 6 */ #define BRW_MATH_FUNCTION_TAN 9 #define BRW_MATH_FUNCTION_POW 10 #define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT_AND_REMAINDER 11 #define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT 12 #define BRW_MATH_FUNCTION_INT_DIV_REMAINDER 13 #define BRW_MATH_INTEGER_UNSIGNED 0 #define BRW_MATH_INTEGER_SIGNED 1 #define BRW_MATH_PRECISION_FULL 0 #define BRW_MATH_PRECISION_PARTIAL 1 #define BRW_MATH_SATURATE_NONE 0 #define BRW_MATH_SATURATE_SATURATE 1 #define BRW_MATH_DATA_VECTOR 0 #define BRW_MATH_DATA_SCALAR 1 #define BRW_URB_OPCODE_WRITE 0 #define BRW_URB_SWIZZLE_NONE 0 #define BRW_URB_SWIZZLE_INTERLEAVE 1 #define BRW_URB_SWIZZLE_TRANSPOSE 2 #define BRW_SCRATCH_SPACE_SIZE_1K 0 #define BRW_SCRATCH_SPACE_SIZE_2K 1 #define BRW_SCRATCH_SPACE_SIZE_4K 2 #define BRW_SCRATCH_SPACE_SIZE_8K 3 #define BRW_SCRATCH_SPACE_SIZE_16K 4 #define BRW_SCRATCH_SPACE_SIZE_32K 5 #define BRW_SCRATCH_SPACE_SIZE_64K 6 #define BRW_SCRATCH_SPACE_SIZE_128K 7 #define BRW_SCRATCH_SPACE_SIZE_256K 8 #define BRW_SCRATCH_SPACE_SIZE_512K 9 #define BRW_SCRATCH_SPACE_SIZE_1M 10 #define BRW_SCRATCH_SPACE_SIZE_2M 11 #define CMD_URB_FENCE 0x6000 #define CMD_CONST_BUFFER_STATE 0x6001 #define CMD_CONST_BUFFER 0x6002 #define CMD_STATE_BASE_ADDRESS 0x6101 #define CMD_STATE_INSN_POINTER 0x6102 #define CMD_PIPELINE_SELECT 0x6104 #define CMD_PIPELINED_STATE_POINTERS 0x7800 #define CMD_BINDING_TABLE_PTRS 0x7801 #define CMD_VERTEX_BUFFER 0x7808 #define CMD_VERTEX_ELEMENT 0x7809 #define CMD_INDEX_BUFFER 0x780a #define CMD_VF_STATISTICS 0x780b #define CMD_DRAW_RECT 0x7900 #define CMD_BLEND_CONSTANT_COLOR 0x7901 #define CMD_CHROMA_KEY 0x7904 #define CMD_DEPTH_BUFFER 0x7905 #define CMD_POLY_STIPPLE_OFFSET 0x7906 #define CMD_POLY_STIPPLE_PATTERN 0x7907 #define CMD_LINE_STIPPLE_PATTERN 0x7908 #define CMD_GLOBAL_DEPTH_OFFSET_CLAMP 0x7908 #define CMD_PIPE_CONTROL 0x7a00 #define CMD_3D_PRIM 0x7b00 #define CMD_MI_FLUSH 0x0200 /* Various values from the R0 vertex header: */ #define R02_PRIM_END 0x1 #define R02_PRIM_START 0x2 /* media pipeline */ #define BRW_VFE_MODE_GENERIC 0x0 #define BRW_VFE_MODE_VLD_MPEG2 0x1 #define BRW_VFE_MODE_IS 0x2 #define BRW_VFE_MODE_AVC_MC 0x4 #define BRW_VFE_MODE_AVC_IT 0x7 #define BRW_VFE_MODE_VC1_IT 0xB #define BRW_VFE_DEBUG_COUNTER_FREE 0 #define BRW_VFE_DEBUG_COUNTER_FROZEN 1 #define BRW_VFE_DEBUG_COUNTER_ONCE 2 #define BRW_VFE_DEBUG_COUNTER_ALWAYS 3 /* VLD_STATE */ #define BRW_MPEG_TOP_FIELD 1 #define BRW_MPEG_BOTTOM_FIELD 2 #define BRW_MPEG_FRAME 3 #define BRW_MPEG_QSCALE_LINEAR 0 #define BRW_MPEG_QSCALE_NONLINEAR 1 #define BRW_MPEG_ZIGZAG_SCAN 0 #define BRW_MPEG_ALTER_VERTICAL_SCAN 1 #define BRW_MPEG_I_PICTURE 1 #define BRW_MPEG_P_PICTURE 2 #define BRW_MPEG_B_PICTURE 3 #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-eu-emit.c000066400000000000000000001003051271037650300303460ustar00rootroot00000000000000/* Copyright (C) Intel Corp. 2006. All Rights Reserved. Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to develop this 3D driver. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **********************************************************************/ /* * Authors: * Keith Whitwell */ #include "cairoint.h" #include "cairo-drm-intel-brw-eu.h" #include /*********************************************************************** * Internal helper for constructing instructions */ static void guess_execution_size( struct brw_instruction *insn, struct brw_reg reg ) { if (reg.width == BRW_WIDTH_8 && insn->header.compression_control == BRW_COMPRESSION_COMPRESSED) insn->header.execution_size = BRW_EXECUTE_16; else insn->header.execution_size = reg.width; /* note - definitions are compatible */ } void brw_instruction_set_destination (struct brw_instruction *insn, struct brw_reg dest) { insn->bits1.da1.dest_reg_file = dest.file; insn->bits1.da1.dest_reg_type = dest.type; insn->bits1.da1.dest_address_mode = dest.address_mode; if (dest.address_mode == BRW_ADDRESS_DIRECT) { insn->bits1.da1.dest_reg_nr = dest.nr; if (insn->header.access_mode == BRW_ALIGN_1) { insn->bits1.da1.dest_subreg_nr = dest.subnr; if (dest.hstride == BRW_HORIZONTAL_STRIDE_0) dest.hstride = BRW_HORIZONTAL_STRIDE_1; insn->bits1.da1.dest_horiz_stride = dest.hstride; } else { insn->bits1.da16.dest_subreg_nr = dest.subnr / 16; insn->bits1.da16.dest_writemask = dest.dw1.bits.writemask; } } else { insn->bits1.ia1.dest_subreg_nr = dest.subnr; /* These are different sizes in align1 vs align16: */ if (insn->header.access_mode == BRW_ALIGN_1) { insn->bits1.ia1.dest_indirect_offset = dest.dw1.bits.indirect_offset; if (dest.hstride == BRW_HORIZONTAL_STRIDE_0) dest.hstride = BRW_HORIZONTAL_STRIDE_1; insn->bits1.ia1.dest_horiz_stride = dest.hstride; } else { insn->bits1.ia16.dest_indirect_offset = dest.dw1.bits.indirect_offset; } } /* NEW: Set the execution size based on dest.width and * insn->compression_control: */ guess_execution_size(insn, dest); } void brw_instruction_set_source0 (struct brw_instruction *insn, struct brw_reg reg) { assert(reg.file != BRW_MESSAGE_REGISTER_FILE); insn->bits1.da1.src0_reg_file = reg.file; insn->bits1.da1.src0_reg_type = reg.type; insn->bits2.da1.src0_abs = reg.abs; insn->bits2.da1.src0_negate = reg.negate; insn->bits2.da1.src0_address_mode = reg.address_mode; if (reg.file == BRW_IMMEDIATE_VALUE) { insn->bits3.ud = reg.dw1.ud; /* Required to set some fields in src1 as well: */ insn->bits1.da1.src1_reg_file = 0; /* arf */ insn->bits1.da1.src1_reg_type = reg.type; } else { if (reg.address_mode == BRW_ADDRESS_DIRECT) { if (insn->header.access_mode == BRW_ALIGN_1) { insn->bits2.da1.src0_subreg_nr = reg.subnr; insn->bits2.da1.src0_reg_nr = reg.nr; } else { insn->bits2.da16.src0_subreg_nr = reg.subnr / 16; insn->bits2.da16.src0_reg_nr = reg.nr; } } else { insn->bits2.ia1.src0_subreg_nr = reg.subnr; if (insn->header.access_mode == BRW_ALIGN_1) { insn->bits2.ia1.src0_indirect_offset = reg.dw1.bits.indirect_offset; } else { insn->bits2.ia16.src0_subreg_nr = reg.dw1.bits.indirect_offset; } } if (insn->header.access_mode == BRW_ALIGN_1) { if (reg.width == BRW_WIDTH_1 && insn->header.execution_size == BRW_EXECUTE_1) { insn->bits2.da1.src0_horiz_stride = BRW_HORIZONTAL_STRIDE_0; insn->bits2.da1.src0_width = BRW_WIDTH_1; insn->bits2.da1.src0_vert_stride = BRW_VERTICAL_STRIDE_0; } else { insn->bits2.da1.src0_horiz_stride = reg.hstride; insn->bits2.da1.src0_width = reg.width; insn->bits2.da1.src0_vert_stride = reg.vstride; } } else { insn->bits2.da16.src0_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X); insn->bits2.da16.src0_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y); insn->bits2.da16.src0_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z); insn->bits2.da16.src0_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W); /* This is an oddity of the fact we're using the same * descriptions for registers in align_16 as align_1: */ if (reg.vstride == BRW_VERTICAL_STRIDE_8) insn->bits2.da16.src0_vert_stride = BRW_VERTICAL_STRIDE_4; else insn->bits2.da16.src0_vert_stride = reg.vstride; } } } void brw_set_src1( struct brw_instruction *insn, struct brw_reg reg ) { assert(reg.file != BRW_MESSAGE_REGISTER_FILE); insn->bits1.da1.src1_reg_file = reg.file; insn->bits1.da1.src1_reg_type = reg.type; insn->bits3.da1.src1_abs = reg.abs; insn->bits3.da1.src1_negate = reg.negate; /* Only src1 can be immediate in two-argument instructions. */ assert(insn->bits1.da1.src0_reg_file != BRW_IMMEDIATE_VALUE); if (reg.file == BRW_IMMEDIATE_VALUE) { insn->bits3.ud = reg.dw1.ud; } else { /* This is a hardware restriction, which may or may not be lifted * in the future: */ assert (reg.address_mode == BRW_ADDRESS_DIRECT); //assert (reg.file == BRW_GENERAL_REGISTER_FILE); if (insn->header.access_mode == BRW_ALIGN_1) { insn->bits3.da1.src1_subreg_nr = reg.subnr; insn->bits3.da1.src1_reg_nr = reg.nr; } else { insn->bits3.da16.src1_subreg_nr = reg.subnr / 16; insn->bits3.da16.src1_reg_nr = reg.nr; } if (insn->header.access_mode == BRW_ALIGN_1) { if (reg.width == BRW_WIDTH_1 && insn->header.execution_size == BRW_EXECUTE_1) { insn->bits3.da1.src1_horiz_stride = BRW_HORIZONTAL_STRIDE_0; insn->bits3.da1.src1_width = BRW_WIDTH_1; insn->bits3.da1.src1_vert_stride = BRW_VERTICAL_STRIDE_0; } else { insn->bits3.da1.src1_horiz_stride = reg.hstride; insn->bits3.da1.src1_width = reg.width; insn->bits3.da1.src1_vert_stride = reg.vstride; } } else { insn->bits3.da16.src1_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X); insn->bits3.da16.src1_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y); insn->bits3.da16.src1_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z); insn->bits3.da16.src1_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W); /* This is an oddity of the fact we're using the same * descriptions for registers in align_16 as align_1: */ if (reg.vstride == BRW_VERTICAL_STRIDE_8) insn->bits3.da16.src1_vert_stride = BRW_VERTICAL_STRIDE_4; else insn->bits3.da16.src1_vert_stride = reg.vstride; } } } static void brw_set_math_message( struct brw_instruction *insn, uint32_t msg_length, uint32_t response_length, uint32_t function, uint32_t integer_type, int low_precision, int saturate, uint32_t dataType ) { brw_set_src1 (insn, brw_imm_d (0)); insn->bits3.math.function = function; insn->bits3.math.int_type = integer_type; insn->bits3.math.precision = low_precision; insn->bits3.math.saturate = saturate; insn->bits3.math.data_type = dataType; insn->bits3.math.response_length = response_length; insn->bits3.math.msg_length = msg_length; insn->bits3.math.msg_target = BRW_MESSAGE_TARGET_MATH; insn->bits3.math.end_of_thread = 0; } static void brw_set_urb_message( struct brw_instruction *insn, int allocate, int used, uint32_t msg_length, uint32_t response_length, int end_of_thread, int complete, uint32_t offset, uint32_t swizzle_control ) { brw_set_src1 (insn, brw_imm_d (0)); insn->bits3.urb.opcode = 0; /* ? */ insn->bits3.urb.offset = offset; insn->bits3.urb.swizzle_control = swizzle_control; insn->bits3.urb.allocate = allocate; insn->bits3.urb.used = used; /* ? */ insn->bits3.urb.complete = complete; insn->bits3.urb.response_length = response_length; insn->bits3.urb.msg_length = msg_length; insn->bits3.urb.msg_target = BRW_MESSAGE_TARGET_URB; insn->bits3.urb.end_of_thread = end_of_thread; } void brw_instruction_set_dp_write_message (struct brw_instruction *insn, uint32_t binding_table_index, uint32_t msg_control, uint32_t msg_type, uint32_t msg_length, uint32_t pixel_scoreboard_clear, uint32_t response_length, uint32_t end_of_thread) { brw_set_src1 (insn, brw_imm_d (0)); insn->bits3.dp_write.binding_table_index = binding_table_index; insn->bits3.dp_write.msg_control = msg_control; insn->bits3.dp_write.pixel_scoreboard_clear = pixel_scoreboard_clear; insn->bits3.dp_write.msg_type = msg_type; insn->bits3.dp_write.send_commit_msg = 0; insn->bits3.dp_write.response_length = response_length; insn->bits3.dp_write.msg_length = msg_length; insn->bits3.dp_write.msg_target = BRW_MESSAGE_TARGET_DATAPORT_WRITE; insn->bits3.urb.end_of_thread = end_of_thread; } static void brw_set_dp_read_message( struct brw_instruction *insn, uint32_t binding_table_index, uint32_t msg_control, uint32_t msg_type, uint32_t target_cache, uint32_t msg_length, uint32_t response_length, uint32_t end_of_thread ) { brw_set_src1 (insn, brw_imm_d (0)); insn->bits3.dp_read.binding_table_index = binding_table_index; insn->bits3.dp_read.msg_control = msg_control; insn->bits3.dp_read.msg_type = msg_type; insn->bits3.dp_read.target_cache = target_cache; insn->bits3.dp_read.response_length = response_length; insn->bits3.dp_read.msg_length = msg_length; insn->bits3.dp_read.msg_target = BRW_MESSAGE_TARGET_DATAPORT_READ; insn->bits3.dp_read.end_of_thread = end_of_thread; } static void brw_set_sampler_message (struct brw_instruction *insn, cairo_bool_t is_g4x, uint32_t binding_table_index, uint32_t sampler, uint32_t msg_type, uint32_t response_length, uint32_t msg_length, cairo_bool_t eot) { brw_set_src1 (insn, brw_imm_d (0)); if (is_g4x) { /* XXX presume the driver is sane! */ insn->bits3.sampler_g4x.binding_table_index = binding_table_index; insn->bits3.sampler_g4x.sampler = sampler; insn->bits3.sampler_g4x.msg_type = msg_type; insn->bits3.sampler_g4x.response_length = response_length; insn->bits3.sampler_g4x.msg_length = msg_length; insn->bits3.sampler_g4x.end_of_thread = eot; insn->bits3.sampler_g4x.msg_target = BRW_MESSAGE_TARGET_SAMPLER; } else { insn->bits3.sampler.binding_table_index = binding_table_index; insn->bits3.sampler.sampler = sampler; insn->bits3.sampler.msg_type = msg_type; insn->bits3.sampler.return_format = BRW_SAMPLER_RETURN_FORMAT_FLOAT32; insn->bits3.sampler.response_length = response_length; insn->bits3.sampler.msg_length = msg_length; insn->bits3.sampler.end_of_thread = eot; insn->bits3.sampler.msg_target = BRW_MESSAGE_TARGET_SAMPLER; } } struct brw_instruction * brw_next_instruction (struct brw_compile *p, uint32_t opcode) { struct brw_instruction *insn; assert(p->nr_insn + 1 < BRW_EU_MAX_INSN); insn = &p->store[p->nr_insn++]; memcpy(insn, p->current, sizeof(*insn)); /* Reset this one-shot flag: */ if (p->current->header.destreg__conditonalmod) { p->current->header.destreg__conditonalmod = 0; p->current->header.predicate_control = BRW_PREDICATE_NORMAL; } insn->header.opcode = opcode; return insn; } static struct brw_instruction *brw_alu1( struct brw_compile *p, uint32_t opcode, struct brw_reg dest, struct brw_reg src ) { struct brw_instruction *insn = brw_next_instruction(p, opcode); brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src); return insn; } static struct brw_instruction *brw_alu2(struct brw_compile *p, uint32_t opcode, struct brw_reg dest, struct brw_reg src0, struct brw_reg src1 ) { struct brw_instruction *insn = brw_next_instruction(p, opcode); brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src0); brw_set_src1(insn, src1); return insn; } /*********************************************************************** * Convenience routines. */ #define ALU1(OP) \ struct brw_instruction *brw_##OP(struct brw_compile *p, \ struct brw_reg dest, \ struct brw_reg src0) \ { \ return brw_alu1(p, BRW_OPCODE_##OP, dest, src0); \ } #define ALU2(OP) \ struct brw_instruction *brw_##OP(struct brw_compile *p, \ struct brw_reg dest, \ struct brw_reg src0, \ struct brw_reg src1) \ { \ return brw_alu2(p, BRW_OPCODE_##OP, dest, src0, src1); \ } ALU1(MOV) ALU2(SEL) ALU1(NOT) ALU2(AND) ALU2(OR) ALU2(XOR) ALU2(SHR) ALU2(SHL) ALU2(RSR) ALU2(RSL) ALU2(ASR) ALU2(ADD) ALU2(MUL) ALU1(FRC) ALU1(RNDD) ALU1(RNDZ) ALU2(MAC) ALU2(MACH) ALU1(LZD) ALU2(DP4) ALU2(DPH) ALU2(DP3) ALU2(DP2) ALU2(LINE) void brw_NOP(struct brw_compile *p) { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_NOP); brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); brw_set_src1(insn, brw_imm_ud(0x0)); } /*********************************************************************** * Comparisons, if/else/endif */ struct brw_instruction *brw_JMPI(struct brw_compile *p, struct brw_reg dest, struct brw_reg src0, struct brw_reg src1) { struct brw_instruction *insn = brw_alu2(p, BRW_OPCODE_JMPI, dest, src0, src1); p->current->header.predicate_control = BRW_PREDICATE_NONE; return insn; } /* EU takes the value from the flag register and pushes it onto some * sort of a stack (presumably merging with any flag value already on * the stack). Within an if block, the flags at the top of the stack * control execution on each channel of the unit, eg. on each of the * 16 pixel values in our wm programs. * * When the matching 'else' instruction is reached (presumably by * countdown of the instruction count patched in by our ELSE/ENDIF * functions), the relevant flags are inverted. * * When the matching 'endif' instruction is reached, the flags are * popped off. If the stack is now empty, normal execution resumes. * * No attempt is made to deal with stack overflow (14 elements?). */ struct brw_instruction *brw_IF(struct brw_compile *p, uint32_t execute_size) { struct brw_instruction *insn; if (p->single_program_flow) { assert(execute_size == BRW_EXECUTE_1); insn = brw_next_instruction(p, BRW_OPCODE_ADD); insn->header.predicate_inverse = 1; } else { insn = brw_next_instruction(p, BRW_OPCODE_IF); } /* Override the defaults for this instruction: */ brw_instruction_set_destination (insn, brw_ip_reg ()); brw_instruction_set_source0 (insn, brw_ip_reg ()); brw_set_src1 (insn, brw_imm_d (0)); insn->header.execution_size = execute_size; insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.predicate_control = BRW_PREDICATE_NORMAL; insn->header.mask_control = BRW_MASK_ENABLE; if (!p->single_program_flow) insn->header.thread_control = BRW_THREAD_SWITCH; p->current->header.predicate_control = BRW_PREDICATE_NONE; return insn; } struct brw_instruction *brw_ELSE(struct brw_compile *p, struct brw_instruction *if_insn) { struct brw_instruction *insn; if (p->single_program_flow) { insn = brw_next_instruction(p, BRW_OPCODE_ADD); } else { insn = brw_next_instruction(p, BRW_OPCODE_ELSE); } brw_instruction_set_destination (insn, brw_ip_reg ()); brw_instruction_set_source0 (insn, brw_ip_reg ()); brw_set_src1 (insn, brw_imm_d (0)); insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.execution_size = if_insn->header.execution_size; insn->header.mask_control = BRW_MASK_ENABLE; if (!p->single_program_flow) insn->header.thread_control = BRW_THREAD_SWITCH; /* Patch the if instruction to point at this instruction. */ if (p->single_program_flow) { assert(if_insn->header.opcode == BRW_OPCODE_ADD); if_insn->bits3.ud = (insn - if_insn + 1) * 16; } else { assert(if_insn->header.opcode == BRW_OPCODE_IF); if_insn->bits3.if_else.jump_count = insn - if_insn; if_insn->bits3.if_else.pop_count = 1; if_insn->bits3.if_else.pad0 = 0; } return insn; } void brw_ENDIF(struct brw_compile *p, struct brw_instruction *patch_insn) { if (p->single_program_flow) { /* In single program flow mode, there's no need to execute an ENDIF, * since we don't need to do any stack operations, and if we're executing * currently, we want to just continue executing. */ struct brw_instruction *next = &p->store[p->nr_insn]; assert(patch_insn->header.opcode == BRW_OPCODE_ADD); patch_insn->bits3.ud = (next - patch_insn) * 16; } else { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_ENDIF); brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); brw_set_src1 (insn, brw_imm_d (0)); insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.execution_size = patch_insn->header.execution_size; insn->header.mask_control = BRW_MASK_ENABLE; insn->header.thread_control = BRW_THREAD_SWITCH; assert(patch_insn->bits3.if_else.jump_count == 0); /* Patch the if or else instructions to point at this or the next * instruction respectively. */ if (patch_insn->header.opcode == BRW_OPCODE_IF) { /* Automagically turn it into an IFF: */ patch_insn->header.opcode = BRW_OPCODE_IFF; patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1; patch_insn->bits3.if_else.pop_count = 0; patch_insn->bits3.if_else.pad0 = 0; } else if (patch_insn->header.opcode == BRW_OPCODE_ELSE) { patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1; patch_insn->bits3.if_else.pop_count = 1; patch_insn->bits3.if_else.pad0 = 0; } else { assert(0); } /* Also pop item off the stack in the endif instruction: */ insn->bits3.if_else.jump_count = 0; insn->bits3.if_else.pop_count = 1; insn->bits3.if_else.pad0 = 0; } } struct brw_instruction *brw_BREAK(struct brw_compile *p) { struct brw_instruction *insn; insn = brw_next_instruction(p, BRW_OPCODE_BREAK); brw_instruction_set_destination(insn, brw_ip_reg()); brw_instruction_set_source0(insn, brw_ip_reg()); brw_set_src1(insn, brw_imm_d (0)); insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.execution_size = BRW_EXECUTE_8; /* insn->header.mask_control = BRW_MASK_DISABLE; */ insn->bits3.if_else.pad0 = 0; return insn; } struct brw_instruction *brw_CONT(struct brw_compile *p) { struct brw_instruction *insn; insn = brw_next_instruction(p, BRW_OPCODE_CONTINUE); brw_instruction_set_destination(insn, brw_ip_reg()); brw_instruction_set_source0(insn, brw_ip_reg()); brw_set_src1 (insn, brw_imm_d (0)); insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.execution_size = BRW_EXECUTE_8; /* insn->header.mask_control = BRW_MASK_DISABLE; */ insn->bits3.if_else.pad0 = 0; return insn; } /* DO/WHILE loop: */ struct brw_instruction *brw_DO(struct brw_compile *p, uint32_t execute_size) { if (p->single_program_flow) { return &p->store[p->nr_insn]; } else { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_DO); /* Override the defaults for this instruction: */ brw_instruction_set_destination(insn, brw_null_reg()); brw_instruction_set_source0(insn, brw_null_reg()); brw_set_src1(insn, brw_null_reg()); insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.execution_size = execute_size; insn->header.predicate_control = BRW_PREDICATE_NONE; /* insn->header.mask_control = BRW_MASK_ENABLE; */ /* insn->header.mask_control = BRW_MASK_DISABLE; */ return insn; } } struct brw_instruction *brw_WHILE(struct brw_compile *p, struct brw_instruction *do_insn) { struct brw_instruction *insn; if (p->single_program_flow) insn = brw_next_instruction(p, BRW_OPCODE_ADD); else insn = brw_next_instruction(p, BRW_OPCODE_WHILE); brw_instruction_set_destination(insn, brw_ip_reg()); brw_instruction_set_source0(insn, brw_ip_reg()); brw_set_src1 (insn, brw_imm_d (0)); insn->header.compression_control = BRW_COMPRESSION_NONE; if (p->single_program_flow) { insn->header.execution_size = BRW_EXECUTE_1; insn->bits3.d = (do_insn - insn) * 16; } else { insn->header.execution_size = do_insn->header.execution_size; assert(do_insn->header.opcode == BRW_OPCODE_DO); insn->bits3.if_else.jump_count = do_insn - insn + 1; insn->bits3.if_else.pop_count = 0; insn->bits3.if_else.pad0 = 0; } /* insn->header.mask_control = BRW_MASK_ENABLE; */ /* insn->header.mask_control = BRW_MASK_DISABLE; */ p->current->header.predicate_control = BRW_PREDICATE_NONE; return insn; } /* FORWARD JUMPS: */ void brw_land_fwd_jump(struct brw_compile *p, struct brw_instruction *jmp_insn) { struct brw_instruction *landing = &p->store[p->nr_insn]; assert(jmp_insn->header.opcode == BRW_OPCODE_JMPI); assert(jmp_insn->bits1.da1.src1_reg_file = BRW_IMMEDIATE_VALUE); jmp_insn->bits3.ud = (landing - jmp_insn) - 1; } /* To integrate with the above, it makes sense that the comparison * instruction should populate the flag register. It might be simpler * just to use the flag reg for most WM tasks? */ void brw_CMP(struct brw_compile *p, struct brw_reg dest, uint32_t conditional, struct brw_reg src0, struct brw_reg src1) { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_CMP); insn->header.destreg__conditonalmod = conditional; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src0); brw_set_src1(insn, src1); /* guess_execution_size(insn, src0); */ /* Make it so that future instructions will use the computed flag * value until brw_set_predicate_control_flag_value() is called * again. */ if (dest.file == BRW_ARCHITECTURE_REGISTER_FILE && dest.nr == 0) { p->current->header.predicate_control = BRW_PREDICATE_NORMAL; p->flag_value = 0xff; } } /*********************************************************************** * Helpers for the various SEND message types: */ /* Invert 8 values */ void brw_math( struct brw_compile *p, struct brw_reg dest, uint32_t function, uint32_t saturate, uint32_t msg_reg_nr, struct brw_reg src, uint32_t data_type, uint32_t precision ) { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1; uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1; /* Example code doesn't set predicate_control for send * instructions. */ insn->header.predicate_control = 0; insn->header.destreg__conditonalmod = msg_reg_nr; response_length = 1; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src); brw_set_math_message(insn, msg_length, response_length, function, BRW_MATH_INTEGER_UNSIGNED, precision, saturate, data_type); } /* Use 2 send instructions to invert 16 elements */ void brw_math_16( struct brw_compile *p, struct brw_reg dest, uint32_t function, uint32_t saturate, uint32_t msg_reg_nr, struct brw_reg src, uint32_t precision ) { struct brw_instruction *insn; uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1; uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1; /* First instruction: */ brw_push_insn_state(p); brw_set_predicate_control_flag_value(p, 0xff); brw_set_compression_control(p, BRW_COMPRESSION_NONE); insn = brw_next_instruction(p, BRW_OPCODE_SEND); insn->header.destreg__conditonalmod = msg_reg_nr; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src); brw_set_math_message(insn, msg_length, response_length, function, BRW_MATH_INTEGER_UNSIGNED, precision, saturate, BRW_MATH_DATA_VECTOR); /* Second instruction: */ insn = brw_next_instruction(p, BRW_OPCODE_SEND); insn->header.compression_control = BRW_COMPRESSION_2NDHALF; insn->header.destreg__conditonalmod = msg_reg_nr+1; brw_instruction_set_destination(insn, offset(dest,1)); brw_instruction_set_source0(insn, src); brw_set_math_message(insn, msg_length, response_length, function, BRW_MATH_INTEGER_UNSIGNED, precision, saturate, BRW_MATH_DATA_VECTOR); brw_pop_insn_state(p); } void brw_dp_WRITE_16( struct brw_compile *p, struct brw_reg src, uint32_t msg_reg_nr, uint32_t scratch_offset ) { { brw_push_insn_state(p); brw_set_mask_control(p, BRW_MASK_DISABLE); brw_set_compression_control(p, BRW_COMPRESSION_NONE); brw_MOV (p, retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D), brw_imm_d (scratch_offset)); brw_pop_insn_state(p); } { uint32_t msg_length = 3; struct brw_reg dest = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW); struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); insn->header.predicate_control = 0; /* XXX */ insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.destreg__conditonalmod = msg_reg_nr; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src); brw_instruction_set_dp_write_message(insn, 255, /* bti */ BRW_DATAPORT_OWORD_BLOCK_4_OWORDS, /* msg_control */ BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE, /* msg_type */ msg_length, 0, /* pixel scoreboard */ 0, /* response_length */ 0); /* eot */ } } void brw_dp_READ_16( struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, uint32_t scratch_offset ) { { brw_push_insn_state(p); brw_set_compression_control(p, BRW_COMPRESSION_NONE); brw_set_mask_control(p, BRW_MASK_DISABLE); brw_MOV (p, retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D), brw_imm_d (scratch_offset)); brw_pop_insn_state(p); } { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); insn->header.predicate_control = 0; /* XXX */ insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.destreg__conditonalmod = msg_reg_nr; brw_instruction_set_destination(insn, dest); /* UW? */ brw_instruction_set_source0(insn, retype(brw_vec8_grf(0), BRW_REGISTER_TYPE_UW)); brw_set_dp_read_message(insn, 255, /* bti */ 3, /* msg_control */ BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ, /* msg_type */ 1, /* target cache */ 1, /* msg_length */ 2, /* response_length */ 0); /* eot */ } } void brw_fb_WRITE(struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, uint32_t binding_table_index, uint32_t msg_length, uint32_t response_length, int eot) { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); insn->header.predicate_control = 0; /* XXX */ insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.destreg__conditonalmod = msg_reg_nr; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src0); brw_instruction_set_dp_write_message(insn, binding_table_index, BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE, /* msg_control */ BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */ msg_length, 1, /* pixel scoreboard */ response_length, eot); } void brw_SAMPLE (struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, uint32_t binding_table_index, uint32_t sampler, uint32_t writemask, uint32_t msg_type, uint32_t response_length, uint32_t msg_length, cairo_bool_t eot) { int need_stall = 0; if(writemask == 0) { /* printf("%s: zero writemask??\n", __FUNCTION__); */ return; } /* Hardware doesn't do destination dependency checking on send * instructions properly. Add a workaround which generates the * dependency by other means. In practice it seems like this bug * only crops up for texture samples, and only where registers are * written by the send and then written again later without being * read in between. Luckily for us, we already track that * information and use it to modify the writemask for the * instruction, so that is a guide for whether a workaround is * needed. */ if (writemask != WRITEMASK_XYZW) { uint32_t dst_offset = 0; uint32_t i, newmask = 0, len = 0; for (i = 0; i < 4; i++) { if (writemask & (1<header.predicate_control = 0; /* XXX */ insn->header.compression_control = BRW_COMPRESSION_NONE; insn->header.destreg__conditonalmod = msg_reg_nr; brw_instruction_set_destination(insn, dest); brw_instruction_set_source0(insn, src0); brw_set_sampler_message (insn, p->is_g4x, binding_table_index, sampler, msg_type, response_length, msg_length, eot); } if (need_stall) { struct brw_reg reg = vec8(offset(dest, response_length-1)); /* mov (8) r9.0<1>:f r9.0<8;8,1>:f { Align1 } */ brw_push_insn_state(p); brw_set_compression_control(p, 0); brw_MOV(p, reg, reg); brw_pop_insn_state(p); } } /* All these variables are pretty confusing - we might be better off * using bitmasks and macros for this, in the old style. Or perhaps * just having the caller instantiate the fields in dword3 itself. */ void brw_urb_WRITE(struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, int allocate, int used, uint32_t msg_length, uint32_t response_length, int eot, int writes_complete, uint32_t offset, uint32_t swizzle) { struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); assert(msg_length < 16); brw_instruction_set_destination (insn, dest); brw_instruction_set_source0 (insn, src0); brw_set_src1 (insn, brw_imm_d (0)); insn->header.destreg__conditonalmod = msg_reg_nr; brw_set_urb_message (insn, allocate, used, msg_length, response_length, eot, writes_complete, offset, swizzle); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-eu-util.c000066400000000000000000000062301271037650300303670ustar00rootroot00000000000000/* Copyright (C) Intel Corp. 2006. All Rights Reserved. Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to develop this 3D driver. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **********************************************************************/ /* * Authors: * Keith Whitwell */ #include "cairoint.h" #include "cairo-drm-intel-brw-eu.h" void brw_math_invert( struct brw_compile *p, struct brw_reg dst, struct brw_reg src) { brw_math( p, dst, BRW_MATH_FUNCTION_INV, BRW_MATH_SATURATE_NONE, 0, src, BRW_MATH_PRECISION_FULL, BRW_MATH_DATA_VECTOR ); } void brw_copy4(struct brw_compile *p, struct brw_reg dst, struct brw_reg src, uint32_t count) { uint32_t i; dst = vec4(dst); src = vec4(src); for (i = 0; i < count; i++) { uint32_t delta = i*32; brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta)); brw_MOV(p, byte_offset(dst, delta+16), byte_offset(src, delta+16)); } } void brw_copy8(struct brw_compile *p, struct brw_reg dst, struct brw_reg src, uint32_t count) { uint32_t i; dst = vec8(dst); src = vec8(src); for (i = 0; i < count; i++) { uint32_t delta = i*32; brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta)); } } void brw_copy_indirect_to_indirect(struct brw_compile *p, struct brw_indirect dst_ptr, struct brw_indirect src_ptr, uint32_t count) { uint32_t i; for (i = 0; i < count; i++) { uint32_t delta = i*32; brw_MOV(p, deref_4f(dst_ptr, delta), deref_4f(src_ptr, delta)); brw_MOV(p, deref_4f(dst_ptr, delta+16), deref_4f(src_ptr, delta+16)); } } void brw_copy_from_indirect(struct brw_compile *p, struct brw_reg dst, struct brw_indirect ptr, uint32_t count) { uint32_t i; dst = vec4(dst); for (i = 0; i < count; i++) { uint32_t delta = i*32; brw_MOV(p, byte_offset(dst, delta), deref_4f(ptr, delta)); brw_MOV(p, byte_offset(dst, delta+16), deref_4f(ptr, delta+16)); } } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-eu.c000066400000000000000000000154741271037650300274260ustar00rootroot00000000000000/* Copyright (C) Intel Corp. 2006. All Rights Reserved. Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to develop this 3D driver. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **********************************************************************/ /* * Authors: * Keith Whitwell */ #include "cairoint.h" #include "cairo-drm-intel-brw-eu.h" #include #include #include /* How does predicate control work when execution_size != 8? Do I * need to test/set for 0xffff when execution_size is 16? */ void brw_set_predicate_control_flag_value( struct brw_compile *p, uint32_t value ) { p->current->header.predicate_control = BRW_PREDICATE_NONE; if (value != 0xff) { if (value != p->flag_value) { brw_push_insn_state(p); brw_MOV(p, brw_flag_reg(), brw_imm_uw(value)); p->flag_value = value; brw_pop_insn_state(p); } p->current->header.predicate_control = BRW_PREDICATE_NORMAL; } } void brw_set_predicate_control( struct brw_compile *p, uint32_t pc ) { p->current->header.predicate_control = pc; } void brw_set_conditionalmod( struct brw_compile *p, uint32_t conditional ) { p->current->header.destreg__conditonalmod = conditional; } void brw_set_access_mode( struct brw_compile *p, uint32_t access_mode ) { p->current->header.access_mode = access_mode; } void brw_set_compression_control( struct brw_compile *p, int compression_control ) { p->current->header.compression_control = compression_control; } void brw_set_mask_control( struct brw_compile *p, uint32_t value ) { p->current->header.mask_control = value; } void brw_set_saturate( struct brw_compile *p, uint32_t value ) { p->current->header.saturate = value; } void brw_push_insn_state( struct brw_compile *p ) { assert(p->current != &p->stack[BRW_EU_MAX_INSN_STACK-1]); memcpy(p->current+1, p->current, sizeof(struct brw_instruction)); p->current++; } void brw_pop_insn_state( struct brw_compile *p ) { assert(p->current != p->stack); p->current--; } /************************************************************************/ void brw_compile_init (struct brw_compile *p, cairo_bool_t is_g4x) { p->nr_insn = 0; p->current = p->stack; memset (p->current, 0, sizeof (p->current[0])); p->is_g4x = is_g4x; /* Some defaults? */ brw_set_mask_control (p, BRW_MASK_ENABLE); /* what does this do? */ brw_set_saturate (p, 0); brw_set_compression_control (p, BRW_COMPRESSION_NONE); brw_set_predicate_control_flag_value (p, 0xff); } const uint32_t * brw_get_program (struct brw_compile *p, uint32_t *sz) { *sz = p->nr_insn * sizeof (struct brw_instruction); return (const uint32_t *)p->store; } /* * Subroutine calls require special attention. * Mesa instructions may be expanded into multiple hardware instructions * so the prog_instruction::BranchTarget field can't be used as an index * into the hardware instructions. * * The BranchTarget field isn't needed, however. Mesa's GLSL compiler * emits CAL and BGNSUB instructions with labels that can be used to map * subroutine calls to actual subroutine code blocks. * * The structures and function here implement patching of CAL instructions * so they jump to the right subroutine code... */ /* * For each OPCODE_BGNSUB we create one of these. */ struct brw_glsl_label { const char *name; /*< the label string */ uint32_t position; /*< the position of the brw instruction for this label */ struct brw_glsl_label *next; /*< next in linked list */ }; /* * For each OPCODE_CAL we create one of these. */ struct brw_glsl_call { uint32_t call_inst_pos; /*< location of the CAL instruction */ const char *sub_name; /*< name of subroutine to call */ struct brw_glsl_call *next; /*< next in linked list */ }; /* * Called for each OPCODE_BGNSUB. */ void brw_save_label(struct brw_compile *c, const char *name, uint32_t position) { struct brw_glsl_label *label = calloc(1, sizeof *label); label->name = name; label->position = position; label->next = c->first_label; c->first_label = label; } /* * Called for each OPCODE_CAL. */ void brw_save_call(struct brw_compile *c, const char *name, uint32_t call_pos) { struct brw_glsl_call *call = calloc(1, sizeof *call); call->call_inst_pos = call_pos; call->sub_name = name; call->next = c->first_call; c->first_call = call; } /* * Lookup a label, return label's position/offset. */ static uint32_t brw_lookup_label(struct brw_compile *c, const char *name) { const struct brw_glsl_label *label; for (label = c->first_label; label; label = label->next) { if (strcmp(name, label->name) == 0) { return label->position; } } abort(); /* should never happen */ return ~0; } /* * When we're done generating code, this function is called to resolve * subroutine calls. */ void brw_resolve_cals(struct brw_compile *c) { const struct brw_glsl_call *call; for (call = c->first_call; call; call = call->next) { const uint32_t sub_loc = brw_lookup_label(c, call->sub_name); struct brw_instruction *brw_call_inst = &c->store[call->call_inst_pos]; struct brw_instruction *brw_sub_inst = &c->store[sub_loc]; int32_t offset = brw_sub_inst - brw_call_inst; /* patch brw_inst1 to point to brw_inst2 */ brw_set_src1(brw_call_inst, brw_imm_d(offset * 16)); } /* free linked list of calls */ { struct brw_glsl_call *call, *next; for (call = c->first_call; call; call = next) { next = call->next; free(call); } c->first_call = NULL; } /* free linked list of labels */ { struct brw_glsl_label *label, *next; for (label = c->first_label; label; label = next) { next = label->next; free(label); } c->first_label = NULL; } } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-eu.h000066400000000000000000000630751271037650300274330ustar00rootroot00000000000000/* Copyright (C) Intel Corp. 2006. All Rights Reserved. Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to develop this 3D driver. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **********************************************************************/ /* * Authors: * Keith Whitwell */ #ifndef CAIRO_DRM_INTEL_BRW_EU_H #define CAIRO_DRM_INTEL_BRW_EU_H #include "cairo-drm-intel-brw-structs.h" #include "cairo-drm-intel-brw-defines.h" #include /* * Writemask values, 1 bit per component. */ #define WRITEMASK_X 0x1 #define WRITEMASK_Y 0x2 #define WRITEMASK_Z 0x4 #define WRITEMASK_W 0x8 #define WRITEMASK_XY (WRITEMASK_X | WRITEMASK_Y) #define WRITEMASK_XZ (WRITEMASK_X | WRITEMASK_Z) #define WRITEMASK_YZ (WRITEMASK_Y | WRITEMASK_Z) #define WRITEMASK_XYZ (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z) #define WRITEMASK_XW (WRITEMASK_X | WRITEMASK_W) #define WRITEMASK_YW (WRITEMASK_Y | WRITEMASK_W) #define WRITEMASK_XYW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_W) #define WRITEMASK_ZW (WRITEMASK_Z | WRITEMASK_W) #define WRITEMASK_XZW (WRITEMASK_X | WRITEMASK_Z | WRITEMASK_W) #define WRITEMASK_YZW (WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W) #define WRITEMASK_XYZW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W) #define BRW_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<2) | ((c)<<4) | ((d)<<6)) #define BRW_GET_SWZ(swz, idx) (((swz) >> ((idx)*2)) & 0x3) #define BRW_SWIZZLE_NOOP BRW_SWIZZLE4 (0,1,2,3) #define BRW_SWIZZLE_XYZW BRW_SWIZZLE4 (0,1,2,3) #define BRW_SWIZZLE_XXXX BRW_SWIZZLE4 (0,0,0,0) #define BRW_SWIZZLE_XYXY BRW_SWIZZLE4 (0,1,0,1) #define REG_SIZE (8*4) /* These aren't hardware structs, just something useful for us to pass around: * * Align1 operation has a lot of control over input ranges. Used in * WM programs to implement shaders decomposed into "channel serial" * or "structure of array" form: */ struct brw_reg { uint32_t type:4; uint32_t file:2; uint32_t nr:8; uint32_t subnr:5; /* :1 in align16 */ uint32_t negate:1; /* source only */ uint32_t abs:1; /* source only */ uint32_t vstride:4; /* source only */ uint32_t width:3; /* src only, align1 only */ uint32_t hstride:2; /* align1 only */ uint32_t address_mode:1; /* relative addressing, hopefully! */ uint32_t pad0:1; union { struct { uint32_t swizzle:8; /* src only, align16 only */ uint32_t writemask:4; /* dest only, align16 only */ int32_t indirect_offset:10; /* relative addressing offset */ uint32_t pad1:10; /* two dwords total */ } bits; float f; int32_t d; uint32_t ud; } dw1; }; struct brw_indirect { uint32_t addr_subnr:4; int32_t addr_offset:10; uint32_t pad:18; }; struct brw_glsl_label; struct brw_glsl_call; #define BRW_EU_MAX_INSN_STACK 5 #define BRW_EU_MAX_INSN 200 struct brw_compile { struct brw_instruction store[BRW_EU_MAX_INSN]; uint32_t nr_insn; cairo_bool_t is_g4x; /* Allow clients to push/pop instruction state: */ struct brw_instruction stack[BRW_EU_MAX_INSN_STACK]; struct brw_instruction *current; uint32_t flag_value; int single_program_flow; struct brw_context *brw; struct brw_glsl_label *first_label; /*< linked list of labels */ struct brw_glsl_call *first_call; /*< linked list of CALs */ }; cairo_private void brw_save_label (struct brw_compile *c, const char *name, uint32_t position); cairo_private void brw_save_call (struct brw_compile *c, const char *name, uint32_t call_pos); cairo_private void brw_resolve_cals (struct brw_compile *c); static cairo_always_inline int type_sz (uint32_t type) { switch (type) { case BRW_REGISTER_TYPE_UD: case BRW_REGISTER_TYPE_D: case BRW_REGISTER_TYPE_F: return 4; case BRW_REGISTER_TYPE_HF: case BRW_REGISTER_TYPE_UW: case BRW_REGISTER_TYPE_W: return 2; case BRW_REGISTER_TYPE_UB: case BRW_REGISTER_TYPE_B: return 1; default: return 0; } } /* * Construct a brw_reg. * \param file one of the BRW_x_REGISTER_FILE values * \param nr register number/index * \param subnr register sub number * \param type one of BRW_REGISTER_TYPE_x * \param vstride one of BRW_VERTICAL_STRIDE_x * \param width one of BRW_WIDTH_x * \param hstride one of BRW_HORIZONTAL_STRIDE_x * \param swizzle one of BRW_SWIZZLE_x * \param writemask WRITEMASK_X/Y/Z/W bitfield */ static cairo_always_inline struct brw_reg brw_reg (uint32_t file, uint32_t nr, uint32_t subnr, uint32_t type, uint32_t vstride, uint32_t width, uint32_t hstride, uint32_t swizzle, uint32_t writemask) { struct brw_reg reg; if (type == BRW_GENERAL_REGISTER_FILE) assert(nr < 128); else if (type == BRW_MESSAGE_REGISTER_FILE) assert(nr < 9); else if (type == BRW_ARCHITECTURE_REGISTER_FILE) assert(nr <= BRW_ARF_IP); reg.type = type; reg.file = file; reg.nr = nr; reg.subnr = subnr * type_sz(type); reg.negate = 0; reg.abs = 0; reg.vstride = vstride; reg.width = width; reg.hstride = hstride; reg.address_mode = BRW_ADDRESS_DIRECT; reg.pad0 = 0; /* Could do better: If the reg is r5.3<0;1,0>, we probably want to * set swizzle and writemask to W, as the lower bits of subnr will * be lost when converted to align16. This is probably too much to * keep track of as you'd want it adjusted by suboffset(), etc. * Perhaps fix up when converting to align16? */ reg.dw1.bits.swizzle = swizzle; reg.dw1.bits.writemask = writemask; reg.dw1.bits.indirect_offset = 0; reg.dw1.bits.pad1 = 0; return reg; } /* Construct float[16] register */ static cairo_always_inline struct brw_reg brw_vec16_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return brw_reg (file, nr, subnr, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_16, BRW_WIDTH_16, BRW_HORIZONTAL_STRIDE_1, BRW_SWIZZLE_XYZW, WRITEMASK_XYZW); } /* Construct float[8] register */ static cairo_always_inline struct brw_reg brw_vec8_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return brw_reg (file, nr, subnr, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_8, BRW_WIDTH_8, BRW_HORIZONTAL_STRIDE_1, BRW_SWIZZLE_XYZW, WRITEMASK_XYZW); } /* Construct float[4] register */ static cairo_always_inline struct brw_reg brw_vec4_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return brw_reg (file, nr, subnr, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_4, BRW_WIDTH_4, BRW_HORIZONTAL_STRIDE_1, BRW_SWIZZLE_XYZW, WRITEMASK_XYZW); } /* Construct float[2] register */ static cairo_always_inline struct brw_reg brw_vec2_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return brw_reg (file, nr, subnr, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_2, BRW_WIDTH_2, BRW_HORIZONTAL_STRIDE_1, BRW_SWIZZLE_XYXY, WRITEMASK_XY); } /* Construct float[1] register */ static cairo_always_inline struct brw_reg brw_vec1_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return brw_reg (file, nr, subnr, BRW_REGISTER_TYPE_F, BRW_VERTICAL_STRIDE_0, BRW_WIDTH_1, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_XXXX, WRITEMASK_X); } static cairo_always_inline struct brw_reg retype (struct brw_reg reg, uint32_t type) { reg.type = type; return reg; } static cairo_always_inline struct brw_reg suboffset (struct brw_reg reg, uint32_t delta) { reg.subnr += delta * type_sz (reg.type); return reg; } static cairo_always_inline struct brw_reg offset (struct brw_reg reg, uint32_t delta) { reg.nr += delta; return reg; } static cairo_always_inline struct brw_reg byte_offset (struct brw_reg reg, uint32_t bytes) { uint32_t newoffset = reg.nr * REG_SIZE + reg.subnr + bytes; reg.nr = newoffset / REG_SIZE; reg.subnr = newoffset % REG_SIZE; return reg; } /* Construct unsigned word[16] register */ static cairo_always_inline struct brw_reg brw_uw16_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return suboffset (retype (brw_vec16_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); } /* Construct unsigned word[8] register */ static cairo_always_inline struct brw_reg brw_uw8_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return suboffset (retype (brw_vec8_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); } /* Construct unsigned word[2] register */ static cairo_always_inline struct brw_reg brw_uw2_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return suboffset (retype (brw_vec2_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); } /* Construct unsigned word[1] register */ static cairo_always_inline struct brw_reg brw_uw1_reg (uint32_t file, uint32_t nr, uint32_t subnr) { return suboffset (retype (brw_vec1_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); } static cairo_always_inline struct brw_reg brw_imm_reg (uint32_t type) { return brw_reg (BRW_IMMEDIATE_VALUE, 0, 0, type, BRW_VERTICAL_STRIDE_0, BRW_WIDTH_1, BRW_HORIZONTAL_STRIDE_0, 0, 0); } /* Construct float immediate register */ static cairo_always_inline struct brw_reg brw_imm_f( float f ) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_F); imm.dw1.f = f; return imm; } /* Construct integer immediate register */ static cairo_always_inline struct brw_reg brw_imm_d( int32_t d ) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_D); imm.dw1.d = d; return imm; } /* Construct uint immediate register */ static cairo_always_inline struct brw_reg brw_imm_ud( uint32_t ud ) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UD); imm.dw1.ud = ud; return imm; } /* Construct ushort immediate register */ static cairo_always_inline struct brw_reg brw_imm_uw( uint16_t uw ) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UW); imm.dw1.ud = uw | (uw << 16); return imm; } /* Construct short immediate register */ static cairo_always_inline struct brw_reg brw_imm_w( int16_t w ) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_W); imm.dw1.d = w | (w << 16); return imm; } /* brw_imm_b and brw_imm_ub aren't supported by hardware - the type * numbers alias with _V and _VF below: */ /* Construct vector of eight signed half-byte values */ static cairo_always_inline struct brw_reg brw_imm_v (uint32_t v) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_V); imm.vstride = BRW_VERTICAL_STRIDE_0; imm.width = BRW_WIDTH_8; imm.hstride = BRW_HORIZONTAL_STRIDE_1; imm.dw1.ud = v; return imm; } /* Construct vector of four 8-bit float values */ static cairo_always_inline struct brw_reg brw_imm_vf (uint32_t v) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF); imm.vstride = BRW_VERTICAL_STRIDE_0; imm.width = BRW_WIDTH_4; imm.hstride = BRW_HORIZONTAL_STRIDE_1; imm.dw1.ud = v; return imm; } #define VF_ZERO 0x0 #define VF_ONE 0x30 #define VF_NEG (1<<7) static cairo_always_inline struct brw_reg brw_imm_vf4 (uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3) { struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF); imm.vstride = BRW_VERTICAL_STRIDE_0; imm.width = BRW_WIDTH_4; imm.hstride = BRW_HORIZONTAL_STRIDE_1; imm.dw1.ud = ((v0 << 0) | (v1 << 8) | (v2 << 16) | (v3 << 24)); return imm; } static cairo_always_inline struct brw_reg brw_address (struct brw_reg reg) { return brw_imm_uw (reg.nr * REG_SIZE + reg.subnr); } /* Construct float[1] general-purpose register */ static cairo_always_inline struct brw_reg brw_vec1_grf (uint32_t nr, uint32_t subnr) { return brw_vec1_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); } /* Construct float[2] general-purpose register */ static cairo_always_inline struct brw_reg brw_vec2_grf (uint32_t nr, uint32_t subnr) { return brw_vec2_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); } /* Construct float[4] general-purpose register */ static cairo_always_inline struct brw_reg brw_vec4_grf (uint32_t nr, uint32_t subnr) { return brw_vec4_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); } /* Construct float[8] general-purpose register */ static cairo_always_inline struct brw_reg brw_vec8_grf (uint32_t nr) { return brw_vec8_reg (BRW_GENERAL_REGISTER_FILE, nr, 0); } static cairo_always_inline struct brw_reg brw_uw8_grf (uint32_t nr, uint32_t subnr) { return brw_uw8_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); } static cairo_always_inline struct brw_reg brw_uw16_grf (uint32_t nr, uint32_t subnr) { return brw_uw16_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); } /* Construct null register (usually used for setting condition codes) */ static cairo_always_inline struct brw_reg brw_null_reg (void) { return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_NULL, 0); } static cairo_always_inline struct brw_reg brw_address_reg (uint32_t subnr) { return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_ADDRESS, subnr); } /* If/else instructions break in align16 mode if writemask & swizzle * aren't xyzw. This goes against the convention for other scalar * regs: */ static cairo_always_inline struct brw_reg brw_ip_reg (void) { return brw_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_IP, 0, BRW_REGISTER_TYPE_UD, BRW_VERTICAL_STRIDE_4, /* ? */ BRW_WIDTH_1, BRW_HORIZONTAL_STRIDE_0, BRW_SWIZZLE_XYZW, WRITEMASK_XYZW); } static cairo_always_inline struct brw_reg brw_acc_reg (void) { return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_ACCUMULATOR, 0); } static cairo_always_inline struct brw_reg brw_flag_reg (void) { return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_FLAG, 0); } static cairo_always_inline struct brw_reg brw_mask_reg (uint32_t subnr) { return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, BRW_ARF_MASK, subnr); } static cairo_always_inline struct brw_reg brw_message4_reg (uint32_t nr) { return brw_vec4_reg (BRW_MESSAGE_REGISTER_FILE, nr, 0); } static cairo_always_inline struct brw_reg brw_message_reg (uint32_t nr) { return brw_vec8_reg (BRW_MESSAGE_REGISTER_FILE, nr, 0); } /* This is almost always called with a numeric constant argument, so * make things easy to evaluate at compile time: */ static cairo_always_inline uint32_t cvt (uint32_t val) { switch (val) { case 0: return 0; case 1: return 1; case 2: return 2; case 4: return 3; case 8: return 4; case 16: return 5; case 32: return 6; } return 0; } static cairo_always_inline struct brw_reg stride (struct brw_reg reg, uint32_t vstride, uint32_t width, uint32_t hstride) { reg.vstride = cvt (vstride); reg.width = cvt (width) - 1; reg.hstride = cvt (hstride); return reg; } static cairo_always_inline struct brw_reg vec16 (struct brw_reg reg) { return stride (reg, 16,16,1); } static cairo_always_inline struct brw_reg vec8 (struct brw_reg reg) { return stride (reg, 8,8,1); } static cairo_always_inline struct brw_reg vec4 (struct brw_reg reg) { return stride (reg, 4,4,1); } static cairo_always_inline struct brw_reg vec2 (struct brw_reg reg) { return stride (reg, 2,2,1); } static cairo_always_inline struct brw_reg vec1 (struct brw_reg reg) { return stride (reg, 0,1,0); } static cairo_always_inline struct brw_reg get_element (struct brw_reg reg, uint32_t elt) { return vec1 (suboffset (reg, elt)); } static cairo_always_inline struct brw_reg get_element_ud (struct brw_reg reg, uint32_t elt) { return vec1 (suboffset (retype (reg, BRW_REGISTER_TYPE_UD), elt)); } static cairo_always_inline struct brw_reg brw_swizzle (struct brw_reg reg, uint32_t x, uint32_t y, uint32_t z, uint32_t w) { reg.dw1.bits.swizzle = BRW_SWIZZLE4 (BRW_GET_SWZ (reg.dw1.bits.swizzle, x), BRW_GET_SWZ (reg.dw1.bits.swizzle, y), BRW_GET_SWZ (reg.dw1.bits.swizzle, z), BRW_GET_SWZ (reg.dw1.bits.swizzle, w)); return reg; } static cairo_always_inline struct brw_reg brw_swizzle1 (struct brw_reg reg, uint32_t x) { return brw_swizzle (reg, x, x, x, x); } static cairo_always_inline struct brw_reg brw_writemask (struct brw_reg reg, uint32_t mask) { reg.dw1.bits.writemask &= mask; return reg; } static cairo_always_inline struct brw_reg brw_set_writemask (struct brw_reg reg, uint32_t mask) { reg.dw1.bits.writemask = mask; return reg; } static cairo_always_inline struct brw_reg negate (struct brw_reg reg) { reg.negate ^= 1; return reg; } static cairo_always_inline struct brw_reg brw_abs (struct brw_reg reg) { reg.abs = 1; return reg; } static cairo_always_inline struct brw_reg brw_vec4_indirect (uint32_t subnr, int32_t offset) { struct brw_reg reg = brw_vec4_grf (0, 0); reg.subnr = subnr; reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER; reg.dw1.bits.indirect_offset = offset; return reg; } static cairo_always_inline struct brw_reg brw_vec1_indirect (uint32_t subnr, int32_t offset) { struct brw_reg reg = brw_vec1_grf (0, 0); reg.subnr = subnr; reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER; reg.dw1.bits.indirect_offset = offset; return reg; } static cairo_always_inline struct brw_reg deref_4f (struct brw_indirect ptr, int32_t offset) { return brw_vec4_indirect (ptr.addr_subnr, ptr.addr_offset + offset); } static cairo_always_inline struct brw_reg deref_1f(struct brw_indirect ptr, int32_t offset) { return brw_vec1_indirect (ptr.addr_subnr, ptr.addr_offset + offset); } static cairo_always_inline struct brw_reg deref_4b(struct brw_indirect ptr, int32_t offset) { return retype (deref_4f (ptr, offset), BRW_REGISTER_TYPE_B); } static cairo_always_inline struct brw_reg deref_1uw(struct brw_indirect ptr, int32_t offset) { return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UW); } static cairo_always_inline struct brw_reg deref_1d (struct brw_indirect ptr, int32_t offset) { return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_D); } static cairo_always_inline struct brw_reg deref_1ud (struct brw_indirect ptr, int32_t offset) { return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UD); } static cairo_always_inline struct brw_reg get_addr_reg (struct brw_indirect ptr) { return brw_address_reg (ptr.addr_subnr); } static cairo_always_inline struct brw_indirect brw_indirect_offset (struct brw_indirect ptr, int32_t offset) { ptr.addr_offset += offset; return ptr; } static cairo_always_inline struct brw_indirect brw_indirect (uint32_t addr_subnr, int32_t offset) { struct brw_indirect ptr; ptr.addr_subnr = addr_subnr; ptr.addr_offset = offset; ptr.pad = 0; return ptr; } static cairo_always_inline struct brw_instruction * current_insn (struct brw_compile *p) { return &p->store[p->nr_insn]; } cairo_private void brw_pop_insn_state (struct brw_compile *p); cairo_private void brw_push_insn_state (struct brw_compile *p); cairo_private void brw_set_mask_control (struct brw_compile *p, uint32_t value); cairo_private void brw_set_saturate (struct brw_compile *p, uint32_t value); cairo_private void brw_set_access_mode (struct brw_compile *p, uint32_t access_mode); cairo_private void brw_set_compression_control (struct brw_compile *p, int control); cairo_private void brw_set_predicate_control_flag_value (struct brw_compile *p, uint32_t value); cairo_private void brw_set_predicate_control (struct brw_compile *p, uint32_t pc); cairo_private void brw_set_conditionalmod (struct brw_compile *p, uint32_t conditional); cairo_private void brw_compile_init (struct brw_compile *p, cairo_bool_t is_g4x); cairo_private const uint32_t *brw_get_program (struct brw_compile *p, uint32_t *sz); /* Helpers for regular instructions: */ #define ALU1(OP) \ cairo_private_no_warn struct brw_instruction * \ brw_##OP(struct brw_compile *p, \ struct brw_reg dest, \ struct brw_reg src0); #define ALU2(OP) \ cairo_private_no_warn struct brw_instruction * \ brw_##OP(struct brw_compile *p, \ struct brw_reg dest, \ struct brw_reg src0, \ struct brw_reg src1); ALU1(MOV) ALU2(SEL) ALU1(NOT) ALU2(AND) ALU2(OR) ALU2(XOR) ALU2(SHR) ALU2(SHL) ALU2(RSR) ALU2(RSL) ALU2(ASR) ALU2(JMPI) ALU2(ADD) ALU2(MUL) ALU1(FRC) ALU1(RNDD) ALU1(RNDZ) ALU2(MAC) ALU2(MACH) ALU1(LZD) ALU2(DP4) ALU2(DPH) ALU2(DP3) ALU2(DP2) ALU2(LINE) #undef ALU1 #undef ALU2 /* Helpers for SEND instruction: */ cairo_private void brw_urb_WRITE (struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, int allocate, int used, uint32_t msg_length, uint32_t response_length, int eot, int writes_complete, uint32_t offset, uint32_t swizzle); cairo_private void brw_fb_WRITE (struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, uint32_t binding_table_index, uint32_t msg_length, uint32_t response_length, int eot); cairo_private void brw_SAMPLE (struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, struct brw_reg src0, uint32_t binding_table_index, uint32_t sampler, uint32_t writemask, uint32_t msg_type, uint32_t response_length, uint32_t msg_length, cairo_bool_t eot); cairo_private void brw_math_16 (struct brw_compile *p, struct brw_reg dest, uint32_t function, uint32_t saturate, uint32_t msg_reg_nr, struct brw_reg src, uint32_t precision); cairo_private void brw_math (struct brw_compile *p, struct brw_reg dest, uint32_t function, uint32_t saturate, uint32_t msg_reg_nr, struct brw_reg src, uint32_t data_type, uint32_t precision); cairo_private void brw_dp_READ_16 (struct brw_compile *p, struct brw_reg dest, uint32_t msg_reg_nr, uint32_t scratch_offset); cairo_private void brw_dp_WRITE_16 (struct brw_compile *p, struct brw_reg src, uint32_t msg_reg_nr, uint32_t scratch_offset); /* If/else/endif. Works by manipulating the execution flags on each * channel. */ cairo_private struct brw_instruction * brw_IF (struct brw_compile *p, uint32_t execute_size); cairo_private struct brw_instruction * brw_ELSE (struct brw_compile *p, struct brw_instruction *if_insn); cairo_private void brw_ENDIF (struct brw_compile *p, struct brw_instruction *if_or_else_insn); /* DO/WHILE loops: */ cairo_private struct brw_instruction * brw_DO (struct brw_compile *p, uint32_t execute_size); cairo_private struct brw_instruction * brw_WHILE (struct brw_compile *p, struct brw_instruction *patch_insn); cairo_private struct brw_instruction * brw_BREAK (struct brw_compile *p); cairo_private struct brw_instruction * brw_CONT (struct brw_compile *p); /* Forward jumps: */ cairo_private void brw_land_fwd_jump (struct brw_compile *p, struct brw_instruction *jmp_insn); cairo_private void brw_NOP (struct brw_compile *p); /* Special case: there is never a destination, execution size will be * taken from src0: */ cairo_private void brw_CMP (struct brw_compile *p, struct brw_reg dest, uint32_t conditional, struct brw_reg src0, struct brw_reg src1); cairo_private void brw_print_reg (struct brw_reg reg); cairo_private struct brw_instruction * brw_next_instruction (struct brw_compile *p, uint32_t opcode); cairo_private void brw_instruction_set_destination (struct brw_instruction *insn, struct brw_reg dest); cairo_private void brw_instruction_set_source0 (struct brw_instruction *insn, struct brw_reg reg); cairo_private void brw_instruction_set_dp_write_message (struct brw_instruction *insn, uint32_t binding_table_index, uint32_t msg_control, uint32_t msg_type, uint32_t msg_length, uint32_t pixel_scoreboard_clear, uint32_t response_length, uint32_t end_of_thread); /*********************************************************************** * brw_eu_util.c: */ cairo_private void brw_copy_indirect_to_indirect (struct brw_compile *p, struct brw_indirect dst_ptr, struct brw_indirect src_ptr, uint32_t count); cairo_private void brw_copy_from_indirect (struct brw_compile *p, struct brw_reg dst, struct brw_indirect ptr, uint32_t count); cairo_private void brw_copy4 (struct brw_compile *p, struct brw_reg dst, struct brw_reg src, uint32_t count); cairo_private void brw_copy8 (struct brw_compile *p, struct brw_reg dst, struct brw_reg src, uint32_t count); cairo_private void brw_math_invert (struct brw_compile *p, struct brw_reg dst, struct brw_reg src); cairo_private void brw_set_src1 (struct brw_instruction *insn, struct brw_reg reg); #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-brw-structs.h000066400000000000000000000743561271037650300305350ustar00rootroot00000000000000/************************************************************************** * * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #ifndef CAIRO_DRM_INTEL_BRW_STRUCTS_H #define CAIRO_DRM_INTEL_BRW_STRUCTS_H #include "cairo-types-private.h" /* Command packets: */ struct header { unsigned int length:16; unsigned int opcode:16; }; union header_union { struct header bits; unsigned int dword; }; struct brw_3d_control { struct { unsigned int length:8; unsigned int notify_enable:1; unsigned int pad:3; unsigned int wc_flush_enable:1; unsigned int depth_stall_enable:1; unsigned int operation:2; unsigned int opcode:16; } header; struct { unsigned int pad:2; unsigned int dest_addr_type:1; unsigned int dest_addr:29; } dest; unsigned int dword2; unsigned int dword3; }; struct brw_3d_primitive { struct { unsigned int length:8; unsigned int pad:2; unsigned int topology:5; unsigned int indexed:1; unsigned int opcode:16; } header; unsigned int verts_per_instance; unsigned int start_vert_location; unsigned int instance_count; unsigned int start_instance_location; unsigned int base_vert_location; }; /* These seem to be passed around as function args, so it works out * better to keep them as #defines: */ #define BRW_FLUSH_READ_CACHE 0x1 #define BRW_FLUSH_STATE_CACHE 0x2 #define BRW_INHIBIT_FLUSH_RENDER_CACHE 0x4 #define BRW_FLUSH_SNAPSHOT_COUNTERS 0x8 struct brw_mi_flush { unsigned int flags:4; unsigned int pad:12; unsigned int opcode:16; }; struct brw_vf_statistics { unsigned int statistics_enable:1; unsigned int pad:15; unsigned int opcode:16; }; struct brw_binding_table_pointers { struct header header; unsigned int vs; unsigned int gs; unsigned int clp; unsigned int sf; unsigned int wm; }; struct brw_blend_constant_color { struct header header; float blend_constant_color[4]; }; struct brw_depthbuffer { union header_union header; union { struct { unsigned int pitch:18; unsigned int format:3; unsigned int pad:4; unsigned int depth_offset_disable:1; unsigned int tile_walk:1; unsigned int tiled_surface:1; unsigned int pad2:1; unsigned int surface_type:3; } bits; unsigned int dword; } dword1; unsigned int dword2_base_addr; union { struct { unsigned int pad:1; unsigned int mipmap_layout:1; unsigned int lod:4; unsigned int width:13; unsigned int height:13; } bits; unsigned int dword; } dword3; union { struct { unsigned int pad:12; unsigned int min_array_element:9; unsigned int depth:11; } bits; unsigned int dword; } dword4; }; struct brw_drawrect { struct header header; unsigned int xmin:16; unsigned int ymin:16; unsigned int xmax:16; unsigned int ymax:16; unsigned int xorg:16; unsigned int yorg:16; }; struct brw_global_depth_offset_clamp { struct header header; float depth_offset_clamp; }; struct brw_indexbuffer { union { struct { unsigned int length:8; unsigned int index_format:2; unsigned int cut_index_enable:1; unsigned int pad:5; unsigned int opcode:16; } bits; unsigned int dword; } header; unsigned int buffer_start; unsigned int buffer_end; }; struct brw_line_stipple { struct header header; struct { unsigned int pattern:16; unsigned int pad:16; } bits0; struct { unsigned int repeat_count:9; unsigned int pad:7; unsigned int inverse_repeat_count:16; } bits1; }; struct brw_pipelined_state_pointers { struct header header; struct { unsigned int pad:5; unsigned int offset:27; } vs; struct { unsigned int enable:1; unsigned int pad:4; unsigned int offset:27; } gs; struct { unsigned int enable:1; unsigned int pad:4; unsigned int offset:27; } clp; struct { unsigned int pad:5; unsigned int offset:27; } sf; struct { unsigned int pad:5; unsigned int offset:27; } wm; struct { unsigned int pad:6; unsigned int offset:26; } cc; }; struct brw_polygon_stipple_offset { struct header header; struct { unsigned int y_offset:5; unsigned int pad:3; unsigned int x_offset:5; unsigned int pad0:19; } bits0; }; struct brw_polygon_stipple { struct header header; unsigned int stipple[32]; }; struct brw_pipeline_select { struct { unsigned int pipeline_select:1; unsigned int pad:15; unsigned int opcode:16; } header; }; struct brw_pipe_control { struct { unsigned int length:8; unsigned int notify_enable:1; unsigned int pad:2; unsigned int instruction_state_cache_flush_enable:1; unsigned int write_cache_flush_enable:1; unsigned int depth_stall_enable:1; unsigned int post_sync_operation:2; unsigned int opcode:16; } header; struct { unsigned int pad:2; unsigned int dest_addr_type:1; unsigned int dest_addr:29; } bits1; unsigned int data0; unsigned int data1; }; struct brw_urb_fence { struct { unsigned int length:8; unsigned int vs_realloc:1; unsigned int gs_realloc:1; unsigned int clp_realloc:1; unsigned int sf_realloc:1; unsigned int vfe_realloc:1; unsigned int cs_realloc:1; unsigned int pad:2; unsigned int opcode:16; } header; struct { unsigned int vs_fence:10; unsigned int gs_fence:10; unsigned int clp_fence:10; unsigned int pad:2; } bits0; struct { unsigned int sf_fence:10; unsigned int vf_fence:10; unsigned int cs_fence:10; unsigned int pad:2; } bits1; }; struct brw_constant_buffer_state { struct header header; struct { unsigned int nr_urb_entries:3; unsigned int pad:1; unsigned int urb_entry_size:5; unsigned int pad0:23; } bits0; }; struct brw_constant_buffer { struct { unsigned int length:8; unsigned int valid:1; unsigned int pad:7; unsigned int opcode:16; } header; struct { unsigned int buffer_length:6; unsigned int buffer_address:26; } bits0; }; struct brw_state_base_address { struct header header; struct { unsigned int modify_enable:1; unsigned int pad:4; unsigned int general_state_address:27; } bits0; struct { unsigned int modify_enable:1; unsigned int pad:4; unsigned int surface_state_address:27; } bits1; struct { unsigned int modify_enable:1; unsigned int pad:4; unsigned int indirect_object_state_address:27; } bits2; struct { unsigned int modify_enable:1; unsigned int pad:11; unsigned int general_state_upper_bound:20; } bits3; struct { unsigned int modify_enable:1; unsigned int pad:11; unsigned int indirect_object_state_upper_bound:20; } bits4; }; struct brw_state_prefetch { struct header header; struct { unsigned int prefetch_count:3; unsigned int pad:3; unsigned int prefetch_pointer:26; } bits0; }; struct brw_system_instruction_pointer { struct header header; struct { unsigned int pad:4; unsigned int system_instruction_pointer:28; } bits0; }; /* State structs for the various fixed function units: */ struct thread0 { unsigned int pad0:1; unsigned int grf_reg_count:3; unsigned int pad1:2; unsigned int kernel_start_pointer:26; }; struct thread1 { unsigned int ext_halt_exception_enable:1; unsigned int sw_exception_enable:1; unsigned int mask_stack_exception_enable:1; unsigned int timeout_exception_enable:1; unsigned int illegal_op_exception_enable:1; unsigned int pad0:3; unsigned int depth_coef_urb_read_offset:6; /* WM only */ unsigned int pad1:2; unsigned int floating_point_mode:1; unsigned int thread_priority:1; unsigned int binding_table_entry_count:8; unsigned int pad3:5; unsigned int single_program_flow:1; }; struct thread2 { unsigned int per_thread_scratch_space:4; unsigned int pad0:6; unsigned int scratch_space_base_pointer:22; }; struct thread3 { unsigned int dispatch_grf_start_reg:4; unsigned int urb_entry_read_offset:6; unsigned int pad0:1; unsigned int urb_entry_read_length:6; unsigned int pad1:1; unsigned int const_urb_entry_read_offset:6; unsigned int pad2:1; unsigned int const_urb_entry_read_length:6; unsigned int pad3:1; }; struct brw_clip_unit_state { struct thread0 thread0; struct thread1 thread1; struct thread2 thread2; struct thread3 thread3; struct { unsigned int pad0:9; unsigned int gs_output_stats:1; /* not always */ unsigned int stats_enable:1; unsigned int nr_urb_entries:7; unsigned int pad1:1; unsigned int urb_entry_allocation_size:5; unsigned int pad2:1; unsigned int max_threads:6; /* may be less */ unsigned int pad3:1; } thread4; struct { unsigned int pad0:13; unsigned int clip_mode:3; unsigned int userclip_enable_flags:8; unsigned int userclip_must_clip:1; unsigned int pad1:1; unsigned int guard_band_enable:1; unsigned int viewport_z_clip_enable:1; unsigned int viewport_xy_clip_enable:1; unsigned int vertex_position_space:1; unsigned int api_mode:1; unsigned int pad2:1; } clip5; struct { unsigned int pad0:5; unsigned int clipper_viewport_state_ptr:27; } clip6; float viewport_xmin; float viewport_xmax; float viewport_ymin; float viewport_ymax; }; struct brw_cc_unit_state { struct { unsigned int pad0:3; unsigned int bf_stencil_pass_depth_pass_op:3; unsigned int bf_stencil_pass_depth_fail_op:3; unsigned int bf_stencil_fail_op:3; unsigned int bf_stencil_func:3; unsigned int bf_stencil_enable:1; unsigned int pad1:2; unsigned int stencil_write_enable:1; unsigned int stencil_pass_depth_pass_op:3; unsigned int stencil_pass_depth_fail_op:3; unsigned int stencil_fail_op:3; unsigned int stencil_func:3; unsigned int stencil_enable:1; } cc0; struct { unsigned int bf_stencil_ref:8; unsigned int stencil_write_mask:8; unsigned int stencil_test_mask:8; unsigned int stencil_ref:8; } cc1; struct { unsigned int logicop_enable:1; unsigned int pad0:10; unsigned int depth_write_enable:1; unsigned int depth_test_function:3; unsigned int depth_test:1; unsigned int bf_stencil_write_mask:8; unsigned int bf_stencil_test_mask:8; } cc2; struct { unsigned int pad0:8; unsigned int alpha_test_func:3; unsigned int alpha_test:1; unsigned int blend_enable:1; unsigned int ia_blend_enable:1; unsigned int pad1:1; unsigned int alpha_test_format:1; unsigned int pad2:16; } cc3; struct { unsigned int pad0:5; unsigned int cc_viewport_state_offset:27; } cc4; struct { unsigned int pad0:2; unsigned int ia_dest_blend_factor:5; unsigned int ia_src_blend_factor:5; unsigned int ia_blend_function:3; unsigned int statistics_enable:1; unsigned int logicop_func:4; unsigned int pad1:11; unsigned int dither_enable:1; } cc5; struct { unsigned int clamp_post_alpha_blend:1; unsigned int clamp_pre_alpha_blend:1; unsigned int clamp_range:2; unsigned int pad0:11; unsigned int y_dither_offset:2; unsigned int x_dither_offset:2; unsigned int dest_blend_factor:5; unsigned int src_blend_factor:5; unsigned int blend_function:3; } cc6; struct { union { float f; unsigned char ub[4]; } alpha_ref; } cc7; }; struct brw_sf_unit_state { struct thread0 thread0; struct { unsigned int pad0:7; unsigned int sw_exception_enable:1; unsigned int pad1:3; unsigned int mask_stack_exception_enable:1; unsigned int pad2:1; unsigned int illegal_op_exception_enable:1; unsigned int pad3:2; unsigned int floating_point_mode:1; unsigned int thread_priority:1; unsigned int binding_table_entry_count:8; unsigned int pad4:5; unsigned int single_program_flow:1; } sf1; struct thread2 thread2; struct thread3 thread3; struct { unsigned int pad0:10; unsigned int stats_enable:1; unsigned int nr_urb_entries:7; unsigned int pad1:1; unsigned int urb_entry_allocation_size:5; unsigned int pad2:1; unsigned int max_threads:6; unsigned int pad3:1; } thread4; struct { unsigned int front_winding:1; unsigned int viewport_transform:1; unsigned int pad0:3; unsigned int sf_viewport_state_offset:27; } sf5; struct { unsigned int pad0:9; unsigned int dest_org_vbias:4; unsigned int dest_org_hbias:4; unsigned int scissor:1; unsigned int disable_2x2_trifilter:1; unsigned int disable_zero_pix_trifilter:1; unsigned int point_rast_rule:2; unsigned int line_endcap_aa_region_width:2; unsigned int line_width:4; unsigned int fast_scissor_disable:1; unsigned int cull_mode:2; unsigned int aa_enable:1; } sf6; struct { unsigned int point_size:11; unsigned int use_point_size_state:1; unsigned int subpixel_precision:1; unsigned int sprite_point:1; unsigned int pad0:11; unsigned int trifan_pv:2; unsigned int linestrip_pv:2; unsigned int tristrip_pv:2; unsigned int line_last_pixel_enable:1; } sf7; }; struct brw_gs_unit_state { struct thread0 thread0; struct thread1 thread1; struct thread2 thread2; struct thread3 thread3; struct { unsigned int pad0:10; unsigned int stats_enable:1; unsigned int nr_urb_entries:7; unsigned int pad1:1; unsigned int urb_entry_allocation_size:5; unsigned int pad2:1; unsigned int max_threads:1; unsigned int pad3:6; } thread4; struct { unsigned int sampler_count:3; unsigned int pad0:2; unsigned int sampler_state_pointer:27; } gs5; struct { unsigned int max_vp_index:4; unsigned int pad0:26; unsigned int reorder_enable:1; unsigned int pad1:1; } gs6; }; struct brw_vs_unit_state { struct thread0 thread0; struct thread1 thread1; struct thread2 thread2; struct thread3 thread3; struct { unsigned int pad0:10; unsigned int stats_enable:1; unsigned int nr_urb_entries:7; unsigned int pad1:1; unsigned int urb_entry_allocation_size:5; unsigned int pad2:1; unsigned int max_threads:4; unsigned int pad3:3; } thread4; struct { unsigned int sampler_count:3; unsigned int pad0:2; unsigned int sampler_state_pointer:27; } vs5; struct { unsigned int vs_enable:1; unsigned int vert_cache_disable:1; unsigned int pad0:30; } vs6; }; struct brw_wm_unit_state { struct thread0 thread0; struct thread1 thread1; struct thread2 thread2; struct thread3 thread3; struct { unsigned int stats_enable:1; unsigned int pad0:1; unsigned int sampler_count:3; unsigned int sampler_state_pointer:27; } wm4; struct { unsigned int enable_8_pix:1; unsigned int enable_16_pix:1; unsigned int enable_32_pix:1; unsigned int pad0:7; unsigned int legacy_global_depth_bias:1; unsigned int line_stipple:1; unsigned int depth_offset:1; unsigned int polygon_stipple:1; unsigned int line_aa_region_width:2; unsigned int line_endcap_aa_region_width:2; unsigned int early_depth_test:1; unsigned int thread_dispatch_enable:1; unsigned int program_uses_depth:1; unsigned int program_computes_depth:1; unsigned int program_uses_killpixel:1; unsigned int legacy_line_rast: 1; unsigned int transposed_urb_read:1; unsigned int max_threads:7; } wm5; float global_depth_offset_constant; float global_depth_offset_scale; }; /* The hardware supports two different modes for border color. The * default (OpenGL) mode uses floating-point color channels, while the * legacy mode uses 4 bytes. * * More significantly, the legacy mode respects the components of the * border color for channels not present in the source, (whereas the * default mode will ignore the border color's alpha channel and use * alpha==1 for an RGB source, for example). * * The legacy mode matches the semantics specified by the Render * extension. */ struct brw_sampler_default_border_color { float color[4]; }; struct brw_sampler_legacy_border_color { uint8_t color[4]; }; struct brw_sampler_state { struct { unsigned int shadow_function:3; unsigned int lod_bias:11; unsigned int min_filter:3; unsigned int mag_filter:3; unsigned int mip_filter:2; unsigned int base_level:5; unsigned int pad:1; unsigned int lod_preclamp:1; unsigned int border_color_mode:1; unsigned int pad0:1; unsigned int disable:1; } ss0; struct { unsigned int r_wrap_mode:3; unsigned int t_wrap_mode:3; unsigned int s_wrap_mode:3; unsigned int pad:3; unsigned int max_lod:10; unsigned int min_lod:10; } ss1; struct { unsigned int pad:5; unsigned int border_color_pointer:27; } ss2; struct { unsigned int pad:19; unsigned int max_aniso:3; unsigned int chroma_key_mode:1; unsigned int chroma_key_index:2; unsigned int chroma_key_enable:1; unsigned int monochrome_filter_width:3; unsigned int monochrome_filter_height:3; } ss3; }; struct brw_clipper_viewport { float xmin; float xmax; float ymin; float ymax; }; struct brw_cc_viewport { float min_depth; float max_depth; }; struct brw_sf_viewport { struct { float m00; float m11; float m22; float m30; float m31; float m32; } viewport; struct { short xmin; short ymin; short xmax; short ymax; } scissor; }; /* Documented in the subsystem/shared-functions/sampler chapter... */ struct brw_surface_state { struct { unsigned int cube_pos_z:1; unsigned int cube_neg_z:1; unsigned int cube_pos_y:1; unsigned int cube_neg_y:1; unsigned int cube_pos_x:1; unsigned int cube_neg_x:1; unsigned int pad:3; unsigned int render_cache_read_mode:1; unsigned int mipmap_layout_mode:1; unsigned int vert_line_stride_ofs:1; unsigned int vert_line_stride:1; unsigned int color_blend:1; unsigned int writedisable_blue:1; unsigned int writedisable_green:1; unsigned int writedisable_red:1; unsigned int writedisable_alpha:1; unsigned int surface_format:9; unsigned int data_return_format:1; unsigned int pad0:1; unsigned int surface_type:3; } ss0; struct { unsigned int base_addr; } ss1; struct { unsigned int render_target_rotation:2; unsigned int mip_count:4; unsigned int width:13; unsigned int height:13; } ss2; struct { unsigned int tile_walk:1; unsigned int tiled_surface:1; unsigned int pad:1; unsigned int pitch:18; unsigned int depth:11; } ss3; struct { unsigned int pad:19; unsigned int min_array_elt:9; unsigned int min_lod:4; } ss4; struct { unsigned int pad:20; unsigned int y_offset:4; unsigned int pad2:1; unsigned int x_offset:7; } ss5; }; struct brw_vertex_buffer_state { struct { unsigned int pitch:11; unsigned int pad:15; unsigned int access_type:1; unsigned int vb_index:5; } vb0; unsigned int start_addr; unsigned int max_index; #if 1 unsigned int instance_data_step_rate; /* not included for sequential/random vertices? */ #endif }; #define BRW_VBP_MAX 17 struct brw_vb_array_state { struct header header; struct brw_vertex_buffer_state vb[BRW_VBP_MAX]; }; struct brw_vertex_element_state { struct { unsigned int src_offset:11; unsigned int pad:5; unsigned int src_format:9; unsigned int pad0:1; unsigned int valid:1; unsigned int vertex_buffer_index:5; } ve0; struct { unsigned int dst_offset:8; unsigned int pad:8; unsigned int vfcomponent3:4; unsigned int vfcomponent2:4; unsigned int vfcomponent1:4; unsigned int vfcomponent0:4; } ve1; }; #define BRW_VEP_MAX 18 struct brw_vertex_element_packet { struct header header; struct brw_vertex_element_state ve[BRW_VEP_MAX]; }; struct brw_urb_immediate { unsigned int opcode:4; unsigned int offset:6; unsigned int swizzle_control:2; unsigned int pad:1; unsigned int allocate:1; unsigned int used:1; unsigned int complete:1; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; }; /* Instruction format for the execution units: */ struct brw_instruction { struct { unsigned int opcode:7; unsigned int pad:1; unsigned int access_mode:1; unsigned int mask_control:1; unsigned int dependency_control:2; unsigned int compression_control:2; unsigned int thread_control:2; unsigned int predicate_control:4; unsigned int predicate_inverse:1; unsigned int execution_size:3; unsigned int destreg__conditonalmod:4; /* destreg - send, conditionalmod - others */ unsigned int pad0:2; unsigned int debug_control:1; unsigned int saturate:1; } header; union { struct { unsigned int dest_reg_file:2; unsigned int dest_reg_type:3; unsigned int src0_reg_file:2; unsigned int src0_reg_type:3; unsigned int src1_reg_file:2; unsigned int src1_reg_type:3; unsigned int pad:1; unsigned int dest_subreg_nr:5; unsigned int dest_reg_nr:8; unsigned int dest_horiz_stride:2; unsigned int dest_address_mode:1; } da1; struct { unsigned int dest_reg_file:2; unsigned int dest_reg_type:3; unsigned int src0_reg_file:2; unsigned int src0_reg_type:3; unsigned int pad:6; int dest_indirect_offset:10; /* offset against the deref'd address reg */ unsigned int dest_subreg_nr:3; /* subnr for the address reg a0.x */ unsigned int dest_horiz_stride:2; unsigned int dest_address_mode:1; } ia1; struct { unsigned int dest_reg_file:2; unsigned int dest_reg_type:3; unsigned int src0_reg_file:2; unsigned int src0_reg_type:3; unsigned int src1_reg_file:2; unsigned int src1_reg_type:3; unsigned int pad0:1; unsigned int dest_writemask:4; unsigned int dest_subreg_nr:1; unsigned int dest_reg_nr:8; unsigned int pad1:2; unsigned int dest_address_mode:1; } da16; struct { unsigned int dest_reg_file:2; unsigned int dest_reg_type:3; unsigned int src0_reg_file:2; unsigned int src0_reg_type:3; unsigned int pad0:6; unsigned int dest_writemask:4; int dest_indirect_offset:6; unsigned int dest_subreg_nr:3; unsigned int pad1:2; unsigned int dest_address_mode:1; } ia16; } bits1; union { struct { unsigned int src0_subreg_nr:5; unsigned int src0_reg_nr:8; unsigned int src0_abs:1; unsigned int src0_negate:1; unsigned int src0_address_mode:1; unsigned int src0_horiz_stride:2; unsigned int src0_width:3; unsigned int src0_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad:6; } da1; struct { int src0_indirect_offset:10; unsigned int src0_subreg_nr:3; unsigned int src0_abs:1; unsigned int src0_negate:1; unsigned int src0_address_mode:1; unsigned int src0_horiz_stride:2; unsigned int src0_width:3; unsigned int src0_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad:6; } ia1; struct { unsigned int src0_swz_x:2; unsigned int src0_swz_y:2; unsigned int src0_subreg_nr:1; unsigned int src0_reg_nr:8; unsigned int src0_abs:1; unsigned int src0_negate:1; unsigned int src0_address_mode:1; unsigned int src0_swz_z:2; unsigned int src0_swz_w:2; unsigned int pad0:1; unsigned int src0_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad1:6; } da16; struct { unsigned int src0_swz_x:2; unsigned int src0_swz_y:2; int src0_indirect_offset:6; unsigned int src0_subreg_nr:3; unsigned int src0_abs:1; unsigned int src0_negate:1; unsigned int src0_address_mode:1; unsigned int src0_swz_z:2; unsigned int src0_swz_w:2; unsigned int pad0:1; unsigned int src0_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad1:6; } ia16; } bits2; union { struct { unsigned int src1_subreg_nr:5; unsigned int src1_reg_nr:8; unsigned int src1_abs:1; unsigned int src1_negate:1; unsigned int pad:1; unsigned int src1_horiz_stride:2; unsigned int src1_width:3; unsigned int src1_vert_stride:4; unsigned int pad0:7; } da1; struct { unsigned int src1_swz_x:2; unsigned int src1_swz_y:2; unsigned int src1_subreg_nr:1; unsigned int src1_reg_nr:8; unsigned int src1_abs:1; unsigned int src1_negate:1; unsigned int pad0:1; unsigned int src1_swz_z:2; unsigned int src1_swz_w:2; unsigned int pad1:1; unsigned int src1_vert_stride:4; unsigned int pad2:7; } da16; struct { int src1_indirect_offset:10; unsigned int src1_subreg_nr:3; unsigned int src1_abs:1; unsigned int src1_negate:1; unsigned int pad0:1; unsigned int src1_horiz_stride:2; unsigned int src1_width:3; unsigned int src1_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad1:6; } ia1; struct { unsigned int src1_swz_x:2; unsigned int src1_swz_y:2; int src1_indirect_offset:6; unsigned int src1_subreg_nr:3; unsigned int src1_abs:1; unsigned int src1_negate:1; unsigned int pad0:1; unsigned int src1_swz_z:2; unsigned int src1_swz_w:2; unsigned int pad1:1; unsigned int src1_vert_stride:4; unsigned int flag_reg_nr:1; unsigned int pad2:6; } ia16; struct { int jump_count:16; /* note: signed */ unsigned int pop_count:4; unsigned int pad0:12; } if_else; struct { unsigned int function:4; unsigned int int_type:1; unsigned int precision:1; unsigned int saturate:1; unsigned int data_type:1; unsigned int pad0:8; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; } math; struct { unsigned int binding_table_index:8; unsigned int sampler:4; unsigned int return_format:2; unsigned int msg_type:2; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; } sampler; struct { uint32_t binding_table_index:8; uint32_t sampler:4; uint32_t msg_type:4; uint32_t response_length:4; uint32_t msg_length:4; uint32_t msg_target:4; uint32_t pad1:3; uint32_t end_of_thread:1; } sampler_g4x; struct brw_urb_immediate urb; struct { unsigned int binding_table_index:8; unsigned int msg_control:4; unsigned int msg_type:2; unsigned int target_cache:2; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; } dp_read; struct { unsigned int binding_table_index:8; unsigned int msg_control:3; unsigned int pixel_scoreboard_clear:1; unsigned int msg_type:3; unsigned int send_commit_msg:1; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; } dp_write; struct { unsigned int pad:16; unsigned int response_length:4; unsigned int msg_length:4; unsigned int msg_target:4; unsigned int pad1:3; unsigned int end_of_thread:1; } generic; uint32_t ud; int32_t d; } bits3; }; /* media pipeline */ struct brw_vfe_state { struct { unsigned int per_thread_scratch_space:4; unsigned int pad3:3; unsigned int extend_vfe_state_present:1; unsigned int pad2:2; unsigned int scratch_base:22; } vfe0; struct { unsigned int debug_counter_control:2; unsigned int children_present:1; unsigned int vfe_mode:4; unsigned int pad2:2; unsigned int num_urb_entries:7; unsigned int urb_entry_alloc_size:9; unsigned int max_threads:7; } vfe1; struct { unsigned int pad4:4; unsigned int interface_descriptor_base:28; } vfe2; }; struct brw_vld_state { struct { unsigned int pad6:6; unsigned int scan_order:1; unsigned int intra_vlc_format:1; unsigned int quantizer_scale_type:1; unsigned int concealment_motion_vector:1; unsigned int frame_predict_frame_dct:1; unsigned int top_field_first:1; unsigned int picture_structure:2; unsigned int intra_dc_precision:2; unsigned int f_code_0_0:4; unsigned int f_code_0_1:4; unsigned int f_code_1_0:4; unsigned int f_code_1_1:4; } vld0; struct { unsigned int pad2:9; unsigned int picture_coding_type:2; unsigned int pad:21; } vld1; struct { unsigned int index_0:4; unsigned int index_1:4; unsigned int index_2:4; unsigned int index_3:4; unsigned int index_4:4; unsigned int index_5:4; unsigned int index_6:4; unsigned int index_7:4; } desc_remap_table0; struct { unsigned int index_8:4; unsigned int index_9:4; unsigned int index_10:4; unsigned int index_11:4; unsigned int index_12:4; unsigned int index_13:4; unsigned int index_14:4; unsigned int index_15:4; } desc_remap_table1; }; struct brw_interface_descriptor { struct { unsigned int grf_reg_blocks:4; unsigned int pad:2; unsigned int kernel_start_pointer:26; } desc0; struct { unsigned int pad:7; unsigned int software_exception:1; unsigned int pad2:3; unsigned int maskstack_exception:1; unsigned int pad3:1; unsigned int illegal_opcode_exception:1; unsigned int pad4:2; unsigned int floating_point_mode:1; unsigned int thread_priority:1; unsigned int single_program_flow:1; unsigned int pad5:1; unsigned int const_urb_entry_read_offset:6; unsigned int const_urb_entry_read_len:6; } desc1; struct { unsigned int pad:2; unsigned int sampler_count:3; unsigned int sampler_state_pointer:27; } desc2; struct { unsigned int binding_table_entry_count:5; unsigned int binding_table_pointer:27; } desc3; }; #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-command-private.h000066400000000000000000001041131271037650300313050ustar00rootroot00000000000000/************************************************************************** * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #ifndef CAIRO_DRM_INTEL_COMMAND_PRIVATE_H #define CAIRO_DRM_INTEL_COMMAND_PRIVATE_H #include "cairo-types-private.h" #define CMD_MI (0x0 << 29) #define CMD_misc (0x1 << 29) #define CMD_2D (0x2 << 29) #define CMD_3D (0x3 << 29) /* 4-7 reserved */ #define MI_NOOP (CMD_MI | 0) /* Batch */ #define MI_BATCH_BUFFER (CMD_MI | (0x30 << 23) | 1) #define MI_BATCH_BUFFER_START (CMD_MI | (0x31 << 23)) #define MI_BATCH_BUFFER_END (CMD_MI | (0x0a << 23)) #define MI_BATCH_NON_SECURE (1) #define MI_BATCH_NON_SECURE_I965 (1 << 8) /* Flush */ #define MI_FLUSH (CMD_MI | (0x04 << 23)) #define MI_WRITE_DIRTY_STATE (1<<4) #define MI_END_SCENE (1<<3) #define MI_GLOBAL_SNAPSHOT_COUNT_RESET (1<<3) #define MI_INHIBIT_RENDER_CACHE_FLUSH (1<<2) #define MI_STATE_INSTRUCTION_CACHE_FLUSH (1<<1) #define MI_INVALIDATE_MAP_CACHE (1<<0) #define PRIM3D (CMD_3D | (0x1f<<24)) #define PRIM3D_TRILIST (PRIM3D | (0x0<<18)) #define PRIM3D_TRISTRIP (PRIM3D | (0x1<<18)) #define PRIM3D_TRISTRIP_RVRSE (PRIM3D | (0x2<<18)) #define PRIM3D_TRIFAN (PRIM3D | (0x3<<18)) #define PRIM3D_POLY (PRIM3D | (0x4<<18)) #define PRIM3D_LINELIST (PRIM3D | (0x5<<18)) #define PRIM3D_LINESTRIP (PRIM3D | (0x6<<18)) #define PRIM3D_RECTLIST (PRIM3D | (0x7<<18)) #define PRIM3D_POINTLIST (PRIM3D | (0x8<<18)) #define PRIM3D_DIB (PRIM3D | (0x9<<18)) #define PRIM3D_CLEAR_RECT (PRIM3D | (0xa<<18)) #define PRIM3D_ZONE_INIT (PRIM3D | (0xd<<18)) #define PRIM3D_MASK (0x1f<<18) #define PRIM3D_INDIRECT_SEQUENTIAL ((1<<23) | (0<<17)) #define PRIM3D_INDIRECT_ELTS ((1<<23) | (1<<17)) /* p137 */ #define _3DSTATE_AA_CMD (CMD_3D | (0x06<<24)) #define AA_LINE_ECAAR_WIDTH_ENABLE (1<<16) #define AA_LINE_ECAAR_WIDTH_0_5 0 #define AA_LINE_ECAAR_WIDTH_1_0 (1<<14) #define AA_LINE_ECAAR_WIDTH_2_0 (2<<14) #define AA_LINE_ECAAR_WIDTH_4_0 (3<<14) #define AA_LINE_REGION_WIDTH_ENABLE (1<<8) #define AA_LINE_REGION_WIDTH_0_5 0 #define AA_LINE_REGION_WIDTH_1_0 (1<<6) #define AA_LINE_REGION_WIDTH_2_0 (2<<6) #define AA_LINE_REGION_WIDTH_4_0 (3<<6) /* 3DSTATE_BACKFACE_STENCIL_OPS, p138*/ #define _3DSTATE_BACKFACE_STENCIL_OPS (CMD_3D | (0x8<<24)) #define BFO_ENABLE_STENCIL_REF (1<<23) #define BFO_STENCIL_REF_SHIFT 15 #define BFO_STENCIL_REF_MASK (0xff<<15) #define BFO_ENABLE_STENCIL_FUNCS (1<<14) #define BFO_STENCIL_TEST_SHIFT 11 #define BFO_STENCIL_TEST_MASK (0x7<<11) #define BFO_STENCIL_FAIL_SHIFT 8 #define BFO_STENCIL_FAIL_MASK (0x7<<8) #define BFO_STENCIL_PASS_Z_FAIL_SHIFT 5 #define BFO_STENCIL_PASS_Z_FAIL_MASK (0x7<<5) #define BFO_STENCIL_PASS_Z_PASS_SHIFT 2 #define BFO_STENCIL_PASS_Z_PASS_MASK (0x7<<2) #define BFO_ENABLE_STENCIL_TWO_SIDE (1<<1) #define BFO_STENCIL_TWO_SIDE (1<<0) /* 3DSTATE_BACKFACE_STENCIL_MASKS, p140 */ #define _3DSTATE_BACKFACE_STENCIL_MASKS (CMD_3D | (0x9<<24)) #define BFM_ENABLE_STENCIL_TEST_MASK (1<<17) #define BFM_ENABLE_STENCIL_WRITE_MASK (1<<16) #define BFM_STENCIL_TEST_MASK_SHIFT 8 #define BFM_STENCIL_TEST_MASK_MASK (0xff<<8) #define BFM_STENCIL_WRITE_MASK_SHIFT 0 #define BFM_STENCIL_WRITE_MASK_MASK (0xff<<0) /* 3DSTATE_BIN_CONTROL p141 */ /* p143 */ #define _3DSTATE_BUF_INFO_CMD (CMD_3D | (0x1d<<24) | (0x8e<<16) | 1) /* Dword 1 */ #define BUF_3D_ID_COLOR_BACK (0x3<<24) #define BUF_3D_ID_DEPTH (0x7<<24) #define BUF_3D_USE_FENCE (1<<23) #define BUF_3D_TILED_SURFACE (1<<22) #define BUF_3D_TILE_WALK_X 0 #define BUF_3D_TILE_WALK_Y (1<<21) #define BUF_3D_PITCH(x) (x) /* Dword 2 */ #define BUF_3D_ADDR(x) ((x) & ~0x3) /* 3DSTATE_CHROMA_KEY */ /* 3DSTATE_CLEAR_PARAMETERS, p150 */ #define _3DSTATE_CLEAR_PARAMETERS (CMD_3D | (0x1d<<24) | (0x9c<<16) | 5) /* Dword 1 */ #define CLEARPARAM_CLEAR_RECT (1 << 16) #define CLEARPARAM_ZONE_INIT (0 << 16) #define CLEARPARAM_WRITE_COLOR (1 << 2) #define CLEARPARAM_WRITE_DEPTH (1 << 1) #define CLEARPARAM_WRITE_STENCIL (1 << 0) /* 3DSTATE_CONSTANT_BLEND_COLOR, p153 */ #define _3DSTATE_CONST_BLEND_COLOR_CMD (CMD_3D | (0x1d<<24) | (0x88<<16)) /* 3DSTATE_COORD_SET_BINDINGS, p154 */ #define _3DSTATE_COORD_SET_BINDINGS (CMD_3D | (0x16<<24)) #define CSB_TCB(iunit, eunit) ((eunit)<<(iunit*3)) /* p156 */ #define _3DSTATE_DFLT_DIFFUSE_CMD (CMD_3D | (0x1d<<24) | (0x99<<16)) /* p157 */ #define _3DSTATE_DFLT_SPEC_CMD (CMD_3D | (0x1d<<24) | (0x9a<<16)) /* p158 */ #define _3DSTATE_DFLT_Z_CMD (CMD_3D | (0x1d<<24) | (0x98<<16)) /* 3DSTATE_DEPTH_OFFSET_SCALE, p159 */ #define _3DSTATE_DEPTH_OFFSET_SCALE (CMD_3D | (0x1d<<24) | (0x97<<16)) /* scale in dword 1 */ /* The depth subrectangle is not supported, but must be disabled. */ /* 3DSTATE_DEPTH_SUBRECT_DISABLE, p160 */ #define _3DSTATE_DEPTH_SUBRECT_DISABLE (CMD_3D | (0x1c<<24) | (0x11<<19) | (1 << 1) | (0 << 0)) /* p161 */ #define _3DSTATE_DST_BUF_VARS_CMD (CMD_3D | (0x1d<<24) | (0x85<<16)) /* Dword 1 */ #define TEX_DEFAULT_COLOR_OGL (0<<30) #define TEX_DEFAULT_COLOR_D3D (1<<30) #define ZR_EARLY_DEPTH (1<<29) #define LOD_PRECLAMP_OGL (1<<28) #define LOD_PRECLAMP_D3D (0<<28) #define DITHER_FULL_ALWAYS (0<<26) #define DITHER_FULL_ON_FB_BLEND (1<<26) #define DITHER_CLAMPED_ALWAYS (2<<26) #define LINEAR_GAMMA_BLEND_32BPP (1<<25) #define DEBUG_DISABLE_ENH_DITHER (1<<24) #define DSTORG_HORT_BIAS(x) ((x)<<20) #define DSTORG_VERT_BIAS(x) ((x)<<16) #define COLOR_4_2_2_CHNL_WRT_ALL 0 #define COLOR_4_2_2_CHNL_WRT_Y (1<<12) #define COLOR_4_2_2_CHNL_WRT_CR (2<<12) #define COLOR_4_2_2_CHNL_WRT_CB (3<<12) #define COLOR_4_2_2_CHNL_WRT_CRCB (4<<12) #define COLR_BUF_8BIT 0 #define COLR_BUF_RGB555 (1<<8) #define COLR_BUF_RGB565 (2<<8) #define COLR_BUF_ARGB8888 (3<<8) #define COLR_BUF_ARGB4444 (8<<8) #define COLR_BUF_ARGB1555 (9<<8) #define COLR_BUF_ARGB2AAA (0xa<<8) #define DEPTH_FRMT_16_FIXED 0 #define DEPTH_FRMT_16_FLOAT (1<<2) #define DEPTH_FRMT_24_FIXED_8_OTHER (2<<2) #define VERT_LINE_STRIDE_1 (1<<1) #define VERT_LINE_STRIDE_0 (0<<1) #define VERT_LINE_STRIDE_OFS_1 1 #define VERT_LINE_STRIDE_OFS_0 0 /* p166 */ #define _3DSTATE_DRAW_RECT_CMD (CMD_3D|(0x1d<<24)|(0x80<<16)|3) /* Dword 1 */ #define DRAW_RECT_DIS_DEPTH_OFS (1<<30) #define DRAW_DITHER_OFS_X(x) ((x)<<26) #define DRAW_DITHER_OFS_Y(x) ((x)<<24) /* Dword 2 */ #define DRAW_YMIN(x) ((x)<<16) #define DRAW_XMIN(x) (x) /* Dword 3 */ #define DRAW_YMAX(x) ((x-1)<<16) #define DRAW_XMAX(x) (x-1) /* Dword 4 */ #define DRAW_YORG(x) ((x)<<16) #define DRAW_XORG(x) (x) /* 3DSTATE_FILTER_COEFFICIENTS_4X4, p170 */ /* 3DSTATE_FILTER_COEFFICIENTS_6X5, p172 */ /* _3DSTATE_FOG_COLOR, p173 */ #define _3DSTATE_FOG_COLOR_CMD (CMD_3D|(0x15<<24)) #define FOG_COLOR_RED(x) ((x)<<16) #define FOG_COLOR_GREEN(x) ((x)<<8) #define FOG_COLOR_BLUE(x) (x) /* _3DSTATE_FOG_MODE, p174 */ #define _3DSTATE_FOG_MODE_CMD (CMD_3D|(0x1d<<24)|(0x89<<16)|2) /* Dword 1 */ #define FMC1_FOGFUNC_MODIFY_ENABLE (1<<31) #define FMC1_FOGFUNC_VERTEX (0<<28) #define FMC1_FOGFUNC_PIXEL_EXP (1<<28) #define FMC1_FOGFUNC_PIXEL_EXP2 (2<<28) #define FMC1_FOGFUNC_PIXEL_LINEAR (3<<28) #define FMC1_FOGFUNC_MASK (3<<28) #define FMC1_FOGINDEX_MODIFY_ENABLE (1<<27) #define FMC1_FOGINDEX_Z (0<<25) #define FMC1_FOGINDEX_W (1<<25) #define FMC1_C1_C2_MODIFY_ENABLE (1<<24) #define FMC1_DENSITY_MODIFY_ENABLE (1<<23) #define FMC1_C1_ONE (1<<13) #define FMC1_C1_MASK (0xffff<<4) /* Dword 2 */ #define FMC2_C2_ONE (1<<16) /* Dword 3 */ #define FMC3_D_ONE (1<<16) /* _3DSTATE_INDEPENDENT_ALPHA_BLEND, p177 */ #define _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD (CMD_3D|(0x0b<<24)) #define IAB_MODIFY_ENABLE (1<<23) #define IAB_ENABLE (1<<22) #define IAB_MODIFY_FUNC (1<<21) #define IAB_FUNC_SHIFT 16 #define IAB_MODIFY_SRC_FACTOR (1<<11) #define IAB_SRC_FACTOR_SHIFT 6 #define IAB_SRC_FACTOR_MASK (BLENDFACT_MASK<<6) #define IAB_MODIFY_DST_FACTOR (1<<5) #define IAB_DST_FACTOR_SHIFT 0 #define IAB_DST_FACTOR_MASK (BLENDFACT_MASK<<0) #define BLENDFACT_ZERO 0x01 #define BLENDFACT_ONE 0x02 #define BLENDFACT_SRC_COLR 0x03 #define BLENDFACT_INV_SRC_COLR 0x04 #define BLENDFACT_SRC_ALPHA 0x05 #define BLENDFACT_INV_SRC_ALPHA 0x06 #define BLENDFACT_DST_ALPHA 0x07 #define BLENDFACT_INV_DST_ALPHA 0x08 #define BLENDFACT_DST_COLR 0x09 #define BLENDFACT_INV_DST_COLR 0x0a #define BLENDFACT_SRC_ALPHA_SATURATE 0x0b #define BLENDFACT_CONST_COLOR 0x0c #define BLENDFACT_INV_CONST_COLOR 0x0d #define BLENDFACT_CONST_ALPHA 0x0e #define BLENDFACT_INV_CONST_ALPHA 0x0f #define BLENDFACT_MASK 0x0f #define BLENDFUNC_ADD 0x0 #define BLENDFUNC_SUBTRACT 0x1 #define BLENDFUNC_REVERSE_SUBTRACT 0x2 #define BLENDFUNC_MIN 0x3 #define BLENDFUNC_MAX 0x4 #define BLENDFUNC_MASK 0x7 /* 3DSTATE_LOAD_INDIRECT, p180 */ #define _3DSTATE_LOAD_INDIRECT (CMD_3D|(0x1d<<24)|(0x7<<16)) #define LI0_STATE_STATIC_INDIRECT (0x01<<8) #define LI0_STATE_DYNAMIC_INDIRECT (0x02<<8) #define LI0_STATE_SAMPLER (0x04<<8) #define LI0_STATE_MAP (0x08<<8) #define LI0_STATE_PROGRAM (0x10<<8) #define LI0_STATE_CONSTANTS (0x20<<8) #define SIS0_BUFFER_ADDRESS(x) ((x)&~0x3) #define SIS0_FORCE_LOAD (1<<1) #define SIS0_BUFFER_VALID (1<<0) #define SIS1_BUFFER_LENGTH(x) ((x)&0xff) #define DIS0_BUFFER_ADDRESS(x) ((x)&~0x3) #define DIS0_BUFFER_RESET (1<<1) #define DIS0_BUFFER_VALID (1<<0) #define SSB0_BUFFER_ADDRESS(x) ((x)&~0x3) #define SSB0_FORCE_LOAD (1<<1) #define SSB0_BUFFER_VALID (1<<0) #define SSB1_BUFFER_LENGTH(x) ((x)&0xff) #define MSB0_BUFFER_ADDRESS(x) ((x)&~0x3) #define MSB0_FORCE_LOAD (1<<1) #define MSB0_BUFFER_VALID (1<<0) #define MSB1_BUFFER_LENGTH(x) ((x)&0xff) #define PSP0_BUFFER_ADDRESS(x) ((x)&~0x3) #define PSP0_FORCE_LOAD (1<<1) #define PSP0_BUFFER_VALID (1<<0) #define PSP1_BUFFER_LENGTH(x) ((x)&0xff) #define PSC0_BUFFER_ADDRESS(x) ((x)&~0x3) #define PSC0_FORCE_LOAD (1<<1) #define PSC0_BUFFER_VALID (1<<0) #define PSC1_BUFFER_LENGTH(x) ((x)&0xff) /* _3DSTATE_RASTERIZATION_RULES */ #define _3DSTATE_RASTER_RULES_CMD (CMD_3D|(0x07<<24)) #define ENABLE_POINT_RASTER_RULE (1<<15) #define OGL_POINT_RASTER_RULE (1<<13) #define ENABLE_TEXKILL_3D_4D (1<<10) #define TEXKILL_3D (0<<9) #define TEXKILL_4D (1<<9) #define ENABLE_LINE_STRIP_PROVOKE_VRTX (1<<8) #define ENABLE_TRI_FAN_PROVOKE_VRTX (1<<5) #define LINE_STRIP_PROVOKE_VRTX(x) ((x)<<6) #define TRI_FAN_PROVOKE_VRTX(x) ((x)<<3) /* _3DSTATE_SCISSOR_ENABLE, p256 */ #define _3DSTATE_SCISSOR_ENABLE_CMD (CMD_3D|(0x1c<<24)|(0x10<<19)) #define ENABLE_SCISSOR_RECT ((1<<1) | 1) #define DISABLE_SCISSOR_RECT (1<<1) /* _3DSTATE_SCISSOR_RECTANGLE_0, p257 */ #define _3DSTATE_SCISSOR_RECT_0_CMD (CMD_3D|(0x1d<<24)|(0x81<<16)|1) /* Dword 1 */ #define SCISSOR_RECT_0_YMIN(x) ((x)<<16) #define SCISSOR_RECT_0_XMIN(x) (x) /* Dword 2 */ #define SCISSOR_RECT_0_YMAX(x) ((x)<<16) #define SCISSOR_RECT_0_XMAX(x) (x) /* p189 */ #define _3DSTATE_LOAD_STATE_IMMEDIATE_1 (CMD_3D | (0x1d<<24) | (0x04<<16)) #define I1_LOAD_S(n) (1<<(4+n)) #define S0_VB_OFFSET_MASK 0xffffffc #define S0_AUTO_CACHE_INV_DISABLE (1<<0) #define S1_VERTEX_WIDTH_SHIFT 24 #define S1_VERTEX_WIDTH_MASK (0x3f<<24) #define S1_VERTEX_PITCH_SHIFT 16 #define S1_VERTEX_PITCH_MASK (0x3f<<16) #define TEXCOORDFMT_2D 0x0 #define TEXCOORDFMT_3D 0x1 #define TEXCOORDFMT_4D 0x2 #define TEXCOORDFMT_1D 0x3 #define TEXCOORDFMT_2D_16 0x4 #define TEXCOORDFMT_4D_16 0x5 #define TEXCOORDFMT_NOT_PRESENT 0xf #define S2_TEXCOORD_FMT0_MASK 0xf #define S2_TEXCOORD_FMT1_SHIFT 4 #define S2_TEXCOORD_FMT(unit, type) ((type)<<(unit*4)) #define S2_TEXCOORD_NONE (~0U) #define TEXCOORD_WRAP_SHORTEST_TCX 8 #define TEXCOORD_WRAP_SHORTEST_TCY 4 #define TEXCOORD_WRAP_SHORTEST_TCZ 2 #define TEXCOORD_PERSPECTIVE_DISABLE 1 #define S3_WRAP_SHORTEST_TCX(unit) (TEXCOORD_WRAP_SHORTEST_TCX << ((unit) * 4)) #define S3_WRAP_SHORTEST_TCY(unit) (TEXCOORD_WRAP_SHORTEST_TCY << ((unit) * 4)) #define S3_WRAP_SHORTEST_TCZ(unit) (TEXCOORD_WRAP_SHORTEST_TCZ << ((unit) * 4)) #define S3_PERSPECTIVE_DISABLE(unit) (TEXCOORD_PERSPECTIVE_DISABLE << ((unit) * 4)) /* S3 not interesting */ #define S4_POINT_WIDTH_SHIFT 23 #define S4_POINT_WIDTH_MASK (0x1ff<<23) #define S4_LINE_WIDTH_SHIFT 19 #define S4_LINE_WIDTH_ONE (0x2<<19) #define S4_LINE_WIDTH_MASK (0xf<<19) #define S4_FLATSHADE_ALPHA (1<<18) #define S4_FLATSHADE_FOG (1<<17) #define S4_FLATSHADE_SPECULAR (1<<16) #define S4_FLATSHADE_COLOR (1<<15) #define S4_CULLMODE_BOTH (0<<13) #define S4_CULLMODE_NONE (1<<13) #define S4_CULLMODE_CW (2<<13) #define S4_CULLMODE_CCW (3<<13) #define S4_CULLMODE_MASK (3<<13) #define S4_VFMT_POINT_WIDTH (1<<12) #define S4_VFMT_SPEC_FOG (1<<11) #define S4_VFMT_COLOR (1<<10) #define S4_VFMT_DEPTH_OFFSET (1<<9) #define S4_VFMT_XYZ (1<<6) #define S4_VFMT_XYZW (2<<6) #define S4_VFMT_XY (3<<6) #define S4_VFMT_XYW (4<<6) #define S4_VFMT_XYZW_MASK (7<<6) #define S4_FORCE_DEFAULT_DIFFUSE (1<<5) #define S4_FORCE_DEFAULT_SPECULAR (1<<4) #define S4_LOCAL_DEPTH_OFFSET_ENABLE (1<<3) #define S4_VFMT_FOG_PARAM (1<<2) #define S4_SPRITE_POINT_ENABLE (1<<1) #define S4_LINE_ANTIALIAS_ENABLE (1<<0) #define S4_VFMT_MASK (S4_VFMT_POINT_WIDTH | \ S4_VFMT_SPEC_FOG | \ S4_VFMT_COLOR | \ S4_VFMT_DEPTH_OFFSET | \ S4_VFMT_XYZW_MASK | \ S4_VFMT_FOG_PARAM) #define S5_WRITEDISABLE_ALPHA (1<<31) #define S5_WRITEDISABLE_RED (1<<30) #define S5_WRITEDISABLE_GREEN (1<<29) #define S5_WRITEDISABLE_BLUE (1<<28) #define S5_WRITEDISABLE_MASK (0xf<<28) #define S5_FORCE_DEFAULT_POINT_SIZE (1<<27) #define S5_LAST_PIXEL_ENABLE (1<<26) #define S5_GLOBAL_DEPTH_OFFSET_ENABLE (1<<25) #define S5_FOG_ENABLE (1<<24) #define S5_STENCIL_REF_SHIFT 16 #define S5_STENCIL_REF_MASK (0xff<<16) #define S5_STENCIL_TEST_FUNC_SHIFT 13 #define S5_STENCIL_TEST_FUNC_MASK (0x7<<13) #define S5_STENCIL_FAIL_SHIFT 10 #define S5_STENCIL_FAIL_MASK (0x7<<10) #define S5_STENCIL_PASS_Z_FAIL_SHIFT 7 #define S5_STENCIL_PASS_Z_FAIL_MASK (0x7<<7) #define S5_STENCIL_PASS_Z_PASS_SHIFT 4 #define S5_STENCIL_PASS_Z_PASS_MASK (0x7<<4) #define S5_STENCIL_WRITE_ENABLE (1<<3) #define S5_STENCIL_TEST_ENABLE (1<<2) #define S5_COLOR_DITHER_ENABLE (1<<1) #define S5_LOGICOP_ENABLE (1<<0) #define COMPAREFUNC_ALWAYS 0 #define COMPAREFUNC_NEVER 0x1 #define COMPAREFUNC_LESS 0x2 #define COMPAREFUNC_EQUAL 0x3 #define COMPAREFUNC_LEQUAL 0x4 #define COMPAREFUNC_GREATER 0x5 #define COMPAREFUNC_NOTEQUAL 0x6 #define COMPAREFUNC_GEQUAL 0x7 #define STENCILOP_KEEP 0 #define STENCILOP_ZERO 0x1 #define STENCILOP_REPLACE 0x2 #define STENCILOP_INCRSAT 0x3 #define STENCILOP_DECRSAT 0x4 #define STENCILOP_INCR 0x5 #define STENCILOP_DECR 0x6 #define STENCILOP_INVERT 0x7 #define S6_ALPHA_TEST_ENABLE (1<<31) #define S6_ALPHA_TEST_FUNC_SHIFT 28 #define S6_ALPHA_TEST_FUNC_MASK (0x7<<28) #define S6_ALPHA_REF_SHIFT 20 #define S6_ALPHA_REF_MASK (0xff<<20) #define S6_DEPTH_TEST_ENABLE (1<<19) #define S6_DEPTH_TEST_FUNC_SHIFT 16 #define S6_DEPTH_TEST_FUNC_MASK (0x7<<16) #define S6_CBUF_BLEND_ENABLE (1<<15) #define S6_CBUF_BLEND_FUNC_SHIFT 12 #define S6_CBUF_BLEND_FUNC_MASK (0x7<<12) #define S6_CBUF_SRC_BLEND_FACT_SHIFT 8 #define S6_CBUF_SRC_BLEND_FACT_MASK (0xf<<8) #define S6_CBUF_DST_BLEND_FACT_SHIFT 4 #define S6_CBUF_DST_BLEND_FACT_MASK (0xf<<4) #define S6_DEPTH_WRITE_ENABLE (1<<3) #define S6_COLOR_WRITE_ENABLE (1<<2) #define S6_TRISTRIP_PV_SHIFT 0 #define S6_TRISTRIP_PV_MASK (0x3<<0) #define S7_DEPTH_OFFSET_CONST_MASK ~0 /* 3DSTATE_MAP_DEINTERLACER_PARAMETERS */ /* 3DSTATE_MAP_PALETTE_LOAD_32, p206 */ /* _3DSTATE_MODES_4, p218 */ #define _3DSTATE_MODES_4_CMD (CMD_3D|(0x0d<<24)) #define ENABLE_LOGIC_OP_FUNC (1<<23) #define LOGIC_OP_FUNC(x) ((x)<<18) #define LOGICOP_MASK (0xf<<18) #define LOGICOP_COPY 0xc #define MODE4_ENABLE_STENCIL_TEST_MASK ((1<<17)|(0xff00)) #define ENABLE_STENCIL_TEST_MASK (1<<17) #define STENCIL_TEST_MASK(x) ((x)<<8) #define MODE4_ENABLE_STENCIL_WRITE_MASK ((1<<16)|(0x00ff)) #define ENABLE_STENCIL_WRITE_MASK (1<<16) #define STENCIL_WRITE_MASK(x) ((x)&0xff) /* _3DSTATE_MODES_5, p220 */ #define _3DSTATE_MODES_5_CMD (CMD_3D|(0x0c<<24)) #define PIPELINE_FLUSH_RENDER_CACHE (1<<18) #define PIPELINE_FLUSH_TEXTURE_CACHE (1<<16) /* p221 */ #define _3DSTATE_PIXEL_SHADER_CONSTANTS (CMD_3D|(0x1d<<24)|(0x6<<16)) #define PS1_REG(n) (1<<(n)) #define PS2_CONST_X(n) (n) #define PS3_CONST_Y(n) (n) #define PS4_CONST_Z(n) (n) #define PS5_CONST_W(n) (n) /* p222 */ #define I915_MAX_TEX_INDIRECT 4 #define I915_MAX_TEX_INSN 32 #define I915_MAX_ALU_INSN 64 #define I915_MAX_DECL_INSN 27 #define I915_MAX_TEMPORARY 16 /* Each instruction is 3 dwords long, though most don't require all * this space. Maximum of 123 instructions. Smaller maxes per insn * type. */ #define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16)) #define REG_TYPE_R 0 /* temporary regs, no need to * dcl, must be written before * read -- Preserved between * phases. */ #define REG_TYPE_T 1 /* Interpolated values, must be * dcl'ed before use. * * 0..7: texture coord, * 8: diffuse spec, * 9: specular color, * 10: fog parameter in w. */ #define REG_TYPE_CONST 2 /* Restriction: only one const * can be referenced per * instruction, though it may be * selected for multiple inputs. * Constants not initialized * default to zero. */ #define REG_TYPE_S 3 /* sampler */ #define REG_TYPE_OC 4 /* output color (rgba) */ #define REG_TYPE_OD 5 /* output depth (w), xyz are * temporaries. If not written, * interpolated depth is used? */ #define REG_TYPE_U 6 /* unpreserved temporaries */ #define REG_TYPE_MASK 0x7 #define REG_NR_MASK 0xf /* REG_TYPE_T: */ #define T_TEX0 0 #define T_TEX1 1 #define T_TEX2 2 #define T_TEX3 3 #define T_TEX4 4 #define T_TEX5 5 #define T_TEX6 6 #define T_TEX7 7 #define T_DIFFUSE 8 #define T_SPECULAR 9 #define T_FOG_W 10 /* interpolated fog is in W coord */ /* Arithmetic instructions */ /* .replicate_swizzle == selection and replication of a particular * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww */ #define A0_NOP (0x0<<24) /* no operation */ #define A0_ADD (0x1<<24) /* dst = src0 + src1 */ #define A0_MOV (0x2<<24) /* dst = src0 */ #define A0_MUL (0x3<<24) /* dst = src0 * src1 */ #define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */ #define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */ #define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */ #define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */ #define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */ #define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */ #define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */ #define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */ #define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */ #define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */ #define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */ #define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */ #define A0_FLR (0x10<<24) /* dst = floor(src0) */ #define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */ #define A0_TRC (0x12<<24) /* dst = int(src0) */ #define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */ #define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */ #define A0_DEST_SATURATE (1<<22) #define A0_DEST_TYPE_SHIFT 19 /* Allow: R, OC, OD, U */ #define A0_DEST_NR_SHIFT 14 /* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ #define A0_DEST_CHANNEL_X (1<<10) #define A0_DEST_CHANNEL_Y (2<<10) #define A0_DEST_CHANNEL_Z (4<<10) #define A0_DEST_CHANNEL_W (8<<10) #define A0_DEST_CHANNEL_ALL (0xf<<10) #define A0_DEST_CHANNEL_SHIFT 10 #define A0_SRC0_TYPE_SHIFT 7 #define A0_SRC0_NR_SHIFT 2 #define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y) #define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z) #define SRC_X 0 #define SRC_Y 1 #define SRC_Z 2 #define SRC_W 3 #define SRC_ZERO 4 #define SRC_ONE 5 #define A1_SRC0_CHANNEL_X_NEGATE (1<<31) #define A1_SRC0_CHANNEL_X_SHIFT 28 #define A1_SRC0_CHANNEL_Y_NEGATE (1<<27) #define A1_SRC0_CHANNEL_Y_SHIFT 24 #define A1_SRC0_CHANNEL_Z_NEGATE (1<<23) #define A1_SRC0_CHANNEL_Z_SHIFT 20 #define A1_SRC0_CHANNEL_W_NEGATE (1<<19) #define A1_SRC0_CHANNEL_W_SHIFT 16 #define A1_SRC1_TYPE_SHIFT 13 #define A1_SRC1_NR_SHIFT 8 #define A1_SRC1_CHANNEL_X_NEGATE (1<<7) #define A1_SRC1_CHANNEL_X_SHIFT 4 #define A1_SRC1_CHANNEL_Y_NEGATE (1<<3) #define A1_SRC1_CHANNEL_Y_SHIFT 0 #define A2_SRC1_CHANNEL_Z_NEGATE (1<<31) #define A2_SRC1_CHANNEL_Z_SHIFT 28 #define A2_SRC1_CHANNEL_W_NEGATE (1<<27) #define A2_SRC1_CHANNEL_W_SHIFT 24 #define A2_SRC2_TYPE_SHIFT 21 #define A2_SRC2_NR_SHIFT 16 #define A2_SRC2_CHANNEL_X_NEGATE (1<<15) #define A2_SRC2_CHANNEL_X_SHIFT 12 #define A2_SRC2_CHANNEL_Y_NEGATE (1<<11) #define A2_SRC2_CHANNEL_Y_SHIFT 8 #define A2_SRC2_CHANNEL_Z_NEGATE (1<<7) #define A2_SRC2_CHANNEL_Z_SHIFT 4 #define A2_SRC2_CHANNEL_W_NEGATE (1<<3) #define A2_SRC2_CHANNEL_W_SHIFT 0 /* Texture instructions */ #define T0_TEXLD (0x15<<24) /* Sample texture using predeclared * sampler and address, and output * filtered texel data to destination * register */ #define T0_TEXLDP (0x16<<24) /* Same as texld but performs a * perspective divide of the texture * coordinate .xyz values by .w before * sampling. */ #define T0_TEXLDB (0x17<<24) /* Same as texld but biases the * computed LOD by w. Only S4.6 two's * comp is used. This implies that a * float to fixed conversion is * done. */ #define T0_TEXKILL (0x18<<24) /* Does not perform a sampling * operation. Simply kills the pixel * if any channel of the address * register is < 0.0. */ #define T0_DEST_TYPE_SHIFT 19 /* Allow: R, OC, OD, U */ /* Note: U (unpreserved) regs do not retain their values between * phases (cannot be used for feedback) * * Note: oC and OD registers can only be used as the destination of a * texture instruction once per phase (this is an implementation * restriction). */ #define T0_DEST_NR_SHIFT 14 /* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ #define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */ #define T0_SAMPLER_NR_MASK (0xf<<0) #define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */ /* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */ #define T1_ADDRESS_REG_NR_SHIFT 17 #define T2_MBZ 0 /* Declaration instructions */ #define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib) * register or an s (sampler) * register. */ #define D0_SAMPLE_TYPE_SHIFT 22 #define D0_SAMPLE_TYPE_2D (0x0<<22) #define D0_SAMPLE_TYPE_CUBE (0x1<<22) #define D0_SAMPLE_TYPE_VOLUME (0x2<<22) #define D0_SAMPLE_TYPE_MASK (0x3<<22) #define D0_TYPE_SHIFT 19 /* Allow: T, S */ #define D0_NR_SHIFT 14 /* Allow T: 0..10, S: 0..15 */ #define D0_CHANNEL_X (1<<10) #define D0_CHANNEL_Y (2<<10) #define D0_CHANNEL_Z (4<<10) #define D0_CHANNEL_W (8<<10) #define D0_CHANNEL_ALL (0xf<<10) #define D0_CHANNEL_NONE (0<<10) #define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y) #define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z) /* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse * or specular declarations. * * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw) * * Must be zero for S (sampler) dcls */ #define D1_MBZ 0 #define D2_MBZ 0 /* p207. * The DWORD count is 3 times the number of bits set in MS1_MAPMASK_MASK */ #define _3DSTATE_MAP_STATE (CMD_3D|(0x1d<<24)|(0x0<<16)) #define MS1_MAPMASK_SHIFT 0 #define MS1_MAPMASK_MASK (0x8fff<<0) #define MS2_UNTRUSTED_SURFACE (1<<31) #define MS2_ADDRESS_MASK 0xfffffffc #define MS2_VERTICAL_LINE_STRIDE (1<<1) #define MS2_VERTICAL_OFFSET (1<<1) #define MS3_HEIGHT_SHIFT 21 #define MS3_WIDTH_SHIFT 10 #define MS3_PALETTE_SELECT (1<<9) #define MS3_MAPSURF_FORMAT_SHIFT 7 #define MS3_MAPSURF_FORMAT_MASK (0x7<<7) #define MAPSURF_8BIT (1<<7) #define MAPSURF_16BIT (2<<7) #define MAPSURF_32BIT (3<<7) #define MAPSURF_422 (5<<7) #define MAPSURF_COMPRESSED (6<<7) #define MAPSURF_4BIT_INDEXED (7<<7) #define MS3_MT_FORMAT_MASK (0x7 << 3) #define MS3_MT_FORMAT_SHIFT 3 #define MT_4BIT_IDX_ARGB8888 (7<<3) /* SURFACE_4BIT_INDEXED */ #define MT_8BIT_I8 (0<<3) /* SURFACE_8BIT */ #define MT_8BIT_L8 (1<<3) #define MT_8BIT_A8 (4<<3) #define MT_8BIT_MONO8 (5<<3) #define MT_16BIT_RGB565 (0<<3) /* SURFACE_16BIT */ #define MT_16BIT_ARGB1555 (1<<3) #define MT_16BIT_ARGB4444 (2<<3) #define MT_16BIT_AY88 (3<<3) #define MT_16BIT_88DVDU (5<<3) #define MT_16BIT_BUMP_655LDVDU (6<<3) #define MT_16BIT_I16 (7<<3) #define MT_16BIT_L16 (8<<3) #define MT_16BIT_A16 (9<<3) #define MT_32BIT_ARGB8888 (0<<3) /* SURFACE_32BIT */ #define MT_32BIT_ABGR8888 (1<<3) #define MT_32BIT_XRGB8888 (2<<3) #define MT_32BIT_XBGR8888 (3<<3) #define MT_32BIT_QWVU8888 (4<<3) #define MT_32BIT_AXVU8888 (5<<3) #define MT_32BIT_LXVU8888 (6<<3) #define MT_32BIT_XLVU8888 (7<<3) #define MT_32BIT_ARGB2101010 (8<<3) #define MT_32BIT_ABGR2101010 (9<<3) #define MT_32BIT_AWVU2101010 (0xA<<3) #define MT_32BIT_GR1616 (0xB<<3) #define MT_32BIT_VU1616 (0xC<<3) #define MT_32BIT_xI824 (0xD<<3) #define MT_32BIT_xA824 (0xE<<3) #define MT_32BIT_xL824 (0xF<<3) #define MT_422_YCRCB_SWAPY (0<<3) /* SURFACE_422 */ #define MT_422_YCRCB_NORMAL (1<<3) #define MT_422_YCRCB_SWAPUV (2<<3) #define MT_422_YCRCB_SWAPUVY (3<<3) #define MT_COMPRESS_DXT1 (0<<3) /* SURFACE_COMPRESSED */ #define MT_COMPRESS_DXT2_3 (1<<3) #define MT_COMPRESS_DXT4_5 (2<<3) #define MT_COMPRESS_FXT1 (3<<3) #define MT_COMPRESS_DXT1_RGB (4<<3) #define MS3_USE_FENCE_REGS (1<<2) #define MS3_TILED_SURFACE (1<<1) #define MS3_TILE_WALK (1<<0) /* The pitch is the pitch measured in DWORDS, minus 1 */ #define MS4_PITCH_SHIFT 21 #define MS4_CUBE_FACE_ENA_NEGX (1<<20) #define MS4_CUBE_FACE_ENA_POSX (1<<19) #define MS4_CUBE_FACE_ENA_NEGY (1<<18) #define MS4_CUBE_FACE_ENA_POSY (1<<17) #define MS4_CUBE_FACE_ENA_NEGZ (1<<16) #define MS4_CUBE_FACE_ENA_POSZ (1<<15) #define MS4_CUBE_FACE_ENA_MASK (0x3f<<15) #define MS4_MAX_LOD_SHIFT 9 #define MS4_MAX_LOD_MASK (0x3f<<9) #define MS4_MIP_LAYOUT_LEGACY (0<<8) #define MS4_MIP_LAYOUT_BELOW_LPT (0<<8) #define MS4_MIP_LAYOUT_RIGHT_LPT (1<<8) #define MS4_VOLUME_DEPTH_SHIFT 0 #define MS4_VOLUME_DEPTH_MASK (0xff<<0) /* p244. * The DWORD count is 3 times the number of bits set in SS1_MAPMASK_MASK. */ #define _3DSTATE_SAMPLER_STATE (CMD_3D|(0x1d<<24)|(0x1<<16)) #define SS1_MAPMASK_SHIFT 0 #define SS1_MAPMASK_MASK (0x8fff<<0) #define SS2_REVERSE_GAMMA_ENABLE (1<<31) #define SS2_PACKED_TO_PLANAR_ENABLE (1<<30) #define SS2_COLORSPACE_CONVERSION (1<<29) #define SS2_CHROMAKEY_SHIFT 27 #define SS2_BASE_MIP_LEVEL_SHIFT 22 #define SS2_BASE_MIP_LEVEL_MASK (0x1f<<22) #define SS2_MIP_FILTER_SHIFT 20 #define SS2_MIP_FILTER_MASK (0x3<<20) #define MIPFILTER_NONE 0 #define MIPFILTER_NEAREST 1 #define MIPFILTER_LINEAR 3 #define SS2_MAG_FILTER_SHIFT 17 #define SS2_MAG_FILTER_MASK (0x7<<17) #define FILTER_NEAREST 0 #define FILTER_LINEAR 1 #define FILTER_ANISOTROPIC 2 #define FILTER_4X4_1 3 #define FILTER_4X4_2 4 #define FILTER_4X4_FLAT 5 #define FILTER_6X5_MONO 6 /* XXX - check */ #define SS2_MIN_FILTER_SHIFT 14 #define SS2_MIN_FILTER_MASK (0x7<<14) #define SS2_LOD_BIAS_SHIFT 5 #define SS2_LOD_BIAS_ONE (0x10<<5) #define SS2_LOD_BIAS_MASK (0x1ff<<5) /* Shadow requires: * MT_X8{I,L,A}24 or MT_{I,L,A}16 texture format * FILTER_4X4_x MIN and MAG filters */ #define SS2_SHADOW_ENABLE (1<<4) #define SS2_MAX_ANISO_MASK (1<<3) #define SS2_MAX_ANISO_2 (0<<3) #define SS2_MAX_ANISO_4 (1<<3) #define SS2_SHADOW_FUNC_SHIFT 0 #define SS2_SHADOW_FUNC_MASK (0x7<<0) /* SS2_SHADOW_FUNC values: see COMPAREFUNC_* */ #define SS3_MIN_LOD_SHIFT 24 #define SS3_MIN_LOD_ONE (0x10<<24) #define SS3_MIN_LOD_MASK (0xff<<24) #define SS3_KILL_PIXEL_ENABLE (1<<17) #define SS3_TCX_ADDR_MODE_SHIFT 12 #define SS3_TCX_ADDR_MODE_MASK (0x7<<12) #define TEXCOORDMODE_WRAP 0 #define TEXCOORDMODE_MIRROR 1 #define TEXCOORDMODE_CLAMP_EDGE 2 #define TEXCOORDMODE_CUBE 3 #define TEXCOORDMODE_CLAMP_BORDER 4 #define TEXCOORDMODE_MIRROR_ONCE 5 #define SS3_TCY_ADDR_MODE_SHIFT 9 #define SS3_TCY_ADDR_MODE_MASK (0x7<<9) #define SS3_TCZ_ADDR_MODE_SHIFT 6 #define SS3_TCZ_ADDR_MODE_MASK (0x7<<6) #define SS3_NORMALIZED_COORDS (1<<5) #define SS3_TEXTUREMAP_INDEX_SHIFT 1 #define SS3_TEXTUREMAP_INDEX_MASK (0xf<<1) #define SS3_DEINTERLACER_ENABLE (1<<0) #define SS4_BORDER_COLOR_MASK (~0) /* 3DSTATE_SPAN_STIPPLE, p258 */ #define _3DSTATE_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) #define ST1_ENABLE (1<<16) #define ST1_MASK (0xffff) #define FLUSH_MAP_CACHE (1<<0) #define FLUSH_RENDER_CACHE (1<<1) /* BLT commands */ #define COLOR_BLT_CMD (CMD_2D | (0x40 << 22) | 3) #define XY_COLOR_BLT_CMD (CMD_2D | (0x50 << 22) | 4) #define XY_SETUP_CLIP_BLT_CMD (CMD_2D | (0x03 << 22) | 1) #define XY_SRC_COPY_BLT_CMD (CMD_2D | (0x53 << 22) | 6) #define SRC_COPY_BLT_CMD (CMD_2D | (0x43 << 22) | 4) #define XY_MONO_PAT_BLT_CMD (CMD_2D | (0x52<<22)|0x7) #define XY_MONO_PAT_VERT_SEED ((1<<10) | (1<<9)|(1<<8)) #define XY_MONO_PAT_HORT_SEED ((1<<14) | (1<<13)|(1<<12)) #define XY_MONO_SRC_BLT_CMD (CMD_2D | (0x54<<22)|(0x6)) #define XY_SETUP_BLT_CMD (CMD_2D | (0x01 << 22) | 6) #define XY_TEXT_IMMEDIATE_BLIT_CMD (CMD_2D | (0x31 << 22)) #define XY_TEXT_BYTE_PACKED (1 << 16) /* BR00 */ #define XY_BLT_WRITE_ALPHA (1 << 21) #define XY_BLT_WRITE_RGB (1 << 20) #define XY_SRC_TILED (1 << 15) #define XY_DST_TILED (1 << 11) /* BR13 */ #define BR13_565 (0x1 << 24) #define BR13_8888 (0x3 << 24) #endif /* CAIRO_DRM_INTEL_COMMAND_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-debug.c000066400000000000000000001005331271037650300273020ustar00rootroot00000000000000/************************************************************************** * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ #include "cairoint.h" #include "cairo-drm-intel-private.h" struct debug_stream { unsigned offset; /* current gtt offset */ const char *ptr; /* pointer to gtt offset zero */ const char *end; /* pointer to gtt offset zero */ }; static cairo_bool_t debug (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t i; const uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); if (len == 0) { fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]); ASSERT_NOT_REACHED; return FALSE; } fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); for (i = 0; i < len; i++) fprintf (stderr, "\t0x%08x\n", ptr[i]); fprintf (stderr, "\n"); stream->offset += len * sizeof(uint32_t); return TRUE; } static const char * get_prim_name (uint32_t val) { switch (val & PRIM3D_MASK) { case PRIM3D_TRILIST: return "TRILIST"; case PRIM3D_TRISTRIP: return "TRISTRIP"; case PRIM3D_TRISTRIP_RVRSE: return "TRISTRIP_RVRSE"; case PRIM3D_TRIFAN: return "TRIFAN"; case PRIM3D_POLY: return "POLY"; case PRIM3D_LINELIST: return "LINELIST"; case PRIM3D_LINESTRIP: return "LINESTRIP"; case PRIM3D_RECTLIST: return "RECTLIST"; case PRIM3D_POINTLIST: return "POINTLIST"; case PRIM3D_DIB: return "DIB"; case PRIM3D_CLEAR_RECT: return "CLEAR_RECT"; case PRIM3D_ZONE_INIT: return "ZONE_INIT"; default: return "????"; } } static cairo_bool_t debug_prim (struct debug_stream *stream, const char *name, cairo_bool_t dump_floats, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); const char *prim = get_prim_name( ptr[0] ); uint32_t i; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s %s (%d dwords):\n", name, prim, len); fprintf (stderr, "\t0x%08x\n", ptr[0]); for (i = 1; i < len; i++) { if (dump_floats) fprintf (stderr, "\t0x%08x // %f\n", ptr[i], *(float *)&ptr[i]); else fprintf (stderr, "\t0x%08x\n", ptr[i]); } fprintf (stderr, "\n"); stream->offset += len * sizeof(uint32_t); return TRUE; } static const char *opcodes[] = { "NOP", "ADD", "MOV", "MUL", "MAD", "DP2ADD", "DP3", "DP4", "FRC", "RCP", "RSQ", "EXP", "LOG", "CMP", "MIN", "MAX", "FLR", "MOD", "TRC", "SGE", "SLT", "TEXLD", "TEXLDP", "TEXLDB", "TEXKILL", "DCL", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f", }; static const int args[] = { 0, /* 0 nop */ 2, /* 1 add */ 1, /* 2 mov */ 2, /* 3 m ul */ 3, /* 4 mad */ 3, /* 5 dp2add */ 2, /* 6 dp3 */ 2, /* 7 dp4 */ 1, /* 8 frc */ 1, /* 9 rcp */ 1, /* a rsq */ 1, /* b exp */ 1, /* c log */ 3, /* d cmp */ 2, /* e min */ 2, /* f max */ 1, /* 10 flr */ 1, /* 11 mod */ 1, /* 12 trc */ 2, /* 13 sge */ 2, /* 14 slt */ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, }; static const char *regname[] = { "R", "T", "CONST", "S", "OC", "OD", "U", "UNKNOWN", }; static void print_reg_type_nr(uint32_t type, uint32_t nr) { switch (type) { case REG_TYPE_T: switch (nr) { case T_DIFFUSE: fprintf (stderr, "T_DIFFUSE"); return; case T_SPECULAR: fprintf (stderr, "T_SPECULAR"); return; case T_FOG_W: fprintf (stderr, "T_FOG_W"); return; default: fprintf (stderr, "T_TEX%d", nr); return; } case REG_TYPE_OC: if (nr == 0) { fprintf (stderr, "oC"); return; } break; case REG_TYPE_OD: if (nr == 0) { fprintf (stderr, "oD"); return; } break; default: break; } fprintf (stderr, "%s[%d]", regname[type], nr); } #define REG_SWIZZLE_MASK 0x7777 #define REG_NEGATE_MASK 0x8888 #define REG_SWIZZLE_XYZW ((SRC_X << A2_SRC2_CHANNEL_X_SHIFT) | \ (SRC_Y << A2_SRC2_CHANNEL_Y_SHIFT) | \ (SRC_Z << A2_SRC2_CHANNEL_Z_SHIFT) | \ (SRC_W << A2_SRC2_CHANNEL_W_SHIFT)) static void print_reg_neg_swizzle(uint32_t reg) { int i; if ((reg & REG_SWIZZLE_MASK) == REG_SWIZZLE_XYZW && (reg & REG_NEGATE_MASK) == 0) return; fprintf (stderr, "."); for (i = 12; i >= 0; i -= 4) { if (reg & (8 << i)) fprintf (stderr, "-"); switch ((reg >> i) & 0x7) { case 0: fprintf (stderr, "x"); break; case 1: fprintf (stderr, "y"); break; case 2: fprintf (stderr, "z"); break; case 3: fprintf (stderr, "w"); break; case 4: fprintf (stderr, "0"); break; case 5: fprintf (stderr, "1"); break; default: fprintf (stderr, "?"); break; } } } static void print_src_reg(uint32_t dword) { uint32_t nr = (dword >> A2_SRC2_NR_SHIFT) & REG_NR_MASK; uint32_t type = (dword >> A2_SRC2_TYPE_SHIFT) & REG_TYPE_MASK; print_reg_type_nr(type, nr); print_reg_neg_swizzle(dword); } static void print_dest_reg(uint32_t dword) { uint32_t nr = (dword >> A0_DEST_NR_SHIFT) & REG_NR_MASK; uint32_t type = (dword >> A0_DEST_TYPE_SHIFT) & REG_TYPE_MASK; print_reg_type_nr(type, nr); if ((dword & A0_DEST_CHANNEL_ALL) == A0_DEST_CHANNEL_ALL) return; fprintf (stderr, "."); if (dword & A0_DEST_CHANNEL_X) fprintf (stderr, "x"); if (dword & A0_DEST_CHANNEL_Y) fprintf (stderr, "y"); if (dword & A0_DEST_CHANNEL_Z) fprintf (stderr, "z"); if (dword & A0_DEST_CHANNEL_W) fprintf (stderr, "w"); } #define GET_SRC0_REG(r0, r1) ((r0<<14)|(r1>>A1_SRC0_CHANNEL_W_SHIFT)) #define GET_SRC1_REG(r0, r1) ((r0<<8)|(r1>>A2_SRC1_CHANNEL_W_SHIFT)) #define GET_SRC2_REG(r) (r) static void print_arith_op(uint32_t opcode, const uint32_t * program) { if (opcode != A0_NOP) { print_dest_reg(program[0]); if (program[0] & A0_DEST_SATURATE) fprintf (stderr, " = SATURATE "); else fprintf (stderr, " = "); } fprintf (stderr, "%s ", opcodes[opcode]); print_src_reg(GET_SRC0_REG(program[0], program[1])); if (args[opcode] == 1) { fprintf (stderr, "\n"); return; } fprintf (stderr, ", "); print_src_reg(GET_SRC1_REG(program[1], program[2])); if (args[opcode] == 2) { fprintf (stderr, "\n"); return; } fprintf (stderr, ", "); print_src_reg(GET_SRC2_REG(program[2])); fprintf (stderr, "\n"); return; } static void print_tex_op(uint32_t opcode, const uint32_t * program) { print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL); fprintf (stderr, " = "); fprintf (stderr, "%s ", opcodes[opcode]); fprintf (stderr, "S[%d],", program[0] & T0_SAMPLER_NR_MASK); print_reg_type_nr((program[1] >> T1_ADDRESS_REG_TYPE_SHIFT) & REG_TYPE_MASK, (program[1] >> T1_ADDRESS_REG_NR_SHIFT) & REG_NR_MASK); fprintf (stderr, "\n"); } static void print_dcl_op(uint32_t opcode, const uint32_t * program) { fprintf (stderr, "%s ", opcodes[opcode]); print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL); fprintf (stderr, "\n"); } static void i915_disassemble_program (const uint32_t * program, uint32_t sz) { uint32_t size = program[0] & 0x1ff; uint32_t i; fprintf (stderr, "\tPROGRAM\n"); assert(size + 2 == sz); program++; for (i = 1; i < sz; i += 3, program += 3) { uint32_t opcode = program[0] & (0x1f << 24); fprintf (stderr, "\t\t"); if ((int) opcode >= A0_NOP && opcode <= A0_SLT) print_arith_op(opcode >> 24, program); else if (opcode >= T0_TEXLD && opcode <= T0_TEXKILL) print_tex_op(opcode >> 24, program); else if (opcode == D0_DCL) print_dcl_op(opcode >> 24, program); else fprintf (stderr, "Unknown opcode 0x%x\n", opcode); } fprintf (stderr, "\tEND-PROGRAM\n\n"); } static cairo_bool_t debug_program (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); if (len == 0) { fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]); ASSERT_NOT_REACHED; return FALSE; } fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); i915_disassemble_program (ptr, len); stream->offset += len * sizeof(uint32_t); return TRUE; } static cairo_bool_t debug_chain (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t old_offset = stream->offset + len * sizeof(uint32_t); uint32_t i; fprintf (stderr, "%s (%d dwords):\n", name, len); for (i = 0; i < len; i++) fprintf (stderr, "\t0x%08x\n", ptr[i]); stream->offset = ptr[1] & ~0x3; if (stream->offset < old_offset) fprintf (stderr, "\n... skipping backwards from 0x%x --> 0x%x ...\n\n", old_offset, stream->offset ); else fprintf (stderr, "\n... skipping from 0x%x --> 0x%x ...\n\n", old_offset, stream->offset ); return TRUE; } static cairo_bool_t debug_variable_length_prim (struct debug_stream *stream) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); const char *prim = get_prim_name( ptr[0] ); uint32_t i, len; uint16_t *idx = (uint16_t *)(ptr+1); for (i = 0; idx[i] != 0xffff; i++) ; len = 1+(i+2)/2; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "3DPRIM, %s variable length %d indicies (%d dwords):\n", prim, i, len); for (i = 0; i < len; i++) fprintf (stderr, "\t0x%08x\n", ptr[i]); fprintf (stderr, "\n"); stream->offset += len * sizeof(uint32_t); return TRUE; } #define BITS(dw, hi, lo, ...) \ do { \ unsigned himask = 0xffffffffU >> (31 - (hi)); \ fprintf (stderr, "\t\t "); \ fprintf (stderr, __VA_ARGS__); \ fprintf (stderr, ": 0x%x\n", ((dw) & himask) >> (lo)); \ } while (0) #define MBZ(dw, hi, lo) do { \ unsigned x = (dw) >> (lo); \ unsigned lomask = (1 << (lo)) - 1; \ unsigned himask; \ himask = (1UL << (hi)) - 1; \ assert ((x & himask & ~lomask) == 0); \ } while (0) #define FLAG(dw, bit, ... ) \ do { \ if (((dw) >> (bit)) & 1) { \ fprintf (stderr, "\t\t "); \ fprintf (stderr, __VA_ARGS__); \ fprintf (stderr, "\n"); \ } \ } while (0) static cairo_bool_t debug_load_immediate (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t bits = (ptr[0] >> 4) & 0xff; uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords, flags: %x):\n", name, len, bits); fprintf (stderr, "\t0x%08x\n", ptr[j++]); if (bits & (1<<0)) { fprintf (stderr, "\t LIS0: 0x%08x\n", ptr[j]); fprintf (stderr, "\t vb address: 0x%08x\n", (ptr[j] & ~0x3)); BITS (ptr[j], 0, 0, "vb invalidate disable"); j++; } if (bits & (1<<1)) { fprintf (stderr, "\t LIS1: 0x%08x\n", ptr[j]); BITS (ptr[j], 29, 24, "vb dword width"); BITS (ptr[j], 21, 16, "vb dword pitch"); BITS (ptr[j], 15, 0, "vb max index"); j++; } if (bits & (1<<2)) { int i; fprintf (stderr, "\t LIS2: 0x%08x\n", ptr[j]); for (i = 0; i < 8; i++) { unsigned tc = (ptr[j] >> (i * 4)) & 0xf; if (tc != 0xf) BITS (tc, 3, 0, "tex coord %d", i); } j++; } if (bits & (1<<3)) { fprintf (stderr, "\t LIS3: 0x%08x\n", ptr[j]); j++; } if (bits & (1<<4)) { fprintf (stderr, "\t LIS4: 0x%08x\n", ptr[j]); BITS (ptr[j], 31, 23, "point width"); BITS (ptr[j], 22, 19, "line width"); FLAG (ptr[j], 18, "alpha flatshade"); FLAG (ptr[j], 17, "fog flatshade"); FLAG (ptr[j], 16, "spec flatshade"); FLAG (ptr[j], 15, "rgb flatshade"); BITS (ptr[j], 14, 13, "cull mode"); FLAG (ptr[j], 12, "vfmt: point width"); FLAG (ptr[j], 11, "vfmt: specular/fog"); FLAG (ptr[j], 10, "vfmt: rgba"); FLAG (ptr[j], 9, "vfmt: depth offset"); BITS (ptr[j], 8, 6, "vfmt: position (2==xyzw)"); FLAG (ptr[j], 5, "force dflt diffuse"); FLAG (ptr[j], 4, "force dflt specular"); FLAG (ptr[j], 3, "local depth offset enable"); FLAG (ptr[j], 2, "vfmt: fp32 fog coord"); FLAG (ptr[j], 1, "sprite point"); FLAG (ptr[j], 0, "antialiasing"); j++; } if (bits & (1<<5)) { fprintf (stderr, "\t LIS5: 0x%08x\n", ptr[j]); BITS (ptr[j], 31, 28, "rgba write disables"); FLAG (ptr[j], 27, "force dflt point width"); FLAG (ptr[j], 26, "last pixel enable"); FLAG (ptr[j], 25, "global z offset enable"); FLAG (ptr[j], 24, "fog enable"); BITS (ptr[j], 23, 16, "stencil ref"); BITS (ptr[j], 15, 13, "stencil test"); BITS (ptr[j], 12, 10, "stencil fail op"); BITS (ptr[j], 9, 7, "stencil pass z fail op"); BITS (ptr[j], 6, 4, "stencil pass z pass op"); FLAG (ptr[j], 3, "stencil write enable"); FLAG (ptr[j], 2, "stencil test enable"); FLAG (ptr[j], 1, "color dither enable"); FLAG (ptr[j], 0, "logiop enable"); j++; } if (bits & (1<<6)) { fprintf (stderr, "\t LIS6: 0x%08x\n", ptr[j]); FLAG (ptr[j], 31, "alpha test enable"); BITS (ptr[j], 30, 28, "alpha func"); BITS (ptr[j], 27, 20, "alpha ref"); FLAG (ptr[j], 19, "depth test enable"); BITS (ptr[j], 18, 16, "depth func"); FLAG (ptr[j], 15, "blend enable"); BITS (ptr[j], 14, 12, "blend func"); BITS (ptr[j], 11, 8, "blend src factor"); BITS (ptr[j], 7, 4, "blend dst factor"); FLAG (ptr[j], 3, "depth write enable"); FLAG (ptr[j], 2, "color write enable"); BITS (ptr[j], 1, 0, "provoking vertex"); j++; } fprintf (stderr, "\n"); assert(j == len); stream->offset += len * sizeof(uint32_t); return TRUE; } static cairo_bool_t debug_load_indirect (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t bits = (ptr[0] >> 8) & 0x3f; uint32_t i, j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); for (i = 0; i < 6; i++) { if (bits & (1<offset += len * sizeof(uint32_t); return TRUE; } static void BR13 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x\n", val); FLAG (val, 30, "clipping enable"); BITS (val, 25, 24, "color depth (3==32bpp)"); BITS (val, 23, 16, "raster op"); BITS (val, 15, 0, "dest pitch"); } static void BR2223 (struct debug_stream *stream, uint32_t val22, uint32_t val23) { union { uint32_t val; short field[2]; } BR22, BR23; BR22.val = val22; BR23.val = val23; fprintf (stderr, "\t0x%08x\n", val22); BITS (val22, 31, 16, "dest y1"); BITS (val22, 15, 0, "dest x1"); fprintf (stderr, "\t0x%08x\n", val23); BITS (val23, 31, 16, "dest y2"); BITS (val23, 15, 0, "dest x2"); /* The blit engine may produce unexpected results when these aren't met */ assert(BR22.field[0] < BR23.field[0]); assert(BR22.field[1] < BR23.field[1]); } static void BR09 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x -- dest address\n", val); } static void BR26 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x\n", val); BITS (val, 31, 16, "src y1"); BITS (val, 15, 0, "src x1"); } static void BR11 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x\n", val); BITS (val, 15, 0, "src pitch"); } static void BR12 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x -- src address\n", val); } static void BR16 (struct debug_stream *stream, uint32_t val) { fprintf (stderr, "\t0x%08x -- color\n", val); } static cairo_bool_t debug_copy_blit (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); BR13(stream, ptr[j++]); BR2223(stream, ptr[j], ptr[j+1]); j += 2; BR09(stream, ptr[j++]); BR26(stream, ptr[j++]); BR11(stream, ptr[j++]); BR12(stream, ptr[j++]); stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_color_blit (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); BR13(stream, ptr[j++]); BR2223(stream, ptr[j], ptr[j+1]); j += 2; BR09(stream, ptr[j++]); BR16(stream, ptr[j++]); stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_modes4 (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j]); BITS (ptr[j], 21, 18, "logicop func"); FLAG (ptr[j], 17, "stencil test mask modify-enable"); FLAG (ptr[j], 16, "stencil write mask modify-enable"); BITS (ptr[j], 15, 8, "stencil test mask"); BITS (ptr[j], 7, 0, "stencil write mask"); fprintf (stderr, "\n"); j++; stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_map_state (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); { fprintf (stderr, "\t0x%08x\n", ptr[j]); BITS (ptr[j], 15, 0, "map mask"); j++; } while (j < len) { { fprintf (stderr, "\t TMn.0: 0x%08x\n", ptr[j]); fprintf (stderr, "\t map address: 0x%08x\n", (ptr[j] & ~0x3)); FLAG (ptr[j], 1, "vertical line stride"); FLAG (ptr[j], 0, "vertical line stride offset"); j++; } { fprintf (stderr, "\t TMn.1: 0x%08x\n", ptr[j]); BITS (ptr[j], 31, 21, "height"); BITS (ptr[j], 20, 10, "width"); BITS (ptr[j], 9, 7, "surface format"); BITS (ptr[j], 6, 3, "texel format"); FLAG (ptr[j], 2, "use fence regs"); FLAG (ptr[j], 1, "tiled surface"); FLAG (ptr[j], 0, "tile walk ymajor"); j++; } { fprintf (stderr, "\t TMn.2: 0x%08x\n", ptr[j]); BITS (ptr[j], 31, 21, "dword pitch"); BITS (ptr[j], 20, 15, "cube face enables"); BITS (ptr[j], 14, 9, "max lod"); FLAG (ptr[j], 8, "mip layout right"); BITS (ptr[j], 7, 0, "depth"); j++; } } stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_sampler_state (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); { fprintf (stderr, "\t0x%08x\n", ptr[j]); BITS (ptr[j], 15, 0, "sampler mask"); j++; } while (j < len) { { fprintf (stderr, "\t TSn.0: 0x%08x\n", ptr[j]); FLAG (ptr[j], 31, "reverse gamma"); FLAG (ptr[j], 30, "planar to packed"); FLAG (ptr[j], 29, "yuv->rgb"); BITS (ptr[j], 28, 27, "chromakey index"); BITS (ptr[j], 26, 22, "base mip level"); BITS (ptr[j], 21, 20, "mip mode filter"); BITS (ptr[j], 19, 17, "mag mode filter"); BITS (ptr[j], 16, 14, "min mode filter"); BITS (ptr[j], 13, 5, "lod bias (s4.4)"); FLAG (ptr[j], 4, "shadow enable"); FLAG (ptr[j], 3, "max-aniso-4"); BITS (ptr[j], 2, 0, "shadow func"); j++; } { fprintf (stderr, "\t TSn.1: 0x%08x\n", ptr[j]); BITS (ptr[j], 31, 24, "min lod"); MBZ( ptr[j], 23, 18 ); FLAG (ptr[j], 17, "kill pixel enable"); FLAG (ptr[j], 16, "keyed tex filter mode"); FLAG (ptr[j], 15, "chromakey enable"); BITS (ptr[j], 14, 12, "tcx wrap mode"); BITS (ptr[j], 11, 9, "tcy wrap mode"); BITS (ptr[j], 8, 6, "tcz wrap mode"); FLAG (ptr[j], 5, "normalized coords"); BITS (ptr[j], 4, 1, "map (surface) index"); FLAG (ptr[j], 0, "EAST deinterlacer enable"); j++; } { fprintf (stderr, "\t TSn.2: 0x%08x (default color)\n", ptr[j]); j++; } } stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_dest_vars (struct debug_stream *stream, const char *name, uint32_t len) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); { fprintf (stderr, "\t0x%08x\n", ptr[j]); FLAG (ptr[j], 31, "early classic ztest"); FLAG (ptr[j], 30, "opengl tex default color"); FLAG (ptr[j], 29, "bypass iz"); FLAG (ptr[j], 28, "lod preclamp"); BITS (ptr[j], 27, 26, "dither pattern"); FLAG (ptr[j], 25, "linear gamma blend"); FLAG (ptr[j], 24, "debug dither"); BITS (ptr[j], 23, 20, "dstorg x"); BITS (ptr[j], 19, 16, "dstorg y"); MBZ (ptr[j], 15, 15 ); BITS (ptr[j], 14, 12, "422 write select"); BITS (ptr[j], 11, 8, "cbuf format"); BITS (ptr[j], 3, 2, "zbuf format"); FLAG (ptr[j], 1, "vert line stride"); FLAG (ptr[j], 1, "vert line stride offset"); j++; } stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t debug_buf_info( struct debug_stream *stream, const char *name, uint32_t len ) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t j = 0; fprintf (stderr, "%04x: ", stream->offset); fprintf (stderr, "%s (%d dwords):\n", name, len); fprintf (stderr, "\t0x%08x\n", ptr[j++]); { fprintf (stderr, "\t0x%08x\n", ptr[j]); BITS (ptr[j], 28, 28, "aux buffer id"); BITS (ptr[j], 27, 24, "buffer id (7=depth, 3=back)"); FLAG (ptr[j], 23, "use fence regs"); FLAG (ptr[j], 22, "tiled surface"); FLAG (ptr[j], 21, "tile walk ymajor"); MBZ (ptr[j], 20, 14); BITS (ptr[j], 13, 2, "dword pitch"); MBZ (ptr[j], 2, 0); j++; } fprintf (stderr, "\t0x%08x -- buffer base address\n", ptr[j++]); stream->offset += len * sizeof(uint32_t); assert(j == len); return TRUE; } static cairo_bool_t decode_3d_i915 (struct debug_stream *stream) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t cmd = *ptr; switch ((cmd >> 24) & 0x1f) { case 0x6: return debug (stream, "3DSTATE_ANTI_ALIASING", 1); case 0x7: return debug (stream, "3DSTATE_RASTERIZATION_RULES", 1); case 0x8: return debug (stream, "3DSTATE_BACKFACE_STENCIL_OPS", 1); case 0x9: return debug (stream, "3DSTATE_BACKFACE_STENCIL_MASKS", 1); case 0xb: return debug (stream, "3DSTATE_INDEPENDENT_ALPHA_BLEND", 1); case 0xc: return debug (stream, "3DSTATE_MODES5", 1); case 0xd: return debug_modes4(stream, "3DSTATE_MODES4", 1); case 0x15: return debug (stream, "3DSTATE_FOG_COLOR", 1); case 0x16: return debug (stream, "3DSTATE_COORD_SET_BINDINGS", 1); case 0x1c: /* 3DState16NP */ switch((cmd >> 19) & 0x1f) { case 0x10: return debug (stream, "3DSTATE_SCISSOR_ENABLE", 1); case 0x11: return debug (stream, "3DSTATE_DEPTH_SUBRECTANGLE_DISABLE", 1); default: break; } break; case 0x1d: /* 3DStateMW */ switch ((cmd >> 16) & 0xff) { case 0x0: return debug_map_state(stream, "3DSTATE_MAP_STATE", (cmd & 0x1f) + 2); case 0x1: return debug_sampler_state(stream, "3DSTATE_SAMPLER_STATE", (cmd & 0x1f) + 2); case 0x4: return debug_load_immediate(stream, "3DSTATE_LOAD_STATE_IMMEDIATE", (cmd & 0xf) + 2); case 0x5: return debug_program(stream, "3DSTATE_PIXEL_SHADER_PROGRAM", (cmd & 0x1ff) + 2); case 0x6: return debug (stream, "3DSTATE_PIXEL_SHADER_CONSTANTS", (cmd & 0xff) + 2); case 0x7: return debug_load_indirect(stream, "3DSTATE_LOAD_INDIRECT", (cmd & 0xff) + 2); case 0x80: return debug (stream, "3DSTATE_DRAWING_RECTANGLE", (cmd & 0xffff) + 2); case 0x81: return debug (stream, "3DSTATE_SCISSOR_RECTANGLE", (cmd & 0xffff) + 2); case 0x83: return debug (stream, "3DSTATE_SPAN_STIPPLE", (cmd & 0xffff) + 2); case 0x85: return debug_dest_vars(stream, "3DSTATE_DEST_BUFFER_VARS", (cmd & 0xffff) + 2); case 0x88: return debug (stream, "3DSTATE_CONSTANT_BLEND_COLOR", (cmd & 0xffff) + 2); case 0x89: return debug (stream, "3DSTATE_FOG_MODE", (cmd & 0xffff) + 2); case 0x8e: return debug_buf_info(stream, "3DSTATE_BUFFER_INFO", (cmd & 0xffff) + 2); case 0x97: return debug (stream, "3DSTATE_DEPTH_OFFSET_SCALE", (cmd & 0xffff) + 2); case 0x98: return debug (stream, "3DSTATE_DEFAULT_Z", (cmd & 0xffff) + 2); case 0x99: return debug (stream, "3DSTATE_DEFAULT_DIFFUSE", (cmd & 0xffff) + 2); case 0x9a: return debug (stream, "3DSTATE_DEFAULT_SPECULAR", (cmd & 0xffff) + 2); case 0x9c: return debug (stream, "3DSTATE_CLEAR_PARAMETERS", (cmd & 0xffff) + 2); default: ASSERT_NOT_REACHED; return 0; } break; case 0x1e: if (cmd & (1 << 23)) return debug (stream, "???", (cmd & 0xffff) + 1); else return debug (stream, "", 1); break; case 0x1f: if ((cmd & (1 << 23)) == 0) { return debug_prim (stream, "3DPRIM (inline)", 1, (cmd & 0x1ffff) + 2); } else if (cmd & (1 << 17)) { if ((cmd & 0xffff) == 0) return debug_variable_length_prim (stream); else return debug_prim (stream, "3DPRIM (indexed)", 0, (((cmd & 0xffff) + 1) / 2) + 1); } else return debug_prim (stream, "3DPRIM (indirect sequential)", 0, 2); break; default: return debug (stream, "", 0); } return FALSE; } static cairo_bool_t decode_3d_i965 (struct debug_stream *stream) { const uint32_t *data = (uint32_t *) (stream->ptr + stream->offset); const uint32_t opcode = (data[0] & 0xffff0000) >> 16; unsigned int idx; const struct { uint32_t opcode; int min_len; int max_len; const char *name; } opcodes_3d[] = { { 0x6000, 3, 3, "URB_FENCE" }, { 0x6001, 2, 2, "CS_URB_STATE" }, { 0x6002, 2, 2, "CONSTANT_BUFFER" }, { 0x6101, 6, 6, "STATE_BASE_ADDRESS" }, { 0x6102, 2, 2 , "STATE_SIP" }, { 0x6104, 1, 1, "3DSTATE_PIPELINE_SELECT" }, { 0x680b, 1, 1, "3DSTATE_VF_STATISTICS" }, { 0x6904, 1, 1, "3DSTATE_PIPELINE_SELECT" }, { 0x7800, 7, 7, "3DSTATE_PIPELINED_POINTERS" }, { 0x7801, 6, 6, "3DSTATE_BINDING_TABLE_POINTERS" }, { 0x780b, 1, 1, "3DSTATE_VF_STATISTICS" }, { 0x7808, 5, 257, "3DSTATE_VERTEX_BUFFERS" }, { 0x7809, 3, 256, "3DSTATE_VERTEX_ELEMENTS" }, { 0x780a, 3, 3, "3DSTATE_INDEX_BUFFER" }, { 0x7900, 4, 4, "3DSTATE_DRAWING_RECTANGLE" }, { 0x7901, 5, 5, "3DSTATE_CONSTANT_COLOR" }, { 0x7905, 5, 7, "3DSTATE_DEPTH_BUFFER" }, { 0x7906, 2, 2, "3DSTATE_POLY_STIPPLE_OFFSET" }, { 0x7907, 33, 33, "3DSTATE_POLY_STIPPLE_PATTERN" }, { 0x7908, 3, 3, "3DSTATE_LINE_STIPPLE" }, { 0x7909, 2, 2, "3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP" }, { 0x790a, 3, 3, "3DSTATE_AA_LINE_PARAMETERS" }, { 0x7b00, 6, 6, "3DPRIMITIVE" }, }, *opcode_3d; for (idx = 0; idx < ARRAY_LENGTH (opcodes_3d); idx++) { opcode_3d = &opcodes_3d[idx]; if (opcode == opcode_3d->opcode) { unsigned int len = 1; if (opcode_3d->max_len != 1) len = (data[0] & 0x000000ff) + 2; return debug (stream, opcode_3d->name, len); } } return FALSE; } static cairo_bool_t decode_3d_i830 (struct debug_stream *stream) { ASSERT_NOT_REACHED; return FALSE; } static cairo_bool_t i915_debug_packet (struct debug_stream *stream, int devid) { uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); uint32_t cmd = *ptr; switch (((cmd >> 29) & 0x7)) { case 0x0: switch ((cmd >> 23) & 0x3f) { case 0x0: return debug (stream, "MI_NOOP", 1); case 0x3: return debug (stream, "MI_WAIT_FOR_EVENT", 1); case 0x4: return debug (stream, "MI_FLUSH", 1); case 0xA: debug (stream, "MI_BATCH_BUFFER_END", 1); return FALSE; case 0x22: return debug (stream, "MI_LOAD_REGISTER_IMM", 3); case 0x31: return debug_chain(stream, "MI_BATCH_BUFFER_START", 2); default: break; } break; case 0x1: break; case 0x2: switch ((cmd >> 22) & 0xff) { case 0x50: return debug_color_blit(stream, "XY_COLOR_BLT", (cmd & 0xff) + 2); case 0x53: return debug_copy_blit(stream, "XY_SRC_COPY_BLT", (cmd & 0xff) + 2); default: return debug (stream, "blit command", (cmd & 0xff) + 2); } break; case 0x3: if (IS_965(devid)) return decode_3d_i965 (stream); else if (IS_9XX(devid)) return decode_3d_i915 (stream); else return decode_3d_i830 (stream); default: break; } fprintf (stderr, "Bogus cmd: %x [%x]\n", (cmd >> 29) & 7, cmd); ASSERT_NOT_REACHED; return 0; } void intel_dump_batchbuffer (const void *batch, uint32_t length, int devid) { struct debug_stream stream; cairo_bool_t done = FALSE; fprintf (stderr, "\nBATCH: (%d dwords)\n", length / 4); stream.offset = 0; stream.ptr = batch; while (! done && stream.offset < length) { if (! i915_debug_packet (&stream, devid)) break; assert (stream.offset <= length); } fprintf (stderr, "END-BATCH\n\n"); fflush (stderr); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-ioctl-private.h000066400000000000000000000306671271037650300310150ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #ifndef CAIRO_DRM_INTEL_IOCTL_PRIVATE_H #define CAIRO_DRM_INTEL_IOCTL_PRIVATE_H #include "cairo-drm-intel-command-private.h" #define I915_PARAM_IRQ_ACTIVE 1 #define I915_PARAM_ALLOW_BATCHBUFFER 2 #define I915_PARAM_LAST_DISPATCH 3 #define I915_PARAM_CHIPSET_ID 4 #define I915_PARAM_HAS_GEM 5 #define I915_PARAM_NUM_FENCES_AVAIL 6 #define I915_PARAM_HAS_OVERLAY 7 #define I915_PARAM_HAS_PAGEFLIPPING 8 #define I915_PARAM_HAS_EXECBUF2 9 struct intel_getparam { int param; int *value; }; /* @{ * Intel memory domains * * Most of these just align with the various caches in * the system and are used to flush and invalidate as * objects end up cached in different domains. */ /* CPU cache */ #define I915_GEM_DOMAIN_CPU 0x00000001 /* Render cache, used by 2D and 3D drawing */ #define I915_GEM_DOMAIN_RENDER 0x00000002 /* Sampler cache, used by texture engine */ #define I915_GEM_DOMAIN_SAMPLER 0x00000004 /* Command queue, used to load batch buffers */ #define I915_GEM_DOMAIN_COMMAND 0x00000008 /* Instruction cache, used by shader programs */ #define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 /* Vertex address cache */ #define I915_GEM_DOMAIN_VERTEX 0x00000020 /* GTT domain - aperture and scanout */ #define I915_GEM_DOMAIN_GTT 0x00000040 /* @} */ #define I915_TILING_NONE 0 #define I915_TILING_X 1 #define I915_TILING_Y 2 #define I915_BIT_6_SWIZZLE_NONE 0 #define I915_BIT_6_SWIZZLE_9 1 #define I915_BIT_6_SWIZZLE_9_10 2 #define I915_BIT_6_SWIZZLE_9_11 3 #define I915_BIT_6_SWIZZLE_9_10_11 4 #define DRM_I915_GEM_EXECBUFFER 0x14 #define DRM_I915_GEM_BUSY 0x17 #define DRM_I915_GEM_THROTTLE 0x18 #define DRM_I915_GEM_CREATE 0x1b #define DRM_I915_GEM_PREAD 0x1c #define DRM_I915_GEM_PWRITE 0x1d #define DRM_I915_GEM_MMAP 0x1e #define DRM_I915_GEM_SET_DOMAIN 0x1f #define DRM_I915_GEM_SET_TILING 0x21 #define DRM_I915_GEM_GET_TILING 0x22 #define DRM_I915_GEM_GET_APERTURE 0x23 #define DRM_I915_GEM_MMAP_GTT 0x24 struct drm_i915_gem_create { /* * Requested size for the object. * * The (page-aligned) allocated size for the object will be returned. */ uint64_t size; /* * Returned handle for the object. * * Object handles are nonzero. */ uint32_t handle; uint32_t pad; }; struct drm_i915_gem_pread { /* Handle for the object being read. */ uint32_t handle; uint32_t pad; /* Offset into the object to read from */ uint64_t offset; /* Length of data to read */ uint64_t size; /* * Pointer to write the data into. * * This is a fixed-size type for 32/64 compatibility. */ uint64_t data_ptr; }; struct drm_i915_gem_pwrite { /* Handle for the object being written to. */ uint32_t handle; uint32_t pad; /* Offset into the object to write to */ uint64_t offset; /* Length of data to write */ uint64_t size; /* * Pointer to read the data from. * * This is a fixed-size type for 32/64 compatibility. */ uint64_t data_ptr; }; struct drm_i915_gem_mmap { /* Handle for the object being mapped. */ uint32_t handle; uint32_t pad; /* Offset in the object to map. */ uint64_t offset; /* * Length of data to map. * * The value will be page-aligned. */ uint64_t size; /* * Returned pointer the data was mapped at. * * This is a fixed-size type for 32/64 compatibility. */ uint64_t addr_ptr; }; struct drm_i915_gem_mmap_gtt { /* Handle for the object being mapped. */ uint32_t handle; uint32_t pad; /* * Fake offset to use for subsequent mmap call * * This is a fixed-size type for 32/64 compatibility. */ uint64_t offset; }; struct drm_i915_gem_set_domain { /* Handle for the object */ uint32_t handle; /* New read domains */ uint32_t read_domains; /* New write domain */ uint32_t write_domain; }; struct drm_i915_gem_relocation_entry { /* * Handle of the buffer being pointed to by this relocation entry. * * It's appealing to make this be an index into the mm_validate_entry * list to refer to the buffer, but this allows the driver to create * a relocation list for state buffers and not re-write it per * exec using the buffer. */ uint32_t target_handle; /* * Value to be added to the offset of the target buffer to make up * the relocation entry. */ uint32_t delta; /* Offset in the buffer the relocation entry will be written into */ uint64_t offset; /* * Offset value of the target buffer that the relocation entry was last * written as. * * If the buffer has the same offset as last time, we can skip syncing * and writing the relocation. This value is written back out by * the execbuffer ioctl when the relocation is written. */ uint64_t presumed_offset; /* * Target memory domains read by this operation. */ uint32_t read_domains; /* * Target memory domains written by this operation. * * Note that only one domain may be written by the whole * execbuffer operation, so that where there are conflicts, * the application will get -EINVAL back. */ uint32_t write_domain; }; struct drm_i915_gem_exec_object { /* * User's handle for a buffer to be bound into the GTT for this * operation. */ uint32_t handle; /* Number of relocations to be performed on this buffer */ uint32_t relocation_count; /* * Pointer to array of struct drm_i915_gem_relocation_entry containing * the relocations to be performed in this buffer. */ uint64_t relocs_ptr; /* Required alignment in graphics aperture */ uint64_t alignment; /* * Returned value of the updated offset of the object, for future * presumed_offset writes. */ uint64_t offset; }; struct drm_i915_gem_execbuffer { /* * List of buffers to be validated with their relocations to be * performend on them. * * This is a pointer to an array of struct drm_i915_gem_validate_entry. * * These buffers must be listed in an order such that all relocations * a buffer is performing refer to buffers that have already appeared * in the validate list. */ uint64_t buffers_ptr; uint32_t buffer_count; /* Offset in the batchbuffer to start execution from. */ uint32_t batch_start_offset; /* Bytes used in batchbuffer from batch_start_offset */ uint32_t batch_len; uint32_t DR1; uint32_t DR4; uint32_t num_cliprects; /* This is a struct drm_clip_rect *cliprects */ uint64_t cliprects_ptr; }; struct drm_i915_gem_busy { /* Handle of the buffer to check for busy */ uint32_t handle; /* Return busy status (1 if busy, 0 if idle) */ uint32_t busy; }; struct drm_i915_gem_set_tiling { /* Handle of the buffer to have its tiling state updated */ uint32_t handle; /* * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, * I915_TILING_Y). * * This value is to be set on request, and will be updated by the * kernel on successful return with the actual chosen tiling layout. * * The tiling mode may be demoted to I915_TILING_NONE when the system * has bit 6 swizzling that can't be managed correctly by GEM. * * Buffer contents become undefined when changing tiling_mode. */ uint32_t tiling_mode; /* * Stride in bytes for the object when in I915_TILING_X or * I915_TILING_Y. */ uint32_t stride; /* * Returned address bit 6 swizzling required for CPU access through * mmap mapping. */ uint32_t swizzle_mode; }; struct drm_i915_gem_get_tiling { /* Handle of the buffer to get tiling state for. */ uint32_t handle; /* * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, * I915_TILING_Y). */ uint32_t tiling_mode; /* * Returned address bit 6 swizzling required for CPU access through * mmap mapping. */ uint32_t swizzle_mode; }; struct drm_i915_gem_get_aperture { /* Total size of the aperture used by i915_gem_execbuffer, in bytes */ uint64_t aper_size; /* * Available space in the aperture used by i915_gem_execbuffer, in * bytes */ uint64_t aper_available_size; }; #define DRM_I915_GETPARAM 0x06 #define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, struct intel_getparam) #define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) #define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) #define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) #define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) #define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) #define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) #define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) #define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) #define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) #define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) #define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) #define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) #define I915_MADV_WILLNEED 0 #define I915_MADV_DONTNEED 1 struct drm_i915_gem_madvise { uint32_t handle; uint32_t madv; uint32_t retained; }; #define DRM_I915_GEM_MADVISE 0x26 #define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) /* XXX execbuffer2 */ struct drm_i915_gem_exec_object2 { /* * User's handle for a buffer to be bound into the GTT for this * operation. */ uint32_t handle; /* Number of relocations to be performed on this buffer */ uint32_t relocation_count; /* * Pointer to array of struct drm_i915_gem_relocation_entry containing * the relocations to be performed in this buffer. */ uint64_t relocs_ptr; /* Required alignment in graphics aperture */ uint64_t alignment; /* * Returned value of the updated offset of the object, for future * presumed_offset writes. */ uint64_t offset; #define EXEC_OBJECT_NEEDS_FENCE (1<<0) uint64_t flags; uint64_t rsvd1; uint64_t rsvd2; }; struct drm_i915_gem_execbuffer2 { /* * List of gem_exec_object2 structs */ uint64_t buffers_ptr; uint32_t buffer_count; /* Offset in the batchbuffer to start execution from. */ uint32_t batch_start_offset; /* Bytes used in batchbuffer from batch_start_offset */ uint32_t batch_len; uint32_t DR1; uint32_t DR4; uint32_t num_cliprects; /* This is a struct drm_clip_rect *cliprects */ uint64_t cliprects_ptr; uint64_t flags; uint64_t rsvd1; uint64_t rsvd2; }; #define I915_GEM_3D_PIPELINE 0x1 #define I915_GEM_MEDIA_PIPELINE 0x2 #define DRM_I915_GEM_EXECBUFFER2 0x29 #define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) struct drm_i915_gem_real_size { uint32_t handle; uint64_t size; }; #define DRM_I915_GEM_REAL_SIZE 0x2a #define DRM_IOCTL_I915_GEM_REAL_SIZE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_REAL_SIZE, struct drm_i915_gem_real_size) #endif /* CAIRO_DRM_INTEL_IOCTL_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-private.h000066400000000000000000000340451271037650300276770ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #ifndef CAIRO_DRM_INTEL_PRIVATE_H #define CAIRO_DRM_INTEL_PRIVATE_H #include "cairoint.h" #include "cairo-cache-private.h" #include "cairo-compiler-private.h" #include "cairo-drm-private.h" #include "cairo-freelist-private.h" #include "cairo-list-private.h" #include "cairo-mutex-private.h" #include "cairo-rtree-private.h" #include "cairo-types-private.h" #include "cairo-drm-intel-ioctl-private.h" #define INTEL_TILING_DEFAULT I915_TILING_Y #define INTEL_BO_CACHE_BUCKETS 12 /* cache surfaces up to 16 MiB */ #define INTEL_GLYPH_CACHE_WIDTH 1024 #define INTEL_GLYPH_CACHE_HEIGHT 1024 #define INTEL_GLYPH_CACHE_MIN_SIZE 1 #define INTEL_GLYPH_CACHE_MAX_SIZE 128 typedef struct _intel_bo { cairo_drm_bo_t base; cairo_list_t link; cairo_list_t cache_list; uint32_t offset; uint32_t batch_read_domains; uint32_t batch_write_domain; uint32_t opaque0; uint32_t opaque1; uint32_t full_size; uint16_t stride; uint16_t _stride; uint32_t tiling :4; uint32_t _tiling :4; uint32_t purgeable :1; uint32_t busy :1; uint32_t cpu :1; struct drm_i915_gem_exec_object2 *exec; void *virtual; } intel_bo_t; #define INTEL_BATCH_SIZE (64*1024) #define INTEL_VERTEX_BUFFER_SIZE (512*1024) #define INTEL_MAX_RELOCS 2048 static inline void intel_bo_mark_purgeable (intel_bo_t *bo) { if (bo->base.name == 0) bo->purgeable = 1; } typedef struct _intel_vertex_buffer intel_vertex_buffer_t; typedef void (*intel_vertex_buffer_new_func_t) (intel_vertex_buffer_t *vertex_buffer); typedef void (*intel_vertex_buffer_start_rectangles_func_t) (intel_vertex_buffer_t *vertex_buffer, uint32_t floats_per_vertex); typedef void (*intel_vertex_buffer_flush_func_t) (intel_vertex_buffer_t *vertex_buffer); typedef void (*intel_vertex_buffer_finish_func_t) (intel_vertex_buffer_t *vertex_buffer); struct _intel_vertex_buffer { uint32_t vbo_batch; /* reloc position in batch, 0 -> not yet allocated */ uint32_t vbo_offset; uint32_t vbo_used; uint32_t vertex_index; uint32_t vertex_count; uint32_t floats_per_vertex; uint32_t rectangle_size; intel_bo_t *last_vbo; uint32_t last_vbo_offset; uint32_t last_vbo_space; intel_vertex_buffer_new_func_t new; intel_vertex_buffer_start_rectangles_func_t start_rectangles; intel_vertex_buffer_flush_func_t flush; intel_vertex_buffer_finish_func_t finish; uint32_t base[INTEL_VERTEX_BUFFER_SIZE / sizeof (uint32_t)]; }; typedef struct _intel_batch intel_batch_t; typedef void (*intel_batch_commit_func_t) (intel_batch_t *batch); typedef void (*intel_batch_reset_func_t) (intel_batch_t *batch); struct _intel_batch { size_t gtt_size; size_t gtt_avail_size; intel_batch_commit_func_t commit; intel_batch_reset_func_t reset; uint16_t exec_count; uint16_t reloc_count; uint16_t used; uint16_t header; intel_bo_t *target_bo[INTEL_MAX_RELOCS]; struct drm_i915_gem_exec_object2 exec[INTEL_MAX_RELOCS]; struct drm_i915_gem_relocation_entry reloc[INTEL_MAX_RELOCS]; uint32_t base[INTEL_BATCH_SIZE / sizeof (uint32_t)]; intel_vertex_buffer_t vertex_buffer; }; typedef struct _intel_buffer { intel_bo_t *bo; uint32_t offset; cairo_format_t format; uint32_t map0, map1; uint32_t width; uint32_t height; uint32_t stride; } intel_buffer_t; typedef struct _intel_buffer_cache { int ref_count; intel_buffer_t buffer; cairo_rtree_t rtree; cairo_list_t link; } intel_buffer_cache_t; typedef struct _intel_glyph { cairo_rtree_node_t node; intel_buffer_cache_t *cache; void **owner; float texcoord[3]; int width, height; } intel_glyph_t; typedef struct _intel_gradient_cache { cairo_pattern_union_t pattern; intel_buffer_t buffer; } intel_gradient_cache_t; #define GRADIENT_CACHE_SIZE 16 typedef struct _intel_surface { cairo_drm_surface_t drm; cairo_cache_entry_t snapshot_cache_entry; } intel_surface_t; typedef void (*intel_reset_context_func_t) (void *device); typedef struct _intel_device { cairo_drm_device_t base; size_t gtt_max_size; size_t gtt_avail_size; cairo_freepool_t bo_pool; cairo_list_t bo_in_flight; cairo_mutex_t mutex; intel_batch_t batch; intel_buffer_cache_t glyph_cache[2]; cairo_list_t fonts; struct { intel_gradient_cache_t cache[GRADIENT_CACHE_SIZE]; unsigned int size; } gradient_cache; cairo_cache_t snapshot_cache; size_t snapshot_cache_max_size; intel_reset_context_func_t reset_context; cairo_status_t (*flush) (struct _intel_device *); } intel_device_t; static inline intel_device_t * to_intel_device (cairo_device_t *base) { return (intel_device_t *) base; } static inline intel_bo_t * to_intel_bo (cairo_drm_bo_t *base) { return (intel_bo_t *) base; } static inline intel_bo_t * intel_bo_reference (intel_bo_t *bo) { return to_intel_bo (cairo_drm_bo_reference (&bo->base)); } cairo_private cairo_bool_t intel_bo_madvise (intel_device_t *device, intel_bo_t *bo, int madv); static cairo_always_inline void intel_bo_destroy (intel_device_t *device, intel_bo_t *bo) { cairo_drm_bo_destroy (&device->base.base, &bo->base); } static inline void intel_bo_in_flight_add (intel_device_t *device, intel_bo_t *bo) { if (bo->base.name == 0 && bo->exec != NULL && cairo_list_is_empty (&bo->cache_list)) cairo_list_add (&bo->cache_list, &device->bo_in_flight); } cairo_private int intel_get (int fd, int param); cairo_private cairo_bool_t intel_info (int fd, uint64_t *gtt_size); cairo_private cairo_status_t intel_device_init (intel_device_t *device, int fd); cairo_private void intel_device_fini (intel_device_t *dev); cairo_private intel_bo_t * intel_bo_create (intel_device_t *dev, uint32_t max_size, uint32_t real_size, cairo_bool_t gpu_target, uint32_t tiling, uint32_t stride); cairo_private intel_bo_t * intel_bo_create_for_name (intel_device_t *dev, uint32_t name); cairo_private void intel_bo_set_tiling (const intel_device_t *dev, intel_bo_t *bo); cairo_private cairo_bool_t intel_bo_is_inactive (const intel_device_t *device, intel_bo_t *bo); cairo_private cairo_bool_t intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo); cairo_private void intel_bo_write (const intel_device_t *dev, intel_bo_t *bo, unsigned long offset, unsigned long size, const void *data); cairo_private void intel_bo_read (const intel_device_t *dev, intel_bo_t *bo, unsigned long offset, unsigned long size, void *data); cairo_private void * intel_bo_map (const intel_device_t *dev, intel_bo_t *bo); cairo_private void intel_bo_unmap (intel_bo_t *bo); cairo_private cairo_status_t intel_bo_init (const intel_device_t *dev, intel_bo_t *bo, uint32_t size, uint32_t initial_domain); cairo_private cairo_status_t intel_bo_init_for_name (const intel_device_t *dev, intel_bo_t *bo, uint32_t size, uint32_t name); cairo_private cairo_surface_t * intel_bo_get_image (const intel_device_t *device, intel_bo_t *bo, const cairo_drm_surface_t *surface); cairo_private cairo_status_t intel_bo_put_image (intel_device_t *dev, intel_bo_t *bo, cairo_image_surface_t *src, int src_x, int src_y, int width, int height, int dst_x, int dst_y); cairo_private void intel_surface_init (intel_surface_t *surface, const cairo_surface_backend_t *backend, cairo_drm_device_t *device, cairo_format_t format, int width, int height); cairo_private cairo_status_t intel_buffer_cache_init (intel_buffer_cache_t *cache, intel_device_t *device, cairo_format_t format, int width, int height); cairo_private cairo_status_t intel_gradient_render (intel_device_t *device, const cairo_gradient_pattern_t *pattern, intel_buffer_t *buffer); cairo_private cairo_int_status_t intel_get_glyph (intel_device_t *device, cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); cairo_private void intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font); cairo_private void intel_scaled_font_fini (cairo_scaled_font_t *scaled_font); cairo_private void intel_glyph_cache_unpin (intel_device_t *device); static inline intel_glyph_t * intel_glyph_pin (intel_glyph_t *glyph) { cairo_rtree_node_t *node = &glyph->node; if (unlikely (node->pinned == 0)) return _cairo_rtree_pin (&glyph->cache->rtree, node); return glyph; } cairo_private cairo_status_t intel_snapshot_cache_insert (intel_device_t *device, intel_surface_t *surface); cairo_private void intel_surface_detach_snapshot (cairo_surface_t *abstract_surface); cairo_private void intel_snapshot_cache_thaw (intel_device_t *device); cairo_private void intel_throttle (intel_device_t *device); cairo_private cairo_status_t intel_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra); cairo_private void intel_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra); cairo_private cairo_surface_t * intel_surface_map_to_image (void *abstract_surface); cairo_private cairo_status_t intel_surface_flush (void *abstract_surface, unsigned flags); cairo_private cairo_status_t intel_surface_finish (void *abstract_surface); cairo_private void intel_dump_batchbuffer (const void *batch, uint32_t length, int devid); static inline uint32_t cairo_const MS3_tiling (uint32_t tiling) { switch (tiling) { default: case I915_TILING_NONE: return 0; case I915_TILING_X: return MS3_TILED_SURFACE; case I915_TILING_Y: return MS3_TILED_SURFACE | MS3_TILE_WALK; } } static inline float cairo_const texcoord_2d_16 (double x, double y) { union { uint32_t ui; float f; } u; u.ui = (_cairo_half_from_float (y) << 16) | _cairo_half_from_float (x); return u.f; } #define PCI_CHIP_I810 0x7121 #define PCI_CHIP_I810_DC100 0x7123 #define PCI_CHIP_I810_E 0x7125 #define PCI_CHIP_I815 0x1132 #define PCI_CHIP_I830_M 0x3577 #define PCI_CHIP_845_G 0x2562 #define PCI_CHIP_I855_GM 0x3582 #define PCI_CHIP_I865_G 0x2572 #define PCI_CHIP_I915_G 0x2582 #define PCI_CHIP_E7221_G 0x258A #define PCI_CHIP_I915_GM 0x2592 #define PCI_CHIP_I945_G 0x2772 #define PCI_CHIP_I945_GM 0x27A2 #define PCI_CHIP_I945_GME 0x27AE #define PCI_CHIP_Q35_G 0x29B2 #define PCI_CHIP_G33_G 0x29C2 #define PCI_CHIP_Q33_G 0x29D2 #define PCI_CHIP_IGD_GM 0xA011 #define PCI_CHIP_IGD_G 0xA001 #define IS_IGDGM(devid) (devid == PCI_CHIP_IGD_GM) #define IS_IGDG(devid) (devid == PCI_CHIP_IGD_G) #define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid)) #define PCI_CHIP_I965_G 0x29A2 #define PCI_CHIP_I965_Q 0x2992 #define PCI_CHIP_I965_G_1 0x2982 #define PCI_CHIP_I946_GZ 0x2972 #define PCI_CHIP_I965_GM 0x2A02 #define PCI_CHIP_I965_GME 0x2A12 #define PCI_CHIP_GM45_GM 0x2A42 #define PCI_CHIP_IGD_E_G 0x2E02 #define PCI_CHIP_Q45_G 0x2E12 #define PCI_CHIP_G45_G 0x2E22 #define PCI_CHIP_G41_G 0x2E32 #define PCI_CHIP_ILD_G 0x0042 #define PCI_CHIP_ILM_G 0x0046 #define IS_MOBILE(devid) (devid == PCI_CHIP_I855_GM || \ devid == PCI_CHIP_I915_GM || \ devid == PCI_CHIP_I945_GM || \ devid == PCI_CHIP_I945_GME || \ devid == PCI_CHIP_I965_GM || \ devid == PCI_CHIP_I965_GME || \ devid == PCI_CHIP_GM45_GM || IS_IGD(devid)) #define IS_G45(devid) (devid == PCI_CHIP_IGD_E_G || \ devid == PCI_CHIP_Q45_G || \ devid == PCI_CHIP_G45_G || \ devid == PCI_CHIP_G41_G) #define IS_GM45(devid) (devid == PCI_CHIP_GM45_GM) #define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid)) #define IS_ILD(devid) (devid == PCI_CHIP_ILD_G) #define IS_ILM(devid) (devid == PCI_CHIP_ILM_G) #define IS_IRONLAKE(devid) (IS_ILD(devid) || IS_ILM(devid)) #define IS_915(devid) (devid == PCI_CHIP_I915_G || \ devid == PCI_CHIP_E7221_G || \ devid == PCI_CHIP_I915_GM) #define IS_945(devid) (devid == PCI_CHIP_I945_G || \ devid == PCI_CHIP_I945_GM || \ devid == PCI_CHIP_I945_GME || \ devid == PCI_CHIP_G33_G || \ devid == PCI_CHIP_Q33_G || \ devid == PCI_CHIP_Q35_G || IS_IGD(devid)) #define IS_965(devid) (devid == PCI_CHIP_I965_G || \ devid == PCI_CHIP_I965_Q || \ devid == PCI_CHIP_I965_G_1 || \ devid == PCI_CHIP_I965_GM || \ devid == PCI_CHIP_I965_GME || \ devid == PCI_CHIP_I946_GZ || \ IS_G4X(devid) || \ IS_IRONLAKE(devid)) #define IS_9XX(devid) (IS_915(devid) || \ IS_945(devid) || \ IS_965(devid)) #endif /* CAIRO_DRM_INTEL_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel-surface.c000066400000000000000000000312151271037650300276440ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-intel-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" /* Basic generic/stub surface for intel chipsets */ #define MAX_SIZE 2048 static cairo_surface_t * intel_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { return cairo_image_surface_create (_cairo_format_from_content (content), width, height); } cairo_status_t intel_surface_finish (void *abstract_surface) { intel_surface_t *surface = abstract_surface; intel_bo_in_flight_add (to_intel_device (surface->drm.base.device), to_intel_bo (surface->drm.bo)); return _cairo_drm_surface_finish (&surface->drm); } static void surface_finish_and_destroy (cairo_surface_t *surface) { cairo_surface_finish (surface); cairo_surface_destroy (surface); } cairo_status_t intel_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { intel_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_status_t status; void *ptr; if (surface->drm.fallback != NULL) { image = surface->drm.fallback; goto DONE; } image = _cairo_surface_has_snapshot (&surface->drm.base, &_cairo_image_surface_backend); if (image != NULL) goto DONE; if (surface->drm.base.backend->flush != NULL) { status = surface->drm.base.backend->flush (surface); if (unlikely (status)) return status; } ptr = intel_bo_map (to_intel_device (surface->drm.base.device), to_intel_bo (surface->drm.bo)); if (unlikely (ptr == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); image = cairo_image_surface_create_for_data (ptr, surface->drm.format, surface->drm.width, surface->drm.height, surface->drm.stride); if (unlikely (image->status)) return image->status; _cairo_surface_attach_snapshot (&surface->drm.base, image, surface_finish_and_destroy); DONE: *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } void intel_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } cairo_surface_t * intel_surface_map_to_image (void *abstract_surface) { intel_surface_t *surface = abstract_surface; if (surface->drm.fallback == NULL) { cairo_surface_t *image; cairo_status_t status; void *ptr; if (surface->drm.base.backend->flush != NULL) { status = surface->drm.base.backend->flush (surface); if (unlikely (status)) return _cairo_surface_create_in_error (status); } ptr = intel_bo_map (to_intel_device (surface->drm.base.device), to_intel_bo (surface->drm.bo)); if (unlikely (ptr == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); image = cairo_image_surface_create_for_data (ptr, surface->drm.format, surface->drm.width, surface->drm.height, surface->drm.stride); if (unlikely (image->status)) return image; surface->drm.fallback = image; } return surface->drm.fallback; } cairo_status_t intel_surface_flush (void *abstract_surface, unsigned flags) { intel_surface_t *surface = abstract_surface; cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->drm.fallback == NULL) return CAIRO_STATUS_SUCCESS; /* kill any outstanding maps */ cairo_surface_finish (surface->drm.fallback); status = cairo_surface_status (surface->drm.fallback); cairo_surface_destroy (surface->drm.fallback); surface->drm.fallback = NULL; return status; } static cairo_int_status_t intel_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip) { return _cairo_surface_paint (intel_surface_map_to_image (abstract_surface), op, source, clip); } static cairo_int_status_t intel_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_clip_t *clip) { return _cairo_surface_mask (intel_surface_map_to_image (abstract_surface), op, source, mask, clip); } static cairo_int_status_t intel_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { return _cairo_surface_stroke (intel_surface_map_to_image (abstract_surface), op, source, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t intel_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { return _cairo_surface_fill (intel_surface_map_to_image (abstract_surface), op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t intel_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { *num_remaining = 0; return _cairo_surface_show_text_glyphs (intel_surface_map_to_image (abstract_surface), op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); } static const cairo_surface_backend_t intel_surface_backend = { CAIRO_SURFACE_TYPE_DRM, _cairo_default_context_create, intel_surface_create_similar, intel_surface_finish, NULL, intel_surface_acquire_source_image, intel_surface_release_source_image, NULL, NULL, NULL, NULL, /* composite */ NULL, /* fill */ NULL, /* trapezoids */ NULL, /* span */ NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_drm_surface_get_extents, NULL, /* old-glyphs */ _cairo_drm_surface_get_font_options, intel_surface_flush, NULL, /* mark dirty */ NULL, NULL, /* font/glyph fini */ intel_surface_paint, intel_surface_mask, intel_surface_stroke, intel_surface_fill, intel_surface_glyphs, }; void intel_surface_init (intel_surface_t *surface, const cairo_surface_backend_t *backend, cairo_drm_device_t *device, cairo_format_t format, int width, int height) { _cairo_surface_init (&surface->drm.base, backend, &device->base, _cairo_content_from_format (format)); _cairo_drm_surface_init (&surface->drm, format, width, height); surface->snapshot_cache_entry.hash = 0; } static cairo_surface_t * intel_surface_create (cairo_drm_device_t *device, cairo_format_t format, int width, int height) { intel_surface_t *surface; cairo_status_t status; surface = malloc (sizeof (intel_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); intel_surface_init (surface, &intel_surface_backend, device, format, width, height); if (width && height) { /* Vol I, p134: size restrictions for textures */ width = (width + 3) & -4; height = (height + 1) & -2; surface->drm.stride = cairo_format_stride_for_width (surface->drm.format, width); surface->drm.bo = &intel_bo_create (to_intel_device (&device->base), surface->drm.stride * height, surface->drm.stride * height, TRUE, I915_TILING_NONE, surface->drm.stride)->base; if (surface->drm.bo == NULL) { status = _cairo_drm_surface_finish (&surface->drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } return &surface->drm.base; } static cairo_surface_t * intel_surface_create_for_name (cairo_drm_device_t *device, unsigned int name, cairo_format_t format, int width, int height, int stride) { intel_surface_t *surface; cairo_status_t status; switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; } if (stride < cairo_format_stride_for_width (format, width)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); surface = malloc (sizeof (intel_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); intel_surface_init (surface, &intel_surface_backend, device, format, width, height); if (width && height) { surface->drm.stride = stride; surface->drm.bo = &intel_bo_create_for_name (to_intel_device (&device->base), name)->base; if (unlikely (surface->drm.bo == NULL)) { status = _cairo_drm_surface_finish (&surface->drm); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } return &surface->drm.base; } static cairo_status_t intel_surface_enable_scan_out (void *abstract_surface) { intel_surface_t *surface = abstract_surface; if (unlikely (surface->drm.bo == NULL)) return _cairo_error (CAIRO_STATUS_INVALID_SIZE); to_intel_bo (surface->drm.bo)->tiling = I915_TILING_X; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t intel_device_throttle (cairo_drm_device_t *device) { intel_throttle (to_intel_device (&device->base)); return CAIRO_STATUS_SUCCESS; } static void intel_device_destroy (void *data) { intel_device_t *device = data; intel_device_fini (device); free (data); } cairo_drm_device_t * _cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id) { intel_device_t *device; cairo_status_t status; if (! intel_info (fd, NULL)) return NULL; device = malloc (sizeof (intel_device_t)); if (unlikely (device == NULL)) return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); status = intel_device_init (device, fd); if (unlikely (status)) { free (device); return (cairo_drm_device_t *) _cairo_device_create_in_error (status); } device->base.surface.create = intel_surface_create; device->base.surface.create_for_name = intel_surface_create_for_name; device->base.surface.create_from_cacheable_image = NULL; device->base.surface.flink = _cairo_drm_surface_flink; device->base.surface.enable_scan_out = intel_surface_enable_scan_out; device->base.surface.map_to_image = intel_surface_map_to_image; device->base.device.flush = NULL; device->base.device.throttle = intel_device_throttle; device->base.device.destroy = intel_device_destroy; return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-intel.c000066400000000000000000001036401271037650300262200ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-ioctl-private.h" #include "cairo-drm-intel-private.h" #include "cairo-drm-intel-ioctl-private.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include #include #include #define GLYPH_CACHE_WIDTH 1024 #define GLYPH_CACHE_HEIGHT 1024 #define GLYPH_CACHE_MIN_SIZE 1 #define GLYPH_CACHE_MAX_SIZE 128 #define IMAGE_CACHE_WIDTH 1024 #define IMAGE_CACHE_HEIGHT 1024 int intel_get (int fd, int param) { struct intel_getparam gp; int value; gp.param = param; gp.value = &value; if (ioctl (fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0) return 0; VG (VALGRIND_MAKE_MEM_DEFINED (&value, sizeof (value))); return value; } cairo_bool_t intel_info (int fd, uint64_t *gtt_size) { struct drm_i915_gem_get_aperture info; if (! intel_get (fd, I915_PARAM_HAS_GEM)) return FALSE; if (! intel_get (fd, I915_PARAM_HAS_EXECBUF2)) return FALSE; if (ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &info) < 0) return FALSE; VG (VALGRIND_MAKE_MEM_DEFINED (&info, sizeof (info))); if (gtt_size != NULL) *gtt_size = info.aper_size; return TRUE; } void intel_bo_write (const intel_device_t *device, intel_bo_t *bo, unsigned long offset, unsigned long size, const void *data) { struct drm_i915_gem_pwrite pwrite; int ret; assert (bo->tiling == I915_TILING_NONE); assert (size); assert (offset < bo->base.size); assert (size+offset <= bo->base.size); intel_bo_set_tiling (device, bo); assert (bo->_tiling == I915_TILING_NONE); memset (&pwrite, 0, sizeof (pwrite)); pwrite.handle = bo->base.handle; pwrite.offset = offset; pwrite.size = size; pwrite.data_ptr = (uint64_t) (uintptr_t) data; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite); } while (ret == -1 && errno == EINTR); assert (ret == 0); bo->busy = FALSE; } void intel_bo_read (const intel_device_t *device, intel_bo_t *bo, unsigned long offset, unsigned long size, void *data) { struct drm_i915_gem_pread pread; int ret; assert (bo->tiling == I915_TILING_NONE); assert (size); assert (offset < bo->base.size); assert (size+offset <= bo->base.size); intel_bo_set_tiling (device, bo); assert (bo->_tiling == I915_TILING_NONE); memset (&pread, 0, sizeof (pread)); pread.handle = bo->base.handle; pread.offset = offset; pread.size = size; pread.data_ptr = (uint64_t) (uintptr_t) data; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PREAD, &pread); } while (ret == -1 && errno == EINTR); assert (ret == 0); bo->cpu = TRUE; bo->busy = FALSE; } void * intel_bo_map (const intel_device_t *device, intel_bo_t *bo) { struct drm_i915_gem_set_domain set_domain; uint32_t domain; int ret; intel_bo_set_tiling (device, bo); if (bo->virtual != NULL) return bo->virtual; if (bo->cpu && bo->tiling == I915_TILING_NONE) { struct drm_i915_gem_mmap mmap_arg; mmap_arg.handle = bo->base.handle; mmap_arg.offset = 0; mmap_arg.size = bo->base.size; mmap_arg.addr_ptr = 0; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg); } while (ret == -1 && errno == EINTR); if (unlikely (ret != 0)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } bo->virtual = (void *) (uintptr_t) mmap_arg.addr_ptr; domain = I915_GEM_DOMAIN_CPU; } else { struct drm_i915_gem_mmap_gtt mmap_arg; void *ptr; /* Get the fake offset back... */ mmap_arg.handle = bo->base.handle; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg); } while (ret == -1 && errno == EINTR); if (unlikely (ret != 0)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } /* and mmap it */ ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE, MAP_SHARED, device->base.fd, mmap_arg.offset); if (unlikely (ptr == MAP_FAILED)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } bo->virtual = ptr; domain = I915_GEM_DOMAIN_GTT; } VG (VALGRIND_MAKE_MEM_DEFINED (bo->virtual, bo->base.size)); set_domain.handle = bo->base.handle; set_domain.read_domains = domain; set_domain.write_domain = domain; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); } while (ret == -1 && errno == EINTR); if (ret != 0) { intel_bo_unmap (bo); _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR); return NULL; } bo->busy = FALSE; return bo->virtual; } void intel_bo_unmap (intel_bo_t *bo) { munmap (bo->virtual, bo->base.size); bo->virtual = NULL; } cairo_bool_t intel_bo_is_inactive (const intel_device_t *device, intel_bo_t *bo) { struct drm_i915_gem_busy busy; if (! bo->busy) return TRUE; /* Is this buffer busy for our intended usage pattern? */ busy.handle = bo->base.handle; busy.busy = 1; ioctl (device->base.fd, DRM_IOCTL_I915_GEM_BUSY, &busy); bo->busy = busy.busy; return ! busy.busy; } cairo_bool_t intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo) { struct drm_i915_gem_set_domain set_domain; int ret; set_domain.handle = bo->base.handle; set_domain.read_domains = I915_GEM_DOMAIN_GTT; set_domain.write_domain = 0; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); } while (ret == -1 && errno == EINTR); return ret == 0; } static inline int pot (int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } cairo_bool_t intel_bo_madvise (intel_device_t *device, intel_bo_t *bo, int advice) { struct drm_i915_gem_madvise madv; madv.handle = bo->base.handle; madv.madv = advice; madv.retained = TRUE; ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MADVISE, &madv); return madv.retained; } static void intel_bo_set_real_size (intel_device_t *device, intel_bo_t *bo, size_t size) { struct drm_i915_gem_real_size arg; int ret; return; if (size == bo->base.size) return; arg.handle = bo->base.handle; arg.size = size; do { ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_REAL_SIZE, &arg); } while (ret == -1 && errno == EINTR); if (ret == 0) { if (size > bo->base.size) { assert (bo->exec == NULL); bo->cpu = TRUE; bo->busy = FALSE; } bo->base.size = size; } } intel_bo_t * intel_bo_create (intel_device_t *device, uint32_t max_size, uint32_t real_size, cairo_bool_t gpu_target, uint32_t tiling, uint32_t stride) { intel_bo_t *bo; uint32_t cache_size; struct drm_i915_gem_create create; int bucket; int ret; max_size = (max_size + 4095) & -4096; real_size = (real_size + 4095) & -4096; cache_size = pot (max_size); bucket = ffs (cache_size / 4096) - 1; if (bucket >= INTEL_BO_CACHE_BUCKETS) cache_size = max_size; if (gpu_target) { intel_bo_t *first = NULL; cairo_list_foreach_entry (bo, intel_bo_t, &device->bo_in_flight, cache_list) { assert (bo->exec != NULL); if (tiling && bo->_tiling && (bo->_tiling != tiling || bo->_stride != stride)) { continue; } if (real_size <= bo->base.size) { if (real_size >= bo->base.size/2) { cairo_list_del (&bo->cache_list); bo = intel_bo_reference (bo); goto DONE; } if (first == NULL) first = bo; } } if (first != NULL) { cairo_list_del (&first->cache_list); bo = intel_bo_reference (first); goto DONE; } } /* no cached buffer available, allocate fresh */ bo = _cairo_freepool_alloc (&device->bo_pool); if (unlikely (bo == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return bo; } cairo_list_init (&bo->cache_list); bo->base.name = 0; bo->offset = 0; bo->virtual = NULL; bo->cpu = TRUE; bo->_tiling = I915_TILING_NONE; bo->_stride = 0; bo->purgeable = 0; bo->busy = FALSE; bo->opaque0 = 0; bo->opaque1 = 0; bo->exec = NULL; bo->batch_read_domains = 0; bo->batch_write_domain = 0; cairo_list_init (&bo->link); create.size = cache_size; create.handle = 0; ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_CREATE, &create); if (unlikely (ret != 0)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); _cairo_freepool_free (&device->bo_pool, bo); return NULL; } bo->base.handle = create.handle; bo->full_size = bo->base.size = create.size; intel_bo_set_real_size (device, bo, real_size); CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); DONE: bo->tiling = tiling; bo->stride = stride; return bo; } intel_bo_t * intel_bo_create_for_name (intel_device_t *device, uint32_t name) { struct drm_i915_gem_get_tiling get_tiling; cairo_status_t status; intel_bo_t *bo; int ret; bo = _cairo_freepool_alloc (&device->bo_pool); if (unlikely (bo == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name); if (unlikely (status)) goto FAIL; CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); cairo_list_init (&bo->cache_list); bo->full_size = bo->base.size; bo->offset = 0; bo->virtual = NULL; bo->purgeable = 0; bo->busy = TRUE; bo->cpu = FALSE; bo->opaque0 = 0; bo->opaque1 = 0; bo->exec = NULL; bo->batch_read_domains = 0; bo->batch_write_domain = 0; cairo_list_init (&bo->link); memset (&get_tiling, 0, sizeof (get_tiling)); get_tiling.handle = bo->base.handle; ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling); if (unlikely (ret != 0)) { _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR); _cairo_drm_bo_close (&device->base, &bo->base); goto FAIL; } bo->_tiling = bo->tiling = get_tiling.tiling_mode; // bo->stride = get_tiling.stride; /* XXX not available from get_tiling */ return bo; FAIL: _cairo_freepool_free (&device->bo_pool, bo); return NULL; } static void intel_bo_release (void *_dev, void *_bo) { intel_device_t *device = _dev; intel_bo_t *bo = _bo; if (bo->virtual != NULL) intel_bo_unmap (bo); assert (bo->exec == NULL); assert (cairo_list_is_empty (&bo->cache_list)); _cairo_drm_bo_close (&device->base, &bo->base); _cairo_freepool_free (&device->bo_pool, bo); } void intel_bo_set_tiling (const intel_device_t *device, intel_bo_t *bo) { struct drm_i915_gem_set_tiling set_tiling; int ret; if (bo->tiling == bo->_tiling && (bo->tiling == I915_TILING_NONE || bo->stride == bo->_stride)) return; do { set_tiling.handle = bo->base.handle; set_tiling.tiling_mode = bo->tiling; set_tiling.stride = bo->stride; ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling); } while (ret == -1 && errno == EINTR); assert (ret == 0); bo->_tiling = bo->tiling; bo->_stride = bo->stride; } cairo_surface_t * intel_bo_get_image (const intel_device_t *device, intel_bo_t *bo, const cairo_drm_surface_t *surface) { cairo_image_surface_t *image; uint8_t *dst; int size, row; image = (cairo_image_surface_t *) cairo_image_surface_create (surface->format, surface->width, surface->height); if (unlikely (image->base.status)) return &image->base; intel_bo_set_tiling (device, bo); if (bo->tiling == I915_TILING_NONE && image->stride == surface->stride) { size = surface->stride * surface->height; intel_bo_read (device, bo, 0, size, image->data); } else { const uint8_t *src; src = intel_bo_map (device, bo); if (unlikely (src == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); size = surface->width; if (surface->format != CAIRO_FORMAT_A8) size *= 4; row = surface->height; dst = image->data; while (row--) { memcpy (dst, src, size); dst += image->stride; src += surface->stride; } } return &image->base; } static cairo_status_t _intel_bo_put_a1_image (intel_device_t *device, intel_bo_t *bo, cairo_image_surface_t *src, int src_x, int src_y, int width, int height, int dst_x, int dst_y) { uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; uint8_t *a8 = buf; uint8_t *data; int x; data = src->data + src_y * src->stride; if (bo->tiling == I915_TILING_NONE && width == bo->stride) { uint8_t *p; int size; size = bo->stride * height; if (size > (int) sizeof (buf)) { a8 = _cairo_malloc_ab (bo->stride, height); if (a8 == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } p = a8; while (height--) { for (x = 0; x < width; x++) { int i = src_x + x; int byte = i / 8; int bit = i % 8; p[x] = data[byte] & (1 << bit) ? 0xff : 0x00; } data += src->stride; p += bo->stride; } intel_bo_write (device, bo, dst_y * bo->stride + dst_x, /* XXX bo_offset */ size, a8); } else { uint8_t *dst; if (width > (int) sizeof (buf)) { a8 = malloc (width); if (a8 == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } dst = intel_bo_map (device, bo); if (dst == NULL) { if (a8 != buf) free (a8); return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); } dst += dst_y * bo->stride + dst_x; /* XXX bo_offset */ while (height--) { for (x = 0; x < width; x++) { int i = src_x + x; int byte = i / 8; int bit = i % 8; a8[x] = data[byte] & (1 << bit) ? 0xff : 0x00; } memcpy (dst, a8, width); dst += bo->stride; data += src->stride; } } if (a8 != buf) free (a8); return CAIRO_STATUS_SUCCESS; } cairo_status_t intel_bo_put_image (intel_device_t *device, intel_bo_t *bo, cairo_image_surface_t *src, int src_x, int src_y, int width, int height, int dst_x, int dst_y) { uint8_t *data; int size; int offset; intel_bo_set_tiling (device, bo); offset = dst_y * bo->stride; data = src->data + src_y * src->stride; switch (src->format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: offset += 4 * dst_x; data += 4 * src_x; size = 4 * width; break; case CAIRO_FORMAT_RGB16_565: offset += 2 * dst_x; data += 2 * src_x; size = 2 * width; break; case CAIRO_FORMAT_A8: offset += dst_x; data += src_x; size = width; break; case CAIRO_FORMAT_A1: return _intel_bo_put_a1_image (device, bo, src, src_x, src_y, width, height, dst_x, dst_y); default: case CAIRO_FORMAT_INVALID: return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } if (bo->tiling == I915_TILING_NONE && src->stride == bo->stride) { intel_bo_write (device, bo, offset, bo->stride * height, data); } else { uint8_t *dst; dst = intel_bo_map (device, bo); if (unlikely (dst == NULL)) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); dst += offset; while (height--) { memcpy (dst, data, size); dst += bo->stride; data += src->stride; } } return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _intel_snapshot_cache_entry_can_remove (const void *closure) { return TRUE; } static void _intel_snapshot_cache_entry_destroy (void *closure) { intel_surface_t *surface = cairo_container_of (closure, intel_surface_t, snapshot_cache_entry); surface->snapshot_cache_entry.hash = 0; } cairo_status_t intel_device_init (intel_device_t *device, int fd) { struct drm_i915_gem_get_aperture aperture; cairo_status_t status; size_t size; int ret; int n; ret = ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); if (ret != 0) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); CAIRO_MUTEX_INIT (device->mutex); device->gtt_max_size = aperture.aper_size; device->gtt_avail_size = aperture.aper_available_size; device->gtt_avail_size -= device->gtt_avail_size >> 5; size = aperture.aper_size / 8; device->snapshot_cache_max_size = size / 4; status = _cairo_cache_init (&device->snapshot_cache, NULL, _intel_snapshot_cache_entry_can_remove, _intel_snapshot_cache_entry_destroy, size); if (unlikely (status)) return status; for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) { device->glyph_cache[n].buffer.bo = NULL; cairo_list_init (&device->glyph_cache[n].rtree.pinned); } cairo_list_init (&device->fonts); device->gradient_cache.size = 0; device->base.bo.release = intel_bo_release; return CAIRO_STATUS_SUCCESS; } static void _intel_gradient_cache_fini (intel_device_t *device) { unsigned int n; for (n = 0; n < device->gradient_cache.size; n++) { _cairo_pattern_fini (&device->gradient_cache.cache[n].pattern.base); if (device->gradient_cache.cache[n].buffer.bo != NULL) cairo_drm_bo_destroy (&device->base.base, &device->gradient_cache.cache[n].buffer.bo->base); } } static void _intel_glyph_cache_fini (intel_device_t *device, intel_buffer_cache_t *cache) { if (cache->buffer.bo == NULL) return; intel_bo_destroy (device, cache->buffer.bo); _cairo_rtree_fini (&cache->rtree); } void intel_device_fini (intel_device_t *device) { cairo_scaled_font_t *scaled_font, *next_scaled_font; int n; cairo_list_foreach_entry_safe (scaled_font, next_scaled_font, cairo_scaled_font_t, &device->fonts, link) { _cairo_scaled_font_revoke_ownership (scaled_font); } for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) _intel_glyph_cache_fini (device, &device->glyph_cache[n]); _cairo_cache_fini (&device->snapshot_cache); _intel_gradient_cache_fini (device); _cairo_freepool_fini (&device->bo_pool); _cairo_drm_device_fini (&device->base); } void intel_throttle (intel_device_t *device) { ioctl (device->base.fd, DRM_IOCTL_I915_GEM_THROTTLE); } void intel_glyph_cache_unpin (intel_device_t *device) { int n; for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) _cairo_rtree_unpin (&device->glyph_cache[n].rtree); } static cairo_status_t intel_glyph_cache_add_glyph (intel_device_t *device, intel_buffer_cache_t *cache, cairo_scaled_glyph_t *scaled_glyph) { cairo_image_surface_t *glyph_surface = scaled_glyph->surface; intel_glyph_t *glyph; cairo_rtree_node_t *node = NULL; double sf_x, sf_y; cairo_status_t status; uint8_t *dst, *src; int width, height; width = glyph_surface->width; if (width < GLYPH_CACHE_MIN_SIZE) width = GLYPH_CACHE_MIN_SIZE; height = glyph_surface->height; if (height < GLYPH_CACHE_MIN_SIZE) height = GLYPH_CACHE_MIN_SIZE; /* search for an available slot */ status = _cairo_rtree_insert (&cache->rtree, width, height, &node); /* search for an unpinned slot */ if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_rtree_evict_random (&cache->rtree, width, height, &node); if (status == CAIRO_STATUS_SUCCESS) status = _cairo_rtree_node_insert (&cache->rtree, node, width, height, &node); } if (unlikely (status)) return status; /* XXX streaming upload? */ height = glyph_surface->height; src = glyph_surface->data; dst = cache->buffer.bo->virtual; if (dst == NULL) { dst = intel_bo_map (device, cache->buffer.bo); if (unlikely (dst == NULL)) return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); } dst += node->y * cache->buffer.stride; switch (glyph_surface->format) { case CAIRO_FORMAT_A1: { uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; uint8_t *a8 = buf; int x; if (width > (int) sizeof (buf)) { a8 = malloc (width); if (unlikely (a8 == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } dst += node->x; width = glyph_surface->width; while (height--) { for (x = 0; x < width; x++) a8[x] = src[x>>3] & (1 << (x&7)) ? 0xff : 0x00; memcpy (dst, a8, width); dst += cache->buffer.stride; src += glyph_surface->stride; } if (a8 != buf) free (a8); break; } case CAIRO_FORMAT_A8: dst += node->x; width = glyph_surface->width; while (height--) { memcpy (dst, src, width); dst += cache->buffer.stride; src += glyph_surface->stride; } break; case CAIRO_FORMAT_ARGB32: dst += 4*node->x; width = 4*glyph_surface->width; while (height--) { memcpy (dst, src, width); dst += cache->buffer.stride; src += glyph_surface->stride; } break; default: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } scaled_glyph->surface_private = node; glyph= (intel_glyph_t *) node; glyph->node.owner = &scaled_glyph->surface_private; glyph->cache = cache; /* compute tex coords: bottom-right, bottom-left, top-left */ sf_x = 1. / cache->buffer.width; sf_y = 1. / cache->buffer.height; glyph->texcoord[0] = texcoord_2d_16 (sf_x * (node->x + glyph_surface->width), sf_y * (node->y + glyph_surface->height)); glyph->texcoord[1] = texcoord_2d_16 (sf_x * node->x, sf_y * (node->y + glyph_surface->height)); glyph->texcoord[2] = texcoord_2d_16 (sf_x * node->x, sf_y * node->y); glyph->width = glyph_surface->width; glyph->height = glyph_surface->height; return CAIRO_STATUS_SUCCESS; } void intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font) { intel_glyph_t *glyph; glyph = scaled_glyph->surface_private; if (glyph != NULL) { /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ glyph->node.owner = NULL; if (! glyph->node.pinned) _cairo_rtree_node_remove (&glyph->cache->rtree, &glyph->node); } } void intel_scaled_font_fini (cairo_scaled_font_t *scaled_font) { cairo_list_del (&scaled_font->link); } static cairo_status_t intel_get_glyph_cache (intel_device_t *device, cairo_format_t format, intel_buffer_cache_t **out) { intel_buffer_cache_t *cache; cairo_status_t status; switch (format) { case CAIRO_FORMAT_ARGB32: cache = &device->glyph_cache[0]; format = CAIRO_FORMAT_ARGB32; break; case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: cache = &device->glyph_cache[1]; format = CAIRO_FORMAT_A8; break; default: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } if (unlikely (cache->buffer.bo == NULL)) { status = intel_buffer_cache_init (cache, device, format, INTEL_GLYPH_CACHE_WIDTH, INTEL_GLYPH_CACHE_HEIGHT); if (unlikely (status)) return status; _cairo_rtree_init (&cache->rtree, INTEL_GLYPH_CACHE_WIDTH, INTEL_GLYPH_CACHE_HEIGHT, 0, sizeof (intel_glyph_t)); } *out = cache; return CAIRO_STATUS_SUCCESS; } cairo_int_status_t intel_get_glyph (intel_device_t *device, cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_bool_t own_surface = FALSE; intel_buffer_cache_t *cache; cairo_status_t status; if (scaled_glyph->surface == NULL) { status = scaled_font->backend->scaled_glyph_init (scaled_font, scaled_glyph, CAIRO_SCALED_GLYPH_INFO_SURFACE); if (unlikely (status)) return status; if (unlikely (scaled_glyph->surface == NULL)) return CAIRO_INT_STATUS_UNSUPPORTED; own_surface = TRUE; } if (unlikely (scaled_glyph->surface->width == 0 || scaled_glyph->surface->height == 0)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (unlikely (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } status = intel_get_glyph_cache (device, scaled_glyph->surface->format, &cache); if (unlikely (status)) return status; status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph); if (unlikely (_cairo_status_is_error (status))) return status; if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { /* no room, replace entire cache */ assert (cache->buffer.bo->exec != NULL); _cairo_rtree_reset (&cache->rtree); intel_bo_destroy (device, cache->buffer.bo); cache->buffer.bo = NULL; status = intel_buffer_cache_init (cache, device, scaled_glyph->surface->format, GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT); if (unlikely (status)) return status; status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph); if (unlikely (status)) return status; } if (own_surface) { /* and release the copy of the image from system memory */ cairo_surface_destroy (&scaled_glyph->surface->base); scaled_glyph->surface = NULL; } return CAIRO_STATUS_SUCCESS; } cairo_status_t intel_buffer_cache_init (intel_buffer_cache_t *cache, intel_device_t *device, cairo_format_t format, int width, int height) { const uint32_t tiling = I915_TILING_Y; uint32_t stride, size; assert ((width & 3) == 0); assert ((height & 1) == 0); cache->buffer.format = format; cache->buffer.width = width; cache->buffer.height = height; switch (format) { default: case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); case CAIRO_FORMAT_ARGB32: cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; stride = width * 4; break; case CAIRO_FORMAT_A8: cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8; stride = width; break; } size = height * stride; cache->buffer.bo = intel_bo_create (device, size, size, FALSE, tiling, stride); if (unlikely (cache->buffer.bo == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cache->buffer.stride = stride; cache->buffer.offset = 0; cache->buffer.map0 |= MS3_tiling (tiling); cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | ((width - 1) << MS3_WIDTH_SHIFT); cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT; cache->ref_count = 0; cairo_list_init (&cache->link); return CAIRO_STATUS_SUCCESS; } cairo_status_t intel_snapshot_cache_insert (intel_device_t *device, intel_surface_t *surface) { cairo_status_t status; surface->snapshot_cache_entry.size = surface->drm.bo->size; if (surface->snapshot_cache_entry.size > device->snapshot_cache_max_size) { return CAIRO_STATUS_SUCCESS; } if (device->snapshot_cache.freeze_count == 0) _cairo_cache_freeze (&device->snapshot_cache); surface->snapshot_cache_entry.hash = (unsigned long) surface; status = _cairo_cache_insert (&device->snapshot_cache, &surface->snapshot_cache_entry); if (unlikely (status)) { surface->snapshot_cache_entry.hash = 0; return status; } return CAIRO_STATUS_SUCCESS; } void intel_surface_detach_snapshot (cairo_surface_t *abstract_surface) { intel_surface_t *surface = (intel_surface_t *) abstract_surface; if (surface->snapshot_cache_entry.hash) { intel_device_t *device; device = (intel_device_t *) surface->drm.base.device; _cairo_cache_remove (&device->snapshot_cache, &surface->snapshot_cache_entry); assert (surface->snapshot_cache_entry.hash == 0); } } void intel_snapshot_cache_thaw (intel_device_t *device) { if (device->snapshot_cache.freeze_count) _cairo_cache_thaw (&device->snapshot_cache); } static cairo_bool_t _gradient_color_stops_equal (const cairo_gradient_pattern_t *a, const cairo_gradient_pattern_t *b) { unsigned int n; if (a->n_stops != b->n_stops) return FALSE; for (n = 0; n < a->n_stops; n++) { if (_cairo_fixed_from_double (a->stops[n].offset) != _cairo_fixed_from_double (b->stops[n].offset)) { return FALSE; } if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) return FALSE; } return TRUE; } static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } static int intel_gradient_sample_width (const cairo_gradient_pattern_t *gradient) { unsigned int n; int width; width = 8; for (n = 1; n < gradient->n_stops; n++) { double dx = gradient->stops[n].offset - gradient->stops[n-1].offset; double delta, max; int ramp; if (dx == 0) continue; max = gradient->stops[n].color.red - gradient->stops[n-1].color.red; delta = gradient->stops[n].color.green - gradient->stops[n-1].color.green; if (delta > max) max = delta; delta = gradient->stops[n].color.blue - gradient->stops[n-1].color.blue; if (delta > max) max = delta; delta = gradient->stops[n].color.alpha - gradient->stops[n-1].color.alpha; if (delta > max) max = delta; ramp = 128 * max / dx; if (ramp > width) width = ramp; } width = (width + 7) & -8; return MIN (width, 1024); } cairo_status_t intel_gradient_render (intel_device_t *device, const cairo_gradient_pattern_t *pattern, intel_buffer_t *buffer) { pixman_image_t *gradient, *image; pixman_gradient_stop_t pixman_stops_stack[32]; pixman_gradient_stop_t *pixman_stops; pixman_point_fixed_t p1, p2; int width; unsigned int i; cairo_status_t status; for (i = 0; i < device->gradient_cache.size; i++) { if (_gradient_color_stops_equal (pattern, &device->gradient_cache.cache[i].pattern.gradient.base)) { *buffer = device->gradient_cache.cache[i].buffer; return CAIRO_STATUS_SUCCESS; } } pixman_stops = pixman_stops_stack; if (unlikely (pattern->n_stops > ARRAY_LENGTH (pixman_stops_stack))) { pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof (pixman_gradient_stop_t)); if (unlikely (pixman_stops == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } for (i = 0; i < pattern->n_stops; i++) { pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); pixman_stops[i].color.red = pattern->stops[i].color.red_short; pixman_stops[i].color.green = pattern->stops[i].color.green_short; pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; } width = intel_gradient_sample_width (pattern); p1.x = 0; p1.y = 0; p2.x = width << 16; p2.y = 0; gradient = pixman_image_create_linear_gradient (&p1, &p2, pixman_stops, pattern->n_stops); if (pixman_stops != pixman_stops_stack) free (pixman_stops); if (unlikely (gradient == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0); pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD); image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, 1, NULL, 0); if (unlikely (image == NULL)) { pixman_image_unref (gradient); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } pixman_image_composite32 (PIXMAN_OP_SRC, gradient, NULL, image, 0, 0, 0, 0, 0, 0, width, 1); pixman_image_unref (gradient); buffer->bo = intel_bo_create (device, 4*width, 4*width, FALSE, I915_TILING_NONE, 4*width); if (unlikely (buffer->bo == NULL)) { pixman_image_unref (image); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } intel_bo_write (device, buffer->bo, 0, 4*width, pixman_image_get_data (image)); pixman_image_unref (image); buffer->offset = 0; buffer->width = width; buffer->height = 1; buffer->stride = 4*width; buffer->format = CAIRO_FORMAT_ARGB32; buffer->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; buffer->map0 |= ((width - 1) << MS3_WIDTH_SHIFT); buffer->map1 = (width - 1) << MS4_PITCH_SHIFT; if (device->gradient_cache.size < GRADIENT_CACHE_SIZE) { i = device->gradient_cache.size++; } else { i = hars_petruska_f54_1_random () % GRADIENT_CACHE_SIZE; _cairo_pattern_fini (&device->gradient_cache.cache[i].pattern.base); intel_bo_destroy (device, device->gradient_cache.cache[i].buffer.bo); } status = _cairo_pattern_init_copy (&device->gradient_cache.cache[i].pattern.base, &pattern->base); if (unlikely (status)) { intel_bo_destroy (device, buffer->bo); /* Ensure the cache is correctly initialised for i965_device_destroy */ _cairo_pattern_init_solid (&device->gradient_cache.cache[i].pattern.solid, CAIRO_COLOR_TRANSPARENT); return status; } device->gradient_cache.cache[i].buffer = *buffer; return CAIRO_STATUS_SUCCESS; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-ioctl-private.h000066400000000000000000000006131271037650300276700ustar00rootroot00000000000000#ifndef CAIRO_DRM_IOCTL_PRIVATE_H #define CAIRO_DRM_IOCTL_PRIVATE_H #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) #define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) #define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) #define DRM_COMMAND_BASE 0x40 #endif /* CAIRO_DRM_IOCTL_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-private.h000066400000000000000000000157331271037650300265710ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. * * Contributors(s): * Chris Wilson */ #ifndef CAIRO_DRM_PRIVATE_H #define CAIRO_DRM_PRIVATE_H #include "cairo-drm.h" #include "cairo-device-private.h" #include "cairo-reference-count-private.h" #include "cairo-surface-private.h" #include /* dev_t */ typedef struct _cairo_drm_device cairo_drm_device_t; typedef cairo_drm_device_t * (*cairo_drm_device_create_func_t) (int fd, dev_t dev, int vendor_id, int chip_id); typedef cairo_int_status_t (*cairo_drm_device_flush_func_t) (cairo_drm_device_t *device); typedef cairo_int_status_t (*cairo_drm_device_throttle_func_t) (cairo_drm_device_t *device); typedef void (*cairo_drm_device_destroy_func_t) (void *data); typedef cairo_surface_t * (*cairo_drm_surface_create_func_t) (cairo_drm_device_t *device, cairo_format_t format, int width, int height); typedef cairo_surface_t * (*cairo_drm_surface_create_for_name_func_t) (cairo_drm_device_t *device, unsigned int name, cairo_format_t format, int width, int height, int stride); typedef cairo_surface_t * (*cairo_drm_surface_create_from_cacheable_image_func_t) (cairo_drm_device_t *device, cairo_surface_t *image); typedef cairo_int_status_t (*cairo_drm_surface_flink_func_t) (void *surface); typedef cairo_status_t (*cairo_drm_surface_enable_scan_out_func_t) (void *surface); typedef cairo_surface_t * (*cairo_drm_surface_map_to_image_func_t) (void *surface); typedef struct _cairo_drm_bo_backend { void (*release) (void *device, void *bo); } cairo_drm_bo_backend_t; typedef struct _cairo_drm_device_backend { cairo_drm_device_flush_func_t flush; cairo_drm_device_throttle_func_t throttle; cairo_drm_device_destroy_func_t destroy; } cairo_drm_device_backend_t; typedef struct _cairo_drm_surface_backend { cairo_drm_surface_create_func_t create; cairo_drm_surface_create_for_name_func_t create_for_name; cairo_drm_surface_create_from_cacheable_image_func_t create_from_cacheable_image; cairo_drm_surface_flink_func_t flink; cairo_drm_surface_enable_scan_out_func_t enable_scan_out; cairo_drm_surface_map_to_image_func_t map_to_image; } cairo_drm_surface_backend_t; typedef struct _cairo_drm_bo { cairo_reference_count_t ref_count; uint32_t name; uint32_t handle; uint32_t size; } cairo_drm_bo_t; struct _cairo_drm_device { cairo_device_t base; int vendor_id; int chip_id; dev_t id; int fd; int max_surface_size; cairo_drm_bo_backend_t bo; cairo_drm_surface_backend_t surface; cairo_drm_device_backend_t device; cairo_drm_device_t *next, *prev; }; typedef struct _cairo_drm_surface { cairo_surface_t base; cairo_drm_bo_t *bo; cairo_format_t format; int width, height, stride; cairo_surface_t *fallback; uint32_t map_count; } cairo_drm_surface_t; static inline cairo_drm_bo_t * cairo_drm_bo_reference (cairo_drm_bo_t *bo) { _cairo_reference_count_inc (&bo->ref_count); return bo; } static cairo_always_inline void cairo_drm_bo_destroy (cairo_device_t *abstract_device, cairo_drm_bo_t *bo) { if (_cairo_reference_count_dec_and_test (&bo->ref_count)) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; device->bo.release (device, bo); } } cairo_private cairo_status_t _cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo, uint32_t name); cairo_private cairo_status_t _cairo_drm_bo_flink (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo); cairo_private void _cairo_drm_bo_close (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo); cairo_private void _cairo_drm_surface_init (cairo_drm_surface_t *surface, cairo_format_t format, int width, int height); cairo_private cairo_status_t _cairo_drm_surface_finish (cairo_drm_surface_t *surface); cairo_private void _cairo_drm_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options); cairo_private cairo_bool_t _cairo_drm_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); cairo_private cairo_int_status_t _cairo_drm_surface_flink (void *abstract_surface); static inline cairo_drm_device_t * _cairo_drm_device_create_in_error (cairo_status_t status) { return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); } cairo_private cairo_drm_device_t * _cairo_drm_device_init (cairo_drm_device_t *device, int fd, dev_t devid, int vendor_id, int chip_id, int max_surface_size); cairo_private void _cairo_drm_device_fini (cairo_drm_device_t *device); /* h/w specific backends */ cairo_private cairo_drm_device_t * _cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id); cairo_private cairo_drm_device_t * _cairo_drm_i915_device_create (int fd, dev_t dev, int vendor_id, int chip_id); cairo_private cairo_drm_device_t * _cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id); cairo_private cairo_drm_device_t * _cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id); #if CAIRO_HAS_GALLIUM_SURFACE cairo_private cairo_drm_device_t * _cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id); #endif slim_hidden_proto (cairo_drm_device_default); slim_hidden_proto (cairo_drm_device_get); slim_hidden_proto (cairo_drm_device_get_for_fd); slim_hidden_proto (cairo_drm_surface_create_for_name); cairo_private cairo_bool_t _cairo_drm_size_is_valid (cairo_device_t *abstract_device, int width, int height); #endif /* CAIRO_DRM_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-radeon-private.h000066400000000000000000000061571271037650300300370ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #ifndef CAIRO_DRM_RADEON_PRIVATE_H #define CAIRO_DRM_RADEON_PRIVATE_H #include "cairo-compiler-private.h" #include "cairo-types-private.h" #include "cairo-drm-private.h" #include "cairo-freelist-private.h" #define RADEON_GEM_DOMAIN_CPU 0x1 #define RADEON_GEM_DOMAIN_GTT 0x2 #define RADEON_GEM_DOMAIN_VRAM 0x4 typedef struct _radeon_bo { cairo_drm_bo_t base; void *virtual; cairo_bool_t in_batch; uint32_t read_domains; uint32_t write_domain; } radeon_bo_t; typedef struct _radeon_device { cairo_drm_device_t base; cairo_freepool_t bo_pool; uint64_t vram_limit; uint64_t gart_limit; } radeon_device_t; cairo_private cairo_status_t radeon_device_init (radeon_device_t *device, int fd); cairo_private void radeon_device_fini (radeon_device_t *device); cairo_private cairo_bool_t radeon_info (int fd, uint64_t *gart_size, uint64_t *vram_size); cairo_private void radeon_bo_write (const radeon_device_t *dev, radeon_bo_t *bo, unsigned long offset, unsigned long size, const void *data); cairo_private void radeon_bo_read (const radeon_device_t *dev, radeon_bo_t *bo, unsigned long offset, unsigned long size, void *data); cairo_private void radeon_bo_wait (const radeon_device_t *dev, radeon_bo_t *bo); cairo_private void * radeon_bo_map (const radeon_device_t *dev, radeon_bo_t *bo); cairo_private void radeon_bo_unmap (radeon_bo_t *bo); cairo_private cairo_drm_bo_t * radeon_bo_create (radeon_device_t *dev, uint32_t size, uint32_t initial_domain); cairo_private cairo_drm_bo_t * radeon_bo_create_for_name (radeon_device_t *dev, uint32_t name); cairo_private cairo_surface_t * radeon_bo_get_image (const radeon_device_t *device, radeon_bo_t *bo, const cairo_drm_surface_t *surface); #endif /* CAIRO_DRM_RADEON_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-radeon-surface.c000066400000000000000000000306221271037650300300020ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-radeon-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" /* Basic stub surface for radeon chipsets */ #define MAX_SIZE 2048 typedef struct _radeon_surface { cairo_drm_surface_t base; } radeon_surface_t; static inline radeon_device_t * to_radeon_device (cairo_device_t *device) { return (radeon_device_t *) device; } static inline radeon_bo_t * to_radeon_bo (cairo_drm_bo_t *bo) { return (radeon_bo_t *) bo; } static cairo_surface_t * radeon_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { return cairo_image_surface_create (_cairo_format_from_content (content), width, height); } static cairo_status_t radeon_surface_finish (void *abstract_surface) { radeon_surface_t *surface = abstract_surface; return _cairo_drm_surface_finish (&surface->base); } static cairo_status_t radeon_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { radeon_surface_t *surface = abstract_surface; cairo_surface_t *image; cairo_status_t status; /* XXX batch flush */ if (surface->base.fallback != NULL) { image = surface->base.fallback; goto DONE; } image = _cairo_surface_has_snapshot (&surface->base.base, &_cairo_image_surface_backend); if (image != NULL) goto DONE; if (surface->base.base.backend->flush != NULL) { status = surface->base.base.backend->flush (surface); if (unlikely (status)) return status; } image = radeon_bo_get_image (to_radeon_device (surface->base.base.device), to_radeon_bo (surface->base.bo), &surface->base); status = image->status; if (unlikely (status)) return status; _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy); DONE: *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void radeon_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { cairo_surface_destroy (&image->base); } static cairo_surface_t * radeon_surface_map_to_image (radeon_surface_t *surface) { if (surface->base.fallback == NULL) { cairo_surface_t *image; cairo_status_t status; void *ptr; if (surface->base.base.backend->flush != NULL) { status = surface->base.base.backend->flush (surface); if (unlikely (status)) return _cairo_surface_create_in_error (status); } ptr = radeon_bo_map (to_radeon_device (surface->base.base.device), to_radeon_bo (surface->base.bo)); if (unlikely (ptr == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); image = cairo_image_surface_create_for_data (ptr, surface->base.format, surface->base.width, surface->base.height, surface->base.stride); if (unlikely (image->status)) { radeon_bo_unmap (to_radeon_bo (surface->base.bo)); return image; } surface->base.fallback = image; } return surface->base.fallback; } static cairo_status_t radeon_surface_flush (void *abstract_surface, unsigned flags) { radeon_surface_t *surface = abstract_surface; cairo_status_t status; if (flags) return CAIRO_STATUS_SUCCESS; if (surface->base.fallback == NULL) return CAIRO_STATUS_SUCCESS; /* kill any outstanding maps */ cairo_surface_finish (surface->base.fallback); status = cairo_surface_status (surface->base.fallback); cairo_surface_destroy (surface->base.fallback); surface->base.fallback = NULL; radeon_bo_unmap (to_radeon_bo (surface->base.bo)); return status; } static cairo_int_status_t radeon_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_clip_t *clip) { return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface), op, source, clip); } static cairo_int_status_t radeon_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, cairo_clip_t *clip) { return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface), op, source, mask, clip); } static cairo_int_status_t radeon_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface), op, source, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t radeon_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, cairo_clip_t *clip) { return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface), op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t radeon_surface_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_clip_t *clip, int *num_remaining) { *num_remaining = 0; return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface), op, source, NULL, 0, glyphs, num_glyphs, NULL, 0, 0, scaled_font, clip); } static const cairo_surface_backend_t radeon_surface_backend = { CAIRO_SURFACE_TYPE_DRM, _cairo_default_context_create, radeon_surface_create_similar, radeon_surface_finish, NULL, radeon_surface_acquire_source_image, radeon_surface_release_source_image, NULL, NULL, NULL, NULL, /* composite */ NULL, /* fill */ NULL, /* trapezoids */ NULL, /* span */ NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_drm_surface_get_extents, NULL, /* old-glyphs */ _cairo_drm_surface_get_font_options, radeon_surface_flush, NULL, /* mark dirty */ NULL, NULL, /* font/glyph fini */ radeon_surface_paint, radeon_surface_mask, radeon_surface_stroke, radeon_surface_fill, radeon_surface_glyphs, }; static void radeon_surface_init (radeon_surface_t *surface, cairo_drm_device_t *device, cairo_format_t format, int width, int height) { _cairo_surface_init (&surface->base.base, &radeon_surface_backend, &device->base, _cairo_content_from_format (format)); _cairo_drm_surface_init (&surface->base, format, width, height); } static cairo_surface_t * radeon_surface_create_internal (cairo_drm_device_t *device, cairo_format_t format, int width, int height) { radeon_surface_t *surface; cairo_status_t status; surface = malloc (sizeof (radeon_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); radeon_surface_init (surface, device, format, width, height); if (width && height) { surface->base.stride = cairo_format_stride_for_width (surface->base.format, width); surface->base.bo = radeon_bo_create (to_radeon_device (&device->base), surface->base.stride * height, RADEON_GEM_DOMAIN_GTT); if (unlikely (surface->base.bo == NULL)) { status = _cairo_drm_surface_finish (&surface->base); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } return &surface->base.base; } static cairo_surface_t * radeon_surface_create (cairo_drm_device_t *device, cairo_format_t format, int width, int height) { switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB16_565: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; } return radeon_surface_create_internal (device, format, width, height); } static cairo_surface_t * radeon_surface_create_for_name (cairo_drm_device_t *device, unsigned int name, cairo_format_t format, int width, int height, int stride) { radeon_surface_t *surface; cairo_status_t status; switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_A1: case CAIRO_FORMAT_RGB16_565: return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_A8: break; } if (stride < cairo_format_stride_for_width (format, width)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); surface = malloc (sizeof (radeon_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); radeon_surface_init (surface, device, format, width, height); if (width && height) { surface->base.stride = stride; surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base), name); if (unlikely (surface->base.bo == NULL)) { status = _cairo_drm_surface_finish (&surface->base); free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } } return &surface->base.base; } static void radeon_device_destroy (void *data) { radeon_device_t *device = data; radeon_device_fini (device); free (data); } cairo_drm_device_t * _cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id) { radeon_device_t *device; uint64_t gart_size, vram_size; cairo_status_t status; if (! radeon_info (fd, &gart_size, &vram_size)) return NULL; device = malloc (sizeof (radeon_device_t)); if (device == NULL) return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY); status = radeon_device_init (device, fd); if (unlikely (status)) { free (device); return _cairo_drm_device_create_in_error (status); } device->base.surface.create = radeon_surface_create; device->base.surface.create_for_name = radeon_surface_create_for_name; device->base.surface.create_from_cacheable_image = NULL; device->base.surface.flink = _cairo_drm_surface_flink; device->base.surface.enable_scan_out = NULL; device->base.device.flush = NULL; device->base.device.throttle = NULL; device->base.device.destroy = radeon_device_destroy; device->vram_limit = vram_size; device->gart_limit = gart_size; return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-radeon.c000066400000000000000000000261711271037650300263600ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-drm-radeon-private.h" #include "cairo-drm-ioctl-private.h" #include "cairo-error-private.h" #include #include #include #define DRM_RADEON_GEM_INFO 0x1c #define DRM_RADEON_GEM_CREATE 0x1d #define DRM_RADEON_GEM_MMAP 0x1e #define DRM_RADEON_GEM_PREAD 0x21 #define DRM_RADEON_GEM_PWRITE 0x22 #define DRM_RADEON_GEM_SET_DOMAIN 0x23 #define DRM_RADEON_GEM_WAIT_IDLE 0x24 #define DRM_RADEON_CS 0x26 #define DRM_RADEON_INFO 0x27 #define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) #define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) #define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) #define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) #define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) #define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) #define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) //#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) struct drm_radeon_gem_info { uint64_t gart_size; uint64_t vram_size; uint64_t vram_visible; }; #define RADEON_GEM_NO_BACKING_STORE 1 struct drm_radeon_gem_create { uint64_t size; uint64_t alignment; uint32_t handle; uint32_t initial_domain; uint32_t flags; }; struct drm_radeon_gem_mmap { uint32_t handle; uint32_t pad; uint64_t offset; uint64_t size; uint64_t addr_ptr; }; struct drm_radeon_gem_set_domain { uint32_t handle; uint32_t read_domains; uint32_t write_domain; }; struct drm_radeon_gem_wait_idle { uint32_t handle; uint32_t pad; }; struct drm_radeon_gem_busy { uint32_t handle; uint32_t busy; }; struct drm_radeon_gem_pread { /** Handle for the object being read. */ uint32_t handle; uint32_t pad; /** Offset into the object to read from */ uint64_t offset; /** Length of data to read */ uint64_t size; /** Pointer to write the data into. */ /* void *, but pointers are not 32/64 compatible */ uint64_t data_ptr; }; struct drm_radeon_gem_pwrite { /** Handle for the object being written to. */ uint32_t handle; uint32_t pad; /** Offset into the object to write to */ uint64_t offset; /** Length of data to write */ uint64_t size; /** Pointer to read the data from. */ /* void *, but pointers are not 32/64 compatible */ uint64_t data_ptr; }; #define RADEON_CHUNK_ID_RELOCS 0x01 #define RADEON_CHUNK_ID_IB 0x02 struct drm_radeon_cs_chunk { uint32_t chunk_id; uint32_t length_dw; uint64_t chunk_data; }; struct drm_radeon_cs_reloc { uint32_t handle; uint32_t read_domains; uint32_t write_domain; uint32_t flags; }; struct drm_radeon_cs { uint32_t num_chunks; uint32_t cs_id; /* this points to uint64_t * which point to cs chunks */ uint64_t chunks; /* updates to the limits after this CS ioctl */ uint64_t gart_limit; uint64_t vram_limit; }; #define RADEON_INFO_DEVICE_ID 0x00 #define RADEON_INFO_NUM_GB_PIPES 0x01 struct drm_radeon_info { uint32_t request; uint32_t pad; uint64_t value; }; cairo_bool_t radeon_info (int fd, uint64_t *gart_size, uint64_t *vram_size) { struct drm_radeon_gem_info info; int ret; ret = ioctl (fd, DRM_IOCTL_RADEON_GEM_INFO, &info); if (ret == -1) return FALSE; if (gart_size != NULL) *gart_size = info.gart_size; if (vram_size != NULL) *vram_size = info.vram_size; return TRUE; } void radeon_bo_write (const radeon_device_t *device, radeon_bo_t *bo, unsigned long offset, unsigned long size, const void *data) { struct drm_radeon_gem_pwrite pwrite; int ret; memset (&pwrite, 0, sizeof (pwrite)); pwrite.handle = bo->base.handle; pwrite.offset = offset; pwrite.size = size; pwrite.data_ptr = (uint64_t) (uintptr_t) data; do { ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PWRITE, &pwrite); } while (ret == -1 && errno == EINTR); /* XXX temporary workaround */ if (ret == -1 && errno == ENOSYS) { uint8_t *ptr; ptr = radeon_bo_map (device, bo); if (ptr != NULL) { memcpy (ptr + offset, data, size); radeon_bo_unmap (bo); } } } void radeon_bo_read (const radeon_device_t *device, radeon_bo_t *bo, unsigned long offset, unsigned long size, void *data) { struct drm_radeon_gem_pread pread; int ret; memset (&pread, 0, sizeof (pread)); pread.handle = bo->base.handle; pread.offset = offset; pread.size = size; pread.data_ptr = (uint64_t) (uintptr_t) data; do { ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PREAD, &pread); } while (ret == -1 && errno == EINTR); /* XXX temporary workaround */ if (ret == -1 && errno == ENOSYS) { uint8_t *ptr; ptr = radeon_bo_map (device, bo); if (ptr != NULL) { memcpy (data, ptr + offset, size); radeon_bo_unmap (bo); } } VG (VALGRIND_MAKE_MEM_DEFINED (data, size)); } void radeon_bo_wait (const radeon_device_t *device, radeon_bo_t *bo) { struct drm_radeon_gem_wait_idle wait; int ret; wait.handle = bo->base.handle; do { ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_WAIT_IDLE, &wait); } while (ret == -1 && (errno == EINTR || errno == EBUSY)); } void * radeon_bo_map (const radeon_device_t *device, radeon_bo_t *bo) { struct drm_radeon_gem_mmap mmap_arg; void *ptr; int ret; assert (bo->virtual == NULL); memset (&mmap_arg, 0, sizeof (mmap_arg)); mmap_arg.handle = bo->base.handle; mmap_arg.offset = 0; mmap_arg.size = bo->base.size; do { ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap_arg); } while (ret == -1 && errno == EINTR); if (unlikely (ret != 0)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } VG (VALGRIND_MAKE_MEM_DEFINED (&mmap_arg, sizeof (mmap_arg))); /* and mmap it */ ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE, MAP_SHARED, device->base.fd, mmap_arg.addr_ptr); if (unlikely (ptr == MAP_FAILED)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } bo->virtual = ptr; /* XXX set_domain? */ return bo->virtual; } void radeon_bo_unmap (radeon_bo_t *bo) { assert (bo->virtual != NULL); munmap (bo->virtual, bo->base.size); bo->virtual = NULL; } cairo_drm_bo_t * radeon_bo_create (radeon_device_t *device, uint32_t size, uint32_t initial_domain) { struct drm_radeon_gem_create create; radeon_bo_t *bo; int ret; bo = _cairo_freepool_alloc (&device->bo_pool); if (unlikely (bo == NULL)) return NULL; create.size = size; create.alignment = 0; create.initial_domain = initial_domain; create.flags = 0; create.handle = 0; do { ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_CREATE, &create); } while (ret == -1 && errno == EINTR); if (ret == -1) { _cairo_freepool_free (&device->bo_pool, bo); return NULL; } bo->base.handle = create.handle; bo->base.size = size; bo->virtual = NULL; bo->in_batch = FALSE; bo->read_domains = 0; bo->write_domain = 0; CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); return &bo->base; } cairo_drm_bo_t * radeon_bo_create_for_name (radeon_device_t *device, uint32_t name) { radeon_bo_t *bo; cairo_status_t status; bo = _cairo_freepool_alloc (&device->bo_pool); if (unlikely (bo == NULL)) return NULL; status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name); if (unlikely (status)) { _cairo_freepool_free (&device->bo_pool, bo); return NULL; } bo->virtual = NULL; bo->in_batch = FALSE; bo->read_domains = 0; bo->write_domain = 0; CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); return &bo->base; } static void radeon_bo_release (void *_dev, void *_bo) { radeon_device_t *device = _dev; radeon_bo_t *bo = _bo; _cairo_drm_bo_close (&device->base, &bo->base); _cairo_freepool_free (&device->bo_pool, bo); } cairo_surface_t * radeon_bo_get_image (const radeon_device_t *device, radeon_bo_t *bo, const cairo_drm_surface_t *surface) { cairo_image_surface_t *image; uint8_t *dst; int size, row; image = (cairo_image_surface_t *) cairo_image_surface_create (surface->format, surface->width, surface->height); if (unlikely (image->base.status)) return &image->base; if (image->stride == surface->stride) { size = surface->stride * surface->height; radeon_bo_read (device, bo, 0, size, image->data); } else { int offset; size = surface->width; if (surface->format != CAIRO_FORMAT_A8) size *= 4; offset = 0; row = surface->height; dst = image->data; while (row--) { radeon_bo_read (device, bo, offset, size, dst); offset += surface->stride; dst += image->stride; } } return &image->base; } static void _radeon_device_init_bo_cache (radeon_device_t *device) { _cairo_freepool_init (&device->bo_pool, sizeof (radeon_bo_t)); } cairo_status_t radeon_device_init (radeon_device_t *device, int fd) { _radeon_device_init_bo_cache (device); device->base.bo.release = radeon_bo_release; return CAIRO_STATUS_SUCCESS; } static void _radeon_bo_cache_fini (radeon_device_t *device) { _cairo_freepool_fini (&device->bo_pool); } void radeon_device_fini (radeon_device_t *device) { _radeon_bo_cache_fini (device); _cairo_drm_device_fini (&device->base); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm-surface.c000066400000000000000000000247441271037650300265440ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" void _cairo_drm_surface_init (cairo_drm_surface_t *surface, cairo_format_t format, int width, int height) { surface->bo = NULL; surface->format = format; surface->width = width; surface->height = height; surface->stride = 0; surface->fallback = NULL; surface->map_count = 0; } cairo_status_t _cairo_drm_surface_finish (cairo_drm_surface_t *surface) { assert (surface->fallback == NULL); if (surface->bo != NULL) cairo_drm_bo_destroy (surface->base.device, surface->bo); return CAIRO_STATUS_SUCCESS; } void _cairo_drm_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); } cairo_bool_t _cairo_drm_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_drm_surface_t *surface = abstract_surface; rectangle->x = 0; rectangle->y = 0; rectangle->width = surface->width; rectangle->height = surface->height; return TRUE; } cairo_surface_t * cairo_drm_surface_create (cairo_device_t *abstract_device, cairo_format_t format, int width, int height) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; cairo_surface_t *surface; if (device != NULL && device->base.status) { surface = _cairo_surface_create_in_error (device->base.status); } else if (device == NULL || device->surface.create == NULL || width == 0 || width > device->max_surface_size || height == 0 || height > device->max_surface_size) { surface = cairo_image_surface_create (format, width, height); } else if (device->base.finished) { surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); } else { surface = device->surface.create (device, format, width, height); if (surface->status == CAIRO_STATUS_INVALID_SIZE) surface = cairo_image_surface_create (format, width, height); } return surface; } cairo_surface_t * cairo_drm_surface_create_for_name (cairo_device_t *abstract_device, unsigned int name, cairo_format_t format, int width, int height, int stride) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; cairo_surface_t *surface; if (! CAIRO_FORMAT_VALID (format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); if (device != NULL && device->base.status) { surface = _cairo_surface_create_in_error (device->base.status); } else if (device == NULL || device->surface.create_for_name == NULL) { /* XXX invalid device! */ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } else if (width == 0 || width > device->max_surface_size || height == 0 || height > device->max_surface_size) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } else if (device->base.finished) { surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); } else { surface = device->surface.create_for_name (device, name, format, width, height, stride); } return surface; } slim_hidden_def (cairo_drm_surface_create_for_name); cairo_surface_t * cairo_drm_surface_create_from_cacheable_image (cairo_device_t *abstract_device, cairo_surface_t *surface) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; if (surface->status) { surface = _cairo_surface_create_in_error (surface->status); } else if (device != NULL && device->base.status) { surface = _cairo_surface_create_in_error (device->base.status); } else if (device == NULL || device->surface.create_from_cacheable_image == NULL) { /* XXX invalid device! */ surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } else if (device->base.finished) { surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); } else { surface = device->surface.create_from_cacheable_image (device, surface); } return surface; } static cairo_drm_surface_t * _cairo_surface_as_drm (cairo_surface_t *abstract_surface) { if (unlikely (abstract_surface->status)) return NULL; if (abstract_surface->type != CAIRO_SURFACE_TYPE_DRM) return NULL; return (cairo_drm_surface_t *) abstract_surface; } cairo_status_t cairo_drm_surface_enable_scan_out (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; cairo_drm_device_t *device; surface = _cairo_surface_as_drm (abstract_surface); if (unlikely (surface == NULL)) return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); if (unlikely (surface->base.finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); device = (cairo_drm_device_t *) surface->base.device; if (device->surface.enable_scan_out == NULL) return CAIRO_STATUS_SUCCESS; if (unlikely (device->base.finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); return device->surface.enable_scan_out (abstract_surface); } unsigned int cairo_drm_surface_get_handle (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } return surface->bo->handle; } cairo_int_status_t _cairo_drm_surface_flink (void *abstract_surface) { cairo_drm_surface_t *surface = abstract_surface; return _cairo_drm_bo_flink ((cairo_drm_device_t *) surface->base.device, surface->bo); } unsigned int cairo_drm_surface_get_name (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; cairo_drm_device_t *device; cairo_status_t status; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return 0; } if (surface->bo->name) return surface->bo->name; device = (cairo_drm_device_t *) surface->base.device; if (device->surface.flink == NULL) return 0; status = device->surface.flink (abstract_surface); if (status) { if (_cairo_status_is_error (status)) status = _cairo_surface_set_error (abstract_surface, status); return 0; } return surface->bo->name; } cairo_format_t cairo_drm_surface_get_format (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) return cairo_image_surface_get_format (abstract_surface); return surface->format; } int cairo_drm_surface_get_width (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) return cairo_image_surface_get_width (abstract_surface); return surface->width; } int cairo_drm_surface_get_height (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) return cairo_image_surface_get_height (abstract_surface); return surface->height; } int cairo_drm_surface_get_stride (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) return cairo_image_surface_get_stride (abstract_surface); return surface->stride; } /* XXX drm or general surface layer? naming? */ cairo_surface_t * cairo_drm_surface_map_to_image (cairo_surface_t *abstract_surface) { cairo_drm_surface_t *surface; cairo_drm_device_t *device; cairo_status_t status; if (unlikely (abstract_surface->status)) return _cairo_surface_create_in_error (abstract_surface->status); surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) { if (_cairo_surface_is_image (abstract_surface)) return cairo_surface_reference (abstract_surface); status = _cairo_surface_set_error (abstract_surface, CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return _cairo_surface_create_in_error (status); } surface->map_count++; device = (cairo_drm_device_t *) surface->base.device; return cairo_surface_reference (device->surface.map_to_image (surface)); } void cairo_drm_surface_unmap (cairo_surface_t *abstract_surface, cairo_surface_t *image) { cairo_drm_surface_t *surface; surface = _cairo_surface_as_drm (abstract_surface); if (surface == NULL) { if (_cairo_surface_is_image (abstract_surface)) cairo_surface_destroy (image); else _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } /* XXX assert image belongs to drm */ //assert (image == drm->fallback); cairo_surface_destroy (image); assert (surface->map_count > 0); if (--surface->map_count == 0) cairo_surface_flush (&surface->base); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/drm/cairo-drm.c000066400000000000000000000246561271037650300251200ustar00rootroot00000000000000/* Cairo - a vector graphics library with display and print output * * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Chris Wilson. */ #include "cairoint.h" #include "cairo-drm-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" #define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE #include #include #include /* open(), close() */ static cairo_drm_device_t *_cairo_drm_known_devices; static cairo_drm_device_t *_cairo_drm_default_device; static const char * get_udev_property(struct udev_device *device, const char *name) { struct udev_list_entry *entry; udev_list_entry_foreach (entry, udev_device_get_properties_list_entry (device)) { if (strcmp (udev_list_entry_get_name (entry), name) == 0) return udev_list_entry_get_value (entry); } return NULL; } static void _device_flush (void *abstract_device) { cairo_drm_device_t *device = abstract_device; device->device.flush (device); } static void _device_finish (void *abstract_device) { cairo_drm_device_t *device = abstract_device; CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); if (device->prev != NULL) device->prev->next = device->next; else _cairo_drm_known_devices = device->next; if (device->next != NULL) device->next->prev = device->prev; CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); if (_cairo_atomic_ptr_cmpxchg (&_cairo_drm_default_device, device, NULL)) { cairo_device_destroy (&device->base); } } static void _device_destroy (void *abstract_device) { cairo_drm_device_t *device = abstract_device; device->device.destroy (device); } static const cairo_device_backend_t _cairo_drm_device_backend = { CAIRO_DEVICE_TYPE_DRM, NULL, NULL, /* lock, unlock */ _device_flush, _device_finish, _device_destroy, }; cairo_drm_device_t * _cairo_drm_device_init (cairo_drm_device_t *dev, int fd, dev_t devid, int vendor_id, int chip_id, int max_surface_size) { assert (CAIRO_MUTEX_IS_LOCKED (_cairo_drm_device_mutex)); _cairo_device_init (&dev->base, &_cairo_drm_device_backend); dev->id = devid; dev->vendor_id = vendor_id; dev->chip_id = chip_id; dev->fd = fd; dev->max_surface_size = max_surface_size; dev->prev = NULL; dev->next = _cairo_drm_known_devices; if (_cairo_drm_known_devices != NULL) _cairo_drm_known_devices->prev = dev; _cairo_drm_known_devices = dev; if (_cairo_drm_default_device == NULL) _cairo_drm_default_device = (cairo_drm_device_t *) cairo_device_reference (&dev->base); return dev; } cairo_device_t * cairo_drm_device_get (struct udev_device *device) { static const struct dri_driver_entry { uint32_t vendor_id; uint32_t chip_id; cairo_drm_device_create_func_t create_func; } driver_map[] = { { 0x8086, 0x29a2, _cairo_drm_i965_device_create }, /* I965_G */ { 0x8086, 0x2982, _cairo_drm_i965_device_create }, /* G35_G */ { 0x8086, 0x2992, _cairo_drm_i965_device_create }, /* I965_Q */ { 0x8086, 0x2972, _cairo_drm_i965_device_create }, /* I946_GZ */ { 0x8086, 0x2a02, _cairo_drm_i965_device_create }, /* I965_GM */ { 0x8086, 0x2a12, _cairo_drm_i965_device_create }, /* I965_GME */ { 0x8086, 0x2e02, _cairo_drm_i965_device_create }, /* IGD_E_G */ { 0x8086, 0x2e22, _cairo_drm_i965_device_create }, /* G45_G */ { 0x8086, 0x2e12, _cairo_drm_i965_device_create }, /* Q45_G */ { 0x8086, 0x2e32, _cairo_drm_i965_device_create }, /* G41_G */ { 0x8086, 0x2a42, _cairo_drm_i965_device_create }, /* GM45_GM */ { 0x8086, 0x2582, _cairo_drm_i915_device_create }, /* I915_G */ { 0x8086, 0x2592, _cairo_drm_i915_device_create }, /* I915_GM */ { 0x8086, 0x258a, _cairo_drm_i915_device_create }, /* E7221_G */ { 0x8086, 0x2772, _cairo_drm_i915_device_create }, /* I945_G */ { 0x8086, 0x27a2, _cairo_drm_i915_device_create }, /* I945_GM */ { 0x8086, 0x27ae, _cairo_drm_i915_device_create }, /* I945_GME */ { 0x8086, 0x29c2, _cairo_drm_i915_device_create }, /* G33_G */ { 0x8086, 0x29b2, _cairo_drm_i915_device_create }, /* Q35_G */ { 0x8086, 0x29d2, _cairo_drm_i915_device_create }, /* Q33_G */ { 0x8086, 0xa011, _cairo_drm_i915_device_create }, /* IGD_GM */ { 0x8086, 0xa001, _cairo_drm_i915_device_create }, /* IGD_G */ /* XXX i830 */ { 0x8086, ~0, _cairo_drm_intel_device_create }, { 0x1002, ~0, _cairo_drm_radeon_device_create }, #if CAIRO_HAS_GALLIUM_SURFACE { ~0, ~0, _cairo_drm_gallium_device_create }, #endif }; cairo_drm_device_t *dev; dev_t devid; struct udev_device *parent; const char *pci_id; uint32_t vendor_id, chip_id; const char *path; int i, fd; devid = udev_device_get_devnum (device); CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) { if (dev->id == devid) { dev = (cairo_drm_device_t *) cairo_device_reference (&dev->base); goto DONE; } } parent = udev_device_get_parent (device); pci_id = get_udev_property (parent, "PCI_ID"); if (pci_id == NULL || sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) { dev = (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR); goto DONE; } #if CAIRO_HAS_GALLIUM_SURFACE if (getenv ("CAIRO_GALLIUM_FORCE")) { i = ARRAY_LENGTH (driver_map) - 1; } else #endif { for (i = 0; i < ARRAY_LENGTH (driver_map); i++) { if (driver_map[i].vendor_id == ~0U) break; if (driver_map[i].vendor_id == vendor_id && (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id)) break; } if (i == ARRAY_LENGTH (driver_map)) { dev = (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR); goto DONE; } } path = udev_device_get_devnode (device); if (path == NULL) path = "/dev/dri/card0"; /* XXX buggy udev? */ fd = open (path, O_RDWR); if (fd == -1) { /* XXX more likely to be a permissions issue... */ _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND); goto DONE; } dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id); if (dev == NULL) close (fd); DONE: CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); return &dev->base; } slim_hidden_def (cairo_drm_device_get); cairo_device_t * cairo_drm_device_get_for_fd (int fd) { struct stat st; struct udev *udev; struct udev_device *device; cairo_device_t *dev = NULL; if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) { //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE); return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); } udev = udev_new (); device = udev_device_new_from_devnum (udev, 'c', st.st_rdev); if (device != NULL) { dev = cairo_drm_device_get (device); udev_device_unref (device); } udev_unref (udev); return dev; } slim_hidden_def (cairo_drm_device_get_for_fd); cairo_device_t * cairo_drm_device_default (void) { struct udev *udev; struct udev_enumerate *e; struct udev_list_entry *entry; cairo_device_t *dev; /* optimistic atomic pointer read */ dev = &_cairo_drm_default_device->base; if (dev != NULL) return dev; udev = udev_new(); if (udev == NULL) return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); e = udev_enumerate_new (udev); udev_enumerate_add_match_subsystem (e, "drm"); udev_enumerate_scan_devices (e); udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) { struct udev_device *device; device = udev_device_new_from_syspath (udev, udev_list_entry_get_name (entry)); dev = cairo_drm_device_get (device); udev_device_unref (device); if (dev != NULL) { if (((cairo_drm_device_t *) dev)->fd == -1) { /* try again, we may find a usable card */ cairo_device_destroy (dev); dev = NULL; } else break; } } udev_enumerate_unref (e); udev_unref (udev); cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */ return dev; } slim_hidden_def (cairo_drm_device_default); void _cairo_drm_device_reset_static_data (void) { if (_cairo_drm_default_device != NULL) { cairo_device_t *device = &_cairo_drm_default_device->base; _cairo_drm_default_device = NULL; cairo_device_destroy (device); } } int cairo_drm_device_get_fd (cairo_device_t *abstract_device) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; if (device->base.status) return -1; return device->fd; } void _cairo_drm_device_fini (cairo_drm_device_t *device) { if (device->fd != -1) close (device->fd); } void cairo_drm_device_throttle (cairo_device_t *abstract_device) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; cairo_status_t status; if (unlikely (device->base.status)) return; if (device->device.throttle == NULL) return; status = device->device.throttle (device); if (unlikely (status)) _cairo_status_set_error (&device->base.status, status); } cairo_bool_t _cairo_drm_size_is_valid (cairo_device_t *abstract_device, int width, int height) { cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; if (unlikely (device->base.status)) return FALSE; return width <= device->max_surface_size && height <= device->max_surface_size; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/skia/000077500000000000000000000000001271037650300232275ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/skia/cairo-skia-context.cpp000066400000000000000000001353251271037650300274500ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California * Copyright © 2005 Red Hat, Inc. * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Chris Wilson */ #include "cairoint.h" #include "cairo-private.h" #include "cairo-error-private.h" #include "cairo-arc-private.h" #include "cairo-backend-private.h" #include "cairo-default-context-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-image-surface-inline.h" #include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-skia-private.h" #include "cairo-surface-backend-private.h" #include #include #include #include #if !defined(INFINITY) #define INFINITY HUGE_VAL #endif #if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED) # define CAIRO_FIXED_TO_SK_SCALAR(x) (x) #elif defined(SK_SCALAR_IS_FIXED) /* This can be done better, but this will do for now */ # define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x)) #else # define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x)) #endif #define UNSUPPORTED static freed_pool_t context_pool; static void _cairo_skia_context_destroy (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->path->reset (); cr->paint->reset (); delete cr->canvas; cairo_surface_destroy (&cr->target->image.base); cairo_surface_destroy (&cr->original->image.base); if (cr->source != NULL) { if (cr->source_image != NULL) { _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra); cr->source_image = NULL; } cairo_surface_destroy (cr->source); cr->source = NULL; } _cairo_fini (&cr->base); _freed_pool_put (&context_pool, cr); } static cairo_surface_t * _cairo_skia_context_get_original_target (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return &cr->original->image.base; } static cairo_surface_t * _cairo_skia_context_get_current_target (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return &cr->target->image.base; } static cairo_status_t _cairo_skia_context_save (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->canvas->save (); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_restore (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->canvas->restore (); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_surface_t *group_surface; cairo_status_t status; int width, height; //clip = _cairo_gstate_get_clip (cr->gstate); width = cr->target->image.width; height = cr->target->image.height; group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base, content, width, height); #if 0 /* Set device offsets on the new surface so that logically it appears at * the same location on the parent surface -- when we pop_group this, * the source pattern will get fixed up for the appropriate target surface * device offsets, so we want to set our own surface offsets from /that/, * and not from the device origin. */ cairo_surface_set_device_offset (group_surface, parent_surface->device_transform.x0 - extents.x, parent_surface->device_transform.y0 - extents.y); /* If we have a current path, we need to adjust it to compensate for * the device offset just applied. */ _cairo_path_fixed_transform (cr->path, &group_surface->device_transform); #endif status = _cairo_skia_context_save (cr); if (unlikely (status)) { cairo_surface_destroy (group_surface); return status; } cairo_surface_destroy (&cr->target->image.base); cr->target = (cairo_skia_surface_t *) group_surface; return CAIRO_STATUS_SUCCESS; } static cairo_pattern_t * _cairo_skia_context_pop_group (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_surface_t *group_surface; cairo_pattern_t *group_pattern; cairo_status_t status; group_surface = cairo_surface_reference (&cr->target->image.base); status = _cairo_skia_context_restore (cr); if (unlikely (status)) { group_pattern = _cairo_pattern_create_in_error (status); goto done; } group_pattern = cairo_pattern_create_for_surface (group_surface); status = group_pattern->status; if (unlikely (status)) goto done; #if 0 _cairo_gstate_get_matrix (cr->gstate, &group_matrix); /* Transform by group_matrix centered around device_transform so that when * we call _cairo_gstate_copy_transformed_pattern the result is a pattern * with a matrix equivalent to the device_transform of group_surface. */ if (_cairo_surface_has_device_transform (group_surface)) { cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); _cairo_pattern_transform (group_pattern, &group_matrix); _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); } else { cairo_pattern_set_matrix (group_pattern, &group_matrix); } /* If we have a current path, we need to adjust it to compensate for * the device offset just removed. */ _cairo_path_fixed_transform (cr->path, &group_surface->device_transform_inverse); #endif done: cairo_surface_destroy (group_surface); return group_pattern; } static inline cairo_surface_t * surface_from_pattern (const cairo_pattern_t *pattern) { return (reinterpret_cast (pattern))->surface; } static inline bool surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap) { cairo_image_surface_t *img = (cairo_image_surface_t *) surface; SkBitmap::Config config; bool opaque; if (unlikely (! format_to_sk_config (img->format, config, opaque))) return false; bitmap.reset (); bitmap.setConfig (config, img->width, img->height, img->stride); bitmap.setIsOpaque (opaque); bitmap.setPixels (img->data); return true; } static inline SkMatrix matrix_to_sk (const cairo_matrix_t& mat) { SkMatrix skm; skm.reset (); skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx)); skm.set (SkMatrix::kMSkewX, SkFloatToScalar (mat.xy)); skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0)); skm.set (SkMatrix::kMSkewY, SkFloatToScalar (mat.yx)); skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy)); skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0)); /* skm[6] = SkFloatToScalar (0.0); skm[7] = SkFloatToScalar (0.0); skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself. It wants Sk_Fract1 (2.30), not Sk_Scalar1 */ return skm; } static inline SkMatrix matrix_inverse_to_sk (const cairo_matrix_t& mat) { cairo_matrix_t inv = mat; cairo_status_t status = cairo_matrix_invert (&inv); assert (status == CAIRO_STATUS_SUCCESS); return matrix_to_sk (inv); } static SkShader::TileMode extend_to_sk (cairo_extend_t extend) { static const SkShader::TileMode modeMap[] = { SkShader::kClamp_TileMode, // NONE behaves like PAD, because noone wants NONE SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode, SkShader::kClamp_TileMode }; return modeMap[extend]; } static inline SkColor color_to_sk (const cairo_color_t& c) { /* Need unpremultiplied 1-byte values */ return SkColorSetARGB ((U8CPU) (c.alpha * 255), (U8CPU) (c.red * 255), (U8CPU) (c.green * 255), (U8CPU) (c.blue * 255)); } static inline SkColor color_stop_to_sk (const cairo_color_stop_t& c) { /* Need unpremultiplied 1-byte values */ return SkColorSetARGB ((U8CPU) (c.alpha * 255), (U8CPU) (c.red * 255), (U8CPU) (c.green * 255), (U8CPU) (c.blue * 255)); } static SkShader* source_to_sk_shader (cairo_skia_context_t *cr, const cairo_pattern_t *pattern) { SkShader *shader = NULL; if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; return new SkColorShader (color_to_sk (solid->color)); } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_t *surface = surface_from_pattern (pattern); cr->source = cairo_surface_reference (surface); if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; shader = SkShader::CreateBitmapShader (*esurf->bitmap, extend_to_sk (pattern->extend), extend_to_sk (pattern->extend)); } else { SkBitmap bitmap; if (! _cairo_surface_is_image (surface)) { cairo_status_t status; status = _cairo_surface_acquire_source_image (surface, &cr->source_image, &cr->source_extra); if (status) return NULL; surface = &cr->source_image->base; } if (unlikely (! surface_to_sk_bitmap (surface, bitmap))) return NULL; shader = SkShader::CreateBitmapShader (bitmap, extend_to_sk (pattern->extend), extend_to_sk (pattern->extend)); } } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */) { cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; SkColor colors_stack[10]; SkScalar pos_stack[10]; SkColor *colors = colors_stack; SkScalar *pos = pos_stack; if (gradient->n_stops > 10) { colors = new SkColor[gradient->n_stops]; pos = new SkScalar[gradient->n_stops]; } for (unsigned int i = 0; i < gradient->n_stops; i++) { pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset); colors[i] = color_stop_to_sk (gradient->stops[i].color); } if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; SkPoint points[2]; points[0].set (SkFloatToScalar (linear->pd1.x), SkFloatToScalar (linear->pd1.y)); points[1].set (SkFloatToScalar (linear->pd2.x), SkFloatToScalar (linear->pd2.y)); shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops, extend_to_sk (pattern->extend)); } else { // XXX todo -- implement real radial shaders in Skia } if (gradient->n_stops > 10) { delete [] colors; delete [] pos; } } if (shader && ! _cairo_matrix_is_identity (&pattern->matrix)) shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix)); return shader; } static inline bool pattern_filter_to_sk (const cairo_pattern_t *pattern) { switch (pattern->filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: return true; default: case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: return false; } } static inline bool pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color) { if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return false; color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color); return true; } static cairo_status_t _cairo_skia_context_set_source (void *abstract_cr, cairo_pattern_t *source) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkColor color; if (cr->source != NULL) { if (cr->source_image != NULL) { _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra); cr->source_image = NULL; } cairo_surface_destroy (cr->source); cr->source = NULL; } if (pattern_to_sk_color (source, color)) { cr->paint->setColor (color); } else { SkShader *shader = source_to_sk_shader (cr, source); if (shader == NULL) { UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } cr->paint->setShader (shader); shader->unref (); cr->paint->setFilterBitmap (pattern_filter_to_sk (source)); } /* XXX change notification */ return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* Need unpremultiplied 1-byte values */ cr->paint->setARGB ((U8CPU) (alpha * 255), (U8CPU) (red * 255), (U8CPU) (green * 255), (U8CPU) (blue * 255)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_source_surface (void *abstract_cr, cairo_surface_t *surface, double x, double y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_pattern_t *pattern; cairo_matrix_t matrix; cairo_status_t status; if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; SkShader *shader; shader = SkShader::CreateBitmapShader (*esurf->bitmap, SkShader::kClamp_TileMode, /* XXX */ SkShader::kClamp_TileMode); cr->paint->setShader (shader); shader->unref (); cr->paint->setFilterBitmap (true); return CAIRO_STATUS_SUCCESS; } pattern = cairo_pattern_create_for_surface (surface); if (unlikely (pattern->status)) return pattern->status; cairo_matrix_init_translate (&matrix, -x, -y); cairo_pattern_set_matrix (pattern, &matrix); status = _cairo_skia_context_set_source (cr, pattern); cairo_pattern_destroy (pattern); return status; } static cairo_pattern_t * _cairo_skia_context_get_source (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return NULL; } static cairo_status_t _cairo_skia_context_set_tolerance (void *abstract_cr, double tolerance) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX ignored */ return CAIRO_STATUS_SUCCESS; } static inline SkXfermode::Mode operator_to_sk (cairo_operator_t op) { static const SkXfermode::Mode modeMap[] = { SkXfermode::kClear_Mode, SkXfermode::kSrc_Mode, SkXfermode::kSrcOver_Mode, SkXfermode::kSrcIn_Mode, SkXfermode::kSrcOut_Mode, SkXfermode::kSrcATop_Mode, SkXfermode::kDst_Mode, SkXfermode::kDstOver_Mode, SkXfermode::kDstIn_Mode, SkXfermode::kDstOut_Mode, SkXfermode::kDstATop_Mode, SkXfermode::kXor_Mode, SkXfermode::kPlus_Mode, // XXX Add? SkXfermode::kPlus_Mode, // XXX SATURATE SkXfermode::kPlus_Mode, SkXfermode::kMultiply_Mode, SkXfermode::kScreen_Mode, SkXfermode::kOverlay_Mode, SkXfermode::kDarken_Mode, SkXfermode::kLighten_Mode, SkXfermode::kColorDodge_Mode, SkXfermode::kColorBurn_Mode, SkXfermode::kHardLight_Mode, SkXfermode::kSoftLight_Mode, SkXfermode::kDifference_Mode, SkXfermode::kExclusion_Mode, SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION, SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR, SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY }; return modeMap[op]; } static cairo_status_t _cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setXfermodeMode (operator_to_sk (op)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_opacity (void *abstract_cr, double opacity) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX */ return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_fill_rule (void *abstract_cr, cairo_fill_rule_t fill_rule) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_line_width (void *abstract_cr, double line_width) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setStrokeWidth (SkFloatToScalar (line_width)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_line_cap (void *abstract_cr, cairo_line_cap_t line_cap) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; static const SkPaint::Cap map[] = { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap }; cr->paint->setStrokeCap (map[line_cap]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_line_join (void *abstract_cr, cairo_line_join_t line_join) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; static const SkPaint::Join map[] = { SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join }; cr->paint->setStrokeJoin (map[line_join]); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_dash (void *abstract_cr, const double *dashes, int num_dashes, double offset) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkScalar intervals_static[20]; SkScalar *intervals = intervals_static; if (num_dashes == 0) { cr->paint->setPathEffect (NULL); return CAIRO_STATUS_SUCCESS; } int loop = 0; if ((num_dashes & 1) != 0) { loop = 1; num_dashes <<= 1; } if (num_dashes > 20) intervals = new SkScalar[num_dashes]; int i = 0; do { for (int j = 0; i < num_dashes; j++) intervals[i++] = SkFloatToScalar (dashes[j]); } while (loop--); SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset)); cr->paint->setPathEffect (dash); dash->unref (); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_miter_limit (void *abstract_cr, double limit) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setStrokeMiter (SkFloatToScalar (limit)); return CAIRO_STATUS_SUCCESS; } static cairo_antialias_t _cairo_skia_context_get_antialias (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE; } static void _cairo_skia_context_get_dash (void *abstract_cr, double *dashes, int *num_dashes, double *offset) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; *num_dashes = 0; /* XXX */ } static cairo_fill_rule_t _cairo_skia_context_get_fill_rule (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkPath::FillType ft; ft = cr->path->getFillType (); if (ft == SkPath::kWinding_FillType) return CAIRO_FILL_RULE_WINDING; if (ft == SkPath::kEvenOdd_FillType) return CAIRO_FILL_RULE_EVEN_ODD;; UNSUPPORTED; return CAIRO_FILL_RULE_WINDING; } static double _cairo_skia_context_get_line_width (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return /* ScalarToFloat */ cr->paint->getStrokeWidth (); } static cairo_line_cap_t _cairo_skia_context_get_line_cap (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; static const cairo_line_cap_t map[] = { CAIRO_LINE_CAP_BUTT, CAIRO_LINE_CAP_ROUND, CAIRO_LINE_CAP_SQUARE }; return map[cr->paint->getStrokeCap ()]; } static cairo_line_join_t _cairo_skia_context_get_line_join (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; static const cairo_line_join_t map[] = { CAIRO_LINE_JOIN_MITER, CAIRO_LINE_JOIN_ROUND, CAIRO_LINE_JOIN_BEVEL }; return map[cr->paint->getStrokeJoin ()]; } static double _cairo_skia_context_get_miter_limit (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return /* SkScalarToFloat */ cr->paint->getStrokeMiter (); } static cairo_operator_t _cairo_skia_context_get_operator (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; //cr->paint->getXfermode (); return CAIRO_OPERATOR_OVER; } static double _cairo_skia_context_get_opacity (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return 1.; } static double _cairo_skia_context_get_tolerance (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX */ return CAIRO_GSTATE_TOLERANCE_DEFAULT; } /* Current tranformation matrix */ static cairo_status_t _cairo_skia_context_translate (void *abstract_cr, double tx, double ty) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_translate (&cr->matrix, tx, ty); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_scale (void *abstract_cr, double sx, double sy) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_scale (&cr->matrix, sx, sy); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rotate (void *abstract_cr, double theta) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_rotate (&cr->matrix, theta); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_transform (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->matrix = *matrix; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_identity_matrix (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_init_identity (&cr->matrix); return CAIRO_STATUS_SUCCESS; } static void _cairo_skia_context_get_matrix (void *abstract_cr, cairo_matrix_t *matrix) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; *matrix = cr->matrix; } static void _cairo_skia_context_user_to_device (void *abstract_cr, double *x, double *y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_transform_point (&cr->matrix, x, y); } static void _cairo_skia_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_transform_distance (&cr->matrix, dx, dy); } static void _cairo_skia_context_device_to_user (void *abstract_cr, double *x, double *y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_t inverse; cairo_status_t status; inverse = cr->matrix; status = cairo_matrix_invert (&inverse); assert (CAIRO_STATUS_SUCCESS == status); cairo_matrix_transform_point (&inverse, x, y); } static void _cairo_skia_context_device_to_user_distance (void *abstract_cr, double *dx, double *dy) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_matrix_t inverse; cairo_status_t status; inverse = cr->matrix; status = cairo_matrix_invert (&inverse); assert (CAIRO_STATUS_SUCCESS == status); cairo_matrix_transform_distance (&inverse, dx, dy); } /* Path constructor */ static cairo_status_t _cairo_skia_context_new_path (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->path->reset (); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_new_sub_path (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->path->rMoveTo (0, 0); /* XXX */ return CAIRO_STATUS_SUCCESS; } static void user_to_device_point (cairo_skia_context_t *cr, double *x, double *y) { cairo_matrix_transform_point (&cr->matrix, x, y); cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y); } static void user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy) { cairo_matrix_transform_distance (&cr->matrix, dx, dy); cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy); } static cairo_status_t _cairo_skia_context_move_to (void *abstract_cr, double x, double y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_point (cr, &x, &y); cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_line_to (void *abstract_cr, double x, double y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_point (cr, &x, &y); cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_curve_to (void *abstract_cr, double x1, double y1, double x2, double y2, double x3, double y3) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_point (cr, &x1, &y1); user_to_device_point (cr, &x2, &y2); user_to_device_point (cr, &x3, &y3); cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1), SkFloatToScalar (x2), SkFloatToScalar (y2), SkFloatToScalar (x3), SkFloatToScalar (y3)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_arc_to (void *abstract_cr, double x1, double y1, double x2, double y2, double radius) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; #if 0 user_to_device_point (cr, &x1, &y1); user_to_device_point (cr, &x2, &y2); user_to_device_distance (cr, &radius, &radius); #endif cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1), SkFloatToScalar (x2), SkFloatToScalar (y2), SkFloatToScalar (radius)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_distance (cr, &dx, &dy); cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_distance (cr, &dx, &dy); cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rel_curve_to (void *abstract_cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; user_to_device_distance (cr, &dx1, &dy1); user_to_device_distance (cr, &dx2, &dy2); user_to_device_distance (cr, &dx3, &dy3); cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1), SkFloatToScalar (dx2), SkFloatToScalar (dy2), SkFloatToScalar (dx3), SkFloatToScalar (dy3)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rel_arc_to (void *abstract_cr, double dx1, double dy1, double dx2, double dy2, double radius) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; #if 0 user_to_device_point (cr, &x1, &y1); user_to_device_point (cr, &x2, &y2); user_to_device_distance (cr, &radius, &radius); #endif cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1), SkFloatToScalar (dx2), SkFloatToScalar (dy2), SkFloatToScalar (radius)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_close_path (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->path->close (); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_rectangle (void *abstract_cr, double x, double y, double width, double height) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; double x1, y1, x2, y2; /* XXX assume no rotation! */ x1 = x, y1 = y; user_to_device_point (cr, &x1, &y1); x2 = x + width, y2 = y + height; user_to_device_point (cr, &x2, &y2); cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1), SkFloatToScalar (x2), SkFloatToScalar (y2)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_arc (void *abstract_cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; /* XXX cr->path->arc() */ /* Do nothing, successfully, if radius is <= 0 */ if (radius <= 0.0) { status = _cairo_skia_context_line_to (cr, xc, yc); if (unlikely (status)) return status; status = _cairo_skia_context_line_to (cr, xc, yc); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } status = _cairo_skia_context_line_to (cr, xc + radius * cos (angle1), yc + radius * sin (angle1)); if (unlikely (status)) return status; if (forward) _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2); else _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2); return CAIRO_STATUS_SUCCESS; } static void _cairo_skia_context_path_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkRect rect; rect = cr->path->getBounds (); UNSUPPORTED; /* XXX transform SkScalar rect to user */ } static cairo_bool_t _cairo_skia_context_has_current_point (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return TRUE; } static cairo_bool_t _cairo_skia_context_get_current_point (void *abstract_cr, double *x, double *y) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkPoint pt; cr->path->getLastPt (&pt); //*x = SkScalarToFloat (pt.x); //*y = SkScalarToFloat (pt.y); //_cairo_gstate_backend_to_user (cr->gstate, x, y); return TRUE; } static cairo_path_t * _cairo_skia_context_copy_path (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX iterate */ UNSUPPORTED; return NULL; } static cairo_path_t * _cairo_skia_context_copy_path_flat (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX iterate and decompose */ UNSUPPORTED; return NULL; } static cairo_status_t _cairo_skia_context_append_path (void *abstract_cr, const cairo_path_t *path) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; // return _cairo_path_append_to_context (path, cr); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_stroke_to_path (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setStyle (SkPaint::kStroke_Style); cr->paint->getFillPath (*cr->path, cr->path); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_paint (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; #if 0 if (cr->source != NULL) { SkBitmap bitmap; SkMatrix bitmapMatrix; if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) { cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type; bitmap = *esurf->bitmap; } else { surface_to_sk_bitmap (&cr->source_image->base, bitmap); } // XXX pattern->matrix, pattern->filter, pattern->extend cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint); } else { cr->canvas->drawPaint (*cr->paint); } #else cr->canvas->drawPaint (*cr->paint); #endif return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_paint_with_alpha (void *abstract_cr, double alpha) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; if (CAIRO_ALPHA_IS_OPAQUE (alpha)) return _cairo_skia_context_paint (cr); cr->paint->setAlpha(SkScalarRound(255*alpha)); status = _cairo_skia_context_paint (cr); cr->paint->setAlpha(255); return status; } static cairo_status_t _cairo_skia_context_mask (void *abstract_cr, cairo_pattern_t *mask) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX */ //UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_stroke_preserve (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setStyle (SkPaint::kStroke_Style); /* XXX pen transformation? */ //assert (_cairo_matrix_is_identity (&cr->matrix)); cr->canvas->drawPath (*cr->path, *cr->paint); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_stroke (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; status = _cairo_skia_context_stroke_preserve (cr); if (unlikely (status)) return status; return _cairo_skia_context_new_path (cr); } static cairo_status_t _cairo_skia_context_in_stroke (void *abstract_cr, double x, double y, cairo_bool_t *inside) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_stroke_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_fill_preserve (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->paint->setStyle (SkPaint::kFill_Style); cr->canvas->drawPath (*cr->path, *cr->paint); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_fill (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; status = _cairo_skia_context_fill_preserve (cr); if (unlikely (status)) return status; return _cairo_skia_context_new_path (cr); } static cairo_status_t _cairo_skia_context_in_fill (void *abstract_cr, double x, double y, cairo_bool_t *inside) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_fill_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_clip_preserve (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cr->canvas->clipPath (*cr->path); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_clip (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; status = _cairo_skia_context_clip_preserve (cr); if (unlikely (status)) return status; return _cairo_skia_context_new_path (cr); } static cairo_status_t _cairo_skia_context_in_clip (void *abstract_cr, double x, double y, cairo_bool_t *inside) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_reset_clip (void *abstract_cr) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; SkRegion rgn(SkIRect::MakeWH (cr->target->bitmap->width (), cr->target->bitmap->height ())); cr->canvas->setClipRegion(rgn); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_clip_extents (void *abstract_cr, double *x1, double *y1, double *x2, double *y2) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_rectangle_list_t * _cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return NULL; } static cairo_status_t _cairo_skia_context_copy_page (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_show_page (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_font_face (void *abstract_cr, cairo_font_face_t *font_face) { // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; //return _cairo_gstate_set_font_face (cr->gstate, font_face); return CAIRO_STATUS_SUCCESS; } static cairo_font_face_t * _cairo_skia_context_get_font_face (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return NULL; } static cairo_status_t _cairo_skia_context_font_extents (void *abstract_cr, cairo_font_extents_t *extents) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_font_size (void *abstract_cr, double size) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_set_font_matrix (void *abstract_cr, const cairo_matrix_t *matrix) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static void _cairo_skia_context_get_font_matrix (void *abstract_cr, cairo_matrix_t *matrix) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; } static cairo_status_t _cairo_skia_context_set_font_options (void *abstract_cr, const cairo_font_options_t *options) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static void _cairo_skia_context_get_font_options (void *abstract_cr, cairo_font_options_t *options) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; } static cairo_status_t _cairo_skia_context_set_scaled_font (void *abstract_cr, cairo_scaled_font_t *scaled_font) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_scaled_font_t * _cairo_skia_context_get_scaled_font (void *abstract_cr) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return _cairo_scaled_font_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } static cairo_status_t _cairo_skia_context_glyphs (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_glyph_text_info_t *info) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; /* XXX */ //UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_glyph_path (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_skia_context_glyph_extents (void *abstract_cr, const cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) { //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static const cairo_backend_t _cairo_skia_context_backend = { CAIRO_TYPE_SKIA, _cairo_skia_context_destroy, _cairo_skia_context_get_original_target, _cairo_skia_context_get_current_target, _cairo_skia_context_save, _cairo_skia_context_restore, _cairo_skia_context_push_group, _cairo_skia_context_pop_group, _cairo_skia_context_set_source_rgba, _cairo_skia_context_set_source_surface, _cairo_skia_context_set_source, _cairo_skia_context_get_source, _cairo_skia_context_set_antialias, _cairo_skia_context_set_dash, _cairo_skia_context_set_fill_rule, _cairo_skia_context_set_line_cap, _cairo_skia_context_set_line_join, _cairo_skia_context_set_line_width, _cairo_skia_context_set_miter_limit, _cairo_skia_context_set_opacity, _cairo_skia_context_set_operator, _cairo_skia_context_set_tolerance, _cairo_skia_context_get_antialias, _cairo_skia_context_get_dash, _cairo_skia_context_get_fill_rule, _cairo_skia_context_get_line_cap, _cairo_skia_context_get_line_join, _cairo_skia_context_get_line_width, _cairo_skia_context_get_miter_limit, _cairo_skia_context_get_opacity, _cairo_skia_context_get_operator, _cairo_skia_context_get_tolerance, _cairo_skia_context_translate, _cairo_skia_context_scale, _cairo_skia_context_rotate, _cairo_skia_context_transform, _cairo_skia_context_set_matrix, _cairo_skia_context_set_identity_matrix, _cairo_skia_context_get_matrix, _cairo_skia_context_user_to_device, _cairo_skia_context_user_to_device_distance, _cairo_skia_context_device_to_user, _cairo_skia_context_device_to_user_distance, _cairo_skia_context_user_to_device, /* XXX backend */ _cairo_skia_context_user_to_device_distance, /* XXX backend */ _cairo_skia_context_device_to_user, /* XXX backend */ _cairo_skia_context_device_to_user_distance, /* XXX backend */ _cairo_skia_context_new_path, _cairo_skia_context_new_sub_path, _cairo_skia_context_move_to, _cairo_skia_context_rel_move_to, _cairo_skia_context_line_to, _cairo_skia_context_rel_line_to, _cairo_skia_context_curve_to, _cairo_skia_context_rel_curve_to, _cairo_skia_context_arc_to, _cairo_skia_context_rel_arc_to, _cairo_skia_context_close_path, _cairo_skia_context_arc, _cairo_skia_context_rectangle, _cairo_skia_context_path_extents, _cairo_skia_context_has_current_point, _cairo_skia_context_get_current_point, _cairo_skia_context_copy_path, _cairo_skia_context_copy_path_flat, _cairo_skia_context_append_path, _cairo_skia_stroke_to_path, _cairo_skia_context_clip, _cairo_skia_context_clip_preserve, _cairo_skia_context_in_clip, _cairo_skia_context_clip_extents, _cairo_skia_context_reset_clip, _cairo_skia_context_copy_clip_rectangle_list, _cairo_skia_context_paint, _cairo_skia_context_paint_with_alpha, _cairo_skia_context_mask, _cairo_skia_context_stroke, _cairo_skia_context_stroke_preserve, _cairo_skia_context_in_stroke, _cairo_skia_context_stroke_extents, _cairo_skia_context_fill, _cairo_skia_context_fill_preserve, _cairo_skia_context_in_fill, _cairo_skia_context_fill_extents, _cairo_skia_context_set_font_face, _cairo_skia_context_get_font_face, _cairo_skia_context_set_font_size, _cairo_skia_context_set_font_matrix, _cairo_skia_context_get_font_matrix, _cairo_skia_context_set_font_options, _cairo_skia_context_get_font_options, _cairo_skia_context_set_scaled_font, _cairo_skia_context_get_scaled_font, _cairo_skia_context_font_extents, _cairo_skia_context_glyphs, _cairo_skia_context_glyph_path, _cairo_skia_context_glyph_extents, _cairo_skia_context_copy_page, _cairo_skia_context_show_page, }; cairo_t * _cairo_skia_context_create (void *target) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target; cairo_skia_context_t *cr; cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool); if (unlikely (cr == NULL)) { cr = new cairo_skia_context_t; if (unlikely (cr == NULL)) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); cr->path = new SkPath; cr->paint = new SkPaint; } _cairo_init (&cr->base, &_cairo_skia_context_backend); cr->source = NULL; cr->source_image = NULL; cr->paint->setStrokeWidth (SkFloatToScalar (2.0)); cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target); cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target); cr->canvas = new SkCanvas (*surface->bitmap); cr->canvas->save (); cairo_matrix_init_identity (&cr->matrix); return &cr->base; } #if 0 void _cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint) { *cr->paint = paint; } void _cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path) { *cr->path = path; } #endif Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/skia/cairo-skia-private.h000066400000000000000000000057041271037650300271000ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Carl D. Worth */ #ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H #define CAIRO_SKIA_CONTEXT_PRIVATE_H #include "cairo-private.h" #include "cairo-image-surface-private.h" #include #include #include #include typedef struct _cairo_skia_context cairo_skia_context_t; typedef struct _cairo_skia_surface cairo_skia_surface_t; struct _cairo_skia_context { cairo_t base; cairo_skia_surface_t *original; cairo_skia_surface_t *target; cairo_matrix_t matrix; SkCanvas *canvas; SkPaint *paint; SkPath *path; cairo_surface_t *source; cairo_image_surface_t *source_image; void *source_extra; }; struct _cairo_skia_surface { cairo_image_surface_t image; SkBitmap *bitmap; }; static inline bool format_to_sk_config (cairo_format_t format, SkBitmap::Config& config, bool& opaque) { opaque = false; switch (format) { case CAIRO_FORMAT_ARGB32: config = SkBitmap::kARGB_8888_Config; break; case CAIRO_FORMAT_RGB24: config = SkBitmap::kARGB_8888_Config; opaque = true; break; case CAIRO_FORMAT_RGB16_565: config = SkBitmap::kRGB_565_Config; opaque = true; break; case CAIRO_FORMAT_A8: config = SkBitmap::kA8_Config; break; case CAIRO_FORMAT_A1: config = SkBitmap::kA1_Config; break; case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_INVALID: default: return false; } return true; } cairo_private cairo_t * _cairo_skia_context_create (void *target); #endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/skia/cairo-skia-surface.cpp000066400000000000000000000216331271037650300274100ustar00rootroot00000000000000/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Corporation. * * Contributor(s): * Vladimir Vukicevic */ #include "cairoint.h" #include "cairo-skia.h" #include "cairo-skia-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-fallback-private.h" static cairo_skia_surface_t * _cairo_skia_surface_create_internal (SkBitmap::Config config, bool opaque, unsigned char *data, int width, int height, int stride); static cairo_surface_t * _cairo_skia_surface_create_similar (void *asurface, cairo_content_t content, int width, int height) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; SkBitmap::Config config; bool opaque; if (content == surface->image.base.content) { config = surface->bitmap->getConfig (); opaque = surface->bitmap->isOpaque (); } else if (! format_to_sk_config (_cairo_format_from_content (content), config, opaque)) { return NULL; } return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base; } static cairo_status_t _cairo_skia_surface_finish (void *asurface) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; cairo_surface_finish (&surface->image.base); delete surface->bitmap; return CAIRO_STATUS_SUCCESS; } static cairo_image_surface_t * _cairo_skia_surface_map_to_image (void *asurface, const cairo_rectangle_int_t *extents) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; surface->bitmap->lockPixels (); return _cairo_image_surface_map_to_image (&surface->image, extents); } static cairo_int_status_t _cairo_skia_surface_unmap_image (void *asurface, cairo_image_surface_t *image) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; cairo_int_status_t status; status = _cairo_image_surface_unmap_image (&surface->image, image); surface->bitmap->unlockPixels (); return status; } static cairo_status_t _cairo_skia_surface_acquire_source_image (void *asurface, cairo_image_surface_t **image_out, void **image_extra) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; surface->bitmap->lockPixels (); *image_out = &surface->image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } static void _cairo_skia_surface_release_source_image (void *asurface, cairo_image_surface_t *image, void *image_extra) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; surface->bitmap->unlockPixels (); } static cairo_bool_t _cairo_skia_surface_get_extents (void *asurface, cairo_rectangle_int_t *extents) { cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; extents->x = extents->y = 0; extents->width = surface->image.width; extents->height = surface->image.height; return TRUE; } static void _cairo_skia_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } static const struct _cairo_surface_backend cairo_skia_surface_backend = { CAIRO_SURFACE_TYPE_SKIA, _cairo_skia_surface_finish, _cairo_skia_context_create, _cairo_skia_surface_create_similar, NULL, //_cairo_skia_surface_create_similar_image, _cairo_skia_surface_map_to_image, _cairo_skia_surface_unmap_image, _cairo_surface_default_source, _cairo_skia_surface_acquire_source_image, _cairo_skia_surface_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_skia_surface_get_extents, _cairo_skia_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ /* XXX native surface functions? */ _cairo_surface_fallback_paint, _cairo_surface_fallback_mask, _cairo_surface_fallback_stroke, _cairo_surface_fallback_fill, NULL, /* fill/stroke */ _cairo_surface_fallback_glyphs }; /* * Surface constructors */ static inline pixman_format_code_t sk_config_to_pixman_format_code (SkBitmap::Config config, bool opaque) { switch (config) { case SkBitmap::kARGB_8888_Config: return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8; case SkBitmap::kA8_Config: return PIXMAN_a8; case SkBitmap::kA1_Config: return PIXMAN_a1; case SkBitmap::kRGB_565_Config: return PIXMAN_r5g6b5; case SkBitmap::kARGB_4444_Config: return PIXMAN_a4r4g4b4; case SkBitmap::kNo_Config: case SkBitmap::kIndex8_Config: case SkBitmap::kRLE_Index8_Config: case SkBitmap::kConfigCount: default: ASSERT_NOT_REACHED; return (pixman_format_code_t) -1; } } static cairo_skia_surface_t * _cairo_skia_surface_create_internal (SkBitmap::Config config, bool opaque, unsigned char *data, int width, int height, int stride) { cairo_skia_surface_t *surface; pixman_image_t *pixman_image; pixman_format_code_t pixman_format; surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t)); if (unlikely (surface == NULL)) return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); pixman_format = sk_config_to_pixman_format_code (config, opaque); pixman_image = pixman_image_create_bits (pixman_format, width, height, (uint32_t *) data, stride); if (unlikely (pixman_image == NULL)) return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->image.base, &cairo_skia_surface_backend, NULL, /* device */ _cairo_content_from_pixman_format (pixman_format)); _cairo_image_surface_init (&surface->image, pixman_image, pixman_format); surface->bitmap = new SkBitmap; surface->bitmap->setConfig (config, width, height, surface->image.stride); surface->bitmap->setIsOpaque (opaque); surface->bitmap->setPixels (surface->image.data); surface->image.base.is_clear = data == NULL; return surface; } cairo_surface_t * cairo_skia_surface_create (cairo_format_t format, int width, int height) { SkBitmap::Config config; bool opaque; if (! CAIRO_FORMAT_VALID (format) || ! format_to_sk_config (format, config, opaque)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base; } cairo_surface_t * cairo_skia_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride) { SkBitmap::Config config; bool opaque; if (! CAIRO_FORMAT_VALID (format) || ! format_to_sk_config (format, config, opaque)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); } return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base; } /*** Todo: *** Skia: - mask() *** Sk: High: - antialiased clipping? Medium: - implement clip path reset (to avoid restore/save) - implement complex radial patterns (2 centers and 2 radii) Low: - implement EXTEND_NONE ***/ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/000077500000000000000000000000001271037650300232425ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-debug.c000066400000000000000000000054071271037650300265350ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-win32-private.h" #include #include void _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) { RGNDATA *rd; unsigned int z; if (header) fprintf (stderr, "%s\n", header); if (rgn == NULL) { fprintf (stderr, " NULL\n"); } z = GetRegionData(rgn, 0, NULL); rd = (RGNDATA*) malloc(z); z = GetRegionData(rgn, z, rd); fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", rd->rdh.nCount, rd->rdh.rcBound.left, rd->rdh.rcBound.top, rd->rdh.rcBound.right - rd->rdh.rcBound.left, rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); for (z = 0; z < rd->rdh.nCount; z++) { RECT r = ((RECT*)rd->Buffer)[z]; fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", z, r.left, r.top, r.right - r.left, r.bottom - r.top); } free(rd); fflush (stderr); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-device.c000066400000000000000000000123431271037650300267030ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-atomic-private.h" #include "cairo-device-private.h" #include "cairo-win32-private.h" #include #include static cairo_device_t *__cairo_win32_device; static cairo_status_t _cairo_win32_device_flush (void *device) { GdiFlush (); return CAIRO_STATUS_SUCCESS; } static void _cairo_win32_device_finish (void *device) { } static void _cairo_win32_device_destroy (void *device) { free (device); } static const cairo_device_backend_t _cairo_win32_device_backend = { CAIRO_DEVICE_TYPE_WIN32, NULL, NULL, /* lock, unlock */ _cairo_win32_device_flush, _cairo_win32_device_finish, _cairo_win32_device_destroy, }; #if 0 D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT ); hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d); #endif static cairo_bool_t is_win98 (void) { OSVERSIONINFO os; os.dwOSVersionInfoSize = sizeof (os); GetVersionEx (&os); return (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId && os.dwMajorVersion == 4 && os.dwMinorVersion == 10); } static void * _cairo_win32_device_get_alpha_blend (cairo_win32_device_t *device) { void *func = NULL; if (is_win98 ()) return NULL; device->msimg32_dll = LoadLibraryW (L"msimg32"); if (device->msimg32_dll) func = GetProcAddress (device->msimg32_dll, "AlphaBlend"); return func; } cairo_device_t * _cairo_win32_device_get (void) { cairo_win32_device_t *device; if (__cairo_win32_device) return cairo_device_reference (__cairo_win32_device); device = malloc (sizeof (*device)); _cairo_device_init (&device->base, &_cairo_win32_device_backend); device->compositor = _cairo_win32_gdi_compositor_get (); device->msimg32_dll = NULL; device->alpha_blend = _cairo_win32_device_get_alpha_blend (device); if (_cairo_atomic_ptr_cmpxchg ((void **)&__cairo_win32_device, NULL, device)) return cairo_device_reference(&device->base); _cairo_win32_device_destroy (device); return cairo_device_reference (__cairo_win32_device); } unsigned _cairo_win32_flags_for_dc (HDC dc) { uint32_t flags = 0; int cap; cap = GetDeviceCaps(dc, RASTERCAPS); if (cap & RC_BITBLT) flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; if (cap & RC_STRETCHBLT) flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; if (cap & RC_STRETCHDIB) flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) { flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; /* These will always be possible, but the actual GetDeviceCaps * calls will return whether they're accelerated or not. * We may want to use our own (pixman) routines sometimes * if they're eventually faster, but for now have GDI do * everything. */ #if 0 flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; #endif } else { cap = GetDeviceCaps(dc, SHADEBLENDCAPS); if (cap != SB_NONE) flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; } return flags; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-display-surface.c000066400000000000000000000770411271037650300305450ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-damage-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-private.h" #include "cairo-win32-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-fallback-private.h" #include "cairo-surface-backend-private.h" #include #include #if defined(__MINGW32__) && !defined(ETO_PDY) # define ETO_PDY 0x2000 #endif #define PELS_72DPI ((LONG)(72. / 0.0254)) /** * SECTION:cairo-win32 * @Title: Win32 Surfaces * @Short_Description: Microsoft Windows surface support * @See_Also: #cairo_surface_t * * The Microsoft Windows surface is used to render cairo graphics to * Microsoft Windows windows, bitmaps, and printing device contexts. * * The surface returned by cairo_win32_printing_surface_create() is of surface * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface * type. * * The surface returned by the other win32 constructors is of surface type * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. **/ /** * CAIRO_HAS_WIN32_SURFACE: * * Defined if the Microsoft Windows surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.0 **/ static const cairo_surface_backend_t cairo_win32_display_surface_backend; static cairo_status_t _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, HDC original_dc, cairo_format_t format, int width, int height, unsigned char **bits_out, int *rowstride_out) { cairo_status_t status; BITMAPINFO *bitmap_info = NULL; struct { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; } bmi_stack; void *bits; int num_palette = 0; /* Quiet GCC */ int i; surface->win32.dc = NULL; surface->bitmap = NULL; surface->is_dib = FALSE; switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: num_palette = 0; break; case CAIRO_FORMAT_A8: num_palette = 256; break; case CAIRO_FORMAT_A1: num_palette = 2; break; } if (num_palette > 2) { bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); if (!bitmap_info) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { bitmap_info = (BITMAPINFO *)&bmi_stack; } bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ bitmap_info->bmiHeader.biSizeImage = 0; bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ bitmap_info->bmiHeader.biPlanes = 1; switch (format) { case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: ASSERT_NOT_REACHED; /* We can't create real RGB24 bitmaps because something seems to * break if we do, especially if we don't set up an image * fallback. It could be a bug with using a 24bpp pixman image * (and creating one with masks). So treat them like 32bpp. * Note: This causes problems when using BitBlt/AlphaBlend/etc! * see end of file. */ case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: bitmap_info->bmiHeader.biBitCount = 32; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ bitmap_info->bmiHeader.biClrImportant = 0; break; case CAIRO_FORMAT_A8: bitmap_info->bmiHeader.biBitCount = 8; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 256; bitmap_info->bmiHeader.biClrImportant = 0; for (i = 0; i < 256; i++) { bitmap_info->bmiColors[i].rgbBlue = i; bitmap_info->bmiColors[i].rgbGreen = i; bitmap_info->bmiColors[i].rgbRed = i; bitmap_info->bmiColors[i].rgbReserved = 0; } break; case CAIRO_FORMAT_A1: bitmap_info->bmiHeader.biBitCount = 1; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 2; bitmap_info->bmiHeader.biClrImportant = 0; for (i = 0; i < 2; i++) { bitmap_info->bmiColors[i].rgbBlue = i * 255; bitmap_info->bmiColors[i].rgbGreen = i * 255; bitmap_info->bmiColors[i].rgbRed = i * 255; bitmap_info->bmiColors[i].rgbReserved = 0; } break; } surface->win32.dc = CreateCompatibleDC (original_dc); if (!surface->win32.dc) goto FAIL; surface->bitmap = CreateDIBSection (surface->win32.dc, bitmap_info, DIB_RGB_COLORS, &bits, NULL, 0); if (!surface->bitmap) goto FAIL; surface->is_dib = TRUE; GdiFlush(); surface->saved_dc_bitmap = SelectObject (surface->win32.dc, surface->bitmap); if (!surface->saved_dc_bitmap) goto FAIL; if (bitmap_info && num_palette > 2) free (bitmap_info); if (bits_out) *bits_out = bits; if (rowstride_out) { /* Windows bitmaps are padded to 32-bit (dword) boundaries */ switch (format) { case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: *rowstride_out = 4 * width; break; case CAIRO_FORMAT_A8: *rowstride_out = (width + 3) & ~3; break; case CAIRO_FORMAT_A1: *rowstride_out = ((width + 31) & ~31) / 8; break; } } surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc); return CAIRO_STATUS_SUCCESS; FAIL: status = _cairo_win32_print_gdi_error (__FUNCTION__); if (bitmap_info && num_palette > 2) free (bitmap_info); if (surface->saved_dc_bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); surface->saved_dc_bitmap = NULL; } if (surface->bitmap) { DeleteObject (surface->bitmap); surface->bitmap = NULL; } if (surface->win32.dc) { DeleteDC (surface->win32.dc); surface->win32.dc = NULL; } return status; } static cairo_surface_t * _cairo_win32_display_surface_create_for_dc (HDC original_dc, cairo_format_t format, int width, int height) { cairo_status_t status; cairo_device_t *device; cairo_win32_display_surface_t *surface; unsigned char *bits; int rowstride; surface = malloc (sizeof (*surface)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->fallback = NULL; status = _create_dc_and_bitmap (surface, original_dc, format, width, height, &bits, &rowstride); if (status) goto FAIL; surface->image = cairo_image_surface_create_for_data (bits, format, width, height, rowstride); status = surface->image->status; if (status) goto FAIL; _cairo_image_surface_set_parent (to_image_surface(surface->image), &surface->win32.base); surface->win32.format = format; surface->win32.extents.x = 0; surface->win32.extents.y = 0; surface->win32.extents.width = width; surface->win32.extents.height = height; surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; device = _cairo_win32_device_get (); _cairo_surface_init (&surface->win32.base, &cairo_win32_display_surface_backend, device, _cairo_content_from_format (format)); cairo_device_destroy (device); return &surface->win32.base; FAIL: if (surface->bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); DeleteObject (surface->bitmap); DeleteDC (surface->win32.dc); } free (surface); return _cairo_surface_create_in_error (status); } static cairo_surface_t * _cairo_win32_display_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_win32_display_surface_t *src = abstract_src; cairo_format_t format = _cairo_format_from_content (content); cairo_surface_t *new_surf = NULL; /* We force a DIB always if: * - we need alpha; or * - the parent is a DIB; or * - the parent is for printing (because we don't care about the * bit depth at that point) * * We also might end up with a DIB even if a DDB is requested if * DDB creation failed due to out of memory. */ if (!(src->is_dib || content & CAIRO_CONTENT_ALPHA)) { /* try to create a ddb */ new_surf = cairo_win32_surface_create_with_ddb (src->win32.dc, CAIRO_FORMAT_RGB24, width, height); if (new_surf->status) new_surf = NULL; } if (new_surf == NULL) { new_surf = _cairo_win32_display_surface_create_for_dc (src->win32.dc, format, width, height); } return new_surf; } static cairo_surface_t * _cairo_win32_display_surface_create_similar_image (void *abstract_other, cairo_format_t format, int width, int height) { cairo_win32_display_surface_t *surface = abstract_other; cairo_image_surface_t *image; surface = (cairo_win32_display_surface_t *) _cairo_win32_display_surface_create_for_dc (surface->win32.dc, format, width, height); if (surface->win32.base.status) return &surface->win32.base; /* And clear in order to comply with our user API semantics */ image = (cairo_image_surface_t *) surface->image; if (! image->base.is_clear) { memset (image->data, 0, image->stride * height); image->base.is_clear = TRUE; } return &image->base; } static cairo_status_t _cairo_win32_display_surface_finish (void *abstract_surface) { cairo_win32_display_surface_t *surface = abstract_surface; if (surface->image && to_image_surface(surface->image)->parent) { assert (to_image_surface(surface->image)->parent == &surface->win32.base); /* Unhook ourselves first to avoid the double-unref from the image */ to_image_surface(surface->image)->parent = NULL; cairo_surface_finish (surface->image); cairo_surface_destroy (surface->image); } /* If we created the Bitmap and DC, destroy them */ if (surface->bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); DeleteObject (surface->bitmap); DeleteDC (surface->win32.dc); } _cairo_win32_display_surface_discard_fallback (surface); if (surface->initial_clip_rgn) DeleteObject (surface->initial_clip_rgn); return CAIRO_STATUS_SUCCESS; } static cairo_image_surface_t * _cairo_win32_display_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_win32_display_surface_t *surface = abstract_surface; cairo_status_t status; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); if (surface->image) goto done; if (surface->fallback == NULL) { surface->fallback = _cairo_win32_display_surface_create_for_dc (surface->win32.dc, surface->win32.format, surface->win32.extents.width, surface->win32.extents.height); if (unlikely (status = surface->fallback->status)) goto err; if (!BitBlt (to_win32_surface(surface->fallback)->dc, 0, 0, surface->win32.extents.width, surface->win32.extents.height, surface->win32.dc, 0, 0, SRCCOPY)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto err; } } surface = to_win32_display_surface (surface->fallback); done: GdiFlush(); return _cairo_surface_map_to_image (surface->image, extents); err: cairo_surface_destroy (surface->fallback); surface->fallback = NULL; return _cairo_image_surface_create_in_error (status); } static cairo_int_status_t _cairo_win32_display_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_win32_display_surface_t *surface = abstract_surface; /* Delay the download until the next flush, which means we also need * to make sure our sources rare flushed. */ TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (surface->fallback) { cairo_rectangle_int_t r; r.x = image->base.device_transform_inverse.x0; r.y = image->base.device_transform_inverse.y0; r.width = image->width; r.height = image->height; TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n", __FUNCTION__, r.x, r.y, r.width, r.height)); surface->fallback->damage = _cairo_damage_add_rectangle (surface->fallback->damage, &r); surface = to_win32_display_surface (surface->fallback); } return _cairo_surface_unmap_image (surface->image, image); } static cairo_status_t _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) { cairo_win32_display_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (flags) return CAIRO_STATUS_SUCCESS; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); if (surface->fallback == NULL) return CAIRO_STATUS_SUCCESS; if (surface->fallback->damage) { cairo_win32_display_surface_t *fallback; cairo_damage_t *damage; damage = _cairo_damage_reduce (surface->fallback->damage); surface->fallback->damage = NULL; fallback = to_win32_display_surface (surface->fallback); assert (fallback->image); TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, damage->region ? cairo_region_num_rectangles (damage->region) : 0)); if (damage->status) { if (!BitBlt (surface->win32.dc, 0, 0, surface->win32.extents.width, surface->win32.extents.height, fallback->win32.dc, 0, 0, SRCCOPY)) status = _cairo_win32_print_gdi_error (__FUNCTION__); } else if (damage->region) { int n = cairo_region_num_rectangles (damage->region), i; for (i = 0; i < n; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (damage->region, i, &rect); TRACE ((stderr, "%s: damage (%d,%d)x(%d,%d)\n", __FUNCTION__, rect.x, rect.y, rect.width, rect.height)); if (!BitBlt (surface->win32.dc, rect.x, rect.y, rect.width, rect.height, fallback->win32.dc, rect.x, rect.y, SRCCOPY)) { status = _cairo_win32_print_gdi_error (__FUNCTION__); break; } } } _cairo_damage_destroy (damage); } else { cairo_surface_destroy (surface->fallback); surface->fallback = NULL; } return status; } static cairo_status_t _cairo_win32_display_surface_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { _cairo_win32_display_surface_discard_fallback (abstract_surface); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface) { RECT rect; int clipBoxType; int gm; XFORM saved_xform; /* GetClipBox/GetClipRgn and friends interact badly with a world transform * set. GetClipBox returns values in logical (transformed) coordinates; * it's unclear what GetClipRgn returns, because the region is empty in the * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn * works in device units. * * So, avoid the whole mess and get rid of the world transform * while we store our initial data and when we restore initial coordinates. * * XXX we may need to modify x/y by the ViewportOrg or WindowOrg * here in GM_COMPATIBLE; unclear. */ gm = GetGraphicsMode (hdc); if (gm == GM_ADVANCED) { GetWorldTransform (hdc, &saved_xform); ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); } clipBoxType = GetClipBox (hdc, &rect); if (clipBoxType == ERROR) { _cairo_win32_print_gdi_error (__FUNCTION__); SetGraphicsMode (hdc, gm); /* XXX: Can we make a more reasonable guess at the error cause here? */ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); } surface->win32.extents.x = rect.left; surface->win32.extents.y = rect.top; surface->win32.extents.width = rect.right - rect.left; surface->win32.extents.height = rect.bottom - rect.top; surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; if (clipBoxType == COMPLEXREGION) { surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { DeleteObject(surface->initial_clip_rgn); surface->initial_clip_rgn = NULL; } } else if (clipBoxType == SIMPLEREGION) { surface->had_simple_clip = TRUE; } if (gm == GM_ADVANCED) SetWorldTransform (hdc, &saved_xform); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, cairo_clip_t *clip) { char stack[512]; cairo_rectangle_int_t extents; int num_rects; RGNDATA *data; size_t data_size; RECT *rects; int i; HRGN gdi_region; cairo_status_t status; cairo_region_t *region; /* The semantics we want is that any clip set by cairo combines * is intersected with the clip on device context that the * surface was created for. To implement this, we need to * save the original clip when first setting a clip on surface. */ assert (_cairo_clip_is_region (clip)); region = _cairo_clip_get_region (clip); if (region == NULL) return CAIRO_STATUS_SUCCESS; cairo_region_get_extents (region, &extents); num_rects = cairo_region_num_rectangles (region); /* XXX see notes in _cairo_win32_save_initial_clip -- * this code will interact badly with a HDC which had an initial * world transform -- we should probably manually transform the * region rects, because SelectClipRgn takes device units, not * logical units (unlike IntersectClipRect). */ data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); if (data_size > sizeof (stack)) { data = malloc (data_size); if (!data) return _cairo_error(CAIRO_STATUS_NO_MEMORY); } else data = (RGNDATA *)stack; data->rdh.dwSize = sizeof (RGNDATAHEADER); data->rdh.iType = RDH_RECTANGLES; data->rdh.nCount = num_rects; data->rdh.nRgnSize = num_rects * sizeof (RECT); data->rdh.rcBound.left = extents.x; data->rdh.rcBound.top = extents.y; data->rdh.rcBound.right = extents.x + extents.width; data->rdh.rcBound.bottom = extents.y + extents.height; rects = (RECT *)data->Buffer; for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].left = rect.x; rects[i].top = rect.y; rects[i].right = rect.x + rect.width; rects[i].bottom = rect.y + rect.height; } gdi_region = ExtCreateRegion (NULL, data_size, data); if ((char *)data != stack) free (data); if (!gdi_region) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* AND the new region into our DC */ status = CAIRO_STATUS_SUCCESS; if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) status = _cairo_win32_print_gdi_error (__FUNCTION__); DeleteObject (gdi_region); return status; } void _cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface) { XFORM saved_xform; int gm = GetGraphicsMode (surface->win32.dc); if (gm == GM_ADVANCED) { GetWorldTransform (surface->win32.dc, &saved_xform); ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY); } /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ SelectClipRgn (surface->win32.dc, surface->initial_clip_rgn); if (surface->had_simple_clip) { /* then if we had a simple clip, intersect */ IntersectClipRect (surface->win32.dc, surface->win32.extents.x, surface->win32.extents.y, surface->win32.extents.x + surface->win32.extents.width, surface->win32.extents.y + surface->win32.extents.height); } if (gm == GM_ADVANCED) SetWorldTransform (surface->win32.dc, &saved_xform); } void _cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface) { if (surface->fallback) { TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); cairo_surface_finish (surface->fallback); cairo_surface_destroy (surface->fallback); surface->fallback = NULL; } } static cairo_int_status_t _cairo_win32_display_surface_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (clip == NULL && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)) _cairo_win32_display_surface_discard_fallback (surface); return _cairo_compositor_paint (device->compositor, surface, op, source, clip); } static cairo_int_status_t _cairo_win32_display_surface_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (clip == NULL && op == CAIRO_OPERATOR_SOURCE) _cairo_win32_display_surface_discard_fallback (surface); return _cairo_compositor_mask (device->compositor, surface, op, source, mask, clip); } static cairo_int_status_t _cairo_win32_display_surface_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_stroke (device->compositor, surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_win32_display_surface_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_fill (device->compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_win32_display_surface_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_glyphs (device->compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); } static const cairo_surface_backend_t cairo_win32_display_surface_backend = { CAIRO_SURFACE_TYPE_WIN32, _cairo_win32_display_surface_finish, _cairo_default_context_create, _cairo_win32_display_surface_create_similar, _cairo_win32_display_surface_create_similar_image, _cairo_win32_display_surface_map_to_image, _cairo_win32_display_surface_unmap_image, _cairo_surface_default_source, _cairo_surface_default_acquire_source_image, _cairo_surface_default_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_win32_surface_get_extents, NULL, /* get_font_options */ _cairo_win32_display_surface_flush, _cairo_win32_display_surface_mark_dirty, _cairo_win32_display_surface_paint, _cairo_win32_display_surface_mask, _cairo_win32_display_surface_stroke, _cairo_win32_display_surface_fill, NULL, /* fill/stroke */ _cairo_win32_display_surface_glyphs, }; /* Notes: * * Win32 alpha-understanding functions * * BitBlt - will copy full 32 bits from a 32bpp DIB to result * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) * (but not safe going RGB24->ARGB32, if RGB24 is also represented * as a 32bpp DIB, since the alpha isn't discarded!) * * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, * it will still copy over the src alpha, because the SCA value (255) will be * multiplied by all the src components. */ /** * cairo_win32_surface_create: * @hdc: the DC to create a surface for * * Creates a cairo surface that targets the given DC. The DC will be * queried for its initial clip extents, and this will be used as the * size of the cairo surface. The resulting surface will always be of * format %CAIRO_FORMAT_RGB24; should you need another surface format, * you will need to create one through * cairo_win32_surface_create_with_dib(). * * Return value: the newly created surface * * Since: 1.0 **/ cairo_surface_t * cairo_win32_surface_create (HDC hdc) { cairo_win32_display_surface_t *surface; cairo_format_t format; cairo_status_t status; cairo_device_t *device; /* Assume that everything coming in as a HDC is RGB24 */ format = CAIRO_FORMAT_RGB24; surface = malloc (sizeof (*surface)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); status = _cairo_win32_save_initial_clip (hdc, surface); if (status) { free (surface); return _cairo_surface_create_in_error (status); } surface->image = NULL; surface->fallback = NULL; surface->win32.format = format; surface->win32.dc = hdc; surface->bitmap = NULL; surface->is_dib = FALSE; surface->saved_dc_bitmap = NULL; surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc); device = _cairo_win32_device_get (); _cairo_surface_init (&surface->win32.base, &cairo_win32_display_surface_backend, device, _cairo_content_from_format (format)); cairo_device_destroy (device); return &surface->win32.base; } /** * cairo_win32_surface_create_with_dib: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a device-independent-bitmap surface not associated with * any particular existing surface or device context. The created * bitmap will be uninitialized. * * Return value: the newly created surface * * Since: 1.2 **/ cairo_surface_t * cairo_win32_surface_create_with_dib (cairo_format_t format, int width, int height) { if (! CAIRO_FORMAT_VALID (format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); return _cairo_win32_display_surface_create_for_dc (NULL, format, width, height); } /** * cairo_win32_surface_create_with_ddb: * @hdc: a DC compatible with the surface to create * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a device-dependent-bitmap surface not associated with * any particular existing surface or device context. The created * bitmap will be uninitialized. * * Return value: the newly created surface * * Since: 1.4 **/ cairo_surface_t * cairo_win32_surface_create_with_ddb (HDC hdc, cairo_format_t format, int width, int height) { cairo_win32_display_surface_t *new_surf; HBITMAP ddb; HDC screen_dc, ddb_dc; HBITMAP saved_dc_bitmap; if (format != CAIRO_FORMAT_RGB24) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); /* XXX handle these eventually format != CAIRO_FORMAT_A8 || format != CAIRO_FORMAT_A1) */ if (!hdc) { screen_dc = GetDC (NULL); hdc = screen_dc; } else { screen_dc = NULL; } ddb_dc = CreateCompatibleDC (hdc); if (ddb_dc == NULL) { new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto FINISH; } ddb = CreateCompatibleBitmap (hdc, width, height); if (ddb == NULL) { DeleteDC (ddb_dc); /* Note that if an app actually does hit this out of memory * condition, it's going to have lots of other issues, as * video memory is probably exhausted. However, it can often * continue using DIBs instead of DDBs. */ new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto FINISH; } saved_dc_bitmap = SelectObject (ddb_dc, ddb); new_surf = (cairo_win32_display_surface_t*) cairo_win32_surface_create (ddb_dc); new_surf->bitmap = ddb; new_surf->saved_dc_bitmap = saved_dc_bitmap; new_surf->is_dib = FALSE; FINISH: if (screen_dc) ReleaseDC (NULL, screen_dc); return &new_surf->win32.base; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-font.c000066400000000000000000002137531271037650300264220ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as GetGlyphIndices */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-win32-private.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-private.h" #include "cairo-scaled-font-subsets-private.h" #include #ifndef SPI_GETFONTSMOOTHINGTYPE #define SPI_GETFONTSMOOTHINGTYPE 0x200a #endif #ifndef FE_FONTSMOOTHINGCLEARTYPE #define FE_FONTSMOOTHINGCLEARTYPE 2 #endif #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 #endif #ifndef TT_PRIM_CSPLINE #define TT_PRIM_CSPLINE 3 #endif #define CMAP_TAG 0x70616d63 /** * SECTION:cairo-win32-fonts * @Title: Win32 Fonts * @Short_Description: Font support for Microsoft Windows * @See_Also: #cairo_font_face_t * * The Microsoft Windows font backend is primarily used to render text on * Microsoft Windows systems. **/ /** * CAIRO_HAS_WIN32_FONT: * * Defined if the Microsoft Windows font backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.8 **/ const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend; typedef struct { cairo_scaled_font_t base; LOGFONTW logfont; BYTE quality; /* We do drawing and metrics computation in a "logical space" which * is similar to font space, except that it is scaled by a factor * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision. */ double logical_scale; /* The size we should actually request the font at from Windows; differs * from the logical_scale because it is quantized for orthogonal * transformations */ double logical_size; /* Transformations from device <=> logical space */ cairo_matrix_t logical_to_device; cairo_matrix_t device_to_logical; /* We special case combinations of 90-degree-rotations, scales and * flips ... that is transformations that take the axes to the * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y * encode the 8 possibilities for orientation (4 rotation angles with * and without a flip), and scale_x, scale_y the scale components. */ cairo_bool_t preserve_axes; cairo_bool_t swap_axes; cairo_bool_t swap_x; cairo_bool_t swap_y; double x_scale; double y_scale; /* The size of the design unit of the font */ int em_square; HFONT scaled_hfont; HFONT unscaled_hfont; cairo_bool_t is_bitmap; cairo_bool_t is_type1; cairo_bool_t delete_scaled_hfont; cairo_bool_t has_type1_notdef_index; unsigned long type1_notdef_index; } cairo_win32_scaled_font_t; static cairo_status_t _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font); static cairo_status_t _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); static cairo_status_t _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); static cairo_status_t _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); static void _cairo_win32_font_face_destroy (void *abstract_face); #define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) static HDC _get_global_font_dc (void) { static HDC hdc; if (!hdc) { hdc = CreateCompatibleDC (NULL); if (!hdc) { _cairo_win32_print_gdi_error ("_get_global_font_dc"); return NULL; } if (!SetGraphicsMode (hdc, GM_ADVANCED)) { _cairo_win32_print_gdi_error ("_get_global_font_dc"); DeleteDC (hdc); return NULL; } } return hdc; } static cairo_status_t _compute_transform (cairo_win32_scaled_font_t *scaled_font, cairo_matrix_t *sc) { cairo_status_t status; if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) && !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) { scaled_font->preserve_axes = TRUE; scaled_font->x_scale = sc->xx; scaled_font->swap_x = (sc->xx < 0); scaled_font->y_scale = sc->yy; scaled_font->swap_y = (sc->yy < 0); scaled_font->swap_axes = FALSE; } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) && !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) { scaled_font->preserve_axes = TRUE; scaled_font->x_scale = sc->yx; scaled_font->swap_x = (sc->yx < 0); scaled_font->y_scale = sc->xy; scaled_font->swap_y = (sc->xy < 0); scaled_font->swap_axes = TRUE; } else { scaled_font->preserve_axes = FALSE; scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; } if (scaled_font->preserve_axes) { if (scaled_font->swap_x) scaled_font->x_scale = - scaled_font->x_scale; if (scaled_font->swap_y) scaled_font->y_scale = - scaled_font->y_scale; scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE * _cairo_lround (scaled_font->y_scale); } /* The font matrix has x and y "scale" components which we extract and * use as character scale values. */ cairo_matrix_init (&scaled_font->logical_to_device, sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); if (!scaled_font->preserve_axes) { status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device, &scaled_font->x_scale, &scaled_font->y_scale, TRUE); /* XXX: Handle vertical text */ if (status) return status; scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale); scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; } cairo_matrix_scale (&scaled_font->logical_to_device, 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); scaled_font->device_to_logical = scaled_font->logical_to_device; status = cairo_matrix_invert (&scaled_font->device_to_logical); if (status) cairo_matrix_init_identity (&scaled_font->device_to_logical); return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _have_cleartype_quality (void) { OSVERSIONINFO version_info; version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (!GetVersionEx (&version_info)) { _cairo_win32_print_gdi_error ("_have_cleartype_quality"); return FALSE; } return (version_info.dwMajorVersion > 5 || (version_info.dwMajorVersion == 5 && version_info.dwMinorVersion >= 1)); /* XP or newer */ } static BYTE _get_system_quality (void) { BOOL font_smoothing; UINT smoothing_type; if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { _cairo_win32_print_gdi_error ("_get_system_quality"); return DEFAULT_QUALITY; } if (font_smoothing) { if (_have_cleartype_quality ()) { if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type, 0)) { _cairo_win32_print_gdi_error ("_get_system_quality"); return DEFAULT_QUALITY; } if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) return CLEARTYPE_QUALITY; } return ANTIALIASED_QUALITY; } else { return DEFAULT_QUALITY; } } /* If face_hfont is non-%NULL then font_matrix must be a simple scale by some * factor S, ctm must be the identity, logfont->lfHeight must be -S, * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must * all be 0, and face_hfont is the result of calling CreateFontIndirectW on * logfont. */ static cairo_status_t _win32_scaled_font_create (LOGFONTW *logfont, HFONT face_hfont, cairo_font_face_t *font_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font_out) { HDC hdc; cairo_win32_scaled_font_t *f; cairo_matrix_t scale; cairo_status_t status; hdc = _get_global_font_dc (); if (hdc == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); f = malloc (sizeof(cairo_win32_scaled_font_t)); if (f == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); f->logfont = *logfont; /* We don't have any control over the hinting style or subpixel * order in the Win32 font API, so we ignore those parts of * cairo_font_options_t. We use the 'antialias' field to set * the 'quality'. * * XXX: The other option we could pay attention to, but don't * here is the hint_metrics options. */ if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) f->quality = _get_system_quality (); else { switch (options->antialias) { case CAIRO_ANTIALIAS_NONE: f->quality = NONANTIALIASED_QUALITY; break; case CAIRO_ANTIALIAS_GRAY: case CAIRO_ANTIALIAS_FAST: case CAIRO_ANTIALIAS_GOOD: f->quality = ANTIALIASED_QUALITY; break; case CAIRO_ANTIALIAS_SUBPIXEL: case CAIRO_ANTIALIAS_BEST: if (_have_cleartype_quality ()) f->quality = CLEARTYPE_QUALITY; else f->quality = ANTIALIASED_QUALITY; break; case CAIRO_ANTIALIAS_DEFAULT: ASSERT_NOT_REACHED; } } f->em_square = 0; f->scaled_hfont = NULL; f->unscaled_hfont = NULL; f->has_type1_notdef_index = FALSE; if (f->quality == logfont->lfQuality || (logfont->lfQuality == DEFAULT_QUALITY && options->antialias == CAIRO_ANTIALIAS_DEFAULT)) { /* If face_hfont is non-NULL, then we can use it to avoid creating our * own --- because the constraints on face_hfont mentioned above * guarantee it was created in exactly the same way that * _win32_scaled_font_get_scaled_hfont would create it. */ f->scaled_hfont = face_hfont; } /* don't delete the hfont if we're using the one passed in to us */ f->delete_scaled_hfont = !f->scaled_hfont; cairo_matrix_multiply (&scale, font_matrix, ctm); status = _compute_transform (f, &scale); if (status) goto FAIL; status = _cairo_scaled_font_init (&f->base, font_face, font_matrix, ctm, options, &_cairo_win32_scaled_font_backend); if (status) goto FAIL; status = _cairo_win32_scaled_font_set_metrics (f); if (status) { _cairo_scaled_font_fini (&f->base); goto FAIL; } *font_out = &f->base; return CAIRO_STATUS_SUCCESS; FAIL: free (f); return status; } static cairo_status_t _win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, HDC hdc) { XFORM xform; _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); if (!SetWorldTransform (hdc, &xform)) return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _win32_scaled_font_set_identity_transform (HDC hdc) { if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, HFONT *hfont_out) { if (!scaled_font->scaled_hfont) { LOGFONTW logfont = scaled_font->logfont; logfont.lfHeight = -scaled_font->logical_size; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; logfont.lfQuality = scaled_font->quality; scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); if (!scaled_font->scaled_hfont) return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); } *hfont_out = scaled_font->scaled_hfont; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, HDC hdc, HFONT *hfont_out) { if (scaled_font->unscaled_hfont == NULL) { OUTLINETEXTMETRIC *otm; unsigned int otm_size; HFONT scaled_hfont; LOGFONTW logfont; cairo_status_t status; status = _win32_scaled_font_get_scaled_hfont (scaled_font, &scaled_hfont); if (status) return status; if (! SelectObject (hdc, scaled_hfont)) return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); otm_size = GetOutlineTextMetrics (hdc, 0, NULL); if (! otm_size) return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); otm = malloc (otm_size); if (otm == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); free (otm); return status; } scaled_font->em_square = otm->otmEMSquare; free (otm); logfont = scaled_font->logfont; logfont.lfHeight = -scaled_font->em_square; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; logfont.lfQuality = scaled_font->quality; scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); if (! scaled_font->unscaled_hfont) return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); } *hfont_out = scaled_font->unscaled_hfont; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, HDC hdc) { cairo_status_t status; HFONT hfont; HFONT old_hfont = NULL; status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont); if (status) return status; old_hfont = SelectObject (hdc, hfont); if (!old_hfont) return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); status = _win32_scaled_font_set_identity_transform (hdc); if (status) { SelectObject (hdc, old_hfont); return status; } SetMapMode (hdc, MM_TEXT); return CAIRO_STATUS_SUCCESS; } cairo_bool_t _cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) { cairo_win32_scaled_font_t *win32_scaled_font; win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; return win32_scaled_font->is_type1; } cairo_bool_t _cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font) { cairo_win32_scaled_font_t *win32_scaled_font; win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; return win32_scaled_font->is_bitmap; } static void _cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) { } /* implement the font backend interface */ static cairo_status_t _cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, cairo_font_face_t **font_face) { LOGFONTW logfont; uint16_t *face_name; int face_name_len; cairo_status_t status; status = _cairo_utf8_to_utf16 (toy_face->family, -1, &face_name, &face_name_len); if (status) return status; if (face_name_len > LF_FACESIZE - 1) face_name_len = LF_FACESIZE - 1; memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); logfont.lfFaceName[face_name_len] = 0; free (face_name); logfont.lfHeight = 0; /* filled in later */ logfont.lfWidth = 0; /* filled in later */ logfont.lfEscapement = 0; /* filled in later */ logfont.lfOrientation = 0; /* filled in later */ switch (toy_face->weight) { case CAIRO_FONT_WEIGHT_NORMAL: default: logfont.lfWeight = FW_NORMAL; break; case CAIRO_FONT_WEIGHT_BOLD: logfont.lfWeight = FW_BOLD; break; } switch (toy_face->slant) { case CAIRO_FONT_SLANT_NORMAL: default: logfont.lfItalic = FALSE; break; case CAIRO_FONT_SLANT_ITALIC: case CAIRO_FONT_SLANT_OBLIQUE: logfont.lfItalic = TRUE; break; } logfont.lfUnderline = FALSE; logfont.lfStrikeOut = FALSE; /* The docs for LOGFONT discourage using this, since the * interpretation is locale-specific, but it's not clear what * would be a better alternative. */ logfont.lfCharSet = DEFAULT_CHARSET; logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); return CAIRO_STATUS_SUCCESS; } static void _cairo_win32_scaled_font_fini (void *abstract_font) { cairo_win32_scaled_font_t *scaled_font = abstract_font; if (scaled_font == NULL) return; if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont) DeleteObject (scaled_font->scaled_hfont); if (scaled_font->unscaled_hfont) DeleteObject (scaled_font->unscaled_hfont); } static cairo_int_status_t _cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font, double x, double y, const char *utf8, cairo_glyph_t **glyphs, int *num_glyphs) { uint16_t *utf16; int n16; int i; WORD *glyph_indices = NULL; cairo_status_t status; double x_pos, y_pos; HDC hdc = NULL; cairo_matrix_t mat; status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); if (status) return status; glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD)); if (!glyph_indices) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL1; } hdc = _get_global_font_dc (); assert (hdc != NULL); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) goto FAIL2; if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) { status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW"); goto FAIL3; } *num_glyphs = n16; *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t)); if (!*glyphs) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL3; } x_pos = x; y_pos = y; mat = scaled_font->base.ctm; status = cairo_matrix_invert (&mat); assert (status == CAIRO_STATUS_SUCCESS); _cairo_scaled_font_freeze_cache (&scaled_font->base); for (i = 0; i < n16; i++) { cairo_scaled_glyph_t *scaled_glyph; (*glyphs)[i].index = glyph_indices[i]; (*glyphs)[i].x = x_pos; (*glyphs)[i].y = y_pos; status = _cairo_scaled_glyph_lookup (&scaled_font->base, glyph_indices[i], CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (status) { free (*glyphs); *glyphs = NULL; break; } x = scaled_glyph->x_advance; y = scaled_glyph->y_advance; cairo_matrix_transform_distance (&mat, &x, &y); x_pos += x; y_pos += y; } _cairo_scaled_font_thaw_cache (&scaled_font->base); FAIL3: cairo_win32_scaled_font_done_font (&scaled_font->base); FAIL2: free (glyph_indices); FAIL1: free (utf16); return status; } static cairo_int_status_t _cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, double x, double y, const char *utf8, cairo_glyph_t **glyphs, int *num_glyphs) { cairo_win32_scaled_font_t *scaled_font = abstract_font; uint16_t *utf16; int n16; GCP_RESULTSW gcp_results; unsigned int buffer_size, i; WCHAR *glyph_indices = NULL; int *dx = NULL; cairo_status_t status; double x_pos, y_pos; double x_incr, y_incr; HDC hdc = NULL; /* GetCharacterPlacement() returns utf16 instead of glyph indices * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */ if (scaled_font->is_type1) return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font, x, y, utf8, glyphs, num_glyphs); /* Compute a vector in user space along the baseline of length one logical space unit */ x_incr = 1; y_incr = 0; cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr); x_incr /= scaled_font->logical_scale; y_incr /= scaled_font->logical_scale; status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); if (status) return status; gcp_results.lStructSize = sizeof (GCP_RESULTS); gcp_results.lpOutString = NULL; gcp_results.lpOrder = NULL; gcp_results.lpCaretPos = NULL; gcp_results.lpClass = NULL; buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ if (buffer_size > INT_MAX) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL1; } hdc = _get_global_font_dc (); assert (hdc != NULL); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) goto FAIL1; while (TRUE) { free (glyph_indices); glyph_indices = NULL; free (dx); dx = NULL; glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR)); dx = _cairo_malloc_ab (buffer_size, sizeof (int)); if (!glyph_indices || !dx) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL2; } gcp_results.nGlyphs = buffer_size; gcp_results.lpDx = dx; gcp_results.lpGlyphs = glyph_indices; if (!GetCharacterPlacementW (hdc, utf16, n16, 0, &gcp_results, GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); goto FAIL2; } if (gcp_results.lpDx && gcp_results.lpGlyphs) break; /* Too small a buffer, try again */ buffer_size += buffer_size / 2; if (buffer_size > INT_MAX) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL2; } } *num_glyphs = gcp_results.nGlyphs; *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t)); if (!*glyphs) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL2; } x_pos = x; y_pos = y; for (i = 0; i < gcp_results.nGlyphs; i++) { (*glyphs)[i].index = glyph_indices[i]; (*glyphs)[i].x = x_pos ; (*glyphs)[i].y = y_pos; x_pos += x_incr * dx[i]; y_pos += y_incr * dx[i]; } FAIL2: free (glyph_indices); free (dx); cairo_win32_scaled_font_done_font (&scaled_font->base); FAIL1: free (utf16); return status; } static unsigned long _cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_win32_scaled_font_t *scaled_font = abstract_font; wchar_t unicode[2]; WORD glyph_index; HDC hdc = NULL; cairo_status_t status; hdc = _get_global_font_dc (); assert (hdc != NULL); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return 0; unicode[0] = ucs4; unicode[1] = 0; if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); glyph_index = 0; } cairo_win32_scaled_font_done_font (&scaled_font->base); return glyph_index; } static cairo_status_t _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) { cairo_status_t status; cairo_font_extents_t extents; TEXTMETRIC metrics; HDC hdc; hdc = _get_global_font_dc (); assert (hdc != NULL); if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) { /* For 90-degree rotations (including 0), we get the metrics * from the GDI in logical space, then convert back to font space */ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; GetTextMetrics (hdc, &metrics); cairo_win32_scaled_font_done_font (&scaled_font->base); extents.ascent = metrics.tmAscent / scaled_font->logical_scale; extents.descent = metrics.tmDescent / scaled_font->logical_scale; extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; extents.max_y_advance = 0; } else { /* For all other transformations, we use the design metrics * of the font. The GDI results from GetTextMetrics() on a * transformed font are inexplicably large and we want to * avoid them. */ status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); if (status) return status; GetTextMetrics (hdc, &metrics); _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); extents.ascent = (double)metrics.tmAscent / scaled_font->em_square; extents.descent = (double)metrics.tmDescent / scaled_font->em_square; extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; extents.max_y_advance = 0; } scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR); /* Need to determine if this is a Type 1 font for the special * handling in _text_to_glyphs. Unlike TrueType or OpenType, * Type1 fonts do not have a "cmap" table (or any other table). * However GetFontData() will retrieve a Type1 font when * requesting that GetFontData() retrieve data from the start of * the file. This is to distinguish Type1 from stroke fonts such * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant * but improves performance for the most common fonts. */ scaled_font->is_type1 = FALSE; if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) && (metrics.tmPitchAndFamily & TMPF_VECTOR)) { if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) && (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR)) { scaled_font->is_type1 = TRUE; } } return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents); } static cairo_status_t _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; GLYPHMETRICS metrics; cairo_status_t status; cairo_text_extents_t extents; HDC hdc; hdc = _get_global_font_dc (); assert (hdc != NULL); if (scaled_font->is_bitmap) { /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */ cairo_font_extents_t font_extents; INT width = 0; UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph); cairo_scaled_font_extents (&scaled_font->base, &font_extents); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); width = 0; } cairo_win32_scaled_font_done_font (&scaled_font->base); if (status) return status; extents.x_bearing = 0; extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale); extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale); extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale; extents.x_advance = extents.width; extents.y_advance = 0; } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { /* If we aren't rotating / skewing the axes, then we get the metrics * from the GDI in device space and convert to font space. */ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), GGO_METRICS | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix) == GDI_ERROR) { memset (&metrics, 0, sizeof (GLYPHMETRICS)); } else { if (metrics.gmBlackBoxX > 0 && scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { /* The bounding box reported by Windows supposedly contains the glyph's "black" area; * however, antialiasing (especially with ClearType) means that the actual image that * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side. * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs, * for example, or other code that uses glyph extents to determine the area to update, * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI, * and two pixels to the right (as tests show some glyphs bleed into this column). */ metrics.gmptGlyphOrigin.x -= 1; metrics.gmBlackBoxX += 3; } } cairo_win32_scaled_font_done_font (&scaled_font->base); if (scaled_font->swap_axes) { extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; extents.width = metrics.gmBlackBoxY / scaled_font->y_scale; extents.height = metrics.gmBlackBoxX / scaled_font->x_scale; extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale; extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale; } else { extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; extents.width = metrics.gmBlackBoxX / scaled_font->x_scale; extents.height = metrics.gmBlackBoxY / scaled_font->y_scale; extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale; extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale; } if (scaled_font->swap_x) { extents.x_bearing = (- extents.x_bearing - extents.width); extents.x_advance = - extents.x_advance; } if (scaled_font->swap_y) { extents.y_bearing = (- extents.y_bearing - extents.height); extents.y_advance = - extents.y_advance; } } else { /* For all other transformations, we use the design metrics * of the font. */ status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); if (status) return status; if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), GGO_METRICS | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix) == GDI_ERROR) { memset (&metrics, 0, sizeof (GLYPHMETRICS)); } _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square; extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square; extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; } _cairo_scaled_glyph_set_metrics (scaled_glyph, &scaled_font->base, &extents); return CAIRO_STATUS_SUCCESS; } /* Not currently used code, but may be useful in the future if we add * back the capability to the scaled font backend interface to get the * actual device space bbox rather than computing it from the * font-space metrics. */ #if 0 static cairo_status_t _cairo_win32_scaled_font_glyph_bbox (void *abstract_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_box_t *bbox) { static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; cairo_win32_scaled_font_t *scaled_font = abstract_font; int x1 = 0, x2 = 0, y1 = 0, y2 = 0; if (num_glyphs > 0) { HDC hdc; GLYPHMETRICS metrics; cairo_status_t status; int i; hdc = _get_global_font_dc (); assert (hdc != NULL); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; for (i = 0; i < num_glyphs; i++) { int x = _cairo_lround (glyphs[i].x); int y = _cairo_lround (glyphs[i].y); GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix); if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) x1 = x + metrics.gmptGlyphOrigin.x; if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) y1 = y - metrics.gmptGlyphOrigin.y; if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX) x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX; if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY) y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY; } cairo_win32_scaled_font_done_font (&scaled_font->base); } bbox->p1.x = _cairo_fixed_from_int (x1); bbox->p1.y = _cairo_fixed_from_int (y1); bbox->p2.x = _cairo_fixed_from_int (x2); bbox->p2.y = _cairo_fixed_from_int (y2); return CAIRO_STATUS_SUCCESS; } #endif typedef struct { cairo_win32_scaled_font_t *scaled_font; HDC hdc; cairo_array_t glyphs; cairo_array_t dx; int start_x; int last_x; int last_y; } cairo_glyph_state_t; static void _start_glyphs (cairo_glyph_state_t *state, cairo_win32_scaled_font_t *scaled_font, HDC hdc) { state->hdc = hdc; state->scaled_font = scaled_font; _cairo_array_init (&state->glyphs, sizeof (WCHAR)); _cairo_array_init (&state->dx, sizeof (int)); } static cairo_status_t _flush_glyphs (cairo_glyph_state_t *state) { cairo_status_t status; int dx = 0; WCHAR * elements; int * dx_elements; status = _cairo_array_append (&state->dx, &dx); if (status) return status; elements = _cairo_array_index (&state->glyphs, 0); dx_elements = _cairo_array_index (&state->dx, 0); if (!ExtTextOutW (state->hdc, state->start_x, state->last_y, ETO_GLYPH_INDEX, NULL, elements, state->glyphs.num_elements, dx_elements)) { return _cairo_win32_print_gdi_error ("_flush_glyphs"); } _cairo_array_truncate (&state->glyphs, 0); _cairo_array_truncate (&state->dx, 0); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _add_glyph (cairo_glyph_state_t *state, unsigned long index, double device_x, double device_y) { cairo_status_t status; double user_x = device_x; double user_y = device_y; WCHAR glyph_index = index; int logical_x, logical_y; cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); logical_x = _cairo_lround (user_x); logical_y = _cairo_lround (user_y); if (state->glyphs.num_elements > 0) { int dx; if (logical_y != state->last_y) { status = _flush_glyphs (state); if (status) return status; state->start_x = logical_x; } else { dx = logical_x - state->last_x; status = _cairo_array_append (&state->dx, &dx); if (status) return status; } } else { state->start_x = logical_x; } state->last_x = logical_x; state->last_y = logical_y; status = _cairo_array_append (&state->glyphs, &glyph_index); if (status) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _finish_glyphs (cairo_glyph_state_t *state) { cairo_status_t status; status = _flush_glyphs (state); _cairo_array_fini (&state->glyphs); _cairo_array_fini (&state->dx); return status; } static cairo_status_t _draw_glyphs_on_surface (cairo_win32_surface_t *surface, cairo_win32_scaled_font_t *scaled_font, COLORREF color, int x_offset, int y_offset, const cairo_glyph_t *glyphs, int num_glyphs) { cairo_glyph_state_t state; cairo_status_t status, status2; int i; if (!SaveDC (surface->dc)) return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); if (status) goto FAIL1; SetTextColor (surface->dc, color); SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); SetBkMode (surface->dc, TRANSPARENT); _start_glyphs (&state, scaled_font, surface->dc); for (i = 0; i < num_glyphs; i++) { status = _add_glyph (&state, glyphs[i].index, glyphs[i].x - x_offset, glyphs[i].y - y_offset); if (status) goto FAIL2; } FAIL2: status2 = _finish_glyphs (&state); if (status == CAIRO_STATUS_SUCCESS) status = status2; cairo_win32_scaled_font_done_font (&scaled_font->base); FAIL1: RestoreDC (surface->dc, -1); return status; } static cairo_int_status_t _cairo_win32_scaled_font_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_glyph_info_t info) { cairo_win32_scaled_font_t *scaled_font = abstract_font; cairo_status_t status; if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph); if (status) return status; } if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph); if (status) return status; } if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph); if (status) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_win32_scaled_font_load_truetype_table (void *abstract_font, unsigned long tag, long offset, unsigned char *buffer, unsigned long *length) { cairo_win32_scaled_font_t *scaled_font = abstract_font; HDC hdc; cairo_status_t status; DWORD ret; hdc = _get_global_font_dc (); assert (hdc != NULL); tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; ret = GetFontData (hdc, tag, offset, buffer, *length); if (ret == GDI_ERROR || (buffer && ret != *length)) status = CAIRO_INT_STATUS_UNSUPPORTED; else *length = ret; cairo_win32_scaled_font_done_font (&scaled_font->base); return status; } static cairo_int_status_t _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, unsigned long index, uint32_t *ucs4) { cairo_win32_scaled_font_t *scaled_font = abstract_font; GLYPHSET *glyph_set; uint16_t *utf16 = NULL; WORD *glyph_indices = NULL; HDC hdc = NULL; int res; unsigned int i, j, num_glyphs; cairo_status_t status; hdc = _get_global_font_dc (); assert (hdc != NULL); status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; res = GetFontUnicodeRanges(hdc, NULL); if (res == 0) { status = _cairo_win32_print_gdi_error ( "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); goto exit1; } glyph_set = malloc (res); if (glyph_set == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto exit1; } res = GetFontUnicodeRanges(hdc, glyph_set); if (res == 0) { status = _cairo_win32_print_gdi_error ( "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); goto exit1; } *ucs4 = (uint32_t) -1; for (i = 0; i < glyph_set->cRanges; i++) { num_glyphs = glyph_set->ranges[i].cGlyphs; utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t)); if (utf16 == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto exit1; } glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD)); if (glyph_indices == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto exit2; } for (j = 0; j < num_glyphs; j++) utf16[j] = glyph_set->ranges[i].wcLow + j; utf16[j] = 0; if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { status = _cairo_win32_print_gdi_error ( "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); goto exit2; } for (j = 0; j < num_glyphs; j++) { if (glyph_indices[j] == index) { *ucs4 = utf16[j]; goto exit2; } } free (glyph_indices); glyph_indices = NULL; free (utf16); utf16 = NULL; } exit2: free (glyph_indices); free (utf16); free (glyph_set); exit1: cairo_win32_scaled_font_done_font (&scaled_font->base); return status; } static cairo_bool_t _cairo_win32_scaled_font_is_synthetic (void *abstract_font) { cairo_win32_scaled_font_t *scaled_font = abstract_font; cairo_status_t status; int weight; cairo_bool_t bold; cairo_bool_t italic; status = _cairo_truetype_get_style (&scaled_font->base, &weight, &bold, &italic); /* If this doesn't work assume it is not synthetic to avoid * unnecessary subsetting fallbacks. */ if (status != CAIRO_STATUS_SUCCESS) return FALSE; if (scaled_font->logfont.lfWeight != weight || scaled_font->logfont.lfItalic != italic) return TRUE; return FALSE; } static cairo_int_status_t _cairo_win32_scaled_font_index_to_glyph_name (void *abstract_font, char **glyph_names, int num_glyph_names, unsigned long glyph_index, unsigned long *glyph_array_index) { cairo_win32_scaled_font_t *scaled_font = abstract_font; int i; /* Windows puts .notdef at index 0 then numbers the remaining * glyphs starting from 1 in the order they appear in the font. */ /* Find the position of .notdef in the list of glyph names. We * only need to do this once per scaled font. */ if (! scaled_font->has_type1_notdef_index) { for (i = 0; i < num_glyph_names; i++) { if (strcmp (glyph_names[i], ".notdef") == 0) { scaled_font->type1_notdef_index = i; scaled_font->has_type1_notdef_index = TRUE; break; } } if (! scaled_font->has_type1_notdef_index) return CAIRO_INT_STATUS_UNSUPPORTED; } /* Once we know the position of .notdef the position of any glyph * in the font can easily be obtained. */ if (glyph_index == 0) *glyph_array_index = scaled_font->type1_notdef_index; else if (glyph_index <= scaled_font->type1_notdef_index) *glyph_array_index = glyph_index - 1; else if (glyph_index < (unsigned long)num_glyph_names) *glyph_array_index = glyph_index; else return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_win32_scaled_font_load_type1_data (void *abstract_font, long offset, unsigned char *buffer, unsigned long *length) { cairo_win32_scaled_font_t *scaled_font = abstract_font; if (! scaled_font->is_type1) return CAIRO_INT_STATUS_UNSUPPORTED; /* Using the tag 0 retrieves the entire font file. This works with * Type 1 fonts as well as TTF/OTF fonts. */ return _cairo_win32_scaled_font_load_truetype_table (scaled_font, 0, offset, buffer, length); } static cairo_surface_t * _compute_mask (cairo_surface_t *surface, int quality) { cairo_image_surface_t *glyph; cairo_image_surface_t *mask; int i, j; glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL); if (unlikely (glyph->base.status)) return &glyph->base; if (quality == CLEARTYPE_QUALITY) { /* Duplicate the green channel of a 4-channel mask into the * alpha channel, then invert the whole mask. */ mask = (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, glyph->width, glyph->height); if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { for (i = 0; i < glyph->height; i++) { uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); uint32_t *q = (uint32_t *) (mask->data + i * mask->stride); for (j = 0; j < glyph->width; j++) { *q++ = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); p++; } } } } else { /* Compute an alpha-mask from a using the green channel of a * (presumed monochrome) RGB24 image. */ mask = (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_A8, glyph->width, glyph->height); if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { for (i = 0; i < glyph->height; i++) { uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); uint8_t *q = (uint8_t *) (mask->data + i * mask->stride); for (j = 0; j < glyph->width; j++) *q++ = 255 - ((*p++ & 0x0000ff00) >> 8); } } } cairo_surface_unmap_image (surface, &glyph->base); return &mask->base; } static cairo_status_t _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_status_t status; cairo_glyph_t glyph; cairo_surface_t *surface; cairo_surface_t *image; int width, height; int x1, y1, x2, y2; x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); width = x2 - x1; height = y2 - y1; surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE, &_cairo_pattern_white.base, NULL); if (status) goto FAIL; glyph.index = _cairo_scaled_glyph_index (scaled_glyph); glyph.x = -x1; glyph.y = -y1; status = _draw_glyphs_on_surface (to_win32_surface (surface), scaled_font, RGB(0,0,0), 0, 0, &glyph, 1); if (status) goto FAIL; image = _compute_mask (surface, scaled_font->quality); status = image->status; if (status) goto FAIL; cairo_surface_set_device_offset (image, -x1, -y1); _cairo_scaled_glyph_set_surface (scaled_glyph, &scaled_font->base, (cairo_image_surface_t *) image); FAIL: cairo_surface_destroy (surface); return status; } static void _cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix, FIXED Fx, FIXED Fy, cairo_fixed_t *fx, cairo_fixed_t *fy) { double x = Fx.value + Fx.fract / 65536.0; double y = Fy.value + Fy.fract / 65536.0; cairo_matrix_transform_point (matrix, &x, &y); *fx = _cairo_fixed_from_double (x); *fy = _cairo_fixed_from_double (y); } static cairo_status_t _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; cairo_status_t status; GLYPHMETRICS metrics; HDC hdc; DWORD bytesGlyph; unsigned char *buffer, *ptr; cairo_path_fixed_t *path; cairo_matrix_t transform; cairo_fixed_t x, y; if (scaled_font->is_bitmap) return CAIRO_INT_STATUS_UNSUPPORTED; hdc = _get_global_font_dc (); assert (hdc != NULL); path = _cairo_path_fixed_create (); if (!path) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) { status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); transform = scaled_font->base.scale; cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square); } else { status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); cairo_matrix_init_identity(&transform); } if (status) goto CLEANUP_PATH; bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), GGO_NATIVE | GGO_GLYPH_INDEX, &metrics, 0, NULL, &matrix); if (bytesGlyph == GDI_ERROR) { status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); goto CLEANUP_FONT; } ptr = buffer = malloc (bytesGlyph); if (!buffer) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_FONT; } if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), GGO_NATIVE | GGO_GLYPH_INDEX, &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); goto CLEANUP_BUFFER; } while (ptr < buffer + bytesGlyph) { TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; unsigned char *endPoly = ptr + header->cb; ptr += sizeof (TTPOLYGONHEADER); _cairo_win32_transform_FIXED_to_fixed (&transform, header->pfxStart.x, header->pfxStart.y, &x, &y); status = _cairo_path_fixed_move_to (path, x, y); if (status) goto CLEANUP_BUFFER; while (ptr < endPoly) { TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; POINTFX *points = curve->apfx; int i; switch (curve->wType) { case TT_PRIM_LINE: for (i = 0; i < curve->cpfx; i++) { _cairo_win32_transform_FIXED_to_fixed (&transform, points[i].x, points[i].y, &x, &y); status = _cairo_path_fixed_line_to (path, x, y); if (status) goto CLEANUP_BUFFER; } break; case TT_PRIM_QSPLINE: for (i = 0; i < curve->cpfx - 1; i++) { cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y)) goto CLEANUP_BUFFER; _cairo_win32_transform_FIXED_to_fixed (&transform, points[i].x, points[i].y, &cx, &cy); if (i + 1 == curve->cpfx - 1) { _cairo_win32_transform_FIXED_to_fixed (&transform, points[i + 1].x, points[i + 1].y, &p2x, &p2y); } else { /* records with more than one curve use interpolation for control points, per http://support.microsoft.com/kb/q87115/ */ _cairo_win32_transform_FIXED_to_fixed (&transform, points[i + 1].x, points[i + 1].y, &x, &y); p2x = (cx + x) / 2; p2y = (cy + y) / 2; } c1x = 2 * cx / 3 + p1x / 3; c1y = 2 * cy / 3 + p1y / 3; c2x = 2 * cx / 3 + p2x / 3; c2y = 2 * cy / 3 + p2y / 3; status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); if (status) goto CLEANUP_BUFFER; } break; case TT_PRIM_CSPLINE: for (i = 0; i < curve->cpfx - 2; i += 2) { cairo_fixed_t x1, y1, x2, y2; _cairo_win32_transform_FIXED_to_fixed (&transform, points[i].x, points[i].y, &x, &y); _cairo_win32_transform_FIXED_to_fixed (&transform, points[i + 1].x, points[i + 1].y, &x1, &y1); _cairo_win32_transform_FIXED_to_fixed (&transform, points[i + 2].x, points[i + 2].y, &x2, &y2); status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2); if (status) goto CLEANUP_BUFFER; } break; } ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); } status = _cairo_path_fixed_close_path (path); if (status) goto CLEANUP_BUFFER; } _cairo_scaled_glyph_set_path (scaled_glyph, &scaled_font->base, path); CLEANUP_BUFFER: free (buffer); CLEANUP_FONT: if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); else cairo_win32_scaled_font_done_font (&scaled_font->base); CLEANUP_PATH: if (status != CAIRO_STATUS_SUCCESS) _cairo_path_fixed_destroy (path); return status; } const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { CAIRO_FONT_TYPE_WIN32, _cairo_win32_scaled_font_fini, _cairo_win32_scaled_font_glyph_init, NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ _cairo_win32_scaled_font_ucs4_to_index, _cairo_win32_scaled_font_load_truetype_table, _cairo_win32_scaled_font_index_to_ucs4, _cairo_win32_scaled_font_is_synthetic, _cairo_win32_scaled_font_index_to_glyph_name, _cairo_win32_scaled_font_load_type1_data }; /* #cairo_win32_font_face_t */ typedef struct _cairo_win32_font_face cairo_win32_font_face_t; /* If hfont is non-%NULL then logfont->lfHeight must be -S for some S, * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must * all be 0, and hfont is the result of calling CreateFontIndirectW on * logfont. */ struct _cairo_win32_font_face { cairo_font_face_t base; LOGFONTW logfont; HFONT hfont; }; /* implement the platform-specific interface */ static cairo_bool_t _is_scale (const cairo_matrix_t *matrix, double scale) { return matrix->xx == scale && matrix->yy == scale && matrix->xy == 0. && matrix->yx == 0. && matrix->x0 == 0. && matrix->y0 == 0.; } static cairo_status_t _cairo_win32_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options, cairo_scaled_font_t **font) { HFONT hfont = NULL; cairo_win32_font_face_t *font_face = abstract_face; if (font_face->hfont) { /* Check whether it's OK to go ahead and use the font-face's HFONT. */ if (_is_scale (ctm, 1.) && _is_scale (font_matrix, -font_face->logfont.lfHeight)) { hfont = font_face->hfont; } } return _win32_scaled_font_create (&font_face->logfont, hfont, &font_face->base, font_matrix, ctm, options, font); } const cairo_font_face_backend_t _cairo_win32_font_face_backend = { CAIRO_FONT_TYPE_WIN32, _cairo_win32_font_face_create_for_toy, _cairo_win32_font_face_destroy, _cairo_win32_font_face_scaled_font_create }; /* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t. * The primary purpose of this mapping is to provide unique * #cairo_font_face_t values so that our cache and mapping from * #cairo_font_face_t => #cairo_scaled_font_t works. Once the * corresponding #cairo_font_face_t objects fall out of downstream * caches, we don't need them in this hash table anymore. * * Modifications to this hash table are protected by * _cairo_win32_font_face_mutex. */ static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; static int _cairo_win32_font_face_keys_equal (const void *key_a, const void *key_b); static void _cairo_win32_font_face_hash_table_destroy (void) { cairo_hash_table_t *hash_table; /* We manually acquire the lock rather than calling * _cairo_win32_font_face_hash_table_lock simply to avoid creating * the table only to destroy it again. */ CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); hash_table = cairo_win32_font_face_hash_table; cairo_win32_font_face_hash_table = NULL; CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); if (hash_table != NULL) _cairo_hash_table_destroy (hash_table); } static cairo_hash_table_t * _cairo_win32_font_face_hash_table_lock (void) { CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); if (unlikely (cairo_win32_font_face_hash_table == NULL)) { cairo_win32_font_face_hash_table = _cairo_hash_table_create (_cairo_win32_font_face_keys_equal); if (unlikely (cairo_win32_font_face_hash_table == NULL)) { CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } } return cairo_win32_font_face_hash_table; } static void _cairo_win32_font_face_hash_table_unlock (void) { CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); } static void _cairo_win32_font_face_destroy (void *abstract_face) { cairo_win32_font_face_t *font_face = abstract_face; cairo_hash_table_t *hash_table; hash_table = _cairo_win32_font_face_hash_table_lock (); /* All created objects must have been mapped in the hash table. */ assert (hash_table != NULL); if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { /* somebody recreated the font whilst we waited for the lock */ _cairo_win32_font_face_hash_table_unlock (); return; } /* Font faces in SUCCESS status are guaranteed to be in the * hashtable. Font faces in an error status are removed from the * hashtable if they are found during a lookup, thus they should * only be removed if they are in the hashtable. */ if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) || _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face) _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); _cairo_win32_font_face_hash_table_unlock (); } static void _cairo_win32_font_face_init_key (cairo_win32_font_face_t *key, LOGFONTW *logfont, HFONT font) { unsigned long hash = _CAIRO_HASH_INIT_VALUE; key->logfont = *logfont; key->hfont = font; hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName)); hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight)); hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic)); key->base.hash_entry.hash = hash; } static int _cairo_win32_font_face_keys_equal (const void *key_a, const void *key_b) { const cairo_win32_font_face_t *face_a = key_a; const cairo_win32_font_face_t *face_b = key_b; if (face_a->logfont.lfWeight == face_b->logfont.lfWeight && face_a->logfont.lfItalic == face_b->logfont.lfItalic && face_a->logfont.lfUnderline == face_b->logfont.lfUnderline && face_a->logfont.lfStrikeOut == face_b->logfont.lfStrikeOut && face_a->logfont.lfCharSet == face_b->logfont.lfCharSet && face_a->logfont.lfOutPrecision == face_b->logfont.lfOutPrecision && face_a->logfont.lfClipPrecision == face_b->logfont.lfClipPrecision && face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily && (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0)) return TRUE; else return FALSE; } /** * cairo_win32_font_face_create_for_logfontw_hfont: * @logfont: A #LOGFONTW structure specifying the font to use. * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and * lfEscapement must be zero. * @font: An #HFONT that can be used when the font matrix is a scale by * -lfHeight and the CTM is identity. * * Creates a new font for the Win32 font backend based on a * #LOGFONT. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * The #cairo_scaled_font_t * returned from cairo_scaled_font_create() is also for the Win32 backend * and can be used with functions such as cairo_win32_scaled_font_select_font(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.6 **/ cairo_font_face_t * cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) { cairo_win32_font_face_t *font_face, key; cairo_hash_table_t *hash_table; cairo_status_t status; hash_table = _cairo_win32_font_face_hash_table_lock (); if (unlikely (hash_table == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } _cairo_win32_font_face_init_key (&key, logfont, font); /* Return existing unscaled font if it exists in the hash table. */ font_face = _cairo_hash_table_lookup (hash_table, &key.base.hash_entry); if (font_face != NULL) { if (font_face->base.status == CAIRO_STATUS_SUCCESS) { cairo_font_face_reference (&font_face->base); _cairo_win32_font_face_hash_table_unlock (); return &font_face->base; } /* remove the bad font from the hash table */ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); } /* Otherwise create it and insert into hash table. */ font_face = malloc (sizeof (cairo_win32_font_face_t)); if (!font_face) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FAIL; } _cairo_win32_font_face_init_key (font_face, logfont, font); _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); if (unlikely (status)) goto FAIL; _cairo_win32_font_face_hash_table_unlock (); return &font_face->base; FAIL: _cairo_win32_font_face_hash_table_unlock (); return (cairo_font_face_t *)&_cairo_font_face_nil; } /** * cairo_win32_font_face_create_for_logfontw: * @logfont: A #LOGFONTW structure specifying the font to use. * The lfHeight, lfWidth, lfOrientation and lfEscapement * fields of this structure are ignored. * * Creates a new font for the Win32 font backend based on a * #LOGFONT. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * The #cairo_scaled_font_t * returned from cairo_scaled_font_create() is also for the Win32 backend * and can be used with functions such as cairo_win32_scaled_font_select_font(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.0 **/ cairo_font_face_t * cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) { return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL); } /** * cairo_win32_font_face_create_for_hfont: * @font: An #HFONT structure specifying the font to use. * * Creates a new font for the Win32 font backend based on a * #HFONT. This font can then be used with * cairo_set_font_face() or cairo_scaled_font_create(). * The #cairo_scaled_font_t * returned from cairo_scaled_font_create() is also for the Win32 backend * and can be used with functions such as cairo_win32_scaled_font_select_font(). * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. * * Since: 1.2 **/ cairo_font_face_t * cairo_win32_font_face_create_for_hfont (HFONT font) { LOGFONTW logfont; GetObjectW (font, sizeof(logfont), &logfont); if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 || logfont.lfWidth != 0) { /* We can't use this font because that optimization requires that * lfEscapement, lfOrientation and lfWidth be zero. */ font = NULL; } return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font); } static cairo_bool_t _cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) { return scaled_font->backend == &_cairo_win32_scaled_font_backend; } /** * cairo_win32_scaled_font_select_font: * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an * object can be created with cairo_win32_font_face_create_for_logfontw(). * @hdc: a device context * * Selects the font into the given device context and changes the * map mode and world transformation of the device context to match * that of the font. This function is intended for use when using * layout APIs such as Uniscribe to do text layout with the * cairo font. After finishing using the device context, you must call * cairo_win32_scaled_font_done_font() to release any resources allocated * by this function. * * See cairo_win32_scaled_font_get_metrics_factor() for converting logical * coordinates from the device context to font space. * * Normally, calls to SaveDC() and RestoreDC() would be made around * the use of this function to preserve the original graphics state. * * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and * the device context is unchanged. * * Since: 1.0 **/ cairo_status_t cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, HDC hdc) { cairo_status_t status; HFONT hfont; HFONT old_hfont = NULL; int old_mode; if (! _cairo_scaled_font_is_win32 (scaled_font)) { return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); } if (scaled_font->status) return scaled_font->status; status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont); if (status) return status; old_hfont = SelectObject (hdc, hfont); if (!old_hfont) return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); old_mode = SetGraphicsMode (hdc, GM_ADVANCED); if (!old_mode) { status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); SelectObject (hdc, old_hfont); return status; } status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); if (status) { SetGraphicsMode (hdc, old_mode); SelectObject (hdc, old_hfont); return status; } SetMapMode (hdc, MM_TEXT); return CAIRO_STATUS_SUCCESS; } /** * cairo_win32_scaled_font_done_font: * @scaled_font: A scaled font from the Win32 font backend. * * Releases any resources allocated by cairo_win32_scaled_font_select_font() * * Since: 1.0 **/ void cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) { if (! _cairo_scaled_font_is_win32 (scaled_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); } } /** * cairo_win32_scaled_font_get_metrics_factor: * @scaled_font: a scaled font from the Win32 font backend * * Gets a scale factor between logical coordinates in the coordinate * space used by cairo_win32_scaled_font_select_font() (that is, the * coordinate system used by the Windows functions to return metrics) and * font space coordinates. * * Return value: factor to multiply logical units by to get font space * coordinates. * * Since: 1.0 **/ double cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) { if (! _cairo_scaled_font_is_win32 (scaled_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); return 1.; } return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; } /** * cairo_win32_scaled_font_get_logical_to_device: * @scaled_font: a scaled font from the Win32 font backend * @logical_to_device: matrix to return * * Gets the transformation mapping the logical space used by @scaled_font * to device space. * * Since: 1.4 **/ void cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, cairo_matrix_t *logical_to_device) { cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; if (! _cairo_scaled_font_is_win32 (scaled_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); cairo_matrix_init_identity (logical_to_device); return; } *logical_to_device = win_font->logical_to_device; } /** * cairo_win32_scaled_font_get_device_to_logical: * @scaled_font: a scaled font from the Win32 font backend * @device_to_logical: matrix to return * * Gets the transformation mapping device space to the logical space * used by @scaled_font. * * Since: 1.4 **/ void cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, cairo_matrix_t *device_to_logical) { cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; if (! _cairo_scaled_font_is_win32 (scaled_font)) { _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); cairo_matrix_init_identity (device_to_logical); return; } *device_to_logical = win_font->device_to_logical; } void _cairo_win32_font_reset_static_data (void) { _cairo_win32_font_face_hash_table_destroy (); } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-gdi-compositor.c000066400000000000000000000463761271037650300304200ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is University of Southern * California. * * Contributor(s): * Carl D. Worth * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ /* The original X drawing API was very restrictive in what it could handle, * pixel-aligned fill/blits are all that map into Cairo's drawing model. */ #include "cairoint.h" #include "cairo-win32-private.h" #include "cairo-boxes-private.h" #include "cairo-clip-inline.h" #include "cairo-compositor-private.h" #include "cairo-image-surface-private.h" #include "cairo-pattern-private.h" #include "cairo-region-private.h" #include "cairo-surface-inline.h" #include "cairo-surface-offset-private.h" #if !defined(AC_SRC_OVER) #define AC_SRC_OVER 0x00 #pragma pack(1) typedef struct { BYTE BlendOp; BYTE BlendFlags; BYTE SourceConstantAlpha; BYTE AlphaFormat; }BLENDFUNCTION; #pragma pack() #endif /* for compatibility with VC++ 6 */ #ifndef AC_SRC_ALPHA #define AC_SRC_ALPHA 0x01 #endif #define PELS_72DPI ((LONG)(72. / 0.0254)) /* the low-level interface */ struct fill_box { HDC dc; HBRUSH brush; }; static cairo_bool_t fill_box (cairo_box_t *box, void *closure) { struct fill_box *fb = closure; RECT rect; rect.left = _cairo_fixed_integer_part (box->p1.x); rect.top = _cairo_fixed_integer_part (box->p1.y); rect.right = _cairo_fixed_integer_part (box->p2.x); rect.bottom = _cairo_fixed_integer_part (box->p2.y); TRACE ((stderr, "%s\n", __FUNCTION__)); return FillRect (fb->dc, &rect, fb->brush); } struct check_box { cairo_rectangle_int_t limit; int tx, ty; }; struct copy_box { cairo_rectangle_int_t limit; int tx, ty; HDC dst, src; BLENDFUNCTION bf; cairo_win32_alpha_blend_func_t alpha_blend; }; static cairo_bool_t copy_box (cairo_box_t *box, void *closure) { const struct copy_box *cb = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); TRACE ((stderr, "%s\n", __FUNCTION__)); return BitBlt (cb->dst, x, y, width, height, cb->src, x + cb->tx, y + cb->ty, SRCCOPY); } static cairo_bool_t alpha_box (cairo_box_t *box, void *closure) { const struct copy_box *cb = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); TRACE ((stderr, "%s\n", __FUNCTION__)); return cb->alpha_blend (cb->dst, x, y, width, height, cb->src, x + cb->tx, y + cb->ty, width, height, cb->bf); } struct upload_box { cairo_rectangle_int_t limit; int tx, ty; HDC dst; BITMAPINFO bi; void *data; }; static cairo_bool_t upload_box (cairo_box_t *box, void *closure) { const struct upload_box *cb = closure; int x = _cairo_fixed_integer_part (box->p1.x); int y = _cairo_fixed_integer_part (box->p1.y); int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); int src_height = -cb->bi.bmiHeader.biHeight; TRACE ((stderr, "%s\n", __FUNCTION__)); return StretchDIBits (cb->dst, x, y + height - 1, width, -height, x + cb->tx, src_height - (y + cb->ty - 1), width, -height, cb->data, &cb->bi, DIB_RGB_COLORS, SRCCOPY); } /* the mid-level: converts boxes into drawing operations */ static COLORREF color_to_rgb(const cairo_color_t *c) { return RGB (c->red_short >> 8, c->green_short >> 8, c->blue_short >> 8); } static cairo_int_status_t fill_boxes (cairo_win32_display_surface_t *dst, const cairo_pattern_t *src, cairo_boxes_t *boxes) { const cairo_color_t *color = &((cairo_solid_pattern_t *) src)->color; cairo_status_t status = CAIRO_STATUS_SUCCESS; struct fill_box fb; TRACE ((stderr, "%s\n", __FUNCTION__)); fb.dc = dst->win32.dc; fb.brush = CreateSolidBrush (color_to_rgb(color)); if (!fb.brush) return _cairo_win32_print_gdi_error (__FUNCTION__); if (! _cairo_boxes_for_each_box (boxes, fill_box, &fb)) status = CAIRO_INT_STATUS_UNSUPPORTED; DeleteObject (fb.brush); return status; } static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure) { struct check_box *data = closure; /* The box is pixel-aligned so the truncation is safe. */ return _cairo_fixed_integer_part (box->p1.x) + data->tx >= data->limit.x && _cairo_fixed_integer_part (box->p1.y) + data->ty >= data->limit.y && _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->limit.x + data->limit.width && _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->limit.y + data->limit.height; } static cairo_status_t copy_boxes (cairo_win32_display_surface_t *dst, const cairo_pattern_t *source, cairo_boxes_t *boxes) { const cairo_surface_pattern_t *pattern; struct copy_box cb; cairo_surface_t *surface; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); pattern = (const cairo_surface_pattern_t *) source; surface = _cairo_surface_get_source (pattern->surface, &cb.limit); if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { surface = to_image_surface(surface)->parent; if (surface == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; } if (surface->type != CAIRO_SURFACE_TYPE_WIN32) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &cb.tx, &cb.ty)) return CAIRO_INT_STATUS_UNSUPPORTED; cb.dst = dst->win32.dc; cb.src = to_win32_surface(surface)->dc; /* First check that the data is entirely within the image */ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) return CAIRO_INT_STATUS_UNSUPPORTED; status = __cairo_surface_flush (surface, 0); if (status) return status; cb.tx += cb.limit.x; cb.ty += cb.limit.y; status = CAIRO_STATUS_SUCCESS; if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) status = CAIRO_INT_STATUS_UNSUPPORTED; _cairo_win32_display_surface_discard_fallback (dst); return status; } static cairo_status_t upload_boxes (cairo_win32_display_surface_t *dst, const cairo_pattern_t *source, cairo_boxes_t *boxes) { const cairo_surface_pattern_t *pattern; struct upload_box cb; cairo_surface_t *surface; cairo_image_surface_t *image; void *image_extra; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &cb.tx, &cb.ty)) return CAIRO_INT_STATUS_UNSUPPORTED; pattern = (const cairo_surface_pattern_t *) source; surface = _cairo_surface_get_source (pattern->surface, &cb.limit); /* First check that the data is entirely within the image */ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) { status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); if (status) return status; } else image = to_image_surface(surface); status = CAIRO_INT_STATUS_UNSUPPORTED; if (!(image->format == CAIRO_FORMAT_ARGB32 || image->format == CAIRO_FORMAT_RGB24)) goto err; if (image->stride != 4*image->width) goto err; cb.dst = dst->win32.dc; cb.data = image->data; cb.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); cb.bi.bmiHeader.biWidth = image->width; cb.bi.bmiHeader.biHeight = -image->height; cb.bi.bmiHeader.biSizeImage = 0; cb.bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; cb.bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; cb.bi.bmiHeader.biPlanes = 1; cb.bi.bmiHeader.biBitCount = 32; cb.bi.bmiHeader.biCompression = BI_RGB; cb.bi.bmiHeader.biClrUsed = 0; cb.bi.bmiHeader.biClrImportant = 0; cb.tx += cb.limit.x; cb.ty += cb.limit.y; status = CAIRO_STATUS_SUCCESS; if (! _cairo_boxes_for_each_box (boxes, upload_box, &cb)) status = CAIRO_INT_STATUS_UNSUPPORTED; _cairo_win32_display_surface_discard_fallback (dst); err: if (&image->base != surface) _cairo_surface_release_source_image (surface, image, image_extra); return status; } static cairo_status_t alpha_blend_boxes (cairo_win32_display_surface_t *dst, const cairo_pattern_t *source, cairo_boxes_t *boxes, uint8_t alpha) { const cairo_surface_pattern_t *pattern; struct copy_box cb; cairo_surface_t *surface; cairo_win32_display_surface_t *src; cairo_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (source->type != CAIRO_PATTERN_TYPE_SURFACE) return CAIRO_INT_STATUS_UNSUPPORTED; pattern = (const cairo_surface_pattern_t *) source; surface = _cairo_surface_get_source (pattern->surface, &cb.limit); if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { surface = to_image_surface(surface)->parent; if (surface == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; } if (surface->type != CAIRO_SURFACE_TYPE_WIN32) return CAIRO_INT_STATUS_UNSUPPORTED; if (! _cairo_matrix_is_integer_translation (&source->matrix, &cb.tx, &cb.ty)) return CAIRO_INT_STATUS_UNSUPPORTED; src = to_win32_display_surface (surface); cb.dst = dst->win32.dc; cb.src = src->win32.dc; /* First check that the data is entirely within the image */ if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) return CAIRO_INT_STATUS_UNSUPPORTED; status = __cairo_surface_flush (&src->win32.base, 0); if (status) return status; cb.bf.BlendOp = AC_SRC_OVER; cb.bf.BlendFlags = 0; cb.bf.SourceConstantAlpha = alpha; cb.bf.AlphaFormat = (src->win32.format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; cb.alpha_blend = to_win32_device(dst->win32.base.device)->alpha_blend; cb.tx += cb.limit.x; cb.ty += cb.limit.y; status = CAIRO_STATUS_SUCCESS; if (! _cairo_boxes_for_each_box (boxes, alpha_box, &cb)) status = CAIRO_INT_STATUS_UNSUPPORTED; _cairo_win32_display_surface_discard_fallback (dst); return status; } static cairo_bool_t can_alpha_blend (cairo_win32_display_surface_t *dst) { if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND) == 0) return FALSE; return to_win32_device(dst->win32.base.device)->alpha_blend != NULL; } static cairo_status_t draw_boxes (cairo_composite_rectangles_t *composite, cairo_boxes_t *boxes) { cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface); cairo_operator_t op = composite->op; const cairo_pattern_t *src = &composite->source_pattern.base; cairo_int_status_t status; TRACE ((stderr, "%s\n", __FUNCTION__)); if (boxes->num_boxes == 0 && composite->is_bounded) return CAIRO_STATUS_SUCCESS; if (!boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (op == CAIRO_OPERATOR_CLEAR) op = CAIRO_OPERATOR_SOURCE; if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (src, &composite->bounded)) op = CAIRO_OPERATOR_SOURCE; if (dst->win32.base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) op = CAIRO_OPERATOR_SOURCE; if (op == CAIRO_OPERATOR_SOURCE) { status = CAIRO_INT_STATUS_UNSUPPORTED; if (src->type == CAIRO_PATTERN_TYPE_SURFACE) { status = copy_boxes (dst, src, boxes); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = upload_boxes (dst, src, boxes); } else if (src->type == CAIRO_PATTERN_TYPE_SOLID) { status = fill_boxes (dst, src, boxes); } return status; } if (op == CAIRO_OPERATOR_OVER && can_alpha_blend (dst)) return alpha_blend_boxes (dst, src, boxes, 255); return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t opacity_boxes (cairo_composite_rectangles_t *composite, cairo_boxes_t *boxes) { cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface); cairo_operator_t op = composite->op; const cairo_pattern_t *src = &composite->source_pattern.base; TRACE ((stderr, "%s\n", __FUNCTION__)); if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) return CAIRO_INT_STATUS_UNSUPPORTED; if (boxes->num_boxes == 0 && composite->is_bounded) return CAIRO_STATUS_SUCCESS; if (!boxes->is_pixel_aligned) return CAIRO_INT_STATUS_UNSUPPORTED; if (op != CAIRO_OPERATOR_OVER) return CAIRO_INT_STATUS_UNSUPPORTED; if (!can_alpha_blend (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; return alpha_blend_boxes (dst, src, boxes, composite->mask_pattern.solid.color.alpha_short >> 8); } /* high-level compositor interface */ static cairo_bool_t check_blit (cairo_composite_rectangles_t *composite) { cairo_win32_display_surface_t *dst; if (composite->clip->path) return FALSE; dst = to_win32_display_surface (composite->surface); if (dst->fallback) return FALSE; if (dst->win32.format != CAIRO_FORMAT_RGB24) return FALSE; if (dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) return TRUE; return dst->image == NULL; } static cairo_int_status_t _cairo_win32_gdi_compositor_paint (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if (check_blit (composite)) { cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_clip_steal_boxes (composite->clip, &boxes); status = draw_boxes (composite, &boxes); _cairo_clip_unsteal_boxes (composite->clip, &boxes); } return status; } static cairo_int_status_t _cairo_win32_gdi_compositor_mask (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite) { cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; if (check_blit (composite)) { cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_clip_steal_boxes (composite->clip, &boxes); status = opacity_boxes (composite, &boxes); _cairo_clip_unsteal_boxes (composite->clip, &boxes); } return status; } static cairo_int_status_t _cairo_win32_gdi_compositor_stroke (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (check_blit (composite) && _cairo_path_fixed_stroke_is_rectilinear (path)) { cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_boxes_init_with_clip (&boxes, composite->clip); status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, style, ctm, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = draw_boxes (composite, &boxes); _cairo_boxes_fini (&boxes); } return status; } static cairo_int_status_t _cairo_win32_gdi_compositor_fill (const cairo_compositor_t *compositor, cairo_composite_rectangles_t *composite, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (check_blit (composite) && _cairo_path_fixed_fill_is_rectilinear (path)) { cairo_boxes_t boxes; TRACE ((stderr, "%s\n", __FUNCTION__)); _cairo_boxes_init_with_clip (&boxes, composite->clip); status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, fill_rule, antialias, &boxes); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = draw_boxes (composite, &boxes); _cairo_boxes_fini (&boxes); } return status; } static cairo_bool_t check_glyphs (cairo_composite_rectangles_t *composite, cairo_scaled_font_t *scaled_font) { if (! _cairo_clip_is_region (composite->clip)) return FALSE; if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) return FALSE; if (! _cairo_pattern_is_opaque_solid (&composite->source_pattern.base)) return FALSE; return (composite->op == CAIRO_OPERATOR_CLEAR || composite->op == CAIRO_OPERATOR_SOURCE || composite->op == CAIRO_OPERATOR_OVER); } static cairo_int_status_t _cairo_win32_gdi_compositor_glyphs (const cairo_compositor_t *compositor, cairo_composite_rectangles_t*composite, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_bool_t overlap) { cairo_int_status_t status; status = CAIRO_INT_STATUS_UNSUPPORTED; if (check_blit (composite) && check_glyphs (composite, scaled_font)) { cairo_win32_display_surface_t *dst = to_win32_display_surface (composite->surface); TRACE ((stderr, "%s\n", __FUNCTION__)); status = _cairo_win32_display_surface_set_clip(dst, composite->clip); if (status) return status; status = _cairo_win32_surface_emit_glyphs (&dst->win32, &composite->source_pattern.base, glyphs, num_glyphs, scaled_font, TRUE); _cairo_win32_display_surface_unset_clip (dst); } return status; } const cairo_compositor_t * _cairo_win32_gdi_compositor_get (void) { static cairo_compositor_t compositor; if (compositor.delegate == NULL) { compositor.delegate = &_cairo_fallback_compositor; compositor.paint = _cairo_win32_gdi_compositor_paint; compositor.mask = _cairo_win32_gdi_compositor_mask; compositor.fill = _cairo_win32_gdi_compositor_fill; compositor.stroke = _cairo_win32_gdi_compositor_stroke; compositor.glyphs = _cairo_win32_gdi_compositor_glyphs; } return &compositor; } Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-printing-surface.c000066400000000000000000001667241271037650300307410ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2007, 2008 Adrian Johnson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Adrian Johnson. * * Contributor(s): * Adrian Johnson * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-paginated-private.h" #include "cairo-clip-private.h" #include "cairo-win32-private.h" #include "cairo-recording-surface-inline.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-image-info-private.h" #include "cairo-image-surface-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" #include #if !defined(POSTSCRIPT_IDENTIFY) # define POSTSCRIPT_IDENTIFY 0x1015 #endif #if !defined(PSIDENT_GDICENTRIC) # define PSIDENT_GDICENTRIC 0x0000 #endif #if !defined(GET_PS_FEATURESETTING) # define GET_PS_FEATURESETTING 0x1019 #endif #if !defined(FEATURESETTING_PSLEVEL) # define FEATURESETTING_PSLEVEL 0x0002 #endif #if !defined(GRADIENT_FILL_RECT_H) # define GRADIENT_FILL_RECT_H 0x00 #endif #if !defined(CHECKJPEGFORMAT) # define CHECKJPEGFORMAT 0x1017 #endif #if !defined(CHECKPNGFORMAT) # define CHECKPNGFORMAT 0x1018 #endif #define PELS_72DPI ((LONG)(72. / 0.0254)) static const char *_cairo_win32_printing_supported_mime_types[] = { CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_PNG, NULL }; static const cairo_surface_backend_t cairo_win32_printing_surface_backend; static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend; static void _cairo_win32_printing_surface_init_ps_mode (cairo_win32_printing_surface_t *surface) { DWORD word; INT ps_feature, ps_level; word = PSIDENT_GDICENTRIC; if (ExtEscape (surface->win32.dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) return; ps_feature = FEATURESETTING_PSLEVEL; if (ExtEscape (surface->win32.dc, GET_PS_FEATURESETTING, sizeof(INT), (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0) return; if (ps_level >= 3) surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; } static void _cairo_win32_printing_surface_init_image_support (cairo_win32_printing_surface_t *surface) { DWORD word; word = CHECKJPEGFORMAT; if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; word = CHECKPNGFORMAT; if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; } /* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not * work unless the GDI function GdiInitializeLanguagePack() has been * called. * * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html * * The only information I could find on the how to use this * undocumented function is the use in: * * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup * * to solve the same problem. The above code first checks if LPK.DLL * is already loaded. If it is not it calls * GdiInitializeLanguagePack() using the prototype * BOOL GdiInitializeLanguagePack (int) * and argument 0. */ static void _cairo_win32_printing_surface_init_language_pack (cairo_win32_printing_surface_t *surface) { typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); gdi_init_lang_pack_func_t gdi_init_lang_pack; HMODULE module; if (GetModuleHandleW (L"LPK.DLL")) return; module = GetModuleHandleW (L"GDI32.DLL"); if (module) { gdi_init_lang_pack = (gdi_init_lang_pack_func_t) GetProcAddress (module, "GdiInitializeLanguagePack"); if (gdi_init_lang_pack) gdi_init_lang_pack (0); } } static cairo_int_status_t analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) { cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; cairo_image_transparency_t transparency; status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (status) return status; transparency = _cairo_image_analyze_transparency (image); switch (transparency) { case CAIRO_IMAGE_UNKNOWN: ASSERT_NOT_REACHED; case CAIRO_IMAGE_IS_OPAQUE: status = CAIRO_STATUS_SUCCESS; break; case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: case CAIRO_IMAGE_HAS_ALPHA: status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; break; } _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } static cairo_bool_t surface_pattern_supported (const cairo_surface_pattern_t *pattern) { if (_cairo_surface_is_recording (pattern->surface)) return TRUE; if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && pattern->surface->backend->acquire_source_image == NULL) { return FALSE; } return TRUE; } static cairo_bool_t pattern_supported (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) return TRUE; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) return surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; return FALSE; } static cairo_int_status_t _cairo_win32_printing_surface_analyze_operation (cairo_win32_printing_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern) { if (! pattern_supported (surface, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; if (!(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_CLEAR)) return CAIRO_INT_STATUS_UNSUPPORTED; if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if ( _cairo_surface_is_recording (surface_pattern->surface)) return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) return CAIRO_STATUS_SUCCESS; /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If * the pattern contains transparency, we return * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis * surface. If the analysis surface determines that there is * anything drawn under this operation, a fallback image will be * used. Otherwise the operation will be replayed during the * render stage and we blend the transarency into the white * background to convert the pattern to opaque. */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; return analyze_surface_pattern_transparency (surface_pattern); } if (_cairo_pattern_is_opaque (pattern, NULL)) return CAIRO_STATUS_SUCCESS; else return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; } static cairo_bool_t _cairo_win32_printing_surface_operation_supported (cairo_win32_printing_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern) { if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) return TRUE; else return FALSE; } static void _cairo_win32_printing_surface_init_clear_color (cairo_win32_printing_surface_t *surface, cairo_solid_pattern_t *color) { if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE); else _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK); } static COLORREF _cairo_win32_printing_surface_flatten_transparency (cairo_win32_printing_surface_t *surface, const cairo_color_t *color) { COLORREF c; BYTE red, green, blue; red = color->red_short >> 8; green = color->green_short >> 8; blue = color->blue_short >> 8; if (!CAIRO_COLOR_IS_OPAQUE(color)) { if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { /* Blend into white */ uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8); red = (color->red_short >> 8) + one_minus_alpha; green = (color->green_short >> 8) + one_minus_alpha; blue = (color->blue_short >> 8) + one_minus_alpha; } else { /* Blend into black */ red = (color->red_short >> 8); green = (color->green_short >> 8); blue = (color->blue_short >> 8); } } c = RGB (red, green, blue); return c; } static cairo_status_t _cairo_win32_printing_surface_select_solid_brush (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *source) { cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; COLORREF color; color = _cairo_win32_printing_surface_flatten_transparency (surface, &pattern->color); surface->brush = CreateSolidBrush (color); if (!surface->brush) return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); surface->old_brush = SelectObject (surface->win32.dc, surface->brush); return CAIRO_STATUS_SUCCESS; } static void _cairo_win32_printing_surface_done_solid_brush (cairo_win32_printing_surface_t *surface) { if (surface->old_brush) { SelectObject (surface->win32.dc, surface->old_brush); DeleteObject (surface->brush); surface->old_brush = NULL; } } static cairo_status_t _cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_printing_surface_t *surface, RECT *clip) { XFORM xform; _cairo_matrix_to_win32_xform (&surface->ctm, &xform); if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); GetClipBox (surface->win32.dc, clip); _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); if (!SetWorldTransform (surface->win32.dc, &xform)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) { RECT clip; cairo_status_t status; GetClipBox (surface->win32.dc, &clip); status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); if (status) return status; FillRect (surface->win32.dc, &clip, surface->brush); _cairo_win32_printing_surface_done_solid_brush (surface); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_printing_surface_t *surface, cairo_surface_pattern_t *pattern) { cairo_content_t old_content; cairo_matrix_t old_ctm; cairo_bool_t old_has_ctm; cairo_rectangle_int_t recording_extents; cairo_status_t status; cairo_extend_t extend; cairo_matrix_t p2d; XFORM xform; int x_tile, y_tile, left, right, top, bottom; RECT clip; cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; cairo_box_t bbox; extend = cairo_pattern_get_extend (&pattern->base); p2d = pattern->base.matrix; status = cairo_matrix_invert (&p2d); /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; cairo_matrix_multiply (&p2d, &p2d, &surface->ctm); surface->ctm = p2d; SaveDC (surface->win32.dc); _cairo_matrix_to_win32_xform (&p2d, &xform); status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); if (status) return status; _cairo_box_round_to_rectangle (&bbox, &recording_extents); status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); if (status) return status; if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); } else { left = 0; right = 1; top = 0; bottom = 1; } old_content = surface->content; if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { surface->content = CAIRO_CONTENT_COLOR; status = _cairo_win32_printing_surface_paint_solid_pattern (surface, &_cairo_pattern_black.base); if (status) return status; } for (y_tile = top; y_tile < bottom; y_tile++) { for (x_tile = left; x_tile < right; x_tile++) { cairo_matrix_t m; double x, y; SaveDC (surface->win32.dc); m = p2d; cairo_matrix_translate (&m, x_tile*recording_extents.width, y_tile*recording_extents.height); if (extend == CAIRO_EXTEND_REFLECT) { if (x_tile % 2) { cairo_matrix_translate (&m, recording_extents.width, 0); cairo_matrix_scale (&m, -1, 1); } if (y_tile % 2) { cairo_matrix_translate (&m, 0, recording_extents.height); cairo_matrix_scale (&m, 1, -1); } } surface->ctm = m; surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); /* Set clip path around bbox of the pattern. */ BeginPath (surface->win32.dc); x = 0; y = 0; cairo_matrix_transform_point (&surface->ctm, &x, &y); MoveToEx (surface->win32.dc, (int) x, (int) y, NULL); x = recording_extents.width; y = 0; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->win32.dc, (int) x, (int) y); x = recording_extents.width; y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->win32.dc, (int) x, (int) y); x = 0; y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); LineTo (surface->win32.dc, (int) x, (int) y); CloseFigure (surface->win32.dc); EndPath (surface->win32.dc); SelectClipPath (surface->win32.dc, RGN_AND); SaveDC (surface->win32.dc); /* Allow clip path to be reset during replay */ status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL, &surface->win32.base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); /* Restore both the clip save and our earlier path SaveDC */ RestoreDC (surface->win32.dc, -2); if (status) return status; } } surface->content = old_content; surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; RestoreDC (surface->win32.dc, -1); return status; } static cairo_int_status_t _cairo_win32_printing_surface_check_jpeg (cairo_win32_printing_surface_t *surface, cairo_surface_t *source, const unsigned char **data, unsigned long *length, cairo_image_info_t *info) { const unsigned char *mime_data; unsigned long mime_data_length; cairo_int_status_t status; DWORD result; if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); if (status) return status; result = 0; if (ExtEscape(surface->win32.dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, sizeof(result), (char *) &result) <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; if (result != 1) return CAIRO_INT_STATUS_UNSUPPORTED; *data = mime_data; *length = mime_data_length; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_win32_printing_surface_check_png (cairo_win32_printing_surface_t *surface, cairo_surface_t *source, const unsigned char **data, unsigned long *length, cairo_image_info_t *info) { const unsigned char *mime_data; unsigned long mime_data_length; cairo_int_status_t status; DWORD result; if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, &mime_data, &mime_data_length); if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); if (status) return status; result = 0; if (ExtEscape(surface->win32.dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, sizeof(result), (char *) &result) <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; if (result != 1) return CAIRO_INT_STATUS_UNSUPPORTED; *data = mime_data; *length = mime_data_length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_t *surface, cairo_surface_pattern_t *pattern) { cairo_status_t status; cairo_extend_t extend; cairo_image_surface_t *image; void *image_extra; cairo_image_surface_t *opaque_image = NULL; BITMAPINFO bi; cairo_matrix_t m; int oldmode; XFORM xform; int x_tile, y_tile, left, right, top, bottom; RECT clip; const cairo_color_t *background_color; const unsigned char *mime_data; unsigned long mime_size; cairo_image_info_t mime_info; cairo_bool_t use_mime; DWORD mime_type; /* If we can't use StretchDIBits with this surface, we can't do anything * here. */ if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) background_color = CAIRO_COLOR_WHITE; else background_color = CAIRO_COLOR_BLACK; extend = cairo_pattern_get_extend (&pattern->base); status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (status) return status; if (image->base.status) { status = image->base.status; goto CLEANUP_IMAGE; } if (image->width == 0 || image->height == 0) { status = CAIRO_STATUS_SUCCESS; goto CLEANUP_IMAGE; } mime_type = BI_JPEG; status = _cairo_win32_printing_surface_check_jpeg (surface, pattern->surface, &mime_data, &mime_size, &mime_info); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { mime_type = BI_PNG; status = _cairo_win32_printing_surface_check_png (surface, pattern->surface, &mime_data, &mime_size, &mime_info); } if (_cairo_status_is_error (status)) return status; use_mime = (status == CAIRO_STATUS_SUCCESS); if (!use_mime && image->format != CAIRO_FORMAT_RGB24) { cairo_surface_t *opaque_surface; cairo_surface_pattern_t image_pattern; cairo_solid_pattern_t background_pattern; opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, image->width, image->height); if (opaque_surface->status) { status = opaque_surface->status; goto CLEANUP_OPAQUE_IMAGE; } _cairo_pattern_init_solid (&background_pattern, background_color); status = _cairo_surface_paint (opaque_surface, CAIRO_OPERATOR_SOURCE, &background_pattern.base, NULL); if (status) goto CLEANUP_OPAQUE_IMAGE; _cairo_pattern_init_for_surface (&image_pattern, &image->base); status = _cairo_surface_paint (opaque_surface, CAIRO_OPERATOR_OVER, &image_pattern.base, NULL); _cairo_pattern_fini (&image_pattern.base); if (status) goto CLEANUP_OPAQUE_IMAGE; opaque_image = (cairo_image_surface_t *) opaque_surface; } else { opaque_image = image; } bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; m = pattern->base.matrix; status = cairo_matrix_invert (&m); /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); SaveDC (surface->win32.dc); _cairo_matrix_to_win32_xform (&m, &xform); if (! SetWorldTransform (surface->win32.dc, &xform)) { status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); goto CLEANUP_OPAQUE_IMAGE; } oldmode = SetStretchBltMode(surface->win32.dc, HALFTONE); GetClipBox (surface->win32.dc, &clip); if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { left = floor ( clip.left / (double) opaque_image->width); right = ceil (clip.right / (double) opaque_image->width); top = floor (clip.top / (double) opaque_image->height); bottom = ceil (clip.bottom / (double) opaque_image->height); } else { left = 0; right = 1; top = 0; bottom = 1; } for (y_tile = top; y_tile < bottom; y_tile++) { for (x_tile = left; x_tile < right; x_tile++) { if (!StretchDIBits (surface->win32.dc, x_tile*opaque_image->width, y_tile*opaque_image->height, opaque_image->width, opaque_image->height, 0, 0, use_mime ? mime_info.width : opaque_image->width, use_mime ? mime_info.height : opaque_image->height, use_mime ? mime_data : opaque_image->data, &bi, DIB_RGB_COLORS, SRCCOPY)) { status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); goto CLEANUP_OPAQUE_IMAGE; } } } SetStretchBltMode(surface->win32.dc, oldmode); RestoreDC (surface->win32.dc, -1); CLEANUP_OPAQUE_IMAGE: if (opaque_image != image) cairo_surface_destroy (&opaque_image->base); CLEANUP_IMAGE: _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; } static cairo_status_t _cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_printing_surface_t *surface, cairo_surface_pattern_t *pattern) { if (_cairo_surface_is_recording (pattern->surface)) { return _cairo_win32_printing_surface_paint_recording_pattern (surface, pattern); } else { return _cairo_win32_printing_surface_paint_image_pattern (surface, pattern); } } static void vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) { /* MSDN says that the range here is 0x0000 .. 0xff00; * that may well be a typo, but just chop the low bits * here. */ vert->Alpha = 0xff00; vert->Red = color->red_short & 0xff00; vert->Green = color->green_short & 0xff00; vert->Blue = color->blue_short & 0xff00; } static cairo_int_status_t _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_printing_surface_t *surface, cairo_linear_pattern_t *pattern) { TRIVERTEX *vert; GRADIENT_RECT *rect; RECT clip; XFORM xform; int i, num_stops; cairo_matrix_t mat, rot; double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs; cairo_extend_t extend; int range_start, range_stop, num_ranges, num_rects, stop; int total_verts, total_rects; cairo_status_t status; extend = cairo_pattern_get_extend (&pattern->base.base); SaveDC (surface->win32.dc); mat = pattern->base.base.matrix; status = cairo_matrix_invert (&mat); /* _cairo_pattern_set_matrix guarantees invertibility */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&mat, &surface->ctm, &mat); p1x = pattern->pd1.x; p1y = pattern->pd1.y; p2x = pattern->pd2.x; p2y = pattern->pd2.y; cairo_matrix_translate (&mat, p1x, p1y); xd = p2x - p1x; yd = p2y - p1y; d = sqrt (xd*xd + yd*yd); sn = yd/d; cs = xd/d; cairo_matrix_init (&rot, cs, sn, -sn, cs, 0, 0); cairo_matrix_multiply (&mat, &rot, &mat); _cairo_matrix_to_win32_xform (&mat, &xform); if (!SetWorldTransform (surface->win32.dc, &xform)) return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); GetClipBox (surface->win32.dc, &clip); if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { range_start = floor (clip.left / d); range_stop = ceil (clip.right / d); } else { range_start = 0; range_stop = 1; } num_ranges = range_stop - range_start; num_stops = pattern->base.n_stops; num_rects = num_stops - 1; /* Add an extra four points and two rectangles for EXTEND_PAD */ vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); for (i = 0; i < num_ranges*num_rects; i++) { vert[i*2].y = (LONG) clip.top; if (i%num_rects == 0) { stop = 0; if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) stop = num_rects; vert[i*2].x = (LONG)(d*(range_start + i/num_rects)); vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color); } else { vert[i*2].x = vert[i*2-1].x; vert[i*2].Red = vert[i*2-1].Red; vert[i*2].Green = vert[i*2-1].Green; vert[i*2].Blue = vert[i*2-1].Blue; vert[i*2].Alpha = vert[i*2-1].Alpha; } stop = i%num_rects + 1; vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset)); vert[i*2+1].y = (LONG) clip.bottom; if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) stop = num_rects - stop; vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color); rect[i].UpperLeft = i*2; rect[i].LowerRight = i*2 + 1; } total_verts = 2*num_ranges*num_rects; total_rects = num_ranges*num_rects; if (extend == CAIRO_EXTEND_PAD) { vert[i*2].x = vert[i*2-1].x; vert[i*2].y = (LONG) clip.top; vert[i*2].Red = vert[i*2-1].Red; vert[i*2].Green = vert[i*2-1].Green; vert[i*2].Blue = vert[i*2-1].Blue; vert[i*2].Alpha = 0xff00; vert[i*2+1].x = clip.right; vert[i*2+1].y = (LONG) clip.bottom; vert[i*2+1].Red = vert[i*2-1].Red; vert[i*2+1].Green = vert[i*2-1].Green; vert[i*2+1].Blue = vert[i*2-1].Blue; vert[i*2+1].Alpha = 0xff00; rect[i].UpperLeft = i*2; rect[i].LowerRight = i*2 + 1; i++; vert[i*2].x = clip.left; vert[i*2].y = (LONG) clip.top; vert[i*2].Red = vert[0].Red; vert[i*2].Green = vert[0].Green; vert[i*2].Blue = vert[0].Blue; vert[i*2].Alpha = 0xff00; vert[i*2+1].x = vert[0].x; vert[i*2+1].y = (LONG) clip.bottom; vert[i*2+1].Red = vert[0].Red; vert[i*2+1].Green = vert[0].Green; vert[i*2+1].Blue = vert[0].Blue; vert[i*2+1].Alpha = 0xff00; rect[i].UpperLeft = i*2; rect[i].LowerRight = i*2 + 1; total_verts += 4; total_rects += 2; } if (!GradientFill (surface->win32.dc, vert, total_verts, rect, total_rects, GRADIENT_FILL_RECT_H)) return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); free (rect); free (vert); RestoreDC (surface->win32.dc, -1); return 0; } static cairo_int_status_t _cairo_win32_printing_surface_paint_pattern (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) { cairo_status_t status; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern); if (status) return status; break; case CAIRO_PATTERN_TYPE_SURFACE: status = _cairo_win32_printing_surface_paint_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); if (status) return status; break; case CAIRO_PATTERN_TYPE_LINEAR: status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); if (status) return status; break; case CAIRO_PATTERN_TYPE_RADIAL: return CAIRO_INT_STATUS_UNSUPPORTED; break; case CAIRO_PATTERN_TYPE_MESH: ASSERT_NOT_REACHED; } return CAIRO_STATUS_SUCCESS; } typedef struct _win32_print_path_info { cairo_win32_printing_surface_t *surface; } win32_path_info_t; static cairo_status_t _cairo_win32_printing_surface_path_move_to (void *closure, const cairo_point_t *point) { win32_path_info_t *path_info = closure; if (path_info->surface->has_ctm) { double x, y; x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); MoveToEx (path_info->surface->win32.dc, (int) x, (int) y, NULL); } else { MoveToEx (path_info->surface->win32.dc, _cairo_fixed_integer_part (point->x), _cairo_fixed_integer_part (point->y), NULL); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_path_line_to (void *closure, const cairo_point_t *point) { win32_path_info_t *path_info = closure; path_info->surface->path_empty = FALSE; if (path_info->surface->has_ctm) { double x, y; x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); LineTo (path_info->surface->win32.dc, (int) x, (int) y); } else { LineTo (path_info->surface->win32.dc, _cairo_fixed_integer_part (point->x), _cairo_fixed_integer_part (point->y)); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_path_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { win32_path_info_t *path_info = closure; POINT points[3]; path_info->surface->path_empty = FALSE; if (path_info->surface->has_ctm) { double x, y; x = _cairo_fixed_to_double (b->x); y = _cairo_fixed_to_double (b->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); points[0].x = (LONG) x; points[0].y = (LONG) y; x = _cairo_fixed_to_double (c->x); y = _cairo_fixed_to_double (c->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); points[1].x = (LONG) x; points[1].y = (LONG) y; x = _cairo_fixed_to_double (d->x); y = _cairo_fixed_to_double (d->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); points[2].x = (LONG) x; points[2].y = (LONG) y; } else { points[0].x = _cairo_fixed_integer_part (b->x); points[0].y = _cairo_fixed_integer_part (b->y); points[1].x = _cairo_fixed_integer_part (c->x); points[1].y = _cairo_fixed_integer_part (c->y); points[2].x = _cairo_fixed_integer_part (d->x); points[2].y = _cairo_fixed_integer_part (d->y); } PolyBezierTo (path_info->surface->win32.dc, points, 3); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_path_close_path (void *closure) { win32_path_info_t *path_info = closure; CloseFigure (path_info->surface->win32.dc); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_emit_path (cairo_win32_printing_surface_t *surface, const cairo_path_fixed_t *path) { win32_path_info_t path_info; path_info.surface = surface; return _cairo_path_fixed_interpret (path, _cairo_win32_printing_surface_path_move_to, _cairo_win32_printing_surface_path_line_to, _cairo_win32_printing_surface_path_curve_to, _cairo_win32_printing_surface_path_close_path, &path_info); } static cairo_int_status_t _cairo_win32_printing_surface_show_page (void *abstract_surface) { cairo_win32_printing_surface_t *surface = abstract_surface; /* Undo both SaveDC's that we did in start_page */ RestoreDC (surface->win32.dc, -2); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias) { cairo_win32_printing_surface_t *surface = cairo_container_of (clipper, cairo_win32_printing_surface_t, clipper); cairo_status_t status; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return CAIRO_STATUS_SUCCESS; if (path == NULL) { RestoreDC (surface->win32.dc, -1); SaveDC (surface->win32.dc); return CAIRO_STATUS_SUCCESS; } BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); EndPath (surface->win32.dc); switch (fill_rule) { case CAIRO_FILL_RULE_WINDING: SetPolyFillMode (surface->win32.dc, WINDING); break; case CAIRO_FILL_RULE_EVEN_ODD: SetPolyFillMode (surface->win32.dc, ALTERNATE); break; default: ASSERT_NOT_REACHED; } SelectClipPath (surface->win32.dc, RGN_AND); return status; } static void _cairo_win32_printing_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { _cairo_font_options_init_default (options); cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } static cairo_int_status_t _cairo_win32_printing_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_win32_printing_surface_t *surface = abstract_surface; cairo_solid_pattern_t clear; cairo_status_t status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (status) return status; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; op = CAIRO_OPERATOR_SOURCE; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_win32_printing_surface_analyze_operation (surface, op, source); assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); return _cairo_win32_printing_surface_paint_pattern (surface, source); } static int _cairo_win32_line_cap (cairo_line_cap_t cap) { switch (cap) { case CAIRO_LINE_CAP_BUTT: return PS_ENDCAP_FLAT; case CAIRO_LINE_CAP_ROUND: return PS_ENDCAP_ROUND; case CAIRO_LINE_CAP_SQUARE: return PS_ENDCAP_SQUARE; default: ASSERT_NOT_REACHED; return 0; } } static int _cairo_win32_line_join (cairo_line_join_t join) { switch (join) { case CAIRO_LINE_JOIN_MITER: return PS_JOIN_MITER; case CAIRO_LINE_JOIN_ROUND: return PS_JOIN_ROUND; case CAIRO_LINE_JOIN_BEVEL: return PS_JOIN_BEVEL; default: ASSERT_NOT_REACHED; return 0; } } static void _cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) { double s; s = fabs (m->xx); if (fabs (m->xy) > s) s = fabs (m->xy); if (fabs (m->yx) > s) s = fabs (m->yx); if (fabs (m->yy) > s) s = fabs (m->yy); *scale = s; s = 1.0/s; cairo_matrix_scale (m, s, s); } static cairo_int_status_t _cairo_win32_printing_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_printing_surface_t *surface = abstract_surface; cairo_int_status_t status; HPEN pen; LOGBRUSH brush; COLORREF color; XFORM xform; DWORD pen_style; DWORD *dash_array; HGDIOBJ obj; unsigned int i; cairo_solid_pattern_t clear; cairo_matrix_t mat; double scale; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (status) return status; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; op = CAIRO_OPERATOR_SOURCE; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { /* Win32 does not support a dash offset. */ if (style->num_dashes > 0 && style->dash_offset != 0.0) return CAIRO_INT_STATUS_UNSUPPORTED; return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); _cairo_matrix_factor_out_scale (&mat, &scale); pen_style = PS_GEOMETRIC; dash_array = NULL; if (style->num_dashes) { pen_style |= PS_USERSTYLE; dash_array = calloc (sizeof (DWORD), style->num_dashes); for (i = 0; i < style->num_dashes; i++) { dash_array[i] = (DWORD) (scale * style->dash[i]); } } else { pen_style |= PS_SOLID; } SetMiterLimit (surface->win32.dc, (FLOAT) (style->miter_limit), NULL); if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; color = _cairo_win32_printing_surface_flatten_transparency (surface, &solid->color); } else { /* Color not used as the pen will only be used by WidenPath() */ color = RGB (0,0,0); } brush.lbStyle = BS_SOLID; brush.lbColor = color; brush.lbHatch = 0; pen_style |= _cairo_win32_line_cap (style->line_cap); pen_style |= _cairo_win32_line_join (style->line_join); pen = ExtCreatePen(pen_style, scale * style->line_width, &brush, style->num_dashes, dash_array); if (pen == NULL) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); obj = SelectObject (surface->win32.dc, pen); if (obj == NULL) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); EndPath (surface->win32.dc); if (status) return status; /* * Switch to user space to set line parameters */ SaveDC (surface->win32.dc); _cairo_matrix_to_win32_xform (&mat, &xform); xform.eDx = 0.0f; xform.eDy = 0.0f; if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); if (source->type == CAIRO_PATTERN_TYPE_SOLID) { StrokePath (surface->win32.dc); } else { if (!WidenPath (surface->win32.dc)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); if (!SelectClipPath (surface->win32.dc, RGN_AND)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); /* Return to device space to paint the pattern */ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); if (!SetWorldTransform (surface->win32.dc, &xform)) return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); status = _cairo_win32_printing_surface_paint_pattern (surface, source); } RestoreDC (surface->win32.dc, -1); DeleteObject (pen); free (dash_array); return status; } static cairo_int_status_t _cairo_win32_printing_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_printing_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_solid_pattern_t clear; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (status) return status; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; op = CAIRO_OPERATOR_SOURCE; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _cairo_win32_printing_surface_analyze_operation (surface, op, source); assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); surface->path_empty = TRUE; BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); EndPath (surface->win32.dc); switch (fill_rule) { case CAIRO_FILL_RULE_WINDING: SetPolyFillMode (surface->win32.dc, WINDING); break; case CAIRO_FILL_RULE_EVEN_ODD: SetPolyFillMode (surface->win32.dc, ALTERNATE); break; default: ASSERT_NOT_REACHED; } if (source->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_win32_printing_surface_select_solid_brush (surface, source); if (status) return status; FillPath (surface->win32.dc); _cairo_win32_printing_surface_done_solid_brush (surface); } else if (surface->path_empty == FALSE) { SaveDC (surface->win32.dc); SelectClipPath (surface->win32.dc, RGN_AND); status = _cairo_win32_printing_surface_paint_pattern (surface, source); RestoreDC (surface->win32.dc, -1); } fflush(stderr); return status; } static cairo_int_status_t _cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_printing_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_matrix_t ctm; cairo_glyph_t *unicode_glyphs; cairo_scaled_font_subsets_glyph_t subset_glyph; int i, first; cairo_bool_t sequence_is_unicode; cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Where possible reverse the glyph indices back to unicode * characters. Strings of glyphs that could not be reversed to * unicode will be printed with ETO_GLYPH_INDEX. * * As _cairo_win32_scaled_font_index_to_ucs4() is a slow * operation, the font subsetting function * _cairo_scaled_font_subsets_map_glyph() is used to obtain * the unicode value because it caches the reverse mapping in * the subsets. */ if (surface->has_ctm) { for (i = 0; i < num_glyphs; i++) cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); scaled_font = cairo_scaled_font_create (scaled_font->font_face, &scaled_font->font_matrix, &ctm, &scaled_font->options); } unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unicode_glyphs == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, scaled_font, glyphs[i].index, NULL, 0, &subset_glyph); if (status) goto fail; unicode_glyphs[i].index = subset_glyph.unicode; } i = 0; first = 0; sequence_is_unicode = unicode_glyphs[0].index <= 0xffff; while (i < num_glyphs) { if (i == num_glyphs - 1 || ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode)) { status = _cairo_win32_surface_emit_glyphs (&surface->win32, source, sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], i - first + 1, scaled_font, ! sequence_is_unicode); first = i + 1; if (i < num_glyphs - 1) sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff; } i++; } fail: if (surface->has_ctm) cairo_scaled_font_destroy (scaled_font); free (unicode_glyphs); return status; } static cairo_int_status_t _cairo_win32_printing_surface_show_glyphs (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_win32_printing_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_pattern_t *opaque = NULL; int i; cairo_matrix_t old_ctm; cairo_bool_t old_has_ctm; cairo_solid_pattern_t clear; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (status) return status; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; op = CAIRO_OPERATOR_SOURCE; } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { /* When printing bitmap fonts to a printer DC, Windows may * substitute an outline font for bitmap font. As the win32 * font backend always uses a screen DC when obtaining the * font metrics the metrics of the substituted font will not * match the metrics that the win32 font backend returns. * * If we are printing a bitmap font, use fallback images to * ensure the font is not substituted. */ #if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; else return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } #endif /* For non win32 fonts we need to check that each glyph has a * path available. If a path is not available, * _cairo_scaled_glyph_lookup() will return * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be * used. */ for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (status) return status; } return _cairo_win32_printing_surface_analyze_operation (surface, op, source); } if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; COLORREF color; color = _cairo_win32_printing_surface_flatten_transparency (surface, &solid->color); opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, GetGValue (color) / 255.0, GetBValue (color) / 255.0); if (opaque->status) return opaque->status; source = opaque; } #if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && source->type == CAIRO_PATTERN_TYPE_SOLID) { return _cairo_win32_printing_surface_emit_win32_glyphs (surface, op, source, glyphs, num_glyphs, scaled_font, clip); } #endif SaveDC (surface->win32.dc); old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; surface->has_ctm = TRUE; surface->path_empty = TRUE; BeginPath (surface->win32.dc); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (status) break; surface->ctm = old_ctm; cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); } EndPath (surface->win32.dc); surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) { if (source->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_win32_printing_surface_select_solid_brush (surface, source); if (status) return status; SetPolyFillMode (surface->win32.dc, WINDING); FillPath (surface->win32.dc); _cairo_win32_printing_surface_done_solid_brush (surface); } else { SelectClipPath (surface->win32.dc, RGN_AND); status = _cairo_win32_printing_surface_paint_pattern (surface, source); } } RestoreDC (surface->win32.dc, -1); if (opaque) cairo_pattern_destroy (opaque); return status; } static const char ** _cairo_win32_printing_surface_get_supported_mime_types (void *abstract_surface) { return _cairo_win32_printing_supported_mime_types; } static cairo_status_t _cairo_win32_printing_surface_finish (void *abstract_surface) { cairo_win32_printing_surface_t *surface = abstract_surface; if (surface->font_subsets != NULL) _cairo_scaled_font_subsets_destroy (surface->font_subsets); return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * _cairo_win32_printing_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, int height) { cairo_rectangle_t extents; extents.x = extents.y = 0; extents.width = width; extents.height = height; return cairo_recording_surface_create (content, &extents); } static cairo_int_status_t _cairo_win32_printing_surface_start_page (void *abstract_surface) { cairo_win32_printing_surface_t *surface = abstract_surface; XFORM xform; double x_res, y_res; cairo_matrix_t inverse_ctm; cairo_status_t status; SaveDC (surface->win32.dc); /* Save application context first, before doing MWT */ /* As the logical coordinates used by GDI functions (eg LineTo) * are integers we need to do some additional work to prevent * rounding errors. For example the obvious way to paint a recording * pattern is to: * * SaveDC() * transform the device context DC by the pattern to device matrix * replay the recording surface * RestoreDC() * * The problem here is that if the pattern to device matrix is * [100 0 0 100 0 0], coordinates in the recording pattern such as * (1.56, 2.23) which correspond to (156, 223) in device space * will be rounded to (100, 200) due to (1.56, 2.23) being * truncated to integers. * * This is solved by saving the current GDI CTM in surface->ctm, * switch the GDI CTM to identity, and transforming all * coordinates by surface->ctm before passing them to GDI. When * painting a recording pattern, surface->ctm is transformed by the * pattern to device matrix. * * For printing device contexts where 1 unit is 1 dpi, switching * the GDI CTM to identity maximises the possible resolution of * coordinates. * * If the device context is an EMF file, using an identity * transform often provides insufficent resolution. The workaround * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] * and scale the cairo CTM by [16 0 0 16 0 0]. The * SetWorldTransform function call to scale the GDI CTM by 1.0/16 * will be recorded in the EMF followed by all the graphics * functions by their coordinateds multiplied by 16. * * To support allowing the user to set a GDI CTM with scale < 1, * we avoid switching to an identity CTM if the CTM xx and yy is < 1. */ SetGraphicsMode (surface->win32.dc, GM_ADVANCED); GetWorldTransform(surface->win32.dc, &xform); if (xform.eM11 < 1 && xform.eM22 < 1) { cairo_matrix_init_identity (&surface->ctm); surface->gdi_ctm.xx = xform.eM11; surface->gdi_ctm.xy = xform.eM21; surface->gdi_ctm.yx = xform.eM12; surface->gdi_ctm.yy = xform.eM22; surface->gdi_ctm.x0 = xform.eDx; surface->gdi_ctm.y0 = xform.eDy; } else { surface->ctm.xx = xform.eM11; surface->ctm.xy = xform.eM21; surface->ctm.yx = xform.eM12; surface->ctm.yy = xform.eM22; surface->ctm.x0 = xform.eDx; surface->ctm.y0 = xform.eDy; cairo_matrix_init_identity (&surface->gdi_ctm); if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); } surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); inverse_ctm = surface->ctm; status = cairo_matrix_invert (&inverse_ctm); if (status) return status; x_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSX); y_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSY); cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); _cairo_surface_set_resolution (&surface->win32.base, x_res, y_res); SaveDC (surface->win32.dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ return CAIRO_STATUS_SUCCESS; } static void _cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_win32_printing_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; } static cairo_bool_t _cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface) { return TRUE; } /** * cairo_win32_printing_surface_create: * @hdc: the DC to create a surface for * * Creates a cairo surface that targets the given DC. The DC will be * queried for its initial clip extents, and this will be used as the * size of the cairo surface. The DC should be a printing DC; * antialiasing will be ignored, and GDI will be used as much as * possible to draw to the surface. * * The returned surface will be wrapped using the paginated surface to * provide correct complex rendering behaviour; cairo_surface_show_page() and * associated methods must be used for correct output. * * Return value: the newly created surface * * Since: 1.6 **/ cairo_surface_t * cairo_win32_printing_surface_create (HDC hdc) { cairo_win32_printing_surface_t *surface; cairo_surface_t *paginated; RECT rect; surface = malloc (sizeof (cairo_win32_printing_surface_t)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); #if 0 if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } #endif _cairo_surface_clipper_init (&surface->clipper, _cairo_win32_printing_surface_clipper_intersect_clip_path); surface->win32.format = CAIRO_FORMAT_RGB24; surface->win32.base.content = CAIRO_CONTENT_COLOR_ALPHA; surface->win32.dc = hdc; surface->brush = NULL; surface->old_brush = NULL; surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); if (surface->font_subsets == NULL) { free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } GetClipBox(hdc, &rect); surface->win32.extents.x = rect.left; surface->win32.extents.y = rect.top; surface->win32.extents.width = rect.right - rect.left; surface->win32.extents.height = rect.bottom - rect.top; surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc); surface->win32.flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; _cairo_win32_printing_surface_init_ps_mode (surface); _cairo_win32_printing_surface_init_image_support (surface); _cairo_win32_printing_surface_init_language_pack (surface); _cairo_surface_init (&surface->win32.base, &cairo_win32_printing_surface_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); paginated = _cairo_paginated_surface_create (&surface->win32.base, CAIRO_CONTENT_COLOR_ALPHA, &cairo_win32_surface_paginated_backend); /* paginated keeps the only reference to surface now, drop ours */ cairo_surface_destroy (&surface->win32.base); return paginated; } static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { CAIRO_SURFACE_TYPE_WIN32_PRINTING, _cairo_win32_printing_surface_finish, _cairo_default_context_create, _cairo_win32_printing_surface_create_similar, NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image */ _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ NULL, /* snapshot */ NULL, /* copy_page */ _cairo_win32_printing_surface_show_page, _cairo_win32_surface_get_extents, _cairo_win32_printing_surface_get_font_options, NULL, /* flush */ NULL, /* mark_dirty_rectangle */ _cairo_win32_printing_surface_paint, NULL, /* mask */ _cairo_win32_printing_surface_stroke, _cairo_win32_printing_surface_fill, NULL, /* fill/stroke */ _cairo_win32_printing_surface_show_glyphs, NULL, /* has_show_text_glyphs */ NULL, /* show_text_glyphs */ _cairo_win32_printing_surface_get_supported_mime_types, }; static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { _cairo_win32_printing_surface_start_page, _cairo_win32_printing_surface_set_paginated_mode, NULL, /* set_bounding_box */ NULL, /* _cairo_win32_printing_surface_has_fallback_images, */ _cairo_win32_printing_surface_supports_fine_grained_fallbacks, }; Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-private.h000066400000000000000000000157451271037650300271340ustar00rootroot00000000000000/* cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor */ #ifndef CAIRO_WIN32_PRIVATE_H #define CAIRO_WIN32_PRIVATE_H #include "cairo-win32.h" #include "cairoint.h" #include "cairo-device-private.h" #include "cairo-surface-clipper-private.h" #include "cairo-surface-private.h" #ifndef SHADEBLENDCAPS #define SHADEBLENDCAPS 120 #endif #ifndef SB_NONE #define SB_NONE 0 #endif #define WIN32_FONT_LOGICAL_SCALE 32 /* Surface DC flag values */ enum { /* If this is a surface created for printing or not */ CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0), /* Whether the DC is a display DC or not */ CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1), /* Whether we can use BitBlt with this surface */ CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2), /* Whether we can use AlphaBlend with this surface */ CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3), /* Whether we can use StretchBlt with this surface */ CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4), /* Whether we can use StretchDIBits with this surface */ CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5), /* Whether we can use GradientFill rectangles with this surface */ CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), /* Whether we can use the CHECKJPEGFORMAT escape function */ CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), /* Whether we can use the CHECKJPEGFORMAT escape function */ CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), }; typedef struct _cairo_win32_surface { cairo_surface_t base; cairo_format_t format; HDC dc; /* Surface DC flags */ unsigned flags; /* We use the x and y parts of extents for situations where * we're not supposed to draw to the entire surface. * For example, during a paint event a program will get * a DC that has been clipped to the dirty region. * A cairo surface constructed for that DC will have extents * that match bounds of the clipped region. */ cairo_rectangle_int_t extents; } cairo_win32_surface_t; #define to_win32_surface(S) ((cairo_win32_surface_t *)(S)) typedef struct _cairo_win32_display_surface { cairo_win32_surface_t win32; /* We create off-screen surfaces as DIBs or DDBs, based on what we created * originally*/ HBITMAP bitmap; cairo_bool_t is_dib; /* Used to save the initial 1x1 monochrome bitmap for the DC to * select back into the DC before deleting the DC and our * bitmap. For Windows XP, this doesn't seem to be necessary * ... we can just delete the DC and that automatically unselects * out bitmap. But it's standard practice so apparently is needed * on some versions of Windows. */ HBITMAP saved_dc_bitmap; cairo_surface_t *image; cairo_surface_t *fallback; HRGN initial_clip_rgn; cairo_bool_t had_simple_clip; } cairo_win32_display_surface_t; #define to_win32_display_surface(S) ((cairo_win32_display_surface_t *)(S)) typedef struct _cairo_win32_printing_surface { cairo_win32_surface_t win32; cairo_surface_clipper_t clipper; cairo_paginated_mode_t paginated_mode; cairo_content_t content; cairo_bool_t path_empty; cairo_bool_t has_ctm; cairo_matrix_t ctm; cairo_bool_t has_gdi_ctm; cairo_matrix_t gdi_ctm; HBRUSH brush, old_brush; cairo_scaled_font_subsets_t *font_subsets; } cairo_win32_printing_surface_t; #define to_win32_printing_surface(S) ((cairo_win32_printing_surface_t *)(S)) typedef BOOL (WINAPI *cairo_win32_alpha_blend_func_t) (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction); typedef struct _cairo_win32_device { cairo_device_t base; HMODULE msimg32_dll; const cairo_compositor_t *compositor; cairo_win32_alpha_blend_func_t alpha_blend; } cairo_win32_device_t; #define to_win32_device(D) ((cairo_win32_device_t *)(D)) #define to_win32_device_from_surface(S) to_win32_device(((cairo_surface_t *)(S))->device) cairo_private cairo_device_t * _cairo_win32_device_get (void); const cairo_compositor_t * _cairo_win32_gdi_compositor_get (void); cairo_status_t _cairo_win32_print_gdi_error (const char *context); cairo_private void _cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface); cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); uint32_t _cairo_win32_flags_for_dc (HDC dc); cairo_int_status_t _cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_bool_t glyph_indexing); static inline void _cairo_matrix_to_win32_xform (const cairo_matrix_t *m, XFORM *xform) { xform->eM11 = (FLOAT) m->xx; xform->eM21 = (FLOAT) m->xy; xform->eM12 = (FLOAT) m->yx; xform->eM22 = (FLOAT) m->yy; xform->eDx = (FLOAT) m->x0; xform->eDy = (FLOAT) m->y0; } cairo_status_t _cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, cairo_clip_t *clip); void _cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface); void _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); cairo_bool_t _cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); cairo_bool_t _cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); #endif /* CAIRO_WIN32_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-surface.c000066400000000000000000000236561271037650300271050ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" #include "cairo-paginated-private.h" #include "cairo-pattern-private.h" #include "cairo-win32-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-fallback-private.h" #include "cairo-surface-backend-private.h" #include #include #if defined(__MINGW32__) && !defined(ETO_PDY) # define ETO_PDY 0x2000 #endif /** * SECTION:cairo-win32 * @Title: Win32 Surfaces * @Short_Description: Microsoft Windows surface support * @See_Also: #cairo_surface_t * * The Microsoft Windows surface is used to render cairo graphics to * Microsoft Windows windows, bitmaps, and printing device contexts. * * The surface returned by cairo_win32_printing_surface_create() is of surface * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface * type. * * The surface returned by the other win32 constructors is of surface type * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. **/ /** * CAIRO_HAS_WIN32_SURFACE: * * Defined if the Microsoft Windows surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.0 **/ /** * _cairo_win32_print_gdi_error: * @context: context string to display along with the error * * Helper function to dump out a human readable form of the * current error code. * * Return value: A cairo status code for the error code **/ cairo_status_t _cairo_win32_print_gdi_error (const char *context) { void *lpMsgBuf; DWORD last_error = GetLastError (); if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &lpMsgBuf, 0, NULL)) { fprintf (stderr, "%s: Unknown GDI error", context); } else { fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); LocalFree (lpMsgBuf); } fflush (stderr); /* We should switch off of last_status, but we'd either return * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there * is no CAIRO_STATUS_UNKNOWN_ERROR. */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_win32_surface_t *surface = abstract_surface; *rectangle = surface->extents; return TRUE; } /** * cairo_win32_surface_get_dc: * @surface: a #cairo_surface_t * * Returns the HDC associated with this surface, or %NULL if none. * Also returns %NULL if the surface is not a win32 surface. * * A call to cairo_surface_flush() is required before using the HDC to * ensure that all pending drawing operations are finished and to * restore any temporary modification cairo has made to its state. A * call to cairo_surface_mark_dirty() is required after the state or * the content of the HDC has been modified. * * Return value: HDC or %NULL if no HDC available. * * Since: 1.2 **/ HDC cairo_win32_surface_get_dc (cairo_surface_t *surface) { if (surface->backend->type == CAIRO_SURFACE_TYPE_WIN32) return to_win32_surface(surface)->dc; if (_cairo_surface_is_paginated (surface)) { cairo_surface_t *target = _cairo_paginated_surface_get_target (surface); if (target->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) return to_win32_surface(target)->dc; } return NULL; } /** * cairo_win32_surface_get_image: * @surface: a #cairo_surface_t * * Returns a #cairo_surface_t image surface that refers to the same bits * as the DIB of the Win32 surface. If the passed-in win32 surface * is not a DIB surface, %NULL is returned. * * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), * or %NULL if the win32 surface is not a DIB. * * Since: 1.4 **/ cairo_surface_t * cairo_win32_surface_get_image (cairo_surface_t *surface) { if (surface->backend->type != CAIRO_SURFACE_TYPE_WIN32) return NULL; GdiFlush(); return to_win32_display_surface(surface)->image; } #define STACK_GLYPH_SIZE 256 cairo_int_status_t _cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, cairo_bool_t glyph_indexing) { #if CAIRO_HAS_WIN32_FONT WORD glyph_buf_stack[STACK_GLYPH_SIZE]; WORD *glyph_buf = glyph_buf_stack; int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; int *dxy_buf = dxy_buf_stack; BOOL win_result = 0; int i, j; cairo_solid_pattern_t *solid_pattern; COLORREF color; cairo_matrix_t device_to_logical; int start_x, start_y; double user_x, user_y; int logical_x, logical_y; unsigned int glyph_index_option; /* We can only handle win32 fonts */ assert (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32); /* We can only handle opaque solid color sources and destinations */ assert (_cairo_pattern_is_opaque_solid(source)); assert (dst->format == CAIRO_FORMAT_RGB24); solid_pattern = (cairo_solid_pattern_t *)source; color = RGB(((int)solid_pattern->color.red_short) >> 8, ((int)solid_pattern->color.green_short) >> 8, ((int)solid_pattern->color.blue_short) >> 8); cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); SaveDC(dst->dc); cairo_win32_scaled_font_select_font(scaled_font, dst->dc); SetTextColor(dst->dc, color); SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); SetBkMode(dst->dc, TRANSPARENT); if (num_glyphs > STACK_GLYPH_SIZE) { glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); } /* It is vital that dx values for dxy_buf are calculated from the delta of * _logical_ x coordinates (not user x coordinates) or else the sum of all * previous dx values may start to diverge from the current glyph's x * coordinate due to accumulated rounding error. As a result strings could * be painted shorter or longer than expected. */ user_x = glyphs[0].x; user_y = glyphs[0].y; cairo_matrix_transform_point(&device_to_logical, &user_x, &user_y); logical_x = _cairo_lround (user_x); logical_y = _cairo_lround (user_y); start_x = logical_x; start_y = logical_y; for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { glyph_buf[i] = (WORD) glyphs[i].index; if (i == num_glyphs - 1) { dxy_buf[j] = 0; dxy_buf[j+1] = 0; } else { double next_user_x = glyphs[i+1].x; double next_user_y = glyphs[i+1].y; int next_logical_x, next_logical_y; cairo_matrix_transform_point(&device_to_logical, &next_user_x, &next_user_y); next_logical_x = _cairo_lround (next_user_x); next_logical_y = _cairo_lround (next_user_y); dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y); logical_x = next_logical_x; logical_y = next_logical_y; } } if (glyph_indexing) glyph_index_option = ETO_GLYPH_INDEX; else glyph_index_option = 0; win_result = ExtTextOutW(dst->dc, start_x, start_y, glyph_index_option | ETO_PDY, NULL, glyph_buf, num_glyphs, dxy_buf); if (!win_result) { _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); } RestoreDC(dst->dc, -1); if (glyph_buf != glyph_buf_stack) { free(glyph_buf); free(dxy_buf); } return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; #else return CAIRO_INT_STATUS_UNSUPPORTED; #endif } #undef STACK_GLYPH_SIZE Indigo-indigo-1.2.3/third_party/cairo-src/cairo/src/win32/cairo-win32-system.c000066400000000000000000000054171271037650300267740ustar00rootroot00000000000000/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ /* This file should include code that is system-specific, not * feature-specific. For example, the DLL initialization/finalization * code on Win32 or OS/2 must live here (not in cairo-whatever-surface.c). * Same about possible ELF-specific code. * * And no other function should live here. */ #include "cairoint.h" #if CAIRO_MUTEX_IMPL_WIN32 #if !CAIRO_WIN32_STATIC_BUILD #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include /* declare to avoid "no previous prototype for 'DllMain'" warning */ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: CAIRO_MUTEX_INITIALIZE (); break; case DLL_PROCESS_DETACH: CAIRO_MUTEX_FINALIZE (); break; } return TRUE; } #endif #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/000077500000000000000000000000001271037650300217105ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/cairo-src/pixman/CMakeLists.txt000066400000000000000000000034761271037650300244620ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(Pixman C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake/) if (USE_SYSTEM_PIXMAN) find_package(PIXMAN) if (NOT PIXMAN_FOUND) MESSAGE(FATAL_ERROR "Cannot find system pixman library") endif() endif() if (NOT PIXMAN_FOUND) message(STATUS "Using local Pixman library") set(Pixman_headers_dir ${CMAKE_CURRENT_SOURCE_DIR}/pixman) include_directories(${Pixman_headers_dir}) add_definitions(-DPACKAGE -DHAVE_PTHREAD_SETSPECIFIC) file (GLOB Pixman_src "pixman/*.c") file (GLOB Pixman_headers "pixman/*.h") list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-arm-neon.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-arm-simd.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-mips-dspr2.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-vmx.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-mmx.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-sse2.c) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-region.c) if (APPLE) list(REMOVE_ITEM Pixman_src ${CMAKE_CURRENT_SOURCE_DIR}/pixman/pixman-timer.c) endif() include_directories(${PNG_INCLUDE_DIR}) add_library(pixman STATIC ${Pixman_src} ${Pixman_headers}) if(NOT PNG_FOUND) target_link_libraries(pixman png) else() target_link_libraries(pixman ${PNG_LIBRARIES}) endif() set_target_properties(pixman PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET pixman PROPERTY FOLDER "third_party") if (NOT NO_STATIC) pack_static(pixman) endif() else() set(Pixman_headers_dir ${PIXMAN_INCLUDE_DIRS}) endif() Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/000077500000000000000000000000001271037650300232045ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/loongson-mmintrin.h000066400000000000000000000235531271037650300270560ustar00rootroot00000000000000/* The gcc-provided loongson intrinsic functions are way too fucking broken * to be of any use, otherwise I'd use them. * * - The hardware instructions are very similar to MMX or iwMMXt. Certainly * close enough that they could have implemented the _mm_*-style intrinsic * interface and had a ton of optimized code available to them. Instead they * implemented something much, much worse. * * - pshuf takes a dead first argument, causing extra instructions to be * generated. * * - There are no 64-bit shift or logical intrinsics, which means you have * to implement them with inline assembly, but this is a nightmare because * gcc doesn't understand that the integer vector datatypes are actually in * floating-point registers, so you end up with braindead code like * * punpcklwd $f9,$f9,$f5 * dmtc1 v0,$f8 * punpcklwd $f19,$f19,$f5 * dmfc1 t9,$f9 * dmtc1 v0,$f9 * dmtc1 t9,$f20 * dmfc1 s0,$f19 * punpcklbh $f20,$f20,$f2 * * where crap just gets copied back and forth between integer and floating- * point registers ad nauseum. * * Instead of trying to workaround the problems from these crap intrinsics, I * just implement the _mm_* intrinsics needed for pixman-mmx.c using inline * assembly. */ #include /* vectors are stored in 64-bit floating-point registers */ typedef double __m64; /* having a 32-bit datatype allows us to use 32-bit loads in places like load8888 */ typedef float __m32; extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_setzero_si64 (void) { return 0.0; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_add_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("paddh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_add_pi32 (__m64 __m1, __m64 __m2) { __m64 ret; asm("paddw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_adds_pu16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("paddush %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_adds_pu8 (__m64 __m1, __m64 __m2) { __m64 ret; asm("paddusb %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_and_si64 (__m64 __m1, __m64 __m2) { __m64 ret; asm("and %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_cmpeq_pi32 (__m64 __m1, __m64 __m2) { __m64 ret; asm("pcmpeqw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_empty (void) { } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_madd_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("pmaddhw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_mulhi_pu16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("pmulhuh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_mullo_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("pmullh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_or_si64 (__m64 __m1, __m64 __m2) { __m64 ret; asm("or %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_packs_pu16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("packushb %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_packs_pi32 (__m64 __m1, __m64 __m2) { __m64 ret; asm("packsswh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } #define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0)) extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_set_pi16 (uint16_t __w3, uint16_t __w2, uint16_t __w1, uint16_t __w0) { if (__builtin_constant_p (__w3) && __builtin_constant_p (__w2) && __builtin_constant_p (__w1) && __builtin_constant_p (__w0)) { uint64_t val = ((uint64_t)__w3 << 48) | ((uint64_t)__w2 << 32) | ((uint64_t)__w1 << 16) | ((uint64_t)__w0 << 0); return *(__m64 *)&val; } else if (__w3 == __w2 && __w2 == __w1 && __w1 == __w0) { /* TODO: handle other cases */ uint64_t val = __w3; uint64_t imm = _MM_SHUFFLE (0, 0, 0, 0); __m64 ret; asm("pshufh %0, %1, %2\n\t" : "=f" (ret) : "f" (*(__m64 *)&val), "f" (*(__m64 *)&imm) ); return ret; } uint64_t val = ((uint64_t)__w3 << 48) | ((uint64_t)__w2 << 32) | ((uint64_t)__w1 << 16) | ((uint64_t)__w0 << 0); return *(__m64 *)&val; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_set_pi32 (unsigned __i1, unsigned __i0) { if (__builtin_constant_p (__i1) && __builtin_constant_p (__i0)) { uint64_t val = ((uint64_t)__i1 << 32) | ((uint64_t)__i0 << 0); return *(__m64 *)&val; } else if (__i1 == __i0) { uint64_t imm = _MM_SHUFFLE (1, 0, 1, 0); __m64 ret; asm("pshufh %0, %1, %2\n\t" : "=f" (ret) : "f" (*(__m32 *)&__i1), "f" (*(__m64 *)&imm) ); return ret; } uint64_t val = ((uint64_t)__i1 << 32) | ((uint64_t)__i0 << 0); return *(__m64 *)&val; } #undef _MM_SHUFFLE extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_shuffle_pi16 (__m64 __m, int64_t __n) { __m64 ret; asm("pshufh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__n) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_slli_pi16 (__m64 __m, int64_t __count) { __m64 ret; asm("psllh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__count) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_slli_si64 (__m64 __m, int64_t __count) { __m64 ret; asm("dsll %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__count) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_srli_pi16 (__m64 __m, int64_t __count) { __m64 ret; asm("psrlh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__count) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_srli_pi32 (__m64 __m, int64_t __count) { __m64 ret; asm("psrlw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__count) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_srli_si64 (__m64 __m, int64_t __count) { __m64 ret; asm("dsrl %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__count) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_sub_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("psubh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_unpackhi_pi8 (__m64 __m1, __m64 __m2) { __m64 ret; asm("punpckhbh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_unpackhi_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("punpckhhw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_unpacklo_pi8 (__m64 __m1, __m64 __m2) { __m64 ret; asm("punpcklbh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } /* Since punpcklbh doesn't care about the high 32-bits, we use the __m32 datatype which * allows load8888 to use 32-bit loads */ extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_unpacklo_pi8_f (__m32 __m1, __m64 __m2) { __m64 ret; asm("punpcklbh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_unpacklo_pi16 (__m64 __m1, __m64 __m2) { __m64 ret; asm("punpcklhw %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_xor_si64 (__m64 __m1, __m64 __m2) { __m64 ret; asm("xor %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) loongson_extract_pi16 (__m64 __m, int64_t __pos) { __m64 ret; asm("pextrh %0, %1, %2\n\t" : "=f" (ret) : "f" (__m), "f" (*(__m64 *)&__pos) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) loongson_insert_pi16 (__m64 __m1, __m64 __m2, int64_t __pos) { __m64 ret; asm("pinsrh_%3 %0, %1, %2\n\t" : "=f" (ret) : "f" (__m1), "f" (__m2), "i" (__pos) ); return ret; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-access-accessors.c000066400000000000000000000000701271037650300300630ustar00rootroot00000000000000#define PIXMAN_FB_ACCESSORS #include "pixman-access.c" Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-access.c000066400000000000000000001200521271037650300261030ustar00rootroot00000000000000/* * * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * 2008 Aaron Plattner, NVIDIA Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "pixman-accessor.h" #include "pixman-private.h" #define CONVERT_RGB24_TO_Y15(s) \ (((((s) >> 16) & 0xff) * 153 + \ (((s) >> 8) & 0xff) * 301 + \ (((s) ) & 0xff) * 58) >> 2) #define CONVERT_RGB24_TO_RGB15(s) \ ((((s) >> 3) & 0x001f) | \ (((s) >> 6) & 0x03e0) | \ (((s) >> 9) & 0x7c00)) /* Fetch macros */ #ifdef WORDS_BIGENDIAN #define FETCH_1(img,l,o) \ (((READ ((img), ((uint32_t *)(l)) + ((o) >> 5))) >> (0x1f - ((o) & 0x1f))) & 0x1) #else #define FETCH_1(img,l,o) \ ((((READ ((img), ((uint32_t *)(l)) + ((o) >> 5))) >> ((o) & 0x1f))) & 0x1) #endif #define FETCH_8(img,l,o) (READ (img, (((uint8_t *)(l)) + ((o) >> 3)))) #ifdef WORDS_BIGENDIAN #define FETCH_4(img,l,o) \ (((4 * (o)) & 4) ? (FETCH_8 (img,l, 4 * (o)) & 0xf) : (FETCH_8 (img,l,(4 * (o))) >> 4)) #else #define FETCH_4(img,l,o) \ (((4 * (o)) & 4) ? (FETCH_8 (img, l, 4 * (o)) >> 4) : (FETCH_8 (img, l, (4 * (o))) & 0xf)) #endif #ifdef WORDS_BIGENDIAN #define FETCH_24(img,l,o) \ ((READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 16) | \ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 2)) << 0)) #else #define FETCH_24(img,l,o) \ ((READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 0) | \ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 2)) << 16)) #endif /* Store macros */ #ifdef WORDS_BIGENDIAN #define STORE_1(img,l,o,v) \ do \ { \ uint32_t *__d = ((uint32_t *)(l)) + ((o) >> 5); \ uint32_t __m, __v; \ \ __m = 1 << (0x1f - ((o) & 0x1f)); \ __v = (v)? __m : 0; \ \ WRITE((img), __d, (READ((img), __d) & ~__m) | __v); \ } \ while (0) #else #define STORE_1(img,l,o,v) \ do \ { \ uint32_t *__d = ((uint32_t *)(l)) + ((o) >> 5); \ uint32_t __m, __v; \ \ __m = 1 << ((o) & 0x1f); \ __v = (v)? __m : 0; \ \ WRITE((img), __d, (READ((img), __d) & ~__m) | __v); \ } \ while (0) #endif #define STORE_8(img,l,o,v) (WRITE (img, (uint8_t *)(l) + ((o) >> 3), (v))) #ifdef WORDS_BIGENDIAN #define STORE_4(img,l,o,v) \ do \ { \ int bo = 4 * (o); \ int v4 = (v) & 0x0f; \ \ STORE_8 (img, l, bo, ( \ bo & 4 ? \ (FETCH_8 (img, l, bo) & 0xf0) | (v4) : \ (FETCH_8 (img, l, bo) & 0x0f) | (v4 << 4))); \ } while (0) #else #define STORE_4(img,l,o,v) \ do \ { \ int bo = 4 * (o); \ int v4 = (v) & 0x0f; \ \ STORE_8 (img, l, bo, ( \ bo & 4 ? \ (FETCH_8 (img, l, bo) & 0x0f) | (v4 << 4) : \ (FETCH_8 (img, l, bo) & 0xf0) | (v4))); \ } while (0) #endif #ifdef WORDS_BIGENDIAN #define STORE_24(img,l,o,v) \ do \ { \ uint8_t *__tmp = (l) + 3 * (o); \ \ WRITE ((img), __tmp++, ((v) & 0x00ff0000) >> 16); \ WRITE ((img), __tmp++, ((v) & 0x0000ff00) >> 8); \ WRITE ((img), __tmp++, ((v) & 0x000000ff) >> 0); \ } \ while (0) #else #define STORE_24(img,l,o,v) \ do \ { \ uint8_t *__tmp = (l) + 3 * (o); \ \ WRITE ((img), __tmp++, ((v) & 0x000000ff) >> 0); \ WRITE ((img), __tmp++, ((v) & 0x0000ff00) >> 8); \ WRITE ((img), __tmp++, ((v) & 0x00ff0000) >> 16); \ } \ while (0) #endif /* * YV12 setup and access macros */ #define YV12_SETUP(image) \ bits_image_t *__bits_image = (bits_image_t *)image; \ uint32_t *bits = __bits_image->bits; \ int stride = __bits_image->rowstride; \ int offset0 = stride < 0 ? \ ((-stride) >> 1) * ((__bits_image->height - 1) >> 1) - stride : \ stride * __bits_image->height; \ int offset1 = stride < 0 ? \ offset0 + ((-stride) >> 1) * ((__bits_image->height) >> 1) : \ offset0 + (offset0 >> 2) /* Note no trailing semicolon on the above macro; if it's there, then * the typical usage of YV12_SETUP(image); will have an extra trailing ; * that some compilers will interpret as a statement -- and then any further * variable declarations will cause an error. */ #define YV12_Y(line) \ ((uint8_t *) ((bits) + (stride) * (line))) #define YV12_U(line) \ ((uint8_t *) ((bits) + offset1 + \ ((stride) >> 1) * ((line) >> 1))) #define YV12_V(line) \ ((uint8_t *) ((bits) + offset0 + \ ((stride) >> 1) * ((line) >> 1))) /* Misc. helpers */ static force_inline void get_shifts (pixman_format_code_t format, int *a, int *r, int *g, int *b) { switch (PIXMAN_FORMAT_TYPE (format)) { case PIXMAN_TYPE_A: *b = 0; *g = 0; *r = 0; *a = 0; break; case PIXMAN_TYPE_ARGB: case PIXMAN_TYPE_ARGB_SRGB: *b = 0; *g = *b + PIXMAN_FORMAT_B (format); *r = *g + PIXMAN_FORMAT_G (format); *a = *r + PIXMAN_FORMAT_R (format); break; case PIXMAN_TYPE_ABGR: *r = 0; *g = *r + PIXMAN_FORMAT_R (format); *b = *g + PIXMAN_FORMAT_G (format); *a = *b + PIXMAN_FORMAT_B (format); break; case PIXMAN_TYPE_BGRA: /* With BGRA formats we start counting at the high end of the pixel */ *b = PIXMAN_FORMAT_BPP (format) - PIXMAN_FORMAT_B (format); *g = *b - PIXMAN_FORMAT_B (format); *r = *g - PIXMAN_FORMAT_G (format); *a = *r - PIXMAN_FORMAT_R (format); break; case PIXMAN_TYPE_RGBA: /* With BGRA formats we start counting at the high end of the pixel */ *r = PIXMAN_FORMAT_BPP (format) - PIXMAN_FORMAT_R (format); *g = *r - PIXMAN_FORMAT_R (format); *b = *g - PIXMAN_FORMAT_G (format); *a = *b - PIXMAN_FORMAT_B (format); break; default: assert (0); break; } } static force_inline uint32_t convert_channel (uint32_t pixel, uint32_t def_value, int n_from_bits, int from_shift, int n_to_bits, int to_shift) { uint32_t v; if (n_from_bits && n_to_bits) v = unorm_to_unorm (pixel >> from_shift, n_from_bits, n_to_bits); else if (n_to_bits) v = def_value; else v = 0; return (v & ((1 << n_to_bits) - 1)) << to_shift; } static force_inline uint32_t convert_pixel (pixman_format_code_t from, pixman_format_code_t to, uint32_t pixel) { int a_from_shift, r_from_shift, g_from_shift, b_from_shift; int a_to_shift, r_to_shift, g_to_shift, b_to_shift; uint32_t a, r, g, b; get_shifts (from, &a_from_shift, &r_from_shift, &g_from_shift, &b_from_shift); get_shifts (to, &a_to_shift, &r_to_shift, &g_to_shift, &b_to_shift); a = convert_channel (pixel, ~0, PIXMAN_FORMAT_A (from), a_from_shift, PIXMAN_FORMAT_A (to), a_to_shift); r = convert_channel (pixel, 0, PIXMAN_FORMAT_R (from), r_from_shift, PIXMAN_FORMAT_R (to), r_to_shift); g = convert_channel (pixel, 0, PIXMAN_FORMAT_G (from), g_from_shift, PIXMAN_FORMAT_G (to), g_to_shift); b = convert_channel (pixel, 0, PIXMAN_FORMAT_B (from), b_from_shift, PIXMAN_FORMAT_B (to), b_to_shift); return a | r | g | b; } static force_inline uint32_t convert_pixel_to_a8r8g8b8 (pixman_image_t *image, pixman_format_code_t format, uint32_t pixel) { if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_GRAY || PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_COLOR) { return image->bits.indexed->rgba[pixel]; } else { return convert_pixel (format, PIXMAN_a8r8g8b8, pixel); } } static force_inline uint32_t convert_pixel_from_a8r8g8b8 (pixman_image_t *image, pixman_format_code_t format, uint32_t pixel) { if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_GRAY) { pixel = CONVERT_RGB24_TO_Y15 (pixel); return image->bits.indexed->ent[pixel & 0x7fff]; } else if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_COLOR) { pixel = convert_pixel (PIXMAN_a8r8g8b8, PIXMAN_x1r5g5b5, pixel); return image->bits.indexed->ent[pixel & 0x7fff]; } else { return convert_pixel (PIXMAN_a8r8g8b8, format, pixel); } } static force_inline uint32_t fetch_and_convert_pixel (pixman_image_t * image, const uint8_t * bits, int offset, pixman_format_code_t format) { uint32_t pixel; switch (PIXMAN_FORMAT_BPP (format)) { case 1: pixel = FETCH_1 (image, bits, offset); break; case 4: pixel = FETCH_4 (image, bits, offset); break; case 8: pixel = READ (image, bits + offset); break; case 16: pixel = READ (image, ((uint16_t *)bits + offset)); break; case 24: pixel = FETCH_24 (image, bits, offset); break; case 32: pixel = READ (image, ((uint32_t *)bits + offset)); break; default: pixel = 0xffff00ff; /* As ugly as possible to detect the bug */ break; } return convert_pixel_to_a8r8g8b8 (image, format, pixel); } static force_inline void convert_and_store_pixel (bits_image_t * image, uint8_t * dest, int offset, pixman_format_code_t format, uint32_t pixel) { uint32_t converted = convert_pixel_from_a8r8g8b8 ( (pixman_image_t *)image, format, pixel); switch (PIXMAN_FORMAT_BPP (format)) { case 1: STORE_1 (image, dest, offset, converted & 0x01); break; case 4: STORE_4 (image, dest, offset, converted & 0xf); break; case 8: WRITE (image, (dest + offset), converted & 0xff); break; case 16: WRITE (image, ((uint16_t *)dest + offset), converted & 0xffff); break; case 24: STORE_24 (image, dest, offset, converted); break; case 32: WRITE (image, ((uint32_t *)dest + offset), converted); break; default: *dest = 0x0; break; } } #define MAKE_ACCESSORS(format) \ static void \ fetch_scanline_ ## format (pixman_image_t *image, \ int x, \ int y, \ int width, \ uint32_t * buffer, \ const uint32_t *mask) \ { \ uint8_t *bits = \ (uint8_t *)(image->bits.bits + y * image->bits.rowstride); \ int i; \ \ for (i = 0; i < width; ++i) \ { \ *buffer++ = \ fetch_and_convert_pixel (image, bits, x + i, PIXMAN_ ## format); \ } \ } \ \ static void \ store_scanline_ ## format (bits_image_t * image, \ int x, \ int y, \ int width, \ const uint32_t *values) \ { \ uint8_t *dest = \ (uint8_t *)(image->bits + y * image->rowstride); \ int i; \ \ for (i = 0; i < width; ++i) \ { \ convert_and_store_pixel ( \ image, dest, i + x, PIXMAN_ ## format, values[i]); \ } \ } \ \ static uint32_t \ fetch_pixel_ ## format (bits_image_t *image, \ int offset, \ int line) \ { \ uint8_t *bits = \ (uint8_t *)(image->bits + line * image->rowstride); \ \ return fetch_and_convert_pixel ((pixman_image_t *)image, \ bits, offset, PIXMAN_ ## format); \ } \ \ static const void *const __dummy__ ## format MAKE_ACCESSORS(a8r8g8b8); MAKE_ACCESSORS(x8r8g8b8); MAKE_ACCESSORS(a8b8g8r8); MAKE_ACCESSORS(x8b8g8r8); MAKE_ACCESSORS(x14r6g6b6); MAKE_ACCESSORS(b8g8r8a8); MAKE_ACCESSORS(b8g8r8x8); MAKE_ACCESSORS(r8g8b8x8); MAKE_ACCESSORS(r8g8b8a8); MAKE_ACCESSORS(r8g8b8); MAKE_ACCESSORS(b8g8r8); MAKE_ACCESSORS(r5g6b5); MAKE_ACCESSORS(b5g6r5); MAKE_ACCESSORS(a1r5g5b5); MAKE_ACCESSORS(x1r5g5b5); MAKE_ACCESSORS(a1b5g5r5); MAKE_ACCESSORS(x1b5g5r5); MAKE_ACCESSORS(a4r4g4b4); MAKE_ACCESSORS(x4r4g4b4); MAKE_ACCESSORS(a4b4g4r4); MAKE_ACCESSORS(x4b4g4r4); MAKE_ACCESSORS(a8); MAKE_ACCESSORS(c8); MAKE_ACCESSORS(g8); MAKE_ACCESSORS(r3g3b2); MAKE_ACCESSORS(b2g3r3); MAKE_ACCESSORS(a2r2g2b2); MAKE_ACCESSORS(a2b2g2r2); MAKE_ACCESSORS(x4a4); MAKE_ACCESSORS(a4); MAKE_ACCESSORS(g4); MAKE_ACCESSORS(c4); MAKE_ACCESSORS(r1g2b1); MAKE_ACCESSORS(b1g2r1); MAKE_ACCESSORS(a1r1g1b1); MAKE_ACCESSORS(a1b1g1r1); MAKE_ACCESSORS(a1); MAKE_ACCESSORS(g1); /********************************** Fetch ************************************/ /* Table mapping sRGB-encoded 8 bit numbers to linearly encoded * floating point numbers. We assume that single precision * floating point follows the IEEE 754 format. */ static const uint32_t to_linear_u[256] = { 0x00000000, 0x399f22b4, 0x3a1f22b4, 0x3a6eb40e, 0x3a9f22b4, 0x3ac6eb61, 0x3aeeb40e, 0x3b0b3e5d, 0x3b1f22b4, 0x3b33070b, 0x3b46eb61, 0x3b5b518a, 0x3b70f18a, 0x3b83e1c5, 0x3b8fe614, 0x3b9c87fb, 0x3ba9c9b5, 0x3bb7ad6d, 0x3bc63547, 0x3bd5635f, 0x3be539bd, 0x3bf5ba70, 0x3c0373b5, 0x3c0c6152, 0x3c15a703, 0x3c1f45bc, 0x3c293e68, 0x3c3391f4, 0x3c3e4149, 0x3c494d43, 0x3c54b6c7, 0x3c607eb1, 0x3c6ca5df, 0x3c792d22, 0x3c830aa8, 0x3c89af9e, 0x3c9085db, 0x3c978dc5, 0x3c9ec7c0, 0x3ca63432, 0x3cadd37d, 0x3cb5a601, 0x3cbdac20, 0x3cc5e639, 0x3cce54ab, 0x3cd6f7d2, 0x3cdfd00e, 0x3ce8ddb9, 0x3cf2212c, 0x3cfb9ac1, 0x3d02a569, 0x3d0798dc, 0x3d0ca7e4, 0x3d11d2ae, 0x3d171963, 0x3d1c7c2e, 0x3d21fb3a, 0x3d2796af, 0x3d2d4ebb, 0x3d332380, 0x3d39152b, 0x3d3f23e3, 0x3d454fd0, 0x3d4b991c, 0x3d51ffeb, 0x3d588466, 0x3d5f26b7, 0x3d65e6fe, 0x3d6cc564, 0x3d73c210, 0x3d7add25, 0x3d810b65, 0x3d84b793, 0x3d88732e, 0x3d8c3e48, 0x3d9018f4, 0x3d940343, 0x3d97fd48, 0x3d9c0714, 0x3da020b9, 0x3da44a48, 0x3da883d6, 0x3daccd70, 0x3db12728, 0x3db59110, 0x3dba0b38, 0x3dbe95b2, 0x3dc3308f, 0x3dc7dbe0, 0x3dcc97b4, 0x3dd1641c, 0x3dd6412a, 0x3ddb2eec, 0x3de02d75, 0x3de53cd3, 0x3dea5d16, 0x3def8e52, 0x3df4d091, 0x3dfa23e5, 0x3dff885e, 0x3e027f06, 0x3e05427f, 0x3e080ea2, 0x3e0ae376, 0x3e0dc104, 0x3e10a752, 0x3e139669, 0x3e168e50, 0x3e198f0e, 0x3e1c98ab, 0x3e1fab2e, 0x3e22c6a0, 0x3e25eb08, 0x3e29186a, 0x3e2c4ed0, 0x3e2f8e42, 0x3e32d6c4, 0x3e362861, 0x3e39831e, 0x3e3ce702, 0x3e405416, 0x3e43ca5e, 0x3e4749e4, 0x3e4ad2ae, 0x3e4e64c2, 0x3e520027, 0x3e55a4e6, 0x3e595303, 0x3e5d0a8a, 0x3e60cb7c, 0x3e6495e0, 0x3e6869bf, 0x3e6c4720, 0x3e702e08, 0x3e741e7f, 0x3e78188c, 0x3e7c1c34, 0x3e8014c0, 0x3e822039, 0x3e84308b, 0x3e8645b8, 0x3e885fc3, 0x3e8a7eb0, 0x3e8ca281, 0x3e8ecb3a, 0x3e90f8df, 0x3e932b72, 0x3e9562f6, 0x3e979f6f, 0x3e99e0e0, 0x3e9c274e, 0x3e9e72b8, 0x3ea0c322, 0x3ea31892, 0x3ea57308, 0x3ea7d28a, 0x3eaa3718, 0x3eaca0b7, 0x3eaf0f69, 0x3eb18332, 0x3eb3fc16, 0x3eb67a15, 0x3eb8fd34, 0x3ebb8576, 0x3ebe12de, 0x3ec0a56e, 0x3ec33d2a, 0x3ec5da14, 0x3ec87c30, 0x3ecb2380, 0x3ecdd008, 0x3ed081ca, 0x3ed338c9, 0x3ed5f508, 0x3ed8b68a, 0x3edb7d52, 0x3ede4962, 0x3ee11abe, 0x3ee3f168, 0x3ee6cd64, 0x3ee9aeb6, 0x3eec955d, 0x3eef815d, 0x3ef272ba, 0x3ef56976, 0x3ef86594, 0x3efb6717, 0x3efe6e02, 0x3f00bd2b, 0x3f02460c, 0x3f03d1a5, 0x3f055ff8, 0x3f06f105, 0x3f0884ce, 0x3f0a1b54, 0x3f0bb499, 0x3f0d509f, 0x3f0eef65, 0x3f1090ef, 0x3f12353c, 0x3f13dc50, 0x3f15862a, 0x3f1732cc, 0x3f18e237, 0x3f1a946d, 0x3f1c4970, 0x3f1e013f, 0x3f1fbbde, 0x3f21794c, 0x3f23398c, 0x3f24fca0, 0x3f26c286, 0x3f288b42, 0x3f2a56d3, 0x3f2c253d, 0x3f2df680, 0x3f2fca9d, 0x3f31a195, 0x3f337b6a, 0x3f35581e, 0x3f3737b1, 0x3f391a24, 0x3f3aff7a, 0x3f3ce7b2, 0x3f3ed2d0, 0x3f40c0d2, 0x3f42b1bc, 0x3f44a58e, 0x3f469c49, 0x3f4895ee, 0x3f4a9280, 0x3f4c91ff, 0x3f4e946c, 0x3f5099c8, 0x3f52a216, 0x3f54ad55, 0x3f56bb88, 0x3f58ccae, 0x3f5ae0cb, 0x3f5cf7de, 0x3f5f11ec, 0x3f612ef0, 0x3f634eef, 0x3f6571ea, 0x3f6797e1, 0x3f69c0d6, 0x3f6beccb, 0x3f6e1bc0, 0x3f704db6, 0x3f7282af, 0x3f74baac, 0x3f76f5ae, 0x3f7933b6, 0x3f7b74c6, 0x3f7db8de, 0x3f800000 }; static const float * const to_linear = (const float *)to_linear_u; static uint8_t to_srgb (float f) { uint8_t low = 0; uint8_t high = 255; while (high - low > 1) { uint8_t mid = (low + high) / 2; if (to_linear[mid] > f) high = mid; else low = mid; } if (to_linear[high] - f < f - to_linear[low]) return high; else return low; } static void fetch_scanline_a8r8g8b8_sRGB_float (pixman_image_t *image, int x, int y, int width, uint32_t * b, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = bits + x; const uint32_t *end = pixel + width; argb_t *buffer = (argb_t *)b; while (pixel < end) { uint32_t p = READ (image, pixel++); argb_t *argb = buffer; argb->a = pixman_unorm_to_float ((p >> 24) & 0xff, 8); argb->r = to_linear [(p >> 16) & 0xff]; argb->g = to_linear [(p >> 8) & 0xff]; argb->b = to_linear [(p >> 0) & 0xff]; buffer++; } } /* Expects a float buffer */ static void fetch_scanline_a2r10g10b10_float (pixman_image_t *image, int x, int y, int width, uint32_t * b, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = bits + x; const uint32_t *end = pixel + width; argb_t *buffer = (argb_t *)b; while (pixel < end) { uint32_t p = READ (image, pixel++); uint64_t a = p >> 30; uint64_t r = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t b = p & 0x3ff; buffer->a = pixman_unorm_to_float (a, 2); buffer->r = pixman_unorm_to_float (r, 10); buffer->g = pixman_unorm_to_float (g, 10); buffer->b = pixman_unorm_to_float (b, 10); buffer++; } } /* Expects a float buffer */ static void fetch_scanline_x2r10g10b10_float (pixman_image_t *image, int x, int y, int width, uint32_t * b, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = (uint32_t *)bits + x; const uint32_t *end = pixel + width; argb_t *buffer = (argb_t *)b; while (pixel < end) { uint32_t p = READ (image, pixel++); uint64_t r = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t b = p & 0x3ff; buffer->a = 1.0; buffer->r = pixman_unorm_to_float (r, 10); buffer->g = pixman_unorm_to_float (g, 10); buffer->b = pixman_unorm_to_float (b, 10); buffer++; } } /* Expects a float buffer */ static void fetch_scanline_a2b10g10r10_float (pixman_image_t *image, int x, int y, int width, uint32_t * b, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = bits + x; const uint32_t *end = pixel + width; argb_t *buffer = (argb_t *)b; while (pixel < end) { uint32_t p = READ (image, pixel++); uint64_t a = p >> 30; uint64_t b = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t r = p & 0x3ff; buffer->a = pixman_unorm_to_float (a, 2); buffer->r = pixman_unorm_to_float (r, 10); buffer->g = pixman_unorm_to_float (g, 10); buffer->b = pixman_unorm_to_float (b, 10); buffer++; } } /* Expects a float buffer */ static void fetch_scanline_x2b10g10r10_float (pixman_image_t *image, int x, int y, int width, uint32_t * b, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = (uint32_t *)bits + x; const uint32_t *end = pixel + width; argb_t *buffer = (argb_t *)b; while (pixel < end) { uint32_t p = READ (image, pixel++); uint64_t b = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t r = p & 0x3ff; buffer->a = 1.0; buffer->r = pixman_unorm_to_float (r, 10); buffer->g = pixman_unorm_to_float (g, 10); buffer->b = pixman_unorm_to_float (b, 10); buffer++; } } static void fetch_scanline_yuy2 (pixman_image_t *image, int x, int line, int width, uint32_t * buffer, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + image->bits.rowstride * line; int i; for (i = 0; i < width; i++) { int16_t y, u, v; int32_t r, g, b; y = ((uint8_t *) bits)[(x + i) << 1] - 16; u = ((uint8_t *) bits)[(((x + i) << 1) & - 4) + 1] - 128; v = ((uint8_t *) bits)[(((x + i) << 1) & - 4) + 3] - 128; /* R = 1.164(Y - 16) + 1.596(V - 128) */ r = 0x012b27 * y + 0x019a2e * v; /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; /* B = 1.164(Y - 16) + 2.018(U - 128) */ b = 0x012b27 * y + 0x0206a2 * u; *buffer++ = 0xff000000 | (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); } } static void fetch_scanline_yv12 (pixman_image_t *image, int x, int line, int width, uint32_t * buffer, const uint32_t *mask) { YV12_SETUP (image); uint8_t *y_line = YV12_Y (line); uint8_t *u_line = YV12_U (line); uint8_t *v_line = YV12_V (line); int i; for (i = 0; i < width; i++) { int16_t y, u, v; int32_t r, g, b; y = y_line[x + i] - 16; u = u_line[(x + i) >> 1] - 128; v = v_line[(x + i) >> 1] - 128; /* R = 1.164(Y - 16) + 1.596(V - 128) */ r = 0x012b27 * y + 0x019a2e * v; /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; /* B = 1.164(Y - 16) + 2.018(U - 128) */ b = 0x012b27 * y + 0x0206a2 * u; *buffer++ = 0xff000000 | (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); } } /**************************** Pixel wise fetching *****************************/ static argb_t fetch_pixel_x2r10g10b10_float (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t p = READ (image, bits + offset); uint64_t r = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t b = p & 0x3ff; argb_t argb; argb.a = 1.0; argb.r = pixman_unorm_to_float (r, 10); argb.g = pixman_unorm_to_float (g, 10); argb.b = pixman_unorm_to_float (b, 10); return argb; } static argb_t fetch_pixel_a2r10g10b10_float (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t p = READ (image, bits + offset); uint64_t a = p >> 30; uint64_t r = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t b = p & 0x3ff; argb_t argb; argb.a = pixman_unorm_to_float (a, 2); argb.r = pixman_unorm_to_float (r, 10); argb.g = pixman_unorm_to_float (g, 10); argb.b = pixman_unorm_to_float (b, 10); return argb; } static argb_t fetch_pixel_a2b10g10r10_float (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t p = READ (image, bits + offset); uint64_t a = p >> 30; uint64_t b = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t r = p & 0x3ff; argb_t argb; argb.a = pixman_unorm_to_float (a, 2); argb.r = pixman_unorm_to_float (r, 10); argb.g = pixman_unorm_to_float (g, 10); argb.b = pixman_unorm_to_float (b, 10); return argb; } static argb_t fetch_pixel_x2b10g10r10_float (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t p = READ (image, bits + offset); uint64_t b = (p >> 20) & 0x3ff; uint64_t g = (p >> 10) & 0x3ff; uint64_t r = p & 0x3ff; argb_t argb; argb.a = 1.0; argb.r = pixman_unorm_to_float (r, 10); argb.g = pixman_unorm_to_float (g, 10); argb.b = pixman_unorm_to_float (b, 10); return argb; } static argb_t fetch_pixel_a8r8g8b8_sRGB_float (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t p = READ (image, bits + offset); argb_t argb; argb.a = pixman_unorm_to_float ((p >> 24) & 0xff, 8); argb.r = to_linear [(p >> 16) & 0xff]; argb.g = to_linear [(p >> 8) & 0xff]; argb.b = to_linear [(p >> 0) & 0xff]; return argb; } static uint32_t fetch_pixel_yuy2 (bits_image_t *image, int offset, int line) { const uint32_t *bits = image->bits + image->rowstride * line; int16_t y, u, v; int32_t r, g, b; y = ((uint8_t *) bits)[offset << 1] - 16; u = ((uint8_t *) bits)[((offset << 1) & - 4) + 1] - 128; v = ((uint8_t *) bits)[((offset << 1) & - 4) + 3] - 128; /* R = 1.164(Y - 16) + 1.596(V - 128) */ r = 0x012b27 * y + 0x019a2e * v; /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; /* B = 1.164(Y - 16) + 2.018(U - 128) */ b = 0x012b27 * y + 0x0206a2 * u; return 0xff000000 | (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); } static uint32_t fetch_pixel_yv12 (bits_image_t *image, int offset, int line) { YV12_SETUP (image); int16_t y = YV12_Y (line)[offset] - 16; int16_t u = YV12_U (line)[offset >> 1] - 128; int16_t v = YV12_V (line)[offset >> 1] - 128; int32_t r, g, b; /* R = 1.164(Y - 16) + 1.596(V - 128) */ r = 0x012b27 * y + 0x019a2e * v; /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; /* B = 1.164(Y - 16) + 2.018(U - 128) */ b = 0x012b27 * y + 0x0206a2 * u; return 0xff000000 | (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); } /*********************************** Store ************************************/ static void store_scanline_a2r10g10b10_float (bits_image_t * image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint32_t *pixel = bits + x; argb_t *values = (argb_t *)v; int i; for (i = 0; i < width; ++i) { uint16_t a, r, g, b; a = pixman_float_to_unorm (values[i].a, 2); r = pixman_float_to_unorm (values[i].r, 10); g = pixman_float_to_unorm (values[i].g, 10); b = pixman_float_to_unorm (values[i].b, 10); WRITE (image, pixel++, (a << 30) | (r << 20) | (g << 10) | b); } } static void store_scanline_x2r10g10b10_float (bits_image_t * image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint32_t *pixel = bits + x; argb_t *values = (argb_t *)v; int i; for (i = 0; i < width; ++i) { uint16_t r, g, b; r = pixman_float_to_unorm (values[i].r, 10); g = pixman_float_to_unorm (values[i].g, 10); b = pixman_float_to_unorm (values[i].b, 10); WRITE (image, pixel++, (r << 20) | (g << 10) | b); } } static void store_scanline_a2b10g10r10_float (bits_image_t * image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint32_t *pixel = bits + x; argb_t *values = (argb_t *)v; int i; for (i = 0; i < width; ++i) { uint16_t a, r, g, b; a = pixman_float_to_unorm (values[i].a, 2); r = pixman_float_to_unorm (values[i].r, 10); g = pixman_float_to_unorm (values[i].g, 10); b = pixman_float_to_unorm (values[i].b, 10); WRITE (image, pixel++, (a << 30) | (b << 20) | (g << 10) | r); } } static void store_scanline_x2b10g10r10_float (bits_image_t * image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint32_t *pixel = bits + x; argb_t *values = (argb_t *)v; int i; for (i = 0; i < width; ++i) { uint16_t r, g, b; r = pixman_float_to_unorm (values[i].r, 10); g = pixman_float_to_unorm (values[i].g, 10); b = pixman_float_to_unorm (values[i].b, 10); WRITE (image, pixel++, (b << 20) | (g << 10) | r); } } static void store_scanline_a8r8g8b8_sRGB_float (bits_image_t * image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint32_t *pixel = bits + x; argb_t *values = (argb_t *)v; int i; for (i = 0; i < width; ++i) { uint8_t a, r, g, b; a = pixman_float_to_unorm (values[i].a, 8); r = to_srgb (values[i].r); g = to_srgb (values[i].g); b = to_srgb (values[i].b); WRITE (image, pixel++, (a << 24) | (r << 16) | (g << 8) | b); } } /* * Contracts a floating point image to 32bpp and then stores it using a * regular 32-bit store proc. Despite the type, this function expects an * argb_t buffer. */ static void store_scanline_generic_float (bits_image_t * image, int x, int y, int width, const uint32_t *values) { uint32_t *argb8_pixels; assert (image->common.type == BITS); argb8_pixels = pixman_malloc_ab (width, sizeof(uint32_t)); if (!argb8_pixels) return; /* Contract the scanline. We could do this in place if values weren't * const. */ pixman_contract_from_float (argb8_pixels, (argb_t *)values, width); image->store_scanline_32 (image, x, y, width, argb8_pixels); free (argb8_pixels); } static void fetch_scanline_generic_float (pixman_image_t *image, int x, int y, int width, uint32_t * buffer, const uint32_t *mask) { image->bits.fetch_scanline_32 (image, x, y, width, buffer, NULL); pixman_expand_to_float ((argb_t *)buffer, buffer, image->bits.format, width); } /* The 32_sRGB paths should be deleted after narrow processing * is no longer invoked for formats that are considered wide. * (Also see fetch_pixel_generic_lossy_32) */ static void fetch_scanline_a8r8g8b8_32_sRGB (pixman_image_t *image, int x, int y, int width, uint32_t *buffer, const uint32_t *mask) { const uint32_t *bits = image->bits.bits + y * image->bits.rowstride; const uint32_t *pixel = (uint32_t *)bits + x; const uint32_t *end = pixel + width; uint32_t tmp; while (pixel < end) { uint8_t a, r, g, b; tmp = READ (image, pixel++); a = (tmp >> 24) & 0xff; r = (tmp >> 16) & 0xff; g = (tmp >> 8) & 0xff; b = (tmp >> 0) & 0xff; r = to_linear[r] * 255.0f + 0.5f; g = to_linear[g] * 255.0f + 0.5f; b = to_linear[b] * 255.0f + 0.5f; *buffer++ = (a << 24) | (r << 16) | (g << 8) | (b << 0); } } static uint32_t fetch_pixel_a8r8g8b8_32_sRGB (bits_image_t *image, int offset, int line) { uint32_t *bits = image->bits + line * image->rowstride; uint32_t tmp = READ (image, bits + offset); uint8_t a, r, g, b; a = (tmp >> 24) & 0xff; r = (tmp >> 16) & 0xff; g = (tmp >> 8) & 0xff; b = (tmp >> 0) & 0xff; r = to_linear[r] * 255.0f + 0.5f; g = to_linear[g] * 255.0f + 0.5f; b = to_linear[b] * 255.0f + 0.5f; return (a << 24) | (r << 16) | (g << 8) | (b << 0); } static void store_scanline_a8r8g8b8_32_sRGB (bits_image_t *image, int x, int y, int width, const uint32_t *v) { uint32_t *bits = image->bits + image->rowstride * y; uint64_t *values = (uint64_t *)v; uint32_t *pixel = bits + x; uint64_t tmp; int i; for (i = 0; i < width; ++i) { uint8_t a, r, g, b; tmp = values[i]; a = (tmp >> 24) & 0xff; r = (tmp >> 16) & 0xff; g = (tmp >> 8) & 0xff; b = (tmp >> 0) & 0xff; r = to_srgb (r * (1/255.0f)); g = to_srgb (g * (1/255.0f)); b = to_srgb (b * (1/255.0f)); WRITE (image, pixel++, a | (r << 16) | (g << 8) | (b << 0)); } } static argb_t fetch_pixel_generic_float (bits_image_t *image, int offset, int line) { uint32_t pixel32 = image->fetch_pixel_32 (image, offset, line); argb_t f; pixman_expand_to_float (&f, &pixel32, image->format, 1); return f; } /* * XXX: The transformed fetch path only works at 32-bpp so far. When all * paths have wide versions, this can be removed. * * WARNING: This function loses precision! */ static uint32_t fetch_pixel_generic_lossy_32 (bits_image_t *image, int offset, int line) { argb_t pixel64 = image->fetch_pixel_float (image, offset, line); uint32_t result; pixman_contract_from_float (&result, &pixel64, 1); return result; } typedef struct { pixman_format_code_t format; fetch_scanline_t fetch_scanline_32; fetch_scanline_t fetch_scanline_float; fetch_pixel_32_t fetch_pixel_32; fetch_pixel_float_t fetch_pixel_float; store_scanline_t store_scanline_32; store_scanline_t store_scanline_float; } format_info_t; #define FORMAT_INFO(format) \ { \ PIXMAN_ ## format, \ fetch_scanline_ ## format, \ fetch_scanline_generic_float, \ fetch_pixel_ ## format, \ fetch_pixel_generic_float, \ store_scanline_ ## format, \ store_scanline_generic_float \ } static const format_info_t accessors[] = { /* 32 bpp formats */ FORMAT_INFO (a8r8g8b8), FORMAT_INFO (x8r8g8b8), FORMAT_INFO (a8b8g8r8), FORMAT_INFO (x8b8g8r8), FORMAT_INFO (b8g8r8a8), FORMAT_INFO (b8g8r8x8), FORMAT_INFO (r8g8b8a8), FORMAT_INFO (r8g8b8x8), FORMAT_INFO (x14r6g6b6), /* sRGB formats */ { PIXMAN_a8r8g8b8_sRGB, fetch_scanline_a8r8g8b8_32_sRGB, fetch_scanline_a8r8g8b8_sRGB_float, fetch_pixel_a8r8g8b8_32_sRGB, fetch_pixel_a8r8g8b8_sRGB_float, store_scanline_a8r8g8b8_32_sRGB, store_scanline_a8r8g8b8_sRGB_float, }, /* 24bpp formats */ FORMAT_INFO (r8g8b8), FORMAT_INFO (b8g8r8), /* 16bpp formats */ FORMAT_INFO (r5g6b5), FORMAT_INFO (b5g6r5), FORMAT_INFO (a1r5g5b5), FORMAT_INFO (x1r5g5b5), FORMAT_INFO (a1b5g5r5), FORMAT_INFO (x1b5g5r5), FORMAT_INFO (a4r4g4b4), FORMAT_INFO (x4r4g4b4), FORMAT_INFO (a4b4g4r4), FORMAT_INFO (x4b4g4r4), /* 8bpp formats */ FORMAT_INFO (a8), FORMAT_INFO (r3g3b2), FORMAT_INFO (b2g3r3), FORMAT_INFO (a2r2g2b2), FORMAT_INFO (a2b2g2r2), FORMAT_INFO (c8), FORMAT_INFO (g8), #define fetch_scanline_x4c4 fetch_scanline_c8 #define fetch_pixel_x4c4 fetch_pixel_c8 #define store_scanline_x4c4 store_scanline_c8 FORMAT_INFO (x4c4), #define fetch_scanline_x4g4 fetch_scanline_g8 #define fetch_pixel_x4g4 fetch_pixel_g8 #define store_scanline_x4g4 store_scanline_g8 FORMAT_INFO (x4g4), FORMAT_INFO (x4a4), /* 4bpp formats */ FORMAT_INFO (a4), FORMAT_INFO (r1g2b1), FORMAT_INFO (b1g2r1), FORMAT_INFO (a1r1g1b1), FORMAT_INFO (a1b1g1r1), FORMAT_INFO (c4), FORMAT_INFO (g4), /* 1bpp formats */ FORMAT_INFO (a1), FORMAT_INFO (g1), /* Wide formats */ { PIXMAN_a2r10g10b10, NULL, fetch_scanline_a2r10g10b10_float, fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10_float, NULL, store_scanline_a2r10g10b10_float }, { PIXMAN_x2r10g10b10, NULL, fetch_scanline_x2r10g10b10_float, fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10_float, NULL, store_scanline_x2r10g10b10_float }, { PIXMAN_a2b10g10r10, NULL, fetch_scanline_a2b10g10r10_float, fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10_float, NULL, store_scanline_a2b10g10r10_float }, { PIXMAN_x2b10g10r10, NULL, fetch_scanline_x2b10g10r10_float, fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10_float, NULL, store_scanline_x2b10g10r10_float }, /* YUV formats */ { PIXMAN_yuy2, fetch_scanline_yuy2, fetch_scanline_generic_float, fetch_pixel_yuy2, fetch_pixel_generic_float, NULL, NULL }, { PIXMAN_yv12, fetch_scanline_yv12, fetch_scanline_generic_float, fetch_pixel_yv12, fetch_pixel_generic_float, NULL, NULL }, { PIXMAN_null }, }; static void setup_accessors (bits_image_t *image) { const format_info_t *info = accessors; while (info->format != PIXMAN_null) { if (info->format == image->format) { image->fetch_scanline_32 = info->fetch_scanline_32; image->fetch_scanline_float = info->fetch_scanline_float; image->fetch_pixel_32 = info->fetch_pixel_32; image->fetch_pixel_float = info->fetch_pixel_float; image->store_scanline_32 = info->store_scanline_32; image->store_scanline_float = info->store_scanline_float; return; } info++; } } #ifndef PIXMAN_FB_ACCESSORS void _pixman_bits_image_setup_accessors_accessors (bits_image_t *image); void _pixman_bits_image_setup_accessors (bits_image_t *image) { if (image->read_func || image->write_func) _pixman_bits_image_setup_accessors_accessors (image); else setup_accessors (image); } #else void _pixman_bits_image_setup_accessors_accessors (bits_image_t *image) { setup_accessors (image); } #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-accessor.h000066400000000000000000000012071271037650300264510ustar00rootroot00000000000000#ifdef PIXMAN_FB_ACCESSORS #define READ(img, ptr) \ (((bits_image_t *)(img))->read_func ((ptr), sizeof(*(ptr)))) #define WRITE(img, ptr,val) \ (((bits_image_t *)(img))->write_func ((ptr), (val), sizeof (*(ptr)))) #define MEMSET_WRAPPED(img, dst, val, size) \ do { \ size_t _i; \ uint8_t *_dst = (uint8_t*)(dst); \ for(_i = 0; _i < (size_t) size; _i++) { \ WRITE((img), _dst +_i, (val)); \ } \ } while (0) #else #define READ(img, ptr) (*(ptr)) #define WRITE(img, ptr, val) (*(ptr) = (val)) #define MEMSET_WRAPPED(img, dst, val, size) \ memset(dst, val, size) #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-common.h000066400000000000000000000707211271037650300267230ustar00rootroot00000000000000/* * Copyright © 2010 Nokia Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) */ #ifndef PIXMAN_ARM_COMMON_H #define PIXMAN_ARM_COMMON_H #include "pixman-inlines.h" /* Define some macros which can expand into proxy functions between * ARM assembly optimized functions and the rest of pixman fast path API. * * All the low level ARM assembly functions have to use ARM EABI * calling convention and take up to 8 arguments: * width, height, dst, dst_stride, src, src_stride, mask, mask_stride * * The arguments are ordered with the most important coming first (the * first 4 arguments are passed to function in registers, the rest are * on stack). The last arguments are optional, for example if the * function is not using mask, then 'mask' and 'mask_stride' can be * omitted when doing a function call. * * Arguments 'src' and 'mask' contain either a pointer to the top left * pixel of the composited rectangle or a pixel color value depending * on the function type. In the case of just a color value (solid source * or mask), the corresponding stride argument is unused. */ #define SKIP_ZERO_SRC 1 #define SKIP_ZERO_MASK 2 #define PIXMAN_ARM_BIND_FAST_PATH_SRC_DST(cputype, name, \ src_type, src_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_##cputype (int32_t w, \ int32_t h, \ dst_type *dst, \ int32_t dst_stride, \ src_type *src, \ int32_t src_stride); \ \ static void \ cputype##_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line; \ src_type *src_line; \ int32_t dst_stride, src_stride; \ \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ \ pixman_composite_##name##_asm_##cputype (width, height, \ dst_line, dst_stride, \ src_line, src_stride); \ } #define PIXMAN_ARM_BIND_FAST_PATH_N_DST(flags, cputype, name, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_##cputype (int32_t w, \ int32_t h, \ dst_type *dst, \ int32_t dst_stride, \ uint32_t src); \ \ static void \ cputype##_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line; \ int32_t dst_stride; \ uint32_t src; \ \ src = _pixman_image_get_solid ( \ imp, src_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_SRC) && src == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ \ pixman_composite_##name##_asm_##cputype (width, height, \ dst_line, dst_stride, \ src); \ } #define PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST(flags, cputype, name, \ mask_type, mask_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_##cputype (int32_t w, \ int32_t h, \ dst_type *dst, \ int32_t dst_stride, \ uint32_t src, \ int32_t unused, \ mask_type *mask, \ int32_t mask_stride); \ \ static void \ cputype##_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line; \ mask_type *mask_line; \ int32_t dst_stride, mask_stride; \ uint32_t src; \ \ src = _pixman_image_get_solid ( \ imp, src_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_SRC) && src == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ mask_stride, mask_line, mask_cnt); \ \ pixman_composite_##name##_asm_##cputype (width, height, \ dst_line, dst_stride, \ src, 0, \ mask_line, mask_stride); \ } #define PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST(flags, cputype, name, \ src_type, src_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_##cputype (int32_t w, \ int32_t h, \ dst_type *dst, \ int32_t dst_stride, \ src_type *src, \ int32_t src_stride, \ uint32_t mask); \ \ static void \ cputype##_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line; \ src_type *src_line; \ int32_t dst_stride, src_stride; \ uint32_t mask; \ \ mask = _pixman_image_get_solid ( \ imp, mask_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_MASK) && mask == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ \ pixman_composite_##name##_asm_##cputype (width, height, \ dst_line, dst_stride, \ src_line, src_stride, \ mask); \ } #define PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST(cputype, name, \ src_type, src_cnt, \ mask_type, mask_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_##cputype (int32_t w, \ int32_t h, \ dst_type *dst, \ int32_t dst_stride, \ src_type *src, \ int32_t src_stride, \ mask_type *mask, \ int32_t mask_stride); \ \ static void \ cputype##_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line; \ src_type *src_line; \ mask_type *mask_line; \ int32_t dst_stride, src_stride, mask_stride; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ mask_stride, mask_line, mask_cnt); \ \ pixman_composite_##name##_asm_##cputype (width, height, \ dst_line, dst_stride, \ src_line, src_stride, \ mask_line, mask_stride); \ } #define PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST(cputype, name, op, \ src_type, dst_type) \ void \ pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype ( \ int32_t w, \ dst_type * dst, \ const src_type * src, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx); \ \ static force_inline void \ scaled_nearest_scanline_##cputype##_##name##_##op (dst_type * pd, \ const src_type * ps, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype (w, pd, ps, \ vx, unit_x, \ max_vx); \ } \ \ FAST_NEAREST_MAINLOOP (cputype##_##name##_cover_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op, \ src_type, dst_type, COVER) \ FAST_NEAREST_MAINLOOP (cputype##_##name##_none_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op, \ src_type, dst_type, NONE) \ FAST_NEAREST_MAINLOOP (cputype##_##name##_pad_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op, \ src_type, dst_type, PAD) \ FAST_NEAREST_MAINLOOP (cputype##_##name##_normal_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op, \ src_type, dst_type, NORMAL) /* Provide entries for the fast path table */ #define PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_NORMAL (op,s,d,func) #define PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST(flags, cputype, name, op, \ src_type, dst_type) \ void \ pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype ( \ int32_t w, \ dst_type * dst, \ const src_type * src, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ const uint8_t * mask); \ \ static force_inline void \ scaled_nearest_scanline_##cputype##_##name##_##op (const uint8_t * mask, \ dst_type * pd, \ const src_type * ps, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype (w, pd, ps, \ vx, unit_x, \ max_vx, \ mask); \ } \ \ FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op,\ src_type, uint8_t, dst_type, COVER, TRUE, FALSE)\ FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op,\ src_type, uint8_t, dst_type, NONE, TRUE, FALSE) \ FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op,\ src_type, uint8_t, dst_type, PAD, TRUE, FALSE) \ FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ scaled_nearest_scanline_##cputype##_##name##_##op,\ src_type, uint8_t, dst_type, NORMAL, TRUE, FALSE) /* Provide entries for the fast path table */ #define PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_NORMAL (op,s,d,func) /*****************************************************************************/ #define PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST(flags, cputype, name, op, \ src_type, dst_type) \ void \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ dst_type * dst, \ const src_type * top, \ const src_type * bottom, \ int wt, \ int wb, \ pixman_fixed_t x, \ pixman_fixed_t ux, \ int width); \ \ static force_inline void \ scaled_bilinear_scanline_##cputype##_##name##_##op ( \ dst_type * dst, \ const uint32_t * mask, \ const src_type * src_top, \ const src_type * src_bottom, \ int32_t w, \ int wt, \ int wb, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ dst, src_top, src_bottom, wt, wb, vx, unit_x, w); \ } \ \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint32_t, dst_type, COVER, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint32_t, dst_type, NONE, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint32_t, dst_type, PAD, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint32_t, dst_type, NORMAL, \ FLAG_NONE) #define PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST(flags, cputype, name, op, \ src_type, dst_type) \ void \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ dst_type * dst, \ const uint8_t * mask, \ const src_type * top, \ const src_type * bottom, \ int wt, \ int wb, \ pixman_fixed_t x, \ pixman_fixed_t ux, \ int width); \ \ static force_inline void \ scaled_bilinear_scanline_##cputype##_##name##_##op ( \ dst_type * dst, \ const uint8_t * mask, \ const src_type * src_top, \ const src_type * src_bottom, \ int32_t w, \ int wt, \ int wb, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ dst, mask, src_top, src_bottom, wt, wb, vx, unit_x, w); \ } \ \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint8_t, dst_type, COVER, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint8_t, dst_type, NONE, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint8_t, dst_type, PAD, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ scaled_bilinear_scanline_##cputype##_##name##_##op, \ src_type, uint8_t, dst_type, NORMAL, \ FLAG_HAVE_NON_SOLID_MASK) #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-neon-asm-bilinear.S000066400000000000000000001304641271037650300307070ustar00rootroot00000000000000/* * Copyright © 2011 SCore Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) * Author: Taekyun Kim (tkq.kim@samsung.com) */ /* * This file contains scaled bilinear scanline functions implemented * using older siarhei's bilinear macro template. * * << General scanline function procedures >> * 1. bilinear interpolate source pixels * 2. load mask pixels * 3. load destination pixels * 4. duplicate mask to fill whole register * 5. interleave source & destination pixels * 6. apply mask to source pixels * 7. combine source & destination pixels * 8, Deinterleave final result * 9. store destination pixels * * All registers with single number (i.e. src0, tmp0) are 64-bits registers. * Registers with double numbers(src01, dst01) are 128-bits registers. * All temp registers can be used freely outside the code block. * Assume that symbol(register .req) OUT and MASK are defined at caller of these macro blocks. * * Remarks * There can be lots of pipeline stalls inside code block and between code blocks. * Further optimizations will be done by new macro templates using head/tail_head/tail scheme. */ /* Prevent the stack from becoming executable for no reason... */ #if defined(__linux__) && defined (__ELF__) .section .note.GNU-stack,"",%progbits #endif .text .fpu neon .arch armv7a .object_arch armv4 .eabi_attribute 10, 0 .eabi_attribute 12, 0 .arm .altmacro .p2align 2 #include "pixman-private.h" #include "pixman-arm-neon-asm.h" /* * Bilinear macros from pixman-arm-neon-asm.S */ /* Supplementary macro for setting function attributes */ .macro pixman_asm_function fname .func fname .global fname #ifdef __ELF__ .hidden fname .type fname, %function #endif fname: .endm /* * Bilinear scaling support code which tries to provide pixel fetching, color * format conversion, and interpolation as separate macros which can be used * as the basic building blocks for constructing bilinear scanline functions. */ .macro bilinear_load_8888 reg1, reg2, tmp mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 vld1.32 {reg1}, [TMP1], STRIDE vld1.32 {reg2}, [TMP1] .endm .macro bilinear_load_0565 reg1, reg2, tmp mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 vld1.32 {reg2[0]}, [TMP1], STRIDE vld1.32 {reg2[1]}, [TMP1] convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp .endm .macro bilinear_load_and_vertical_interpolate_two_8888 \ acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 bilinear_load_8888 reg1, reg2, tmp1 vmull.u8 acc1, reg1, d28 vmlal.u8 acc1, reg2, d29 bilinear_load_8888 reg3, reg4, tmp2 vmull.u8 acc2, reg3, d28 vmlal.u8 acc2, reg4, d29 .endm .macro bilinear_load_and_vertical_interpolate_four_8888 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi bilinear_load_and_vertical_interpolate_two_8888 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi bilinear_load_and_vertical_interpolate_two_8888 \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi .endm .macro bilinear_load_and_vertical_interpolate_two_0565 \ acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {acc2lo[0]}, [TMP1], STRIDE vld1.32 {acc2hi[0]}, [TMP2], STRIDE vld1.32 {acc2lo[1]}, [TMP1] vld1.32 {acc2hi[1]}, [TMP2] convert_0565_to_x888 acc2, reg3, reg2, reg1 vzip.u8 reg1, reg3 vzip.u8 reg2, reg4 vzip.u8 reg3, reg4 vzip.u8 reg1, reg2 vmull.u8 acc1, reg1, d28 vmlal.u8 acc1, reg2, d29 vmull.u8 acc2, reg3, d28 vmlal.u8 acc2, reg4, d29 .endm .macro bilinear_load_and_vertical_interpolate_four_0565 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {xacc2lo[0]}, [TMP1], STRIDE vld1.32 {xacc2hi[0]}, [TMP2], STRIDE vld1.32 {xacc2lo[1]}, [TMP1] vld1.32 {xacc2hi[1]}, [TMP2] convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {yacc2lo[0]}, [TMP1], STRIDE vzip.u8 xreg1, xreg3 vld1.32 {yacc2hi[0]}, [TMP2], STRIDE vzip.u8 xreg2, xreg4 vld1.32 {yacc2lo[1]}, [TMP1] vzip.u8 xreg3, xreg4 vld1.32 {yacc2hi[1]}, [TMP2] vzip.u8 xreg1, xreg2 convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 vmull.u8 xacc1, xreg1, d28 vzip.u8 yreg1, yreg3 vmlal.u8 xacc1, xreg2, d29 vzip.u8 yreg2, yreg4 vmull.u8 xacc2, xreg3, d28 vzip.u8 yreg3, yreg4 vmlal.u8 xacc2, xreg4, d29 vzip.u8 yreg1, yreg2 vmull.u8 yacc1, yreg1, d28 vmlal.u8 yacc1, yreg2, d29 vmull.u8 yacc2, yreg3, d28 vmlal.u8 yacc2, yreg4, d29 .endm .macro bilinear_store_8888 numpix, tmp1, tmp2 .if numpix == 4 vst1.32 {d0, d1}, [OUT]! .elseif numpix == 2 vst1.32 {d0}, [OUT]! .elseif numpix == 1 vst1.32 {d0[0]}, [OUT, :32]! .else .error bilinear_store_8888 numpix is unsupported .endif .endm .macro bilinear_store_0565 numpix, tmp1, tmp2 vuzp.u8 d0, d1 vuzp.u8 d2, d3 vuzp.u8 d1, d3 vuzp.u8 d0, d2 convert_8888_to_0565 d2, d1, d0, q1, tmp1, tmp2 .if numpix == 4 vst1.16 {d2}, [OUT]! .elseif numpix == 2 vst1.32 {d2[0]}, [OUT]! .elseif numpix == 1 vst1.16 {d2[0]}, [OUT]! .else .error bilinear_store_0565 numpix is unsupported .endif .endm /* * Macros for loading mask pixels into register 'mask'. * vdup must be done in somewhere else. */ .macro bilinear_load_mask_x numpix, mask .endm .macro bilinear_load_mask_8 numpix, mask .if numpix == 4 vld1.32 {mask[0]}, [MASK]! .elseif numpix == 2 vld1.16 {mask[0]}, [MASK]! .elseif numpix == 1 vld1.8 {mask[0]}, [MASK]! .else .error bilinear_load_mask_8 numpix is unsupported .endif pld [MASK, #prefetch_offset] .endm .macro bilinear_load_mask mask_fmt, numpix, mask bilinear_load_mask_&mask_fmt numpix, mask .endm /* * Macros for loading destination pixels into register 'dst0' and 'dst1'. * Interleave should be done somewhere else. */ .macro bilinear_load_dst_0565_src numpix, dst0, dst1, dst01 .endm .macro bilinear_load_dst_8888_src numpix, dst0, dst1, dst01 .endm .macro bilinear_load_dst_8888 numpix, dst0, dst1, dst01 .if numpix == 4 vld1.32 {dst0, dst1}, [OUT] .elseif numpix == 2 vld1.32 {dst0}, [OUT] .elseif numpix == 1 vld1.32 {dst0[0]}, [OUT] .else .error bilinear_load_dst_8888 numpix is unsupported .endif pld [OUT, #(prefetch_offset * 4)] .endm .macro bilinear_load_dst_8888_over numpix, dst0, dst1, dst01 bilinear_load_dst_8888 numpix, dst0, dst1, dst01 .endm .macro bilinear_load_dst_8888_add numpix, dst0, dst1, dst01 bilinear_load_dst_8888 numpix, dst0, dst1, dst01 .endm .macro bilinear_load_dst dst_fmt, op, numpix, dst0, dst1, dst01 bilinear_load_dst_&dst_fmt&_&op numpix, dst0, dst1, dst01 .endm /* * Macros for duplicating partially loaded mask to fill entire register. * We will apply mask to interleaved source pixels, that is * (r0, r1, r2, r3, g0, g1, g2, g3) x (m0, m1, m2, m3, m0, m1, m2, m3) * (b0, b1, b2, b3, a0, a1, a2, a3) x (m0, m1, m2, m3, m0, m1, m2, m3) * So, we need to duplicate loaded mask into whole register. * * For two pixel case * (r0, r1, x, x, g0, g1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) * (b0, b1, x, x, a0, a1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) * We can do some optimizations for this including last pixel cases. */ .macro bilinear_duplicate_mask_x numpix, mask .endm .macro bilinear_duplicate_mask_8 numpix, mask .if numpix == 4 vdup.32 mask, mask[0] .elseif numpix == 2 vdup.16 mask, mask[0] .elseif numpix == 1 vdup.8 mask, mask[0] .else .error bilinear_duplicate_mask_8 is unsupported .endif .endm .macro bilinear_duplicate_mask mask_fmt, numpix, mask bilinear_duplicate_mask_&mask_fmt numpix, mask .endm /* * Macros for interleaving src and dst pixels to rrrr gggg bbbb aaaa form. * Interleave should be done when maks is enabled or operator is 'over'. */ .macro bilinear_interleave src0, src1, dst0, dst1 vuzp.8 src0, src1 vuzp.8 dst0, dst1 vuzp.8 src0, src1 vuzp.8 dst0, dst1 .endm .macro bilinear_interleave_src_dst_x_src \ numpix, src0, src1, src01, dst0, dst1, dst01 .endm .macro bilinear_interleave_src_dst_x_over \ numpix, src0, src1, src01, dst0, dst1, dst01 bilinear_interleave src0, src1, dst0, dst1 .endm .macro bilinear_interleave_src_dst_x_add \ numpix, src0, src1, src01, dst0, dst1, dst01 .endm .macro bilinear_interleave_src_dst_8_src \ numpix, src0, src1, src01, dst0, dst1, dst01 bilinear_interleave src0, src1, dst0, dst1 .endm .macro bilinear_interleave_src_dst_8_over \ numpix, src0, src1, src01, dst0, dst1, dst01 bilinear_interleave src0, src1, dst0, dst1 .endm .macro bilinear_interleave_src_dst_8_add \ numpix, src0, src1, src01, dst0, dst1, dst01 bilinear_interleave src0, src1, dst0, dst1 .endm .macro bilinear_interleave_src_dst \ mask_fmt, op, numpix, src0, src1, src01, dst0, dst1, dst01 bilinear_interleave_src_dst_&mask_fmt&_&op \ numpix, src0, src1, src01, dst0, dst1, dst01 .endm /* * Macros for applying masks to src pixels. (see combine_mask_u() function) * src, dst should be in interleaved form. * mask register should be in form (m0, m1, m2, m3). */ .macro bilinear_apply_mask_to_src_x \ numpix, src0, src1, src01, mask, \ tmp01, tmp23, tmp45, tmp67 .endm .macro bilinear_apply_mask_to_src_8 \ numpix, src0, src1, src01, mask, \ tmp01, tmp23, tmp45, tmp67 vmull.u8 tmp01, src0, mask vmull.u8 tmp23, src1, mask /* bubbles */ vrshr.u16 tmp45, tmp01, #8 vrshr.u16 tmp67, tmp23, #8 /* bubbles */ vraddhn.u16 src0, tmp45, tmp01 vraddhn.u16 src1, tmp67, tmp23 .endm .macro bilinear_apply_mask_to_src \ mask_fmt, numpix, src0, src1, src01, mask, \ tmp01, tmp23, tmp45, tmp67 bilinear_apply_mask_to_src_&mask_fmt \ numpix, src0, src1, src01, mask, \ tmp01, tmp23, tmp45, tmp67 .endm /* * Macros for combining src and destination pixels. * Interleave or not is depending on operator 'op'. */ .macro bilinear_combine_src \ numpix, src0, src1, src01, dst0, dst1, dst01, \ tmp01, tmp23, tmp45, tmp67, tmp8 .endm .macro bilinear_combine_over \ numpix, src0, src1, src01, dst0, dst1, dst01, \ tmp01, tmp23, tmp45, tmp67, tmp8 vdup.32 tmp8, src1[1] /* bubbles */ vmvn.8 tmp8, tmp8 /* bubbles */ vmull.u8 tmp01, dst0, tmp8 /* bubbles */ vmull.u8 tmp23, dst1, tmp8 /* bubbles */ vrshr.u16 tmp45, tmp01, #8 vrshr.u16 tmp67, tmp23, #8 /* bubbles */ vraddhn.u16 dst0, tmp45, tmp01 vraddhn.u16 dst1, tmp67, tmp23 /* bubbles */ vqadd.u8 src01, dst01, src01 .endm .macro bilinear_combine_add \ numpix, src0, src1, src01, dst0, dst1, dst01, \ tmp01, tmp23, tmp45, tmp67, tmp8 vqadd.u8 src01, dst01, src01 .endm .macro bilinear_combine \ op, numpix, src0, src1, src01, dst0, dst1, dst01, \ tmp01, tmp23, tmp45, tmp67, tmp8 bilinear_combine_&op \ numpix, src0, src1, src01, dst0, dst1, dst01, \ tmp01, tmp23, tmp45, tmp67, tmp8 .endm /* * Macros for final deinterleaving of destination pixels if needed. */ .macro bilinear_deinterleave numpix, dst0, dst1, dst01 vuzp.8 dst0, dst1 /* bubbles */ vuzp.8 dst0, dst1 .endm .macro bilinear_deinterleave_dst_x_src numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst_x_over numpix, dst0, dst1, dst01 bilinear_deinterleave numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst_x_add numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst_8_src numpix, dst0, dst1, dst01 bilinear_deinterleave numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst_8_over numpix, dst0, dst1, dst01 bilinear_deinterleave numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst_8_add numpix, dst0, dst1, dst01 bilinear_deinterleave numpix, dst0, dst1, dst01 .endm .macro bilinear_deinterleave_dst mask_fmt, op, numpix, dst0, dst1, dst01 bilinear_deinterleave_dst_&mask_fmt&_&op numpix, dst0, dst1, dst01 .endm .macro bilinear_interpolate_last_pixel src_fmt, mask_fmt, dst_fmt, op bilinear_load_&src_fmt d0, d1, d2 bilinear_load_mask mask_fmt, 1, d4 bilinear_load_dst dst_fmt, op, 1, d18, d19, q9 vmull.u8 q1, d0, d28 vmlal.u8 q1, d1, d29 /* 5 cycles bubble */ vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 /* 5 cycles bubble */ bilinear_duplicate_mask mask_fmt, 1, d4 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) /* 3 cycles bubble */ vmovn.u16 d0, q0 /* 1 cycle bubble */ bilinear_interleave_src_dst \ mask_fmt, op, 1, d0, d1, q0, d18, d19, q9 bilinear_apply_mask_to_src \ mask_fmt, 1, d0, d1, q0, d4, \ q3, q8, q10, q11 bilinear_combine \ op, 1, d0, d1, q0, d18, d19, q9, \ q3, q8, q10, q11, d5 bilinear_deinterleave_dst mask_fmt, op, 1, d0, d1, q0 bilinear_store_&dst_fmt 1, q2, q3 .endm .macro bilinear_interpolate_two_pixels src_fmt, mask_fmt, dst_fmt, op bilinear_load_and_vertical_interpolate_two_&src_fmt \ q1, q11, d0, d1, d20, d21, d22, d23 bilinear_load_mask mask_fmt, 2, d4 bilinear_load_dst dst_fmt, op, 2, d18, d19, q9 vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q10, d22, d31 vmlal.u16 q10, d23, d31 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) bilinear_duplicate_mask mask_fmt, 2, d4 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vmovn.u16 d0, q0 bilinear_interleave_src_dst \ mask_fmt, op, 2, d0, d1, q0, d18, d19, q9 bilinear_apply_mask_to_src \ mask_fmt, 2, d0, d1, q0, d4, \ q3, q8, q10, q11 bilinear_combine \ op, 2, d0, d1, q0, d18, d19, q9, \ q3, q8, q10, q11, d5 bilinear_deinterleave_dst mask_fmt, op, 2, d0, d1, q0 bilinear_store_&dst_fmt 2, q2, q3 .endm .macro bilinear_interpolate_four_pixels src_fmt, mask_fmt, dst_fmt, op bilinear_load_and_vertical_interpolate_four_&src_fmt \ q1, q11, d0, d1, d20, d21, d22, d23 \ q3, q9, d4, d5, d16, d17, d18, d19 pld [TMP1, PF_OFFS] sub TMP1, TMP1, STRIDE vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q10, d22, d31 vmlal.u16 q10, d23, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d6, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d6, d30 vmlal.u16 q2, d7, d30 vshll.u16 q8, d18, #BILINEAR_INTERPOLATION_BITS bilinear_load_mask mask_fmt, 4, d22 bilinear_load_dst dst_fmt, op, 4, d2, d3, q1 pld [TMP1, PF_OFFS] vmlsl.u16 q8, d18, d31 vmlal.u16 q8, d19, d31 vadd.u16 q12, q12, q13 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) bilinear_duplicate_mask mask_fmt, 4, d22 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vmovn.u16 d0, q0 vmovn.u16 d1, q2 vadd.u16 q12, q12, q13 bilinear_interleave_src_dst \ mask_fmt, op, 4, d0, d1, q0, d2, d3, q1 bilinear_apply_mask_to_src \ mask_fmt, 4, d0, d1, q0, d22, \ q3, q8, q9, q10 bilinear_combine \ op, 4, d0, d1, q0, d2, d3, q1, \ q3, q8, q9, q10, d23 bilinear_deinterleave_dst mask_fmt, op, 4, d0, d1, q0 bilinear_store_&dst_fmt 4, q2, q3 .endm .set BILINEAR_FLAG_USE_MASK, 1 .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 /* * Main template macro for generating NEON optimized bilinear scanline functions. * * Bilinear scanline generator macro take folling arguments: * fname - name of the function to generate * src_fmt - source color format (8888 or 0565) * dst_fmt - destination color format (8888 or 0565) * src/dst_bpp_shift - (1 << bpp_shift) is the size of src/dst pixel in bytes * process_last_pixel - code block that interpolate one pixel and does not * update horizontal weight * process_two_pixels - code block that interpolate two pixels and update * horizontal weight * process_four_pixels - code block that interpolate four pixels and update * horizontal weight * process_pixblock_head - head part of middle loop * process_pixblock_tail - tail part of middle loop * process_pixblock_tail_head - tail_head of middle loop * pixblock_size - number of pixels processed in a single middle loop * prefetch_distance - prefetch in the source image by that many pixels ahead */ .macro generate_bilinear_scanline_func \ fname, \ src_fmt, dst_fmt, src_bpp_shift, dst_bpp_shift, \ bilinear_process_last_pixel, \ bilinear_process_two_pixels, \ bilinear_process_four_pixels, \ bilinear_process_pixblock_head, \ bilinear_process_pixblock_tail, \ bilinear_process_pixblock_tail_head, \ pixblock_size, \ prefetch_distance, \ flags pixman_asm_function fname .if pixblock_size == 8 .elseif pixblock_size == 4 .else .error unsupported pixblock size .endif .if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 OUT .req r0 TOP .req r1 BOTTOM .req r2 WT .req r3 WB .req r4 X .req r5 UX .req r6 WIDTH .req ip TMP1 .req r3 TMP2 .req r4 PF_OFFS .req r7 TMP3 .req r8 TMP4 .req r9 STRIDE .req r2 mov ip, sp push {r4, r5, r6, r7, r8, r9} mov PF_OFFS, #prefetch_distance ldmia ip, {WB, X, UX, WIDTH} .else OUT .req r0 MASK .req r1 TOP .req r2 BOTTOM .req r3 WT .req r4 WB .req r5 X .req r6 UX .req r7 WIDTH .req ip TMP1 .req r4 TMP2 .req r5 PF_OFFS .req r8 TMP3 .req r9 TMP4 .req r10 STRIDE .req r3 .set prefetch_offset, prefetch_distance mov ip, sp push {r4, r5, r6, r7, r8, r9, r10, ip} mov PF_OFFS, #prefetch_distance ldmia ip, {WT, WB, X, UX, WIDTH} .endif mul PF_OFFS, PF_OFFS, UX .if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 vpush {d8-d15} .endif sub STRIDE, BOTTOM, TOP .unreq BOTTOM cmp WIDTH, #0 ble 3f vdup.u16 q12, X vdup.u16 q13, UX vdup.u8 d28, WT vdup.u8 d29, WB vadd.u16 d25, d25, d26 /* ensure good destination alignment */ cmp WIDTH, #1 blt 0f tst OUT, #(1 << dst_bpp_shift) beq 0f vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 bilinear_process_last_pixel sub WIDTH, WIDTH, #1 0: vadd.u16 q13, q13, q13 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 cmp WIDTH, #2 blt 0f tst OUT, #(1 << (dst_bpp_shift + 1)) beq 0f bilinear_process_two_pixels sub WIDTH, WIDTH, #2 0: .if pixblock_size == 8 cmp WIDTH, #4 blt 0f tst OUT, #(1 << (dst_bpp_shift + 2)) beq 0f bilinear_process_four_pixels sub WIDTH, WIDTH, #4 0: .endif subs WIDTH, WIDTH, #pixblock_size blt 1f mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) bilinear_process_pixblock_head subs WIDTH, WIDTH, #pixblock_size blt 5f 0: bilinear_process_pixblock_tail_head subs WIDTH, WIDTH, #pixblock_size bge 0b 5: bilinear_process_pixblock_tail 1: .if pixblock_size == 8 tst WIDTH, #4 beq 2f bilinear_process_four_pixels 2: .endif /* handle the remaining trailing pixels */ tst WIDTH, #2 beq 2f bilinear_process_two_pixels 2: tst WIDTH, #1 beq 3f bilinear_process_last_pixel 3: .if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 vpop {d8-d15} .endif .if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 pop {r4, r5, r6, r7, r8, r9} .else pop {r4, r5, r6, r7, r8, r9, r10, ip} .endif bx lr .unreq OUT .unreq TOP .unreq WT .unreq WB .unreq X .unreq UX .unreq WIDTH .unreq TMP1 .unreq TMP2 .unreq PF_OFFS .unreq TMP3 .unreq TMP4 .unreq STRIDE .if ((flags) & BILINEAR_FLAG_USE_MASK) != 0 .unreq MASK .endif .endfunc .endm /* src_8888_8_8888 */ .macro bilinear_src_8888_8_8888_process_last_pixel bilinear_interpolate_last_pixel 8888, 8, 8888, src .endm .macro bilinear_src_8888_8_8888_process_two_pixels bilinear_interpolate_two_pixels 8888, 8, 8888, src .endm .macro bilinear_src_8888_8_8888_process_four_pixels bilinear_interpolate_four_pixels 8888, 8, 8888, src .endm .macro bilinear_src_8888_8_8888_process_pixblock_head bilinear_src_8888_8_8888_process_four_pixels .endm .macro bilinear_src_8888_8_8888_process_pixblock_tail .endm .macro bilinear_src_8888_8_8888_process_pixblock_tail_head bilinear_src_8888_8_8888_process_pixblock_tail bilinear_src_8888_8_8888_process_pixblock_head .endm /* src_8888_8_0565 */ .macro bilinear_src_8888_8_0565_process_last_pixel bilinear_interpolate_last_pixel 8888, 8, 0565, src .endm .macro bilinear_src_8888_8_0565_process_two_pixels bilinear_interpolate_two_pixels 8888, 8, 0565, src .endm .macro bilinear_src_8888_8_0565_process_four_pixels bilinear_interpolate_four_pixels 8888, 8, 0565, src .endm .macro bilinear_src_8888_8_0565_process_pixblock_head bilinear_src_8888_8_0565_process_four_pixels .endm .macro bilinear_src_8888_8_0565_process_pixblock_tail .endm .macro bilinear_src_8888_8_0565_process_pixblock_tail_head bilinear_src_8888_8_0565_process_pixblock_tail bilinear_src_8888_8_0565_process_pixblock_head .endm /* src_0565_8_x888 */ .macro bilinear_src_0565_8_x888_process_last_pixel bilinear_interpolate_last_pixel 0565, 8, 8888, src .endm .macro bilinear_src_0565_8_x888_process_two_pixels bilinear_interpolate_two_pixels 0565, 8, 8888, src .endm .macro bilinear_src_0565_8_x888_process_four_pixels bilinear_interpolate_four_pixels 0565, 8, 8888, src .endm .macro bilinear_src_0565_8_x888_process_pixblock_head bilinear_src_0565_8_x888_process_four_pixels .endm .macro bilinear_src_0565_8_x888_process_pixblock_tail .endm .macro bilinear_src_0565_8_x888_process_pixblock_tail_head bilinear_src_0565_8_x888_process_pixblock_tail bilinear_src_0565_8_x888_process_pixblock_head .endm /* src_0565_8_0565 */ .macro bilinear_src_0565_8_0565_process_last_pixel bilinear_interpolate_last_pixel 0565, 8, 0565, src .endm .macro bilinear_src_0565_8_0565_process_two_pixels bilinear_interpolate_two_pixels 0565, 8, 0565, src .endm .macro bilinear_src_0565_8_0565_process_four_pixels bilinear_interpolate_four_pixels 0565, 8, 0565, src .endm .macro bilinear_src_0565_8_0565_process_pixblock_head bilinear_src_0565_8_0565_process_four_pixels .endm .macro bilinear_src_0565_8_0565_process_pixblock_tail .endm .macro bilinear_src_0565_8_0565_process_pixblock_tail_head bilinear_src_0565_8_0565_process_pixblock_tail bilinear_src_0565_8_0565_process_pixblock_head .endm /* over_8888_8888 */ .macro bilinear_over_8888_8888_process_last_pixel bilinear_interpolate_last_pixel 8888, x, 8888, over .endm .macro bilinear_over_8888_8888_process_two_pixels bilinear_interpolate_two_pixels 8888, x, 8888, over .endm .macro bilinear_over_8888_8888_process_four_pixels bilinear_interpolate_four_pixels 8888, x, 8888, over .endm .macro bilinear_over_8888_8888_process_pixblock_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vld1.32 {d22}, [TMP1], STRIDE vld1.32 {d23}, [TMP1] mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 vmull.u8 q8, d22, d28 vmlal.u8 q8, d23, d29 vld1.32 {d22}, [TMP2], STRIDE vld1.32 {d23}, [TMP2] mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmull.u8 q9, d22, d28 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 .endm .macro bilinear_over_8888_8888_process_pixblock_tail vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d2, d3}, [OUT, :128] pld [OUT, #(prefetch_offset * 4)] vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vmovn.u16 d6, q0 vmovn.u16 d7, q2 vuzp.8 d6, d7 vuzp.8 d2, d3 vuzp.8 d6, d7 vuzp.8 d2, d3 vdup.32 d4, d7[1] vmvn.8 d4, d4 vmull.u8 q11, d2, d4 vmull.u8 q2, d3, d4 vrshr.u16 q1, q11, #8 vrshr.u16 q10, q2, #8 vraddhn.u16 d2, q1, q11 vraddhn.u16 d3, q10, q2 vqadd.u8 q3, q1, q3 vuzp.8 d6, d7 vuzp.8 d6, d7 vadd.u16 q12, q12, q13 vst1.32 {d6, d7}, [OUT, :128]! .endm .macro bilinear_over_8888_8888_process_pixblock_tail_head vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 vmlsl.u16 q2, d20, d30 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vld1.32 {d20}, [TMP1], STRIDE vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d2, d3}, [OUT, :128] pld [OUT, PF_OFFS] vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d22}, [TMP2], STRIDE vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vmovn.u16 d6, q0 vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vmovn.u16 d7, q2 vld1.32 {d22}, [TMP3], STRIDE vuzp.8 d6, d7 vuzp.8 d2, d3 vuzp.8 d6, d7 vuzp.8 d2, d3 vdup.32 d4, d7[1] vld1.32 {d23}, [TMP3] vmvn.8 d4, d4 vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vmull.u8 q11, d2, d4 vmull.u8 q2, d3, d4 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d16, d30 vrshr.u16 q1, q11, #8 vmlal.u16 q0, d17, d30 vrshr.u16 q8, q2, #8 vraddhn.u16 d2, q1, q11 vraddhn.u16 d3, q8, q2 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vqadd.u8 q3, q1, q3 vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vuzp.8 d6, d7 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vuzp.8 d6, d7 vmlsl.u16 q1, d18, d31 vadd.u16 q12, q12, q13 vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vst1.32 {d6, d7}, [OUT, :128]! .endm /* over_8888_8_8888 */ .macro bilinear_over_8888_8_8888_process_last_pixel bilinear_interpolate_last_pixel 8888, 8, 8888, over .endm .macro bilinear_over_8888_8_8888_process_two_pixels bilinear_interpolate_two_pixels 8888, 8, 8888, over .endm .macro bilinear_over_8888_8_8888_process_four_pixels bilinear_interpolate_four_pixels 8888, 8, 8888, over .endm .macro bilinear_over_8888_8_8888_process_pixblock_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 vld1.32 {d0}, [TMP1], STRIDE mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vld1.32 {d1}, [TMP1] mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 vld1.32 {d2}, [TMP2], STRIDE mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vld1.32 {d3}, [TMP2] vmull.u8 q2, d0, d28 vmull.u8 q3, d2, d28 vmlal.u8 q2, d1, d29 vmlal.u8 q3, d3, d29 vshll.u16 q0, d4, #BILINEAR_INTERPOLATION_BITS vshll.u16 q1, d6, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d4, d30 vmlsl.u16 q1, d6, d31 vmlal.u16 q0, d5, d30 vmlal.u16 q1, d7, d31 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d2}, [TMP3], STRIDE vld1.32 {d3}, [TMP3] pld [TMP4, PF_OFFS] vld1.32 {d4}, [TMP4], STRIDE vld1.32 {d5}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q3, d2, d28 vmlal.u8 q3, d3, d29 vmull.u8 q1, d4, d28 vmlal.u8 q1, d5, d29 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d22[0]}, [MASK]! pld [MASK, #prefetch_offset] vadd.u16 q12, q12, q13 vmovn.u16 d16, q0 .endm .macro bilinear_over_8888_8_8888_process_pixblock_tail vshll.u16 q9, d6, #BILINEAR_INTERPOLATION_BITS vshll.u16 q10, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q9, d6, d30 vmlsl.u16 q10, d2, d31 vmlal.u16 q9, d7, d30 vmlal.u16 q10, d3, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vdup.32 d22, d22[0] vshrn.u32 d18, q9, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d19, q10, #(2 * BILINEAR_INTERPOLATION_BITS) vmovn.u16 d17, q9 vld1.32 {d18, d19}, [OUT, :128] pld [OUT, PF_OFFS] vuzp.8 d16, d17 vuzp.8 d18, d19 vuzp.8 d16, d17 vuzp.8 d18, d19 vmull.u8 q10, d16, d22 vmull.u8 q11, d17, d22 vrsra.u16 q10, q10, #8 vrsra.u16 q11, q11, #8 vrshrn.u16 d16, q10, #8 vrshrn.u16 d17, q11, #8 vdup.32 d22, d17[1] vmvn.8 d22, d22 vmull.u8 q10, d18, d22 vmull.u8 q11, d19, d22 vrshr.u16 q9, q10, #8 vrshr.u16 q0, q11, #8 vraddhn.u16 d18, q9, q10 vraddhn.u16 d19, q0, q11 vqadd.u8 q9, q8, q9 vuzp.8 d18, d19 vuzp.8 d18, d19 vst1.32 {d18, d19}, [OUT, :128]! .endm .macro bilinear_over_8888_8_8888_process_pixblock_tail_head vshll.u16 q9, d6, #BILINEAR_INTERPOLATION_BITS mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 vshll.u16 q10, d2, #BILINEAR_INTERPOLATION_BITS vld1.32 {d0}, [TMP1], STRIDE mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlsl.u16 q9, d6, d30 vmlsl.u16 q10, d2, d31 vld1.32 {d1}, [TMP1] mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 vmlal.u16 q9, d7, d30 vmlal.u16 q10, d3, d31 vld1.32 {d2}, [TMP2], STRIDE mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vld1.32 {d3}, [TMP2] vdup.32 d22, d22[0] vshrn.u32 d18, q9, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d19, q10, #(2 * BILINEAR_INTERPOLATION_BITS) vmull.u8 q2, d0, d28 vmull.u8 q3, d2, d28 vmovn.u16 d17, q9 vld1.32 {d18, d19}, [OUT, :128] pld [OUT, #(prefetch_offset * 4)] vmlal.u8 q2, d1, d29 vmlal.u8 q3, d3, d29 vuzp.8 d16, d17 vuzp.8 d18, d19 vshll.u16 q0, d4, #BILINEAR_INTERPOLATION_BITS vshll.u16 q1, d6, #BILINEAR_INTERPOLATION_BITS vuzp.8 d16, d17 vuzp.8 d18, d19 vmlsl.u16 q0, d4, d30 vmlsl.u16 q1, d6, d31 vmull.u8 q10, d16, d22 vmull.u8 q11, d17, d22 vmlal.u16 q0, d5, d30 vmlal.u16 q1, d7, d31 vrsra.u16 q10, q10, #8 vrsra.u16 q11, q11, #8 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vrshrn.u16 d16, q10, #8 vrshrn.u16 d17, q11, #8 vld1.32 {d2}, [TMP3], STRIDE vdup.32 d22, d17[1] vld1.32 {d3}, [TMP3] vmvn.8 d22, d22 pld [TMP4, PF_OFFS] vld1.32 {d4}, [TMP4], STRIDE vmull.u8 q10, d18, d22 vmull.u8 q11, d19, d22 vld1.32 {d5}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q3, d2, d28 vrshr.u16 q9, q10, #8 vrshr.u16 q15, q11, #8 vmlal.u8 q3, d3, d29 vmull.u8 q1, d4, d28 vraddhn.u16 d18, q9, q10 vraddhn.u16 d19, q15, q11 vmlal.u8 q1, d5, d29 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vqadd.u8 q9, q8, q9 vld1.32 {d22[0]}, [MASK]! vuzp.8 d18, d19 vadd.u16 q12, q12, q13 vuzp.8 d18, d19 vmovn.u16 d16, q0 vst1.32 {d18, d19}, [OUT, :128]! .endm /* add_8888_8888 */ .macro bilinear_add_8888_8888_process_last_pixel bilinear_interpolate_last_pixel 8888, x, 8888, add .endm .macro bilinear_add_8888_8888_process_two_pixels bilinear_interpolate_two_pixels 8888, x, 8888, add .endm .macro bilinear_add_8888_8888_process_four_pixels bilinear_interpolate_four_pixels 8888, x, 8888, add .endm .macro bilinear_add_8888_8888_process_pixblock_head bilinear_add_8888_8888_process_four_pixels .endm .macro bilinear_add_8888_8888_process_pixblock_tail .endm .macro bilinear_add_8888_8888_process_pixblock_tail_head bilinear_add_8888_8888_process_pixblock_tail bilinear_add_8888_8888_process_pixblock_head .endm /* add_8888_8_8888 */ .macro bilinear_add_8888_8_8888_process_last_pixel bilinear_interpolate_last_pixel 8888, 8, 8888, add .endm .macro bilinear_add_8888_8_8888_process_two_pixels bilinear_interpolate_two_pixels 8888, 8, 8888, add .endm .macro bilinear_add_8888_8_8888_process_four_pixels bilinear_interpolate_four_pixels 8888, 8, 8888, add .endm .macro bilinear_add_8888_8_8888_process_pixblock_head bilinear_add_8888_8_8888_process_four_pixels .endm .macro bilinear_add_8888_8_8888_process_pixblock_tail .endm .macro bilinear_add_8888_8_8888_process_pixblock_tail_head bilinear_add_8888_8_8888_process_pixblock_tail bilinear_add_8888_8_8888_process_pixblock_head .endm /* Bilinear scanline functions */ generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_neon, \ 8888, 8888, 2, 2, \ bilinear_src_8888_8_8888_process_last_pixel, \ bilinear_src_8888_8_8888_process_two_pixels, \ bilinear_src_8888_8_8888_process_four_pixels, \ bilinear_src_8888_8_8888_process_pixblock_head, \ bilinear_src_8888_8_8888_process_pixblock_tail, \ bilinear_src_8888_8_8888_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_neon, \ 8888, 0565, 2, 1, \ bilinear_src_8888_8_0565_process_last_pixel, \ bilinear_src_8888_8_0565_process_two_pixels, \ bilinear_src_8888_8_0565_process_four_pixels, \ bilinear_src_8888_8_0565_process_pixblock_head, \ bilinear_src_8888_8_0565_process_pixblock_tail, \ bilinear_src_8888_8_0565_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_neon, \ 0565, 8888, 1, 2, \ bilinear_src_0565_8_x888_process_last_pixel, \ bilinear_src_0565_8_x888_process_two_pixels, \ bilinear_src_0565_8_x888_process_four_pixels, \ bilinear_src_0565_8_x888_process_pixblock_head, \ bilinear_src_0565_8_x888_process_pixblock_tail, \ bilinear_src_0565_8_x888_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_neon, \ 0565, 0565, 1, 1, \ bilinear_src_0565_8_0565_process_last_pixel, \ bilinear_src_0565_8_0565_process_two_pixels, \ bilinear_src_0565_8_0565_process_four_pixels, \ bilinear_src_0565_8_0565_process_pixblock_head, \ bilinear_src_0565_8_0565_process_pixblock_tail, \ bilinear_src_0565_8_0565_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_neon, \ 8888, 8888, 2, 2, \ bilinear_over_8888_8888_process_last_pixel, \ bilinear_over_8888_8888_process_two_pixels, \ bilinear_over_8888_8888_process_four_pixels, \ bilinear_over_8888_8888_process_pixblock_head, \ bilinear_over_8888_8888_process_pixblock_tail, \ bilinear_over_8888_8888_process_pixblock_tail_head, \ 4, 28, 0 generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_neon, \ 8888, 8888, 2, 2, \ bilinear_over_8888_8_8888_process_last_pixel, \ bilinear_over_8888_8_8888_process_two_pixels, \ bilinear_over_8888_8_8888_process_four_pixels, \ bilinear_over_8888_8_8888_process_pixblock_head, \ bilinear_over_8888_8_8888_process_pixblock_tail, \ bilinear_over_8888_8_8888_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_neon, \ 8888, 8888, 2, 2, \ bilinear_add_8888_8888_process_last_pixel, \ bilinear_add_8888_8888_process_two_pixels, \ bilinear_add_8888_8888_process_four_pixels, \ bilinear_add_8888_8888_process_pixblock_head, \ bilinear_add_8888_8888_process_pixblock_tail, \ bilinear_add_8888_8888_process_pixblock_tail_head, \ 4, 28, 0 generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_neon, \ 8888, 8888, 2, 2, \ bilinear_add_8888_8_8888_process_last_pixel, \ bilinear_add_8888_8_8888_process_two_pixels, \ bilinear_add_8888_8_8888_process_four_pixels, \ bilinear_add_8888_8_8888_process_pixblock_head, \ bilinear_add_8888_8_8888_process_pixblock_tail, \ bilinear_add_8888_8_8888_process_pixblock_tail_head, \ 4, 28, BILINEAR_FLAG_USE_MASK Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-neon-asm.S000066400000000000000000003740031271037650300271230ustar00rootroot00000000000000/* * Copyright © 2009 Nokia Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) */ /* * This file contains implementations of NEON optimized pixel processing * functions. There is no full and detailed tutorial, but some functions * (those which are exposing some new or interesting features) are * extensively commented and can be used as examples. * * You may want to have a look at the comments for following functions: * - pixman_composite_over_8888_0565_asm_neon * - pixman_composite_over_n_8_0565_asm_neon */ /* Prevent the stack from becoming executable for no reason... */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif .text .fpu neon .arch armv7a .object_arch armv4 .eabi_attribute 10, 0 /* suppress Tag_FP_arch */ .eabi_attribute 12, 0 /* suppress Tag_Advanced_SIMD_arch */ .arm .altmacro .p2align 2 #include "pixman-private.h" #include "pixman-arm-neon-asm.h" /* Global configuration options and preferences */ /* * The code can optionally make use of unaligned memory accesses to improve * performance of handling leading/trailing pixels for each scanline. * Configuration variable RESPECT_STRICT_ALIGNMENT can be set to 0 for * example in linux if unaligned memory accesses are not configured to * generate.exceptions. */ .set RESPECT_STRICT_ALIGNMENT, 1 /* * Set default prefetch type. There is a choice between the following options: * * PREFETCH_TYPE_NONE (may be useful for the ARM cores where PLD is set to work * as NOP to workaround some HW bugs or for whatever other reason) * * PREFETCH_TYPE_SIMPLE (may be useful for simple single-issue ARM cores where * advanced prefetch intruduces heavy overhead) * * PREFETCH_TYPE_ADVANCED (useful for superscalar cores such as ARM Cortex-A8 * which can run ARM and NEON instructions simultaneously so that extra ARM * instructions do not add (many) extra cycles, but improve prefetch efficiency) * * Note: some types of function can't support advanced prefetch and fallback * to simple one (those which handle 24bpp pixels) */ .set PREFETCH_TYPE_DEFAULT, PREFETCH_TYPE_ADVANCED /* Prefetch distance in pixels for simple prefetch */ .set PREFETCH_DISTANCE_SIMPLE, 64 /* * Implementation of pixman_composite_over_8888_0565_asm_neon * * This function takes a8r8g8b8 source buffer, r5g6b5 destination buffer and * performs OVER compositing operation. Function fast_composite_over_8888_0565 * from pixman-fast-path.c does the same in C and can be used as a reference. * * First we need to have some NEON assembly code which can do the actual * operation on the pixels and provide it to the template macro. * * Template macro quite conveniently takes care of emitting all the necessary * code for memory reading and writing (including quite tricky cases of * handling unaligned leading/trailing pixels), so we only need to deal with * the data in NEON registers. * * NEON registers allocation in general is recommented to be the following: * d0, d1, d2, d3 - contain loaded source pixel data * d4, d5, d6, d7 - contain loaded destination pixels (if they are needed) * d24, d25, d26, d27 - contain loading mask pixel data (if mask is used) * d28, d29, d30, d31 - place for storing the result (destination pixels) * * As can be seen above, four 64-bit NEON registers are used for keeping * intermediate pixel data and up to 8 pixels can be processed in one step * for 32bpp formats (16 pixels for 16bpp, 32 pixels for 8bpp). * * This particular function uses the following registers allocation: * d0, d1, d2, d3 - contain loaded source pixel data * d4, d5 - contain loaded destination pixels (they are needed) * d28, d29 - place for storing the result (destination pixels) */ /* * Step one. We need to have some code to do some arithmetics on pixel data. * This is implemented as a pair of macros: '*_head' and '*_tail'. When used * back-to-back, they take pixel data from {d0, d1, d2, d3} and {d4, d5}, * perform all the needed calculations and write the result to {d28, d29}. * The rationale for having two macros and not just one will be explained * later. In practice, any single monolitic function which does the work can * be split into two parts in any arbitrary way without affecting correctness. * * There is one special trick here too. Common template macro can optionally * make our life a bit easier by doing R, G, B, A color components * deinterleaving for 32bpp pixel formats (and this feature is used in * 'pixman_composite_over_8888_0565_asm_neon' function). So it means that * instead of having 8 packed pixels in {d0, d1, d2, d3} registers, we * actually use d0 register for blue channel (a vector of eight 8-bit * values), d1 register for green, d2 for red and d3 for alpha. This * simple conversion can be also done with a few NEON instructions: * * Packed to planar conversion: * vuzp.8 d0, d1 * vuzp.8 d2, d3 * vuzp.8 d1, d3 * vuzp.8 d0, d2 * * Planar to packed conversion: * vzip.8 d0, d2 * vzip.8 d1, d3 * vzip.8 d2, d3 * vzip.8 d0, d1 * * But pixel can be loaded directly in planar format using VLD4.8 NEON * instruction. It is 1 cycle slower than VLD1.32, so this is not always * desirable, that's why deinterleaving is optional. * * But anyway, here is the code: */ .macro pixman_composite_over_8888_0565_process_pixblock_head /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format and put data into d6 - red, d7 - green, d30 - blue */ vshrn.u16 d6, q2, #8 vshrn.u16 d7, q2, #3 vsli.u16 q2, q2, #5 vsri.u8 d6, d6, #5 vmvn.8 d3, d3 /* invert source alpha */ vsri.u8 d7, d7, #6 vshrn.u16 d30, q2, #2 /* now do alpha blending, storing results in 8-bit planar format into d16 - red, d19 - green, d18 - blue */ vmull.u8 q10, d3, d6 vmull.u8 q11, d3, d7 vmull.u8 q12, d3, d30 vrshr.u16 q13, q10, #8 vrshr.u16 q3, q11, #8 vrshr.u16 q15, q12, #8 vraddhn.u16 d20, q10, q13 vraddhn.u16 d23, q11, q3 vraddhn.u16 d22, q12, q15 .endm .macro pixman_composite_over_8888_0565_process_pixblock_tail /* ... continue alpha blending */ vqadd.u8 d16, d2, d20 vqadd.u8 q9, q0, q11 /* convert the result to r5g6b5 and store it into {d28, d29} */ vshll.u8 q14, d16, #8 vshll.u8 q8, d19, #8 vshll.u8 q9, d18, #8 vsri.u16 q14, q8, #5 vsri.u16 q14, q9, #11 .endm /* * OK, now we got almost everything that we need. Using the above two * macros, the work can be done right. But now we want to optimize * it a bit. ARM Cortex-A8 is an in-order core, and benefits really * a lot from good code scheduling and software pipelining. * * Let's construct some code, which will run in the core main loop. * Some pseudo-code of the main loop will look like this: * head * while (...) { * tail * head * } * tail * * It may look a bit weird, but this setup allows to hide instruction * latencies better and also utilize dual-issue capability more * efficiently (make pairs of load-store and ALU instructions). * * So what we need now is a '*_tail_head' macro, which will be used * in the core main loop. A trivial straightforward implementation * of this macro would look like this: * * pixman_composite_over_8888_0565_process_pixblock_tail * vst1.16 {d28, d29}, [DST_W, :128]! * vld1.16 {d4, d5}, [DST_R, :128]! * vld4.32 {d0, d1, d2, d3}, [SRC]! * pixman_composite_over_8888_0565_process_pixblock_head * cache_preload 8, 8 * * Now it also got some VLD/VST instructions. We simply can't move from * processing one block of pixels to the other one with just arithmetics. * The previously processed data needs to be written to memory and new * data needs to be fetched. Fortunately, this main loop does not deal * with partial leading/trailing pixels and can load/store a full block * of pixels in a bulk. Additionally, destination buffer is already * 16 bytes aligned here (which is good for performance). * * New things here are DST_R, DST_W, SRC and MASK identifiers. These * are the aliases for ARM registers which are used as pointers for * accessing data. We maintain separate pointers for reading and writing * destination buffer (DST_R and DST_W). * * Another new thing is 'cache_preload' macro. It is used for prefetching * data into CPU L2 cache and improve performance when dealing with large * images which are far larger than cache size. It uses one argument * (actually two, but they need to be the same here) - number of pixels * in a block. Looking into 'pixman-arm-neon-asm.h' can provide some * details about this macro. Moreover, if good performance is needed * the code from this macro needs to be copied into '*_tail_head' macro * and mixed with the rest of code for optimal instructions scheduling. * We are actually doing it below. * * Now after all the explanations, here is the optimized code. * Different instruction streams (originaling from '*_head', '*_tail' * and 'cache_preload' macro) use different indentation levels for * better readability. Actually taking the code from one of these * indentation levels and ignoring a few VLD/VST instructions would * result in exactly the code from '*_head', '*_tail' or 'cache_preload' * macro! */ #if 1 .macro pixman_composite_over_8888_0565_process_pixblock_tail_head vqadd.u8 d16, d2, d20 vld1.16 {d4, d5}, [DST_R, :128]! vqadd.u8 q9, q0, q11 vshrn.u16 d6, q2, #8 fetch_src_pixblock vshrn.u16 d7, q2, #3 vsli.u16 q2, q2, #5 vshll.u8 q14, d16, #8 PF add PF_X, PF_X, #8 vshll.u8 q8, d19, #8 PF tst PF_CTL, #0xF vsri.u8 d6, d6, #5 PF addne PF_X, PF_X, #8 vmvn.8 d3, d3 PF subne PF_CTL, PF_CTL, #1 vsri.u8 d7, d7, #6 vshrn.u16 d30, q2, #2 vmull.u8 q10, d3, d6 PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] vmull.u8 q11, d3, d7 vmull.u8 q12, d3, d30 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vsri.u16 q14, q8, #5 PF cmp PF_X, ORIG_W vshll.u8 q9, d18, #8 vrshr.u16 q13, q10, #8 PF subge PF_X, PF_X, ORIG_W vrshr.u16 q3, q11, #8 vrshr.u16 q15, q12, #8 PF subges PF_CTL, PF_CTL, #0x10 vsri.u16 q14, q9, #11 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! vraddhn.u16 d20, q10, q13 vraddhn.u16 d23, q11, q3 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vraddhn.u16 d22, q12, q15 vst1.16 {d28, d29}, [DST_W, :128]! .endm #else /* If we did not care much about the performance, we would just use this... */ .macro pixman_composite_over_8888_0565_process_pixblock_tail_head pixman_composite_over_8888_0565_process_pixblock_tail vst1.16 {d28, d29}, [DST_W, :128]! vld1.16 {d4, d5}, [DST_R, :128]! fetch_src_pixblock pixman_composite_over_8888_0565_process_pixblock_head cache_preload 8, 8 .endm #endif /* * And now the final part. We are using 'generate_composite_function' macro * to put all the stuff together. We are specifying the name of the function * which we want to get, number of bits per pixel for the source, mask and * destination (0 if unused, like mask in this case). Next come some bit * flags: * FLAG_DST_READWRITE - tells that the destination buffer is both read * and written, for write-only buffer we would use * FLAG_DST_WRITEONLY flag instead * FLAG_DEINTERLEAVE_32BPP - tells that we prefer to work with planar data * and separate color channels for 32bpp format. * The next things are: * - the number of pixels processed per iteration (8 in this case, because * that's the maximum what can fit into four 64-bit NEON registers). * - prefetch distance, measured in pixel blocks. In this case it is 5 times * by 8 pixels. That would be 40 pixels, or up to 160 bytes. Optimal * prefetch distance can be selected by running some benchmarks. * * After that we specify some macros, these are 'default_init', * 'default_cleanup' here which are empty (but it is possible to have custom * init/cleanup macros to be able to save/restore some extra NEON registers * like d8-d15 or do anything else) followed by * 'pixman_composite_over_8888_0565_process_pixblock_head', * 'pixman_composite_over_8888_0565_process_pixblock_tail' and * 'pixman_composite_over_8888_0565_process_pixblock_tail_head' * which we got implemented above. * * The last part is the NEON registers allocation scheme. */ generate_composite_function \ pixman_composite_over_8888_0565_asm_neon, 32, 0, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_over_8888_0565_process_pixblock_head, \ pixman_composite_over_8888_0565_process_pixblock_tail, \ pixman_composite_over_8888_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 24 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_over_n_0565_process_pixblock_head /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format and put data into d6 - red, d7 - green, d30 - blue */ vshrn.u16 d6, q2, #8 vshrn.u16 d7, q2, #3 vsli.u16 q2, q2, #5 vsri.u8 d6, d6, #5 vsri.u8 d7, d7, #6 vshrn.u16 d30, q2, #2 /* now do alpha blending, storing results in 8-bit planar format into d16 - red, d19 - green, d18 - blue */ vmull.u8 q10, d3, d6 vmull.u8 q11, d3, d7 vmull.u8 q12, d3, d30 vrshr.u16 q13, q10, #8 vrshr.u16 q3, q11, #8 vrshr.u16 q15, q12, #8 vraddhn.u16 d20, q10, q13 vraddhn.u16 d23, q11, q3 vraddhn.u16 d22, q12, q15 .endm .macro pixman_composite_over_n_0565_process_pixblock_tail /* ... continue alpha blending */ vqadd.u8 d16, d2, d20 vqadd.u8 q9, q0, q11 /* convert the result to r5g6b5 and store it into {d28, d29} */ vshll.u8 q14, d16, #8 vshll.u8 q8, d19, #8 vshll.u8 q9, d18, #8 vsri.u16 q14, q8, #5 vsri.u16 q14, q9, #11 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_n_0565_process_pixblock_tail_head pixman_composite_over_n_0565_process_pixblock_tail vld1.16 {d4, d5}, [DST_R, :128]! vst1.16 {d28, d29}, [DST_W, :128]! pixman_composite_over_n_0565_process_pixblock_head cache_preload 8, 8 .endm .macro pixman_composite_over_n_0565_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d3[0]}, [DUMMY] vdup.8 d0, d3[0] vdup.8 d1, d3[1] vdup.8 d2, d3[2] vdup.8 d3, d3[3] vmvn.8 d3, d3 /* invert source alpha */ .endm generate_composite_function \ pixman_composite_over_n_0565_asm_neon, 0, 0, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_0565_init, \ default_cleanup, \ pixman_composite_over_n_0565_process_pixblock_head, \ pixman_composite_over_n_0565_process_pixblock_tail, \ pixman_composite_over_n_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 24 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_8888_0565_process_pixblock_head vshll.u8 q8, d1, #8 vshll.u8 q14, d2, #8 vshll.u8 q9, d0, #8 .endm .macro pixman_composite_src_8888_0565_process_pixblock_tail vsri.u16 q14, q8, #5 vsri.u16 q14, q9, #11 .endm .macro pixman_composite_src_8888_0565_process_pixblock_tail_head vsri.u16 q14, q8, #5 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF fetch_src_pixblock PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vsri.u16 q14, q9, #11 PF cmp PF_X, ORIG_W PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] vshll.u8 q8, d1, #8 vst1.16 {d28, d29}, [DST_W, :128]! PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 vshll.u8 q14, d2, #8 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! vshll.u8 q9, d0, #8 .endm generate_composite_function \ pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_8888_0565_process_pixblock_head, \ pixman_composite_src_8888_0565_process_pixblock_tail, \ pixman_composite_src_8888_0565_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_src_0565_8888_process_pixblock_head vshrn.u16 d30, q0, #8 vshrn.u16 d29, q0, #3 vsli.u16 q0, q0, #5 vmov.u8 d31, #255 vsri.u8 d30, d30, #5 vsri.u8 d29, d29, #6 vshrn.u16 d28, q0, #2 .endm .macro pixman_composite_src_0565_8888_process_pixblock_tail .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_src_0565_8888_process_pixblock_tail_head pixman_composite_src_0565_8888_process_pixblock_tail vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! fetch_src_pixblock pixman_composite_src_0565_8888_process_pixblock_head cache_preload 8, 8 .endm generate_composite_function \ pixman_composite_src_0565_8888_asm_neon, 16, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_0565_8888_process_pixblock_head, \ pixman_composite_src_0565_8888_process_pixblock_tail, \ pixman_composite_src_0565_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_add_8_8_process_pixblock_head vqadd.u8 q14, q0, q2 vqadd.u8 q15, q1, q3 .endm .macro pixman_composite_add_8_8_process_pixblock_tail .endm .macro pixman_composite_add_8_8_process_pixblock_tail_head fetch_src_pixblock PF add PF_X, PF_X, #32 PF tst PF_CTL, #0xF vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! PF addne PF_X, PF_X, #32 PF subne PF_CTL, PF_CTL, #1 vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! PF cmp PF_X, ORIG_W PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 vqadd.u8 q14, q0, q2 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vqadd.u8 q15, q1, q3 .endm generate_composite_function \ pixman_composite_add_8_8_asm_neon, 8, 0, 8, \ FLAG_DST_READWRITE, \ 32, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_add_8_8_process_pixblock_head, \ pixman_composite_add_8_8_process_pixblock_tail, \ pixman_composite_add_8_8_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_add_8888_8888_process_pixblock_tail_head fetch_src_pixblock PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF vld1.32 {d4, d5, d6, d7}, [DST_R, :128]! PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vst1.32 {d28, d29, d30, d31}, [DST_W, :128]! PF cmp PF_X, ORIG_W PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 vqadd.u8 q14, q0, q2 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vqadd.u8 q15, q1, q3 .endm generate_composite_function \ pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_add_8_8_process_pixblock_head, \ pixman_composite_add_8_8_process_pixblock_tail, \ pixman_composite_add_8888_8888_process_pixblock_tail_head generate_composite_function_single_scanline \ pixman_composite_scanline_add_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_add_8_8_process_pixblock_head, \ pixman_composite_add_8_8_process_pixblock_tail, \ pixman_composite_add_8888_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_out_reverse_8888_8888_process_pixblock_head vmvn.8 d24, d3 /* get inverted alpha */ /* do alpha blending */ vmull.u8 q8, d24, d4 vmull.u8 q9, d24, d5 vmull.u8 q10, d24, d6 vmull.u8 q11, d24, d7 .endm .macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 .endm .macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vrshr.u16 q14, q8, #8 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 PF cmp PF_X, ORIG_W vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 fetch_src_pixblock PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] vmvn.8 d22, d3 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! PF subge PF_X, PF_X, ORIG_W vmull.u8 q8, d22, d4 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q9, d22, d5 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! vmull.u8 q10, d22, d6 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vmull.u8 q11, d22, d7 .endm generate_composite_function_single_scanline \ pixman_composite_scanline_out_reverse_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_out_reverse_8888_8888_process_pixblock_head, \ pixman_composite_out_reverse_8888_8888_process_pixblock_tail, \ pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_8888_8888_process_pixblock_head pixman_composite_out_reverse_8888_8888_process_pixblock_head .endm .macro pixman_composite_over_8888_8888_process_pixblock_tail pixman_composite_out_reverse_8888_8888_process_pixblock_tail vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm .macro pixman_composite_over_8888_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vrshr.u16 q14, q8, #8 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 PF cmp PF_X, ORIG_W vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 fetch_src_pixblock PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] vmvn.8 d22, d3 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! PF subge PF_X, PF_X, ORIG_W vmull.u8 q8, d22, d4 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q9, d22, d5 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! vmull.u8 q10, d22, d6 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vmull.u8 q11, d22, d7 .endm generate_composite_function \ pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_over_8888_8888_process_pixblock_head, \ pixman_composite_over_8888_8888_process_pixblock_tail, \ pixman_composite_over_8888_8888_process_pixblock_tail_head generate_composite_function_single_scanline \ pixman_composite_scanline_over_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_over_8888_8888_process_pixblock_head, \ pixman_composite_over_8888_8888_process_pixblock_tail, \ pixman_composite_over_8888_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_n_8888_process_pixblock_head /* deinterleaved source pixels in {d0, d1, d2, d3} */ /* inverted alpha in {d24} */ /* destination pixels in {d4, d5, d6, d7} */ vmull.u8 q8, d24, d4 vmull.u8 q9, d24, d5 vmull.u8 q10, d24, d6 vmull.u8 q11, d24, d7 .endm .macro pixman_composite_over_n_8888_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q2, q10, #8 vrshr.u16 q3, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q2, q10 vraddhn.u16 d31, q3, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm .macro pixman_composite_over_n_8888_process_pixblock_tail_head vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q2, q10, #8 vrshr.u16 q3, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q2, q10 vraddhn.u16 d31, q3, q11 vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vqadd.u8 q14, q0, q14 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0x0F PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vqadd.u8 q15, q1, q15 PF cmp PF_X, ORIG_W vmull.u8 q8, d24, d4 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vmull.u8 q9, d24, d5 PF subge PF_X, PF_X, ORIG_W vmull.u8 q10, d24, d6 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q11, d24, d7 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm .macro pixman_composite_over_n_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d3[0]}, [DUMMY] vdup.8 d0, d3[0] vdup.8 d1, d3[1] vdup.8 d2, d3[2] vdup.8 d3, d3[3] vmvn.8 d24, d3 /* get inverted alpha */ .endm generate_composite_function \ pixman_composite_over_n_8888_asm_neon, 0, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8888_init, \ default_cleanup, \ pixman_composite_over_8888_8888_process_pixblock_head, \ pixman_composite_over_8888_8888_process_pixblock_tail, \ pixman_composite_over_n_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head vrshr.u16 q14, q8, #8 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 PF cmp PF_X, ORIG_W vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 vld4.8 {d0, d1, d2, d3}, [DST_R, :128]! vmvn.8 d22, d3 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! PF subge PF_X, PF_X, ORIG_W vmull.u8 q8, d22, d4 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q9, d22, d5 vmull.u8 q10, d22, d6 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vmull.u8 q11, d22, d7 .endm .macro pixman_composite_over_reverse_n_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d7[0]}, [DUMMY] vdup.8 d4, d7[0] vdup.8 d5, d7[1] vdup.8 d6, d7[2] vdup.8 d7, d7[3] .endm generate_composite_function \ pixman_composite_over_reverse_n_8888_asm_neon, 0, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_reverse_n_8888_init, \ default_cleanup, \ pixman_composite_over_8888_8888_process_pixblock_head, \ pixman_composite_over_8888_8888_process_pixblock_tail, \ pixman_composite_over_reverse_n_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 4, /* src_basereg */ \ 24 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_over_8888_8_0565_process_pixblock_head vmull.u8 q0, d24, d8 /* IN for SRC pixels (part1) */ vmull.u8 q1, d24, d9 vmull.u8 q6, d24, d10 vmull.u8 q7, d24, d11 vshrn.u16 d6, q2, #8 /* convert DST_R data to 32-bpp (part1) */ vshrn.u16 d7, q2, #3 vsli.u16 q2, q2, #5 vrshr.u16 q8, q0, #8 /* IN for SRC pixels (part2) */ vrshr.u16 q9, q1, #8 vrshr.u16 q10, q6, #8 vrshr.u16 q11, q7, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q9 vraddhn.u16 d2, q6, q10 vraddhn.u16 d3, q7, q11 vsri.u8 d6, d6, #5 /* convert DST_R data to 32-bpp (part2) */ vsri.u8 d7, d7, #6 vmvn.8 d3, d3 vshrn.u16 d30, q2, #2 vmull.u8 q8, d3, d6 /* now do alpha blending */ vmull.u8 q9, d3, d7 vmull.u8 q10, d3, d30 .endm .macro pixman_composite_over_8888_8_0565_process_pixblock_tail /* 3 cycle bubble (after vmull.u8) */ vrshr.u16 q13, q8, #8 vrshr.u16 q11, q9, #8 vrshr.u16 q15, q10, #8 vraddhn.u16 d16, q8, q13 vraddhn.u16 d27, q9, q11 vraddhn.u16 d26, q10, q15 vqadd.u8 d16, d2, d16 /* 1 cycle bubble */ vqadd.u8 q9, q0, q13 vshll.u8 q14, d16, #8 /* convert to 16bpp */ vshll.u8 q8, d19, #8 vshll.u8 q9, d18, #8 vsri.u16 q14, q8, #5 /* 1 cycle bubble */ vsri.u16 q14, q9, #11 .endm .macro pixman_composite_over_8888_8_0565_process_pixblock_tail_head vld1.16 {d4, d5}, [DST_R, :128]! vshrn.u16 d6, q2, #8 fetch_mask_pixblock vshrn.u16 d7, q2, #3 fetch_src_pixblock vmull.u8 q6, d24, d10 vrshr.u16 q13, q8, #8 vrshr.u16 q11, q9, #8 vrshr.u16 q15, q10, #8 vraddhn.u16 d16, q8, q13 vraddhn.u16 d27, q9, q11 vraddhn.u16 d26, q10, q15 vqadd.u8 d16, d2, d16 vmull.u8 q1, d24, d9 vqadd.u8 q9, q0, q13 vshll.u8 q14, d16, #8 vmull.u8 q0, d24, d8 vshll.u8 q8, d19, #8 vshll.u8 q9, d18, #8 vsri.u16 q14, q8, #5 vmull.u8 q7, d24, d11 vsri.u16 q14, q9, #11 cache_preload 8, 8 vsli.u16 q2, q2, #5 vrshr.u16 q8, q0, #8 vrshr.u16 q9, q1, #8 vrshr.u16 q10, q6, #8 vrshr.u16 q11, q7, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q9 vraddhn.u16 d2, q6, q10 vraddhn.u16 d3, q7, q11 vsri.u8 d6, d6, #5 vsri.u8 d7, d7, #6 vmvn.8 d3, d3 vshrn.u16 d30, q2, #2 vst1.16 {d28, d29}, [DST_W, :128]! vmull.u8 q8, d3, d6 vmull.u8 q9, d3, d7 vmull.u8 q10, d3, d30 .endm generate_composite_function \ pixman_composite_over_8888_8_0565_asm_neon, 32, 8, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_8888_8_0565_process_pixblock_head, \ pixman_composite_over_8888_8_0565_process_pixblock_tail, \ pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 24 /* mask_basereg */ /******************************************************************************/ /* * This function needs a special initialization of solid mask. * Solid source pixel data is fetched from stack at ARGS_STACK_OFFSET * offset, split into color components and replicated in d8-d11 * registers. Additionally, this function needs all the NEON registers, * so it has to save d8-d15 registers which are callee saved according * to ABI. These registers are restored from 'cleanup' macro. All the * other NEON registers are caller saved, so can be clobbered freely * without introducing any problems. */ .macro pixman_composite_over_n_8_0565_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d11[0]}, [DUMMY] vdup.8 d8, d11[0] vdup.8 d9, d11[1] vdup.8 d10, d11[2] vdup.8 d11, d11[3] .endm .macro pixman_composite_over_n_8_0565_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_n_8_0565_asm_neon, 0, 8, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8_0565_init, \ pixman_composite_over_n_8_0565_cleanup, \ pixman_composite_over_8888_8_0565_process_pixblock_head, \ pixman_composite_over_8888_8_0565_process_pixblock_tail, \ pixman_composite_over_8888_8_0565_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_8888_n_0565_init add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) vpush {d8-d15} vld1.32 {d24[0]}, [DUMMY] vdup.8 d24, d24[3] .endm .macro pixman_composite_over_8888_n_0565_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_8888_n_0565_asm_neon, 32, 0, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_8888_n_0565_init, \ pixman_composite_over_8888_n_0565_cleanup, \ pixman_composite_over_8888_8_0565_process_pixblock_head, \ pixman_composite_over_8888_8_0565_process_pixblock_tail, \ pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 24 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_0565_0565_process_pixblock_head .endm .macro pixman_composite_src_0565_0565_process_pixblock_tail .endm .macro pixman_composite_src_0565_0565_process_pixblock_tail_head vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! fetch_src_pixblock cache_preload 16, 16 .endm generate_composite_function \ pixman_composite_src_0565_0565_asm_neon, 16, 0, 16, \ FLAG_DST_WRITEONLY, \ 16, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_0565_0565_process_pixblock_head, \ pixman_composite_src_0565_0565_process_pixblock_tail, \ pixman_composite_src_0565_0565_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_n_8_process_pixblock_head .endm .macro pixman_composite_src_n_8_process_pixblock_tail .endm .macro pixman_composite_src_n_8_process_pixblock_tail_head vst1.8 {d0, d1, d2, d3}, [DST_W, :128]! .endm .macro pixman_composite_src_n_8_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d0[0]}, [DUMMY] vsli.u64 d0, d0, #8 vsli.u64 d0, d0, #16 vsli.u64 d0, d0, #32 vorr d1, d0, d0 vorr q1, q0, q0 .endm .macro pixman_composite_src_n_8_cleanup .endm generate_composite_function \ pixman_composite_src_n_8_asm_neon, 0, 0, 8, \ FLAG_DST_WRITEONLY, \ 32, /* number of pixels, processed in a single block */ \ 0, /* prefetch distance */ \ pixman_composite_src_n_8_init, \ pixman_composite_src_n_8_cleanup, \ pixman_composite_src_n_8_process_pixblock_head, \ pixman_composite_src_n_8_process_pixblock_tail, \ pixman_composite_src_n_8_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_n_0565_process_pixblock_head .endm .macro pixman_composite_src_n_0565_process_pixblock_tail .endm .macro pixman_composite_src_n_0565_process_pixblock_tail_head vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! .endm .macro pixman_composite_src_n_0565_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d0[0]}, [DUMMY] vsli.u64 d0, d0, #16 vsli.u64 d0, d0, #32 vorr d1, d0, d0 vorr q1, q0, q0 .endm .macro pixman_composite_src_n_0565_cleanup .endm generate_composite_function \ pixman_composite_src_n_0565_asm_neon, 0, 0, 16, \ FLAG_DST_WRITEONLY, \ 16, /* number of pixels, processed in a single block */ \ 0, /* prefetch distance */ \ pixman_composite_src_n_0565_init, \ pixman_composite_src_n_0565_cleanup, \ pixman_composite_src_n_0565_process_pixblock_head, \ pixman_composite_src_n_0565_process_pixblock_tail, \ pixman_composite_src_n_0565_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_n_8888_process_pixblock_head .endm .macro pixman_composite_src_n_8888_process_pixblock_tail .endm .macro pixman_composite_src_n_8888_process_pixblock_tail_head vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! .endm .macro pixman_composite_src_n_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d0[0]}, [DUMMY] vsli.u64 d0, d0, #32 vorr d1, d0, d0 vorr q1, q0, q0 .endm .macro pixman_composite_src_n_8888_cleanup .endm generate_composite_function \ pixman_composite_src_n_8888_asm_neon, 0, 0, 32, \ FLAG_DST_WRITEONLY, \ 8, /* number of pixels, processed in a single block */ \ 0, /* prefetch distance */ \ pixman_composite_src_n_8888_init, \ pixman_composite_src_n_8888_cleanup, \ pixman_composite_src_n_8888_process_pixblock_head, \ pixman_composite_src_n_8888_process_pixblock_tail, \ pixman_composite_src_n_8888_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_8888_8888_process_pixblock_head .endm .macro pixman_composite_src_8888_8888_process_pixblock_tail .endm .macro pixman_composite_src_8888_8888_process_pixblock_tail_head vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! fetch_src_pixblock cache_preload 8, 8 .endm generate_composite_function \ pixman_composite_src_8888_8888_asm_neon, 32, 0, 32, \ FLAG_DST_WRITEONLY, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_8888_8888_process_pixblock_head, \ pixman_composite_src_8888_8888_process_pixblock_tail, \ pixman_composite_src_8888_8888_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_x888_8888_process_pixblock_head vorr q0, q0, q2 vorr q1, q1, q2 .endm .macro pixman_composite_src_x888_8888_process_pixblock_tail .endm .macro pixman_composite_src_x888_8888_process_pixblock_tail_head vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! fetch_src_pixblock vorr q0, q0, q2 vorr q1, q1, q2 cache_preload 8, 8 .endm .macro pixman_composite_src_x888_8888_init vmov.u8 q2, #0xFF vshl.u32 q2, q2, #24 .endm generate_composite_function \ pixman_composite_src_x888_8888_asm_neon, 32, 0, 32, \ FLAG_DST_WRITEONLY, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ pixman_composite_src_x888_8888_init, \ default_cleanup, \ pixman_composite_src_x888_8888_process_pixblock_head, \ pixman_composite_src_x888_8888_process_pixblock_tail, \ pixman_composite_src_x888_8888_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_n_8_8888_process_pixblock_head /* expecting solid source in {d0, d1, d2, d3} */ /* mask is in d24 (d25, d26, d27 are unused) */ /* in */ vmull.u8 q8, d24, d0 vmull.u8 q9, d24, d1 vmull.u8 q10, d24, d2 vmull.u8 q11, d24, d3 vrsra.u16 q8, q8, #8 vrsra.u16 q9, q9, #8 vrsra.u16 q10, q10, #8 vrsra.u16 q11, q11, #8 .endm .macro pixman_composite_src_n_8_8888_process_pixblock_tail vrshrn.u16 d28, q8, #8 vrshrn.u16 d29, q9, #8 vrshrn.u16 d30, q10, #8 vrshrn.u16 d31, q11, #8 .endm .macro pixman_composite_src_n_8_8888_process_pixblock_tail_head fetch_mask_pixblock PF add PF_X, PF_X, #8 vrshrn.u16 d28, q8, #8 PF tst PF_CTL, #0x0F vrshrn.u16 d29, q9, #8 PF addne PF_X, PF_X, #8 vrshrn.u16 d30, q10, #8 PF subne PF_CTL, PF_CTL, #1 vrshrn.u16 d31, q11, #8 PF cmp PF_X, ORIG_W vmull.u8 q8, d24, d0 PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] vmull.u8 q9, d24, d1 PF subge PF_X, PF_X, ORIG_W vmull.u8 q10, d24, d2 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q11, d24, d3 PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! vrsra.u16 q8, q8, #8 vrsra.u16 q9, q9, #8 vrsra.u16 q10, q10, #8 vrsra.u16 q11, q11, #8 .endm .macro pixman_composite_src_n_8_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d3[0]}, [DUMMY] vdup.8 d0, d3[0] vdup.8 d1, d3[1] vdup.8 d2, d3[2] vdup.8 d3, d3[3] .endm .macro pixman_composite_src_n_8_8888_cleanup .endm generate_composite_function \ pixman_composite_src_n_8_8888_asm_neon, 0, 8, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_src_n_8_8888_init, \ pixman_composite_src_n_8_8888_cleanup, \ pixman_composite_src_n_8_8888_process_pixblock_head, \ pixman_composite_src_n_8_8888_process_pixblock_tail, \ pixman_composite_src_n_8_8888_process_pixblock_tail_head, \ /******************************************************************************/ .macro pixman_composite_src_n_8_8_process_pixblock_head vmull.u8 q0, d24, d16 vmull.u8 q1, d25, d16 vmull.u8 q2, d26, d16 vmull.u8 q3, d27, d16 vrsra.u16 q0, q0, #8 vrsra.u16 q1, q1, #8 vrsra.u16 q2, q2, #8 vrsra.u16 q3, q3, #8 .endm .macro pixman_composite_src_n_8_8_process_pixblock_tail vrshrn.u16 d28, q0, #8 vrshrn.u16 d29, q1, #8 vrshrn.u16 d30, q2, #8 vrshrn.u16 d31, q3, #8 .endm .macro pixman_composite_src_n_8_8_process_pixblock_tail_head fetch_mask_pixblock PF add PF_X, PF_X, #8 vrshrn.u16 d28, q0, #8 PF tst PF_CTL, #0x0F vrshrn.u16 d29, q1, #8 PF addne PF_X, PF_X, #8 vrshrn.u16 d30, q2, #8 PF subne PF_CTL, PF_CTL, #1 vrshrn.u16 d31, q3, #8 PF cmp PF_X, ORIG_W vmull.u8 q0, d24, d16 PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] vmull.u8 q1, d25, d16 PF subge PF_X, PF_X, ORIG_W vmull.u8 q2, d26, d16 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q3, d27, d16 PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! vrsra.u16 q0, q0, #8 vrsra.u16 q1, q1, #8 vrsra.u16 q2, q2, #8 vrsra.u16 q3, q3, #8 .endm .macro pixman_composite_src_n_8_8_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d16[0]}, [DUMMY] vdup.8 d16, d16[3] .endm .macro pixman_composite_src_n_8_8_cleanup .endm generate_composite_function \ pixman_composite_src_n_8_8_asm_neon, 0, 8, 8, \ FLAG_DST_WRITEONLY, \ 32, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_src_n_8_8_init, \ pixman_composite_src_n_8_8_cleanup, \ pixman_composite_src_n_8_8_process_pixblock_head, \ pixman_composite_src_n_8_8_process_pixblock_tail, \ pixman_composite_src_n_8_8_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_n_8_8888_process_pixblock_head /* expecting deinterleaved source data in {d8, d9, d10, d11} */ /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ /* and destination data in {d4, d5, d6, d7} */ /* mask is in d24 (d25, d26, d27 are unused) */ /* in */ vmull.u8 q6, d24, d8 vmull.u8 q7, d24, d9 vmull.u8 q8, d24, d10 vmull.u8 q9, d24, d11 vrshr.u16 q10, q6, #8 vrshr.u16 q11, q7, #8 vrshr.u16 q12, q8, #8 vrshr.u16 q13, q9, #8 vraddhn.u16 d0, q6, q10 vraddhn.u16 d1, q7, q11 vraddhn.u16 d2, q8, q12 vraddhn.u16 d3, q9, q13 vmvn.8 d25, d3 /* get inverted alpha */ /* source: d0 - blue, d1 - green, d2 - red, d3 - alpha */ /* destination: d4 - blue, d5 - green, d6 - red, d7 - alpha */ /* now do alpha blending */ vmull.u8 q8, d25, d4 vmull.u8 q9, d25, d5 vmull.u8 q10, d25, d6 vmull.u8 q11, d25, d7 .endm .macro pixman_composite_over_n_8_8888_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q6, q10, #8 vrshr.u16 q7, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q6, q10 vraddhn.u16 d31, q7, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm .macro pixman_composite_over_n_8_8888_process_pixblock_tail_head vrshr.u16 q14, q8, #8 vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vrshr.u16 q15, q9, #8 fetch_mask_pixblock vrshr.u16 q6, q10, #8 PF add PF_X, PF_X, #8 vrshr.u16 q7, q11, #8 PF tst PF_CTL, #0x0F vraddhn.u16 d28, q14, q8 PF addne PF_X, PF_X, #8 vraddhn.u16 d29, q15, q9 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d30, q6, q10 PF cmp PF_X, ORIG_W vraddhn.u16 d31, q7, q11 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] vmull.u8 q6, d24, d8 PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] vmull.u8 q7, d24, d9 PF subge PF_X, PF_X, ORIG_W vmull.u8 q8, d24, d10 PF subges PF_CTL, PF_CTL, #0x10 vmull.u8 q9, d24, d11 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! vqadd.u8 q14, q0, q14 PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! vqadd.u8 q15, q1, q15 vrshr.u16 q10, q6, #8 vrshr.u16 q11, q7, #8 vrshr.u16 q12, q8, #8 vrshr.u16 q13, q9, #8 vraddhn.u16 d0, q6, q10 vraddhn.u16 d1, q7, q11 vraddhn.u16 d2, q8, q12 vraddhn.u16 d3, q9, q13 vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! vmvn.8 d25, d3 vmull.u8 q8, d25, d4 vmull.u8 q9, d25, d5 vmull.u8 q10, d25, d6 vmull.u8 q11, d25, d7 .endm .macro pixman_composite_over_n_8_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d11[0]}, [DUMMY] vdup.8 d8, d11[0] vdup.8 d9, d11[1] vdup.8 d10, d11[2] vdup.8 d11, d11[3] .endm .macro pixman_composite_over_n_8_8888_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_n_8_8888_asm_neon, 0, 8, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8_8888_init, \ pixman_composite_over_n_8_8888_cleanup, \ pixman_composite_over_n_8_8888_process_pixblock_head, \ pixman_composite_over_n_8_8888_process_pixblock_tail, \ pixman_composite_over_n_8_8888_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_n_8_8_process_pixblock_head vmull.u8 q0, d24, d8 vmull.u8 q1, d25, d8 vmull.u8 q6, d26, d8 vmull.u8 q7, d27, d8 vrshr.u16 q10, q0, #8 vrshr.u16 q11, q1, #8 vrshr.u16 q12, q6, #8 vrshr.u16 q13, q7, #8 vraddhn.u16 d0, q0, q10 vraddhn.u16 d1, q1, q11 vraddhn.u16 d2, q6, q12 vraddhn.u16 d3, q7, q13 vmvn.8 q12, q0 vmvn.8 q13, q1 vmull.u8 q8, d24, d4 vmull.u8 q9, d25, d5 vmull.u8 q10, d26, d6 vmull.u8 q11, d27, d7 .endm .macro pixman_composite_over_n_8_8_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_n_8_8_process_pixblock_tail_head vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! pixman_composite_over_n_8_8_process_pixblock_tail fetch_mask_pixblock cache_preload 32, 32 vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! pixman_composite_over_n_8_8_process_pixblock_head .endm .macro pixman_composite_over_n_8_8_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d8[0]}, [DUMMY] vdup.8 d8, d8[3] .endm .macro pixman_composite_over_n_8_8_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_n_8_8_asm_neon, 0, 8, 8, \ FLAG_DST_READWRITE, \ 32, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8_8_init, \ pixman_composite_over_n_8_8_cleanup, \ pixman_composite_over_n_8_8_process_pixblock_head, \ pixman_composite_over_n_8_8_process_pixblock_tail, \ pixman_composite_over_n_8_8_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_n_8888_8888_ca_process_pixblock_head /* * 'combine_mask_ca' replacement * * input: solid src (n) in {d8, d9, d10, d11} * dest in {d4, d5, d6, d7 } * mask in {d24, d25, d26, d27} * output: updated src in {d0, d1, d2, d3 } * updated mask in {d24, d25, d26, d3 } */ vmull.u8 q0, d24, d8 vmull.u8 q1, d25, d9 vmull.u8 q6, d26, d10 vmull.u8 q7, d27, d11 vmull.u8 q9, d11, d25 vmull.u8 q12, d11, d24 vmull.u8 q13, d11, d26 vrshr.u16 q8, q0, #8 vrshr.u16 q10, q1, #8 vrshr.u16 q11, q6, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q10 vraddhn.u16 d2, q6, q11 vrshr.u16 q11, q12, #8 vrshr.u16 q8, q9, #8 vrshr.u16 q6, q13, #8 vrshr.u16 q10, q7, #8 vraddhn.u16 d24, q12, q11 vraddhn.u16 d25, q9, q8 vraddhn.u16 d26, q13, q6 vraddhn.u16 d3, q7, q10 /* * 'combine_over_ca' replacement * * output: updated dest in {d28, d29, d30, d31} */ vmvn.8 q12, q12 vmvn.8 d26, d26 vmull.u8 q8, d24, d4 vmull.u8 q9, d25, d5 vmvn.8 d27, d3 vmull.u8 q10, d26, d6 vmull.u8 q11, d27, d7 .endm .macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail /* ... continue 'combine_over_ca' replacement */ vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q6, q10, #8 vrshr.u16 q7, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q6, q10 vraddhn.u16 d31, q7, q11 vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm .macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vrshr.u16 q6, q10, #8 vrshr.u16 q7, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q6, q10 vraddhn.u16 d31, q7, q11 fetch_mask_pixblock vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 cache_preload 8, 8 pixman_composite_over_n_8888_8888_ca_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm .macro pixman_composite_over_n_8888_8888_ca_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d11[0]}, [DUMMY] vdup.8 d8, d11[0] vdup.8 d9, d11[1] vdup.8 d10, d11[2] vdup.8 d11, d11[3] .endm .macro pixman_composite_over_n_8888_8888_ca_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_n_8888_8888_ca_asm_neon, 0, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8888_8888_ca_init, \ pixman_composite_over_n_8888_8888_ca_cleanup, \ pixman_composite_over_n_8888_8888_ca_process_pixblock_head, \ pixman_composite_over_n_8888_8888_ca_process_pixblock_tail, \ pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_over_n_8888_0565_ca_process_pixblock_head /* * 'combine_mask_ca' replacement * * input: solid src (n) in {d8, d9, d10, d11} [B, G, R, A] * mask in {d24, d25, d26} [B, G, R] * output: updated src in {d0, d1, d2 } [B, G, R] * updated mask in {d24, d25, d26} [B, G, R] */ vmull.u8 q0, d24, d8 vmull.u8 q1, d25, d9 vmull.u8 q6, d26, d10 vmull.u8 q9, d11, d25 vmull.u8 q12, d11, d24 vmull.u8 q13, d11, d26 vrshr.u16 q8, q0, #8 vrshr.u16 q10, q1, #8 vrshr.u16 q11, q6, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q10 vraddhn.u16 d2, q6, q11 vrshr.u16 q11, q12, #8 vrshr.u16 q8, q9, #8 vrshr.u16 q6, q13, #8 vraddhn.u16 d24, q12, q11 vraddhn.u16 d25, q9, q8 /* * convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format * and put data into d16 - blue, d17 - green, d18 - red */ vshrn.u16 d17, q2, #3 vshrn.u16 d18, q2, #8 vraddhn.u16 d26, q13, q6 vsli.u16 q2, q2, #5 vsri.u8 d18, d18, #5 vsri.u8 d17, d17, #6 /* * 'combine_over_ca' replacement * * output: updated dest in d16 - blue, d17 - green, d18 - red */ vmvn.8 q12, q12 vshrn.u16 d16, q2, #2 vmvn.8 d26, d26 vmull.u8 q6, d16, d24 vmull.u8 q7, d17, d25 vmull.u8 q11, d18, d26 .endm .macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail /* ... continue 'combine_over_ca' replacement */ vrshr.u16 q10, q6, #8 vrshr.u16 q14, q7, #8 vrshr.u16 q15, q11, #8 vraddhn.u16 d16, q10, q6 vraddhn.u16 d17, q14, q7 vraddhn.u16 d18, q15, q11 vqadd.u8 q8, q0, q8 vqadd.u8 d18, d2, d18 /* * convert the results in d16, d17, d18 to r5g6b5 and store * them into {d28, d29} */ vshll.u8 q14, d18, #8 vshll.u8 q10, d17, #8 vshll.u8 q15, d16, #8 vsri.u16 q14, q10, #5 vsri.u16 q14, q15, #11 .endm .macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head fetch_mask_pixblock vrshr.u16 q10, q6, #8 vrshr.u16 q14, q7, #8 vld1.16 {d4, d5}, [DST_R, :128]! vrshr.u16 q15, q11, #8 vraddhn.u16 d16, q10, q6 vraddhn.u16 d17, q14, q7 vraddhn.u16 d22, q15, q11 /* process_pixblock_head */ /* * 'combine_mask_ca' replacement * * input: solid src (n) in {d8, d9, d10, d11} [B, G, R, A] * mask in {d24, d25, d26} [B, G, R] * output: updated src in {d0, d1, d2 } [B, G, R] * updated mask in {d24, d25, d26} [B, G, R] */ vmull.u8 q6, d26, d10 vqadd.u8 q8, q0, q8 vmull.u8 q0, d24, d8 vqadd.u8 d22, d2, d22 vmull.u8 q1, d25, d9 /* * convert the result in d16, d17, d22 to r5g6b5 and store * it into {d28, d29} */ vshll.u8 q14, d22, #8 vshll.u8 q10, d17, #8 vshll.u8 q15, d16, #8 vmull.u8 q9, d11, d25 vsri.u16 q14, q10, #5 vmull.u8 q12, d11, d24 vmull.u8 q13, d11, d26 vsri.u16 q14, q15, #11 cache_preload 8, 8 vrshr.u16 q8, q0, #8 vrshr.u16 q10, q1, #8 vrshr.u16 q11, q6, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q10 vraddhn.u16 d2, q6, q11 vrshr.u16 q11, q12, #8 vrshr.u16 q8, q9, #8 vrshr.u16 q6, q13, #8 vraddhn.u16 d24, q12, q11 vraddhn.u16 d25, q9, q8 /* * convert 8 r5g6b5 pixel data from {d4, d5} to planar * 8-bit format and put data into d16 - blue, d17 - green, * d18 - red */ vshrn.u16 d17, q2, #3 vshrn.u16 d18, q2, #8 vraddhn.u16 d26, q13, q6 vsli.u16 q2, q2, #5 vsri.u8 d17, d17, #6 vsri.u8 d18, d18, #5 /* * 'combine_over_ca' replacement * * output: updated dest in d16 - blue, d17 - green, d18 - red */ vmvn.8 q12, q12 vshrn.u16 d16, q2, #2 vmvn.8 d26, d26 vmull.u8 q7, d17, d25 vmull.u8 q6, d16, d24 vmull.u8 q11, d18, d26 vst1.16 {d28, d29}, [DST_W, :128]! .endm .macro pixman_composite_over_n_8888_0565_ca_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d11[0]}, [DUMMY] vdup.8 d8, d11[0] vdup.8 d9, d11[1] vdup.8 d10, d11[2] vdup.8 d11, d11[3] .endm .macro pixman_composite_over_n_8888_0565_ca_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_n_8888_0565_ca_asm_neon, 0, 32, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_n_8888_0565_ca_init, \ pixman_composite_over_n_8888_0565_ca_cleanup, \ pixman_composite_over_n_8888_0565_ca_process_pixblock_head, \ pixman_composite_over_n_8888_0565_ca_process_pixblock_tail, \ pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_in_n_8_process_pixblock_head /* expecting source data in {d0, d1, d2, d3} */ /* and destination data in {d4, d5, d6, d7} */ vmull.u8 q8, d4, d3 vmull.u8 q9, d5, d3 vmull.u8 q10, d6, d3 vmull.u8 q11, d7, d3 .endm .macro pixman_composite_in_n_8_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d28, q8, q14 vraddhn.u16 d29, q9, q15 vraddhn.u16 d30, q10, q12 vraddhn.u16 d31, q11, q13 .endm .macro pixman_composite_in_n_8_process_pixblock_tail_head pixman_composite_in_n_8_process_pixblock_tail vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! cache_preload 32, 32 pixman_composite_in_n_8_process_pixblock_head vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm .macro pixman_composite_in_n_8_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d3[0]}, [DUMMY] vdup.8 d3, d3[3] .endm .macro pixman_composite_in_n_8_cleanup .endm generate_composite_function \ pixman_composite_in_n_8_asm_neon, 0, 0, 8, \ FLAG_DST_READWRITE, \ 32, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_in_n_8_init, \ pixman_composite_in_n_8_cleanup, \ pixman_composite_in_n_8_process_pixblock_head, \ pixman_composite_in_n_8_process_pixblock_tail, \ pixman_composite_in_n_8_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 24 /* mask_basereg */ .macro pixman_composite_add_n_8_8_process_pixblock_head /* expecting source data in {d8, d9, d10, d11} */ /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ /* and destination data in {d4, d5, d6, d7} */ /* mask is in d24, d25, d26, d27 */ vmull.u8 q0, d24, d11 vmull.u8 q1, d25, d11 vmull.u8 q6, d26, d11 vmull.u8 q7, d27, d11 vrshr.u16 q10, q0, #8 vrshr.u16 q11, q1, #8 vrshr.u16 q12, q6, #8 vrshr.u16 q13, q7, #8 vraddhn.u16 d0, q0, q10 vraddhn.u16 d1, q1, q11 vraddhn.u16 d2, q6, q12 vraddhn.u16 d3, q7, q13 vqadd.u8 q14, q0, q2 vqadd.u8 q15, q1, q3 .endm .macro pixman_composite_add_n_8_8_process_pixblock_tail .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_add_n_8_8_process_pixblock_tail_head pixman_composite_add_n_8_8_process_pixblock_tail vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! fetch_mask_pixblock cache_preload 32, 32 pixman_composite_add_n_8_8_process_pixblock_head .endm .macro pixman_composite_add_n_8_8_init add DUMMY, sp, #ARGS_STACK_OFFSET vpush {d8-d15} vld1.32 {d11[0]}, [DUMMY] vdup.8 d11, d11[3] .endm .macro pixman_composite_add_n_8_8_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_add_n_8_8_asm_neon, 0, 8, 8, \ FLAG_DST_READWRITE, \ 32, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_add_n_8_8_init, \ pixman_composite_add_n_8_8_cleanup, \ pixman_composite_add_n_8_8_process_pixblock_head, \ pixman_composite_add_n_8_8_process_pixblock_tail, \ pixman_composite_add_n_8_8_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_add_8_8_8_process_pixblock_head /* expecting source data in {d0, d1, d2, d3} */ /* destination data in {d4, d5, d6, d7} */ /* mask in {d24, d25, d26, d27} */ vmull.u8 q8, d24, d0 vmull.u8 q9, d25, d1 vmull.u8 q10, d26, d2 vmull.u8 q11, d27, d3 vrshr.u16 q0, q8, #8 vrshr.u16 q1, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d0, q0, q8 vraddhn.u16 d1, q1, q9 vraddhn.u16 d2, q12, q10 vraddhn.u16 d3, q13, q11 vqadd.u8 q14, q0, q2 vqadd.u8 q15, q1, q3 .endm .macro pixman_composite_add_8_8_8_process_pixblock_tail .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_add_8_8_8_process_pixblock_tail_head pixman_composite_add_8_8_8_process_pixblock_tail vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! fetch_mask_pixblock fetch_src_pixblock cache_preload 32, 32 pixman_composite_add_8_8_8_process_pixblock_head .endm .macro pixman_composite_add_8_8_8_init .endm .macro pixman_composite_add_8_8_8_cleanup .endm generate_composite_function \ pixman_composite_add_8_8_8_asm_neon, 8, 8, 8, \ FLAG_DST_READWRITE, \ 32, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_add_8_8_8_init, \ pixman_composite_add_8_8_8_cleanup, \ pixman_composite_add_8_8_8_process_pixblock_head, \ pixman_composite_add_8_8_8_process_pixblock_tail, \ pixman_composite_add_8_8_8_process_pixblock_tail_head /******************************************************************************/ .macro pixman_composite_add_8888_8888_8888_process_pixblock_head /* expecting source data in {d0, d1, d2, d3} */ /* destination data in {d4, d5, d6, d7} */ /* mask in {d24, d25, d26, d27} */ vmull.u8 q8, d27, d0 vmull.u8 q9, d27, d1 vmull.u8 q10, d27, d2 vmull.u8 q11, d27, d3 /* 1 cycle bubble */ vrsra.u16 q8, q8, #8 vrsra.u16 q9, q9, #8 vrsra.u16 q10, q10, #8 vrsra.u16 q11, q11, #8 .endm .macro pixman_composite_add_8888_8888_8888_process_pixblock_tail /* 2 cycle bubble */ vrshrn.u16 d28, q8, #8 vrshrn.u16 d29, q9, #8 vrshrn.u16 d30, q10, #8 vrshrn.u16 d31, q11, #8 vqadd.u8 q14, q2, q14 /* 1 cycle bubble */ vqadd.u8 q15, q3, q15 .endm .macro pixman_composite_add_8888_8888_8888_process_pixblock_tail_head fetch_src_pixblock vrshrn.u16 d28, q8, #8 fetch_mask_pixblock vrshrn.u16 d29, q9, #8 vmull.u8 q8, d27, d0 vrshrn.u16 d30, q10, #8 vmull.u8 q9, d27, d1 vrshrn.u16 d31, q11, #8 vmull.u8 q10, d27, d2 vqadd.u8 q14, q2, q14 vmull.u8 q11, d27, d3 vqadd.u8 q15, q3, q15 vrsra.u16 q8, q8, #8 vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! vrsra.u16 q9, q9, #8 vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! vrsra.u16 q10, q10, #8 cache_preload 8, 8 vrsra.u16 q11, q11, #8 .endm generate_composite_function \ pixman_composite_add_8888_8888_8888_asm_neon, 32, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_add_8888_8888_8888_process_pixblock_head, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail_head generate_composite_function_single_scanline \ pixman_composite_scanline_add_mask_asm_neon, 32, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_add_8888_8888_8888_process_pixblock_head, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail_head /******************************************************************************/ generate_composite_function \ pixman_composite_add_8888_8_8888_asm_neon, 32, 8, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_add_8888_8888_8888_process_pixblock_head, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 27 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_add_n_8_8888_init add DUMMY, sp, #ARGS_STACK_OFFSET vld1.32 {d3[0]}, [DUMMY] vdup.8 d0, d3[0] vdup.8 d1, d3[1] vdup.8 d2, d3[2] vdup.8 d3, d3[3] .endm .macro pixman_composite_add_n_8_8888_cleanup .endm generate_composite_function \ pixman_composite_add_n_8_8888_asm_neon, 0, 8, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_add_n_8_8888_init, \ pixman_composite_add_n_8_8888_cleanup, \ pixman_composite_add_8888_8888_8888_process_pixblock_head, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 27 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_add_8888_n_8888_init add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) vld1.32 {d27[0]}, [DUMMY] vdup.8 d27, d27[3] .endm .macro pixman_composite_add_8888_n_8888_cleanup .endm generate_composite_function \ pixman_composite_add_8888_n_8888_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_add_8888_n_8888_init, \ pixman_composite_add_8888_n_8888_cleanup, \ pixman_composite_add_8888_8888_8888_process_pixblock_head, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 27 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_head /* expecting source data in {d0, d1, d2, d3} */ /* destination data in {d4, d5, d6, d7} */ /* solid mask is in d15 */ /* 'in' */ vmull.u8 q8, d15, d3 vmull.u8 q6, d15, d2 vmull.u8 q5, d15, d1 vmull.u8 q4, d15, d0 vrshr.u16 q13, q8, #8 vrshr.u16 q12, q6, #8 vrshr.u16 q11, q5, #8 vrshr.u16 q10, q4, #8 vraddhn.u16 d3, q8, q13 vraddhn.u16 d2, q6, q12 vraddhn.u16 d1, q5, q11 vraddhn.u16 d0, q4, q10 vmvn.8 d24, d3 /* get inverted alpha */ /* now do alpha blending */ vmull.u8 q8, d24, d4 vmull.u8 q9, d24, d5 vmull.u8 q10, d24, d6 vmull.u8 q11, d24, d7 .endm .macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail fetch_src_pixblock cache_preload 8, 8 fetch_mask_pixblock pixman_composite_out_reverse_8888_n_8888_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm generate_composite_function_single_scanline \ pixman_composite_scanline_out_reverse_mask_asm_neon, 32, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_out_reverse_8888_n_8888_process_pixblock_head, \ pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail, \ pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 12 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_over_8888_n_8888_process_pixblock_head pixman_composite_out_reverse_8888_n_8888_process_pixblock_head .endm .macro pixman_composite_over_8888_n_8888_process_pixblock_tail pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail vqadd.u8 q14, q0, q14 vqadd.u8 q15, q1, q15 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_8888_n_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! pixman_composite_over_8888_n_8888_process_pixblock_tail fetch_src_pixblock cache_preload 8, 8 pixman_composite_over_8888_n_8888_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm .macro pixman_composite_over_8888_n_8888_init add DUMMY, sp, #48 vpush {d8-d15} vld1.32 {d15[0]}, [DUMMY] vdup.8 d15, d15[3] .endm .macro pixman_composite_over_8888_n_8888_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_8888_n_8888_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_8888_n_8888_init, \ pixman_composite_over_8888_n_8888_cleanup, \ pixman_composite_over_8888_n_8888_process_pixblock_head, \ pixman_composite_over_8888_n_8888_process_pixblock_tail, \ pixman_composite_over_8888_n_8888_process_pixblock_tail_head /******************************************************************************/ /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_8888_8888_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! pixman_composite_over_8888_n_8888_process_pixblock_tail fetch_src_pixblock cache_preload 8, 8 fetch_mask_pixblock pixman_composite_over_8888_n_8888_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_over_8888_8888_8888_asm_neon, 32, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_8888_n_8888_process_pixblock_head, \ pixman_composite_over_8888_n_8888_process_pixblock_tail, \ pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 12 /* mask_basereg */ generate_composite_function_single_scanline \ pixman_composite_scanline_over_mask_asm_neon, 32, 32, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_8888_n_8888_process_pixblock_head, \ pixman_composite_over_8888_n_8888_process_pixblock_tail, \ pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 12 /* mask_basereg */ /******************************************************************************/ /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_8888_8_8888_process_pixblock_tail_head vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! pixman_composite_over_8888_n_8888_process_pixblock_tail fetch_src_pixblock cache_preload 8, 8 fetch_mask_pixblock pixman_composite_over_8888_n_8888_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_over_8888_8_8888_asm_neon, 32, 8, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_8888_n_8888_process_pixblock_head, \ pixman_composite_over_8888_n_8888_process_pixblock_tail, \ pixman_composite_over_8888_8_8888_process_pixblock_tail_head \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 15 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_0888_0888_process_pixblock_head .endm .macro pixman_composite_src_0888_0888_process_pixblock_tail .endm .macro pixman_composite_src_0888_0888_process_pixblock_tail_head vst3.8 {d0, d1, d2}, [DST_W]! fetch_src_pixblock cache_preload 8, 8 .endm generate_composite_function \ pixman_composite_src_0888_0888_asm_neon, 24, 0, 24, \ FLAG_DST_WRITEONLY, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_0888_0888_process_pixblock_head, \ pixman_composite_src_0888_0888_process_pixblock_tail, \ pixman_composite_src_0888_0888_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_0888_8888_rev_process_pixblock_head vswp d0, d2 .endm .macro pixman_composite_src_0888_8888_rev_process_pixblock_tail .endm .macro pixman_composite_src_0888_8888_rev_process_pixblock_tail_head vst4.8 {d0, d1, d2, d3}, [DST_W]! fetch_src_pixblock vswp d0, d2 cache_preload 8, 8 .endm .macro pixman_composite_src_0888_8888_rev_init veor d3, d3, d3 .endm generate_composite_function \ pixman_composite_src_0888_8888_rev_asm_neon, 24, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ pixman_composite_src_0888_8888_rev_init, \ default_cleanup, \ pixman_composite_src_0888_8888_rev_process_pixblock_head, \ pixman_composite_src_0888_8888_rev_process_pixblock_tail, \ pixman_composite_src_0888_8888_rev_process_pixblock_tail_head, \ 0, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_0888_0565_rev_process_pixblock_head vshll.u8 q8, d1, #8 vshll.u8 q9, d2, #8 .endm .macro pixman_composite_src_0888_0565_rev_process_pixblock_tail vshll.u8 q14, d0, #8 vsri.u16 q14, q8, #5 vsri.u16 q14, q9, #11 .endm .macro pixman_composite_src_0888_0565_rev_process_pixblock_tail_head vshll.u8 q14, d0, #8 fetch_src_pixblock vsri.u16 q14, q8, #5 vsri.u16 q14, q9, #11 vshll.u8 q8, d1, #8 vst1.16 {d28, d29}, [DST_W, :128]! vshll.u8 q9, d2, #8 .endm generate_composite_function \ pixman_composite_src_0888_0565_rev_asm_neon, 24, 0, 16, \ FLAG_DST_WRITEONLY, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_0888_0565_rev_process_pixblock_head, \ pixman_composite_src_0888_0565_rev_process_pixblock_tail, \ pixman_composite_src_0888_0565_rev_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_pixbuf_8888_process_pixblock_head vmull.u8 q8, d3, d0 vmull.u8 q9, d3, d1 vmull.u8 q10, d3, d2 .endm .macro pixman_composite_src_pixbuf_8888_process_pixblock_tail vrshr.u16 q11, q8, #8 vswp d3, d31 vrshr.u16 q12, q9, #8 vrshr.u16 q13, q10, #8 vraddhn.u16 d30, q11, q8 vraddhn.u16 d29, q12, q9 vraddhn.u16 d28, q13, q10 .endm .macro pixman_composite_src_pixbuf_8888_process_pixblock_tail_head vrshr.u16 q11, q8, #8 vswp d3, d31 vrshr.u16 q12, q9, #8 vrshr.u16 q13, q10, #8 fetch_src_pixblock vraddhn.u16 d30, q11, q8 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d29, q12, q9 vraddhn.u16 d28, q13, q10 vmull.u8 q8, d3, d0 vmull.u8 q9, d3, d1 vmull.u8 q10, d3, d2 vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! PF cmp PF_X, ORIG_W PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! .endm generate_composite_function \ pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_pixbuf_8888_process_pixblock_head, \ pixman_composite_src_pixbuf_8888_process_pixblock_tail, \ pixman_composite_src_pixbuf_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_src_rpixbuf_8888_process_pixblock_head vmull.u8 q8, d3, d0 vmull.u8 q9, d3, d1 vmull.u8 q10, d3, d2 .endm .macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail vrshr.u16 q11, q8, #8 vswp d3, d31 vrshr.u16 q12, q9, #8 vrshr.u16 q13, q10, #8 vraddhn.u16 d28, q11, q8 vraddhn.u16 d29, q12, q9 vraddhn.u16 d30, q13, q10 .endm .macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head vrshr.u16 q11, q8, #8 vswp d3, d31 vrshr.u16 q12, q9, #8 vrshr.u16 q13, q10, #8 fetch_src_pixblock vraddhn.u16 d28, q11, q8 PF add PF_X, PF_X, #8 PF tst PF_CTL, #0xF PF addne PF_X, PF_X, #8 PF subne PF_CTL, PF_CTL, #1 vraddhn.u16 d29, q12, q9 vraddhn.u16 d30, q13, q10 vmull.u8 q8, d3, d0 vmull.u8 q9, d3, d1 vmull.u8 q10, d3, d2 vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! PF cmp PF_X, ORIG_W PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! .endm generate_composite_function \ pixman_composite_src_rpixbuf_8888_asm_neon, 32, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 10, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_src_rpixbuf_8888_process_pixblock_head, \ pixman_composite_src_rpixbuf_8888_process_pixblock_tail, \ pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 0, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_over_0565_8_0565_process_pixblock_head /* mask is in d15 */ convert_0565_to_x888 q4, d2, d1, d0 convert_0565_to_x888 q5, d6, d5, d4 /* source pixel data is in {d0, d1, d2, XX} */ /* destination pixel data is in {d4, d5, d6, XX} */ vmvn.8 d7, d15 vmull.u8 q6, d15, d2 vmull.u8 q5, d15, d1 vmull.u8 q4, d15, d0 vmull.u8 q8, d7, d4 vmull.u8 q9, d7, d5 vmull.u8 q13, d7, d6 vrshr.u16 q12, q6, #8 vrshr.u16 q11, q5, #8 vrshr.u16 q10, q4, #8 vraddhn.u16 d2, q6, q12 vraddhn.u16 d1, q5, q11 vraddhn.u16 d0, q4, q10 .endm .macro pixman_composite_over_0565_8_0565_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q13, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q12, q13 vqadd.u8 q0, q0, q14 vqadd.u8 q1, q1, q15 /* 32bpp result is in {d0, d1, d2, XX} */ convert_8888_to_0565 d2, d1, d0, q14, q15, q3 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_over_0565_8_0565_process_pixblock_tail_head fetch_mask_pixblock pixman_composite_over_0565_8_0565_process_pixblock_tail fetch_src_pixblock vld1.16 {d10, d11}, [DST_R, :128]! cache_preload 8, 8 pixman_composite_over_0565_8_0565_process_pixblock_head vst1.16 {d28, d29}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_over_0565_8_0565_asm_neon, 16, 8, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_0565_8_0565_process_pixblock_head, \ pixman_composite_over_0565_8_0565_process_pixblock_tail, \ pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 10, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 15 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_over_0565_n_0565_init add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) vpush {d8-d15} vld1.32 {d15[0]}, [DUMMY] vdup.8 d15, d15[3] .endm .macro pixman_composite_over_0565_n_0565_cleanup vpop {d8-d15} .endm generate_composite_function \ pixman_composite_over_0565_n_0565_asm_neon, 16, 0, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ pixman_composite_over_0565_n_0565_init, \ pixman_composite_over_0565_n_0565_cleanup, \ pixman_composite_over_0565_8_0565_process_pixblock_head, \ pixman_composite_over_0565_8_0565_process_pixblock_tail, \ pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 10, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 15 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_add_0565_8_0565_process_pixblock_head /* mask is in d15 */ convert_0565_to_x888 q4, d2, d1, d0 convert_0565_to_x888 q5, d6, d5, d4 /* source pixel data is in {d0, d1, d2, XX} */ /* destination pixel data is in {d4, d5, d6, XX} */ vmull.u8 q6, d15, d2 vmull.u8 q5, d15, d1 vmull.u8 q4, d15, d0 vrshr.u16 q12, q6, #8 vrshr.u16 q11, q5, #8 vrshr.u16 q10, q4, #8 vraddhn.u16 d2, q6, q12 vraddhn.u16 d1, q5, q11 vraddhn.u16 d0, q4, q10 .endm .macro pixman_composite_add_0565_8_0565_process_pixblock_tail vqadd.u8 q0, q0, q2 vqadd.u8 q1, q1, q3 /* 32bpp result is in {d0, d1, d2, XX} */ convert_8888_to_0565 d2, d1, d0, q14, q15, q3 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_add_0565_8_0565_process_pixblock_tail_head fetch_mask_pixblock pixman_composite_add_0565_8_0565_process_pixblock_tail fetch_src_pixblock vld1.16 {d10, d11}, [DST_R, :128]! cache_preload 8, 8 pixman_composite_add_0565_8_0565_process_pixblock_head vst1.16 {d28, d29}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_add_0565_8_0565_asm_neon, 16, 8, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_add_0565_8_0565_process_pixblock_head, \ pixman_composite_add_0565_8_0565_process_pixblock_tail, \ pixman_composite_add_0565_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 10, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 15 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_out_reverse_8_0565_process_pixblock_head /* mask is in d15 */ convert_0565_to_x888 q5, d6, d5, d4 /* destination pixel data is in {d4, d5, d6, xx} */ vmvn.8 d24, d15 /* get inverted alpha */ /* now do alpha blending */ vmull.u8 q8, d24, d4 vmull.u8 q9, d24, d5 vmull.u8 q10, d24, d6 .endm .macro pixman_composite_out_reverse_8_0565_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vraddhn.u16 d0, q14, q8 vraddhn.u16 d1, q15, q9 vraddhn.u16 d2, q12, q10 /* 32bpp result is in {d0, d1, d2, XX} */ convert_8888_to_0565 d2, d1, d0, q14, q15, q3 .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_out_reverse_8_0565_process_pixblock_tail_head fetch_src_pixblock pixman_composite_out_reverse_8_0565_process_pixblock_tail vld1.16 {d10, d11}, [DST_R, :128]! cache_preload 8, 8 pixman_composite_out_reverse_8_0565_process_pixblock_head vst1.16 {d28, d29}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_out_reverse_8_0565_asm_neon, 8, 0, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_out_reverse_8_0565_process_pixblock_head, \ pixman_composite_out_reverse_8_0565_process_pixblock_tail, \ pixman_composite_out_reverse_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 10, /* dst_r_basereg */ \ 15, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ .macro pixman_composite_out_reverse_8_8888_process_pixblock_head /* src is in d0 */ /* destination pixel data is in {d4, d5, d6, d7} */ vmvn.8 d1, d0 /* get inverted alpha */ /* now do alpha blending */ vmull.u8 q8, d1, d4 vmull.u8 q9, d1, d5 vmull.u8 q10, d1, d6 vmull.u8 q11, d1, d7 .endm .macro pixman_composite_out_reverse_8_8888_process_pixblock_tail vrshr.u16 q14, q8, #8 vrshr.u16 q15, q9, #8 vrshr.u16 q12, q10, #8 vrshr.u16 q13, q11, #8 vraddhn.u16 d28, q14, q8 vraddhn.u16 d29, q15, q9 vraddhn.u16 d30, q12, q10 vraddhn.u16 d31, q13, q11 /* 32bpp result is in {d28, d29, d30, d31} */ .endm /* TODO: expand macros and do better instructions scheduling */ .macro pixman_composite_out_reverse_8_8888_process_pixblock_tail_head fetch_src_pixblock pixman_composite_out_reverse_8_8888_process_pixblock_tail vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! cache_preload 8, 8 pixman_composite_out_reverse_8_8888_process_pixblock_head vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! .endm generate_composite_function \ pixman_composite_out_reverse_8_8888_asm_neon, 8, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ 5, /* prefetch distance */ \ default_init, \ default_cleanup, \ pixman_composite_out_reverse_8_8888_process_pixblock_head, \ pixman_composite_out_reverse_8_8888_process_pixblock_tail, \ pixman_composite_out_reverse_8_8888_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 0 /* mask_basereg */ /******************************************************************************/ generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_8888_8888_OVER_asm_neon, 32, 0, 32, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_over_8888_8888_process_pixblock_head, \ pixman_composite_over_8888_8888_process_pixblock_tail, \ pixman_composite_over_8888_8888_process_pixblock_tail_head generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_8888_0565_OVER_asm_neon, 32, 0, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_over_8888_0565_process_pixblock_head, \ pixman_composite_over_8888_0565_process_pixblock_tail, \ pixman_composite_over_8888_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 0, /* src_basereg */ \ 24 /* mask_basereg */ generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_8888_0565_SRC_asm_neon, 32, 0, 16, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_src_8888_0565_process_pixblock_head, \ pixman_composite_src_8888_0565_process_pixblock_tail, \ pixman_composite_src_8888_0565_process_pixblock_tail_head generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_0565_8888_SRC_asm_neon, 16, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init, \ default_cleanup, \ pixman_composite_src_0565_8888_process_pixblock_head, \ pixman_composite_src_0565_8888_process_pixblock_tail, \ pixman_composite_src_0565_8888_process_pixblock_tail_head generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_neon, 32, 8, 16, \ FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ 8, /* number of pixels, processed in a single block */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_8888_8_0565_process_pixblock_head, \ pixman_composite_over_8888_8_0565_process_pixblock_tail, \ pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 4, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 24 /* mask_basereg */ generate_composite_function_nearest_scanline \ pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_neon, 16, 8, 16, \ FLAG_DST_READWRITE, \ 8, /* number of pixels, processed in a single block */ \ default_init_need_all_regs, \ default_cleanup_need_all_regs, \ pixman_composite_over_0565_8_0565_process_pixblock_head, \ pixman_composite_over_0565_8_0565_process_pixblock_tail, \ pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ 28, /* dst_w_basereg */ \ 10, /* dst_r_basereg */ \ 8, /* src_basereg */ \ 15 /* mask_basereg */ /******************************************************************************/ /* Supplementary macro for setting function attributes */ .macro pixman_asm_function fname .func fname .global fname #ifdef __ELF__ .hidden fname .type fname, %function #endif fname: .endm /* * Bilinear scaling support code which tries to provide pixel fetching, color * format conversion, and interpolation as separate macros which can be used * as the basic building blocks for constructing bilinear scanline functions. */ .macro bilinear_load_8888 reg1, reg2, tmp mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 vld1.32 {reg1}, [TMP1], STRIDE vld1.32 {reg2}, [TMP1] .endm .macro bilinear_load_0565 reg1, reg2, tmp mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 vld1.32 {reg2[0]}, [TMP1], STRIDE vld1.32 {reg2[1]}, [TMP1] convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp .endm .macro bilinear_load_and_vertical_interpolate_two_8888 \ acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 bilinear_load_8888 reg1, reg2, tmp1 vmull.u8 acc1, reg1, d28 vmlal.u8 acc1, reg2, d29 bilinear_load_8888 reg3, reg4, tmp2 vmull.u8 acc2, reg3, d28 vmlal.u8 acc2, reg4, d29 .endm .macro bilinear_load_and_vertical_interpolate_four_8888 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi bilinear_load_and_vertical_interpolate_two_8888 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi bilinear_load_and_vertical_interpolate_two_8888 \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi .endm .macro bilinear_load_and_vertical_interpolate_two_0565 \ acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {acc2lo[0]}, [TMP1], STRIDE vld1.32 {acc2hi[0]}, [TMP2], STRIDE vld1.32 {acc2lo[1]}, [TMP1] vld1.32 {acc2hi[1]}, [TMP2] convert_0565_to_x888 acc2, reg3, reg2, reg1 vzip.u8 reg1, reg3 vzip.u8 reg2, reg4 vzip.u8 reg3, reg4 vzip.u8 reg1, reg2 vmull.u8 acc1, reg1, d28 vmlal.u8 acc1, reg2, d29 vmull.u8 acc2, reg3, d28 vmlal.u8 acc2, reg4, d29 .endm .macro bilinear_load_and_vertical_interpolate_four_0565 \ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {xacc2lo[0]}, [TMP1], STRIDE vld1.32 {xacc2hi[0]}, [TMP2], STRIDE vld1.32 {xacc2lo[1]}, [TMP1] vld1.32 {xacc2hi[1]}, [TMP2] convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #1 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #1 vld1.32 {yacc2lo[0]}, [TMP1], STRIDE vzip.u8 xreg1, xreg3 vld1.32 {yacc2hi[0]}, [TMP2], STRIDE vzip.u8 xreg2, xreg4 vld1.32 {yacc2lo[1]}, [TMP1] vzip.u8 xreg3, xreg4 vld1.32 {yacc2hi[1]}, [TMP2] vzip.u8 xreg1, xreg2 convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 vmull.u8 xacc1, xreg1, d28 vzip.u8 yreg1, yreg3 vmlal.u8 xacc1, xreg2, d29 vzip.u8 yreg2, yreg4 vmull.u8 xacc2, xreg3, d28 vzip.u8 yreg3, yreg4 vmlal.u8 xacc2, xreg4, d29 vzip.u8 yreg1, yreg2 vmull.u8 yacc1, yreg1, d28 vmlal.u8 yacc1, yreg2, d29 vmull.u8 yacc2, yreg3, d28 vmlal.u8 yacc2, yreg4, d29 .endm .macro bilinear_store_8888 numpix, tmp1, tmp2 .if numpix == 4 vst1.32 {d0, d1}, [OUT, :128]! .elseif numpix == 2 vst1.32 {d0}, [OUT, :64]! .elseif numpix == 1 vst1.32 {d0[0]}, [OUT, :32]! .else .error bilinear_store_8888 numpix is unsupported .endif .endm .macro bilinear_store_0565 numpix, tmp1, tmp2 vuzp.u8 d0, d1 vuzp.u8 d2, d3 vuzp.u8 d1, d3 vuzp.u8 d0, d2 convert_8888_to_0565 d2, d1, d0, q1, tmp1, tmp2 .if numpix == 4 vst1.16 {d2}, [OUT, :64]! .elseif numpix == 2 vst1.32 {d2[0]}, [OUT, :32]! .elseif numpix == 1 vst1.16 {d2[0]}, [OUT, :16]! .else .error bilinear_store_0565 numpix is unsupported .endif .endm .macro bilinear_interpolate_last_pixel src_fmt, dst_fmt bilinear_load_&src_fmt d0, d1, d2 vmull.u8 q1, d0, d28 vmlal.u8 q1, d1, d29 /* 5 cycles bubble */ vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 /* 5 cycles bubble */ vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) /* 3 cycles bubble */ vmovn.u16 d0, q0 /* 1 cycle bubble */ bilinear_store_&dst_fmt 1, q2, q3 .endm .macro bilinear_interpolate_two_pixels src_fmt, dst_fmt bilinear_load_and_vertical_interpolate_two_&src_fmt \ q1, q11, d0, d1, d20, d21, d22, d23 vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q10, d22, d31 vmlal.u16 q10, d23, d31 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vmovn.u16 d0, q0 bilinear_store_&dst_fmt 2, q2, q3 .endm .macro bilinear_interpolate_four_pixels src_fmt, dst_fmt bilinear_load_and_vertical_interpolate_four_&src_fmt \ q1, q11, d0, d1, d20, d21, d22, d23 \ q3, q9, d4, d5, d16, d17, d18, d19 pld [TMP1, PF_OFFS] sub TMP1, TMP1, STRIDE vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d2, d30 vmlal.u16 q0, d3, d30 vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q10, d22, d31 vmlal.u16 q10, d23, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d6, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d6, d30 vmlal.u16 q2, d7, d30 vshll.u16 q8, d18, #BILINEAR_INTERPOLATION_BITS pld [TMP2, PF_OFFS] vmlsl.u16 q8, d18, d31 vmlal.u16 q8, d19, d31 vadd.u16 q12, q12, q13 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vmovn.u16 d0, q0 vmovn.u16 d1, q2 vadd.u16 q12, q12, q13 bilinear_store_&dst_fmt 4, q2, q3 .endm .macro bilinear_interpolate_four_pixels_head src_fmt, dst_fmt .ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_head .else bilinear_interpolate_four_pixels src_fmt, dst_fmt .endif .endm .macro bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt .ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail .endif .endm .macro bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt .ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail_head .else bilinear_interpolate_four_pixels src_fmt, dst_fmt .endif .endm .macro bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt .ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_head .else bilinear_interpolate_four_pixels_head src_fmt, dst_fmt bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt .endif .endm .macro bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt .ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail .else bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt .endif .endm .macro bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt .ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail_head .else bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt .endif .endm .set BILINEAR_FLAG_UNROLL_4, 0 .set BILINEAR_FLAG_UNROLL_8, 1 .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 /* * Main template macro for generating NEON optimized bilinear scanline * functions. * * Bilinear scanline scaler macro template uses the following arguments: * fname - name of the function to generate * src_fmt - source color format (8888 or 0565) * dst_fmt - destination color format (8888 or 0565) * bpp_shift - (1 << bpp_shift) is the size of source pixel in bytes * prefetch_distance - prefetch in the source image by that many * pixels ahead */ .macro generate_bilinear_scanline_func fname, src_fmt, dst_fmt, \ src_bpp_shift, dst_bpp_shift, \ prefetch_distance, flags pixman_asm_function fname OUT .req r0 TOP .req r1 BOTTOM .req r2 WT .req r3 WB .req r4 X .req r5 UX .req r6 WIDTH .req ip TMP1 .req r3 TMP2 .req r4 PF_OFFS .req r7 TMP3 .req r8 TMP4 .req r9 STRIDE .req r2 mov ip, sp push {r4, r5, r6, r7, r8, r9} mov PF_OFFS, #prefetch_distance ldmia ip, {WB, X, UX, WIDTH} mul PF_OFFS, PF_OFFS, UX .if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 vpush {d8-d15} .endif sub STRIDE, BOTTOM, TOP .unreq BOTTOM cmp WIDTH, #0 ble 3f vdup.u16 q12, X vdup.u16 q13, UX vdup.u8 d28, WT vdup.u8 d29, WB vadd.u16 d25, d25, d26 /* ensure good destination alignment */ cmp WIDTH, #1 blt 0f tst OUT, #(1 << dst_bpp_shift) beq 0f vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 bilinear_interpolate_last_pixel src_fmt, dst_fmt sub WIDTH, WIDTH, #1 0: vadd.u16 q13, q13, q13 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 cmp WIDTH, #2 blt 0f tst OUT, #(1 << (dst_bpp_shift + 1)) beq 0f bilinear_interpolate_two_pixels src_fmt, dst_fmt sub WIDTH, WIDTH, #2 0: .if ((flags) & BILINEAR_FLAG_UNROLL_8) != 0 /*********** 8 pixels per iteration *****************/ cmp WIDTH, #4 blt 0f tst OUT, #(1 << (dst_bpp_shift + 2)) beq 0f bilinear_interpolate_four_pixels src_fmt, dst_fmt sub WIDTH, WIDTH, #4 0: subs WIDTH, WIDTH, #8 blt 1f mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt subs WIDTH, WIDTH, #8 blt 5f 0: bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt subs WIDTH, WIDTH, #8 bge 0b 5: bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt 1: tst WIDTH, #4 beq 2f bilinear_interpolate_four_pixels src_fmt, dst_fmt 2: .else /*********** 4 pixels per iteration *****************/ subs WIDTH, WIDTH, #4 blt 1f mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) bilinear_interpolate_four_pixels_head src_fmt, dst_fmt subs WIDTH, WIDTH, #4 blt 5f 0: bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt subs WIDTH, WIDTH, #4 bge 0b 5: bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt 1: /****************************************************/ .endif /* handle the remaining trailing pixels */ tst WIDTH, #2 beq 2f bilinear_interpolate_two_pixels src_fmt, dst_fmt 2: tst WIDTH, #1 beq 3f bilinear_interpolate_last_pixel src_fmt, dst_fmt 3: .if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 vpop {d8-d15} .endif pop {r4, r5, r6, r7, r8, r9} bx lr .unreq OUT .unreq TOP .unreq WT .unreq WB .unreq X .unreq UX .unreq WIDTH .unreq TMP1 .unreq TMP2 .unreq PF_OFFS .unreq TMP3 .unreq TMP4 .unreq STRIDE .endfunc .endm /*****************************************************************************/ .set have_bilinear_interpolate_four_pixels_8888_8888, 1 .macro bilinear_interpolate_four_pixels_8888_8888_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vld1.32 {d22}, [TMP1], STRIDE vld1.32 {d23}, [TMP1] mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 vmull.u8 q8, d22, d28 vmlal.u8 q8, d23, d29 vld1.32 {d22}, [TMP2], STRIDE vld1.32 {d23}, [TMP2] mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmull.u8 q9, d22, d28 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 .endm .macro bilinear_interpolate_four_pixels_8888_8888_tail vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vadd.u16 q12, q12, q13 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vmovn.u16 d6, q0 vmovn.u16 d7, q2 vadd.u16 q12, q12, q13 vst1.32 {d6, d7}, [OUT, :128]! .endm .macro bilinear_interpolate_four_pixels_8888_8888_tail_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vld1.32 {d20}, [TMP1], STRIDE vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d22}, [TMP2], STRIDE vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vmovn.u16 d6, q0 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmovn.u16 d7, q2 vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vadd.u16 q12, q12, q13 vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vst1.32 {d6, d7}, [OUT, :128]! vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 .endm /*****************************************************************************/ .set have_bilinear_interpolate_eight_pixels_8888_0565, 1 .macro bilinear_interpolate_eight_pixels_8888_0565_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vld1.32 {d20}, [TMP1], STRIDE vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vld1.32 {d22}, [TMP2], STRIDE vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vld1.32 {d20}, [TMP1], STRIDE vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d22}, [TMP2], STRIDE vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vmovn.u16 d8, q0 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmovn.u16 d9, q2 vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vadd.u16 q12, q12, q13 vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 .endm .macro bilinear_interpolate_eight_pixels_8888_0565_tail vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vadd.u16 q12, q12, q13 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vmovn.u16 d10, q0 vmovn.u16 d11, q2 vadd.u16 q12, q12, q13 vuzp.u8 d8, d9 vuzp.u8 d10, d11 vuzp.u8 d9, d11 vuzp.u8 d8, d10 vshll.u8 q6, d9, #8 vshll.u8 q5, d10, #8 vshll.u8 q7, d8, #8 vsri.u16 q5, q6, #5 vsri.u16 q5, q7, #11 vst1.32 {d10, d11}, [OUT, :128]! .endm .macro bilinear_interpolate_eight_pixels_8888_0565_tail_head mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlal.u16 q1, d19, d31 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vuzp.u8 d8, d9 vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vld1.32 {d20}, [TMP1], STRIDE vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d22}, [TMP2], STRIDE vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vmovn.u16 d10, q0 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmovn.u16 d11, q2 vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vadd.u16 q12, q12, q13 vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vuzp.u8 d10, d11 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vmlsl.u16 q1, d18, d31 mov TMP1, X, asr #16 add X, X, UX add TMP1, TOP, TMP1, asl #2 mov TMP2, X, asr #16 add X, X, UX add TMP2, TOP, TMP2, asl #2 vmlal.u16 q1, d19, d31 vuzp.u8 d9, d11 vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS vuzp.u8 d8, d10 vmlsl.u16 q2, d20, d30 vmlal.u16 q2, d21, d30 vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS vld1.32 {d20}, [TMP1], STRIDE vmlsl.u16 q3, d22, d31 vmlal.u16 q3, d23, d31 vld1.32 {d21}, [TMP1] vmull.u8 q8, d20, d28 vmlal.u8 q8, d21, d29 vshll.u8 q6, d9, #8 vshll.u8 q5, d10, #8 vshll.u8 q7, d8, #8 vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) vsri.u16 q5, q6, #5 vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) vsri.u16 q5, q7, #11 vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) vld1.32 {d22}, [TMP2], STRIDE vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) vadd.u16 q12, q12, q13 vld1.32 {d23}, [TMP2] vmull.u8 q9, d22, d28 mov TMP3, X, asr #16 add X, X, UX add TMP3, TOP, TMP3, asl #2 mov TMP4, X, asr #16 add X, X, UX add TMP4, TOP, TMP4, asl #2 vmlal.u8 q9, d23, d29 vld1.32 {d22}, [TMP3], STRIDE vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) vld1.32 {d23}, [TMP3] vmull.u8 q10, d22, d28 vmlal.u8 q10, d23, d29 vmovn.u16 d8, q0 vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS vmovn.u16 d9, q2 vmlsl.u16 q0, d16, d30 vmlal.u16 q0, d17, d30 pld [TMP4, PF_OFFS] vld1.32 {d16}, [TMP4], STRIDE vadd.u16 q12, q12, q13 vld1.32 {d17}, [TMP4] pld [TMP4, PF_OFFS] vmull.u8 q11, d16, d28 vmlal.u8 q11, d17, d29 vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS vst1.32 {d10, d11}, [OUT, :128]! vmlsl.u16 q1, d18, d31 .endm /*****************************************************************************/ generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon, 8888, 8888, \ 2, 2, 28, BILINEAR_FLAG_UNROLL_4 generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_neon, 8888, 0565, \ 2, 1, 28, BILINEAR_FLAG_UNROLL_8 | BILINEAR_FLAG_USE_ALL_NEON_REGS generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_0565_x888_SRC_asm_neon, 0565, 8888, \ 1, 2, 28, BILINEAR_FLAG_UNROLL_4 generate_bilinear_scanline_func \ pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_neon, 0565, 0565, \ 1, 1, 28, BILINEAR_FLAG_UNROLL_4 Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-neon-asm.h000066400000000000000000001161121271037650300271430ustar00rootroot00000000000000/* * Copyright © 2009 Nokia Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) */ /* * This file contains a macro ('generate_composite_function') which can * construct 2D image processing functions, based on a common template. * Any combinations of source, destination and mask images with 8bpp, * 16bpp, 24bpp, 32bpp color formats are supported. * * This macro takes care of: * - handling of leading and trailing unaligned pixels * - doing most of the work related to L2 cache preload * - encourages the use of software pipelining for better instructions * scheduling * * The user of this macro has to provide some configuration parameters * (bit depths for the images, prefetch distance, etc.) and a set of * macros, which should implement basic code chunks responsible for * pixels processing. See 'pixman-arm-neon-asm.S' file for the usage * examples. * * TODO: * - try overlapped pixel method (from Ian Rickards) when processing * exactly two blocks of pixels * - maybe add an option to do reverse scanline processing */ /* * Bit flags for 'generate_composite_function' macro which are used * to tune generated functions behavior. */ .set FLAG_DST_WRITEONLY, 0 .set FLAG_DST_READWRITE, 1 .set FLAG_DEINTERLEAVE_32BPP, 2 /* * Offset in stack where mask and source pointer/stride can be accessed * from 'init' macro. This is useful for doing special handling for solid mask. */ .set ARGS_STACK_OFFSET, 40 /* * Constants for selecting preferable prefetch type. */ .set PREFETCH_TYPE_NONE, 0 /* No prefetch at all */ .set PREFETCH_TYPE_SIMPLE, 1 /* A simple, fixed-distance-ahead prefetch */ .set PREFETCH_TYPE_ADVANCED, 2 /* Advanced fine-grained prefetch */ /* * Definitions of supplementary pixld/pixst macros (for partial load/store of * pixel data). */ .macro pixldst1 op, elem_size, reg1, mem_operand, abits .if abits > 0 op&.&elem_size {d®1}, [&mem_operand&, :&abits&]! .else op&.&elem_size {d®1}, [&mem_operand&]! .endif .endm .macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits .if abits > 0 op&.&elem_size {d®1, d®2}, [&mem_operand&, :&abits&]! .else op&.&elem_size {d®1, d®2}, [&mem_operand&]! .endif .endm .macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits .if abits > 0 op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&, :&abits&]! .else op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&]! .endif .endm .macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits op&.&elem_size {d®1[idx]}, [&mem_operand&]! .endm .macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand op&.&elem_size {d®1, d®2, d®3}, [&mem_operand&]! .endm .macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand op&.&elem_size {d®1[idx], d®2[idx], d®3[idx]}, [&mem_operand&]! .endm .macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits .if numbytes == 32 pixldst4 op, elem_size, %(basereg+4), %(basereg+5), \ %(basereg+6), %(basereg+7), mem_operand, abits .elseif numbytes == 16 pixldst2 op, elem_size, %(basereg+2), %(basereg+3), mem_operand, abits .elseif numbytes == 8 pixldst1 op, elem_size, %(basereg+1), mem_operand, abits .elseif numbytes == 4 .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 32) pixldst0 op, 32, %(basereg+0), 1, mem_operand, abits .elseif elem_size == 16 pixldst0 op, 16, %(basereg+0), 2, mem_operand, abits pixldst0 op, 16, %(basereg+0), 3, mem_operand, abits .else pixldst0 op, 8, %(basereg+0), 4, mem_operand, abits pixldst0 op, 8, %(basereg+0), 5, mem_operand, abits pixldst0 op, 8, %(basereg+0), 6, mem_operand, abits pixldst0 op, 8, %(basereg+0), 7, mem_operand, abits .endif .elseif numbytes == 2 .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 16) pixldst0 op, 16, %(basereg+0), 1, mem_operand, abits .else pixldst0 op, 8, %(basereg+0), 2, mem_operand, abits pixldst0 op, 8, %(basereg+0), 3, mem_operand, abits .endif .elseif numbytes == 1 pixldst0 op, 8, %(basereg+0), 1, mem_operand, abits .else .error "unsupported size: numbytes" .endif .endm .macro pixld numpix, bpp, basereg, mem_operand, abits=0 .if bpp > 0 .if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) pixldst4 vld4, 8, %(basereg+4), %(basereg+5), \ %(basereg+6), %(basereg+7), mem_operand, abits .elseif (bpp == 24) && (numpix == 8) pixldst3 vld3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand .elseif (bpp == 24) && (numpix == 4) pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand .elseif (bpp == 24) && (numpix == 2) pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand .elseif (bpp == 24) && (numpix == 1) pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand .else pixldst %(numpix * bpp / 8), vld1, %(bpp), basereg, mem_operand, abits .endif .endif .endm .macro pixst numpix, bpp, basereg, mem_operand, abits=0 .if bpp > 0 .if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) pixldst4 vst4, 8, %(basereg+4), %(basereg+5), \ %(basereg+6), %(basereg+7), mem_operand, abits .elseif (bpp == 24) && (numpix == 8) pixldst3 vst3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand .elseif (bpp == 24) && (numpix == 4) pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand .elseif (bpp == 24) && (numpix == 2) pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand .elseif (bpp == 24) && (numpix == 1) pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand .else pixldst %(numpix * bpp / 8), vst1, %(bpp), basereg, mem_operand, abits .endif .endif .endm .macro pixld_a numpix, bpp, basereg, mem_operand .if (bpp * numpix) <= 128 pixld numpix, bpp, basereg, mem_operand, %(bpp * numpix) .else pixld numpix, bpp, basereg, mem_operand, 128 .endif .endm .macro pixst_a numpix, bpp, basereg, mem_operand .if (bpp * numpix) <= 128 pixst numpix, bpp, basereg, mem_operand, %(bpp * numpix) .else pixst numpix, bpp, basereg, mem_operand, 128 .endif .endm /* * Pixel fetcher for nearest scaling (needs TMP1, TMP2, VX, UNIT_X register * aliases to be defined) */ .macro pixld1_s elem_size, reg1, mem_operand .if elem_size == 16 mov TMP1, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP1, mem_operand, TMP1, asl #1 mov TMP2, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP2, mem_operand, TMP2, asl #1 vld1.16 {d®1&[0]}, [TMP1, :16] mov TMP1, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP1, mem_operand, TMP1, asl #1 vld1.16 {d®1&[1]}, [TMP2, :16] mov TMP2, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP2, mem_operand, TMP2, asl #1 vld1.16 {d®1&[2]}, [TMP1, :16] vld1.16 {d®1&[3]}, [TMP2, :16] .elseif elem_size == 32 mov TMP1, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP1, mem_operand, TMP1, asl #2 mov TMP2, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP2, mem_operand, TMP2, asl #2 vld1.32 {d®1&[0]}, [TMP1, :32] vld1.32 {d®1&[1]}, [TMP2, :32] .else .error "unsupported" .endif .endm .macro pixld2_s elem_size, reg1, reg2, mem_operand .if 0 /* elem_size == 32 */ mov TMP1, VX, asr #16 add VX, VX, UNIT_X, asl #1 add TMP1, mem_operand, TMP1, asl #2 mov TMP2, VX, asr #16 sub VX, VX, UNIT_X add TMP2, mem_operand, TMP2, asl #2 vld1.32 {d®1&[0]}, [TMP1, :32] mov TMP1, VX, asr #16 add VX, VX, UNIT_X, asl #1 add TMP1, mem_operand, TMP1, asl #2 vld1.32 {d®2&[0]}, [TMP2, :32] mov TMP2, VX, asr #16 add VX, VX, UNIT_X add TMP2, mem_operand, TMP2, asl #2 vld1.32 {d®1&[1]}, [TMP1, :32] vld1.32 {d®2&[1]}, [TMP2, :32] .else pixld1_s elem_size, reg1, mem_operand pixld1_s elem_size, reg2, mem_operand .endif .endm .macro pixld0_s elem_size, reg1, idx, mem_operand .if elem_size == 16 mov TMP1, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP1, mem_operand, TMP1, asl #1 vld1.16 {d®1&[idx]}, [TMP1, :16] .elseif elem_size == 32 mov TMP1, VX, asr #16 adds VX, VX, UNIT_X 5: subpls VX, VX, SRC_WIDTH_FIXED bpl 5b add TMP1, mem_operand, TMP1, asl #2 vld1.32 {d®1&[idx]}, [TMP1, :32] .endif .endm .macro pixld_s_internal numbytes, elem_size, basereg, mem_operand .if numbytes == 32 pixld2_s elem_size, %(basereg+4), %(basereg+5), mem_operand pixld2_s elem_size, %(basereg+6), %(basereg+7), mem_operand pixdeinterleave elem_size, %(basereg+4) .elseif numbytes == 16 pixld2_s elem_size, %(basereg+2), %(basereg+3), mem_operand .elseif numbytes == 8 pixld1_s elem_size, %(basereg+1), mem_operand .elseif numbytes == 4 .if elem_size == 32 pixld0_s elem_size, %(basereg+0), 1, mem_operand .elseif elem_size == 16 pixld0_s elem_size, %(basereg+0), 2, mem_operand pixld0_s elem_size, %(basereg+0), 3, mem_operand .else pixld0_s elem_size, %(basereg+0), 4, mem_operand pixld0_s elem_size, %(basereg+0), 5, mem_operand pixld0_s elem_size, %(basereg+0), 6, mem_operand pixld0_s elem_size, %(basereg+0), 7, mem_operand .endif .elseif numbytes == 2 .if elem_size == 16 pixld0_s elem_size, %(basereg+0), 1, mem_operand .else pixld0_s elem_size, %(basereg+0), 2, mem_operand pixld0_s elem_size, %(basereg+0), 3, mem_operand .endif .elseif numbytes == 1 pixld0_s elem_size, %(basereg+0), 1, mem_operand .else .error "unsupported size: numbytes" .endif .endm .macro pixld_s numpix, bpp, basereg, mem_operand .if bpp > 0 pixld_s_internal %(numpix * bpp / 8), %(bpp), basereg, mem_operand .endif .endm .macro vuzp8 reg1, reg2 vuzp.8 d®1, d®2 .endm .macro vzip8 reg1, reg2 vzip.8 d®1, d®2 .endm /* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ .macro pixdeinterleave bpp, basereg .if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) vuzp8 %(basereg+0), %(basereg+1) vuzp8 %(basereg+2), %(basereg+3) vuzp8 %(basereg+1), %(basereg+3) vuzp8 %(basereg+0), %(basereg+2) .endif .endm /* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ .macro pixinterleave bpp, basereg .if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) vzip8 %(basereg+0), %(basereg+2) vzip8 %(basereg+1), %(basereg+3) vzip8 %(basereg+2), %(basereg+3) vzip8 %(basereg+0), %(basereg+1) .endif .endm /* * This is a macro for implementing cache preload. The main idea is that * cache preload logic is mostly independent from the rest of pixels * processing code. It starts at the top left pixel and moves forward * across pixels and can jump across scanlines. Prefetch distance is * handled in an 'incremental' way: it starts from 0 and advances to the * optimal distance over time. After reaching optimal prefetch distance, * it is kept constant. There are some checks which prevent prefetching * unneeded pixel lines below the image (but it still can prefetch a bit * more data on the right side of the image - not a big issue and may * be actually helpful when rendering text glyphs). Additional trick is * the use of LDR instruction for prefetch instead of PLD when moving to * the next line, the point is that we have a high chance of getting TLB * miss in this case, and PLD would be useless. * * This sounds like it may introduce a noticeable overhead (when working with * fully cached data). But in reality, due to having a separate pipeline and * instruction queue for NEON unit in ARM Cortex-A8, normal ARM code can * execute simultaneously with NEON and be completely shadowed by it. Thus * we get no performance overhead at all (*). This looks like a very nice * feature of Cortex-A8, if used wisely. We don't have a hardware prefetcher, * but still can implement some rather advanced prefetch logic in software * for almost zero cost! * * (*) The overhead of the prefetcher is visible when running some trivial * pixels processing like simple copy. Anyway, having prefetch is a must * when working with the graphics data. */ .macro PF a, x:vararg .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) a x .endif .endm .macro cache_preload std_increment, boost_increment .if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) .if regs_shortage PF ldr ORIG_W, [sp] /* If we are short on regs, ORIG_W is kept on stack */ .endif .if std_increment != 0 PF add PF_X, PF_X, #std_increment .endif PF tst PF_CTL, #0xF PF addne PF_X, PF_X, #boost_increment PF subne PF_CTL, PF_CTL, #1 PF cmp PF_X, ORIG_W .if src_bpp_shift >= 0 PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] .endif .if dst_r_bpp != 0 PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] .endif .if mask_bpp_shift >= 0 PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] .endif PF subge PF_X, PF_X, ORIG_W PF subges PF_CTL, PF_CTL, #0x10 .if src_bpp_shift >= 0 PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! .endif .if dst_r_bpp != 0 PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! .endif .if mask_bpp_shift >= 0 PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! .endif .endif .endm .macro cache_preload_simple .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) .if src_bpp > 0 pld [SRC, #(PREFETCH_DISTANCE_SIMPLE * src_bpp / 8)] .endif .if dst_r_bpp > 0 pld [DST_R, #(PREFETCH_DISTANCE_SIMPLE * dst_r_bpp / 8)] .endif .if mask_bpp > 0 pld [MASK, #(PREFETCH_DISTANCE_SIMPLE * mask_bpp / 8)] .endif .endif .endm .macro fetch_mask_pixblock pixld pixblock_size, mask_bpp, \ (mask_basereg - pixblock_size * mask_bpp / 64), MASK .endm /* * Macro which is used to process leading pixels until destination * pointer is properly aligned (at 16 bytes boundary). When destination * buffer uses 16bpp format, this is unnecessary, or even pointless. */ .macro ensure_destination_ptr_alignment process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head .if dst_w_bpp != 24 tst DST_R, #0xF beq 2f .irp lowbit, 1, 2, 4, 8, 16 local skip1 .if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) .if lowbit < 16 /* we don't need more than 16-byte alignment */ tst DST_R, #lowbit beq 1f .endif pixld_src (lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC pixld (lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK .if dst_r_bpp > 0 pixld_a (lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R .else add DST_R, DST_R, #lowbit .endif PF add PF_X, PF_X, #(lowbit * 8 / dst_w_bpp) sub W, W, #(lowbit * 8 / dst_w_bpp) 1: .endif .endr pixdeinterleave src_bpp, src_basereg pixdeinterleave mask_bpp, mask_basereg pixdeinterleave dst_r_bpp, dst_r_basereg process_pixblock_head cache_preload 0, pixblock_size cache_preload_simple process_pixblock_tail pixinterleave dst_w_bpp, dst_w_basereg .irp lowbit, 1, 2, 4, 8, 16 .if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) .if lowbit < 16 /* we don't need more than 16-byte alignment */ tst DST_W, #lowbit beq 1f .endif pixst_a (lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W 1: .endif .endr .endif 2: .endm /* * Special code for processing up to (pixblock_size - 1) remaining * trailing pixels. As SIMD processing performs operation on * pixblock_size pixels, anything smaller than this has to be loaded * and stored in a special way. Loading and storing of pixel data is * performed in such a way that we fill some 'slots' in the NEON * registers (some slots naturally are unused), then perform compositing * operation as usual. In the end, the data is taken from these 'slots' * and saved to memory. * * cache_preload_flag - allows to suppress prefetch if * set to 0 * dst_aligned_flag - selects whether destination buffer * is aligned */ .macro process_trailing_pixels cache_preload_flag, \ dst_aligned_flag, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head tst W, #(pixblock_size - 1) beq 2f .irp chunk_size, 16, 8, 4, 2, 1 .if pixblock_size > chunk_size tst W, #chunk_size beq 1f pixld_src chunk_size, src_bpp, src_basereg, SRC pixld chunk_size, mask_bpp, mask_basereg, MASK .if dst_aligned_flag != 0 pixld_a chunk_size, dst_r_bpp, dst_r_basereg, DST_R .else pixld chunk_size, dst_r_bpp, dst_r_basereg, DST_R .endif .if cache_preload_flag != 0 PF add PF_X, PF_X, #chunk_size .endif 1: .endif .endr pixdeinterleave src_bpp, src_basereg pixdeinterleave mask_bpp, mask_basereg pixdeinterleave dst_r_bpp, dst_r_basereg process_pixblock_head .if cache_preload_flag != 0 cache_preload 0, pixblock_size cache_preload_simple .endif process_pixblock_tail pixinterleave dst_w_bpp, dst_w_basereg .irp chunk_size, 16, 8, 4, 2, 1 .if pixblock_size > chunk_size tst W, #chunk_size beq 1f .if dst_aligned_flag != 0 pixst_a chunk_size, dst_w_bpp, dst_w_basereg, DST_W .else pixst chunk_size, dst_w_bpp, dst_w_basereg, DST_W .endif 1: .endif .endr 2: .endm /* * Macro, which performs all the needed operations to switch to the next * scanline and start the next loop iteration unless all the scanlines * are already processed. */ .macro advance_to_next_scanline start_of_loop_label .if regs_shortage ldrd W, [sp] /* load W and H (width and height) from stack */ .else mov W, ORIG_W .endif add DST_W, DST_W, DST_STRIDE, lsl #dst_bpp_shift .if src_bpp != 0 add SRC, SRC, SRC_STRIDE, lsl #src_bpp_shift .endif .if mask_bpp != 0 add MASK, MASK, MASK_STRIDE, lsl #mask_bpp_shift .endif .if (dst_w_bpp != 24) sub DST_W, DST_W, W, lsl #dst_bpp_shift .endif .if (src_bpp != 24) && (src_bpp != 0) sub SRC, SRC, W, lsl #src_bpp_shift .endif .if (mask_bpp != 24) && (mask_bpp != 0) sub MASK, MASK, W, lsl #mask_bpp_shift .endif subs H, H, #1 mov DST_R, DST_W .if regs_shortage str H, [sp, #4] /* save updated height to stack */ .endif bge start_of_loop_label .endm /* * Registers are allocated in the following way by default: * d0, d1, d2, d3 - reserved for loading source pixel data * d4, d5, d6, d7 - reserved for loading destination pixel data * d24, d25, d26, d27 - reserved for loading mask pixel data * d28, d29, d30, d31 - final destination pixel data for writeback to memory */ .macro generate_composite_function fname, \ src_bpp_, \ mask_bpp_, \ dst_w_bpp_, \ flags, \ pixblock_size_, \ prefetch_distance, \ init, \ cleanup, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head, \ dst_w_basereg_ = 28, \ dst_r_basereg_ = 4, \ src_basereg_ = 0, \ mask_basereg_ = 24 .func fname .global fname /* For ELF format also set function visibility to hidden */ #ifdef __ELF__ .hidden fname .type fname, %function #endif fname: push {r4-r12, lr} /* save all registers */ /* * Select prefetch type for this function. If prefetch distance is * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch * has to be used instead of ADVANCED. */ .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT .if prefetch_distance == 0 .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE .elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ ((src_bpp_ == 24) || (mask_bpp_ == 24) || (dst_w_bpp_ == 24)) .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE .endif /* * Make some macro arguments globally visible and accessible * from other macros */ .set src_bpp, src_bpp_ .set mask_bpp, mask_bpp_ .set dst_w_bpp, dst_w_bpp_ .set pixblock_size, pixblock_size_ .set dst_w_basereg, dst_w_basereg_ .set dst_r_basereg, dst_r_basereg_ .set src_basereg, src_basereg_ .set mask_basereg, mask_basereg_ .macro pixld_src x:vararg pixld x .endm .macro fetch_src_pixblock pixld_src pixblock_size, src_bpp, \ (src_basereg - pixblock_size * src_bpp / 64), SRC .endm /* * Assign symbolic names to registers */ W .req r0 /* width (is updated during processing) */ H .req r1 /* height (is updated during processing) */ DST_W .req r2 /* destination buffer pointer for writes */ DST_STRIDE .req r3 /* destination image stride */ SRC .req r4 /* source buffer pointer */ SRC_STRIDE .req r5 /* source image stride */ DST_R .req r6 /* destination buffer pointer for reads */ MASK .req r7 /* mask pointer */ MASK_STRIDE .req r8 /* mask stride */ PF_CTL .req r9 /* combined lines counter and prefetch */ /* distance increment counter */ PF_X .req r10 /* pixel index in a scanline for current */ /* pretetch position */ PF_SRC .req r11 /* pointer to source scanline start */ /* for prefetch purposes */ PF_DST .req r12 /* pointer to destination scanline start */ /* for prefetch purposes */ PF_MASK .req r14 /* pointer to mask scanline start */ /* for prefetch purposes */ /* * Check whether we have enough registers for all the local variables. * If we don't have enough registers, original width and height are * kept on top of stack (and 'regs_shortage' variable is set to indicate * this for the rest of code). Even if there are enough registers, the * allocation scheme may be a bit different depending on whether source * or mask is not used. */ .if (PREFETCH_TYPE_CURRENT < PREFETCH_TYPE_ADVANCED) ORIG_W .req r10 /* saved original width */ DUMMY .req r12 /* temporary register */ .set regs_shortage, 0 .elseif mask_bpp == 0 ORIG_W .req r7 /* saved original width */ DUMMY .req r8 /* temporary register */ .set regs_shortage, 0 .elseif src_bpp == 0 ORIG_W .req r4 /* saved original width */ DUMMY .req r5 /* temporary register */ .set regs_shortage, 0 .else ORIG_W .req r1 /* saved original width */ DUMMY .req r1 /* temporary register */ .set regs_shortage, 1 .endif .set mask_bpp_shift, -1 .if src_bpp == 32 .set src_bpp_shift, 2 .elseif src_bpp == 24 .set src_bpp_shift, 0 .elseif src_bpp == 16 .set src_bpp_shift, 1 .elseif src_bpp == 8 .set src_bpp_shift, 0 .elseif src_bpp == 0 .set src_bpp_shift, -1 .else .error "requested src bpp (src_bpp) is not supported" .endif .if mask_bpp == 32 .set mask_bpp_shift, 2 .elseif mask_bpp == 24 .set mask_bpp_shift, 0 .elseif mask_bpp == 8 .set mask_bpp_shift, 0 .elseif mask_bpp == 0 .set mask_bpp_shift, -1 .else .error "requested mask bpp (mask_bpp) is not supported" .endif .if dst_w_bpp == 32 .set dst_bpp_shift, 2 .elseif dst_w_bpp == 24 .set dst_bpp_shift, 0 .elseif dst_w_bpp == 16 .set dst_bpp_shift, 1 .elseif dst_w_bpp == 8 .set dst_bpp_shift, 0 .else .error "requested dst bpp (dst_w_bpp) is not supported" .endif .if (((flags) & FLAG_DST_READWRITE) != 0) .set dst_r_bpp, dst_w_bpp .else .set dst_r_bpp, 0 .endif .if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) .set DEINTERLEAVE_32BPP_ENABLED, 1 .else .set DEINTERLEAVE_32BPP_ENABLED, 0 .endif .if prefetch_distance < 0 || prefetch_distance > 15 .error "invalid prefetch distance (prefetch_distance)" .endif .if src_bpp > 0 ldr SRC, [sp, #40] .endif .if mask_bpp > 0 ldr MASK, [sp, #48] .endif PF mov PF_X, #0 .if src_bpp > 0 ldr SRC_STRIDE, [sp, #44] .endif .if mask_bpp > 0 ldr MASK_STRIDE, [sp, #52] .endif mov DST_R, DST_W .if src_bpp == 24 sub SRC_STRIDE, SRC_STRIDE, W sub SRC_STRIDE, SRC_STRIDE, W, lsl #1 .endif .if mask_bpp == 24 sub MASK_STRIDE, MASK_STRIDE, W sub MASK_STRIDE, MASK_STRIDE, W, lsl #1 .endif .if dst_w_bpp == 24 sub DST_STRIDE, DST_STRIDE, W sub DST_STRIDE, DST_STRIDE, W, lsl #1 .endif /* * Setup advanced prefetcher initial state */ PF mov PF_SRC, SRC PF mov PF_DST, DST_R PF mov PF_MASK, MASK /* PF_CTL = prefetch_distance | ((h - 1) << 4) */ PF mov PF_CTL, H, lsl #4 PF add PF_CTL, #(prefetch_distance - 0x10) init .if regs_shortage push {r0, r1} .endif subs H, H, #1 .if regs_shortage str H, [sp, #4] /* save updated height to stack */ .else mov ORIG_W, W .endif blt 9f cmp W, #(pixblock_size * 2) blt 8f /* * This is the start of the pipelined loop, which if optimized for * long scanlines */ 0: ensure_destination_ptr_alignment process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ pixld_a pixblock_size, dst_r_bpp, \ (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R fetch_src_pixblock pixld pixblock_size, mask_bpp, \ (mask_basereg - pixblock_size * mask_bpp / 64), MASK PF add PF_X, PF_X, #pixblock_size process_pixblock_head cache_preload 0, pixblock_size cache_preload_simple subs W, W, #(pixblock_size * 2) blt 2f 1: process_pixblock_tail_head cache_preload_simple subs W, W, #pixblock_size bge 1b 2: process_pixblock_tail pixst_a pixblock_size, dst_w_bpp, \ (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W /* Process the remaining trailing pixels in the scanline */ process_trailing_pixels 1, 1, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head advance_to_next_scanline 0b .if regs_shortage pop {r0, r1} .endif cleanup pop {r4-r12, pc} /* exit */ /* * This is the start of the loop, designed to process images with small width * (less than pixblock_size * 2 pixels). In this case neither pipelining * nor prefetch are used. */ 8: /* Process exactly pixblock_size pixels if needed */ tst W, #pixblock_size beq 1f pixld pixblock_size, dst_r_bpp, \ (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R fetch_src_pixblock pixld pixblock_size, mask_bpp, \ (mask_basereg - pixblock_size * mask_bpp / 64), MASK process_pixblock_head process_pixblock_tail pixst pixblock_size, dst_w_bpp, \ (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W 1: /* Process the remaining trailing pixels in the scanline */ process_trailing_pixels 0, 0, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head advance_to_next_scanline 8b 9: .if regs_shortage pop {r0, r1} .endif cleanup pop {r4-r12, pc} /* exit */ .purgem fetch_src_pixblock .purgem pixld_src .unreq SRC .unreq MASK .unreq DST_R .unreq DST_W .unreq ORIG_W .unreq W .unreq H .unreq SRC_STRIDE .unreq DST_STRIDE .unreq MASK_STRIDE .unreq PF_CTL .unreq PF_X .unreq PF_SRC .unreq PF_DST .unreq PF_MASK .unreq DUMMY .endfunc .endm /* * A simplified variant of function generation template for a single * scanline processing (for implementing pixman combine functions) */ .macro generate_composite_function_scanline use_nearest_scaling, \ fname, \ src_bpp_, \ mask_bpp_, \ dst_w_bpp_, \ flags, \ pixblock_size_, \ init, \ cleanup, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head, \ dst_w_basereg_ = 28, \ dst_r_basereg_ = 4, \ src_basereg_ = 0, \ mask_basereg_ = 24 .func fname .global fname /* For ELF format also set function visibility to hidden */ #ifdef __ELF__ .hidden fname .type fname, %function #endif fname: .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE /* * Make some macro arguments globally visible and accessible * from other macros */ .set src_bpp, src_bpp_ .set mask_bpp, mask_bpp_ .set dst_w_bpp, dst_w_bpp_ .set pixblock_size, pixblock_size_ .set dst_w_basereg, dst_w_basereg_ .set dst_r_basereg, dst_r_basereg_ .set src_basereg, src_basereg_ .set mask_basereg, mask_basereg_ .if use_nearest_scaling != 0 /* * Assign symbolic names to registers for nearest scaling */ W .req r0 DST_W .req r1 SRC .req r2 VX .req r3 UNIT_X .req ip MASK .req lr TMP1 .req r4 TMP2 .req r5 DST_R .req r6 SRC_WIDTH_FIXED .req r7 .macro pixld_src x:vararg pixld_s x .endm ldr UNIT_X, [sp] push {r4-r8, lr} ldr SRC_WIDTH_FIXED, [sp, #(24 + 4)] .if mask_bpp != 0 ldr MASK, [sp, #(24 + 8)] .endif .else /* * Assign symbolic names to registers */ W .req r0 /* width (is updated during processing) */ DST_W .req r1 /* destination buffer pointer for writes */ SRC .req r2 /* source buffer pointer */ DST_R .req ip /* destination buffer pointer for reads */ MASK .req r3 /* mask pointer */ .macro pixld_src x:vararg pixld x .endm .endif .if (((flags) & FLAG_DST_READWRITE) != 0) .set dst_r_bpp, dst_w_bpp .else .set dst_r_bpp, 0 .endif .if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) .set DEINTERLEAVE_32BPP_ENABLED, 1 .else .set DEINTERLEAVE_32BPP_ENABLED, 0 .endif .macro fetch_src_pixblock pixld_src pixblock_size, src_bpp, \ (src_basereg - pixblock_size * src_bpp / 64), SRC .endm init mov DST_R, DST_W cmp W, #pixblock_size blt 8f ensure_destination_ptr_alignment process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head subs W, W, #pixblock_size blt 7f /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ pixld_a pixblock_size, dst_r_bpp, \ (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R fetch_src_pixblock pixld pixblock_size, mask_bpp, \ (mask_basereg - pixblock_size * mask_bpp / 64), MASK process_pixblock_head subs W, W, #pixblock_size blt 2f 1: process_pixblock_tail_head subs W, W, #pixblock_size bge 1b 2: process_pixblock_tail pixst_a pixblock_size, dst_w_bpp, \ (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W 7: /* Process the remaining trailing pixels in the scanline (dst aligned) */ process_trailing_pixels 0, 1, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head cleanup .if use_nearest_scaling != 0 pop {r4-r8, pc} /* exit */ .else bx lr /* exit */ .endif 8: /* Process the remaining trailing pixels in the scanline (dst unaligned) */ process_trailing_pixels 0, 0, \ process_pixblock_head, \ process_pixblock_tail, \ process_pixblock_tail_head cleanup .if use_nearest_scaling != 0 pop {r4-r8, pc} /* exit */ .unreq DST_R .unreq SRC .unreq W .unreq VX .unreq UNIT_X .unreq TMP1 .unreq TMP2 .unreq DST_W .unreq MASK .unreq SRC_WIDTH_FIXED .else bx lr /* exit */ .unreq SRC .unreq MASK .unreq DST_R .unreq DST_W .unreq W .endif .purgem fetch_src_pixblock .purgem pixld_src .endfunc .endm .macro generate_composite_function_single_scanline x:vararg generate_composite_function_scanline 0, x .endm .macro generate_composite_function_nearest_scanline x:vararg generate_composite_function_scanline 1, x .endm /* Default prologue/epilogue, nothing special needs to be done */ .macro default_init .endm .macro default_cleanup .endm /* * Prologue/epilogue variant which additionally saves/restores d8-d15 * registers (they need to be saved/restored by callee according to ABI). * This is required if the code needs to use all the NEON registers. */ .macro default_init_need_all_regs vpush {d8-d15} .endm .macro default_cleanup_need_all_regs vpop {d8-d15} .endm /******************************************************************************/ /* * Conversion of 8 r5g6b6 pixels packed in 128-bit register (in) * into a planar a8r8g8b8 format (with a, r, g, b color components * stored into 64-bit registers out_a, out_r, out_g, out_b respectively). * * Warning: the conversion is destructive and the original * value (in) is lost. */ .macro convert_0565_to_8888 in, out_a, out_r, out_g, out_b vshrn.u16 out_r, in, #8 vshrn.u16 out_g, in, #3 vsli.u16 in, in, #5 vmov.u8 out_a, #255 vsri.u8 out_r, out_r, #5 vsri.u8 out_g, out_g, #6 vshrn.u16 out_b, in, #2 .endm .macro convert_0565_to_x888 in, out_r, out_g, out_b vshrn.u16 out_r, in, #8 vshrn.u16 out_g, in, #3 vsli.u16 in, in, #5 vsri.u8 out_r, out_r, #5 vsri.u8 out_g, out_g, #6 vshrn.u16 out_b, in, #2 .endm /* * Conversion from planar a8r8g8b8 format (with a, r, g, b color components * in 64-bit registers in_a, in_r, in_g, in_b respectively) into 8 r5g6b6 * pixels packed in 128-bit register (out). Requires two temporary 128-bit * registers (tmp1, tmp2) */ .macro convert_8888_to_0565 in_r, in_g, in_b, out, tmp1, tmp2 vshll.u8 tmp1, in_g, #8 vshll.u8 out, in_r, #8 vshll.u8 tmp2, in_b, #8 vsri.u16 out, tmp1, #5 vsri.u16 out, tmp2, #11 .endm /* * Conversion of four r5g6b5 pixels (in) to four x8r8g8b8 pixels * returned in (out0, out1) registers pair. Requires one temporary * 64-bit register (tmp). 'out1' and 'in' may overlap, the original * value from 'in' is lost */ .macro convert_four_0565_to_x888_packed in, out0, out1, tmp vshl.u16 out0, in, #5 /* G top 6 bits */ vshl.u16 tmp, in, #11 /* B top 5 bits */ vsri.u16 in, in, #5 /* R is ready in top bits */ vsri.u16 out0, out0, #6 /* G is ready in top bits */ vsri.u16 tmp, tmp, #5 /* B is ready in top bits */ vshr.u16 out1, in, #8 /* R is in place */ vsri.u16 out0, tmp, #8 /* G & B is in place */ vzip.u16 out0, out1 /* everything is in place */ .endm Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-neon.c000066400000000000000000000641321271037650300263640ustar00rootroot00000000000000/* * Copyright © 2009 ARM Ltd, Movial Creative Technologies Oy * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of ARM Ltd not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. ARM Ltd makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Ian Rickards (ian.rickards@arm.com) * Author: Jonathan Morton (jonathan.morton@movial.com) * Author: Markku Vire (markku.vire@movial.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" #include "pixman-arm-common.h" PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_x888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0565_0565, uint16_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_0888, uint8_t, 3, uint8_t, 3) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_8888_0565, uint32_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0565_8888, uint16_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_8888_rev, uint8_t, 3, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_0565_rev, uint8_t, 3, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_pixbuf_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_rpixbuf_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, add_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, add_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, over_8888_0565, uint32_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, over_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, out_reverse_8_0565, uint8_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, out_reverse_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_n_0565, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_n_8888, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_reverse_n_8888, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_DST (0, neon, in_n_8, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_0565, uint8_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8888_8888_ca, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8888_0565_ca, uint32_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, add_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, add_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (0, neon, src_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (0, neon, src_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_8888_n_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_8888_n_0565, uint32_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_0565_n_0565, uint16_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, add_8888_n_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8_8_8, uint8_t, 1, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_0565_8_0565, uint16_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8888_8_8888, uint32_t, 1, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8888_8888_8888, uint32_t, 1, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8_8888, uint32_t, 1, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8888_8888, uint32_t, 1, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8_0565, uint32_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_0565_8_0565, uint16_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_8888, OVER, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_0565, OVER, uint32_t, uint16_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_0565, SRC, uint32_t, uint16_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 0565_8888, SRC, uint16_t, uint32_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_0565, OVER, uint32_t, uint16_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, neon, 0565_8_0565, OVER, uint16_t, uint16_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 8888_8888, SRC, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 8888_0565, SRC, uint32_t, uint16_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 0565_x888, SRC, uint16_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 0565_0565, SRC, uint16_t, uint16_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, neon, 8888_8888, OVER, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, neon, 8888_8888, ADD, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 8888_8_8888, SRC, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 8888_8_0565, SRC, uint32_t, uint16_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 0565_8_x888, SRC, uint16_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 0565_8_0565, SRC, uint16_t, uint16_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_8888, OVER, uint32_t, uint32_t) PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_8888, ADD, uint32_t, uint32_t) void pixman_composite_src_n_8_asm_neon (int32_t w, int32_t h, uint8_t *dst, int32_t dst_stride, uint8_t src); void pixman_composite_src_n_0565_asm_neon (int32_t w, int32_t h, uint16_t *dst, int32_t dst_stride, uint16_t src); void pixman_composite_src_n_8888_asm_neon (int32_t w, int32_t h, uint32_t *dst, int32_t dst_stride, uint32_t src); static pixman_bool_t arm_neon_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t _xor) { /* stride is always multiple of 32bit units in pixman */ uint32_t byte_stride = stride * sizeof(uint32_t); switch (bpp) { case 8: pixman_composite_src_n_8_asm_neon ( width, height, (uint8_t *)(((char *) bits) + y * byte_stride + x), byte_stride, _xor & 0xff); return TRUE; case 16: pixman_composite_src_n_0565_asm_neon ( width, height, (uint16_t *)(((char *) bits) + y * byte_stride + x * 2), byte_stride / 2, _xor & 0xffff); return TRUE; case 32: pixman_composite_src_n_8888_asm_neon ( width, height, (uint32_t *)(((char *) bits) + y * byte_stride + x * 4), byte_stride / 4, _xor); return TRUE; default: return FALSE; } } static pixman_bool_t arm_neon_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { if (src_bpp != dst_bpp) return FALSE; switch (src_bpp) { case 16: pixman_composite_src_0565_0565_asm_neon ( width, height, (uint16_t *)(((char *) dst_bits) + dest_y * dst_stride * 4 + dest_x * 2), dst_stride * 2, (uint16_t *)(((char *) src_bits) + src_y * src_stride * 4 + src_x * 2), src_stride * 2); return TRUE; case 32: pixman_composite_src_8888_8888_asm_neon ( width, height, (uint32_t *)(((char *) dst_bits) + dest_y * dst_stride * 4 + dest_x * 4), dst_stride, (uint32_t *)(((char *) src_bits) + src_y * src_stride * 4 + src_x * 4), src_stride); return TRUE; default: return FALSE; } } static const pixman_fast_path_t arm_neon_fast_paths[] = { PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, neon_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, neon_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, neon_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, neon_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, neon_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, neon_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, neon_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, neon_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, neon_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, neon_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, neon_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, neon_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, neon_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, neon_composite_src_0888_0888), PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, x8r8g8b8, neon_composite_src_0888_8888_rev), PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, r5g6b5, neon_composite_src_0888_0565_rev), PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8r8g8b8, neon_composite_src_pixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8b8g8r8, neon_composite_src_rpixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8r8g8b8, neon_composite_src_rpixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8b8g8r8, neon_composite_src_pixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, neon_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, neon_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, neon_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, neon_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8, neon_composite_src_n_8_8), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8, neon_composite_over_n_8_8), PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, neon_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, neon_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, neon_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, neon_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, neon_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, neon_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, neon_composite_over_n_0565), PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, neon_composite_over_n_8888), PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, neon_composite_over_n_8888), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, neon_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, neon_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, neon_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, neon_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, neon_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, neon_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, neon_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, neon_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, r5g6b5, neon_composite_over_8888_n_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, b5g6r5, neon_composite_over_8888_n_0565), PIXMAN_STD_FAST_PATH (OVER, r5g6b5, solid, r5g6b5, neon_composite_over_0565_n_0565), PIXMAN_STD_FAST_PATH (OVER, b5g6r5, solid, b5g6r5, neon_composite_over_0565_n_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, neon_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, neon_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, neon_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, neon_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, r5g6b5, neon_composite_over_8888_8_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, b5g6r5, neon_composite_over_8888_8_0565), PIXMAN_STD_FAST_PATH (OVER, r5g6b5, a8, r5g6b5, neon_composite_over_0565_8_0565), PIXMAN_STD_FAST_PATH (OVER, b5g6r5, a8, b5g6r5, neon_composite_over_0565_8_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, neon_composite_over_8888_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, neon_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, neon_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, neon_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, neon_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, neon_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, neon_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, a8r8g8b8, neon_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, a8b8g8r8, neon_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, neon_composite_add_n_8_8), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, neon_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, neon_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8, a8, a8, neon_composite_add_8_8_8), PIXMAN_STD_FAST_PATH (ADD, r5g6b5, a8, r5g6b5, neon_composite_add_0565_8_0565), PIXMAN_STD_FAST_PATH (ADD, b5g6r5, a8, b5g6r5, neon_composite_add_0565_8_0565), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8, a8r8g8b8, neon_composite_add_8888_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, a8, a8b8g8r8, neon_composite_add_8888_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, a8r8g8b8, neon_composite_add_8888_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, solid, a8r8g8b8, neon_composite_add_8888_n_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, solid, a8b8g8r8, neon_composite_add_8888_n_8888), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, neon_composite_add_8_8), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, neon_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, neon_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (IN, solid, null, a8, neon_composite_in_n_8), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, neon_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, neon_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, r5g6b5, neon_composite_out_reverse_8_0565), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, b5g6r5, neon_composite_out_reverse_8_0565), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8r8g8b8, neon_composite_out_reverse_8_8888), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8b8g8r8, neon_composite_out_reverse_8_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, neon_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, neon_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, b5g6r5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, b5g6r5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, b5g6r5, neon_8888_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, x8b8g8r8, neon_0565_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_8888), /* Note: NONE repeat is not supported yet */ SIMPLE_NEAREST_FAST_PATH_COVER (SRC, r5g6b5, a8r8g8b8, neon_0565_8888), SIMPLE_NEAREST_FAST_PATH_COVER (SRC, b5g6r5, a8b8g8r8, neon_0565_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, r5g6b5, a8r8g8b8, neon_0565_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, b5g6r5, a8b8g8r8, neon_0565_8888), PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_8_0565), PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8b8g8r8, b5g6r5, neon_8888_8_0565), PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, r5g6b5, r5g6b5, neon_0565_8_0565), PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, b5g6r5, b5g6r5, neon_0565_8_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_x888), SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, neon_0565_0565), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, neon_8888_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_8_x888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, r5g6b5, neon_0565_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), { PIXMAN_OP_NONE }, }; #define BIND_COMBINE_U(name) \ void \ pixman_composite_scanline_##name##_mask_asm_neon (int32_t w, \ const uint32_t *dst, \ const uint32_t *src, \ const uint32_t *mask); \ \ void \ pixman_composite_scanline_##name##_asm_neon (int32_t w, \ const uint32_t *dst, \ const uint32_t *src); \ \ static void \ neon_combine_##name##_u (pixman_implementation_t *imp, \ pixman_op_t op, \ uint32_t * dest, \ const uint32_t * src, \ const uint32_t * mask, \ int width) \ { \ if (mask) \ pixman_composite_scanline_##name##_mask_asm_neon (width, dest, \ src, mask); \ else \ pixman_composite_scanline_##name##_asm_neon (width, dest, src); \ } BIND_COMBINE_U (over) BIND_COMBINE_U (add) BIND_COMBINE_U (out_reverse) pixman_implementation_t * _pixman_implementation_create_arm_neon (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, arm_neon_fast_paths); imp->combine_32[PIXMAN_OP_OVER] = neon_combine_over_u; imp->combine_32[PIXMAN_OP_ADD] = neon_combine_add_u; imp->combine_32[PIXMAN_OP_OUT_REVERSE] = neon_combine_out_reverse_u; imp->blt = arm_neon_blt; imp->fill = arm_neon_fill; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-simd-asm-scaled.S000066400000000000000000000113221271037650300303410ustar00rootroot00000000000000/* * Copyright © 2008 Mozilla Corporation * Copyright © 2010 Nokia Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Mozilla Corporation not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Mozilla Corporation makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Jeff Muizelaar (jeff@infidigm.net) * */ /* Prevent the stack from becoming executable */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif .text .arch armv6 .object_arch armv4 .arm .altmacro .p2align 2 /* Supplementary macro for setting function attributes */ .macro pixman_asm_function fname .func fname .global fname #ifdef __ELF__ .hidden fname .type fname, %function #endif fname: .endm /* * Note: This code is only using armv5te instructions (not even armv6), * but is scheduled for ARM Cortex-A8 pipeline. So it might need to * be split into a few variants, tuned for each microarchitecture. * * TODO: In order to get good performance on ARM9/ARM11 cores (which don't * have efficient write combining), it needs to be changed to use 16-byte * aligned writes using STM instruction. * * Nearest scanline scaler macro template uses the following arguments: * fname - name of the function to generate * bpp_shift - (1 << bpp_shift) is the size of pixel in bytes * t - type suffix for LDR/STR instructions * prefetch_distance - prefetch in the source image by that many * pixels ahead * prefetch_braking_distance - stop prefetching when that many pixels are * remaining before the end of scanline */ .macro generate_nearest_scanline_func fname, bpp_shift, t, \ prefetch_distance, \ prefetch_braking_distance pixman_asm_function fname W .req r0 DST .req r1 SRC .req r2 VX .req r3 UNIT_X .req ip TMP1 .req r4 TMP2 .req r5 VXMASK .req r6 PF_OFFS .req r7 SRC_WIDTH_FIXED .req r8 ldr UNIT_X, [sp] push {r4, r5, r6, r7, r8, r10} mvn VXMASK, #((1 << bpp_shift) - 1) ldr SRC_WIDTH_FIXED, [sp, #28] /* define helper macro */ .macro scale_2_pixels ldr&t TMP1, [SRC, TMP1] and TMP2, VXMASK, VX, asr #(16 - bpp_shift) adds VX, VX, UNIT_X str&t TMP1, [DST], #(1 << bpp_shift) 9: subpls VX, VX, SRC_WIDTH_FIXED bpl 9b ldr&t TMP2, [SRC, TMP2] and TMP1, VXMASK, VX, asr #(16 - bpp_shift) adds VX, VX, UNIT_X str&t TMP2, [DST], #(1 << bpp_shift) 9: subpls VX, VX, SRC_WIDTH_FIXED bpl 9b .endm /* now do the scaling */ and TMP1, VXMASK, VX, asr #(16 - bpp_shift) adds VX, VX, UNIT_X 9: subpls VX, VX, SRC_WIDTH_FIXED bpl 9b subs W, W, #(8 + prefetch_braking_distance) blt 2f /* calculate prefetch offset */ mov PF_OFFS, #prefetch_distance mla PF_OFFS, UNIT_X, PF_OFFS, VX 1: /* main loop, process 8 pixels per iteration with prefetch */ pld [SRC, PF_OFFS, asr #(16 - bpp_shift)] add PF_OFFS, UNIT_X, lsl #3 scale_2_pixels scale_2_pixels scale_2_pixels scale_2_pixels subs W, W, #8 bge 1b 2: subs W, W, #(4 - 8 - prefetch_braking_distance) blt 2f 1: /* process the remaining pixels */ scale_2_pixels scale_2_pixels subs W, W, #4 bge 1b 2: tst W, #2 beq 2f scale_2_pixels 2: tst W, #1 ldrne&t TMP1, [SRC, TMP1] strne&t TMP1, [DST] /* cleanup helper macro */ .purgem scale_2_pixels .unreq DST .unreq SRC .unreq W .unreq VX .unreq UNIT_X .unreq TMP1 .unreq TMP2 .unreq VXMASK .unreq PF_OFFS .unreq SRC_WIDTH_FIXED /* return */ pop {r4, r5, r6, r7, r8, r10} bx lr .endfunc .endm generate_nearest_scanline_func \ pixman_scaled_nearest_scanline_0565_0565_SRC_asm_armv6, 1, h, 80, 32 generate_nearest_scanline_func \ pixman_scaled_nearest_scanline_8888_8888_SRC_asm_armv6, 2, , 48, 32 Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-simd-asm.S000066400000000000000000000553421271037650300271220ustar00rootroot00000000000000/* * Copyright © 2012 Raspberry Pi Foundation * Copyright © 2012 RISC OS Open Ltd * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. The copyright holders make no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Ben Avison (bavison@riscosopen.org) * */ /* Prevent the stack from becoming executable */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif .text .arch armv6 .object_arch armv4 .arm .altmacro .p2align 2 #include "pixman-arm-simd-asm.h" /* A head macro should do all processing which results in an output of up to * 16 bytes, as far as the final load instruction. The corresponding tail macro * should complete the processing of the up-to-16 bytes. The calling macro will * sometimes choose to insert a preload or a decrement of X between them. * cond ARM condition code for code block * numbytes Number of output bytes that should be generated this time * firstreg First WK register in which to place output * unaligned_src Whether to use non-wordaligned loads of source image * unaligned_mask Whether to use non-wordaligned loads of mask image * preload If outputting 16 bytes causes 64 bytes to be read, whether an extra preload should be output */ .macro blit_init line_saved_regs STRIDE_D, STRIDE_S .endm .macro blit_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload pixld cond, numbytes, firstreg, SRC, unaligned_src .endm .macro blit_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment WK4 .req STRIDE_D WK5 .req STRIDE_S WK6 .req MASK WK7 .req STRIDE_M 110: pixld , 16, 0, SRC, unaligned_src pixld , 16, 4, SRC, unaligned_src pld [SRC, SCRATCH] pixst , 16, 0, DST pixst , 16, 4, DST subs X, X, #32*8/src_bpp bhs 110b .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm generate_composite_function \ pixman_composite_src_8888_8888_asm_armv6, 32, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ 4, /* prefetch distance */ \ blit_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ blit_process_head, \ nop_macro, /* process tail */ \ blit_inner_loop generate_composite_function \ pixman_composite_src_0565_0565_asm_armv6, 16, 0, 16, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ 4, /* prefetch distance */ \ blit_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ blit_process_head, \ nop_macro, /* process tail */ \ blit_inner_loop generate_composite_function \ pixman_composite_src_8_8_asm_armv6, 8, 0, 8, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ 3, /* prefetch distance */ \ blit_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ blit_process_head, \ nop_macro, /* process tail */ \ blit_inner_loop /******************************************************************************/ .macro src_n_8888_init ldr SRC, [sp, #ARGS_STACK_OFFSET] mov STRIDE_S, SRC mov MASK, SRC mov STRIDE_M, SRC .endm .macro src_n_0565_init ldrh SRC, [sp, #ARGS_STACK_OFFSET] orr SRC, SRC, lsl #16 mov STRIDE_S, SRC mov MASK, SRC mov STRIDE_M, SRC .endm .macro src_n_8_init ldrb SRC, [sp, #ARGS_STACK_OFFSET] orr SRC, SRC, lsl #8 orr SRC, SRC, lsl #16 mov STRIDE_S, SRC mov MASK, SRC mov STRIDE_M, SRC .endm .macro fill_process_tail cond, numbytes, firstreg WK4 .req SRC WK5 .req STRIDE_S WK6 .req MASK WK7 .req STRIDE_M pixst cond, numbytes, 4, DST .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm generate_composite_function \ pixman_composite_src_n_8888_asm_armv6, 0, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ 0, /* prefetch distance doesn't apply */ \ src_n_8888_init \ nop_macro, /* newline */ \ nop_macro /* cleanup */ \ nop_macro /* process head */ \ fill_process_tail generate_composite_function \ pixman_composite_src_n_0565_asm_armv6, 0, 0, 16, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ 0, /* prefetch distance doesn't apply */ \ src_n_0565_init \ nop_macro, /* newline */ \ nop_macro /* cleanup */ \ nop_macro /* process head */ \ fill_process_tail generate_composite_function \ pixman_composite_src_n_8_asm_armv6, 0, 0, 8, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ 0, /* prefetch distance doesn't apply */ \ src_n_8_init \ nop_macro, /* newline */ \ nop_macro /* cleanup */ \ nop_macro /* process head */ \ fill_process_tail /******************************************************************************/ .macro src_x888_8888_pixel, cond, reg orr&cond WK®, WK®, #0xFF000000 .endm .macro pixman_composite_src_x888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload pixld cond, numbytes, firstreg, SRC, unaligned_src .endm .macro pixman_composite_src_x888_8888_process_tail cond, numbytes, firstreg src_x888_8888_pixel cond, %(firstreg+0) .if numbytes >= 8 src_x888_8888_pixel cond, %(firstreg+1) .if numbytes == 16 src_x888_8888_pixel cond, %(firstreg+2) src_x888_8888_pixel cond, %(firstreg+3) .endif .endif .endm generate_composite_function \ pixman_composite_src_x888_8888_asm_armv6, 32, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_SCRATCH, \ 3, /* prefetch distance */ \ nop_macro, /* init */ \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ pixman_composite_src_x888_8888_process_head, \ pixman_composite_src_x888_8888_process_tail /******************************************************************************/ .macro src_0565_8888_init /* Hold loop invariants in MASK and STRIDE_M */ ldr MASK, =0x07E007E0 mov STRIDE_M, #0xFF000000 /* Set GE[3:0] to 1010 so SEL instructions do what we want */ ldr SCRATCH, =0x80008000 uadd8 SCRATCH, SCRATCH, SCRATCH .endm .macro src_0565_8888_2pixels, reg1, reg2 and SCRATCH, WK®1, MASK @ 00000GGGGGG0000000000gggggg00000 bic WK®2, WK®1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg mov WK®1, WK®2, lsl #16 @ rrrrr000000bbbbb0000000000000000 mov SCRATCH, SCRATCH, ror #19 @ GGGG0000ggggggggggg00000GGGGGGGG bic WK®2, WK®2, WK®1, lsr #16 @ RRRRR000000BBBBB0000000000000000 orr WK®1, WK®1, WK®1, lsr #5 @ rrrrrrrrrr0bbbbbbbbbb00000000000 orr WK®2, WK®2, WK®2, lsr #5 @ RRRRRRRRRR0BBBBBBBBBB00000000000 pkhtb WK®1, WK®1, WK®1, asr #5 @ rrrrrrrr--------bbbbbbbb-------- sel WK®1, WK®1, SCRATCH @ rrrrrrrrggggggggbbbbbbbb-------- mov SCRATCH, SCRATCH, ror #16 @ ggg00000GGGGGGGGGGGG0000gggggggg pkhtb WK®2, WK®2, WK®2, asr #5 @ RRRRRRRR--------BBBBBBBB-------- sel WK®2, WK®2, SCRATCH @ RRRRRRRRGGGGGGGGBBBBBBBB-------- orr WK®1, STRIDE_M, WK®1, lsr #8 @ 11111111rrrrrrrrggggggggbbbbbbbb orr WK®2, STRIDE_M, WK®2, lsr #8 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB .endm /* This version doesn't need STRIDE_M, but is one instruction longer. It would however be preferable for an XRGB target, since we could knock off the last 2 instructions, but is that a common case? and SCRATCH, WK®1, MASK @ 00000GGGGGG0000000000gggggg00000 bic WK®1, WK®1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg mov WK®2, WK®1, lsr #16 @ 0000000000000000RRRRR000000BBBBB mov SCRATCH, SCRATCH, ror #27 @ GGGGGGGGGGGG0000ggggggggggg00000 bic WK®1, WK®1, WK®2, lsl #16 @ 0000000000000000rrrrr000000bbbbb mov WK®2, WK®2, lsl #3 @ 0000000000000RRRRR000000BBBBB000 mov WK®1, WK®1, lsl #3 @ 0000000000000rrrrr000000bbbbb000 orr WK®2, WK®2, WK®2, lsr #5 @ 0000000000000RRRRRRRRRR0BBBBBBBB orr WK®1, WK®1, WK®1, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb pkhbt WK®2, WK®2, WK®2, lsl #5 @ --------RRRRRRRR--------BBBBBBBB pkhbt WK®1, WK®1, WK®1, lsl #5 @ --------rrrrrrrr--------bbbbbbbb sel WK®2, SCRATCH, WK®2 @ --------RRRRRRRRGGGGGGGGBBBBBBBB sel WK®1, SCRATCH, WK®1 @ --------rrrrrrrrggggggggbbbbbbbb orr WK®2, WK®2, #0xFF000000 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB orr WK®1, WK®1, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb */ .macro src_0565_8888_1pixel, reg bic SCRATCH, WK®, MASK @ 0000000000000000rrrrr000000bbbbb and WK®, WK®, MASK @ 000000000000000000000gggggg00000 mov SCRATCH, SCRATCH, lsl #3 @ 0000000000000rrrrr000000bbbbb000 mov WK®, WK®, lsl #5 @ 0000000000000000gggggg0000000000 orr SCRATCH, SCRATCH, SCRATCH, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb orr WK®, WK®, WK®, lsr #6 @ 000000000000000gggggggggggg00000 pkhbt SCRATCH, SCRATCH, SCRATCH, lsl #5 @ --------rrrrrrrr--------bbbbbbbb sel WK®, WK®, SCRATCH @ --------rrrrrrrrggggggggbbbbbbbb orr WK®, WK®, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb .endm .macro src_0565_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload .if numbytes == 16 pixldst ld,, 8, firstreg, %(firstreg+2),,, SRC, unaligned_src .elseif numbytes == 8 pixld , 4, firstreg, SRC, unaligned_src .elseif numbytes == 4 pixld , 2, firstreg, SRC, unaligned_src .endif .endm .macro src_0565_8888_process_tail cond, numbytes, firstreg .if numbytes == 16 src_0565_8888_2pixels firstreg, %(firstreg+1) src_0565_8888_2pixels %(firstreg+2), %(firstreg+3) .elseif numbytes == 8 src_0565_8888_2pixels firstreg, %(firstreg+1) .else src_0565_8888_1pixel firstreg .endif .endm generate_composite_function \ pixman_composite_src_0565_8888_asm_armv6, 16, 0, 32, \ FLAG_DST_WRITEONLY | FLAG_BRANCH_OVER, \ 3, /* prefetch distance */ \ src_0565_8888_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ src_0565_8888_process_head, \ src_0565_8888_process_tail /******************************************************************************/ .macro add_8_8_8pixels cond, dst1, dst2 uqadd8&cond WK&dst1, WK&dst1, MASK uqadd8&cond WK&dst2, WK&dst2, STRIDE_M .endm .macro add_8_8_4pixels cond, dst uqadd8&cond WK&dst, WK&dst, MASK .endm .macro add_8_8_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload WK4 .req MASK WK5 .req STRIDE_M .if numbytes == 16 pixld cond, 8, 4, SRC, unaligned_src pixld cond, 16, firstreg, DST, 0 add_8_8_8pixels cond, firstreg, %(firstreg+1) pixld cond, 8, 4, SRC, unaligned_src .else pixld cond, numbytes, 4, SRC, unaligned_src pixld cond, numbytes, firstreg, DST, 0 .endif .unreq WK4 .unreq WK5 .endm .macro add_8_8_process_tail cond, numbytes, firstreg .if numbytes == 16 add_8_8_8pixels cond, %(firstreg+2), %(firstreg+3) .elseif numbytes == 8 add_8_8_8pixels cond, firstreg, %(firstreg+1) .else add_8_8_4pixels cond, firstreg .endif .endm generate_composite_function \ pixman_composite_add_8_8_asm_armv6, 8, 0, 8, \ FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_PRESERVES_SCRATCH, \ 2, /* prefetch distance */ \ nop_macro, /* init */ \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ add_8_8_process_head, \ add_8_8_process_tail /******************************************************************************/ .macro over_8888_8888_init /* Hold loop invariant in MASK */ ldr MASK, =0x00800080 /* Set GE[3:0] to 0101 so SEL instructions do what we want */ uadd8 SCRATCH, MASK, MASK line_saved_regs STRIDE_D, STRIDE_S, ORIG_W .endm .macro over_8888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload WK4 .req STRIDE_D WK5 .req STRIDE_S WK6 .req STRIDE_M WK7 .req ORIG_W pixld , numbytes, %(4+firstreg), SRC, unaligned_src pixld , numbytes, firstreg, DST, 0 .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm .macro over_8888_8888_check_transparent numbytes, reg0, reg1, reg2, reg3 /* Since these colours a premultiplied by alpha, only 0 indicates transparent (any other colour with 0 in the alpha byte is luminous) */ teq WK®0, #0 .if numbytes > 4 teqeq WK®1, #0 .if numbytes > 8 teqeq WK®2, #0 teqeq WK®3, #0 .endif .endif .endm .macro over_8888_8888_prepare next mov WK&next, WK&next, lsr #24 .endm .macro over_8888_8888_1pixel src, dst, offset, next /* src = destination component multiplier */ rsb WK&src, WK&src, #255 /* Split even/odd bytes of dst into SCRATCH/dst */ uxtb16 SCRATCH, WK&dst uxtb16 WK&dst, WK&dst, ror #8 /* Multiply through, adding 0.5 to the upper byte of result for rounding */ mla SCRATCH, SCRATCH, WK&src, MASK mla WK&dst, WK&dst, WK&src, MASK /* Where we would have had a stall between the result of the first MLA and the shifter input, * reload the complete source pixel */ ldr WK&src, [SRC, #offset] /* Multiply by 257/256 to approximate 256/255 */ uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 /* In this stall, start processing the next pixel */ .if offset < -4 mov WK&next, WK&next, lsr #24 .endif uxtab16 WK&dst, WK&dst, WK&dst, ror #8 /* Recombine even/odd bytes of multiplied destination */ mov SCRATCH, SCRATCH, ror #8 sel WK&dst, SCRATCH, WK&dst /* Saturated add of source to multiplied destination */ uqadd8 WK&dst, WK&dst, WK&src .endm .macro over_8888_8888_process_tail cond, numbytes, firstreg WK4 .req STRIDE_D WK5 .req STRIDE_S WK6 .req STRIDE_M WK7 .req ORIG_W over_8888_8888_check_transparent numbytes, %(4+firstreg), %(5+firstreg), %(6+firstreg), %(7+firstreg) beq 10f over_8888_8888_prepare %(4+firstreg) .set PROCESS_REG, firstreg .set PROCESS_OFF, -numbytes .rept numbytes / 4 over_8888_8888_1pixel %(4+PROCESS_REG), %(0+PROCESS_REG), PROCESS_OFF, %(5+PROCESS_REG) .set PROCESS_REG, PROCESS_REG+1 .set PROCESS_OFF, PROCESS_OFF+4 .endr pixst , numbytes, firstreg, DST 10: .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm generate_composite_function \ pixman_composite_over_8888_8888_asm_armv6, 32, 0, 32 \ FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ 2, /* prefetch distance */ \ over_8888_8888_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ over_8888_8888_process_head, \ over_8888_8888_process_tail /******************************************************************************/ /* Multiply each byte of a word by a byte. * Useful when there aren't any obvious ways to fill the stalls with other instructions. * word Register containing 4 bytes * byte Register containing byte multiplier (bits 8-31 must be 0) * tmp Scratch register * half Register containing the constant 0x00800080 * GE[3:0] bits must contain 0101 */ .macro mul_8888_8 word, byte, tmp, half /* Split even/odd bytes of word apart */ uxtb16 tmp, word uxtb16 word, word, ror #8 /* Multiply bytes together with rounding, then by 257/256 */ mla tmp, tmp, byte, half mla word, word, byte, half /* 1 stall follows */ uxtab16 tmp, tmp, tmp, ror #8 /* 1 stall follows */ uxtab16 word, word, word, ror #8 /* Recombine bytes */ mov tmp, tmp, ror #8 sel word, tmp, word .endm /******************************************************************************/ .macro over_8888_n_8888_init /* Mask is constant */ ldr MASK, [sp, #ARGS_STACK_OFFSET+8] /* Hold loop invariant in STRIDE_M */ ldr STRIDE_M, =0x00800080 /* We only want the alpha bits of the constant mask */ mov MASK, MASK, lsr #24 /* Set GE[3:0] to 0101 so SEL instructions do what we want */ uadd8 SCRATCH, STRIDE_M, STRIDE_M line_saved_regs Y, STRIDE_D, STRIDE_S, ORIG_W .endm .macro over_8888_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload WK4 .req Y WK5 .req STRIDE_D WK6 .req STRIDE_S WK7 .req ORIG_W pixld , numbytes, %(4+(firstreg%2)), SRC, unaligned_src pixld , numbytes, firstreg, DST, 0 .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm .macro over_8888_n_8888_1pixel src, dst mul_8888_8 WK&src, MASK, SCRATCH, STRIDE_M sub WK7, WK6, WK&src, lsr #24 mul_8888_8 WK&dst, WK7, SCRATCH, STRIDE_M uqadd8 WK&dst, WK&dst, WK&src .endm .macro over_8888_n_8888_process_tail cond, numbytes, firstreg WK4 .req Y WK5 .req STRIDE_D WK6 .req STRIDE_S WK7 .req ORIG_W over_8888_8888_check_transparent numbytes, %(4+(firstreg%2)), %(5+(firstreg%2)), %(6+firstreg), %(7+firstreg) beq 10f mov WK6, #255 .set PROCESS_REG, firstreg .rept numbytes / 4 .if numbytes == 16 && PROCESS_REG == 2 /* We're using WK6 and WK7 as temporaries, so half way through * 4 pixels, reload the second two source pixels but this time * into WK4 and WK5 */ ldmdb SRC, {WK4, WK5} .endif over_8888_n_8888_1pixel %(4+(PROCESS_REG%2)), %(PROCESS_REG) .set PROCESS_REG, PROCESS_REG+1 .endr pixst , numbytes, firstreg, DST 10: .unreq WK4 .unreq WK5 .unreq WK6 .unreq WK7 .endm generate_composite_function \ pixman_composite_over_8888_n_8888_asm_armv6, 32, 0, 32 \ FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ 2, /* prefetch distance */ \ over_8888_n_8888_init, \ nop_macro, /* newline */ \ nop_macro, /* cleanup */ \ over_8888_n_8888_process_head, \ over_8888_n_8888_process_tail /******************************************************************************/ .macro over_n_8_8888_init /* Source is constant, but splitting it into even/odd bytes is a loop invariant */ ldr SRC, [sp, #ARGS_STACK_OFFSET] /* Not enough registers to hold this constant, but we still use it here to set GE[3:0] */ ldr SCRATCH, =0x00800080 uxtb16 STRIDE_S, SRC uxtb16 SRC, SRC, ror #8 /* Set GE[3:0] to 0101 so SEL instructions do what we want */ uadd8 SCRATCH, SCRATCH, SCRATCH line_saved_regs Y, STRIDE_D, STRIDE_M, ORIG_W .endm .macro over_n_8_8888_newline ldr STRIDE_D, =0x00800080 b 1f .ltorg 1: .endm .macro over_n_8_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload WK4 .req STRIDE_M pixld , numbytes/4, 4, MASK, unaligned_mask pixld , numbytes, firstreg, DST, 0 .unreq WK4 .endm .macro over_n_8_8888_1pixel src, dst uxtb Y, WK4, ror #src*8 /* Trailing part of multiplication of source */ mla SCRATCH, STRIDE_S, Y, STRIDE_D mla Y, SRC, Y, STRIDE_D mov ORIG_W, #255 uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 uxtab16 Y, Y, Y, ror #8 mov SCRATCH, SCRATCH, ror #8 sub ORIG_W, ORIG_W, Y, lsr #24 sel Y, SCRATCH, Y /* Then multiply the destination */ mul_8888_8 WK&dst, ORIG_W, SCRATCH, STRIDE_D uqadd8 WK&dst, WK&dst, Y .endm .macro over_n_8_8888_process_tail cond, numbytes, firstreg WK4 .req STRIDE_M teq WK4, #0 beq 10f .set PROCESS_REG, firstreg .rept numbytes / 4 over_n_8_8888_1pixel %(PROCESS_REG-firstreg), %(PROCESS_REG) .set PROCESS_REG, PROCESS_REG+1 .endr pixst , numbytes, firstreg, DST 10: .unreq WK4 .endm generate_composite_function \ pixman_composite_over_n_8_8888_asm_armv6, 0, 8, 32 \ FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ 2, /* prefetch distance */ \ over_n_8_8888_init, \ over_n_8_8888_newline, \ nop_macro, /* cleanup */ \ over_n_8_8888_process_head, \ over_n_8_8888_process_tail /******************************************************************************/ Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-simd-asm.h000066400000000000000000000763141271037650300271510ustar00rootroot00000000000000/* * Copyright © 2012 Raspberry Pi Foundation * Copyright © 2012 RISC OS Open Ltd * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. The copyright holders make no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Ben Avison (bavison@riscosopen.org) * */ /* * Because the alignment of pixel data to cachelines, and even the number of * cachelines per row can vary from row to row, and because of the need to * preload each scanline once and only once, this prefetch strategy treats * each row of pixels independently. When a pixel row is long enough, there * are three distinct phases of prefetch: * * an inner loop section, where each time a cacheline of data is * processed, another cacheline is preloaded (the exact distance ahead is * determined empirically using profiling results from lowlevel-blt-bench) * * a leading section, where enough cachelines are preloaded to ensure no * cachelines escape being preloaded when the inner loop starts * * a trailing section, where a limited number (0 or more) of cachelines * are preloaded to deal with data (if any) that hangs off the end of the * last iteration of the inner loop, plus any trailing bytes that were not * enough to make up one whole iteration of the inner loop * * There are (in general) three distinct code paths, selected between * depending upon how long the pixel row is. If it is long enough that there * is at least one iteration of the inner loop (as described above) then * this is described as the "wide" case. If it is shorter than that, but * there are still enough bytes output that there is at least one 16-byte- * long, 16-byte-aligned write to the destination (the optimum type of * write), then this is the "medium" case. If it is not even this long, then * this is the "narrow" case, and there is no attempt to align writes to * 16-byte boundaries. In the "medium" and "narrow" cases, all the * cachelines containing data from the pixel row are prefetched up-front. */ /* * Determine whether we put the arguments on the stack for debugging. */ #undef DEBUG_PARAMS /* * Bit flags for 'generate_composite_function' macro which are used * to tune generated functions behavior. */ .set FLAG_DST_WRITEONLY, 0 .set FLAG_DST_READWRITE, 1 .set FLAG_COND_EXEC, 0 .set FLAG_BRANCH_OVER, 2 .set FLAG_PROCESS_PRESERVES_PSR, 0 .set FLAG_PROCESS_CORRUPTS_PSR, 4 .set FLAG_PROCESS_DOESNT_STORE, 0 .set FLAG_PROCESS_DOES_STORE, 8 /* usually because it needs to conditionally skip it */ .set FLAG_NO_SPILL_LINE_VARS, 0 .set FLAG_SPILL_LINE_VARS_WIDE, 16 .set FLAG_SPILL_LINE_VARS_NON_WIDE, 32 .set FLAG_SPILL_LINE_VARS, 48 .set FLAG_PROCESS_CORRUPTS_SCRATCH, 0 .set FLAG_PROCESS_PRESERVES_SCRATCH, 64 /* * Offset into stack where mask and source pointer/stride can be accessed. */ #ifdef DEBUG_PARAMS .set ARGS_STACK_OFFSET, (9*4+9*4) #else .set ARGS_STACK_OFFSET, (9*4) #endif /* * Constants for selecting preferable prefetch type. */ .set PREFETCH_TYPE_NONE, 0 .set PREFETCH_TYPE_STANDARD, 1 /* * Definitions of macros for load/store of pixel data. */ .macro pixldst op, cond=al, numbytes, reg0, reg1, reg2, reg3, base, unaligned=0 .if numbytes == 16 .if unaligned == 1 op&r&cond WK®0, [base], #4 op&r&cond WK®1, [base], #4 op&r&cond WK®2, [base], #4 op&r&cond WK®3, [base], #4 .else op&m&cond&ia base!, {WK®0,WK®1,WK®2,WK®3} .endif .elseif numbytes == 8 .if unaligned == 1 op&r&cond WK®0, [base], #4 op&r&cond WK®1, [base], #4 .else op&m&cond&ia base!, {WK®0,WK®1} .endif .elseif numbytes == 4 op&r&cond WK®0, [base], #4 .elseif numbytes == 2 op&r&cond&h WK®0, [base], #2 .elseif numbytes == 1 op&r&cond&b WK®0, [base], #1 .else .error "unsupported size: numbytes" .endif .endm .macro pixst_baseupdated cond, numbytes, reg0, reg1, reg2, reg3, base .if numbytes == 16 stm&cond&db base, {WK®0,WK®1,WK®2,WK®3} .elseif numbytes == 8 stm&cond&db base, {WK®0,WK®1} .elseif numbytes == 4 str&cond WK®0, [base, #-4] .elseif numbytes == 2 str&cond&h WK®0, [base, #-2] .elseif numbytes == 1 str&cond&b WK®0, [base, #-1] .else .error "unsupported size: numbytes" .endif .endm .macro pixld cond, numbytes, firstreg, base, unaligned pixldst ld, cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base, unaligned .endm .macro pixst cond, numbytes, firstreg, base .if (flags) & FLAG_DST_READWRITE pixst_baseupdated cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base .else pixldst st, cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base .endif .endm .macro PF a, x:vararg .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_STANDARD) a x .endif .endm .macro preload_leading_step1 bpp, ptr, base /* If the destination is already 16-byte aligned, then we need to preload * between 0 and prefetch_distance (inclusive) cache lines ahead so there * are no gaps when the inner loop starts. */ .if bpp > 0 PF bic, ptr, base, #31 .set OFFSET, 0 .rept prefetch_distance+1 PF pld, [ptr, #OFFSET] .set OFFSET, OFFSET+32 .endr .endif .endm .macro preload_leading_step2 bpp, bpp_shift, ptr, base /* However, if the destination is not 16-byte aligned, we may need to * preload more cache lines than that. The question we need to ask is: * are the bytes corresponding to the leading pixels more than the amount * by which the source pointer will be rounded down for preloading, and if * so, by how many cache lines? Effectively, we want to calculate * leading_bytes = ((-dst)&15)*src_bpp/dst_bpp * inner_loop_offset = (src+leading_bytes)&31 * extra_needed = leading_bytes - inner_loop_offset * and test if extra_needed is <= 0, <= 32, or > 32 (where > 32 is only * possible when there are 4 src bytes for every 1 dst byte). */ .if bpp > 0 .ifc base,DST /* The test can be simplified further when preloading the destination */ PF tst, base, #16 PF beq, 61f .else .if bpp/dst_w_bpp == 4 PF add, SCRATCH, base, WK0, lsl #bpp_shift-dst_bpp_shift PF and, SCRATCH, SCRATCH, #31 PF rsb, SCRATCH, SCRATCH, WK0, lsl #bpp_shift-dst_bpp_shift PF sub, SCRATCH, SCRATCH, #1 /* so now ranges are -16..-1 / 0..31 / 32..63 */ PF movs, SCRATCH, SCRATCH, #32-6 /* so this sets NC / nc / Nc */ PF bcs, 61f PF bpl, 60f PF pld, [ptr, #32*(prefetch_distance+2)] .else PF mov, SCRATCH, base, lsl #32-5 PF add, SCRATCH, SCRATCH, WK0, lsl #32-5+bpp_shift-dst_bpp_shift PF rsbs, SCRATCH, SCRATCH, WK0, lsl #32-5+bpp_shift-dst_bpp_shift PF bls, 61f .endif .endif 60: PF pld, [ptr, #32*(prefetch_distance+1)] 61: .endif .endm #define IS_END_OF_GROUP(INDEX,SIZE) ((SIZE) < 2 || ((INDEX) & ~((INDEX)+1)) & ((SIZE)/2)) .macro preload_middle bpp, base, scratch_holds_offset .if bpp > 0 /* prefetch distance = 256/bpp, stm distance = 128/dst_w_bpp */ .if IS_END_OF_GROUP(SUBBLOCK,256/128*dst_w_bpp/bpp) .if scratch_holds_offset PF pld, [base, SCRATCH] .else PF bic, SCRATCH, base, #31 PF pld, [SCRATCH, #32*prefetch_distance] .endif .endif .endif .endm .macro preload_trailing bpp, bpp_shift, base .if bpp > 0 .if bpp*pix_per_block > 256 /* Calculations are more complex if more than one fetch per block */ PF and, WK1, base, #31 PF add, WK1, WK1, WK0, lsl #bpp_shift PF add, WK1, WK1, #32*(bpp*pix_per_block/256-1)*(prefetch_distance+1) PF bic, SCRATCH, base, #31 80: PF pld, [SCRATCH, #32*(prefetch_distance+1)] PF add, SCRATCH, SCRATCH, #32 PF subs, WK1, WK1, #32 PF bhi, 80b .else /* If exactly one fetch per block, then we need either 0, 1 or 2 extra preloads */ PF mov, SCRATCH, base, lsl #32-5 PF adds, SCRATCH, SCRATCH, X, lsl #32-5+bpp_shift PF adceqs, SCRATCH, SCRATCH, #0 /* The instruction above has two effects: ensures Z is only * set if C was clear (so Z indicates that both shifted quantities * were 0), and clears C if Z was set (so C indicates that the sum * of the shifted quantities was greater and not equal to 32) */ PF beq, 82f PF bic, SCRATCH, base, #31 PF bcc, 81f PF pld, [SCRATCH, #32*(prefetch_distance+2)] 81: PF pld, [SCRATCH, #32*(prefetch_distance+1)] 82: .endif .endif .endm .macro preload_line narrow_case, bpp, bpp_shift, base /* "narrow_case" - just means that the macro was invoked from the "narrow" * code path rather than the "medium" one - because in the narrow case, * the row of pixels is known to output no more than 30 bytes, then * (assuming the source pixels are no wider than the the destination * pixels) they cannot possibly straddle more than 2 32-byte cachelines, * meaning there's no need for a loop. * "bpp" - number of bits per pixel in the channel (source, mask or * destination) that's being preloaded, or 0 if this channel is not used * for reading * "bpp_shift" - log2 of ("bpp"/8) (except if "bpp"=0 of course) * "base" - base address register of channel to preload (SRC, MASK or DST) */ .if bpp > 0 .if narrow_case && (bpp <= dst_w_bpp) /* In these cases, each line for each channel is in either 1 or 2 cache lines */ PF bic, WK0, base, #31 PF pld, [WK0] PF add, WK1, base, X, LSL #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 90f PF pld, [WK1] 90: .else PF bic, WK0, base, #31 PF pld, [WK0] PF add, WK1, base, X, lsl #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 92f 91: PF add, WK0, WK0, #32 PF cmp, WK0, WK1 PF pld, [WK0] PF bne, 91b 92: .endif .endif .endm .macro conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, 0 .if decrementx sub&cond X, X, #8*numbytes/dst_w_bpp .endif process_tail cond, numbytes, firstreg .if !((flags) & FLAG_PROCESS_DOES_STORE) pixst cond, numbytes, firstreg, DST .endif .endm .macro conditional_process1 cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .if (flags) & FLAG_BRANCH_OVER .ifc cond,mi bpl 100f .endif .ifc cond,cs bcc 100f .endif .ifc cond,ne beq 100f .endif conditional_process1_helper , process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx 100: .else conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .endif .endm .macro conditional_process2 test, cond1, cond2, process_head, process_tail, numbytes1, numbytes2, firstreg1, firstreg2, unaligned_src, unaligned_mask, decrementx .if (flags) & (FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE) /* Can't interleave reads and writes */ test conditional_process1 cond1, process_head, process_tail, numbytes1, firstreg1, unaligned_src, unaligned_mask, decrementx .if (flags) & FLAG_PROCESS_CORRUPTS_PSR test .endif conditional_process1 cond2, process_head, process_tail, numbytes2, firstreg2, unaligned_src, unaligned_mask, decrementx .else /* Can interleave reads and writes for better scheduling */ test process_head cond1, numbytes1, firstreg1, unaligned_src, unaligned_mask, 0 process_head cond2, numbytes2, firstreg2, unaligned_src, unaligned_mask, 0 .if decrementx sub&cond1 X, X, #8*numbytes1/dst_w_bpp sub&cond2 X, X, #8*numbytes2/dst_w_bpp .endif process_tail cond1, numbytes1, firstreg1 process_tail cond2, numbytes2, firstreg2 pixst cond1, numbytes1, firstreg1, DST pixst cond2, numbytes2, firstreg2, DST .endif .endm .macro test_bits_1_0_ptr movs SCRATCH, WK0, lsl #32-1 /* C,N = bits 1,0 of DST */ .endm .macro test_bits_3_2_ptr movs SCRATCH, WK0, lsl #32-3 /* C,N = bits 3, 2 of DST */ .endm .macro leading_15bytes process_head, process_tail /* On entry, WK0 bits 0-3 = number of bytes until destination is 16-byte aligned */ /* Use unaligned loads in all cases for simplicity */ .if dst_w_bpp == 8 conditional_process2 test_bits_1_0_ptr, mi, cs, process_head, process_tail, 1, 2, 1, 2, 1, 1, 1 .elseif dst_w_bpp == 16 test_bits_1_0_ptr conditional_process1 cs, process_head, process_tail, 2, 2, 1, 1, 1 .endif conditional_process2 test_bits_3_2_ptr, mi, cs, process_head, process_tail, 4, 8, 1, 2, 1, 1, 1 .endm .macro test_bits_3_2_pix movs SCRATCH, X, lsl #dst_bpp_shift+32-3 .endm .macro test_bits_1_0_pix .if dst_w_bpp == 8 movs SCRATCH, X, lsl #dst_bpp_shift+32-1 .else movs SCRATCH, X, lsr #1 .endif .endm .macro trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask conditional_process2 test_bits_3_2_pix, cs, mi, process_head, process_tail, 8, 4, 0, 2, unaligned_src, unaligned_mask, 0 .if dst_w_bpp == 16 test_bits_1_0_pix conditional_process1 cs, process_head, process_tail, 2, 0, unaligned_src, unaligned_mask, 0 .elseif dst_w_bpp == 8 conditional_process2 test_bits_1_0_pix, cs, mi, process_head, process_tail, 2, 1, 0, 1, unaligned_src, unaligned_mask, 0 .endif .endm .macro wide_case_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment 110: .set SUBBLOCK, 0 /* this is a count of STMs; there can be up to 8 STMs per block */ .rept pix_per_block*dst_w_bpp/128 process_head , 16, 0, unaligned_src, unaligned_mask, 1 .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle src_bpp, SRC, 1 .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle mask_bpp, MASK, 1 .else preload_middle src_bpp, SRC, 0 preload_middle mask_bpp, MASK, 0 .endif .if (dst_r_bpp > 0) && ((SUBBLOCK % 2) == 0) /* Because we know that writes are 16-byte aligned, it's relatively easy to ensure that * destination prefetches are 32-byte aligned. It's also the easiest channel to offset * preloads for, to achieve staggered prefetches for multiple channels, because there are * always two STMs per prefetch, so there is always an opposite STM on which to put the * preload. Note, no need to BIC the base register here */ PF pld, [DST, #32*prefetch_distance - dst_alignment] .endif process_tail , 16, 0 .if !((flags) & FLAG_PROCESS_DOES_STORE) pixst , 16, 0, DST .endif .set SUBBLOCK, SUBBLOCK+1 .endr subs X, X, #pix_per_block bhs 110b .endm .macro wide_case_inner_loop_and_trailing_pixels process_head, process_tail, process_inner_loop, exit_label, unaligned_src, unaligned_mask /* Destination now 16-byte aligned; we have at least one block before we have to stop preloading */ .if dst_r_bpp > 0 tst DST, #16 bne 111f process_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, 16 b 112f 111: .endif process_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, 0 112: /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */ .if (src_bpp*pix_per_block > 256) || (mask_bpp*pix_per_block > 256) || (dst_r_bpp*pix_per_block > 256) PF and, WK0, X, #pix_per_block-1 .endif preload_trailing src_bpp, src_bpp_shift, SRC preload_trailing mask_bpp, mask_bpp_shift, MASK preload_trailing dst_r_bpp, dst_bpp_shift, DST add X, X, #(prefetch_distance+2)*pix_per_block - 128/dst_w_bpp /* The remainder of the line is handled identically to the medium case */ medium_case_inner_loop_and_trailing_pixels process_head, process_tail,, exit_label, unaligned_src, unaligned_mask .endm .macro medium_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask 120: process_head , 16, 0, unaligned_src, unaligned_mask, 0 process_tail , 16, 0 .if !((flags) & FLAG_PROCESS_DOES_STORE) pixst , 16, 0, DST .endif subs X, X, #128/dst_w_bpp bhs 120b /* Trailing pixels */ tst X, #128/dst_w_bpp - 1 beq exit_label trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask .endm .macro narrow_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask tst X, #16*8/dst_w_bpp conditional_process1 ne, process_head, process_tail, 16, 0, unaligned_src, unaligned_mask, 0 /* Trailing pixels */ /* In narrow case, it's relatively unlikely to be aligned, so let's do without a branch here */ trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask .endm .macro switch_on_alignment action, process_head, process_tail, process_inner_loop, exit_label /* Note that if we're reading the destination, it's already guaranteed to be aligned at this point */ .if mask_bpp == 8 || mask_bpp == 16 tst MASK, #3 bne 141f .endif .if src_bpp == 8 || src_bpp == 16 tst SRC, #3 bne 140f .endif action process_head, process_tail, process_inner_loop, exit_label, 0, 0 .if src_bpp == 8 || src_bpp == 16 b exit_label 140: action process_head, process_tail, process_inner_loop, exit_label, 1, 0 .endif .if mask_bpp == 8 || mask_bpp == 16 b exit_label 141: .if src_bpp == 8 || src_bpp == 16 tst SRC, #3 bne 142f .endif action process_head, process_tail, process_inner_loop, exit_label, 0, 1 .if src_bpp == 8 || src_bpp == 16 b exit_label 142: action process_head, process_tail, process_inner_loop, exit_label, 1, 1 .endif .endif .endm .macro end_of_line restore_x, vars_spilled, loop_label, last_one .if vars_spilled /* Sadly, GAS doesn't seem have an equivalent of the DCI directive? */ /* This is ldmia sp,{} */ .word 0xE89D0000 | LINE_SAVED_REGS .endif subs Y, Y, #1 .if vars_spilled .if (LINE_SAVED_REGS) & (1<<1) str Y, [sp] .endif .endif add DST, DST, STRIDE_D .if src_bpp > 0 add SRC, SRC, STRIDE_S .endif .if mask_bpp > 0 add MASK, MASK, STRIDE_M .endif .if restore_x mov X, ORIG_W .endif bhs loop_label .ifc "last_one","" .if vars_spilled b 197f .else b 198f .endif .else .if (!vars_spilled) && ((flags) & FLAG_SPILL_LINE_VARS) b 198f .endif .endif .endm .macro generate_composite_function fname, \ src_bpp_, \ mask_bpp_, \ dst_w_bpp_, \ flags_, \ prefetch_distance_, \ init, \ newline, \ cleanup, \ process_head, \ process_tail, \ process_inner_loop .func fname .global fname /* For ELF format also set function visibility to hidden */ #ifdef __ELF__ .hidden fname .type fname, %function #endif /* * Make some macro arguments globally visible and accessible * from other macros */ .set src_bpp, src_bpp_ .set mask_bpp, mask_bpp_ .set dst_w_bpp, dst_w_bpp_ .set flags, flags_ .set prefetch_distance, prefetch_distance_ /* * Select prefetch type for this function. */ .if prefetch_distance == 0 .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE .else .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_STANDARD .endif .if src_bpp == 32 .set src_bpp_shift, 2 .elseif src_bpp == 24 .set src_bpp_shift, 0 .elseif src_bpp == 16 .set src_bpp_shift, 1 .elseif src_bpp == 8 .set src_bpp_shift, 0 .elseif src_bpp == 0 .set src_bpp_shift, -1 .else .error "requested src bpp (src_bpp) is not supported" .endif .if mask_bpp == 32 .set mask_bpp_shift, 2 .elseif mask_bpp == 24 .set mask_bpp_shift, 0 .elseif mask_bpp == 8 .set mask_bpp_shift, 0 .elseif mask_bpp == 0 .set mask_bpp_shift, -1 .else .error "requested mask bpp (mask_bpp) is not supported" .endif .if dst_w_bpp == 32 .set dst_bpp_shift, 2 .elseif dst_w_bpp == 24 .set dst_bpp_shift, 0 .elseif dst_w_bpp == 16 .set dst_bpp_shift, 1 .elseif dst_w_bpp == 8 .set dst_bpp_shift, 0 .else .error "requested dst bpp (dst_w_bpp) is not supported" .endif .if (((flags) & FLAG_DST_READWRITE) != 0) .set dst_r_bpp, dst_w_bpp .else .set dst_r_bpp, 0 .endif .set pix_per_block, 16*8/dst_w_bpp .if src_bpp != 0 .if 32*8/src_bpp > pix_per_block .set pix_per_block, 32*8/src_bpp .endif .endif .if mask_bpp != 0 .if 32*8/mask_bpp > pix_per_block .set pix_per_block, 32*8/mask_bpp .endif .endif .if dst_r_bpp != 0 .if 32*8/dst_r_bpp > pix_per_block .set pix_per_block, 32*8/dst_r_bpp .endif .endif /* The standard entry conditions set up by pixman-arm-common.h are: * r0 = width (pixels) * r1 = height (rows) * r2 = pointer to top-left pixel of destination * r3 = destination stride (pixels) * [sp] = source pixel value, or pointer to top-left pixel of source * [sp,#4] = 0 or source stride (pixels) * The following arguments are unused for non-mask operations * [sp,#8] = mask pixel value, or pointer to top-left pixel of mask * [sp,#12] = 0 or mask stride (pixels) */ /* * Assign symbolic names to registers */ X .req r0 /* pixels to go on this line */ Y .req r1 /* lines to go */ DST .req r2 /* destination pixel pointer */ STRIDE_D .req r3 /* destination stride (bytes, minus width) */ SRC .req r4 /* source pixel pointer */ STRIDE_S .req r5 /* source stride (bytes, minus width) */ MASK .req r6 /* mask pixel pointer (if applicable) */ STRIDE_M .req r7 /* mask stride (bytes, minus width) */ WK0 .req r8 /* pixel data registers */ WK1 .req r9 WK2 .req r10 WK3 .req r11 SCRATCH .req r12 ORIG_W .req r14 /* width (pixels) */ fname: push {r4-r11, lr} /* save all registers */ subs Y, Y, #1 blo 199f #ifdef DEBUG_PARAMS sub sp, sp, #9*4 #endif .if src_bpp > 0 ldr SRC, [sp, #ARGS_STACK_OFFSET] ldr STRIDE_S, [sp, #ARGS_STACK_OFFSET+4] .endif .if mask_bpp > 0 ldr MASK, [sp, #ARGS_STACK_OFFSET+8] ldr STRIDE_M, [sp, #ARGS_STACK_OFFSET+12] .endif #ifdef DEBUG_PARAMS add Y, Y, #1 stmia sp, {r0-r7,pc} sub Y, Y, #1 #endif init lsl STRIDE_D, #dst_bpp_shift /* stride in bytes */ sub STRIDE_D, STRIDE_D, X, lsl #dst_bpp_shift .if src_bpp > 0 lsl STRIDE_S, #src_bpp_shift sub STRIDE_S, STRIDE_S, X, lsl #src_bpp_shift .endif .if mask_bpp > 0 lsl STRIDE_M, #mask_bpp_shift sub STRIDE_M, STRIDE_M, X, lsl #mask_bpp_shift .endif /* Are we not even wide enough to have one 16-byte aligned 16-byte block write? */ cmp X, #2*16*8/dst_w_bpp - 1 blo 170f .if src_bpp || mask_bpp || dst_r_bpp /* Wide and medium cases are the same for fill */ /* To preload ahead on the current line, we need at least (prefetch_distance+2) 32-byte blocks on all prefetch channels */ cmp X, #(prefetch_distance+3)*pix_per_block - 1 blo 160f /* Wide case */ /* Adjust X so that the decrement instruction can also test for * inner loop termination. We want it to stop when there are * (prefetch_distance+1) complete blocks to go. */ sub X, X, #(prefetch_distance+2)*pix_per_block mov ORIG_W, X .if (flags) & FLAG_SPILL_LINE_VARS_WIDE /* This is stmdb sp!,{} */ .word 0xE92D0000 | LINE_SAVED_REGS .endif 151: /* New line */ newline preload_leading_step1 src_bpp, WK1, SRC preload_leading_step1 mask_bpp, WK2, MASK preload_leading_step1 dst_r_bpp, WK3, DST tst DST, #15 beq 154f rsb WK0, DST, #0 /* bits 0-3 = number of leading bytes until destination aligned */ .if (src_bpp != 0 && src_bpp != 2*dst_w_bpp) || (mask_bpp != 0 && mask_bpp != 2*dst_w_bpp) PF and, WK0, WK0, #15 .endif preload_leading_step2 src_bpp, src_bpp_shift, WK1, SRC preload_leading_step2 mask_bpp, mask_bpp_shift, WK2, MASK preload_leading_step2 dst_r_bpp, dst_bpp_shift, WK3, DST leading_15bytes process_head, process_tail 154: /* Destination now 16-byte aligned; we have at least one prefetch on each channel as well as at least one 16-byte output block */ .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) and SCRATCH, SRC, #31 rsb SCRATCH, SCRATCH, #32*prefetch_distance .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) and SCRATCH, MASK, #31 rsb SCRATCH, SCRATCH, #32*prefetch_distance .endif .ifc "process_inner_loop","" switch_on_alignment wide_case_inner_loop_and_trailing_pixels, process_head, process_tail, wide_case_inner_loop, 157f .else switch_on_alignment wide_case_inner_loop_and_trailing_pixels, process_head, process_tail, process_inner_loop, 157f .endif 157: /* Check for another line */ end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_WIDE), 151b .endif .ltorg 160: /* Medium case */ mov ORIG_W, X .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE /* This is stmdb sp!,{} */ .word 0xE92D0000 | LINE_SAVED_REGS .endif 161: /* New line */ newline preload_line 0, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ preload_line 0, mask_bpp, mask_bpp_shift, MASK preload_line 0, dst_r_bpp, dst_bpp_shift, DST sub X, X, #128/dst_w_bpp /* simplifies inner loop termination */ tst DST, #15 beq 164f rsb WK0, DST, #0 /* bits 0-3 = number of leading bytes until destination aligned */ leading_15bytes process_head, process_tail 164: /* Destination now 16-byte aligned; we have at least one 16-byte output block */ switch_on_alignment medium_case_inner_loop_and_trailing_pixels, process_head, process_tail,, 167f 167: /* Check for another line */ end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 161b .ltorg 170: /* Narrow case, less than 31 bytes, so no guarantee of at least one 16-byte block */ .if dst_w_bpp < 32 mov ORIG_W, X .endif .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE /* This is stmdb sp!,{} */ .word 0xE92D0000 | LINE_SAVED_REGS .endif 171: /* New line */ newline preload_line 1, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ preload_line 1, mask_bpp, mask_bpp_shift, MASK preload_line 1, dst_r_bpp, dst_bpp_shift, DST .if dst_w_bpp == 8 tst DST, #3 beq 174f 172: subs X, X, #1 blo 177f process_head , 1, 0, 1, 1, 0 process_tail , 1, 0 .if !((flags) & FLAG_PROCESS_DOES_STORE) pixst , 1, 0, DST .endif tst DST, #3 bne 172b .elseif dst_w_bpp == 16 tst DST, #2 beq 174f subs X, X, #1 blo 177f process_head , 2, 0, 1, 1, 0 process_tail , 2, 0 .if !((flags) & FLAG_PROCESS_DOES_STORE) pixst , 2, 0, DST .endif .endif 174: /* Destination now 4-byte aligned; we have 0 or more output bytes to go */ switch_on_alignment narrow_case_inner_loop_and_trailing_pixels, process_head, process_tail,, 177f 177: /* Check for another line */ end_of_line %(dst_w_bpp < 32), %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 171b, last_one 197: .if (flags) & FLAG_SPILL_LINE_VARS add sp, sp, #LINE_SAVED_REG_COUNT*4 .endif 198: cleanup #ifdef DEBUG_PARAMS add sp, sp, #9*4 /* junk the debug copy of arguments */ #endif 199: pop {r4-r11, pc} /* exit */ .ltorg .unreq X .unreq Y .unreq DST .unreq STRIDE_D .unreq SRC .unreq STRIDE_S .unreq MASK .unreq STRIDE_M .unreq WK0 .unreq WK1 .unreq WK2 .unreq WK3 .unreq SCRATCH .unreq ORIG_W .endfunc .endm .macro line_saved_regs x:vararg .set LINE_SAVED_REGS, 0 .set LINE_SAVED_REG_COUNT, 0 .irp SAVED_REG,x .ifc "SAVED_REG","Y" .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<1) .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 .endif .ifc "SAVED_REG","STRIDE_D" .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<3) .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 .endif .ifc "SAVED_REG","STRIDE_S" .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<5) .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 .endif .ifc "SAVED_REG","STRIDE_M" .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<7) .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 .endif .ifc "SAVED_REG","ORIG_W" .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<14) .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 .endif .endr .endm .macro nop_macro x:vararg .endm Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm-simd.c000066400000000000000000000265601271037650300263640ustar00rootroot00000000000000/* * Copyright © 2008 Mozilla Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Mozilla Corporation not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Mozilla Corporation makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Jeff Muizelaar (jeff@infidigm.net) * */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include "pixman-arm-common.h" #include "pixman-inlines.h" PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_x888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_0565_0565, uint16_t, 1, uint16_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_0565_8888, uint16_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, add_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, over_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, armv6, over_8888_n_8888, uint32_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, armv6, over_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (armv6, 0565_0565, SRC, uint16_t, uint16_t) PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (armv6, 8888_8888, SRC, uint32_t, uint32_t) void pixman_composite_src_n_8888_asm_armv6 (int32_t w, int32_t h, uint32_t *dst, int32_t dst_stride, uint32_t src); void pixman_composite_src_n_0565_asm_armv6 (int32_t w, int32_t h, uint16_t *dst, int32_t dst_stride, uint16_t src); void pixman_composite_src_n_8_asm_armv6 (int32_t w, int32_t h, uint8_t *dst, int32_t dst_stride, uint8_t src); static pixman_bool_t arm_simd_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, /* in 32-bit words */ int bpp, int x, int y, int width, int height, uint32_t _xor) { /* stride is always multiple of 32bit units in pixman */ uint32_t byte_stride = stride * sizeof(uint32_t); switch (bpp) { case 8: pixman_composite_src_n_8_asm_armv6 ( width, height, (uint8_t *)(((char *) bits) + y * byte_stride + x), byte_stride, _xor & 0xff); return TRUE; case 16: pixman_composite_src_n_0565_asm_armv6 ( width, height, (uint16_t *)(((char *) bits) + y * byte_stride + x * 2), byte_stride / 2, _xor & 0xffff); return TRUE; case 32: pixman_composite_src_n_8888_asm_armv6 ( width, height, (uint32_t *)(((char *) bits) + y * byte_stride + x * 4), byte_stride / 4, _xor); return TRUE; default: return FALSE; } } static pixman_bool_t arm_simd_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, /* in 32-bit words */ int dst_stride, /* in 32-bit words */ int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { if (src_bpp != dst_bpp) return FALSE; switch (src_bpp) { case 8: pixman_composite_src_8_8_asm_armv6 ( width, height, (uint8_t *)(((char *) dst_bits) + dest_y * dst_stride * 4 + dest_x * 1), dst_stride * 4, (uint8_t *)(((char *) src_bits) + src_y * src_stride * 4 + src_x * 1), src_stride * 4); return TRUE; case 16: pixman_composite_src_0565_0565_asm_armv6 ( width, height, (uint16_t *)(((char *) dst_bits) + dest_y * dst_stride * 4 + dest_x * 2), dst_stride * 2, (uint16_t *)(((char *) src_bits) + src_y * src_stride * 4 + src_x * 2), src_stride * 2); return TRUE; case 32: pixman_composite_src_8888_8888_asm_armv6 ( width, height, (uint32_t *)(((char *) dst_bits) + dest_y * dst_stride * 4 + dest_x * 4), dst_stride, (uint32_t *)(((char *) src_bits) + src_y * src_stride * 4 + src_x * 4), src_stride); return TRUE; default: return FALSE; } } static const pixman_fast_path_t arm_simd_fast_paths[] = { PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, armv6_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, armv6_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, armv6_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, a1r5g5b5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a1b5g5r5, null, a1b5g5r5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, x1r5g5b5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a1b5g5r5, null, x1b5g5r5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, x1r5g5b5, null, x1r5g5b5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, x1b5g5r5, null, x1b5g5r5, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a4r4g4b4, null, a4r4g4b4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a4b4g4r4, null, a4b4g4r4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a4r4g4b4, null, x4r4g4b4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a4b4g4r4, null, x4b4g4r4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, x4r4g4b4, null, x4r4g4b4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, x4b4g4r4, null, x4b4g4r4, armv6_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a8, null, a8, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, r3g3b2, null, r3g3b2, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, b2g3r3, null, b2g3r3, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, a2r2g2b2, null, a2r2g2b2, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, a2b2g2r2, null, a2b2g2r2, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, c8, null, c8, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, g8, null, g8, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, x4a4, null, x4a4, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, x4c4, null, x4c4, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, x4g4, null, x4g4, armv6_composite_src_8_8), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, armv6_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, armv6_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, armv6_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, armv6_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, armv6_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, armv6_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, armv6_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, armv6_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, armv6_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, armv6_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, armv6_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, armv6_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, armv6_composite_add_8_8), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, armv6_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, armv6_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, armv6_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, armv6_composite_over_n_8_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, r5g6b5, armv6_0565_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, b5g6r5, armv6_0565_0565), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, armv6_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, armv6_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, armv6_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, armv6_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, armv6_8888_8888), PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, armv6_8888_8888), { PIXMAN_OP_NONE }, }; pixman_implementation_t * _pixman_implementation_create_arm_simd (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, arm_simd_fast_paths); imp->blt = arm_simd_blt; imp->fill = arm_simd_fill; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-arm.c000066400000000000000000000126701271037650300254270ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" typedef enum { ARM_V7 = (1 << 0), ARM_V6 = (1 << 1), ARM_VFP = (1 << 2), ARM_NEON = (1 << 3), ARM_IWMMXT = (1 << 4) } arm_cpu_features_t; #if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT) #if defined(_MSC_VER) /* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */ #include extern int pixman_msvc_try_arm_neon_op (); extern int pixman_msvc_try_arm_simd_op (); static arm_cpu_features_t detect_cpu_features (void) { arm_cpu_features_t features = 0; __try { pixman_msvc_try_arm_simd_op (); features |= ARM_V6; } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) { } __try { pixman_msvc_try_arm_neon_op (); features |= ARM_NEON; } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) { } return features; } #elif defined(__APPLE__) && defined(TARGET_OS_IPHONE) /* iOS */ #include "TargetConditionals.h" static arm_cpu_features_t detect_cpu_features (void) { arm_cpu_features_t features = 0; features |= ARM_V6; /* Detection of ARM NEON on iOS is fairly simple because iOS binaries * contain separate executable images for each processor architecture. * So all we have to do is detect the armv7 architecture build. The * operating system automatically runs the armv7 binary for armv7 devices * and the armv6 binary for armv6 devices. */ #if defined(__ARM_NEON__) features |= ARM_NEON; #endif return features; } #elif defined(__ANDROID__) || defined(ANDROID) /* Android */ #include static arm_cpu_features_t detect_cpu_features (void) { arm_cpu_features_t features = 0; AndroidCpuFamily cpu_family; uint64_t cpu_features; cpu_family = android_getCpuFamily(); cpu_features = android_getCpuFeatures(); if (cpu_family == ANDROID_CPU_FAMILY_ARM) { if (cpu_features & ANDROID_CPU_ARM_FEATURE_ARMv7) features |= ARM_V7; if (cpu_features & ANDROID_CPU_ARM_FEATURE_VFPv3) features |= ARM_VFP; if (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON) features |= ARM_NEON; } return features; } #elif defined (__linux__) /* linux ELF */ #include #include #include #include #include #include #include static arm_cpu_features_t detect_cpu_features (void) { arm_cpu_features_t features = 0; Elf32_auxv_t aux; int fd; fd = open ("/proc/self/auxv", O_RDONLY); if (fd >= 0) { while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t)) { if (aux.a_type == AT_HWCAP) { uint32_t hwcap = aux.a_un.a_val; /* hardcode these values to avoid depending on specific * versions of the hwcap header, e.g. HWCAP_NEON */ if ((hwcap & 64) != 0) features |= ARM_VFP; if ((hwcap & 512) != 0) features |= ARM_IWMMXT; /* this flag is only present on kernel 2.6.29 */ if ((hwcap & 4096) != 0) features |= ARM_NEON; } else if (aux.a_type == AT_PLATFORM) { const char *plat = (const char*) aux.a_un.a_val; if (strncmp (plat, "v7l", 3) == 0) features |= (ARM_V7 | ARM_V6); else if (strncmp (plat, "v6l", 3) == 0) features |= ARM_V6; } } close (fd); } return features; } #else /* Unknown */ static arm_cpu_features_t detect_cpu_features (void) { return 0; } #endif /* Linux elf */ static pixman_bool_t have_feature (arm_cpu_features_t feature) { static pixman_bool_t initialized; static arm_cpu_features_t features; if (!initialized) { features = detect_cpu_features(); initialized = TRUE; } return (features & feature) == feature; } #endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */ pixman_implementation_t * _pixman_arm_get_implementations (pixman_implementation_t *imp) { #ifdef USE_ARM_SIMD if (!_pixman_disabled ("arm-simd") && have_feature (ARM_V6)) imp = _pixman_implementation_create_arm_simd (imp); #endif #ifdef USE_ARM_IWMMXT if (!_pixman_disabled ("arm-iwmmxt") && have_feature (ARM_IWMMXT)) imp = _pixman_implementation_create_mmx (imp); #endif #ifdef USE_ARM_NEON if (!_pixman_disabled ("arm-neon") && have_feature (ARM_NEON)) imp = _pixman_implementation_create_arm_neon (imp); #endif return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-bits-image.c000066400000000000000000001347651271037650300267030ustar00rootroot00000000000000/* * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * 2008 Aaron Plattner, NVIDIA Corporation * Copyright © 2000 SuSE, Inc. * Copyright © 2007, 2009 Red Hat, Inc. * Copyright © 2008 André Tupinambá * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "pixman-private.h" #include "pixman-combine32.h" #include "pixman-inlines.h" static uint32_t * _pixman_image_get_scanline_generic_float (pixman_iter_t * iter, const uint32_t *mask) { pixman_iter_get_scanline_t fetch_32 = iter->data; uint32_t *buffer = iter->buffer; fetch_32 (iter, NULL); pixman_expand_to_float ((argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); return iter->buffer; } /* Fetch functions */ static force_inline uint32_t fetch_pixel_no_alpha (bits_image_t *image, int x, int y, pixman_bool_t check_bounds) { if (check_bounds && (x < 0 || x >= image->width || y < 0 || y >= image->height)) { return 0; } return image->fetch_pixel_32 (image, x, y); } typedef uint32_t (* get_pixel_t) (bits_image_t *image, int x, int y, pixman_bool_t check_bounds); static force_inline uint32_t bits_image_fetch_pixel_nearest (bits_image_t *image, pixman_fixed_t x, pixman_fixed_t y, get_pixel_t get_pixel) { int x0 = pixman_fixed_to_int (x - pixman_fixed_e); int y0 = pixman_fixed_to_int (y - pixman_fixed_e); if (image->common.repeat != PIXMAN_REPEAT_NONE) { repeat (image->common.repeat, &x0, image->width); repeat (image->common.repeat, &y0, image->height); return get_pixel (image, x0, y0, FALSE); } else { return get_pixel (image, x0, y0, TRUE); } } static force_inline uint32_t bits_image_fetch_pixel_bilinear (bits_image_t *image, pixman_fixed_t x, pixman_fixed_t y, get_pixel_t get_pixel) { pixman_repeat_t repeat_mode = image->common.repeat; int width = image->width; int height = image->height; int x1, y1, x2, y2; uint32_t tl, tr, bl, br; int32_t distx, disty; x1 = x - pixman_fixed_1 / 2; y1 = y - pixman_fixed_1 / 2; distx = pixman_fixed_to_bilinear_weight (x1); disty = pixman_fixed_to_bilinear_weight (y1); x1 = pixman_fixed_to_int (x1); y1 = pixman_fixed_to_int (y1); x2 = x1 + 1; y2 = y1 + 1; if (repeat_mode != PIXMAN_REPEAT_NONE) { repeat (repeat_mode, &x1, width); repeat (repeat_mode, &y1, height); repeat (repeat_mode, &x2, width); repeat (repeat_mode, &y2, height); tl = get_pixel (image, x1, y1, FALSE); bl = get_pixel (image, x1, y2, FALSE); tr = get_pixel (image, x2, y1, FALSE); br = get_pixel (image, x2, y2, FALSE); } else { tl = get_pixel (image, x1, y1, TRUE); tr = get_pixel (image, x2, y1, TRUE); bl = get_pixel (image, x1, y2, TRUE); br = get_pixel (image, x2, y2, TRUE); } return bilinear_interpolation (tl, tr, bl, br, distx, disty); } static uint32_t * bits_image_fetch_bilinear_no_repeat_8888 (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t * ima = iter->image; int offset = iter->x; int line = iter->y++; int width = iter->width; uint32_t * buffer = iter->buffer; bits_image_t *bits = &ima->bits; pixman_fixed_t x_top, x_bottom, x; pixman_fixed_t ux_top, ux_bottom, ux; pixman_vector_t v; uint32_t top_mask, bottom_mask; uint32_t *top_row; uint32_t *bottom_row; uint32_t *end; uint32_t zero[2] = { 0, 0 }; uint32_t one = 1; int y, y1, y2; int disty; int mask_inc; int w; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (bits->common.transform, &v)) return iter->buffer; ux = ux_top = ux_bottom = bits->common.transform->matrix[0][0]; x = x_top = x_bottom = v.vector[0] - pixman_fixed_1/2; y = v.vector[1] - pixman_fixed_1/2; disty = pixman_fixed_to_bilinear_weight (y); /* Load the pointers to the first and second lines from the source * image that bilinear code must read. * * The main trick in this code is about the check if any line are * outside of the image; * * When I realize that a line (any one) is outside, I change * the pointer to a dummy area with zeros. Once I change this, I * must be sure the pointer will not change, so I set the * variables to each pointer increments inside the loop. */ y1 = pixman_fixed_to_int (y); y2 = y1 + 1; if (y1 < 0 || y1 >= bits->height) { top_row = zero; x_top = 0; ux_top = 0; } else { top_row = bits->bits + y1 * bits->rowstride; x_top = x; ux_top = ux; } if (y2 < 0 || y2 >= bits->height) { bottom_row = zero; x_bottom = 0; ux_bottom = 0; } else { bottom_row = bits->bits + y2 * bits->rowstride; x_bottom = x; ux_bottom = ux; } /* Instead of checking whether the operation uses the mast in * each loop iteration, verify this only once and prepare the * variables to make the code smaller inside the loop. */ if (!mask) { mask_inc = 0; mask = &one; } else { /* If have a mask, prepare the variables to check it */ mask_inc = 1; } /* If both are zero, then the whole thing is zero */ if (top_row == zero && bottom_row == zero) { memset (buffer, 0, width * sizeof (uint32_t)); return iter->buffer; } else if (bits->format == PIXMAN_x8r8g8b8) { if (top_row == zero) { top_mask = 0; bottom_mask = 0xff000000; } else if (bottom_row == zero) { top_mask = 0xff000000; bottom_mask = 0; } else { top_mask = 0xff000000; bottom_mask = 0xff000000; } } else { top_mask = 0; bottom_mask = 0; } end = buffer + width; /* Zero fill to the left of the image */ while (buffer < end && x < pixman_fixed_minus_1) { *buffer++ = 0; x += ux; x_top += ux_top; x_bottom += ux_bottom; mask += mask_inc; } /* Left edge */ while (buffer < end && x < 0) { uint32_t tr, br; int32_t distx; tr = top_row[pixman_fixed_to_int (x_top) + 1] | top_mask; br = bottom_row[pixman_fixed_to_int (x_bottom) + 1] | bottom_mask; distx = pixman_fixed_to_bilinear_weight (x); *buffer++ = bilinear_interpolation (0, tr, 0, br, distx, disty); x += ux; x_top += ux_top; x_bottom += ux_bottom; mask += mask_inc; } /* Main part */ w = pixman_int_to_fixed (bits->width - 1); while (buffer < end && x < w) { if (*mask) { uint32_t tl, tr, bl, br; int32_t distx; tl = top_row [pixman_fixed_to_int (x_top)] | top_mask; tr = top_row [pixman_fixed_to_int (x_top) + 1] | top_mask; bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask; br = bottom_row [pixman_fixed_to_int (x_bottom) + 1] | bottom_mask; distx = pixman_fixed_to_bilinear_weight (x); *buffer = bilinear_interpolation (tl, tr, bl, br, distx, disty); } buffer++; x += ux; x_top += ux_top; x_bottom += ux_bottom; mask += mask_inc; } /* Right Edge */ w = pixman_int_to_fixed (bits->width); while (buffer < end && x < w) { if (*mask) { uint32_t tl, bl; int32_t distx; tl = top_row [pixman_fixed_to_int (x_top)] | top_mask; bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask; distx = pixman_fixed_to_bilinear_weight (x); *buffer = bilinear_interpolation (tl, 0, bl, 0, distx, disty); } buffer++; x += ux; x_top += ux_top; x_bottom += ux_bottom; mask += mask_inc; } /* Zero fill to the left of the image */ while (buffer < end) *buffer++ = 0; return iter->buffer; } static force_inline uint32_t bits_image_fetch_pixel_convolution (bits_image_t *image, pixman_fixed_t x, pixman_fixed_t y, get_pixel_t get_pixel) { pixman_fixed_t *params = image->common.filter_params; int x_off = (params[0] - pixman_fixed_1) >> 1; int y_off = (params[1] - pixman_fixed_1) >> 1; int32_t cwidth = pixman_fixed_to_int (params[0]); int32_t cheight = pixman_fixed_to_int (params[1]); int32_t i, j, x1, x2, y1, y2; pixman_repeat_t repeat_mode = image->common.repeat; int width = image->width; int height = image->height; int srtot, sgtot, sbtot, satot; params += 2; x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); x2 = x1 + cwidth; y2 = y1 + cheight; srtot = sgtot = sbtot = satot = 0; for (i = y1; i < y2; ++i) { for (j = x1; j < x2; ++j) { int rx = j; int ry = i; pixman_fixed_t f = *params; if (f) { uint32_t pixel; if (repeat_mode != PIXMAN_REPEAT_NONE) { repeat (repeat_mode, &rx, width); repeat (repeat_mode, &ry, height); pixel = get_pixel (image, rx, ry, FALSE); } else { pixel = get_pixel (image, rx, ry, TRUE); } srtot += (int)RED_8 (pixel) * f; sgtot += (int)GREEN_8 (pixel) * f; sbtot += (int)BLUE_8 (pixel) * f; satot += (int)ALPHA_8 (pixel) * f; } params++; } } satot = (satot + 0x8000) >> 16; srtot = (srtot + 0x8000) >> 16; sgtot = (sgtot + 0x8000) >> 16; sbtot = (sbtot + 0x8000) >> 16; satot = CLIP (satot, 0, 0xff); srtot = CLIP (srtot, 0, 0xff); sgtot = CLIP (sgtot, 0, 0xff); sbtot = CLIP (sbtot, 0, 0xff); return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot)); } static uint32_t bits_image_fetch_pixel_separable_convolution (bits_image_t *image, pixman_fixed_t x, pixman_fixed_t y, get_pixel_t get_pixel) { pixman_fixed_t *params = image->common.filter_params; pixman_repeat_t repeat_mode = image->common.repeat; int width = image->width; int height = image->height; int cwidth = pixman_fixed_to_int (params[0]); int cheight = pixman_fixed_to_int (params[1]); int x_phase_bits = pixman_fixed_to_int (params[2]); int y_phase_bits = pixman_fixed_to_int (params[3]); int x_phase_shift = 16 - x_phase_bits; int y_phase_shift = 16 - y_phase_bits; int x_off = ((cwidth << 16) - pixman_fixed_1) >> 1; int y_off = ((cheight << 16) - pixman_fixed_1) >> 1; pixman_fixed_t *y_params; int srtot, sgtot, sbtot, satot; int32_t x1, x2, y1, y2; int32_t px, py; int i, j; /* Round x and y to the middle of the closest phase before continuing. This * ensures that the convolution matrix is aligned right, since it was * positioned relative to a particular phase (and not relative to whatever * exact fraction we happen to get here). */ x = ((x >> x_phase_shift) << x_phase_shift) + ((1 << x_phase_shift) >> 1); y = ((y >> y_phase_shift) << y_phase_shift) + ((1 << y_phase_shift) >> 1); px = (x & 0xffff) >> x_phase_shift; py = (y & 0xffff) >> y_phase_shift; y_params = params + 4 + (1 << x_phase_bits) * cwidth + py * cheight; x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); x2 = x1 + cwidth; y2 = y1 + cheight; srtot = sgtot = sbtot = satot = 0; for (i = y1; i < y2; ++i) { pixman_fixed_48_16_t fy = *y_params++; pixman_fixed_t *x_params = params + 4 + px * cwidth; if (fy) { for (j = x1; j < x2; ++j) { pixman_fixed_t fx = *x_params++; int rx = j; int ry = i; if (fx) { pixman_fixed_t f; uint32_t pixel; if (repeat_mode != PIXMAN_REPEAT_NONE) { repeat (repeat_mode, &rx, width); repeat (repeat_mode, &ry, height); pixel = get_pixel (image, rx, ry, FALSE); } else { pixel = get_pixel (image, rx, ry, TRUE); } f = (fy * fx + 0x8000) >> 16; srtot += (int)RED_8 (pixel) * f; sgtot += (int)GREEN_8 (pixel) * f; sbtot += (int)BLUE_8 (pixel) * f; satot += (int)ALPHA_8 (pixel) * f; } } } } satot = (satot + 0x8000) >> 16; srtot = (srtot + 0x8000) >> 16; sgtot = (sgtot + 0x8000) >> 16; sbtot = (sbtot + 0x8000) >> 16; satot = CLIP (satot, 0, 0xff); srtot = CLIP (srtot, 0, 0xff); sgtot = CLIP (sgtot, 0, 0xff); sbtot = CLIP (sbtot, 0, 0xff); return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot)); } static force_inline uint32_t bits_image_fetch_pixel_filtered (bits_image_t *image, pixman_fixed_t x, pixman_fixed_t y, get_pixel_t get_pixel) { switch (image->common.filter) { case PIXMAN_FILTER_NEAREST: case PIXMAN_FILTER_FAST: return bits_image_fetch_pixel_nearest (image, x, y, get_pixel); break; case PIXMAN_FILTER_BILINEAR: case PIXMAN_FILTER_GOOD: case PIXMAN_FILTER_BEST: return bits_image_fetch_pixel_bilinear (image, x, y, get_pixel); break; case PIXMAN_FILTER_CONVOLUTION: return bits_image_fetch_pixel_convolution (image, x, y, get_pixel); break; case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: return bits_image_fetch_pixel_separable_convolution (image, x, y, get_pixel); break; default: break; } return 0; } static uint32_t * bits_image_fetch_affine_no_alpha (pixman_iter_t * iter, const uint32_t * mask) { pixman_image_t *image = iter->image; int offset = iter->x; int line = iter->y++; int width = iter->width; uint32_t * buffer = iter->buffer; pixman_fixed_t x, y; pixman_fixed_t ux, uy; pixman_vector_t v; int i; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (image->common.transform) { if (!pixman_transform_point_3d (image->common.transform, &v)) return iter->buffer; ux = image->common.transform->matrix[0][0]; uy = image->common.transform->matrix[1][0]; } else { ux = pixman_fixed_1; uy = 0; } x = v.vector[0]; y = v.vector[1]; for (i = 0; i < width; ++i) { if (!mask || mask[i]) { buffer[i] = bits_image_fetch_pixel_filtered ( &image->bits, x, y, fetch_pixel_no_alpha); } x += ux; y += uy; } return buffer; } /* General fetcher */ static force_inline uint32_t fetch_pixel_general (bits_image_t *image, int x, int y, pixman_bool_t check_bounds) { uint32_t pixel; if (check_bounds && (x < 0 || x >= image->width || y < 0 || y >= image->height)) { return 0; } pixel = image->fetch_pixel_32 (image, x, y); if (image->common.alpha_map) { uint32_t pixel_a; x -= image->common.alpha_origin_x; y -= image->common.alpha_origin_y; if (x < 0 || x >= image->common.alpha_map->width || y < 0 || y >= image->common.alpha_map->height) { pixel_a = 0; } else { pixel_a = image->common.alpha_map->fetch_pixel_32 ( image->common.alpha_map, x, y); pixel_a = ALPHA_8 (pixel_a); } pixel &= 0x00ffffff; pixel |= (pixel_a << 24); } return pixel; } static uint32_t * bits_image_fetch_general (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int offset = iter->x; int line = iter->y++; int width = iter->width; uint32_t * buffer = iter->buffer; pixman_fixed_t x, y, w; pixman_fixed_t ux, uy, uw; pixman_vector_t v; int i; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (image->common.transform) { if (!pixman_transform_point_3d (image->common.transform, &v)) return buffer; ux = image->common.transform->matrix[0][0]; uy = image->common.transform->matrix[1][0]; uw = image->common.transform->matrix[2][0]; } else { ux = pixman_fixed_1; uy = 0; uw = 0; } x = v.vector[0]; y = v.vector[1]; w = v.vector[2]; for (i = 0; i < width; ++i) { pixman_fixed_t x0, y0; if (!mask || mask[i]) { if (w != 0) { x0 = ((pixman_fixed_48_16_t)x << 16) / w; y0 = ((pixman_fixed_48_16_t)y << 16) / w; } else { x0 = 0; y0 = 0; } buffer[i] = bits_image_fetch_pixel_filtered ( &image->bits, x0, y0, fetch_pixel_general); } x += ux; y += uy; w += uw; } return buffer; } typedef uint32_t (* convert_pixel_t) (const uint8_t *row, int x); static force_inline void bits_image_fetch_separable_convolution_affine (pixman_image_t * image, int offset, int line, int width, uint32_t * buffer, const uint32_t * mask, convert_pixel_t convert_pixel, pixman_format_code_t format, pixman_repeat_t repeat_mode) { bits_image_t *bits = &image->bits; pixman_fixed_t *params = image->common.filter_params; int cwidth = pixman_fixed_to_int (params[0]); int cheight = pixman_fixed_to_int (params[1]); int x_off = ((cwidth << 16) - pixman_fixed_1) >> 1; int y_off = ((cheight << 16) - pixman_fixed_1) >> 1; int x_phase_bits = pixman_fixed_to_int (params[2]); int y_phase_bits = pixman_fixed_to_int (params[3]); int x_phase_shift = 16 - x_phase_bits; int y_phase_shift = 16 - y_phase_bits; pixman_fixed_t vx, vy; pixman_fixed_t ux, uy; pixman_vector_t v; int k; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (image->common.transform, &v)) return; ux = image->common.transform->matrix[0][0]; uy = image->common.transform->matrix[1][0]; vx = v.vector[0]; vy = v.vector[1]; for (k = 0; k < width; ++k) { pixman_fixed_t *y_params; int satot, srtot, sgtot, sbtot; pixman_fixed_t x, y; int32_t x1, x2, y1, y2; int32_t px, py; int i, j; if (mask && !mask[k]) goto next; /* Round x and y to the middle of the closest phase before continuing. This * ensures that the convolution matrix is aligned right, since it was * positioned relative to a particular phase (and not relative to whatever * exact fraction we happen to get here). */ x = ((vx >> x_phase_shift) << x_phase_shift) + ((1 << x_phase_shift) >> 1); y = ((vy >> y_phase_shift) << y_phase_shift) + ((1 << y_phase_shift) >> 1); px = (x & 0xffff) >> x_phase_shift; py = (y & 0xffff) >> y_phase_shift; x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); x2 = x1 + cwidth; y2 = y1 + cheight; satot = srtot = sgtot = sbtot = 0; y_params = params + 4 + (1 << x_phase_bits) * cwidth + py * cheight; for (i = y1; i < y2; ++i) { pixman_fixed_t fy = *y_params++; if (fy) { pixman_fixed_t *x_params = params + 4 + px * cwidth; for (j = x1; j < x2; ++j) { pixman_fixed_t fx = *x_params++; int rx = j; int ry = i; if (fx) { pixman_fixed_t f; uint32_t pixel, mask; uint8_t *row; mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; if (repeat_mode != PIXMAN_REPEAT_NONE) { repeat (repeat_mode, &rx, bits->width); repeat (repeat_mode, &ry, bits->height); row = (uint8_t *)bits->bits + bits->rowstride * 4 * ry; pixel = convert_pixel (row, rx) | mask; } else { if (rx < 0 || ry < 0 || rx >= bits->width || ry >= bits->height) { pixel = 0; } else { row = (uint8_t *)bits->bits + bits->rowstride * 4 * ry; pixel = convert_pixel (row, rx) | mask; } } f = ((pixman_fixed_32_32_t)fx * fy + 0x8000) >> 16; srtot += (int)RED_8 (pixel) * f; sgtot += (int)GREEN_8 (pixel) * f; sbtot += (int)BLUE_8 (pixel) * f; satot += (int)ALPHA_8 (pixel) * f; } } } } satot = (satot + 0x8000) >> 16; srtot = (srtot + 0x8000) >> 16; sgtot = (sgtot + 0x8000) >> 16; sbtot = (sbtot + 0x8000) >> 16; satot = CLIP (satot, 0, 0xff); srtot = CLIP (srtot, 0, 0xff); sgtot = CLIP (sgtot, 0, 0xff); sbtot = CLIP (sbtot, 0, 0xff); buffer[k] = (satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot << 0); next: vx += ux; vy += uy; } } static const uint8_t zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static force_inline void bits_image_fetch_bilinear_affine (pixman_image_t * image, int offset, int line, int width, uint32_t * buffer, const uint32_t * mask, convert_pixel_t convert_pixel, pixman_format_code_t format, pixman_repeat_t repeat_mode) { pixman_fixed_t x, y; pixman_fixed_t ux, uy; pixman_vector_t v; bits_image_t *bits = &image->bits; int i; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (image->common.transform, &v)) return; ux = image->common.transform->matrix[0][0]; uy = image->common.transform->matrix[1][0]; x = v.vector[0]; y = v.vector[1]; for (i = 0; i < width; ++i) { int x1, y1, x2, y2; uint32_t tl, tr, bl, br; int32_t distx, disty; int width = image->bits.width; int height = image->bits.height; const uint8_t *row1; const uint8_t *row2; if (mask && !mask[i]) goto next; x1 = x - pixman_fixed_1 / 2; y1 = y - pixman_fixed_1 / 2; distx = pixman_fixed_to_bilinear_weight (x1); disty = pixman_fixed_to_bilinear_weight (y1); y1 = pixman_fixed_to_int (y1); y2 = y1 + 1; x1 = pixman_fixed_to_int (x1); x2 = x1 + 1; if (repeat_mode != PIXMAN_REPEAT_NONE) { uint32_t mask; mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; repeat (repeat_mode, &x1, width); repeat (repeat_mode, &y1, height); repeat (repeat_mode, &x2, width); repeat (repeat_mode, &y2, height); row1 = (uint8_t *)bits->bits + bits->rowstride * 4 * y1; row2 = (uint8_t *)bits->bits + bits->rowstride * 4 * y2; tl = convert_pixel (row1, x1) | mask; tr = convert_pixel (row1, x2) | mask; bl = convert_pixel (row2, x1) | mask; br = convert_pixel (row2, x2) | mask; } else { uint32_t mask1, mask2; int bpp; /* Note: PIXMAN_FORMAT_BPP() returns an unsigned value, * which means if you use it in expressions, those * expressions become unsigned themselves. Since * the variables below can be negative in some cases, * that will lead to crashes on 64 bit architectures. * * So this line makes sure bpp is signed */ bpp = PIXMAN_FORMAT_BPP (format); if (x1 >= width || x2 < 0 || y1 >= height || y2 < 0) { buffer[i] = 0; goto next; } if (y2 == 0) { row1 = zero; mask1 = 0; } else { row1 = (uint8_t *)bits->bits + bits->rowstride * 4 * y1; row1 += bpp / 8 * x1; mask1 = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; } if (y1 == height - 1) { row2 = zero; mask2 = 0; } else { row2 = (uint8_t *)bits->bits + bits->rowstride * 4 * y2; row2 += bpp / 8 * x1; mask2 = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; } if (x2 == 0) { tl = 0; bl = 0; } else { tl = convert_pixel (row1, 0) | mask1; bl = convert_pixel (row2, 0) | mask2; } if (x1 == width - 1) { tr = 0; br = 0; } else { tr = convert_pixel (row1, 1) | mask1; br = convert_pixel (row2, 1) | mask2; } } buffer[i] = bilinear_interpolation ( tl, tr, bl, br, distx, disty); next: x += ux; y += uy; } } static force_inline void bits_image_fetch_nearest_affine (pixman_image_t * image, int offset, int line, int width, uint32_t * buffer, const uint32_t * mask, convert_pixel_t convert_pixel, pixman_format_code_t format, pixman_repeat_t repeat_mode) { pixman_fixed_t x, y; pixman_fixed_t ux, uy; pixman_vector_t v; bits_image_t *bits = &image->bits; int i; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (image->common.transform, &v)) return; ux = image->common.transform->matrix[0][0]; uy = image->common.transform->matrix[1][0]; x = v.vector[0]; y = v.vector[1]; for (i = 0; i < width; ++i) { int width, height, x0, y0; const uint8_t *row; if (mask && !mask[i]) goto next; width = image->bits.width; height = image->bits.height; x0 = pixman_fixed_to_int (x - pixman_fixed_e); y0 = pixman_fixed_to_int (y - pixman_fixed_e); if (repeat_mode == PIXMAN_REPEAT_NONE && (y0 < 0 || y0 >= height || x0 < 0 || x0 >= width)) { buffer[i] = 0; } else { uint32_t mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; if (repeat_mode != PIXMAN_REPEAT_NONE) { repeat (repeat_mode, &x0, width); repeat (repeat_mode, &y0, height); } row = (uint8_t *)bits->bits + bits->rowstride * 4 * y0; buffer[i] = convert_pixel (row, x0) | mask; } next: x += ux; y += uy; } } static force_inline uint32_t convert_a8r8g8b8 (const uint8_t *row, int x) { return *(((uint32_t *)row) + x); } static force_inline uint32_t convert_x8r8g8b8 (const uint8_t *row, int x) { return *(((uint32_t *)row) + x); } static force_inline uint32_t convert_a8 (const uint8_t *row, int x) { return *(row + x) << 24; } static force_inline uint32_t convert_r5g6b5 (const uint8_t *row, int x) { return convert_0565_to_0888 (*((uint16_t *)row + x)); } #define MAKE_SEPARABLE_CONVOLUTION_FETCHER(name, format, repeat_mode) \ static uint32_t * \ bits_image_fetch_separable_convolution_affine_ ## name (pixman_iter_t *iter, \ const uint32_t * mask) \ { \ bits_image_fetch_separable_convolution_affine ( \ iter->image, \ iter->x, iter->y++, \ iter->width, \ iter->buffer, mask, \ convert_ ## format, \ PIXMAN_ ## format, \ repeat_mode); \ \ return iter->buffer; \ } #define MAKE_BILINEAR_FETCHER(name, format, repeat_mode) \ static uint32_t * \ bits_image_fetch_bilinear_affine_ ## name (pixman_iter_t *iter, \ const uint32_t * mask) \ { \ bits_image_fetch_bilinear_affine (iter->image, \ iter->x, iter->y++, \ iter->width, \ iter->buffer, mask, \ convert_ ## format, \ PIXMAN_ ## format, \ repeat_mode); \ return iter->buffer; \ } #define MAKE_NEAREST_FETCHER(name, format, repeat_mode) \ static uint32_t * \ bits_image_fetch_nearest_affine_ ## name (pixman_iter_t *iter, \ const uint32_t * mask) \ { \ bits_image_fetch_nearest_affine (iter->image, \ iter->x, iter->y++, \ iter->width, \ iter->buffer, mask, \ convert_ ## format, \ PIXMAN_ ## format, \ repeat_mode); \ return iter->buffer; \ } #define MAKE_FETCHERS(name, format, repeat_mode) \ MAKE_NEAREST_FETCHER (name, format, repeat_mode) \ MAKE_BILINEAR_FETCHER (name, format, repeat_mode) \ MAKE_SEPARABLE_CONVOLUTION_FETCHER (name, format, repeat_mode) MAKE_FETCHERS (pad_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_PAD) MAKE_FETCHERS (none_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_NONE) MAKE_FETCHERS (reflect_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_REFLECT) MAKE_FETCHERS (normal_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_NORMAL) MAKE_FETCHERS (pad_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_PAD) MAKE_FETCHERS (none_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_NONE) MAKE_FETCHERS (reflect_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_REFLECT) MAKE_FETCHERS (normal_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_NORMAL) MAKE_FETCHERS (pad_a8, a8, PIXMAN_REPEAT_PAD) MAKE_FETCHERS (none_a8, a8, PIXMAN_REPEAT_NONE) MAKE_FETCHERS (reflect_a8, a8, PIXMAN_REPEAT_REFLECT) MAKE_FETCHERS (normal_a8, a8, PIXMAN_REPEAT_NORMAL) MAKE_FETCHERS (pad_r5g6b5, r5g6b5, PIXMAN_REPEAT_PAD) MAKE_FETCHERS (none_r5g6b5, r5g6b5, PIXMAN_REPEAT_NONE) MAKE_FETCHERS (reflect_r5g6b5, r5g6b5, PIXMAN_REPEAT_REFLECT) MAKE_FETCHERS (normal_r5g6b5, r5g6b5, PIXMAN_REPEAT_NORMAL) static void replicate_pixel_32 (bits_image_t * bits, int x, int y, int width, uint32_t * buffer) { uint32_t color; uint32_t *end; color = bits->fetch_pixel_32 (bits, x, y); end = buffer + width; while (buffer < end) *(buffer++) = color; } static void replicate_pixel_float (bits_image_t * bits, int x, int y, int width, uint32_t * b) { argb_t color; argb_t *buffer = (argb_t *)b; argb_t *end; color = bits->fetch_pixel_float (bits, x, y); end = buffer + width; while (buffer < end) *(buffer++) = color; } static void bits_image_fetch_untransformed_repeat_none (bits_image_t *image, pixman_bool_t wide, int x, int y, int width, uint32_t * buffer) { uint32_t w; if (y < 0 || y >= image->height) { memset (buffer, 0, width * (wide? sizeof (argb_t) : 4)); return; } if (x < 0) { w = MIN (width, -x); memset (buffer, 0, w * (wide ? sizeof (argb_t) : 4)); width -= w; buffer += w * (wide? 4 : 1); x += w; } if (x < image->width) { w = MIN (width, image->width - x); if (wide) image->fetch_scanline_float ((pixman_image_t *)image, x, y, w, buffer, NULL); else image->fetch_scanline_32 ((pixman_image_t *)image, x, y, w, buffer, NULL); width -= w; buffer += w * (wide? 4 : 1); x += w; } memset (buffer, 0, width * (wide ? sizeof (argb_t) : 4)); } static void bits_image_fetch_untransformed_repeat_normal (bits_image_t *image, pixman_bool_t wide, int x, int y, int width, uint32_t * buffer) { uint32_t w; while (y < 0) y += image->height; while (y >= image->height) y -= image->height; if (image->width == 1) { if (wide) replicate_pixel_float (image, 0, y, width, buffer); else replicate_pixel_32 (image, 0, y, width, buffer); return; } while (width) { while (x < 0) x += image->width; while (x >= image->width) x -= image->width; w = MIN (width, image->width - x); if (wide) image->fetch_scanline_float ((pixman_image_t *)image, x, y, w, buffer, NULL); else image->fetch_scanline_32 ((pixman_image_t *)image, x, y, w, buffer, NULL); buffer += w * (wide? 4 : 1); x += w; width -= w; } } static uint32_t * bits_image_fetch_untransformed_32 (pixman_iter_t * iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t * buffer = iter->buffer; if (image->common.repeat == PIXMAN_REPEAT_NONE) { bits_image_fetch_untransformed_repeat_none ( &image->bits, FALSE, x, y, width, buffer); } else { bits_image_fetch_untransformed_repeat_normal ( &image->bits, FALSE, x, y, width, buffer); } iter->y++; return buffer; } static uint32_t * bits_image_fetch_untransformed_float (pixman_iter_t * iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t * buffer = iter->buffer; if (image->common.repeat == PIXMAN_REPEAT_NONE) { bits_image_fetch_untransformed_repeat_none ( &image->bits, TRUE, x, y, width, buffer); } else { bits_image_fetch_untransformed_repeat_normal ( &image->bits, TRUE, x, y, width, buffer); } iter->y++; return buffer; } typedef struct { pixman_format_code_t format; uint32_t flags; pixman_iter_get_scanline_t get_scanline_32; pixman_iter_get_scanline_t get_scanline_float; } fetcher_info_t; static const fetcher_info_t fetcher_info[] = { { PIXMAN_any, (FAST_PATH_NO_ALPHA_MAP | FAST_PATH_ID_TRANSFORM | FAST_PATH_NO_CONVOLUTION_FILTER | FAST_PATH_NO_PAD_REPEAT | FAST_PATH_NO_REFLECT_REPEAT), bits_image_fetch_untransformed_32, bits_image_fetch_untransformed_float }, #define FAST_BILINEAR_FLAGS \ (FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_HAS_TRANSFORM | \ FAST_PATH_AFFINE_TRANSFORM | \ FAST_PATH_X_UNIT_POSITIVE | \ FAST_PATH_Y_UNIT_ZERO | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_BILINEAR_FILTER) { PIXMAN_a8r8g8b8, FAST_BILINEAR_FLAGS, bits_image_fetch_bilinear_no_repeat_8888, _pixman_image_get_scanline_generic_float }, { PIXMAN_x8r8g8b8, FAST_BILINEAR_FLAGS, bits_image_fetch_bilinear_no_repeat_8888, _pixman_image_get_scanline_generic_float }, #define GENERAL_BILINEAR_FLAGS \ (FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_HAS_TRANSFORM | \ FAST_PATH_AFFINE_TRANSFORM | \ FAST_PATH_BILINEAR_FILTER) #define GENERAL_NEAREST_FLAGS \ (FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_HAS_TRANSFORM | \ FAST_PATH_AFFINE_TRANSFORM | \ FAST_PATH_NEAREST_FILTER) #define GENERAL_SEPARABLE_CONVOLUTION_FLAGS \ (FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_HAS_TRANSFORM | \ FAST_PATH_AFFINE_TRANSFORM | \ FAST_PATH_SEPARABLE_CONVOLUTION_FILTER) #define SEPARABLE_CONVOLUTION_AFFINE_FAST_PATH(name, format, repeat) \ { PIXMAN_ ## format, \ GENERAL_SEPARABLE_CONVOLUTION_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ bits_image_fetch_separable_convolution_affine_ ## name, \ _pixman_image_get_scanline_generic_float \ }, #define BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \ { PIXMAN_ ## format, \ GENERAL_BILINEAR_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ bits_image_fetch_bilinear_affine_ ## name, \ _pixman_image_get_scanline_generic_float \ }, #define NEAREST_AFFINE_FAST_PATH(name, format, repeat) \ { PIXMAN_ ## format, \ GENERAL_NEAREST_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ bits_image_fetch_nearest_affine_ ## name, \ _pixman_image_get_scanline_generic_float \ }, #define AFFINE_FAST_PATHS(name, format, repeat) \ SEPARABLE_CONVOLUTION_AFFINE_FAST_PATH(name, format, repeat) \ BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \ NEAREST_AFFINE_FAST_PATH(name, format, repeat) AFFINE_FAST_PATHS (pad_a8r8g8b8, a8r8g8b8, PAD) AFFINE_FAST_PATHS (none_a8r8g8b8, a8r8g8b8, NONE) AFFINE_FAST_PATHS (reflect_a8r8g8b8, a8r8g8b8, REFLECT) AFFINE_FAST_PATHS (normal_a8r8g8b8, a8r8g8b8, NORMAL) AFFINE_FAST_PATHS (pad_x8r8g8b8, x8r8g8b8, PAD) AFFINE_FAST_PATHS (none_x8r8g8b8, x8r8g8b8, NONE) AFFINE_FAST_PATHS (reflect_x8r8g8b8, x8r8g8b8, REFLECT) AFFINE_FAST_PATHS (normal_x8r8g8b8, x8r8g8b8, NORMAL) AFFINE_FAST_PATHS (pad_a8, a8, PAD) AFFINE_FAST_PATHS (none_a8, a8, NONE) AFFINE_FAST_PATHS (reflect_a8, a8, REFLECT) AFFINE_FAST_PATHS (normal_a8, a8, NORMAL) AFFINE_FAST_PATHS (pad_r5g6b5, r5g6b5, PAD) AFFINE_FAST_PATHS (none_r5g6b5, r5g6b5, NONE) AFFINE_FAST_PATHS (reflect_r5g6b5, r5g6b5, REFLECT) AFFINE_FAST_PATHS (normal_r5g6b5, r5g6b5, NORMAL) /* Affine, no alpha */ { PIXMAN_any, (FAST_PATH_NO_ALPHA_MAP | FAST_PATH_HAS_TRANSFORM | FAST_PATH_AFFINE_TRANSFORM), bits_image_fetch_affine_no_alpha, _pixman_image_get_scanline_generic_float }, /* General */ { PIXMAN_any, 0, bits_image_fetch_general, _pixman_image_get_scanline_generic_float }, { PIXMAN_null }, }; static void bits_image_property_changed (pixman_image_t *image) { _pixman_bits_image_setup_accessors (&image->bits); } void _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter) { pixman_format_code_t format = image->common.extended_format_code; uint32_t flags = image->common.flags; const fetcher_info_t *info; for (info = fetcher_info; info->format != PIXMAN_null; ++info) { if ((info->format == format || info->format == PIXMAN_any) && (info->flags & flags) == info->flags) { if (iter->iter_flags & ITER_NARROW) { iter->get_scanline = info->get_scanline_32; } else { iter->data = info->get_scanline_32; iter->get_scanline = info->get_scanline_float; } return; } } /* Just in case we somehow didn't find a scanline function */ iter->get_scanline = _pixman_iter_get_scanline_noop; } static uint32_t * dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t * buffer = iter->buffer; image->bits.fetch_scanline_32 (image, x, y, width, buffer, mask); if (image->common.alpha_map) { uint32_t *alpha; if ((alpha = malloc (width * sizeof (uint32_t)))) { int i; x -= image->common.alpha_origin_x; y -= image->common.alpha_origin_y; image->common.alpha_map->fetch_scanline_32 ( (pixman_image_t *)image->common.alpha_map, x, y, width, alpha, mask); for (i = 0; i < width; ++i) { buffer[i] &= ~0xff000000; buffer[i] |= (alpha[i] & 0xff000000); } free (alpha); } } return iter->buffer; } static uint32_t * dest_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) { bits_image_t * image = &iter->image->bits; int x = iter->x; int y = iter->y; int width = iter->width; argb_t * buffer = (argb_t *)iter->buffer; image->fetch_scanline_float ( (pixman_image_t *)image, x, y, width, (uint32_t *)buffer, mask); if (image->common.alpha_map) { argb_t *alpha; if ((alpha = malloc (width * sizeof (argb_t)))) { int i; x -= image->common.alpha_origin_x; y -= image->common.alpha_origin_y; image->common.alpha_map->fetch_scanline_float ( (pixman_image_t *)image->common.alpha_map, x, y, width, (uint32_t *)alpha, mask); for (i = 0; i < width; ++i) buffer[i].a = alpha[i].a; free (alpha); } } return iter->buffer; } static void dest_write_back_narrow (pixman_iter_t *iter) { bits_image_t * image = &iter->image->bits; int x = iter->x; int y = iter->y; int width = iter->width; const uint32_t *buffer = iter->buffer; image->store_scanline_32 (image, x, y, width, buffer); if (image->common.alpha_map) { x -= image->common.alpha_origin_x; y -= image->common.alpha_origin_y; image->common.alpha_map->store_scanline_32 ( image->common.alpha_map, x, y, width, buffer); } iter->y++; } static void dest_write_back_wide (pixman_iter_t *iter) { bits_image_t * image = &iter->image->bits; int x = iter->x; int y = iter->y; int width = iter->width; const uint32_t *buffer = iter->buffer; image->store_scanline_float (image, x, y, width, buffer); if (image->common.alpha_map) { x -= image->common.alpha_origin_x; y -= image->common.alpha_origin_y; image->common.alpha_map->store_scanline_float ( image->common.alpha_map, x, y, width, buffer); } iter->y++; } void _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter) { if (iter->iter_flags & ITER_NARROW) { if ((iter->iter_flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) == (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) { iter->get_scanline = _pixman_iter_get_scanline_noop; } else { iter->get_scanline = dest_get_scanline_narrow; } iter->write_back = dest_write_back_narrow; } else { iter->get_scanline = dest_get_scanline_wide; iter->write_back = dest_write_back_wide; } } static uint32_t * create_bits (pixman_format_code_t format, int width, int height, int * rowstride_bytes, pixman_bool_t clear) { int stride; size_t buf_size; int bpp; /* what follows is a long-winded way, avoiding any possibility of integer * overflows, of saying: * stride = ((width * bpp + 0x1f) >> 5) * sizeof (uint32_t); */ bpp = PIXMAN_FORMAT_BPP (format); if (_pixman_multiply_overflows_int (width, bpp)) return NULL; stride = width * bpp; if (_pixman_addition_overflows_int (stride, 0x1f)) return NULL; stride += 0x1f; stride >>= 5; stride *= sizeof (uint32_t); if (_pixman_multiply_overflows_size (height, stride)) return NULL; buf_size = height * stride; if (rowstride_bytes) *rowstride_bytes = stride; if (clear) return calloc (buf_size, 1); else return malloc (buf_size); } pixman_bool_t _pixman_bits_image_init (pixman_image_t * image, pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride, pixman_bool_t clear) { uint32_t *free_me = NULL; if (!bits && width && height) { int rowstride_bytes; free_me = bits = create_bits (format, width, height, &rowstride_bytes, clear); if (!bits) return FALSE; rowstride = rowstride_bytes / (int) sizeof (uint32_t); } _pixman_image_init (image); image->type = BITS; image->bits.format = format; image->bits.width = width; image->bits.height = height; image->bits.bits = bits; image->bits.free_me = free_me; image->bits.read_func = NULL; image->bits.write_func = NULL; image->bits.rowstride = rowstride; image->bits.indexed = NULL; image->common.property_changed = bits_image_property_changed; _pixman_image_reset_clip_region (image); return TRUE; } static pixman_image_t * create_bits_image_internal (pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride_bytes, pixman_bool_t clear) { pixman_image_t *image; /* must be a whole number of uint32_t's */ return_val_if_fail ( bits == NULL || (rowstride_bytes % sizeof (uint32_t)) == 0, NULL); return_val_if_fail (PIXMAN_FORMAT_BPP (format) >= PIXMAN_FORMAT_DEPTH (format), NULL); image = _pixman_image_allocate (); if (!image) return NULL; if (!_pixman_bits_image_init (image, format, width, height, bits, rowstride_bytes / (int) sizeof (uint32_t), clear)) { free (image); return NULL; } return image; } /* If bits is NULL, a buffer will be allocated and initialized to 0 */ PIXMAN_EXPORT pixman_image_t * pixman_image_create_bits (pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride_bytes) { return create_bits_image_internal ( format, width, height, bits, rowstride_bytes, TRUE); } /* If bits is NULL, a buffer will be allocated and _not_ initialized */ PIXMAN_EXPORT pixman_image_t * pixman_image_create_bits_no_clear (pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride_bytes) { return create_bits_image_internal ( format, width, height, bits, rowstride_bytes, FALSE); } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-combine-float.c000066400000000000000000000717401271037650300273720ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2010, 2012 Soren Sandmann Pedersen * Copyright © 2010, 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Soren Sandmann Pedersen (sandmann@cs.au.dk) */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "pixman-private.h" /* Workaround for http://gcc.gnu.org/PR54965 */ /* GCC 4.6 has problems with force_inline, so just use normal inline instead */ #if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 6) #undef force_inline #define force_inline __inline__ #endif typedef float (* combine_channel_t) (float sa, float s, float da, float d); static force_inline void combine_inner (pixman_bool_t component, float *dest, const float *src, const float *mask, int n_pixels, combine_channel_t combine_a, combine_channel_t combine_c) { int i; if (!mask) { for (i = 0; i < 4 * n_pixels; i += 4) { float sa = src[i + 0]; float sr = src[i + 1]; float sg = src[i + 2]; float sb = src[i + 3]; float da = dest[i + 0]; float dr = dest[i + 1]; float dg = dest[i + 2]; float db = dest[i + 3]; dest[i + 0] = combine_a (sa, sa, da, da); dest[i + 1] = combine_c (sa, sr, da, dr); dest[i + 2] = combine_c (sa, sg, da, dg); dest[i + 3] = combine_c (sa, sb, da, db); } } else { for (i = 0; i < 4 * n_pixels; i += 4) { float sa, sr, sg, sb; float ma, mr, mg, mb; float da, dr, dg, db; sa = src[i + 0]; sr = src[i + 1]; sg = src[i + 2]; sb = src[i + 3]; if (component) { ma = mask[i + 0]; mr = mask[i + 1]; mg = mask[i + 2]; mb = mask[i + 3]; sr *= mr; sg *= mg; sb *= mb; ma *= sa; mr *= sa; mg *= sa; mb *= sa; sa = ma; } else { ma = mask[i + 0]; sa *= ma; sr *= ma; sg *= ma; sb *= ma; ma = mr = mg = mb = sa; } da = dest[i + 0]; dr = dest[i + 1]; dg = dest[i + 2]; db = dest[i + 3]; dest[i + 0] = combine_a (ma, sa, da, da); dest[i + 1] = combine_c (mr, sr, da, dr); dest[i + 2] = combine_c (mg, sg, da, dg); dest[i + 3] = combine_c (mb, sb, da, db); } } } #define MAKE_COMBINER(name, component, combine_a, combine_c) \ static void \ combine_ ## name ## _float (pixman_implementation_t *imp, \ pixman_op_t op, \ float *dest, \ const float *src, \ const float *mask, \ int n_pixels) \ { \ combine_inner (component, dest, src, mask, n_pixels, \ combine_a, combine_c); \ } #define MAKE_COMBINERS(name, combine_a, combine_c) \ MAKE_COMBINER(name ## _ca, TRUE, combine_a, combine_c) \ MAKE_COMBINER(name ## _u, FALSE, combine_a, combine_c) /* * Porter/Duff operators */ typedef enum { ZERO, ONE, SRC_ALPHA, DEST_ALPHA, INV_SA, INV_DA, SA_OVER_DA, DA_OVER_SA, INV_SA_OVER_DA, INV_DA_OVER_SA, ONE_MINUS_SA_OVER_DA, ONE_MINUS_DA_OVER_SA, ONE_MINUS_INV_DA_OVER_SA, ONE_MINUS_INV_SA_OVER_DA } combine_factor_t; #define CLAMP(f) \ (((f) < 0)? 0 : (((f) > 1.0) ? 1.0 : (f))) static force_inline float get_factor (combine_factor_t factor, float sa, float da) { float f = -1; switch (factor) { case ZERO: f = 0.0f; break; case ONE: f = 1.0f; break; case SRC_ALPHA: f = sa; break; case DEST_ALPHA: f = da; break; case INV_SA: f = 1 - sa; break; case INV_DA: f = 1 - da; break; case SA_OVER_DA: if (FLOAT_IS_ZERO (da)) f = 1.0f; else f = CLAMP (sa / da); break; case DA_OVER_SA: if (FLOAT_IS_ZERO (sa)) f = 1.0f; else f = CLAMP (da / sa); break; case INV_SA_OVER_DA: if (FLOAT_IS_ZERO (da)) f = 1.0f; else f = CLAMP ((1.0f - sa) / da); break; case INV_DA_OVER_SA: if (FLOAT_IS_ZERO (sa)) f = 1.0f; else f = CLAMP ((1.0f - da) / sa); break; case ONE_MINUS_SA_OVER_DA: if (FLOAT_IS_ZERO (da)) f = 0.0f; else f = CLAMP (1.0f - sa / da); break; case ONE_MINUS_DA_OVER_SA: if (FLOAT_IS_ZERO (sa)) f = 0.0f; else f = CLAMP (1.0f - da / sa); break; case ONE_MINUS_INV_DA_OVER_SA: if (FLOAT_IS_ZERO (sa)) f = 0.0f; else f = CLAMP (1.0f - (1.0f - da) / sa); break; case ONE_MINUS_INV_SA_OVER_DA: if (FLOAT_IS_ZERO (da)) f = 0.0f; else f = CLAMP (1.0f - (1.0f - sa) / da); break; } return f; } #define MAKE_PD_COMBINERS(name, a, b) \ static float force_inline \ pd_combine_ ## name (float sa, float s, float da, float d) \ { \ const float fa = get_factor (a, sa, da); \ const float fb = get_factor (b, sa, da); \ \ return MIN (1.0f, s * fa + d * fb); \ } \ \ MAKE_COMBINERS(name, pd_combine_ ## name, pd_combine_ ## name) MAKE_PD_COMBINERS (clear, ZERO, ZERO) MAKE_PD_COMBINERS (src, ONE, ZERO) MAKE_PD_COMBINERS (dst, ZERO, ONE) MAKE_PD_COMBINERS (over, ONE, INV_SA) MAKE_PD_COMBINERS (over_reverse, INV_DA, ONE) MAKE_PD_COMBINERS (in, DEST_ALPHA, ZERO) MAKE_PD_COMBINERS (in_reverse, ZERO, SRC_ALPHA) MAKE_PD_COMBINERS (out, INV_DA, ZERO) MAKE_PD_COMBINERS (out_reverse, ZERO, INV_SA) MAKE_PD_COMBINERS (atop, DEST_ALPHA, INV_SA) MAKE_PD_COMBINERS (atop_reverse, INV_DA, SRC_ALPHA) MAKE_PD_COMBINERS (xor, INV_DA, INV_SA) MAKE_PD_COMBINERS (add, ONE, ONE) MAKE_PD_COMBINERS (saturate, INV_DA_OVER_SA, ONE) MAKE_PD_COMBINERS (disjoint_clear, ZERO, ZERO) MAKE_PD_COMBINERS (disjoint_src, ONE, ZERO) MAKE_PD_COMBINERS (disjoint_dst, ZERO, ONE) MAKE_PD_COMBINERS (disjoint_over, ONE, INV_SA_OVER_DA) MAKE_PD_COMBINERS (disjoint_over_reverse, INV_DA_OVER_SA, ONE) MAKE_PD_COMBINERS (disjoint_in, ONE_MINUS_INV_DA_OVER_SA, ZERO) MAKE_PD_COMBINERS (disjoint_in_reverse, ZERO, ONE_MINUS_INV_SA_OVER_DA) MAKE_PD_COMBINERS (disjoint_out, INV_DA_OVER_SA, ZERO) MAKE_PD_COMBINERS (disjoint_out_reverse, ZERO, INV_SA_OVER_DA) MAKE_PD_COMBINERS (disjoint_atop, ONE_MINUS_INV_DA_OVER_SA, INV_SA_OVER_DA) MAKE_PD_COMBINERS (disjoint_atop_reverse, INV_DA_OVER_SA, ONE_MINUS_INV_SA_OVER_DA) MAKE_PD_COMBINERS (disjoint_xor, INV_DA_OVER_SA, INV_SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_clear, ZERO, ZERO) MAKE_PD_COMBINERS (conjoint_src, ONE, ZERO) MAKE_PD_COMBINERS (conjoint_dst, ZERO, ONE) MAKE_PD_COMBINERS (conjoint_over, ONE, ONE_MINUS_SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_over_reverse, ONE_MINUS_DA_OVER_SA, ONE) MAKE_PD_COMBINERS (conjoint_in, DA_OVER_SA, ZERO) MAKE_PD_COMBINERS (conjoint_in_reverse, ZERO, SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_out, ONE_MINUS_DA_OVER_SA, ZERO) MAKE_PD_COMBINERS (conjoint_out_reverse, ZERO, ONE_MINUS_SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_atop, DA_OVER_SA, ONE_MINUS_SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_atop_reverse, ONE_MINUS_DA_OVER_SA, SA_OVER_DA) MAKE_PD_COMBINERS (conjoint_xor, ONE_MINUS_DA_OVER_SA, ONE_MINUS_SA_OVER_DA) /* * PDF blend modes: * * The following blend modes have been taken from the PDF ISO 32000 * specification, which at this point in time is available from * http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf * The relevant chapters are 11.3.5 and 11.3.6. * The formula for computing the final pixel color given in 11.3.6 is: * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs) * with B() being the blend function. * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs * * These blend modes should match the SVG filter draft specification, as * it has been designed to mirror ISO 32000. Note that at the current point * no released draft exists that shows this, as the formulas have not been * updated yet after the release of ISO 32000. * * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an * argument. Note that this implementation operates on premultiplied colors, * while the PDF specification does not. Therefore the code uses the formula * ar.Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as) */ #define MAKE_SEPARABLE_PDF_COMBINERS(name) \ static force_inline float \ combine_ ## name ## _a (float sa, float s, float da, float d) \ { \ return da + sa - da * sa; \ } \ \ static force_inline float \ combine_ ## name ## _c (float sa, float s, float da, float d) \ { \ float f = (1 - sa) * d + (1 - da) * s; \ \ return f + blend_ ## name (sa, s, da, d); \ } \ \ MAKE_COMBINERS (name, combine_ ## name ## _a, combine_ ## name ## _c) static force_inline float blend_multiply (float sa, float s, float da, float d) { return d * s; } static force_inline float blend_screen (float sa, float s, float da, float d) { return d * sa + s * da - s * d; } static force_inline float blend_overlay (float sa, float s, float da, float d) { if (2 * d < da) return 2 * s * d; else return sa * da - 2 * (da - d) * (sa - s); } static force_inline float blend_darken (float sa, float s, float da, float d) { s = s * da; d = d * sa; if (s > d) return d; else return s; } static force_inline float blend_lighten (float sa, float s, float da, float d) { s = s * da; d = d * sa; if (s > d) return s; else return d; } static force_inline float blend_color_dodge (float sa, float s, float da, float d) { if (FLOAT_IS_ZERO (d)) return 0.0f; else if (d * sa >= sa * da - s * da) return sa * da; else if (FLOAT_IS_ZERO (sa - s)) return sa * da; else return sa * sa * d / (sa - s); } static force_inline float blend_color_burn (float sa, float s, float da, float d) { if (d >= da) return sa * da; else if (sa * (da - d) >= s * da) return 0.0f; else if (FLOAT_IS_ZERO (s)) return 0.0f; else return sa * (da - sa * (da - d) / s); } static force_inline float blend_hard_light (float sa, float s, float da, float d) { if (2 * s < sa) return 2 * s * d; else return sa * da - 2 * (da - d) * (sa - s); } static force_inline float blend_soft_light (float sa, float s, float da, float d) { if (2 * s < sa) { if (FLOAT_IS_ZERO (da)) return d * sa; else return d * sa - d * (da - d) * (sa - 2 * s) / da; } else { if (FLOAT_IS_ZERO (da)) { return 0.0f; } else { if (4 * d <= da) return d * sa + (2 * s - sa) * d * ((16 * d / da - 12) * d / da + 3); else return d * sa + (sqrtf (d * da) - d) * (2 * s - sa); } } } static force_inline float blend_difference (float sa, float s, float da, float d) { float dsa = d * sa; float sda = s * da; if (sda < dsa) return dsa - sda; else return sda - dsa; } static force_inline float blend_exclusion (float sa, float s, float da, float d) { return s * da + d * sa - 2 * d * s; } MAKE_SEPARABLE_PDF_COMBINERS (multiply) MAKE_SEPARABLE_PDF_COMBINERS (screen) MAKE_SEPARABLE_PDF_COMBINERS (overlay) MAKE_SEPARABLE_PDF_COMBINERS (darken) MAKE_SEPARABLE_PDF_COMBINERS (lighten) MAKE_SEPARABLE_PDF_COMBINERS (color_dodge) MAKE_SEPARABLE_PDF_COMBINERS (color_burn) MAKE_SEPARABLE_PDF_COMBINERS (hard_light) MAKE_SEPARABLE_PDF_COMBINERS (soft_light) MAKE_SEPARABLE_PDF_COMBINERS (difference) MAKE_SEPARABLE_PDF_COMBINERS (exclusion) /* * PDF nonseperable blend modes. * * These are implemented using the following functions to operate in Hsl * space, with Cmax, Cmid, Cmin referring to the max, mid and min value * of the red, green and blue components. * * LUM (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue * * clip_color (C): * l = LUM (C) * min = Cmin * max = Cmax * if n < 0.0 * C = l + (((C – l) × l) â„ (l – min)) * if x > 1.0 * C = l + (((C – l) × (1 – l)) (max – l)) * return C * * set_lum (C, l): * d = l – LUM (C) * C += d * return clip_color (C) * * SAT (C) = CH_MAX (C) - CH_MIN (C) * * set_sat (C, s): * if Cmax > Cmin * Cmid = ( ( ( Cmid – Cmin ) × s ) â„ ( Cmax – Cmin ) ) * Cmax = s * else * Cmid = Cmax = 0.0 * Cmin = 0.0 * return C */ /* For premultiplied colors, we need to know what happens when C is * multiplied by a real number. LUM and SAT are linear: * * LUM (r × C) = r × LUM (C) SAT (r × C) = r × SAT (C) * * If we extend clip_color with an extra argument a and change * * if x >= 1.0 * * into * * if x >= a * * then clip_color is also linear: * * r * clip_color (C, a) = clip_color (r_c, ra); * * for positive r. * * Similarly, we can extend set_lum with an extra argument that is just passed * on to clip_color: * * r × set_lum ( C, l, a) * * = r × clip_color ( C + l - LUM (C), a) * * = clip_color ( r * C + r × l - LUM (r × C), r * a) * * = set_lum ( r * C, r * l, r * a) * * Finally, set_sat: * * r * set_sat (C, s) = set_sat (x * C, r * s) * * The above holds for all non-zero x because they x'es in the fraction for * C_mid cancel out. Specifically, it holds for x = r: * * r * set_sat (C, s) = set_sat (r_c, rs) * * * * * So, for the non-separable PDF blend modes, we have (using s, d for * non-premultiplied colors, and S, D for premultiplied: * * Color: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1) * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d) * * * Luminosity: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1) * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d) * * * Saturation: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1) * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)), * a_s * LUM (D), a_s * a_d) * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d)) * * Hue: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1) * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d) * */ typedef struct { float r; float g; float b; } rgb_t; static force_inline float minf (float a, float b) { return a < b? a : b; } static force_inline float maxf (float a, float b) { return a > b? a : b; } static force_inline float channel_min (const rgb_t *c) { return minf (minf (c->r, c->g), c->b); } static force_inline float channel_max (const rgb_t *c) { return maxf (maxf (c->r, c->g), c->b); } static force_inline float get_lum (const rgb_t *c) { return c->r * 0.3f + c->g * 0.59f + c->b * 0.11f; } static force_inline float get_sat (const rgb_t *c) { return channel_max (c) - channel_min (c); } static void clip_color (rgb_t *color, float a) { float l = get_lum (color); float n = channel_min (color); float x = channel_max (color); float t; if (n < 0.0f) { t = l - n; if (FLOAT_IS_ZERO (t)) { color->r = 0.0f; color->g = 0.0f; color->b = 0.0f; } else { color->r = l + (((color->r - l) * l) / t); color->g = l + (((color->g - l) * l) / t); color->b = l + (((color->b - l) * l) / t); } } if (x > a) { t = x - l; if (FLOAT_IS_ZERO (t)) { color->r = a; color->g = a; color->b = a; } else { color->r = l + (((color->r - l) * (a - l) / t)); color->g = l + (((color->g - l) * (a - l) / t)); color->b = l + (((color->b - l) * (a - l) / t)); } } } static void set_lum (rgb_t *color, float sa, float l) { float d = l - get_lum (color); color->r = color->r + d; color->g = color->g + d; color->b = color->b + d; clip_color (color, sa); } static void set_sat (rgb_t *src, float sat) { float *max, *mid, *min; float t; if (src->r > src->g) { if (src->r > src->b) { max = &(src->r); if (src->g > src->b) { mid = &(src->g); min = &(src->b); } else { mid = &(src->b); min = &(src->g); } } else { max = &(src->b); mid = &(src->r); min = &(src->g); } } else { if (src->r > src->b) { max = &(src->g); mid = &(src->r); min = &(src->b); } else { min = &(src->r); if (src->g > src->b) { max = &(src->g); mid = &(src->b); } else { max = &(src->b); mid = &(src->g); } } } t = *max - *min; if (FLOAT_IS_ZERO (t)) { *mid = *max = 0.0f; } else { *mid = ((*mid - *min) * sat) / t; *max = sat; } *min = 0.0f; } /* * Hue: * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb)) */ static force_inline void blend_hsl_hue (rgb_t *res, const rgb_t *dest, float da, const rgb_t *src, float sa) { res->r = src->r * da; res->g = src->g * da; res->b = src->b * da; set_sat (res, get_sat (dest) * sa); set_lum (res, sa * da, get_lum (dest) * sa); } /* * Saturation: * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb)) */ static force_inline void blend_hsl_saturation (rgb_t *res, const rgb_t *dest, float da, const rgb_t *src, float sa) { res->r = dest->r * sa; res->g = dest->g * sa; res->b = dest->b * sa; set_sat (res, get_sat (src) * da); set_lum (res, sa * da, get_lum (dest) * sa); } /* * Color: * B(Cb, Cs) = set_lum (Cs, LUM (Cb)) */ static force_inline void blend_hsl_color (rgb_t *res, const rgb_t *dest, float da, const rgb_t *src, float sa) { res->r = src->r * da; res->g = src->g * da; res->b = src->b * da; set_lum (res, sa * da, get_lum (dest) * sa); } /* * Luminosity: * B(Cb, Cs) = set_lum (Cb, LUM (Cs)) */ static force_inline void blend_hsl_luminosity (rgb_t *res, const rgb_t *dest, float da, const rgb_t *src, float sa) { res->r = dest->r * sa; res->g = dest->g * sa; res->b = dest->b * sa; set_lum (res, sa * da, get_lum (src) * da); } #define MAKE_NON_SEPARABLE_PDF_COMBINERS(name) \ static void \ combine_ ## name ## _u_float (pixman_implementation_t *imp, \ pixman_op_t op, \ float *dest, \ const float *src, \ const float *mask, \ int n_pixels) \ { \ int i; \ \ for (i = 0; i < 4 * n_pixels; i += 4) \ { \ float sa, da; \ rgb_t sc, dc, rc; \ \ sa = src[i + 0]; \ sc.r = src[i + 1]; \ sc.g = src[i + 2]; \ sc.b = src[i + 3]; \ \ da = dest[i + 0]; \ dc.r = dest[i + 1]; \ dc.g = dest[i + 2]; \ dc.b = dest[i + 3]; \ \ if (mask) \ { \ float ma = mask[i + 0]; \ \ /* Component alpha is not supported for HSL modes */ \ sa *= ma; \ sc.r *= ma; \ sc.g *= ma; \ sc.g *= ma; \ } \ \ blend_ ## name (&rc, &dc, da, &sc, sa); \ \ dest[i + 0] = sa + da - sa * da; \ dest[i + 1] = (1 - sa) * dc.r + (1 - da) * sc.r + rc.r; \ dest[i + 2] = (1 - sa) * dc.g + (1 - da) * sc.g + rc.g; \ dest[i + 3] = (1 - sa) * dc.b + (1 - da) * sc.b + rc.b; \ } \ } MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_hue) MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_saturation) MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_color) MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_luminosity) void _pixman_setup_combiner_functions_float (pixman_implementation_t *imp) { /* Unified alpha */ imp->combine_float[PIXMAN_OP_CLEAR] = combine_clear_u_float; imp->combine_float[PIXMAN_OP_SRC] = combine_src_u_float; imp->combine_float[PIXMAN_OP_DST] = combine_dst_u_float; imp->combine_float[PIXMAN_OP_OVER] = combine_over_u_float; imp->combine_float[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u_float; imp->combine_float[PIXMAN_OP_IN] = combine_in_u_float; imp->combine_float[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u_float; imp->combine_float[PIXMAN_OP_OUT] = combine_out_u_float; imp->combine_float[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u_float; imp->combine_float[PIXMAN_OP_ATOP] = combine_atop_u_float; imp->combine_float[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u_float; imp->combine_float[PIXMAN_OP_XOR] = combine_xor_u_float; imp->combine_float[PIXMAN_OP_ADD] = combine_add_u_float; imp->combine_float[PIXMAN_OP_SATURATE] = combine_saturate_u_float; /* Disjoint, unified */ imp->combine_float[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u_float; imp->combine_float[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u_float; /* Conjoint, unified */ imp->combine_float[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u_float; imp->combine_float[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u_float; /* PDF operators, unified */ imp->combine_float[PIXMAN_OP_MULTIPLY] = combine_multiply_u_float; imp->combine_float[PIXMAN_OP_SCREEN] = combine_screen_u_float; imp->combine_float[PIXMAN_OP_OVERLAY] = combine_overlay_u_float; imp->combine_float[PIXMAN_OP_DARKEN] = combine_darken_u_float; imp->combine_float[PIXMAN_OP_LIGHTEN] = combine_lighten_u_float; imp->combine_float[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u_float; imp->combine_float[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u_float; imp->combine_float[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u_float; imp->combine_float[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u_float; imp->combine_float[PIXMAN_OP_DIFFERENCE] = combine_difference_u_float; imp->combine_float[PIXMAN_OP_EXCLUSION] = combine_exclusion_u_float; imp->combine_float[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u_float; imp->combine_float[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u_float; imp->combine_float[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u_float; imp->combine_float[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u_float; /* Component alpha combiners */ imp->combine_float_ca[PIXMAN_OP_CLEAR] = combine_clear_ca_float; imp->combine_float_ca[PIXMAN_OP_SRC] = combine_src_ca_float; imp->combine_float_ca[PIXMAN_OP_DST] = combine_dst_ca_float; imp->combine_float_ca[PIXMAN_OP_OVER] = combine_over_ca_float; imp->combine_float_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_IN] = combine_in_ca_float; imp->combine_float_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_OUT] = combine_out_ca_float; imp->combine_float_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_ATOP] = combine_atop_ca_float; imp->combine_float_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_XOR] = combine_xor_ca_float; imp->combine_float_ca[PIXMAN_OP_ADD] = combine_add_ca_float; imp->combine_float_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca_float; /* Disjoint CA */ imp->combine_float_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca_float; /* Conjoint CA */ imp->combine_float_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca_float; imp->combine_float_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca_float; /* PDF operators CA */ imp->combine_float_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca_float; imp->combine_float_ca[PIXMAN_OP_SCREEN] = combine_screen_ca_float; imp->combine_float_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca_float; imp->combine_float_ca[PIXMAN_OP_DARKEN] = combine_darken_ca_float; imp->combine_float_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca_float; imp->combine_float_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca_float; imp->combine_float_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca_float; imp->combine_float_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca_float; imp->combine_float_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca_float; imp->combine_float_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca_float; imp->combine_float_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca_float; /* It is not clear that these make sense, so make them noops for now */ imp->combine_float_ca[PIXMAN_OP_HSL_HUE] = combine_dst_u_float; imp->combine_float_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst_u_float; imp->combine_float_ca[PIXMAN_OP_HSL_COLOR] = combine_dst_u_float; imp->combine_float_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst_u_float; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-combine32.c000066400000000000000000002114531271037650300264310ustar00rootroot00000000000000/* * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" #include "pixman-combine32.h" /* component alpha helper functions */ static void combine_mask_ca (uint32_t *src, uint32_t *mask) { uint32_t a = *mask; uint32_t x; uint16_t xa; if (!a) { *(src) = 0; return; } x = *(src); if (a == ~0) { x = x >> A_SHIFT; x |= x << G_SHIFT; x |= x << R_SHIFT; *(mask) = x; return; } xa = x >> A_SHIFT; UN8x4_MUL_UN8x4 (x, a); *(src) = x; UN8x4_MUL_UN8 (a, xa); *(mask) = a; } static void combine_mask_value_ca (uint32_t *src, const uint32_t *mask) { uint32_t a = *mask; uint32_t x; if (!a) { *(src) = 0; return; } if (a == ~0) return; x = *(src); UN8x4_MUL_UN8x4 (x, a); *(src) = x; } static void combine_mask_alpha_ca (const uint32_t *src, uint32_t *mask) { uint32_t a = *(mask); uint32_t x; if (!a) return; x = *(src) >> A_SHIFT; if (x == MASK) return; if (a == ~0) { x |= x << G_SHIFT; x |= x << R_SHIFT; *(mask) = x; return; } UN8x4_MUL_UN8 (a, x); *(mask) = a; } /* * There are two ways of handling alpha -- either as a single unified value or * a separate value for each component, hence each macro must have two * versions. The unified alpha version has a 'u' at the end of the name, * the component version has a 'ca'. Similarly, functions which deal with * this difference will have two versions using the same convention. */ static force_inline uint32_t combine_mask (const uint32_t *src, const uint32_t *mask, int i) { uint32_t s, m; if (mask) { m = *(mask + i) >> A_SHIFT; if (!m) return 0; } s = *(src + i); if (mask) UN8x4_MUL_UN8 (s, m); return s; } static void combine_clear (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { memset (dest, 0, width * sizeof(uint32_t)); } static void combine_dst (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { return; } static void combine_src_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; if (!mask) { memcpy (dest, src, width * sizeof (uint32_t)); } else { for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); *(dest + i) = s; } } } static void combine_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; if (!mask) { for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t a = ALPHA_8 (s); if (a == 0xFF) { *(dest + i) = s; } else if (s) { uint32_t d = *(dest + i); uint32_t ia = a ^ 0xFF; UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); *(dest + i) = d; } } } else { for (i = 0; i < width; ++i) { uint32_t m = ALPHA_8 (*(mask + i)); if (m == 0xFF) { uint32_t s = *(src + i); uint32_t a = ALPHA_8 (s); if (a == 0xFF) { *(dest + i) = s; } else if (s) { uint32_t d = *(dest + i); uint32_t ia = a ^ 0xFF; UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); *(dest + i) = d; } } else if (m) { uint32_t s = *(src + i); if (s) { uint32_t d = *(dest + i); UN8x4_MUL_UN8 (s, m); UN8x4_MUL_UN8_ADD_UN8x4 (d, ALPHA_8 (~s), s); *(dest + i) = d; } } } } } static void combine_over_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t ia = ALPHA_8 (~*(dest + i)); UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); *(dest + i) = s; } } static void combine_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t a = ALPHA_8 (*(dest + i)); UN8x4_MUL_UN8 (s, a); *(dest + i) = s; } } static void combine_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t a = ALPHA_8 (s); UN8x4_MUL_UN8 (d, a); *(dest + i) = d; } } static void combine_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t a = ALPHA_8 (~*(dest + i)); UN8x4_MUL_UN8 (s, a); *(dest + i) = s; } } static void combine_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t a = ALPHA_8 (~s); UN8x4_MUL_UN8 (d, a); *(dest + i) = d; } } static void combine_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t dest_a = ALPHA_8 (d); uint32_t src_ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); *(dest + i) = s; } } static void combine_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t src_a = ALPHA_8 (s); uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); *(dest + i) = s; } } static void combine_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t src_ia = ALPHA_8 (~s); uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); *(dest + i) = s; } } static void combine_add_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); UN8x4_ADD_UN8x4 (d, s); *(dest + i) = d; } } static void combine_saturate_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint16_t sa, da; sa = s >> A_SHIFT; da = ~d >> A_SHIFT; if (sa > da) { sa = DIV_UN8 (da, sa); UN8x4_MUL_UN8 (s, sa); } ; UN8x4_ADD_UN8x4 (d, s); *(dest + i) = d; } } /* * PDF blend modes: * The following blend modes have been taken from the PDF ISO 32000 * specification, which at this point in time is available from * http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf * The relevant chapters are 11.3.5 and 11.3.6. * The formula for computing the final pixel color given in 11.3.6 is: * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs) * with B() being the blend function. * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs * * These blend modes should match the SVG filter draft specification, as * it has been designed to mirror ISO 32000. Note that at the current point * no released draft exists that shows this, as the formulas have not been * updated yet after the release of ISO 32000. * * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an * argument. Note that this implementation operates on premultiplied colors, * while the PDF specification does not. Therefore the code uses the formula * Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as) */ /* * Multiply * B(Dca, ad, Sca, as) = Dca.Sca */ static void combine_multiply_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t ss = s; uint32_t src_ia = ALPHA_8 (~s); uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (ss, dest_ia, d, src_ia); UN8x4_MUL_UN8x4 (d, s); UN8x4_ADD_UN8x4 (d, ss); *(dest + i) = d; } } static void combine_multiply_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t m = *(mask + i); uint32_t s = *(src + i); uint32_t d = *(dest + i); uint32_t r = d; uint32_t dest_ia = ALPHA_8 (~d); combine_mask_ca (&s, &m); UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (r, ~m, s, dest_ia); UN8x4_MUL_UN8x4 (d, s); UN8x4_ADD_UN8x4 (r, d); *(dest + i) = r; } } #define PDF_SEPARABLE_BLEND_MODE(name) \ static void \ combine_ ## name ## _u (pixman_implementation_t *imp, \ pixman_op_t op, \ uint32_t * dest, \ const uint32_t * src, \ const uint32_t * mask, \ int width) \ { \ int i; \ for (i = 0; i < width; ++i) { \ uint32_t s = combine_mask (src, mask, i); \ uint32_t d = *(dest + i); \ uint8_t sa = ALPHA_8 (s); \ uint8_t isa = ~sa; \ uint8_t da = ALPHA_8 (d); \ uint8_t ida = ~da; \ uint32_t result; \ \ result = d; \ UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (result, isa, s, ida); \ \ *(dest + i) = result + \ (DIV_ONE_UN8 (sa * (uint32_t)da) << A_SHIFT) + \ (blend_ ## name (RED_8 (d), da, RED_8 (s), sa) << R_SHIFT) + \ (blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), sa) << G_SHIFT) + \ (blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), sa)); \ } \ } \ \ static void \ combine_ ## name ## _ca (pixman_implementation_t *imp, \ pixman_op_t op, \ uint32_t * dest, \ const uint32_t * src, \ const uint32_t * mask, \ int width) \ { \ int i; \ for (i = 0; i < width; ++i) { \ uint32_t m = *(mask + i); \ uint32_t s = *(src + i); \ uint32_t d = *(dest + i); \ uint8_t da = ALPHA_8 (d); \ uint8_t ida = ~da; \ uint32_t result; \ \ combine_mask_ca (&s, &m); \ \ result = d; \ UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (result, ~m, s, ida); \ \ result += \ (DIV_ONE_UN8 (ALPHA_8 (m) * (uint32_t)da) << A_SHIFT) + \ (blend_ ## name (RED_8 (d), da, RED_8 (s), RED_8 (m)) << R_SHIFT) + \ (blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), GREEN_8 (m)) << G_SHIFT) + \ (blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), BLUE_8 (m))); \ \ *(dest + i) = result; \ } \ } /* * Screen * B(Dca, ad, Sca, as) = Dca.sa + Sca.da - Dca.Sca */ static inline uint32_t blend_screen (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { return DIV_ONE_UN8 (sca * da + dca * sa - sca * dca); } PDF_SEPARABLE_BLEND_MODE (screen) /* * Overlay * B(Dca, Da, Sca, Sa) = * if 2.Dca < Da * 2.Sca.Dca * otherwise * Sa.Da - 2.(Da - Dca).(Sa - Sca) */ static inline uint32_t blend_overlay (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { uint32_t rca; if (2 * dca < da) rca = 2 * sca * dca; else rca = sa * da - 2 * (da - dca) * (sa - sca); return DIV_ONE_UN8 (rca); } PDF_SEPARABLE_BLEND_MODE (overlay) /* * Darken * B(Dca, Da, Sca, Sa) = min (Sca.Da, Dca.Sa) */ static inline uint32_t blend_darken (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { uint32_t s, d; s = sca * da; d = dca * sa; return DIV_ONE_UN8 (s > d ? d : s); } PDF_SEPARABLE_BLEND_MODE (darken) /* * Lighten * B(Dca, Da, Sca, Sa) = max (Sca.Da, Dca.Sa) */ static inline uint32_t blend_lighten (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { uint32_t s, d; s = sca * da; d = dca * sa; return DIV_ONE_UN8 (s > d ? s : d); } PDF_SEPARABLE_BLEND_MODE (lighten) /* * Color dodge * B(Dca, Da, Sca, Sa) = * if Dca == 0 * 0 * if Sca == Sa * Sa.Da * otherwise * Sa.Da. min (1, Dca / Da / (1 - Sca/Sa)) */ static inline uint32_t blend_color_dodge (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { if (sca >= sa) { return dca == 0 ? 0 : DIV_ONE_UN8 (sa * da); } else { uint32_t rca = dca * sa / (sa - sca); return DIV_ONE_UN8 (sa * MIN (rca, da)); } } PDF_SEPARABLE_BLEND_MODE (color_dodge) /* * Color burn * B(Dca, Da, Sca, Sa) = * if Dca == Da * Sa.Da * if Sca == 0 * 0 * otherwise * Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca)) */ static inline uint32_t blend_color_burn (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { if (sca == 0) { return dca < da ? 0 : DIV_ONE_UN8 (sa * da); } else { uint32_t rca = (da - dca) * sa / sca; return DIV_ONE_UN8 (sa * (MAX (rca, da) - rca)); } } PDF_SEPARABLE_BLEND_MODE (color_burn) /* * Hard light * B(Dca, Da, Sca, Sa) = * if 2.Sca < Sa * 2.Sca.Dca * otherwise * Sa.Da - 2.(Da - Dca).(Sa - Sca) */ static inline uint32_t blend_hard_light (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { if (2 * sca < sa) return DIV_ONE_UN8 (2 * sca * dca); else return DIV_ONE_UN8 (sa * da - 2 * (da - dca) * (sa - sca)); } PDF_SEPARABLE_BLEND_MODE (hard_light) /* * Soft light * B(Dca, Da, Sca, Sa) = * if (2.Sca <= Sa) * Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa)) * otherwise if Dca.4 <= Da * Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3) * otherwise * (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa)) */ static inline uint32_t blend_soft_light (uint32_t dca_org, uint32_t da_org, uint32_t sca_org, uint32_t sa_org) { double dca = dca_org * (1.0 / MASK); double da = da_org * (1.0 / MASK); double sca = sca_org * (1.0 / MASK); double sa = sa_org * (1.0 / MASK); double rca; if (2 * sca < sa) { if (da == 0) rca = dca * sa; else rca = dca * sa - dca * (da - dca) * (sa - 2 * sca) / da; } else if (da == 0) { rca = 0; } else if (4 * dca <= da) { rca = dca * sa + (2 * sca - sa) * dca * ((16 * dca / da - 12) * dca / da + 3); } else { rca = dca * sa + (sqrt (dca * da) - dca) * (2 * sca - sa); } return rca * MASK + 0.5; } PDF_SEPARABLE_BLEND_MODE (soft_light) /* * Difference * B(Dca, Da, Sca, Sa) = abs (Dca.Sa - Sca.Da) */ static inline uint32_t blend_difference (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { uint32_t dcasa = dca * sa; uint32_t scada = sca * da; if (scada < dcasa) return DIV_ONE_UN8 (dcasa - scada); else return DIV_ONE_UN8 (scada - dcasa); } PDF_SEPARABLE_BLEND_MODE (difference) /* * Exclusion * B(Dca, Da, Sca, Sa) = (Sca.Da + Dca.Sa - 2.Sca.Dca) */ /* This can be made faster by writing it directly and not using * PDF_SEPARABLE_BLEND_MODE, but that's a performance optimization */ static inline uint32_t blend_exclusion (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa) { return DIV_ONE_UN8 (sca * da + dca * sa - 2 * dca * sca); } PDF_SEPARABLE_BLEND_MODE (exclusion) #undef PDF_SEPARABLE_BLEND_MODE /* * PDF nonseperable blend modes are implemented using the following functions * to operate in Hsl space, with Cmax, Cmid, Cmin referring to the max, mid * and min value of the red, green and blue components. * * LUM (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue * * clip_color (C): * l = LUM (C) * min = Cmin * max = Cmax * if n < 0.0 * C = l + ( ( ( C – l ) × l ) â„ ( l – min ) ) * if x > 1.0 * C = l + ( ( ( C – l ) × ( 1 – l ) ) â„ ( max – l ) ) * return C * * set_lum (C, l): * d = l – LUM (C) * C += d * return clip_color (C) * * SAT (C) = CH_MAX (C) - CH_MIN (C) * * set_sat (C, s): * if Cmax > Cmin * Cmid = ( ( ( Cmid – Cmin ) × s ) â„ ( Cmax – Cmin ) ) * Cmax = s * else * Cmid = Cmax = 0.0 * Cmin = 0.0 * return C */ /* For premultiplied colors, we need to know what happens when C is * multiplied by a real number. LUM and SAT are linear: * * LUM (r × C) = r × LUM (C) SAT (r * C) = r * SAT (C) * * If we extend clip_color with an extra argument a and change * * if x >= 1.0 * * into * * if x >= a * * then clip_color is also linear: * * r * clip_color (C, a) = clip_color (r_c, ra); * * for positive r. * * Similarly, we can extend set_lum with an extra argument that is just passed * on to clip_color: * * r * set_lum ( C, l, a) * * = r × clip_color ( C + l - LUM (C), a) * * = clip_color ( r * C + r × l - r * LUM (C), r * a) * * = set_lum ( r * C, r * l, r * a) * * Finally, set_sat: * * r * set_sat (C, s) = set_sat (x * C, r * s) * * The above holds for all non-zero x, because the x'es in the fraction for * C_mid cancel out. Specifically, it holds for x = r: * * r * set_sat (C, s) = set_sat (r_c, rs) * */ /* So, for the non-separable PDF blend modes, we have (using s, d for * non-premultiplied colors, and S, D for premultiplied: * * Color: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1) * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d) * * * Luminosity: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1) * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d) * * * Saturation: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1) * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)), * a_s * LUM (D), a_s * a_d) * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d)) * * Hue: * * a_s * a_d * B(s, d) * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1) * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d) * */ #define CH_MIN(c) (c[0] < c[1] ? (c[0] < c[2] ? c[0] : c[2]) : (c[1] < c[2] ? c[1] : c[2])) #define CH_MAX(c) (c[0] > c[1] ? (c[0] > c[2] ? c[0] : c[2]) : (c[1] > c[2] ? c[1] : c[2])) #define LUM(c) ((c[0] * 30 + c[1] * 59 + c[2] * 11) / 100) #define SAT(c) (CH_MAX (c) - CH_MIN (c)) #define PDF_NON_SEPARABLE_BLEND_MODE(name) \ static void \ combine_ ## name ## _u (pixman_implementation_t *imp, \ pixman_op_t op, \ uint32_t *dest, \ const uint32_t *src, \ const uint32_t *mask, \ int width) \ { \ int i; \ for (i = 0; i < width; ++i) \ { \ uint32_t s = combine_mask (src, mask, i); \ uint32_t d = *(dest + i); \ uint8_t sa = ALPHA_8 (s); \ uint8_t isa = ~sa; \ uint8_t da = ALPHA_8 (d); \ uint8_t ida = ~da; \ uint32_t result; \ uint32_t sc[3], dc[3], c[3]; \ \ result = d; \ UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (result, isa, s, ida); \ dc[0] = RED_8 (d); \ sc[0] = RED_8 (s); \ dc[1] = GREEN_8 (d); \ sc[1] = GREEN_8 (s); \ dc[2] = BLUE_8 (d); \ sc[2] = BLUE_8 (s); \ blend_ ## name (c, dc, da, sc, sa); \ \ *(dest + i) = result + \ (DIV_ONE_UN8 (sa * (uint32_t)da) << A_SHIFT) + \ (DIV_ONE_UN8 (c[0]) << R_SHIFT) + \ (DIV_ONE_UN8 (c[1]) << G_SHIFT) + \ (DIV_ONE_UN8 (c[2])); \ } \ } static void set_lum (uint32_t dest[3], uint32_t src[3], uint32_t sa, uint32_t lum) { double a, l, min, max; double tmp[3]; a = sa * (1.0 / MASK); l = lum * (1.0 / MASK); tmp[0] = src[0] * (1.0 / MASK); tmp[1] = src[1] * (1.0 / MASK); tmp[2] = src[2] * (1.0 / MASK); l = l - LUM (tmp); tmp[0] += l; tmp[1] += l; tmp[2] += l; /* clip_color */ l = LUM (tmp); min = CH_MIN (tmp); max = CH_MAX (tmp); if (min < 0) { if (l - min == 0.0) { tmp[0] = 0; tmp[1] = 0; tmp[2] = 0; } else { tmp[0] = l + (tmp[0] - l) * l / (l - min); tmp[1] = l + (tmp[1] - l) * l / (l - min); tmp[2] = l + (tmp[2] - l) * l / (l - min); } } if (max > a) { if (max - l == 0.0) { tmp[0] = a; tmp[1] = a; tmp[2] = a; } else { tmp[0] = l + (tmp[0] - l) * (a - l) / (max - l); tmp[1] = l + (tmp[1] - l) * (a - l) / (max - l); tmp[2] = l + (tmp[2] - l) * (a - l) / (max - l); } } dest[0] = tmp[0] * MASK + 0.5; dest[1] = tmp[1] * MASK + 0.5; dest[2] = tmp[2] * MASK + 0.5; } static void set_sat (uint32_t dest[3], uint32_t src[3], uint32_t sat) { int id[3]; uint32_t min, max; if (src[0] > src[1]) { if (src[0] > src[2]) { id[0] = 0; if (src[1] > src[2]) { id[1] = 1; id[2] = 2; } else { id[1] = 2; id[2] = 1; } } else { id[0] = 2; id[1] = 0; id[2] = 1; } } else { if (src[0] > src[2]) { id[0] = 1; id[1] = 0; id[2] = 2; } else { id[2] = 0; if (src[1] > src[2]) { id[0] = 1; id[1] = 2; } else { id[0] = 2; id[1] = 1; } } } max = dest[id[0]]; min = dest[id[2]]; if (max > min) { dest[id[1]] = (dest[id[1]] - min) * sat / (max - min); dest[id[0]] = sat; dest[id[2]] = 0; } else { dest[0] = dest[1] = dest[2] = 0; } } /* * Hue: * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb)) */ static inline void blend_hsl_hue (uint32_t c[3], uint32_t dc[3], uint32_t da, uint32_t sc[3], uint32_t sa) { c[0] = sc[0] * da; c[1] = sc[1] * da; c[2] = sc[2] * da; set_sat (c, c, SAT (dc) * sa); set_lum (c, c, sa * da, LUM (dc) * sa); } PDF_NON_SEPARABLE_BLEND_MODE (hsl_hue) /* * Saturation: * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb)) */ static inline void blend_hsl_saturation (uint32_t c[3], uint32_t dc[3], uint32_t da, uint32_t sc[3], uint32_t sa) { c[0] = dc[0] * sa; c[1] = dc[1] * sa; c[2] = dc[2] * sa; set_sat (c, c, SAT (sc) * da); set_lum (c, c, sa * da, LUM (dc) * sa); } PDF_NON_SEPARABLE_BLEND_MODE (hsl_saturation) /* * Color: * B(Cb, Cs) = set_lum (Cs, LUM (Cb)) */ static inline void blend_hsl_color (uint32_t c[3], uint32_t dc[3], uint32_t da, uint32_t sc[3], uint32_t sa) { c[0] = sc[0] * da; c[1] = sc[1] * da; c[2] = sc[2] * da; set_lum (c, c, sa * da, LUM (dc) * sa); } PDF_NON_SEPARABLE_BLEND_MODE (hsl_color) /* * Luminosity: * B(Cb, Cs) = set_lum (Cb, LUM (Cs)) */ static inline void blend_hsl_luminosity (uint32_t c[3], uint32_t dc[3], uint32_t da, uint32_t sc[3], uint32_t sa) { c[0] = dc[0] * sa; c[1] = dc[1] * sa; c[2] = dc[2] * sa; set_lum (c, c, sa * da, LUM (sc) * da); } PDF_NON_SEPARABLE_BLEND_MODE (hsl_luminosity) #undef SAT #undef LUM #undef CH_MAX #undef CH_MIN #undef PDF_NON_SEPARABLE_BLEND_MODE /* All of the disjoint/conjoint composing functions * * The four entries in the first column indicate what source contributions * come from each of the four areas of the picture -- areas covered by neither * A nor B, areas covered only by A, areas covered only by B and finally * areas covered by both A and B. * * Disjoint Conjoint * Fa Fb Fa Fb * (0,0,0,0) 0 0 0 0 * (0,A,0,A) 1 0 1 0 * (0,0,B,B) 0 1 0 1 * (0,A,B,A) 1 min((1-a)/b,1) 1 max(1-a/b,0) * (0,A,B,B) min((1-b)/a,1) 1 max(1-b/a,0) 1 * (0,0,0,A) max(1-(1-b)/a,0) 0 min(1,b/a) 0 * (0,0,0,B) 0 max(1-(1-a)/b,0) 0 min(a/b,1) * (0,A,0,0) min(1,(1-b)/a) 0 max(1-b/a,0) 0 * (0,0,B,0) 0 min(1,(1-a)/b) 0 max(1-a/b,0) * (0,0,B,A) max(1-(1-b)/a,0) min(1,(1-a)/b) min(1,b/a) max(1-a/b,0) * (0,A,0,B) min(1,(1-b)/a) max(1-(1-a)/b,0) max(1-b/a,0) min(1,a/b) * (0,A,B,0) min(1,(1-b)/a) min(1,(1-a)/b) max(1-b/a,0) max(1-a/b,0) * * See http://marc.info/?l=xfree-render&m=99792000027857&w=2 for more * information about these operators. */ #define COMBINE_A_OUT 1 #define COMBINE_A_IN 2 #define COMBINE_B_OUT 4 #define COMBINE_B_IN 8 #define COMBINE_CLEAR 0 #define COMBINE_A (COMBINE_A_OUT | COMBINE_A_IN) #define COMBINE_B (COMBINE_B_OUT | COMBINE_B_IN) #define COMBINE_A_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_A_IN) #define COMBINE_B_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_B_IN) #define COMBINE_A_ATOP (COMBINE_B_OUT | COMBINE_A_IN) #define COMBINE_B_ATOP (COMBINE_A_OUT | COMBINE_B_IN) #define COMBINE_XOR (COMBINE_A_OUT | COMBINE_B_OUT) /* portion covered by a but not b */ static uint8_t combine_disjoint_out_part (uint8_t a, uint8_t b) { /* min (1, (1-b) / a) */ b = ~b; /* 1 - b */ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */ return MASK; /* 1 */ return DIV_UN8 (b, a); /* (1-b) / a */ } /* portion covered by both a and b */ static uint8_t combine_disjoint_in_part (uint8_t a, uint8_t b) { /* max (1-(1-b)/a,0) */ /* = - min ((1-b)/a - 1, 0) */ /* = 1 - min (1, (1-b)/a) */ b = ~b; /* 1 - b */ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */ return 0; /* 1 - 1 */ return ~DIV_UN8(b, a); /* 1 - (1-b) / a */ } /* portion covered by a but not b */ static uint8_t combine_conjoint_out_part (uint8_t a, uint8_t b) { /* max (1-b/a,0) */ /* = 1-min(b/a,1) */ /* min (1, (1-b) / a) */ if (b >= a) /* b >= a -> b/a >= 1 */ return 0x00; /* 0 */ return ~DIV_UN8(b, a); /* 1 - b/a */ } /* portion covered by both a and b */ static uint8_t combine_conjoint_in_part (uint8_t a, uint8_t b) { /* min (1,b/a) */ if (b >= a) /* b >= a -> b/a >= 1 */ return MASK; /* 1 */ return DIV_UN8 (b, a); /* b/a */ } #define GET_COMP(v, i) ((uint16_t) (uint8_t) ((v) >> i)) #define ADD(x, y, i, t) \ ((t) = GET_COMP (x, i) + GET_COMP (y, i), \ (uint32_t) ((uint8_t) ((t) | (0 - ((t) >> G_SHIFT)))) << (i)) #define GENERIC(x, y, i, ax, ay, t, u, v) \ ((t) = (MUL_UN8 (GET_COMP (y, i), ay, (u)) + \ MUL_UN8 (GET_COMP (x, i), ax, (v))), \ (uint32_t) ((uint8_t) ((t) | \ (0 - ((t) >> G_SHIFT)))) << (i)) static void combine_disjoint_general_u (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width, uint8_t combine) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t m, n, o, p; uint16_t Fa, Fb, t, u, v; uint8_t sa = s >> A_SHIFT; uint8_t da = d >> A_SHIFT; switch (combine & COMBINE_A) { default: Fa = 0; break; case COMBINE_A_OUT: Fa = combine_disjoint_out_part (sa, da); break; case COMBINE_A_IN: Fa = combine_disjoint_in_part (sa, da); break; case COMBINE_A: Fa = MASK; break; } switch (combine & COMBINE_B) { default: Fb = 0; break; case COMBINE_B_OUT: Fb = combine_disjoint_out_part (da, sa); break; case COMBINE_B_IN: Fb = combine_disjoint_in_part (da, sa); break; case COMBINE_B: Fb = MASK; break; } m = GENERIC (s, d, 0, Fa, Fb, t, u, v); n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v); o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v); p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v); s = m | n | o | p; *(dest + i) = s; } } static void combine_disjoint_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint16_t a = s >> A_SHIFT; if (s != 0x00) { uint32_t d = *(dest + i); a = combine_disjoint_out_part (d >> A_SHIFT, a); UN8x4_MUL_UN8_ADD_UN8x4 (d, a, s); *(dest + i) = d; } } } static void combine_disjoint_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_IN); } static void combine_disjoint_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_IN); } static void combine_disjoint_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_OUT); } static void combine_disjoint_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_OUT); } static void combine_disjoint_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP); } static void combine_disjoint_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP); } static void combine_disjoint_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_u (dest, src, mask, width, COMBINE_XOR); } static void combine_conjoint_general_u (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width, uint8_t combine) { int i; for (i = 0; i < width; ++i) { uint32_t s = combine_mask (src, mask, i); uint32_t d = *(dest + i); uint32_t m, n, o, p; uint16_t Fa, Fb, t, u, v; uint8_t sa = s >> A_SHIFT; uint8_t da = d >> A_SHIFT; switch (combine & COMBINE_A) { default: Fa = 0; break; case COMBINE_A_OUT: Fa = combine_conjoint_out_part (sa, da); break; case COMBINE_A_IN: Fa = combine_conjoint_in_part (sa, da); break; case COMBINE_A: Fa = MASK; break; } switch (combine & COMBINE_B) { default: Fb = 0; break; case COMBINE_B_OUT: Fb = combine_conjoint_out_part (da, sa); break; case COMBINE_B_IN: Fb = combine_conjoint_in_part (da, sa); break; case COMBINE_B: Fb = MASK; break; } m = GENERIC (s, d, 0, Fa, Fb, t, u, v); n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v); o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v); p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v); s = m | n | o | p; *(dest + i) = s; } } static void combine_conjoint_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OVER); } static void combine_conjoint_over_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OVER); } static void combine_conjoint_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_IN); } static void combine_conjoint_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_IN); } static void combine_conjoint_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OUT); } static void combine_conjoint_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OUT); } static void combine_conjoint_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP); } static void combine_conjoint_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP); } static void combine_conjoint_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_u (dest, src, mask, width, COMBINE_XOR); } /* Component alpha combiners */ static void combine_clear_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { memset (dest, 0, width * sizeof(uint32_t)); } static void combine_src_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t m = *(mask + i); combine_mask_value_ca (&s, &m); *(dest + i) = s; } } static void combine_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t a; combine_mask_ca (&s, &m); a = ~m; if (a) { uint32_t d = *(dest + i); UN8x4_MUL_UN8x4_ADD_UN8x4 (d, a, s); s = d; } *(dest + i) = s; } } static void combine_over_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint32_t a = ~d >> A_SHIFT; if (a) { uint32_t s = *(src + i); uint32_t m = *(mask + i); UN8x4_MUL_UN8x4 (s, m); UN8x4_MUL_UN8_ADD_UN8x4 (s, a, d); *(dest + i) = s; } } } static void combine_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint16_t a = d >> A_SHIFT; uint32_t s = 0; if (a) { uint32_t m = *(mask + i); s = *(src + i); combine_mask_value_ca (&s, &m); if (a != MASK) UN8x4_MUL_UN8 (s, a); } *(dest + i) = s; } } static void combine_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t a; combine_mask_alpha_ca (&s, &m); a = m; if (a != ~0) { uint32_t d = 0; if (a) { d = *(dest + i); UN8x4_MUL_UN8x4 (d, a); } *(dest + i) = d; } } } static void combine_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint16_t a = ~d >> A_SHIFT; uint32_t s = 0; if (a) { uint32_t m = *(mask + i); s = *(src + i); combine_mask_value_ca (&s, &m); if (a != MASK) UN8x4_MUL_UN8 (s, a); } *(dest + i) = s; } } static void combine_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t a; combine_mask_alpha_ca (&s, &m); a = ~m; if (a != ~0) { uint32_t d = 0; if (a) { d = *(dest + i); UN8x4_MUL_UN8x4 (d, a); } *(dest + i) = d; } } } static void combine_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t ad; uint16_t as = d >> A_SHIFT; combine_mask_ca (&s, &m); ad = ~m; UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); *(dest + i) = d; } } static void combine_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t ad; uint16_t as = ~d >> A_SHIFT; combine_mask_ca (&s, &m); ad = m; UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); *(dest + i) = d; } } static void combine_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t d = *(dest + i); uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t ad; uint16_t as = ~d >> A_SHIFT; combine_mask_ca (&s, &m); ad = ~m; UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); *(dest + i) = d; } } static void combine_add_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s = *(src + i); uint32_t m = *(mask + i); uint32_t d = *(dest + i); combine_mask_value_ca (&s, &m); UN8x4_ADD_UN8x4 (d, s); *(dest + i) = d; } } static void combine_saturate_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; for (i = 0; i < width; ++i) { uint32_t s, d; uint16_t sa, sr, sg, sb, da; uint16_t t, u, v; uint32_t m, n, o, p; d = *(dest + i); s = *(src + i); m = *(mask + i); combine_mask_ca (&s, &m); sa = (m >> A_SHIFT); sr = (m >> R_SHIFT) & MASK; sg = (m >> G_SHIFT) & MASK; sb = m & MASK; da = ~d >> A_SHIFT; if (sb <= da) m = ADD (s, d, 0, t); else m = GENERIC (s, d, 0, (da << G_SHIFT) / sb, MASK, t, u, v); if (sg <= da) n = ADD (s, d, G_SHIFT, t); else n = GENERIC (s, d, G_SHIFT, (da << G_SHIFT) / sg, MASK, t, u, v); if (sr <= da) o = ADD (s, d, R_SHIFT, t); else o = GENERIC (s, d, R_SHIFT, (da << G_SHIFT) / sr, MASK, t, u, v); if (sa <= da) p = ADD (s, d, A_SHIFT, t); else p = GENERIC (s, d, A_SHIFT, (da << G_SHIFT) / sa, MASK, t, u, v); *(dest + i) = m | n | o | p; } } static void combine_disjoint_general_ca (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width, uint8_t combine) { int i; for (i = 0; i < width; ++i) { uint32_t s, d; uint32_t m, n, o, p; uint32_t Fa, Fb; uint16_t t, u, v; uint32_t sa; uint8_t da; s = *(src + i); m = *(mask + i); d = *(dest + i); da = d >> A_SHIFT; combine_mask_ca (&s, &m); sa = m; switch (combine & COMBINE_A) { default: Fa = 0; break; case COMBINE_A_OUT: m = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> 0), da); n = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT; o = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT; p = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT; Fa = m | n | o | p; break; case COMBINE_A_IN: m = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> 0), da); n = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT; o = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT; p = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT; Fa = m | n | o | p; break; case COMBINE_A: Fa = ~0; break; } switch (combine & COMBINE_B) { default: Fb = 0; break; case COMBINE_B_OUT: m = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> 0)); n = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT; o = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT; p = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT; Fb = m | n | o | p; break; case COMBINE_B_IN: m = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> 0)); n = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT; o = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT; p = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT; Fb = m | n | o | p; break; case COMBINE_B: Fb = ~0; break; } m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v); n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v); o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v); p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v); s = m | n | o | p; *(dest + i) = s; } } static void combine_disjoint_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER); } static void combine_disjoint_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_IN); } static void combine_disjoint_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_IN); } static void combine_disjoint_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT); } static void combine_disjoint_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT); } static void combine_disjoint_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP); } static void combine_disjoint_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP); } static void combine_disjoint_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_disjoint_general_ca (dest, src, mask, width, COMBINE_XOR); } static void combine_conjoint_general_ca (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width, uint8_t combine) { int i; for (i = 0; i < width; ++i) { uint32_t s, d; uint32_t m, n, o, p; uint32_t Fa, Fb; uint16_t t, u, v; uint32_t sa; uint8_t da; s = *(src + i); m = *(mask + i); d = *(dest + i); da = d >> A_SHIFT; combine_mask_ca (&s, &m); sa = m; switch (combine & COMBINE_A) { default: Fa = 0; break; case COMBINE_A_OUT: m = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> 0), da); n = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT; o = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT; p = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT; Fa = m | n | o | p; break; case COMBINE_A_IN: m = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> 0), da); n = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT; o = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT; p = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT; Fa = m | n | o | p; break; case COMBINE_A: Fa = ~0; break; } switch (combine & COMBINE_B) { default: Fb = 0; break; case COMBINE_B_OUT: m = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> 0)); n = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT; o = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT; p = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT; Fb = m | n | o | p; break; case COMBINE_B_IN: m = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> 0)); n = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT; o = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT; p = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT; Fb = m | n | o | p; break; case COMBINE_B: Fb = ~0; break; } m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v); n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v); o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v); p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v); s = m | n | o | p; *(dest + i) = s; } } static void combine_conjoint_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER); } static void combine_conjoint_over_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OVER); } static void combine_conjoint_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_IN); } static void combine_conjoint_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_IN); } static void combine_conjoint_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT); } static void combine_conjoint_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT); } static void combine_conjoint_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP); } static void combine_conjoint_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP); } static void combine_conjoint_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { combine_conjoint_general_ca (dest, src, mask, width, COMBINE_XOR); } void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp) { /* Unified alpha */ imp->combine_32[PIXMAN_OP_CLEAR] = combine_clear; imp->combine_32[PIXMAN_OP_SRC] = combine_src_u; imp->combine_32[PIXMAN_OP_DST] = combine_dst; imp->combine_32[PIXMAN_OP_OVER] = combine_over_u; imp->combine_32[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u; imp->combine_32[PIXMAN_OP_IN] = combine_in_u; imp->combine_32[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u; imp->combine_32[PIXMAN_OP_OUT] = combine_out_u; imp->combine_32[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u; imp->combine_32[PIXMAN_OP_ATOP] = combine_atop_u; imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u; imp->combine_32[PIXMAN_OP_XOR] = combine_xor_u; imp->combine_32[PIXMAN_OP_ADD] = combine_add_u; imp->combine_32[PIXMAN_OP_SATURATE] = combine_saturate_u; /* Disjoint, unified */ imp->combine_32[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear; imp->combine_32[PIXMAN_OP_DISJOINT_SRC] = combine_src_u; imp->combine_32[PIXMAN_OP_DISJOINT_DST] = combine_dst; imp->combine_32[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u; imp->combine_32[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_u; imp->combine_32[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u; imp->combine_32[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u; imp->combine_32[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u; imp->combine_32[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u; imp->combine_32[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u; imp->combine_32[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u; imp->combine_32[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u; /* Conjoint, unified */ imp->combine_32[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear; imp->combine_32[PIXMAN_OP_CONJOINT_SRC] = combine_src_u; imp->combine_32[PIXMAN_OP_CONJOINT_DST] = combine_dst; imp->combine_32[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u; imp->combine_32[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u; imp->combine_32[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u; imp->combine_32[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u; imp->combine_32[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u; imp->combine_32[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u; imp->combine_32[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u; imp->combine_32[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u; imp->combine_32[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u; imp->combine_32[PIXMAN_OP_MULTIPLY] = combine_multiply_u; imp->combine_32[PIXMAN_OP_SCREEN] = combine_screen_u; imp->combine_32[PIXMAN_OP_OVERLAY] = combine_overlay_u; imp->combine_32[PIXMAN_OP_DARKEN] = combine_darken_u; imp->combine_32[PIXMAN_OP_LIGHTEN] = combine_lighten_u; imp->combine_32[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u; imp->combine_32[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u; imp->combine_32[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u; imp->combine_32[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u; imp->combine_32[PIXMAN_OP_DIFFERENCE] = combine_difference_u; imp->combine_32[PIXMAN_OP_EXCLUSION] = combine_exclusion_u; imp->combine_32[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u; imp->combine_32[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u; imp->combine_32[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u; imp->combine_32[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u; /* Component alpha combiners */ imp->combine_32_ca[PIXMAN_OP_CLEAR] = combine_clear_ca; imp->combine_32_ca[PIXMAN_OP_SRC] = combine_src_ca; /* dest */ imp->combine_32_ca[PIXMAN_OP_OVER] = combine_over_ca; imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca; imp->combine_32_ca[PIXMAN_OP_IN] = combine_in_ca; imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_OUT] = combine_out_ca; imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_ATOP] = combine_atop_ca; imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_XOR] = combine_xor_ca; imp->combine_32_ca[PIXMAN_OP_ADD] = combine_add_ca; imp->combine_32_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca; /* Disjoint CA */ imp->combine_32_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_SRC] = combine_src_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_DST] = combine_dst; imp->combine_32_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca; /* Conjoint CA */ imp->combine_32_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_SRC] = combine_src_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_DST] = combine_dst; imp->combine_32_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca; imp->combine_32_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca; imp->combine_32_ca[PIXMAN_OP_SCREEN] = combine_screen_ca; imp->combine_32_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca; imp->combine_32_ca[PIXMAN_OP_DARKEN] = combine_darken_ca; imp->combine_32_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca; imp->combine_32_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca; imp->combine_32_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca; imp->combine_32_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca; imp->combine_32_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca; imp->combine_32_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca; imp->combine_32_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca; /* It is not clear that these make sense, so make them noops for now */ imp->combine_32_ca[PIXMAN_OP_HSL_HUE] = combine_dst; imp->combine_32_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst; imp->combine_32_ca[PIXMAN_OP_HSL_COLOR] = combine_dst; imp->combine_32_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-combine32.h000066400000000000000000000156561271037650300264450ustar00rootroot00000000000000#define COMPONENT_SIZE 8 #define MASK 0xff #define ONE_HALF 0x80 #define A_SHIFT 8 * 3 #define R_SHIFT 8 * 2 #define G_SHIFT 8 #define A_MASK 0xff000000 #define R_MASK 0xff0000 #define G_MASK 0xff00 #define RB_MASK 0xff00ff #define AG_MASK 0xff00ff00 #define RB_ONE_HALF 0x800080 #define RB_MASK_PLUS_ONE 0x10000100 #define ALPHA_8(x) ((x) >> A_SHIFT) #define RED_8(x) (((x) >> R_SHIFT) & MASK) #define GREEN_8(x) (((x) >> G_SHIFT) & MASK) #define BLUE_8(x) ((x) & MASK) /* * ARMv6 has UQADD8 instruction, which implements unsigned saturated * addition for 8-bit values packed in 32-bit registers. It is very useful * for UN8x4_ADD_UN8x4, UN8_rb_ADD_UN8_rb and ADD_UN8 macros (which would * otherwise need a lot of arithmetic operations to simulate this operation). * Since most of the major ARM linux distros are built for ARMv7, we are * much less dependent on runtime CPU detection and can get practical * benefits from conditional compilation here for a lot of users. */ #if defined(USE_GCC_INLINE_ASM) && defined(__arm__) && \ !defined(__aarch64__) && (!defined(__thumb__) || defined(__thumb2__)) #if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \ defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_7__) || \ defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \ defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) static force_inline uint32_t un8x4_add_un8x4 (uint32_t x, uint32_t y) { uint32_t t; asm ("uqadd8 %0, %1, %2" : "=r" (t) : "%r" (x), "r" (y)); return t; } #define UN8x4_ADD_UN8x4(x, y) \ ((x) = un8x4_add_un8x4 ((x), (y))) #define UN8_rb_ADD_UN8_rb(x, y, t) \ ((t) = un8x4_add_un8x4 ((x), (y)), (x) = (t)) #define ADD_UN8(x, y, t) \ ((t) = (x), un8x4_add_un8x4 ((t), (y))) #endif #endif /*****************************************************************************/ /* * Helper macros. */ #define MUL_UN8(a, b, t) \ ((t) = (a) * (uint16_t)(b) + ONE_HALF, ((((t) >> G_SHIFT ) + (t) ) >> G_SHIFT )) #define DIV_UN8(a, b) \ (((uint16_t) (a) * MASK + ((b) / 2)) / (b)) #ifndef ADD_UN8 #define ADD_UN8(x, y, t) \ ((t) = (x) + (y), \ (uint32_t) (uint8_t) ((t) | (0 - ((t) >> G_SHIFT)))) #endif #define DIV_ONE_UN8(x) \ (((x) + ONE_HALF + (((x) + ONE_HALF) >> G_SHIFT)) >> G_SHIFT) /* * The methods below use some tricks to be able to do two color * components at the same time. */ /* * x_rb = (x_rb * a) / 255 */ #define UN8_rb_MUL_UN8(x, a, t) \ do \ { \ t = ((x) & RB_MASK) * (a); \ t += RB_ONE_HALF; \ x = (t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT; \ x &= RB_MASK; \ } while (0) /* * x_rb = min (x_rb + y_rb, 255) */ #ifndef UN8_rb_ADD_UN8_rb #define UN8_rb_ADD_UN8_rb(x, y, t) \ do \ { \ t = ((x) + (y)); \ t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); \ x = (t & RB_MASK); \ } while (0) #endif /* * x_rb = (x_rb * a_rb) / 255 */ #define UN8_rb_MUL_UN8_rb(x, a, t) \ do \ { \ t = (x & MASK) * (a & MASK); \ t |= (x & R_MASK) * ((a >> R_SHIFT) & MASK); \ t += RB_ONE_HALF; \ t = (t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT; \ x = t & RB_MASK; \ } while (0) /* * x_c = (x_c * a) / 255 */ #define UN8x4_MUL_UN8(x, a) \ do \ { \ uint32_t r1__, r2__, t__; \ \ r1__ = (x); \ UN8_rb_MUL_UN8 (r1__, (a), t__); \ \ r2__ = (x) >> G_SHIFT; \ UN8_rb_MUL_UN8 (r2__, (a), t__); \ \ (x) = r1__ | (r2__ << G_SHIFT); \ } while (0) /* * x_c = (x_c * a) / 255 + y_c */ #define UN8x4_MUL_UN8_ADD_UN8x4(x, a, y) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x); \ r2__ = (y) & RB_MASK; \ UN8_rb_MUL_UN8 (r1__, (a), t__); \ UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ \ r2__ = (x) >> G_SHIFT; \ r3__ = ((y) >> G_SHIFT) & RB_MASK; \ UN8_rb_MUL_UN8 (r2__, (a), t__); \ UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ \ (x) = r1__ | (r2__ << G_SHIFT); \ } while (0) /* * x_c = (x_c * a + y_c * b) / 255 */ #define UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8(x, a, y, b) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x); \ r2__ = (y); \ UN8_rb_MUL_UN8 (r1__, (a), t__); \ UN8_rb_MUL_UN8 (r2__, (b), t__); \ UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ \ r2__ = ((x) >> G_SHIFT); \ r3__ = ((y) >> G_SHIFT); \ UN8_rb_MUL_UN8 (r2__, (a), t__); \ UN8_rb_MUL_UN8 (r3__, (b), t__); \ UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ \ (x) = r1__ | (r2__ << G_SHIFT); \ } while (0) /* * x_c = (x_c * a_c) / 255 */ #define UN8x4_MUL_UN8x4(x, a) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x); \ r2__ = (a); \ UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ \ r2__ = (x) >> G_SHIFT; \ r3__ = (a) >> G_SHIFT; \ UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ \ (x) = r1__ | (r2__ << G_SHIFT); \ } while (0) /* * x_c = (x_c * a_c) / 255 + y_c */ #define UN8x4_MUL_UN8x4_ADD_UN8x4(x, a, y) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x); \ r2__ = (a); \ UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ r2__ = (y) & RB_MASK; \ UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ \ r2__ = ((x) >> G_SHIFT); \ r3__ = ((a) >> G_SHIFT); \ UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ r3__ = ((y) >> G_SHIFT) & RB_MASK; \ UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ \ (x) = r1__ | (r2__ << G_SHIFT); \ } while (0) /* * x_c = (x_c * a_c + y_c * b) / 255 */ #define UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8(x, a, y, b) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x); \ r2__ = (a); \ UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ r2__ = (y); \ UN8_rb_MUL_UN8 (r2__, (b), t__); \ UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ \ r2__ = (x) >> G_SHIFT; \ r3__ = (a) >> G_SHIFT; \ UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ r3__ = (y) >> G_SHIFT; \ UN8_rb_MUL_UN8 (r3__, (b), t__); \ UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ \ x = r1__ | (r2__ << G_SHIFT); \ } while (0) /* x_c = min(x_c + y_c, 255) */ #ifndef UN8x4_ADD_UN8x4 #define UN8x4_ADD_UN8x4(x, y) \ do \ { \ uint32_t r1__, r2__, r3__, t__; \ \ r1__ = (x) & RB_MASK; \ r2__ = (y) & RB_MASK; \ UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ \ r2__ = ((x) >> G_SHIFT) & RB_MASK; \ r3__ = ((y) >> G_SHIFT) & RB_MASK; \ UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ \ x = r1__ | (r2__ << G_SHIFT); \ } while (0) #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-compiler.h000066400000000000000000000142221271037650300264620ustar00rootroot00000000000000/* Pixman uses some non-standard compiler features. This file ensures * they exist * * The features are: * * FUNC must be defined to expand to the current function * PIXMAN_EXPORT should be defined to whatever is required to * export functions from a shared library * limits limits for various types must be defined * inline must be defined * force_inline must be defined */ #if defined (__GNUC__) # define FUNC ((const char*) (__PRETTY_FUNCTION__)) #elif defined (__sun) || (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) # define FUNC ((const char*) (__func__)) #else # define FUNC ((const char*) ("???")) #endif #if defined (__GNUC__) # define unlikely(expr) __builtin_expect ((expr), 0) #else # define unlikely(expr) (expr) #endif #if defined (__GNUC__) # define MAYBE_UNUSED __attribute__((unused)) #else # define MAYBE_UNUSED #endif #ifndef INT16_MIN # define INT16_MIN (-32767-1) #endif #ifndef INT16_MAX # define INT16_MAX (32767) #endif #ifndef INT32_MIN # define INT32_MIN (-2147483647-1) #endif #ifndef INT32_MAX # define INT32_MAX (2147483647) #endif #ifndef UINT32_MIN # define UINT32_MIN (0) #endif #ifndef UINT32_MAX # define UINT32_MAX (4294967295U) #endif #ifndef INT64_MIN # define INT64_MIN (-9223372036854775807-1) #endif #ifndef INT64_MAX # define INT64_MAX (9223372036854775807) #endif #ifndef SIZE_MAX # define SIZE_MAX ((size_t)-1) #endif #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #ifdef _MSC_VER /* 'inline' is available only in C++ in MSVC */ # define inline __inline # define force_inline __forceinline # define noinline __declspec(noinline) #elif defined __GNUC__ || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) # define inline __inline__ # define force_inline __inline__ __attribute__ ((__always_inline__)) # define noinline __attribute__((noinline)) #else # ifndef force_inline # define force_inline inline # endif # ifndef noinline # define noinline # endif #endif /* GCC visibility */ #if defined(__GNUC__) && __GNUC__ >= 4 && !defined(_WIN32) # define PIXMAN_EXPORT __attribute__ ((visibility("default"))) /* Sun Studio 8 visibility */ #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) # define PIXMAN_EXPORT __global #else # define PIXMAN_EXPORT #endif /* member offsets */ #define CONTAINER_OF(type, member, data) \ ((type *)(((uint8_t *)data) - offsetof (type, member))) /* TLS */ #if defined(PIXMAN_NO_TLS) # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ static type name # define PIXMAN_GET_THREAD_LOCAL(name) \ (&name) #elif defined(TLS) # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ static TLS type name # define PIXMAN_GET_THREAD_LOCAL(name) \ (&name) #elif defined(__MINGW32__) # define _NO_W32_PSEUDO_MODIFIERS # include # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ static volatile int tls_ ## name ## _initialized = 0; \ static void *tls_ ## name ## _mutex = NULL; \ static unsigned tls_ ## name ## _index; \ \ static type * \ tls_ ## name ## _alloc (void) \ { \ type *value = calloc (1, sizeof (type)); \ if (value) \ TlsSetValue (tls_ ## name ## _index, value); \ return value; \ } \ \ static force_inline type * \ tls_ ## name ## _get (void) \ { \ type *value; \ if (!tls_ ## name ## _initialized) \ { \ if (!tls_ ## name ## _mutex) \ { \ void *mutex = CreateMutexA (NULL, 0, NULL); \ if (InterlockedCompareExchangePointer ( \ &tls_ ## name ## _mutex, mutex, NULL) != NULL) \ { \ CloseHandle (mutex); \ } \ } \ WaitForSingleObject (tls_ ## name ## _mutex, 0xFFFFFFFF); \ if (!tls_ ## name ## _initialized) \ { \ tls_ ## name ## _index = TlsAlloc (); \ tls_ ## name ## _initialized = 1; \ } \ ReleaseMutex (tls_ ## name ## _mutex); \ } \ if (tls_ ## name ## _index == 0xFFFFFFFF) \ return NULL; \ value = TlsGetValue (tls_ ## name ## _index); \ if (!value) \ value = tls_ ## name ## _alloc (); \ return value; \ } # define PIXMAN_GET_THREAD_LOCAL(name) \ tls_ ## name ## _get () #elif defined(_MSC_VER) # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ static __declspec(thread) type name # define PIXMAN_GET_THREAD_LOCAL(name) \ (&name) #elif defined(HAVE_PTHREAD_SETSPECIFIC) #include # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ static pthread_once_t tls_ ## name ## _once_control = PTHREAD_ONCE_INIT; \ static pthread_key_t tls_ ## name ## _key; \ \ static void \ tls_ ## name ## _destroy_value (void *value) \ { \ free (value); \ } \ \ static void \ tls_ ## name ## _make_key (void) \ { \ pthread_key_create (&tls_ ## name ## _key, \ tls_ ## name ## _destroy_value); \ } \ \ static type * \ tls_ ## name ## _alloc (void) \ { \ type *value = calloc (1, sizeof (type)); \ if (value) \ pthread_setspecific (tls_ ## name ## _key, value); \ return value; \ } \ \ static force_inline type * \ tls_ ## name ## _get (void) \ { \ type *value = NULL; \ if (pthread_once (&tls_ ## name ## _once_control, \ tls_ ## name ## _make_key) == 0) \ { \ value = pthread_getspecific (tls_ ## name ## _key); \ if (!value) \ value = tls_ ## name ## _alloc (); \ } \ return value; \ } # define PIXMAN_GET_THREAD_LOCAL(name) \ tls_ ## name ## _get () #else # error "Unknown thread local support for this system. Pixman will not work with multiple threads. Define PIXMAN_NO_TLS to acknowledge and accept this limitation and compile pixman without thread-safety support." #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-conical-gradient.c000066400000000000000000000124421271037650300300500ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" static force_inline double coordinates_to_parameter (double x, double y, double angle) { double t; t = atan2 (y, x) + angle; while (t < 0) t += 2 * M_PI; while (t >= 2 * M_PI) t -= 2 * M_PI; return 1 - t * (1 / (2 * M_PI)); /* Scale t to [0, 1] and * make rotation CCW */ } static uint32_t * conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t *buffer = iter->buffer; gradient_t *gradient = (gradient_t *)image; conical_gradient_t *conical = (conical_gradient_t *)image; uint32_t *end = buffer + width; pixman_gradient_walker_t walker; pixman_bool_t affine = TRUE; double cx = 1.; double cy = 0.; double cz = 0.; double rx = x + 0.5; double ry = y + 0.5; double rz = 1.; _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); if (image->common.transform) { pixman_vector_t v; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (image->common.transform, &v)) return iter->buffer; cx = image->common.transform->matrix[0][0] / 65536.; cy = image->common.transform->matrix[1][0] / 65536.; cz = image->common.transform->matrix[2][0] / 65536.; rx = v.vector[0] / 65536.; ry = v.vector[1] / 65536.; rz = v.vector[2] / 65536.; affine = image->common.transform->matrix[2][0] == 0 && v.vector[2] == pixman_fixed_1; } if (affine) { rx -= conical->center.x / 65536.; ry -= conical->center.y / 65536.; while (buffer < end) { if (!mask || *mask++) { double t = coordinates_to_parameter (rx, ry, conical->angle); *buffer = _pixman_gradient_walker_pixel ( &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t)); } ++buffer; rx += cx; ry += cy; } } else { while (buffer < end) { double x, y; if (!mask || *mask++) { double t; if (rz != 0) { x = rx / rz; y = ry / rz; } else { x = y = 0.; } x -= conical->center.x / 65536.; y -= conical->center.y / 65536.; t = coordinates_to_parameter (x, y, conical->angle); *buffer = _pixman_gradient_walker_pixel ( &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t)); } ++buffer; rx += cx; ry += cy; rz += cz; } } iter->y++; return iter->buffer; } static uint32_t * conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) { uint32_t *buffer = conical_get_scanline_narrow (iter, NULL); pixman_expand_to_float ( (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); return buffer; } void _pixman_conical_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) { if (iter->iter_flags & ITER_NARROW) iter->get_scanline = conical_get_scanline_narrow; else iter->get_scanline = conical_get_scanline_wide; } PIXMAN_EXPORT pixman_image_t * pixman_image_create_conical_gradient (const pixman_point_fixed_t * center, pixman_fixed_t angle, const pixman_gradient_stop_t *stops, int n_stops) { pixman_image_t *image = _pixman_image_allocate (); conical_gradient_t *conical; if (!image) return NULL; conical = &image->conical; if (!_pixman_init_gradient (&conical->common, stops, n_stops)) { free (image); return NULL; } angle = MOD (angle, pixman_int_to_fixed (360)); image->type = CONICAL; conical->center = *center; conical->angle = (pixman_fixed_to_double (angle) / 180.0) * M_PI; return image; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-edge-accessors.c000066400000000000000000000000671271037650300275340ustar00rootroot00000000000000 #define PIXMAN_FB_ACCESSORS #include "pixman-edge.c" Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-edge-imp.h000066400000000000000000000106211271037650300263360ustar00rootroot00000000000000/* * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef rasterize_span #endif static void RASTERIZE_EDGES (pixman_image_t *image, pixman_edge_t *l, pixman_edge_t *r, pixman_fixed_t t, pixman_fixed_t b) { pixman_fixed_t y = t; uint32_t *line; uint32_t *buf = (image)->bits.bits; int stride = (image)->bits.rowstride; int width = (image)->bits.width; line = buf + pixman_fixed_to_int (y) * stride; for (;;) { pixman_fixed_t lx; pixman_fixed_t rx; int lxi; int rxi; lx = l->x; rx = r->x; #if N_BITS == 1 /* For the non-antialiased case, round the coordinates up, in effect * sampling just slightly to the left of the pixel. This is so that * when the sample point lies exactly on the line, we round towards * north-west. * * (The AA case does a similar adjustment in RENDER_SAMPLES_X) */ lx += X_FRAC_FIRST(1) - pixman_fixed_e; rx += X_FRAC_FIRST(1) - pixman_fixed_e; #endif /* clip X */ if (lx < 0) lx = 0; if (pixman_fixed_to_int (rx) >= width) #if N_BITS == 1 rx = pixman_int_to_fixed (width); #else /* Use the last pixel of the scanline, covered 100%. * We can't use the first pixel following the scanline, * because accessing it could result in a buffer overrun. */ rx = pixman_int_to_fixed (width) - 1; #endif /* Skip empty (or backwards) sections */ if (rx > lx) { /* Find pixel bounds for span */ lxi = pixman_fixed_to_int (lx); rxi = pixman_fixed_to_int (rx); #if N_BITS == 1 { #define LEFT_MASK(x) \ (((x) & 0x1f) ? \ SCREEN_SHIFT_RIGHT (0xffffffff, (x) & 0x1f) : 0) #define RIGHT_MASK(x) \ (((32 - (x)) & 0x1f) ? \ SCREEN_SHIFT_LEFT (0xffffffff, (32 - (x)) & 0x1f) : 0) #define MASK_BITS(x,w,l,n,r) { \ n = (w); \ r = RIGHT_MASK ((x) + n); \ l = LEFT_MASK (x); \ if (l) { \ n -= 32 - ((x) & 0x1f); \ if (n < 0) { \ n = 0; \ l &= r; \ r = 0; \ } \ } \ n >>= 5; \ } uint32_t *a = line; uint32_t startmask; uint32_t endmask; int nmiddle; int width = rxi - lxi; int x = lxi; a += x >> 5; x &= 0x1f; MASK_BITS (x, width, startmask, nmiddle, endmask); if (startmask) { WRITE(image, a, READ(image, a) | startmask); a++; } while (nmiddle--) WRITE(image, a++, 0xffffffff); if (endmask) WRITE(image, a, READ(image, a) | endmask); } #else { DEFINE_ALPHA(line,lxi); int lxs; int rxs; /* Sample coverage for edge pixels */ lxs = RENDER_SAMPLES_X (lx, N_BITS); rxs = RENDER_SAMPLES_X (rx, N_BITS); /* Add coverage across row */ if (lxi == rxi) { ADD_ALPHA (rxs - lxs); } else { int xi; ADD_ALPHA (N_X_FRAC(N_BITS) - lxs); STEP_ALPHA; for (xi = lxi + 1; xi < rxi; xi++) { ADD_ALPHA (N_X_FRAC(N_BITS)); STEP_ALPHA; } ADD_ALPHA (rxs); } } #endif } if (y == b) break; #if N_BITS > 1 if (pixman_fixed_frac (y) != Y_FRAC_LAST(N_BITS)) { RENDER_EDGE_STEP_SMALL (l); RENDER_EDGE_STEP_SMALL (r); y += STEP_Y_SMALL(N_BITS); } else #endif { RENDER_EDGE_STEP_BIG (l); RENDER_EDGE_STEP_BIG (r); y += STEP_Y_BIG(N_BITS); line += stride; } } } #undef rasterize_span Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-edge.c000066400000000000000000000251351271037650300255540ustar00rootroot00000000000000/* * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" #include "pixman-accessor.h" /* * Step across a small sample grid gap */ #define RENDER_EDGE_STEP_SMALL(edge) \ { \ edge->x += edge->stepx_small; \ edge->e += edge->dx_small; \ if (edge->e > 0) \ { \ edge->e -= edge->dy; \ edge->x += edge->signdx; \ } \ } /* * Step across a large sample grid gap */ #define RENDER_EDGE_STEP_BIG(edge) \ { \ edge->x += edge->stepx_big; \ edge->e += edge->dx_big; \ if (edge->e > 0) \ { \ edge->e -= edge->dy; \ edge->x += edge->signdx; \ } \ } #ifdef PIXMAN_FB_ACCESSORS #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_accessors #else #define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_no_accessors #endif /* * 4 bit alpha */ #define N_BITS 4 #define RASTERIZE_EDGES rasterize_edges_4 #ifndef WORDS_BIGENDIAN #define SHIFT_4(o) ((o) << 2) #else #define SHIFT_4(o) ((1 - (o)) << 2) #endif #define GET_4(x, o) (((x) >> SHIFT_4 (o)) & 0xf) #define PUT_4(x, o, v) \ (((x) & ~(0xf << SHIFT_4 (o))) | (((v) & 0xf) << SHIFT_4 (o))) #define DEFINE_ALPHA(line, x) \ uint8_t *__ap = (uint8_t *) line + ((x) >> 1); \ int __ao = (x) & 1 #define STEP_ALPHA ((__ap += __ao), (__ao ^= 1)) #define ADD_ALPHA(a) \ { \ uint8_t __o = READ (image, __ap); \ uint8_t __a = (a) + GET_4 (__o, __ao); \ WRITE (image, __ap, PUT_4 (__o, __ao, __a | (0 - ((__a) >> 4)))); \ } #include "pixman-edge-imp.h" #undef ADD_ALPHA #undef STEP_ALPHA #undef DEFINE_ALPHA #undef RASTERIZE_EDGES #undef N_BITS /* * 1 bit alpha */ #define N_BITS 1 #define RASTERIZE_EDGES rasterize_edges_1 #include "pixman-edge-imp.h" #undef RASTERIZE_EDGES #undef N_BITS /* * 8 bit alpha */ static force_inline uint8_t clip255 (int x) { if (x > 255) return 255; return x; } #define ADD_SATURATE_8(buf, val, length) \ do \ { \ int i__ = (length); \ uint8_t *buf__ = (buf); \ int val__ = (val); \ \ while (i__--) \ { \ WRITE (image, (buf__), clip255 (READ (image, (buf__)) + (val__))); \ (buf__)++; \ } \ } while (0) /* * We want to detect the case where we add the same value to a long * span of pixels. The triangles on the end are filled in while we * count how many sub-pixel scanlines contribute to the middle section. * * +--------------------------+ * fill_height =| \ / * +------------------+ * |================| * fill_start fill_end */ static void rasterize_edges_8 (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { pixman_fixed_t y = t; uint32_t *line; int fill_start = -1, fill_end = -1; int fill_size = 0; uint32_t *buf = (image)->bits.bits; int stride = (image)->bits.rowstride; int width = (image)->bits.width; line = buf + pixman_fixed_to_int (y) * stride; for (;;) { uint8_t *ap = (uint8_t *) line; pixman_fixed_t lx, rx; int lxi, rxi; /* clip X */ lx = l->x; if (lx < 0) lx = 0; rx = r->x; if (pixman_fixed_to_int (rx) >= width) { /* Use the last pixel of the scanline, covered 100%. * We can't use the first pixel following the scanline, * because accessing it could result in a buffer overrun. */ rx = pixman_int_to_fixed (width) - 1; } /* Skip empty (or backwards) sections */ if (rx > lx) { int lxs, rxs; /* Find pixel bounds for span. */ lxi = pixman_fixed_to_int (lx); rxi = pixman_fixed_to_int (rx); /* Sample coverage for edge pixels */ lxs = RENDER_SAMPLES_X (lx, 8); rxs = RENDER_SAMPLES_X (rx, 8); /* Add coverage across row */ if (lxi == rxi) { WRITE (image, ap + lxi, clip255 (READ (image, ap + lxi) + rxs - lxs)); } else { WRITE (image, ap + lxi, clip255 (READ (image, ap + lxi) + N_X_FRAC (8) - lxs)); /* Move forward so that lxi/rxi is the pixel span */ lxi++; /* Don't bother trying to optimize the fill unless * the span is longer than 4 pixels. */ if (rxi - lxi > 4) { if (fill_start < 0) { fill_start = lxi; fill_end = rxi; fill_size++; } else { if (lxi >= fill_end || rxi < fill_start) { /* We're beyond what we saved, just fill it */ ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); fill_start = lxi; fill_end = rxi; fill_size = 1; } else { /* Update fill_start */ if (lxi > fill_start) { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), lxi - fill_start); fill_start = lxi; } else if (lxi < fill_start) { ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), fill_start - lxi); } /* Update fill_end */ if (rxi < fill_end) { ADD_SATURATE_8 (ap + rxi, fill_size * N_X_FRAC (8), fill_end - rxi); fill_end = rxi; } else if (fill_end < rxi) { ADD_SATURATE_8 (ap + fill_end, N_X_FRAC (8), rxi - fill_end); } fill_size++; } } } else { ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), rxi - lxi); } WRITE (image, ap + rxi, clip255 (READ (image, ap + rxi) + rxs)); } } if (y == b) { /* We're done, make sure we clean up any remaining fill. */ if (fill_start != fill_end) { if (fill_size == N_Y_FRAC (8)) { MEMSET_WRAPPED (image, ap + fill_start, 0xff, fill_end - fill_start); } else { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); } } break; } if (pixman_fixed_frac (y) != Y_FRAC_LAST (8)) { RENDER_EDGE_STEP_SMALL (l); RENDER_EDGE_STEP_SMALL (r); y += STEP_Y_SMALL (8); } else { RENDER_EDGE_STEP_BIG (l); RENDER_EDGE_STEP_BIG (r); y += STEP_Y_BIG (8); if (fill_start != fill_end) { if (fill_size == N_Y_FRAC (8)) { MEMSET_WRAPPED (image, ap + fill_start, 0xff, fill_end - fill_start); } else { ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), fill_end - fill_start); } fill_start = fill_end = -1; fill_size = 0; } line += stride; } } } #ifndef PIXMAN_FB_ACCESSORS static #endif void PIXMAN_RASTERIZE_EDGES (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { switch (PIXMAN_FORMAT_BPP (image->bits.format)) { case 1: rasterize_edges_1 (image, l, r, t, b); break; case 4: rasterize_edges_4 (image, l, r, t, b); break; case 8: rasterize_edges_8 (image, l, r, t, b); break; default: break; } } #ifndef PIXMAN_FB_ACCESSORS PIXMAN_EXPORT void pixman_rasterize_edges (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b) { return_if_fail (image->type == BITS); return_if_fail (PIXMAN_FORMAT_TYPE (image->bits.format) == PIXMAN_TYPE_A); if (image->bits.read_func || image->bits.write_func) pixman_rasterize_edges_accessors (image, l, r, t, b); else pixman_rasterize_edges_no_accessors (image, l, r, t, b); } #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-fast-path.c000066400000000000000000002111641271037650300265360ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" #include "pixman-combine32.h" #include "pixman-inlines.h" static force_inline uint32_t fetch_24 (uint8_t *a) { if (((uintptr_t)a) & 1) { #ifdef WORDS_BIGENDIAN return (*a << 16) | (*(uint16_t *)(a + 1)); #else return *a | (*(uint16_t *)(a + 1) << 8); #endif } else { #ifdef WORDS_BIGENDIAN return (*(uint16_t *)a << 8) | *(a + 2); #else return *(uint16_t *)a | (*(a + 2) << 16); #endif } } static force_inline void store_24 (uint8_t *a, uint32_t v) { if (((uintptr_t)a) & 1) { #ifdef WORDS_BIGENDIAN *a = (uint8_t) (v >> 16); *(uint16_t *)(a + 1) = (uint16_t) (v); #else *a = (uint8_t) (v); *(uint16_t *)(a + 1) = (uint16_t) (v >> 8); #endif } else { #ifdef WORDS_BIGENDIAN *(uint16_t *)a = (uint16_t)(v >> 8); *(a + 2) = (uint8_t)v; #else *(uint16_t *)a = (uint16_t)v; *(a + 2) = (uint8_t)(v >> 16); #endif } } static force_inline uint32_t over (uint32_t src, uint32_t dest) { uint32_t a = ~src >> 24; UN8x4_MUL_UN8_ADD_UN8x4 (dest, a, src); return dest; } static force_inline uint32_t in (uint32_t x, uint8_t y) { uint16_t a = y; UN8x4_MUL_UN8 (x, a); return x; } /* * Naming convention: * * op_src_mask_dest */ static void fast_composite_over_x888_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *src, *src_line; uint32_t *dst, *dst_line; uint8_t *mask, *mask_line; int src_stride, mask_stride, dst_stride; uint8_t m; uint32_t s, d; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { src = src_line; src_line += src_stride; dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; if (m) { s = *src | 0xff000000; if (m == 0xff) { *dst = s; } else { d = in (s, m); *dst = over (d, *dst); } } src++; dst++; } } } static void fast_composite_in_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint8_t *dst_line, *dst; uint8_t *mask_line, *mask, m; int dst_stride, mask_stride; int32_t w; uint16_t t; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); if (srca == 0xff) { while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; if (m == 0) *dst = 0; else if (m != 0xff) *dst = MUL_UN8 (m, *dst, t); dst++; } } } else { while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; m = MUL_UN8 (m, srca, t); if (m == 0) *dst = 0; else if (m != 0xff) *dst = MUL_UN8 (m, *dst, t); dst++; } } } } static void fast_composite_in_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int dst_stride, src_stride; int32_t w; uint8_t s; uint16_t t; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; if (s == 0) *dst = 0; else if (s != 0xff) *dst = MUL_UN8 (s, *dst, t); dst++; } } } static void fast_composite_over_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst_line, *dst, d; uint8_t *mask_line, *mask, m; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; if (m == 0xff) { if (srca == 0xff) *dst = src; else *dst = over (src, *dst); } else if (m) { d = in (src, m); *dst = over (d, *dst); } dst++; } } } static void fast_composite_add_n_8888_8888_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, s; uint32_t *dst_line, *dst, d; uint32_t *mask_line, *mask, ma; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { ma = *mask++; if (ma) { d = *dst; s = src; UN8x4_MUL_UN8x4_ADD_UN8x4 (s, ma, d); *dst = s; } dst++; } } } static void fast_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca, s; uint32_t *dst_line, *dst, d; uint32_t *mask_line, *mask, ma; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { ma = *mask++; if (ma == 0xffffffff) { if (srca == 0xff) *dst = src; else *dst = over (src, *dst); } else if (ma) { d = *dst; s = src; UN8x4_MUL_UN8x4 (s, ma); UN8x4_MUL_UN8 (ma, srca); ma = ~ma; UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ma, s); *dst = d; } dst++; } } } static void fast_composite_over_n_8_0888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint8_t *dst_line, *dst; uint32_t d; uint8_t *mask_line, *mask, m; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 3); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; if (m == 0xff) { if (srca == 0xff) { d = src; } else { d = fetch_24 (dst); d = over (src, d); } store_24 (dst, d); } else if (m) { d = over (in (src, m), fetch_24 (dst)); store_24 (dst, d); } dst += 3; } } } static void fast_composite_over_n_8_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint16_t *dst_line, *dst; uint32_t d; uint8_t *mask_line, *mask, m; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { m = *mask++; if (m == 0xff) { if (srca == 0xff) { d = src; } else { d = *dst; d = over (src, convert_0565_to_0888 (d)); } *dst = convert_8888_to_0565 (d); } else if (m) { d = *dst; d = over (in (src, m), convert_0565_to_0888 (d)); *dst = convert_8888_to_0565 (d); } dst++; } } } static void fast_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca, s; uint16_t src16; uint16_t *dst_line, *dst; uint32_t d; uint32_t *mask_line, *mask, ma; int dst_stride, mask_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; src16 = convert_8888_to_0565 (src); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { ma = *mask++; if (ma == 0xffffffff) { if (srca == 0xff) { *dst = src16; } else { d = *dst; d = over (src, convert_0565_to_0888 (d)); *dst = convert_8888_to_0565 (d); } } else if (ma) { d = *dst; d = convert_0565_to_0888 (d); s = src; UN8x4_MUL_UN8x4 (s, ma); UN8x4_MUL_UN8 (ma, srca); ma = ~ma; UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ma, s); *dst = convert_8888_to_0565 (d); } dst++; } } } static void fast_composite_over_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src, s; int dst_stride, src_stride; uint8_t a; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; a = s >> 24; if (a == 0xff) *dst = s; else if (s) *dst = over (s, *dst); dst++; } } } static void fast_composite_src_x888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) *dst++ = (*src++) | 0xff000000; } } #if 0 static void fast_composite_over_8888_0888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint32_t d; uint32_t *src_line, *src, s; uint8_t a; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 3); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; a = s >> 24; if (a) { if (a == 0xff) d = s; else d = over (s, fetch_24 (dst)); store_24 (dst, d); } dst += 3; } } } #endif static void fast_composite_over_8888_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t d; uint32_t *src_line, *src, s; uint8_t a; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; a = s >> 24; if (s) { if (a == 0xff) { d = s; } else { d = *dst; d = over (s, convert_0565_to_0888 (d)); } *dst = convert_8888_to_0565 (d); } dst++; } } } static void fast_composite_add_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int dst_stride, src_stride; int32_t w; uint8_t s, d; uint16_t t; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; if (s) { if (s != 0xff) { d = *dst; t = d + s; s = t | (0 - (t >> 8)); } *dst = s; } dst++; } } } static void fast_composite_add_0565_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t d; uint16_t *src_line, *src; uint32_t s; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint16_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; if (s) { d = *dst; s = convert_0565_to_8888 (s); if (d) { d = convert_0565_to_8888 (d); UN8x4_ADD_UN8x4 (s, d); } *dst = convert_8888_to_0565 (s); } dst++; } } } static void fast_composite_add_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; uint32_t s, d; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; if (s) { if (s != 0xffffffff) { d = *dst; if (d) UN8x4_ADD_UN8x4 (s, d); } *dst = s; } dst++; } } } static void fast_composite_add_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t src; uint8_t sa; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); sa = (src >> 24); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { uint16_t tmp; uint16_t a; uint32_t m, d; uint32_t r; a = *mask++; d = *dst; m = MUL_UN8 (sa, a, tmp); r = ADD_UN8 (m, d, tmp); *dst++ = r; } } } #ifdef WORDS_BIGENDIAN #define CREATE_BITMASK(n) (0x80000000 >> (n)) #define UPDATE_BITMASK(n) ((n) >> 1) #else #define CREATE_BITMASK(n) (1 << (n)) #define UPDATE_BITMASK(n) ((n) << 1) #endif #define TEST_BIT(p, n) \ (*((p) + ((n) >> 5)) & CREATE_BITMASK ((n) & 31)) #define SET_BIT(p, n) \ do { *((p) + ((n) >> 5)) |= CREATE_BITMASK ((n) & 31); } while (0); static void fast_composite_add_1_1 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (src_image, 0, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, 0, dest_y, uint32_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { /* * TODO: improve performance by processing uint32_t data instead * of individual bits */ if (TEST_BIT (src, src_x + w)) SET_BIT (dst, dest_x + w); } } } static void fast_composite_over_n_1_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst, *dst_line; uint32_t *mask, *mask_line; int mask_stride, dst_stride; uint32_t bitcache, bitmask; int32_t w; if (width <= 0) return; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, 0, mask_y, uint32_t, mask_stride, mask_line, 1); mask_line += mask_x >> 5; if (srca == 0xff) { while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; bitcache = *mask++; bitmask = CREATE_BITMASK (mask_x & 31); while (w--) { if (bitmask == 0) { bitcache = *mask++; bitmask = CREATE_BITMASK (0); } if (bitcache & bitmask) *dst = src; bitmask = UPDATE_BITMASK (bitmask); dst++; } } } else { while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; bitcache = *mask++; bitmask = CREATE_BITMASK (mask_x & 31); while (w--) { if (bitmask == 0) { bitcache = *mask++; bitmask = CREATE_BITMASK (0); } if (bitcache & bitmask) *dst = over (src, *dst); bitmask = UPDATE_BITMASK (bitmask); dst++; } } } } static void fast_composite_over_n_1_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint16_t *dst, *dst_line; uint32_t *mask, *mask_line; int mask_stride, dst_stride; uint32_t bitcache, bitmask; int32_t w; uint32_t d; uint16_t src565; if (width <= 0) return; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, 0, mask_y, uint32_t, mask_stride, mask_line, 1); mask_line += mask_x >> 5; if (srca == 0xff) { src565 = convert_8888_to_0565 (src); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; bitcache = *mask++; bitmask = CREATE_BITMASK (mask_x & 31); while (w--) { if (bitmask == 0) { bitcache = *mask++; bitmask = CREATE_BITMASK (0); } if (bitcache & bitmask) *dst = src565; bitmask = UPDATE_BITMASK (bitmask); dst++; } } } else { while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; bitcache = *mask++; bitmask = CREATE_BITMASK (mask_x & 31); while (w--) { if (bitmask == 0) { bitcache = *mask++; bitmask = CREATE_BITMASK (0); } if (bitcache & bitmask) { d = over (src, convert_0565_to_0888 (*dst)); *dst = convert_8888_to_0565 (d); } bitmask = UPDATE_BITMASK (bitmask); dst++; } } } } /* * Simple bitblt */ static void fast_composite_solid_fill (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (dest_image->bits.format == PIXMAN_a1) { src = src >> 31; } else if (dest_image->bits.format == PIXMAN_a8) { src = src >> 24; } else if (dest_image->bits.format == PIXMAN_r5g6b5 || dest_image->bits.format == PIXMAN_b5g6r5) { src = convert_8888_to_0565 (src); } pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, PIXMAN_FORMAT_BPP (dest_image->bits.format), dest_x, dest_y, width, height, src); } static void fast_composite_src_memcpy (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); int bpp = PIXMAN_FORMAT_BPP (dest_image->bits.format) / 8; uint32_t n_bytes = width * bpp; int dst_stride, src_stride; uint8_t *dst; uint8_t *src; src_stride = src_image->bits.rowstride * 4; dst_stride = dest_image->bits.rowstride * 4; src = (uint8_t *)src_image->bits.bits + src_y * src_stride + src_x * bpp; dst = (uint8_t *)dest_image->bits.bits + dest_y * dst_stride + dest_x * bpp; while (height--) { memcpy (dst, src, n_bytes); dst += dst_stride; src += src_stride; } } FAST_NEAREST (8888_8888_cover, 8888, 8888, uint32_t, uint32_t, SRC, COVER) FAST_NEAREST (8888_8888_none, 8888, 8888, uint32_t, uint32_t, SRC, NONE) FAST_NEAREST (8888_8888_pad, 8888, 8888, uint32_t, uint32_t, SRC, PAD) FAST_NEAREST (8888_8888_normal, 8888, 8888, uint32_t, uint32_t, SRC, NORMAL) FAST_NEAREST (x888_8888_cover, x888, 8888, uint32_t, uint32_t, SRC, COVER) FAST_NEAREST (x888_8888_pad, x888, 8888, uint32_t, uint32_t, SRC, PAD) FAST_NEAREST (x888_8888_normal, x888, 8888, uint32_t, uint32_t, SRC, NORMAL) FAST_NEAREST (8888_8888_cover, 8888, 8888, uint32_t, uint32_t, OVER, COVER) FAST_NEAREST (8888_8888_none, 8888, 8888, uint32_t, uint32_t, OVER, NONE) FAST_NEAREST (8888_8888_pad, 8888, 8888, uint32_t, uint32_t, OVER, PAD) FAST_NEAREST (8888_8888_normal, 8888, 8888, uint32_t, uint32_t, OVER, NORMAL) FAST_NEAREST (8888_565_cover, 8888, 0565, uint32_t, uint16_t, SRC, COVER) FAST_NEAREST (8888_565_none, 8888, 0565, uint32_t, uint16_t, SRC, NONE) FAST_NEAREST (8888_565_pad, 8888, 0565, uint32_t, uint16_t, SRC, PAD) FAST_NEAREST (8888_565_normal, 8888, 0565, uint32_t, uint16_t, SRC, NORMAL) FAST_NEAREST (565_565_normal, 0565, 0565, uint16_t, uint16_t, SRC, NORMAL) FAST_NEAREST (8888_565_cover, 8888, 0565, uint32_t, uint16_t, OVER, COVER) FAST_NEAREST (8888_565_none, 8888, 0565, uint32_t, uint16_t, OVER, NONE) FAST_NEAREST (8888_565_pad, 8888, 0565, uint32_t, uint16_t, OVER, PAD) FAST_NEAREST (8888_565_normal, 8888, 0565, uint32_t, uint16_t, OVER, NORMAL) #define REPEAT_MIN_WIDTH 32 static void fast_composite_tiled_repeat (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); pixman_composite_func_t func; pixman_format_code_t mask_format; uint32_t src_flags, mask_flags; int32_t sx, sy; int32_t width_remain; int32_t num_pixels; int32_t src_width; int32_t i, j; pixman_image_t extended_src_image; uint32_t extended_src[REPEAT_MIN_WIDTH * 2]; pixman_bool_t need_src_extension; uint32_t *src_line; int32_t src_stride; int32_t src_bpp; pixman_composite_info_t info2 = *info; src_flags = (info->src_flags & ~FAST_PATH_NORMAL_REPEAT) | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; if (mask_image) { mask_format = mask_image->common.extended_format_code; mask_flags = info->mask_flags; } else { mask_format = PIXMAN_null; mask_flags = FAST_PATH_IS_OPAQUE; } _pixman_implementation_lookup_composite ( imp->toplevel, info->op, src_image->common.extended_format_code, src_flags, mask_format, mask_flags, dest_image->common.extended_format_code, info->dest_flags, &imp, &func); src_bpp = PIXMAN_FORMAT_BPP (src_image->bits.format); if (src_image->bits.width < REPEAT_MIN_WIDTH && (src_bpp == 32 || src_bpp == 16 || src_bpp == 8) && !src_image->bits.indexed) { sx = src_x; sx = MOD (sx, src_image->bits.width); sx += width; src_width = 0; while (src_width < REPEAT_MIN_WIDTH && src_width <= sx) src_width += src_image->bits.width; src_stride = (src_width * (src_bpp >> 3) + 3) / (int) sizeof (uint32_t); /* Initialize/validate stack-allocated temporary image */ _pixman_bits_image_init (&extended_src_image, src_image->bits.format, src_width, 1, &extended_src[0], src_stride, FALSE); _pixman_image_validate (&extended_src_image); info2.src_image = &extended_src_image; need_src_extension = TRUE; } else { src_width = src_image->bits.width; need_src_extension = FALSE; } sx = src_x; sy = src_y; while (--height >= 0) { sx = MOD (sx, src_width); sy = MOD (sy, src_image->bits.height); if (need_src_extension) { if (src_bpp == 32) { PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint32_t, src_stride, src_line, 1); for (i = 0; i < src_width; ) { for (j = 0; j < src_image->bits.width; j++, i++) extended_src[i] = src_line[j]; } } else if (src_bpp == 16) { uint16_t *src_line_16; PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint16_t, src_stride, src_line_16, 1); src_line = (uint32_t*)src_line_16; for (i = 0; i < src_width; ) { for (j = 0; j < src_image->bits.width; j++, i++) ((uint16_t*)extended_src)[i] = ((uint16_t*)src_line)[j]; } } else if (src_bpp == 8) { uint8_t *src_line_8; PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint8_t, src_stride, src_line_8, 1); src_line = (uint32_t*)src_line_8; for (i = 0; i < src_width; ) { for (j = 0; j < src_image->bits.width; j++, i++) ((uint8_t*)extended_src)[i] = ((uint8_t*)src_line)[j]; } } info2.src_y = 0; } else { info2.src_y = sy; } width_remain = width; while (width_remain > 0) { num_pixels = src_width - sx; if (num_pixels > width_remain) num_pixels = width_remain; info2.src_x = sx; info2.width = num_pixels; info2.height = 1; func (imp, &info2); width_remain -= num_pixels; info2.mask_x += num_pixels; info2.dest_x += num_pixels; sx = 0; } sx = src_x; sy++; info2.mask_x = info->mask_x; info2.mask_y++; info2.dest_x = info->dest_x; info2.dest_y++; } if (need_src_extension) _pixman_image_fini (&extended_src_image); } /* Use more unrolling for src_0565_0565 because it is typically CPU bound */ static force_inline void scaled_nearest_scanline_565_565_SRC (uint16_t * dst, const uint16_t * src, int32_t w, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t fully_transparent_src) { uint16_t tmp1, tmp2, tmp3, tmp4; while ((w -= 4) >= 0) { tmp1 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; tmp2 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; tmp3 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; tmp4 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; *dst++ = tmp1; *dst++ = tmp2; *dst++ = tmp3; *dst++ = tmp4; } if (w & 2) { tmp1 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; tmp2 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; *dst++ = tmp1; *dst++ = tmp2; } if (w & 1) *dst = *(src + pixman_fixed_to_int (vx)); } FAST_NEAREST_MAINLOOP (565_565_cover_SRC, scaled_nearest_scanline_565_565_SRC, uint16_t, uint16_t, COVER) FAST_NEAREST_MAINLOOP (565_565_none_SRC, scaled_nearest_scanline_565_565_SRC, uint16_t, uint16_t, NONE) FAST_NEAREST_MAINLOOP (565_565_pad_SRC, scaled_nearest_scanline_565_565_SRC, uint16_t, uint16_t, PAD) static force_inline uint32_t fetch_nearest (pixman_repeat_t src_repeat, pixman_format_code_t format, uint32_t *src, int x, int src_width) { if (repeat (src_repeat, &x, src_width)) { if (format == PIXMAN_x8r8g8b8 || format == PIXMAN_x8b8g8r8) return *(src + x) | 0xff000000; else return *(src + x); } else { return 0; } } static force_inline void combine_over (uint32_t s, uint32_t *dst) { if (s) { uint8_t ia = 0xff - (s >> 24); if (ia) UN8x4_MUL_UN8_ADD_UN8x4 (*dst, ia, s); else *dst = s; } } static force_inline void combine_src (uint32_t s, uint32_t *dst) { *dst = s; } static void fast_composite_scaled_nearest (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line; uint32_t *src_line; int dst_stride, src_stride; int src_width, src_height; pixman_repeat_t src_repeat; pixman_fixed_t unit_x, unit_y; pixman_format_code_t src_format; pixman_vector_t v; pixman_fixed_t vy; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); /* pass in 0 instead of src_x and src_y because src_x and src_y need to be * transformed from destination space to source space */ PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, uint32_t, src_stride, src_line, 1); /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point_3d (src_image->common.transform, &v)) return; unit_x = src_image->common.transform->matrix[0][0]; unit_y = src_image->common.transform->matrix[1][1]; /* Round down to closest integer, ensuring that 0.5 rounds to 0, not 1 */ v.vector[0] -= pixman_fixed_e; v.vector[1] -= pixman_fixed_e; src_height = src_image->bits.height; src_width = src_image->bits.width; src_repeat = src_image->common.repeat; src_format = src_image->bits.format; vy = v.vector[1]; while (height--) { pixman_fixed_t vx = v.vector[0]; int y = pixman_fixed_to_int (vy); uint32_t *dst = dst_line; dst_line += dst_stride; /* adjust the y location by a unit vector in the y direction * this is equivalent to transforming y+1 of the destination point to source space */ vy += unit_y; if (!repeat (src_repeat, &y, src_height)) { if (op == PIXMAN_OP_SRC) memset (dst, 0, sizeof (*dst) * width); } else { int w = width; uint32_t *src = src_line + y * src_stride; while (w >= 2) { uint32_t s1, s2; int x1, x2; x1 = pixman_fixed_to_int (vx); vx += unit_x; x2 = pixman_fixed_to_int (vx); vx += unit_x; w -= 2; s1 = fetch_nearest (src_repeat, src_format, src, x1, src_width); s2 = fetch_nearest (src_repeat, src_format, src, x2, src_width); if (op == PIXMAN_OP_OVER) { combine_over (s1, dst++); combine_over (s2, dst++); } else { combine_src (s1, dst++); combine_src (s2, dst++); } } while (w--) { uint32_t s; int x; x = pixman_fixed_to_int (vx); vx += unit_x; s = fetch_nearest (src_repeat, src_format, src, x, src_width); if (op == PIXMAN_OP_OVER) combine_over (s, dst++); else combine_src (s, dst++); } } } } #define CACHE_LINE_SIZE 64 #define FAST_SIMPLE_ROTATE(suffix, pix_type) \ \ static void \ blt_rotated_90_trivial_##suffix (pix_type *dst, \ int dst_stride, \ const pix_type *src, \ int src_stride, \ int w, \ int h) \ { \ int x, y; \ for (y = 0; y < h; y++) \ { \ const pix_type *s = src + (h - y - 1); \ pix_type *d = dst + dst_stride * y; \ for (x = 0; x < w; x++) \ { \ *d++ = *s; \ s += src_stride; \ } \ } \ } \ \ static void \ blt_rotated_270_trivial_##suffix (pix_type *dst, \ int dst_stride, \ const pix_type *src, \ int src_stride, \ int w, \ int h) \ { \ int x, y; \ for (y = 0; y < h; y++) \ { \ const pix_type *s = src + src_stride * (w - 1) + y; \ pix_type *d = dst + dst_stride * y; \ for (x = 0; x < w; x++) \ { \ *d++ = *s; \ s -= src_stride; \ } \ } \ } \ \ static void \ blt_rotated_90_##suffix (pix_type *dst, \ int dst_stride, \ const pix_type *src, \ int src_stride, \ int W, \ int H) \ { \ int x; \ int leading_pixels = 0, trailing_pixels = 0; \ const int TILE_SIZE = CACHE_LINE_SIZE / sizeof(pix_type); \ \ /* \ * split processing into handling destination as TILE_SIZExH cache line \ * aligned vertical stripes (optimistically assuming that destination \ * stride is a multiple of cache line, if not - it will be just a bit \ * slower) \ */ \ \ if ((uintptr_t)dst & (CACHE_LINE_SIZE - 1)) \ { \ leading_pixels = TILE_SIZE - (((uintptr_t)dst & \ (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ if (leading_pixels > W) \ leading_pixels = W; \ \ /* unaligned leading part NxH (where N < TILE_SIZE) */ \ blt_rotated_90_trivial_##suffix ( \ dst, \ dst_stride, \ src, \ src_stride, \ leading_pixels, \ H); \ \ dst += leading_pixels; \ src += leading_pixels * src_stride; \ W -= leading_pixels; \ } \ \ if ((uintptr_t)(dst + W) & (CACHE_LINE_SIZE - 1)) \ { \ trailing_pixels = (((uintptr_t)(dst + W) & \ (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ if (trailing_pixels > W) \ trailing_pixels = W; \ W -= trailing_pixels; \ } \ \ for (x = 0; x < W; x += TILE_SIZE) \ { \ /* aligned middle part TILE_SIZExH */ \ blt_rotated_90_trivial_##suffix ( \ dst + x, \ dst_stride, \ src + src_stride * x, \ src_stride, \ TILE_SIZE, \ H); \ } \ \ if (trailing_pixels) \ { \ /* unaligned trailing part NxH (where N < TILE_SIZE) */ \ blt_rotated_90_trivial_##suffix ( \ dst + W, \ dst_stride, \ src + W * src_stride, \ src_stride, \ trailing_pixels, \ H); \ } \ } \ \ static void \ blt_rotated_270_##suffix (pix_type *dst, \ int dst_stride, \ const pix_type *src, \ int src_stride, \ int W, \ int H) \ { \ int x; \ int leading_pixels = 0, trailing_pixels = 0; \ const int TILE_SIZE = CACHE_LINE_SIZE / sizeof(pix_type); \ \ /* \ * split processing into handling destination as TILE_SIZExH cache line \ * aligned vertical stripes (optimistically assuming that destination \ * stride is a multiple of cache line, if not - it will be just a bit \ * slower) \ */ \ \ if ((uintptr_t)dst & (CACHE_LINE_SIZE - 1)) \ { \ leading_pixels = TILE_SIZE - (((uintptr_t)dst & \ (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ if (leading_pixels > W) \ leading_pixels = W; \ \ /* unaligned leading part NxH (where N < TILE_SIZE) */ \ blt_rotated_270_trivial_##suffix ( \ dst, \ dst_stride, \ src + src_stride * (W - leading_pixels), \ src_stride, \ leading_pixels, \ H); \ \ dst += leading_pixels; \ W -= leading_pixels; \ } \ \ if ((uintptr_t)(dst + W) & (CACHE_LINE_SIZE - 1)) \ { \ trailing_pixels = (((uintptr_t)(dst + W) & \ (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ if (trailing_pixels > W) \ trailing_pixels = W; \ W -= trailing_pixels; \ src += trailing_pixels * src_stride; \ } \ \ for (x = 0; x < W; x += TILE_SIZE) \ { \ /* aligned middle part TILE_SIZExH */ \ blt_rotated_270_trivial_##suffix ( \ dst + x, \ dst_stride, \ src + src_stride * (W - x - TILE_SIZE), \ src_stride, \ TILE_SIZE, \ H); \ } \ \ if (trailing_pixels) \ { \ /* unaligned trailing part NxH (where N < TILE_SIZE) */ \ blt_rotated_270_trivial_##suffix ( \ dst + W, \ dst_stride, \ src - trailing_pixels * src_stride, \ src_stride, \ trailing_pixels, \ H); \ } \ } \ \ static void \ fast_composite_rotate_90_##suffix (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ pix_type *dst_line; \ pix_type *src_line; \ int dst_stride, src_stride; \ int src_x_t, src_y_t; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, pix_type, \ dst_stride, dst_line, 1); \ src_x_t = -src_y + pixman_fixed_to_int ( \ src_image->common.transform->matrix[0][2] + \ pixman_fixed_1 / 2 - pixman_fixed_e) - height;\ src_y_t = src_x + pixman_fixed_to_int ( \ src_image->common.transform->matrix[1][2] + \ pixman_fixed_1 / 2 - pixman_fixed_e); \ PIXMAN_IMAGE_GET_LINE (src_image, src_x_t, src_y_t, pix_type, \ src_stride, src_line, 1); \ blt_rotated_90_##suffix (dst_line, dst_stride, src_line, src_stride, \ width, height); \ } \ \ static void \ fast_composite_rotate_270_##suffix (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ pix_type *dst_line; \ pix_type *src_line; \ int dst_stride, src_stride; \ int src_x_t, src_y_t; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, pix_type, \ dst_stride, dst_line, 1); \ src_x_t = src_y + pixman_fixed_to_int ( \ src_image->common.transform->matrix[0][2] + \ pixman_fixed_1 / 2 - pixman_fixed_e); \ src_y_t = -src_x + pixman_fixed_to_int ( \ src_image->common.transform->matrix[1][2] + \ pixman_fixed_1 / 2 - pixman_fixed_e) - width; \ PIXMAN_IMAGE_GET_LINE (src_image, src_x_t, src_y_t, pix_type, \ src_stride, src_line, 1); \ blt_rotated_270_##suffix (dst_line, dst_stride, src_line, src_stride, \ width, height); \ } FAST_SIMPLE_ROTATE (8, uint8_t) FAST_SIMPLE_ROTATE (565, uint16_t) FAST_SIMPLE_ROTATE (8888, uint32_t) static const pixman_fast_path_t c_fast_paths[] = { PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, fast_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, fast_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, r8g8b8, fast_composite_over_n_8_0888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b8g8r8, fast_composite_over_n_8_0888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, fast_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, fast_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, fast_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, fast_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a1, a8r8g8b8, fast_composite_over_n_1_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a1, x8r8g8b8, fast_composite_over_n_1_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a1, a8b8g8r8, fast_composite_over_n_1_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a1, x8b8g8r8, fast_composite_over_n_1_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a1, r5g6b5, fast_composite_over_n_1_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a1, b5g6r5, fast_composite_over_n_1_0565), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, fast_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, fast_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, fast_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, fast_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, fast_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, fast_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, fast_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, fast_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, fast_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, fast_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, fast_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, fast_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, fast_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, fast_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, fast_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, fast_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (ADD, r5g6b5, null, r5g6b5, fast_composite_add_0565_0565), PIXMAN_STD_FAST_PATH (ADD, b5g6r5, null, b5g6r5, fast_composite_add_0565_0565), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, fast_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, fast_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, fast_composite_add_8_8), PIXMAN_STD_FAST_PATH (ADD, a1, null, a1, fast_composite_add_1_1), PIXMAN_STD_FAST_PATH_CA (ADD, solid, a8r8g8b8, a8r8g8b8, fast_composite_add_n_8888_8888_ca), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, fast_composite_add_n_8_8), PIXMAN_STD_FAST_PATH (SRC, solid, null, a8r8g8b8, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, x8r8g8b8, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, a8b8g8r8, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, x8b8g8r8, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, a1, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, a8, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, solid, null, r5g6b5, fast_composite_solid_fill), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, fast_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, fast_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, b8g8r8a8, null, b8g8r8x8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, b8g8r8a8, null, b8g8r8a8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, b8g8r8x8, null, b8g8r8x8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, b8g8r8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, x1r5g5b5, null, x1r5g5b5, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, x1r5g5b5, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (SRC, a8, null, a8, fast_composite_src_memcpy), PIXMAN_STD_FAST_PATH (IN, a8, null, a8, fast_composite_in_8_8), PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, fast_composite_in_n_8_8), SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, r5g6b5, 8888_565), SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, r5g6b5, 8888_565), SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, r5g6b5, 565_565), SIMPLE_NEAREST_FAST_PATH_COVER (SRC, x8r8g8b8, a8r8g8b8, x888_8888), SIMPLE_NEAREST_FAST_PATH_COVER (SRC, x8b8g8r8, a8b8g8r8, x888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, x8r8g8b8, a8r8g8b8, x888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, x8b8g8r8, a8b8g8r8, x888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (SRC, x8r8g8b8, a8r8g8b8, x888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (SRC, x8b8g8r8, a8b8g8r8, x888_8888), SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, 8888_8888), SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, 8888_565), #define NEAREST_FAST_PATH(op,s,d) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, SCALED_NEAREST_FLAGS, \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest, \ } NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8), NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8), NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8), NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8), NEAREST_FAST_PATH (SRC, x8r8g8b8, a8r8g8b8), NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8), NEAREST_FAST_PATH (SRC, x8b8g8r8, a8b8g8r8), NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8), NEAREST_FAST_PATH (OVER, x8r8g8b8, x8r8g8b8), NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8), NEAREST_FAST_PATH (OVER, x8b8g8r8, x8b8g8r8), NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8), NEAREST_FAST_PATH (OVER, x8r8g8b8, a8r8g8b8), NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8), NEAREST_FAST_PATH (OVER, x8b8g8r8, a8b8g8r8), NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8), #define SIMPLE_ROTATE_FLAGS(angle) \ (FAST_PATH_ROTATE_ ## angle ## _TRANSFORM | \ FAST_PATH_NEAREST_FILTER | \ FAST_PATH_SAMPLES_COVER_CLIP_NEAREST | \ FAST_PATH_STANDARD_FLAGS) #define SIMPLE_ROTATE_FAST_PATH(op,s,d,suffix) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (90), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_rotate_90_##suffix, \ }, \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (270), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_rotate_270_##suffix, \ } SIMPLE_ROTATE_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, 8888), SIMPLE_ROTATE_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, 8888), SIMPLE_ROTATE_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, 8888), SIMPLE_ROTATE_FAST_PATH (SRC, r5g6b5, r5g6b5, 565), SIMPLE_ROTATE_FAST_PATH (SRC, a8, a8, 8), /* Simple repeat fast path entry. */ { PIXMAN_OP_any, PIXMAN_any, (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | FAST_PATH_BITS_IMAGE | FAST_PATH_NORMAL_REPEAT), PIXMAN_any, 0, PIXMAN_any, FAST_PATH_STD_DEST_FLAGS, fast_composite_tiled_repeat }, { PIXMAN_OP_NONE }, }; #ifdef WORDS_BIGENDIAN #define A1_FILL_MASK(n, offs) (((1U << (n)) - 1) << (32 - (offs) - (n))) #else #define A1_FILL_MASK(n, offs) (((1U << (n)) - 1) << (offs)) #endif static force_inline void pixman_fill1_line (uint32_t *dst, int offs, int width, int v) { if (offs) { int leading_pixels = 32 - offs; if (leading_pixels >= width) { if (v) *dst |= A1_FILL_MASK (width, offs); else *dst &= ~A1_FILL_MASK (width, offs); return; } else { if (v) *dst++ |= A1_FILL_MASK (leading_pixels, offs); else *dst++ &= ~A1_FILL_MASK (leading_pixels, offs); width -= leading_pixels; } } while (width >= 32) { if (v) *dst++ = 0xFFFFFFFF; else *dst++ = 0; width -= 32; } if (width > 0) { if (v) *dst |= A1_FILL_MASK (width, 0); else *dst &= ~A1_FILL_MASK (width, 0); } } static void pixman_fill1 (uint32_t *bits, int stride, int x, int y, int width, int height, uint32_t filler) { uint32_t *dst = bits + y * stride + (x >> 5); int offs = x & 31; if (filler & 1) { while (height--) { pixman_fill1_line (dst, offs, width, 1); dst += stride; } } else { while (height--) { pixman_fill1_line (dst, offs, width, 0); dst += stride; } } } static void pixman_fill8 (uint32_t *bits, int stride, int x, int y, int width, int height, uint32_t filler) { int byte_stride = stride * (int) sizeof (uint32_t); uint8_t *dst = (uint8_t *) bits; uint8_t v = filler & 0xff; int i; dst = dst + y * byte_stride + x; while (height--) { for (i = 0; i < width; ++i) dst[i] = v; dst += byte_stride; } } static void pixman_fill16 (uint32_t *bits, int stride, int x, int y, int width, int height, uint32_t filler) { int short_stride = (stride * (int)sizeof (uint32_t)) / (int)sizeof (uint16_t); uint16_t *dst = (uint16_t *)bits; uint16_t v = filler & 0xffff; int i; dst = dst + y * short_stride + x; while (height--) { for (i = 0; i < width; ++i) dst[i] = v; dst += short_stride; } } static void pixman_fill32 (uint32_t *bits, int stride, int x, int y, int width, int height, uint32_t filler) { int i; bits = bits + y * stride + x; while (height--) { for (i = 0; i < width; ++i) bits[i] = filler; bits += stride; } } static pixman_bool_t fast_path_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler) { switch (bpp) { case 1: pixman_fill1 (bits, stride, x, y, width, height, filler); break; case 8: pixman_fill8 (bits, stride, x, y, width, height, filler); break; case 16: pixman_fill16 (bits, stride, x, y, width, height, filler); break; case 32: pixman_fill32 (bits, stride, x, y, width, height, filler); break; default: return FALSE; } return TRUE; } /*****************************************************************************/ static uint32_t * fast_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) { int32_t w = iter->width; uint32_t *dst = iter->buffer; const uint16_t *src = (const uint16_t *)iter->bits; iter->bits += iter->stride; /* Align the source buffer at 4 bytes boundary */ if (w > 0 && ((uintptr_t)src & 3)) { *dst++ = convert_0565_to_8888 (*src++); w--; } /* Process two pixels per iteration */ while ((w -= 2) >= 0) { uint32_t sr, sb, sg, t0, t1; uint32_t s = *(const uint32_t *)src; src += 2; sr = (s >> 8) & 0x00F800F8; sb = (s << 3) & 0x00F800F8; sg = (s >> 3) & 0x00FC00FC; sr |= sr >> 5; sb |= sb >> 5; sg |= sg >> 6; t0 = ((sr << 16) & 0x00FF0000) | ((sg << 8) & 0x0000FF00) | (sb & 0xFF) | 0xFF000000; t1 = (sr & 0x00FF0000) | ((sg >> 8) & 0x0000FF00) | (sb >> 16) | 0xFF000000; #ifdef WORDS_BIGENDIAN *dst++ = t1; *dst++ = t0; #else *dst++ = t0; *dst++ = t1; #endif } if (w & 1) { *dst = convert_0565_to_8888 (*src); } return iter->buffer; } static uint32_t * fast_dest_fetch_noop (pixman_iter_t *iter, const uint32_t *mask) { iter->bits += iter->stride; return iter->buffer; } /* Helper function for a workaround, which tries to ensure that 0x1F001F * constant is always allocated in a register on RISC architectures. */ static force_inline uint32_t convert_8888_to_0565_workaround (uint32_t s, uint32_t x1F001F) { uint32_t a, b; a = (s >> 3) & x1F001F; b = s & 0xFC00; a |= a >> 5; a |= b >> 5; return a; } static void fast_write_back_r5g6b5 (pixman_iter_t *iter) { int32_t w = iter->width; uint16_t *dst = (uint16_t *)(iter->bits - iter->stride); const uint32_t *src = iter->buffer; /* Workaround to ensure that x1F001F variable is allocated in a register */ static volatile uint32_t volatile_x1F001F = 0x1F001F; uint32_t x1F001F = volatile_x1F001F; while ((w -= 4) >= 0) { uint32_t s1 = *src++; uint32_t s2 = *src++; uint32_t s3 = *src++; uint32_t s4 = *src++; *dst++ = convert_8888_to_0565_workaround (s1, x1F001F); *dst++ = convert_8888_to_0565_workaround (s2, x1F001F); *dst++ = convert_8888_to_0565_workaround (s3, x1F001F); *dst++ = convert_8888_to_0565_workaround (s4, x1F001F); } if (w & 2) { *dst++ = convert_8888_to_0565_workaround (*src++, x1F001F); *dst++ = convert_8888_to_0565_workaround (*src++, x1F001F); } if (w & 1) { *dst = convert_8888_to_0565_workaround (*src, x1F001F); } } typedef struct { pixman_format_code_t format; pixman_iter_get_scanline_t get_scanline; pixman_iter_write_back_t write_back; } fetcher_info_t; static const fetcher_info_t fetchers[] = { { PIXMAN_r5g6b5, fast_fetch_r5g6b5, fast_write_back_r5g6b5 }, { PIXMAN_null } }; static pixman_bool_t fast_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; #define FLAGS \ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) if ((iter->iter_flags & ITER_NARROW) && (iter->image_flags & FLAGS) == FLAGS) { const fetcher_info_t *f; for (f = &fetchers[0]; f->format != PIXMAN_null; f++) { if (image->common.extended_format_code == f->format) { uint8_t *b = (uint8_t *)image->bits.bits; int s = image->bits.rowstride * 4; iter->bits = b + s * iter->y + iter->x * PIXMAN_FORMAT_BPP (f->format) / 8; iter->stride = s; iter->get_scanline = f->get_scanline; return TRUE; } } } return FALSE; } static pixman_bool_t fast_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; if ((iter->iter_flags & ITER_NARROW) && (iter->image_flags & FAST_PATH_STD_DEST_FLAGS) == FAST_PATH_STD_DEST_FLAGS) { const fetcher_info_t *f; for (f = &fetchers[0]; f->format != PIXMAN_null; f++) { if (image->common.extended_format_code == f->format) { uint8_t *b = (uint8_t *)image->bits.bits; int s = image->bits.rowstride * 4; iter->bits = b + s * iter->y + iter->x * PIXMAN_FORMAT_BPP (f->format) / 8; iter->stride = s; if ((iter->iter_flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) == (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) { iter->get_scanline = fast_dest_fetch_noop; } else { iter->get_scanline = f->get_scanline; } iter->write_back = f->write_back; return TRUE; } } } return FALSE; } pixman_implementation_t * _pixman_implementation_create_fast_path (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, c_fast_paths); imp->fill = fast_path_fill; imp->src_iter_init = fast_src_iter_init; imp->dest_iter_init = fast_dest_iter_init; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-filter.c000066400000000000000000000214571271037650300261400ustar00rootroot00000000000000/* * Copyright 2012, Red Hat, Inc. * Copyright 2012, Soren Sandmann * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Soren Sandmann */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" typedef double (* kernel_func_t) (double x); typedef struct { pixman_kernel_t kernel; kernel_func_t func; double width; } filter_info_t; static double impulse_kernel (double x) { return (x == 0.0)? 1.0 : 0.0; } static double box_kernel (double x) { return 1; } static double linear_kernel (double x) { return 1 - fabs (x); } static double gaussian_kernel (double x) { #define SQRT2 (1.4142135623730950488016887242096980785696718753769480) #define SIGMA (SQRT2 / 2.0) return exp (- x * x / (2 * SIGMA * SIGMA)) / (SIGMA * sqrt (2.0 * M_PI)); } static double sinc (double x) { if (x == 0.0) return 1.0; else return sin (M_PI * x) / (M_PI * x); } static double lanczos (double x, int n) { return sinc (x) * sinc (x * (1.0 / n)); } static double lanczos2_kernel (double x) { return lanczos (x, 2); } static double lanczos3_kernel (double x) { return lanczos (x, 3); } static double nice_kernel (double x) { return lanczos3_kernel (x * 0.75); } static double general_cubic (double x, double B, double C) { double ax = fabs(x); if (ax < 1) { return ((12 - 9 * B - 6 * C) * ax * ax * ax + (-18 + 12 * B + 6 * C) * ax * ax + (6 - 2 * B)) / 6; } else if (ax >= 1 && ax < 2) { return ((-B - 6 * C) * ax * ax * ax + (6 * B + 30 * C) * ax * ax + (-12 * B - 48 * C) * ax + (8 * B + 24 * C)) / 6; } else { return 0; } } static double cubic_kernel (double x) { /* This is the Mitchell-Netravali filter. * * (0.0, 0.5) would give us the Catmull-Rom spline, * but that one seems to be indistinguishable from Lanczos2. */ return general_cubic (x, 1/3.0, 1/3.0); } static const filter_info_t filters[] = { { PIXMAN_KERNEL_IMPULSE, impulse_kernel, 0.0 }, { PIXMAN_KERNEL_BOX, box_kernel, 1.0 }, { PIXMAN_KERNEL_LINEAR, linear_kernel, 2.0 }, { PIXMAN_KERNEL_CUBIC, cubic_kernel, 4.0 }, { PIXMAN_KERNEL_GAUSSIAN, gaussian_kernel, 6 * SIGMA }, { PIXMAN_KERNEL_LANCZOS2, lanczos2_kernel, 4.0 }, { PIXMAN_KERNEL_LANCZOS3, lanczos3_kernel, 6.0 }, { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel, 8.0 }, }; /* This function scales @kernel2 by @scale, then * aligns @x1 in @kernel1 with @x2 in @kernel2 and * and integrates the product of the kernels across @width. * * This function assumes that the intervals are within * the kernels in question. E.g., the caller must not * try to integrate a linear kernel ouside of [-1:1] */ static double integral (pixman_kernel_t kernel1, double x1, pixman_kernel_t kernel2, double scale, double x2, double width) { /* If the integration interval crosses zero, break it into * two separate integrals. This ensures that filters such * as LINEAR that are not differentiable at 0 will still * integrate properly. */ if (x1 < 0 && x1 + width > 0) { return integral (kernel1, x1, kernel2, scale, x2, - x1) + integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1); } else if (x2 < 0 && x2 + width > 0) { return integral (kernel1, x1, kernel2, scale, x2, - x2) + integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2); } else if (kernel1 == PIXMAN_KERNEL_IMPULSE) { assert (width == 0.0); return filters[kernel2].func (x2 * scale); } else if (kernel2 == PIXMAN_KERNEL_IMPULSE) { assert (width == 0.0); return filters[kernel1].func (x1); } else { /* Integration via Simpson's rule */ #define N_SEGMENTS 128 #define SAMPLE(a1, a2) \ (filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale)) double s = 0.0; double h = width / (double)N_SEGMENTS; int i; s = SAMPLE (x1, x2); for (i = 1; i < N_SEGMENTS; i += 2) { double a1 = x1 + h * i; double a2 = x2 + h * i; s += 2 * SAMPLE (a1, a2); if (i >= 2 && i < N_SEGMENTS - 1) s += 4 * SAMPLE (a1, a2); } s += SAMPLE (x1 + width, x2 + width); return h * s * (1.0 / 3.0); } } static pixman_fixed_t * create_1d_filter (int *width, pixman_kernel_t reconstruct, pixman_kernel_t sample, double scale, int n_phases) { pixman_fixed_t *params, *p; double step; double size; int i; size = scale * filters[sample].width + filters[reconstruct].width; *width = ceil (size); p = params = malloc (*width * n_phases * sizeof (pixman_fixed_t)); if (!params) return NULL; step = 1.0 / n_phases; for (i = 0; i < n_phases; ++i) { double frac = step / 2.0 + i * step; pixman_fixed_t new_total; int x, x1, x2; double total; /* Sample convolution of reconstruction and sampling * filter. See rounding.txt regarding the rounding * and sample positions. */ x1 = ceil (frac - *width / 2.0 - 0.5); x2 = x1 + *width; total = 0; for (x = x1; x < x2; ++x) { double pos = x + 0.5 - frac; double rlow = - filters[reconstruct].width / 2.0; double rhigh = rlow + filters[reconstruct].width; double slow = pos - scale * filters[sample].width / 2.0; double shigh = slow + scale * filters[sample].width; double c = 0.0; double ilow, ihigh; if (rhigh >= slow && rlow <= shigh) { ilow = MAX (slow, rlow); ihigh = MIN (shigh, rhigh); c = integral (reconstruct, ilow, sample, 1.0 / scale, ilow - pos, ihigh - ilow); } total += c; *p++ = (pixman_fixed_t)(c * 65535.0 + 0.5); } /* Normalize */ p -= *width; total = 1 / total; new_total = 0; for (x = x1; x < x2; ++x) { pixman_fixed_t t = (*p) * total + 0.5; new_total += t; *p++ = t; } if (new_total != pixman_fixed_1) *(p - *width / 2) += (pixman_fixed_1 - new_total); } return params; } /* Create the parameter list for a SEPARABLE_CONVOLUTION filter * with the given kernels and scale parameters */ PIXMAN_EXPORT pixman_fixed_t * pixman_filter_create_separable_convolution (int *n_values, pixman_fixed_t scale_x, pixman_fixed_t scale_y, pixman_kernel_t reconstruct_x, pixman_kernel_t reconstruct_y, pixman_kernel_t sample_x, pixman_kernel_t sample_y, int subsample_bits_x, int subsample_bits_y) { double sx = fabs (pixman_fixed_to_double (scale_x)); double sy = fabs (pixman_fixed_to_double (scale_y)); pixman_fixed_t *horz = NULL, *vert = NULL, *params = NULL; int subsample_x, subsample_y; int width, height; subsample_x = (1 << subsample_bits_x); subsample_y = (1 << subsample_bits_y); horz = create_1d_filter (&width, reconstruct_x, sample_x, sx, subsample_x); vert = create_1d_filter (&height, reconstruct_y, sample_y, sy, subsample_y); if (!horz || !vert) goto out; *n_values = 4 + width * subsample_x + height * subsample_y; params = malloc (*n_values * sizeof (pixman_fixed_t)); if (!params) goto out; params[0] = pixman_int_to_fixed (width); params[1] = pixman_int_to_fixed (height); params[2] = pixman_int_to_fixed (subsample_bits_x); params[3] = pixman_int_to_fixed (subsample_bits_y); memcpy (params + 4, horz, width * subsample_x * sizeof (pixman_fixed_t)); memcpy (params + 4 + width * subsample_x, vert, height * subsample_y * sizeof (pixman_fixed_t)); out: free (horz); free (vert); return params; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-general.c000066400000000000000000000162571271037650300262720ustar00rootroot00000000000000/* * Copyright © 2009 Red Hat, Inc. * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * 2008 Aaron Plattner, NVIDIA Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "pixman-private.h" static pixman_bool_t general_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; if (image->type == LINEAR) _pixman_linear_gradient_iter_init (image, iter); else if (image->type == RADIAL) _pixman_radial_gradient_iter_init (image, iter); else if (image->type == CONICAL) _pixman_conical_gradient_iter_init (image, iter); else if (image->type == BITS) _pixman_bits_image_src_iter_init (image, iter); else if (image->type == SOLID) _pixman_log_error (FUNC, "Solid image not handled by noop"); else _pixman_log_error (FUNC, "Pixman bug: unknown image type\n"); return TRUE; } static pixman_bool_t general_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { if (iter->image->type == BITS) { _pixman_bits_image_dest_iter_init (iter->image, iter); return TRUE; } else { _pixman_log_error (FUNC, "Trying to write to a non-writable image"); return FALSE; } } typedef struct op_info_t op_info_t; struct op_info_t { uint8_t src, dst; }; #define ITER_IGNORE_BOTH \ (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB | ITER_LOCALIZED_ALPHA) static const op_info_t op_flags[PIXMAN_N_OPERATORS] = { /* Src Dst */ { ITER_IGNORE_BOTH, ITER_IGNORE_BOTH }, /* CLEAR */ { ITER_LOCALIZED_ALPHA, ITER_IGNORE_BOTH }, /* SRC */ { ITER_IGNORE_BOTH, ITER_LOCALIZED_ALPHA }, /* DST */ { 0, ITER_LOCALIZED_ALPHA }, /* OVER */ { ITER_LOCALIZED_ALPHA, 0 }, /* OVER_REVERSE */ { ITER_LOCALIZED_ALPHA, ITER_IGNORE_RGB }, /* IN */ { ITER_IGNORE_RGB, ITER_LOCALIZED_ALPHA }, /* IN_REVERSE */ { ITER_LOCALIZED_ALPHA, ITER_IGNORE_RGB }, /* OUT */ { ITER_IGNORE_RGB, ITER_LOCALIZED_ALPHA }, /* OUT_REVERSE */ { 0, 0 }, /* ATOP */ { 0, 0 }, /* ATOP_REVERSE */ { 0, 0 }, /* XOR */ { ITER_LOCALIZED_ALPHA, ITER_LOCALIZED_ALPHA }, /* ADD */ { 0, 0 }, /* SATURATE */ }; #define SCANLINE_BUFFER_LENGTH 8192 static void general_composite_rect (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8]; uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer; uint8_t *src_buffer, *mask_buffer, *dest_buffer; pixman_iter_t src_iter, mask_iter, dest_iter; pixman_combine_32_func_t compose; pixman_bool_t component_alpha; iter_flags_t narrow, src_iter_flags; int Bpp; int i; if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT) && (!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT) && (dest_image->common.flags & FAST_PATH_NARROW_FORMAT)) { narrow = ITER_NARROW; Bpp = 4; } else { narrow = 0; Bpp = 16; } if (width * Bpp > SCANLINE_BUFFER_LENGTH) { scanline_buffer = pixman_malloc_abc (width, 3, Bpp); if (!scanline_buffer) return; } src_buffer = scanline_buffer; mask_buffer = src_buffer + width * Bpp; dest_buffer = mask_buffer + width * Bpp; if (!narrow) { /* To make sure there aren't any NANs in the buffers */ memset (src_buffer, 0, width * Bpp); memset (mask_buffer, 0, width * Bpp); memset (dest_buffer, 0, width * Bpp); } /* src iter */ src_iter_flags = narrow | op_flags[op].src; _pixman_implementation_src_iter_init (imp->toplevel, &src_iter, src_image, src_x, src_y, width, height, src_buffer, src_iter_flags, info->src_flags); /* mask iter */ if ((src_iter_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) == (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) { /* If it doesn't matter what the source is, then it doesn't matter * what the mask is */ mask_image = NULL; } component_alpha = mask_image && mask_image->common.type == BITS && mask_image->common.component_alpha && PIXMAN_FORMAT_RGB (mask_image->bits.format); _pixman_implementation_src_iter_init ( imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height, mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB), info->mask_flags); /* dest iter */ _pixman_implementation_dest_iter_init ( imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height, dest_buffer, narrow | op_flags[op].dst, info->dest_flags); compose = _pixman_implementation_lookup_combiner ( imp->toplevel, op, component_alpha, narrow); for (i = 0; i < height; ++i) { uint32_t *s, *m, *d; m = mask_iter.get_scanline (&mask_iter, NULL); s = src_iter.get_scanline (&src_iter, m); d = dest_iter.get_scanline (&dest_iter, NULL); compose (imp->toplevel, op, d, s, m, width); dest_iter.write_back (&dest_iter); } if (scanline_buffer != (uint8_t *) stack_scanline_buffer) free (scanline_buffer); } static const pixman_fast_path_t general_fast_path[] = { { PIXMAN_OP_any, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, general_composite_rect }, { PIXMAN_OP_NONE } }; pixman_implementation_t * _pixman_implementation_create_general (void) { pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path); _pixman_setup_combiner_functions_32 (imp); _pixman_setup_combiner_functions_float (imp); imp->src_iter_init = general_src_iter_init; imp->dest_iter_init = general_dest_iter_init; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-glyph.c000066400000000000000000000416471271037650300260010ustar00rootroot00000000000000/* * Copyright 2010, 2012, Soren Sandmann * Copyright 2010, 2011, 2012, Red Hat, Inc * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Soren Sandmann */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include typedef struct glyph_metrics_t glyph_metrics_t; typedef struct glyph_t glyph_t; #define TOMBSTONE ((glyph_t *)0x1) /* XXX: These numbers are arbitrary---we've never done any measurements. */ #define N_GLYPHS_HIGH_WATER (16384) #define N_GLYPHS_LOW_WATER (8192) #define HASH_SIZE (2 * N_GLYPHS_HIGH_WATER) #define HASH_MASK (HASH_SIZE - 1) struct glyph_t { void * font_key; void * glyph_key; int origin_x; int origin_y; pixman_image_t * image; pixman_link_t mru_link; }; struct pixman_glyph_cache_t { int n_glyphs; int n_tombstones; int freeze_count; pixman_list_t mru; glyph_t * glyphs[HASH_SIZE]; }; static void free_glyph (glyph_t *glyph) { pixman_list_unlink (&glyph->mru_link); pixman_image_unref (glyph->image); free (glyph); } static unsigned int hash (const void *font_key, const void *glyph_key) { size_t key = (size_t)font_key + (size_t)glyph_key; /* This hash function is based on one found on Thomas Wang's * web page at * * http://www.concentric.net/~Ttwang/tech/inthash.htm * */ key = (key << 15) - key - 1; key = key ^ (key >> 12); key = key + (key << 2); key = key ^ (key >> 4); key = key + (key << 3) + (key << 11); key = key ^ (key >> 16); return key; } static glyph_t * lookup_glyph (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key) { unsigned idx; glyph_t *g; idx = hash (font_key, glyph_key); while ((g = cache->glyphs[idx++ & HASH_MASK])) { if (g != TOMBSTONE && g->font_key == font_key && g->glyph_key == glyph_key) { return g; } } return NULL; } static void insert_glyph (pixman_glyph_cache_t *cache, glyph_t *glyph) { unsigned idx; glyph_t **loc; idx = hash (glyph->font_key, glyph->glyph_key); /* Note: we assume that there is room in the table. If there isn't, * this will be an infinite loop. */ do { loc = &cache->glyphs[idx++ & HASH_MASK]; } while (*loc && *loc != TOMBSTONE); if (*loc == TOMBSTONE) cache->n_tombstones--; cache->n_glyphs++; *loc = glyph; } static void remove_glyph (pixman_glyph_cache_t *cache, glyph_t *glyph) { unsigned idx; idx = hash (glyph->font_key, glyph->glyph_key); while (cache->glyphs[idx & HASH_MASK] != glyph) idx++; cache->glyphs[idx & HASH_MASK] = TOMBSTONE; cache->n_tombstones++; cache->n_glyphs--; /* Eliminate tombstones if possible */ if (cache->glyphs[(idx + 1) & HASH_MASK] == NULL) { while (cache->glyphs[idx & HASH_MASK] == TOMBSTONE) { cache->glyphs[idx & HASH_MASK] = NULL; cache->n_tombstones--; idx--; } } } static void clear_table (pixman_glyph_cache_t *cache) { int i; for (i = 0; i < HASH_SIZE; ++i) { glyph_t *glyph = cache->glyphs[i]; if (glyph && glyph != TOMBSTONE) free_glyph (glyph); cache->glyphs[i] = NULL; } cache->n_glyphs = 0; cache->n_tombstones = 0; } PIXMAN_EXPORT pixman_glyph_cache_t * pixman_glyph_cache_create (void) { pixman_glyph_cache_t *cache; if (!(cache = malloc (sizeof *cache))) return NULL; memset (cache->glyphs, 0, sizeof (cache->glyphs)); cache->n_glyphs = 0; cache->n_tombstones = 0; cache->freeze_count = 0; pixman_list_init (&cache->mru); return cache; } PIXMAN_EXPORT void pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache) { return_if_fail (cache->freeze_count == 0); clear_table (cache); free (cache); } PIXMAN_EXPORT void pixman_glyph_cache_freeze (pixman_glyph_cache_t *cache) { cache->freeze_count++; } PIXMAN_EXPORT void pixman_glyph_cache_thaw (pixman_glyph_cache_t *cache) { if (--cache->freeze_count == 0 && cache->n_glyphs + cache->n_tombstones > N_GLYPHS_HIGH_WATER) { if (cache->n_tombstones > N_GLYPHS_HIGH_WATER) { /* More than half the entries are * tombstones. Just dump the whole table. */ clear_table (cache); } while (cache->n_glyphs > N_GLYPHS_LOW_WATER) { glyph_t *glyph = CONTAINER_OF (glyph_t, mru_link, cache->mru.tail); remove_glyph (cache, glyph); free_glyph (glyph); } } } PIXMAN_EXPORT const void * pixman_glyph_cache_lookup (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key) { return lookup_glyph (cache, font_key, glyph_key); } PIXMAN_EXPORT const void * pixman_glyph_cache_insert (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key, int origin_x, int origin_y, pixman_image_t *image) { glyph_t *glyph; int32_t width, height; return_val_if_fail (cache->freeze_count > 0, NULL); return_val_if_fail (image->type == BITS, NULL); width = image->bits.width; height = image->bits.height; if (cache->n_glyphs >= HASH_SIZE) return NULL; if (!(glyph = malloc (sizeof *glyph))) return NULL; glyph->font_key = font_key; glyph->glyph_key = glyph_key; glyph->origin_x = origin_x; glyph->origin_y = origin_y; if (!(glyph->image = pixman_image_create_bits ( image->bits.format, width, height, NULL, -1))) { free (glyph); return NULL; } pixman_image_composite32 (PIXMAN_OP_SRC, image, NULL, glyph->image, 0, 0, 0, 0, 0, 0, width, height); if (PIXMAN_FORMAT_A (glyph->image->bits.format) != 0 && PIXMAN_FORMAT_RGB (glyph->image->bits.format) != 0) { pixman_image_set_component_alpha (glyph->image, TRUE); } pixman_list_prepend (&cache->mru, &glyph->mru_link); _pixman_image_validate (glyph->image); insert_glyph (cache, glyph); return glyph; } PIXMAN_EXPORT void pixman_glyph_cache_remove (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key) { glyph_t *glyph; if ((glyph = lookup_glyph (cache, font_key, glyph_key))) { remove_glyph (cache, glyph); free_glyph (glyph); } } PIXMAN_EXPORT void pixman_glyph_get_extents (pixman_glyph_cache_t *cache, int n_glyphs, pixman_glyph_t *glyphs, pixman_box32_t *extents) { int i; extents->x1 = extents->y1 = INT32_MAX; extents->x2 = extents->y2 = INT32_MIN; for (i = 0; i < n_glyphs; ++i) { glyph_t *glyph = (glyph_t *)glyphs[i].glyph; int x1, y1, x2, y2; x1 = glyphs[i].x - glyph->origin_x; y1 = glyphs[i].y - glyph->origin_y; x2 = glyphs[i].x - glyph->origin_x + glyph->image->bits.width; y2 = glyphs[i].y - glyph->origin_y + glyph->image->bits.height; if (x1 < extents->x1) extents->x1 = x1; if (y1 < extents->y1) extents->y1 = y1; if (x2 > extents->x2) extents->x2 = x2; if (y2 > extents->y2) extents->y2 = y2; } } /* This function returns a format that is suitable for use as a mask for the * set of glyphs in question. */ PIXMAN_EXPORT pixman_format_code_t pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs) { pixman_format_code_t format = PIXMAN_a1; int i; for (i = 0; i < n_glyphs; ++i) { const glyph_t *glyph = glyphs[i].glyph; pixman_format_code_t glyph_format = glyph->image->bits.format; if (PIXMAN_FORMAT_TYPE (glyph_format) == PIXMAN_TYPE_A) { if (PIXMAN_FORMAT_A (glyph_format) > PIXMAN_FORMAT_A (format)) format = glyph_format; } else { return PIXMAN_a8r8g8b8; } } return format; } static pixman_bool_t box32_intersect (pixman_box32_t *dest, const pixman_box32_t *box1, const pixman_box32_t *box2) { dest->x1 = MAX (box1->x1, box2->x1); dest->y1 = MAX (box1->y1, box2->y1); dest->x2 = MIN (box1->x2, box2->x2); dest->y2 = MIN (box1->y2, box2->y2); return dest->x2 > dest->x1 && dest->y2 > dest->y1; } PIXMAN_EXPORT void pixman_composite_glyphs_no_mask (pixman_op_t op, pixman_image_t *src, pixman_image_t *dest, int32_t src_x, int32_t src_y, int32_t dest_x, int32_t dest_y, pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs) { pixman_region32_t region; pixman_format_code_t glyph_format = PIXMAN_null; uint32_t glyph_flags = 0; pixman_format_code_t dest_format; uint32_t dest_flags; pixman_composite_func_t func = NULL; pixman_implementation_t *implementation = NULL; pixman_composite_info_t info; int i; _pixman_image_validate (src); _pixman_image_validate (dest); dest_format = dest->common.extended_format_code; dest_flags = dest->common.flags; pixman_region32_init (®ion); if (!_pixman_compute_composite_region32 ( ®ion, src, NULL, dest, src_x - dest_x, src_y - dest_y, 0, 0, 0, 0, dest->bits.width, dest->bits.height)) { goto out; } info.op = op; info.src_image = src; info.dest_image = dest; info.src_flags = src->common.flags; info.dest_flags = dest->common.flags; for (i = 0; i < n_glyphs; ++i) { glyph_t *glyph = (glyph_t *)glyphs[i].glyph; pixman_image_t *glyph_img = glyph->image; pixman_box32_t glyph_box; pixman_box32_t *pbox; uint32_t extra = FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; pixman_box32_t composite_box; int n; glyph_box.x1 = dest_x + glyphs[i].x - glyph->origin_x; glyph_box.y1 = dest_y + glyphs[i].y - glyph->origin_y; glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width; glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height; pbox = pixman_region32_rectangles (®ion, &n); info.mask_image = glyph_img; while (n--) { if (box32_intersect (&composite_box, pbox, &glyph_box)) { if (glyph_img->common.extended_format_code != glyph_format || glyph_img->common.flags != glyph_flags) { glyph_format = glyph_img->common.extended_format_code; glyph_flags = glyph_img->common.flags; _pixman_implementation_lookup_composite ( get_implementation(), op, src->common.extended_format_code, src->common.flags, glyph_format, glyph_flags | extra, dest_format, dest_flags, &implementation, &func); } info.src_x = src_x + composite_box.x1 - dest_x; info.src_y = src_y + composite_box.y1 - dest_y; info.mask_x = composite_box.x1 - (dest_x + glyphs[i].x - glyph->origin_x); info.mask_y = composite_box.y1 - (dest_y + glyphs[i].y - glyph->origin_y); info.dest_x = composite_box.x1; info.dest_y = composite_box.y1; info.width = composite_box.x2 - composite_box.x1; info.height = composite_box.y2 - composite_box.y1; info.mask_flags = glyph_flags; func (implementation, &info); } pbox++; } pixman_list_move_to_front (&cache->mru, &glyph->mru_link); } out: pixman_region32_fini (®ion); } static void add_glyphs (pixman_glyph_cache_t *cache, pixman_image_t *dest, int off_x, int off_y, int n_glyphs, const pixman_glyph_t *glyphs) { pixman_format_code_t glyph_format = PIXMAN_null; uint32_t glyph_flags = 0; pixman_composite_func_t func = NULL; pixman_implementation_t *implementation = NULL; pixman_format_code_t dest_format; uint32_t dest_flags; pixman_box32_t dest_box; pixman_composite_info_t info; pixman_image_t *white_img = NULL; pixman_bool_t white_src = FALSE; int i; _pixman_image_validate (dest); dest_format = dest->common.extended_format_code; dest_flags = dest->common.flags; info.op = PIXMAN_OP_ADD; info.dest_image = dest; info.src_x = 0; info.src_y = 0; info.dest_flags = dest_flags; dest_box.x1 = 0; dest_box.y1 = 0; dest_box.x2 = dest->bits.width; dest_box.y2 = dest->bits.height; for (i = 0; i < n_glyphs; ++i) { glyph_t *glyph = (glyph_t *)glyphs[i].glyph; pixman_image_t *glyph_img = glyph->image; pixman_box32_t glyph_box; pixman_box32_t composite_box; if (glyph_img->common.extended_format_code != glyph_format || glyph_img->common.flags != glyph_flags) { pixman_format_code_t src_format, mask_format; glyph_format = glyph_img->common.extended_format_code; glyph_flags = glyph_img->common.flags; if (glyph_format == dest->bits.format) { src_format = glyph_format; mask_format = PIXMAN_null; info.src_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; info.mask_flags = FAST_PATH_IS_OPAQUE; info.mask_image = NULL; white_src = FALSE; } else { if (!white_img) { static const pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff }; if (!(white_img = pixman_image_create_solid_fill (&white))) goto out; _pixman_image_validate (white_img); } src_format = PIXMAN_solid; mask_format = glyph_format; info.src_flags = white_img->common.flags; info.mask_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; info.src_image = white_img; white_src = TRUE; } _pixman_implementation_lookup_composite ( get_implementation(), PIXMAN_OP_ADD, src_format, info.src_flags, mask_format, info.mask_flags, dest_format, dest_flags, &implementation, &func); } glyph_box.x1 = glyphs[i].x - glyph->origin_x + off_x; glyph_box.y1 = glyphs[i].y - glyph->origin_y + off_y; glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width; glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height; if (box32_intersect (&composite_box, &glyph_box, &dest_box)) { int src_x = composite_box.x1 - glyph_box.x1; int src_y = composite_box.y1 - glyph_box.y1; if (white_src) info.mask_image = glyph_img; else info.src_image = glyph_img; info.mask_x = info.src_x = src_x; info.mask_y = info.src_y = src_y; info.dest_x = composite_box.x1; info.dest_y = composite_box.y1; info.width = composite_box.x2 - composite_box.x1; info.height = composite_box.y2 - composite_box.y1; func (implementation, &info); pixman_list_move_to_front (&cache->mru, &glyph->mru_link); } } out: if (white_img) pixman_image_unref (white_img); } /* Conceptually, for each glyph, (white IN glyph) is PIXMAN_OP_ADDed to an * infinitely big mask image at the position such that the glyph origin point * is positioned at the (glyphs[i].x, glyphs[i].y) point. * * Then (mask_x, mask_y) in the infinite mask and (src_x, src_y) in the source * image are both aligned with (dest_x, dest_y) in the destination image. Then * these three images are composited within the * * (dest_x, dest_y, dst_x + width, dst_y + height) * * rectangle. * * TODO: * - Trim the mask to the destination clip/image? * - Trim composite region based on sources, when the op ignores 0s. */ PIXMAN_EXPORT void pixman_composite_glyphs (pixman_op_t op, pixman_image_t *src, pixman_image_t *dest, pixman_format_code_t mask_format, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height, pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs) { pixman_image_t *mask; if (!(mask = pixman_image_create_bits (mask_format, width, height, NULL, -1))) return; if (PIXMAN_FORMAT_A (mask_format) != 0 && PIXMAN_FORMAT_RGB (mask_format) != 0) { pixman_image_set_component_alpha (mask, TRUE); } add_glyphs (cache, mask, - mask_x, - mask_y, n_glyphs, glyphs); pixman_image_composite32 (op, src, mask, dest, src_x, src_y, 0, 0, dest_x, dest_y, width, height); pixman_image_unref (mask); } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-gradient-walker.c000066400000000000000000000131541271037650300277260ustar00rootroot00000000000000/* * * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" void _pixman_gradient_walker_init (pixman_gradient_walker_t *walker, gradient_t * gradient, pixman_repeat_t repeat) { walker->num_stops = gradient->n_stops; walker->stops = gradient->stops; walker->left_x = 0; walker->right_x = 0x10000; walker->a_s = 0.0f; walker->a_b = 0.0f; walker->r_s = 0.0f; walker->r_b = 0.0f; walker->g_s = 0.0f; walker->g_b = 0.0f; walker->b_s = 0.0f; walker->b_b = 0.0f; walker->repeat = repeat; walker->need_reset = TRUE; } static void gradient_walker_reset (pixman_gradient_walker_t *walker, pixman_fixed_48_16_t pos) { int32_t x, left_x, right_x; pixman_color_t *left_c, *right_c; int n, count = walker->num_stops; pixman_gradient_stop_t *stops = walker->stops; float la, lr, lg, lb; float ra, rr, rg, rb; float lx, rx; if (walker->repeat == PIXMAN_REPEAT_NORMAL) { x = (int32_t)pos & 0xffff; } else if (walker->repeat == PIXMAN_REPEAT_REFLECT) { x = (int32_t)pos & 0xffff; if ((int32_t)pos & 0x10000) x = 0x10000 - x; } else { x = pos; } for (n = 0; n < count; n++) { if (x < stops[n].x) break; } left_x = stops[n - 1].x; left_c = &stops[n - 1].color; right_x = stops[n].x; right_c = &stops[n].color; if (walker->repeat == PIXMAN_REPEAT_NORMAL) { left_x += (pos - x); right_x += (pos - x); } else if (walker->repeat == PIXMAN_REPEAT_REFLECT) { if ((int32_t)pos & 0x10000) { pixman_color_t *tmp_c; int32_t tmp_x; tmp_x = 0x10000 - right_x; right_x = 0x10000 - left_x; left_x = tmp_x; tmp_c = right_c; right_c = left_c; left_c = tmp_c; x = 0x10000 - x; } left_x += (pos - x); right_x += (pos - x); } else if (walker->repeat == PIXMAN_REPEAT_NONE) { if (n == 0) right_c = left_c; else if (n == count) left_c = right_c; } /* The alpha channel is scaled to be in the [0, 255] interval, * and the red/green/blue channels are scaled to be in [0, 1]. * This ensures that after premultiplication all channels will * be in the [0, 255] interval. */ la = (left_c->alpha * (1.0f/257.0f)); lr = (left_c->red * (1.0f/257.0f)); lg = (left_c->green * (1.0f/257.0f)); lb = (left_c->blue * (1.0f/257.0f)); ra = (right_c->alpha * (1.0f/257.0f)); rr = (right_c->red * (1.0f/257.0f)); rg = (right_c->green * (1.0f/257.0f)); rb = (right_c->blue * (1.0f/257.0f)); lx = left_x * (1.0f/65536.0f); rx = right_x * (1.0f/65536.0f); if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX) { walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f; walker->a_b = (la + ra) / 2.0f; walker->r_b = (lr + rr) / 510.0f; walker->g_b = (lg + rg) / 510.0f; walker->b_b = (lb + rb) / 510.0f; } else { float w_rec = 1.0f / (rx - lx); walker->a_b = (la * rx - ra * lx) * w_rec; walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f); walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f); walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f); walker->a_s = (ra - la) * w_rec; walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f); walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f); walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f); } walker->left_x = left_x; walker->right_x = right_x; walker->need_reset = FALSE; } uint32_t _pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker, pixman_fixed_48_16_t x) { float a, r, g, b; uint8_t a8, r8, g8, b8; uint32_t v; float y; if (walker->need_reset || x < walker->left_x || x >= walker->right_x) gradient_walker_reset (walker, x); y = x * (1.0f / 65536.0f); a = walker->a_s * y + walker->a_b; r = a * (walker->r_s * y + walker->r_b); g = a * (walker->g_s * y + walker->g_b); b = a * (walker->b_s * y + walker->b_b); a8 = a + 0.5f; r8 = r + 0.5f; g8 = g + 0.5f; b8 = b + 0.5f; v = ((a8 << 24) & 0xff000000) | ((r8 << 16) & 0x00ff0000) | ((g8 << 8) & 0x0000ff00) | ((b8 >> 0) & 0x000000ff); return v; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-image.c000066400000000000000000000564531271037650300257410ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "pixman-private.h" static const pixman_color_t transparent_black = { 0, 0, 0, 0 }; static void gradient_property_changed (pixman_image_t *image) { gradient_t *gradient = &image->gradient; int n = gradient->n_stops; pixman_gradient_stop_t *stops = gradient->stops; pixman_gradient_stop_t *begin = &(gradient->stops[-1]); pixman_gradient_stop_t *end = &(gradient->stops[n]); switch (gradient->common.repeat) { default: case PIXMAN_REPEAT_NONE: begin->x = INT32_MIN; begin->color = transparent_black; end->x = INT32_MAX; end->color = transparent_black; break; case PIXMAN_REPEAT_NORMAL: begin->x = stops[n - 1].x - pixman_fixed_1; begin->color = stops[n - 1].color; end->x = stops[0].x + pixman_fixed_1; end->color = stops[0].color; break; case PIXMAN_REPEAT_REFLECT: begin->x = - stops[0].x; begin->color = stops[0].color; end->x = pixman_int_to_fixed (2) - stops[n - 1].x; end->color = stops[n - 1].color; break; case PIXMAN_REPEAT_PAD: begin->x = INT32_MIN; begin->color = stops[0].color; end->x = INT32_MAX; end->color = stops[n - 1].color; break; } } pixman_bool_t _pixman_init_gradient (gradient_t * gradient, const pixman_gradient_stop_t *stops, int n_stops) { return_val_if_fail (n_stops > 0, FALSE); /* We allocate two extra stops, one before the beginning of the stop list, * and one after the end. These stops are initialized to whatever color * would be used for positions outside the range of the stop list. * * This saves a bit of computation in the gradient walker. * * The pointer we store in the gradient_t struct still points to the * first user-supplied struct, so when freeing, we will have to * subtract one. */ gradient->stops = pixman_malloc_ab (n_stops + 2, sizeof (pixman_gradient_stop_t)); if (!gradient->stops) return FALSE; gradient->stops += 1; memcpy (gradient->stops, stops, n_stops * sizeof (pixman_gradient_stop_t)); gradient->n_stops = n_stops; gradient->common.property_changed = gradient_property_changed; return TRUE; } void _pixman_image_init (pixman_image_t *image) { image_common_t *common = &image->common; pixman_region32_init (&common->clip_region); common->alpha_count = 0; common->have_clip_region = FALSE; common->clip_sources = FALSE; common->transform = NULL; common->repeat = PIXMAN_REPEAT_NONE; common->filter = PIXMAN_FILTER_NEAREST; common->filter_params = NULL; common->n_filter_params = 0; common->alpha_map = NULL; common->component_alpha = FALSE; common->ref_count = 1; common->property_changed = NULL; common->client_clip = FALSE; common->destroy_func = NULL; common->destroy_data = NULL; common->dirty = TRUE; } pixman_bool_t _pixman_image_fini (pixman_image_t *image) { image_common_t *common = (image_common_t *)image; common->ref_count--; if (common->ref_count == 0) { if (image->common.destroy_func) image->common.destroy_func (image, image->common.destroy_data); pixman_region32_fini (&common->clip_region); free (common->transform); free (common->filter_params); if (common->alpha_map) pixman_image_unref ((pixman_image_t *)common->alpha_map); if (image->type == LINEAR || image->type == RADIAL || image->type == CONICAL) { if (image->gradient.stops) { /* See _pixman_init_gradient() for an explanation of the - 1 */ free (image->gradient.stops - 1); } /* This will trigger if someone adds a property_changed * method to the linear/radial/conical gradient overwriting * the general one. */ assert ( image->common.property_changed == gradient_property_changed); } if (image->type == BITS && image->bits.free_me) free (image->bits.free_me); return TRUE; } return FALSE; } pixman_image_t * _pixman_image_allocate (void) { pixman_image_t *image = malloc (sizeof (pixman_image_t)); if (image) _pixman_image_init (image); return image; } static void image_property_changed (pixman_image_t *image) { image->common.dirty = TRUE; } /* Ref Counting */ PIXMAN_EXPORT pixman_image_t * pixman_image_ref (pixman_image_t *image) { image->common.ref_count++; return image; } /* returns TRUE when the image is freed */ PIXMAN_EXPORT pixman_bool_t pixman_image_unref (pixman_image_t *image) { if (_pixman_image_fini (image)) { free (image); return TRUE; } return FALSE; } PIXMAN_EXPORT void pixman_image_set_destroy_function (pixman_image_t * image, pixman_image_destroy_func_t func, void * data) { image->common.destroy_func = func; image->common.destroy_data = data; } PIXMAN_EXPORT void * pixman_image_get_destroy_data (pixman_image_t *image) { return image->common.destroy_data; } void _pixman_image_reset_clip_region (pixman_image_t *image) { image->common.have_clip_region = FALSE; } /* Executive Summary: This function is a no-op that only exists * for historical reasons. * * There used to be a bug in the X server where it would rely on * out-of-bounds accesses when it was asked to composite with a * window as the source. It would create a pixman image pointing * to some bogus position in memory, but then set a clip region * to the position where the actual bits were. * * Due to a bug in old versions of pixman, where it would not clip * against the image bounds when a clip region was set, this would * actually work. So when the pixman bug was fixed, a workaround was * added to allow certain out-of-bound accesses. This function disabled * those workarounds. * * Since 0.21.2, pixman doesn't do these workarounds anymore, so now * this function is a no-op. */ PIXMAN_EXPORT void pixman_disable_out_of_bounds_workaround (void) { } static void compute_image_info (pixman_image_t *image) { pixman_format_code_t code; uint32_t flags = 0; /* Transform */ if (!image->common.transform) { flags |= (FAST_PATH_ID_TRANSFORM | FAST_PATH_X_UNIT_POSITIVE | FAST_PATH_Y_UNIT_ZERO | FAST_PATH_AFFINE_TRANSFORM); } else { flags |= FAST_PATH_HAS_TRANSFORM; if (image->common.transform->matrix[2][0] == 0 && image->common.transform->matrix[2][1] == 0 && image->common.transform->matrix[2][2] == pixman_fixed_1) { flags |= FAST_PATH_AFFINE_TRANSFORM; if (image->common.transform->matrix[0][1] == 0 && image->common.transform->matrix[1][0] == 0) { if (image->common.transform->matrix[0][0] == -pixman_fixed_1 && image->common.transform->matrix[1][1] == -pixman_fixed_1) { flags |= FAST_PATH_ROTATE_180_TRANSFORM; } flags |= FAST_PATH_SCALE_TRANSFORM; } else if (image->common.transform->matrix[0][0] == 0 && image->common.transform->matrix[1][1] == 0) { pixman_fixed_t m01 = image->common.transform->matrix[0][1]; pixman_fixed_t m10 = image->common.transform->matrix[1][0]; if (m01 == -pixman_fixed_1 && m10 == pixman_fixed_1) flags |= FAST_PATH_ROTATE_90_TRANSFORM; else if (m01 == pixman_fixed_1 && m10 == -pixman_fixed_1) flags |= FAST_PATH_ROTATE_270_TRANSFORM; } } if (image->common.transform->matrix[0][0] > 0) flags |= FAST_PATH_X_UNIT_POSITIVE; if (image->common.transform->matrix[1][0] == 0) flags |= FAST_PATH_Y_UNIT_ZERO; } /* Filter */ switch (image->common.filter) { case PIXMAN_FILTER_NEAREST: case PIXMAN_FILTER_FAST: flags |= (FAST_PATH_NEAREST_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); break; case PIXMAN_FILTER_BILINEAR: case PIXMAN_FILTER_GOOD: case PIXMAN_FILTER_BEST: flags |= (FAST_PATH_BILINEAR_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); /* Here we have a chance to optimize BILINEAR filter to NEAREST if * they are equivalent for the currently used transformation matrix. */ if (flags & FAST_PATH_ID_TRANSFORM) { flags |= FAST_PATH_NEAREST_FILTER; } else if ( /* affine and integer translation components in matrix ... */ ((flags & FAST_PATH_AFFINE_TRANSFORM) && !pixman_fixed_frac (image->common.transform->matrix[0][2] | image->common.transform->matrix[1][2])) && ( /* ... combined with a simple rotation */ (flags & (FAST_PATH_ROTATE_90_TRANSFORM | FAST_PATH_ROTATE_180_TRANSFORM | FAST_PATH_ROTATE_270_TRANSFORM)) || /* ... or combined with a simple non-rotated translation */ (image->common.transform->matrix[0][0] == pixman_fixed_1 && image->common.transform->matrix[1][1] == pixman_fixed_1 && image->common.transform->matrix[0][1] == 0 && image->common.transform->matrix[1][0] == 0) ) ) { /* FIXME: there are some affine-test failures, showing that * handling of BILINEAR and NEAREST filter is not quite * equivalent when getting close to 32K for the translation * components of the matrix. That's likely some bug, but for * now just skip BILINEAR->NEAREST optimization in this case. */ pixman_fixed_t magic_limit = pixman_int_to_fixed (30000); if (image->common.transform->matrix[0][2] <= magic_limit && image->common.transform->matrix[1][2] <= magic_limit && image->common.transform->matrix[0][2] >= -magic_limit && image->common.transform->matrix[1][2] >= -magic_limit) { flags |= FAST_PATH_NEAREST_FILTER; } } break; case PIXMAN_FILTER_CONVOLUTION: break; case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; break; default: flags |= FAST_PATH_NO_CONVOLUTION_FILTER; break; } /* Repeat mode */ switch (image->common.repeat) { case PIXMAN_REPEAT_NONE: flags |= FAST_PATH_NO_REFLECT_REPEAT | FAST_PATH_NO_PAD_REPEAT | FAST_PATH_NO_NORMAL_REPEAT; break; case PIXMAN_REPEAT_REFLECT: flags |= FAST_PATH_NO_PAD_REPEAT | FAST_PATH_NO_NONE_REPEAT | FAST_PATH_NO_NORMAL_REPEAT; break; case PIXMAN_REPEAT_PAD: flags |= FAST_PATH_NO_REFLECT_REPEAT | FAST_PATH_NO_NONE_REPEAT | FAST_PATH_NO_NORMAL_REPEAT; break; default: flags |= FAST_PATH_NO_REFLECT_REPEAT | FAST_PATH_NO_PAD_REPEAT | FAST_PATH_NO_NONE_REPEAT; break; } /* Component alpha */ if (image->common.component_alpha) flags |= FAST_PATH_COMPONENT_ALPHA; else flags |= FAST_PATH_UNIFIED_ALPHA; flags |= (FAST_PATH_NO_ACCESSORS | FAST_PATH_NARROW_FORMAT); /* Type specific checks */ switch (image->type) { case SOLID: code = PIXMAN_solid; if (image->solid.color.alpha == 0xffff) flags |= FAST_PATH_IS_OPAQUE; break; case BITS: if (image->bits.width == 1 && image->bits.height == 1 && image->common.repeat != PIXMAN_REPEAT_NONE) { code = PIXMAN_solid; } else { code = image->bits.format; flags |= FAST_PATH_BITS_IMAGE; } if (!PIXMAN_FORMAT_A (image->bits.format) && PIXMAN_FORMAT_TYPE (image->bits.format) != PIXMAN_TYPE_GRAY && PIXMAN_FORMAT_TYPE (image->bits.format) != PIXMAN_TYPE_COLOR) { flags |= FAST_PATH_SAMPLES_OPAQUE; if (image->common.repeat != PIXMAN_REPEAT_NONE) flags |= FAST_PATH_IS_OPAQUE; } if (image->bits.read_func || image->bits.write_func) flags &= ~FAST_PATH_NO_ACCESSORS; if (PIXMAN_FORMAT_IS_WIDE (image->bits.format)) flags &= ~FAST_PATH_NARROW_FORMAT; break; case RADIAL: code = PIXMAN_unknown; /* * As explained in pixman-radial-gradient.c, every point of * the plane has a valid associated radius (and thus will be * colored) if and only if a is negative (i.e. one of the two * circles contains the other one). */ if (image->radial.a >= 0) break; /* Fall through */ case CONICAL: case LINEAR: code = PIXMAN_unknown; if (image->common.repeat != PIXMAN_REPEAT_NONE) { int i; flags |= FAST_PATH_IS_OPAQUE; for (i = 0; i < image->gradient.n_stops; ++i) { if (image->gradient.stops[i].color.alpha != 0xffff) { flags &= ~FAST_PATH_IS_OPAQUE; break; } } } break; default: code = PIXMAN_unknown; break; } /* Alpha map */ if (!image->common.alpha_map) { flags |= FAST_PATH_NO_ALPHA_MAP; } else { if (PIXMAN_FORMAT_IS_WIDE (image->common.alpha_map->format)) flags &= ~FAST_PATH_NARROW_FORMAT; } /* Both alpha maps and convolution filters can introduce * non-opaqueness in otherwise opaque images. Also * an image with component alpha turned on is only opaque * if all channels are opaque, so we simply turn it off * unconditionally for those images. */ if (image->common.alpha_map || image->common.filter == PIXMAN_FILTER_CONVOLUTION || image->common.filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION || image->common.component_alpha) { flags &= ~(FAST_PATH_IS_OPAQUE | FAST_PATH_SAMPLES_OPAQUE); } image->common.flags = flags; image->common.extended_format_code = code; } void _pixman_image_validate (pixman_image_t *image) { if (image->common.dirty) { compute_image_info (image); /* It is important that property_changed is * called *after* compute_image_info() because * property_changed() can make use of the flags * to set up accessors etc. */ if (image->common.property_changed) image->common.property_changed (image); image->common.dirty = FALSE; } if (image->common.alpha_map) _pixman_image_validate ((pixman_image_t *)image->common.alpha_map); } PIXMAN_EXPORT pixman_bool_t pixman_image_set_clip_region32 (pixman_image_t * image, pixman_region32_t *region) { image_common_t *common = (image_common_t *)image; pixman_bool_t result; if (region) { if ((result = pixman_region32_copy (&common->clip_region, region))) image->common.have_clip_region = TRUE; } else { _pixman_image_reset_clip_region (image); result = TRUE; } image_property_changed (image); return result; } PIXMAN_EXPORT pixman_bool_t pixman_image_set_clip_region (pixman_image_t * image, pixman_region16_t *region) { image_common_t *common = (image_common_t *)image; pixman_bool_t result; if (region) { if ((result = pixman_region32_copy_from_region16 (&common->clip_region, region))) image->common.have_clip_region = TRUE; } else { _pixman_image_reset_clip_region (image); result = TRUE; } image_property_changed (image); return result; } PIXMAN_EXPORT void pixman_image_set_has_client_clip (pixman_image_t *image, pixman_bool_t client_clip) { image->common.client_clip = client_clip; } PIXMAN_EXPORT pixman_bool_t pixman_image_set_transform (pixman_image_t * image, const pixman_transform_t *transform) { static const pixman_transform_t id = { { { pixman_fixed_1, 0, 0 }, { 0, pixman_fixed_1, 0 }, { 0, 0, pixman_fixed_1 } } }; image_common_t *common = (image_common_t *)image; pixman_bool_t result; if (common->transform == transform) return TRUE; if (!transform || memcmp (&id, transform, sizeof (pixman_transform_t)) == 0) { free (common->transform); common->transform = NULL; result = TRUE; goto out; } if (common->transform && memcmp (common->transform, transform, sizeof (pixman_transform_t)) == 0) { return TRUE; } if (common->transform == NULL) common->transform = malloc (sizeof (pixman_transform_t)); if (common->transform == NULL) { result = FALSE; goto out; } memcpy (common->transform, transform, sizeof(pixman_transform_t)); result = TRUE; out: image_property_changed (image); return result; } PIXMAN_EXPORT void pixman_image_set_repeat (pixman_image_t *image, pixman_repeat_t repeat) { if (image->common.repeat == repeat) return; image->common.repeat = repeat; image_property_changed (image); } PIXMAN_EXPORT pixman_bool_t pixman_image_set_filter (pixman_image_t * image, pixman_filter_t filter, const pixman_fixed_t *params, int n_params) { image_common_t *common = (image_common_t *)image; pixman_fixed_t *new_params; if (params == common->filter_params && filter == common->filter) return TRUE; if (filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) { int width = pixman_fixed_to_int (params[0]); int height = pixman_fixed_to_int (params[1]); int x_phase_bits = pixman_fixed_to_int (params[2]); int y_phase_bits = pixman_fixed_to_int (params[3]); int n_x_phases = (1 << x_phase_bits); int n_y_phases = (1 << y_phase_bits); return_val_if_fail ( n_params == 4 + n_x_phases * width + n_y_phases * height, FALSE); } new_params = NULL; if (params) { new_params = pixman_malloc_ab (n_params, sizeof (pixman_fixed_t)); if (!new_params) return FALSE; memcpy (new_params, params, n_params * sizeof (pixman_fixed_t)); } common->filter = filter; if (common->filter_params) free (common->filter_params); common->filter_params = new_params; common->n_filter_params = n_params; image_property_changed (image); return TRUE; } PIXMAN_EXPORT void pixman_image_set_source_clipping (pixman_image_t *image, pixman_bool_t clip_sources) { if (image->common.clip_sources == clip_sources) return; image->common.clip_sources = clip_sources; image_property_changed (image); } /* Unlike all the other property setters, this function does not * copy the content of indexed. Doing this copying is simply * way, way too expensive. */ PIXMAN_EXPORT void pixman_image_set_indexed (pixman_image_t * image, const pixman_indexed_t *indexed) { bits_image_t *bits = (bits_image_t *)image; if (bits->indexed == indexed) return; bits->indexed = indexed; image_property_changed (image); } PIXMAN_EXPORT void pixman_image_set_alpha_map (pixman_image_t *image, pixman_image_t *alpha_map, int16_t x, int16_t y) { image_common_t *common = (image_common_t *)image; return_if_fail (!alpha_map || alpha_map->type == BITS); if (alpha_map && common->alpha_count > 0) { /* If this image is being used as an alpha map itself, * then you can't give it an alpha map of its own. */ return; } if (alpha_map && alpha_map->common.alpha_map) { /* If the image has an alpha map of its own, * then it can't be used as an alpha map itself */ return; } if (common->alpha_map != (bits_image_t *)alpha_map) { if (common->alpha_map) { common->alpha_map->common.alpha_count--; pixman_image_unref ((pixman_image_t *)common->alpha_map); } if (alpha_map) { common->alpha_map = (bits_image_t *)pixman_image_ref (alpha_map); common->alpha_map->common.alpha_count++; } else { common->alpha_map = NULL; } } common->alpha_origin_x = x; common->alpha_origin_y = y; image_property_changed (image); } PIXMAN_EXPORT void pixman_image_set_component_alpha (pixman_image_t *image, pixman_bool_t component_alpha) { if (image->common.component_alpha == component_alpha) return; image->common.component_alpha = component_alpha; image_property_changed (image); } PIXMAN_EXPORT pixman_bool_t pixman_image_get_component_alpha (pixman_image_t *image) { return image->common.component_alpha; } PIXMAN_EXPORT void pixman_image_set_accessors (pixman_image_t * image, pixman_read_memory_func_t read_func, pixman_write_memory_func_t write_func) { return_if_fail (image != NULL); if (image->type == BITS) { image->bits.read_func = read_func; image->bits.write_func = write_func; image_property_changed (image); } } PIXMAN_EXPORT uint32_t * pixman_image_get_data (pixman_image_t *image) { if (image->type == BITS) return image->bits.bits; return NULL; } PIXMAN_EXPORT int pixman_image_get_width (pixman_image_t *image) { if (image->type == BITS) return image->bits.width; return 0; } PIXMAN_EXPORT int pixman_image_get_height (pixman_image_t *image) { if (image->type == BITS) return image->bits.height; return 0; } PIXMAN_EXPORT int pixman_image_get_stride (pixman_image_t *image) { if (image->type == BITS) return image->bits.rowstride * (int) sizeof (uint32_t); return 0; } PIXMAN_EXPORT int pixman_image_get_depth (pixman_image_t *image) { if (image->type == BITS) return PIXMAN_FORMAT_DEPTH (image->bits.format); return 0; } PIXMAN_EXPORT pixman_format_code_t pixman_image_get_format (pixman_image_t *image) { if (image->type == BITS) return image->bits.format; return PIXMAN_null; } uint32_t _pixman_image_get_solid (pixman_implementation_t *imp, pixman_image_t * image, pixman_format_code_t format) { uint32_t result; if (image->type == SOLID) { result = image->solid.color_32; } else if (image->type == BITS) { if (image->bits.format == PIXMAN_a8r8g8b8) result = image->bits.bits[0]; else if (image->bits.format == PIXMAN_x8r8g8b8) result = image->bits.bits[0] | 0xff000000; else if (image->bits.format == PIXMAN_a8) result = (*(uint8_t *)image->bits.bits) << 24; else goto otherwise; } else { pixman_iter_t iter; otherwise: _pixman_implementation_src_iter_init ( imp, &iter, image, 0, 0, 1, 1, (uint8_t *)&result, ITER_NARROW, image->common.flags); result = *iter.get_scanline (&iter, NULL); } /* If necessary, convert RGB <--> BGR. */ if (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB && PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB_SRGB) { result = (((result & 0xff000000) >> 0) | ((result & 0x00ff0000) >> 16) | ((result & 0x0000ff00) >> 0) | ((result & 0x000000ff) << 16)); } return result; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-implementation.c000066400000000000000000000246761271037650300277060ustar00rootroot00000000000000/* * Copyright © 2009 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" pixman_implementation_t * _pixman_implementation_create (pixman_implementation_t *fallback, const pixman_fast_path_t *fast_paths) { pixman_implementation_t *imp; assert (fast_paths); if ((imp = malloc (sizeof (pixman_implementation_t)))) { pixman_implementation_t *d; memset (imp, 0, sizeof *imp); imp->fallback = fallback; imp->fast_paths = fast_paths; /* Make sure the whole fallback chain has the right toplevel */ for (d = imp; d != NULL; d = d->fallback) d->toplevel = imp; } return imp; } #define N_CACHED_FAST_PATHS 8 typedef struct { struct { pixman_implementation_t * imp; pixman_fast_path_t fast_path; } cache [N_CACHED_FAST_PATHS]; } cache_t; PIXMAN_DEFINE_THREAD_LOCAL (cache_t, fast_path_cache); static void dummy_composite_rect (pixman_implementation_t *imp, pixman_composite_info_t *info) { } void _pixman_implementation_lookup_composite (pixman_implementation_t *toplevel, pixman_op_t op, pixman_format_code_t src_format, uint32_t src_flags, pixman_format_code_t mask_format, uint32_t mask_flags, pixman_format_code_t dest_format, uint32_t dest_flags, pixman_implementation_t **out_imp, pixman_composite_func_t *out_func) { pixman_implementation_t *imp; cache_t *cache; int i; /* Check cache for fast paths */ cache = PIXMAN_GET_THREAD_LOCAL (fast_path_cache); for (i = 0; i < N_CACHED_FAST_PATHS; ++i) { const pixman_fast_path_t *info = &(cache->cache[i].fast_path); /* Note that we check for equality here, not whether * the cached fast path matches. This is to prevent * us from selecting an overly general fast path * when a more specific one would work. */ if (info->op == op && info->src_format == src_format && info->mask_format == mask_format && info->dest_format == dest_format && info->src_flags == src_flags && info->mask_flags == mask_flags && info->dest_flags == dest_flags && info->func) { *out_imp = cache->cache[i].imp; *out_func = cache->cache[i].fast_path.func; goto update_cache; } } for (imp = toplevel; imp != NULL; imp = imp->fallback) { const pixman_fast_path_t *info = imp->fast_paths; while (info->op != PIXMAN_OP_NONE) { if ((info->op == op || info->op == PIXMAN_OP_any) && /* Formats */ ((info->src_format == src_format) || (info->src_format == PIXMAN_any)) && ((info->mask_format == mask_format) || (info->mask_format == PIXMAN_any)) && ((info->dest_format == dest_format) || (info->dest_format == PIXMAN_any)) && /* Flags */ (info->src_flags & src_flags) == info->src_flags && (info->mask_flags & mask_flags) == info->mask_flags && (info->dest_flags & dest_flags) == info->dest_flags) { *out_imp = imp; *out_func = info->func; /* Set i to the last spot in the cache so that the * move-to-front code below will work */ i = N_CACHED_FAST_PATHS - 1; goto update_cache; } ++info; } } /* We should never reach this point */ _pixman_log_error ( FUNC, "No composite function found\n" "\n" "The most likely cause of this is that this system has issues with\n" "thread local storage\n"); *out_imp = NULL; *out_func = dummy_composite_rect; return; update_cache: if (i) { while (i--) cache->cache[i + 1] = cache->cache[i]; cache->cache[0].imp = *out_imp; cache->cache[0].fast_path.op = op; cache->cache[0].fast_path.src_format = src_format; cache->cache[0].fast_path.src_flags = src_flags; cache->cache[0].fast_path.mask_format = mask_format; cache->cache[0].fast_path.mask_flags = mask_flags; cache->cache[0].fast_path.dest_format = dest_format; cache->cache[0].fast_path.dest_flags = dest_flags; cache->cache[0].fast_path.func = *out_func; } } static void dummy_combine (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { } pixman_combine_32_func_t _pixman_implementation_lookup_combiner (pixman_implementation_t *imp, pixman_op_t op, pixman_bool_t component_alpha, pixman_bool_t narrow) { while (imp) { pixman_combine_32_func_t f = NULL; switch ((narrow << 1) | component_alpha) { case 0: /* not narrow, not component alpha */ f = (pixman_combine_32_func_t)imp->combine_float[op]; break; case 1: /* not narrow, component_alpha */ f = (pixman_combine_32_func_t)imp->combine_float_ca[op]; break; case 2: /* narrow, not component alpha */ f = imp->combine_32[op]; break; case 3: /* narrow, component_alpha */ f = imp->combine_32_ca[op]; break; } if (f) return f; imp = imp->fallback; } /* We should never reach this point */ _pixman_log_error (FUNC, "No known combine function\n"); return dummy_combine; } pixman_bool_t _pixman_implementation_blt (pixman_implementation_t * imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { while (imp) { if (imp->blt && (*imp->blt) (imp, src_bits, dst_bits, src_stride, dst_stride, src_bpp, dst_bpp, src_x, src_y, dest_x, dest_y, width, height)) { return TRUE; } imp = imp->fallback; } return FALSE; } pixman_bool_t _pixman_implementation_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler) { while (imp) { if (imp->fill && ((*imp->fill) (imp, bits, stride, bpp, x, y, width, height, filler))) { return TRUE; } imp = imp->fallback; } return FALSE; } pixman_bool_t _pixman_implementation_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter, pixman_image_t *image, int x, int y, int width, int height, uint8_t *buffer, iter_flags_t iter_flags, uint32_t image_flags) { iter->image = image; iter->buffer = (uint32_t *)buffer; iter->x = x; iter->y = y; iter->width = width; iter->height = height; iter->iter_flags = iter_flags; iter->image_flags = image_flags; while (imp) { if (imp->src_iter_init && (*imp->src_iter_init) (imp, iter)) return TRUE; imp = imp->fallback; } return FALSE; } pixman_bool_t _pixman_implementation_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter, pixman_image_t *image, int x, int y, int width, int height, uint8_t *buffer, iter_flags_t iter_flags, uint32_t image_flags) { iter->image = image; iter->buffer = (uint32_t *)buffer; iter->x = x; iter->y = y; iter->width = width; iter->height = height; iter->iter_flags = iter_flags; iter->image_flags = image_flags; while (imp) { if (imp->dest_iter_init && (*imp->dest_iter_init) (imp, iter)) return TRUE; imp = imp->fallback; } return FALSE; } pixman_bool_t _pixman_disabled (const char *name) { const char *env; if ((env = getenv ("PIXMAN_DISABLE"))) { do { const char *end; int len; if ((end = strchr (env, ' '))) len = end - env; else len = strlen (env); if (strlen (name) == len && strncmp (name, env, len) == 0) { printf ("pixman: Disabled %s implementation\n", name); return TRUE; } env += len; } while (*env++); } return FALSE; } pixman_implementation_t * _pixman_choose_implementation (void) { pixman_implementation_t *imp; imp = _pixman_implementation_create_general(); if (!_pixman_disabled ("fast")) imp = _pixman_implementation_create_fast_path (imp); imp = _pixman_x86_get_implementations (imp); imp = _pixman_arm_get_implementations (imp); imp = _pixman_ppc_get_implementations (imp); imp = _pixman_mips_get_implementations (imp); imp = _pixman_implementation_create_noop (imp); return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-inlines.h000066400000000000000000001376011271037650300263200ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #ifndef PIXMAN_FAST_PATH_H__ #define PIXMAN_FAST_PATH_H__ #include "pixman-private.h" #define PIXMAN_REPEAT_COVER -1 /* Flags describing input parameters to fast path macro template. * Turning on some flag values may indicate that * "some property X is available so template can use this" or * "some property X should be handled by template". * * FLAG_HAVE_SOLID_MASK * Input mask is solid so template should handle this. * * FLAG_HAVE_NON_SOLID_MASK * Input mask is bits mask so template should handle this. * * FLAG_HAVE_SOLID_MASK and FLAG_HAVE_NON_SOLID_MASK are mutually * exclusive. (It's not allowed to turn both flags on) */ #define FLAG_NONE (0) #define FLAG_HAVE_SOLID_MASK (1 << 1) #define FLAG_HAVE_NON_SOLID_MASK (1 << 2) /* To avoid too short repeated scanline function calls, extend source * scanlines having width less than below constant value. */ #define REPEAT_NORMAL_MIN_WIDTH 64 static force_inline pixman_bool_t repeat (pixman_repeat_t repeat, int *c, int size) { if (repeat == PIXMAN_REPEAT_NONE) { if (*c < 0 || *c >= size) return FALSE; } else if (repeat == PIXMAN_REPEAT_NORMAL) { while (*c >= size) *c -= size; while (*c < 0) *c += size; } else if (repeat == PIXMAN_REPEAT_PAD) { *c = CLIP (*c, 0, size - 1); } else /* REFLECT */ { *c = MOD (*c, size * 2); if (*c >= size) *c = size * 2 - *c - 1; } return TRUE; } static force_inline int pixman_fixed_to_bilinear_weight (pixman_fixed_t x) { return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) & ((1 << BILINEAR_INTERPOLATION_BITS) - 1); } #if BILINEAR_INTERPOLATION_BITS <= 4 /* Inspired by Filter_32_opaque from Skia */ static force_inline uint32_t bilinear_interpolation (uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, int distx, int disty) { int distxy, distxiy, distixy, distixiy; uint32_t lo, hi; distx <<= (4 - BILINEAR_INTERPOLATION_BITS); disty <<= (4 - BILINEAR_INTERPOLATION_BITS); distxy = distx * disty; distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ distixiy = 16 * 16 - (disty << 4) - (distx << 4) + distxy; /* (16 - distx) * (16 - disty) */ lo = (tl & 0xff00ff) * distixiy; hi = ((tl >> 8) & 0xff00ff) * distixiy; lo += (tr & 0xff00ff) * distxiy; hi += ((tr >> 8) & 0xff00ff) * distxiy; lo += (bl & 0xff00ff) * distixy; hi += ((bl >> 8) & 0xff00ff) * distixy; lo += (br & 0xff00ff) * distxy; hi += ((br >> 8) & 0xff00ff) * distxy; return ((lo >> 8) & 0xff00ff) | (hi & ~0xff00ff); } #else #if SIZEOF_LONG > 4 static force_inline uint32_t bilinear_interpolation (uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, int distx, int disty) { uint64_t distxy, distxiy, distixy, distixiy; uint64_t tl64, tr64, bl64, br64; uint64_t f, r; distx <<= (8 - BILINEAR_INTERPOLATION_BITS); disty <<= (8 - BILINEAR_INTERPOLATION_BITS); distxy = distx * disty; distxiy = distx * (256 - disty); distixy = (256 - distx) * disty; distixiy = (256 - distx) * (256 - disty); /* Alpha and Blue */ tl64 = tl & 0xff0000ff; tr64 = tr & 0xff0000ff; bl64 = bl & 0xff0000ff; br64 = br & 0xff0000ff; f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; r = f & 0x0000ff0000ff0000ull; /* Red and Green */ tl64 = tl; tl64 = ((tl64 << 16) & 0x000000ff00000000ull) | (tl64 & 0x0000ff00ull); tr64 = tr; tr64 = ((tr64 << 16) & 0x000000ff00000000ull) | (tr64 & 0x0000ff00ull); bl64 = bl; bl64 = ((bl64 << 16) & 0x000000ff00000000ull) | (bl64 & 0x0000ff00ull); br64 = br; br64 = ((br64 << 16) & 0x000000ff00000000ull) | (br64 & 0x0000ff00ull); f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull); return (uint32_t)(r >> 16); } #else static force_inline uint32_t bilinear_interpolation (uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, int distx, int disty) { int distxy, distxiy, distixy, distixiy; uint32_t f, r; distx <<= (8 - BILINEAR_INTERPOLATION_BITS); disty <<= (8 - BILINEAR_INTERPOLATION_BITS); distxy = distx * disty; distxiy = (distx << 8) - distxy; /* distx * (256 - disty) */ distixy = (disty << 8) - distxy; /* disty * (256 - distx) */ distixiy = 256 * 256 - (disty << 8) - (distx << 8) + distxy; /* (256 - distx) * (256 - disty) */ /* Blue */ r = (tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy; /* Green */ f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy; r |= f & 0xff000000; tl >>= 16; tr >>= 16; bl >>= 16; br >>= 16; r >>= 16; /* Red */ f = (tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy; r |= f & 0x00ff0000; /* Alpha */ f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy; r |= f & 0xff000000; return r; } #endif #endif // BILINEAR_INTERPOLATION_BITS <= 4 /* * For each scanline fetched from source image with PAD repeat: * - calculate how many pixels need to be padded on the left side * - calculate how many pixels need to be padded on the right side * - update width to only count pixels which are fetched from the image * All this information is returned via 'width', 'left_pad', 'right_pad' * arguments. The code is assuming that 'unit_x' is positive. * * Note: 64-bit math is used in order to avoid potential overflows, which * is probably excessive in many cases. This particular function * may need its own correctness test and performance tuning. */ static force_inline void pad_repeat_get_scanline_bounds (int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x, int32_t * width, int32_t * left_pad, int32_t * right_pad) { int64_t max_vx = (int64_t) source_image_width << 16; int64_t tmp; if (vx < 0) { tmp = ((int64_t) unit_x - 1 - vx) / unit_x; if (tmp > *width) { *left_pad = *width; *width = 0; } else { *left_pad = (int32_t) tmp; *width -= (int32_t) tmp; } } else { *left_pad = 0; } tmp = ((int64_t) unit_x - 1 - vx + max_vx) / unit_x - *left_pad; if (tmp < 0) { *right_pad = *width; *width = 0; } else if (tmp >= *width) { *right_pad = 0; } else { *right_pad = *width - (int32_t) tmp; *width = (int32_t) tmp; } } /* A macroified version of specialized nearest scalers for some * common 8888 and 565 formats. It supports SRC and OVER ops. * * There are two repeat versions, one that handles repeat normal, * and one without repeat handling that only works if the src region * used is completely covered by the pre-repeated source samples. * * The loops are unrolled to process two pixels per iteration for better * performance on most CPU architectures (superscalar processors * can issue several operations simultaneously, other processors can hide * instructions latencies by pipelining operations). Unrolling more * does not make much sense because the compiler will start running out * of spare registers soon. */ #define GET_8888_ALPHA(s) ((s) >> 24) /* This is not actually used since we don't have an OVER with 565 source, but it is needed to build. */ #define GET_0565_ALPHA(s) 0xff #define GET_x888_ALPHA(s) 0xff #define FAST_NEAREST_SCANLINE(scanline_func_name, SRC_FORMAT, DST_FORMAT, \ src_type_t, dst_type_t, OP, repeat_mode) \ static force_inline void \ scanline_func_name (dst_type_t *dst, \ const src_type_t *src, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t src_width_fixed, \ pixman_bool_t fully_transparent_src) \ { \ uint32_t d; \ src_type_t s1, s2; \ uint8_t a1, a2; \ int x1, x2; \ \ if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER && fully_transparent_src) \ return; \ \ if (PIXMAN_OP_ ## OP != PIXMAN_OP_SRC && PIXMAN_OP_ ## OP != PIXMAN_OP_OVER) \ abort(); \ \ while ((w -= 2) >= 0) \ { \ x1 = pixman_fixed_to_int (vx); \ vx += unit_x; \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ { \ /* This works because we know that unit_x is positive */ \ while (vx >= 0) \ vx -= src_width_fixed; \ } \ s1 = *(src + x1); \ \ x2 = pixman_fixed_to_int (vx); \ vx += unit_x; \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ { \ /* This works because we know that unit_x is positive */ \ while (vx >= 0) \ vx -= src_width_fixed; \ } \ s2 = *(src + x2); \ \ if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER) \ { \ a1 = GET_ ## SRC_FORMAT ## _ALPHA(s1); \ a2 = GET_ ## SRC_FORMAT ## _ALPHA(s2); \ \ if (a1 == 0xff) \ { \ *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ } \ else if (s1) \ { \ d = convert_ ## DST_FORMAT ## _to_8888 (*dst); \ s1 = convert_ ## SRC_FORMAT ## _to_8888 (s1); \ a1 ^= 0xff; \ UN8x4_MUL_UN8_ADD_UN8x4 (d, a1, s1); \ *dst = convert_8888_to_ ## DST_FORMAT (d); \ } \ dst++; \ \ if (a2 == 0xff) \ { \ *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s2); \ } \ else if (s2) \ { \ d = convert_## DST_FORMAT ## _to_8888 (*dst); \ s2 = convert_## SRC_FORMAT ## _to_8888 (s2); \ a2 ^= 0xff; \ UN8x4_MUL_UN8_ADD_UN8x4 (d, a2, s2); \ *dst = convert_8888_to_ ## DST_FORMAT (d); \ } \ dst++; \ } \ else /* PIXMAN_OP_SRC */ \ { \ *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s2); \ } \ } \ \ if (w & 1) \ { \ x1 = pixman_fixed_to_int (vx); \ s1 = *(src + x1); \ \ if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER) \ { \ a1 = GET_ ## SRC_FORMAT ## _ALPHA(s1); \ \ if (a1 == 0xff) \ { \ *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ } \ else if (s1) \ { \ d = convert_## DST_FORMAT ## _to_8888 (*dst); \ s1 = convert_ ## SRC_FORMAT ## _to_8888 (s1); \ a1 ^= 0xff; \ UN8x4_MUL_UN8_ADD_UN8x4 (d, a1, s1); \ *dst = convert_8888_to_ ## DST_FORMAT (d); \ } \ dst++; \ } \ else /* PIXMAN_OP_SRC */ \ { \ *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ } \ } \ } #define FAST_NEAREST_MAINLOOP_INT(scale_func_name, scanline_func, src_type_t, mask_type_t, \ dst_type_t, repeat_mode, have_mask, mask_is_solid) \ static void \ fast_composite_scaled_nearest ## scale_func_name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type_t *dst_line; \ mask_type_t *mask_line; \ src_type_t *src_first_line; \ int y; \ pixman_fixed_t src_width_fixed = pixman_int_to_fixed (src_image->bits.width); \ pixman_fixed_t max_vy; \ pixman_vector_t v; \ pixman_fixed_t vx, vy; \ pixman_fixed_t unit_x, unit_y; \ int32_t left_pad, right_pad; \ \ src_type_t *src; \ dst_type_t *dst; \ mask_type_t solid_mask; \ const mask_type_t *mask = &solid_mask; \ int src_stride, mask_stride, dst_stride; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type_t, dst_stride, dst_line, 1); \ if (have_mask) \ { \ if (mask_is_solid) \ solid_mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); \ else \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type_t, \ mask_stride, mask_line, 1); \ } \ /* pass in 0 instead of src_x and src_y because src_x and src_y need to be \ * transformed from destination space to source space */ \ PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, src_type_t, src_stride, src_first_line, 1); \ \ /* reference point is the center of the pixel */ \ v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; \ v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; \ v.vector[2] = pixman_fixed_1; \ \ if (!pixman_transform_point_3d (src_image->common.transform, &v)) \ return; \ \ unit_x = src_image->common.transform->matrix[0][0]; \ unit_y = src_image->common.transform->matrix[1][1]; \ \ /* Round down to closest integer, ensuring that 0.5 rounds to 0, not 1 */ \ v.vector[0] -= pixman_fixed_e; \ v.vector[1] -= pixman_fixed_e; \ \ vx = v.vector[0]; \ vy = v.vector[1]; \ \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ { \ max_vy = pixman_int_to_fixed (src_image->bits.height); \ \ /* Clamp repeating positions inside the actual samples */ \ repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ repeat (PIXMAN_REPEAT_NORMAL, &vy, max_vy); \ } \ \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD || \ PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ { \ pad_repeat_get_scanline_bounds (src_image->bits.width, vx, unit_x, \ &width, &left_pad, &right_pad); \ vx += left_pad * unit_x; \ } \ \ while (--height >= 0) \ { \ dst = dst_line; \ dst_line += dst_stride; \ if (have_mask && !mask_is_solid) \ { \ mask = mask_line; \ mask_line += mask_stride; \ } \ \ y = pixman_fixed_to_int (vy); \ vy += unit_y; \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ repeat (PIXMAN_REPEAT_NORMAL, &vy, max_vy); \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ { \ repeat (PIXMAN_REPEAT_PAD, &y, src_image->bits.height); \ src = src_first_line + src_stride * y; \ if (left_pad > 0) \ { \ scanline_func (mask, dst, \ src + src_image->bits.width - src_image->bits.width + 1, \ left_pad, -pixman_fixed_e, 0, src_width_fixed, FALSE); \ } \ if (width > 0) \ { \ scanline_func (mask + (mask_is_solid ? 0 : left_pad), \ dst + left_pad, src + src_image->bits.width, width, \ vx - src_width_fixed, unit_x, src_width_fixed, FALSE); \ } \ if (right_pad > 0) \ { \ scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \ dst + left_pad + width, src + src_image->bits.width, \ right_pad, -pixman_fixed_e, 0, src_width_fixed, FALSE); \ } \ } \ else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ { \ static const src_type_t zero[1] = { 0 }; \ if (y < 0 || y >= src_image->bits.height) \ { \ scanline_func (mask, dst, zero + 1, left_pad + width + right_pad, \ -pixman_fixed_e, 0, src_width_fixed, TRUE); \ continue; \ } \ src = src_first_line + src_stride * y; \ if (left_pad > 0) \ { \ scanline_func (mask, dst, zero + 1, left_pad, \ -pixman_fixed_e, 0, src_width_fixed, TRUE); \ } \ if (width > 0) \ { \ scanline_func (mask + (mask_is_solid ? 0 : left_pad), \ dst + left_pad, src + src_image->bits.width, width, \ vx - src_width_fixed, unit_x, src_width_fixed, FALSE); \ } \ if (right_pad > 0) \ { \ scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \ dst + left_pad + width, zero + 1, right_pad, \ -pixman_fixed_e, 0, src_width_fixed, TRUE); \ } \ } \ else \ { \ src = src_first_line + src_stride * y; \ scanline_func (mask, dst, src + src_image->bits.width, width, vx - src_width_fixed, \ unit_x, src_width_fixed, FALSE); \ } \ } \ } /* A workaround for old sun studio, see: https://bugs.freedesktop.org/show_bug.cgi?id=32764 */ #define FAST_NEAREST_MAINLOOP_COMMON(scale_func_name, scanline_func, src_type_t, mask_type_t, \ dst_type_t, repeat_mode, have_mask, mask_is_solid) \ FAST_NEAREST_MAINLOOP_INT(_ ## scale_func_name, scanline_func, src_type_t, mask_type_t, \ dst_type_t, repeat_mode, have_mask, mask_is_solid) #define FAST_NEAREST_MAINLOOP_NOMASK(scale_func_name, scanline_func, src_type_t, dst_type_t, \ repeat_mode) \ static force_inline void \ scanline_func##scale_func_name##_wrapper ( \ const uint8_t *mask, \ dst_type_t *dst, \ const src_type_t *src, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t fully_transparent_src) \ { \ scanline_func (dst, src, w, vx, unit_x, max_vx, fully_transparent_src); \ } \ FAST_NEAREST_MAINLOOP_INT (scale_func_name, scanline_func##scale_func_name##_wrapper, \ src_type_t, uint8_t, dst_type_t, repeat_mode, FALSE, FALSE) #define FAST_NEAREST_MAINLOOP(scale_func_name, scanline_func, src_type_t, dst_type_t, \ repeat_mode) \ FAST_NEAREST_MAINLOOP_NOMASK(_ ## scale_func_name, scanline_func, src_type_t, \ dst_type_t, repeat_mode) #define FAST_NEAREST(scale_func_name, SRC_FORMAT, DST_FORMAT, \ src_type_t, dst_type_t, OP, repeat_mode) \ FAST_NEAREST_SCANLINE(scaled_nearest_scanline_ ## scale_func_name ## _ ## OP, \ SRC_FORMAT, DST_FORMAT, src_type_t, dst_type_t, \ OP, repeat_mode) \ FAST_NEAREST_MAINLOOP_NOMASK(_ ## scale_func_name ## _ ## OP, \ scaled_nearest_scanline_ ## scale_func_name ## _ ## OP, \ src_type_t, dst_type_t, repeat_mode) #define SCALED_NEAREST_FLAGS \ (FAST_PATH_SCALE_TRANSFORM | \ FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NEAREST_FILTER | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_NARROW_FORMAT) #define SIMPLE_NEAREST_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ } #define SIMPLE_NEAREST_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_NEAREST_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ } #define SIMPLE_NEAREST_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ } #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ } #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ } #define SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ } #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ } #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_NEAREST_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ } #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ } /* Prefer the use of 'cover' variant, because it is faster */ #define SIMPLE_NEAREST_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_NORMAL (op,s,d,func) #define SIMPLE_NEAREST_A8_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD (op,s,d,func) #define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_PAD (op,s,d,func) /*****************************************************************************/ /* * Identify 5 zones in each scanline for bilinear scaling. Depending on * whether 2 pixels to be interpolated are fetched from the image itself, * from the padding area around it or from both image and padding area. */ static force_inline void bilinear_pad_repeat_get_scanline_bounds (int32_t source_image_width, pixman_fixed_t vx, pixman_fixed_t unit_x, int32_t * left_pad, int32_t * left_tz, int32_t * width, int32_t * right_tz, int32_t * right_pad) { int width1 = *width, left_pad1, right_pad1; int width2 = *width, left_pad2, right_pad2; pad_repeat_get_scanline_bounds (source_image_width, vx, unit_x, &width1, &left_pad1, &right_pad1); pad_repeat_get_scanline_bounds (source_image_width, vx + pixman_fixed_1, unit_x, &width2, &left_pad2, &right_pad2); *left_pad = left_pad2; *left_tz = left_pad1 - left_pad2; *right_tz = right_pad2 - right_pad1; *right_pad = right_pad1; *width -= *left_pad + *left_tz + *right_tz + *right_pad; } /* * Main loop template for single pass bilinear scaling. It needs to be * provided with 'scanline_func' which should do the compositing operation. * The needed function has the following prototype: * * scanline_func (dst_type_t * dst, * const mask_type_ * mask, * const src_type_t * src_top, * const src_type_t * src_bottom, * int32_t width, * int weight_top, * int weight_bottom, * pixman_fixed_t vx, * pixman_fixed_t unit_x, * pixman_fixed_t max_vx, * pixman_bool_t zero_src) * * Where: * dst - destination scanline buffer for storing results * mask - mask buffer (or single value for solid mask) * src_top, src_bottom - two source scanlines * width - number of pixels to process * weight_top - weight of the top row for interpolation * weight_bottom - weight of the bottom row for interpolation * vx - initial position for fetching the first pair of * pixels from the source buffer * unit_x - position increment needed to move to the next pair * of pixels * max_vx - image size as a fixed point value, can be used for * implementing NORMAL repeat (when it is supported) * zero_src - boolean hint variable, which is set to TRUE when * all source pixels are fetched from zero padding * zone for NONE repeat * * Note: normally the sum of 'weight_top' and 'weight_bottom' is equal to * BILINEAR_INTERPOLATION_RANGE, but sometimes it may be less than that * for NONE repeat when handling fuzzy antialiased top or bottom image * edges. Also both top and bottom weight variables are guaranteed to * have value, which is less than BILINEAR_INTERPOLATION_RANGE. * For example, the weights can fit into unsigned byte or be used * with 8-bit SIMD multiplication instructions for 8-bit interpolation * precision. */ #define FAST_BILINEAR_MAINLOOP_INT(scale_func_name, scanline_func, src_type_t, mask_type_t, \ dst_type_t, repeat_mode, flags) \ static void \ fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type_t *dst_line; \ mask_type_t *mask_line; \ src_type_t *src_first_line; \ int y1, y2; \ pixman_fixed_t max_vx = INT32_MAX; /* suppress uninitialized variable warning */ \ pixman_vector_t v; \ pixman_fixed_t vx, vy; \ pixman_fixed_t unit_x, unit_y; \ int32_t left_pad, left_tz, right_tz, right_pad; \ \ dst_type_t *dst; \ mask_type_t solid_mask; \ const mask_type_t *mask = &solid_mask; \ int src_stride, mask_stride, dst_stride; \ \ int src_width; \ pixman_fixed_t src_width_fixed; \ int max_x; \ pixman_bool_t need_src_extension; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type_t, dst_stride, dst_line, 1); \ if (flags & FLAG_HAVE_SOLID_MASK) \ { \ solid_mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); \ mask_stride = 0; \ } \ else if (flags & FLAG_HAVE_NON_SOLID_MASK) \ { \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type_t, \ mask_stride, mask_line, 1); \ } \ \ /* pass in 0 instead of src_x and src_y because src_x and src_y need to be \ * transformed from destination space to source space */ \ PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, src_type_t, src_stride, src_first_line, 1); \ \ /* reference point is the center of the pixel */ \ v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; \ v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; \ v.vector[2] = pixman_fixed_1; \ \ if (!pixman_transform_point_3d (src_image->common.transform, &v)) \ return; \ \ unit_x = src_image->common.transform->matrix[0][0]; \ unit_y = src_image->common.transform->matrix[1][1]; \ \ v.vector[0] -= pixman_fixed_1 / 2; \ v.vector[1] -= pixman_fixed_1 / 2; \ \ vy = v.vector[1]; \ \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD || \ PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ { \ bilinear_pad_repeat_get_scanline_bounds (src_image->bits.width, v.vector[0], unit_x, \ &left_pad, &left_tz, &width, &right_tz, &right_pad); \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ { \ /* PAD repeat does not need special handling for 'transition zones' and */ \ /* they can be combined with 'padding zones' safely */ \ left_pad += left_tz; \ right_pad += right_tz; \ left_tz = right_tz = 0; \ } \ v.vector[0] += left_pad * unit_x; \ } \ \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ { \ vx = v.vector[0]; \ repeat (PIXMAN_REPEAT_NORMAL, &vx, pixman_int_to_fixed(src_image->bits.width)); \ max_x = pixman_fixed_to_int (vx + (width - 1) * (int64_t)unit_x) + 1; \ \ if (src_image->bits.width < REPEAT_NORMAL_MIN_WIDTH) \ { \ src_width = 0; \ \ while (src_width < REPEAT_NORMAL_MIN_WIDTH && src_width <= max_x) \ src_width += src_image->bits.width; \ \ need_src_extension = TRUE; \ } \ else \ { \ src_width = src_image->bits.width; \ need_src_extension = FALSE; \ } \ \ src_width_fixed = pixman_int_to_fixed (src_width); \ } \ \ while (--height >= 0) \ { \ int weight1, weight2; \ dst = dst_line; \ dst_line += dst_stride; \ vx = v.vector[0]; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ { \ mask = mask_line; \ mask_line += mask_stride; \ } \ \ y1 = pixman_fixed_to_int (vy); \ weight2 = pixman_fixed_to_bilinear_weight (vy); \ if (weight2) \ { \ /* both weight1 and weight2 are smaller than BILINEAR_INTERPOLATION_RANGE */ \ y2 = y1 + 1; \ weight1 = BILINEAR_INTERPOLATION_RANGE - weight2; \ } \ else \ { \ /* set both top and bottom row to the same scanline and tweak weights */ \ y2 = y1; \ weight1 = weight2 = BILINEAR_INTERPOLATION_RANGE / 2; \ } \ vy += unit_y; \ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ { \ src_type_t *src1, *src2; \ src_type_t buf1[2]; \ src_type_t buf2[2]; \ repeat (PIXMAN_REPEAT_PAD, &y1, src_image->bits.height); \ repeat (PIXMAN_REPEAT_PAD, &y2, src_image->bits.height); \ src1 = src_first_line + src_stride * y1; \ src2 = src_first_line + src_stride * y2; \ \ if (left_pad > 0) \ { \ buf1[0] = buf1[1] = src1[0]; \ buf2[0] = buf2[1] = src2[0]; \ scanline_func (dst, mask, \ buf1, buf2, left_pad, weight1, weight2, 0, 0, 0, FALSE); \ dst += left_pad; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += left_pad; \ } \ if (width > 0) \ { \ scanline_func (dst, mask, \ src1, src2, width, weight1, weight2, vx, unit_x, 0, FALSE); \ dst += width; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += width; \ } \ if (right_pad > 0) \ { \ buf1[0] = buf1[1] = src1[src_image->bits.width - 1]; \ buf2[0] = buf2[1] = src2[src_image->bits.width - 1]; \ scanline_func (dst, mask, \ buf1, buf2, right_pad, weight1, weight2, 0, 0, 0, FALSE); \ } \ } \ else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ { \ src_type_t *src1, *src2; \ src_type_t buf1[2]; \ src_type_t buf2[2]; \ /* handle top/bottom zero padding by just setting weights to 0 if needed */ \ if (y1 < 0) \ { \ weight1 = 0; \ y1 = 0; \ } \ if (y1 >= src_image->bits.height) \ { \ weight1 = 0; \ y1 = src_image->bits.height - 1; \ } \ if (y2 < 0) \ { \ weight2 = 0; \ y2 = 0; \ } \ if (y2 >= src_image->bits.height) \ { \ weight2 = 0; \ y2 = src_image->bits.height - 1; \ } \ src1 = src_first_line + src_stride * y1; \ src2 = src_first_line + src_stride * y2; \ \ if (left_pad > 0) \ { \ buf1[0] = buf1[1] = 0; \ buf2[0] = buf2[1] = 0; \ scanline_func (dst, mask, \ buf1, buf2, left_pad, weight1, weight2, 0, 0, 0, TRUE); \ dst += left_pad; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += left_pad; \ } \ if (left_tz > 0) \ { \ buf1[0] = 0; \ buf1[1] = src1[0]; \ buf2[0] = 0; \ buf2[1] = src2[0]; \ scanline_func (dst, mask, \ buf1, buf2, left_tz, weight1, weight2, \ pixman_fixed_frac (vx), unit_x, 0, FALSE); \ dst += left_tz; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += left_tz; \ vx += left_tz * unit_x; \ } \ if (width > 0) \ { \ scanline_func (dst, mask, \ src1, src2, width, weight1, weight2, vx, unit_x, 0, FALSE); \ dst += width; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += width; \ vx += width * unit_x; \ } \ if (right_tz > 0) \ { \ buf1[0] = src1[src_image->bits.width - 1]; \ buf1[1] = 0; \ buf2[0] = src2[src_image->bits.width - 1]; \ buf2[1] = 0; \ scanline_func (dst, mask, \ buf1, buf2, right_tz, weight1, weight2, \ pixman_fixed_frac (vx), unit_x, 0, FALSE); \ dst += right_tz; \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += right_tz; \ } \ if (right_pad > 0) \ { \ buf1[0] = buf1[1] = 0; \ buf2[0] = buf2[1] = 0; \ scanline_func (dst, mask, \ buf1, buf2, right_pad, weight1, weight2, 0, 0, 0, TRUE); \ } \ } \ else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ { \ int32_t num_pixels; \ int32_t width_remain; \ src_type_t * src_line_top; \ src_type_t * src_line_bottom; \ src_type_t buf1[2]; \ src_type_t buf2[2]; \ src_type_t extended_src_line0[REPEAT_NORMAL_MIN_WIDTH*2]; \ src_type_t extended_src_line1[REPEAT_NORMAL_MIN_WIDTH*2]; \ int i, j; \ \ repeat (PIXMAN_REPEAT_NORMAL, &y1, src_image->bits.height); \ repeat (PIXMAN_REPEAT_NORMAL, &y2, src_image->bits.height); \ src_line_top = src_first_line + src_stride * y1; \ src_line_bottom = src_first_line + src_stride * y2; \ \ if (need_src_extension) \ { \ for (i=0; ibits.width; j++, i++) \ { \ extended_src_line0[i] = src_line_top[j]; \ extended_src_line1[i] = src_line_bottom[j]; \ } \ } \ \ src_line_top = &extended_src_line0[0]; \ src_line_bottom = &extended_src_line1[0]; \ } \ \ /* Top & Bottom wrap around buffer */ \ buf1[0] = src_line_top[src_width - 1]; \ buf1[1] = src_line_top[0]; \ buf2[0] = src_line_bottom[src_width - 1]; \ buf2[1] = src_line_bottom[0]; \ \ width_remain = width; \ \ while (width_remain > 0) \ { \ /* We use src_width_fixed because it can make vx in original source range */ \ repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ \ /* Wrap around part */ \ if (pixman_fixed_to_int (vx) == src_width - 1) \ { \ /* for positive unit_x \ * num_pixels = max(n) + 1, where vx + n*unit_x < src_width_fixed \ * \ * vx is in range [0, src_width_fixed - pixman_fixed_e] \ * So we are safe from overflow. \ */ \ num_pixels = ((src_width_fixed - vx - pixman_fixed_e) / unit_x) + 1; \ \ if (num_pixels > width_remain) \ num_pixels = width_remain; \ \ scanline_func (dst, mask, buf1, buf2, num_pixels, \ weight1, weight2, pixman_fixed_frac(vx), \ unit_x, src_width_fixed, FALSE); \ \ width_remain -= num_pixels; \ vx += num_pixels * unit_x; \ dst += num_pixels; \ \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += num_pixels; \ \ repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ } \ \ /* Normal scanline composite */ \ if (pixman_fixed_to_int (vx) != src_width - 1 && width_remain > 0) \ { \ /* for positive unit_x \ * num_pixels = max(n) + 1, where vx + n*unit_x < (src_width_fixed - 1) \ * \ * vx is in range [0, src_width_fixed - pixman_fixed_e] \ * So we are safe from overflow here. \ */ \ num_pixels = ((src_width_fixed - pixman_fixed_1 - vx - pixman_fixed_e) \ / unit_x) + 1; \ \ if (num_pixels > width_remain) \ num_pixels = width_remain; \ \ scanline_func (dst, mask, src_line_top, src_line_bottom, num_pixels, \ weight1, weight2, vx, unit_x, src_width_fixed, FALSE); \ \ width_remain -= num_pixels; \ vx += num_pixels * unit_x; \ dst += num_pixels; \ \ if (flags & FLAG_HAVE_NON_SOLID_MASK) \ mask += num_pixels; \ } \ } \ } \ else \ { \ scanline_func (dst, mask, src_first_line + src_stride * y1, \ src_first_line + src_stride * y2, width, \ weight1, weight2, vx, unit_x, max_vx, FALSE); \ } \ } \ } /* A workaround for old sun studio, see: https://bugs.freedesktop.org/show_bug.cgi?id=32764 */ #define FAST_BILINEAR_MAINLOOP_COMMON(scale_func_name, scanline_func, src_type_t, mask_type_t, \ dst_type_t, repeat_mode, flags) \ FAST_BILINEAR_MAINLOOP_INT(_ ## scale_func_name, scanline_func, src_type_t, mask_type_t,\ dst_type_t, repeat_mode, flags) #define SCALED_BILINEAR_FLAGS \ (FAST_PATH_SCALE_TRANSFORM | \ FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_BILINEAR_FILTER | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_NARROW_FORMAT) #define SIMPLE_BILINEAR_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_BILINEAR_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ } #define SIMPLE_BILINEAR_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ } #define SIMPLE_BILINEAR_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_null, 0, \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ } #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ } #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ } #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ } #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_PAD(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_PAD_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ } #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NONE(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NONE_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ } #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_COVER(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ } #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func) \ { PIXMAN_OP_ ## op, \ PIXMAN_ ## s, \ (SCALED_BILINEAR_FLAGS | \ FAST_PATH_NORMAL_REPEAT | \ FAST_PATH_X_UNIT_POSITIVE), \ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ } /* Prefer the use of 'cover' variant, because it is faster */ #define SIMPLE_BILINEAR_FAST_PATH(op,s,d,func) \ SIMPLE_BILINEAR_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_BILINEAR_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_BILINEAR_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_BILINEAR_FAST_PATH_NORMAL (op,s,d,func) #define SIMPLE_BILINEAR_A8_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_BILINEAR_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_BILINEAR_A8_MASK_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NORMAL (op,s,d,func) #define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_PAD (op,s,d,func), \ SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NORMAL (op,s,d,func) #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-linear-gradient.c000066400000000000000000000165101271037650300277120ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" static pixman_bool_t linear_gradient_is_horizontal (pixman_image_t *image, int x, int y, int width, int height) { linear_gradient_t *linear = (linear_gradient_t *)image; pixman_vector_t v; pixman_fixed_32_32_t l; pixman_fixed_48_16_t dx, dy; double inc; if (image->common.transform) { /* projective transformation */ if (image->common.transform->matrix[2][0] != 0 || image->common.transform->matrix[2][1] != 0 || image->common.transform->matrix[2][2] == 0) { return FALSE; } v.vector[0] = image->common.transform->matrix[0][1]; v.vector[1] = image->common.transform->matrix[1][1]; v.vector[2] = image->common.transform->matrix[2][2]; } else { v.vector[0] = 0; v.vector[1] = pixman_fixed_1; v.vector[2] = pixman_fixed_1; } dx = linear->p2.x - linear->p1.x; dy = linear->p2.y - linear->p1.y; l = dx * dx + dy * dy; if (l == 0) return FALSE; /* * compute how much the input of the gradient walked changes * when moving vertically through the whole image */ inc = height * (double) pixman_fixed_1 * pixman_fixed_1 * (dx * v.vector[0] + dy * v.vector[1]) / (v.vector[2] * (double) l); /* check that casting to integer would result in 0 */ if (-1 < inc && inc < 1) return TRUE; return FALSE; } static uint32_t * linear_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t * buffer = iter->buffer; pixman_vector_t v, unit; pixman_fixed_32_32_t l; pixman_fixed_48_16_t dx, dy; gradient_t *gradient = (gradient_t *)image; linear_gradient_t *linear = (linear_gradient_t *)image; uint32_t *end = buffer + width; pixman_gradient_walker_t walker; _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; if (image->common.transform) { if (!pixman_transform_point_3d (image->common.transform, &v)) return iter->buffer; unit.vector[0] = image->common.transform->matrix[0][0]; unit.vector[1] = image->common.transform->matrix[1][0]; unit.vector[2] = image->common.transform->matrix[2][0]; } else { unit.vector[0] = pixman_fixed_1; unit.vector[1] = 0; unit.vector[2] = 0; } dx = linear->p2.x - linear->p1.x; dy = linear->p2.y - linear->p1.y; l = dx * dx + dy * dy; if (l == 0 || unit.vector[2] == 0) { /* affine transformation only */ pixman_fixed_32_32_t t, next_inc; double inc; if (l == 0 || v.vector[2] == 0) { t = 0; inc = 0; } else { double invden, v2; invden = pixman_fixed_1 * (double) pixman_fixed_1 / (l * (double) v.vector[2]); v2 = v.vector[2] * (1. / pixman_fixed_1); t = ((dx * v.vector[0] + dy * v.vector[1]) - (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; } next_inc = 0; if (((pixman_fixed_32_32_t )(inc * width)) == 0) { register uint32_t color; color = _pixman_gradient_walker_pixel (&walker, t); while (buffer < end) *buffer++ = color; } else { int i; i = 0; while (buffer < end) { if (!mask || *mask++) { *buffer = _pixman_gradient_walker_pixel (&walker, t + next_inc); } i++; next_inc = inc * i; buffer++; } } } else { /* projective transformation */ double t; t = 0; while (buffer < end) { if (!mask || *mask++) { if (v.vector[2] != 0) { double invden, v2; invden = pixman_fixed_1 * (double) pixman_fixed_1 / (l * (double) v.vector[2]); v2 = v.vector[2] * (1. / pixman_fixed_1); t = ((dx * v.vector[0] + dy * v.vector[1]) - (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; } *buffer = _pixman_gradient_walker_pixel (&walker, t); } ++buffer; v.vector[0] += unit.vector[0]; v.vector[1] += unit.vector[1]; v.vector[2] += unit.vector[2]; } } iter->y++; return iter->buffer; } static uint32_t * linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) { uint32_t *buffer = linear_get_scanline_narrow (iter, NULL); pixman_expand_to_float ( (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); return buffer; } void _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) { if (linear_gradient_is_horizontal ( iter->image, iter->x, iter->y, iter->width, iter->height)) { if (iter->iter_flags & ITER_NARROW) linear_get_scanline_narrow (iter, NULL); else linear_get_scanline_wide (iter, NULL); iter->get_scanline = _pixman_iter_get_scanline_noop; } else { if (iter->iter_flags & ITER_NARROW) iter->get_scanline = linear_get_scanline_narrow; else iter->get_scanline = linear_get_scanline_wide; } } PIXMAN_EXPORT pixman_image_t * pixman_image_create_linear_gradient (const pixman_point_fixed_t * p1, const pixman_point_fixed_t * p2, const pixman_gradient_stop_t *stops, int n_stops) { pixman_image_t *image; linear_gradient_t *linear; image = _pixman_image_allocate (); if (!image) return NULL; linear = &image->linear; if (!_pixman_init_gradient (&linear->common, stops, n_stops)) { free (image); return NULL; } linear->p1 = *p1; linear->p2 = *p2; image->type = LINEAR; return image; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-matrix.c000066400000000000000000000702451271037650300261560ustar00rootroot00000000000000/* * Copyright © 2008 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* * Matrix interfaces */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" #define F(x) pixman_int_to_fixed (x) static force_inline int count_leading_zeros (uint32_t x) { #ifdef __GNUC__ return __builtin_clz (x); #else int n = 0; while (x) { n++; x >>= 1; } return 32 - n; #endif } /* * Large signed/unsigned integer division with rounding for the platforms with * only 64-bit integer data type supported (no 128-bit data type). * * Arguments: * hi, lo - high and low 64-bit parts of the dividend * div - 48-bit divisor * * Returns: lowest 64 bits of the result as a return value and highest 64 * bits of the result to "result_hi" pointer */ /* grade-school unsigned division (128-bit by 48-bit) with rounding to nearest */ static force_inline uint64_t rounded_udiv_128_by_48 (uint64_t hi, uint64_t lo, uint64_t div, uint64_t *result_hi) { uint64_t tmp, remainder, result_lo; assert(div < ((uint64_t)1 << 48)); remainder = hi % div; *result_hi = hi / div; tmp = (remainder << 16) + (lo >> 48); result_lo = tmp / div; remainder = tmp % div; tmp = (remainder << 16) + ((lo >> 32) & 0xFFFF); result_lo = (result_lo << 16) + (tmp / div); remainder = tmp % div; tmp = (remainder << 16) + ((lo >> 16) & 0xFFFF); result_lo = (result_lo << 16) + (tmp / div); remainder = tmp % div; tmp = (remainder << 16) + (lo & 0xFFFF); result_lo = (result_lo << 16) + (tmp / div); remainder = tmp % div; /* round to nearest */ if (remainder * 2 >= div && ++result_lo == 0) *result_hi += 1; return result_lo; } /* signed division (128-bit by 49-bit) with rounding to nearest */ static inline int64_t rounded_sdiv_128_by_49 (int64_t hi, uint64_t lo, int64_t div, int64_t *signed_result_hi) { uint64_t result_lo, result_hi; int sign = 0; if (div < 0) { div = -div; sign ^= 1; } if (hi < 0) { if (lo != 0) hi++; hi = -hi; lo = -lo; sign ^= 1; } result_lo = rounded_udiv_128_by_48 (hi, lo, div, &result_hi); if (sign) { if (result_lo != 0) result_hi++; result_hi = -result_hi; result_lo = -result_lo; } if (signed_result_hi) { *signed_result_hi = result_hi; } return result_lo; } /* * Multiply 64.16 fixed point value by (2^scalebits) and convert * to 128-bit integer. */ static force_inline void fixed_64_16_to_int128 (int64_t hi, int64_t lo, int64_t *rhi, int64_t *rlo, int scalebits) { /* separate integer and fractional parts */ hi += lo >> 16; lo &= 0xFFFF; if (scalebits <= 0) { *rlo = hi >> (-scalebits); *rhi = *rlo >> 63; } else { *rhi = hi >> (64 - scalebits); *rlo = (uint64_t)hi << scalebits; if (scalebits < 16) *rlo += lo >> (16 - scalebits); else *rlo += lo << (scalebits - 16); } } /* * Convert 112.16 fixed point value to 48.16 with clamping for the out * of range values. */ static force_inline pixman_fixed_48_16_t fixed_112_16_to_fixed_48_16 (int64_t hi, int64_t lo, pixman_bool_t *clampflag) { if ((lo >> 63) != hi) { *clampflag = TRUE; return hi >= 0 ? INT64_MAX : INT64_MIN; } else { return lo; } } /* * Transform a point with 31.16 fixed point coordinates from the destination * space to a point with 48.16 fixed point coordinates in the source space. * No overflows are possible for affine transformations and the results are * accurate including the least significant bit. Projective transformations * may overflow, in this case the results are just clamped to return maximum * or minimum 48.16 values (so that the caller can at least handle the NONE * and PAD repeats correctly) and the return value is FALSE to indicate that * such clamping has happened. */ PIXMAN_EXPORT pixman_bool_t pixman_transform_point_31_16 (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result) { pixman_bool_t clampflag = FALSE; int i; int64_t tmp[3][2], divint; uint16_t divfrac; /* input vector values must have no more than 31 bits (including sign) * in the integer part */ assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); for (i = 0; i < 3; i++) { tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16); tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF); tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16); tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF); tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16); tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF); } /* * separate 64-bit integer and 16-bit fractional parts for the divisor, * which is also scaled by 65536 after fixed point multiplication. */ divint = tmp[2][0] + (tmp[2][1] >> 16); divfrac = tmp[2][1] & 0xFFFF; if (divint == pixman_fixed_1 && divfrac == 0) { /* * this is a simple affine transformation */ result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); result->v[2] = pixman_fixed_1; } else if (divint == 0 && divfrac == 0) { /* * handle zero divisor (if the values are non-zero, set the * results to maximum positive or minimum negative) */ clampflag = TRUE; result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); if (result->v[0] > 0) result->v[0] = INT64_MAX; else if (result->v[0] < 0) result->v[0] = INT64_MIN; if (result->v[1] > 0) result->v[1] = INT64_MAX; else if (result->v[1] < 0) result->v[1] = INT64_MIN; } else { /* * projective transformation, analyze the top 32 bits of the divisor */ int32_t hi32divbits = divint >> 32; if (hi32divbits < 0) hi32divbits = ~hi32divbits; if (hi32divbits == 0) { /* the divisor is small, we can actually keep all the bits */ int64_t hi, rhi, lo, rlo; int64_t div = (divint << 16) + divfrac; fixed_64_16_to_int128 (tmp[0][0], tmp[0][1], &hi, &lo, 32); rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); result->v[0] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); fixed_64_16_to_int128 (tmp[1][0], tmp[1][1], &hi, &lo, 32); rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); result->v[1] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); } else { /* the divisor needs to be reduced to 48 bits */ int64_t hi, rhi, lo, rlo, div; int shift = 32 - count_leading_zeros (hi32divbits); fixed_64_16_to_int128 (divint, divfrac, &hi, &div, 16 - shift); fixed_64_16_to_int128 (tmp[0][0], tmp[0][1], &hi, &lo, 32 - shift); rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); result->v[0] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); fixed_64_16_to_int128 (tmp[1][0], tmp[1][1], &hi, &lo, 32 - shift); rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); result->v[1] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); } } result->v[2] = pixman_fixed_1; return !clampflag; } PIXMAN_EXPORT void pixman_transform_point_31_16_affine (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result) { int64_t hi0, lo0, hi1, lo1; /* input vector values must have no more than 31 bits (including sign) * in the integer part */ assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); hi0 = (int64_t)t->matrix[0][0] * (v->v[0] >> 16); lo0 = (int64_t)t->matrix[0][0] * (v->v[0] & 0xFFFF); hi0 += (int64_t)t->matrix[0][1] * (v->v[1] >> 16); lo0 += (int64_t)t->matrix[0][1] * (v->v[1] & 0xFFFF); hi0 += (int64_t)t->matrix[0][2]; hi1 = (int64_t)t->matrix[1][0] * (v->v[0] >> 16); lo1 = (int64_t)t->matrix[1][0] * (v->v[0] & 0xFFFF); hi1 += (int64_t)t->matrix[1][1] * (v->v[1] >> 16); lo1 += (int64_t)t->matrix[1][1] * (v->v[1] & 0xFFFF); hi1 += (int64_t)t->matrix[1][2]; result->v[0] = hi0 + ((lo0 + 0x8000) >> 16); result->v[1] = hi1 + ((lo1 + 0x8000) >> 16); result->v[2] = pixman_fixed_1; } PIXMAN_EXPORT void pixman_transform_point_31_16_3d (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result) { int i; int64_t tmp[3][2]; /* input vector values must have no more than 31 bits (including sign) * in the integer part */ assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16))); assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); for (i = 0; i < 3; i++) { tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16); tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF); tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16); tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF); tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16); tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF); } result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); result->v[2] = tmp[2][0] + ((tmp[2][1] + 0x8000) >> 16); } PIXMAN_EXPORT void pixman_transform_init_identity (struct pixman_transform *matrix) { int i; memset (matrix, '\0', sizeof (struct pixman_transform)); for (i = 0; i < 3; i++) matrix->matrix[i][i] = F (1); } typedef pixman_fixed_32_32_t pixman_fixed_34_30_t; PIXMAN_EXPORT pixman_bool_t pixman_transform_point_3d (const struct pixman_transform *transform, struct pixman_vector * vector) { pixman_vector_48_16_t tmp; tmp.v[0] = vector->vector[0]; tmp.v[1] = vector->vector[1]; tmp.v[2] = vector->vector[2]; pixman_transform_point_31_16_3d (transform, &tmp, &tmp); vector->vector[0] = tmp.v[0]; vector->vector[1] = tmp.v[1]; vector->vector[2] = tmp.v[2]; return vector->vector[0] == tmp.v[0] && vector->vector[1] == tmp.v[1] && vector->vector[2] == tmp.v[2]; } PIXMAN_EXPORT pixman_bool_t pixman_transform_point (const struct pixman_transform *transform, struct pixman_vector * vector) { pixman_vector_48_16_t tmp; tmp.v[0] = vector->vector[0]; tmp.v[1] = vector->vector[1]; tmp.v[2] = vector->vector[2]; if (!pixman_transform_point_31_16 (transform, &tmp, &tmp)) return FALSE; vector->vector[0] = tmp.v[0]; vector->vector[1] = tmp.v[1]; vector->vector[2] = tmp.v[2]; return vector->vector[0] == tmp.v[0] && vector->vector[1] == tmp.v[1] && vector->vector[2] == tmp.v[2]; } PIXMAN_EXPORT pixman_bool_t pixman_transform_multiply (struct pixman_transform * dst, const struct pixman_transform *l, const struct pixman_transform *r) { struct pixman_transform d; int dx, dy; int o; for (dy = 0; dy < 3; dy++) { for (dx = 0; dx < 3; dx++) { pixman_fixed_48_16_t v; pixman_fixed_32_32_t partial; v = 0; for (o = 0; o < 3; o++) { partial = (pixman_fixed_32_32_t) l->matrix[dy][o] * (pixman_fixed_32_32_t) r->matrix[o][dx]; v += (partial + 0x8000) >> 16; } if (v > pixman_max_fixed_48_16 || v < pixman_min_fixed_48_16) return FALSE; d.matrix[dy][dx] = (pixman_fixed_t) v; } } *dst = d; return TRUE; } PIXMAN_EXPORT void pixman_transform_init_scale (struct pixman_transform *t, pixman_fixed_t sx, pixman_fixed_t sy) { memset (t, '\0', sizeof (struct pixman_transform)); t->matrix[0][0] = sx; t->matrix[1][1] = sy; t->matrix[2][2] = F (1); } static pixman_fixed_t fixed_inverse (pixman_fixed_t x) { return (pixman_fixed_t) ((((pixman_fixed_48_16_t) F (1)) * F (1)) / x); } PIXMAN_EXPORT pixman_bool_t pixman_transform_scale (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t sx, pixman_fixed_t sy) { struct pixman_transform t; if (sx == 0 || sy == 0) return FALSE; if (forward) { pixman_transform_init_scale (&t, sx, sy); if (!pixman_transform_multiply (forward, &t, forward)) return FALSE; } if (reverse) { pixman_transform_init_scale (&t, fixed_inverse (sx), fixed_inverse (sy)); if (!pixman_transform_multiply (reverse, reverse, &t)) return FALSE; } return TRUE; } PIXMAN_EXPORT void pixman_transform_init_rotate (struct pixman_transform *t, pixman_fixed_t c, pixman_fixed_t s) { memset (t, '\0', sizeof (struct pixman_transform)); t->matrix[0][0] = c; t->matrix[0][1] = -s; t->matrix[1][0] = s; t->matrix[1][1] = c; t->matrix[2][2] = F (1); } PIXMAN_EXPORT pixman_bool_t pixman_transform_rotate (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t c, pixman_fixed_t s) { struct pixman_transform t; if (forward) { pixman_transform_init_rotate (&t, c, s); if (!pixman_transform_multiply (forward, &t, forward)) return FALSE; } if (reverse) { pixman_transform_init_rotate (&t, c, -s); if (!pixman_transform_multiply (reverse, reverse, &t)) return FALSE; } return TRUE; } PIXMAN_EXPORT void pixman_transform_init_translate (struct pixman_transform *t, pixman_fixed_t tx, pixman_fixed_t ty) { memset (t, '\0', sizeof (struct pixman_transform)); t->matrix[0][0] = F (1); t->matrix[0][2] = tx; t->matrix[1][1] = F (1); t->matrix[1][2] = ty; t->matrix[2][2] = F (1); } PIXMAN_EXPORT pixman_bool_t pixman_transform_translate (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t tx, pixman_fixed_t ty) { struct pixman_transform t; if (forward) { pixman_transform_init_translate (&t, tx, ty); if (!pixman_transform_multiply (forward, &t, forward)) return FALSE; } if (reverse) { pixman_transform_init_translate (&t, -tx, -ty); if (!pixman_transform_multiply (reverse, reverse, &t)) return FALSE; } return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_transform_bounds (const struct pixman_transform *matrix, struct pixman_box16 * b) { struct pixman_vector v[4]; int i; int x1, y1, x2, y2; v[0].vector[0] = F (b->x1); v[0].vector[1] = F (b->y1); v[0].vector[2] = F (1); v[1].vector[0] = F (b->x2); v[1].vector[1] = F (b->y1); v[1].vector[2] = F (1); v[2].vector[0] = F (b->x2); v[2].vector[1] = F (b->y2); v[2].vector[2] = F (1); v[3].vector[0] = F (b->x1); v[3].vector[1] = F (b->y2); v[3].vector[2] = F (1); for (i = 0; i < 4; i++) { if (!pixman_transform_point (matrix, &v[i])) return FALSE; x1 = pixman_fixed_to_int (v[i].vector[0]); y1 = pixman_fixed_to_int (v[i].vector[1]); x2 = pixman_fixed_to_int (pixman_fixed_ceil (v[i].vector[0])); y2 = pixman_fixed_to_int (pixman_fixed_ceil (v[i].vector[1])); if (i == 0) { b->x1 = x1; b->y1 = y1; b->x2 = x2; b->y2 = y2; } else { if (x1 < b->x1) b->x1 = x1; if (y1 < b->y1) b->y1 = y1; if (x2 > b->x2) b->x2 = x2; if (y2 > b->y2) b->y2 = y2; } } return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_transform_invert (struct pixman_transform * dst, const struct pixman_transform *src) { struct pixman_f_transform m; pixman_f_transform_from_pixman_transform (&m, src); if (!pixman_f_transform_invert (&m, &m)) return FALSE; if (!pixman_transform_from_pixman_f_transform (dst, &m)) return FALSE; return TRUE; } static pixman_bool_t within_epsilon (pixman_fixed_t a, pixman_fixed_t b, pixman_fixed_t epsilon) { pixman_fixed_t t = a - b; if (t < 0) t = -t; return t <= epsilon; } #define EPSILON (pixman_fixed_t) (2) #define IS_SAME(a, b) (within_epsilon (a, b, EPSILON)) #define IS_ZERO(a) (within_epsilon (a, 0, EPSILON)) #define IS_ONE(a) (within_epsilon (a, F (1), EPSILON)) #define IS_UNIT(a) \ (within_epsilon (a, F (1), EPSILON) || \ within_epsilon (a, F (-1), EPSILON) || \ IS_ZERO (a)) #define IS_INT(a) (IS_ZERO (pixman_fixed_frac (a))) PIXMAN_EXPORT pixman_bool_t pixman_transform_is_identity (const struct pixman_transform *t) { return (IS_SAME (t->matrix[0][0], t->matrix[1][1]) && IS_SAME (t->matrix[0][0], t->matrix[2][2]) && !IS_ZERO (t->matrix[0][0]) && IS_ZERO (t->matrix[0][1]) && IS_ZERO (t->matrix[0][2]) && IS_ZERO (t->matrix[1][0]) && IS_ZERO (t->matrix[1][2]) && IS_ZERO (t->matrix[2][0]) && IS_ZERO (t->matrix[2][1])); } PIXMAN_EXPORT pixman_bool_t pixman_transform_is_scale (const struct pixman_transform *t) { return (!IS_ZERO (t->matrix[0][0]) && IS_ZERO (t->matrix[0][1]) && IS_ZERO (t->matrix[0][2]) && IS_ZERO (t->matrix[1][0]) && !IS_ZERO (t->matrix[1][1]) && IS_ZERO (t->matrix[1][2]) && IS_ZERO (t->matrix[2][0]) && IS_ZERO (t->matrix[2][1]) && !IS_ZERO (t->matrix[2][2])); } PIXMAN_EXPORT pixman_bool_t pixman_transform_is_int_translate (const struct pixman_transform *t) { return (IS_ONE (t->matrix[0][0]) && IS_ZERO (t->matrix[0][1]) && IS_INT (t->matrix[0][2]) && IS_ZERO (t->matrix[1][0]) && IS_ONE (t->matrix[1][1]) && IS_INT (t->matrix[1][2]) && IS_ZERO (t->matrix[2][0]) && IS_ZERO (t->matrix[2][1]) && IS_ONE (t->matrix[2][2])); } PIXMAN_EXPORT pixman_bool_t pixman_transform_is_inverse (const struct pixman_transform *a, const struct pixman_transform *b) { struct pixman_transform t; if (!pixman_transform_multiply (&t, a, b)) return FALSE; return pixman_transform_is_identity (&t); } PIXMAN_EXPORT void pixman_f_transform_from_pixman_transform (struct pixman_f_transform * ft, const struct pixman_transform *t) { int i, j; for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) ft->m[j][i] = pixman_fixed_to_double (t->matrix[j][i]); } } PIXMAN_EXPORT pixman_bool_t pixman_transform_from_pixman_f_transform (struct pixman_transform * t, const struct pixman_f_transform *ft) { int i, j; for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) { double d = ft->m[j][i]; if (d < -32767.0 || d > 32767.0) return FALSE; d = d * 65536.0 + 0.5; t->matrix[j][i] = (pixman_fixed_t) floor (d); } } return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_invert (struct pixman_f_transform * dst, const struct pixman_f_transform *src) { static const int a[3] = { 2, 2, 1 }; static const int b[3] = { 1, 0, 0 }; pixman_f_transform_t d; double det; int i, j; det = 0; for (i = 0; i < 3; i++) { double p; int ai = a[i]; int bi = b[i]; p = src->m[i][0] * (src->m[ai][2] * src->m[bi][1] - src->m[ai][1] * src->m[bi][2]); if (i == 1) p = -p; det += p; } if (det == 0) return FALSE; det = 1 / det; for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) { double p; int ai = a[i]; int aj = a[j]; int bi = b[i]; int bj = b[j]; p = (src->m[ai][aj] * src->m[bi][bj] - src->m[ai][bj] * src->m[bi][aj]); if (((i + j) & 1) != 0) p = -p; d.m[j][i] = det * p; } } *dst = d; return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_point (const struct pixman_f_transform *t, struct pixman_f_vector * v) { struct pixman_f_vector result; int i, j; double a; for (j = 0; j < 3; j++) { a = 0; for (i = 0; i < 3; i++) a += t->m[j][i] * v->v[i]; result.v[j] = a; } if (!result.v[2]) return FALSE; for (j = 0; j < 2; j++) v->v[j] = result.v[j] / result.v[2]; v->v[2] = 1; return TRUE; } PIXMAN_EXPORT void pixman_f_transform_point_3d (const struct pixman_f_transform *t, struct pixman_f_vector * v) { struct pixman_f_vector result; int i, j; double a; for (j = 0; j < 3; j++) { a = 0; for (i = 0; i < 3; i++) a += t->m[j][i] * v->v[i]; result.v[j] = a; } *v = result; } PIXMAN_EXPORT void pixman_f_transform_multiply (struct pixman_f_transform * dst, const struct pixman_f_transform *l, const struct pixman_f_transform *r) { struct pixman_f_transform d; int dx, dy; int o; for (dy = 0; dy < 3; dy++) { for (dx = 0; dx < 3; dx++) { double v = 0; for (o = 0; o < 3; o++) v += l->m[dy][o] * r->m[o][dx]; d.m[dy][dx] = v; } } *dst = d; } PIXMAN_EXPORT void pixman_f_transform_init_scale (struct pixman_f_transform *t, double sx, double sy) { t->m[0][0] = sx; t->m[0][1] = 0; t->m[0][2] = 0; t->m[1][0] = 0; t->m[1][1] = sy; t->m[1][2] = 0; t->m[2][0] = 0; t->m[2][1] = 0; t->m[2][2] = 1; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_scale (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double sx, double sy) { struct pixman_f_transform t; if (sx == 0 || sy == 0) return FALSE; if (forward) { pixman_f_transform_init_scale (&t, sx, sy); pixman_f_transform_multiply (forward, &t, forward); } if (reverse) { pixman_f_transform_init_scale (&t, 1 / sx, 1 / sy); pixman_f_transform_multiply (reverse, reverse, &t); } return TRUE; } PIXMAN_EXPORT void pixman_f_transform_init_rotate (struct pixman_f_transform *t, double c, double s) { t->m[0][0] = c; t->m[0][1] = -s; t->m[0][2] = 0; t->m[1][0] = s; t->m[1][1] = c; t->m[1][2] = 0; t->m[2][0] = 0; t->m[2][1] = 0; t->m[2][2] = 1; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_rotate (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double c, double s) { struct pixman_f_transform t; if (forward) { pixman_f_transform_init_rotate (&t, c, s); pixman_f_transform_multiply (forward, &t, forward); } if (reverse) { pixman_f_transform_init_rotate (&t, c, -s); pixman_f_transform_multiply (reverse, reverse, &t); } return TRUE; } PIXMAN_EXPORT void pixman_f_transform_init_translate (struct pixman_f_transform *t, double tx, double ty) { t->m[0][0] = 1; t->m[0][1] = 0; t->m[0][2] = tx; t->m[1][0] = 0; t->m[1][1] = 1; t->m[1][2] = ty; t->m[2][0] = 0; t->m[2][1] = 0; t->m[2][2] = 1; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_translate (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double tx, double ty) { struct pixman_f_transform t; if (forward) { pixman_f_transform_init_translate (&t, tx, ty); pixman_f_transform_multiply (forward, &t, forward); } if (reverse) { pixman_f_transform_init_translate (&t, -tx, -ty); pixman_f_transform_multiply (reverse, reverse, &t); } return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_f_transform_bounds (const struct pixman_f_transform *t, struct pixman_box16 * b) { struct pixman_f_vector v[4]; int i; int x1, y1, x2, y2; v[0].v[0] = b->x1; v[0].v[1] = b->y1; v[0].v[2] = 1; v[1].v[0] = b->x2; v[1].v[1] = b->y1; v[1].v[2] = 1; v[2].v[0] = b->x2; v[2].v[1] = b->y2; v[2].v[2] = 1; v[3].v[0] = b->x1; v[3].v[1] = b->y2; v[3].v[2] = 1; for (i = 0; i < 4; i++) { if (!pixman_f_transform_point (t, &v[i])) return FALSE; x1 = floor (v[i].v[0]); y1 = floor (v[i].v[1]); x2 = ceil (v[i].v[0]); y2 = ceil (v[i].v[1]); if (i == 0) { b->x1 = x1; b->y1 = y1; b->x2 = x2; b->y2 = y2; } else { if (x1 < b->x1) b->x1 = x1; if (y1 < b->y1) b->y1 = y1; if (x2 > b->x2) b->x2 = x2; if (y2 > b->y2) b->y2 = y2; } } return TRUE; } PIXMAN_EXPORT void pixman_f_transform_init_identity (struct pixman_f_transform *t) { int i, j; for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) t->m[j][i] = i == j ? 1 : 0; } } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips-dspr2-asm.S000066400000000000000000003536251271037650300274160ustar00rootroot00000000000000/* * Copyright (c) 2012 * MIPS Technologies, Inc., California. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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. * * Author: Nemanja Lukic (nlukic@mips.com) */ #include "pixman-private.h" #include "pixman-mips-dspr2-asm.h" LEAF_MIPS_DSPR2(pixman_fill_buff16_mips) /* * a0 - *dest * a1 - count (bytes) * a2 - value to fill buffer with */ beqz a1, 3f andi t1, a0, 0x0002 beqz t1, 0f /* check if address is 4-byte aligned */ nop sh a2, 0(a0) addiu a0, a0, 2 addiu a1, a1, -2 0: srl t1, a1, 5 /* t1 how many multiples of 32 bytes */ replv.ph a2, a2 /* replicate fill value (16bit) in a2 */ beqz t1, 2f nop 1: addiu t1, t1, -1 beqz t1, 11f addiu a1, a1, -32 pref 30, 32(a0) sw a2, 0(a0) sw a2, 4(a0) sw a2, 8(a0) sw a2, 12(a0) sw a2, 16(a0) sw a2, 20(a0) sw a2, 24(a0) sw a2, 28(a0) b 1b addiu a0, a0, 32 11: sw a2, 0(a0) sw a2, 4(a0) sw a2, 8(a0) sw a2, 12(a0) sw a2, 16(a0) sw a2, 20(a0) sw a2, 24(a0) sw a2, 28(a0) addiu a0, a0, 32 2: blez a1, 3f addiu a1, a1, -2 sh a2, 0(a0) b 2b addiu a0, a0, 2 3: jr ra nop END(pixman_fill_buff16_mips) LEAF_MIPS32R2(pixman_fill_buff32_mips) /* * a0 - *dest * a1 - count (bytes) * a2 - value to fill buffer with */ beqz a1, 3f nop srl t1, a1, 5 /* t1 how many multiples of 32 bytes */ beqz t1, 2f nop 1: addiu t1, t1, -1 beqz t1, 11f addiu a1, a1, -32 pref 30, 32(a0) sw a2, 0(a0) sw a2, 4(a0) sw a2, 8(a0) sw a2, 12(a0) sw a2, 16(a0) sw a2, 20(a0) sw a2, 24(a0) sw a2, 28(a0) b 1b addiu a0, a0, 32 11: sw a2, 0(a0) sw a2, 4(a0) sw a2, 8(a0) sw a2, 12(a0) sw a2, 16(a0) sw a2, 20(a0) sw a2, 24(a0) sw a2, 28(a0) addiu a0, a0, 32 2: blez a1, 3f addiu a1, a1, -4 sw a2, 0(a0) b 2b addiu a0, a0, 4 3: jr ra nop END(pixman_fill_buff32_mips) LEAF_MIPS_DSPR2(pixman_composite_src_8888_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - w */ beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop li t4, 0xf800f800 li t5, 0x07e007e0 li t6, 0x001f001f 1: lw t0, 0(a1) lw t1, 4(a1) addiu a1, a1, 8 addiu a2, a2, -2 CONVERT_2x8888_TO_2x0565 t0, t1, t2, t3, t4, t5, t6, t7, t8 sh t2, 0(a0) sh t3, 2(a0) addiu t2, a2, -1 bgtz t2, 1b addiu a0, a0, 4 2: beqz a2, 3f nop lw t0, 0(a1) CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 sh t1, 0(a0) 3: j ra nop END(pixman_composite_src_8888_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_0565_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (r5g6b5) * a2 - w */ beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop li t4, 0x07e007e0 li t5, 0x001F001F 1: lhu t0, 0(a1) lhu t1, 2(a1) addiu a1, a1, 4 addiu a2, a2, -2 CONVERT_2x0565_TO_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 sw t2, 0(a0) sw t3, 4(a0) addiu t2, a2, -1 bgtz t2, 1b addiu a0, a0, 8 2: beqz a2, 3f nop lhu t0, 0(a1) CONVERT_1x0565_TO_1x8888 t0, t1, t2, t3 sw t1, 0(a0) 3: j ra nop END(pixman_composite_src_0565_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_x888_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (x8r8g8b8) * a2 - w */ beqz a2, 4f nop li t9, 0xff000000 srl t8, a2, 3 /* t1 = how many multiples of 8 src pixels */ beqz t8, 3f /* branch if less than 8 src pixels */ nop 1: addiu t8, t8, -1 beqz t8, 2f addiu a2, a2, -8 pref 0, 32(a1) lw t0, 0(a1) lw t1, 4(a1) lw t2, 8(a1) lw t3, 12(a1) lw t4, 16(a1) lw t5, 20(a1) lw t6, 24(a1) lw t7, 28(a1) addiu a1, a1, 32 or t0, t0, t9 or t1, t1, t9 or t2, t2, t9 or t3, t3, t9 or t4, t4, t9 or t5, t5, t9 or t6, t6, t9 or t7, t7, t9 pref 30, 32(a0) sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) b 1b addiu a0, a0, 32 2: lw t0, 0(a1) lw t1, 4(a1) lw t2, 8(a1) lw t3, 12(a1) lw t4, 16(a1) lw t5, 20(a1) lw t6, 24(a1) lw t7, 28(a1) addiu a1, a1, 32 or t0, t0, t9 or t1, t1, t9 or t2, t2, t9 or t3, t3, t9 or t4, t4, t9 or t5, t5, t9 or t6, t6, t9 or t7, t7, t9 sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) beqz a2, 4f addiu a0, a0, 32 3: lw t0, 0(a1) addiu a1, a1, 4 addiu a2, a2, -1 or t1, t0, t9 sw t1, 0(a0) bnez a2, 3b addiu a0, a0, 4 4: jr ra nop END(pixman_composite_src_x888_8888_asm_mips) #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) LEAF_MIPS_DSPR2(pixman_composite_src_0888_8888_rev_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (b8g8r8) * a2 - w */ beqz a2, 6f nop lui t8, 0xff00; srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ beqz t9, 4f /* branch if less than 4 src pixels */ nop li t0, 0x1 li t1, 0x2 li t2, 0x3 andi t3, a1, 0x3 beq t3, t0, 1f nop beq t3, t1, 2f nop beq t3, t2, 3f nop 0: beqz t9, 4f addiu t9, t9, -1 lw t0, 0(a1) /* t0 = R2 | B1 | G1 | R1 */ lw t1, 4(a1) /* t1 = G3 | R3 | B2 | G2 */ lw t2, 8(a1) /* t2 = B4 | G4 | R4 | B3 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = B1 | R2 | R1 | G1 */ wsbh t1, t1 /* t1 = R3 | G3 | G2 | B2 */ wsbh t2, t2 /* t2 = G4 | B4 | B3 | R4 */ packrl.ph t3, t1, t0 /* t3 = G2 | B2 | B1 | R2 */ packrl.ph t4, t0, t0 /* t4 = R1 | G1 | B1 | R2 */ rotr t3, t3, 16 /* t3 = B1 | R2 | G2 | B2 */ or t3, t3, t8 /* t3 = FF | R2 | G2 | B2 */ srl t4, t4, 8 /* t4 = 0 | R1 | G1 | B1 */ or t4, t4, t8 /* t4 = FF | R1 | G1 | B1 */ packrl.ph t5, t2, t1 /* t5 = B3 | R4 | R3 | G3 */ rotr t5, t5, 24 /* t5 = R4 | R3 | G3 | B3 */ or t5, t5, t8 /* t5 = FF | R3 | G3 | B3 */ rotr t2, t2, 16 /* t2 = B3 | R4 | G4 | B4 */ or t2, t2, t8 /* t5 = FF | R3 | G3 | B3 */ sw t4, 0(a0) sw t3, 4(a0) sw t5, 8(a0) sw t2, 12(a0) b 0b addiu a0, a0, 16 1: lbu t6, 0(a1) /* t6 = 0 | 0 | 0 | R1 */ lhu t7, 1(a1) /* t7 = 0 | 0 | B1 | G1 */ sll t6, t6, 16 /* t6 = 0 | R1 | 0 | 0 */ wsbh t7, t7 /* t7 = 0 | 0 | G1 | B1 */ or t7, t6, t7 /* t7 = 0 | R1 | G1 | B1 */ 11: beqz t9, 4f addiu t9, t9, -1 lw t0, 3(a1) /* t0 = R3 | B2 | G2 | R2 */ lw t1, 7(a1) /* t1 = G4 | R4 | B3 | G3 */ lw t2, 11(a1) /* t2 = B5 | G5 | R5 | B4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = B2 | R3 | R2 | G2 */ wsbh t1, t1 /* t1 = R4 | G4 | G3 | B3 */ wsbh t2, t2 /* t2 = G5 | B5 | B4 | R5 */ packrl.ph t3, t1, t0 /* t3 = G3 | B3 | B2 | R3 */ packrl.ph t4, t2, t1 /* t4 = B4 | R5 | R4 | G4 */ rotr t0, t0, 24 /* t0 = R3 | R2 | G2 | B2 */ rotr t3, t3, 16 /* t3 = B2 | R3 | G3 | B3 */ rotr t4, t4, 24 /* t4 = R5 | R4 | G4 | B4 */ or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ or t0, t0, t8 /* t0 = FF | R2 | G2 | B2 */ or t3, t3, t8 /* t1 = FF | R3 | G3 | B3 */ or t4, t4, t8 /* t3 = FF | R4 | G4 | B4 */ sw t7, 0(a0) sw t0, 4(a0) sw t3, 8(a0) sw t4, 12(a0) rotr t7, t2, 16 /* t7 = xx | R5 | G5 | B5 */ b 11b addiu a0, a0, 16 2: lhu t7, 0(a1) /* t7 = 0 | 0 | G1 | R1 */ wsbh t7, t7 /* t7 = 0 | 0 | R1 | G1 */ 21: beqz t9, 4f addiu t9, t9, -1 lw t0, 2(a1) /* t0 = B2 | G2 | R2 | B1 */ lw t1, 6(a1) /* t1 = R4 | B3 | G3 | R3 */ lw t2, 10(a1) /* t2 = G5 | R5 | B4 | G4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = G2 | B2 | B1 | R2 */ wsbh t1, t1 /* t1 = B3 | R4 | R3 | G3 */ wsbh t2, t2 /* t2 = R5 | G5 | G4 | B4 */ precr_sra.ph.w t7, t0, 0 /* t7 = R1 | G1 | B1 | R2 */ rotr t0, t0, 16 /* t0 = B1 | R2 | G2 | B2 */ packrl.ph t3, t2, t1 /* t3 = G4 | B4 | B3 | R4 */ rotr t1, t1, 24 /* t1 = R4 | R3 | G3 | B3 */ srl t7, t7, 8 /* t7 = 0 | R1 | G1 | B1 */ rotr t3, t3, 16 /* t3 = B3 | R4 | G4 | B4 */ or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ or t0, t0, t8 /* t0 = FF | R2 | G2 | B2 */ or t1, t1, t8 /* t1 = FF | R3 | G3 | B3 */ or t3, t3, t8 /* t3 = FF | R4 | G4 | B4 */ sw t7, 0(a0) sw t0, 4(a0) sw t1, 8(a0) sw t3, 12(a0) srl t7, t2, 16 /* t7 = 0 | 0 | R5 | G5 */ b 21b addiu a0, a0, 16 3: lbu t7, 0(a1) /* t7 = 0 | 0 | 0 | R1 */ 31: beqz t9, 4f addiu t9, t9, -1 lw t0, 1(a1) /* t0 = G2 | R2 | B1 | G1 */ lw t1, 5(a1) /* t1 = B3 | G3 | R3 | B2 */ lw t2, 9(a1) /* t2 = R5 | B4 | G4 | R4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = R2 | G2 | G1 | B1 */ wsbh t1, t1 /* t1 = G3 | B3 | B2 | R3 */ wsbh t2, t2 /* t2 = B4 | R5 | R4 | G4 */ precr_sra.ph.w t7, t0, 0 /* t7 = xx | R1 | G1 | B1 */ packrl.ph t3, t1, t0 /* t3 = B2 | R3 | R2 | G2 */ rotr t1, t1, 16 /* t1 = B2 | R3 | G3 | B3 */ rotr t4, t2, 24 /* t4 = R5 | R4 | G4 | B4 */ rotr t3, t3, 24 /* t3 = R3 | R2 | G2 | B2 */ or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ or t3, t3, t8 /* t3 = FF | R2 | G2 | B2 */ or t1, t1, t8 /* t1 = FF | R3 | G3 | B3 */ or t4, t4, t8 /* t4 = FF | R4 | G4 | B4 */ sw t7, 0(a0) sw t3, 4(a0) sw t1, 8(a0) sw t4, 12(a0) srl t7, t2, 16 /* t7 = 0 | 0 | xx | R5 */ b 31b addiu a0, a0, 16 4: beqz a2, 6f nop 5: lbu t0, 0(a1) /* t0 = 0 | 0 | 0 | R */ lbu t1, 1(a1) /* t1 = 0 | 0 | 0 | G */ lbu t2, 2(a1) /* t2 = 0 | 0 | 0 | B */ addiu a1, a1, 3 sll t0, t0, 16 /* t2 = 0 | R | 0 | 0 */ sll t1, t1, 8 /* t1 = 0 | 0 | G | 0 */ or t2, t2, t1 /* t2 = 0 | 0 | G | B */ or t2, t2, t0 /* t2 = 0 | R | G | B */ or t2, t2, t8 /* t2 = FF | R | G | B */ sw t2, 0(a0) addiu a2, a2, -1 bnez a2, 5b addiu a0, a0, 4 6: j ra nop END(pixman_composite_src_0888_8888_rev_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_0888_0565_rev_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (b8g8r8) * a2 - w */ SAVE_REGS_ON_STACK 0, v0, v1 beqz a2, 6f nop li t6, 0xf800f800 li t7, 0x07e007e0 li t8, 0x001F001F srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ beqz t9, 4f /* branch if less than 4 src pixels */ nop li t0, 0x1 li t1, 0x2 li t2, 0x3 andi t3, a1, 0x3 beq t3, t0, 1f nop beq t3, t1, 2f nop beq t3, t2, 3f nop 0: beqz t9, 4f addiu t9, t9, -1 lw t0, 0(a1) /* t0 = R2 | B1 | G1 | R1 */ lw t1, 4(a1) /* t1 = G3 | R3 | B2 | G2 */ lw t2, 8(a1) /* t2 = B4 | G4 | R4 | B3 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = B1 | R2 | R1 | G1 */ wsbh t1, t1 /* t1 = R3 | G3 | G2 | B2 */ wsbh t2, t2 /* t2 = G4 | B4 | B3 | R4 */ packrl.ph t3, t1, t0 /* t3 = G2 | B2 | B1 | R2 */ packrl.ph t4, t0, t0 /* t4 = R1 | G1 | B1 | R2 */ rotr t3, t3, 16 /* t3 = B1 | R2 | G2 | B2 */ srl t4, t4, 8 /* t4 = 0 | R1 | G1 | B1 */ packrl.ph t5, t2, t1 /* t5 = B3 | R4 | R3 | G3 */ rotr t5, t5, 24 /* t5 = R4 | R3 | G3 | B3 */ rotr t2, t2, 16 /* t2 = B3 | R4 | G4 | B4 */ CONVERT_2x8888_TO_2x0565 t4, t3, t4, t3, t6, t7, t8, v0, v1 CONVERT_2x8888_TO_2x0565 t5, t2, t5, t2, t6, t7, t8, v0, v1 sh t4, 0(a0) sh t3, 2(a0) sh t5, 4(a0) sh t2, 6(a0) b 0b addiu a0, a0, 8 1: lbu t4, 0(a1) /* t4 = 0 | 0 | 0 | R1 */ lhu t5, 1(a1) /* t5 = 0 | 0 | B1 | G1 */ sll t4, t4, 16 /* t4 = 0 | R1 | 0 | 0 */ wsbh t5, t5 /* t5 = 0 | 0 | G1 | B1 */ or t5, t4, t5 /* t5 = 0 | R1 | G1 | B1 */ 11: beqz t9, 4f addiu t9, t9, -1 lw t0, 3(a1) /* t0 = R3 | B2 | G2 | R2 */ lw t1, 7(a1) /* t1 = G4 | R4 | B3 | G3 */ lw t2, 11(a1) /* t2 = B5 | G5 | R5 | B4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = B2 | R3 | R2 | G2 */ wsbh t1, t1 /* t1 = R4 | G4 | G3 | B3 */ wsbh t2, t2 /* t2 = G5 | B5 | B4 | R5 */ packrl.ph t3, t1, t0 /* t3 = G3 | B3 | B2 | R3 */ packrl.ph t4, t2, t1 /* t4 = B4 | R5 | R4 | G4 */ rotr t0, t0, 24 /* t0 = R3 | R2 | G2 | B2 */ rotr t3, t3, 16 /* t3 = B2 | R3 | G3 | B3 */ rotr t4, t4, 24 /* t4 = R5 | R4 | G4 | B4 */ CONVERT_2x8888_TO_2x0565 t5, t0, t5, t0, t6, t7, t8, v0, v1 CONVERT_2x8888_TO_2x0565 t3, t4, t3, t4, t6, t7, t8, v0, v1 sh t5, 0(a0) sh t0, 2(a0) sh t3, 4(a0) sh t4, 6(a0) rotr t5, t2, 16 /* t5 = xx | R5 | G5 | B5 */ b 11b addiu a0, a0, 8 2: lhu t5, 0(a1) /* t5 = 0 | 0 | G1 | R1 */ wsbh t5, t5 /* t5 = 0 | 0 | R1 | G1 */ 21: beqz t9, 4f addiu t9, t9, -1 lw t0, 2(a1) /* t0 = B2 | G2 | R2 | B1 */ lw t1, 6(a1) /* t1 = R4 | B3 | G3 | R3 */ lw t2, 10(a1) /* t2 = G5 | R5 | B4 | G4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = G2 | B2 | B1 | R2 */ wsbh t1, t1 /* t1 = B3 | R4 | R3 | G3 */ wsbh t2, t2 /* t2 = R5 | G5 | G4 | B4 */ precr_sra.ph.w t5, t0, 0 /* t5 = R1 | G1 | B1 | R2 */ rotr t0, t0, 16 /* t0 = B1 | R2 | G2 | B2 */ packrl.ph t3, t2, t1 /* t3 = G4 | B4 | B3 | R4 */ rotr t1, t1, 24 /* t1 = R4 | R3 | G3 | B3 */ srl t5, t5, 8 /* t5 = 0 | R1 | G1 | B1 */ rotr t3, t3, 16 /* t3 = B3 | R4 | G4 | B4 */ CONVERT_2x8888_TO_2x0565 t5, t0, t5, t0, t6, t7, t8, v0, v1 CONVERT_2x8888_TO_2x0565 t1, t3, t1, t3, t6, t7, t8, v0, v1 sh t5, 0(a0) sh t0, 2(a0) sh t1, 4(a0) sh t3, 6(a0) srl t5, t2, 16 /* t5 = 0 | 0 | R5 | G5 */ b 21b addiu a0, a0, 8 3: lbu t5, 0(a1) /* t5 = 0 | 0 | 0 | R1 */ 31: beqz t9, 4f addiu t9, t9, -1 lw t0, 1(a1) /* t0 = G2 | R2 | B1 | G1 */ lw t1, 5(a1) /* t1 = B3 | G3 | R3 | B2 */ lw t2, 9(a1) /* t2 = R5 | B4 | G4 | R4 */ addiu a1, a1, 12 addiu a2, a2, -4 wsbh t0, t0 /* t0 = R2 | G2 | G1 | B1 */ wsbh t1, t1 /* t1 = G3 | B3 | B2 | R3 */ wsbh t2, t2 /* t2 = B4 | R5 | R4 | G4 */ precr_sra.ph.w t5, t0, 0 /* t5 = xx | R1 | G1 | B1 */ packrl.ph t3, t1, t0 /* t3 = B2 | R3 | R2 | G2 */ rotr t1, t1, 16 /* t1 = B2 | R3 | G3 | B3 */ rotr t4, t2, 24 /* t4 = R5 | R4 | G4 | B4 */ rotr t3, t3, 24 /* t3 = R3 | R2 | G2 | B2 */ CONVERT_2x8888_TO_2x0565 t5, t3, t5, t3, t6, t7, t8, v0, v1 CONVERT_2x8888_TO_2x0565 t1, t4, t1, t4, t6, t7, t8, v0, v1 sh t5, 0(a0) sh t3, 2(a0) sh t1, 4(a0) sh t4, 6(a0) srl t5, t2, 16 /* t5 = 0 | 0 | xx | R5 */ b 31b addiu a0, a0, 8 4: beqz a2, 6f nop 5: lbu t0, 0(a1) /* t0 = 0 | 0 | 0 | R */ lbu t1, 1(a1) /* t1 = 0 | 0 | 0 | G */ lbu t2, 2(a1) /* t2 = 0 | 0 | 0 | B */ addiu a1, a1, 3 sll t0, t0, 16 /* t2 = 0 | R | 0 | 0 */ sll t1, t1, 8 /* t1 = 0 | 0 | G | 0 */ or t2, t2, t1 /* t2 = 0 | 0 | G | B */ or t2, t2, t0 /* t2 = 0 | R | G | B */ CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 sh t3, 0(a0) addiu a2, a2, -1 bnez a2, 5b addiu a0, a0, 2 6: RESTORE_REGS_FROM_STACK 0, v0, v1 j ra nop END(pixman_composite_src_0888_0565_rev_asm_mips) #endif LEAF_MIPS_DSPR2(pixman_composite_src_pixbuf_8888_asm_mips) /* * a0 - dst (a8b8g8r8) * a1 - src (a8r8g8b8) * a2 - w */ SAVE_REGS_ON_STACK 0, v0 li v0, 0x00ff00ff beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: lw t0, 0(a1) lw t1, 4(a1) addiu a1, a1, 8 addiu a2, a2, -2 srl t2, t0, 24 srl t3, t1, 24 MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t2, t3, t0, t1, v0, t4, t5, t6, t7, t8, t9 sll t0, t0, 8 sll t1, t1, 8 andi t2, t2, 0xff andi t3, t3, 0xff or t0, t0, t2 or t1, t1, t3 wsbh t0, t0 wsbh t1, t1 rotr t0, t0, 16 rotr t1, t1, 16 sw t0, 0(a0) sw t1, 4(a0) addiu t2, a2, -1 bgtz t2, 1b addiu a0, a0, 8 2: beqz a2, 3f nop lw t0, 0(a1) srl t1, t0, 24 MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t3, t4, t5 sll t0, t0, 8 andi t1, t1, 0xff or t0, t0, t1 wsbh t0, t0 rotr t0, t0, 16 sw t0, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_composite_src_pixbuf_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_rpixbuf_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - w */ SAVE_REGS_ON_STACK 0, v0 li v0, 0x00ff00ff beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: lw t0, 0(a1) lw t1, 4(a1) addiu a1, a1, 8 addiu a2, a2, -2 srl t2, t0, 24 srl t3, t1, 24 MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t2, t3, t0, t1, v0, t4, t5, t6, t7, t8, t9 sll t0, t0, 8 sll t1, t1, 8 andi t2, t2, 0xff andi t3, t3, 0xff or t0, t0, t2 or t1, t1, t3 rotr t0, t0, 8 rotr t1, t1, 8 sw t0, 0(a0) sw t1, 4(a0) addiu t2, a2, -1 bgtz t2, 1b addiu a0, a0, 8 2: beqz a2, 3f nop lw t0, 0(a1) srl t1, t0, 24 MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t3, t4, t5 sll t0, t0, 8 andi t1, t1, 0xff or t0, t0, t1 rotr t0, t0, 8 sw t0, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_composite_src_rpixbuf_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_n_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, v0 li v0, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: /* a1 = source (32bit constant) */ lbu t0, 0(a2) /* t2 = mask (a8) */ lbu t1, 1(a2) /* t3 = mask (a8) */ addiu a2, a2, 2 MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, t2, t3, v0, t4, t5, t6, t7, t8, t9 sw t2, 0(a0) sw t3, 4(a0) addiu a3, a3, -2 addiu t2, a3, -1 bgtz t2, 1b addiu a0, a0, 8 beqz a3, 3f nop 2: lbu t0, 0(a2) addiu a2, a2, 1 MIPS_UN8x4_MUL_UN8 a1, t0, t1, v0, t3, t4, t5 sw t1, 0(a0) addiu a3, a3, -1 addiu a0, a0, 4 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_composite_src_n_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_src_n_8_8_asm_mips) /* * a0 - dst (a8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ li t9, 0x00ff00ff beqz a3, 3f nop srl t7, a3, 2 /* t7 = how many multiples of 4 dst pixels */ beqz t7, 1f /* branch if less than 4 src pixels */ nop srl t8, a1, 24 replv.ph t8, t8 0: beqz t7, 1f addiu t7, t7, -1 lbu t0, 0(a2) lbu t1, 1(a2) lbu t2, 2(a2) lbu t3, 3(a2) addiu a2, a2, 4 precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr.qb.ph t0, t3, t1 muleu_s.ph.qbl t2, t0, t8 muleu_s.ph.qbr t3, t0, t8 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t2, t2, t3 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a3, a3, -4 b 0b addiu a0, a0, 4 1: beqz a3, 3f nop srl t8, a1, 24 2: lbu t0, 0(a2) addiu a2, a2, 1 mul t2, t0, t8 shra_r.ph t3, t2, 8 andi t3, t3, 0x00ff addq.ph t2, t2, t3 shra_r.ph t2, t2, 8 sb t2, 0(a0) addiu a3, a3, -1 bnez a3, 2b addiu a0, a0, 1 3: j ra nop END(pixman_composite_src_n_8_8_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_8888_ca_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - mask (a8r8g8b8) * a3 - w */ beqz a3, 8f nop SAVE_REGS_ON_STACK 8, s0, s1, s2, s3, s4, s5 li t6, 0xff addiu t7, zero, -1 /* t7 = 0xffffffff */ srl t8, a1, 24 /* t8 = srca */ li t9, 0x00ff00ff addiu t1, a3, -1 beqz t1, 4f /* last pixel */ nop 0: lw t0, 0(a2) /* t0 = mask */ lw t1, 4(a2) /* t1 = mask */ addiu a3, a3, -2 /* w = w - 2 */ or t2, t0, t1 beqz t2, 3f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 8 and t2, t0, t1 beq t2, t7, 1f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */ nop //if(ma) lw t2, 0(a0) /* t2 = dst */ lw t3, 4(a0) /* t3 = dst */ MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, t4, t5, t9, s0, s1, s2, s3, s4, s5 MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t8, t8, t0, t1, t9, s0, s1, s2, s3, s4, s5 not t0, t0 not t1, t1 MIPS_2xUN8x4_MUL_2xUN8x4 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 addu_s.qb t2, t4, t2 addu_s.qb t3, t5, t3 sw t2, 0(a0) sw t3, 4(a0) addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 8 b 4f nop 1: //if (t0 == 0xffffffff) && (t1 == 0xffffffff): beq t8, t6, 2f /* if (srca == 0xff) */ nop lw t2, 0(a0) /* t2 = dst */ lw t3, 4(a0) /* t3 = dst */ not t0, a1 not t1, a1 srl t0, t0, 24 srl t1, t1, 24 MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 addu_s.qb t2, a1, t2 addu_s.qb t3, a1, t3 sw t2, 0(a0) sw t3, 4(a0) addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 8 b 4f nop 2: sw a1, 0(a0) sw a1, 4(a0) 3: addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 8 4: beqz a3, 7f nop /* a1 = src */ lw t0, 0(a2) /* t0 = mask */ beqz t0, 7f /* if (t0 == 0) */ nop beq t0, t7, 5f /* if (t0 == 0xffffffff) */ nop //if(ma) lw t1, 0(a0) /* t1 = dst */ MIPS_UN8x4_MUL_UN8x4 a1, t0, t2, t9, t3, t4, t5, s0 MIPS_UN8x4_MUL_UN8 t0, t8, t0, t9, t3, t4, t5 not t0, t0 MIPS_UN8x4_MUL_UN8x4 t1, t0, t1, t9, t3, t4, t5, s0 addu_s.qb t1, t2, t1 sw t1, 0(a0) RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 j ra nop 5: //if (t0 == 0xffffffff) beq t8, t6, 6f /* if (srca == 0xff) */ nop lw t1, 0(a0) /* t1 = dst */ not t0, a1 srl t0, t0, 24 MIPS_UN8x4_MUL_UN8 t1, t0, t1, t9, t2, t3, t4 addu_s.qb t1, a1, t1 sw t1, 0(a0) RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 j ra nop 6: sw a1, 0(a0) 7: RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 8: j ra nop END(pixman_composite_over_n_8888_8888_ca_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_0565_ca_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (32bit constant) * a2 - mask (a8r8g8b8) * a3 - w */ beqz a3, 8f nop SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 li t6, 0xff addiu t7, zero, -1 /* t7 = 0xffffffff */ srl t8, a1, 24 /* t8 = srca */ li t9, 0x00ff00ff li s6, 0xf800f800 li s7, 0x07e007e0 li s8, 0x001F001F addiu t1, a3, -1 beqz t1, 4f /* last pixel */ nop 0: lw t0, 0(a2) /* t0 = mask */ lw t1, 4(a2) /* t1 = mask */ addiu a3, a3, -2 /* w = w - 2 */ or t2, t0, t1 beqz t2, 3f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 8 and t2, t0, t1 beq t2, t7, 1f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */ nop //if(ma) lhu t2, 0(a0) /* t2 = dst */ lhu t3, 2(a0) /* t3 = dst */ MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, t4, t5, t9, s0, s1, s2, s3, s4, s5 MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t8, t8, t0, t1, t9, s0, s1, s2, s3, s4, s5 not t0, t0 not t1, t1 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, s7, s8, s0, s1, s2, s3 MIPS_2xUN8x4_MUL_2xUN8x4 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 addu_s.qb t2, t4, t2 addu_s.qb t3, t5, t3 CONVERT_2x8888_TO_2x0565 t2, t3, t2, t3, s6, s7, s8, s0, s1 sh t2, 0(a0) sh t3, 2(a0) addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 4 b 4f nop 1: //if (t0 == 0xffffffff) && (t1 == 0xffffffff): beq t8, t6, 2f /* if (srca == 0xff) */ nop lhu t2, 0(a0) /* t2 = dst */ lhu t3, 2(a0) /* t3 = dst */ not t0, a1 not t1, a1 srl t0, t0, 24 srl t1, t1, 24 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, s7, s8, s0, s1, s2, s3 MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 addu_s.qb t2, a1, t2 addu_s.qb t3, a1, t3 CONVERT_2x8888_TO_2x0565 t2, t3, t2, t3, s6, s7, s8, s0, s1 sh t2, 0(a0) sh t3, 2(a0) addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 4 b 4f nop 2: CONVERT_1x8888_TO_1x0565 a1, t2, s0, s1 sh t2, 0(a0) sh t2, 2(a0) 3: addiu t1, a3, -1 bgtz t1, 0b addiu a0, a0, 4 4: beqz a3, 7f nop /* a1 = src */ lw t0, 0(a2) /* t0 = mask */ beqz t0, 7f /* if (t0 == 0) */ nop beq t0, t7, 5f /* if (t0 == 0xffffffff) */ nop //if(ma) lhu t1, 0(a0) /* t1 = dst */ MIPS_UN8x4_MUL_UN8x4 a1, t0, t2, t9, t3, t4, t5, s0 MIPS_UN8x4_MUL_UN8 t0, t8, t0, t9, t3, t4, t5 not t0, t0 CONVERT_1x0565_TO_1x8888 t1, s1, s2, s3 MIPS_UN8x4_MUL_UN8x4 s1, t0, s1, t9, t3, t4, t5, s0 addu_s.qb s1, t2, s1 CONVERT_1x8888_TO_1x0565 s1, t1, s0, s2 sh t1, 0(a0) RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 j ra nop 5: //if (t0 == 0xffffffff) beq t8, t6, 6f /* if (srca == 0xff) */ nop lhu t1, 0(a0) /* t1 = dst */ not t0, a1 srl t0, t0, 24 CONVERT_1x0565_TO_1x8888 t1, s1, s2, s3 MIPS_UN8x4_MUL_UN8 s1, t0, s1, t9, t2, t3, t4 addu_s.qb s1, a1, s1 CONVERT_1x8888_TO_1x0565 s1, t1, s0, s2 sh t1, 0(a0) RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 j ra nop 6: CONVERT_1x8888_TO_1x0565 a1, t1, s0, s2 sh t1, 0(a0) 7: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 8: j ra nop END(pixman_composite_over_n_8888_0565_ca_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8_8_asm_mips) /* * a0 - dst (a8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, v0 li t9, 0x00ff00ff beqz a3, 3f nop srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ beqz v0, 1f /* branch if less than 4 src pixels */ nop srl t8, a1, 24 replv.ph t8, t8 0: beqz v0, 1f addiu v0, v0, -1 lbu t0, 0(a2) lbu t1, 1(a2) lbu t2, 2(a2) lbu t3, 3(a2) lbu t4, 0(a0) lbu t5, 1(a0) lbu t6, 2(a0) lbu t7, 3(a0) addiu a2, a2, 4 precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr_sra.ph.w t5, t4, 0 precr_sra.ph.w t7, t6, 0 precr.qb.ph t0, t3, t1 precr.qb.ph t1, t7, t5 muleu_s.ph.qbl t2, t0, t8 muleu_s.ph.qbr t3, t0, t8 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t0, t2, t3 not t6, t0 preceu.ph.qbl t7, t6 preceu.ph.qbr t6, t6 muleu_s.ph.qbl t2, t1, t7 muleu_s.ph.qbr t3, t1, t6 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t1, t2, t3 addu_s.qb t2, t0, t1 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a3, a3, -4 b 0b addiu a0, a0, 4 1: beqz a3, 3f nop srl t8, a1, 24 2: lbu t0, 0(a2) lbu t1, 0(a0) addiu a2, a2, 1 mul t2, t0, t8 shra_r.ph t3, t2, 8 andi t3, t3, 0x00ff addq.ph t2, t2, t3 shra_r.ph t2, t2, 8 not t3, t2 andi t3, t3, 0x00ff mul t4, t1, t3 shra_r.ph t5, t4, 8 andi t5, t5, 0x00ff addq.ph t4, t4, t5 shra_r.ph t4, t4, 8 andi t4, t4, 0x00ff addu_s.qb t2, t2, t4 sb t2, 0(a0) addiu a3, a3, -1 bnez a3, 2b addiu a0, a0, 1 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_composite_over_n_8_8_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 4, s0, s1, s2, s3, s4 beqz a3, 4f nop li t4, 0x00ff00ff li t5, 0xff addiu t0, a3, -1 beqz t0, 3f /* last pixel */ srl t6, a1, 24 /* t6 = srca */ not s4, a1 beq t5, t6, 2f /* if (srca == 0xff) */ srl s4, s4, 24 1: /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ lbu t1, 1(a2) /* t1 = mask */ or t2, t0, t1 beqz t2, 111f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 2 and t3, t0, t1 lw t2, 0(a0) /* t2 = dst */ beq t3, t5, 11f /* if (t0 == 0xff) && (t1 == 0xff) */ lw t3, 4(a0) /* t3 = dst */ MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, s0, s1, t4, t6, t7, t8, t9, s2, s3 not s2, s0 not s3, s1 srl s2, s2, 24 srl s3, s3, 24 MIPS_2xUN8x4_MUL_2xUN8 t2, t3, s2, s3, t2, t3, t4, t0, t1, t6, t7, t8, t9 addu_s.qb s2, t2, s0 addu_s.qb s3, t3, s1 sw s2, 0(a0) b 111f sw s3, 4(a0) 11: MIPS_2xUN8x4_MUL_2xUN8 t2, t3, s4, s4, t2, t3, t4, t0, t1, t6, t7, t8, t9 addu_s.qb s2, t2, a1 addu_s.qb s3, t3, a1 sw s2, 0(a0) sw s3, 4(a0) 111: addiu a3, a3, -2 addiu t0, a3, -1 bgtz t0, 1b addiu a0, a0, 8 b 3f nop 2: /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ lbu t1, 1(a2) /* t1 = mask */ or t2, t0, t1 beqz t2, 222f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 2 and t3, t0, t1 beq t3, t5, 22f /* if (t0 == 0xff) && (t1 == 0xff) */ nop lw t2, 0(a0) /* t2 = dst */ lw t3, 4(a0) /* t3 = dst */ OVER_2x8888_2x8_2x8888 a1, a1, t0, t1, t2, t3, \ t6, t7, t4, t8, t9, s0, s1, s2, s3 sw t6, 0(a0) b 222f sw t7, 4(a0) 22: sw a1, 0(a0) sw a1, 4(a0) 222: addiu a3, a3, -2 addiu t0, a3, -1 bgtz t0, 2b addiu a0, a0, 8 3: blez a3, 4f nop /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ beqz t0, 4f /* if (t0 == 0) */ addiu a2, a2, 1 move t3, a1 beq t0, t5, 31f /* if (t0 == 0xff) */ lw t1, 0(a0) /* t1 = dst */ MIPS_UN8x4_MUL_UN8 a1, t0, t3, t4, t6, t7, t8 31: not t2, t3 srl t2, t2, 24 MIPS_UN8x4_MUL_UN8 t1, t2, t1, t4, t6, t7, t8 addu_s.qb t2, t1, t3 sw t2, 0(a0) 4: RESTORE_REGS_FROM_STACK 4, s0, s1, s2, s3, s4 j ra nop END(pixman_composite_over_n_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 beqz a3, 4f nop li t4, 0x00ff00ff li t5, 0xff li t6, 0xf800f800 li t7, 0x07e007e0 li t8, 0x001F001F addiu t1, a3, -1 beqz t1, 3f /* last pixel */ srl t0, a1, 24 /* t0 = srca */ not v0, a1 beq t0, t5, 2f /* if (srca == 0xff) */ srl v0, v0, 24 1: /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ lbu t1, 1(a2) /* t1 = mask */ or t2, t0, t1 beqz t2, 111f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 2 lhu t2, 0(a0) /* t2 = dst */ lhu t3, 2(a0) /* t3 = dst */ CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, t7, t8, t9, s2, s3, s4 and t9, t0, t1 beq t9, t5, 11f /* if (t0 == 0xff) && (t1 == 0xff) */ nop MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, s2, s3, t4, t9, s4, s5, s6, s7, s8 not s4, s2 not s5, s3 srl s4, s4, 24 srl s5, s5, 24 MIPS_2xUN8x4_MUL_2xUN8 s0, s1, s4, s5, s0, s1, t4, t9, t0, t1, s6, s7, s8 addu_s.qb s4, s2, s0 addu_s.qb s5, s3, s1 CONVERT_2x8888_TO_2x0565 s4, s5, t2, t3, t6, t7, t8, s0, s1 sh t2, 0(a0) b 111f sh t3, 2(a0) 11: MIPS_2xUN8x4_MUL_2xUN8 s0, s1, v0, v0, s0, s1, t4, t9, t0, t1, s6, s7, s8 addu_s.qb s4, a1, s0 addu_s.qb s5, a1, s1 CONVERT_2x8888_TO_2x0565 s4, s5, t2, t3, t6, t7, t8, s0, s1 sh t2, 0(a0) sh t3, 2(a0) 111: addiu a3, a3, -2 addiu t0, a3, -1 bgtz t0, 1b addiu a0, a0, 4 b 3f nop 2: CONVERT_1x8888_TO_1x0565 a1, s0, s1, s2 21: /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ lbu t1, 1(a2) /* t1 = mask */ or t2, t0, t1 beqz t2, 222f /* if (t0 == 0) && (t1 == 0) */ addiu a2, a2, 2 and t9, t0, t1 move s2, s0 beq t9, t5, 22f /* if (t0 == 0xff) && (t2 == 0xff) */ move s3, s0 lhu t2, 0(a0) /* t2 = dst */ lhu t3, 2(a0) /* t3 = dst */ CONVERT_2x0565_TO_2x8888 t2, t3, s2, s3, t7, t8, s4, s5, s6, s7 OVER_2x8888_2x8_2x8888 a1, a1, t0, t1, s2, s3, \ t2, t3, t4, t9, s4, s5, s6, s7, s8 CONVERT_2x8888_TO_2x0565 t2, t3, s2, s3, t6, t7, t8, s4, s5 22: sh s2, 0(a0) sh s3, 2(a0) 222: addiu a3, a3, -2 addiu t0, a3, -1 bgtz t0, 21b addiu a0, a0, 4 3: blez a3, 4f nop /* a1 = src */ lbu t0, 0(a2) /* t0 = mask */ beqz t0, 4f /* if (t0 == 0) */ nop lhu t1, 0(a0) /* t1 = dst */ CONVERT_1x0565_TO_1x8888 t1, t2, t3, t7 beq t0, t5, 31f /* if (t0 == 0xff) */ move t3, a1 MIPS_UN8x4_MUL_UN8 a1, t0, t3, t4, t7, t8, t9 31: not t6, t3 srl t6, t6, 24 MIPS_UN8x4_MUL_UN8 t2, t6, t2, t4, t7, t8, t9 addu_s.qb t1, t2, t3 CONVERT_1x8888_TO_1x0565 t1, t2, t3, t7 sh t2, 0(a0) 4: RESTORE_REGS_FROM_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 j ra nop END(pixman_composite_over_n_8_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_n_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (32bit constant) * a3 - w */ SAVE_REGS_ON_STACK 0, s0 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 srl a2, a2, 24 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ addiu a1, a1, 8 OVER_2x8888_2x8_2x8888 t0, t1, a2, a2, t2, t3, \ t5, t6, t4, t7, t8, t9, t0, t1, s0 sw t5, 0(a0) sw t6, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ OVER_8888_8_8888 t0, a2, t1, t3, t4, t5, t6, t7, t8 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0 j ra nop END(pixman_composite_over_8888_n_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_n_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - mask (32bit constant) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 li t6, 0x00ff00ff li t7, 0xf800f800 li t8, 0x07e007e0 li t9, 0x001F001F beqz a3, 3f nop srl a2, a2, 24 addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ lhu t3, 2(a0) /* t2 = destination (r5g6b5) */ addiu a1, a1, 8 CONVERT_2x0565_TO_2x8888 t2, t3, t4, t5, t8, t9, s0, s1, t2, t3 OVER_2x8888_2x8_2x8888 t0, t1, a2, a2, t4, t5, \ t2, t3, t6, t0, t1, s0, s1, s2, s3 CONVERT_2x8888_TO_2x0565 t2, t3, t4, t5, t7, t8, t9, s0, s1 sh t4, 0(a0) sh t5, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t1, t2, t4, t5 OVER_8888_8_8888 t0, a2, t2, t1, t6, t3, t4, t5, t7 CONVERT_1x8888_TO_1x0565 t1, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 j ra nop END(pixman_composite_over_8888_n_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_0565_n_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (r5g6b5) * a2 - mask (32bit constant) * a3 - w */ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 li t6, 0x00ff00ff li t7, 0xf800f800 li t8, 0x07e007e0 li t9, 0x001F001F beqz a3, 3f nop srl a2, a2, 24 addiu t1, a3, -1 beqz t1, 2f nop 1: lhu t0, 0(a1) /* t0 = source (r5g6b5) */ lhu t1, 2(a1) /* t1 = source (r5g6b5) */ /* a2 = mask (32bit constant) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ addiu a1, a1, 4 CONVERT_2x0565_TO_2x8888 t0, t1, t4, t5, t8, t9, s0, s1, s2, s3 CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, t8, t9, s2, s3, s4, s5 OVER_2x8888_2x8_2x8888 t4, t5, a2, a2, s0, s1, \ t0, t1, t6, s2, s3, s4, s5, t4, t5 CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t7, t8, t9, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop lhu t0, 0(a1) /* t0 = source (r5g6b5) */ /* a2 = mask (32bit constant) */ lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t0, t2, t4, t5 CONVERT_1x0565_TO_1x8888 t1, t3, t4, t5 OVER_8888_8_8888 t2, a2, t3, t0, t6, t1, t4, t5, t7 CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 j ra nop END(pixman_composite_over_0565_n_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ addiu a1, a1, 8 addiu a2, a2, 2 OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, t5, t6, \ t7, t8, t4, t9, s0, s1, t0, t1, t2 sw t7, 0(a0) sw t8, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ OVER_8888_8_8888 t0, t1, t2, t3, t4, t5, t6, t7, t8 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1 j ra nop END(pixman_composite_over_8888_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_8_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 li t6, 0x00ff00ff li t7, 0xf800f800 li t8, 0x07e007e0 li t9, 0x001F001F beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lhu t4, 0(a0) /* t4 = destination (r5g6b5) */ lhu t5, 2(a0) /* t5 = destination (r5g6b5) */ addiu a1, a1, 8 addiu a2, a2, 2 CONVERT_2x0565_TO_2x8888 t4, t5, s0, s1, t8, t9, s2, s3, s4, s5 OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, s0, s1, \ t4, t5, t6, s2, s3, s4, s5, t0, t1 CONVERT_2x8888_TO_2x0565 t4, t5, s0, s1, t7, t8, t9, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t2, t3, t4, t5 OVER_8888_8_8888 t0, t1, t3, t2, t6, t4, t5, t7, t8 CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 j ra nop END(pixman_composite_over_8888_8_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_0565_8_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (r5g6b5) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 li t4, 0xf800f800 li t5, 0x07e007e0 li t6, 0x001F001F li t7, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lhu t0, 0(a1) /* t0 = source (r5g6b5) */ lhu t1, 2(a1) /* t1 = source (r5g6b5) */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ addiu a1, a1, 4 addiu a2, a2, 2 CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, t0, t1 OVER_2x8888_2x8_2x8888 s0, s1, t2, t3, s2, s3, \ t0, t1, t7, s4, s5, t8, t9, s0, s1 CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop lhu t0, 0(a1) /* t0 = source (r5g6b5) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 OVER_8888_8_8888 t3, t1, t4, t0, t7, t2, t5, t6, t8 CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 j ra nop END(pixman_composite_over_0565_8_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_8888_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (a8r8g8b8) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lw t2, 0(a2) /* t2 = mask (a8r8g8b8) */ lw t3, 4(a2) /* t3 = mask (a8r8g8b8) */ lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ addiu a1, a1, 8 addiu a2, a2, 8 srl t2, t2, 24 srl t3, t3, 24 OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, t5, t6, t7, t8, t4, t9, s0, s1, s2, t0, t1 sw t7, 0(a0) sw t8, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 0(a2) /* t1 = mask (a8r8g8b8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ srl t1, t1, 24 OVER_8888_8_8888 t0, t1, t2, t3, t4, t5, t6, t7, t8 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_over_8888_8888_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ addiu a1, a1, 8 not t5, t0 srl t5, t5, 24 not t6, t1 srl t6, t6, 24 or t7, t5, t6 beqz t7, 11f or t8, t0, t1 beqz t8, 12f MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t5, t6, t7, t8, t4, t9, s0, s1, s2, t2, t3 addu_s.qb t0, t7, t0 addu_s.qb t1, t8, t1 11: sw t0, 0(a0) sw t1, 4(a0) 12: addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a2, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ addiu a1, a1, 4 not t2, t0 srl t2, t2, 24 beqz t2, 21f nop beqz t0, 3f MIPS_UN8x4_MUL_UN8 t1, t2, t3, t4, t5, t6, t7 addu_s.qb t0, t3, t0 21: sw t0, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_over_8888_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_8888_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - w */ SAVE_REGS_ON_STACK 8, s0, s1, s2, s3, s4, s5 li t4, 0x00ff00ff li s3, 0xf800f800 li s4, 0x07e007e0 li s5, 0x001F001F beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ addiu a1, a1, 8 not t5, t0 srl t5, t5, 24 not t6, t1 srl t6, t6, 24 or t7, t5, t6 beqz t7, 11f or t8, t0, t1 beqz t8, 12f CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, s4, s5, t7, t8, t9, s2 MIPS_2xUN8x4_MUL_2xUN8 s0, s1, t5, t6, t7, t8, t4, t9, t2, t3, s2, s0, s1 addu_s.qb t0, t7, t0 addu_s.qb t1, t8, t1 11: CONVERT_2x8888_TO_2x0565 t0, t1, t7, t8, s3, s4, s5, t2, t3 sh t7, 0(a0) sh t8, 2(a0) 12: addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a2, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ addiu a1, a1, 4 not t2, t0 srl t2, t2, 24 beqz t2, 21f nop beqz t0, 3f CONVERT_1x0565_TO_1x8888 t1, s0, t8, t9 MIPS_UN8x4_MUL_UN8 s0, t2, t3, t4, t5, t6, t7 addu_s.qb t0, t3, t0 21: CONVERT_1x8888_TO_1x0565 t0, s0, t8, t9 sh s0, 0(a0) 3: RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 j ra nop END(pixman_composite_over_8888_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (32bit constant) * a2 - w */ beqz a2, 5f nop not t0, a1 srl t0, t0, 24 bgtz t0, 1f nop CONVERT_1x8888_TO_1x0565 a1, t1, t2, t3 0: sh t1, 0(a0) addiu a2, a2, -1 bgtz a2, 0b addiu a0, a0, 2 j ra nop 1: SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff li t5, 0xf800f800 li t6, 0x07e007e0 li t7, 0x001F001F addiu t1, a2, -1 beqz t1, 3f nop 2: lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ lhu t2, 2(a0) /* t2 = destination (r5g6b5) */ CONVERT_2x0565_TO_2x8888 t1, t2, t3, t8, t6, t7, t9, s0, s1, s2 MIPS_2xUN8x4_MUL_2xUN8 t3, t8, t0, t0, t1, t2, t4, t9, s0, s1, s2, t3, t8 addu_s.qb t1, t1, a1 addu_s.qb t2, t2, a1 CONVERT_2x8888_TO_2x0565 t1, t2, t3, t8, t5, t6, t7, s0, s1 sh t3, 0(a0) sh t8, 2(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 2b addiu a0, a0, 4 3: beqz a2, 4f nop lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t1, t2, s0, s1 MIPS_UN8x4_MUL_UN8 t2, t0, t1, t4, s0, s1, s2 addu_s.qb t1, t1, a1 CONVERT_1x8888_TO_1x0565 t1, t2, s0, s1 sh t2, 0(a0) 4: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 5: j ra nop END(pixman_composite_over_n_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - w */ beqz a2, 5f nop not t0, a1 srl t0, t0, 24 bgtz t0, 1f nop 0: sw a1, 0(a0) addiu a2, a2, -1 bgtz a2, 0b addiu a0, a0, 4 j ra nop 1: SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff addiu t1, a2, -1 beqz t1, 3f nop 2: lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t0, t7, t8, t4, t9, s0, s1, s2, t2, t3 addu_s.qb t7, t7, a1 addu_s.qb t8, t8, a1 sw t7, 0(a0) sw t8, 4(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 2b addiu a0, a0, 8 3: beqz a2, 4f nop lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ MIPS_UN8x4_MUL_UN8 t1, t0, t3, t4, t5, t6, t7 addu_s.qb t3, t3, a1 sw t3, 0(a0) 4: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 5: j ra nop END(pixman_composite_over_n_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8_8_8_asm_mips) /* * a0 - dst (a8) * a1 - src (a8) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, v0, v1 li t9, 0x00ff00ff beqz a3, 3f nop srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ beqz v0, 1f /* branch if less than 4 src pixels */ nop 0: beqz v0, 1f addiu v0, v0, -1 lbu t0, 0(a2) lbu t1, 1(a2) lbu t2, 2(a2) lbu t3, 3(a2) lbu t4, 0(a0) lbu t5, 1(a0) lbu t6, 2(a0) lbu t7, 3(a0) addiu a2, a2, 4 precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr_sra.ph.w t5, t4, 0 precr_sra.ph.w t7, t6, 0 precr.qb.ph t0, t3, t1 precr.qb.ph t1, t7, t5 lbu t4, 0(a1) lbu v1, 1(a1) lbu t7, 2(a1) lbu t8, 3(a1) addiu a1, a1, 4 precr_sra.ph.w v1, t4, 0 precr_sra.ph.w t8, t7, 0 muleu_s.ph.qbl t2, t0, t8 muleu_s.ph.qbr t3, t0, v1 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t0, t2, t3 addu_s.qb t2, t0, t1 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a3, a3, -4 b 0b addiu a0, a0, 4 1: beqz a3, 3f nop 2: lbu t8, 0(a1) lbu t0, 0(a2) lbu t1, 0(a0) addiu a1, a1, 1 addiu a2, a2, 1 mul t2, t0, t8 shra_r.ph t3, t2, 8 andi t3, t3, 0xff addq.ph t2, t2, t3 shra_r.ph t2, t2, 8 andi t2, t2, 0xff addu_s.qb t2, t2, t1 sb t2, 0(a0) addiu a3, a3, -1 bnez a3, 2b addiu a0, a0, 1 3: RESTORE_REGS_FROM_STACK 0, v0, v1 j ra nop END(pixman_composite_add_8_8_8_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_n_8_8_asm_mips) /* * a0 - dst (a8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, v0 li t9, 0x00ff00ff beqz a3, 3f nop srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ beqz v0, 1f /* branch if less than 4 src pixels */ nop srl t8, a1, 24 replv.ph t8, t8 0: beqz v0, 1f addiu v0, v0, -1 lbu t0, 0(a2) lbu t1, 1(a2) lbu t2, 2(a2) lbu t3, 3(a2) lbu t4, 0(a0) lbu t5, 1(a0) lbu t6, 2(a0) lbu t7, 3(a0) addiu a2, a2, 4 precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr_sra.ph.w t5, t4, 0 precr_sra.ph.w t7, t6, 0 precr.qb.ph t0, t3, t1 precr.qb.ph t1, t7, t5 muleu_s.ph.qbl t2, t0, t8 muleu_s.ph.qbr t3, t0, t8 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t0, t2, t3 addu_s.qb t2, t0, t1 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a3, a3, -4 b 0b addiu a0, a0, 4 1: beqz a3, 3f nop srl t8, a1, 24 2: lbu t0, 0(a2) lbu t1, 0(a0) addiu a2, a2, 1 mul t2, t0, t8 shra_r.ph t3, t2, 8 andi t3, t3, 0xff addq.ph t2, t2, t3 shra_r.ph t2, t2, 8 andi t2, t2, 0xff addu_s.qb t2, t2, t1 sb t2, 0(a0) addiu a3, a3, -1 bnez a3, 2b addiu a0, a0, 1 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_composite_add_n_8_8_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_n_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: /* a1 = source (32bit constant) */ lbu t0, 0(a2) /* t0 = mask (a8) */ lbu t1, 1(a2) /* t1 = mask (a8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ addiu a2, a2, 2 MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 a1, a1, \ t0, t1, \ t2, t3, \ t5, t6, \ t4, t7, t8, t9, s0, s1, s2 sw t5, 0(a0) sw t6, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop /* a1 = source (32bit constant) */ lbu t0, 0(a2) /* t0 = mask (a8) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ MIPS_UN8x4_MUL_UN8_ADD_UN8x4 a1, t0, t1, t2, t4, t3, t5, t6 sw t2, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_add_n_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_0565_8_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (r5g6b5) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 li t4, 0xf800f800 li t5, 0x07e007e0 li t6, 0x001F001F li t7, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lhu t0, 0(a1) /* t0 = source (r5g6b5) */ lhu t1, 2(a1) /* t1 = source (r5g6b5) */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ addiu a1, a1, 4 addiu a2, a2, 2 CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, s6, s7 MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 s0, s1, \ t2, t3, \ s2, s3, \ t0, t1, \ t7, s4, s5, s6, s7, t8, t9 CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop lhu t0, 0(a1) /* t0 = source (r5g6b5) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t3, t1, t4, t0, t7, t2, t5, t6 CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 j ra nop END(pixman_composite_add_0565_8_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8888_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (a8) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ addiu a1, a1, 8 addiu a2, a2, 2 MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ t2, t3, \ t5, t6, \ t7, t8, \ t4, t9, s0, s1, s2, t0, t1 sw t7, 0(a0) sw t8, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t3, t4, t5, t6, t7 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_add_8888_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8888_n_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (32bit constant) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a3, 3f nop srl a2, a2, 24 addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ addiu a1, a1, 8 MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ a2, a2, \ t2, t3, \ t5, t6, \ t4, t7, t8, t9, s0, s1, s2 sw t5, 0(a0) sw t6, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ /* a2 = mask (32bit constant) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, a2, t1, t3, t4, t5, t6, t7 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_add_8888_n_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8888_8888_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - mask (a8r8g8b8) * a3 - w */ SAVE_REGS_ON_STACK 0, s0, s1, s2 li t4, 0x00ff00ff beqz a3, 3f nop addiu t1, a3, -1 beqz t1, 2f nop 1: lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ lw t2, 0(a2) /* t2 = mask (a8r8g8b8) */ lw t3, 4(a2) /* t3 = mask (a8r8g8b8) */ lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ addiu a1, a1, 8 addiu a2, a2, 8 srl t2, t2, 24 srl t3, t3, 24 MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ t2, t3, \ t5, t6, \ t7, t8, \ t4, t9, s0, s1, s2, t0, t1 sw t7, 0(a0) sw t8, 4(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a3, 3f nop lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ lw t1, 0(a2) /* t1 = mask (a8r8g8b8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ srl t1, t1, 24 MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t3, t4, t5, t6, t7 sw t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2 j ra nop END(pixman_composite_add_8888_8888_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8_8_asm_mips) /* * a0 - dst (a8) * a1 - src (a8) * a2 - w */ beqz a2, 3f nop srl t9, a2, 2 /* t9 = how many multiples of 4 dst pixels */ beqz t9, 1f /* branch if less than 4 src pixels */ nop 0: beqz t9, 1f addiu t9, t9, -1 lbu t0, 0(a1) lbu t1, 1(a1) lbu t2, 2(a1) lbu t3, 3(a1) lbu t4, 0(a0) lbu t5, 1(a0) lbu t6, 2(a0) lbu t7, 3(a0) addiu a1, a1, 4 precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr_sra.ph.w t5, t4, 0 precr_sra.ph.w t7, t6, 0 precr.qb.ph t0, t3, t1 precr.qb.ph t1, t7, t5 addu_s.qb t2, t0, t1 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a2, a2, -4 b 0b addiu a0, a0, 4 1: beqz a2, 3f nop 2: lbu t0, 0(a1) lbu t1, 0(a0) addiu a1, a1, 1 addu_s.qb t2, t0, t1 sb t2, 0(a0) addiu a2, a2, -1 bnez a2, 2b addiu a0, a0, 1 3: j ra nop END(pixman_composite_add_8_8_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_add_8888_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - w */ beqz a2, 4f nop srl t9, a2, 2 /* t1 = how many multiples of 4 src pixels */ beqz t9, 3f /* branch if less than 4 src pixels */ nop 1: addiu t9, t9, -1 beqz t9, 2f addiu a2, a2, -4 lw t0, 0(a1) lw t1, 4(a1) lw t2, 8(a1) lw t3, 12(a1) lw t4, 0(a0) lw t5, 4(a0) lw t6, 8(a0) lw t7, 12(a0) addiu a1, a1, 16 addu_s.qb t4, t4, t0 addu_s.qb t5, t5, t1 addu_s.qb t6, t6, t2 addu_s.qb t7, t7, t3 sw t4, 0(a0) sw t5, 4(a0) sw t6, 8(a0) sw t7, 12(a0) b 1b addiu a0, a0, 16 2: lw t0, 0(a1) lw t1, 4(a1) lw t2, 8(a1) lw t3, 12(a1) lw t4, 0(a0) lw t5, 4(a0) lw t6, 8(a0) lw t7, 12(a0) addiu a1, a1, 16 addu_s.qb t4, t4, t0 addu_s.qb t5, t5, t1 addu_s.qb t6, t6, t2 addu_s.qb t7, t7, t3 sw t4, 0(a0) sw t5, 4(a0) sw t6, 8(a0) sw t7, 12(a0) beqz a2, 4f addiu a0, a0, 16 3: lw t0, 0(a1) lw t1, 0(a0) addiu a1, a1, 4 addiu a2, a2, -1 addu_s.qb t1, t1, t0 sw t1, 0(a0) bnez a2, 3b addiu a0, a0, 4 4: jr ra nop END(pixman_composite_add_8888_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_out_reverse_8_0565_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8) * a2 - w */ beqz a2, 4f nop SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 li t2, 0xf800f800 li t3, 0x07e007e0 li t4, 0x001F001F li t5, 0x00ff00ff addiu t1, a2, -1 beqz t1, 2f nop 1: lbu t0, 0(a1) /* t0 = source (a8) */ lbu t1, 1(a1) /* t1 = source (a8) */ lhu t6, 0(a0) /* t6 = destination (r5g6b5) */ lhu t7, 2(a0) /* t7 = destination (r5g6b5) */ addiu a1, a1, 2 not t0, t0 not t1, t1 andi t0, 0xff /* t0 = neg source1 */ andi t1, 0xff /* t1 = neg source2 */ CONVERT_2x0565_TO_2x8888 t6, t7, t8, t9, t3, t4, s0, s1, s2, s3 MIPS_2xUN8x4_MUL_2xUN8 t8, t9, t0, t1, t6, t7, t5, s0, s1, s2, s3, t8, t9 CONVERT_2x8888_TO_2x0565 t6, t7, t8, t9, t2, t3, t4, s0, s1 sh t8, 0(a0) sh t9, 2(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a2, 3f nop lbu t0, 0(a1) /* t0 = source (a8) */ lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ not t0, t0 andi t0, 0xff /* t0 = neg source */ CONVERT_1x0565_TO_1x8888 t1, t2, t3, t4 MIPS_UN8x4_MUL_UN8 t2, t0, t1, t5, t3, t4, t6 CONVERT_1x8888_TO_1x0565 t1, t2, t3, t4 sh t2, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 4: j ra nop END(pixman_composite_out_reverse_8_0565_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_out_reverse_8_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8) * a2 - w */ beqz a2, 3f nop li t4, 0x00ff00ff addiu t1, a2, -1 beqz t1, 2f nop 1: lbu t0, 0(a1) /* t0 = source (a8) */ lbu t1, 1(a1) /* t1 = source (a8) */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ addiu a1, a1, 2 not t0, t0 not t1, t1 andi t0, 0xff /* t0 = neg source */ andi t1, 0xff /* t1 = neg source */ MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t5, t6, t4, t7, t8, t9, t2, t3, t0 sw t5, 0(a0) sw t6, 4(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a2, 3f nop lbu t0, 0(a1) /* t0 = source (a8) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ not t0, t0 andi t0, 0xff /* t0 = neg source */ MIPS_UN8x4_MUL_UN8 t1, t0, t2, t4, t3, t5, t6 sw t2, 0(a0) 3: j ra nop END(pixman_composite_out_reverse_8_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_over_reverse_n_8888_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (32bit constant) * a2 - w */ beqz a2, 5f nop SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 li t0, 0x00ff00ff srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ beqz t9, 2f /* branch if less than 4 src pixels */ nop 1: beqz t9, 2f addiu t9, t9, -1 lw t1, 0(a0) lw t2, 4(a0) lw t3, 8(a0) lw t4, 12(a0) addiu a2, a2, -4 not t5, t1 not t6, t2 not t7, t3 not t8, t4 srl t5, t5, 24 srl t6, t6, 24 srl t7, t7, 24 srl t8, t8, 24 replv.ph t5, t5 replv.ph t6, t6 replv.ph t7, t7 replv.ph t8, t8 muleu_s.ph.qbl s0, a1, t5 muleu_s.ph.qbr s1, a1, t5 muleu_s.ph.qbl s2, a1, t6 muleu_s.ph.qbr s3, a1, t6 muleu_s.ph.qbl s4, a1, t7 muleu_s.ph.qbr s5, a1, t7 muleu_s.ph.qbl s6, a1, t8 muleu_s.ph.qbr s7, a1, t8 shra_r.ph t5, s0, 8 shra_r.ph t6, s1, 8 shra_r.ph t7, s2, 8 shra_r.ph t8, s3, 8 and t5, t5, t0 and t6, t6, t0 and t7, t7, t0 and t8, t8, t0 addq.ph s0, s0, t5 addq.ph s1, s1, t6 addq.ph s2, s2, t7 addq.ph s3, s3, t8 shra_r.ph s0, s0, 8 shra_r.ph s1, s1, 8 shra_r.ph s2, s2, 8 shra_r.ph s3, s3, 8 shra_r.ph t5, s4, 8 shra_r.ph t6, s5, 8 shra_r.ph t7, s6, 8 shra_r.ph t8, s7, 8 and t5, t5, t0 and t6, t6, t0 and t7, t7, t0 and t8, t8, t0 addq.ph s4, s4, t5 addq.ph s5, s5, t6 addq.ph s6, s6, t7 addq.ph s7, s7, t8 shra_r.ph s4, s4, 8 shra_r.ph s5, s5, 8 shra_r.ph s6, s6, 8 shra_r.ph s7, s7, 8 precr.qb.ph t5, s0, s1 precr.qb.ph t6, s2, s3 precr.qb.ph t7, s4, s5 precr.qb.ph t8, s6, s7 addu_s.qb t5, t1, t5 addu_s.qb t6, t2, t6 addu_s.qb t7, t3, t7 addu_s.qb t8, t4, t8 sw t5, 0(a0) sw t6, 4(a0) sw t7, 8(a0) sw t8, 12(a0) b 1b addiu a0, a0, 16 2: beqz a2, 4f nop 3: lw t1, 0(a0) not t2, t1 srl t2, t2, 24 replv.ph t2, t2 muleu_s.ph.qbl t4, a1, t2 muleu_s.ph.qbr t5, a1, t2 shra_r.ph t6, t4, 8 shra_r.ph t7, t5, 8 and t6,t6,t0 and t7,t7,t0 addq.ph t8, t4, t6 addq.ph t9, t5, t7 shra_r.ph t8, t8, 8 shra_r.ph t9, t9, 8 precr.qb.ph t9, t8, t9 addu_s.qb t9, t1, t9 sw t9, 0(a0) addiu a2, a2, -1 bnez a2, 3b addiu a0, a0, 4 4: RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 5: j ra nop END(pixman_composite_over_reverse_n_8888_asm_mips) LEAF_MIPS_DSPR2(pixman_composite_in_n_8_asm_mips) /* * a0 - dst (a8) * a1 - src (32bit constant) * a2 - w */ li t9, 0x00ff00ff beqz a2, 3f nop srl t7, a2, 2 /* t7 = how many multiples of 4 dst pixels */ beqz t7, 1f /* branch if less than 4 src pixels */ nop srl t8, a1, 24 replv.ph t8, t8 0: beqz t7, 1f addiu t7, t7, -1 lbu t0, 0(a0) lbu t1, 1(a0) lbu t2, 2(a0) lbu t3, 3(a0) precr_sra.ph.w t1, t0, 0 precr_sra.ph.w t3, t2, 0 precr.qb.ph t0, t3, t1 muleu_s.ph.qbl t2, t0, t8 muleu_s.ph.qbr t3, t0, t8 shra_r.ph t4, t2, 8 shra_r.ph t5, t3, 8 and t4, t4, t9 and t5, t5, t9 addq.ph t2, t2, t4 addq.ph t3, t3, t5 shra_r.ph t2, t2, 8 shra_r.ph t3, t3, 8 precr.qb.ph t2, t2, t3 sb t2, 0(a0) srl t2, t2, 8 sb t2, 1(a0) srl t2, t2, 8 sb t2, 2(a0) srl t2, t2, 8 sb t2, 3(a0) addiu a2, a2, -4 b 0b addiu a0, a0, 4 1: beqz a2, 3f nop srl t8, a1, 24 2: lbu t0, 0(a0) mul t2, t0, t8 shra_r.ph t3, t2, 8 andi t3, t3, 0x00ff addq.ph t2, t2, t3 shra_r.ph t2, t2, 8 sb t2, 0(a0) addiu a2, a2, -1 bnez a2, 2b addiu a0, a0, 1 3: j ra nop END(pixman_composite_in_n_8_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_8888_OVER_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (a8r8g8b8) * a2 - w * a3 - vx * 16(sp) - unit_x */ SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 lw t8, 16(sp) /* t8 = unit_x */ li t6, 0x00ff00ff beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ addu a3, a3, t8 /* a3 = vx + unit_x */ sra t1, a3, 16 /* t0 = vx >> 16 */ sll t1, t1, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t1, a1, t1 lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ addu a3, a3, t8 /* a3 = vx + unit_x */ lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ OVER_2x8888_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t9, s0, s1, s2, s3 sw t4, 0(a0) sw t5, 4(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 8 2: beqz a2, 3f nop sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ addu a3, a3, t8 /* a3 = vx + unit_x */ OVER_8888_8888 t0, t1, t2, t6, t4, t5, t3, t7 sw t2, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 j ra nop END(pixman_scaled_nearest_scanline_8888_8888_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_0565_OVER_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - w * a3 - vx * 16(sp) - unit_x */ SAVE_REGS_ON_STACK 24, s0, s1, s2, s3, s4, v0, v1 lw t8, 40(sp) /* t8 = unit_x */ li t4, 0x00ff00ff li t5, 0xf800f800 li t6, 0x07e007e0 li t7, 0x001F001F beqz a2, 3f nop addiu t1, a2, -1 beqz t1, 2f nop 1: sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ addu a3, a3, t8 /* a3 = vx + unit_x */ sra t1, a3, 16 /* t0 = vx >> 16 */ sll t1, t1, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t1, a1, t1 lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ addu a3, a3, t8 /* a3 = vx + unit_x */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ CONVERT_2x0565_TO_2x8888 t2, t3, v0, v1, t6, t7, s0, s1, s2, s3 OVER_2x8888_2x8888 t0, t1, v0, v1, t2, t3, t4, t9, s0, s1, s2, s3, s4 CONVERT_2x8888_TO_2x0565 t2, t3, v0, v1, t5, t6, t7, t9, s2 sh v0, 0(a0) sh v1, 2(a0) addiu a2, a2, -2 addiu t1, a2, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a2, 3f nop sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ addu a3, a3, t8 /* a3 = vx + unit_x */ CONVERT_1x0565_TO_1x8888 t1, t2, t5, t6 OVER_8888_8888 t0, t2, t1, t4, t3, t5, t6, t7 CONVERT_1x8888_TO_1x0565 t1, t2, t5, t6 sh t2, 0(a0) 3: RESTORE_REGS_FROM_STACK 24, s0, s1, s2, s3, s4, v0, v1 j ra nop END(pixman_scaled_nearest_scanline_8888_0565_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_0565_8888_SRC_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - src (r5g6b5) * a2 - w * a3 - vx * 16(sp) - unit_x */ SAVE_REGS_ON_STACK 0, v0 beqz a2, 3f nop lw v0, 16(sp) /* v0 = unit_x */ addiu t1, a2, -1 beqz t1, 2f nop li t4, 0x07e007e0 li t5, 0x001F001F 1: sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 1 /* t0 = t0 * 2 ((r5g6b5)) */ addu t0, a1, t0 lhu t0, 0(t0) /* t0 = source ((r5g6b5)) */ addu a3, a3, v0 /* a3 = vx + unit_x */ sra t1, a3, 16 /* t1 = vx >> 16 */ sll t1, t1, 1 /* t1 = t1 * 2 ((r5g6b5)) */ addu t1, a1, t1 lhu t1, 0(t1) /* t1 = source ((r5g6b5)) */ addu a3, a3, v0 /* a3 = vx + unit_x */ addiu a2, a2, -2 CONVERT_2x0565_TO_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 sw t2, 0(a0) sw t3, 4(a0) addiu t2, a2, -1 bgtz t2, 1b addiu a0, a0, 8 2: beqz a2, 3f nop sra t0, a3, 16 /* t0 = vx >> 16 */ sll t0, t0, 1 /* t0 = t0 * 2 ((r5g6b5)) */ addu t0, a1, t0 lhu t0, 0(t0) /* t0 = source ((r5g6b5)) */ CONVERT_1x0565_TO_1x8888 t0, t1, t2, t3 sw t1, 0(a0) 3: RESTORE_REGS_FROM_STACK 0, v0 j ra nop END(pixman_scaled_nearest_scanline_0565_8888_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (a8r8g8b8) * a2 - mask (a8) * a3 - w * 16(sp) - vx * 20(sp) - unit_x */ beqz a3, 4f nop SAVE_REGS_ON_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 lw v0, 36(sp) /* v0 = vx */ lw v1, 40(sp) /* v1 = unit_x */ li t6, 0x00ff00ff li t7, 0xf800f800 li t8, 0x07e007e0 li t9, 0x001F001F addiu t1, a3, -1 beqz t1, 2f nop 1: sra t0, v0, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ addu v0, v0, v1 /* v0 = vx + unit_x */ sra t1, v0, 16 /* t1 = vx >> 16 */ sll t1, t1, 2 /* t1 = t1 * 4 (a8r8g8b8) */ addu t1, a1, t1 lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ addu v0, v0, v1 /* v0 = vx + unit_x */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lhu t4, 0(a0) /* t4 = destination (r5g6b5) */ lhu t5, 2(a0) /* t5 = destination (r5g6b5) */ addiu a2, a2, 2 CONVERT_2x0565_TO_2x8888 t4, t5, s0, s1, t8, t9, s2, s3, s4, s5 OVER_2x8888_2x8_2x8888 t0, t1, \ t2, t3, \ s0, s1, \ t4, t5, \ t6, s2, s3, s4, s5, t2, t3 CONVERT_2x8888_TO_2x0565 t4, t5, s0, s1, t7, t8, t9, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop sra t0, v0, 16 /* t0 = vx >> 16 */ sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ addu t0, a1, t0 lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t2, t3, t4, t5 OVER_8888_8_8888 t0, t1, t3, t2, t6, t4, t5, t7, t8 CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 4: j ra nop END(pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_mips) /* * a0 - dst (r5g6b5) * a1 - src (r5g6b5) * a2 - mask (a8) * a3 - w * 16(sp) - vx * 20(sp) - unit_x */ beqz a3, 4f nop SAVE_REGS_ON_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 lw v0, 36(sp) /* v0 = vx */ lw v1, 40(sp) /* v1 = unit_x */ li t4, 0xf800f800 li t5, 0x07e007e0 li t6, 0x001F001F li t7, 0x00ff00ff addiu t1, a3, -1 beqz t1, 2f nop 1: sra t0, v0, 16 /* t0 = vx >> 16 */ sll t0, t0, 1 /* t0 = t0 * 2 (r5g6b5) */ addu t0, a1, t0 lhu t0, 0(t0) /* t0 = source (r5g6b5) */ addu v0, v0, v1 /* v0 = vx + unit_x */ sra t1, v0, 16 /* t1 = vx >> 16 */ sll t1, t1, 1 /* t1 = t1 * 2 (r5g6b5) */ addu t1, a1, t1 lhu t1, 0(t1) /* t1 = source (r5g6b5) */ addu v0, v0, v1 /* v0 = vx + unit_x */ lbu t2, 0(a2) /* t2 = mask (a8) */ lbu t3, 1(a2) /* t3 = mask (a8) */ lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ addiu a2, a2, 2 CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, t0, t1 OVER_2x8888_2x8_2x8888 s0, s1, \ t2, t3, \ s2, s3, \ t0, t1, \ t7, t8, t9, s4, s5, s0, s1 CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 sh s0, 0(a0) sh s1, 2(a0) addiu a3, a3, -2 addiu t1, a3, -1 bgtz t1, 1b addiu a0, a0, 4 2: beqz a3, 3f nop sra t0, v0, 16 /* t0 = vx >> 16 */ sll t0, t0, 1 /* t0 = t0 * 2 (r5g6b5) */ addu t0, a1, t0 lhu t0, 0(t0) /* t0 = source (r5g6b5) */ lbu t1, 0(a2) /* t1 = mask (a8) */ lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 OVER_8888_8_8888 t3, t1, t4, t0, t7, t2, t5, t6, t8 CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 sh t3, 0(a0) 3: RESTORE_REGS_FROM_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 4: j ra nop END(pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 lw s0, 36(sp) /* s0 = wt */ lw s1, 40(sp) /* s1 = wb */ lw s2, 44(sp) /* s2 = vx */ lw s3, 48(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a1) /* t0 = tl */ lwx t1, t8(a1) /* t1 = tr */ addiu a3, a3, -1 lwx t2, t9(a2) /* t2 = bl */ lwx t3, t8(a2) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez a3, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 lw s0, 36(sp) /* s0 = wt */ lw s1, 40(sp) /* s1 = wb */ lw s2, 44(sp) /* s2 = vx */ lw s3, 48(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a1) /* t0 = tl */ lwx t1, t8(a1) /* t1 = tr */ addiu a3, a3, -1 lwx t2, t9(a2) /* t2 = bl */ lwx t3, t8(a2) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 addu s2, s2, s3 /* vx += unit_x; */ sh t1, 0(a0) bnez a3, 0b addiu a0, a0, 2 RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8888_SRC_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li v1, 0x07e007e0 li s8, 0x001f001f sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 1 addiu t8, t9, 2 lhx t0, t9(a1) /* t0 = tl */ lhx t1, t8(a1) /* t1 = tr */ andi t1, t1, 0xffff addiu a3, a3, -1 lhx t2, t9(a2) /* t2 = bl */ lhx t3, t8(a2) /* t3 = br */ andi t3, t3, 0xffff CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez a3, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_0565_8888_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li v1, 0x07e007e0 li s8, 0x001f001f sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 1 addiu t8, t9, 2 lhx t0, t9(a1) /* t0 = tl */ lhx t1, t8(a1) /* t1 = tr */ andi t1, t1, 0xffff addiu a3, a3, -1 lhx t2, t9(a2) /* t2 = bl */ lhx t3, t8(a2) /* t3 = br */ andi t3, t3, 0xffff CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 addu s2, s2, s3 /* vx += unit_x; */ sh t1, 0(a0) bnez a3, 0b addiu a0, a0, 2 RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 40(sp) /* s0 = wt */ lw s1, 44(sp) /* s1 = wb */ lw s2, 48(sp) /* s2 = vx */ lw s3, 52(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li s8, 0x00ff00ff sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a1) /* t0 = tl */ lwx t1, t8(a1) /* t1 = tr */ addiu a3, a3, -1 lwx t2, t9(a2) /* t2 = bl */ lwx t3, t8(a2) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lw t1, 0(a0) /* t1 = dest */ OVER_8888_8888 t0, t1, t2, s8, t3, t4, t5, t6 addu s2, s2, s3 /* vx += unit_x; */ sw t2, 0(a0) bnez a3, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_mips) /* * a0 - *dst * a1 - *src_top * a2 - *src_bottom * a3 - w * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x */ beqz a3, 1f nop SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 lw s0, 36(sp) /* s0 = wt */ lw s1, 40(sp) /* s1 = wb */ lw s2, 44(sp) /* s2 = vx */ lw s3, 48(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a1) /* t0 = tl */ lwx t1, t8(a1) /* t1 = tr */ addiu a3, a3, -1 lwx t2, t9(a2) /* t2 = bl */ lwx t3, t8(a2) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lw t1, 0(a0) addu_s.qb t2, t0, t1 addu s2, s2, s3 /* vx += unit_x; */ sw t2, 0(a0) bnez a3, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_mips) /* * a0 - *dst * a1 - *mask * a2 - *src_top * a3 - *src_bottom * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ lw v1, 32(sp) beqz v1, 1f nop SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li s8, 0x00ff00ff sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a2) /* t0 = tl */ lwx t1, t8(a2) /* t1 = tr */ addiu v1, v1, -1 lwx t2, t9(a3) /* t2 = bl */ lwx t3, t8(a3) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ addiu a1, a1, 1 MIPS_UN8x4_MUL_UN8 t0, t1, t0, s8, t2, t3, t4 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez v1, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_mips) /* * a0 - *dst * a1 - *mask * a2 - *src_top * a3 - *src_bottom * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ lw v1, 32(sp) beqz v1, 1f nop SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li s8, 0x00ff00ff sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a2) /* t0 = tl */ lwx t1, t8(a2) /* t1 = tr */ addiu v1, v1, -1 lwx t2, t9(a3) /* t2 = bl */ lwx t3, t8(a3) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ addiu a1, a1, 1 MIPS_UN8x4_MUL_UN8 t0, t1, t0, s8, t2, t3, t4 CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 addu s2, s2, s3 /* vx += unit_x; */ sh t1, 0(a0) bnez v1, 0b addiu a0, a0, 2 RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_mips) /* * a0 - *dst * a1 - *mask * a2 - *src_top * a3 - *src_bottom * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ lw t0, 32(sp) beqz t0, 1f nop SAVE_REGS_ON_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra lw s0, 48(sp) /* s0 = wt */ lw s1, 52(sp) /* s1 = wb */ lw s2, 56(sp) /* s2 = vx */ lw s3, 60(sp) /* s3 = unit_x */ lw ra, 64(sp) /* ra = w */ li v0, 0x00ff00ff li v1, 0x07e007e0 li s8, 0x001f001f sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ li t5, BILINEAR_INTERPOLATION_RANGE subu t5, t5, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 1 addiu t8, t9, 2 lhx t0, t9(a2) /* t0 = tl */ lhx t1, t8(a2) /* t1 = tr */ andi t1, t1, 0xffff addiu ra, ra, -1 lhx t2, t9(a3) /* t2 = bl */ lhx t3, t8(a3) /* t3 = br */ andi t3, t3, 0xffff CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ addiu a1, a1, 1 MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t2, t3, t4 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez ra, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra 1: j ra nop END(pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_mips) /* * a0 - *dst * a1 - *mask * a2 - *src_top * a3 - *src_bottom * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ lw t0, 32(sp) beqz t0, 1f nop SAVE_REGS_ON_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra lw s0, 48(sp) /* s0 = wt */ lw s1, 52(sp) /* s1 = wb */ lw s2, 56(sp) /* s2 = vx */ lw s3, 60(sp) /* s3 = unit_x */ lw ra, 64(sp) /* ra = w */ li v0, 0x00ff00ff li v1, 0x07e007e0 li s8, 0x001f001f sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ li t5, BILINEAR_INTERPOLATION_RANGE subu t5, t5, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 1 addiu t8, t9, 2 lhx t0, t9(a2) /* t0 = tl */ lhx t1, t8(a2) /* t1 = tr */ andi t1, t1, 0xffff addiu ra, ra, -1 lhx t2, t9(a3) /* t2 = bl */ lhx t3, t8(a3) /* t3 = br */ andi t3, t3, 0xffff CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ addiu a1, a1, 1 MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t2, t3, t4 CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 addu s2, s2, s3 /* vx += unit_x; */ sh t1, 0(a0) bnez ra, 0b addiu a0, a0, 2 RESTORE_REGS_FROM_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra 1: j ra nop END(pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_mips) /* * a0 - dst (a8r8g8b8) * a1 - mask (a8) * a2 - src_top (a8r8g8b8) * a3 - src_bottom (a8r8g8b8) * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw v1, 60(sp) /* v1 = w(sp + 32 + 28 save regs stack offset)*/ beqz v1, 1f nop lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li s8, 0x00ff00ff sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a2) /* t0 = tl */ lwx t1, t8(a2) /* t1 = tr */ addiu v1, v1, -1 lwx t2, t9(a3) /* t2 = bl */ lwx t3, t8(a3) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, \ t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ lw t2, 0(a0) /* t2 = dst */ addiu a1, a1, 1 OVER_8888_8_8888 t0, t1, t2, t0, s8, t3, t4, t5, t6 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez v1, 0b addiu a0, a0, 4 1: RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 j ra nop END(pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_mips) LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_mips) /* * a0 - *dst * a1 - *mask * a2 - *src_top * a3 - *src_bottom * 16(sp) - wt * 20(sp) - wb * 24(sp) - vx * 28(sp) - unit_x * 32(sp) - w */ lw v1, 32(sp) beqz v1, 1f nop SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 lw s0, 44(sp) /* s0 = wt */ lw s1, 48(sp) /* s1 = wb */ lw s2, 52(sp) /* s2 = vx */ lw s3, 56(sp) /* s3 = unit_x */ li v0, BILINEAR_INTERPOLATION_RANGE li s8, 0x00ff00ff sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) 0: andi t4, s2, 0xffff /* t4 = (short)vx */ srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ mul s5, s0, t4 /* s5 = wt*(vx>>8) */ mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ mul s7, s1, t4 /* s7 = wb*(vx>>8) */ sra t9, s2, 16 sll t9, t9, 2 addiu t8, t9, 4 lwx t0, t9(a2) /* t0 = tl */ lwx t1, t8(a2) /* t1 = tr */ addiu v1, v1, -1 lwx t2, t9(a3) /* t2 = bl */ lwx t3, t8(a3) /* t3 = br */ BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 lbu t1, 0(a1) /* t1 = mask */ lw t2, 0(a0) /* t2 = dst */ addiu a1, a1, 1 MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t0, s8, t3, t4, t5 addu s2, s2, s3 /* vx += unit_x; */ sw t0, 0(a0) bnez v1, 0b addiu a0, a0, 4 RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 1: j ra nop END(pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_mips) Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips-dspr2-asm.h000066400000000000000000000664531271037650300274430ustar00rootroot00000000000000/* * Copyright (c) 2012 * MIPS Technologies, Inc., California. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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. * * Author: Nemanja Lukic (nlukic@mips.com) */ #ifndef PIXMAN_MIPS_DSPR2_ASM_H #define PIXMAN_MIPS_DSPR2_ASM_H #define zero $0 #define AT $1 #define v0 $2 #define v1 $3 #define a0 $4 #define a1 $5 #define a2 $6 #define a3 $7 #define t0 $8 #define t1 $9 #define t2 $10 #define t3 $11 #define t4 $12 #define t5 $13 #define t6 $14 #define t7 $15 #define s0 $16 #define s1 $17 #define s2 $18 #define s3 $19 #define s4 $20 #define s5 $21 #define s6 $22 #define s7 $23 #define t8 $24 #define t9 $25 #define k0 $26 #define k1 $27 #define gp $28 #define sp $29 #define fp $30 #define s8 $30 #define ra $31 /* * LEAF_MIPS32R2 - declare leaf routine for MIPS32r2 */ #define LEAF_MIPS32R2(symbol) \ .globl symbol; \ .align 2; \ .type symbol, @function; \ .ent symbol, 0; \ symbol: .frame sp, 0, ra; \ .set push; \ .set arch=mips32r2; \ .set noreorder; \ .set noat; /* * LEAF_MIPS32R2 - declare leaf routine for MIPS DSPr2 */ #define LEAF_MIPS_DSPR2(symbol) \ LEAF_MIPS32R2(symbol) \ .set dspr2; /* * END - mark end of function */ #define END(function) \ .set pop; \ .end function; \ .size function,.-function /* * Checks if stack offset is big enough for storing/restoring regs_num * number of register to/from stack. Stack offset must be greater than * or equal to the number of bytes needed for storing registers (regs_num*4). * Since MIPS ABI allows usage of first 16 bytes of stack frame (this is * preserved for input arguments of the functions, already stored in a0-a3), * stack size can be further optimized by utilizing this space. */ .macro CHECK_STACK_OFFSET regs_num, stack_offset .if \stack_offset < \regs_num * 4 - 16 .error "Stack offset too small." .endif .endm /* * Saves set of registers on stack. Maximum number of registers that * can be saved on stack is limitted to 14 (a0-a3, v0-v1 and s0-s7). * Stack offset is number of bytes that are added to stack pointer (sp) * before registers are pushed in order to provide enough space on stack * (offset must be multiple of 4, and must be big enough, as described by * CHECK_STACK_OFFSET macro). This macro is intended to be used in * combination with RESTORE_REGS_FROM_STACK macro. Example: * SAVE_REGS_ON_STACK 4, v0, v1, s0, s1 * RESTORE_REGS_FROM_STACK 4, v0, v1, s0, s1 */ .macro SAVE_REGS_ON_STACK stack_offset = 0, r1, \ r2 = 0, r3 = 0, r4 = 0, \ r5 = 0, r6 = 0, r7 = 0, \ r8 = 0, r9 = 0, r10 = 0, \ r11 = 0, r12 = 0, r13 = 0, \ r14 = 0 .if (\stack_offset < 0) || (\stack_offset - (\stack_offset / 4) * 4) .error "Stack offset must be pozitive and multiple of 4." .endif .if \stack_offset != 0 addiu sp, sp, -\stack_offset .endif sw \r1, 0(sp) .if \r2 != 0 sw \r2, 4(sp) .endif .if \r3 != 0 sw \r3, 8(sp) .endif .if \r4 != 0 sw \r4, 12(sp) .endif .if \r5 != 0 CHECK_STACK_OFFSET 5, \stack_offset sw \r5, 16(sp) .endif .if \r6 != 0 CHECK_STACK_OFFSET 6, \stack_offset sw \r6, 20(sp) .endif .if \r7 != 0 CHECK_STACK_OFFSET 7, \stack_offset sw \r7, 24(sp) .endif .if \r8 != 0 CHECK_STACK_OFFSET 8, \stack_offset sw \r8, 28(sp) .endif .if \r9 != 0 CHECK_STACK_OFFSET 9, \stack_offset sw \r9, 32(sp) .endif .if \r10 != 0 CHECK_STACK_OFFSET 10, \stack_offset sw \r10, 36(sp) .endif .if \r11 != 0 CHECK_STACK_OFFSET 11, \stack_offset sw \r11, 40(sp) .endif .if \r12 != 0 CHECK_STACK_OFFSET 12, \stack_offset sw \r12, 44(sp) .endif .if \r13 != 0 CHECK_STACK_OFFSET 13, \stack_offset sw \r13, 48(sp) .endif .if \r14 != 0 CHECK_STACK_OFFSET 14, \stack_offset sw \r14, 52(sp) .endif .endm /* * Restores set of registers from stack. Maximum number of registers that * can be restored from stack is limitted to 14 (a0-a3, v0-v1 and s0-s7). * Stack offset is number of bytes that are added to stack pointer (sp) * after registers are restored (offset must be multiple of 4, and must * be big enough, as described by CHECK_STACK_OFFSET macro). This macro is * intended to be used in combination with RESTORE_REGS_FROM_STACK macro. * Example: * SAVE_REGS_ON_STACK 4, v0, v1, s0, s1 * RESTORE_REGS_FROM_STACK 4, v0, v1, s0, s1 */ .macro RESTORE_REGS_FROM_STACK stack_offset = 0, r1, \ r2 = 0, r3 = 0, r4 = 0, \ r5 = 0, r6 = 0, r7 = 0, \ r8 = 0, r9 = 0, r10 = 0, \ r11 = 0, r12 = 0, r13 = 0, \ r14 = 0 .if (\stack_offset < 0) || (\stack_offset - (\stack_offset/4)*4) .error "Stack offset must be pozitive and multiple of 4." .endif lw \r1, 0(sp) .if \r2 != 0 lw \r2, 4(sp) .endif .if \r3 != 0 lw \r3, 8(sp) .endif .if \r4 != 0 lw \r4, 12(sp) .endif .if \r5 != 0 CHECK_STACK_OFFSET 5, \stack_offset lw \r5, 16(sp) .endif .if \r6 != 0 CHECK_STACK_OFFSET 6, \stack_offset lw \r6, 20(sp) .endif .if \r7 != 0 CHECK_STACK_OFFSET 7, \stack_offset lw \r7, 24(sp) .endif .if \r8 != 0 CHECK_STACK_OFFSET 8, \stack_offset lw \r8, 28(sp) .endif .if \r9 != 0 CHECK_STACK_OFFSET 9, \stack_offset lw \r9, 32(sp) .endif .if \r10 != 0 CHECK_STACK_OFFSET 10, \stack_offset lw \r10, 36(sp) .endif .if \r11 != 0 CHECK_STACK_OFFSET 11, \stack_offset lw \r11, 40(sp) .endif .if \r12 != 0 CHECK_STACK_OFFSET 12, \stack_offset lw \r12, 44(sp) .endif .if \r13 != 0 CHECK_STACK_OFFSET 13, \stack_offset lw \r13, 48(sp) .endif .if \r14 != 0 CHECK_STACK_OFFSET 14, \stack_offset lw \r14, 52(sp) .endif .if \stack_offset != 0 addiu sp, sp, \stack_offset .endif .endm /* * Conversion of single r5g6b5 pixel (in_565) to single a8r8g8b8 pixel * returned in (out_8888) register. Requires two temporary registers * (scratch1 and scratch2). */ .macro CONVERT_1x0565_TO_1x8888 in_565, \ out_8888, \ scratch1, scratch2 lui \out_8888, 0xff00 sll \scratch1, \in_565, 0x3 andi \scratch2, \scratch1, 0xff ext \scratch1, \in_565, 0x2, 0x3 or \scratch1, \scratch2, \scratch1 or \out_8888, \out_8888, \scratch1 sll \scratch1, \in_565, 0x5 andi \scratch1, \scratch1, 0xfc00 srl \scratch2, \in_565, 0x1 andi \scratch2, \scratch2, 0x300 or \scratch2, \scratch1, \scratch2 or \out_8888, \out_8888, \scratch2 andi \scratch1, \in_565, 0xf800 srl \scratch2, \scratch1, 0x5 andi \scratch2, \scratch2, 0xff00 or \scratch1, \scratch1, \scratch2 sll \scratch1, \scratch1, 0x8 or \out_8888, \out_8888, \scratch1 .endm /* * Conversion of two r5g6b5 pixels (in1_565 and in2_565) to two a8r8g8b8 pixels * returned in (out1_8888 and out2_8888) registers. Requires four scratch * registers (scratch1 ... scratch4). It also requires maskG and maskB for * color component extractions. These masks must have following values: * li maskG, 0x07e007e0 * li maskB, 0x001F001F */ .macro CONVERT_2x0565_TO_2x8888 in1_565, in2_565, \ out1_8888, out2_8888, \ maskG, maskB, \ scratch1, scratch2, scratch3, scratch4 sll \scratch1, \in1_565, 16 or \scratch1, \scratch1, \in2_565 lui \out2_8888, 0xff00 ori \out2_8888, \out2_8888, 0xff00 shrl.ph \scratch2, \scratch1, 11 and \scratch3, \scratch1, \maskG shra.ph \scratch4, \scratch2, 2 shll.ph \scratch2, \scratch2, 3 shll.ph \scratch3, \scratch3, 5 or \scratch2, \scratch2, \scratch4 shrl.qb \scratch4, \scratch3, 6 or \out2_8888, \out2_8888, \scratch2 or \scratch3, \scratch3, \scratch4 and \scratch1, \scratch1, \maskB shll.ph \scratch2, \scratch1, 3 shra.ph \scratch4, \scratch1, 2 or \scratch2, \scratch2, \scratch4 or \scratch3, \scratch2, \scratch3 precrq.ph.w \out1_8888, \out2_8888, \scratch3 precr_sra.ph.w \out2_8888, \scratch3, 0 .endm /* * Conversion of single a8r8g8b8 pixel (in_8888) to single r5g6b5 pixel * returned in (out_565) register. Requires two temporary registers * (scratch1 and scratch2). */ .macro CONVERT_1x8888_TO_1x0565 in_8888, \ out_565, \ scratch1, scratch2 ext \out_565, \in_8888, 0x3, 0x5 srl \scratch1, \in_8888, 0x5 andi \scratch1, \scratch1, 0x07e0 srl \scratch2, \in_8888, 0x8 andi \scratch2, \scratch2, 0xf800 or \out_565, \out_565, \scratch1 or \out_565, \out_565, \scratch2 .endm /* * Conversion of two a8r8g8b8 pixels (in1_8888 and in2_8888) to two r5g6b5 * pixels returned in (out1_565 and out2_565) registers. Requires two temporary * registers (scratch1 and scratch2). It also requires maskR, maskG and maskB * for color component extractions. These masks must have following values: * li maskR, 0xf800f800 * li maskG, 0x07e007e0 * li maskB, 0x001F001F * Value of input register in2_8888 is lost. */ .macro CONVERT_2x8888_TO_2x0565 in1_8888, in2_8888, \ out1_565, out2_565, \ maskR, maskG, maskB, \ scratch1, scratch2 precr.qb.ph \scratch1, \in2_8888, \in1_8888 precrq.qb.ph \in2_8888, \in2_8888, \in1_8888 and \out1_565, \scratch1, \maskR shrl.ph \scratch1, \scratch1, 3 shll.ph \in2_8888, \in2_8888, 3 and \scratch1, \scratch1, \maskB or \out1_565, \out1_565, \scratch1 and \in2_8888, \in2_8888, \maskG or \out1_565, \out1_565, \in2_8888 srl \out2_565, \out1_565, 16 .endm /* * Multiply pixel (a8) with single pixel (a8r8g8b8). It requires maskLSR needed * for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro MIPS_UN8x4_MUL_UN8 s_8888, \ m_8, \ d_8888, \ maskLSR, \ scratch1, scratch2, scratch3 replv.ph \m_8, \m_8 /* 0 | M | 0 | M */ muleu_s.ph.qbl \scratch1, \s_8888, \m_8 /* A*M | R*M */ muleu_s.ph.qbr \scratch2, \s_8888, \m_8 /* G*M | B*M */ shra_r.ph \scratch3, \scratch1, 8 shra_r.ph \d_8888, \scratch2, 8 and \scratch3, \scratch3, \maskLSR /* 0 |A*M| 0 |R*M */ and \d_8888, \d_8888, \maskLSR /* 0 |G*M| 0 |B*M */ addq.ph \scratch1, \scratch1, \scratch3 /* A*M+A*M | R*M+R*M */ addq.ph \scratch2, \scratch2, \d_8888 /* G*M+G*M | B*M+B*M */ shra_r.ph \scratch1, \scratch1, 8 shra_r.ph \scratch2, \scratch2, 8 precr.qb.ph \d_8888, \scratch1, \scratch2 .endm /* * Multiply two pixels (a8) with two pixels (a8r8g8b8). It requires maskLSR * needed for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro MIPS_2xUN8x4_MUL_2xUN8 s1_8888, \ s2_8888, \ m1_8, \ m2_8, \ d1_8888, \ d2_8888, \ maskLSR, \ scratch1, scratch2, scratch3, \ scratch4, scratch5, scratch6 replv.ph \m1_8, \m1_8 /* 0 | M1 | 0 | M1 */ replv.ph \m2_8, \m2_8 /* 0 | M2 | 0 | M2 */ muleu_s.ph.qbl \scratch1, \s1_8888, \m1_8 /* A1*M1 | R1*M1 */ muleu_s.ph.qbr \scratch2, \s1_8888, \m1_8 /* G1*M1 | B1*M1 */ muleu_s.ph.qbl \scratch3, \s2_8888, \m2_8 /* A2*M2 | R2*M2 */ muleu_s.ph.qbr \scratch4, \s2_8888, \m2_8 /* G2*M2 | B2*M2 */ shra_r.ph \scratch5, \scratch1, 8 shra_r.ph \d1_8888, \scratch2, 8 shra_r.ph \scratch6, \scratch3, 8 shra_r.ph \d2_8888, \scratch4, 8 and \scratch5, \scratch5, \maskLSR /* 0 |A1*M1| 0 |R1*M1 */ and \d1_8888, \d1_8888, \maskLSR /* 0 |G1*M1| 0 |B1*M1 */ and \scratch6, \scratch6, \maskLSR /* 0 |A2*M2| 0 |R2*M2 */ and \d2_8888, \d2_8888, \maskLSR /* 0 |G2*M2| 0 |B2*M2 */ addq.ph \scratch1, \scratch1, \scratch5 addq.ph \scratch2, \scratch2, \d1_8888 addq.ph \scratch3, \scratch3, \scratch6 addq.ph \scratch4, \scratch4, \d2_8888 shra_r.ph \scratch1, \scratch1, 8 shra_r.ph \scratch2, \scratch2, 8 shra_r.ph \scratch3, \scratch3, 8 shra_r.ph \scratch4, \scratch4, 8 precr.qb.ph \d1_8888, \scratch1, \scratch2 precr.qb.ph \d2_8888, \scratch3, \scratch4 .endm /* * Multiply pixel (a8r8g8b8) with single pixel (a8r8g8b8). It requires maskLSR * needed for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro MIPS_UN8x4_MUL_UN8x4 s_8888, \ m_8888, \ d_8888, \ maskLSR, \ scratch1, scratch2, scratch3, scratch4 preceu.ph.qbl \scratch1, \m_8888 /* 0 | A | 0 | R */ preceu.ph.qbr \scratch2, \m_8888 /* 0 | G | 0 | B */ muleu_s.ph.qbl \scratch3, \s_8888, \scratch1 /* A*A | R*R */ muleu_s.ph.qbr \scratch4, \s_8888, \scratch2 /* G*G | B*B */ shra_r.ph \scratch1, \scratch3, 8 shra_r.ph \scratch2, \scratch4, 8 and \scratch1, \scratch1, \maskLSR /* 0 |A*A| 0 |R*R */ and \scratch2, \scratch2, \maskLSR /* 0 |G*G| 0 |B*B */ addq.ph \scratch1, \scratch1, \scratch3 addq.ph \scratch2, \scratch2, \scratch4 shra_r.ph \scratch1, \scratch1, 8 shra_r.ph \scratch2, \scratch2, 8 precr.qb.ph \d_8888, \scratch1, \scratch2 .endm /* * Multiply two pixels (a8r8g8b8) with two pixels (a8r8g8b8). It requires * maskLSR needed for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro MIPS_2xUN8x4_MUL_2xUN8x4 s1_8888, \ s2_8888, \ m1_8888, \ m2_8888, \ d1_8888, \ d2_8888, \ maskLSR, \ scratch1, scratch2, scratch3, \ scratch4, scratch5, scratch6 preceu.ph.qbl \scratch1, \m1_8888 /* 0 | A | 0 | R */ preceu.ph.qbr \scratch2, \m1_8888 /* 0 | G | 0 | B */ preceu.ph.qbl \scratch3, \m2_8888 /* 0 | A | 0 | R */ preceu.ph.qbr \scratch4, \m2_8888 /* 0 | G | 0 | B */ muleu_s.ph.qbl \scratch5, \s1_8888, \scratch1 /* A*A | R*R */ muleu_s.ph.qbr \scratch6, \s1_8888, \scratch2 /* G*G | B*B */ muleu_s.ph.qbl \scratch1, \s2_8888, \scratch3 /* A*A | R*R */ muleu_s.ph.qbr \scratch2, \s2_8888, \scratch4 /* G*G | B*B */ shra_r.ph \scratch3, \scratch5, 8 shra_r.ph \scratch4, \scratch6, 8 shra_r.ph \d1_8888, \scratch1, 8 shra_r.ph \d2_8888, \scratch2, 8 and \scratch3, \scratch3, \maskLSR /* 0 |A*A| 0 |R*R */ and \scratch4, \scratch4, \maskLSR /* 0 |G*G| 0 |B*B */ and \d1_8888, \d1_8888, \maskLSR /* 0 |A*A| 0 |R*R */ and \d2_8888, \d2_8888, \maskLSR /* 0 |G*G| 0 |B*B */ addq.ph \scratch3, \scratch3, \scratch5 addq.ph \scratch4, \scratch4, \scratch6 addq.ph \d1_8888, \d1_8888, \scratch1 addq.ph \d2_8888, \d2_8888, \scratch2 shra_r.ph \scratch3, \scratch3, 8 shra_r.ph \scratch4, \scratch4, 8 shra_r.ph \scratch5, \d1_8888, 8 shra_r.ph \scratch6, \d2_8888, 8 precr.qb.ph \d1_8888, \scratch3, \scratch4 precr.qb.ph \d2_8888, \scratch5, \scratch6 .endm /* * OVER operation on single a8r8g8b8 source pixel (s_8888) and single a8r8g8b8 * destination pixel (d_8888) using a8 mask (m_8). It also requires maskLSR * needed for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro OVER_8888_8_8888 s_8888, \ m_8, \ d_8888, \ out_8888, \ maskLSR, \ scratch1, scratch2, scratch3, scratch4 MIPS_UN8x4_MUL_UN8 \s_8888, \m_8, \ \scratch1, \maskLSR, \ \scratch2, \scratch3, \scratch4 not \scratch2, \scratch1 srl \scratch2, \scratch2, 24 MIPS_UN8x4_MUL_UN8 \d_8888, \scratch2, \ \d_8888, \maskLSR, \ \scratch3, \scratch4, \out_8888 addu_s.qb \out_8888, \d_8888, \scratch1 .endm /* * OVER operation on two a8r8g8b8 source pixels (s1_8888 and s2_8888) and two * a8r8g8b8 destination pixels (d1_8888 and d2_8888) using a8 masks (m1_8 and * m2_8). It also requires maskLSR needed for rounding process. maskLSR must * have following value: * li maskLSR, 0x00ff00ff */ .macro OVER_2x8888_2x8_2x8888 s1_8888, \ s2_8888, \ m1_8, \ m2_8, \ d1_8888, \ d2_8888, \ out1_8888, \ out2_8888, \ maskLSR, \ scratch1, scratch2, scratch3, \ scratch4, scratch5, scratch6 MIPS_2xUN8x4_MUL_2xUN8 \s1_8888, \s2_8888, \ \m1_8, \m2_8, \ \scratch1, \scratch2, \ \maskLSR, \ \scratch3, \scratch4, \out1_8888, \ \out2_8888, \scratch5, \scratch6 not \scratch3, \scratch1 srl \scratch3, \scratch3, 24 not \scratch4, \scratch2 srl \scratch4, \scratch4, 24 MIPS_2xUN8x4_MUL_2xUN8 \d1_8888, \d2_8888, \ \scratch3, \scratch4, \ \d1_8888, \d2_8888, \ \maskLSR, \ \scratch5, \scratch6, \out1_8888, \ \out2_8888, \scratch3, \scratch4 addu_s.qb \out1_8888, \d1_8888, \scratch1 addu_s.qb \out2_8888, \d2_8888, \scratch2 .endm /* * OVER operation on single a8r8g8b8 source pixel (s_8888) and single a8r8g8b8 * destination pixel (d_8888). It also requires maskLSR needed for rounding * process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro OVER_8888_8888 s_8888, \ d_8888, \ out_8888, \ maskLSR, \ scratch1, scratch2, scratch3, scratch4 not \scratch1, \s_8888 srl \scratch1, \scratch1, 24 MIPS_UN8x4_MUL_UN8 \d_8888, \scratch1, \ \out_8888, \maskLSR, \ \scratch2, \scratch3, \scratch4 addu_s.qb \out_8888, \out_8888, \s_8888 .endm /* * OVER operation on two a8r8g8b8 source pixels (s1_8888 and s2_8888) and two * a8r8g8b8 destination pixels (d1_8888 and d2_8888). It also requires maskLSR * needed for rounding process. maskLSR must have following value: * li maskLSR, 0x00ff00ff */ .macro OVER_2x8888_2x8888 s1_8888, \ s2_8888, \ d1_8888, \ d2_8888, \ out1_8888, \ out2_8888, \ maskLSR, \ scratch1, scratch2, scratch3, \ scratch4, scratch5, scratch6 not \scratch1, \s1_8888 srl \scratch1, \scratch1, 24 not \scratch2, \s2_8888 srl \scratch2, \scratch2, 24 MIPS_2xUN8x4_MUL_2xUN8 \d1_8888, \d2_8888, \ \scratch1, \scratch2, \ \out1_8888, \out2_8888, \ \maskLSR, \ \scratch3, \scratch4, \scratch5, \ \scratch6, \d1_8888, \d2_8888 addu_s.qb \out1_8888, \out1_8888, \s1_8888 addu_s.qb \out2_8888, \out2_8888, \s2_8888 .endm .macro MIPS_UN8x4_MUL_UN8_ADD_UN8x4 s_8888, \ m_8, \ d_8888, \ out_8888, \ maskLSR, \ scratch1, scratch2, scratch3 MIPS_UN8x4_MUL_UN8 \s_8888, \m_8, \ \out_8888, \maskLSR, \ \scratch1, \scratch2, \scratch3 addu_s.qb \out_8888, \out_8888, \d_8888 .endm .macro MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 s1_8888, \ s2_8888, \ m1_8, \ m2_8, \ d1_8888, \ d2_8888, \ out1_8888, \ out2_8888, \ maskLSR, \ scratch1, scratch2, scratch3, \ scratch4, scratch5, scratch6 MIPS_2xUN8x4_MUL_2xUN8 \s1_8888, \s2_8888, \ \m1_8, \m2_8, \ \out1_8888, \out2_8888, \ \maskLSR, \ \scratch1, \scratch2, \scratch3, \ \scratch4, \scratch5, \scratch6 addu_s.qb \out1_8888, \out1_8888, \d1_8888 addu_s.qb \out2_8888, \out2_8888, \d2_8888 .endm .macro BILINEAR_INTERPOLATE_SINGLE_PIXEL tl, tr, bl, br, \ scratch1, scratch2, \ alpha, red, green, blue \ wt1, wt2, wb1, wb2 andi \scratch1, \tl, 0xff andi \scratch2, \tr, 0xff andi \alpha, \bl, 0xff andi \red, \br, 0xff multu $ac0, \wt1, \scratch1 maddu $ac0, \wt2, \scratch2 maddu $ac0, \wb1, \alpha maddu $ac0, \wb2, \red ext \scratch1, \tl, 8, 8 ext \scratch2, \tr, 8, 8 ext \alpha, \bl, 8, 8 ext \red, \br, 8, 8 multu $ac1, \wt1, \scratch1 maddu $ac1, \wt2, \scratch2 maddu $ac1, \wb1, \alpha maddu $ac1, \wb2, \red ext \scratch1, \tl, 16, 8 ext \scratch2, \tr, 16, 8 ext \alpha, \bl, 16, 8 ext \red, \br, 16, 8 mflo \blue, $ac0 multu $ac2, \wt1, \scratch1 maddu $ac2, \wt2, \scratch2 maddu $ac2, \wb1, \alpha maddu $ac2, \wb2, \red ext \scratch1, \tl, 24, 8 ext \scratch2, \tr, 24, 8 ext \alpha, \bl, 24, 8 ext \red, \br, 24, 8 mflo \green, $ac1 multu $ac3, \wt1, \scratch1 maddu $ac3, \wt2, \scratch2 maddu $ac3, \wb1, \alpha maddu $ac3, \wb2, \red mflo \red, $ac2 mflo \alpha, $ac3 precr.qb.ph \alpha, \alpha, \red precr.qb.ph \scratch1, \green, \blue precrq.qb.ph \tl, \alpha, \scratch1 .endm #endif //PIXMAN_MIPS_DSPR2_ASM_H Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips-dspr2.c000066400000000000000000000614061271037650300266510ustar00rootroot00000000000000/* * Copyright (c) 2012 * MIPS Technologies, Inc., California. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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. * * Author: Nemanja Lukic (nlukic@mips.com) */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include "pixman-mips-dspr2.h" PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_x888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_8888_0565, uint32_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0565_8888, uint16_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_0565_0565, uint16_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_0888_0888, uint8_t, 3, uint8_t, 3) #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0888_8888_rev, uint8_t, 3, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0888_0565_rev, uint8_t, 3, uint16_t, 1) #endif PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_pixbuf_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_rpixbuf_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, over_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, over_8888_0565, uint32_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, add_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, add_8888_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, out_reverse_8_0565, uint8_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, out_reverse_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (0, src_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (0, src_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8888_8888_ca, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8888_0565_ca, uint32_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_0565, uint8_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, add_n_8_8, uint8_t, 1, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, add_n_8_8888, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_8888_n_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_8888_n_0565, uint32_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_0565_n_0565, uint16_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, add_8888_n_8888, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_n_0565, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_n_8888, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_reverse_n_8888, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_N_DST (0, in_n_8, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8_8_8, uint8_t, 1, uint8_t, 1, uint8_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8888_8_8888, uint32_t, 1, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8888_8888_8888, uint32_t, 1, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_0565_8_0565, uint16_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8_8888, uint32_t, 1, uint8_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8_0565, uint32_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_0565_8_0565, uint16_t, 1, uint8_t, 1, uint16_t, 1) PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8888_8888, uint32_t, 1, uint32_t, 1, uint32_t, 1) PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (8888_8888, OVER, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (8888_0565, OVER, uint32_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (0565_8888, SRC, uint16_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 8888_8888, SRC, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 8888_0565, SRC, uint32_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 0565_8888, SRC, uint16_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 0565_0565, SRC, uint16_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, 8888_8888, OVER, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, 8888_8888, ADD, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_0565, OVER, uint32_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, 0565_8_0565, OVER, uint16_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 8888_8_8888, SRC, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 8888_8_0565, SRC, uint32_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 0565_8_x888, SRC, uint16_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 0565_8_0565, SRC, uint16_t, uint16_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_8888, OVER, uint32_t, uint32_t) PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_8888, ADD, uint32_t, uint32_t) static pixman_bool_t mips_dspr2_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t _xor) { uint8_t *byte_line; uint32_t byte_width; switch (bpp) { case 16: stride = stride * (int) sizeof (uint32_t) / 2; byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); byte_width = width * 2; stride *= 2; while (height--) { uint8_t *dst = byte_line; byte_line += stride; pixman_fill_buff16_mips (dst, byte_width, _xor & 0xffff); } return TRUE; case 32: stride = stride * (int) sizeof (uint32_t) / 4; byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); byte_width = width * 4; stride *= 4; while (height--) { uint8_t *dst = byte_line; byte_line += stride; pixman_fill_buff32_mips (dst, byte_width, _xor); } return TRUE; default: return FALSE; } } static pixman_bool_t mips_dspr2_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { if (src_bpp != dst_bpp) return FALSE; uint8_t *src_bytes; uint8_t *dst_bytes; uint32_t byte_width; switch (src_bpp) { case 16: src_stride = src_stride * (int) sizeof (uint32_t) / 2; dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; src_bytes =(uint8_t *)(((uint16_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = width * 2; src_stride *= 2; dst_stride *= 2; while (height--) { uint8_t *src = src_bytes; uint8_t *dst = dst_bytes; src_bytes += src_stride; dst_bytes += dst_stride; pixman_mips_fast_memcpy (dst, src, byte_width); } return TRUE; case 32: src_stride = src_stride * (int) sizeof (uint32_t) / 4; dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; src_bytes = (uint8_t *)(((uint32_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = width * 4; src_stride *= 4; dst_stride *= 4; while (height--) { uint8_t *src = src_bytes; uint8_t *dst = dst_bytes; src_bytes += src_stride; dst_bytes += dst_stride; pixman_mips_fast_memcpy (dst, src, byte_width); } return TRUE; default: return FALSE; } } static const pixman_fast_path_t mips_dspr2_fast_paths[] = { PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, mips_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, mips_composite_src_0565_0565), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, mips_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, mips_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, mips_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, mips_composite_src_8888_0565), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, mips_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, mips_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, mips_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, mips_composite_src_0565_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, mips_composite_src_8888_8888), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, mips_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, mips_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, mips_composite_src_0888_0888), #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, x8r8g8b8, mips_composite_src_0888_8888_rev), PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, r5g6b5, mips_composite_src_0888_0565_rev), #endif PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8r8g8b8, mips_composite_src_pixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8b8g8r8, mips_composite_src_rpixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8r8g8b8, mips_composite_src_rpixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8b8g8r8, mips_composite_src_pixbuf_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, mips_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, mips_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, mips_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, mips_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8, mips_composite_src_n_8_8), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, mips_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, mips_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, mips_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, mips_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, mips_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, mips_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8, mips_composite_over_n_8_8), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, mips_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, mips_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, mips_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, mips_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, mips_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, mips_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, mips_composite_over_n_0565), PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, mips_composite_over_n_8888), PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, mips_composite_over_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, mips_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, mips_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, r5g6b5, mips_composite_over_8888_n_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, b5g6r5, mips_composite_over_8888_n_0565), PIXMAN_STD_FAST_PATH (OVER, r5g6b5, solid, r5g6b5, mips_composite_over_0565_n_0565), PIXMAN_STD_FAST_PATH (OVER, b5g6r5, solid, b5g6r5, mips_composite_over_0565_n_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, mips_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, mips_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, mips_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, mips_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, r5g6b5, mips_composite_over_8888_8_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, b5g6r5, mips_composite_over_8888_8_0565), PIXMAN_STD_FAST_PATH (OVER, r5g6b5, a8, r5g6b5, mips_composite_over_0565_8_0565), PIXMAN_STD_FAST_PATH (OVER, b5g6r5, a8, b5g6r5, mips_composite_over_0565_8_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, mips_composite_over_8888_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, mips_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, mips_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, mips_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, mips_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, mips_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, mips_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, mips_composite_add_n_8_8), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, mips_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, mips_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8, a8, a8, mips_composite_add_8_8_8), PIXMAN_STD_FAST_PATH (ADD, r5g6b5, a8, r5g6b5, mips_composite_add_0565_8_0565), PIXMAN_STD_FAST_PATH (ADD, b5g6r5, a8, b5g6r5, mips_composite_add_0565_8_0565), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8, a8r8g8b8, mips_composite_add_8888_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, a8, a8b8g8r8, mips_composite_add_8888_8_8888), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, a8r8g8b8, mips_composite_add_8888_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, solid, a8r8g8b8, mips_composite_add_8888_n_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, solid, a8b8g8r8, mips_composite_add_8888_n_8888), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, mips_composite_add_8_8), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, mips_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, mips_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, r5g6b5, mips_composite_out_reverse_8_0565), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, b5g6r5, mips_composite_out_reverse_8_0565), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8r8g8b8, mips_composite_out_reverse_8_8888), PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8b8g8r8, mips_composite_out_reverse_8_8888), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, mips_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, mips_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (IN, solid, null, a8, mips_composite_in_n_8), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8888), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mips_8888_8888), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8888), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mips_8888_8888), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, mips_8888_0565), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, b5g6r5, mips_8888_0565), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, x8b8g8r8, mips_0565_8888), PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8888), /* Note: NONE repeat is not supported yet */ SIMPLE_NEAREST_FAST_PATH_COVER (SRC, r5g6b5, a8r8g8b8, mips_0565_8888), SIMPLE_NEAREST_FAST_PATH_COVER (SRC, b5g6r5, a8b8g8r8, mips_0565_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, r5g6b5, a8r8g8b8, mips_0565_8888), SIMPLE_NEAREST_FAST_PATH_PAD (SRC, b5g6r5, a8b8g8r8, mips_0565_8888), PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8r8g8b8, r5g6b5, mips_8888_8_0565), PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8b8g8r8, b5g6r5, mips_8888_8_0565), PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, r5g6b5, r5g6b5, mips_0565_8_0565), PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, b5g6r5, b5g6r5, mips_0565_8_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, r5g6b5, mips_8888_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, r5g6b5, mips_8888_0565), SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, mips_0565_0565), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, mips_8888_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, r5g6b5, mips_8888_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, r5g6b5, mips_8888_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8_x888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, r5g6b5, mips_0565_8_0565), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), { PIXMAN_OP_NONE }, }; static void mips_dspr2_combine_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) pixman_composite_over_8888_8888_8888_asm_mips ( dest, (uint32_t *)src, (uint32_t *)mask, width); else pixman_composite_over_8888_8888_asm_mips ( dest, (uint32_t *)src, width); } pixman_implementation_t * _pixman_implementation_create_mips_dspr2 (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, mips_dspr2_fast_paths); imp->combine_32[PIXMAN_OP_OVER] = mips_dspr2_combine_over_u; imp->blt = mips_dspr2_blt; imp->fill = mips_dspr2_fill; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips-dspr2.h000066400000000000000000000704231271037650300266550ustar00rootroot00000000000000/* * Copyright (c) 2012 * MIPS Technologies, Inc., California. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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. * * Author: Nemanja Lukic (nlukic@mips.com) */ #ifndef PIXMAN_MIPS_DSPR2_H #define PIXMAN_MIPS_DSPR2_H #include "pixman-private.h" #include "pixman-inlines.h" #define SKIP_ZERO_SRC 1 #define SKIP_ZERO_MASK 2 #define DO_FAST_MEMCPY 3 void pixman_mips_fast_memcpy (void *dst, void *src, uint32_t n_bytes); void pixman_fill_buff16_mips (void *dst, uint32_t n_bytes, uint16_t value); void pixman_fill_buff32_mips (void *dst, uint32_t n_bytes, uint32_t value); /****************************************************************/ #define PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST(flags, name, \ src_type, src_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_mips (dst_type *dst, \ src_type *src, \ int32_t w); \ \ static void \ mips_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line, *dst; \ src_type *src_line, *src; \ int32_t dst_stride, src_stride; \ int bpp = PIXMAN_FORMAT_BPP (dest_image->bits.format) / 8; \ \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ \ while (height--) \ { \ dst = dst_line; \ dst_line += dst_stride; \ src = src_line; \ src_line += src_stride; \ \ if (flags == DO_FAST_MEMCPY) \ pixman_mips_fast_memcpy (dst, src, width * bpp); \ else \ pixman_composite_##name##_asm_mips (dst, src, width); \ } \ } /****************************************************************/ #define PIXMAN_MIPS_BIND_FAST_PATH_N_DST(flags, name, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_mips (dst_type *dst, \ uint32_t src, \ int32_t w); \ \ static void \ mips_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line, *dst; \ int32_t dst_stride; \ uint32_t src; \ \ src = _pixman_image_get_solid ( \ imp, src_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_SRC) && src == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ \ while (height--) \ { \ dst = dst_line; \ dst_line += dst_stride; \ \ pixman_composite_##name##_asm_mips (dst, src, width); \ } \ } /*******************************************************************/ #define PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST(flags, name, \ mask_type, mask_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_mips (dst_type *dst, \ uint32_t src, \ mask_type *mask, \ int32_t w); \ \ static void \ mips_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line, *dst; \ mask_type *mask_line, *mask; \ int32_t dst_stride, mask_stride; \ uint32_t src; \ \ src = _pixman_image_get_solid ( \ imp, src_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_SRC) && src == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ mask_stride, mask_line, mask_cnt); \ \ while (height--) \ { \ dst = dst_line; \ dst_line += dst_stride; \ mask = mask_line; \ mask_line += mask_stride; \ pixman_composite_##name##_asm_mips (dst, src, mask, width); \ } \ } /*******************************************************************/ #define PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST(flags, name, \ src_type, src_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_mips (dst_type *dst, \ src_type *src, \ uint32_t mask, \ int32_t w); \ \ static void \ mips_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line, *dst; \ src_type *src_line, *src; \ int32_t dst_stride, src_stride; \ uint32_t mask; \ \ mask = _pixman_image_get_solid ( \ imp, mask_image, dest_image->bits.format); \ \ if ((flags & SKIP_ZERO_MASK) && mask == 0) \ return; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ \ while (height--) \ { \ dst = dst_line; \ dst_line += dst_stride; \ src = src_line; \ src_line += src_stride; \ \ pixman_composite_##name##_asm_mips (dst, src, mask, width); \ } \ } /************************************************************************/ #define PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST(name, src_type, src_cnt, \ mask_type, mask_cnt, \ dst_type, dst_cnt) \ void \ pixman_composite_##name##_asm_mips (dst_type *dst, \ src_type *src, \ mask_type *mask, \ int32_t w); \ \ static void \ mips_composite_##name (pixman_implementation_t *imp, \ pixman_composite_info_t *info) \ { \ PIXMAN_COMPOSITE_ARGS (info); \ dst_type *dst_line, *dst; \ src_type *src_line, *src; \ mask_type *mask_line, *mask; \ int32_t dst_stride, src_stride, mask_stride; \ \ PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ dst_stride, dst_line, dst_cnt); \ PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ src_stride, src_line, src_cnt); \ PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ mask_stride, mask_line, mask_cnt); \ \ while (height--) \ { \ dst = dst_line; \ dst_line += dst_stride; \ mask = mask_line; \ mask_line += mask_stride; \ src = src_line; \ src_line += src_stride; \ pixman_composite_##name##_asm_mips (dst, src, mask, width); \ } \ } /****************************************************************************/ #define PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST(name, op, \ src_type, dst_type) \ void \ pixman_scaled_nearest_scanline_##name##_##op##_asm_mips ( \ dst_type * dst, \ const src_type * src, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x); \ \ static force_inline void \ scaled_nearest_scanline_mips_##name##_##op (dst_type * pd, \ const src_type * ps, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ pixman_scaled_nearest_scanline_##name##_##op##_asm_mips (pd, ps, w, \ vx, unit_x); \ } \ \ FAST_NEAREST_MAINLOOP (mips_##name##_cover_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, dst_type, COVER) \ FAST_NEAREST_MAINLOOP (mips_##name##_none_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, dst_type, NONE) \ FAST_NEAREST_MAINLOOP (mips_##name##_pad_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, dst_type, PAD) /* Provide entries for the fast path table */ #define PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_FAST_PATH_PAD (op,s,d,func) /*****************************************************************************/ #define PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST(flags, name, op, \ src_type, dst_type) \ void \ pixman_scaled_nearest_scanline_##name##_##op##_asm_mips ( \ dst_type * dst, \ const src_type * src, \ const uint8_t * mask, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x); \ \ static force_inline void \ scaled_nearest_scanline_mips_##name##_##op (const uint8_t * mask, \ dst_type * pd, \ const src_type * ps, \ int32_t w, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_nearest_scanline_##name##_##op##_asm_mips (pd, ps, \ mask, w, \ vx, unit_x); \ } \ \ FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_cover_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, COVER, TRUE, FALSE)\ FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_none_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, NONE, TRUE, FALSE) \ FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_pad_##op, \ scaled_nearest_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, PAD, TRUE, FALSE) /* Provide entries for the fast path table */ #define PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH(op,s,d,func) \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD (op,s,d,func) /****************************************************************************/ #define PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST(flags, name, op, \ src_type, dst_type) \ void \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips( \ dst_type * dst, \ const src_type * src_top, \ const src_type * src_bottom, \ int32_t w, \ int wt, \ int wb, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x); \ static force_inline void \ scaled_bilinear_scanline_mips_##name##_##op (dst_type * dst, \ const uint32_t * mask, \ const src_type * src_top, \ const src_type * src_bottom, \ int32_t w, \ int wt, \ int wb, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips (dst, src_top, \ src_bottom, w, \ wt, wb, \ vx, unit_x); \ } \ \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_cover_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint32_t, dst_type, COVER, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_none_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint32_t, dst_type, NONE, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_pad_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint32_t, dst_type, PAD, FLAG_NONE) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_normal_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint32_t, dst_type, NORMAL, \ FLAG_NONE) /*****************************************************************************/ #define PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST(flags, name, op, \ src_type, dst_type) \ void \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips ( \ dst_type * dst, \ const uint8_t * mask, \ const src_type * top, \ const src_type * bottom, \ int wt, \ int wb, \ pixman_fixed_t x, \ pixman_fixed_t ux, \ int width); \ \ static force_inline void \ scaled_bilinear_scanline_mips_##name##_##op (dst_type * dst, \ const uint8_t * mask, \ const src_type * src_top, \ const src_type * src_bottom, \ int32_t w, \ int wt, \ int wb, \ pixman_fixed_t vx, \ pixman_fixed_t unit_x, \ pixman_fixed_t max_vx, \ pixman_bool_t zero_src) \ { \ if ((flags & SKIP_ZERO_SRC) && zero_src) \ return; \ pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips ( \ dst, mask, src_top, src_bottom, wt, wb, vx, unit_x, w); \ } \ \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_cover_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, COVER, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_none_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, NONE, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_pad_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, PAD, \ FLAG_HAVE_NON_SOLID_MASK) \ FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_normal_##op, \ scaled_bilinear_scanline_mips_##name##_##op, \ src_type, uint8_t, dst_type, NORMAL, \ FLAG_HAVE_NON_SOLID_MASK) #endif //PIXMAN_MIPS_DSPR2_H Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips-memcpy-asm.S000066400000000000000000000244741271037650300276530ustar00rootroot00000000000000/* * Copyright (c) 2012 * MIPS Technologies, Inc., California. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the MIPS Technologies, Inc., 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 MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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. */ #include "pixman-mips-dspr2-asm.h" /* * This routine could be optimized for MIPS64. The current code only * uses MIPS32 instructions. */ #ifdef EB # define LWHI lwl /* high part is left in big-endian */ # define SWHI swl /* high part is left in big-endian */ # define LWLO lwr /* low part is right in big-endian */ # define SWLO swr /* low part is right in big-endian */ #else # define LWHI lwr /* high part is right in little-endian */ # define SWHI swr /* high part is right in little-endian */ # define LWLO lwl /* low part is left in big-endian */ # define SWLO swl /* low part is left in big-endian */ #endif LEAF_MIPS32R2(pixman_mips_fast_memcpy) slti AT, a2, 8 bne AT, zero, $last8 move v0, a0 /* memcpy returns the dst pointer */ /* Test if the src and dst are word-aligned, or can be made word-aligned */ xor t8, a1, a0 andi t8, t8, 0x3 /* t8 is a0/a1 word-displacement */ bne t8, zero, $unaligned negu a3, a0 andi a3, a3, 0x3 /* we need to copy a3 bytes to make a0/a1 aligned */ beq a3, zero, $chk16w /* when a3=0 then the dst (a0) is word-aligned */ subu a2, a2, a3 /* now a2 is the remining bytes count */ LWHI t8, 0(a1) addu a1, a1, a3 SWHI t8, 0(a0) addu a0, a0, a3 /* Now the dst/src are mutually word-aligned with word-aligned addresses */ $chk16w: andi t8, a2, 0x3f /* any whole 64-byte chunks? */ /* t8 is the byte count after 64-byte chunks */ beq a2, t8, $chk8w /* if a2==t8, no 64-byte chunks */ /* There will be at most 1 32-byte chunk after it */ subu a3, a2, t8 /* subtract from a2 the reminder */ /* Here a3 counts bytes in 16w chunks */ addu a3, a0, a3 /* Now a3 is the final dst after 64-byte chunks */ addu t0, a0, a2 /* t0 is the "past the end" address */ /* * When in the loop we exercise "pref 30, x(a0)", the a0+x should not be past * the "t0-32" address * This means: for x=128 the last "safe" a0 address is "t0-160" * Alternatively, for x=64 the last "safe" a0 address is "t0-96" * In the current version we use "pref 30, 128(a0)", so "t0-160" is the limit */ subu t9, t0, 160 /* t9 is the "last safe pref 30, 128(a0)" address */ pref 0, 0(a1) /* bring the first line of src, addr 0 */ pref 0, 32(a1) /* bring the second line of src, addr 32 */ pref 0, 64(a1) /* bring the third line of src, addr 64 */ pref 30, 32(a0) /* safe, as we have at least 64 bytes ahead */ /* In case the a0 > t9 don't use "pref 30" at all */ sgtu v1, a0, t9 bgtz v1, $loop16w /* skip "pref 30, 64(a0)" for too short arrays */ nop /* otherwise, start with using pref30 */ pref 30, 64(a0) $loop16w: pref 0, 96(a1) lw t0, 0(a1) bgtz v1, $skip_pref30_96 /* skip "pref 30, 96(a0)" */ lw t1, 4(a1) pref 30, 96(a0) /* continue setting up the dest, addr 96 */ $skip_pref30_96: lw t2, 8(a1) lw t3, 12(a1) lw t4, 16(a1) lw t5, 20(a1) lw t6, 24(a1) lw t7, 28(a1) pref 0, 128(a1) /* bring the next lines of src, addr 128 */ sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) lw t0, 32(a1) bgtz v1, $skip_pref30_128 /* skip "pref 30, 128(a0)" */ lw t1, 36(a1) pref 30, 128(a0) /* continue setting up the dest, addr 128 */ $skip_pref30_128: lw t2, 40(a1) lw t3, 44(a1) lw t4, 48(a1) lw t5, 52(a1) lw t6, 56(a1) lw t7, 60(a1) pref 0, 160(a1) /* bring the next lines of src, addr 160 */ sw t0, 32(a0) sw t1, 36(a0) sw t2, 40(a0) sw t3, 44(a0) sw t4, 48(a0) sw t5, 52(a0) sw t6, 56(a0) sw t7, 60(a0) addiu a0, a0, 64 /* adding 64 to dest */ sgtu v1, a0, t9 bne a0, a3, $loop16w addiu a1, a1, 64 /* adding 64 to src */ move a2, t8 /* Here we have src and dest word-aligned but less than 64-bytes to go */ $chk8w: pref 0, 0x0(a1) andi t8, a2, 0x1f /* is there a 32-byte chunk? */ /* the t8 is the reminder count past 32-bytes */ beq a2, t8, $chk1w /* when a2=t8, no 32-byte chunk */ nop lw t0, 0(a1) lw t1, 4(a1) lw t2, 8(a1) lw t3, 12(a1) lw t4, 16(a1) lw t5, 20(a1) lw t6, 24(a1) lw t7, 28(a1) addiu a1, a1, 32 sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) addiu a0, a0, 32 $chk1w: andi a2, t8, 0x3 /* now a2 is the reminder past 1w chunks */ beq a2, t8, $last8 subu a3, t8, a2 /* a3 is count of bytes in 1w chunks */ addu a3, a0, a3 /* now a3 is the dst address past the 1w chunks */ /* copying in words (4-byte chunks) */ $wordCopy_loop: lw t3, 0(a1) /* the first t3 may be equal t0 ... optimize? */ addiu a1, a1, 4 addiu a0, a0, 4 bne a0, a3, $wordCopy_loop sw t3, -4(a0) /* For the last (<8) bytes */ $last8: blez a2, leave addu a3, a0, a2 /* a3 is the last dst address */ $last8loop: lb v1, 0(a1) addiu a1, a1, 1 addiu a0, a0, 1 bne a0, a3, $last8loop sb v1, -1(a0) leave: j ra nop /* * UNALIGNED case */ $unaligned: /* got here with a3="negu a0" */ andi a3, a3, 0x3 /* test if the a0 is word aligned */ beqz a3, $ua_chk16w subu a2, a2, a3 /* bytes left after initial a3 bytes */ LWHI v1, 0(a1) LWLO v1, 3(a1) addu a1, a1, a3 /* a3 may be here 1, 2 or 3 */ SWHI v1, 0(a0) addu a0, a0, a3 /* below the dst will be word aligned (NOTE1) */ $ua_chk16w: andi t8, a2, 0x3f /* any whole 64-byte chunks? */ /* t8 is the byte count after 64-byte chunks */ beq a2, t8, $ua_chk8w /* if a2==t8, no 64-byte chunks */ /* There will be at most 1 32-byte chunk after it */ subu a3, a2, t8 /* subtract from a2 the reminder */ /* Here a3 counts bytes in 16w chunks */ addu a3, a0, a3 /* Now a3 is the final dst after 64-byte chunks */ addu t0, a0, a2 /* t0 is the "past the end" address */ subu t9, t0, 160 /* t9 is the "last safe pref 30, 128(a0)" address */ pref 0, 0(a1) /* bring the first line of src, addr 0 */ pref 0, 32(a1) /* bring the second line of src, addr 32 */ pref 0, 64(a1) /* bring the third line of src, addr 64 */ pref 30, 32(a0) /* safe, as we have at least 64 bytes ahead */ /* In case the a0 > t9 don't use "pref 30" at all */ sgtu v1, a0, t9 bgtz v1, $ua_loop16w /* skip "pref 30, 64(a0)" for too short arrays */ nop /* otherwise, start with using pref30 */ pref 30, 64(a0) $ua_loop16w: pref 0, 96(a1) LWHI t0, 0(a1) LWLO t0, 3(a1) LWHI t1, 4(a1) bgtz v1, $ua_skip_pref30_96 LWLO t1, 7(a1) pref 30, 96(a0) /* continue setting up the dest, addr 96 */ $ua_skip_pref30_96: LWHI t2, 8(a1) LWLO t2, 11(a1) LWHI t3, 12(a1) LWLO t3, 15(a1) LWHI t4, 16(a1) LWLO t4, 19(a1) LWHI t5, 20(a1) LWLO t5, 23(a1) LWHI t6, 24(a1) LWLO t6, 27(a1) LWHI t7, 28(a1) LWLO t7, 31(a1) pref 0, 128(a1) /* bring the next lines of src, addr 128 */ sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) LWHI t0, 32(a1) LWLO t0, 35(a1) LWHI t1, 36(a1) bgtz v1, $ua_skip_pref30_128 LWLO t1, 39(a1) pref 30, 128(a0) /* continue setting up the dest, addr 128 */ $ua_skip_pref30_128: LWHI t2, 40(a1) LWLO t2, 43(a1) LWHI t3, 44(a1) LWLO t3, 47(a1) LWHI t4, 48(a1) LWLO t4, 51(a1) LWHI t5, 52(a1) LWLO t5, 55(a1) LWHI t6, 56(a1) LWLO t6, 59(a1) LWHI t7, 60(a1) LWLO t7, 63(a1) pref 0, 160(a1) /* bring the next lines of src, addr 160 */ sw t0, 32(a0) sw t1, 36(a0) sw t2, 40(a0) sw t3, 44(a0) sw t4, 48(a0) sw t5, 52(a0) sw t6, 56(a0) sw t7, 60(a0) addiu a0, a0, 64 /* adding 64 to dest */ sgtu v1, a0, t9 bne a0, a3, $ua_loop16w addiu a1, a1, 64 /* adding 64 to src */ move a2, t8 /* Here we have src and dest word-aligned but less than 64-bytes to go */ $ua_chk8w: pref 0, 0x0(a1) andi t8, a2, 0x1f /* is there a 32-byte chunk? */ /* the t8 is the reminder count */ beq a2, t8, $ua_chk1w /* when a2=t8, no 32-byte chunk */ LWHI t0, 0(a1) LWLO t0, 3(a1) LWHI t1, 4(a1) LWLO t1, 7(a1) LWHI t2, 8(a1) LWLO t2, 11(a1) LWHI t3, 12(a1) LWLO t3, 15(a1) LWHI t4, 16(a1) LWLO t4, 19(a1) LWHI t5, 20(a1) LWLO t5, 23(a1) LWHI t6, 24(a1) LWLO t6, 27(a1) LWHI t7, 28(a1) LWLO t7, 31(a1) addiu a1, a1, 32 sw t0, 0(a0) sw t1, 4(a0) sw t2, 8(a0) sw t3, 12(a0) sw t4, 16(a0) sw t5, 20(a0) sw t6, 24(a0) sw t7, 28(a0) addiu a0, a0, 32 $ua_chk1w: andi a2, t8, 0x3 /* now a2 is the reminder past 1w chunks */ beq a2, t8, $ua_smallCopy subu a3, t8, a2 /* a3 is count of bytes in 1w chunks */ addu a3, a0, a3 /* now a3 is the dst address past the 1w chunks */ /* copying in words (4-byte chunks) */ $ua_wordCopy_loop: LWHI v1, 0(a1) LWLO v1, 3(a1) addiu a1, a1, 4 addiu a0, a0, 4 /* note: dst=a0 is word aligned here, see NOTE1 */ bne a0, a3, $ua_wordCopy_loop sw v1, -4(a0) /* Now less than 4 bytes (value in a2) left to copy */ $ua_smallCopy: beqz a2, leave addu a3, a0, a2 /* a3 is the last dst address */ $ua_smallCopy_loop: lb v1, 0(a1) addiu a1, a1, 1 addiu a0, a0, 1 bne a0, a3, $ua_smallCopy_loop sb v1, -1(a0) j ra nop END(pixman_mips_fast_memcpy) Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mips.c000066400000000000000000000057541271037650300256250ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #if defined(USE_MIPS_DSPR2) || defined(USE_LOONGSON_MMI) #include #include static pixman_bool_t have_feature (const char *search_string) { #if defined (__linux__) /* linux ELF */ /* Simple detection of MIPS features at runtime for Linux. * It is based on /proc/cpuinfo, which reveals hardware configuration * to user-space applications. According to MIPS (early 2010), no similar * facility is universally available on the MIPS architectures, so it's up * to individual OSes to provide such. */ const char *file_name = "/proc/cpuinfo"; char cpuinfo_line[256]; FILE *f = NULL; if ((f = fopen (file_name, "r")) == NULL) return FALSE; while (fgets (cpuinfo_line, sizeof (cpuinfo_line), f) != NULL) { if (strstr (cpuinfo_line, search_string) != NULL) { fclose (f); return TRUE; } } fclose (f); #endif /* Did not find string in the proc file, or not Linux ELF. */ return FALSE; } #endif pixman_implementation_t * _pixman_mips_get_implementations (pixman_implementation_t *imp) { #ifdef USE_LOONGSON_MMI /* I really don't know if some Loongson CPUs don't have MMI. */ if (!_pixman_disabled ("loongson-mmi") && have_feature ("Loongson")) imp = _pixman_implementation_create_mmx (imp); #endif #ifdef USE_MIPS_DSPR2 if (!_pixman_disabled ("mips-dspr2")) { int already_compiling_everything_for_dspr2 = 0; #if defined(__mips_dsp) && (__mips_dsp_rev >= 2) already_compiling_everything_for_dspr2 = 1; #endif if (already_compiling_everything_for_dspr2 || /* Only currently available MIPS core that supports DSPr2 is 74K. */ have_feature ("MIPS 74K")) { imp = _pixman_implementation_create_mips_dspr2 (imp); } } #endif return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-mmx.c000066400000000000000000003010351271037650300254450ustar00rootroot00000000000000/* * Copyright © 2004, 2005 Red Hat, Inc. * Copyright © 2004 Nicholas Miell * Copyright © 2005 Trolltech AS * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Søren Sandmann (sandmann@redhat.com) * Minor Improvements: Nicholas Miell (nmiell@gmail.com) * MMX code paths for fbcompose.c by Lars Knoll (lars@trolltech.com) * * Based on work by Owen Taylor */ #ifdef HAVE_CONFIG_H #include #endif #if defined USE_X86_MMX || defined USE_ARM_IWMMXT || defined USE_LOONGSON_MMI #ifdef USE_LOONGSON_MMI #include #else #include #endif #include "pixman-private.h" #include "pixman-combine32.h" #include "pixman-inlines.h" #ifdef VERBOSE #define CHECKPOINT() error_f ("at %s %d\n", __FUNCTION__, __LINE__) #else #define CHECKPOINT() #endif #if defined USE_ARM_IWMMXT && __GNUC__ == 4 && __GNUC_MINOR__ < 8 /* Empty the multimedia state. For some reason, ARM's mmintrin.h doesn't provide this. */ extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_empty (void) { } #endif #ifdef USE_X86_MMX # if (defined(__SUNPRO_C) || defined(_MSC_VER) || defined(_WIN64)) # include # else /* We have to compile with -msse to use xmmintrin.h, but that causes SSE * instructions to be generated that we don't want. Just duplicate the * functions we want to use. */ extern __inline int __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_movemask_pi8 (__m64 __A) { int ret; asm ("pmovmskb %1, %0\n\t" : "=r" (ret) : "y" (__A) ); return ret; } extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_mulhi_pu16 (__m64 __A, __m64 __B) { asm ("pmulhuw %1, %0\n\t" : "+y" (__A) : "y" (__B) ); return __A; } # ifdef __OPTIMIZE__ extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) _mm_shuffle_pi16 (__m64 __A, int8_t const __N) { __m64 ret; asm ("pshufw %2, %1, %0\n\t" : "=y" (ret) : "y" (__A), "K" (__N) ); return ret; } # else # define _mm_shuffle_pi16(A, N) \ ({ \ __m64 ret; \ \ asm ("pshufw %2, %1, %0\n\t" \ : "=y" (ret) \ : "y" (A), "K" ((const int8_t)N) \ ); \ \ ret; \ }) # endif # endif #endif #ifndef _MSC_VER #define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0)) #endif /* Notes about writing mmx code * * give memory operands as the second operand. If you give it as the * first, gcc will first load it into a register, then use that * register * * ie. use * * _mm_mullo_pi16 (x, mmx_constant); * * not * * _mm_mullo_pi16 (mmx_constant, x); * * Also try to minimize dependencies. i.e. when you need a value, try * to calculate it from a value that was calculated as early as * possible. */ /* --------------- MMX primitives ------------------------------------- */ /* If __m64 is defined as a struct or union, then define M64_MEMBER to be * the name of the member used to access the data. * If __m64 requires using mm_cvt* intrinsics functions to convert between * uint64_t and __m64 values, then define USE_CVT_INTRINSICS. * If __m64 and uint64_t values can just be cast to each other directly, * then define USE_M64_CASTS. * If __m64 is a double datatype, then define USE_M64_DOUBLE. */ #ifdef _MSC_VER # define M64_MEMBER m64_u64 #elif defined(__ICC) # define USE_CVT_INTRINSICS #elif defined(USE_LOONGSON_MMI) # define USE_M64_DOUBLE #elif defined(__GNUC__) # define USE_M64_CASTS #elif defined(__SUNPRO_C) # if (__SUNPRO_C >= 0x5120) && !defined(__NOVECTORSIZE__) /* Solaris Studio 12.3 (Sun C 5.12) introduces __attribute__(__vector_size__) * support, and defaults to using it to define __m64, unless __NOVECTORSIZE__ * is defined. If it is used, then the mm_cvt* intrinsics must be used. */ # define USE_CVT_INTRINSICS # else /* For Studio 12.2 or older, or when __attribute__(__vector_size__) is * disabled, __m64 is defined as a struct containing "unsigned long long l_". */ # define M64_MEMBER l_ # endif #endif #if defined(USE_M64_CASTS) || defined(USE_CVT_INTRINSICS) || defined(USE_M64_DOUBLE) typedef uint64_t mmxdatafield; #else typedef __m64 mmxdatafield; #endif typedef struct { mmxdatafield mmx_4x00ff; mmxdatafield mmx_4x0080; mmxdatafield mmx_565_rgb; mmxdatafield mmx_565_unpack_multiplier; mmxdatafield mmx_565_pack_multiplier; mmxdatafield mmx_565_r; mmxdatafield mmx_565_g; mmxdatafield mmx_565_b; mmxdatafield mmx_packed_565_rb; mmxdatafield mmx_packed_565_g; mmxdatafield mmx_expand_565_g; mmxdatafield mmx_expand_565_b; mmxdatafield mmx_expand_565_r; #ifndef USE_LOONGSON_MMI mmxdatafield mmx_mask_0; mmxdatafield mmx_mask_1; mmxdatafield mmx_mask_2; mmxdatafield mmx_mask_3; #endif mmxdatafield mmx_full_alpha; mmxdatafield mmx_4x0101; mmxdatafield mmx_ff000000; } mmx_data_t; #if defined(_MSC_VER) # define MMXDATA_INIT(field, val) { val ## UI64 } #elif defined(M64_MEMBER) /* __m64 is a struct, not an integral type */ # define MMXDATA_INIT(field, val) field = { val ## ULL } #else /* mmxdatafield is an integral type */ # define MMXDATA_INIT(field, val) field = val ## ULL #endif static const mmx_data_t c = { MMXDATA_INIT (.mmx_4x00ff, 0x00ff00ff00ff00ff), MMXDATA_INIT (.mmx_4x0080, 0x0080008000800080), MMXDATA_INIT (.mmx_565_rgb, 0x000001f0003f001f), MMXDATA_INIT (.mmx_565_unpack_multiplier, 0x0000008404100840), MMXDATA_INIT (.mmx_565_pack_multiplier, 0x2000000420000004), MMXDATA_INIT (.mmx_565_r, 0x000000f800000000), MMXDATA_INIT (.mmx_565_g, 0x0000000000fc0000), MMXDATA_INIT (.mmx_565_b, 0x00000000000000f8), MMXDATA_INIT (.mmx_packed_565_rb, 0x00f800f800f800f8), MMXDATA_INIT (.mmx_packed_565_g, 0x0000fc000000fc00), MMXDATA_INIT (.mmx_expand_565_g, 0x07e007e007e007e0), MMXDATA_INIT (.mmx_expand_565_b, 0x001f001f001f001f), MMXDATA_INIT (.mmx_expand_565_r, 0xf800f800f800f800), #ifndef USE_LOONGSON_MMI MMXDATA_INIT (.mmx_mask_0, 0xffffffffffff0000), MMXDATA_INIT (.mmx_mask_1, 0xffffffff0000ffff), MMXDATA_INIT (.mmx_mask_2, 0xffff0000ffffffff), MMXDATA_INIT (.mmx_mask_3, 0x0000ffffffffffff), #endif MMXDATA_INIT (.mmx_full_alpha, 0x00ff000000000000), MMXDATA_INIT (.mmx_4x0101, 0x0101010101010101), MMXDATA_INIT (.mmx_ff000000, 0xff000000ff000000), }; #ifdef USE_CVT_INTRINSICS # define MC(x) to_m64 (c.mmx_ ## x) #elif defined(USE_M64_CASTS) # define MC(x) ((__m64)c.mmx_ ## x) #elif defined(USE_M64_DOUBLE) # define MC(x) (*(__m64 *)&c.mmx_ ## x) #else # define MC(x) c.mmx_ ## x #endif static force_inline __m64 to_m64 (uint64_t x) { #ifdef USE_CVT_INTRINSICS return _mm_cvtsi64_m64 (x); #elif defined M64_MEMBER /* __m64 is a struct, not an integral type */ __m64 res; res.M64_MEMBER = x; return res; #elif defined USE_M64_DOUBLE return *(__m64 *)&x; #else /* USE_M64_CASTS */ return (__m64)x; #endif } static force_inline uint64_t to_uint64 (__m64 x) { #ifdef USE_CVT_INTRINSICS return _mm_cvtm64_si64 (x); #elif defined M64_MEMBER /* __m64 is a struct, not an integral type */ uint64_t res = x.M64_MEMBER; return res; #elif defined USE_M64_DOUBLE return *(uint64_t *)&x; #else /* USE_M64_CASTS */ return (uint64_t)x; #endif } static force_inline __m64 shift (__m64 v, int s) { if (s > 0) return _mm_slli_si64 (v, s); else if (s < 0) return _mm_srli_si64 (v, -s); else return v; } static force_inline __m64 negate (__m64 mask) { return _mm_xor_si64 (mask, MC (4x00ff)); } static force_inline __m64 pix_multiply (__m64 a, __m64 b) { __m64 res; res = _mm_mullo_pi16 (a, b); res = _mm_adds_pu16 (res, MC (4x0080)); res = _mm_mulhi_pu16 (res, MC (4x0101)); return res; } static force_inline __m64 pix_add (__m64 a, __m64 b) { return _mm_adds_pu8 (a, b); } static force_inline __m64 expand_alpha (__m64 pixel) { return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (3, 3, 3, 3)); } static force_inline __m64 expand_alpha_rev (__m64 pixel) { return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (0, 0, 0, 0)); } static force_inline __m64 invert_colors (__m64 pixel) { return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (3, 0, 1, 2)); } static force_inline __m64 over (__m64 src, __m64 srca, __m64 dest) { return _mm_adds_pu8 (src, pix_multiply (dest, negate (srca))); } static force_inline __m64 over_rev_non_pre (__m64 src, __m64 dest) { __m64 srca = expand_alpha (src); __m64 srcfaaa = _mm_or_si64 (srca, MC (full_alpha)); return over (pix_multiply (invert_colors (src), srcfaaa), srca, dest); } static force_inline __m64 in (__m64 src, __m64 mask) { return pix_multiply (src, mask); } #ifndef _MSC_VER static force_inline __m64 in_over (__m64 src, __m64 srca, __m64 mask, __m64 dest) { return over (in (src, mask), pix_multiply (srca, mask), dest); } #else #define in_over(src, srca, mask, dest) \ over (in (src, mask), pix_multiply (srca, mask), dest) #endif /* Elemental unaligned loads */ static force_inline __m64 ldq_u(__m64 *p) { #ifdef USE_X86_MMX /* x86's alignment restrictions are very relaxed. */ return *(__m64 *)p; #elif defined USE_ARM_IWMMXT int align = (uintptr_t)p & 7; __m64 *aligned_p; if (align == 0) return *p; aligned_p = (__m64 *)((uintptr_t)p & ~7); return (__m64) _mm_align_si64 (aligned_p[0], aligned_p[1], align); #else struct __una_u64 { __m64 x __attribute__((packed)); }; const struct __una_u64 *ptr = (const struct __una_u64 *) p; return (__m64) ptr->x; #endif } static force_inline uint32_t ldl_u(const uint32_t *p) { #ifdef USE_X86_MMX /* x86's alignment restrictions are very relaxed. */ return *p; #else struct __una_u32 { uint32_t x __attribute__((packed)); }; const struct __una_u32 *ptr = (const struct __una_u32 *) p; return ptr->x; #endif } static force_inline __m64 load (const uint32_t *v) { #ifdef USE_LOONGSON_MMI __m64 ret; asm ("lwc1 %0, %1\n\t" : "=f" (ret) : "m" (*v) ); return ret; #else return _mm_cvtsi32_si64 (*v); #endif } static force_inline __m64 load8888 (const uint32_t *v) { #ifdef USE_LOONGSON_MMI return _mm_unpacklo_pi8_f (*(__m32 *)v, _mm_setzero_si64 ()); #else return _mm_unpacklo_pi8 (load (v), _mm_setzero_si64 ()); #endif } static force_inline __m64 load8888u (const uint32_t *v) { uint32_t l = ldl_u (v); return load8888 (&l); } static force_inline __m64 pack8888 (__m64 lo, __m64 hi) { return _mm_packs_pu16 (lo, hi); } static force_inline void store (uint32_t *dest, __m64 v) { #ifdef USE_LOONGSON_MMI asm ("swc1 %1, %0\n\t" : "=m" (*dest) : "f" (v) : "memory" ); #else *dest = _mm_cvtsi64_si32 (v); #endif } static force_inline void store8888 (uint32_t *dest, __m64 v) { v = pack8888 (v, _mm_setzero_si64 ()); store (dest, v); } static force_inline pixman_bool_t is_equal (__m64 a, __m64 b) { #ifdef USE_LOONGSON_MMI /* __m64 is double, we can compare directly. */ return a == b; #else return _mm_movemask_pi8 (_mm_cmpeq_pi8 (a, b)) == 0xff; #endif } static force_inline pixman_bool_t is_opaque (__m64 v) { #ifdef USE_LOONGSON_MMI return is_equal (_mm_and_si64 (v, MC (full_alpha)), MC (full_alpha)); #else __m64 ffs = _mm_cmpeq_pi8 (v, v); return (_mm_movemask_pi8 (_mm_cmpeq_pi8 (v, ffs)) & 0x40); #endif } static force_inline pixman_bool_t is_zero (__m64 v) { return is_equal (v, _mm_setzero_si64 ()); } /* Expand 16 bits positioned at @pos (0-3) of a mmx register into * * 00RR00GG00BB * * --- Expanding 565 in the low word --- * * m = (m << (32 - 3)) | (m << (16 - 5)) | m; * m = m & (01f0003f001f); * m = m * (008404100840); * m = m >> 8; * * Note the trick here - the top word is shifted by another nibble to * avoid it bumping into the middle word */ static force_inline __m64 expand565 (__m64 pixel, int pos) { __m64 p = pixel; __m64 t1, t2; /* move pixel to low 16 bit and zero the rest */ #ifdef USE_LOONGSON_MMI p = loongson_extract_pi16 (p, pos); #else p = shift (shift (p, (3 - pos) * 16), -48); #endif t1 = shift (p, 36 - 11); t2 = shift (p, 16 - 5); p = _mm_or_si64 (t1, p); p = _mm_or_si64 (t2, p); p = _mm_and_si64 (p, MC (565_rgb)); pixel = _mm_mullo_pi16 (p, MC (565_unpack_multiplier)); return _mm_srli_pi16 (pixel, 8); } /* Expand 4 16 bit pixels in an mmx register into two mmx registers of * * AARRGGBBRRGGBB */ static force_inline void expand_4xpacked565 (__m64 vin, __m64 *vout0, __m64 *vout1, int full_alpha) { __m64 t0, t1, alpha = _mm_setzero_si64 (); __m64 r = _mm_and_si64 (vin, MC (expand_565_r)); __m64 g = _mm_and_si64 (vin, MC (expand_565_g)); __m64 b = _mm_and_si64 (vin, MC (expand_565_b)); if (full_alpha) alpha = _mm_cmpeq_pi32 (alpha, alpha); /* Replicate high bits into empty low bits. */ r = _mm_or_si64 (_mm_srli_pi16 (r, 8), _mm_srli_pi16 (r, 13)); g = _mm_or_si64 (_mm_srli_pi16 (g, 3), _mm_srli_pi16 (g, 9)); b = _mm_or_si64 (_mm_slli_pi16 (b, 3), _mm_srli_pi16 (b, 2)); r = _mm_packs_pu16 (r, _mm_setzero_si64 ()); /* 00 00 00 00 R3 R2 R1 R0 */ g = _mm_packs_pu16 (g, _mm_setzero_si64 ()); /* 00 00 00 00 G3 G2 G1 G0 */ b = _mm_packs_pu16 (b, _mm_setzero_si64 ()); /* 00 00 00 00 B3 B2 B1 B0 */ t1 = _mm_unpacklo_pi8 (r, alpha); /* A3 R3 A2 R2 A1 R1 A0 R0 */ t0 = _mm_unpacklo_pi8 (b, g); /* G3 B3 G2 B2 G1 B1 G0 B0 */ *vout0 = _mm_unpacklo_pi16 (t0, t1); /* A1 R1 G1 B1 A0 R0 G0 B0 */ *vout1 = _mm_unpackhi_pi16 (t0, t1); /* A3 R3 G3 B3 A2 R2 G2 B2 */ } static force_inline __m64 expand8888 (__m64 in, int pos) { if (pos == 0) return _mm_unpacklo_pi8 (in, _mm_setzero_si64 ()); else return _mm_unpackhi_pi8 (in, _mm_setzero_si64 ()); } static force_inline __m64 expandx888 (__m64 in, int pos) { return _mm_or_si64 (expand8888 (in, pos), MC (full_alpha)); } static force_inline void expand_4x565 (__m64 vin, __m64 *vout0, __m64 *vout1, __m64 *vout2, __m64 *vout3, int full_alpha) { __m64 v0, v1; expand_4xpacked565 (vin, &v0, &v1, full_alpha); *vout0 = expand8888 (v0, 0); *vout1 = expand8888 (v0, 1); *vout2 = expand8888 (v1, 0); *vout3 = expand8888 (v1, 1); } static force_inline __m64 pack_565 (__m64 pixel, __m64 target, int pos) { __m64 p = pixel; __m64 t = target; __m64 r, g, b; r = _mm_and_si64 (p, MC (565_r)); g = _mm_and_si64 (p, MC (565_g)); b = _mm_and_si64 (p, MC (565_b)); #ifdef USE_LOONGSON_MMI r = shift (r, -(32 - 8)); g = shift (g, -(16 - 3)); b = shift (b, -(0 + 3)); p = _mm_or_si64 (r, g); p = _mm_or_si64 (p, b); return loongson_insert_pi16 (t, p, pos); #else r = shift (r, -(32 - 8) + pos * 16); g = shift (g, -(16 - 3) + pos * 16); b = shift (b, -(0 + 3) + pos * 16); if (pos == 0) t = _mm_and_si64 (t, MC (mask_0)); else if (pos == 1) t = _mm_and_si64 (t, MC (mask_1)); else if (pos == 2) t = _mm_and_si64 (t, MC (mask_2)); else if (pos == 3) t = _mm_and_si64 (t, MC (mask_3)); p = _mm_or_si64 (r, t); p = _mm_or_si64 (g, p); return _mm_or_si64 (b, p); #endif } static force_inline __m64 pack_4xpacked565 (__m64 a, __m64 b) { __m64 rb0 = _mm_and_si64 (a, MC (packed_565_rb)); __m64 rb1 = _mm_and_si64 (b, MC (packed_565_rb)); __m64 t0 = _mm_madd_pi16 (rb0, MC (565_pack_multiplier)); __m64 t1 = _mm_madd_pi16 (rb1, MC (565_pack_multiplier)); __m64 g0 = _mm_and_si64 (a, MC (packed_565_g)); __m64 g1 = _mm_and_si64 (b, MC (packed_565_g)); t0 = _mm_or_si64 (t0, g0); t1 = _mm_or_si64 (t1, g1); t0 = shift(t0, -5); #ifdef USE_ARM_IWMMXT t1 = shift(t1, -5); return _mm_packs_pu32 (t0, t1); #else t1 = shift(t1, -5 + 16); return _mm_shuffle_pi16 (_mm_or_si64 (t0, t1), _MM_SHUFFLE (3, 1, 2, 0)); #endif } #ifndef _MSC_VER static force_inline __m64 pack_4x565 (__m64 v0, __m64 v1, __m64 v2, __m64 v3) { return pack_4xpacked565 (pack8888 (v0, v1), pack8888 (v2, v3)); } static force_inline __m64 pix_add_mul (__m64 x, __m64 a, __m64 y, __m64 b) { x = pix_multiply (x, a); y = pix_multiply (y, b); return pix_add (x, y); } #else /* MSVC only handles a "pass by register" of up to three SSE intrinsics */ #define pack_4x565(v0, v1, v2, v3) \ pack_4xpacked565 (pack8888 (v0, v1), pack8888 (v2, v3)) #define pix_add_mul(x, a, y, b) \ ( x = pix_multiply (x, a), \ y = pix_multiply (y, b), \ pix_add (x, y) ) #endif /* --------------- MMX code patch for fbcompose.c --------------------- */ static force_inline __m64 combine (const uint32_t *src, const uint32_t *mask) { __m64 vsrc = load8888 (src); if (mask) { __m64 m = load8888 (mask); m = expand_alpha (m); vsrc = pix_multiply (vsrc, m); } return vsrc; } static force_inline __m64 core_combine_over_u_pixel_mmx (__m64 vsrc, __m64 vdst) { vsrc = _mm_unpacklo_pi8 (vsrc, _mm_setzero_si64 ()); if (is_opaque (vsrc)) { return vsrc; } else if (!is_zero (vsrc)) { return over (vsrc, expand_alpha (vsrc), _mm_unpacklo_pi8 (vdst, _mm_setzero_si64 ())); } return _mm_unpacklo_pi8 (vdst, _mm_setzero_si64 ()); } static void mmx_combine_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 vsrc = combine (src, mask); if (is_opaque (vsrc)) { store8888 (dest, vsrc); } else if (!is_zero (vsrc)) { __m64 sa = expand_alpha (vsrc); store8888 (dest, over (vsrc, sa, load8888 (dest))); } ++dest; ++src; if (mask) ++mask; } _mm_empty (); } static void mmx_combine_over_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 d, da; __m64 s = combine (src, mask); d = load8888 (dest); da = expand_alpha (d); store8888 (dest, over (d, da, s)); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 a; __m64 x = combine (src, mask); a = load8888 (dest); a = expand_alpha (a); x = pix_multiply (x, a); store8888 (dest, x); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 a = combine (src, mask); __m64 x; x = load8888 (dest); a = expand_alpha (a); x = pix_multiply (x, a); store8888 (dest, x); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 a; __m64 x = combine (src, mask); a = load8888 (dest); a = expand_alpha (a); a = negate (a); x = pix_multiply (x, a); store8888 (dest, x); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 a = combine (src, mask); __m64 x; x = load8888 (dest); a = expand_alpha (a); a = negate (a); x = pix_multiply (x, a); store8888 (dest, x); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 da, d, sia; __m64 s = combine (src, mask); d = load8888 (dest); sia = expand_alpha (s); sia = negate (sia); da = expand_alpha (d); s = pix_add_mul (s, da, d, sia); store8888 (dest, s); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end; end = dest + width; while (dest < end) { __m64 dia, d, sa; __m64 s = combine (src, mask); d = load8888 (dest); sa = expand_alpha (s); dia = expand_alpha (d); dia = negate (dia); s = pix_add_mul (s, dia, d, sa); store8888 (dest, s); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 dia, d, sia; __m64 s = combine (src, mask); d = load8888 (dest); sia = expand_alpha (s); dia = expand_alpha (d); sia = negate (sia); dia = negate (dia); s = pix_add_mul (s, dia, d, sia); store8888 (dest, s); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_add_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { __m64 d; __m64 s = combine (src, mask); d = load8888 (dest); s = pix_add (s, d); store8888 (dest, s); ++dest; ++src; if (mask) mask++; } _mm_empty (); } static void mmx_combine_saturate_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = dest + width; while (dest < end) { uint32_t s, sa, da; uint32_t d = *dest; __m64 ms = combine (src, mask); __m64 md = load8888 (dest); store8888(&s, ms); da = ~d >> 24; sa = s >> 24; if (sa > da) { uint32_t quot = DIV_UN8 (da, sa) << 24; __m64 msa = load8888 ("); msa = expand_alpha (msa); ms = pix_multiply (ms, msa); } md = pix_add (md, ms); store8888 (dest, md); ++src; ++dest; if (mask) mask++; } _mm_empty (); } static void mmx_combine_src_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); s = pix_multiply (s, a); store8888 (dest, s); ++src; ++mask; ++dest; } _mm_empty (); } static void mmx_combine_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 sa = expand_alpha (s); store8888 (dest, in_over (s, sa, a, d)); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_over_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); store8888 (dest, over (d, da, in (s, a))); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); s = pix_multiply (s, a); s = pix_multiply (s, da); store8888 (dest, s); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 sa = expand_alpha (s); a = pix_multiply (a, sa); d = pix_multiply (d, a); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); da = negate (da); s = pix_multiply (s, a); s = pix_multiply (s, da); store8888 (dest, s); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 sa = expand_alpha (s); a = pix_multiply (a, sa); a = negate (a); d = pix_multiply (d, a); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); __m64 sa = expand_alpha (s); s = pix_multiply (s, a); a = pix_multiply (a, sa); a = negate (a); d = pix_add_mul (d, a, s, da); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); __m64 sa = expand_alpha (s); s = pix_multiply (s, a); a = pix_multiply (a, sa); da = negate (da); d = pix_add_mul (d, a, s, da); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); __m64 da = expand_alpha (d); __m64 sa = expand_alpha (s); s = pix_multiply (s, a); a = pix_multiply (a, sa); da = negate (da); a = negate (a); d = pix_add_mul (d, a, s, da); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } static void mmx_combine_add_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { const uint32_t *end = src + width; while (src < end) { __m64 a = load8888 (mask); __m64 s = load8888 (src); __m64 d = load8888 (dest); s = pix_multiply (s, a); d = pix_add (s, d); store8888 (dest, d); ++src; ++dest; ++mask; } _mm_empty (); } /* ------------- MMX code paths called from fbpict.c -------------------- */ static void mmx_composite_over_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, *dst; int32_t w; int dst_stride; __m64 vsrc, vsrca; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { store8888 (dst, over (vsrc, vsrca, load8888 (dst))); w--; dst++; } while (w >= 2) { __m64 vdest; __m64 dest0, dest1; vdest = *(__m64 *)dst; dest0 = over (vsrc, vsrca, expand8888 (vdest, 0)); dest1 = over (vsrc, vsrca, expand8888 (vdest, 1)); *(__m64 *)dst = pack8888 (dest0, dest1); dst += 2; w -= 2; } CHECKPOINT (); if (w) { store8888 (dst, over (vsrc, vsrca, load8888 (dst))); } } _mm_empty (); } static void mmx_composite_over_n_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint16_t *dst_line, *dst; int32_t w; int dst_stride; __m64 vsrc, vsrca; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (over (vsrc, vsrca, vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; } while (w >= 4) { __m64 vdest = *(__m64 *)dst; __m64 v0, v1, v2, v3; expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); v0 = over (vsrc, vsrca, v0); v1 = over (vsrc, vsrca, v1); v2 = over (vsrc, vsrca, v2); v3 = over (vsrc, vsrca, v3); *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); dst += 4; w -= 4; } CHECKPOINT (); while (w) { uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (over (vsrc, vsrca, vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; } } _mm_empty (); } static void mmx_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line; uint32_t *mask_line; int dst_stride, mask_stride; __m64 vsrc, vsrca; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { int twidth = width; uint32_t *p = (uint32_t *)mask_line; uint32_t *q = (uint32_t *)dst_line; while (twidth && (uintptr_t)q & 7) { uint32_t m = *(uint32_t *)p; if (m) { __m64 vdest = load8888 (q); vdest = in_over (vsrc, vsrca, load8888 (&m), vdest); store8888 (q, vdest); } twidth--; p++; q++; } while (twidth >= 2) { uint32_t m0, m1; m0 = *p; m1 = *(p + 1); if (m0 | m1) { __m64 dest0, dest1; __m64 vdest = *(__m64 *)q; dest0 = in_over (vsrc, vsrca, load8888 (&m0), expand8888 (vdest, 0)); dest1 = in_over (vsrc, vsrca, load8888 (&m1), expand8888 (vdest, 1)); *(__m64 *)q = pack8888 (dest0, dest1); } p += 2; q += 2; twidth -= 2; } if (twidth) { uint32_t m = *(uint32_t *)p; if (m) { __m64 vdest = load8888 (q); vdest = in_over (vsrc, vsrca, load8888 (&m), vdest); store8888 (q, vdest); } twidth--; p++; q++; } dst_line += dst_stride; mask_line += mask_stride; } _mm_empty (); } static void mmx_composite_over_8888_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; uint32_t mask; __m64 vmask; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); vmask = expand_alpha (load8888 (&mask)); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { __m64 s = load8888 (src); __m64 d = load8888 (dst); store8888 (dst, in_over (s, expand_alpha (s), vmask, d)); w--; dst++; src++; } while (w >= 2) { __m64 vs = ldq_u ((__m64 *)src); __m64 vd = *(__m64 *)dst; __m64 vsrc0 = expand8888 (vs, 0); __m64 vsrc1 = expand8888 (vs, 1); *(__m64 *)dst = pack8888 ( in_over (vsrc0, expand_alpha (vsrc0), vmask, expand8888 (vd, 0)), in_over (vsrc1, expand_alpha (vsrc1), vmask, expand8888 (vd, 1))); w -= 2; dst += 2; src += 2; } if (w) { __m64 s = load8888 (src); __m64 d = load8888 (dst); store8888 (dst, in_over (s, expand_alpha (s), vmask, d)); } } _mm_empty (); } static void mmx_composite_over_x888_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; uint32_t mask; __m64 vmask; int dst_stride, src_stride; int32_t w; __m64 srca; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); vmask = expand_alpha (load8888 (&mask)); srca = MC (4x00ff); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { uint32_t ssrc = *src | 0xff000000; __m64 s = load8888 (&ssrc); __m64 d = load8888 (dst); store8888 (dst, in_over (s, srca, vmask, d)); w--; dst++; src++; } while (w >= 16) { __m64 vd0 = *(__m64 *)(dst + 0); __m64 vd1 = *(__m64 *)(dst + 2); __m64 vd2 = *(__m64 *)(dst + 4); __m64 vd3 = *(__m64 *)(dst + 6); __m64 vd4 = *(__m64 *)(dst + 8); __m64 vd5 = *(__m64 *)(dst + 10); __m64 vd6 = *(__m64 *)(dst + 12); __m64 vd7 = *(__m64 *)(dst + 14); __m64 vs0 = ldq_u ((__m64 *)(src + 0)); __m64 vs1 = ldq_u ((__m64 *)(src + 2)); __m64 vs2 = ldq_u ((__m64 *)(src + 4)); __m64 vs3 = ldq_u ((__m64 *)(src + 6)); __m64 vs4 = ldq_u ((__m64 *)(src + 8)); __m64 vs5 = ldq_u ((__m64 *)(src + 10)); __m64 vs6 = ldq_u ((__m64 *)(src + 12)); __m64 vs7 = ldq_u ((__m64 *)(src + 14)); vd0 = pack8888 ( in_over (expandx888 (vs0, 0), srca, vmask, expand8888 (vd0, 0)), in_over (expandx888 (vs0, 1), srca, vmask, expand8888 (vd0, 1))); vd1 = pack8888 ( in_over (expandx888 (vs1, 0), srca, vmask, expand8888 (vd1, 0)), in_over (expandx888 (vs1, 1), srca, vmask, expand8888 (vd1, 1))); vd2 = pack8888 ( in_over (expandx888 (vs2, 0), srca, vmask, expand8888 (vd2, 0)), in_over (expandx888 (vs2, 1), srca, vmask, expand8888 (vd2, 1))); vd3 = pack8888 ( in_over (expandx888 (vs3, 0), srca, vmask, expand8888 (vd3, 0)), in_over (expandx888 (vs3, 1), srca, vmask, expand8888 (vd3, 1))); vd4 = pack8888 ( in_over (expandx888 (vs4, 0), srca, vmask, expand8888 (vd4, 0)), in_over (expandx888 (vs4, 1), srca, vmask, expand8888 (vd4, 1))); vd5 = pack8888 ( in_over (expandx888 (vs5, 0), srca, vmask, expand8888 (vd5, 0)), in_over (expandx888 (vs5, 1), srca, vmask, expand8888 (vd5, 1))); vd6 = pack8888 ( in_over (expandx888 (vs6, 0), srca, vmask, expand8888 (vd6, 0)), in_over (expandx888 (vs6, 1), srca, vmask, expand8888 (vd6, 1))); vd7 = pack8888 ( in_over (expandx888 (vs7, 0), srca, vmask, expand8888 (vd7, 0)), in_over (expandx888 (vs7, 1), srca, vmask, expand8888 (vd7, 1))); *(__m64 *)(dst + 0) = vd0; *(__m64 *)(dst + 2) = vd1; *(__m64 *)(dst + 4) = vd2; *(__m64 *)(dst + 6) = vd3; *(__m64 *)(dst + 8) = vd4; *(__m64 *)(dst + 10) = vd5; *(__m64 *)(dst + 12) = vd6; *(__m64 *)(dst + 14) = vd7; w -= 16; dst += 16; src += 16; } while (w) { uint32_t ssrc = *src | 0xff000000; __m64 s = load8888 (&ssrc); __m64 d = load8888 (dst); store8888 (dst, in_over (s, srca, vmask, d)); w--; dst++; src++; } } _mm_empty (); } static void mmx_composite_over_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; uint32_t s; int dst_stride, src_stride; uint8_t a; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w--) { s = *src++; a = s >> 24; if (a == 0xff) { *dst = s; } else if (s) { __m64 ms, sa; ms = load8888 (&s); sa = expand_alpha (ms); store8888 (dst, over (ms, sa, load8888 (dst))); } dst++; } } _mm_empty (); } static void mmx_composite_over_8888_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); #if 0 /* FIXME */ assert (src_image->drawable == mask_image->drawable); #endif while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { __m64 vsrc = load8888 (src); uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 ( over (vsrc, expand_alpha (vsrc), vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; src++; } CHECKPOINT (); while (w >= 4) { __m64 vdest = *(__m64 *)dst; __m64 v0, v1, v2, v3; __m64 vsrc0, vsrc1, vsrc2, vsrc3; expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); vsrc0 = load8888 ((src + 0)); vsrc1 = load8888 ((src + 1)); vsrc2 = load8888 ((src + 2)); vsrc3 = load8888 ((src + 3)); v0 = over (vsrc0, expand_alpha (vsrc0), v0); v1 = over (vsrc1, expand_alpha (vsrc1), v1); v2 = over (vsrc2, expand_alpha (vsrc2), v2); v3 = over (vsrc3, expand_alpha (vsrc3), v3); *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); w -= 4; dst += 4; src += 4; } CHECKPOINT (); while (w) { __m64 vsrc = load8888 (src); uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (over (vsrc, expand_alpha (vsrc), vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; src++; } } _mm_empty (); } static void mmx_composite_over_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; __m64 vsrc, vsrca; uint64_t srcsrc; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; srcsrc = (uint64_t)src << 32 | src; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { uint64_t m = *mask; if (m) { __m64 vdest = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m)), load8888 (dst)); store8888 (dst, vdest); } w--; mask++; dst++; } CHECKPOINT (); while (w >= 2) { uint64_t m0, m1; m0 = *mask; m1 = *(mask + 1); if (srca == 0xff && (m0 & m1) == 0xff) { *(uint64_t *)dst = srcsrc; } else if (m0 | m1) { __m64 vdest; __m64 dest0, dest1; vdest = *(__m64 *)dst; dest0 = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m0)), expand8888 (vdest, 0)); dest1 = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m1)), expand8888 (vdest, 1)); *(__m64 *)dst = pack8888 (dest0, dest1); } mask += 2; dst += 2; w -= 2; } CHECKPOINT (); if (w) { uint64_t m = *mask; if (m) { __m64 vdest = load8888 (dst); vdest = in_over ( vsrc, vsrca, expand_alpha_rev (to_m64 (m)), vdest); store8888 (dst, vdest); } } } _mm_empty (); } static pixman_bool_t mmx_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler) { uint64_t fill; __m64 vfill; uint32_t byte_width; uint8_t *byte_line; #if defined __GNUC__ && defined USE_X86_MMX __m64 v1, v2, v3, v4, v5, v6, v7; #endif if (bpp != 16 && bpp != 32 && bpp != 8) return FALSE; if (bpp == 8) { stride = stride * (int) sizeof (uint32_t) / 1; byte_line = (uint8_t *)(((uint8_t *)bits) + stride * y + x); byte_width = width; stride *= 1; filler = (filler & 0xff) * 0x01010101; } else if (bpp == 16) { stride = stride * (int) sizeof (uint32_t) / 2; byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); byte_width = 2 * width; stride *= 2; filler = (filler & 0xffff) * 0x00010001; } else { stride = stride * (int) sizeof (uint32_t) / 4; byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); byte_width = 4 * width; stride *= 4; } fill = ((uint64_t)filler << 32) | filler; vfill = to_m64 (fill); #if defined __GNUC__ && defined USE_X86_MMX __asm__ ( "movq %7, %0\n" "movq %7, %1\n" "movq %7, %2\n" "movq %7, %3\n" "movq %7, %4\n" "movq %7, %5\n" "movq %7, %6\n" : "=&y" (v1), "=&y" (v2), "=&y" (v3), "=&y" (v4), "=&y" (v5), "=&y" (v6), "=y" (v7) : "y" (vfill)); #endif while (height--) { int w; uint8_t *d = byte_line; byte_line += stride; w = byte_width; if (w >= 1 && ((uintptr_t)d & 1)) { *(uint8_t *)d = (filler & 0xff); w--; d++; } if (w >= 2 && ((uintptr_t)d & 3)) { *(uint16_t *)d = filler; w -= 2; d += 2; } while (w >= 4 && ((uintptr_t)d & 7)) { *(uint32_t *)d = filler; w -= 4; d += 4; } while (w >= 64) { #if defined __GNUC__ && defined USE_X86_MMX __asm__ ( "movq %1, (%0)\n" "movq %2, 8(%0)\n" "movq %3, 16(%0)\n" "movq %4, 24(%0)\n" "movq %5, 32(%0)\n" "movq %6, 40(%0)\n" "movq %7, 48(%0)\n" "movq %8, 56(%0)\n" : : "r" (d), "y" (vfill), "y" (v1), "y" (v2), "y" (v3), "y" (v4), "y" (v5), "y" (v6), "y" (v7) : "memory"); #else *(__m64*) (d + 0) = vfill; *(__m64*) (d + 8) = vfill; *(__m64*) (d + 16) = vfill; *(__m64*) (d + 24) = vfill; *(__m64*) (d + 32) = vfill; *(__m64*) (d + 40) = vfill; *(__m64*) (d + 48) = vfill; *(__m64*) (d + 56) = vfill; #endif w -= 64; d += 64; } while (w >= 4) { *(uint32_t *)d = filler; w -= 4; d += 4; } if (w >= 2) { *(uint16_t *)d = filler; w -= 2; d += 2; } if (w >= 1) { *(uint8_t *)d = (filler & 0xff); w--; d++; } } _mm_empty (); return TRUE; } static void mmx_composite_src_x888_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t *src_line, *src, s; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { s = *src++; *dst = convert_8888_to_0565 (s); dst++; w--; } while (w >= 4) { __m64 vdest; __m64 vsrc0 = ldq_u ((__m64 *)(src + 0)); __m64 vsrc1 = ldq_u ((__m64 *)(src + 2)); vdest = pack_4xpacked565 (vsrc0, vsrc1); *(__m64 *)dst = vdest; w -= 4; src += 4; dst += 4; } while (w) { s = *src++; *dst = convert_8888_to_0565 (s); dst++; w--; } } _mm_empty (); } static void mmx_composite_src_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; __m64 vsrc; uint64_t srcsrc; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) { mmx_fill (imp, dest_image->bits.bits, dest_image->bits.rowstride, PIXMAN_FORMAT_BPP (dest_image->bits.format), dest_x, dest_y, width, height, 0); return; } srcsrc = (uint64_t)src << 32 | src; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); vsrc = load8888 (&src); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { uint64_t m = *mask; if (m) { __m64 vdest = in (vsrc, expand_alpha_rev (to_m64 (m))); store8888 (dst, vdest); } else { *dst = 0; } w--; mask++; dst++; } CHECKPOINT (); while (w >= 2) { uint64_t m0, m1; m0 = *mask; m1 = *(mask + 1); if (srca == 0xff && (m0 & m1) == 0xff) { *(uint64_t *)dst = srcsrc; } else if (m0 | m1) { __m64 dest0, dest1; dest0 = in (vsrc, expand_alpha_rev (to_m64 (m0))); dest1 = in (vsrc, expand_alpha_rev (to_m64 (m1))); *(__m64 *)dst = pack8888 (dest0, dest1); } else { *(uint64_t *)dst = 0; } mask += 2; dst += 2; w -= 2; } CHECKPOINT (); if (w) { uint64_t m = *mask; if (m) { __m64 vdest = load8888 (dst); vdest = in (vsrc, expand_alpha_rev (to_m64 (m))); store8888 (dst, vdest); } else { *dst = 0; } } } _mm_empty (); } static void mmx_composite_over_n_8_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint16_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; __m64 vsrc, vsrca, tmp; __m64 srcsrcsrcsrc; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); tmp = pack_565 (vsrc, _mm_setzero_si64 (), 0); srcsrcsrcsrc = expand_alpha_rev (tmp); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { uint64_t m = *mask; if (m) { uint64_t d = *dst; __m64 vd = to_m64 (d); __m64 vdest = in_over ( vsrc, vsrca, expand_alpha_rev (to_m64 (m)), expand565 (vd, 0)); vd = pack_565 (vdest, _mm_setzero_si64 (), 0); *dst = to_uint64 (vd); } w--; mask++; dst++; } CHECKPOINT (); while (w >= 4) { uint64_t m0, m1, m2, m3; m0 = *mask; m1 = *(mask + 1); m2 = *(mask + 2); m3 = *(mask + 3); if (srca == 0xff && (m0 & m1 & m2 & m3) == 0xff) { *(__m64 *)dst = srcsrcsrcsrc; } else if (m0 | m1 | m2 | m3) { __m64 vdest = *(__m64 *)dst; __m64 v0, v1, v2, v3; __m64 vm0, vm1, vm2, vm3; expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); vm0 = to_m64 (m0); v0 = in_over (vsrc, vsrca, expand_alpha_rev (vm0), v0); vm1 = to_m64 (m1); v1 = in_over (vsrc, vsrca, expand_alpha_rev (vm1), v1); vm2 = to_m64 (m2); v2 = in_over (vsrc, vsrca, expand_alpha_rev (vm2), v2); vm3 = to_m64 (m3); v3 = in_over (vsrc, vsrca, expand_alpha_rev (vm3), v3); *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3);; } w -= 4; mask += 4; dst += 4; } CHECKPOINT (); while (w) { uint64_t m = *mask; if (m) { uint64_t d = *dst; __m64 vd = to_m64 (d); __m64 vdest = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m)), expand565 (vd, 0)); vd = pack_565 (vdest, _mm_setzero_si64 (), 0); *dst = to_uint64 (vd); } w--; mask++; dst++; } } _mm_empty (); } static void mmx_composite_over_pixbuf_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); #if 0 /* FIXME */ assert (src_image->drawable == mask_image->drawable); #endif while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { __m64 vsrc = load8888 (src); uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (over_rev_non_pre (vsrc, vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; src++; } CHECKPOINT (); while (w >= 4) { uint32_t s0, s1, s2, s3; unsigned char a0, a1, a2, a3; s0 = *src; s1 = *(src + 1); s2 = *(src + 2); s3 = *(src + 3); a0 = (s0 >> 24); a1 = (s1 >> 24); a2 = (s2 >> 24); a3 = (s3 >> 24); if ((a0 & a1 & a2 & a3) == 0xFF) { __m64 v0 = invert_colors (load8888 (&s0)); __m64 v1 = invert_colors (load8888 (&s1)); __m64 v2 = invert_colors (load8888 (&s2)); __m64 v3 = invert_colors (load8888 (&s3)); *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); } else if (s0 | s1 | s2 | s3) { __m64 vdest = *(__m64 *)dst; __m64 v0, v1, v2, v3; __m64 vsrc0 = load8888 (&s0); __m64 vsrc1 = load8888 (&s1); __m64 vsrc2 = load8888 (&s2); __m64 vsrc3 = load8888 (&s3); expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); v0 = over_rev_non_pre (vsrc0, v0); v1 = over_rev_non_pre (vsrc1, v1); v2 = over_rev_non_pre (vsrc2, v2); v3 = over_rev_non_pre (vsrc3, v3); *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); } w -= 4; dst += 4; src += 4; } CHECKPOINT (); while (w) { __m64 vsrc = load8888 (src); uint64_t d = *dst; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (over_rev_non_pre (vsrc, vdest), vdest, 0); *dst = to_uint64 (vdest); w--; dst++; src++; } } _mm_empty (); } static void mmx_composite_over_pixbuf_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); #if 0 /* FIXME */ assert (src_image->drawable == mask_image->drawable); #endif while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { __m64 s = load8888 (src); __m64 d = load8888 (dst); store8888 (dst, over_rev_non_pre (s, d)); w--; dst++; src++; } while (w >= 2) { uint32_t s0, s1; unsigned char a0, a1; __m64 d0, d1; s0 = *src; s1 = *(src + 1); a0 = (s0 >> 24); a1 = (s1 >> 24); if ((a0 & a1) == 0xFF) { d0 = invert_colors (load8888 (&s0)); d1 = invert_colors (load8888 (&s1)); *(__m64 *)dst = pack8888 (d0, d1); } else if (s0 | s1) { __m64 vdest = *(__m64 *)dst; d0 = over_rev_non_pre (load8888 (&s0), expand8888 (vdest, 0)); d1 = over_rev_non_pre (load8888 (&s1), expand8888 (vdest, 1)); *(__m64 *)dst = pack8888 (d0, d1); } w -= 2; dst += 2; src += 2; } if (w) { __m64 s = load8888 (src); __m64 d = load8888 (dst); store8888 (dst, over_rev_non_pre (s, d)); } } _mm_empty (); } static void mmx_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint16_t *dst_line; uint32_t *mask_line; int dst_stride, mask_stride; __m64 vsrc, vsrca; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { int twidth = width; uint32_t *p = (uint32_t *)mask_line; uint16_t *q = (uint16_t *)dst_line; while (twidth && ((uintptr_t)q & 7)) { uint32_t m = *(uint32_t *)p; if (m) { uint64_t d = *q; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (in_over (vsrc, vsrca, load8888 (&m), vdest), vdest, 0); *q = to_uint64 (vdest); } twidth--; p++; q++; } while (twidth >= 4) { uint32_t m0, m1, m2, m3; m0 = *p; m1 = *(p + 1); m2 = *(p + 2); m3 = *(p + 3); if ((m0 | m1 | m2 | m3)) { __m64 vdest = *(__m64 *)q; __m64 v0, v1, v2, v3; expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); v0 = in_over (vsrc, vsrca, load8888 (&m0), v0); v1 = in_over (vsrc, vsrca, load8888 (&m1), v1); v2 = in_over (vsrc, vsrca, load8888 (&m2), v2); v3 = in_over (vsrc, vsrca, load8888 (&m3), v3); *(__m64 *)q = pack_4x565 (v0, v1, v2, v3); } twidth -= 4; p += 4; q += 4; } while (twidth) { uint32_t m; m = *(uint32_t *)p; if (m) { uint64_t d = *q; __m64 vdest = expand565 (to_m64 (d), 0); vdest = pack_565 (in_over (vsrc, vsrca, load8888 (&m), vdest), vdest, 0); *q = to_uint64 (vdest); } twidth--; p++; q++; } mask_line += mask_stride; dst_line += dst_stride; } _mm_empty (); } static void mmx_composite_in_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t src; uint8_t sa; __m64 vsrc, vsrca; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); sa = src >> 24; vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 7) { uint16_t tmp; uint8_t a; uint32_t m, d; a = *mask++; d = *dst; m = MUL_UN8 (sa, a, tmp); d = MUL_UN8 (m, d, tmp); *dst++ = d; w--; } while (w >= 4) { __m64 vmask; __m64 vdest; vmask = load8888u ((uint32_t *)mask); vdest = load8888 ((uint32_t *)dst); store8888 ((uint32_t *)dst, in (in (vsrca, vmask), vdest)); dst += 4; mask += 4; w -= 4; } while (w--) { uint16_t tmp; uint8_t a; uint32_t m, d; a = *mask++; d = *dst; m = MUL_UN8 (sa, a, tmp); d = MUL_UN8 (m, d, tmp); *dst++ = d; } } _mm_empty (); } static void mmx_composite_in_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int src_stride, dst_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 3) { uint8_t s, d; uint16_t tmp; s = *src; d = *dst; *dst = MUL_UN8 (s, d, tmp); src++; dst++; w--; } while (w >= 4) { uint32_t *s = (uint32_t *)src; uint32_t *d = (uint32_t *)dst; store8888 (d, in (load8888u (s), load8888 (d))); w -= 4; dst += 4; src += 4; } while (w--) { uint8_t s, d; uint16_t tmp; s = *src; d = *dst; *dst = MUL_UN8 (s, d, tmp); src++; dst++; } } _mm_empty (); } static void mmx_composite_add_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t src; uint8_t sa; __m64 vsrc, vsrca; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); sa = src >> 24; if (src == 0) return; vsrc = load8888 (&src); vsrca = expand_alpha (vsrc); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 3) { uint16_t tmp; uint16_t a; uint32_t m, d; uint32_t r; a = *mask++; d = *dst; m = MUL_UN8 (sa, a, tmp); r = ADD_UN8 (m, d, tmp); *dst++ = r; w--; } while (w >= 4) { __m64 vmask; __m64 vdest; vmask = load8888u ((uint32_t *)mask); vdest = load8888 ((uint32_t *)dst); store8888 ((uint32_t *)dst, _mm_adds_pu8 (in (vsrca, vmask), vdest)); dst += 4; mask += 4; w -= 4; } while (w--) { uint16_t tmp; uint16_t a; uint32_t m, d; uint32_t r; a = *mask++; d = *dst; m = MUL_UN8 (sa, a, tmp); r = ADD_UN8 (m, d, tmp); *dst++ = r; } } _mm_empty (); } static void mmx_composite_add_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int dst_stride, src_stride; int32_t w; uint8_t s, d; uint16_t t; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { s = *src; d = *dst; t = d + s; s = t | (0 - (t >> 8)); *dst = s; dst++; src++; w--; } while (w >= 8) { *(__m64*)dst = _mm_adds_pu8 (ldq_u ((__m64 *)src), *(__m64*)dst); dst += 8; src += 8; w -= 8; } while (w) { s = *src; d = *dst; t = d + s; s = t | (0 - (t >> 8)); *dst = s; dst++; src++; w--; } } _mm_empty (); } static void mmx_composite_add_0565_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t d; uint16_t *src_line, *src; uint32_t s; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint16_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { s = *src++; if (s) { d = *dst; s = convert_0565_to_8888 (s); if (d) { d = convert_0565_to_8888 (d); UN8x4_ADD_UN8x4 (s, d); } *dst = convert_8888_to_0565 (s); } dst++; w--; } while (w >= 4) { __m64 vdest = *(__m64 *)dst; __m64 vsrc = ldq_u ((__m64 *)src); __m64 vd0, vd1; __m64 vs0, vs1; expand_4xpacked565 (vdest, &vd0, &vd1, 0); expand_4xpacked565 (vsrc, &vs0, &vs1, 0); vd0 = _mm_adds_pu8 (vd0, vs0); vd1 = _mm_adds_pu8 (vd1, vs1); *(__m64 *)dst = pack_4xpacked565 (vd0, vd1); dst += 4; src += 4; w -= 4; } while (w--) { s = *src++; if (s) { d = *dst; s = convert_0565_to_8888 (s); if (d) { d = convert_0565_to_8888 (d); UN8x4_ADD_UN8x4 (s, d); } *dst = convert_8888_to_0565 (s); } dst++; } } _mm_empty (); } static void mmx_composite_add_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; int32_t w; CHECKPOINT (); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 7) { store (dst, _mm_adds_pu8 (load ((const uint32_t *)src), load ((const uint32_t *)dst))); dst++; src++; w--; } while (w >= 2) { *(__m64 *)dst = _mm_adds_pu8 (ldq_u ((__m64 *)src), *(__m64*)dst); dst += 2; src += 2; w -= 2; } if (w) { store (dst, _mm_adds_pu8 (load ((const uint32_t *)src), load ((const uint32_t *)dst))); } } _mm_empty (); } static pixman_bool_t mmx_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { uint8_t * src_bytes; uint8_t * dst_bytes; int byte_width; if (src_bpp != dst_bpp) return FALSE; if (src_bpp == 16) { src_stride = src_stride * (int) sizeof (uint32_t) / 2; dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; src_bytes = (uint8_t *)(((uint16_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = 2 * width; src_stride *= 2; dst_stride *= 2; } else if (src_bpp == 32) { src_stride = src_stride * (int) sizeof (uint32_t) / 4; dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; src_bytes = (uint8_t *)(((uint32_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = 4 * width; src_stride *= 4; dst_stride *= 4; } else { return FALSE; } while (height--) { int w; uint8_t *s = src_bytes; uint8_t *d = dst_bytes; src_bytes += src_stride; dst_bytes += dst_stride; w = byte_width; if (w >= 1 && ((uintptr_t)d & 1)) { *(uint8_t *)d = *(uint8_t *)s; w -= 1; s += 1; d += 1; } if (w >= 2 && ((uintptr_t)d & 3)) { *(uint16_t *)d = *(uint16_t *)s; w -= 2; s += 2; d += 2; } while (w >= 4 && ((uintptr_t)d & 7)) { *(uint32_t *)d = ldl_u ((uint32_t *)s); w -= 4; s += 4; d += 4; } while (w >= 64) { #if (defined (__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))) && defined USE_X86_MMX __asm__ ( "movq (%1), %%mm0\n" "movq 8(%1), %%mm1\n" "movq 16(%1), %%mm2\n" "movq 24(%1), %%mm3\n" "movq 32(%1), %%mm4\n" "movq 40(%1), %%mm5\n" "movq 48(%1), %%mm6\n" "movq 56(%1), %%mm7\n" "movq %%mm0, (%0)\n" "movq %%mm1, 8(%0)\n" "movq %%mm2, 16(%0)\n" "movq %%mm3, 24(%0)\n" "movq %%mm4, 32(%0)\n" "movq %%mm5, 40(%0)\n" "movq %%mm6, 48(%0)\n" "movq %%mm7, 56(%0)\n" : : "r" (d), "r" (s) : "memory", "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7"); #else __m64 v0 = ldq_u ((__m64 *)(s + 0)); __m64 v1 = ldq_u ((__m64 *)(s + 8)); __m64 v2 = ldq_u ((__m64 *)(s + 16)); __m64 v3 = ldq_u ((__m64 *)(s + 24)); __m64 v4 = ldq_u ((__m64 *)(s + 32)); __m64 v5 = ldq_u ((__m64 *)(s + 40)); __m64 v6 = ldq_u ((__m64 *)(s + 48)); __m64 v7 = ldq_u ((__m64 *)(s + 56)); *(__m64 *)(d + 0) = v0; *(__m64 *)(d + 8) = v1; *(__m64 *)(d + 16) = v2; *(__m64 *)(d + 24) = v3; *(__m64 *)(d + 32) = v4; *(__m64 *)(d + 40) = v5; *(__m64 *)(d + 48) = v6; *(__m64 *)(d + 56) = v7; #endif w -= 64; s += 64; d += 64; } while (w >= 4) { *(uint32_t *)d = ldl_u ((uint32_t *)s); w -= 4; s += 4; d += 4; } if (w >= 2) { *(uint16_t *)d = *(uint16_t *)s; w -= 2; s += 2; d += 2; } } _mm_empty (); return TRUE; } static void mmx_composite_copy_area (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); mmx_blt (imp, src_image->bits.bits, dest_image->bits.bits, src_image->bits.rowstride, dest_image->bits.rowstride, PIXMAN_FORMAT_BPP (src_image->bits.format), PIXMAN_FORMAT_BPP (dest_image->bits.format), src_x, src_y, dest_x, dest_y, width, height); } static void mmx_composite_over_x888_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *src, *src_line; uint32_t *dst, *dst_line; uint8_t *mask, *mask_line; int src_stride, mask_stride, dst_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { src = src_line; src_line += src_stride; dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w--) { uint64_t m = *mask; if (m) { uint32_t ssrc = *src | 0xff000000; __m64 s = load8888 (&ssrc); if (m == 0xff) { store8888 (dst, s); } else { __m64 sa = expand_alpha (s); __m64 vm = expand_alpha_rev (to_m64 (m)); __m64 vdest = in_over (s, sa, vm, load8888 (dst)); store8888 (dst, vdest); } } mask++; dst++; src++; } } _mm_empty (); } static void mmx_composite_over_reverse_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, *dst; int32_t w; int dst_stride; __m64 vsrc; CHECKPOINT (); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); vsrc = load8888 (&src); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; CHECKPOINT (); while (w && (uintptr_t)dst & 7) { __m64 vdest = load8888 (dst); store8888 (dst, over (vdest, expand_alpha (vdest), vsrc)); w--; dst++; } while (w >= 2) { __m64 vdest = *(__m64 *)dst; __m64 dest0 = expand8888 (vdest, 0); __m64 dest1 = expand8888 (vdest, 1); dest0 = over (dest0, expand_alpha (dest0), vsrc); dest1 = over (dest1, expand_alpha (dest1), vsrc); *(__m64 *)dst = pack8888 (dest0, dest1); dst += 2; w -= 2; } CHECKPOINT (); if (w) { __m64 vdest = load8888 (dst); store8888 (dst, over (vdest, expand_alpha (vdest), vsrc)); } } _mm_empty (); } #define BSHIFT ((1 << BILINEAR_INTERPOLATION_BITS)) #define BMSK (BSHIFT - 1) #define BILINEAR_DECLARE_VARIABLES \ const __m64 mm_wt = _mm_set_pi16 (wt, wt, wt, wt); \ const __m64 mm_wb = _mm_set_pi16 (wb, wb, wb, wb); \ const __m64 mm_BSHIFT = _mm_set_pi16 (BSHIFT, BSHIFT, BSHIFT, BSHIFT); \ const __m64 mm_addc7 = _mm_set_pi16 (0, 1, 0, 1); \ const __m64 mm_xorc7 = _mm_set_pi16 (0, BMSK, 0, BMSK); \ const __m64 mm_ux = _mm_set_pi16 (unit_x, unit_x, unit_x, unit_x); \ const __m64 mm_zero = _mm_setzero_si64 (); \ __m64 mm_x = _mm_set_pi16 (vx, vx, vx, vx) #define BILINEAR_INTERPOLATE_ONE_PIXEL(pix) \ do { \ /* fetch 2x2 pixel block into 2 mmx registers */ \ __m64 t = ldq_u ((__m64 *)&src_top [pixman_fixed_to_int (vx)]); \ __m64 b = ldq_u ((__m64 *)&src_bottom [pixman_fixed_to_int (vx)]); \ /* vertical interpolation */ \ __m64 t_hi = _mm_mullo_pi16 (_mm_unpackhi_pi8 (t, mm_zero), mm_wt); \ __m64 t_lo = _mm_mullo_pi16 (_mm_unpacklo_pi8 (t, mm_zero), mm_wt); \ __m64 b_hi = _mm_mullo_pi16 (_mm_unpackhi_pi8 (b, mm_zero), mm_wb); \ __m64 b_lo = _mm_mullo_pi16 (_mm_unpacklo_pi8 (b, mm_zero), mm_wb); \ __m64 hi = _mm_add_pi16 (t_hi, b_hi); \ __m64 lo = _mm_add_pi16 (t_lo, b_lo); \ vx += unit_x; \ if (BILINEAR_INTERPOLATION_BITS < 8) \ { \ /* calculate horizontal weights */ \ __m64 mm_wh = _mm_add_pi16 (mm_addc7, _mm_xor_si64 (mm_xorc7, \ _mm_srli_pi16 (mm_x, \ 16 - BILINEAR_INTERPOLATION_BITS))); \ /* horizontal interpolation */ \ __m64 p = _mm_unpacklo_pi16 (lo, hi); \ __m64 q = _mm_unpackhi_pi16 (lo, hi); \ lo = _mm_madd_pi16 (p, mm_wh); \ hi = _mm_madd_pi16 (q, mm_wh); \ } \ else \ { \ /* calculate horizontal weights */ \ __m64 mm_wh_lo = _mm_sub_pi16 (mm_BSHIFT, _mm_srli_pi16 (mm_x, \ 16 - BILINEAR_INTERPOLATION_BITS)); \ __m64 mm_wh_hi = _mm_srli_pi16 (mm_x, \ 16 - BILINEAR_INTERPOLATION_BITS); \ /* horizontal interpolation */ \ __m64 mm_lo_lo = _mm_mullo_pi16 (lo, mm_wh_lo); \ __m64 mm_lo_hi = _mm_mullo_pi16 (hi, mm_wh_hi); \ __m64 mm_hi_lo = _mm_mulhi_pu16 (lo, mm_wh_lo); \ __m64 mm_hi_hi = _mm_mulhi_pu16 (hi, mm_wh_hi); \ lo = _mm_add_pi32 (_mm_unpacklo_pi16 (mm_lo_lo, mm_hi_lo), \ _mm_unpacklo_pi16 (mm_lo_hi, mm_hi_hi)); \ hi = _mm_add_pi32 (_mm_unpackhi_pi16 (mm_lo_lo, mm_hi_lo), \ _mm_unpackhi_pi16 (mm_lo_hi, mm_hi_hi)); \ } \ mm_x = _mm_add_pi16 (mm_x, mm_ux); \ /* shift and pack the result */ \ hi = _mm_srli_pi32 (hi, BILINEAR_INTERPOLATION_BITS * 2); \ lo = _mm_srli_pi32 (lo, BILINEAR_INTERPOLATION_BITS * 2); \ lo = _mm_packs_pi32 (lo, hi); \ lo = _mm_packs_pu16 (lo, lo); \ pix = lo; \ } while (0) #define BILINEAR_SKIP_ONE_PIXEL() \ do { \ vx += unit_x; \ mm_x = _mm_add_pi16 (mm_x, mm_ux); \ } while(0) static force_inline void scaled_bilinear_scanline_mmx_8888_8888_SRC (uint32_t * dst, const uint32_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; __m64 pix; while (w--) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix); store (dst, pix); dst++; } _mm_empty (); } FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_cover_SRC, scaled_bilinear_scanline_mmx_8888_8888_SRC, uint32_t, uint32_t, uint32_t, COVER, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_pad_SRC, scaled_bilinear_scanline_mmx_8888_8888_SRC, uint32_t, uint32_t, uint32_t, PAD, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_none_SRC, scaled_bilinear_scanline_mmx_8888_8888_SRC, uint32_t, uint32_t, uint32_t, NONE, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_normal_SRC, scaled_bilinear_scanline_mmx_8888_8888_SRC, uint32_t, uint32_t, uint32_t, NORMAL, FLAG_NONE) static force_inline void scaled_bilinear_scanline_mmx_8888_8888_OVER (uint32_t * dst, const uint32_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; __m64 pix1, pix2; while (w) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (!is_zero (pix1)) { pix2 = load (dst); store8888 (dst, core_combine_over_u_pixel_mmx (pix1, pix2)); } w--; dst++; } _mm_empty (); } FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_cover_OVER, scaled_bilinear_scanline_mmx_8888_8888_OVER, uint32_t, uint32_t, uint32_t, COVER, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_pad_OVER, scaled_bilinear_scanline_mmx_8888_8888_OVER, uint32_t, uint32_t, uint32_t, PAD, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_none_OVER, scaled_bilinear_scanline_mmx_8888_8888_OVER, uint32_t, uint32_t, uint32_t, NONE, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_normal_OVER, scaled_bilinear_scanline_mmx_8888_8888_OVER, uint32_t, uint32_t, uint32_t, NORMAL, FLAG_NONE) static force_inline void scaled_bilinear_scanline_mmx_8888_8_8888_OVER (uint32_t * dst, const uint8_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; __m64 pix1, pix2; uint32_t m; while (w) { m = (uint32_t) *mask++; if (m) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (m == 0xff && is_opaque (pix1)) { store (dst, pix1); } else { __m64 ms, md, ma, msa; pix2 = load (dst); ma = expand_alpha_rev (to_m64 (m)); ms = _mm_unpacklo_pi8 (pix1, _mm_setzero_si64 ()); md = _mm_unpacklo_pi8 (pix2, _mm_setzero_si64 ()); msa = expand_alpha (ms); store8888 (dst, (in_over (ms, msa, ma, md))); } } else { BILINEAR_SKIP_ONE_PIXEL (); } w--; dst++; } _mm_empty (); } FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_cover_OVER, scaled_bilinear_scanline_mmx_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, COVER, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_pad_OVER, scaled_bilinear_scanline_mmx_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, PAD, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_none_OVER, scaled_bilinear_scanline_mmx_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, NONE, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_normal_OVER, scaled_bilinear_scanline_mmx_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, NORMAL, FLAG_HAVE_NON_SOLID_MASK) static uint32_t * mmx_fetch_x8r8g8b8 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; uint32_t *dst = iter->buffer; uint32_t *src = (uint32_t *)iter->bits; iter->bits += iter->stride; while (w && ((uintptr_t)dst) & 7) { *dst++ = (*src++) | 0xff000000; w--; } while (w >= 8) { __m64 vsrc1 = ldq_u ((__m64 *)(src + 0)); __m64 vsrc2 = ldq_u ((__m64 *)(src + 2)); __m64 vsrc3 = ldq_u ((__m64 *)(src + 4)); __m64 vsrc4 = ldq_u ((__m64 *)(src + 6)); *(__m64 *)(dst + 0) = _mm_or_si64 (vsrc1, MC (ff000000)); *(__m64 *)(dst + 2) = _mm_or_si64 (vsrc2, MC (ff000000)); *(__m64 *)(dst + 4) = _mm_or_si64 (vsrc3, MC (ff000000)); *(__m64 *)(dst + 6) = _mm_or_si64 (vsrc4, MC (ff000000)); dst += 8; src += 8; w -= 8; } while (w) { *dst++ = (*src++) | 0xff000000; w--; } _mm_empty (); return iter->buffer; } static uint32_t * mmx_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; uint32_t *dst = iter->buffer; uint16_t *src = (uint16_t *)iter->bits; iter->bits += iter->stride; while (w && ((uintptr_t)dst) & 0x0f) { uint16_t s = *src++; *dst++ = convert_0565_to_8888 (s); w--; } while (w >= 4) { __m64 vsrc = ldq_u ((__m64 *)src); __m64 mm0, mm1; expand_4xpacked565 (vsrc, &mm0, &mm1, 1); *(__m64 *)(dst + 0) = mm0; *(__m64 *)(dst + 2) = mm1; dst += 4; src += 4; w -= 4; } while (w) { uint16_t s = *src++; *dst++ = convert_0565_to_8888 (s); w--; } _mm_empty (); return iter->buffer; } static uint32_t * mmx_fetch_a8 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; uint32_t *dst = iter->buffer; uint8_t *src = iter->bits; iter->bits += iter->stride; while (w && (((uintptr_t)dst) & 15)) { *dst++ = *(src++) << 24; w--; } while (w >= 8) { __m64 mm0 = ldq_u ((__m64 *)src); __m64 mm1 = _mm_unpacklo_pi8 (_mm_setzero_si64(), mm0); __m64 mm2 = _mm_unpackhi_pi8 (_mm_setzero_si64(), mm0); __m64 mm3 = _mm_unpacklo_pi16 (_mm_setzero_si64(), mm1); __m64 mm4 = _mm_unpackhi_pi16 (_mm_setzero_si64(), mm1); __m64 mm5 = _mm_unpacklo_pi16 (_mm_setzero_si64(), mm2); __m64 mm6 = _mm_unpackhi_pi16 (_mm_setzero_si64(), mm2); *(__m64 *)(dst + 0) = mm3; *(__m64 *)(dst + 2) = mm4; *(__m64 *)(dst + 4) = mm5; *(__m64 *)(dst + 6) = mm6; dst += 8; src += 8; w -= 8; } while (w) { *dst++ = *(src++) << 24; w--; } _mm_empty (); return iter->buffer; } typedef struct { pixman_format_code_t format; pixman_iter_get_scanline_t get_scanline; } fetcher_info_t; static const fetcher_info_t fetchers[] = { { PIXMAN_x8r8g8b8, mmx_fetch_x8r8g8b8 }, { PIXMAN_r5g6b5, mmx_fetch_r5g6b5 }, { PIXMAN_a8, mmx_fetch_a8 }, { PIXMAN_null } }; static pixman_bool_t mmx_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; #define FLAGS \ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) if ((iter->iter_flags & ITER_NARROW) && (iter->image_flags & FLAGS) == FLAGS) { const fetcher_info_t *f; for (f = &fetchers[0]; f->format != PIXMAN_null; f++) { if (image->common.extended_format_code == f->format) { uint8_t *b = (uint8_t *)image->bits.bits; int s = image->bits.rowstride * 4; iter->bits = b + s * iter->y + iter->x * PIXMAN_FORMAT_BPP (f->format) / 8; iter->stride = s; iter->get_scanline = f->get_scanline; return TRUE; } } } return FALSE; } static const pixman_fast_path_t mmx_fast_paths[] = { PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, mmx_composite_over_n_8_0565 ), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, mmx_composite_over_n_8_0565 ), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, mmx_composite_over_n_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, mmx_composite_over_n_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, mmx_composite_over_n_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, mmx_composite_over_n_8_8888 ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, mmx_composite_over_n_8888_8888_ca ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, mmx_composite_over_n_8888_8888_ca ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, mmx_composite_over_n_8888_0565_ca ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, mmx_composite_over_n_8888_8888_ca ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, mmx_composite_over_n_8888_8888_ca ), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, mmx_composite_over_n_8888_0565_ca ), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, a8r8g8b8, mmx_composite_over_pixbuf_8888 ), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, x8r8g8b8, mmx_composite_over_pixbuf_8888 ), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, r5g6b5, mmx_composite_over_pixbuf_0565 ), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, a8b8g8r8, mmx_composite_over_pixbuf_8888 ), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, x8b8g8r8, mmx_composite_over_pixbuf_8888 ), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, b5g6r5, mmx_composite_over_pixbuf_0565 ), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, a8r8g8b8, mmx_composite_over_x888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, x8r8g8b8, mmx_composite_over_x888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, a8b8g8r8, mmx_composite_over_x888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, x8b8g8r8, mmx_composite_over_x888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, mmx_composite_over_8888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, mmx_composite_over_8888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, mmx_composite_over_8888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, mmx_composite_over_8888_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, mmx_composite_over_x888_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, mmx_composite_over_x888_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, mmx_composite_over_x888_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, mmx_composite_over_x888_8_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, mmx_composite_over_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, mmx_composite_over_n_8888 ), PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, mmx_composite_over_n_0565 ), PIXMAN_STD_FAST_PATH (OVER, solid, null, b5g6r5, mmx_composite_over_n_0565 ), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, mmx_composite_over_8888_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, mmx_composite_over_8888_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, mmx_composite_over_8888_0565 ), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, mmx_composite_over_8888_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, mmx_composite_over_8888_8888 ), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, mmx_composite_over_8888_0565 ), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, mmx_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, mmx_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (ADD, r5g6b5, null, r5g6b5, mmx_composite_add_0565_0565 ), PIXMAN_STD_FAST_PATH (ADD, b5g6r5, null, b5g6r5, mmx_composite_add_0565_0565 ), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, mmx_composite_add_8888_8888 ), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, mmx_composite_add_8888_8888 ), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, mmx_composite_add_8_8 ), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, mmx_composite_add_n_8_8 ), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, mmx_composite_src_x888_0565 ), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, mmx_composite_src_x888_0565 ), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, mmx_composite_src_x888_0565 ), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, mmx_composite_src_x888_0565 ), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, mmx_composite_src_n_8_8888 ), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, mmx_composite_src_n_8_8888 ), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, mmx_composite_src_n_8_8888 ), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, mmx_composite_src_n_8_8888 ), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, mmx_composite_copy_area ), PIXMAN_STD_FAST_PATH (IN, a8, null, a8, mmx_composite_in_8_8 ), PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, mmx_composite_in_n_8_8 ), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_8888 ), SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_8888 ), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_8_8888 ), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_8_8888 ), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_8_8888 ), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_8_8888 ), { PIXMAN_OP_NONE }, }; pixman_implementation_t * _pixman_implementation_create_mmx (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, mmx_fast_paths); imp->combine_32[PIXMAN_OP_OVER] = mmx_combine_over_u; imp->combine_32[PIXMAN_OP_OVER_REVERSE] = mmx_combine_over_reverse_u; imp->combine_32[PIXMAN_OP_IN] = mmx_combine_in_u; imp->combine_32[PIXMAN_OP_IN_REVERSE] = mmx_combine_in_reverse_u; imp->combine_32[PIXMAN_OP_OUT] = mmx_combine_out_u; imp->combine_32[PIXMAN_OP_OUT_REVERSE] = mmx_combine_out_reverse_u; imp->combine_32[PIXMAN_OP_ATOP] = mmx_combine_atop_u; imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = mmx_combine_atop_reverse_u; imp->combine_32[PIXMAN_OP_XOR] = mmx_combine_xor_u; imp->combine_32[PIXMAN_OP_ADD] = mmx_combine_add_u; imp->combine_32[PIXMAN_OP_SATURATE] = mmx_combine_saturate_u; imp->combine_32_ca[PIXMAN_OP_SRC] = mmx_combine_src_ca; imp->combine_32_ca[PIXMAN_OP_OVER] = mmx_combine_over_ca; imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = mmx_combine_over_reverse_ca; imp->combine_32_ca[PIXMAN_OP_IN] = mmx_combine_in_ca; imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = mmx_combine_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_OUT] = mmx_combine_out_ca; imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = mmx_combine_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_ATOP] = mmx_combine_atop_ca; imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = mmx_combine_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_XOR] = mmx_combine_xor_ca; imp->combine_32_ca[PIXMAN_OP_ADD] = mmx_combine_add_ca; imp->blt = mmx_blt; imp->fill = mmx_fill; imp->src_iter_init = mmx_src_iter_init; return imp; } #endif /* USE_X86_MMX || USE_ARM_IWMMXT || USE_LOONGSON_MMI */ Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-noop.c000066400000000000000000000116021271037650300256150ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2011 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" #include "pixman-combine32.h" #include "pixman-inlines.h" static void noop_composite (pixman_implementation_t *imp, pixman_composite_info_t *info) { return; } static void dest_write_back_direct (pixman_iter_t *iter) { iter->buffer += iter->image->bits.rowstride; } static uint32_t * noop_get_scanline (pixman_iter_t *iter, const uint32_t *mask) { uint32_t *result = iter->buffer; iter->buffer += iter->image->bits.rowstride; return result; } static uint32_t * get_scanline_null (pixman_iter_t *iter, const uint32_t *mask) { return NULL; } static pixman_bool_t noop_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; #define FLAGS \ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM) if (!image) { iter->get_scanline = get_scanline_null; } else if ((iter->iter_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) == (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) { iter->get_scanline = _pixman_iter_get_scanline_noop; } else if (image->common.extended_format_code == PIXMAN_solid && (iter->image->type == SOLID || (iter->image_flags & FAST_PATH_NO_ALPHA_MAP))) { if (iter->iter_flags & ITER_NARROW) { uint32_t *buffer = iter->buffer; uint32_t *end = buffer + iter->width; uint32_t color; if (image->type == SOLID) color = image->solid.color_32; else color = image->bits.fetch_pixel_32 (&image->bits, 0, 0); while (buffer < end) *(buffer++) = color; } else { argb_t *buffer = (argb_t *)iter->buffer; argb_t *end = buffer + iter->width; argb_t color; if (image->type == SOLID) color = image->solid.color_float; else color = image->bits.fetch_pixel_float (&image->bits, 0, 0); while (buffer < end) *(buffer++) = color; } iter->get_scanline = _pixman_iter_get_scanline_noop; } else if (image->common.extended_format_code == PIXMAN_a8r8g8b8 && (iter->iter_flags & ITER_NARROW) && (iter->image_flags & FLAGS) == FLAGS && iter->x >= 0 && iter->y >= 0 && iter->x + iter->width <= image->bits.width && iter->y + iter->height <= image->bits.height) { iter->buffer = image->bits.bits + iter->y * image->bits.rowstride + iter->x; iter->get_scanline = noop_get_scanline; } else { return FALSE; } return TRUE; } static pixman_bool_t noop_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; uint32_t image_flags = iter->image_flags; uint32_t iter_flags = iter->iter_flags; if ((image_flags & FAST_PATH_STD_DEST_FLAGS) == FAST_PATH_STD_DEST_FLAGS && (iter_flags & ITER_NARROW) == ITER_NARROW && ((image->common.extended_format_code == PIXMAN_a8r8g8b8) || (image->common.extended_format_code == PIXMAN_x8r8g8b8 && (iter_flags & (ITER_LOCALIZED_ALPHA))))) { iter->buffer = image->bits.bits + iter->y * image->bits.rowstride + iter->x; iter->get_scanline = _pixman_iter_get_scanline_noop; iter->write_back = dest_write_back_direct; return TRUE; } else { return FALSE; } } static const pixman_fast_path_t noop_fast_paths[] = { { PIXMAN_OP_DST, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, noop_composite }, { PIXMAN_OP_NONE }, }; pixman_implementation_t * _pixman_implementation_create_noop (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, noop_fast_paths); imp->src_iter_init = noop_src_iter_init; imp->dest_iter_init = noop_dest_iter_init; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-ppc.c000066400000000000000000000071141271037650300254270ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #ifdef USE_VMX /* The CPU detection code needs to be in a file not compiled with * "-maltivec -mabi=altivec", as gcc would try to save vector register * across function calls causing SIGILL on cpus without Altivec/vmx. */ #ifdef __APPLE__ #include static pixman_bool_t pixman_have_vmx (void) { int error, have_vmx; size_t length = sizeof(have_vmx); error = sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0); if (error) return FALSE; return have_vmx; } #elif defined (__OpenBSD__) #include #include #include static pixman_bool_t pixman_have_vmx (void) { int error, have_vmx; int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC }; size_t length = sizeof(have_vmx); error = sysctl (mib, 2, &have_vmx, &length, NULL, 0); if (error != 0) return FALSE; return have_vmx; } #elif defined (__linux__) #include #include #include #include #include #include #include static pixman_bool_t pixman_have_vmx (void) { int have_vmx = FALSE; int fd; struct { unsigned long type; unsigned long value; } aux; fd = open ("/proc/self/auxv", O_RDONLY); if (fd >= 0) { while (read (fd, &aux, sizeof (aux)) == sizeof (aux)) { if (aux.type == AT_HWCAP && (aux.value & PPC_FEATURE_HAS_ALTIVEC)) { have_vmx = TRUE; break; } } close (fd); } return have_vmx; } #else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */ #include #include static jmp_buf jump_env; static void vmx_test (int sig, siginfo_t *si, void * unused) { longjmp (jump_env, 1); } static pixman_bool_t pixman_have_vmx (void) { struct sigaction sa, osa; int jmp_result; sa.sa_flags = SA_SIGINFO; sigemptyset (&sa.sa_mask); sa.sa_sigaction = vmx_test; sigaction (SIGILL, &sa, &osa); jmp_result = setjmp (jump_env); if (jmp_result == 0) { asm volatile ( "vor 0, 0, 0" ); } sigaction (SIGILL, &osa, NULL); return (jmp_result == 0); } #endif /* __APPLE__ */ #endif /* USE_VMX */ pixman_implementation_t * _pixman_ppc_get_implementations (pixman_implementation_t *imp) { #ifdef USE_VMX if (!_pixman_disabled ("vmx") && pixman_have_vmx ()) imp = _pixman_implementation_create_vmx (imp); #endif return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-private.h000066400000000000000000001026501271037650300263250ustar00rootroot00000000000000#include #ifndef PIXMAN_PRIVATE_H #define PIXMAN_PRIVATE_H /* * The defines which are shared between C and assembly code */ /* bilinear interpolation precision (must be <= 8) */ #define BILINEAR_INTERPOLATION_BITS 7 #define BILINEAR_INTERPOLATION_RANGE (1 << BILINEAR_INTERPOLATION_BITS) /* * C specific part */ #ifndef __ASSEMBLER__ #ifndef PACKAGE # error config.h must be included before pixman-private.h #endif #define PIXMAN_DISABLE_DEPRECATED #define PIXMAN_USE_INTERNAL_API #include "pixman.h" #include #include #include #include #include #include "pixman-compiler.h" /* * Images */ typedef struct image_common image_common_t; typedef struct solid_fill solid_fill_t; typedef struct gradient gradient_t; typedef struct linear_gradient linear_gradient_t; typedef struct horizontal_gradient horizontal_gradient_t; typedef struct vertical_gradient vertical_gradient_t; typedef struct conical_gradient conical_gradient_t; typedef struct radial_gradient radial_gradient_t; typedef struct bits_image bits_image_t; typedef struct circle circle_t; typedef struct argb_t argb_t; struct argb_t { float a; float r; float g; float b; }; typedef void (*fetch_scanline_t) (pixman_image_t *image, int x, int y, int width, uint32_t *buffer, const uint32_t *mask); typedef uint32_t (*fetch_pixel_32_t) (bits_image_t *image, int x, int y); typedef argb_t (*fetch_pixel_float_t) (bits_image_t *image, int x, int y); typedef void (*store_scanline_t) (bits_image_t * image, int x, int y, int width, const uint32_t *values); typedef enum { BITS, LINEAR, CONICAL, RADIAL, SOLID } image_type_t; typedef void (*property_changed_func_t) (pixman_image_t *image); struct image_common { image_type_t type; int32_t ref_count; pixman_region32_t clip_region; int32_t alpha_count; /* How many times this image is being used as an alpha map */ pixman_bool_t have_clip_region; /* FALSE if there is no clip */ pixman_bool_t client_clip; /* Whether the source clip was set by a client */ pixman_bool_t clip_sources; /* Whether the clip applies when * the image is used as a source */ pixman_bool_t dirty; pixman_transform_t * transform; pixman_repeat_t repeat; pixman_filter_t filter; pixman_fixed_t * filter_params; int n_filter_params; bits_image_t * alpha_map; int alpha_origin_x; int alpha_origin_y; pixman_bool_t component_alpha; property_changed_func_t property_changed; pixman_image_destroy_func_t destroy_func; void * destroy_data; uint32_t flags; pixman_format_code_t extended_format_code; }; struct solid_fill { image_common_t common; pixman_color_t color; uint32_t color_32; argb_t color_float; }; struct gradient { image_common_t common; int n_stops; pixman_gradient_stop_t *stops; }; struct linear_gradient { gradient_t common; pixman_point_fixed_t p1; pixman_point_fixed_t p2; }; struct circle { pixman_fixed_t x; pixman_fixed_t y; pixman_fixed_t radius; }; struct radial_gradient { gradient_t common; circle_t c1; circle_t c2; circle_t delta; double a; double inva; double mindr; }; struct conical_gradient { gradient_t common; pixman_point_fixed_t center; double angle; }; struct bits_image { image_common_t common; pixman_format_code_t format; const pixman_indexed_t * indexed; int width; int height; uint32_t * bits; uint32_t * free_me; int rowstride; /* in number of uint32_t's */ fetch_scanline_t fetch_scanline_32; fetch_pixel_32_t fetch_pixel_32; store_scanline_t store_scanline_32; fetch_scanline_t fetch_scanline_float; fetch_pixel_float_t fetch_pixel_float; store_scanline_t store_scanline_float; /* Used for indirect access to the bits */ pixman_read_memory_func_t read_func; pixman_write_memory_func_t write_func; }; union pixman_image { image_type_t type; image_common_t common; bits_image_t bits; gradient_t gradient; linear_gradient_t linear; conical_gradient_t conical; radial_gradient_t radial; solid_fill_t solid; }; typedef struct pixman_iter_t pixman_iter_t; typedef uint32_t *(* pixman_iter_get_scanline_t) (pixman_iter_t *iter, const uint32_t *mask); typedef void (* pixman_iter_write_back_t) (pixman_iter_t *iter); typedef enum { ITER_NARROW = (1 << 0), /* "Localized alpha" is when the alpha channel is used only to compute * the alpha value of the destination. This means that the computation * of the RGB values of the result is independent of the alpha value. * * For example, the OVER operator has localized alpha for the * destination, because the RGB values of the result can be computed * without knowing the destination alpha. Similarly, ADD has localized * alpha for both source and destination because the RGB values of the * result can be computed without knowing the alpha value of source or * destination. * * When he destination is xRGB, this is useful knowledge, because then * we can treat it as if it were ARGB, which means in some cases we can * avoid copying it to a temporary buffer. */ ITER_LOCALIZED_ALPHA = (1 << 1), ITER_IGNORE_ALPHA = (1 << 2), ITER_IGNORE_RGB = (1 << 3) } iter_flags_t; struct pixman_iter_t { /* These are initialized by _pixman_implementation_{src,dest}_init */ pixman_image_t * image; uint32_t * buffer; int x, y; int width; int height; iter_flags_t iter_flags; uint32_t image_flags; /* These function pointers are initialized by the implementation */ pixman_iter_get_scanline_t get_scanline; pixman_iter_write_back_t write_back; /* These fields are scratch data that implementations can use */ void * data; uint8_t * bits; int stride; }; void _pixman_bits_image_setup_accessors (bits_image_t *image); void _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter); void _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter); void _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); void _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); void _pixman_conical_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); void _pixman_image_init (pixman_image_t *image); pixman_bool_t _pixman_bits_image_init (pixman_image_t * image, pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride, pixman_bool_t clear); pixman_bool_t _pixman_image_fini (pixman_image_t *image); pixman_image_t * _pixman_image_allocate (void); pixman_bool_t _pixman_init_gradient (gradient_t * gradient, const pixman_gradient_stop_t *stops, int n_stops); void _pixman_image_reset_clip_region (pixman_image_t *image); void _pixman_image_validate (pixman_image_t *image); #define PIXMAN_IMAGE_GET_LINE(image, x, y, type, out_stride, line, mul) \ do \ { \ uint32_t *__bits__; \ int __stride__; \ \ __bits__ = image->bits.bits; \ __stride__ = image->bits.rowstride; \ (out_stride) = \ __stride__ * (int) sizeof (uint32_t) / (int) sizeof (type); \ (line) = \ ((type *) __bits__) + (out_stride) * (y) + (mul) * (x); \ } while (0) /* * Gradient walker */ typedef struct { float a_s, a_b; float r_s, r_b; float g_s, g_b; float b_s, b_b; pixman_fixed_t left_x; pixman_fixed_t right_x; pixman_gradient_stop_t *stops; int num_stops; pixman_repeat_t repeat; pixman_bool_t need_reset; } pixman_gradient_walker_t; void _pixman_gradient_walker_init (pixman_gradient_walker_t *walker, gradient_t * gradient, pixman_repeat_t repeat); void _pixman_gradient_walker_reset (pixman_gradient_walker_t *walker, pixman_fixed_48_16_t pos); uint32_t _pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker, pixman_fixed_48_16_t x); /* * Edges */ #define MAX_ALPHA(n) ((1 << (n)) - 1) #define N_Y_FRAC(n) ((n) == 1 ? 1 : (1 << ((n) / 2)) - 1) #define N_X_FRAC(n) ((n) == 1 ? 1 : (1 << ((n) / 2)) + 1) #define STEP_Y_SMALL(n) (pixman_fixed_1 / N_Y_FRAC (n)) #define STEP_Y_BIG(n) (pixman_fixed_1 - (N_Y_FRAC (n) - 1) * STEP_Y_SMALL (n)) #define Y_FRAC_FIRST(n) (STEP_Y_BIG (n) / 2) #define Y_FRAC_LAST(n) (Y_FRAC_FIRST (n) + (N_Y_FRAC (n) - 1) * STEP_Y_SMALL (n)) #define STEP_X_SMALL(n) (pixman_fixed_1 / N_X_FRAC (n)) #define STEP_X_BIG(n) (pixman_fixed_1 - (N_X_FRAC (n) - 1) * STEP_X_SMALL (n)) #define X_FRAC_FIRST(n) (STEP_X_BIG (n) / 2) #define X_FRAC_LAST(n) (X_FRAC_FIRST (n) + (N_X_FRAC (n) - 1) * STEP_X_SMALL (n)) #define RENDER_SAMPLES_X(x, n) \ ((n) == 1? 0 : (pixman_fixed_frac (x) + \ X_FRAC_FIRST (n)) / STEP_X_SMALL (n)) void pixman_rasterize_edges_accessors (pixman_image_t *image, pixman_edge_t * l, pixman_edge_t * r, pixman_fixed_t t, pixman_fixed_t b); /* * Implementations */ typedef struct pixman_implementation_t pixman_implementation_t; typedef struct { pixman_op_t op; pixman_image_t * src_image; pixman_image_t * mask_image; pixman_image_t * dest_image; int32_t src_x; int32_t src_y; int32_t mask_x; int32_t mask_y; int32_t dest_x; int32_t dest_y; int32_t width; int32_t height; uint32_t src_flags; uint32_t mask_flags; uint32_t dest_flags; } pixman_composite_info_t; #define PIXMAN_COMPOSITE_ARGS(info) \ MAYBE_UNUSED pixman_op_t op = info->op; \ MAYBE_UNUSED pixman_image_t * src_image = info->src_image; \ MAYBE_UNUSED pixman_image_t * mask_image = info->mask_image; \ MAYBE_UNUSED pixman_image_t * dest_image = info->dest_image; \ MAYBE_UNUSED int32_t src_x = info->src_x; \ MAYBE_UNUSED int32_t src_y = info->src_y; \ MAYBE_UNUSED int32_t mask_x = info->mask_x; \ MAYBE_UNUSED int32_t mask_y = info->mask_y; \ MAYBE_UNUSED int32_t dest_x = info->dest_x; \ MAYBE_UNUSED int32_t dest_y = info->dest_y; \ MAYBE_UNUSED int32_t width = info->width; \ MAYBE_UNUSED int32_t height = info->height typedef void (*pixman_combine_32_func_t) (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width); typedef void (*pixman_combine_float_func_t) (pixman_implementation_t *imp, pixman_op_t op, float * dest, const float * src, const float * mask, int n_pixels); typedef void (*pixman_composite_func_t) (pixman_implementation_t *imp, pixman_composite_info_t *info); typedef pixman_bool_t (*pixman_blt_func_t) (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height); typedef pixman_bool_t (*pixman_fill_func_t) (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler); typedef pixman_bool_t (*pixman_iter_init_func_t) (pixman_implementation_t *imp, pixman_iter_t *iter); void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp); void _pixman_setup_combiner_functions_float (pixman_implementation_t *imp); typedef struct { pixman_op_t op; pixman_format_code_t src_format; uint32_t src_flags; pixman_format_code_t mask_format; uint32_t mask_flags; pixman_format_code_t dest_format; uint32_t dest_flags; pixman_composite_func_t func; } pixman_fast_path_t; struct pixman_implementation_t { pixman_implementation_t * toplevel; pixman_implementation_t * fallback; const pixman_fast_path_t * fast_paths; pixman_blt_func_t blt; pixman_fill_func_t fill; pixman_iter_init_func_t src_iter_init; pixman_iter_init_func_t dest_iter_init; pixman_combine_32_func_t combine_32[PIXMAN_N_OPERATORS]; pixman_combine_32_func_t combine_32_ca[PIXMAN_N_OPERATORS]; pixman_combine_float_func_t combine_float[PIXMAN_N_OPERATORS]; pixman_combine_float_func_t combine_float_ca[PIXMAN_N_OPERATORS]; }; uint32_t _pixman_image_get_solid (pixman_implementation_t *imp, pixman_image_t * image, pixman_format_code_t format); pixman_implementation_t * _pixman_implementation_create (pixman_implementation_t *fallback, const pixman_fast_path_t *fast_paths); void _pixman_implementation_lookup_composite (pixman_implementation_t *toplevel, pixman_op_t op, pixman_format_code_t src_format, uint32_t src_flags, pixman_format_code_t mask_format, uint32_t mask_flags, pixman_format_code_t dest_format, uint32_t dest_flags, pixman_implementation_t **out_imp, pixman_composite_func_t *out_func); pixman_combine_32_func_t _pixman_implementation_lookup_combiner (pixman_implementation_t *imp, pixman_op_t op, pixman_bool_t component_alpha, pixman_bool_t wide); pixman_bool_t _pixman_implementation_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height); pixman_bool_t _pixman_implementation_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler); pixman_bool_t _pixman_implementation_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter, pixman_image_t *image, int x, int y, int width, int height, uint8_t *buffer, iter_flags_t flags, uint32_t image_flags); pixman_bool_t _pixman_implementation_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter, pixman_image_t *image, int x, int y, int width, int height, uint8_t *buffer, iter_flags_t flags, uint32_t image_flags); /* Specific implementations */ pixman_implementation_t * _pixman_implementation_create_general (void); pixman_implementation_t * _pixman_implementation_create_fast_path (pixman_implementation_t *fallback); pixman_implementation_t * _pixman_implementation_create_noop (pixman_implementation_t *fallback); #if defined USE_X86_MMX || defined USE_ARM_IWMMXT || defined USE_LOONGSON_MMI pixman_implementation_t * _pixman_implementation_create_mmx (pixman_implementation_t *fallback); #endif #ifdef USE_SSE2 pixman_implementation_t * _pixman_implementation_create_sse2 (pixman_implementation_t *fallback); #endif #ifdef USE_ARM_SIMD pixman_implementation_t * _pixman_implementation_create_arm_simd (pixman_implementation_t *fallback); #endif #ifdef USE_ARM_NEON pixman_implementation_t * _pixman_implementation_create_arm_neon (pixman_implementation_t *fallback); #endif #ifdef USE_MIPS_DSPR2 pixman_implementation_t * _pixman_implementation_create_mips_dspr2 (pixman_implementation_t *fallback); #endif #ifdef USE_VMX pixman_implementation_t * _pixman_implementation_create_vmx (pixman_implementation_t *fallback); #endif pixman_bool_t _pixman_implementation_disabled (const char *name); pixman_implementation_t * _pixman_x86_get_implementations (pixman_implementation_t *imp); pixman_implementation_t * _pixman_arm_get_implementations (pixman_implementation_t *imp); pixman_implementation_t * _pixman_ppc_get_implementations (pixman_implementation_t *imp); pixman_implementation_t * _pixman_mips_get_implementations (pixman_implementation_t *imp); pixman_implementation_t * _pixman_choose_implementation (void); pixman_bool_t _pixman_disabled (const char *name); /* * Utilities */ pixman_bool_t _pixman_compute_composite_region32 (pixman_region32_t * region, pixman_image_t * src_image, pixman_image_t * mask_image, pixman_image_t * dest_image, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height); uint32_t * _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask); /* These "formats" all have depth 0, so they * will never clash with any real ones */ #define PIXMAN_null PIXMAN_FORMAT (0, 0, 0, 0, 0, 0) #define PIXMAN_solid PIXMAN_FORMAT (0, 1, 0, 0, 0, 0) #define PIXMAN_pixbuf PIXMAN_FORMAT (0, 2, 0, 0, 0, 0) #define PIXMAN_rpixbuf PIXMAN_FORMAT (0, 3, 0, 0, 0, 0) #define PIXMAN_unknown PIXMAN_FORMAT (0, 4, 0, 0, 0, 0) #define PIXMAN_any PIXMAN_FORMAT (0, 5, 0, 0, 0, 0) #define PIXMAN_OP_any (PIXMAN_N_OPERATORS + 1) #define FAST_PATH_ID_TRANSFORM (1 << 0) #define FAST_PATH_NO_ALPHA_MAP (1 << 1) #define FAST_PATH_NO_CONVOLUTION_FILTER (1 << 2) #define FAST_PATH_NO_PAD_REPEAT (1 << 3) #define FAST_PATH_NO_REFLECT_REPEAT (1 << 4) #define FAST_PATH_NO_ACCESSORS (1 << 5) #define FAST_PATH_NARROW_FORMAT (1 << 6) #define FAST_PATH_COMPONENT_ALPHA (1 << 8) #define FAST_PATH_SAMPLES_OPAQUE (1 << 7) #define FAST_PATH_UNIFIED_ALPHA (1 << 9) #define FAST_PATH_SCALE_TRANSFORM (1 << 10) #define FAST_PATH_NEAREST_FILTER (1 << 11) #define FAST_PATH_HAS_TRANSFORM (1 << 12) #define FAST_PATH_IS_OPAQUE (1 << 13) #define FAST_PATH_NO_NORMAL_REPEAT (1 << 14) #define FAST_PATH_NO_NONE_REPEAT (1 << 15) #define FAST_PATH_X_UNIT_POSITIVE (1 << 16) #define FAST_PATH_AFFINE_TRANSFORM (1 << 17) #define FAST_PATH_Y_UNIT_ZERO (1 << 18) #define FAST_PATH_BILINEAR_FILTER (1 << 19) #define FAST_PATH_ROTATE_90_TRANSFORM (1 << 20) #define FAST_PATH_ROTATE_180_TRANSFORM (1 << 21) #define FAST_PATH_ROTATE_270_TRANSFORM (1 << 22) #define FAST_PATH_SAMPLES_COVER_CLIP_NEAREST (1 << 23) #define FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR (1 << 24) #define FAST_PATH_BITS_IMAGE (1 << 25) #define FAST_PATH_SEPARABLE_CONVOLUTION_FILTER (1 << 26) #define FAST_PATH_PAD_REPEAT \ (FAST_PATH_NO_NONE_REPEAT | \ FAST_PATH_NO_NORMAL_REPEAT | \ FAST_PATH_NO_REFLECT_REPEAT) #define FAST_PATH_NORMAL_REPEAT \ (FAST_PATH_NO_NONE_REPEAT | \ FAST_PATH_NO_PAD_REPEAT | \ FAST_PATH_NO_REFLECT_REPEAT) #define FAST_PATH_NONE_REPEAT \ (FAST_PATH_NO_NORMAL_REPEAT | \ FAST_PATH_NO_PAD_REPEAT | \ FAST_PATH_NO_REFLECT_REPEAT) #define FAST_PATH_REFLECT_REPEAT \ (FAST_PATH_NO_NONE_REPEAT | \ FAST_PATH_NO_NORMAL_REPEAT | \ FAST_PATH_NO_PAD_REPEAT) #define FAST_PATH_STANDARD_FLAGS \ (FAST_PATH_NO_CONVOLUTION_FILTER | \ FAST_PATH_NO_ACCESSORS | \ FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NARROW_FORMAT) #define FAST_PATH_STD_DEST_FLAGS \ (FAST_PATH_NO_ACCESSORS | \ FAST_PATH_NO_ALPHA_MAP | \ FAST_PATH_NARROW_FORMAT) #define SOURCE_FLAGS(format) \ (FAST_PATH_STANDARD_FLAGS | \ ((PIXMAN_ ## format == PIXMAN_solid) ? \ 0 : (FAST_PATH_SAMPLES_COVER_CLIP_NEAREST | FAST_PATH_NEAREST_FILTER | FAST_PATH_ID_TRANSFORM))) #define MASK_FLAGS(format, extra) \ ((PIXMAN_ ## format == PIXMAN_null) ? 0 : (SOURCE_FLAGS (format) | extra)) #define FAST_PATH(op, src, src_flags, mask, mask_flags, dest, dest_flags, func) \ PIXMAN_OP_ ## op, \ PIXMAN_ ## src, \ src_flags, \ PIXMAN_ ## mask, \ mask_flags, \ PIXMAN_ ## dest, \ dest_flags, \ func #define PIXMAN_STD_FAST_PATH(op, src, mask, dest, func) \ { FAST_PATH ( \ op, \ src, SOURCE_FLAGS (src), \ mask, MASK_FLAGS (mask, FAST_PATH_UNIFIED_ALPHA), \ dest, FAST_PATH_STD_DEST_FLAGS, \ func) } #define PIXMAN_STD_FAST_PATH_CA(op, src, mask, dest, func) \ { FAST_PATH ( \ op, \ src, SOURCE_FLAGS (src), \ mask, MASK_FLAGS (mask, FAST_PATH_COMPONENT_ALPHA), \ dest, FAST_PATH_STD_DEST_FLAGS, \ func) } extern pixman_implementation_t *global_implementation; static force_inline pixman_implementation_t * get_implementation (void) { #ifndef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR if (!global_implementation) global_implementation = _pixman_choose_implementation (); #endif return global_implementation; } /* This function is exported for the sake of the test suite and not part * of the ABI. */ PIXMAN_EXPORT pixman_implementation_t * _pixman_internal_only_get_implementation (void); /* Memory allocation helpers */ void * pixman_malloc_ab (unsigned int n, unsigned int b); void * pixman_malloc_abc (unsigned int a, unsigned int b, unsigned int c); pixman_bool_t _pixman_multiply_overflows_size (size_t a, size_t b); pixman_bool_t _pixman_multiply_overflows_int (unsigned int a, unsigned int b); pixman_bool_t _pixman_addition_overflows_int (unsigned int a, unsigned int b); /* Compositing utilities */ void pixman_expand_to_float (argb_t *dst, const uint32_t *src, pixman_format_code_t format, int width); void pixman_contract_from_float (uint32_t *dst, const argb_t *src, int width); /* Region Helpers */ pixman_bool_t pixman_region32_copy_from_region16 (pixman_region32_t *dst, pixman_region16_t *src); pixman_bool_t pixman_region16_copy_from_region32 (pixman_region16_t *dst, pixman_region32_t *src); /* Doubly linked lists */ typedef struct pixman_link_t pixman_link_t; struct pixman_link_t { pixman_link_t *next; pixman_link_t *prev; }; typedef struct pixman_list_t pixman_list_t; struct pixman_list_t { pixman_link_t *head; pixman_link_t *tail; }; static force_inline void pixman_list_init (pixman_list_t *list) { list->head = (pixman_link_t *)list; list->tail = (pixman_link_t *)list; } static force_inline void pixman_list_prepend (pixman_list_t *list, pixman_link_t *link) { link->next = list->head; link->prev = (pixman_link_t *)list; list->head->prev = link; list->head = link; } static force_inline void pixman_list_unlink (pixman_link_t *link) { link->prev->next = link->next; link->next->prev = link->prev; } static force_inline void pixman_list_move_to_front (pixman_list_t *list, pixman_link_t *link) { pixman_list_unlink (link); pixman_list_prepend (list, link); } /* Misc macros */ #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #ifndef MIN # define MIN(a, b) ((a < b) ? a : b) #endif #ifndef MAX # define MAX(a, b) ((a > b) ? a : b) #endif /* Integer division that rounds towards -infinity */ #define DIV(a, b) \ ((((a) < 0) == ((b) < 0)) ? (a) / (b) : \ ((a) - (b) + 1 - (((b) < 0) << 1)) / (b)) /* Modulus that produces the remainder wrt. DIV */ #define MOD(a, b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) #define CLIP(v, low, high) ((v) < (low) ? (low) : ((v) > (high) ? (high) : (v))) #define FLOAT_IS_ZERO(f) (-FLT_MIN < (f) && (f) < FLT_MIN) /* Conversion between 8888 and 0565 */ static force_inline uint16_t convert_8888_to_0565 (uint32_t s) { /* The following code can be compiled into just 4 instructions on ARM */ uint32_t a, b; a = (s >> 3) & 0x1F001F; b = s & 0xFC00; a |= a >> 5; a |= b >> 5; return (uint16_t)a; } static force_inline uint32_t convert_0565_to_0888 (uint16_t s) { return (((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | ((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | ((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000))); } static force_inline uint32_t convert_0565_to_8888 (uint16_t s) { return convert_0565_to_0888 (s) | 0xff000000; } /* Trivial versions that are useful in macros */ static force_inline uint32_t convert_8888_to_8888 (uint32_t s) { return s; } static force_inline uint32_t convert_x888_to_8888 (uint32_t s) { return s | 0xff000000; } static force_inline uint16_t convert_0565_to_0565 (uint16_t s) { return s; } #define PIXMAN_FORMAT_IS_WIDE(f) \ (PIXMAN_FORMAT_A (f) > 8 || \ PIXMAN_FORMAT_R (f) > 8 || \ PIXMAN_FORMAT_G (f) > 8 || \ PIXMAN_FORMAT_B (f) > 8 || \ PIXMAN_FORMAT_TYPE (f) == PIXMAN_TYPE_ARGB_SRGB) #ifdef WORDS_BIGENDIAN # define SCREEN_SHIFT_LEFT(x,n) ((x) << (n)) # define SCREEN_SHIFT_RIGHT(x,n) ((x) >> (n)) #else # define SCREEN_SHIFT_LEFT(x,n) ((x) >> (n)) # define SCREEN_SHIFT_RIGHT(x,n) ((x) << (n)) #endif static force_inline uint32_t unorm_to_unorm (uint32_t val, int from_bits, int to_bits) { uint32_t result; if (from_bits == 0) return 0; /* Delete any extra bits */ val &= ((1 << from_bits) - 1); if (from_bits >= to_bits) return val >> (from_bits - to_bits); /* Start out with the high bit of val in the high bit of result. */ result = val << (to_bits - from_bits); /* Copy the bits in result, doubling the number of bits each time, until * we fill all to_bits. Unrolled manually because from_bits and to_bits * are usually known statically, so the compiler can turn all of this * into a few shifts. */ #define REPLICATE() \ do \ { \ if (from_bits < to_bits) \ { \ result |= result >> from_bits; \ \ from_bits *= 2; \ } \ } \ while (0) REPLICATE(); REPLICATE(); REPLICATE(); REPLICATE(); REPLICATE(); return result; } uint16_t pixman_float_to_unorm (float f, int n_bits); float pixman_unorm_to_float (uint16_t u, int n_bits); /* * Various debugging code */ #undef DEBUG #define COMPILE_TIME_ASSERT(x) \ do { typedef int compile_time_assertion [(x)?1:-1]; } while (0) /* Turn on debugging depending on what type of release this is */ #if (((PIXMAN_VERSION_MICRO % 2) == 0) && ((PIXMAN_VERSION_MINOR % 2) == 1)) /* Debugging gets turned on for development releases because these * are the things that end up in bleeding edge distributions such * as Rawhide etc. * * For performance reasons we don't turn it on for stable releases or * random git checkouts. (Random git checkouts are often used for * performance work). */ # define DEBUG #endif void _pixman_log_error (const char *function, const char *message); #define return_if_fail(expr) \ do \ { \ if (unlikely (!(expr))) \ { \ _pixman_log_error (FUNC, "The expression " # expr " was false"); \ return; \ } \ } \ while (0) #define return_val_if_fail(expr, retval) \ do \ { \ if (unlikely (!(expr))) \ { \ _pixman_log_error (FUNC, "The expression " # expr " was false"); \ return (retval); \ } \ } \ while (0) #define critical_if_fail(expr) \ do \ { \ if (unlikely (!(expr))) \ _pixman_log_error (FUNC, "The expression " # expr " was false"); \ } \ while (0) /* * Matrix */ typedef struct { pixman_fixed_48_16_t v[3]; } pixman_vector_48_16_t; pixman_bool_t pixman_transform_point_31_16 (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result); void pixman_transform_point_31_16_3d (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result); void pixman_transform_point_31_16_affine (const pixman_transform_t *t, const pixman_vector_48_16_t *v, pixman_vector_48_16_t *result); /* * Timers */ #ifdef PIXMAN_TIMERS static inline uint64_t oil_profile_stamp_rdtsc (void) { uint32_t hi, lo; __asm__ __volatile__ ("rdtsc\n" : "=a" (lo), "=d" (hi)); return lo | (((uint64_t)hi) << 32); } #define OIL_STAMP oil_profile_stamp_rdtsc typedef struct pixman_timer_t pixman_timer_t; struct pixman_timer_t { int initialized; const char * name; uint64_t n_times; uint64_t total; pixman_timer_t *next; }; extern int timer_defined; void pixman_timer_register (pixman_timer_t *timer); #define TIMER_BEGIN(tname) \ { \ static pixman_timer_t timer ## tname; \ uint64_t begin ## tname; \ \ if (!timer ## tname.initialized) \ { \ timer ## tname.initialized = 1; \ timer ## tname.name = # tname; \ pixman_timer_register (&timer ## tname); \ } \ \ timer ## tname.n_times++; \ begin ## tname = OIL_STAMP (); #define TIMER_END(tname) \ timer ## tname.total += OIL_STAMP () - begin ## tname; \ } #else #define TIMER_BEGIN(tname) #define TIMER_END(tname) #endif /* PIXMAN_TIMERS */ #endif /* __ASSEMBLER__ */ #endif /* PIXMAN_PRIVATE_H */ Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-radial-gradient.c000066400000000000000000000343241271037650300276770ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. * Copyright © 2000 SuSE, Inc. * 2005 Lars Knoll & Zack Rusin, Trolltech * Copyright © 2007 Red Hat, Inc. * * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" static inline pixman_fixed_32_32_t dot (pixman_fixed_48_16_t x1, pixman_fixed_48_16_t y1, pixman_fixed_48_16_t z1, pixman_fixed_48_16_t x2, pixman_fixed_48_16_t y2, pixman_fixed_48_16_t z2) { /* * Exact computation, assuming that the input values can * be represented as pixman_fixed_16_16_t */ return x1 * x2 + y1 * y2 + z1 * z2; } static inline double fdot (double x1, double y1, double z1, double x2, double y2, double z2) { /* * Error can be unbound in some special cases. * Using clever dot product algorithms (for example compensated * dot product) would improve this but make the code much less * obvious */ return x1 * x2 + y1 * y2 + z1 * z2; } static uint32_t radial_compute_color (double a, double b, double c, double inva, double dr, double mindr, pixman_gradient_walker_t *walker, pixman_repeat_t repeat) { /* * In this function error propagation can lead to bad results: * - discr can have an unbound error (if b*b-a*c is very small), * potentially making it the opposite sign of what it should have been * (thus clearing a pixel that would have been colored or vice-versa) * or propagating the error to sqrtdiscr; * if discr has the wrong sign or b is very small, this can lead to bad * results * * - the algorithm used to compute the solutions of the quadratic * equation is not numerically stable (but saves one division compared * to the numerically stable one); * this can be a problem if a*c is much smaller than b*b * * - the above problems are worse if a is small (as inva becomes bigger) */ double discr; if (a == 0) { double t; if (b == 0) return 0; t = pixman_fixed_1 / 2 * c / b; if (repeat == PIXMAN_REPEAT_NONE) { if (0 <= t && t <= pixman_fixed_1) return _pixman_gradient_walker_pixel (walker, t); } else { if (t * dr >= mindr) return _pixman_gradient_walker_pixel (walker, t); } return 0; } discr = fdot (b, a, 0, b, -c, 0); if (discr >= 0) { double sqrtdiscr, t0, t1; sqrtdiscr = sqrt (discr); t0 = (b + sqrtdiscr) * inva; t1 = (b - sqrtdiscr) * inva; /* * The root that must be used is the biggest one that belongs * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any * solution that results in a positive radius otherwise). * * If a > 0, t0 is the biggest solution, so if it is valid, it * is the correct result. * * If a < 0, only one of the solutions can be valid, so the * order in which they are tested is not important. */ if (repeat == PIXMAN_REPEAT_NONE) { if (0 <= t0 && t0 <= pixman_fixed_1) return _pixman_gradient_walker_pixel (walker, t0); else if (0 <= t1 && t1 <= pixman_fixed_1) return _pixman_gradient_walker_pixel (walker, t1); } else { if (t0 * dr >= mindr) return _pixman_gradient_walker_pixel (walker, t0); else if (t1 * dr >= mindr) return _pixman_gradient_walker_pixel (walker, t1); } } return 0; } static uint32_t * radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) { /* * Implementation of radial gradients following the PDF specification. * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference * Manual (PDF 32000-1:2008 at the time of this writing). * * In the radial gradient problem we are given two circles (câ‚,râ‚) and * (câ‚‚,râ‚‚) that define the gradient itself. * * Mathematically the gradient can be defined as the family of circles * * ((1-t)·câ‚ + t·(câ‚‚), (1-t)·râ‚ + t·râ‚‚) * * excluding those circles whose radius would be < 0. When a point * belongs to more than one circle, the one with a bigger t is the only * one that contributes to its color. When a point does not belong * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). * Further limitations on the range of values for t are imposed when * the gradient is not repeated, namely t must belong to [0,1]. * * The graphical result is the same as drawing the valid (radius > 0) * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient * is not repeated) using SOURCE operator composition. * * It looks like a cone pointing towards the viewer if the ending circle * is smaller than the starting one, a cone pointing inside the page if * the starting circle is the smaller one and like a cylinder if they * have the same radius. * * What we actually do is, given the point whose color we are interested * in, compute the t values for that point, solving for t in: * * length((1-t)·câ‚ + t·(câ‚‚) - p) = (1-t)·râ‚ + t·râ‚‚ * * Let's rewrite it in a simpler way, by defining some auxiliary * variables: * * cd = câ‚‚ - câ‚ * pd = p - câ‚ * dr = râ‚‚ - râ‚ * length(t·cd - pd) = râ‚ + t·dr * * which actually means * * hypot(t·cdx - pdx, t·cdy - pdy) = râ‚ + t·dr * * or * * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = râ‚ + t·dr. * * If we impose (as stated earlier) that râ‚ + t·dr >= 0, it becomes: * * (t·cdx - pdx)² + (t·cdy - pdy)² = (râ‚ + t·dr)² * * where we can actually expand the squares and solve for t: * * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = * = r₲ + 2·râ‚·t·dr + t²·dr² * * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + râ‚·dr)t + * (pdx² + pdy² - r₲) = 0 * * A = cdx² + cdy² - dr² * B = pdx·cdx + pdy·cdy + râ‚·dr * C = pdx² + pdy² - r₲ * At² - 2Bt + C = 0 * * The solutions (unless the equation degenerates because of A = 0) are: * * t = (B ± ⎷(B² - A·C)) / A * * The solution we are going to prefer is the bigger one, unless the * radius associated to it is negative (or it falls outside the valid t * range). * * Additional observations (useful for optimizations): * A does not depend on p * * A < 0 <=> one of the two circles completely contains the other one * <=> for every p, the radiuses associated with the two t solutions * have opposite sign */ pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint32_t *buffer = iter->buffer; gradient_t *gradient = (gradient_t *)image; radial_gradient_t *radial = (radial_gradient_t *)image; uint32_t *end = buffer + width; pixman_gradient_walker_t walker; pixman_vector_t v, unit; /* reference point is the center of the pixel */ v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; v.vector[2] = pixman_fixed_1; _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); if (image->common.transform) { if (!pixman_transform_point_3d (image->common.transform, &v)) return iter->buffer; unit.vector[0] = image->common.transform->matrix[0][0]; unit.vector[1] = image->common.transform->matrix[1][0]; unit.vector[2] = image->common.transform->matrix[2][0]; } else { unit.vector[0] = pixman_fixed_1; unit.vector[1] = 0; unit.vector[2] = 0; } if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) { /* * Given: * * t = (B ± ⎷(B² - A·C)) / A * * where * * A = cdx² + cdy² - dr² * B = pdx·cdx + pdy·cdy + râ‚·dr * C = pdx² + pdy² - r₲ * det = B² - A·C * * Since we have an affine transformation, we know that (pdx, pdy) * increase linearly with each pixel, * * pdx = pdxâ‚€ + n·ux, * pdy = pdyâ‚€ + n·uy, * * we can then express B, C and det through multiple differentiation. */ pixman_fixed_32_32_t b, db, c, dc, ddc; /* warning: this computation may overflow */ v.vector[0] -= radial->c1.x; v.vector[1] -= radial->c1.y; /* * B and C are computed and updated exactly. * If fdot was used instead of dot, in the worst case it would * lose 11 bits of precision in each of the multiplication and * summing up would zero out all the bit that were preserved, * thus making the result 0 instead of the correct one. * This would mean a worst case of unbound relative error or * about 2^10 absolute error */ b = dot (v.vector[0], v.vector[1], radial->c1.radius, radial->delta.x, radial->delta.y, radial->delta.radius); db = dot (unit.vector[0], unit.vector[1], 0, radial->delta.x, radial->delta.y, 0); c = dot (v.vector[0], v.vector[1], -((pixman_fixed_48_16_t) radial->c1.radius), v.vector[0], v.vector[1], radial->c1.radius); dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], 0, unit.vector[0], unit.vector[1], 0); ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, unit.vector[0], unit.vector[1], 0); while (buffer < end) { if (!mask || *mask++) { *buffer = radial_compute_color (radial->a, b, c, radial->inva, radial->delta.radius, radial->mindr, &walker, image->common.repeat); } b += db; c += dc; dc += ddc; ++buffer; } } else { /* projective */ /* Warning: * error propagation guarantees are much looser than in the affine case */ while (buffer < end) { if (!mask || *mask++) { if (v.vector[2] != 0) { double pdx, pdy, invv2, b, c; invv2 = 1. * pixman_fixed_1 / v.vector[2]; pdx = v.vector[0] * invv2 - radial->c1.x; /* / pixman_fixed_1 */ pdy = v.vector[1] * invv2 - radial->c1.y; /* / pixman_fixed_1 */ b = fdot (pdx, pdy, radial->c1.radius, radial->delta.x, radial->delta.y, radial->delta.radius); /* / pixman_fixed_1 / pixman_fixed_1 */ c = fdot (pdx, pdy, -radial->c1.radius, pdx, pdy, radial->c1.radius); /* / pixman_fixed_1 / pixman_fixed_1 */ *buffer = radial_compute_color (radial->a, b, c, radial->inva, radial->delta.radius, radial->mindr, &walker, image->common.repeat); } else { *buffer = 0; } } ++buffer; v.vector[0] += unit.vector[0]; v.vector[1] += unit.vector[1]; v.vector[2] += unit.vector[2]; } } iter->y++; return iter->buffer; } static uint32_t * radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) { uint32_t *buffer = radial_get_scanline_narrow (iter, NULL); pixman_expand_to_float ( (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); return buffer; } void _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) { if (iter->iter_flags & ITER_NARROW) iter->get_scanline = radial_get_scanline_narrow; else iter->get_scanline = radial_get_scanline_wide; } PIXMAN_EXPORT pixman_image_t * pixman_image_create_radial_gradient (const pixman_point_fixed_t * inner, const pixman_point_fixed_t * outer, pixman_fixed_t inner_radius, pixman_fixed_t outer_radius, const pixman_gradient_stop_t *stops, int n_stops) { pixman_image_t *image; radial_gradient_t *radial; image = _pixman_image_allocate (); if (!image) return NULL; radial = &image->radial; if (!_pixman_init_gradient (&radial->common, stops, n_stops)) { free (image); return NULL; } image->type = RADIAL; radial->c1.x = inner->x; radial->c1.y = inner->y; radial->c1.radius = inner_radius; radial->c2.x = outer->x; radial->c2.y = outer->y; radial->c2.radius = outer_radius; /* warning: this computations may overflow */ radial->delta.x = radial->c2.x - radial->c1.x; radial->delta.y = radial->c2.y - radial->c1.y; radial->delta.radius = radial->c2.radius - radial->c1.radius; /* computed exactly, then cast to double -> every bit of the double representation is correct (53 bits) */ radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius, radial->delta.x, radial->delta.y, radial->delta.radius); if (radial->a != 0) radial->inva = 1. * pixman_fixed_1 / radial->a; radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius; return image; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-region.c000066400000000000000000002275351271037650300261430ustar00rootroot00000000000000/* * Copyright 1987, 1988, 1989, 1998 The Open Group * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of The Open Group shall not be * used in advertising or otherwise to promote the sale, use or other dealings * in this Software without prior written authorization from The Open Group. * * Copyright 1987, 1988, 1989 by * Digital Equipment Corporation, Maynard, Massachusetts. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Digital not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Copyright © 1998 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "pixman-private.h" #define PIXREGION_NIL(reg) ((reg)->data && !(reg)->data->numRects) /* not a region */ #define PIXREGION_NAR(reg) ((reg)->data == pixman_broken_data) #define PIXREGION_NUMRECTS(reg) ((reg)->data ? (reg)->data->numRects : 1) #define PIXREGION_SIZE(reg) ((reg)->data ? (reg)->data->size : 0) #define PIXREGION_RECTS(reg) \ ((reg)->data ? (box_type_t *)((reg)->data + 1) \ : &(reg)->extents) #define PIXREGION_BOXPTR(reg) ((box_type_t *)((reg)->data + 1)) #define PIXREGION_BOX(reg, i) (&PIXREGION_BOXPTR (reg)[i]) #define PIXREGION_TOP(reg) PIXREGION_BOX (reg, (reg)->data->numRects) #define PIXREGION_END(reg) PIXREGION_BOX (reg, (reg)->data->numRects - 1) #define GOOD_RECT(rect) ((rect)->x1 < (rect)->x2 && (rect)->y1 < (rect)->y2) #define BAD_RECT(rect) ((rect)->x1 > (rect)->x2 || (rect)->y1 > (rect)->y2) #ifdef DEBUG #define GOOD(reg) \ do \ { \ if (!PREFIX (_selfcheck (reg))) \ _pixman_log_error (FUNC, "Malformed region " # reg); \ } while (0) #else #define GOOD(reg) #endif static const box_type_t PREFIX (_empty_box_) = { 0, 0, 0, 0 }; static const region_data_type_t PREFIX (_empty_data_) = { 0, 0 }; #if defined (__llvm__) && !defined (__clang__) static const volatile region_data_type_t PREFIX (_broken_data_) = { 0, 0 }; #else static const region_data_type_t PREFIX (_broken_data_) = { 0, 0 }; #endif static box_type_t *pixman_region_empty_box = (box_type_t *)&PREFIX (_empty_box_); static region_data_type_t *pixman_region_empty_data = (region_data_type_t *)&PREFIX (_empty_data_); static region_data_type_t *pixman_broken_data = (region_data_type_t *)&PREFIX (_broken_data_); static pixman_bool_t pixman_break (region_type_t *region); /* * The functions in this file implement the Region abstraction used extensively * throughout the X11 sample server. A Region is simply a set of disjoint * (non-overlapping) rectangles, plus an "extent" rectangle which is the * smallest single rectangle that contains all the non-overlapping rectangles. * * A Region is implemented as a "y-x-banded" array of rectangles. This array * imposes two degrees of order. First, all rectangles are sorted by top side * y coordinate first (y1), and then by left side x coordinate (x1). * * Furthermore, the rectangles are grouped into "bands". Each rectangle in a * band has the same top y coordinate (y1), and each has the same bottom y * coordinate (y2). Thus all rectangles in a band differ only in their left * and right side (x1 and x2). Bands are implicit in the array of rectangles: * there is no separate list of band start pointers. * * The y-x band representation does not minimize rectangles. In particular, * if a rectangle vertically crosses a band (the rectangle has scanlines in * the y1 to y2 area spanned by the band), then the rectangle may be broken * down into two or more smaller rectangles stacked one atop the other. * * ----------- ----------- * | | | | band 0 * | | -------- ----------- -------- * | | | | in y-x banded | | | | band 1 * | | | | form is | | | | * ----------- | | ----------- -------- * | | | | band 2 * -------- -------- * * An added constraint on the rectangles is that they must cover as much * horizontal area as possible: no two rectangles within a band are allowed * to touch. * * Whenever possible, bands will be merged together to cover a greater vertical * distance (and thus reduce the number of rectangles). Two bands can be merged * only if the bottom of one touches the top of the other and they have * rectangles in the same places (of the same width, of course). * * Adam de Boor wrote most of the original region code. Joel McCormack * substantially modified or rewrote most of the core arithmetic routines, and * added pixman_region_validate in order to support several speed improvements * to pixman_region_validate_tree. Bob Scheifler changed the representation * to be more compact when empty or a single rectangle, and did a bunch of * gratuitous reformatting. Carl Worth did further gratuitous reformatting * while re-merging the server and client region code into libpixregion. * Soren Sandmann did even more gratuitous reformatting. */ /* true iff two Boxes overlap */ #define EXTENTCHECK(r1, r2) \ (!( ((r1)->x2 <= (r2)->x1) || \ ((r1)->x1 >= (r2)->x2) || \ ((r1)->y2 <= (r2)->y1) || \ ((r1)->y1 >= (r2)->y2) ) ) /* true iff (x,y) is in Box */ #define INBOX(r, x, y) \ ( ((r)->x2 > x) && \ ((r)->x1 <= x) && \ ((r)->y2 > y) && \ ((r)->y1 <= y) ) /* true iff Box r1 contains Box r2 */ #define SUBSUMES(r1, r2) \ ( ((r1)->x1 <= (r2)->x1) && \ ((r1)->x2 >= (r2)->x2) && \ ((r1)->y1 <= (r2)->y1) && \ ((r1)->y2 >= (r2)->y2) ) static size_t PIXREGION_SZOF (size_t n) { size_t size = n * sizeof(box_type_t); if (n > UINT32_MAX / sizeof(box_type_t)) return 0; if (sizeof(region_data_type_t) > UINT32_MAX - size) return 0; return size + sizeof(region_data_type_t); } static region_data_type_t * alloc_data (size_t n) { size_t sz = PIXREGION_SZOF (n); if (!sz) return NULL; return malloc (sz); } #define FREE_DATA(reg) if ((reg)->data && (reg)->data->size) free ((reg)->data) #define RECTALLOC_BAIL(region, n, bail) \ do \ { \ if (!(region)->data || \ (((region)->data->numRects + (n)) > (region)->data->size)) \ { \ if (!pixman_rect_alloc (region, n)) \ goto bail; \ } \ } while (0) #define RECTALLOC(region, n) \ do \ { \ if (!(region)->data || \ (((region)->data->numRects + (n)) > (region)->data->size)) \ { \ if (!pixman_rect_alloc (region, n)) { \ return FALSE; \ } \ } \ } while (0) #define ADDRECT(next_rect, nx1, ny1, nx2, ny2) \ do \ { \ next_rect->x1 = nx1; \ next_rect->y1 = ny1; \ next_rect->x2 = nx2; \ next_rect->y2 = ny2; \ next_rect++; \ } \ while (0) #define NEWRECT(region, next_rect, nx1, ny1, nx2, ny2) \ do \ { \ if (!(region)->data || \ ((region)->data->numRects == (region)->data->size)) \ { \ if (!pixman_rect_alloc (region, 1)) \ return FALSE; \ next_rect = PIXREGION_TOP (region); \ } \ ADDRECT (next_rect, nx1, ny1, nx2, ny2); \ region->data->numRects++; \ critical_if_fail (region->data->numRects <= region->data->size); \ } while (0) #define DOWNSIZE(reg, numRects) \ do \ { \ if (((numRects) < ((reg)->data->size >> 1)) && \ ((reg)->data->size > 50)) \ { \ region_data_type_t * new_data; \ size_t data_size = PIXREGION_SZOF (numRects); \ \ if (!data_size) \ { \ new_data = NULL; \ } \ else \ { \ new_data = (region_data_type_t *) \ realloc ((reg)->data, data_size); \ } \ \ if (new_data) \ { \ new_data->size = (numRects); \ (reg)->data = new_data; \ } \ } \ } while (0) PIXMAN_EXPORT pixman_bool_t PREFIX (_equal) (region_type_t *reg1, region_type_t *reg2) { int i; box_type_t *rects1; box_type_t *rects2; if (reg1->extents.x1 != reg2->extents.x1) return FALSE; if (reg1->extents.x2 != reg2->extents.x2) return FALSE; if (reg1->extents.y1 != reg2->extents.y1) return FALSE; if (reg1->extents.y2 != reg2->extents.y2) return FALSE; if (PIXREGION_NUMRECTS (reg1) != PIXREGION_NUMRECTS (reg2)) return FALSE; rects1 = PIXREGION_RECTS (reg1); rects2 = PIXREGION_RECTS (reg2); for (i = 0; i != PIXREGION_NUMRECTS (reg1); i++) { if (rects1[i].x1 != rects2[i].x1) return FALSE; if (rects1[i].x2 != rects2[i].x2) return FALSE; if (rects1[i].y1 != rects2[i].y1) return FALSE; if (rects1[i].y2 != rects2[i].y2) return FALSE; } return TRUE; } int PREFIX (_print) (region_type_t *rgn) { int num, size; int i; box_type_t * rects; num = PIXREGION_NUMRECTS (rgn); size = PIXREGION_SIZE (rgn); rects = PIXREGION_RECTS (rgn); fprintf (stderr, "num: %d size: %d\n", num, size); fprintf (stderr, "extents: %d %d %d %d\n", rgn->extents.x1, rgn->extents.y1, rgn->extents.x2, rgn->extents.y2); for (i = 0; i < num; i++) { fprintf (stderr, "%d %d %d %d \n", rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2); } fprintf (stderr, "\n"); return(num); } PIXMAN_EXPORT void PREFIX (_init) (region_type_t *region) { region->extents = *pixman_region_empty_box; region->data = pixman_region_empty_data; } PIXMAN_EXPORT void PREFIX (_init_rect) (region_type_t * region, int x, int y, unsigned int width, unsigned int height) { region->extents.x1 = x; region->extents.y1 = y; region->extents.x2 = x + width; region->extents.y2 = y + height; if (!GOOD_RECT (®ion->extents)) { if (BAD_RECT (®ion->extents)) _pixman_log_error (FUNC, "Invalid rectangle passed"); PREFIX (_init) (region); return; } region->data = NULL; } PIXMAN_EXPORT void PREFIX (_init_with_extents) (region_type_t *region, box_type_t *extents) { if (!GOOD_RECT (extents)) { if (BAD_RECT (extents)) _pixman_log_error (FUNC, "Invalid rectangle passed"); PREFIX (_init) (region); return; } region->extents = *extents; region->data = NULL; } PIXMAN_EXPORT void PREFIX (_fini) (region_type_t *region) { GOOD (region); FREE_DATA (region); } PIXMAN_EXPORT int PREFIX (_n_rects) (region_type_t *region) { return PIXREGION_NUMRECTS (region); } PIXMAN_EXPORT box_type_t * PREFIX (_rectangles) (region_type_t *region, int *n_rects) { if (n_rects) *n_rects = PIXREGION_NUMRECTS (region); return PIXREGION_RECTS (region); } static pixman_bool_t pixman_break (region_type_t *region) { FREE_DATA (region); region->extents = *pixman_region_empty_box; region->data = pixman_broken_data; return FALSE; } static pixman_bool_t pixman_rect_alloc (region_type_t * region, int n) { region_data_type_t *data; if (!region->data) { n++; region->data = alloc_data (n); if (!region->data) return pixman_break (region); region->data->numRects = 1; *PIXREGION_BOXPTR (region) = region->extents; } else if (!region->data->size) { region->data = alloc_data (n); if (!region->data) return pixman_break (region); region->data->numRects = 0; } else { size_t data_size; if (n == 1) { n = region->data->numRects; if (n > 500) /* XXX pick numbers out of a hat */ n = 250; } n += region->data->numRects; data_size = PIXREGION_SZOF (n); if (!data_size) { data = NULL; } else { data = (region_data_type_t *) realloc (region->data, PIXREGION_SZOF (n)); } if (!data) return pixman_break (region); region->data = data; } region->data->size = n; return TRUE; } PIXMAN_EXPORT pixman_bool_t PREFIX (_copy) (region_type_t *dst, region_type_t *src) { GOOD (dst); GOOD (src); if (dst == src) return TRUE; dst->extents = src->extents; if (!src->data || !src->data->size) { FREE_DATA (dst); dst->data = src->data; return TRUE; } if (!dst->data || (dst->data->size < src->data->numRects)) { FREE_DATA (dst); dst->data = alloc_data (src->data->numRects); if (!dst->data) return pixman_break (dst); dst->data->size = src->data->numRects; } dst->data->numRects = src->data->numRects; memmove ((char *)PIXREGION_BOXPTR (dst), (char *)PIXREGION_BOXPTR (src), dst->data->numRects * sizeof(box_type_t)); return TRUE; } /*====================================================================== * Generic Region Operator *====================================================================*/ /*- *----------------------------------------------------------------------- * pixman_coalesce -- * Attempt to merge the boxes in the current band with those in the * previous one. We are guaranteed that the current band extends to * the end of the rects array. Used only by pixman_op. * * Results: * The new index for the previous band. * * Side Effects: * If coalescing takes place: * - rectangles in the previous band will have their y2 fields * altered. * - region->data->numRects will be decreased. * *----------------------------------------------------------------------- */ static inline int pixman_coalesce (region_type_t * region, /* Region to coalesce */ int prev_start, /* Index of start of previous band */ int cur_start) /* Index of start of current band */ { box_type_t *prev_box; /* Current box in previous band */ box_type_t *cur_box; /* Current box in current band */ int numRects; /* Number rectangles in both bands */ int y2; /* Bottom of current band */ /* * Figure out how many rectangles are in the band. */ numRects = cur_start - prev_start; critical_if_fail (numRects == region->data->numRects - cur_start); if (!numRects) return cur_start; /* * The bands may only be coalesced if the bottom of the previous * matches the top scanline of the current. */ prev_box = PIXREGION_BOX (region, prev_start); cur_box = PIXREGION_BOX (region, cur_start); if (prev_box->y2 != cur_box->y1) return cur_start; /* * Make sure the bands have boxes in the same places. This * assumes that boxes have been added in such a way that they * cover the most area possible. I.e. two boxes in a band must * have some horizontal space between them. */ y2 = cur_box->y2; do { if ((prev_box->x1 != cur_box->x1) || (prev_box->x2 != cur_box->x2)) return (cur_start); prev_box++; cur_box++; numRects--; } while (numRects); /* * The bands may be merged, so set the bottom y of each box * in the previous band to the bottom y of the current band. */ numRects = cur_start - prev_start; region->data->numRects -= numRects; do { prev_box--; prev_box->y2 = y2; numRects--; } while (numRects); return prev_start; } /* Quicky macro to avoid trivial reject procedure calls to pixman_coalesce */ #define COALESCE(new_reg, prev_band, cur_band) \ do \ { \ if (cur_band - prev_band == new_reg->data->numRects - cur_band) \ prev_band = pixman_coalesce (new_reg, prev_band, cur_band); \ else \ prev_band = cur_band; \ } while (0) /*- *----------------------------------------------------------------------- * pixman_region_append_non_o -- * Handle a non-overlapping band for the union and subtract operations. * Just adds the (top/bottom-clipped) rectangles into the region. * Doesn't have to check for subsumption or anything. * * Results: * None. * * Side Effects: * region->data->numRects is incremented and the rectangles overwritten * with the rectangles we're passed. * *----------------------------------------------------------------------- */ static inline pixman_bool_t pixman_region_append_non_o (region_type_t * region, box_type_t * r, box_type_t * r_end, int y1, int y2) { box_type_t *next_rect; int new_rects; new_rects = r_end - r; critical_if_fail (y1 < y2); critical_if_fail (new_rects != 0); /* Make sure we have enough space for all rectangles to be added */ RECTALLOC (region, new_rects); next_rect = PIXREGION_TOP (region); region->data->numRects += new_rects; do { critical_if_fail (r->x1 < r->x2); ADDRECT (next_rect, r->x1, y1, r->x2, y2); r++; } while (r != r_end); return TRUE; } #define FIND_BAND(r, r_band_end, r_end, ry1) \ do \ { \ ry1 = r->y1; \ r_band_end = r + 1; \ while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) { \ r_band_end++; \ } \ } while (0) #define APPEND_REGIONS(new_reg, r, r_end) \ do \ { \ int new_rects; \ if ((new_rects = r_end - r)) { \ RECTALLOC_BAIL (new_reg, new_rects, bail); \ memmove ((char *)PIXREGION_TOP (new_reg), (char *)r, \ new_rects * sizeof(box_type_t)); \ new_reg->data->numRects += new_rects; \ } \ } while (0) /*- *----------------------------------------------------------------------- * pixman_op -- * Apply an operation to two regions. Called by pixman_region_union, pixman_region_inverse, * pixman_region_subtract, pixman_region_intersect.... Both regions MUST have at least one * rectangle, and cannot be the same object. * * Results: * TRUE if successful. * * Side Effects: * The new region is overwritten. * overlap set to TRUE if overlap_func ever returns TRUE. * * Notes: * The idea behind this function is to view the two regions as sets. * Together they cover a rectangle of area that this function divides * into horizontal bands where points are covered only by one region * or by both. For the first case, the non_overlap_func is called with * each the band and the band's upper and lower extents. For the * second, the overlap_func is called to process the entire band. It * is responsible for clipping the rectangles in the band, though * this function provides the boundaries. * At the end of each band, the new region is coalesced, if possible, * to reduce the number of rectangles in the region. * *----------------------------------------------------------------------- */ typedef pixman_bool_t (*overlap_proc_ptr) (region_type_t *region, box_type_t * r1, box_type_t * r1_end, box_type_t * r2, box_type_t * r2_end, int y1, int y2); static pixman_bool_t pixman_op (region_type_t * new_reg, /* Place to store result */ region_type_t * reg1, /* First region in operation */ region_type_t * reg2, /* 2d region in operation */ overlap_proc_ptr overlap_func, /* Function to call for over- * lapping bands */ int append_non1, /* Append non-overlapping bands * in region 1 ? */ int append_non2 /* Append non-overlapping bands * in region 2 ? */ ) { box_type_t *r1; /* Pointer into first region */ box_type_t *r2; /* Pointer into 2d region */ box_type_t *r1_end; /* End of 1st region */ box_type_t *r2_end; /* End of 2d region */ int ybot; /* Bottom of intersection */ int ytop; /* Top of intersection */ region_data_type_t *old_data; /* Old data for new_reg */ int prev_band; /* Index of start of * previous band in new_reg */ int cur_band; /* Index of start of current * band in new_reg */ box_type_t * r1_band_end; /* End of current band in r1 */ box_type_t * r2_band_end; /* End of current band in r2 */ int top; /* Top of non-overlapping band */ int bot; /* Bottom of non-overlapping band*/ int r1y1; /* Temps for r1->y1 and r2->y1 */ int r2y1; int new_size; int numRects; /* * Break any region computed from a broken region */ if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2)) return pixman_break (new_reg); /* * Initialization: * set r1, r2, r1_end and r2_end appropriately, save the rectangles * of the destination region until the end in case it's one of * the two source regions, then mark the "new" region empty, allocating * another array of rectangles for it to use. */ r1 = PIXREGION_RECTS (reg1); new_size = PIXREGION_NUMRECTS (reg1); r1_end = r1 + new_size; numRects = PIXREGION_NUMRECTS (reg2); r2 = PIXREGION_RECTS (reg2); r2_end = r2 + numRects; critical_if_fail (r1 != r1_end); critical_if_fail (r2 != r2_end); old_data = (region_data_type_t *)NULL; if (((new_reg == reg1) && (new_size > 1)) || ((new_reg == reg2) && (numRects > 1))) { old_data = new_reg->data; new_reg->data = pixman_region_empty_data; } /* guess at new size */ if (numRects > new_size) new_size = numRects; new_size <<= 1; if (!new_reg->data) new_reg->data = pixman_region_empty_data; else if (new_reg->data->size) new_reg->data->numRects = 0; if (new_size > new_reg->data->size) { if (!pixman_rect_alloc (new_reg, new_size)) { free (old_data); return FALSE; } } /* * Initialize ybot. * In the upcoming loop, ybot and ytop serve different functions depending * on whether the band being handled is an overlapping or non-overlapping * band. * In the case of a non-overlapping band (only one of the regions * has points in the band), ybot is the bottom of the most recent * intersection and thus clips the top of the rectangles in that band. * ytop is the top of the next intersection between the two regions and * serves to clip the bottom of the rectangles in the current band. * For an overlapping band (where the two regions intersect), ytop clips * the top of the rectangles of both regions and ybot clips the bottoms. */ ybot = MIN (r1->y1, r2->y1); /* * prev_band serves to mark the start of the previous band so rectangles * can be coalesced into larger rectangles. qv. pixman_coalesce, above. * In the beginning, there is no previous band, so prev_band == cur_band * (cur_band is set later on, of course, but the first band will always * start at index 0). prev_band and cur_band must be indices because of * the possible expansion, and resultant moving, of the new region's * array of rectangles. */ prev_band = 0; do { /* * This algorithm proceeds one source-band (as opposed to a * destination band, which is determined by where the two regions * intersect) at a time. r1_band_end and r2_band_end serve to mark the * rectangle after the last one in the current band for their * respective regions. */ critical_if_fail (r1 != r1_end); critical_if_fail (r2 != r2_end); FIND_BAND (r1, r1_band_end, r1_end, r1y1); FIND_BAND (r2, r2_band_end, r2_end, r2y1); /* * First handle the band that doesn't intersect, if any. * * Note that attention is restricted to one band in the * non-intersecting region at once, so if a region has n * bands between the current position and the next place it overlaps * the other, this entire loop will be passed through n times. */ if (r1y1 < r2y1) { if (append_non1) { top = MAX (r1y1, ybot); bot = MIN (r1->y2, r2y1); if (top != bot) { cur_band = new_reg->data->numRects; if (!pixman_region_append_non_o (new_reg, r1, r1_band_end, top, bot)) goto bail; COALESCE (new_reg, prev_band, cur_band); } } ytop = r2y1; } else if (r2y1 < r1y1) { if (append_non2) { top = MAX (r2y1, ybot); bot = MIN (r2->y2, r1y1); if (top != bot) { cur_band = new_reg->data->numRects; if (!pixman_region_append_non_o (new_reg, r2, r2_band_end, top, bot)) goto bail; COALESCE (new_reg, prev_band, cur_band); } } ytop = r1y1; } else { ytop = r1y1; } /* * Now see if we've hit an intersecting band. The two bands only * intersect if ybot > ytop */ ybot = MIN (r1->y2, r2->y2); if (ybot > ytop) { cur_band = new_reg->data->numRects; if (!(*overlap_func)(new_reg, r1, r1_band_end, r2, r2_band_end, ytop, ybot)) { goto bail; } COALESCE (new_reg, prev_band, cur_band); } /* * If we've finished with a band (y2 == ybot) we skip forward * in the region to the next band. */ if (r1->y2 == ybot) r1 = r1_band_end; if (r2->y2 == ybot) r2 = r2_band_end; } while (r1 != r1_end && r2 != r2_end); /* * Deal with whichever region (if any) still has rectangles left. * * We only need to worry about banding and coalescing for the very first * band left. After that, we can just group all remaining boxes, * regardless of how many bands, into one final append to the list. */ if ((r1 != r1_end) && append_non1) { /* Do first non_overlap1Func call, which may be able to coalesce */ FIND_BAND (r1, r1_band_end, r1_end, r1y1); cur_band = new_reg->data->numRects; if (!pixman_region_append_non_o (new_reg, r1, r1_band_end, MAX (r1y1, ybot), r1->y2)) { goto bail; } COALESCE (new_reg, prev_band, cur_band); /* Just append the rest of the boxes */ APPEND_REGIONS (new_reg, r1_band_end, r1_end); } else if ((r2 != r2_end) && append_non2) { /* Do first non_overlap2Func call, which may be able to coalesce */ FIND_BAND (r2, r2_band_end, r2_end, r2y1); cur_band = new_reg->data->numRects; if (!pixman_region_append_non_o (new_reg, r2, r2_band_end, MAX (r2y1, ybot), r2->y2)) { goto bail; } COALESCE (new_reg, prev_band, cur_band); /* Append rest of boxes */ APPEND_REGIONS (new_reg, r2_band_end, r2_end); } free (old_data); if (!(numRects = new_reg->data->numRects)) { FREE_DATA (new_reg); new_reg->data = pixman_region_empty_data; } else if (numRects == 1) { new_reg->extents = *PIXREGION_BOXPTR (new_reg); FREE_DATA (new_reg); new_reg->data = (region_data_type_t *)NULL; } else { DOWNSIZE (new_reg, numRects); } return TRUE; bail: free (old_data); return pixman_break (new_reg); } /*- *----------------------------------------------------------------------- * pixman_set_extents -- * Reset the extents of a region to what they should be. Called by * pixman_region_subtract and pixman_region_intersect as they can't * figure it out along the way or do so easily, as pixman_region_union can. * * Results: * None. * * Side Effects: * The region's 'extents' structure is overwritten. * *----------------------------------------------------------------------- */ static void pixman_set_extents (region_type_t *region) { box_type_t *box, *box_end; if (!region->data) return; if (!region->data->size) { region->extents.x2 = region->extents.x1; region->extents.y2 = region->extents.y1; return; } box = PIXREGION_BOXPTR (region); box_end = PIXREGION_END (region); /* * Since box is the first rectangle in the region, it must have the * smallest y1 and since box_end is the last rectangle in the region, * it must have the largest y2, because of banding. Initialize x1 and * x2 from box and box_end, resp., as good things to initialize them * to... */ region->extents.x1 = box->x1; region->extents.y1 = box->y1; region->extents.x2 = box_end->x2; region->extents.y2 = box_end->y2; critical_if_fail (region->extents.y1 < region->extents.y2); while (box <= box_end) { if (box->x1 < region->extents.x1) region->extents.x1 = box->x1; if (box->x2 > region->extents.x2) region->extents.x2 = box->x2; box++; } critical_if_fail (region->extents.x1 < region->extents.x2); } /*====================================================================== * Region Intersection *====================================================================*/ /*- *----------------------------------------------------------------------- * pixman_region_intersect_o -- * Handle an overlapping band for pixman_region_intersect. * * Results: * TRUE if successful. * * Side Effects: * Rectangles may be added to the region. * *----------------------------------------------------------------------- */ /*ARGSUSED*/ static pixman_bool_t pixman_region_intersect_o (region_type_t *region, box_type_t * r1, box_type_t * r1_end, box_type_t * r2, box_type_t * r2_end, int y1, int y2) { int x1; int x2; box_type_t * next_rect; next_rect = PIXREGION_TOP (region); critical_if_fail (y1 < y2); critical_if_fail (r1 != r1_end && r2 != r2_end); do { x1 = MAX (r1->x1, r2->x1); x2 = MIN (r1->x2, r2->x2); /* * If there's any overlap between the two rectangles, add that * overlap to the new region. */ if (x1 < x2) NEWRECT (region, next_rect, x1, y1, x2, y2); /* * Advance the pointer(s) with the leftmost right side, since the next * rectangle on that list may still overlap the other region's * current rectangle. */ if (r1->x2 == x2) { r1++; } if (r2->x2 == x2) { r2++; } } while ((r1 != r1_end) && (r2 != r2_end)); return TRUE; } PIXMAN_EXPORT pixman_bool_t PREFIX (_intersect) (region_type_t * new_reg, region_type_t * reg1, region_type_t * reg2) { GOOD (reg1); GOOD (reg2); GOOD (new_reg); /* check for trivial reject */ if (PIXREGION_NIL (reg1) || PIXREGION_NIL (reg2) || !EXTENTCHECK (®1->extents, ®2->extents)) { /* Covers about 20% of all cases */ FREE_DATA (new_reg); new_reg->extents.x2 = new_reg->extents.x1; new_reg->extents.y2 = new_reg->extents.y1; if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2)) { new_reg->data = pixman_broken_data; return FALSE; } else { new_reg->data = pixman_region_empty_data; } } else if (!reg1->data && !reg2->data) { /* Covers about 80% of cases that aren't trivially rejected */ new_reg->extents.x1 = MAX (reg1->extents.x1, reg2->extents.x1); new_reg->extents.y1 = MAX (reg1->extents.y1, reg2->extents.y1); new_reg->extents.x2 = MIN (reg1->extents.x2, reg2->extents.x2); new_reg->extents.y2 = MIN (reg1->extents.y2, reg2->extents.y2); FREE_DATA (new_reg); new_reg->data = (region_data_type_t *)NULL; } else if (!reg2->data && SUBSUMES (®2->extents, ®1->extents)) { return PREFIX (_copy) (new_reg, reg1); } else if (!reg1->data && SUBSUMES (®1->extents, ®2->extents)) { return PREFIX (_copy) (new_reg, reg2); } else if (reg1 == reg2) { return PREFIX (_copy) (new_reg, reg1); } else { /* General purpose intersection */ if (!pixman_op (new_reg, reg1, reg2, pixman_region_intersect_o, FALSE, FALSE)) return FALSE; pixman_set_extents (new_reg); } GOOD (new_reg); return(TRUE); } #define MERGERECT(r) \ do \ { \ if (r->x1 <= x2) \ { \ /* Merge with current rectangle */ \ if (x2 < r->x2) \ x2 = r->x2; \ } \ else \ { \ /* Add current rectangle, start new one */ \ NEWRECT (region, next_rect, x1, y1, x2, y2); \ x1 = r->x1; \ x2 = r->x2; \ } \ r++; \ } while (0) /*====================================================================== * Region Union *====================================================================*/ /*- *----------------------------------------------------------------------- * pixman_region_union_o -- * Handle an overlapping band for the union operation. Picks the * left-most rectangle each time and merges it into the region. * * Results: * TRUE if successful. * * Side Effects: * region is overwritten. * overlap is set to TRUE if any boxes overlap. * *----------------------------------------------------------------------- */ static pixman_bool_t pixman_region_union_o (region_type_t *region, box_type_t * r1, box_type_t * r1_end, box_type_t * r2, box_type_t * r2_end, int y1, int y2) { box_type_t *next_rect; int x1; /* left and right side of current union */ int x2; critical_if_fail (y1 < y2); critical_if_fail (r1 != r1_end && r2 != r2_end); next_rect = PIXREGION_TOP (region); /* Start off current rectangle */ if (r1->x1 < r2->x1) { x1 = r1->x1; x2 = r1->x2; r1++; } else { x1 = r2->x1; x2 = r2->x2; r2++; } while (r1 != r1_end && r2 != r2_end) { if (r1->x1 < r2->x1) MERGERECT (r1); else MERGERECT (r2); } /* Finish off whoever (if any) is left */ if (r1 != r1_end) { do { MERGERECT (r1); } while (r1 != r1_end); } else if (r2 != r2_end) { do { MERGERECT (r2); } while (r2 != r2_end); } /* Add current rectangle */ NEWRECT (region, next_rect, x1, y1, x2, y2); return TRUE; } PIXMAN_EXPORT pixman_bool_t PREFIX(_intersect_rect) (region_type_t *dest, region_type_t *source, int x, int y, unsigned int width, unsigned int height) { region_type_t region; region.data = NULL; region.extents.x1 = x; region.extents.y1 = y; region.extents.x2 = x + width; region.extents.y2 = y + height; return PREFIX(_intersect) (dest, source, ®ion); } /* Convenience function for performing union of region with a * single rectangle */ PIXMAN_EXPORT pixman_bool_t PREFIX (_union_rect) (region_type_t *dest, region_type_t *source, int x, int y, unsigned int width, unsigned int height) { region_type_t region; region.extents.x1 = x; region.extents.y1 = y; region.extents.x2 = x + width; region.extents.y2 = y + height; if (!GOOD_RECT (®ion.extents)) { if (BAD_RECT (®ion.extents)) _pixman_log_error (FUNC, "Invalid rectangle passed"); return PREFIX (_copy) (dest, source); } region.data = NULL; return PREFIX (_union) (dest, source, ®ion); } PIXMAN_EXPORT pixman_bool_t PREFIX (_union) (region_type_t *new_reg, region_type_t *reg1, region_type_t *reg2) { /* Return TRUE if some overlap * between reg1, reg2 */ GOOD (reg1); GOOD (reg2); GOOD (new_reg); /* checks all the simple cases */ /* * Region 1 and 2 are the same */ if (reg1 == reg2) return PREFIX (_copy) (new_reg, reg1); /* * Region 1 is empty */ if (PIXREGION_NIL (reg1)) { if (PIXREGION_NAR (reg1)) return pixman_break (new_reg); if (new_reg != reg2) return PREFIX (_copy) (new_reg, reg2); return TRUE; } /* * Region 2 is empty */ if (PIXREGION_NIL (reg2)) { if (PIXREGION_NAR (reg2)) return pixman_break (new_reg); if (new_reg != reg1) return PREFIX (_copy) (new_reg, reg1); return TRUE; } /* * Region 1 completely subsumes region 2 */ if (!reg1->data && SUBSUMES (®1->extents, ®2->extents)) { if (new_reg != reg1) return PREFIX (_copy) (new_reg, reg1); return TRUE; } /* * Region 2 completely subsumes region 1 */ if (!reg2->data && SUBSUMES (®2->extents, ®1->extents)) { if (new_reg != reg2) return PREFIX (_copy) (new_reg, reg2); return TRUE; } if (!pixman_op (new_reg, reg1, reg2, pixman_region_union_o, TRUE, TRUE)) return FALSE; new_reg->extents.x1 = MIN (reg1->extents.x1, reg2->extents.x1); new_reg->extents.y1 = MIN (reg1->extents.y1, reg2->extents.y1); new_reg->extents.x2 = MAX (reg1->extents.x2, reg2->extents.x2); new_reg->extents.y2 = MAX (reg1->extents.y2, reg2->extents.y2); GOOD (new_reg); return TRUE; } /*====================================================================== * Batch Rectangle Union *====================================================================*/ #define EXCHANGE_RECTS(a, b) \ { \ box_type_t t; \ t = rects[a]; \ rects[a] = rects[b]; \ rects[b] = t; \ } static void quick_sort_rects ( box_type_t rects[], int numRects) { int y1; int x1; int i, j; box_type_t *r; /* Always called with numRects > 1 */ do { if (numRects == 2) { if (rects[0].y1 > rects[1].y1 || (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1)) { EXCHANGE_RECTS (0, 1); } return; } /* Choose partition element, stick in location 0 */ EXCHANGE_RECTS (0, numRects >> 1); y1 = rects[0].y1; x1 = rects[0].x1; /* Partition array */ i = 0; j = numRects; do { r = &(rects[i]); do { r++; i++; } while (i != numRects && (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1))); r = &(rects[j]); do { r--; j--; } while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1)); if (i < j) EXCHANGE_RECTS (i, j); } while (i < j); /* Move partition element back to middle */ EXCHANGE_RECTS (0, j); /* Recurse */ if (numRects - j - 1 > 1) quick_sort_rects (&rects[j + 1], numRects - j - 1); numRects = j; } while (numRects > 1); } /*- *----------------------------------------------------------------------- * pixman_region_validate -- * * Take a ``region'' which is a non-y-x-banded random collection of * rectangles, and compute a nice region which is the union of all the * rectangles. * * Results: * TRUE if successful. * * Side Effects: * The passed-in ``region'' may be modified. * overlap set to TRUE if any retangles overlapped, * else FALSE; * * Strategy: * Step 1. Sort the rectangles into ascending order with primary key y1 * and secondary key x1. * * Step 2. Split the rectangles into the minimum number of proper y-x * banded regions. This may require horizontally merging * rectangles, and vertically coalescing bands. With any luck, * this step in an identity transformation (ala the Box widget), * or a coalescing into 1 box (ala Menus). * * Step 3. Merge the separate regions down to a single region by calling * pixman_region_union. Maximize the work each pixman_region_union call does by using * a binary merge. * *----------------------------------------------------------------------- */ static pixman_bool_t validate (region_type_t * badreg) { /* Descriptor for regions under construction in Step 2. */ typedef struct { region_type_t reg; int prev_band; int cur_band; } region_info_t; region_info_t stack_regions[64]; int numRects; /* Original numRects for badreg */ region_info_t *ri; /* Array of current regions */ int num_ri; /* Number of entries used in ri */ int size_ri; /* Number of entries available in ri */ int i; /* Index into rects */ int j; /* Index into ri */ region_info_t *rit; /* &ri[j] */ region_type_t *reg; /* ri[j].reg */ box_type_t *box; /* Current box in rects */ box_type_t *ri_box; /* Last box in ri[j].reg */ region_type_t *hreg; /* ri[j_half].reg */ pixman_bool_t ret = TRUE; if (!badreg->data) { GOOD (badreg); return TRUE; } numRects = badreg->data->numRects; if (!numRects) { if (PIXREGION_NAR (badreg)) return FALSE; GOOD (badreg); return TRUE; } if (badreg->extents.x1 < badreg->extents.x2) { if ((numRects) == 1) { FREE_DATA (badreg); badreg->data = (region_data_type_t *) NULL; } else { DOWNSIZE (badreg, numRects); } GOOD (badreg); return TRUE; } /* Step 1: Sort the rects array into ascending (y1, x1) order */ quick_sort_rects (PIXREGION_BOXPTR (badreg), numRects); /* Step 2: Scatter the sorted array into the minimum number of regions */ /* Set up the first region to be the first rectangle in badreg */ /* Note that step 2 code will never overflow the ri[0].reg rects array */ ri = stack_regions; size_ri = sizeof (stack_regions) / sizeof (stack_regions[0]); num_ri = 1; ri[0].prev_band = 0; ri[0].cur_band = 0; ri[0].reg = *badreg; box = PIXREGION_BOXPTR (&ri[0].reg); ri[0].reg.extents = *box; ri[0].reg.data->numRects = 1; badreg->extents = *pixman_region_empty_box; badreg->data = pixman_region_empty_data; /* Now scatter rectangles into the minimum set of valid regions. If the * next rectangle to be added to a region would force an existing rectangle * in the region to be split up in order to maintain y-x banding, just * forget it. Try the next region. If it doesn't fit cleanly into any * region, make a new one. */ for (i = numRects; --i > 0;) { box++; /* Look for a region to append box to */ for (j = num_ri, rit = ri; --j >= 0; rit++) { reg = &rit->reg; ri_box = PIXREGION_END (reg); if (box->y1 == ri_box->y1 && box->y2 == ri_box->y2) { /* box is in same band as ri_box. Merge or append it */ if (box->x1 <= ri_box->x2) { /* Merge it with ri_box */ if (box->x2 > ri_box->x2) ri_box->x2 = box->x2; } else { RECTALLOC_BAIL (reg, 1, bail); *PIXREGION_TOP (reg) = *box; reg->data->numRects++; } goto next_rect; /* So sue me */ } else if (box->y1 >= ri_box->y2) { /* Put box into new band */ if (reg->extents.x2 < ri_box->x2) reg->extents.x2 = ri_box->x2; if (reg->extents.x1 > box->x1) reg->extents.x1 = box->x1; COALESCE (reg, rit->prev_band, rit->cur_band); rit->cur_band = reg->data->numRects; RECTALLOC_BAIL (reg, 1, bail); *PIXREGION_TOP (reg) = *box; reg->data->numRects++; goto next_rect; } /* Well, this region was inappropriate. Try the next one. */ } /* for j */ /* Uh-oh. No regions were appropriate. Create a new one. */ if (size_ri == num_ri) { size_t data_size; /* Oops, allocate space for new region information */ size_ri <<= 1; data_size = size_ri * sizeof(region_info_t); if (data_size / size_ri != sizeof(region_info_t)) goto bail; if (ri == stack_regions) { rit = malloc (data_size); if (!rit) goto bail; memcpy (rit, ri, num_ri * sizeof (region_info_t)); } else { rit = (region_info_t *) realloc (ri, data_size); if (!rit) goto bail; } ri = rit; rit = &ri[num_ri]; } num_ri++; rit->prev_band = 0; rit->cur_band = 0; rit->reg.extents = *box; rit->reg.data = (region_data_type_t *)NULL; /* MUST force allocation */ if (!pixman_rect_alloc (&rit->reg, (i + num_ri) / num_ri)) goto bail; next_rect: ; } /* for i */ /* Make a final pass over each region in order to COALESCE and set * extents.x2 and extents.y2 */ for (j = num_ri, rit = ri; --j >= 0; rit++) { reg = &rit->reg; ri_box = PIXREGION_END (reg); reg->extents.y2 = ri_box->y2; if (reg->extents.x2 < ri_box->x2) reg->extents.x2 = ri_box->x2; COALESCE (reg, rit->prev_band, rit->cur_band); if (reg->data->numRects == 1) /* keep unions happy below */ { FREE_DATA (reg); reg->data = (region_data_type_t *)NULL; } } /* Step 3: Union all regions into a single region */ while (num_ri > 1) { int half = num_ri / 2; for (j = num_ri & 1; j < (half + (num_ri & 1)); j++) { reg = &ri[j].reg; hreg = &ri[j + half].reg; if (!pixman_op (reg, reg, hreg, pixman_region_union_o, TRUE, TRUE)) ret = FALSE; if (hreg->extents.x1 < reg->extents.x1) reg->extents.x1 = hreg->extents.x1; if (hreg->extents.y1 < reg->extents.y1) reg->extents.y1 = hreg->extents.y1; if (hreg->extents.x2 > reg->extents.x2) reg->extents.x2 = hreg->extents.x2; if (hreg->extents.y2 > reg->extents.y2) reg->extents.y2 = hreg->extents.y2; FREE_DATA (hreg); } num_ri -= half; if (!ret) goto bail; } *badreg = ri[0].reg; if (ri != stack_regions) free (ri); GOOD (badreg); return ret; bail: for (i = 0; i < num_ri; i++) FREE_DATA (&ri[i].reg); if (ri != stack_regions) free (ri); return pixman_break (badreg); } /*====================================================================== * Region Subtraction *====================================================================*/ /*- *----------------------------------------------------------------------- * pixman_region_subtract_o -- * Overlapping band subtraction. x1 is the left-most point not yet * checked. * * Results: * TRUE if successful. * * Side Effects: * region may have rectangles added to it. * *----------------------------------------------------------------------- */ /*ARGSUSED*/ static pixman_bool_t pixman_region_subtract_o (region_type_t * region, box_type_t * r1, box_type_t * r1_end, box_type_t * r2, box_type_t * r2_end, int y1, int y2) { box_type_t * next_rect; int x1; x1 = r1->x1; critical_if_fail (y1 < y2); critical_if_fail (r1 != r1_end && r2 != r2_end); next_rect = PIXREGION_TOP (region); do { if (r2->x2 <= x1) { /* * Subtrahend entirely to left of minuend: go to next subtrahend. */ r2++; } else if (r2->x1 <= x1) { /* * Subtrahend precedes minuend: nuke left edge of minuend. */ x1 = r2->x2; if (x1 >= r1->x2) { /* * Minuend completely covered: advance to next minuend and * reset left fence to edge of new minuend. */ r1++; if (r1 != r1_end) x1 = r1->x1; } else { /* * Subtrahend now used up since it doesn't extend beyond * minuend */ r2++; } } else if (r2->x1 < r1->x2) { /* * Left part of subtrahend covers part of minuend: add uncovered * part of minuend to region and skip to next subtrahend. */ critical_if_fail (x1 < r2->x1); NEWRECT (region, next_rect, x1, y1, r2->x1, y2); x1 = r2->x2; if (x1 >= r1->x2) { /* * Minuend used up: advance to new... */ r1++; if (r1 != r1_end) x1 = r1->x1; } else { /* * Subtrahend used up */ r2++; } } else { /* * Minuend used up: add any remaining piece before advancing. */ if (r1->x2 > x1) NEWRECT (region, next_rect, x1, y1, r1->x2, y2); r1++; if (r1 != r1_end) x1 = r1->x1; } } while ((r1 != r1_end) && (r2 != r2_end)); /* * Add remaining minuend rectangles to region. */ while (r1 != r1_end) { critical_if_fail (x1 < r1->x2); NEWRECT (region, next_rect, x1, y1, r1->x2, y2); r1++; if (r1 != r1_end) x1 = r1->x1; } return TRUE; } /*- *----------------------------------------------------------------------- * pixman_region_subtract -- * Subtract reg_s from reg_m and leave the result in reg_d. * S stands for subtrahend, M for minuend and D for difference. * * Results: * TRUE if successful. * * Side Effects: * reg_d is overwritten. * *----------------------------------------------------------------------- */ PIXMAN_EXPORT pixman_bool_t PREFIX (_subtract) (region_type_t *reg_d, region_type_t *reg_m, region_type_t *reg_s) { GOOD (reg_m); GOOD (reg_s); GOOD (reg_d); /* check for trivial rejects */ if (PIXREGION_NIL (reg_m) || PIXREGION_NIL (reg_s) || !EXTENTCHECK (®_m->extents, ®_s->extents)) { if (PIXREGION_NAR (reg_s)) return pixman_break (reg_d); return PREFIX (_copy) (reg_d, reg_m); } else if (reg_m == reg_s) { FREE_DATA (reg_d); reg_d->extents.x2 = reg_d->extents.x1; reg_d->extents.y2 = reg_d->extents.y1; reg_d->data = pixman_region_empty_data; return TRUE; } /* Add those rectangles in region 1 that aren't in region 2, do yucky subtraction for overlaps, and just throw away rectangles in region 2 that aren't in region 1 */ if (!pixman_op (reg_d, reg_m, reg_s, pixman_region_subtract_o, TRUE, FALSE)) return FALSE; /* * Can't alter reg_d's extents before we call pixman_op because * it might be one of the source regions and pixman_op depends * on the extents of those regions being unaltered. Besides, this * way there's no checking against rectangles that will be nuked * due to coalescing, so we have to examine fewer rectangles. */ pixman_set_extents (reg_d); GOOD (reg_d); return TRUE; } /*====================================================================== * Region Inversion *====================================================================*/ /*- *----------------------------------------------------------------------- * pixman_region_inverse -- * Take a region and a box and return a region that is everything * in the box but not in the region. The careful reader will note * that this is the same as subtracting the region from the box... * * Results: * TRUE. * * Side Effects: * new_reg is overwritten. * *----------------------------------------------------------------------- */ PIXMAN_EXPORT pixman_bool_t PREFIX (_inverse) (region_type_t *new_reg, /* Destination region */ region_type_t *reg1, /* Region to invert */ box_type_t * inv_rect) /* Bounding box for inversion */ { region_type_t inv_reg; /* Quick and dirty region made from the * bounding box */ GOOD (reg1); GOOD (new_reg); /* check for trivial rejects */ if (PIXREGION_NIL (reg1) || !EXTENTCHECK (inv_rect, ®1->extents)) { if (PIXREGION_NAR (reg1)) return pixman_break (new_reg); new_reg->extents = *inv_rect; FREE_DATA (new_reg); new_reg->data = (region_data_type_t *)NULL; return TRUE; } /* Add those rectangles in region 1 that aren't in region 2, * do yucky subtraction for overlaps, and * just throw away rectangles in region 2 that aren't in region 1 */ inv_reg.extents = *inv_rect; inv_reg.data = (region_data_type_t *)NULL; if (!pixman_op (new_reg, &inv_reg, reg1, pixman_region_subtract_o, TRUE, FALSE)) return FALSE; /* * Can't alter new_reg's extents before we call pixman_op because * it might be one of the source regions and pixman_op depends * on the extents of those regions being unaltered. Besides, this * way there's no checking against rectangles that will be nuked * due to coalescing, so we have to examine fewer rectangles. */ pixman_set_extents (new_reg); GOOD (new_reg); return TRUE; } /* In time O(log n), locate the first box whose y2 is greater than y. * Return @end if no such box exists. */ static box_type_t * find_box_for_y (box_type_t *begin, box_type_t *end, int y) { box_type_t *mid; if (end == begin) return end; if (end - begin == 1) { if (begin->y2 > y) return begin; else return end; } mid = begin + (end - begin) / 2; if (mid->y2 > y) { /* If no box is found in [begin, mid], the function * will return @mid, which is then known to be the * correct answer. */ return find_box_for_y (begin, mid, y); } else { return find_box_for_y (mid, end, y); } } /* * rect_in(region, rect) * This routine takes a pointer to a region and a pointer to a box * and determines if the box is outside/inside/partly inside the region. * * The idea is to travel through the list of rectangles trying to cover the * passed box with them. Anytime a piece of the rectangle isn't covered * by a band of rectangles, part_out is set TRUE. Any time a rectangle in * the region covers part of the box, part_in is set TRUE. The process ends * when either the box has been completely covered (we reached a band that * doesn't overlap the box, part_in is TRUE and part_out is false), the * box has been partially covered (part_in == part_out == TRUE -- because of * the banding, the first time this is true we know the box is only * partially in the region) or is outside the region (we reached a band * that doesn't overlap the box at all and part_in is false) */ PIXMAN_EXPORT pixman_region_overlap_t PREFIX (_contains_rectangle) (region_type_t * region, box_type_t * prect) { box_type_t * pbox; box_type_t * pbox_end; int part_in, part_out; int numRects; int x, y; GOOD (region); numRects = PIXREGION_NUMRECTS (region); /* useful optimization */ if (!numRects || !EXTENTCHECK (®ion->extents, prect)) return(PIXMAN_REGION_OUT); if (numRects == 1) { /* We know that it must be PIXMAN_REGION_IN or PIXMAN_REGION_PART */ if (SUBSUMES (®ion->extents, prect)) return(PIXMAN_REGION_IN); else return(PIXMAN_REGION_PART); } part_out = FALSE; part_in = FALSE; /* (x,y) starts at upper left of rect, moving to the right and down */ x = prect->x1; y = prect->y1; /* can stop when both part_out and part_in are TRUE, or we reach prect->y2 */ for (pbox = PIXREGION_BOXPTR (region), pbox_end = pbox + numRects; pbox != pbox_end; pbox++) { /* getting up to speed or skipping remainder of band */ if (pbox->y2 <= y) { if ((pbox = find_box_for_y (pbox, pbox_end, y)) == pbox_end) break; } if (pbox->y1 > y) { part_out = TRUE; /* missed part of rectangle above */ if (part_in || (pbox->y1 >= prect->y2)) break; y = pbox->y1; /* x guaranteed to be == prect->x1 */ } if (pbox->x2 <= x) continue; /* not far enough over yet */ if (pbox->x1 > x) { part_out = TRUE; /* missed part of rectangle to left */ if (part_in) break; } if (pbox->x1 < prect->x2) { part_in = TRUE; /* definitely overlap */ if (part_out) break; } if (pbox->x2 >= prect->x2) { y = pbox->y2; /* finished with this band */ if (y >= prect->y2) break; x = prect->x1; /* reset x out to left again */ } else { /* * Because boxes in a band are maximal width, if the first box * to overlap the rectangle doesn't completely cover it in that * band, the rectangle must be partially out, since some of it * will be uncovered in that band. part_in will have been set true * by now... */ part_out = TRUE; break; } } if (part_in) { if (y < prect->y2) return PIXMAN_REGION_PART; else return PIXMAN_REGION_IN; } else { return PIXMAN_REGION_OUT; } } /* PREFIX(_translate) (region, x, y) * translates in place */ PIXMAN_EXPORT void PREFIX (_translate) (region_type_t *region, int x, int y) { overflow_int_t x1, x2, y1, y2; int nbox; box_type_t * pbox; GOOD (region); region->extents.x1 = x1 = region->extents.x1 + x; region->extents.y1 = y1 = region->extents.y1 + y; region->extents.x2 = x2 = region->extents.x2 + x; region->extents.y2 = y2 = region->extents.y2 + y; if (((x1 - PIXMAN_REGION_MIN) | (y1 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x2) | (PIXMAN_REGION_MAX - y2)) >= 0) { if (region->data && (nbox = region->data->numRects)) { for (pbox = PIXREGION_BOXPTR (region); nbox--; pbox++) { pbox->x1 += x; pbox->y1 += y; pbox->x2 += x; pbox->y2 += y; } } return; } if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0) { region->extents.x2 = region->extents.x1; region->extents.y2 = region->extents.y1; FREE_DATA (region); region->data = pixman_region_empty_data; return; } if (x1 < PIXMAN_REGION_MIN) region->extents.x1 = PIXMAN_REGION_MIN; else if (x2 > PIXMAN_REGION_MAX) region->extents.x2 = PIXMAN_REGION_MAX; if (y1 < PIXMAN_REGION_MIN) region->extents.y1 = PIXMAN_REGION_MIN; else if (y2 > PIXMAN_REGION_MAX) region->extents.y2 = PIXMAN_REGION_MAX; if (region->data && (nbox = region->data->numRects)) { box_type_t * pbox_out; for (pbox_out = pbox = PIXREGION_BOXPTR (region); nbox--; pbox++) { pbox_out->x1 = x1 = pbox->x1 + x; pbox_out->y1 = y1 = pbox->y1 + y; pbox_out->x2 = x2 = pbox->x2 + x; pbox_out->y2 = y2 = pbox->y2 + y; if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0) { region->data->numRects--; continue; } if (x1 < PIXMAN_REGION_MIN) pbox_out->x1 = PIXMAN_REGION_MIN; else if (x2 > PIXMAN_REGION_MAX) pbox_out->x2 = PIXMAN_REGION_MAX; if (y1 < PIXMAN_REGION_MIN) pbox_out->y1 = PIXMAN_REGION_MIN; else if (y2 > PIXMAN_REGION_MAX) pbox_out->y2 = PIXMAN_REGION_MAX; pbox_out++; } if (pbox_out != pbox) { if (region->data->numRects == 1) { region->extents = *PIXREGION_BOXPTR (region); FREE_DATA (region); region->data = (region_data_type_t *)NULL; } else { pixman_set_extents (region); } } } GOOD (region); } PIXMAN_EXPORT void PREFIX (_reset) (region_type_t *region, box_type_t *box) { GOOD (region); critical_if_fail (GOOD_RECT (box)); region->extents = *box; FREE_DATA (region); region->data = NULL; } PIXMAN_EXPORT void PREFIX (_clear) (region_type_t *region) { GOOD (region); FREE_DATA (region); region->extents = *pixman_region_empty_box; region->data = pixman_region_empty_data; } /* box is "return" value */ PIXMAN_EXPORT int PREFIX (_contains_point) (region_type_t * region, int x, int y, box_type_t * box) { box_type_t *pbox, *pbox_end; int numRects; GOOD (region); numRects = PIXREGION_NUMRECTS (region); if (!numRects || !INBOX (®ion->extents, x, y)) return(FALSE); if (numRects == 1) { if (box) *box = region->extents; return(TRUE); } pbox = PIXREGION_BOXPTR (region); pbox_end = pbox + numRects; pbox = find_box_for_y (pbox, pbox_end, y); for (;pbox != pbox_end; pbox++) { if ((y < pbox->y1) || (x < pbox->x1)) break; /* missed it */ if (x >= pbox->x2) continue; /* not there yet */ if (box) *box = *pbox; return(TRUE); } return(FALSE); } PIXMAN_EXPORT int PREFIX (_not_empty) (region_type_t * region) { GOOD (region); return(!PIXREGION_NIL (region)); } PIXMAN_EXPORT box_type_t * PREFIX (_extents) (region_type_t * region) { GOOD (region); return(®ion->extents); } /* * Clip a list of scanlines to a region. The caller has allocated the * space. FSorted is non-zero if the scanline origins are in ascending order. * * returns the number of new, clipped scanlines. */ PIXMAN_EXPORT pixman_bool_t PREFIX (_selfcheck) (region_type_t *reg) { int i, numRects; if ((reg->extents.x1 > reg->extents.x2) || (reg->extents.y1 > reg->extents.y2)) { return FALSE; } numRects = PIXREGION_NUMRECTS (reg); if (!numRects) { return ((reg->extents.x1 == reg->extents.x2) && (reg->extents.y1 == reg->extents.y2) && (reg->data->size || (reg->data == pixman_region_empty_data))); } else if (numRects == 1) { return (!reg->data); } else { box_type_t * pbox_p, * pbox_n; box_type_t box; pbox_p = PIXREGION_RECTS (reg); box = *pbox_p; box.y2 = pbox_p[numRects - 1].y2; pbox_n = pbox_p + 1; for (i = numRects; --i > 0; pbox_p++, pbox_n++) { if ((pbox_n->x1 >= pbox_n->x2) || (pbox_n->y1 >= pbox_n->y2)) { return FALSE; } if (pbox_n->x1 < box.x1) box.x1 = pbox_n->x1; if (pbox_n->x2 > box.x2) box.x2 = pbox_n->x2; if ((pbox_n->y1 < pbox_p->y1) || ((pbox_n->y1 == pbox_p->y1) && ((pbox_n->x1 < pbox_p->x2) || (pbox_n->y2 != pbox_p->y2)))) { return FALSE; } } return ((box.x1 == reg->extents.x1) && (box.x2 == reg->extents.x2) && (box.y1 == reg->extents.y1) && (box.y2 == reg->extents.y2)); } } PIXMAN_EXPORT pixman_bool_t PREFIX (_init_rects) (region_type_t *region, const box_type_t *boxes, int count) { box_type_t *rects; int displacement; int i; /* if it's 1, then we just want to set the extents, so call * the existing method. */ if (count == 1) { PREFIX (_init_rect) (region, boxes[0].x1, boxes[0].y1, boxes[0].x2 - boxes[0].x1, boxes[0].y2 - boxes[0].y1); return TRUE; } PREFIX (_init) (region); /* if it's 0, don't call pixman_rect_alloc -- 0 rectangles is * a special case, and causing pixman_rect_alloc would cause * us to leak memory (because the 0-rect case should be the * static pixman_region_empty_data data). */ if (count == 0) return TRUE; if (!pixman_rect_alloc (region, count)) return FALSE; rects = PIXREGION_RECTS (region); /* Copy in the rects */ memcpy (rects, boxes, sizeof(box_type_t) * count); region->data->numRects = count; /* Eliminate empty and malformed rectangles */ displacement = 0; for (i = 0; i < count; ++i) { box_type_t *box = &rects[i]; if (box->x1 >= box->x2 || box->y1 >= box->y2) displacement++; else if (displacement) rects[i - displacement] = rects[i]; } region->data->numRects -= displacement; /* If eliminating empty rectangles caused there * to be only 0 or 1 rectangles, deal with that. */ if (region->data->numRects == 0) { FREE_DATA (region); PREFIX (_init) (region); return TRUE; } if (region->data->numRects == 1) { region->extents = rects[0]; FREE_DATA (region); region->data = NULL; GOOD (region); return TRUE; } /* Validate */ region->extents.x1 = region->extents.x2 = 0; return validate (region); } #define READ(_ptr) (*(_ptr)) static inline box_type_t * bitmap_addrect (region_type_t *reg, box_type_t *r, box_type_t **first_rect, int rx1, int ry1, int rx2, int ry2) { if ((rx1 < rx2) && (ry1 < ry2) && (!(reg->data->numRects && ((r-1)->y1 == ry1) && ((r-1)->y2 == ry2) && ((r-1)->x1 <= rx1) && ((r-1)->x2 >= rx2)))) { if (reg->data->numRects == reg->data->size) { if (!pixman_rect_alloc (reg, 1)) return NULL; *first_rect = PIXREGION_BOXPTR(reg); r = *first_rect + reg->data->numRects; } r->x1 = rx1; r->y1 = ry1; r->x2 = rx2; r->y2 = ry2; reg->data->numRects++; if (r->x1 < reg->extents.x1) reg->extents.x1 = r->x1; if (r->x2 > reg->extents.x2) reg->extents.x2 = r->x2; r++; } return r; } /* Convert bitmap clip mask into clipping region. * First, goes through each line and makes boxes by noting the transitions * from 0 to 1 and 1 to 0. * Then it coalesces the current line with the previous if they have boxes * at the same X coordinates. * Stride is in number of uint32_t per line. */ PIXMAN_EXPORT void PREFIX (_init_from_image) (region_type_t *region, pixman_image_t *image) { uint32_t mask0 = 0xffffffff & ~SCREEN_SHIFT_RIGHT(0xffffffff, 1); box_type_t *first_rect, *rects, *prect_line_start; box_type_t *old_rect, *new_rect; uint32_t *pw, w, *pw_line, *pw_line_end; int irect_prev_start, irect_line_start; int h, base, rx1 = 0, crects; int ib; pixman_bool_t in_box, same; int width, height, stride; PREFIX(_init) (region); critical_if_fail (region->data); return_if_fail (image->type == BITS); return_if_fail (image->bits.format == PIXMAN_a1); pw_line = pixman_image_get_data (image); width = pixman_image_get_width (image); height = pixman_image_get_height (image); stride = pixman_image_get_stride (image) / 4; first_rect = PIXREGION_BOXPTR(region); rects = first_rect; region->extents.x1 = width - 1; region->extents.x2 = 0; irect_prev_start = -1; for (h = 0; h < height; h++) { pw = pw_line; pw_line += stride; irect_line_start = rects - first_rect; /* If the Screen left most bit of the word is set, we're starting in * a box */ if (READ(pw) & mask0) { in_box = TRUE; rx1 = 0; } else { in_box = FALSE; } /* Process all words which are fully in the pixmap */ pw_line_end = pw + (width >> 5); for (base = 0; pw < pw_line_end; base += 32) { w = READ(pw++); if (in_box) { if (!~w) continue; } else { if (!w) continue; } for (ib = 0; ib < 32; ib++) { /* If the Screen left most bit of the word is set, we're * starting a box */ if (w & mask0) { if (!in_box) { rx1 = base + ib; /* start new box */ in_box = TRUE; } } else { if (in_box) { /* end box */ rects = bitmap_addrect (region, rects, &first_rect, rx1, h, base + ib, h + 1); if (rects == NULL) goto error; in_box = FALSE; } } /* Shift the word VISUALLY left one. */ w = SCREEN_SHIFT_LEFT(w, 1); } } if (width & 31) { /* Process final partial word on line */ w = READ(pw++); for (ib = 0; ib < (width & 31); ib++) { /* If the Screen left most bit of the word is set, we're * starting a box */ if (w & mask0) { if (!in_box) { rx1 = base + ib; /* start new box */ in_box = TRUE; } } else { if (in_box) { /* end box */ rects = bitmap_addrect(region, rects, &first_rect, rx1, h, base + ib, h + 1); if (rects == NULL) goto error; in_box = FALSE; } } /* Shift the word VISUALLY left one. */ w = SCREEN_SHIFT_LEFT(w, 1); } } /* If scanline ended with last bit set, end the box */ if (in_box) { rects = bitmap_addrect(region, rects, &first_rect, rx1, h, base + (width & 31), h + 1); if (rects == NULL) goto error; } /* if all rectangles on this line have the same x-coords as * those on the previous line, then add 1 to all the previous y2s and * throw away all the rectangles from this line */ same = FALSE; if (irect_prev_start != -1) { crects = irect_line_start - irect_prev_start; if (crects != 0 && crects == ((rects - first_rect) - irect_line_start)) { old_rect = first_rect + irect_prev_start; new_rect = prect_line_start = first_rect + irect_line_start; same = TRUE; while (old_rect < prect_line_start) { if ((old_rect->x1 != new_rect->x1) || (old_rect->x2 != new_rect->x2)) { same = FALSE; break; } old_rect++; new_rect++; } if (same) { old_rect = first_rect + irect_prev_start; while (old_rect < prect_line_start) { old_rect->y2 += 1; old_rect++; } rects -= crects; region->data->numRects -= crects; } } } if(!same) irect_prev_start = irect_line_start; } if (!region->data->numRects) { region->extents.x1 = region->extents.x2 = 0; } else { region->extents.y1 = PIXREGION_BOXPTR(region)->y1; region->extents.y2 = PIXREGION_END(region)->y2; if (region->data->numRects == 1) { free (region->data); region->data = NULL; } } error: return; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-region16.c000066400000000000000000000044531271037650300263020ustar00rootroot00000000000000/* * Copyright © 2008 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * Red Hat, Inc. not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Red Hat, Inc. makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Soren Sandmann */ #ifdef HAVE_CONFIG_H #include #endif #undef PIXMAN_DISABLE_DEPRECATED #include "pixman-private.h" #include typedef pixman_box16_t box_type_t; typedef pixman_region16_data_t region_data_type_t; typedef pixman_region16_t region_type_t; typedef int32_t overflow_int_t; typedef struct { int x, y; } point_type_t; #define PREFIX(x) pixman_region##x #define PIXMAN_REGION_MAX INT16_MAX #define PIXMAN_REGION_MIN INT16_MIN #include "pixman-region.c" /* This function exists only to make it possible to preserve the X ABI - * it should go away at first opportunity. * * The problem is that the X ABI exports the three structs and has used * them through macros. So the X server calls this function with * the addresses of those structs which makes the existing code continue to * work. */ PIXMAN_EXPORT void pixman_region_set_static_pointers (pixman_box16_t *empty_box, pixman_region16_data_t *empty_data, pixman_region16_data_t *broken_data) { pixman_region_empty_box = empty_box; pixman_region_empty_data = empty_data; pixman_broken_data = broken_data; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-region32.c000066400000000000000000000032131271037650300262710ustar00rootroot00000000000000/* * Copyright © 2008 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * Red Hat, Inc. not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Red Hat, Inc. makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Soren Sandmann */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include typedef pixman_box32_t box_type_t; typedef pixman_region32_data_t region_data_type_t; typedef pixman_region32_t region_type_t; typedef int64_t overflow_int_t; typedef struct { int x, y; } point_type_t; #define PREFIX(x) pixman_region32##x #define PIXMAN_REGION_MAX INT32_MAX #define PIXMAN_REGION_MIN INT32_MIN #include "pixman-region.c" Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-solid-fill.c000066400000000000000000000041671271037650300267100ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007, 2009 Red Hat, Inc. * Copyright © 2009 Soren Sandmann * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" static uint32_t color_to_uint32 (const pixman_color_t *color) { return (color->alpha >> 8 << 24) | (color->red >> 8 << 16) | (color->green & 0xff00) | (color->blue >> 8); } static argb_t color_to_float (const pixman_color_t *color) { argb_t result; result.a = pixman_unorm_to_float (color->alpha, 16); result.r = pixman_unorm_to_float (color->red, 16); result.g = pixman_unorm_to_float (color->green, 16); result.b = pixman_unorm_to_float (color->blue, 16); return result; } PIXMAN_EXPORT pixman_image_t * pixman_image_create_solid_fill (const pixman_color_t *color) { pixman_image_t *img = _pixman_image_allocate (); if (!img) return NULL; img->type = SOLID; img->solid.color = *color; img->solid.color_32 = color_to_uint32 (color); img->solid.color_float = color_to_float (color); return img; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-sse2.c000066400000000000000000004715141271037650300255320ustar00rootroot00000000000000/* * Copyright © 2008 Rodrigo Kumpera * Copyright © 2008 André Tupinambá * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Rodrigo Kumpera (kumpera@gmail.com) * André Tupinambá (andrelrt@gmail.com) * * Based on work by Owen Taylor and Søren Sandmann */ #ifdef HAVE_CONFIG_H #include #endif #include /* for _mm_shuffle_pi16 and _MM_SHUFFLE */ #include /* for SSE2 intrinsics */ #include "pixman-private.h" #include "pixman-combine32.h" #include "pixman-inlines.h" static __m128i mask_0080; static __m128i mask_00ff; static __m128i mask_0101; static __m128i mask_ffff; static __m128i mask_ff000000; static __m128i mask_alpha; static __m128i mask_565_r; static __m128i mask_565_g1, mask_565_g2; static __m128i mask_565_b; static __m128i mask_red; static __m128i mask_green; static __m128i mask_blue; static __m128i mask_565_fix_rb; static __m128i mask_565_fix_g; static __m128i mask_565_rb; static __m128i mask_565_pack_multiplier; static force_inline __m128i unpack_32_1x128 (uint32_t data) { return _mm_unpacklo_epi8 (_mm_cvtsi32_si128 (data), _mm_setzero_si128 ()); } static force_inline void unpack_128_2x128 (__m128i data, __m128i* data_lo, __m128i* data_hi) { *data_lo = _mm_unpacklo_epi8 (data, _mm_setzero_si128 ()); *data_hi = _mm_unpackhi_epi8 (data, _mm_setzero_si128 ()); } static force_inline __m128i unpack_565_to_8888 (__m128i lo) { __m128i r, g, b, rb, t; r = _mm_and_si128 (_mm_slli_epi32 (lo, 8), mask_red); g = _mm_and_si128 (_mm_slli_epi32 (lo, 5), mask_green); b = _mm_and_si128 (_mm_slli_epi32 (lo, 3), mask_blue); rb = _mm_or_si128 (r, b); t = _mm_and_si128 (rb, mask_565_fix_rb); t = _mm_srli_epi32 (t, 5); rb = _mm_or_si128 (rb, t); t = _mm_and_si128 (g, mask_565_fix_g); t = _mm_srli_epi32 (t, 6); g = _mm_or_si128 (g, t); return _mm_or_si128 (rb, g); } static force_inline void unpack_565_128_4x128 (__m128i data, __m128i* data0, __m128i* data1, __m128i* data2, __m128i* data3) { __m128i lo, hi; lo = _mm_unpacklo_epi16 (data, _mm_setzero_si128 ()); hi = _mm_unpackhi_epi16 (data, _mm_setzero_si128 ()); lo = unpack_565_to_8888 (lo); hi = unpack_565_to_8888 (hi); unpack_128_2x128 (lo, data0, data1); unpack_128_2x128 (hi, data2, data3); } static force_inline uint16_t pack_565_32_16 (uint32_t pixel) { return (uint16_t) (((pixel >> 8) & 0xf800) | ((pixel >> 5) & 0x07e0) | ((pixel >> 3) & 0x001f)); } static force_inline __m128i pack_2x128_128 (__m128i lo, __m128i hi) { return _mm_packus_epi16 (lo, hi); } static force_inline __m128i pack_565_2packedx128_128 (__m128i lo, __m128i hi) { __m128i rb0 = _mm_and_si128 (lo, mask_565_rb); __m128i rb1 = _mm_and_si128 (hi, mask_565_rb); __m128i t0 = _mm_madd_epi16 (rb0, mask_565_pack_multiplier); __m128i t1 = _mm_madd_epi16 (rb1, mask_565_pack_multiplier); __m128i g0 = _mm_and_si128 (lo, mask_green); __m128i g1 = _mm_and_si128 (hi, mask_green); t0 = _mm_or_si128 (t0, g0); t1 = _mm_or_si128 (t1, g1); /* Simulates _mm_packus_epi32 */ t0 = _mm_slli_epi32 (t0, 16 - 5); t1 = _mm_slli_epi32 (t1, 16 - 5); t0 = _mm_srai_epi32 (t0, 16); t1 = _mm_srai_epi32 (t1, 16); return _mm_packs_epi32 (t0, t1); } static force_inline __m128i pack_565_2x128_128 (__m128i lo, __m128i hi) { __m128i data; __m128i r, g1, g2, b; data = pack_2x128_128 (lo, hi); r = _mm_and_si128 (data, mask_565_r); g1 = _mm_and_si128 (_mm_slli_epi32 (data, 3), mask_565_g1); g2 = _mm_and_si128 (_mm_srli_epi32 (data, 5), mask_565_g2); b = _mm_and_si128 (_mm_srli_epi32 (data, 3), mask_565_b); return _mm_or_si128 (_mm_or_si128 (_mm_or_si128 (r, g1), g2), b); } static force_inline __m128i pack_565_4x128_128 (__m128i* xmm0, __m128i* xmm1, __m128i* xmm2, __m128i* xmm3) { return _mm_packus_epi16 (pack_565_2x128_128 (*xmm0, *xmm1), pack_565_2x128_128 (*xmm2, *xmm3)); } static force_inline int is_opaque (__m128i x) { __m128i ffs = _mm_cmpeq_epi8 (x, x); return (_mm_movemask_epi8 (_mm_cmpeq_epi8 (x, ffs)) & 0x8888) == 0x8888; } static force_inline int is_zero (__m128i x) { return _mm_movemask_epi8 ( _mm_cmpeq_epi8 (x, _mm_setzero_si128 ())) == 0xffff; } static force_inline int is_transparent (__m128i x) { return (_mm_movemask_epi8 ( _mm_cmpeq_epi8 (x, _mm_setzero_si128 ())) & 0x8888) == 0x8888; } static force_inline __m128i expand_pixel_32_1x128 (uint32_t data) { return _mm_shuffle_epi32 (unpack_32_1x128 (data), _MM_SHUFFLE (1, 0, 1, 0)); } static force_inline __m128i expand_alpha_1x128 (__m128i data) { return _mm_shufflehi_epi16 (_mm_shufflelo_epi16 (data, _MM_SHUFFLE (3, 3, 3, 3)), _MM_SHUFFLE (3, 3, 3, 3)); } static force_inline void expand_alpha_2x128 (__m128i data_lo, __m128i data_hi, __m128i* alpha_lo, __m128i* alpha_hi) { __m128i lo, hi; lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (3, 3, 3, 3)); hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (3, 3, 3, 3)); *alpha_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (3, 3, 3, 3)); *alpha_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (3, 3, 3, 3)); } static force_inline void expand_alpha_rev_2x128 (__m128i data_lo, __m128i data_hi, __m128i* alpha_lo, __m128i* alpha_hi) { __m128i lo, hi; lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (0, 0, 0, 0)); hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (0, 0, 0, 0)); *alpha_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (0, 0, 0, 0)); *alpha_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (0, 0, 0, 0)); } static force_inline void pix_multiply_2x128 (__m128i* data_lo, __m128i* data_hi, __m128i* alpha_lo, __m128i* alpha_hi, __m128i* ret_lo, __m128i* ret_hi) { __m128i lo, hi; lo = _mm_mullo_epi16 (*data_lo, *alpha_lo); hi = _mm_mullo_epi16 (*data_hi, *alpha_hi); lo = _mm_adds_epu16 (lo, mask_0080); hi = _mm_adds_epu16 (hi, mask_0080); *ret_lo = _mm_mulhi_epu16 (lo, mask_0101); *ret_hi = _mm_mulhi_epu16 (hi, mask_0101); } static force_inline void pix_add_multiply_2x128 (__m128i* src_lo, __m128i* src_hi, __m128i* alpha_dst_lo, __m128i* alpha_dst_hi, __m128i* dst_lo, __m128i* dst_hi, __m128i* alpha_src_lo, __m128i* alpha_src_hi, __m128i* ret_lo, __m128i* ret_hi) { __m128i t1_lo, t1_hi; __m128i t2_lo, t2_hi; pix_multiply_2x128 (src_lo, src_hi, alpha_dst_lo, alpha_dst_hi, &t1_lo, &t1_hi); pix_multiply_2x128 (dst_lo, dst_hi, alpha_src_lo, alpha_src_hi, &t2_lo, &t2_hi); *ret_lo = _mm_adds_epu8 (t1_lo, t2_lo); *ret_hi = _mm_adds_epu8 (t1_hi, t2_hi); } static force_inline void negate_2x128 (__m128i data_lo, __m128i data_hi, __m128i* neg_lo, __m128i* neg_hi) { *neg_lo = _mm_xor_si128 (data_lo, mask_00ff); *neg_hi = _mm_xor_si128 (data_hi, mask_00ff); } static force_inline void invert_colors_2x128 (__m128i data_lo, __m128i data_hi, __m128i* inv_lo, __m128i* inv_hi) { __m128i lo, hi; lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (3, 0, 1, 2)); hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (3, 0, 1, 2)); *inv_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (3, 0, 1, 2)); *inv_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (3, 0, 1, 2)); } static force_inline void over_2x128 (__m128i* src_lo, __m128i* src_hi, __m128i* alpha_lo, __m128i* alpha_hi, __m128i* dst_lo, __m128i* dst_hi) { __m128i t1, t2; negate_2x128 (*alpha_lo, *alpha_hi, &t1, &t2); pix_multiply_2x128 (dst_lo, dst_hi, &t1, &t2, dst_lo, dst_hi); *dst_lo = _mm_adds_epu8 (*src_lo, *dst_lo); *dst_hi = _mm_adds_epu8 (*src_hi, *dst_hi); } static force_inline void over_rev_non_pre_2x128 (__m128i src_lo, __m128i src_hi, __m128i* dst_lo, __m128i* dst_hi) { __m128i lo, hi; __m128i alpha_lo, alpha_hi; expand_alpha_2x128 (src_lo, src_hi, &alpha_lo, &alpha_hi); lo = _mm_or_si128 (alpha_lo, mask_alpha); hi = _mm_or_si128 (alpha_hi, mask_alpha); invert_colors_2x128 (src_lo, src_hi, &src_lo, &src_hi); pix_multiply_2x128 (&src_lo, &src_hi, &lo, &hi, &lo, &hi); over_2x128 (&lo, &hi, &alpha_lo, &alpha_hi, dst_lo, dst_hi); } static force_inline void in_over_2x128 (__m128i* src_lo, __m128i* src_hi, __m128i* alpha_lo, __m128i* alpha_hi, __m128i* mask_lo, __m128i* mask_hi, __m128i* dst_lo, __m128i* dst_hi) { __m128i s_lo, s_hi; __m128i a_lo, a_hi; pix_multiply_2x128 (src_lo, src_hi, mask_lo, mask_hi, &s_lo, &s_hi); pix_multiply_2x128 (alpha_lo, alpha_hi, mask_lo, mask_hi, &a_lo, &a_hi); over_2x128 (&s_lo, &s_hi, &a_lo, &a_hi, dst_lo, dst_hi); } /* load 4 pixels from a 16-byte boundary aligned address */ static force_inline __m128i load_128_aligned (__m128i* src) { return _mm_load_si128 (src); } /* load 4 pixels from a unaligned address */ static force_inline __m128i load_128_unaligned (const __m128i* src) { return _mm_loadu_si128 (src); } /* save 4 pixels using Write Combining memory on a 16-byte * boundary aligned address */ static force_inline void save_128_write_combining (__m128i* dst, __m128i data) { _mm_stream_si128 (dst, data); } /* save 4 pixels on a 16-byte boundary aligned address */ static force_inline void save_128_aligned (__m128i* dst, __m128i data) { _mm_store_si128 (dst, data); } /* save 4 pixels on a unaligned address */ static force_inline void save_128_unaligned (__m128i* dst, __m128i data) { _mm_storeu_si128 (dst, data); } static force_inline __m128i load_32_1x128 (uint32_t data) { return _mm_cvtsi32_si128 (data); } static force_inline __m128i expand_alpha_rev_1x128 (__m128i data) { return _mm_shufflelo_epi16 (data, _MM_SHUFFLE (0, 0, 0, 0)); } static force_inline __m128i expand_pixel_8_1x128 (uint8_t data) { return _mm_shufflelo_epi16 ( unpack_32_1x128 ((uint32_t)data), _MM_SHUFFLE (0, 0, 0, 0)); } static force_inline __m128i pix_multiply_1x128 (__m128i data, __m128i alpha) { return _mm_mulhi_epu16 (_mm_adds_epu16 (_mm_mullo_epi16 (data, alpha), mask_0080), mask_0101); } static force_inline __m128i pix_add_multiply_1x128 (__m128i* src, __m128i* alpha_dst, __m128i* dst, __m128i* alpha_src) { __m128i t1 = pix_multiply_1x128 (*src, *alpha_dst); __m128i t2 = pix_multiply_1x128 (*dst, *alpha_src); return _mm_adds_epu8 (t1, t2); } static force_inline __m128i negate_1x128 (__m128i data) { return _mm_xor_si128 (data, mask_00ff); } static force_inline __m128i invert_colors_1x128 (__m128i data) { return _mm_shufflelo_epi16 (data, _MM_SHUFFLE (3, 0, 1, 2)); } static force_inline __m128i over_1x128 (__m128i src, __m128i alpha, __m128i dst) { return _mm_adds_epu8 (src, pix_multiply_1x128 (dst, negate_1x128 (alpha))); } static force_inline __m128i in_over_1x128 (__m128i* src, __m128i* alpha, __m128i* mask, __m128i* dst) { return over_1x128 (pix_multiply_1x128 (*src, *mask), pix_multiply_1x128 (*alpha, *mask), *dst); } static force_inline __m128i over_rev_non_pre_1x128 (__m128i src, __m128i dst) { __m128i alpha = expand_alpha_1x128 (src); return over_1x128 (pix_multiply_1x128 (invert_colors_1x128 (src), _mm_or_si128 (alpha, mask_alpha)), alpha, dst); } static force_inline uint32_t pack_1x128_32 (__m128i data) { return _mm_cvtsi128_si32 (_mm_packus_epi16 (data, _mm_setzero_si128 ())); } static force_inline __m128i expand565_16_1x128 (uint16_t pixel) { __m128i m = _mm_cvtsi32_si128 (pixel); m = unpack_565_to_8888 (m); return _mm_unpacklo_epi8 (m, _mm_setzero_si128 ()); } static force_inline uint32_t core_combine_over_u_pixel_sse2 (uint32_t src, uint32_t dst) { uint8_t a; __m128i xmms; a = src >> 24; if (a == 0xff) { return src; } else if (src) { xmms = unpack_32_1x128 (src); return pack_1x128_32 ( over_1x128 (xmms, expand_alpha_1x128 (xmms), unpack_32_1x128 (dst))); } return dst; } static force_inline uint32_t combine1 (const uint32_t *ps, const uint32_t *pm) { uint32_t s = *ps; if (pm) { __m128i ms, mm; mm = unpack_32_1x128 (*pm); mm = expand_alpha_1x128 (mm); ms = unpack_32_1x128 (s); ms = pix_multiply_1x128 (ms, mm); s = pack_1x128_32 (ms); } return s; } static force_inline __m128i combine4 (const __m128i *ps, const __m128i *pm) { __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_msk_lo, xmm_msk_hi; __m128i s; if (pm) { xmm_msk_lo = load_128_unaligned (pm); if (is_transparent (xmm_msk_lo)) return _mm_setzero_si128 (); } s = load_128_unaligned (ps); if (pm) { unpack_128_2x128 (s, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_msk_lo, &xmm_msk_lo, &xmm_msk_hi); expand_alpha_2x128 (xmm_msk_lo, xmm_msk_hi, &xmm_msk_lo, &xmm_msk_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_msk_lo, &xmm_msk_hi, &xmm_src_lo, &xmm_src_hi); s = pack_2x128_128 (xmm_src_lo, xmm_src_hi); } return s; } static force_inline void core_combine_over_u_sse2_mask (uint32_t * pd, const uint32_t* ps, const uint32_t* pm, int w) { uint32_t s, d; /* Align dst on a 16-byte boundary */ while (w && ((uintptr_t)pd & 15)) { d = *pd; s = combine1 (ps, pm); if (s) *pd = core_combine_over_u_pixel_sse2 (s, d); pd++; ps++; pm++; w--; } while (w >= 4) { __m128i mask = load_128_unaligned ((__m128i *)pm); if (!is_zero (mask)) { __m128i src; __m128i src_hi, src_lo; __m128i mask_hi, mask_lo; __m128i alpha_hi, alpha_lo; src = load_128_unaligned ((__m128i *)ps); if (is_opaque (_mm_and_si128 (src, mask))) { save_128_aligned ((__m128i *)pd, src); } else { __m128i dst = load_128_aligned ((__m128i *)pd); __m128i dst_hi, dst_lo; unpack_128_2x128 (mask, &mask_lo, &mask_hi); unpack_128_2x128 (src, &src_lo, &src_hi); expand_alpha_2x128 (mask_lo, mask_hi, &mask_lo, &mask_hi); pix_multiply_2x128 (&src_lo, &src_hi, &mask_lo, &mask_hi, &src_lo, &src_hi); unpack_128_2x128 (dst, &dst_lo, &dst_hi); expand_alpha_2x128 (src_lo, src_hi, &alpha_lo, &alpha_hi); over_2x128 (&src_lo, &src_hi, &alpha_lo, &alpha_hi, &dst_lo, &dst_hi); save_128_aligned ( (__m128i *)pd, pack_2x128_128 (dst_lo, dst_hi)); } } pm += 4; ps += 4; pd += 4; w -= 4; } while (w) { d = *pd; s = combine1 (ps, pm); if (s) *pd = core_combine_over_u_pixel_sse2 (s, d); pd++; ps++; pm++; w--; } } static force_inline void core_combine_over_u_sse2_no_mask (uint32_t * pd, const uint32_t* ps, int w) { uint32_t s, d; /* Align dst on a 16-byte boundary */ while (w && ((uintptr_t)pd & 15)) { d = *pd; s = *ps; if (s) *pd = core_combine_over_u_pixel_sse2 (s, d); pd++; ps++; w--; } while (w >= 4) { __m128i src; __m128i src_hi, src_lo, dst_hi, dst_lo; __m128i alpha_hi, alpha_lo; src = load_128_unaligned ((__m128i *)ps); if (!is_zero (src)) { if (is_opaque (src)) { save_128_aligned ((__m128i *)pd, src); } else { __m128i dst = load_128_aligned ((__m128i *)pd); unpack_128_2x128 (src, &src_lo, &src_hi); unpack_128_2x128 (dst, &dst_lo, &dst_hi); expand_alpha_2x128 (src_lo, src_hi, &alpha_lo, &alpha_hi); over_2x128 (&src_lo, &src_hi, &alpha_lo, &alpha_hi, &dst_lo, &dst_hi); save_128_aligned ( (__m128i *)pd, pack_2x128_128 (dst_lo, dst_hi)); } } ps += 4; pd += 4; w -= 4; } while (w) { d = *pd; s = *ps; if (s) *pd = core_combine_over_u_pixel_sse2 (s, d); pd++; ps++; w--; } } static force_inline void sse2_combine_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { if (pm) core_combine_over_u_sse2_mask (pd, ps, pm, w); else core_combine_over_u_sse2_no_mask (pd, ps, w); } static void sse2_combine_over_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_alpha_lo, xmm_alpha_hi; /* Align dst on a 16-byte boundary */ while (w && ((uintptr_t)pd & 15)) { d = *pd; s = combine1 (ps, pm); *pd++ = core_combine_over_u_pixel_sse2 (d, s); w--; ps++; if (pm) pm++; } while (w >= 4) { /* I'm loading unaligned because I'm not sure * about the address alignment. */ xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi); over_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_src_lo, &xmm_src_hi); /* rebuid the 4 pixel data and save*/ save_128_aligned ((__m128i*)pd, pack_2x128_128 (xmm_src_lo, xmm_src_hi)); w -= 4; ps += 4; pd += 4; if (pm) pm += 4; } while (w) { d = *pd; s = combine1 (ps, pm); *pd++ = core_combine_over_u_pixel_sse2 (d, s); ps++; w--; if (pm) pm++; } } static force_inline uint32_t core_combine_in_u_pixel_sse2 (uint32_t src, uint32_t dst) { uint32_t maska = src >> 24; if (maska == 0) { return 0; } else if (maska != 0xff) { return pack_1x128_32 ( pix_multiply_1x128 (unpack_32_1x128 (dst), expand_alpha_1x128 (unpack_32_1x128 (src)))); } return dst; } static void sse2_combine_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; while (w && ((uintptr_t)pd & 15)) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_in_u_pixel_sse2 (d, s); w--; ps++; if (pm) pm++; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*) pd); xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*) pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_in_u_pixel_sse2 (d, s); w--; ps++; if (pm) pm++; } } static void sse2_combine_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; while (w && ((uintptr_t)pd & 15)) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_in_u_pixel_sse2 (s, d); ps++; w--; if (pm) pm++; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*) pd); xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*)pm); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_src_lo, &xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_in_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } } static void sse2_combine_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { while (w && ((uintptr_t)pd & 15)) { uint32_t s = combine1 (ps, pm); uint32_t d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), negate_1x128 ( expand_alpha_1x128 (unpack_32_1x128 (s))))); if (pm) pm++; ps++; w--; } while (w >= 4) { __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); negate_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_src_lo, &xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; if (pm) pm += 4; w -= 4; } while (w) { uint32_t s = combine1 (ps, pm); uint32_t d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), negate_1x128 ( expand_alpha_1x128 (unpack_32_1x128 (s))))); ps++; if (pm) pm++; w--; } } static void sse2_combine_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { while (w && ((uintptr_t)pd & 15)) { uint32_t s = combine1 (ps, pm); uint32_t d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (s), negate_1x128 ( expand_alpha_1x128 (unpack_32_1x128 (d))))); w--; ps++; if (pm) pm++; } while (w >= 4) { __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); negate_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { uint32_t s = combine1 (ps, pm); uint32_t d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (s), negate_1x128 ( expand_alpha_1x128 (unpack_32_1x128 (d))))); w--; ps++; if (pm) pm++; } } static force_inline uint32_t core_combine_atop_u_pixel_sse2 (uint32_t src, uint32_t dst) { __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i sa = negate_1x128 (expand_alpha_1x128 (s)); __m128i da = expand_alpha_1x128 (d); return pack_1x128_32 (pix_add_multiply_1x128 (&s, &da, &d, &sa)); } static void sse2_combine_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; while (w && ((uintptr_t)pd & 15)) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_atop_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } while (w >= 4) { xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); negate_2x128 (xmm_alpha_src_lo, xmm_alpha_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); pix_add_multiply_2x128 ( &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_atop_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } } static force_inline uint32_t core_combine_reverse_atop_u_pixel_sse2 (uint32_t src, uint32_t dst) { __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i sa = expand_alpha_1x128 (s); __m128i da = negate_1x128 (expand_alpha_1x128 (d)); return pack_1x128_32 (pix_add_multiply_1x128 (&s, &da, &d, &sa)); } static void sse2_combine_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; while (w && ((uintptr_t)pd & 15)) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_reverse_atop_u_pixel_sse2 (s, d); ps++; w--; if (pm) pm++; } while (w >= 4) { xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_add_multiply_2x128 ( &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_reverse_atop_u_pixel_sse2 (s, d); ps++; w--; if (pm) pm++; } } static force_inline uint32_t core_combine_xor_u_pixel_sse2 (uint32_t src, uint32_t dst) { __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i neg_d = negate_1x128 (expand_alpha_1x128 (d)); __m128i neg_s = negate_1x128 (expand_alpha_1x128 (s)); return pack_1x128_32 (pix_add_multiply_1x128 (&s, &neg_d, &d, &neg_s)); } static void sse2_combine_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dst, const uint32_t * src, const uint32_t * mask, int width) { int w = width; uint32_t s, d; uint32_t* pd = dst; const uint32_t* ps = src; const uint32_t* pm = mask; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; while (w && ((uintptr_t)pd & 15)) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_xor_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } while (w >= 4) { xmm_src = combine4 ((__m128i*) ps, (__m128i*) pm); xmm_dst = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); negate_2x128 (xmm_alpha_src_lo, xmm_alpha_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_add_multiply_2x128 ( &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; w -= 4; if (pm) pm += 4; } while (w) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_xor_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } } static force_inline void sse2_combine_add_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dst, const uint32_t * src, const uint32_t * mask, int width) { int w = width; uint32_t s, d; uint32_t* pd = dst; const uint32_t* ps = src; const uint32_t* pm = mask; while (w && (uintptr_t)pd & 15) { s = combine1 (ps, pm); d = *pd; ps++; if (pm) pm++; *pd++ = _mm_cvtsi128_si32 ( _mm_adds_epu8 (_mm_cvtsi32_si128 (s), _mm_cvtsi32_si128 (d))); w--; } while (w >= 4) { __m128i s; s = combine4 ((__m128i*)ps, (__m128i*)pm); save_128_aligned ( (__m128i*)pd, _mm_adds_epu8 (s, load_128_aligned ((__m128i*)pd))); pd += 4; ps += 4; if (pm) pm += 4; w -= 4; } while (w--) { s = combine1 (ps, pm); d = *pd; ps++; *pd++ = _mm_cvtsi128_si32 ( _mm_adds_epu8 (_mm_cvtsi32_si128 (s), _mm_cvtsi32_si128 (d))); if (pm) pm++; } } static force_inline uint32_t core_combine_saturate_u_pixel_sse2 (uint32_t src, uint32_t dst) { __m128i ms = unpack_32_1x128 (src); __m128i md = unpack_32_1x128 (dst); uint32_t sa = src >> 24; uint32_t da = ~dst >> 24; if (sa > da) { ms = pix_multiply_1x128 ( ms, expand_alpha_1x128 (unpack_32_1x128 (DIV_UN8 (da, sa) << 24))); } return pack_1x128_32 (_mm_adds_epu16 (md, ms)); } static void sse2_combine_saturate_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, d; uint32_t pack_cmp; __m128i xmm_src, xmm_dst; while (w && (uintptr_t)pd & 15) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); w--; ps++; if (pm) pm++; } while (w >= 4) { xmm_dst = load_128_aligned ((__m128i*)pd); xmm_src = combine4 ((__m128i*)ps, (__m128i*)pm); pack_cmp = _mm_movemask_epi8 ( _mm_cmpgt_epi32 ( _mm_srli_epi32 (xmm_src, 24), _mm_srli_epi32 (_mm_xor_si128 (xmm_dst, mask_ff000000), 24))); /* if some alpha src is grater than respective ~alpha dst */ if (pack_cmp) { s = combine1 (ps++, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); if (pm) pm++; s = combine1 (ps++, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); if (pm) pm++; s = combine1 (ps++, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); if (pm) pm++; s = combine1 (ps++, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); if (pm) pm++; } else { save_128_aligned ((__m128i*)pd, _mm_adds_epu8 (xmm_dst, xmm_src)); pd += 4; ps += 4; if (pm) pm += 4; } w -= 4; } while (w--) { s = combine1 (ps, pm); d = *pd; *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); ps++; if (pm) pm++; } } static void sse2_combine_src_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst_lo, xmm_dst_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; *pd++ = pack_1x128_32 ( pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m))); w--; } while (w >= 4) { xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; *pd++ = pack_1x128_32 ( pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m))); w--; } } static force_inline uint32_t core_combine_over_ca_pixel_sse2 (uint32_t src, uint32_t mask, uint32_t dst) { __m128i s = unpack_32_1x128 (src); __m128i expAlpha = expand_alpha_1x128 (s); __m128i unpk_mask = unpack_32_1x128 (mask); __m128i unpk_dst = unpack_32_1x128 (dst); return pack_1x128_32 (in_over_1x128 (&s, &expAlpha, &unpk_mask, &unpk_dst)); } static void sse2_combine_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_over_ca_pixel_sse2 (s, m, d); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_over_ca_pixel_sse2 (s, m, d); w--; } } static force_inline uint32_t core_combine_over_reverse_ca_pixel_sse2 (uint32_t src, uint32_t mask, uint32_t dst) { __m128i d = unpack_32_1x128 (dst); return pack_1x128_32 ( over_1x128 (d, expand_alpha_1x128 (d), pix_multiply_1x128 (unpack_32_1x128 (src), unpack_32_1x128 (mask)))); } static void sse2_combine_over_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_over_reverse_ca_pixel_sse2 (s, m, d); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); over_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask_lo, &xmm_mask_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_mask_lo, xmm_mask_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_over_reverse_ca_pixel_sse2 (s, m, d); w--; } } static void sse2_combine_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m)), expand_alpha_1x128 (unpack_32_1x128 (d)))); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (m)), expand_alpha_1x128 (unpack_32_1x128 (d)))); w--; } } static void sse2_combine_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), pix_multiply_1x128 (unpack_32_1x128 (m), expand_alpha_1x128 (unpack_32_1x128 (s))))); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), pix_multiply_1x128 (unpack_32_1x128 (m), expand_alpha_1x128 (unpack_32_1x128 (s))))); w--; } } static void sse2_combine_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (m)), negate_1x128 (expand_alpha_1x128 (unpack_32_1x128 (d))))); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi); negate_2x128 (xmm_alpha_lo, xmm_alpha_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (m)), negate_1x128 (expand_alpha_1x128 (unpack_32_1x128 (d))))); w--; } } static void sse2_combine_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), negate_1x128 (pix_multiply_1x128 ( unpack_32_1x128 (m), expand_alpha_1x128 (unpack_32_1x128 (s)))))); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask_lo, &xmm_mask_hi); negate_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (d), negate_1x128 (pix_multiply_1x128 ( unpack_32_1x128 (m), expand_alpha_1x128 (unpack_32_1x128 (s)))))); w--; } } static force_inline uint32_t core_combine_atop_ca_pixel_sse2 (uint32_t src, uint32_t mask, uint32_t dst) { __m128i m = unpack_32_1x128 (mask); __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i sa = expand_alpha_1x128 (s); __m128i da = expand_alpha_1x128 (d); s = pix_multiply_1x128 (s, m); m = negate_1x128 (pix_multiply_1x128 (m, sa)); return pack_1x128_32 (pix_add_multiply_1x128 (&d, &m, &s, &da)); } static void sse2_combine_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_atop_ca_pixel_sse2 (s, m, d); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_mask_lo, &xmm_mask_hi); negate_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_add_multiply_2x128 ( &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_atop_ca_pixel_sse2 (s, m, d); w--; } } static force_inline uint32_t core_combine_reverse_atop_ca_pixel_sse2 (uint32_t src, uint32_t mask, uint32_t dst) { __m128i m = unpack_32_1x128 (mask); __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i da = negate_1x128 (expand_alpha_1x128 (d)); __m128i sa = expand_alpha_1x128 (s); s = pix_multiply_1x128 (s, m); m = pix_multiply_1x128 (m, sa); return pack_1x128_32 (pix_add_multiply_1x128 (&d, &m, &s, &da)); } static void sse2_combine_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_reverse_atop_ca_pixel_sse2 (s, m, d); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_mask_lo, &xmm_mask_hi); negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_add_multiply_2x128 ( &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_reverse_atop_ca_pixel_sse2 (s, m, d); w--; } } static force_inline uint32_t core_combine_xor_ca_pixel_sse2 (uint32_t src, uint32_t mask, uint32_t dst) { __m128i a = unpack_32_1x128 (mask); __m128i s = unpack_32_1x128 (src); __m128i d = unpack_32_1x128 (dst); __m128i alpha_dst = negate_1x128 (pix_multiply_1x128 ( a, expand_alpha_1x128 (s))); __m128i dest = pix_multiply_1x128 (s, a); __m128i alpha_src = negate_1x128 (expand_alpha_1x128 (d)); return pack_1x128_32 (pix_add_multiply_1x128 (&d, &alpha_dst, &dest, &alpha_src)); } static void sse2_combine_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_xor_ca_pixel_sse2 (s, m, d); w--; } while (w >= 4) { xmm_dst_hi = load_128_aligned ((__m128i*)pd); xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, &xmm_mask_lo, &xmm_mask_hi); negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); negate_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_add_multiply_2x128 ( &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = core_combine_xor_ca_pixel_sse2 (s, m, d); w--; } } static void sse2_combine_add_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * pd, const uint32_t * ps, const uint32_t * pm, int w) { uint32_t s, m, d; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask_lo, xmm_mask_hi; while (w && (uintptr_t)pd & 15) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( _mm_adds_epu8 (pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } while (w >= 4) { xmm_src_hi = load_128_unaligned ((__m128i*)ps); xmm_mask_hi = load_128_unaligned ((__m128i*)pm); xmm_dst_hi = load_128_aligned ((__m128i*)pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_src_lo, &xmm_src_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 ( _mm_adds_epu8 (xmm_src_lo, xmm_dst_lo), _mm_adds_epu8 (xmm_src_hi, xmm_dst_hi))); ps += 4; pd += 4; pm += 4; w -= 4; } while (w) { s = *ps++; m = *pm++; d = *pd; *pd++ = pack_1x128_32 ( _mm_adds_epu8 (pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } } static force_inline __m128i create_mask_16_128 (uint16_t mask) { return _mm_set1_epi16 (mask); } /* Work around a code generation bug in Sun Studio 12. */ #if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) # define create_mask_2x32_128(mask0, mask1) \ (_mm_set_epi32 ((mask0), (mask1), (mask0), (mask1))) #else static force_inline __m128i create_mask_2x32_128 (uint32_t mask0, uint32_t mask1) { return _mm_set_epi32 (mask0, mask1, mask0, mask1); } #endif static void sse2_composite_over_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, *dst, d; int32_t w; int dst_stride; __m128i xmm_src, xmm_alpha; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); xmm_src = expand_pixel_32_1x128 (src); xmm_alpha = expand_alpha_1x128 (xmm_src); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; while (w && (uintptr_t)dst & 15) { d = *dst; *dst++ = pack_1x128_32 (over_1x128 (xmm_src, xmm_alpha, unpack_32_1x128 (d))); w--; } while (w >= 4) { xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_dst_lo, &xmm_dst_hi); /* rebuid the 4 pixel data and save*/ save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); w -= 4; dst += 4; } while (w) { d = *dst; *dst++ = pack_1x128_32 (over_1x128 (xmm_src, xmm_alpha, unpack_32_1x128 (d))); w--; } } } static void sse2_composite_over_n_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint16_t *dst_line, *dst, d; int32_t w; int dst_stride; __m128i xmm_src, xmm_alpha; __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); xmm_src = expand_pixel_32_1x128 (src); xmm_alpha = expand_alpha_1x128 (xmm_src); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; while (w && (uintptr_t)dst & 15) { d = *dst; *dst++ = pack_565_32_16 ( pack_1x128_32 (over_1x128 (xmm_src, xmm_alpha, expand565_16_1x128 (d)))); w--; } while (w >= 8) { xmm_dst = load_128_aligned ((__m128i*)dst); unpack_565_128_4x128 (xmm_dst, &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_dst0, &xmm_dst1); over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_dst2, &xmm_dst3); xmm_dst = pack_565_4x128_128 ( &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); save_128_aligned ((__m128i*)dst, xmm_dst); dst += 8; w -= 8; } while (w--) { d = *dst; *dst++ = pack_565_32_16 ( pack_1x128_32 (over_1x128 (xmm_src, xmm_alpha, expand565_16_1x128 (d)))); } } } static void sse2_composite_add_n_8888_8888_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, d; uint32_t *mask_line, m; uint32_t pack_cmp; int dst_stride, mask_stride; __m128i xmm_src; __m128i xmm_dst; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i mmx_src, mmx_mask, mmx_dest; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); xmm_src = _mm_unpacklo_epi8 ( create_mask_2x32_128 (src, src), _mm_setzero_si128 ()); mmx_src = xmm_src; while (height--) { int w = width; const uint32_t *pm = (uint32_t *)mask_line; uint32_t *pd = (uint32_t *)dst_line; dst_line += dst_stride; mask_line += mask_stride; while (w && (uintptr_t)pd & 15) { m = *pm++; if (m) { d = *pd; mmx_mask = unpack_32_1x128 (m); mmx_dest = unpack_32_1x128 (d); *pd = pack_1x128_32 ( _mm_adds_epu8 (pix_multiply_1x128 (mmx_mask, mmx_src), mmx_dest)); } pd++; w--; } while (w >= 4) { xmm_mask = load_128_unaligned ((__m128i*)pm); pack_cmp = _mm_movemask_epi8 ( _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); /* if all bits in mask are zero, pack_cmp are equal to 0xffff */ if (pack_cmp != 0xffff) { xmm_dst = load_128_aligned ((__m128i*)pd); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_src, &xmm_src, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); xmm_mask_hi = pack_2x128_128 (xmm_mask_lo, xmm_mask_hi); save_128_aligned ( (__m128i*)pd, _mm_adds_epu8 (xmm_mask_hi, xmm_dst)); } pd += 4; pm += 4; w -= 4; } while (w) { m = *pm++; if (m) { d = *pd; mmx_mask = unpack_32_1x128 (m); mmx_dest = unpack_32_1x128 (d); *pd = pack_1x128_32 ( _mm_adds_epu8 (pix_multiply_1x128 (mmx_mask, mmx_src), mmx_dest)); } pd++; w--; } } } static void sse2_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, d; uint32_t *mask_line, m; uint32_t pack_cmp; int dst_stride, mask_stride; __m128i xmm_src, xmm_alpha; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); xmm_src = _mm_unpacklo_epi8 ( create_mask_2x32_128 (src, src), _mm_setzero_si128 ()); xmm_alpha = expand_alpha_1x128 (xmm_src); mmx_src = xmm_src; mmx_alpha = xmm_alpha; while (height--) { int w = width; const uint32_t *pm = (uint32_t *)mask_line; uint32_t *pd = (uint32_t *)dst_line; dst_line += dst_stride; mask_line += mask_stride; while (w && (uintptr_t)pd & 15) { m = *pm++; if (m) { d = *pd; mmx_mask = unpack_32_1x128 (m); mmx_dest = unpack_32_1x128 (d); *pd = pack_1x128_32 (in_over_1x128 (&mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest)); } pd++; w--; } while (w >= 4) { xmm_mask = load_128_unaligned ((__m128i*)pm); pack_cmp = _mm_movemask_epi8 ( _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); /* if all bits in mask are zero, pack_cmp are equal to 0xffff */ if (pack_cmp != 0xffff) { xmm_dst = load_128_aligned ((__m128i*)pd); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } pd += 4; pm += 4; w -= 4; } while (w) { m = *pm++; if (m) { d = *pd; mmx_mask = unpack_32_1x128 (m); mmx_dest = unpack_32_1x128 (d); *pd = pack_1x128_32 ( in_over_1x128 (&mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest)); } pd++; w--; } } } static void sse2_composite_over_8888_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; uint32_t mask; int32_t w; int dst_stride, src_stride; __m128i xmm_mask; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_lo, xmm_alpha_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); mask = _pixman_image_get_solid (imp, mask_image, PIXMAN_a8r8g8b8); xmm_mask = create_mask_16_128 (mask >> 24); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { uint32_t s = *src++; if (s) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (s); __m128i alpha = expand_alpha_1x128 (ms); __m128i dest = xmm_mask; __m128i alpha_dst = unpack_32_1x128 (d); *dst = pack_1x128_32 ( in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); } dst++; w--; } while (w >= 4) { xmm_src = load_128_unaligned ((__m128i*)src); if (!is_zero (xmm_src)) { xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask, &xmm_mask, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } dst += 4; src += 4; w -= 4; } while (w) { uint32_t s = *src++; if (s) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (s); __m128i alpha = expand_alpha_1x128 (ms); __m128i mask = xmm_mask; __m128i dest = unpack_32_1x128 (d); *dst = pack_1x128_32 ( in_over_1x128 (&ms, &alpha, &mask, &dest)); } dst++; w--; } } } static void sse2_composite_src_x888_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst; uint32_t *src_line, *src, s; int dst_stride, src_stride; int32_t w; PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { s = *src++; *dst = convert_8888_to_0565 (s); dst++; w--; } while (w >= 8) { __m128i xmm_src0 = load_128_unaligned ((__m128i *)src + 0); __m128i xmm_src1 = load_128_unaligned ((__m128i *)src + 1); save_128_aligned ((__m128i*)dst, pack_565_2packedx128_128 (xmm_src0, xmm_src1)); w -= 8; src += 8; dst += 8; } while (w) { s = *src++; *dst = convert_8888_to_0565 (s); dst++; w--; } } } static void sse2_composite_src_x888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int32_t w; int dst_stride, src_stride; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { *dst++ = *src++ | 0xff000000; w--; } while (w >= 16) { __m128i xmm_src1, xmm_src2, xmm_src3, xmm_src4; xmm_src1 = load_128_unaligned ((__m128i*)src + 0); xmm_src2 = load_128_unaligned ((__m128i*)src + 1); xmm_src3 = load_128_unaligned ((__m128i*)src + 2); xmm_src4 = load_128_unaligned ((__m128i*)src + 3); save_128_aligned ((__m128i*)dst + 0, _mm_or_si128 (xmm_src1, mask_ff000000)); save_128_aligned ((__m128i*)dst + 1, _mm_or_si128 (xmm_src2, mask_ff000000)); save_128_aligned ((__m128i*)dst + 2, _mm_or_si128 (xmm_src3, mask_ff000000)); save_128_aligned ((__m128i*)dst + 3, _mm_or_si128 (xmm_src4, mask_ff000000)); dst += 16; src += 16; w -= 16; } while (w) { *dst++ = *src++ | 0xff000000; w--; } } } static void sse2_composite_over_x888_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; uint32_t mask; int dst_stride, src_stride; int32_t w; __m128i xmm_mask, xmm_alpha; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); mask = _pixman_image_get_solid (imp, mask_image, PIXMAN_a8r8g8b8); xmm_mask = create_mask_16_128 (mask >> 24); xmm_alpha = mask_00ff; while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { uint32_t s = (*src++) | 0xff000000; uint32_t d = *dst; __m128i src = unpack_32_1x128 (s); __m128i alpha = xmm_alpha; __m128i mask = xmm_mask; __m128i dest = unpack_32_1x128 (d); *dst++ = pack_1x128_32 ( in_over_1x128 (&src, &alpha, &mask, &dest)); w--; } while (w >= 4) { xmm_src = _mm_or_si128 ( load_128_unaligned ((__m128i*)src), mask_ff000000); xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha, &xmm_alpha, &xmm_mask, &xmm_mask, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); dst += 4; src += 4; w -= 4; } while (w) { uint32_t s = (*src++) | 0xff000000; uint32_t d = *dst; __m128i src = unpack_32_1x128 (s); __m128i alpha = xmm_alpha; __m128i mask = xmm_mask; __m128i dest = unpack_32_1x128 (d); *dst++ = pack_1x128_32 ( in_over_1x128 (&src, &alpha, &mask, &dest)); w--; } } } static void sse2_composite_over_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); int dst_stride, src_stride; uint32_t *dst_line, *dst; uint32_t *src_line, *src; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); dst = dst_line; src = src_line; while (height--) { sse2_combine_over_u (imp, op, dst, src, NULL, width); dst += dst_stride; src += src_stride; } } static force_inline uint16_t composite_over_8888_0565pixel (uint32_t src, uint16_t dst) { __m128i ms; ms = unpack_32_1x128 (src); return pack_565_32_16 ( pack_1x128_32 ( over_1x128 ( ms, expand_alpha_1x128 (ms), expand565_16_1x128 (dst)))); } static void sse2_composite_over_8888_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst, d; uint32_t *src_line, *src, s; int dst_stride, src_stride; int32_t w; __m128i xmm_alpha_lo, xmm_alpha_hi; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; src = src_line; dst_line += dst_stride; src_line += src_stride; w = width; /* Align dst on a 16-byte boundary */ while (w && ((uintptr_t)dst & 15)) { s = *src++; d = *dst; *dst++ = composite_over_8888_0565pixel (s, d); w--; } /* It's a 8 pixel loop */ while (w >= 8) { /* I'm loading unaligned because I'm not sure * about the address alignment. */ xmm_src = load_128_unaligned ((__m128i*) src); xmm_dst = load_128_aligned ((__m128i*) dst); /* Unpacking */ unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_565_128_4x128 (xmm_dst, &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); /* I'm loading next 4 pixels from memory * before to optimze the memory read. */ xmm_src = load_128_unaligned ((__m128i*) (src + 4)); over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst0, &xmm_dst1); /* Unpacking */ unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst2, &xmm_dst3); save_128_aligned ( (__m128i*)dst, pack_565_4x128_128 ( &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); w -= 8; dst += 8; src += 8; } while (w--) { s = *src++; d = *dst; *dst++ = composite_over_8888_0565pixel (s, d); } } } static void sse2_composite_over_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t m, d; __m128i xmm_src, xmm_alpha, xmm_def; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); xmm_def = create_mask_2x32_128 (src, src); xmm_src = expand_pixel_32_1x128 (src); xmm_alpha = expand_alpha_1x128 (xmm_src); mmx_src = xmm_src; mmx_alpha = xmm_alpha; while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { uint8_t m = *mask++; if (m) { d = *dst; mmx_mask = expand_pixel_8_1x128 (m); mmx_dest = unpack_32_1x128 (d); *dst = pack_1x128_32 (in_over_1x128 (&mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest)); } w--; dst++; } while (w >= 4) { m = *((uint32_t*)mask); if (srca == 0xff && m == 0xffffffff) { save_128_aligned ((__m128i*)dst, xmm_def); } else if (m) { xmm_dst = load_128_aligned ((__m128i*) dst); xmm_mask = unpack_32_1x128 (m); xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); /* Unpacking */ unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } w -= 4; dst += 4; mask += 4; } while (w) { uint8_t m = *mask++; if (m) { d = *dst; mmx_mask = expand_pixel_8_1x128 (m); mmx_dest = unpack_32_1x128 (d); *dst = pack_1x128_32 (in_over_1x128 (&mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest)); } w--; dst++; } } } #if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) __attribute__((__force_align_arg_pointer__)) #endif static pixman_bool_t sse2_fill (pixman_implementation_t *imp, uint32_t * bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler) { uint32_t byte_width; uint8_t *byte_line; __m128i xmm_def; if (bpp == 8) { uint8_t b; uint16_t w; stride = stride * (int) sizeof (uint32_t) / 1; byte_line = (uint8_t *)(((uint8_t *)bits) + stride * y + x); byte_width = width; stride *= 1; b = filler & 0xff; w = (b << 8) | b; filler = (w << 16) | w; } else if (bpp == 16) { stride = stride * (int) sizeof (uint32_t) / 2; byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); byte_width = 2 * width; stride *= 2; filler = (filler & 0xffff) * 0x00010001; } else if (bpp == 32) { stride = stride * (int) sizeof (uint32_t) / 4; byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); byte_width = 4 * width; stride *= 4; } else { return FALSE; } xmm_def = create_mask_2x32_128 (filler, filler); while (height--) { int w; uint8_t *d = byte_line; byte_line += stride; w = byte_width; if (w >= 1 && ((uintptr_t)d & 1)) { *(uint8_t *)d = filler; w -= 1; d += 1; } while (w >= 2 && ((uintptr_t)d & 3)) { *(uint16_t *)d = filler; w -= 2; d += 2; } while (w >= 4 && ((uintptr_t)d & 15)) { *(uint32_t *)d = filler; w -= 4; d += 4; } while (w >= 128) { save_128_aligned ((__m128i*)(d), xmm_def); save_128_aligned ((__m128i*)(d + 16), xmm_def); save_128_aligned ((__m128i*)(d + 32), xmm_def); save_128_aligned ((__m128i*)(d + 48), xmm_def); save_128_aligned ((__m128i*)(d + 64), xmm_def); save_128_aligned ((__m128i*)(d + 80), xmm_def); save_128_aligned ((__m128i*)(d + 96), xmm_def); save_128_aligned ((__m128i*)(d + 112), xmm_def); d += 128; w -= 128; } if (w >= 64) { save_128_aligned ((__m128i*)(d), xmm_def); save_128_aligned ((__m128i*)(d + 16), xmm_def); save_128_aligned ((__m128i*)(d + 32), xmm_def); save_128_aligned ((__m128i*)(d + 48), xmm_def); d += 64; w -= 64; } if (w >= 32) { save_128_aligned ((__m128i*)(d), xmm_def); save_128_aligned ((__m128i*)(d + 16), xmm_def); d += 32; w -= 32; } if (w >= 16) { save_128_aligned ((__m128i*)(d), xmm_def); d += 16; w -= 16; } while (w >= 4) { *(uint32_t *)d = filler; w -= 4; d += 4; } if (w >= 2) { *(uint16_t *)d = filler; w -= 2; d += 2; } if (w >= 1) { *(uint8_t *)d = filler; w -= 1; d += 1; } } return TRUE; } static void sse2_composite_src_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src, srca; uint32_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t m; __m128i xmm_src, xmm_def; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); srca = src >> 24; if (src == 0) { sse2_fill (imp, dest_image->bits.bits, dest_image->bits.rowstride, PIXMAN_FORMAT_BPP (dest_image->bits.format), dest_x, dest_y, width, height, 0); return; } PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); xmm_def = create_mask_2x32_128 (src, src); xmm_src = expand_pixel_32_1x128 (src); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { uint8_t m = *mask++; if (m) { *dst = pack_1x128_32 ( pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m))); } else { *dst = 0; } w--; dst++; } while (w >= 4) { m = *((uint32_t*)mask); if (srca == 0xff && m == 0xffffffff) { save_128_aligned ((__m128i*)dst, xmm_def); } else if (m) { xmm_mask = unpack_32_1x128 (m); xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); /* Unpacking */ unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_src, &xmm_src, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_mask_lo, xmm_mask_hi)); } else { save_128_aligned ((__m128i*)dst, _mm_setzero_si128 ()); } w -= 4; dst += 4; mask += 4; } while (w) { uint8_t m = *mask++; if (m) { *dst = pack_1x128_32 ( pix_multiply_1x128 ( xmm_src, expand_pixel_8_1x128 (m))); } else { *dst = 0; } w--; dst++; } } } static void sse2_composite_over_n_8_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint16_t *dst_line, *dst, d; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t m; __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; __m128i xmm_src, xmm_alpha; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); xmm_src = expand_pixel_32_1x128 (src); xmm_alpha = expand_alpha_1x128 (xmm_src); mmx_src = xmm_src; mmx_alpha = xmm_alpha; while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { m = *mask++; if (m) { d = *dst; mmx_mask = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); mmx_dest = expand565_16_1x128 (d); *dst = pack_565_32_16 ( pack_1x128_32 ( in_over_1x128 ( &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); } w--; dst++; } while (w >= 8) { xmm_dst = load_128_aligned ((__m128i*) dst); unpack_565_128_4x128 (xmm_dst, &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); m = *((uint32_t*)mask); mask += 4; if (m) { xmm_mask = unpack_32_1x128 (m); xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); /* Unpacking */ unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst0, &xmm_dst1); } m = *((uint32_t*)mask); mask += 4; if (m) { xmm_mask = unpack_32_1x128 (m); xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); /* Unpacking */ unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst2, &xmm_dst3); } save_128_aligned ( (__m128i*)dst, pack_565_4x128_128 ( &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); w -= 8; dst += 8; } while (w) { m = *mask++; if (m) { d = *dst; mmx_mask = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); mmx_dest = expand565_16_1x128 (d); *dst = pack_565_32_16 ( pack_1x128_32 ( in_over_1x128 ( &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); } w--; dst++; } } } static void sse2_composite_over_pixbuf_0565 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint16_t *dst_line, *dst, d; uint32_t *src_line, *src, s; int dst_stride, src_stride; int32_t w; uint32_t opaque, zero; __m128i ms; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { s = *src++; d = *dst; ms = unpack_32_1x128 (s); *dst++ = pack_565_32_16 ( pack_1x128_32 ( over_rev_non_pre_1x128 (ms, expand565_16_1x128 (d)))); w--; } while (w >= 8) { /* First round */ xmm_src = load_128_unaligned ((__m128i*)src); xmm_dst = load_128_aligned ((__m128i*)dst); opaque = is_opaque (xmm_src); zero = is_zero (xmm_src); unpack_565_128_4x128 (xmm_dst, &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); /* preload next round*/ xmm_src = load_128_unaligned ((__m128i*)(src + 4)); if (opaque) { invert_colors_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst0, &xmm_dst1); } else if (!zero) { over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst0, &xmm_dst1); } /* Second round */ opaque = is_opaque (xmm_src); zero = is_zero (xmm_src); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); if (opaque) { invert_colors_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst2, &xmm_dst3); } else if (!zero) { over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst2, &xmm_dst3); } save_128_aligned ( (__m128i*)dst, pack_565_4x128_128 ( &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); w -= 8; src += 8; dst += 8; } while (w) { s = *src++; d = *dst; ms = unpack_32_1x128 (s); *dst++ = pack_565_32_16 ( pack_1x128_32 ( over_rev_non_pre_1x128 (ms, expand565_16_1x128 (d)))); w--; } } } static void sse2_composite_over_pixbuf_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst, d; uint32_t *src_line, *src, s; int dst_stride, src_stride; int32_t w; uint32_t opaque, zero; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && (uintptr_t)dst & 15) { s = *src++; d = *dst; *dst++ = pack_1x128_32 ( over_rev_non_pre_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (d))); w--; } while (w >= 4) { xmm_src_hi = load_128_unaligned ((__m128i*)src); opaque = is_opaque (xmm_src_hi); zero = is_zero (xmm_src_hi); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); if (opaque) { invert_colors_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } else if (!zero) { xmm_dst_hi = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } w -= 4; dst += 4; src += 4; } while (w) { s = *src++; d = *dst; *dst++ = pack_1x128_32 ( over_rev_non_pre_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (d))); w--; } } } static void sse2_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint16_t *dst_line, *dst, d; uint32_t *mask_line, *mask, m; int dst_stride, mask_stride; int w; uint32_t pack_cmp; __m128i xmm_src, xmm_alpha; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); xmm_src = expand_pixel_32_1x128 (src); xmm_alpha = expand_alpha_1x128 (xmm_src); mmx_src = xmm_src; mmx_alpha = xmm_alpha; while (height--) { w = width; mask = mask_line; dst = dst_line; mask_line += mask_stride; dst_line += dst_stride; while (w && ((uintptr_t)dst & 15)) { m = *(uint32_t *) mask; if (m) { d = *dst; mmx_mask = unpack_32_1x128 (m); mmx_dest = expand565_16_1x128 (d); *dst = pack_565_32_16 ( pack_1x128_32 ( in_over_1x128 ( &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); } w--; dst++; mask++; } while (w >= 8) { /* First round */ xmm_mask = load_128_unaligned ((__m128i*)mask); xmm_dst = load_128_aligned ((__m128i*)dst); pack_cmp = _mm_movemask_epi8 ( _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); unpack_565_128_4x128 (xmm_dst, &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); /* preload next round */ xmm_mask = load_128_unaligned ((__m128i*)(mask + 4)); /* preload next round */ if (pack_cmp != 0xffff) { in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst0, &xmm_dst1); } /* Second round */ pack_cmp = _mm_movemask_epi8 ( _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); if (pack_cmp != 0xffff) { in_over_2x128 (&xmm_src, &xmm_src, &xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst2, &xmm_dst3); } save_128_aligned ( (__m128i*)dst, pack_565_4x128_128 ( &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); w -= 8; dst += 8; mask += 8; } while (w) { m = *(uint32_t *) mask; if (m) { d = *dst; mmx_mask = unpack_32_1x128 (m); mmx_dest = expand565_16_1x128 (d); *dst = pack_565_32_16 ( pack_1x128_32 ( in_over_1x128 ( &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); } w--; dst++; mask++; } } } static void sse2_composite_in_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; uint32_t d, m; uint32_t src; int32_t w; __m128i xmm_alpha; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && ((uintptr_t)dst & 15)) { m = (uint32_t) *mask++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 (xmm_alpha, unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } while (w >= 16) { xmm_mask = load_128_unaligned ((__m128i*)mask); xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); mask += 16; dst += 16; w -= 16; } while (w) { m = (uint32_t) *mask++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 ( pix_multiply_1x128 ( xmm_alpha, unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } } } static void sse2_composite_in_n_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; int dst_stride; uint32_t d; uint32_t src; int32_t w; __m128i xmm_alpha; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); src = src >> 24; if (src == 0xff) return; if (src == 0x00) { pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, 8, dest_x, dest_y, width, height, src); return; } while (height--) { dst = dst_line; dst_line += dst_stride; w = width; while (w && ((uintptr_t)dst & 15)) { d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 ( xmm_alpha, unpack_32_1x128 (d))); w--; } while (w >= 16) { xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, &xmm_dst_lo, &xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); dst += 16; w -= 16; } while (w) { d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 ( xmm_alpha, unpack_32_1x128 (d))); w--; } } } static void sse2_composite_in_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int src_stride, dst_stride; int32_t w; uint32_t s, d; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; w = width; while (w && ((uintptr_t)dst & 15)) { s = (uint32_t) *src++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 ( unpack_32_1x128 (s), unpack_32_1x128 (d))); w--; } while (w >= 16) { xmm_src = load_128_unaligned ((__m128i*)src); xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_dst_lo, &xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); src += 16; dst += 16; w -= 16; } while (w) { s = (uint32_t) *src++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (d))); w--; } } } static void sse2_composite_add_n_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t src; uint32_t m, d; __m128i xmm_alpha; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && ((uintptr_t)dst & 15)) { m = (uint32_t) *mask++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( _mm_adds_epu16 ( pix_multiply_1x128 ( xmm_alpha, unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } while (w >= 16) { xmm_mask = load_128_unaligned ((__m128i*)mask); xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); xmm_dst_lo = _mm_adds_epu16 (xmm_mask_lo, xmm_dst_lo); xmm_dst_hi = _mm_adds_epu16 (xmm_mask_hi, xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); mask += 16; dst += 16; w -= 16; } while (w) { m = (uint32_t) *mask++; d = (uint32_t) *dst; *dst++ = (uint8_t) pack_1x128_32 ( _mm_adds_epu16 ( pix_multiply_1x128 ( xmm_alpha, unpack_32_1x128 (m)), unpack_32_1x128 (d))); w--; } } } static void sse2_composite_add_n_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; int dst_stride; int32_t w; uint32_t src; __m128i xmm_src; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); src >>= 24; if (src == 0x00) return; if (src == 0xff) { pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, 8, dest_x, dest_y, width, height, 0xff); return; } src = (src << 24) | (src << 16) | (src << 8) | src; xmm_src = _mm_set_epi32 (src, src, src, src); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; while (w && ((uintptr_t)dst & 15)) { *dst = (uint8_t)_mm_cvtsi128_si32 ( _mm_adds_epu8 ( xmm_src, _mm_cvtsi32_si128 (*dst))); w--; dst++; } while (w >= 16) { save_128_aligned ( (__m128i*)dst, _mm_adds_epu8 (xmm_src, load_128_aligned ((__m128i*)dst))); dst += 16; w -= 16; } while (w) { *dst = (uint8_t)_mm_cvtsi128_si32 ( _mm_adds_epu8 ( xmm_src, _mm_cvtsi32_si128 (*dst))); w--; dst++; } } } static void sse2_composite_add_8_8 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint8_t *dst_line, *dst; uint8_t *src_line, *src; int dst_stride, src_stride; int32_t w; uint16_t t; PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; src = src_line; dst_line += dst_stride; src_line += src_stride; w = width; /* Small head */ while (w && (uintptr_t)dst & 3) { t = (*dst) + (*src++); *dst++ = t | (0 - (t >> 8)); w--; } sse2_combine_add_u (imp, op, (uint32_t*)dst, (uint32_t*)src, NULL, w >> 2); /* Small tail */ dst += w & 0xfffc; src += w & 0xfffc; w &= 3; while (w) { t = (*dst) + (*src++); *dst++ = t | (0 - (t >> 8)); w--; } } } static void sse2_composite_add_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint32_t *src_line, *src; int dst_stride, src_stride; PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; src = src_line; src_line += src_stride; sse2_combine_add_u (imp, op, dst, src, NULL, width); } } static void sse2_composite_add_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst, src; int dst_stride; __m128i xmm_src; PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; if (src == ~0) { pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, 32, dest_x, dest_y, width, height, ~0); return; } xmm_src = _mm_set_epi32 (src, src, src, src); while (height--) { int w = width; uint32_t d; dst = dst_line; dst_line += dst_stride; while (w && (uintptr_t)dst & 15) { d = *dst; *dst++ = _mm_cvtsi128_si32 ( _mm_adds_epu8 (xmm_src, _mm_cvtsi32_si128 (d))); w--; } while (w >= 4) { save_128_aligned ((__m128i*)dst, _mm_adds_epu8 (xmm_src, load_128_aligned ((__m128i*)dst))); dst += 4; w -= 4; } while (w--) { d = *dst; *dst++ = _mm_cvtsi128_si32 (_mm_adds_epu8 (xmm_src, _mm_cvtsi32_si128 (d))); } } } static void sse2_composite_add_n_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *dst_line, *dst; uint8_t *mask_line, *mask; int dst_stride, mask_stride; int32_t w; uint32_t src; __m128i xmm_src; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; xmm_src = expand_pixel_32_1x128 (src); PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); while (height--) { dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && ((uintptr_t)dst & 15)) { uint8_t m = *mask++; if (m) { *dst = pack_1x128_32 (_mm_adds_epu16 (pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m)), unpack_32_1x128 (*dst))); } dst++; w--; } while (w >= 4) { uint32_t m = *(uint32_t*)mask; if (m) { __m128i xmm_mask_lo, xmm_mask_hi; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_dst = load_128_aligned ((__m128i*)dst); __m128i xmm_mask = _mm_unpacklo_epi8 (unpack_32_1x128(m), _mm_setzero_si128 ()); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); pix_multiply_2x128 (&xmm_src, &xmm_src, &xmm_mask_lo, &xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); xmm_dst_lo = _mm_adds_epu16 (xmm_mask_lo, xmm_dst_lo); xmm_dst_hi = _mm_adds_epu16 (xmm_mask_hi, xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } w -= 4; dst += 4; mask += 4; } while (w) { uint8_t m = *mask++; if (m) { *dst = pack_1x128_32 (_mm_adds_epu16 (pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m)), unpack_32_1x128 (*dst))); } dst++; w--; } } } static pixman_bool_t sse2_blt (pixman_implementation_t *imp, uint32_t * src_bits, uint32_t * dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { uint8_t * src_bytes; uint8_t * dst_bytes; int byte_width; if (src_bpp != dst_bpp) return FALSE; if (src_bpp == 16) { src_stride = src_stride * (int) sizeof (uint32_t) / 2; dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; src_bytes =(uint8_t *)(((uint16_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = 2 * width; src_stride *= 2; dst_stride *= 2; } else if (src_bpp == 32) { src_stride = src_stride * (int) sizeof (uint32_t) / 4; dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; src_bytes = (uint8_t *)(((uint32_t *)src_bits) + src_stride * (src_y) + (src_x)); dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); byte_width = 4 * width; src_stride *= 4; dst_stride *= 4; } else { return FALSE; } while (height--) { int w; uint8_t *s = src_bytes; uint8_t *d = dst_bytes; src_bytes += src_stride; dst_bytes += dst_stride; w = byte_width; while (w >= 2 && ((uintptr_t)d & 3)) { *(uint16_t *)d = *(uint16_t *)s; w -= 2; s += 2; d += 2; } while (w >= 4 && ((uintptr_t)d & 15)) { *(uint32_t *)d = *(uint32_t *)s; w -= 4; s += 4; d += 4; } while (w >= 64) { __m128i xmm0, xmm1, xmm2, xmm3; xmm0 = load_128_unaligned ((__m128i*)(s)); xmm1 = load_128_unaligned ((__m128i*)(s + 16)); xmm2 = load_128_unaligned ((__m128i*)(s + 32)); xmm3 = load_128_unaligned ((__m128i*)(s + 48)); save_128_aligned ((__m128i*)(d), xmm0); save_128_aligned ((__m128i*)(d + 16), xmm1); save_128_aligned ((__m128i*)(d + 32), xmm2); save_128_aligned ((__m128i*)(d + 48), xmm3); s += 64; d += 64; w -= 64; } while (w >= 16) { save_128_aligned ((__m128i*)d, load_128_unaligned ((__m128i*)s) ); w -= 16; d += 16; s += 16; } while (w >= 4) { *(uint32_t *)d = *(uint32_t *)s; w -= 4; s += 4; d += 4; } if (w >= 2) { *(uint16_t *)d = *(uint16_t *)s; w -= 2; s += 2; d += 2; } } return TRUE; } static void sse2_composite_copy_area (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); sse2_blt (imp, src_image->bits.bits, dest_image->bits.bits, src_image->bits.rowstride, dest_image->bits.rowstride, PIXMAN_FORMAT_BPP (src_image->bits.format), PIXMAN_FORMAT_BPP (dest_image->bits.format), src_x, src_y, dest_x, dest_y, width, height); } static void sse2_composite_over_x888_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *src, *src_line, s; uint32_t *dst, *dst_line, d; uint8_t *mask, *mask_line; uint32_t m; int src_stride, mask_stride, dst_stride; int32_t w; __m128i ms; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { src = src_line; src_line += src_stride; dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { s = 0xff000000 | *src++; m = (uint32_t) *mask++; d = *dst; ms = unpack_32_1x128 (s); if (m != 0xff) { __m128i ma = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); __m128i md = unpack_32_1x128 (d); ms = in_over_1x128 (&ms, &mask_00ff, &ma, &md); } *dst++ = pack_1x128_32 (ms); w--; } while (w >= 4) { m = *(uint32_t*) mask; xmm_src = _mm_or_si128 ( load_128_unaligned ((__m128i*)src), mask_ff000000); if (m == 0xffffffff) { save_128_aligned ((__m128i*)dst, xmm_src); } else { xmm_dst = load_128_aligned ((__m128i*)dst); xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_rev_2x128 ( xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &mask_00ff, &mask_00ff, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } src += 4; dst += 4; mask += 4; w -= 4; } while (w) { m = (uint32_t) *mask++; if (m) { s = 0xff000000 | *src; if (m == 0xff) { *dst = s; } else { __m128i ma, md, ms; d = *dst; ma = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); md = unpack_32_1x128 (d); ms = unpack_32_1x128 (s); *dst = pack_1x128_32 (in_over_1x128 (&ms, &mask_00ff, &ma, &md)); } } src++; dst++; w--; } } } static void sse2_composite_over_8888_8_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *src, *src_line, s; uint32_t *dst, *dst_line, d; uint8_t *mask, *mask_line; uint32_t m; int src_stride, mask_stride, dst_stride; int32_t w; __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { src = src_line; src_line += src_stride; dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { uint32_t sa; s = *src++; m = (uint32_t) *mask++; d = *dst; sa = s >> 24; if (m) { if (sa == 0xff && m == 0xff) { *dst = s; } else { __m128i ms, md, ma, msa; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (s); md = unpack_32_1x128 (d); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } dst++; w--; } while (w >= 4) { m = *(uint32_t *) mask; if (m) { xmm_src = load_128_unaligned ((__m128i*)src); if (m == 0xffffffff && is_opaque (xmm_src)) { save_128_aligned ((__m128i *)dst, xmm_src); } else { xmm_dst = load_128_aligned ((__m128i *)dst); xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } } src += 4; dst += 4; mask += 4; w -= 4; } while (w) { uint32_t sa; s = *src++; m = (uint32_t) *mask++; d = *dst; sa = s >> 24; if (m) { if (sa == 0xff && m == 0xff) { *dst = s; } else { __m128i ms, md, ma, msa; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (s); md = unpack_32_1x128 (d); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } dst++; w--; } } } static void sse2_composite_over_reverse_n_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t src; uint32_t *dst_line, *dst; __m128i xmm_src; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_dsta_hi, xmm_dsta_lo; int dst_stride; int32_t w; src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); if (src == 0) return; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); xmm_src = expand_pixel_32_1x128 (src); while (height--) { dst = dst_line; dst_line += dst_stride; w = width; while (w && (uintptr_t)dst & 15) { __m128i vd; vd = unpack_32_1x128 (*dst); *dst = pack_1x128_32 (over_1x128 (vd, expand_alpha_1x128 (vd), xmm_src)); w--; dst++; } while (w >= 4) { __m128i tmp_lo, tmp_hi; xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dsta_lo, &xmm_dsta_hi); tmp_lo = xmm_src; tmp_hi = xmm_src; over_2x128 (&xmm_dst_lo, &xmm_dst_hi, &xmm_dsta_lo, &xmm_dsta_hi, &tmp_lo, &tmp_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (tmp_lo, tmp_hi)); w -= 4; dst += 4; } while (w) { __m128i vd; vd = unpack_32_1x128 (*dst); *dst = pack_1x128_32 (over_1x128 (vd, expand_alpha_1x128 (vd), xmm_src)); w--; dst++; } } } static void sse2_composite_over_8888_8888_8888 (pixman_implementation_t *imp, pixman_composite_info_t *info) { PIXMAN_COMPOSITE_ARGS (info); uint32_t *src, *src_line, s; uint32_t *dst, *dst_line, d; uint32_t *mask, *mask_line; uint32_t m; int src_stride, mask_stride, dst_stride; int32_t w; __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; PIXMAN_IMAGE_GET_LINE ( dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); PIXMAN_IMAGE_GET_LINE ( mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); PIXMAN_IMAGE_GET_LINE ( src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); while (height--) { src = src_line; src_line += src_stride; dst = dst_line; dst_line += dst_stride; mask = mask_line; mask_line += mask_stride; w = width; while (w && (uintptr_t)dst & 15) { uint32_t sa; s = *src++; m = (*mask++) >> 24; d = *dst; sa = s >> 24; if (m) { if (sa == 0xff && m == 0xff) { *dst = s; } else { __m128i ms, md, ma, msa; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (s); md = unpack_32_1x128 (d); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } dst++; w--; } while (w >= 4) { xmm_mask = load_128_unaligned ((__m128i*)mask); if (!is_transparent (xmm_mask)) { xmm_src = load_128_unaligned ((__m128i*)src); if (is_opaque (xmm_mask) && is_opaque (xmm_src)) { save_128_aligned ((__m128i *)dst, xmm_src); } else { xmm_dst = load_128_aligned ((__m128i *)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); expand_alpha_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } } src += 4; dst += 4; mask += 4; w -= 4; } while (w) { uint32_t sa; s = *src++; m = (*mask++) >> 24; d = *dst; sa = s >> 24; if (m) { if (sa == 0xff && m == 0xff) { *dst = s; } else { __m128i ms, md, ma, msa; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (s); md = unpack_32_1x128 (d); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } dst++; w--; } } } /* A variant of 'sse2_combine_over_u' with minor tweaks */ static force_inline void scaled_nearest_scanline_sse2_8888_8888_OVER (uint32_t* pd, const uint32_t* ps, int32_t w, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t src_width_fixed, pixman_bool_t fully_transparent_src) { uint32_t s, d; const uint32_t* pm = NULL; __m128i xmm_dst_lo, xmm_dst_hi; __m128i xmm_src_lo, xmm_src_hi; __m128i xmm_alpha_lo, xmm_alpha_hi; if (fully_transparent_src) return; /* Align dst on a 16-byte boundary */ while (w && ((uintptr_t)pd & 15)) { d = *pd; s = combine1 (ps + pixman_fixed_to_int (vx), pm); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; *pd++ = core_combine_over_u_pixel_sse2 (s, d); if (pm) pm++; w--; } while (w >= 4) { __m128i tmp; uint32_t tmp1, tmp2, tmp3, tmp4; tmp1 = *(ps + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp2 = *(ps + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp3 = *(ps + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp4 = *(ps + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp = _mm_set_epi32 (tmp4, tmp3, tmp2, tmp1); xmm_src_hi = combine4 ((__m128i*)&tmp, (__m128i*)pm); if (is_opaque (xmm_src_hi)) { save_128_aligned ((__m128i*)pd, xmm_src_hi); } else if (!is_zero (xmm_src_hi)) { xmm_dst_hi = load_128_aligned ((__m128i*) pd); unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 ( xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst_lo, &xmm_dst_hi); /* rebuid the 4 pixel data and save*/ save_128_aligned ((__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } w -= 4; pd += 4; if (pm) pm += 4; } while (w) { d = *pd; s = combine1 (ps + pixman_fixed_to_int (vx), pm); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; *pd++ = core_combine_over_u_pixel_sse2 (s, d); if (pm) pm++; w--; } } FAST_NEAREST_MAINLOOP (sse2_8888_8888_cover_OVER, scaled_nearest_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, COVER) FAST_NEAREST_MAINLOOP (sse2_8888_8888_none_OVER, scaled_nearest_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, NONE) FAST_NEAREST_MAINLOOP (sse2_8888_8888_pad_OVER, scaled_nearest_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, PAD) FAST_NEAREST_MAINLOOP (sse2_8888_8888_normal_OVER, scaled_nearest_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, NORMAL) static force_inline void scaled_nearest_scanline_sse2_8888_n_8888_OVER (const uint32_t * mask, uint32_t * dst, const uint32_t * src, int32_t w, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t src_width_fixed, pixman_bool_t zero_src) { __m128i xmm_mask; __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_lo, xmm_alpha_hi; if (zero_src || (*mask >> 24) == 0) return; xmm_mask = create_mask_16_128 (*mask >> 24); while (w && (uintptr_t)dst & 15) { uint32_t s = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; if (s) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (s); __m128i alpha = expand_alpha_1x128 (ms); __m128i dest = xmm_mask; __m128i alpha_dst = unpack_32_1x128 (d); *dst = pack_1x128_32 ( in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); } dst++; w--; } while (w >= 4) { uint32_t tmp1, tmp2, tmp3, tmp4; tmp1 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp2 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp3 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; tmp4 = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; xmm_src = _mm_set_epi32 (tmp4, tmp3, tmp2, tmp1); if (!is_zero (xmm_src)) { xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask, &xmm_mask, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ( (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } dst += 4; w -= 4; } while (w) { uint32_t s = *(src + pixman_fixed_to_int (vx)); vx += unit_x; while (vx >= 0) vx -= src_width_fixed; if (s) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (s); __m128i alpha = expand_alpha_1x128 (ms); __m128i mask = xmm_mask; __m128i dest = unpack_32_1x128 (d); *dst = pack_1x128_32 ( in_over_1x128 (&ms, &alpha, &mask, &dest)); } dst++; w--; } } FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_cover_OVER, scaled_nearest_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, COVER, TRUE, TRUE) FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_pad_OVER, scaled_nearest_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, PAD, TRUE, TRUE) FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_none_OVER, scaled_nearest_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, NONE, TRUE, TRUE) FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_normal_OVER, scaled_nearest_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, NORMAL, TRUE, TRUE) #if BILINEAR_INTERPOLATION_BITS < 8 # define BILINEAR_DECLARE_VARIABLES \ const __m128i xmm_wt = _mm_set_epi16 (wt, wt, wt, wt, wt, wt, wt, wt); \ const __m128i xmm_wb = _mm_set_epi16 (wb, wb, wb, wb, wb, wb, wb, wb); \ const __m128i xmm_addc = _mm_set_epi16 (0, 1, 0, 1, 0, 1, 0, 1); \ const __m128i xmm_ux = _mm_set_epi16 (unit_x, -unit_x, unit_x, -unit_x, \ unit_x, -unit_x, unit_x, -unit_x); \ const __m128i xmm_zero = _mm_setzero_si128 (); \ __m128i xmm_x = _mm_set_epi16 (vx, -(vx + 1), vx, -(vx + 1), \ vx, -(vx + 1), vx, -(vx + 1)) #else # define BILINEAR_DECLARE_VARIABLES \ const __m128i xmm_wt = _mm_set_epi16 (wt, wt, wt, wt, wt, wt, wt, wt); \ const __m128i xmm_wb = _mm_set_epi16 (wb, wb, wb, wb, wb, wb, wb, wb); \ const __m128i xmm_addc = _mm_set_epi16 (0, 0, 0, 0, 1, 1, 1, 1); \ const __m128i xmm_ux = _mm_set_epi16 (unit_x, unit_x, unit_x, unit_x, \ -unit_x, -unit_x, -unit_x, -unit_x); \ const __m128i xmm_zero = _mm_setzero_si128 (); \ __m128i xmm_x = _mm_set_epi16 (vx, vx, vx, vx, \ -(vx + 1), -(vx + 1), -(vx + 1), -(vx + 1)) #endif #define BILINEAR_INTERPOLATE_ONE_PIXEL(pix) \ do { \ __m128i xmm_wh, xmm_lo, xmm_hi, a; \ /* fetch 2x2 pixel block into sse2 registers */ \ __m128i tltr = _mm_loadl_epi64 ( \ (__m128i *)&src_top[pixman_fixed_to_int (vx)]); \ __m128i blbr = _mm_loadl_epi64 ( \ (__m128i *)&src_bottom[pixman_fixed_to_int (vx)]); \ vx += unit_x; \ /* vertical interpolation */ \ a = _mm_add_epi16 (_mm_mullo_epi16 (_mm_unpacklo_epi8 (tltr, xmm_zero), \ xmm_wt), \ _mm_mullo_epi16 (_mm_unpacklo_epi8 (blbr, xmm_zero), \ xmm_wb)); \ if (BILINEAR_INTERPOLATION_BITS < 8) \ { \ /* calculate horizontal weights */ \ xmm_wh = _mm_add_epi16 (xmm_addc, _mm_srli_epi16 (xmm_x, \ 16 - BILINEAR_INTERPOLATION_BITS)); \ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \ /* horizontal interpolation */ \ a = _mm_madd_epi16 (_mm_unpackhi_epi16 (_mm_shuffle_epi32 ( \ a, _MM_SHUFFLE (1, 0, 3, 2)), a), xmm_wh); \ } \ else \ { \ /* calculate horizontal weights */ \ xmm_wh = _mm_add_epi16 (xmm_addc, _mm_srli_epi16 (xmm_x, \ 16 - BILINEAR_INTERPOLATION_BITS)); \ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \ /* horizontal interpolation */ \ xmm_lo = _mm_mullo_epi16 (a, xmm_wh); \ xmm_hi = _mm_mulhi_epu16 (a, xmm_wh); \ a = _mm_add_epi32 (_mm_unpacklo_epi16 (xmm_lo, xmm_hi), \ _mm_unpackhi_epi16 (xmm_lo, xmm_hi)); \ } \ /* shift and pack the result */ \ a = _mm_srli_epi32 (a, BILINEAR_INTERPOLATION_BITS * 2); \ a = _mm_packs_epi32 (a, a); \ a = _mm_packus_epi16 (a, a); \ pix = _mm_cvtsi128_si32 (a); \ } while (0) #define BILINEAR_SKIP_ONE_PIXEL() \ do { \ vx += unit_x; \ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \ } while(0) static force_inline void scaled_bilinear_scanline_sse2_8888_8888_SRC (uint32_t * dst, const uint32_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; uint32_t pix1, pix2, pix3, pix4; while ((w -= 4) >= 0) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); BILINEAR_INTERPOLATE_ONE_PIXEL (pix3); BILINEAR_INTERPOLATE_ONE_PIXEL (pix4); *dst++ = pix1; *dst++ = pix2; *dst++ = pix3; *dst++ = pix4; } if (w & 2) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); *dst++ = pix1; *dst++ = pix2; } if (w & 1) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); *dst = pix1; } } FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_SRC, scaled_bilinear_scanline_sse2_8888_8888_SRC, uint32_t, uint32_t, uint32_t, COVER, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_pad_SRC, scaled_bilinear_scanline_sse2_8888_8888_SRC, uint32_t, uint32_t, uint32_t, PAD, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_none_SRC, scaled_bilinear_scanline_sse2_8888_8888_SRC, uint32_t, uint32_t, uint32_t, NONE, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_normal_SRC, scaled_bilinear_scanline_sse2_8888_8888_SRC, uint32_t, uint32_t, uint32_t, NORMAL, FLAG_NONE) static force_inline void scaled_bilinear_scanline_sse2_8888_8888_OVER (uint32_t * dst, const uint32_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; uint32_t pix1, pix2, pix3, pix4; while (w && ((uintptr_t)dst & 15)) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (pix1) { pix2 = *dst; *dst = core_combine_over_u_pixel_sse2 (pix1, pix2); } w--; dst++; } while (w >= 4) { __m128i xmm_src; __m128i xmm_src_hi, xmm_src_lo, xmm_dst_hi, xmm_dst_lo; __m128i xmm_alpha_hi, xmm_alpha_lo; BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); BILINEAR_INTERPOLATE_ONE_PIXEL (pix3); BILINEAR_INTERPOLATE_ONE_PIXEL (pix4); xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1); if (!is_zero (xmm_src)) { if (is_opaque (xmm_src)) { save_128_aligned ((__m128i *)dst, xmm_src); } else { __m128i xmm_dst = load_128_aligned ((__m128i *)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i *)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } } w -= 4; dst += 4; } while (w) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (pix1) { pix2 = *dst; *dst = core_combine_over_u_pixel_sse2 (pix1, pix2); } w--; dst++; } } FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_OVER, scaled_bilinear_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, uint32_t, COVER, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_pad_OVER, scaled_bilinear_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, uint32_t, PAD, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_none_OVER, scaled_bilinear_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, uint32_t, NONE, FLAG_NONE) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_normal_OVER, scaled_bilinear_scanline_sse2_8888_8888_OVER, uint32_t, uint32_t, uint32_t, NORMAL, FLAG_NONE) static force_inline void scaled_bilinear_scanline_sse2_8888_8_8888_OVER (uint32_t * dst, const uint8_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; uint32_t pix1, pix2, pix3, pix4; uint32_t m; while (w && ((uintptr_t)dst & 15)) { uint32_t sa; m = (uint32_t) *mask++; if (m) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); sa = pix1 >> 24; if (sa == 0xff && m == 0xff) { *dst = pix1; } else { __m128i ms, md, ma, msa; pix2 = *dst; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (pix1); md = unpack_32_1x128 (pix2); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } else { BILINEAR_SKIP_ONE_PIXEL (); } w--; dst++; } while (w >= 4) { __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; m = *(uint32_t*)mask; if (m) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); BILINEAR_INTERPOLATE_ONE_PIXEL (pix3); BILINEAR_INTERPOLATE_ONE_PIXEL (pix4); xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1); if (m == 0xffffffff && is_opaque (xmm_src)) { save_128_aligned ((__m128i *)dst, xmm_src); } else { xmm_dst = load_128_aligned ((__m128i *)dst); xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } } else { BILINEAR_SKIP_ONE_PIXEL (); BILINEAR_SKIP_ONE_PIXEL (); BILINEAR_SKIP_ONE_PIXEL (); BILINEAR_SKIP_ONE_PIXEL (); } w -= 4; dst += 4; mask += 4; } while (w) { uint32_t sa; m = (uint32_t) *mask++; if (m) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); sa = pix1 >> 24; if (sa == 0xff && m == 0xff) { *dst = pix1; } else { __m128i ms, md, ma, msa; pix2 = *dst; ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); ms = unpack_32_1x128 (pix1); md = unpack_32_1x128 (pix2); msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); } } else { BILINEAR_SKIP_ONE_PIXEL (); } w--; dst++; } } FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_cover_OVER, scaled_bilinear_scanline_sse2_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, COVER, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_pad_OVER, scaled_bilinear_scanline_sse2_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, PAD, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_none_OVER, scaled_bilinear_scanline_sse2_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, NONE, FLAG_HAVE_NON_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_normal_OVER, scaled_bilinear_scanline_sse2_8888_8_8888_OVER, uint32_t, uint8_t, uint32_t, NORMAL, FLAG_HAVE_NON_SOLID_MASK) static force_inline void scaled_bilinear_scanline_sse2_8888_n_8888_OVER (uint32_t * dst, const uint32_t * mask, const uint32_t * src_top, const uint32_t * src_bottom, int32_t w, int wt, int wb, pixman_fixed_t vx, pixman_fixed_t unit_x, pixman_fixed_t max_vx, pixman_bool_t zero_src) { BILINEAR_DECLARE_VARIABLES; uint32_t pix1, pix2, pix3, pix4; __m128i xmm_mask; if (zero_src || (*mask >> 24) == 0) return; xmm_mask = create_mask_16_128 (*mask >> 24); while (w && ((uintptr_t)dst & 15)) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (pix1) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (pix1); __m128i alpha = expand_alpha_1x128 (ms); __m128i dest = xmm_mask; __m128i alpha_dst = unpack_32_1x128 (d); *dst = pack_1x128_32 (in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); } dst++; w--; } while (w >= 4) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); BILINEAR_INTERPOLATE_ONE_PIXEL (pix3); BILINEAR_INTERPOLATE_ONE_PIXEL (pix4); if (pix1 | pix2 | pix3 | pix4) { __m128i xmm_src, xmm_src_lo, xmm_src_hi; __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; __m128i xmm_alpha_lo, xmm_alpha_hi; xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1); xmm_dst = load_128_aligned ((__m128i*)dst); unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, &xmm_mask, &xmm_mask, &xmm_dst_lo, &xmm_dst_hi); save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); } dst += 4; w -= 4; } while (w) { BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); if (pix1) { uint32_t d = *dst; __m128i ms = unpack_32_1x128 (pix1); __m128i alpha = expand_alpha_1x128 (ms); __m128i dest = xmm_mask; __m128i alpha_dst = unpack_32_1x128 (d); *dst = pack_1x128_32 (in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); } dst++; w--; } } FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_cover_OVER, scaled_bilinear_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, COVER, FLAG_HAVE_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_pad_OVER, scaled_bilinear_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, PAD, FLAG_HAVE_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_none_OVER, scaled_bilinear_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, NONE, FLAG_HAVE_SOLID_MASK) FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_normal_OVER, scaled_bilinear_scanline_sse2_8888_n_8888_OVER, uint32_t, uint32_t, uint32_t, NORMAL, FLAG_HAVE_SOLID_MASK) static const pixman_fast_path_t sse2_fast_paths[] = { /* PIXMAN_OP_OVER */ PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, sse2_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, sse2_composite_over_n_8_0565), PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, sse2_composite_over_n_8888), PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, sse2_composite_over_n_8888), PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, sse2_composite_over_n_0565), PIXMAN_STD_FAST_PATH (OVER, solid, null, b5g6r5, sse2_composite_over_n_0565), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, sse2_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, sse2_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, sse2_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, sse2_composite_over_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, sse2_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, sse2_composite_over_8888_0565), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, sse2_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, sse2_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, sse2_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, sse2_composite_over_n_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, sse2_composite_over_8888_8888_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, sse2_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, sse2_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, sse2_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, sse2_composite_over_8888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, sse2_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, sse2_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, sse2_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, sse2_composite_over_x888_8_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, a8r8g8b8, sse2_composite_over_x888_n_8888), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, x8r8g8b8, sse2_composite_over_x888_n_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, a8b8g8r8, sse2_composite_over_x888_n_8888), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, x8b8g8r8, sse2_composite_over_x888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, sse2_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, sse2_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, sse2_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, sse2_composite_over_8888_n_8888), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, sse2_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, sse2_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, sse2_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, sse2_composite_over_n_8888_8888_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, sse2_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, sse2_composite_over_n_8888_0565_ca), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, a8r8g8b8, sse2_composite_over_pixbuf_8888), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, x8r8g8b8, sse2_composite_over_pixbuf_8888), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, a8b8g8r8, sse2_composite_over_pixbuf_8888), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, x8b8g8r8, sse2_composite_over_pixbuf_8888), PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, r5g6b5, sse2_composite_over_pixbuf_0565), PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, b5g6r5, sse2_composite_over_pixbuf_0565), PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), /* PIXMAN_OP_OVER_REVERSE */ PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, sse2_composite_over_reverse_n_8888), PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, sse2_composite_over_reverse_n_8888), /* PIXMAN_OP_ADD */ PIXMAN_STD_FAST_PATH_CA (ADD, solid, a8r8g8b8, a8r8g8b8, sse2_composite_add_n_8888_8888_ca), PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, sse2_composite_add_8_8), PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, sse2_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, sse2_composite_add_8888_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, sse2_composite_add_n_8_8), PIXMAN_STD_FAST_PATH (ADD, solid, null, a8, sse2_composite_add_n_8), PIXMAN_STD_FAST_PATH (ADD, solid, null, x8r8g8b8, sse2_composite_add_n_8888), PIXMAN_STD_FAST_PATH (ADD, solid, null, a8r8g8b8, sse2_composite_add_n_8888), PIXMAN_STD_FAST_PATH (ADD, solid, null, x8b8g8r8, sse2_composite_add_n_8888), PIXMAN_STD_FAST_PATH (ADD, solid, null, a8b8g8r8, sse2_composite_add_n_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8r8g8b8, sse2_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, sse2_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8b8g8r8, sse2_composite_add_n_8_8888), PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, sse2_composite_add_n_8_8888), /* PIXMAN_OP_SRC */ PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, sse2_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, sse2_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, sse2_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, sse2_composite_src_n_8_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, sse2_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, sse2_composite_src_x888_8888), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, sse2_composite_copy_area), PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, sse2_composite_copy_area), /* PIXMAN_OP_IN */ PIXMAN_STD_FAST_PATH (IN, a8, null, a8, sse2_composite_in_8_8), PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, sse2_composite_in_n_8_8), PIXMAN_STD_FAST_PATH (IN, solid, null, a8, sse2_composite_in_n_8), SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888), SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_n_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888), SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_n_8888), SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888), SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8_8888), SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8_8888), { PIXMAN_OP_NONE }, }; static uint32_t * sse2_fetch_x8r8g8b8 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; __m128i ff000000 = mask_ff000000; uint32_t *dst = iter->buffer; uint32_t *src = (uint32_t *)iter->bits; iter->bits += iter->stride; while (w && ((uintptr_t)dst) & 0x0f) { *dst++ = (*src++) | 0xff000000; w--; } while (w >= 4) { save_128_aligned ( (__m128i *)dst, _mm_or_si128 ( load_128_unaligned ((__m128i *)src), ff000000)); dst += 4; src += 4; w -= 4; } while (w) { *dst++ = (*src++) | 0xff000000; w--; } return iter->buffer; } static uint32_t * sse2_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; uint32_t *dst = iter->buffer; uint16_t *src = (uint16_t *)iter->bits; __m128i ff000000 = mask_ff000000; iter->bits += iter->stride; while (w && ((uintptr_t)dst) & 0x0f) { uint16_t s = *src++; *dst++ = convert_0565_to_8888 (s); w--; } while (w >= 8) { __m128i lo, hi, s; s = _mm_loadu_si128 ((__m128i *)src); lo = unpack_565_to_8888 (_mm_unpacklo_epi16 (s, _mm_setzero_si128 ())); hi = unpack_565_to_8888 (_mm_unpackhi_epi16 (s, _mm_setzero_si128 ())); save_128_aligned ((__m128i *)(dst + 0), _mm_or_si128 (lo, ff000000)); save_128_aligned ((__m128i *)(dst + 4), _mm_or_si128 (hi, ff000000)); dst += 8; src += 8; w -= 8; } while (w) { uint16_t s = *src++; *dst++ = convert_0565_to_8888 (s); w--; } return iter->buffer; } static uint32_t * sse2_fetch_a8 (pixman_iter_t *iter, const uint32_t *mask) { int w = iter->width; uint32_t *dst = iter->buffer; uint8_t *src = iter->bits; __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; iter->bits += iter->stride; while (w && (((uintptr_t)dst) & 15)) { *dst++ = *(src++) << 24; w--; } while (w >= 16) { xmm0 = _mm_loadu_si128((__m128i *)src); xmm1 = _mm_unpacklo_epi8 (_mm_setzero_si128(), xmm0); xmm2 = _mm_unpackhi_epi8 (_mm_setzero_si128(), xmm0); xmm3 = _mm_unpacklo_epi16 (_mm_setzero_si128(), xmm1); xmm4 = _mm_unpackhi_epi16 (_mm_setzero_si128(), xmm1); xmm5 = _mm_unpacklo_epi16 (_mm_setzero_si128(), xmm2); xmm6 = _mm_unpackhi_epi16 (_mm_setzero_si128(), xmm2); _mm_store_si128(((__m128i *)(dst + 0)), xmm3); _mm_store_si128(((__m128i *)(dst + 4)), xmm4); _mm_store_si128(((__m128i *)(dst + 8)), xmm5); _mm_store_si128(((__m128i *)(dst + 12)), xmm6); dst += 16; src += 16; w -= 16; } while (w) { *dst++ = *(src++) << 24; w--; } return iter->buffer; } typedef struct { pixman_format_code_t format; pixman_iter_get_scanline_t get_scanline; } fetcher_info_t; static const fetcher_info_t fetchers[] = { { PIXMAN_x8r8g8b8, sse2_fetch_x8r8g8b8 }, { PIXMAN_r5g6b5, sse2_fetch_r5g6b5 }, { PIXMAN_a8, sse2_fetch_a8 }, { PIXMAN_null } }; static pixman_bool_t sse2_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter) { pixman_image_t *image = iter->image; #define FLAGS \ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) if ((iter->iter_flags & ITER_NARROW) && (iter->image_flags & FLAGS) == FLAGS) { const fetcher_info_t *f; for (f = &fetchers[0]; f->format != PIXMAN_null; f++) { if (image->common.extended_format_code == f->format) { uint8_t *b = (uint8_t *)image->bits.bits; int s = image->bits.rowstride * 4; iter->bits = b + s * iter->y + iter->x * PIXMAN_FORMAT_BPP (f->format) / 8; iter->stride = s; iter->get_scanline = f->get_scanline; return TRUE; } } } return FALSE; } #if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) __attribute__((__force_align_arg_pointer__)) #endif pixman_implementation_t * _pixman_implementation_create_sse2 (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, sse2_fast_paths); /* SSE2 constants */ mask_565_r = create_mask_2x32_128 (0x00f80000, 0x00f80000); mask_565_g1 = create_mask_2x32_128 (0x00070000, 0x00070000); mask_565_g2 = create_mask_2x32_128 (0x000000e0, 0x000000e0); mask_565_b = create_mask_2x32_128 (0x0000001f, 0x0000001f); mask_red = create_mask_2x32_128 (0x00f80000, 0x00f80000); mask_green = create_mask_2x32_128 (0x0000fc00, 0x0000fc00); mask_blue = create_mask_2x32_128 (0x000000f8, 0x000000f8); mask_565_fix_rb = create_mask_2x32_128 (0x00e000e0, 0x00e000e0); mask_565_fix_g = create_mask_2x32_128 (0x0000c000, 0x0000c000); mask_0080 = create_mask_16_128 (0x0080); mask_00ff = create_mask_16_128 (0x00ff); mask_0101 = create_mask_16_128 (0x0101); mask_ffff = create_mask_16_128 (0xffff); mask_ff000000 = create_mask_2x32_128 (0xff000000, 0xff000000); mask_alpha = create_mask_2x32_128 (0x00ff0000, 0x00000000); mask_565_rb = create_mask_2x32_128 (0x00f800f8, 0x00f800f8); mask_565_pack_multiplier = create_mask_2x32_128 (0x20000004, 0x20000004); /* Set up function pointers */ imp->combine_32[PIXMAN_OP_OVER] = sse2_combine_over_u; imp->combine_32[PIXMAN_OP_OVER_REVERSE] = sse2_combine_over_reverse_u; imp->combine_32[PIXMAN_OP_IN] = sse2_combine_in_u; imp->combine_32[PIXMAN_OP_IN_REVERSE] = sse2_combine_in_reverse_u; imp->combine_32[PIXMAN_OP_OUT] = sse2_combine_out_u; imp->combine_32[PIXMAN_OP_OUT_REVERSE] = sse2_combine_out_reverse_u; imp->combine_32[PIXMAN_OP_ATOP] = sse2_combine_atop_u; imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = sse2_combine_atop_reverse_u; imp->combine_32[PIXMAN_OP_XOR] = sse2_combine_xor_u; imp->combine_32[PIXMAN_OP_ADD] = sse2_combine_add_u; imp->combine_32[PIXMAN_OP_SATURATE] = sse2_combine_saturate_u; imp->combine_32_ca[PIXMAN_OP_SRC] = sse2_combine_src_ca; imp->combine_32_ca[PIXMAN_OP_OVER] = sse2_combine_over_ca; imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = sse2_combine_over_reverse_ca; imp->combine_32_ca[PIXMAN_OP_IN] = sse2_combine_in_ca; imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = sse2_combine_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_OUT] = sse2_combine_out_ca; imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = sse2_combine_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_ATOP] = sse2_combine_atop_ca; imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = sse2_combine_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_XOR] = sse2_combine_xor_ca; imp->combine_32_ca[PIXMAN_OP_ADD] = sse2_combine_add_ca; imp->blt = sse2_blt; imp->fill = sse2_fill; imp->src_iter_init = sse2_src_iter_init; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-timer.c000066400000000000000000000035221271037650300257640ustar00rootroot00000000000000/* * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" #ifdef PIXMAN_TIMERS static pixman_timer_t *timers; static void dump_timers (void) { pixman_timer_t *timer; for (timer = timers; timer != NULL; timer = timer->next) { printf ("%s: total: %llu n: %llu avg: %f\n", timer->name, timer->total, timer->n_times, timer->total / (double)timer->n_times); } } void pixman_timer_register (pixman_timer_t *timer) { static int initialized; int atexit (void (*function)(void)); if (!initialized) { atexit (dump_timers); initialized = 1; } timer->next = timers; timers = timer; } #endif Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-trap.c000066400000000000000000000407261271037650300256210ustar00rootroot00000000000000/* * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc. * Copyright © 2004 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" /* * Compute the smallest value greater than or equal to y which is on a * grid row. */ PIXMAN_EXPORT pixman_fixed_t pixman_sample_ceil_y (pixman_fixed_t y, int n) { pixman_fixed_t f = pixman_fixed_frac (y); pixman_fixed_t i = pixman_fixed_floor (y); f = DIV (f - Y_FRAC_FIRST (n) + (STEP_Y_SMALL (n) - pixman_fixed_e), STEP_Y_SMALL (n)) * STEP_Y_SMALL (n) + Y_FRAC_FIRST (n); if (f > Y_FRAC_LAST (n)) { if (pixman_fixed_to_int (i) == 0x7fff) { f = 0xffff; /* saturate */ } else { f = Y_FRAC_FIRST (n); i += pixman_fixed_1; } } return (i | f); } /* * Compute the largest value strictly less than y which is on a * grid row. */ PIXMAN_EXPORT pixman_fixed_t pixman_sample_floor_y (pixman_fixed_t y, int n) { pixman_fixed_t f = pixman_fixed_frac (y); pixman_fixed_t i = pixman_fixed_floor (y); f = DIV (f - pixman_fixed_e - Y_FRAC_FIRST (n), STEP_Y_SMALL (n)) * STEP_Y_SMALL (n) + Y_FRAC_FIRST (n); if (f < Y_FRAC_FIRST (n)) { if (pixman_fixed_to_int (i) == 0x8000) { f = 0; /* saturate */ } else { f = Y_FRAC_LAST (n); i -= pixman_fixed_1; } } return (i | f); } /* * Step an edge by any amount (including negative values) */ PIXMAN_EXPORT void pixman_edge_step (pixman_edge_t *e, int n) { pixman_fixed_48_16_t ne; e->x += n * e->stepx; ne = e->e + n * (pixman_fixed_48_16_t) e->dx; if (n >= 0) { if (ne > 0) { int nx = (ne + e->dy - 1) / e->dy; e->e = ne - nx * (pixman_fixed_48_16_t) e->dy; e->x += nx * e->signdx; } } else { if (ne <= -e->dy) { int nx = (-ne) / e->dy; e->e = ne + nx * (pixman_fixed_48_16_t) e->dy; e->x -= nx * e->signdx; } } } /* * A private routine to initialize the multi-step * elements of an edge structure */ static void _pixman_edge_multi_init (pixman_edge_t * e, int n, pixman_fixed_t *stepx_p, pixman_fixed_t *dx_p) { pixman_fixed_t stepx; pixman_fixed_48_16_t ne; ne = n * (pixman_fixed_48_16_t) e->dx; stepx = n * e->stepx; if (ne > 0) { int nx = ne / e->dy; ne -= nx * (pixman_fixed_48_16_t)e->dy; stepx += nx * e->signdx; } *dx_p = ne; *stepx_p = stepx; } /* * Initialize one edge structure given the line endpoints and a * starting y value */ PIXMAN_EXPORT void pixman_edge_init (pixman_edge_t *e, int n, pixman_fixed_t y_start, pixman_fixed_t x_top, pixman_fixed_t y_top, pixman_fixed_t x_bot, pixman_fixed_t y_bot) { pixman_fixed_t dx, dy; e->x = x_top; e->e = 0; dx = x_bot - x_top; dy = y_bot - y_top; e->dy = dy; e->dx = 0; if (dy) { if (dx >= 0) { e->signdx = 1; e->stepx = dx / dy; e->dx = dx % dy; e->e = -dy; } else { e->signdx = -1; e->stepx = -(-dx / dy); e->dx = -dx % dy; e->e = 0; } _pixman_edge_multi_init (e, STEP_Y_SMALL (n), &e->stepx_small, &e->dx_small); _pixman_edge_multi_init (e, STEP_Y_BIG (n), &e->stepx_big, &e->dx_big); } pixman_edge_step (e, y_start - y_top); } /* * Initialize one edge structure given a line, starting y value * and a pixel offset for the line */ PIXMAN_EXPORT void pixman_line_fixed_edge_init (pixman_edge_t * e, int n, pixman_fixed_t y, const pixman_line_fixed_t *line, int x_off, int y_off) { pixman_fixed_t x_off_fixed = pixman_int_to_fixed (x_off); pixman_fixed_t y_off_fixed = pixman_int_to_fixed (y_off); const pixman_point_fixed_t *top, *bot; if (line->p1.y <= line->p2.y) { top = &line->p1; bot = &line->p2; } else { top = &line->p2; bot = &line->p1; } pixman_edge_init (e, n, y, top->x + x_off_fixed, top->y + y_off_fixed, bot->x + x_off_fixed, bot->y + y_off_fixed); } PIXMAN_EXPORT void pixman_add_traps (pixman_image_t * image, int16_t x_off, int16_t y_off, int ntrap, const pixman_trap_t *traps) { int bpp; int height; pixman_fixed_t x_off_fixed; pixman_fixed_t y_off_fixed; pixman_edge_t l, r; pixman_fixed_t t, b; _pixman_image_validate (image); height = image->bits.height; bpp = PIXMAN_FORMAT_BPP (image->bits.format); x_off_fixed = pixman_int_to_fixed (x_off); y_off_fixed = pixman_int_to_fixed (y_off); while (ntrap--) { t = traps->top.y + y_off_fixed; if (t < 0) t = 0; t = pixman_sample_ceil_y (t, bpp); b = traps->bot.y + y_off_fixed; if (pixman_fixed_to_int (b) >= height) b = pixman_int_to_fixed (height) - 1; b = pixman_sample_floor_y (b, bpp); if (b >= t) { /* initialize edge walkers */ pixman_edge_init (&l, bpp, t, traps->top.l + x_off_fixed, traps->top.y + y_off_fixed, traps->bot.l + x_off_fixed, traps->bot.y + y_off_fixed); pixman_edge_init (&r, bpp, t, traps->top.r + x_off_fixed, traps->top.y + y_off_fixed, traps->bot.r + x_off_fixed, traps->bot.y + y_off_fixed); pixman_rasterize_edges (image, &l, &r, t, b); } traps++; } } #if 0 static void dump_image (pixman_image_t *image, const char * title) { int i, j; if (!image->type == BITS) printf ("%s is not a regular image\n", title); if (!image->bits.format == PIXMAN_a8) printf ("%s is not an alpha mask\n", title); printf ("\n\n\n%s: \n", title); for (i = 0; i < image->bits.height; ++i) { uint8_t *line = (uint8_t *)&(image->bits.bits[i * image->bits.rowstride]); for (j = 0; j < image->bits.width; ++j) printf ("%c", line[j] ? '#' : ' '); printf ("\n"); } } #endif PIXMAN_EXPORT void pixman_add_trapezoids (pixman_image_t * image, int16_t x_off, int y_off, int ntraps, const pixman_trapezoid_t *traps) { int i; #if 0 dump_image (image, "before"); #endif for (i = 0; i < ntraps; ++i) { const pixman_trapezoid_t *trap = &(traps[i]); if (!pixman_trapezoid_valid (trap)) continue; pixman_rasterize_trapezoid (image, trap, x_off, y_off); } #if 0 dump_image (image, "after"); #endif } PIXMAN_EXPORT void pixman_rasterize_trapezoid (pixman_image_t * image, const pixman_trapezoid_t *trap, int x_off, int y_off) { int bpp; int height; pixman_fixed_t y_off_fixed; pixman_edge_t l, r; pixman_fixed_t t, b; return_if_fail (image->type == BITS); _pixman_image_validate (image); if (!pixman_trapezoid_valid (trap)) return; height = image->bits.height; bpp = PIXMAN_FORMAT_BPP (image->bits.format); y_off_fixed = pixman_int_to_fixed (y_off); t = trap->top + y_off_fixed; if (t < 0) t = 0; t = pixman_sample_ceil_y (t, bpp); b = trap->bottom + y_off_fixed; if (pixman_fixed_to_int (b) >= height) b = pixman_int_to_fixed (height) - 1; b = pixman_sample_floor_y (b, bpp); if (b >= t) { /* initialize edge walkers */ pixman_line_fixed_edge_init (&l, bpp, t, &trap->left, x_off, y_off); pixman_line_fixed_edge_init (&r, bpp, t, &trap->right, x_off, y_off); pixman_rasterize_edges (image, &l, &r, t, b); } } static const pixman_bool_t zero_src_has_no_effect[PIXMAN_N_OPERATORS] = { FALSE, /* Clear 0 0 */ FALSE, /* Src 1 0 */ TRUE, /* Dst 0 1 */ TRUE, /* Over 1 1-Aa */ TRUE, /* OverReverse 1-Ab 1 */ FALSE, /* In Ab 0 */ FALSE, /* InReverse 0 Aa */ FALSE, /* Out 1-Ab 0 */ TRUE, /* OutReverse 0 1-Aa */ TRUE, /* Atop Ab 1-Aa */ FALSE, /* AtopReverse 1-Ab Aa */ TRUE, /* Xor 1-Ab 1-Aa */ TRUE, /* Add 1 1 */ }; static pixman_bool_t get_trap_extents (pixman_op_t op, pixman_image_t *dest, const pixman_trapezoid_t *traps, int n_traps, pixman_box32_t *box) { int i; /* When the operator is such that a zero source has an * effect on the underlying image, we have to * composite across the entire destination */ if (!zero_src_has_no_effect [op]) { box->x1 = 0; box->y1 = 0; box->x2 = dest->bits.width; box->y2 = dest->bits.height; return TRUE; } box->x1 = INT32_MAX; box->y1 = INT32_MAX; box->x2 = INT32_MIN; box->y2 = INT32_MIN; for (i = 0; i < n_traps; ++i) { const pixman_trapezoid_t *trap = &(traps[i]); int y1, y2; if (!pixman_trapezoid_valid (trap)) continue; y1 = pixman_fixed_to_int (trap->top); if (y1 < box->y1) box->y1 = y1; y2 = pixman_fixed_to_int (pixman_fixed_ceil (trap->bottom)); if (y2 > box->y2) box->y2 = y2; #define EXTEND_MIN(x) \ if (pixman_fixed_to_int ((x)) < box->x1) \ box->x1 = pixman_fixed_to_int ((x)); #define EXTEND_MAX(x) \ if (pixman_fixed_to_int (pixman_fixed_ceil ((x))) > box->x2) \ box->x2 = pixman_fixed_to_int (pixman_fixed_ceil ((x))); #define EXTEND(x) \ EXTEND_MIN(x); \ EXTEND_MAX(x); EXTEND(trap->left.p1.x); EXTEND(trap->left.p2.x); EXTEND(trap->right.p1.x); EXTEND(trap->right.p2.x); } if (box->x1 >= box->x2 || box->y1 >= box->y2) return FALSE; return TRUE; } /* * pixman_composite_trapezoids() * * All the trapezoids are conceptually rendered to an infinitely big image. * The (0, 0) coordinates of this image are then aligned with the (x, y) * coordinates of the source image, and then both images are aligned with * the (x, y) coordinates of the destination. Then these three images are * composited across the entire destination. */ PIXMAN_EXPORT void pixman_composite_trapezoids (pixman_op_t op, pixman_image_t * src, pixman_image_t * dst, pixman_format_code_t mask_format, int x_src, int y_src, int x_dst, int y_dst, int n_traps, const pixman_trapezoid_t * traps) { int i; return_if_fail (PIXMAN_FORMAT_TYPE (mask_format) == PIXMAN_TYPE_A); if (n_traps <= 0) return; _pixman_image_validate (src); _pixman_image_validate (dst); if (op == PIXMAN_OP_ADD && (src->common.flags & FAST_PATH_IS_OPAQUE) && (mask_format == dst->common.extended_format_code) && !(dst->common.have_clip_region)) { for (i = 0; i < n_traps; ++i) { const pixman_trapezoid_t *trap = &(traps[i]); if (!pixman_trapezoid_valid (trap)) continue; pixman_rasterize_trapezoid (dst, trap, x_dst, y_dst); } } else { pixman_image_t *tmp; pixman_box32_t box; int i; if (!get_trap_extents (op, dst, traps, n_traps, &box)) return; if (!(tmp = pixman_image_create_bits ( mask_format, box.x2 - box.x1, box.y2 - box.y1, NULL, -1))) return; for (i = 0; i < n_traps; ++i) { const pixman_trapezoid_t *trap = &(traps[i]); if (!pixman_trapezoid_valid (trap)) continue; pixman_rasterize_trapezoid (tmp, trap, - box.x1, - box.y1); } pixman_image_composite (op, src, tmp, dst, x_src + box.x1, y_src + box.y1, 0, 0, x_dst + box.x1, y_dst + box.y1, box.x2 - box.x1, box.y2 - box.y1); pixman_image_unref (tmp); } } static int greater_y (const pixman_point_fixed_t *a, const pixman_point_fixed_t *b) { if (a->y == b->y) return a->x > b->x; return a->y > b->y; } /* * Note that the definition of this function is a bit odd because * of the X coordinate space (y increasing downwards). */ static int clockwise (const pixman_point_fixed_t *ref, const pixman_point_fixed_t *a, const pixman_point_fixed_t *b) { pixman_point_fixed_t ad, bd; ad.x = a->x - ref->x; ad.y = a->y - ref->y; bd.x = b->x - ref->x; bd.y = b->y - ref->y; return ((pixman_fixed_32_32_t) bd.y * ad.x - (pixman_fixed_32_32_t) ad.y * bd.x) < 0; } static void triangle_to_trapezoids (const pixman_triangle_t *tri, pixman_trapezoid_t *traps) { const pixman_point_fixed_t *top, *left, *right, *tmp; top = &tri->p1; left = &tri->p2; right = &tri->p3; if (greater_y (top, left)) { tmp = left; left = top; top = tmp; } if (greater_y (top, right)) { tmp = right; right = top; top = tmp; } if (clockwise (top, right, left)) { tmp = right; right = left; left = tmp; } /* * Two cases: * * + + * / \ / \ * / \ / \ * / + + \ * / -- -- \ * / -- -- \ * / --- --- \ * +-- --+ */ traps->top = top->y; traps->left.p1 = *top; traps->left.p2 = *left; traps->right.p1 = *top; traps->right.p2 = *right; if (right->y < left->y) traps->bottom = right->y; else traps->bottom = left->y; traps++; *traps = *(traps - 1); if (right->y < left->y) { traps->top = right->y; traps->bottom = left->y; traps->right.p1 = *right; traps->right.p2 = *left; } else { traps->top = left->y; traps->bottom = right->y; traps->left.p1 = *left; traps->left.p2 = *right; } } static pixman_trapezoid_t * convert_triangles (int n_tris, const pixman_triangle_t *tris) { pixman_trapezoid_t *traps; int i; if (n_tris <= 0) return NULL; traps = pixman_malloc_ab (n_tris, 2 * sizeof (pixman_trapezoid_t)); if (!traps) return NULL; for (i = 0; i < n_tris; ++i) triangle_to_trapezoids (&(tris[i]), traps + 2 * i); return traps; } PIXMAN_EXPORT void pixman_composite_triangles (pixman_op_t op, pixman_image_t * src, pixman_image_t * dst, pixman_format_code_t mask_format, int x_src, int y_src, int x_dst, int y_dst, int n_tris, const pixman_triangle_t * tris) { pixman_trapezoid_t *traps; if ((traps = convert_triangles (n_tris, tris))) { pixman_composite_trapezoids (op, src, dst, mask_format, x_src, y_src, x_dst, y_dst, n_tris * 2, traps); free (traps); } } PIXMAN_EXPORT void pixman_add_triangles (pixman_image_t *image, int32_t x_off, int32_t y_off, int n_tris, const pixman_triangle_t *tris) { pixman_trapezoid_t *traps; if ((traps = convert_triangles (n_tris, tris))) { pixman_add_trapezoids (image, x_off, y_off, n_tris * 2, traps); free (traps); } } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-utils.c000066400000000000000000000165321271037650300260110ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 1999 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" pixman_bool_t _pixman_multiply_overflows_size (size_t a, size_t b) { return a >= SIZE_MAX / b; } pixman_bool_t _pixman_multiply_overflows_int (unsigned int a, unsigned int b) { return a >= INT32_MAX / b; } pixman_bool_t _pixman_addition_overflows_int (unsigned int a, unsigned int b) { return a > INT32_MAX - b; } void * pixman_malloc_ab (unsigned int a, unsigned int b) { if (a >= INT32_MAX / b) return NULL; return malloc (a * b); } void * pixman_malloc_abc (unsigned int a, unsigned int b, unsigned int c) { if (a >= INT32_MAX / b) return NULL; else if (a * b >= INT32_MAX / c) return NULL; else return malloc (a * b * c); } static force_inline uint16_t float_to_unorm (float f, int n_bits) { uint32_t u; if (f > 1.0) f = 1.0; if (f < 0.0) f = 0.0; u = f * (1 << n_bits); u -= (u >> n_bits); return u; } static force_inline float unorm_to_float (uint16_t u, int n_bits) { uint32_t m = ((1 << n_bits) - 1); return (u & m) * (1.f / (float)m); } /* * This function expands images from a8r8g8b8 to argb_t. To preserve * precision, it needs to know from which source format the a8r8g8b8 pixels * originally came. * * For example, if the source was PIXMAN_x1r5g5b5 and the red component * contained bits 12345, then the 8-bit value is 12345123. To correctly * expand this to floating point, it should be 12345 / 31.0 and not * 12345123 / 255.0. */ void pixman_expand_to_float (argb_t *dst, const uint32_t *src, pixman_format_code_t format, int width) { static const float multipliers[16] = { 0.0f, 1.0f / ((1 << 1) - 1), 1.0f / ((1 << 2) - 1), 1.0f / ((1 << 3) - 1), 1.0f / ((1 << 4) - 1), 1.0f / ((1 << 5) - 1), 1.0f / ((1 << 6) - 1), 1.0f / ((1 << 7) - 1), 1.0f / ((1 << 8) - 1), 1.0f / ((1 << 9) - 1), 1.0f / ((1 << 10) - 1), 1.0f / ((1 << 11) - 1), 1.0f / ((1 << 12) - 1), 1.0f / ((1 << 13) - 1), 1.0f / ((1 << 14) - 1), 1.0f / ((1 << 15) - 1), }; int a_size, r_size, g_size, b_size; int a_shift, r_shift, g_shift, b_shift; float a_mul, r_mul, g_mul, b_mul; uint32_t a_mask, r_mask, g_mask, b_mask; int i; if (!PIXMAN_FORMAT_VIS (format)) format = PIXMAN_a8r8g8b8; /* * Determine the sizes of each component and the masks and shifts * required to extract them from the source pixel. */ a_size = PIXMAN_FORMAT_A (format); r_size = PIXMAN_FORMAT_R (format); g_size = PIXMAN_FORMAT_G (format); b_size = PIXMAN_FORMAT_B (format); a_shift = 32 - a_size; r_shift = 24 - r_size; g_shift = 16 - g_size; b_shift = 8 - b_size; a_mask = ((1 << a_size) - 1); r_mask = ((1 << r_size) - 1); g_mask = ((1 << g_size) - 1); b_mask = ((1 << b_size) - 1); a_mul = multipliers[a_size]; r_mul = multipliers[r_size]; g_mul = multipliers[g_size]; b_mul = multipliers[b_size]; /* Start at the end so that we can do the expansion in place * when src == dst */ for (i = width - 1; i >= 0; i--) { const uint32_t pixel = src[i]; dst[i].a = a_mask? ((pixel >> a_shift) & a_mask) * a_mul : 1.0f; dst[i].r = ((pixel >> r_shift) & r_mask) * r_mul; dst[i].g = ((pixel >> g_shift) & g_mask) * g_mul; dst[i].b = ((pixel >> b_shift) & b_mask) * b_mul; } } uint16_t pixman_float_to_unorm (float f, int n_bits) { return float_to_unorm (f, n_bits); } float pixman_unorm_to_float (uint16_t u, int n_bits) { return unorm_to_float (u, n_bits); } void pixman_contract_from_float (uint32_t *dst, const argb_t *src, int width) { int i; for (i = 0; i < width; ++i) { uint8_t a, r, g, b; a = float_to_unorm (src[i].a, 8); r = float_to_unorm (src[i].r, 8); g = float_to_unorm (src[i].g, 8); b = float_to_unorm (src[i].b, 8); dst[i] = (a << 24) | (r << 16) | (g << 8) | (b << 0); } } uint32_t * _pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask) { return iter->buffer; } #define N_TMP_BOXES (16) pixman_bool_t pixman_region16_copy_from_region32 (pixman_region16_t *dst, pixman_region32_t *src) { int n_boxes, i; pixman_box32_t *boxes32; pixman_box16_t *boxes16; pixman_bool_t retval; boxes32 = pixman_region32_rectangles (src, &n_boxes); boxes16 = pixman_malloc_ab (n_boxes, sizeof (pixman_box16_t)); if (!boxes16) return FALSE; for (i = 0; i < n_boxes; ++i) { boxes16[i].x1 = boxes32[i].x1; boxes16[i].y1 = boxes32[i].y1; boxes16[i].x2 = boxes32[i].x2; boxes16[i].y2 = boxes32[i].y2; } pixman_region_fini (dst); retval = pixman_region_init_rects (dst, boxes16, n_boxes); free (boxes16); return retval; } pixman_bool_t pixman_region32_copy_from_region16 (pixman_region32_t *dst, pixman_region16_t *src) { int n_boxes, i; pixman_box16_t *boxes16; pixman_box32_t *boxes32; pixman_box32_t tmp_boxes[N_TMP_BOXES]; pixman_bool_t retval; boxes16 = pixman_region_rectangles (src, &n_boxes); if (n_boxes > N_TMP_BOXES) boxes32 = pixman_malloc_ab (n_boxes, sizeof (pixman_box32_t)); else boxes32 = tmp_boxes; if (!boxes32) return FALSE; for (i = 0; i < n_boxes; ++i) { boxes32[i].x1 = boxes16[i].x1; boxes32[i].y1 = boxes16[i].y1; boxes32[i].x2 = boxes16[i].x2; boxes32[i].y2 = boxes16[i].y2; } pixman_region32_fini (dst); retval = pixman_region32_init_rects (dst, boxes32, n_boxes); if (boxes32 != tmp_boxes) free (boxes32); return retval; } /* This function is exported for the sake of the test suite and not part * of the ABI. */ PIXMAN_EXPORT pixman_implementation_t * _pixman_internal_only_get_implementation (void) { return get_implementation (); } void _pixman_log_error (const char *function, const char *message) { static int n_messages = 0; if (n_messages < 10) { fprintf (stderr, "*** BUG ***\n" "In %s: %s\n" "Set a breakpoint on '_pixman_log_error' to debug\n\n", function, message); n_messages++; } } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-version.h000066400000000000000000000033131271037650300263340ustar00rootroot00000000000000/* * Copyright © 2008 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Author: Carl D. Worth */ #ifndef PIXMAN_VERSION_H__ #define PIXMAN_VERSION_H__ #ifndef PIXMAN_H__ # error pixman-version.h should only be included by pixman.h #endif #define PIXMAN_VERSION_MAJOR 0 #define PIXMAN_VERSION_MINOR 30 #define PIXMAN_VERSION_MICRO 2 #define PIXMAN_VERSION_STRING "0.30.2" #define PIXMAN_VERSION_ENCODE(major, minor, micro) ( \ ((major) * 10000) \ + ((minor) * 100) \ + ((micro) * 1)) #define PIXMAN_VERSION PIXMAN_VERSION_ENCODE( \ PIXMAN_VERSION_MAJOR, \ PIXMAN_VERSION_MINOR, \ PIXMAN_VERSION_MICRO) #endif /* PIXMAN_VERSION_H__ */ Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-vmx.c000066400000000000000000001174511271037650300254650ustar00rootroot00000000000000/* * Copyright © 2007 Luca Barbato * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Luca Barbato not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Luca Barbato makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Luca Barbato (lu_zero@gentoo.org) * * Based on fbmmx.c by Owen Taylor, Søren Sandmann and Nicholas Miell */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include "pixman-combine32.h" #include #define AVV(x...) {x} static force_inline vector unsigned int splat_alpha (vector unsigned int pix) { return vec_perm (pix, pix, (vector unsigned char)AVV ( 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0C, 0x0C, 0x0C)); } static force_inline vector unsigned int pix_multiply (vector unsigned int p, vector unsigned int a) { vector unsigned short hi, lo, mod; /* unpack to short */ hi = (vector unsigned short) vec_mergeh ((vector unsigned char)AVV (0), (vector unsigned char)p); mod = (vector unsigned short) vec_mergeh ((vector unsigned char)AVV (0), (vector unsigned char)a); hi = vec_mladd (hi, mod, (vector unsigned short) AVV (0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080)); hi = vec_adds (hi, vec_sr (hi, vec_splat_u16 (8))); hi = vec_sr (hi, vec_splat_u16 (8)); /* unpack to short */ lo = (vector unsigned short) vec_mergel ((vector unsigned char)AVV (0), (vector unsigned char)p); mod = (vector unsigned short) vec_mergel ((vector unsigned char)AVV (0), (vector unsigned char)a); lo = vec_mladd (lo, mod, (vector unsigned short) AVV (0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080)); lo = vec_adds (lo, vec_sr (lo, vec_splat_u16 (8))); lo = vec_sr (lo, vec_splat_u16 (8)); return (vector unsigned int)vec_packsu (hi, lo); } static force_inline vector unsigned int pix_add (vector unsigned int a, vector unsigned int b) { return (vector unsigned int)vec_adds ((vector unsigned char)a, (vector unsigned char)b); } static force_inline vector unsigned int pix_add_mul (vector unsigned int x, vector unsigned int a, vector unsigned int y, vector unsigned int b) { vector unsigned int t1, t2; t1 = pix_multiply (x, a); t2 = pix_multiply (y, b); return pix_add (t1, t2); } static force_inline vector unsigned int negate (vector unsigned int src) { return vec_nor (src, src); } /* dest*~srca + src */ static force_inline vector unsigned int over (vector unsigned int src, vector unsigned int srca, vector unsigned int dest) { vector unsigned char tmp = (vector unsigned char) pix_multiply (dest, negate (srca)); tmp = vec_adds ((vector unsigned char)src, tmp); return (vector unsigned int)tmp; } /* in == pix_multiply */ #define in_over(src, srca, mask, dest) \ over (pix_multiply (src, mask), \ pix_multiply (srca, mask), dest) #define COMPUTE_SHIFT_MASK(source) \ source ## _mask = vec_lvsl (0, source); #define COMPUTE_SHIFT_MASKS(dest, source) \ dest ## _mask = vec_lvsl (0, dest); \ source ## _mask = vec_lvsl (0, source); \ store_mask = vec_lvsr (0, dest); #define COMPUTE_SHIFT_MASKC(dest, source, mask) \ mask ## _mask = vec_lvsl (0, mask); \ dest ## _mask = vec_lvsl (0, dest); \ source ## _mask = vec_lvsl (0, source); \ store_mask = vec_lvsr (0, dest); /* notice you have to declare temp vars... * Note: tmp3 and tmp4 must remain untouched! */ #define LOAD_VECTORS(dest, source) \ tmp1 = (typeof(tmp1))vec_ld (0, source); \ tmp2 = (typeof(tmp2))vec_ld (15, source); \ tmp3 = (typeof(tmp3))vec_ld (0, dest); \ v ## source = (typeof(v ## source)) \ vec_perm (tmp1, tmp2, source ## _mask); \ tmp4 = (typeof(tmp4))vec_ld (15, dest); \ v ## dest = (typeof(v ## dest)) \ vec_perm (tmp3, tmp4, dest ## _mask); #define LOAD_VECTORSC(dest, source, mask) \ tmp1 = (typeof(tmp1))vec_ld (0, source); \ tmp2 = (typeof(tmp2))vec_ld (15, source); \ tmp3 = (typeof(tmp3))vec_ld (0, dest); \ v ## source = (typeof(v ## source)) \ vec_perm (tmp1, tmp2, source ## _mask); \ tmp4 = (typeof(tmp4))vec_ld (15, dest); \ tmp1 = (typeof(tmp1))vec_ld (0, mask); \ v ## dest = (typeof(v ## dest)) \ vec_perm (tmp3, tmp4, dest ## _mask); \ tmp2 = (typeof(tmp2))vec_ld (15, mask); \ v ## mask = (typeof(v ## mask)) \ vec_perm (tmp1, tmp2, mask ## _mask); #define LOAD_VECTORSM(dest, source, mask) \ LOAD_VECTORSC (dest, source, mask) \ v ## source = pix_multiply (v ## source, \ splat_alpha (v ## mask)); #define STORE_VECTOR(dest) \ edges = vec_perm (tmp4, tmp3, dest ## _mask); \ tmp3 = vec_perm ((vector unsigned char)v ## dest, edges, store_mask); \ tmp1 = vec_perm (edges, (vector unsigned char)v ## dest, store_mask); \ vec_st ((vector unsigned int) tmp3, 15, dest); \ vec_st ((vector unsigned int) tmp1, 0, dest); static void vmx_combine_over_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = over (vsrc, splat_alpha (vsrc), vdest); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); dest[i] = d; } } static void vmx_combine_over_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = over (vsrc, splat_alpha (vsrc), vdest); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t ia; UN8x4_MUL_UN8 (s, m); ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); dest[i] = d; } } static void vmx_combine_over_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_over_u_mask (dest, src, mask, width); else vmx_combine_over_u_no_mask (dest, src, width); } static void vmx_combine_over_reverse_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = over (vdest, splat_alpha (vdest), vsrc); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t ia = ALPHA_8 (~dest[i]); UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); dest[i] = s; } } static void vmx_combine_over_reverse_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = over (vdest, splat_alpha (vdest), vsrc); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t ia = ALPHA_8 (~dest[i]); UN8x4_MUL_UN8 (s, m); UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); dest[i] = s; } } static void vmx_combine_over_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_over_reverse_u_mask (dest, src, mask, width); else vmx_combine_over_reverse_u_no_mask (dest, src, width); } static void vmx_combine_in_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_multiply (vsrc, splat_alpha (vdest)); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t a = ALPHA_8 (dest[i]); UN8x4_MUL_UN8 (s, a); dest[i] = s; } } static void vmx_combine_in_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_multiply (vsrc, splat_alpha (vdest)); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t a = ALPHA_8 (dest[i]); UN8x4_MUL_UN8 (s, m); UN8x4_MUL_UN8 (s, a); dest[i] = s; } } static void vmx_combine_in_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_in_u_mask (dest, src, mask, width); else vmx_combine_in_u_no_mask (dest, src, width); } static void vmx_combine_in_reverse_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_multiply (vdest, splat_alpha (vsrc)); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t d = dest[i]; uint32_t a = ALPHA_8 (src[i]); UN8x4_MUL_UN8 (d, a); dest[i] = d; } } static void vmx_combine_in_reverse_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_multiply (vdest, splat_alpha (vsrc)); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t d = dest[i]; uint32_t a = src[i]; UN8x4_MUL_UN8 (a, m); a = ALPHA_8 (a); UN8x4_MUL_UN8 (d, a); dest[i] = d; } } static void vmx_combine_in_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_in_reverse_u_mask (dest, src, mask, width); else vmx_combine_in_reverse_u_no_mask (dest, src, width); } static void vmx_combine_out_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_multiply (vsrc, splat_alpha (negate (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t a = ALPHA_8 (~dest[i]); UN8x4_MUL_UN8 (s, a); dest[i] = s; } } static void vmx_combine_out_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_multiply (vsrc, splat_alpha (negate (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t a = ALPHA_8 (~dest[i]); UN8x4_MUL_UN8 (s, m); UN8x4_MUL_UN8 (s, a); dest[i] = s; } } static void vmx_combine_out_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_out_u_mask (dest, src, mask, width); else vmx_combine_out_u_no_mask (dest, src, width); } static void vmx_combine_out_reverse_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_multiply (vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t d = dest[i]; uint32_t a = ALPHA_8 (~src[i]); UN8x4_MUL_UN8 (d, a); dest[i] = d; } } static void vmx_combine_out_reverse_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_multiply (vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t d = dest[i]; uint32_t a = src[i]; UN8x4_MUL_UN8 (a, m); a = ALPHA_8 (~a); UN8x4_MUL_UN8 (d, a); dest[i] = d; } } static void vmx_combine_out_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_out_reverse_u_mask (dest, src, mask, width); else vmx_combine_out_reverse_u_no_mask (dest, src, width); } static void vmx_combine_atop_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_add_mul (vsrc, splat_alpha (vdest), vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t dest_a = ALPHA_8 (d); uint32_t src_ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); dest[i] = s; } } static void vmx_combine_atop_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_add_mul (vsrc, splat_alpha (vdest), vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t dest_a = ALPHA_8 (d); uint32_t src_ia; UN8x4_MUL_UN8 (s, m); src_ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); dest[i] = s; } } static void vmx_combine_atop_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_atop_u_mask (dest, src, mask, width); else vmx_combine_atop_u_no_mask (dest, src, width); } static void vmx_combine_atop_reverse_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_add_mul (vdest, splat_alpha (vsrc), vsrc, splat_alpha (negate (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t src_a = ALPHA_8 (s); uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); dest[i] = s; } } static void vmx_combine_atop_reverse_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_add_mul (vdest, splat_alpha (vsrc), vsrc, splat_alpha (negate (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t src_a; uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8 (s, m); src_a = ALPHA_8 (s); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); dest[i] = s; } } static void vmx_combine_atop_reverse_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_atop_reverse_u_mask (dest, src, mask, width); else vmx_combine_atop_reverse_u_no_mask (dest, src, width); } static void vmx_combine_xor_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_add_mul (vsrc, splat_alpha (negate (vdest)), vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t src_ia = ALPHA_8 (~s); uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); dest[i] = s; } } static void vmx_combine_xor_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_add_mul (vsrc, splat_alpha (negate (vdest)), vdest, splat_alpha (negate (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t src_ia; uint32_t dest_ia = ALPHA_8 (~d); UN8x4_MUL_UN8 (s, m); src_ia = ALPHA_8 (~s); UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); dest[i] = s; } } static void vmx_combine_xor_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_xor_u_mask (dest, src, mask, width); else vmx_combine_xor_u_no_mask (dest, src, width); } static void vmx_combine_add_u_no_mask (uint32_t * dest, const uint32_t *src, int width) { int i; vector unsigned int vdest, vsrc; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKS (dest, src); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORS (dest, src); vdest = pix_add (vsrc, vdest); STORE_VECTOR (dest); src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t s = src[i]; uint32_t d = dest[i]; UN8x4_ADD_UN8x4 (d, s); dest[i] = d; } } static void vmx_combine_add_u_mask (uint32_t * dest, const uint32_t *src, const uint32_t *mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, src_mask, mask_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSM (dest, src, mask); vdest = pix_add (vsrc, vdest); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t m = ALPHA_8 (mask[i]); uint32_t s = src[i]; uint32_t d = dest[i]; UN8x4_MUL_UN8 (s, m); UN8x4_ADD_UN8x4 (d, s); dest[i] = d; } } static void vmx_combine_add_u (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { if (mask) vmx_combine_add_u_mask (dest, src, mask, width); else vmx_combine_add_u_no_mask (dest, src, width); } static void vmx_combine_src_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_multiply (vsrc, vmask); STORE_VECTOR (dest); mask += 4; src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; UN8x4_MUL_UN8x4 (s, a); dest[i] = s; } } static void vmx_combine_over_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = in_over (vsrc, splat_alpha (vsrc), vmask, vdest); STORE_VECTOR (dest); mask += 4; src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (s); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ~a, s); dest[i] = d; } } static void vmx_combine_over_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = over (vdest, splat_alpha (vdest), pix_multiply (vsrc, vmask)); STORE_VECTOR (dest); mask += 4; src += 4; dest += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t ida = ALPHA_8 (~d); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8_ADD_UN8x4 (s, ida, d); dest[i] = s; } } static void vmx_combine_in_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_multiply (pix_multiply (vsrc, vmask), splat_alpha (vdest)); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t da = ALPHA_8 (dest[i]); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (s, da); dest[i] = s; } } static void vmx_combine_in_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_multiply (vdest, pix_multiply (vmask, splat_alpha (vsrc))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (src[i]); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4 (d, a); dest[i] = d; } } static void vmx_combine_out_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_multiply ( pix_multiply (vsrc, vmask), splat_alpha (negate (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t da = ALPHA_8 (~d); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (s, da); dest[i] = s; } } static void vmx_combine_out_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_multiply ( vdest, negate (pix_multiply (vmask, splat_alpha (vsrc)))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (s); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4 (d, ~a); dest[i] = d; } } static void vmx_combine_atop_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask, vsrca; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vsrca = splat_alpha (vsrc); vsrc = pix_multiply (vsrc, vmask); vmask = pix_multiply (vmask, vsrca); vdest = pix_add_mul (vsrc, splat_alpha (vdest), negate (vmask), vdest); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (s); uint32_t da = ALPHA_8 (d); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); dest[i] = d; } } static void vmx_combine_atop_reverse_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_add_mul (vdest, pix_multiply (vmask, splat_alpha (vsrc)), pix_multiply (vsrc, vmask), negate (splat_alpha (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (s); uint32_t da = ALPHA_8 (~d); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, a, s, da); dest[i] = d; } } static void vmx_combine_xor_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_add_mul (vdest, negate (pix_multiply (vmask, splat_alpha (vsrc))), pix_multiply (vsrc, vmask), negate (splat_alpha (vdest))); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; uint32_t sa = ALPHA_8 (s); uint32_t da = ALPHA_8 (~d); UN8x4_MUL_UN8x4 (s, a); UN8x4_MUL_UN8 (a, sa); UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); dest[i] = d; } } static void vmx_combine_add_ca (pixman_implementation_t *imp, pixman_op_t op, uint32_t * dest, const uint32_t * src, const uint32_t * mask, int width) { int i; vector unsigned int vdest, vsrc, vmask; vector unsigned char tmp1, tmp2, tmp3, tmp4, edges, dest_mask, mask_mask, src_mask, store_mask; COMPUTE_SHIFT_MASKC (dest, src, mask); /* printf ("%s\n",__PRETTY_FUNCTION__); */ for (i = width / 4; i > 0; i--) { LOAD_VECTORSC (dest, src, mask); vdest = pix_add (pix_multiply (vsrc, vmask), vdest); STORE_VECTOR (dest); src += 4; dest += 4; mask += 4; } for (i = width % 4; --i >= 0;) { uint32_t a = mask[i]; uint32_t s = src[i]; uint32_t d = dest[i]; UN8x4_MUL_UN8x4 (s, a); UN8x4_ADD_UN8x4 (s, d); dest[i] = s; } } static const pixman_fast_path_t vmx_fast_paths[] = { { PIXMAN_OP_NONE }, }; pixman_implementation_t * _pixman_implementation_create_vmx (pixman_implementation_t *fallback) { pixman_implementation_t *imp = _pixman_implementation_create (fallback, vmx_fast_paths); /* Set up function pointers */ imp->combine_32[PIXMAN_OP_OVER] = vmx_combine_over_u; imp->combine_32[PIXMAN_OP_OVER_REVERSE] = vmx_combine_over_reverse_u; imp->combine_32[PIXMAN_OP_IN] = vmx_combine_in_u; imp->combine_32[PIXMAN_OP_IN_REVERSE] = vmx_combine_in_reverse_u; imp->combine_32[PIXMAN_OP_OUT] = vmx_combine_out_u; imp->combine_32[PIXMAN_OP_OUT_REVERSE] = vmx_combine_out_reverse_u; imp->combine_32[PIXMAN_OP_ATOP] = vmx_combine_atop_u; imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = vmx_combine_atop_reverse_u; imp->combine_32[PIXMAN_OP_XOR] = vmx_combine_xor_u; imp->combine_32[PIXMAN_OP_ADD] = vmx_combine_add_u; imp->combine_32_ca[PIXMAN_OP_SRC] = vmx_combine_src_ca; imp->combine_32_ca[PIXMAN_OP_OVER] = vmx_combine_over_ca; imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = vmx_combine_over_reverse_ca; imp->combine_32_ca[PIXMAN_OP_IN] = vmx_combine_in_ca; imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = vmx_combine_in_reverse_ca; imp->combine_32_ca[PIXMAN_OP_OUT] = vmx_combine_out_ca; imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = vmx_combine_out_reverse_ca; imp->combine_32_ca[PIXMAN_OP_ATOP] = vmx_combine_atop_ca; imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = vmx_combine_atop_reverse_ca; imp->combine_32_ca[PIXMAN_OP_XOR] = vmx_combine_xor_ca; imp->combine_32_ca[PIXMAN_OP_ADD] = vmx_combine_add_ca; return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman-x86.c000066400000000000000000000131441271037650300252720ustar00rootroot00000000000000/* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #if defined(USE_X86_MMX) || defined (USE_SSE2) /* The CPU detection code needs to be in a file not compiled with * "-mmmx -msse", as gcc would generate CMOV instructions otherwise * that would lead to SIGILL instructions on old CPUs that don't have * it. */ typedef enum { X86_MMX = (1 << 0), X86_MMX_EXTENSIONS = (1 << 1), X86_SSE = (1 << 2) | X86_MMX_EXTENSIONS, X86_SSE2 = (1 << 3), X86_CMOV = (1 << 4) } cpu_features_t; #ifdef HAVE_GETISAX #include static cpu_features_t detect_cpu_features (void) { cpu_features_t features = 0; unsigned int result = 0; if (getisax (&result, 1)) { if (result & AV_386_CMOV) features |= X86_CMOV; if (result & AV_386_MMX) features |= X86_MMX; if (result & AV_386_AMD_MMX) features |= X86_MMX_EXTENSIONS; if (result & AV_386_SSE) features |= X86_SSE; if (result & AV_386_SSE2) features |= X86_SSE2; } return features; } #else #define _PIXMAN_X86_64 \ (defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)) static pixman_bool_t have_cpuid (void) { #if _PIXMAN_X86_64 || defined (_MSC_VER) return TRUE; #elif defined (__GNUC__) uint32_t result; __asm__ volatile ( "pushf" "\n\t" "pop %%eax" "\n\t" "mov %%eax, %%ecx" "\n\t" "xor $0x00200000, %%eax" "\n\t" "push %%eax" "\n\t" "popf" "\n\t" "pushf" "\n\t" "pop %%eax" "\n\t" "xor %%ecx, %%eax" "\n\t" "mov %%eax, %0" "\n\t" : "=r" (result) : : "%eax", "%ecx"); return !!result; #else #error "Unknown compiler" #endif } static void pixman_cpuid (uint32_t feature, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { #if defined (__GNUC__) #if _PIXMAN_X86_64 __asm__ volatile ( "cpuid" "\n\t" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) : "a" (feature)); #else /* On x86-32 we need to be careful about the handling of %ebx * and %esp. We can't declare either one as clobbered * since they are special registers (%ebx is the "PIC * register" holding an offset to global data, %esp the * stack pointer), so we need to make sure that %ebx is * preserved, and that %esp has its original value when * accessing the output operands. */ __asm__ volatile ( "xchg %%ebx, %1" "\n\t" "cpuid" "\n\t" "xchg %%ebx, %1" "\n\t" : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) : "a" (feature)); #endif #elif defined (_MSC_VER) int info[4]; __cpuid (info, feature); *a = info[0]; *b = info[1]; *c = info[2]; *d = info[3]; #else #error Unknown compiler #endif } static cpu_features_t detect_cpu_features (void) { uint32_t a, b, c, d; cpu_features_t features = 0; if (!have_cpuid()) return features; /* Get feature bits */ pixman_cpuid (0x01, &a, &b, &c, &d); if (d & (1 << 15)) features |= X86_CMOV; if (d & (1 << 23)) features |= X86_MMX; if (d & (1 << 25)) features |= X86_SSE; if (d & (1 << 26)) features |= X86_SSE2; /* Check for AMD specific features */ if ((features & X86_MMX) && !(features & X86_SSE)) { char vendor[13]; /* Get vendor string */ memset (vendor, 0, sizeof vendor); pixman_cpuid (0x00, &a, &b, &c, &d); memcpy (vendor + 0, &b, 4); memcpy (vendor + 4, &d, 4); memcpy (vendor + 8, &c, 4); if (strcmp (vendor, "AuthenticAMD") == 0 || strcmp (vendor, "Geode by NSC") == 0) { pixman_cpuid (0x80000000, &a, &b, &c, &d); if (a >= 0x80000001) { pixman_cpuid (0x80000001, &a, &b, &c, &d); if (d & (1 << 22)) features |= X86_MMX_EXTENSIONS; } } } return features; } #endif static pixman_bool_t have_feature (cpu_features_t feature) { static pixman_bool_t initialized; static cpu_features_t features; if (!initialized) { features = detect_cpu_features(); initialized = TRUE; } return (features & feature) == feature; } #endif pixman_implementation_t * _pixman_x86_get_implementations (pixman_implementation_t *imp) { #define MMX_BITS (X86_MMX | X86_MMX_EXTENSIONS) #define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2) #ifdef USE_X86_MMX if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS)) imp = _pixman_implementation_create_mmx (imp); #endif #ifdef USE_SSE2 if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS)) imp = _pixman_implementation_create_sse2 (imp); #endif return imp; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman.c000066400000000000000000001063161271037650300246530ustar00rootroot00000000000000/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ /* * Copyright © 2000 SuSE, Inc. * Copyright © 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #ifdef HAVE_CONFIG_H #include #endif #include "pixman-private.h" #include pixman_implementation_t *global_implementation; #ifdef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR static void __attribute__((constructor)) pixman_constructor (void) { global_implementation = _pixman_choose_implementation (); } #endif typedef struct operator_info_t operator_info_t; struct operator_info_t { uint8_t opaque_info[4]; }; #define PACK(neither, src, dest, both) \ {{ (uint8_t)PIXMAN_OP_ ## neither, \ (uint8_t)PIXMAN_OP_ ## src, \ (uint8_t)PIXMAN_OP_ ## dest, \ (uint8_t)PIXMAN_OP_ ## both }} static const operator_info_t operator_table[] = { /* Neither Opaque Src Opaque Dst Opaque Both Opaque */ PACK (CLEAR, CLEAR, CLEAR, CLEAR), PACK (SRC, SRC, SRC, SRC), PACK (DST, DST, DST, DST), PACK (OVER, SRC, OVER, SRC), PACK (OVER_REVERSE, OVER_REVERSE, DST, DST), PACK (IN, IN, SRC, SRC), PACK (IN_REVERSE, DST, IN_REVERSE, DST), PACK (OUT, OUT, CLEAR, CLEAR), PACK (OUT_REVERSE, CLEAR, OUT_REVERSE, CLEAR), PACK (ATOP, IN, OVER, SRC), PACK (ATOP_REVERSE, OVER_REVERSE, IN_REVERSE, DST), PACK (XOR, OUT, OUT_REVERSE, CLEAR), PACK (ADD, ADD, ADD, ADD), PACK (SATURATE, OVER_REVERSE, DST, DST), {{ 0 /* 0x0e */ }}, {{ 0 /* 0x0f */ }}, PACK (CLEAR, CLEAR, CLEAR, CLEAR), PACK (SRC, SRC, SRC, SRC), PACK (DST, DST, DST, DST), PACK (DISJOINT_OVER, DISJOINT_OVER, DISJOINT_OVER, DISJOINT_OVER), PACK (DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE), PACK (DISJOINT_IN, DISJOINT_IN, DISJOINT_IN, DISJOINT_IN), PACK (DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE), PACK (DISJOINT_OUT, DISJOINT_OUT, DISJOINT_OUT, DISJOINT_OUT), PACK (DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE), PACK (DISJOINT_ATOP, DISJOINT_ATOP, DISJOINT_ATOP, DISJOINT_ATOP), PACK (DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE), PACK (DISJOINT_XOR, DISJOINT_XOR, DISJOINT_XOR, DISJOINT_XOR), {{ 0 /* 0x1c */ }}, {{ 0 /* 0x1d */ }}, {{ 0 /* 0x1e */ }}, {{ 0 /* 0x1f */ }}, PACK (CLEAR, CLEAR, CLEAR, CLEAR), PACK (SRC, SRC, SRC, SRC), PACK (DST, DST, DST, DST), PACK (CONJOINT_OVER, CONJOINT_OVER, CONJOINT_OVER, CONJOINT_OVER), PACK (CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE), PACK (CONJOINT_IN, CONJOINT_IN, CONJOINT_IN, CONJOINT_IN), PACK (CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE), PACK (CONJOINT_OUT, CONJOINT_OUT, CONJOINT_OUT, CONJOINT_OUT), PACK (CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE), PACK (CONJOINT_ATOP, CONJOINT_ATOP, CONJOINT_ATOP, CONJOINT_ATOP), PACK (CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE), PACK (CONJOINT_XOR, CONJOINT_XOR, CONJOINT_XOR, CONJOINT_XOR), {{ 0 /* 0x2c */ }}, {{ 0 /* 0x2d */ }}, {{ 0 /* 0x2e */ }}, {{ 0 /* 0x2f */ }}, PACK (MULTIPLY, MULTIPLY, MULTIPLY, MULTIPLY), PACK (SCREEN, SCREEN, SCREEN, SCREEN), PACK (OVERLAY, OVERLAY, OVERLAY, OVERLAY), PACK (DARKEN, DARKEN, DARKEN, DARKEN), PACK (LIGHTEN, LIGHTEN, LIGHTEN, LIGHTEN), PACK (COLOR_DODGE, COLOR_DODGE, COLOR_DODGE, COLOR_DODGE), PACK (COLOR_BURN, COLOR_BURN, COLOR_BURN, COLOR_BURN), PACK (HARD_LIGHT, HARD_LIGHT, HARD_LIGHT, HARD_LIGHT), PACK (SOFT_LIGHT, SOFT_LIGHT, SOFT_LIGHT, SOFT_LIGHT), PACK (DIFFERENCE, DIFFERENCE, DIFFERENCE, DIFFERENCE), PACK (EXCLUSION, EXCLUSION, EXCLUSION, EXCLUSION), PACK (HSL_HUE, HSL_HUE, HSL_HUE, HSL_HUE), PACK (HSL_SATURATION, HSL_SATURATION, HSL_SATURATION, HSL_SATURATION), PACK (HSL_COLOR, HSL_COLOR, HSL_COLOR, HSL_COLOR), PACK (HSL_LUMINOSITY, HSL_LUMINOSITY, HSL_LUMINOSITY, HSL_LUMINOSITY), }; /* * Optimize the current operator based on opacity of source or destination * The output operator should be mathematically equivalent to the source. */ static pixman_op_t optimize_operator (pixman_op_t op, uint32_t src_flags, uint32_t mask_flags, uint32_t dst_flags) { pixman_bool_t is_source_opaque, is_dest_opaque; #define OPAQUE_SHIFT 13 COMPILE_TIME_ASSERT (FAST_PATH_IS_OPAQUE == (1 << OPAQUE_SHIFT)); is_dest_opaque = (dst_flags & FAST_PATH_IS_OPAQUE); is_source_opaque = ((src_flags & mask_flags) & FAST_PATH_IS_OPAQUE); is_dest_opaque >>= OPAQUE_SHIFT - 1; is_source_opaque >>= OPAQUE_SHIFT; return operator_table[op].opaque_info[is_dest_opaque | is_source_opaque]; } /* * Computing composite region */ static inline pixman_bool_t clip_general_image (pixman_region32_t * region, pixman_region32_t * clip, int dx, int dy) { if (pixman_region32_n_rects (region) == 1 && pixman_region32_n_rects (clip) == 1) { pixman_box32_t * rbox = pixman_region32_rectangles (region, NULL); pixman_box32_t * cbox = pixman_region32_rectangles (clip, NULL); int v; if (rbox->x1 < (v = cbox->x1 + dx)) rbox->x1 = v; if (rbox->x2 > (v = cbox->x2 + dx)) rbox->x2 = v; if (rbox->y1 < (v = cbox->y1 + dy)) rbox->y1 = v; if (rbox->y2 > (v = cbox->y2 + dy)) rbox->y2 = v; if (rbox->x1 >= rbox->x2 || rbox->y1 >= rbox->y2) { pixman_region32_init (region); return FALSE; } } else if (!pixman_region32_not_empty (clip)) { return FALSE; } else { if (dx || dy) pixman_region32_translate (region, -dx, -dy); if (!pixman_region32_intersect (region, region, clip)) return FALSE; if (dx || dy) pixman_region32_translate (region, dx, dy); } return pixman_region32_not_empty (region); } static inline pixman_bool_t clip_source_image (pixman_region32_t * region, pixman_image_t * image, int dx, int dy) { /* Source clips are ignored, unless they are explicitly turned on * and the clip in question was set by an X client. (Because if * the clip was not set by a client, then it is a hierarchy * clip and those should always be ignored for sources). */ if (!image->common.clip_sources || !image->common.client_clip) return TRUE; return clip_general_image (region, &image->common.clip_region, dx, dy); } /* * returns FALSE if the final region is empty. Indistinguishable from * an allocation failure, but rendering ignores those anyways. */ pixman_bool_t _pixman_compute_composite_region32 (pixman_region32_t * region, pixman_image_t * src_image, pixman_image_t * mask_image, pixman_image_t * dest_image, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height) { region->extents.x1 = dest_x; region->extents.x2 = dest_x + width; region->extents.y1 = dest_y; region->extents.y2 = dest_y + height; region->extents.x1 = MAX (region->extents.x1, 0); region->extents.y1 = MAX (region->extents.y1, 0); region->extents.x2 = MIN (region->extents.x2, dest_image->bits.width); region->extents.y2 = MIN (region->extents.y2, dest_image->bits.height); region->data = 0; /* Check for empty operation */ if (region->extents.x1 >= region->extents.x2 || region->extents.y1 >= region->extents.y2) { region->extents.x1 = 0; region->extents.x2 = 0; region->extents.y1 = 0; region->extents.y2 = 0; return FALSE; } if (dest_image->common.have_clip_region) { if (!clip_general_image (region, &dest_image->common.clip_region, 0, 0)) return FALSE; } if (dest_image->common.alpha_map) { if (!pixman_region32_intersect_rect (region, region, dest_image->common.alpha_origin_x, dest_image->common.alpha_origin_y, dest_image->common.alpha_map->width, dest_image->common.alpha_map->height)) { return FALSE; } if (!pixman_region32_not_empty (region)) return FALSE; if (dest_image->common.alpha_map->common.have_clip_region) { if (!clip_general_image (region, &dest_image->common.alpha_map->common.clip_region, -dest_image->common.alpha_origin_x, -dest_image->common.alpha_origin_y)) { return FALSE; } } } /* clip against src */ if (src_image->common.have_clip_region) { if (!clip_source_image (region, src_image, dest_x - src_x, dest_y - src_y)) return FALSE; } if (src_image->common.alpha_map && src_image->common.alpha_map->common.have_clip_region) { if (!clip_source_image (region, (pixman_image_t *)src_image->common.alpha_map, dest_x - (src_x - src_image->common.alpha_origin_x), dest_y - (src_y - src_image->common.alpha_origin_y))) { return FALSE; } } /* clip against mask */ if (mask_image && mask_image->common.have_clip_region) { if (!clip_source_image (region, mask_image, dest_x - mask_x, dest_y - mask_y)) return FALSE; if (mask_image->common.alpha_map && mask_image->common.alpha_map->common.have_clip_region) { if (!clip_source_image (region, (pixman_image_t *)mask_image->common.alpha_map, dest_x - (mask_x - mask_image->common.alpha_origin_x), dest_y - (mask_y - mask_image->common.alpha_origin_y))) { return FALSE; } } } return TRUE; } typedef struct { pixman_fixed_48_16_t x1; pixman_fixed_48_16_t y1; pixman_fixed_48_16_t x2; pixman_fixed_48_16_t y2; } box_48_16_t; static pixman_bool_t compute_transformed_extents (pixman_transform_t *transform, const pixman_box32_t *extents, box_48_16_t *transformed) { pixman_fixed_48_16_t tx1, ty1, tx2, ty2; pixman_fixed_t x1, y1, x2, y2; int i; x1 = pixman_int_to_fixed (extents->x1) + pixman_fixed_1 / 2; y1 = pixman_int_to_fixed (extents->y1) + pixman_fixed_1 / 2; x2 = pixman_int_to_fixed (extents->x2) - pixman_fixed_1 / 2; y2 = pixman_int_to_fixed (extents->y2) - pixman_fixed_1 / 2; if (!transform) { transformed->x1 = x1; transformed->y1 = y1; transformed->x2 = x2; transformed->y2 = y2; return TRUE; } tx1 = ty1 = INT64_MAX; tx2 = ty2 = INT64_MIN; for (i = 0; i < 4; ++i) { pixman_fixed_48_16_t tx, ty; pixman_vector_t v; v.vector[0] = (i & 0x01)? x1 : x2; v.vector[1] = (i & 0x02)? y1 : y2; v.vector[2] = pixman_fixed_1; if (!pixman_transform_point (transform, &v)) return FALSE; tx = (pixman_fixed_48_16_t)v.vector[0]; ty = (pixman_fixed_48_16_t)v.vector[1]; if (tx < tx1) tx1 = tx; if (ty < ty1) ty1 = ty; if (tx > tx2) tx2 = tx; if (ty > ty2) ty2 = ty; } transformed->x1 = tx1; transformed->y1 = ty1; transformed->x2 = tx2; transformed->y2 = ty2; return TRUE; } #define IS_16BIT(x) (((x) >= INT16_MIN) && ((x) <= INT16_MAX)) #define ABS(f) (((f) < 0)? (-(f)) : (f)) #define IS_16_16(f) (((f) >= pixman_min_fixed_48_16 && ((f) <= pixman_max_fixed_48_16))) static pixman_bool_t analyze_extent (pixman_image_t *image, const pixman_box32_t *extents, uint32_t *flags) { pixman_transform_t *transform; pixman_fixed_t x_off, y_off; pixman_fixed_t width, height; pixman_fixed_t *params; box_48_16_t transformed; pixman_box32_t exp_extents; if (!image) return TRUE; /* Some compositing functions walk one step * outside the destination rectangle, so we * check here that the expanded-by-one source * extents in destination space fits in 16 bits */ if (!IS_16BIT (extents->x1 - 1) || !IS_16BIT (extents->y1 - 1) || !IS_16BIT (extents->x2 + 1) || !IS_16BIT (extents->y2 + 1)) { return FALSE; } transform = image->common.transform; if (image->common.type == BITS) { /* During repeat mode calculations we might convert the * width/height of an image to fixed 16.16, so we need * them to be smaller than 16 bits. */ if (image->bits.width >= 0x7fff || image->bits.height >= 0x7fff) return FALSE; if ((image->common.flags & FAST_PATH_ID_TRANSFORM) == FAST_PATH_ID_TRANSFORM && extents->x1 >= 0 && extents->y1 >= 0 && extents->x2 <= image->bits.width && extents->y2 <= image->bits.height) { *flags |= FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; return TRUE; } switch (image->common.filter) { case PIXMAN_FILTER_CONVOLUTION: params = image->common.filter_params; x_off = - pixman_fixed_e - ((params[0] - pixman_fixed_1) >> 1); y_off = - pixman_fixed_e - ((params[1] - pixman_fixed_1) >> 1); width = params[0]; height = params[1]; break; case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: params = image->common.filter_params; x_off = - pixman_fixed_e - ((params[0] - pixman_fixed_1) >> 1); y_off = - pixman_fixed_e - ((params[1] - pixman_fixed_1) >> 1); width = params[0]; height = params[1]; break; case PIXMAN_FILTER_GOOD: case PIXMAN_FILTER_BEST: case PIXMAN_FILTER_BILINEAR: x_off = - pixman_fixed_1 / 2; y_off = - pixman_fixed_1 / 2; width = pixman_fixed_1; height = pixman_fixed_1; break; case PIXMAN_FILTER_FAST: case PIXMAN_FILTER_NEAREST: x_off = - pixman_fixed_e; y_off = - pixman_fixed_e; width = 0; height = 0; break; default: return FALSE; } } else { x_off = 0; y_off = 0; width = 0; height = 0; } if (!compute_transformed_extents (transform, extents, &transformed)) return FALSE; /* Expand the source area by a tiny bit so account of different rounding that * may happen during sampling. Note that (8 * pixman_fixed_e) is very far from * 0.5 so this won't cause the area computed to be overly pessimistic. */ transformed.x1 -= 8 * pixman_fixed_e; transformed.y1 -= 8 * pixman_fixed_e; transformed.x2 += 8 * pixman_fixed_e; transformed.y2 += 8 * pixman_fixed_e; if (image->common.type == BITS) { if (pixman_fixed_to_int (transformed.x1) >= 0 && pixman_fixed_to_int (transformed.y1) >= 0 && pixman_fixed_to_int (transformed.x2) < image->bits.width && pixman_fixed_to_int (transformed.y2) < image->bits.height) { *flags |= FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; } if (pixman_fixed_to_int (transformed.x1 - pixman_fixed_1 / 2) >= 0 && pixman_fixed_to_int (transformed.y1 - pixman_fixed_1 / 2) >= 0 && pixman_fixed_to_int (transformed.x2 + pixman_fixed_1 / 2) < image->bits.width && pixman_fixed_to_int (transformed.y2 + pixman_fixed_1 / 2) < image->bits.height) { *flags |= FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR; } } /* Check we don't overflow when the destination extents are expanded by one. * This ensures that compositing functions can simply walk the source space * using 16.16 variables without worrying about overflow. */ exp_extents = *extents; exp_extents.x1 -= 1; exp_extents.y1 -= 1; exp_extents.x2 += 1; exp_extents.y2 += 1; if (!compute_transformed_extents (transform, &exp_extents, &transformed)) return FALSE; if (!IS_16_16 (transformed.x1 + x_off - 8 * pixman_fixed_e) || !IS_16_16 (transformed.y1 + y_off - 8 * pixman_fixed_e) || !IS_16_16 (transformed.x2 + x_off + 8 * pixman_fixed_e + width) || !IS_16_16 (transformed.y2 + y_off + 8 * pixman_fixed_e + height)) { return FALSE; } return TRUE; } /* * Work around GCC bug causing crashes in Mozilla with SSE2 * * When using -msse, gcc generates movdqa instructions assuming that * the stack is 16 byte aligned. Unfortunately some applications, such * as Mozilla and Mono, end up aligning the stack to 4 bytes, which * causes the movdqa instructions to fail. * * The __force_align_arg_pointer__ makes gcc generate a prologue that * realigns the stack pointer to 16 bytes. * * On x86-64 this is not necessary because the standard ABI already * calls for a 16 byte aligned stack. * * See https://bugs.freedesktop.org/show_bug.cgi?id=15693 */ #if defined (USE_SSE2) && defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) __attribute__((__force_align_arg_pointer__)) #endif PIXMAN_EXPORT void pixman_image_composite32 (pixman_op_t op, pixman_image_t * src, pixman_image_t * mask, pixman_image_t * dest, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height) { pixman_format_code_t src_format, mask_format, dest_format; pixman_region32_t region; pixman_box32_t extents; pixman_implementation_t *imp; pixman_composite_func_t func; pixman_composite_info_t info; const pixman_box32_t *pbox; int n; _pixman_image_validate (src); if (mask) _pixman_image_validate (mask); _pixman_image_validate (dest); src_format = src->common.extended_format_code; info.src_flags = src->common.flags; if (mask && !(mask->common.flags & FAST_PATH_IS_OPAQUE)) { mask_format = mask->common.extended_format_code; info.mask_flags = mask->common.flags; } else { mask_format = PIXMAN_null; info.mask_flags = FAST_PATH_IS_OPAQUE; } dest_format = dest->common.extended_format_code; info.dest_flags = dest->common.flags; /* Check for pixbufs */ if ((mask_format == PIXMAN_a8r8g8b8 || mask_format == PIXMAN_a8b8g8r8) && (src->type == BITS && src->bits.bits == mask->bits.bits) && (src->common.repeat == mask->common.repeat) && (info.src_flags & info.mask_flags & FAST_PATH_ID_TRANSFORM) && (src_x == mask_x && src_y == mask_y)) { if (src_format == PIXMAN_x8b8g8r8) src_format = mask_format = PIXMAN_pixbuf; else if (src_format == PIXMAN_x8r8g8b8) src_format = mask_format = PIXMAN_rpixbuf; } pixman_region32_init (®ion); if (!_pixman_compute_composite_region32 ( ®ion, src, mask, dest, src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height)) { goto out; } extents = *pixman_region32_extents (®ion); extents.x1 -= dest_x - src_x; extents.y1 -= dest_y - src_y; extents.x2 -= dest_x - src_x; extents.y2 -= dest_y - src_y; if (!analyze_extent (src, &extents, &info.src_flags)) goto out; extents.x1 -= src_x - mask_x; extents.y1 -= src_y - mask_y; extents.x2 -= src_x - mask_x; extents.y2 -= src_y - mask_y; if (!analyze_extent (mask, &extents, &info.mask_flags)) goto out; /* If the clip is within the source samples, and the samples are * opaque, then the source is effectively opaque. */ #define NEAREST_OPAQUE (FAST_PATH_SAMPLES_OPAQUE | \ FAST_PATH_NEAREST_FILTER | \ FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) #define BILINEAR_OPAQUE (FAST_PATH_SAMPLES_OPAQUE | \ FAST_PATH_BILINEAR_FILTER | \ FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR) if ((info.src_flags & NEAREST_OPAQUE) == NEAREST_OPAQUE || (info.src_flags & BILINEAR_OPAQUE) == BILINEAR_OPAQUE) { info.src_flags |= FAST_PATH_IS_OPAQUE; } if ((info.mask_flags & NEAREST_OPAQUE) == NEAREST_OPAQUE || (info.mask_flags & BILINEAR_OPAQUE) == BILINEAR_OPAQUE) { info.mask_flags |= FAST_PATH_IS_OPAQUE; } /* * Check if we can replace our operator by a simpler one * if the src or dest are opaque. The output operator should be * mathematically equivalent to the source. */ info.op = optimize_operator (op, info.src_flags, info.mask_flags, info.dest_flags); _pixman_implementation_lookup_composite ( get_implementation (), info.op, src_format, info.src_flags, mask_format, info.mask_flags, dest_format, info.dest_flags, &imp, &func); info.src_image = src; info.mask_image = mask; info.dest_image = dest; pbox = pixman_region32_rectangles (®ion, &n); while (n--) { info.src_x = pbox->x1 + src_x - dest_x; info.src_y = pbox->y1 + src_y - dest_y; info.mask_x = pbox->x1 + mask_x - dest_x; info.mask_y = pbox->y1 + mask_y - dest_y; info.dest_x = pbox->x1; info.dest_y = pbox->y1; info.width = pbox->x2 - pbox->x1; info.height = pbox->y2 - pbox->y1; func (imp, &info); pbox++; } out: pixman_region32_fini (®ion); } PIXMAN_EXPORT void pixman_image_composite (pixman_op_t op, pixman_image_t * src, pixman_image_t * mask, pixman_image_t * dest, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dest_x, int16_t dest_y, uint16_t width, uint16_t height) { pixman_image_composite32 (op, src, mask, dest, src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height); } PIXMAN_EXPORT pixman_bool_t pixman_blt (uint32_t *src_bits, uint32_t *dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { return _pixman_implementation_blt (get_implementation(), src_bits, dst_bits, src_stride, dst_stride, src_bpp, dst_bpp, src_x, src_y, dest_x, dest_y, width, height); } PIXMAN_EXPORT pixman_bool_t pixman_fill (uint32_t *bits, int stride, int bpp, int x, int y, int width, int height, uint32_t filler) { return _pixman_implementation_fill ( get_implementation(), bits, stride, bpp, x, y, width, height, filler); } static uint32_t color_to_uint32 (const pixman_color_t *color) { return (color->alpha >> 8 << 24) | (color->red >> 8 << 16) | (color->green & 0xff00) | (color->blue >> 8); } static pixman_bool_t color_to_pixel (const pixman_color_t *color, uint32_t * pixel, pixman_format_code_t format) { uint32_t c = color_to_uint32 (color); if (!(format == PIXMAN_a8r8g8b8 || format == PIXMAN_x8r8g8b8 || format == PIXMAN_a8b8g8r8 || format == PIXMAN_x8b8g8r8 || format == PIXMAN_b8g8r8a8 || format == PIXMAN_b8g8r8x8 || format == PIXMAN_r8g8b8a8 || format == PIXMAN_r8g8b8x8 || format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5 || format == PIXMAN_a8 || format == PIXMAN_a1)) { return FALSE; } if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { c = ((c & 0xff000000) >> 0) | ((c & 0x00ff0000) >> 16) | ((c & 0x0000ff00) >> 0) | ((c & 0x000000ff) << 16); } if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { c = ((c & 0xff000000) >> 24) | ((c & 0x00ff0000) >> 8) | ((c & 0x0000ff00) << 8) | ((c & 0x000000ff) << 24); } if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_RGBA) c = ((c & 0xff000000) >> 24) | (c << 8); if (format == PIXMAN_a1) c = c >> 31; else if (format == PIXMAN_a8) c = c >> 24; else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) c = convert_8888_to_0565 (c); #if 0 printf ("color: %x %x %x %x\n", color->alpha, color->red, color->green, color->blue); printf ("pixel: %x\n", c); #endif *pixel = c; return TRUE; } PIXMAN_EXPORT pixman_bool_t pixman_image_fill_rectangles (pixman_op_t op, pixman_image_t * dest, const pixman_color_t * color, int n_rects, const pixman_rectangle16_t *rects) { pixman_box32_t stack_boxes[6]; pixman_box32_t *boxes; pixman_bool_t result; int i; if (n_rects > 6) { boxes = pixman_malloc_ab (sizeof (pixman_box32_t), n_rects); if (boxes == NULL) return FALSE; } else { boxes = stack_boxes; } for (i = 0; i < n_rects; ++i) { boxes[i].x1 = rects[i].x; boxes[i].y1 = rects[i].y; boxes[i].x2 = boxes[i].x1 + rects[i].width; boxes[i].y2 = boxes[i].y1 + rects[i].height; } result = pixman_image_fill_boxes (op, dest, color, n_rects, boxes); if (boxes != stack_boxes) free (boxes); return result; } PIXMAN_EXPORT pixman_bool_t pixman_image_fill_boxes (pixman_op_t op, pixman_image_t * dest, const pixman_color_t *color, int n_boxes, const pixman_box32_t *boxes) { pixman_image_t *solid; pixman_color_t c; int i; _pixman_image_validate (dest); if (color->alpha == 0xffff) { if (op == PIXMAN_OP_OVER) op = PIXMAN_OP_SRC; } if (op == PIXMAN_OP_CLEAR) { c.red = 0; c.green = 0; c.blue = 0; c.alpha = 0; color = &c; op = PIXMAN_OP_SRC; } if (op == PIXMAN_OP_SRC) { uint32_t pixel; if (color_to_pixel (color, &pixel, dest->bits.format)) { pixman_region32_t fill_region; int n_rects, j; pixman_box32_t *rects; if (!pixman_region32_init_rects (&fill_region, boxes, n_boxes)) return FALSE; if (dest->common.have_clip_region) { if (!pixman_region32_intersect (&fill_region, &fill_region, &dest->common.clip_region)) return FALSE; } rects = pixman_region32_rectangles (&fill_region, &n_rects); for (j = 0; j < n_rects; ++j) { const pixman_box32_t *rect = &(rects[j]); pixman_fill (dest->bits.bits, dest->bits.rowstride, PIXMAN_FORMAT_BPP (dest->bits.format), rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1, pixel); } pixman_region32_fini (&fill_region); return TRUE; } } solid = pixman_image_create_solid_fill (color); if (!solid) return FALSE; for (i = 0; i < n_boxes; ++i) { const pixman_box32_t *box = &(boxes[i]); pixman_image_composite32 (op, solid, NULL, dest, 0, 0, 0, 0, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1); } pixman_image_unref (solid); return TRUE; } /** * pixman_version: * * Returns the version of the pixman library encoded in a single * integer as per %PIXMAN_VERSION_ENCODE. The encoding ensures that * later versions compare greater than earlier versions. * * A run-time comparison to check that pixman's version is greater than * or equal to version X.Y.Z could be performed as follows: * * * if (pixman_version() >= PIXMAN_VERSION_ENCODE(X,Y,Z)) {...} * * * See also pixman_version_string() as well as the compile-time * equivalents %PIXMAN_VERSION and %PIXMAN_VERSION_STRING. * * Return value: the encoded version. **/ PIXMAN_EXPORT int pixman_version (void) { return PIXMAN_VERSION; } /** * pixman_version_string: * * Returns the version of the pixman library as a human-readable string * of the form "X.Y.Z". * * See also pixman_version() as well as the compile-time equivalents * %PIXMAN_VERSION_STRING and %PIXMAN_VERSION. * * Return value: a string containing the version. **/ PIXMAN_EXPORT const char* pixman_version_string (void) { return PIXMAN_VERSION_STRING; } /** * pixman_format_supported_source: * @format: A pixman_format_code_t format * * Return value: whether the provided format code is a supported * format for a pixman surface used as a source in * rendering. * * Currently, all pixman_format_code_t values are supported. **/ PIXMAN_EXPORT pixman_bool_t pixman_format_supported_source (pixman_format_code_t format) { switch (format) { /* 32 bpp formats */ case PIXMAN_a2b10g10r10: case PIXMAN_x2b10g10r10: case PIXMAN_a2r10g10b10: case PIXMAN_x2r10g10b10: case PIXMAN_a8r8g8b8: case PIXMAN_a8r8g8b8_sRGB: case PIXMAN_x8r8g8b8: case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_b8g8r8a8: case PIXMAN_b8g8r8x8: case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8: case PIXMAN_r8g8b8: case PIXMAN_b8g8r8: case PIXMAN_r5g6b5: case PIXMAN_b5g6r5: case PIXMAN_x14r6g6b6: /* 16 bpp formats */ case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: /* 8bpp formats */ case PIXMAN_a8: case PIXMAN_r3g3b2: case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4: /* Collides with PIXMAN_c8 case PIXMAN_x4c4: */ /* Collides with PIXMAN_g8 case PIXMAN_x4g4: */ /* 4bpp formats */ case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: case PIXMAN_g4: /* 1bpp formats */ case PIXMAN_a1: case PIXMAN_g1: /* YUV formats */ case PIXMAN_yuy2: case PIXMAN_yv12: return TRUE; default: return FALSE; } } /** * pixman_format_supported_destination: * @format: A pixman_format_code_t format * * Return value: whether the provided format code is a supported * format for a pixman surface used as a destination in * rendering. * * Currently, all pixman_format_code_t values are supported * except for the YUV formats. **/ PIXMAN_EXPORT pixman_bool_t pixman_format_supported_destination (pixman_format_code_t format) { /* YUV formats cannot be written to at the moment */ if (format == PIXMAN_yuy2 || format == PIXMAN_yv12) return FALSE; return pixman_format_supported_source (format); } PIXMAN_EXPORT pixman_bool_t pixman_compute_composite_region (pixman_region16_t * region, pixman_image_t * src_image, pixman_image_t * mask_image, pixman_image_t * dest_image, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dest_x, int16_t dest_y, uint16_t width, uint16_t height) { pixman_region32_t r32; pixman_bool_t retval; pixman_region32_init (&r32); retval = _pixman_compute_composite_region32 ( &r32, src_image, mask_image, dest_image, src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height); if (retval) { if (!pixman_region16_copy_from_region32 (region, &r32)) retval = FALSE; } pixman_region32_fini (&r32); return retval; } Indigo-indigo-1.2.3/third_party/cairo-src/pixman/pixman/pixman.h000066400000000000000000001273361271037650300246650ustar00rootroot00000000000000/*********************************************************** Copyright 1987, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* * Copyright © 1998, 2004 Keith Packard * Copyright 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef PIXMAN_H__ #define PIXMAN_H__ #include #ifdef __cplusplus #define PIXMAN_BEGIN_DECLS extern "C" { #define PIXMAN_END_DECLS } #else #define PIXMAN_BEGIN_DECLS #define PIXMAN_END_DECLS #endif PIXMAN_BEGIN_DECLS /* * Standard integers */ #if !defined (PIXMAN_DONT_DEFINE_STDINT) #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__) || defined (__HP_cc) # include /* VS 2010 (_MSC_VER 1600) has stdint.h */ #elif defined (_MSC_VER) && _MSC_VER < 1600 typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #elif defined (_AIX) # include #else # include #endif #endif /* * Boolean */ typedef int pixman_bool_t; /* * Fixpoint numbers */ typedef int64_t pixman_fixed_32_32_t; typedef pixman_fixed_32_32_t pixman_fixed_48_16_t; typedef uint32_t pixman_fixed_1_31_t; typedef uint32_t pixman_fixed_1_16_t; typedef int32_t pixman_fixed_16_16_t; typedef pixman_fixed_16_16_t pixman_fixed_t; #define pixman_fixed_e ((pixman_fixed_t) 1) #define pixman_fixed_1 (pixman_int_to_fixed(1)) #define pixman_fixed_1_minus_e (pixman_fixed_1 - pixman_fixed_e) #define pixman_fixed_minus_1 (pixman_int_to_fixed(-1)) #define pixman_fixed_to_int(f) ((int) ((f) >> 16)) #define pixman_int_to_fixed(i) ((pixman_fixed_t) ((i) << 16)) #define pixman_fixed_to_double(f) (double) ((f) / (double) pixman_fixed_1) #define pixman_double_to_fixed(d) ((pixman_fixed_t) ((d) * 65536.0)) #define pixman_fixed_frac(f) ((f) & pixman_fixed_1_minus_e) #define pixman_fixed_floor(f) ((f) & ~pixman_fixed_1_minus_e) #define pixman_fixed_ceil(f) pixman_fixed_floor ((f) + pixman_fixed_1_minus_e) #define pixman_fixed_fraction(f) ((f) & pixman_fixed_1_minus_e) #define pixman_fixed_mod_2(f) ((f) & (pixman_fixed1 | pixman_fixed_1_minus_e)) #define pixman_max_fixed_48_16 ((pixman_fixed_48_16_t) 0x7fffffff) #define pixman_min_fixed_48_16 (-((pixman_fixed_48_16_t) 1 << 31)) /* * Misc structs */ typedef struct pixman_color pixman_color_t; typedef struct pixman_point_fixed pixman_point_fixed_t; typedef struct pixman_line_fixed pixman_line_fixed_t; typedef struct pixman_vector pixman_vector_t; typedef struct pixman_transform pixman_transform_t; struct pixman_color { uint16_t red; uint16_t green; uint16_t blue; uint16_t alpha; }; struct pixman_point_fixed { pixman_fixed_t x; pixman_fixed_t y; }; struct pixman_line_fixed { pixman_point_fixed_t p1, p2; }; /* * Fixed point matrices */ struct pixman_vector { pixman_fixed_t vector[3]; }; struct pixman_transform { pixman_fixed_t matrix[3][3]; }; /* forward declaration (sorry) */ struct pixman_box16; typedef union pixman_image pixman_image_t; void pixman_transform_init_identity (struct pixman_transform *matrix); pixman_bool_t pixman_transform_point_3d (const struct pixman_transform *transform, struct pixman_vector *vector); pixman_bool_t pixman_transform_point (const struct pixman_transform *transform, struct pixman_vector *vector); pixman_bool_t pixman_transform_multiply (struct pixman_transform *dst, const struct pixman_transform *l, const struct pixman_transform *r); void pixman_transform_init_scale (struct pixman_transform *t, pixman_fixed_t sx, pixman_fixed_t sy); pixman_bool_t pixman_transform_scale (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t sx, pixman_fixed_t sy); void pixman_transform_init_rotate (struct pixman_transform *t, pixman_fixed_t cos, pixman_fixed_t sin); pixman_bool_t pixman_transform_rotate (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t c, pixman_fixed_t s); void pixman_transform_init_translate (struct pixman_transform *t, pixman_fixed_t tx, pixman_fixed_t ty); pixman_bool_t pixman_transform_translate (struct pixman_transform *forward, struct pixman_transform *reverse, pixman_fixed_t tx, pixman_fixed_t ty); pixman_bool_t pixman_transform_bounds (const struct pixman_transform *matrix, struct pixman_box16 *b); pixman_bool_t pixman_transform_invert (struct pixman_transform *dst, const struct pixman_transform *src); pixman_bool_t pixman_transform_is_identity (const struct pixman_transform *t); pixman_bool_t pixman_transform_is_scale (const struct pixman_transform *t); pixman_bool_t pixman_transform_is_int_translate (const struct pixman_transform *t); pixman_bool_t pixman_transform_is_inverse (const struct pixman_transform *a, const struct pixman_transform *b); /* * Floating point matrices */ typedef struct pixman_f_transform pixman_f_transform_t; typedef struct pixman_f_vector pixman_f_vector_t; struct pixman_f_vector { double v[3]; }; struct pixman_f_transform { double m[3][3]; }; pixman_bool_t pixman_transform_from_pixman_f_transform (struct pixman_transform *t, const struct pixman_f_transform *ft); void pixman_f_transform_from_pixman_transform (struct pixman_f_transform *ft, const struct pixman_transform *t); pixman_bool_t pixman_f_transform_invert (struct pixman_f_transform *dst, const struct pixman_f_transform *src); pixman_bool_t pixman_f_transform_point (const struct pixman_f_transform *t, struct pixman_f_vector *v); void pixman_f_transform_point_3d (const struct pixman_f_transform *t, struct pixman_f_vector *v); void pixman_f_transform_multiply (struct pixman_f_transform *dst, const struct pixman_f_transform *l, const struct pixman_f_transform *r); void pixman_f_transform_init_scale (struct pixman_f_transform *t, double sx, double sy); pixman_bool_t pixman_f_transform_scale (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double sx, double sy); void pixman_f_transform_init_rotate (struct pixman_f_transform *t, double cos, double sin); pixman_bool_t pixman_f_transform_rotate (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double c, double s); void pixman_f_transform_init_translate (struct pixman_f_transform *t, double tx, double ty); pixman_bool_t pixman_f_transform_translate (struct pixman_f_transform *forward, struct pixman_f_transform *reverse, double tx, double ty); pixman_bool_t pixman_f_transform_bounds (const struct pixman_f_transform *t, struct pixman_box16 *b); void pixman_f_transform_init_identity (struct pixman_f_transform *t); typedef enum { PIXMAN_REPEAT_NONE, PIXMAN_REPEAT_NORMAL, PIXMAN_REPEAT_PAD, PIXMAN_REPEAT_REFLECT } pixman_repeat_t; typedef enum { PIXMAN_FILTER_FAST, PIXMAN_FILTER_GOOD, PIXMAN_FILTER_BEST, PIXMAN_FILTER_NEAREST, PIXMAN_FILTER_BILINEAR, PIXMAN_FILTER_CONVOLUTION, /* The SEPARABLE_CONVOLUTION filter takes the following parameters: * * width: integer given as 16.16 fixpoint number * height: integer given as 16.16 fixpoint number * x_phase_bits: integer given as 16.16 fixpoint * y_phase_bits: integer given as 16.16 fixpoint * xtables: (1 << x_phase_bits) tables of size width * ytables: (1 << y_phase_bits) tables of size height * * When sampling at (x, y), the location is first rounded to one of * n_x_phases * n_y_phases subpixel positions. These subpixel positions * determine an xtable and a ytable to use. * * Conceptually a width x height matrix is then formed in which each entry * is the product of the corresponding entries in the x and y tables. * This matrix is then aligned with the image pixels such that its center * is as close as possible to the subpixel location chosen earlier. Then * the image is convolved with the matrix and the resulting pixel returned. */ PIXMAN_FILTER_SEPARABLE_CONVOLUTION } pixman_filter_t; typedef enum { PIXMAN_OP_CLEAR = 0x00, PIXMAN_OP_SRC = 0x01, PIXMAN_OP_DST = 0x02, PIXMAN_OP_OVER = 0x03, PIXMAN_OP_OVER_REVERSE = 0x04, PIXMAN_OP_IN = 0x05, PIXMAN_OP_IN_REVERSE = 0x06, PIXMAN_OP_OUT = 0x07, PIXMAN_OP_OUT_REVERSE = 0x08, PIXMAN_OP_ATOP = 0x09, PIXMAN_OP_ATOP_REVERSE = 0x0a, PIXMAN_OP_XOR = 0x0b, PIXMAN_OP_ADD = 0x0c, PIXMAN_OP_SATURATE = 0x0d, PIXMAN_OP_DISJOINT_CLEAR = 0x10, PIXMAN_OP_DISJOINT_SRC = 0x11, PIXMAN_OP_DISJOINT_DST = 0x12, PIXMAN_OP_DISJOINT_OVER = 0x13, PIXMAN_OP_DISJOINT_OVER_REVERSE = 0x14, PIXMAN_OP_DISJOINT_IN = 0x15, PIXMAN_OP_DISJOINT_IN_REVERSE = 0x16, PIXMAN_OP_DISJOINT_OUT = 0x17, PIXMAN_OP_DISJOINT_OUT_REVERSE = 0x18, PIXMAN_OP_DISJOINT_ATOP = 0x19, PIXMAN_OP_DISJOINT_ATOP_REVERSE = 0x1a, PIXMAN_OP_DISJOINT_XOR = 0x1b, PIXMAN_OP_CONJOINT_CLEAR = 0x20, PIXMAN_OP_CONJOINT_SRC = 0x21, PIXMAN_OP_CONJOINT_DST = 0x22, PIXMAN_OP_CONJOINT_OVER = 0x23, PIXMAN_OP_CONJOINT_OVER_REVERSE = 0x24, PIXMAN_OP_CONJOINT_IN = 0x25, PIXMAN_OP_CONJOINT_IN_REVERSE = 0x26, PIXMAN_OP_CONJOINT_OUT = 0x27, PIXMAN_OP_CONJOINT_OUT_REVERSE = 0x28, PIXMAN_OP_CONJOINT_ATOP = 0x29, PIXMAN_OP_CONJOINT_ATOP_REVERSE = 0x2a, PIXMAN_OP_CONJOINT_XOR = 0x2b, PIXMAN_OP_MULTIPLY = 0x30, PIXMAN_OP_SCREEN = 0x31, PIXMAN_OP_OVERLAY = 0x32, PIXMAN_OP_DARKEN = 0x33, PIXMAN_OP_LIGHTEN = 0x34, PIXMAN_OP_COLOR_DODGE = 0x35, PIXMAN_OP_COLOR_BURN = 0x36, PIXMAN_OP_HARD_LIGHT = 0x37, PIXMAN_OP_SOFT_LIGHT = 0x38, PIXMAN_OP_DIFFERENCE = 0x39, PIXMAN_OP_EXCLUSION = 0x3a, PIXMAN_OP_HSL_HUE = 0x3b, PIXMAN_OP_HSL_SATURATION = 0x3c, PIXMAN_OP_HSL_COLOR = 0x3d, PIXMAN_OP_HSL_LUMINOSITY = 0x3e #ifdef PIXMAN_USE_INTERNAL_API , PIXMAN_N_OPERATORS, PIXMAN_OP_NONE = PIXMAN_N_OPERATORS #endif } pixman_op_t; /* * Regions */ typedef struct pixman_region16_data pixman_region16_data_t; typedef struct pixman_box16 pixman_box16_t; typedef struct pixman_rectangle16 pixman_rectangle16_t; typedef struct pixman_region16 pixman_region16_t; struct pixman_region16_data { long size; long numRects; /* pixman_box16_t rects[size]; in memory but not explicitly declared */ }; struct pixman_rectangle16 { int16_t x, y; uint16_t width, height; }; struct pixman_box16 { int16_t x1, y1, x2, y2; }; struct pixman_region16 { pixman_box16_t extents; pixman_region16_data_t *data; }; typedef enum { PIXMAN_REGION_OUT, PIXMAN_REGION_IN, PIXMAN_REGION_PART } pixman_region_overlap_t; /* This function exists only to make it possible to preserve * the X ABI - it should go away at first opportunity. */ void pixman_region_set_static_pointers (pixman_box16_t *empty_box, pixman_region16_data_t *empty_data, pixman_region16_data_t *broken_data); /* creation/destruction */ void pixman_region_init (pixman_region16_t *region); void pixman_region_init_rect (pixman_region16_t *region, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region_init_rects (pixman_region16_t *region, const pixman_box16_t *boxes, int count); void pixman_region_init_with_extents (pixman_region16_t *region, pixman_box16_t *extents); void pixman_region_init_from_image (pixman_region16_t *region, pixman_image_t *image); void pixman_region_fini (pixman_region16_t *region); /* manipulation */ void pixman_region_translate (pixman_region16_t *region, int x, int y); pixman_bool_t pixman_region_copy (pixman_region16_t *dest, pixman_region16_t *source); pixman_bool_t pixman_region_intersect (pixman_region16_t *new_reg, pixman_region16_t *reg1, pixman_region16_t *reg2); pixman_bool_t pixman_region_union (pixman_region16_t *new_reg, pixman_region16_t *reg1, pixman_region16_t *reg2); pixman_bool_t pixman_region_union_rect (pixman_region16_t *dest, pixman_region16_t *source, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region_intersect_rect (pixman_region16_t *dest, pixman_region16_t *source, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region_subtract (pixman_region16_t *reg_d, pixman_region16_t *reg_m, pixman_region16_t *reg_s); pixman_bool_t pixman_region_inverse (pixman_region16_t *new_reg, pixman_region16_t *reg1, pixman_box16_t *inv_rect); pixman_bool_t pixman_region_contains_point (pixman_region16_t *region, int x, int y, pixman_box16_t *box); pixman_region_overlap_t pixman_region_contains_rectangle (pixman_region16_t *region, pixman_box16_t *prect); pixman_bool_t pixman_region_not_empty (pixman_region16_t *region); pixman_box16_t * pixman_region_extents (pixman_region16_t *region); int pixman_region_n_rects (pixman_region16_t *region); pixman_box16_t * pixman_region_rectangles (pixman_region16_t *region, int *n_rects); pixman_bool_t pixman_region_equal (pixman_region16_t *region1, pixman_region16_t *region2); pixman_bool_t pixman_region_selfcheck (pixman_region16_t *region); void pixman_region_reset (pixman_region16_t *region, pixman_box16_t *box); void pixman_region_clear (pixman_region16_t *region); /* * 32 bit regions */ typedef struct pixman_region32_data pixman_region32_data_t; typedef struct pixman_box32 pixman_box32_t; typedef struct pixman_rectangle32 pixman_rectangle32_t; typedef struct pixman_region32 pixman_region32_t; struct pixman_region32_data { long size; long numRects; /* pixman_box32_t rects[size]; in memory but not explicitly declared */ }; struct pixman_rectangle32 { int32_t x, y; uint32_t width, height; }; struct pixman_box32 { int32_t x1, y1, x2, y2; }; struct pixman_region32 { pixman_box32_t extents; pixman_region32_data_t *data; }; /* creation/destruction */ void pixman_region32_init (pixman_region32_t *region); void pixman_region32_init_rect (pixman_region32_t *region, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region32_init_rects (pixman_region32_t *region, const pixman_box32_t *boxes, int count); void pixman_region32_init_with_extents (pixman_region32_t *region, pixman_box32_t *extents); void pixman_region32_init_from_image (pixman_region32_t *region, pixman_image_t *image); void pixman_region32_fini (pixman_region32_t *region); /* manipulation */ void pixman_region32_translate (pixman_region32_t *region, int x, int y); pixman_bool_t pixman_region32_copy (pixman_region32_t *dest, pixman_region32_t *source); pixman_bool_t pixman_region32_intersect (pixman_region32_t *new_reg, pixman_region32_t *reg1, pixman_region32_t *reg2); pixman_bool_t pixman_region32_union (pixman_region32_t *new_reg, pixman_region32_t *reg1, pixman_region32_t *reg2); pixman_bool_t pixman_region32_intersect_rect (pixman_region32_t *dest, pixman_region32_t *source, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region32_union_rect (pixman_region32_t *dest, pixman_region32_t *source, int x, int y, unsigned int width, unsigned int height); pixman_bool_t pixman_region32_subtract (pixman_region32_t *reg_d, pixman_region32_t *reg_m, pixman_region32_t *reg_s); pixman_bool_t pixman_region32_inverse (pixman_region32_t *new_reg, pixman_region32_t *reg1, pixman_box32_t *inv_rect); pixman_bool_t pixman_region32_contains_point (pixman_region32_t *region, int x, int y, pixman_box32_t *box); pixman_region_overlap_t pixman_region32_contains_rectangle (pixman_region32_t *region, pixman_box32_t *prect); pixman_bool_t pixman_region32_not_empty (pixman_region32_t *region); pixman_box32_t * pixman_region32_extents (pixman_region32_t *region); int pixman_region32_n_rects (pixman_region32_t *region); pixman_box32_t * pixman_region32_rectangles (pixman_region32_t *region, int *n_rects); pixman_bool_t pixman_region32_equal (pixman_region32_t *region1, pixman_region32_t *region2); pixman_bool_t pixman_region32_selfcheck (pixman_region32_t *region); void pixman_region32_reset (pixman_region32_t *region, pixman_box32_t *box); void pixman_region32_clear (pixman_region32_t *region); /* Copy / Fill / Misc */ pixman_bool_t pixman_blt (uint32_t *src_bits, uint32_t *dst_bits, int src_stride, int dst_stride, int src_bpp, int dst_bpp, int src_x, int src_y, int dest_x, int dest_y, int width, int height); pixman_bool_t pixman_fill (uint32_t *bits, int stride, int bpp, int x, int y, int width, int height, uint32_t _xor); int pixman_version (void); const char* pixman_version_string (void); /* * Images */ typedef struct pixman_indexed pixman_indexed_t; typedef struct pixman_gradient_stop pixman_gradient_stop_t; typedef uint32_t (* pixman_read_memory_func_t) (const void *src, int size); typedef void (* pixman_write_memory_func_t) (void *dst, uint32_t value, int size); typedef void (* pixman_image_destroy_func_t) (pixman_image_t *image, void *data); struct pixman_gradient_stop { pixman_fixed_t x; pixman_color_t color; }; #define PIXMAN_MAX_INDEXED 256 /* XXX depth must be <= 8 */ #if PIXMAN_MAX_INDEXED <= 256 typedef uint8_t pixman_index_type; #endif struct pixman_indexed { pixman_bool_t color; uint32_t rgba[PIXMAN_MAX_INDEXED]; pixman_index_type ent[32768]; }; /* * While the protocol is generous in format support, the * sample implementation allows only packed RGB and GBR * representations for data to simplify software rendering, */ #define PIXMAN_FORMAT(bpp,type,a,r,g,b) (((bpp) << 24) | \ ((type) << 16) | \ ((a) << 12) | \ ((r) << 8) | \ ((g) << 4) | \ ((b))) #define PIXMAN_FORMAT_BPP(f) (((f) >> 24) ) #define PIXMAN_FORMAT_TYPE(f) (((f) >> 16) & 0xff) #define PIXMAN_FORMAT_A(f) (((f) >> 12) & 0x0f) #define PIXMAN_FORMAT_R(f) (((f) >> 8) & 0x0f) #define PIXMAN_FORMAT_G(f) (((f) >> 4) & 0x0f) #define PIXMAN_FORMAT_B(f) (((f) ) & 0x0f) #define PIXMAN_FORMAT_RGB(f) (((f) ) & 0xfff) #define PIXMAN_FORMAT_VIS(f) (((f) ) & 0xffff) #define PIXMAN_FORMAT_DEPTH(f) (PIXMAN_FORMAT_A(f) + \ PIXMAN_FORMAT_R(f) + \ PIXMAN_FORMAT_G(f) + \ PIXMAN_FORMAT_B(f)) #define PIXMAN_TYPE_OTHER 0 #define PIXMAN_TYPE_A 1 #define PIXMAN_TYPE_ARGB 2 #define PIXMAN_TYPE_ABGR 3 #define PIXMAN_TYPE_COLOR 4 #define PIXMAN_TYPE_GRAY 5 #define PIXMAN_TYPE_YUY2 6 #define PIXMAN_TYPE_YV12 7 #define PIXMAN_TYPE_BGRA 8 #define PIXMAN_TYPE_RGBA 9 #define PIXMAN_TYPE_ARGB_SRGB 10 #define PIXMAN_FORMAT_COLOR(f) \ (PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ARGB || \ PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ABGR || \ PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_BGRA || \ PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_RGBA) /* 32bpp formats */ typedef enum { PIXMAN_a8r8g8b8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,8,8,8,8), PIXMAN_x8r8g8b8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,8,8,8), PIXMAN_a8b8g8r8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,8,8,8,8), PIXMAN_x8b8g8r8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,0,8,8,8), PIXMAN_b8g8r8a8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_BGRA,8,8,8,8), PIXMAN_b8g8r8x8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_BGRA,0,8,8,8), PIXMAN_r8g8b8a8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_RGBA,8,8,8,8), PIXMAN_r8g8b8x8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_RGBA,0,8,8,8), PIXMAN_x14r6g6b6 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,6,6,6), PIXMAN_x2r10g10b10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,10,10,10), PIXMAN_a2r10g10b10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,2,10,10,10), PIXMAN_x2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,0,10,10,10), PIXMAN_a2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,2,10,10,10), /* sRGB formats */ PIXMAN_a8r8g8b8_sRGB = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB_SRGB,8,8,8,8), /* 24bpp formats */ PIXMAN_r8g8b8 = PIXMAN_FORMAT(24,PIXMAN_TYPE_ARGB,0,8,8,8), PIXMAN_b8g8r8 = PIXMAN_FORMAT(24,PIXMAN_TYPE_ABGR,0,8,8,8), /* 16bpp formats */ PIXMAN_r5g6b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,5,6,5), PIXMAN_b5g6r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,5,6,5), PIXMAN_a1r5g5b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,1,5,5,5), PIXMAN_x1r5g5b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,5,5,5), PIXMAN_a1b5g5r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,1,5,5,5), PIXMAN_x1b5g5r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,5,5,5), PIXMAN_a4r4g4b4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,4,4,4,4), PIXMAN_x4r4g4b4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,4,4,4), PIXMAN_a4b4g4r4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,4,4,4,4), PIXMAN_x4b4g4r4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,4,4,4), /* 8bpp formats */ PIXMAN_a8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_A,8,0,0,0), PIXMAN_r3g3b2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ARGB,0,3,3,2), PIXMAN_b2g3r3 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ABGR,0,3,3,2), PIXMAN_a2r2g2b2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ARGB,2,2,2,2), PIXMAN_a2b2g2r2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ABGR,2,2,2,2), PIXMAN_c8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_COLOR,0,0,0,0), PIXMAN_g8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_GRAY,0,0,0,0), PIXMAN_x4a4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_A,4,0,0,0), PIXMAN_x4c4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_COLOR,0,0,0,0), PIXMAN_x4g4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_GRAY,0,0,0,0), /* 4bpp formats */ PIXMAN_a4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_A,4,0,0,0), PIXMAN_r1g2b1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ARGB,0,1,2,1), PIXMAN_b1g2r1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ABGR,0,1,2,1), PIXMAN_a1r1g1b1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ARGB,1,1,1,1), PIXMAN_a1b1g1r1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ABGR,1,1,1,1), PIXMAN_c4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_COLOR,0,0,0,0), PIXMAN_g4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_GRAY,0,0,0,0), /* 1bpp formats */ PIXMAN_a1 = PIXMAN_FORMAT(1,PIXMAN_TYPE_A,1,0,0,0), PIXMAN_g1 = PIXMAN_FORMAT(1,PIXMAN_TYPE_GRAY,0,0,0,0), /* YUV formats */ PIXMAN_yuy2 = PIXMAN_FORMAT(16,PIXMAN_TYPE_YUY2,0,0,0,0), PIXMAN_yv12 = PIXMAN_FORMAT(12,PIXMAN_TYPE_YV12,0,0,0,0) } pixman_format_code_t; /* Querying supported format values. */ pixman_bool_t pixman_format_supported_destination (pixman_format_code_t format); pixman_bool_t pixman_format_supported_source (pixman_format_code_t format); /* Constructors */ pixman_image_t *pixman_image_create_solid_fill (const pixman_color_t *color); pixman_image_t *pixman_image_create_linear_gradient (const pixman_point_fixed_t *p1, const pixman_point_fixed_t *p2, const pixman_gradient_stop_t *stops, int n_stops); pixman_image_t *pixman_image_create_radial_gradient (const pixman_point_fixed_t *inner, const pixman_point_fixed_t *outer, pixman_fixed_t inner_radius, pixman_fixed_t outer_radius, const pixman_gradient_stop_t *stops, int n_stops); pixman_image_t *pixman_image_create_conical_gradient (const pixman_point_fixed_t *center, pixman_fixed_t angle, const pixman_gradient_stop_t *stops, int n_stops); pixman_image_t *pixman_image_create_bits (pixman_format_code_t format, int width, int height, uint32_t *bits, int rowstride_bytes); pixman_image_t *pixman_image_create_bits_no_clear (pixman_format_code_t format, int width, int height, uint32_t * bits, int rowstride_bytes); /* Destructor */ pixman_image_t *pixman_image_ref (pixman_image_t *image); pixman_bool_t pixman_image_unref (pixman_image_t *image); void pixman_image_set_destroy_function (pixman_image_t *image, pixman_image_destroy_func_t function, void *data); void * pixman_image_get_destroy_data (pixman_image_t *image); /* Set properties */ pixman_bool_t pixman_image_set_clip_region (pixman_image_t *image, pixman_region16_t *region); pixman_bool_t pixman_image_set_clip_region32 (pixman_image_t *image, pixman_region32_t *region); void pixman_image_set_has_client_clip (pixman_image_t *image, pixman_bool_t clien_clip); pixman_bool_t pixman_image_set_transform (pixman_image_t *image, const pixman_transform_t *transform); void pixman_image_set_repeat (pixman_image_t *image, pixman_repeat_t repeat); pixman_bool_t pixman_image_set_filter (pixman_image_t *image, pixman_filter_t filter, const pixman_fixed_t *filter_params, int n_filter_params); void pixman_image_set_source_clipping (pixman_image_t *image, pixman_bool_t source_clipping); void pixman_image_set_alpha_map (pixman_image_t *image, pixman_image_t *alpha_map, int16_t x, int16_t y); void pixman_image_set_component_alpha (pixman_image_t *image, pixman_bool_t component_alpha); pixman_bool_t pixman_image_get_component_alpha (pixman_image_t *image); void pixman_image_set_accessors (pixman_image_t *image, pixman_read_memory_func_t read_func, pixman_write_memory_func_t write_func); void pixman_image_set_indexed (pixman_image_t *image, const pixman_indexed_t *indexed); uint32_t *pixman_image_get_data (pixman_image_t *image); int pixman_image_get_width (pixman_image_t *image); int pixman_image_get_height (pixman_image_t *image); int pixman_image_get_stride (pixman_image_t *image); /* in bytes */ int pixman_image_get_depth (pixman_image_t *image); pixman_format_code_t pixman_image_get_format (pixman_image_t *image); typedef enum { PIXMAN_KERNEL_IMPULSE, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, PIXMAN_KERNEL_CUBIC, PIXMAN_KERNEL_GAUSSIAN, PIXMAN_KERNEL_LANCZOS2, PIXMAN_KERNEL_LANCZOS3, PIXMAN_KERNEL_LANCZOS3_STRETCHED /* Jim Blinn's 'nice' filter */ } pixman_kernel_t; /* Create the parameter list for a SEPARABLE_CONVOLUTION filter * with the given kernels and scale parameters. */ pixman_fixed_t * pixman_filter_create_separable_convolution (int *n_values, pixman_fixed_t scale_x, pixman_fixed_t scale_y, pixman_kernel_t reconstruct_x, pixman_kernel_t reconstruct_y, pixman_kernel_t sample_x, pixman_kernel_t sample_y, int subsample_bits_x, int subsample_bits_y); pixman_bool_t pixman_image_fill_rectangles (pixman_op_t op, pixman_image_t *image, const pixman_color_t *color, int n_rects, const pixman_rectangle16_t *rects); pixman_bool_t pixman_image_fill_boxes (pixman_op_t op, pixman_image_t *dest, const pixman_color_t *color, int n_boxes, const pixman_box32_t *boxes); /* Composite */ pixman_bool_t pixman_compute_composite_region (pixman_region16_t *region, pixman_image_t *src_image, pixman_image_t *mask_image, pixman_image_t *dest_image, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dest_x, int16_t dest_y, uint16_t width, uint16_t height); void pixman_image_composite (pixman_op_t op, pixman_image_t *src, pixman_image_t *mask, pixman_image_t *dest, int16_t src_x, int16_t src_y, int16_t mask_x, int16_t mask_y, int16_t dest_x, int16_t dest_y, uint16_t width, uint16_t height); void pixman_image_composite32 (pixman_op_t op, pixman_image_t *src, pixman_image_t *mask, pixman_image_t *dest, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height); /* Executive Summary: This function is a no-op that only exists * for historical reasons. * * There used to be a bug in the X server where it would rely on * out-of-bounds accesses when it was asked to composite with a * window as the source. It would create a pixman image pointing * to some bogus position in memory, but then set a clip region * to the position where the actual bits were. * * Due to a bug in old versions of pixman, where it would not clip * against the image bounds when a clip region was set, this would * actually work. So when the pixman bug was fixed, a workaround was * added to allow certain out-of-bound accesses. This function disabled * those workarounds. * * Since 0.21.2, pixman doesn't do these workarounds anymore, so now this * function is a no-op. */ void pixman_disable_out_of_bounds_workaround (void); /* * Glyphs */ typedef struct pixman_glyph_cache_t pixman_glyph_cache_t; typedef struct { int x, y; const void *glyph; } pixman_glyph_t; pixman_glyph_cache_t *pixman_glyph_cache_create (void); void pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache); void pixman_glyph_cache_freeze (pixman_glyph_cache_t *cache); void pixman_glyph_cache_thaw (pixman_glyph_cache_t *cache); const void * pixman_glyph_cache_lookup (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key); const void * pixman_glyph_cache_insert (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key, int origin_x, int origin_y, pixman_image_t *glyph_image); void pixman_glyph_cache_remove (pixman_glyph_cache_t *cache, void *font_key, void *glyph_key); void pixman_glyph_get_extents (pixman_glyph_cache_t *cache, int n_glyphs, pixman_glyph_t *glyphs, pixman_box32_t *extents); pixman_format_code_t pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs); void pixman_composite_glyphs (pixman_op_t op, pixman_image_t *src, pixman_image_t *dest, pixman_format_code_t mask_format, int32_t src_x, int32_t src_y, int32_t mask_x, int32_t mask_y, int32_t dest_x, int32_t dest_y, int32_t width, int32_t height, pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs); void pixman_composite_glyphs_no_mask (pixman_op_t op, pixman_image_t *src, pixman_image_t *dest, int32_t src_x, int32_t src_y, int32_t dest_x, int32_t dest_y, pixman_glyph_cache_t *cache, int n_glyphs, const pixman_glyph_t *glyphs); /* * Trapezoids */ typedef struct pixman_edge pixman_edge_t; typedef struct pixman_trapezoid pixman_trapezoid_t; typedef struct pixman_trap pixman_trap_t; typedef struct pixman_span_fix pixman_span_fix_t; typedef struct pixman_triangle pixman_triangle_t; /* * An edge structure. This represents a single polygon edge * and can be quickly stepped across small or large gaps in the * sample grid */ struct pixman_edge { pixman_fixed_t x; pixman_fixed_t e; pixman_fixed_t stepx; pixman_fixed_t signdx; pixman_fixed_t dy; pixman_fixed_t dx; pixman_fixed_t stepx_small; pixman_fixed_t stepx_big; pixman_fixed_t dx_small; pixman_fixed_t dx_big; }; struct pixman_trapezoid { pixman_fixed_t top, bottom; pixman_line_fixed_t left, right; }; struct pixman_triangle { pixman_point_fixed_t p1, p2, p3; }; /* whether 't' is a well defined not obviously empty trapezoid */ #define pixman_trapezoid_valid(t) \ ((t)->left.p1.y != (t)->left.p2.y && \ (t)->right.p1.y != (t)->right.p2.y && \ (int) ((t)->bottom - (t)->top) > 0) struct pixman_span_fix { pixman_fixed_t l, r, y; }; struct pixman_trap { pixman_span_fix_t top, bot; }; pixman_fixed_t pixman_sample_ceil_y (pixman_fixed_t y, int bpp); pixman_fixed_t pixman_sample_floor_y (pixman_fixed_t y, int bpp); void pixman_edge_step (pixman_edge_t *e, int n); void pixman_edge_init (pixman_edge_t *e, int bpp, pixman_fixed_t y_start, pixman_fixed_t x_top, pixman_fixed_t y_top, pixman_fixed_t x_bot, pixman_fixed_t y_bot); void pixman_line_fixed_edge_init (pixman_edge_t *e, int bpp, pixman_fixed_t y, const pixman_line_fixed_t *line, int x_off, int y_off); void pixman_rasterize_edges (pixman_image_t *image, pixman_edge_t *l, pixman_edge_t *r, pixman_fixed_t t, pixman_fixed_t b); void pixman_add_traps (pixman_image_t *image, int16_t x_off, int16_t y_off, int ntrap, const pixman_trap_t *traps); void pixman_add_trapezoids (pixman_image_t *image, int16_t x_off, int y_off, int ntraps, const pixman_trapezoid_t *traps); void pixman_rasterize_trapezoid (pixman_image_t *image, const pixman_trapezoid_t *trap, int x_off, int y_off); void pixman_composite_trapezoids (pixman_op_t op, pixman_image_t * src, pixman_image_t * dst, pixman_format_code_t mask_format, int x_src, int y_src, int x_dst, int y_dst, int n_traps, const pixman_trapezoid_t * traps); void pixman_composite_triangles (pixman_op_t op, pixman_image_t * src, pixman_image_t * dst, pixman_format_code_t mask_format, int x_src, int y_src, int x_dst, int y_dst, int n_tris, const pixman_triangle_t * tris); void pixman_add_triangles (pixman_image_t *image, int32_t x_off, int32_t y_off, int n_tris, const pixman_triangle_t *tris); PIXMAN_END_DECLS #endif /* PIXMAN_H__ */ Indigo-indigo-1.2.3/third_party/inchi/000077500000000000000000000000001271037650300176245ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/inchi/CMakeLists.txt000066400000000000000000000010711271037650300223630ustar00rootroot00000000000000PROJECT(InChI) file (GLOB InChI_src inchi_dll/*.c) file (GLOB InChI_headers inchi_dll/*.h) add_library(inchi STATIC ${InChI_src} ${InChI_headers}) set_target_properties(inchi PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET inchi PROPERTY FOLDER "third_party") if (NOT NO_STATIC) pack_static(inchi) endif() if (APPLE AND NOT DEFINED $ENV{VERBOSE}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-parentheses -Wno-sizeof-pointer-memaccess -Wno-tautological-compare -Wno-unsequenced -Wno-comment -Wno-logical-op-parentheses") endif() Indigo-indigo-1.2.3/third_party/inchi/LICENCE000066400000000000000000000421041271037650300206120ustar00rootroot00000000000000IUPAC/InChI-Trust Licence for the International Chemical Identifier (InChI) Software version 1.04, September 2011 ("IUPAC/InChI-Trust InChI Licence No. 1.0") Copyright (C) IUPAC and InChI Trust Limited This library is free software; you can redistribute it and/or modify it under the terms of the IUPAC/InChI Trust InChI Licence No. 1.0), or (at your option) any later version. Terms and Conditions for Copying, Distribution and Modification of the InChI Software 0. This Licence Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Licence. The Licensee is addressed as "you". 'IUPAC' means the International Union of Pure and Applied Chemistry. 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 Licence; 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 Licence and to the absence of any warranty; and distribute a copy of this Licence 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 Licence. This requirement does not extend to any "work that uses the Library" that might also be compiled or linked against the "work based on the Library." 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 Licence, 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 Licence, 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 Licence. 3. You may opt to apply the terms of the ordinary GNU General Public Licence instead of this Licence to a given copy of the Library. To do this, you must alter all the notices that refer to this Licence, so that they refer to the ordinary GNU General Public Licence, version 2, instead of to this Licence. (If a newer version than version 2 of the ordinary GNU General Public Licence 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 Licence 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 Licence. 6. You may combine 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. 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 Licence. You must supply a copy of this Licence. 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 Licence. 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 to the Library were used in the work (which must be distributed under Sections 1 and 2 above). b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) 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. d) 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. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. 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 Licence, 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 Licence. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this Licence. However, parties who have received copies, or rights, from you under this Licence will not have their Licences terminated so long as such parties remain in full compliance. 9. You are not required to accept this Licence, 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 Licence. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this Licence 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 Licence 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 with this Licence. 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 Licence, they do not excuse you from the conditions of this Licence. If you cannot distribute so as to satisfy simultaneously your obligations under this Licence and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent Licence 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 Licence 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 Licence 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 Licence. 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 Licence 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 Licence incorporates the limitation as if written in the body of this Licence. 13. IUPAC and the InChI Trust Limited may publish revised and/or new versions of the IUPAC/InChI Trust Licence for the International Chemical Identifier (InChI) Software 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 Licence which applied 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 IUPAC and the InChI Trust Limited. 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. 15. If you modify the Library in any way whatsoever, the output from any such modified Library may not be referred to as 'InChI' or any similar name. Any attempt to refer to such output as 'InChI' will automatically terminate your rights under this Licence. 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 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 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 Instructions for Use You must attach the following notices to the library at the beginning of each source file - as a minimum each file needs to contain the "copyright" line and a link to the full notice. [INSERT YOUR LIBRARY'S NAME AND ITS PURPOSE] Copyright (C) [YEAR][COPYRIGHT OWNER] This library is free software; you can redistribute it and/or modify it under the terms of the IUPAC/InChI Trust InChI Licence 1.0, or any later version. Please note that this library is distributed WITHOUT ANY WARRANTIES whatsoever, whether expressed or implied. See the IUPAC/InChI Trust Licence for the International Chemical Identifier (InChI) Software version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No. 1.0") for more details. You should have received a copy of the IUPAC/InChI Trust InChI Licence No. 1.0 with this library; if not, please write to: The InChI Trust c/o FIZ CHEMIE Berlin Franklinstrasse 11 10587 Berlin GERMANY or email to: ulrich@inchi-trust.org. In the event that you require anything else or have any questions, please write to: [INSERT COPYRIGHT OWNERS DETAILS] [INSERT ADDRESS] or contact us via email at: [INSERT EMAIL ADDRESS] (C) 2011 IUPAC and InChI Trust Limited Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/000077500000000000000000000000001271037650300215515ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/aux2atom.h000066400000000000000000004277171271037650300235040ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* The code in this #include file reads InChI AuxInfo */ /****************************************************************************/ #define MIN_BOND_LENGTH (1.0e-6) #define INCHI_LINE_LEN 512 /*1024*/ /*256*/ #define INCHI_LINE_ADD 384 /*128*/ /*64*/ /* Note: (INCHI_LINE_LEN - INCHI_LINE_ADD) > (length of the longest item: szCoord) = 33 */ /*****************************************************************************/ #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #define AB_MAX_WELL_DEFINED_PARITY inchi_max(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* 1, 2 => well defined parities, uncluding 'unknown' */ #define AB_MIN_WELL_DEFINED_PARITY inchi_min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) */ #define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) #define inchi_NUMH2(AT,CUR_AT) ((AT[CUR_AT].num_iso_H[0]>0?AT[CUR_AT].num_iso_H[0]:0) +AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) #define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ #define SB_PARITY_SHFT 3 #define SB_PARITY_MASK 0x07 #define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ #define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ #endif #ifdef TARGET_API_LIB void FreeInchi_Atom( inchi_Atom **at ); inchi_Atom *CreateInchi_Atom( int num_atoms ); void FreeInchi_Input( inchi_Input *inp_at_data ); S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ); int is_element_a_metal( char szEl[] ); #endif #ifndef TARGET_EXE_USING_API void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ); int Extract0DParities( inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, int num_stereo0D, char *pStrErr, int *err, int vABParityUnknown); #endif #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) /* inchi_fgets */ #endif #ifdef TARGET_API_LIB /******************************************************************************************************/ void FreeInchi_Atom( inchi_Atom **at ) { if ( at && *at ) { inchi_free( *at ); *at = NULL; } } /******************************************************************************************************/ inchi_Atom *CreateInchi_Atom( int num_atoms ) { inchi_Atom *p = (inchi_Atom* ) inchi_calloc(num_atoms, sizeof(inchi_Atom) ); return p; } /******************************************************************************************************/ void FreeInchi_Input( inchi_Input *inp_at_data ) { FreeInchi_Atom( &inp_at_data->atom ); FreeInchi_Stereo0D( &inp_at_data->stereo0D ); memset( inp_at_data, 0, sizeof(*inp_at_data) ); } /*************************************************************************/ S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) { for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) ; return nPathLen? pathAtom : NULL; } /************************************************/ int is_element_a_metal( char szEl[] ) { static const char szMetals[] = "K;V;Y;W;U;" "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; const int len = strlen(szEl); const char *p; if ( 0 < len && len <= 2 && isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && (p = strstr(szMetals, szEl) ) && p[len] == ';' ) { return 1; /*return AtType_Metal;*/ } return 0; } #endif #ifndef TARGET_EXE_USING_API /******************************************************************************************************/ inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ) { return (inchi_Stereo0D* ) inchi_calloc(num_stereo0D, sizeof(inchi_Stereo0D) ); } /******************************************************************************************************/ void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ) { if ( stereo0D && *stereo0D ) { inchi_free( *stereo0D ); *stereo0D = NULL; } } #endif #define INPUT_FILE INCHI_IOSTREAM #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #ifdef TARGET_API_LIB #define INChITo_Atom ll_INChIToInchi_Atom #else #define INChITo_Atom ee_INChIToIbChI_Atom #define FindToken e_FindToken #define LoadLine e_LoadLine #endif #define AT_NUM_BONDS(AT) (AT).num_bonds #define ATOM_NUMBER AT_NUM #define IN_NEIGH_LIST is_in_the_slist /*#define INPUT_FILE INCHI_IOSTREAM*/ #define Create_Atom CreateInchi_Atom #define AT_BONDS_VAL(AT,I) AT[I].num_iso_H[0] #define ISOLATED_ATOM (-15) #define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K+1] #define IS_METAL_ATOM(AT,I) is_element_a_metal( AT[I].elname ) #else #define inchi_Atom inp_ATOM #define AT_NUM_BONDS(AT) (AT).valence #define ATOM_NUMBER AT_NUMB #define IN_NEIGH_LIST is_in_the_list #define inchi_NUMH2(AT,N) NUMH(AT,N) #define INChITo_Atom cc_INChIToInpAtom /*#define INPUT_FILE FILE*/ #define Create_Atom CreateInpAtom #define AT_BONDS_VAL(AT,I) AT[I].chem_bonds_valence #define ISOLATED_ATOM 15 #define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K] #define IS_METAL_ATOM(AT,I) is_el_a_metal( AT[I].el_number ) #endif /*****************************************************************************/ /* local prototypes */ char *FindToken( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, const char *sToken, int lToken, char *szLine, int nLenLine, char *p, int *res ); char *LoadLine( INPUT_FILE *inp_molfile, int *bTooLongLine, int *bItemIsOver, char **s, char *szLine, int nLenLine, int nMinLen2Load, char *p, int *res ); int INChITo_Atom(INPUT_FILE *inp_molfile, MOL_COORD **szCoord, inchi_Stereo0D **stereo0D, int *num_stereo0D, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) /*****************************************************************************/ int INChIToInchi_Atom ( INCHI_IOSTREAM *inp_molfile, inchi_Stereo0D **stereo0D, int *num_stereo0D, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); int INChIToInchi_Atom ( INCHI_IOSTREAM *inp_molfile, inchi_Stereo0D **stereo0D, int *num_stereo0D, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) { return INChITo_Atom ( inp_molfile, NULL, stereo0D, num_stereo0D, bDoNotAddH, vABParityUnknown, nInputType, at, max_num_at, num_dimensions, num_bonds, pSdfLabel, pSdfValue, Id, pInpAtomFlags, err, pStrErr ); } #else int INChIToInpAtom ( INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inp_ATOM **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); int INChIToInpAtom ( INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inp_ATOM **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) { return INChITo_Atom ( inp_molfile, szCoord, NULL, NULL, bDoNotAddH, vABParityUnknown, nInputType, at, max_num_at, num_dimensions, num_bonds, pSdfLabel, pSdfValue, Id, pInpAtomFlags, err, pStrErr ); } #endif /*****************************************************************************/ char *FindToken( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, const char *sToken, int lToken, char *szLine, int nLenLine, char *p, int *res ) { char *q; int res2; while ( !(q = strstr( p, sToken ) ) ) { if ( (q = strrchr( p, '/' )) && (q + lToken > szLine + *res) ) { *res -= q - szLine; /* res = the length of the szLine to be left in */ memmove( szLine, q, *res + 1); } else { *res = 0; } if ( !*bTooLongLine || 0 > (res2 = inchi_ios_getsTab1( szLine + *res, nLenLine - *res - 1, inp_molfile, bTooLongLine ) ) ) { /* the line is over or end of file */ return NULL; } else { *res += res2; p = szLine; } } return q + lToken; } /*****************************************************************************/ char *LoadLine( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, int *bItemIsOver, char **s, char *szLine, int nLenLine, int nMinLen2Load, char *p, int *res ) { int pos = p - szLine, res2; if ( !*bItemIsOver && nLenLine - (*res - pos) > nMinLen2Load ) { /* load the next portion if possible */ if ( pos ) { *res -= pos; memmove( szLine, p, *res+1 ); p = szLine; if ( *s ) { *s -= pos; } pos = 0; } res2 = inchi_ios_getsTab1( szLine + *res, nLenLine - *res - 1, inp_molfile, bTooLongLine ); if ( res2 > 0 ) { *bItemIsOver = ( (*s = strchr( p + *res, '/') ) || !*bTooLongLine ); *res += res2; } else { *bItemIsOver = 1; } } return p; } /*****************************************************************************/ int INChITo_Atom(INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, inchi_Stereo0D **stereo0D, int *num_stereo0D, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, int max_num_at, int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) { int num_atoms = 0, bFindNext = 0, len, bHeaderRead, bItemIsOver, bErrorMsg, bRestoreInfo; int bFatal = 0, num_struct = 0; int i, k, k2, res, bond_type, bond_stereo1, bond_stereo2, bond_char, neigh, bond_parity, bond_parityNM; int bTooLongLine, res2, bTooLongLine2, pos, hlen, hk; long longID; char szLine[INCHI_LINE_LEN], szNextLine[INCHI_LINE_ADD], *p, *q, *s, parity; int b2D=0, b3D=0, b23D, nNumBonds = 0, bNonZeroXYZ, bNonMetal; int len_stereo0D = 0, max_len_stereo0D = 0; inchi_Stereo0D *atom_stereo0D = NULL; inchi_Atom *atom = NULL; MOL_COORD *pszCoord = NULL; INCHI_MODE InpAtomFlags = 0; /* 0 or FLAG_INP_AT_NONCHIRAL or FLAG_INP_AT_CHIRAL */ static const char szIsoH[] = "hdt"; /* plain tags */ static const char sStructHdrPln[] = "Structure:"; static const char sStructHdrPlnNoLblVal[] = " is missing"; static char sStructHdrPlnAuxStart[64] =""; /*"$1.1Beta/";*/ static int lenStructHdrPlnAuxStart = 0; static const char sStructHdrPlnRevAt[] = "/rA:"; static const char sStructHdrPlnRevBn[] = "/rB:"; static const char sStructHdrPlnRevXYZ[] = "/rC:"; const char *sToken; int lToken; if ( !lenStructHdrPlnAuxStart ) { lenStructHdrPlnAuxStart = sprintf( sStructHdrPlnAuxStart, "AuxInfo=" ); } if ( at ) { if ( *at && max_num_at ) { memset( *at, 0, max_num_at * sizeof(**at) ); } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) if ( stereo0D && num_stereo0D ) { if ( *stereo0D && *num_stereo0D ) { max_len_stereo0D = *num_stereo0D; memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D )); } else { max_len_stereo0D = 0; } } #else if ( szCoord && *szCoord ) { inchi_free( *szCoord ); *szCoord = NULL; } #endif } else { bFindNext = 1; } bHeaderRead = bErrorMsg = bRestoreInfo = 0; *num_dimensions = *num_bonds = 0; /*************************************************************/ /* extract reversibility info from plain text INChI format */ /*************************************************************/ if ( nInputType == INPUT_INCHI_PLAIN ) { bHeaderRead = hk = 0; while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) ) { /********************* find and interpret structure header ************/ if ( !bTooLongLine && (hlen=sizeof(sStructHdrPln)-1, !memcmp(szLine, sStructHdrPln, hlen)) ) { p = szLine + hlen; longID = 0; num_atoms = 0; /* structure number */ longID = strtol( p, &q, 10 ); if ( q && q[0] == '.' && q[1] == ' ' ) { p = q+2; } p = p + strspn( p, " \n\r" ); if ( pSdfLabel ) { pSdfLabel[0] = '\0'; } if ( pSdfValue ) { pSdfValue[0] = '\0'; } if ( *p ) { /* has label name */ /*p ++;*/ if ( q = strchr( p, '=' ) ) { /* '=' separates label name from the value */ len = inchi_min( q-p+1, MAX_SDF_HEADER-1); if ( pSdfLabel ) { mystrncpy( pSdfLabel, p, len ); LtrimRtrim( pSdfLabel, &len ); } p = q+1; q = p + (int)strlen( p ); if ( q-p > 0 ) { len = inchi_min( q-p+1, MAX_SDF_VALUE-1); if ( pSdfValue ) { mystrncpy( pSdfValue, p, len ); } p = q; } } else if ( q = strstr( p, sStructHdrPlnNoLblVal ) ) { len = inchi_min( q-p+1, MAX_SDF_HEADER-1); if ( pSdfLabel ) { mystrncpy( pSdfLabel, p, len ); } p = q+1; } } if ( Id ) *Id = longID; bHeaderRead = 1; bErrorMsg = bRestoreInfo = 0; } else if ( !memcmp( szLine, sStructHdrPlnAuxStart, lenStructHdrPlnAuxStart) ) { /* found the header of the AuxInfo, read AuxInfo head of the line */ if ( !bHeaderRead ) { longID = 0; if ( Id ) *Id = longID; if ( pSdfLabel ) { pSdfLabel[0] = '\0'; } if ( pSdfValue ) { pSdfValue[0] = '\0'; } } bHeaderRead = 0; /* check for empty "AuxInfo=ver//" */ p = strchr( szLine + lenStructHdrPlnAuxStart, '/' ); if ( p && p[1] == '/' && (!p[2] || '\n' == p[2]) ) { goto bypass_end_of_INChI_plain; } /***************** search for atoms block (plain) **********************/ p = szLine; sToken = sStructHdrPlnRevAt; lToken = sizeof(sStructHdrPlnRevAt)-1; /* search for sToken in the line; load next segments of the line if sToken has not found */ p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, szLine, sizeof(szLine), p, &res ); if ( !p ) { *err = INCHI_INP_ERROR_ERR; num_atoms = INCHI_INP_ERROR_RET; MOLFILE_ERR_SET (*err, 0, "Missing atom data"); goto bypass_end_of_INChI_plain; } else { /* atoms block started */ i = 0; res2 = bTooLongLine2 = -1; bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; while ( 1 ) { p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); if ( !i ) { /* allocate atom */ num_atoms = strtol( p, &q, 10 ); if ( !num_atoms || !q || !*q ) { num_atoms = 0; /* no atom data */ goto bypass_end_of_INChI_plain; } p = q; /* Molfile chirality flag */ switch( *p ) { case 'c': InpAtomFlags |= FLAG_INP_AT_CHIRAL; p ++; break; case 'n': InpAtomFlags |= FLAG_INP_AT_NONCHIRAL; p ++; break; } if ( at && *at ) { if ( num_atoms > max_num_at ) { inchi_free( *at ); *at = NULL; } else { memset( *at, 0, max_num_at * sizeof( **at ) ); atom = *at; } } if ( !at || !*at ) { atom = Create_Atom( num_atoms+1 ); if ( !atom ) { num_atoms = INCHI_INP_FATAL_RET; /* was -1; error */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI_plain; } } if ( stereo0D && *stereo0D ) { if ( num_atoms > max_len_stereo0D ) { FreeInchi_Stereo0D( stereo0D ); } else { memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); atom_stereo0D = *stereo0D; } } if ( !stereo0D || !*stereo0D ) { max_len_stereo0D = num_atoms+1; atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); if ( !atom_stereo0D ) { num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI_plain; } } } /* element, first char */ if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) { break; /* end of atoms block */ } atom[i].elname[0] = *p ++; /* element, second char */ if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) { atom[i].elname[1] = *p ++; } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else atom[i].el_number = get_periodic_table_number( atom[i].elname ); #endif /* bonds' valence + number of non-isotopic H */ if ( isdigit( UCINT *p ) ) { AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); if ( !AT_BONDS_VAL(atom,i) ) AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ p = q; } /* charge */ atom[i].charge = (*p == '+')? 1 : (*p == '-')? -1 : 0; if ( atom[i].charge ) { p ++; if ( isdigit( UCINT *p ) ) { atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); p = q; } } /* radical */ if ( *p == '.' ) { p ++; if ( isdigit( UCINT *p ) ) { atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); p = q; } } /* isotopic mass */ if ( *p == 'i' ) { p ++; if ( isdigit( UCINT *p ) ) { int mw = strtol( p, &q, 10 ); p = q; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) atom[i].isotopic_mass = mw; #else mw -= get_atw_from_elnum( atom[i].el_number ); if ( mw >= 0 ) mw ++; atom[i].iso_atw_diff = mw; #endif } } /* parity */ switch( *p ) { case 'o': parity = INCHI_PARITY_ODD; p ++; break; case 'e': parity = INCHI_PARITY_EVEN; p ++; break; case 'u': parity = INCHI_PARITY_UNKNOWN; p ++; break; case '?': parity = INCHI_PARITY_UNDEFINED; p ++; break; default: parity = 0; break; } if ( parity ) { atom_stereo0D[len_stereo0D].central_atom = i; atom_stereo0D[len_stereo0D].parity = parity; atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; len_stereo0D ++; } /* isotopic h, d, t */ for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { if ( *p == szIsoH[k] ) { NUM_ISO_Hk(atom,i,k) = 1; p ++; if ( isdigit( UCINT *p ) ) { NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); p = q; } } } i ++; } if ( !bItemIsOver || i != num_atoms || s && p != s ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of atoms"); goto bypass_end_of_INChI_plain; } } /***************** search for bonds block (plain) and read it *****************/ /*p = szLine;*/ sToken = sStructHdrPlnRevBn; lToken = sizeof(sStructHdrPlnRevBn)-1; /* search for sToken in the line; load next segments of the line if sToken has not found */ p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, szLine, sizeof(szLine), p, &res ); if ( !p ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Missing bonds data"); goto bypass_end_of_INChI_plain; } else { /* bonds block started */ i = 1; res2 = bTooLongLine2 = -1; bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; if ( 1 == num_atoms ) { /* needed because the next '/' may be still out of szLine */ p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); } while ( i < num_atoms ) { p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); if ( i >= num_atoms || s && p >= s ) { break; /* end of bonds (plain) */ } /* bond, first char */ if ( *p == ';' ) { p ++; i ++; continue; } if ( !isalpha( UCINT *p ) ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); goto bypass_end_of_INChI_plain; } bond_char = *p ++; /* bond parity */ switch( *p ) { case '-': bond_parity = INCHI_PARITY_ODD; p ++; break; case '+': bond_parity = INCHI_PARITY_EVEN; p ++; break; case 'u': bond_parity = INCHI_PARITY_UNKNOWN; p ++; break; case '?': bond_parity = INCHI_PARITY_UNDEFINED; p ++; break; default: bond_parity = 0; break; } if ( bond_parity ) { switch( *p ) { case '-': bond_parityNM = INCHI_PARITY_ODD; p ++; break; case '+': bond_parityNM = INCHI_PARITY_EVEN; p ++; break; case 'u': bond_parityNM = INCHI_PARITY_UNKNOWN; p ++; break; case '?': bond_parityNM = INCHI_PARITY_UNDEFINED; p ++; break; default: bond_parityNM = 0; break; } } else { bond_parityNM = 0; } /* neighbor of the current atom */ if ( !isdigit( UCINT *p ) ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); goto bypass_end_of_INChI_plain; } neigh = (int)strtol( p, &q, 10 )-1; if ( i >= num_atoms || neigh >= num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); goto bypass_end_of_INChI_plain; } p = q; bond_stereo1 = bond_stereo2 = 0; /* bond type & 2D stereo */ switch( bond_char ) { case 'v': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; break; case 'V': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; break; case 'w': bond_type = INCHI_BOND_TYPE_DOUBLE; bond_stereo1 = bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; break; case 's': bond_type = INCHI_BOND_TYPE_SINGLE; break; case 'd': bond_type = INCHI_BOND_TYPE_DOUBLE; break; case 't': bond_type = INCHI_BOND_TYPE_TRIPLE; break; case 'a': bond_type = INCHI_BOND_TYPE_ALTERN; break; case 'p': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; break; case 'P': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; break; case 'n': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; break; case 'N': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; break; default: num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bond type"); goto bypass_end_of_INChI_plain; } k = AT_NUM_BONDS(atom[i]) ++; atom[i].bond_type[k] = bond_type; atom[i].bond_stereo[k] = bond_stereo1; atom[i].neighbor[k] = (ATOM_NUMBER)neigh; k2 = AT_NUM_BONDS(atom[neigh]) ++; atom[neigh].bond_type[k2] = bond_type; atom[neigh].bond_stereo[k2] = bond_stereo2; atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; bond_parity |= (bond_parityNM << SB_PARITY_SHFT); if ( bond_parity ) { if ( max_len_stereo0D <= len_stereo0D ) { /* realloc atom_Stereo0D */ inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); if ( !new_atom_stereo0D ) { num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI_plain; } memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); FreeInchi_Stereo0D( &atom_stereo0D ); atom_stereo0D = new_atom_stereo0D; max_len_stereo0D += num_atoms; } /* (a) i may be allene endpoint and neigh = allene middle point or (b) i may be allene middle point and neigh = allene endpoint !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! */ atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ atom_stereo0D[len_stereo0D].neighbor[2] = i; atom_stereo0D[len_stereo0D].parity = bond_parity; atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ len_stereo0D ++; } } if ( !bItemIsOver || i != num_atoms || s && p != s ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of bonds"); goto bypass_end_of_INChI_plain; } } /***************** search for coordinates block (plain) **********************/ /*p = szLine;*/ sToken = sStructHdrPlnRevXYZ; lToken = sizeof(sStructHdrPlnRevXYZ)-1; /* search for sToken in the line; load next segments of the line if sToken has not found */ p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, szLine, sizeof(szLine), p, &res ); if ( !p ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Missing atom coordinates data"); goto bypass_end_of_INChI_plain; } else { /* coordinates block started */ if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) { memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); } else { num_atoms = INCHI_INP_FATAL_RET; /* allocation error */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI_plain; } i = 0; res2 = bTooLongLine2 = -1; bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; while ( i < num_atoms ) { p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); if ( i >= num_atoms || s && p >= s ) { break; /* end of bonds (plain) */ } /* coord, first char */ if ( *p == ';' ) { for ( k = 0; k < NUM_COORD; k ++ ) { pszCoord[i][LEN_COORD*k + 4] = '0'; } p ++; i ++; continue; } for ( k = 0; k < 3; k ++ ) { double xyz; bNonZeroXYZ = 0; if ( *p == ';' ) { pszCoord[i][LEN_COORD*k + 4] = '0'; xyz = 0.0; } else if ( *p == ',' ) { /* empty */ pszCoord[i][LEN_COORD*k + 4] = '0'; xyz = 0.0; p ++; } else { xyz = strtod( p, &q ); bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; if ( q != NULL ) { memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); if ( *q == ',' ) q ++; p = q; } else { pszCoord[i][LEN_COORD*k + 4] = '0'; } } switch( k ) { case 0: atom[i].x = xyz; b2D |= bNonZeroXYZ; break; case 1: atom[i].y = xyz; b2D |= bNonZeroXYZ; break; case 2: b3D |= bNonZeroXYZ; atom[i].z = xyz; break; } } if ( *p == ';' ) { p ++; /* end of this triple of coordinates */ i ++; } else { num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong atom coordinates data"); goto bypass_end_of_INChI_plain; } } if ( !bItemIsOver || s && p != s || i != num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of coordinates"); goto bypass_end_of_INChI_plain; } } /* end of coordinates */ /* set special valences and implicit H (xml) */ b23D = b2D | b3D; b2D = b3D = 0; if ( at ) { if ( !*at ) { int a1, a2, n1, n2, valence; int chem_bonds_valence; int nX=0, nY=0, nZ=0, nXYZ; *at = atom; /* special valences */ for ( bNonMetal = 0; bNonMetal < 1; bNonMetal ++ ) { for ( a1 = 0; a1 < num_atoms; a1 ++ ) { int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else int bHasMetalNeighbor=0; #endif memset( num_bond_type, 0, sizeof(num_bond_type) ); valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ AT_BONDS_VAL(atom, a1) = 0; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else atom[a1].orig_at_number = a1+1; #endif nX = nY = nZ = 0; for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { bond_type = 0; MOLFILE_ERR_SET (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); } num_bond_type[ bond_type ] ++; nNumBonds ++; if ( b23D ) { neigh = atom[a1].neighbor[n1]; nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); } } chem_bonds_valence = 0; for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; } if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { /* accept input aromatic bonds for now */ switch ( n2 ) { case 2: chem_bonds_valence += 3; /* =A- */ break; case 3: chem_bonds_valence += 4; /* =A< */ break; default: /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { ATOM_NUMBER *p1; a2 = atom[a1].neighbor[n1]; p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); if ( p1 ) { atom[a1].bond_type[n1] = atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; } else { *err = -2; /* Program error */ MOLFILE_ERR_SET (*err, 0, "Program error interpreting InChI aux"); num_atoms = 0; goto bypass_end_of_INChI_plain; /* no structure */ } } } chem_bonds_valence += n2; *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ MOLFILE_ERR_SET (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); break; } } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) /************************************************************************************* * * Set number of hydrogen atoms */ { int num_iso_H; num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; if ( valence == ISOLATED_ATOM ) { atom[a1].num_iso_H[0] = 0; } else if ( valence && valence >= chem_bonds_valence ) { atom[a1].num_iso_H[0] = valence - chem_bonds_valence; } else if ( valence || bDoNotAddH ) { atom[a1].num_iso_H[0] = 0; } else if ( !bDoNotAddH ) { atom[a1].num_iso_H[0] = -1; /* auto add H */ } } #else /* added 2006-07-19 to process aromatic bonds same way as from molfile */ if ( n2 && !valence ) { int num_H = NUMH(atom, a1); /* only isotopic */ int chem_valence = chem_bonds_valence; int bUnusualValenceArom = detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, atom[a1].radical, chem_valence, num_H, atom[a1].valence ); int bUnusualValenceNoArom = detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, atom[a1].radical, chem_valence-1, num_H, atom[a1].valence ); #if ( CHECK_AROMBOND2ALT == 1 ) if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) #else if ( bUnusualValenceArom && !bUnusualValenceNoArom ) #endif { /* typically NH in 5-member aromatic ring */ chem_bonds_valence --; } } else if ( n2 && valence ) { /* atom has aromatic bonds AND the chemical valence is known */ int num_H = NUMH(atom, a1); int chem_valence = chem_bonds_valence + num_H; if ( valence == chem_valence-1 ) { /* typically NH in 5-member aromatic ring */ chem_bonds_valence --; } } atom[a1].chem_bonds_valence = chem_bonds_valence; atom[a1].num_H = get_num_H( atom[a1].elname, atom[a1].num_H, atom[a1].num_iso_H, atom[a1].charge, atom[a1].radical, atom[a1].chem_bonds_valence, valence, 0, bDoNotAddH, bHasMetalNeighbor ); #endif } } nNumBonds /= 2; if ( b23D && nNumBonds ) { nXYZ = nX+nY+nZ; b2D = (nXYZ > 0); b3D = (nXYZ == 3); *num_dimensions = b3D? 3 : b2D? 2 : 0; *num_bonds = nNumBonds; } /*======= 0D parities =================================*/ #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) { *stereo0D = atom_stereo0D; *num_stereo0D = len_stereo0D; } else { FreeInchi_Stereo0D( &atom_stereo0D ); *num_stereo0D = len_stereo0D = 0; } #endif for ( i = 0; i < len_stereo0D; i ++ ) { ATOM_NUMBER *p1, *p2; int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; switch( atom_stereo0D[i].type ) { case INCHI_StereoType_Tetrahedral: a1 = atom_stereo0D[i].central_atom; if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) { int ii, kk = 0; if ( AT_NUM_BONDS(atom[a1]) == 3 ) { atom_stereo0D[i].neighbor[kk++] = a1; } for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) { atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; } } break; case INCHI_StereoType_DoubleBond: #define MAX_CHAIN_LEN 20 a1 = atom_stereo0D[i].neighbor[1]; a2 = atom_stereo0D[i].neighbor[2]; p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); if ( !p1 || !p2 ) { atom_stereo0D[i].type = INCHI_StereoType_None; atom_stereo0D[i].central_atom = NO_ATOM; atom_stereo0D[i].neighbor[0] = atom_stereo0D[i].neighbor[3] = -1; *err |= 64; /* Error in cumulene stereo */ MOLFILE_ERR_SET (*err, 0, "0D stereobond not recognized"); break; } /* streobond, allene, or cumulene */ sb_ord_from_a1 = p1 - atom[a1].neighbor; sb_ord_from_a2 = p2 - atom[a2].neighbor; if ( AT_NUM_BONDS(atom[a1]) == 2 && atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, a1) && (AT_NUM_BONDS(atom[a2]) != 2 || atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ } if ( AT_NUM_BONDS(atom[a2]) == 2 && atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, a2) && (AT_NUM_BONDS(atom[a1]) != 2 || atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ } if ( bEnd2 + bEnd1 == 1 ) { /* allene or cumulene */ ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; if ( bEnd2 && !bEnd1 ) { cur = a1; a1 = a2; a2 = cur; sb_ord_from_a1 = sb_ord_from_a2; } sb_ord_from_a2 = -1; cur = a1; next = a2; len = 0; chain[len++] = cur; chain[len++] = next; while ( len < MAX_CHAIN_LEN ) { /* arbitrary very high upper limit to prevent infinite loop */ prev = cur; cur = next; /* follow double bond path && avoid going back */ if ( AT_NUM_BONDS(atom[cur]) == 2 && atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, cur) ) { next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; chain[len++] = next; } else { break; } } if ( len > 2 && (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) { sb_ord_from_a2 = p2 - atom[cur].neighbor; a2 = cur; /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; atom_stereo0D[i].neighbor[1] = a1; atom_stereo0D[i].neighbor[2] = a2; atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; if ( len % 2 ) { atom_stereo0D[i].central_atom = chain[len/2]; atom_stereo0D[i].type = INCHI_StereoType_Allene; } else { atom_stereo0D[i].central_atom = NO_ATOM; } } else { /* error */ atom_stereo0D[i].type = INCHI_StereoType_None; atom_stereo0D[i].central_atom = NO_ATOM; atom_stereo0D[i].neighbor[0] = atom_stereo0D[i].neighbor[3] = -1; *err |= 64; /* Error in cumulene stereo */ MOLFILE_ERR_SET (*err, 0, "Cumulene stereo not recognized (0D)"); } #undef MAX_CHAIN_LEN } else { /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ sb_ord_from_a1 = p1 - atom[a1].neighbor; sb_ord_from_a2 = p2 - atom[a2].neighbor; atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; atom_stereo0D[i].central_atom = NO_ATOM; } if ( atom_stereo0D[i].type != INCHI_StereoType_None && sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) { /* Detected well-defined disconnected stereo * locate first non-metal neighbors */ int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; for ( k = 0; k < 2; k ++ ) { a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; min_neigh = num_atoms; for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) { cur_neigh = atom[a].neighbor[j]; if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) { min_neigh = inchi_min( cur_neigh, min_neigh ); } } if ( min_neigh < num_atoms ) { atom_stereo0D[i].neighbor[k?3:0] = min_neigh; } else { MOLFILE_ERR_SET (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); } } } break; } } /* end of 0D parities extraction */ /*exit_cycle:;*/ } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else /* transfer atom_stereo0D[] to atom[] */ if ( len_stereo0D ) { Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, pStrErr, err, vABParityUnknown ); } #endif if ( pInpAtomFlags ) { /* save chirality flag */ *pInpAtomFlags |= InpAtomFlags; } } else if ( atom ) { inchi_free( atom ); atom = NULL; } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else #if ( FIX_READ_AUX_MEM_LEAK == 1 ) /* 2005-08-04 avoid memory leak */ if ( atom_stereo0D && !(stereo0D && *stereo0D == atom_stereo0D) ) { FreeInchi_Stereo0D( &atom_stereo0D ); } #endif if ( szCoord ) { *szCoord = pszCoord; pszCoord = NULL; } else #endif if ( pszCoord ) { inchi_free( pszCoord ); pszCoord = NULL; } goto bypass_end_of_INChI_plain; /*return num_atoms;*/ } } if ( atom_stereo0D ) { FreeInchi_Stereo0D( &atom_stereo0D ); } /* end of struct. reading cycle */ if ( res <= 0 ) { if ( *err == INCHI_INP_ERROR_ERR ) { return num_atoms; } *err = INCHI_INP_EOF_ERR; return INCHI_INP_EOF_RET; /* no more data */ } bypass_end_of_INChI_plain: /* cleanup */ if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) { if ( stereo0D && *stereo0D == atom_stereo0D ) { *stereo0D = NULL; *num_stereo0D = 0; } FreeInchi_Stereo0D( &atom_stereo0D ); } while ( bTooLongLine && 0 < inchi_ios_getsTab1( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) { ; } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) /* cleanup */ if ( !*at ) { if ( atom ) { inchi_free( atom ); atom = NULL; } if ( pszCoord ) { inchi_free( pszCoord ); pszCoord = NULL; } } #endif return num_atoms; } /***********************************************************/ /* extract reversibility info from xml text INChI format */ /* */ /* OBSOLETE CODE because InChI output in XML */ /* does not exist anymore. Unsupported. */ /* */ /***********************************************************/ if ( nInputType == INPUT_INCHI_XML ) { /* xml tags */ static const char sStructHdrXml[] = " max_num_at ) { inchi_free( *at ); *at = NULL; } else { memset( *at, 0, max_num_at * sizeof( **at ) ); atom = *at; } } if ( !at || !*at ) { atom = Create_Atom( num_atoms+1 ); if ( !atom ) { num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI; } } if ( stereo0D && *stereo0D ) { if ( num_atoms > max_len_stereo0D ) { FreeInchi_Stereo0D( stereo0D ); } else { memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); atom_stereo0D = *stereo0D; } } if ( !stereo0D || !*stereo0D ) { max_len_stereo0D = num_atoms+1; atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); if ( !atom_stereo0D ) { num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI; } } i = 0; bItemIsOver = 0; res2 = bTooLongLine2 = -1; /* read all atoms xml */ while ( i < num_atoms ) { pos = p - szLine; if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { /* load next line if possible */ res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevAtEnd, sizeof(sStructRevXmlRevAtEnd)-1) ) { if ( pos ) { res -= pos; /* number of chars left to process in szLine */ memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ } memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); res += res2; szLine[res] = '\0'; bTooLongLine = bTooLongLine2; p = szLine; } else { bItemIsOver = 1; } } /* element, first char */ if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) { bHeaderRead = 0; /* wrong atom data */ num_atoms = INCHI_INP_ERROR_RET; /* was 0, error */ *err = INCHI_INP_ERROR_ERR; /* 40 */ MOLFILE_ERR_SET (*err, 0, "Wrong atoms data"); goto bypass_end_of_INChI; } atom[i].elname[0] = *p ++; /* element, second char */ if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) { atom[i].elname[1] = *p ++; } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else atom[i].el_number = get_periodic_table_number( atom[i].elname ); #endif /* bonds' valence */ if ( isdigit( UCINT *p ) ) { AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); if ( !AT_BONDS_VAL(atom,i) ) AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ p = q; } /* charge */ atom[i].charge = (*p == '+')? 1 : (*p == '-')? -1 : 0; if ( atom[i].charge ) { p ++; if ( isdigit( UCINT *p ) ) { atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); p = q; } } /* radical */ if ( *p == '.' ) { p ++; if ( isdigit( UCINT *p ) ) { atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); p = q; } } /* isotopic mass */ if ( *p == 'i' ) { p ++; if ( isdigit( UCINT *p ) ) { int mw = strtol( p, &q, 10 ); p = q; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) atom[i].isotopic_mass = mw; #else mw -= get_atw_from_elnum( atom[i].el_number ); if ( mw >= 0 ) mw ++; atom[i].iso_atw_diff = mw; #endif } } /* parity */ switch( *p ) { case 'o': parity = INCHI_PARITY_ODD; p ++; break; case 'e': parity = INCHI_PARITY_EVEN; p ++; break; case 'u': parity = INCHI_PARITY_UNKNOWN; p ++; break; case '?': parity = INCHI_PARITY_UNDEFINED; p ++; break; default: parity = 0; break; } if ( parity ) { atom_stereo0D[len_stereo0D].central_atom = i; atom_stereo0D[len_stereo0D].parity = parity; atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; len_stereo0D ++; } /* isotopic h, d, t */ for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { if ( *p == szIsoH[k] ) { NUM_ISO_Hk(atom,i,k) = 1; p ++; if ( isdigit( UCINT *p ) ) { NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); p = q; } } } i ++; } if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of atoms"); goto bypass_end_of_INChI; } /********************** bonds xml ****************************/ res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); if ( res <= 0 ) { num_atoms = 0; /* no data */ goto bypass_end_of_INChI; } if ( memcmp(szLine, sStructRevXmlRevBn, sizeof(sStructRevXmlRevBn)-1) ) { bHeaderRead = 0; /* invalid reversibility info; look for another header */ continue; } /* read (the head of) the xml bonds line */ res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); if ( res <= 0 ) { num_atoms = INCHI_INP_ERROR_RET; /* was 0; error: no data -- eof? */ *err = INCHI_INP_ERROR_ERR; goto bypass_end_of_INChI; } i = 1; bItemIsOver = 0; res2 = bTooLongLine2 = -1; p = szLine; if ( !memcmp(szLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) { /* empty bonds section */ res = 0; bItemIsOver = 1; } /* read all bonds (xml), starting from atom 1 (not 0) */ while ( i < num_atoms ) { pos = p - szLine; if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { /* load next line if possible */ res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) { if ( pos ) { res -= pos; /* number of chars left to process in szLine */ memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ } memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); res += res2; szLine[res] = '\0'; bTooLongLine = bTooLongLine2; p = szLine; } else { bItemIsOver = 1; } } if ( i >= num_atoms ) { break; } /* bond, first char */ if ( *p == ';' ) { p ++; i ++; continue; } if ( !isalpha( UCINT *p ) ) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); goto bypass_end_of_INChI; } bond_char = *p ++; /* bond parity */ switch( *p ) { case '-': bond_parity = INCHI_PARITY_ODD; p ++; break; case '+': bond_parity = INCHI_PARITY_EVEN; p ++; break; case 'u': bond_parity = INCHI_PARITY_UNKNOWN; p ++; break; case '?': bond_parity = INCHI_PARITY_UNDEFINED; p ++; break; default: bond_parity = 0; break; } if ( bond_parity ) { switch( *p ) { case '-': bond_parityNM = INCHI_PARITY_ODD; p ++; break; case '+': bond_parityNM = INCHI_PARITY_EVEN; p ++; break; case 'u': bond_parityNM = INCHI_PARITY_UNKNOWN; p ++; break; case '?': bond_parityNM = INCHI_PARITY_UNDEFINED; p ++; break; default: bond_parityNM = 0; break; } } else { bond_parityNM = 0; } /* neighbor of the current atom */ if ( !isdigit( UCINT *p ) ) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); goto bypass_end_of_INChI; } neigh = (int)strtol( p, &q, 10 )-1; if ( i >= num_atoms || neigh >= num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); goto bypass_end_of_INChI; } p = q; bond_stereo1 = bond_stereo2 = 0; /* bond type & 2D stereo */ switch( bond_char ) { case 'v': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; break; case 'V': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; break; case 'w': bond_type = INCHI_BOND_TYPE_DOUBLE; bond_stereo1 = bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; break; case 's': bond_type = INCHI_BOND_TYPE_SINGLE; break; case 'd': bond_type = INCHI_BOND_TYPE_DOUBLE; break; case 't': bond_type = INCHI_BOND_TYPE_TRIPLE; break; case 'a': bond_type = INCHI_BOND_TYPE_ALTERN; break; case 'p': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; break; case 'P': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; break; case 'n': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; break; case 'N': bond_type = INCHI_BOND_TYPE_SINGLE; bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; break; default: num_atoms = INCHI_INP_ERROR_RET; /* error */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong bond type"); goto bypass_end_of_INChI; } k = AT_NUM_BONDS(atom[i]) ++; atom[i].bond_type[k] = bond_type; atom[i].bond_stereo[k] = bond_stereo1; atom[i].neighbor[k] = (ATOM_NUMBER)neigh; k2 = AT_NUM_BONDS(atom[neigh]) ++; atom[neigh].bond_type[k2] = bond_type; atom[neigh].bond_stereo[k2] = bond_stereo2; atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; bond_parity |= (bond_parityNM << SB_PARITY_SHFT); if ( bond_parity ) { if ( max_len_stereo0D <= len_stereo0D ) { /* realloc atom_Stereo0D */ inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); if ( !new_atom_stereo0D ) { num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI; } memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); FreeInchi_Stereo0D( &atom_stereo0D ); atom_stereo0D = new_atom_stereo0D; max_len_stereo0D += num_atoms; } /* (a) i may be allene endpoint and neigh = allene middle point or (b) i may be allene middle point and neigh = allene endpoint !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! */ atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ atom_stereo0D[len_stereo0D].neighbor[2] = i; atom_stereo0D[len_stereo0D].parity = bond_parity; atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ len_stereo0D ++; } } if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of bonds"); goto bypass_end_of_INChI; } /********************** coordinates xml ****************************/ if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) { memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); if ( res <= 0 || /* compare the header */ memcmp(szLine, sStructRevXmlRevXYZ, sizeof(sStructRevXmlRevXYZ)-1) || /* read (the head of) the coordinates (xml) line */ 0 >= (res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ))) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Missing atom coordinates data"); goto bypass_end_of_INChI; } i = 0; bItemIsOver = 0; res2 = bTooLongLine2 = -1; p = szLine; if ( !memcmp(szLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) { /* empty bonds section */ res = 0; bItemIsOver = 1; } /* read all coordinates (xml), starting from atom 1 (not 0) */ while ( i < num_atoms ) { pos = p - szLine; if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { /* load next line if possible */ res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) { if ( pos ) { res -= pos; /* number of chars left to process in szLine */ memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ } memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); res += res2; szLine[res] = '\0'; bTooLongLine = bTooLongLine2; p = szLine; } else { bItemIsOver = 1; } } /* coord, first char */ if ( *p == ';' ) { for ( k = 0; k < NUM_COORD; k ++ ) { pszCoord[i][LEN_COORD*k + 4] = '0'; } p ++; i ++; continue; } for ( k = 0; k < 3; k ++ ) { double xyz; bNonZeroXYZ = 0; if ( *p == ';' ) { pszCoord[i][LEN_COORD*k + 4] = '0'; xyz = 0.0; } else if ( *p == ',' ) { /* empty */ pszCoord[i][LEN_COORD*k + 4] = '0'; xyz = 0.0; p ++; } else { xyz = strtod( p, &q ); bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; if ( q != NULL ) { memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); if ( *q == ',' ) q ++; p = q; } else { pszCoord[i][LEN_COORD*k + 4] = '0'; } } switch( k ) { case 0: atom[i].x = xyz; b2D |= bNonZeroXYZ; break; case 1: atom[i].y = xyz; b2D |= bNonZeroXYZ; break; case 2: b3D |= bNonZeroXYZ; atom[i].z = xyz; break; } } if ( *p == ';' ) { p ++; /* end of this triple of coordinates */ i ++; } else { num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong atom coordinates data"); goto bypass_end_of_INChI; } } if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ *err = INCHI_INP_ERROR_ERR; MOLFILE_ERR_SET (*err, 0, "Wrong number of coordinates"); goto bypass_end_of_INChI; } } else { /* allocation failed */ num_atoms = INCHI_INP_FATAL_RET; *err = INCHI_INP_FATAL_ERR; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); goto bypass_end_of_INChI; } /* set special valences and implicit H (xml) */ b23D = b2D | b3D; b2D = b3D = 0; if ( at ) { if ( !*at ) { int a1, a2, n1, n2, valence; int chem_bonds_valence; int nX=0, nY=0, nZ=0, nXYZ; *at = atom; /* special valences */ for ( bNonMetal = 0; bNonMetal < 1 /*2*/; bNonMetal ++ ) { for ( a1 = 0; a1 < num_atoms; a1 ++ ) { int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else int bHasMetalNeighbor=0; #endif memset( num_bond_type, 0, sizeof(num_bond_type) ); valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ AT_BONDS_VAL(atom, a1) = 0; #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else atom[a1].orig_at_number = a1+1; #endif nX = nY = nZ = 0; for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { bond_type = 0; /* cannot happen */ MOLFILE_ERR_SET (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); } num_bond_type[ bond_type ] ++; nNumBonds ++; if ( b23D ) { neigh = atom[a1].neighbor[n1]; nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); } } chem_bonds_valence = 0; for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; } if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { /* accept input aromatic bonds for now */ switch ( n2 ) { case 2: chem_bonds_valence += 3; /* =A- */ break; case 3: chem_bonds_valence += 4; /* =A< */ break; default: /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { ATOM_NUMBER *p1; a2 = atom[a1].neighbor[n1]; p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); if ( p1 ) { atom[a1].bond_type[n1] = atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; } else { *err = -2; /* Program error */ MOLFILE_ERR_SET (*err, 0, "Program error interpreting InChI aux"); num_atoms = 0; goto bypass_end_of_INChI; /* no structure */ } } } chem_bonds_valence += n2; *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ MOLFILE_ERR_SET (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); break; } } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) /************************************************************************************* * * Set number of hydrogen atoms */ { int num_iso_H; num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; if ( valence == ISOLATED_ATOM ) { atom[a1].num_iso_H[0] = 0; } else if ( valence && valence >= chem_bonds_valence ) { atom[a1].num_iso_H[0] = valence - chem_bonds_valence; } else if ( valence || bDoNotAddH ) { atom[a1].num_iso_H[0] = 0; } else if ( !bDoNotAddH ) { atom[a1].num_iso_H[0] = -1; /* auto add H */ } } #else /* added 2006-07-19 to process aromatic bonds same way as from molfile */ if ( n2 && !valence ) { int num_H = NUMH(atom, a1); /* only isotopic */ int chem_valence = chem_bonds_valence; int bUnusualValenceArom = detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, atom[a1].radical, chem_valence, num_H, atom[a1].valence ); int bUnusualValenceNoArom = detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, atom[a1].radical, chem_valence-1, num_H, atom[a1].valence ); #if ( CHECK_AROMBOND2ALT == 1 ) if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) #else if ( bUnusualValenceArom && !bUnusualValenceNoArom ) #endif { /* typically NH in 5-member aromatic ring */ chem_bonds_valence --; } } else if ( n2 && valence ) { /* atom has aromatic bonds AND the chemical valence is known */ int num_H = NUMH(atom, a1); int chem_valence = chem_bonds_valence + num_H; if ( valence == chem_valence-1 ) { /* typically NH in 5-member aromatic ring */ chem_bonds_valence --; } } atom[a1].chem_bonds_valence = chem_bonds_valence; atom[a1].num_H = get_num_H( atom[a1].elname, atom[a1].num_H, atom[a1].num_iso_H, atom[a1].charge, atom[a1].radical, atom[a1].chem_bonds_valence, valence, 0, bDoNotAddH, bHasMetalNeighbor ); #endif } } nNumBonds /= 2; if ( b23D && nNumBonds ) { nXYZ = nX+nY+nZ; b2D = (nXYZ > 0); b3D = (nXYZ == 3); *num_dimensions = b3D? 3 : b2D? 2 : 0; *num_bonds = nNumBonds; } /*======= 0D parities =================================*/ #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) { *stereo0D = atom_stereo0D; *num_stereo0D = len_stereo0D; } else { FreeInchi_Stereo0D( &atom_stereo0D ); *num_stereo0D = len_stereo0D = 0; } #endif for ( i = 0; i < len_stereo0D; i ++ ) { ATOM_NUMBER *p1, *p2; int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; switch( atom_stereo0D[i].type ) { case INCHI_StereoType_Tetrahedral: a1 = atom_stereo0D[i].central_atom; if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) { int ii, kk = 0; if ( AT_NUM_BONDS(atom[a1]) == 3 ) { atom_stereo0D[i].neighbor[kk++] = a1; } for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) { atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; } } break; case INCHI_StereoType_DoubleBond: #define MAX_CHAIN_LEN 20 a1 = atom_stereo0D[i].neighbor[1]; a2 = atom_stereo0D[i].neighbor[2]; p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); if ( !p1 || !p2 ) { atom_stereo0D[i].type = INCHI_StereoType_None; atom_stereo0D[i].central_atom = NO_ATOM; atom_stereo0D[i].neighbor[0] = atom_stereo0D[i].neighbor[3] = -1; *err |= 64; /* Error in cumulene stereo */ MOLFILE_ERR_SET (*err, 0, "0D stereobond not recognized"); break; } /* streobond, allene, or cumulene */ sb_ord_from_a1 = p1 - atom[a1].neighbor; sb_ord_from_a2 = p2 - atom[a2].neighbor; if ( AT_NUM_BONDS(atom[a1]) == 2 && atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, a1) && (AT_NUM_BONDS(atom[a2]) != 2 || atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ } if ( AT_NUM_BONDS(atom[a2]) == 2 && atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, a2) && (AT_NUM_BONDS(atom[a1]) != 2 || atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ } if ( bEnd2 + bEnd1 == 1 ) { /* allene or cumulene */ ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; if ( bEnd2 && !bEnd1 ) { cur = a1; a1 = a2; a2 = cur; sb_ord_from_a1 = sb_ord_from_a2; } sb_ord_from_a2 = -1; cur = a1; next = a2; len = 0; chain[len++] = cur; chain[len++] = next; while ( len < MAX_CHAIN_LEN ) { /* arbitrary very high upper limit to prevent infinite loop */ prev = cur; cur = next; /* follow double bond path && avoid going back */ if ( AT_NUM_BONDS(atom[cur]) == 2 && atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && 0 == inchi_NUMH2(atom, cur) ) { next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; chain[len++] = next; } else { break; } } if ( len > 2 && (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) { sb_ord_from_a2 = p2 - atom[cur].neighbor; a2 = cur; /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; atom_stereo0D[i].neighbor[1] = a1; atom_stereo0D[i].neighbor[2] = a2; atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; if ( len % 2 ) { atom_stereo0D[i].central_atom = chain[len/2]; atom_stereo0D[i].type = INCHI_StereoType_Allene; } else { atom_stereo0D[i].central_atom = NO_ATOM; } } else { /* error */ atom_stereo0D[i].type = INCHI_StereoType_None; atom_stereo0D[i].central_atom = NO_ATOM; atom_stereo0D[i].neighbor[0] = atom_stereo0D[i].neighbor[3] = -1; *err |= 64; /* Error in cumulene stereo */ MOLFILE_ERR_SET (*err, 0, "Cumulene stereo not recognized (0D)"); } #undef MAX_CHAIN_LEN } else { /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ sb_ord_from_a1 = p1 - atom[a1].neighbor; sb_ord_from_a2 = p2 - atom[a2].neighbor; atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; atom_stereo0D[i].central_atom = NO_ATOM; } if ( atom_stereo0D[i].type != INCHI_StereoType_None && sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) { /* Detected well-defined disconnected stereo * locate first non-metal neighbors */ int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; for ( k = 0; k < 2; k ++ ) { a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; min_neigh = num_atoms; for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) { cur_neigh = atom[a].neighbor[j]; if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) { min_neigh = inchi_min( cur_neigh, min_neigh ); } } if ( min_neigh < num_atoms ) { atom_stereo0D[i].neighbor[k?3:0] = min_neigh; } else { MOLFILE_ERR_SET (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); } } } break; } } /* end of 0D parities extraction */ /*exit_cycle:;*/ } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else /* transfer atom_stereo0D[] to atom[] */ if ( len_stereo0D ) { Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, pStrErr, err, vABParityUnknown ); } #endif if ( pInpAtomFlags ) { /* save chirality flag */ *pInpAtomFlags |= InpAtomFlags; } } else if ( atom ) { inchi_free( atom ); atom = NULL; } #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else if ( szCoord ) { *szCoord = pszCoord; pszCoord = NULL; } else #endif if ( pszCoord ) { inchi_free( pszCoord ); } goto bypass_end_of_INChI; /*return num_atoms;*/ } } if ( atom_stereo0D ) { FreeInchi_Stereo0D( &atom_stereo0D ); } /* end of struct. reading cycle, code never used? */ if ( res <= 0 ) { if ( *err == INCHI_INP_ERROR_ERR ) { return num_atoms; } *err = INCHI_INP_EOF_ERR; return INCHI_INP_EOF_RET; /* no more data */ } bypass_end_of_INChI: /* cleanup */ if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) { if ( stereo0D && *stereo0D == atom_stereo0D ) { *stereo0D = NULL; *num_stereo0D = 0; } FreeInchi_Stereo0D( &atom_stereo0D ); } if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) num_struct --; if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) num_struct ++; while ( num_struct > 0 && 0 < inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) { if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) num_struct --; else if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) num_struct ++; } return num_atoms; } return num_atoms; #undef AT_NUM_BONDS #undef ATOM_NUMBER #undef IN_NEIGH_LIST #undef inchi_NUMH2 #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) #else #undef inchi_Atom #endif #undef AT_NUM_BONDS #undef ATOM_NUMBER #undef IN_NEIGH_LIST #undef inchi_NUMH2 #undef INChITo_Atom #undef MoreParms #undef INPUT_FILE #undef Create_Atom #undef AT_BONDS_VAL #undef ISOLATED_ATOM #undef NUM_ISO_Hk #undef IS_METAL_ATOM } #ifdef TARGET_EXE_USING_API /**********************************************************************************/ int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) { /* inp_ATOM *at = NULL; */ int num_dimensions_new; int num_inp_bonds_new; int num_inp_atoms_new; int num_inp_0D_new; inchi_Atom *at_new = NULL; inchi_Atom *at_old = NULL; inchi_Stereo0D *stereo0D_new = NULL; inchi_Stereo0D *stereo0D_old = NULL; int nNumAtoms = 0, nNumStereo0D = 0; MOL_COORD *szCoordNew = NULL; MOL_COORD *szCoordOld = NULL; int i, j; if ( pStrErr ) { pStrErr[0] = '\0'; } /*FreeOrigAtData( orig_at_data );*/ if ( lSdfId ) *lSdfId = 0; do { at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; szCoordOld = NULL; num_inp_atoms_new = INChIToInchi_Atom( inp_molfile, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, bDoNotAddH, vABParityUnknown, nInputType, orig_at_data? &at_new:NULL, MAX_ATOMS, &num_dimensions_new, &num_inp_bonds_new, pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); if ( num_inp_atoms_new <= 0 && !*err ) { MOLFILE_ERR_SET (*err, 0, "Empty structure"); *err = 98; } else if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { *err = 0; /* end of file */ break; } else if ( num_inp_atoms_new > 0 && orig_at_data ) { /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; if ( nNumAtoms >= MAX_ATOMS ) { MOLFILE_ERR_SET (*err, 0, "Too many atoms"); *err = 70; orig_at_data->num_atoms = -1; } else if ( !at_old ) { /* the first structure */ orig_at_data->atom = at_new; at_new = NULL; orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; } else if ( orig_at_data->atom = CreateInchi_Atom( nNumAtoms ) ) { /* switch at_new <--> orig_at_data->at; */ if ( orig_at_data->num_atoms ) { memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); /* adjust numbering in the newly read structure */ for ( i = 0; i < num_inp_atoms_new; i ++ ) { for ( j = 0; j < at_new[i].num_bonds; j ++ ) { at_new[i].neighbor[j] += orig_at_data->num_atoms; } } } FreeInchi_Atom( &at_old ); /* copy newly read structure */ memcpy( orig_at_data->atom + orig_at_data->num_atoms, at_new, num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); /* cpy newly read 0D stereo */ if ( num_inp_0D_new > 0 && stereo0D_new ) { if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) { memcpy( orig_at_data->stereo0D, stereo0D_old, orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]) ); /* adjust numbering in the newly read structure */ for ( i = 0; i < num_inp_0D_new; i ++ ) { if ( stereo0D_new[i].central_atom >= 0 ) { stereo0D_new[i].central_atom += orig_at_data->num_atoms; } for ( j = 0; j < 4; j ++ ) { stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; } } FreeInchi_Stereo0D( &stereo0D_old ); memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, stereo0D_new, num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]) ); } else { num_inp_0D_new = 0; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); *err = -1; } } else { num_inp_0D_new = 0; } /* update lengths */ orig_at_data->num_atoms += num_inp_atoms_new; orig_at_data->num_stereo0D += num_inp_0D_new; } else { MOLFILE_ERR_SET (*err, 0, "Out of RAM"); *err = -1; } } else if ( num_inp_atoms_new > 0 ) { nNumAtoms += num_inp_atoms_new; } FreeInchi_Atom( &at_new ); num_inp_atoms_new = 0; FreeInchi_Stereo0D( &stereo0D_new ); num_inp_0D_new = 0; } while ( !*err && bMergeAllInputStructures ); /* if ( !*err ) { orig_at_data->num_components = MarkDisconnectedComponents( orig_at_data ); if ( orig_at_data->num_components == 0 ) { MOLFILE_ERR_SET (*err, 0, "No components found"); *err = 99; } if ( orig_at_data->num_components < 0 ) { MOLFILE_ERR_SET (*err, 0, "Too many components"); *err = 99; } } */ if ( szCoordNew ) { inchi_free( szCoordNew ); } if ( at_new ) { inchi_free( at_new ); } /* if ( !*err ) { if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) { MOLFILE_ERR_SET (*err, 0, "Cannot reconcile stereobond parities"); if (!orig_at_data->num_atoms) { *err = 1; } } } */ if ( *err ) { FreeInchi_Input( orig_at_data ); } if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ } return orig_at_data? orig_at_data->num_atoms : nNumAtoms; } #endif #ifndef TARGET_EXE_USING_API #undef AB_MAX_WELL_DEFINED_PARITY #undef AB_MIN_WELL_DEFINED_PARITY #include "extr_ct.h" /****************************************************************************************/ int Extract0DParities( inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, int num_stereo0D, char *pStrErr, int *err, int vABParityUnknown) { /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ if ( stereo0D && num_stereo0D > 0 ) { int i0D, a2, k, k_prev, type, j, j1, j2, len, parity, parityNM; int sb_ord_from_i1, sb_ord_from_i2, sn_ord_from_i1, sn_ord_from_i2; AT_NUMB i1n, i2n, i1, i2; for ( i0D = 0; i0D < num_stereo0D; i0D ++ ) { parity = (stereo0D[i0D].parity & SB_PARITY_MASK); parityNM = (stereo0D[i0D].parity & SB_PARITY_FLAG) >> SB_PARITY_SHFT; if ( parity == INCHI_PARITY_NONE || parity != INCHI_PARITY_ODD && parity != INCHI_PARITY_EVEN && parity != INCHI_PARITY_UNKNOWN && parity != INCHI_PARITY_UNDEFINED ) { char szTemp[16]; sprintf( szTemp, "#%d", i0D+1 ); MOLFILE_ERR_SET (*err, 0, "Wrong 0D stereo descriptor(s):"); MOLFILE_ERR_SET (*err, 0, szTemp); continue; /* warning */ } type = stereo0D[i0D].type; a2 = stereo0D[i0D].central_atom; /* central atom or -1 */ j = -1; len = 0; sb_ord_from_i1 = sb_ord_from_i2 = sn_ord_from_i1 = sn_ord_from_i2 = -1; i1n = i2n = i1 = i2 = MAX_ATOMS+1; if ( (type == INCHI_StereoType_Tetrahedral || type == INCHI_StereoType_Allene ) && 0 <= a2 && a2 < nNumAtoms || type == INCHI_StereoType_DoubleBond && a2 == NO_ATOM) { /* test the quadruplet */ for ( j = 0, k_prev = -1; j < 4; j ++, k_prev = k ) { k = stereo0D[i0D].neighbor[j]; if ( k < 0 || k >= nNumAtoms || k_prev == k ) break; /* tetrahedral atom connectivity test */ if ( type == INCHI_StereoType_Tetrahedral && k != a2 && !is_in_the_list( at[a2].neighbor, (AT_NUMB)k, at[a2].valence) ) { break; } /* Double bond, Cumulene and allene are tested in the next if() */ } } /* find in the adjacency lists the double bond neighbor that leads to the opposite atom */ if ( j == 4 && (type == INCHI_StereoType_Allene || type == INCHI_StereoType_DoubleBond) ) { AT_NUMB *p1 = NULL, *p2 = NULL, *q1 = NULL, *q2 = NULL; i1n = (AT_NUMB)stereo0D[i0D].neighbor[0]; i1 = (AT_NUMB)stereo0D[i0D].neighbor[1]; i2 = (AT_NUMB)stereo0D[i0D].neighbor[2]; i2n = (AT_NUMB)stereo0D[i0D].neighbor[3]; /* find q1 and q2 */ if ( !(q1 = is_in_the_list( at[i1].neighbor, i1n, at[i1].valence)) || !(q2 = is_in_the_list( at[i2].neighbor, i2n, at[i2].valence)) ) { j = -2; /* error flag */ } else /* allene or cumulene; follow double bonds from i1 to i2 */ if ( !(p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence)) ) { /* at[i1] and at[i2] are not connected: can be only allene or cumulene */ AT_NUMB prev, cur, next; int num_dbond, i, next_ord, half_len; cur = next = i1; len = half_len = 0; while ( len < 20 ) { /* arbitrary very high upper limit to prevent infinite loop */ prev = cur; cur = next; for ( i = 0, num_dbond = 0; i < at[cur].valence; i ++ ) { /* follow double bond path && avoid going back */ if ( at[cur].bond_type[i] == BOND_TYPE_DOUBLE && prev != at[cur].neighbor[i] ) { next = at[cur].neighbor[i]; next_ord = i; num_dbond ++; } } if ( num_dbond == 1 && next != i1 ) { len ++; if ( len == 1 ) { sb_ord_from_i1 = next_ord; } if ( type == INCHI_StereoType_Allene && next == (AT_NUMB)a2 ) { half_len = len; } } else { break; } } if ( cur == i2 && prev != cur && 0 == num_dbond && len > 1 && (p2 = is_in_the_list( at[i2].neighbor, prev, at[i2].valence)) && (type != INCHI_StereoType_Allene || len == 2*half_len )) { sb_ord_from_i2 = p2 - at[i2].neighbor; sn_ord_from_i1 = q1 - at[i1].neighbor; sn_ord_from_i2 = q2 - at[i2].neighbor; } else { j = -5; /* error flag */ } } else /* allene must have been already processed, otherwise error */ if ( type == INCHI_StereoType_Allene ) { /* error: atoms #1 and #2 of allene are connected */ j = -3; /* error flag */ } else /* double bond only; the bond type is not checked because at the end of the normalization it may happen to be alternating */ if ( type == INCHI_StereoType_DoubleBond && (p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence) ) ) { sb_ord_from_i1 = p1 - at[i1].neighbor; sb_ord_from_i2 = p2 - at[i2].neighbor; sn_ord_from_i1 = q1 - at[i1].neighbor; sn_ord_from_i2 = q2 - at[i2].neighbor; } else { j = -4; /* error flag */ } } if ( j != 4 ) { char szTemp[16]; sprintf( szTemp, "#%d", i0D+1 ); MOLFILE_ERR_SET (*err, 0, "Wrong 0D stereo descriptor(s):"); MOLFILE_ERR_SET (*err, 0, szTemp); continue; /* error */ } switch ( type ) { case INCHI_StereoType_None: continue; case INCHI_StereoType_DoubleBond: case INCHI_StereoType_Allene: for ( j1 = 0; j1 < MAX_NUM_STEREO_BONDS && at[i1].sb_parity[j1]; j1 ++ ) ; for ( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && at[i2].sb_parity[j2]; j2 ++ ) ; if ( j1 < MAX_NUM_STEREO_BONDS && j2 < MAX_NUM_STEREO_BONDS && sb_ord_from_i1 >= 0 && sb_ord_from_i2 >= 0 && sn_ord_from_i1 >= 0 && sn_ord_from_i2 >= 0) { switch( parity ) { case INCHI_PARITY_ODD: at[i1].sb_parity[j1] = AB_PARITY_ODD; at[i2].sb_parity[j2] = AB_PARITY_EVEN; break; case INCHI_PARITY_EVEN: at[i1].sb_parity[j1] = AB_PARITY_ODD; at[i2].sb_parity[j2] = AB_PARITY_ODD; break; case INCHI_PARITY_UNDEFINED: at[i1].sb_parity[j1] = AB_PARITY_UNDF; at[i2].sb_parity[j2] = AB_PARITY_UNDF; break; default: if ( parity == INCHI_PARITY_UNKNOWN ) { at[i1].sb_parity[j1] = vABParityUnknown; at[i2].sb_parity[j2] = vABParityUnknown; } else { at[i1].sb_parity[j1] = AB_PARITY_NONE; at[i2].sb_parity[j2] = AB_PARITY_NONE; } break; } switch( parityNM ) { case INCHI_PARITY_ODD: at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; at[i2].sb_parity[j2] |= AB_PARITY_EVEN << SB_PARITY_SHFT; break; case INCHI_PARITY_EVEN: at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; at[i2].sb_parity[j2] |= AB_PARITY_ODD << SB_PARITY_SHFT; break; case INCHI_PARITY_UNDEFINED: at[i1].sb_parity[j1] |= AB_PARITY_UNDF << SB_PARITY_SHFT; at[i2].sb_parity[j2] |= AB_PARITY_UNDF << SB_PARITY_SHFT; break; default: if ( parityNM == INCHI_PARITY_UNKNOWN ) { at[i1].sb_parity[j1] |= vABParityUnknown << SB_PARITY_SHFT; at[i2].sb_parity[j2] |= vABParityUnknown << SB_PARITY_SHFT; } break; } at[i1].sb_ord[j1] = sb_ord_from_i1; at[i1].sn_ord[j1] = sn_ord_from_i1; at[i1].sn_orig_at_num[j1] = at[i1n].orig_at_number; at[i2].sb_ord[j2] = sb_ord_from_i2; at[i2].sn_ord[j2] = sn_ord_from_i2; at[i2].sn_orig_at_num[j2] = at[i2n].orig_at_number; } break; case INCHI_StereoType_Tetrahedral: switch( parity ) { case INCHI_PARITY_ODD: at[a2].p_parity = AB_PARITY_ODD; break; case INCHI_PARITY_EVEN: at[a2].p_parity = AB_PARITY_EVEN; break; case INCHI_PARITY_UNDEFINED: at[a2].p_parity = AB_PARITY_UNDF; break; default: if (parity == INCHI_PARITY_UNKNOWN ) { at[a2].p_parity = vABParityUnknown; break; } else continue; } for ( j = 0; j < 4; j ++ ) { k = stereo0D[i0D].neighbor[j]; at[a2].p_orig_at_num[j] = at[k].orig_at_number; } break; default: break; } } /* take care of Unknown stereobonds: */ /* copy their Unknown stereo descriptors to at->bond_stereo (2005-03-01) */ /* Note: to this stage, unk/undef set to what was requested */ /*( through vABParityUnknown ) (2009-12-12) */ FixUnkn0DStereoBonds(at, nNumAtoms); #ifdef TARGET_API_LIB if ( k = ReconcileAllCmlBondParities( at, nNumAtoms, 0 ) ) { char szErrCode[16]; sprintf( szErrCode, "%d", k); AddMOLfileError( pStrErr, "0D Parities Reconciliation failed:" ); AddMOLfileError( pStrErr, szErrCode ); } #endif } return 0; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/extr_ct.h000066400000000000000000000321771271037650300234040ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __EXTR_CT_H__ #define __EXTR_CT_H__ #include "mode.h" #include "ichisize.h" struct AtData { char element[3]; int maxvalence; }; #define NUM_CHEM_ELEMENTS 127 /* well above number of known chem. elements */ #define AT_ISO_SORT_KEY_MULT 32 /* up to 32 identical hydrogen isotopes */ /* (similar to T_GROUP_ISOWT_MULT) */ /* changed from 16 9-12-2003 */ typedef long AT_ISO_SORT_KEY; /* signed, should hold up to 4096*max_iso_diff */ /* (similar to T_GROUP_ISOWT) */ /* = num_1H + AT_ISO_SORT_KEY_MULT*(num_D + AT_ISO_SORT_KEY_MULT*(num_T+AT_ISO_SORT_KEY_MULT*iso_atw_diff)) */ /* typedef signed char AT_ISOTOPIC; */ /* + or - */ typedef struct tagStereoCarb { AT_NUMB at_num; U_CHAR parity; } AT_STEREO_CARB; typedef struct tagStereoDble { AT_NUMB at_num1; AT_NUMB at_num2; U_CHAR parity; } AT_STEREO_DBLE; typedef struct tagIsotopicAtom { AT_NUMB at_num; NUM_H num_1H; NUM_H num_D; NUM_H num_T; NUM_H iso_atw_diff; } AT_ISOTOPIC; typedef AT_NUMB AT_STEREO; #define BYTE_BITS 8 /* number of bits in one byte */ #define BOND_MASK 0xf /* 4 bits */ #define BOND_BITS 4 /* 3 or 4 does not matter; 2 is too small for BOND_TAUTOM */ #define BOND_ADD (BOND_BITS==2?-1:0) /* subtract 1 from bonds stored in CT */ typedef struct tagAtom { char elname[ATOM_EL_LEN]; AT_NUMB neighbor[MAXVAL]; /* changed to unsigned 2-2-95. D.Ch. */ AT_NUMB init_rank; /* also used in remove_terminal_HDT() to save orig. at. number */ AT_NUMB orig_at_number; AT_NUMB orig_compt_at_numb; /* low 3 bits=bond type; high 5 bits (in case of cut-vertex atom) = an attached part number */ U_CHAR bond_type[MAXVAL]; U_CHAR el_number; /* periodic table number = charge of the nucleus = number of the protons */ /* U_CHAR hill_type; */ /* number in psudo hill order */ S_CHAR valence; S_CHAR chem_bonds_valence; /* 8-24-00 to treat tautomer centerpoints, etc. */ S_CHAR num_H; /* first not including D, T; add_DT_to_num_H() includes. */ S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* num 1H, 2H(D), 3H(T) */ S_CHAR cFlags; S_CHAR iso_atw_diff; /* abs(iso_atw_diff) < 127 or 31 - ??? */ AT_ISO_SORT_KEY iso_sort_key; /* = num_1H + AT_ISO_SORT_KEY_MULT^1*num_D + AT_ISO_SORT_KEY_MULT^2*num_T + AT_ISO_SORT_KEY_MULT^3*iso_atw_diff */ S_CHAR charge; S_CHAR radical; /* 1=>doublet(.), 2=> triplet as singlet (:) ???? why are they same ???? */ S_CHAR marked; AT_NUMB endpoint; /* tautomer analysis. If != 0 then the hydrogens & (-)charge are in the tautomer group. */ /* Pairs stereo_bond_neighbor[] and stereo_bond_neighbor2[], etc initially refer to non-isotopic and isotopic cases, respectively. To use same stereo processing code these arrays are swapped when switching from non-isotopic to isotopic processing and back. */ AT_NUMB stereo_bond_neighbor[MAX_NUM_STEREO_BONDS]; /* Original number of an opposite atom */ AT_NUMB stereo_bond_neighbor2[MAX_NUM_STEREO_BONDS]; /* (stereo bond neighbor) +1; */ S_CHAR stereo_bond_ord[MAX_NUM_STEREO_BONDS]; /* Ordering number of a bond/neighbor in the direction to the */ S_CHAR stereo_bond_ord2[MAX_NUM_STEREO_BONDS]; /* stereo bond opposite atom (important for cumulenes); */ S_CHAR stereo_bond_z_prod[MAX_NUM_STEREO_BONDS]; /* Relative atom-neighbors */ S_CHAR stereo_bond_z_prod2[MAX_NUM_STEREO_BONDS]; /* double bond planes orientation; */ S_CHAR stereo_bond_parity[MAX_NUM_STEREO_BONDS]; /* parity + MULT_STEREOBOND*chain_length, */ S_CHAR stereo_bond_parity2[MAX_NUM_STEREO_BONDS]; /* where: */ /* parity (Mask 0x07=BITS_PARITY): 0 = AB_PARITY_NONE = not a stereo bond 1/2 = AB_PARITY_ODD/EVEN = bond parity defined from initial ranks 3 = AB_PARITY_UNKN = geometry is unknown to the user 4 = AB_PARITY_UNDF = not enough geometry info to find the parity 6 = AB_PARITY_CALC = calculate later from the neighbor ranks; some ot them can be replaced with AB_PARITY_ODD/EVEN after equivalence ranks have been determined length (Mask 0x38=MASK_CUMULENE_LEN, length=stereo_bond_parity[i]/MULT_STEREOBOND): 0 => double or alternating stereogenic bond 1 => cumulene with 2 double bonds (stereogenic center) 2 => cumulene with 3 double bonds (stereogenic bond) length <= (MAX_CUMULENE_LEN=2) bit KNOWN_PARITIES_EQL = 0x40: all pairs of const. equ. atoms are connected by stereo bonds and these bonds have identical parities */ S_CHAR parity; /* -- Mask 0x07=BITS_PARITY: -- 0 = AB_PARITY_NONE => no parity; also parity&0x38 = 0 1 = AB_PARITY_ODD => odd parity 2 = AB_PARITY_EVEN => even parity 3 = AB_PARITY_UNKN => user marked as unknown parity 4 = AB_PARITY_UNDF => parity cannot be defined because of symmetry or not well defined geometry */ S_CHAR parity2; /* parity including parity due to isotopic terminal H */ /* bit msks: 0x07 => known parity (1,2,3,4) or AB_PARITY_CALC=6, AB_PARITY_IISO = 6 */ /* 0x40 => KNOWN_PARITIES_EQL */ S_CHAR stereo_atom_parity; /* similar to stereo_bond_parity[]: known in advance AB_PARITY_* value + KNOWN_PARITIES_EQL bit */ S_CHAR stereo_atom_parity2; S_CHAR final_parity; /* defined by equivalence ranks */ S_CHAR final_parity2; /* defined by equivalence ranks, incl. due to terminal isotopic H */ S_CHAR bAmbiguousStereo; S_CHAR bHasStereoOrEquToStereo; S_CHAR bHasStereoOrEquToStereo2; #if ( FIND_RING_SYSTEMS == 1 ) S_CHAR bCutVertex; AT_NUMB nRingSystem; AT_NUMB nNumAtInRingSystem; AT_NUMB nBlockSystem; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) AT_NUMB nDistanceFromTerminal; #endif #endif S_CHAR z_dir[3]; } sp_ATOM ; #define BOND_SINGLE BOND_TYPE_SINGLE /* 1 */ #define BOND_DOUBLE BOND_TYPE_DOUBLE /* 2 */ #define BOND_TRIPLE BOND_TYPE_TRIPLE /* 3 */ #define BOND_ALTERN BOND_TYPE_ALTERN /* 4 single/double */ #define BOND_ALT_123 5 /* single/double/triple */ #define BOND_ALT_13 6 /* single/triple */ #define BOND_ALT_23 7 /* double/triple */ #define BOND_TAUTOM 8 #define BOND_ALT12NS 9 #define BOND_NUMDIF 9 /* number of different kinds of bonds */ #define BOND_TYPE_MASK 0x0f #define BOND_MARK_ALL 0xf0 /* complement to BOND_TYPE_MASK */ #define BOND_MARK_ALT12 0x10 #define BOND_MARK_ALT123 0x20 #define BOND_MARK_ALT13 0x30 #define BOND_MARK_ALT23 0x40 #define BOND_MARK_ALT12NS 0x50 /* 1 or 2, non-stereo */ #define BOND_MARK_MASK 0x70 #define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) #define BITS_PARITY 0x07 /* mask to retrieve half-bond parity */ #define MASK_CUMULENE_LEN 0x38 /* mask to retrieve (cumulene chain length - 1)*MULT_STEREOBOND */ #define KNOWN_PARITIES_EQL 0x40 /* parity is same for all pairs of constit. equivalent atoms */ #define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ #define MULT_STEREOBOND 0x08 /* multiplier for cumulene chain length odd length => chiral, even length => stereogenic bond */ #define MAKE_BITS_CUMULENE_LEN(X) ((X)*MULT_STEREOBOND) #define GET_BITS_CUMULENE_LEN(X) ((X)&MASK_CUMULENE_LEN) #define BOND_CHAIN_LEN(X) (GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND) /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ #define IS_ALLENE_CHAIN(X) ((GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND)%2) /* atom or bond parity value definitions */ #define AB_PARITY_NONE 0 /* 0 => no parity; also parity&0x38 = 0 */ #define AB_PARITY_ODD 1 /* 1 => odd parity */ #define AB_PARITY_EVEN 2 /* 2 => even parity */ #define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ #define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ #define AB_PARITY_IISO 5 /* 5 => no parity because of identical atoms */ #define AB_PARITY_CALC 6 /* 6 => calculate parity later */ #define AB_PARITY_0D 8 /* 8 => bit signifies 0D case -- not used */ #define AB_INV_PARITY_BITS (AB_PARITY_ODD ^ AB_PARITY_EVEN) #define AB_MAX_KNOWN_PARITY 4 /* precalculated from const. equivalence parities */ #define AB_MIN_KNOWN_PARITY 1 #define AB_MAX_PART_DEFINED_PARITY 3 /* 1, 2, 3 => defined parities, uncluding 'unknown' */ #define AB_MIN_PART_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN, AB_PARITY_UNKN) */ #define AB_MAX_WELL_DEFINED_PARITY 2 /* 1, 2 => well defined parities, uncluding 'unknown' */ #define AB_MIN_WELL_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN) */ #define AB_MIN_ILL_DEFINED_PARITY 3 #define AB_MAX_ILL_DEFINED_PARITY 4 #define AB_MAX_ANY_PARITY 4 #define AB_MIN_ANY_PARITY 1 #define AMBIGUOUS_STEREO 1 #define AMBIGUOUS_STEREO_ATOM 2 #define AMBIGUOUS_STEREO_BOND 4 #define AMBIGUOUS_STEREO_ATOM_ISO 8 #define AMBIGUOUS_STEREO_BOND_ISO 16 #define AMBIGUOUS_STEREO_ERROR 32 #define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ #define ATOM_PARITY_VAL(X) (X) #define ATOM_PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= (X) && (X) <= AB_MAX_PART_DEFINED_PARITY) #define ATOM_PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_ILL_DEFINED_PARITY) #define ATOM_PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= (X) && (X) <= AB_MAX_KNOWN_PARITY) #define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) #define ATOM_PARITY_NOT_UNKN(X) (ATOM_PARITY_KNOWN(X) && (X) != AB_PARITY_UNKN) #define PARITY_VAL(X) ((X) & BITS_PARITY) #define PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_PART_DEFINED_PARITY) #define PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_ILL_DEFINED_PARITY) #define PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_KNOWN_PARITY) #define PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_WELL_DEFINED_PARITY) #define PARITY_CALCULATE(X) (AB_PARITY_CALC == PARITY_VAL(X)) #define BOND_PARITY_PART_DEFINED(X) (PARITY_PART_DEF(X) || PARITY_CALCULATE(X)) #define BOND_PARITY_PART_KNOWN(X) (PARITY_KNOWN(X) || PARITY_CALCULATE(X)) #define ALL_BUT_PARITY(X) ((X)&~BITS_PARITY) #define ALWAYS_SET_STEREO_PARITY 0 #define NO_ISOLATED_NON_6RING_AROM_BOND 0 /* for Yuri */ #define SAVE_6_AROM_CENTERS 0 /* for Yuri */ #endif /* __EXTR_CT_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichi.h000066400000000000000000000347311271037650300226460ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHI_H__ #define __INCHI_H__ #include "incomdef.h" #define REQ_MODE_BASIC 0x000001 /* B include Fixed-H layer */ #define REQ_MODE_TAUT 0x000002 /* T include Mobile-H layer */ #define REQ_MODE_ISO 0x000004 /* I */ #define REQ_MODE_NON_ISO 0x000008 /* NI */ #define REQ_MODE_STEREO 0x000010 /* S */ #define REQ_MODE_ISO_STEREO 0x000020 /* IS */ #define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ #define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ #define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ /* new 10-10-2003 */ #define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ #define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ #define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ #define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ #define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ /* end of 10-10-2003 */ /*^^^ 2009-12-05 */ #define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ /*^^^ 2009-12-05 */ #define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ #define REQ_MODE_MIN_SB_RING_SHFT 16 #define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) #define WARN_FAILED_STEREO 0x0001 #define WARN_FAILED_ISOTOPIC 0x0002 #define WARN_FAILED_ISOTOPIC_STEREO 0x0004 #define ERR_NO_CANON_RESULTS 0x0008 /*********** compare components flags **********************************/ #define CMP_COMPONENTS 0x0001 /* perform compare components */ #define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ #define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ /****************** chemical identifier member definitions *************/ typedef struct tagINChI_IsotopicAtom { AT_NUMB nAtomNumber; /* Canonical atom number */ NUM_H nIsoDifference; /* 0=non-isotopic; 1=rounded avg. atomic mass */ NUM_H nNum_H; /* number of 1H isotopic atoms attached */ NUM_H nNum_D; /* number of 2H isotopic atoms attached */ NUM_H nNum_T; /* number of 3H isotopic atoms attached */ } INChI_IsotopicAtom; typedef struct tagINChI_IsotopicTGroup { AT_NUMB nTGroupNumber; /* Tautomeric group number */ AT_NUMB nNum_H; /* number of 1H isotopic atoms */ AT_NUMB nNum_D; /* number of 2H isotopic atoms */ AT_NUMB nNum_T; /* number of 3H isotopic atoms */ } INChI_IsotopicTGroup; typedef struct tagINChI_Stereo { /* [N] = allocated length */ /* ---- possibly tetrahedral stereogenic atoms */ int nNumberOfStereoCenters; AT_NUMB *nNumber; /* Canonical number of a possibly tetrahedral * stereogenic atom or allenes [nNumberOfAtoms] */ S_CHAR *t_parity; /* tetrahedral (relative, see nCompInv2Abs) atom parities [nNumberOfAtoms] */ /* ---- possibly tetrahedral stereogenic atoms of the iverted structure */ AT_NUMB *nNumberInv; /* Canonical number of a possibly tetrahedral * stereogenic atom or allene [nNumberOfAtoms] */ S_CHAR *t_parityInv; /* tetrahedral inverted atom parities [nNumberOfAtoms] */ /* bFlagAbsStereoIsInverted = nCompInv2Abs==-1: Abs stereo = Inverted */ int nCompInv2Abs; /* 0=>Inv = Abs stereo; -1=> Inv < Abs stereo, +1=> Inv > Abs stereo; +2=> in reading InChI: no /m was found and in /sN N>0 */ int bTrivialInv; /* 1=> nCompInv2Abs!= 0 && Inverted = Abs stereo with inverted parities 1<-->2 */ /* ---- possibly stereogenic bonds and tetrahedral cumuleles */ int nNumberOfStereoBonds; AT_NUMB *nBondAtom1; /* Canonical number of a first atom * [number of bonds] */ AT_NUMB *nBondAtom2; /* Canonical number of a second atom * [number of bonds] */ S_CHAR *b_parity; /* possibly stereogenic bond parities * [number of bonds] */ } INChI_Stereo; #define INCHI_FLAG_ACID_TAUT 0x0001 /* tautomerism of dissociated acid invoked */ #define INCHI_FLAG_REL_STEREO 0x0002 /* requested relative stereo */ #define INCHI_FLAG_RAC_STEREO 0x0004 /* requested racemic stereo */ #define INCHI_FLAG_SC_IGN_ALL_UU 0x0008 /* ignored all undefined/unknown stereocenters, non-isotopic */ #define INCHI_FLAG_SB_IGN_ALL_UU 0x0010 /* ignored all undefined/unknown stereocenters, non-isotopic */ #define INCHI_FLAG_SC_IGN_ALL_ISO_UU 0x0020 /* ignored all undefined/unknown stereocenters, isotopic */ #define INCHI_FLAG_SB_IGN_ALL_ISO_UU 0x0040 /* ignored all undefined/unknown stereocenters, isotopic */ #define INCHI_FLAG_HARD_ADD_REM_PROTON 0x0080 /* in normalization a proton has been added or removed along alt path */ #define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ #define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ #define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ #define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ #define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ #define INCHI_OUT_XML 0x0020 /* output xml INChI */ #define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ #define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ #define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */ #define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ #define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ #define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ #define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ #define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ #define INCHI_OUT_STDINCHI 0x4000 #define INCHI_OUT_SAVEOPT 0x8000 /* Bits encoding InChI creation options to be saved */ #define SAVE_OPT_SLUUD 0x0001 #define SAVE_OPT_SUU 0x0002 #define SAVE_OPT_FIXEDH 0x0004 #define SAVE_OPT_RECMET 0x0008 #define SAVE_OPT_KET 0x0010 #define SAVE_OPT_15T 0x0020 #define INCHI_OUT_PRINT_OPTIONS (INCHI_OUT_EMBED_REC | \ INCHI_OUT_XML | \ INCHI_OUT_PLAIN_TEXT | \ INCHI_OUT_PLAIN_TEXT_COMMENTS | \ INCHI_OUT_XML_TEXT_COMMENTS) /*******REQ_MODE_SB_IGN_ALL_UU*************** chemical identifier definition *****************/ typedef struct tagINChI { /* [N] = allocated length */ int nErrorCode; /* 0 = success */ INCHI_MODE nFlags; /* INCHI_FLAG_ACID_TAUT tautomerism of dissociated acid invoked INCHI_FLAG_REL_STEREO requested relative stereo INCHI_FLAG_RAC_STEREO requested racemic stereo INCHI_FLAG_SC_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic INCHI_FLAG_SB_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic INCHI_FLAG_SC_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic INCHI_FLAG_SB_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic INCHI_FLAG_HARD_ADD_REM_PROTON in normalization a proton has been added or removed along alt path */ /* ---- basic & tautomer layer */ int nTotalCharge; int nNumberOfAtoms; char *szHillFormula; U_CHAR *nAtom; /* atomic numbers [nNumberOfAtoms] from the Periodic Table */ int lenConnTable; AT_NUMB *nConnTable; /* Connection table [nNumberOfAtoms+NumberOfBonds] */ int lenTautomer; AT_NUMB *nTautomer; /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 1} - old * NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} - new * Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom; in tautomeric * representation these H on tautomeric atoms are not included [nNumberOfAtoms] */ S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms, * in non-atautomeric representation only [nNumberOfAtoms] */ /* ---- isotopic & isotopic tautomeric layer */ int nNumberOfIsotopicAtoms; INChI_IsotopicAtom *IsotopicAtom; /* [nNumberOfIsotopicAtoms] */ int nNumberOfIsotopicTGroups; /* in reversing InChI keeps a pointer to stolen from AuxInfo coordinates */ INChI_IsotopicTGroup *IsotopicTGroup; /* [nNumberOfIsotopicAtoms] */ /* ---- stereo layer */ INChI_Stereo *Stereo; INChI_Stereo *StereoIsotopic; /* not including mobile H groups */ AT_NUMB *nPossibleLocationsOfIsotopicH; /* [0]=> length including 0th element, location1,...*/ int bDeleted; #if ( bREUSE_INCHI == 1 ) int nRefCount; #endif #if ( bRELEASE_VERSION == 0 ) int bExtract; #endif #if ( READ_INCHI_STRING == 1 ) int nLink; /* negative: ignore InChI; positive: index of (Reconnected component) + 1 linked to it */ #endif } INChI; typedef INChI *PINChI2[TAUT_NUM]; typedef struct tagOrigInfo { S_CHAR cCharge; S_CHAR cRadical; /* 0=none, 1=doublet, 2=triplet, 3=unknown */ S_CHAR cUnusualValence; /* see get_unusual_el_valence() */ } ORIG_INFO; /******************** auxiliary chemical identifier info **************/ typedef struct tagINChI_Aux { /* [N] = allocated length */ int nErrorCode; /* 0 = success */ int nNumberOfAtoms; int nNumberOfTGroups; /* non-zero only in tautomeric representation */ int bIsIsotopic; /* filled out even though isotopic has not been requested */ int bIsTautomeric; /* filled out even though tautomeric has not been requested; non-zero if taut exists */ /* canonical numbers of the atoms: nOrigAtNosInCanonOrd[i-1]+1 = */ /* input atom number for the canonical number i */ AT_NUMB *nOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ AT_NUMB *nIsotopicOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ /* same for the inverted structure */ AT_NUMB *nOrigAtNosInCanonOrdInv; /* inveterted stereo [nNumberOfInputAtoms*1.5]; */ AT_NUMB *nIsotopicOrigAtNosInCanonOrdInv; /* [nNumberOfInputAtoms*1.5]; */ AT_NUMB *nConstitEquNumbers; /* [nNumberOfAtoms*1.5] */ AT_NUMB *nConstitEquTGroupNumbers; /* [nNumberOfAtoms/2] */ AT_NUMB *nConstitEquIsotopicNumbers; /* [nNumberOfAtoms*1.5] */ AT_NUMB *nConstitEquIsotopicTGroupNumbers; /* [nNumberOfAtoms/2] */ #if ( bREUSE_INCHI == 1 ) int nRefCount; #endif #if ( TEST_RENUMB_ATOMS == 1 ) unsigned long ulNormTime; unsigned long ulCanonTime; #endif ORIG_INFO *OrigInfo; MOL_COORD *szOrigCoord; NUM_H nNumRemovedProtons; NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered randomly distributed, including removed protons; order: 0=>1H, 1=>D, 2=>T */ int bDeleted; INCHI_MODE bTautFlags; /* t_group_info->bTautFlags */ INCHI_MODE bTautFlagsDone; /* t_group_info->bTautFlagsDone */ INCHI_MODE bNormalizationFlags; /* t_group_info->tni.bNormalizationFlags */ int nCanonFlags; } INChI_Aux; typedef INChI_Aux *PINChI_Aux2[TAUT_NUM]; /********************* array of pointers for sorting components and INChI output *********/ typedef struct tagINChIforSort { INChI *pINChI[TAUT_NUM]; INChI_Aux *pINChI_Aux[TAUT_NUM]; short ord_number; /* for stable sort */ short n1; /* points to the original; used in structure reconstruction only */ short n2; /* points to the original; used in structure reconstruction only */ short n3; /* points to the original; used in structure reconstruction only */ }INCHI_SORT; #endif /* __INCHI_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichi_bns.c000066400000000000000000015022411271037650300235000ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "ichierr.h" #include "incomdef.h" #include "inpdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #define BNS_MARK_ONLY_BLOCKS 1 /* 1 => find only blocks, do not search for ring systems */ #define ALLOW_ONLY_SIMPLE_ALT_PATH 0 /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */ #define CHECK_TG_ALT_PATH 0 /* 1=> when chacking alt path of a tautomeric atom modify t-group, not the atom */ /* 0=> old mode */ #define FIX_CPOINT_BOND_CAP 1 /* 1=> fix bug in case of double bond from neutral cpoint */ #define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */ #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) #define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN) #else #define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN & PBNS->edge_forbidden_mask) #endif typedef enum tagAtTypeTotals { /* counts do not include: charged atom adjacent to another charged atom atom in an unusual valence state or adjacent to an atom in an unusual valence state radicals different from singlet */ /*ATTOT_NUM_Plus. */ /* number of positive charges, +1, is (ATTOT_NUM_CHARGES + ATTOT_TOT_CHARGE)/2 */ /*ATTOT_NUM_Minus.*/ /* number of negative charges, -1, is (ATTOT_NUM_CHARGES - ATTOT_TOT_CHARGE)/2 */ ATTOT_NUM_NP_Plus, /* 0 no H: =N(+)=, #N(+)-, =N(+)<, does not include onium cations >P(+)<, >N(+)< */ ATTOT_NUM_NP_Proton, /* 1 H(+): -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+), N=N,P */ ATTOT_NUM_NP_H, /* 2 H: -NH2, =NH, >NH -NH(-) */ ATTOT_NUM_N_Minus, /* 3 (-): -NH(-), >N(-), =N(-) */ ATTOT_NUM_NP, /* 4 no H: >N- =N-, #N */ ATTOT_NUM_ON, /* 5 -N=O: do not allow -N=O => -NH-OH during H(+) add/removal */ ATTOT_NUM_COH, /* 6 =C-OH, #C-OH; O=O,S,Se,Te */ ATTOT_NUM_CSH, /* 7 -C-SH, -C-SeH -C-TeH */ ATTOT_NUM_ZOH, /* 8 =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge, Z != C */ ATTOT_NUM_OOH, /* 9 -O-OH, O=O,S,Se,Te */ ATTOT_NUM_ZOOH, /* 10 O=Z-OH, O=O,S,Se,Te */ ATTOT_NUM_NOH, /* 11 =N-OH, -N(-)-OH */ ATTOT_NUM_N_OH, /* 12 >N-OH, -NH-OH, >NH(+)-OH, -N(-)-OH */ ATTOT_NUM_CO, /* 13 -C=O, =C=O; O=O,S,Se,Te */ ATTOT_NUM_ZO, /* 14 -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ ATTOT_NUM_NO, /* 15 -N=O, =N(+)=O */ ATTOT_NUM_N_O, /* 16 >N(+)=O, =N(+)=O */ ATTOT_NUM_CO_Minus, /* 17 =C-O(-), #C-O(-); O=O,S,Se,Te */ ATTOT_NUM_CS_Minus, /* 18 -C-S(-); S = S, Se, Te */ ATTOT_NUM_ZO_Minus, /* 19 =Z-O(-), #Z-O(-); O = O, S, Se, Te */ ATTOT_NUM_OO_Minus, /* 20 -O-O(-), O=O,S,Se,Te */ ATTOT_NUM_ZOO_Minus, /* 21 O=Z-O(-), O=O,S,Se,Te */ ATTOT_NUM_NO_Minus, /* 22 >N-O(-), -NH-O(-) */ ATTOT_NUM_N_O_Minus, /* 23 -NH-O(-), >N-O(-); O = O, S, Se, Te */ ATTOT_NUM_O_Minus, /* 24 -Z-O(-); O=O,S,Se,Te */ ATTOT_NUM_OH_Plus, /* 25 any OH(+) */ ATTOT_NUM_O_Plus, /* 26 any O(+) without H */ ATTOT_NUM_Proton, /* 27 proton */ ATTOT_NUM_HalAnion, /* 28 Halogen anion */ ATTOT_NUM_HalAcid, /* 29 Halogen acid */ ATTOT_NUM_Errors, /* 30 for debugging */ ATTOT_TOT_CHARGE, /* 31 total of positive and negative single charges, +1 and -1 */ ATTOT_NUM_CHARGES, /* 32 number of positive and negative single charges, +1 and -1 */ ATTOT_ARRAY_LEN /* 33 array length */ } AT_TYPE_TOTALS; #define ATBIT_NP_Plus (1 << ATTOT_NUM_NP_Plus) #define ATBIT_NP_Proton (1 << ATTOT_NUM_NP_Proton) #define ATBIT_NP_H (1 << ATTOT_NUM_NP_H) #define ATBIT_N_Minus (1 << ATTOT_NUM_N_Minus) #define ATBIT_NP (1 << ATTOT_NUM_NP) #define ATBIT_ON (1 << ATTOT_NUM_ON) #define ATBIT_COH (1 << ATTOT_NUM_COH) #define ATBIT_CSH (1 << ATTOT_NUM_CSH) #define ATBIT_ZOH (1 << ATTOT_NUM_ZOH) #define ATBIT_OOH (1 << ATTOT_NUM_OOH) #define ATBIT_ZOOH (1 << ATTOT_NUM_ZOOH) #define ATBIT_NOH (1 << ATTOT_NUM_NOH) #define ATBIT_N_OH (1 << ATTOT_NUM_N_OH) #define ATBIT_CO (1 << ATTOT_NUM_CO) #define ATBIT_ZO (1 << ATTOT_NUM_ZO) #define ATBIT_NO (1 << ATTOT_NUM_NO) #define ATBIT_N_O (1 << ATTOT_NUM_N_O) #define ATBIT_CO_Minus (1 << ATTOT_NUM_CO_Minus) #define ATBIT_CS_Minus (1 << ATTOT_NUM_CS_Minus) #define ATBIT_ZO_Minus (1 << ATTOT_NUM_ZO_Minus) #define ATBIT_OO_Minus (1 << ATTOT_NUM_OO_Minus) #define ATBIT_ZOO_Minus (1 << ATTOT_NUM_ZOO_Minus) #define ATBIT_NO_Minus (1 << ATTOT_NUM_NO_Minus) #define ATBIT_N_O_Minus (1 << ATTOT_NUM_N_O_Minus) #define ATBIT_O_Minus (1 << ATTOT_NUM_O_Minus) #define ATBIT_OH_Plus (1 << ATTOT_NUM_OH_Plus) #define ATBIT_O_Plus (1 << ATTOT_NUM_O_Plus) #define ATBIT_Proton (1 << ATTOT_NUM_Proton) #define ATBIT_HalAnion (1 << ATTOT_NUM_HalAnion) #define ATBIT_HalAcid (1 << ATTOT_NUM_HalAcid) #define ATBIT_Errors (1 << ATTOT_NUM_Errors) typedef struct tagProtonRemovalMaskAndType { int typePos; /* atoms accessible to positive charges */ int maskPos; int typeNeg; /* atoms accessible to negative charges */ int maskNeg; int typeH; /* atoms accessible to hydrogen atoms */ int maskH; } PRMAT; #define PR_SIMPLE_MSK (ATBIT_NP_Proton | ATBIT_OH_Plus) #define PR_SIMPLE_TYP (ATT_ATOM_N | ATT_ATOM_P | ATT_O_PLUS) #define ATBIT_MSK_NP (ATBIT_NP_Plus | ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_N_Minus | ATBIT_NP) #define KNOWN_ACIDIC_TYPE (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO) #define ATBIT_MSK_OS (ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH |\ ATBIT_CO | ATBIT_ZO | ATBIT_NO | ATBIT_N_O |\ ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_ZO_Minus | ATBIT_OO_Minus |\ ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_N_O_Minus /*| ATBIT_O_Minus*/ ) #define ATBIT_MSK_H (ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH |\ ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH) #define ATTYP_OS (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO /*| ATT_OTHER_NEG_O*/ | ATT_OTHER_ZO) #define ATTYP_NP (ATT_ATOM_N | ATT_ATOM_P) #define ATTYP_N (ATT_ATOM_N) #define ATTYP_P (ATT_ATOM_P) /************* simple proton removal from acids **************************/ #define AR_ANY_OH 0 /* 1 => create unknown to be acidic anions */ #define AR_SIMPLE_STEPS 3 /* acidic groups for proton removal, step 1 */ #define AR_SIMPLE_MSK1 (ATBIT_COH | ATBIT_CSH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_HalAcid) #define AR_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_HalAcid) /* acidic groups for proton removal, step 2 */ #define AR_SIMPLE_MSK2 (AR_ANY_OH? (ATBIT_N_OH) :0) #define AR_SIMPLE_TYP2 (AR_ANY_OH? (ATT_N_O) :0) /* acidic groups for proton removal, step 3 */ #define AR_SIMPLE_MSK3 (AR_ANY_OH? (ATBIT_ZOH) :0) #define AR_SIMPLE_TYP3 (AR_ANY_OH? (ATT_OTHER_ZO):0) /************* simple proton addition to acids **************************/ #define AA_ANY_O_Minus 0 /* 1 => neutralize unknown to be acidic anions */ #define AA_SIMPLE_STEPS 3 /* acidic groups for proton addition, step 1 */ #define AA_SIMPLE_MSK1 (ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_OO_Minus | ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_O_Minus | ATBIT_HalAnion) #define AA_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_OH_MINUS | ATT_HalAnion ) /* acidic groups for proton addition, step 2 */ #define AA_SIMPLE_MSK2 (AA_ANY_O_Minus? (ATBIT_N_O_Minus) :0) #define AA_SIMPLE_TYP2 (AA_ANY_O_Minus? (ATT_N_O) :0) /* acidic groups for proton addition, step 3 */ #define AA_SIMPLE_MSK3 (AA_ANY_O_Minus? (ATBIT_ZO_Minus | ATBIT_O_Minus):0) #define AA_SIMPLE_TYP3 (AA_ANY_O_Minus? (ATT_OTHER_ZO) :0) #if ( FIX_NP_MINUS_BUG == 1 ) /* allow to add H(+) to =N(-) which previously was #N */ #undef AA_SIMPLE_STEPS #define AA_SIMPLE_STEPS 4 #define AA_SIMPLE_MSK4 ATBIT_N_Minus #define AA_SIMPLE_TYP4 ATT_NP_MINUS_V23 #endif /************* hard proton removal from NP **************************/ /* (+) charge group for proton removal: mask & type */ #define PR_HARD_MSK_POS ATBIT_MSK_NP #define PR_HARD_TYP_POS ATTYP_N #define PR_HARD_TYP_POSP ATTYP_P /* (-) charge group for proton removal */ #define PR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) #define PR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) /* H-group for proton removal */ #define PR_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) #define PR_HARD_TYP_H (ATTYP_N | ATTYP_OS) /************* hard proton removal from acids **************************/ /* (+) charge group for proton removal: mask & type */ #define AR_HARD_MSK_POS ATBIT_MSK_NP #define AR_HARD_TYP_POS ATTYP_N /* (-) charge group for proton removal */ #define AR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) #define AR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) /* H-group acid for proton removal */ #define AR_HARD_MSK_HA (ATBIT_CO | ATBIT_NO ) #define AR_HARD_TYP_HA (ATT_ACIDIC_CO | ATT_NO) /* H-group non-acid for proton removal */ #define AR_HARD_MSK_HN ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~AR_HARD_MSK_HA) #define AR_HARD_TYP_HN ((ATTYP_N | ATTYP_OS) /*& ~AR_HARD_TYP_HA*/) /************* hard proton addition to acids **************************/ /* (+) charge group for proton removal: mask & type */ #define AA_HARD_MSK_POS ATBIT_MSK_NP #define AA_HARD_TYP_POS ATTYP_N /* (-) charge group for negative charge removal */ #define AA_HARD_MSK_NEG ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~(ATBIT_CO | ATBIT_NO )) #define AA_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) /* (-) charge group to accept negative charges */ #define AA_HARD_MSK_CO (ATBIT_CO | ATBIT_NO ) #define AA_HARD_TYP_CO (ATT_ACIDIC_CO | ATT_NO) /* H-group non-acid for proton removal */ #define AA_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) #define AA_HARD_TYP_H (ATTYP_N | ATTYP_OS) /*****************************************************************************/ #define BNS_MAX_NUM_FLOW_CHANGES (1+2*MAX_BOND_EDGE_CAP) /* -- opiginal Pascal values -- #define NO_VERTEX 0 #define BLOSSOM_BASE -1 #define FIRST_INDX 1 */ #define TREE_NOT_IN_M 0 /* not in T or T' */ #define TREE_IN_2 1 /* in T' and not s-reachable */ #define TREE_IN_2BLOSS 2 /* in T' and in a blossom, is s-reachable */ #define TREE_IN_1 3 /* in T and is s-reachable */ #define TREE_IS_S_REACHABLE(X) (Tree[X] >= TREE_IN_2BLOSS) #define TREE_IS_ON_SCANQ TREE_IS_S_REACHABLE /* #define TREE_IS_ON_SCANQ(X) (Tree[X] != TREE_NOT_IN_M) */ #define TREE_MARK(X, MARK) do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0) /***************************************************************************** * store changes done to check whether an alternating path exists * (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath) ******************************************************************************/ typedef struct tagAltPathChanges { /* caps changed in up to 2 vertices */ VertexFlow nOldCapsVert[2][MAXVAL+1]; Vertex vOldVert[2]; S_CHAR bSetOldCapsVert[2]; /* number of caps to restore, including st-cap */ /* save ids of the newly created temporary vertices */ Vertex vNewVertex[2]; S_CHAR bSetNew[2]; /* indicators whether to remove vertices */ } ALT_PATH_CHANGES; /*****************************************************************************/ /* Local functions */ int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ); int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ); int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ); int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ); int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ); int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ); int CompTGroupNumber( const void *tg1, const void *tg2 ); int CompCGroupNumber( const void *cg1, const void *cg2 ); /* Rings, Blocks, Non-stereo bonds */ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ); int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); /* Called from BalancedNetworkSearch */ int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ); /* Vertex Get2ndNeighbor1( BN_STRUCT* pBNS, Vertex u, EdgeIndex iedge ); not used */ Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ); Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ); int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ); int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ); int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); Vertex FindBase( Vertex u, Vertex *BasePtr ); int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ); Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, Edge *SwitchEdge, Vertex *BasePtr, Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ); int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ); int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ); /* int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); int SetBondsRestoreBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow ); */ int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ); int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ); int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ); int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ); int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ); /* Bonds testing */ /* int bRestoreFlowToCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); */ int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ); int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ); int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ); int bIsAltBond( int bond_type ); /* Fix bonds */ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); /* Alt path testing */ int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ); int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ); Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ); int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ); int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ); static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms); int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ); /* Protons removal, charge neutralization */ int is_acidic_CO( inp_ATOM *atom, int at_no ); int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ); int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ); int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ); int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ); int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ); int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ); int is_Z_atom( U_CHAR el_number ); int IsZOX( inp_ATOM *atom, int at_x, int ord ); int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ); int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, BN_STRUCT *pBNS, int nType, int nMask, int nCharge ); int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, BN_STRUCT *pBNS, int nType, int nMask ); int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ); int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ); int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ); int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ); int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ); int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ); int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ); int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ); int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ); int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType ); #if ( BNS_RAD_SEARCH == 1 ) int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u); int cmp_rad_endpoints( const void *a1, const void *a2 ); int cmp_endpoints_rad( const void *a1, const void *a2 ); #endif int bHasChargedNeighbor( inp_ATOM *at, int iat ); /*****************************************************************************/ /**** prim(v) is v' *****/ #define prim(v) (Vertex)((v)^1) /*****************************************************************************/ #define SwitchEdge_Vert1(u) SwitchEdge[u][0] #define SwitchEdge_Vert2(u) Get2ndEdgeVertex( pBNS, SwitchEdge[u] ) #define SwitchEdge_IEdge(u) SwitchEdge[u][1] /*****************************************************************************/ /*****************************************************************************/ /* Returns value > 0 if a bond has been changed */ /*****************************************************************************/ int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ) { /*flow1 = edge->flow;*/ /* output from BNS */ switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { case 0: /* the flow has not been permitted to change inside the BNS */ /* nothing to do */ /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ break; case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS; update flow0 */ /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ edge->flow0 = edge->flow; /* SAVE NEW EDGE FLOW AS THE INITIAL FLOW FROM CHEM. BONDS */ break; case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ edge->flow = edge->flow-delta; /* CHANGE EDGE FLOW BACK (RESTORE) */ break; case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS */ /* nothing to do */ /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ break; } return 0; } /*****************************************************************************/ /* Returns value > 0 if a bond has been changed; do not change flow */ /*****************************************************************************/ int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ) { int flow1, flow2, tmp, ret = 0; int bond_mark, bond_type, new_bond_type; if ( !edge->pass || !bond_type21 ) return 0; switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { case 0: /* the flow has not been permitted to change inside the BNS: simulated in case of check one bond */ case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS: obsolete mode, unexpected bChangeFlow */ flow1 = edge->flow0; /* output from BNS, the original (old) flow value */ flow2 = flow1 + delta; /* the flow would be changed to this value by the BNS if permitted */ break; case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS */ case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ flow2 = edge->flow; /* output from BNS, the changed (new) value */ flow1 = edge->flow0; /* the original flow (old) value before the BNS */ break; default: return 0; /* added 2006-03-21 */ } if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && (bChangeFlow & BNS_EF_ALTR_NS) !=BNS_EF_ALTR_NS ) { /* set new bond types according to the new flow values */ new_bond_type = flow2+BOND_SINGLE; if ( *bond_type12 != new_bond_type ) { *bond_type12 = *bond_type21 = new_bond_type; ret ++; } } else if ( bChangeFlow & BNS_EF_ALTR_BONDS ) { if ( flow1 == flow2 ) { goto exit_function; } /* update alternating bond information */ if ( flow1 > flow2 ) { /* make sure flow2 > flow1 */ tmp = flow1; flow1 = flow2; flow2 = tmp; } bond_mark = 0; switch( bond_type = (*bond_type12 & BOND_TYPE_MASK) ) { case BOND_SINGLE: case BOND_DOUBLE: case BOND_TRIPLE: /* assume that the input bond type fits either flow1 or flow2 */ if ( flow1 == 0 && flow2 == 1 ) { if ( bChangeFlow & BNS_EF_SET_NOSTEREO ) { bond_mark = BOND_MARK_ALT12NS; bond_type = BOND_ALT12NS; } else { bond_mark = BOND_MARK_ALT12; bond_type = BOND_ALTERN; } } else if ( flow1 == 0 && flow2 == 2 ) { bond_mark = BOND_MARK_ALT13; bond_type = BOND_ALT_13; } else if ( flow1 == 1 && flow2 == 2 ) { bond_mark = BOND_MARK_ALT23; bond_type = BOND_ALT_23; } else { return BNS_BOND_ERR; /* error */ } break; case BOND_TAUTOM: if ( flow1 == 0 && flow2 == 1 ) { bond_mark = BOND_MARK_ALT12NS; } else { return BNS_BOND_ERR; /* error */ } break; default: new_bond_type = bond_type; bond_mark = (*bond_type12 & BOND_MARK_MASK); switch( bond_mark ) { case BOND_MARK_ALT12: if ( (bChangeFlow & BNS_EF_SET_NOSTEREO) && flow1 == 0 && flow2 == 1 ) { bond_mark = BOND_MARK_ALT12NS; new_bond_type = BOND_ALT12NS; break; } case BOND_MARK_ALT12NS: if ( flow1 == 2 || flow2 == 2 ) { bond_mark = BOND_MARK_ALT123; new_bond_type = BOND_ALT_123; } break; case BOND_MARK_ALT13: if ( flow1 == 1 || flow2 == 1 ) { bond_mark = BOND_MARK_ALT123; new_bond_type = BOND_ALT_123; } break; case BOND_MARK_ALT23: if ( flow1 == 0 || flow2 == 0 ) { bond_mark = BOND_MARK_ALT123; new_bond_type = BOND_ALT_123; } break; case BOND_MARK_ALT123: break; case 0: /* special case: second alt bond testing */ if ( flow1 == 0 && flow2 == 1 ) { bond_mark = BOND_MARK_ALT12; } else if ( flow1 == 0 && flow2 == 2 ) { bond_mark = BOND_MARK_ALT13; } else if ( flow1 == 1 && flow2 == 2 ) { bond_mark = BOND_MARK_ALT23; } else { return BNS_BOND_ERR; /* error */ } break; default: return BNS_BOND_ERR; /* error */ } switch( bond_type ) { case BOND_TAUTOM: break; case BOND_ALTERN: case BOND_ALT12NS: case BOND_ALT_123: case BOND_ALT_13: case BOND_ALT_23: bond_type = new_bond_type; break; default: return BNS_BOND_ERR; /* error */ } } new_bond_type = bond_type | bond_mark; if ( new_bond_type != *bond_type12 ) { *bond_type12 = *bond_type21 = new_bond_type; ret ++; } } exit_function: return ret; } /*****************************************************************************/ int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ) { /* Run BNS until no aug pass is found */ int pass, delta=0, nSumDelta; nSumDelta = 0; for ( pass = 0; pass < pBNS->max_altp; pass ++ ) { pBNS->alt_path = pBNS->altp[pass]; pBNS->bChangeFlow = 0; delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); ReInitBnData( pBD ); if ( 0 < delta ) { pBNS->num_altp ++; nSumDelta += abs( delta ); } else { break; } } if ( IS_BNS_ERROR(delta) ) return delta; return nSumDelta; /* number of eliminated pairs of "dots" */ } /*****************************************************************************/ int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ) { BNS_VERTEX *vert = pBNS->vert + v1; inp_ATOM *at = atom + v1; S_CHAR cValue; int nChanges = 0; /* set only on the 1st pass */ if ( !vert->st_edge.pass ) { return 0; } /* adjust chem_bonds_valence */ cValue = at->chem_bonds_valence - at->valence; if ( cValue >= 0 && cValue != vert->st_edge.flow ) { at->chem_bonds_valence = at->valence + vert->st_edge.flow; nChanges ++; } /* adjast radical */ switch ( vert->st_edge.cap - vert->st_edge.flow ) { case 0: cValue = 0; break; case 1: cValue = RADICAL_DOUBLET; break; case 2: cValue = RADICAL_TRIPLET; break; default: return BNS_BOND_ERR; } if ( cValue != at->radical ) { at->radical = cValue; nChanges ++; } return nChanges; } /*****************************************************************************/ int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ) { int i, mask, num; for ( i = 0, num = 0; i < num_atoms; i ++ ) { if ( mark[i] ) { mark[i] = 0; #if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) /* add ignoring adjacent charges */ at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 ); #else at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); #endif num ++; } } return num; } /************************************************************************************/ /* Eliminate neutral representation ambiguity: replace (+)--N==(-) with (+)==N--(-) */ /* here (+) is positive charge group, (-) is negative charge group, N is N or P */ /* this reduces possibility of ion pair -OH => -O(+) + H(+) creation instead of */ /* removing H(+) from N or P */ /* */ /*==== Call this function after alt path was found and new flows have been set. ====*/ /* */ /************************************************************************************/ int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ) { int pass, i, v0, v1, v2, ineigh1, /*ineigh0,*/ /*ineigh2,*/ vLast, n, delta, ret, err = 0; BNS_EDGE *edge; int nFound, k; for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); v0 = v2 = NO_VERTEX; /* negative number */ for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 /*, ineigh0 = ineigh1*/ ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; /* follow the BN Structure, not the inp_ATOM, to take care of swithching to t-groups, c-groups or other fictitious edges/vertices */ v2 = edge->neighbor12 ^ v1; if ( v1 < num_atoms && ( v0 >= num_atoms && ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) || v2 >= num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) ) ) { int cgPos, cgNeg; int neighPos = -1, neighNeg = -1; BNS_EDGE *edgePos, *edgeNeg; nFound = 0; for ( k = pBNS->vert[v1].num_adj_edges-1; k >= 0 && (neighPos < 0 || neighNeg < 0); k -- ) { BNS_EDGE *next_edge = pBNS->edge + pBNS->vert[v1].iedge[k]; int v = next_edge->neighbor12 ^ v1; if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_GROUP ) { if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_NEGATIVE ) { cgNeg = v; neighNeg = k; nFound ++; } else { cgPos = v; neighPos = k; nFound ++; } } } if ( 2 == nFound && neighPos >= 0 && neighNeg >= 0 ) { /* both c-groups have been found */ edgePos = pBNS->edge + pBNS->vert[v1].iedge[neighPos]; edgeNeg = pBNS->edge + pBNS->vert[v1].iedge[neighNeg]; if ( edgePos->flow < edgeNeg->flow ) { /* ambiguity found; replace (+cg)--N==(-cg) with (+cg)==N--(-cg) */ int dflow = edgeNeg->flow - edgePos->flow; edgePos->flow += dflow; pBNS->vert[cgPos].st_edge.cap += dflow; pBNS->vert[cgPos].st_edge.flow += dflow; edgeNeg->flow -= dflow; pBNS->vert[cgNeg].st_edge.cap -= dflow; pBNS->vert[cgNeg].st_edge.flow -= dflow; ret ++; } } } } if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } } return err? err : ret; } /*********************************************************************************/ int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ) { int i, iso, tot_num_iso_H, num_H, /* number of H before the removal, including explicit H */ nNum2Remove, /*number of H to remove */ nNumRemovedExplicitH, nNumExplicit2Implicit; S_CHAR num_iso_H[NUM_H_ISOTOPES]; inp_ATOM *at_H; if ( !nDelta ) { return 0; } /* add */ if ( nDelta > 0 ) { at[at_no].num_H += nDelta; t_group_info->tni.nNumRemovedProtons --; return nDelta; } /* remove */ nNum2Remove = -nDelta; nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; /* number of explicit H saved separately in at[num_atoms+i], i=0..nNumRemovedExplicitH-1 */ tot_num_iso_H = NUM_ISO_H(at,at_no); num_H = at[at_no].num_H; /* tot_num_iso_H = NUM_ISO_H(at,at_no); num_H = at[at_no].num_H; nNumAtomExplicitH = 0; nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; tot_num_explicit_iso_H = 0; */ at_H = at + num_atoms; memcpy( num_iso_H, at[at_no].num_iso_H, sizeof(num_iso_H)); /* Remove all explicit H, otherwise a false stereo can occur. Example: remove H(+) from the following substructure: H H A / A / >X==N(+) produces false stereogenic bond: >X==N B \ B H To avoid this effect all explicit H atoms must be removed */ nNumExplicit2Implicit = 0; for ( i = 0; i < nNumRemovedExplicitH; ) { if ( at_H[i].neighbor[0] == at_no ) { int m, k, orig_no = at_H[i].orig_at_number; nNumRemovedExplicitH --; nNumExplicit2Implicit ++; if ( nNumRemovedExplicitH > i ) { inp_ATOM at_i = at_H[i]; memmove( at_H+i, at_H+i+1, sizeof(at_H[0])*(nNumRemovedExplicitH-i) ); at_H[nNumRemovedExplicitH] = at_i; /* save removed H (for debugging purposes?) */ } /* adjust 0D parities */ if ( at[at_no].sb_parity[0] ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[at_no].sb_parity[m]; m ++ ) { if ( at[at_no].sn_orig_at_num[m] == orig_no ) { #ifdef _DEBUG if ( at[at_no].sn_ord[m] >= 0 ) { int stop = 1; /* sb maintenance error */ } #endif if ( at[at_no].valence >= MIN_NUM_STEREO_BOND_NEIGH ) { at[at_no].sn_ord[m] = k = (at[at_no].sb_ord[m]==0); at[at_no].sn_orig_at_num[m] = at[(int)at[at_no].neighbor[k]].orig_at_number; if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { at[at_no].sb_parity[m] = 3 - at[at_no].sb_parity[m]; } } else { at[at_no].sn_ord[m] = -99; /* no sb neighbor exists anymore */ at[at_no].sn_orig_at_num[m] = 0; if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; if ( 0 < get_opposite_sb_atom( at, at_no, at[at_no].sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ) ) { at[at_no].sb_parity[m] = at[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = AB_PARITY_UNDF; } #ifdef _DEBUG else { int stop = 1; /* sb maintenance error */ } #endif } } } } } /* do not increment i here: we have shifted next at_H[] element to the ith position and decremented nNumRemovedExplicitH */ } else { i ++; } } for ( iso = -1; iso < NUM_H_ISOTOPES && 0 < nNum2Remove; iso ++ ) { /* each pass removes up to one H */ if ( iso < 0 ) { /* try to remove non-isotopic */ while ( tot_num_iso_H < num_H && 0 < nNum2Remove ) { /* non-isotopic H exists */ num_H --; t_group_info->tni.nNumRemovedProtons ++; nNum2Remove --; } } else { /* remove isotopic */ while ( num_iso_H[iso] && num_H && 0 < nNum2Remove ) { /* isotopic H exists */ num_H --; num_iso_H[iso] --; t_group_info->tni.nNumRemovedProtonsIsotopic[iso] ++; t_group_info->tni.nNumRemovedProtons ++; nNum2Remove --; } } } #if ( bRELEASE_VERSION != 1 ) if ( nNum2Remove ) { int stop = 1; /* Program error */ } #endif if ( nDelta + nNum2Remove < 0 ) { at[at_no].num_H = num_H; memcpy( at[at_no].num_iso_H, num_iso_H, sizeof(at[0].num_iso_H)); t_group_info->tni.nNumRemovedExplicitH = nNumRemovedExplicitH; } return nDelta + nNum2Remove; } /*********************************************************************************/ int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ) { int pass, i, v0, v1, v2, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, err = 0; BNS_EDGE *edge; int nDeltaH, nDeltaCharge; int mask, type; for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); v0 = v2 = NO_VERTEX; for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; /* follow the BN Structure, not the inp_ATOM, to take care of swithching to t-groups, c-groups or other fictitious edges/vertices */ v2 = edge->neighbor12 ^ v1; if ( v1 < num_atoms && (v0 >= num_atoms || v2 >= num_atoms) ) { nDeltaH = nDeltaCharge = 0; if ( v0 >= num_atoms ) { /* delta(v0-v1) = -delta(v1-v2) along the alternating path */ if ( pBNS->vert[v0].type & BNS_VERT_TYPE_TGROUP ) { nDeltaH -= delta; } else if ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) { nDeltaCharge += delta; } } if ( v2 >= num_atoms ) { if ( pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP ) { nDeltaH += delta; } else if ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) { nDeltaCharge -= delta; } } if ( nDeltaH || nDeltaCharge ) { if ( bSubtract ) { if ( !mark[v1] ) { /* first time the atom has been encountered: subtract */ #if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 2 ); #else type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 1 ); #endif ret ++; /* number of changed atoms */ mark[v1] ++; } } else { /* Change */ at[v1].charge += nDeltaCharge; if ( nDeltaH ) { AddOrRemoveExplOrImplH( nDeltaH, at, num_atoms, (AT_NUMB)v1, t_group_info ); } ret ++; /* number of changed atoms */ } } } } if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } } return err? err : ret; } /*********************************************************************************/ int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) { int pass, i, v0, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, ret_val, err = 0; BNS_EDGE *edge; int bMovingRad = 0, bChangeFlowAdd; int bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); /* bCheckMovingRad = (bChangeFlow & BNS_EF_ALTR_NS) == BNS_EF_ALTR_NS && pBNS->tot_st_cap > pBNS->tot_st_flow; */ for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); if ( (bChangeFlow0 & BNS_EF_SET_NOSTEREO) && (pBNS->vert[v1].st_edge.cap0 > pBNS->vert[v1].st_edge.flow0 || pBNS->vert[vLast].st_edge.cap0 > pBNS->vert[vLast].st_edge.flow0 ) ) { bMovingRad ++; bChangeFlowAdd = BNS_EF_SET_NOSTEREO; ret |= 2; } else { bChangeFlowAdd = 0; } /* start vertex */ if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { /* restore s-v1 edge flow to the BNS this pass input value */ ; /*pBNS->vert[v1].st_edge.flow -= delta;*/ } else if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { if ( v1 < num_atoms ) { /* will produce wrong result if called for v1 next time? */ ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v1 ); if ( ret_val < 0 ) { err = BNS_PROGRAM_ERR; } else { ret |= (ret_val > 0); } } /*pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;*/ } pBNS->vert[v1].st_edge.pass = 0; v0 = v2 = NO_VERTEX; for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; /* follow the BN Structure, not the inp_ATOM, to take care of swithching to t-groups, c-groups or other fictitious edges/vertices */ v2 = edge->neighbor12 ^ v1; /* change at->chem_bonds_valence 2004-03-08 */ if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && v1 < num_atoms ) { if ( v0 >= num_atoms && v2 < num_atoms ) { at[v1].chem_bonds_valence += delta; /* change in v1-v2 bond order */ } else if ( v0 < num_atoms && v2 >= num_atoms && v0 != NO_VERTEX ) { at[v1].chem_bonds_valence -= delta; /* change in v0-v1 bond order */ } } if ( !edge->pass ) continue; if ( v1 < num_atoms && ineigh1 < at[v1].valence && v2 < num_atoms && ineigh2 < at[v2].valence ) { if ( (bChangeFlow0 & BNS_EF_ALTR_NS )==BNS_EF_ALTR_NS && (bChangeFlow0 & BNS_EF_SAVE_ALL)==BNS_EF_SAVE_ALL ) { /* 2004-07-02 special mode: save new ring bonds and mark as non-stereo non-ring bonds */ if ( at[v1].nRingSystem != at[v2].nRingSystem ) { /* non-ring bond (bridge) */ bChangeFlowAdd = BNS_EF_ALTR_NS; } else { /* ring bond */ bChangeFlowAdd = 0; } } /* change bonds on the first pass only: in this case all flow correspond to the BNS output */ ret_val = SetAtomBondType( edge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], delta, bChangeFlow | bChangeFlowAdd ); if ( ret_val < 0 ) { err = BNS_PROGRAM_ERR; } else { ret |= (ret_val > 0); } } edge->pass = 0; } if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } else if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { /* restore v2-t edge flow to the BNS this pass input value */ /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ ; /*pBNS->vert[v2].st_edge.flow += delta; */ } else if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { if ( v2 < num_atoms ) { ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v2 ); if ( ret_val < 0 ) { err = BNS_PROGRAM_ERR; } else { ret |= (ret_val > 0); } } /*pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;*/ } pBNS->vert[v2].st_edge.pass = 0; } return err? err : ret; } /*********************************************************************************/ int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ) { int pass, i, j, v1, v2, ineigh1, ineigh2, vLast, vFirst, n, delta, err = 0; BNS_EDGE *edge; S_CHAR cDelta[MAX_ALT_AATG_ARRAY_LEN]; AT_NUMB nVertex[MAX_ALT_AATG_ARRAY_LEN]; int nLenDelta = 0, last_i, nNumFound; for ( pass = pBNS->num_altp-1; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; vFirst = v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); v2 = NO_VERTEX; pAATG->nNumFound = 0; /* initialize */ if ( nEnd1 != vFirst && nEnd1 != vLast ) { nEnd1 = -1; /* really not the end */ } if ( nEnd2 != vFirst && nEnd2 != vLast ) { nEnd2 = -1; /* really not the end */ } for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; /* follow the BN Structure, not the inp_ATOM, to take care of swithching to t-groups, c-groups or other fictitious edges/vertices */ v2 = edge->neighbor12 ^ v1; /* if ( v1 < num_atoms && v2 < num_atoms ) { continue; } */ /* BNS increased edge flow by delta */ if ( v1 >= num_atoms && ((pBNS->vert[v1].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v1].type & BNS_VERT_TYPE_TEMP)) && 0 <= v2 && v2 < num_atoms && (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM ) ) { /* if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; pAATG->nNumFound ++; } */ /* BNS increased bond order in v1(t-group)-v2(atom) by delta: added delta attachments */ if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { cDelta[nLenDelta] = delta; nVertex[nLenDelta] = v2; nLenDelta ++; } } else if ( v2 >= num_atoms && ((pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v2].type & BNS_VERT_TYPE_TEMP)) && 0 <= v1 && v1 < num_atoms && (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM ) ) { /* if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; pAATG->nNumFound ++; } */ /* BNS increased bond order in v1(atom)-v2(t-group) by delta: added delta attachments */ if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { cDelta[nLenDelta] = delta; nVertex[nLenDelta] = v1; nLenDelta ++; } } else /* special case when the testing 'dot' was placed on an atom (should be nEnd1 only) */ if ( 0 <= v1 && v1 == nEnd1 || v1 == nEnd2 && 0 <= v2 && v2 < num_atoms ) { if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { cDelta[nLenDelta] = -delta; nVertex[nLenDelta] = v1; nLenDelta ++; } } else if ( 0 <= v2 && v2 == nEnd1 || v2 == nEnd2 && 0 <= v1 && v1 < num_atoms ) { if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { cDelta[nLenDelta] = -delta; nVertex[nLenDelta] = v2; nLenDelta ++; } } } if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } else { last_i = -1; nNumFound = 0; /* first run */ for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ /* describe ordinary aug. paths of moving a single attachment */ /* we are looking for aug. paths describing movement of 2 or more */ if ( cDelta[j] > 0 && cDelta[i] > 0 || cDelta[j] < 0 && cDelta[i] < 0 ) { if ( j == last_i ) { /* three attachments moved */ return 0; } v1 = nVertex[j]; if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { nNumFound ++; } v2 = nVertex[i]; if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { nNumFound ++; } last_i = i; } } if ( !nNumFound ) { return 0; } if ( nNumFound > 4 ) { return 0; } if ( nNumFound < 4 ) { return 0; } /* second run */ for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ /* describe ordinary aug. paths of moving a single attachment */ /* we are looking for aug. paths describing movement of 2 or more */ if ( cDelta[j] > 0 && cDelta[i] > 0 || cDelta[j] < 0 && cDelta[i] < 0 ) { v1 = nVertex[i-1]; if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; pAATG->nNumFound ++; } v2 = nVertex[i]; if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; pAATG->nNumFound ++; } } } } } return err? err : pAATG->nNumFound; } /*********************************************************************************/ int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ) { int pass, i, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, err = 0; BNS_EDGE *edge; for ( pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); v2 = NO_VERTEX; /* starting vertex */ if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { pBNS->vert[v1].st_edge.flow -= delta; /* restore s-v1 edge flow to the BNS input value */ } else if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow; } /* augmenting path edges */ for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; v2 = edge->neighbor12 ^ v1; RestoreEdgeFlow( edge, delta, bChangeFlow ); edge->pass = 0; } /* ending vertex */ if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } else if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { /* restore v2-t edge flow to the original value */ /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ pBNS->vert[v2].st_edge.flow += delta; } else if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow; } } return err? err : ret; } /***************************************************************************************/ int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ) { int nBondType = ( BOND_TYPE_MASK & bond_type ); int nBondAttrib = ( BOND_MARK_MASK & bond_type ); if ( bTestForNonStereoBond ) { if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { switch( nTestFlow ) { case 0: /* single: can be 1 (single)? */ if ( nBondAttrib == BOND_MARK_ALT12NS|| nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT13 ) { return 0; /* yes, already checked */ } break; case 1: /* double: can be 2 (double)? */ if ( nBondAttrib == BOND_MARK_ALT12NS|| nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT23 ) { return 0; /* yes, already checked */ } break; case 2: /* triple: can be 3 (triple)? */ if ( nBondAttrib == BOND_MARK_ALT13 || nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT23 ) { return 0; /* yes, already checked */ } break; } } } else { if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { switch( nTestFlow ) { case 0: /* single: can be 1 (single)? */ if ( nBondAttrib == BOND_MARK_ALT12 || nBondAttrib == BOND_MARK_ALT12NS|| nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT13 ) { return 0; } break; case 1: /* double: can be 2 (double)? */ if ( nBondAttrib == BOND_MARK_ALT12 || nBondAttrib == BOND_MARK_ALT12NS|| nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT23 ) { return 0; /* yes */ } break; case 2: /* triple: can be 3 (triple)? */ if ( nBondAttrib == BOND_MARK_ALT13 || nBondAttrib == BOND_MARK_ALT123 || nBondAttrib == BOND_MARK_ALT23 ) { return 0; } break; } } } return 1; } /***********************************************************************************/ int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ) { int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0; for ( j = 0; j < at->valence; j ++ ) { bond_type = at->bond_type[j] & BOND_TYPE_MASK; switch( bond_type ) { case 0: /* for structure from InChI reconstruction */ case BOND_SINGLE: case BOND_DOUBLE: case BOND_TRIPLE: nBondsValence += bond_type; break; case BOND_ALTERN: nAltBonds ++; break; default: nNumWrong ++; } } switch ( nAltBonds ) { case 0: break; case 1: nBondsValence += 1; /* 1 or greater than 3 is wrong */ nNumWrong ++; break; default: nBondsValence += nAltBonds+1; break; } if ( nNumAltBonds ) *nNumAltBonds = nAltBonds; if ( nNumWrongBonds ) *nNumWrongBonds = nNumWrong; return nBondsValence; } /******************************************************************************/ /* if radical or has aromatic bonds then augment to the lowest "multiplicity" */ /******************************************************************************/ int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ) { int bError=0, nOrigDelta=0, ret, num_removed; #if( CHECK_AROMBOND2ALT == 1 ) int *pcValMinusBondsVal = NULL; int i, nValMinusBondsVal, nAltBonds, bIgnore, valen, is_rad, excess; /* find valence excess (it may only be due to aromatic bonds) */ for ( i = 0; i < num_atoms; i ++ ) { valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; bIgnore += (nAltBonds > 3); if ( !bIgnore && nValMinusBondsVal > 0 ) { if ( !pcValMinusBondsVal && !(pcValMinusBondsVal = (int *)inchi_calloc(num_atoms, sizeof(pcValMinusBondsVal[0])))) { bError = BNS_OUT_OF_RAM; goto exit_function; } /* mark atoms that have extra unsatisfied valence due to aromatic bonds */ is_rad = (at[i].radical == RADICAL_DOUBLET); excess = nValMinusBondsVal+ is_rad; pcValMinusBondsVal[i] = excess; } } #endif /* CHECK_AROMBOND2ALT */ /* match bonds to valences */ do { num_removed = 0; ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else { nOrigDelta += ret; num_removed = pBNS->num_altp; /* number of augmenting paths */ if ( ret > 0 ) { /* save new bonds in at[] and flows in pBNS and at[] */ ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ if ( IS_BNS_ERROR( ret ) ) { bError = ret; } ret = RestoreBnStructFlow( pBNS, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ if ( IS_BNS_ERROR( ret ) ) { bError = ret; } } ReInitBnStructAltPaths( pBNS ); } } while ( num_removed && num_removed == pBNS->max_altp && !bError ); #if( CHECK_AROMBOND2ALT == 1 ) /* check whether aromatic bonds have been replaces with alternating bonds */ if ( !bError && pcValMinusBondsVal ) { for ( i = 0; i < num_atoms; i ++ ) { if ( !pcValMinusBondsVal[i] ) continue; valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; is_rad = (at[i].radical == RADICAL_DOUBLET); excess = nValMinusBondsVal + is_rad; if ( bIgnore || ( pcValMinusBondsVal[i] - excess ) != 1 ) { /* radical excess has not been reduced */ bError = BNS_ALTBOND_ERR; break; } } } exit_function: if ( pcValMinusBondsVal ) inchi_free( pcValMinusBondsVal ); #endif /* CHECK_AROMBOND2ALT */ return bError? bError : nOrigDelta; } /***************************************************************************************/ int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ) { int ret, iat, ineigh, neigh; int nMinFlow, nMaxFlow, nTestFlow, nCurFlow; int iedge, bSuccess, bError, nDots, nChanges, bTestForNonStereoBond; /* Normalize bonds and find tautomeric groups */ bError = 0; nChanges = 0; bTestForNonStereoBond = pBNS->tot_st_cap > pBNS->tot_st_flow; for ( iat = 0; iat < num_atoms && !bError; iat ++ ) { for ( ineigh = 0; ineigh < at[iat].valence && !bError; ineigh ++ ) { neigh = at[iat].neighbor[ineigh]; if ( neigh < iat ) continue; /* we have already tested the bond */ iedge = pBNS->vert[iat].iedge[ineigh]; if ( IS_FORBIDDEN(pBNS->edge[iedge].forbidden, pBNS) ) continue; if ( nBondTypeToTest && (at[iat].bond_type[ineigh] & BOND_TYPE_MASK) != nBondTypeToTest ) continue; nMinFlow = nMinFlow2Check( pBNS, iedge ); nMaxFlow = nMaxFlow2Check( pBNS, iedge ); nCurFlow = nCurFlow2Check( pBNS, iedge ); if ( nMinFlow == nMaxFlow ) { if ( nMaxFlow && bTestForNonStereoBond ) { nTestFlow = nMaxFlow - (int)(pBNS->tot_st_cap - pBNS->tot_st_flow); /* temporary use of nTestFlow */ nMinFlow = inchi_max( 0, nTestFlow ); } else { continue; } } for ( nTestFlow = nMinFlow; nTestFlow <= nMaxFlow && !bError; nTestFlow ++ ) { if ( nTestFlow == nCurFlow ) continue; if ( !bNeedToTestTheFlow( at[iat].bond_type[ineigh], nTestFlow, bTestForNonStereoBond ) ) continue; bSuccess = 0; nDots = bSetFlowToCheckOneBond( pBNS, iedge, nTestFlow, fcd ); if ( IS_BNS_ERROR(nDots) ) { if ( nDots == BNS_CANT_SET_BOND ) { ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); if ( !IS_BNS_ERROR( ret ) ) { continue; } } bError = nDots; } else if ( nDots > 0 ) { ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else if ( ret > 0 ) { if ( 2*ret == nDots ) { ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else { nChanges += (ret & 1); ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else if ( ret >= 0 ) { nChanges += (ret & 1); bSuccess = 1; } else { bError = ret; } } } /* typically 2*ret < nDots; 2*ret > nDots should not happen. Check later */ ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } } /* --- reinitialize to repeat the calculations --- */ ReInitBnStructAltPaths( pBNS ); } else if ( nDots == 0 ) { ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else { nChanges += (ret & 1); } } ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } } } } return bError? bError : nChanges; } /************************************************************************/ static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms) { int i, j, val; for ( i = 0; i < num_atoms; i++ ) { for ( val = at[i].valence, j = 0; j < val; j ++ ) { at[i].bond_type[j] &= BOND_TYPE_MASK; } } } /***************************************************************************************/ int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) { static U_CHAR el_number_O; static U_CHAR el_number_C; static U_CHAR el_number_N; int i, j, neigh, num_found; BNS_IEDGE iedge; /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ S_CHAR edge_forbidden_mask = forbidden_mask; pBNS->edge_forbidden_mask |= forbidden_mask; if ( !el_number_C ) { el_number_O = (U_CHAR)get_periodic_table_number( "O" ); el_number_C = (U_CHAR)get_periodic_table_number( "C" ); el_number_N = (U_CHAR)get_periodic_table_number( "N" ); } num_found = 0; for ( i = 0; i < num_atoms; i ++ ) { /* acetyl */ if ( at[i].el_number == el_number_C && 3 == at[i].valence && 4 == at[i].chem_bonds_valence ) { int num_O = 0; int bond_to_O_val = 0; int forbidden_bond_pos = -1; int forbidden_bond_val = -1; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( at[neigh].el_number == el_number_O && at[neigh].valence == 1 ) { num_O ++; bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); } else { forbidden_bond_pos = j; forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); } } if ( 2 == num_O && 3 == bond_to_O_val && 1 == forbidden_bond_val ) { iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_found ++; } } else /* nitro */ if ( at[i].el_number == el_number_N && 3 == at[i].valence && (4 == at[i].chem_bonds_valence || 5 == at[i].chem_bonds_valence) ) { int num_O = 0; int bond_to_O_val = 0; int forbidden_bond_pos = -1; int forbidden_bond_val = -1; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( at[neigh].el_number == el_number_O && at[neigh].valence == 1 ) { num_O ++; bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); } else { forbidden_bond_pos = j; forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); } } if ( 2 == num_O && (3 == bond_to_O_val || 4 == bond_to_O_val) && 1 == forbidden_bond_val ) { iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_found ++; } } } #if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 ) num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask ); #endif #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms ); #endif return num_found; } #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) /************************************************************************/ int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) { static U_CHAR el_number_N; int i, j, neigh, num_found; BNS_IEDGE iedge; S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; if ( !el_number_N ) { el_number_N = (U_CHAR)get_periodic_table_number( "N" ); } for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { /* -NH-NH- or -NH-NH3 */ if ( at[i].el_number == el_number_N && at[i].valence < 3 && at[i].num_H && 3 == at[i].chem_bonds_valence + at[i].num_H && at[i].chem_bonds_valence == at[i].valence && !at[i].charge && !at[i].radical ) { for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( neigh < i && at[neigh].el_number == el_number_N && at[neigh].valence < 3 && at[neigh].num_H && 3 == at[neigh].chem_bonds_valence + at[neigh].num_H && at[neigh].chem_bonds_valence == at[neigh].valence && !at[neigh].charge && !at[neigh].radical) { iedge = pBNS->vert[i].iedge[j]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_found ++; } } } } return num_found; } /************************************************************************/ int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) { static U_CHAR el_number_N; int i, j, neigh, num_found; BNS_IEDGE iedge; S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; if ( !el_number_N ) { el_number_N = (U_CHAR)get_periodic_table_number( "N" ); } for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { /* -NH-NH- or -NH-NH3 */ if ( at[i].el_number == el_number_N && at[i].valence < 3 ) { for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( neigh < i && at[neigh].el_number == el_number_N && at[neigh].valence < 3 ) { if ( BOND_TYPE_SINGLE != (at[i].bond_type[j] & BOND_TYPE_MASK) ) { iedge = pBNS->vert[i].iedge[j]; if ( pBNS->edge[iedge].forbidden & edge_forbidden_mask ) { pBNS->edge[iedge].forbidden &= ~edge_forbidden_mask; num_found ++; } } } } } } return num_found; } #endif /******************************************************/ /* fixes bonds set by remove_ion_pairs() in strutil.c */ /******************************************************/ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) { int num_changes = 0; /* 0 1 2 3 4 5 6 7 8 9 8 9 */ #if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ #else static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ #endif static char en[12]; /* same number: 8 elements */ static int ne=0; /* will be 8 and 10 */ #define ELEM_N_FST 0 #define ELEM_N_LEN 4 #define ELEM_O_FST 4 #define ELEM_O_LEN 4 #define ELEM_S_FST (ELEM_O_FST+1) #define ELEM_S_LEN (ELEM_O_LEN-1) #define ELEM_C_FST 8 #define ELEM_C_LEN 2 #define MAX_NEIGH 6 int i, k, n1, n2, n3, n4, i1, i2, i3, i4, bond_type; inp_ATOM *a; char elname[ATOM_EL_LEN]; int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N; BNS_IEDGE iedge; /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ S_CHAR edge_forbidden_mask = forbidden_mask; pBNS->edge_forbidden_mask |= edge_forbidden_mask; if ( !ne ) { /* one time initialization */ const char *b, *e; int len; for ( b = el; e = strchr( b, ';'); b = e+1 ) { len = e-b; memcpy( elname, b, len ); elname[len] = '\0'; en[ne++] = get_periodic_table_number( elname ); } en[ne] = '\0'; en[ne+1] = '\0'; } for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { if ( !a->charge && !a->radical && 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && 0 == num_of_H( at, i ) && 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { /* found N(V), no H */ if ( 2 == nNoMetalNumBonds(at, i) ) { /* #N= */ /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */ #if ( FIX_N_V_METAL_BONDS_GPF == 1 ) if ( 0 > (i1 = nNoMetalNeighIndex( at, i )) || 0 > (i2 = nNoMetalOtherNeighIndex( at, i, n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ) ) { /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */ continue; /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */ } #else i1 = nNoMetalNeighIndex( at, i ); n1 = a->neighbor[i1]; /* non-metal neighbor #1 */ i2 = nNoMetalOtherNeighIndex( at, i, n1 ); #endif n2 = a->neighbor[i2]; /* non-metal neighbor #2 */ /* forbid all edges to non-metals */ iedge = pBNS->vert[i].iedge[i1]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ iedge = pBNS->vert[i].iedge[i2]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ num_changes ++; /* added 11-15-2005 */ /* i n3 */ /* forbid single bond edge beyond the neighboring =N- as in #N=N- */ if ( (at[i].bond_type[i1] & BOND_TYPE_MASK) == BOND_TYPE_DOUBLE ) { i3 = i1; n3 = n1; } else { i3 = i2; n3 = n2; } if ( 0 == NUMH(at, n3) && 2 == nNoMetalNumBonds( at, n3 ) && 3 == nNoMetalBondsValence( at, n3 ) && NULL != memchr( en+ELEM_N_FST, at[n3].el_number, ELEM_N_LEN) && 0 <= (k = nNoMetalOtherNeighIndex( at, n3, i )) ) { /* found =N- ; forbid the edge*/ iedge = pBNS->vert[n3].iedge[k]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } else if ( 3 == nNoMetalNumBonds(at, i) && /* | */ /* found =N= */ /* locate all non-metal neighbors */ 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = a->neighbor[j[0]] )) && 0 <= (j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = a->neighbor[j[1]] )) ) { /* count specific neighbors: N(V)=N, N(V)-N N(V)=O, and N(V)-N */ /* if there is a single neighbor connected by a double bond, namely N(V)=N and/or N(V)=O, then fix the bond(s). If N(V)=O was fixed then do not fix another bond */ m[2] = a->neighbor[j[2]]; num_O = num_N = 0; for ( k= 0; k < 3; k ++ ) { n1 = m[k]; i1 = j[k]; if ( NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { k_N = k; num_N ++; } else if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && 1 == nNoMetalNumBonds( at, n1 ) ) { k_O = k; num_O ++; } } num_other = 0; if ( 1 == num_O && 0 == at[n1=m[k_O]].charge && 0 == at[n1].radical && BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_O]] & BOND_TYPE_MASK) ) { /* fix bond to neighbor =O */ iedge = pBNS->vert[i].iedge[i1]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; num_other ++; /* indicator: double to a terminal O has been fixed */ } if ( !num_other && num_O <= 1 && 1 == num_N && 0 == at[n1=m[k_N]].charge && 0 == at[n1].radical && BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_N]] & BOND_TYPE_MASK ) ) { /* fix bond to neighbor =N */ iedge = pBNS->vert[i].iedge[i1]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } else if ( 4 == nNoMetalNumBonds(at, i) ) { /* | */ /* found -N=N- */ /* | */ /* locate non-metal neighbor connected by a double bond; * if it is =N- then fix the double bond and the single bond beyond the neighbor */ num_N = 0; num_other = 0; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { if ( BOND_TYPE_DOUBLE == (at[i].bond_type[i1] & BOND_TYPE_MASK) && !is_el_a_metal(at[n1=(int)at[i].neighbor[i1]].el_number) && NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { num_N ++; n2 = n1; i2 = i1; } } if ( 1 == num_N && 0 == NUMH(at, n2) && 2 == nNoMetalNumBonds( at, n2 ) && 3 == nNoMetalBondsValence(at, n2) && 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) && BOND_TYPE_SINGLE == (at[n2].bond_type[i3] & BOND_TYPE_MASK) ) { /* fix the single bond beyond the N(V) neighbor N(V)=N- */ iedge = pBNS->vert[n2].iedge[i3]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; /* fix the double bond */ iedge = pBNS->vert[i].iedge[i2]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } } else if ( !a->charge && !a->radical && 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && 0 == num_of_H( at, i ) && 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && 3 == nNoMetalNumBonds( at, i ) ) { /* found S(IV), no H, one double bond, total 3 bonds */ /* OH / in O=S (X != O) fix single bond O-X (type 1) \ X X / in Z=S (X, Y != OH) fix double bond Z=S (type 2) \ Y */ num_N = 0; /* number of non-metal neighbors connected by a double bond */ num_OH = 0; /* number of neighbors OH connected by a single bond */ num_OM = 0; /* number of charged neighbors O connected by a single bond */ num_O = 0; /* number of neighbors =O connected by a double bond */ num_other = 0; for ( i1 = 0; i1 < a->valence; i1 ++ ) { n1=(int)a->neighbor[i1]; if ( is_el_a_metal(at[n1].el_number) ) { continue; } bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); if ( BOND_TYPE_DOUBLE == bond_type ) { num_N ++; n2 = n1; i2 = i1; if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { num_O ++; } } else if ( BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ) && 1 == nNoMetalBondsValence(at, n1 ) && NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { if ( 0 == at[n1].charge ) { num_OH ++; n3 = n1; i3 = i1; } else { num_OM ++; } } else { num_other ++; n4 = n1; i4 = i1; } } if ( 1 == num_N && 1 == num_O && 1 == num_OH + num_OM ) { if ( 1 == num_other ) { /* type 1: fix the single bond S-X */ iedge = pBNS->vert[i].iedge[i4]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } else if ( 1 == num_N && !num_OH && !num_OM ) { int bFound = 0; /* flag */ int bDoNotFixAnyBond = 0; /* flag */ /* avoid case N=S-NH or N=S-N(-); N = N, P, As, Sb */ if ( NULL != memchr( en+ELEM_N_FST, at[n2].el_number, ELEM_N_LEN) ) { U_CHAR el_number = at[n2].el_number; for ( i1 = 0; i1 < a->valence; i1 ++ ) { n1=(int)a->neighbor[i1]; bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); if ( BOND_TYPE_SINGLE == bond_type && (NUMH(at, n1) || -1 == at[n1].charge) && el_number == at[n1].el_number ) { i3 = i1; n3 = n1; bFound ++; } } } /* exception: check if Z==X and they belong to the same ring system */ for ( i1 = 0; i1 < a->valence; i1 ++ ) { if ( i1 != i2 ) { n1=(int)a->neighbor[i1]; if ( at[n2].el_number == at[n1].el_number && at[n2].nRingSystem == at[n1].nRingSystem ) { bDoNotFixAnyBond ++; } } } if ( bDoNotFixAnyBond ) { ; /* do nothing */ } else if ( bFound ) { if ( 1 == bFound && 0 <= ( i4 = nNoMetalOtherNeighIndex2( at, i, n2, n3) ) ) { /* fix bond i4 */ iedge = pBNS->vert[i].iedge[i4]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } else { /* fix the double bond >S=X */ iedge = pBNS->vert[i].iedge[i2]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; /* -- test later -- if ( 2 == nNoMetalNumBonds( at, n2 ) && 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) ) { iedge = pBNS->vert[n2].iedge[i3]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } -------------------*/ } } } else if ( !a->charge && !a->radical && 4 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && 0 == num_of_H( at, i ) && 4 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && 4 == nNoMetalNumBonds( at, i ) ) { /* found S(VI), no H, two double bonds or one triple bond */ /* O || in O=S--Y- (X, Y -- non-terminal) fix single bonds S-X, S-Y (type 1) \ X-- O || in O=S--O(-) (X -- non-terminal) fix single bond S-X (type 2) \ X-- O || in O=S--OH (X -- non-terminal) fix single bond S-X (type 3) \ X-- */ int iN[4]; /* indexes of non-terminal neighbors connected by a single bond */ num_N = 0; /* number of non-metal neighbors connected by a double bond */ num_OH = 0; /* number of neighbors OH connected by a single bond */ num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ num_O = 0; /* number of neighbors =O connected by a double bond */ num_X = 0; /* number of terminal atom X != O connected by a single bond */ num_other = 0; for ( i1 = 0; i1 < a->valence; i1 ++ ) { n1=(int)a->neighbor[i1]; if ( is_el_a_metal(at[n1].el_number) ) { continue; } bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); if ( BOND_TYPE_DOUBLE == bond_type ) { num_N ++; if ( (0 == at[n1].charge #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) || 1 == at[n1].charge && 2 == at[n1].valence #endif ) && 0 == at[n1].radical && 0 == num_of_H( at, n1 ) && NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && 1 == nNoMetalNumBonds( at, n1 ) ) { num_O ++; } } else if ( BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ) && NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && 1 >= num_of_H( at, n1 ) && 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { num_OH ++; /* -OH or -O(-) */ } else if ( BOND_TYPE_SINGLE == bond_type && 1 < nNoMetalNumBonds( at, n1 ) ) { iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ } else if ( BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ) ) { num_X ++; /* other terminal neighbor connected by a single bond */ } else { num_other ++; } } if ( num_N == num_O && 2 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { for ( i2 = 0; i2 < num_OM; i2 ++ ) { i1 = iN[i2]; /* fix bond i1 */ iedge = pBNS->vert[i].iedge[i1]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } } else if ( !a->charge && !a->radical && 6 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && 0 == num_of_H( at, i ) && 6 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && 5 == nNoMetalNumBonds( at, i ) ) { /* found S(VIII), no H, three double bonds or two triple bond */ /* O || in O=S--Y-- (X, Y -- non-terminal) fix single bond S-X, S-Y (type 4) //\ O X-- note: this structure is a mistakenly drawn structure O O || || O=S--O--Y-- or O=S--Y-- \ \ X-- O--X-- */ int iN[5]; /* indexes of non-terminal neighbors connected by a single bond */ num_N = 0; /* number of non-metal neighbors connected by a double bond */ num_OH = 0; /* number of neighbors OH connected by a single bond */ num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ num_O = 0; /* number of neighbors =O connected by a double bond */ num_X = 0; /* number of terminal atom X != O connected by a single bond */ num_other = 0; for ( i1 = 0; i1 < a->valence; i1 ++ ) { n1=(int)a->neighbor[i1]; if ( is_el_a_metal(at[n1].el_number) ) { continue; } bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); if ( BOND_TYPE_DOUBLE == bond_type ) { num_N ++; if ( (0 == at[n1].charge #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) || 1 == at[n1].charge && 2 == at[n1].valence #endif ) && 0 == at[n1].radical && 0 == num_of_H( at, n1 ) && NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && 1 == nNoMetalNumBonds( at, n1 ) ) { num_O ++; } } else if ( BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ) && NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && 1 >= num_of_H( at, n1 ) && 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { num_OH ++; /* -OH or -O(-) */ } else if ( BOND_TYPE_SINGLE == bond_type && 1 < nNoMetalNumBonds( at, n1 ) ) { iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ } else if ( BOND_TYPE_SINGLE == bond_type && 1 == nNoMetalNumBonds( at, n1 ) ) { num_X ++; /* other terminal neighbor connected by a single bond */ } else { num_other ++; } } if ( num_N == num_O && 3 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { for ( i2 = 0; i2 < num_OM; i2 ++ ) { i1 = iN[i2]; /* fix bond i1 */ iedge = pBNS->vert[i].iedge[i1]; pBNS->edge[iedge].forbidden |= edge_forbidden_mask; num_changes ++; } } } } return num_changes; } #define ALL_NONMETAL_Z 0 /***************************************************************************************/ int is_Z_atom( U_CHAR el_number ) { typedef enum tag_Z_elnumber { el_C , el_N , el_P , el_As, el_Sb, el_S , el_Se, el_Te, el_Cl, el_Br, el_I , #if ( ALL_NONMETAL_Z == 1 ) el_B , el_O , el_Si, el_Ge, el_F , el_At, #endif el_len } Z_ELNUMBER; static U_CHAR el_numb[el_len]; /* return is_el_a_metal( (int)el_number ); */ if ( !el_numb[el_C] ) { el_numb[el_C ] = (U_CHAR)get_periodic_table_number( "C" ); el_numb[el_N ] = (U_CHAR)get_periodic_table_number( "N" ); el_numb[el_P ] = (U_CHAR)get_periodic_table_number( "P" ); el_numb[el_As] = (U_CHAR)get_periodic_table_number( "As" ); el_numb[el_Sb] = (U_CHAR)get_periodic_table_number( "Sb" ); el_numb[el_S ] = (U_CHAR)get_periodic_table_number( "S" ); el_numb[el_Se] = (U_CHAR)get_periodic_table_number( "Se" ); el_numb[el_Te] = (U_CHAR)get_periodic_table_number( "Te" ); el_numb[el_Cl] = (U_CHAR)get_periodic_table_number( "Cl" ); el_numb[el_Br] = (U_CHAR)get_periodic_table_number( "Br" ); el_numb[el_I ] = (U_CHAR)get_periodic_table_number( "I" ); #if ( ALL_NONMETAL_Z == 1 ) el_numb[el_B ] = (U_CHAR)get_periodic_table_number( "B" ); el_numb[el_O ] = (U_CHAR)get_periodic_table_number( "O" ); el_numb[el_Si] = (U_CHAR)get_periodic_table_number( "Si" ); el_numb[el_Ge] = (U_CHAR)get_periodic_table_number( "Ge" ); el_numb[el_F ] = (U_CHAR)get_periodic_table_number( "F" ); el_numb[el_At] = (U_CHAR)get_periodic_table_number( "At" ); #endif } if ( memchr( el_numb, el_number, el_len ) ) { return 1; } return 0; } /***************************************************************************************/ int IsZOX( inp_ATOM *atom, int at_x, int ord ) { /* detect O==Z--X, O=O,S,Se,Te */ static U_CHAR el_number_O = 0; static U_CHAR el_number_S = 0; static U_CHAR el_number_Se = 0; static U_CHAR el_number_Te = 0; inp_ATOM *at_Z = atom + atom[at_x].neighbor[ord]; int i, neigh, num_O; if ( !el_number_O ) { el_number_O = (U_CHAR)get_periodic_table_number( "O" ); el_number_S = (U_CHAR)get_periodic_table_number( "S" ); el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); } for ( i = 0, num_O = 0; i < at_Z->valence; i ++ ) { neigh = at_Z->neighbor[i]; if ( neigh == at_x ) { continue; } if ( atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 && atom[neigh].charge == 0 && atom[neigh].radical == 0 && (atom[neigh].el_number == el_number_O || atom[neigh].el_number == el_number_S || atom[neigh].el_number == el_number_Se || atom[neigh].el_number == el_number_Te ) ) { num_O ++; } } return num_O; } /***************************************************************************************/ int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ) { static U_CHAR el_number_C = 0; static U_CHAR el_number_O = 0; static U_CHAR el_number_S = 0; static U_CHAR el_number_Se = 0; static U_CHAR el_number_Te = 0; static U_CHAR el_number_P = 0; static U_CHAR el_number_N = 0; static U_CHAR el_number_H = 0; static U_CHAR el_number_F = 0; static U_CHAR el_number_Cl = 0; static U_CHAR el_number_Br = 0; static U_CHAR el_number_I = 0; inp_ATOM *at = atom + at_no; #if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract > 0 ? -1 : 1; /* 0 or -2 => add, 1 or 2 => subtract */ int bNoAdjIon = (bSubtract==0 || bSubtract==1); #else int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract? -1 : 1; #endif int bUnsatNHasTerminalO = 0; if ( !el_number_C ) { el_number_C = (U_CHAR)get_periodic_table_number( "C" ); el_number_O = (U_CHAR)get_periodic_table_number( "O" ); el_number_S = (U_CHAR)get_periodic_table_number( "S" ); el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); el_number_P = (U_CHAR)get_periodic_table_number( "P" ); el_number_N = (U_CHAR)get_periodic_table_number( "N" ); el_number_H = (U_CHAR)get_periodic_table_number( "H" ); el_number_F = (U_CHAR)get_periodic_table_number( "F" ); el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); el_number_I = (U_CHAR)get_periodic_table_number( "I" ); } type = ATT_NONE; mask = 0; if ( at->radical && at->radical != RADICAL_SINGLET ) { goto exit_function; } if ( is_el_a_metal( at->el_number ) ) { goto exit_function; /* metal */ } if ( at->charge < -1 || at->charge > 1 ) { goto exit_function; } if ( !at->valence && at->charge == 1 && !at->num_H && !at->radical && at->el_number == el_number_H ) { /* a proton (#1) */ type = ATT_PROTON; mask = ATBIT_Proton; goto count_mask_bits; } if ( !at->valence && at->charge == -1 && !at->num_H && !at->radical && ( at->el_number == el_number_F || at->el_number == el_number_Cl || at->el_number == el_number_Br || at->el_number == el_number_I ) ) { /* a halogen anion (#2) */ type = ATT_HalAnion; mask = ATBIT_HalAnion; goto count_mask_bits; } #if ( HAL_ACID_H_XCHG == 1 ) /* halogen/chalcogen acid */ if ( !at->valence && at->charge == 0 && 1 == at->num_H && !at->radical && ( at->el_number == el_number_F || at->el_number == el_number_Cl || at->el_number == el_number_Br || at->el_number == el_number_I ) || !at->valence && at->charge == 0 && 2 == at->num_H && !at->radical && ( at->el_number == el_number_O || at->el_number == el_number_S || at->el_number == el_number_Se || at->el_number == el_number_Te ) ) { /* a halogen/chalcogen acid (#3) */ type = ATT_HalAcid; mask = ATBIT_HalAcid; goto count_mask_bits; } #endif if ( detect_unusual_el_valence( at->el_number, at->charge, at->radical, at->chem_bonds_valence, at->num_H, at->valence ) ) { goto exit_function; /* unusual valence state */ } /* check neighbors */ for ( i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i ++ ) { neigh = at->neighbor[i]; #if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 ) { goto exit_function; /* neighboring charge */ } if ( atom[neigh].charge && at->charge ) { if ( bNoAdjIon ) { goto exit_function; /* neighboring charge */ } type = ATT_NONE; mask = 0; goto count_mask_bits; } #else if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge ) { goto exit_function; /* neighboring charge */ } #endif if ( detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical, atom[neigh].chem_bonds_valence, atom[neigh].num_H, atom[neigh].valence ) ) { goto exit_function; /* neighbor in unusual valence state */ } if ( is_Z_atom( atom[neigh].el_number ) ) { num_z ++; } if ( is_el_a_metal( atom[neigh].el_number ) ) { num_m ++; } num_o += (atom[neigh].el_number == el_number_O); if ( at->el_number == el_number_N && at->valence == 2 && !at->charge && /*at->valence < at->chem_bonds_valence &&*/ atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 && (atom[neigh].el_number == el_number_O || atom[neigh].el_number == el_number_S || atom[neigh].el_number == el_number_Se || atom[neigh].el_number == el_number_Te )) { bUnsatNHasTerminalO ++; } } /* O, S, Se, Te */ if ( at->el_number == el_number_O || at->el_number == el_number_S || at->el_number == el_number_Se || at->el_number == el_number_Te ) { if ( at->charge == 1 ) { if ( at->num_H ) { /* #4 */ type = ATT_O_PLUS; mask |= ATBIT_OH_Plus; } else { /* #5 */ type = ATT_O_PLUS; mask |= ATBIT_O_Plus; } } else if ( at->valence > 1 ) { goto exit_function; /* not a terminal atom #C1 */ } else if ( at->valence && !(num_z || num_o) ) { if ( num_m == at->valence ) { goto exit_function; /* #C2 */ } goto count_mask_bits; /* #C3 count charges, no donor or acceptor found */ } else /* here at->neigh[0] is one of: O, or Z={C, N, P, As, Sb, S, Se, Te, Cl, Br, I} */ if ( at->valence ) { neigh = at->neighbor[0]; /* Z or O only */ if ( !atom[neigh].charge && atom[neigh].el_number == el_number_C && atom[neigh].chem_bonds_valence > atom[neigh].valence ) { /* =C-OH, #C-OH, =C-O(-), #C-O(-), -C=O, =C=O; O = O, S, Se, Te */ type = ATT_ACIDIC_CO; if ( at->num_H == 1 ) { mask |= (ATBIT_COH); /* #6: =C-OH, #C-OH; O=O,S,Se,Te */ /*nAtTypeTotals[ATTOT_NUM_COH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_CO_Minus); /* #7: =C-O(-), #C-O(-); O=O,S,Se,Te */ /*nAtTypeTotals[ATTOT_NUM_CO_Minus] ++;*/ } else if ( !at->num_H && !at->charge ) { mask |= (ATBIT_CO); /* #8 -C=O, =C=O; O=O,S,Se,Te */ /*nAtTypeTotals[ATTOT_NUM_CO] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else if ( !atom[neigh].charge && (atom[neigh].el_number == el_number_O || atom[neigh].el_number == el_number_S || atom[neigh].el_number == el_number_Se || atom[neigh].el_number == el_number_Te ) && atom[neigh].chem_bonds_valence == atom[neigh].valence ) { /* -O-OH, -O-O(-); O = O, S, Se, Te */ type = ATT_OO; if ( at->num_H == 1 ) { mask |= (ATBIT_OOH); /* #9 -O-OH */ /*nAtTypeTotals[ATTOT_NUM_OOH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_OO_Minus); /* #10 -O-O(-) */ /*nAtTypeTotals[ATTOT_NUM_OO_Minus] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else if ( !atom[neigh].charge && atom[neigh].chem_bonds_valence == atom[neigh].valence && atom[neigh].el_number == el_number_C && at->el_number != el_number_O ) { /* >C-S(-), >C-SH; S = S, Se, Te */ type = ATT_ACIDIC_S; if ( at->num_H == 1 ) { mask |= (ATBIT_CSH); /* #11: >C-SH, >CH-SH, -CH2-SH; S = S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_CSH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_CS_Minus); /* #12: >C-S(-), >CH-S(-), -CH2-S(-); S = S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_CS_Minus] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else if ( atom[neigh].el_number == el_number_N && atom[neigh].valence == 2 && (!atom[neigh].num_H || atom[neigh].num_H == 1 && atom[neigh].charge == 1) ) { /* N or N(-) or NH(+) neighbor */ type = ATT_NO; /* single bond only */ if ( at->num_H == 1 ) { mask |= (ATBIT_NOH); /* #13: =N-OH, =NH(+)-OH, #N(+)-OH, -N(-)-OH; O = O, S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_NO_Minus); /* #14: =N-O(-); O = O, S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ } else if ( atom[neigh].charge == 1 || atom[neigh].charge == 0 ) { mask |= (ATBIT_NO); /* #15: =N(+)=O, -NH(+)=O -N=O */ /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else if ( atom[neigh].el_number == el_number_N ) { type = ATT_N_O; /* #16: single bond only */ if ( at->num_H == 1 ) { mask |= (ATBIT_N_OH); /* #16: -NH-OH, >N-OH or >N(+)charge == -1 ) { mask |= (ATBIT_N_O_Minus); /* #17: -NH-O(-), >N-O(-); O = O, S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ } else if ( atom[neigh].charge == 1 ) { mask |= (ATBIT_N_O); /* #18: >N(+)=O */ /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else if ( atom[neigh].el_number != el_number_C && atom[neigh].el_number != el_number_O && !is_el_a_metal( atom[neigh].el_number ) && atom[neigh].chem_bonds_valence > atom[neigh].valence ) { /* =Z-OH, #Z-OH, =Z-O(-), #Z-O(-), -Z=O, =Z=O; =Z(+)-OH, #Z(+)-OH, =Z-O(-), #Z-O(-), -Z(+)=O, =Z(+)=O; O = O, S, Se, Te */ /* neigh = Z\{N,C} = P, As, Sb, S, Se, Te, Cl, Br, I */ if ( at->chem_bonds_valence == 1 && IsZOX( atom, at_no, 0 ) ) { type = ATT_ZOO; if ( at->num_H == 1 ) { mask |= (ATBIT_ZOOH); /* 18: O=Z-OH; O=O,S,Se,Te; Z may have charge */ /*nAtTypeTotals[ATTOT_NUM_ZOOH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_ZOO_Minus); /* 19: O=Z-O(-); O = O, S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_ZOO_Minus] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } else { type = ATT_OTHER_ZO; if ( at->num_H == 1 ) { mask |= (ATBIT_ZOH); /* 20: =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge */ /*nAtTypeTotals[ATTOT_NUM_ZOH] ++;*/ } else if ( at->charge == -1 ) { mask |= (ATBIT_ZO_Minus); /* 21: =Z-O(-), #Z-O(-); O = O, S, Se, Te */ /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ } else if ( at->num_H == 0 ) { mask |= (ATBIT_ZO); /* 22: -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ /*nAtTypeTotals[ATTOT_NUM_ZO] ++;*/ } else { mask |= (ATBIT_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ } } } else if ( at->charge == -1 && !is_el_a_metal( atom[neigh].el_number ) ) { /* >Z-O(-); O=O,S,Se,Te */ type = ATT_OTHER_NEG_O; mask |= (ATBIT_O_Minus); /* 23: -Z-O(-); O=O,S,Se,Te */ /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ } } else if ( at->charge == -1 && at->num_H == 1 ) { type = ATT_OH_MINUS; mask |= (ATBIT_O_Minus); /* 25: HO(-); O=O,S,Se,Te */ } } else /* P, N, neutral valence = 3 (not 5) */ if ( (at->el_number == el_number_N || at->el_number == el_number_P) && 0 <= at->valence && at->valence <= 3 && at->chem_bonds_valence + at->num_H == 3 + at->charge ) { if ( at->valence && !(num_z /*|| num_o == at->valence*/) ) { if ( num_m == at->valence ) { goto exit_function; } goto count_mask_bits; /* N(III), N(-)(II), N(+)(IV) and same P that have only oxygen neighbors are ignored here */ } type = (at->el_number == el_number_N)? ATT_ATOM_N : ATT_ATOM_P; switch ( at->charge ) { case -1: if (at->el_number == el_number_N) { mask |= (ATBIT_N_Minus); /* 26: -NH(-), =N(-), >N(-) */ if ( at->num_H ) mask |= (ATBIT_NP_H); /* 27: -NH(-) */ #if ( FIX_NP_MINUS_BUG == 1 ) else if ( at->valence == 1 && at->chem_bonds_valence >= 2 && (at->bond_type[0] & BOND_MARK_ALL) ) type |= ATT_NP_MINUS_V23; /* =N(-) created by normalization 2010-03-11 DT */ #endif } /*nAtTypeTotals[ATTOT_NUM_N_Minus] += (at->el_number == el_number_N);*/ break; case 0: if ( at->num_H ) { mask |= (ATBIT_NP_H); /* 28: -NH2, =NH, >NH */ /*nAtTypeTotals[ATTOT_NUM_NP_H] ++;*/ } else { if ( bUnsatNHasTerminalO == 1 ) { mask |= (ATBIT_ON); /* 29: -N=O,-N=OH(+) only, not =N-OH */ } else { mask |= (ATBIT_NP); /* 30: -P=O,-P=OH(+), >N- =N- (incl. =N-OH) , #N */ /*nAtTypeTotals[ATTOT_NUM_NP] ++;*/ } } break; /* ignore neutral N or P */ case 1: if ( at->num_H ) { mask |= (ATBIT_NP_Proton); /* 31: NH4(+), -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+) */ /*nAtTypeTotals[ATTOT_NUM_NP_Proton] ++;*/ } else if ( at->chem_bonds_valence > at->valence ) { mask |= (ATBIT_NP_Plus); /* =N(+)=, #N(+)-, =N(+)< */ /*nAtTypeTotals[ATTOT_NUM_NP_Plus] ++;*/ } else { type = 0; /* 32: ignore onium cations >N(+)< */ } break; default: mask |= (1 << ATTOT_NUM_Errors); /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ break; } } count_mask_bits: if ( nAtTypeTotals ) { if ( mask && !(mask & (ATBIT_Errors)) ) { for ( i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i ++, bit <<= 1 ) { if ( bit & mask ) { nAtTypeTotals[i] += delta; } } } /* count charges */ if ( at->charge ) { nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at->charge; nAtTypeTotals[ATTOT_NUM_CHARGES] += delta; } } if ( pMask ) { *pMask = mask; } exit_function: if ( mask & (ATBIT_Errors) ) { type = 0; if ( nAtTypeTotals ) { nAtTypeTotals[ATTOT_NUM_Errors] += 1; } } return type; } /***************************************************************************************/ int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ) { int i, mask, type, num_removed; for ( i = 0, num_removed = 0; i < num_atoms; i ++ ) { if ( (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && (PR_SIMPLE_MSK & mask ) ) { #if ( bRELEASE_VERSION == 0 ) if ( at[i].charge != 1 || at[i].num_H == 0 ) { return -1; /* program error */ } #endif type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ at[i].charge = 0; AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, t_group_info ); /*at[i].num_H --;*/ num_removed ++; #if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ #else type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* bug: subtract instead of add */ #endif /* if ( nAtTypeTotals ) { nAtTypeTotals[ATTOT_NUM_NP_Proton] --; if ( at[i].num_H ) { nAtTypeTotals[ATTOT_NUM_NP_H] ++; } else { nAtTypeTotals[ATTOT_NUM_NP] ++; } nAtTypeTotals[ATTOT_TOT_CHARGE] --; nAtTypeTotals[ATTOT_NUM_CHARGES] --; } */ } } return num_removed; } /***************************************************************************************/ int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ) { int mask; if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) #if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) && ( at[endpoint].charge == nCharge || !at[endpoint].charge ) #endif ) { return 1; } return 0; } /***************************************************************************************/ int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ) { if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_H, PR_HARD_MSK_H, 0 ) ) { /* obtain donor/acceptor info */ int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; if ( neutral_valence != 2 /* O, S, Se, Te */ && neutral_valence != 3 /* N, P */ ) { return -1; /* wrong endpoint neutral valence */ } else { int edge_flow = at[endpoint].num_H; int num_bonds = at[endpoint].valence; int edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ edge_flow = inchi_min( edge_flow, edge_cap); /* what this means: */ if ( edge_cap ) { if ( edge_cap > edge_flow ) *cSubType |= SALT_ACCEPTOR; if ( edge_flow ) *cSubType |= SALT_DONOR_H; return 4; } } } return -1; } /***************************************************************************************/ int bIsNegAtomType( inp_ATOM *at, int endpoint, int *cSubType ) { int sub_type = 0; if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ) ) { /* obtain donor/acceptor info */ int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; if ( neutral_valence != 2 /* O, S, Se, Te */ && neutral_valence != 3 /* N, P */ ) { return -1; /* wrong endpoint neutral valence */ } else { int edge_flow = (at[endpoint].charge == -1); int num_bonds = at[endpoint].valence; int edge_cap = neutral_valence - num_bonds - at[endpoint].num_H; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ edge_flow = inchi_min( edge_flow, edge_cap); /* what this means: */ if ( edge_cap ) { if ( edge_cap > edge_flow ) sub_type |= SALT_ACCEPTOR; if ( edge_flow ) { sub_type |= SALT_DONOR_Neg; } if ( sub_type ) { *cSubType |= sub_type; return 4; } } } } return -1; } /*****************************************************************************/ int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ) { int ret1, ret2, ret; int sub_type = 0; ret1 = bIsHDonorAccAtomType( at, i, &sub_type ); ret2 = bIsNegAtomType( at, i, &sub_type ); ret = inchi_max(ret1, ret2); if ( ret > 0 && sub_type ) { *cSubType |= sub_type; return ret; } return -1; } /***************************************************************************************/ int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, BN_STRUCT *pBNS, int nType, int nMask, int nCharge ) { int k, c_point, cg, centerpoint, fictpoint, type, ret = 0; int num_cg = 1; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; /* new c-group bns-ID */ BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ BNS_VERTEX *vertex_cpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ int mask, num_CPoints; /* Debug: check overflow */ if ( num_vertices + num_cg >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } /* count new c-group edges */ for ( c_point = 0, num_CPoints = 0; c_point < num_atoms; c_point ++ ) { if ( (nType & GetAtomChargeType( at, c_point, NULL, &mask, 0 )) && (mask & nMask) #if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) && ( at[c_point].charge == nCharge || !at[c_point].charge ) #endif ) { num_CPoints ++; } } if ( !num_CPoints ) { return 0; } /* clear the new vertex */ memset( pBNS->vert+num_vertices, 0, 1*sizeof(pBNS->vert[0]) ); /* *old* Make sure the last t-group has the largest t-group ID: this is necessary to correctly add new edges and vertices for testing augmenting paths */ /**************************************/ /* initialize new fictitious vertex */ /* representing c-point group */ /**************************************/ ver_ficpont_prev = pBNS->vert+num_vertices - 1; for ( cg = 0; cg < num_cg; cg ++, ver_ficpont_prev = vert_ficpoint ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the being added vertex Note: nGroupNumber are not contiguous */ vert_ficpoint = pBNS->vert+num_vertices + cg; vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; vert_ficpoint->max_adj_edges = num_CPoints+BNS_ADD_EDGES; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP | ((nCharge<0)?BNS_VERT_TYPE_C_NEGATIVE:0); } /************************************************/ /* connect c-points to the fictitious vertices */ /* representing c-point groups; set caps, flows */ /************************************************/ cg = 1; for ( c_point = 0; c_point < num_atoms; c_point ++ ) { if ( (nType & (type=GetAtomChargeType( at, c_point, NULL, &mask, 0 ))) && (mask & nMask) #if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) && ( at[c_point].charge == nCharge || !at[c_point].charge) #endif ); else continue; fictpoint = cg + num_vertices - 1; /* c-group vertex index */ vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ /* Debug: check overflow */ if ( fictpoint >= pBNS->max_vertices || num_edges >= pBNS->max_edges || vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { ret = BNS_VERT_EDGE_OVFL; break; } vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; if ( (KNOWN_ACIDIC_TYPE & type) && nCharge < 0 ) { vertex_cpoint->type |= pBNS->type_TACN; } #if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ /* if their current capacity is zero */ /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { int iedge = vertex_cpoint->iedge[k]; if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between c_point and centerpoint */ centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALTERN || bond_type == BOND_SINGLE ) { pBNS->edge[iedge].cap = 1; } } } } #endif /* } FIX_CPOINT_BOND_CAP */ /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ edge = pBNS->edge + num_edges; edge->cap = 1; edge->flow = 0; edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; /* remove previous temporary ban */ #endif /* nCharge = +1: mark edge to c-point having no (+)-moveable charge with flow=1 */ /* nCharge = -1: mark edge to c-point having -1 moveable charge with flow=1 */ if ( nCharge==1 && at[c_point].charge != 1 || nCharge==-1 && at[c_point].charge == -1 ) /*if ( !CHARGED_CPOINT(at,c_point) )*/ { /* increment new edge flow, update st_edges of the adjacent vertices */ edge->flow ++; /* increment c-group vertex st-flow & cap */ vert_ficpoint->st_edge.flow ++; vert_ficpoint->st_edge.cap ++; /* increment c-point vertex st-flow & cap */ vertex_cpoint->st_edge.flow ++; vertex_cpoint->st_edge.cap ++; } #if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ /* if their current capacity is zero */ /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { int iedge = vertex_cpoint->iedge[k]; VertexFlow nNewCap = vertex_cpoint->st_edge.cap; centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between c_point and centerpoint */ if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); pBNS->edge[iedge].cap = nNewCap; } } #if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ else if ( centerpoint < pBNS->num_atoms && edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { pBNS->edge[iedge].cap ++; } #endif } #endif /* } FIX_CPOINT_BOND_CAP */ /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; edge->cap0 = edge->cap; edge->flow0 = edge->flow; } ret = pBNS->num_vertices; /* new c-group atom number */ pBNS->num_edges = num_edges; pBNS->num_vertices += num_cg; pBNS->num_c_groups += num_cg; return ret; } /*********************************************************************************/ int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, BN_STRUCT *pBNS, int nType, int nMask ) { int ret = 0; /* ret = ReInitBnStruct( pBNS ); */ int k, endpoint, tg, centerpoint, fictpoint; int num_tg = 1; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ BNS_VERTEX *vert_endpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds; /* Debug: check overflow */ if ( num_vertices + num_tg >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } /* count new t-group edges */ for ( endpoint = 0, num_endpoints = 0; endpoint < num_atoms; endpoint ++ ) { if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) ) { num_endpoints ++; } } if ( !num_endpoints ) { return 0; } /* since t-group IDs may be not contiguous, clear all vertices that will be added. all-zeroes-vertex will be ignored by the BNS */ memset( pBNS->vert+num_vertices, 0, num_tg*sizeof(pBNS->vert[0]) ); /* *old* Make sure the last t-group has the largest t-group ID: this is necessary to correctly add new edges and vertices for testing augmenting paths */ /**************************************/ /* initialize new fictitious vertex */ /* representing t-point group */ /**************************************/ ver_ficpont_prev = pBNS->vert+num_vertices - 1; for ( tg = 0; tg < num_tg; tg ++, ver_ficpont_prev = vert_ficpoint ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the vertex that is being added Note: nGroupNumber are not contiguous */ vert_ficpoint = pBNS->vert+num_vertices + tg; vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; vert_ficpoint->max_adj_edges = num_endpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; vert_ficpoint->type |= BNS_VERT_TYPE_TGROUP; } tg = 1; for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask)); else continue; fictpoint = tg + num_vertices - 1; vert_ficpoint = pBNS->vert + fictpoint; vert_endpoint = pBNS->vert + endpoint; /* Debug: check overflow */ if ( fictpoint >= pBNS->max_vertices || num_edges >= pBNS->max_edges || vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { ret = BNS_VERT_EDGE_OVFL; break; } /* obtain donor/acceptor info */ neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; if ( neutral_valence != 2 /* O, S, Se, Te */ && neutral_valence != 3 /* N, P */ ) { ret = BNS_PROGRAM_ERR; /* wrong endpoint neutral valence */ break; } edge_flow = at[endpoint].num_H; num_bonds = at[endpoint].valence; edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ if ( 3 == neutral_valence /* N or P */ && 1 < num_bonds ) { edge_cap ++; /* allow -NH2(+)- => -N=, >NH(+)- => >N- */ } edge_flow = inchi_min( edge_flow, edge_cap); /* if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { ret = BNS_BOND_ERR; break; } */ vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ edge = pBNS->edge + num_edges; edge->cap = edge_cap; edge->flow = edge_flow; edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; #endif /* adjust st_flow and st_cap of the adjacent vertices */ /* adjust t-group vertex st-flow & cap */ vert_ficpoint->st_edge.flow += edge->flow; vert_ficpoint->st_edge.cap += edge->flow; /* adjust endpoint vertex st-flow & cap */ vert_endpoint->st_edge.flow += edge->flow; vert_endpoint->st_edge.cap += edge->flow; /* adjust edge cap & flow according to the number of H and number of bonds */ for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { int iedge = vert_endpoint->iedge[k]; VertexFlow nNewCap = vert_endpoint->st_edge.cap; if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between endpoint and centerpoint */ centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); pBNS->edge[iedge].cap = nNewCap; } } } /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; edge->cap0 = edge->cap; edge->flow0 = edge->flow; } ret = pBNS->num_vertices; /* new t-group atom number */ pBNS->num_edges = num_edges; pBNS->num_vertices += num_tg; pBNS->num_t_groups += num_tg; return ret; } /*********************************************************************************/ int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ) { int ret = 0; /* ret = ReInitBnStruct( pBNS ); */ int k, endpoint, /*centerpoint, fictpoint,*/ iedge; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; BNS_VERTEX *vert_ficpoint /*, *ver_ficpont_prev*/; /* fictitious vertex describing t-group */ BNS_VERTEX *vert_endpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ /*int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;*/ int is_t_group = 0, is_c_group = 0; /* Debug: check overflow */ if ( pBNS->num_added_atoms + pBNS->num_c_groups + pBNS->num_t_groups + num_atoms >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } if ( tg + 1 != num_vertices ) { return BNS_VERT_EDGE_OVFL; } vert_ficpoint = pBNS->vert + tg; if ( vert_ficpoint->type & BNS_VERT_TYPE_TGROUP ) { is_t_group = 1; } if ( vert_ficpoint->type & BNS_VERT_TYPE_C_GROUP ) { is_c_group = 1; if ( vert_ficpoint->type & BNS_VERT_TYPE_C_NEGATIVE ) is_c_group = 2; } for ( k = vert_ficpoint->num_adj_edges-1; 0 <= k; k -- ) { iedge = vert_ficpoint->iedge[k]; if ( iedge + 1 != num_edges ) { return BNS_VERT_EDGE_OVFL; } edge = pBNS->edge + iedge; endpoint = edge->neighbor12 ^ tg; vert_endpoint = pBNS->vert + endpoint; /* adjust st_flow, st_cap */ vert_endpoint->st_edge.cap0 = vert_endpoint->st_edge.cap -= edge->flow; vert_endpoint->st_edge.flow0 = vert_endpoint->st_edge.flow -= edge->flow; if ( pBNS->type_TACN && (vert_endpoint->type & pBNS->type_TACN) == pBNS->type_TACN ) { vert_endpoint->type ^= pBNS->type_TACN; } if ( is_t_group ) { vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_ENDPOINT); } if ( is_c_group ) { vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_C_POINT); } /* remove edge */ if ( edge->neigh_ord[0]+1 != vert_endpoint->num_adj_edges ) { return BNS_VERT_EDGE_OVFL; } vert_endpoint->num_adj_edges --; memset( edge, 0, sizeof(*edge) ); num_edges --; if ( 1 == is_t_group && endpoint < num_atoms ) { at->endpoint = 0; } if ( 1 == is_c_group && endpoint < num_atoms ) { at->c_point = 0; } } memset( vert_ficpoint, 0, sizeof(*vert_ficpoint) ); num_vertices --; pBNS->num_edges = num_edges; pBNS->num_vertices = num_vertices; if ( is_t_group ) pBNS->num_t_groups --; if ( is_c_group ) pBNS->num_c_groups --; return ret; } /******************************************************************************************/ int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ) { int i, j; BNS_EDGE *pEdge=NULL; for ( i = 0; i < pBNS->num_vertices; i ++ ) { pBNS->vert[i].st_edge.flow0 = pBNS->vert[i].st_edge.flow; pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; for ( j = 0; j < pBNS->vert[i].num_adj_edges; j ++ ) { pEdge = pBNS->edge + pBNS->vert[i].iedge[j]; pEdge->cap0 = pEdge->cap; pEdge->flow0 = pEdge->flow; } } return 0; } /******************************************************************************************/ int ArTypMask[] = { AR_SIMPLE_TYP1, AR_SIMPLE_MSK1, AR_SIMPLE_TYP2, AR_SIMPLE_MSK2, AR_SIMPLE_TYP3, AR_SIMPLE_MSK3, 0, 0 }; /******************************************************************************************/ int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ) { int i, j, max_j=-1, mask, type, num_removed; int num[AR_SIMPLE_STEPS+1], num_tot; for ( j = 0; ArTypMask[2*j]; j ++ ) { num[max_j = j] = 0; } for ( i = 0; i < num_atoms; i ++ ) { if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; j <= max_j; j ++ ) { if ( (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { num[j] ++; break; } } } } for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { if ( (num_tot += num[j]) >= num2remove ) { max_j = j; break; } } if ( !num_tot ) { return 0; } for ( i = 0, num_removed = 0; i < num_atoms && num_removed < num2remove; i ++ ) { if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; j <= max_j; j ++ ) { if ( num[j] && (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ num[j] --; at[i].charge --; AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); /*at[i].num_H --;*/ num_removed ++; type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ break; } } } } /* pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] -= num_removed; pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] += num_removed; */ return num_removed; } /******************************************************************************************/ int bHasAcidicHydrogen( inp_ATOM *at, int i ) { int bFound = 0, j, type, mask; if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; ArTypMask[2*j]; j ++ ) { if ( (type & ArTypMask[2*j]) && (mask & ArTypMask[2*j+1]) ) { bFound ++; break; } } } return bFound; } /******************************************************************************************/ int bHasOtherExchangableH ( inp_ATOM *at, int i ) { int bFound = 0, type, mask; if ( at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { if ( (type & ATT_ATOM_N) && (mask & ATBIT_NP_H) ) { bFound ++; } } return bFound; } /******************************************************************************************/ int AaTypMask[] = { AA_SIMPLE_TYP1, AA_SIMPLE_MSK1, #if ( FIX_NP_MINUS_BUG == 1 ) AA_SIMPLE_TYP4, AA_SIMPLE_MSK4, /* should not follow 0,0 pair */ #endif AA_SIMPLE_TYP2, AA_SIMPLE_MSK2, AA_SIMPLE_TYP3, AA_SIMPLE_MSK3, 0, 0 }; /******************************************************************************************/ int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ) { int i, j, max_j=-1, mask, type, num_added; int num[AR_SIMPLE_STEPS+1], num_tot; for ( j = 0; AaTypMask[2*j]; j ++ ) { num[max_j = j] = 0; } for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; j <= max_j; j ++ ) { if ( (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { num[j] ++; break; } } } } for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { if ( (num_tot += num[j]) >= num2add ) { max_j = j; break; } } if ( !num_tot ) { return 0; } for ( i = 0, num_added = 0; i < num_atoms && num_added < num2add; i ++ ) { if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; j <= max_j; j ++ ) { if ( num[j] && (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ num[j] --; at[i].charge ++; AddOrRemoveExplOrImplH( 1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); /*at[i].num_H ++;*/ num_added ++; type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ break; } } } } /* pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] += num_added; pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] -= num_added; */ return num_added; } /******************************************************************************************/ int bHasAcidicMinus( inp_ATOM *at, int i ) { int bFound = 0, j, type, mask; if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { for ( j = 0; AaTypMask[2*j]; j ++ ) { if ( (type & AaTypMask[2*j]) && (mask & AaTypMask[2*j+1]) ) { bFound ++; break; } } } return bFound; } /****************************************************************************************** Create 2 tautomeric groups: (1) for O on -C=O, (2) for the rest of the atoms. Pull H from (2) to (1); remove later *******************************************************************************************/ int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) { int cg_Plus = 0; int cg_Minus = 0; int tg_H_Other = 0; int tg_H_Acid = 0; int ret = 0, ret2; int nDelta, nNumChanges = 0, nNumMoved2AcidH = 0, nNumNeutralized = 0, nPrevNumCharges; int nPosCharges, nPosCharges2; int nNegCharges, nNegCharges2; /* int nNumNP_H, nNumNP_H2; int nNumOS_H, nNumOS_H2; */ nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ /* prevent free exchange H <-> (-) */ pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); pBNS->type_T = BNS_VERT_TYPE_TGROUP; pBNS->type_TACN = BNS_VERT_TYPE_ACID; /* create (+) charge group */ cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_POS, AR_HARD_MSK_POS, 1 ); /* create (-) charge group */ /* if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_N_Minus] ) */ cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_NEG, AR_HARD_MSK_NEG, -1 ); pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); pBNS->type_T = BNS_VERT_TYPE_TGROUP; pBNS->type_TACN = BNS_VERT_TYPE_ACID; /* create tautomeric group for non-acidic or negatively charged acidic O */ tg_H_Other = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HN, AR_HARD_MSK_HN ); /* create tautomeric group for possibly acidic O */ tg_H_Acid = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HA, AR_HARD_MSK_HA ); if ( tg_H_Other >= num_atoms && tg_H_Acid >= num_atoms ) { /* find alt path to remove one proton */ do { /* remove a proton */ nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, tg_H_Other /*nVertDoubleBond*/, tg_H_Acid /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } nNumMoved2AcidH ++; if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; } } } while ( (ret & 1) && nNumMoved2AcidH < num2remove ); /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ if ( (nNumMoved2AcidH /*|| bCancelChargesAlways*/) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { do { nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; } } } while ( ret & 1 ); } } ret = 0; if ( tg_H_Acid >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Acid, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( tg_H_Other >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Other, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Minus >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Plus >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); if ( !ret && ret2 ) ret = ret2; } pBNS->type_CN = 0; pBNS->type_T = 0; pBNS->type_TACN = 0; if ( ret ) { return ret; } if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { } nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { return BNS_PROGRAM_ERR; } if ( nNumCanceledCharges ) { #if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) *nNumCanceledCharges += 2*nNumNeutralized; #else *nNumCanceledCharges = 2*nNumNeutralized; #endif } return nNumMoved2AcidH; } /******************************************************************************************/ int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) { int cg_Plus = 0; int cg_Minus_CO = 0; int cg_Minus_Other = 0; int tg_H = 0; int ret = 0, ret2; int nDelta, nNumChanges = 0, nNumMoved2AcidMinus = 0, nNumNeutralized = 0, nPrevNumCharges; int nPosCharges, nPosCharges2; int nNegCharges, nNegCharges2; /* int nNumNP_H, nNumNP_H2; int nNumOS_H, nNumOS_H2; */ nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ /* prevent free exchange H <-> (-) */ pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); pBNS->type_T = BNS_VERT_TYPE_TGROUP; pBNS->type_TACN = BNS_VERT_TYPE_ACID; /* create (+) charge group */ cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_POS, AA_HARD_MSK_POS, 1 ); /* create (-) charge group */ /* if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_N_Minus] ) */ cg_Minus_CO = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_CO, AA_HARD_MSK_CO, -1 ); cg_Minus_Other = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_NEG, AA_HARD_MSK_NEG, -1 ); pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); pBNS->type_T = BNS_VERT_TYPE_TGROUP; pBNS->type_TACN = BNS_VERT_TYPE_ACID; /* create tautomeric group for all H */ tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_H, AA_HARD_MSK_H ); if ( cg_Minus_Other >= num_atoms && cg_Minus_CO >= num_atoms ) { /* find alt path to remove one proton */ do { /* add a proton */ nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, cg_Minus_Other /*nVertDoubleBond*/, cg_Minus_CO /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } nNumMoved2AcidMinus ++; if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; } } } while ( (ret & 1) && nNumMoved2AcidMinus < num2add ); /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ if ( (nNumMoved2AcidMinus /*|| bCancelChargesAlways*/) && cg_Minus_Other >= num_atoms && cg_Plus >= num_atoms && pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { do { nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, cg_Minus_Other /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; } } } while ( ret & 1 ); } } ret = 0; if ( tg_H >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Minus_Other >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_Other, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Minus_CO >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_CO, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Plus >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); if ( !ret && ret2 ) ret = ret2; } pBNS->type_CN = 0; pBNS->type_T = 0; pBNS->type_TACN = 0; if ( ret ) { return ret; } if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { } nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { return BNS_PROGRAM_ERR; } if ( nNumCanceledCharges ) { #if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) *nNumCanceledCharges += 2*nNumNeutralized; #else *nNumCanceledCharges = 2*nNumNeutralized; #endif } return nNumMoved2AcidMinus; } /******************************************************************************************/ /* examples include removal of H from tautomeric O that belongs to the same t-group as N: */ /* >N(+)=-N=-OH =(taut.)=> >N(+)=-NH-=O =(+charge move)=> >N-=NH(+)-=O => >N-=N-=O + H(+) */ /******************************************************************************************/ int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) { int cg_Plus = 0; int cg_Minus = 0; int tg_H = 0; #if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) int cg_PlusP = 0; #endif #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) int nPrevRemovedProtons, nCurrRemovedProtons; #endif int ret = 0, ret2; int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges; int nPosCharges, nPosCharges2; int nNegCharges, nNegCharges2; /* int nNumNP_H, nNumNP_H2; int nNumOS_H, nNumOS_H2; */ nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ /* prevent free exchange H <-> (-) */ pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); pBNS->type_T = BNS_VERT_TYPE_TGROUP; pBNS->type_TACN = BNS_VERT_TYPE_ACID; /* create (+) charge group */ cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POS, PR_HARD_MSK_POS, 1 ); /* create (-) charge group */ /* if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_N_Minus] ) */ #if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) cg_PlusP = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POSP, PR_HARD_MSK_POS, 1 ); #endif cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ); /* create single tautomeric group */ tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H ); if ( tg_H >= num_atoms && cg_Plus >= num_atoms ) { #if ( FIX_N_MINUS_NORN_BUG == 1 ) /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */ if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { do { nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; #endif ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; if ( nCurrRemovedProtons != nPrevRemovedProtons ) { return BNS_RADICAL_ERR; } #endif if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; } } } while ( ret & 1 ); } #endif /* find alt path to remove one proton */ do { /* remove a proton */ nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; #endif ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, tg_H /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; if ( nCurrRemovedProtons != nPrevRemovedProtons + (ret & 1) ) { return BNS_RADICAL_ERR; } #endif if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } nNumRemovedProtons ++; if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; } } } while ( ret & 1 ); /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { do { nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; #endif ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); if ( IS_BNS_ERROR( ret ) ) { return ret; } #if ( FIX_REM_PROTON_COUNT_BUG == 1 ) nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; if ( nCurrRemovedProtons != nPrevRemovedProtons ) { return BNS_RADICAL_ERR; } #endif if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (0 != (ret & 2)); if ( nDelta ) { /* radical pair has disappeared */ ; /* goto quick_exit;*/ } if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; } } } while ( ret & 1 ); } } ret = 0; if ( tg_H >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); if ( !ret && ret2 ) ret = ret2; } if ( cg_Minus >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); if ( !ret && ret2 ) ret = ret2; } #if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) if ( cg_PlusP >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_PlusP, pBNS ); if ( !ret && ret2 ) ret = ret2; } #endif if ( cg_Plus >= num_atoms ) { ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); if ( !ret && ret2 ) ret = ret2; } pBNS->type_CN = 0; pBNS->type_T = 0; pBNS->type_TACN = 0; if ( ret ) { return ret; } if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { } nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; /* nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; */ if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != nNumRemovedProtons ) { return BNS_PROGRAM_ERR; } if ( nNumCanceledCharges ) { #if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) *nNumCanceledCharges += 2*nNumNeutralized; #else *nNumCanceledCharges = 2*nNumNeutralized; #endif } return nNumRemovedProtons; } /***************************************************************************************/ int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ) { int i, max_num_ions, mask, type; /*int max_protons, max_O_Minus, num_H = 0, num_CO=0;*/ if ( nAtTypeTotals ) { memset( nAtTypeTotals, 0, ATTOT_ARRAY_LEN * sizeof(nAtTypeTotals[0]) ); } for ( i = 0; i < num_atoms; i++ ) { type = GetAtomChargeType( atom, i, nAtTypeTotals, &mask, 0 ); atom[i].at_type = type; /* num_H += ((type & PR_HARD_TYP_H) && (mask & ATBIT_MSK_H)); num_CO += ((type & AR_HARD_TYP_HA) && (mask & AR_HARD_MSK_HA)); */ } if ( nAtTypeTotals ) { /* max_protons = nAtTypeTotals[ATTOT_NUM_NP_Proton] + inchi_min(num_H, nAtTypeTotals[ATTOT_NUM_NP_Plus]); max_O_Minus = nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_OO_Minus] + nAtTypeTotals[ATTOT_NUM_ZOO_Minus] + nAtTypeTotals[ATTOT_NUM_NO_Minus] + nAtTypeTotals[ATTOT_NUM_O_Minus] +nAtTypeTotals[ATTOT_NUM_N_Minus]; ; max_num_ions = max_protons + max_O_Minus + nAtTypeTotals[ATTOT_NUM_CHARGES]; */ max_num_ions = nAtTypeTotals[ATTOT_NUM_CHARGES]; } else { max_num_ions = 0; } return max_num_ions; } /***************************************************************************************/ int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) { /* prepare data structure */ int num; int nNumCanceledCharges = 0; int nNumHardRemovedProtons = 0; int nNumHardRemovedAcidicProtons = 0; T_GROUP_INFO *t_group_info = pAATG->t_group_info; int ret=0, bError = 0; int bAllowHardRemove = (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__SALTS) && (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT2_SALTS) && (t_group_info->bTautFlags & TG_FLAG_MOVE_POS_CHARGES ) && (t_group_info->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS); if ( pAATG->nMarkedAtom && num_atoms < pAATG->nAllocLen ) { inchi_free( pAATG->nMarkedAtom ); qzfree( pAATG->nEndPoint ); memset( pAATG, 0, sizeof(*pAATG) ); } if ( !pAATG->nMarkedAtom && (pAATG->nMarkedAtom = (S_CHAR *) inchi_malloc( num_atoms * sizeof(pAATG->nMarkedAtom[0]))) ) { pAATG->nAllocLen = num_atoms; pAATG->nNumFound = 0; } /* o TECHMAN-5.1. Remove protons from charged heteroatoms */ /* (TECHMAN-5.1a) Simple remove of protons from N, P, and O,S,Se,Te */ if ( num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton] + pAATG->nAtTypeTotals[ATTOT_NUM_OH_Plus] ) { ret = SimpleRemoveHplusNPO(at, num_atoms, pAATG->nAtTypeTotals, t_group_info); if ( ret != num ) { bError = BNS_PROGRAM_ERR; goto exit_function; } /*t_group_info->nNumRemovedProtons += ret;*/ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NPO_SIMPLE_REMOVED : 0; } if ( (num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Plus]) && bAllowHardRemove ) { /* [TECHMAN-5.1b] Hard removing more protons from cationic N; charges may be canceled */ ret = HardRemoveHplusNP(at, num_atoms, 1, &nNumCanceledCharges, pAATG, pBNS, pBD); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } nNumHardRemovedProtons += ret; /*t_group_info->nNumRemovedProtons += ret;*/ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NP_HARD_REMOVED : 0; } if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 ) { /* o TECHMAN-5.2. Remove protons from neutral heteroatoms */ /* (TECHMAN-5.2a) Simple removal */ ret = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } /*t_group_info->nNumRemovedProtons += ret;*/ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_REMOVED : 0; if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 && bAllowHardRemove ) { /* (TECHMAN-5.2b) Hard removal */ ret = HardRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( ret > 0 ) { int ret2 = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, ret ); if ( ret2 != ret ) { bError = BNS_PROGRAM_ERR; goto exit_function; } /*t_group_info->nNumRemovedProtons += ret;*/ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_REMOVED : 0; nNumHardRemovedAcidicProtons += ret; } } } else if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 ) { ret = SimpleAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } /*t_group_info->nNumRemovedProtons -= ret;*/ /* CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N along an alternating path from another heteroatom (t-group will be detected). */ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_ADDED : 0; if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 && bAllowHardRemove ) { ret = HardAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( ret > 0 ) { int ret2 = SimpleAddAcidicProtons( at, num_atoms, pAATG, ret ); if ( ret2 != ret ) { bError = BNS_PROGRAM_ERR; goto exit_function; } /*t_group_info->nNumRemovedProtons -= ret;*/ t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_ADDED : 0; nNumHardRemovedAcidicProtons -= ret; } } } t_group_info->tni.bNormalizationFlags |= nNumCanceledCharges? FLAG_PROTON_CHARGE_CANCEL : 0; exit_function: if ( bError ) { ret = IS_BNS_ERROR(bError)? bError : BNS_PROGRAM_ERR; } return ret; } /********************************/ /* */ /* Main normalization procedure */ /* */ /********************************/ int mark_alt_bonds_and_taut_groups ( inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone ) { BN_STRUCT *pBNS = NULL; BN_DATA *pBD = NULL; int bError, nChanges, nTotChanges, taut_found, salt_found, taut_pass, salt_pass, salt_step, ret, ret2, num; int nOrigDelta, num_changed_bonds; int max_altp = BN_MAX_ALTP; int bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); BNS_FLOW_CHANGES fcd[BNS_MAX_NUM_FLOW_CHANGES+1]; C_GROUP_INFO CGroupInfo; C_GROUP_INFO *c_group_info = &CGroupInfo; S_GROUP_INFO SGroupInfo; S_GROUP_INFO *s_group_info = &SGroupInfo; INCHI_MODE *pbTautFlags = t_group_info? &t_group_info->bTautFlags : inpbTautFlags; INCHI_MODE *pbTautFlagsDone = t_group_info? &t_group_info->bTautFlagsDone : inpbTautFlagsDone; int nAtTypeTotals[ATTOT_ARRAY_LEN]; int nNumOrigTotAtoms; BN_AATG aatg; BN_AATG *pAATG = &aatg; #ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ int i, n_arom_radicals=0, *stored_radicals=NULL; #endif nChanges = 0; bError = 0; memset( c_group_info, 0, sizeof(*c_group_info) ); memset( s_group_info, 0, sizeof(*s_group_info) ); memset( pAATG, 0, sizeof(*pAATG) ); #ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ for ( i = 0; i < num_atoms; i ++ ) { if ( (at[i].radical==RADICAL_DOUBLET) && (at[i].valence==2) && (at[i].bond_type[0]==BOND_ALTERN) && (at[i].bond_type[1]==BOND_ALTERN) ) { n_arom_radicals++; if ( !stored_radicals ) { stored_radicals = (int *) inchi_calloc(num_atoms, sizeof(int)); /* 2011-08-05 explicit cast added due to Evan Bolton */ if ( !stored_radicals ) { bError = BNS_OUT_OF_RAM; goto exit_function; } stored_radicals[i] = RADICAL_DOUBLET; at[i].radical = 0; at[i].num_H++; } } } #endif if ( (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES) && num_atoms > 1 ) { /* charge groups memory allocation */ c_group_info->c_group = (C_GROUP *)inchi_calloc(num_atoms/2, sizeof(c_group_info->c_group[0])); c_group_info->c_candidate = (C_CANDIDATE*)inchi_calloc(num_atoms, sizeof(c_group_info->c_candidate[0])); if (c_group_info->c_group && c_group_info->c_candidate) { c_group_info->max_num_c_groups = num_atoms/2; c_group_info->max_num_candidates = num_atoms; } else { bError = BNS_OUT_OF_RAM; /* error: out of RAM */ /*printf("BNS_OUT_OF_RAM-1: num_at=%d, c_gr=%lx c_can=%lx\n", num_atoms, c_group_info->c_group, c_group_info->c_candidate);*/ goto exit_function; } } if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { if ( t_group_info ) { /* salt groups memory allocation */ s_group_info->s_candidate = (S_CANDIDATE*)inchi_calloc(num_atoms, sizeof(s_group_info->s_candidate[0])); if (s_group_info->s_candidate) { s_group_info->max_num_candidates = num_atoms; } else { bError = BNS_OUT_OF_RAM; /* error: out of RAM */ /*printf("BNS_OUT_OF_RAM-2\n");*/ goto exit_function; } } } if ( t_group_info ) { if ( t_group_info->tGroupNumber ) inchi_free( t_group_info->tGroupNumber ); t_group_info->tGroupNumber = (AT_NUMB *)inchi_calloc( 2*num_atoms+1, sizeof(t_group_info->tGroupNumber[0]) ); if ( !t_group_info->tGroupNumber ) { /*printf("BNS_OUT_OF_RAM-9\n");*/ bError = BNS_OUT_OF_RAM; /* error: out of RAM */ goto exit_function; } num = t_group_info->tni.nNumRemovedExplicitH; memset ( &t_group_info->tni, 0, sizeof(t_group_info->tni) ); t_group_info->tni.nNumRemovedExplicitH = num; } /* again: */ /* allocate Balanced Network Data Strucures; replace Alternating bonds with Single */ if ( (pBNS = AllocateAndInitBnStruct( at, num_atoms, BNS_ADD_ATOMS, BNS_ADD_EDGES, max_altp, &num_changed_bonds )) && (pBD = AllocateAndInitBnData( pBNS->max_vertices )) ) { pBNS->pbTautFlags = pbTautFlags; /* carry through all functions */ pBNS->pbTautFlagsDone = pbTautFlagsDone; /* carry through all functions */ #if ( BNS_PROTECT_FROM_TAUT == 1 ) /* protect bonds to acetyl and nitro */ SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK ); #endif /* set bonds in case of input "aromatic" bonds or multiple radicals */ #ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ if ( n_arom_radicals ) { ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); if ( stored_radicals ) { for ( i = 0; i < num_atoms; i ++ ) { if ( stored_radicals[i] ) { at[i].radical = stored_radicals[i]; at[i].num_H--; } } } if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } } #endif ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); /* (here pair(s) of radicals could have disappeared from the atoms) */ if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } pBNS->tot_st_flow += 2*ret; /*return 0;*/ /* debug */ nOrigDelta = ret; if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { /* has radical */ bChangeFlow |= BNS_EF_SET_NOSTEREO; } /******************************************************************** * Remove protons from NH(+), but not PH(+) * Add protons to COO(-) etc. * or remove protons from COOH etc to make the organic part neutral * Note: for now (-) from N(-) can be only canceled or moved to -C=O ********************************************************************/ if ( ( *pbTautFlags & TG_FLAG_VARIABLE_PROTONS ) && t_group_info && mark_at_type( at, num_atoms, nAtTypeTotals ) && nAtTypeTotals[ATTOT_NUM_CHARGES] ) { /* the structure is simple to neutralize if it yields exactly num[H(+)] = num[N,P H(+)] num[N,S,O(-)] = num[=C-O(-)] + num[C-S(-)] + num[N(-)] + num[other O(-), S(-)] and n(p) = num[H(+)] - num[N,S,O(-)] (no protons, no negative N,O,S condition) Additional check is needed if: min{num[N,PH], num[N,P(+), not onium]} > 0 => possibility to yield more H(+) min_charge = orig_charge(P,N,O,S) - n(p) - n(OH,SH) max_charge = orig_charge(P,N,O,S) - n(p) + n(O,S,N(-)) */ nNumOrigTotAtoms = t_group_info->tni.nNumRemovedExplicitH + num_atoms; pAATG->nAtTypeTotals = nAtTypeTotals; pAATG->t_group_info = t_group_info; #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; #endif /***********************************************************/ /* */ /* ( D E ) P R O T O N A T I O N */ /* */ /***********************************************************/ ret = RemoveNPProtonsAndAcidCharges( at, num_atoms, pAATG, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( t_group_info->tni.bNormalizationFlags ) { SetInitCapFlowToCurrent( pBNS ); if ( at_fixed_bonds_out ) { /* copy modified initial tautomeric structure for displaying Warning: implicit H counts in at_fixed_bonds_out include explicit Hs */ memcpy( at_fixed_bonds_out, at, nNumOrigTotAtoms*sizeof(at_fixed_bonds_out[0]) ); /* -- will be done in FillOutInputInfAtom() -- RemoveExcessiveImplicitH( num_atoms, t_group_info->tni.nNumRemovedExplicitH, at_fixed_bonds_out ); */ } } } /****************** initial bonds normalization ***************/ if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { /******************* find moveable positive charges **********************/ do { /* cycling while ret>0 added 2004-06-04 */ #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); #endif ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( ret ) { nChanges += ret; ret2 = AddCGroups2BnStruct( pBNS, at, num_atoms, c_group_info ); if ( IS_BNS_ERROR( ret2 ) ) { bError = ret2; goto exit_function; } *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; } } while ( ret > 0 ); #if ( BNS_RAD_SEARCH == 1 ) #else /* moveable charges may allow to cancel radicals -- check it */ if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( ret > 0 ) { /* pBNS->tot_st_flow += 2*ret; ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } */ bError = BNS_RADICAL_ERR; goto exit_function; } } #endif } /************************************************************************/ /******** test bonds for bond tautomerism **************/ /******** replace moveable bonds with "alternating" bonds **************/ /************************************************************************/ ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } nChanges += ret; /*********************** end of initial bonds normalization *************/ nTotChanges = 0; /* check for tautomerism */ /* find new tautomer groups */ salt_pass = 0; salt_step = 0; salt_found = 0; /*************************************************************/ /* */ /* M A I N C Y C L E B E G I N */ /* */ /*************************************************************/ do { nTotChanges += nChanges; nChanges = 0; taut_pass = 0; /**************** regular bond/H/(-)/positive charges tautomerism cycle begin **************/ do { taut_pass ++; for ( taut_found = 0; 0 < (ret=MarkTautomerGroups( at, num_atoms, t_group_info, c_group_info, pBNS, pBD )); taut_found ++ ) ; if ( ret < 0 ) { bError = ret; } if ( taut_found && !salt_pass ) { *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__ATOMS_DONE; } if ( taut_found || salt_found ) { /****************** repeat bonds normalization ***************/ ret = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } #if ( BNS_RAD_SEARCH == 1 ) #else /* discovered moveable charges and H-atoms may allow to cancel radicals */ if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } if ( ret > 0 ) { /* pBNS->tot_st_flow += 2*ret; ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } */ bError = BNS_RADICAL_ERR; goto exit_function; } } #endif /****************** update bonds normalization ***************/ if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { /******************* find moveable charges ***************/ do { /* cycling while ret>0 added 2004-06-04 */ #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); #endif ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } nChanges+= ret; if ( ret > 0 ) { ret2 = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); if ( IS_BNS_ERROR( ret2 ) ) { bError = ret2; goto exit_function; } *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; } } while ( ret > 0 ); } /************************************************************************/ /******** find moveable bonds: **************/ /******** test bonds for bond tautomerism **************/ /******** replace moveable bonds with "alternating" bonds **************/ /************************************************************************/ ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; goto exit_function; } nChanges+= ret; /****************** end of update bonds normalization ***************/ } salt_found = 0; } while( taut_found && !bError ); /**************** regular bond/H/(-)/positive charges tautomerism cycle end **************/ if ( bError ) { break; } /******************* 'salt' tautomerism permitted *************************/ if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { if ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) { /*********** requested one or more "salt" attachement migrartion test ********/ if ( !nChanges && salt_pass && salt_step ) { break; /* done */ } if ( !salt_step ) { /* salt step 0: process one attachment migrartion */ #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); #endif salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, t_group_info, c_group_info, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( salt_found < 0 ) { bError = salt_found; break; } else if ( salt_found > 0 ) { *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; } salt_step = !salt_found; /* if new 'salt' atoms have been found then repeat regular taut. search * MarkTautomerGroups() and do not perform salt step 1 * if new 'salt' atoms have NOT been found then switch to salt step 1 * and never repeat salt step 0 for the current structure */ } if ( salt_step /*|| (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)*/ ) { /* salt step 1: process more than one attachment migration */ #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); #endif salt_found = MarkSaltChargeGroups2 ( at, num_atoms, s_group_info, t_group_info, c_group_info, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( salt_found < 0 ) { bError = salt_found; break; } else if ( salt_found == 1 || salt_found == 5 ) { *pbTautFlagsDone |= TG_FLAG_TEST_TAUT2_SALTS_DONE; if ( salt_found == 5 ) { *pbTautFlagsDone |= TG_FLAG_TEST_TAUT3_SALTS_DONE; } /* salt_found == 2 => only negative charges involved */ } } salt_pass ++; } else { /* !( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ /*************** requested only one attachement migration test **********/ if ( !nChanges && salt_pass ) { /* one attachment migration */ break; } /* salt step 0: process one attachment migration */ #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); #endif salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, t_group_info, c_group_info, pBNS, pBD ); #if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; #endif if ( salt_found < 0 ) { bError = salt_found; break; } else if ( salt_found > 0 ) { *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; } salt_pass ++; } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) */ } while ( salt_found && !bError ); /*************************************************************/ /* */ /* M A I N C Y C L E E N D */ /* */ /*************************************************************/ if ( *pbTautFlags & TG_FLAG_MERGE_TAUT_SALTS ) { if ( !bError && s_group_info /*&& s_group_info->num_candidates > 0*/ ) { ret = MergeSaltTautGroups( at, num_atoms, s_group_info, t_group_info, c_group_info, pBNS ); if ( ret < 0 ) { bError = ret; } else if ( ret > 0 ) { *pbTautFlagsDone |= TG_FLAG_MERGE_TAUT_SALTS_DONE; } } } if ( !bError && t_group_info && (t_group_info->bTautFlags & TG_FLAG_VARIABLE_PROTONS) && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE|TG_FLAG_FOUND_ISOTOPIC_H_DONE)) ) { ret = MakeIsotopicHGroup( at, num_atoms, s_group_info, t_group_info ); if ( ret < 0 ) { bError = ret; } } /* success */ remove_alt_bond_marks( at, num_atoms); /************************************************ * Temporarily ignore all non-alternating bonds * and mark non-ring alt bonds non-stereogenic ************************************************/ ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 ); MarkRingSystemsAltBns( pBNS, 0 ); MarkNonStereoAltBns( pBNS, at, num_atoms, 0 ); #if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */ /* stereobonds bonds that lost stereo get "Either" stereo_type */ ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 ); MarkRingSystemsAltBns( pBNS, 1 ); MarkNonStereoAltBns( pBNS, at, num_atoms, 1 ); #endif } else { bError = BNS_OUT_OF_RAM; /*printf("BNS_OUT_OF_RAM-3\n");*/ } exit_function: pBNS = DeAllocateBnStruct( pBNS ); pBD = DeAllocateBnData( pBD ); /*#if ( MOVE_CHARGES == 1 )*/ if ( c_group_info ) { if ( c_group_info->c_group ) { inchi_free( c_group_info->c_group ); } if ( c_group_info->c_candidate ) { inchi_free( c_group_info->c_candidate ); } } /*#endif*/ if ( s_group_info && s_group_info->s_candidate ) { inchi_free( s_group_info->s_candidate ); } if ( pAATG && pAATG->nMarkedAtom ) { inchi_free( pAATG->nMarkedAtom ); qzfree( pAATG->nEndPoint ); /*qzfree( pAATG->nAtTypeTotals );*/ /* nAtTypeTotals is a stack array */ } if ( t_group_info && t_group_info->tGroupNumber ) { inchi_free( t_group_info->tGroupNumber ); t_group_info->tGroupNumber = NULL; } if ( !bError && num_atoms == 1 && at[0].at_type == ATT_PROTON && t_group_info && !t_group_info->tni.nNumRemovedExplicitH ) { /* remove single isolated proton */ t_group_info->tni.nNumRemovedProtons = 1; t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_SINGLE_REMOVED; if ( at[0].iso_atw_diff ) { t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff-1] ++; } if ( at_fixed_bonds_out ) { memcpy( at_fixed_bonds_out, at, num_atoms*sizeof(at_fixed_bonds_out[0]) ); } /*num_atoms --;*/ } /* Additional currently unused info: nOrigDelta > 0: original structure has been changed due to fiund augmenting path(s) nChanges > 0: either alt. bonds or taut. groups have been found */ #ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ if ( stored_radicals ) inchi_free( stored_radicals ); #endif return bError? bError : num_atoms; /* ret = 0 => success, any other => error */ } /*********************************************************************************/ int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ) { BNS_EDGE *pEdge = pBNS->edge + iedge; int nMaxFlow = (pEdge->cap & EDGE_FLOW_MASK); /* edge cap */ if ( nMaxFlow > MAX_BOND_EDGE_CAP ) { nMaxFlow = MAX_BOND_EDGE_CAP; } return nMaxFlow; } /*********************************************************************************/ int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ) { BNS_EDGE *pEdge = pBNS->edge + iedge; int nCurFlow = (pEdge->flow & EDGE_FLOW_MASK); /* edge flow */ return nCurFlow; } /*********************************************************************************/ int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ) { BNS_EDGE *pEdge = pBNS->edge + iedge; Vertex v1 = pEdge->neighbor1; Vertex v2 = v1 ^ pEdge->neighbor12; int f12 = (pEdge->flow & EDGE_FLOW_MASK); int rescap1, rescap2, rescap12, i, iedge_i; if ( f12 > 0 ) { for ( i = 0, rescap1 = 0; i < pBNS->vert[v1].num_adj_edges; i ++ ) { iedge_i = pBNS->vert[v1].iedge[i]; if ( iedge_i == iedge ) continue; rescap1 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); } for ( i = 0, rescap2 = 0; i < pBNS->vert[v2].num_adj_edges; i ++ ) { iedge_i = pBNS->vert[v2].iedge[i]; if ( iedge_i == iedge ) continue; rescap2 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); } rescap12 = inchi_min( rescap1, rescap2 ); rescap12 = inchi_min( rescap12, f12 ); return f12-rescap12; } return 0; } /**********************************************************************************/ int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) { int ifcd, iedge, new_flow, ret_val, nChanges = 0, bError=0; int bChangeFlow; Vertex v1, v2; int ineigh1, ineigh2; BNS_EDGE *pEdge; bChangeFlow0 &= ~BNS_EF_CHNG_RSTR; /* do not change pEdge flow in SetBondType */ if ( !bChangeFlow0 ) return 0; bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); /* find the next to the last changed */ if ( bChangeFlow0 & BNS_EF_SET_NOSTEREO ) { for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) { iedge = fcd[ifcd].iedge; pEdge = pBNS->edge + iedge; if ( !pEdge->pass ) { continue; } if ( !ifcd && nTestFlow>=0 ) { new_flow = nTestFlow; } else { new_flow = (int)pEdge->flow; } v1 = pEdge->neighbor1; v2 = pEdge->neighbor12 ^ v1; if ( v1 < num_atoms && v2 < num_atoms && new_flow != pEdge->flow0 ) { if ( (pBNS->vert[v1].st_edge.cap0 == pBNS->vert[v1].st_edge.flow0) != (pBNS->vert[v1].st_edge.cap == pBNS->vert[v1].st_edge.flow ) || (pBNS->vert[v2].st_edge.cap0 == pBNS->vert[v2].st_edge.flow0) != (pBNS->vert[v2].st_edge.cap == pBNS->vert[v2].st_edge.flow )) { bChangeFlow |= BNS_EF_SET_NOSTEREO; nChanges |= BNS_EF_SET_NOSTEREO; } } } } else { for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) ; } /* restore in reversed order to correctly handle vertex changed more than once */ for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { iedge = fcd[ifcd].iedge; pEdge = pBNS->edge + iedge; if ( !pEdge->pass ) { continue; } if ( !ifcd && nTestFlow>=0 ) { new_flow = nTestFlow; } else { new_flow = (int)pEdge->flow; } v1 = pEdge->neighbor1; v2 = pEdge->neighbor12 ^ v1; if ( v1 < num_atoms && v2 < num_atoms && bChangeFlow && new_flow != pEdge->flow0 ) { ineigh1 = pEdge->neigh_ord[0]; ineigh2 = pEdge->neigh_ord[1]; ret_val = SetAtomBondType( pEdge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], new_flow-pEdge->flow0, bChangeFlow ); if ( !IS_BNS_ERROR( ret_val ) ) { nChanges |= (ret_val > 0); } else { bError = ret_val; } } pEdge->pass = 0; } return bError? bError : nChanges; } /**********************************************************************************/ int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ) { int ifcd, iedge; Vertex v1, v2; BNS_EDGE *pEdge; /* find the next to the last changed */ for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) ; /* restore in reversed order to correctly handle vertex changed more than once */ for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { /* restore edge flow & cap */ iedge = fcd[ifcd].iedge; pEdge = pBNS->edge + iedge; pEdge->flow = fcd[ifcd].flow; pEdge->cap = fcd[ifcd].cap; pEdge->pass = 0; /* restore st-flow, cap */ if ( NO_VERTEX != (v1 = fcd[ifcd].v1) ) { pBNS->vert[v1].st_edge.flow = fcd[ifcd].flow_st1; pBNS->vert[v1].st_edge.cap = fcd[ifcd].cap_st1; pBNS->vert[v1].st_edge.pass = 0; } if ( NO_VERTEX != (v2 = fcd[ifcd].v2) ) { pBNS->vert[v2].st_edge.flow = fcd[ifcd].flow_st2; pBNS->vert[v2].st_edge.cap = fcd[ifcd].cap_st2; pBNS->vert[v2].st_edge.pass = 0; } } return 0; } /**********************************************************************************/ int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ) { BNS_EDGE *pEdge = pBNS->edge + iedge; int f12 = (pEdge->flow & EDGE_FLOW_MASK); /* the original flow */ int ifcd = 0; int nDots = 0; int i, iedge_i; fcd[ifcd].iedge = NO_VERTEX; if ( f12 < flow ) { /* Increase edge flow: Grab flow from the neighbors and delete it: set flow12=cap12 = 0 */ /************************************************************************************/ /* For example, simulate a new fixed double bond in place of a single bond and */ /* creates ONE or NONE (in case of a radical on adjacent atom) augmenting paths and */ /* makes it impossible for the BNS to set same flow as it originally was */ /************************************************************************************/ Vertex v1 = pEdge->neighbor1; Vertex v2 = v1 ^ pEdge->neighbor12; Vertex v_i; /* neighbor of v1 or v2 */ BNS_EDGE *pEdge_i; int delta1, delta2, f, st_edge_rescap; if ( (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { return BNS_CANT_SET_BOND; } if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 ) { return BNS_CAP_FLOW_ERR; } fcd[ifcd].iedge = iedge; fcd[ifcd].flow = pEdge->flow; fcd[ifcd].cap = pEdge->cap; fcd[ifcd].v1 = v1; fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; fcd[ifcd].v2 = v2; fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ pEdge->pass |= 64; delta1 = delta2 = flow - f12; if ( f12 > 0 ) { /* remove old edge flow from the flow and cap of the adjacent vertices' st-edges */ pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); /* delete current edge flow and capacity */ pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); } pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); /* grab the adjacent vertex1 radical (st_edge_rescap) if it exists */ st_edge_rescap = (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK); while ( st_edge_rescap && delta1 ) { st_edge_rescap --; /* grab the radical */ delta1 --; pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); nDots --; } /* grab the adjacent vertex2 radical (st_edge_rescap) if it exists */ st_edge_rescap = (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK); while ( st_edge_rescap && delta2 ) { st_edge_rescap --; /* grab the radical */ delta2 --; pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); nDots --; } /* grab flows from v1 neighbors */ for ( i = 0; delta1 && i < pBNS->vert[v1].num_adj_edges; i ++ ) { iedge_i = pBNS->vert[v1].iedge[i]; if ( iedge_i == iedge ) continue; pEdge_i = pBNS->edge + iedge_i; if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) continue; f = (pEdge_i->flow & EDGE_FLOW_MASK); if ( f ) { v_i = pEdge_i->neighbor12 ^ v1; fcd[ifcd].iedge = iedge_i; fcd[ifcd].flow = pEdge_i->flow; fcd[ifcd].cap = pEdge_i->cap; fcd[ifcd].v1 = v_i; fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; fcd[ifcd].v2 = NO_VERTEX; fcd[ifcd].flow_st2 = 0; fcd[ifcd].cap_st2 = 0; fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ pEdge_i->pass |= 64; while ( f && delta1 ) { f --; delta1 --; pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); /* next 2 lines added 01-22-2002 */ pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); nDots ++; } } } /* grab flows from v2 neighbors */ for ( i = 0; delta2 && i < pBNS->vert[v2].num_adj_edges; i ++ ) { iedge_i = pBNS->vert[v2].iedge[i]; if ( iedge_i == iedge ) continue; pEdge_i = pBNS->edge + iedge_i; if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) continue; f = (pEdge_i->flow & EDGE_FLOW_MASK); if ( f ) { v_i = pEdge_i->neighbor12 ^ v2; fcd[ifcd].iedge = iedge_i; fcd[ifcd].flow = pEdge_i->flow; fcd[ifcd].cap = pEdge_i->cap; fcd[ifcd].v1 = v_i; fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; fcd[ifcd].v2 = NO_VERTEX; fcd[ifcd].flow_st2 = 0; fcd[ifcd].cap_st2 = 0; fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ pEdge_i->pass |= 64; while ( f && delta2 ) { f --; delta2 --; pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); /* next 2 lines added 01-22-2002 */ pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); nDots ++; } } } if ( delta1 || delta2 ) { return BNS_CANT_SET_BOND; } } if ( f12 >= flow ) { /* Decrease edge flow: Redirect flow to the neighbors and delete it on the edge: set flow12=cap12 = 0 */ /* f12==flow fixes flow through the edge so that BNS cannot change it */ /**********************************************************************************************/ /* For example, simulate a removal of a double bond and create ONE or NONE augmenting path */ /* Make it impossible for BNS to set same flow as it originally was */ /**********************************************************************************************/ Vertex v1 = pEdge->neighbor1; Vertex v2 = (v1 ^ pEdge->neighbor12); int delta; /* if NOT (st-cap >= st-flow >= f12 >= flow) then error in the BN structure */ if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { return BNS_CAP_FLOW_ERR; } fcd[ifcd].iedge = iedge; fcd[ifcd].flow = pEdge->flow; fcd[ifcd].cap = pEdge->cap; fcd[ifcd].v1 = v1; fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; fcd[ifcd].v2 = v2; fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ pEdge->pass |= 64; delta = f12 - flow; /* remove current edge flow from st-edges */ /* -- seem to be a bug -- pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); */ /* replacement to the above 2 lines 01-16-2002 */ /* remove old edge flow from the flow of the adjacent vertices' st-edges */ pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); /* added 01-16-2002: reduce st-cap if new flow > 0 */ /* remove new edge flow from the cap of the adjacent vertices' st-edges */ pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); /* delete current edge flow and capacity */ pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); nDots = 2*delta; } return nDots; } /**********************************************************************************/ /* Connect new (fictitious, temporary) vertex to to nVertDoubleBond by a new edge */ /* Add radical (set st-cap=1) to the new vertex, set cap=1 to the new edge */ /* Add radical (set st-cap=1) to nVertSingleBond */ /* Find augmenting path connecting new vertex to nVertSingleBond */ /* This corresponds to moving H-atom from nVertSingleBond to nVertDoubleBond */ /**********************************************************************************/ int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ) { Vertex vlast = pBNS->num_vertices - 1; Vertex vnew = pBNS->num_vertices; Vertex v2 = nVertDoubleBond; BNS_VERTEX *pVert2 = pBNS->vert + v2; /* pointer to an old vertex */ BNS_VERTEX *pNewVert = pBNS->vert + vnew; /* pointer to a new vertex */ EdgeIndex iedge = pBNS->num_edges; BNS_EDGE *pEdge = pBNS->edge + iedge; /* pointer to a new edge */ if ( iedge >= pBNS->max_edges || vnew >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; /* edges or vertices overflow */ } if ( (pBNS->vert[vlast].iedge - pBNS->iedge) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges ) { return BNS_VERT_EDGE_OVFL; /* iedges overflow */ } if ( pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0 ) { return BNS_VERT_EDGE_OVFL; /* neighbors overflow */ } /* fill out the new edge, set its cap and flow, connect */ /* memset( pEdge, 0, sizeof(*pEdge) ); */ pEdge->cap = pEdge->cap0 = nCap; pEdge->flow = pEdge->flow0 = nFlow; pEdge->pass = 0; pEdge->neighbor1 = v2; pEdge->neighbor12 = v2 ^ vnew; pEdge->forbidden = 0; /* fill out the new vertex */ /* memset( pNewVert, 0, sizeof(*pNewVert) ); */ pNewVert->max_adj_edges = nMaxAdjEdges; pNewVert->num_adj_edges = 0; pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap; pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow; pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */ pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges; pNewVert->type = BNS_VERT_TYPE_TEMP; *nDots += nCap - nFlow; pEdge->neigh_ord[v2>vnew] = pVert2->num_adj_edges; pEdge->neigh_ord[v2num_adj_edges; /* connect new edge to v2 */ pVert2->iedge[pVert2->num_adj_edges ++] = iedge; /* connect new edge to vnew */ pNewVert->iedge[pNewVert->num_adj_edges ++] = iedge; /* fix v2 flow and cap */ *nDots -= (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; pVert2->st_edge.flow += nFlow; if ( pVert2->st_edge.cap < pVert2->st_edge.flow ) { pVert2->st_edge.cap = pVert2->st_edge.flow; } *nDots += (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; pBNS->num_edges ++; pBNS->num_vertices ++; return vnew; } /*****************************************************************************************************/ int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ) { int ip1 = p1 - pBNS->vert; int ip2 = p2 - pBNS->vert; int ie = pBNS->num_edges; BNS_EDGE *e = pBNS->edge + ie; /* debug: check bounds */ if ( ip1 >= pBNS->max_vertices || ip1 < 0 || ip2 >= pBNS->max_vertices || ip2 < 0 || ie >= pBNS->max_edges || ie < 0 || (p1->iedge - pBNS->iedge) < 0 || (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || (p2->iedge - pBNS->iedge) < 0 || (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || p1->num_adj_edges >= p1->max_adj_edges || p2->num_adj_edges >= p2->max_adj_edges ) { return BNS_VERT_EDGE_OVFL; } /* clear the edge */ memset( e, 0, sizeof(*e) ); /* connect */ e->neighbor1 = inchi_min( ip1, ip2 ); e->neighbor12 = ip1 ^ ip2; p1->iedge[p1->num_adj_edges] = ie; p2->iedge[p2->num_adj_edges] = ie; e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; e->cap = e->cap0 = nEdgeCap; e->flow = e->flow0 = nEdgeFlow; p1->st_edge.flow += nEdgeFlow; p2->st_edge.flow += nEdgeFlow; if ( p1->st_edge.cap < p1->st_edge.flow ) { p1->st_edge.cap = p1->st_edge.flow; } if ( p2->st_edge.cap < p2->st_edge.flow ) { p2->st_edge.cap = p2->st_edge.flow; } pBNS->num_edges ++; return ie; } /**********************************************************************************/ BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) { if ( v1 < pBNS->num_atoms ) { Vertex v2; BNS_EDGE *pEdge1; BNS_VERTEX *pVert1 = pBNS->vert+v1; int i = pVert1->num_adj_edges-1; while( 0 <= i ) { pEdge1 = pBNS->edge + pVert1->iedge[i]; v2 = pEdge1->neighbor12 ^ v1; if ( pBNS->vert[v2].type == type ) { return IS_FORBIDDEN(pEdge1->forbidden, pBNS)? NO_VERTEX : pVert1->iedge[i]; } i --; } return NO_VERTEX; /* not found t-group */ } else if ( v1 < pBNS->num_vertices ) { return NO_VERTEX; } return BNS_VERT_EDGE_OVFL; } /**********************************************************************************/ Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) { if ( v1 < pBNS->num_atoms ) { Vertex v2; BNS_EDGE *pEdge1; BNS_VERTEX *pVert1 = pBNS->vert+v1; int i = pVert1->num_adj_edges-1; AT_NUMB type2; if ( type == BNS_VERT_TYPE_ENDPOINT ) type2 = BNS_VERT_TYPE_TGROUP; else if ( type == BNS_VERT_TYPE_C_POINT ) type2 = BNS_VERT_TYPE_C_GROUP; else type2 = 0; if ( (pVert1->type & type) == type ) { while( 0 <= i ) { pEdge1 = pBNS->edge + pVert1->iedge[i]; v2 = pEdge1->neighbor12 ^ v1; if ( pBNS->vert[v2].type == type2 ) { if ( IS_FORBIDDEN(pEdge1->forbidden, pBNS) ) { return NO_VERTEX; } return v2; } i --; } } return BNS_BOND_ERR; /* not found t-group */ } else if ( v1 < pBNS->num_vertices ) { return NO_VERTEX; } return BNS_VERT_EDGE_OVFL; } /**********************************************************************************/ int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ) { BNS_VERTEX *pVert1 = pBNS->vert + v1; BNS_VERTEX *pVert; BNS_EDGE *pEdge; Vertex v; int i, n; VertexFlow nNewCap; /* Change v1: increment its st-cap */ n = 0; nOldCapVertSingleBond[n++] = pVert1->st_edge.cap; /*if ( pVert1->st_edge.cap == pVert1->st_edge.flow ) {*/ pVert1->st_edge.cap ++; *nDots += 1; /*}*/ /* increment caps of adjacent edges if (1) the neighbor has st-cap != 0 and (2) (edge cap==0) OR (nSumEdgeCap < pVert1->st_edge.cap && pVert->st_edge.flow > pVert1->st_edge.cap) */ if ( !(pVert1->type & BNS_VERT_TYPE_ANY_GROUP) ) { /* AT_NUMB nSumEdgeCap = 0; for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { pEdge = pBNS->edge + pVert1->iedge[i]; nSumEdgeCap += pEdge->cap; } */ /* do not increment caps of t-group or c-group edges */ for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { pEdge = pBNS->edge + pVert1->iedge[i]; nOldCapVertSingleBond[n++] = pEdge->cap; /* save edge cap */ v = pEdge->neighbor12 ^ v1; if ( v == v2 && !bAdjacentDonors ) { continue; } pVert = pBNS->vert + v; if ( pVert->type & BNS_VERT_TYPE_ANY_GROUP ) continue; nNewCap = inchi_min(pVert->st_edge.cap, pVert1->st_edge.cap); nNewCap = inchi_min(nNewCap, MAX_BOND_EDGE_CAP); pEdge->cap = nNewCap; /* change edge cap */ /* if ( pVert->st_edge.cap > 0 && !pEdge->cap ) { pEdge->cap ++; } else if ( pVert->st_edge.flow > pVert1->st_edge.cap && pEdge->cap < MAX_BOND_EDGE_CAP && nSumEdgeCap < pVert1->st_edge.cap ) { pEdge->cap ++; } */ } } return n; /* number of elements in nOldCapVertSingleBond[*] */ } /**********************************************************************************/ #define BNS_CHK_ALTP_NO_ALTPATH 0 #define BNS_CHK_ALTP_SAME_TGROUP 1 #define BNS_CHK_ALTP_SAME_VERTEX 2 #define BNS_CHK_ALTP_SET_SUCCESS 4 /**********************************************************************************/ int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ) { if ( !pBNS->vert[nVertDoubleBond].st_edge.flow && !( path_type == ALT_PATH_MODE_REM2H_CHG || path_type == ALT_PATH_MODE_ADD2H_CHG || path_type == ALT_PATH_MODE_REM2H_TST || path_type == ALT_PATH_MODE_ADD2H_TST ) ) { return BNS_CHK_ALTP_NO_ALTPATH; } else { Vertex vNew; Vertex v1 = nVertSingleBond; Vertex v2 = nVertDoubleBond; BNS_VERTEX *pVert1 = pBNS->vert + v1; BNS_VERTEX *pVert2 = pBNS->vert + v2; int n, bAdjacentDonors = 0; int ifcd = 0; Vertex t1=NO_VERTEX; Vertex t2=NO_VERTEX; int iapc; /*#if ( TEST_REMOVE_S_ATOMS == 1 )*/ /* && ALT_PATH_MODE_4_SALT == path_type */ if ( ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) && ALT_PATH_MODE_4_SALT2 == path_type && (BNS_VERT_TYPE_ENDPOINT & type) ) { /* --------------------------------------------------------- \ action | DB action (v2) | SB action (v1) | vertex \ | accept H @ vertex | donate H @ vertex | type \ | nVertDoubleBond | nVertSingleBond | ----------------+-------------------+-------------------+ -ZH (v1) | error | -ZH(.) | (cap>0 on edge | | increment | except v1-v2) | | st-cap on Z | ----------------+-------------------+-------------------+ =Z (v2) | =Z-(.) | error | (st-flow>0) | add fict vertex | | | with st-cap=1 | | ----------------+-------------------+-------------------+ endpoint | T(.) | T-(.) | of t-group | increment | add fict vertex | represented | st-cap on T | with st-cap=1 | by fictitious | | | vertex T | | | --------------------------------------------------------- */ int bSet_v1; /* indicator: v1 has been set */ int bSet_v2; /* indicator: v2 has been set */ int i; Vertex v1t = NO_VERTEX; Vertex v2t = NO_VERTEX; Vertex v1Act, v2Act; Vertex v; memset( apc, 0, sizeof(*apc) ); fcd[ifcd].iedge = NO_VERTEX; *nDots = 0; if ( v1 == v2 ) { return BNS_CHK_ALTP_SAME_VERTEX; } /* check whether v1 has neighbors adjacent to multiple bonds */ for ( i = 0, n = 0; i < pVert1->num_adj_edges; i ++ ) { v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ if ( v == v2 ) continue; /* ignore connection to v2 */ n += (pBNS->vert[v].st_edge.cap > 0); } if ( !n ) { return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ } v1Act = v1; v2Act = v2; /* find t-group that contains v1 */ if ( (pVert1->type & type) == type ) { v1t = GetGroupVertex(pBNS, v1, type); if ( IS_BNS_ERROR( v1t ) ) { return v1t; } if ( v1t != NO_VERTEX ) { v1Act = v1t; } } /* find t-group that contains v2 */ if ( (pVert2->type & type) == type ) { v2t = GetGroupVertex(pBNS, v2, type); if ( IS_BNS_ERROR( v2t ) ) { return v2t; } if ( v2t != NO_VERTEX ) { v2Act = v2t; } } if ( v1t != NO_VERTEX && v1t == v2t ) { return BNS_CHK_ALTP_SAME_TGROUP; } bSet_v1 = bSet_v2 = 0; /* create new edges adjacent to v1t or v2 */ iapc = 0; if ( v1t != NO_VERTEX ) { /* create new edge and vertex, connect to v1t */ vNew = bAddNewVertex( pBNS, v1t, 1, 0, 1, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; bSet_v1 = 1; iapc ++; } if ( v2t == NO_VERTEX ) { /* create new edge and vertex, connect to v2 */ vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; bSet_v2 = 1; iapc ++; } /* add st-cap to v1 and/or v2t */ iapc = 0; if ( !bSet_v1 ) { /* add st-cap to v1 */ if ( v1t != NO_VERTEX ) { return BNS_BOND_ERR; } n = bAddStCapToAVertex( pBNS, v1, v2Act, apc->nOldCapsVert[iapc], nDots, 0 ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v1; iapc ++; } if ( !bSet_v2 ) { /* add st-cap to v2t */ if ( v2t == NO_VERTEX ) { return BNS_BOND_ERR; } n = bAddStCapToAVertex( pBNS, v2t, v1Act, apc->nOldCapsVert[iapc], nDots, 0 ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v2t; iapc ++; } if ( *nDots < 0 || *nDots %2 ) { return BNS_SET_ALTP_ERR; } return BNS_CHK_ALTP_SET_SUCCESS; } /* ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ /*#endif*/ /* ( TEST_REMOVE_S_ATOMS == 1 && ALT_PATH_MODE_4_SALT == path_type ) */ /*****************************************************************/ if ( path_type == ALT_PATH_MODE_REM2H_CHG || path_type == ALT_PATH_MODE_ADD2H_CHG || path_type == ALT_PATH_MODE_REM2H_TST || path_type == ALT_PATH_MODE_ADD2H_TST ) { /* added 2004-03-18 */ int bDonors = (path_type == ALT_PATH_MODE_REM2H_CHG) || (path_type == ALT_PATH_MODE_REM2H_TST); int bSet_v1; /* indicator: v1 has been set */ int bSet_v2; /* indicator: v2 has been set */ int i, cap = 1; Vertex v1t = NO_VERTEX; Vertex v2t = NO_VERTEX; Vertex v1Act, v2Act; Vertex v; memset( apc, 0, sizeof(*apc) ); fcd[ifcd].iedge = NO_VERTEX; *nDots = 0; /* if ( v1 == v2 ) { return BNS_CHK_ALTP_SAME_VERTEX; } */ /* check whether v1 and v2 have proper neighbors */ for ( i = 0, n = bAdjacentDonors = 0; i < pVert1->num_adj_edges; i ++ ) { v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ /* do not ignore connection to v2 if ( v == v2 ) continue; */ n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert1->iedge[i])->flow > 0); bAdjacentDonors += bDonors ? (v == v2) && ((pBNS->edge + pVert1->iedge[i])->flow < MAX_BOND_EDGE_CAP) : 0; /* two donors connected by a single or double bond */ } if ( !n && !bAdjacentDonors ) { return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ } for ( i = 0, n = bAdjacentDonors = 0; i < pVert2->num_adj_edges; i ++ ) { v = (pBNS->edge + pVert2->iedge[i])->neighbor12 ^ v2; /* v is adjacent to v2 */ /* do not ignore connection to v1 if ( v == v1 ) continue; */ n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert2->iedge[i])->flow > 0); bAdjacentDonors += bDonors ? (v == v1) && ((pBNS->edge + pVert2->iedge[i])->flow < MAX_BOND_EDGE_CAP ) : 0; /* two donors connected by a single or double bond */ } if ( !n && !bAdjacentDonors ) { return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ } v1Act = v1; v2Act = v2; /* find t-group that contains v1 */ if ( (pVert1->type & type) == type ) { v1t = GetGroupVertex(pBNS, v1, type); if ( BNS_BOND_ERR == v1t ) { v1t = NO_VERTEX; } else if ( IS_BNS_ERROR( v1t ) ) { return v1t; } else if ( v1t != NO_VERTEX ) { v1Act = v1t; } } /* find t-group that contains v2 */ if ( (pVert2->type & type) == type ) { v2t = GetGroupVertex(pBNS, v2, type); if ( BNS_BOND_ERR == v2t ) { v2t = NO_VERTEX; } else if ( IS_BNS_ERROR( v2t ) ) { return v2t; } else if ( v2t != NO_VERTEX ) { v2Act = v2t; } } if ( v1t != NO_VERTEX && v1t == v2t ) { cap = 2; /* same t-group */ } /* bAddNewVertex: (bDonors != 0) == (vit != NO_VERTEX), i=1,2 */ bSet_v1 = bSet_v2 = 0; /* create new edges adjacent to v1t or v2 */ iapc = 0; if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { /* create new edge and vertex, connect to v1Act */ vNew = bAddNewVertex( pBNS, v1Act, cap, 0, 1, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; bSet_v1 = 1; iapc ++; } if ( (bDonors != 0) == (v2t != NO_VERTEX) && cap == 1 ) { /* create new edge and vertex, connect to v2Act; do not do it if cap==2 */ vNew = bAddNewVertex( pBNS, v2Act, cap, 0, 1, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; bSet_v2 = 1; iapc ++; } else if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { bSet_v2 = 1; } /* add st-cap to v1 and/or v2t */ iapc = 0; /* if cap=2 then just increment st_cap 2 times */ if ( !bSet_v1 ) { /* add st-cap to v1 */ if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { return BNS_BOND_ERR; } n = bAddStCapToAVertex( pBNS, v1Act, v2Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v1Act; iapc ++; } if ( !bSet_v2 ) { /* add st-cap to v2t */ if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { return BNS_BOND_ERR; } n = bAddStCapToAVertex( pBNS, v2Act, v1Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v2Act; iapc ++; } if ( *nDots < 0 || *nDots %2 ) { return BNS_SET_ALTP_ERR; } return BNS_CHK_ALTP_SET_SUCCESS; } /**************************************************************************/ if ( path_type == ALT_PATH_MODE_REM_PROTON ) { /* added 2004-03-05 */ if ( v1 >= 0 && v2 >= 0 && (pVert1->type & BNS_VERT_TYPE_ANY_GROUP) && (pVert2->type & BNS_VERT_TYPE_ANY_GROUP) ) { /* create new edge and vertex, connect to v2 */ if ( (pBNS->vert[v1].type & BNS_VERT_TYPE_C_GROUP) && (pBNS->vert[v1].st_edge.flow == 2*pBNS->vert[v1].num_adj_edges ) ) { /* so far in a charge group max edge flow = 1 2004-03-08 */ return BNS_CHK_ALTP_NO_ALTPATH; } memset( apc, 0, sizeof(*apc) ); fcd[ifcd].iedge = NO_VERTEX; *nDots = 0; iapc = 0; vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; /*iapc ++;*/ /* add st-cap (dot) to v1 */ n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[iapc], nDots, 0 ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v1; iapc ++; return BNS_CHK_ALTP_SET_SUCCESS; } } #if ( NEUTRALIZE_ENDPOINTS == 1 ) /* { */ *nDots = 0; memset( apc, 0, sizeof(*apc) ); fcd[ifcd].iedge = NO_VERTEX; if ( type & BNS_VERT_TYPE_ENDPOINT ) { BNS_IEDGE iedge; AT_NUMB type2; int ret2; /* prohibit charge movement */ type2 = BNS_VERT_TYPE_C_GROUP; iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); if (iedge != NO_VERTEX ) { /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge * when moving tautomeric H-atoms */ ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); if ( IS_BNS_ERROR(ret2) ) { return ret2; } *nDots += ret2; while ( fcd[ifcd].iedge != NO_VERTEX ) { ifcd ++; } } iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); if (iedge != NO_VERTEX ) { /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge * when moving tautomeric H-atoms */ ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); if ( IS_BNS_ERROR(ret2) ) { return ret2; } *nDots += ret2; while ( fcd[ifcd].iedge != NO_VERTEX ) { ifcd ++; } } /* set hydrogen counts */ type2 = BNS_VERT_TYPE_TGROUP; iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); if (iedge != NO_VERTEX ) { /* set flow=1 on an edge to a t-group vertex to make sure there is * a moveable hydrogen atom or (-) on v1 when moving tautomeric H-atoms */ #if ( FIX_H_CHECKING_TAUT == 1 ) ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); if ( IS_BNS_ERROR(ret2) ) { return ret2; } *nDots += ret2; while ( fcd[ifcd].iedge != NO_VERTEX ) { ifcd ++; } #else t1 = pBNS->edge[iedge].neighbor12 ^ v1; #endif } iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); if (iedge != NO_VERTEX ) { /* set flow=0 on an edge to a t-group vertex to make sure there is * no moveable hydrogen atom or (-) on v2 when moving tautomeric H-atoms */ #if ( FIX_H_CHECKING_TAUT == 1 ) ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 0, fcd+ifcd ); if ( IS_BNS_ERROR(ret2) ) { return ret2; } *nDots += ret2; while ( fcd[ifcd].iedge != NO_VERTEX ) { ifcd ++; } #else t2 = pBNS->edge[iedge].neighbor12 ^ v2; #endif } #if ( FIX_H_CHECKING_TAUT == 1 ) #else if ( t1 == t2 && t1 != NO_VERTEX ) { return BNS_CHK_ALTP_SAME_TGROUP; } #endif iapc = 0; /* create new edge and vertex with cap=1 at v2 and/or t1 */ if ( t1 != NO_VERTEX ) { /* create new edge and vertex, connect to t1 */ vNew = bAddNewVertex( pBNS, t1, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; iapc ++; } if ( t2 == NO_VERTEX ) { /* create new edge and vertex, connect to v2 */ vNew = bAddNewVertex( pBNS, v2, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[iapc] = vNew; apc->bSetNew[iapc] = 1; iapc ++; } /* add st-cap to v1 and/or v2t */ iapc = 0; if ( t1 == NO_VERTEX ) { /* add st-cap to v1 */ n = bAddStCapToAVertex( pBNS, v1, (Vertex)(t2 == NO_VERTEX? v2:t2), apc->nOldCapsVert[iapc], nDots, 0 ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = v1; iapc ++; } if ( t2 != NO_VERTEX ) { /* add st-cap to t2 */ n = bAddStCapToAVertex( pBNS, t2, (Vertex)(t1 == NO_VERTEX? v1:t1), apc->nOldCapsVert[iapc], nDots, 0 ); apc->bSetOldCapsVert[iapc] = n; apc->vOldVert[iapc] = t2; iapc ++; } } else { /* create new edge and vertex, connect to v2 */ vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[0] = vNew; apc->bSetNew[0] = 1; /* add st-cap to v1 */ n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots, 0 ); apc->bSetOldCapsVert[0] = n; apc->vOldVert[0] = v1; } #else /* } NEUTRALIZE_ENDPOINTS == 0 {*/ *nDots = 0; memset( apc, 0, sizeof(*apc) ); fcd[ifcd].iedge = NO_VERTEX; /* create new edge and vertex, connect to v2 */ vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots, 0 ); if ( IS_BNS_ERROR(vNew) ) { return vNew; } apc->vNewVertex[0] = vNew; apc->bSetNew[0] = 1; /* add st-cap to v1 */ n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots ); apc->bSetOldCapsVert[0] = n; apc->vOldVert[0] = v1; #endif /* } NEUTRALIZE_ENDPOINTS */ if ( *nDots < 0 || *nDots %2 ) { return BNS_SET_ALTP_ERR; } return BNS_CHK_ALTP_SET_SUCCESS; } /*return BNS_CHK_ALTP_NO_ALTPATH;*/ } /**********************************************************************************/ int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ) /* int nVertDoubleBond, int nVertSingleBond, int nNewVertex, AT_NUMB *nOldCapVertSingleBond */ { BNS_EDGE *pEdge; Vertex vNew; Vertex vOld; BNS_VERTEX *pOldVert; BNS_VERTEX *pNewVert; int i, j, n, ret; ret = 0; if ( bChangeFlow & BNS_EF_UPD_H_CHARGE ) { /* remove new temp. vertices and edges connectong them to the structure */ for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { if ( apc->bSetNew[i] ) { vNew = apc->vNewVertex[i]; pNewVert = pBNS->vert + vNew; for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { pEdge = pBNS->edge+pNewVert->iedge[j]; vOld = pEdge->neighbor12 ^ vNew; pOldVert = pBNS->vert + vOld; pOldVert->st_edge.flow -= pEdge->flow; pOldVert->st_edge.cap -= pEdge->flow; /* disconnect new edge from pOldVert */ pOldVert->iedge[--pOldVert->num_adj_edges] = 0; /* clear the new edge */ memset( pEdge, 0, sizeof(*pEdge) ); /* and decrement the total number of edges */ pBNS->num_edges --; } /* clear the new vertex */ memset( pNewVert, 0, sizeof( pNewVert ) ); /* and decrement the total number of vertices (new vertice ids are contiguous */ pBNS->num_vertices --; ret ++; } } /* Restore changed caps of old vertices */ for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { if ( n = apc->bSetOldCapsVert[i] ) { pOldVert = pBNS->vert + apc->vOldVert[i]; if ( pOldVert->st_edge.flow <= apc->nOldCapsVert[i][0] ) { pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; n --; ret ++; for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { pEdge = pBNS->edge + pOldVert->iedge[j]; pEdge->cap = apc->nOldCapsVert[i][j+1]; } } } } } else { /* Restore changed caps of old vertices */ for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { if ( n = apc->bSetOldCapsVert[i] ) { pOldVert = pBNS->vert + apc->vOldVert[i]; pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; n --; ret ++; for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { pEdge = pBNS->edge + pOldVert->iedge[j]; pEdge->cap = apc->nOldCapsVert[i][j+1]; } } } /* remove new temp. vertices and edges connectong them to the structure */ for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { if ( apc->bSetNew[i] ) { vNew = apc->vNewVertex[i]; pNewVert = pBNS->vert + vNew; for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { pEdge = pBNS->edge+pNewVert->iedge[j]; vOld = pEdge->neighbor12 ^ vNew; pOldVert = pBNS->vert + vOld; /* disconnect new edge from pOldVert */ pOldVert->iedge[--pOldVert->num_adj_edges] = 0; /* clear the new edge */ memset( pEdge, 0, sizeof(*pEdge) ); /* and decrement the total number of edges */ pBNS->num_edges --; } /* clear the new vertex */ memset( pNewVert, 0, sizeof( pNewVert ) ); /* and decrement the total number of vertices (new vertice ids are contiguous */ pBNS->num_vertices --; ret ++; } } } return 0; } /**********************************************************************************/ int bExistsAnyAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, int nVert2, int nVert1, int path_type ) { int nRet1, nRet2; nRet1 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert2, nVert1, path_type ); if ( nRet1 > 0 ) return nRet1; nRet2 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert1, nVert2, path_type ); if ( nRet2 > 0 ) return nRet2; if ( IS_BNS_ERROR( nRet1 ) ) return nRet1; if ( IS_BNS_ERROR( nRet2 ) ) return nRet2; return 0; } #define ALT_PATH_TAUTOM 1 #define ALT_PATH_CHARGE 2 #define ALT_PATH_4_SALT 3 /**********************************************************************************/ int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ) { int i, vt; BNS_VERTEX *pVert; /* vertices */ BNS_EDGE *pEdge; /* edges */ if ( 0 <= v && v < pBNS->num_atoms && (pVert = pBNS->vert+v) && (pVert->type & BNS_VERT_TYPE_ENDPOINT) ) { for ( i = pVert->num_adj_edges - 1; 0 <= i; i -- ) { pEdge = pBNS->edge + pVert->iedge[i]; vt = pEdge->neighbor12 ^ v; if ( pBNS->vert[vt].type & BNS_VERT_TYPE_TGROUP ) { return !IS_FORBIDDEN(pEdge->forbidden, pBNS); } } } return 0; } /**********************************************************************************/ #if ( BNS_RAD_SEARCH == 1 ) /**********************************************************************************/ int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ) { EdgeIndex iuv; Vertex v_O, v_ChgOrH; /* the previous atom along the path: should be a terminal atom */ if ( v_1 == NO_VERTEX ) { v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ); } v_O = v_1 / 2 - 1; if ( v_O < 0 || v_O >= pBNS->num_atoms ) { return 0; } /* make sure v_O is a terminal atom: its second neighbor is not an atom */ if ( pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM ) { return 0; } /* the next to previous vertex vertex along the path: should be a Charge or Taut group vertex */ if ( v_2 == NO_VERTEX ) { v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv ); } v_ChgOrH = v_2 / 2 - 1; if ( v_ChgOrH < pBNS->num_atoms ) { return 0; } /* make sure v_ChgOrH is a charge or taut_group */ if ( pBNS->vert[v_ChgOrH].type & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP) ) return 1; return 0; } /**********************************************************************************/ int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u) { EdgeIndex iuv; int i, num_found; Vertex v, w; Vertex u_last, v2; switch( pBD->bRadSrchMode ) { case RAD_SRCH_NORM: /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */ /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms */ num_found = 0; while ( u > Vertex_t && (u % 2 || u/2 > pBNS->num_atoms ) ) { u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); } w = u/2 - 1; /* Check whether u is a radical endpoint */ if ( Vertex_t < u && w < pBNS->num_atoms && pBNS->vert[w].st_edge.cap == (pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK) ) { /* u is an atom; it is not a radical atom */ /* now search for the starting radical atom by following the path back from u */ v = u_last = u; while( v > Vertex_t ) { u = v; v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */ } /* check whether u is a radical atom */ if ( !(u%2) && Vertex_t < u && (u = u/2 - 1) < pBNS->num_atoms && pBNS->vert[u].st_edge.cap > (pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK) ) { /* at pBNS->vert[u] we have found the radical that originated the path */ /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */ /* to which the radical may be moved (farthest reachable atom) */ /* add *all* atoms that may receive radical from u_rad */ /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */ for ( v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ) ) { if ( !(v%2) && (v2 = v/2 - 1) < pBNS->num_atoms && pBNS->vert[v2].st_edge.cap == (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) ) { /* check exception */ if ( pBNS->type_TACN && bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ) ) { continue; } /* add */ for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { /* check whether this pair, (u,w), has already been saved */ if ( u == pBD->RadEndpoints[i] && v2 == pBD->RadEndpoints[i+1] ) { break; } } if ( i >= pBD->nNumRadEndpoints ) { /* add new (u,w) pair */ if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { /* add */ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = v2; /* endpoint */ num_found ++; /*return 1;*/ /* registered */ } else { return BNS_VERT_EDGE_OVFL; } } } } if ( num_found ) { return 1; } } } break; case RAD_SRCH_FROM_FICT: /* find nearest atom accessible from a fictitious vertex */ /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ v = u; w = NO_VERTEX; /* the nearest atom -- radical-endpoint */ u = NO_VERTEX; /* fictitious vertex carrying a radical */ while ( v > Vertex_t ) { u = v; if ( !(v % 2) && v/2 <= pBNS->num_atoms && pBNS->vert[v/2-1].st_edge.cap - pBNS->vert[v/2-1].st_edge.flow < 2 ) { w = v; /* vertex w is atom that may be singlet or doublet but not triplet */ } v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); } v = u/2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */ if ( w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms || pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow || (w = w/2 - 1) >= pBNS->num_atoms ) { break; /* reject */ } u = v; /* at pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */ for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { if ( u == pBD->RadEndpoints[i] && w == pBD->RadEndpoints[i+1] ) { break; /* this pair has already been stored */ } } if ( i >= pBD->nNumRadEndpoints ) { /* a new pair has been found */ if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { /* add */ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = w; /* endpoint */ return 1; /* registered */ } else { return BNS_VERT_EDGE_OVFL; } } break; } return 0; /* rejected */ } /**********************************************************************************/ int cmp_rad_endpoints( const void *a1, const void *a2 ) { /* Vertex radical_vertex, radical_endpoint */ const Vertex *p1 = (const Vertex *)a1; const Vertex *p2 = (const Vertex *)a2; if ( p1[0] < p2[0] ) return -1; if ( p1[0] > p2[0] ) return 1; if ( p1[1] < p2[1] ) return -1; if ( p1[1] > p2[1] ) return 1; return 0; } /**********************************************************************************/ int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) { BNS_EDGE *e; EdgeIndex ie; BNS_VERTEX *p1, *p2; Vertex v1, v2; int i, delta, rad; for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { ie = pBD->RadEdges[i]; if ( ie < 0 || ie >= pBNS->num_edges ) { goto error_exit; } e = pBNS->edge + ie; v1 = e->neighbor1; v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ if ( ie + 1 != pBNS->num_edges || v1 < 0 || v1 >= pBNS->num_vertices || v2 < 0 || v2 >= pBNS->num_vertices ) { goto error_exit; } p1 = pBNS->vert + v1; p2 = pBNS->vert + v2; if ( p2->iedge[p2->num_adj_edges-1] != ie || p1->iedge[p1->num_adj_edges-1] != ie ) { goto error_exit; } p2->num_adj_edges --; p1->num_adj_edges --; p2->iedge[p2->num_adj_edges] = 0; p1->iedge[p1->num_adj_edges] = 0; p2->st_edge.flow -= e->flow; p1->st_edge.flow -= e->flow; if ( !p2->num_adj_edges && v2 >= pBNS->num_atoms ) { if ( v2+1 != pBNS->num_vertices ) { goto error_exit; } memset( p2, 0, sizeof(*p2) ); pBNS->num_vertices --; } if ( !p1->num_adj_edges && v1 >= pBNS->num_atoms ) { if ( v1+1 != pBNS->num_vertices ) { goto error_exit; } memset( p1, 0, sizeof(*p1) ); pBNS->num_vertices --; } if ( at && v1 < pBNS->num_atoms ) { delta = p1->st_edge.cap - p1->st_edge.flow; rad = at[v1].radical; switch( delta ) { case 0: if ( rad == RADICAL_DOUBLET ) rad = 0; break; case 1: if ( rad != RADICAL_DOUBLET ) rad = RADICAL_DOUBLET; } at[v1].radical = rad; } memset( e, 0, sizeof(*e) ); pBNS->num_edges --; } pBD->nNumRadEdges = 0; pBD->nNumRadicals = 0; pBD->bRadSrchMode = RAD_SRCH_NORM; return 0; error_exit: return BNS_PROGRAM_ERR; } /**********************************************************************************/ int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) { BNS_EDGE *e; EdgeIndex ie; BNS_VERTEX *p1, *p2; Vertex v1, v2; int i, delta, rad; int p1_num_adj_edges, p2_num_adj_edges; for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { ie = pBD->RadEdges[i]; if ( ie < 0 || ie >= pBNS->num_edges ) { goto error_exit; } e = pBNS->edge + ie; v1 = e->neighbor1; /* atom */ v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ if ( v1 < 0 || v1 >= pBNS->num_atoms || v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices ) { goto error_exit; } p1 = pBNS->vert + v1; p2 = pBNS->vert + v2; p1_num_adj_edges = e->neigh_ord[0]; p2_num_adj_edges = e->neigh_ord[1]; if ( p2->iedge[p2_num_adj_edges] != ie || p1->iedge[p1_num_adj_edges] != ie ) { goto error_exit; } if ( at && v1 < pBNS->num_atoms ) { delta = p1->st_edge.cap - p1->st_edge.flow + e->flow; rad = at[v1].radical; switch( delta ) { case 0: if ( rad == RADICAL_DOUBLET ) rad = 0; break; case 1: if ( rad != RADICAL_DOUBLET ) rad = RADICAL_DOUBLET; } at[v1].radical = rad; } } return 0; error_exit: return BNS_PROGRAM_ERR; } /**********************************************************************************/ int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) { int ret, i, j, k, num_new_edges, delta; BNS_VERTEX *pRad, *pEndp; Vertex wRad, vRad, vEndp, nNumRadicals; int nDots=0 /* added initialization, 2006-03 */, nNumEdges; if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { return 0; } pBD->nNumRadEndpoints = 0; pBD->nNumRadEdges = 0; pBD->bRadSrchMode = bRadSrchMode; pBNS->alt_path = pBNS->altp[0]; pBNS->bChangeFlow = 0; ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); ReInitBnData( pBD ); ReInitBnStructAltPaths( pBNS ); if ( !ret && pBD->nNumRadEndpoints >= 2 ) { /* sort by radical locations */ qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); num_new_edges = 0; nNumRadicals = 0; /* create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */ /* connecting the new vertices radical vertices */ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { wRad = pBD->RadEndpoints[i]; pRad = pBNS->vert + wRad; delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); if ( delta <= 0 ) { delta = 1; } nNumEdges = 0; for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { nNumEdges ++; } /* add new aux vertex to the radical atom/vertex */ vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); if ( IS_BNS_ERROR( vRad ) ) { ret = vRad; goto error_exit; } pRad = pBNS->vert + vRad; pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; /* replace references to vertex wRad with vRad */ for ( k = i, nNumEdges = 0; k < j; k += 2 ) { pBD->RadEndpoints[k] = vRad; } nNumRadicals ++; } /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { vRad = pBD->RadEndpoints[i]; pRad = pBNS->vert + vRad; for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { /* connect vew vertex pRad to radical endpoints */ vEndp = pBD->RadEndpoints[j+1]; pEndp = pBNS->vert + vEndp; ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 ); if ( IS_BNS_ERROR( ret ) ) { goto error_exit; } pBD->RadEdges[pBD->nNumRadEdges ++] = ret; } } pBD->nNumRadicals = nNumRadicals; return nNumRadicals; /* done */ } return 0; /* nothing to do */ error_exit: RemoveRadEndpoints( pBNS, pBD, NULL ); return ret; } /**********************************************************************************/ #define MAX_NUM_RAD 256 /***************************************************************************/ int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) { int ret = 0, i, j, k, n, num_new_edges, delta = 1; BNS_VERTEX *pRad, *pEndp; Vertex wRad, vRad, vEndp, nNumRadicals; Vertex vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD]; int nNumRad = 0; int edge_flow; int nDots=0 /* added initialization, 2006-03 */, nNumEdges; NodeSet VertSet; if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { return 0; } /* find all radicals: their vertices have st_cap-st_flow=delta */ /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { if ( pBNS->vert[i].st_edge.cap - delta == (pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK) ) { if ( nNumRad < MAX_NUM_RAD ) { pBNS->vert[i].st_edge.cap -= delta; pBNS->tot_st_cap -= delta; vRadList[nNumRad] = i; /* radical position; i > j <=> vRadList[i] > vRadList[j] */ vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable * atoms in common with this radical atom * always keep vRadEqul[nNumRad] <= nNumRad */ nNumRad ++; } } } if ( pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad ) { return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */ } memset( &VertSet, 0, sizeof(VertSet) ); /* find reachable atoms by enabling each radical separately */ for ( j = 0; j < nNumRad; j ++ ) { i = vRadList[j]; pBD->nNumRadEndpoints = 0; pBD->nNumRadEdges = 0; pBD->bRadSrchMode = bRadSrchMode; pBNS->alt_path = pBNS->altp[0]; pBNS->bChangeFlow = 0; pBNS->vert[i].st_edge.cap += delta; /* enable single radical */ pBNS->tot_st_cap += delta; ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */ ReInitBnData( pBD ); ReInitBnStructAltPaths( pBNS ); pBD->bRadSrchMode = RAD_SRCH_NORM; pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */ pBNS->tot_st_cap -= delta; if ( IS_BNS_ERROR( ret ) ) { goto error_exit; } else if ( ret ) { ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */ goto error_exit; } if ( !ret && pBD->nNumRadEndpoints >= 2 ) { /* sort by: primary_key=radical locations, secondary_key=radical endoint */ qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); if ( pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints-2] != i ) { ret = BNS_RADICAL_ERR; /* more than one radical vertex */ goto error_exit; } if ( nNumRad > 1 ) { /* if more than one radical then save reachable atoms in bitmaps to allow */ /* faster finding whether same atoms are reachable by two or more radicals */ /* Later merge such sets */ if ( NULL == VertSet.bitword ) { SetBitCreate( ); if ( !NodeSetCreate( &VertSet, pBNS->num_atoms, nNumRad ) ) { ret = BNS_OUT_OF_RAM; /* out of RAM */ goto error_exit; } } NodeSetFromRadEndpoints( &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints); /* do not allow any radical center be treated as a reachable atom: */ RemoveFromNodeSet( &VertSet, j, vRadList, nNumRad ); } } } /* restore radical st_cap so that st_cap-st_flow=delta */ for ( j = 0; j < nNumRad; j ++ ) { i = vRadList[j]; pBNS->vert[i].st_edge.cap += delta; pBNS->tot_st_cap += delta; } /* merge lists that have common radical endpoints */ /* defect: if vertex sets i and j do not intersect they will be compared 2 times */ /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */ if ( nNumRad > 1 ) { for ( i = 0; i < nNumRad; i ++ ) { if ( vRadEqul[i] != i ) continue; do { n = 0; for ( j = i+1; j < nNumRad; j ++ ) { if ( vRadEqul[j] != j ) continue; if ( DoNodeSetsIntersect( &VertSet, i, j) ) { AddNodeSet2ToNodeSet1( &VertSet, i, j); vRadEqul[j] = i; /* Set j was copied to set i; i < j */ n ++; } } } while( n ); } /* fill out pBD->RadEndpoints[] */ for ( i = 0, n = 0; i < nNumRad; i ++ ) { if ( i == vRadEqul[i] ) { if ( !IsNodeSetEmpty( &VertSet, i) ) { /* store equivalent radicals */ for ( j = i+1; j < nNumRad; j ++ ) { if (i == vRadEqul[j] ) { pBD->RadEndpoints[n++] = vRadList[i]; pBD->RadEndpoints[n++] = -vRadList[j]-2; /* equivalent radical, alvays not zero */ } } /* store endpoints */ n = AddNodesToRadEndpoints( &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv ); if ( n < 0 ) { ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */ goto error_exit; } } else { pBD->RadEndpoints[n++] = vRadList[i]; pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ } } } pBD->nNumRadEndpoints = n; NodeSetFree( &VertSet ); } else if ( nNumRad == 1 && !pBD->nNumRadEndpoints ) { /* 2006-07-30: a single radical; no possible endpoint found */ for ( i = 0, n = 0; i < nNumRad; i ++ ) { pBD->RadEndpoints[n++] = vRadList[i]; pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ } pBD->nNumRadEndpoints = n; } if ( !ret && pBD->nNumRadEndpoints >= 2 ) { /* already sorted by radical locations */ num_new_edges = 0; nNumRadicals = 0; /************************************************************************** * create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 * connecting the new vertices radical vertices * * * Original structure: atom A is a radical center A==B--C*--D==E * A*--B==C--D==E atoms C and E are reachable: A==B--C===D--E* * * Resultant temporary structure: * A---B==C--D==E * || / / * || / / The additional new vertex (*) and its * || / / 3 edges replace the radical with alternating * || / / circuits that allow same bond changes * || / / as moving the radical to atoms C or E. * ||// "Double bonds" here have edge cap=1, flow=1 * (*) "Single bonds" have edge cap=1, flow=0 * * The "equivalent radical centers" (which have at least one reachable atom * in common) are connected to (*) with "double bonds" (edge cap=1, flow=1). * Reachable non-radical atoms are connected by edges with cap=1, flow=0 * After running BNS to find alt.path a "double bond" from (*) may move * to another atom thus muving the radical. * * Number of additional (*) vertices = number of sets of * "equivalent radical centers". * Each such a set may include one or more radical centers. * * The radicals will be re-created in RemoveRadEndpoints() ***************************************************************************/ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { wRad = pBD->RadEndpoints[i]; pRad = pBNS->vert + wRad; delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); if ( delta <= 0 ) { delta = 1; } nNumEdges = 0; for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { nNumEdges += (pBD->RadEndpoints[j+1] != -1); /* immobile radicals have one edge only */ } /* add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */ /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */ vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); if ( IS_BNS_ERROR( vRad ) ) { ret = vRad; goto error_exit; } pRad = pBNS->vert + vRad; pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; /* replace references to vertex wRad with vRad */ for ( k = i, nNumEdges = 0; k < j; k += 2 ) { pBD->RadEndpoints[k] = vRad; } nNumRadicals ++; } /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { vRad = pBD->RadEndpoints[i]; pRad = pBNS->vert + vRad; for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { /* connect vew vertex pRad to radical endpoints */ vEndp = pBD->RadEndpoints[j+1]; if ( vEndp == -1 ) continue; if ( vEndp < 0 ) { edge_flow = 1; vEndp = -vEndp - 2; /* equivalent radical centers */ } else { edge_flow = 0; } pEndp = pBNS->vert + vEndp; ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow ); if ( IS_BNS_ERROR( ret ) ) { goto error_exit; } pBD->RadEdges[pBD->nNumRadEdges ++] = ret; } } pBD->nNumRadicals = nNumRadicals; return nNumRadicals; /* done */ } return 0; /* nothing to do */ error_exit: RemoveRadEndpoints( pBNS, pBD, NULL ); NodeSetFree( &VertSet ); return ret; } #else /**********************************************************************************/ int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) { return 0; } int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) { return 0; } int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) { return 0; } #endif /**********************************************************************************/ /* Return value ret bits if not IS_BNS_ERROR(ret): ret & 1 => Success ret & 2 => Bonds changed to Alt (ret & ~3) >> 2 => nDelta: number of removed dots */ int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ) { ALT_PATH_CHANGES apc; int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1; int bAdjustRadicals = 0; AT_NUMB type; BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1]; ENDPOINT_INFO eif; #if ( KETO_ENOL_TAUT == 1 ) ENDPOINT_INFO eif2; #endif /* initialize */ switch( path_type ) { case ALT_PATH_MODE_TAUTOM: /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ type = BNS_VERT_TYPE_ENDPOINT; bChangeFlow = BNS_EF_CHNG_RSTR; if ( !at[nVertSingleBond].endpoint && (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) return 0; if ( !at[nVertDoubleBond].endpoint && (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) return 0; break; #if ( KETO_ENOL_TAUT == 1 ) case ALT_PATH_MODE_TAUTOM_KET: /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ type = BNS_VERT_TYPE_ENDPOINT; bChangeFlow = BNS_EF_CHNG_RSTR; if ( !at[nVertSingleBond].endpoint && (!nGetEndpointInfo_KET( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) return 0; if ( !at[nVertDoubleBond].endpoint && (!nGetEndpointInfo_KET( at, nVertDoubleBond, &eif2 ) || !eif2.cAcceptor ) ) return 0; /* if ( eif.cKetoEnolCode + eif2.cKetoEnolCode != 3 ) return 0; */ break; #endif case ALT_PATH_MODE_CHARGE: /* Find alt path allowing to move (+). Purpose: establish "charge groups", mark alt. bonds due to (+) charge movement */ type = BNS_VERT_TYPE_C_POINT; bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); break; case ALT_PATH_MODE_4_SALT: case ALT_PATH_MODE_4_SALT2: /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms". Purpose: mark alt bonds due to this "long range" tautomerism. */ type = BNS_VERT_TYPE_ENDPOINT; bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) return 0; if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ && (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) return 0; memset( &apc, 0, sizeof(apc) ); break; case ALT_PATH_MODE_REM2H_CHG: bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ case ALT_PATH_MODE_REM2H_TST: bChangeFlow |= BNS_EF_CHNG_RSTR; type = BNS_VERT_TYPE_ENDPOINT; /* allow non-tautomeric donors or any tautomeric atom */ if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* not linked to a t-group or the edge forbidden */&& (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) /* not a donor */ return 0; if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* not connected to a t-group */ && (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cDonor ) ) return 0; memset( &apc, 0, sizeof(apc) ); break; case ALT_PATH_MODE_ADD2H_CHG: bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ case ALT_PATH_MODE_ADD2H_TST: bChangeFlow |= BNS_EF_CHNG_RSTR; type = BNS_VERT_TYPE_ENDPOINT; /* allow non-tautomeric acceptors or any tautomeric atom */ if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cAcceptor ) ) return 0; if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertSingleBond].endpoint*/ && (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) return 0; break; case ALT_PATH_MODE_REM_PROTON: /* alt path is between the t-group (nVertDoubleBond) and the (+)-charge group (nVertSingleBond) */ type = 0; /*bDoMarkChangedBonds = 0;*/ bChangeFlow = (BNS_EF_SAVE_ALL | BNS_EF_UPD_H_CHARGE) | BNS_EF_ALTR_NS; /* added BNS_EF_ALTR_NS: set non-stereo altern non-ring bonds 2004-07-02*/ break; default: type = 0; bChangeFlow = BNS_EF_CHNG_RSTR; break; } bError = 0; bSuccess = 0; nDelta = 0; ret = SetRadEndpoints2( pBNS, pBD, RAD_SRCH_NORM ); if ( IS_BNS_ERROR( ret ) ) { return ret; } /* set BNS to check alt path */ ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots ); switch( ret ) { case BNS_CHK_ALTP_NO_ALTPATH: ret = RemoveRadEndpoints( pBNS, pBD, NULL ); return ret; case BNS_CHK_ALTP_SAME_TGROUP: bSuccess = 1; goto reinit_BNS; case BNS_CHK_ALTP_SAME_VERTEX: ret = RemoveRadEndpoints( pBNS, pBD, NULL ); return ret? ret : 1; /* very strange ... set a breakpoint here */ case BNS_CHK_ALTP_SET_SUCCESS: break; /* actually check the existence of the altpath */ case BNS_CANT_SET_BOND: goto reinit_BNS; default: ret_val = RemoveRadEndpoints( pBNS, pBD, NULL ); if ( IS_BNS_ERROR( ret ) ) { return ret; } return BNS_PROGRAM_ERR; } bAdjustRadicals = ( (bChangeFlow & BNS_EF_UPD_RAD_ORI) && !(bChangeFlow & BNS_EF_RSTR_FLOW) ); /***************************************************************** * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges) * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater * because some of the bonds are effectively removed and dots * (vertex st-caps) may be added * -- to make sure there is no (+) charge on a tautomeric endpoint * -- to fix positions of moveable tautomeric attachements * (H and (-)-charges) at the ends of an alt path */ /* run BNS */ ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else if ( ret > 0 ) { if ( 2*ret >= nDots ) { nDelta = 2*ret - nDots; /* non-zero means augmentation created another alt. path -- between radicals */ if ( pAATG && pAATG->nMarkedAtom ) { if ( pAATG->nAtTypeTotals && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { memset( pAATG->nMarkedAtom, 0, num_atoms*sizeof(pAATG->nMarkedAtom[0]) ); /* mark atoms that have charge or H changed, check their input types (that is, before changes), and subtract their input charge/H from nAtTypeTotals */ SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 ); /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */ /* Atom types will be checked and nAtTypeTotals will be changed in AddChangedAtHChargeBNS() later */ SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 ); } else if ( !pAATG->nAtTypeTotals ){ bDoMarkChangedBonds = MarkAtomsAtTautGroups( pBNS, num_atoms, pAATG, nVertSingleBond, nVertDoubleBond ); if ( bDoMarkChangedBonds < 0 ) { bError = bDoMarkChangedBonds; bDoMarkChangedBonds = 0; } } } if ( bDoMarkChangedBonds ) { /* mark bonds that were changed to configure bond testing */ ret_val = bSetBondsAfterCheckOneBond( pBNS, fcd, -1, at, num_atoms, bChangeFlow ); if ( IS_BNS_ERROR( ret_val ) ) { bError = ret_val; } /*ret = SetBondsRestoreBnStructFlow( pBNS, at, num_atoms, bChangeFlow );*/ /* mark all other changed bonds */ ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } else if ( !(ret & 1) && !(ret_val & 1) ) { bSuccess = 1; } else if ( ((ret & 1) || (ret_val & 1)) && (bChangeFlow & BNS_EF_ALTR_BONDS) || (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { /* some bonds have been changed to alternating */ bSuccess = 3; } else { bError = BNS_BOND_ERR; } if ( !bError && pAATG && pAATG->nMarkedAtom && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */ if ( bAdjustRadicals ) { ret_val = RestoreRadicalsOnly( pBNS, pBD, at ); if ( IS_BNS_ERROR( ret_val ) ) { bError = ret_val; } } /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */ /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */ AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom ); if ( bChangeFlow & BNS_EF_CHNG_FLOW ) { /* eliminate ambiguities in already changed flow: replace (+)--N==(-) with (+)==N--(-) (both represent neutral N) */ EliminatePlusMinusChargeAmbiguity( pBNS, num_atoms ); } } } } ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); if ( IS_BNS_ERROR( ret ) ) { bError = ret; } } reinit_BNS: /* --- reinitialize to repeat the calculations --- */ bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE ); bRestoreFlowAfterCheckOneBond( pBNS, fcd ); ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals? at : NULL ); ReInitBnStructAltPaths( pBNS ); return bError? bError : ret_val? ret_val : (bSuccess + 4*nDelta); } /*****************************************************************************/ BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ) { BN_STRUCT *pBNS = NULL; BNS_VERTEX *vert; int neigh, num_changed_bonds=0; U_CHAR bond_type, bond_mark; int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; int tot_st_cap, tot_st_flow; int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp; #if ( BNS_RAD_SEARCH == 1 ) int num_rad = 0; nMaxAddEdges += 1; #endif #if ( FIX_NUM_TG == 1 ) max_tg = inchi_max( num_atoms / 2, 5); #else max_tg = num_atoms; #endif num_changed_bonds = 0; for ( i = 0, num_bonds = 0; i < num_atoms; i ++ ) { num_bonds += at[i].valence; #if ( BNS_RAD_SEARCH == 1 ) num_rad += (at[i].radical == RADICAL_DOUBLET); #endif } /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ num_edges = (num_bonds /= 2); /* +1 for a super-tautomeric group */ max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; /* +max_tg for edges between t-groups and super-tautomeric group */ max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices + max_tg; #if ( BNS_RAD_SEARCH == 1 ) if ( num_rad ) { max_vertices *= 2; max_edges *= 2; } #endif max_iedges = 2*max_edges; len_alt_path = max_vertices+iALTP_HDR_LEN+1; /* may overflow if an edge is traversed in 2 directions */ if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { return DeAllocateBnStruct( pBNS ); } /* alt path init */ for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { return DeAllocateBnStruct( pBNS ); } ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ /* re-init */ ALTP_DELTA(pBNS->altp[num_altp]) = 0; ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; } pBNS->alt_path = NULL; pBNS->num_altp = 0; pBNS->max_altp = num_altp; /* fill vertices (no connectivity) */ pBNS->vert[0].iedge = pBNS->iedge; for ( i = 0; i < num_atoms; i ++ ) { k = pBNS->vert[i].max_adj_edges = at[i].valence + (nMaxAddEdges + NUM_KINDS_OF_GROUPS); pBNS->vert[i+1].iedge = pBNS->vert[i].iedge + k; } pBNS->num_atoms = num_atoms; /* number of real atoms */ pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; /* number of added t-groups */ pBNS->num_c_groups = 0; pBNS->nMaxAddAtoms = nMaxAddAtoms; pBNS->nMaxAddEdges = nMaxAddEdges; pBNS->num_vertices = num_atoms; /* current number of vertices, a sum of pBNS->num_atoms pBNS->num_t_groups pBNS->num_added_atoms */ pBNS->max_vertices = max_vertices; pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ pBNS->max_edges = max_edges; pBNS->max_iedges = max_iedges; /* To remove t-groups and added atoms: In atoms i = 0..pBNS->num_atoms-1 pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; pBNS->num_vertices = pBNS->num_atoms; pBNS->num_edges = pBNS->num_bonds; pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; pBNS->num_added_edges = 0; ALTP_DELTA(pBNS->alt_path) = 0; ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; ALTP_PATH_LEN(pBNS->alt_path) = 0; */ /* fill edges and connectivity */ tot_st_cap = tot_st_flow = 0; for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { vert = &pBNS->vert[i]; st_cap = 0; st_flow = 0; flag_alt_bond = 0; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; /* find this bond at the neighbor */ for ( k = 0; k < at[neigh].valence; k ++ ) { if ( at[neigh].neighbor[k] == i ) { break; } } bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/ ) { /* make Unknown or Alternating bonds single */ bond_type = 1; at[i].bond_type[j] = bond_mark | bond_type; num_changed_bonds ++; } if ( neigh > i ) { /* this is the first time we encounter this bond */ f1 = MAX_AT_FLOW(at[i]); f2 = MAX_AT_FLOW(at[neigh]); edge_flow = bond_type-1; if ( edge_flow > MAX_BOND_EDGE_CAP ) { flag_alt_bond ++; edge_flow = 0; /* BNS will determine flows (that is, bonds) */ edge_cap = AROM_BOND_EDGE_CAP; } else { #if ( 0 && KETO_ENOL_TAUT == 1 ) /* ????? */ edge_cap = inchi_max(f1, f2); #else edge_cap = inchi_min(f1, f2); #endif edge_cap = inchi_min(edge_cap, MAX_BOND_EDGE_CAP); /* max capacity = 2 means up to triple bond */ } pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); pBNS->edge[n_edges].flow = pBNS->edge[n_edges].flow0 = edge_flow; pBNS->edge[n_edges].cap = pBNS->edge[n_edges].cap0 = edge_cap; pBNS->edge[n_edges].neigh_ord[0] = j; pBNS->edge[n_edges].neigh_ord[1] = k; pBNS->edge[n_edges].pass = 0; pBNS->edge[n_edges].forbidden = 0; vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; } else { /* this is the second time we encounter this bond. It was stored at */ int iedge = pBNS->vert[neigh].iedge[k]; edge_cap = pBNS->edge[iedge].cap; edge_flow = pBNS->edge[iedge].flow; } st_flow += edge_flow; st_cap += edge_cap; } vert->num_adj_edges = j; vert->st_edge.cap = vert->st_edge.cap0 = MAX_AT_FLOW(at[i]); vert->st_edge.flow = vert->st_edge.flow0 = st_flow; vert->type = BNS_VERT_TYPE_ATOM; tot_st_cap += vert->st_edge.cap; tot_st_flow += vert->st_edge.flow; } *pNum_changed_bonds = num_changed_bonds/2; pBNS->num_edges = n_edges; /* number of edges */ pBNS->num_added_edges = 0; pBNS->tot_st_cap = tot_st_cap; pBNS->tot_st_flow = tot_st_flow; return pBNS; } /*****************************************************************************/ BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ) { int i; if ( pBNS ) { if ( pBNS->edge ) { inchi_free( pBNS->edge ); } for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP ; i ++ ) { if ( pBNS->altp[i] ) { inchi_free( pBNS->altp[i] ); } } if ( pBNS->vert ) { if ( pBNS->vert[0].iedge ) { inchi_free( pBNS->vert[0].iedge ); } inchi_free( pBNS->vert ); } inchi_free( pBNS ); } return NULL; } /*****************************************************************************/ int ReInitBnStructAltPaths( BN_STRUCT *pBNS ) { int i; for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i ++ ) { if ( pBNS->altp[i] ) { ALTP_DELTA(pBNS->altp[i]) = 0; ALTP_PATH_LEN(pBNS->altp[i]) = 0; ALTP_START_ATOM(pBNS->altp[i]) = NO_VERTEX; ALTP_END_ATOM(pBNS->altp[i]) = NO_VERTEX; } } pBNS->alt_path = NULL; pBNS->num_altp = 0; return i; } /*****************************************************************************/ int ReInitBnStructAddGroups( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi, C_GROUP_INFO *cgi ) { int ret; /* strip all t-groups and c-groups */ ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); if ( ret ) { ret = BNS_REINIT_ERR; goto exit_function; } /*#if ( MOVE_CHARGES == 1 )*/ if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { /* add c-groups */ ret = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } } /*#endif*/ /* add t-groups */ ret = AddTGroups2BnStruct( pBNS, at, num_atoms, tgi ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } exit_function: return ret; } /*****************************************************************************/ int ReInitBnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ) { int i, vfict, kfict, iedgefict, endpoint, centerpoint, iedge, k; int ret = 0; if ( pBNS ) { if ( pBNS->vert && pBNS->edge ) { /* debug */ for ( k = 0, i = 0; k < pBNS->num_edges; k ++ ) { if ( pBNS->edge[k].pass ) { i ++; } } ret += i * 100; /* restore flow and cap on edges to vertices connected to fictitious atoms */ for ( vfict = pBNS->num_atoms; vfict < pBNS->num_vertices; vfict ++ ) { for ( kfict = 0; kfict < pBNS->vert[vfict].num_adj_edges; kfict ++ ) { iedgefict = pBNS->vert[vfict].iedge[kfict]; /* fictitious edge to the endpoint */ endpoint = pBNS->edge[iedgefict].neighbor12 ^ vfict; /* the endpoint */ /* to simlify restore cap and flow in ALL edges to the endpoint */ if ( bRemoveGroupsFromAtoms && endpoint < num_at ) { at[endpoint].c_point = 0; at[endpoint].endpoint = 0; } for ( k = 0; k < pBNS->vert[endpoint].num_adj_edges; k ++ ) { iedge = pBNS->vert[endpoint].iedge[k]; /* edge to endpoint */ centerpoint = pBNS->edge[iedge].neighbor12 ^ endpoint; pBNS->edge[iedge].cap = pBNS->edge[iedge].cap0; pBNS->edge[iedge].flow = pBNS->edge[iedge].flow0; pBNS->edge[iedge].pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) pBNS->edge[iedge].forbidden &= pBNS->edge_forbidden_mask; #endif pBNS->vert[centerpoint].st_edge.cap = pBNS->vert[centerpoint].st_edge.cap0; pBNS->vert[centerpoint].st_edge.flow = pBNS->vert[centerpoint].st_edge.flow0; } pBNS->vert[endpoint].st_edge.cap = pBNS->vert[endpoint].st_edge.cap0; pBNS->vert[endpoint].st_edge.flow = pBNS->vert[endpoint].st_edge.flow0; pBNS->vert[endpoint].type &= BNS_VERT_TYPE_ATOM; } } /* reset number of neighbors */ if ( pBNS->num_edges > pBNS->num_bonds ) { for ( i = 0; i < pBNS->num_atoms; i ++ ) { pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; } } } else { ret += 2; } if ( !pBNS->edge ) { ret += 4; } if ( !pBNS->iedge ) { ret += 8; } ReInitBnStructAltPaths( pBNS ); pBNS->num_vertices = pBNS->num_atoms; pBNS->num_edges = pBNS->num_bonds; pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; pBNS->num_c_groups = 0; pBNS->num_added_edges = 0; } else { ret += 1; } return ret; } /*****************************************************************************/ int CompTGroupNumber( const void *tg1, const void *tg2 ) { return (int)((const T_GROUP *)tg1)->nGroupNumber - (int)((const T_GROUP *)tg2)->nGroupNumber; } /*****************************************************************************/ int CompCGroupNumber( const void *cg1, const void *cg2 ) { return (int)((const C_GROUP *)cg1)->nGroupNumber - (int)((const C_GROUP *)cg2)->nGroupNumber; } /*****************************************************************************/ int AddTGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi ) { int ret = 0; /* ret = ReInitBnStruct( pBNS ); */ if ( tgi && tgi->num_t_groups && tgi->t_group ) { int i, k, endpoint, centerpoint, fictpoint; int num_tg = tgi->num_t_groups; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ BNS_VERTEX *vert_endpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ int nMaxTGroupNumber = 0; ENDPOINT_INFO eif; /* Debug: check overflow */ if ( num_vertices + num_tg >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } /* find the largest t-group ID */ for ( i = 0; i < num_tg; i ++ ) { if ( tgi->t_group[i].nGroupNumber > nMaxTGroupNumber ) { nMaxTGroupNumber = tgi->t_group[i].nGroupNumber; } } /* since t-group IDs may be not contiguous, clear all vertices that will be added. all-zeroes-vertex will be ignored by the BNS */ memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); /* Make sure the last t-group has the largest t-group ID: this is necessary to correctly add new edges and vertices for testing augmenting paths */ #if ( bRELEASE_VERSION != 1 ) insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); for ( i = 1; i < num_tg; i ++ ) { if ( 1 != tgi->t_group[i].nGroupNumber - tgi->t_group[i-1].nGroupNumber ) { return BNS_BOND_ERR; } } #else if ( nMaxTGroupNumber != tgi->t_group[num_tg-1].nGroupNumber ) { insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); } #endif /* initialize new fictitious vertices */ ver_ficpont_prev = pBNS->vert+num_vertices - 1; for ( i = 0; i < num_tg; i ++, ver_ficpont_prev = vert_ficpoint ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the vertex that is being added Note: nGroupNumber are not contiguous */ vert_ficpoint = pBNS->vert+num_vertices + tgi->t_group[i].nGroupNumber - 1; vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; vert_ficpoint->max_adj_edges = tgi->t_group[i].nNumEndpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; vert_ficpoint->type = BNS_VERT_TYPE_TGROUP; } for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { if ( !at[endpoint].endpoint ) continue; fictpoint = at[endpoint].endpoint + num_vertices - 1; vert_ficpoint = pBNS->vert + fictpoint; vert_endpoint = pBNS->vert + endpoint; /* Debug: check overflow */ if ( fictpoint >= pBNS->max_vertices || num_edges >= pBNS->max_edges || vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { ret = BNS_VERT_EDGE_OVFL; break; } /* obtain donor/acceptor info */ if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { #if ( KETO_ENOL_TAUT == 1 ) if ( !((tgi->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) && nGetEndpointInfo_KET( at, endpoint, &eif )) ) #endif { ret = BNS_BOND_ERR; break; } } vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { int iedge = vert_endpoint->iedge[k]; if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between endpoint and centerpoint */ centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); if (bond_type == BOND_TAUTOM || bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_SINGLE ) { pBNS->edge[iedge].cap = 1; } } } } /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ edge = pBNS->edge + num_edges; edge->cap = 1; edge->flow = 0; edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; #endif /* later include case when the charge change allows the endpoint to become tautomeric */ /* mark endoint having moveable H atom with flow=1 */ /* -- old "no charges" version -- */ /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ /* -- the following line takes charges into account -- */ if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ { /* increment edge flow */ edge->flow ++; /* increment one vertex st-flow & cap */ vert_ficpoint->st_edge.flow ++; vert_ficpoint->st_edge.cap ++; /* increment another vertex st-flow & cap */ vert_endpoint->st_edge.flow ++; vert_endpoint->st_edge.cap ++; } /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; edge->cap0 = edge->cap; edge->flow0 = edge->flow; } pBNS->num_edges = num_edges; pBNS->num_vertices += nMaxTGroupNumber; pBNS->num_t_groups = num_tg; } return ret; } /*****************************************************************************/ /*#if ( MOVE_CHARGES == 1 )*/ /* { */ /*****************************************************************************/ int AddCGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi ) { int ret = 0; /* ret = ReInitBnStruct( pBNS ); */ if ( cgi && cgi->num_c_groups && cgi->c_group ) { int i, k, c_point, centerpoint, fictpoint; int num_cg = cgi->num_c_groups; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ BNS_VERTEX *vertex_cpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ int nMaxCGroupNumber = 0; /* Debug: check overflow */ if ( num_vertices + num_cg >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } /* find the largest t-group ID */ for ( i = 0; i < num_cg; i ++ ) { if ( cgi->c_group[i].nGroupNumber > nMaxCGroupNumber ) { nMaxCGroupNumber = cgi->c_group[i].nGroupNumber; } } /* since t-group IDs may be not contiguous, clear all vertices that will be added. all-zeroes-vertex will be ignored by the BNS */ memset( pBNS->vert+num_vertices, 0, nMaxCGroupNumber*sizeof(pBNS->vert[0]) ); /* Make sure the last t-group has the largest t-group ID: this is necessary to correctly add new edges and vertices for testing augmenting paths */ #if ( bRELEASE_VERSION != 1 ) insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); for ( i = 1; i < num_cg; i ++ ) { if ( 1 != cgi->c_group[i].nGroupNumber - cgi->c_group[i-1].nGroupNumber ) { return BNS_BOND_ERR; } } #else if ( nMaxCGroupNumber != cgi->c_group[num_cg-1].nGroupNumber ) { insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); } #endif /**************************************/ /* initialize new fictitious vertices */ /* representing c-point groups */ /**************************************/ ver_ficpont_prev = pBNS->vert+num_vertices - 1; for ( i = 0; i < num_cg; i ++, ver_ficpont_prev = vert_ficpoint ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the being added vertex Note: nGroupNumber are not contiguous */ vert_ficpoint = pBNS->vert+num_vertices + cgi->c_group[i].nGroupNumber - 1; vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; vert_ficpoint->max_adj_edges = cgi->c_group[i].num_CPoints+BNS_ADD_EDGES; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP; } /************************************************/ /* connect c-points to the fictitious vertices */ /* representing c-point groups; set caps, flows */ /************************************************/ for ( c_point = 0; c_point < num_atoms; c_point ++ ) { if ( !at[c_point].c_point ) continue; fictpoint = at[c_point].c_point + num_vertices - 1; /* c-group vertex index */ vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ /* Debug: check overflow */ if ( fictpoint >= pBNS->max_vertices || num_edges >= pBNS->max_edges || vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { ret = BNS_VERT_EDGE_OVFL; break; } vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; #if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ /* if their current capacity is zero */ /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { int iedge = vertex_cpoint->iedge[k]; if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between c_point and centerpoint */ centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALTERN || bond_type == BOND_SINGLE ) { pBNS->edge[iedge].cap = 1; } } } } #endif /* } FIX_CPOINT_BOND_CAP */ /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ edge = pBNS->edge + num_edges; edge->cap = 1; edge->flow = 0; edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; #endif /* mark edge to c-point having NO moveable charge with flow=1 */ if ( !CHARGED_CPOINT(at,c_point) ) { /* increment edge flow */ edge->flow ++; /* increment c-group vertex st-flow & cap */ vert_ficpoint->st_edge.flow ++; vert_ficpoint->st_edge.cap ++; /* increment c-point vertex st-flow & cap */ vertex_cpoint->st_edge.flow ++; vertex_cpoint->st_edge.cap ++; } #if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ /* if their current capacity is zero */ /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { int iedge = vertex_cpoint->iedge[k]; VertexFlow nNewCap = vertex_cpoint->st_edge.cap; centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between c_point and centerpoint */ if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); pBNS->edge[iedge].cap = nNewCap; } #if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ else if ( centerpoint < pBNS->num_atoms && edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { pBNS->edge[iedge].cap ++; } #endif } } #endif /* } FIX_CPOINT_BOND_CAP */ /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; edge->cap0 = edge->cap; edge->flow0 = edge->flow; } pBNS->num_edges = num_edges; pBNS->num_vertices += nMaxCGroupNumber; pBNS->num_c_groups = num_cg; } return ret; } /*#endif*/ /* } MOVE_CHARGES == 1 */ /*****************************************************************************/ void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ) { int i; for ( i = 0; i < size; i ++ ) { v[i] = value; } } /*****************************************************************************/ void ClearAllBnDataEdges( Edge *e, Vertex value, int size ) { int i; for ( i = 0; i < size; i ++ ) { e[i][0] = value; } } /*****************************************************************************/ BN_DATA *DeAllocateBnData( BN_DATA *pBD ) { if ( pBD ) { if ( pBD->BasePtr ) inchi_free( pBD->BasePtr ); if ( pBD->SwitchEdge ) inchi_free( pBD->SwitchEdge ); if ( pBD->Tree ) inchi_free( pBD->Tree ); if ( pBD->ScanQ ) inchi_free( pBD->ScanQ ); if ( pBD->Pu ) inchi_free( pBD->Pu ); if ( pBD->Pv ) inchi_free( pBD->Pv ); #if ( BNS_RAD_SEARCH == 1 ) if ( pBD->RadEndpoints ) { inchi_free( pBD->RadEndpoints ); } if ( pBD->RadEdges ) { inchi_free( pBD->RadEdges ); } #endif inchi_free( pBD ); } return NULL; } /*****************************************************************************/ BN_DATA *AllocateAndInitBnData( int max_num_vertices ) { BN_DATA *pBD = NULL; int max_len_Pu_Pv; max_num_vertices = 2*max_num_vertices+2; max_len_Pu_Pv = max_num_vertices/2+1; max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */ if ( !(pBD = (BN_DATA *) inchi_calloc( 1, sizeof(BN_DATA) ) ) || !(pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || !(pBD->SwitchEdge = (Edge *) inchi_calloc( max_num_vertices, sizeof(Edge ) ) ) || !(pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof(S_CHAR) ) ) || !(pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || !(pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || #if ( BNS_RAD_SEARCH == 1 ) !(pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || !(pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof(EdgeIndex) ) ) || #endif !(pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) ) { pBD = DeAllocateBnData( pBD ); } else { /* Initialize data */ ClearAllBnDataEdges(pBD->SwitchEdge, NO_VERTEX, max_num_vertices); ClearAllBnDataVertices(pBD->BasePtr, NO_VERTEX, max_num_vertices); memset(pBD->Tree, TREE_NOT_IN_M, max_num_vertices); pBD->QSize = -1; pBD->max_len_Pu_Pv = max_len_Pu_Pv; pBD->max_num_vertices = max_num_vertices; #if ( BNS_RAD_SEARCH == 1 ) pBD->nNumRadEndpoints = 0; #endif } return pBD; } /*****************************************************************************/ int ReInitBnData( BN_DATA *pBD ) { int i, ret = 0; Vertex u, v; if ( pBD ) { if ( !pBD->ScanQ ) { ret += 2; } if ( !pBD->BasePtr ) { ret += 4; } if ( !pBD->SwitchEdge ) { ret += 8; } if ( !pBD->Tree ) { ret += 16; } if ( !ret ) { for ( i = 0; i <= pBD->QSize; i ++ ) { u = pBD->ScanQ[i]; v = prim(u); pBD->BasePtr[u] = pBD->BasePtr[v] = pBD->SwitchEdge_Vert1(u) = pBD->SwitchEdge_Vert1(v) = NO_VERTEX; pBD->Tree[u] = pBD->Tree[v] = TREE_NOT_IN_M; } } pBD->QSize = -1; if ( !pBD->Pu ) { ret += 32; } if ( !pBD->Pv ) { ret += 64; } } else { ret += 1; } return ret; } /*****************************************************************************/ int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ) { int i = v / 2 - 1; if ( i >= 0 ) { if ( pBNS->vert[i].st_edge.cap > 0 ) { return pBNS->vert[i].num_adj_edges+1; /* add 1 neighbor for s or t */ } else { return 0; /* since the edge s-v has zero capacity, we ignore vertex v */ } } else { return pBNS->num_vertices; } } /*****************************************************************************/ Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ) { /* Vertex ret; */ if ( uv[1] >= 0 ) { /* -- debug -- if ( uv[1] > pBNS->num_edges || uv[0] > 2*pBNS->num_vertices+3 ) { int stop = 1; } ret = ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; if ( ret > 2*pBNS->num_vertices+3 ) { int stop = 1; } return ret; -- end debug -- */ return ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; /*short u = uv[0]-FIRST_INDX; */ /*short t = 2*(((u / 2 - 1) ^ pBNS->edge[uv[1]].neighbor12) + 1) + ((u+1) & 1) + FIRST_INDX; */ /*return t; */ } if ( uv[0] <= 1 ) return -(1 + uv[1]); /* vertex1 is s or t, return x or y */ else return uv[0] % 2; /* vertex1 is x or y, return s or t; never happens? -- NSC 3737, 7634,... */ } /*****************************************************************************/ Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ) { /* neigh = 0 => the neighbor is s or t except case when v is s or t. */ /* v= FIRST_INDX or FIRST_INDX+1: v is s or t respectively */ int i, neighbor; if ( (i = v - 2) >= 0 ) { /* neighbor of x or y */ if ( neigh ) { neigh --; /* x or y */ *iedge = pBNS->vert[i/2].iedge[neigh]; if ( !(pBNS->edge[*iedge].cap & EDGE_FLOW_MASK) || IS_FORBIDDEN(pBNS->edge[*iedge].forbidden, pBNS) ) { return NO_VERTEX; } neighbor = (i ^ (2 * pBNS->edge[*iedge].neighbor12 + 1)) + 2; /* parity opposite to v parity */ } else { /* neighbor of x or y is s or t */ neighbor = (v & 1); /* s or t, same parity as v */ *iedge = -( neighbor + 1 ); } } else { /* neighbor of s or t: x or y, same parity as v */ if ( !(pBNS->vert[neigh].st_edge.cap & EDGE_FLOW_ST_MASK) ) { return NO_VERTEX; } neighbor = 2*neigh + 2 + (v & 1); /* parity same as the parity of v */ *iedge = -( neighbor + 1 ); } return neighbor; } /*****************************************************************************/ int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ) { int i = u / 2 - 1; int j = v / 2 - 1; int bBackward = BNS_WRONG_PARMS; *uv = NULL; if ( i >= 0 ) { /* u is an atom */ if ( j >= 0 ) { /* v is an atom */ if ( (u+v)%2 ) { *uv = pBNS->edge+iuv; bBackward = ( u & 1 ); *s_or_t = 0; } } else /* v is s or t */ if ( v >= 0 && !((u+v)%2) ) { *uv = (BNS_EDGE*)&pBNS->vert[i].st_edge; bBackward = !(v & 1); *s_or_t = v+3; /* 3=> v=s, 4=> v=t */ } } else if ( j >= 0 ) { /* u is s or t */ if ( u >= 0 && !((u+v)%2) ) { /* v is an atom */ *uv = (BNS_EDGE*)&pBNS->vert[j].st_edge; bBackward = (u & 1 ); *s_or_t = u+1; /* 1=> u=s, 2=> u=t */ } } return bBackward; } /*****************************************************************************/ int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ) { int f, flow, ret=0; BNS_ST_EDGE *pst_edge; BNS_EDGE *pedge; S_CHAR s_or_t; int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); if ( !IS_BNS_ERROR(bBackward) ) { if ( bBackward ) { delta = -delta; } if ( s_or_t ) { pst_edge = ( BNS_ST_EDGE *) pedge; flow = pst_edge->flow; f = (flow & EDGE_FLOW_ST_MASK) + delta; /* new flow */ if ( !delta ) { /*((BNS_ST_EDGE *)pedge)->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;*/ pst_edge->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH; } else { int cap = pst_edge->cap; if ( f < 0 || f > cap ) { ret = BNS_WRONG_PARMS; } else { if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { f -= delta; /* do not actually change the flow, only find the augmenting path */ } else if ( delta ) { /*((BNS_ST_EDGE *)pedge)->pass ++;*/ pst_edge->pass ++; } flow = (flow & ~(EDGE_FLOW_ST_PATH | EDGE_FLOW_ST_MASK)) + f; /*((BNS_ST_EDGE *)pedge)->flow = flow;*/ pst_edge->flow = flow; /*((BNS_ST_EDGE *)pedge)->delta += delta; */ if ( bReverse ) { /* u <- v; Note: in case of bReverse s_or_t has actually been determined for the u' <- v' pair; therefore s and t should be switched in order to correctly determine the 1st or the last atom on the augmenting path. */ switch( s_or_t ) { case 1: /* u = t: t<-v, v is the last vertex */ ALTP_END_ATOM(pBNS->alt_path) = v / 2 - 1; break; case 2: /* u = s: s<-v, error */ ret = BNS_WRONG_PARMS; break; case 3: /* v = t: u<-t, error */ ret = BNS_WRONG_PARMS; break; case 4: /* v = s: u<-s, u is the first vertex */ ALTP_START_ATOM(pBNS->alt_path) = u / 2 - 1; ALTP_DELTA(pBNS->alt_path) = delta; break; default: ret = BNS_WRONG_PARMS; break; } } else { /* u -> v */ switch( s_or_t ) { case 1: /* u = s: s->v, v is the first vertex */ ALTP_START_ATOM(pBNS->alt_path) = v / 2 - 1; ALTP_DELTA(pBNS->alt_path) = delta; break; case 2: /* u = t: t->v, error */ ret = BNS_WRONG_PARMS; break; case 3: /* v = s: u->s, error */ ret = BNS_WRONG_PARMS; break; case 4: /* v = t: u->t, u is the last vertex */ ALTP_END_ATOM(pBNS->alt_path) = u / 2 - 1; break; default: ret = BNS_WRONG_PARMS; break; } } } } } else { f = (pedge->flow & EDGE_FLOW_MASK) + delta; if ( !delta ) { pedge->flow &= ~EDGE_FLOW_PATH; } else { if ( f < 0 || f > pedge->cap ) { ret = BNS_WRONG_PARMS; } else { AT_NUMB iu = u / 2 - 1; AT_NUMB iv = v / 2 - 1; int indx; if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { f -= delta; /* do not actually change the flow, only find the augmenting path */ } else if ( delta ) { pedge->pass ++; } pedge->flow = (pedge->flow & ~(EDGE_FLOW_PATH | EDGE_FLOW_MASK)) | f; if ( ALTP_MAY_ADD(pBNS->alt_path) ) { /* bReverse? u <- v : u -> v */ indx = bReverse? (pedge->neighbor1 == iv) : (pedge->neighbor1 == iu); ALTP_CUR_THIS_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[1-indx]; ALTP_CUR_NEXT_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[indx]; ALTP_NEXT(pBNS->alt_path); } else { ALTP_OVERFLOW(pBNS->alt_path) = 1; ret = BNS_ALTPATH_OVFL; } } } } return ret? ret : f; } return bBackward; } /*********************************************************************************/ /* find residual capacity and mark the edge as belonging to the augmenting path */ /*********************************************************************************/ int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) { BNS_ST_EDGE *pst_edge; BNS_EDGE *pedge; int f, flow; S_CHAR s_or_t; int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); if ( !IS_BNS_ERROR( bBackward ) ) { if ( s_or_t ) { pst_edge = (BNS_ST_EDGE *)pedge; flow = pst_edge->flow; f = (flow & EDGE_FLOW_ST_MASK); if ( !bBackward ) { f = (int)pst_edge->cap - f; } if ( flow & EDGE_FLOW_ST_PATH ) { pBNS->bNotASimplePath ++; f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ } else { pst_edge->flow |= EDGE_FLOW_ST_PATH; /* mark the edge */ } } else { flow = pedge->flow; f = flow & EDGE_FLOW_MASK; if ( !bBackward ) { f = (int)pedge->cap - f; } if ( flow & EDGE_FLOW_PATH ) { f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ pBNS->bNotASimplePath ++; } else { pedge->flow |= EDGE_FLOW_PATH; /* mark the edge */ } } return f; } return bBackward; } /***************************************************************************** * Get previous vertex in the searched path * * z is SwitchEdge_Vert2(y) != y. Go backward from z to y * ******************************************************************************/ Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ) { Vertex w, z, x2, y2, n; EdgeIndex iwy; w = SwitchEdge_Vert1(y); z = SwitchEdge_Vert2(y); iwy = SwitchEdge_IEdge(y); if ( z == y ) { *iuv = iwy; return w; } x2 = prim(y); y2 = prim(z); n = 0; while ( y2 != NO_VERTEX ) { w = SwitchEdge_Vert1(y2); z = SwitchEdge_Vert2(y2); iwy = SwitchEdge_IEdge(y2); if ( w == x2 ) { *iuv = iwy; /*return z; */ return (y + z)%2? z : prim(z); } n ++; #ifdef _DEBUG if ( n ) { int stop = 1; } #endif if ( w == y2 ) return NO_VERTEX; y2 = w; } return y2; } #define CHECK_TACN 1 /***************************************************************************** The purpose is to avoid paths (H-group)[u]---atom[v]---((-)-cgroup)[w], where atom[v] is not acidic and (-) and H are not interchangeable without explicit bond tautomerism. It is important that acidic atoms are only O,S,Se,Te and should have only one chemical bond. Only because of this an early rejection of the vertex v (before it gets on SCANQ) is possible. CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N along an alternating path from another heteroatom (t-group will be detected). *****************************************************************************/ #if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ /*****************************************************************************/ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) { #define TYPE_T 1 /* t-group [also called H-group] */ #define TYPE_CN 2 /* (-)c-group */ int i, degree, ret, u_is_taut=0, w_is_taut, num_allowed=0, num_found_groups=0; Vertex w; EdgeIndex ivw; if ( !pBNS->type_TACN || u <= 1 || v <= 1 || (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { return 0; /* add/remove H(+) is allowed for acidic atoms */ } if ( !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; if ( u_is_taut ) { /* u is either t-group vertex or (-) c-group */ degree = GetVertexDegree( pBNS, v ); for ( i = 0; i < degree; i ++ ) { /* v = vert[u].neighbor[i]; */ w = GetVertexNeighbor( pBNS, v, i, &ivw ); if ( w == NO_VERTEX || w <= 1 ) { continue; /* the atom has only single bonds or it is s or t, ignore it */ } if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { num_allowed ++; w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : 0; if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN) ) { num_found_groups ++; } } } if ( num_found_groups && num_allowed == 1 ) { return 1; /* reject */ } } return 0; #undef TYPE_T #undef TYPE_CN } #else /* } FIX_TACN_POSSIBLE_BUG { */ /*****************************************************************************/ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) { int i, degree, ret, u_is_taut=0, num_allowed=0, num_found_groups=0; Vertex w; EdgeIndex ivw; if ( !pBNS->type_TACN || u <= 1 || v <= 1 || (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { return 0; /* add/remove H(+) is allowed for acidic atoms */ } if ( !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ if ( (u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN) ) { /* u is either t-group vertex or (-) c-group */ degree = GetVertexDegree( pBNS, v ); for ( i = 0; i < degree; i ++ ) { /* v = vert[u].neighbor[i]; */ w = GetVertexNeighbor( pBNS, v, i, &ivw ); if ( w == NO_VERTEX || w <= 1 ) { continue; /* the atom has only single bonds or it is s or t, ignore it */ } if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { num_allowed ++; if ( (u_is_taut? ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN) : ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T ) ) ) { num_found_groups ++; } } } if ( num_found_groups && num_allowed == 1 ) { return 1; /* reject */ } } return 0; } #endif /* } FIX_TACN_POSSIBLE_BUG */ /***************************************************************************** The purpose is to avoid paths (H-group)[u]---atom[v]---((-)-cgroup)[w], where atom[v] is not acidic and (-) and H are not interchangeable without explicit bond tautomerism. It is important that acidic atoms are only O,S,Se,Te and should have only one chemical bond. Only because of this an early rejection of the vertex v (before it gets on SCANQ) is possible. *****************************************************************************/ #if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ /*****************************************************************************/ int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) { #define TYPE_T 1 /* t-group [also called H-group] */ #define TYPE_CN 2 /* (-)c-group */ int u_is_taut=0, w_is_taut=0; Vertex u; EdgeIndex iuv; if ( v <= 1 || w <= 1 ) return 0; #if ( CHECK_TACN == 1 ) if ( !pBNS->type_TACN || (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { return 0; } if ( !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ #endif u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); /* u = SwitchEdge_Vert1(v); iuv = SwitchEdge_IEdge(v); */ if ( u == NO_VERTEX || iuv < 0 ) return 0; /* should not happen */ /* check edge adjacency */ if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { return 0; /* !!! should not happen !!! */ } #if ( CHECK_TACN == 1 ) u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN ) ) { /* rescap must have already been checked */ return 1; } #endif return 0; #undef TYPE_T #undef TYPE_CN } #else /* } FIX_TACN_POSSIBLE_BUG { */ /*****************************************************************************/ int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) { int u_is_taut=0, w_is_taut=0; Vertex u; EdgeIndex iuv; if ( v <= 1 || w <= 1 ) return 0; #if ( CHECK_TACN == 1 ) if ( !pBNS->type_TACN || (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { return 0; } if ( !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ #endif u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); /* u = SwitchEdge_Vert1(v); iuv = SwitchEdge_IEdge(v); */ if ( u == NO_VERTEX || iuv < 0 ) return 0; /* should not happen */ /* check edge adjacency */ if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { return 0; /* !!! should not happen !!! */ } #if ( CHECK_TACN == 1 ) if ( ((u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && ((w_is_taut = (pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T) || ( (pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && u_is_taut + w_is_taut == 1 ) { /* rescap must have already been checked */ return 1; } #endif return 0; } #endif /* } FIX_TACN_POSSIBLE_BUG { */ #if ( FIX_KEEP_H_ON_NH_ANION == 1 ) /*********************************************************************************/ /* Detect an attempt to remove H from -NH(-) to make =N(-); */ /* all taut atoma except N are 'acidic' */ /*********************************************************************************/ int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ) { int i, u2, v2, vat2; Vertex vtg, vat; BNS_VERTEX *pvAT, *pvCN; BNS_EDGE *pEdge; if ( !pBNS->type_TACN || u <= 1 || v <= 1 || u%2 || !(v%2) /* the edge flow may only increase */ ) { return 0; } if ((pBNS->vert[u2 = u/2-1].type & pBNS->type_TACN) || (pBNS->vert[v2 = v/2-1].type & pBNS->type_TACN) ) { return 0; /* add/remove H is allowed for acidic atoms */ } if ( !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ /* find which of u, v vertices is N and which is t-group */ if ( ((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && v2 < pBNS->num_atoms ) { vtg = u; vat = v; } else if ( ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T ) && u2 < pBNS->num_atoms ) { vtg = v; vat = u; } else { return 0; } vat2 = vat/2-1; pvAT = pBNS->vert + vat2; /* atom */ for ( i = pvAT->num_adj_edges-1; 0 <= i; i -- ) { pEdge = pBNS->edge + pvAT->iedge[i]; pvCN = pBNS->vert + (pEdge->neighbor12 ^ vat2); if ( ((pvCN->type & pBNS->type_CN) == pBNS->type_CN) && pEdge->flow > 0 ) { return 1; /* detected */ } } return 0; } #endif #if ( FIX_AVOID_ADP == 1 ) /************************************************************************ Detect (tg)-N=A-A=A-A=N-(tg) u v w k = 5 4 3 2 1 0 1 2 ^ odd number means ADP *************************************************************************/ int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) { #define TYPE_T 1 /* t-group [also called H-group] */ #define TYPE_CN 2 /* (-)c-group */ #define TYPE_AT 4 int k, v2, u2, w2, u2_next, type0, type1, type2, type; Vertex u, u_next; EdgeIndex iuv; if ( v <= 1 || w <= 1 ) return 0; if ( !pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN ) return 0; /* should not happen */ v2 = v/2 - 1; w2 = w/2 - 1; if ( v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms ) goto cross_edge; if ( !((pBNS->vert[w2].type & pBNS->type_T) == pBNS->type_T ) && !((pBNS->vert[w2].type & pBNS->type_CN) == pBNS->type_CN) ) goto cross_edge; /* v ia an atom, w is a t-group, v != w' */ for ( k = 0, u = v; 1 < (u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv )); k ++ ) { u2 = u/2 - 1; if ( u2 >= pBNS->num_atoms ) { /* moving backward along the alt path we have found a vertex that is not an atom. Possibly it is a t- or (-)c-group */ if ( !( k % 2 ) ) { return 0; /* even vertex -- always okay */ } if ( !((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && !((pBNS->vert[u2].type & pBNS->type_CN) == pBNS->type_CN) ) { /* not a t- or (-)c-group */ return 0; } u2_next = u_next/2 - 1; if ( !(pBNS->vert[v2 ].type & pBNS->type_TACN) && !(pBNS->vert[u2_next].type & pBNS->type_TACN) ) { /* none of the atoms at the ends are N */ return 0; } return 1; } } return 0; cross_edge: /***************************************************************************** * v and w (v=w') are same vertex reached with opposite "phases". * w cannot be (t) because this would have been detected earlier -- ??? * (t)-A=A-A=A-A=A-(t) * v * 3 2 1 0 1 2 3 4 * kv kw * (kv + kw)%2 == 1 <==> aggressive deprotonation *****************************************************************************/ if ( v == prim(w) ) { type0 = 0; if ( v2 >= pBNS->num_atoms ) { type0 = ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : ((pBNS->vert[v2].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; } } return 0; } #endif /*****************************************************************************/ int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) { BNS_ST_EDGE *pst_edge; BNS_EDGE *pedge; int f; S_CHAR s_or_t; int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); if ( !IS_BNS_ERROR(bBackward) ) { if ( s_or_t ) { pst_edge = (BNS_ST_EDGE *)pedge; f = (pst_edge->flow & EDGE_FLOW_ST_MASK); if ( !bBackward ) { f = (int)pst_edge->cap-f; } } else { f = (pedge->flow & EDGE_FLOW_MASK); if ( !bBackward ) { f = (int)pedge->cap-f; } } return f; } return bBackward; /* error */ } /********************************************************************************* W.Kocay, D.Stone, "An Algorithm for Balanced Flows", The Journal of Combinatorial Mathematics and Combinatorial Computing, vol. 19 (1995) pp. 3-31 W.Kocay, D.Stone, "Balanced network flows", Bulletin of the Institute of Combinatorics and its Applications, vol. 7 (1993), pp. 17--32 N = a balanced network (bipartite directed graph) of: n=2*V+2 vertices (incl. s (source) and t (target); each other vertex i is included 2 times: set X (x[i]) of V vertices (v) and a set Y (y[j]) of V complementary vertices (v') so that x[i]' = y[i], x[i]''=x[i], and m=2*E+2*V edges (each original undirected edge i-j is represented as 2 directed edges: x[i]->y[j] and x[j]->y[i]; plus V edges s->x[i] and V edges y[j]->t) v' = a complement vertex to v v'u' = (uv)' = a complement edge to uv rescap(uv) = cap(uv)-f(uv) if uv is a forward edge = f(uv) if uv is a backward edge (i) 0 <= f(uv) <= cap(uv) (ii) f+(u) = f-(u) for all u in X, Y where f+(u) is a total flow out of u, and f-(u) is a total flow into u (iii) f(uv) = f((uv)') (balanced flow condition) S = a set of all s-searchable vertices S- = all other vertices if t in S, then N contains a valid augmenting path P, so that flow can be augmented on both P and P' if t not in S, then let K=[S,S-], the set of all edges directed from S to S-. K is an edge-cut that has a special structure. Let A = {x[i], y[i] |x[i] not in S, y[i] in S} B = {x[i], y[i] |x[i] in S, y[i] not in S} C = {x[i], y[i] |x[i] in S, y[i] in S} D = {x[i], y[i] |x[i] not in S, y[i] not in S} N[C] = subgraph of N induced by C; it consists of of a number of connected components C[i] K[i] = those edges of K with one endpoint in C[i]. If t is in S- then i) Each C[i] = C[i]' ii) There are no edges between C and D iii) Each K[i] has odd capacity, it is called a balanced edge-cut. balcap(K) = cap(K) - odd(K), where odd(K) is the number of connected components in N[C]. Name "odd(K)" is because each cap(K[i]) is odd. Max-Balanced-Flow-Min-Balanced-Cut Theorem: Let f be a balanced flow in N, and let K be any balanced edge-cut. The value of a maximum balanced flow equals the capacity of a minimum edge-cut, that is, val(f) = balcap(K) when f is maximum and K is minimum. *********************************************************************************/ /*******************************************************/ /* */ /* VERTEX NUMBERING */ /* */ /* Total number of atoms = n */ /* Total number of vertices = 2*n+2 */ /*******************************************************/ /* */ /* atom numbering starts from 0: */ /* */ /* atoms s t x0 y0 x1 y1 ... xi yi ...xn yn */ /* vertices 0 1 2 3 4 5 ... 2i-2 2i-1 ...2n-2 2n-1 */ /* */ /* atom = vertex/2-1; if negative then s or t */ /* */ /* vertex = (atom + 1) * 2 + i; i=0 for x, i=1 for y */ /* */ /*******************************************************/ /*********************************************************************************/ /* v' variable is called prim(v) for now */ /*********************************************************************************/ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ) { /* N has source s, target t, the mirror network M is constructed */ /* the tree T contains a valid sv-path for each v in T. Simultaneously the complementary tree T' is built as indicated in comments. The trees T and T' must have no edges or vertices in common. Initially T will be built as in breadth-first-search, and T' will be the complementary tree, it will contain the complementary valid v't-path. */ Vertex *BasePtr = pBD->BasePtr; Edge *SwitchEdge = pBD->SwitchEdge; S_CHAR *Tree = pBD->Tree; Vertex *ScanQ = pBD->ScanQ; int QSize = pBD->QSize; Vertex *Pu = pBD->Pu; Vertex *Pv = pBD->Pv; int max_len_Pu_Pv= pBD->max_len_Pu_Pv; /* added to translate into C */ int i, k, degree, delta, ret = 0; Vertex u, b_u, v, b_v, w; EdgeIndex iuv; #if ( BNS_RAD_SEARCH == 1 ) int n, bRadSearch = (BNS_EF_RAD_SRCH & bChangeFlow) && pBD->RadEndpoints; BRS_MODE bRadSrchMode = RAD_SRCH_NORM; int bRadSearchPrelim = 0; if ( bRadSearch ) { pBD->nNumRadEndpoints = 0; bRadSrchMode = pBD->bRadSrchMode; bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM; } #endif /* -- Always -- Vertex_s = FIRST_INDX; Vertex_t = Vertex_s+1; */ QSize = k = 0; /* put s on ScanQ = set S */ ScanQ[QSize] = Vertex_s; BasePtr[Vertex_t] = Vertex_s; BasePtr[Vertex_s] = BLOSSOM_BASE; /* create initial blossom C(Vertex_s) with base s */ Tree[Vertex_s] = TREE_IN_1; do { u = ScanQ[k]; /* select u from the head of ScanQ */ /* since u is on the queue, it has a blossom C(U) with base b_u */ b_u = FindBase( u, BasePtr ); degree = GetVertexDegree( pBNS, u ); #if ( BNS_RAD_SEARCH == 1 ) n = 0; #endif for ( i = 0; i < degree; i ++ ) { /* v = vert[u].neighbor[i]; */ v = GetVertexNeighbor( pBNS, u, i, &iuv ); if ( v == NO_VERTEX ) { continue; /* the atom has only single bonds, ignore it */ } #if ( BNS_RAD_SEARCH == 1 ) if ( !k && bRadSrchMode == RAD_SRCH_FROM_FICT && v/2 <= pBNS->num_atoms ) { continue; /* start from fict. vertices only */ } if ( bRadSearchPrelim && v/2 > pBNS->num_atoms ) { continue; /* during initial add/remove H allow radical movement only through real atoms */ } #endif if ( /* PrevPt[u] != v ** avoid edges of T */ ( SwitchEdge_Vert1(u) != v || SwitchEdge_Vert2(u) != u ) /* avoid edges of T */ && (ret = rescap(pBNS, u, v, iuv)) > 0 ) { /* special treatment to prevent H<->(-) replacement on non-acidic atoms */ /*----------------------------------------------------------------------*/ if ( pBNS->type_TACN ) { if ( bIgnoreVertexNonTACN_atom( pBNS, u, v ) ) { continue; } if ( bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ) ) { continue; } #if ( FIX_KEEP_H_ON_NH_ANION == 1 ) if ( bIsRemovedHfromNHaion( pBNS, u, v ) ) { continue; } #endif #if ( FIX_AVOID_ADP == 1 ) if ( bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ) ) { continue; } #endif } /*----------------------------------------------------------------------*/ b_v = FindBase(v, BasePtr); /* Notation: b_x is a base of x */ if ( b_v == NO_VERTEX ) { /* originally 0 instead of NO_VERTEX */ /* Important note: following "A. Note of Implementing the Algorithm" from the article by Kocay and Stone, all references to PrevPt[a] have been replaced with SwitchEdge[a][0]=SwitchEdge_Vert1(a); to be on a safe side the check whether (SwitchEdge_Vert2(a)==a) has been added. */ /* v is not yet in T or T' -- add it to T and M */ /*PrevPt[v] = u; ** this effectively adds v to T and v' to T' */ QSize ++; ScanQ[QSize] = v; TREE_MARK(v, TREE_IN_1); /* mark v s-reachable (in T) */ TREE_MARK(prim(v), TREE_IN_2); /* mark v' in T' */ /* Switch Edges: If u in T then Pu (a valid su-path) can be constructed by successfully executing u=PrevPt[u] until u=s. For vertices in T' the situation is different. Suppose uv and v'u' are added to a mirror network M creating a blossom: s T (Note: in the code v' is prim(v), T / \ u' is prim(u), etc.) / ... w=x1 / \ u in T, v in T' u=y2 v'=y3 \ / <--- two added edges uv and (uv)'=v'u' -------- X ------------intersection of the edges ------------------------ / \ <--- (the edges intersection in the picture is shown as X) u'=x2 v=x3 u' it T', v' in T \ / T' w'=y1 \ ... \ / t T' Vertices v and u' now become s-reachable; The valid paths to v and u' must use the edges uv and v'u' respectively. For each vertex z in S we define a switch-edge that allows a valid sz-path to be constructed, SwitchEdge[v]=uv and SwitchEdge[u']=v'u'. (We don't know the direction of the edge uv, it may be (u,v) or (v,u). In either case, the complementary edge is indicated by v'u'). Vertex w' also becomes s-reachable when uv is added to M, and a valid sw'-path must use one of uv and v'u'. Therefore we choose one of them, say uv (see below the rule of choosing the switch-edge), and define SwitchEdge[w'] = uv. When the addition of an edge uv to M causes a vertex z to become s-reachable (where z was previously non-reachable), z is placed on the ScanQ, that is, into S. The edge uv is said to be a switch-edge for z. Rule: We choose the the order of the vertices uv to be such that the valid sz-path consists of a valid su-path, followed by edge uv, followed by a valid vz-path. For vertices z in T we can take SwitchEdge[z]=yz where y=PrevPt[z] since it is the edge yz that allows z to be s-reachable. For vertices z not in S we take SwitchEdge[z]=NONE. */ /* v is not yet in T or T' -- add it to T and M */ SwitchEdge_Vert1(v) = u; /* this effectively adds uv and v'u' to M */ SwitchEdge_IEdge(v) = iuv; BasePtr[prim(v)] = v; BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */ #if ( BNS_RAD_SEARCH == 1 ) n ++; #endif } else if ( TREE_IS_S_REACHABLE(prim(v)) /*Is_s_Reachable(prim(v)*/ /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */ /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */ && (SwitchEdge_Vert1(prim(u)) != prim(v) || SwitchEdge_Vert2(prim(u)) != prim(u)) /* avoid edges of T' */ && b_u != b_v && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge )) #if ( FIX_KEEP_H_ON_NH_ANION == 1 ) && !(pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim(v), u )) #endif ) { #if ( BNS_RAD_SEARCH == 1 ) n ++; #endif /* there is now a valid sv-path via u avoiding b_v (unless v==b_v) => u, v, u', and v' now all become part of the same connected component of M[C] */ w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree ); /* this constructed the new blossom and returned its base */ if ( IS_BNS_ERROR( w ) ) { pBD->QSize = QSize; return w; /* error */ } b_u = w; /* the new base of C(u) */ if ( prim(w) == Vertex_t ) { /* t is now s-reachable, a valid augmenting path P exists in M */ delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */ if ( IS_BNS_ERROR( delta ) ) { pBD->QSize = QSize; return delta; /* error */ } #if ( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 ) if ( pBNS->bNotASimplePath || abs(delta) > 1 ) { delta = 0; } #endif if ( delta ) { pBNS->bChangeFlow |= (bChangeFlow & BNS_EF_CHNG_FLOW); } ret = PullFlow( pBNS, SwitchEdge, Vertex_s, Vertex_t, delta, 0, bChangeFlow ); /* augment on a pair of valid st-paths */ pBD->QSize = QSize; return ( IS_BNS_ERROR(ret)? ret : delta ); } } } else if ( IS_BNS_ERROR( ret ) ) { pBD->QSize = QSize; return ret; /* error */ } } #if ( BNS_RAD_SEARCH == 1 ) if ( bRadSearch && !n ) { /* the BNS stopped at u */ n = RegisterRadEndpoint( pBNS, pBD, u); if ( IS_BNS_ERROR( n ) ) { pBD->QSize = QSize; return n; } } #endif k ++; /* advance ScanQ */ } while( k <= QSize ); /* if this point is reached, no valid augmenting path exists, ScanQ contains the set S of all s-reachable vertices and K=[S,S-] is a minimum balanced edge-cut */ /* ClearFlowMarks( vert, num_vert); */ pBD->QSize = QSize; return 0; } /******************************************************************** Blossoms. The vertices of a mirror network M consist T U T'. Intersection T ^ T' is empty. The edges of M consist of switch-edges and their complements because edges are added in complementary pairs, one of which is always a switch-edge. The base of every blossom is in T. Let C(i) be a blossom with base b_i. Since C(i)=C(i)', C(i) contains vertices of T and T'. Since every valid sv-path to v in C(i) contains b_i, b_i is the first s-reachable vertex of C(i). Suppose the mirror network M contains a valid sz-path P(z) to all vertices z in ScanQ. Every vertex of P(z) is s-reachable therefore its vertices are all in blossoms or trivial blossoms. Let z be an s-reachable vertex and P(z) be a valid path in M. Then every valid sz-path in M contains exactly the same sequence of blossom bases as P(z). *********************************************************************/ /*********************************************************************** BasePtr[u] = -2 NO_VERTEX u is not a blossom -1 BLOSSOM_BASE u is the base of its blossom v a vertex closer to the base ************************************************************************/ Vertex FindBase( Vertex u, Vertex *BasePtr ) { if ( BasePtr[u] == NO_VERTEX ) { return NO_VERTEX; } else if ( BasePtr[u] == BLOSSOM_BASE ) { return u; } else { Vertex b; b = FindBase(BasePtr[u], BasePtr ); BasePtr[u] = b; /* path compression */ return b; } } /*********************************************************************************/ /* Returns index of the last path element and the path */ /*********************************************************************************/ int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ) { /* x is the base of a blossom, construct a valid Path of blossom bases to s */ int i = 0; Path[i] = x; while ( x != Vertex_s ) { x = FindBase(SwitchEdge_Vert1(x), BasePtr); if ( ++i < MaxPathLen ) { Path[i] = x; } else { return BNS_WRONG_PARMS; } } return i; } /*********************************************************************************/ /* Make a blossom */ /*********************************************************************************/ Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, Edge *SwitchEdge, Vertex *BasePtr, Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ) { /* In order to find the base of the new blossom, the paths P(u) and P(v') are constructed and compared in order to find the last blossom base they have in common which is reachable on a valid path. Edge uv connects two blossoms, their bases are b_u and b_v. */ Vertex w, z; int len_Pu, len_Pv; int i, j; EdgeIndex izw; len_Pu = FindPathToVertex_s( b_u, SwitchEdge, BasePtr, Pu, max_len_Pu_Pv ); if ( IS_BNS_ERROR( len_Pu ) ) { return len_Pu; } len_Pv = FindPathToVertex_s( b_v, SwitchEdge, BasePtr, Pv, max_len_Pu_Pv ); if ( IS_BNS_ERROR( len_Pv ) ) { return len_Pv; } i = len_Pu; j = len_Pv; /* initially Pu[i] and Pv[j] both equal to s, but their first elements are different */ /* find the last blossom base common to Pu and Pv */ while ( i >= 0 && j >= 0 && Pu[i] == Pv[j] ) { /* was (Pu[i]==Pv[j] && i>=0 && j>=0) => tested Pu[-1], Pv[-1] <- pointed by W.Ihlenfeldt 08-26-2004*/ i --; j --; } i ++; w = Pu[i]; /* w is the last common vertex */ z = SwitchEdge_Vert1(w); izw = SwitchEdge_IEdge(w); /* now extend the blossom if rescap(zw) >= 2 */ while ( w != Vertex_s && rescap(pBNS, z, w, izw) >= 2 ) { i++; w = Pu[i]; z = SwitchEdge_Vert1(w); izw = SwitchEdge_IEdge(w); } /* w is the base of the new blossom */ /* first follow the path Pu from w to b_u */ for ( i = i-1; i >= 0; i -- ) { z = Pu[i]; /* z is the base of the blossom */ BasePtr[z] = w; BasePtr[prim(z)] = w; /* w is the new base of the blossom */ /* z and z' may already be part of a blossom that is being swallowed into a larger blossom. We don't want to change the switch edge in that case. */ if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */) { SwitchEdge_Vert1(prim(z)) = prim(v); /* set the switch edge of z' */ /* SwitchEdge[prim(z)][1] = prim(u); */ SwitchEdge_IEdge(prim(z)) = iuv; (*pQSize) ++; ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ } } /* now follow the path Pv */ for ( j = j; j >= 0; j -- ) { z = Pv[j]; /* z is the base of the blossom */ BasePtr[z] = w; BasePtr[prim(z)] = w; /* w is the new base of the blossom */ /* z and z' may already be part of a blossom that is being swallowed into a larger blossom. We don't want to change the switch edge in that case. */ if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */ ) { SwitchEdge_Vert1(prim(z)) = u; /* set the switch edge of z' */ /* SwitchEdge[prim(z)][1] = v; */ SwitchEdge_IEdge(prim(z)) = iuv; (*pQSize) ++; ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ } } if ( !TREE_IS_ON_SCANQ(prim(w)) /* !IsInScanQ(prim(w))*/ ) { /* add w' to the blossom */ SwitchEdge_Vert1(prim(w)) = u; /* set the switch edge of w' */ /* SwitchEdge[prim(w)][1] = v; */ SwitchEdge_IEdge(prim(w)) = iuv; (*pQSize) ++; ScanQ[*pQSize] = prim(w); /* add w' to ScanQ */ TREE_MARK(prim(w), TREE_IN_2BLOSS); /* mark w' s-reachable */ } return w; } /***************************************************************************** When t is found to be s-reachable, a valid st-path P is known to exist. Its complementary path P' is also valid. Once the residual capacity delta(P) is known, the flow is augmented by calling PullFlow(s,t,delta). It constructs the path P by using the switch-edges. Let uv=SwitchEdge[t]. Then P is given by a valid su-path, followed by the edge uv, followed by a valid vt-path. PullFlow is a recursive procedure that constructs the path and its complement. Let wz=SwitchEdge[y]. PullFlow(x, y, delta) uses the xw- and zy-portions of P (see below). Since it must also augment on P' simultaneously, the zy-portion is replaced by the y'z'-portion. x y' | | P: x--w--z--y P | | P' P': y'-z'-w'-x' | o o \ / w' z \ / o----\ /----o / \ / \ / \ / \/ X \/ /\ / \ /\ / \ w / \ z' / \ \ o---- ----o / Using a switch-edge wz and w'z' \ / to construct P and P' o o | | | | x' y **********************************************************************************/ /*********************************************************************************/ int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ) { /* Augment the flow by delta on all edges on a path P between x and y in the order of the path; AugmentEdge( pBNS, w, z, iwz, delta, 0 ) means the path is in w->z direction AugmentEdge( pBNS, w, z, iwz, delta, 1 ) means the path is in w<-z direction Unlike PullFlow in the paper by Kocay & Stone, here the augmentation always starts at "s", proceeds sequentially through the path end terminates at "t". Since we do not really need the complement path, PullFlow ignores it. */ Vertex w, z; EdgeIndex iwz; int ret = 0; w = SwitchEdge_Vert1(y); z = SwitchEdge_Vert2(y); iwz = SwitchEdge_IEdge(y); if ( bReverse ) { /* P consists of a path from x to w, then wz, then a path from z to y. */ /* z may equal y, in which case z is just PrevPt[y] */ if ( z != y ) { ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ } if ( !IS_BNS_ERROR(ret) ) { ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); } /* Do not augment the complementary path: AugmentEdge( prim(z), prim(w), vert, delta); */ /* w may equal x, in which case there is no need to call PullFlow(x, w) */ if ( w != x && !IS_BNS_ERROR(ret) ) { ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ } } else { /* P consists of a path from x to w, then wz, then a path from z to y. */ /* w may equal x, in which case there is no need to call PullFlow(x, w) */ if ( w != x && !IS_BNS_ERROR(ret) ) { ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ } if ( !IS_BNS_ERROR(ret) ) { ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); } /* z may equal y, in which case z is just PrevPt[y] */ if ( z != y && !IS_BNS_ERROR(ret) ) { ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ } } return ret; } /******************************************************************************** Before augmenting on the two paths, it is necessary to find delta(P). This can be done by following the paths and computing the minimum residual capacity of all edges on P. An edge on both P and P' counts for only half of its actual residual capacity, since augmentng on P by delta will simutaneously reduce its capacity on P' by delta. The path P can only be followed by using the switch-edges, as in PullFlow(...). FindPathCap( x, y, delta ) is a recursive procedure that finds the residual capacity on the portion of P between x and y. delta is the minimum capacity found so far along the path. ********************************************************************************/ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ) { /* find the minimum residual capacity of all edges between x and y in a valid st-path P. delta is the minimum found so far the vertices occur in order s,...,x,...,y,...,t along P the vertices occur in order s,...,y',...,x',...,t along P' */ Vertex w, z, iwz; int cap, delta2; static int level; if ( level ++ > 50 ) { #ifdef _DEBUG int stop = 1; #else ; #endif } w = SwitchEdge_Vert1(y); z = SwitchEdge_Vert2(y); /* wz is on the path P */ iwz = SwitchEdge_IEdge(y); /* edge index */ /* rescap_mark() detects edges passed 2 times and reduces rescap */ cap = rescap_mark( pBNS, w, z, iwz ); if ( IS_BNS_ERROR( cap ) ) { level --; return cap; } if ( cap < delta ) { delta = cap; } /* P consists of a path from x to w, then wz, then a path from z to y */ if ( w != x ) { delta2 = FindPathCap( pBNS, SwitchEdge, x, w, delta ); delta = inchi_min( delta2, delta ); } if ( z != y ) { delta2 = FindPathCap( pBNS, SwitchEdge, prim(y), prim(z), delta ); delta = inchi_min( delta2, delta ); } level --; return delta; } /*********************************************************************************/ /* BT = bond types */ #define BT_ALTERN_BOND 1 /* 1-2, possibly stereo */ #define BT_OTHER_ALTERN_BOND 2 /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */ #define BT_ALTERN_NS_BOND 4 #define BT_TAUTOM_BOND 8 #define BT_ALTERN_UNKN_BOND 16 #define BT_IGNORE_BOND 0 #define BT_NONSTEREO_MASK (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND) #define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND) #define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND) /* BNS members redefinitions for finding non-stereo bonds */ /* BNS_EDGE */ #define nBlockNumberAltBns flow /* internal variable of the DFS traversal: mark traversed bonds */ #define nNumAtInBlockAltBns cap #define nBondTypeInpAltBns pass /* 0=>cannot be stereo at all, 1=>alt or taut non-stereo, 2=>can be stereo */ #define nBondNonStereoAltBns cap /* 1=>found to be non-stereogenic although BondTypeInp=2; 0=>as in BondTypeInp */ #if ( BNS_MARK_ONLY_BLOCKS == 1 ) /* { */ /* BNS_VERTEX */ #define bCutVertexAltBns st_edge.cap0 /* cut-vertex flag */ #define nRingSystemAltBns st_edge.cap /* ordering number of a ring system */ #define nNumAtInRingSystemAltBns st_edge.flow0 /* number of vertices in a ring system */ #define nBlockSystemAltBns st_edge.flow /* result of the DFS traversal: even cirquit must be within one block */ #endif /* } */ #define valenceAltBns num_adj_edges /*********************************************************************************/ /********************************************************************************/ int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ) { AT_NUMB *nStackAtom = NULL; int nTopStackAtom; AT_NUMB *nRingStack = NULL; int nTopRingStack; /* was AT_NUMB */ AT_NUMB *nBondStack = NULL; int nTopBondStack; AT_NUMB *nDfsNumber = NULL; AT_NUMB *nLowNumber = NULL; S_CHAR *cNeighNumb = NULL; AT_NUMB nDfs; AT_NUMB nNumAtInRingSystem; int i, j, u, w, start, nNumRingSystems, nNumStartChildren; BNS_VERTEX *at = pBNS->vert; BNS_EDGE *bond = pBNS->edge; int num_atoms = pBNS->num_atoms; int num_edges = pBNS->num_bonds; /* allocate arrays */ nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); nBondStack = (AT_NUMB *) (num_edges? inchi_malloc(num_edges*sizeof(nBondStack[0])):NULL); /* special case: no bonds 2006-03 */ cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); /* check allocation */ if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack && num_edges || !cNeighNumb ) { nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ goto exit_function; } /******************************************** * * Find Cut-vertices & Blocks * * 1\ /5 has 3 blocks (maximal subgraphs that * Example: | >3--4< | are nonseparable by deleting a single vertex): * 2/ \6 (1,2,3, has 3 bonds), (3,4, has 1 bond), and (4,5,6, has 3 bonds) * * Cut-vertices or articulation points are * intersections of the blocks: points 3 and 4. ********************************************/ /******************************************************** RingSystemAlt are atoms connected by alternating bonds (as must be indicated in bIsAltBond()): BOND_ALTERN BOND_ALT_123 BOND_ALT_13 BOND_ALT_23 Since other bonds may be present, we possibly need to restart to move to another component *********************************************************/ nNumRingSystems = 0; memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); for ( start = 0; start < num_atoms; start ++ ) { if ( nDfsNumber[start] ) continue; for ( i = 0; i < at[start].valenceAltBns; i ++ ) { if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALTERN_BOND ) goto found_alt; } continue; found_alt: /* initiation */ u = start; /* start atom */ nDfs = 0; nTopStackAtom =-1; nTopRingStack =-1; nTopBondStack =-1; memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nLowNumber[u] = nDfsNumber[u] = ++nDfs; nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nNumStartChildren = 0; do { /* advance */ /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ /* replaced due to missing sequence point */ while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) { cNeighNumb[i] ++; if ( !(bond[w=at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK) ) { continue; } u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); if ( !nDfsNumber[u] ) { /* tree edge, 1st visit -- advance */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nBondStack[++nTopBondStack] = (AT_NUMB)w; nLowNumber[u] = nDfsNumber[u] = ++nDfs; nNumStartChildren += (i == start); } else if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ /* back edge: u is not a predecessor of i */ if ( nDfsNumber[u] < nDfsNumber[i] ) { /* Back edge, 1st visit: u is ancestor of i. Save and compare */ nBondStack[++nTopBondStack] = (AT_NUMB)w; if ( nLowNumber[i] > nDfsNumber[u] ) { nLowNumber[i] = nDfsNumber[u]; } } } } cNeighNumb[i] = 0; /* back up */ if ( i != start ) { u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ if ( nLowNumber[i] >= nDfsNumber[u] ) { /* output the block; the block was entered through its first bond u->i */ nNumRingSystems ++; /*at[u].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ nNumAtInRingSystem = 1; /* if ( u != start || nNumStartChildren > 1 ) { at[u].bCutVertexAltBns += 1; // mark cut-vertex (articulation point) } */ while ( nTopRingStack >= 0 ) { j = nRingStack[nTopRingStack--]; /*at[j].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ nNumAtInRingSystem ++; if ( i == j ) { break; } } while ( nTopBondStack >= 0 ) { w = nBondStack[nTopBondStack--]; bond[w].nBlockNumberAltBns = nNumRingSystems; /* mark the bond */ bond[w].nNumAtInBlockAltBns = nNumAtInRingSystem; if ( i == bond[w].neighbor1 && u == (i ^ bond[w].neighbor12) || u == bond[w].neighbor1 && i == (u ^ bond[w].neighbor12)) { break; } } } else if ( nLowNumber[u] > nLowNumber[i] ) { /* inherit */ nLowNumber[u] = nLowNumber[i]; } } } while ( --nTopStackAtom >= 0 ); } #if ( BNS_MARK_ONLY_BLOCKS != 1 ) /* { */ /******************************************** * * Find Ring Systems * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. * ********************************************/ /* initiation */ nNumRingSystems = 0; for ( start = 0; start < num_atoms; start ++ ) { if ( at[start].nRingSystemAltBns ) continue; for ( i = 0; i < at[start].valenceAltBns; i ++ ) { if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALT_BOND_MASK ) goto found_alt2; } continue; found_alt2: u = start; /* start atom */ nDfs = 0; nTopStackAtom =-1; nTopRingStack =-1; memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nLowNumber[u] = nDfsNumber[u] = ++nDfs; nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; do { /* advance */ advance_ring: /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ /* replaced due to missing sequence point */ if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) { cNeighNumb[i] ++; if ( !(bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND) ) { goto advance_ring; } u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); if ( !nDfsNumber[u] ) { /* tree edge, 1st visit -- advance */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nLowNumber[u] = nDfsNumber[u] = ++nDfs; } else if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* back edge: u is not a predecessor of i */ if ( nDfsNumber[u] < nDfsNumber[i] ) { /* Back edge, 1st visit: u is ancestor of i. Compare */ if ( nLowNumber[i] > nDfsNumber[u] ) { nLowNumber[i] = nDfsNumber[u]; } } } goto advance_ring; } else { cNeighNumb[i] = 0; } /* back up */ if ( nDfsNumber[i] == nLowNumber[i] ) { /* found a ring system */ nNumRingSystems ++; /* unwind nRingStack[] down to i */ /* count atoms in a ring system */ for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { nNumAtInRingSystem ++; if ( i == (int)nRingStack[j] ) { break; } } while ( nTopRingStack >= 0 ) { j = (int)nRingStack[nTopRingStack--]; at[j].nRingSystemAltBns = (AT_NUMB)nNumRingSystems; /* ring system id */ at[j].nNumAtInRingSystemAltBns = nNumAtInRingSystem; if ( i == j ) { /* reached atom on the top of nStackAtom[] stack */ break; } } } else if ( nTopStackAtom > 0 ) { j = (int)nStackAtom[nTopStackAtom-1]; /* inherit nLowNumber */ if ( nLowNumber[j] > nLowNumber[i] ) { nLowNumber[j] = nLowNumber[i]; } } } while ( --nTopStackAtom >= 0 ); } #endif /* } BNS_MARK_ONLY_BLOCKS != 1 */ exit_function: if ( nStackAtom ) inchi_free( nStackAtom ); if ( nRingStack ) inchi_free( nRingStack ); if ( nDfsNumber ) inchi_free( nDfsNumber ); if ( nLowNumber ) inchi_free( nLowNumber ); if ( nBondStack ) inchi_free( nBondStack ); if ( cNeighNumb ) inchi_free( cNeighNumb ); return nNumRingSystems; } /*****************************************************************************/ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) { Vertex v, v2; int ret, bond_type, num_to_test, j; BNS_EDGE *pBond; BNS_VERTEX *pAtom; /* strip all t-groups and c-groups */ num_to_test = 0; if ( bUnknAltAsNoStereo ) { for ( j = 0; j < pBNS->num_edges; j ++ ) { pBNS->edge[j].pass = 0; } } ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); if ( ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { ret = BNS_REINIT_ERR; goto exit_function; } /* eliminate bonds and fix st-caps */ for ( v = 0; v < num_atoms; v ++ ) { pAtom = pBNS->vert + v; for ( j = 0; j < pAtom->valenceAltBns; j ++ ) { pBond = pBNS->edge + pAtom->iedge[j]; if ( pBond->neighbor1 == v ) { bond_type = (at[v].bond_type[j] & BOND_TYPE_MASK); v2 = pBond->neighbor12 ^ v; if ( at[v].endpoint || at[v2].endpoint ) { bond_type = 0; /* any bond to an endpoint considered non-stereogenic */ } #if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) if ( bUnknAltAsNoStereo ) { if ( bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER ) { bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */ } } #endif switch ( bond_type ) { case BOND_ALTERN : pBond->nBondTypeInpAltBns = BT_ALTERN_BOND; num_to_test ++; break; case BOND_ALT_123: case BOND_ALT_13 : case BOND_ALT_23 : pBond->nBondTypeInpAltBns = BT_OTHER_ALTERN_BOND; break; case BOND_TAUTOM : pBond->nBondTypeInpAltBns = BT_TAUTOM_BOND; break; case BOND_ALT12NS: pBond->nBondTypeInpAltBns = BT_ALTERN_NS_BOND; break; case 0: case BOND_SINGLE : case BOND_DOUBLE : case BOND_TRIPLE : pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; break; default: pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; break; } pBond->nBondNonStereoAltBns = pBond->nBlockNumberAltBns = pBond->nNumAtInBlockAltBns = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) pBond->forbidden &= pBNS->edge_forbidden_mask; #endif } } pAtom->bCutVertexAltBns = pAtom->nRingSystemAltBns = pAtom->nNumAtInRingSystemAltBns = pAtom->nBlockSystemAltBns = 0; } return num_to_test; exit_function: return ret; } /*****************************************************************************/ int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) { int num_bonds = pBNS->num_bonds; int ret; int ibond, ib1, ib2; BNS_EDGE *pBond; Vertex iat1, iat2; ret = 0; if ( pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { ret = BNS_REINIT_ERR; goto exit_function; } if ( bUnknAltAsNoStereo ) { for ( ibond=0; ibond < num_bonds; ibond ++ ) { pBond = pBNS->edge + ibond; if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { continue; } iat1 = pBond->neighbor1; iat2 = pBond->neighbor12 ^ iat1; ib1 = pBond->neigh_ord[0]; ib2 = pBond->neigh_ord[1]; if ( /* alt bond non-adjacent to a taut. endpoint: */ (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || /* alt bond adjacent to a taut. endpoint: */ (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) ) { if ( (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) { /* bond_type = BOND_ALT12NS; */ at[iat1].bond_stereo[ib1] = at[iat2].bond_stereo[ib2] =STEREO_DBLE_EITHER; ret ++; } } } } else { for ( ibond=0; ibond < num_bonds; ibond ++ ) { pBond = pBNS->edge + ibond; if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { continue; } iat1 = pBond->neighbor1; iat2 = pBond->neighbor12 ^ iat1; ib1 = pBond->neigh_ord[0]; ib2 = pBond->neigh_ord[1]; if ( /* alt bond non-adjacent to a taut. endpoint: */ (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || /* alt bond adjacent to a taut. endpoint: */ (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) ) { at[iat1].bond_type[ib1] = at[iat2].bond_type[ib2] =BOND_ALT12NS; ret ++; } } } exit_function: return ret; } #if ( READ_INCHI_STRING == 1 ) /*****************************************************************************/ #ifndef RI_ERR_ALLOC /* from ichirvrs.h */ #define RI_ERR_ALLOC (-1) #define RI_ERR_SYNTAX (-2) #define RI_ERR_PROGR (-3) #endif /*****************************************************************************/ int bHasChargedNeighbor( inp_ATOM *at, int iat ) { int i; for( i = 0; i < at[iat].valence; i ++ ) { if ( at[(int)at[iat].neighbor[i]].charge ) return 1; } return 0; } /********************************************************************************* *num_protons_to_add = nToBeRemovedByNormFromRevrs nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be added by the Normalization of the Reconstructed Structure nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by the InChI Normalization of the Reconstructed Structure OrigStruct -> NormOrig + n(orig)*H(+) RevrStruct -> NormRevr + n(revr)*H(+) nToBeRemovedByNormFromRevrs = n(orig) - n(revr) [each may be negative] n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means: ----------------------------------------------------------- - Too many protons were added by the Normalization to the Reconstructed Structure (a) n(revr) < 0 => protons were added while they should not have been added; Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms on the condition ADP cannot add in another way; (b) n(orig) > n(revr) => 0 => too few protons were removed Solution: (the easiest) attach H(+) to =O or -N< or -N= Solution: move (+) from N or OH to an atom adjacent to (-) charge or to an atom that is not N. n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means: ----------------------------------------------------------- - Too few protons were added by the Normalization to the Reconstructed Stucture (a) n(orig) < 0 => protons were not added while they should have been added; Solution: move (-) to O by replacing =O with -O(-) (b) 0 <= n(orig) < n(revr) => too many protons were removed Note: it is critically important to takr into account cumbersome Normalization Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP However, if N(+) is present then ADP will always try to remove a proton *********************************************************************************/ int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, int num_tg, int nChargeRevrs, int nChargeInChI ) { int i, j, ret = 0; int nAtTypeTotals[ATTOT_ARRAY_LEN]; int num_prot = *num_protons_to_add; int type, mask, bSuccess, nTotCharge, nNumSuccess = 0; int max_j_Aa=-1, max_j_Ar=-1; /* for the reference: #define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ FLAG_PROTON_NP_HARD_REMOVED | \ FLAG_PROTON_AC_SIMPLE_ADDED | \ FLAG_PROTON_AC_SIMPLE_REMOVED | \ FLAG_PROTON_AC_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_ADDED | \ FLAG_PROTON_SINGLE_REMOVED | \ FLAG_PROTON_CHARGE_CANCEL ) #define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_ADDED ) */ /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal a typical case is (=) on N or O instead of C(-) if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition */ mark_at_type( at, num_atoms, nAtTypeTotals ); for ( i = nTotCharge = 0; i < num_atoms; i ++ ) { nTotCharge += at[i].charge; } /* size for SimpleAddAcidicProtons() */ for ( max_j_Aa = 0; AaTypMask[2*max_j_Aa]; max_j_Aa ++ ) ; /* size for SimpleRemoveAcidicProtons */ for ( max_j_Ar = 0; ArTypMask[2*max_j_Ar]; max_j_Ar ++ ) ; if ( num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { /* remove proton(s) */ /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */ for ( i = 0; i < num_atoms && num_prot; i ++ ) { /* choose an atom */ if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge || !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ) ) { continue; } /* try to remove a proton and check whether InChI would add it back */ at[i].charge --; at[i].num_H --; type = GetAtomChargeType( at, i, NULL, &mask, 0 ); at[i].charge ++; at[i].num_H ++; if ( type ) { for ( bSuccess = 0, j = 0; j < max_j_Aa; j ++ ) { if ( bSuccess = (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { break; /* the proton may be added to this atom */ } } if ( bSuccess ) { type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ at[i].charge --; at[i].num_H --; type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ num_prot ++; /* success */ nNumSuccess ++; } } } } if ( num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { /* alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */ int endp2, centp, k, i0, k0; for ( i = 0; i < num_atoms; i ++ ) { /* choose an atom */ if ( !at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity || at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ) ) { continue; } /* looking for tautomeric =O */ if ( 1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H || 2 != get_endpoint_valence( at[i].el_number ) ) { continue; } centp = at[i].neighbor[0]; if ( at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ) ) { continue; } /* found a possible centerpoint, looking for -NH endpoint */ for ( k = 0; k < at[centp].valence; k ++ ) { if ( at[centp].bond_type[k] != BOND_TYPE_SINGLE ) { continue; } endp2 = at[centp].neighbor[k]; if ( at[endp2].endpoint != at[i].endpoint || !at[endp2].num_H || at[endp2].charge || at[endp2].sb_parity[0] || at[endp2].p_parity || at[endp2].valence != at[endp2].chem_bonds_valence || 3 != at[endp2].chem_bonds_valence + at[endp2].num_H || 3 != get_endpoint_valence( at[endp2].el_number ) ) { continue; } /* find bonds in reciprocal ajacency lists */ for ( i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0 ++ ) ; for ( k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0 ++ ) ; if ( i0 == at[centp].valence || k0 == at[endp2].valence ) { return RI_ERR_PROGR; } /* -NH has been found */ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */ at[i].bond_type[0] --; at[centp].bond_type[i0] --; at[i].chem_bonds_valence --; at[i].charge --; at[endp2].bond_type[k0] ++; at[centp].bond_type[k] ++; at[endp2].chem_bonds_valence ++; at[endp2].num_H --; num_prot ++; nNumSuccess ++; type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */ type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */ } } } if ( num_prot > 0 ) { /* add protons */ /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */ for ( i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr >= 0; i ++ ) { /* choose an atom */ if ( at[i].sb_parity[0] || at[i].p_parity || at[i].num_H || at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ) ) { continue; } /* try to add a proton and check whether InChI would remove it back */ at[i].charge ++; at[i].num_H ++; type = GetAtomChargeType( at, i, NULL, &mask, 0 ); at[i].charge --; at[i].num_H --; if ( type ) { for ( bSuccess = 0, j = 0; j < max_j_Ar; j ++ ) { if ( bSuccess = (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { break; } } if ( bSuccess ) { type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ at[i].charge ++; at[i].num_H ++; type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ num_prot --; /* success */ nNumSuccess ++; } } } /* 2. Use test from SimpleRemoveHplusNPO() */ for ( i = 0; i < num_atoms && num_prot; i ++ ) { /* choose an atom */ if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ) ) { continue; } /* try to add a proton and check whether InChI would remove it back */ at[i].num_H ++; at[i].charge ++; bSuccess = (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && (PR_SIMPLE_MSK & mask ); at[i].num_H --; /* failed */ at[i].charge --; if ( bSuccess ) { type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ at[i].num_H ++; at[i].charge ++; type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ num_prot --; /* succeeded */ nNumSuccess ++; } } } if ( num_prot < 0 && (bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED) && 1 == num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { /* try to remove protons from tautomeric N (specific ADP must be present) */ int nNumAcceptors_DB_O=0, nNumDonors_SB_NH=0, num_max, num_success; for ( i = 0; i < num_atoms; i ++ ) { /* choose an atom */ if ( !at[i].endpoint || at[i].radical || at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) { continue; } type = GetAtomChargeType( at, i, NULL, &mask, 0 ); if ( (type & AA_HARD_TYP_CO) && (mask & AA_HARD_MSK_CO) ) { nNumAcceptors_DB_O ++; } else if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && at[i].valence == at[i].chem_bonds_valence ) { nNumDonors_SB_NH ++; } } num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH ); for ( i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i ++ ) { /* choose an atom */ if ( !at[i].endpoint|| at[i].radical || at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) { continue; } type = GetAtomChargeType( at, i, NULL, &mask, 0 ); if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && at[i].valence == at[i].chem_bonds_valence ) { type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ at[i].num_H --; at[i].charge --; type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ num_prot ++; num_success ++; nNumSuccess ++; } } } /*exit_function:*/ *num_protons_to_add = num_prot; return ret<0? ret : nNumSuccess; } /*****************************************************************************/ int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ) { int i, j, k, n, ret = 0; int nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H; int iCurIso; /* 0=> 1H, 1=> D, 2=> T */ int iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */ static U_CHAR el_number_H = 0; /* distribute isotopes from heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */ iCurMode1 = 0; iCurMode2 = num_tg ? 1 : 0; if ( !el_number_H ) { el_number_H = (U_CHAR) get_periodic_table_number( "H" ); } for ( iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode ++ ) { for ( iCurIso = 2; 0 <= iCurIso; iCurIso -- ) { /* check for isotopic H to add */ if ( !num_protons_to_add[iCurIso] ) { continue; } if ( 0 > num_protons_to_add[iCurIso] ) { ret = RI_ERR_PROGR; goto exit_function; } /* limits for atom scanning */ min_at = 0; max_at = num_atoms; /* cycle withio the limits */ for ( i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i ++ ) { /* pick an atom */ if ( iCurMode ) { if ( at[i].endpoint ) j = i; /* atom number */ else continue; } else if ( !at[i].endpoint && 1 == bHeteroAtomMayHaveXchgIsoH( at, i ) ) { /* atom number */ j = i; } else if ( at[i].el_number == el_number_H && at[i].charge == 1 && !at[i].valence && !at[i].radical && !at[i].iso_atw_diff ) { /* proton, not isotopic; make it isotopic */ at[i].iso_atw_diff = 1 + iCurIso; num_protons_to_add[iCurIso] --; nNumSuccess ++; continue; } else { continue; } /* j is the atom number */ /* count implicit H */ num_H = at[j].num_H; num_iso_H = NUM_ISO_H(at,j); while ( num_H > 0 && num_protons_to_add[iCurIso] > 0 ) { /* substitute one implicit H with an isotopic atom H */ at[j].num_iso_H[iCurIso] ++; at[j].num_H --; num_protons_to_add[iCurIso] --; num_H --; num_iso_H ++; nNumSuccess ++; } /* count explicit H */ num_expl_H = num_expl_iso_H = 0; for ( k = 0; k < at[j].valence && num_atoms <= (n=at[j].neighbor[k]); k ++ ) { num_expl_H += (0 == at[n].iso_atw_diff); num_expl_iso_H += (0 != at[n].iso_atw_diff); } while ( num_expl_H > 0 && num_protons_to_add[iCurIso] > 0 ) { /* substitute one explicit H with an isotopic atom H */ n = at[j].neighbor[num_expl_H]; if ( at[n].iso_atw_diff ) { ret = RI_ERR_PROGR; goto exit_function; } at[n].iso_atw_diff = 1 + iCurIso; num_expl_H --; num_expl_iso_H ++; num_protons_to_add[iCurIso] --; nNumSuccess ++; } } } } exit_function: return ret<0? ret : nNumSuccess; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichi_bns.h000066400000000000000000000553471271037650300235160ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHI_BNS_H___ #define __INCHI_BNS_H___ #define BN_MAX_ALTP 16 /*#define MAX_VERTEX 1024*/ /* including s; if vert[] has num_vert then MAX_VERTEX has (2*num_vert+2+FIRST_INDX) elements */ /* forward declarations */ struct BalancedNetworkStructure; struct BalancedNetworkData; struct tagTautomerGroupsInfo; struct tagChargeGroupsInfo; struct BN_AtomsAtTautGroup; struct tagSaltChargeCandidate; /* define BNS types */ typedef S_SHORT Vertex; typedef S_SHORT EdgeIndex; typedef S_SHORT Edge[2]; /* Edge[0] = vertex1, Edge[1] = iedge or -(1+vertex1) if vertex2 = s or t */ typedef S_SHORT BNS_IEDGE; typedef S_SHORT EdgeFlow; typedef S_SHORT VertexFlow; #define BNS_EDGE_FORBIDDEN_MASK 1 #define BNS_EDGE_FORBIDDEN_TEMP 2 #define BNS_EDGE_FORBIDDEN_TEST 4 /* BNS vertex types */ #define BNS_VERT_TYPE_ATOM 0x0001 #define BNS_VERT_TYPE_ENDPOINT 0x0002 /* attribute */ #define BNS_VERT_TYPE_TGROUP 0x0004 #define BNS_VERT_TYPE_C_POINT 0x0008 #define BNS_VERT_TYPE_C_GROUP 0x0010 #define BNS_VERT_TYPE_SUPER_TGROUP 0x0020 #define BNS_VERT_TYPE_TEMP 0x0040 #define BNS_VERT_TYPE__AUX 0x0080 /* vertex added to build charge substructures */ #define BNS_VERT_TYPE_C_NEGATIVE 0x0100 /* negative charge group; attribute, should be used with BNS_VERT_TYPE_C_GROUP */ #define BNS_VERT_TYPE_ACID 0x0200 /* only for this type are allowed paths: t_group-atom-c_group_neg (path_TACN) */ #define BNS_VERT_TYPE_CARBON_GR 0x0400 /* charge of carbon atom; should be used with BNS_VT_C_POS, BNS_VT_C_NEG */ #define BNS_VERT_TYPE_METAL_GR 0x0800 /* metal atom group; may be used alone or with BNS_VT_M_POS, BNS_VT_M_NEG */ #define BNS_VERT_TYPE_ANY_GROUP (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_SUPER_TGROUP) /* InChI->Structure */ #define BNS_VT_C_POS BNS_VERT_TYPE_C_GROUP /* positive charge group, heteroat */ #define BNS_VT_C_NEG (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE) /* negative charge group, heteroat */ #define BNS_VT_C_POS_C (BNS_VT_C_POS | BNS_VERT_TYPE_CARBON_GR) /* positive charge group, C, Si, Ge, Sn */ #define BNS_VT_C_NEG_C (BNS_VT_C_NEG | BNS_VERT_TYPE_CARBON_GR) /* negative charge group, C, Si, Ge, Sn */ #define BNS_VT_C_POS_M (BNS_VT_C_POS | BNS_VERT_TYPE_METAL_GR) /* positive charge group, metal */ #define BNS_VT_C_NEG_M (BNS_VT_C_NEG | BNS_VERT_TYPE_METAL_GR) /* negative charge group, metal */ #define BNS_VT_M_GROUP BNS_VERT_TYPE_METAL_GR /* metal-group, flower vertex */ #define BNS_VT_C_POS_ALL (BNS_VERT_TYPE_SUPER_TGROUP | BNS_VERT_TYPE_C_GROUP) /* supergroup (+) */ #define BNS_VT_C_NEG_ALL (BNS_VT_C_POS_ALL | BNS_VERT_TYPE_C_NEGATIVE) /* supergroup (-) */ #define BNS_VT_CHRG_STRUCT (BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP) /* ChargeStruct vertex */ #define BNS_VT_YVCONNECTOR BNS_VERT_TYPE__AUX /* group connection */ #define IS_BNS_VT_C_OR_CSUPER_GR(X) ((X) & BNS_VT_C_POS) #define IS_BNS_VT_C_GR(X) (((X) & BNS_VT_C_POS_ALL) == BNS_VERT_TYPE_C_GROUP) #define IS_BNS_VT_CM_GR(X) (((X) & BNS_VT_C_POS_M) == BNS_VT_C_POS_M) /* metal charge group */ #define IS_BNS_VT_M_GR(X) ((X) == BNS_VERT_TYPE_METAL_GR ) /* metal flower base or vertices */ #define IS_BNS_VT_YVCONNECTOR(X) (((X) & BNS_VERT_TYPE__AUX) && !((X) & BNS_VERT_TYPE_TEMP)) #define IS_BNS_VT_CHRG_STRUCT(X) (((X) & BNS_VERT_TYPE__AUX) && ((X) & BNS_VERT_TYPE_TEMP)) #define IS_BNS_VT_ATOM(X) ((X) & BNS_VERT_TYPE_ATOM) #define BNS_ADD_SUPER_TGROUP 1 /* reserve one more edge for a t-group to connect to a single super-t-group */ #define NUM_KINDS_OF_GROUPS 2 /* 1 accounts for t-group kind, one more 1 accounts for c-group kind */ #define BNS_ADD_ATOMS 2 /* max. number of fictitious atoms to add (except t-gtoups) */ #define BNS_ADD_EDGES 1 /* max. number of edges to add to each atom (except edges to a t-group or c-group) */ typedef enum tagAltPathConst { iALTP_MAX_LEN, /* 0 */ iALTP_FLOW, /* 1 */ iALTP_PATH_LEN, /* 2 */ iALTP_START_ATOM, /* 3 */ iALTP_END_ATOM, /* 4 */ iALTP_NEIGHBOR, /* 5 */ iALTP_HDR_LEN = iALTP_NEIGHBOR } ALT_CONST; #define ALTP_PATH_LEN(altp) (altp)[iALTP_PATH_LEN].number /* number of bonds = number of atoms-1*/ #define ALTP_END_ATOM(altp) (altp)[iALTP_END_ATOM].number #define ALTP_START_ATOM(altp) (altp)[iALTP_START_ATOM].number #define ALTP_THIS_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[0] /* 0 <= X < path_len */ #define ALTP_NEXT_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[1] #define ALTP_CUR_THIS_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[0] /* 0 <= X < path_len */ #define ALTP_CUR_NEXT_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[1] #define ALTP_NEXT(altp) (++ALTP_PATH_LEN(altp)) #define ALTP_PREV(altp) (--ALTP_PATH_LEN(altp)) #define ALTP_MAY_ADD(altp) (iALTP_NEIGHBOR + (altp)[iALTP_PATH_LEN].number < (altp)[iALTP_MAX_LEN].number) #define ALTP_ALLOCATED_LEN(altp) (altp)[iALTP_MAX_LEN].number #define ALTP_DELTA(altp) (altp)[iALTP_FLOW].flow[0] #define ALTP_OVERFLOW(altp) (altp)[iALTP_FLOW].flow[1] #define Vertex_s 0 #define Vertex_t 1 #define NO_VERTEX -2 #define BLOSSOM_BASE -1 #define ADD_CAPACITY_RADICAL 1 /* add capacity to radical */ #define MAX_BOND_EDGE_CAP 2 /* triple bond */ #define AROM_BOND_EDGE_CAP 1 #define MAX_TGROUP_EDGE_CAP 2 /* -NH2 provides max. capacity */ /* edge to s or t */ #define EDGE_FLOW_ST_MASK 0x3fff /* mask for flow */ #define EDGE_FLOW_ST_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ /* edges between other vertices */ /* EdgeFlow defined as S_SHORT; change from S_CHAR made 9-23-2005 */ #define EDGE_FLOW_MASK 0x3fff /* mask for flow */ #define EDGE_FLOW_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ /*********************************************************************************/ #if ( ADD_CAPACITY_RADICAL == 1 ) /* { */ /* -- do not treat triplets as moving dots -- 2004-02-18 -- #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ (((X).radical==RADICAL_DOUBLET)+2*((X).radical==RADICAL_TRIPLET)):0)) */ #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ (((X).radical==RADICAL_DOUBLET)/*+2*((X).radical==RADICAL_TRIPLET)*/):0)) #else /* } ADD_CAPACITY_RADICAL { */ #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence) #endif /* } ADD_CAPACITY_RADICAL */ /**************************** BNS_EDGE ************************************/ typedef struct BnsEdge { AT_NUMB neighbor1; /* the smaller neighbor */ AT_NUMB neighbor12; /* neighbor1 ^ neighbor2 */ AT_NUMB neigh_ord[2]; /* ordering number of the neighbor: [0]: atneighbor */ EdgeFlow cap; /* Edge capacity */ EdgeFlow cap0; /* Initial edge capacity */ EdgeFlow flow; /* Edge flow */ EdgeFlow flow0; /* Initial flow */ /*S_CHAR delta; */ S_CHAR pass; /* number of times changed in AugmentEdge() */ S_CHAR forbidden; } BNS_EDGE; /**************************** BNS_ST_EDGE ************************************/ typedef struct BnsStEdge { VertexFlow cap; /* Edge capacity */ VertexFlow cap0; /* Initial edge capacity */ VertexFlow flow; /* Edge flow */ VertexFlow flow0; /* Initial edge flow */ S_CHAR pass; /* number of times changed in AugmentEdge() */ /*S_CHAR delta; */ } BNS_ST_EDGE; /**************************** BNS_VERTEX ************************************/ typedef struct BnsVertex { BNS_ST_EDGE st_edge; /* 0,1 capacity and flow of the edge to s or t */ AT_NUMB type; /* 2, atom, t-group, or added atom: BNS_VERT_TYPE_TGROUP, etc. */ AT_NUMB num_adj_edges; /* 3, actual number of neighbors incl. t-groups, excl. s or t */ AT_NUMB max_adj_edges; /* 4, including reserved */ /*S_CHAR path_neigh[2];*/ /* 5 found path information */ /* indexes of Edges */ BNS_IEDGE *iedge; /* 6 a pointer to the array of edge indexes adjacent to this vertex */ }BNS_VERTEX; /**************************** BNS_ALT_PATH ************************************/ typedef union BnsAltPath { VertexFlow flow[2]; Vertex number; AT_NUMB ineigh[2]; } BNS_ALT_PATH; /**************************** BN_STRUCT ************************************/ typedef struct BalancedNetworkStructure { int num_atoms; /* number of real atoms */ /*int len_atoms; */ /* size of filled with real atoms portion of the BNS_VERTEX data */ int num_added_atoms; /* number of added fictitious atoms */ int nMaxAddAtoms; /* max. number of atoms to add (not including t-groups) */ int num_c_groups; /* number of added c-groups */ int num_t_groups; /* number of added t-groups */ int num_vertices; /* total number currently in effect; includes t-groups and added atoms */ /*int len_vertices; */ /* allocation size for BNS_VERTEX data */ int num_bonds; /* number of real bonds/2 = number of edges between real atoms */ int num_edges; /* number of currently in effect */ int num_iedges; /* added 9-16-2005; used only in InChI Reversing */ int num_added_edges; /* number of added edges (not including edges to t-groups) */ int nMaxAddEdges; /* max. number edges of add to each atom (not including edges to t-groups) */ int max_vertices; /* allocation size for BNS_VERTEX structures */ int max_edges; /* allocation size for edge[]; iedge has length 2*max_edges */ int max_iedges; /* allocation size for iedge */ int tot_st_cap; /* not used, only calculated */ int tot_st_flow; /* not used, only calculated */ int len_alt_path; /* length of alt_path[] */ int bNotASimplePath; /* alternating path traversed same bond 2 times in opposite directions */ int bChangeFlow; /* actually change flow */ BNS_VERTEX *vert; /* vertices */ BNS_EDGE *edge; /* edges */ BNS_IEDGE *iedge; BNS_ALT_PATH *alt_path; /* current altp[] element */ BNS_ALT_PATH *altp[BN_MAX_ALTP]; /* keep alt. paths */ int max_altp; int num_altp; INCHI_MODE *pbTautFlags; /* carry it through all functions; never NULL */ INCHI_MODE *pbTautFlagsDone; /* carry it through all functions; never NULL */ AT_NUMB type_TACN; /* BNS_VERT_TYPE_ACID: if non-zero than only for it path type_T-type_TACN-type_CN allowed */ AT_NUMB type_T; /* BNS_VERT_TYPE_TGROUP */ AT_NUMB type_CN; /* BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE */ S_CHAR edge_forbidden_mask; } BN_STRUCT; /********************* BN_DATA *******************************************/ typedef enum tagBnsRadSrchMode { RAD_SRCH_NORM = 0, /* normal search for normalization */ RAD_SRCH_FROM_FICT = 1 /* search from fict. vertices to atoms */ } BRS_MODE; typedef struct BalancedNetworkData { Vertex *BasePtr; /*[MAX_VERTEX]; pointer toward the base of C(v) */ Edge *SwitchEdge; /*[MAX_VERTEX]; a pair of vertices and an edge, implemented here as [*][2] array */ S_CHAR *Tree; /*[MAX_VERTEX]; indicates presence in ScanQ, T, T', s-reachability */ Vertex *ScanQ; /*[MAX_VERTEX]; contains the set S of s-reachable vertices */ int QSize; /* index of the last element added to ScanQ */ Vertex *Pu; /*[MAX_VERTEX/2+1] */ Vertex *Pv; /*[MAX_VERTEX/2+1] */ int max_num_vertices; /* allocation size of all except Pu, Pv */ int max_len_Pu_Pv; /* allocation size of Pu and Pv */ #if ( BNS_RAD_SEARCH == 1 ) Vertex *RadEndpoints; /*[MAX_VERTEX*/ int nNumRadEndpoints; EdgeIndex *RadEdges; int nNumRadEdges; int nNumRadicals; BRS_MODE bRadSrchMode; /* 1 => connect fict. vertices-radicals to the accessible atoms */ #endif } BN_DATA; /* internal array size */ #define MAX_ALT_AATG_ARRAY_LEN 127 /* detected endpoint markings */ #define AATG_MARK_IN_PATH 1 /* atom in path detected by the BNS */ #define AATG_MARK_WAS_IN_PATH 2 /* found to be in path before next level */ /* output */ #define AATG_MARK_MAIN_TYPE 4 /* atom O-"salt" */ #define AATG_MARK_OTHER_TYPE 8 /* other atom to be tested */ struct tagTautomerGroupsInfo; /* forward declaration */ /******************** atoms in alt path through taut group ****************/ typedef struct BN_AtomsAtTautGroup { int nAllocLen; int nNumFound; int nNumMainAdj2Tgroup; int nNumOthersAdj2Tgroup; AT_NUMB *nEndPoint; /* original t-group number */ S_CHAR *nMarkedAtom; /* atom mark, see AATG_MARK_* */ int *nAtTypeTotals; struct tagTautomerGroupsInfo *t_group_info; } BN_AATG; /************ store changes in flow and capacity to test a bond ****************/ typedef struct tagBNS_FLOW_CHANGES { BNS_IEDGE iedge; EdgeFlow flow; EdgeFlow cap; Vertex v1; VertexFlow cap_st1; VertexFlow flow_st1; Vertex v2; VertexFlow cap_st2; VertexFlow flow_st2; } BNS_FLOW_CHANGES; #define ALT_PATH_MODE_TAUTOM 1 #define ALT_PATH_MODE_CHARGE 2 #define ALT_PATH_MODE_4_SALT 3 /* mark alt bonds along the path */ #define ALT_PATH_MODE_4_SALT2 4 /* mark alt bonds along the path, path to taut. group fict. vertex if exists */ #define ALT_PATH_MODE_REM2H_CHG 5 /* remove 2 H along alt. path AH-=-BH => A=-=B and change bonds to alternating */ #define ALT_PATH_MODE_ADD2H_CHG 6 /* add 2 H along alt. path A=-=B => AH-=-BH and change bonds to alternating */ #define ALT_PATH_MODE_REM2H_TST 7 /* test-remove 2 H along alt. path AH-=-BH => A=-=B; restore changed bonds */ #define ALT_PATH_MODE_ADD2H_TST 8 /* test-add 2 H along alt. path A=-=B => AH-=-BH; restore changed bonds */ #define ALT_PATH_MODE_REM_PROTON 9 /* remove proton, adjust bonds, charges, H-counts 2004-03-05 */ #if ( KETO_ENOL_TAUT == 1 ) #define ALT_PATH_MODE_TAUTOM_KET 10 /* same as ALT_PATH_MODE_TAUTOM, applies to C=-OH or CH-=O; H may be (-) */ #endif typedef U_SHORT bitWord; #define BIT_WORD_MASK ((bitWord)~0) typedef struct tagNodeSet { bitWord **bitword; int num_set; /* number of sets */ int len_set; /* number of bitWords in each set */ } NodeSet; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /********************************************************************************* bChangeFlow: 1 => change flow inside the BNS search 3 => change flow inside the BNS search and undo the flow change in the BNS structure here 4 => change bonds in the structure according to the flow 8 => make altern. bonds in the structure Note: (bChangeFlow & 1) == 1 is needed for multiple runs **********************************************************************************/ /* "EF" = "Edge Flow" */ #define BNS_EF_CHNG_FLOW 1 /* change Balanced Network (BN) flow inside the BNS search */ #define BNS_EF_RSTR_FLOW 2 /* undo BN flow changes after BNS */ #define BNS_EF_CHNG_RSTR (BNS_EF_CHNG_FLOW | BNS_EF_RSTR_FLOW) #define BNS_EF_CHNG_BONDS 4 /* change bonds in the structure according to the BN flow */ #define BNS_EF_ALTR_BONDS 8 /* make altern. bonds in the structure if the flow has changed */ #define BNS_EF_UPD_RAD_ORI 16 /* update BN flow0 & Atom radical values: flow0 := flow, radical:=st_cap - st_flow */ #define BNS_EF_SET_NOSTEREO 32 /* in combination with BNS_EF_ALTR_BONDS only: ALT12 bond cannot be stereogenic */ #define BNS_EF_UPD_H_CHARGE 64 /* update charges and H-counts according to change flow to c- and t-group vertices */ #define BNS_EF_SAVE_ALL (BNS_EF_CHNG_FLOW | BNS_EF_CHNG_BONDS | BNS_EF_UPD_RAD_ORI) #define BNS_EF_ALTR_NS (BNS_EF_ALTR_BONDS | BNS_EF_SET_NOSTEREO) #define BNS_EF_RAD_SRCH 128 /* search for rafical paths closures */ int SetBitCreate( void ); int NodeSetCreate( NodeSet *pSet, int n, int L ); void NodeSetFree( NodeSet *pSet ); int IsNodeSetEmpty( NodeSet *cur_nodes, int k); int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2); void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2); void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v); void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v); int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ); int nExists2AtMoveAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, int jj2, int jj1, struct tagSaltChargeCandidate *s_candidate, int nNumCandidates, AT_NUMB *nForbiddenAtom, int nNumForbiddenAtoms); int bExistsAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); int bExistsAnyAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); int AddTGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, struct tagTautomerGroupsInfo *tgi ); int AddSuperTGroup2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, struct tagTautomerGroupsInfo *tgi ); int AddCGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, struct tagChargeGroupsInfo *cgi ); int ReInitBnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ); int ReInitBnStructAddGroups( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, struct tagTautomerGroupsInfo *tgi, struct tagChargeGroupsInfo *cgi ); int DisconnectTestAtomFromTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv2, BNS_FLOW_CHANGES *fcd ); int DisconnectTGroupFromSuperTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv1, int *pv2, BNS_FLOW_CHANGES *fcd ); int ReconnectTestAtomToTGroup( struct BalancedNetworkStructure *pBNS, int v1, int v2, int ie, BNS_FLOW_CHANGES *fcd ); int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ); /* moved from ichi_bns.c 2005-08-23 */ int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ); BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *num_changed_bonds ); BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ); int ReInitBnStructAltPaths( BN_STRUCT *pBNS ); int ReInitBnStructForMoveableAltBondTest( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ); void ClearAllBnDataEdges( Edge *e, Vertex value, int size ); BN_DATA *DeAllocateBnData( BN_DATA *pBD ); BN_DATA *AllocateAndInitBnData( int max_num_vertices ); int ReInitBnData( BN_DATA *pBD ); int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); /* main function: find augmenting path */ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ); int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, int num_tg, int nChargeRevrs, int nChargeInChI ); int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHI_BNS_H___ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichi_io.c000066400000000000000000000761451271037650300233350ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include #include "mode.h" #include "ichi_io.h" #include "ichicomp.h" #include "util.h" #ifndef INCHI_ADD_STR_LEN #define INCHI_ADD_STR_LEN 32768 #endif #ifdef TARGET_LIB_FOR_WINCHI extern void (*FWPRINT) (const char * format, va_list argptr ); #endif /*^^^ Internal functions */ int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); int GetMaxPrintfLength( const char *lpszFormat, va_list argList); char *inchi_fgetsTab( char *szLine, int len, FILE *f ); int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHI_IOSTREAM OPERATIONS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Init INCHI_IOSTREAM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) { memset( ios, 0, sizeof(*ios) ); switch (io_type) { case INCHI_IOSTREAM_FILE: ios->type = INCHI_IOSTREAM_FILE; break; case INCHI_IOSTREAM_STRING: default: ios->type = INCHI_IOSTREAM_STRING; break; } ios->f = f; return; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, flush INCHI_IOSTREAM string buffer to file (if non-NULL); then free buffer. If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the file. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void inchi_ios_flush(INCHI_IOSTREAM* ios) { if (ios->type == INCHI_IOSTREAM_STRING) { if (ios->s.pStr) { if (ios->s.nUsedLength > 0) { if (ios->f) { fprintf(ios->f,"%-s", ios->s.pStr); fflush(ios->f); } inchi_free(ios->s.pStr ); ios->s.pStr = NULL; ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; } } } else if (ios->type == INCHI_IOSTREAM_FILE) { /* output to plain file: just flush it. */ fflush(ios->f); } return; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, flush INCHI_IOSTREAM string buffer to file (if non-NULL) and another file f2 supplied as parameter (typically, it will be stderr); then free buffer. If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the both files. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) { if (ios->type == INCHI_IOSTREAM_STRING) { if (ios->s.pStr) { if (ios->s.nUsedLength > 0) { if (ios->f) { fprintf(ios->f,"%-s", ios->s.pStr); fflush(ios->f); } if (f2!=ios->f) fprintf(f2,"%-s", ios->s.pStr); inchi_free(ios->s.pStr ); ios->s.pStr = NULL; ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; } } } else if (ios->type == INCHI_IOSTREAM_FILE) { /* output to plain file: just flush it. */ if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) fflush(ios->f); if ( f2 && f2!=stderr && f2!=stdout) fflush(f2); } return; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Close INCHI_IOSTREAM: free string buffer and close the file. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void inchi_ios_close(INCHI_IOSTREAM* ios) { if (ios->s.pStr) inchi_free(ios->s.pStr); ios->s.pStr = NULL; ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) fclose(ios->f); return; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Reset INCHI_IOSTREAM: set string buffer ptr to NULL (but do _not_ free memory)and close the file. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void inchi_ios_reset(INCHI_IOSTREAM* ios) { ios->s.pStr = NULL; ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) fclose(ios->f); return; } /*******************************************************************/ int inchi_ios_str_getc(INCHI_IOSTREAM *ios) { if (ios->type==INCHI_IOSTREAM_STRING) { if ( ios->s.nPtr < ios->s.nUsedLength ) { return (int)ios->s.pStr[ios->s.nPtr++]; } return EOF; } else if (ios->type==INCHI_IOSTREAM_FILE) { return fgetc( ios->f ); } /* error */ return EOF; } /*******************************************************************/ char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) { int length=0, c=0; if ( -- len < 0 ) { return NULL; } while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) { szLine[length++] = (char)c; if ( c == '\n' ) break; } if ( !length && EOF == c ) { return NULL; } szLine[length] = '\0'; return szLine; } /********************************************************************************/ /* read up to len or tab or LF; if empty read next until finds non-empty line */ /* remove leading and trailing white spaces; keep zero termination */ /********************************************************************************/ char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) { int length=0, c=0; if ( --len < 0 ) { return NULL; } while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) { if ( c == '\t' ) c = '\n'; szLine[length++] = (char)c; if ( c == '\n' ) break; } if ( !length && EOF == c ) { return NULL; } szLine[length] = '\0'; return szLine; } /*******************************************************************/ int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) { int length; char *p; do { p = inchi_ios_str_gets( szLine, len-1, f ); if ( !p ) { *bTooLongLine = 0; return -1; /* end of file or cannot read */ } szLine[len-1] = '\0'; /* *bTooLongLine = !strchr( szLine, '\n' ); */ p = strchr( szLine, '\n' ); *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); LtrimRtrim( szLine, &length ); } while ( !length ); return length; } /*******************************************************************/ /* read up to len or tab or LF; if empty read next until finds non-empty line */ /* remove leading and trailing white spaces; keep zero termination */ /*******************************************************************/ int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) { int length; char *p; do { p = inchi_ios_str_getsTab( szLine, len-1, f ); if ( !p ) { *bTooLongLine = 0; return -1; /* end of file or cannot read */ } szLine[len-1] = '\0'; /* *bTooLongLine = !strchr( szLine, '\n' ); */ p = strchr( szLine, '\n' ); *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); LtrimRtrim( szLine, &length ); } while ( !length ); return length; } /*******************************************************************/ int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) { int length; char *p; /*do {*/ p = inchi_ios_str_getsTab( szLine, len-1, f ); if ( !p ) { *bTooLongLine = 0; return -1; /* end of file or cannot read */ } szLine[len-1] = '\0'; /* *bTooLongLine = !strchr( szLine, '\n' ); */ p = strchr( szLine, '\n' ); *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); LtrimRtrim( szLine, &length ); /*} while ( !length );*/ return length; } /*****************************************************************/ int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) { int ret=0, ret2=0; va_list argList; if (!ios) return -1; if (ios->type == INCHI_IOSTREAM_STRING) { /* output to string buffer */ int max_len; my_va_start( argList, lpszFormat ); max_len = GetMaxPrintfLength( lpszFormat, argList); va_end( argList ); if ( max_len >= 0 ) { if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) { /* enlarge output string */ int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); if ( new_str ) { if ( ios->s.pStr ) { if ( ios->s.nUsedLength > 0 ) memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); inchi_free( ios->s.pStr ); } ios->s.pStr = new_str; ios->s.nAllocatedLength += nAddLength; } else return -1; /* failed */ } /* output */ my_va_start( argList, lpszFormat ); ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); va_end(argList); if ( ret >= 0 ) ios->s.nUsedLength += ret; #ifdef TARGET_LIB_FOR_WINCHI if( FWPRINT ) { my_va_start( argList, lpszFormat ); FWPRINT( lpszFormat, argList ); va_end( argList ); } #endif return ret; } return -1; } else if (ios->type == INCHI_IOSTREAM_FILE) { /* output to file */ if (ios->f) { my_va_start( argList, lpszFormat ); ret = vfprintf( ios->f, lpszFormat, argList ); va_end( argList ); } else { my_va_start( argList, lpszFormat ); ret2 = vfprintf( stdout, lpszFormat, argList ); va_end( argList ); } #ifdef TARGET_LIB_FOR_WINCHI if( FWPRINT ) { my_va_start( argList, lpszFormat ); FWPRINT( lpszFormat, argList ); va_end( argList ); } #endif return ret? ret : ret2; } /* no output */ return 0; } /**********************************************************************/ /* This function's output should not be displayed in the output pane */ /**********************************************************************/ int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) { va_list argList; if (!ios) return -1; if (ios->type == INCHI_IOSTREAM_STRING) { /* output to string buffer */ int ret=0, max_len; my_va_start( argList, lpszFormat ); max_len = GetMaxPrintfLength( lpszFormat, argList); va_end( argList ); if ( max_len >= 0 ) { if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) { /* enlarge output string */ int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); if ( new_str ) { if ( ios->s.pStr ) { if ( ios->s.nUsedLength > 0 ) { memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); } inchi_free( ios->s.pStr ); } ios->s.pStr = new_str; ios->s.nAllocatedLength += nAddLength; } else { return -1; /* failed */ } } /* output */ my_va_start( argList, lpszFormat ); ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); va_end(argList); if ( ret >= 0 ) { ios->s.nUsedLength += ret; } return ret; } return -1; } else if (ios->type == INCHI_IOSTREAM_FILE) { my_va_start( argList, lpszFormat ); inchi_print_nodisplay( ios->f, lpszFormat, argList); va_end(argList); } /* no output */ return 0; } /*****************************************************************/ /* Print to string buffer or to file+stderr */ int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) { int ret=0, ret2=0; va_list argList; if (!ios) return -1; if (ios->type == INCHI_IOSTREAM_STRING) /* was #if ( defined(TARGET_API_LIB) || defined(INCHI_STANDALONE_EXE) ) */ { /* output to string buffer */ int max_len, nAddLength = 0; char *new_str = NULL; my_va_start( argList, lpszFormat ); max_len = GetMaxPrintfLength( lpszFormat, argList); va_end( argList ); if ( max_len >= 0 ) { if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) { /* enlarge output string */ nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); if ( new_str ) { if ( ios->s.pStr ) { if ( ios->s.nUsedLength > 0 ) { memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); } inchi_free( ios->s.pStr ); } ios->s.pStr = new_str; ios->s.nAllocatedLength += nAddLength; } else { return -1; /* failed */ } } /* output */ my_va_start( argList, lpszFormat ); ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); va_end(argList); if ( ret >= 0 ) { ios->s.nUsedLength += ret; } return ret; } return -1; } else if (ios->type == INCHI_IOSTREAM_FILE) { if ( ios->f) { /* output to plain file */ my_va_start( argList, lpszFormat ); ret = inchi_vfprintf( ios->f, lpszFormat, argList ); va_end( argList ); /*^^^ No output to stderr from within dll or GUI program */ #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) if ( ios->f != stderr ) { my_va_start( argList, lpszFormat ); ret2 = vfprintf( stderr, lpszFormat, argList ); va_end( argList ); } #endif return ret? ret : ret2; } } /* no output */ return 0; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLAIN FILE OPERATIONS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* Print to file, echoing to stderr */ int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) { int ret=0, ret2=0; va_list argList; if (f) { my_va_start( argList, lpszFormat ); ret = inchi_vfprintf( f, lpszFormat, argList ); va_end( argList ); /*^^^ No output to stderr from within dll or GUI program */ #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) if ( f != stderr ) { my_va_start( argList, lpszFormat ); ret2 = vfprintf( stderr, lpszFormat, argList ); va_end( argList ); } #endif return ret? ret : ret2; } return 0; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* Print to file */ int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) { int ret=0; if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) { #define CONSOLE_LINE_LEN 80 #ifndef COMPILE_ANSI_ONLY char szLine[CONSOLE_LINE_LEN]; ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); if ( ret < 0 ) { /* output is longer than the console line */ /*^^^ Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); } fputs( szLine, f ); #else ret = vfprintf( f, lpszFormat, argList ); #endif #undef CONSOLE_LINE_LEN } else { ret = vfprintf( f, lpszFormat, argList ); } return ret; } /**********************************************************************/ /* This function's output should not be displayed in the output pane */ /**********************************************************************/ int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ) { int ret=0; va_list argList; FILE* fi; if (f) fi = f; else fi = stdout; my_va_start( argList, lpszFormat ); ret = vfprintf( fi, lpszFormat, argList ); return ret; } #if ( FIX_READ_LONG_LINE_BUG == 1 ) /********************************************************************/ int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) { int length; char *p; char szSkip[256]; int bTooLongLine = 0; do { p = inchi_fgetsTab( szLine, len, f ); if ( !p ) { return -1; /* end of file or cannot read */ } bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); LtrimRtrim( szLine, &length ); } while ( !length ); if ( bTooLongLine ) { while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { if ( strchr( szSkip, '\n' ) ) break; } } return length; } #else /********************************************************************/ int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) { int length; char *p; char szSkip[256]; int bTooLongLine = 0; do { p = inchi_fgetsTab( szLine, len-1, f ); if ( !p ) { return -1; /* end of file or cannot read */ } szLine[len-1] = '\0'; /* bTooLongLine = !strchr( szLine, '\n' ); */ bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); LtrimRtrim( szLine, &length ); } while ( !length ); if ( bTooLongLine ) { while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { szSkip[sizeof(szSkip)-1] = '\0'; if ( strchr( szSkip, '\n' ) ) break; } } return length; } #endif /*******************************************************************/ /* read up to len or tab or LF; if empty read next until finds non-empty line */ /* remove leading and trailing white spaces; keep zero termination */ /*******************************************************************/ char *inchi_fgetsTab( char *szLine, int len, FILE *f ) { int length=0, c=0; len --; while ( length < len && EOF != (c = fgetc( f )) ) { if ( c == '\t' ) c = '\n'; szLine[length++] = (char)c; if ( c == '\n' ) break; } if ( !length && EOF == c ) { return NULL; } szLine[length] = '\0'; return szLine; } /******************************************************************/ /* read not more than line_len bytes from an lf-terminated line */ /* if input line is too long quietly ignore the rest of the line */ char* inchi_fgetsLf( char* line, int line_len, FILE* inp ) { char *p, *q; memset( line, 0, line_len ); if ( NULL != (p = fgets( line, line_len, inp ) ) && NULL == strchr(p, '\n' ) ){ char temp[64]; /* bypass up to '\n' or up to end of file whichever comes first*/ while ( NULL != fgets( temp, sizeof(temp), inp ) && NULL == strchr(temp,'\n') ) ; } if ( p && (q = strchr(line, '\r')) ) { /* fix CR CR LF line terminator. */ q[0] = '\n'; q[1] = '\0'; } return p; } /***************************************************************** * * Estimate printf string length * * The code is based on Microsoft Knowledge Base article Q127038: * "FIX: CString::Format Gives Assertion Failed, Access Violation" * (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) * *****************************************************************/ #define FORCE_ANSI 0x10000 #define FORCE_UNICODE 0x20000 /* formatting (using wsprintf style formatting)*/ int GetMaxPrintfLength( const char *lpszFormat, va_list argList) { /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ const char * lpsz; int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; nMaxLen = 0; /* make a guess at the maximum length of the resulting string */ for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) { /* handle '%' character, but watch out for '%%' */ if (*lpsz != '%' || *( ++ lpsz ) == '%') { nMaxLen += 1; continue; } nItemLen = 0; /* handle '%' character with format */ nWidth = 0; for (; *lpsz; lpsz ++ ) { /* check for valid flags */ if (*lpsz == '#') nMaxLen += 2; /* for '0x' */ else if (*lpsz == '*') nWidth = va_arg(argList, int); else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' || *lpsz == ' ') ; else /* hit non-flag character */ break; } /* get width and skip it */ if (nWidth == 0) { /* width indicated by */ nWidth = atoi(lpsz); for (; *lpsz && isdigit(*lpsz); lpsz ++ ) ; } /*ASSERT(nWidth >= 0);*/ if ( nWidth < 0 ) goto exit_error; /* instead of exception */ nPrecision = 0; if (*lpsz == '.') { /* skip past '.' separator (width.precision)*/ lpsz ++; /* get precision and skip it*/ if (*lpsz == '*') { nPrecision = va_arg(argList, int); lpsz ++; } else { nPrecision = atoi(lpsz); for (; *lpsz && isdigit(*lpsz); lpsz ++) ; } if ( nPrecision < 0 ) goto exit_error; /* instead of exception */ } /* should be on type modifier or specifier */ nModifier = 0; switch (*lpsz) { /* modifiers that affect size */ case 'h': switch ( lpsz[1] ) { case 'd': case 'i': case 'o': case 'x': case 'X': case 'u': /* short unsigned, short double, etc. -- added to the original MS example */ /* ignore the fact that these modifiers do affect size */ lpsz ++; break; default: nModifier = FORCE_ANSI; lpsz ++; break; } break; case 'l': switch ( lpsz[1] ) { case 'd': case 'i': case 'o': case 'x': case 'X': case 'u': case 'f': /* long float -- post ANSI C */ /* long unsigned, long double, etc. -- added to the original MS example */ /* ignore the fact that these modifiers do affect size */ lpsz ++; break; default: /* nModifier = FORCE_UNICODE; lpsz ++; break; */ goto exit_error; /* no UNICODE, please */ } break; /* modifiers that do not affect size */ case 'F': case 'N': case 'L': lpsz ++; break; } /* now should be on specifier */ switch (*lpsz | nModifier) { /* single characters*/ case 'c': case 'C': nItemLen = 2; va_arg(argList, int); break; case 'c'|FORCE_ANSI: case 'C'|FORCE_ANSI: nItemLen = 2; va_arg(argList, int); break; case 'c'|FORCE_UNICODE: case 'C'|FORCE_UNICODE: goto exit_error; /* no UNICODE, please */ /* nItemLen = 2; va_arg(argList, wchar_t); break; */ /* strings*/ case 's': case 'S': nItemLen = strlen(va_arg(argList, char*)); nItemLen = inchi_max(1, nItemLen); break; case 's'|FORCE_ANSI: case 'S'|FORCE_ANSI: nItemLen = strlen(va_arg(argList, char*)); nItemLen = inchi_max(1, nItemLen); break; case 's'|FORCE_UNICODE: case 'S'|FORCE_UNICODE: goto exit_error; /* no UNICODE, please */ /* nItemLen = wcslen(va_arg(argList, wchar_t*)); nItemLen = inchi_max(1, nItemLen); break; */ } /* adjust nItemLen for strings */ if (nItemLen != 0) { nItemLen = inchi_max(nItemLen, nWidth); if (nPrecision != 0) nItemLen = inchi_min(nItemLen, nPrecision); } else { switch (*lpsz) { /* integers */ case 'd': case 'i': case 'u': case 'x': case 'X': case 'o': va_arg(argList, int); nItemLen = 32; nItemLen = inchi_max(nItemLen, nWidth+nPrecision); break; case 'e': case 'f': case 'g': case 'G': va_arg(argList, double); nItemLen = 32; nItemLen = inchi_max(nItemLen, nWidth+nPrecision); break; case 'p': va_arg(argList, void*); nItemLen = 32; nItemLen = inchi_max(nItemLen, nWidth+nPrecision); break; /* no output */ case 'n': va_arg(argList, int*); break; default: /*ASSERT(FALSE);*/ /* unknown formatting option*/ goto exit_error; /* instead of exception */ } } /* adjust nMaxLen for output nItemLen */ nMaxLen += nItemLen; } return nMaxLen; exit_error: return -1; /* wrong format */ } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichi_io.h000066400000000000000000000060201271037650300233230ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHI_IOSTREAM OPERATIONS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); void inchi_ios_flush(INCHI_IOSTREAM *ios); void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); void inchi_ios_close(INCHI_IOSTREAM *ios); void inchi_ios_reset(INCHI_IOSTREAM *ios); int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); /* Print to string buffer or to file+stderr */ int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLAIN FILE OPERATIONS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* Print to file, echoing to stderr */ int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ); char* inchi_fgetsLf( char* line, int line_len, FILE* inp ); int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichican2.c000066400000000000000000006470711271037650300234140ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include /* #define CHECK_WIN32_VC_HEAP */ #include "mode.h" #include "ichi.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "inpdef.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" #define MAX_CELLS 1024 #define MAX_NODES 1024 #define MAX_SET_SIZE 2048 /*16384*/ #define MAX_LAYERS 7 #define INFINITY 0x3FFF #define EMPTY_CT 0 #define EMPTY_H_NUMBER (INFINITY-1) #define BASE_H_NUMBER ((INFINITY-1)/2) #define EMPTY_ISO_SORT_KEY LONG_MAX #define SEPARATE_CANON_CALLS 0 /* #define INCHI_CANON_USE_HASH */ #define INCHI_CANON_MIN /****************************************************************/ #ifdef INCHI_CANON_USE_HASH typedef unsigned long U_INT_32; typedef unsigned char U_INT_08; typedef U_INT_32 CtHash; CtHash hash_mark_bit; #endif /* -- moved to ichi_bns.h -- typedef U_SHORT bitWord; #define BIT_WORD_MASK ((bitWord)~0) */ static bitWord *bBit = NULL; static int num_bit = 0; /*bitWord mark_bit; */ /* highest bit in AT_NUMB */ /*bitWord mask_bit; */ /* ~mark_bit */ AT_NUMB rank_mark_bit; AT_NUMB rank_mask_bit; typedef AT_NUMB Node; typedef NEIGH_LIST Graph; /* typedef struct tagGraph { int dummy; } Graph; */ typedef struct tagUnorderedPartition { /* AT_NUMB *next; */ /* links */ AT_NUMB *equ2; /* mcr */ } UnorderedPartition; typedef struct tagCell { int first; /* index of the first cell element in Partition::AtNumber[] */ int next; /* next after the last index */ int prev; /* position of the previously returned cell element */ } Cell; #ifdef NEVER /* moved to ichi_bns.h */ typedef struct tagNodeSet { bitWord **bitword; int num_set; /* number of sets */ int len_set; /* number of bitWords in each set */ } NodeSet; #endif typedef struct tagTransposition { AT_NUMB *nAtNumb; } Transposition; typedef struct tagCTable { AT_RANK *Ctbl; /* connection table */ /* Format-atoms: atom_rank[k] neigh_rank[k][1]...neigh_rank[k][n] 1) atom_rank[k1] < atom_rank[k2] <=> k1 < k2 where 2) atom_rank[k] > neigh_rank[k][i], i=1..n 3) neigh_rank[k][i] < neigh_rank[k][j] <=> i < j Format-tgroup: tgroup_rank[k] endpoint_rank[k][1]...endpoint_rank[k][n] where 1) tgroup_rank[k1] < tgroup_rank[k2] <=> k1 < k2 2) endpoint_rank[k][i] < endpoint_rank[k][j] <=> i < j Note: tgroup_rank[k] > endpoint_rank[k][j] for all j by construction */ int lenCt; /* used length */ int nLenCTAtOnly; /* to split Ctnl comparison in case of bDigraph != 0 */ int maxlenCt; /* allocated length of Ctbl */ int maxPos; /* allocated length of nextCtblPos */ int maxVert; /* max number of vertices to separate atoms from taut groups */ int lenPos; /* first unused element of nextCtblPos */ AT_RANK *nextAtRank; /* rank (k value) after the last node of the Ctbl portion*/ AT_NUMB *nextCtblPos; /* first unused element of Ctbl */ /* hydrogen atoms fixed in tautomeric representation: compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ NUM_H *NumH; int lenNumH; /* used length */ int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ /* hydrogen atoms fixed in non-tautomeric representation only: compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ NUM_H *NumHfixed; /*int lenNumHfixed; */ /* used length */ /*int maxlenNumHfixed; */ /* max length = n+1 */ /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ AT_ISO_SORT_KEY *iso_sort_key; int len_iso_sort_key; /* used length */ int maxlen_iso_sort_key; /* max length = n_tg+1 */ S_CHAR *iso_exchg_atnos; int len_iso_exchg_atnos; int maxlen_iso_exchg_atnos; /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) AT_ISO_SORT_KEY *iso_sort_key_Hfixed; int len_iso_sort_key_Hfixed; /* used length */ int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ #endif #ifdef INCHI_CANON_USE_HASH CtHash *hash; #endif } ConTable; /**************************************************************/ typedef struct tagkLeast { int k; int i; } kLeast; /*************local prototypes **********************************/ int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ); void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, ConTable *Ct, int k, int n, int n_tg ); void CtPartClear( ConTable *Ct, int k ); void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ); int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ); int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ); void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ); void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ); int CtFullCompareLayers( kLeast *kLeastForLayer ); int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, int *L_rho, int *I_rho, int *k_rho ); int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ); void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ); int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ); void CleanNumH( NUM_H *NumH, int len ); int CleanCt( AT_RANK *Ct, int len ); void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ); void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ); int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ); Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ); int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ); int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ); void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v); void CellMakeEmpty( Cell *baseW, int k ); Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ); int CellGetNumberOfNodes( Partition *p, Cell *W ); int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ); int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ); void PartitionCopy( Partition *To, Partition *From, int n ); int PartitionSatisfiesLemma_2_25( Partition *p, int n ); void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ); void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ); int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ); int PartitionIsDiscrete( Partition *p, int n); void PartitionFree( Partition *p ); int PartitionCreate( Partition *p, int n); void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n); void UnorderedPartitionFree( UnorderedPartition *p ); int UnorderedPartitionCreate( UnorderedPartition *p, int n ); void CTableFree( ConTable *Ct ); int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ); void TranspositionFree( Transposition *p ); int TranspositionCreate( Transposition *p, int n ); void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ); void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ); int WriteGraph( Graph *G, int n, int gnum, char *fname, char *fmode ); int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ); void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ); int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, AT_RANK **pRankStack, int nNumPrevRanks, AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT* pCS ); #if ( SEPARATE_CANON_CALLS == 1 ) /* for profiling purposes */ int CanonGraph01( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph02( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph03( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph04( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph05( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph06( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph07( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph08( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph09( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph10( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph11( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } int CanonGraph12( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { return CanonGraph( n, n_tg, n_max, bDigraph, G, pi , nSymmRank, nCanonRank, nAtomNumberCanon, pCD, pCC, pp_zb_rho_inp, pp_zb_rho_out ); } #else #define CanonGraph01 CanonGraph #define CanonGraph02 CanonGraph #define CanonGraph03 CanonGraph #define CanonGraph04 CanonGraph #define CanonGraph05 CanonGraph #define CanonGraph06 CanonGraph #define CanonGraph07 CanonGraph #define CanonGraph08 CanonGraph #define CanonGraph09 CanonGraph #define CanonGraph10 CanonGraph #define CanonGraph11 CanonGraph #define CanonGraph12 CanonGraph #endif #ifdef INCHI_CANON_USE_HASH /****************************************************************/ static call_fill_crc32_data = 1; static U_INT_32 crc32_data[256]; void fill_crc32_data() { U_INT_32 c; int n, k; for (n = 0; n < 256; n++) { c = (U_INT_32)n; for (k = 0; k < 8; k++) c = c & 1 ? 0xEDB88320L ^ (c >> 1) : c >> 1; crc32_data[n] = c; } call_fill_crc32_data = 0; } /****************************************************************/ unsigned long add2crc32( unsigned long crc32, AT_NUMB n ) { U_INT_08 chr; if (call_fill_crc32_data) { fill_crc32_data(); } chr = n % 128; crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); chr = n / 128; crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); return crc32; } #endif /****************************************************************/ int TranspositionCreate( Transposition *p, int n ) { p->nAtNumb = (AT_NUMB*)inchi_calloc( n, sizeof(p->nAtNumb[0]) ); if ( p->nAtNumb ) { return 1; } return 0; } /****************************************************************/ void TranspositionFree( Transposition *p ) { if ( p && p->nAtNumb ) { inchi_free( p->nAtNumb ); p->nAtNumb = NULL; } } /****************************************************************/ int NodeSetCreate( NodeSet *pSet, int n, int L ) { int i, len; len = (n+ num_bit - 1)/num_bit; pSet->bitword = (bitWord**)inchi_calloc(L, sizeof(pSet->bitword[0])); if ( !pSet->bitword ) { return 0; } pSet->bitword[0] = (bitWord*)inchi_calloc(len*L, sizeof(pSet->bitword[0][0])); if ( !pSet->bitword[0] ) { /* cleanup */ inchi_free( pSet->bitword ); pSet->bitword = NULL; return 0; /* failed */ } for ( i = 1; i < L; i ++ ) { pSet->bitword[i] = pSet->bitword[i-1]+len; } pSet->len_set = len; pSet->num_set = L; return 1; } /****************************************************************/ void NodeSetFree( NodeSet *pSet ) { if ( pSet && pSet->bitword ) { if ( pSet->bitword[0] ) { inchi_free( pSet->bitword[0] ); } inchi_free( pSet->bitword ); pSet->bitword = NULL; } } /****************************************************************/ int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ) { int maxlenCt = pCD->nMaxLenLinearCT + 1; /* add one element for CtPartInfinity() */ int maxlenNumH = pCD->NumH? (pCD->maxlenNumH + 1) : 0; int maxlenNumHfixed = pCD->NumHfixed? (pCD->maxlenNumHfixed + 1) : 0; int maxlenIso = pCD->maxlen_iso_sort_key? (pCD->maxlen_iso_sort_key+1) : 0; int maxlenIsoExchg = pCD->iso_exchg_atnos? (pCD->maxlen_iso_exchg_atnos+1) : 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) int maxlenIsoHfixed = pCD->maxlen_iso_sort_key_Hfixed? (pCD->maxlen_iso_sort_key_Hfixed+1):0; #endif memset( Ct, 0, sizeof(Ct[0]) ); Ct->maxVert = n; n ++; Ct->Ctbl = (AT_RANK*) inchi_calloc(maxlenCt, sizeof(Ct->Ctbl[0]) ); Ct->nextCtblPos = (AT_NUMB*) inchi_calloc(n, sizeof(Ct->nextCtblPos[0]) ); Ct->nextAtRank = (AT_RANK*) inchi_calloc(n, sizeof(Ct->nextAtRank[0]) ); if ( maxlenNumH ) { Ct->NumH = (NUM_H *) inchi_calloc(maxlenNumH, sizeof(Ct->NumH[0])); } if ( maxlenNumHfixed ) { Ct->NumHfixed = (NUM_H *) inchi_calloc(maxlenNumHfixed, sizeof(Ct->NumH[0])); } if ( maxlenIso ) { Ct->iso_sort_key = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIso, sizeof(Ct->iso_sort_key[0])); } if ( maxlenIsoExchg ) { Ct->iso_exchg_atnos = (S_CHAR *)inchi_calloc( maxlenIsoExchg, sizeof(Ct->iso_exchg_atnos[0])); } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( maxlenIsoHfixed ) { Ct->iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIsoHfixed, sizeof(Ct->iso_sort_key_Hfixed[0])); } #endif #ifdef INCHI_CANON_USE_HASH Ct->hash = (CtHash*) inchi_calloc(n, sizeof(Ct->hash[0]) ); #endif Ct->lenCt = 0; Ct->nLenCTAtOnly = pCD->nLenCTAtOnly; Ct->maxlenCt = maxlenCt; Ct->lenNumH = 0; Ct->maxlenNumH = maxlenNumH; Ct->len_iso_sort_key = 0; Ct->maxlen_iso_sort_key = maxlenIso; Ct->len_iso_exchg_atnos = 0; Ct->maxlen_iso_exchg_atnos = maxlenIso; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) Ct->len_iso_sort_key_Hfixed = 0; Ct->maxlen_iso_sort_key_Hfixed = maxlenIsoHfixed; #endif Ct->maxPos = n; Ct->lenPos = 0; Ct->nextAtRank[0] = 0; Ct->nextCtblPos[0] = 0; if ( Ct->Ctbl && Ct->nextCtblPos && (!maxlenNumH || Ct->NumH) && (!maxlenNumHfixed || Ct->NumHfixed ) ) { return 1; } return 0; } /****************************************************************/ void CTableFree( ConTable *Ct ) { if ( Ct ) { if ( Ct->Ctbl ) inchi_free( Ct->Ctbl ); if ( Ct->nextCtblPos ) inchi_free( Ct->nextCtblPos ); if ( Ct->nextAtRank ) inchi_free( Ct->nextAtRank ); if ( Ct->NumH ) inchi_free( Ct->NumH ); if ( Ct->NumHfixed ) inchi_free( Ct->NumHfixed ); if ( Ct->iso_sort_key ) inchi_free( Ct->iso_sort_key ); if ( Ct->iso_exchg_atnos ) inchi_free( Ct->iso_exchg_atnos ); #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( Ct->iso_sort_key_Hfixed ) inchi_free( Ct->iso_sort_key_Hfixed ); #endif #ifdef INCHI_CANON_USE_HASH if ( Ct->hash ) inchi_free( Ct->hash ); #endif memset( Ct, 0, sizeof( Ct[0] ) ); } } /****************************************************************/ int UnorderedPartitionCreate( UnorderedPartition *p, int n ) { p->equ2 = (AT_NUMB*)inchi_calloc( n, sizeof(p->equ2[0])); /* p->next = (AT_NUMB*)inchi_calloc( n, sizeof(p->next[0])); */ if ( p->equ2 /*&& p->next*/ ) return 1; return 0; } /****************************************************************/ void UnorderedPartitionFree( UnorderedPartition *p ) { if (p->equ2) inchi_free(p->equ2); /* if (p->next) inchi_free(p->next); */ p->equ2 = NULL; /* p->next = NULL; */ } /****************************************************************/ void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n) { int i; for ( i = 0; i < n; i ++ ) { p->equ2[i] = (AT_NUMB)i; /* p->next[i] = INFINITY; */ } INCHI_HEAPCHK } /****************************************************************/ int PartitionCreate( Partition *p, int n) { p->AtNumber = (AT_NUMB*)inchi_calloc( n, sizeof(p->AtNumber[0])); p->Rank = (AT_RANK*)inchi_calloc( n, sizeof(p->Rank[0])); if ( p->AtNumber && p->Rank ) { return 1; } return 0; } /****************************************************************/ void PartitionFree( Partition *p ) { if ( p ) { if ( p->AtNumber ) { inchi_free( p->AtNumber ); p->AtNumber = NULL; } if ( p->Rank ) { inchi_free( p->Rank ); p->Rank = NULL; } } } /****************************************************************/ int PartitionIsDiscrete( Partition *p, int n) { int i; AT_RANK r; for ( i = 0, r = 1; i < n; i ++, r ++ ) { if ( r != (rank_mask_bit & p->Rank[p->AtNumber[i]]) ) { INCHI_HEAPCHK return 0; } } INCHI_HEAPCHK return 1; } /****************************************************************/ int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ) { int i; AT_RANK r; Cell *W = baseW+k-1; i = (k > 1)? baseW[k-2].first+1 : 0; if ( i < n ) { /* bypass single vertex cells */ for ( r = (AT_RANK)(i+1); i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); i ++, r++ ) ; } if ( i < n ) { W->first = i; for ( r = (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]), i++ ; i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); i ++ ) ; W->next = i; INCHI_HEAPCHK return (W->next - W->first); } W->first = INFINITY; W->next = 0; INCHI_HEAPCHK return 0; } /****************************************************************/ void CellMakeEmpty( Cell *baseW, int k ) { k --; baseW[k].first = INFINITY; baseW[k].next = 0; baseW[k].prev = -1; INCHI_HEAPCHK } /****************************************************************/ void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v) { bitWord *Bits = cur_nodes->bitword[l-1]; int len = cur_nodes->len_set*sizeof(bitWord); int i, j; memset( Bits, 0, len ); for ( i = 0; i < num_v; i ++ ) { j = (int)v[i]-1; Bits[ j / num_bit ] |= bBit[ j % num_bit ]; } INCHI_HEAPCHK } /****************************************************************/ int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ) { int i; int n = cur_nodes->len_set; bitWord *BitsNode = cur_nodes->bitword[lcur_nodes-1]; bitWord *BitsSet = set->bitword[lset-1]; /* find any BitsNode[i] bit not in BitsSet[i] */ for ( i = 0; i < n; i ++ ) { if ( BitsNode[i] & ~BitsSet[i] ) { INCHI_HEAPCHK return 0; } } INCHI_HEAPCHK return 1; } /****************************************************************/ void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ) { int i, j1, j2; AT_RANK r, r1; bitWord *McrBits = Mcr->bitword[l-1]; bitWord *FixBits = Fix->bitword[l-1]; int len = Mcr->len_set*sizeof(bitWord); memset( McrBits, 0, len ); memset( FixBits, 0, len ); for ( i = 0, r = 1; i < n; i ++, r ++ ) { if ( r == (r1=(rank_mask_bit&p->Rank[j1=(int)p->AtNumber[i]])) ) { FixBits[j1 / num_bit] |= bBit[j1 % num_bit]; McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; } else { for ( r = r1; i+1 < n && r == (rank_mask_bit&p->Rank[j2=(int)p->AtNumber[i+1]]); i ++ ) { if ( j1 > j2 ) { j1 = j2; } } McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; } } INCHI_HEAPCHK } /************* used in ichi_bns.c ********************************/ void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v) { bitWord *Bits = cur_nodes->bitword[k]; int len = cur_nodes->len_set*sizeof(bitWord); int i, j; memset( Bits, 0, len ); for ( i = 1; i < num_v; i += 2 ) { j = (int)RadEndpoints[i]; Bits[ j / num_bit ] |= bBit[ j % num_bit ]; } } /************* used in ichi_bns.c ********************************/ void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v) { if ( cur_nodes->bitword ) { bitWord *Bits = cur_nodes->bitword[k]; /*int len = cur_nodes->len_set*sizeof(bitWord);*/ int i, j; for ( i = 0; i < num_v; i ++ ) { j = (int) v[i]; Bits[ j / num_bit ] &= ~bBit[ j % num_bit ]; } } } /************* used in ichi_bns.c ********************************/ int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2) { if ( cur_nodes->bitword ) { bitWord *Bits1 = cur_nodes->bitword[k1]; bitWord *Bits2 = cur_nodes->bitword[k2]; int len = cur_nodes->len_set; int i; for ( i = 0; i < len; i ++ ) { if ( Bits1[i] & Bits2[i] ) return 1; } } return 0; } /************* used in ichi_bns.c ********************************/ int IsNodeSetEmpty( NodeSet *cur_nodes, int k) { if ( cur_nodes->bitword ) { bitWord *Bits = cur_nodes->bitword[k]; int len = cur_nodes->len_set; int i; for ( i = 0; i < len; i ++ ) { if ( Bits[i] ) return 0; } } return 1; } /************* used in ichi_bns.c ********************************/ void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2) { if ( cur_nodes->bitword ) { bitWord *Bits1 = cur_nodes->bitword[k1]; bitWord *Bits2 = cur_nodes->bitword[k2]; int len = cur_nodes->len_set; int i; for ( i = 0; i < len; i ++ ) { Bits1[i] |= Bits2[i]; } } } /************* used in ichi_bns.c ********************************/ int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ) { int n = nStart; if ( cur_nodes->bitword ) { bitWord *Bits = cur_nodes->bitword[k]; int len = cur_nodes->len_set; int i, j; Vertex v; for ( i = 0, v = 0; i < len; i ++ ) { if ( Bits[i] ) { for ( j = 0; j < num_bit; j ++, v ++ ) { if ( Bits[i] & bBit[j] ) { if ( n >= nLen ) { return -1; /* overflow */ } RadEndpoints[n ++] = vRad; RadEndpoints[n ++] = v; } } } else { v += num_bit; } } } return n; } /****************************************************************/ void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ) { int i; for ( i = 0; i < n; i ++ ) { gamma->nAtNumb[(int)pFrom->AtNumber[i]] =pTo->AtNumber[i]; } INCHI_HEAPCHK } /**************************************************************************************/ /* Get minimal set (class) representative and partially compress the partitioning */ /* mcr = minimal class representative. */ AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ) { AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ INCHI_HEAPCHK n1=nEqArray[(int)n]; if ( n == n1 ) { return n; } /* 1st pass: find mcr */ while ( n1 != (n2=nEqArray[(int)n1])) { n1 = n2; } /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ mcr = n1; n1 = n; while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { nEqArray[(int)n1]=mcr; n1 = n2; } INCHI_HEAPCHK return ( mcr ); } /**************************************************************************************/ /* Join 2 sets (classes) that have members n1 and n2 */ int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) { n1 = nGetMcr2( nEqArray, n1 ); n2 = nGetMcr2( nEqArray, n2 ); if ( n1 < n2 ) { nEqArray[n2] = n1; INCHI_HEAPCHK return 1; /* a change has been made */ } if ( n2 < n1 ) { nEqArray[n1] = n2; INCHI_HEAPCHK return 1; /* a change has been made */ } INCHI_HEAPCHK return 0; /* no changes */ } /****************************************************************/ Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ) { Node ret = (Node)(1+ nGetMcr2( p1->equ2, (AT_RANK)(v-1) )); INCHI_HEAPCHK return ret; } /****************************************************************/ /* change p2 to (p2 v p1) */ int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ) { int i, j; int nNumChanges = 0; for ( i = 0; i < n; i ++ ) { if ( (j=(int)p1->equ2[i]) == i || p2->equ2[(int)i] == p2->equ2[(int)j] ) { continue; } nNumChanges += nJoin2Mcrs2(p2->equ2, (AT_NUMB)i, (AT_NUMB)j ); } INCHI_HEAPCHK return nNumChanges; } /****************************************************************/ int PartitionSatisfiesLemma_2_25( Partition *p, int n ) { int nPartitionSize = 0; int nNumNonTrivialCells = 0; AT_RANK r; int i, num; for ( i = num = 0, r=1; i < n; i ++, r++ ) { if ( (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]) == r ) { nPartitionSize ++; if ( num ) { /* num+1 = cell size > 1 */ nNumNonTrivialCells ++; num = 0; } } else { num ++; } } /* check Lemma_2_25 conditions */ if ( n <= nPartitionSize+4 || n == nPartitionSize + nNumNonTrivialCells || n == nPartitionSize + nNumNonTrivialCells + 1 ) { return 1; } return 0; } /****************************************************************/ void PartitionCopy( Partition *To, Partition *From, int n ) { int i; memcpy( To->AtNumber, From->AtNumber, n*sizeof(To->AtNumber[0])); memcpy( To->Rank, From->Rank, n*sizeof(To->AtNumber[0])); for ( i = 0; i < n; i ++ ) { To->Rank[i] &= rank_mask_bit; } INCHI_HEAPCHK } /****************************************************************/ /* makes new equitable partition (p+1) out of p; first reduce the rank of vertex v */ int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ) { int nNumNewRanks, i, j; long lNumNeighListIter = 0; AT_RANK rv, r; AT_NUMB s, sv; for ( i = 1; i <= 2; i ++ ) { if ( !p[i].AtNumber ) { p[i].AtNumber = (AT_NUMB *) inchi_malloc(n_max*sizeof(p[0].AtNumber[0])); } if ( !p[i].Rank ) { p[i].Rank = (AT_RANK *) inchi_malloc(n_max*sizeof(p[0].Rank[0])); } if ( !p[i].AtNumber || !p[i].Rank ) { INCHI_HEAPCHK return CT_OUT_OF_RAM; } } PartitionCopy( p+1, p, n_tg ); sv = v-1; /* atom number we are looking for */ if ( sv >= (AT_NUMB) n_tg ) { INCHI_HEAPCHK return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ } rv = p[1].Rank[(int)sv]; /* rank of this atom */ /* second, locate sv among all vertices that have same rank as v */ s = n_max + 1; /* always greater than sv; this initialization is needed only to keep the compiler happy */ for ( j = (int)rv-1; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]) && s != sv; j -- ) ; if ( s != sv ) { INCHI_HEAPCHK return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ } /* shift preceding atom numbers to the right to fill the gap after removing sv */ r = rv-1; /* initialization only to keep compiler happy */ for ( i = j--; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]); i = j, j -- ) { p[1].AtNumber[i] = s; } r = (i > 0)? (r+1):1; /* new reduced rank = (next lower rank)+1 or 1 */ /* insert sv and adjust its rank */ p[1].AtNumber[i] = sv; p[1].Rank[(int)sv] = r; /* make equitable partition */ if ( bDigraph ) { /* nNumNewRanks = DifferentiateRanks2( n_tg, G, nNumPrevRanks+1, p[1].Rank, p[2].Rank, p[1].AtNumber, &lNumNeighListIter, 1 ); */ nNumNewRanks = DifferentiateRanks4( n_tg, G, nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, p[1].AtNumber, (AT_RANK)n, &lNumNeighListIter ); } else { /* nNumNewRanks = DifferentiateRanks2( n_tg, G, nNumPrevRanks+1, p[1].Rank, p[2].Rank, p[1].AtNumber, &lNumNeighListIter, 1 ); */ nNumNewRanks = DifferentiateRanks3( n_tg, G, nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, p[1].AtNumber, &lNumNeighListIter ); } INCHI_HEAPCHK return nNumNewRanks; } typedef struct tagNodeValues { NUM_H NumH; AT_ISO_SORT_KEY iso_sort_key; NUM_H NumHfixed; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) AT_ISO_SORT_KEY iso_sort_key_Hfixed; #endif AT_NUMB nAtNumber; } NV; /****************************************************************/ /* return min node > vPrev or INFINITY if not found */ /* Input: v = previous atom number + 1 or 0 on first call*/ Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ) { AT_NUMB i; AT_NUMB uCurAtNumb, uMinAtNumb = INFINITY; /* in case of emty cell: (W->first=INFINITY) > (W->next=0); returns INFINITY */ if ( W->first > W->next ) { return INFINITY; } #if ( USE_AUX_RANKING == 1 ) if ( pCD && pCD->nAuxRank ) { AT_RANK uMinAuxRank, uCurAuxRank; int nCurAtNumb; #if ( USE_AUX_RANKING_ALL == 1 ) AT_RANK uInpAuxRank; int nInpAtNumb, nMinAtNumb; #endif for ( i = W->first; i < W->next; i ++ ) { uCurAtNumb = p->AtNumber[(int)i]; if ( !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) ) { break; /* found the first unmarked yet node */ } } if ( i == W->next ) { return INFINITY; } #if ( USE_AUX_RANKING_ALL == 1 ) /*==== vertex ordering definition === * vertex v1 < v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) < AtNumb(v2)) || (AuxRank(v1) < AuxRank(v2)) * vertex v1 > v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) > AtNumb(v2)) || (AuxRank(v1) > AuxRank(v2)) * vertex v1 = v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) == AtNumb(v2)) */ /* set initial vMin so that vMin > any vertex */ uMinAuxRank = INFINITY; nMinAtNumb = INFINITY; /* set vInp */ if ( v ) { nInpAtNumb = (int)v - 1; /* previous vertex */ uInpAuxRank = pCD->nAuxRank[nInpAtNumb]; } else { nInpAtNumb = -1; /* less than any vertex */ uInpAuxRank = 0; } /* find vMin = min { vCur : (vCur > vInp) && (vCur in W) } */ for ( ; i < W->next; i ++ ) { nCurAtNumb = (int)p->AtNumber[(int)i]; if ( !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { /* vertex nCurAtNumb is not marked, find whether it fits the conditions */ uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; if ( uCurAuxRank == uInpAuxRank && nCurAtNumb > nInpAtNumb || uCurAuxRank > uInpAuxRank ) { /* here vCur > vInp */ if ( uCurAuxRank == uMinAuxRank && nCurAtNumb < nMinAtNumb ) { /* vCur < vMin (1) */ nMinAtNumb = nCurAtNumb; } else if ( uCurAuxRank < uMinAuxRank ) { /* vCur < vMin (2) */ uMinAuxRank = uCurAuxRank; nMinAtNumb = nCurAtNumb; } } } } uMinAtNumb = (nMinAtNumb==INFINITY)? INFINITY : (AT_NUMB)nMinAtNumb; #else if ( v ) { nCurAtNumb = (int)v-1; /* any valid found node must have nAuxRank == uMinAuxRank */ uMinAuxRank = pCD->nAuxRank[nCurAtNumb]; } else { /* any valid found node must have minimal uMinAuxRank from pCD->nAuxRank[] */ uMinAuxRank = INFINITY; /* undefined */ } for ( ; i < W->next; i ++ ) { uCurAtNumb = p->AtNumber[(int)i]; nCurAtNumb = (int)uCurAtNumb; if ( uCurAtNumb >= v && !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; if ( v ) { /* get next node */ /* find node with smallest uCurAtNumb among nodes with aux. ranks equal to uMinAuxRank */ if ( uCurAuxRank == uMinAuxRank && uCurAtNumb < uMinAtNumb ) { uMinAtNumb = uCurAtNumb; } } else { /* get first node */ /* find node with smallest smallest uCurAtNumb among nodes with smallest aux. ranks */ if ( uMinAuxRank > uCurAuxRank ) { uMinAuxRank = uCurAuxRank; uMinAtNumb = uCurAtNumb; } else if ( uMinAuxRank == uCurAuxRank && uCurAtNumb < uMinAtNumb ) { uMinAtNumb = uCurAtNumb; } } } } #endif } else #endif /* } USE_AUX_RANKING */ { for ( i = W->first; i < W->next; i ++ ) { uCurAtNumb = p->AtNumber[(int)i]; if ( uCurAtNumb >= v && !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) && uCurAtNumb < uMinAtNumb ) { uMinAtNumb = uCurAtNumb; } } } if ( uMinAtNumb != INFINITY ) uMinAtNumb ++; INCHI_HEAPCHK return uMinAtNumb; } /****************************************************************/ int CellGetNumberOfNodes( Partition *p, Cell *W ) { int first = W->first; int next = W->next; int i, num; for ( i = first, num = 0; i < next; i ++ ) { if ( !( rank_mark_bit & p->Rank[(int)p->AtNumber[i]] ) ) { num++; } } INCHI_HEAPCHK return num; } /****************************************************************/ int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ) { bitWord *McrBits = Mcr->bitword[l-1]; int first = W->first; int next = W->next; int i, j, k; if ( first >= next ) { /* for testing only */ return 0; } for ( i = first, k = 0; i < next; i ++ ) { j = (int)p->AtNumber[i]; if ( !(McrBits[ j / num_bit ] & bBit[ j % num_bit ]) ) { /* BC: reading uninit memory ???-not examined yet */ k += !(p->Rank[j] & rank_mark_bit); /* for testing only */ p->Rank[j] |= rank_mark_bit; } } INCHI_HEAPCHK return k; } /****************************************************************/ void CtPartClear( ConTable *Ct, int k ) { int start; int len; /* connection table */ start = k>1? Ct->nextCtblPos[k-1] : 0; len = Ct->lenCt - start; if ( len > 0 ) { memset( Ct->Ctbl + start, 0, (Ct->lenCt - start)*sizeof(Ct->Ctbl[0]) ); } Ct->lenCt = start; Ct->lenPos = k; INCHI_HEAPCHK } /**********************************************************************************/ /* Sort neighbors according to ranks in ascending order */ void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ) { AT_NUMB *i, *j, *pk, tmp, rj; int k, num = (int)*base++; for( k=1, pk = base; k < num; k++, pk ++ ) { i = pk; j = i + 1; rj = (rank_mask_bit & nRank[(int)*j]); if ( rj < max_rj ) { while ( j > base && rj < (rank_mask_bit & nRank[(int)*i])) { tmp = *i; *i = *j; *j = tmp; j = i --; } } } INCHI_HEAPCHK } /****************************************************************/ /* may need previous Lambda */ void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, ConTable *Ct, int k, int n, int n_tg ) /* k = (new index in Ct->nextAtRank[] and Ct->nextCtblPos[]) + 1 */ { int startCtbl; int startAtOrd; AT_RANK r, rj, nn, j, rj_prev; int i, m; #ifdef INCHI_CANON_USE_HASH CtHash hash = 0; #endif static int count; /* for debug only */ count ++; INCHI_HEAPCHK k --; if ( k ) { startCtbl = Ct->nextCtblPos[k-1]; startAtOrd = Ct->nextAtRank[k-1]-1; /* here p->Rank[p->AtNumber[r-1]] = r */ } else { startCtbl = 0; startAtOrd = 0; } /******* well-defined (by fixed ranks) part of the connection table ************/ r = (rank_mask_bit & p->Rank[(int)p->AtNumber[startAtOrd]]); for ( i = startAtOrd; i < n_tg && r == (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); i++, r ++ ) { Ct->Ctbl[startCtbl++] = r; insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); nn = G[m][0]; /* number of neighbors */ rj_prev = 0; /* debug only */ #ifdef INCHI_CANON_USE_HASH hash = add2crc32( hash, (AT_NUMB)(r + n) ); #endif for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { Ct->Ctbl[startCtbl++] = rj; #ifdef INCHI_CANON_USE_HASH hash = add2crc32( hash, rj ); #endif #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) /* debug only */ if ( rj < rj_prev ) { int stop = 1; /* */ } #endif rj_prev = rj; } } INCHI_HEAPCHK /****************** well-defined part of base hydrogen atoms *******************/ if ( pCD->NumH && Ct->NumH ) { nn = inchi_min(n, i); for ( j = startAtOrd; j < nn; j ++ ) { /* atoms */ Ct->NumH[j] = pCD->NumH[p->AtNumber[j]]; } for ( ; j < i; j ++ ) { /* t-groups */ int data_pos = n + T_NUM_NO_ISOTOPIC * ((int)p->AtNumber[j] - n); for ( m = 0; m < T_NUM_NO_ISOTOPIC; m ++ ) { Ct->NumH[nn ++] = pCD->NumH[data_pos ++]; } } Ct->lenNumH = nn; } else { Ct->lenNumH = 0; } INCHI_HEAPCHK /****************** well-defined part of fixed hydrogen atoms *******************/ if ( pCD->NumHfixed && Ct->NumHfixed ) { nn = inchi_min(n, i); for ( j = startAtOrd; j < nn; j ++ ) { Ct->NumHfixed[j] = pCD->NumHfixed[p->AtNumber[j]]; INCHI_HEAPCHK } /* Ct->lenNumHfixed = nn; */ } else { ;/* Ct->lenNumHfixed = 0; */ } INCHI_HEAPCHK /****************** well-defined part of isotopic keys ***************************/ if ( pCD->iso_sort_key && Ct->iso_sort_key ) { for ( j = startAtOrd; j < i; j ++ ) { Ct->iso_sort_key[j] = pCD->iso_sort_key[p->AtNumber[j]]; } Ct->len_iso_sort_key = i; } else { Ct->len_iso_sort_key = 0; } INCHI_HEAPCHK /****************** well-defined part of isotopic iso_exchg_atnos ***************************/ if ( pCD->iso_exchg_atnos && Ct->iso_exchg_atnos ) { for ( j = startAtOrd; j < i; j ++ ) { Ct->iso_exchg_atnos[j] = pCD->iso_exchg_atnos[p->AtNumber[j]]; } Ct->len_iso_exchg_atnos = i; } else { Ct->len_iso_exchg_atnos = 0; } INCHI_HEAPCHK /******** well-defined part of isotopic keys for fixed hydrogen atoms ************/ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( pCD->iso_sort_key_Hfixed && Ct->iso_sort_key_Hfixed ) { nn = inchi_min(n, i); for ( j = startAtOrd; j < nn; j ++ ) { Ct->iso_sort_key_Hfixed[j] = pCD->iso_sort_key_Hfixed[p->AtNumber[j]]; } Ct->len_iso_sort_key_Hfixed = nn; } else { Ct->len_iso_sort_key_Hfixed = 0; } #endif INCHI_HEAPCHK Ct->lenCt = startCtbl; /* not aways increases */ Ct->nextCtblPos[k] = startCtbl; Ct->nextAtRank[k] = r; Ct->lenPos = k+1; /* the rest of the CTable */ #ifdef INCHI_CANON_USE_HASH while ( i < n ) { r = (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); hash = add2crc32( hash, (AT_NUMB)(r + n) ); r++; insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); nn = G[m][0]; rj_prev = 0; /* debug only */ for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { hash = add2crc32( hash, rj ); } i ++; } Ct->hash[k] = hash; #endif INCHI_HEAPCHK } /****************************************************************/ void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ) { int startCtbl; /*int startAtOrd;*/ k --; if ( k ) { startCtbl = Ct->nextCtblPos[k-1]; /*startAtOrd = Ct->nextAtRank[k-1]-1;*/ /* here p->Rank[p->AtNumber[r-1]] = r */ if ( cmp ) { memset( cmp, 0, k*sizeof(cmp[0]) ); } } else { startCtbl = 0; /*startAtOrd = 0;*/ } if ( !startCtbl || Ct->Ctbl[startCtbl-1] != EMPTY_CT ) { Ct->Ctbl[startCtbl] = EMPTY_CT; } INCHI_HEAPCHK } /****************************************************************/ /* Return value: -1 <=> *Lambda1 < *Lambda2 0 <=> *Lambda1 = *Lambda2 +1 <=> *Lambda1 > *Lambda2 Input: k+1 = value of level at which the comparison is executed (that is, in the calling program k(caller) = k+1) Stars (*) below mark the differences: bSplitTautCompare != 0 => directed graph; compare: non-tautomeric part of CT in layer 0; (*) non-tautomeric H in layer 1; (*) tautomeric part of CT & H in layer 2; (*) fixed H in layer 3; isotopic atoms, non-taut H & t-groups in layer 4; fixed isotopic H in layer 5; <- move to layer 4 bSplitTautCompare == 0 => undirected graph; compare: full CT in Layer 0; (*) taut and non-taut H in Layer 1; (*) * nothing * in layer 2; (*) fixed H in layer 3; isotopic atoms, non-taut H & t-groups in layer 4; fixed isotopic H in layer 5; <- move to layer 4 */ int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ) { int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ int midCt /* end of atoms only Ct */, midNumH=0 /* end of atoms only NumH */, maxVert; int diff, i, k1, k2, lenNumH, len_iso_sort_key, /*mid_iso_sort_key,*/ midAt; int nLayer = 0; k --; i = -1; /* set kLeastForLayer[nLayer].k = (k+1) or -(k+1) kLeastForLayer[nLayer].i = iDiff if all the conditions are met: 1) kLeastForLayer[nLayer].k = 0 2) diff==0 for all layers < nLayer sign: if the final diff < 0 then kLeastForLayer[nLayer].k = -(k+1) else if the final diff > 0 then kLeastForLayer[nLayer].k = +(k+1) k+1 instead of k takes into account k--; statememt above) meaning: ======== abs(kLeastForLayer[nLayer].k) is the greatest level k at which difference at layer nLayer are zeroes of hidden by differences in smaller nLayer. "Hidden by difference in smaller level" means that nLayer of comparison has not been reached because the difference was discovered at a previous layer. Lambda vs zf_zeta comparison ============================================= accept only diff == 0 Lambda vs pzb_rho and pzb_rho_fix comparison ============================================= Maintain kLeastForLayer[] and kLeastForLayerFix[] The algorithm provides that pzb_rho(m-1) < pzb_rho(m) <= pzb_rho_fix Definition: pzb_rho(m-1) < pzb_rho(m) means that ----------------------------------------------- pzb_rho(m-1)[nLayerCurr] == pzb_rho(m)[nLayerCurr] for nLayerCurr = 0..nLayerDiff-1 pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] Definition: pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] means that ------------------------------------------------------------------------- pzb_rho(m-1)[nLayerDiff][i] == pzb_rho(m)[nLayerDiff][i] for i=0..iDdiff-1 pzb_rho(m-1)[nLayerDiff][iDdiff] < pzb_rho(m)[nLayerDiff][iDdiff] This defines nLayerDiff(pzb1, pzb2) where pszb1 = pzb_rho(a), pzb2=pzb_rho(b) (a= 0 *) && ((L_fix < L_rho) || (L_fix == L_rho && I_fix < I_rho)) => qzb_rho_fix = kLeastForLayerFix[L_fix].k if prevoiusly qzb_rho_fix == 0 b) otherwise do not change qzb_rho_fix, except the following: c) Special case L_rho == L_fix && I_rho == I_fix. Let L=L_rho, I = I_rho. Compare 3 valirs: Lambda[L][I], pzb_rho(m)[L][I], pzb_rho_fix[L][I] The algorithm provides pzb_rho(m)[L][I] < pzb_rho_fix[L][I] (pzb_rho(m)[L][I]==pzb_rho_fix[L][I] <=> pzb_rho(m)[L][I]==pzb_rho_fix[L][I] is impossible by construction) There are 3 possibilities: c1) Lambda[L][I] < pzb_rho(m)[L][I] < pzb_rho_fix[L][I] <=> kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k < 0 => qzb_rho := kLeastForLayer[L].k, reject too small Lambda c2) pzb_rho(m)[L][I] < Lambda[L][I] < pzb_rho_fix[L][I] kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k < 0 => qzb_rho := kLeastForLayer[L].k, accept Lambda, rho:=nu c3) pzb_rho(m)[L][I] < pzb_rho_fix[L][I] < Lambda[L][I] kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k > 0 => qzb_rho_fix := kLeastForLayerFix[L].k, reject too big Lambda Case kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k > 0 is impossible because it means pzb_rho_fix < Lambda < pzb_rho(m) <=> pzb_rho_fix < pzb_rho(m) Case (c3) occurs in case of (a) Case (c1) 2. Comparison Lambda vs pzb_rho before reaching discrete partition ---------------------------------------------------------------------- a) (L_rho < L_fix) || (L_rho == L_fix && I_rho < I_fix) => Lambda differs from pzb_rho(m) in the part of pzb_rho(m) that will never change qzb_rho = kLeastForLayer[L_rho].k; reject Labmda or accept pzb_rho(m+1):=Labmda b) (L_rho == L_fix && I_rho > I_fix) && kLeastForLayer[L_rho].k < 0 Lambda < pzb_rho(m), therefore qzb_rho = kLeastForLayer[L_rho].k; reject Labmda c) (L_rho > L_fix) => qzb_rho := 0 because more significant difference may be discovered in layer < L_rho later. The final comparison may be needed at the level of discrete partition. */ if ( cmp ) { for ( i = 0; i <= k && !cmp[i]; i++ ) ; if ( i < k ) { cmp[k] = cmp[i]; return (int)cmp[i]; } } k1 = Ct1->lenPos-1; k2 = Ct2->lenPos-1; #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( k > k1 || k > k2 ) { int stop = 1; } #endif diff = 0; if ( k ) { startCt1 = Ct1->nextCtblPos[k-1]; startCt2 = Ct2->nextCtblPos[k-1]; startAt1 = Ct1->nextAtRank[k-1]-1; startAt2 = Ct2->nextAtRank[k-1]-1; } else { startCt1 = startCt2 = 0; startAt1 = startAt2 = 0; } endCt1 = Ct1->nextCtblPos[k]; endCt2 = Ct2->nextCtblPos[k]; endAt1 = (int)Ct1->nextAtRank[k]-1; endAt2 = (int)Ct2->nextAtRank[k]-1; maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); #ifdef INCHI_CANON_USE_HASH if ( !diff ) { if ( Ct1->hash[k] > Ct2->hash[k] ) diff = 1; else if ( Ct1->hash[k] < Ct2->hash[k] ) diff = -1; } if ( diff ) { goto done; } #endif /************************** lengths **************************************************/ if ( diff = -(startCt1 - startCt2) ) { /* comparing two INFINITY terminations */ if ( bOnlyCommon && startCt1 >= Ct1->nLenCTAtOnly && startCt2 >= Ct2->nLenCTAtOnly && Ct1->Ctbl[startCt1] == EMPTY_CT && Ct2->Ctbl[startCt2] == EMPTY_CT ) { return 0; } if ( bOnlyCommon ) { startCt1 = startCt2 = inchi_min(startCt1, startCt2); startAt1 = startAt2 = inchi_min(startAt1, startAt2); if ( Ct1->lenCt == Ct2->lenCt ) { endCt1 = endCt2 = inchi_max(endCt1, endCt2); endAt1 = endAt2 = inchi_max(endAt1, endAt2); } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; } #endif } else /* comparing (taut tail) vs INFINITY termination -- ??? */ if ( startCt1 > startCt2 && Ct1->maxVert > Ct2->maxVert && startAt2 == Ct2->maxVert ) { return 0; } else { goto done; } } lenNumH = Ct1->lenNumH; len_iso_sort_key = Ct1->len_iso_sort_key; if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ if ( bOnlyCommon ) { endCt1 = endCt2 = inchi_min(endCt1, endCt2); endAt1 = endAt2 = inchi_min(endAt1, endAt2); lenNumH = inchi_min(Ct1->lenNumH, Ct2->lenNumH); len_iso_sort_key = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); } else /* take care of case when comparing tautomeric vs non-tautomeric: since (taut)->maxVert > (non-taut)->maxVert, --??? (taut)->maxlenCt > (non-taut)->maxlenCt --!!! compare up to min out of the two, ignoring INFINITY in the last position */ if ( endCt1 > endCt2 && Ct1->maxlenCt > Ct2->maxlenCt ) { if ( endAt2 == Ct2->maxVert + 1 ) { /* remove INFINITY termination of the shorter CT */ /* should never happen */ endAt2 --; len_iso_sort_key = lenNumH = endAt1 = endAt2; endCt2 --; endCt1 = endCt2; diff = 0; } else if ( endAt2 == Ct2->maxVert ) { /* remove INFINITY termination of CT */ len_iso_sort_key = lenNumH = endAt1 = endAt2; endCt1 = endCt2; diff = 0; } else { goto done; } } else { goto done; } } if ( bSplitTautCompare ) { midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); if ( midCt > endCt1 ) { midCt = endCt1; } midAt = inchi_min(maxVert, endAt1); } else { midCt = endCt1; midAt = endAt1; } /*endCt = min(endCt1, endCt2);*/ /*************************************************************************/ /************ layer 0: connection table without tautomeric groups ********/ /*************************************************************************/ for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) /*for ( i = startCt1; i < endCt && !(diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]); i ++ )*/ ; if ( i < midCt ) { diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; goto done; } /*************************************************************************/ /******** layer 1 NumH: H atoms without tautomeric H *********************/ /*************************************************************************/ nLayer ++; /*============= check limits for consistency ==========*/ if ( diff = -(startAt1 - startAt2) ) { goto done; /* should not happen */ } if ( diff = -(endAt1 - endAt2) ) { goto done; /* should not happen */ } /*============= comparison =============================*/ if ( Ct1->NumH && Ct2->NumH ) { if ( endAt1 < maxVert ) { midNumH = lenNumH = endAt1; } else if ( bSplitTautCompare ) { midNumH = maxVert; } else { midNumH = lenNumH; } /* lenNumH = (endAt2 >= maxVert)? lenNumH : endAt2; */ /* endAt1 = (endAt2 == n)? lenNumH : endAt2; */ for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) ; if ( i < midNumH ) { diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; goto done; } } /*************************************************************************/ /************** layer 2: tautomeric part of CT and tautomeric H **********/ /*************************************************************************/ nLayer ++; for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) ; /* compare tautomeric groups part of CT */ if ( i < endCt1 ) { diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; goto done; } if ( Ct1->NumH && Ct2->NumH ) { for ( i = midNumH; i < lenNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) ; /* compare tautomeric H */ if ( i < lenNumH ) { diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; i += endCt1 - midCt; goto done; } } /*************************************************************************/ /************** layer 3: Fixed H atoms ***********************************/ /*************************************************************************/ nLayer ++; if ( Ct1->NumHfixed && Ct2->NumHfixed ) { for ( i = startAt1; i < midAt && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) ; if ( i < midAt ) { diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; goto done; } } /*************************************************************************/ /************** layer 4: isotopic atoms H, incl. tautomeric **************/ /*************************************************************************/ nLayer ++; if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) ; if ( i < endAt1 ) { diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; goto done; } } if ( Ct1->iso_exchg_atnos && Ct2->len_iso_exchg_atnos ) { for ( i = startAt1; i < endAt1 && Ct1->iso_exchg_atnos[i] == Ct2->iso_exchg_atnos[i]; i ++ ) ; if ( i < endAt1 ) { diff = Ct1->iso_exchg_atnos[i] > Ct2->iso_exchg_atnos[i]? 1:-1; goto done; } } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) /*************************************************************************/ /************** layer 6: Fixed isotopic H atoms **************************/ /*************************************************************************/ nLayer ++; if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) ; if ( i < midAt ) { diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; goto done; } } #endif done: #ifdef INCHI_CANON_MIN diff = -diff; #endif if ( diff ) { diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ if ( kLeastForLayer ) { #if ( bRELEASE_VERSION != 1 ) if ( abs(kLeastForLayer[nLayer].k) > k+1 ) { /* for debug only */ int stop = 1; /* */ } #endif if ( !kLeastForLayer[nLayer].k ) { kLeastForLayer[nLayer].k = (diff > 0)? (k+1) : -(k+1); kLeastForLayer[nLayer].i = i; } if ( nLayer /* && !bOnlyCommon */) { diff = 0; } } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* for debug only */ } #endif if ( cmp ) { cmp[k] = (diff > 0)? 1 : (diff < 0)? -1 : 0; } return diff; } /**************************************************************************************************************/ int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ) { int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ int midCt /* end of atoms only in Ctbl */, midNumH = 0 /* end of atoms only NumH */, midAt /* end of atoms only */; int diff, i, k1, k2, lenNumH1, lenNumH2, lenNumH, maxVert /* min num atoms */; int len_iso_sort_key1, len_iso_sort_key2, len_iso_sort_key /*, mid_iso_sort_key*/; int nLayer = 0; k1 = Ct1->lenPos-1; k2 = Ct2->lenPos-1; diff = 0; startCt1 = startCt2 = 0; startAt1 = startAt2 = 0; endCt1 = Ct1->nextCtblPos[k1]; endCt2 = Ct2->nextCtblPos[k2]; endAt1 = (int)Ct1->nextAtRank[k1]-1; endAt2 = (int)Ct2->nextAtRank[k2]-1; maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); if ( bOnlyCommon ) { endCt1 = inchi_min(endCt1, endCt2); endCt1 = endCt2 = inchi_min(endCt1, Ct1->lenCt); endAt1 = endAt2 = inchi_min(endAt1, endAt2); if ( Ct1->Ctbl[endCt1] == EMPTY_CT || Ct1->Ctbl[endCt1] == 0 || Ct2->Ctbl[endCt1] == EMPTY_CT || Ct2->Ctbl[endCt1] == 0 ) { endCt1 = endCt2 = endCt1-1; } lenNumH = lenNumH1 = lenNumH2 = inchi_min(Ct1->lenNumH, Ct2->lenNumH); len_iso_sort_key = len_iso_sort_key1 = len_iso_sort_key2 = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); } else { if ( Ct1->Ctbl[endCt1-1] == EMPTY_CT ) { endCt1 --; } if ( Ct2->Ctbl[endCt2-1] == EMPTY_CT ) { endCt2 --; } lenNumH1 = Ct1->lenNumH; lenNumH2 = Ct2->lenNumH; lenNumH = inchi_min(lenNumH1, lenNumH2); len_iso_sort_key1 = Ct1->len_iso_sort_key; len_iso_sort_key2 = Ct2->len_iso_sort_key; len_iso_sort_key = inchi_min(len_iso_sort_key1, len_iso_sort_key2); } if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ goto done; } if ( bSplitTautCompare ) { midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); if ( midCt > endCt1 ) { midCt = endCt1; } midAt = inchi_min(maxVert, endAt1); } else { midCt = endCt1; midAt = endAt1; } /*************************************************************************/ /************ layer 0: connection table without tautomeric groups ********/ /*************************************************************************/ for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) ; if ( i < midCt ) { diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; goto done; } /*************************************************************************/ /************* layer 1: H atoms without tautomeric H *********************/ /*************************************************************************/ nLayer ++; if ( Ct1->NumH && Ct2->NumH ) { if ( diff = -(lenNumH1 - lenNumH2) ) { /* negative sign reproduces results for NSC=28393 */ goto done; } if ( endAt1 < maxVert ) { midNumH = lenNumH1 = endAt1; } else if ( bSplitTautCompare ) { midNumH = maxVert; } else { midNumH = lenNumH1; } for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) ; if ( i < midNumH ) { diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; goto done; } } /*************************************************************************/ /************** layer 2: tautomeric part of CT and tautomeric H **********/ /*************************************************************************/ nLayer ++; for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) ; /* compare tautomeric groups part of CT */ if ( i < endCt1 ) { diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; goto done; } if ( Ct1->NumH && Ct2->NumH ) { for ( i = midNumH; i < lenNumH1 && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) ; /* compare tautomeric H */ if ( i < lenNumH1 ) { diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; goto done; } } /*************************************************************************/ /************** layer 3: Fixed H atoms ***********************************/ /*************************************************************************/ nLayer ++; if ( Ct1->NumHfixed && Ct2->NumHfixed ) { for ( i = startAt1; i < endAt1 && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) ; if ( i < endAt1 ) { diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; goto done; } } /*************************************************************************/ /************** layer 4: isotopic atoms, H and isotopic taut H ***********/ /*************************************************************************/ nLayer ++; if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { if ( diff = -(len_iso_sort_key1 - len_iso_sort_key2) ) { /* negative sign reproduces results for NSC=28393 */ goto done; } for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) ; if ( i < endAt1 ) { diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; goto done; } } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) /*************************************************************************/ /************** layer 6: Fixed isotopic H atoms **************************/ /*************************************************************************/ nLayer ++; if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) ; if ( i < midAt ) { diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; goto done; } } #endif done: #ifdef INCHI_CANON_MIN diff = -diff; #endif if ( diff ) { diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ } return diff; } /****************************************************************/ int CtFullCompareLayers( kLeast *kLeastForLayer ) { int iLayer; /* check for the rejection condition: Lambda > zb_rho_fix */ for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { if ( kLeastForLayer[iLayer].k ) { return (kLeastForLayer[iLayer].k > 0)? (iLayer+1) : -(iLayer+1); } } return 0; } /**************************************************************/ int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, int *L_rho, int *I_rho, int *k_rho ) { int iLayer; if ( kLeast_rho ) { for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { if ( kLeast_rho[iLayer].k ) { *L_rho = iLayer; *I_rho = kLeast_rho[iLayer].i; *k_rho = kLeast_rho[iLayer].k; break; } } if ( iLayer == MAX_LAYERS ) { if ( nOneAdditionalLayer ) { *L_rho = nOneAdditionalLayer; /* ??? subtract 1 ??? */ *I_rho = -1; *k_rho = 0; return 0; /* difference may be in the first additional layer */ } else { *L_rho = INFINITY; *I_rho = -1; *k_rho = 0; return 0; /* no difference found */ } } else { return 1; /* difference in a real layer */ } } else { return -1; /* no input, should not happen */ } } /**************************************************************/ int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ) { int L_rho, I_rho, k_rho; if ( 0 < CtCompareLayersGetFirstDiff( kLeast_rho, nOneAdditionalLayer, &L_rho, &I_rho, &k_rho ) && /* differences has been found in a real layer or all real layers are identical */ L_rho <= L_rho_fix_prev ) { /* in this layer pzb_rho == pzb_rho_fix or in the previous real layer */ return k_rho > 0? (L_rho+1) : -(L_rho+1); } return 0; } /****************************************************************/ void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ) { int i; if ( kLeastForLayer ) { for ( i = 0; i < MAX_LAYERS; i ++ ) { if ( abs(kLeastForLayer[i].k) >= hzz ) { kLeastForLayer[i].k = 0; kLeastForLayer[i].i = 0; } } } } /****************************************************************/ void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ) { int startCt1, startCt2, endCt1, endCt2; int len2, len2H, len2Hfixed, len2iso_sort_key, len2iso_exchg_atnos, i; int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) int len2iso_sort_key_Hfixed; #endif k --; if ( k ) { startCt1 = Ct1->nextCtblPos[k-1]; startCt2 = Ct2->nextCtblPos[k-1]; startAt1 = Ct1->nextAtRank[k-1]-1; startAt2 = Ct2->nextAtRank[k-1]-1; } else { startCt1 = startCt2 = 0; startAt1 = startAt2 = 0; } endCt1 = Ct1->nextCtblPos[k]; endCt2 = Ct2->nextCtblPos[k]; endAt1 = (int)Ct1->nextAtRank[k]-1; endAt2 = (int)Ct2->nextAtRank[k]-1; len2 = endCt2-startCt2; /* len = min(len1, len2); */ #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( startCt1 != startCt2 || startAt1 != startAt2 ) { int stop = 1; } #endif /* copy connection table: Ctbl */ for ( i = 0; i < len2; i ++ ) { Ct1->Ctbl[startCt1+i] = Ct2->Ctbl[startCt2+i]; } /* copy number of H: NumH */ len2H = 0; if ( Ct1->NumH && Ct2->NumH ) { len2H = endAt2-startAt2; if ( endAt2 > Ct2->maxVert ) { len2H = Ct2->lenNumH - startAt2; } for ( i = 0; i < len2H; i ++ ) { Ct1->NumH[startAt1+i] = Ct2->NumH[startAt2+i]; } } /* copy number of fixed H */ len2Hfixed = 0; if ( Ct1->NumHfixed && Ct2->NumHfixed ) { len2Hfixed = endAt2-startAt2; for ( i = 0; i < len2Hfixed; i ++ ) { Ct1->NumHfixed[startAt1+i] = Ct2->NumHfixed[startAt2+i]; } } /* copy isotopic keys */ len2iso_sort_key = 0; if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { len2iso_sort_key = endAt2-startAt2; for ( i = 0; i < len2iso_sort_key; i ++ ) { Ct1->iso_sort_key[startAt1+i] = Ct2->iso_sort_key[startAt2+i]; } } len2iso_exchg_atnos = 0; if ( Ct1->iso_exchg_atnos && Ct2->iso_exchg_atnos ) { len2iso_exchg_atnos = endAt2-startAt2; for ( i = 0; i < len2iso_exchg_atnos; i ++ ) { Ct1->iso_exchg_atnos[startAt1+i] = Ct2->iso_exchg_atnos[startAt2+i]; } } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) len2iso_sort_key_Hfixed = 0; if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { len2iso_sort_key_Hfixed = endAt2-startAt2; for ( i = 0; i < len2iso_sort_key; i ++ ) { Ct1->iso_sort_key_Hfixed[startAt1+i] = Ct2->iso_sort_key_Hfixed[startAt2+i]; } } #endif Ct1->lenCt = startCt1 + len2; Ct1->nextCtblPos[k] = startCt1 + len2; Ct1->nextAtRank[k] = Ct2->nextAtRank[k]; if ( len2H ) { Ct1->lenNumH = startAt1 + len2H; } /* if ( len2Hfixed ) { Ct1->lenNumHfixed = startAt1 + len2Hfixed; } */ if ( len2iso_sort_key ) { Ct1->len_iso_sort_key = startAt1 + len2iso_sort_key; } if ( len2iso_exchg_atnos ) { Ct1->len_iso_exchg_atnos = startAt1 + len2iso_exchg_atnos; } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( len2iso_sort_key_Hfixed ) { Ct1->len_iso_sort_key_Hfixed = startAt1 + len2iso_sort_key_Hfixed; } #endif #ifdef INCHI_CANON_USE_HASH Ct1->hash[k] = Ct2->hash[k]; #endif Ct1->lenPos = k+1; INCHI_HEAPCHK } /****************************************************************/ void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ) { /* Ct1 does not have INFINITY termination */ int k; for ( k = 0; k < Ct2->lenPos; k ++ ) { CtPartCopy( Ct1 /* to */, Ct2 /* from */, k+1 ); } } /****************************************************************/ void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ) { int i, j, k, mcr, num; AT_RANK next; bitWord *McrBits = McrSet->bitword[l-1]; bitWord *FixBits = FixSet->bitword[l-1]; int len = McrSet->len_set*sizeof(bitWord); memset( McrBits, 0, len ); memset( FixBits, 0, len ); for ( i = 0; i < n; i ++ ) { p->equ2[i] = INFINITY; /* for debug only */ } for ( i = 0; i < n; i ++ ) { j = (int)(next = gamma->nAtNumb[i]); if ( j == i ) { FixBits[ i / num_bit ] |= bBit[ i % num_bit ]; McrBits[ i / num_bit ] |= bBit[ i % num_bit ]; /* p->next[i] = INFINITY; */ /* no link to same orbit points */ p->equ2[i] = next; /* fixed point */ } else if ( !(rank_mark_bit & next) ) { gamma->nAtNumb[i] |= rank_mark_bit; mcr = inchi_min(j, i); num = 0; /* mark all nodes in the cycle to ignore later; find mcr */ while( !(rank_mark_bit & (next = gamma->nAtNumb[j])) ) { gamma->nAtNumb[j] |= rank_mark_bit; j = (int)next; if ( mcr > j ) { mcr = j; } num ++; } McrBits[ mcr / num_bit ] |= bBit[mcr % num_bit]; /* save mcr */ /* fill out the unordered partition, the mcr first, other in the cycle after that */ p->equ2[mcr] = mcr; for ( k = mcr; mcr != (j = (int)(rank_mask_bit & gamma->nAtNumb[k])); k = j ) { p->equ2[j] = mcr; } } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) /* for debug only */ for ( i = 0; i < n; i ++ ) { if ( p->equ2[i] >= n ) { int stop = 1; } } #endif /* remove the marks */ for ( i = 0; i < n; i ++ ) { gamma->nAtNumb[i] &= rank_mask_bit; } INCHI_HEAPCHK } /****************************************************************/ int SetBitCreate( void ) { bitWord b1, b2; AT_NUMB n1, n2; #ifdef INCHI_CANON_USE_HASH CtHash h1, h2; #endif int i; if ( bBit ) { INCHI_HEAPCHK return 0; /* already created */ } b1 = 1; num_bit = 1; for ( b1=1, num_bit=1; b1 < (b2 = (bitWord)((b1 << 1)& BIT_WORD_MASK)); b1 = b2, num_bit ++ ) ; bBit = (bitWord*)inchi_calloc( num_bit, sizeof(bitWord)); if ( !bBit ) { INCHI_HEAPCHK return -1; /* failed */ } for ( i = 0, b1=1; i < num_bit; i++, b1 <<= 1 ) { bBit[i] = b1; } for ( n1 = 1; n1 < (n2 = (AT_RANK)((n1 << 1)& AT_RANK_MASK) ); n1 = n2 ) ; rank_mark_bit = n1; rank_mask_bit = ~n1; #ifdef INCHI_CANON_USE_HASH for ( h1 = 1; h1 < (h2 = (h1 << 1)); h1 = h2 ) ; hash_mark_bit = h1; #endif INCHI_HEAPCHK return 1; } /****************************************************************/ int SetBitFree( void ) { if ( bBit ) { inchi_free( bBit ); bBit = NULL; INCHI_HEAPCHK return 1; /* success */ } INCHI_HEAPCHK return 0; /* already destroyed */ } #ifdef NEVER /* { how to renumber a graph */ /*********************************************************************/ void RenumberTheGraph( int n, NEIGH_LIST *NeighList, AT_NUMB *old2new, AT_NUMB *new2old, S_CHAR *mark, int bDone ) { int i, k, j; NEIGH_LIST nl; /* renumber neighbors */ for ( i = 0; i < n; i ++ ) { for ( j = 1; j <= NeighList[i][0]; j ++ ) { NeighList[i][j] = old2new[NeighList[i][j]]; } } /* rearrange NeighList in situ using new2old[] */ for ( k = 0; k < n; k ++ ) { if ( mark[k] & bDone ) continue; if ( k == ( j = new2old[k] ) ) { mark[k] |= bDone; continue; } /* transposition cycle */ i = k; nl = NeighList[k]; do { NeighList[i] = NeighList[j]; mark[i] |= bDone; i = j; } while ( k != ( j = new2old[i] ) ); NeighList[i] = nl; mark[i] |= bDone; } #ifdef NEVER /* rearrange NeighList in situ using old2new[] */ s = 0; for ( k = 0; k < n; k ++ ) { if ( mark[k] & bDone ) continue; if ( k == ( j = old2new[k] ) ) { mark[k] |= bDone; continue; } /* transposition cycle */ i = k; /* NeighList[j] goes to ith position */ nl2[s] = NeighList[j]; s ^= 1; do { nl2[s] = NeighList[i]; NeighList[i] = nl2[s ^= 1]; mark[i] |= bDone; i = j; j = old2new[i]; } while ( k != ( j = old2new[i] ) ); NeighList[j] = nl2[s ^= 1]; mark[j] |= bDone; } #endif } /***************************************************************************************/ void RearrangeAtRankArray ( int n, AT_RANK *nRank, AT_NUMB *new2old, S_CHAR *mark, int bDone ) { int i, k, j; AT_RANK r; /* rearrange the array in situ using new2old[] */ for ( k = 0; k < n; k ++ ) { if ( mark[k] & bDone ) continue; if ( k == ( j = new2old[k] ) ) { mark[k] |= bDone; continue; } /* transposition cycle */ i = k; r = nRank[k]; do { nRank[i] = nRank[j]; mark[i] |= bDone; i = j; } while ( k != ( j = new2old[i] ) ); nRank[i] = r; mark[i] |= bDone; } } /***************************************************************************************/ void RenumberAtNumbArray( int n, AT_NUMB *nAtNumb, AT_NUMB *old2new ) { int i; for ( i = 0; i < n; i ++ ) { nAtNumb[i] = old2new[nAtNumb[i]]; } } /****************************************************************/ int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, AT_RANK **pRankStack, int nNumPrevRanks, AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT* pCS ) { void *pzb_rho=NULL; int ret, cmp1=0, cmp2=0; int i, j, k, n; AT_NUMB *old2new = NULL, *new2old = NULL, m, r1, r2; S_CHAR *mark = NULL; int nMaxLenCt = pCS->nMaxLenLinearCT; AT_RANK *pCt = pCS->LinearCT; int nLenCt = pCS->nLenLinearCT; AT_RANK *pCt0 = pCS->LinearCT; int nLenCt0 = pCS->nLenLinearCT; CANON_DATA CanonData; CANON_DATA *pCD = &CanonData; CANON_COUNTS CanonCounts; CANON_COUNTS *pCC = &CanonCounts; memset (pCD, 0, sizeof(pCD[0])); memset (pCC, 0, sizeof(pCC[0])); /* pointers */ pCD->LinearCT = pCS->LinearCT; /* variables - unchanged */ pCD->ulTimeOutTime = pCS->ulTimeOutTime; pCD->nMaxLenLinearCT = pCS->nMaxLenLinearCT; /* return values & input/output */ pCD->nLenLinearCT = pCS->nLenLinearCT; pCC->lNumBreakTies = pCS->lNumBreakTies; pCC->lNumDecreasedCT = pCS->lNumDecreasedCT; pCC->lNumRejectedCT = pCS->lNumRejectedCT; pCC->lNumEqualCT = pCS->lNumEqualCT; pCC->lNumTotCT = pCS->lNumTotCT; ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, NULL, &pzb_rho ); pCS->nLenLinearCT = pCD->nLenLinearCT; pCS->lNumBreakTies = pCC->lNumBreakTies; pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; pCS->lNumRejectedCT = pCC->lNumRejectedCT; pCS->lNumEqualCT = pCC->lNumEqualCT; pCS->lNumTotCT = pCC->lNumTotCT; /* save the connection table for comparison with the 2nd one */ pCt0 = (AT_RANK*)inchi_calloc(nMaxLenCt, sizeof(pCt0[0])); memcpy(pCt0, pCS->LinearCT, nMaxLenCt*sizeof(pCt0[0])); nLenCt0 = pCS->nLenLinearCT; /**********************************************************/ /* rearrange numbering to make canon. numbering the first */ /**********************************************************/ n = num_at_tg; /* 1. get transpositions */ old2new = (AT_NUMB*) inchi_calloc( n, sizeof(old2new[0]) ); new2old = (AT_NUMB*) inchi_calloc( n, sizeof(new2old[0]) ); mark = (S_CHAR *) inchi_calloc( n, sizeof(mark[0]) ); for ( i = 0; i < n; i ++ ) { /* forward transposition: at[i] -> at[old2new[i]] position */ old2new[i] = m = nCanonRank[i]-1; /* forward transposition: at[new2old[i]] -> at[i] position */ new2old[m] = i; } /* rearrange input data according to the new numbering */ RenumberTheGraph( n, NeighList, old2new, new2old, mark, 1 ); RearrangeAtRankArray ( n, pRankStack[0], new2old, mark, 2 ); RenumberAtNumbArray ( n, pRankStack[1], old2new ); /* make sure the atom numbers are sorted */ for ( i = k = 0, r1 = pRankStack[0][pRankStack[1][i]]; i < n; r1 = r2) { for ( j = i++; i < n && r1 == (r2 = pRankStack[0][pRankStack[1][i]]); i ++ ) ; if ( i - j > 1 ) { k += insertions_sort_AT_RANK( pRankStack[1]+j, i-j ); } } ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, &pzb_rho, NULL ); pCS->nLenLinearCT = pCD->nLenLinearCT; pCS->lNumBreakTies = pCC->lNumBreakTies; pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; pCS->lNumRejectedCT = pCC->lNumRejectedCT; pCS->lNumEqualCT = pCC->lNumEqualCT; pCS->lNumTotCT = pCC->lNumTotCT; /* compare the connection tables */ cmp1 = nLenCt0 - pCS->nLenLinearCT; cmp2 = memcmp( pCt0, pCS->LinearCT, pCS->nLenLinearCT*sizeof(pCt0[0])); #ifdef _DEBUG if ( cmp1 || cmp2 ) { int stop = 1; } #endif /**********************************************************/ /* rearrange numbering back to the original numbering */ /**********************************************************/ /* restore the input data to its original numbering */ RenumberTheGraph( n, NeighList, new2old, old2new, mark, 4 ); RearrangeAtRankArray ( n, pRankStack[0], old2new, mark, 8 ); RenumberAtNumbArray ( n, pRankStack[1], new2old ); /* rearrange the output data to the original numbering */ RearrangeAtRankArray ( n, nCanonRank, old2new, mark, 16 ); RenumberAtNumbArray ( n, pCS->nPrevAtomNumber, new2old ); RearrangeAtRankArray ( n, nSymmRank, old2new, mark, 32 ); /* free memory */ CTableFree( pzb_rho ); if ( pzb_rho ) { inchi_free( pzb_rho ); } inchi_free( old2new ); inchi_free( new2old ); inchi_free( mark ); inchi_free( pCt0 ); return ret; } #endif /* } */ #define QZFIX_OK(X) ((X)<=0) int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ) { int nLastLayer = -1, nNumLast = 0, nLayer = 0; if ( !pCD || !pzb_rho_fix ) { return 0; } nLayer ++; /* 1 */ if ( pCD->NumH && !pzb_rho_fix->NumH ) { nLastLayer = nLayer; nNumLast ++; } nLayer ++; /* 2 */ if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt ) { nLastLayer = nLayer; nNumLast ++; } nLayer ++; /* 3 */ if ( pCD->NumHfixed && !pzb_rho_fix->NumHfixed ) { nLastLayer = nLayer; nNumLast ++; } nLayer ++; /* 4 */ if ( pCD->iso_sort_key && !pzb_rho_fix->iso_sort_key ) { nLastLayer = nLayer; nNumLast ++; } /* nLayer ++; // 5 if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pCD->iso_sort_key && (pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt || !pzb_rho_fix->iso_sort_key ) ) { nLastLayer = nLayer; nNumLast ++; } */ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) nLayer ++; /* 6 */ if ( pCD->iso_sort_key_Hfixed && !pzb_rho_fix->iso_sort_key_Hfixed ) { nLastLayer = nLayer; nNumLast ++; } #endif if ( 1 == nNumLast ) { return nLastLayer; } return 0; } /*#define QZFIX_OK(X) (!(X))*/ /********************** CanonGraph ************************************* * A naive implementation of graph canonical numbering algorithm * * from "Practical Graph Isomorphism" by Brendan D. McKay, * * Congressus Numerantium, Vol. 30 (1981), pp. 45 - 87. * * Note: Several typos fixed, added chem. struct. specifics * ***********************************************************************/ /* on entry: pi[0] is equitable */ /*******************************************************10/21/2003****** * Later add optimization: if Aut(G) <= Aut(G0) due to some additional * * layer of coloring applied to G and the following is known about G0: * * * * * 0) canonical numbering of G should be same as that of G0 * * * * 1) canonical numbering as v= v0[n] {vertex number v from * * G0 canonical number n) * * 2) orbits of Aut(G0) as UnorderedPartition theta0 * * * * then when choosing next v[i] for refining the partition consider * * only vertices from the Aut(G0) orbit of V(i), that is, only such * * v[i] that: * * * * GetUnorderedPartitionMcrNode( &theta0, v[i] ) == * * GetUnorderedPartitionMcrNode( &theta0, v0[i] ) * ***********************************************************************/ int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, CANON_DATA *pCD, CANON_COUNTS *pCC, ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) { /* bDigraph != 0 means consider edges from atoms to t-groups as directed, that is, do not include t-group ranks in comparing neighbors when refining partition */ /* Always set lab = true dig = true */ /* in the comments: m = |zeta| r = |rho| m < n or r < n in case pi[k] in P (i.e. satisfies Lemma 2.25) Just after passing point B: =========================== K = k-1 wi = v[i], i = 1..K Gamma(0) = Gamma = Aut(G)pi Gamma(i) = Gamma(w1,w2,...,wi) pointwise stabilizer for i=1..K zeta is a terminal node => the coarsest equitable partition that fixes w1,...,wK is discrete => Gamma(K)=1 At point A only: index = |Gamma(k-1)|/|Gamma(k)| At points A and B: size = |Gamma(k-1)| theta = theta(Gamma(k-1)); Gamma(k-1) = , where Y is the set of all automprhisms output up to the present stage (in Step 10 = L10 ) |Y| <= n - |theta| */ AT_RANK *pCt = pCD->LinearCT; /*int nMaxLenCt = pCD->nMaxLenLinearCT;*/ int *nLenCt = &pCD->nLenLinearCT; CANON_DATA *pCD1 = pCD; int i, k, k2, index, l, ok, ret=0, res; int t_Lemma; /* hh: if pi[k-1] satisfies Lemma 2-25 then t_Lemma = min{i| i=1..k && pi[i-1] satisfies Lemma 2-25}*/ /* otherwise t_Lemma = k --> here this is always the case */ int t_eq_zeta; /* ht: min{i|i=1..m && all terminal modes descended from or equal to zeta(i) have been shown to be equivalent}. */ int h_zeta; /* h: the longest common ancestor of zeta and nu is nu(h_zeta) */ int h_rho; /* hb: the longest common ancestor of rho and nu is nu(h_rho) */ int hz_rho; /* hzb: max{i|i=1..min(k,r) && Lambda(G,pi,nu(i)) == Lambda(G,pi,rho(i))} */ int hz_zeta; /* hzf: max{i|i=1..min(k,m) && Lambda(G,pi,nu(i)) == Lambda(G,pi,zeta(i))} */ int qzb_rho; /* Ct(Lambda[k]) - Ct(rho[k]) */ double size; /* |Aut(G)| */ int nNumLayers = (NULL != pCD->NumH) + (NULL != pCD->NumHfixed) + /* (bDigraph && pCD->nLenLinearCT > pCD->nLenCTAtOnly)*/ /* ??? tautomeric */ (NULL != pCD->iso_sort_key) #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + (NULL != pCD->iso_sort_key_Hfixed) #endif ; int dig = (bDigraph || nNumLayers); int bSplitTautCompare = (bDigraph || nNumLayers); /* compare taut. H and tgroups connections after H */ /* digraph: 1=>do not use Lemma 2.25, 0 => use */ int lab = 1; /* label: 1=>find canonical numbering; 0=>do not find canonical numbering, do not use rho */ int r; /* |rho| */ int bZetaEqRho = lab; int bZetaIsomorph; long lNumEqlZeta; const int L = MAX_SET_SIZE; UnorderedPartition theta, theta_from_gamma; Cell *W; /* W[i] is the first non-trivial cell of pi[i+1] */ Node *v; /* v[i] is in W[i] to create T(G,pi,nu[i+1]) */ Node tvc, tvh; S_CHAR *e, *qzb=NULL; /* qzb = NULL always */ /* current node CT */ ConTable Lambda; /* first leaf CT */ ConTable zf_zeta; /* Ct for zeta, the first discovered terminal node */ /* best leaf/node CT: find the greatest pzb_rho possibly subject to pzb_rho[k] <= pzb_rho_fix[k] condition */ ConTable *pzb_rho = NULL; /* Ct for rho, the best discovered terminal node */ /* fixed input CT: for all k pzb_rho[k] <= pzb_rho_fix[k]; at the end pzb_rho == pzb_rho_fix */ ConTable *pzb_rho_fix = (pp_zb_rho_inp && *pp_zb_rho_inp)? *pp_zb_rho_inp:NULL; NodeSet Omega; /* MAX_SET_SIZE */ NodeSet Phi; /* MAX_SET_SIZE */ NodeSet cur_nodes; /* 1 each */ Transposition gamma; Partition zeta; /* the first discovered terminal node */ Partition rho; /* the best discovered terminal node */ int nNumFoundGenerators=0; int qzb_rho_fix = 0; int hzb_rho_fix = 0; int bRhoIsDiscrete = 1; kLeast kLeast_rho[MAX_LAYERS]; kLeast kLeast_rho_fix[MAX_LAYERS]; int nOneAdditionalLayer; int pzb_rho_fix_reached = 0; int L_rho_fix_prev = 0, I_rho_fix_prev=-1, k_rho_fix_prev=0; /* Note: Layered comparison should be consistent, especially in layer numbers. Layered comparison is implemented in: CtFullCompare() CtPartCompare() GetOneAdditionalLayer() The partial comparison results in kLeast[] are used in CtFullCompareLayers() CtPartCompareLayers() CtCompareLayersGetFirstDiff() UpdateCompareLayers() */ nOneAdditionalLayer = GetOneAdditionalLayer( pCD1, pzb_rho_fix ); /* next 2 lines for debug only */ /* num_g++; */ /* WriteGraph( G, n_tg, num_g, "V:\\IChI_v10\\Gordon-Graphs\\hard\\k06g08v312-alt.dre", "a+" ); */ /* memory allocation */ if ( 0 > SetBitCreate() ) { return -1; } if ( pzb_rho_fix && pzb_rho_fix->nLenCTAtOnly != pCD->nLenCTAtOnly ) { /* consistency check */ return -2; } ok = 1; ok &= UnorderedPartitionCreate( &theta, n_tg ); ok &= UnorderedPartitionCreate( &theta_from_gamma, n_tg ); ok &= (NULL != (W = (Cell*)inchi_calloc(n_tg, sizeof(W[0])))); ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(v[0])))); ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(e[0])))); /* ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(W[0])))); ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(W[0])))); */ /* ok &= (NULL != (qzb = (S_CHAR*)calloc(n_tg, sizeof(W[0])))); */ ok &= CTableCreate( &Lambda, n, pCD ); ok &= CTableCreate( &zf_zeta, n, pCD ); ok &= ( (pzb_rho = (ConTable *)inchi_calloc( 1, sizeof( *pzb_rho ) ) ) && CTableCreate( pzb_rho, n, pCD ) ); ok &= NodeSetCreate( &Omega, n_tg, MAX_SET_SIZE ); ok &= NodeSetCreate( &Phi, n_tg, MAX_SET_SIZE ); ok &= NodeSetCreate( &cur_nodes, n_tg, 1 ); ok &= PartitionCreate( &zeta, n_tg); ok &= PartitionCreate( &rho, n_tg); ok &= TranspositionCreate( &gamma, n_tg ); INCHI_HEAPCHK /*L1:*/ k = 1; size = 1.0; h_zeta = hz_rho = index = l = 0; if ( !ok ) { goto exit_function; /* initialization failed */ } UnorderedPartitionMakeDiscrete(&theta, n_tg); t_Lemma = 2; pCC->lNumBreakTies = 0; pCC->lNumDecreasedCT = 0; pCC->lNumRejectedCT = 0; pCC->lNumEqualCT = 1; pCC->lNumTotCT = 0; lNumEqlZeta = 1; hzb_rho_fix = 1; memset( kLeast_rho, 0, sizeof(kLeast_rho) ); memset( kLeast_rho_fix, 0, sizeof(kLeast_rho_fix) ); if ( PartitionIsDiscrete( &pi[k-1], n_tg ) ) { /* added the following 3 lines to the original to create Ct */ PartitionCopy( &rho, &pi[k-1], n_tg ); CtPartFill( G, pCD, &pi[k-1], pzb_rho, 1, n, n_tg ); CtPartInfinity( pzb_rho, qzb, 2 ); pCC->lNumTotCT ++; r = k; /* goto L18; */ goto exit_function; } if ( !dig && PartitionSatisfiesLemma_2_25( &pi[0], n ) ) t_Lemma = 1; /* PartitionGetFirstCell( &pi[k-1], &W[k-1], k, n ); v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); CtPartClear( &Lambda, 1 ); e[k-1] = 0; */ CtPartClear( &Lambda, 1 ); INCHI_HEAPCHK /* L2: reach the first leaf and save it in zeta and rho */ while( k ) { /* the two next lines intentionally switched */ /* Create equitable partition in pi[k] */ PartitionGetFirstCell( &pi[k-1], W, k, n ); v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); e[k-1] = 0; if ( dig || !PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) t_Lemma = k+1; /* e[k-1] = 0; */ { Node vv = v[k-1]; if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], vv /*v[k-1]*/, n, n_tg, n_max, bDigraph, 0 )) ) { goto exit_error; }} pCC->lNumBreakTies ++; k ++; CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); /* return -1; *//* debug only */ /* if(h_zeta==0)goto L5; L5: */ /* the first terminal node has not been reached yet */ /* search for the predefined numbering */ if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); if ( QZFIX_OK(qzb_rho_fix) ) { hzb_rho_fix = k; } } if ( lab && QZFIX_OK(qzb_rho_fix) ) /* DCh */ CtPartCopy( pzb_rho, &Lambda, k-1 ); CtPartCopy( &zf_zeta, &Lambda, k-1 ); /*goto L4; L4:*/ if ( PartitionIsDiscrete( &pi[k-1], n ) ) { break; /* goto L7; */ } /* goto L2; */ } pCC->lNumTotCT ++; /* L7; L7: */ /* if ( h_zeta == 0 ) goto L18; L18:*/ h_zeta = t_eq_zeta = hz_zeta = k; CtPartInfinity( &zf_zeta, NULL, k ); /******************** <<<===== B **************************/ PartitionCopy( &zeta, &pi[k-1], n_tg ); if ( lab ) { if ( pzb_rho_fix ) { if ( 0 == qzb_rho_fix ) { qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); if ( qzb_rho_fix > 0 ) { hzb_rho_fix = 1; } } if ( hzb_rho_fix > 1 ) { PartitionCopy( &rho, &pi[hzb_rho_fix-1], n_tg ); /*CtPartInfinity( pzb_rho, qzb, k );*/ } hz_rho = h_rho = hzb_rho_fix; bRhoIsDiscrete = (hzb_rho_fix == k); if ( bRhoIsDiscrete ) { CtPartInfinity( pzb_rho, qzb, k ); pzb_rho_fix_reached = !qzb_rho_fix; CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; } #endif } else { PartitionCopy( &rho, &pi[k-1], n_tg ); hz_rho = h_rho = k; CtPartInfinity( pzb_rho, qzb, k ); } qzb_rho = 0; } r = k; v[k-1] = INFINITY; /* DCh */ CellMakeEmpty( W, k ); /* DCh */ k --; goto L13; L2: /* the two next lines intentionally switched */ /* Create equitable partition in pi[k] */ if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], v[k-1], n, n_tg, n_max, bDigraph, 0 )) ) { goto exit_error; } pCC->lNumBreakTies ++; k ++; CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); e[k-1] = 0; /* moved */ v[k-1] = INFINITY; /* added by DCh. */ CellMakeEmpty( W, k ); /* DCh */ if ( hz_zeta == k-1 && 0 == CtPartCompare( &Lambda, &zf_zeta, NULL, NULL, k-1, 0, bSplitTautCompare ) ) { hz_zeta = k; /* max{k|Lambda(G,pi,nu(k))==Lambda(G,pi,zeta) } */ } /* added */ /* -- old code --- if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); if ( QZFIX_OK(qzb_rho_fix) ) { hzb_rho_fix = k; } else { pCC->lNumRejectedCT ++; } } */ /* --- new code ---*/ if ( pzb_rho_fix && !qzb_rho_fix ) { qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); if ( !qzb_rho_fix && bRhoIsDiscrete ) { qzb_rho_fix = CtPartCompareLayers( kLeast_rho_fix, L_rho_fix_prev, nOneAdditionalLayer ); #if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) if ( qzb_rho_fix ) { int L_rho_fix_diff = abs(qzb_rho_fix)-1; if ( L_rho_fix_diff < L_rho_fix_prev || L_rho_fix_diff == L_rho_fix_prev && kLeast_rho_fix[L_rho_fix_diff].i < I_rho_fix_prev ) { qzb_rho_fix = L_rho_fix_diff+1; /* positive difference will be rejected */ } } #endif #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( qzb_rho_fix ) { int stop = 1; /* debug only */ } #endif } if ( !QZFIX_OK(qzb_rho_fix) ) { pCC->lNumRejectedCT ++; } } if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { hzb_rho_fix = k; } /* if (!lab) goto L3; */ if ( lab && QZFIX_OK(qzb_rho_fix) ) { /* once the difference has been found it is meaningful as long as k increments */ /* cur_qzb2 = CtPartCompare( &Lambda, pzb_rho, qzb, k-1 ); */ /* rho compare */ if ( hz_rho == k-1 && !qzb_rho && bRhoIsDiscrete ) { int qzb_rho_temp = 0; qzb_rho = CtPartCompare( &Lambda, pzb_rho, qzb, kLeast_rho, k-1, 0, bSplitTautCompare ); /* old code */ if ( !qzb_rho && pzb_rho_fix_reached && nOneAdditionalLayer && 0 > kLeast_rho[nOneAdditionalLayer].k ) { qzb_rho_temp = -(nOneAdditionalLayer+1); /* qzb_rho = -(nOneAdditionalLayer+1); *//* early rejection */ } /* new code */ if ( !qzb_rho && bRhoIsDiscrete ) { qzb_rho = CtPartCompareLayers( kLeast_rho, L_rho_fix_prev, 0 ); #if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) if ( qzb_rho ) { int L_rho_diff = abs(qzb_rho)-1; if ( L_rho_diff < L_rho_fix_prev || L_rho_diff == L_rho_fix_prev && kLeast_rho[L_rho_diff].i < I_rho_fix_prev ) { qzb_rho = -(L_rho_diff+1); /* negative difference will be rejected */ } } #endif } /* compare old results to new */ #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( qzb_rho_temp && qzb_rho_temp != qzb_rho ) { int stop = 1; /* */ } #endif if ( !qzb_rho ) { hz_rho = k; } else if ( qzb_rho < 0 ) { pCC->lNumRejectedCT ++; } } if ( qzb_rho > 0 || !qzb_rho && !bRhoIsDiscrete ) { /* found better rho */ if ( !nNumLayers ) { CtPartCopy( pzb_rho, &Lambda, k-1 ); } } } /*L3:*/ /*if ( hz_rho == k || (lab && qzb_rho >= 0 ) )*/ /*if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 ) ) goto L4; else goto L6;*/ if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 && QZFIX_OK(qzb_rho_fix) ) ) { /*L4: check for possible isomorphism or found a better rho */ if ( PartitionIsDiscrete( &pi[k-1], n ) ) { pCC->lNumTotCT ++; goto L7; } PartitionGetFirstCell( &pi[k-1], W, k, n ); v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); if ( !dig && PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) { ; /* found additional isomprphism */ } else { t_Lemma = k+1; } e[k-1] = 0; /* created new cell W[k-1] */ goto L2; } L6: /* a better rho or no good node was found at this level; return to smaller k */ k2 = k; k = inchi_min(t_Lemma-1, inchi_max(t_eq_zeta-1, hz_rho)); if ( k2 == t_Lemma ) goto L13; /* store isomorphism found from Lemma 2.25. should be dig=0 !!! */ if ( dig ) { #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) int stop = 1; #endif goto L13; } l = inchi_min(l+1, L); PartitionGetMcrAndFixSet( &pi[t_Lemma-1], &Omega, &Phi, n_tg, l ); goto L12; L7: /* from L4: pi[k-1] is discrete */ if ( h_zeta == 0 ) { /*goto L18;*/ /* error. the first T(nu) leaf was found */ ret = CT_CANON_ERR; goto exit_error; } if ( k != hz_zeta ) goto L8; /* here zeta^gamma == nu */ /* if ( G^gamma == G ) goto L10; */ if ( 0 == (res=CtFullCompare( &Lambda, &zf_zeta, 0, bSplitTautCompare )) ) { PartitionGetTransposition( &zeta, &pi[k-1], n_tg, &gamma ); bZetaIsomorph = 1; /* for testing only */ lNumEqlZeta ++; goto L10; } else #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) { int stop = 1; } #endif /* !!! we should never come here !!! */ if ( !nNumLayers ) { ret = -2; goto exit_error; } L8: /* here nu is discrete: check rho for being a bettere leaf or isomorphism */ /*if ( !lab || qzb_rho < 0 || !QZFIX_OK(qzb_rho_fix) )*/ if ( !lab || qzb_rho < 0 && ( !pzb_rho_fix || qzb_rho_fix > 0 ) ) goto L6; if ( pzb_rho_fix && kLeast_rho_fix && 0 == qzb_rho_fix ) { /* check for the rejection condition: Lambda > zb_rho_fix */ if ( kLeast_rho_fix ) { int qzb_rho_fix_alt; qzb_rho_fix = CtFullCompareLayers( kLeast_rho_fix ); /* for debug only */ qzb_rho_fix_alt = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); if ( qzb_rho_fix != qzb_rho_fix_alt ) { #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) int stop = 1; #endif qzb_rho_fix = qzb_rho_fix_alt; } /* end debug */ } else { qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); } if ( !pzb_rho_fix_reached ) { pzb_rho_fix_reached = !qzb_rho_fix; } if ( 0 < qzb_rho_fix ) { /* Lambda > pzb_rho_fix, ignore this node */ /* hzb_rho_fix = min( hzb_rho_fix, hz_rho ); */ /* ??? */ qzb_rho_fix = 0; goto L6; } qzb_rho_fix = 0; } if ( qzb_rho < 0 ) goto L6; if ( qzb_rho > 0 || !bRhoIsDiscrete ) goto L9; /* note: p67 says k > PartitionSize( &rho, n ) */ if ( k < r ) { goto L9; /* cannot understand it... */ } /* !!! we should never come here if G(nu) != G(rho): CtPartCompare must be enough !!! */ /* if ( G(nu) > G(rho) ) goto L9; */ if ( kLeast_rho ) { int cur_qzb_alt; qzb_rho = CtFullCompareLayers( kLeast_rho ); /* for debug only */ cur_qzb_alt = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); if ( qzb_rho != cur_qzb_alt ) { #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) int stop = 1; #endif qzb_rho = cur_qzb_alt; } /* end debug */ } else { qzb_rho = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); } /* qzb_rho difference can be due to layers 1..MAX_LAYERS-1 only */ if ( 0 < qzb_rho ) { /* CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); */ qzb_rho = 0; goto L9; } /* if ( G(nu) < G(rho) ) goto L6; */ if ( 0 > qzb_rho ) { qzb_rho = 0; goto L6; } /* nu^gamma == rho */ if ( r != k ) { /* if() is for debug only */ r = k; } PartitionGetTransposition( &pi[k-1], &rho, n_tg, &gamma ); bZetaIsomorph = 0; /* DCh */ pCC->lNumEqualCT ++; goto L10; L9: /* rho := nu; */ PartitionCopy( &rho, &pi[k-1], n_tg ); if ( nNumLayers ) { CtFullCopy( pzb_rho, &Lambda ); } bZetaEqRho = 0; qzb_rho = 0; CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); memset( kLeast_rho, 0, sizeof(kLeast_rho) ); h_rho = hz_rho = k; CtPartInfinity( pzb_rho, qzb, k ); pCC->lNumDecreasedCT ++; pCC->lNumEqualCT = 1; bRhoIsDiscrete = 1; goto L6; L10: /* discrete pi[k-1] && G^gamma == G */ pCC->lNumEqualCT += bZetaEqRho || !(bZetaIsomorph || qzb_rho); l = inchi_min(l+1, L); /* Omega[l] := mcr(gamma); Phi[l] := fix(gamma); */ TranspositionGetMcrAndFixSetAndUnorderedPartition( &gamma, &Omega, &Phi, n_tg, l, &theta_from_gamma ); /* if ( theta(gamma) <= theta ) goto L11; theta := theta v theta(gamma); UnorderedPartitionJoin() returns 0 if theta_from_gamma is finer than theta, which means no changes in theta: theta_from_gamma ^ theta == theta. */ if ( !UnorderedPartitionJoin( &theta_from_gamma, &theta, n_tg ) ) goto L11; /* no new isomorphism found */ /* Output gamma (it is the Aut(G) generator) -- omitted -- */ nNumFoundGenerators ++; /* if ( tvc in mcr(theta) ) goto L11; */ if ( tvc == GetUnorderedPartitionMcrNode( &theta, tvc ) ) goto L11; k = h_zeta; goto L13; L11: k = lab? h_rho : h_zeta; /***Changed*** originally was k = h_rho; */ L12: /* if ( e[k-1] == 1 ) */ #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( e[k-1] == 1 && v[k-1] == INFINITY ) { int stop = 1; /* testing only */ } #endif if ( e[k-1] == 1 && v[k-1] != INFINITY ) { /* INFINITY for testing only */ CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, l ); } L13: if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || ConsoleQuit && (*ConsoleQuit)() ) { ret = CT_USER_QUIT_ERR; goto exit_error; } if ( bInchiTimeIsOver(pCD->ulTimeOutTime) ) { ret = CT_TIMEOUT_ERR; goto exit_error; } if ( k == 0 ) goto exit_function; /* stop */ if ( lab && k < h_rho ) { /***Added***/ h_rho = k; } if ( k > h_zeta ) { if ( v[k-1] == INFINITY ) {/*** Added by DCh for testing only ****/ k --; goto L13; } goto L17; } if ( k == h_zeta ) goto L14; h_zeta = k; tvc = tvh = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); L14: /* if v[k] and tvh are in the same cell of theta then index ++ */ if ( GetUnorderedPartitionMcrNode( &theta, v[k-1] ) == GetUnorderedPartitionMcrNode( &theta, tvh ) ) { index ++; } v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); if ( v[k-1] == INFINITY ) goto L16; if ( v[k-1] != GetUnorderedPartitionMcrNode( &theta, v[k-1] ) ) goto L14; L15: t_Lemma = inchi_min(t_Lemma, k+1); hz_zeta = inchi_min(hz_zeta, k); /* if ( lab && hz_rho >= k ) { hz_rho = k; qzb_rho = 0; } */ if ( lab ) { if ( hz_rho >= k /*-1*/ ) qzb_rho = 0; if ( hz_rho > k ) hz_rho = k; UpdateCompareLayers( kLeast_rho, hz_rho ); } if ( pzb_rho_fix ) { if ( hzb_rho_fix >= k /*-1*/ ) qzb_rho_fix = 0; if ( hzb_rho_fix > k ) hzb_rho_fix = k; UpdateCompareLayers( kLeast_rho_fix, hzb_rho_fix ); } goto L2; L16: if ( t_eq_zeta == k+1 && index == CellGetNumberOfNodes( &pi[k-1], &W[k-1] ) ) t_eq_zeta = k; size *= (double)index; /******************** <<<===== A **************************/ /* passed K times after passing point A. At these passes k = K, K-1, ..., 1 in this order */ index = 0; k --; goto L13; L17: /* if ( e[k-1] == 0 ) */ #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( e[k-1] == 0 && v[k-1] == INFINITY ) { /* testing only */ int stop = 1; /* */ } #endif /* if ( e[k] == 0 set W[k] = Intersection(W[k], Omega[i]) for each i = 1..l, such that {v[1]..v[k-1]} in Phi[i] */ if ( e[k-1] == 0 && v[k-1] != INFINITY ) /* Added v[k-1]!=... DCh */ { NodeSetFromVertices( &cur_nodes, 1, v, k-1 ); for ( i = 1; i <= l; i ++ ) { if ( AllNodesAreInSet( &cur_nodes, 1, &Phi, i ) ) { CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, i ); } } } e[k-1] = 1; v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); if ( v[k-1] != INFINITY ) goto L15; k --; goto L13; /* L18: see above */ exit_function: /* CtPartFill( G, pCD, &rho, pzb_rho, 1, n, n_tg ); */ if ( !bRhoIsDiscrete ) { ret = CT_CANON_ERR; goto exit_error; } if ( pzb_rho_fix ) { qzb_rho_fix = CtFullCompare( pzb_rho_fix, pzb_rho, 1, bSplitTautCompare ); if ( qzb_rho_fix ) { ret = CT_CANON_ERR; goto exit_error; } } /* SymmRank */ memset( nSymmRank, 0, n_tg * sizeof(nSymmRank[0]) ); for ( i = 0; i < n_tg; i ++ ) { k = rho.AtNumber[i]; k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; if ( !nSymmRank[k2] || nSymmRank[k2] > rho.Rank[k] ) { nSymmRank[k2] = rho.Rank[k]; } } for ( i = 0; i < n_tg; i ++ ) { k = rho.AtNumber[i]; k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; nSymmRank[k] = nSymmRank[k2]; } /* CanonRank, nAtomNumberCanon */ memcpy( nCanonRank, rho.Rank, n_tg * sizeof(nCanonRank[0]) ); memcpy( nAtomNumberCanon, rho.AtNumber, n_tg * sizeof(nAtomNumberCanon[0]) ); /* LinearCT */ *nLenCt = pzb_rho->lenCt-1; if ( pCt ) { memcpy( pCt, pzb_rho->Ctbl, *nLenCt*sizeof(pCt[0]) ); } pCC->lNumTotCT = pCC->lNumDecreasedCT + pCC->lNumRejectedCT + pCC->lNumEqualCT; pCC->dGroupSize = size; pCC->lNumGenerators = nNumFoundGenerators; pCC->lNumStoredIsomorphisms = l; /* Note: check nNumFoundGenerators */ if ( pp_zb_rho_out && !*pp_zb_rho_out ) { *pp_zb_rho_out = pzb_rho; pzb_rho = NULL; } exit_error: INCHI_HEAPCHK UnorderedPartitionFree( &theta ); UnorderedPartitionFree( &theta_from_gamma ); if ( W ) inchi_free( W ); if ( v ) inchi_free( v ); if ( e ) inchi_free( e ); if ( qzb ) inchi_free( qzb ); CTableFree( &Lambda ); CTableFree( &zf_zeta ); if ( pzb_rho ) { CTableFree( pzb_rho ); inchi_free( pzb_rho ); pzb_rho = NULL; } /* CTableFree( &zf_zeta2 ); */ NodeSetFree( &Omega ); NodeSetFree( &Phi ); /* NodeSetFree( &mcr_theta, n, 1 ); */ NodeSetFree( &cur_nodes ); PartitionFree( &zeta ); /* PartitionFree( &zeta2 ); */ PartitionFree( &rho ); TranspositionFree( &gamma ); return ret; } /********************************************************************************************** * SetInitialRanks2: Set initial ranks in nRank according to pAtomInvariant[] values * Make sure enough prines have been generated. **********************************************************************************************/ /* Upon exit: */ /* nAtomNumber[i]: number (from 0) of an atom in the ith (from 0) position of the sorted order */ /* nNewRank[i]: initial rank of the atom[i] based on atom invariants; from 1 to num_atoms */ /* Return value: Number of different ranks */ int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ) { int i, nNumDiffRanks; AT_RANK nCurrentRank; for ( i = 0; i < num_atoms; i++ ) nAtomNumber[i] = (AT_RANK)i; /* global for qsort */ pAtomInvariant2ForSort = pAtomInvariant2; qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompAtomInvariants2 ); /* nNewRank[i]: non-decreading order; do not increment nCurrentRank */ /* if consecutive sorted atom invariants are identical */ for ( i=num_atoms-1, nCurrentRank=nNewRank[nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; 0 < i ; i -- ) { /* Note: CompAtomInvariants2Only() in following line implicitly reads pAtomInvariant2 pointed by pAtomInvariant2ForSort */ if ( CompAtomInvariants2Only( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { nNumDiffRanks ++; nCurrentRank = (AT_RANK)i; } nNewRank[nAtomNumber[i - 1]] = nCurrentRank; } return nNumDiffRanks; } /****************************************************************************/ void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ) { int i, k, j, i_t_group; /* tautomers */ T_GROUP *t_group=NULL; int num_t_groups = 0; int num_tautomer_iso = 0; #define ELEM_NAME_LEN 2 char ChemElements[ELEM_NAME_LEN*NUM_CHEM_ELEMENTS+ELEM_NAME_LEN]; char CurElement[ELEM_NAME_LEN + ELEM_NAME_LEN], *pCurElem; int nNumChemElements = 0; int nNumHydrogenAtoms = 0; int nNumCarbonAtoms = 0; memset( ChemElements, 0, sizeof(ChemElements) ); memset( CurElement, 0, sizeof(CurElement) ); nNumChemElements = 0; if ( num_at_tg > num_atoms && t_group_info ) { t_group = t_group_info->t_group; num_t_groups = t_group_info->num_t_groups; num_tautomer_iso = t_group_info->bIgnoreIsotopic? 0 : T_NUM_ISOTOPIC; } if ( !bTautGroupsOnly ) { for ( i = 0; i < num_atoms; i ++ ) { if ( !strcmp( at[i].elname, "C" ) ) { nNumCarbonAtoms ++; } else if ( !strcmp( at[i].elname, "H" ) || !strcmp( at[i].elname, "D" ) || !strcmp( at[i].elname, "T" ) ) { nNumHydrogenAtoms ++; } else { CurElement[0] = at[i].elname[0]; CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; if ( ! (pCurElem = strstr( ChemElements, CurElement ) ) ) { strcat( ChemElements, CurElement ); nNumChemElements ++; } } } if ( nNumChemElements > 1 ) { qsort( ChemElements, nNumChemElements, ELEM_NAME_LEN, CompChemElemLex ); } if ( nNumCarbonAtoms ) { if ( nNumChemElements ) { memmove( ChemElements + ELEM_NAME_LEN, ChemElements, ELEM_NAME_LEN*nNumChemElements ); } ChemElements[0] = 'C'; ChemElements[1] = ' '; nNumChemElements ++; } if ( nNumHydrogenAtoms ) { ChemElements[ ELEM_NAME_LEN*nNumChemElements ] = 'H'; ChemElements[ ELEM_NAME_LEN*nNumChemElements+1 ] = ' '; nNumChemElements ++; } /* general */ for ( i = 0; i < num_atoms; i ++ ) { memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); CurElement[0] = at[i].elname[0]; CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; pCurElem = strstr( ChemElements, CurElement ); if ( pCurElem ) { j = (pCurElem - ChemElements)/ELEM_NAME_LEN + 1; } else { j = nNumChemElements; /* must be D or T */ } /* at[i].hill_type = (U_CHAR) j; */ pAtomInvariant[i].val[AT_INV_HILL_ORDER] = j; pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = at[i].valence; if ( bHydrogensInRanks ) { pAtomInvariant[i].val[AT_INV_NUM_H] = ((t_group && at[i].endpoint>0)? 0 : at[i].num_H); } if ( bHydrogensFixedInRanks ) { pAtomInvariant[i].val[AT_INV_NUM_H_FIX] = ((t_group && at[i].endpoint>0)? at[i].num_H : 0); } if ( !bDigraph && t_group && (i_t_group = (int)at[i].endpoint-1) >= 0 && i_t_group < num_t_groups ) { pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[i_t_group].nNumEndpoints; for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { pAtomInvariant[i].val[AT_INV_TG_NUMBERS+j] = t_group[i_t_group].num[j]; } for ( j = 0; j < num_tautomer_iso; j ++ ) { pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[i_t_group].num[j + T_NUM_NO_ISOTOPIC]; } } pAtomInvariant[i].iso_sort_key = bIgnoreIsotopic? 0 : at[i].iso_sort_key; } } else { /* fill tautomeric groups only */ memset ( pAtomInvariant, 0, num_at_tg*sizeof(pAtomInvariant[0]) ); } /**************************************/ /* tautomeric groups */ /**************************************/ for ( i = num_atoms; i < num_at_tg; i ++ ) { k = i - num_atoms; memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); if ( !t_group ) continue; /* make sure ranks of t-groups are larger than that of any atom */ /* greater than for any real atom */ pAtomInvariant[i].val[AT_INV_HILL_ORDER] = bTautGroupsOnly? num_at_tg : nNumChemElements+1; /* greater than for any real atom */ pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = MAXVAL+1; if ( k < num_t_groups ) { pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[k].nNumEndpoints; for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j]; } for ( j = 0; j < num_tautomer_iso; j ++ ) { pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j + T_NUM_NO_ISOTOPIC]; } } } } /*****************************************************************************/ void CleanNumH( NUM_H *NumH, int len ) { int i; if ( NumH ) { for ( i = 0; i < len; i ++ ) { if ( NumH[i] == EMPTY_H_NUMBER ) { NumH[i] = 0; } else { NumH[i] -= BASE_H_NUMBER; } } } } /*****************************************************************************/ int CleanCt( AT_RANK *Ct, int len ) { if ( Ct && Ct[len] == EMPTY_CT ) { Ct[len] = 0; return 1; } return 0; } /*****************************************************************************/ void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ) { int i; if ( isk ) { for ( i = 0; i < len; i ++ ) { if ( isk[i] == EMPTY_ISO_SORT_KEY ) { isk[i] = 0; } } } } /*****************************************************************************/ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ) { int i; AT_ISO_SORT_KEY k1, k2; if ( isk1 && isk2 ) { for ( i = 0; i < len; i ++ ) { k1 = (isk1[i] == EMPTY_ISO_SORT_KEY)? 0 : isk1[i]; k2 = (isk2[i] == EMPTY_ISO_SORT_KEY)? 0 : isk2[i]; isk1[i] = k1 | k2; } } else if ( isk1 ) { CleanIsoSortKeys( isk1, len ); } } #endif #define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} #define FREE_ARRAY(X) if (X) inchi_free(X); /*****************************************************************************/ void DeAllocBCN( BCN *pBCN ) { int i, k; FTCN *ftcn; if ( !pBCN ) return; if ( pBCN->pRankStack ) { for ( i = 0; i < pBCN->nMaxLenRankStack; i ++ ) { FREE_ARRAY( pBCN->pRankStack[i] ) } FREE_ARRAY( pBCN->pRankStack ) } for ( k = 0; k < TAUT_NUM; k ++ ) { ftcn = pBCN->ftcn + k; FreeNeighList( ftcn->NeighList ); FREE_ARRAY( ftcn->LinearCt ) PartitionFree( &ftcn->PartitionCt ); FREE_ARRAY( ftcn->nSymmRankCt ) FREE_ARRAY( ftcn->nNumHOrig ) FREE_ARRAY( ftcn->nNumH ) FREE_ARRAY( ftcn->nNumHOrigFixH ) FREE_ARRAY( ftcn->nNumHFixH ) PartitionFree( &ftcn->PartitionCtIso ); FREE_ARRAY( ftcn->nSymmRankCtIso ) FREE_ARRAY( ftcn->iso_sort_keys ) FREE_ARRAY( ftcn->iso_sort_keysOrig ) FREE_ARRAY( ftcn->iso_exchg_atnos ) FREE_ARRAY( ftcn->iso_exchg_atnosOrig ) } } #undef FREE_CONTABLE #undef FREE_ARRAY /*****************************************************************************/ #if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) /* debug: find whether canonical equivalence is different from equitable partition */ int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ) { AT_RANK *nRank = NULL; AT_RANK *nAtomNumber = NULL; AT_RANK *nTempRank = NULL; AT_RANK nCurSymm, nCurRank; ATOM_INVARIANT2 *pAtomInvariant = NULL; NEIGH_LIST *NeighList = NULL; int nNumCurrRanks, i, is, ir, j; long lCount; int bIsNotSame = 0; if ( at && nSymmRank ) { if ( !(nRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nRank[0]))) || !(nAtomNumber = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nAtomNumber[0]))) || !(nTempRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nTempRank[0]))) || !(pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_atoms, sizeof(pAtomInvariant[0]))) ) { goto exit_err; } if ( !(NeighList = CreateNeighList( num_atoms, num_atoms, at, 0, NULL )) ) { goto exit_err; } FillOutAtomInvariant2( at, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, 1 /*bHydrogensInRanks*/, 1 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); lCount = 0; /* make equitable partition in pBCN->pRankStack[0,1] */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList, nNumCurrRanks, nRank, nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); /* at this point the equitable partition is in nRank; the order of atoms is in nAtomNumber*/ /* compare */ nCurSymm = nCurRank = 0; for ( i = 0; i < num_atoms; i ++ ) { j = (int)nAtomNumber[i]; if ( nCurSymm != nSymmRank[j] ) { nCurSymm = nSymmRank[j]; is = i; } if ( nCurRank != nRank[j] ) { nCurRank = nRank[j]; ir = i; } if ( is != ir ) { bIsNotSame = 1; break; } } } exit_err: if ( nRank ) inchi_free( nRank ); if ( nAtomNumber ) inchi_free( nAtomNumber ); if ( nTempRank ) inchi_free( nTempRank ); if ( pAtomInvariant ) inchi_free( pAtomInvariant ); if ( NeighList ) FreeNeighList( NeighList ); return bIsNotSame; } #endif /*****************************************************************************/ int GetBaseCanonRanking( int num_atoms, int num_at_tg, sp_ATOM* at[], T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ) { int ret = 0; int iBase; /* base structure index, always valid; = TAUT_YES except special fully non-taut mode */ int iOther; /* other than basic structure index, usually non-taut; may be = iBase */ int bReqNonTaut; /* 1 => requested non-tautomeric results */ int bReqTaut; /* 1 => requested tautomeric results and the base structure is tautomeric */ int bChanged; sp_ATOM *at_base = NULL; sp_ATOM *at_other = NULL; int bTautIgnoreIsotopic = 0; /*int bIgnoreIsotopic = 0;*/ int nNumCurrRanks = 0; int nMaxLenRankStack = 0; int num_max = num_at_tg; long lCount; /* local allocations */ ATOM_INVARIANT2 *pAtomInvariant = NULL; NEIGH_LIST *NeighList[TAUT_NUM]; ConTable *Ct_Temp = NULL; /* initial partition for canonicalization */ AT_RANK *nRank = NULL; AT_NUMB *nAtomNumber = NULL; /* canonicalization output */ ConTable *Ct_NoH = NULL; AT_RANK *nCanonRankNoH = NULL; AT_NUMB *nAtomNumberCanonNoH = NULL; AT_RANK *nSymmRankNoH = NULL; ConTable *Ct_NoTautH = NULL; AT_RANK *nSymmRankNoTautH = NULL; AT_RANK *nCanonRankNoTautH = NULL; AT_NUMB *nAtomNumberCanonNoTautH = NULL; NUM_H *numHNoTautH = NULL; int lenNumHNoTautH; int maxlenNumHNoTautH; ConTable *Ct_Base = NULL; AT_RANK *nSymmRankBase = NULL; AT_RANK *nCanonRankBase = NULL; AT_NUMB *nAtomNumberCanonBase = NULL; NUM_H *numH = NULL; int lenNumH; int maxlenNumH = 0; #if ( USE_AUX_RANKING == 1 ) AT_RANK *nRankAux = NULL; AT_NUMB *nAtomNumberAux = NULL; ATOM_INVARIANT2 *pAtomInvariantAux= NULL; #endif ConTable *Ct_FixH = NULL; AT_RANK *nSymmRankFixH = NULL; AT_RANK *nCanonRankFixH = NULL; AT_NUMB *nAtomNumberCanonFixH = NULL; NUM_H *NumHfixed = NULL; int maxlenNumHfixed; /* isotopic canonicalization */ ConTable *Ct_NoTautHIso = NULL; AT_RANK *nSymmRankNoTautHIso = NULL; AT_RANK *nCanonRankNoTautHIso = NULL; AT_NUMB *nAtomNumberCanonNoTautHIso = NULL; AT_ISO_SORT_KEY *iso_sort_key_NoTautH = NULL; int maxlen_iso_sort_key_NoTautH; int len_iso_sort_key_NoTautH; int num_iso_NoTautH, num_iso_NoAuxBase; ConTable *Ct_BaseIso = NULL; AT_RANK *nSymmRankBaseIso = NULL; AT_RANK *nCanonRankBaseIso = NULL; AT_NUMB *nAtomNumberCanonBaseIso = NULL; AT_ISO_SORT_KEY *iso_sort_keyBase = NULL; int maxlen_iso_sort_keyBase; int len_iso_sort_keyBase; int bUseIsoAuxBase[TAUT_NUM]; S_CHAR *iso_exchg_atnos = NULL; int len_iso_exchg_atnos; int maxlen_iso_exchg_atnos; int num_iso_Base; AT_ISO_SORT_KEY iso_sort_key; ConTable *Ct_FixHIso = NULL; AT_RANK *nSymmRankFixHIso = NULL; AT_RANK *nCanonRankFixHIso = NULL; AT_NUMB *nAtomNumberCanonFixHIso = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) AT_ISO_SORT_KEY iso_sort_key2; AT_ISO_SORT_KEY *iso_sort_key_Hfixed = NULL; int maxlen_iso_sort_key_Hfixed; int len_iso_sort_key_Hfixed; int num_iso_Hfixed; #endif AT_RANK *nTempRank = NULL; CANON_DATA pCD[3]; /* = &CanonData; */ CANON_COUNTS CanonCounts; CANON_COUNTS *pCC = &CanonCounts; int i, j, k, m; int nCanonFlags[2]; /*^^^ */ int iflag; memset (pCD, 0, sizeof(pCD)); memset (pCC, 0, sizeof(pCC[0])); memset ( bUseIsoAuxBase, 0, sizeof(bUseIsoAuxBase) ); memset ( nCanonFlags, 0, sizeof(nCanonFlags) ); NeighList[TAUT_NON] = NULL; NeighList[TAUT_YES] = NULL; /* select base structure, find whether it is tautomeric or not */ if ( at[TAUT_YES] && s[TAUT_YES].nLenCT && t_group_info && (s[TAUT_YES].nLenLinearCTTautomer > 0 && /* ordinary tautomerism */ t_group_info->t_group && t_group_info->num_t_groups > 0 || /* protons have been moved */ (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || /* tautomerism due to possible isotopic proton exchange */ t_group_info->nNumIsotopicEndpoints > 1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) ) ) { /* tautomeric: (1) has tautomeric atoms OR (2) H-atoms have been rearranged due to proton addition/removal OR (3) Found isotopic H-atoms on tautomeric or hetero atoms */ iBase = TAUT_YES; bReqTaut = 1; bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1) && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { iOther = TAUT_NON; /* tautomeric and non-tautomeric */ bReqNonTaut = 1; } else { iOther = iBase; /* tautomeric only */ bReqNonTaut = 0; } } else if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { /* force pure non-tautomeric processing; happens for testing only */ iBase = TAUT_NON; bReqTaut = 0; iOther = iBase; bReqNonTaut = 1; num_at_tg = num_atoms; } else if ( at[TAUT_YES] && s[TAUT_YES].nLenCT ) { /* although the user requested tautomeric processing, tautomerism has not been found */ /* however, the results should be saved in the TAUT_YES elements of the arrays */ iBase = TAUT_YES; bReqTaut = 0; bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1); iOther = iBase; bReqNonTaut = 1; num_at_tg = num_atoms; } else { ret = CT_UNKNOWN_ERR; goto exit_error; } if ( bReqTaut ) { /* save "process isotopic" mark; temporarily set it to NO */ bTautIgnoreIsotopic = t_group_info->bIgnoreIsotopic; t_group_info->bIgnoreIsotopic = 1; } lenNumH = num_atoms; /* isotopic canonicalization */ num_iso_NoTautH = 0; len_iso_sort_key_NoTautH = 0; maxlen_iso_sort_key_NoTautH = 0; num_iso_Base = 0; len_iso_sort_keyBase = 0; maxlen_iso_sort_keyBase = 0; len_iso_exchg_atnos = 0; maxlen_iso_exchg_atnos = 0; len_iso_exchg_atnos = 0; maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) num_iso_Hfixed = len_iso_sort_key_Hfixed = maxlen_iso_sort_key_Hfixed = 0; #endif /* prepare initial data */ at_base = at[iBase]; at_other = at[iOther]; pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_max, sizeof(pAtomInvariant[0]) ); nSymmRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoH[0] ) ); nCanonRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoH[0] ) ); nAtomNumberCanonNoH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoH[0]) ); nRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nRank[0] ) ); nAtomNumber = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumber[0]) ); nTempRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nTempRank[0] ) ); if ( !pAtomInvariant || !nSymmRankNoH || !nCanonRankNoH || !nAtomNumberCanonNoH || !nRank || !nAtomNumber || !nTempRank ) { goto exit_error_alloc; } #if ( USE_AUX_RANKING == 1 ) nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); if ( !nRankAux || !nAtomNumberAux || !pAtomInvariantAux ) { goto exit_error_alloc; } #endif if ( bReqTaut ) { if ( !(NeighList[TAUT_YES] = CreateNeighList( num_atoms, num_at_tg, at_base, 0, t_group_info )) ) goto exit_error_alloc; /* needed for the hydrogenless structure */ if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) goto exit_error_alloc; } else { if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) goto exit_error_alloc; NeighList[TAUT_YES] = NULL; INCHI_HEAPCHK } /* avoid memory leaks in case of error */ /* pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; */ pBCN->nMaxLenRankStack = 0; pBCN->num_max = num_max; /* allocated nRank[] arrays lengths in pRankStack */ pBCN->num_at_tg = num_at_tg; /* all of the following arrays have this length */ pBCN->num_atoms = num_atoms; pBCN->ulTimeOutTime = ulTimeOutTime; /* initial partitioning of a hydrogenless skeleton: fill out the inveriant */ FillOutAtomInvariant2( at_base, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); lCount = 0; /* make equitable partition in pBCN->pRankStack[0,1] */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], nNumCurrRanks, nRank, nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); /* allocate partition stack */ nMaxLenRankStack = 2*(num_at_tg-nNumCurrRanks) + 8; /* was 2*(...) + 6 */ pBCN->pRankStack = (AT_RANK **) inchi_calloc( nMaxLenRankStack, sizeof(pBCN->pRankStack[0]) ); if ( !pBCN->pRankStack ) { pBCN->nMaxLenRankStack = 0; /* avoid memory leaks in case of error */ goto exit_error_alloc; } pBCN->nMaxLenRankStack = nMaxLenRankStack; /* avoid memory leaks in case of error */ /* init partition stack */ pBCN->pRankStack[0] = nRank; pBCN->pRankStack[1] = nAtomNumber; /********************************************************************************************/ /* get NoH/no taut groups canonical numbering, connection table, and equivalence partition */ /********************************************************************************************/ /* pointers */ pCD[iOther].LinearCT = NULL; pCD[iOther].NumH = NULL; pCD[iOther].NumHfixed = NULL; pCD[iOther].iso_sort_key = NULL; pCD[iOther].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].iso_sort_key_Hfixed = NULL; #endif /* variables - unchanged */ pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; /* return values & input/output */ pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; pCD[iOther].lenNumH = 0; pCD[iOther].lenNumHfixed = 0; pCD[iOther].len_iso_sort_key = 0; pCD[iOther].maxlen_iso_sort_key = 0; pCD[iOther].len_iso_exchg_atnos = 0; pCD[iOther].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].len_iso_sort_key_Hfixed = 0; pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; #endif ret = CanonGraph01( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); if ( ret < 0 ) { goto exit_error; } /* update initial partitioning */ nNumCurrRanks = FixCanonEquivalenceInfo( num_atoms, nSymmRankNoH, nRank, nTempRank, nAtomNumber, &bChanged ); /* repartition if necessary */ if ( bChanged & 3 ) { if ( Ct_NoH ) { CTableFree( Ct_NoH ); inchi_free( Ct_NoH ); Ct_NoH = NULL; } pCD[iOther].nCanonFlags |= CANON_FLAG_NO_H_RECANON; ret = CanonGraph02( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); if ( ret < 0 ) { goto exit_error; } } /********************************************************************************/ /* get NoTautH canonical numbering, connection table, and equivalence partition */ /********************************************************************************/ maxlenNumHNoTautH = num_atoms + 1; nSymmRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautH[0] ) ); nCanonRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautH[0] ) ); nAtomNumberCanonNoTautH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautH[0]) ); numHNoTautH = (NUM_H *) inchi_calloc( maxlenNumHNoTautH, sizeof(numHNoTautH[0]) ); if ( !numHNoTautH || !nSymmRankNoTautH || !nCanonRankNoTautH || !nAtomNumberCanonNoTautH ) { goto exit_error_alloc; } /* find number of H atoms attached to not-a-tautomeric-endpoint atoms */ for ( i = 0; i < num_atoms; i ++ ) { numHNoTautH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; } /* pointers */ pCD[iOther].LinearCT = NULL; pCD[iOther].NumH = numHNoTautH; pCD[iOther].NumHfixed = NULL; pCD[iOther].iso_sort_key = NULL; pCD[iOther].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].iso_sort_key_Hfixed = NULL; #endif /* variables - unchanged */ pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; pCD[iOther].maxlenNumH = maxlenNumHNoTautH; /* return values & input/output */ pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; pCD[iOther].lenNumHfixed = 0; pCD[iOther].len_iso_sort_key = 0; pCD[iOther].maxlen_iso_sort_key = 0; pCD[iOther].len_iso_exchg_atnos = 0; pCD[iOther].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].len_iso_sort_key_Hfixed = 0; pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; #endif pCD[iOther].nAuxRank = NULL; /* check whether we need NoTautH cononicalization */ memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); for ( i = 0; i < num_atoms; i ++ ) { if ( nTempRank[nSymmRankNoH[i]-1] < i ) { nTempRank[nSymmRankNoH[i]-1] = i; /* greatest class representative */ } } for ( i = 0; i < num_atoms; i ++ ) { if ( numHNoTautH[i] != numHNoTautH[nTempRank[nSymmRankNoH[i]-1]] ) { pCD[iOther].nCanonFlags |= CANON_FLAG_NO_TAUT_H_DIFF; break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ } } /* i = 0; *//* debug: force to call the canonicalization */ if ( i < num_atoms ) { /* needs canonicalization */ /* get aux canonical ranking of the structure with attached H */ #if ( USE_AUX_RANKING == 1 ) /* refine no-H partition according to not-a-taut-H distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankNoH[i]; pAtomInvariantAux[i].val[1] = numHNoTautH[i]; /* additional differentiation: not-a-taut-H distribution */ } /* initial partitioning */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable partition */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], nNumCurrRanks, nRankAux, nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iOther].nAuxRank = nRankAux; #endif ret = CanonGraph03( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankNoTautH, nCanonRankNoTautH, nAtomNumberCanonNoTautH, pCD+iOther, pCC, &Ct_NoH, &Ct_NoTautH ); if ( ret < 0 ) { goto exit_error; } /* in case of non-tautomeric structure the final results are in: nSymmRankNoTautH nCanonRankNoTautH nAtomNumberCanonNoTautH Ct_NoTautH numHNoTautH (original H positions) */ } else { /* copy the results of the previous (no H) canonicalization */ /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { CtFullCopy( Ct_Temp, Ct_NoH ); /* since Ct_NoH does not have Ct_NoH->NumH we have to fill out Ct_Temp->NumH separately */ for ( i = 0; i < num_atoms; i ++ ) { Ct_Temp->NumH[nCanonRankNoH[i]-1] = numHNoTautH[i]; /*Ct_Temp->NumH[i] = numHNoTautH[nAtomNumberCanonNoH[i]]; -- alternative */ } Ct_Temp->lenNumH = num_atoms; } else { goto exit_error_alloc; } Ct_NoTautH = Ct_Temp; Ct_Temp = NULL; memcpy( nSymmRankNoTautH, nSymmRankNoH, num_atoms*sizeof(nSymmRankNoTautH[0]) ); memcpy( nCanonRankNoTautH, nCanonRankNoH, num_atoms*sizeof(nCanonRankNoTautH[0]) ); memcpy( nAtomNumberCanonNoTautH, nAtomNumberCanonNoH, num_atoms*sizeof(nAtomNumberCanonNoTautH[0]) ); } /* in case of non-tautomeric component this is the final result */ /* i = CtFullCompare( Ct_NoTautH, Ct_Temp, num_atoms, 0, 0 );*/ /*******************************************************************************************/ /* If only Isotopic atoms and isotopic H, tautomerism has not been found: */ /* get isotopic canonical numbering, connection table, and equivalence partition */ /*******************************************************************************************/ if ( s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic && !bReqTaut && bReqNonTaut ) { maxlen_iso_sort_key_NoTautH = num_atoms+1; nSymmRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautHIso[0] ) ); nCanonRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautHIso[0] ) ); nAtomNumberCanonNoTautHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautHIso[0]) ); iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); if ( !nSymmRankNoTautHIso || !nCanonRankNoTautHIso || !nAtomNumberCanonNoTautHIso || !iso_sort_key_NoTautH ) { goto exit_error_alloc; } /* fill out isotopic non-tautomeric keys */ num_iso_NoTautH = 0; for ( i = 0; i < num_atoms; i ++ ) { if ( at_base[i].endpoint ) { /* should not happen */ iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); } else { iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); } if ( iso_sort_key ) { iso_sort_key_NoTautH[i] = iso_sort_key; num_iso_NoTautH ++; } else { iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; } } /* pointers */ pCD[iOther].LinearCT = NULL; /* LinearCT; */ pCD[iOther].NumH = numHNoTautH; pCD[iOther].NumHfixed = NULL; pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; pCD[iOther].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].iso_sort_key_Hfixed = NULL; #endif /* variables - unchanged */ pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; pCD[iOther].maxlenNumH = maxlenNumHNoTautH; /* return values & input/output */ pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; pCD[iOther].lenNumH = lenNumHNoTautH /*= num_atoms*/; pCD[iOther].lenNumHfixed = 0; pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; pCD[iOther].len_iso_exchg_atnos = 0; pCD[iOther].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].len_iso_sort_key_Hfixed = 0; pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; #endif pCD[iOther].nAuxRank = NULL; if ( num_iso_NoTautH ) { /* check whether we need NoTautH cononicalization */ memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); for ( i = 0; i < num_atoms; i ++ ) { if ( nTempRank[nSymmRankNoTautH[i]-1] < i ) { nTempRank[nSymmRankNoTautH[i]-1] = i; /* greatest class representative */ } } for ( i = 0; i < num_atoms; i ++ ) { if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankNoTautH[i]-1]] ) { pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF; break; /* atoms so far found to be equivalent differ in isotopes; the canonicalization is needed */ } } } else { i = num_atoms; } /* i = 0; *//* debug: force to call the canonicalization */ if ( i < num_atoms ) { /* we need canonicalization */ /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ #if ( USE_AUX_RANKING == 1 ) /* refine no-taut-H partition according to non-taut H isotopic distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; pAtomInvariantAux[i].iso_sort_key = iso_sort_key_NoTautH[i]; /* additional differentiation */ } /* initial ranks for non-taut H isotopic distribution */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], nNumCurrRanks, nRankAux, nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iOther].nAuxRank = nRankAux; #endif ret = CanonGraph04( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankNoTautHIso, nCanonRankNoTautHIso, nAtomNumberCanonNoTautHIso, pCD+iOther, pCC, &Ct_NoTautH, &Ct_NoTautHIso ); if ( ret < 0 ) { goto exit_error; } /* in case of non-tautomeric structure the final results are in: nSymmRankNoTautHIso nCanonRankNoTautHIso nAtomNumberCanonNoTautHIso Ct_NoTautHIso iso_sort_key_NoTautH (original isotopic atom positions) */ } else { /* copy the results of the previous (no taut H) canonicalization */ /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { CtFullCopy( Ct_Temp, Ct_NoTautH ); /* since Ct_NoTautH does not have Ct_NoTautH->iso_sort_key we have to fill out Ct_Temp->iso_sort_key separately */ for ( i = 0; i < num_atoms; i ++ ) { Ct_Temp->iso_sort_key[nCanonRankNoTautH[i]-1] = iso_sort_key_NoTautH[i]; } Ct_Temp->len_iso_sort_key = num_atoms; } else { goto exit_error_alloc; } Ct_NoTautHIso = Ct_Temp; Ct_Temp = NULL; memcpy( nSymmRankNoTautHIso, nSymmRankNoTautH, num_atoms*sizeof(nSymmRankNoTautHIso[0]) ); memcpy( nCanonRankNoTautHIso, nCanonRankNoTautH, num_atoms*sizeof(nCanonRankNoTautHIso[0]) ); memcpy( nAtomNumberCanonNoTautHIso, nAtomNumberCanonNoTautH, num_atoms*sizeof(nAtomNumberCanonNoTautHIso[0]) ); } /* in case of non-tautomeric component this is the final result */ /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ } if ( bReqTaut ) { /*****************************************************************************/ /* Tautomeric Structure Canonicalizaton: */ /* get base canonical numbering, connection table, and equivalence partition */ /*****************************************************************************/ /* find H atoms attached to non-tautomeric-endpoints and to tautomeric endpoints */ maxlenNumH = num_atoms + T_NUM_NO_ISOTOPIC*(num_at_tg-num_atoms) + 1; /* including negative charges */ nSymmRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBase[0] ) ); nCanonRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBase[0] ) ); nAtomNumberCanonBase = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBase[0]) ); numH = (NUM_H *) inchi_calloc( maxlenNumH, sizeof(numH[0]) ); if ( !numH || !nSymmRankBase || !nCanonRankBase || !nAtomNumberCanonBase ) { goto exit_error_alloc; } /* non-tautomeric H counts */ for ( i = 0; i < num_atoms; i ++ ) { numH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; } /* tautomeric H and negative charge counts */ for ( i = k = num_atoms; i < num_at_tg; i ++ ) { m = i-num_atoms; for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { /* non-zeroes for j=1 are negative charge counts; T_NUM_NO_ISOTOPIC=2 entry per t-group */ numH[k ++] = t_group_info->t_group[m].num[j]? t_group_info->t_group[m].num[j]+BASE_H_NUMBER : EMPTY_H_NUMBER; } } /* pointers */ pCD[iBase].LinearCT = NULL; pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ pCD[iBase].NumHfixed = NULL; pCD[iBase].iso_sort_key = NULL; pCD[iBase].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iBase].iso_sort_key_Hfixed = NULL; #endif /* variables - unchanged */ pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; pCD[iBase].maxlenNumH = maxlenNumH; /* return values & input/output */ pCD[iBase].nLenLinearCT = s[iBase].nLenCT; pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; pCD[iBase].lenNumH = lenNumH = k; pCD[iBase].lenNumHfixed = 0; pCD[iBase].len_iso_sort_key = 0; pCD[iBase].maxlen_iso_sort_key = 0; pCD[iBase].len_iso_exchg_atnos = 0; pCD[iBase].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iBase].len_iso_sort_key_Hfixed = 0; pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; #endif pCD[iBase].nAuxRank = NULL; /* make sure the initial partition is equitable (at this point t-groups do not have ranks yet) */ FillOutAtomInvariant2( at_base, num_atoms, num_at_tg, pAtomInvariant, 1 /*bIgnoreIsotopic*/, 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 1 /*bTaut=bDigraph*/, 1 /* bTautGroupsOnly */, t_group_info ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariant[i].val[0] = pBCN->pRankStack[0][i]; } /* initial ranks for t-group(s) only */ nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariant, nRank, nAtomNumber ); /* make equitable, call digraph procedure; pBCN->pRankStack[0] is nRank, pBCN->pRankStack[1] is nAtomNumber This should only split ranks of tautomeric groups */ nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], nNumCurrRanks, pBCN->pRankStack[0], nTempRank /* temp array */, pBCN->pRankStack[1], (AT_RANK)num_atoms, &lCount ); #if ( USE_AUX_RANKING == 1 ) /* refine no-H partition according to non-taut H distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; pAtomInvariantAux[i].val[1] = numH[i]; /* additional differentiation */ } for ( j = i; i < num_at_tg; i ++ ) { pAtomInvariantAux[i].val[0] = nRank[i]; } /* initial ranks for t-group(s) */ nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable, call digraph procedure */ nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], nNumCurrRanks, nRankAux, nTempRank /* temp array */, nAtomNumberAux, (AT_RANK)num_atoms, &lCount ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iBase].nAuxRank = nRankAux; #endif ret = CanonGraph05( num_atoms, num_at_tg, num_max, 1 /* digraph*/, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, nSymmRankBase, nCanonRankBase, nAtomNumberCanonBase, pCD+iBase, pCC, &Ct_NoTautH, &Ct_Base ); if ( ret < 0 ) { goto exit_error; } /* tautomeric isotopic structure */ /**************************************************************************************/ /* Isotopic atoms and isotopic H atoms and isotopic tautomeric groups */ /* get isotopic canonical numbering, connection table, and equivalence partition */ /**************************************************************************************/ if ( s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic || bUseIsoAuxBase[iBase] && !bTautIgnoreIsotopic ) { t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; nSymmRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBaseIso[0] ) ); nCanonRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBaseIso[0] ) ); nAtomNumberCanonBaseIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBaseIso[0]) ); if ( bUseIsoAuxBase[iBase] ) { maxlen_iso_exchg_atnos = num_max+1; iso_exchg_atnos = (S_CHAR *) inchi_calloc( maxlen_iso_exchg_atnos, sizeof(iso_exchg_atnos[0]) ); } maxlen_iso_sort_keyBase = num_max+1; /* num_at_tg+1;*/ iso_sort_keyBase = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_keyBase, sizeof(iso_sort_keyBase[0]) ); if ( !nSymmRankBaseIso || !nCanonRankBaseIso || !nAtomNumberCanonBaseIso || !iso_sort_keyBase || maxlen_iso_exchg_atnos && !iso_exchg_atnos ) { goto exit_error_alloc; } /* atoms */ num_iso_NoTautH = 0; num_iso_NoAuxBase = 0; if ( iso_exchg_atnos ) { len_iso_exchg_atnos = num_at_tg; } for ( i = 0; i < num_atoms; i ++ ) { if ( at_base[i].endpoint || iso_exchg_atnos && (at_base[i].cFlags & AT_FLAG_ISO_H_POINT) ) { /* tautomeric or may have exchangeable isotopic H */ iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); if ( iso_exchg_atnos ) { num_iso_NoAuxBase += !at_base[i].endpoint; /* these non-taut atom may exchange isotopic H as tautomeric atoms do */ } } else { /* non-mobile H */ iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); if ( iso_exchg_atnos ) { iso_exchg_atnos[i] = 1; /* atom cannot have exchangable isotopic H atom(s) */ } } if ( iso_sort_key ) { num_iso_NoTautH ++; iso_sort_keyBase[i] = iso_sort_key; } else { iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; } } /* check marking and count of non-taut atoms that may exchange isotopic H -- debug only */ if ( iso_exchg_atnos ) { if ( num_iso_NoAuxBase != t_group_info->nIsotopicEndpointAtomNumber[0] ) { ret = CT_ISOCOUNT_ERR; goto exit_error; } for ( i = 1; i <= num_iso_NoAuxBase; i ++ ) { j = t_group_info->nIsotopicEndpointAtomNumber[i]; if ( at_base[j].endpoint || !(at_base[j].cFlags & AT_FLAG_ISO_H_POINT) ) { ret = CT_ISOCOUNT_ERR; goto exit_error; } } } /* t-groups */ num_iso_Base = 0; if ( iso_exchg_atnos ) { for ( i = num_atoms; i < num_at_tg; i ++ ) { iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; /* new mode: do not provide info about isotopic tautomeric H */ } } else { for ( i = num_atoms; i < num_at_tg; i ++ ) { /* should not happen anymore */ m = i-num_atoms; if ( iso_sort_key = t_group_info->t_group[m].iWeight ) { /* old approach: each t-group has its own isotopic "weight" */ num_iso_Base ++; iso_sort_keyBase[i] = iso_sort_key; } else { iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; } } } if ( !num_iso_NoAuxBase && iso_exchg_atnos ) { /* all atoms that may exchange isotopic H are either tautomeric or not present */ inchi_free( iso_exchg_atnos ); iso_exchg_atnos = NULL; len_iso_exchg_atnos = 0; maxlen_iso_exchg_atnos = 0; } if ( !num_iso_NoTautH && !num_iso_Base && iso_sort_keyBase ) { /* no isotopic atoms present */ inchi_free( iso_sort_keyBase ); iso_sort_keyBase = NULL; maxlen_iso_sort_keyBase = 0; } else { len_iso_sort_keyBase = num_at_tg; } if ( !iso_exchg_atnos && !iso_sort_keyBase ) { /* no isotopic part at all or only tautomeric groups */ inchi_free( nSymmRankBaseIso ); nSymmRankBaseIso = NULL; inchi_free( nCanonRankBaseIso ); nCanonRankBaseIso = NULL; inchi_free( nAtomNumberCanonBaseIso ); nAtomNumberCanonBaseIso = NULL; } else { /* proceed with tautomeric isotopic canonicalization */ /* pointers */ pCD[iBase].LinearCT = NULL; pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ pCD[iBase].NumHfixed = NULL; pCD[iBase].iso_sort_key = iso_sort_keyBase; pCD[iBase].iso_exchg_atnos = iso_exchg_atnos; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iBase].iso_sort_key_Hfixed = NULL; #endif /* variables - unchanged */ pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; pCD[iBase].maxlenNumH = maxlenNumH; /* return values & input/output */ pCD[iBase].nLenLinearCT = s[iBase].nLenCT; pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; pCD[iBase].lenNumH = lenNumH /* = k */; pCD[iBase].lenNumHfixed = 0; pCD[iBase].len_iso_sort_key = len_iso_sort_keyBase; pCD[iBase].maxlen_iso_sort_key = maxlen_iso_sort_keyBase; pCD[iBase].len_iso_exchg_atnos = len_iso_exchg_atnos; pCD[iBase].maxlen_iso_exchg_atnos = maxlen_iso_exchg_atnos; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iBase].len_iso_sort_key_Hfixed = 0; pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; #endif pCD[iBase].nAuxRank = NULL; if ( num_iso_NoTautH || num_iso_Base || num_iso_NoAuxBase ) { /* check whether we need actual canonicalization */ memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); for ( i = 0; i < num_at_tg; i ++ ) { if ( nTempRank[nSymmRankBase[i]-1] < i ) { nTempRank[nSymmRankBase[i]-1] = i; /* greatest class representative */ } } for ( i = 0; i < num_at_tg; i ++ ) { if ( (iso_sort_keyBase? (iso_sort_keyBase[i] != iso_sort_keyBase[nTempRank[nSymmRankBase[i]-1]]):0) || (iso_exchg_atnos? (iso_exchg_atnos[i] != iso_exchg_atnos[nTempRank[nSymmRankBase[i]-1]]):0)) { pCD[iBase].nCanonFlags |= CANON_FLAG_ISO_TAUT_DIFF; break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ } } } else { i = num_at_tg; /* should not happen */ } /* i = 0; *//* debug: force to call the canonicalization */ if ( i < num_at_tg ) { /* we need canonicalization */ /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ #if ( USE_AUX_RANKING == 1 ) /* refine no-taut-H partition according to non-taut H + t-groups isotopic distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_at_tg; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; pAtomInvariantAux[i].iso_sort_key = iso_sort_keyBase? iso_sort_keyBase[i] : 0; /* additional differentiation */ pAtomInvariantAux[i].iso_aux_key = iso_exchg_atnos? iso_exchg_atnos[i] : 0; } /* initial ranks for non-taut H isotopic distribution */ nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable, not a digraph procedure */ nNumCurrRanks = DifferentiateRanks2( num_at_tg, NeighList[TAUT_YES], nNumCurrRanks, nRankAux, nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means first use qsort */ ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iBase].nAuxRank = nRankAux; #endif ret = CanonGraph06( num_atoms, num_at_tg, num_max, 1 /* digraph */, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, nSymmRankBaseIso, nCanonRankBaseIso, nAtomNumberCanonBaseIso, pCD+iBase, pCC, &Ct_Base, &Ct_BaseIso ); if ( ret < 0 ) { goto exit_error; } /* in case of a tautomeric structure the final results are in: nSymmRankBaseIso nCanonRankBaseIso nAtomNumberCanonBaseIso Ct_BaseIso iso_sort_keyBase (original isotopic atom & t-group positions) Ct_BaseIso->iso_exchg_atnos: 0=>can exchange isotopic H, including tautomeric atoms iso_exchg_atnos : same, in order of t_group_info->nIsotopicEndpointAtomNumber[] */ } else { /* copy the results of the previous (no taut H) canonicalization */ /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && CTableCreate( Ct_Temp, num_atoms, pCD+iBase) ) { CtFullCopy( Ct_Temp, Ct_Base ); /* since Ct_Base does not have Ct_Base->iso_sort_key we have to fill out Ct_Temp->iso_sort_key separately */ if ( iso_sort_keyBase ) { for ( i = 0; i < num_at_tg; i ++ ) { Ct_Temp->iso_sort_key[nCanonRankBase[i]-1] = iso_sort_keyBase[i]; } Ct_Temp->len_iso_sort_key = num_at_tg; } else { Ct_Temp->len_iso_sort_key = 0; } if ( iso_exchg_atnos ) { for ( i = 0; i < num_atoms; i ++ ) { Ct_Temp->iso_exchg_atnos[nCanonRankBase[i]-1] = iso_exchg_atnos[i]; } Ct_Temp->len_iso_exchg_atnos = num_at_tg; } else { Ct_Temp->len_iso_exchg_atnos = 0; } } else { goto exit_error_alloc; } Ct_BaseIso = Ct_Temp; Ct_Temp = NULL; memcpy( nSymmRankBaseIso, nSymmRankBase, num_at_tg*sizeof(nSymmRankBaseIso[0]) ); memcpy( nCanonRankBaseIso, nCanonRankBase, num_at_tg*sizeof(nCanonRankBaseIso[0]) ); memcpy( nAtomNumberCanonBaseIso, nAtomNumberCanonBase, num_at_tg*sizeof(nAtomNumberCanonBaseIso[0]) ); } /* in case of non-tautomeric component this is the final result */ /* i = CtFullCompare( Ct_BaseIso, Ct_Temp, num_at_tg, 0, 0 );*/ t_group_info->bIgnoreIsotopic = 1; } } } /**********************************************************************************/ /* get "fixed H" canonical numbering, connection table, and equivalence partition */ /**********************************************************************************/ if ( bReqTaut && bReqNonTaut ) { maxlenNumHfixed = num_atoms + 1; nSymmRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixH[0] ) ); nCanonRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixH[0] ) ); nAtomNumberCanonFixH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixH[0]) ); NumHfixed = (NUM_H *) inchi_calloc( maxlenNumHfixed, sizeof(NumHfixed[0]) ); if ( !NumHfixed || !nSymmRankFixH || !nCanonRankFixH || !nAtomNumberCanonFixH ) { goto exit_error_alloc; } for ( i = 0; i < num_atoms; i ++ ) { /* fixed and non-tautomeric H different in taut and non-taut structures */ if ( at_base[i].endpoint ) { NumHfixed[i] = at_other[i].num_H? at_other[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; } else if ( at_other[i].num_H != at_base[i].num_H ) { NumHfixed[i] = (NUM_H)at_other[i].num_H - (NUM_H)at_base[i].num_H + BASE_H_NUMBER; } else { NumHfixed[i] = EMPTY_H_NUMBER; } } /* pointers */ pCD[iOther].LinearCT = NULL; /* LinearCT; */ pCD[iOther].NumH = numHNoTautH; pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ pCD[iOther].iso_sort_key = NULL; pCD[iOther].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].iso_sort_key_Hfixed = NULL; #endif pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; pCD[iOther].maxlenNumH = maxlenNumHNoTautH; pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; /* return values & input/output */ pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; pCD[iOther].lenNumHfixed = num_atoms; pCD[iOther].len_iso_sort_key = 0; pCD[iOther].maxlen_iso_sort_key = 0; pCD[iOther].len_iso_exchg_atnos = 0; pCD[iOther].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].len_iso_sort_key_Hfixed = 0; pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; #endif pCD[iOther].nAuxRank = NULL; #if ( USE_AUX_RANKING == 1 ) if ( !nRankAux ) nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); if ( !nAtomNumberAux ) nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); if ( !pAtomInvariantAux ) pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); if ( !nRankAux || !nAtomNumberAux || !pAtomInvariantAux ) { goto exit_error_alloc; } /* refine no-H partition according to non-taut H distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; pAtomInvariantAux[i].val[1] = NumHfixed[i]; /* additional differentiation */ } /* initial ranks for t-group(s) */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable, digraph procedure */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], nNumCurrRanks, nRankAux, nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iOther].nAuxRank = nRankAux; #endif ret = CanonGraph07( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankFixH, nCanonRankFixH, nAtomNumberCanonFixH, pCD+iOther, pCC, &Ct_NoTautH, &Ct_FixH ); if ( ret < 0 ) { goto exit_error; } /*******************************************************************************************/ /* get "fixed H" isotopic canonical numbering, connection table, and equivalence partition */ /*******************************************************************************************/ iflag = s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic; if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ iflag = iflag || s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic; if (iflag) { #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) maxlen_iso_sort_key_Hfixed = #endif maxlen_iso_sort_key_NoTautH = num_atoms+1; nSymmRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixHIso[0] ) ); nCanonRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixHIso[0] ) ); nAtomNumberCanonFixHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixHIso[0]) ); #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_Hfixed, sizeof(iso_sort_key_Hfixed[0]) ); #endif iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); if ( !nSymmRankFixHIso || !nCanonRankFixHIso || !nAtomNumberCanonFixHIso || #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) !iso_sort_key_Hfixed || #endif !iso_sort_key_NoTautH ) { goto exit_error_alloc; } #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) /* fill out isotopic non-tautomeric keys */ for ( i = 0; i < num_atoms; i ++ ) { if ( at_base[i].endpoint ) { iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); iso_sort_key2 = make_iso_sort_key( 0, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); } else { iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); iso_sort_key2 = 0; } if ( iso_sort_key ) { iso_sort_key_NoTautH[i] = iso_sort_key; num_iso_NoTautH ++; } else { iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; } if ( iso_sort_key2 ) { num_iso_Hfixed ++; iso_sort_key_Hfixed[i] = iso_sort_key2; } else { iso_sort_key_Hfixed[i] = EMPTY_ISO_SORT_KEY; } } #else /* fill out isotopic non-tautomeric keys */ for ( i = 0; i < num_atoms; i ++ ) { if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ { /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ if ( at_other ) { iso_sort_key = make_iso_sort_key( at_other[i].iso_atw_diff, at_other[i].num_iso_H[0], at_other[i].num_iso_H[1], at_other[i].num_iso_H[2]); } else { iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); } } else iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); if ( iso_sort_key ) { iso_sort_key_NoTautH[i] = iso_sort_key; num_iso_NoTautH ++; } else { iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; } } #endif /* pointers */ pCD[iOther].LinearCT = NULL; /* LinearCT; */ pCD[iOther].NumH = numHNoTautH; pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; pCD[iOther].iso_exchg_atnos = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].iso_sort_key_Hfixed = iso_sort_key_Hfixed; #endif pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; pCD[iOther].maxlenNumH = maxlenNumHNoTautH; pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; /* return values & input/output */ pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; pCD[iOther].lenNumHfixed = num_atoms; pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; pCD[iOther].len_iso_exchg_atnos = 0; pCD[iOther].maxlen_iso_exchg_atnos = 0; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) pCD[iOther].len_iso_sort_key_Hfixed = len_iso_sort_key_Hfixed = num_atoms; pCD[iOther].maxlen_iso_sort_key_Hfixed = maxlen_iso_sort_key_Hfixed; #endif pCD[iOther].nAuxRank = NULL; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( num_iso_Hfixed || num_iso_NoTautH ) #else if ( num_iso_NoTautH ) #endif { /* check whether we need NoTautH cononicalization */ memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); for ( i = 0; i < num_atoms; i ++ ) { if ( nTempRank[nSymmRankFixH[i]-1] < i ) { nTempRank[nSymmRankFixH[i]-1] = i; /* greatest class representative */ } } for ( i = 0; i < num_atoms; i ++ ) { #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) if ( iso_sort_key_Hfixed[i] != iso_sort_key_Hfixed[nTempRank[nSymmRankFixH[i]-1]] ) break; #endif if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankFixH[i]-1]]) break; /* atoms so far found to be equivalent have different isotopic shifts; the canonicalization is needed */ } } else { i = num_atoms; /* should not happen */ } /* i = 0; *//* debug: force to call the canonicalization */ if ( i < num_atoms ) { pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_FIXED_H_DIFF; /* we need canonicalization */ /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ #if ( USE_AUX_RANKING == 1 ) /* refine fixed-taut-H partition according to the isotopic distribution */ memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); for ( i = 0; i < num_atoms; i ++ ) { pAtomInvariantAux[i].val[0] = nSymmRankFixH[i]; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) iso_sort_key = 0; if (iso_sort_key_NoTautH[i]!=EMPTY_ISO_SORT_KEY) iso_sort_key |= iso_sort_key_NoTautH[i]; if (iso_sort_key_Hfixed[i] !=EMPTY_ISO_SORT_KEY) iso_sort_key |= iso_sort_key_Hfixed[i]; if ( !iso_sort_key ) iso_sort_key = EMPTY_ISO_SORT_KEY; #else iso_sort_key = iso_sort_key_NoTautH[i]; #endif pAtomInvariantAux[i].iso_sort_key = iso_sort_key; /* additional differentiation */ } /* initial ranks for non-taut H isotopic distribution */ nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); /* make equitable, digraph procedure */ nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], nNumCurrRanks, nRankAux, nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); /* to accelerate do not call CanonGraph() to find really equivalent atoms */ pCD[iOther].nAuxRank = nRankAux; #endif ret = CanonGraph08( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, nSymmRankFixHIso, nCanonRankFixHIso, nAtomNumberCanonFixHIso, pCD+iOther, pCC, &Ct_FixH, &Ct_FixHIso ); if ( ret < 0 ) { goto exit_error; } /* in case of non-tautomeric structure the final results are in: nSymmRankFixHIso nCanonRankFixHIso nAtomNumberCanonFixHIso Ct_FixHIso iso_sort_keyBase ([0..num_atoms] original isotopic atom positions) iso_sort_key_Hfixed (original fixed tautomeric H distribution) */ } else { /* copy the results of the previous (no taut H) canonicalization */ /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { CtFullCopy( Ct_Temp, Ct_FixH ); /* since Ct_FixH does not have Ct_FixH->iso_sort_key and Ct_FixH->iso_sort_key_Hfixed we have to fill out Ct_Temp->iso_sort_key and Ct_Temp->iso_sort_key_Hfixed separately */ for ( i = 0; i < num_atoms; i ++ ) { Ct_Temp->iso_sort_key[nCanonRankFixH[i]-1] = iso_sort_key_NoTautH[i]; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) Ct_Temp->iso_sort_key_Hfixed[nCanonRankFixH[i]-1] = iso_sort_key_Hfixed[i]; #endif } Ct_Temp->len_iso_sort_key = num_atoms; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) Ct_Temp->len_iso_sort_key_Hfixed = num_atoms; #endif /*Ct_Temp->lenNumH = num_atoms;*/ } else { goto exit_error_alloc; } Ct_FixHIso = Ct_Temp; Ct_Temp = NULL; memcpy( nSymmRankFixHIso, nSymmRankFixH, num_atoms*sizeof(nSymmRankFixHIso[0]) ); memcpy( nCanonRankFixHIso, nCanonRankFixH, num_atoms*sizeof(nCanonRankFixHIso[0]) ); memcpy( nAtomNumberCanonFixHIso, nAtomNumberCanonFixH, num_atoms*sizeof(nAtomNumberCanonFixHIso[0]) ); } /* in case of non-tautomeric component this is the final result */ /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ } } /* "fixed H" canonical numbering */ /* consistency check: compare canonical connection tables, H-atoms, isotopic H & taut groups */ ret = 0; ret |= (Ct_NoH->lenCt != Ct_NoTautH->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautH->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); if ( bReqTaut ) { if ( Ct_FixH ) { ret |= (Ct_NoTautH->lenCt != Ct_FixH->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_FixH->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); ret |= (Ct_NoTautH->lenNumH != Ct_FixH->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_FixH->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); } ret |= (Ct_NoTautH->lenCt > Ct_Base->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_Base->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); ret |= (Ct_NoTautH->lenNumH > Ct_Base->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_Base->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); } /* isotopic canonicalization */ if ( Ct_NoTautHIso ) { ret |= (Ct_NoH->lenCt != Ct_NoTautHIso->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautHIso->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); ret |= (Ct_NoTautH->lenNumH != Ct_NoTautHIso->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_NoTautHIso->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); } else if ( Ct_BaseIso ) { ret |= (Ct_BaseIso->lenCt != Ct_Base->lenCt) || memcmp(Ct_BaseIso->Ctbl, Ct_Base->Ctbl, Ct_BaseIso->lenCt * sizeof(Ct_BaseIso->Ctbl[0])); ret |= (Ct_BaseIso->lenNumH != Ct_Base->lenNumH) || memcmp( Ct_BaseIso->NumH, Ct_Base->NumH, Ct_BaseIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); if ( Ct_FixHIso ) { ret |= (Ct_FixHIso->lenCt > Ct_BaseIso->lenCt) || memcmp(Ct_FixHIso->Ctbl, Ct_BaseIso->Ctbl, Ct_FixHIso->lenCt * sizeof(Ct_FixHIso->Ctbl[0])); ret |= (Ct_FixHIso->lenNumH > Ct_BaseIso->lenNumH) || memcmp( Ct_FixHIso->NumH, Ct_BaseIso->NumH, Ct_FixHIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); } } if ( ret ) { goto exit_error; } if ( bReqTaut ) { /* restore save "process isotopic" mark; temporarily set it to NO */ t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; } /* output the canonicalization results */ pBCN->num_max = num_max; pBCN->num_at_tg = num_at_tg; pBCN->num_atoms = num_atoms; pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; NeighList[TAUT_NON] = NULL; pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; NeighList[TAUT_YES] = NULL; if ( bReqTaut ) { /* tautomeric results */ /* base tautomeric structure, iBase = TAUT_YES */ pBCN->ftcn[TAUT_YES].num_at_tg = num_at_tg; pBCN->ftcn[TAUT_YES].num_atoms = num_atoms; pBCN->ftcn[TAUT_YES].LinearCt = Ct_Base->Ctbl; Ct_Base->Ctbl = NULL; pBCN->ftcn[TAUT_YES].nLenLinearCtAtOnly = s[iBase].nLenCTAtOnly; pBCN->ftcn[TAUT_YES].nMaxLenLinearCt = s[iBase].nLenCT+1; pBCN->ftcn[TAUT_YES].nLenLinearCt = s[iBase].nLenCT; pBCN->ftcn[TAUT_YES].PartitionCt.Rank = nCanonRankBase; nCanonRankBase = NULL; pBCN->ftcn[TAUT_YES].PartitionCt.AtNumber = nAtomNumberCanonBase; nAtomNumberCanonBase = NULL; pBCN->ftcn[TAUT_YES].nSymmRankCt = nSymmRankBase; nSymmRankBase = NULL; pBCN->ftcn[TAUT_YES].nNumHOrig = numH; numH = NULL; pBCN->ftcn[TAUT_YES].nNumH = Ct_Base->NumH; Ct_Base->NumH = NULL; pBCN->ftcn[TAUT_YES].nLenNumH = inchi_min(maxlenNumH, Ct_Base->maxlenNumH); /* fixed H structure: exists only if the structure is tautomeric */ pBCN->ftcn[TAUT_YES].nNumHOrigFixH = NULL; pBCN->ftcn[TAUT_YES].nNumHFixH = NULL; pBCN->ftcn[TAUT_YES].nLenNumHFixH = 0; pBCN->ftcn[TAUT_YES].nCanonFlags |= pCD[iBase].nCanonFlags; CleanNumH( pBCN->ftcn[TAUT_YES].nNumHOrig, pBCN->ftcn[TAUT_YES].nLenNumH ); CleanNumH( pBCN->ftcn[TAUT_YES].nNumH, pBCN->ftcn[TAUT_YES].nLenNumH ); CleanCt ( pBCN->ftcn[TAUT_YES].LinearCt, pBCN->ftcn[TAUT_YES].nLenLinearCt ); /* isotopic canonicalization */ if ( Ct_BaseIso ) { pBCN->ftcn[TAUT_YES].PartitionCtIso.Rank = nCanonRankBaseIso; nCanonRankBaseIso = NULL; pBCN->ftcn[TAUT_YES].PartitionCtIso.AtNumber = nAtomNumberCanonBaseIso; nAtomNumberCanonBaseIso = NULL; pBCN->ftcn[TAUT_YES].nSymmRankCtIso = nSymmRankBaseIso; nSymmRankBaseIso = NULL; pBCN->ftcn[TAUT_YES].iso_sort_keys = Ct_BaseIso->iso_sort_key; Ct_BaseIso->iso_sort_key = NULL; pBCN->ftcn[TAUT_YES].iso_sort_keysOrig = iso_sort_keyBase; iso_sort_keyBase = NULL; pBCN->ftcn[TAUT_YES].len_iso_sort_keys = len_iso_sort_keyBase; pBCN->ftcn[TAUT_YES].iso_exchg_atnos = Ct_BaseIso->iso_exchg_atnos; Ct_BaseIso->iso_exchg_atnos = NULL; pBCN->ftcn[TAUT_YES].iso_exchg_atnosOrig = iso_exchg_atnos; iso_exchg_atnos = NULL; CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keys, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keysOrig, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); } } /* tautomeric results */ if ( bReqNonTaut ) { /* non-tautomeric results */ /* TAUT_NON if tautomeric + non-tautomeric or special non-taut request TAUT_YES if the structure happened to be non-tautomeric while user requested tautomeric processing In both cases the correct index is iOther. TAUT_NON replaced with iOther 4-2-2004 */ if ( !bReqTaut ) { /* rearrange the results for a non-tautomeric structure */ nSymmRankFixH = nSymmRankNoTautH; nSymmRankNoTautH = NULL; nCanonRankFixH = nCanonRankNoTautH; nCanonRankNoTautH = NULL; nAtomNumberCanonFixH = nAtomNumberCanonNoTautH; nAtomNumberCanonNoTautH = NULL; Ct_FixH = Ct_NoTautH; Ct_NoTautH = NULL; /* isotopic canonicalization */ nSymmRankFixHIso = nSymmRankNoTautHIso; nSymmRankNoTautHIso = NULL; nCanonRankFixHIso = nCanonRankNoTautHIso; nCanonRankNoTautHIso = NULL; nAtomNumberCanonFixHIso = nAtomNumberCanonNoTautHIso; nAtomNumberCanonNoTautHIso = NULL; Ct_FixHIso = Ct_NoTautHIso; Ct_NoTautHIso = NULL; if ( iOther == TAUT_YES && pBCN->ftcn[TAUT_NON].NeighList && !pBCN->ftcn[TAUT_YES].NeighList ) { /* here only non-taut results go to pBCN->ftcn[TAUT_YES] Since non-taut NeighList is always in pBCN->ftcn[TAUT_NON].NeighList, move it to pBCN->ftcn[TAUT_YES].NeighList. 2004-04-02. */ pBCN->ftcn[TAUT_YES].NeighList = pBCN->ftcn[TAUT_NON].NeighList; pBCN->ftcn[TAUT_NON].NeighList = NULL; } } pBCN->ftcn[iOther].num_at_tg = num_atoms; pBCN->ftcn[iOther].num_atoms = num_atoms; pBCN->ftcn[iOther].LinearCt = Ct_FixH->Ctbl; Ct_FixH->Ctbl = NULL; pBCN->ftcn[iOther].nLenLinearCtAtOnly = s[iOther].nLenCTAtOnly; pBCN->ftcn[iOther].nMaxLenLinearCt = s[iOther].nLenCTAtOnly+1; pBCN->ftcn[iOther].nLenLinearCt = s[iOther].nLenCTAtOnly; pBCN->ftcn[iOther].PartitionCt.Rank = nCanonRankFixH; nCanonRankFixH = NULL; pBCN->ftcn[iOther].PartitionCt.AtNumber = nAtomNumberCanonFixH; nAtomNumberCanonFixH = NULL; pBCN->ftcn[iOther].nSymmRankCt = nSymmRankFixH; nSymmRankFixH = NULL; pBCN->ftcn[iOther].nNumHOrig = numHNoTautH; numHNoTautH = NULL; pBCN->ftcn[iOther].nNumH = Ct_FixH->NumH; Ct_FixH->NumH = NULL; pBCN->ftcn[iOther].nLenNumH = inchi_min(maxlenNumHNoTautH,Ct_FixH->maxlenNumH); /* fixed H structure: exists only if the structure is tautomeric */ pBCN->ftcn[iOther].nNumHOrigFixH = NumHfixed; NumHfixed = NULL; pBCN->ftcn[iOther].nNumHFixH = Ct_FixH->NumHfixed; Ct_FixH->NumHfixed = NULL; pBCN->ftcn[iOther].nLenNumHFixH = num_atoms; pBCN->ftcn[iOther].nCanonFlags |= pCD[iOther].nCanonFlags; /* original H */ CleanNumH( pBCN->ftcn[iOther].nNumHOrig, pBCN->ftcn[iOther].nLenNumH ); CleanNumH( pBCN->ftcn[iOther].nNumHOrigFixH, pBCN->ftcn[iOther].nLenNumH ); /* canonical H positions */ CleanNumH( pBCN->ftcn[iOther].nNumH, pBCN->ftcn[iOther].nLenNumH ); CleanNumH( pBCN->ftcn[iOther].nNumHFixH, pBCN->ftcn[iOther].nLenNumH ); /* connection table */ CleanCt( pBCN->ftcn[iOther].LinearCt, pBCN->ftcn[iOther].nLenLinearCt ); /* isotopic canonicalization */ if ( Ct_FixHIso ) { pBCN->ftcn[iOther].PartitionCtIso.Rank = nCanonRankFixHIso; nCanonRankFixHIso = NULL; pBCN->ftcn[iOther].PartitionCtIso.AtNumber = nAtomNumberCanonFixHIso; nAtomNumberCanonFixHIso = NULL; pBCN->ftcn[iOther].nSymmRankCtIso = nSymmRankFixHIso; nSymmRankFixHIso = NULL; pBCN->ftcn[iOther].iso_sort_keys = Ct_FixHIso->iso_sort_key; Ct_FixHIso->iso_sort_key = NULL; pBCN->ftcn[iOther].iso_sort_keysOrig = iso_sort_key_NoTautH; iso_sort_key_NoTautH = NULL; pBCN->ftcn[iOther].len_iso_sort_keys = len_iso_sort_key_NoTautH; #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, Ct_FixHIso->iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); #else CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, pBCN->ftcn[iOther].len_iso_sort_keys ); CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, pBCN->ftcn[iOther].len_iso_sort_keys ); #endif } } /* non-tautomeric results */ goto exit_function; exit_error_alloc: ret = CT_OUT_OF_RAM; goto exit_function; exit_error: if ( !RETURNED_ERROR(ret) ) { ret = CT_CANON_ERR; } goto exit_function; exit_function: #define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} #define FREE_ARRAY(X) if (X) inchi_free(X); FreeNeighList( NeighList[TAUT_NON] ); FreeNeighList( NeighList[TAUT_YES] ); FREE_CONTABLE( Ct_NoH ) FREE_CONTABLE( Ct_NoTautH ) FREE_CONTABLE( Ct_Base ) FREE_CONTABLE( Ct_FixH ) FREE_CONTABLE( Ct_Temp ) /* isotopic canonicalization */ FREE_CONTABLE( Ct_NoTautHIso ) FREE_CONTABLE( Ct_BaseIso ) FREE_CONTABLE( Ct_FixHIso ) /* free the first two pointers from pBCN->pRankStack */ FREE_ARRAY( nRank ) FREE_ARRAY( nAtomNumber ) if ( pBCN->pRankStack ) { pBCN->pRankStack[0] = pBCN->pRankStack[1] = NULL; } #if ( USE_AUX_RANKING == 1 ) FREE_ARRAY( nRankAux ) FREE_ARRAY( nAtomNumberAux ) FREE_ARRAY( pAtomInvariantAux ) #endif FREE_ARRAY( pAtomInvariant ) FREE_ARRAY( nCanonRankNoH ) FREE_ARRAY( nAtomNumberCanonNoH ) FREE_ARRAY( nSymmRankNoH ) FREE_ARRAY( nSymmRankNoTautH ) FREE_ARRAY( nCanonRankNoTautH ) FREE_ARRAY( nAtomNumberCanonNoTautH ) FREE_ARRAY( numHNoTautH ) FREE_ARRAY( nSymmRankBase ) FREE_ARRAY( nCanonRankBase ) FREE_ARRAY( nAtomNumberCanonBase ) FREE_ARRAY( numH ) FREE_ARRAY( nSymmRankFixH ) FREE_ARRAY( nCanonRankFixH ) FREE_ARRAY( nAtomNumberCanonFixH ) FREE_ARRAY( NumHfixed ) /* isotopic canonicalization */ FREE_ARRAY( nSymmRankNoTautHIso ) FREE_ARRAY( nCanonRankNoTautHIso ) FREE_ARRAY( nAtomNumberCanonNoTautHIso ) FREE_ARRAY( iso_sort_key_NoTautH ) FREE_ARRAY( nSymmRankBaseIso ) FREE_ARRAY( nCanonRankBaseIso ) FREE_ARRAY( nAtomNumberCanonBaseIso ) FREE_ARRAY( iso_sort_keyBase ) FREE_ARRAY( iso_exchg_atnos ) FREE_ARRAY( nSymmRankFixHIso ) FREE_ARRAY( nCanonRankFixHIso ) FREE_ARRAY( nAtomNumberCanonFixHIso ) #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) FREE_ARRAY( iso_sort_key_Hfixed ) #endif FREE_ARRAY( nTempRank ) #undef FREE_CONTABLE #undef FREE_ARRAY return ret; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicano.c000066400000000000000000003102441271037650300234760ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #include "ichitime.h" #include "ichi.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "inpdef.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" /**************************************************************************** * * Globals for sorting */ const NEIGH_LIST *pNeighList_RankForSort; const ATOM_INVARIANT2 *pAtomInvariant2ForSort; const AT_NUMB *pNeighborsForSort; const AT_RANK *pn_RankForSort; AT_RANK nMaxAtNeighRankForSort; int nNumCompNeighborsRanksCountEql; #define tsort insertions_sort /* local prototypes */ void FillOutAtomInvariant( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT* pAtomInvariant, CANON_STAT* pCS ); int Canon_INChI1( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); int Canon_INChI2( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); #ifdef COMPILE_ANSI_ONLY static clock_t InchiClock(void); #ifdef INCHI_USETIMES static clock_t InchiClock(void) { struct tms buf; clock_t c = times( &buf ); if ( c != (clock_t)-1 ) { return buf.tms_utime; } return 0; } #else static clock_t InchiClock(void) { clock_t c = clock(); if ( c != (clock_t)-1 ) { return c; } return 0; } #endif #define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) #define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) const clock_t FullMaxClock = (clock_t)(-1); const clock_t HalfMaxClock = (clock_t)(-1) / 2; clock_t MaxPositiveClock = 0; clock_t MinNegativeClock = 0; clock_t HalfMaxPositiveClock = 0; clock_t HalfMinNegativeClock = 0; static void FillMaxMinClock(void); /* keep compiler happy */ static void FillMaxMinClock(void) { if ( !MaxPositiveClock ) { clock_t valPos=0, val1 = 1; while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { valPos = val1; } MaxPositiveClock = valPos; MinNegativeClock = -valPos; HalfMaxPositiveClock = MaxPositiveClock / 2; HalfMinNegativeClock = MinNegativeClock / 2; } } /******** get current process time ****************************************/ void InchiTimeGet( inchiTime *TickEnd ) { TickEnd->clockTime = InchiClock(); } /******** returns difference TickEnd - TickStart in milliseconds **********/ long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) { if ( FullMaxClock > 0 ) { clock_t delta; if ( !TickEnd || !TickStart ) return 0; /* clock_t is unsigned */ if ( TickEnd->clockTime > TickStart->clockTime ) { if ( TickEnd->clockTime > HalfMaxClock && TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; return -INCHI_MSEC(delta); } delta = TickEnd->clockTime - TickStart->clockTime; return INCHI_MSEC(delta); } else if ( TickEnd->clockTime < TickStart->clockTime ) { if ( TickStart->clockTime > HalfMaxClock && TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; return INCHI_MSEC(delta); } delta = TickStart->clockTime - TickEnd->clockTime; return -INCHI_MSEC(delta); } return 0; /* TickEnd->clockTime == TickStart->clockTime */ } else { /* may happen under Win32 only where clock_t is SIGNED long */ clock_t delta; FillMaxMinClock( ); if ( !TickEnd || !TickStart ) return 0; if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { delta = TickEnd->clockTime - TickStart->clockTime; } else if ( TickEnd->clockTime >= HalfMaxPositiveClock && TickStart->clockTime <= HalfMinNegativeClock ) { /* end is earlier than start */ delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); delta = -delta; } else if ( TickEnd->clockTime <= HalfMinNegativeClock && TickStart->clockTime >= HalfMaxPositiveClock ) { /* start was earlier than end */ delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); } else { /* there was no overflow, clock passed zero */ delta = TickEnd->clockTime - TickStart->clockTime; } return INCHI_MSEC(delta); } } /******************* get elapsed time from TickStart ************************/ long InchiTimeElapsed( inchiTime *TickStart ) { inchiTime TickEnd; if ( !TickStart ) return 0; InchiTimeGet( &TickEnd ); return InchiTimeMsecDiff( &TickEnd, TickStart ); } /******************* add number of milliseconds to time *********************/ void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) { clock_t delta; if ( !TickEnd ) return; if ( FullMaxClock > 0 ) { /* clock_t is unsigned */ delta = INCHI_CLOCK_T(nNumMsec); TickEnd->clockTime += delta; } else { /* may happen under Win32 only where clock_t is SIGNED long */ /* clock_t is unsigned */ FillMaxMinClock( ); delta = INCHI_CLOCK_T(nNumMsec); TickEnd->clockTime += delta; } } /******************* check whether time has expired *********************/ int bInchiTimeIsOver( inchiTime *TickStart ) { if ( FullMaxClock > 0 ) { clock_t clockCurrTime; if ( !TickStart ) return 0; clockCurrTime = InchiClock(); /* clock_t is unsigned */ if ( TickStart->clockTime > clockCurrTime ) { if ( TickStart->clockTime > HalfMaxClock && TickStart->clockTime - clockCurrTime > HalfMaxClock ) { /* overflow in clockCurrTime, actually clockCurrTime was later */ return 1; } return 0; } else if ( TickStart->clockTime < clockCurrTime ) { if ( clockCurrTime > HalfMaxClock && clockCurrTime - TickStart->clockTime > HalfMaxClock ) { /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ return 0; } return 1; } return 0; /* TickStart->clockTime == clockCurrTime */ } else { /* may happen under Win32 only where clock_t is SIGNED long */ clock_t clockCurrTime; FillMaxMinClock( ); if ( !TickStart ) return 0; clockCurrTime = InchiClock(); if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || clockCurrTime <= 0 && TickStart->clockTime <= 0) { return (clockCurrTime > TickStart->clockTime); } else if ( clockCurrTime >= HalfMaxPositiveClock && TickStart->clockTime <= HalfMinNegativeClock ) { /* curr is earlier than start */ return 0; } else if ( clockCurrTime <= HalfMinNegativeClock && TickStart->clockTime >= HalfMaxPositiveClock ) { /* start was earlier than curr */ return 1; } else { /* there was no overflow, clock passed zero */ return (clockCurrTime > TickStart->clockTime); } } } #else /******** get current process time ****************************************/ void InchiTimeGet( inchiTime *TickEnd ) { if ( TickEnd ) { struct _timeb timeb; _ftime( &timeb ); TickEnd->clockTime = (unsigned long)timeb.time; TickEnd->millitime = (long)timeb.millitm; } } /******** returns difference TickEnd - TickStart in milliseconds **********/ long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) { long delta; if ( !TickEnd || !TickStart ) { return 0; } if ( TickEnd->clockTime >= TickStart->clockTime ) { delta = (long)(TickEnd->clockTime - TickStart->clockTime); delta *= 1000; delta += TickEnd->millitime - TickStart->millitime; } else { delta = -(long)(TickStart->clockTime - TickEnd->clockTime); delta *= 1000; delta += TickEnd->millitime - TickStart->millitime; } return delta; } /******************* get elapsed time from TickStart ************************/ long InchiTimeElapsed( inchiTime *TickStart ) { inchiTime TickEnd; if ( !TickStart ) return 0; InchiTimeGet( &TickEnd ); return InchiTimeMsecDiff( &TickEnd, TickStart ); } /******************* add number of milliseconds to time *********************/ void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) { long delta; if ( !TickEnd ) return; TickEnd->clockTime += nNumMsec / 1000; delta = nNumMsec % 1000 + TickEnd->millitime; TickEnd->clockTime += delta / 1000; TickEnd->millitime = delta % 1000; } /******************* check whether time has expired *********************/ int bInchiTimeIsOver( inchiTime *TickEnd ) { struct _timeb timeb; if ( !TickEnd ) return 0; _ftime( &timeb ); if ( TickEnd->clockTime > (unsigned long)timeb.time ) return 0; if ( TickEnd->clockTime < (unsigned long)timeb.time || TickEnd->millitime < (long)timeb.millitm ) { return 1; } return 0; } #endif /****************************************************************************/ /* length of canonic representation in sizeof(AT_NUMB) units */ int GetCanonLengths( int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ) { /* include taut. groups as additional "atoms" to the connection table 07-22-2002 */ int i, nNumCT, nNumBonds, nNumTBonds=0, nNumDblBondsStereo=0, nNumAsymCarbStereo=0, nNumIsotopic=0; T_GROUP *t_group = (s->nLenLinearCTTautomer && t_group_info)? t_group_info->t_group : NULL; for (nNumBonds = 0, i = 0; i < num_at; i ++ ) { nNumBonds += at[i].valence; if ( at[i].iso_sort_key ) { nNumIsotopic ++; /* not including tautomeric endpoints that are isotopic only due to mobile atoms */ } if ( at[i].parity > 0 ) { /* ignore hydrogen isotope parities in at[i].parity2 */ int j = 0, nStereoBondsToAtom=0; /* number of stereo double bonds at this atom */ int k; for ( ; j < MAX_NUM_STEREO_BONDS && (k=at[i].stereo_bond_neighbor[j]); j ++ ) { nStereoBondsToAtom += (at[k-1].parity > 0); } nNumDblBondsStereo += nStereoBondsToAtom; nNumAsymCarbStereo += !j; } } nNumDblBondsStereo /= 2; nNumBonds /= 2; s->nLenBonds = inchi_max( s->nLenBonds, nNumBonds ); nNumCT = nNumBonds; /* total number of neighbors in the CT */ #if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) nNumCT += num_at; #endif s->nLenCTAtOnly = inchi_max(s->nLenCTAtOnly, nNumCT); if ( t_group ) { for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { nNumTBonds += t_group[i].nNumEndpoints; } nNumCT += nNumTBonds; #if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) nNumCT += t_group_info->num_t_groups; #endif } nNumCT = inchi_max( 1, nNumCT ); /* keep GetBaseCanonRanking() happy */ s->nLenCT = inchi_max(s->nLenCT, nNumCT); s->nLenIsotopic = inchi_max(s->nLenIsotopic, nNumIsotopic); s->nLenLinearCTStereoDble = inchi_max(s->nLenLinearCTStereoDble, nNumDblBondsStereo); s->nLenLinearCTStereoCarb = inchi_max(s->nLenLinearCTStereoCarb, nNumAsymCarbStereo); if ( t_group_info ) s->nLenIsotopicEndpoints = inchi_max(s->nLenIsotopicEndpoints, t_group_info->nNumIsotopicEndpoints); return 0; } /****************************************************************************/ int DeAllocateCS( CANON_STAT *pCS ) { #define LOCAL_FREE(X) do{if(X){inchi_free(X); X=NULL;}}while(0) /* connection table */ LOCAL_FREE( pCS->LinearCT ); LOCAL_FREE( pCS->nCanonOrd ); LOCAL_FREE( pCS->nSymmRank ); LOCAL_FREE( pCS->nNum_H ); LOCAL_FREE( pCS->nNum_H_fixed ); LOCAL_FREE( pCS->nExchgIsoH ); /* isotopic */ LOCAL_FREE( pCS->LinearCTIsotopic ); LOCAL_FREE( pCS->nSymmRankIsotopic ); LOCAL_FREE( pCS->nCanonOrdIsotopic ); /* isotopic tautomeric */ LOCAL_FREE( pCS->LinearCTIsotopicTautomer ); LOCAL_FREE( pCS->nCanonOrdIsotopicTaut ); LOCAL_FREE( pCS->nSymmRankIsotopicTaut ); /* stereo */ LOCAL_FREE( pCS->LinearCTStereoDble ); LOCAL_FREE( pCS->LinearCTStereoCarb ); LOCAL_FREE( pCS->LinearCTStereoDbleInv ); LOCAL_FREE( pCS->LinearCTStereoCarbInv ); LOCAL_FREE( pCS->nCanonOrdStereo ); LOCAL_FREE( pCS->nCanonOrdStereoInv ); LOCAL_FREE( pCS->nCanonOrdStereoTaut ); /* isotopic stereo */ LOCAL_FREE( pCS->LinearCTIsotopicStereoDble ); LOCAL_FREE( pCS->LinearCTIsotopicStereoCarb ); LOCAL_FREE( pCS->LinearCTIsotopicStereoDbleInv ); LOCAL_FREE( pCS->LinearCTIsotopicStereoCarbInv ); LOCAL_FREE( pCS->bRankUsedForStereo ); LOCAL_FREE( pCS->bAtomUsedForStereo ); LOCAL_FREE( pCS->nCanonOrdIsotopicStereo ); LOCAL_FREE( pCS->nCanonOrdIsotopicStereoInv ); LOCAL_FREE( pCS->nCanonOrdIsotopicStereoTaut ); /* tautomeric part of the connection table */ LOCAL_FREE( pCS->LinearCTTautomer ); LOCAL_FREE( pCS->nCanonOrdTaut ); LOCAL_FREE( pCS->nSymmRankTaut ); LOCAL_FREE( pCS->LinearCT2 ); /* for establishing constitutional equivalence */ LOCAL_FREE( pCS->nPrevAtomNumber ); FreeNeighList( pCS->NeighList ); pCS->NeighList = NULL; /* set zero lengths */ pCS->nMaxLenLinearCTStereoDble = 0; pCS->nLenLinearCTStereoDble = 0; pCS->nMaxLenLinearCTStereoCarb = 0; pCS->nLenLinearCTStereoCarb = 0; pCS->nMaxLenLinearCTIsotopicStereoDble = 0; pCS->nLenLinearCTIsotopicStereoDble = 0; pCS->nMaxLenLinearCTIsotopicStereoCarb = 0; pCS->nLenLinearCTIsotopicStereoCarb = 0; pCS->nMaxLenLinearCTTautomer = 0; pCS->nLenLinearCTTautomer = 0; pCS->nMaxLenLinearCTIsotopic = 0; pCS->nLenLinearCTIsotopic = 0; pCS->nMaxLenLinearCTIsotopicTautomer = 0; pCS->nLenLinearCTIsotopicTautomer = 0; /* set canon numbering lengths to zero */ pCS->nLenCanonOrd = 0; pCS->nLenCanonOrdIsotopic = 0; pCS->nLenCanonOrdIsotopicTaut = 0; pCS->nLenCanonOrdStereo = 0; pCS->nLenCanonOrdStereoTaut = 0; pCS->nLenCanonOrdIsotopicStereo = 0; pCS->nLenCanonOrdIsotopicStereoTaut = 0; pCS->nLenCanonOrdTaut = 0; return 0; #undef LOCAL_FREE } /****************************************************************************/ int AllocateCS( CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ) { #define pCS_CALLOC(PTR,TYPE,LEN) (pCS->PTR=(TYPE*)inchi_calloc((size_t)(LEN),sizeof(*pCS->PTR))) int num_err = 0; int num_t_groups = num_at_tg - num_at; pCS->nMode = nMode; /* connection table */ if ( (nMode & CMODE_CT) && nLenCT > 0 ) { num_err += !pCS_CALLOC(LinearCT, AT_NUMB, nLenCT); pCS->nMaxLenLinearCT = pCS->nLenLinearCT = nLenCT; pCS->nLenLinearCTAtOnly = nLenCTAtOnly; num_err += !pCS_CALLOC(nCanonOrd, AT_RANK, num_at_tg); num_err += !pCS_CALLOC(nSymmRank, AT_RANK, num_at_tg); if ( pBCN ) { num_err += !pCS_CALLOC(nNum_H, S_CHAR, num_at); num_err += !pCS_CALLOC(nNum_H_fixed, S_CHAR, num_at); num_err += !pCS_CALLOC(nExchgIsoH, S_CHAR, num_at); } } /* isotopic */ if ( (nMode & CMODE_ISO) && nLenIsotopic > 0 ) { num_err += !pCS_CALLOC(LinearCTIsotopic, AT_ISOTOPIC, nLenIsotopic); pCS->nMaxLenLinearCTIsotopic = pCS->nLenLinearCTIsotopic = nLenIsotopic; } /* isotopic tautomeric */ if ( (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) ) { if ( nLenLinearCTIsotopicTautomer > 0 ) { num_err += !pCS_CALLOC(LinearCTIsotopicTautomer, AT_ISO_TGROUP, nLenLinearCTIsotopicTautomer); pCS->nMaxLenLinearCTIsotopicTautomer = pCS->nLenLinearCTIsotopicTautomer = nLenLinearCTIsotopicTautomer; } if ( num_t_groups > 0 ) { num_err += !pCS_CALLOC(nCanonOrdIsotopicTaut, AT_RANK, num_t_groups); num_err += !pCS_CALLOC(nSymmRankIsotopicTaut, AT_RANK, num_t_groups); } } /* isotopic atoms & t-groups */ if ( (nMode & CMODE_ISO) /*&& nLenIsotopic > 0*/ || (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) && nLenLinearCTIsotopicTautomer > 0 ) { num_err += !pCS_CALLOC(nSymmRankIsotopic, AT_RANK, num_at_tg); num_err += !pCS_CALLOC(nCanonOrdIsotopic, AT_RANK, num_at_tg); } /* stereo */ if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoDble > 0 ) { num_err += !pCS_CALLOC(LinearCTStereoDble, AT_STEREO_DBLE, nLenLinearCTStereoDble); num_err += !pCS_CALLOC(LinearCTStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTStereoDble); pCS->nLenLinearCTStereoDbleInv = pCS->nMaxLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble = nLenLinearCTStereoDble; } if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoCarb > 0 ) { num_err += !pCS_CALLOC(LinearCTStereoCarb, AT_STEREO_CARB, nLenLinearCTStereoCarb); num_err += !pCS_CALLOC(LinearCTStereoCarbInv, AT_STEREO_CARB, nLenLinearCTStereoCarb); pCS->nLenLinearCTStereoCarbInv = pCS->nMaxLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb = nLenLinearCTStereoCarb; } if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) ) { num_err += !pCS_CALLOC(nCanonOrdStereo, AT_RANK, num_at_tg); num_err += !pCS_CALLOC(nCanonOrdStereoInv, AT_RANK, num_at_tg); if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { num_err += !pCS_CALLOC(nCanonOrdStereoTaut, AT_RANK, num_t_groups); } } /* isotopic stereo */ if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoDble > 0 ) { num_err += !pCS_CALLOC(LinearCTIsotopicStereoDble, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); num_err += !pCS_CALLOC(LinearCTIsotopicStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); pCS->nLenLinearCTIsotopicStereoDbleInv = pCS->nMaxLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = nLenLinearCTIsotopicStereoDble; } if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoCarb > 0 ) { num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarb, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarbInv, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); pCS->nLenLinearCTIsotopicStereoCarbInv = pCS->nMaxLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = nLenLinearCTIsotopicStereoCarb; } if ( (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { num_err += !pCS_CALLOC(nCanonOrdIsotopicStereo, AT_RANK, num_at_tg); num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoInv, AT_RANK, num_at_tg); if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoTaut, AT_RANK, num_t_groups); } } if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) || (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { num_err += !pCS_CALLOC(bRankUsedForStereo, S_CHAR, num_at); num_err += !pCS_CALLOC(bAtomUsedForStereo, S_CHAR, num_at); } /* tautomeric part of the connection table */ if ( (nMode & CMODE_CT) && (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 ) { num_err += !pCS_CALLOC(LinearCTTautomer, AT_TAUTOMER, nLenLinearCTTautomer); pCS->nMaxLenLinearCTTautomer = pCS->nLenLinearCTTautomer = nLenLinearCTTautomer; if ( num_t_groups > 0 ) { num_err += !pCS_CALLOC(nCanonOrdTaut, AT_RANK, num_t_groups); num_err += !pCS_CALLOC(nSymmRankTaut, AT_RANK, num_t_groups); } } if ( nMode & CMODE_CT ) num_err += !pCS_CALLOC(LinearCT2, AT_NUMB, nLenCT); /* for establishing constitutional equivalence */ num_err += !pCS_CALLOC(nPrevAtomNumber, AT_RANK, num_at_tg); /* set canon numbering lengths to zero */ pCS->nLenCanonOrd = 0; pCS->nLenCanonOrdIsotopic = 0; pCS->nLenCanonOrdIsotopicTaut = 0; pCS->nLenCanonOrdStereo = 0; pCS->nLenCanonOrdStereoTaut = 0; pCS->nLenCanonOrdIsotopicStereo = 0; pCS->nLenCanonOrdIsotopicStereoTaut = 0; pCS->nLenCanonOrdTaut = 0; if ( num_err ) { DeAllocateCS( pCS ); return CT_OUT_OF_RAM; /* */ } return 0; #undef pCS_CALLOC } /****************************************************************************/ #define COMPARE_WITH_CT(CT, CTLEN, VALUE, CONDITION) \ if ( CONDITION ) { \ if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) \ return 1; /* not a minimal CT */ \ (CONDITION) = (VALUE) == (CT)[CTLEN]; \ } \ (CT)[CTLEN] = VALUE; \ (CTLEN)++ #define COMPARE_WITH_CTVAL(CTVAL, VALUE, CONDITION) \ if ( CONDITION ) { \ if ( (VALUE) CT_GREATER_THAN (CTVAL) ) \ return 1; /* not a minimal CT */ \ (CONDITION) = (VALUE) == (CTVAL); \ } \ (CTVAL) = VALUE #define COMPARE_WITH_CT2(CT, CTLEN, VALUE, CONDITION, OPER) \ if ( CONDITION ) { \ if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) { \ (OPER); \ return 1; /* not a minimal CT */ \ } \ (CONDITION) = (VALUE) == (CT)[CTLEN]; \ } \ (CT)[CTLEN] = VALUE; \ (CTLEN)++ /****************************************************************************/ int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, const AT_RANK *nAtomNumber, AT_ISOTOPIC *LinearCTIsotopic, int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic) { /* at[i].init_rank = initial ranks before canonizing */ /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ /* nAtomNumber[nRank[i]-1] = i; */ int i, k, rank; int nLinearCTIsotopicLen=0; /* the following parts of the "name" should be compared */ /* after the connection table comparison is done */ /* to avoid wrong difference sign. So, these parts */ /* go to a separate buffers. */ if ( LinearCTIsotopic && nMaxLenLinearCTIsotopic > 0 ) { memset( LinearCTIsotopic, 0, nMaxLenLinearCTIsotopic * sizeof(LinearCTIsotopic[0]) ); } else { return 0; } /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ for ( rank = 1; rank <= num_atoms; rank ++ ) { i = (int)nAtomNumber[rank-1]; /* current atom */ /**************************************************** add isotopic atom info to LinearCTIsotopic *****************************************************/ /* if the atom itself is not isotopic then add it only if */ /* the atom is not an endpoint AND has attached T or D or 1H. */ k = ( !at[i].endpoint && !(at[i].cFlags & AT_FLAG_ISO_H_POINT) && (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2]) ); if ( at[i].iso_atw_diff || k ) { if ( CHECK_OVERFLOW(nLinearCTIsotopicLen, nMaxLenLinearCTIsotopic) ) return CT_OVERFLOW; /* */ LinearCTIsotopic[nLinearCTIsotopicLen].at_num = (AT_RANK)rank; LinearCTIsotopic[nLinearCTIsotopicLen].iso_atw_diff = at[i].iso_atw_diff; LinearCTIsotopic[nLinearCTIsotopicLen].num_1H = (NUM_H)(k? at[i].num_iso_H[0] : 0); LinearCTIsotopic[nLinearCTIsotopicLen].num_D = (NUM_H)(k? at[i].num_iso_H[1] : 0); LinearCTIsotopic[nLinearCTIsotopicLen].num_T = (NUM_H)(k? at[i].num_iso_H[2] : 0); nLinearCTIsotopicLen ++; } } /* end of cycle over all atoms. */ if ( LinearCTIsotopic ) { if ( *pnLenLinearCTIsotopic ) { if ( *pnLenLinearCTIsotopic != nLinearCTIsotopicLen ) return CT_LEN_MISMATCH; /* */ }else *pnLenLinearCTIsotopic = nLinearCTIsotopicLen; } /* Return value: >0 => OK */ return nLinearCTIsotopicLen; } /****************************************************************************/ int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, const AT_RANK *nRank, const AT_RANK *nAtomNumber, const AT_RANK *nSymmRank, const AT_RANK *nRankIso, const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, AT_TAUTOMER *LinearCTTautomer, int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, AT_ISO_TGROUP *LinearCTIsotopicTautomer, int nMaxLenLinearCTIsotopicTautomer, int *pnLenLinearCTIsotopicTautomer, T_GROUP_INFO *t_group_info) { /* nRank[i] = Canonical numbers of atoms,.. */ /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ /* nAtomNumber[nRank[i]-1] = i; */ T_GROUP *t_group; int i, j, len=0, g, num_num, offset, max_len = 0, len_iso=0; const static int max_num_num = sizeof(t_group->num)/sizeof(t_group->num[0]); const static int max_num_iso = sizeof(LinearCTIsotopicTautomer->num)/sizeof(LinearCTIsotopicTautomer->num[0])+T_NUM_NO_ISOTOPIC; /**************************************************************************** Tautomeric groups 07-22-2002, modified 12-2003 ****************************************************************************/ if ( num_at_tg > num_atoms && t_group_info && t_group_info->num_t_groups ) { int num_t_groups = t_group_info->num_t_groups; AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; /* equivalence */ AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; AT_RANK nOffset = (AT_RANK)num_atoms; /* Fill Canonical ranks and Symmetry Ranks */ /* memcpy( tPrevGroupNumber, tGroupNumber, num_t_groups*sizeof(tPrevGroupNumber[0])); */ for ( i = num_atoms, j = 0; i < num_at_tg; i ++, j ++ ) { /* tPrevGroupNumber[j] = */ tGroupNumber[j] = nAtomNumber[i] - nOffset; tSymmRank[j] = nSymmRank[i] - nOffset; if ( bIsoTaut ) { tiGroupNumber[j] = nAtomNumberIso[i] - nOffset; tiSymmRank[j] = nSymmRankIso[i] - nOffset; } } /* Sort enpoints within each tautomeric group according to the canonical ranks */ pn_RankForSort = nRank; for ( i = 0; i < num_t_groups; i ++ ) { qsort( t_group_info->nEndpointAtomNumber + (int)t_group_info->t_group[i].nFirstEndpointAtNoPos, t_group_info->t_group[i].nNumEndpoints, sizeof(t_group_info->nEndpointAtomNumber[0]), CompRank ); } /* fill out LinearCTTautomer */ if ( nMaxLenLinearCTTautomer ) { max_len = T_GROUP_HDR_LEN * t_group_info->num_t_groups + t_group_info->nNumEndpoints+1; if ( max_len > nMaxLenLinearCTTautomer ) return CT_OVERFLOW; /* */ } /**************************************************************** * tautomer group format (#: is an offset) **************************************************************** * HEADER (T_GROUP_HDR_LEN=3+3iso) * 0: N = number of endpoints ( t_group->nNumEndpoints ) * 1: number of mobile groups ( t_group->num[0] ) * 2: number of neg. charges ( t_group->num[1] ) {note: T_NUM_NO_ISOTOPIC=2} * ENDPOINT RANKS * 3..N+2: sorted tautomer group endpoint ranks; the sorting order is in * t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+j], j=0..N-1 * * End mark : N==0 ****************************************************************/ /* num_num = t_group_info->bIgnoreIsotopic? T_NUM_NO_ISOTOPIC : max_num_num; */ num_num = max_num_num; /* always include isotopic info; ignore it at the CT comparison step. */ for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { g = tGroupNumber[i]; /* ith tautomeric group number in canonical order */ t_group = t_group_info->t_group + g; /******************************************************* * Tautomer non-isotopic part: LinearCTTautomer *******************************************************/ /* check length */ if ( CHECK_OVERFLOW(len + T_GROUP_HDR_LEN + t_group->nNumEndpoints, max_len) ) return CT_OVERFLOW; /* */ /* t_group header: number of endpoints */ LinearCTTautomer[len++] = t_group->nNumEndpoints; /* t_group header: */ /* (a) number of mobile groups in the t_group (number of H + number of (-) ) and */ /* (b) number of mobile negative charges (-) in the t_group */ for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { LinearCTTautomer[len++] = t_group->num[j]; } /* t_group endpoint ranks link the group to the tautomeric endpoint atoms in the structure */ /* according to their ranks */ for ( j = 0, offset = t_group->nFirstEndpointAtNoPos; j < t_group->nNumEndpoints; j ++ ) { LinearCTTautomer[len++] = nRank[(int)t_group_info->nEndpointAtomNumber[offset+j]]; } } if ( nMaxLenLinearCTTautomer ) { LinearCTTautomer[len++] = 0; /* or CT_INITVALUE ??? */ if ( len != max_len ) { len = -len; /* program error */ /* */ } else if ( *pnLenLinearCTTautomer && *pnLenLinearCTTautomer != len ) { return CT_LEN_MISMATCH; } else { *pnLenLinearCTTautomer = len; } } else { *pnLenLinearCTTautomer = 0; } /****************************************************************** * Isotopic Tautomeric mobile groups part: LinearCTIsotopicTautomer ******************************************************************/ if ( nMaxLenLinearCTIsotopicTautomer && !t_group_info->nNumIsotopicEndpoints ) { for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { g = tiGroupNumber[i]; /* ith tautomeric group number in canonical order */ t_group = t_group_info->t_group + g; /* find if mobile hydrogens are isotopic */ if ( !t_group->iWeight ) { continue; /* no isotopic H */ } if ( CHECK_OVERFLOW(len_iso, nMaxLenLinearCTIsotopicTautomer) ) return CT_OVERFLOW; /* */ for ( j = T_NUM_NO_ISOTOPIC; j < max_num_num && j < max_num_iso; j ++ ) { /* num_T, num_D, num_1H */ LinearCTIsotopicTautomer[len_iso].num[j-T_NUM_NO_ISOTOPIC] = t_group->num[j]; } /* link to tautomer group LinearCTTautomer[i]: */ LinearCTIsotopicTautomer[len_iso++].tgroup_num = (AT_NUMB)(i + 1); /* t_group isotopic rank */ } } if ( nMaxLenLinearCTIsotopicTautomer ) { if ( *pnLenLinearCTIsotopicTautomer && *pnLenLinearCTIsotopicTautomer != len_iso ) { return CT_LEN_MISMATCH; } *pnLenLinearCTIsotopicTautomer = len_iso; } else { *pnLenLinearCTIsotopicTautomer = 0; } } return len; } /**************************************************************************** * * Update a linear connection table out of final ranks */ int UpdateFullLinearCT( int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, CANON_STAT* pCS, int bFirstTime ) { /* at[i].init_rank = initial ranks before canonizing */ /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ /* nAtomNumber[nRank[i]-1] = i; */ AT_NUMB nNeighborNumber[MAXVAL]; int i, j, k, num_neigh, rank, bCompare; /*, nRetVal; */ T_GROUP_INFO *t_group_info = NULL; T_GROUP *t_group = NULL; AT_NUMB *nEndpointAtomNumber = NULL; int nCTLen=0, nCTLenAtOnly=0; AT_NUMB r_neigh; AT_NUMB *LinearCT = pCS->LinearCT; /* the following parts of the "name" should be compared */ /* after the connection table comparison is done */ /* to avoid wrong difference sign. So, these parts */ /* go to a separate buffers. */ /* -- currently not used at all at all -- */ #if CT_ATOMID != CT_ATOMID_DONTINCLUDE AT_NUMB r0_at_type; #endif bCompare = bFirstTime? 0 : 1; if ( num_at_tg > num_atoms ) { t_group_info = pCS->t_group_info; t_group = t_group_info->t_group; } else { t_group_info = NULL; t_group = NULL; } /**********************************************************************/ /* */ /* CYCLE 1: FILL OUT CONNECTION TABLE(S) FOR ALL ATOMS */ /* ** NOT INCLUDING ISOTOPIC ATOMS AND 1H, 2H(D), 3H(T) ** */ /* */ /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ /**********************************************************************/ for ( rank = 1; rank <= num_atoms; rank ++ ) { i = (int)nAtomNumber[rank-1]; /* current atom */ #if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) r0_at_type = (AT_NUMB)rank; /* current Rank */ #else #if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) r0_at_type = (AT_NUMB)at[i].init_rank; /* chemical + neighborhood ID */ #else #if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) #else #error Undefined or wrong definition of CT_ATOMID #endif #endif #endif /* add atom to the CT */ #if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) return CT_OVERFLOW; /* */ COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); #endif /******************************************************* add neighbors and (if required) bonds to CT ********************************************************/ /* sort neighbors */ num_neigh = at[i].valence; for ( k = 0; k < num_neigh; k ++) { nNeighborNumber[k] = (AT_NUMB)k; } pNeighborsForSort = at[i].neighbor; pn_RankForSort = nRank; insertions_sort( nNeighborNumber, (size_t)num_neigh, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); for ( k = 0; k < num_neigh; k ++) { /* rank = (new current atom Rank) */ if ( (int)(r_neigh = (AT_NUMB)nRank[(int)at[i].neighbor[(int)nNeighborNumber[k]]]) CT_NEIGH_SMALLER_THAN rank ) { if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) return CT_OVERFLOW; /* */ COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); } } /* add CT row delimiter */ } /* end of cycle over all atoms. */ nCTLenAtOnly = nCTLen; /************************************************************** Tautomeric groups 07-22-2002 ***************************************************************/ for ( rank = num_atoms + 1; rank <= num_at_tg; rank ++ ) { j = (int)nAtomNumber[rank-1]; /* current "atom" */ i = j - num_atoms; /* current t-group */ #if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) r0_at_type = (AT_NUMB)rank; /* current Rank */ #else #if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) r0_at_type = (AT_NUMB)rank; /* current Rank or (AT_NUMB)at[i].init_rank; ==> chemical + neighborhood ID */ #else #if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) #else #error Undefined or wrong definition of CT_ATOMID #endif #endif #endif /* add atom to the CT */ #if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) return CT_OVERFLOW; /* */ COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); #endif /******************************************************* add neighbors and (if required) bonds to CT ********************************************************/ /* sort endpoints */ nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)t_group[i].nFirstEndpointAtNoPos; pn_RankForSort = nRank; num_neigh = (int)t_group[i].nNumEndpoints; insertions_sort( nEndpointAtomNumber, (size_t)num_neigh, sizeof(nEndpointAtomNumber[0]), CompRank); for ( k = 0; k < num_neigh; k ++) { /* rank = (new current atom Rank) */ if ( (int)(r_neigh = (AT_NUMB)nRank[(int)nEndpointAtomNumber[k]]) CT_NEIGH_SMALLER_THAN rank ) { if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) return CT_OVERFLOW; /* */ COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); } } } /* end of cycle over all tautomeric groups. */ /* compare bonds types */ /* compare elements */ if ( LinearCT ) { if ( pCS->nLenLinearCT ) { if ( pCS->nLenLinearCT != nCTLen ) return CT_LEN_MISMATCH; /* */ } else { pCS->nLenLinearCT = nCTLen; } if ( pCS->nLenLinearCT ) { if ( pCS->nLenLinearCTAtOnly != nCTLenAtOnly ) return CT_LEN_MISMATCH; /* */ } else { pCS->nLenLinearCTAtOnly = nCTLenAtOnly; } } /* Return: 0=> identical CT; -1=> new CT is smaller than the previous one */ return (bCompare-1); } /*********************************************************************************************/ /* if (*bChanged & 1) then nSymmRank has been rearranged because for some r min{i: r=nSymmRank[nAtomNumber[i]]}+1 != r if (*bChanged & 2) then ranks nTempRank[] from nSymmRank[] differ from input nCurrRank[] on exit: nSymmRank[] have been updated if (*bChanged & 1) nCurrRank[] have been updated if (*bChanged & 1) nTempRank[] is always same as nCurrRank[] nAtomNumber[] have been sorted so that (i < j) <=> (nSymmRank[nAtomNumber[i]] <= nSymmRank[nAtomNumber[j]]) */ int FixCanonEquivalenceInfo( int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged) { int nNumDiffRanks, bChangeSymmRank, bChangeCurrRank=0; /* sort equivalence information */ /* int i; for ( i = 0; i < num_at_tg; i ++ ) { nAtomNumber[i] = i; } */ pn_RankForSort = nSymmRank; /* minimal class representatives: min ranks for equiv. atoms */ qsort( nAtomNumber, num_at_tg, sizeof(nAtomNumber[0]), CompRanksOrd ); /* convert equivalence information nSymmRank[] into ranks array nTempRank[] */ /* eq. info contains min. possible ranks for eq. atoms; nCurrRank contains max. possible ranks */ nNumDiffRanks = SortedEquInfoToRanks( nSymmRank/*inp*/, nTempRank/*out*/, nAtomNumber, num_at_tg, &bChangeSymmRank ); /* check whether nCurrRank is same as new initial ranks calculated from nSymmRank[] */ bChangeCurrRank = memcmp( nCurrRank, nTempRank, num_at_tg*sizeof(nTempRank[0])); /*----------------------------------------------------------------------- if ( bChangeSymmRank || bChangeCurrRank ) { This is the case when the initial equitable partitioning does not produce constitutionally equivalent classes of atoms. Rebuild nSymmRank[] according to the new nCurrRank[] := nTempRank[] For such structures the found canonical numbers of the constitutionally equivalent atoms are not contiguous (see nCanonRank and nSymmRank examples below). Here arrays nCurrRank, nAtomNumber, and nSymmRank are changed so that later the contiguous canonical numbers for equivalent atoms can be obtained (see GetCanonRanking under "III. Get final canonical numbering (no stereo, no isotopic)". Example: for CAS=37520-11-9 (ID=21247: Ethane, 1,2-dicyclopropyl-), the numbers are the "final canon. numbers, nCanonRank" 1 HC 7 5 3 | \ | >CH--CH2 CH | / \ / | HC H2C--CH< | \ | 2 6 8 CH 4 the arrays (arranged according to ordering in nAtomNumberTemp) are: before SortedEquInfoToRanks after SortedRanksToEquInfo orig. atom nos.,nAtomNumberTemp: {4 5 6 7 0 1 2 3} {4 5 6 7 0 1 2 3} order numbers for sorted ranks: {0 1 2 3 4 5 6 7} {0 1 2 3 4 5 6 7} canonical numbering, nCanonRank: {1 2 5 6 3 4 7 8} {1 2 5 6 3 4 7 8} constit. equivalence, nSymmRank: {1 1 1 1 3 3 7 7} {1 1 1 1 5 5 7 7} used later initial equivalence, nCurrRank: {6 6 6 6 6 6 8 8} {4 4 4 4 6 6 8 8} used later initial numbering, nAtomNumber: {2 3 4 7 0 1 6 7} {0 1 2 3 4 5 6 7} used later final, no stereo, no isotopic, after III. GetCanonRanking: final canon. numbers, nCanonRank: {1 2 3 4 5 6 7 8} final } ----------------------------------------------------------------------------------*/ if ( bChangeCurrRank ) { memcpy( nCurrRank, nTempRank, num_at_tg*sizeof(nCurrRank[0]) ); } if ( bChangeSymmRank ) { SortedRanksToEquInfo( nSymmRank/*out*/, nTempRank/*inp*/, nAtomNumber, num_at_tg ); } if ( bChanged ) { *bChanged = (0 != bChangeSymmRank) | 2*(0 != bChangeCurrRank); } return nNumDiffRanks; } /* isotopic canonicalization */ /*********************************************************************** * * Canon_INChI (former GetCanonRankingUsingEquivInfo) * */ int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) { /**************************************************************** 0. Initiation, Prepare initial ranks for GetCanonRanking() I. Find constitutionally equivalent atoms and possibly canonical numbering I.1 Set tautomer=On, stereo=isotopic=Off I.2 GetCanonRanking(): Find constitutionally equivalent atoms and possibly canonical numbering 1.3 Fix canonical equivalence info if needed (if the fix is needed then the numbering is not canonical) II. Get final non-isotopic canonical numbering. Simultaneously obtain non-minimal isotopic and stereo CTs GetCanonRanking() with pCS->bKeepSymmRank = 1 FillOutStereoParities() (create initial stereo descriptors) save non-isotopic canonicalization final results hide isotopic and tautomeric results (for historical reasons only) III. Find constitutionally equivalent isotopic atoms (for isotopic stereo canonicalization) III.1 Allocate more memory III.2 fill allocated memory with the initial data III.3 duplicate, save old and add isotopic info to the new pCS->t_group_info III.4 Prepare initial isotopic ranks for GetCanonRanking() III.5 GetCanonRanking() to Find constitutionally equivalent ISOTOPIC atoms and tautomer groups III.6 Fix canonical isotopic equivalence information and derive ranks out of it IV. Prepare a second Rank/AtomNumber Stack for mapping. V. Optimize isotopic part (optimized) map_isotopic_atoms2() save isotopic canonical numbering VI. Optimize stereo descriptors (optimized) map_stereo_bonds4() VII. Optimize isotopic stereo descriptors (optimized) SwitchAtomStereoAndIsotopicStereo() SetCtToIsotopicStereo() FillOutStereoParities() SetUseAtomForStereo() map_stereo_bonds4() SwitchAtomStereoAndIsotopicStereo() SetCtToNonIsotopicStereo() *****************************************************************/ int nRet = 0, i, n; /******************************************************** input non-stereo canonical info ********************************************************/ BCN *pBCN = pCS->pBCN; FTCN *ftcn = pBCN->ftcn + bTautFtcn; /******************************************************** set mode flags ********************************************************/ /* tautomeric structure */ int bTaut = (num_at_tg > num_atoms) && pCS->t_group_info && pCS->t_group_info->num_t_groups && pCS->t_group_info->t_group; /* special case: induced by exchangable isotopic H inequivalence of atoms in formally non-tautomeric structure */ int bIsoXchgH = pCS->t_group_info && pCS->t_group_info->nNumIsotopicEndpoints > 1 && pCS->t_group_info->nIsotopicEndpointAtomNumber && pCS->t_group_info->nIsotopicEndpointAtomNumber[0] && (pCS->t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) /* && (ftcn->nCanonFlags & CANON_FLAG_ISO_TAUT_DIFF)*/; int bHasIsotopicCanonData = (ftcn->PartitionCtIso.AtNumber && ftcn->PartitionCtIso.Rank && ftcn->nSymmRankCtIso); /* bHasIsotopicCanonData==0 means * (1) No isotopic atoms in the component OR * (2) the component has only exchangable isotopic H that do not change canonical numbering and equivalence. */ T_GROUP_INFO *t_group_info1 = bTaut? pCS->t_group_info : NULL; /*int bIsoXchgH = t_group_info1 && t_group_info1->nNumIsotopicEndpoints && t_group_info1->nIsotopicEndpointAtomNumber;*/ /* isotopic canonicalization */ int bCanonIsotopic = bHasIsotopicCanonData && ( nMode & CMODE_ISO ) && ( pCS->LinearCTIsotopic || pCS->LinearCTIsotopicTautomer || bIsoXchgH ); /* stereo canonicalization */ int bCanonStereo = ( nMode & CMODE_STEREO ) && ( pCS->LinearCTStereoDble || pCS->LinearCTStereoCarb ); /* stereo isotopic canonicalization */ int bCanonIsoStereo = bHasIsotopicCanonData && ( nMode & CMODE_ISO_STEREO ) && (pCS->LinearCTIsotopicStereoDble || pCS->LinearCTIsotopicStereoCarb) && bCanonIsotopic; int bIsoTaut = (bTaut && bCanonIsotopic); int bIgnoreIsotopicInputGroups; int bIgnoreIsotopicInputAtoms; AT_RANK **pRankStack1 = pBCN->pRankStack; int nRankStackLen = pBCN->nMaxLenRankStack; int num_max = pBCN->num_max; /* allocation lengths in *pRankStack1[] */ NEIGH_LIST *NeighList = ftcn->NeighList; int nNumCurrRanks = 0; AT_RANK *nTempRank = NULL; AT_RANK *nSymmRank = NULL; AT_RANK *nAtomNumber = NULL; AT_RANK *nRank = NULL; AT_RANK **pRankStack2 = NULL; AT_RANK *nCanonRankStereo = NULL; AT_RANK *nCanonRankStereoInv = NULL; AT_RANK *nSymmStereo = NULL; AT_RANK *nCanonRankIsotopicStereo = NULL; AT_RANK *nCanonRankIsotopicStereoInv = NULL; CUR_TREE *cur_tree = NULL; CUR_TREE CurrentTree; /*AT_ISO_TGROUP *LinearCTIsotopicTautomer = NULL; */ CANON_STAT CS2; CANON_STAT* pCS2 = &CS2; inchiTime ulStartTime, ulEndTime; /*=========== Mode Bits (low 8 bits, bit 0 is Least Significant Bit) =========== Mode Bits Description '0' c 0 Only one connection table canonicalization '1' C 1 Recalculate CT using fixed nSymmRank '2' i 1|2 Isotopic canonicalization (internal) '3' I 1|2|4 Isotopic canonicalization (output) '4' s 1|8 Stereo canonicalization '5' S 1|2|4|16 Stereo isotopic canonicalization '6' A 1|2|4|8|16 Output All --- high 8 bits ---- --- obsolete, only historical interest. ------ 1-2 : 0 => at[i].init_rank from Morgan+NeighList 1 => at[i].init_rank from Atom Invariants 2 => at[i].init_rank from nSymmRank[] (at[i].init_rank is included in LinearCT depending on CT_ATOMID definition) 3 : 1 => Get Stereo canonical info 4 : 1 => Get Isotopic canonical info 5 : 1 => Get Charge/Radical canonical info ==================================================================*/ /*int nOutputMode = 0;*/ /* obsolete */ int bSwitchedAtomToIsotopic = 0; /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ int vABParityUnknown = AB_PARITY_UNDF; if ( 0 != ( nMode & REQ_MODE_DIFF_UU_STEREO) ) { /* Make labels for unknown and undefined stereo different */ vABParityUnknown = AB_PARITY_UNKN; } InchiTimeGet( &ulStartTime ); *pCS2 = *pCS; /* save input information and pointers to allocated memory */ /* set "ignore isotopic differences in tautomer groups" true */ if ( bTaut ) { /* save request for isotopic tautomeric groups */ bIgnoreIsotopicInputGroups = pCS->t_group_info->bIgnoreIsotopic; pCS->t_group_info->bIgnoreIsotopic = 1; } else { bIgnoreIsotopicInputGroups = 1; } /* save request for isotopic name */ bIgnoreIsotopicInputAtoms = pCS->bIgnoreIsotopic; /* set "ignore isotopic differences in atoms" true */ pCS->bIgnoreIsotopic = 1; /* save non-isotopic and isotopic canonicalization results */ pCS->nCanonFlags = ftcn->nCanonFlags; /* 1. non-isotopic */ /* linear CT, H */ memcpy( pCS->LinearCT, ftcn->LinearCt, ftcn->nLenLinearCt * sizeof(pCS->LinearCT[0]) ); if ( pCS->nNum_H && ftcn->nNumH ) { for ( i = 0; i < num_atoms; i ++ ) { pCS->nNum_H[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumH[i]); } } if ( pCS->nNum_H_fixed && ftcn->nNumHFixH ) { for ( i = 0; i < num_atoms; i ++ ) { pCS->nNum_H_fixed[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumHFixH[i]); } } pCS->nLenLinearCT = ftcn->nLenLinearCt; pCS->nLenLinearCTAtOnly = ftcn->nLenLinearCtAtOnly; /* save non-isotopic atoms equivalence and numbering */ if ( pCS->nSymmRank ) { memcpy( pCS->nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(pCS->nSymmRank[0]) ); } if ( pCS->nCanonOrd ) { memcpy( pCS->nCanonOrd, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(pCS->nCanonOrd[0]) ); pCS->nLenCanonOrd = num_atoms; } if ( ftcn->iso_exchg_atnos && pCS->nExchgIsoH ) { for ( i = 0; i < num_atoms; i ++ ) { pCS->nExchgIsoH[i] = !ftcn->iso_exchg_atnos[i]; /* (pCS->nExchgIsoH[i]==1) => tautomeric or hetero atoms that may exchange isotopic H */ } } /* 2. isotopic */ if ( bCanonIsotopic ) { /* linear CT, num_H are same as non-isotopic */ /* save atoms equivalence and numbering */ if ( pCS->nSymmRankIsotopic ) { memcpy( pCS->nSymmRankIsotopic, ftcn->nSymmRankCtIso, num_at_tg * sizeof(pCS->nSymmRankIsotopic[0])); } if ( pCS->nCanonOrdIsotopic ) { memcpy( pCS->nCanonOrdIsotopic, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(pCS->nCanonOrdIsotopic[0]) ); pCS->nLenCanonOrdIsotopic = num_at_tg; } nRet = FillIsotopicAtLinearCT( num_atoms, at, ftcn->PartitionCtIso.AtNumber, pCS->LinearCTIsotopic, pCS->nMaxLenLinearCTIsotopic, &pCS->nLenLinearCTIsotopic ); if ( RETURNED_ERROR(nRet) ) { goto exit_function; } if ( nRet < 0 ) { nRet = CT_TAUCOUNT_ERR; goto exit_function; } } else { pCS->nMaxLenLinearCTIsotopic = 0; pCS->nMaxLenLinearCTIsotopicTautomer = 0; } /* fill out tautomeric groups, isotopic and non-isotopic tautomeric CT and t_group_info1->tGroupNumber */ if ( bTaut ) { bIsoTaut = bIsoTaut && ftcn->PartitionCtIso.Rank && ftcn->PartitionCtIso.AtNumber && ftcn->nSymmRankCtIso; nRet = FillTautLinearCT2( num_atoms, num_at_tg, bIsoTaut, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, ftcn->nSymmRankCt, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, ftcn->nSymmRankCtIso, pCS->LinearCTTautomer, pCS->nMaxLenLinearCTTautomer, &pCS->nLenLinearCTTautomer, pCS->LinearCTIsotopicTautomer, pCS->nMaxLenLinearCTIsotopicTautomer, &pCS->nLenLinearCTIsotopicTautomer, t_group_info1 ); if ( RETURNED_ERROR(nRet) ) { goto exit_function; } if ( nRet <= 0 ) { nRet = CT_TAUCOUNT_ERR; goto exit_function; } else { /* tautomeric groups: save non-isotopic symmetry & t_group order */ int num_t_groups = t_group_info1->num_t_groups; AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; if ( pCS->nSymmRankTaut ) { memcpy( pCS->nSymmRankTaut, tSymmRank, num_t_groups * sizeof(pCS->nSymmRank[0]) ); /* fixed 5-23-02 */ } if ( pCS->nCanonOrdTaut ) { memcpy( pCS->nCanonOrdTaut, tGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdTaut[0]) ); pCS->nLenCanonOrdTaut = num_t_groups; } if ( bCanonIsotopic /*&& pCS->nLenLinearCTIsotopicTautomer*/ ) { /* tautomeric groups: save isotopic symmetry & t_group order */ /*AT_NUMB ntRankOffset = (AT_RANK)num_atoms;*/ AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; if ( pCS->nSymmRankIsotopicTaut ) { memcpy( pCS->nSymmRankIsotopicTaut, tiSymmRank, num_t_groups * sizeof(pCS->nSymmRankIsotopicTaut[0]) ); } memcpy( pCS->nCanonOrdIsotopicTaut, tiGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdIsotopicTaut[0]) ); pCS->nLenCanonOrdIsotopicTaut = num_t_groups; } } } /* save connection table if requested */ if ( pCS->LinearCT2 ) { memcpy( pCS->LinearCT2, pCS->LinearCT, sizeof(pCS->LinearCT2[0])*pCS->nLenLinearCT ); pCS->nLenLinearCT2 = pCS->nLenLinearCT; pCS->nLenLinearCTAtOnly2 = pCS->nLenLinearCTAtOnly; } if ( num_atoms <= 1 ) { bCanonStereo = 0; /* a sinle atom + possibly terminal hydrogen atoms */ if ( num_atoms < 1 || !at[0].parity2 ) { bCanonIsoStereo = 0; /* structure; for example Cl- or CH4 */ } } if ( !bCanonStereo && !(bCanonIsotopic && bCanonIsoStereo) ) { goto exit_function; /* skip stereo canonicalization */ } /********************************************************** Mode ***********************************************************/ nMode = nMode & CANON_MODE_MASK; /* memory allocation */ nAtomNumber = (AT_RANK *)qmalloc(num_max*sizeof(*nAtomNumber)); nRank = (AT_RANK *)qmalloc(num_max*sizeof(*nRank)); nTempRank = (AT_RANK *)qmalloc(num_max*sizeof(*nTempRank)); nSymmRank = (AT_RANK *)qmalloc(num_max*sizeof(*nSymmRank)); /*********************************************** 0.1 Initialization ************************************************/ if ( !NeighList || !nAtomNumber || !nTempRank || !nRank || !pCS->LinearCT ) { nRet = CT_OUT_OF_RAM; /* program error */ /* */ goto exit_function; } pCS->NeighList = NeighList; *pCS2 = *pCS; /* save input information and pointers to allocated memory */ if ( !(nMode & CMODE_NOEQ_STEREO) && (bCanonStereo || bCanonIsoStereo ) ) { /* will be used to discover vertex equivalences in stereo canonicalization */ memset( &CurrentTree, 0, sizeof(CurrentTree) ); cur_tree = &CurrentTree; } pCS->bCmpStereo = 0; pCS->bCmpIsotopicStereo = 0; if ( bCanonStereo || bCanonIsoStereo ) { int ii, nn; /* stereo or isotopic canonicalization: we need a second set of ranks for mapping */ /* (isotopic atoms or stereo can only increase nNumCurrRanks) */ pRankStack2 = (AT_RANK **) inchi_calloc( nRankStackLen, sizeof(AT_RANK *) ); if ( pRankStack2 ) { /* prepare for ranks reuse */ for ( nn = 2; nn < nRankStackLen && pRankStack1[nn]; nn ++ ) { pRankStack1[nn][0] = 0; /* means ranks have to be calculated */ } /* reuse memory to reduce number of allocations: */ /* move last half of pointers from pRankStack1 to pRankStack2 */ /* The first 2 elements will be assigned separately */ if ( (nn = (nn-2)/2) > 0 ) { for ( ii = 2+nn; ii < nRankStackLen && pRankStack1[ii]; ii ++ ) { pRankStack2[ii-nn] = pRankStack1[ii]; pRankStack1[ii] = NULL; } } } else { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; /* program error */ } } if ( bCanonStereo ) { /* *pCS2 = *pCS; */ /* save input information and pointers to allocated memory */ /* initial ranking for non-isotopic mapping */ memcpy( nAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); memcpy( nRank, ftcn->PartitionCt.Rank, num_at_tg * sizeof(nRank[0]) ); memcpy( nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(nSymmRank[0]) ); /* nSymmRank changes if canonical numbers of constitutionally equivalent atoms are not contiguous */ nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); /* atom numbers in canonical order */ memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); /* fill stereo part of the connection table with initial (not optimized) parities */ /* input pCS->LinearCTStereoDble pCS->LinearCTStereoCarb pCS->nMaxLenLinearCTStereoCarb pCS->nMaxLenLinearCTStereoDble */ nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, nRank, nAtomNumber, pCS, 0 /* bIsotopic */ ); /* output pCS->LinearCTStereoDble pCS->LinearCTStereoCarb pCS2->nLenLinearCTStereoCarb pCS2->nLenLinearCTStereoDble */ if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } if ( nRet < 0 ) { nRet = CT_STEREOCOUNT_ERR; goto exit_function; } /*************************************************************** * * VI. Optimize non-isotopic stereo descriptors (optimized) * ***************************************************************/ /* allocate memory for stereo canonicalization */ if ( !nCanonRankStereo ) nCanonRankStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereo)); if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); if ( !(nMode & CMODE_NOEQ_STEREO) && 0 > CurTreeAlloc( cur_tree, num_at_tg ) ) { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /* check allocations and assign first 2 elements of pRankStack2 */ if ( pRankStack1 && pRankStack2 && nCanonRankStereo && /* nCurrRankStereo && nAtomNumberCurrStereo &&*/ (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { pRankStack1[0] = pRankStack2[0] = nRank; pRankStack1[1] = pRankStack2[1] = nAtomNumber; } else { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /**************************************************************** * * VI-A. Optimize non-isotopic non-inverted stereo descriptors * ****************************************************************/ /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ } /* set the 1st ranks to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ } /* for debugging or statistics */ pCS->lNumBreakTies = pCS->lNumNeighListIter= pCS->lNumTotCT = pCS->lNumDecreasedCT = pCS->lNumRejectedCT = pCS->lNumEqualCT = 0; pCS->bKeepSymmRank = 0; pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ /****************************************************************************** nCanonRank contains input canonical numbering nCanonRankStereo will be filled with a transposition of canonical numbering which (1) keeps connection table unchanged and (2) provides minimal stereo descriptors in pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) */ nRet = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, nCanonRankStereo, nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, NeighList, pCS, cur_tree, 0 /* nNumMappedBonds */, vABParityUnknown); if ( RETURNED_ERROR( nRet ) ) { if ( nRet == CT_TIMEOUT_ERR ) goto exit_function; else goto exit_function; /* program error */ } else { int bFailed = 0; if ( !nRet ) { bFailed = 1; /* progrm error */ pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); nRet = CT_STEREOCOUNT_ERR; /* */ goto exit_function; /* program error */ } else { /* save non-isotopic lengths */ pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble; pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb; nRet = 0; } /* save stereo canonical numbering */ if ( pCS->nCanonOrdStereo ) { for ( i = n = 0; i < num_at_tg; i ++ ) { if ( nCanonRankStereo[i] && (int)nCanonRankStereo[i] <= num_at_tg ) { pCS->nCanonOrdStereo[ (int)nCanonRankStereo[i] - 1 ] = (AT_NUMB)i; } else { bFailed ++; } } pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; } /* save stereo tautomer groups numbering */ if ( bTaut && pCS->nCanonOrdStereoTaut ) { if ( 0 < (nRet = SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankStereo ) ) ) { /*non-isotopic contains symmetry ranks */ int num_t_groups = t_group_info1->num_t_groups; AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ memcpy( pCS->nCanonOrdStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdStereoTaut[0]) ); pCS->nLenCanonOrdStereoTaut = ( bFailed ) ? -num_t_groups : num_t_groups; } else if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } else { nRet = 0; } /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ } } /**************************************************** * * VI-B. Optimize INVERTED stereo descriptors * ****************************************************/ if ( !nCanonRankStereoInv ) nCanonRankStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereoInv)); if ( !nCanonRankStereoInv ) { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /* copy previous non-isotopic stereo canonicalization results to Inv initial data */ /* assign pointers */ pCS->LinearCTStereoDble = pCS2->LinearCTStereoDbleInv; pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarbInv; /* copy the lengths */ pCS2->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; pCS2->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { /* copy previous results, the canonical stereo CT */ memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); } memcpy( nCanonRankStereoInv, nCanonRankStereo, num_max * sizeof(nCanonRankStereoInv[0]) ); if ( pCS->nCanonOrdStereoInv && pCS->nCanonOrdStereo ) { /* in case there is nothing to invert */ memcpy( pCS->nCanonOrdStereoInv, pCS->nCanonOrdStereo, num_at_tg*sizeof(pCS->nCanonOrdStereoInv[0])); } /****************************** * * Invert stereo * ******************************/ /********************************************************************************* * Create initial approximation for the minimization of the stereo descriptors: * invert stereogenic atom parities, one parity in each allene, all parities in * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble */ nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 1 /* bInvertLinearCTStereo */ ); if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } else if ( nRet > 0 ) { /* InvertStereo() has done some changes */ nRet = 0; /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ } /* set the 1st ranks to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ } /* for debugging or statistics */ pCS->lNumBreakTies = pCS->lNumNeighListIter= pCS->lNumTotCT = pCS->lNumDecreasedCT = pCS->lNumRejectedCT = pCS->lNumEqualCT = 0; pCS->bKeepSymmRank = 0; pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ /****************************************************************************** ftcn->PartitionCt.Rank contains input canonical numbering nCanonRankStereoInv will be filled with a transposition of canonical numbering which (1) keeps connection table unchanged and (2) provides minimal stereo descriptors in pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) ******************************************************************************/ nRet = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, nCanonRankStereoInv, nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, NeighList, pCS, cur_tree, 0, vABParityUnknown); if ( RETURNED_ERROR( nRet ) ) { if ( nRet == CT_TIMEOUT_ERR ) goto exit_function; else goto exit_function; /* program error */ } else { int bFailed = 0; if ( !nRet ) { bFailed = 1; /* progrm error */ pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); nRet = CT_STEREOCOUNT_ERR; /* */ goto exit_function; /* program error */ } /* save non-isotopic pointers & lengths for INVERTED stereo */ pCS->nLenLinearCTStereoDbleInv = pCS2->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble; pCS->nLenLinearCTStereoCarbInv = pCS2->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb; /* restore pointers and lengths to non-inverted stereo */ /* -- this is needed for InvertStereo() back, see below */ pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; /* consistency check */ if ( pCS->nLenLinearCTStereoDbleInv != pCS->nLenLinearCTStereoDble || pCS->nLenLinearCTStereoCarbInv != pCS->nLenLinearCTStereoCarb ) { nRet = CT_CALC_STEREO_ERR; goto exit_function; /* program error */ } /****************************** * * Invert stereo back * ****************************** * (make sure that pointers * pCS->LinearCTStereoCarb, * pCS->LinearCTStereoDble * and corresponding lengths * have been restored) ******************************/ /********************************************************************************* * invert only stereogenic atom parities and one parity in each allene, DO NOT * change parities in pCS->LinearCTStereoCarb and pCS->nLenLinearCTStereoDble */ nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 0 ); if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } nRet = 0; /* save stereo canonical numbering */ if ( pCS->nCanonOrdStereoInv ) { for ( i = n = 0; i < num_at_tg; i ++ ) { if ( nCanonRankStereoInv[i] && (int)nCanonRankStereoInv[i] <= num_at_tg ) { pCS->nCanonOrdStereoInv[ (int)nCanonRankStereoInv[i] - 1 ] = (AT_NUMB)i; } else { bFailed ++; } } pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; } /* compare inverted and non-inverted stereo */ pCS->bCmpStereo = 2 + CompareLinCtStereo( pCS->LinearCTStereoDbleInv, pCS->nLenLinearCTStereoDbleInv, pCS->LinearCTStereoCarbInv, pCS->nLenLinearCTStereoCarbInv, pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble, pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb ); } } else if ( 0 == nRet ) { /* nothing has been done, restore pointers and lengths for stereo */ pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; } } /* restore "ignore isotopic differences in tautomer groups" */ if ( bTaut ) { /* save request for isotopic tautomeric groups */ pCS->t_group_info->bIgnoreIsotopic = bIgnoreIsotopicInputGroups; } /* restore request for isotopic name */ pCS->bIgnoreIsotopic = bIgnoreIsotopicInputAtoms; if ( bCanonIsoStereo && bCanonIsotopic ) { /**************************************************************** * * VII. Optimize isotopic stereo descriptors (optimized) * ****************************************************************/ /* pCS->LinearCTIsotopic = NULL; */ /* initial ranking for isotopic mapping */ memcpy( nAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); memcpy( nRank, ftcn->PartitionCtIso.Rank, num_at_tg * sizeof(nRank[0]) ); memcpy( nSymmRank, ftcn->nSymmRankCtIso, num_at_tg * sizeof(nSymmRank[0]) ); /* nSymmRank will change if canonical numbers of of constitutionally equivalent atoms are not contiguous */ nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); /* allocate memory for optimized stereo canonicalization */ /* for stereo canonical numbering to be found. */ if ( !nCanonRankIsotopicStereo ) nCanonRankIsotopicStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereo)); if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); if ( !(nMode & CMODE_NOEQ_STEREO) && CurTreeAlloc( cur_tree, num_at_tg ) ) { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /* check allocations and assign first 2 elements of pRankStack2 */ if ( pRankStack1 && pRankStack2 && nCanonRankIsotopicStereo && (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { pRankStack1[0] = pRankStack2[0] = nRank; /* pRankStack1[0,1] shall be unchanged */ pRankStack1[1] = pRankStack2[1] = nAtomNumber; } else { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /****************************************************************** Important: fill out a list of stereo atoms and bonds including those which are stereo due to isotopic atoms only and create LinearCT stereo descriptors for the canonical numbering ******************************************************************/ /* at[] has certain members for non-isotopic and isotopic stereo; switch them */ SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); /* prepare stereo connection tables' pointers */ SetCtToIsotopicStereo( pCS, pCS2 ); nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, nRank, nAtomNumber, pCS, 1 /* bIsotopic */); if (RETURNED_ERROR(nRet)) { goto exit_function; /* program error */ } else if ( !nRet ) { /* no isotopic stereo */ pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = 0; pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = 0; pCS->nLenCanonOrdIsotopicStereo = 0; pCS->nLenCanonOrdIsotopicStereoTaut = 0; pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTIsotopicStereoDbleInv = 0; pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTIsotopicStereoCarbInv = 0; goto bypass_isotopic_stereo; } else { nRet = 0; /* not an error */ } /************************************************************* * * VII-A. Optimize non-inverted isotopic stereo descriptors * *************************************************************/ /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ } /* set the 1st ranks to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ } /* for debugging or statistics */ pCS->lNumBreakTies = pCS->lNumNeighListIter= pCS->lNumTotCT = pCS->lNumDecreasedCT = pCS->lNumRejectedCT = pCS->lNumEqualCT = 0; pCS->bKeepSymmRank = 0; pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ /************************************************************************************** nCanonRankIsotopic contains input canonical numbering nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering that (1) keeps connection table unchanged and (2) provides minimal stereo descriptors in pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) ***************************************************************************************/ nRet = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, nCanonRankIsotopicStereo, nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, NeighList, pCS, cur_tree, 0, vABParityUnknown); if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } else { int bFailed = 0; if ( !nRet ) { bFailed = 1; /* program error */ pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); nRet = CT_STEREOCOUNT_ERR; /* */ goto exit_function; /* program error */ } else { /* save isotopic lengths */ pCS->nLenLinearCTIsotopicStereoDble = pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTStereoDble; pCS->nLenLinearCTIsotopicStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTStereoCarb; /* save stereo canonical numbering */ if ( pCS->nCanonOrdIsotopicStereo ) { for ( i = n = 0; i < num_at_tg; i ++ ) { if ( nCanonRankIsotopicStereo[i] && (int)nCanonRankIsotopicStereo[i] <= num_at_tg ) { pCS->nCanonOrdIsotopicStereo[ (int)nCanonRankIsotopicStereo[i] - 1 ] = (AT_NUMB)i; } else { bFailed ++; } } pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; } /* save stereo tautomer groups numbering */ if ( pCS->nCanonOrdIsotopicStereoTaut ) { if ( 0 < (nRet=SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankIsotopicStereo ) ) ) { /*non-isotopic contains symmetry ranks */ int num_t_groups = t_group_info1->num_t_groups; AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ memcpy( pCS->nCanonOrdIsotopicStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdIsotopicStereoTaut[0]) ); pCS->nLenCanonOrdIsotopicStereoTaut = bFailed? -num_t_groups:num_t_groups; /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ } else if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } else { nRet = 0; } } } } /********************************************************** * * VII-B. Optimize INVERTED isotopic stereo descriptors * **********************************************************/ if ( !nCanonRankIsotopicStereoInv ) nCanonRankIsotopicStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereoInv)); if ( !nCanonRankIsotopicStereoInv ) { nRet = CT_OUT_OF_RAM; /* */ goto exit_function; } /* copy previous isotopic stereo canonicalization results to Inv initial data */ /* assign pointers */ pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDbleInv; /* enable stereo */ pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarbInv; /* copy the lengths */ pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { /* copy previous results, the canonical stereo CT */ memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTIsotopicStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); } memcpy( nCanonRankIsotopicStereoInv, nCanonRankIsotopicStereo, num_max * sizeof(nCanonRankIsotopicStereoInv[0]) ); if ( pCS->nCanonOrdIsotopicStereoInv && pCS->nCanonOrdIsotopicStereo ) { /* in case there is nothing to invert */ memcpy( pCS->nCanonOrdIsotopicStereoInv, pCS->nCanonOrdIsotopicStereo, num_at_tg*sizeof(pCS->nCanonOrdIsotopicStereoInv[0])); } /****************************** * * Invert isotopic stereo * ******************************/ /********************************************************************************* * Create initial approximation for the minimization of the stereo descriptors: * invert stereogenic atom parities, one parity in each allene, all parities in * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble */ nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 1 ); if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } else if ( nRet > 0 ) { /* InvertStereo() has done some changes */ nRet = 0; /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ } /* set the 1st ranks to zero: prepare for ranks reuse */ for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ } /* for debugging or statistics */ pCS->lNumBreakTies = pCS->lNumNeighListIter= pCS->lNumTotCT = pCS->lNumDecreasedCT = pCS->lNumRejectedCT = pCS->lNumEqualCT = 0; pCS->bKeepSymmRank = 0; pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ /************************************************************************************** nCanonRankIsotopic contains input canonical numbering nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering that (1) keeps connection table unchanged and (2) provides minimal stereo descriptors in pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) */ nRet = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, nCanonRankIsotopicStereoInv, nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, NeighList, pCS, cur_tree, 0, vABParityUnknown); if ( RETURNED_ERROR( nRet ) ) { if ( nRet == CT_TIMEOUT_ERR ) goto exit_function; else goto exit_function; /* program error */ } else { int bFailed = 0; if ( !nRet ) { bFailed = 1; /* program error */ pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); nRet = CT_STEREOCOUNT_ERR; /* */ goto exit_function; /* program error */ } /* save isotopic pointers & lengths for INVERTED stereo */ /* save isotopic lengths */ pCS->nLenLinearCTIsotopicStereoDbleInv = pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTStereoDble; pCS->nLenLinearCTIsotopicStereoCarbInv = pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTStereoCarb; /* restore pointers and lengths to non-inverted isotopic stereo */ /* -- this is needed for InvertStereo() back, see below */ pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; /* consistency check */ if ( pCS->nLenLinearCTIsotopicStereoDbleInv != pCS->nLenLinearCTIsotopicStereoDble || pCS->nLenLinearCTIsotopicStereoCarbInv != pCS->nLenLinearCTIsotopicStereoCarb ) { nRet = CT_CALC_STEREO_ERR; goto exit_function; /* program error */ } /****************************** * * Invert stereo back * ****************************** * (make sure that pointers * pCS->LinearCTStereoCarb, * pCS->LinearCTStereoDble * and corresponding lengths * have been restored) ******************************/ nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 0 ); if ( RETURNED_ERROR( nRet ) ) { goto exit_function; } nRet = 0; /* save stereo canonical numbering */ if ( pCS->nCanonOrdIsotopicStereoInv ) { for ( i = n = 0; i < num_at_tg; i ++ ) { if ( nCanonRankIsotopicStereoInv[i] && (int)nCanonRankIsotopicStereoInv[i] <= num_at_tg ) { pCS->nCanonOrdIsotopicStereoInv[ (int)nCanonRankIsotopicStereoInv[i] - 1 ] = (AT_NUMB)i; } else { bFailed ++; } } pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; } /* compare inverted and non-inverted isotopic stereo */ pCS->bCmpIsotopicStereo = 2 + CompareLinCtStereo( pCS->LinearCTIsotopicStereoDbleInv, pCS->nLenLinearCTIsotopicStereoDbleInv, pCS->LinearCTIsotopicStereoCarbInv, pCS->nLenLinearCTIsotopicStereoCarbInv, pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble, pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb ); } } else if ( 0 == nRet ) { /* nothing has been done, restore pointers and lengths for stereo */ pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; } bypass_isotopic_stereo:; /* ??? */ pCS->LinearCTIsotopic = pCS2->LinearCTIsotopic; } exit_function: if ( bSwitchedAtomToIsotopic ) { SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); SetCtToNonIsotopicStereo( pCS, pCS2 ); /* ??? */ } /* restore non-isotopic connection table */ if ( pCS->LinearCT2 ) { inchi_swap( (char*)&pCS->LinearCT, (char*)&pCS->LinearCT2, sizeof(pCS->LinearCT) ); inchi_swap( (char*)&pCS->nLenLinearCT, (char*)&pCS->nLenLinearCT2, sizeof(pCS->nLenLinearCT) ); inchi_swap( (char*)&pCS->nLenLinearCTAtOnly, (char*)&pCS->nLenLinearCTAtOnly2, sizeof(pCS->nLenLinearCTAtOnly) ); } /* free memory */ i = 2; if ( pRankStack1 ) { pRankStack1[0] = pRankStack1[1] = NULL; /* deallocated separately */ for ( ; i < nRankStackLen && pRankStack1[i]; i ++ ) ; } if ( pRankStack1 && pRankStack2 ) { for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { if ( i < nRankStackLen - 1 ) { pRankStack1[i++] = pRankStack2[n]; } else { inchi_free( pRankStack2[n] ); } } inchi_free( pRankStack2 ); } pCS->NeighList = NULL; /* keep the pointer in pBCN->ftcn[bTaut].NeighList for further deallocation */ qfree ( nAtomNumber ); qfree ( nTempRank ); qfree ( nRank ); qfree ( nSymmRank ); qfree( nSymmStereo ); CurTreeFree( cur_tree ); /* memory leak fix */ /* qfree ( nCurrRankIsotopicStereo ); qfree ( nAtomNumberCurrIsotopicStereo); */ qfree ( nCanonRankIsotopicStereo ); qfree ( nCanonRankIsotopicStereoInv ); qfree( nCanonRankStereo ); qfree( nCanonRankStereoInv ); InchiTimeGet( &ulEndTime ); pCS->lTotalTime = InchiTimeMsecDiff(&ulEndTime, &ulStartTime); return (nRet >= -1)? num_atoms : nRet; /* cannot easily get number of ranks for now */ } /**************************************************************************************/ int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) { if ( pCS->pBCN && !pCS->NeighList ) { /* new version */ return Canon_INChI3( num_atoms, num_at_tg, at, pCS, nMode, bTautFtcn); } return CT_CANON_ERR; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicano.h000066400000000000000000000055371271037650300235110ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHICANO_H__ #define __INCHICANO_H__ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int GetCanonLengths(int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ); int AllocateCS(CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ); int DeAllocateCS(CANON_STAT *pCS ); void DeAllocBCN(BCN *pBCN ); int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); int GetBaseCanonRanking(int num_atoms, int num_at_tg, sp_ATOM* at[], T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ); int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ); int UpdateFullLinearCT(int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, CANON_STAT* pCS, int bFirstTime ); int FixCanonEquivalenceInfo(int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHICANO_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicans.c000066400000000000000000002433711271037650300235100ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #include "ichister.h" #include "ichicomp.h" typedef struct tagStereoBondNeighbor { /* *n = sort key */ AT_RANK nRank; /* *1 opposite atom rank; equal ranks mean constit. equivalence */ AT_RANK nNeighRank1; /* rank of the neighbor in the direction to the opposite atom */ AT_RANK nNeighRank2; /* rank of the opposite atom neighbor in the direction to the current atom */ AT_RANK num; /* number of same type bonds to constitutionally equivalent neighbors */ AT_RANK num_any_parity; /* at least one atom has parity in 1..4 range */ AT_RANK num_defined_parity; /* number of neighbors with defined parity <= num */ /* AT_RANK num_undef_parity; */ /* AT_RANK num_unkn_parity; */ AT_RANK what2do; U_CHAR cumulene_len; /* high nimble bits: (cumulene length - 1) */ U_CHAR bond_type; /* *2 all same, not a real bond type */ } STEREO_BOND_NEIGH; /* local prototypes */ int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ); int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ); int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, const AT_RANK *nAtomNumber ); int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber ); int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ); int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, const AT_RANK *nRank, const AT_RANK *nCanonRank ); AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, AT_RANK *nVisited1, AT_RANK *nVisited2, const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ); int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, CANON_STAT *pCS); int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, const AT_RANK *nAtomNumber ); int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, CANON_STAT *pCS); int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber ); /**********************************************************************************/ /* Depth First Search for an atom with parity */ int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ) { int i, next_atom; if ( visited[cur_atom] ) return 0; if ( at[cur_atom].parity ) return 1; visited[cur_atom] = 1; for ( i = 0; i < at[cur_atom].valence; i ++ ) { next_atom = at[cur_atom].neighbor[i]; if ( next_atom != from_atom && find_atoms_with_parity( at, visited, cur_atom, next_atom ) ) return 1; } return 0; } /**********************************************************************************/ int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ) { int parity; if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { parity = at[jn].stereo_bond_parity[k1] ^ PARITY_VAL(at[jn].stereo_bond_parity[k1]); at[jn].stereo_bond_parity[k1] = parity | PARITY_VAL(new_parity); at[jn].parity = PARITY_VAL(new_parity); return 1; /* success */ } return 0; /* failed */ } /**********************************************************************************/ int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ) { int k2; if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { for ( k2 = k1; k2+1 < MAX_NUM_STEREO_BOND_NEIGH; k2++ ) { at[jn].stereo_bond_neighbor[k2] = at[jn].stereo_bond_neighbor[k2+1]; at[jn].stereo_bond_ord[k2] = at[jn].stereo_bond_ord[k2+1]; at[jn].stereo_bond_z_prod[k2] = at[jn].stereo_bond_z_prod[k2+1]; at[jn].stereo_bond_parity[k2] = at[jn].stereo_bond_parity[k2+1]; } at[jn].stereo_bond_neighbor[k2] = 0; at[jn].stereo_bond_ord[k2] = 0; at[jn].stereo_bond_z_prod[k2] = 0; at[jn].stereo_bond_parity[k2] = 0; if ( !at[jn].stereo_bond_neighbor[0] ) { /* curled braces added 6-6-2002 */ at[jn].parity = 0; at[jn].stereo_atom_parity = 0; at[jn].final_parity = 0; /* at[jn].bHasStereoOrEquToStereo = 0; */ } return 1; /* success */ } return 0; /* failed */ } /**********************************************************************************/ int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ) { int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; /* opposite end */ for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { if ( kn - 1 == jc ) { ret = SetHalfStereoBondIllDefPariy( at, jn, /* atom number*/ k1 /* stereo bond number*/, new_parity ); break; } } if ( ret ) { ret = SetHalfStereoBondIllDefPariy( at, jc, k, new_parity ); } return ret; } /**********************************************************************************/ int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ) { int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; /* opposite end */ for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { if ( kn - 1 == jc ) { ret = RemoveHalfStereoBond( at, jn, k1 ); break; } } if ( ret ) { ret = RemoveHalfStereoBond( at, jc, k ); } return ret; } /**********************************************************************************/ int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ) { if ( at[jc].parity ) { at[jc].parity = 0; /* remove parity */ at[jc].stereo_atom_parity = 0; at[jc].final_parity = 0; /* at[jc].bHasStereoOrEquToStereo = 0; */ return 1; } return 0; /* failed: not a stereo center */ } /**********************************************************************************/ /* Remove stereo parity from centers having constitutionally equivalent */ /* cut-vertex neighbors whose attachments do not have stereogenic elements. */ /* Currently checks ALL constitutionally equivalent neighbors. */ /* To optimize, check only one. */ int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ) { int i, i1, i2, j, k, k1, k2, kn /* neigh*/, val, ic/* center*/, jc, num_implicit_H; int num_neighbors_with_parity, num_no_parity_atoms, num_removed_parities=-1, num_removed_parities0; AT_RANK nNeighborNumber[MAX_NUM_STEREO_ATOM_NEIGH]; AT_RANK nPrevAtomRank, nPrevNeighRank; S_CHAR *visited = (S_CHAR *) inchi_malloc(num_atoms * sizeof(visited[0])); if ( !visited ) goto exit_function; num_removed_parities = 0; num_no_parity_atoms = 0; do { num_removed_parities0 = num_removed_parities; for ( i = i1 = 0, nPrevAtomRank = 0; i <= num_atoms; i++ ) { /* bounds violation check (i!=num_atoms) added 6-21-2002 */ if ( i == num_atoms || nPrevAtomRank != nRank[j = nAtomNumber[i]] /* at[j].parity && 1 < at[j].valence && at[j].valence < MAX_NUM_STEREO_ATOM_NEIGH*/ ) { /* end of constitutionally equivalent atoms sequence */ /* nPrevRank = nRank[j]; */ i2 = i; if ( i2 - i1 > num_no_parity_atoms /*&& at[jc = nAtomNumber[i1]].parity*/ ) { /* at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] are constitutionally equivalent and some of them have parity */ jc = nAtomNumber[i1]; num_no_parity_atoms = 0; val = at[jc].valence; /* all equivalent atoms have equal valences, etc. (except parities) */ num_implicit_H = at[jc].endpoint? 0 : at[jc].num_H; /* Only atoms with valence <= MAX_NUM_STEREO_ATOM_NEIGH may have parity. However, check: */ if ( val + num_implicit_H > MAX_NUM_STEREO_ATOM_NEIGH ) { continue; /* program error ??? */ /* */ } for ( k = 0; k < val; k ++ ) { nNeighborNumber[k] = k; /* initialize an array of indexes for sorting */ } /* check parities */ for ( ic = i1; ic < i2; ic ++ ) { jc = nAtomNumber[ic]; /* sort neighbors according to their canon. equivalence ranks */ pNeighborsForSort = at[jc].neighbor; pn_RankForSort = nRank; insertions_sort( nNeighborNumber, val, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); num_neighbors_with_parity = -1; /* non-zero */ for ( k = k1 = 0, nPrevNeighRank = 0; k <= val; k ++ ) { if ( k == val || nPrevNeighRank != nRank[at[jc].neighbor[nNeighborNumber[k]]] ) { k2 = k; if ( k2 - k1 > 1 ) { /* found 2 or more constitutionally equivalent neighbors */ /* Check if they have only non-stereogenic neighbors */ for ( kn = k1, num_neighbors_with_parity = 0; kn < k2; kn ++ ) { memset( visited, 0, num_atoms * sizeof(visited[0])); visited[jc] = 1; /* starting point; the only atom with parity */ num_neighbors_with_parity += find_atoms_with_parity( at, visited, jc, (int)at[jc].neighbor[nNeighborNumber[kn]] ); } } if ( !num_neighbors_with_parity ) { break; /* at[jc] cannot have defined parity */ } if ( k + 1 < val ) { k1 = k; /* at least 2 more neighbors left */ nPrevNeighRank = nRank[at[jc].neighbor[nNeighborNumber[k]]]; } else { break; } } } if ( num_implicit_H > 1 ) { if ( bIsotopic && (at[jc].num_iso_H[0] > 1 || at[jc].num_iso_H[1] > 1 || at[jc].num_iso_H[2] > 1 ) || num_implicit_H > NUM_H_ISOTOPES || !bIsotopic ) { num_neighbors_with_parity = 0; } } /* increment if: */ /* (a) constitutionally equivalent neighbors do exist, and */ /* (b) all constitutionally equivalent neighbors do not have parity, and */ /* (c) all constitutionally equivalent neighbors are not connected to atoms with parity */ num_no_parity_atoms += !num_neighbors_with_parity; } if ( num_no_parity_atoms == i2 - i1 ) { /* all atoms at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] cannot be */ /* stereo centers or belong to stereo bonds */ for ( ic = i1; ic < i2; ic ++ ) { int jn; jc = nAtomNumber[ic]; at[jc].parity = 0; /* remove parity */ at[jc].stereo_atom_parity = 0; at[jc].final_parity = 0; at[jc].bHasStereoOrEquToStereo = 0; /* remove stereo bonds */ for ( k = 0; k < MAX_NUM_STEREO_BOND_NEIGH && (jn=at[jc].stereo_bond_neighbor[k]); k++ ) { jn--; /* stereo bond neighbor */ /* opposite end */ for ( k1 = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { if ( kn - 1 == jc ) { RemoveHalfStereoBond( at, jn, k1 ); break; } } /* at at[jc] stereo bond end; since references to all at[jc] */ /* stereo bond neighbors are to be removed, do not shift them */ at[jc].stereo_bond_neighbor[k] = 0; at[jc].stereo_bond_ord[k] = 0; at[jc].stereo_bond_z_prod[k] = 0; at[jc].stereo_bond_parity[k] = 0; } } num_removed_parities += num_no_parity_atoms; } } if ( i < num_atoms ) { nPrevAtomRank = nRank[j]; i1 = i; } num_no_parity_atoms = 0; } num_no_parity_atoms += (i < num_atoms && !at[j].parity); } } while ( num_removed_parities != num_removed_parities0 ); exit_function: if ( visited ) inchi_free( visited ); return num_removed_parities; } /********************************************************************************** * * Add stereo descriptor(s) for atom #i * **********************************************************************************/ int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen , int bAllene ) { if ( !LinearCTStereoDble && !LinearCTStereoCarb ) { return 0; /* return immediately if no stereo have been requested */ } /*************************************************** add stereo centers and stereo bonds to the CT ***************************************************/ if ( at[i].parity || at[i].stereo_bond_neighbor[0] ) { AT_RANK r_neigh, rank = nRank[i]; AT_NUMB nNeighborNumber2[MAXVAL]; unsigned parity; int k; int num_allene = 0; if ( ATOM_PARITY_WELL_DEF(at[i].parity) && num_trans < 0 ) { /* number of neighbors transpositions to the sorted order is unknown. Find it. */ /* If parity is not well-defined then doing this is a waste of time */ int num_neigh = at[i].valence; for ( k = 0; k < num_neigh; k ++) { nNeighborNumber2[k] = k; } pNeighborsForSort = at[i].neighbor; pn_RankForSort = nRank; num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); #ifndef CT_NEIGH_INCREASE num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ #endif } /* stereo bonds */ if ( LinearCTStereoDble && at[i].stereo_bond_neighbor[0] ) { /* HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, AT_RANK *nRank ) */ AT_NUMB nStereoNeighNumber[MAX_NUM_STEREO_BONDS], nStereoNeigh[MAX_NUM_STEREO_BONDS], n; int num_stereo, stereo_neigh, stereo_neigh_ord, stereo_bond_parity; for ( num_stereo = 0; num_stereo < MAX_NUM_STEREO_BONDS && (n=at[i].stereo_bond_neighbor[num_stereo]); num_stereo ++ ) { nStereoNeighNumber[num_stereo] = num_stereo; nStereoNeigh[num_stereo] = n-1; num_allene += IS_ALLENE_CHAIN(at[i].stereo_bond_parity[num_stereo]); } if ( bAllene > 0 && !num_allene || bAllene == 0 && num_allene ) { return 0; } /* sort stereo bonds according to the ranks of the neighbors */ pNeighborsForSort = nStereoNeigh; pn_RankForSort = nRank; insertions_sort( nStereoNeighNumber, num_stereo, sizeof(nStereoNeighNumber[0]), CompNeighborsAT_NUMBER ); /* process stereo bonds one by one */ for ( k = 0; k < num_stereo; k ++ ) { stereo_neigh = nStereoNeigh[stereo_neigh_ord=(int)nStereoNeighNumber[k]]; if ( (r_neigh = (AT_NUMB)nRank[stereo_neigh]) CT_NEIGH_SMALLER_THAN rank ) { /* accept only neighbors that have smaller ranks */ stereo_bond_parity = PARITY_VAL(at[i].stereo_bond_parity[stereo_neigh_ord]); if ( stereo_bond_parity == AB_PARITY_NONE ) continue; /* stereo_neigh = at[i].stereo_bond_neighbor[nStereoNeighNumber[k]]-1; */ if ( ATOM_PARITY_KNOWN(stereo_bond_parity) ) { parity = stereo_bond_parity; } else if ( ATOM_PARITY_WELL_DEF(at[i].parity) && ATOM_PARITY_WELL_DEF(at[stereo_neigh].parity) && MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[stereo_neigh_ord]) ) { /* bond parity can be calculated */ int half_parity1, half_parity2, j, nn, stereo_neigh_ord2; stereo_neigh_ord2 = -1; for ( j = 0; j < MAX_NUM_STEREO_BONDS && (nn=(int)at[stereo_neigh].stereo_bond_neighbor[j]); j++ ) { if ( i+1 == nn ) { /* found the opposite end of the stereo bond */ stereo_neigh_ord2 = j; break; } } if ( stereo_neigh_ord2 >= 0 ) { half_parity1 = HalfStereoBondParity( at, i, stereo_neigh_ord, nRank ); half_parity2 = HalfStereoBondParity( at, stereo_neigh, stereo_neigh_ord2, nRank ); if ( ATOM_PARITY_WELL_DEF(half_parity1) && ATOM_PARITY_WELL_DEF(half_parity2) ) { parity = 2 - ( half_parity1 + half_parity2 + (at[i].stereo_bond_z_prod[stereo_neigh_ord] < 0))%2; } else { return CT_STEREOBOND_ERROR; /* */ } } else { return CT_STEREOBOND_ERROR; /* */ } } else { /* parity cannot be calculated: not enough info or 'unknown' */ if ( AB_PARITY_NONE == (parity = inchi_max(at[i].parity, at[stereo_neigh].parity)) ) continue; if ( ATOM_PARITY_WELL_DEF(parity) ) parity = AB_PARITY_UNDF; /* should not happen */ } if ( CHECK_OVERFLOW(*nStereoDbleLen, nMaxStereoDbleLen) ) return CT_OVERFLOW; /* */ /* first stereo bond atom */ LinearCTStereoDble[*nStereoDbleLen].at_num1 = rank; /* second stereo bond atom (opposite end) */ LinearCTStereoDble[*nStereoDbleLen].at_num2 = r_neigh; /* bond parity */ LinearCTStereoDble[*nStereoDbleLen].parity = parity; (*nStereoDbleLen) ++; } } } /* stereo carbon */ if ( bAllene > 0 ) { return 0; } if ( LinearCTStereoCarb && !at[i].stereo_bond_neighbor[0] ) { if ( CHECK_OVERFLOW(*nStereoCarbLen, nMaxStereoCarbLen) ) return CT_OVERFLOW; /* */ /* stereo atom rank */ LinearCTStereoCarb[*nStereoCarbLen].at_num = rank; /* stereo atom parity */ parity = ATOM_PARITY_WELL_DEF(at[i].parity)? (2 - (at[i].parity + num_trans)%2) : at[i].parity; LinearCTStereoCarb[*nStereoCarbLen].parity = parity; (*nStereoCarbLen) ++; } } return 0; } /**********************************************************************************/ void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ) { int i; /* switch atom stereo data */ for ( i = 0; i < num_atoms; i ++ ) { inchi_swap( (char*)&at[i].parity, (char*)&at[i].parity2, sizeof( at[i].parity ) ); inchi_swap( (char*)&at[i].final_parity, (char*)&at[i].final_parity2, sizeof( at[i].final_parity ) ); inchi_swap( (char*)&at[i].stereo_atom_parity, (char*)&at[i].stereo_atom_parity2, sizeof( at[i].stereo_atom_parity ) ); inchi_swap( (char*)&at[i].bHasStereoOrEquToStereo, (char*)&at[i].bHasStereoOrEquToStereo2, sizeof( at[i].bHasStereoOrEquToStereo ) ); inchi_swap( (char*)at[i].stereo_bond_neighbor, (char*)at[i].stereo_bond_neighbor2, sizeof( at[i].stereo_bond_neighbor ) ); inchi_swap( (char*)at[i].stereo_bond_ord, (char*)at[i].stereo_bond_ord2, sizeof( at[i].stereo_bond_ord ) ); inchi_swap( (char*)at[i].stereo_bond_z_prod, (char*)at[i].stereo_bond_z_prod2, sizeof( at[i].stereo_bond_z_prod ) ); inchi_swap( (char*)at[i].stereo_bond_parity, (char*)at[i].stereo_bond_parity2, sizeof( at[i].stereo_bond_parity ) ); } *bSwitched = !*bSwitched; } /**********************************************************************************/ void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) { pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; /* enable stereo */ pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; pCS->LinearCTStereoDbleInv = pCS2->LinearCTIsotopicStereoDbleInv; /* enable inv. stereo */ pCS->LinearCTStereoCarbInv = pCS2->LinearCTIsotopicStereoCarbInv; pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTIsotopicStereoDble; pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTIsotopicStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; } /**********************************************************************************/ void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) { pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; /* enable stereo */ pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; pCS->LinearCTStereoDbleInv = pCS2->LinearCTStereoDbleInv; /* enable inv. stereo */ pCS->LinearCTStereoCarbInv = pCS2->LinearCTStereoCarbInv; pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTStereoDble; pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTStereoCarb; pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; pCS->nLenLinearCTIsotopicStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; pCS->nLenLinearCTIsotopicStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; } /**********************************************************************************/ int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ) { int ret=0, i; /* initialize zero lengths */ pCS->nLenLinearCTStereoCarb = 0; pCS->nLenLinearCTStereoDble = 0; /* fill atom by atom */ for ( i = 0; !ret && i < num_atoms; i ++ ) { ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble , 0 /* bAllene */ ); } for ( i = 0; !ret && i < num_atoms; i ++ ) { ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble , 1 /* bAllene */); } return ret; } /**********************************************************************************/ /* Find stereo bond parities known in advance */ int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, const AT_RANK *nAtomNumber ) { int i, j, n, m, j1, k, num_neigh1, num_neigh2, iMax1, parity; int trans_i1, trans_i2, trans_k1, trans_k2, prev_trans, trans_k, num_set; int i1, i2, k1, k2, n1, n2, m1, m2, /*stereo_bond_parity,*/ cumulene_len; AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank; AT_RANK nNeighRank1[MAX_NUM_STEREO_BONDS], nNeighRank2[MAX_NUM_STEREO_BONDS]; AT_RANK nNeighCanonRank1[MAX_NUM_STEREO_BONDS], nNeighCanonRank2[MAX_NUM_STEREO_BONDS]; for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { continue; } if ( !PARITY_WELL_DEF(at[i1].parity) ) { continue; } nAtomRank1 = nRank[i1]; iMax1 = (int)nAtomRank1-1; num_neigh1 = at[i1].valence; for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { i2 --; /* found a stereo bond at[i1]-at[i2] adjacent to at[i1] */ for ( n2 = 0, m=0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) ; /* locate stereo bond (#n2) at the opposite atom at[i2] */ if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } if ( i1 < i2 ) { continue; /* do not process same bond 2 times */ } if ( PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || !PARITY_VAL(at[i1].stereo_bond_parity[n1]) ) { continue; } if ( !PARITY_WELL_DEF(at[i1].parity) || !PARITY_WELL_DEF(at[i2].parity) ) { continue; } if ( PARITY_VAL(at[i1].stereo_bond_parity[n1]) != AB_PARITY_CALC ) { continue; /* ?? program error ?? should not happen */ /* */ } /*stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]);*/ cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); nAtomRank2 = nRank[i2]; nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; num_neigh2 = at[i2].valence; /* store ranks of at[i1] stereo bond neighbors except one connected by a stereo bond */ k = (int)at[i1].stereo_bond_ord[n1]; trans_i1 = 0; for ( i = j = 0; i < num_neigh1; i ++ ) { if ( i != k ) { nNeighRank1[j] = nRank[(int)at[i1].neighbor[i]]; j ++; } } if ( j == 2 ) { if ( nNeighRank1[0] == nNeighRank1[1] ) { /* neighbors are constitutionally identical, can't find bond parity */ continue; } trans_i1 = insertions_sort(nNeighRank1, j, sizeof(nNeighRank1[0]), comp_AT_RANK); } /* store ranks of at[i2] stereo bond neighbors except one connected by a stereo bond */ k = (int)at[i2].stereo_bond_ord[n2]; trans_i2 = 0; for ( i = j = 0; i < num_neigh2; i ++ ) { if ( i != k ) { nNeighRank2[j] = nRank[(int)at[i2].neighbor[i]]; j ++; } } if ( j == 2 ) { if ( nNeighRank2[0] == nNeighRank2[1] ) { /* neighbors are constitutionally identical, can't find bond parity */ continue; } trans_i2 = insertions_sort(nNeighRank2, j, sizeof(nNeighRank2[0]), comp_AT_RANK); } prev_trans = -1; trans_k1 = -2; trans_k = -4; /* 2004-04-28 */ /* find all pairs of atoms that can be mapped on at[i1], at[i2] pair */ for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { /* at[k1] is constitutionally equivalent to at[i1] */ /* find all at[k1] neighbors that have rank nAtomRank2; */ /* then find at[k2] constitutionally equivalent at at[i2] */ if ( at[k1].valence != num_neigh1 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { int prev, next, len; if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { continue; } m2 = -1; /* undefined yet */ prev = k1; len = 0; if ( cumulene_len ) { for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { if ( at[next].valence == 2 && !at[next].num_H ) { j = ((int)at[next].neighbor[0] == prev); prev = next; next = at[next].neighbor[j]; } else { break; /* cannot continue */ } } if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { continue; /* not found */ } k2 = next; } if ( at[k2].valence != num_neigh2 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } /* store canon. ranks of at[k1] neighbors */ /* use i,j,k,m,n */ for ( n = j = 0; n < num_neigh1; n ++ ) { if ( n != m1 ) { i=(int)at[k1].neighbor[n]; for ( m = 0; m < num_neigh1-1; m ++ ) { if ( nRank[i] == nNeighRank1[m] ) { nNeighCanonRank1[m] = nCanonRank[i]; j ++; break; } } } } if ( j != num_neigh1-1 ) { return CT_STEREOCOUNT_ERR; /* */ } if ( j == 2 ) { trans_k1 = insertions_sort(nNeighCanonRank1, j, sizeof(nNeighCanonRank1[0]), comp_AT_RANK); } else { trans_k1 = 0; } /* store canon. ranks of at[k2] neighbors */ /* use i,j,k,m,n */ for ( n = j = 0; n < num_neigh2; n ++ ) { i=(int)at[k2].neighbor[n]; if ( i == prev ) { /* neighbor belongs to the stereobond */ m2 = n; } else { for ( m = 0; m < num_neigh2-1; m ++ ) { if ( nRank[i] == nNeighRank2[m] ) { nNeighCanonRank2[m] = nCanonRank[i]; j ++; break; } } } } if ( j != num_neigh2-1 || m2 < 0 ) { return CT_STEREOCOUNT_ERR; /* */ } if ( j == 2 ) { trans_k2 = insertions_sort(nNeighCanonRank2, j, sizeof(nNeighCanonRank2[0]), comp_AT_RANK); } else { trans_k2 = 0; } trans_k = (trans_k1 + trans_k2)%2; if ( prev_trans < 0 ) { prev_trans = trans_k; } else if ( prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ break; /* different number of transpositions */ } } /* end of the second atom mapping cycle */ if ( prev_trans >= 0 && prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ break; } } /* end of the first atom mapping cycle */ if ( prev_trans == trans_k ) { /* was == trans_k1, changed 9-23-2003 */ int z_prod; /* all mappings of canonical numbers on the */ /* stereo bond at[i1]-at[i2] produce equivalent numberings. */ /* Therefore the stereo bond parity is known at this time. */ /* parity_1 = at[i1].parity + (trans_i1 + trans_k1 + num_neigh1 - 1) + (int)at[i1].stereo_bond_ord[n1] */ /* expression in parentheses is equivalent to rank[first neigh] > rank[second neigh] */ /* same for parity_2. */ /* parity_2 = at[i2].parity + (trans_i2 + trans_k2 + num_neigh2 - 1) + (int)at[i2].stereo_bond_ord[n2] */ /* Sum of the two parities (without stereo_bond_z_prod) is: */ parity = (at[i1].parity + at[i2].parity + prev_trans + trans_i1 + trans_i2 + num_neigh1 + num_neigh2 + (int)at[i1].stereo_bond_ord[n1] + (int)at[i2].stereo_bond_ord[n2] ) %2; z_prod = at[i1].stereo_bond_z_prod[n1]; if ( MIN_DOT_PROD > abs(z_prod)) { parity = AB_PARITY_UNDF; /* undefined because of geometry */ } else { parity = (z_prod > 0)? 2 - parity : 1 + parity; } at[i1].stereo_bond_parity[n1] = ALL_BUT_PARITY(at[i1].stereo_bond_parity[n1]) | parity; at[i2].stereo_bond_parity[n2] = ALL_BUT_PARITY(at[i2].stereo_bond_parity[n2]) | parity; num_set ++; } } } return num_set; } /**********************************************************************************/ /* Find stereo center parities known in advance */ int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber ) { int j, n, m, j1, num_neigh1, num_neigh2, iMax1; int num_set, /*num_sb1, num_sb2,*/ bDifferentParities; int i1, i2, k1, k2, n1, n2, m1, m2, s1, s2, stereo_bond_parity, stereo_bond_parity2, cumulene_len; AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank, nAtom2NeighRank; num_set = 0; for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { continue; } nAtomRank1 = nRank[i1]; iMax1 = (int)nAtomRank1-1; num_neigh1 = at[i1].valence; /* count stereogenic bonds adjacent to at[i1] */ for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && at[i1].stereo_bond_neighbor[n1]; n1++ ) ; /*num_sb1 = n1;*/ /* search for bonds possibly constitutionally equivalent to each of the adjacent bonds */ /* and find if all of them have same already known parity */ for ( n1 = 0, i2 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { i2 --; nAtomRank2 = nRank[i2]; if ( nAtomRank2 < nAtomRank1 || nAtomRank2 == nAtomRank1 && i1 < i2 ) { /* An attempt to reduce unnecessary repetitions. */ /* We still have repetitions because we do not accumulate a list of */ /* processed (nAtomRank2, nAtomRank1) pairs. */ continue; } bDifferentParities = -1; /* parities have not been compared yet */ /* found a stereo bond at[i1]-at[i2] (adjacent to at[i1]) */ /* if ( !PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || (at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL) ) { continue; } */ if ( at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL ) { continue; } /* stereo bond has known or unknown parity; we have not checked it yet */ for ( n2 = 0; n2 < MAX_NUM_STEREO_BONDS && at[i2].stereo_bond_neighbor[n2]; n2++ ) ; /*num_sb2 = n2;*/ for ( n2 = 0, m = 0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) ; if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { return CT_STEREOCOUNT_ERR; /* program error: stereo bonds data in two directions are different */ /* */ } stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]); cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; nAtom2NeighRank = nRank[(int)at[i2].neighbor[(int)at[i2].stereo_bond_ord[n2]]]; num_neigh2 = at[i2].valence; /* find all pairs of atoms that possibly can be mapped on at[i1], at[i2] pair */ /* (we may also find pairs that cannot be mapped, but we cannot miss any pair */ /* that can be mapped) */ for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { /* at[k1] is constitutionally equivalent to at[i1] */ /* find all at[k1] stereo bond neighbors at[k2] that have rank nAtomRank2; */ /* then find at[k2] constitutionally equivalent at at[i2] */ if ( at[k1].valence != num_neigh1 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } if ( !at[k1].bHasStereoOrEquToStereo ) { at[k1].bHasStereoOrEquToStereo = 1; } /* -- do not check number of stereo bonds, check bonds themselves -- for ( s1 = 0; s1 < MAX_NUM_STEREO_BONDS && at[k1].stereo_bond_neighbor[s1]; s1++ ) ; if ( num_sb1 != s1 ) { bDifferentParities = 1; } */ for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { /* Looking for at[k1] neighbor with nRank=nAtom1NeighRank. */ /* This neighbor may be on the bond constit. equivalent to at[i1]-at[i2] stereo bond */ /* (or may be constit. equivalent an adjacent to at[i1] atom in a stereogenic cumulene chain) */ int prev, next, len; if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { continue; } /* found at[k1] neighbor with nRank=nAtom1NeighRank */ m2 = -1; /* undefined yet */ prev = k1; len = 0; /* if cumulene then bypass the cumulene chain */ if ( cumulene_len ) { for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { if ( at[next].valence == 2 && !at[next].num_H ) { j = ((int)at[next].neighbor[0] == prev); prev = next; next = at[next].neighbor[j]; } else { break; /* cannot continue: end of cumulene chain */ } } if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { continue; /* cumulene chain not found at this neighbor */ } if ( nAtom2NeighRank != nRank[prev] ) { /* continue; */ /* ??? program error ??? If not, must be a very rare event */ return CT_STEREOCOUNT_ERR; /* */ } k2 = next; } /* a connected pair of constit. equivalent atoms found */ if ( at[k2].valence != num_neigh2 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } for ( n = 0; n < num_neigh2; n ++ ) { if ( prev == (int)at[k2].neighbor[n] ) { m2 = n; /* found bond from the opposite end of a possibly stereogenic bond */ break; } } if ( m2 < 0 ) { return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ } if ( !at[k2].bHasStereoOrEquToStereo ) { at[k2].bHasStereoOrEquToStereo = 1; } /* check if atoms at[k1] and at[k2] are connected by a stereo bond */ for ( s1 = 0, m = 0; s1 < MAX_NUM_STEREO_BONDS && (m=(int)at[k1].stereo_bond_neighbor[s1]) && m-1 != k2; s1++ ) ; if ( m-1 != k2 ) { bDifferentParities = 1; /* cannot find the stereo bond */ at[k1].bHasStereoOrEquToStereo = at[k2].bHasStereoOrEquToStereo = 2; continue; } /* -- do not check number of stereo bonds, check bonds themselves -- for ( s2 = 0; s2 < MAX_NUM_STEREO_BONDS && at[k2].stereo_bond_neighbor[s2]; s2++ ) ; if ( num_sb2 != s2 ) { bDifferentParities = 1; continue; } */ for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) && m-1 != k1; s2++ ) ; if ( m-1 != k1 ) { /* bDifferentParities = 1; // cannot find the stereo bond continue; */ return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ } if ( at[k1].stereo_bond_parity[s1] != at[k2].stereo_bond_parity[s2] ) { bDifferentParities = 1; continue; } stereo_bond_parity2 = PARITY_VAL(at[k1].stereo_bond_parity[s1]); if ( stereo_bond_parity2 != stereo_bond_parity ) { bDifferentParities = 1; continue; } if ( stereo_bond_parity2 == stereo_bond_parity && bDifferentParities < 0 ) { bDifferentParities = 0; } } } /* mark equal parities */ if ( 0 == bDifferentParities && PARITY_KNOWN( stereo_bond_parity ) ) { for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { /* at[k1] is constitutionally equivalent to at[i1] */ for ( s1 = 0, k2 = 0; s1 < MAX_NUM_STEREO_BONDS && (k2=(int)at[k1].stereo_bond_neighbor[s1]); s1++ ) { k2--; if ( nRank[k2] == nAtomRank2 ) { int b1, b2; for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) && m-1 != k1; s2++ ) ; if ( m-1 != k1 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } /* mark the stereo bonds */ b1 = !(at[k1].stereo_bond_parity[s1] & KNOWN_PARITIES_EQL); b2 = !(at[k2].stereo_bond_parity[s2] & KNOWN_PARITIES_EQL); if ( 2 == b1 + b2 ) { at[k1].stereo_bond_parity[s1] |= KNOWN_PARITIES_EQL; at[k2].stereo_bond_parity[s2] |= KNOWN_PARITIES_EQL; num_set ++; } else if ( b1 || b2 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } } } } } } } return num_set; } #if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ /**********************************************************************************/ /* Return next atom number (and its canon. rank) on the path prev->cur->next */ /* in order of ascending canonical ranks of the next atoms: *cr(output) > *cr(input) */ /* To start the sequence let *cr=0 */ /* If no more neighbors available the return value = 0; if successgul then the return value = 1. */ int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ) { int i, val; AT_RANK cr1 = MAX_ATOMS+1, j, j1=MAX_ATOMS+1, crj; for ( i = 0, val = at[(int)cur].valence; i < val; i ++ ) { if ( (j=at[cur].neighbor[i]) != prev && cr1 > (crj=nCanonRank[(int)j]) && crj > *cr ) { cr1 = crj; j1 = j; } } if ( cr1 <= MAX_ATOMS ) { *cr = cr1; *n = (AT_RANK)j1; return 1; } return 0; /* program error */ /* */ } /**********************************************************************************/ /* Find next pair of neighbors having the next greater canon. rank */ /* The neighbors should be constitutionally identical and traversed simultaneouly or not traversed at all */ /* If a bond cur1-*n1 or cur2-*n2 is a stereo bond then reject if their stereo bond parities are different or */ /* cannot be calculated without breaking ties. */ int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, const AT_RANK *nRank, const AT_RANK *nCanonRank ) { AT_RANK cr1, cr2, s1, s2; int i1, i2, k1, k2; cr1 = ( *n1 > MAX_ATOMS )? 0 : nCanonRank[(int)*n1]; cr2 = ( *n2 > MAX_ATOMS )? 0 : nCanonRank[(int)*n2]; if ( !GetNextNeighborAndRank( at, cur1, prev1, n1, &cr1, nCanonRank ) || !GetNextNeighborAndRank( at, cur2, prev2, n2, &cr2, nCanonRank ) || nRank[(int)*n1] != nRank[(int)*n2] || nVisited1[(int)*n1] != nVisited2[(int)*n2] ) { return 0; /* program error; no breakpoint here */ /* */ } /* Even though the bond or cumulene might have already been checked, check it: this is */ /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ /* The code works both for a stereo bond and a stereogenic cumulene. */ for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == *n1)); i1 ++ ) ; for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == *n2)); i2 ++ ) ; if ( k1 != k2 ) { return 0; /* possibly not an error: constit. equivalent atoms on a stereo bond and not on a stereo bond */ } if ( k1 /* yes, it is a stero bond */ && ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] || /* PARITY_KNOWN (at[cur1].stereo_bond_parity[i1] ) */ /* replaced 08-13-2002 with the next: */ !PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1] ) /* it suffices to check only one parity */ ) ) { return 0; /* different or (currently) unknown stereo bond parities */ } return 1; /* stereo bonds have known parities */ } /**********************************************************************************/ /* Simultaneously DFS-traverse 2 paths starting at the bonds prev1->cur1 and prev2->cur2 */ /* The two paths MUST go through the pairs of constitutionally identical atoms, each atom being on one path. */ /* Reject if encountered atoms having currently unknown (without breaking ties) */ /* parities or having different known or unknown or undefined parities. */ /* Save length of the path into nVisited[cur. atom number]. */ /* Only one nVisited[] array is sufficient because the paths from the beginning are in different ring systems. */ AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, AT_RANK *nVisited1, AT_RANK *nVisited2, const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ) { int k; AT_RANK n1, n2; nLength ++; /* number of successfully traversed pairs of atoms */ nVisited1[cur1] = nLength; nVisited2[cur2] = nLength; /* the atoms must be either both stereogenic and have well-defined parities or non-stereogenic at all. */ if ( at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity || at[cur1].stereo_atom_parity && !PARITY_WELL_DEF (at[cur1].stereo_atom_parity) ) { return 0; /* Reject: Different or unknown in advance parities */ } if ( at[cur1].valence != at[cur2].valence ) { return 0; /* program error */ /* */ } if ( at[cur1].valence == 1 ) { return nLength; /* so far success */ } for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { return 0; /* different neighbors */ } /* In a DFS we do not traverse already visited atoms */ if ( !nVisited1[n1] ) { /* recursion */ if ( ! (nLength = PathsHaveIdenticalKnownParities( at, cur1, n1, cur2, n2, nVisited1, nVisited2, nRank, nCanonRank, nLength ) ) ) { return 0; } } } /* To be on a safe side, recheck after all nVisited[] have been set */ for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { return 0; /* different neighbors */ } } return nLength; } /**********************************************************************************/ /* Remove stereo marks from the bonds that are known to be non-stereo */ /* (compare neighbors if they are attached by cut-edges) */ int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, CANON_STAT *pCS) { int j, n, m, ret; int i1, n1, s2; AT_RANK nAtomRank1, nAtomRank2, neigh[3], opposite_atom, *nVisited = NULL; ret = 0; for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { continue; } for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { continue; } opposite_atom = (AT_RANK)(s2-1); /* s2 = at[i1].neighbor[m=(int)at[i1].stereo_bond_ord[n1]]; */ m=(int)at[i1].stereo_bond_ord[n1]; for ( j = 0, n = 0; j < at[i1].valence; j ++ ) { /* if ( at[i1].neighbor[j] != s2 ) */ if ( j != m ) { neigh[n++] = at[i1].neighbor[j]; } } if ( n > 2 ) { ret = CT_STEREOBOND_ERROR; /* */ goto exit_function; } if ( n != 2 || nRank[(int)neigh[0]] != nRank[(int)neigh[1]] ) { continue; /* may happen if another half-bond has not a defined parity */ } if ( at[i1].nRingSystem == at[(int)neigh[0]].nRingSystem ) { continue; /* no more ring system membership check is necessary because */ } /* the two neighbors are to be constitutionally equivalent atoms */ if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { ret = CT_OUT_OF_RAM; /* */ goto exit_function; } memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); nVisited[i1] = 1; if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nVisited, nVisited, nRank, nCanonRank, 1 ) ) { if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond number*/ ) ) { ret = CT_STEREOBOND_ERROR; /* */ goto exit_function; } n1 --; /* cycle counter may temporarily become negative */ /* Remove from pCS */ nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ memmove( pCS->LinearCTStereoDble + n, pCS->LinearCTStereoDble + n + 1, (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); } pCS->nLenLinearCTStereoDble --; #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; #endif m = -1; /* set flag "found" */ break; } } if ( m >= 0) { ret = CT_STEREOCOUNT_ERR; /* bond not found */ goto exit_function; } ret ++; /* number of removed known in advance non-stereo bonds */ } } } exit_function: if ( nVisited ) { inchi_free( nVisited ); } return ret; } #endif /* } REMOVE_KNOWN_NONSTEREO */ /**********************************************************************************/ /* Find stereo center parities known in advance */ int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, const AT_RANK *nAtomNumber ) { int i, j, n, m, j1, k, num_neigh, iMax, trans_i, trans_k, prev_trans, num_set; AT_RANK nAtomRank; AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; AT_RANK nNeighCanonRank[MAX_NUM_STEREO_ATOM_NEIGH]; for ( i = 0, num_set = 0; i < num_atoms; i ++ ) { if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { continue; } if ( at[i].stereo_atom_parity != AB_PARITY_CALC || !PARITY_WELL_DEF(at[i].parity) ) { continue; } num_neigh = at[i].valence; for ( j = 0; j < num_neigh; j ++ ) { nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; } nAtomRank = nRank[i]; if ( num_neigh == 1 ) { /* other neighbors must be implicit H */ at[i].stereo_atom_parity = at[i].parity; trans_i = 0; } else { /* sort constitutional equivalence ranks of the neighbors */ trans_i = insertions_sort(nNeighRank, num_neigh, sizeof(nNeighRank[0]), comp_AT_RANK); for ( j = 1; j < num_neigh; j ++ ) { if ( nNeighRank[j-1] == nNeighRank[j] ) break; /* at[i] has consitutionally identical neighbors */ } if ( j < num_neigh ) { /* at least 2 neighbors are const. identical; parity cannot be calculated at this time */ continue; /* try next stereo atom */ } } prev_trans = -1; trans_k = 0; /* find neighbors of constitutionally equivalent stereo centers */ /* and at[i] parities in case those centers are mapped on at[i] */ for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { /* at[k] is constitutionally equivalent to at[i] */ if ( (int)at[k].valence != num_neigh ) { return CT_STEREOCOUNT_ERR; /* */ } /* -- commented out to accept non-stereogenic atoms since -- -- they may participate in mapping stereocenters 12-16-2003 -- if ( !PARITY_VAL(at[k].parity) ) { continue; // not a stereogenic atom } */ for ( j = 0, m = 0; m < num_neigh; m ++ ) { for ( n = 0; n < num_neigh; n ++ ) { if ( nRank[(int)at[k].neighbor[n]] == nNeighRank[m] ) { /* save canonical numbers (ranks) of the neighbors in * order of increasing constit. equivalence ranks */ nNeighCanonRank[m] = nCanonRank[(int)at[k].neighbor[n]]; j ++; break; } } } if ( j != num_neigh ) { return CT_STEREOCOUNT_ERR; /* */ } trans_k = insertions_sort(nNeighCanonRank, num_neigh, sizeof(nNeighCanonRank[0]), comp_AT_RANK); trans_k %= 2; if ( prev_trans < 0 ) { prev_trans = trans_k; } else if ( trans_k != prev_trans ) { /* different mappings may produce different parities. Cannot find the parity at this time */ /* this may happen when a set of constit. equivalent atoms has non-contiguous canonical numbers */ break; } } if ( trans_k == prev_trans ) { at[i].stereo_atom_parity = 2 - (at[i].parity + trans_i + prev_trans ) % 2; num_set ++; } } return num_set; } #if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ /**********************************************************************************/ /* DFS along paths starting from the stereocenter through pairs of cut-edges */ int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nRank, CANON_STAT *pCS) { int i, j, n, m, k, num_neigh, ret = 0; /*AT_RANK nAtomRank;*/ AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH], nNeighOrd[MAX_NUM_STEREO_ATOM_NEIGH]; AT_RANK *nVisited = NULL; for ( i = 0; i < num_atoms; i ++ ) { if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { continue; } if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && PARITY_WELL_DEF(at[i].stereo_atom_parity) ) { continue; } num_neigh = at[i].valence; for ( j = 0; j < num_neigh; j ++ ) { nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; nNeighOrd[j] = j; } /*nAtomRank = nRank[i];*/ if ( num_neigh == 1 ) { continue; } pn_RankForSort = nNeighRank; insertions_sort(nNeighOrd, num_neigh, sizeof(nNeighRank[0]), CompRanksOrd); for ( j = k = 1; k && j < num_neigh; j ++ ) { if ( at[i].nRingSystem != at[(int)at[i].neighbor[(int)nNeighOrd[j]]].nRingSystem && /* no more ring system membership check is necessary because */ /* the two neighbors are to be constitutionally equivalent atoms: */ nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[j]] ) { k = j; do { if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { ret = CT_OUT_OF_RAM; /* */ goto exit_function; } memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); nVisited[i] = 1; if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[j-1]], (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[k]], nVisited, nVisited, nRank, nCanonRank, 1 ) ) { at[i].parity = 0; /* remove parity */ at[i].stereo_atom_parity = 0; at[i].final_parity = 0; /* at[i].bHasStereoOrEquToStereo = 0; */ for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoCarb[n].at_num == nCanonRank[i] ) { if ( n < m ) { /* remove pCS->LinearCTStereoCarb[n] */ memmove( pCS->LinearCTStereoCarb + n, pCS->LinearCTStereoCarb + n + 1, (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); } pCS->nLenLinearCTStereoCarb --; k = 0; #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; #endif break; } } if ( k ) { ret = CT_STEREOCOUNT_ERR; /* */ goto exit_function; } ret ++; break; } } while ( ++k < num_neigh && nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[k]] ); } } } exit_function: if ( nVisited ) { inchi_free( nVisited ); } return ret; } #endif /* } REMOVE_KNOWN_NONSTEREO */ /**********************************************************************************/ /* Find stereo center parities known in advance */ int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber ) { int i, j1, k, num_centers, iMax, bDifferentParities; AT_RANK nAtomRank; int parity, parity_k; num_centers = 0; for ( i = 0; i < num_atoms; i ++ ) { if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { continue; } if ( at[i].bHasStereoOrEquToStereo ) { continue; /* already marked */ } if ( /*!PARITY_KNOWN(at[i].stereo_atom_parity) ||*/ (at[i].stereo_atom_parity & KNOWN_PARITIES_EQL) ) { continue; } parity = PARITY_VAL(at[i].stereo_atom_parity); if ( parity == AB_PARITY_NONE ) { continue; } nAtomRank = nRank[i]; bDifferentParities = -1; /* find constitutionally equivalent stereo centers and compare their known at this time parities */ for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { /* at[k] is constitutionally equivalent to at[i] */ parity_k = PARITY_VAL(at[k].stereo_atom_parity); if ( parity_k != parity ) { bDifferentParities = 1; } else if ( parity_k == parity && bDifferentParities < 0 ) { bDifferentParities = 0; } if ( !parity_k ) { at[k].bHasStereoOrEquToStereo = 2; } else if ( !at[k].bHasStereoOrEquToStereo ) { at[k].bHasStereoOrEquToStereo = 1; } } if ( 0 == bDifferentParities && PARITY_KNOWN( parity ) ) { for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { /* at[k] is constitutionally equivalent to at[i] */ at[k].stereo_atom_parity |= KNOWN_PARITIES_EQL; num_centers ++; } } } return num_centers; } /*****************************************************************************/ /* invert known parities in at[] and in pCS->LinearCTStereoDble */ /* pCS->LinearCTStereoCarb */ /* nCanonRank[] contains canonical ranks used to fill pCS->LinearCTStereo... */ /* nAtomNumberCanon[] will be filled with atom numbers in canonical order */ /*****************************************************************************/ int InvertStereo( sp_ATOM *at, int num_at_tg, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, int bInvertLinearCTStereo ) { int i, j, j1, j2, num_changes, parity, cumulene_len; num_changes = 0; for ( i = 0; i < num_at_tg; i ++ ) { nAtomNumberCanon[(int)nCanonRank[i]-1] = i; } for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++ ) { parity = pCS->LinearCTStereoCarb[i].parity; if ( ATOM_PARITY_WELL_DEF( parity ) ) { j = nAtomNumberCanon[(int)pCS->LinearCTStereoCarb[i].at_num-1]; if ( PARITY_WELL_DEF(at[j].parity) ) { at[j].parity ^= AB_INV_PARITY_BITS; } else { goto exit_error; /* inconsistency */ } if ( bInvertLinearCTStereo ) { pCS->LinearCTStereoCarb[i].parity ^= AB_INV_PARITY_BITS; } num_changes ++; if ( PARITY_WELL_DEF(at[j].stereo_atom_parity) ) { at[j].stereo_atom_parity ^= AB_INV_PARITY_BITS; } if ( PARITY_WELL_DEF(at[j].final_parity) ) { at[j].final_parity ^= AB_INV_PARITY_BITS; } } } for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++ ) { parity = pCS->LinearCTStereoDble[i].parity; if ( ATOM_PARITY_WELL_DEF( parity ) ) { j1 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num1-1]; cumulene_len = BOND_CHAIN_LEN(at[j1].stereo_bond_parity[0]); if ( cumulene_len % 2 ) { /* invert only in case of allene */ j2 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num2-1]; /* checks for debug only */ if ( 1 < MAX_NUM_STEREO_BONDS ) { if ( at[j1].stereo_bond_neighbor[1] || at[j2].stereo_bond_neighbor[1] ) { goto exit_error; /* inconsitency: atom has more than one cumulene bond */ } } if ( cumulene_len != BOND_CHAIN_LEN(at[j2].stereo_bond_parity[0]) || j1+1 != at[j2].stereo_bond_neighbor[0] || j2+1 != at[j1].stereo_bond_neighbor[0] ) { goto exit_error; /* inconsitency: atoms should refer to each other */ } /* invert parities */ if ( PARITY_WELL_DEF(at[j1].parity) && PARITY_WELL_DEF(at[j2].parity) ) { j = inchi_min( j1, j2 ); at[j].parity ^= AB_INV_PARITY_BITS; /* for reversability always invert only atom with the smaller number */ } else { goto exit_error; /* inconsistency */ } if ( bInvertLinearCTStereo ) { pCS->LinearCTStereoDble[i].parity ^= AB_INV_PARITY_BITS; } num_changes ++; if ( PARITY_WELL_DEF(at[j1].stereo_bond_parity[0]) ) { at[j1].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; } if ( PARITY_WELL_DEF(at[j2].stereo_bond_parity[0]) ) { at[j2].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; } } } } return num_changes; exit_error: return CT_STEREOCOUNT_ERR; } /**********************************************************************************/ /* Make sure atoms stereo descriptors fit molecular symmetry and remove */ /* parity from obviously non-stereo atoms and bonds */ int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ) { int ret; /* unmark atoms with 2 or more constitutionally equivalent neighbors */ /* such that there is no path through them to an atom with parity */ ret = UnmarkNonStereo( at, num_atoms, nRank, nAtomNumber, bIsotopic ); if ( ret < 0 ) return ret; /* program error? */ /* */ ret = FillAllStereoDescriptors( at, num_atoms, nCanonRank, nAtomNumberCanon, pCS ); /* ret<0: error */ if ( !ret ) { ret = pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble; } if ( ret < 0 ) { return ret; /* program error? */ /* */ } if ( ret >= 0 ) { int ret2; ret2 = SetKnownStereoCenterParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); if ( ret2 >= 0 ) { ret2 = MarkKnownEqualStereoCenterParities( at, num_atoms, nRank, nAtomNumber ); } if ( ret2 >= 0 ) { ret2 = SetKnownStereoBondParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); if ( ret2 >= 0 ) { ret2 = MarkKnownEqualStereoBondParities( at, num_atoms, nRank, nAtomNumber); } } #if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ if ( ret2 >= 0 ) { int ret3; do { ret2 = RemoveKnownNonStereoCenterParities( at, num_atoms, nCanonRank, nRank, pCS); if ( ret2 >= 0 ) { ret3 = RemoveKnownNonStereoBondParities( at, num_atoms, nCanonRank, nRank, pCS); ret2 = ret3 >= 0? ret2+ret3 : ret3; } } while( ret2 > 0 ); } if ( RETURNED_ERROR( ret2 ) ) { ret = ret2; } #endif /* } REMOVE_KNOWN_NONSTEREO */ } return ret; /* non-zero means error */ } /**********************************************************************************/ int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ) { int k1; AT_RANK sNeigh = (AT_RANK)(iAt2+1); AT_RANK s; for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = at[iAt1].stereo_bond_neighbor[k1]); k1 ++ ) { if ( s == sNeigh ) { return k1; } } return -1; /* neighbor not found */ } /**********************************************************************************/ /* Extracted from FillSingleStereoDescriptors(...) */ /**********************************************************************************/ int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ) { int k1, k2, s, parity; if ( at[i].stereo_bond_neighbor[0] ) { for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = (int)at[i].stereo_bond_neighbor[k1]); k1 ++ ) { if ( --s == n ) { goto neigh1_found; } } return -1; /* error: not a stereo neighbor */ neigh1_found: if ( PARITY_KNOWN( at[i].stereo_bond_parity[k1] ) ) { return PARITY_VAL( at[i].stereo_bond_parity[k1] ); } for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && (s = (int)at[n].stereo_bond_neighbor[k2]); k2 ++ ) { if ( --s == i ) { goto neigh2_found; } } return -1; /* error: not a stereo neighbor */ neigh2_found:; } else { return -1; /* error: not a stereo bond */ } if ( ATOM_PARITY_WELL_DEF(at[i].parity) && ATOM_PARITY_WELL_DEF(at[n].parity) && MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[k1]) ) { /* bond parity can be calculated */ int half_parity1, half_parity2; /* check whether all neighbors are defined */ half_parity1 = HalfStereoBondParity( at, i, k1, nRank ); half_parity2 = HalfStereoBondParity( at, n, k2, nRank ); if ( !half_parity1 || !half_parity2 ) return 0; /* ranks undefined or not a stereo bond */ if ( ATOM_PARITY_WELL_DEF(half_parity1) && ATOM_PARITY_WELL_DEF(half_parity2) ) { parity = 2 - ( half_parity1 + half_parity2 + (at[i].stereo_bond_z_prod[k1] < 0))%2; } else { return CT_STEREOBOND_ERROR; /* */ } } else { /* parity cannot be calculated: not enough info or 'unknown' */ if ( AB_PARITY_NONE != (parity = inchi_max(at[i].parity, at[n].parity)) ) { parity = AB_PARITY_UNDF; /* should not happen */ } } return parity; } /**********************************************************************************/ /* Extracted from FillSingleStereoDescriptors(...) */ /**********************************************************************************/ int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ) { AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; int j, k, parity; if ( at->valence > MAX_NUM_STEREO_ATOM_NEIGH ) { parity = -1; /* error */ } else { for ( j = k = 0; j < at->valence; j ++ ) { if ( at->neighbor[j] != nAvoidNeighbor ) { nNeighRank[k++] = nCanonRank[(int)at->neighbor[j]]; } } if ( k ) { parity = insertions_sort( nNeighRank, k, sizeof(nNeighRank[0]), comp_AT_RANK); if ( nNeighRank[0] ) { parity = 2 - parity % 2; } else { parity = 0; /* not all ranks are known */ } } else { /* special case: HX= with implicit H */ parity = 2; } } return parity; } /**********************************************************************************/ int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ) { AT_NUMB nNeighborNumber2[MAXVAL]; int parity; int k, num_trans; if ( !at[i].parity ) { return 0; /* not a stereo center */ } if ( at[i].stereo_bond_neighbor[0] ) { return -1; /* a stereo bond atom, not a stereo center */ } if ( ATOM_PARITY_WELL_DEF(at[i].parity) ) { /* number of neighbors transpositions to the sorted order is unknown. Find it. */ /* If parity is not well-defined then doing this is a waste of time */ int num_neigh = at[i].valence; for ( k = 0; k < num_neigh; k ++) { if ( !nRank[(int)at[i].neighbor[k]] ) return 0; /* stereo at[i] does not belong to the traversed part of the structure */ nNeighborNumber2[k] = k; } pNeighborsForSort = at[i].neighbor; pn_RankForSort = nRank; num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); #ifndef CT_NEIGH_INCREASE num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ #endif parity = 2 - (at[i].parity + num_trans)%2; } else { parity = at[i].parity; } return parity; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicant.h000066400000000000000000000406271271037650300235150ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHICANT_H__ #define __INCHICANT_H__ /******************************************************/ /* */ /* Canonicalization definitions */ /* */ /******************************************************/ #include "ichisize.h" #ifndef INCHI_US_SHORT_DEF typedef signed short S_SHORT; typedef unsigned short U_SHORT; #define INCHI_US_SHORT_DEF #endif /*typedef unsigned long INCHI_MODE;*/ typedef union tagSplitLong { unsigned long ul; U_SHORT us[2]; }SU_LONG; #define _HI 1 /* Intel platform */ #define _LO 0 #define NEIGH_LIST_LEN 4 #define U_LONG_LEN 2 #ifndef defined_NEIGH_LIST typedef AT_RANK *NEIGH_LIST; #define defined_NEIGH_LIST #endif typedef struct tagEQUIV_INFO { int nNumSets; int *nCutVertexAtom; /* cut-vertex atom for the set of equivalent atoms */ int *nFirstInSet; /* first of equivalent atoms in the connected to the cut-vertex atom parts of the structure */ int *nNumInSet; /* number of the equivalent atoms connected to the cut-vertex atom */ int *nAtomNo; /* eqivalent atom number */ int *nAddToRank; /* number to add to the rank to normalize */ } EQUIV_INFO; #define MOL_PART_MASK (~0x0U ^ 0x07U) typedef struct tagAtData_dch { char element[3]; int valence; }AT_DATA; #define MAXVAL 20 /* maximum valence */ #define ATOM_EL_LEN 6 typedef struct tagAtomInvariantBytes { S_CHAR cNotExactlyHillOrderNumber; S_CHAR cNumberOfConnections; /* S_CHAR cNumberOfNonHydrogenBonds; */ S_CHAR cAtomicNumber; #if ( HYDROGENS_IN_INIT_RANKS == 1 ) S_CHAR cNumberOfAttachedHydrogens; #endif } ATOM_INVARIANT_BYTES; typedef struct tagAtomInvariant { /* non-isotopic part */ #if ( USE_DISTANCES_FOR_RANKING == 1 ) AT_RANK nDistanceFromTerminal; #endif ATOM_INVARIANT_BYTES b; AT_RANK cNum_tautomer; /* 0 or for tautomer endpoint: number of endpoints in the group */ AT_RANK cNum_tautomer_num[T_NUM_NO_ISOTOPIC]; /* 0 or numbers from t_gtroup */ /* isotopic part */ AT_ISO_SORT_KEY iso_sort_key; AT_RANK cNum_tautomer_iso[T_NUM_ISOTOPIC]; /* 0 or numbers from t_group */ } ATOM_INVARIANT; /**********************************/ typedef enum tagAtInvariantIndexes { AT_INV_HILL_ORDER, AT_INV_NUM_CONNECTIONS, AT_INV_NUM_H, /* for endpoint + undirected graph, otherwise 0 */ AT_INV_NUM_TG_ENDPOINTS, AT_INV_TG_NUMBERS, /* num H, num (-) */ AT_INV_NUM_H_FIX = AT_INV_TG_NUMBERS+T_NUM_NO_ISOTOPIC, AT_INV_BREAK1, /* here compare iso sort key */ AT_INV_TAUT_ISO = AT_INV_BREAK1, AT_INV_LENGTH = AT_INV_TAUT_ISO + T_NUM_ISOTOPIC } AT_INV_INDEXES; typedef struct tagAtomInvariant2 { AT_NUMB val[AT_INV_LENGTH]; AT_ISO_SORT_KEY iso_sort_key; S_CHAR iso_aux_key; } ATOM_INVARIANT2; /******************* Partition **********************************/ typedef struct tagPartition { AT_RANK *Rank; AT_NUMB *AtNumber; } Partition; /********************* BFCN *************************************/ typedef struct tagFixHOrTautCanonNumbering { int num_at_tg; /* = num_atoms for non-taut */ int num_atoms; int nCanonFlags; NEIGH_LIST *NeighList; /* length = num_at_tg */ /****************************/ /* base structure */ /****************************/ AT_RANK *LinearCt; /* connection table atoms (+taut. groups, directed graph)*/ int nLenLinearCtAtOnly; int nLenLinearCt; int nMaxLenLinearCt; Partition PartitionCt; /* canonical numbering */ AT_RANK *nSymmRankCt; /* orbits */ /* orig. fixed by tautomerism H positions */ NUM_H *nNumHOrig; /* original H atoms positions + taut. info, excluding tautomeric H */ NUM_H *nNumH; /* canonical H atoms positions + taut. info, excluding tautomeric H */ int nLenNumH; /* length = num_atoms + 2*num_taut_groups */ /* fixed H: original positions of tautomeric H; exists obly for tautomeric structures */ NUM_H *nNumHOrigFixH; /* original fixed positions of tautomeric H */ NUM_H *nNumHFixH; /* canonical fixed positions of tautomeric H */ int nLenNumHFixH; /* length = num_atoms */ /*******************************************************************************/ /* the following exists only if isotopic and isotopic results requested */ /*******************************************************************************/ Partition PartitionCtIso; /* canonical numbering of isotopic base structure, defined later */ AT_RANK *nSymmRankCtIso; /* orbits of isotopic structure */ AT_ISO_SORT_KEY *iso_sort_keys; /* original isotopic sort keys for atoms and taut groups */ AT_ISO_SORT_KEY *iso_sort_keysOrig; /* canonical isotopic sort keys for atoms and taut groups */ int len_iso_sort_keys; S_CHAR *iso_exchg_atnos; /* canonical: 0=> tautomeric or may have isotopic H exchanged */ S_CHAR *iso_exchg_atnosOrig; /* original: 0=> tautomeric or may have isotopic H exchanged */ } FTCN; /******************** BCN *************************************/ typedef struct tagBaseCanonNumbering { AT_RANK **pRankStack; int nMaxLenRankStack; int num_max; /* allocated nRank[] arrays lengths in pRankStack */ int num_at_tg; /* all of the following arrays have this length */ int num_atoms; struct tagInchiTime *ulTimeOutTime; FTCN ftcn[TAUT_NUM]; } BCN; /*********************************** * * CANON_STAT */ typedef struct tagCanonStat { /* statistics */ long lNumBreakTies; long lNumNeighListIter; long lNumTotCT; long lNumDecreasedCT; long lNumRejectedCT; long lNumEqualCT; struct tagInchiTime *ulTimeOutTime; long lTotalTime; /* control */ int bFirstCT; int bKeepSymmRank; int bStereoIsBetter; int nCanonFlags; /* data : */ AT_NUMB *LinearCT; /* connection table only */ AT_ISOTOPIC *LinearCTIsotopic; AT_ISO_TGROUP *LinearCTIsotopicTautomer; AT_STEREO_DBLE *LinearCTStereoDble; AT_STEREO_CARB *LinearCTStereoCarb; AT_STEREO_DBLE *LinearCTStereoDbleInv; AT_STEREO_CARB *LinearCTStereoCarbInv; AT_STEREO_DBLE *LinearCTIsotopicStereoDble; AT_STEREO_CARB *LinearCTIsotopicStereoCarb; AT_STEREO_DBLE *LinearCTIsotopicStereoDbleInv; AT_STEREO_CARB *LinearCTIsotopicStereoCarbInv; AT_TAUTOMER *LinearCTTautomer; /* minimal */ /* second copies of line notation arrays */ AT_NUMB *LinearCT2; /* to save non-isotopic CT */ int nLenLinearCTStereoDble; int nLenLinearCTStereoDbleInv; int nMaxLenLinearCTStereoDble; /* new */ int bCmpStereo; /* 0 => no stereo to invert; 1 => StereoCtInv < StereoCt; 2 => StereoCtInv = StereoCt; 3 => StereoCtInv > StereoCt; */ int nLenLinearCTStereoCarb; int nLenLinearCTStereoCarbInv; int nMaxLenLinearCTStereoCarb; /* new */ int nLenLinearCTIsotopic; int nMaxLenLinearCTIsotopic; int nLenLinearCTIsotopicTautomer; int nMaxLenLinearCTIsotopicTautomer; int nLenLinearCT; /* connection table only */ int nLenLinearCT2; /* connection table only, non-isotopic result */ int nLenLinearCTAtOnly; /* connection table only without tautomeric pseudoatoms */ int nLenLinearCTAtOnly2; /* connection table only, non-isotopic result without tautomeric pseudoatoms */ int nMaxLenLinearCT; /* connection table only */ int nLenLinearCTTautomer; int nMaxLenLinearCTTautomer; int bCmpIsotopicStereo; /* 0 => no stereo to invert; 1 => StereoCtInv < StereoCt; 2 => StereoCtInv = StereoCt; 3 => StereoCtInv > StereoCt; */ int nLenLinearCTIsotopicStereoDble; int nLenLinearCTIsotopicStereoDbleInv; int nMaxLenLinearCTIsotopicStereoDble; int nLenLinearCTIsotopicStereoCarb; /* new */ int nLenLinearCTIsotopicStereoCarbInv; /* new */ int nMaxLenLinearCTIsotopicStereoCarb; S_CHAR *bRankUsedForStereo; /* canon. rank used for stereo mapping */ S_CHAR *bAtomUsedForStereo; /* 0 if not a stereo atom or during a canon. rank being mapped on this atom; */ /* STEREO_AT_MARK if an unpapped stereogenic atom */ /* or a number of stereogenic bonds adjacent to an atom */ AT_RANK *nPrevAtomNumber; AT_RANK *nCanonOrd; /* atom numbers in order of increasing canon. ranks */ AT_RANK *nSymmRank; /* symmetry numbers in order of atoms */ AT_RANK *nCanonOrdTaut; /* t-group numbers numbers in order of increasing canon. ranks */ AT_RANK *nSymmRankTaut; /* t-group symmetry numbers in order of t-groups */ AT_RANK *nCanonOrdStereo; /* atom numbers in order of increasing canon. ranks */ AT_RANK *nCanonOrdStereoInv; /* atom numbers in order of increasing canon. ranks */ AT_RANK *nCanonOrdStereoTaut; /* t-group numbers in order of increasing canon. ranks */ AT_RANK *nSymmRankIsotopic; AT_RANK *nCanonOrdIsotopic; /* atom numbers in order of increasing canon. ranks */ AT_RANK *nSymmRankIsotopicTaut; /* !!! */ AT_RANK *nCanonOrdIsotopicTaut; /*/ t-group numbers in order of increasing canon. ranks */ AT_RANK *nCanonOrdIsotopicStereo; AT_RANK *nCanonOrdIsotopicStereoInv; AT_RANK *nCanonOrdIsotopicStereoTaut; /* !!! */ /* actual lengths if successfully calculated */ int nLenCanonOrd; /* Superceded by any of the following > 0 */ int nLenCanonOrdTaut; /* !!! Superceded by any of the following > 0 */ int nLenCanonOrdIsotopic; int nLenCanonOrdIsotopicTaut; /* !!! */ int nLenCanonOrdStereo; int nLenCanonOrdStereoTaut; /* !!! */ int nLenCanonOrdIsotopicStereo; int nLenCanonOrdIsotopicStereoTaut; /* !!! */ /* other */ int bHasIsotopicInTautomerGroups; T_GROUP_INFO *t_group_info; int bIgnoreIsotopic; int bDoubleBondSquare; /* 0 or 2 */ INCHI_MODE nMode; #if ( bRELEASE_VERSION == 0 ) int bExtract; /* for debug only */ #endif NEIGH_LIST *NeighList; BCN *pBCN; S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom except tautomeric [num_atoms], in order of canonical numbers */ S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms (for non-atautomeric representation) [num_atoms] */ S_CHAR *nExchgIsoH; } CANON_STAT; /**************************************************/ typedef struct tagCanonData { /* same names/types as in ConTable; here the order is from original numbering */ AT_NUMB *LinearCT; /* output ?? */ int nMaxLenLinearCT; int nLenLinearCT; int nLenCTAtOnly; int nCanonFlags; /* hydrogen atoms fixed in tautomeric representation: compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ NUM_H *NumH; int lenNumH; /* used length */ int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ /* hydrogen atoms fixed in non-tautomeric representation only: compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ NUM_H *NumHfixed; int lenNumHfixed; /* used length */ int maxlenNumHfixed; /* max length = n+1 */ /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ AT_ISO_SORT_KEY *iso_sort_key; int len_iso_sort_key; /* used length */ int maxlen_iso_sort_key; /* max length = n_tg+1 */ S_CHAR *iso_exchg_atnos; int len_iso_exchg_atnos; /* used length */ int maxlen_iso_exchg_atnos; /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ #if ( USE_ISO_SORT_KEY_HFIXED == 1 ) AT_ISO_SORT_KEY *iso_sort_key_Hfixed; int len_iso_sort_key_Hfixed; /* used length */ int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ #endif /* auxiliary ranking */ AT_RANK *nAuxRank; struct tagInchiTime *ulTimeOutTime; /* timeout */ } CANON_DATA; /**************************************************/ typedef struct tagCanonCounts { long lNumBreakTies; long lNumDecreasedCT; long lNumRejectedCT; long lNumEqualCT; long lNumTotCT; double dGroupSize; long lNumGenerators; long lNumStoredIsomorphisms; } CANON_COUNTS; /*********************************************** tree structure: one segment canon. rank at.no orig. atom numbers on which the canon. rank has been successfully mapped ... at.no except the last at.no: it is not known if it has been mapped until all atoms are mapped num.at+1 number of atoms in this segment */ typedef struct tagCurTree { AT_NUMB *tree; int max_len; /* allocated length of tree in sizeof(tree[0]) units */ int cur_len; /* currently used length */ int incr_len; /* reallocation increment */ } CUR_TREE; #endif /* __INCHICANT_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicomn.h000066400000000000000000000376561271037650300235340ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHICOMN_H__ #define __INCHICOMN_H__ #include "ichierr.h" /******************************** * * Globals for sorting */ extern const NEIGH_LIST *pNeighList_RankForSort; extern const ATOM_INVARIANT2 *pAtomInvariant2ForSort; extern const AT_NUMB *pNeighborsForSort; extern const AT_RANK *pn_RankForSort; extern AT_RANK nMaxAtNeighRankForSort; extern int nNumCompNeighborsRanksCountEql; #define tsort insertions_sort typedef struct tagEquNeigh { int num_to; /* number of neighbors with equal mapping ranks; one of them has min. canon. number */ AT_RANK to_at[4]; /* to_atom neighbors #s with equal mapping ranks */ AT_RANK from_at; /* from_at neighbor # which has min. canon. number and can be mapped on any of the above to_at[] */ AT_RANK rank; /* equal mapping rank value */ AT_RANK canon_rank; /* min. canon. number */ } EQ_NEIGH; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*******************************************************************/ /* ichiisot.c */ int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ); AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H); int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ); /***********************************************************************/ /* ichisort.c */ void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ); int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ); int insertions_sort_AT_RANK( AT_RANK *base, int num ); void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank); int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ); int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ); int CompNeighborsAT_NUMBER( const void* a1, const void* a2); int comp_AT_RANK( const void* a1, const void* a2); int CompRank(const void* a1, const void* a2 ); int CompRanksOrd( const void* a1, const void* a2 ); int CompAtomInvariants2Only( const void* a1, const void* a2 ); int CompAtomInvariants2( const void* a1, const void* a2 ); int CompNeighListRanks( const void* a1, const void* a2 ); int CompNeighListRanksOrd( const void* a1, const void* a2 ); int CompNeighLists( const void* a1, const void* a2 ); int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ); int CompNeighborsRanksCountEql( const void* a1, const void* a2 ); int CompRanksInvOrd( const void* a1, const void* a2 ); int CompChemElemLex( const void *a1, const void *a2 ); NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, int bDoubleBondSquare, T_GROUP_INFO *t_group_info ); NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ); void FreeNeighList( NEIGH_LIST *pp ); int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS); /******************************************************************************/ /* ichimap.c */ void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ); int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ); int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ); int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ); int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber ); int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ); void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter ); int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ); int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); int parity_of_mapped_atom2( int from_at, int to_at, const sp_ATOM *at, EQ_NEIGH *pEN, const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, sp_ATOM *at, EQ_NEIGH *pEN, const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ); int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ); int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, AT_RANK *nTempRank, int nNumMappedRanks, int *pnNewNumMappedRanks, CANON_STAT *pCS, NEIGH_LIST *NeighList, AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ); int ClearPreviousMappings( AT_RANK **pRankStack1 ); int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ); int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ); int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ); int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, int vABParityUnknown); int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ); int map_stereo_bonds4 ( sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ AT_RANK *nCanonRankTo, /* output canonical numbering*/ const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, AT_RANK *nTempRank, int nNumMappedRanksInput, AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, int vABParityUnknown); int map_stereo_atoms4 ( sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, AT_RANK *nTempRank, int nNumMappedRanksInput, AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms, int vABParityUnknown); int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ); int CurTreeReAlloc( CUR_TREE *cur_tree ); void CurTreeFree( CUR_TREE *cur_tree ); int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ); int CurTreeRemoveLastRank( CUR_TREE *cur_tree ); int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ); int CurTreeGetPos( CUR_TREE *cur_tree ); int CurTreeSetPos( CUR_TREE *cur_tree, int len ); int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ); int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ); int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ); int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ); int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ); void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ); void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ); int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ); int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ); int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, int *bFirstTime, S_CHAR *bAtomUsedForStereo, const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, const sp_ATOM *at, int num_atoms, int bAllene ); int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ /* output: suggested canon. rank > than input if success */ AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ int *bFirstTime, /* 1 at the time of the 1st call */ S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ int num_atoms ); int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, int vABParityUnknown); int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nAtomNumberCanonFrom, sp_ATOM *at ); int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nAtomNumberCanonFrom, const sp_ATOM *at ); int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ); int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, AT_RANK at_rank_canon1, U_CHAR parity ); int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ); int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); /***************************************************************************/ /* ichicans.c */ int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ); int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen , int bAllene ); void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ); void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ); int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ); int InvertStereo( sp_ATOM *at, int num_at_tg, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, int bInvertLinearCTStereo ); int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ); int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ); int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ); int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ); int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHICOMN_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichicomp.h000066400000000000000000000054631271037650300235250ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHI_COMPAT_H__ #define __INCHI_COMPAT_H__ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* compatibility */ #if ( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) /* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ #define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) #else #define my_va_start va_start #endif /* ANSI redefinitions */ #ifdef COMPILE_ANSI_ONLY /* { */ #ifndef __isascii #define __isascii(val) ((unsigned)(val) <= 0x7F) #endif /* #ifndef __GNUC__ */ /* these non-ANSI functions are implemented in gcc */ #include /* this #include provides size_t definition */ /* implementation is located in util.c */ /*#if ( !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 )*/ #if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) /* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ int memicmp (const void*, const void*, size_t); int stricmp( const char *s1, const char *s2 ); char *_strnset( char *string, int c, size_t count ); char *_strdup( const char *string ); #endif /* #endif */ #endif /* } */ #define inchi_max(a,b) (((a)>(b))?(a):(b)) #define inchi_min(a,b) (((a)<(b))?(a):(b)) #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHI_COMPAT_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichidrp.h000066400000000000000000000143701271037650300233510ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHIDRP_H__ #define __INCHIDRP_H__ #ifndef COMPILE_ANSI_ONLY /* { */ /******************************************** * Parameters for the structure drawing ********************************************/ #define TDP_LEN_LBL 16 /* length of a label (label: Req., Shown, Found) */ /* #define TDP_NUM_LBL 3 */ /* number of labels */ /* #define TDP_NUM_PAR 3 */ /* number of types per label (types: B/T, I/N, S) */ typedef enum tagTblTypes {itBASIC, itISOTOPIC, itSTEREO, TDP_NUM_PAR} TBL_TYPES; /* types */ typedef enum tagTblLabels{ ilSHOWN, TDP_NUM_LBL} TBL_LABELS; /* labels */ typedef struct tagTblDrawPatms { char ReqShownFoundTxt[TDP_NUM_LBL][TDP_LEN_LBL]; char ReqShownFound[TDP_NUM_LBL][TDP_NUM_PAR]; int nOrientation; /* 10*degrees: 0 or 2700 */ int bDrawTbl; } TBL_DRAW_PARMS; /*********************************************/ typedef struct tagDrawParmsSettings { TBL_DRAW_PARMS *tdp; unsigned long ulDisplTime; int bOrigAtom; int nFontSize; } SET_DRAW_PARMS; /* input only: how to draw or calculate */ /*********************************************/ typedef struct tagReturnedDrawParms { int bEsc; } RET_DRAW_PARMS; /*********************************************/ typedef struct tagPersistDrawParms { int rcPict[4]; } PER_DRAW_PARMS; /* saved between displaying different structures */ /*********************************************/ typedef struct tagDrawParms { SET_DRAW_PARMS sdp; /* how to draw: fill on the 1st call */ RET_DRAW_PARMS rdp; /* returned when drawing window is closed */ PER_DRAW_PARMS *pdp; /* persistent: save between calls (window size) */ #ifndef TARGET_LIB_FOR_WINCHI #ifndef COMPILE_ANSI_ONLY AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ AT_NUMB nNumEquSets; /* max mark value */ AT_NUMB nCurEquLabel; /* current mark */ #endif #endif } DRAW_PARMS; /* Settings: How to draw the structure */ #endif /* } COMPILE_ANSI_ONLY */ #define MAX_NUM_PATHS 4 typedef enum tagInputType { INPUT_NONE=0, INPUT_MOLFILE=1, INPUT_SDFILE=2, INPUT_INCHI_XML=3, INPUT_INCHI_PLAIN=4, INPUT_CMLFILE=5, INPUT_INCHI=6, INPUT_MAX } INPUT_TYPE; /* bCalcInChIHash values */ typedef enum tagInChIHashCalc { INCHIHASH_NONE=0, INCHIHASH_KEY=1, INCHIHASH_KEY_XTRA1=2, INCHIHASH_KEY_XTRA2=3, INCHIHASH_KEY_XTRA1_XTRA2=4 } INCHI_HASH_CALC; typedef struct tagInputParms { char szSdfDataHeader[MAX_SDF_HEADER+1]; char *pSdfLabel; char *pSdfValue; long lSdfId; long lMolfileNumber; #ifndef COMPILE_ANSI_ONLY DRAW_PARMS dp; PER_DRAW_PARMS pdp; TBL_DRAW_PARMS tdp; #endif /* -- Files -- ip->path[0] => Input ip->path[1] => Output (INChI) ip->path[2] => Log ip->path[3] => Problem structures ip->path[4] => Errors file (ACD) */ const char *path[MAX_NUM_PATHS]; int num_paths; long first_struct_number; long last_struct_number; INPUT_TYPE nInputType; INCHI_MODE nMode; int bAbcNumbers; /*int bXml;*/ int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ int bCtPredecessors; int bXmlStarted; int bDisplayEachComponentINChI; long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ long msec_LeftTime; long ulDisplTime; /* not used: max structure or question display time */ int bDisplay; int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ int bMergeAllInputStructures; int bSaveWarningStructsAsProblem; int bSaveAllGoodStructsAsProblem; int bGetSdfileId; int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ int bDisplayCompositeResults; int bDoNotAddH; int bNoStructLabels; int bChiralFlag; int bAllowEmptyStructure; /*^^^ */ int bCalcInChIHash; int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ /*^^^ */ INCHI_MODE bTautFlags; INCHI_MODE bTautFlagsDone; #if ( READ_INCHI_STRING == 1 ) int bReadInChIOptions; #endif /* post v.1 features */ #if ( UNDERIVATIZE == 1 ) int bUnderivatize; #endif #if ( RING2CHAIN == 1 ) int bRing2Chain; #endif #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) int bIngnoreUnchanged; #endif } INPUT_PARMS; #endif /* __INCHIDRP_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichierr.h000066400000000000000000000123641271037650300233550ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHIERR_H__ #define __INCHIERR_H__ #define _IS_OKAY 0 #define _IS_WARNING 1 #define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ #define _IS_FATAL 3 #define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ #define _IS_EOF -1 /* end of file */ #define _IS_SKIP -2 #define CT_ERR_FIRST (-30000) #define CT_OVERFLOW (CT_ERR_FIRST- 0) /*(-30000) */ #define CT_LEN_MISMATCH (CT_ERR_FIRST- 1) /*(-30001) */ #define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ #define CT_RANKING_ERR (CT_ERR_FIRST- 3) /*(-30003) */ #define CT_ISOCOUNT_ERR (CT_ERR_FIRST- 4) /*(-30004) */ #define CT_TAUCOUNT_ERR (CT_ERR_FIRST- 5) /*(-30005) */ #define CT_ISOTAUCOUNT_ERR (CT_ERR_FIRST- 6) /*(-30006) */ #define CT_MAPCOUNT_ERR (CT_ERR_FIRST- 7) /*(-30007) */ #define CT_TIMEOUT_ERR (CT_ERR_FIRST- 8) /*(-30008) */ #define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ #define CT_STEREOCOUNT_ERR (CT_ERR_FIRST-10) /*(-30010) */ #define CT_ATOMCOUNT_ERR (CT_ERR_FIRST-11) /*(-30011) */ #define CT_STEREOBOND_ERROR (CT_ERR_FIRST-12) /*(-30012) */ #define CT_USER_QUIT_ERR (CT_ERR_FIRST-13) /*(-30013) */ #define CT_REMOVE_STEREO_ERR (CT_ERR_FIRST-14) /*(-30014) */ #define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ #define CT_CANON_ERR (CT_ERR_FIRST-16) /*(-30016) */ #define CT_STEREO_CANON_ERR (CT_ERR_FIRST-17) /*(-30017) */ #define CT_WRONG_FORMULA (CT_ERR_FIRST-18) /*(-30017) */ #define CT_UNKNOWN_ERR (CT_ERR_FIRST-19) /*(-30019) */ #define CT_ERR_MIN CT_UNKNOWN_ERR #define CT_ERR_MAX CT_ERR_FIRST #define CHECK_OVERFLOW(Len, Maxlen) ( (Len) >= (Maxlen) ) #define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) #define BNS_ERR -9999 #define BNS_WRONG_PARMS (BNS_ERR + 0) /*(-9999)*/ #define BNS_OUT_OF_RAM (BNS_ERR + 1) /*(-9998)*/ #define BNS_PROGRAM_ERR (BNS_ERR + 2) /*(-9997)*/ #define BNS_ALTPATH_OVFL (BNS_ERR + 3) /*(-9996)*/ #define BNS_BOND_ERR (BNS_ERR + 4) /*(-9995)*/ #define BNS_VERT_NUM_ERR (BNS_ERR + 5) /*(-9994)*/ #define BNS_VERT_EDGE_OVFL (BNS_ERR + 6) /*(-9993)*/ #define BNS_SET_ALTP_ERR (BNS_ERR + 7) /*(-9992)*/ #define BNS_CPOINT_ERR (BNS_ERR + 8) /*(-9991)*/ #define BNS_CANT_SET_BOND (BNS_ERR + 9) /*(-9990)*/ #define BNS_CAP_FLOW_ERR (BNS_ERR + 10) /*(-9989)*/ #define BNS_RADICAL_ERR (BNS_ERR + 11) /*(-9988)*/ #define BNS_REINIT_ERR (BNS_ERR + 12) /*(-9987)*/ #define BNS_ALTBOND_ERR (BNS_ERR + 13) /*(-9986)*/ #define BNS_MAX_ERR_VALUE (BNS_ERR + 19) /*(-9980)*/ #define IS_BNS_ERROR(X) (BNS_ERR <= (X) && (X) <= BNS_MAX_ERR_VALUE) #define INCHI_INP_ERROR_ERR 40 #define INCHI_INP_ERROR_RET (-1) #define INCHI_INP_FATAL_ERR 1 #define INCHI_INP_FATAL_RET 0 #define INCHI_INP_EOF_ERR 11 #define INCHI_INP_EOF_RET 0 #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif extern int (*UserAction)(void); /* callback */ extern int (*ConsoleQuit)(void); /* Console user issued CTRL+C etc. */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #define LOG_MASK_WARN 1 #define LOG_MASK_ERR 2 #define LOG_MASK_FATAL 4 #define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) #define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) #ifdef TARGET_LIB_FOR_WINCHI #include #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif extern void (*FWPRINT) (const char * format, va_list argptr ); extern void (*DRAWDATA) ( struct DrawData * pDrawData); extern int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ); extern struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif #define USER_ACTION_QUIT 1 #endif /* __INCHIERR_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiisot.c000066400000000000000000000114101271037650300235250ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #ifdef NEVER /**********************************************************************************/ int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ) { int is_negative; AT_ISO_SORT_KEY HOnlyAtwPart; static const AT_ISO_SORT_KEY MultAtwDiff = AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT; if ( !iso_sort_key ) { *num_1H = *num_2H = *num_3H = *iso_atw_diff = 0; return 0; } else if ( iso_sort_key < 0 ) { is_negative = 1; iso_sort_key = -iso_sort_key; HOnlyAtwPart = MultAtwDiff - iso_sort_key % MultAtwDiff; iso_sort_key += HOnlyAtwPart; } else { is_negative = 0; HOnlyAtwPart = iso_sort_key % MultAtwDiff; iso_sort_key -= HOnlyAtwPart; } iso_sort_key /= MultAtwDiff; *num_1H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; *num_2H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; *num_3H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); *iso_atw_diff = (S_CHAR)(is_negative? -iso_sort_key : iso_sort_key); return 1; } #endif /**********************************************************************************/ AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H) { AT_ISO_SORT_KEY iso_sort_key = 0, mult=1; iso_sort_key += mult * num_1H; mult *= AT_ISO_SORT_KEY_MULT; iso_sort_key += mult * num_2H; mult *= AT_ISO_SORT_KEY_MULT; iso_sort_key += mult * num_3H; mult *= AT_ISO_SORT_KEY_MULT; iso_sort_key += mult * iso_atw_diff; return iso_sort_key; } /**********************************************************************************/ /* set sp_ATOM isotopic sort keys */ int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ) { int i, num_isotopic = 0, bMergedTgroup; AT_ISO_SORT_KEY iso_sort_key; T_GROUP *t_group = (t_group_info && t_group_info->t_group && t_group_info->num_t_groups > 0)? t_group_info->t_group : NULL; if ( bHasIsotopicInTautomerGroups ) *bHasIsotopicInTautomerGroups = 0; for ( i = 0; i < num_at; i ++ ) { bMergedTgroup = (t_group_info && t_group_info->nIsotopicEndpointAtomNumber && (at[i].cFlags & AT_FLAG_ISO_H_POINT)); if ( (!at[i].endpoint || !t_group) && !bMergedTgroup ) { iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, at[i].num_iso_H[0], at[i].num_iso_H[1], at[i].num_iso_H[2]); } else { /* H isotopes go to the tautomer part of the CT (name) */ /* if (at[i].endpoint && t_group) ... */ iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, 0, 0, 0); if ( bHasIsotopicInTautomerGroups ) *bHasIsotopicInTautomerGroups += (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2] || bMergedTgroup); } at[i].iso_sort_key = iso_sort_key; num_isotopic += (iso_sort_key != 0); } return num_isotopic; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichilnct.c000066400000000000000000000325361271037650300235230ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include #include #include /* for use in the InChI library */ #include "mode.h" #include "util.h" #include "ichierr.h" #include "ichicomp.h" #include "ichi_io.h" #include "inchi_api.h" #define INChIToInchi_Atom l_INChIToInchi_Atom int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); /* This contains executable code. Included in lReadAux.c, e_ReadINCH.c, ReadINCH.c, */ #include "aux2atom.h" extern int bLibInchiSemaphore; /*****************************************************************************************************/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo ( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ) { int bDiffUnkUndfStereo = 0; return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, pInchiInp ); } EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo(char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ) { INCHI_IOSTREAM inp; int num_at, nRet = inchi_Ret_OKAY, err = 0; INCHI_MODE bChiral = 0; /* the input string may contain the following line: "Structure NNN. HHH=VVV" */ long lNumber; /* structure number NNN from the input */ char szHeader[MAX_SDF_HEADER]; /* stucture label header HHH from the input */ char szLabel[MAX_SDF_VALUE]; /* stucture label VVV from the input */ /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ int vABParityUnknown = AB_PARITY_UNDF; if ( 0 != bDiffUnkUndfStereo ) { /* Make labels for unknown and undefined stereo different */ vABParityUnknown = AB_PARITY_UNKN; } if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ return inchi_Ret_BUSY; } bLibInchiSemaphore = 1; if ( pInchiInp && pInchiInp->pInp ) { /* clear output fields */ inchi_Input *pInp = pInchiInp->pInp; char *szOptions = pInp->szOptions; memset( pInchiInp, 0, sizeof(*pInchiInp) ); memset( pInp, 0, sizeof(*pInp) ); pInp->szOptions = szOptions; pInchiInp->pInp = pInp; } else { bLibInchiSemaphore = 0; return inchi_Ret_ERROR; } szHeader[0] = '\0'; szLabel[0] = '\0'; lNumber = 0; /* prepare input string pointers */ inchi_ios_init(&inp, INCHI_IOSTREAM_STRING, NULL); /* fix bug discovered by Burt Leland 2008-12-23 */ inp.s.pStr = szInchiAuxInfo; inp.s.nUsedLength = strlen(szInchiAuxInfo); inp.s.nAllocatedLength = inp.s.nUsedLength+1; inp.s.nPtr = 0; num_at = INChIToInchi_Input( &inp, pInchiInp->pInp, 1, bDoNotAddH, vABParityUnknown, INPUT_INCHI_PLAIN, szHeader, szLabel, &lNumber, &bChiral, &err, pInchiInp->szErrMsg ); pInchiInp->bChiral = bChiral; if ( num_at <= 0 ) { if ( 10 < err && err < 20 ) { nRet = inchi_Ret_EOF; } else if ( err == 9 ) { nRet = inchi_Ret_ERROR; /* sdfile bypassed to $$$$ */ } else if ( err && err < 30 ) { nRet = inchi_Ret_FATAL; } else if ( 98 == err ) { nRet = inchi_Ret_WARNING; /* empty AuxInfo */ } else if ( err ) { nRet = inchi_Ret_ERROR; } else if ( pInchiInp->szErrMsg[0] ) { nRet = inchi_Ret_WARNING; } } if ( nRet != inchi_Ret_OKAY && nRet != inchi_Ret_WARNING ) { Free_inchi_Input( pInchiInp->pInp ); pInchiInp->bChiral = 0; } bLibInchiSemaphore = 0; return nRet; } /*****************************************************************************************************/ void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ) { Free_inchi_Input( pInp ); } void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ) { FreeInchi_Atom( &pInp->atom ); FreeInchi_Stereo0D ( &pInp->stereo0D ); pInp->num_atoms = 0; pInp->num_stereo0D = 0; } /*#endif*/ /* INCHI_MAIN */ #ifndef TARGET_API_LIB #error "TARGET_API_LIB MUST be defined here" #endif /**********************************************************************************/ int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) { /* inp_ATOM *at = NULL; */ int num_dimensions_new; int num_inp_bonds_new; int num_inp_atoms_new; int num_inp_0D_new; inchi_Atom *at_new = NULL; inchi_Atom *at_old = NULL; inchi_Stereo0D *stereo0D_new = NULL; inchi_Stereo0D *stereo0D_old = NULL; int nNumAtoms = 0, nNumStereo0D = 0; MOL_COORD *szCoordNew = NULL; MOL_COORD *szCoordOld = NULL; int i, j; if ( pStrErr ) { pStrErr[0] = '\0'; } /*FreeOrigAtData( orig_at_data );*/ if ( lSdfId ) *lSdfId = 0; do { at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; szCoordOld = NULL; num_inp_atoms_new = INChIToInchi_Atom( inp_molfile, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, bDoNotAddH, vABParityUnknown, nInputType, orig_at_data? &at_new:NULL, MAX_ATOMS, &num_dimensions_new, &num_inp_bonds_new, pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); if ( num_inp_atoms_new <= 0 && !*err ) { MOLFILE_ERR_SET (*err, 0, "Empty structure"); *err = 98; } else if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { *err = 0; /* end of file */ break; } else if ( num_inp_atoms_new > 0 && orig_at_data ) { /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; if ( nNumAtoms >= MAX_ATOMS ) { MOLFILE_ERR_SET (*err, 0, "Too many atoms"); *err = 70; orig_at_data->num_atoms = -1; } else if ( !at_old ) { /* the first structure */ orig_at_data->atom = at_new; at_new = NULL; orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; } else if ( orig_at_data->atom = CreateInchi_Atom( nNumAtoms ) ) { /* switch at_new <--> orig_at_data->at; */ if ( orig_at_data->num_atoms ) { memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); /* adjust numbering in the newly read structure */ for ( i = 0; i < num_inp_atoms_new; i ++ ) { for ( j = 0; j < at_new[i].num_bonds; j ++ ) { at_new[i].neighbor[j] += orig_at_data->num_atoms; } } } FreeInchi_Atom( &at_old ); /* copy newly read structure */ memcpy( orig_at_data->atom + orig_at_data->num_atoms, at_new, num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); /* copy newly read 0D stereo */ if ( num_inp_0D_new > 0 && stereo0D_new ) { if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) { memcpy( orig_at_data->stereo0D, stereo0D_old, orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]) ); /* adjust numbering in the newly read structure */ for ( i = 0; i < num_inp_0D_new; i ++ ) { if ( stereo0D_new[i].central_atom >= 0 ) { stereo0D_new[i].central_atom += orig_at_data->num_atoms; } for ( j = 0; j < 4; j ++ ) { stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; } } FreeInchi_Stereo0D( &stereo0D_old ); memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, stereo0D_new, num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]) ); } else { num_inp_0D_new = 0; MOLFILE_ERR_SET (*err, 0, "Out of RAM"); *err = -1; } } else { num_inp_0D_new = 0; } /* update lengths */ orig_at_data->num_atoms += num_inp_atoms_new; orig_at_data->num_stereo0D += num_inp_0D_new; } else { MOLFILE_ERR_SET (*err, 0, "Out of RAM"); *err = -1; } } else if ( num_inp_atoms_new > 0 ) { nNumAtoms += num_inp_atoms_new; } FreeInchi_Atom( &at_new ); num_inp_atoms_new = 0; FreeInchi_Stereo0D( &stereo0D_new ); num_inp_0D_new = 0; } while ( !*err && bMergeAllInputStructures ); /* if ( !*err ) { orig_at_data->num_components = MarkDisconnectedComponents( orig_at_data ); if ( orig_at_data->num_components == 0 ) { MOLFILE_ERR_SET (*err, 0, "No components found"); *err = 99; } if ( orig_at_data->num_components < 0 ) { MOLFILE_ERR_SET (*err, 0, "Too many components"); *err = 99; } } */ if ( szCoordNew ) { inchi_free( szCoordNew ); } if ( at_new ) { inchi_free( at_new ); } /* if ( !*err ) { if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) { MOLFILE_ERR_SET (*err, 0, "Cannot reconcile stereobond parities"); if (!orig_at_data->num_atoms) { *err = 1; } } } */ if ( *err ) { FreeInchi_Input( orig_at_data ); } if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ } return orig_at_data? orig_at_data->num_atoms : nNumAtoms; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimain.h000066400000000000000000000247171271037650300235160ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHIMAIN_H__ #define __INCHIMAIN_H__ #define ESC_KEY 27 #define INCHI_SEGM_BUFLEN 64000 /********************************************************************/ typedef struct tagStructData { unsigned long ulStructTime; int nErrorCode; int nErrorType; int nStructReadError; char pStrErrStruct[STR_ERR_LEN]; long fPtrStart; /* or number of processed structures */ long fPtrEnd; /* or number of errors */ int bXmlStructStarted; int bUserQuit; int bUserQuitComponent; int bUserQuitComponentDisplay; int bChiralFlag; /* information related to normal or disconnected layers */ int num_taut[INCHI_NUM]; int num_non_taut[INCHI_NUM]; INCHI_MODE bTautFlags[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ INCHI_MODE bTautFlagsDone[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ /* debugging info */ #if ( bRELEASE_VERSION == 0 ) int bExtract; #endif } STRUCT_DATA; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int process_single_input( int argc, char *argv[ ] ); int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, char *szSdfDataValue, unsigned long *ulDisplTime, int bReleaseVersion, INCHI_IOSTREAM *log_file); void HelpCommandLineParms(INCHI_IOSTREAM *f); int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ); #ifndef COMPILE_ANSI_ONLY int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H nNumRemovedProtonsIsotopic[], int bIsotopic, int j /*bTautomeric*/, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ); void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); #endif int PrintInputParms(INCHI_IOSTREAM *log_file, INPUT_PARMS *ip); const char *ErrMsg( int nErrorCode ); int SortAndPrintINChI(INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, INCHI_IOSTREAM *log_file, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], NORM_CANON_FLAGS *pncFlags, long num_inp, PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int *pSortPrintINChIFlags, unsigned char save_opt_bits); void FreeAllINChIArrays(PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int num_components[2]); void FreeINChIArrays(PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components ); void SplitTime(unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ); int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, int inp_index, int *out_index ); int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ); int GetOneComponent(STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, char *pStr, int nStrLen ); int CreateOneComponentINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, int i, long num_inp, INP_ATOM_DATA **inp_norm_data, NORM_CANON_FLAGS *pncFlags, INCHI_IOSTREAM *log_file); int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, char *pStr, int nStrLen ); int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, long num_inp, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, char *pStr, int nStrLen ); #if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); #endif void eat_keyboard_input( void ); int user_quit( const char *msg, unsigned long ulMaxTime ); int GetOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen, STRUCT_FPTRS *struct_fptrs ); int ProcessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, PINChI2 *pINChI2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, long num_inp, char *pStr, int nStrLen, unsigned char save_opt_bits); int CreateOneStructureINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, PINChI2 *pINChI2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], int iINChI, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], long num_inp, char *pStr, int nStrLen, NORM_CANON_FLAGS *pncFlags ); int bIsStructChiral(PINChI2 *pINChI2[INCHI_NUM], int num_components[]); int PreprocessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ); int FillOutOrigStruct(ORIG_ATOM_DATA *orig_inp_data, ORIG_STRUCT *pOrigStruct, STRUCT_DATA *sd); void FreeOrigStruct( ORIG_STRUCT *pOrigStruct); int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, /* the following are InChI library-specific parameters */ inp_ATOM **at, int *num_at, char *szMsg, int nMsgLen, unsigned long WarningFlags[2][2]); int CompareHillFormulasNoH(const char *f1, const char *f2, int *num_H1, int *num_H2); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHIMAIN_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimak2.c000066400000000000000000001522221271037650300234100ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include #include "mode.h" #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" #include "ichimain.h" #include "ichimake.h" int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, AT_NUMB *nTautomer, int lenTautomer, int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ); int MakeHillFormula( U_CHAR *nAtom, int num_atoms, char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ); #if ( FIX_DALKE_BUGS == 1 ) #else char *AllocateAndFillHillFormula( INChI *pINChI ); #endif int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ); int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, AT_STEREO_DBLE *LinearCTStereoDble, AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ); int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic , AT_STEREO_CARB *LinearCTStereoCarbInv , AT_STEREO_DBLE *LinearCTStereoDbleInv , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ); int GetHillFormulaIndexLength( int count ); int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); int CleanCoord( MOL_COORD szCoord, int delim ); /**********************************************************************************************/ int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) { int nLen; if ( szHillFormula && !*bOverflow ) { if ( nLen_szLinearCT > ( nLen = strlen(szHillFormula) ) ) { memcpy( szLinearCT, szHillFormula, nLen+1 ); return nLen; } *bOverflow |= 1; return nLen_szLinearCT+1; } return 0; } /********************************************************************************************** * MS Windows dependent: sprintf() is supposed to return the length of the output string * Carbon atoms are always first * Bridging hydrogen atoms are always last **********************************************************************************************/ int GetHillFormulaIndexLength( int count ) { char szCount[16]; if ( count > 1 ) { return sprintf( szCount, "%d", count ); } return 0; } /**********************************************************************************************/ int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, AT_NUMB *nTautomer, int lenTautomer, int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ) { char szElement[4]; U_CHAR nPrevAtom = (U_CHAR)-2; int bCarbon, bHydrogen, nElemLen, nFormLen, nNumNonHAtoms; int mult, i, num_H, num_C; num_H = 0; num_C = 0; bCarbon = 0; bHydrogen = 0; nElemLen = 0; nFormLen = 0; mult = 0; nNumNonHAtoms = num_atoms; for ( i = 0; i < num_atoms; i ++ ) { if ( nPrevAtom != nAtom[i] ) { if ( mult ) { if ( bHydrogen ) { num_H += mult; }else if ( bCarbon ) { num_C += mult; } else { nFormLen += nElemLen; nFormLen += GetHillFormulaIndexLength( mult ); } } if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { return -1; /* wrong element */ } mult = 1; nElemLen = strlen(szElement); nPrevAtom = nAtom[i]; bCarbon = !strcmp( szElement, "C" ); bHydrogen = !strcmp( szElement, "H" ); if ( bHydrogen ) { nNumNonHAtoms = i; } } else { mult ++; } num_H += nNum_H[i]; } /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...) */ if ( nTautomer && lenTautomer > 0 ) { int num_groups = nTautomer[0]; for ( i = 1; i < lenTautomer && num_groups > 0; i += nTautomer[i]+1, num_groups -- ) { num_H += nTautomer[i+1]; } } if ( mult ) { if ( bHydrogen ) { num_H += mult; } else if ( bCarbon ) { num_C += mult; } else { nFormLen += nElemLen; nFormLen += GetHillFormulaIndexLength( mult ); } } if ( num_C ) { nFormLen += strlen( "C" ); nFormLen += GetHillFormulaIndexLength( num_C ); } if ( num_H ) { nFormLen += strlen( "H" ); nFormLen += GetHillFormulaIndexLength( num_H ); } *pnum_C = num_C; *pnum_H = num_H; *pnLen = nFormLen; *pnNumNonHAtoms = nNumNonHAtoms; return 0; } /**********************************************************************************************/ int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ) { char szMult[16]; int len1, len2; if ( mult > 0 && !*bOverflow && 0 < (len1 = strlen( szElement )) ) { if ( mult > 1 ) { len2 = sprintf( szMult, "%d", mult ); } else { len2 = 0; szMult[0] = '\0'; } if ( len1 + len2 < nLenLinearCT ) { memcpy( szLinearCT, szElement, len1 ); memcpy( szLinearCT+len1, szMult, len2+1 ); /* adding zero termination */ return len1+len2; } else { (*bOverflow) ++; } } return 0; } /**********************************************************************************************/ /* if num_C > 0 then nAtom does not contain C or H */ /* otherwise all elements are in alphabetic order */ int MakeHillFormula( U_CHAR *nAtom, int num_atoms, char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ) { char szElement[4]; int mult, compare2H; int i, nLen, bOvfl; U_CHAR nPrevAtom; nLen = 0; mult = 0; bOvfl = 0; nPrevAtom = (U_CHAR)-2; /* non-existent number */ if ( num_C ) { nLen += AddElementAndCount( "C", num_C, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); if ( num_H ) { nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); num_H = 0; } } for ( i = 0; i < num_atoms; i ++ ) { if ( nPrevAtom != nAtom[i] ) { if ( mult ) { nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); } mult = 1; if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { return -1; /* wrong element */ } nPrevAtom = nAtom[i]; if ( !strcmp( "C", szElement ) ) { return -1; } compare2H = strcmp( "H", szElement ); if ( !compare2H ) { return -1; } if ( compare2H < 0 && num_H ) { /* H-atom should be located in front of szElement */ nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); num_H = 0; } } else { mult ++; } } if ( mult ) { /* the last element if any */ nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); } if ( num_H ) { /* if H has not been output... */ nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); } *bOverflow |= (0 != bOvfl); return bOvfl? nLen_szLinearCT+1: nLen; } /**********************************************************************************************/ char *AllocateAndFillHillFormula( INChI *pINChI ) { int num_C, num_H, nLen, nNumNonHAtoms, ret, bOverflow; char *pHillFormula = NULL; bOverflow = 0; if ( !GetHillFormulaCounts( pINChI->nAtom, pINChI->nNum_H, pINChI->nNumberOfAtoms, pINChI->nTautomer, pINChI->lenTautomer, &num_C, &num_H, &nLen, &nNumNonHAtoms ) ) { if ( pHillFormula = (char*) inchi_malloc( nLen+1 ) ) { ret = MakeHillFormula( pINChI->nAtom+num_C, nNumNonHAtoms-num_C, pHillFormula, nLen+1, num_C, num_H, &bOverflow ); if ( ret != nLen || bOverflow ) { inchi_free( pHillFormula ); pHillFormula = NULL; } } } return pHillFormula; } /************************************************************************************/ /* return value: 0 => copied to stereo bonds; 1=> Allene copied to stereocenters */ /* on input nNumberOfStereoBonds==NULL means second call, use Stereo->...Inv */ /************************************************************************************/ int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, AT_STEREO_DBLE *LinearCTStereoDble, AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ) { int cumulene_len, j, next_j /* ordering number of the central allene atom */, next_neigh; AT_RANK at_num; int parity; if ( pCanonOrd && pCanonRank ) { j = pCanonOrd[(int)LinearCTStereoDble->at_num1-1]; /* if allene then find the central atom, at[next_j] */ if ( bIsotopic ) { cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity2[0]); if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor2[1]) ) { next_j = at[j].neighbor[(int)at[j].stereo_bond_ord2[0]]; for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { next_neigh = (j == at[next_j].neighbor[0]); j = next_j; next_j = at[next_j].neighbor[next_neigh]; } /* next_j is the central atom */ } else { cumulene_len = -1; /* not an allene */ } } else { cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity[0]); if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor[1]) ) { next_j = at[j].neighbor[(int)at[j].stereo_bond_ord[0]]; for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { next_neigh = (j == at[next_j].neighbor[0]); j = next_j; next_j = at[next_j].neighbor[next_neigh]; } } else { cumulene_len = -1; /* not an allene */ } } if ( !cumulene_len ) { /* allene has been found; insert new stereocenter and parity */ AT_NUMB *nNumber; S_CHAR *t_parity; nNumber = nNumberOfStereoBonds? Stereo->nNumber : Stereo->nNumberInv; t_parity = nNumberOfStereoBonds? Stereo->t_parity : Stereo->t_parityInv; at_num = pCanonRank[next_j]; parity = LinearCTStereoDble->parity; /* free room for the new stereocenter */ for ( j = 0; j < *nNumberOfStereoCenters && Stereo->nNumber[j] < at_num; j ++ ) ; if ( j < *nNumberOfStereoCenters ) { memmove( nNumber + j + 1, nNumber + j, (*nNumberOfStereoCenters-j)*sizeof(nNumber[0]) ); memmove( t_parity + j + 1, t_parity + j, (*nNumberOfStereoCenters-j)*sizeof(t_parity[0]) ); } /* fill the new stereo center info */ nNumber[j] = at_num; t_parity[j] = parity; (*nNumberOfStereoCenters) ++; return 1; } } /* save the stereo bond info */ if ( nNumberOfStereoBonds ) { j = *nNumberOfStereoBonds; Stereo->b_parity[j] = LinearCTStereoDble->parity; Stereo->nBondAtom1[j] = LinearCTStereoDble->at_num1; Stereo->nBondAtom2[j] = LinearCTStereoDble->at_num2; (*nNumberOfStereoBonds) ++; } return 0; } /***************************************************************************/ int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic , AT_STEREO_CARB *LinearCTStereoCarbInv , AT_STEREO_DBLE *LinearCTStereoDbleInv , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ) { int n, i, nErrorCode = 0, len; int bAllene; int diff; int lenInv, bAlleneInv; /* stereo centers */ n = Stereo->nNumberOfStereoCenters = nLenLinearCTStereoCarb; for ( i = 0; i < n; i ++ ) { Stereo->nNumber[i] = LinearCTStereoCarb[i].at_num; Stereo->t_parity[i] = LinearCTStereoCarb[i].parity; Stereo->nNumberInv[i] = LinearCTStereoCarbInv[i].at_num; Stereo->t_parityInv[i] = LinearCTStereoCarbInv[i].parity; } /* stereo bonds */ n = nLenLinearCTStereoDble; lenInv = Stereo->nNumberOfStereoCenters; for ( i = len = 0; i < n; i ++ ) { bAllene = Copy2StereoBondOrAllene( Stereo, &Stereo->nNumberOfStereoCenters, &len, LinearCTStereoDble+i, pCanonOrd, pCanonRank, at, bIsotopic ); bAlleneInv = Copy2StereoBondOrAllene( Stereo, &lenInv, NULL, LinearCTStereoDbleInv+i, pCanonOrdInv, pCanonRankInv, at, bIsotopic ); /* make sure double bond stereo is identical in original and inverted geometry */ /* Note: all allenes are AFTER double bonds in LinearCTStereoDble... */ if ( bAllene != bAlleneInv || !bAllene && CompareLinCtStereoDble ( LinearCTStereoDble+i, 1, LinearCTStereoDbleInv+i, 1 ) ) { nErrorCode = -4; /* double bond stereo Inv is NOT identical to Abs */ goto exit_function; } } Stereo->nNumberOfStereoBonds = len; if ( lenInv != Stereo->nNumberOfStereoCenters ) { nErrorCode = -5; /* different number of stereo centers in Abs and Inv */ goto exit_function; } /* compare inverted stereocenters to absolute */ n = Stereo->nNumberOfStereoCenters; diff = 0; for ( i = 0, diff = 0; i < n; i ++ ) { if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { diff = (Stereo->nNumberInv[i] > Stereo->nNumber[i])? 2 : -2; break; /* Abs != Inv */ } if ( Stereo->t_parityInv[i] != Stereo->t_parity[i] ) { diff = (Stereo->t_parityInv[i] > Stereo->t_parity[i])? 1 : -1; break; /* Abs != Inv */ } } Stereo->nCompInv2Abs = (diff > 0)? 1 : (diff < 0)? -1 : 0; if ( diff == -1 || diff == 1 ) { /* the first found difference was in parities */ for ( i = 0, diff = 0; i < n; i ++ ) { if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { diff = 2; /* difference in stereo center numbering */ break; } /* parities can be only 1, 2, 3, 4. Therefore only mutually inverted pairs * (t_parityInv, t_parity) = (1,2) or (2,1) statisfy conditions * (t_parityInv != t_parity) && (t_parityInv + t_parity == 3) */ if ( Stereo->t_parityInv[i] == Stereo->t_parity[i] || Stereo->t_parityInv[i] + Stereo->t_parity[i] != 3 ) { diff = 1; /* parities are same or different and cannot be obtained by simple inversion */ break; } } Stereo->bTrivialInv = !diff; } else { Stereo->bTrivialInv = 0; } exit_function: return nErrorCode; } /***************************************************************************/ int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ) { int n, i, j1, j2, num, mark_atom, mark_bond; if ( !pCanonOrd ) return -1; num = 0; n = nLenLinearCTStereoCarb; mark_atom = bIsotopic? AMBIGUOUS_STEREO_ATOM_ISO : AMBIGUOUS_STEREO_ATOM; for ( i = 0; i < n; i ++ ) { /* mark ambiguous stereo centers (for displaying and "Ambiguous stereo" message) */ if ( ATOM_PARITY_NOT_UNKN(LinearCTStereoCarb[i].parity) && at[j1=pCanonOrd[(int)LinearCTStereoCarb[i].at_num-1]].bAmbiguousStereo ) { at[j1].bAmbiguousStereo |= mark_atom; norm_at[j1].bAmbiguousStereo |= mark_atom; num ++; } } n = nLenLinearCTStereoDble; mark_bond = bIsotopic? AMBIGUOUS_STEREO_BOND_ISO : AMBIGUOUS_STEREO_BOND; for ( i = 0; i < n; i ++ ) { /* mark ambiguous stereo bonds or allenes (for displaying and "Ambiguous stereo" message) */ if ( ATOM_PARITY_WELL_DEF(LinearCTStereoDble[i].parity) ) { j1=pCanonOrd[(int)LinearCTStereoDble[i].at_num1-1]; j2=pCanonOrd[(int)LinearCTStereoDble[i].at_num2-1]; if ( at[j1].bAmbiguousStereo || at[j2].bAmbiguousStereo ) { /* if it is an allene then mark the central atom only because the bonds should not be marked to avoid misleading message "Ambiguous stereo: bond(s)": Allene makes a stereocenter */ int j1_parity = bIsotopic? at[j1].stereo_bond_parity2[0] : at[j1].stereo_bond_parity[0]; int cumulene_len = BOND_CHAIN_LEN(j1_parity); /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !(bIsotopic? at[j1].stereo_bond_neighbor2[1] : at[j1].stereo_bond_neighbor[1] )) ) { /* found an allene; locate its central atom */ int next_j, next_neigh; int j = j1; next_j = at[j].neighbor[bIsotopic? at[j].stereo_bond_ord2[0] : at[j].stereo_bond_ord[0] ]; for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { next_neigh = (j == at[next_j].neighbor[0]); j = next_j; next_j = at[next_j].neighbor[next_neigh]; } /* next_j is the central atom */ if ( 2==at[next_j].valence ) { at[next_j].bAmbiguousStereo |= mark_atom; norm_at[next_j].bAmbiguousStereo |= mark_atom; num ++; continue; /* do not mark the cumulene "bond" endpoints */ } } /* not an allene, mark double bond or cumulene end atoms */ if ( at[j1].bAmbiguousStereo ) { at[j1].bAmbiguousStereo |= mark_bond; /* ??? */ norm_at[j1].bAmbiguousStereo |= mark_bond; num ++; } if ( at[j2].bAmbiguousStereo ) { at[j2].bAmbiguousStereo |= mark_bond; /* ??? */ norm_at[j2].bAmbiguousStereo |= mark_bond; num ++; } } } } return num; } /**********************************************************************************************/ INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ) { INCHI_MODE nRet = 0; int i, n; if ( !Stereo || Stereo && !Stereo->nNumberOfStereoCenters && !Stereo->nNumberOfStereoBonds) { return nRet; } /* stereocenters */ if ( !Stereo->nCompInv2Abs && (n=Stereo->nNumberOfStereoCenters) > 0 && (nUserMode & REQ_MODE_SC_IGN_ALL_UU) ) { for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]); i ++ ) ; if ( i == n ) { Stereo->nNumberOfStereoCenters = 0; for ( i = 0; i < n; i ++ ) { Stereo->t_parity[i] = 0; Stereo->nNumber[i] = 0; Stereo->t_parityInv[i] = 0; Stereo->nNumberInv[i] = 0; } nRet |= REQ_MODE_SC_IGN_ALL_UU; } } /* stereobonds */ if ( (n=Stereo->nNumberOfStereoBonds) > 0 && (nUserMode & REQ_MODE_SB_IGN_ALL_UU) ) { for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]); i ++ ) ; if ( i == n ) { Stereo->nNumberOfStereoBonds = 0; for ( i = 0; i < n; i ++ ) { Stereo->b_parity[i] = 0; Stereo->nBondAtom1[i] = 0; Stereo->nBondAtom2[i] = 0; } nRet |= REQ_MODE_SB_IGN_ALL_UU; } } return nRet; } #if ( defined(TARGET_API_LIB) || ADD_CMLPP==1 ) /**********************************************************************************************/ void WriteCoord( char *str, double x ) { if ( x < -9999999.9 ) { sprintf( str, "%10.2e", x ); } else if ( x < -999999.99 ) { sprintf( str, "%10.2f", x ); } else if ( x < -99999.999 ) { sprintf( str, "%10.3f", x ); } else if ( x < 99999.9999 ) { sprintf( str, "%10.4f", x ); } else if ( x < 999999.999 ) { sprintf( str, "%10.3f", x ); } else if ( x < 9999999.99 ) { sprintf( str, "%10.2f", x ); } else if ( x < 99999999.9 ) { sprintf( str, "%10.1f", x ); } else { sprintf( str, "%10.3e", x ); } } #endif /* used CANON_STAT members pCS->LinearCT pCS->LinearCTIsotopic pCS->LinearCTIsotopicStereoCarb pCS->LinearCTIsotopicStereoCarbInv pCS->LinearCTIsotopicStereoDble pCS->LinearCTIsotopicStereoDbleInv pCS->LinearCTIsotopicTautomer pCS->LinearCTStereoCarb pCS->LinearCTStereoCarbInv pCS->LinearCTStereoDble pCS->LinearCTStereoDbleInv pCS->nCanonOrd pCS->nCanonOrdIsotopic pCS->nCanonOrdIsotopicStereo pCS->nCanonOrdIsotopicStereoInv pCS->nCanonOrdIsotopicStereoTaut pCS->nCanonOrdIsotopicTaut pCS->nCanonOrdStereo pCS->nCanonOrdStereoInv pCS->nCanonOrdStereoTaut pCS->nCanonOrdTaut pCS->nLenCanonOrd pCS->nLenCanonOrdIsotopic pCS->nLenCanonOrdIsotopicStereo pCS->nLenCanonOrdIsotopicStereoTaut pCS->nLenCanonOrdIsotopicTaut pCS->nLenCanonOrdStereo pCS->nLenCanonOrdStereoTaut pCS->nLenCanonOrdTaut pCS->nLenLinearCTAtOnly pCS->nLenLinearCTIsotopic pCS->nLenLinearCTIsotopicStereoCarb pCS->nLenLinearCTIsotopicStereoDble pCS->nLenLinearCTIsotopicTautomer pCS->nLenLinearCTStereoCarb pCS->nLenLinearCTStereoDble pCS->nNum_H pCS->nNum_H_fixed pCS->nSymmRank pCS->nSymmRankIsotopic pCS->nSymmRankIsotopicTaut pCS->nSymmRankTaut pCS->t_group_info pCS->t_group_info->num_t_groups */ /**********************************************************************************************/ int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, int num_atoms, int num_at_tg, int num_removed_H, sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, INCHI_MODE nUserMode, char *pStrErrStruct ) { int i, j, m, n, g, len, ii, ret=0; AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; T_GROUP_INFO *t_group_info = pCS->t_group_info; T_GROUP *t_group; int nErrorCode = 0; AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; AT_RANK nMinOrd; INChI_Stereo *Stereo; int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; INCHI_MODE nStereoUnmarkMode; /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ /* Check for warnings */ if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { nErrorCode |= WARN_FAILED_STEREO; } if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { nErrorCode |= WARN_FAILED_ISOTOPIC; } if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; } pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ if ( !pCanonRankAtoms || !pSortOrd ) { nErrorCode = 0; ret = CT_OUT_OF_RAM; /* */ pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; goto exit_function; } /* total charge */ for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { n += at[i].charge; } pINChI->nTotalCharge = n; /* number of atoms */ pINChI->nNumberOfAtoms = num_atoms; pINChI_Aux->nNumberOfAtoms = num_atoms; /* removed protons and detachable isotopic H */ if ( bTautomeric && t_group_info ) { pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; } if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; } if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { AddMOLfileError(pStrErrStruct, "Proton(s) added/removed"); } if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { AddMOLfileError(pStrErrStruct, "Charges neutralized"); } } /* abs or rel stereo may establish one of two canonical numberings */ if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && pCS->nLenCanonOrdStereo > 0 && (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv ) { pCanonRank = pCanonRankAtoms; pCanonOrd = pCS->nCanonOrdStereo; pCanonRankInv = pSortOrd; pCanonOrdInv = pCS->nCanonOrdStereoInv; Stereo = pINChI->Stereo; for ( i = 0; i < num_at_tg; i ++ ) { pCanonRankInv[pCanonOrdInv[i]] = pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } /********************************************************************/ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ /********************************************************************/ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ , pCS->LinearCTStereoCarbInv , pCS->LinearCTStereoDbleInv , pCanonOrdInv, pCanonRankInv ); if ( Stereo->t_parityInv && Stereo->nNumberInv ) { if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { pINChI->nFlags |= INCHI_FLAG_REL_STEREO; } if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; } if ( Stereo->nCompInv2Abs ) { if ( Stereo->nCompInv2Abs == -1 ) { /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ AT_NUMB *nNumberInv = Stereo->nNumberInv; S_CHAR *t_parityInv = Stereo->t_parityInv; Stereo->nNumberInv = Stereo->nNumber; Stereo->t_parityInv = Stereo->t_parity; Stereo->nNumber = nNumberInv; Stereo->t_parity = t_parityInv; /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ } } } for ( i = 0; i < num_atoms; i ++ ) { pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } if ( bUseNumberingInv ) { /* switch ptrs back to avoid confusion */ switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); /* save inverted stereo ranks & order because it represents the smallest (relative) */ memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); /* change pCS->nCanonOrdStereo[] to inverted: */ memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); } pCanonRankInv = NULL; pCanonOrdInv = NULL; pOrigNosInCanonOrd = NULL; } else { /*------------------------------ no stereo */ pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; pCanonRank = pCanonRankAtoms; pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_atoms; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } for ( ; i < num_at_tg; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } } } /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ if ( pINChI_Aux->OrigInfo ) { /* charges, radicals, valences */ for ( i = 0; i < num_atoms; i ++ ) { ii = pCanonOrd[i]; if ( norm_at[ii].valence || norm_at[ii].num_H ) { pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : norm_at[ii].radical? 3 : 0 ; pINChI_Aux->OrigInfo[i].cUnusualValence = get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); } else { /* charge of a single atom component is in the INChI; valence = 0 is standard */ pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : norm_at[ii].radical? 3 : 0 ; } } } /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ pSymmRank = pCS->nSymmRank; if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { for ( i = 0; i < num_atoms; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ pSortOrd[i] = i; } for ( ; i < num_at_tg; i ++ ) { pSortOrd[i] = MAX_ATOMS; /* for debugging only */ } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent atoms: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ } /* at this point j == i */ } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ } nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ } } } else { nErrorCode |= ERR_NO_CANON_RESULTS; ret = -1; /* program error; no breakpoint here */ goto exit_function; } /* atomic numbers from the Periodic Table */ for ( i = 0; i < num_atoms; i ++ ) { pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; } /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { nErrorCode |= ERR_NO_CANON_RESULTS; ret = -2; goto exit_function; } memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; /* tautomeric group(s) canonical representation */ len = 0; if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) { /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; /* number of tautomeric groups */ pINChI->nTautomer[len ++] = (AT_NUMB)n; /* store each tautomeric group, one by one */ for ( i = 0; i < n; i ++ ) { g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; /* Num(H), Num(-) */ for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) pINChI->nTautomer[len ++] = t_group->num[j]; for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) pINChI->nTautomer[len ++] = 0; /* should not happen */ /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ for ( j = (int)t_group->nFirstEndpointAtNoPos, m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ } } pINChI->lenTautomer = len; pINChI_Aux->nNumberOfTGroups = n; } else { pINChI->lenTautomer = 0; pINChI_Aux->nNumberOfTGroups = 0; if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { /* only protons (re)moved or added */ pINChI->lenTautomer = 1; pINChI->nTautomer[0] = 0; } } /* number of H (excluding tautomeric) */ if ( pCS->nNum_H ) { for ( i = 0; i < num_atoms; i ++ ) { pINChI->nNum_H[i] = pCS->nNum_H[i]; } } /* number of fixed H (tautomeric H in non-tautomeric representation) */ if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { for ( i = 0; i < num_atoms; i ++ ) { pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; } } /*********************************************************** * tautomeric group(s) numbering and symmetry; * should not depend on switching to rel. stereo numbering */ if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; pSymmRank = pCS->nSymmRankTaut; if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { for ( i = 0; i < n; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; /* make is start from 1, not from zero */ if ( j - i > 1 ) { /* found a sequence of more than one equivalent t-groups: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; } nMinOrd = pSortOrd[j]; /* at the end j == n */ } } } } /* Allocate and fill Hill formula */ if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { nErrorCode = 0; ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; goto exit_function; } if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); } } /*************************/ /* mark ambiguous stereo */ /*************************/ MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); /************************************************************************ * * isotopic part */ /* abs or rel stereo may establish one of two canonical numberings */ if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && pCS->nLenCanonOrdIsotopicStereo > 0 && (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv ) { /* found isotopic stereo */ pCanonRank = pCanonRankAtoms; pCanonOrd = pCS->nCanonOrdIsotopicStereo; pCanonRankInv = pSortOrd; pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; Stereo = pINChI->StereoIsotopic; for ( i = 0; i < num_at_tg; i ++ ) { pCanonRankInv[pCanonOrdInv[i]] = pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } /********************************************************************/ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ /********************************************************************/ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble , pCanonOrd, pCanonRank, at, 1 /* isotopic */ , pCS->LinearCTIsotopicStereoCarbInv , pCS->LinearCTIsotopicStereoDbleInv , pCanonOrdInv, pCanonRankInv ); if ( Stereo->t_parityInv && Stereo->nNumberInv ) { if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { pINChI->nFlags |= INCHI_FLAG_REL_STEREO; } if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; } if ( Stereo->nCompInv2Abs ) { if ( Stereo->nCompInv2Abs == -1 ) { /* switch pointers so that the stereo becomes the smallest (relative) */ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ AT_NUMB *nNumberInv = Stereo->nNumberInv; S_CHAR *t_parityInv = Stereo->t_parityInv; Stereo->nNumberInv = Stereo->nNumber; Stereo->t_parityInv = Stereo->t_parity; Stereo->nNumber = nNumberInv; Stereo->t_parity = t_parityInv; switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); bUseIsotopicNumberingInv = 1; } } } for ( i = 0; i < num_atoms; i ++ ) { pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } if ( bUseIsotopicNumberingInv ) { switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); } pCanonRankInv = NULL; pCanonOrdInv = NULL; pOrigNosInCanonOrd = NULL; } else { /* no isotopic stereo */ pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; pCanonRank = pCanonRankAtoms; pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } } } /*pCanonOrdIso = pCanonOrd;*/ pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; pSymmRank = pCS->nSymmRankIsotopic; if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { for ( i = 0; i < num_atoms; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; pSortOrd[i] = i; } for ( ; i < num_at_tg; i ++ ) { pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent atoms: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ } nMinOrd = pSortOrd[j]; } } } else { goto exit_function; /* no isotopic info available */ } /* isotopic atoms */ n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; for ( i = 0; i < n; i ++ ) { pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; } /* isotopic tautomeric groups */ n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; for ( i = 0; i < n; i ++ ) { pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; } /* atoms that may exchange isotopic H-atoms */ if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { for ( i = 0, j = 1; i < num_atoms; i ++ ) { if ( pCS->nExchgIsoH[i] ) { pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ } } pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ } if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); } } /* mark ambiguous stereo */ MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); /*********************************************************** * isotopic tautomeric group(s) numbering and symmetry; * should not depend on switching to rel. stereo numbering */ if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && t_group_info && t_group_info->num_t_groups > 0 ) { n = t_group_info->num_t_groups; pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : pCS->nLenCanonOrdIsotopicTaut > 0? (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; pSymmRank = pCS->nSymmRankIsotopicTaut; if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { for ( i = 0; i < n; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent t-groups: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ } nMinOrd = pSortOrd[j]; /* at the end j = n */ } } } } exit_function: if ( pCanonRankAtoms ) inchi_free( pCanonRankAtoms ); if ( pSortOrd ) inchi_free( pSortOrd ); pINChI->nErrorCode |= nErrorCode; pINChI_Aux->nErrorCode |= nErrorCode; return ret; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimake.c000066400000000000000000006306611271037650300235030ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #if ( TEST_RENUMB_ATOMS == 1 ) #include "ichitime.h" #endif #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" #include "ichimain.h" #include "ichimake.h" #include "ichister.h" #include "ichi_io.h" int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); int GetElementAndCount( const char **f, char *szEl, int *count ); int CompareHillFormulas( const char *f1, const char *f2 ); int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ); int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */); int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, AT_NUMB *nOrigAtNosInCanonOrd, int num_at ); int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers); int CheckCanonNumberingCorrectness( int num_atoms, int num_at_tg, sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, char *pStrErrStruct ); static int CompareDfsDescendants4CT( const void *a1, const void *a2 ); int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ); #if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); #endif #if ( READ_INCHI_STRING == 1 ) /* { */ /*************************************************************************************/ int CompareReversedStereoINChI2( INChI_Stereo *s1, INChI_Stereo *s2, ICR *picr); #endif /**********************************************************************************************/ int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ) { int i, j, val; memset( at, 0, sizeof(at[0])*num_inp_at ); for ( i = 0; i < num_inp_at; i ++ ) { strncpy( at[i].elname, inp_at[i].elname, sizeof(at[0].elname) ); at[i].el_number = (U_CHAR)get_periodic_table_number( at[i].elname ); val = at[i].valence = inp_at[i].valence; for ( j = 0; j < val; j ++ ) { at[i].neighbor[j] = inp_at[i].neighbor[j]; at[i].bond_type[j] = inp_at[i].bond_type[j]; } at[i].chem_bonds_valence = inp_at[i].chem_bonds_valence; at[i].orig_at_number = inp_at[i].orig_at_number; at[i].orig_compt_at_numb= inp_at[i].orig_compt_at_numb; at[i].endpoint = inp_at[i].endpoint; at[i].iso_atw_diff = inp_at[i].iso_atw_diff; at[i].num_H = inp_at[i].num_H; at[i].cFlags = inp_at[i].cFlags; for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { at[i].num_iso_H[j] = inp_at[i].num_iso_H[j]; } at[i].charge = inp_at[i].charge; at[i].radical = inp_at[i].radical; #if ( FIND_RING_SYSTEMS == 1 ) at[i].nBlockSystem = inp_at[i].nBlockSystem; at[i].bCutVertex = inp_at[i].bCutVertex; at[i].nRingSystem = inp_at[i].nRingSystem; at[i].nNumAtInRingSystem = inp_at[i].nNumAtInRingSystem; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) at[i].nDistanceFromTerminal = inp_at[i].nDistanceFromTerminal; #endif #endif /* at[i].x = inp_at[i].x; at[i].y = inp_at[i].y; at[i].z = inp_at[i].z; */ } return 0; } /**********************************************************************************************/ int GetElementAndCount( const char **f, char *szEl, int *count ) { const char *p = *f; char *q; int i = 0; if ( *p ) { if ( isupper( UCINT *p ) ) { szEl[i++] = *p++; if ( *p && islower( UCINT *p ) ) { szEl[i++] = *p++; } szEl[i] = '\0'; if ( 1 == i && szEl[0] == 'C' ) { szEl[0] = 'A'; /* less than any element: */ /* carbon-containing compounds should be first */ } if ( *p && isdigit( UCINT *p ) ) { *count = strtol( p, &q, 10 ); p = q; } else { *count = 1; } *f = p; /* next element; */ return 1; } return -1; /* not a chemical formula */ } strcpy( szEl, "Zz" ); /* zero termination 'element' is larger than any other element */ *count = 9999; /* zero termination 'element count' is larger than any other count */ return 0; } /********************************************************************************************** E1 < E2 if strcmp( E1, E2) < 0 OR E2 is empty and E1 is not n1 < n2 if value n1 > n2 Sorting order: C10H22N C10H22 C2 Ag2Cl2 Ag2Cl Ag2F2 Ag2 AgCl AgF F6S F2S **********************************************************************************************/ int CompareHillFormulas( const char *f1, const char *f2 ) { char szEl1[4], szEl2[4]; int count1, count2, ret1, ret2, ret; do { ret1 = GetElementAndCount( &f1, szEl1, &count1 ); ret2 = GetElementAndCount( &f2, szEl2, &count2 ); if ( 0 <= ret1 && 0 <= ret2 ) { if ( ret = strcmp( szEl1, szEl2 ) ) { return ret; /* lexicographic order, string termination > any character */ } if ( ret = count2 - count1 ) { return ret; /* inverse atom count order */ } } else { return 0; /* program error */ } } while ( 0 < ret1 && 0 < ret2 ); return 0; } /**********************************************************************************************/ int CompareHillFormulasNoH( const char *f1, const char *f2, int *num_H1, int *num_H2 ) { char szEl1[4], szEl2[4]; int count1, count2, ret1, ret2, ret; do { ret1 = GetElementAndCount( &f1, szEl1, &count1 ); if ( 0 < ret1 && szEl1[0] == 'H' && !szEl1[1] ) { *num_H1 += count1; ret1 = GetElementAndCount( &f1, szEl1, &count1 ); } ret2 = GetElementAndCount( &f2, szEl2, &count2 ); if ( 0 < ret2 && szEl2[0] == 'H' && !szEl2[1] ) { *num_H2 += count2; ret2 = GetElementAndCount( &f2, szEl2, &count2 ); } if ( 0 <= ret1 && 0 <= ret2 ) { if ( ret = strcmp( szEl1, szEl2 ) ) { return ret; /* lexicographic order, string termination > any character */ } if ( ret = count2 - count1 ) { return ret; /* inverse atom count order */ } } else { return 0; /* program error */ } } while ( 0 < ret1 && 0 < ret2 ); return 0; } /**************************************************************/ int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ) { int len1, len2, ret, i; len1 = i1->lenTautomer > 0 && i1->nTautomer[0]? i1->lenTautomer:0; len2 = i2->lenTautomer > 0 && i2->nTautomer[0]? i2->lenTautomer:0; if ( ret = len2 - len1 ) { return ret; } for ( i = 0; i < len1; i ++ ) { if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) return ret; } return 0; } /**********************************************************************************************/ /* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ /**********************************************************************************************/ int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic) { int ret, num, i, num_H1, num_H2; const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ /* INChI_Stereo *Stereo1, *Stereo2; */ n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; i1 = p1->pINChI[n1]; i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; /* non-deleted-non-empty < deleted < empty */ if ( i1 && !i2 ) return 0; /* non-empty is the smallest (first) */ if ( !i1 && i2 ) return 0; if ( !i1 && !i2 ) return 0; if ( i1->bDeleted ) return 1; /* deleted is the largest (last) among non-empty */ if ( i2->bDeleted ) return -1; if ( i1->nNumberOfAtoms > 0 && !i2->nNumberOfAtoms ) return 0; i2 = i2; num_H1 = num_H2 = 0; /* do not compare terminal H */ if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ } /********************************************************* compare non-isotopic non-tautomeric part *********************************************************/ /* compare number of atoms (excluding terminal H) */ if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) return ret; /* more atoms first */ /* compare elements (excluding terminal H) */ num = i1->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) return ret; /* greater periodic number first */ } /********************************************************** compare connection tables ***********************************************************/ if ( ret = i2->lenConnTable - i1->lenConnTable ) return ret; /* longer connection table first */ num = i2->lenConnTable; for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) return ret; /* greater connection table first */ } /********************************************************* compare compare total number of H (inverse: H3 < H2 ) **********************************************************/ if ( ret = num_H2 - num_H1 ) return ret; /********************************************************* compare non-tautomeric num_H: N < NH3 < NH2 < NH **********************************************************/ num = i1->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { if ( i2->nNum_H[i] != i1->nNum_H[i] ) { return !i2->nNum_H[i]? 1 : /* no H first */ !i1->nNum_H[i]? -1 : (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; } } /********************************************************* compare non-isotopic tautomeric part *********************************************************/ if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { return ret; } /* if ( ret = i2->lenTautomer - i1->lenTautomer ) return ret; num = inchi_min( i2->lenTautomer, i1->lenTautomer ); for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) return ret; } */ /********************************************************* * * * at this point both components are either tautomeric * * or non-tautomeric * * * *********************************************************/ /********************************************************* non-tautomeric "fixed H" specific *********************************************************/ if ( /*TAUT_NON == bTaut &&*/ (i2 && i2->nNum_H_fixed ) ) { /* first, compare non-tautomeric chem. formulas -- they may be different */ /* secondly, compare fixed-H distribution */ if ( i2->nNum_H_fixed ) { num = i2->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { if ( i2->nNum_H_fixed[i] != 0 ) { return 1; } } } } /********************************************************* compare non-isotopic stereo *********************************************************/ ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); if ( ret ) { return ret; } /******************************************************* do not switch back to tautomeric i1, i2 *******************************************************/ /* -- how to switch back -- if ( i1t ) { i1 = i1t; i1t = NULL; } if ( i2t ) { i2 = i2t; i2t = NULL; } */ /****************************************************** compare isotopic non-tautomeric part ******************************************************/ if ( bCompareIsotopic ) { if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) return ret; num = i1->nNumberOfIsotopicAtoms; /* compare isotopic atoms */ for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) return ret; } /* compare isotopic H */ /* if tautomeric comparison mode then here are compared only non-tautomeric H */ for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) return ret; } /***************************************************** compare isotopic tautomeric part *****************************************************/ if ( ret = i2->nNumberOfIsotopicTGroups || i1->nNumberOfIsotopicTGroups ) return ret; /* num = i1->nNumberOfIsotopicTGroups; for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) return ret; } */ /**************************************************** compare isotopic stereo ****************************************************/ ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); if ( ret ) { return ret; } } /********************************************************** compare charges: non-charged first, then in order of ascending charges (negative first) ***********************************************************/ if ( i2->nTotalCharge && i1->nTotalCharge ) { /* both are charged; smaller charges first */ ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; return ret; } if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { /* only one is charged; uncharged first */ return ret; } /* stable sort */ /*ret = p1->ord_number - p2->ord_number;*/ return ret; } /*************************** stereo ***********************************************************/ typedef enum tagSp3StereoTypeTmp { SP3_NONE = 0, /* no sp3 stereo: no /t, /m, /s segments */ /* /t is present: */ SP3_ONLY = 1, /* no /s or /m segment: inversion leaves the structure unchanged */ SP3_ABS = 2, /* abs stereo: both /m and /s are present */ SP3_REL = 4, /* rel stereo: /s is present, /m is not */ SP3_RAC = 8, /* racemic stereo: /s is presen, /m is nott */ SP3_TYPE = (SP3_ABS|SP3_REL|SP3_RAC), /* bitmap for checking the presence of /m */ SP3_ANY = (SP3_ABS|SP3_REL|SP3_RAC|SP3_ONLY) /* bitmap for checking the presence of /t */ } SP3_TYPE_TMP; /**********************************************************************************************/ int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ) { int nRet = SP3_NONE; if ( pINChI && !pINChI->bDeleted && Stereo && 0 < Stereo->nNumberOfStereoCenters ) { if ( 0 != Stereo->nCompInv2Abs ) { if ( pINChI->nFlags & INCHI_FLAG_REL_STEREO ) { #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( 1 < Stereo->nNumberOfStereoCenters ) { nRet = SP3_REL; } #else nRet = SP3_REL; #endif } else if ( pINChI->nFlags & INCHI_FLAG_RAC_STEREO ) { #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( 1 < Stereo->nNumberOfStereoCenters ) { nRet = SP3_REL; } #else nRet = SP3_RAC; #endif } else { nRet = SP3_ABS; } } else #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( !(( pINChI->nFlags & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO) ) && 1 == Stereo->nNumberOfStereoCenters) ) #endif { nRet = SP3_ONLY; /* SP3_NONE if relative stereo and 1 stereocenter */ } } return nRet; } /* char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; */ /**********************************************************************************************/ /* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ /**********************************************************************************************/ int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ) { int ret = 0, num, i, num_H1, num_H2; const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ INChI_Stereo *Stereo1, *Stereo2; INChI_Stereo *IsoStereo1, *IsoStereo2; int bRelRac[DIFL_LENGTH]; char *psDifSegs; n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; i1 = p1->pINChI[n1]; i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; num_H1 = num_H2 = 0; memset( bRelRac, DIFV_BOTH_EMPTY, sizeof(bRelRac) ); /*=====================*/ /*==== /f ======*/ /*=====================*/ if ( i1 && !i1->bDeleted && i1->szHillFormula && i1->szHillFormula[0] ) { sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { if ( !CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) && num_H1 == num_H2 ) { sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_EQL2PRECED; } else { sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; } } else { sDifSegs[DIFL_F][DIFS_f_FORMULA] |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } } else { sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; } else { sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; } } /*=====================*/ /*==== /c ======*/ /*=====================*/ if ( i1 && !i1->bDeleted && i1->lenConnTable > 1 ) { sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; } else { sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; } /*=====================*/ /*==== /h ======*/ /*=====================*/ /* M: H atoms */ if ( i1 && !i1->bDeleted ) { num_H1 = (i1->lenTautomer > 0 && i1->nTautomer && i1->nTautomer[0])? 1 : 0; /* number of t-groups */ if ( !num_H1 && i1->nNum_H ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { /* immobile H */ if ( i1->nNum_H[i] ) { num_H1 = 1; break; } } } sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= num_H1? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; } else { sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; } /* F: fixed mobile H */ if ( i2 && !i2->bDeleted && i2->nNum_H_fixed ) { num_H2 = 0; if ( i1 && !i1->bDeleted ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { num_H2 = 1; break; } } } sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= num_H2? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; } else { sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; } /* MI: exchangable isotopic H: see OutputINChI1(), num_iso_H[] */ /*=====================*/ /*==== /q ======*/ /*=====================*/ psDifSegs = &sDifSegs[DIFL_F][DIFS_q_CHARGE]; if ( i1 && !i1->bDeleted ) { if ( i1->nTotalCharge ) { sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; } else { sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; } if ( i2 && !i2->bDeleted ) { if ( i1->nTotalCharge ) { if ( i1->nTotalCharge == i2->nTotalCharge ) { *psDifSegs |= DIFV_EQL2PRECED; } else if ( i2->nTotalCharge ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_IS_EMPTY; } } else { if ( i2->nTotalCharge ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } } else if ( !i2 ) { if (bFixTranspChargeBug==1) { /* bug explanation: component #1 is tautomeric, component #2 is not Mobile-H(#2) > Mobile-H(#1) Fixed-H(#2) = Mobile-H(#2) < Fixed-H(#1) Layer first_charge second_charge Mobile-H 0 (comp#1) -1 (comp#2) Fixed-H none (comp#2) -1 (comp#1) v1.01 charge compared decided that charge layers are same and omitted Fixed-H /q layer Solution: when component permutation is detected AND fixed-H component does not exist, compare Mobile-H charge [0 (comp#1) in the example] to the charge of Mobile-H [-1 (comp#2)] of the component that has none Fixed-H charge */ /* Fixed-H i2 is empty because Fixed-H struct is same as Mobile-H */ if ( p1->ord_number != p2->ord_number && /* component order in Fixed-H is different from Mobile-H */ n1 == TAUT_YES && p2->pINChI[TAUT_YES] && !p2->pINChI[TAUT_YES]->bDeleted && p2->pINChI[TAUT_YES]->nNumberOfAtoms ) { int i2_nTotalCharge = p2->pINChI[TAUT_YES]->nTotalCharge; if ( i1->nTotalCharge ) { if ( i1->nTotalCharge == i2_nTotalCharge ) { *psDifSegs |= DIFV_EQL2PRECED; } else if ( i2_nTotalCharge ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_IS_EMPTY; } } else { if ( i2_nTotalCharge ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } } else { *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; } } else /* if (bFixTranspChargeBug==1) */ { *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; } } else /* if ( !i2 ) { */ { /* i2 && i2->bDeleted */ *psDifSegs |= i1->nTotalCharge? DIFV_IS_EMPTY : DIFV_BOTH_EMPTY; } } else { sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; if ( i2 && !i2->bDeleted ) { if ( i2->nTotalCharge ) { sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; } else { sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; } } } /*************** stereo *****************/ if ( i1 && !i1->bDeleted ) { Stereo1 = i1->Stereo; IsoStereo1 = i1->StereoIsotopic; } else { Stereo1 = NULL; IsoStereo1 = NULL; } if ( i2 && !i2->bDeleted ) { Stereo2 = i2->Stereo; IsoStereo2 = i2->StereoIsotopic; } else { Stereo2 = NULL; IsoStereo2 = NULL; } /*=====================*/ /*==== /b ======*/ /*=====================*/ /* M double bond stereo */ psDifSegs = &sDifSegs[DIFL_M][DIFS_b_SBONDS]; if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* F double bond stereo */ psDifSegs = &sDifSegs[DIFL_F][DIFS_b_SBONDS]; if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { if ( Eql_INChI_Stereo( Stereo1, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else { if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } /* MI double bond stereo */ psDifSegs = &sDifSegs[DIFL_MI][DIFS_b_SBONDS]; if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds ) { if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP2, Stereo1, EQL_SP2, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else { if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } /* FI double bond stereo */ psDifSegs = &sDifSegs[DIFL_FI][DIFS_b_SBONDS]; if ( IsoStereo2 && IsoStereo2->nNumberOfStereoBonds ) { if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { if ( !(Stereo1 && Stereo1->nNumberOfStereoBonds) && !(Stereo2 && Stereo2->nNumberOfStereoBonds) && Eql_INChI_Stereo( IsoStereo2, EQL_SP2, IsoStereo1, EQL_SP2, 0 ) ) { *psDifSegs |= DIFV_FI_EQ_MI; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } } else { /* the solution table for FI stereo, in case of FI stereo is empty E = segment is empty, NE = not empty +==============================+ | M | MI | F | result | +=====+=====+=====+============+ | E | E | E | both empty | +-----+-----+-----+------------+ | NE | E | E | both empty | +-----+-----+-----+------------+ | E | NE | E | is empty | +-----+-----+-----+------------+ | NE | NE | E | both empty | +-----+-----+-----+------------+ | E | E | NE | is empty | +-----+-----+-----+------------+ | NE | E | NE | is empty | +-----+-----+-----+------------+ | E | NE | NE | is empty | +-----+-----+-----+------------+ | NE | NE | ME | is empty | +==============================+ */ if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds && !(Stereo1 && Stereo1->nNumberOfStereoBonds) ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } /*==================================*/ /*==== /t, /m, /s for M ======*/ /*==================================*/ /* M sp3 stereo */ bRelRac[DIFL_M ] = GetSp3RelRacAbs( i1, Stereo1 ); /* Mobile-H */ bRelRac[DIFL_MI] = GetSp3RelRacAbs( i1, IsoStereo1 ); bRelRac[DIFL_F ] = GetSp3RelRacAbs( i2, Stereo2 ); /* Fixed-H */ bRelRac[DIFL_FI] = GetSp3RelRacAbs( i2, IsoStereo2 ); if ( SP3_NONE != bRelRac[DIFL_M] ) { sDifSegs[DIFL_M][DIFS_t_SATOMS] |= (bRelRac[DIFL_M] & SP3_ANY)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; sDifSegs[DIFL_M][DIFS_m_SP3INV] |= (bRelRac[DIFL_M] & SP3_ABS)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; sDifSegs[DIFL_M][DIFS_s_STYPE] |= (bRelRac[DIFL_M] & SP3_TYPE)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; } else { sDifSegs[DIFL_M][DIFS_t_SATOMS] |= DIFV_BOTH_EMPTY; sDifSegs[DIFL_M][DIFS_m_SP3INV] |= DIFV_BOTH_EMPTY; sDifSegs[DIFL_M][DIFS_s_STYPE] |= DIFV_BOTH_EMPTY; } /*=====================*/ /*==== /t ======*/ /*=====================*/ /* F sp3 stereo */ psDifSegs = &sDifSegs[DIFL_F][DIFS_t_SATOMS]; if ( SP3_ANY & bRelRac[DIFL_F] ) { if ( Eql_INChI_Stereo( Stereo2, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else if ( SP3_ANY & bRelRac[DIFL_M] ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* MI sp3 stereo */ psDifSegs = &sDifSegs[DIFL_MI][DIFS_t_SATOMS]; if ( SP3_ANY & bRelRac[DIFL_MI] ) { if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else if ( SP3_ANY & bRelRac[DIFL_M] ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* FI sp3 stereo */ psDifSegs = &sDifSegs[DIFL_FI][DIFS_t_SATOMS]; if ( SP3_ANY & bRelRac[DIFL_FI] ) { if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP3, Stereo2, EQL_SP3, 0 ) ) { *psDifSegs |= DIFV_EQL2PRECED; } else if ( !(SP3_ANY & bRelRac[DIFL_M]) && !(SP3_ANY & bRelRac[DIFL_F]) && Eql_INChI_Stereo( IsoStereo2, EQL_SP3, IsoStereo1, EQL_SP3, 0 ) ) { *psDifSegs |= DIFV_FI_EQ_MI; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else /* similar to /b */ if ( (SP3_ANY & bRelRac[DIFL_F]) ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else if ( (SP3_ANY & bRelRac[DIFL_MI]) && !(SP3_ANY & bRelRac[DIFL_M]) ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /*=====================*/ /*==== /m ======*/ /*=====================*/ /* F sp3 abs stereo inversion */ psDifSegs = &sDifSegs[DIFL_F][DIFS_m_SP3INV]; if ( bRelRac[DIFL_F] & SP3_ABS ) { /* the order of || operands below is critically important: || is not a commutative operation */ if ( !(bRelRac[DIFL_M] & SP3_ABS) || Stereo2->nCompInv2Abs != Stereo1->nCompInv2Abs ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_EQL2PRECED; } } else if ( bRelRac[DIFL_M] & SP3_ABS ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* MI sp3 abs stereo inversion */ psDifSegs = &sDifSegs[DIFL_MI][DIFS_m_SP3INV]; if ( SP3_ABS & bRelRac[DIFL_MI] ) { if ( (SP3_ABS & bRelRac[DIFL_M]) && IsoStereo1->nCompInv2Abs == Stereo1->nCompInv2Abs ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else if ( SP3_ABS & bRelRac[DIFL_M] ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* FI sp3 abs stereo inversion */ psDifSegs = &sDifSegs[DIFL_FI][DIFS_m_SP3INV]; if ( SP3_ABS & bRelRac[DIFL_FI] ) { if ( (SP3_ABS & bRelRac[DIFL_F]) && IsoStereo2->nCompInv2Abs == Stereo2->nCompInv2Abs ) { *psDifSegs |= DIFV_EQL2PRECED; } else if ( !(SP3_ABS & bRelRac[DIFL_M]) && !(SP3_ABS & bRelRac[DIFL_F]) && (SP3_ABS & bRelRac[DIFL_MI]) && /* make sure IsoStereo1 != NULL */ IsoStereo2->nCompInv2Abs == IsoStereo1->nCompInv2Abs ) { *psDifSegs |= DIFV_FI_EQ_MI; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else /* similar to /b */ /* the order of || operands below is critically important: || is no a commutative operation */ if ( (SP3_ABS & bRelRac[DIFL_F]) ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else if ( (SP3_ABS & bRelRac[DIFL_MI]) && !(SP3_ABS & bRelRac[DIFL_M]) ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /*=====================*/ /*==== /s ======*/ /*=====================*/ /* F sp3 stereo type */ psDifSegs = &sDifSegs[DIFL_F][DIFS_s_STYPE]; if ( bRelRac[DIFL_F] & SP3_TYPE ) { if ( (bRelRac[DIFL_F] & SP3_TYPE) == (bRelRac[DIFL_M] & SP3_TYPE) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else if ( bRelRac[DIFL_M] & SP3_TYPE ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* MI sp3 stereo type */ psDifSegs = &sDifSegs[DIFL_MI][DIFS_s_STYPE]; if ( SP3_TYPE & bRelRac[DIFL_MI] ) { if ( (SP3_TYPE & bRelRac[DIFL_MI]) == (SP3_TYPE & bRelRac[DIFL_M]) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else if ( SP3_TYPE & bRelRac[DIFL_M] ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* FI sp3 stereo type */ psDifSegs = &sDifSegs[DIFL_FI][DIFS_s_STYPE]; if ( SP3_TYPE & bRelRac[DIFL_FI] ) { if ( (SP3_TYPE & bRelRac[DIFL_FI]) == (SP3_TYPE & bRelRac[DIFL_F]) ) { *psDifSegs |= DIFV_EQL2PRECED; } else if ( !(SP3_TYPE & bRelRac[DIFL_M]) && !(SP3_TYPE & bRelRac[DIFL_F]) && (SP3_TYPE & bRelRac[DIFL_MI]) ) { *psDifSegs |= DIFV_FI_EQ_MI; } else { *psDifSegs |= DIFV_NEQ2PRECED; } } else /* similar to /b */ /* the order of || operands below is critically important: || is not a commutative operation */ if ( (SP3_TYPE & bRelRac[DIFL_F]) ) { *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ } else if ( (SP3_TYPE & bRelRac[DIFL_MI]) && !(SP3_TYPE & bRelRac[DIFL_M]) ) { *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /*=====================*/ /*==== /o ======*/ /*=====================*/ if ( p1 && p2 && p1->ord_number != p2->ord_number ) { sDifSegs[DIFL_F][DIFS_o_TRANSP] |= DIFV_NEQ2PRECED; } /*=====================*/ /*==== /i ======*/ /*=====================*/ /* M isotopic atoms */ psDifSegs = &sDifSegs[DIFL_MI][DIFS_i_IATOMS]; if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } /* F isotopic atoms */ psDifSegs = &sDifSegs[DIFL_FI][DIFS_i_IATOMS]; if ( i2 && !i2->bDeleted ) { if ( i2->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups ) { if ( !i1 || i1->bDeleted || i2->nNumberOfIsotopicAtoms != i1->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups != i1->nNumberOfIsotopicTGroups ) { *psDifSegs |= DIFV_NEQ2PRECED; } else { int diff; num = i1->nNumberOfIsotopicAtoms; diff = 0; for ( i = 0; i < num; i ++ ) { /* compare isotopic atoms */ if ( diff = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) break; if ( diff = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) break; /* compare isotopic H */ if ( diff = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) break; if ( diff = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) break; if ( diff = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) break; } if ( !diff ) { num = i1->nNumberOfIsotopicTGroups; for ( i = 0; i < num; i ++ ) { if ( diff = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) break; if ( diff = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) break; if ( diff = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) return diff; if ( diff = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) break; } } *psDifSegs |= diff? DIFV_NEQ2PRECED : DIFV_FI_EQ_MI; } } else if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { *psDifSegs |= DIFV_IS_EMPTY; } } else if ( !i2 ) { if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { *psDifSegs |= DIFV_EQL2PRECED; } else { *psDifSegs |= DIFV_BOTH_EMPTY; } } return ret; } /**********************************************************************************************/ int INChI_SegmentAction( char cDifSegs ) { if ( !(cDifSegs & DIFV_OUTPUT_OMIT_F) ) { return INCHI_SEGM_OMIT; } if ( (cDifSegs & DIFV_OUTPUT_EMPTY_T) && !(cDifSegs & DIFV_OUTPUT_EMPTY_F) ) { return INCHI_SEGM_EMPTY; } if ( (cDifSegs & DIFV_OUTPUT_FILL_T) ) { return INCHI_SEGM_FILL; } return INCHI_SEGM_OMIT; /* the control flow shoul never reach this point */ } /**********************************************************************************************/ int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ) { /**************************************************** 1. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI and there is NO succeeding non-empty layer then mark the 1st element of the layer DIFV_BOTH_EMPTY; this layerr will be omitted. 2. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI and there IS a succeeding non-empty layer then mark the 1st element of the layer DIFV_IS_EMPTY and all other elements DIFV_BOTH_EMPTY; only the first empty segment of this layerr will be output. 3. If NOT all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI and the 1st element of the layer is DIFV_BOTH_EMPTY then mark it DIFV_IS_EMPTY; it will be output as empty (except M layer). */ int i, nLayer, sBits, nFirstSegm; #define nFirstFmlSegm DIFS_f_FORMULA #define nFirstIsoSegm DIFS_i_IATOMS /* FI */ nLayer = DIFL_FI; nFirstSegm = nFirstIsoSegm; sBits = 0; for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { sBits |= sDifSegs[nLayer][i]; } if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { /* Omit the FI layer */ memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); } else if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; } /* MI */ nLayer = DIFL_MI; nFirstSegm = nFirstIsoSegm; sBits = 0; for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { sBits |= sDifSegs[nLayer][i]; } if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { /* Omit the MI layer */ memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); } else if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; } /* F */ nLayer = DIFL_F; nFirstSegm = nFirstFmlSegm; sBits = 0; for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { sBits |= sDifSegs[nLayer][i]; } if ( !(sBits & DIFV_OUTPUT_OMIT_F) && sDifSegs[DIFL_FI][nFirstIsoSegm] == DIFV_BOTH_EMPTY ) { /* Omit the F layer: no non-iotopic and no isotopic segments */ memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); } else /* do not omit fixed-H layer */ if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; } /* M -- leave as it is */ return 0; #undef nFirstFmlSegm #undef nFirstIsoSegm } /*********************************************************************************************/ int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ) { int i, num, ret; if ( Stereo2 && Stereo1 ) { /* compare stereogenic bonds */ num = inchi_min( Stereo2->nNumberOfStereoBonds, Stereo1->nNumberOfStereoBonds ); for ( i = 0; i < num; i ++ ) { if ( ret = (int)Stereo2->nBondAtom1[i] - (int)Stereo1->nBondAtom1[i] ) return ret; if ( ret = (int)Stereo2->nBondAtom2[i] - (int)Stereo1->nBondAtom2[i] ) return ret; if ( ret = (int)Stereo2->b_parity[i] - (int)Stereo1->b_parity[i] ) return ret; } if ( ret = (int)Stereo2->nNumberOfStereoBonds - (int)Stereo1->nNumberOfStereoBonds ) return ret; /* compare stereogenic atoms */ #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( ((nFlags1 | nFlags2) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) ) && 1 == Stereo2->nNumberOfStereoCenters && 1 == Stereo1->nNumberOfStereoCenters ) { ; /* do not compare single stereocenters in case of relative stereo */ } else #endif { num = inchi_min( Stereo2->nNumberOfStereoCenters, Stereo1->nNumberOfStereoCenters ); for ( i = 0; i < num; i ++ ) { if ( ret = (int)Stereo2->nNumber[i] - (int)Stereo1->nNumber[i] ) return ret; if ( ret = (int)Stereo2->t_parity[i] - (int)Stereo1->t_parity[i] ) return ret; } if ( ret = (int)Stereo2->nNumberOfStereoCenters - (int)Stereo1->nNumberOfStereoCenters ) return ret; /* compare stereo-abs-is-inverted flags for non-relative, non-racemic */ if ( !((nFlags1 | nFlags2) & (INCHI_FLAG_RAC_STEREO | INCHI_FLAG_REL_STEREO)) ) { if ( ret = (Stereo2->nCompInv2Abs < 0) - (Stereo1->nCompInv2Abs < 0) ) { return ret; } } } } else if ( Stereo2 && ( Stereo2->nNumberOfStereoBonds > 0 || Stereo2->nNumberOfStereoCenters > 0 #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) && /* do not compare single stereocenters in case of relative stereo */ !((nFlags2 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && 1 == Stereo2->nNumberOfStereoCenters ) #endif ) ) { return 1; }else if ( Stereo1 && ( Stereo1->nNumberOfStereoBonds > 0 || Stereo1->nNumberOfStereoCenters > 0 #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) && /* do not compare single stereocenters in case of relative stereo */ !((nFlags1 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && 1 == Stereo1->nNumberOfStereoCenters ) #endif ) ) { return -1; } return 0; } /**********************************************************************************************/ /* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ /**********************************************************************************************/ int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic) { int ret, num, i, num_H1, num_H2; const INChI *i1 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ const INChI *i2 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ int n2; /* TAUT_YES if tautomeric i2 exists, otherwise TAUT_NON */ const INChI *i1n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ const INChI *i2n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ /*const INChI *i1t = NULL;*/ /* temp for i1 if both tautomeric AND non-tautomeric exist */ /*const INChI *i2t = NULL;*/ /* temp for i2 if both tautomeric AND non-tautomeric exist */ /* INChI_Stereo *Stereo1, *Stereo2; */ n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; n2 = ( p2->pINChI[TAUT_YES] && p2->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; i1 = p1->pINChI[n1]; i1n = (n1 == TAUT_YES && p1->pINChI[TAUT_NON] && p1->pINChI[TAUT_NON]->nNumberOfAtoms)? p1->pINChI[TAUT_NON] : (const INChI *)NULL; i2 = p2->pINChI[n2]; i2n = (n2 == TAUT_YES && p2->pINChI[TAUT_NON] && p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; /* non-deleted-non-empty < deleted < empty */ if ( i1 && !i2 ) return -1; /* non-empty is the smallest (first) */ if ( !i1 && i2 ) return 1; if ( !i1 && !i2 ) return 0; if ( i1->bDeleted && !i2->bDeleted ) return 1; /* deleted is the largest (last) among non-empty */ if ( !i1->bDeleted && i2->bDeleted ) return -1; num_H1 = num_H2 = 0; /* do not compare terminal H */ if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ } /********************************************************* compare non-isotopic non-tautomeric part *********************************************************/ /* compare number of atoms (excluding terminal H) */ if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) return ret; /* more atoms first */ /* compare elements (excluding terminal H) */ num = i1->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) return ret; /* greater periodic number first */ } /********************************************************** compare connection tables ***********************************************************/ if ( ret = i2->lenConnTable - i1->lenConnTable ) return ret; /* longer connection table first */ num = i2->lenConnTable; for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) return ret; /* greater connection table first */ } /********************************************************* compare compare total number of H (inverse: H3 < H2 ) **********************************************************/ if ( ret = num_H2 - num_H1 ) return ret; /********************************************************* compare non-tautomeric num_H: N < NH3 < NH2 < NH **********************************************************/ num = i1->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { if ( i2->nNum_H[i] != i1->nNum_H[i] ) { return !i2->nNum_H[i]? 1 : /* no H first */ !i1->nNum_H[i]? -1 : (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; } } /********************************************************* compare non-isotopic tautomeric part *********************************************************/ if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { return ret; } /* if ( ret = i2->lenTautomer - i1->lenTautomer ) return ret; num = inchi_min( i2->lenTautomer, i1->lenTautomer ); for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) return ret; } */ /********************************************************* * * * at this point both components are either tautomeric * * or non-tautomeric * * * *********************************************************/ /********************************************************* non-tautomeric "fixed H" specific *********************************************************/ if ( TAUT_NON == bTaut && (i1n && i1n->nNum_H_fixed || i2n && i2n->nNum_H_fixed) ) { /* first, compare non-tautomeric chem. formulas -- they may be different */ const char *f1 = (i1n /*&& i1n->nNum_H_fixed*/)? i1n->szHillFormula : i1->szHillFormula; const char *f2 = (i2n /*&& i2n->nNum_H_fixed*/)? i2n->szHillFormula : i2->szHillFormula; if ( f1 && f2 &&(ret = CompareHillFormulas( f1, f2 ))) { return ret; } /* secondly, compare fixed-H distribution */ if ( i1n && i1n->nNum_H_fixed && i2n && i2n->nNum_H_fixed ) { num = inchi_min( i1n->nNumberOfAtoms, i2n->nNumberOfAtoms); for ( i = 0; i < num; i ++ ) { if ( i2n->nNum_H_fixed[i] != i1n->nNum_H_fixed[i] ) { return !i2n->nNum_H_fixed[i]? 1 : /* no fixed H first */ !i1n->nNum_H_fixed[i]? -1 : (int)i2n->nNum_H_fixed[i] - (int)i1n->nNum_H_fixed[i]; } } if ( ret = (int)i2n->nNumberOfAtoms - (int)i1n->nNumberOfAtoms ) { return ret; /* should not happen */ } } else if ( i1n && i1n->nNum_H_fixed ) { num = i1n->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ if ( i1n->nNum_H_fixed[i] ) { return -1; /* i1n->nNum_H_fixed[i] > 0? -1:1;*/ } } /* p1 is tautomeric, p2 is not tautomeric; this must have been detected earlier */ /*return -1;*/ /* has fixed H first *//* */ /* removed 2004-05-04 */ } else { num = i2n->nNumberOfAtoms; for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ if ( i2n->nNum_H_fixed[i] ) { return 1; /* i2n->nNum_H_fixed[i] > 0? 1:-1;*/ } } /* p2 is tautomeric, p1 is not tautomeric; this must have been detected earlier */ /*return 1; */ /* has fixed H first *//* */ /* removed 2004-05-04 */ } } /************************************************************************* if requested non-tautomeric comparison then prepare to compare non-taut non-isotopic stereo, etc. *************************************************************************/ if ( TAUT_NON == bTaut ) { if ( i1n ) { /*i1t = i1;*/ i1 = i1n; } if ( i2n ) { /*i2t = i2;*/ i2 = i2n; } } /********************************************************* compare non-isotopic stereo *********************************************************/ ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); if ( ret ) { return ret; } /******************************************************* do not switch back to tautomeric i1, i2 *******************************************************/ /* -- how to switch back -- if ( i1t ) { i1 = i1t; i1t = NULL; } if ( i2t ) { i2 = i2t; i2t = NULL; } */ /****************************************************** compare isotopic non-tautomeric part ******************************************************/ if ( bCompareIsotopic ) { if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) return ret; num = i1->nNumberOfIsotopicAtoms; /* compare isotopic atoms */ for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) return ret; } /* compare isotopic H */ /* if tautomeric comparison mode then here are compared only non-tautomeric H */ for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) return ret; if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) return ret; } /***************************************************** compare isotopic tautomeric part *****************************************************/ if ( ret = i2->nNumberOfIsotopicTGroups - i1->nNumberOfIsotopicTGroups ) return ret; num = i1->nNumberOfIsotopicTGroups; for ( i = 0; i < num; i ++ ) { if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) return ret; if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) return ret; } /**************************************************** compare isotopic stereo ****************************************************/ ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); if ( ret ) { return ret; } } /********************************************************** compare charges: non-charged first, then in order of ascending charges (negative first) ***********************************************************/ if ( i2->nTotalCharge && i1->nTotalCharge ) { /* both are charged; smaller charges first */ ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; return ret; } if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { /* only one is charged; uncharged first */ return ret; } /* stable sort */ /*ret = p1->ord_number - p2->ord_number;*/ return ret; } /***********************************************************************/ int CompINChINonTaut2(const void *p1, const void *p2) { int ret; ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); #if ( CANON_FIXH_TRANS == 1 ) if ( !ret ) { /* to obtain canonical transposition 2004-05-10 */ ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); } #endif if ( !ret ) { /* stable sort */ ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; } return ret; } /***********************************************************************/ int CompINChITaut2(const void *p1, const void *p2) { int ret; ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); #if ( CANON_FIXH_TRANS == 1 ) if ( !ret ) { /* to obtain canonical transposition 2004-05-10 */ ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); } #endif if ( !ret ) { /* stable sort */ ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; } return ret; } /**********************************************************************************************/ /* strrev from K&R is not in ANSI-compatible C library */ void mystrrev( char *p ) { char c, *q = p; while( *q++ ) ; q -= 2; /* pointer to the last character */ while ( p < q ) { c = *q; /* swap */ *q-- = *p; *p++ = c; } } /*****************************************************************************************/ /* Find DFS order for CT(canon. numbers and Hs) output */ /*****************************************************************************************/ static AT_NUMB *gDfs4CT_nDfsNumber; static AT_NUMB *gDfs4CT_nNumDescendants; static int gDfs4CT_nCurrentAtom; /**********************************************************************************************/ static int CompareDfsDescendants4CT( const void *a1, const void *a2 ) { int neigh1 = (int)*(const AT_RANK*)a1; int neigh2 = (int)*(const AT_RANK*)a2; if ( neigh1 > MAX_ATOMS ) { if ( neigh2 > MAX_ATOMS ) { return 0; } return 1; } else if ( neigh2 > MAX_ATOMS ) { return -1; } else { AT_RANK nCurDfsNumber = gDfs4CT_nDfsNumber[gDfs4CT_nCurrentAtom]; int nDesc1 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh1]? 0 : (int)gDfs4CT_nNumDescendants[neigh1]; int nDesc2 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh2]? 0 : (int)gDfs4CT_nNumDescendants[neigh2]; int ret; if ( ret = nDesc1 - nDesc2 ) { return ret; } return (int)neigh1 - (int)neigh2; /* canon. numbers difference */ } } /**********************************************************************************************/ /* sp_ATOM *at, AT_RANK *nRank, int num_atoms */ AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ) { AT_NUMB *nStackAtom = NULL; int nTopStackAtom=-1; AT_NUMB *nNumDescendants = NULL; /* number of descendants incl. closures and the atom itself */ AT_NUMB *nDfsNumber = NULL; S_CHAR *cNeighNumb = NULL; NEIGH_LIST *nl = NULL; AT_NUMB nDfs; int i, j, u, k, start, num_rings, nTotOutputStringLen; AT_NUMB *nOutputString = NULL, cDelim; int bCtPredecessors = (nCtMode & CT_MODE_PREDECESSORS); /* int nNumStartChildren; */ /* allocate arrays */ nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); nNumDescendants = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nNumDescendants[0])); nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); nl = CreateNeighListFromLinearCT( LinearCT, nLenCT, num_atoms ); /* check allocation */ if ( !nStackAtom || !nNumDescendants || !nDfsNumber || !cNeighNumb || !nl ) { /* ret = CT_OUT_OF_RAM; */ /* program error */ /* */ goto exit_function; } if ( bCtPredecessors ) { start = 0; } else { /* find DFS start vertex (atom) */ for ( i = 1, start = 0; i < num_atoms; i ++ ) { if ( nl[i][0] < nl[start][0] ) { /* index = nRank-1 */ start = i; } } } /* vertex information: 1. Number of (forward edges) + (back edges, first visit -- ring closures): nl[i][0] 2. Number of vertices traversed from this vertex, including the vertex: nNumDescendants[i] 3. Each edge information: a. forward edge (0) or back edge (1) indicator: nDfsNumber[i] > nDfsNumber[neigh] b. neighbor at another end of the edge neigh = nl[i][k+1], k < i Total per edge: 2 + 2*(number of edges) */ /* DFS initiation */ u = start; /* start atom */ nDfs = 0; nTopStackAtom =-1; memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); memset( nNumDescendants, 0, num_atoms*sizeof(nNumDescendants[0])); memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nDfsNumber[u] = ++nDfs; if ( bCtPredecessors ) { nNumDescendants[u] = 0; /* atom #1 has no predecessor */ } else { nNumDescendants[u] = 1; /* count itself as a descendant */ } nStackAtom[++nTopStackAtom] = (AT_NUMB)u; /* nNumStartChildren = 0; */ num_rings = 0; /* DFS */ do { /* advance */ while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ /* replaced due to missing sequence point; undefined behavior, pointed by Geoffrey Hutchison */ { cNeighNumb[i] ++; u = (int)nl[i][j]; /* jth neighbor of the vertex i */ if ( !nDfsNumber[u] ) { /* tree edge, 1st visit -- advance */ /* put unexplored vertex u on the stack for further examination */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nDfsNumber[u] = ++nDfs; if ( bCtPredecessors ) { nNumDescendants[u] = i+1; /* predecessor's rank */ } else { nNumDescendants[u] ++; /* count atom u as its descendant */ } } else if ( nTopStackAtom && u != (int)nStackAtom[nTopStackAtom-1] && /* back edge: u is not a predecessor of i */ nDfsNumber[u] < nDfsNumber[i] ) { /* Back edge, 1st visit: u is an ancestor of i (ring closure) */ if ( !bCtPredecessors ) { nNumDescendants[i] ++; /* count closures as descendants */ } num_rings ++; /* count ring closures */ } else { nl[i][j] = MAX_ATOMS+1; /* back edge, 2nd visit: mark as deleted */ } } cNeighNumb[i] = 0; /* all neighbors of the ith atom have been traversed; resore the neighbor counter */ /* back up */ if ( !bCtPredecessors && nTopStackAtom /* that is, i != start */) { u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ nNumDescendants[u] += nNumDescendants[i]; /* add descendants */ } } while ( --nTopStackAtom >= 0 ); /* Sort the neighbors in ascending order so that: primary key = number of descendants in the DFS tree; closure neighbor is 0 secondary key = canonical number (here vertex number = canonical number - 1) */ /* set static globals for the sorting: */ gDfs4CT_nDfsNumber = nDfsNumber; gDfs4CT_nNumDescendants = nNumDescendants; gDfs4CT_nCurrentAtom = -1; /* sorting; deleted will be the last neighbors */ for ( i = 0; i < num_atoms; i ++ ) { if ( nl[i][0] > 1 ) { gDfs4CT_nCurrentAtom = i; insertions_sort( &nl[i][1], nl[i][0], sizeof(nl[i][1]), CompareDfsDescendants4CT ); } /* reduce number of neighbors to exclude deleted */ for ( k = 0; k < nl[i][0] && nl[i][k+1] <= MAX_ATOMS; k ++ ) ; nl[i][0] = k; } nTotOutputStringLen = 3*(num_atoms+num_rings+1); /* last 3 elements are a 'zero termination' */ if ( bCtPredecessors ) { if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { cDelim = '-'; for ( u = 0, k = -3 ; u < num_atoms; u ++ ) { k += 3; if ( k+6 > nTotOutputStringLen ) { goto exit_error; /* program error */ } nOutputString[k] = nNumDescendants[u]? nNumDescendants[u] : MAX_ATOMS+1; nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; nOutputString[k+2] = k? ',' : '\0'; for ( j = 1; j <= nl[u][0] && nDfsNumber[u] > nDfsNumber[i=nl[u][j]]; j ++ ) { /* closures */ k += 3; if ( k+6 > nTotOutputStringLen ) { goto exit_error; /* program error */ } nOutputString[k] = i+1; /* closure */ nOutputString[k+1] = 0; nOutputString[k+2] = cDelim; } } } } else { if ( nNumDescendants ) { /* do not need anymore */ inchi_free( nNumDescendants ); nNumDescendants = NULL; } /* the output string contains: (num_atoms) atoms for the DFS (spanning) tree (num_atoms-1) delimiters for the DFS (spanning) tree 1 character for each atom that has 1 terminal hydrogen atoms 2 characters for each atom that has 2-9 terminal hydrogen atoms 3 characters for each atom that has 10-99 terminal hydrogen atoms, etc. (num_rings) atoms for the ring closures (num_rings) delimiters for the ring closures */ if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { u = start; /* start atom */ nTopStackAtom =-1; memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; /* output the starting atom */ k = 0; nOutputString[k] = u+1; nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; nOutputString[k+2] = '\0'; do { /* advance */ while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ /* replaced due to missing sequence point; undefined behavior, reported by Geoffrey Hutchison */ { k += 3; if ( k+6 > nTotOutputStringLen ) { goto exit_error; /* program error */ } cNeighNumb[i] ++; u = (int)nl[i][j]; /* neighbor */ /* output neighbor's canonical number */ nOutputString[k] = u+1; if ( nDfsNumber[u] > nDfsNumber[i] ) { /* tree edge, 1st visit -- advance */ /* put 'unexplored' vertex u on the stack */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; /* output neighbor's number of H */ nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; } else { nOutputString[k+1] = 0; } /* output a delimiter preceding the neighbor */ if ( 1 < nl[i][0] ) { if ( j == 1 ) { cDelim = '('; } else if ( j == nl[i][0] ) { cDelim = ')'; } else { cDelim = ','; } } else { cDelim = '-'; } nOutputString[k+2] = cDelim; } cNeighNumb[i] = 0; /* back up: nothing else to do */ } while ( --nTopStackAtom >= 0 ); } } goto exit_function; exit_error: if ( nOutputString ) { inchi_free( nOutputString ); nOutputString = NULL; } exit_function: if ( nStackAtom ) inchi_free( nStackAtom ); if ( nNumDescendants ) inchi_free( nNumDescendants ); if ( nDfsNumber ) inchi_free( nDfsNumber ); if ( cNeighNumb ) inchi_free( cNeighNumb ); if ( nl ) FreeNeighList( nl ); return nOutputString; } /**********************************************************************************************/ int GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) { if ( err && err == 9 ) return _IS_ERROR; /* sdfile bypassed to $$$$ */ if ( err && err < 30 ) return _IS_FATAL; if ( num_inp_atoms <= 0 || err ) { if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) return _IS_WARNING; return _IS_ERROR; } if ( pStrErrStruct[0] ) return _IS_WARNING; return _IS_OKAY; } /**********************************************************************************************/ int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ char *pStrErrStruct, int nErrorType, int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ) { int b_ok; #ifdef TARGET_LIB_FOR_WINCHI int bPlainText = (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW ) && !(ip->bINChIOutputOptions & INCHI_OUT_XML); #else int bPlainText = 0; #endif if ( !bPlainText && *bXmlStructStarted <= 0 ) { return nErrorType; } /* Fatal error, Error, Warning */ if ( nErrorType ) { if ( bPlainText ) { if ( !(b_ok=OutputINChIPlainError( output_file, pStr, nStrLen, pStrErrStruct, nErrorType ) ) ) { inchi_ios_eprint( log_file, "Cannot create message for error (structure #%ld.%s%s%s%s) Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } else { inchi_ios_print( output_file, "\n" ); /* add a blank line after the WINCHI Window message */ } } else { if ( !(b_ok=OutputINChIXmlError( output_file, pStr, nStrLen, 2, /*err,*/ pStrErrStruct, nErrorType ) ) ) { inchi_ios_eprint( log_file, "Cannot create xml tag for error (structure #%ld.%s%s%s%s) Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } if ( !b_ok || nErrorType == _IS_FATAL || nErrorType == _IS_ERROR ) { /* close current structure output */ if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); *bXmlStructStarted = -1; b_ok = 0; } else { *bXmlStructStarted = 0; } } } return b_ok? nErrorType : _IS_FATAL; } return nErrorType; } #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ /***************************************************************************************/ int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ) { if ( s1 == NULL && s2 == NULL ) return 0; if ( (s1 == NULL) ^ (s2 == NULL) ) return 20; if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) return 21; if ( s1->nNumberOfStereoCenters > 0 ) { if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) return 22; if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) return 23; if ( s1->nNumberInv && s2->nNumberInv ) { if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) return 28; if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) return 29; if ( s1->nCompInv2Abs != s2->nCompInv2Abs || s1->bTrivialInv != s2->bTrivialInv ) { return 30; } } else if ( s1->nNumberInv || s2->nNumberInv ) { return 31; } } if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) return 24; if ( s1->nNumberOfStereoBonds > 0 ) { if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) return 25; if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) return 26; if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) return 27; } return 0; } /***************************************************************************************/ int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ) { int ret; if ( i1 == NULL && i2 == NULL ) return 0; if ( (i1 == NULL) ^ (i2 == NULL) ) return 1; if ( i1->nErrorCode == i2->nErrorCode ) { if ( i1->nErrorCode ) return 0; } else { return 2; } if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) return 3; if ( i1->nNumberOfAtoms > 0 ) { if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) return 4; if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) return 5; if ( i1->nNum_H_fixed && i2->nNum_H_fixed && memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { return 6; } if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) return 7; } if ( i1->lenConnTable != i2->lenConnTable ) return 8; if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) return 9; if ( i1->lenTautomer != i2->lenTautomer ) return 10; if ( i1->lenTautomer > 0 && memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) return 11; if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) return 12; if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) return 13; if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) return 14; if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) return 15; if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) return 16; if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) return 17; if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) return 18; } else if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { return 19; } /* ret = 20..31 */ if ( ret = CompareStereoINChI( i1->Stereo, i2->Stereo ) ) return ret; /* ret = 40..51 */ if ( ret = CompareStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) return ret+20; return 0; } #endif /* } TEST_RENUMB_ATOMS == 1 */ #if ( READ_INCHI_STRING == 1 ) /* { */ /*************************************************************************************/ int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */) { if ( s1 == NULL && s2 == NULL ) return 0; if ( (s1 == NULL) ^ (s2 == NULL) ) { INChI_Stereo *s = s1? s1 : s2; if ( s->nNumberOfStereoCenters || s->nNumberOfStereoBonds ) { return 20; /* Diff: Missing Stereo */ } else { return 0; } } if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) return 21; /* Diff: Number of sp3 stereocenters */ if ( s1->nNumberOfStereoCenters > 0 ) { if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) return 22; /* Diff: sp3 stereocenter locations */ if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) return 23; /* Diff: sp3 stereocenter parities */ if ( s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) return 24; /* Diff: sp3 inversion */ /* if ( s1->nNumberInv && s2->nNumberInv ) { if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) return 25; if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) return 26; if ( s1->nCompInv2Abs != s2->nCompInv2Abs || s1->bTrivialInv != s2->bTrivialInv ) { return 27; } } else if ( s1->nNumberInv || s2->nNumberInv ) { return 28; } */ } if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) return 25; /* Diff: Number of stereobonds */ if ( s1->nNumberOfStereoBonds > 0 ) { if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) return 26; /* Diff: Stereobond 1st atom locations */ if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) return 27; /* Diff: Stereobond 2nd atom locations */ if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) return 28; /* Diff: Stereobond parities */ } return 0; } /*************************************************************************************/ int CompareReversedStereoINChI2( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) { int ret = 0; int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; if ( (nNumSc1 || nNumSc1) && ( nNumSc1 != nNumSc2 || memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { if ( s1->nNumber[j1] == s2->nNumber[j2] ) { if ( s1->t_parity[j1] == s2->t_parity[j2] ) { num_eq ++; } else { num_dif ++; } j1 ++; j2 ++; } else if ( s1->nNumber[j1] < s2->nNumber[j2] ) { num_in1_only ++; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSc ) { if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; } } j1 ++; } else { num_in2_only ++; if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSc ) { if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; } } j2 ++; } } while ( j1 < nNumSc1 ) { if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } num_in1_only ++; if ( bAddSc ) { if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; } } j1 ++; } while ( j2 < nNumSc2 ) { if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } num_in2_only ++; if ( bAddSc ) { if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; } j2 ++; } if ( num_dif ) { ret |= IDIF_SC_PARITY; } if ( num_in1_only ) { if ( num_extra_undf ) { ret |= IDIF_SC_EXTRA_UNDF; } if ( num_in1_only != num_extra_undf ) { ret |= IDIF_SC_EXTRA; } } if ( num_in2_only ) { if ( num_miss_undf ) { ret |= IDIF_SC_MISS_UNDF; } if ( num_in2_only != num_miss_undf ) { ret |= IDIF_SC_MISS; } } } if ( s1 && s2 && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { ret |= IDIF_SC_INV; } if ( (nNumSb1 || nNumSb2 ) && (nNumSb1 != nNumSb2 || memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { if ( s1->b_parity[j1] == s2->b_parity[j2] ) { num_eq ++; } else { num_dif ++; } j1 ++; j2 ++; } else if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { num_in1_only ++; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; } } j1 ++; } else { num_in2_only ++; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; } } j2 ++; } } while ( j1 < nNumSb1 ) { num_in1_only ++; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; } } j1 ++; } while ( j2 < nNumSb2 ) { num_in2_only ++; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; } } j2 ++; } if ( num_dif ) { ret |= IDIF_SB_PARITY; } if ( num_in1_only ) { if ( num_extra_undf ) { ret |= IDIF_SB_EXTRA_UNDF; } if ( num_in1_only != num_extra_undf ) { ret |= IDIF_SB_EXTRA; } } if ( num_in2_only ) { if ( num_miss_undf ) { ret |= IDIF_SB_MISS_UNDF; } if ( num_in2_only != num_miss_undf ) { ret |= IDIF_SB_MISS; } } } return ret; } /*************************************************************************************/ int CompareReversedINChI( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2 ) { int ret; if ( i1 == NULL && i2 == NULL ) return 0; if ( (i1 == NULL) ^ (i2 == NULL) ) return 1; /* Diff: Missing InChI */ if ( i1->nErrorCode == i2->nErrorCode ) { if ( i1->nErrorCode ) return 0; } else { return 2; /* Diff: Error codes */ } if ( i1->bDeleted != i2->bDeleted ) { return 1; /* Diff: Missing InChI */ } if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) return 3; /* Diff: Num. atoms */ if ( i1->nNumberOfAtoms > 0 ) { if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) return 4; /* Diff: Elements */ if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) return 7; /* Diff: Hill Formulas */ if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { if ( i1->lenConnTable > 1 || i2->lenConnTable > 1 ) { return 5; /* Diff: H Locations (mobile H present) */ } else { return 6; /* Diff: H Locations (no mobile H) */ } } /* fixed H */ if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { int bHasFixedH1 = 0, bHasFixedH2 = 0, i, j1, j2; if ( i1->nNum_H_fixed ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] ) { bHasFixedH1 ++; } } } if ( i2->nNum_H_fixed ) { for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { bHasFixedH2 ++; } } } /* count the differences */ j1 = j2 = 0; if ( bHasFixedH1 && !bHasFixedH2 ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] > 0 ) { j1 ++; } else if ( i1->nNum_H_fixed[i] < 0 ) { j2 ++; } } return 18; /* Diff: Extra Fixed-H */ } else if ( !bHasFixedH1 && bHasFixedH2 ) { for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( 0 > i2->nNum_H_fixed[i] ) { j1 ++; } else if ( 0 < i2->nNum_H_fixed[i] ) { j2 ++; } } return 19; /* Diff: Missed Fixed-H */ } else if ( bHasFixedH1 && bHasFixedH2 && memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { j1 ++; } else if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { j2 ++; } } } ret = (j1 && j2)? 20 : j1? 18 : j2? 19 : 0; if ( ret ) { return ret; /* 20 => Diff: NotEql Fixed-H */ /* 19 => Diff: Missed Fixed-H (i1 has less) */ /* 18 => Diff: Extra Fixed-H (i1 has more) */ } } } if ( i1->lenConnTable != i2->lenConnTable ) return 8; /* Diff: Connections length */ if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) return 9; /* Diff: Connections */ /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) return 10; /* Diff: Mobile groups length */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) return 11; /* Diff: Mobile groups */ if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) return 12; /* Diff: Isotopic atoms number */ if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) return 13; /* Diff: Isotopic atoms */ if ( i1->nTotalCharge != i2->nTotalCharge ) return 14; /* Diff: Charge */ /* if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) return 14; if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) return 15; */ if ( a1 && a2 ) { if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) return 16; /* Diff: Number of removed protons */ if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) return 17; /* Diff: Removed isotopic H */ } /* if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) return 18; } else if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { return 19; } */ /* ret = 20..31 => 40..51 */ if ( ret = CompareReversedStereoINChI( i1->Stereo, i2->Stereo ) ) return ret+20; /* ret = 40..51 => 60..71 */ if ( !i2->StereoIsotopic && i2->Stereo && i1->StereoIsotopic && 0 < (i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters) && 0 == CompareReversedStereoINChI( i1->StereoIsotopic, i2->Stereo ) ) { /* InChI from reversed structure does not contain fully duplicated isotopic stereo */ ; } else if ( ret = CompareReversedStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) { return ret+40; } return 0; } /*******************************************************************************/ int CompareIcr( ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ) { int nNumExtraBits1 = 0, nNumExtraBits2 = 0, bit1, bit2; INCHI_MODE Flg1=picr1->flags, Flg2 = picr2->flags, cur_bit = 1, in1, in2; int i, ret; /* compare flags */ in1 = in2 = 0; for ( i = 0; Flg1 || Flg2; i ++, Flg1 >>= 1, Flg2 >>= 1, cur_bit <<= 1 ) { if ( !(mask & cur_bit) ) { continue; } bit1 = Flg1 & 1; bit2 = Flg2 & 1; if ( bit1 && !bit2 ) { in1 |= 1 << i; nNumExtraBits1 ++; } else if ( !bit1 && bit2 ) { in2 |= 1 << i; nNumExtraBits2 ++; } } if ( nNumExtraBits1 && !nNumExtraBits2 ) { ret = 1; } else if ( !nNumExtraBits1 && nNumExtraBits2 ) { ret = -1; } else if ( !in1 && !in2 ) { ret = 0; } else { ret = 2; /* compare produced undefined results */ } if ( pin1 ) *pin1 = in1; if ( pin2 ) *pin2 = in2; /* more detailed compare not implemented */ return ret; } /*********************************************************************************************************/ INCHI_MODE CompareReversedINChI2( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2, ICR *picr, int *err ) { INCHI_MODE ret = 0; INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; *err = 0; memset( picr, 0, sizeof(*picr) ); if ( i1 == NULL && i2 == NULL ) return 0; if ( (i1 == NULL) ^ (i2 == NULL) ) { ret |= IDIF_PROBLEM; /* one InChI exists while another doesn't */ goto exit_function; } if ( i1->nErrorCode == i2->nErrorCode ) { if ( i1->nErrorCode ) { ret |= IDIF_PROBLEM; /* both InChI have same error codes */ goto exit_function; } } else { ret |= IDIF_PROBLEM; /* at least one InChI has an error code */ goto exit_function; } if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { ret |= IDIF_NUM_AT; goto exit_function; } if ( i1->nNumberOfAtoms > 0 ) { if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { ret |= IDIF_ATOMS; goto exit_function; } /* IDIF_NON_TAUT_H, IDIF_MORE_FH, IDIF_LESS_FH */ if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { ret |= IDIF_POSITION_H; for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; picr->num_diff_pos_H ++; } } } /* fixed H */ if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { int bHasFixedH1 = 0, bHasFixedH2 = 0, i; if ( i1->nNum_H_fixed ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] ) { bHasFixedH1 ++; } } } if ( i2->nNum_H_fixed ) { for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { bHasFixedH2 ++; } } } if ( bHasFixedH1 && !bHasFixedH2 ) { for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] ) { if ( j < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at1_more[j] = i; picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; j ++; } } } picr->num_fixed_H1_more = j; ret |= IDIF_MORE_FH; /* Extra Fixed-H */ } else if ( !bHasFixedH1 && bHasFixedH2 ) { for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { if ( j < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at2_more[j] = i; picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; j ++; } } } picr->num_fixed_H2_more = j; ret |= IDIF_LESS_FH; /* Missed Fixed-H */ } else if ( bHasFixedH1 && bHasFixedH2 && memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { if ( j1 < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at1_more[j1] = i; picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; j1 ++; } } else if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { if ( j2 < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at2_more[j2] = i; picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; j2 ++; } } } ret |= (j1? IDIF_MORE_FH:0) | (j2? IDIF_LESS_FH:0); picr->num_fixed_H1_more = j1; picr->num_fixed_H2_more = j2; } } } /* compare formulas and H */ num_H1 = 0; num_H2 = 0; ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); picr->tot_num_H1 = num_H1; picr->tot_num_H2 = num_H2; if ( ret2 ) { ret |= IDIF_NUM_EL; goto exit_function; } if ( num_H1 > num_H2 ) { ret |= IDIF_MORE_H; } if ( num_H1 < num_H2 ) { ret |= IDIF_LESS_H; } if ( i1->lenConnTable != i2->lenConnTable ) { ret |= IDIF_CON_LEN; goto exit_function; } else if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { ret |= IDIF_CON_TBL; goto exit_function; } /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ /* if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { ret |= IDIF_TAUT_LEN; } */ /* compare number of t-groups */ n1 = i1->lenTautomer? i1->nTautomer[0] : 0; n2 = i2->lenTautomer? i2->nTautomer[0] : 0; if ( !n1 && n2 ) { ret |= IDIF_NO_TAUT; } else if ( n1 && !n2 ) { ret |= IDIF_WRONG_TAUT; } else if ( n1 == 1 && n2 > 1 ) { ret |= IDIF_SINGLE_TG; } else if ( n1 > 1 && n2 == 1 ) { ret |= IDIF_MULTIPLE_TG; } else if ( n1 != n2 ) { ret |= IDIF_NUM_TG; } if ( n1 || n2 ) { /* number of endpoints */ int num1 = 0, num2 = 0, num_M1=0, num_M2=0; int len, num_eq, num_in1_only, num_in2_only; AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); num_H1 = num_H2=0; /* collect endpoints, H, (-) */ if ( !pe1 || !pe2 ) { if ( pe1 ) inchi_free( pe1 ); if ( pe2 ) inchi_free( pe2 ); *err = -1; /* allocation error */ goto exit_function; } for ( m = 1; m < i1->lenTautomer; m += len ) { len = i1->nTautomer[m ++]; num_H1 += i1->nTautomer[m]; num_M1 += i1->nTautomer[m+1]; for ( j = 2; j < len; j ++ ) { pe1[num1 ++] = i1->nTautomer[m + j]; } } for ( m = 1; m < i2->lenTautomer; m += len ) { len = i2->nTautomer[m ++]; num_H2 += i2->nTautomer[m]; num_M2 += i2->nTautomer[m+1]; for ( j = 2; j < len; j ++ ) { pe2[num2 ++] = i2->nTautomer[m + j]; } } picr->num_taut_H1 = num_H1; picr->num_taut_H2 = num_H2; picr->num_taut_M1 = num_M1; picr->num_taut_M2 = num_M2; /* sort endpoints */ insertions_sort_AT_RANK( pe1, num1 ); insertions_sort_AT_RANK( pe2, num2 ); /* compare */ /* if ( num1 < num2 ) { ret |= IDIF_LESS_TG_ENDP; } else if ( num1 > num2 ) { ret |= IDIF_MORE_TG_ENDP; } */ /* compare all */ num_eq = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { if( pe1[j1] == pe2[j2] ) { j1 ++; j2 ++; num_eq ++; } else if ( pe1[j1] < pe2[j2] ) { /* BC: fixed, was pe2[j1] 2006-03-27 */ if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; } j1 ++; num_in1_only ++; } else { if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; } j2 ++; num_in2_only ++; } } while ( j1 < num1 ) { if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; } j1 ++; num_in1_only ++; } while ( j2 < num2 ) { if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; } j2 ++; num_in2_only ++; } if ( num_in1_only ) { ret |= IDIF_EXTRA_TG_ENDP; } if ( num_in2_only ) { ret |= IDIF_MISS_TG_ENDP; } if ( !num_in1_only && !num_in2_only && num_eq ) { ; /* same t-groups endpoints */ } else { ret |= IDIF_DIFF_TG_ENDP; } inchi_free( pe1 ); inchi_free( pe2 ); } if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && ( i1->lenTautomer != i2->lenTautomer || memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) ret |= IDIF_TG; if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { ret |= IDIF_NUM_ISO_AT; } else if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) ret |= IDIF_ISO_AT; if ( i1->nTotalCharge != i2->nTotalCharge ) ret |= IDIF_CHARGE; if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { ret |= IDIF_REM_PROT; } if ( a1 && (!a2 || a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { ret |= IDIF_REM_ISO_H; } /* if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) return 18; } else if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { return 19; } */ if ( i1->StereoIsotopic && i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { Stereo1 = i1->StereoIsotopic; } else { Stereo1 = i1->Stereo; } if ( i2->StereoIsotopic && i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { Stereo2 = i2->StereoIsotopic; } else { Stereo2 = i2->Stereo; } ret |= CompareReversedStereoINChI2( Stereo1, Stereo2, picr ); exit_function: picr->flags = ret; return ret; } #endif /* } READ_INCHI_STRING */ /***************************************************************************************/ int Create_INChI( INChI **ppINChI, INChI_Aux **ppINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, /* not used */ inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], int num_inp_at, INCHI_MODE nUserMode, INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, struct tagInchiTime *ulMaxTime, T_GROUP_INFO *ti_out, char *pStrErrStruct) { /* #define NON_TAUT 0 #define TAUT 1 */ sp_ATOM *at[TAUT_NUM]; /* at[0]=>non-tautomeric, at[1]=>tautomeric */ /* inp_ATOM *out_norm_taut_at, *out_norm_nontaut_at; */ int i, n1, n2, num_atoms, num_at_tg, num_removed_H, num_removed_H_taut=0, ret=0, ret2=0; INCHI_MODE nMode=0; T_GROUP_INFO vt_group_info; T_GROUP_INFO vt_group_info_orig; T_GROUP_INFO * /*const*/ t_group_info = &vt_group_info; T_GROUP_INFO * /*const*/ t_group_info_orig = &vt_group_info_orig; CANON_STAT CS, CS2; CANON_STAT *pCS = &CS; CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ ATOM_SIZES s[TAUT_NUM]; BCN Bcn; BCN *pBCN = &Bcn; int bHasIsotopicAtoms = 0; int bMayHaveStereo = 0; int num_taut_at = 0; inp_ATOM *out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ INChI *pINChI=NULL; /* added initialization 2006-03 */ INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ int bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); INCHI_MODE bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); INCHI_MODE bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); #if ( bRELEASE_VERSION == 0 ) int bExtract = 0; /* EXTR_HAS_ATOM_WITH_DEFINED_PARITY; */ #endif /*^^^ */ int bFixIsoFixedH = 0; int bFixTermHChrg = 0; #if ( TEST_RENUMB_ATOMS == 1 ) long ulNormTime=0; long ulCanonTime=0, ulCanonTime2=0; inchiTime ulNormTimeStart; inchiTime ulCanonTimeStart; InchiTimeGet( &ulNormTimeStart ); #endif /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ int vABParityUnknown = AB_PARITY_UNDF; if ( 0 != ( nUserMode & REQ_MODE_DIFF_UU_STEREO) ) { /* Make labels for unknown and undefined stereo different */ vABParityUnknown = AB_PARITY_UNKN; } /*^^^ */ #if ( FIX_ISO_FIXEDH_BUG == 1 ) if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) bFixIsoFixedH = 1; #endif #if ( FIX_TERM_H_CHRG_BUG == 1 ) if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) bFixTermHChrg = 1; #endif /*^^^ */ memset( s, 0, sizeof(s) ); if ( pBCN ) { memset( pBCN, 0, sizeof( pBCN[0] ) ); } memset( t_group_info, 0, sizeof(*t_group_info) ); memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); /*norm_at[TAUT_NON] = out_norm_data[TAUT_NON]->at; *//* output normalized non-tautomeric component */ /*norm_at[TAUT_YES] = out_norm_data[TAUT_YES]->at; *//* output normalized tautomeric component */ /*norm_at_fixed_bonds[TAUT_NON] = NULL;*/ /*norm_at_fixed_bonds[TAUT_YES] = out_norm_data[TAUT_YES]->at_fixed_bonds;*/ for ( i = 0; i < TAUT_NUM; i ++ ) { if ( out_norm_data[i]->at ) { if ( !(at[i] = (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*at[0]) ) ) ) { ret = -1; } } else { at[i] = NULL; } } if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) { ret = -1; goto exit_function; } /* the first struct to process: tautomeric if exists else non-tautomeric */ out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; /* copy the input structure to be normalized to the buffer for the normalization data */ memcpy( out_at, inp_at, num_inp_at*sizeof(out_at[0]) ); /* tautomeric groups setting */ t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ t_group_info->bTautFlags = *pbTautFlags; t_group_info->bTautFlagsDone = *pbTautFlagsDone; /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ if ( TG_FLAG_H_ALREADY_REMOVED & bTautFlags ) { INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; if ( out_norm_data1 ) { num_at_tg = num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; num_removed_H = out_norm_data1->num_removed_H; t_group_info->tni.nNumRemovedExplicitH = num_removed_H; } else { ret = -1; goto exit_function; } } else { num_at_tg = num_atoms = remove_terminal_HDT( num_inp_at, out_at, bFixTermHChrg ); num_removed_H = num_inp_at - num_atoms; t_group_info->tni.nNumRemovedExplicitH = num_removed_H; add_DT_to_num_H( num_atoms, out_at ); } /*fix_odd_things( num_atoms, out_at );*/ #if ( FIND_RING_SYSTEMS == 1 ) MarkRingSystemsInp( out_at, num_atoms, 0 ); #endif /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ if ( out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) { memcpy( out_norm_data[TAUT_YES]->at, out_at, num_inp_at*sizeof(out_at[0]) ); } if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) { memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, out_at, num_inp_at*sizeof(out_at[0]) ); } if ( out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) { memcpy( out_norm_data[TAUT_NON]->at, out_at, num_inp_at*sizeof(out_at[0]) ); } /******************************************************************************* * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal * set stereo parities in taut_at[], non_taut_at[] * obtain max. lenghts of the name stereo parts * Ignore absence/presence of isotopic stereo for now * mark isotopic atoms *******************************************************************************/ if ( out_norm_data[TAUT_YES]->at && at[TAUT_YES] ) { /* final normalization of possibly tautomeric structure */ ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_YES]->at, out_norm_data[TAUT_YES]->at_fixed_bonds, num_atoms, t_group_info, NULL, NULL ); if ( ret < 0 ) { goto exit_function;/* out of RAM or other normalization problem */ } num_taut_at = ret; /* number of atoms without removed H? */ num_removed_H_taut = t_group_info->tni.nNumRemovedExplicitH; out_norm_data[TAUT_YES]->num_at = num_atoms + num_removed_H_taut; /* protons might have been removed */ out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut; out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; } /* mark deleted isolated tautomeric H(+) */ if ( num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) { out_norm_data[TAUT_YES]->bDeleted = 1; FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); } else if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && out_norm_data[TAUT_YES]->at_fixed_bonds) { out_norm_data[TAUT_YES]->bTautPreprocessed = 1; } /* if ( !(t_group_info->tni.bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT & ~FLAG_PROTON_SINGLE_REMOVED)) && out_norm_data[TAUT_YES]->at_fixed_bonds) { FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); } */ /*out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;*/ out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; /* create internal sp_ATOM at[] out of out_norm_data[]->at */ inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, at[TAUT_YES] ); /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ ret = set_stereo_parity( out_norm_data[TAUT_YES]->at, at[TAUT_YES], num_taut_at, num_removed_H_taut, &s[TAUT_YES].nMaxNumStereoAtoms, &s[TAUT_YES].nMaxNumStereoBonds, nUserMode, bPointedEdgeStereo, vABParityUnknown ); #if ( bRELEASE_VERSION == 0 ) if ( 0 < ret ) { bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; } if ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) { bExtract |= EXTR_TAUT_TREATMENT_CHARGES; } #endif if ( RETURNED_ERROR( ret ) ) { goto exit_function; /* stereo bond error */ } s[TAUT_YES].bMayHaveStereo = (s[TAUT_YES].nMaxNumStereoAtoms || s[TAUT_YES].nMaxNumStereoBonds); /* * mark isotopic atoms and atoms that have non-tautomeric * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) */ s[TAUT_YES].num_isotopic_atoms = set_atom_iso_sort_keys( num_taut_at, at[TAUT_YES], t_group_info, &s[TAUT_YES].bHasIsotopicTautGroups ); /************************************************************************** * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) * structure for canonicalizaton: ************************************************************************** * remove t-groups that have no H, * remove charges from t-groups if requested * renumber t-groups and find final t_group_info->num_t_groups * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] * calculate length of the t-group part of the connection table **************************************************************************/ s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( at[TAUT_YES], num_taut_at, t_group_info ); if ( RETURNED_ERROR(s[TAUT_YES].nLenLinearCTTautomer) ) { /* added error treatment 9-11-2003 */ ret = s[TAUT_YES].nLenLinearCTTautomer; goto exit_function; /* error has happened; no breakpoint here s[TAUT_YES].nLenLinearCTTautomer = 0; */ } else if ( s[TAUT_YES].nLenLinearCTTautomer > 0 ) { num_at_tg = num_taut_at+t_group_info->num_t_groups; /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); /* mark isotopic tautomer groups: calculate t_group->iWeight */ s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); if ( s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) { /* ??? -error cannot happen- error has happened; no breakpoint here */ s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; } out_norm_data[TAUT_YES]->bTautomeric = s[TAUT_YES].nLenLinearCTTautomer; } /* new variable: s[TAUT_YES].nLenCT introduced 7-22-2002 */ GetCanonLengths( num_taut_at, at[TAUT_YES], &s[TAUT_YES], t_group_info ); } if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && at[TAUT_NON] && !s[TAUT_YES].nLenLinearCTTautomer ) { /* the structure is non-tautomeric: use tautomeric treatment results only for it */ inchi_free( at[TAUT_NON] ); at[TAUT_NON] = NULL; } else if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && !at[TAUT_NON] && at[TAUT_YES] && !s[TAUT_YES].nLenLinearCTTautomer ) { /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ out_norm_data[TAUT_YES]->bTautomeric = 0; } else if ( out_norm_data[TAUT_NON]->at && at[TAUT_NON] ) { /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_NON]->at, NULL, num_atoms, NULL, &bTautFlags, &bTautFlagsDone ); if ( ret < 0 ) { goto exit_function; /* out of RAM or other normalization problem */ } out_norm_data[TAUT_NON]->num_at = num_atoms + num_removed_H; out_norm_data[TAUT_NON]->num_removed_H = num_removed_H; out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; out_norm_data[TAUT_NON]->bNormalizationFlags = 0; /* create internal sp_ATOM at[] out of out_norm_data[]->at */ inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, at[TAUT_NON] ); /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ ret = set_stereo_parity( out_norm_data[TAUT_NON]->at, at[TAUT_NON], num_atoms, num_removed_H, &s[TAUT_NON].nMaxNumStereoAtoms, &s[TAUT_NON].nMaxNumStereoBonds, nUserMode, bPointedEdgeStereo, vABParityUnknown ); #if ( bRELEASE_VERSION == 0 ) if ( 0 < ret ) { bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; } #endif if ( RETURNED_ERROR( ret ) ) { goto exit_function; /* stereo bond error */ } s[TAUT_NON].bMayHaveStereo = (s[TAUT_NON].nMaxNumStereoAtoms || s[TAUT_NON].nMaxNumStereoBonds); /* * mark isotopic atoms and atoms that have non-tautomeric * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) */ s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( num_atoms, at[TAUT_NON], NULL, NULL ); GetCanonLengths( num_atoms, at[TAUT_NON], &s[TAUT_NON], NULL); out_norm_data[TAUT_NON]->bTautomeric = 0; } /**********************************************************/ /* common */ bMayHaveStereo = s[TAUT_YES].bMayHaveStereo || s[TAUT_NON].bMayHaveStereo; bHasIsotopicAtoms = s[TAUT_NON].num_isotopic_atoms > 0 || s[TAUT_NON].bHasIsotopicTautGroups > 0 || s[TAUT_YES].num_isotopic_atoms > 0 || s[TAUT_YES].bHasIsotopicTautGroups > 0 ; /*^^^ */ if (bFixIsoFixedH) /* 2008-03-21 DT */ bHasIsotopicAtoms = bHasIsotopicAtoms || s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; /*^^^ */ bHasIsotopicAtoms = bHasIsotopicAtoms || s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); /* default mode */ if ( !(nUserMode & REQ_MODE_DEFAULT) ) { /* default */ nUserMode |= REQ_MODE_DEFAULT; } /* adjust the mode to the reality */ if ( ( nUserMode & REQ_MODE_ISO ) && !bHasIsotopicAtoms ) { nUserMode ^= REQ_MODE_ISO; nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ } if ( (nUserMode & REQ_MODE_STEREO) && ( nUserMode & REQ_MODE_ISO ) ) { nUserMode |= REQ_MODE_ISO_STEREO; } if ( (nUserMode & REQ_MODE_STEREO) && !( nUserMode & REQ_MODE_NON_ISO ) ) { nUserMode ^= REQ_MODE_STEREO; } if ( !bMayHaveStereo ) { if ( nUserMode & REQ_MODE_STEREO ) nUserMode ^= REQ_MODE_STEREO; if ( nUserMode & REQ_MODE_ISO_STEREO ) nUserMode ^= REQ_MODE_ISO_STEREO; } if ( (nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !at[TAUT_NON]) ) { nUserMode ^= REQ_MODE_BASIC; } if ( (nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !at[TAUT_YES]) ) { nUserMode ^= REQ_MODE_TAUT; } switch ((int)nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) { case REQ_MODE_BASIC: n1 = TAUT_NON; n2 = TAUT_NON; break; case REQ_MODE_TAUT: n1 = TAUT_YES; n2 = TAUT_YES; break; case (REQ_MODE_BASIC | REQ_MODE_TAUT): n1 = TAUT_NON; n2 = TAUT_YES; break; default: ret = -3; goto exit_function; /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ } #if ( TEST_RENUMB_ATOMS == 1 ) ulNormTime = InchiTimeElapsed( &ulNormTimeStart); #endif /************************************************************ * * * Obtain all non-stereo canonical numberings * * * ************************************************************/ #if ( TEST_RENUMB_ATOMS == 1 ) InchiTimeGet( &ulCanonTimeStart ); #endif if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { /* added for special non-isotopic test mode 2004-10-04 */ if ( t_group_info ) { t_group_info->bIgnoreIsotopic = 1; if ( t_group_info->nIsotopicEndpointAtomNumber ) { t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); } memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); } for ( i = 0; i < TAUT_NUM; i ++ ) { s[i].bHasIsotopicTautGroups = 0; s[i].bIgnoreIsotopic = 1; s[i].nLenIsotopic = 0; s[i].nLenIsotopicEndpoints = 0; s[i].nLenLinearCTIsotopicTautomer = 0; s[i].num_isotopic_atoms = 0; } bHasIsotopicAtoms = 0; } ret = GetBaseCanonRanking( num_atoms, num_at_tg, at, t_group_info, s, pBCN, ulMaxTime, bFixIsoFixedH ); #if ( TEST_RENUMB_ATOMS == 1 ) ulCanonTime = InchiTimeElapsed( &ulCanonTimeStart ); #endif if ( ret < 0 ) { goto exit_function; /* program error */ } #if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) /* Debug only: find whether canonical equivalence is different from equitable partition */ if ( bCanonIsFinerThanEquitablePartition( num_atoms, at[n1], pBCN->ftcn[TAUT_NON].nSymmRankCt ) ) { bExtract |= EXTR_CANON_NE_EQUITABLE; } #endif /* added for special non-isotopic test mode 2004-10-04 */ if ( !pBCN->ftcn[n1].PartitionCt.Rank ) { n1 = ALT_TAUT(n1); } if ( !pBCN->ftcn[n2].PartitionCt.Rank ) { n2 = ALT_TAUT(n2); } if ( n1 > n2 ) { ret = CT_TAUCOUNT_ERR; goto exit_function; /* program error */ } /************************************************************ * * * Obtain stereo canonical numberings * * * ************************************************************/ for ( i = n2; i >= n1 && !RETURNED_ERROR( ret ); i -- ) { memset( pCS, 0, sizeof(*pCS) ); switch( i ) { case TAUT_NON: /* non-tautomeric */ nMode = 0; nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; nMode |= (s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; nMode |= (bHasIsotopicAtoms && s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; } if ( ret= AllocateCS( pCS, num_atoms, num_atoms, s[TAUT_NON].nLenCT, s[TAUT_NON].nLenCTAtOnly, s[TAUT_NON].nLenLinearCTStereoDble, s[TAUT_NON].nMaxNumStereoBonds, s[TAUT_NON].nLenLinearCTStereoCarb, s[TAUT_NON].nMaxNumStereoAtoms, 0, 0, s[TAUT_NON].nLenIsotopic, nMode, pBCN ) ) { goto exit_function; } *pCS2 = *pCS; break; case TAUT_YES: /* tautomeric */ nMode = 0; nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; nMode |= (s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; nMode |= (bHasIsotopicAtoms && s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; } if ( ret= AllocateCS( pCS, num_atoms, num_at_tg, s[TAUT_YES].nLenCT, s[TAUT_YES].nLenCTAtOnly, s[TAUT_YES].nLenLinearCTStereoDble, s[TAUT_YES].nMaxNumStereoBonds, s[TAUT_YES].nLenLinearCTStereoCarb, s[TAUT_YES].nMaxNumStereoAtoms, s[TAUT_YES].nLenLinearCTTautomer, s[TAUT_YES].nLenLinearCTIsotopicTautomer, s[TAUT_YES].nLenIsotopic, nMode, pBCN ) ) { goto exit_function; } *pCS2 = *pCS; break; } /*^^^ 2009-12-05 */ nMode |= (nUserMode & REQ_MODE_DIFF_UU_STEREO)? REQ_MODE_DIFF_UU_STEREO : 0; /*^^^ 2009-12-05 */ /* settings */ pCS->lNumDecreasedCT = -1; pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ pCS->bIgnoreIsotopic = !((s[TAUT_NON].num_isotopic_atoms || s[TAUT_YES].num_isotopic_atoms || s[TAUT_YES].bHasIsotopicTautGroups) || (nUserMode & REQ_MODE_NON_ISO) || !(nUserMode & REQ_MODE_ISO)); if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ } if ( i == TAUT_YES ) { /* tautomeric */ pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ pCS->t_group_info->bIgnoreIsotopic = !(s[TAUT_YES].bHasIsotopicTautGroups || (nUserMode & REQ_MODE_NON_ISO) || !(nUserMode & REQ_MODE_ISO)); if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ } } pCS->ulTimeOutTime = pBCN->ulTimeOutTime; /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== * * Mode Bits Description * '0' c 0 Only one connection table canonicalization * '1' C 1 Recalculate CT using fixed nSymmRank * '2' i 1|2 Isotopic canonicalization (internal) * '3' I 1|2|4 Isotopic canonicalization (output) * '4' s 1|8 Stereo canonicalization * '5' S 1|2|4|16 Stereo isotopic canonicalization * '6' A 1|2|4|8|16 Output All */ #if ( TEST_RENUMB_ATOMS == 1 ) InchiTimeGet( &ulCanonTimeStart ); #endif /*************************************** The last canonicalization step ***************************************/ if ( pBCN ) { /* USE_CANON2 == 1 */ pCS->NeighList = NULL; pCS->pBCN = pBCN; ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); } else { /* old way */ pCS->NeighList = CreateNeighList( num_atoms, i?num_at_tg:num_atoms, at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); pCS->pBCN = NULL; ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); } pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ pINChI_Aux = ppINChI_Aux[i]; if ( ret <= 0 ) { /***************************************/ /* failure in Canon_INChI() */ /***************************************/ pINChI->nErrorCode = ret; pINChI_Aux->nErrorCode = ret; } else { /***************************************/ /* success Canon_INChI() */ /* save canonicalization results in */ /* pINChI and pINChI_Aux */ /***************************************/ pINChI->nErrorCode = 0; pINChI_Aux->nErrorCode = 0; pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; pINChI_Aux->nCanonFlags = pCS->nCanonFlags; pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; /* may return an error or a warning */ ret = FillOutINChI( pINChI, pINChI_Aux, num_atoms, i?num_at_tg:num_atoms, i?num_removed_H_taut:num_removed_H, at[i], out_norm_data[i]->at, pCS, i, nUserMode, pStrErrStruct ); if ( RETURNED_ERROR( ret ) ) { /* failure in FillOutINChI() */ pINChI->nErrorCode = ret; pINChI_Aux->nErrorCode = ret; } else { /****************************/ /* success in FillOutINChI() */ /****************************/ #if ( bRELEASE_VERSION == 0 ) if ( pINChI->Stereo && (pINChI->Stereo->nCompInv2Abs && !pINChI->Stereo->bTrivialInv) || pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nCompInv2Abs && !pINChI->StereoIsotopic->bTrivialInv) ) { bExtract |= EXTR_NON_TRIVIAL_STEREO; } #endif /* mark non-tautomeric representation as having another, tautomeric representation */ if ( pINChI_Aux && s[TAUT_YES].nLenLinearCTTautomer ) { pINChI_Aux->bIsTautomeric = s[TAUT_YES].nLenLinearCTTautomer; } #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= bExtract; pINChI->bExtract |= pCS->bExtract; #endif ret2 = CheckCanonNumberingCorrectness( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, i, pStrErrStruct ); if ( ret2 ) { pINChI->nErrorCode = ret2; pINChI_Aux->nErrorCode = ret2; ret = ret2; } } } #if ( TEST_RENUMB_ATOMS == 1 ) ulCanonTime2 = InchiTimeElapsed( &ulCanonTimeStart ); pINChI_Aux->ulCanonTime = ulCanonTime+ulCanonTime2; pINChI_Aux->ulNormTime = ulNormTime; #endif FreeNeighList( pCS->NeighList ); DeAllocateCS( pCS2 ); pINChI = NULL; /* avoid dangling pointers */ pINChI_Aux = NULL; /* avoid dangling pointers */ } if ( ret == 0 ) { ret = num_atoms; } /* treat the results later */ exit_function: DeAllocBCN( pBCN ); if ( at[TAUT_YES] ) inchi_free( at[TAUT_YES] ); if ( at[TAUT_NON] ) inchi_free( at[TAUT_NON] ); if ( ti_out ) { *ti_out = *t_group_info; } else { free_t_group_info( t_group_info ); } free_t_group_info( t_group_info_orig ); return ret; } #ifndef COMPILE_ANSI_ONLY /* { */ /***************************************************************************************/ int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, AT_NUMB *nOrigAtNosInCanonOrd, int num_at ) { AT_NUMB *nCanonNbr, *nOrigAtNos, *nOrigAtNosOrd; int i, ret; ret = 0; nCanonNbr = (AT_NUMB *)inchi_calloc( num_at, sizeof(nCanonNbr[0]) ); nOrigAtNos = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNos[0]) ); nOrigAtNosOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNosOrd[0]) ); if ( !nCanonNbr || !nOrigAtNos || !nAtomOrdNbr || !nOrigAtNosOrd ) { ret = CT_OUT_OF_RAM; /* */ goto exit_function; } for ( i = 0; i < num_at; i ++ ) { nCanonNbr[i] = nAtomOrdNbr[i] = nOrigAtNosOrd[i] = (AT_NUMB)i; nOrigAtNos[i] = norm_at[i].orig_at_number; } /* get nCanonNbr[]: canon. numbers-1 in order of increasing original atom numbers */ pn_RankForSort = nOrigAtNosInCanonOrd; qsort( nCanonNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); /* get nOrigAtNosOrd[]: norm_atom ord. numbers the same order of increasing original atom numbers */ pn_RankForSort = nOrigAtNos; qsort( nOrigAtNosOrd, num_at, sizeof(nOrigAtNosOrd[0]), CompRank ); /* check whether the 2 sets of origiginal atom numbers have identical elements */ for ( i = 0; i < num_at; i ++ ) { if ( nOrigAtNosInCanonOrd[nCanonNbr[i]] != nOrigAtNos[nOrigAtNosOrd[i]] ) { ret = CT_RANKING_ERR; /* */ goto exit_function; } } for ( i = 0; i < num_at; i ++ ) { nAtomOrdNbr[(int)nCanonNbr[i]] = nOrigAtNosOrd[i]; } /* pn_RankForSort = nCanonNbr; qsort( nAtomOrdNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); */ exit_function: if ( nCanonNbr ) inchi_free( nCanonNbr ); if ( nOrigAtNos ) inchi_free( nOrigAtNos ); if ( nOrigAtNosOrd ) inchi_free( nOrigAtNosOrd ); return ret; } /***************************************************************************************/ int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) { int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len; int num_iso_H[NUM_H_ISOTOPES]; char *str; AT_NUMB g, e; int num_at = pINChI->nNumberOfAtoms; int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; AT_NUMB *nOrigAtNosInCanonOrd; INChI_Stereo *Stereo; AT_NUMB *nConstitEquNumbers; AT_NUMB *nConstitEquTGroupNumbers; S_CHAR *t_parity = NULL; AT_NUMB *nNumber = NULL; int bIncludeIsotopicH; AT_NUMB *nNormAtNosInCanonOrd; int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); int bRelRac= bRel || bRac; int bDoDisplaySp3 = 1; inf_ATOM *inf_norm_at = inf_norm_at_data? inf_norm_at_data->at : NULL; ret = 0; num_err = 0; if ( !inf_norm_at ) return ret; /* prepare removeable protons and H info */ inf_norm_at_data->nNumRemovedProtons = pINChI_Aux->nNumRemovedProtons; MakeRemovedProtonsString( pINChI_Aux->nNumRemovedProtons, pINChI_Aux->nNumRemovedIsotopicH, NULL, bIsotopic, inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); /* fill out info atom */ if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) bIsotopic = 0; Stereo = bIsotopic? pINChI->StereoIsotopic : pINChI->Stereo; bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { bDoDisplaySp3 = 0; if ( Stereo->nCompInv2Abs ) { inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; } } #endif /* flag has stereo */ if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { inf_norm_at_data->StereoFlags |= INF_STEREO; } /* if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { bDoDisplaySp3 = 0; } */ if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { /* inversion changes stereo */ if ( bRel ) { inf_norm_at_data->StereoFlags |= INF_STEREO_REL; } else if ( bRac ) { inf_norm_at_data->StereoFlags |= INF_STEREO_RAC; } else { inf_norm_at_data->StereoFlags |= INF_STEREO_ABS; } if ( bRelRac ) { inf_norm_at_data->StereoFlags |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; } } if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { /* display Inv stereo which is Absolute Stereo */ nNumber = Stereo->nNumberInv; t_parity = Stereo->t_parityInv; nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : pINChI_Aux->nOrigAtNosInCanonOrdInv; } else { /* display Inv stereo which is Absolute Stereo */ if ( bDoDisplaySp3 ) { nNumber = Stereo->nNumber; t_parity = Stereo->t_parity; } nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : pINChI_Aux->nOrigAtNosInCanonOrd; } nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : pINChI_Aux->nConstitEquNumbers; nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : pINChI_Aux->nConstitEquTGroupNumbers; memset( inf_norm_at, 0, init_num_at*sizeof(inf_norm_at[0]) ); /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { goto exit_function; } /* atom canonical and equivalence numbers > 0 */ for ( i = 0; i < num_at; i ++ ) { j = (int)nNormAtNosInCanonOrd[i]; if ( j < 0 || j >= num_at ) continue; inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; #ifdef DISPLAY_DEBUG_DATA inf_norm_at[j].nDebugData = 0; #if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) inf_norm_at[j].nDebugData = norm_at[j].c_point; #endif #endif } /* tautomeric groups */ if ( nNumberOfTGroups ) { /* : start from 1: bypass number of t-groups : j is a counter within the current t-group : g is a tautomeric group canonical number : e is a tautomeric group equivalence */ for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ e = nConstitEquTGroupNumbers[(int)g - 1]; /* bypass number of hydrogen atoms, negative charges, ... */ for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { /* scan canonical numbers of atoms within the atom t-group */ k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; inf_norm_at[k].nTautGroupCanonNbr = g; inf_norm_at[k].nTautGroupEquNbr = e; } } if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { ret = CT_TAUCOUNT_ERR; /* */ goto exit_function; } } /* atoms that may exchange isotopic H */ if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { for ( i = 1; i < n; i ++ ) { j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; k = (int)nNormAtNosInCanonOrd[j - 1]; if ( !inf_norm_at[k].nTautGroupCanonNbr ) { inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; } } } #if ( DISPLAY_RING_SYSTEMS == 1 ) /* debug only */ for ( j = 0; j < num_at; j ++ ) { inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; #if ( USE_DISTANCES_FOR_RANKING == 1 ) inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; #else inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; inf_norm_at[j].nTautGroupEquNbr = 0; #endif } #endif /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ len_str = sizeof(inf_norm_at[0].at_string); for ( i = 0; i < init_num_at; i ++ ) { str = inf_norm_at[i].at_string; len = 0; bIncludeIsotopicH = bIsotopic && !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT); /* isotopic mass */ atw = 0; if ( norm_at[i].iso_atw_diff && bIsotopic ) { if ( norm_at[i].at_type == ATT_PROTON ) { ; /* do not set isotopic mass of a tautomeric proton */ } else if ( norm_at[i].el_number == PERIODIC_NUMBER_H && norm_at[i].chem_bonds_valence == 1 && !norm_at[i].charge && !norm_at[i].radical && !norm_at[i].num_H && (inf_norm_at[j=(int)norm_at[i].neighbor[0]].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { ; /* do not set isotopic mass of an exchangeable proton */ } else { atw = get_atw(norm_at[i].elname); atw += (norm_at[i].iso_atw_diff>0)? norm_at[i].iso_atw_diff-1:norm_at[i].iso_atw_diff; /*len += sprintf( str+len, "^%d", atw );*/ } } /* element name */ if ( norm_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { len += sprintf( str+len, "%s", atw==2? "D" : "T" ); } else { if ( atw ) { len += sprintf( str+len, "^%d", atw ); } len += sprintf( str+len, "%s", norm_at[i].elname ); } /* hydrogens */ /* find number of previuosly removed terminal hydrogen atoms because these terminal H will be displayed */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { num_iso_H[j] = norm_at[i].num_iso_H[j]; } /* n = number of implicit H to display */ for ( j = num_at, n = (int)norm_at[i].num_H; j < init_num_at; j ++ ) { /* subtract number of removed terminal */ /* H atoms from the total number of H atoms */ if ( i == (int)norm_at[j].neighbor[0] ) { n -= 1; /* found explicit H => decrement number of implicit H */ m = (int)norm_at[j].iso_atw_diff-1; if ( 0 <= m && m < NUM_H_ISOTOPES ) { /* subtract number of removed terminal isotopic H */ /* atoms from the total number of isotopic H atoms */ num_iso_H[m] -= 1; } } } /* at this point n = number of implicit H to display, num_iso_H[] contains number of implicit isotopic H among n */ if ( bIncludeIsotopicH ) { /* subtract number of isotopic H atoms from the total number of H atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { n -= num_iso_H[j]; } } /* non-isotopic hydrogen atoms */ if ( n > 1 ) { len += sprintf( str+len, "H%d", n ); } else if ( n == 1 ) { len += sprintf( str+len, "H" ); } /* isotopic hydrogen atoms */ if ( bIncludeIsotopicH ) { for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { if ( num_iso_H[j] ) { if ( j == 0 || j != 1 && j != 2 ) { len += sprintf( str+len, "^%dH", j+1 ); } else { len += sprintf( str+len, j == 1? "D" : "T" ); } if ( num_iso_H[j] != 1 ) { len += sprintf( str+len, "%d", (int)num_iso_H[j] ); } } } } if ( norm_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { char *q; if ( !str[2] ) { str[1] = '2'; /* quick fix: replace HH with H2 */ } else if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { len = 1 + sprintf( str+1, "%d", n+1 ); } } /* if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { str[1] = '2'; } */ /* charge */ if ( abs(norm_at[i].charge) > 1 ) len += sprintf( str+len, "%+d", norm_at[i].charge ); else if ( abs(norm_at[i].charge) == 1 ) len += sprintf( str+len, "%s", norm_at[i].charge>0? "+" : "-" ); /* radical */ if ( norm_at[i].radical ) len += sprintf( str+len, "%s", norm_at[i].radical==RADICAL_SINGLET? ":" : norm_at[i].radical==RADICAL_DOUBLET? "." : norm_at[i].radical==RADICAL_TRIPLET? ".." : "?"); } /* stereogenic centers */ if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { for ( i = 0; i < num_stereo; i ++ ) { j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; c = t_parity[i]; c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; inf_norm_at[j].cStereoCenterParity = c; str=inf_norm_at[j].at_string; len = strlen(str); if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { str[len++] = '('; str[len++] = inf_norm_at[j].cStereoCenterParity; str[len++] = ')'; str[len] = '\0'; /* mark ambuguous stereo center */ if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { memmove( str+1, str, len+1 ); str[0] = '!'; /* output the atom in red color */ } } } } /* stereogenic bonds */ /* (cumulenes with odd number of double bonds are stereocenters, */ /* and atom parity should be set) */ if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { for ( i = 0; i < num_stereo; i ++ ) { int start_at, num_eql=0, bAmbiguousStereoBond = 0; j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; start_at = j; c = Stereo->b_parity[i]; c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; /* mark ambuguous stereo bond atom(s) */ if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && str[0] != '!' ) { memmove( str+1, str, len ); str[0] = '!'; /* output the atom in red color */ bAmbiguousStereoBond ++; } if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && str[0] != '!' ) { memmove( str+1, str, len ); str[0] = '!'; /* output the atom in red color */ bAmbiguousStereoBond ++; } /* find the opposite atom k. */ /* Note: since it may be a cumulene, find the shortest(best) path */ /* to atom number k to avoid confusion in case of, for example, */ /* 4-member aromatic rings. */ best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ for ( n = 0; n < norm_at[j].valence; n ++ ) { if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { /* single bond cannot be stereogenic. */ continue; } /* best_len = MAX_CUMULENE_LEN+1; */ len = 0; /* number of bonds in cumulene - 1 */ next_atom[len] = (int)norm_at[j].neighbor[n]; next_neigh[len] = n; cur_atom = j; while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0] == cur_atom); next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]; cur_atom = next_atom[len]; len ++; } if ( next_atom[len] == k ) { if ( len < best_len ) { memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); best_len = len; num_eql = 0; if ( len == 0 ) { break; /* path length cannot be smaller than 1 */ } } else if ( len == best_len ) { num_eql ++; } } } if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { if ( num_eql ) { num_err ++; /* program error; no breakpoint here */ } if ( best_len % 2 ) { /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ j = best_next_atom[best_len/2]; inf_norm_at[j].cStereoCenterParity = c; str=inf_norm_at[j].at_string; len = strlen(str); if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { str[len++] = '('; str[len++] = inf_norm_at[j].cStereoCenterParity; str[len++] = ')'; str[len] = '\0'; } } else { /* odd number of bonds: draw parity on the central bond */ if ( best_len == 0 ) { /* double bond */ j = start_at; k = best_next_neigh[0]; } else { /* cumulene */ best_len = best_len/2-1; j = best_next_atom[best_len]; k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ } /* mark "forward" bond */ for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) ; if ( m < MAX_STEREO_BONDS ) { inf_norm_at[j].cStereoBondParity[m] = c; inf_norm_at[j].cStereoBondNumber[m] = k; inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; } else { num_err ++; /* program error; no breakpoint here */ } /* mark "backward" bond */ n = norm_at[j].neighbor[k]; for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]; k ++ ) ; if ( k < norm_at[n].valence ) { j = n; for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) ; if ( m < MAX_STEREO_BONDS ) { inf_norm_at[j].cStereoBondParity[m] = c; inf_norm_at[j].cStereoBondNumber[m] = k; inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; } else { num_err ++; /* program error; no breakpoint here */ } } else { num_err ++; /* program error; no breakpoint here */ } } } else { num_err ++; /* program error; no breakpoint here */ } } } for ( i = 0; i < init_num_at; i ++ ) { /* canonical numbers */ if ( inf_norm_at[i].nCanonNbr ) { str = inf_norm_at[i].at_string; len = strlen(str); len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { if ( inf_norm_at[i].nCanonEquNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); } else if ( len + 1 < len_str ) { len += 1; strcat( str, "/" ); } } /* tautomeric groups */ if ( inf_norm_at[i].nTautGroupCanonNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); if ( inf_norm_at[i].nTautGroupEquNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); } } if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { str[len++] = '/'; str[len++] = '*'; str[len] = '\0'; } #ifdef DISPLAY_DEBUG_DATA if ( inf_norm_at[i].nDebugData ) { len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); } #endif } } exit_function: if ( nNormAtNosInCanonOrd ) inchi_free( nNormAtNosInCanonOrd ); return ret; } /***************************************************************************************/ int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) { int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len, bIncludeIsotopicH; int num_iso_H[NUM_H_ISOTOPES]; char *str; AT_NUMB g, e; int num_at = pINChI->nNumberOfAtoms; int num_H = init_num_at - num_at; /* number of removed H */ int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; AT_NUMB *nOrigAtNosInCanonOrd; INChI_Stereo *Stereo; AT_NUMB *nConstitEquNumbers; AT_NUMB *nConstitEquTGroupNumbers; S_CHAR *t_parity = NULL; AT_NUMB *nNumber = NULL; AT_NUMB *nNormAtNosInCanonOrd; int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); int bRelRac= bRel || bRac; int bDoDisplaySp3 = 1; inf_ATOM *inf_norm_at = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset : NULL; inp_ATOM *norm_at = inp_norm_at? inp_norm_at + offset : NULL; inf_ATOM *inf_norm_at_H = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset_H : NULL; inp_ATOM *norm_at_H = inp_norm_at? inp_norm_at + offset_H : NULL; ret = 0; num_err = 0; if ( !inf_norm_at ) return ret; /* -- already added in FillOutCompositeCanonInfAtom() -- if ( bIsotopic ) { for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { if ( pINChI_Aux->nNumRemovedIsotopicH[i] ) { inf_norm_at_data->num_iso_H[i] += pINChI_Aux->nNumRemovedIsotopicH[i]; inf_norm_at_data->num_removed_iso_H ++; } } } */ if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) bIsotopic = 0; Stereo = bIsotopic? pINChI->StereoIsotopic : pINChI->Stereo; bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { bDoDisplaySp3 = 0; if ( Stereo->nCompInv2Abs ) { inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; } } #endif /* flag has stereo */ if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { (*pStereoFlags) |= INF_STEREO; } /* if ( bDoDisplaySp3 && bRelRac && Stereo->nCompInv2Abs && Stereo->nNumberOfStereoCenters < 2 ) { bDoDisplaySp3 = 0; } */ if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { /* inversion changes stereo */ if ( bRel ) { (*pStereoFlags) |= INF_STEREO_REL; } else if ( bRac ) { (*pStereoFlags) |= INF_STEREO_RAC; } else { (*pStereoFlags) |= INF_STEREO_ABS; } if ( bRelRac ) { (*pStereoFlags) |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; } } if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { /* display Inv stereo which is Absolute Stereo */ nNumber = Stereo->nNumberInv; t_parity = Stereo->t_parityInv; nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : pINChI_Aux->nOrigAtNosInCanonOrdInv; } else { /* display Output stereo which is Absolute Stereo */ if ( bDoDisplaySp3 ) { nNumber = Stereo->nNumber; t_parity = Stereo->t_parity; } nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : pINChI_Aux->nOrigAtNosInCanonOrd; } nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : pINChI_Aux->nConstitEquNumbers; nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : pINChI_Aux->nConstitEquTGroupNumbers; memset( inf_norm_at, 0, num_at*sizeof(inf_norm_at[0]) ); if ( num_H > 0 ) { memset( inf_norm_at_H, 0, num_H*sizeof(inf_norm_at[0]) ); } /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { goto exit_function; } /* atom canonical and equivalence numbers > 0 */ for ( i = 0; i < num_at; i ++ ) { j = (int)nNormAtNosInCanonOrd[i]; if ( j < 0 || j >= num_at ) continue; inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; #ifdef DISPLAY_DEBUG_DATA inf_norm_at[j].nDebugData = 0; #if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) inf_norm_at[j].nDebugData = norm_at[j].c_point; #endif #endif } /* tautomeric groups */ if ( nNumberOfTGroups ) { /* : start from 1: bypass number of t-groups : j is a counter within the current t-group : g is a tautomeric group canonical number : e is a tautomeric group equivalence */ for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ e = nConstitEquTGroupNumbers[(int)g - 1]; /* bypass number of hydrogen atoms, negative charges, ... */ for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { /* scan canonical numbers of atoms within the atom t-group */ k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; inf_norm_at[k].nTautGroupCanonNbr = g; inf_norm_at[k].nTautGroupEquNbr = e; } } if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { ret = CT_TAUCOUNT_ERR; /* */ goto exit_function; } } /* atoms that may exchange isotopic H */ if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { for ( i = 1; i < n; i ++ ) { j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; k = (int)nNormAtNosInCanonOrd[j - 1]; if ( !inf_norm_at[k].nTautGroupCanonNbr ) { inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; } } } #if ( DISPLAY_RING_SYSTEMS == 1 ) /* debug only */ for ( j = 0; j < num_at; j ++ ) { inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; #if ( USE_DISTANCES_FOR_RANKING == 1 ) inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; #else inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; inf_norm_at[j].nTautGroupEquNbr = 0; #endif } #endif /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ len_str = sizeof(inf_norm_at[0].at_string); for ( i = 0; i < init_num_at; i ++ ) { inf_ATOM *cur_inf_norm_at = (i < num_at)? inf_norm_at+i : inf_norm_at_H+i-num_at; inp_ATOM *cur_norm_at = (i < num_at)? norm_at +i : norm_at_H +i-num_at; str = cur_inf_norm_at->at_string; len = 0; bIncludeIsotopicH = bIsotopic && (i >= num_at || !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT)); /* isotopic mass */ atw = 0; if ( cur_norm_at->iso_atw_diff && bIsotopic ) { if ( cur_norm_at->at_type == ATT_PROTON ) { ; /* do not set isotopic mass of a tautomeric proton */ } else if ( num_at <= i && cur_norm_at->el_number == PERIODIC_NUMBER_H && cur_norm_at->chem_bonds_valence == 1 && !cur_norm_at->charge && !cur_norm_at->radical && !cur_norm_at->num_H && 0 <= (j=(int)cur_norm_at->neighbor[0]-offset) && j < num_at && (inf_norm_at[j].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { ; /* do not set isotopic mass of an exchangeable proton */ } else { atw = get_atw(cur_norm_at->elname); atw += (cur_norm_at->iso_atw_diff>0)? cur_norm_at->iso_atw_diff-1:cur_norm_at->iso_atw_diff; /*len += sprintf( str+len, "^%d", atw );*/ } } /* element name */ if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { len += sprintf( str+len, "%s", atw==2? "D" : "T" ); } else { if ( atw ) { len += sprintf( str+len, "^%d", atw ); } len += sprintf( str+len, "%s", cur_norm_at->elname ); } /* hydrogens */ /* find number of previuosly removed terminal hydrogen atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { num_iso_H[j] = cur_norm_at->num_iso_H[j]; } for ( j = 0, n = (int)cur_norm_at->num_H; j < num_H; j ++ ) { /* subtract number of removed terminal */ /* H atoms from the total number of H atoms */ if ( i == (int)norm_at_H[j].neighbor[0]-offset ) { n -= 1; m = (int)norm_at_H[j].iso_atw_diff-1; if ( 0 <= m && m < NUM_H_ISOTOPES ) { /* subtract number of removed terminal isotopic */ /* H atoms from the total number of isotopic H atoms */ num_iso_H[m] -= 1; } } } if ( bIncludeIsotopicH ) { /* subtract number of isotopic H atoms from the total number of H atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { n -= num_iso_H[j]; } } /* non-isotopic hydrogen atoms */ if ( n > 1 ) { len += sprintf( str+len, "H%d", n ); } else if ( n == 1 ) { len += sprintf( str+len, "H" ); } /* isotopic hydrogen atoms */ if ( bIncludeIsotopicH ) { for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { if ( num_iso_H[j] ) { if ( j == 0 || j != 1 && j != 2 ) { len += sprintf( str+len, "^%dH", j+1 ); } else { len += sprintf( str+len, j == 1? "D" : "T" ); } if ( num_iso_H[j] != 1 ) { len += sprintf( str+len, "%d", (int)num_iso_H[j] ); } } } } if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { char *q; if ( !str[2] ) { str[1] = '2'; /* quick fix: replace HH with H2 */ } else if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { len = 1 + sprintf( str+1, "%d", n+1 ); } } /* charge */ if ( abs(cur_norm_at->charge) > 1 ) len += sprintf( str+len, "%+d", cur_norm_at->charge ); else if ( abs(cur_norm_at->charge) == 1 ) len += sprintf( str+len, "%s", cur_norm_at->charge>0? "+" : "-" ); /* radical */ if ( cur_norm_at->radical ) len += sprintf( str+len, "%s", cur_norm_at->radical==RADICAL_SINGLET? ":" : cur_norm_at->radical==RADICAL_DOUBLET? "." : cur_norm_at->radical==RADICAL_TRIPLET? ".." : "?"); } /* stereogenic centers */ if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { for ( i = 0; i < num_stereo; i ++ ) { j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; c = t_parity[i]; c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; inf_norm_at[j].cStereoCenterParity = c; str=inf_norm_at[j].at_string; len = strlen(str); if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { str[len++] = '('; str[len++] = inf_norm_at[j].cStereoCenterParity; str[len++] = ')'; str[len] = '\0'; /* mark ambuguous stereo center */ if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { memmove( str+1, str, len+1 ); str[0] = '!'; /* output the atom in red color */ } } } } /* stereogenic bonds */ /* (cumulenes with odd number of double bonds are stereocenters, */ /* and atom parity should be set) */ if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { for ( i = 0; i < num_stereo; i ++ ) { int start_at, num_eql=0, bAmbiguousStereoBond = 0; j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; start_at = j; c = Stereo->b_parity[i]; c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; /* mark ambuguous stereo bond atom(s) */ if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && str[0] != '!' ) { memmove( str+1, str, len ); str[0] = '!'; /* output the atom in red color */ bAmbiguousStereoBond ++; } if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && str[0] != '!' ) { memmove( str+1, str, len ); str[0] = '!'; /* output the atom in red color */ bAmbiguousStereoBond ++; } /* find the opposite atom k. */ /* Note: since it may be a cumulene, find the shortest(best) path */ /* to atom number k to avoid confusion in case of, for example, */ /* 4-member aromatic rings. */ best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ for ( n = 0; n < norm_at[j].valence; n ++ ) { if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { /* single bond cannot be stereogenic. */ continue; } /* best_len = MAX_CUMULENE_LEN+1; */ len = 0; /* number of bonds in cumulene - 1 */ next_atom[len] = (int)norm_at[j].neighbor[n]-offset; next_neigh[len] = n; cur_atom = j; while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0]-offset == cur_atom); next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]-offset; cur_atom = next_atom[len]; len ++; } if ( next_atom[len] == k ) { if ( len < best_len ) { memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); best_len = len; num_eql = 0; if ( len == 0 ) { break; /* path length cannot be smaller than 1 */ } } else if ( len == best_len ) { num_eql ++; } } } if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { if ( num_eql ) { num_err ++; /* program error; no breakpoint here */ } if ( best_len % 2 ) { /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ j = best_next_atom[best_len/2]; inf_norm_at[j].cStereoCenterParity = c; str=inf_norm_at[j].at_string; len = strlen(str); if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { str[len++] = '('; str[len++] = inf_norm_at[j].cStereoCenterParity; str[len++] = ')'; str[len] = '\0'; } } else { /* odd number of bonds: draw parity on the central bond */ if ( best_len == 0 ) { /* double bond */ j = start_at; k = best_next_neigh[0]; } else { /* cumulene */ best_len = best_len/2-1; j = best_next_atom[best_len]; k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ } /* mark "forward" bond */ for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) ; if ( m < MAX_STEREO_BONDS ) { inf_norm_at[j].cStereoBondParity[m] = c; inf_norm_at[j].cStereoBondNumber[m] = k; inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; } else { num_err ++; /* program error; no breakpoint here */ } /* mark "backward" bond */ n = norm_at[j].neighbor[k]-offset; for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]-offset; k ++ ) ; if ( k < norm_at[n].valence ) { j = n; for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) ; if ( m < MAX_STEREO_BONDS ) { inf_norm_at[j].cStereoBondParity[m] = c; inf_norm_at[j].cStereoBondNumber[m] = k; inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; } else { num_err ++; /* program error; no breakpoint here */ } } else { num_err ++; /* program error; no breakpoint here */ } } } else { num_err ++; /* program error; no breakpoint here */ } } } for ( i = 0; i < num_at; i ++ ) { /* canonical numbers */ if ( inf_norm_at[i].nCanonNbr ) { str = inf_norm_at[i].at_string; len = strlen(str); len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { if ( inf_norm_at[i].nCanonEquNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); } else if ( len + 1 < len_str ) { len += 1; strcat( str, "/" ); } } /* tautomeric groups */ if ( inf_norm_at[i].nTautGroupCanonNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); if ( inf_norm_at[i].nTautGroupEquNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); } } if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { str[len++] = '/'; str[len++] = '*'; str[len] = '\0'; } #ifdef DISPLAY_DEBUG_DATA if ( inf_norm_at[i].nDebugData ) { len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); } #endif } } exit_function: if ( nNormAtNosInCanonOrd ) inchi_free( nNormAtNosInCanonOrd ); return ret; } /***************************************************************************************/ int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers) { int i, j, m, n, ret, len_str, len, atw; int num_iso_H[NUM_H_ISOTOPES]; char *str; int num_at = init_num_at - num_removed_H; int (*MakeNumber)(char*, int, const char*, int) = MakeDecNumber; inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; ret = 0; if ( !inf_at ) return ret; memset( inf_at, 0, init_num_at*sizeof(inf_at[0]) ); inf_at_data->nNumRemovedProtons = nNumRemovedProtons; MakeRemovedProtonsString( nNumRemovedProtons, nNumRemovedProtonsIsotopic, NULL, bIsotopic, inf_at_data->szRemovedProtons, NULL ); /* atom canonical and equivalence numbers > 0 */ for ( i = 0; i < num_at; i ++ ) { #if ( DISPLAY_ORIG_AT_NUMBERS == 1 ) inf_at[i].nCanonNbr = inp_at[i].orig_at_number; #else inf_at[i].nCanonNbr = (AT_NUMB)(i+1); #endif } /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ len_str = sizeof(inf_at[0].at_string); for ( i = 0; i < init_num_at; i ++ ) { str = inf_at[i].at_string; len = 0; /* isotopic mass */ atw = 0; if ( inp_at[i].iso_atw_diff && bIsotopic ) { atw = get_atw(inp_at[i].elname); atw += (inp_at[i].iso_atw_diff>0)? inp_at[i].iso_atw_diff-1:inp_at[i].iso_atw_diff; /*len += sprintf( str+len, "^%d", atw );*/ } /* element name */ if ( inp_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { len += sprintf( str+len, "%s", atw==2? "D" : "T" ); } else { if ( atw ) { len += sprintf( str+len, "^%d", atw ); } len += sprintf( str+len, "%s", inp_at[i].elname ); } /* hydrogens */ /* find number of previuosly removed terminal hydrogen atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { num_iso_H[j] = inp_at[i].num_iso_H[j]; } for ( j = num_at, n = (int)inp_at[i].num_H; j < init_num_at; j ++ ) { /* subtract number of removed terminal */ /* H atoms from the total number of H atoms */ if ( i == (int)inp_at[j].neighbor[0] ) { n -= 1; m = (int)inp_at[j].iso_atw_diff-1; if ( 0 <= m && m < NUM_H_ISOTOPES ) { /* subtract number of removed terminal isotopic */ /* H atoms from the total number of isotopic H atoms */ num_iso_H[m] -= 1; } } } if ( bIsotopic && !bAdd_DT_to_num_H ) { /* subtract number of isotopic H atoms from the total number of H atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { n -= num_iso_H[j]; } } /* non-isotopic hydrogen atoms */ if ( n > 1 ) { len += sprintf( str+len, "H%d", n ); } else if ( n == 1 ) { len += sprintf( str+len, "H" ); /* fixed 12-21-2002: removed 3rd argument */ } if ( bIsotopic ) { /* isotopic hydrogen atoms */ for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { if ( num_iso_H[j] ) { if ( j == 0 || j != 1 && j != 2 ) { len += sprintf( str+len, "^%dH", j+1 ); } else { len += sprintf( str+len, j == 1? "D" : "T" ); } if ( num_iso_H[j] != 1 ) { len += sprintf( str+len, "%d", (int)num_iso_H[j] ); } } } } if ( inp_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { char *q; if ( !str[2] ) { str[1] = '2'; /* quick fix: replace HH with H2 */ } else if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { len = 1 + sprintf( str+1, "%d", n+1 ); } } /* if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { str[1] = '2'; } */ /* charge */ if ( abs(inp_at[i].charge) > 1 ) len += sprintf( str+len, "%+d", inp_at[i].charge ); else if ( abs(inp_at[i].charge) == 1 ) len += sprintf( str+len, "%s", inp_at[i].charge>0? "+" : "-" ); /* radical */ if ( inp_at[i].radical ) len += sprintf( str+len, "%s", inp_at[i].radical==RADICAL_SINGLET? ":" : inp_at[i].radical==RADICAL_DOUBLET? "." : inp_at[i].radical==RADICAL_TRIPLET? ".." : "?"); } for ( i = 0; i < init_num_at; i ++ ) { /* canonical numbers */ if ( inf_at[i].nCanonNbr ) { str = inf_at[i].at_string; len = strlen(str); len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonNbr ); if ( inf_at[i].nCanonEquNbr || inf_at[i].nTautGroupCanonNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonEquNbr ); } /* tautomeric groups */ if ( inf_at[i].nTautGroupCanonNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupCanonNbr ); if ( inf_at[i].nTautGroupEquNbr ) { len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupEquNbr ); } } } } ret = init_num_at; return ret; } /**********************************************************************************************/ int FillOutInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode ) { if ( norm_at && inf_norm_at_data && inf_norm_at_data->at ) { if ( pINChI && pINChI_Aux ) { return FillOutCanonInfAtom( norm_at, inf_norm_at_data, init_num_at, bIsotopic, pINChI, pINChI_Aux, bAbcNumbers, nMode); } else { return FillOutInputInfAtom( norm_at, inf_norm_at_data, init_num_at, num_removed_H, bAdd_DT_to_num_H, nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers); } } return 0; } /***************************************************************************************/ int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, INF_ATOM_DATA *inf_norm_at_data, int bIsotopic, int bTautomeric, PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, INCHI_MODE nMode) { int i, num_components, j, k, ret; inp_ATOM *inp_norm_at; INChI *pINChI; INChI_Aux *pINChI_Aux; int num_inp_at, num_at, num_H, offset, offset_H, next_offset, next_offset_H; if ( composite_norm_data && inf_norm_at_data && (bTautomeric == TAUT_INI || pINChI2 && pINChI_Aux2) ) { composite_norm_data += bTautomeric; inp_norm_at = composite_norm_data->at; num_components = composite_norm_data->num_components; offset = 0; offset_H = composite_norm_data->num_at - composite_norm_data->num_removed_H; if ( bTautomeric == TAUT_INI ) { ret = FillOutInputInfAtom( composite_norm_data->at, inf_norm_at_data, composite_norm_data->num_at, composite_norm_data->num_removed_H, 0 /*bAdd_DT_to_num_H*/, composite_norm_data->nNumRemovedProtons, composite_norm_data->nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers); return ret; } else { for ( i = 0; i < num_components; i ++ ) { j = inchi_min(bTautomeric, TAUT_YES); /* count isotopic H on removed atoms -- isolated H(+) cations */ inf_norm_at_data->nNumRemovedProtons += pINChI_Aux2[i][j]->nNumRemovedProtons; if ( bIsotopic && bTautomeric == TAUT_YES ) { for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { if ( pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k] ) { inf_norm_at_data->num_iso_H[k] += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; inf_norm_at_data->num_removed_iso_H += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; } } } /* ignore deleted components */ if ( pINChI2[i][j] && pINChI2[i][j]->bDeleted ) { continue; } if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { j = ALT_TAUT(j); if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { continue; /* error ??? */ } } pINChI = pINChI2[i][j]; pINChI_Aux = pINChI_Aux2[i][j]; next_offset = composite_norm_data->nOffsetAtAndH[2*i]; next_offset_H = composite_norm_data->nOffsetAtAndH[2*i+1]; num_at = next_offset - offset; if ( num_at <= 0 ) continue; num_H = next_offset_H - offset_H; num_inp_at = num_at + num_H; if ( num_at != pINChI->nNumberOfAtoms || num_at != pINChI_Aux->nNumberOfAtoms ) { return 0; /* error */ } ret = FillOutOneCanonInfAtom(inp_norm_at, inf_norm_at_data, inf_norm_at_data->pStereoFlags+i+1, num_inp_at, offset, offset_H, bIsotopic, pINChI, pINChI_Aux, bAbcNumbers, nMode); if ( ret ) return 0; /* error */ inf_norm_at_data->StereoFlags |= inf_norm_at_data->pStereoFlags[i+1]; offset = next_offset; offset_H = next_offset_H; } } MakeRemovedProtonsString( inf_norm_at_data->nNumRemovedProtons, inf_norm_at_data->num_iso_H, NULL, bIsotopic, inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); } return 1; } #endif /* } ifndef COMPILE_ANSI_ONLY */ /**********************************************************************************************/ int CheckCanonNumberingCorrectness(int num_atoms, int num_at_tg, sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, char *pStrErrStruct ) { int i, ret=0; AT_NUMB *pCanonOrd=NULL; int nErrorCode = 0; AT_NUMB *pCanonRank; /* canonical ranks of the atoms or tautomeric groups */ AT_NUMB *pCanonRankAtoms=NULL; static int count=0; /* for debug only */ count ++; pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); /********************************************************************************************** * * non-isotopic part */ pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; pCanonRank = pCanonRankAtoms; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_at_tg; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { nErrorCode |= WARN_FAILED_STEREO; } } else { nErrorCode |= ERR_NO_CANON_RESULTS; goto exit_function; } /********************************************************************************************** * * isotopic part */ pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; pCanonRank = pCanonRankAtoms; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_at_tg; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { nErrorCode |= (pCS->nLenCanonOrdIsotopicStereo? WARN_FAILED_ISOTOPIC_STEREO : WARN_FAILED_ISOTOPIC); } } exit_function: if ( pCanonRankAtoms ) inchi_free( pCanonRankAtoms ); if ( nErrorCode ) { return CT_CANON_ERR; } return 0; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimake.h000066400000000000000000000330601271037650300234760ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHIMAKE_H__ #define __INCHIMAKE_H__ /***********************************************************************/ /* replace all ' ' delimiters with ',' */ #define ITEM_DELIMETER "," #define EXTRA_SPACE "" #define COMMA_EXTRA_SPACE "," #define LEN_EXTRA_SPACE 0 /**********************************************************************************************/ /* nCtMode for output INChI */ #define CT_MODE_NO_ORPHANS 1 /* no orphans, that CT should have only atoms with neighbors */ #define CT_MODE_ABC_NUMBERS 2 #define CT_MODE_ATOM_COUNTS 4 #define CT_MODE_PREDECESSORS 8 #define CT_MODE_EQL_H_TOGETHER 16 #define CT_MODE_ABC_NUM_CLOSURES 32 /* in CT_MODE_ABC_NUMBERS output AB1AC2AB instead of AB-AC-A-B */ /*************** Macros for retrieving requested INChI and INChI_Aux *****************************/ /* S->pINChI[TAUT_YES] has info: */ #define HAS_T(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms) /* S->pINChI[TAUT_NON] has info: */ #define HAS_N(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms) /* S->pINChI[TAUT_YES] has tautomeric info: */ #define HAS_TT(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && S->pINChI[TAUT_YES]->lenTautomer>0) /* S->pINChI[TAUT_YES] has non-taitomeric info: */ #define HAS_TN(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && !S->pINChI[TAUT_YES]->lenTautomer) /* S->pINChI[TAUT_NON] has non-tautomeric info: */ #define HAS_NN(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms && !S->pINChI[TAUT_NON]->lenTautomer) #define GET_II(M,S) ((M==OUT_N1)? (HAS_TN(S)? TAUT_YES : HAS_NN(S)? TAUT_NON : -1): \ (M==OUT_T1 || M==OUT_TN)? (HAS_T(S) ? TAUT_YES : HAS_N(S) ? TAUT_NON : -1): \ (M==OUT_NN)? (HAS_NN(S)? TAUT_NON : HAS_TN(S)? TAUT_YES : -1): \ (M==OUT_NT)? ((HAS_TT(S) && HAS_NN(S)) ? TAUT_NON : -1) : -1) /*********************************/ /* Equivalence flags definitions */ /*********************************/ /* What is equal (ii = INChI_ITEM) */ #define iiSTEREO 0x0001 /* stereo (sp2 or sp3) */ #define iiSTEREO_INV 0x0002 /* inverted stereo (sp3) */ #define iiNUMB 0x0004 /* numbering or inverted stereo numbering */ #define iiEQU 0x0008 /* equivalence info */ /* derived: (iiSTEREO_INV | iiNUMB) = numbering of inverted stereo */ /* Additional info to what is equal (INCHI_ITEM_TYPE = iit) */ #define iitISO 0x0010 /* Isotopic */ #define iitNONTAUT 0x0020 /* Non-tautomeric */ /* derived: (iitISO | iitNONTAUT) = isotopic non-tautomeric */ /* Where is the equivalent item located (INChI_ITEM_EQUAL_TO = iiEq2) */ #define iiEq2NONTAUT 0x0040 /* non-tautomeric */ #define iiEq2ISO 0x0080 /* isotopic */ #define iiEq2INV 0x0100 /* equal to inverted (stereo sp3) or to numbering of inverted stereo */ #define iiEmpty 0x0200 /* item is empty while in the preceding layer the item is not empty */ /*********************** Printing strings external declarations *******************************/ extern const char sCompDelim[]; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /**********************************************************************************************/ int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, int num_atoms, int num_at_tg, int num_removed_H, sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, INCHI_MODE nUserMode, char *pStrErrStruct ); int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ); int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ); int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, S_CHAR *nNum_H, int num_atoms, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ); AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ); const char *EquString( int EquVal ); int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bUseMulipliers); int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bUseMulipliers); int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, int num_components, int bUseMulipliers); int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components); int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components); int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions); int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers); int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions); int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions); int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions); int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components); int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, int *bOverflow, int TAUT_MODE, int num_components); int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /*__INCHIMAKE_H__*/ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimap1.c000066400000000000000000001134171271037650300234170ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #include "ichicomp.h" /**************************************************************************************/ /* Check if all equivalent to cr1 possibly stereogenic atoms: */ /* 1) have KNOWN parity, and */ /* 2) their parities are same */ /**************************************************************************************/ int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nAtomNumberCanonFrom, const sp_ATOM *at ) { int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; AT_RANK r1 = pRankStack1[0][n1]; int iMax1 = (int)r1; int i1, s1; int bFound=0, stereo_atom_parity=-1; /* find one stereo atom such that canon_rank1 can be mapped on it */ for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { if ( at[s1].stereo_bond_neighbor[0] ) { bFound=0; /* at[s1] is not sp3-stereogenic: it belongs to a stereobond */ break; } else if ( i1 == 1 ) { stereo_atom_parity = PARITY_VAL(at[s1].stereo_atom_parity); if ( !ATOM_PARITY_KNOWN(stereo_atom_parity) ) { bFound=0; /* at[s1] does not have a KNOWN parity */ break; } } else if ( stereo_atom_parity != PARITY_VAL(at[s1].stereo_atom_parity) ) { bFound=0; /* two equivalent atoms have different parities */ break; } bFound ++; } return bFound; } /**************************************************************************************/ /* get next available (not mapped yet) rank for a stereo center atom */ int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ /* output: suggested canon. rank > than input if success */ AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ int *bFirstTime, /* 1 at the time of the 1st call */ S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ int num_atoms ) { AT_RANK canon_rank1_inp = *canon_rank1; AT_RANK cr1; /* canonical rank (canonical number) */ AT_RANK r1; /* mapping rank */ int n1; /* ord. number of an atom with the canon. number */ int s1; /* ord. number of an atom with stereo */ int i1, bFound=0; int iMax1; if ( canon_rank1_inp < *canon_rank1_min ) { canon_rank1_inp = *canon_rank1_min; } else if ( canon_rank1_inp < 1 ) { canon_rank1_inp = 1; } else { canon_rank1_inp ++; /* next canonical rank */ } cr1 = canon_rank1_inp; while ( (int) cr1 <= num_atoms ) { n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1 (which has canon. rank cr1) ord. number */ iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ /* find atoms "to" to which the canon. number can be mapped; they have mapping rank r1, number s1 */ for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { /* looking for a stereo center atom that has mapping rank r1 */ if ( bAtomUsedForStereo[s1] == STEREO_AT_MARK ) { /* found a sterogenic atom that has not been mapped yet */ bFound = 1; break; } } if ( bFound ) { /* one stereogenic not mapped yet atom "to" has been found */ if ( *bFirstTime ) { *canon_rank1_min = cr1; *bFirstTime = 0; } break; } else { /* a not mapped yet stereogenic atom has not found */ /* for the mapping rank r1 defined by the canonical rank cr1; try next cr1 */ cr1 ++; } } if ( bFound ) { /* success */ *canon_rank1 = cr1; return 1; } return 0; } /**********************************************************************/ int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ) { int i, num, ret = 0; /* compare double bonds */ if ( LinearCTStereoDble1 && LinearCTStereoDble2 ) { num = inchi_min(nLenLinearCTStereoDble1, nLenLinearCTStereoDble2); for ( i = 0; i < num; i ++ ) { if ( ret = (int)LinearCTStereoDble1[i].at_num1 - (int)LinearCTStereoDble2[i].at_num1 ) break; if ( ret = (int)LinearCTStereoDble1[i].at_num2 - (int)LinearCTStereoDble2[i].at_num2 ) break; if ( ret = (int)LinearCTStereoDble1[i].parity - (int)LinearCTStereoDble2[i].parity ) break; } if ( !ret ) { ret = nLenLinearCTStereoDble1 - nLenLinearCTStereoDble2; } } else if ( LinearCTStereoDble1 && nLenLinearCTStereoDble1 > 0 ) { ret = 1; } else if ( LinearCTStereoDble2 && nLenLinearCTStereoDble2 > 0 ) { ret = -1; } return ret; } /**********************************************************************/ int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) { int i, num, ret = 0; /* compare stereocenters */ if ( LinearCTStereoCarb1 && LinearCTStereoCarb2 ) { num = inchi_min(nLenLinearCTStereoCarb1, nLenLinearCTStereoCarb2); for ( i = 0; i < num; i ++ ) { if ( ret = (int)LinearCTStereoCarb1[i].at_num - (int)LinearCTStereoCarb2[i].at_num ) break; if ( ret = (int)LinearCTStereoCarb1[i].parity - (int)LinearCTStereoCarb2[i].parity ) break; } if ( !ret ) { ret = nLenLinearCTStereoCarb1 - nLenLinearCTStereoCarb2; } } else if ( LinearCTStereoCarb1 && nLenLinearCTStereoCarb1 > 0 ) { ret = 1; } else if ( LinearCTStereoCarb2 && nLenLinearCTStereoCarb2 > 0 ) { ret = -1; } return ret; } /**********************************************************************/ int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) { int ret; /* compare double bonds */ ret = CompareLinCtStereoDble ( LinearCTStereoDble1, nLenLinearCTStereoDble1, LinearCTStereoDble2, nLenLinearCTStereoDble2 ); if ( !ret ) { ret = CompareLinCtStereoCarb ( LinearCTStereoCarb1, nLenLinearCTStereoCarb1, LinearCTStereoCarb2, nLenLinearCTStereoCarb2 ); } return ret; } /**************************************************************************************/ int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, AT_RANK at_rank_canon1, U_CHAR parity ) { if ( LinearCTStereoCarb->at_num CT_GREATER_THAN at_rank_canon1 ) return 1; if ( LinearCTStereoCarb->at_num != at_rank_canon1 ) return -1; if ( LinearCTStereoCarb->parity CT_GREATER_THAN parity ) return 1; if ( LinearCTStereoCarb->parity != parity ) return -1; return 0; } /**************************************************************************************/ /* Find atom number from the mapping rank and return 1, or */ /* if the mapping rank is tied and the atom number is not unique then return 0 */ int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ) { int r = (int)nAtRank-1; AT_NUMB i = pRankStack[1][r]; if ( nAtRank == pRankStack[0][(int)i] && (!r || nAtRank != pRankStack[0][pRankStack[1][r-1]]) ) { *nAtNumber = i; return 1; } return 0; } /**************************************************************************************/ /* Get minimal set (class) representative and partially compress the partitioning */ /* mcr = minimal class representative. */ AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ) { AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ n1=nEqArray[(int)n]; if ( n == n1 ) { return n; } /* 1st pass: find mcr */ while ( n1 != (n2=nEqArray[(int)n1])) { n1 = n2; } /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ mcr = n1; n1 = n; while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { nEqArray[(int)n1]=mcr; n1 = n2; } return ( mcr ); } /**************************************************************************************/ /* Join 2 sets (classes) that have members n1 and n2 */ int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) { n1 = nGetMcr( nEqArray, n1 ); n2 = nGetMcr( nEqArray, n2 ); if ( n1 < n2 ) { nEqArray[n2] = n1; return 1; /* a change has been made */ } if ( n2 < n1 ) { nEqArray[n1] = n2; return 1; /* a change has been made */ } return 0; /* no changes */ } /********************************************************************************* * For all pairs of atoms that are: * * (a) connected by a possibly stereogenic bond * * (b) "equivalent" at this point to canon_rank1-canon_rank2 : * * Check if they: * * 1) are connected by a stereo bond or cumulene bonds of the same length * * 2) have KNOWN parity, and * * 3) their parities are same * *********************************************************************************/ int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nAtomNumberCanonFrom, sp_ATOM *at ) { int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; /* at1 has canon_rank1 */ int n2 = (int)nAtomNumberCanonFrom[(int)canon_rank2-1]; /* at2 has canon_rank2 */ AT_RANK r1 = pRankStack1[0][n1]; /* at1 mapping rank */ AT_RANK r2 = pRankStack1[0][n2]; /* at2 mapping rank */ AT_RANK rNeigh1, rNeigh2; int iMax1 = (int)r1; /* int iMax2 = (int)r2; */ int i1, i2, s1, s2, k1, k2, m, k, num_equal; int bNotFound=1, cumulene_len, stereo_bond_parity; /* at the first atom that possibly may have canon_rank1 find one stereo bond such that */ /* canon_rank1-canon_rank2 possibly may be mapped on it */ for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { /* at[n1] may be possible to map on at[s1] */ for ( k1 = 0, s2= 0, bNotFound=1; k1 < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k1]) && (bNotFound = (r2 != pRankStack2[0][--s2])); k1 ++ ) ; /* continue until the 1st at[s2] (to which at[n2] may be mapped) have been found */ if ( !bNotFound ) { break; /* stop at 1st found */ } } if ( bNotFound ) { return -1; /* error: no mapping exists */ } for ( k2 = 0, m = 0; k2 < MAX_NUM_STEREO_BONDS && (m=(int)at[s2].stereo_bond_neighbor[k2]) && m-1 != s1; k2 ++ ) ; if ( m-1 != s1 ) { return -1; /* program error: stereo bond in opposite direction not found */ } stereo_bond_parity = at[s1].stereo_bond_parity[k1]; if ( !PARITY_KNOWN(stereo_bond_parity) ) { return 0; } cumulene_len = BOND_CHAIN_LEN(stereo_bond_parity); rNeigh1 = pRankStack2[0][(int)at[s1].neighbor[(int)at[s1].stereo_bond_ord[k1]]]; rNeigh2 = pRankStack2[0][(int)at[s2].neighbor[(int)at[s2].stereo_bond_ord[k2]]]; num_equal = 0; /* Search among ALL neighbors because sometimes a stereo bond may be mapped on a non-stereo bond. */ /* If is so then return 0: not all mappings are stereo-equivalent */ for ( s1 = 1; s1 <= iMax1 && r1 == pRankStack2[0][i1=(int)pRankStack2[1][iMax1-s1]]; s1++ ) { for ( k = 0; k < at[i1].valence; k ++ ) { n1 = at[i1].neighbor[k]; if ( rNeigh1 != pRankStack2[0][n1] ) { continue; /* wrong neighbor */ } if ( cumulene_len ) { int prev, next, len, j; for ( prev=i1, len=0, next = n1; len < cumulene_len; len ++ ) { if ( at[next].valence == 2 && !at[next].num_H ) { j = ((int)at[next].neighbor[0] == prev); prev = next; next = at[next].neighbor[j]; } else { break; /* cannot continue */ } } if ( len != cumulene_len || r2 != pRankStack2[0][next] || rNeigh2 != pRankStack2[0][prev] ) { /* cumulene chain not found */ continue; } i2 = next; } else { i2 = n1; } /* find if a stereogenic bond between at[i1]-at[i2] exists */ for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (m=(int)at[i1].stereo_bond_neighbor[k1]) && m-1 != i2; k1 ++ ) ; if ( m-1 != i2 ) { return 0; } for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[k2]) && m-1 != i1; k2 ++ ) ; if ( m-1 != i1 ) { return 0; } if ( at[i1].stereo_bond_parity[k1] != at[i2].stereo_bond_parity[k2] ) { return -1; /* program error */ } if ( stereo_bond_parity != at[i1].stereo_bond_parity[k1] ) { return 0; } num_equal ++; } } return num_equal; } /**************************************************************************************/ /* get min. ranks for the stereo bond atoms */ int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, int *bFirstTime, S_CHAR *bAtomUsedForStereo, const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, const sp_ATOM *at, int num_atoms, int bAllene ) { AT_RANK canon_rank1_inp = *canon_rank1; AT_RANK canon_rank2_inp = *canon_rank2; AT_RANK cr1, cr2; /* canonical ranks (canonical numbers) */ AT_RANK r1, r2; /* mapping ranks */ int n1, n2; /* ord. numbers of atoms with stereo */ int s1, s2; /* ord. numbers of atoms with canon. numbers */ int i1, i2, k, m; int iMax1, iMax2; if ( canon_rank1_inp < *canon_rank1_min || canon_rank1_inp == *canon_rank1_min && canon_rank2_inp < *canon_rank2_min ) { canon_rank1_inp = *canon_rank1_min; canon_rank2_inp = *canon_rank2_min; } else if ( canon_rank1_inp < 2 ) { canon_rank1_inp = 2; canon_rank2_inp = 0; } cr1 = canon_rank1_inp; cr2 = num_atoms; /* initialize. 1/8/2002 */ while ( (int) cr1 <= num_atoms ) { cr2 = cr1; n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1=at[n1] (which has canon. rank) ord. number */ iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { /* looking for a stereo bond atom that has mapping rank r1 */ /* found at[s1] such that rank cr1 can be mapped on at[s1] because cr1 and s1 have equal */ /* mapping rank = r1. Check at[s1] stereo bonds */ if ( bAtomUsedForStereo[s1] && bAtomUsedForStereo[s1] < STEREO_AT_MARK ) { for ( k = 0, s2 = 0; k < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k]); k ++ ) { /* stereo bond at[s1]-at[s2] has been found */ if ( bAtomUsedForStereo[--s2] ) { /* stereo bonds have not been mapped. however, this check is not needed */ int cumulene_len = BOND_CHAIN_LEN(at[s1].stereo_bond_parity[k]); if ( cumulene_len%2 && !bAllene || /* 09-26-2003 */ !(cumulene_len%2) && bAllene ) { /* 08-17-2003 Fix05 */ continue; } iMax2 = (int)(r2 = pRankStack2[0][s2]); /* mapping rank of atom2 */ /* Go back to canonical ranks and find an atom that has mapping rank r2 */ /* and is connected to the atom with canonical rank cr1 (possibly by cumulene chain) */ /* These cr1-cr2 canon. ranks possibly can be mapped on at[s1]-at[s2] stereo bond */ for ( i2 = 1; i2 <= iMax2 && r2 == pRankStack1[0][n2=(int)pRankStack1[1][iMax2-i2]]; i2++ ) { if ( cumulene_len ) { int prev, next, len, j; for ( m = 0; m < at[n1].valence; m ++ ) { for ( prev=n1, len=0, next = (int)at[n1].neighbor[m]; len < cumulene_len; len ++ ) { if ( at[next].valence == 2 && !at[next].num_H ) { j = ((int)at[next].neighbor[0] == prev); prev = next; next = at[next].neighbor[j]; } else { break; /* cannot continue */ } } if ( len == cumulene_len && n2 == next ) { break; } } } else { for ( m = 0; m < at[n1].valence && n2 != (int)at[n1].neighbor[m]; m ++ ) ; } if ( m < at[n1].valence && nCanonRankFrom[n2] < cr2 && nCanonRankFrom[n2] > canon_rank2_inp ) { cr2 = nCanonRankFrom[n2]; /* found a candidate for cr2 */ } } } } } } if ( cr2 >= cr1 ) { /* not found for this r1 */ cr1 ++; canon_rank2_inp = 0; } else { /* found cr2 < cr1 */ if ( *bFirstTime ) { *canon_rank1_min = cr1; *canon_rank2_min = cr2; *bFirstTime = 0; } break; } } if ( cr1 > cr2 && cr1 <= num_atoms ) { /* success */ *canon_rank1 = cr1; *canon_rank2 = cr2; return 1; } return 0; } /******************************************************************************************/ int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, int vABParityUnknown) { /* sequence of (stereo_bond_parity, sb_parity_calc) pairs: (BEST_PARITY, BEST_PARITY) | (BEST_PARITY, WORSE_PARITY) | (WORSE_PARITY, WORSE_PARITY) (BEST_PARITY, 0) \___________________________________________/ | (WORSE_PARITY, 0) | (AB_PARITY_UNKN, 0) | (AB_PARITY_UNDF, 0) | Meaning: stereo_bond_parity is the parity we are looking for stereo_bond_parity==sb_parity_calc => parity to be calculated from canonical numbers stereo_bond_parity!=sb_parity_calc => parity is already known */ get_next_parity: switch ( *stereo_bond_parity ) { case BEST_PARITY: switch ( *sb_parity_calc ) { case 0: /* BEST_PARITY(known) : (BEST_PARITY, 0) -> */ *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ if ( !nNumWorse ) { goto get_next_parity; } break; case BEST_PARITY: /* BEST_PARITY(calc) : (BEST_PARITY, BEST_PARITY) -> */ *sb_parity_calc = WORSE_PARITY; /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY) */ if ( !nNumBest ) { goto get_next_parity; } break; case WORSE_PARITY: /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY)-> */ *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(calc): (WORSE_PARITY,WORSE_PARITY) */ if ( !nNumCalc ) { /* added 12-17-2003 */ goto get_next_parity; } break; } break; case WORSE_PARITY: switch ( *sb_parity_calc ) { case 0: /* WORSE_PARITY(known) : (WORSE_PARITY, 0) -> */ *stereo_bond_parity = vABParityUnknown /* AB_PARITY_UNKN */;/* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) */ if ( !nNumUnkn ) { goto get_next_parity; } break; case BEST_PARITY: /* error */ return CT_STEREOCOUNT_ERR; /* */ case WORSE_PARITY: /* WORSE_PARITY(calc) : (WORSE_PARITY,WORSE_PARITY)-> */ *sb_parity_calc = 0; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ if ( !nNumWorse ) { goto get_next_parity; } break; } break; case AB_PARITY_UNKN: /* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) -> */ if ( *sb_parity_calc ) /* error */ { return CT_STEREOCOUNT_ERR; /* */ } *stereo_bond_parity = AB_PARITY_UNDF; /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) */ if ( !nNumUndf ) { return 1; /*goto next_canon_ranks;*/ } break; case AB_PARITY_UNDF: /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) -> */ if ( *sb_parity_calc ) { /* error */ return CT_STEREOCOUNT_ERR; /* */ } return 1; /*goto next_canon_ranks;*/ /* next canon ranks */ } return 0; } /**************************************************************************************/ int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ) { if ( LinearCTStereoDble->at_num1 CT_GREATER_THAN at_rank_canon1 ) return 1; if ( LinearCTStereoDble->at_num1 != at_rank_canon1 ) return -1; if ( LinearCTStereoDble->at_num2 CT_GREATER_THAN at_rank_canon2 ) return 1; if ( LinearCTStereoDble->at_num2 != at_rank_canon2 ) return -1; if ( LinearCTStereoDble->parity CT_GREATER_THAN bond_parity ) return 1; if ( LinearCTStereoDble->parity != bond_parity ) return -1; return 0; } /**************************************************************************************/ /* Set for at[i]: */ /* 0 if atom has no parity */ /* STEREO_AT_MARK=8 if atom has stereo parity and has no stereo bonds */ /* num_stereo_bonds number of stereogenic bonds adjacent to the atom <= 3 */ void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ) { int i, k; memset( bAtomUsedForStereo, 0, sizeof( bAtomUsedForStereo[0] )*num_atoms ); for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].parity ) { for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[i].stereo_bond_neighbor[k]; k ++ ) ; bAtomUsedForStereo[i] = k? k : STEREO_AT_MARK; } } } /********************************************************************************* * tree structure: one segment * * canon. rank * at.no // orig. atom numbers on which the canon. * // rank has been successfully mapped * ... * at.no // except the last at.no: it is not known if * // it has been mapped until all atoms are mapped * num.at+1 // number of atoms in this segment plus one canon. rank * *********************************************************************************/ /******************************************************************************** typedef struct tagCurTree { AT_NUMB *tree; int max_len; // allocated length of tree in sizeof(tree[0]) units int cur_len; // currently used length int incr_len; // reallocation increment } CUR_TREE; *********************************************************************************/ /**************************************************************************************/ int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ) { if ( cur_tree ) { if ( cur_tree->tree && cur_tree->max_len > 0 && !(cur_tree->max_len % num_atoms) ) { /* do not reallocate */ cur_tree->cur_len = 0; cur_tree->incr_len = num_atoms; memset( cur_tree->tree, 0, cur_tree->max_len * sizeof(cur_tree->tree[0]) ); return 0; /* ok */ } inchi_free( cur_tree->tree ); memset( cur_tree, 0, sizeof(*cur_tree) ); if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( num_atoms, sizeof(cur_tree->tree[0]) ) ) { cur_tree->incr_len = cur_tree->max_len = num_atoms; return 0; /* ok */ } } return -1; /* error */ /* */ } /**************************************************************************************/ int CurTreeReAlloc( CUR_TREE *cur_tree ) { if ( cur_tree ) { if ( cur_tree->tree && cur_tree->max_len > 0 && cur_tree->incr_len > 0 ) { void *p = cur_tree->tree; if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( cur_tree->max_len + cur_tree->incr_len, sizeof(cur_tree->tree[0]) ) ) { memcpy( cur_tree->tree, p, cur_tree->cur_len * sizeof(cur_tree->tree[0]) ); inchi_free( p ); cur_tree->max_len += cur_tree->incr_len; return 0; /* ok */ } } } return -1; /* error */ /* */ } /**************************************************************************************/ void CurTreeFree( CUR_TREE *cur_tree ) { if ( cur_tree ) { inchi_free( cur_tree->tree ); memset( cur_tree, 0, sizeof(*cur_tree) ); } } /**************************************************************************************/ int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ) { if ( cur_tree ) { if ( cur_tree->cur_len + 2 > cur_tree->max_len ) { if ( CurTreeReAlloc( cur_tree ) ) { return -1; /* error */ /* */ } } cur_tree->tree[cur_tree->cur_len++] = rank; cur_tree->tree[cur_tree->cur_len++] = 1; return 0; } return -1; /* error */ /* */ } /**************************************************************************************/ int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) { if ( cur_tree && cur_tree->cur_len > 0 ) { int rank_pos; rank_pos = cur_tree->cur_len-1; rank_pos -= cur_tree->tree[rank_pos]; if ( rank_pos >= 0 ) { return (rank == cur_tree->tree[rank_pos]); } } return 0; /* not found */ } /**************************************************************************************/ int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ) { if ( cur_tree && cur_tree->tree && cur_tree->cur_len >= 2 ) { if ( 1 == cur_tree->tree[ cur_tree->cur_len - 1 ] ) { return CurTreeRemoveLastRank( cur_tree ); /* 0=> success, -1=>failed */ } return 1; /* cannot remove */ } return -1; /* error */ /* */ } /**************************************************************************************/ int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ) { if ( cur_tree ) { if ( cur_tree->cur_len + 1 > cur_tree->max_len ) { if ( CurTreeReAlloc( cur_tree ) ) { return -1; /* error */ /* */ } } if ( cur_tree->cur_len > 0 ) { AT_NUMB new_len = cur_tree->tree[ --cur_tree->cur_len ] + 1; cur_tree->tree[cur_tree->cur_len++] = (AT_NUMB)at_no; cur_tree->tree[cur_tree->cur_len++] = new_len; return 0; } } return -1; } /**************************************************************************************/ void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ) { /* on first entry: shift = 1; other values may occur in subsequent recursion */ /* cur_tree[cur_tree->cur_len - shift] is the length of a segment */ /* action: remove all atoms except the last from all segments that have length value positon to the right from tpos */ int cur_length_pos; if ( cur_tree && cur_tree->tree && (cur_length_pos = cur_tree->cur_len - shift) > tpos ) { if ( cur_tree->tree[ cur_length_pos ] > 2 ) { /* current segment contains more than 1 atom. Leave in the segment: rank, the last atom, length value */ /* subtract (old segment length)-(new segment length) from the tree length */ /* actual segment length including segment length value = (cur_tree->tree[cur_length_pos]+1) */ cur_tree->cur_len -= (int)cur_tree->tree[ cur_length_pos ] - 2; memmove( cur_tree->tree + cur_length_pos - cur_tree->tree[ cur_length_pos ] + 1, /* 1st atom pos */ cur_tree->tree + cur_length_pos - 1, /* last atom in the current segment position */ (shift+1)*sizeof(cur_tree->tree[0]) ); /* (current segment length) distance from the last tree element has not changed */ cur_tree->tree[ cur_tree->cur_len - shift] = 2; /* add 3 to move to the previous segment length position */ shift += 3; /* lenghth = 3 accounts for 3 currently present. segment items: (1) the last atom, (2) rank, (3) length value */ } else { shift += (int)cur_tree->tree[ cur_length_pos ] + 1; /* cur_tree->cur_len - (previous segment length position) */ } CurTreeKeepLastAtomsOnly( cur_tree, tpos, shift ); } } /**************************************************************************************/ int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ) { if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { AT_NUMB len = cur_tree->tree[ cur_tree->cur_len - 1 ]; if ( len >= 2 && (int)cur_tree->tree[ cur_tree->cur_len - 2 ] == at_no ) { cur_tree->tree[--cur_tree->cur_len-1] = len-1; return 0; } return 1; /* not found */ } return -1; /* error */ /* */ } /**************************************************************************************/ int CurTreeGetPos( CUR_TREE *cur_tree ) { if ( cur_tree ) { return cur_tree->cur_len; } return -1; } /**************************************************************************************/ int CurTreeSetPos( CUR_TREE *cur_tree, int len ) { if ( cur_tree ) { cur_tree->cur_len = len; return 0; } return -1; } /**************************************************************************************/ int CurTreeRemoveLastRank( CUR_TREE *cur_tree ) { if ( cur_tree && cur_tree->cur_len > 0 ) { cur_tree->cur_len -= cur_tree->tree[cur_tree->cur_len-1]+1; if ( cur_tree->cur_len >= 0 ) { return 0; } } return -1; } /**************************************************************************************/ /* Find if the atom is equivalent to already successfully tried current atoms */ int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ) { if ( cur_tree && cur_tree->tree && nSymmStereo && cur_tree->cur_len > 1 ) { AT_NUMB nEq = nSymmStereo[at_no]; int end = cur_tree->cur_len-1; int len = cur_tree->tree[end]-1; for ( ; len > 0; len -- ) { if ( nSymmStereo[(int)cur_tree->tree[end-len]] == nEq ) return 1; } return 0; } return -1; /* error */ /* */ } #ifdef NEVER /* not used */ /**************************************************************************************/ int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ) { if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { AT_NUMB len = cur_tree->tree[ --cur_tree->cur_len ]; if ( len >= 2 ) { cur_tree->tree[cur_tree->cur_len-1] = len-1; return 0; } } return -1; } /**************************************************************************************/ int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) { if ( !CurTreeRemoveLastRank( cur_tree ) ) { return CurTreeAddRank( cur_tree, rank ); } return -1; } /**************************************************************************************/ /* returns cur_tree->cur_len for the block containing the rank */ int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ) { int i, k; if ( cur_tree && cur_tree->tree && (i=cur_tree->cur_len) > 0 ) { while( 0 <= (k = i-(int)cur_tree->tree[i-1]-1) ) { if ( cur_tree->tree[k] == rank ) { return i; } i = k; } } return -1; /* error */ /* */ } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimap2.c000066400000000000000000004214261271037650300234220ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #include "ichicomp.h" #define MAP_MODE_STD 0 /* Standard approach: switch 2 neighbors */ #define MAP_MODE_C2v 1 /* Check for C2v reflection leading to parity inversion */ #define MAP_MODE_C2 2 /* Check for C2 rotation preserving parities */ #define MAP_MODE_S4 3 /* Check for S4 rotation/reflection leading to parity inversion */ /* important: MAP_MODE_STD < (MAP_MODE_C2v, MAP_MODE_C2) < MAP_MODE_S4 */ /* local prototypes */ void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ); int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, AT_RANK *neigh_num, int in1, int in2, int mode, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ); int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ); int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ); int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, int bParitiesInverted, int mode, CANON_STAT *pCS, int vABParityUnknown); int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, AT_RANK *nCanonRank, const AT_RANK *nSymmRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, int vABParityUnknown); int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, AT_RANK *nCanonRank, const AT_RANK *nSymmRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, int vABParityUnknown); int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); /************************************************************************************** * * Convert sorted equivalence information (nSymmRank) to ranks (nRank) * nSymmRank and nRank may point to the same array * */ int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ) { AT_RANK rNew, rOld, nNumDiffRanks; int i, j, nNumChanges = 0; for ( i = num_atoms-1, j = (int)nAtomNumber[i], rOld = nSymmRank[j], rNew = nRank[j] = (AT_RANK)num_atoms, nNumDiffRanks = 1; i > 0; i -- ) { j = (int)nAtomNumber[i-1]; if ( nSymmRank[j] != rOld ) { nNumDiffRanks ++; rNew = (AT_RANK)i; nNumChanges += (rOld != rNew+1); rOld = nSymmRank[j]; } nRank[j] = rNew; } if ( bChanged ) { *bChanged = (0 != nNumChanges); } return nNumDiffRanks; } /************************************************************************************** * * Convert sorted ranks (nRank) to sorted equivalence information (nSymmRank) * nSymmRank and nRank may point to the same array * */ int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ) { AT_RANK rNew, rOld, nNumDiffRanks; int i, j; for ( i = 1, j = (int)nAtomNumber[0], rOld = nRank[j], rNew = nSymmRank[j] = 1, nNumDiffRanks = 1; i < num_atoms; i ++ ) { j = (int)nAtomNumber[i]; if ( nRank[j] != rOld ) { nNumDiffRanks ++; rNew = (AT_RANK)(i+1); rOld = nRank[j]; } nSymmRank[j] = rNew; } return nNumDiffRanks; } /**************************************************************************************/ void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ) { AT_RANK *tmp = *p1; *p1 = *p2; *p2 = tmp; } /**************************************************************************************/ /* Set ranks from the products vector and previous ranks */ /* nRank[] and nNewRank[] should refer to different arrays for now */ /**************************************************************************************/ int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber ) { int i, j, nNumDiffRanks, nNumNewRanks; AT_RANK r1, r2; /* -- nAtomNumber[] is already properly set -- for ( i = 0; i < num_atoms; i++ ) { nAtomNumber[i] = (AT_RANK)i; } */ /* set globals for qsort */ pNeighList_RankForSort = NeighList; pn_RankForSort = nRank; nNumDiffRanks = 0; nNumNewRanks = 0; memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); /* sorting */ for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { nNewRank[j] = r2; nNumDiffRanks ++; i ++; continue; } r1 = r2; insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighLists ); /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighLists );*/ j = r2-1; nNewRank[(int)nAtomNumber[j]] = r2; nNumDiffRanks ++; while( j > i ) { if ( CompareNeighListLex( NeighList[(int)nAtomNumber[j-1]], NeighList[(int)nAtomNumber[j]], nRank ) ) { r2 = j; nNumDiffRanks ++; nNumNewRanks ++; } j --; nNewRank[(int)nAtomNumber[j]] = r2; } i = r1; } return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; } /**************************************************************************************/ /* Set ranks from the products vector and previous ranks */ /* When comparing neigh lists ignore ranks > max_at_no */ /* nRank[] and nNewRank[] should refer to different arrays for now */ /**************************************************************************************/ int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ) { int i, j, nNumDiffRanks, nNumNewRanks; AT_RANK r1, r2; /* -- nAtomNumber[] is already properly set -- for ( i = 0; i < num_atoms; i++ ) { nAtomNumber[i] = (AT_RANK)i; } */ /* set globals for CompNeighListsUpToMaxRank */ pNeighList_RankForSort = NeighList; pn_RankForSort = nRank; nNumDiffRanks = 0; nNumNewRanks = 0; nMaxAtNeighRankForSort = nMaxAtRank; memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); /* sorting */ for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { /* non-tied rank: singleton */ nNewRank[j] = r2; nNumDiffRanks ++; i ++; continue; } /* tied rank r2 r2-i atoms have rank r2 next atom after them is in position r2 */ r1 = r2; insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighListsUpToMaxRank ); /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighListsUpToMaxRank );*/ j = r2-1; /* prepare cycle backward, from j to i step -1 */ nNewRank[(int)nAtomNumber[j]] = r2; nNumDiffRanks ++; while( j > i ) { if ( CompareNeighListLexUpToMaxRank( NeighList[nAtomNumber[j-1]], NeighList[nAtomNumber[j]], nRank, nMaxAtRank ) ) { r2 = j; nNumDiffRanks ++; nNumNewRanks ++; } j --; nNewRank[(int)nAtomNumber[j]] = r2; } i = r1; } return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; } /**************************************************************************************/ /* Set ranks from the products vector and previous ranks */ /* nRank[] and nNewRank[] should refer to different arrays for now */ /**************************************************************************************/ int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ) { int i, nNumDiffRanks; AT_RANK nCurrentRank; /* -- nAtomNumber[] is already properly set -- for ( i = 0; i < num_atoms; i++ ) { nAtomNumber[i] = (AT_RANK)i; } */ /* set globals for qsort */ pNeighList_RankForSort = NeighList; pn_RankForSort = nRank; /* sorting */ if ( bUseAltSort & 1 ) tsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); else qsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); for ( i=num_atoms-1, nCurrentRank=nNewRank[(int)nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; 0 < i ; i -- ) { /* Note: CompNeighListRanks() in following line implicitly reads nRank pointed by pn_RankForSort */ if ( CompNeighListRanks( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { nNumDiffRanks ++; nCurrentRank = (AT_RANK)i; } nNewRank[(int)nAtomNumber[i - 1]] = nCurrentRank; } return nNumDiffRanks; } /**************************************************************************************/ /* Sort NeighList[] lists of neighbors according to the ranks of the neighbors */ /**************************************************************************************/ void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) { int i; for ( i = 0; i < num_atoms; i ++ ) { insertions_sort_NeighListBySymmAndCanonRank( NeighList[i], nSymmRank, nCanonRank ); } } /**************************************************************************************/ int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) { int k, i; AT_RANK nPrevRank = 0; /* * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. * */ for ( k = 0; k < num_atoms; k ++ ) { i = nAtomNumber[k]; if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { /* nRank[i] is tied (duplicated) */ insertions_sort_NeighList_AT_NUMBERS( NeighList[i], nRank ); } nPrevRank = nRank[i]; } return 0; } /**************************************************************************************/ int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) { int k, i; AT_RANK nPrevRank = 0; /* * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. * */ for ( k = 0; k < num_atoms; k ++ ) { i = nAtomNumber[k]; if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { /* nRank[i] is tied (duplicated) */ insertions_sort_NeighList_AT_NUMBERS3( NeighList[i], nRank ); } nPrevRank = nRank[i]; } return 0; } /************************************************************************************** * * Differentiate2 * * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length * for example, nAtomNumber[i] = i; * Note2: this version does not calculate neighbor lists for non-tied ranks */ int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) { /*int nNumPrevRanks;*/ /* SortNeighLists2 needs sorted ranks */ pn_RankForSort = pnCurrRank; if ( bUseAltSort & 1 ) tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank /* CompRanksOrd*/ ); else qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRanksOrd ); do { *lNumIter += 1; /*nNumPrevRanks = nNumCurrRanks;*/ switch_ptrs( &pnCurrRank, &pnPrevRank ); SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); /* the following call creates pnCurrRank out of pnPrevRank */ nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, 1, CompNeighListRanksOrd ); } while ( /*nNumPrevRanks != nNumCurrRanks ||*/ memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); return nNumCurrRanks; } /************************************************************************************** * * Differentiate3 * * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length * for example, nAtomNumber[i] = i; * Note2: this version does not calculate neighbor lists for non-tied ranks */ int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter ) { /* static long count = 0; count ++; if ( count == 103 ) { int stop=1; } */ /* SortNeighLists3 needs sorted ranks: ranks/atnumbers must have been already sorted */ do { *lNumIter += 1; switch_ptrs( &pnCurrRank, &pnPrevRank ); SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); /* the following call creates pnCurrRank out of pnPrevRank */ nNumCurrRanks = SetNewRanksFromNeighLists3( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber); } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); return nNumCurrRanks; } /************************************************************************************** * * Differentiate4: ignore neighbors with rank > num_atoms * * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length * for example, nAtomNumber[i] = i; * Note2: this version does not sort neighbor lists for non-tied ranks */ int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ) { /* static long count = 0; count ++; if ( count == 103 ) { int stop=1; } */ /* SortNeighLists4 needs sorted ranks: ranks/atnumbers must have been already sorted */ do { *lNumIter += 1; switch_ptrs( &pnCurrRank, &pnPrevRank ); SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); /* the following call creates pnCurrRank out of pnPrevRank */ nNumCurrRanks = SetNewRanksFromNeighLists4( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, nMaxAtRank ); } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); return nNumCurrRanks; } /************************************************************************************** * * DifferentiateBasic (sort according to ranks only) * * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length * for example, nAtomNumber[i] = i; * Note2: this version does not calculate neighbor lists for non-tied ranks */ int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) { int nNumPrevRanks; /* SortNeighLists2 needs sorted ranks */ pn_RankForSort = pnCurrRank; if ( bUseAltSort & 1 ) tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); else qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); do { *lNumIter += 1; nNumPrevRanks = nNumCurrRanks; switch_ptrs( &pnCurrRank, &pnPrevRank ); SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); /* the following call creates pnCurrRank out of pnPrevRank */ nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, bUseAltSort, CompNeighListRanks ); } while ( nNumPrevRanks != nNumCurrRanks || memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); return nNumCurrRanks; } /************************************************************************************** * For the purpose of mapping an atom to an atom: * (a) find number of tied ranks * (b) if number of tied ranks > 1 then: * 1) find the rank for breaking a tie * 2) allocate memory for breaking the tie if it has not been allocated * 3) find out if atom 1 ("from") has already been mapped * Return value: * < 0: error * = 1: has already been mapped, to tie to break * > 1: we need to break a tie */ int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ) { AT_RANK *nRank1 = *pRankStack1++; AT_RANK *nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ AT_RANK *nRank2 = *pRankStack2++; AT_RANK *nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ AT_RANK r, *pTempArray; int iMax, i, i1, i2; *bAddStack = 0; *bMapped1 = 0; *nNewRank = 0; r = nRank1[at_no1]; if ( r != nRank2[at_no2] ) return CT_MAPCOUNT_ERR; /* atoms cannot be mapped onto each other: they have different ranks */ /* */ iMax = r - 1; /* find i1 and i2 = numbers of ranks in nRank1[] and nRank2[] equal to r: */ for ( i1 = 1; i1 <= iMax && r == nRank1[nAtomNumber1[iMax-i1]]; i1 ++ ) ; for ( i2 = 1; i2 <= iMax && r == nRank2[nAtomNumber2[iMax-i2]]; i2 ++ ) ; if ( i2 != i1 ) return CT_MAPCOUNT_ERR; /* program error: must be identical number of equal ranks */ /* */ /* found i1 equal rank(s); preceding (smaller) non-equal rank is r-i1 */ /* To break the tie we have to reduce the rank r to r-i1+1 */ /************ Note ******************************* * IF ( i=r-1 && 0 <= i && i < num_atoms AND * nRank[nAtomNumber1[i]] == r ) * THEN: * nRank[nAtomNumber1[i+1]] > r; (if i+1 < num_atoms) * nRank[nAtomNumber1[i-1]] <= r; (if i > 0) * * IF r = nRank[i] THEN * nRank[nAtomNumber1[r-1]] == r * nRank[nAtomNumber1[r-i-1]] <= nRank[nAtomNumber1[r-i]] (for 1 <= i < r ) */ if ( i1 > 1 ) { /* int bAtFromHasAlreadyBeenMapped = 0; */ *nNewRank = r - i1 + 1; /* grab an existing or allocate a new array */ /* we need 4 arrays: 2 for ranks + 2 for numbers */ for ( i = 0; i < 4; i ++ ) { if ( i < 2 ) { pTempArray = *pRankStack1; *bMapped1 += (pTempArray && pTempArray[0]); } else { pTempArray = *pRankStack2; } if ( !pTempArray && !(pTempArray = (AT_RANK *) inchi_malloc(length))) return CT_OUT_OF_RAM; /* out of RAM */ /* */ /* copy "to" contents */ switch( i ) { case 2: memcpy( pTempArray, nRank2, length ); break; case 3: memcpy( pTempArray, nAtomNumber2, length ); break; } if ( i < 2 ) *pRankStack1 ++ = pTempArray; else { *pRankStack2 ++ = pTempArray; } } *bAddStack = 2; /* to break the tie we added 2 more arrays to pRankStack1 and pRankStack2 */ } return i1; } /************************************************************************************** * * * * Stereo Mappings * * * **************************************************************************************/ /************************************************************************************** * Parity for a half of a stereo bond. If both halfs have the same parity * then the bond is "trans" (E,-,1), otherwise it is "cis" (Z,+,2). * The advantage of this approach is: The bond parity does not depend on the * rank of the atom located on the opposite end of the stereogenic bond. * As the result all bond parities of, for example, benzene, can be calculated * from equivalence ranks only, without any mappings. * * Input: at_no1 = number of atom for which the half-bond parity is calculated * i_sb_neigh = ordering number of the stereo bond in at->stereo_bond_neighbor[] * * Returns: 0=> no parity can be found; 1=> odd parity; 2=> even parity * */ int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ) { /* Suppose neighbors #0,#1,#2 have ranks a, b, c. Remove rank of the neighbor connected by the stereogenic bond (NCSB) from the a, b, c list and denote the two left as r[0], r[1], in the same order. Let iNCSB be an ordering number (0,1,or 2) of the NCSB. Assume the neighbor connected by the stereogenic bond has infinite positive rank. Position the half-bond so that the stereogenic bond neighbor is to the right from the atom (see below) Definition. =========== if rank(X) != rank(Y) then Half-bond parity = (rank(X) > rank(Y)), that is, Y \ if ( rank(X) < rank(Y) ) then Half-bond parity is Even C==NCSB if ( rank(X) > rank(Y) ) then Half-bond parity is Odd / if ( rank(X) = rank(Y) ) then Half-bond parity cannot be defined X 1 2 1 \ \ \ C==NCSB C==NCSB C==NCSB C==NCSB / / / 2 1 1 Parity = 1 Parity = 1 Parity = 2 Parity = 2 (Odd) (Odd) (Even) or 0 (Even) or 0 Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 Consider the following cases to prove the formula: Case 1: 3 explicit neighbors ============================ If (1) atom's geometric parity = even (which means neighbors #0, #1, #2 are located clockwise), and (2) neighbors other than NCSB have different ranks, then, assuming that NCSB always has the largest (infinite) rank (this is consistent with the assumption that implicit hydrogens have smallest ranks), we have 3 possibilities: c a b \ \ \ C==a C==b C==c / / / b c a iNCSB = 0 1 2 Half-bond parity = b>c ab (0=even, 1=odd) r[0]>r[1] r[0]r[1] Half-bond parity for all 3 cases = (iNCSB + (r[0] > r[1]))%2 The following slight modification will work for both odd and even geometric parity: Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 even parity (0) => atom above the bond has lower rank than the atom below the bond. Case 2: 2 explicit neighbors ============================ One implicit hydrogen atom H or hydrogen isotope (implicit rank=0). Assume r[1]=0 H a Note. The same method \ \ works for C==a C==b / / N==a and a b H / \ b N==b iNCSB = 0 1 Half-bond parity = b>0 a<0 (r[1]=0, r[0]>0) r[0]>r[1] r[0] r[1]) + (Atom C geometric parity))%2 Case 3: 1 explicit neighbor (NCSB) ================================== Two implicit hydrogens, (number of neighbors on non-streogenic bonds)==0: Atom C geometric parity: Even Odd Note. The same method works for D H \ \ Even and Odd C==a C==a / / H N==a H D \ / N==a H iNCSB = 0 0 Half-bond parity = (0<0)=0 (0<0)+1 = 1 (r[1]=0, r[0]=0) r[1] r[1]) + (Atom C geometric parity))%2 */ int i, j, k, iNeigh, parity, at1_parity, at_no2; AT_RANK r[MAX_NUM_STEREO_BOND_NEIGH]; if ( at[at_no1].valence > MAX_NUM_STEREO_BOND_NEIGH || ( at1_parity = at[at_no1].parity ) <= 0 ) { return 0; } if ( !PARITY_WELL_DEF( at1_parity ) ) { if ( PARITY_KNOWN( at1_parity ) ) { return at1_parity; } return -at1_parity; } if ( 0 > i_sb_neigh || i_sb_neigh >= MAX_NUM_STEREO_BOND_NEIGH ) { return CT_STEREOBOND_ERROR; /* */ } for ( i = 0; i <= i_sb_neigh; i ++ ) { if ( !at[at_no1].stereo_bond_neighbor[i] ) { return CT_STEREOBOND_ERROR; /* */ } } at_no2 = at[at_no1].neighbor[(int)at[at_no1].stereo_bond_ord[i_sb_neigh]]; memset( r, 0, sizeof( r ) ); for ( i = j = 0, iNeigh = -1; i < at[at_no1].valence; i ++ ) { if ( (k = (int)at[at_no1].neighbor[i]) == at_no2 ) { iNeigh = i; } else { r[j++] = nRank[k]; } } if ( iNeigh < 0 || iNeigh != at[at_no1].stereo_bond_ord[i_sb_neigh] ) { return CT_STEREOBOND_ERROR; /* */ } if ( j > 0 && !r[0] || j > 1 && !r[1] ) return 0; /* undefined ranks */ if ( j == 2 && r[0] == r[1] || iNeigh < 0 ) { parity = AB_PARITY_CALC; /* cannot calculate bond parity without additional breaking ties. */ } else { parity = 2 - (at[at_no1].parity + iNeigh + (r[1] < r[0])) % 2; } return parity; } /**************************************************************************************/ int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, sp_ATOM *at, EQ_NEIGH *pEN, const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ) { int i, j, k, num_neigh; int to_sb_neigh_ord, from_sb_neigh_ord, parity; AT_RANK r_to[MAX_NUM_STEREO_BOND_NEIGH], at_no_to[MAX_NUM_STEREO_BOND_NEIGH]; AT_RANK r_canon_from[MAX_NUM_STEREO_BOND_NEIGH], at_no_from[MAX_NUM_STEREO_BOND_NEIGH]; AT_RANK r, r_sb_neigh; for ( i = 0; i < MAX_NUM_STEREO_BOND_NEIGH; i ++ ) { r_to[i] = r_canon_from[i] = 0; } if ( pEN ) { memset( pEN, 0, sizeof(*pEN)); } /* for debug only */ if ( nRankFrom[from_at] != nRankTo[to_at] || nRankFrom[from_neigh] != nRankTo[to_neigh] || at[to_at].valence != at[from_at].valence ) { return 0; /* program error: both atoms must be mapped */ /* */ } parity = PARITY_VAL(at[to_at].parity); num_neigh = at[to_at].valence; if ( num_neigh > MAX_NUM_STEREO_BOND_NEIGH || num_neigh < MIN_NUM_STEREO_BOND_NEIGH ) { /* 2 neighbors are possible in case of stereo bond with implicit H */ /* or a stereocenter -CHD- with an implicit H */ if ( num_neigh == 1 && at[to_at].stereo_bond_neighbor[0] ) { /* 1 neighbor can happen in case of a terminal =CHD */ if ( PARITY_WELL_DEF(parity) ) return 2 - parity % 2; else if ( parity ) return parity; else return AB_PARITY_UNDF; /* undefined parity */ } return 0; /* program error */ /* */ } if ( ATOM_PARITY_KNOWN(parity) ) { if ( !ATOM_PARITY_WELL_DEF(parity) ) return parity; } else if ( parity ) { return 0; /* parity; */ } else { return 0; /* AB_PARITY_UNDF; */ /* possibly program error: undefined parity */ } /* locate at[to_at].stereo_bond_neighbor[] ordering numbers */ for ( i = 0, to_sb_neigh_ord=-1; i < MAX_NUM_STEREO_BONDS && (k=(int)at[to_at].stereo_bond_neighbor[i]); i ++ ) { if ( k == to_neigh+1 ) { to_sb_neigh_ord = i; break; } } if ( to_sb_neigh_ord < 0 ) { return 0; /* program error: not a stereo bond */ /* */ } to_sb_neigh_ord = (int)at[to_at].stereo_bond_ord[to_sb_neigh_ord]; r_sb_neigh = nRankTo[(int)at[to_at].neighbor[to_sb_neigh_ord]]; for ( i = j = 0; i < num_neigh; i ++ ) { if ( i != to_sb_neigh_ord ) { r_to[j] = nRankTo[(int)(at_no_to[j]=at[to_at].neighbor[i])]; if ( r_sb_neigh == r_to[j] ) { return 0; /* stereo bond atoms are not fully mapped */ } j ++; } } if ( j+1 != num_neigh ) { return 0; /* program error */ /* */ } if ( j == 1 ) { /* only one neighbor; no mapping needed */ return 2-(parity+1+to_sb_neigh_ord)%2; } if ( j != 2 ) { return 0; /* program error: j can be only 0, 1, or 2 */ /* */ } if ( r_to[0] == r_to[1] ) { /* double bond neighbors need to be mapped */ j = 0; from_sb_neigh_ord = -1; for ( i = 0; i < num_neigh; i ++ ) { k = at[from_at].neighbor[i]; r = nRankFrom[k]; if ( r == r_sb_neigh ) { from_sb_neigh_ord = i; /* we need this value only for error-checking */ } else if ( r == r_to[0] ) { r_canon_from[j] = nCanonRankFrom[k]; at_no_from[j] = (AT_RANK)k; j ++; } else { return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ } } if ( from_sb_neigh_ord < 0 || j != 2 ) { return 0; /* program error: rank of a neighbor not found */ /* */ } if ( pEN ) { /* j == 2 */ pEN->to_at[0] = at_no_to[0]; pEN->to_at[1] = at_no_to[1]; pEN->num_to = 2; /* number of stored in pEN->to_at[] central atom neighbors */ pEN->rank = r_to[0]; /* mapping rank of the tied neighbors */ /* i := index of the smaller out of r_canon_from[1] and r_canon_from[0] */ i = (r_canon_from[1] < r_canon_from[0]); pEN->from_at = at_no_from[i]; pEN->canon_rank = r_canon_from[i]; } return -((int)r_to[0]); } /* double bond neighbors a mapped: r_to[0] != r_to[1] */ from_sb_neigh_ord = -1; for ( i = 0; i < num_neigh; i ++ ) { k = at[from_at].neighbor[i]; r = nRankFrom[k]; if ( r == r_sb_neigh ) { from_sb_neigh_ord = i; /* we need this value only for error-checking */ } else if ( r == r_to[0] ) { r_canon_from[0] = nCanonRankFrom[k]; /* at_no_from[0] = (AT_RANK)k; */ } else if ( r == r_to[1] ) { r_canon_from[1] = nCanonRankFrom[k]; /* at_no_from[1] = (AT_RANK)k; */ } else { return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ } } if ( !r_canon_from[0] || !r_canon_from[1] || from_sb_neigh_ord < 0 ) { return 0; /* program error: neighbor rank not found */ /* */ } return 2 - (parity + to_sb_neigh_ord + (r_canon_from[1] */ if ( num_neigh > MAX_NUM_STEREO_ATOM_NEIGH || num_neigh < 2 ) { /* 2 neighbors are possible in case of stereo bond with implicit H */ /* or a stereocenter >CHD with two implicit H */ if ( num_neigh == 1 ) { /* 1 neighbor can happen in case of a terminal -CHDT or =CHD */ if ( at[to_at].parity ) return at[to_at].parity; else return AB_PARITY_UNDF; /* undefined parity */ } return 0; /* program error */ /* */ } for ( i = 0; i < num_neigh; i ++ ) { /* initialization of locals */ nNeighNumberTo[i] = nNeighNumberFrom[i] = i; nNeighRankTo[i] = nRankTo[(int)at[to_at].neighbor[i]]; /* mapping rank */ nNeighRankFrom[i] = nRankFrom[j=(int)at[from_at].neighbor[i]]; /* mapping rank */ nNeighRankFromCanon[i] = nCanonRankFrom[j]; /* canonical number */ } pn_RankForSort = nNeighRankFrom; nNumCompNeighborsRanksCountEql = 0; /* sort mapping ranks-from */ num_trans_from = insertions_sort( nNeighNumberFrom, num_neigh, sizeof(nNeighNumberFrom[0]), CompNeighborsRanksCountEql ); if ( nNumCompNeighborsRanksCountEql ) { /* At least 2 neighbors have equal mapping ranks (are tied). */ /* Find tied from-neighbors with minimal canonical rank (nCanonRankFrom[]) */ r_canon_from_min = MAX_ATOMS+1; /* max possible rank + 1 */ for ( i = 1, r = 0, r1 = nNeighRankFrom[neigh1=nNeighNumberFrom[0]]; i < num_neigh; i ++, r1 = r2, neigh1 = neigh2 ) { r2 = nNeighRankFrom[neigh2=nNeighNumberFrom[i]]; if ( r2 == r1 ) { /* found neighbors with tied ranks */ if ( r != r2 ) { /* the 1st pair of neighbor with this rank */ r = r2; if ( (r_canon_from=nNeighRankFromCanon[neigh1]) < r_canon_from_min ) { r_canon_from_min = r_canon_from; /* min canon rank */ neigh_canon_from_min = neigh1; /* neighbor number */ } } if ( (r_canon_from=nNeighRankFromCanon[neigh2]) < r_canon_from_min ) { r_canon_from_min = r_canon_from; neigh_canon_from_min = neigh2; } } } if ( r ) { /* neighbors with tied ranks have been found => parity cannot be determined without additional mapping */ /* find to-neighbors on which neigh_canon_from_min can be mapped */ r1 = nNeighRankFrom[neigh_canon_from_min]; if ( pEN ) { for ( i = j = 0; i < num_neigh; i ++ ) { if ( r1 == nNeighRankTo[i] ) { pEN->to_at[j++] = at[to_at].neighbor[i]; } } insertions_sort( pEN->to_at, j, sizeof(pEN->to_at[0]), CompRanksInvOrd ); pEN->num_to = j; /* number of stored in pEN->to_at[] central atom neighbors */ pEN->from_at = at[from_at].neighbor[neigh_canon_from_min]; /* neighbor with min. canon number */ pEN->rank = r1; /* mapping rank of the tied neighbors */ pEN->canon_rank = r_canon_from_min; /* canon. rank of the pEN->from_at */ } else { /* debug only */ for ( i = j = 0; i < num_neigh; i ++ ) { if ( r1 == nNeighRankTo[i] ) { j++; } } } /* debug only */ if ( j <= 1 || !r1 || r_canon_from_min > MAX_ATOMS ) { return 0; /* program error */ /* */ } return -r; /* means parity cannot be determined */ } return 0; /* program error */ } /* All neighbors have different mapping ranks; */ /* therefore no additional mapping of the neighbors is necessary */ if ( !ATOM_PARITY_WELL_DEF(at[to_at].parity) ) return at[to_at].parity; /* unknown parity or cannot be determined */ pn_RankForSort = nNeighRankTo; num_trans_to = insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); /* Map canonical ranks of neighbors. Mapped on each other "to" and "from" atoms have equal mapping ranks */ for ( i = 0; i < num_neigh; i ++ ) { if ( nNeighRankTo[j=nNeighNumberTo[i]] != nNeighRankFrom[k=nNeighNumberFrom[i]] ) return 0; /* program error: mapping ranks not equal, from_at neigborhood cannot be mapped on to_at neighbood. */ /* */ nNeighRankToCanon[j] = nNeighRankFromCanon[k]; /* potential problem: other atom(s) may have same mapping rank and */ /* different canon. rank(s). */ /* we may save some memory by eliminating nNeighRankFromCanon[]: */ /* nNeighRankToCanon[j] = nCanonRankFrom[at[from_at].neighbor[k]] */ } pn_RankForSort = nNeighRankToCanon; num_trans_to += insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); #ifndef CT_NEIGH_INCREASE num_trans_to += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order of canon. numbers */ #endif return 2 - (num_trans_to + at[to_at].parity)%2; } /************************************************************************************** * * Phase II: map canonicaly numbrered structure onto itself * to obtain a minimal or maximal stereo part of the CT * **************************************************************************************/ int ClearPreviousMappings( AT_RANK **pRankStack1 ) { int i; for ( i = 0; pRankStack1[i]; i ++ ) { pRankStack1[i][0] = 0; } return i; } /**************************************************************************************/ /* map one atom ("from") onto another ("to"): untie their mapping ranks if they are tied. */ int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, AT_RANK *nTempRank, int nNumMappedRanks, int *pnNewNumMappedRanks, CANON_STAT *pCS, NEIGH_LIST *NeighList, AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ) { AT_RANK *nRank1, *nAtomNumber1; /* ranks for mapping "1", "from" */ AT_RANK *nRank2, *nAtomNumber2; /* ranks for mapping "2", "to" */ AT_RANK *nNewRank1=NULL, *nNewAtomNumber1=NULL; /* ranks for mapping "1", "from" */ AT_RANK *nNewRank2=NULL, *nNewAtomNumber2=NULL; /* ranks for mapping "2", "to" */ int length = num_max*sizeof(AT_RANK); int nNewNumRanks2, nNewNumRanks1; int i, bAtFromHasAlreadyBeenMapped, nNumTies; AT_RANK nNewRank; nNumTies = NumberOfTies( pRankStack1, pRankStack2, length, at_no1, at_no2, &nNewRank, bAddStack, &bAtFromHasAlreadyBeenMapped ); if ( RETURNED_ERROR(nNumTies) ) return nNumTies; /* error */ nRank1 = *pRankStack1++; nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ nRank2 = *pRankStack2++; nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ if ( nNumTies > 1 ) { nNewRank1 = *pRankStack1++; nNewAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ nNewRank2 = *pRankStack2++; nNewAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ /* break a tie for "to" */ memcpy( nNewRank2, nRank2, length ); memcpy( nNewAtomNumber2, nAtomNumber2, length ); nNewRank2[at_no2] = nNewRank; nNewNumRanks2 = DifferentiateRanks2( num_atoms, NeighList, nNumMappedRanks, nNewRank2, nTempRank, nNewAtomNumber2, &pCS->lNumNeighListIter, 1 ); pCS->lNumBreakTies ++; /* Check whether the old mapping can be reused */ if ( 2 == bAtFromHasAlreadyBeenMapped && nNewRank == nNewRank1[at_no1] ) { for ( i = 0; i < num_atoms; i ++ ) { if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { bAtFromHasAlreadyBeenMapped = 0; /* It cannot. */ break; } } } else { bAtFromHasAlreadyBeenMapped = 0; } if ( 2 != bAtFromHasAlreadyBeenMapped ) { /* break a tie for "from" */ for ( i = 0; pRankStack1[i]; i ++ ) { pRankStack1[i][0] = 0; } memcpy( nNewRank1, nRank1, length ); memcpy( nNewAtomNumber1, nAtomNumber1, length ); /* GPF: bad nAtomNumber1 */ nNewRank1[at_no1] = nNewRank; nNewNumRanks1 = DifferentiateRanks2( num_atoms, NeighList, nNumMappedRanks, nNewRank1, nTempRank, nNewAtomNumber1, &pCS->lNumNeighListIter, 1 ); pCS->lNumBreakTies ++; } else { nNewNumRanks1 = nNewNumRanks2; } if ( nNewNumRanks1 != nNewNumRanks2 ) return CT_MAPCOUNT_ERR; /* program error */ /* */ *pnNewNumMappedRanks = nNewNumRanks2; /* debug only */ for ( i = 0; i < num_atoms; i ++ ) { if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { return CT_MAPCOUNT_ERR; /* program error */ /* */ } } } else { *pnNewNumMappedRanks = nNumMappedRanks; } return ( nNewRank1 )? nNewRank1[at_no1] : nRank1[at_no1]; /* mapping rank value */ } /**************************************************************************************/ int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ) { int i, j, neighbor_no; for ( i = 0; i < num_atoms; i ++ ) { if ( nRank2[i] != nRank1[i] ) { if ( i != at_no /*&& ATOM_PARITY_WELL_DEF(at[i].parity)*/ && at[i].bHasStereoOrEquToStereo && !(at[i].stereo_atom_parity & KNOWN_PARITIES_EQL ) && !at[i].stereo_bond_neighbor[0] ) { return 1; /* may have changed stereo atoms order */ } for ( j = 0; j < at[i].valence; j ++ ) { neighbor_no = at[i].neighbor[j]; if ( neighbor_no != at_no /*&& ATOM_PARITY_WELL_DEF(at[neighbor_no].parity)*/ && at[neighbor_no].bHasStereoOrEquToStereo && !(at[neighbor_no].stereo_atom_parity & KNOWN_PARITIES_EQL ) && !at[neighbor_no].stereo_bond_neighbor[0] ) return 1; /* may have changed stereo atom parity */ } } } return 0; } /**************************************************************************************/ #if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ /**************************************************************************************/ void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) { if ( *nAtomNumberCanon1 ) { inchi_free( *nAtomNumberCanon1 ); *nAtomNumberCanon1 = NULL; } if ( *nAtomNumberCanon2 ) { inchi_free( *nAtomNumberCanon2 ); *nAtomNumberCanon2 = NULL; } if ( *nl ) { FreeNeighList( *nl ); *nl = 0; } if ( *nl1 ) { FreeNeighList( *nl1 ); *nl1 = 0; } if ( *nl2 ) { FreeNeighList( *nl2 ); *nl2 = 0; } if ( *nVisited1 ) { inchi_free( *nVisited1 ); *nVisited1 = NULL; } if ( *nVisited2 ) { inchi_free( *nVisited2 ); *nVisited2 = NULL; } } /**************************************************************************************/ int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) { DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); *nAtomNumberCanon1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon1) ); *nAtomNumberCanon2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon2) ); *nl = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); *nl1 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); *nl2 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); *nVisited1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited1) ); *nVisited2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited2) ); if ( !*nl || !*nl1 || !*nl2 || !*nVisited1 || !*nVisited2 || !*nAtomNumberCanon1 || !*nAtomNumberCanon2 ) { DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); return 0; } /* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ SortNeighListsBySymmAndCanonRank( num_atoms, *nl, nSymmRank, nCanonRank ); SortNeighListsBySymmAndCanonRank( num_atoms, *nl1, nSymmRank, nCanonRank ); SortNeighListsBySymmAndCanonRank( num_atoms, *nl2, nSymmRank, nCanonRank ); return 1; } /**************************************************************************************/ AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ) { int i; AT_RANK nRank2; for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = nAtomRank[(int)nAtomNumb[i]]); i -- ) ; if ( i >= 0 ) nRank2 ++; else nRank2 = 1; return nRank2; } /**************************************************************************************/ int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, AT_RANK *neigh_num, int in1, int in2, int mode, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ) { AT_RANK nRank1, nRank2; int nNumDiffRanks, nNumDiffRanks1, nNumDiffRanks2, i; int n1 = (int)neigh_num[in1]; int n2 = (int)neigh_num[in2]; int other_neigh[2], other_neig_ord[2], num_other_neigh; /* asymmetric calculation */ if ( mode == MAP_MODE_S4 && in1 || /* for S4 we need only (in1,in2) = (0,1) (0,2) (0,3) pairs of neighbors */ mode != MAP_MODE_STD && at[ia].valence != MAX_NUM_STEREO_ATOM_NEIGH || mode != MAP_MODE_STD && nSymmRank[n1] != nSymmRank[n2] ) { return 0; } /* 1. Create initial ranks from equivalence information stored in nSymmRank */ memcpy( pRankStack1[0], nSymmRank, num_at_tg * sizeof(pRankStack1[0][0]) ); pn_RankForSort = pRankStack1[0]; tsort( pRankStack1[1], num_at_tg, sizeof(pRankStack1[1][0]), CompRanksOrd ); nNumDiffRanks = SortedEquInfoToRanks( pRankStack1[0]/*inp*/, pRankStack1[0]/*out*/, pRankStack1[1], num_at_tg, NULL ); /* other neighbors */ num_other_neigh = 0; if ( at[ia].valence <= MAX_NUM_STEREO_ATOM_NEIGH && mode ) { for ( i = 0; i < at[ia].valence; i ++ ) { if ( i != in1 && i != in2 ) { other_neigh[num_other_neigh] = (int)neigh_num[i]; other_neig_ord[num_other_neigh] = i; num_other_neigh ++; } } } if ( mode != MAP_MODE_STD && nSymmRank[other_neigh[0]] != nSymmRank[other_neigh[1]] || mode == MAP_MODE_S4 && nSymmRank[n1] != nSymmRank[other_neigh[1]] ) { return 0; } /* 2. Fix at[ia] */ if ( pRankStack1[0][ia] != nSymmRank[ia] ) { /* at[ia] is constitutionally equivalent to some other atom. Fix at[ia]. */ pRankStack1[0][ia] = nSymmRank[ia]; nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); } /* 3. In case of a double bond/cumulene only: */ /* fix at[ib] -- the opposite double bond/cumulene atom */ if ( ib < num_atoms ) { /* find the smallest possible rank */ nRank1 = pRankStack1[0][ib]; nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); /* if the rank is smaller than pRankStack1[0][ib] then fix at[ib] */ if ( nRank2 != nRank1 ) { pRankStack1[0][ib] = nRank2; nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); } } /************************************************************************************** * Note: It may (or may not?) make sense to fix "other neighbors": * in case of a stereo center fix neighbors other than n1, n2 * in case of a double bond/cumulene fix the opposite atom neighbors * The ranks assigned to the other neighbors in case of their equivalence * should be in the ascending order of their canonical ranks ???? * *** For now we do not fix other neighbors *** **************************************************************************************/ /* 4. Check whether the neighbors still have equal ranks */ if ( pRankStack1[0][n1] != pRankStack1[0][n2] ) { return 0; /* the two neighbors are not constitutionally equivalent */ } /* 5. Find new smallest possible rank for n1 and n2 */ nRank1 = pRankStack1[0][n1]; nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); /* 6. Copy the results to the 2nd eq. rank arrays */ memcpy( pRankStack2[0], pRankStack1[0], num_at_tg * sizeof(pRankStack2[0][0]) ); memcpy( pRankStack2[1], pRankStack1[1], num_at_tg * sizeof(pRankStack2[0][0]) ); /* 7. Break neighbor tie: map n1(1) <--> n2(2) */ pRankStack1[0][n1] = nRank2; nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); pRankStack2[0][n2] = nRank2; nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack2[0], nTempRank, pRankStack2[1], lNumIter, 1 ); if ( nNumDiffRanks1 != nNumDiffRanks2 ) { return -1; /* */ } if ( mode == MAP_MODE_C2v || mode == MAP_MODE_C2 ) { /* Check for C2v reflection leading to parity inversion (mode=1) or C2 rotation (mode=2) */ AT_RANK nRank10, nRank20; int nn1, nn2; /* * C2v & C2: map * n1(1) <--> n2(2) -- at this point already done * n1(2) <--> n2(1) --> do at i = 0 * * C2v: other neighbors must be unmoved: map * other_neigh[0](1) <--> other_neigh[0](2) * other_neigh[1](1) <--> other_neigh[1](2) * * C2: other neighbors should be mapped on each other * other_neigh[0](1) <--> other_neigh[1](2) * other_neigh[1](1) <--> other_neigh[0](2) */ for ( i = 0; i <= 2; i ++ ) { if ( i == 0 ) { /* C2v & C2. Map n2(1) <--> n1(2) */ nn1 = n2; nn2 = n1; } else if ( mode == MAP_MODE_C2v ) { /* was '=', pointed by WDI */ /* i = 1 or 2 * C2v. Other neighbors must be unmoved: map * i=1: other_neigh[0](1) <--> other_neigh[0](2) * i=2: other_neigh[1](1) <--> other_neigh[1](2) */ nn1 = other_neigh[i-1]; /* 0 or 1 */ nn2 = other_neigh[i-1]; /* 0 or 1 */ } else if ( mode == MAP_MODE_C2 ) { /* was '=', pointed by WDI */ /* i = 1 or 2 * C2. Other neighbors should be mapped on each other * i=1: other_neigh[0](1) <--> other_neigh[1](2) * i=2: other_neigh[1](1) <--> other_neigh[0](2) */ nn1 = other_neigh[i-1]; /* 0 or 1 */ nn2 = other_neigh[2-i]; /* 1 or 0 */ } else { return -1; /* program error */ } /* map nn1(1) <--> nn2(2) */ nRank10 = pRankStack1[0][nn1]; nRank20 = pRankStack2[0][nn2]; nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); if ( nRank10 == nRank20 && nRank1 == nRank2 ) { if ( nRank10 == nRank1 ) { ;/* atoms are already mapped */ } else { /* need additional mapping: ranks are not fixed yet */ pRankStack1[0][nn1] = nRank1; nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); pRankStack2[0][nn2] = nRank2; nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack2[0], nTempRank, pRankStack2[1], lNumIter, 1 ); if ( nNumDiffRanks1 != nNumDiffRanks2 ) { return -1; /* */ } } } else { return 0; /* mapping is not possible */ } } } if ( mode == MAP_MODE_S4 ) { /* * Check for S4 reflection/rotation leading to parity inversion (mode=3) * * At this point n1(1) <--> n2(2) have been mapped and n1 has index in1 = 0 * Below indexes in neigh_num[] are in brackets; [i] means neigh_num[i]. * Numbers (#) in parentheses refer to pRankStack# * * in2=1: [0](1) <--> [1](2) mapping has been done; add more mappings: * [1](1) <--> [2](2) [x]=[2] * [2](1) <--> [3](2) [y]=[3] * [3](1) <--> [0](2) * this will succeed if C2 axis crosses middle of [0]-[2] and [1]-[3] lines * * in2=2: [0](1) <--> [2](2) mapping has been done; add more mappings: * [2](1) <--> [3](2) [x]=[3] * [3](1) <--> [1](2) [y]=[1] * [1](1) <--> [0](2) * this will succeed if C2 axis crosses middle of [0]-[3] and [1]-[2] lines * * in2=3: [0](1) <--> [3](2) mapping has been done; add more mappings: * [3](1) <--> [1](2) [x]=[1] * [1](1) <--> [2](2) [y]=[2] * [2](1) <--> [0](2) * this will succeed if C2 axis crosses middle of [0]-[1] and [2]-[3] lines * * In general: * [in1](1) <--> [in2](2) * [in2](1) <--> [x] (2) i=0 * [x] (1) <--> [y] (2) i=1 * [y] (1) <--> [in1](2) i=2 * * in1=0 always * ===== how to find x, y from in2 ==== * in2=1 => x,y = 2, 3 or [x] = other_neigh[0], [y] = other_neigh[1] * in2=2 => x,y = 3, 1 or [x] = other_neigh[1], [y] = other_neigh[0] * in2=3 => x,y = 1, 2 or [x] = other_neigh[0], [y] = other_neigh[1] * ==================================== */ AT_RANK nRank10, nRank20; int nn1, nn2; for ( i = 0; i <= 2; i ++ ) { switch( i ) { case 0: /* [in2](1) <--> [x](2); */ nn1 = n2; /* [in2] */ nn2 = other_neigh[1-in2%2]; /* [x] */ break; case 1: /* [x](1) <--> [y](2) */ nn1 = other_neigh[1-in2%2]; /* [x] */ nn2 = other_neigh[ in2%2]; /* [y] */ break; case 2: nn1 = other_neigh[ in2%2]; /* [y] */ nn2 = n1; /* [in1] */ break; default: return -1; /* program error */ } /* map nn1(1) <--> nn2(2) */ nRank10 = pRankStack1[0][nn1]; nRank20 = pRankStack2[0][nn2]; nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); if ( nRank10 == nRank20 && nRank1 == nRank2 ) { if ( nRank10 == nRank1 ) { ;/* atoms are already mapped */ } else { /* need additional mapping: ranks are not fixed yet */ pRankStack1[0][nn1] = nRank1; nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); pRankStack2[0][nn2] = nRank2; nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack2[0], nTempRank, pRankStack2[1], lNumIter, 1 ); if ( nNumDiffRanks1 != nNumDiffRanks2 ) { return -1; /* */ } } } else { return 0; /* mapping is not possible */ } } } #if ( BREAK_ONE_MORE_SC_TIE == 1 ) /* { */ /* Check for a very highly symmetrical stereo center 12-06-2002 */ if ( ib >= num_atoms && at[ia].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { int num_eq; nRank1 = pRankStack1[0][n2]; for ( i = 0, num_eq = 0; i < at[ia].valence; i ++ ) { num_eq += ( nRank1 == pRankStack1[0][at[ia].neighbor[i]]); } if ( num_eq == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = pRankStack1[0][(int)pRankStack1[1][i]]); i -- ) ; if ( i >= 0 ) nRank2 ++; else nRank2 = 1; /* 7a. Break another neighbor tie */ nNumDiffRanks = nNumDiffRanks1; pRankStack1[0][n2] = nRank2; nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); pRankStack2[0][n1] = nRank2; nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack2[0], nTempRank, pRankStack2[1], lNumIter, 1 ); } } if ( nNumDiffRanks1 != nNumDiffRanks2 ) { return -1; /* */ } #endif /* } BREAK_ONE_MORE_SC_TIE */ #if ( BREAK_ALSO_NEIGH_TIE == 1 ) /* check whether neighbor's neighbors are tied and untie them */ if ( at[n1].nRingSystem == at[n2].nRingSystem && ib >= num_atoms ) { AT_RANK NeighNeighList[MAX_NUM_STEREO_ATOM_NEIGH+1]; int m, neigh1=-1, neigh2=-1; nRank1 = nRank2 = 0; /* n1 */ NeighNeighList[0] = at[n1].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ for ( i = 0, m = 1; i < at[n1].valence; i ++ ) { int neigh = at[n1].neighbor[i]; if ( neigh != ia ) { NeighNeighList[m ++] = neigh; } } insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack1[0], nCanonRank ); for ( m = 2; m < at[n1].valence; m ++ ) { if ( pRankStack1[0][NeighNeighList[m]] == pRankStack1[0][NeighNeighList[m-1]] ) { neigh1 = NeighNeighList[m-1]; break; } } /* n2 */ NeighNeighList[0] = at[n2].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ for ( i = 0, m = 1; i < at[n2].valence; i ++ ) { int neigh = at[n2].neighbor[i]; if ( neigh != ia ) { NeighNeighList[m ++] = neigh; } } insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack2[0], nCanonRank ); for ( m = 2; m < at[n2].valence; m ++ ) { if ( pRankStack2[0][NeighNeighList[m]] == pRankStack2[0][NeighNeighList[m-1]] ) { #if ( BREAK_ALSO_NEIGH_TIE_ROTATE == 1 ) neigh2 = NeighNeighList[m]; /* [m] to obtain same axis orientation around ia= 0 && neigh2 >= 0 && pRankStack1[0][neigh1] == pRankStack2[0][neigh2] ) { /* neighbors' neighbors are tied */ nRank1 = pRankStack1[0][neigh1]; nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); /* Break neighbor's neighbor tie */ nNumDiffRanks = nNumDiffRanks1; pRankStack1[0][neigh1] = nRank2; nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack1[0], nTempRank, pRankStack1[1], lNumIter, 1 ); pRankStack2[0][neigh2] = nRank2; nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, nNumDiffRanks, pRankStack2[0], nTempRank, pRankStack2[1], lNumIter, 1 ); } } #endif /* for debug only */ for ( i = 0; i < num_at_tg; i ++ ) { if ( pRankStack1[0][(int)pRankStack1[1][i]] != pRankStack2[0][(int)pRankStack2[1][i]] ) { return -1; /* */ } } /* Resort lists of neighbors */ SortNeighListsBySymmAndCanonRank( num_atoms, nl1, pRankStack1[0], nCanonRank ); SortNeighListsBySymmAndCanonRank( num_atoms, nl2, pRankStack2[0], nCanonRank ); return nNumDiffRanks1+1; } /**************************************************************************************/ int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ) { AT_RANK s1, s2; int i1, i2, k1, k2; if ( nRank1[n1] != nRank2[n2] ) { return -1; /* parallel traversal in stereo removal failed */ /* */ } switch ( !nVisited1[n1] + !nVisited2[n2] ) { case 0: if ( nVisited1[n1] != n2+1 || nVisited2[n2] != n1+1 ) { return -1; /* 0; */ /* possibly error???: we have come to an alreardy traversed pair and */ /* found that the pair previously has not been traversed synchroneously. */ } /* -- Happens in C60. */ break; case 1: return -1; /* 0; */ /* possibly error: one is zero, another is not a zero. Happens in C60 */ /* case 2: */ /* both are zero, OK. */ } if ( nVisitOrd1[n1] != nVisitOrd2[n2] ) { return -1; /* 0; */ /* different DFS trees */ } /* at[n1] and at[n2] are next to at[cur1] and at[cur2] respectively */ /* Even though the bond might have already been checked, check whether */ /* it is a stereo bond/cumulene. If it is, check the bond/cumulene parity. */ /* Even though the bond or cumulene might have already been checked, check it: this is */ /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ /* The code works both for a stereo bond and a stereogenic cumulene. */ for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == n1)); i1 ++ ) ; for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == n2)); i2 ++ ) ; /* -- this does not work in case of cumulenes -- for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(s1-1 == n1)); i1 ++ ) ; for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(s2-1 == n2)); i2 ++ ) ; */ if ( k1 != k2 ) { return 0; /* not an error: a stereo bond and not a stereo bond */ } if ( k1 ) { /* here k1 == k2 */ int bCheckBond1, bCheckBond2; s1 --; s2 --; bCheckBond1 = (cur1 != nAvoidCheckAtom[0] || s1 != nAvoidCheckAtom[1]) && (cur1 != nAvoidCheckAtom[1] || s1 != nAvoidCheckAtom[0]); bCheckBond2 = (cur2 != nAvoidCheckAtom[0] || s2 != nAvoidCheckAtom[1]) && (cur2 != nAvoidCheckAtom[1] || s2 != nAvoidCheckAtom[0]); if ( bCheckBond1 != bCheckBond2 ) return 0; if ( !bCheckBond1 && !bCheckBond2 ) { return 1; /* do not go any further in this direction */ } if ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] ) { /* different values of at[].stereo_bond_parity: definitely different bonds */ /* known parities */ if ( PARITY_KNOWN(at[cur1].stereo_bond_parity[i1] ) && PARITY_KNOWN(at[cur2].stereo_bond_parity[i2] ) ) { return 0; /* different currently known stereo bond parities */ } #if ( PROPAGATE_ILL_DEF_STEREO != 1 ) /* well defined and to be calculated from the ranks */ if ( !(PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_WELL_DEF (at[cur2].stereo_bond_parity[i2]) || PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) || PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) ) ) { /* do not reject if: "well defined" and "calculate" or "calculate" and "calculate" */ return 0; } #endif } #if ( PROPAGATE_ILL_DEF_STEREO != 1 ) if ( (cur1 != cur2 || s1 != s2) && (cur1 != s2 || cur2 != s1) ) { /* two different stereo bonds */ if ( PARITY_ILL_DEF( at[cur1].stereo_bond_parity[i1] ) || PARITY_ILL_DEF( at[cur2].stereo_bond_parity[i2] ) ) { return 0; } } #endif } return 1; /* stereo bonds to n1 and n2 have same known parities or are not stereo bonds */ } /**************************************************************************************/ int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ) { int k, k1, k2, ret=0, bParitiesInvertedZero=0, *pbParitiesInverted; AT_RANK n1, n2; nVisited1[cur1] = cur2+1; /* symmetrically exchange atom numbers */ nVisited2[cur2] = cur1+1; (*nLength) ++; nVisitOrd1[cur1] = *nLength; /* save DFS visit order */ nVisitOrd2[cur2] = *nLength; /* new version allows all inverted parities */ if ( PARITY_WELL_DEF(at[cur1].stereo_atom_parity) && PARITY_WELL_DEF(at[cur2].stereo_atom_parity) ) { if ( *bParitiesInverted < 0 ) { *bParitiesInverted = (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2; } else if ( *bParitiesInverted != (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2 ) { return 0; /* Different known in advance parities have wrong "inverted" relation */ } } else if ( PARITY_KNOWN(at[cur1].stereo_atom_parity) && PARITY_KNOWN(at[cur2].stereo_atom_parity) && at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity ) { return 0; /* Different known in advance parities */ } if ( cur1 != cur2 && !at[cur1].stereo_bond_neighbor[0] && !at[cur2].stereo_bond_neighbor[0] && PARITY_KNOWN(at[cur1].parity) != PARITY_KNOWN(at[cur2].parity) ) { return 0; /* one atom is stereogenic, another (presumably equivalent) is not. 9-11-2002 */ } #if ( PROPAGATE_ILL_DEF_STEREO != 1 ) if ( cur1 != cur2 && (PARITY_ILL_DEF(at[cur1].stereo_atom_parity) || PARITY_ILL_DEF(at[cur2].stereo_atom_parity)) ) { return 0; /* Cannot detect whether the paths are same or different */ } #endif if ( at[cur1].valence != at[cur2].valence ) { return CT_REMOVE_STEREO_ERR; /* program error */ /* */ } if ( at[cur1].valence == 1 ) { return 1; /* so far success */ } if ( nl1[(int)cur1][0] != nl2[(int)cur2][0] || nl1[(int)cur1][0] != at[cur1].valence ) { return CT_REMOVE_STEREO_ERR; /* error: different valences */ /* */ } for ( k = 1, k1 = 1, k2 = 1; k < at[cur1].valence; k ++, k1 ++, k2 ++ ) { if ( (n1 = nl1[(int)cur1][k1]) == prev1 ) { n1 = nl1[(int)cur1][++k1]; /* don't go back */ } if ( (n2 = nl2[(int)cur2][k2]) == prev2 ) { n2 = nl2[(int)cur2][++k2]; /* don't go back */ } if ( 0 >= (ret = CheckNextSymmNeighborsAndBonds( at, cur1, cur2, n1, n2, nAvoidCheckAtom, nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, nRank1, nRank2 ) ) ) { return ret; /* different neighbors or bonds */ } if ( !nVisited1[n1] ) { /* recursion */ /* allow all inverted parities only inside a single ring system containing the starting point */ pbParitiesInverted = (at[cur1].nRingSystem == at[n1].nRingSystem)? bParitiesInverted:&bParitiesInvertedZero; if ( 0 >= (ret = CreateCheckSymmPaths( at, cur1, n1, cur2, n2, nAvoidCheckAtom, nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, nl1, nl2, nRank1, nRank2, nCanonRank, nLength, pbParitiesInverted, mode ) ) ) { return ret; } } } return 1; /* Success */ } /**************************************************************************************/ /* Compare parities */ #define MAX_OTHER_NEIGH 2 /* nNeighMode */ #define NEIGH_MODE_RING 1 #define NEIGH_MODE_CHAIN 2 #define CHECKING_STEREOCENTER 1 #define CHECKING_STEREOBOND 2 #define COMP_STEREO_SUCCESS 1 #define NOT_WELL_DEF_UNKN 2 #define NOT_WELL_DEF_UNDF 4 #define PARITY_IMPOSSIBLE 999 /************************************************************************************** Note: the following C2v/S4 stereo center symmetry recognition is not included in the final InChI version released in April 2005 It is disabled in the mode.h (CHECK_C2v_S4_SYMM = 0) As the result, the only central atom in S4 or atoms on C2v axis may have pasrity (-) even though these atoms are not stereogenic. Reason: Not finished/tested yet ************************************************************************************** In case of stereocenter with 2 pairs of constitutionally identical neighbors : G(n) > H(m) means group G has n elements; group H has m elements and group H is a subgroup of G Td(24) > D2d(8> > D2(4) > S4(4) > C2(2) -- Test for S4 > C2v(4) > C2(2) -- Test for C2v > Cs(2) Td(24) > C3v(6) > C3(3) -- does not have 2 pairs of constitutionally identical neighbors > Cs(2) The pair of atoms to check for the existence of a steregenic atom: X, Y X Y \ / C / \ A B Conditions to check: (a) Old #0: Map canonical numbers X1 <--> Y2 Traverse DFS from X and Y If all parities vs. canon. numbers unchanged except that of C then C is not stereogenic (b) C2v #1: discover ACB symmetry plain Cv o Map canonical numbers X1 <--> Y2, Fix(Ai), Fix(Bi) o Make sure that after mapping X1 <--> Y2 the atoms Ai and Bi still have equal mapping ranks Traverse DFS from X and Y In this case canonical numbers will be reflected in plane ACB if it exists. o Criterion of the presence of the symmetry plain is: --> all stereogenic atoms and allenes parities are inverted (c) C2v #2: discover vertical axis C2 o Map canonical numbers X1 <--> Y2 and A1 <--> B2 o Make sure that after mapping X1 <--> Y2 the atoms Ai and Bi still have equal mapping ranks o Traverse DFS from X1 and Y2 In this case canonical numbers will be rotated by 180 degrees around the vertical axis (this may be considered as a superposition of two Cv reflections in perpendicular vertical planes) o Criterion of the presence of the C2 axis is: --> all stereogenic atoms and allenes parities are not changed (d) S4 #3: discover axis horizontal S4 axis o Map canonical numbers X1 <--> Y2, Y1 <--> A2, A1 <--> B2, B1 <--> X2 o Traverse DFS from X1 and Y2 In this case the canonical numbers will be rotated by 90 degrees and reflected in a horizontal plane. 3 attempts corrresponding to transpositions 0132, 0213, 0321 are sufficient (XY=01,02,03) o Criterion of the presence of the S4 symmetry axis is: --> all stereogenic atoms and allenes parities are inverted ***************************************************************************************/ /**************************************************************************************/ int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, AT_RANK *nVisited1, AT_RANK *nVisited2, AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, int bParitiesInverted, int mode, CANON_STAT *pCS, int vABParityUnknown) { int i, i01, i02, i11, i12, i21, i22, k, parity, parity1, parity2, parity12, num_other_neigh; int nNumEqStereogenic, nCheckingMode, not_well_def_parities; AT_RANK other_neigh[MAX_NUM_STEREO_ATOM_NEIGH], neigh, r1, r2; int nNumComparedCenters = 0, nNumComparedBonds = 0, bCurParityInv1=0 /*, bCurParityInv2=0*/; int bCurRotated=0, nNumDiff=0, nNumInv=0; int s1, s2; nCheckingMode = ( prev_sb_neigh < num_atoms )? CHECKING_STEREOBOND : CHECKING_STEREOCENTER; not_well_def_parities = 0; nNumEqStereogenic = 0; if ( nNeighMode != NEIGH_MODE_RING && bParitiesInverted != 0 || abs(bParitiesInverted) != 1 ) { bParitiesInverted = 0; } if ( bParitiesInverted ) { for ( i = 0, i11 = i22 = 0; i < num_atoms; i ++ ) { /* count number of atoms that have not been visited */ i11 += !nVisited1[i]; i22 += !nVisited2[i]; nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ } if ( i11 || i22 ) { if ( bParitiesInverted == 1 ) return 0; /* only a part of the structure has been inverted */ else bParitiesInverted = 0; } } else { for ( i = 0; i < num_atoms; i ++ ) { nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ } } if ( bParitiesInverted > 0 && !(mode == MAP_MODE_C2v || mode == MAP_MODE_S4) || bParitiesInverted == 0 && !(mode == MAP_MODE_C2 || mode == MAP_MODE_STD)) { return 0; } /************************************************************************************** * The following discussion assumes that the canonical numbers are * switched for some pairs of constitutionally identical atoms * in such a way that the new numbering is an equivalent to the * nCanonRank[] canonical numbering (the transposition belongs to the * automorphism group of the chemical structure having no stereo). * At this point non-zero elements of nVisited1[] and nVisited2[] * together contain transposition P of the atom numbers. * and P2 respectively of the ordering atom numbers: nVisitedi[k] = Pi(k)+1; * In this implementation: * P1(k)=k for all k * P2(cur)=cur, P2(next1)=next2, P2(next2)=next1 * * Below we call one of the numberings "old", another "new". * * *IF* the old and the new canonical numberings produce same parities for stereogenic * elements for the same canonical number(s) * (that is, old_parity(canon_number) == new_parity(canon_number) * *except* the currently being tested stereocenter at[cur] or stereobond/cumulene * at[cur]=at[prev_sb_neigh], whose parity MUST be inverted * * *THEN* the stereocenter or stereobond/cumulene is not stereogenic with one * * *EXCEPTION* If the currently tested stereogenic element is constitutionally * equivalent to two or more other stereogenic elements that have been * permuted then the currently tested one is still stereogenic. **************************************************************************************/ /* * 1. replace the assigned in each of the parallel traversals atom numbers * with the canon. ranks corresponding to the atom numbers in the * currently numbered atoms at[]. * One of obtained this way canonical numberings (probably nVisited1[]) * is same as the nCanonRank[] because usually nVisited1[i] = i+1 or 0 */ for ( i = 0; i < num_atoms; i ++ ) { if ( nVisited1[i] ) { /* canonical number of the atom mapped on atom #i in 'left' path */ nVisited1[i] = nCanonRank[ (int)nVisited1[i] - 1 ]; /* reverse: atom # from the mapped canonical rank in 'left' path */ nAtomNumberCanon1[nVisited1[i] - 1] = i; } if ( nVisited2[i] ) { /* canonical number of the atom mapped on atom #i in 'right' path */ nVisited2[i] = nCanonRank[ (int)nVisited2[i] - 1 ]; /* reverse: atom # from the mapped canonical rank in 'right' path */ nAtomNumberCanon2[nVisited2[i] - 1] = i; } /* if 'left' and 'right' path do not have atoms in common except the starting atom (and in case of stereobond, the end atom) some of nVisitedi[i] elements may be zero. */ } /* * if started with a stereobond then check whether its parity has changed. * If yes then continue, otherwise parities are different * * if started with a stereo center then prev_sb_neigh = MAX_ATOMS+1 * * If the transposition of next1 and next2 changes only the parity of the starting stereo atom or stereo bond * then the stereo bond or stereo atom is not stereogenic * * The exception: the stereogenic elememt in question is equivalent * to two or more traversed other stereogenic elememts * (see nNumEqStereogenic below, case similar to trimethylcyclopropane: * 3 or more constitutionally equivalent stereogenic elements) */ if ( nCheckingMode == CHECKING_STEREOBOND ) { /****************************************************************************** * * Possibly stereogenic starting bond or cumulene at[cur]-at[prev_sb_neigh] * *******************************************************************************/ /* checking the starting stereo bond */ if ( nVisited1[prev_sb_neigh] || nVisited2[prev_sb_neigh] ) { /* the bond or cumulene is in the ring and the opposite atom has been visited */ if ( nVisited1[prev_sb_neigh] != nVisited2[prev_sb_neigh] || nCanonRank[prev_sb_neigh] != nVisited2[prev_sb_neigh] ) { return 0; /* error: we came back to the same bond/cumulene and */ /* */ /* assigned different canon. ranks to the opposite atom. */ } if ( at[prev_sb_neigh].valence + at[prev_sb_neigh].num_H > 3 ) return 0; /* at[prev_sb_neigh] atom can not be adjacent to a stereo bond/cumulene */ /* or does not have 3 attachments (hydrogens are not considered here) */ for ( i = 0, k = 0; i < MAX_NUM_STEREO_BONDS && (neigh=at[prev_sb_neigh].stereo_bond_neighbor[i]) && !(k=(neigh-1 == cur)); i ++ ) ; if ( !k ) { return -1; /* program error: could not locate stereogenic bond mark on the opposite atom */ } k = (int)at[prev_sb_neigh].stereo_bond_ord[i]; /* seq. number of the double or cumulene bond on at[prev_sb_neigh] */ for ( i = 0, num_other_neigh = 0; i < at[prev_sb_neigh].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { if ( i != k ) { /* do not include the double or cumulene bond */ other_neigh[num_other_neigh ++] = at[prev_sb_neigh].neighbor[i]; } } if ( num_other_neigh + at[prev_sb_neigh].num_H > MAX_OTHER_NEIGH ) { return CT_STEREOCOUNT_ERR; /* */ } for ( i = 0; i < num_other_neigh; i ++ ) { k = (int)other_neigh[i]; if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { return 0; /* parity of the statring stereo bond/cumulene has not changed. */ } if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { return 0; /* parity of the statring stereo bond/cumulene has not changed. */ } } } } if ( nCheckingMode == CHECKING_STEREOCENTER ) { /************************************************** * * Possibly stereogenic starting atom at[cur] * **************************************************/ /* checking the starting stereo center */ for ( i = 0, num_other_neigh = 0; i < at[cur].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { neigh = at[cur].neighbor[i]; if ( neigh != next1 && neigh != next2 ) { other_neigh[num_other_neigh ++] = neigh; } } if ( num_other_neigh + at[cur].num_H > MAX_OTHER_NEIGH ) { return CT_STEREOCOUNT_ERR; /* */ } /* if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { if ( nVisited1[other_neigh[0]] == nCanonRank[other_neigh[0]] || nVisited2[other_neigh[0]] == nCanonRank[other_neigh[0]] || nVisited1[other_neigh[1]] == nCanonRank[other_neigh[1]] || nVisited2[other_neigh[1]] == nCanonRank[other_neigh[1]] ) { bParitiesInverted = 0; bCurRotated = 1; } } */ /* bParitiesInverted = -1 means no predefined stereocenter has been checked */ if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { /* special case: 4 canonically eq. neighbors */ int canon_parity, parity_vis_1, parity_vis_2; canon_parity = GetPermutationParity( at+cur, MAX_ATOMS+1, nCanonRank ); parity_vis_1 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited1 ); parity_vis_2 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited2 ); if ( parity_vis_1 != parity_vis_2 ) { return 0; } if ( bParitiesInverted == 1 && parity_vis_1 == canon_parity ) { return 0; /* not a typical case of inversion during the mapping of D4h stereocenter */ } else if ( bParitiesInverted == -1 ) { if ( parity_vis_1 == canon_parity ) { bParitiesInverted = 0; } else { bParitiesInverted = 1; } } } /* at this point bParitiesInverted >= 0 */ if ( !bParitiesInverted && !bCurRotated ) { for ( i = 0; i < num_other_neigh; i ++ ) { k = (int)other_neigh[i]; if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { return 0; /* parity of the statring stereo center has not changed. */ } if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { return 0; /* parity of the statring stereo center has not changed. */ } } } } /***************************************************** * Check other (non-starting) stereo centers ******************************************************/ for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++, nNumComparedCenters += (k > 0) ) { r1 = pCS->LinearCTStereoCarb[i].at_num; i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that has canon rank r1 */ i11 = nAtomNumberCanon1[r1-1]; /* = (MAX_ATOMS+1) > num_atoms if the atom has not been traversed */ i12 = nAtomNumberCanon2[r1-1]; /* = otherwise < num_atoms */ s1 = (i11 < num_atoms); /* 1 => the center was traversed on path #1 */ s2 = (i12 < num_atoms); /* 1 => the center was traversed on path #2 */ bCurParityInv1 = (bParitiesInverted && at[cur].nRingSystem == at[i11].nRingSystem && at[cur].nRingSystem == at[i12].nRingSystem ); k = 0; /* check whether the two stereo centers (they can be one and the same atom) have been traversed */ if ( !s1 && !s2 ) { continue; /* Both stereo centers have not been traversed; check the next pair. */ } if ( nCheckingMode == CHECKING_STEREOCENTER ) { /* check whether the stereocenters are the starting stereocenter */ switch( (cur == i11) + (cur == i12) ) { case 2: continue; /* do not recheck the starting atom */ case 1: return -1; /* possibly program error */ /* */ /* case 0: */ /* break; */ /* the stereo centers are not the sarting stereo center */ } if ( cur == i01 ) { return -1; /* program error: in this case at least one of the i11, i12 must be == cur */ /* */ } } if ( nNeighMode == NEIGH_MODE_RING ) { if ( i11 != i12 && !bCurParityInv1 ) { return -1; /* failed: the two stereo atoms have not been traversed synchronously */ } if ( !at[i11].parity || !at[i12].parity ) { return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ } } if ( nNeighMode == NEIGH_MODE_CHAIN ) { if ( s1+s2 != 1 ) { return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ } if ( s1 && !at[i11].parity || s2 && !at[i12].parity ) { return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ } } parity = pCS->LinearCTStereoCarb[i].parity; if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01) && (i12 != i01) || /* in NEIGH_MODE_RING case we know that i11 == i12 except bCurParityInv1 == 1 */ nNeighMode == NEIGH_MODE_CHAIN /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ ) { /**************************************************************** * Case of two transposed atoms or a circular permutation in D4h */ parity1 = s1? GetStereoCenterParity( at, i11, nVisited1 ) : PARITY_IMPOSSIBLE; parity2 = s2? GetStereoCenterParity( at, i12, nVisited2 ) : PARITY_IMPOSSIBLE; if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { return -1; /* should not happen: must have been detected at the time of the traversal */ } if ( s1 && s2 ) { if ( bCurParityInv1 ) { int parity1orig = GetStereoCenterParity( at, i11, nCanonRank ); int parity2orig = GetStereoCenterParity( at, i12, nCanonRank ); if ( i11 == i12 || (parity1 == parity1orig || parity2 == parity2orig || parity1 != parity2) && ATOM_PARITY_WELL_DEF(parity1) || parity1 != parity2 && (!ATOM_PARITY_WELL_DEF(parity1) || !ATOM_PARITY_WELL_DEF(parity2)) ) /*return -1; */ /* should be different atoms with inverted parities */ nNumDiff ++; } else { if ( i11 != i12 || parity1 != parity2 ) return -1; /* program error: must be the same atom */ } } parity12 = s1? parity1 : parity2; if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { /* symmetrical neighbors have well-defined equal parities */ k ++; if ( nCheckingMode == CHECKING_STEREOCENTER && nNeighMode == NEIGH_MODE_RING ) { /* all 3: cur, i01, i11 are different atoms (here i11==i12) */ /* here nSymmRank[i01]==nSymmRank[i11] due to the parallel traversal */ if ( nSymmRank[cur] == nSymmRank[i01] ) { nNumEqStereogenic ++; /* all 3 are equ */ } } } else if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { /* apparently different well-defined parities */ if ( !bCurParityInv1 ) { nNumInv ++; /* return 0; */ } } else { #if ( PROPAGATE_ILL_DEF_STEREO == 1 ) /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; if ( parity1 | parity2 ) { not_well_def_parities |= ( parity1 | parity2 ); k ++; } else { return -1; /* program error */ /* */ } #else return 0; #endif } } else if ( i11 == i01 && i12 == i01 ) { /********************************************************************/ /* i11 == i12 are same atom as i01, nNeighMode == NEIGH_MODE_RING */ if ( !s1 || !s2 ) { return -1; } /* the parity of the new neighbors permutation must be same as the old one */ /* this must work for well-defined and ill-defined parities. */ /* actual parity (that includes the geometry) is not important here. */ /* old permutation */ parity = GetPermutationParity( at+i01, MAX_ATOMS+1, nCanonRank ); /* new parmutation */ parity1 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited1 ); parity2 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited2 ); if ( parity != parity1 || parity != parity2 ) { return 0; } k ++; } else { /* nNeighMode == NEIGH_MODE_RING and only one out of the two (i11 == i01) (i12 == i01) is true */ return -1; } /* nNumComparedCenters += (k > 0); */ } if ( bCurRotated || nNumDiff || nNumInv ) { return 0; } /* !!!! Add here bParitiesInverted == 1 case !!!! */ /******************************************************/ /* Check other (non-starting) stereo bonds/cumulenes */ /******************************************************/ for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++, nNumComparedBonds += (k > 0) ) { r1 = pCS->LinearCTStereoDble[i].at_num1; r2 = pCS->LinearCTStereoDble[i].at_num2; i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that originally has canon rank r1 */ i02 = nAtomNumberCanon[r2-1]; /* ord. number of the atom that originally has canon rank r2 */ i11 = nAtomNumberCanon1[r1-1]; /* ord. number of the atom that got canon rank r1 during the parallel traversal */ i12 = nAtomNumberCanon1[r2-1]; /* ord. number of the atom that got canon rank r2 during the parallel traversal */ i21 = nAtomNumberCanon2[r1-1]; i22 = nAtomNumberCanon2[r2-1]; s1 = (i11 < num_atoms && i12 < num_atoms); s2 = (i21 < num_atoms && i22 < num_atoms); k = 0; /* check whether the two stereo bonds/allenes (they can be one and the same) have been traversed */ if ( !s1 && !s2 ) { continue; /* Both stereo bonds/cumulenes have not been traversed; check the next pair. */ } if ( nCheckingMode == CHECKING_STEREOBOND ) { switch ( (i11 == cur && i12 == prev_sb_neigh || i12 == cur && i11 == prev_sb_neigh) + (i21 == cur && i22 == prev_sb_neigh || i22 == cur && i21 == prev_sb_neigh) ) { case 2: continue; /* do not recheck the starting bond/cumulene */ case 1: return -1; /* possibly program error */ /* */ /* case 0: */ /* break; */ /* the stereo centers are not the sarting stereo center */ } if ( (i01 == cur && i02 == prev_sb_neigh) || (i02 == cur && i01 == prev_sb_neigh) ) { return -1; /* program error: in this case at least one of the i1x, i2x must be == cur */ /* */ } } if ( nNeighMode == NEIGH_MODE_RING ) { if ( (i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) ) { return -1; /* failed: the two bonds/cumulenes have not been traversed synchronously */ } if ( 0 > GetStereoNeighborPos( at, i11, i12 ) ) { return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ } } if ( nNeighMode == NEIGH_MODE_CHAIN ) { if ( s1+s2 != 1 ) { return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ } if ( s1 && 0 > GetStereoNeighborPos( at, i11, i12 ) || s2 && 0 > GetStereoNeighborPos( at, i21, i22 ) ) { return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ } } parity = pCS->LinearCTStereoDble[i].parity; /* bMustBeIdentical = ATOM_PARITY_ILL_DEF(parity); */ /* nNumEqStereogenic = 0; */ if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01 || i12 != i02) && (i11 != i02 || i12 != i01) || nNeighMode == NEIGH_MODE_CHAIN /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ ) { /*******************************************/ /* case of two transposed bonds/cumulenes */ parity1 = s1? GetStereoBondParity( at, i11, i12, nVisited1 ) : PARITY_IMPOSSIBLE; parity2 = s2? GetStereoBondParity( at, i21, i22, nVisited2 ) : PARITY_IMPOSSIBLE; if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { return -1; /* should not happen: must have been detected at the time of traversal */ } if ( s1 && s2 && ((i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) || parity1 != parity2 ) ) { return -1; /* program error: must be the same bond/cumulene */ } parity12 = s1? parity1 : parity2; if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { /* symmetrical neighbors have well-defined equal parities */ k ++; if ( nCheckingMode == CHECKING_STEREOBOND && nNeighMode == NEIGH_MODE_RING ) { /* all 3 bonds: cur-prev_sb_neigh, i01-i02, i11-i12 are different */ /* (here == compared as unordered pairs) */ if ( nSymmRank[cur] == nSymmRank[i01] && nSymmRank[prev_sb_neigh] == nSymmRank[i02] || nSymmRank[cur] == nSymmRank[i02] && nSymmRank[prev_sb_neigh] == nSymmRank[i01] ) { nNumEqStereogenic ++; } } } else if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { /* apparently different well-defined parities */ return 0; } else { /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ #if ( PROPAGATE_ILL_DEF_STEREO == 1 ) parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; if ( parity1 | parity2 ) { not_well_def_parities |= ( parity1 | parity2 ); k ++; } else { return -1; /* program error */ } #else return 0; #endif } } else { /*****************************************************************************************/ /* i11-i12 and i21-i22 are same as i01-i02 bond/cumulene, nNeighMode == NEIGH_MODE_RING */ AT_NUMB n1, n2; int j; if ( !s1 || !s2 ) { return -1; } /* find neighbors along the stereo bond/cumulene */ for ( j = 0, n1 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i01].stereo_bond_neighbor[j]; j ++ ) { if ( (int)at[i01].stereo_bond_neighbor[j] == i02+1 ) { n1 = at[i01].neighbor[ (int)at[i01].stereo_bond_ord[j] ]; break; } } for ( j = 0, n2 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i02].stereo_bond_neighbor[j]; j ++ ) { if ( (int)at[i02].stereo_bond_neighbor[j] == i01+1 ) { n2 = at[i02].neighbor[ (int)at[i02].stereo_bond_ord[j] ]; break; } } if ( n1 > MAX_ATOMS || n2 > MAX_ATOMS ) { return CT_REMOVE_STEREO_ERR; } /* the parity of the new neighbors permutation must be same as the old one */ /* this must work for well-defined and ill-defined parities. */ /* actual parity (that includes the geometry) is not important here. */ /* old permutation */ parity = GetPermutationParity( at+i01, n1, nCanonRank) + GetPermutationParity( at+i02, n2, nCanonRank); /* new parmutation */ parity1 = GetPermutationParity( at+i01, n1, nVisited1 ) + GetPermutationParity( at+i02, n2, nVisited1 ); parity2 = GetPermutationParity( at+i01, n1, nVisited2 ) + GetPermutationParity( at+i02, n2, nVisited2 ); if ( parity %2 != parity1 % 2 || parity1 % 2 != parity2 % 2 ) { return 0; } k ++; } /* nNumComparedBonds += ( k > 0 ); */ } if ( nNumEqStereogenic > 0 ) { /* case similar to trimethylcyclopropane: 3 constitutionally equivalent stereogenic elements */ /* the transposition does not change the parities */ #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_2EQL2CENTER_TO_REMOVE_PARITY; #endif return 0; } /* ========================================================================================= Note ==== At this point the comparison is complete and no difference sufficient to establish absence of stereo parity has been found. However, non-zero not_well_def_parities means that an ill-defined parity was compared to an ill-defined or well-defined parity. This means that the parity of the atom or bond being checked cannot be well-defined anymore. ========================================================================================*/ not_well_def_parities |= COMP_STEREO_SUCCESS; return not_well_def_parities; /* Add 1 to indicate success. The stereogenic elements might have been */ /* removed while checking existence of the previous atom/bond stereo */ /* return (nNumComparedCenters + nNumComparedBonds + 1); */ } /********************************************************************************/ /* Remove stereo marks from the bonds that are calculated to be non-stereo */ /* Such bonds must have 2 constitutionally equivalent attachments */ /* (can find two canonical numberings that change only one stereo bond parity) */ int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, AT_RANK *nCanonRank, const AT_RANK *nSymmRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, int vABParityUnknown) { int j, n, m, ret, ret1, ret2, ret_failed=0; int i1, n1, s2; /* n1 must be SIGNED integer */ AT_RANK nAtomRank1, nAtomRank2, neigh[3], nAvoidCheckAtom[2], opposite_atom, nLength; int nNeighMode = NEIGH_MODE_CHAIN; int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; NEIGH_LIST *nl01, *nl02; const AT_RANK *nSymmRank1, *nSymmRank2; ret = 0; second_pass: for ( i1 = 0; i1 < num_atoms && !RETURNED_ERROR(ret_failed); i1 ++ ) { if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { continue; } for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && !RETURNED_ERROR(ret_failed) && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { continue; } opposite_atom = (AT_RANK)(s2-1); s2 = at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]; /* different from opposite_atom in case of a cumulene */ for ( j = 1, n = 0; j <= (int)at[i1].valence; j ++ ) { if ( nl[i1][j] != s2 ) { neigh[n++] = nl[i1][j]; /* sorting guarantees that canon. rank of neigh[0] is greater or equal */ } } if ( n != 2 ) { ret = CT_STEREOBOND_ERROR; /* */ goto exit_function; } if ( nSymmRank[(int)neigh[0]] != nSymmRank[(int)neigh[1]] ) { continue; /* may happen if another half-bond has not a defined parity */ } bRingNeigh = (at[(int)neigh[0]].nRingSystem == at[(int)neigh[1]].nRingSystem); switch ( nNeighMode ) { case NEIGH_MODE_CHAIN: if ( bRingNeigh ) { nNumEqRingNeigh ++; continue; } nl01 = nl; nl02 = nl; nSymmRank1 = nSymmRank; nSymmRank2 = nSymmRank; break; case NEIGH_MODE_RING: if ( !bRingNeigh ) continue; /* break a tie between the two contitutionally equivalent neighbors, */ /* refine the two partitions, sort neighbors lists nl1, nl2 */ bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, opposite_atom, i1, neigh, 0, 1, 0, pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, nl1, nl2, &pCS->lNumNeighListIter ); if ( bSymmNeigh <= 0 ) { if ( ret_failed > bSymmNeigh ) ret_failed = bSymmNeigh; continue; } nl01 = nl1; nl02 = nl2; nSymmRank1 = pRankStack1[0]; nSymmRank2 = pRankStack2[0]; break; default: return CT_STEREOCOUNT_ERR; /* */ } /* initialize arrays */ memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); nLength = 1; nVisited1[i1] = i1+1; /* start atoms are the same */ nVisited2[i1] = i1+1; nAtomNumberCanon1[i1] = nLength; nAtomNumberCanon2[i1] = nLength; nAvoidCheckAtom[0] = i1; nAvoidCheckAtom[1] = opposite_atom; bParitiesInverted = (nNeighMode == NEIGH_MODE_RING && IS_ALLENE_CHAIN(at[i1].stereo_bond_parity[n1]) && PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && at[i1].nRingSystem == at[opposite_atom].nRingSystem && at[opposite_atom].valence==MAX_NUM_STEREO_BONDS)? -1 : 0; ret1 = ret2 = 0; if ( 0 < (ret1=CreateCheckSymmPaths( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nAvoidCheckAtom, nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, &bParitiesInverted, 0 ) ) && 0 < (ret2=CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, nVisited1, nVisited2, opposite_atom, (AT_RANK)i1, neigh[0], neigh[1], nNeighMode, bParitiesInverted, 0, pCS, vABParityUnknown ) ) ) { if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { /* possibly change the parity to unknown or undefined */ int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; if ( PARITY_ILL_DEF(at[i1].stereo_bond_parity[n1]) && PARITY_VAL(at[i1].stereo_bond_parity[n1]) > new_parity || PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) ) { /* set new unknown or undefined parity */ SetOneStereoBondIllDefParity( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/, new_parity ); /* change in pCS */ nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { pCS->LinearCTStereoDble[n].parity = new_parity; #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; #endif m = -1; break; } } if ( m >= 0 ) { ret = CT_STEREOCOUNT_ERR; /* */ goto exit_function; } ret ++; } } else { /* remove the parity */ if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/ ) ) { ret = CT_STEREOBOND_ERROR; /* */ goto exit_function; } n1 --; /* cycle counter may temporarily become negative */ /* Remove from the pCS */ nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ memmove( pCS->LinearCTStereoDble + n, pCS->LinearCTStereoDble + n + 1, (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); } pCS->nLenLinearCTStereoDble --; #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; #endif m = -1; break; } } if ( m >= 0 ) { ret = CT_STEREOCOUNT_ERR; /* */ goto exit_function; } ret ++; } } else { if ( !ret_failed ) { ret_failed = (ret1<0)? ret1 : (ret2<0)? ret2 : 0; } if ( !RETURNED_ERROR(ret_failed) ) { if ( RETURNED_ERROR( ret1 ) ) ret_failed = ret1; else if ( RETURNED_ERROR( ret2 ) ) ret_failed = ret2; } } } } if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { nNeighMode = NEIGH_MODE_RING; goto second_pass; } exit_function: return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret_failed+1) : ret; } /****************************************************************************/ /* Remove stereo marks from the atoms that are calculated to be non-stereo */ /* (can find two numberings that change only one stereo center parity) */ int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, AT_RANK *nCanonRank, const AT_RANK *nSymmRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, int vABParityUnknown) { int j, n, m, ret; int i, k, ret1, ret2, ret_failed=0, mode, max_mode; AT_RANK nAtomRank1, neigh[MAX_NUM_STEREO_ATOM_NEIGH], nAvoidCheckAtom[2], nLength; int nNeighMode = NEIGH_MODE_CHAIN; int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; NEIGH_LIST *nl01, *nl02; const AT_RANK *nSymmRank1, *nSymmRank2; ret = 0; second_pass: for ( i = 0; i < num_atoms && !RETURNED_ERROR(ret_failed); i ++ ) { if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { continue; } if ( at[i].valence > MAX_NUM_STEREO_ATOM_NEIGH ) { continue; /* error: stereo center cannot have more than 4 neighbors */ /* */ } /* at[i1] is a stereo center */ if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && !PARITY_ILL_DEF(at[i].stereo_atom_parity) ) { continue; } /* neighbors sorted according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ /* sorting guarantees that for two constit. equ. neighbors canon. ranks of the first is greater */ /* !!! previously (but not anymore) the canon. rank of neigh[0] was greater than the others !!! */ for ( j = 0; j < at[i].valence; j ++ ) { neigh[j] = nl[i][j+1]; /* sorting does NOT guarantee that canon. rank of neigh[0] is greater than others */ } /* * mode = 0 => Standard approach: switch 2 neighbors * 1 => Check for C2v reflection leading to parity inversion * 2 => Check for C2 rotation preserving parities * 3 => Check for S4 rotation/reflection leading to parity inversion */ #if ( CHECK_C2v_S4_SYMM == 1 ) if ( nNeighMode = NEIGH_MODE_RING && at[i].valence == 4 && nSymmRank[(int)neigh[0]] == nSymmRank[(int)neigh[1]] && nSymmRank[(int)neigh[2]] == nSymmRank[(int)neigh[3]] && !at[i].bCutVertex ) { if ( nSymmRank[(int)neigh[1]] == nSymmRank[(int)neigh[2]] ) { max_mode = MAP_MODE_S4; } else { max_mode = inchi_max(MAP_MODE_C2v, MAP_MODE_C2); } } else { max_mode = MAP_MODE_STD; } #else max_mode = MAP_MODE_STD; #endif for ( j = 0; j < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); j ++ ) { for ( k = j+1; k < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); k ++ ) { for ( mode = 0; mode <= max_mode && at[i].parity && !RETURNED_ERROR(ret_failed); mode ++ ) { if ( nSymmRank[(int)neigh[j]] != nSymmRank[(int)neigh[k]] ) { continue; /* the two neighbors are not constitutionally identical */ } bRingNeigh = (at[(int)neigh[j]].nRingSystem == at[(int)neigh[k]].nRingSystem); switch ( nNeighMode ) { case NEIGH_MODE_CHAIN: if ( bRingNeigh ) { nNumEqRingNeigh ++; continue; } nl01 = nl; nl02 = nl; nSymmRank1 = nSymmRank; nSymmRank2 = nSymmRank; break; case NEIGH_MODE_RING: if ( !bRingNeigh ) continue; /* break a tie between the two contitutionally equivalent neighbors, */ /* refine the two partitions, sort neighbors lists nl1, nl2 */ bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, MAX_ATOMS+1, i, neigh, j, k, mode, pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, nl1, nl2, &pCS->lNumNeighListIter ); if ( bSymmNeigh <= 0 ) { if ( ret_failed > bSymmNeigh ) ret_failed = bSymmNeigh; continue; } nl01 = nl1; nl02 = nl2; nSymmRank1 = pRankStack1[0]; nSymmRank2 = pRankStack2[0]; break; default: return CT_STEREOCOUNT_ERR; /* */ } /* initialize arrays */ memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); nLength = 1; nVisited1[i] = i+1; /* start atom is same */ nVisited2[i] = i+1; nAtomNumberCanon1[i] = nLength; nAtomNumberCanon2[i] = nLength; nAvoidCheckAtom[0] = i; nAvoidCheckAtom[1] = MAX_ATOMS+1; bParitiesInverted = (mode==MAP_MODE_C2v || mode==MAP_MODE_S4)? -1 : 0; /* if (nNeighMode==NEIGH_MODE_RING && at[i].valence==MAX_NUM_STEREO_ATOM_NEIGH) { AT_RANK other_neigh[2]; int n; for ( m = n = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { if ( at[i].neighbor[m] != neigh[j] && at[i].neighbor[m] != neigh[k] ) other_neigh[n++] = at[i].neighbor[m]; } if ( nSymmRank[(int)other_neigh[0]] == nSymmRank[(int)other_neigh[1]] ) bParitiesInverted = -1; } */ /* allow matching inverted centers only in case all equivalent neighbors in same ring system */ ret2 = 0; /* initilize. 1/8/2002 */ if ( 0 < (ret1 = CreateCheckSymmPaths( at, (AT_RANK)i, neigh[j], (AT_RANK)i, neigh[k], nAvoidCheckAtom, nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, &bParitiesInverted, mode ) ) && 0 < (ret2 = CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, nVisited1, nVisited2, (AT_RANK)MAX_ATOMS, (AT_RANK)i, neigh[j], neigh[k], nNeighMode, bParitiesInverted, mode, pCS, vABParityUnknown) ) ) { if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { /* possibly change the parity to unknown or undefined */ int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; if ( PARITY_ILL_DEF(at[i].stereo_atom_parity) && PARITY_VAL(at[i].stereo_atom_parity) > new_parity || PARITY_CALCULATE(at[i].stereo_atom_parity) ) { /* set new unknown or undefined parity */ at[i].stereo_atom_parity = (at[i].stereo_atom_parity ^ PARITY_VAL(at[i].stereo_atom_parity)) | PARITY_VAL(new_parity); at[i].parity = PARITY_VAL(new_parity); /* Remove from pCS */ nAtomRank1 = nCanonRank[i]; for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { pCS->LinearCTStereoCarb[n].parity = PARITY_VAL(new_parity); #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; #endif m = -1; break; } } if ( m >= 0 ) { ret = CT_STEREOCOUNT_ERR; /* */ goto exit_function; } ret ++; /* number of removed or set unknown/undefined parities */ } } else { RemoveOneStereoCenter( at, i /* atom number*/ ); /* Remove from pCS */ nAtomRank1 = nCanonRank[i]; for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ memmove( pCS->LinearCTStereoCarb + n, pCS->LinearCTStereoCarb + n + 1, (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); } pCS->nLenLinearCTStereoCarb --; #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; #endif m = -1; break; } } if ( m >= 0 ) { ret = CT_STEREOCOUNT_ERR; /* */ goto exit_function; } ret ++; /* number of removed or set unknown/undefined parities */ } } else { if ( !ret_failed ) { if ( ret1 < 0 ) { ret_failed = ret1; } else if ( ret2 < 0 ) { ret_failed = ret2; } } if ( !RETURNED_ERROR(ret_failed) ) { if ( RETURNED_ERROR( ret1 ) ) ret_failed = ret1; else if ( RETURNED_ERROR( ret2 ) ) ret_failed = ret2; } } } } } } if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { nNeighMode = NEIGH_MODE_RING; goto second_pass; } exit_function: return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret+1) : ret; } /**************************************************************************************/ int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, int vABParityUnknown) { NEIGH_LIST *nl = NULL, *nl1 = NULL, *nl2 = NULL; AT_RANK *nVisited1 = NULL, *nVisited2 = NULL, *nAtomNumberCanon1 = NULL, *nAtomNumberCanon2 = NULL; int nNumRemoved = 0, nTotRemoved = 0, ret = 0, ret1 = 0, ret2 = 0; if ( !AllocateForNonStereoRemoval( at, num_atoms, nSymmRank, nCanonRank, &nAtomNumberCanon1, &nAtomNumberCanon2, &nl, &nl1, &nl2, &nVisited1, &nVisited2 ) ) { return CT_OUT_OF_RAM; /* */ } do { nNumRemoved = 0; /* bonds */ ret = RemoveCalculatedNonStereoBondParities( at, num_atoms, num_at_tg, pRankStack1, pRankStack2, nTempRank, NeighList, nCanonRank, nSymmRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2, pCS, vABParityUnknown); if ( RETURNED_ERROR( ret ) ) { goto exit_function; } if ( ret < 0 ) { if ( ret < ret1 ) { /* */ ret1 = ret; } ret = - ( ret + 1 ); /* number of removed */ } nNumRemoved += ret; /* centers */ ret = RemoveCalculatedNonStereoCenterParities( at, num_atoms, num_at_tg, pRankStack1, pRankStack2, nTempRank, NeighList, nCanonRank, nSymmRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2, pCS, vABParityUnknown); if ( RETURNED_ERROR( ret ) ) { goto exit_function; } if ( ret < 0 ) { if ( ret < ret2 ) { /* */ ret2 = ret; } ret = - ( ret + 1 ); /* number of removed */ } nNumRemoved += ret; nTotRemoved += nNumRemoved; } while ( nNumRemoved ); if ( !RETURNED_ERROR( ret1 ) && !RETURNED_ERROR( ret2 ) ) { ret = inchi_min( ret1, ret2 ); ret = (ret >= 0)? nTotRemoved : -(1+nTotRemoved); } exit_function: DeAllocateForNonStereoRemoval( &nAtomNumberCanon1, &nAtomNumberCanon2, &nl, &nl1, &nl2, &nVisited1, &nVisited2 ); return ret; } #endif /* } REMOVE_CALC_NONSTEREO */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichimap4.c000066400000000000000000002526751271037650300234340ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #include "ichicomp.h" #define SB_DEPTH 6 /************************************************ map_stereo_bonds4 and map_stereo_atoms4 use the following members of CANON_STAT *pCS: pCS->bKeepSymmRank // ??? almost unused, replaced with nSymmStereo != NULL ??? pCS->bFirstCT pCS->bStereoIsBetter pCS->lNumNeighListIter pCS->lNumBreakTies pCS->lNumRejectedCT pCS->lNumTotCT pCS->lNumEqualCT pCS->lNumDecreasedCT pCS->bExtract (bRELEASE_VERSION == 0) pCS->ulTimeOutTime pCS->bRankUsedForStereo pCS->bAtomUsedForStereo pCS->LinearCTStereoDble pCS->LinearCTStereoCarb pCS->nLenLinearCTStereoCarb pCS->nLenLinearCTStereoDble pCS->nPrevAtomNumber ************************************************/ /********************************************************************************/ int map_stereo_bonds4 ( sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ AT_RANK *nCanonRankTo, /* output canonical stereo numbering*/ const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, AT_RANK *nTempRank, int nNumMappedRanksInput, AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, int vABParityUnknown) { int nTotSuccess = 0; /* 1=>full mapping has been completed; * 2=>obtained a better stereo; * 4=>restart (stereo bond or atom removed from the stereo CT) */ int tpos1; AT_STEREO_DBLE prevBond; tpos1 = CurTreeGetPos( cur_tree ); total_restart: if ( !nNumMappedBonds ) { memset( pCS->bRankUsedForStereo, 0, sizeof( pCS->bRankUsedForStereo[0] )*num_atoms ); SetUseAtomForStereo( pCS->bAtomUsedForStereo, at, num_atoms ); if ( pCS->bFirstCT && nSymmStereo && !pCS->bKeepSymmRank ) { int i; for ( i = 0; i < num_at_tg; i ++ ) { /* nSymmStereo[i] = min. {k | at[k] stereo eq. to at[i]} */ nSymmStereo[i] = i; /* for union-join to keep track of stereo-equivalent atoms */ } } } if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { int at_rank1, at_rank2, bStereoIsBetterWasSetHere; /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ AT_RANK canon_min1, canon_min2; int bFirstCanonRank; int i, j, j1, j2, at_from1, at_from2, at_to1, at_to2, iMax, c; int nStackPtr[SB_DEPTH], nNumMappedRanks[SB_DEPTH], LastMappedTo1; int istk, istk2, istk3, bAddStack, nNumAtTo1Success; int ret1, ret2, parity1, parity2; AT_RANK at_rank_canon1; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num1; */ /* canonical numbers of atoms */ AT_RANK at_rank_canon2; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num2; */ /* adjacent to the stereogenic bond */ int nNumChoices, nNumUnkn, nNumUndf, nNumBest, nNumWorse, nNumCalc, sb_parity_calc; int stereo_bond_parity, prev_stereo_bond_parity, pass, bAllParitiesIdentical, bAllParitiesIdentical2; AT_STEREO_DBLE prevBond2; prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; bFirstCanonRank=1; canon_min1=canon_min2=0; /* // find candidates for atom_from1, atom_to1; they must have identical mapping ranks at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; // rank "from" for mapping at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; // rank "from" for mapping */ if ( nNumMappedBonds ) { at_rank_canon1 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num1; at_rank_canon2 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num2; } else { at_rank_canon1 = 0; at_rank_canon2 = 0; } goto bypass_next_canon_ranks_check; next_canon_ranks: /* Save time: avoid calling Next_SB_At_CanonRanks2() */ if ( !pCS->bStereoIsBetter /* ??? && !pCS->bFirstCT ???*/ && at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && at_rank_canon2 >= pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) { if ( !nTotSuccess ) { pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; } CurTreeSetPos( cur_tree, tpos1 ); return nTotSuccess; } bypass_next_canon_ranks_check: CurTreeSetPos( cur_tree, tpos1 ); /* find next available canon. numbers for a stereogenic bond pair of atoms */ /* process allenes AFTER all double bonds and odd-number-of-double-bonds cumulenes */ if ( !(ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1, &at_rank_canon2, /* canonical numbers */ &canon_min1, &canon_min2, &bFirstCanonRank, pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, nCanonRankFrom, nAtomNumberCanonFrom, at, num_atoms, bAllene ) ) ) { /* failed to find next stereo bond to assign parity */ if ( !bAllene && bFirstCanonRank ) { /* all stereobond have been processed; try to find allene to continue */ AT_RANK at_rank_canon1_Allene = 0, canon_min1_Allene = 0; AT_RANK at_rank_canon2_Allene = 0, canon_min2_Allene = 0; if ( ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1_Allene, &at_rank_canon2_Allene, &canon_min1_Allene, &canon_min2_Allene, &bFirstCanonRank, pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, nCanonRankFrom, nAtomNumberCanonFrom, at, num_atoms, 1 ) ) { at_rank_canon1 = at_rank_canon1_Allene; at_rank_canon2 = at_rank_canon2_Allene; canon_min1 = canon_min1_Allene; canon_min2 = canon_min2_Allene; bAllene = 1; /* switch to allenes */ } } } if ( !ret1 || !pCS->bStereoIsBetter && (at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && at_rank_canon2 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) ) { /* new ranks provide greater pCS->LinearCTStereoDble[nNumMappedBonds] and therefore rejected */ if ( !nTotSuccess ) { pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; /* restore stereo bond CT for the current bond */ } return nTotSuccess; } /* current stereo bond initialization */ nNumChoices = 0; nNumUnkn = 0; nNumUndf = 0; nNumBest = 0; nNumWorse = 0; nNumCalc = 0; pass=0; prev_stereo_bond_parity = 0; at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; /* atom 1 rank "from" for mapping */ at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; /* atom 2 rank "from" for mapping */ /* we are going to map bond (at[at_from1], at[at_from2]) and canonical ranks of its atoms (at_rank_canon1, at_rank_canon2) onto a stereogenic bond (at[at_to1], at[at_to2]) */ iMax = at_rank1-1; /* test correctness: sorted pRankStack2[0][] and pRankStack1[0][] should have same ranks for both atoms */ if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] || at_rank2 != pRankStack2[0][pRankStack2[1][at_rank2-1]] ) { /* program error: "from" and "to" mapping ranks are not equal */ return CT_STEREOCOUNT_ERR; /* */ } /* -- do not check stereo features of "from" atoms: -- in case of "bond/charge isomerism" they may be missing. if ( !at[at_from1].stereo_bond_neighbor[0] || !at[at_from2].stereo_bond_neighbor[0] ) return CT_STEREOCOUNT_ERR; // program error */ /* find out if we have a choice in mapping: check all possible pairs (at_to1, at_to2) such that at_from1 is possibly constitutionally equivalent to at_to1, at_from2 to at_to2 */ for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { if ( !at[at_to1].stereo_bond_neighbor[0] ) continue; /* at_to1 does not belong to a stereo bond */ for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { at_to2 --; if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) continue; /* at_from2 cannot be mapped on at_to2 */ stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); i = 0; switch(stereo_bond_parity) { case AB_PARITY_UNDF: nNumUndf ++; break; /* 4 */ case AB_PARITY_UNKN: nNumUnkn ++; break; /* 3 (occurs if forced different to UNDF)*/ case BEST_PARITY: nNumBest ++; break; /* 1 */ case WORSE_PARITY: nNumWorse ++; break; /* 2 */ case AB_PARITY_CALC: nNumCalc ++; break; /* 6 */ case AB_PARITY_NONE: i ++; break; /* 0 */ } nNumChoices += !i; } } if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } if ( !nNumChoices ) { goto next_canon_ranks; } /* Determine the first parity to search */ sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; /* ============================================================== Search sequence: sb_parity_calc stereo_bond_parity ============================================================== BEST_PARITY (calc) BEST_PARITY BEST_PARITY BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY WORSE_PARITY (known) WORSE_PARITY 0 AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 if (sb_parity_calc==stereo_bond_parity) then "calc" else "known" */ repeat_all: if ( !nNumMappedBonds ) pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 7-13-2002 */ if ( !pass ++ ) { /* select the smallest (best) parity to search */ if ( sb_parity_calc ) { stereo_bond_parity = BEST_PARITY; } else { stereo_bond_parity = nNumBest? BEST_PARITY : nNumWorse? WORSE_PARITY : nNumUnkn? AB_PARITY_UNKN : nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; } } else { /* second pass: since the first pass failed, search for a worse result */ prev_stereo_bond_parity = stereo_bond_parity; i = NextStereoParity2Test( &stereo_bond_parity, &sb_parity_calc, nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, vABParityUnknown); switch ( i ) { case 0: break; /* obtained next parity to test */ case 1: goto next_canon_ranks; default: return i; /* program error */ } } if ( stereo_bond_parity == AB_PARITY_NONE ) { /* error? */ return CT_STEREOCOUNT_ERR; /* */ } /* check if the new requested parity is good (small) enough */ if ( !pCS->bStereoIsBetter ) { c = CompareLinCtStereoDoubleToValues( nTotSuccess? pCS->LinearCTStereoDble+nNumMappedBonds : &prevBond, at_rank_canon1, at_rank_canon2, (U_CHAR)stereo_bond_parity ); if ( c < 0 ) { if ( !nTotSuccess ) { pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; } CurTreeSetPos( cur_tree, tpos1 ); return nTotSuccess; } } bAllParitiesIdentical = 0; bAllParitiesIdentical2 = 0; LastMappedTo1 = -1; bStereoIsBetterWasSetHere = 0; istk = istk2 = istk3 = 0; if ( !nNumMappedBonds && prev_stereo_bond_parity != stereo_bond_parity ) pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 5-24-2002 */ if ( prev_stereo_bond_parity != stereo_bond_parity ) { CurTreeSetPos( cur_tree, tpos1 ); /* start over */ } /* Mapping: here at_rank1 = nRankTo, at_to1 = nAtomNumberTo */ for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { nNumAtTo1Success = 0; if ( !at[at_to1].stereo_bond_neighbor[0] ) continue; /* at_to1 does not belong to a stereo bond */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) { /* at_to1 is known to be stereogenically equivalent to another atom tried with at_rank_canon1 */ continue; } bAllParitiesIdentical2 = 0; for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { EQ_NEIGH EN1[2], EN2[2]; int bond_parity, num1, num2; AT_RANK at_rank_canon_n1, at_rank_canon_n2; at_to2 --; if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) continue; /* at_from2 cannot be mapped on at_to2 even without mapping at_from1 to at_to1 */ /* check whether the bond parity corresponds to the requested bond parity */ if ( PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]) ) { if ( stereo_bond_parity == sb_parity_calc ) { continue; /* requested parity to be calculated, found known parity */ } if ( stereo_bond_parity != PARITY_VAL(at[at_to1].stereo_bond_parity[j2]) ) { continue; /* parity differs from the requested parity */ } } else if ( PARITY_CALCULATE( at[at_to1].stereo_bond_parity[j2]) ) { if ( stereo_bond_parity != sb_parity_calc ) { continue; /* requested known parity, found parity to be calculated */ } } else { return CT_STEREOCOUNT_ERR; /* unknown parity type */ /* */ } /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ istk = 0; nStackPtr[0] = 0; nNumMappedRanks[0] = nNumMappedRanksInput; bAddStack = 0; bAllParitiesIdentical = ((at[at_to1].stereo_bond_parity[j2] & KNOWN_PARITIES_EQL )) && PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]); if ( !bAllParitiesIdentical && !nNumCalc && (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse )==3) { /* only one kind of bond parity is present; check whether all parities are really same */ bAllParitiesIdentical = All_SB_Same( at_rank_canon1, at_rank_canon2, /* canonical numbers */ pRankStack1, pRankStack2, nAtomNumberCanonFrom, at ); if ( bAllParitiesIdentical < 0 ) { return CT_STEREOCOUNT_ERR; /* */ } } /***************************************************************** * do the mapping only if parities are not same */ if ( !bAllParitiesIdentical ) { /* map atom 1 or reuse previous mapping */ if ( LastMappedTo1 != at_to1 ) { /* avoid repetitve mapping to the same first at_to1 using LastMappedTo1 variable */ /* map atom 1 */ ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], &bAddStack ); if ( RETURNED_ERROR(ret1) ) { return ret1; /* error */ } nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; LastMappedTo1 = at_to1; if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeAddRank( cur_tree, at_rank_canon1 ); } CurTreeAddAtom( cur_tree, at_to1 ); } } istk ++; /* = 1 */ /* check if we can map atom 2 */ if ( pRankStack1[nStackPtr[istk]][at_from2] != pRankStack2[nStackPtr[istk]][at_to2] ) { /* * This may happen when: * A) Charge/bond isomerism, for example cyclopentadiene(-), or * B) possibly stereogenic bond in an alternating ring has heighbors * in 2 symmetrically attached rings. * Such an alternating bond cannot be mapped on possibly stereogenic bond * that has neighbors belonging to 1 of the symmetrically attached rings only. * For example: * A---B---C---D If all atoms are Carbons then B, C, F, G are constitutionally * || || || || equivalent. However, bonds B-C, F-G are not equivalent to * || || || || B-F and C-G and cannot be mapped on them. * E---F---G---H If at_from1=B, at_from2=F, at_to1=B, then at_from2 cannot be mapped on at_to2=C * If at_from1=B, at_from2=F, at_to1=C, then at_from2 cannot be mapped on at_to2=B * etc. */ if ( sb_parity_calc != stereo_bond_parity) { /* can be passed only once for each bond */ nNumChoices --; nNumUndf -= (stereo_bond_parity == AB_PARITY_UNDF); nNumUnkn -= (stereo_bond_parity == AB_PARITY_UNKN); nNumBest -= (stereo_bond_parity == BEST_PARITY); nNumWorse-= (stereo_bond_parity == WORSE_PARITY); /* nNumCalc = nNumChoices - (nNumUndf + nNumUnkn + nNumBest + nNumWorse); */ } else if ( sb_parity_calc == BEST_PARITY ) { /* can be passed 2 times: for BEST_PARITY and WORSE_PARITY in this order */ nNumChoices --; /* do not repeate for WORSE_PARITY */ nNumCalc --; } continue; /* Happens for ID=80036,80253,91354,95532,101532,103788 */ } if ( nStackPtr[istk] > nStackPtr[istk-1] ) { bAllParitiesIdentical2 = All_SB_Same( at_rank_canon1, at_rank_canon2, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], nAtomNumberCanonFrom, at ); if ( bAllParitiesIdentical2 < 0 ) { return CT_STEREOBOND_ERROR; /* */ } } else { bAllParitiesIdentical2 = 0; } if ( bAllParitiesIdentical2 ) { /* do no mapping when all equivalent bonds have same parity */ /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ ClearPreviousMappings( pRankStack1+nStackPtr[istk]+2 ); } else { if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to2, nSymmStereo ) ) { continue; } /* map atom 2 */ ret2 = map_an_atom2( num_at_tg, num_max, at_from2, at_to2, nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], &bAddStack ); if ( RETURNED_ERROR(ret2) ) { return ret2; /* program error */ } nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; istk ++; /* = 2 */ if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { CurTreeAddRank( cur_tree, at_rank_canon2 ); } CurTreeAddAtom( cur_tree, at_to2 ); } } } else { /* do no mapping when all equivalent bonds have same parity */ /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ ClearPreviousMappings( pRankStack1+2 ); } /* we have a precalculated (known) bond parity */ /************************************************************ * * Known Bond Parity case: do not map stereo bond neighbors */ if ( stereo_bond_parity != sb_parity_calc ) /* parity is known */ { /* accept bond parity and do not map the neighbors */ bond_parity = stereo_bond_parity; /* same code as under " make a decision to accept current mapping" comment below */ /* with one exception: istk instead of istk3 */ c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); if ( c < 0 && !pCS->bStereoIsBetter ) { /* reject */ pCS->lNumRejectedCT ++; /* remove failed atom2 from the tree */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } continue; /* to next at_to2; Reject this at_to2: not a minimal CT. */ } else { /* accept */ if ( c > 0 && !pCS->bStereoIsBetter ) { /* bond entry is less than the previusly found */ pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; } pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; /* recursive call */ pCS->bRankUsedForStereo[at_from1] ++; pCS->bRankUsedForStereo[at_from2] ++; if ( !bAllParitiesIdentical ) { pCS->bAtomUsedForStereo[at_to1] --; pCS->bAtomUsedForStereo[at_to2] --; } ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], nTempRank, nNumMappedRanks[istk], nSymmStereo, NeighList, pCS, cur_tree, nNumMappedBonds+1 , vABParityUnknown); if ( !bAllParitiesIdentical ) { pCS->bAtomUsedForStereo[at_to1] ++; pCS->bAtomUsedForStereo[at_to2] ++; } pCS->bRankUsedForStereo[at_from1] --; pCS->bRankUsedForStereo[at_from2] --; if ( ret2 == 4 ) { if ( nNumMappedBonds ) { return ret2; } else { pCS->bFirstCT = 1; goto total_restart; } } if ( RETURNED_ERROR(ret2) ) { if ( ret2 == CT_TIMEOUT_ERR ) return ret2; else return ret2; /* program error */ } if ( ret2 > 0 ) { nTotSuccess |= 1; nNumAtTo1Success ++; if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ nTotSuccess |= 2; /* Obtained a smaller CT */ } } else { if ( bStereoIsBetterWasSetHere ) { /* rollback */ pCS->bStereoIsBetter = 0; pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; } /* remove failed atom2 from the tree */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } /* if ( 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveLastAtom( cur_tree, at_to1 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } */ } bStereoIsBetterWasSetHere = 0; } if ( bAllParitiesIdentical || bAllParitiesIdentical2 ) { break; /* j2 cycle, at_to2 (no need to repeat) */ } continue; /* to next at_to2 */ } /*************************************************************************** * * Unknown Bond Parity case: may need to map stereo bond neighbors * **************************************************************************** * Ranks are not known in advance * check if at_from1/at_to1 half-bond has neighbors with equal mapping ranks */ parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[0], nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); /* old approach -- before E/Z parities parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN1[0], nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); */ /* the following commented out statement is not needed here. */ /* parity2 = parity_of_mapped_atom2( at_from2, at_to2, at, &EN2[0], nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); */ if ( !parity1 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } num1 = parity1 > 0? 1:2; /* parity < 0 means additional mapping is needed to set parity */ /* --- try all possible mappings of the stereo bond ending atoms' neighbors --- */ at_rank_canon_n1 = 0; at_rank_canon_n2 = 0; for ( i = 0; i < num1; i ++ ) { int at_from_n1, at_to_n1, at_no_n1_num_success = 0; istk2 = istk; if ( num1 == 2 ) { at_rank_canon_n1 = nCanonRankFrom[EN1[0].from_at]; /* an additional neighbor mapping is necessary; */ /* we need to map only one at_from1 neighbor to make all neighbors have different ranks */ at_from_n1 = EN1[0].from_at; at_to_n1 = EN1[0].to_at[i]; if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n1, nSymmStereo ) ) continue; /* if ( nSymmStereo && !pCS->bFirstCT ) { if ( i && nSymmStereo[at_to_n1] == nSymmStereo[(int)EN1[0].to_at[0]] ) { continue; // do not test stereo equivalent atoms except the first one } } */ /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ ret1 = map_an_atom2( num_at_tg, num_max, at_from_n1, at_to_n1, nTempRank, nNumMappedRanks[istk2], &nNumMappedRanks[istk2+1], pCS, NeighList, pRankStack1+nStackPtr[istk2], pRankStack2+nStackPtr[istk2], &bAddStack ); if ( RETURNED_ERROR(ret1) ) { return ret1; /* program error */ /* */ } nStackPtr[istk2+1] = nStackPtr[istk2] + bAddStack; istk2 ++; /* <= 3 */ /* debug */ if ( istk2 >= SB_DEPTH ) { return CT_OVERFLOW; /* program error */ /* */ } if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { CurTreeAddRank( cur_tree, at_rank_canon_n1 ); } CurTreeAddAtom( cur_tree, at_to_n1 ); } /* now that all at_from1 neighbors have been mapped the parity must be defined */ parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[1], nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); if ( parity1 <= 0 ) return CT_STEREOCOUNT_ERR; /* program error */ /* */ } else { nNumMappedRanks[istk2+1] = nNumMappedRanks[istk2]; nStackPtr[istk2+1] = nStackPtr[istk2]; istk2 ++; /* <= 3 */ } /* check if at_from2/at_to2 half-bond has neighbors with equal mapping ranks */ parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[0], nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); if ( !parity2 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } num2 = parity2 > 0? 1:2; at_rank_canon_n2 = 0; for ( j = 0; j < num2; j ++ ) { int at_from_n2, at_to_n2; istk3 = istk2; if ( num2 == 2 ) { at_rank_canon_n2 = nCanonRankFrom[EN2[0].from_at]; /* we need to map only one at_from2 neighbor to make its neighbors have different ranks */ at_from_n2 = EN2[0].from_at; at_to_n2 = EN2[0].to_at[j]; if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n2, nSymmStereo ) ) continue; /* if ( nSymmStereo && !pCS->bFirstCT ) { if ( j && nSymmStereo[at_to_n2] == nSymmStereo[(int)EN2[0].to_at[0]] ) { continue; // do not test stereo equivalent atoms except the first one } } */ /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ ret1 = map_an_atom2( num_at_tg, num_max, at_from_n2, at_to_n2, nTempRank, nNumMappedRanks[istk3], &nNumMappedRanks[istk3+1], pCS, NeighList, pRankStack1+nStackPtr[istk3], pRankStack2+nStackPtr[istk3], &bAddStack ); if ( RETURNED_ERROR(ret1) ) { return ret1; /* program error */ } nStackPtr[istk3+1] = nStackPtr[istk3]+bAddStack; istk3 ++; /* <= 4 */ if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { CurTreeAddRank( cur_tree, at_rank_canon_n2 ); } CurTreeAddAtom( cur_tree, at_to_n2 ); } parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[1], nCanonRankFrom, pRankStack1[nStackPtr[istk3]], pRankStack2[nStackPtr[istk3]] ); if ( parity2 <= 0 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } } else { /* no additional mapping is needed to set atom's parity */ nNumMappedRanks[istk3+1] = nNumMappedRanks[istk3]; nStackPtr[istk3+1] = nStackPtr[istk3]; istk3 ++; /* <= 4 */ } /******************************************************************* * at this point the stereo bond is fully mapped to find its parity *******************************************************************/ if ( parity1 <= 0 || parity2 <= 0 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } /* find current bond parity AB_PARITY_ODD */ if ( ATOM_PARITY_WELL_DEF(parity1) && ATOM_PARITY_WELL_DEF(parity2) ) { bond_parity = 2 - (parity1 + parity2)%2; } else { bond_parity = inchi_max(parity1, parity2); } if ( ATOM_PARITY_WELL_DEF(bond_parity) && at[at_to1].stereo_bond_z_prod[j2] < 0 ) bond_parity = 2 - (bond_parity+1)%2; /* invert the bond parity */ /******************************************************** * make a decision whether to accept the current mapping */ c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); if ( sb_parity_calc != bond_parity || c < 0 && !pCS->bStereoIsBetter ) { /* reject */ pCS->lNumRejectedCT ++; /* remove failed atom2 from the tree */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } continue; /* Reject: not a minimal CT. */ } else { /* try to accept */ if ( c > 0 && !pCS->bStereoIsBetter ) { /* bond_parity is less than the previusly found */ pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; } /* accept */ pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; /* recursive call */ pCS->bRankUsedForStereo[at_from1] ++; pCS->bRankUsedForStereo[at_from2] ++; pCS->bAtomUsedForStereo[at_to1] --; pCS->bAtomUsedForStereo[at_to2] --; ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, nSymmRank, pRankStack1+nStackPtr[istk3], pRankStack2+nStackPtr[istk3], nTempRank, nNumMappedRanks[istk3], nSymmStereo, NeighList, pCS, cur_tree, nNumMappedBonds+1 , vABParityUnknown); pCS->bRankUsedForStereo[at_from1] --; pCS->bRankUsedForStereo[at_from2] --; pCS->bAtomUsedForStereo[at_to1] ++; pCS->bAtomUsedForStereo[at_to2] ++; if ( ret2 == 4 ) { if ( nNumMappedBonds ) { return ret2; } else { pCS->bFirstCT = 1; goto total_restart; } } if ( RETURNED_ERROR(ret2) ) { if ( ret2 == CT_TIMEOUT_ERR ) return ret2; else return ret2; /* program error */ } if ( ret2 > 0 ) { nTotSuccess |= 1; nNumAtTo1Success ++; if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ nTotSuccess |= 2; /* Obtained a smaller CT */ } at_no_n1_num_success ++; } else { if ( bStereoIsBetterWasSetHere ) { /* rollback */ pCS->bStereoIsBetter = 0; pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; } if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } } bStereoIsBetterWasSetHere = 0; } } /* end choices in mapping neighbors of the 2nd half-bond */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { CurTreeRemoveLastRank( cur_tree ); } /* added 2006-07-20 */ if ( !at_no_n1_num_success && tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to_n1 ); } } /* end choices in mapping neighbors of the 1st half-bond */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { CurTreeRemoveLastRank( cur_tree ); } } /* end of choices in mapping at_from2 */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { CurTreeRemoveLastRank( cur_tree ); } if ( !nNumAtTo1Success ) { if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } } if ( bAllParitiesIdentical /*&& !nSymmStereo*/ ) { break; } } /* end of choices in mapping at_from1 */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveLastRank( cur_tree ); } else /* CurTree consistecy check (debug only) */ if ( tpos1 != CurTreeGetPos( cur_tree ) ) { return CT_STEREOCOUNT_ERR; /* */ } if ( !nTotSuccess || stereo_bond_parity == sb_parity_calc ) { goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ } /* Previously the control flow never came here... */ if ( !nTotSuccess ) { pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; CurTreeSetPos( cur_tree, tpos1 ); /* Occurs when atoms are not really equvalent ( -O= without positive charge in "aromatic" ring) */ return 0; /* Happens for ID=92439,100318,100319 when EXCL_ALL_AROM_BOND_PARITY=0 and * nNumChoices=0. * Results from impossible previous mapping of symmetric relatively * to a central ring aromatic circles while central ring is not symmetrical due to * alternate bonds (in the central ring number of pi-electrons, atoms and bonds * are symmetrical). * Does not happen when alternate bonds of the central ring * are treated as aromatic by attaching a (+) charge to the oxygen. */ } } else { int ret; if ( !nNumMappedBonds ) { pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT has not been processed yet */ } if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; } /* all stereo bonds have been mapped; now start processing stereo atoms... */ ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumMappedRanksInput, nSymmStereo, NeighList, pCS, cur_tree, 0 , vABParityUnknown); if ( ret == 4 ) { if ( nNumMappedBonds ) { return ret; } else { pCS->bFirstCT = 1; goto total_restart; } } if ( RETURNED_ERROR(ret) ) { if ( ret == CT_TIMEOUT_ERR ) return ret; else return ret; /* program error */ } if ( ret > 0 ) { nTotSuccess |= 1; if ( ret & 2 ) { CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ nTotSuccess |= 2; /* Obtained a smaller CT */ } } } if ( !nTotSuccess && pCS->nLenLinearCTStereoDble && nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; } return nTotSuccess; /* ok */ } /**************************************************************************************** */ int map_stereo_atoms4 ( sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, AT_RANK *nTempRank, int nNumMappedRanksInput, AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms , int vABParityUnknown ) { /* * Do not check whether "from" atoms have any stereo features. */ int nTotSuccess = 0; AT_STEREO_CARB prevAtom; int tpos1; tpos1 = CurTreeGetPos( cur_tree ); if ( nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ int j1, at_from1, at_to1, /*at_from2, at_to2,*/ iMax, lvl, bStereoIsBetterWasSetHere; int istk, istk2, bAddStack, nNumAtTo1Success, c, bFirstTime=1, bAllParitiesIdentical; EQ_NEIGH EN[5], *pEN; int nStackPtr[5], nMappedRanks[5], j[5], *nSP, *nMR, bLastLvlFailed; AT_RANK at_rank_canon1, cr[5], at_to[5]; AT_RANK canon_rank1_min = 0; int at_rank1; /* rank for mapping */ int nNumChoices, nNumUnkn, nNumUndf, nNumWorse, nNumBest, nNumCalc; int stereo_center_parity, prev_stereo_center_parity, sb_parity_calc, pass; AT_STEREO_CARB prevAtom2; prevAtom = pCS->LinearCTStereoCarb[nNumMappedAtoms]; /* save to restore in case of failure */ at_rank_canon1 = nNumMappedAtoms? pCS->LinearCTStereoCarb[nNumMappedAtoms-1].at_num:0; goto bypass_next_canon_rank_check; next_canon_rank: if ( !pCS->bStereoIsBetter /*??? && !pCS->bFirstCT ???*/ && at_rank_canon1 >= pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { /* cannot find next available canonical number */ if ( !nTotSuccess ) { pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ } CurTreeSetPos( cur_tree, tpos1 ); return nTotSuccess; } bypass_next_canon_rank_check: CurTreeSetPos( cur_tree, tpos1 ); /* find next available canon. number for a stereogenic atom */ if ( !Next_SC_At_CanonRank2( &at_rank_canon1, &canon_rank1_min, &bFirstTime, pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, nAtomNumberCanonFrom, num_atoms ) || !pCS->bStereoIsBetter && at_rank_canon1 > pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { /* cannot find next available canonical number */ if ( !nTotSuccess ) { pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ } return nTotSuccess; } nNumChoices = 0; nNumUnkn = 0; nNumUndf = 0; nNumBest = 0; nNumWorse = 0; nNumCalc = 0; pass = 0; prev_stereo_center_parity = 0; /* get mapping rank for the canon. number */ at_rank1 = pRankStack1[0][at_from1=(int)nAtomNumberCanonFrom[at_rank_canon1 - 1]]; iMax = at_rank1-1; /* for debug only */ if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] ) return CT_STEREOCOUNT_ERR; /* program error */ /* */ /* count special parities of the not mapped yet "to" atoms */ for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { if ( !at[at_to1].stereo_bond_neighbor[0] && pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) { int no_choice = 0; stereo_center_parity = PARITY_VAL(at[at_to1].stereo_atom_parity); switch(stereo_center_parity) { case AB_PARITY_UNDF: nNumUndf ++; break; /* 4 */ case AB_PARITY_UNKN: nNumUnkn ++; break; /* 3 */ case BEST_PARITY: nNumBest ++; break; /* 1 */ case WORSE_PARITY: nNumWorse ++; break; /* 2 */ case AB_PARITY_CALC: nNumCalc ++; break; case AB_PARITY_NONE: no_choice ++; break; /* 0 */ } nNumChoices += !no_choice; } } if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } if ( !nNumChoices ) { goto next_canon_rank; } /* Determine the first parity to search */ sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; /* ============================================================== Search sequence: sb_parity_calc stereo_center_parity ============================================================== BEST_PARITY (calc) BEST_PARITY BEST_PARITY BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY WORSE_PARITY (known) WORSE_PARITY 0 AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 if (sb_parity_calc==stereo_center_parity) then "calc" else "known" */ repeat_all: if ( !pass ++ ) { /* select the smallest parity to search */ if ( sb_parity_calc ) { stereo_center_parity = BEST_PARITY; } else { stereo_center_parity = nNumBest? BEST_PARITY : nNumWorse? WORSE_PARITY : nNumUnkn? AB_PARITY_UNKN : nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; } } else { prev_stereo_center_parity = stereo_center_parity; j1 = NextStereoParity2Test( &stereo_center_parity, &sb_parity_calc, nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, vABParityUnknown ); switch ( j1 ) { case 0: break; /* obtained next parity to test */ case 1: goto next_canon_rank; default: return j1; /* program error */ } } if ( stereo_center_parity == AB_PARITY_NONE ) { /* error? */ return CT_STEREOCOUNT_ERR; /* */ } /* check if the new requested parity is small enough */ if ( !pCS->bStereoIsBetter ) { c = CompareLinCtStereoAtomToValues( nTotSuccess? pCS->LinearCTStereoCarb+nNumMappedAtoms : &prevAtom, at_rank_canon1, (U_CHAR)stereo_center_parity ); if ( c < 0 ) { if ( !nTotSuccess ) { pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; } CurTreeSetPos( cur_tree, tpos1 ); return nTotSuccess; } } bAllParitiesIdentical = 0; bStereoIsBetterWasSetHere = 0; istk = istk2 = 0; CurTreeSetPos( cur_tree, tpos1 ); /* start over */ /* if ( prev_stereo_center_parity != stereo_center_parity ) { CurTreeSetPos( cur_tree, tpos1 ); } */ /* nRankTo nAtomNumberTo */ for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { int ret, ret1, ret2, parity1; nNumAtTo1Success = 0; /* if ( !(at[at_to1].stereo_atom_parity && !at[at_to1].stereo_bond_neighbor[0] && pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) ) */ if ( !at[at_to1].stereo_atom_parity || at[at_to1].stereo_bond_neighbor[0] || pCS->bAtomUsedForStereo[at_to1] != STEREO_AT_MARK ) /* simplify 12-17-2003 */ continue; /* Do not map on non-stereogenic atom constitutionally * equivalent to a steregenic atom. Here * at[at_to1] is not a sterereo center; | | * bonds tautomerism is a usual cause. -P(+)-CH=P- * For example, consider a fragment: | | * The two atoms P may be constitutionally * equivalent, P(+) may be seen as a stereocenter * while another P has a double bond (Now such a P(V) IS a stereocenter). */ /* check whether the stereocenter parity corresponds to the requested stereocenter parity */ if ( PARITY_KNOWN(at[at_to1].stereo_atom_parity) ) { if ( stereo_center_parity == sb_parity_calc ) { continue; /* requested parity to be calculated, found known parity */ } if ( stereo_center_parity != PARITY_VAL(at[at_to1].stereo_atom_parity) ) { continue; /* parity differs from the requested parity */ } } else if ( PARITY_CALCULATE( at[at_to1].stereo_atom_parity) ) { if ( stereo_center_parity != sb_parity_calc ) { continue; /* requested known parity, found patity to be calculated */ } } else { return CT_STEREOCOUNT_ERR; /* unknown parity type */ } bAllParitiesIdentical = (( at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && PARITY_KNOWN(at[at_to1].stereo_atom_parity)); if ( !bAllParitiesIdentical && !nNumCalc && (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse)==3 ) { /* only one kind of stereocenter parity is present; check whether all parities are really same */ bAllParitiesIdentical = All_SC_Same( at_rank_canon1, /* canonical number */ pRankStack1, pRankStack2, nAtomNumberCanonFrom, at ); if ( bAllParitiesIdentical < 0 ) { return CT_STEREOCOUNT_ERR; } } if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) continue; /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ istk = 0; nStackPtr[istk] = 0; nMappedRanks[istk] = nNumMappedRanksInput; bAddStack = 0; /* if all equivalent atoms have same known parity, do not map any of them here */ if ( !bAllParitiesIdentical ) { /* map the central atom */ /* this mapping is always possible */ ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, nTempRank, nMappedRanks[istk], &nMappedRanks[istk+1], pCS, NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], &bAddStack ); if ( RETURNED_ERROR(ret1) ) { return ret1; /* error */ } nStackPtr[istk+1] = nStackPtr[istk] + bAddStack; istk ++; } else { ClearPreviousMappings( pRankStack1+2 ); /* precaution */ } /********************************************************************************* * * Unknown Stereocenter Parity case: possibly need to map stereo center neighbors */ if ( stereo_center_parity == sb_parity_calc ) { /* find out the parity */ parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); /* if parity is well-defined then returned EN[istk].num_to=0 */ if ( !parity1 ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } if ( !EN[istk].num_to && parity1 != sb_parity_calc ) { continue; /* looking for the parity value = sb_parity_calc */ } } else { /* Known parity */ parity1 = stereo_center_parity; EN[istk].num_to = 0; } /*********************************************************************** * no need to map the neighbors: parity is known or has been calculated */ if ( stereo_center_parity == sb_parity_calc && !EN[istk].num_to || /* now well-defined, but unknown in advance atom parity OR */ stereo_center_parity != sb_parity_calc ) /* known in advance parity = stereo_center_parity */ { /* do not need to map the neighbors */ c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, at_rank_canon1, (U_CHAR)parity1 ); if ( c < 0 && !pCS->bStereoIsBetter ) { /* reject */ pCS->lNumRejectedCT ++; continue; /* Reject: not a minimal CT. Should not happen */ } else { /* accept */ if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeAddRank( cur_tree, at_rank_canon1 ); } CurTreeAddAtom( cur_tree, at_to1 ); } if ( c > 0 && !pCS->bStereoIsBetter ) { /* stereo center entry is less than the previusly found */ pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; } pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; pCS->bRankUsedForStereo[at_from1] = 3; #if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) if ( !bAllParitiesIdentical ) #endif pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], nTempRank, nMappedRanks[istk], nSymmStereo, NeighList, pCS, cur_tree, nNumMappedAtoms+1 , vABParityUnknown); pCS->bRankUsedForStereo[at_from1] = 0; #if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) if ( !bAllParitiesIdentical ) #endif pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; if ( ret == 4 ) { return ret; } if ( RETURNED_ERROR(ret) ) { if ( ret == CT_TIMEOUT_ERR ) return ret; else return ret; /* program error */ } if ( ret > 0 ) { nTotSuccess |= 1; nNumAtTo1Success ++; if ( bStereoIsBetterWasSetHere || (ret & 2) ) { CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ nTotSuccess |= 2; /* Obtained a smaller CT */ } } else { if ( bStereoIsBetterWasSetHere ) { pCS->bStereoIsBetter = 0; pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; } /* remove failed atom1 from the tree */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } } bStereoIsBetterWasSetHere = 0; } /* if ( (at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && ATOM_PARITY_KNOWN(stereo_center_parity) && !nSymmStereo ) { // ??? add && !nSymmStereo ??? break; // do not repeat for the same kind of stereo atom with the parity known in advance } */ if ( bAllParitiesIdentical ) { break; /* do not repeat for the same kind of stereo atom with the parity known in advance */ } continue; } /*************************************************** * * Need to map the neighbors */ if ( stereo_center_parity != sb_parity_calc ) { return CT_STEREOCOUNT_ERR; /* program error */ /* */ } /* -- has already been calculated -- parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); */ if ( !parity1 ) { return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ } if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeAddRank( cur_tree, at_rank_canon1 ); } CurTreeAddAtom( cur_tree, at_to1 ); } /****************************************************** * Need to fix the neighbors to define the atom parity ******************************************************/ /* a recursion replaced with the hand-made stack */ lvl = 0; /* the "recursion" depth level */ nSP = &nStackPtr[istk]; nMR = &nMappedRanks[istk]; pEN = &EN[istk]; bLastLvlFailed = 0; /* entering "recursion" depth level lvl */ next_lvl: if ( pEN[lvl].num_to ) { /* Found tied neighbors. Try all transpositions of the tied neighbors. * j is a number of the "to" tied neighbor in the pEN[lvl].to_at[*] to * which the pEN[lvl].from_at "from" neighbor's canonical number is mapped */ j[lvl] = 0; next_j: cr[lvl] = nCanonRankFrom[pEN[lvl].from_at]; at_to[lvl] = pEN[lvl].to_at[j[lvl]]; if ( j[lvl] && tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) && 1 == CurTreeIsLastAtomEqu( cur_tree, at_to[lvl], nSymmStereo ) ) { lvl ++; bLastLvlFailed = 0; goto backup; /* do not test stereo equivalent atoms except the first one */ } ret2 = map_an_atom2( num_at_tg, num_max, pEN[lvl].from_at, /* from */ pEN[lvl].to_at[j[lvl]], /* to */ nTempRank, nMR[lvl], &nMR[lvl+1], pCS, NeighList, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], &bAddStack ); if ( RETURNED_ERROR(ret2) ) { return ret2; /* program error */ } /* next recursion depth level */ if ( bAddStack ) { if ( tpos1 == CurTreeGetPos( cur_tree ) || 0 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { CurTreeAddRank( cur_tree, cr[lvl] ); } CurTreeAddAtom( cur_tree, at_to[lvl] ); } nSP[lvl+1] = nSP[lvl] + bAddStack; lvl ++; /* upon increment lvl = number of additionally mapped neighbors * (entering next recursion level) */ /* check if the mapping has defined the parity */ parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &pEN[lvl], nCanonRankFrom, pRankStack1[nSP[lvl]], pRankStack2[nSP[lvl]] ); if ( !parity1 ) { return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ } if ( parity1 < 0 ) { goto next_lvl; /* we need at least one more mapping to define the parity */ } /********************************************************** * * Check the parity * ********************************************************** * make a decision whether to accept the current mapping */ c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, at_rank_canon1, (U_CHAR)parity1 ); if ( sb_parity_calc != parity1 || c < 0 && !pCS->bStereoIsBetter ) { pCS->lNumRejectedCT ++; bLastLvlFailed = 1; } else /* the parity has been defined (all neighbors have untied ranks) */ /* if ( bAcceptAllParities || parity1 == BEST_PARITY ) */ { /********************************************************************* * * Process the parity here. We are at the top of the recursion stack. * *********************************************************************/ /* try to accept current neighbors mapping */ if ( c > 0 && !pCS->bStereoIsBetter ) { pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; } pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; pCS->bRankUsedForStereo[at_from1] = 3; pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, nSymmRank, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], nTempRank, nMR[lvl], nSymmStereo, NeighList, pCS, cur_tree, nNumMappedAtoms+1 , vABParityUnknown ); pCS->bRankUsedForStereo[at_from1] = 0; pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; if ( ret == 4 ) { return ret; } if ( RETURNED_ERROR(ret) ) { if ( ret == CT_TIMEOUT_ERR ) return ret; else return ret; /* program error */ } if ( ret > 0 ) { nTotSuccess |= 1; nNumAtTo1Success ++; if ( bStereoIsBetterWasSetHere || (ret & 2) ) { CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ nTotSuccess |= 2; /* Obtained a smaller CT */ } } else { if ( bStereoIsBetterWasSetHere ) { pCS->bStereoIsBetter = 0; pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; } bLastLvlFailed = 1; } bStereoIsBetterWasSetHere = 0; /* avoid redundant repetitions: */ /* check if neighbors mappings have altered another stereo center parity */ if ( !nSymmStereo && !might_change_other_atom_parity( at, num_atoms, at_to1, pRankStack2[nSP[lvl]] /* ranks after neigbors mapping */, pRankStack2[nStackPtr[istk]] /* ranks before the mapping neighbors */) ) { goto done; } } /* Continue the cycle. Go to the previous "recursion" level */ backup: while (lvl -- > 0 ) { j[lvl] ++; /* next neighbor at this level */ if ( j[lvl] < pEN[lvl].num_to ) { if ( bLastLvlFailed ) { if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to[lvl] ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } bLastLvlFailed = 0; } /* Done with this level. Go back one level */ goto next_j; } /* remove failed atom from the tree */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { CurTreeRemoveLastRank( cur_tree ); } } goto done; } else { cr[lvl] = 0; } done:; /* at this point lvl=0. */ if ( !nNumAtTo1Success ) { if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); CurTreeRemoveLastRankIfNoAtoms( cur_tree ); } } } /* end of stereo atom mapping cycle */ if ( tpos1 < CurTreeGetPos( cur_tree ) && 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { CurTreeRemoveLastRank( cur_tree ); } else /* CurTree consistency check (debug only) */ if ( tpos1 != CurTreeGetPos( cur_tree ) ) { return CT_STEREOCOUNT_ERR; /* */ } if ( !nTotSuccess || stereo_center_parity == sb_parity_calc ) { goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ } } else { /**************************************************** * * All stereogenic atoms and bonds have been mapped * ****************************************************/ if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || ConsoleQuit && (*ConsoleQuit)() ) { return CT_USER_QUIT_ERR; } if ( pCS->bStereoIsBetter || pCS->bFirstCT ) { /* All stereo atoms have been mapped. Current stereo name is better than all previous. * Create new numbering for the new CT * break all remaining "from" ties */ int i1, ret; AT_RANK rc, n1, n2; ret=BreakAllTies( num_at_tg, num_max, pRankStack1, NeighList, nTempRank, pCS); if ( RETURNED_ERROR( ret ) ) { return ret; } /* break all remaining "from" ties */ ret=BreakAllTies( num_at_tg, num_max, pRankStack2, NeighList, nTempRank, pCS); if ( RETURNED_ERROR( ret ) ) { return ret; } /* move stack pointers to the "nAtomNumber[*]" after all ties are broken */ pRankStack1 += 2; pRankStack2 += 2; /* Now final mapping ranks of "to" atom (*pRankStack2)[i] and "from" atom (*pRankStack1)[i] * are equal and all ranks are different, that is, we have a full mapping * Copy so far best canonical numbering from "from" to "to". */ memset( pCS->nPrevAtomNumber, 0, num_at_tg*sizeof(pCS->nPrevAtomNumber[0]) ); for ( i1 = 0; i1 < num_at_tg; i1 ++ ) { n1 = pRankStack1[1][i1]; rc = nCanonRankFrom[n1]; /* new canon. rank */ n2 = pRankStack2[1][i1]; /* orig. atom number */ nCanonRankTo[n2] = rc; /* assign new canon. number to the atom */ /* use this array to find stereo-equivalent atoms */ pCS->nPrevAtomNumber[rc-1] = n2; /* ord. number of the atom having canon. rank = rc */ nSymmStereo[i1] = i1; /* restart search for stereo equivalent atoms */ /* check mapping correctness */ if ( pRankStack1[0][n1] != pRankStack2[0][n2] || nSymmRank[n1] != nSymmRank[n2] ) { return CT_STEREO_CANON_ERR; /* stereo mapping error */ } } /* statistics */ pCS->lNumTotCT ++; pCS->lNumEqualCT = 1; pCS->lNumDecreasedCT ++; pCS->bStereoIsBetter = 0; /* prepare to start over */ nTotSuccess = 1; pCS->bFirstCT = 0; #if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ if ( !(pCS->nMode & CMODE_REDNDNT_STEREO ) ) { i1 = RemoveCalculatedNonStereo( at, num_atoms, num_at_tg, pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRankTo, pCS->nPrevAtomNumber, pCS, vABParityUnknown); if ( RETURNED_ERROR( i1 ) ) { return i1; } if ( i1 < 0 ) { #if ( bRELEASE_VERSION == 0 ) pCS->bExtract |= EXTR_REMOVE_PARITY_WARNING; #endif i1 = -(1+i1); } if ( i1 > 0 ) { return 4; /* total restart: due to newly found stereo equivalence */ /* the length of the stereo CT has changed */ } } #endif /* } REMOVE_CALC_NONSTEREO */ pRankStack1 -= 2; pRankStack2 -= 2; } else { /* current stereo name is same as previous. We do not need a full mapping. */ if ( nSymmStereo ) { int num_changes = 0; AT_RANK r, n1, n2, r_max, cr; r_max = (AT_RANK)num_at_tg; for ( r = 1; r <= r_max; r ++ ) { if ( bUniqueAtNbrFromMappingRank( pRankStack1, r, &n1 ) ) { if ( bUniqueAtNbrFromMappingRank( pRankStack2, r, &n2 ) ) { /* atoms at[n1], at[n2] have identical untied mapping rank r */ cr = nCanonRankFrom[(int)n1]-1; /* (new at[n2] canonical rank)-1 */ /* pCS->nPrevAtomNumber[(int)cr] = */ /* previous ordering number of an atom with the canon. rank = cr+1; */ /* make this atom equivalent to atom at[n2]: */ num_changes += nJoin2Mcrs( nSymmStereo, pCS->nPrevAtomNumber[(int)cr], n2 ); } else { return CT_MAPCOUNT_ERR; /* mapping ranks must be either both tied or untied. */ /* */ } } } if ( num_changes ) { /* compress trees to stars */ for ( r = r_max-1; r; r -- ) { nGetMcr( nSymmStereo, r ); } } } /* statistics */ pCS->lNumEqualCT ++; pCS->lNumTotCT ++; nTotSuccess = 1; } if ( bInchiTimeIsOver( pCS->ulTimeOutTime ) ) { return CT_TIMEOUT_ERR; } } if ( !nTotSuccess && nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; CurTreeSetPos( cur_tree, tpos1 ); } return nTotSuccess; /* return to the previous level of the recursion. */ } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichinorm.c000066400000000000000000004250111271037650300235300ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "inpdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichierr.h" #include "util.h" #include "ichicomp.h" #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* defined in ichisort.c, prototype in ichicomn.h */ int insertions_sort_AT_RANK( AT_RANK *base, int num ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* local prototypes */ int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ); /********************************************************************************/ int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ) { int ret = (int)((const inp_ATOM*)a1)->iso_atw_diff - (int)((const inp_ATOM*)a2)->iso_atw_diff; if ( !ret ) /* make the sort stable */ ret = (int)((const inp_ATOM*)a1)->component - (int)((const inp_ATOM*)a2)->component; return ret; } typedef struct tagTreeAtom { AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ S_CHAR valence; /* number of bonds = number of neighbors */ AT_NUMB nRingSystem; AT_NUMB nBlockSystem; S_CHAR bCutVertex; } tre_ATOM; #if ( FIND_RING_SYSTEMS == 1 ) /* { */ /********************************************************************************/ int MarkRingSystemsInp( inp_ATOM *at, int num_atoms, int start ) { AT_NUMB *nStackAtom = NULL; int nTopStackAtom=-1; AT_NUMB *nRingStack = NULL; int nTopRingStack=-1; /* was AT_NUMB */ AT_NUMB *nDfsNumber = NULL; AT_NUMB *nLowNumber = NULL; S_CHAR *cNeighNumb = NULL; AT_NUMB nDfs; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) AT_NUMB nRs, *nRsConnect = NULL; int k; AT_NUMB *tree = NULL; int nNumConnect, nMaxNumConnect, nLenConnect; #endif AT_NUMB nNumAtInRingSystem; int i, j, u, /*start,*/ nNumRingSystems, nNumStartChildren; /* allocate arrays */ nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) nRsConnect = (AT_NUMB *)inchi_calloc(3*num_atoms+3,sizeof(nRsConnect[0])); #endif /* check allocation */ if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !cNeighNumb #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) || !nRsConnect #endif ) { nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ goto exit_function; } /******************************************** * * Find Cut-vertices & Blocks * ********************************************/ /* initiation */ /*start = 0;*/ nNumRingSystems = 0; u = start; /* start atom */ nDfs = 0; nTopStackAtom =-1; nTopRingStack =-1; memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nLowNumber[u] = nDfsNumber[u] = ++nDfs; nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nNumStartChildren = 0; do { /* advance */ advance_block: /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ /* replaced due to missing sequence point */ if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) { cNeighNumb[i] ++; u = (int)at[i].neighbor[j]; if ( !nDfsNumber[u] ) { /* tree edge, 1st visit -- advance */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nLowNumber[u] = nDfsNumber[u] = ++nDfs; nNumStartChildren += (i == start); } else if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ /* back edge: u is not a predecessor of i */ if ( nDfsNumber[u] < nDfsNumber[i] ) { /* Back edge, 1st visit: u is an ancestor of i. Compare */ if ( nLowNumber[i] > nDfsNumber[u] ) { nLowNumber[i] = nDfsNumber[u]; } } } /* may comment out ? */ goto advance_block; } else { cNeighNumb[i] = 0; } /* back up */ if ( i != start ) { u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ if ( nLowNumber[i] >= nDfsNumber[u] ) { /* output the block */ nNumRingSystems ++; at[u].nBlockSystem = nNumRingSystems; if ( u != start || nNumStartChildren > 1 ) { at[u].bCutVertex += 1; } while ( nTopRingStack >= 0 ) { j = nRingStack[nTopRingStack--]; at[j].nBlockSystem = nNumRingSystems; /* mark the atom */ if ( i == j ) { break; } } } else if ( nLowNumber[u] > nLowNumber[i] ) { /* inherit */ nLowNumber[u] = nLowNumber[i]; } } } while ( --nTopStackAtom >= 0 ); /******************************************** * * Find Ring Systems * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. * ********************************************/ /* initiation */ /*start = 0;*/ nNumRingSystems = 0; u = start; /* start atom */ nDfs = 0; nTopStackAtom =-1; nTopRingStack =-1; memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); /* push the start atom on the stack */ nLowNumber[u] = nDfsNumber[u] = ++nDfs; nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) nNumConnect = nLenConnect = nMaxNumConnect = 0; #endif do { /* advance */ advance_ring: /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ /* replaced due to missing sequence point */ if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) { cNeighNumb[i] ++; u = (int)at[i].neighbor[j]; if ( !nDfsNumber[u] ) { /* tree edge, 1st visit -- advance */ nStackAtom[++nTopStackAtom] = (AT_NUMB)u; nRingStack[++nTopRingStack] = (AT_NUMB)u; nLowNumber[u] = nDfsNumber[u] = ++nDfs; } else if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* back edge: u is not a predecessor of i */ if ( nDfsNumber[u] < nDfsNumber[i] ) { /* Back edge, 1st visit: u is ancestor of i. Compare */ if ( nLowNumber[i] > nDfsNumber[u] ) { nLowNumber[i] = nDfsNumber[u]; } } } goto advance_ring; } else { cNeighNumb[i] = 0; } /* back up */ if ( nDfsNumber[i] == nLowNumber[i] ) { /* found a ring system */ nNumRingSystems ++; /* unwind nRingStack[] down to i */ #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) nNumConnect = 2; /* data structure: for each ring system nRsConnect[] contains: * 1) nNumConnect+1 = (number of already discovered neighboring "ring systems" + 1)+1 * 2) nNumAtInRingSystem * 3) (nNumConnect-1) numbers (IDs) of neighboring ring systems. * BFS guarantees that each neighboring ring system is encountered only one time * Number of all neighboring ring systems = (nNumConnect-1)+1 = nNumConnect * (One additional ring system is where the BFS retracts from the vertex #i, * except when i=DFS root node. In the latter case there is/are only (nNumConnect-1) * neighboring ring system(s). */ #endif /* count atoms in a ring system */ for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { nNumAtInRingSystem ++; if ( i == (int)nRingStack[j] ) { break; } } while ( nTopRingStack >= 0 ) { j = (int)nRingStack[nTopRingStack--]; at[j].nRingSystem = (AT_NUMB)nNumRingSystems; /* ring system id */ at[j].nNumAtInRingSystem = nNumAtInRingSystem; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) for ( k = 0; k < at[j].valence; k ++ ) { if ( (nRs = at[at[j].neighbor[k]].nRingSystem) && (int)nRs != nNumRingSystems ) { nRsConnect[nLenConnect + (nNumConnect++)] = nRs; /* adjacent ring system id */ } } #endif if ( i == j ) { /* reached atom on the top of nStackAtom[] stack */ break; } } #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) nRsConnect[nLenConnect] = nNumConnect; nRsConnect[nLenConnect+1] = nNumAtInRingSystem; nLenConnect += nNumConnect; if ( nMaxNumConnect < nNumConnect ) { /* max number of neighboring ring systems */ nMaxNumConnect = nNumConnect; } #endif } else if ( nTopStackAtom > 0 ) { j = (int)nStackAtom[nTopStackAtom-1]; /* inherit nLowNumber */ if ( nLowNumber[j] > nLowNumber[i] ) { nLowNumber[j] = nLowNumber[i]; } } } while ( --nTopStackAtom >= 0 ); #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) /* normally disabled */ nMaxNumConnect ++; if ( nNumRingSystems > 1 ) { int nCol = nMaxNumConnect+1; int nNumInSyst= nMaxNumConnect; int nMaxNeigh = nMaxNumConnect-1; #define T(a,b) tree[(a)*nCol+b] if ( tree = (AT_NUMB *)inchi_calloc( nCol * (nNumRingSystems+1), sizeof(tree[0])) ) { int len, neigh; /* reuse previous allocations */ AT_NUMB *nNumVisitedNeighbors = nStackAtom; AT_NUMB *nDistanceFromTerminal = nRingStack; AT_NUMB *nCurrActiveRingSystem = nDfsNumber; AT_NUMB *nNextActiveRingSystem = nLowNumber; int nNumCurrActiveRingSystems, nNumNextActiveRingSystems, pass; /* build a "condensation graph (actually, a tree)" in which * each vertex corresponds to a ring system T(row, col) = T(ring syst, neighbors) * Number of rows = column length = max. number of ring system neighbors + 2 * Number of cols = row length = number of ring systems + 1 * Neighboring ring systems are contiguously stored in a row * T(i,0) = number of neighbors, 1 <= i <= nNumRingSystems; * T(i,k) = number of a neighboring ring system, 1 <= k <= T(i,0) * T(i,nCol-1) = number of atoms in the system #i */ for ( i = 1, j = 0; len=nRsConnect[j]; i ++ ) { T(i, nNumInSyst) = nRsConnect[j+1]; for ( k = 2; k < len; k ++ ) { neigh = nRsConnect[j+k]; if ( T(i,0) < nMaxNeigh && T(neigh,0) < nMaxNeigh ) { T(i,0) ++; T(neigh,0) ++; T(i,T(i,0)) = neigh; T(neigh,T(neigh,0)) = i; } else { nNumRingSystems = CT_OVERFLOW; /* program error */ /* */ goto exit_function; } } j += len; } /* clear memory */ memset( nNumVisitedNeighbors, 0, nNumRingSystems*sizeof(nNumVisitedNeighbors[0]) ); memset( nDistanceFromTerminal, 0, nNumRingSystems*sizeof(nDistanceFromTerminal[0]) ); memset( nCurrActiveRingSystem, 0, nNumRingSystems*sizeof(nCurrActiveRingSystem[0]) ); memset( nNextActiveRingSystem, 0, nNumRingSystems*sizeof(nNextActiveRingSystem[0]) ); nNumNextActiveRingSystems = 0; for ( i = 0; i < nNumRingSystems; i ++ ) { if ( 1 == T(i+1,0) ) { nNextActiveRingSystem[i] = 1; /* number of traversed neighbors + 1 */ nDistanceFromTerminal[i] = 1; nNumNextActiveRingSystems ++; } else { nNextActiveRingSystem[i] = 0; nDistanceFromTerminal[i] = 0; } nNumVisitedNeighbors[i] = 0; } /* nCurrActiveRingSystem[i] = a sum of: * 1) +1 if it is or was active * 2) +(number of neighbors from which it was reached) * 3) +1 if it was left and not active anymore */ pass = 0; do { nNumCurrActiveRingSystems = nNumNextActiveRingSystems; nNumNextActiveRingSystems = 0; memcpy( nCurrActiveRingSystem, nNextActiveRingSystem, nNumRingSystems*sizeof(nNextActiveRingSystem[0])); for ( i = 0; i < nNumRingSystems; i ++ ) { if ( T(i+1,0) == nCurrActiveRingSystem[i] ) { /* on the previous pass currently active ring system i+1 bas been reached * from all neighbors except one; * the neighbors from which it was reached have * T(neigh,0)+1 == nCurrActiveRingSystem[i] * this ring system has not been left yet */ for ( k = 1, len=T(i+1,0); k <= len; k ++ ) { neigh = (int)T(i+1,k); if ( T(neigh,0) >= nCurrActiveRingSystem[neigh-1] ) { if ( 0 == pass ) { nDistanceFromTerminal[i] = 1; } break; } } if ( k <= len ) { /* neigh was not reached from at least 2 neighbors * walk along -R- chain (T(neigh,0)==2) up to * 1) a terminal system, not including it or * 2) a branching point. * * pass = 0: started from terminal systems: * reach the branching point. * If chain system next to a terminal system has already been reached * then walk along it according to Note below * * pass > 0: started from branching points * 2a) If the branching point has not been reached from 2 or more neighbors, * then include it * 2b) If the branching point has not been reached from 1 neighbor only, * then do not include it: it will be a starting point later * Note: if a chain atom already has nDistanceFromTerminal[i] > 0, then * the last atom should be the one such that * its nDistanceFromTerminal[]+1>= nDistanceFromTerminal[] of the * next in the chain */ int bOk = 0; k = i+1; /* starting point */ if ( 0 == pass && T(k,nNumInSyst) > 1 ) { nNumNextActiveRingSystems ++; /* request next pass */ continue; /* stop a the terminal ring system */ } while( 2 == T(neigh,0) ) { /* walk along a chain */ if ( !nNextActiveRingSystem[neigh-1] ) { nNextActiveRingSystem[neigh-1] = 1; /* make neighbor active */ } else if ( nDistanceFromTerminal[k-1]+1 <= nDistanceFromTerminal[neigh-1] ) { /* walking along the chain; already have had a walk */ /* in the opposite direction at this pass */ } else { /* k is the last; neigh (it is a bridge -X-) has not been reached */ bOk = 1; break; } nNextActiveRingSystem[k-1] ++; /* leave system k */ if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ } nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; j = (T(neigh,1)==k)? 2:1; k = neigh; neigh = T(k,j); /* next in the chain */ nNumNextActiveRingSystems ++; if ( T(k,nNumInSyst) > 1 ) { bOk = 1; break; /* stop on a ring system */ } } /* neigh is a terminal or a bridge or a branching point */ if ( 2 > T(neigh,0) ) { /* neighbor is a terminal atom */ if ( 1 < pass ) { nNumRingSystems = CT_UNKNOWN_ERR; /* error (debug only) */ /* */ goto exit_function; } continue; } if ( 2 == T(neigh,0) ) { /* neighbor is a bridge */ continue; } /* neighbor is a branching point */ if ( T(neigh,0) > nCurrActiveRingSystem[neigh-1] ) { /* move to the neigh (make neigh active): on previous pass it */ /* has not been reached from 2 or more neighbors */ if ( !nNextActiveRingSystem[neigh-1] ) { nNextActiveRingSystem[neigh-1] = 1; } if ( nDistanceFromTerminal[neigh-1] < nDistanceFromTerminal[k-1]+1 ) { nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; } nNextActiveRingSystem[k-1] ++; /* leave system k */ if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ } nNumNextActiveRingSystems ++; } } } } pass ++; } while ( nNumNextActiveRingSystems ); for ( i = 0; i < num_atoms; i ++ ) { at[i].nDistanceFromTerminal = nDistanceFromTerminal[(int)at[i].nRingSystem-1]; } inchi_free( tree ); tree = NULL; #undef T } else { nNumRingSystems = CT_OUT_OF_RAM; /* error */ /* */ goto exit_function; } } #endif exit_function: if ( nStackAtom ) inchi_free( nStackAtom ); if ( nRingStack ) inchi_free( nRingStack ); if ( nDfsNumber ) inchi_free( nDfsNumber ); if ( nLowNumber ) inchi_free( nLowNumber ); if ( cNeighNumb ) inchi_free( cNeighNumb ); #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) if ( nRsConnect ) inchi_free( nRsConnect ); if ( tree ) inchi_free( tree ); #endif return nNumRingSystems; } #endif /* } FIND_RING_SYSTEMS */ /********************************************************************************/ /* Return value: new number of atoms > 0 or -1=out of RAM */ int remove_terminal_HDT( int num_atoms, inp_ATOM *at, int bFixTermHChrg ) { AT_NUMB *new_ord; inp_ATOM *new_at; char *p; const static char szHDT[]="HDT"; const static int kMax = sizeof(szHDT); /* = 4 */ int ret = -1; int num_hydrogens=0, num_H = 0; /* number of terminal H, D, T */ int i, j, k, n, m; int val; AT_RANK new_HydrogenAt_order[NUM_H_ISOTOPES+1]; AT_RANK new_OtherNeigh_order[MAXVAL]; S_CHAR old_trans[MAX_NUM_STEREO_BONDS]; int num_OtherNeigh, num_HydrogenAt; new_ord=(AT_NUMB *)inchi_calloc(num_atoms, sizeof(new_ord[0])); /* changed malloc to calloc 9-11-2003 */ new_at =(inp_ATOM *) inchi_malloc(sizeof(new_at[0]) *num_atoms); if (!new_ord || !new_at) goto exit_function; /* move H. D, T to the end of the list of atoms */ for ( i = 0; i < num_atoms; i ++ ) { at[i].component = i; /* temporarily save original numbering */ /* get k = temp. hydrogen isotope/non-hydrogen atom type: */ /* k=0:H, k=2:D, k=3:T, k=4=kMax: not a hydrogen */ k = at[i].elname[1]? kMax : (p=(char*)strchr(szHDT, at[i].elname[0]))? p-szHDT : kMax; /* set hydrogen isotope atw differences */ /* Notes: k-value of isotopic H is incremented to correct iso_atw_diff value later. */ /* 1H isotope cannot be detected here. */ if ( k == ATW_H || k == ATW_H+1 ) { /* D or T, k = 1 or 2 */ at[i].elname[0] = 'H'; /* hydrogen isotope */ at[i].iso_atw_diff = ++k; /* increment k to make k = iso_atw_diff ( 2 for D, 3 for T ) */ } num_H += (k != kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && !NUMH(at,i) ); } /* special case: HD, HT, DT, HH: the only non-isotopic H or * the lightest isotopic H out of two is removed * to become implicit (make the heavier H the "central atom"). * Note: This must be consistent with mol_to_atom() * treatment of isotopic Hn aliases. */ if ( 2 == num_H && 2 == num_atoms && !NUMH(at,0) && !NUMH(at,1) ) { if ( at[0].iso_atw_diff >= at[1].iso_atw_diff ) { new_ord[0] = 0; new_ord[1] = 1; } else { new_ord[0] = 1; new_ord[1] = 0; } if ( at[new_ord[1]].charge ) { at[new_ord[0]].charge += at[new_ord[1]].charge; at[new_ord[1]].charge = 0; } new_at[new_ord[0]] = at[0]; new_at[new_ord[1]] = at[1]; num_hydrogens = 1; } else { /* general case except H-H */ for ( i = 0; i < num_atoms; i ++ ) { k = (at[i].elname[1] || NUMH(at,i))? kMax : (at[i].elname[0]=='H')? at[i].iso_atw_diff : kMax; if ( k < kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && /* the order of comparison is important */ ((n=(int)at[i].neighbor[0]) > i /* at[n] has not been encountered yet*/ || (int)new_ord[n] < num_atoms - num_hydrogens) /* at[n] might have been encountered; it has not been moved */ ) { /* found an explicit terminal hydrogen */ num_hydrogens ++; if ( k==0 && ATW_H <= at[i].iso_atw_diff && at[i].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) { k = at[i].iso_atw_diff; /* H isotope has already been marked above or elsewhere */ } if ( at[i].charge ) { /* transfer charge from the hydrogen */ at[n].charge += at[i].charge; at[i].charge = 0; if (bFixTermHChrg) { /*^^^^^ Fixed bug (July 6, 2008 IPl) : if terminal H was charged (not neutralized before call of remove_terminal_HDT) and had an ordering number > than that of heavy-atom neighbour, then charge on neighbour atom was not adjusted (though charge on H was removed). ^^^^^ */ if ( i > n ) /* new_at[new_ord[n]] has been created and filled already */ new_at[new_ord[n]].charge = at[n].charge; } /*^^^^^ */ } new_ord[i] = num_atoms - num_hydrogens; /* move hydrogens to the end of the list */ } else { /* atom is not an explicit terminal hydrogen */ new_ord[i] = i - num_hydrogens; /* adjust non-hydrogens positions */ } /* copy atom to the new position */ new_at[new_ord[i]] = at[i]; } /* i */ } /* general case except H-H */ if ( num_hydrogens ) { int num_others = num_atoms-num_hydrogens; /* atoms which are not terminal H, D, T */ if ( num_hydrogens > 1 ) { /* sort hydrogen isotopes in ascending order, */ /* orig, numbers being the secondary sorting key */ qsort( new_at+num_others, num_hydrogens, sizeof(new_at[0]), cmp_iso_atw_diff_component_no ); } /* save new numbering of hydrogen atoms using temporarily saved orig numbering */ for ( i = num_others; i < num_atoms; i ++ ) { new_ord[(int)new_at[i].component] = i; } /* renumber neighbors according to new_ord[] and detach terminal hydrogens */ for ( i = 0; i < num_others; i++ ) { memset( new_HydrogenAt_order, 0, sizeof(new_HydrogenAt_order) ); memset( new_OtherNeigh_order, 0, sizeof(new_OtherNeigh_order) ); num_OtherNeigh = 0; num_HydrogenAt = 0; num_H = 0; for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { old_trans[m] = 2 - (new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2; } for ( k = j = val = 0; k < new_at[i].valence; k++ ) { if ( num_others <= ( n = new_ord[new_at[i].neighbor[k]] ) ) { /* discovered neighbor = disconnected explicit hydrogen * i = new atom new_at[i] ordering number * n = new number of the explicit H * k = ordering number of the explicit H in new_at[i] adjacency list */ if ( 0 < new_at[n].iso_atw_diff && new_at[n].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) { /* make explicit isotopic H implicit */ new_at[i].num_iso_H[new_at[n].iso_atw_diff-1] ++; /* isotopic H */ num_HydrogenAt += !new_HydrogenAt_order[new_at[n].iso_atw_diff]; new_HydrogenAt_order[new_at[n].iso_atw_diff] = k+1; } else { /* make explicit non-isotopic H implicit */ new_at[i].num_H ++; /* non-isotopic H */ num_HydrogenAt += !num_H; num_H ++; new_HydrogenAt_order[0] = k+1; } /* decrement chem. bonds valence because one bond is removed */ new_at[i].chem_bonds_valence = inchi_max( 0, new_at[i].chem_bonds_valence-1 ); new_at[n].neighbor[0] = i; /* update removed hydrogen neighbor number */ if ( new_at[i].sb_parity[0] ) { /* if the removed H is an SB neighbor then mark it as removed */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { if ( k == (int)new_at[i].sn_ord[m] ) { new_at[i].sn_ord[m] = -(new_at[n].iso_atw_diff+1); /* means the SB neighbor has been removed; (-4)=H, (-3)=1H, (-2)=D, (-1)=T */ } } } } else { /* discovered a regular (not an explicit H) neighbor */ if ( new_at[i].sb_parity[0] ) { if ( num_OtherNeigh < MAX_NUM_STEREO_BONDS ) { new_OtherNeigh_order[num_OtherNeigh] = k+1; } num_OtherNeigh ++; /* increment outside of if() to detect overflow */ if ( val != k ) { /* store new stereobond and sb-neighbor ordering numbers */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { if ( k == (int)new_at[i].sb_ord[m] ) new_at[i].sb_ord[m] = val; else if ( k == (int)new_at[i].sn_ord[m] ) new_at[i].sn_ord[m] = val; } } } new_at[i].neighbor[val] = new_ord[new_at[i].neighbor[k]]; new_at[i].bond_type[val] = new_at[i].bond_type[k]; new_at[i].bond_stereo[val] = new_at[i].bond_stereo[k]; val ++; } } if ( new_at[i].valence > val && new_at[i].sb_parity[0] ) { if ( num_HydrogenAt == new_at[i].valence - val && num_HydrogenAt + num_OtherNeigh <= MAXVAL ) { /* recalculate parity so that it would describe neighbor sequence H,1H,D,T,neigh[0],neigh[1]... */ memmove( new_OtherNeigh_order + num_HydrogenAt, new_OtherNeigh_order, num_OtherNeigh*sizeof(new_OtherNeigh_order[0])); for ( k = 0, j = 1; k <= NUM_H_ISOTOPES; k ++ ) { if ( new_HydrogenAt_order[k] ) { new_OtherNeigh_order[num_HydrogenAt - j] = new_HydrogenAt_order[k]; for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { if ( (int)new_at[i].sn_ord[m] == -(k+1) ) { new_at[i].sn_ord[m] = -j; /* negative means explicit H isotope ord are (contiguously) in front of the adjacency list */ } } j ++; } } /* at this point new_OtherNeigh_order[] contains incremented old ordering numbers in new order */ k = insertions_sort_AT_RANK( new_OtherNeigh_order, num_HydrogenAt + num_OtherNeigh ); k = k%2; /* seems to be of no use */ /*if ( k ) {*/ /* for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { if ( PARITY_WELL_DEF(new_at[i].sb_parity[m]) ) { if ( old_trans[m] != 2 - (4 + new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2 ) { new_at[i].sb_parity[m] = 3 - new_at[i].sb_parity[m]; } } } */ /*}*/ } #ifdef _DEBUG else { /* error */ int stop = 1; } #endif } new_at[i].valence = val; } memcpy( at, new_at, sizeof(at[0])*num_atoms ); ret = num_others; } else { ret = num_atoms; } exit_function: if ( new_ord ) inchi_free ( new_ord ); if ( new_at ) inchi_free ( new_at ); return ret; } /************************************************************************/ int add_DT_to_num_H( int num_atoms, inp_ATOM *at ) /* assume num_1H, num_D and num_T are not included in num_H */ { int i, j; for ( i = 0; i < num_atoms; i ++ ) { for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) at[i].num_H += at[i].num_iso_H[j]; } return 0; } /***************************************************************/ /* not used --- int FixAromaticOxygenAndSulfur( inp_ATOM *atom ) { if ( !atom->elname[1] && (atom->elname[0]=='O' || atom->elname[0]=='S') && atom->valence==2 && !atom->charge && !atom->radical && atom->bond_type[0] + atom->bond_type[1] == 3 ) { atom->charge = 1; return 1; // fixed } return 0; } */ /******************************************************************** InChI post-version 1.01 features implementation (v. 1.04 : underivatize is still experimental and for engineering mode) ********************************************************************/ #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) static U_CHAR el_number_O; static U_CHAR el_number_C; static U_CHAR el_number_N; static U_CHAR el_number_P; static U_CHAR el_number_S; static U_CHAR el_number_Si; static U_CHAR el_number_F; static U_CHAR el_number_Cl; static U_CHAR el_number_Br; static U_CHAR el_number_I; static U_CHAR el_number_B; typedef struct tagAtPair { AT_NUMB at[2]; /* at[0] < at[1] */ } R2C_ATPAIR; int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ); int mark_arom_bonds( inp_ATOM *at, int num_atoms ); void set_R2C_el_numbers( void ); int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ); int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ); int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ); int UnMarkOneComponent( inp_ATOM *at, int num_atoms ); int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ); int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ); int cmp_r2c_atpair( const void *p1, const void *p2 ); int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ); int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ); /********************************************************************/ void set_R2C_el_numbers( void ) { if ( !el_number_O ) { el_number_O = (U_CHAR)get_periodic_table_number( "O" ); el_number_C = (U_CHAR)get_periodic_table_number( "C" ); el_number_N = (U_CHAR)get_periodic_table_number( "N" ); el_number_P = (U_CHAR)get_periodic_table_number( "P" ); el_number_S = (U_CHAR)get_periodic_table_number( "S" ); el_number_Si = (U_CHAR)get_periodic_table_number( "Si" ); el_number_F = (U_CHAR)get_periodic_table_number( "F" ); el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); el_number_I = (U_CHAR)get_periodic_table_number( "I" ); el_number_B = (U_CHAR)get_periodic_table_number( "B" ); } } /***************************************************************/ int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ) { int i; for ( i = 0; i < orig_inp_data->num_inp_atoms; i ++ ) { orig_inp_data->at[i].orig_compt_at_numb = 0; orig_inp_data->at[i].component = 0; } if ( orig_inp_data->nCurAtLen ) { inchi_free( orig_inp_data->nCurAtLen ); orig_inp_data->nCurAtLen = NULL; } if ( orig_inp_data->nOldCompNumber ) { inchi_free( orig_inp_data->nOldCompNumber ); orig_inp_data->nOldCompNumber = NULL; } orig_inp_data->num_components = 0; return 0; } /***************************************************************/ int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ) { int i; for ( i = 0; i < num_atoms; i ++ ) { at[i].bCutVertex = 0; at[i].nRingSystem = 0; at[i].nNumAtInRingSystem = 0; at[i].nBlockSystem = 0; } return 0; } /***************************************************************/ int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ) { int i; for ( i = 0; i < num_atoms; i ++ ) { at[i].at_type = 0; at[i].cFlags = 0; } return 0; } /***************************************************************/ int UnMarkOneComponent( inp_ATOM *at, int num_atoms ) { int i; for ( i = 0; i < num_atoms; i ++ ) { at[i].orig_compt_at_numb = 0; at[i].component = 0; } return 0; } /***************************************************************/ int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ) /* assume num_1H, num_D and num_T are included in num_H */ { int i, j; for ( i = 0; i < num_atoms; i ++ ) { for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) at[i].num_H -= at[i].num_iso_H[j]; } return 0; } /***************************************************************/ int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ) { int i, j; inp_ATOM *a; /* chack correctness */ if ( len_cur < 0 ) return len_cur; if ( len_add < 0 ) return len_add; if ( len_cur + len_add > len_at ) return -1; /* copy */ memcpy( at+len_cur, add, len_add*sizeof(at[0]) ); /* modify */ if ( len_cur ) { a = at + len_cur; for ( i = 0; i < len_add; i ++ ) { for ( j = 0; j < a[i].valence; j ++ ) { a[i].neighbor[j] += len_cur; } } } return len_cur + len_add; } /****************************************************************************/ int mark_arom_bonds( inp_ATOM *at, int num_atoms ) { INCHI_MODE bTautFlags=0, bTautFlagsDone = 0; inp_ATOM *at_fixed_bonds_out = NULL; T_GROUP_INFO *t_group_info = NULL; int ret; ret = mark_alt_bonds_and_taut_groups ( at, at_fixed_bonds_out, num_atoms, t_group_info, &bTautFlags, &bTautFlagsDone ); return ret; } /********************************************************************/ int cmp_r2c_atpair( const void *p1, const void *p2 ) { const R2C_ATPAIR *ap1 = (const R2C_ATPAIR *)p1; const R2C_ATPAIR *ap2 = (const R2C_ATPAIR *)p2; int diff = (int)ap1->at[0] - (int)ap2->at[0]; if ( !diff ) { diff = (int)ap1->at[1] - (int)ap2->at[1]; } return diff; } /***************************************************************/ int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ) { R2C_ATPAIR ap1; int i1, i2, i3, diff; int n = at1 > at2; ap1.at[n] = at1; ap1.at[1-n] = at2; i1 = 0; i2 = num_ap-1; /* search for ap1 by simple bisections */ do { i3 = (i1 + i2)/2; if ( !(diff = cmp_r2c_atpair(&ap1, ap+i3) ) ) { return i3+1; /* found => positive number */ } else if ( diff > 0 ) { i1 = i3 + 1; } else { i2 = i3 - 1; } }while ( i2 >= i1 ); return 0; /* not found */ } /***************************************************************/ /* DFS search for atoms that do not have a flag */ int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ) { if ( !at[start].at_type ) { int i; AT_NUMB neigh; at[start].at_type = cFlags; num ++; for ( i = 0; i < at[start].valence; i ++ ) { neigh = at[start].neighbor[i]; if ( has_atom_pair( ap, num_ap, start, neigh ) ) continue; num = mark_atoms_ap( at, neigh, ap, num_ap, num, cFlags ); } } return num; /* number of atoms traversed forward from at[start] */ } #endif /* RING2CHAIN || UNDERIVATIZE */ #if ( UNDERIVATIZE == 1 ) /***************************************************************/ #ifdef NEVER typedef struct tagAtTypeBitmap { AT_NUMB ord1 : 5; /* up to 2^5-1 = 31 = 0x0037 */ AT_NUMB ord2 : 5; AT_NUMB type : 6; /* up to 2^6-1 = 63 = 0x0077 */ } AtTypeBitmap; typedef union tagAtTypeUnion { AT_NUMB num; AtTypeBitmap bits; } AtTypeUnion; #endif #define DERIV_AT_LEN 4 typedef struct tagDerivAttachment { char typ[DERIV_AT_LEN]; char ord[DERIV_AT_LEN]; char num[DERIV_AT_LEN]; } DERIV_AT; #define DERIV_BRIDGE_O 0x0001 /* R1-O-R2 => R1-OH + HO-R2 */ #define DERIV_BRIDGE_NH 0x0002 /* R1-NH-R2 amine */ #define DERIV_AMINE_tN 0x0004 /* R1-N(-R2)-R3 tertiary amine */ #define DERIV_RING_O 0x0008 /* -O- in a ring */ #define DERIV_RING_NH 0x0010 /* -NH- in a ring */ #define DERIV_UNMARK 0x0040 /* unmark the cut */ #define DERIV_NOT 0x1000 /* cannot be a derivatization agent atom */ #define DERIV_DUPLIC 0x0080 /* duplicated disconnection */ #define DERIV_RING (DERIV_RING_O | DERIV_RING_NH) #define MAX_AT_DERIV 12 #define NOT_AT_DERIV 99 #define MIN_AT_LEFT_DERIV 3 #define NO_ORD_VAL 0x0037 #define CFLAG_MARK_BRANCH 1 /* for main derivative traversal */ #define CFLAG_MARK_BLOCK 2 /* for block detection */ #define CFLAG_MARK_BLOCK_INV ((char)~(CFLAG_MARK_BLOCK)) /* for block detection */ #define COUNT_ALL_NOT_DERIV 1 /* 1=> count ALL atoms that are not in deriv. agents */ /* 0=> only atoms that are not in DERIV_RING */ int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ); int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ); int is_C_or_S_DB_O( inp_ATOM *at, int i ); int is_C_unsat_not_arom( inp_ATOM *at, int i ); int is_C_Alk( inp_ATOM *at, int i, char cFlags ); int is_Si_IV( inp_ATOM *at, int i ); int is_P_TB_N( inp_ATOM *at, int i ); int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ); int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ); int add_to_da( DERIV_AT *da, DERIV_AT *add ); int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ); int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ); int is_silyl( inp_ATOM *at, int start, int ord_prev ); int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ); int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ); int is_phenyl( inp_ATOM *at, int start, int ord_prev ); int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ); int remove_deriv( DERIV_AT *da1, int idrv ); int remove_deriv_mark( DERIV_AT *da1, int idrv ); int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ); int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ); /***************************************************************/ /* DFS search for atoms that do not have a flag */ int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ) { if ( !(at[start].cFlags & cFlags) ) { int i; at[start].cFlags |= cFlags; num ++; for ( i = 0; i < at[start].valence; i ++ ) { num = mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags ); } } return num; /* number of atoms traversed forward from at[start] */ } /***************************************************************/ /* DFS search for atoms that do have a flag */ int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ) { if ( at[start].cFlags & cFlags ) { int i; at[start].cFlags &= cInvFlags; num ++; for ( i = 0; i < at[start].valence; i ++ ) { num = un_mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags, cInvFlags ); } } return num; /* number of atoms traversed forward from at[start] */ } /***************************************************************/ int is_C_or_S_DB_O( inp_ATOM *at, int i ) { int j, neigh; if ( at[i].el_number != el_number_C && at[i].el_number != el_number_S || at[i].charge || at[i].radical ) return 0; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( (at[neigh].el_number == el_number_O || at[neigh].el_number == el_number_S ) && !at[neigh].num_H && 1 == at[neigh].valence && 2 == at[neigh].chem_bonds_valence ) { return 1; } } return 0; } /***************************************************************/ int is_C_unsat_not_arom( inp_ATOM *at, int i ) { int j, neigh, num_arom, num_DB; if ( at[i].el_number != el_number_C || at[i].valence == at[i].chem_bonds_valence || at[i].valence+1 < at[i].chem_bonds_valence || at[i].chem_bonds_valence + at[i].num_H != 4 || at[i].charge || at[i].radical ) return 0; num_arom = num_DB = 0; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; num_arom += at[i].bond_type[j] == BOND_TYPE_ALTERN; if ( (at[neigh].el_number == el_number_O || at[neigh].el_number == el_number_S ) && !at[neigh].num_H && 1 == at[neigh].valence && 2 == at[neigh].chem_bonds_valence ) { continue; } num_DB += at[i].bond_type[j] == BOND_TYPE_DOUBLE; } return num_DB && !num_arom; } /***************************************************************/ int is_C_Alk( inp_ATOM *at, int i, char cFlags ) { if ( at[i].el_number == el_number_C && at[i].valence == at[i].chem_bonds_valence ) { int j, k; U_CHAR el; for ( j = 0; j < at[i].valence; j ++ ) { k = at[i].neighbor[j]; if ( at[k].cFlags & cFlags ) continue; el = at[k].el_number; if ( el != el_number_C && el != el_number_F && el != el_number_Cl && el != el_number_Br && el != el_number_I ) { return 0; } } return 1; } return 0; } /***************************************************************/ int is_Si_IV( inp_ATOM *at, int i ) { if ( at[i].el_number != el_number_Si || at[i].charge || at[i].radical || at[i].valence != 4 || at[i].chem_bonds_valence != 4 ) return 0; return 1; } /***************************************************************/ int is_P_TB_N( inp_ATOM *at, int i ) { int j, k; if ( at[i].el_number != el_number_P || at[i].chem_bonds_valence - at[i].valence != 2 ) return 0; for ( j = 0; j < at[i].valence; j ++ ) { k = at[i].neighbor[j]; if ( at[k].el_number == el_number_N && at[k].valence == 1 && at[k].chem_bonds_valence == 3 ) return 1; } return 0; } /***************************************************************/ int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ) { int neigh = at[iat].neighbor[iord]; int neigh_from = -1; U_CHAR el = at[neigh].el_number; int bOk = 0; switch ( type ) { case DERIV_BRIDGE_O: neigh_from = at[iat].neighbor[!iord]; /* -> A--O--B -> traversing from A(neigh_from) to B(neigh); may we cut O--B bond? */ /* do not cut bond "---" in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ if ( !(is_C_or_S_DB_O( at, neigh ) && is_Si_IV( at, neigh_from )) && !is_C_unsat_not_arom( at, neigh ) ) { bOk = ( el == el_number_C || el == el_number_Si || el == el_number_S || el == el_number_P ); } break; case DERIV_BRIDGE_NH: /* -> A--NH--B -> traversing from A(neigh_from) to B(neigh); may we cut NH--B bond? */ bOk = ( is_C_or_S_DB_O( at, neigh ) || is_C_Alk( at, neigh, cFlags ) || is_Si_IV( at, neigh ) || is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); break; case DERIV_AMINE_tN: bOk = ( is_C_or_S_DB_O( at, neigh ) || is_C_Alk( at, neigh, cFlags ) || is_Si_IV( at, neigh ) || is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); break; } return bOk; } /***************************************************************/ /* determines derivative type on the forward step of the DFS */ /***************************************************************/ int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ) { int i, j, m, nBlockSystemFrom, nOrdBack1, nOrdBack2, nOrdBack3, nBackType1, nBackType2; memset( da1, 0, sizeof(*da1) ); if ( at[k].cFlags & cFlags ) { return 0; } for ( m = 0; m < at[k].valence && !(at[(int)at[k].neighbor[m]].cFlags & cFlags); m ++ ) ; if ( m == at[k].valence ) return -1; /* error: at least one neighbor must have cFlags */ if ( at[k].valence == 1 && at[k].num_H && ( at[k].el_number == el_number_O || at[k].el_number == el_number_N || at[k].el_number == el_number_S || at[k].el_number == el_number_P ) ) { return DERIV_NOT; } if ( is_el_a_metal( at[k].el_number ) ) { return DERIV_NOT; } #ifdef NEVER if ( at[k].el_number == el_number_N && at[k].valence >= 2 && at[k].chem_bonds_valence <= 3 ) { return DERIV_NOT; /* prohibit -N-, -N=, allow -N# as in isocyano -N#C or NO2 */ } #endif /* m is the ord of the bond from which at[k] was reached first time */ if ( at[k].nNumAtInRingSystem == 1 && (at[k].el_number == el_number_O || at[k].el_number == el_number_S) && at[k].valence == 2 && at[k].chem_bonds_valence == 2 && !at[k].num_H && !at[k].charge && !at[k].radical) { /* -> A--O--B -> traversing from A to B; cut O--B */ /* check for carboxy A(=O)-O-B and A--O--B(=O) */ /* int has_A_CO = is_C_or_S_DB_O( at, at[k].neighbor[m] ); */ int has_B_CO = is_C_or_S_DB_O( at, at[k].neighbor[!m] ); int is_A_Si_IV = is_Si_IV( at, at[k].neighbor[m] ); /* int is_B_Si_IV = is_Si_IV( at, at[k].neighbor[!m] );*/ if ( is_A_Si_IV && has_B_CO ) { ; /* do not cut bond --- in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ } else if ( is_possibly_deriv_neigh( at, k, !m, DERIV_BRIDGE_O, cFlags ) ) { da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ da1->typ[0] = DERIV_BRIDGE_O; /* type */ return DERIV_BRIDGE_O; /* R-O-R */ } } if ( at[k].bCutVertex && at[k].el_number == el_number_N && at[k].valence == 2 && at[k].chem_bonds_valence == at[k].valence && at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ da1->typ[0] = DERIV_BRIDGE_NH; /* type */ return DERIV_BRIDGE_NH; /* R1-N(-R2)-R3 or R1-NH-R2 amine */ } if ( at[k].bCutVertex && at[k].el_number == el_number_N && at[k].valence == 3 && at[k].chem_bonds_valence == at[k].valence && at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { int rm1 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+1)%3]].nRingSystem ); int rm2 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); int r12 = ( at[at[k].neighbor[(m+1)%3]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); int ord[2]= {-1, -1}; i = 0; /* type: tertriary amine */ switch( rm1 + rm2 + r12 ) { case 0: /* -N< has no ring bonds */ if ( is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { ord[i ++] = (m+1)%3; /* ord of a non-ring neighbor, not traversed yet */ } if ( is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { ord[i ++] = (m+2)%3; /* ord of another non-ring neighbor, not traversed yet */ } if ( i == 2 && ord[0] > ord[1] ) { int tmp = ord[0]; ord[0] = ord[1]; ord[1] = tmp; } break; case 1: if ( rm1 && is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { ord[i++] = (m+2)%3; /* ord of a single non-ring neighbor, not traversed yet */ } else if ( rm2 && is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { ord[i++] = (m+1)%3; /* ord of a single non-ring neighbor, not traversed yet */ } } for ( j = 0; j < i; j ++ ) { da1->ord[j] = ord[j]; da1->typ[j] = DERIV_AMINE_tN; } if ( i ) { return DERIV_AMINE_tN; } return 0; /* all neighbors or two untraversed edges are in one ring system */ } if ( at[k].bCutVertex && /* DD */ at[k].valence == at[k].chem_bonds_valence && (!at[k].num_H || at[k].el_number == el_number_C && 1 == at[k].num_H) && !at[k].charge && !at[k].radical && (at[k].el_number == el_number_C && at[k].valence+at[k].num_H == 4 || at[k].el_number == el_number_Si && at[k].valence == 4 || at[k].el_number == el_number_B && at[k].valence == 3) ) { /* entering path: ->X--O--DD --X--O | \ / DD = C, Si, B | DD | / \ O = O, NH --Y--O X, Y -- ignored for now */ nBlockSystemFrom = 0; nBackType1 = nBackType2 = 0; nOrdBack1 = nOrdBack2 = nOrdBack3 = -1; j=(int)at[k].neighbor[m]; if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && at[j].chem_bonds_valence == at[j].valence && at[j].nNumAtInRingSystem >= 5 && !at[j].num_H && !at[j].charge && !at[j].radical ) { nBackType1 = DERIV_RING_O; nBlockSystemFrom = at[j].nBlockSystem; nOrdBack1 = m; /* predecessor */ } else if ( at[j].el_number == el_number_N && at[j].valence == 2 && at[j].chem_bonds_valence == at[j].valence && at[j].nNumAtInRingSystem >= 5 && 1==at[j].num_H && !at[j].charge && !at[j].radical ) { nBackType1 = DERIV_RING_NH; nBlockSystemFrom = at[j].nBlockSystem; nOrdBack1 = m; /* predecessor */ } if ( nBlockSystemFrom ) { int num1, num2, bFound; at[k].cFlags |= CFLAG_MARK_BLOCK; num1 = mark_atoms_cFlags( at, at[k].neighbor[nOrdBack1], 1, CFLAG_MARK_BLOCK ); for ( i = 0; i < at[k].valence; i ++ ) { if ( i == nOrdBack1 ) continue; j=(int)at[k].neighbor[i]; bFound = 0; if ( at[j].cFlags & CFLAG_MARK_BLOCK ) { if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && at[j].chem_bonds_valence == at[j].valence && at[j].nNumAtInRingSystem >= 5 && !at[j].num_H && !at[j].charge && !at[j].radical ) { bFound = 1; if ( nOrdBack2 < 0 ) { nOrdBack2 = i; /* predecessor #2 */ nBackType2 = DERIV_RING_O; } else { nOrdBack3 = i; /* predecessor #3 -- should not happen */ } } if ( at[j].el_number == el_number_N && at[j].valence == 2 && at[j].chem_bonds_valence == at[j].valence && at[j].nNumAtInRingSystem >= 5 && 1==at[j].num_H && !at[j].charge && !at[j].radical ) { bFound = 1; if ( nOrdBack2 < 0 ) { nOrdBack2 = i; /* predecessor #2 */ nBackType2 = DERIV_RING_NH; } else { nOrdBack3 = i; /* predecessor #3 -- should not happen */ } } if ( !bFound ) { nOrdBack3 = 99; /* reject: wrong neighboring atom in the same block */ break; } } } num2 = un_mark_atoms_cFlags( at, k, 0, CFLAG_MARK_BLOCK, CFLAG_MARK_BLOCK_INV ); if ( num1 != num2 ) { return -1; /* mark_atoms_cFlags() program error */ } if ( nOrdBack2 >= 0 && nOrdBack3 < 0 ) { if ( nOrdBack1 < nOrdBack2 ) { da1->ord[0] = nOrdBack1; /* ord of a ring neighbor, traversed */ da1->typ[0] = nBackType1; da1->ord[1] = nOrdBack2; /* ord of another ring neighbor, not traversed yet */ da1->typ[1] = nBackType2; } else { da1->ord[0] = nOrdBack2; /* ord of a ring neighbor, traversed */ da1->typ[0] = nBackType2; da1->ord[1] = nOrdBack1; /* ord of another ring neighbor, not traversed yet */ da1->typ[1] = nBackType1; } return nBackType1 | nBackType2; } } } return 0; } /***************************************************************/ int add_to_da( DERIV_AT *da, DERIV_AT *add ) { /* if add has more than 1 element the elements are in ascending add.ord[] order */ int i, j, len; for ( len = 0; len < DERIV_AT_LEN && da->typ[len]; len ++ ) ; for ( j = 0; j < DERIV_AT_LEN && add->typ[j]; j ++ ) { for ( i = 0; i < len; i ++ ) { if ( add->ord[j] == da->ord[i] ) { if ( add->typ[j] != da->typ[i] ) { return -1; /* error, should not happen */ } if ( add->num[j] != da->num[i] ) { return -2; /* error, should not happen */ } break; } } if ( i == len ) { if ( len < DERIV_AT_LEN ) { da->ord[i] = add->ord[j]; da->typ[i] = add->typ[j]; da->num[i] = add->num[j]; len ++; } else { return -3; /* overflow, should not happen */ } } } return 0; } /***************************************************************/ /* DFS search for atoms that do not have a flag */ int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ) { int i, nFound=0; DERIV_AT da1; if ( !(at[start].cFlags & cFlags) ) { if ( DERIV_NOT == get_traversed_deriv_type( at, da, start, &da1, cFlags ) ) { nFound ++; /* at[start] cannot belong to a derivatizing agent */ } num ++; at[start].cFlags |= cFlags; if ( da1.typ[0] ) { /* possibly a derivatization agent attachment point. */ /* check neighbors that have not been traversed yet */ int n1=0, n2=0, i1=-1, i2=-1, nFound1=0, nFound2=0; switch( da1.typ[0] ) { case DERIV_BRIDGE_O: case DERIV_BRIDGE_NH: n1 = mark_atoms_deriv( at, da, at[start].neighbor[(int)da1.ord[0]], 0, cFlags, &nFound1 ); if ( n1 > MAX_AT_DERIV || nFound1 ) { da1.typ[0] = 0; } else { da1.num[0] = n1; nFound ++; } break; case DERIV_AMINE_tN: n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=da1.ord[0]], 0, cFlags, &nFound1 ); if ( da1.typ[1] ) { n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=da1.ord[1]], 0, cFlags, &nFound2 ); } if ( 0 < n1 && n1 <= MAX_AT_DERIV && !nFound1 ) { da1.num[0] = n1; i = 1; nFound ++; } else { da1.ord[0] = da1.ord[1]; da1.num[0] = da1.num[1]; da1.typ[0] = da1.typ[1]; da1.typ[1] = 0; i = 0; } if ( 0 < n2 && n2 <= MAX_AT_DERIV && !nFound2 ) { da1.num[i] = n2; nFound ++; } else { da1.typ[i] = 0; } break; case DERIV_RING_O: case DERIV_RING_NH: for ( i = 0; i < at[start].valence; i ++ ) { if ( i != da1.ord[0] && i != da1.ord[1] && !(at[at[start].neighbor[i]].cFlags & cFlags) ) { if ( !n1 ) n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=i], 0, cFlags, &nFound1 ); else n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=i], 0, cFlags, &nFound2 ); } } if ( n1+n2+1 > MAX_AT_DERIV || nFound1 || nFound2 ) { da1.typ[1] = da1.typ[0] = 0; } else { da1.num[0] = n1; da1.num[1] = n2; nFound ++; } break; } if ( n1 < 0 ) return n1; if ( n2 < 0 ) return n2; /* errors */ if ( i = add_to_da( da+start, &da1 ) ) { return i; /* error */ } *pbFound += nFound1 + nFound2 + nFound; num += n1 + n2; } else { *pbFound += nFound; } for ( i = 0; i < at[start].valence; i ++ ) { num = mark_atoms_deriv( at, da, at[start].neighbor[i], num, cFlags, pbFound ); if ( num < 0 ) return num; } } /* *pbFound = number of derivatizer attachment points traversed forward from at[start] */ return num; /* number of atoms traversed forward from at[start] */ } /***************************************************************/ int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ) { int num = 0; if ( !(at[at[start].neighbor[ord]].cFlags & cFlags) ) { at[at[start].neighbor[ord]].cFlags |= cFlags; num ++; num = mark_atoms_deriv( at, da, start, num, cFlags, bFound ); } return num; } /*************************************************************** List of allowed derivatives Legend: ->- marks the bond to be disconnexted: X->-Y => XD + TY where TY is a derivatizing agent eventually to be discarded Allowed Derivative Types List ============================= DERIV_BRIDGE_O, DERIV_BRIDGE_NH, DERIV_AMINE_tN ----------------------------------------------- CH3 CH3 CH3 CH3 CH3 | | | | | X->-Si--CH3 X->-Si---Si--CH3 X->-Si----C--CH3 X= O, NH, N | | | | | CH3 CH3 CH3 CH3 CH3 4 atoms 7 atoms 7 atoms is_silyl() O O O F O || || || | || R--C--O->-CH3 R--C--O->-CH2--CH3 R--C--O->-C--F R--C--O->-CF2-CF2-CF3 | F 1 atom 2 atoms 3 atoms 10 atoms is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() A. DERIV_BRIDGE_NH, DERIV_AMINE_tN ----------------------------------- O O O F O || || || | || N->-C--CH3 N->-C--CH2--CH3 N->-C---C--F N->-C--CF2-CF2-CF3 | F 3 atoms 5 atoms 8 atoms 12 atoms is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() DERIV_RING_O (da contains >B- or >C< or >CH- atom) ------------ C----O R----O R----O | \ | \ CH3 | \ | > | > / | > | \ | \ / | \ | B--CH3 | C | CH--Ph | / | / \ | / | > | > \ | > | / | / CH3 | / C----O R----O R----O 5-member 5 or 6-member 5 or 6-member 2 atoms 3 atoms 7 atoms DERIV_RING_NH ------------ None in the list ***************************************************************/ int is_silyl( inp_ATOM *at, int start, int ord_prev ) { int i, neigh, num_Me=0, iC_IV=-1, iSi_IV=-1, i_C_or_Si_IV; if ( at[start].el_number != el_number_Si || at[start].valence != 4 || at[start].valence != at[start].chem_bonds_valence || at[start].charge || at[start].radical ) return 0; for ( i = 0; i < at[start].valence; i ++ ) { if ( i == ord_prev ) continue; neigh = at[start].neighbor[i]; if ( at[neigh].charge || at[neigh].radical || at[neigh].valence != at[neigh].chem_bonds_valence) return 0; if ( at[neigh].valence == 4 ) { if ( at[neigh].el_number == el_number_C && iC_IV < 0 && iSi_IV < 0 ) iC_IV = neigh; else if ( at[neigh].el_number == el_number_Si && iC_IV < 0 && iSi_IV < 0 ) iSi_IV = neigh; else return 0; } else if ( at[neigh].valence == 1 && at[neigh].valence == at[neigh].chem_bonds_valence && at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { num_Me ++; } else { return 0; } } if ( num_Me == 3 && iC_IV < 0 && iSi_IV < 0 ) return 1; /* Si(CH3)3 */ /* next C(IV) or Si(IV) */ i_C_or_Si_IV = iC_IV >= 0? iC_IV : iSi_IV >= 0? iSi_IV : -1; if ( num_Me != 2 || i_C_or_Si_IV < 0 ) return 0; num_Me = 0; for ( i = 0; i < at[i_C_or_Si_IV].valence; i ++ ) { neigh = at[i_C_or_Si_IV].neighbor[i]; if ( neigh == start ) continue; if ( at[neigh].charge || at[neigh].radical || at[neigh].valence != at[neigh].chem_bonds_valence) return 0; if ( at[neigh].valence == 1 && at[neigh].valence == at[neigh].chem_bonds_valence && at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { num_Me ++; } else { return 0; } } if ( num_Me == 3 ) return 2; /* Si(CH3)2Si/C(CH3)3 */ return 0; } /****************************************************************/ int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ) { int i, neigh = -1; if ( at[start].el_number != el_number_C || at[start].valence > 2 || at[start].valence != at[start].chem_bonds_valence || at[start].chem_bonds_valence + at[start].num_H != 4 || at[start].charge || at[start].radical ) return 0; for ( i = 0; i < at[start].valence; i ++ ) { if ( i == ord_prev ) continue; if ( neigh >= 0 ) return 0; neigh = at[start].neighbor[i]; if ( at[neigh].el_number != el_number_C || at[neigh].valence > 1 || at[neigh].valence != at[neigh].chem_bonds_valence || at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || at[neigh].charge || at[neigh].radical ) return 0; } return 1 + (neigh >= 0); } #ifdef NEVER /**************************************************************** CF3 -CF3 or -CF< CF3 *****************************************************************/ int is_CF3_or_isoC3F7( inp_ATOM *at, int start, int ord_prev ) { int i, k, num_C_IV, iC_IV[2], neigh, num_F, iC; if ( at[start].el_number != el_number_C || at[start].valence != 4 || at[start].valence != at[start].chem_bonds_valence || at[start].chem_bonds_valence + at[start].num_H != 4 || at[start].charge || at[start].radical ) return 0; iC_IV[0] = iC_IV[1] = num_F = 0; for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { if ( i == ord_prev ) continue; neigh = at[start].neighbor[i]; if ( at[neigh].valence != at[neigh].chem_bonds_valence || at[neigh].charge || at[neigh].radical ) return 0; if ( at[neigh].el_number == el_number_F ) { if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) return 0; num_F ++; } else if ( at[neigh].el_number == el_number_C && at[neigh].valence == 4 && !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { if ( num_C_IV > 1 ) return 0; iC_IV[num_C_IV++] = neigh; } } if ( !num_C_IV && 3 == num_F ) return 1; /* -CF3 */ if ( 2 != num_C_IV || 1 != num_F ) return 0; /* detect iso-C3F7 */ for ( k = 0; k < num_C_IV; k ++ ) { iC = iC_IV[k]; num_F = 0; for ( i = 0; i < at[iC].valence; i ++ ) { neigh = at[iC].neighbor[i]; if ( neigh == start ) continue; if ( at[neigh].valence != at[neigh].chem_bonds_valence || at[neigh].charge || at[neigh].radical ) return 0; if ( at[neigh].el_number == el_number_F ) { if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) return 0; num_F ++; } else { return 0; } } if ( num_F != 3 ) return 0; } return 2; /* iso-C3F7 */ } #endif /**************************************************************/ int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ) { int i, num_C_IV, iC_IV, neigh, num_F, num_C=0; AT_NUMB *p; while( num_C < 4 ) { if ( at[start].el_number != el_number_C || at[start].valence != 4 || at[start].valence != at[start].chem_bonds_valence || at[start].chem_bonds_valence + at[start].num_H != 4 || at[start].charge || at[start].radical ) return 0; iC_IV = num_F = 0; for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { if ( i == ord_prev ) continue; neigh = at[start].neighbor[i]; if ( at[neigh].valence != at[neigh].chem_bonds_valence || at[neigh].charge || at[neigh].radical ) return 0; if ( at[neigh].el_number == el_number_F ) { if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) return 0; num_F ++; } else if ( at[neigh].el_number == el_number_C && at[neigh].valence == 4 && !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { if ( num_C_IV ) return 0; iC_IV = neigh; num_C_IV++; } } if ( num_C_IV + num_F != 3 ) return 0; num_C ++; /* found -CF2-C or -CF3 */ if ( !num_C_IV ) break; /* -CF3 */ /* treat next C(IV) as a new start atom */ if ( p = is_in_the_list( at[iC_IV].neighbor, (AT_NUMB) start, at[iC_IV].valence ) ) { start = iC_IV; ord_prev = p - at[iC_IV].neighbor; } else { return -1; /* program error */ } } return num_C == 1? 1 : num_C == 3? 2 : 0; } /****************************************************************/ int is_phenyl( inp_ATOM *at, int start, int ord_prev ) { int k, neigh, cur_at, ord; if ( at[start].el_number != el_number_C || at[start].valence != 3 || at[start].valence+1 != at[start].chem_bonds_valence || at[start].chem_bonds_valence + at[start].num_H != 4 || at[start].charge || at[start].radical ) return 0; ord = (ord_prev + 1) % 3; cur_at = start; for ( k = 0; k < 5; k ++ ) { neigh = at[cur_at].neighbor[ord]; if ( at[neigh].el_number != el_number_C || at[neigh].valence != 2 || at[neigh].valence+1 != at[neigh].chem_bonds_valence || at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || at[neigh].charge || at[neigh].radical ) return 0; ord = (at[neigh].neighbor[0] == cur_at); cur_at = neigh; } return (at[cur_at].neighbor[ord] == start); } /****************************************************************/ int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) { int i, k, neigh_at[2], prev_ord[2], neigh, is_B=0, is_C=0; AT_NUMB *p; if ( da1->typ[idrv] != DERIV_RING_O || da1->typ[idrv+1] != DERIV_RING_O ) return 0; if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) return 0; if ( at[start].el_number == el_number_B && at[start].valence == 3 ) is_B = 1; else if ( at[start].el_number == el_number_C && (at[start].valence == 3 || at[start].valence == 4) && at[start].chem_bonds_valence == at[start].valence && at[start].num_H + at[start].chem_bonds_valence == 4 ) is_C = 1; else return 0; /* locate bonds connecting >B- or >C< or >C- to the rest of the derivative */ for ( i = k = 0; i < at[start].valence; i ++ ) { if ( i != da1->ord[idrv] && i != da1->ord[idrv+1] ) { neigh = at[start].neighbor[i]; if ( k < 2 && (p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence)) ) { neigh_at[k] = neigh; prev_ord[k] = p - at[neigh].neighbor; k ++; } else { return -1; /* program error */ } } } if ( is_B && k == 1 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) ) return 1; if ( is_C && k == 1 && at[start].num_H == 1 && is_phenyl( at, neigh_at[0], prev_ord[0]) ) return 1; if ( is_C && k == 2 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) && is_Me_or_Et( at, neigh_at[1], prev_ord[1]) ) return 1; return 0; } /****************************************************************/ int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) { int i, k, prev_ord, neigh, iC, iO, iNxt; AT_NUMB *p; if ( !da1->typ[idrv] || (da1->typ[idrv] & DERIV_RING) ) return 0; if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) return 0; neigh = at[start].neighbor[(int)da1->ord[idrv]]; p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence); if ( !p ) return -1; /* program error */ prev_ord = p - at[neigh].neighbor; /* eliminate silyl possibility */ if ( is_silyl( at, neigh, prev_ord ) ) return 1; if ( da1->typ[idrv] == DERIV_BRIDGE_O ) { /* check acetyl */ iC = at[start].neighbor[!da1->ord[idrv]]; if ( at[iC].charge || at[iC].radical || at[iC].num_H || at[iC].el_number != el_number_C || at[iC].valence != 3 || at[iC].valence+1 != at[iC].chem_bonds_valence ) return 0; for ( i = k = 0; i < at[iC].valence; i ++ ) { iO = at[iC].neighbor[i]; if ( at[iO].charge || at[iO].radical || at[iO].num_H || at[iO].el_number != el_number_O || at[iO].valence != 1 || at[iO].valence+1 != at[iO].chem_bonds_valence ) continue; k ++; /* number of =O */ } if ( k != 1 ) return 0; /* check derivative */ return ( is_Me_or_Et( at, neigh, prev_ord ) || is_CF3_or_linC3F7( at, neigh, prev_ord ) ); } if ( da1->typ[idrv] == DERIV_BRIDGE_NH || da1->typ[idrv] == DERIV_AMINE_tN ) { /* check acetyl */ iNxt = -1; iC = at[start].neighbor[(int)da1->ord[idrv]]; if ( at[iC].charge || at[iC].radical || at[iC].num_H || at[iC].el_number != el_number_C || at[iC].valence != 3 || at[iC].valence+1 != at[iC].chem_bonds_valence ) return 0; for ( i = k = 0; i < at[iC].valence; i ++ ) { iO = at[iC].neighbor[i]; if ( at[iO].charge || at[iO].radical || at[iO].num_H || at[iO].el_number != el_number_O || at[iO].valence != 1 || at[iO].valence+1 != at[iO].chem_bonds_valence ) { if ( iO != start ) { if ( iNxt < 0 ) iNxt = iO; else return 0; } continue; } k ++; /* number of =O */ } if ( k != 1 || iNxt < 0 ) return 0; /* find bond from iNxt to iC */ p = is_in_the_list( at[iNxt].neighbor, (AT_NUMB) iC, at[iNxt].valence); if ( !p ) return -1; /* program error */ prev_ord = p - at[iNxt].neighbor; /* check derivative */ return ( is_Me_or_Et( at, iNxt, prev_ord ) || is_CF3_or_linC3F7( at, iNxt, prev_ord ) ); } return 0; } /****************************************************************/ int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ) { int i, ret = -1; if ( da1->typ[*idrv] & DERIV_RING ) { /* find the first ord of this derivative; ord of ring derivatives are in pairs */ int j = -1; for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { if ( da1->typ[i] & DERIV_RING ) { if ( i == *idrv || i+1 == *idrv ) { *idrv = j = i; break; } i ++; /* bypass the second bond to the same derivatization agent */ } } /* check consistency */ if ( j == -1 || j+1 >= DERIV_AT_LEN || !(da1->typ[j] & DERIV_RING) || !(da1->typ[j+1] & DERIV_RING) ) { ret = -1; /* program error */ } else { ret = is_deriv_ring( at, start, num_atoms, da1, j ); } } else if ( da1->typ[*idrv] ) { ret = is_deriv_ring( at, start, num_atoms, da1, *idrv ); } return ret; } /******************************************************/ int remove_deriv( DERIV_AT *da1, int idrv ) { int i, j, ret = -1; if ( da1->typ[idrv] & DERIV_RING ) { /* find the first ord of this derivative; ord of ring derivatives are in pairs */ j = -1; for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { if ( da1->typ[i] & DERIV_RING ) { if ( i == idrv || i+1 == idrv ) { j = i; break; } i ++; /* bypass the second bond to the same derivatization agent */ } } /* delete if data are consistent */ if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { for ( ; j < DERIV_AT_LEN-2 && da1->typ[j+2]; j ++ ) { da1->typ[j] = da1->typ[j+2]; da1->num[j] = da1->num[j+2]; da1->ord[j] = da1->ord[j+2]; } for ( ; j < DERIV_AT_LEN; j ++ ) { da1->typ[j] = 0; da1->num[j] = 0; da1->ord[j] = 0; } ret = 0; } } else { j = idrv; for ( ; j < DERIV_AT_LEN-1 && da1->typ[j+1]; j ++ ) { da1->typ[j] = da1->typ[j+1]; da1->num[j] = da1->num[j+1]; da1->ord[j] = da1->ord[j+1]; } for ( ; j < DERIV_AT_LEN; j ++ ) { da1->typ[j] = 0; da1->num[j] = 0; da1->ord[j] = 0; } ret = 0; } return ret; } /******************************************************/ int remove_deriv_mark( DERIV_AT *da1, int idrv ) { int i, j, ret = -1; if ( da1->typ[idrv] & DERIV_RING ) { /* find the first ord of this derivative; ord of ring derivatives are in pairs */ j = -1; for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { if ( da1->typ[i] & DERIV_RING ) { if ( i == idrv || i+1 == idrv ) { j = i; break; } i ++; /* bypass the second bond to the same derivatization agent */ } } /* delete if data are consistent */ if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { da1->typ[j] |= DERIV_DUPLIC; da1->typ[j+1] |= DERIV_DUPLIC; ret = 0; } } else { j = idrv; da1->typ[j] |= DERIV_DUPLIC; ret = 0; } return ret; } /****************************************************************/ int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ) { int i, j, num_da, num_cuts=0, ret=0; for ( i = 0; i < num_atoms; i ++ ) { if ( !da[i].typ[0] ) continue; /* count deriative attachments */ for ( num_da = 0; num_da < DERIV_AT_LEN && da[i].typ[num_da]; num_da ++ ) ; if ( num_da > 2 ) return -1; /* should not happen */ if ( num_da == 2 && da[i].typ[0] != da[i].typ[1] ) { da[i].typ[0] = da[i].typ[1] = 0; /* do not allow */ continue; } if ( da[i].typ[0] & DERIV_RING ) { ret = 0; if ( num_da == 2 && 1 + da[i].num[0] + da[i].num[1] <= MAX_AT_DERIV && 0 < (ret=is_deriv_ring( at, i, num_atoms, da+i, 0 ) ) ) { num_cuts += 2; } else if ( ret < 0 ) { return ret; } else { da[i].typ[0] = da[i].typ[1] = 0; /* not a derivative */ } } else { ret = 0; if ( da[i].num[0] <= MAX_AT_DERIV && 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, 0 )) ) { num_cuts ++; j = 1; } else if ( ret < 0 ) { return ret; } else { da[i].ord[0] = da[i].ord[1]; da[i].num[0] = da[i].num[1]; da[i].typ[0] = da[i].typ[1]; da[i].typ[1] = 0; j = 0; } if ( da[i].typ[j] && da[i].num[j] <= MAX_AT_DERIV && 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, j )) ) { num_cuts ++; } else if ( ret < 0 ) { return ret; } else { da[i].typ[j] = 0; } } } return num_cuts; } /***************************************************************/ int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ) { int ret = -1; /* error flag */ int iord = (int)da[iat].ord[icut]; /* ord of the bond in iat */ if ( da[iat].typ[icut] & DERIV_DUPLIC ) { return 0; } else if ( iord < 0 ) { return -1; /* program error */ } else { /* find other da[] that affect at[iat] */ int jat = at[iat].neighbor[iord]; /* opposite atom */ AT_NUMB *p = is_in_the_list( at[jat].neighbor, (AT_NUMB) iat, at[jat].valence ); int jord = p? (p - at[jat].neighbor) : -1; int i, iD=1, iT=2; if ( jord < 0 ) { return -1; /* program error */ } ret = DisconnectInpAtBond( at, NULL, iat, iord ); if ( ret == 1 ) { if ( da[iat].typ[icut] & DERIV_RING ) { /* at[jat] belongs to the main structure */ at[jat].num_H ++; /* add D to the main structure */ at[jat].num_iso_H[iD] ++; at[iat].num_H ++; /* add T to the derivatizing fragment */ at[iat].num_iso_H[iT] ++; } else { at[iat].num_H ++; /* add D to the main structure */ at[iat].num_iso_H[iD] ++; at[jat].num_H ++; /* add T to the derivatizing fragment */ at[jat].num_iso_H[iT] ++; } /* adjust ord for other bonds */ for ( i = 0; i < DERIV_AT_LEN && da[iat].typ[i]; i ++ ) { if ( da[iat].ord[i] == iord ) { da[iat].ord[i] = -(1+da[iat].ord[i]); /* mark the bond being disconnected */ } else if ( da[iat].ord[i] > iord ) { da[iat].ord[i] --; } } for ( i = 0; i < DERIV_AT_LEN && da[jat].typ[i]; i ++ ) { if ( da[jat].ord[i] == jord ) { /* opposite atom needs the same bond to be disconnected */ if ( da[iat].num[icut] == da[jat].num[i] ) { iD=2; /* mark both as fragments */ } else if ( da[iat].num[icut] > da[jat].num[i] ) { iD = 2; /* greater as a main structure */ iT = 1; /* mark smaller as a derivatizing fragment */ } da[jat].ord[i] = -(1+da[jat].ord[i]); da[jat].typ[i] |= DERIV_DUPLIC; } else if ( da[jat].ord[i] > jord ) { da[jat].ord[i] --; } } } } return ret; } /***************************************************************/ int underivatize( ORIG_ATOM_DATA *orig_inp_data ) { #define REMOVE_CUT_DERIV 1 /* remove disconnected derivatizing agents */ int ret = 0, i, j, k, m, n, num_atoms, num_components, i_component, nFound, num, cur_num_at, len; int num_cuts, num_ring_cuts, num_cut_pieces, num_cuts_to_check; inp_ATOM *at = orig_inp_data->at; INP_ATOM_DATA *inp_cur_data = NULL; DERIV_AT *da = NULL; int nTotNumCuts = 0; set_R2C_el_numbers( ); /* prepare */ num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ orig_inp_data->num_inp_atoms = num_atoms; /* initialize */ UnMarkDisconnectedComponents( orig_inp_data ); num_cuts = 0; /* mark */ num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); for ( i_component = 0; i_component < num_components; i_component ++ ) { CreateInpAtomData( inp_cur_data+i_component, orig_inp_data->nCurAtLen[i_component], 0 ); inp_cur_data[i_component].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i_component+1, inp_cur_data[i_component].at ); /* error processing */ if ( inp_cur_data[i_component].num_at <= 0 || orig_inp_data->nCurAtLen[i_component] != inp_cur_data[i_component].num_at ) { ret = -(i_component+1); /* severe error */ goto exit_function; } /* initialize */ num_atoms = inp_cur_data[i_component].num_at; at = inp_cur_data[i_component].at; add_DT_to_num_H( num_atoms, at ); UnMarkRingSystemsInp( at, num_atoms ); UnMarkOtherIndicators( at, num_atoms ); UnMarkOneComponent( at, num_atoms ); MarkRingSystemsInp( at, num_atoms, 0 ); ret = mark_arom_bonds( at, num_atoms ); if ( ret < 0 ) { goto exit_function; } ret = 0; if ( da ) inchi_free( da ); da = (DERIV_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); /* detect derivatives */ nFound = 0; for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].bCutVertex && !da[i].typ[0] ) { for ( k = 0; k < at[i].valence; k ++ ) { num = count_one_bond_atoms( at, da, i, k, CFLAG_MARK_BRANCH, &nFound ); UnMarkOtherIndicators( at, num_atoms ); if ( num < 0 ) { ret = num; /* severe error */ goto exit_function; } } } } /* prepare cuts: remove cuts that are not to be done */ /* in addition, count ring cuts DERIV_RING */ num_ring_cuts = 0; num_cuts = 0; num_cut_pieces = 0; for ( i = num = 0; i < num_atoms; i ++ ) { for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) ; switch( len ) { case 0: continue; case 1: /* single cut: unconditional */ num_cuts += 1; num_cut_pieces += 1; continue; case 2: if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { /* double cut, unconditional */ num_ring_cuts += 2; num_cuts += 2; num_cut_pieces += 1; continue; } else if ( da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { /* double cut, unconditional */ num_cuts += 2; num_cut_pieces += 2; continue; } if ( da[i].typ[0] == da[i].typ[1] ) { /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { da[i].num[0] = NOT_AT_DERIV; } if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { da[i].num[1] = NOT_AT_DERIV; } if ( da[i].num[0] > da[i].num[1] ) { da[i].num[0] = da[i].num[1]; da[i].ord[0] = da[i].ord[1]; da[i].typ[0] = da[i].typ[1]; da[i].typ[1] = 0; num_cuts += 1; num_cut_pieces += 1; } else if ( da[i].num[0] < da[i].num[1] ) { da[i].typ[1] = 0; num_cuts += 1; num_cut_pieces += 1; } else { /* attachments have same size: ignore both */ /* ??? check for standard derivatizations ??? */ da[i].typ[0] = 0; da[i].typ[1] = 0; } continue; } ret = -88; goto exit_function; /* unexpected */ case 3: if ( da[i].typ[0] == da[i].typ[1] && da[i].typ[0] == da[i].typ[2] && da[i].typ[0] == DERIV_AMINE_tN ) { int x, y, z; if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { da[i].num[0] = NOT_AT_DERIV; } if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { da[i].num[1] = NOT_AT_DERIV; } if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 2 ) ) { da[i].num[2] = NOT_AT_DERIV; } x = (da[i].num[0] < da[i].num[1])? 0 : 1; x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ z = (da[i].num[0] < da[i].num[1])? 1 : 0; z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ y = ((x+1)^(z+1))-1; /* median */ if (da[i].num[x] == da[i].num[z] ) { /* no cuts */ da[i].typ[0] = 0; da[i].typ[1] = 0; da[i].typ[2] = 0; continue; /* all deriv. agents have same size, ignore */ /* ??? check for standard derivatizations ??? */ } else if ( da[i].num[x] == da[i].num[y] ) { /* two smallest */ switch( z ) { case 0: da[i].num[0] = da[i].num[1]; da[i].ord[0] = da[i].ord[1]; da[i].typ[0] = da[i].typ[1]; da[i].num[1] = da[i].num[2]; da[i].ord[1] = da[i].ord[2]; da[i].typ[1] = da[i].typ[2]; break; case 1: da[i].num[1] = da[i].num[2]; da[i].ord[1] = da[i].ord[2]; da[i].typ[1] = da[i].typ[2]; break; case 2: break; } da[i].typ[2] = 0; num_cuts += 2; num_cut_pieces += 2; } else { /* one smallest */ if ( x ) { da[i].num[0] = da[i].num[x]; da[i].ord[0] = da[i].ord[x]; da[i].typ[0] = da[i].typ[x]; } da[i].typ[1] = 0; da[i].typ[2] = 0; num_cuts += 1; num_cut_pieces += 1; } continue; } ret = -88; goto exit_function; /* unexpected */ case 4: if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { int n01 = inchi_max( da[i].num[0], da[i].num[1] ); int n23 = inchi_max( da[i].num[2], da[i].num[3] ); if ( n01 < n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 0) ) { da[i].typ[2] = 0; da[i].typ[3] = 0; num_cuts += 2; num_ring_cuts += 2; num_cut_pieces += 1; } else if ( n01 > n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 2) ) { da[i].num[0] = da[i].num[2]; da[i].ord[0] = da[i].ord[2]; da[i].typ[0] = da[i].typ[2]; da[i].num[1] = da[i].num[3]; da[i].ord[1] = da[i].ord[3]; da[i].typ[1] = da[i].typ[3]; da[i].typ[2] = 0; da[i].typ[3] = 0; num_cuts += 2; num_ring_cuts += 2; num_cut_pieces += 1; } else { da[i].typ[0] = 0; da[i].typ[1] = 0; da[i].typ[2] = 0; da[i].typ[3] = 0; } continue; } ret = -88; goto exit_function; /* unexpected */ } } /* eliminate cases when da[i1].typ[j1] && da[i2].typ[j2] && at[i1].neighbor[da[i1].ord[j1]] == i2 && at[i2].neighbor[da[i2].ord[j2]] == i1 that is, same bond is in the da[] elements of the adjacent atoms */ nFound = 0; /* number of cuts to remove */ for ( i = 0; i < num_atoms; i ++ ) { for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { if ( da[i].typ[j] & DERIV_DUPLIC ) continue; n = at[i].neighbor[(int)da[i].ord[j]]; if ( n < i ) continue; for ( k = 0; k < MAX_AT_DERIV && da[n].typ[k]; k ++ ) { if ( da[n].typ[k] & DERIV_DUPLIC ) continue; m = at[n].neighbor[(int)da[n].ord[k]]; if ( m == i ) { /* same bond in da[i].typ[j] and da[n].typ[k] */ /* check whether both derivatives are acceptable */ int k1=k, j1=j; int ret_i = is_deriv_chain_or_ring( at, i, num_atoms, da+i, &j1 ); int ret_n = is_deriv_chain_or_ring( at, n, num_atoms, da+n, &k1 ); if ( ret_i < 0 ) { ret = ret_i; goto exit_function; } if ( ret_n < 0 ) { ret = ret_n; goto exit_function; } if ( !ret_i || ret_i && ret_n ) { if ( da[i].typ[j1] & DERIV_RING ) { num_cuts -= 2; num_ring_cuts -= 2; } else { num_cuts -= 1; } num_cut_pieces -= 1; if (ret = remove_deriv_mark( da+i, j1 )) { goto exit_function; } nFound ++; } if ( !ret_n || ret_i && ret_n ) { if ( da[n].typ[k1] & DERIV_RING ) { num_cuts -= 2; num_ring_cuts -= 2; } else { num_cuts -= 1; } num_cut_pieces -= 1; if ( ret = remove_deriv_mark( da+n, k1 ) ) { goto exit_function; } nFound ++; } } } } } if ( nFound ) { for ( i = 0; i < num_atoms; i ++ ) { for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { /* attn: j is changed inside the cycle body */ if ( da[i].typ[j] & DERIV_DUPLIC ) { if (ret = remove_deriv( da+i, j )) { goto exit_function; } j --; } } } } /* make sure DERIV_RING type disconnections increase */ /* number of components by the number of disconnected derivateves */ /* Avoid cases like these: O--R--O DO--R--OD / \ R--X Y--R => R--XT2 T2Y--R \ / O--R--O DO--R--OD O--O DO--OD / \ R--X--O---Y--R => R--X OD2 Y--R T2 T2 */ /* count DERIV_RING-type attachments */ #if ( COUNT_ALL_NOT_DERIV == 1 ) num_cuts_to_check = num_cuts; #else num_cuts_to_check = num_ring_cuts; #endif if ( num_cuts_to_check >= 2 ) { /* check */ R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( num_cuts_to_check * sizeof(ap[0]) ); AT_NUMB comp_num; int /*n,*/ m_at, m_ord; AT_NUMB at1, at2; if ( !ap ) { ret = -1; goto exit_function; /* malloc failure */ } repeat_without_deriv_ring: comp_num = 0; /* fill out the array of bonds to be cut */ for ( i = j = 0; i < num_atoms; i ++ ) { if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && da[i].num[0] <= MAX_AT_DERIV && da[i].num[1] <= MAX_AT_DERIV ) { if ( j+1 >= num_cuts_to_check ) { ret = -2; goto exit_r2c_num; /* wrong number of cuts = num */ } for ( k = 0; k < 2; k ++ ) { at1 = i; at2 = at[i].neighbor[(int)da[i].ord[k]]; n = ( at1 > at2 ); ap[j].at[n] = at1; ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ j ++; } if ( 0 < cmp_r2c_atpair( ap+j-2, ap+j-1 ) ) { R2C_ATPAIR ap1 = ap[j-2]; ap[j-2] = ap[j-1]; ap[j-1] = ap1; /* sort each pair */ } } #if ( COUNT_ALL_NOT_DERIV == 1 ) else { for ( k = 0; k < DERIV_AT_LEN && da[i].typ[k]; k ++ ) { if ( j >= num_cuts_to_check || (da[i].typ[k] & DERIV_RING) ) { ret = -2; goto exit_r2c_num; /* wrong number of cuts = num or wrong type */ } at1 = i; at2 = at[i].neighbor[(int)da[i].ord[k]]; n = ( at1 > at2 ); ap[j].at[n] = at1; ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ j ++; } } #endif } if ( j != num_cuts_to_check ) { ret = -3; goto exit_r2c_num; /* wrong number of cuts = num */ } /* !!!!!!!! check that there are no derivatives inside a derivative */ comp_num = 0; /* here it is the number of removed cuts */ for ( i = 0; i < num_cuts_to_check; i += j ) { for ( j = n = 0; j < 2; j ++ ) { int atj = (int)ap[i].at[j]; if ( da[atj].typ[0] && at[atj].neighbor[(int)da[atj].ord[0]] == ap[i].at[1-j] ) { k = j; n ++; m_at = atj; m_ord = 0; } else if ( da[atj].typ[1] && at[atj].neighbor[(int)da[atj].ord[1]] == ap[i].at[1-j] ) { k = j; n ++; m_at = atj; m_ord = 1; } } if ( n != 1 ) { ret = -3; goto exit_r2c_num; /* wrong atom pair */ } if ( (da[m_at].typ[m_ord] & DERIV_RING) ) { n = (int)ap[i].at[k]; /* atom inside the derivation attachment */ j = 2; /* number of bonds to cut */ if ( i+j > num_cuts_to_check || (int)ap[i+1].at[0] != n && (int)ap[i+1].at[1] != n ) { ret = -3; goto exit_r2c_num; /* wrong atom pair */ } } else { n = ap[i].at[1-k]; /* atom inside the derivation attachment */ j = 1; /* number of bonds to cut */ } /* at[n] belongs to the derivation agent */ cur_num_at = mark_atoms_ap( at, n, ap+i, j, 0, 1 ); for ( k = 0; k < num_cuts_to_check; k ++ ) { if ( k == i || k==i+j-1 ) continue; if ( at[(int)ap[k].at[0]].at_type || at[(int)ap[k].at[1]].at_type ) { /* unmark the cut: found a cut inside the derivatizing agent */ da[m_at].typ[m_ord] |= DERIV_UNMARK; num_cuts -= 1; num_cut_pieces -= 1; if ( j == 2 ) { da[m_at].typ[1-m_ord] |= DERIV_UNMARK; num_cuts -= 1; num_ring_cuts -= 2; } comp_num ++; break; } } UnMarkOtherIndicators( at, num_atoms ); } if ( comp_num ) { for ( i = 0; i < num_atoms; i ++ ) { if ( da[i].typ[0] & DERIV_UNMARK ) { da[i].num[0] = da[i].num[1]; da[i].ord[0] = da[i].ord[1]; da[i].typ[0] = da[i].typ[1]; da[i].typ[1] = 0; j = 0; } else { j = 1; } if ( da[i].typ[j] & DERIV_UNMARK ) { da[i].typ[j] = 0; } } #if ( COUNT_ALL_NOT_DERIV == 1 ) num_cuts_to_check = num_cuts; #else num_cuts_to_check = num_ring_cuts; #endif if ( num_cuts < 0 || num_ring_cuts < 0 || num_cut_pieces < 0 ) { ret = -3; goto exit_r2c_num; /* wrong number of cuts = num */ } goto repeat_without_deriv_ring; } /* sort the bonds for subsequent searching by bisections */ if ( num_cuts_to_check > 1 ) { qsort( ap, num_cuts_to_check, sizeof(ap[0]), cmp_r2c_atpair); } /* mark components to be disconnected */ comp_num = 0; /* number of components */ cur_num_at = 0; /* number of atoms left after disconnecting the derivatizing agent */ UnMarkOtherIndicators( at, num_atoms ); for ( i = 0; i < num_cuts_to_check; i ++ ) { n = 0; for ( j = 0; j < 2; j ++ ) { if ( da[(int)ap[i].at[j]].typ[0] ) { k = j; n ++; } } if ( n != 1 ) { ret = -3; goto exit_r2c_num; /* wrong atom pair */ } n = ap[i].at[k]; /* marked atom */ if ( (da[n].typ[0] & DERIV_RING) ) { n = ap[i].at[1-k]; } /* at[n] belongs to the derivation agent */ if ( !at[n].at_type ) { comp_num ++; cur_num_at = mark_atoms_ap( at, n, ap, num_cuts_to_check, cur_num_at, comp_num ); } } if ( comp_num > 1 ) { /* eliminate offending DERIV_RING type derivatives */ if ( num_ring_cuts <= 2 ) { ret = -99; goto exit_r2c_num; } n = 0; for ( i = j = 0; i < num_atoms; i ++ ) { if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { int at1a = at[i].neighbor[(int)da[i].ord[0]]; int at2a = at[i].neighbor[(int)da[i].ord[1]]; if ( at[at1a].at_type != at[at2a].at_type ) { da[i].typ[0] = 0; /* eliminate this cut */ da[i].typ[1] = 0; n ++; num_cuts_to_check -= 2; num_cuts -= 2; num_ring_cuts -= 2; num_cut_pieces -= 1; } } } if ( n > 0 && num_cuts_to_check > 2 ) { goto repeat_without_deriv_ring; } } ret = 0; exit_r2c_num: inchi_free( ap ); UnMarkOtherIndicators( at, num_atoms ); if ( ret < 0 || num_cuts_to_check >= 2 && cur_num_at < MIN_AT_LEFT_DERIV ) { goto exit_function; /* unexpected error or nothing left */ } } if ( !num_cuts ) { continue; /*goto exit_function;*/ } /* eliminate derivatives that are not in the list */ num_cuts = EliminateDerivNotInList( at, da, num_atoms ); if ( num_cuts < 0 ) { ret = num_cuts; goto exit_function; } /* make cuts */ num_cuts = 0; for ( i = num = 0; i < num_atoms; i ++ ) { for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) ; switch( len ) { case 0: continue; case 1: /* single cut: unconditional */ make_single_cut( at, da, i, 0 ); num_cuts += 1; continue; case 2: if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) || da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { /* double cut, unconditional */ make_single_cut( at, da, i, 1 ); make_single_cut( at, da, i, 0 ); num_cuts += 1; continue; } if ( da[i].typ[0] == da[i].typ[1] ) { /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ if ( da[i].num[0] > da[i].num[1] ) { make_single_cut( at, da, i, 1 ); num_cuts += 1; } else if ( da[i].num[0] < da[i].num[1] ) { make_single_cut( at, da, i, 0 ); num_cuts += 1; } continue; } ret = -88; goto exit_function; /* unexpected */ case 3: if ( da[i].typ[0] == da[i].typ[1] && da[i].typ[0] == da[i].typ[2] && da[i].typ[0] == DERIV_AMINE_tN ) { int x, y, z; x = (da[i].num[0] < da[i].num[1])? 0 : 1; x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ z = (da[i].num[0] < da[i].num[1])? 1 : 0; z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ y = ((x+1)^(z+1))-1; /* median */ if (da[i].num[x] == da[i].num[z] ) continue; /* all deriv. agents have same size */ /* two smallest */ if ( da[i].num[x] == da[i].num[y] && x < y ) { int t = x; /* first cut x > y */ x = y; y = t; } make_single_cut( at, da, i, x ); num_cuts += 1; if ( da[i].num[x] == da[i].num[y] ) { /* equally small */ make_single_cut( at, da, i, y ); num_cuts += 1; } continue; } ret = -88; goto exit_function; /* unexpected */ case 4: if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { int n01 = inchi_max( da[i].num[0], da[i].num[1] ); int n23 = inchi_max( da[i].num[2], da[i].num[3] ); if ( n01 < n23 ) { make_single_cut( at, da, i, 1 ); make_single_cut( at, da, i, 0 ); num_cuts += 1; } else if ( n01 > n23 ) { make_single_cut( at, da, i, 3 ); make_single_cut( at, da, i, 2 ); num_cuts += 1; } continue; } } } nTotNumCuts += num_cuts; #if ( REMOVE_CUT_DERIV == 1 ) /* normally YES */ if ( num_cuts ) { /* remove marked with Tritium disconnected derivative attachments */ ORIG_ATOM_DATA Orig_inp_data1, *orig_inp_data1; INP_ATOM_DATA *inp_cur_data1 = NULL; int num_components1, i_component1, num_component_left=0; orig_inp_data1 = &Orig_inp_data1; memset( orig_inp_data1, 0, sizeof(orig_inp_data1[0]) ); UnMarkRingSystemsInp( at, num_atoms ); UnMarkOtherIndicators( at, num_atoms ); UnMarkOneComponent( at, num_atoms ); for (i = 0; i < num_atoms; i ++ ) { orig_inp_data1->num_inp_bonds += at[i].valence; } orig_inp_data1->num_inp_bonds /= 2; orig_inp_data1->num_inp_atoms = num_atoms; orig_inp_data1->at = at; /* = from inp_cur_data[i_component].at */ num_components1 = MarkDisconnectedComponents( orig_inp_data1, 0 ); inp_cur_data1 = (INP_ATOM_DATA *)inchi_calloc( num_components1, sizeof(inp_cur_data1[0]) ); /* extract components and discard disconnected derivatizing agents */ for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { CreateInpAtomData( inp_cur_data1+i_component1, orig_inp_data1->nCurAtLen[i_component1], 0 ); inp_cur_data1[i_component1].num_at = ExtractConnectedComponent( orig_inp_data1->at, orig_inp_data1->num_inp_atoms, i_component1+1, inp_cur_data1[i_component1].at ); /* error processing */ if ( inp_cur_data1[i_component1].num_at <= 0 || orig_inp_data1->nCurAtLen[i_component1] != inp_cur_data1[i_component1].num_at ) { ret = -(i_component1+1); /* severe error */ break; } /* if the component has tritium then discard: it is a derivatizing agent */ for (i = 0; i < inp_cur_data1[i_component1].num_at; i ++ ) { if ( inp_cur_data1[i_component1].at[i].num_iso_H[1] ) { inp_cur_data1[i_component1].at[i].num_iso_H[1] = 0; /* remove deuterium */ } if ( inp_cur_data1[i_component1].at[i].num_iso_H[2] ) { FreeInpAtomData( inp_cur_data1+i_component1 ); break; } } } /* merge components into one -- must be only one */ for ( i_component1 = 0, num_atoms = 0; i_component1 < num_components1; i_component1 ++ ) { num_atoms += inp_cur_data1[i_component1].num_at; } at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); cur_num_at = 0; for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { /* clean and prepare */ if ( !inp_cur_data1[i_component1].num_at ) continue; /* removed derivatizing object */ /*UnMarkOneComponent( inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at );*/ /* merge one by one */ cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at ); FreeInpAtomData( inp_cur_data1+i_component1 ); /* cleanup */ num_component_left ++; } /* replace the component */ /* order of the following two statements is critically important */ UnMarkDisconnectedComponents( orig_inp_data1 ); /* orig_inp_data1->at is same as inp_cur_data[i_component].at */ FreeInpAtomData( inp_cur_data+i_component ); /* cleanup the original component */ inp_cur_data[i_component].at = at; inp_cur_data[i_component].num_at = cur_num_at; inchi_free( inp_cur_data1 ); } #endif } if ( nTotNumCuts ) { /* merge components into one */ for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { num_atoms += inp_cur_data[i].num_at; } at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); cur_num_at = 0; for ( i = 0; i < num_components; i ++ ) { /* clean and prepare */ UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); /* merge one by one */ cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); } /* replace orig_inp_data */ if ( cur_num_at == num_atoms ) { inchi_free( orig_inp_data->at ); orig_inp_data->at = at; orig_inp_data->num_inp_atoms = cur_num_at; if ( orig_inp_data->szCoord ) { inchi_free( orig_inp_data->szCoord ); orig_inp_data->szCoord = NULL; } UnMarkDisconnectedComponents( orig_inp_data ); } else { if ( at ) { inchi_free( at ); at = NULL; } ret = -999; /* num atoms mismatch */ } } exit_function: if ( da ) { inchi_free( da ); da = NULL; } for ( i_component = 0; i_component < num_components; i_component ++ ) { FreeInpAtomData( inp_cur_data+i_component ); } inchi_free( inp_cur_data ); inp_cur_data = NULL; return ret? ret : nTotNumCuts; } #endif /* UNDERIVATIZE */ /********************************************************************/ #if ( RING2CHAIN == 1 ) /* type=1 (incl sugars: W=O, A=C(sat), Z=C(sat), Y=O, B=C(sat)-OH A---W A---WH / | / | | ---> | \ | \ B---Z---YH B---Z===Y | | | | C(opt) C(opt) type=2 [not implemented] R---W R---WH / \ / | Z ---> | Z \ / \ // R---YH R---Y */ #define R2C_EMPTY 127 typedef struct tagRing2Chain { /* atom Z */ char type; /* 1 => sugar-like */ char ordW; /* ordering number of W-neighbor; bond to break; H to add */ char ordY; /* ordering number of YH-neighbor; bond to increment; H to remove */ char ordC; /* atom B = C(sat) */ char ordCopt; /* if exists, saturated C connected by a chain-bond to Z */ } R2C_AT; int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ); int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ); /********************************************************************/ int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ) { int i, j, neigh, neighneigh, nRingSystem, num_found; R2C_AT da1; if ( at[iZ].valence > 4 ) return 0; if ( at[iZ].valence != at[iZ].chem_bonds_valence ) return 0; /* approach limitation: no double bonds */ if ( at[iZ].el_number != el_number_C ) return 0; /* sugar-specific */ if ( at[iZ].nNumAtInRingSystem < 5 ) return 0; /* not in a suitable ring */ if ( !at[iZ].bCutVertex ) return 0; /* recognize only type 1 for now */ nRingSystem = at[iZ].nRingSystem; memset ( &da1, R2C_EMPTY, sizeof(da1) ); for ( i = 0, num_found = 0; i < at[iZ].valence; i ++ ) { neigh = at[iZ].neighbor[i]; if ( at[neigh].charge || at[neigh].radical ) return 0; if ( at[neigh].el_number == el_number_O && at[neigh].valence == 1 && at[neigh].chem_bonds_valence == 1 && at[neigh].num_H == 1 ) { /* found Z-OH, i.e. Z-YH */ if ( da1.ordY == R2C_EMPTY ) { da1.ordY = i; num_found ++; continue; } else { return 0; } } if ( at[neigh].el_number == el_number_O && at[neigh].valence == 2 && at[neigh].chem_bonds_valence == 2 && at[neigh].num_H == 0 && at[neigh].nRingSystem == nRingSystem ) { /* found Z-O-, i.e. Z-W- */ if ( da1.ordW == R2C_EMPTY ) { /* j = index of the oppozite to at[iZ] neighbor of at[neigh] */ j = (at[neigh].neighbor[0] == iZ); neighneigh = at[neigh].neighbor[j]; if ( at[neighneigh].valence != at[neighneigh].chem_bonds_valence || at[neighneigh].el_number != el_number_C ) return 0; /* sugar-specific */ da1.ordW = i; num_found ++; continue; } else { return 0; } } if ( at[neigh].el_number == el_number_C && at[neigh].valence > 2 && at[neigh].chem_bonds_valence == at[neigh].valence && at[neigh].num_H <= 1 && at[neigh].nRingSystem == nRingSystem ) { /* sugar-specfic: carbon in the ring should have -OH neighbor */ int iOH; for ( j = 0; j < at[neigh].valence; j ++ ) { iOH = at[neigh].neighbor[j]; if ( at[iOH].el_number == el_number_O && at[iOH].valence == 1 && at[iOH].chem_bonds_valence == 1 && at[iOH].num_H == 1 && !at[iOH].charge && !at[iOH].radical ) { if ( da1.ordC == R2C_EMPTY ) { da1.ordC = i; num_found ++; break; } else { return 0; } } } if ( j < at[neigh].valence ) continue; } if ( at[neigh].el_number == el_number_C && at[neigh].chem_bonds_valence == at[neigh].valence && at[neigh].nRingSystem != nRingSystem ) { /* extra carbon neighbor of Z */ if ( da1.ordCopt == R2C_EMPTY ) { da1.ordCopt = i; continue; } } return 0; /* unexpectd neighbor */ } if (num_found == 3) { da1.type = 1; da[iZ] = da1; return 1; /* disconnection found */ } return 0; } /********************************************************************/ int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ) { int ret = -1; /* error flag */ int iordW = (int)da[iZ].ordW; /* ord of the bond in iZ */ int iordY = (int)da[iZ].ordY; /* ord of the bond in iZ */ int iordC = (int)da[iZ].ordC; int iW, iY, num_iso_H, i, jordZ; AT_NUMB *p; if ( da[iZ].type != 1 ) { return 0; } if ( 0 > iordW || iordW >= at[iZ].valence || 0 > iordY || iordY >= at[iZ].valence || 0 > iordC || iordC >= at[iZ].valence /* suger-specific*/) { return -1; /* program error */ } /* find other da[] that affect at[iZ] */ iW = at[iZ].neighbor[iordW]; /* opposite atom to disconnect and add H */ iY = at[iZ].neighbor[iordY]; /* opposite atom to increment the bond and remove H*/ if ( !at[iY].num_H || at[iZ].bond_type[iordY] != BOND_TYPE_SINGLE ) { return -2; /* program error */ } /* increment at[iZ]--at[iY] bond */ p = is_in_the_list( at[iY].neighbor, (AT_NUMB) iZ, at[iY].valence ); if ( !p ) { return -3; /* program error */ } jordZ = p - at[iY].neighbor; at[iZ].bond_type[iordY] ++; at[iZ].chem_bonds_valence ++; at[iY].bond_type[jordZ] ++; at[iY].chem_bonds_valence ++; /* disconnect at[iZ]--at[iW] bond */ ret = DisconnectInpAtBond( at, NULL, iZ, iordW ); if ( ret != 1 ) { return -4; /* program error */ } /* disconnection succeeded */ /* transfer H from at[iY] to at[iW] */ num_iso_H = NUM_ISO_H(at, iY); if ( at[iY].num_H == num_iso_H ) { for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { if ( at[iY].num_iso_H[i] ) { at[iY].num_iso_H[i] --; at[iW].num_iso_H[i] ++; } } } at[iY].num_H --; at[iW].num_H ++; return 1; } /********************************************************************/ int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ) { int ret = 0, i, j, n, num_atoms, num_components, nFound, num, num_cuts, iZ, cur_num_at; inp_ATOM *at = orig_inp_data->at; INP_ATOM_DATA *inp_cur_data = NULL; R2C_AT *da = NULL; set_R2C_el_numbers( ); /* prepare */ num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ orig_inp_data->num_inp_atoms = num_atoms; /* initialize */ UnMarkDisconnectedComponents( orig_inp_data ); num_cuts = 0; /* mark */ num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); iZ = -1; for ( j = 0; j < num_components; j ++ ) { CreateInpAtomData( inp_cur_data+j, orig_inp_data->nCurAtLen[j], 0 ); inp_cur_data[j].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, j+1, inp_cur_data[j].at ); /* error processing */ if ( inp_cur_data[j].num_at <= 0 || orig_inp_data->nCurAtLen[j] != inp_cur_data[j].num_at ) { ret = -(j+1); /* severe error */ goto exit_function; } /* initialize */ num_atoms = inp_cur_data[j].num_at; at = inp_cur_data[j].at; add_DT_to_num_H( num_atoms, at ); UnMarkRingSystemsInp( at, num_atoms ); UnMarkOtherIndicators( at, num_atoms ); UnMarkOneComponent( at, num_atoms ); MarkRingSystemsInp( at, num_atoms, 0 ); ret = mark_arom_bonds( at, num_atoms ); if ( ret < 0 ) { goto exit_function; } ret = 0; if ( da ) inchi_free( da ); da = (R2C_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); /* detect ring-to-chain possibilities */ nFound = 0; for ( i = 0, num=0; i < num_atoms; i ++ ) { if ( at[i].bCutVertex /* type 1 specific*/ && !da[i].type ) { num += (n=detect_r2c_Zatom( at, da, i )); if ( n == 1 ) iZ = i; UnMarkOtherIndicators( at, num_atoms ); } } if ( num == 1 ) { /* convert ring to chain: make single cut */ ret = cut_ring_to_chain( at, da, iZ ); if ( ret < 0 ) { goto exit_function; } num_cuts += (ret == 1); } else if ( num ) { /* allocate an array of bonds to be cut */ R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( sizeof(ap[0]) * num ); AT_NUMB comp_num = 0; if ( !ap ) { ret = -1; /* malloc failure */ goto exit_function; } /* fill out the array of bonds to be cut */ for ( i = j = 0; i < num_atoms; i ++ ) { if ( da[i].type == 1 ) { AT_NUMB at1 = i; AT_NUMB at2 = at[i].neighbor[(int)da[i].ordW]; if ( j >= num ) { ret = -2; goto exit_r2c_num; /* wrong number of cuts = num */ } n = ( at1 > at2 ); ap[j].at[n] = at1; ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ j ++; } } if ( j != num ) { ret = -3; goto exit_r2c_num; /* wrong number of cuts = num */ } /* sort the bonds for subsequent searching by bisections */ qsort( ap, num, sizeof(ap[0]), cmp_r2c_atpair); /* mark components to be disconnected */ for ( i = 0; i < num; i ++ ) { for ( j = 0; j < 2; j ++ ) { if ( !at[ap[i].at[j]].at_type ) { comp_num ++; mark_atoms_ap( at, (int)ap[i].at[j], ap, num, 0, comp_num ); } } } /* convert ring to chain */ for ( i = 0; i < num; i ++ ) { int i1 = ap[i].at[0]; int i2 = ap[i].at[1]; iZ = -1; if ( at[i1].at_type == at[i2].at_type ) { /* by definition, there are no adjacent iZ atoms; one iZ atom per bond */ if ( da[i1].type == 1 && at[i1].neighbor[(int)da[i1].ordW] == i2 ) { iZ = i1; } else if ( da[i2].type == 1 && at[i2].neighbor[(int)da[i2].ordW] == i1 ) { iZ = i2; } else { ret = -3; goto exit_r2c_num; } ret = cut_ring_to_chain( at, da, iZ ); if ( ret < 0 ) { goto exit_r2c_num; } num_cuts += (ret == 1); } } ret = 0; exit_r2c_num: inchi_free( ap ); UnMarkOtherIndicators( at, num_atoms ); if ( ret < 0 ) { goto exit_function; } } } if ( num_cuts ) { /* merge components into one */ for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { num_atoms += inp_cur_data[i].num_at; } at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); cur_num_at = 0; for ( i = 0; i < num_components; i ++ ) { /* clean and prepare */ UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); /* merge one by one */ cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); } /* replace orig_inp_data */ if ( cur_num_at == num_atoms ) { inchi_free( orig_inp_data->at ); orig_inp_data->at = at; orig_inp_data->num_inp_atoms = cur_num_at; if ( orig_inp_data->szCoord ) { inchi_free( orig_inp_data->szCoord ); orig_inp_data->szCoord = NULL; } UnMarkDisconnectedComponents( orig_inp_data ); } else { if ( at ) { inchi_free( at ); at = NULL; } ret = -999; /* num atoms mismatch */ } } exit_function: if ( da ) { inchi_free( da ); da = NULL; } for ( j = 0; j < num_components; j ++ ) { FreeInpAtomData( inp_cur_data+j ); } inchi_free( inp_cur_data ); inp_cur_data = NULL; return ret? ret : num_cuts; } #endif /* RING2CHAIN */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichinorm.h000066400000000000000000000117351271037650300235410ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHINORM_H__ #define __INCHINORM_H__ #include "mode.h" #include "ichi_bns.h" #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* main normalization procedure */ int mark_alt_bonds_and_taut_groups (inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone); int MarkTautomerGroups(inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD); int MarkChargeGroups(inp_ATOM *at, int num_atoms, C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD); int MarkSaltChargeGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD); int MarkSaltChargeGroups2(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD); int MergeSaltTautGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS); int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info); int remove_terminal_HDT(int num_atoms, inp_ATOM *at, int bFixTermHChrg); int RemoveExcessiveImplicitH(int num_atoms, int num_removed_H, inp_ATOM *at); int add_DT_to_num_H(int num_atoms, inp_ATOM *at); int MarkRingSystemsInp(inp_ATOM *at, int num_atoms, int start); int free_t_group_info(T_GROUP_INFO *t_group_info); int make_a_copy_of_t_group_info(T_GROUP_INFO *t_group_info, T_GROUP_INFO *t_group_info_orig); int set_tautomer_iso_sort_keys(T_GROUP_INFO *t_group_info); int CountTautomerGroups(sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); int CountTautomerGroupsInpAt(inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); int SortTautomerGroupsAndEndpoints(T_GROUP_INFO *t_group_info, int num_atoms, int num_at_tg, AT_RANK *nRank); int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, const AT_RANK *nAtomNumber, AT_ISOTOPIC *LinearCTIsotopic, int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic); int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, const AT_RANK *nRank, const AT_RANK *nAtomNumber, const AT_RANK *nSymmRank, const AT_RANK *nRankIso, const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, AT_TAUTOMER *LinearCTTautomer, int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, AT_ISO_TGROUP *LinearCTIsotopicTautomer, int nMaxLenLinearCTIsotopicTautomer, int *pnLenLinearCTIsotopicTautomer, T_GROUP_INFO *t_group_info); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHINORM_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiparm.c000066400000000000000000000037511271037650300235170ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include /* #include */ #include #include #include "mode.h" /* moved from below, suggestion by David Mosenkis */ #ifndef COMPILE_ANSI_ONLY #include #endif #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "ichidrp.h" #include "ichierr.h" #include "ichimain.h" #include "extr_ct.h" #ifdef TARGET_LIB_FOR_WINCHI #include "ichi_lib.h" #endif #include "ichicomp.h" #if ( ADD_CMLPP == 1 ) #include "readcml.hpp" #include "debug.h" #endif #include "ichi_io.h" #include "ichiparm.h" Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiparm.h000066400000000000000000003310201271037650300235150ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ int DetectInputINChIFileType(FILE **inp_file, INPUT_PARMS *ip, const char *fmode); void HelpCommandLineParmsReduced(INCHI_IOSTREAM *f); /*****************************************************************************************/ int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, char *szSdfDataValue, unsigned long *ulDisplTime, int bReleaseVersion, INCHI_IOSTREAM *log_file) { int i, k, c; const char *q; unsigned long ul; int nFontSize = -9; int nMode = 0; int nReleaseMode = nMode | (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO); #if ( MIN_SB_RING_SIZE > 0 ) int nMinDbRinSize = MIN_SB_RING_SIZE, mdbr=0; #endif /*int bNotRecognized=0;*/ char szNameSuffix[32], szOutputPath[512]; int bNameSuffix, bOutputPath; int bMergeAllInputStructures; int bDisconnectSalts = (DISCONNECT_SALTS==1); int bDoNotAddH = 0; int bVer1Options = 1; int bReconnectCoord = (RECONNECT_METALS==1); int bDisconnectCoord = (DISCONNECT_METALS==1); #ifdef TARGET_LIB_FOR_WINCHI /* int bVer1Options = 0; int bReconnectCoord = 1; int bDisconnectCoord = 1; */ int is_gui = 1; int bINChIOutputOptions = INCHI_OUT_EMBED_REC; /* embed reconnected & output full aux info */ int bCompareComponents = CMP_COMPONENTS; #else /* int bVer1Options = 1; int bReconnectCoord = (RECONNECT_METALS==1); int bDisconnectCoord = (DISCONNECT_METALS==1); */ int is_gui = 0; int bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0); /*| INCHI_OUT_NO_AUX_INFO INCHI_OUT_SHORT_AUX_INFO*/ int bCompareComponents = 0; #endif int bDisconnectCoordChkVal = (CHECK_METAL_VALENCE == 1); int bMovePositiveCharges = (MOVE_CHARGES==1); int bAcidTautomerism = (DISCONNECT_SALTS==1)? (TEST_REMOVE_S_ATOMS==1? 2:1):0; int bUnchargedAcidTaut = (CHARGED_SALTS_ONLY==0); int bMergeSaltTGroups = (DISCONNECT_SALTS==1); int bDisplayCompositeResults = 0; #define VER103_DEFAULT_MODE (REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO |\ REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU) INCHI_MODE bVer1DefaultMode = VER103_DEFAULT_MODE; const char *ext[MAX_NUM_PATHS+1]; const char *pArg; double t; int bRecognizedOption; int bDisplay = 0; int bNoStructLabels = 0; int bOutputMolfileOnly = 0; int bOutputMolfileDT = 0; int bOutputMolfileSplit = 0; int bForcedChiralFlag = 0; #if ( READ_INCHI_STRING == 1 ) int bDisplayIfRestoreWarnings = 0; #endif #ifdef TARGET_LIB_FOR_WINCHI int bXml = INCHI_OUT_XML; #else int bXml = INCHI_OUT_PLAIN_TEXT; #endif int bTgFlagVariableProtons = 1; int bTgFlagHardAddRenProtons = 1; #ifdef STEREO_WEDGE_ONLY int bPointedEdgeStereo = STEREO_WEDGE_ONLY; /* NEWPS TG_FLAG_POINTED_EDGE_STEREO*/ #endif #if ( FIX_ADJ_RAD == 1 ) int bFixAdjacentRad = 0; #endif int bAddPhosphineStereo = 1; int bAddArsineStereo = 1; int bFixSp3bug = 1; int bFixFB2 = 1; int bKetoEnolTaut = 0; int b15TautNonRing = 0; int bStdFormat = 1; int bHashKey = 0; int bHashXtra1 = 0; int bHashXtra2 = 0; ext[0] = ".mol"; ext[1] = bVer1Options? ".txt" : ".ich"; ext[2] = ".log"; ext[3] = ".prb"; ext[4] = ""; #if ( MAX_NUM_PATHS < 4 ) #error Wrong initialization #endif /* init table parms */ memset(ip, 0, sizeof(*ip)); /* default are standard InChI generation options */ bVer1DefaultMode &= ~REQ_MODE_BASIC; /* "FIXEDH - OFF" */ bReconnectCoord = 0; /* "RECMET - OFF" */ bPointedEdgeStereo = 1; /* "NEWPS" */ ip->bFixNonUniformDraw = 1; /* "FNUD" */ #ifndef COMPILE_ANSI_ONLY strcpy( ip->tdp.ReqShownFoundTxt[ilSHOWN], "Shown" ); ip->dp.sdp.tdp = &ip->tdp; ip->dp.pdp = &ip->pdp; #endif memset( szNameSuffix, 0, sizeof(szNameSuffix) ); bNameSuffix = 0; memset(szOutputPath, 0, sizeof(szOutputPath) ); bOutputPath = 0; bMergeAllInputStructures = 0; *ulDisplTime = 0; #ifdef TARGET_API_LIB ip->msec_MaxTime = 0; /* milliseconds, default = unlimited in libinchi */ #else ip->msec_MaxTime = 60000; /* milliseconds, default = 60 sec */ #endif if ( bReleaseVersion ) { /* normal */ /*ip->bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT;*/ /*ip->bXml = 1;*/ ip->bAbcNumbers = 0; ip->bCtPredecessors = 0; /* -- testing -- ip->bXml = 0; ip->bAbcNumbers = 1; */ } else { /*bXml = INCHI_OUT_PLAIN_TEXT;*/ nReleaseMode = 0; } if ( bVer1Options ) { bNameSuffix = 1; szNameSuffix[0] = '\0'; } /* Parse */ for ( i = 1; i < argc; i ++ ) { /* if ( !(bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) */ if ( is_gui && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) { /*=== parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ pArg = argv[i]+1; /*--- Input options ---*/ if ( !stricmp( pArg, "INPAUX" )) { if (INPUT_NONE == ip->nInputType) ip->nInputType = INPUT_INCHI_PLAIN; } else if ( INPUT_NONE == ip->nInputType && (!memicmp( pArg, "SDF", 3 )) && ( pArg[3] == ':' ) ) { k = 0; mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); LtrimRtrim( ip->szSdfDataHeader, &k ); if ( k ) { ip->pSdfLabel = ip->szSdfDataHeader; ip->pSdfValue = szSdfDataValue; ip->nInputType = INPUT_SDFILE; } else { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; ip->nInputType = INPUT_MOLFILE; } } else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "MOL" ) ) { ip->nInputType = INPUT_MOLFILE; } else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "SDF" ) ) { ip->nInputType = INPUT_MOLFILE; } #if ( ADD_CMLPP == 1 ) else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) { /* CMLfile label */ ip->nInputType = INPUT_CMLFILE; } #endif else if ( !memicmp( pArg, "START:", 6 ) ) { ip->first_struct_number = strtol(pArg+6, NULL, 10); } else if ( !memicmp( pArg, "END:", 4 ) ) { ip->last_struct_number = strtol(pArg+4, NULL, 10); } #ifdef BUILD_WITH_ENG_OPTIONS else if ( !memicmp( pArg, "RSB:", 4 ) ) { mdbr = (int)strtol(pArg+4, NULL, 10); } else if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) { bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); } else if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) { bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); } else if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) { bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); } else if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) { bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); } else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); } else if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) { bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); } else if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) { bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16));; } else if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) { bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; /*else bNotRecognized = 2*bReleaseVersion;*/ } #endif /*--- Output options ---*/ #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) else if ( !stricmp( pArg, "Tabbed" ) ) { bXml |= INCHI_OUT_TABBED_OUTPUT; } #endif else if ( !stricmp( pArg, "NOLABELS" ) ) { bNoStructLabels = 1; } else if ( !stricmp( pArg, "SAVEOPT" ) ) { bINChIOutputOptions |= INCHI_OUT_SAVEOPT; } else if ( !stricmp( pArg, "AUXNONE" ) ) { /* no aux. info */ bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; } #if ( defined(BUILD_WITH_ENG_OPTIONS) || defined(TARGET_LIB_FOR_WINCHI) ) else if ( !stricmp( pArg, "SDFID" ) ) { ip->bGetSdfileId = 1; } else if ( !stricmp( pArg, "XML" ) ) { bXml &= ~INCHI_OUT_PLAIN_TEXT; bXml |= INCHI_OUT_XML; /*bNotRecognized = 2*bReleaseVersion;*/ } else if ( !stricmp( pArg, "PLAIN" ) ) { bXml |= INCHI_OUT_PLAIN_TEXT; bXml &= ~INCHI_OUT_XML; } else if ( !stricmp( pArg, "ANNPLAIN" ) ) { bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; } else if ( !stricmp( pArg, "ANNXML" ) ) { bXml |= INCHI_OUT_XML_TEXT_COMMENTS; bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; } else if ( !memicmp( pArg, "AUXINFO:", 8 ) && isdigit(UCINT pArg[8]) ) { k = strtol(pArg+8, NULL, 10); if ( k == 0 ) { bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; } else if ( k == 1 ) { bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include full aux info */ } else if ( k == 2 ) { bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; } else { bINChIOutputOptions = k; /* override everything */ } } else if ( !stricmp( pArg, "MERGE" ) ) { bMergeAllInputStructures = 1; } else if ( !stricmp( pArg, "PGO" ) ) { ip->bSaveAllGoodStructsAsProblem = 1; } else if ( !stricmp( pArg, "DCR" ) ) { bDisplayCompositeResults = 1; } else if ( !stricmp( pArg, "DSB" ) ) { nMode |= REQ_MODE_NO_ALT_SBONDS; } else if ( !stricmp( pArg, "NOHDR" ) ) { bNoStructLabels = 1; } else if ( !stricmp( pArg, "NoVarH" ) ) { bTgFlagVariableProtons = 0; } #endif /* BUILD_WITH_ENG_OPTIONS */ /*--- All modes (std and non-std InChI) structure perception options ---*/ else if ( !stricmp( pArg, "SNON" ) ) { bVer1DefaultMode &= ~REQ_MODE_STEREO; nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); } else if ( !stricmp( pArg, "NEWPSOFF" ) ) { bPointedEdgeStereo = 0; } else if ( !stricmp( pArg, "DONOTADDH" ) ) { bDoNotAddH = 1; } /* Non-standard */ #ifndef USE_STDINCHI_API /* Non-std InChI structure perception options */ else if ( !stricmp( pArg, "SREL" ) ) { if ( nMode & REQ_MODE_RACEMIC_STEREO ) { nMode ^= REQ_MODE_RACEMIC_STEREO; } if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) { nMode ^= REQ_MODE_CHIR_FLG_STEREO; } nMode |= REQ_MODE_RELATIVE_STEREO; nMode |= REQ_MODE_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "SRAC" ) ) { if ( nMode & REQ_MODE_RELATIVE_STEREO ) { nMode ^= REQ_MODE_RELATIVE_STEREO; } if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) { nMode ^= REQ_MODE_CHIR_FLG_STEREO; } nMode |= REQ_MODE_RACEMIC_STEREO; nMode |= REQ_MODE_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "SUCF" ) ) { if ( nMode & REQ_MODE_RELATIVE_STEREO ) { nMode ^= REQ_MODE_RELATIVE_STEREO; } if ( nMode & REQ_MODE_RACEMIC_STEREO ) { nMode ^= REQ_MODE_RACEMIC_STEREO; } nMode |= REQ_MODE_CHIR_FLG_STEREO; /* stereo defined by the Chiral flag */ nMode |= REQ_MODE_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "ChiralFlagON" ) ) { /* used only with /SUCF */ /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; } else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) { /* used only with /SUCF */ /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; } /*--- Non-std InChI creation options ---*/ else if ( !stricmp( pArg, "SUU" ) ) { /* include omitted undef/unknown stereo */ bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); bStdFormat = 0; } else if ( !stricmp( pArg, "SLUUD" ) ) { /* make labels for unknown and undefined stereo different */ bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; bStdFormat = 0; } /* FixedH */ else if ( !stricmp( pArg, "FIXEDH" ) ) { bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ bStdFormat = 0; } /* RecMet */ else if ( !stricmp( pArg, "RECMET" ) ) { /* reconnect metals */ bReconnectCoord = 1; bStdFormat = 0; } #if ( KETO_ENOL_TAUT == 1 ) else if ( !stricmp( pArg, "KET" ) ) { bKetoEnolTaut = 1; bStdFormat = 0; } #endif #if ( TAUT_15_NON_RING == 1 ) else if ( !stricmp( pArg, "15T" ) ) { b15TautNonRing = 1; bStdFormat = 0; } #endif #endif /*--- Generation options ---*/ /* InChIKey/InChI hash */ else if ( !stricmp( pArg, "Key" ) ) { bHashKey = 1; } else if ( !stricmp( pArg, "XHash1" ) ) { bHashXtra1 = 1; } else if ( !stricmp( pArg, "XHash2" ) ) { bHashXtra2 = 1; } /*--- (engineering) Undo bug/draw fixes options ---*/ #ifdef BUILD_WITH_ENG_OPTIONS else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) { bFixSp3bug = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FBOFF" ) ) { bFixSp3bug = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FB2OFF" ) ) { bFixFB2 = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "SPXYZOFF" ) ) { bAddPhosphineStereo = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "SASXYZOFF" ) ) { bAddArsineStereo = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FNUDOFF" ) ) { ip->bFixNonUniformDraw = 0; bStdFormat = 0; } /*--- (hidden) Old structure-perception and InChI creation options ---*/ /*--- (engineering) Old structure-perception and InChI creation options ---*/ else if ( !stricmp( pArg, "NOUUSB" ) ) { nMode |= REQ_MODE_SB_IGN_ALL_UU; bStdFormat = 0; } else if ( !stricmp( pArg, "NOUUSC" ) ) { nMode |= REQ_MODE_SC_IGN_ALL_UU; bStdFormat = 0; } #if ( FIX_ADJ_RAD == 1 ) else if ( !stricmp( pArg, "FixRad" ) ) { bFixAdjacentRad = 1; bStdFormat = 0; } #endif #if ( UNDERIVATIZE == 1 ) else if ( !stricmp( pArg, "DoDRV" ) ) { ip->bUnderivatize = 1; bStdFormat = 0; } #endif #if ( RING2CHAIN == 1 ) else if ( !stricmp( pArg, "DoR2C" ) ) { ip->bRing2Chain = 1; bStdFormat = 0; } #endif #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) else if ( !stricmp( pArg, "DoneOnly" ) ) { ip->bIngnoreUnchanged = 1; bStdFormat = 0; } #endif else if ( !stricmp( pArg, "NoADP" ) ) { bTgFlagHardAddRenProtons = 0; bStdFormat = 0; } else if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) { bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) { bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) { bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) { bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) { bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); bStdFormat = 0; } else if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) { bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16)); bStdFormat = 0; } else if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) { bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; /*else bNotRecognized = 2*bReleaseVersion;*/ bStdFormat = 0; } /*--- (hidden) Old output and other options ---*/ else if ( !memicmp( pArg, "O:", 2 ) ) { bNameSuffix = 1; strncpy(szNameSuffix, pArg+2, sizeof(szNameSuffix)-1); } else if ( !memicmp( pArg, "OP:", 3 ) ) { bOutputPath = 1; strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); } else if ( !stricmp( pArg, "ALT" ) ) { ip->bAbcNumbers = 1; bStdFormat = 0; } else if ( !stricmp( pArg, "SCT" ) ) { ip->bCtPredecessors = 1; bStdFormat = 0; } else if ( !stricmp( pArg, "CMP" ) ) { bCompareComponents = CMP_COMPONENTS; } else if ( !stricmp( pArg, "CMPNONISO" ) ) { bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; } else if ( !stricmp( pArg, "PW" ) ) { ip->bSaveWarningStructsAsProblem = 1; } #endif /* BUILD_WITH_ENG_OPTIONS */ else { /*for ( k = 0; c=pArg[k]; k ++ )*/ k = 0; c=pArg[k]; /* prohibit multiple option concatenations, strict syntax check 2008-11-05 DT */ { c = toupper( c ); switch ( c ) { case 'D': bDisplay |= 1; if ( (pArg[k+1] == 'C' || pArg[k+1] == 'c') && !pArg[k+2] ) { bDisplay |= 1; k++; ip->bDisplayEachComponentINChI = 1; } else if ( !pArg[k+1] ) { bDisplay |= 1; } break; case 'W': if ( pArg[k+1] == 'D' ) { /* restore Display Time functionality */ c = 'D'; k ++; } t = strtod( pArg+k+1, (char**)&q ); /* cast deliberately discards 'const' qualifier */ if ( q > pArg+k+1 && errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) { ul = 0; } else { ul = (unsigned long)(t*1000.0); } if ( /*q > pArg+k &&*/ !*q ) { k = q - pArg - 1; /* k will be incremented by the for() cycle */ switch( c ) { case 'D': *ulDisplTime = ul; break; case 'W': ip->msec_MaxTime = ul; break; } } break; case 'F': c = (int)strtol( pArg+k+1, (char**)&q, 10 ); /* cast deliberately discards 'const' qualifier */ if ( q > pArg+k && !*q ) { k = q - pArg - 1; if ( abs(c) > 5 ) { nFontSize = -c; /* font size 5 or less is too small */ } } break; default: if ( !pArg[k+1] ) { switch ( c ) { case 'B': nMode |= REQ_MODE_BASIC; nReleaseMode = 0; /*bNotRecognized = bReleaseVersion;*/ bStdFormat = 0; break; case 'T': nMode |= REQ_MODE_TAUT; nReleaseMode = 0; /*bNotRecognized = bReleaseVersion;*/ break; case 'I': nMode |= REQ_MODE_ISO; nReleaseMode = 0; /*bNotRecognized = bReleaseVersion;*/ break; case 'N': nMode |= REQ_MODE_NON_ISO; nReleaseMode = 0; bStdFormat = 0; /*bNotRecognized = bReleaseVersion;*/ break; case 'S': nMode |= REQ_MODE_STEREO; nReleaseMode = 0; /*bNotRecognized = bReleaseVersion;*/ break; case 'E': if ( nReleaseMode & REQ_MODE_STEREO ) { nReleaseMode ^= REQ_MODE_STEREO; bStdFormat = 0; } break; #ifndef TARGET_LIB_FOR_WINCHI default: inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); #endif } } #ifndef TARGET_LIB_FOR_WINCHI else { inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); } #endif } /* if ( bNotRecognized && bNotRecognized == bReleaseVersion ) { inchi_ios_eprint(stderr, "Unrecognized option: \"%c\".\n", c); bNotRecognized = 0; } */ } } /* if ( bNotRecognized && bNotRecognized == 2*bReleaseVersion ) { inchi_ios_eprint(stderr, "Unrecognized option: \"%s\".\n", argv[i]); bNotRecognized = 0; } */ } /*=== end of parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ else if ( (bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) { /*=== parsing stand-alone/library InChI options ===*/ pArg = argv[i] + 1; bRecognizedOption = 2; bVer1Options += 2; /* always on: REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO */ #ifdef CML_DEBUG printf ("1 argv %d %s\n", i, argv[i]); #endif /*--- Input options ---*/ if ( !stricmp( pArg, "STDIO" ) ) { bNameSuffix = 0; } else if ( !stricmp( pArg, "INPAUX" )) { if (INPUT_NONE == ip->nInputType) ip->nInputType = INPUT_INCHI_PLAIN; } else if ( /* INPUT_NONE == ip->nInputType &&*/ !memicmp( pArg, "SDF:", 4 ) ) { /* SDfile label */ k = 0; mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); LtrimRtrim( ip->szSdfDataHeader, &k ); if ( k ) { ip->pSdfLabel = ip->szSdfDataHeader; ip->pSdfValue = szSdfDataValue; if ( INPUT_NONE == ip->nInputType ) { ip->nInputType = INPUT_SDFILE; } } else { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; if ( INPUT_NONE == ip->nInputType ) { ip->nInputType = INPUT_MOLFILE; } } } #if ( ADD_CMLPP == 1 ) else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) { /* CMLfile label */ ip->nInputType = INPUT_CMLFILE; } #endif else if ( !memicmp( pArg, "START:", 6 ) ) { ip->first_struct_number = strtol(pArg+6, NULL, 10); } else if ( !memicmp( pArg, "END:", 4 ) ) { ip->last_struct_number = strtol(pArg+4, NULL, 10); } #ifdef BUILD_WITH_ENG_OPTIONS else if ( !memicmp( pArg, "RSB:", 4 )) { mdbr = (int)strtol(pArg+4, NULL, 10); } #endif /*--- Output options ---*/ #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) else if ( !stricmp( pArg, "Tabbed" ) ) { bXml |= INCHI_OUT_TABBED_OUTPUT; } #endif else if ( !stricmp( pArg, "NOLABELS" ) ) { bNoStructLabels = 1; } else if ( !stricmp( pArg, "SAVEOPT" ) ) { bINChIOutputOptions |= INCHI_OUT_SAVEOPT; } else if ( !stricmp( pArg, "AUXNONE" ) ) { /* no aux. info */ bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; } else if ( !stricmp( pArg, "OUTPUTSDF" ) ) { /* output SDfile */ bOutputMolfileOnly = 1; } else if ( !stricmp( pArg, "SdfAtomsDT" ) ) { /* output isotopes H as D and T in SDfile */ bOutputMolfileDT = 1; } else if ( !stricmp( pArg, "D" ) ) { /* display the structures */ bDisplay |= 1; } else if ( !memicmp( pArg, "F", 1 ) && (c = (int)strtol( pArg+1, (char**)&q, 10 ), q > pArg+1) ) { nFontSize = -c; /* struct. display font size */ } else if ( !stricmp( pArg, "EQU" ) ) { bCompareComponents = CMP_COMPONENTS; } /*--- All modes (std and non-std InChI) structure perception options ---*/ else if ( !stricmp( pArg, "SNON" ) ) { bVer1DefaultMode &= ~REQ_MODE_STEREO; /* no stereo */ nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); } else if ( !stricmp( pArg, "NEWPSOFF" ) ) { bPointedEdgeStereo = 0; } else if ( !stricmp( pArg, "DONOTADDH" ) ) { bDoNotAddH = 1; } /* Non-standard */ #ifndef USE_STDINCHI_API /* Non-std InChI structure perception options */ else if ( !stricmp( pArg, "SREL" ) ) { bVer1DefaultMode |= REQ_MODE_STEREO; /* relative stereo */ nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_CHIR_FLG_STEREO); nMode |= REQ_MODE_RELATIVE_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "SRAC" ) ) { /* REQ_MODE_CHIR_FLG_STEREO */ bVer1DefaultMode |= REQ_MODE_STEREO; /* racemic stereo */ nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); nMode |= REQ_MODE_RACEMIC_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "SUCF" ) ) { bVer1DefaultMode |= REQ_MODE_STEREO; /* stereo defined by the Chiral flag */ nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); nMode |= REQ_MODE_CHIR_FLG_STEREO; bStdFormat = 0; } else if ( !stricmp( pArg, "ChiralFlagON" ) ) { /* used only with /SUCF */ /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; } else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) { /* used only with /SUCF */ /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; } /*--- Non-std InChI creation options ---*/ /* Stereo */ else if ( !stricmp( pArg, "SUU" ) ) { /* include omitted undef/unknown stereo */ bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); bStdFormat = 0; } else if ( !stricmp( pArg, "SLUUD" ) ) { /* Make labels for unknown and undefined stereo different */ bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; bStdFormat = 0; } /* FixedH */ else if ( !stricmp( pArg, "FIXEDH" ) ) { bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ bStdFormat = 0; } /* RecMet */ else if ( !stricmp( pArg, "RECMET" ) ) { /* reconnect metals */ bReconnectCoord = 1; bStdFormat = 0; } #if ( KETO_ENOL_TAUT == 1 ) else if ( !stricmp( pArg, "KET" ) ) { bKetoEnolTaut = 1; bStdFormat = 0; } #endif #if ( TAUT_15_NON_RING == 1 ) else if ( !stricmp( pArg, "15T" ) ) { b15TautNonRing = 1; bStdFormat = 0; } #endif #endif /*--- Generation options ---*/ else if ( !memicmp( pArg, "W", 1 ) && (t = strtod( pArg+1, (char**)&q ), q > pArg+1) ) { if ( errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) { ul = 0; } else { ul = (unsigned long)(t*1000.0); /* max. time per structure */ } ip->msec_MaxTime = ul; } else if ( !stricmp( pArg, "WarnOnEmptyStructure" ) ) { ip->bAllowEmptyStructure = 1; } /* InChIKey/InChI hash */ else if ( !stricmp( pArg, "Key" ) ) { bHashKey = 1; } else if ( !stricmp( pArg, "XHash1" ) ) { bHashXtra1 = 1; } else if ( !stricmp( pArg, "XHash2" ) ) { bHashXtra2 = 1; } /*--- Conversion modes ---*/ #if ( READ_INCHI_STRING == 1 ) else if ( !stricmp( pArg, "InChI2InChI" ) ) { /* Read InChI Identifiers and output InChI Identifiers */ ip->nInputType = INPUT_INCHI; ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; } else if ( !stricmp( pArg, "InChI2Struct" ) ) { /* Split InChI Identifiers into components */ ip->bReadInChIOptions |= READ_INCHI_TO_STRUCTURE; ip->bReadInChIOptions &= ~READ_INCHI_OUTPUT_INCHI; ip->nInputType = INPUT_INCHI; } #ifdef BUILD_WITH_ENG_OPTIONS else if ( !stricmp( pArg, "KeepBalanceP" ) ) { /* When spliting InChI Identifiers into components: */ /* If MobileH output then add p to each component; */ /* Otherwise add one more component containing balance */ /* of protons and exchangeable isotopic H */ ip->bReadInChIOptions |= READ_INCHI_KEEP_BALANCE_P; bStdFormat = 0; } #endif #endif /*--- (engineering) Undo bug/draw fixes options ---*/ #ifdef BUILD_WITH_ENG_OPTIONS else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) { bFixSp3bug = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FBOFF" ) ) { bFixSp3bug = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FB2OFF" ) ) { bFixFB2 = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "SPXYZOFF" ) ) { bAddPhosphineStereo = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "SASXYZOFF" ) ) { bAddArsineStereo = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "FNUDOFF" ) ) { ip->bFixNonUniformDraw = 0; bStdFormat = 0; } /*--- (engineering) Old structure-perception and InChI creation options ---*/ #if ( FIX_ADJ_RAD == 1 ) else if ( !stricmp( pArg, "FixRad" ) ) { bFixAdjacentRad = 1; bStdFormat = 0; } #endif #if ( UNDERIVATIZE == 1 ) else if ( !stricmp( pArg, "DoDRV" ) ) { ip->bUnderivatize = 1; bStdFormat = 0; } #endif #if ( RING2CHAIN == 1 ) else if ( !stricmp( pArg, "DoR2C" ) ) { ip->bRing2Chain = 1; bStdFormat = 0; } #endif #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) else if ( !stricmp( pArg, "DoneOnly" ) ) { ip->bIngnoreUnchanged = 1; bStdFormat = 0; } #endif else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); bStdFormat = 0; } else if ( !stricmp( pArg, "NoADP" ) ) { bTgFlagHardAddRenProtons = 0; bStdFormat = 0; } /* Tautomer perception off */ else if ( !stricmp( pArg, "EXACT" ) ) { bVer1DefaultMode |= REQ_MODE_BASIC; bStdFormat = 0; } else if ( !stricmp( pArg, "ONLYRECSALT" ) ) { /* do not disconnect salts */ bDisconnectSalts = 0; bStdFormat = 0; } else if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) { bVer1DefaultMode |= REQ_MODE_BASIC; bVer1DefaultMode &= ~REQ_MODE_TAUT; bStdFormat = 0; } else if ( !stricmp( pArg, "ONLYNONISO" ) ) { bVer1DefaultMode |= REQ_MODE_NON_ISO; bVer1DefaultMode &= ~REQ_MODE_ISO; bStdFormat = 0; } else if ( !stricmp( pArg, "TAUT" ) ) { bVer1DefaultMode &= ~REQ_MODE_BASIC; bVer1DefaultMode |= REQ_MODE_TAUT; } else if ( !stricmp( pArg, "ONLYRECMET" ) ) { /* do not disconnect metals */ bDisconnectCoord = 0; bStdFormat = 0; } /*--- (hidden) Old output and other options ---*/ else if ( !stricmp( pArg, "SdfSplit" ) ) { /* Split single Molfiles into disconnected components */ bOutputMolfileSplit = 1; } else if ( !stricmp( pArg, "DCR" ) ) { bDisplayCompositeResults = 1; } else if ( !stricmp( pArg, "AUXFULL" ) || !stricmp( pArg, "AUXMAX" ) ) { /* full aux info */ bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include short aux info */ } else if ( !stricmp( pArg, "AUXMIN" ) ) { /* minimal aux info */ bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; } #if ( READ_INCHI_STRING == 1 ) else if ( !stricmp( pArg, "DDSRC" ) ) { bDisplayIfRestoreWarnings = 1; /* InChI->Structure debugging: Display Debug Structure Restore Components */ } #endif else if ( !stricmp( pArg, "NoVarH" ) ) { bTgFlagVariableProtons = 0; } else if ( !stricmp( pArg, "FULL" ) ) { bVer1DefaultMode = VER103_DEFAULT_MODE; nMode = 0; bReconnectCoord = 1; /* full output */ bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_SHORT_AUX_INFO; ip->bCtPredecessors = 0; ip->bAbcNumbers = 0; bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); } else if ( !stricmp( pArg, "MIN" ) ) { bVer1DefaultMode = VER103_DEFAULT_MODE; nMode = 0; bReconnectCoord = 1; /* minimal output */ bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_NO_AUX_INFO; /* minimal compressed output */ ip->bCtPredecessors = 1; ip->bAbcNumbers = 1; bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); } else if ( !stricmp( pArg, "COMPRESS" ) ) { ip->bAbcNumbers = 1; ip->bCtPredecessors = 1; /* compressed output */ } #if ( READ_INCHI_STRING == 1 ) else if ( !stricmp( pArg, "InChI2InChI" ) ) { /* Read InChI Identifiers and output InChI Identifiers */ ip->nInputType = INPUT_INCHI; ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; } else if ( !stricmp( pArg, "SplitInChI" ) ) { /* Split InChI Identifiers into components */ ip->bReadInChIOptions |= READ_INCHI_SPLIT_OUTPUT; } #endif else if ( !stricmp( pArg, "MOLFILENUMBER" ) ) { ip->bGetMolfileNumber |= 1; } else if ( !stricmp( pArg, "OutputPLAIN" ) ) { bXml |= INCHI_OUT_PLAIN_TEXT; bXml &= ~INCHI_OUT_XML; } else if ( !stricmp( pArg, "OutputXML" ) ) { bXml |= INCHI_OUT_XML; bXml &= ~INCHI_OUT_PLAIN_TEXT; } else if ( !stricmp( pArg, "OutputANNPLAIN" ) ) { bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; bXml |= INCHI_OUT_WINCHI_WINDOW; /* debug */ } else if ( !stricmp( pArg, "OutputANNXML" ) ) { bXml |= INCHI_OUT_XML_TEXT_COMMENTS; bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; } else if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) { bVer1DefaultMode |= REQ_MODE_BASIC; bVer1DefaultMode &= ~REQ_MODE_TAUT; } else if ( !stricmp( pArg, "ONLYNONISO" ) ) { bVer1DefaultMode |= REQ_MODE_NON_ISO; bVer1DefaultMode &= ~REQ_MODE_ISO; } else if ( !stricmp( pArg, "TAUT" ) ) { bVer1DefaultMode &= ~REQ_MODE_BASIC; bVer1DefaultMode |= REQ_MODE_TAUT; } else if ( !stricmp( pArg, "ONLYRECMET" ) ) { /* do not disconnect metals */ bDisconnectCoord = 0; } else if ( !stricmp( pArg, "ONLYRECSALT" ) ) { /* do not disconnect salts */ bDisconnectSalts = 0; } else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { /* added -- 2010-03-01 DT */ bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); } else if ( !memicmp( pArg, "RSB:", 4 )) { mdbr = (int)strtol(pArg+4, NULL, 10); } else if ( !stricmp( pArg, "EQU" ) ) { bCompareComponents = CMP_COMPONENTS; } else if ( !stricmp( pArg, "EQUNONISO" ) ) { bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; } else if ( !memicmp( pArg, "OP:", 3 ) ) { bOutputPath = 1; strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); } #endif /* BUILD_WITH_ENG_OPTIONS */ /* Display unrecognized option */ else { bRecognizedOption = 0; #ifndef TARGET_LIB_FOR_WINCHI inchi_ios_eprint(log_file, "Unrecognized option: \"%s\".\n", pArg); #endif } bVer1Options |= bRecognizedOption; } /*=== end of parsing stand-alone/library InChI options ===*/ else if ( ip->num_paths < MAX_NUM_PATHS ) { char *sz; if ( argv[i] && argv[i][0] ) { if ( sz = (char*) inchi_malloc( (strlen(argv[i]) + 1)*sizeof(sz[0])) ) { strcpy( sz, argv[i] ); } #ifdef CML_DEBUG printf ("1 path %d argv %d %s\n", ip -> num_paths, i, argv [i]); #endif ip->path[ip->num_paths++] = sz; } } } /* for ( i = 1; i < argc; i ++ ) */ if ( bHashKey != 0 ) /* Suppress InChIKey calculation if: compressed output OR Inchi2Struct OR Inchi2Inchi */ { if ( (ip->bAbcNumbers ==1) && (ip->bCtPredecessors == 1) ) { #ifndef TARGET_LIB_FOR_WINCHI inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'Compress' option\n"); return -1; #endif bHashKey = 0; } if ( ip->nInputType == INPUT_INCHI ) { #ifndef TARGET_LIB_FOR_WINCHI inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available in InChI conversion mode\n"); return -1; #endif bHashKey = 0; } else if ( bOutputMolfileOnly == 1 ) { #ifndef TARGET_LIB_FOR_WINCHI inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'OutputSDF' option\n"); return -1; #endif bHashKey = 0; } } if ( bNameSuffix || bOutputPath ) { const char *p = NULL; char *r = NULL; char *sz; int len; const char szNUL[] = "NUL"; /* fix for AMD processor: use const char[] instead of just "NUL" constant 2008-11-5 DT */ /* find the 1st path */ for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( !p && ip->path[i] && ip->path[i][0] ) { p = ip->path[i]; break; } } /* fix output path */ if ( bOutputPath && szOutputPath[0] && p ) { /* remove last slash */ len = strlen(szOutputPath); if ( len > 0 && szOutputPath[len-1] != INCHI_PATH_DELIM ) { szOutputPath[len++] = INCHI_PATH_DELIM; szOutputPath[len] = '\0'; } if ( len > 0 && (r = (char *)strrchr( p, INCHI_PATH_DELIM ) ) && r[1] ) { strcat( szOutputPath, r+1 ); p = szOutputPath; } } /* add missing paths */ for ( i = 0; p && i < MAX_NUM_PATHS; i ++ ) { /* fix for AMD processor: changed order 2008-11-5 DT */ if ( !ip->path[i] || !ip->path[i][0] ) { len = strlen( p ) + strlen(szNameSuffix) + strlen( ext[i] ); if ( sz = (char*) inchi_malloc( (len+1)*sizeof(sz[0]) ) ) { strcpy( sz, p ); strcat( sz, szNameSuffix ); strcat( sz, ext[i] ); ip->num_paths++; ip->path[i] =sz; } } else if ( !stricmp( ip->path[i], szNUL ) ) { inchi_free( (char *)ip->path[i] ); /* cast deliberately const qualifier */ ip->path[i] = NULL; } } } #if ( READ_INCHI_STRING == 1 ) if ( INPUT_INCHI == ip->nInputType ) { bCompareComponents = 0; /*bDisplayCompositeResults = 0;*/ #if ( I2S_MODIFY_OUTPUT == 1 ) if ( !(ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE ) ) #endif { bOutputMolfileOnly = 0; /*bNoStructLabels = 1;*/ bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; bINChIOutputOptions &= ~INCHI_OUT_ONLY_AUX_INFO; } ip->bDisplayIfRestoreWarnings = bDisplayIfRestoreWarnings; if ( !(bINChIOutputOptions & (INCHI_OUT_SDFILE_ONLY | /* not in bINChIOutputOptions yet */ INCHI_OUT_XML | /* not in bINChIOutputOptions yet */ INCHI_OUT_PLAIN_TEXT | /* not in bINChIOutputOptions yet */ INCHI_OUT_PLAIN_TEXT_COMMENTS | /* not in bINChIOutputOptions yet */ INCHI_OUT_XML_TEXT_COMMENTS /* not in bINChIOutputOptions yet */ ) ) #if ( I2S_MODIFY_OUTPUT == 1 ) && !bOutputMolfileOnly && !(bXml & (INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS)) #endif ) { bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT; } } #endif if ( bVer1Options ) { nMode |= bVer1DefaultMode; } else if ( bReleaseVersion ) { nMode |= nReleaseMode; } #if ( defined(COMPILE_ANSI_ONLY) || defined(TARGET_LIB_FOR_WINCHI) ) if ( bCompareComponents && !(bDisplay & 1) ) { bCompareComponents = 0; } #endif /* Save original options */ /* nOrigMode = nMode; */ #ifndef COMPILE_ANSI_ONLY ip->dp.sdp.nFontSize = nFontSize; ip->dp.sdp.ulDisplTime = *ulDisplTime; ip->bDisplay = bDisplay; #ifdef TARGET_LIB_FOR_WINCHI ip->bDisplayCompositeResults = bDisplay; #else ip->bDisplayCompositeResults = bDisplayCompositeResults; #endif #else ip->bDisplayEachComponentINChI = 0; bCompareComponents = 0; #endif ip->bMergeAllInputStructures = bMergeAllInputStructures; ip->bDoNotAddH = bDoNotAddH; /* set default options */ if ( !nMode || nMode == REQ_MODE_STEREO ) { /* requested all output */ nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO); } else { if ( !(nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) ) { nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT); } if ( (nMode & REQ_MODE_STEREO) && !(nMode & (REQ_MODE_ISO | REQ_MODE_NON_ISO)) ) { nMode |= (REQ_MODE_ISO | REQ_MODE_NON_ISO); } } /* if the user requested isotopic then unconditionally add non-isotopic output. */ if ( nMode & REQ_MODE_ISO ) { nMode |= REQ_MODE_NON_ISO; } #if ( MIN_SB_RING_SIZE > 0 ) if ( mdbr ) { nMinDbRinSize = mdbr; } nMode |= (nMinDbRinSize << REQ_MODE_MIN_SB_RING_SHFT) & REQ_MODE_MIN_SB_RING_MASK; #endif /* input file */ if ( ip->nInputType == INPUT_NONE && ip->num_paths > 0 ) { ip->nInputType = INPUT_MOLFILE; /* default */ #if ( ADD_CMLPP == 1 ) { const char *p; if ( ip->path[0] && ( p = strrchr(ip->path[0], '.' ) ) && !stricmp( p, ".cml") ) { ip->nInputType = INPUT_CMLFILE; } } #endif } ip->nMode = nMode; if ( (bCompareComponents & CMP_COMPONENTS) && (nMode & REQ_MODE_BASIC) ) { bCompareComponents |= CMP_COMPONENTS_NONTAUT; /* compare non-tautomeric */ } ip->bCompareComponents = bCompareComponents; ip->bINChIOutputOptions = bINChIOutputOptions | (bOutputMolfileOnly? INCHI_OUT_SDFILE_ONLY : 0); if ( bOutputMolfileOnly ) { bXml &= ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); #if ( SDF_OUTPUT_DT == 1 ) ip->bINChIOutputOptions |= bOutputMolfileDT? INCHI_OUT_SDFILE_ATOMS_DT : 0; ip->bINChIOutputOptions |= bOutputMolfileSplit? INCHI_OUT_SDFILE_SPLIT : 0; #endif } if ( bXml & INCHI_OUT_XML ) { bXml &= ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); } #ifdef TARGET_LIB_FOR_WINCHI if ( !(bDisplay & 1) ) { bXml &= ~(INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); /* do not ouput comments in wINChI text file results */ } else { bXml |= INCHI_OUT_WINCHI_WINDOW; } #endif ip->bINChIOutputOptions |= bXml; ip->bNoStructLabels = bNoStructLabels; if ( bForcedChiralFlag ) { ip->bChiralFlag = bForcedChiralFlag; } /*******************************************/ /* tautomeric/salts settings */ /*******************************************/ ip->bTautFlags = 0; /* initialize */ ip->bTautFlagsDone = 0; /* initialize */ /* find regular tautomerism */ ip->bTautFlags |= TG_FLAG_TEST_TAUT__ATOMS; /* disconnect salts */ ip->bTautFlags |= bDisconnectSalts? TG_FLAG_DISCONNECT_SALTS : 0; /* if possible find long-range H/(-) taut. on =C-OH, >C=O */ ip->bTautFlags |= bAcidTautomerism? TG_FLAG_TEST_TAUT__SALTS : 0; /* allow long-range movement of N(+), P(+) charges */ ip->bTautFlags |= bMovePositiveCharges? TG_FLAG_MOVE_POS_CHARGES : 0; /* multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ ip->bTautFlags |= (bAcidTautomerism > 1)? TG_FLAG_TEST_TAUT2_SALTS : 0; /* (debug) allow to find long-range H-only tautomerism on =C-OH, >C=O */ ip->bTautFlags |= (bUnchargedAcidTaut==1)? TG_FLAG_ALLOW_NO_NEGTV_O : 0; /* merge =C-OH and >C=O containing t-groups and other =C-OH groups */ ip->bTautFlags |= bMergeSaltTGroups? TG_FLAG_MERGE_TAUT_SALTS : 0; ip->bTautFlags |= bDisconnectCoord? TG_FLAG_DISCONNECT_COORD : 0; ip->bTautFlags |=(bDisconnectCoord && bReconnectCoord)? TG_FLAG_RECONNECT_COORD : 0; ip->bTautFlags |= bDisconnectCoordChkVal? TG_FLAG_CHECK_VALENCE_COORD : 0; ip->bTautFlags |= bTgFlagVariableProtons? TG_FLAG_VARIABLE_PROTONS : 0; ip->bTautFlags |= bTgFlagHardAddRenProtons? TG_FLAG_HARD_ADD_REM_PROTONS : 0; ip->bTautFlags |= bKetoEnolTaut? TG_FLAG_KETO_ENOL_TAUT : 0; ip->bTautFlags |= b15TautNonRing? TG_FLAG_1_5_TAUT : 0; #ifdef STEREO_WEDGE_ONLY ip->bTautFlags |= bPointedEdgeStereo? TG_FLAG_POINTED_EDGE_STEREO : 0; #endif #if ( FIX_ADJ_RAD == 1 ) ip->bTautFlags |= bFixAdjacentRad? TG_FLAG_FIX_ADJ_RADICALS : 0; #endif ip->bTautFlags |= bAddPhosphineStereo? TG_FLAG_PHOSPHINE_STEREO : 0; ip->bTautFlags |= bAddArsineStereo? TG_FLAG_ARSINE_STEREO : 0; ip->bTautFlags |= bFixSp3bug? TG_FLAG_FIX_SP3_BUG : 0; if (bFixFB2) { #if ( FIX_ISO_FIXEDH_BUG == 1 ) ip->bTautFlags |= TG_FLAG_FIX_ISO_FIXEDH_BUG; /* accomodate FIX_ISO_FIXEDH_BUG */ #endif #if ( FIX_TERM_H_CHRG_BUG == 1 ) ip->bTautFlags |= TG_FLAG_FIX_TERM_H_CHRG_BUG; /* accomodate FIX_TERM_H_CHRG_BUG */ #endif #if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) ip->bINChIOutputOptions |= INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG; #endif } if ( !ip->nInputType ) ip->nInputType = INPUT_MOLFILE; /* Check if /SNon requested turn OFF SUU/SLUUD */ if ( ! (ip->nMode & REQ_MODE_STEREO) ) { ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; ip->nMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); } /* Standard InChI ? */ if (bStdFormat) { ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; } /* InChIKey ? */ if ( !bHashKey ) ip->bCalcInChIHash = INCHIHASH_NONE; else ip->bCalcInChIHash = INCHIHASH_KEY; /* Extension(s) to hash (in non-std mode only) ? */ if ( !bHashKey ) { if ( (bHashXtra1!=0) || (bHashXtra2!=0) ) inchi_ios_eprint(log_file, "Hash extension(s) not generated: InChIKey not requested"); } else { if ( bHashXtra1 ) { if ( bHashXtra2 ) ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1_XTRA2; else ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1; } else if ( bHashXtra2 ) { ip->bCalcInChIHash = INCHIHASH_KEY_XTRA2; } } return 0; } /*******************************************************************/ int PrintInputParms( INCHI_IOSTREAM *log_file, INPUT_PARMS *ip ) { INCHI_MODE nMode = ip->nMode; int k; int bStdFormat = 1; int first=1; #ifdef TARGET_LIB_FOR_WINCHI int bInChI2Struct = 0; /* as of December 2008, winchi-1 does not convert InChI to structure */ #else int bInChI2Struct = (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE) && ip->nInputType == INPUT_INCHI; #endif if ( !(ip->bINChIOutputOptions & INCHI_OUT_STDINCHI) ) bStdFormat = 0; /* some stereo */ if ( ! (nMode & REQ_MODE_STEREO) ) { inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); first = 0; inchi_ios_eprint( log_file, " Stereo OFF\n"); } else { if ( ! (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags) ) { if (first) { inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); first = 0; } inchi_ios_eprint( log_file, " Both ends of wedge point to stereocenters\n"); } } if ( ip->bDoNotAddH ) { if (first) inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); inchi_ios_eprint( log_file, " Do not add H\n"); } /* Generation/conversion indicator */ if (bStdFormat) { if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct ) inchi_ios_eprint( log_file, "Generating standard InChI\n" ); #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) && !defined(TARGET_EXE_USING_API) ) /* effective only in command line program InChI or stdInChI */ else if ( bInChI2Struct ) inchi_ios_eprint( log_file, "Converting InChI(s) to structure(s) in %s\n", (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)? "MOL format" : "aux. info format" ); #endif } else inchi_ios_eprint( log_file, "Generating non-standard InChI with the options: \n" ); /* SDfile output */ if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { inchi_ios_eprint( log_file, "Output SDfile only without stereochemical information and atom coordinates%s\n", (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? "\n(write H isotopes as D, T)":"" ); } /* Fixed/Mobile H */ if (!bStdFormat) { if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC | REQ_MODE_TAUT) ) inchi_ios_eprint( log_file, " Mobile H Perception OFF (include FixedH layer)\n" ); else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_TAUT) ) inchi_ios_eprint( log_file, " Mobile H Perception ON (omit FixedH layer)\n" ); else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC) ) inchi_ios_eprint( log_file, " Mobile H ignored\n" ); else inchi_ios_eprint( log_file, " Undefined Mobile H mode\n" ); if ( (ip->bTautFlags & TG_FLAG_VARIABLE_PROTONS) ) if ( !(ip->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS) ) inchi_ios_eprint( log_file, " Disabled Aggressive (De)protonation\n" ); #if ( FIND_RING_SYSTEMS != 1 ) inchi_ios_eprint( log_file, " %s5-, 6-, 7-memb. ring taut. ignored\n", i?"; ":""); #endif /* RecMet */ if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) { if ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD ) inchi_ios_eprint( log_file, " Include bonds to metals\n"); else inchi_ios_eprint( log_file, " Do not reconnect metals (omit RecMet layer)\n"); } else inchi_ios_eprint( log_file, " Do not disconnect metals\n"); /* isotopic - always ON, output disabled. 09-17-2009*/ /* if ( nMode & REQ_MODE_ISO ) inchi_ios_eprint( log_file, " Isotopic ON\n"); else if ( nMode & REQ_MODE_NON_ISO ) inchi_ios_eprint( log_file, " Isotopic OFF\n"); */ #if ( FIX_ADJ_RAD == 1 ) if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) inchi_ios_eprint( log_file, "Fix Adjacent Radicals\n" ); #endif /* stereo */ if ( nMode & REQ_MODE_STEREO ) { inchi_ios_eprint( log_file, " %s%s%s%sStereo ON\n", ( nMode & REQ_MODE_NOEQ_STEREO )? "Slow ":"", ( nMode & REQ_MODE_REDNDNT_STEREO )? "Redund. ":"", ( nMode & REQ_MODE_NO_ALT_SBONDS)? "No AltBond ":"", ( nMode & REQ_MODE_RACEMIC_STEREO)? "Racemic " : ( nMode & REQ_MODE_RELATIVE_STEREO)? "Relative " : ( nMode & REQ_MODE_CHIR_FLG_STEREO)? "Chiral Flag " : "Absolute " ); if ( 0 == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) inchi_ios_eprint( log_file, " Include undefined/unknown stereogenic centers and bonds\n"); else if ( REQ_MODE_SC_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers\n"); else if ( REQ_MODE_SB_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic bonds\n"); else /*case REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers and bonds\n"); if ( 0 != (nMode & REQ_MODE_DIFF_UU_STEREO) ) { inchi_ios_eprint( log_file, " Make labels for unknown and undefined stereo different\n"); } #if ( defined(MIN_SB_RING_SIZE) && MIN_SB_RING_SIZE > 0 ) k = (ip->nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT; if ( bRELEASE_VERSION != 1 || k != MIN_SB_RING_SIZE ) { if ( k >= 3 ) inchi_ios_eprint( log_file, " Min. stereobond ring size: %d\n", k ); else inchi_ios_eprint( log_file, " Min. stereobond ring size: NONE\n" ); } #endif } /* stereo */ } /* !bStdFormat */ if ( !bStdFormat ) { if ( TG_FLAG_KETO_ENOL_TAUT & ip->bTautFlags) inchi_ios_eprint( log_file, " Account for keto-enol tautomerism\n"); else inchi_ios_eprint( log_file, " Do not account for keto-enol tautomerism\n"); if ( TG_FLAG_1_5_TAUT & ip->bTautFlags) inchi_ios_eprint( log_file, " Account for 1,5-tautomerism\n"); else inchi_ios_eprint( log_file, " Do not account for 1,5-tautomerism\n"); #ifdef BUILD_WITH_ENG_OPTIONS if ( TG_FLAG_PHOSPHINE_STEREO & ip->bTautFlags ) inchi_ios_eprint( log_file, " Include phosphine stereochemistry\n"); else inchi_ios_eprint( log_file, " Do not include phosphine stereochemistry\n"); if ( TG_FLAG_ARSINE_STEREO & ip->bTautFlags ) inchi_ios_eprint( log_file, " Include arsine stereochemistry\n"); else inchi_ios_eprint( log_file, " Do not include arsine stereochemistry\n"); if ( ! (TG_FLAG_FIX_SP3_BUG & ip->bTautFlags) ) inchi_ios_eprint( log_file, " Turned OFF fix of bug leading to missing or undefined sp3 parity\n"); if ( !(TG_FLAG_FIX_ISO_FIXEDH_BUG & ip->bTautFlags) ) inchi_ios_eprint( log_file, " Turned OFF bug-fixes found after v.1.02b release\n"); if ( !(ip->bFixNonUniformDraw) ) inchi_ios_eprint( log_file, " Turned OFF fixes of non-uniform drawing issues\n"); if ( ! (TG_FLAG_MOVE_POS_CHARGES & ip->bTautFlags) ) inchi_ios_eprint( log_file, " MovePos turned OFF\n"); #endif } /* !bStdFormat */ if ( ip->bCalcInChIHash != INCHIHASH_NONE ) { if (bStdFormat) inchi_ios_eprint( log_file, "Generating standard InChIKey\n"); else inchi_ios_eprint( log_file, "Generating InChIKey\n"); if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) inchi_ios_eprint( log_file, "Generating hash extension (1st block)\n"); else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) inchi_ios_eprint( log_file, "Generating hash extension (2nd block)\n"); else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) inchi_ios_eprint( log_file, "Generating hash extension (two blocks)\n"); } if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { inchi_ios_eprint( log_file, "Saving InChI creation options" ); if ( bStdFormat ) { inchi_ios_eprint( log_file, " suppressed for standard InChI" ); /* NB: actual suppression takes place on InChI serialization */ /* (as on e.g. Inchi2Inchi conversion it may appear that we create non-std */ /* InChI instead of standard one) */ } inchi_ios_eprint( log_file, "\n" ); } if ( ip->bAllowEmptyStructure ) inchi_ios_eprint( log_file, "Issue warning on empty structure\n" ); /* Input */ if ( ip->nInputType ) { inchi_ios_eprint( log_file, "Input format: %s", ip->nInputType == INPUT_MOLFILE? "MOLfile" : ip->nInputType == INPUT_SDFILE? "SDfile" : ip->nInputType == INPUT_CMLFILE? "CMLfile" : #if ( READ_INCHI_STRING == 1 ) ip->nInputType == INPUT_INCHI? "InChI (plain identifier)" : #endif ip->nInputType == INPUT_INCHI_XML? "InChI AuxInfo (xml)" : ip->nInputType == INPUT_INCHI_PLAIN? "InChI AuxInfo (plain)" : "Unknown" ); if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) && ip->bGetMolfileNumber ) inchi_ios_eprint( log_file, " (attempting to read Molfile number)" ); inchi_ios_eprint( log_file, "\n"); } if ( ip->szSdfDataHeader[0] && ip->nInputType != INPUT_SDFILE ) inchi_ios_eprint( log_file, " SDfile data header: \"%s\"\n", ip->szSdfDataHeader); /* Output */ inchi_ios_eprint( log_file, "Output format: %s%s\n", (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT)? "Plain text" : (ip->bINChIOutputOptions & INCHI_OUT_XML)? "XML": ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && bInChI2Struct )? "SDfile only (without stereochemical info and atom coordinates)" : ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct)? "SDfile only" : "Unknown", ((ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT))? ", tabbed":""); #if ( bRELEASE_VERSION == 1 ) if ( ip->bCtPredecessors || ip->bAbcNumbers ) { if ( ip->bCtPredecessors && ip->bAbcNumbers ) inchi_ios_eprint( log_file, "Representation: Compressed\n"); else inchi_ios_eprint( log_file, "Connection table: %s, %s\n", ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", ip->bAbcNumbers? "Shorter alternative":"Numerical"); } #else if ( (bRELEASE_VERSION != 1) || ip->bCtPredecessors || ip->bAbcNumbers ) inchi_ios_eprint( log_file, "Connection table: %s, %s\n", ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", ip->bAbcNumbers? "Shorter alternative":"Numerical"); else inchi_ios_eprint( log_file, "Representation: Numerical"); #endif if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) { if( ip->bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO ) inchi_ios_eprint( log_file, "Aux. info suppressed\n"); else if ( ip->bINChIOutputOptions & INCHI_OUT_SHORT_AUX_INFO ) inchi_ios_eprint( log_file, "Minimal Aux. info\n"); else inchi_ios_eprint( log_file, "Full Aux. info\n"); } if ( ip->msec_MaxTime ) { unsigned long seconds = ip->msec_MaxTime/1000; unsigned long milliseconds = (ip->msec_MaxTime%1000); inchi_ios_eprint( log_file, "Timeout per structure: %lu.%03lu sec\n", seconds, milliseconds); } else inchi_ios_eprint( log_file, "No timeout"); inchi_ios_eprint( log_file, "Up to %d atoms per structure\n", MAX_ATOMS); if ( ip->first_struct_number > 1 ) inchi_ios_eprint( log_file, "Skipping %ld structure%s\n", ip->first_struct_number-1, ip->first_struct_number==2? "":"s" ); if ( ip->last_struct_number > 0 ) inchi_ios_eprint( log_file, "Terminate after structure #%ld\n", ip->last_struct_number ); if ( ip->bSaveWarningStructsAsProblem && ip->path[3] && ip->path[3][0] ) inchi_ios_eprint( log_file, "Saving warning structures into the problem file\n"); if ( ip->bSaveAllGoodStructsAsProblem && ip->path[3] && ip->path[3][0] ) inchi_ios_eprint( log_file, "Saving only all good structures into the problem file\n"); /* Report debug modes */ #if ( bRELEASE_VERSION != 1 ) inchi_ios_eprint( log_file, "Release version = NO\n"); #endif #if ( TRACE_MEMORY_LEAKS == 1 && defined(_DEBUG) ) inchi_ios_eprint( log_file, "Tracing memory leaks (SLOW)\n"); #endif inchi_ios_eprint( log_file, "\n" ); #if ( bRELEASE_VERSION != 1 ) #if ( FIND_RING_SYSTEMS == 1 ) inchi_ios_eprint( log_file, "Find ring systems=Y\nTautomers:\n" ); inchi_ios_eprint( log_file, " 4-pyridinol=%s\n", TAUT_4PYRIDINOL_RINGS==1? "Y":"N"); inchi_ios_eprint( log_file, " pyrazole=%s\n", TAUT_PYRAZOLE_RINGS==1? "Y":"N"); inchi_ios_eprint( log_file, " tropolone=%s\n", TAUT_TROPOLONE_7==1? "Y":"N"); inchi_ios_eprint( log_file, " tropolone-5=%s\n", TAUT_TROPOLONE_5==1? "Y":"N"); inchi_ios_eprint( log_file, "Only chain attachments to tautomeric rings=%s\n", TAUT_RINGS_ATTACH_CHAIN==1? "Y":"N"); #endif if ( ip->bGetSdfileId ) inchi_ios_eprint( log_file, "Extracting SDfile IDs\n"); inchi_ios_eprint( log_file, "\nDbg: MOVE_CHARGES=%d\n", 0!=(ip->bTautFlags&TG_FLAG_MOVE_POS_CHARGES)); inchi_ios_eprint( log_file, " REPLACE_ALT_WITH_TAUT=%d; NEUTRALIZE_ENDPOINTS=%d; BNS_PROTECT_FROM_TAUT=%d\n", REPLACE_ALT_WITH_TAUT, NEUTRALIZE_ENDPOINTS, BNS_PROTECT_FROM_TAUT); inchi_ios_eprint( log_file, " DISCONNECT_SALTS=%d; TEST_TAUT_SALTS=%d; TEST_TAUT2_SALTS=%d\n", 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_SALTS), 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT__SALTS), 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT2_SALTS)); inchi_ios_eprint( log_file, " CHARGED_ACID_TAUT_ONLY=%d MERGE_TAUT_SALTS=%d\n", 0==(ip->bTautFlags&TG_FLAG_ALLOW_NO_NEGTV_O), 0!=(ip->bTautFlags&TG_FLAG_MERGE_TAUT_SALTS)); inchi_ios_eprint( log_file, " DISCONNECT_COORD=%d\n", 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_COORD) ); #if ( TEST_RENUMB_ATOMS == 1 ) inchi_ios_eprint( log_file, "\nDbg: TEST_RENUMB_ATOMS=%d; TEST_RENUMB_NEIGH=%d; TEST_RENUMB_SWITCH=%d\n", TEST_RENUMB_ATOMS, TEST_RENUMB_NEIGH, TEST_RENUMB_SWITCH ); inchi_ios_eprint( log_file, " TEST_RENUMB_ATOMS_SAVE_LONGEST=%d\n", TEST_RENUMB_ATOMS_SAVE_LONGEST); #endif #endif /* ( bRELEASE_VERSION != 1 ) */ return 0; } /************************************************************************************/ void HelpCommandLineParms( INCHI_IOSTREAM *f ) { if ( !f ) return; #if ( bRELEASE_VERSION == 1 ) inchi_ios_print_nodisplay( f, #ifdef TARGET_EXE_USING_API "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); #else "%s ver %s%s.\n\nUsage:\ninchi-%s inputFile [outputFile [logFile [problemFile]]] [%coption [%coption...]]\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); #if ( BUILD_WITH_AMI == 1 ) inchi_ios_print_nodisplay( f, "inchi-%s inputFiles... %cAMI [%coption[ %coption...]]\n", INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); #endif #endif inchi_ios_print_nodisplay( f, "\nOptions:\n"); inchi_ios_print_nodisplay( f, "\nInput\n"); inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); #if ( ADD_CMLPP == 1 ) inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); #endif /* inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); */ #if ( BUILD_WITH_AMI == 1 ) inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); #endif inchi_ios_print_nodisplay( f, "Output\n"); inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); /*inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ /*inchi_ios_print_nodisplay( f, " FULL Standard set of options for Full Verbose Output\n");*/ /*inchi_ios_print_nodisplay( f, " MIN Standard set of options for Minimal Concise Output\n");*/ #if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) inchi_ios_print_nodisplay( f, " D Display the structures\n"); inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); #endif /*inchi_ios_print_nodisplay( f, " PLAIN Plain text output (Default: XML format)\n");*/ inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); #if ( SDF_OUTPUT_DT == 1 ) inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); #endif #if ( BUILD_WITH_AMI == 1 ) inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); #endif inchi_ios_print_nodisplay( f, "Structure perception\n"); inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); #ifndef USE_STDINCHI_API inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); #endif inchi_ios_print_nodisplay( f, "Generation\n"); inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); inchi_ios_print_nodisplay( f, "Conversion\n"); #ifdef TARGET_EXE_USING_API inchi_ios_print_nodisplay( f, " InChI2InChI Test mode: Mol/SDfile->%s->%s\n", INCHI_NAME, INCHI_NAME); inchi_ios_print_nodisplay( f, " InChI2Struct Test mode: Mol/SDfile->%s->Structure->(%s+AuxInfo)\n", INCHI_NAME, INCHI_NAME); #else inchi_ios_print_nodisplay( f, " InChI2InChI Convert %s string(s) into %s string(s)\n", INCHI_NAME, INCHI_NAME); inchi_ios_print_nodisplay( f, " InChI2Struct Convert InChI string(s) to structure(s) in InChI aux.info format\n"); #endif #ifdef BUILD_WITH_ENG_OPTIONS #if 0 inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); #if ( FIX_ADJ_RAD == 1 ) inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); #endif inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); #endif #endif /* breleaseversion<1 #else inchi_ios_print_nodisplay( f, "%s ver %s. Special testing version 12-12-2002.\n", INCHI_NAME, INCHI_VERSION); inchi_ios_print_nodisplay( f, "\nUsage:\ncINChI09b inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); inchi_ios_print_nodisplay( f, "\nOptions:\n"); inchi_ios_print_nodisplay( f, "\tB Basic\n"); inchi_ios_print_nodisplay( f, "\tT basic Tautomeric\n"); inchi_ios_print_nodisplay( f, "\tI Isotopic\n"); inchi_ios_print_nodisplay( f, "\tN Non-isotopic\n"); inchi_ios_print_nodisplay( f, "\tS Stereo\n"); inchi_ios_print_nodisplay( f, "\tE Exclude Stereo\n"); inchi_ios_print_nodisplay( f, "\tD Display the structures\n"); inchi_ios_print_nodisplay( f, "\tALT produce shorter ALTernative representation (Abc)\n"); inchi_ios_print_nodisplay( f, "\tSCT produce shorter connection table representation\n"); inchi_ios_print_nodisplay( f, "\tXML output in xml format\n"); inchi_ios_print_nodisplay( f, "\tPLAIN output in plain format\n"); inchi_ios_print_nodisplay( f, "\tMERGE Merge all MOLfiles from the input file into one compound\n"); inchi_ios_print_nodisplay( f, "\tWnumber time-out per structure in seconds, W0 means unlimited\n"); inchi_ios_print_nodisplay( f, "\tFnumber set display Font size, points\n"); inchi_ios_print_nodisplay( f, "\tSREL Relative Stereo\n"); inchi_ios_print_nodisplay( f, "\tSRAC Racemic Stereo\n"); inchi_ios_print_nodisplay( f, "\tNOUUSB Omit stereobonds if all are unknown/undefined\n"); inchi_ios_print_nodisplay( f, "\tNOUUSC Omit stereocenters if all are unknown/undefined\n"); inchi_ios_print_nodisplay( f, "\tSS Slow Stereo: do not use stereo equivalence\n"); inchi_ios_print_nodisplay( f, "\tRS Do not test for Redundant Stereo elements\n"); inchi_ios_print_nodisplay( f, "\tPW Save warning structures in the problems file\n"); inchi_ios_print_nodisplay( f, "\tPGO Save only all good structures in the problems file\n"); inchi_ios_print_nodisplay( f, "\tDSB Double Stereo Bonds only (ignore alternating bonds stereo)\n"); inchi_ios_print_nodisplay( f, "\tRSB:n Min Ring Size for detecting for Stereo Bonds (n=1 => all)\n"); inchi_ios_print_nodisplay( f, "\tAUXINFO:0 do not output auxiliary information (default:1)\n"); inchi_ios_print_nodisplay( f, "\tDISCONSALT:0 do not disconnect salts (default:1)\n"); inchi_ios_print_nodisplay( f, "\tDISCONMETAL:0 do not disconnect metals (default:1)\n"); inchi_ios_print_nodisplay( f, "\tDISCONMETALCHKVAL:1 do not disconnect if typical valence (default:0)\n"); inchi_ios_print_nodisplay( f, "\tRECONMETAL:0 do not reconnect metals (default:1)\n"); inchi_ios_print_nodisplay( f, "\tMOVEPOS:0 do not check moveable positive charges (default:1)\n"); inchi_ios_print_nodisplay( f, "\tACIDTAUT:n n=1: one H/(-) tautomerism, 2: more (deflt), 0:none\n"); inchi_ios_print_nodisplay( f, "\tMERGESALTTG:1 merge salt t-groups (default), 0: do not merge\n"); inchi_ios_print_nodisplay( f, "\tUNCHARGEDACIDS:1 Apply salt (acid) tautomerism in neutral species\n"); inchi_ios_print_nodisplay( f, "\tO:[suffix] Open all 4 files adding suffix to the inputFile name\n"); inchi_ios_print_nodisplay( f, "\tOP:outputpath Set output path\n"); inchi_ios_print_nodisplay( f, "\tMOL input file is a MOLfile (default)\n"); inchi_ios_print_nodisplay( f, "\tSDF[:DataHeader] Include SDfile data for the header into the results\n"); inchi_ios_print_nodisplay( f, "\tSDFID extract CAS r.n. in addition to requested SDfile data\n"); inchi_ios_print_nodisplay( f, "\tSTART:number Start at the given structure ordering number\n"); inchi_ios_print_nodisplay( f, "\tEND:number Terminate after the given structure ordering number\n"); */ #endif } /*^^^ */ /************************************************************************************/ void HelpCommandLineParmsReduced( INCHI_IOSTREAM *f ) { if ( !f ) return; #if ( bRELEASE_VERSION == 1 ) /*^^^ */ inchi_ios_print_nodisplay( f, #ifdef TARGET_EXE_USING_API "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); #else "%s ver %s%s.\n\nUsage:\nc%s-%s inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, INCHI_NAME, INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); #endif /*^^^ */ inchi_ios_print_nodisplay( f, "\nOptions:\n"); inchi_ios_print_nodisplay( f, "\nInput\n"); inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); #if ( ADD_CMLPP == 1 ) inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); #endif /* inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); */ #if ( BUILD_WITH_AMI == 1 ) inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); #endif inchi_ios_print_nodisplay( f, "Output\n"); inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); /* inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ #if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) inchi_ios_print_nodisplay( f, " D Display the structures\n"); inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); #endif inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); #if ( SDF_OUTPUT_DT == 1 ) inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); #endif #if ( BUILD_WITH_AMI == 1 ) inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); #endif inchi_ios_print_nodisplay( f, "Structure perception\n"); inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); #ifndef USE_STDINCHI_API inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); #endif inchi_ios_print_nodisplay( f, "Generation\n"); inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); #ifdef BUILD_WITH_ENG_OPTIONS #if 0 inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); #if ( FIX_ADJ_RAD == 1 ) inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); #endif inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); #endif #endif #endif /* #if ( bRELEASE_VERSION == 1 ) */ } #define fprintf2 inchi_fprintf #ifndef TARGET_API_LIB /************************************************************************************/ int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ) { /* -- Files -- ip->path[0] => Input ip->path[1] => Output (INChI) ip->path[2] => Log ip->path[3] => Problem structures ip->path[4] => Errors file (ACD Labs) */ /* logfile -- open as early as possible */ if ( !ip->path[2] || !ip->path[2][0] ) { fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" ); */ fprintf2( stderr, "Log file not specified. Using standard error output.\n"); *log_file = stderr; } else if ( !(*log_file = fopen( ip->path[2], "w" ) ) ) { fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ fprintf2( stderr, "Cannot open log file '%s'. Using standard error output.\n", ip->path[2] ); *log_file = stderr; } else { fprintf2( *log_file, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ fprintf2( *log_file, "Opened log file '%s'\n", ip->path[2] ); } /* input file */ if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE || ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI || ip->nInputType == INPUT_INCHI_PLAIN ) && ip->num_paths > 0 ) { const char *fmode = NULL; #if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) /* compilers that definitely allow fopen "rb" (binary read) mode */ fmode = "rb"; if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "rb" ) ) ) { fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); goto exit_function; } else { if ( ip->nInputType == INPUT_CMLFILE ) { int c; #ifdef CML_DEBUG printf ("cr %d lf %d ret %d\n", (int) '\r', (int) '\f', (int) '\n'); #endif /* read up to the end of the first line */ while( (c = fgetc( *inp_file )) && c != EOF && c != '\n' && c != '\r' ) ; if ( c == '\r' || c == EOF ) { /* text file contains CR; close and reopen as "text" */ fclose( *inp_file ); if ( !(*inp_file = fopen( ip->path[0], "r" ) ) ) { fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); goto exit_function; } fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); fmode = "r"; } else { fclose( *inp_file ); if ( !(*inp_file = fopen( ip->path[0], "rb" ) ) ) { fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); goto exit_function; } fprintf2( *log_file, "Opened input file '%s': no CR.\n", ip->path[0] ); fmode = "rb"; } } /* CML */ else fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); } #else if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "r" ) ) ) { fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); goto exit_function; } else { fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); } fmode = "r"; #endif DetectInputINChIFileType( inp_file, ip, fmode ); } else if ( (ip->nInputType != INPUT_MOLFILE && ip->nInputType != INPUT_SDFILE && ip->nInputType != INPUT_CMLFILE && ip->nInputType != INPUT_INCHI /*^^^ post-1.02b */ && ip->nInputType != INPUT_INCHI_PLAIN )) { fprintf2( *log_file, "Input file type not specified. Terminating.\n"); goto exit_function; } else { fprintf2( *log_file, "Input file not specified. Using standard input.\n"); *inp_file = stdin; } /* output file */ if ( !ip->path[1] || !ip->path[1][0] ) { fprintf2( *log_file, "Output file not specified. Using standard output.\n"); *output_file = stdout; } else { if ( !(*output_file = fopen( ip->path[1], "w" ) ) ) { fprintf2( *log_file, "Cannot open output file '%s'. Terminating.\n", ip->path[1] ); goto exit_function; } else { fprintf2( *log_file, "Opened output file '%s'\n", ip->path[1] ); if ( (ip->bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT)) && *inp_file != stdin && !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !ip->bNoStructLabels && !(ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT)) { PrintFileName( "* Input_File: \"%s\"\n", *output_file, ip->path[0] ); } } } /* problem file */ if ( ip->path[3] && ip->path[3][0] ) { const char *fmode = "w"; #if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) if ( ip->nInputType != INPUT_CMLFILE ) { fmode = "wb"; } #endif if ( !(*prb_file = fopen( ip->path[3], fmode ) ) ) { fprintf2( *log_file, "Cannot open problem file '%s'. Terminating.\n", ip->path[3] ); goto exit_function; } else { fprintf2( *log_file, "Opened problem file '%s'\n", ip->path[3] ); } } return 1; /* success */ exit_function: return 0; /* failed */ } #define NUM_VERSIONS 7 #define LEN_VERSIONS 64 /*******************************************************************/ static int bMatchOnePrefix( int len, char *str, int lenPrefix[], char strPrefix[][LEN_VERSIONS], int numPrefix); /*******************************************************************/ static int bMatchOnePrefix( int len, char *str, int lenPrefix[], char strPrefix[][LEN_VERSIONS], int numPrefix) { int i; for ( i = 0; i < numPrefix; i ++ ) { if ( len >= lenPrefix[i] && !memcmp( str, strPrefix[i], lenPrefix[i] ) ) { return 1; } } return 0; } /*******************************************************************/ int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ) { char szLine[256], ret = 0; static char szPlnVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "INChI:1.1Beta/";*/ static int lenPlnVersion[NUM_VERSIONS]; static char szPlnAuxVer[NUM_VERSIONS][LEN_VERSIONS]; /* = "AuxInfo:1.1Beta/";*/ static int lenPlnAuxVer[NUM_VERSIONS]; static char szXmlVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "";*/ static int lenXmlVersion[NUM_VERSIONS]; static char szXmlStruct[LEN_VERSIONS] = "nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI ) { return 1; } if ( !bInitilized ) { lenPlnVersion[0] = sprintf( szPlnVersion[0], "%s=%s/", INCHI_NAME, INCHI_VERSION ); lenPlnVersion[1] = sprintf( szPlnVersion[1], "INChI=1.12Beta/" ); lenPlnVersion[2] = sprintf( szPlnVersion[2], "INChI=1.0RC/" ); lenPlnVersion[3] = sprintf( szPlnVersion[3], "InChI=1.0RC/" ); lenPlnVersion[4] = sprintf( szPlnVersion[4], "InChI=1/" ); lenPlnVersion[5] = sprintf( szPlnVersion[5], "MoChI=1a/" ); lenPlnVersion[6] = sprintf( szPlnVersion[6], "InChI=1S/" ); lenPlnAuxVer[0] = sprintf( szPlnAuxVer[0], "AuxInfo=%s/", INCHI_VERSION ); lenPlnAuxVer[1] = sprintf( szPlnAuxVer[1], "AuxInfo=1.12Beta/" ); lenPlnAuxVer[2] = sprintf( szPlnAuxVer[2], "AuxInfo=1.0RC/" ); lenPlnAuxVer[3] = sprintf( szPlnAuxVer[3], "AuxInfo=1.0RC/" ); lenPlnAuxVer[4] = sprintf( szPlnAuxVer[4], "AuxInfo=1/" ); lenPlnAuxVer[5] = sprintf( szPlnAuxVer[5], "AuxInfo=1a/" ); lenPlnAuxVer[6] = sprintf( szPlnAuxVer[6], "AuxInfo=1/" ); lenXmlVersion[0] = sprintf( szXmlVersion[0], "<%s version=\"%s\">", INCHI_NAME, INCHI_VERSION ); lenXmlVersion[1] = sprintf( szXmlVersion[1], "" ); lenXmlVersion[2] = sprintf( szXmlVersion[2], "" ); lenXmlVersion[3] = sprintf( szXmlVersion[3], "" ); lenXmlVersion[4] = sprintf( szXmlVersion[4], "" ); lenXmlVersion[5] = sprintf( szXmlVersion[5], "" ); lenXmlVersion[6] = sprintf( szXmlVersion[6], "" ); lenXmlIdentVer[0] = sprintf( szXmlIdentVer[0], "= 2 && !bINChI_xml ) { ip->nInputType = INPUT_INCHI_PLAIN; ret = 1; } else if ( !bINChI_plain && bINChI_xml >= 3 ) { ip->nInputType = INPUT_INCHI_XML; ret = 1; } /*exit_function:*/ fclose ( *inp_file ); *inp_file = fopen( ip->path[0], fmode ); return ret; } #undef NUM_VERSIONS #undef LEN_VERSIONS #endif /* TARGET_API_LIB */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiprt1.c000066400000000000000000005334561271037650300234600ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include #include "mode.h" #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichister.h" #include "ichicomp.h" #include "ichimain.h" #include "ichimake.h" #include "ichi_io.h" int PrintXmlStartTag(char *pStr, int indent, int bEnd, const char *tag, const char *l1, int v1, const char *l2, int v2, const char *l3, int v3, const char *l4, int v4, const char *l5, int v5, const char *l6, int v6); int Needs2addXmlEntityRefs(const char *s ); int AddXmlEntityRefs(const char *p, char *d ); #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); #endif int str_LineStart(const char *tag, char *tag2, int val2, char *pStr, int ind ); int str_LineEnd(const char *tag, int tot_len, int nStrLen, int *bOverflow, char *pStr, int ind, int bPlainTextTags ); int CleanOrigCoord(MOL_COORD szCoord, int delim ); int WriteOrigCoord(int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len); int WriteOrigAtoms(int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd); int WriteOrigBonds(int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd); void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2); char VER_STRING[64]; const char sCompDelim[] = ";"; /* component delimiter */ const char sIdenticalValues[] = "*"; /* identical component */ const char x_space[] = " "; /* xml output: words & additional tags */ const char x_inchi[] = INCHI_NAME; const char x_inchi_ver[] = "version"; /* "InChI.version"; */ const char x_curr_ver[] = INCHI_VERSION; const char x_structure[] = "structure"; const char x_number[] = "number"; const char x_header[] = "id.name"; const char x_value[] = "id.value"; const char x_empty[] = ""; const char x_type[] = "type"; const char x_message[] = "message"; const char x_text[] = "value"; const char x_ferr[] = "fatal (aborted)"; const char x_err[] = "error (no InChI)"; const char x_warn[] = "warning"; const char x_basic[] = "identifier"; const char x_tautomeric[] = "mobile-H"; const char x_reconnected[] = "reconnected"; const char x_ver[] = "version"; const char x_type_alpha[] = "alpha"; const char x_type_numer[] = "numeric"; const char x_type_predec[] = "sct"; const char x_type_normal[] = "normal"; const char x_type_short[] = "compressed"; const char x_basic_layer[] = "basic"; const char x_aux_basic[] = "identifier.auxiliary-info"; const char x_aux_comm[] = "!-- This section is NOT a part of the identifier, it is not unique --"; const char x_ign_uu_sp2[] = "omit_undef_dbond"; const char x_ign_uu_sp3[] = "omit_undef_sp3"; const char x_line_opening[] = "<"; const char x_line_closing[] = ""; const char x_abs[] = "1"; const char x_rel[] = "2"; const char x_rac[] = "3"; #define MAX_TAG_LEN 64 typedef struct tagInchiTag { const char *szPlainLabel; const char *szPlainComment; const char *szXmlLabel; int bAlwaysOutput; } INCHI_TAG; /* identifier */ const INCHI_TAG IdentLbl[] = { /* prefixes: may be combined in this order */ /* IL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed H */ /* IL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ /* IL_STER_ORD, */ { "/", "stereo", "stereo", 0 }, /* stereo */ /* items */ /* IL_VERS_ORD, */ { "" , "version", "version", 1 }, /* IL_FML__ORD, */ { "/", "formula", "formula", 1 }, /* basic part formula */ /* IL_CONN_ORD, */ { "/c", "connections", "connections", 1 }, /* IL_ALLH_ORD, */ { "/h", "H_atoms", "H", 1 }, /* IL_CHRG_ORD, */ { "/q", "charge", "charge", 1 }, /* IL_PROT_ORD, */ { "/p", "protons", "protons", 0 }, /* stereo */ /* IL_DBND_ORD, */ { "/b", "dbond", "dbond", 0 }, /* IL_SP3S_ORD, */ { "/t", "sp3", "sp3", 0 }, /* IL_INVS_ORD, */ { "/m", "sp3:inverted", "abs.inverted", 0 }, /* mirrored */ /* IL_TYPS_ORD, */ { "/s", "type (1=abs, 2=rel, 3=rac)", "type", 0 }, /* stereo type */ /* isotopic */ /* IL_ATMS_ORD, */ { "/i", "atoms", "atoms", 1 }, /* isotopic mobile H only */ /* IL_XCGA_ORD, */ { "/h", "exchangeable_H", "H-isotopic", 1 }, /* fixed H only */ /* IL_FMLF_ORD, */ { "/f", "formula", "formula", 1 }, /* fixed H formula */ /* IL_HFIX_ORD, */ { "/h", "H_fixed" , "H-fixed" , 1 }, /* fixed-H */ /* IL_TRNS_ORD, */ { "/o", "transposition", "transposition", 0 }, /* order */ /* IL_REC__ORD, */ { "/r", "reconnected bond(s) to metal(s) formula", "formula", 0 } }; /* Parsing plain text InChI (FML is a chemical formula) ======================== 1.12Beta/FML /i /f[FML] /i [/o] /rFML /i /f[FML] /i [/o] end | | | | | | | | | Labels | chqpbtms | hbtms | hqbtms | btms | chqpbtms | hbtms | hqbtms | btms | inside: | | | | | | | | | | non-iso- | iso- | fix- | iso- | non-iso- | iso- | fix- | iso- | meaning: | topic | topic | ed H | topic | topic | topic | ed H | topic | |----------+-------+--------+---------|----------+-------+--------+---------| | mobile-H | fixed-H | mobile-H | fixed-H | |----------+-------+--------+---------|----------+-------+--------+---------| | | | | normal or disconected metal | reconnected bonds to metal | |_____________________________________|_____________________________________| meanings of h: /h - immobile H & mobile H group(s) /i/h - exchangeable isotopic H (common) /f/h - fixed-H /f/i/h - never happens */ typedef enum tagIdentLblOrd { IL_FIXH_ORD, IL_ISOT_ORD, IL_STER_ORD, IL_VERS_ORD, IL_FML__ORD, IL_CONN_ORD, IL_ALLH_ORD, IL_CHRG_ORD, IL_PROT_ORD, IL_DBND_ORD, IL_SP3S_ORD, IL_INVS_ORD, IL_TYPS_ORD, IL_ATMS_ORD, IL_XCGA_ORD, IL_FMLF_ORD, IL_HFIX_ORD, IL_TRNS_ORD, IL_REC__ORD, IL_MAX_ORD /* max number of tags */ } IDENT_LBL_ORD; typedef enum tagIdentLblBit { IL_FIXH = 1 << IL_FIXH_ORD, IL_ISOT = 1 << IL_ISOT_ORD, IL_STER = 1 << IL_STER_ORD, IL_VERS = 1 << IL_VERS_ORD, IL_FML_ = 1 << IL_FML__ORD, IL_CONN = 1 << IL_CONN_ORD, IL_ALLH = 1 << IL_ALLH_ORD, IL_CHRG = 1 << IL_CHRG_ORD, IL_PROT = 1 << IL_PROT_ORD, IL_DBND = 1 << IL_DBND_ORD, IL_SP3S = 1 << IL_SP3S_ORD, IL_INVS = 1 << IL_INVS_ORD, IL_TYPS = 1 << IL_TYPS_ORD, IL_ATMS = 1 << IL_ATMS_ORD, IL_XCGA = 1 << IL_XCGA_ORD, IL_FMLF = 1 << IL_FMLF_ORD, IL_HFIX = 1 << IL_HFIX_ORD, IL_TRNS = 1 << IL_TRNS_ORD, IL_REC_ = 1 << IL_REC__ORD } IDENT_LBL_BIT; /* aux info */ const INCHI_TAG AuxLbl[] = { /* prefixes may be combined in this order */ /* AL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed-H */ /* AL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ /* AL_STER_ORD, */ { "/", "abs_stereo_inverted", "stereo.abs.inverted", 0 }, /* inv abs sp3 stereo */ /* AL_REVR_ORD, */ { "/", "reversibility", "reversibility", 0 }, /* reversibility */ /* items */ /* AL_VERS_ORD, */ { "", "version", "version", 1 }, /* AL_NORM_ORD, */ { "/", "normalization_type", "norm-type", 1 }, /* AL_ANBR_ORD, */ { "/N:", "original_atom_numbers", "atom.orig-nbr", 1 }, /* AL_AEQU_ORD, */ { "/E:", "atom_equivalence", "atom.equivalence", 0 }, /* AL_GEQU_ORD, */ { "/gE:", "group_equivalence", "group.equivalence", 0 }, /* inv abs sp3 stereo */ /* AL_SP3I_ORD, */ { "/it:", "sp3", "sp3", 0 }, /* AL_SP3N_ORD, */ { "/iN:", "original_atom_numbers", "atom.orig-nbr", 0 }, /* AL_CRV__ORD, */ { "/CRV:", "charge_radical_valence", "charges-rad-val", 0 }, /* reversibility */ /* AL_ATMR_ORD, */ { "/rA:", "atoms", "atoms", 0 }, /* AL_BNDR_ORD, */ { "/rB:", "bonds", "bonds", 0 }, /* AL_XYZR_ORD, */ { "/rC:", "xyz", "xyz", 0 }, /* fixed-H only */ /* AL_FIXN_ORD, */ { "/F:", "original_atom_numbers", "atom.orig-nbr", 1 }, /* isotopic only */ /* AL_ISON_ORD, */ { "/I:", "original_atom_numbers", "atom.orig-nbr", 1 }, /* AL_REC__ORD, */ { "/R:", "reconnected bond(s) to metal(s) part", "", 1 } }; typedef enum tagAuxLblOrd { AL_FIXH_ORD, AL_ISOT_ORD, AL_STER_ORD, AL_REVR_ORD, AL_VERS_ORD, AL_NORM_ORD, AL_ANBR_ORD, AL_AEQU_ORD, AL_GEQU_ORD, AL_SP3I_ORD, AL_SP3N_ORD, AL_CRV__ORD, AL_ATMR_ORD, AL_BNDR_ORD, AL_XYZR_ORD, AL_FIXN_ORD, AL_ISON_ORD, AL_REC__ORD, AL_MAX_ORD /* max number of tags */ } AUX_LBL_ORD; typedef enum tagAuxLblBit { AL_FIXH = 1 << AL_FIXH_ORD, AL_ISOT = 1 << AL_ISOT_ORD, AL_STER = 1 << AL_STER_ORD, AL_REVR = 1 << AL_REVR_ORD, AL_VERS = 1 << AL_VERS_ORD, AL_NORM = 1 << AL_NORM_ORD, AL_ANBR = 1 << AL_ANBR_ORD, AL_AEQU = 1 << AL_AEQU_ORD, AL_GEQU = 1 << AL_GEQU_ORD, AL_SP3I = 1 << AL_SP3I_ORD, AL_SP3N = 1 << AL_SP3N_ORD, AL_CRV_ = 1 << AL_CRV__ORD, AL_ATMR = 1 << AL_ATMR_ORD, AL_BNDR = 1 << AL_BNDR_ORD, AL_XYZR = 1 << AL_XYZR_ORD, AL_FIXN = 1 << AL_FIXN_ORD, AL_ISON = 1 << AL_ISON_ORD, AL_REC_ = 1 << AL_REC__ORD } AUX_LBL_BIT; const int MAX_TAG_NUM = inchi_max((int)IL_MAX_ORD, (int)AL_MAX_ORD); char *szGetTag(const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways); #define SP(N) (x_space+sizeof(x_space)-1-(N)) /**********************************************************************************************/ typedef struct tagXmlEntityRef { char nChar; const char *pRef; } X_REF; const X_REF xmlRef[] = { {'<', "<"}, {'&', "&"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {0, NULL}, }; const char szRefChars[sizeof(xmlRef)/sizeof(xmlRef[0])] = {'<', '&', '>', '"', '\'', '\0' }; /**********************************************************************************************/ int PrintXmlStartTag(char *pStr, int indent, int bEnd, const char *tag, const char *l1, int v1, const char *l2, int v2, const char *l3, int v3, const char *l4, int v4, const char *l5, int v5, const char *l6, int v6) { int len=0; if ( tag ) { len += sprintf( pStr+len, "%s<%s", SP(indent), tag); } if ( l1 ) { len += sprintf( pStr+len, " %s=\"%d\"", l1, v1); } if ( l2 ) { len += sprintf( pStr+len, " %s=\"%d\"", l2, v2); } if ( l3 ) { len += sprintf( pStr+len, " %s=\"%d\"", l3, v3); } if ( l4 ) { len += sprintf( pStr+len, " %s=\"%d\"", l4, v4); } if ( l5 ) { len += sprintf( pStr+len, " %s=\"%d\"", l5, v5); } if ( l6 ) { len += sprintf( pStr+len, " %s=\"%d\"", l6, v6); } if ( (bEnd & 3) ) { len += sprintf( pStr+len, "%s%s", (bEnd & 1)?"/":"", (bEnd & 2)?">":""); } return len; } /**********************************************************************************************/ int Needs2addXmlEntityRefs( const char *s ) { int len = 0; const X_REF *q = xmlRef, *r; const char *p; if ( s && *s ) { for ( q = xmlRef, len = 0; q->nChar; q ++ ) { for ( p = s; p = strchr( p, q->nChar ); p ++ ) { if ( q->nChar == '&' ) { for ( r = xmlRef; r->nChar; r ++ ) { if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) goto DoNotSubstitute; } } len += strlen(q->pRef)-1; DoNotSubstitute:; } } if ( len ) { len += strlen( s ); } } return len; } /**********************************************************************************************/ int AddXmlEntityRefs( const char *p, char *d ) { int len_d, n; const X_REF *q = xmlRef, *r; len_d = 0; while ( *p ) { n = strcspn( p, szRefChars ); if ( n > 0 ) { /* first n characters of p do not contain referenced chars; copy them */ strncpy( d+len_d, p, n ); /* does not have zero termination */ len_d += n; /* new destination length */ p += n; /* position of the referenced char in the source */ } if ( *p ) { if ( *p == '&' ) { for ( r = xmlRef; r->nChar; r ++ ) { if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) { d[len_d++] = *p; goto DoNotSubstitute; } } } q = xmlRef + (strchr( szRefChars, UCINT *p) - szRefChars); strcpy( d+len_d, q->pRef ); /* add entity reference and zero termination */ len_d += strlen( d + len_d ); /* new destination length */ DoNotSubstitute: p ++; } else { d[len_d] = '\0'; /* add zero termination */ } } return len_d; } /**********************************************************************************************/ int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ) { char pStr[128]; sprintf( pStr, "<%s %s=\"%s\">", x_inchi, x_inchi_ver, x_curr_ver ); inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); return 0; } /**********************************************************************************************/ int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ) { char pStr[128]; sprintf( pStr, "", x_inchi ); inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); return 0; } /**********************************************************************************************/ int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, int nStrLen, int bNoStructLabels, int num_input_struct, const char *szSdfLabel, const char *szSdfValue ) { char szBuf[64]; int nEstLen1; int nEstLen2; int ret = 0; int tot_len; char *pSdfLabel = NULL, *pSdfValue = NULL, *p; /* substitute special characters (see szRefChars[]) with xml Entity References */ int len; if ( bNoStructLabels ) { /* no labela at all */ inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ tot_len = 0; tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); tot_len += sprintf(pStr+tot_len, ">" ); inchi_ios_print( output_file, "%s\n", pStr ); ret = 1; /* success */ } else if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) { /* only structure number if present */ inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ tot_len = 0; tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); if ( num_input_struct > 0 ) { tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); } tot_len += sprintf(pStr+tot_len, ">" ); inchi_ios_print( output_file, "%s\n", pStr ); ret = 1; /* success */ } else { if ( len = Needs2addXmlEntityRefs( szSdfLabel ) ) { if ( p = (char*) inchi_malloc( len+1 ) ) { AddXmlEntityRefs( szSdfLabel, p ); szSdfLabel = pSdfLabel = p; } } if ( len = Needs2addXmlEntityRefs( szSdfValue ) ) { if ( p = (char*) inchi_malloc( len+1 ) ) { AddXmlEntityRefs( szSdfValue, p ); szSdfValue = pSdfValue = p; } } nEstLen1 = ind + 1 + sizeof(x_structure)-1 + 1 + sizeof(x_number)-1 + 1 + sprintf(szBuf,"\"%d\"", num_input_struct) + 2; nEstLen2 = 1 + sizeof(x_header)-1 + 1 + 2 + (szSdfLabel? strlen(szSdfLabel):0) + 1 + sizeof(x_value) -1 + 1 + 2 + (szSdfValue? strlen(szSdfValue):0) + 2; if ( nEstLen1 <= nStrLen ) { inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ tot_len = 0; tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); if ( nEstLen1 + nEstLen2 <= nStrLen ) { tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_header, szSdfLabel? szSdfLabel:x_empty); tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_value, szSdfValue? szSdfValue:x_empty); } tot_len += sprintf(pStr+tot_len, ">" ); inchi_ios_print( output_file, "%s\n", pStr ); ret = 1; /* success */ } if ( pSdfValue ) { inchi_free ( pSdfValue ); } if ( pSdfLabel ) { inchi_free( pSdfLabel ); } } return ret; /* 0 => Buffer overflow */ } /**********************************************************************************************/ int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ) { if ( output_file && pStr ) { int nEstLen1 = ind + 1 + 1 + sizeof(x_structure)-1 + 2; if ( nEstLen1 <= nStrLen ) { sprintf(pStr, "%s", SP(ind), x_structure); inchi_ios_print( output_file, "%s\n", pStr ); return 1; } } return 0; } /**********************************************************************************************/ int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, /*int nErrorNumber,*/ char *pErrorText, int bError ) { /* char szBuf[64]; */ const char *pErr; char *pNewErrorText=NULL, *szErrorText = pErrorText; int nEstLen, len=0, ret = 0; switch( bError ) { case _IS_WARNING: pErr = x_warn; break; case _IS_ERROR: pErr = x_err; break; default: /* _IS_FATAL */ pErr = x_ferr; break; } #if ( ENTITY_REFS_IN_XML_MESSAGES == 1 ) /* insert xml entity references if necessary */ if ( len = Needs2addXmlEntityRefs( szErrorText ) ) { if ( pNewErrorText = (char*) inchi_malloc( len+1 ) ) { AddXmlEntityRefs( szErrorText, pNewErrorText ); szErrorText = pNewErrorText; } } #else szErrorText = pErrorText; #endif nEstLen = ind + 1 + sizeof(x_message)-1 + 1 + sizeof(x_type)-1 + 1 + 1 + strlen(pErr)-1 /* + 1 + sizeof(x_code)-1 + 1 + sprintf(szBuf, "%d", nErrorNumber) */ + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 2; if ( nEstLen <= nStrLen ) { /* sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%d\" %s=\"%s\"/>", SP(ind), x_message, x_type, pErr, x_code, nErrorNumber, x_text, szErrorText ); */ sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%s\"/>", SP(ind), x_message, x_type, pErr, x_text, szErrorText ); inchi_ios_print( output_file, "%s\n", pStr ); /* pErrorText[0] = '\0'; // do not repeat same output */ ret = 1; } if ( pNewErrorText ) inchi_free( pNewErrorText ); return ret; } /**********************************************************************************************/ int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, char *pErrorText, int bError ) { /* char szBuf[64]; */ const char *pErr; char *pNewErrorText=NULL, *szErrorText = pErrorText; int nEstLen, ret = 0; switch( bError ) { case _IS_WARNING: pErr = x_warn; break; case _IS_ERROR: pErr = x_err; break; default: /* _IS_FATAL */ pErr = x_ferr; break; } /* <%s: >, x_message */ nEstLen = sizeof(x_message)-1 + 1 + 1 /* <%s=\"%s\">, x_type, pErr */ + sizeof(x_type)-1 + 1 + 1 + strlen(pErr) + 1 /* < %s=\"%s\"\n>, x_text, szErrorText */ + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 1 + 1; if ( nEstLen < nStrLen ) { sprintf( pStr, "%s: %s=\"%s\" %s=\"%s\"", x_message, x_type, pErr, x_text, szErrorText ); inchi_ios_print( output_file, "%s\n", pStr ); ret = 1; } if ( pNewErrorText ) inchi_free( pNewErrorText ); return ret; } /**************************************************************************/ #ifndef OUT_TN /* defined in mode.h; quoted here for reference purposes only */ #define OUT_N1 0 /* non-tautomeric only */ #define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ #define OUT_NT 2 /* only non-taut representations of tautomeric */ #define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; sepatately output non-taut representations of tautomeric if present */ /* OUT_TN = OUT_T1 + OUT_NT */ #endif /******************************************************************/ const char *EquString( int EquVal ) { int bFrom = EquVal & (iiSTEREO | iiSTEREO_INV | iiNUMB | iiEQU ); int bType = EquVal & (iitISO | iitNONTAUT ); int bEq2 = EquVal & (iiEq2NONTAUT | iiEq2ISO | iiEq2INV ); const char *r = ""; #if ( FIX_EMPTY_LAYER_BUG == 1 ) int bEmpty= EquVal & iiEmpty; if ( bEmpty ) { r = "e"; return r; } #endif switch ( bFrom ) { case iiSTEREO: /* ------------ Stereo --------------------*/ switch ( bType ) { case iitISO: /* iso main stereo =... */ switch( bEq2 ) { case 0: r = "m"; /* iso main stereo = main stereo */ break; default: r = "??"; /* should not happen */ break; } break; case iitNONTAUT: /* non-taut stereo =... */ switch( bEq2 ) { case 0: r = "m"; /* non-taut stereo = main stereo */ break; default: r = "??"; /* should not happen */ break; } break; case (iitNONTAUT | iitISO): /* iso non-taut stereo = ... */ switch( bEq2 ) { case 0: r = "m"; /* iso non-taut stereo = main stereo */ break; case iiEq2ISO: r = "M"; /* iso non-taut stereo = main iso stereo */ break; case iiEq2NONTAUT: r = "n"; /* iso non-taut stereo = non-taut stereo */ break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } break; case iiSTEREO_INV: /*---------- Inverted Aux Stereo ------*/ if ( bEq2 & iiEq2INV ) { /* stereo = Inverted(another stereo) */ bEq2 &= ~iiEq2INV; switch( bType ) { case 0: /* main = ...*/ switch( bEq2 ) { case 0: r = "im"; /* main = Inv(main) */ break; case iiEq2ISO: r = "iM"; /* main = Inv(main iso) */ break; case iiEq2NONTAUT: r = "in"; /* maim = Inv(non-taut) */ break; case (iiEq2NONTAUT | iiEq2ISO): r = "iN"; /* maim = Inv(non-taut iso ) */ break; default: r = "??"; /* should not happen */ break; } break; case iitISO: /* main iso = ...*/ switch( bEq2 ) { case 0: r = "im"; /* main iso = Inv(main) */ break; case iiEq2ISO: r = "iM"; /* main iso = Inv(main iso) */ break; case iiEq2NONTAUT: r = "in"; /* maim iso = Inv(non-taut) */ break; case (iiEq2NONTAUT | iiEq2ISO): r = "iN"; /* maim = Inv(non-taut iso ) */ break; default: r = "??"; /* should not happen */ break; } break; case iitNONTAUT: /* non-taut = ... */ switch( bEq2 ) { case 0: r = "im"; /* non-taut = Inv(main) */ break; case iiEq2ISO: r = "iM"; /* non-taut = Inv(main iso) */ break; case iiEq2NONTAUT: r = "in"; /* non-taut = Inv(non-taut) */ break; case (iiEq2NONTAUT | iiEq2ISO): r = "iN"; /* non-taut = Inv(non-taut iso ) */ break; default: r = "??"; /* should not happen */ break; } break; case (iitNONTAUT | iitISO): switch( bEq2 ) { case 0: r = "im"; /* non-taut iso = Inv(main) */ break; case iiEq2ISO: r = "iM"; /* non-taut iso = Inv(main iso) */ break; case iiEq2NONTAUT: r = "in"; /* non-taut iso = Inv(non-taut) */ break; case (iiEq2NONTAUT | iiEq2ISO): r = "iN"; /* non-taut iso = Inv(non-taut iso ) */ break; default: r = "??"; /* should not happen */ } break; default: r = "??"; /* should not happen */ break; } } else { /* Inv stereo = another (non-inverted) stereo */ switch( bType ) { case iitISO: /* main iso = ...*/ switch( bEq2 ) { case 0: r = "m"; /* main = (inverted aux) main */ break; default: r = "??"; /* should not happen */ break; } break; case iitNONTAUT: /* non-taut = ... */ switch( bEq2 ) { case 0: r = "m"; /* non-taut = (inverted aux) main */ break; default: r = "??"; /* should not happen */ break; } break; case (iitNONTAUT | iitISO): /* non-taut iso = ...*/ switch( bEq2 ) { case 0: r = "m"; /* non-taut iso = (inverted aux) main */ break; case iiEq2ISO: r = "M"; /* non-taut iso = (inverted aux) main iso */ break; case iiEq2NONTAUT: r = "n"; /* non-taut iso = (inverted aux) non-taut */ break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } } break; case ( iiNUMB | iiSTEREO_INV): /*------------- Inv Stereo Numbering ------------*/ switch( bType ) { case 0: /* inv stereo numb main = ...*/ switch( bEq2 ) { case 0: r = "m"; /* inv stereo numb main = main numb */ break; default: r = "??"; /* should not happen */ break; } break; case iitISO: /* inv stereo iso numb main = ...*/ switch( bEq2 ) { case 0: r = "m"; /* inv stereo iso numb main = main numb */ break; case iiEq2INV: r = "im"; /* inv stereo iso numb main = InvStereo(main) numb */ break; case iiEq2ISO: r = "M"; /* inv stereo iso numb main = isotopic main numb */ break; default: r = "??"; /* should not happen */ break; } break; case iitNONTAUT: /* inv stereo numb non-taut = ... */ switch( bEq2 ) { case 0: r = "m"; /* inv stereo numb non-taut = main numb */ break; case iiEq2NONTAUT: r = "n"; /* inv stereo numb non-taut = non-taut numb */ break; case iiEq2INV: r = "im"; /* inv stereo numb non-taut = InvStereo(main) numb */ break; default: r = "??"; /* should not happen */ break; } break; case (iitNONTAUT | iitISO): /* inv stereo numb non-taut iso = ... */ switch( bEq2 ) { case 0: r = "m"; /* inv stereo numb non-taut iso = main numb */ break; case iiEq2ISO: r = "M"; /* inv stereo numb non-taut iso = main numb iso */ break; case (iiEq2ISO | iiEq2INV): r = "iM"; /* inv stereo numb non-taut iso = InvStereo(main iso) numb */ break; case iiEq2NONTAUT: r = "n"; /* inv stereo numb non-taut iso = non-taut numb */ break; case (iiEq2NONTAUT | iiEq2ISO): r = "N"; /* inv stereo numb non-taut iso = non-taut iso numb */ break; case iiEq2INV: r = "im"; /* inv stereo numb non-taut iso = InvStereo(main) numb */ break; case (iiEq2NONTAUT | iiEq2INV): r = "in"; /* inv stereo numb non-taut iso = InvStereo(non-taut) numb ) */ break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } break; case iiNUMB: /*------------- Canonical Numbering ------------*/ switch( bType ) { case 0: /* numb main = ...*/ r = "??"; /* should not happen */ break; case iitISO: /* iso numb main = ...*/ switch( bEq2 ) { case 0: r = "m"; /* iso numb main = main numb */ break; default: r = "??"; /* should not happen */ } break; case iitNONTAUT: /* numb non-taut = ... */ switch( bEq2 ) { case 0: r = "m"; /* numb non-taut = main numb */ break; default: r = "??"; /* should not happen */ } break; case (iitNONTAUT | iitISO): /* numb non-taut iso = ... */ switch( bEq2 ) { case 0: r = "m"; /* numb non-taut iso = main numb */ break; case iiEq2ISO: r = "M"; /* numb non-taut iso = main numb iso */ break; case iiEq2NONTAUT: r = "n"; /* numb non-taut iso = non-taut numb */ break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } break; case iiEQU: /*------------- Atom Equivalence ------------*/ switch( bType ) { case 0: /* equivalence main = ...*/ r = "??"; /* should not happen */ break; case iitISO: /* equivalence main iso = ...*/ switch( bEq2 ) { case 0: r = "m"; /* equivalence main = main equ */ break; default: r = "??"; /* should not happen */ break; } break; case iitNONTAUT: /* equivalence non-taut = ... */ switch( bEq2 ) { case 0: r = "m"; /* equivalence non-taut = main equ */ break; default: r = "??"; /* should not happen */ break; } break; case (iitNONTAUT | iitISO): /* equivalence non-taut iso = ... */ switch( bEq2 ) { case 0: r = "m"; /* equivalence non-taut iso = main equ */ break; case iiEq2ISO: r = "M"; /* equivalence non-taut iso = main iso equ */ break; case iiEq2NONTAUT: r = "n"; /* equivalence non-taut iso = non-taut equ */ break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } break; default: r = "??"; /* should not happen */ break; } return r; } /**********************************************************************************************/ #define OUT_NONTAUT OUT_NN /* was OUT_NT until 2004-04-07 */ /**********************************************************************************************/ int OutputINChI2(char *pStr, int nStrLen, INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], int iINChI, ORIG_STRUCT *pOrigStruct, int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, int num_components2[], int num_non_taut2[], int num_taut2[], INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, int num_input_struct, const char *szSdfLabel, const char *szSdfValue, long lSdfId, int *pSortPrintINChIFlags, unsigned char save_opt_bits) { int bINChIOutputOptions0 = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS); int bINChIOutputOptionsCur; int bCurOption, ret, i; ret = 0; for ( i = 0; i < 3; i ++ ) { switch( i ) { case 0: bCurOption = INCHI_OUT_XML; break; case 1: bCurOption = INCHI_OUT_PLAIN_TEXT; break; case 2: bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; break; default: continue; } if ( bINChIOutputOptions & bCurOption ) { bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; if ( i != 1 ) { bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; } ret |= OutputINChI1( pStr, nStrLen, pINChISortTautAndNonTaut2, iINChI, pOrigStruct, bDisconnectedCoord, bOutputType, bINChIOutputOptionsCur, bXml, bAbcNumbers,bCtPredecessors, bNoStructLabels, num_components2, num_non_taut2, num_taut2, output_file, log_file, num_input_struct, szSdfLabel, szSdfValue, lSdfId, pSortPrintINChIFlags, save_opt_bits); } } return ret; } /**********************************************************************************/ char *szGetTag( const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways ) { int i, j, bit, num, len; if ( 0 < nTag && nTag < 3 ) { /* no plain text comments: pick up the last tag */ for ( i = 0, j = -1, bit = 1; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { if ( bTag & bit ) { j = i; } } if ( j >= 0 ) { strcpy( szTag, nTag == 1? Tag[j].szXmlLabel : nTag == 2? Tag[j].szPlainLabel : "???" ); if ( nTag != 2 ) { *bAlways = Tag[j].bAlwaysOutput; } return szTag; } } else if ( nTag == 3 ) { /* plain text with comments */ szTag[0] = '{'; szTag[1] = '\0'; for ( i = 0, j = -1, bit = 1, num=0; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { if ( bTag & bit ) { j = i; if ( num ++ ) { strcat( szTag, ":" ); } strcat( szTag, Tag[i].szPlainComment ); } } if ( num ) { strcat( szTag, "}" ); num = strlen( Tag[j].szPlainLabel ); len = strlen( szTag ); if ( len ) { memmove( szTag + num, szTag, len+1 ); memcpy( szTag, Tag[j].szPlainLabel, num ); } else { strcpy ( szTag, Tag[j].szPlainLabel ); } *bAlways = Tag[j].bAlwaysOutput; } else { strcpy( szTag, "???" ); } return szTag; } strcpy( szTag, "???" ); return szTag; } /***************************************************************************************/ /* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ /***************************************************************************************/ int OutputINChI1(char *pStr, int nStrLen, INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], int iINChI, ORIG_STRUCT *pOrigStruct, int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, int num_components2[], int num_non_taut2[], int num_taut2[], INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, int num_input_struct, const char *szSdfLabel, const char *szSdfValue, long lSdfId, int *pSortPrintINChIFlags, unsigned char save_opt_bits) { /* bINChIOutputOptions bits: INCHI_OUT_NO_AUX_INFO 0x0001 do not output Aux Info INCHI_OUT_SHORT_AUX_INFO 0x0002 output short version of Aux Info INCHI_OUT_ONLY_AUX_INFO 0x0004 output only Aux Info INCHI_OUT_EMBED_REC 0x0008 embed reconnected INChI into disconnected INChI */ /*int ATOM_MODE = ((bAbcNumbers?2:0)|5|(bCtPredecessors?8:0));*/ int ATOM_MODE = ((bAbcNumbers?CT_MODE_ABC_NUMBERS:0) | CT_MODE_ATOM_COUNTS | CT_MODE_NO_ORPHANS #if ( EQL_H_NUM_TOGETHER == 1 ) | CT_MODE_EQL_H_TOGETHER #endif #if ( ABC_CT_NUM_CLOSURES == 1 ) | (bAbcNumbers && bCtPredecessors? CT_MODE_ABC_NUM_CLOSURES:0) #endif | (bCtPredecessors?CT_MODE_PREDECESSORS:0)); int TAUT_MODE = (bAbcNumbers?CT_MODE_ABC_NUMBERS:0); char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; /* bOutputType = TAUT_YES => tautomeric only (if no tautomeric components then no output; TAUT_NON => only non-tautomeric output (if no non-taut present then no output; TAUT_BOTH => tautomeric and non-tautomeric */ int i, j, ii, jj, /*ii2, jj2,*/ tot_len, tot_len2, bOverflow, bEmbeddedOutputCalled=0; int bIsotopic, bTautIsoHNum, bTautIsoAt, bHasIsotopicAtoms[TAUT_NUM]; int bStereoSp2[TAUT_NUM], bStereoSp3[TAUT_NUM]; int bIsotopicStereoSp2[TAUT_NUM], bIsotopicStereoSp3[TAUT_NUM]; int bStereoAbsInverted[TAUT_NUM], bIsotopicStereoAbsInverted[TAUT_NUM]; int bStereoAbs[TAUT_NUM], bIsotopicStereoAbs[TAUT_NUM]; int bAtomEqu[TAUT_NUM], bTautEqu[TAUT_NUM], bIsotopicAtomEqu[TAUT_NUM], bIsotopicTautEqu[TAUT_NUM]; int bInvStereo[TAUT_NUM], bInvIsotopicStereo[TAUT_NUM]; int bInvStereoOrigNumb[TAUT_NUM], bInvIsotopicStereoOrigNumb[TAUT_NUM], bIsotopicOrigNumb[TAUT_NUM]; int bTautomeric, bNonTautomeric, bTautomericAcid, bHardAddRemProton, iCurTautMode; int bRequestedRacemicStereo=0, bRequestedRelativeStereo = 0, bRelRac; int bRacemicStereo[TAUT_NUM], bRelativeStereo[TAUT_NUM]; int bIsotopicRacemicStereo[TAUT_NUM], bIsotopicRelativeStereo[TAUT_NUM]; int bChargesRadVal[TAUT_NUM], bOrigCoord[TAUT_NUM]; int bIgn_UU_Sp3[TAUT_NUM], bIgn_UU_Sp2[TAUT_NUM]; int bIgn_UU_Sp3_Iso[TAUT_NUM], bIgn_UU_Sp2_Iso[TAUT_NUM]; int ind, inc, bNonTautIsIdenticalToTaut = 1; int bNonTautNonIsoIdentifierNotEmpty = 0, bNonTautIsoIdentifierNotEmpty = 0; INCHI_SORT **pINChISortTautAndNonTaut = pINChISortTautAndNonTaut2[iINChI]; INCHI_SORT *pINChISort =pINChISortTautAndNonTaut[TAUT_YES]; INCHI_SORT *pINChISort2=pINChISortTautAndNonTaut[TAUT_YES]; INCHI_SORT *is, *is2; INChI *pINChI /*, *pINChI2*/; INChI_Aux *pINChI_Aux = NULL; int ret = 0; /* 0=>failed, 1=>success */ int bOutType = bOutputType; /* ??? */ int nTag; int bTautomericOutputAllowed, bSecondNonTautPass; int num_components = num_components2[iINChI]; int num_comp[TAUT_NUM], max_num_comp; int num_iso_H[NUM_H_ISOTOPES], bHasIsoH; int nNumRemovedProtons, nNumMovedProtons; int bTautAndNonTaut, bTautIsNonTaut; int bAlways = 0; int bUseMulipliers = 1; int bOmitRepetitions = 1; int bPlainTextTags = 2; /* 0 => no plain tags, 1=> plain text tags, 2=>plaintext tags without consecutive // */ int bPlainText = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS)); int bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS); int bPlainTabbedOutput; int bTag1, bTag2, bTag3, bFhTag; /* tag bits */ int nCurINChISegment, nSegmAction; char szTag1[MAX_TAG_LEN], szTag2[MAX_TAG_LEN], szTag3[MAX_TAG_LEN]; const char *pLF, *pTAB; /*^^^ 15 April, 2008 */ int bFixTranspChargeBug = 0; #if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) /* 2008-01-02 */ if ( INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG & bINChIOutputOptions ) bFixTranspChargeBug = 1; #endif /*^^^ 15 April, 2008 */ bXml = 0 != (bINChIOutputOptions & INCHI_OUT_XML); nTag = bPlainTextCommnts? 3 : bPlainText? 2 : bXml? 1 : 0; /* tag type */ ind = bXml? 1 : -1; inc = bXml? 1 : -1; pLF = bPlainTextCommnts? "\n" : "\0"; bFhTag = 0; bPlainTabbedOutput = 0 != (bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT) && bPlainText && !bXml && !bPlainTextCommnts; #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) pTAB = bPlainTabbedOutput? "\t" : "\n"; #else pTAB = "\n"; #endif memset( sDifSegs, DIFV_BOTH_EMPTY, sizeof(sDifSegs) ); if ( !pStr ) { inchi_ios_eprint(log_file, "Cannot allocate output buffer. No output for structure #%d.%s%s%s%s\n", num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue)); return ret; } bSecondNonTautPass = 0; /* -- commented out to allow empty InChI -- if (!num_components ) { return 0; } */ /* init version string */ if ( !VER_STRING[0] ) { strcpy(VER_STRING, "(V"); strcat(VER_STRING, INCHI_VERSION); strcat(VER_STRING, ")"); } for ( i = 0; i < TAUT_NUM; i ++ ) { bHasIsotopicAtoms[i] = num_comp[i] = bStereoSp2[i] = bStereoSp3[i] = bIsotopicStereoSp2[i] = bIsotopicStereoSp3[i] = bIsotopicOrigNumb[i] = bStereoAbs[i] = bIsotopicStereoAbs[i] = bStereoAbsInverted[i] = bIsotopicStereoAbsInverted[i] = bRacemicStereo[i] = bRelativeStereo[i] = bIsotopicRacemicStereo[i] = bIsotopicRelativeStereo[i] = bAtomEqu[i] = bTautEqu[i] = bIsotopicAtomEqu[i] = bIsotopicTautEqu[i] = bInvStereo[i] = bInvIsotopicStereo[i] = bInvStereoOrigNumb[i] = bInvIsotopicStereoOrigNumb[i] = bIgn_UU_Sp3[i] = bIgn_UU_Sp2[i] = bIgn_UU_Sp3_Iso[i] = bIgn_UU_Sp2_Iso[i] = bChargesRadVal[i] = bOrigCoord[i] = 0; } /* find if it is isotopic */ bIsotopic = bTautomeric = bNonTautomeric = bTautomericAcid = bHardAddRemProton = bTautIsoHNum = bTautIsoAt = 0; bTautAndNonTaut = bTautIsNonTaut = 0; /* x = bStereo, bStereoSp2, bStereoSp3, bStereoAbsInverted, bIsotopicStereo, bIsotopicStereoSp2, bIsotopicStereoSp3, bIsotopicStereoAbsInverted OUT_N1: x[TAUT_NON] refers to non-tautomeric only OUT_T1: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric OUT_NT: x[TAUT_NON] refers to non-taut representations of tautomeric OUT_TN: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric x[TAUT_NON] refers to non-taut representations of tautomeric */ memset( num_iso_H, 0, sizeof(num_iso_H) ); nNumRemovedProtons = 0; nNumMovedProtons = 0; bHasIsoH = 0; bTautomericOutputAllowed = (bOutType==OUT_T1 || bOutType== OUT_TN); pINChISort=pINChISortTautAndNonTaut[bTautomericOutputAllowed? TAUT_YES : TAUT_NON]; is = pINChISort; is2 = (bOutType== OUT_TN)? pINChISortTautAndNonTaut[TAUT_NON] : NULL; for ( i = 0, is2 = pINChISortTautAndNonTaut[TAUT_NON]; i < num_components; i ++, is ++, is2? is2++:NULL ) { CompINChILayers( is, is2, sDifSegs, bFixTranspChargeBug ); bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !CompINChITautVsNonTaut(is, is2, 1); if ( is && (pINChI_Aux = is->pINChI_Aux[TAUT_YES]) ) { for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { bHasIsoH += abs(pINChI_Aux->nNumRemovedIsotopicH[j]); num_iso_H[j] += pINChI_Aux->nNumRemovedIsotopicH[j]; } nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; nNumMovedProtons += abs(pINChI_Aux->nNumRemovedProtons); } if ( bTautomericOutputAllowed ) { /* check for removed isotopic H */ for ( j = TAUT_YES; j < TAUT_NUM; j ++ ) { switch ( bOutType ) { case OUT_N1: /* x[TAUT_NON]: non-tautomeric only -- never happens */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_NON; break; case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_YES; break; case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric -- never happens */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_NON; break; /* main path of control flow */ case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; * x[TAUT_NON]: non-taut only if tautomeric is present */ jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; if ( jj == TAUT_YES ) { /* Fix12 */ if ( is->pINChI[jj]->lenTautomer > 0 ) { bTautAndNonTaut += (!is->pINChI[jj]->bDeleted && HAS_N(is)); } else { bTautIsNonTaut ++; } } if ( jj < 0 ) continue; ii = j; break; default: continue; } if ( jj != j ) continue; if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 && (pINChI_Aux = is->pINChI_Aux[jj]) ) { bTautIsoHNum += (pINChI_Aux->nNumRemovedIsotopicH[0] + pINChI_Aux->nNumRemovedIsotopicH[1] + pINChI_Aux->nNumRemovedIsotopicH[2]); bTautIsoAt += (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); } } } } sDifSegs[DIFL_M ][DIFS_p_PROTONS] = nNumRemovedProtons? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; sDifSegs[DIFL_MI][DIFS_h_H_ATOMS] = bHasIsoH? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; MarkUnusedAndEmptyLayers( sDifSegs ); bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !bTautIsoHNum; /*********************************************************************************************/ for ( i = 0, is = pINChISort; i < num_components; i ++, is ++ ) { int bCurIso, bCurStereo, bCurIsoStereo, bCurHasIsoStereo /* Fix14 */, bCurTaut /*, bCurTaut2*/; int bCompExists, bCurIsoHPos, bCurIsoHStereo; int bCurStereoSp2, bCurIsoStereoSp2, bCurStereoSp3, bCurIsoStereoSp3, bCurIsoStereoSp3Inv; int bCurRacemic, bCurRelative, bCurIsoRacemic, bCurIsoRelative; bCompExists = 0; for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) { switch ( bOutType ) { case OUT_N1: /* x[TAUT_NON]: non-tautomeric only */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_NON; break; case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_YES; break; case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric */ jj = GET_II(bOutType,is); if ( jj != j ) continue; ii = TAUT_NON; break; /* main control flow comes here: requested both mobile and fixed H results */ case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; * x[TAUT_NON]: non-taut only if tautomeric is present */ jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; if ( jj < 0 ) { /* Fix12 */ if ( bTautAndNonTaut && bTautIsNonTaut && j == TAUT_NON && 0 <= (jj = GET_II(OUT_T1,is)) && !is->pINChI[jj]->bDeleted && !is->pINChI[jj]->lenTautomer ) { ; /* the requested non-tautomeric component is in tautomeric position (is->pINChI[TAUT_YES]); process it also as non-tautomeric if Fixed-H layer was requested */ } else { continue; } } ii = j; /* ii is what we wanted; jj is what we found (0 = TAUT_NON: fixed_H, 1 = TAUT_YES: mobile_H) */ /* -- not used 2004-09-16 --- if ( is2 ) { jj2 = ( j == TAUT_YES )? GET_II(OUT_T1,is2) : ( j == TAUT_NON )? GET_II(OUT_NT,is2) : -1; if ( jj2 >= 0 ) { ii2 = j; } else { ii2 = -1; } } else { jj2 = ii2 = -1; } -----------------------------*/ break; default: continue; } if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 ) { /*pINChI_Aux = is->pINChI_Aux[jj];*/ bCompExists ++; bCurTaut = (pINChI->lenTautomer > 0); bCurIso = (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); bCurIsoHPos = (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1 || pINChI->lenTautomer > 1); /* present isotopic H + their possible positions AND/OR isotopic atoms */ bCurIsoHStereo = bCurIsoHPos && (bTautIsoHNum || bTautIsoAt) || bCurIso; if ( jj == j && pINChI->bDeleted ) { num_comp[j] --; if ( bCurTaut ) { bTautomeric |= 1; /* tautomeric representation is present */ bNonTautomeric |= HAS_N(is); } bIsotopic |= bCurIso; continue; /* deleted H(+) in tautomeric representation */ } bCurStereoSp2 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoBonds > 0); bCurHasIsoStereo = bCurStereoSp3 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoCenters > 0 ); bCurIsoStereoSp2 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoBonds > 0); bCurIsoStereoSp3 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0); bCurIsoStereoSp3Inv = bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs; /* inversion changes sp3 stereo */ bRequestedRacemicStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_RAC_STEREO)); bRequestedRelativeStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_REL_STEREO)); /* check whether isotopic stereo is same as non-isotopic; if same than do not output isotopic stereo */ if ( bCurStereoSp2 && bCurIsoStereoSp2 ) { bCurIsoStereoSp2 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP2, pINChI->StereoIsotopic, EQL_SP2, 0 ); } if ( bCurStereoSp3 && bCurIsoStereoSp3 ) { /* bCurIsoStereoSp3=0 means (iso stereo sp3) = (non-iso stereo sp3) or (iso stereo sp3) = Inv(non-iso stereo sp3) */ bCurIsoStereoSp3 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP3, pINChI->StereoIsotopic, EQL_SP3, (pINChI->nFlags & INCHI_FLAG_RAC_STEREO) || (pINChI->nFlags & INCHI_FLAG_REL_STEREO) ); if ( !bCurIsoStereoSp3 ) { /* inversion changes iso sp3 differently from non-iso sp3 Fix11 */ bCurIsoStereoSp3Inv &= (pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs); } } bCurRelative = bRequestedRelativeStereo && bCurStereoSp3; #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) bCurRelative = bCurRelative && (pINChI->Stereo->nNumberOfStereoCenters > 1 ) && (pINChI->Stereo->nCompInv2Abs != 0) && #endif bCurIsoRelative = bRequestedRelativeStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) bCurIsoRelative = bCurIsoRelative && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 1 ) && (pINChI->StereoIsotopic->nCompInv2Abs != 0) && #endif bCurRacemic = bRequestedRacemicStereo && bCurStereoSp3; #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) bCurRacemic = bCurRacemic && (pINChI->Stereo->nCompInv2Abs != 0) && (pINChI->Stereo->nNumberOfStereoCenters > 0 ) ? pINChI->Stereo->nNumberOfStereoCenters : 0; #endif bCurIsoRacemic = bRequestedRacemicStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) bCurIsoRacemic = bCurIsoRacemic & (pINChI->StereoIsotopic->nCompInv2Abs != 0) && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0 ) ? pINChI->StereoIsotopic->nNumberOfStereoCenters : 0; #endif if ( bRequestedRelativeStereo ) { bCurStereoSp3 = bCurRelative || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ bCurIsoStereoSp3 = bCurIsoRelative ? bCurIsoStereoSp3 : 0; } else { if ( bRequestedRacemicStereo ) { bCurStereoSp3 = bCurRacemic > 1 || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ bCurIsoStereoSp3 = bCurIsoRacemic > 1? bCurIsoStereoSp3 : 0; } } bCurStereo = bCurStereoSp2 || bCurStereoSp3; bCurIsoStereo = bCurIsoStereoSp2 || bCurIsoStereoSp3; bIsotopic |= bCurIso; bHasIsotopicAtoms[ii] |= bCurIso; bStereoSp2[ii] |= bCurStereoSp2; bStereoSp3[ii] |= bCurStereoSp3; bIgn_UU_Sp3[ii] |= !bCurStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_UU); bIgn_UU_Sp2[ii] |= !bCurStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_UU); bIsotopicStereoSp2[ii] |= bCurIsoStereoSp2; bIsotopicStereoSp3[ii] |= bCurIsoStereoSp3; bIgn_UU_Sp3_Iso[ii] |= !bCurIsoStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_ISO_UU); bIgn_UU_Sp2_Iso[ii] |= !bCurIsoStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_ISO_UU); bStereoAbs[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs != 0); bStereoAbsInverted[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs < 0); /* Fix08: missing isotopic inverted flag if isotopic = inverted non-isotopic */ bIsotopicStereoAbsInverted[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs < 0) || !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && pINChI->StereoIsotopic->nCompInv2Abs && pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; /* Fix 11: missing /s1 if only isotopic stereo is inverted */ bIsotopicStereoAbs[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs != 0) || !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && pINChI->StereoIsotopic->nCompInv2Abs && pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; bRelativeStereo[ii] |= bCurRelative; bIsotopicRelativeStereo[ii] |= bCurIsoRelative; bRacemicStereo[ii] |= bCurRacemic; bIsotopicRacemicStereo[ii] |= bCurIsoRacemic; bTautomericAcid |= (0!=(pINChI->nFlags & INCHI_FLAG_ACID_TAUT)); bHardAddRemProton |= (0!=(pINChI->nFlags & INCHI_FLAG_HARD_ADD_REM_PROTON)); if ( bCurTaut ) { bTautomeric |= 1; /* tautomeric representation is present */ /* does tautomeric structure have also a non-tautomeric repesentation? */ bNonTautomeric |= HAS_N(is); } /* auxiliary info */ if ( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) && (pINChI_Aux = is->pINChI_Aux[jj]) ) { /* detect presence of constitutional equivalence onfo */ int bCurEqu, bCurTautEqu=0, bCurIsoEqu=0, bCurIsoTautEqu=0; /* Fix15-disabled */ bAtomEqu[ii] |= (bCurEqu = bHasEquString( pINChI_Aux->nConstitEquNumbers, pINChI_Aux->nNumberOfAtoms)); if ( bCurTaut ) { bTautEqu[ii] |= (bCurTautEqu = bHasEquString( pINChI_Aux->nConstitEquTGroupNumbers, pINChI_Aux->nNumberOfTGroups)); } if ( bCurIso ) { bIsotopicAtomEqu[ii] |= (bCurIsoEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicNumbers, pINChI_Aux->nNumberOfAtoms)) /*|| bCurEqu*/; if ( bCurTaut ) { bIsotopicTautEqu[ii] |= (bCurIsoTautEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicTGroupNumbers, pINChI_Aux->nNumberOfTGroups)) /*|| bCurTautEqu*/; } /* non-zero if isotopic numbering for inverted isotopic stereo is different */ bIsotopicOrigNumb[ii] |= bCurHasIsoStereo && /* Fix14 */ pINChI_Aux->nOrigAtNosInCanonOrdInv && pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrdInv, pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, sizeof(pINChI_Aux->nOrigAtNosInCanonOrdInv[0]) * pINChI_Aux->nNumberOfAtoms)); } /* inverted stereo */ if ( bCurStereoSp3 && pINChI->Stereo->nCompInv2Abs ) { bInvStereo[ii] |= 1; bInvStereoOrigNumb[ii] |= pINChI_Aux->nOrigAtNosInCanonOrd && pINChI_Aux->nOrigAtNosInCanonOrdInv && (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrd, pINChI_Aux->nOrigAtNosInCanonOrdInv, sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]) * pINChI_Aux->nNumberOfAtoms)); } /* inverted isotopic stereo */ if ( bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs ) { bInvIsotopicStereo[ii] |= 1; bInvIsotopicStereoOrigNumb[ii] |= pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv && (0 != memcmp( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]) * pINChI_Aux->nNumberOfAtoms)); } if ( pINChI_Aux->OrigInfo && bHasOrigInfo(pINChI_Aux->OrigInfo, pINChI_Aux->nNumberOfAtoms) ) { bChargesRadVal[ii] |= 1; } } } } if ( bCompExists ) { for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) { num_comp[j] ++; } } } if ( bTautomeric /*&& bTautomericAcid*/ ) /* "&& bTautomericAcid" commented out 2004-06-02 */ { bTautomeric += bTautomericAcid; /* long-range tautomerism */ bTautomeric += (bHardAddRemProton? 4 : 0); } if ( bRequestedRacemicStereo || bRequestedRelativeStereo ) { /* do not output inverted stereo info */ for ( i = 0; i < TAUT_NUM; i ++ ) { /* Fix11 */ bStereoAbsInverted[i] = bStereoAbs[i] = bInvStereo[i] = bInvStereoOrigNumb[i] = 0; /* bIsotopicRelativeStereo[i]=0 may happen because iso stereo is same or inverted non-iso stereo */ bIsotopicStereoAbsInverted[i] = bIsotopicStereoAbs[i] = bInvIsotopicStereo[i] = bInvIsotopicStereoOrigNumb[i] = 0; /* -- commented out: Fix11-- if ( bRacemicStereo[i] || bRelativeStereo[i] ) { bStereoAbsInverted[i] = bStereoAbs[i] = bInvStereo[i] = bInvStereoOrigNumb[i] = 0; } if ( bIsotopicRacemicStereo[i] || bIsotopicRelativeStereo[i] ) { bIsotopicStereoAbsInverted[i] = bIsotopicStereoAbs[i] = bInvIsotopicStereo[i] = bInvIsotopicStereoOrigNumb[i] = 0; } */ } } iCurTautMode = bOutType == OUT_N1? TAUT_NON: /* only non-taut */ bOutType == OUT_T1? TAUT_YES: /* tautomeric if present, otherwise non-tautomeric */ bOutType == OUT_NT? TAUT_NON: /* only non-taut representations of tautomeric */ bOutType == OUT_TN? TAUT_YES: /* tautomeric if present otherwise non-tautomeric; */ -1; /* separately output non-taut representations of tautomeric if present */ if ( iCurTautMode < 0 ) { return 0; /* error */ } if ( bXml ) { ind += inc* (1+iINChI); } bOverflow = 0; num_components = num_comp[iCurTautMode]; max_num_comp = inchi_max(num_comp[TAUT_NON], num_comp[TAUT_YES]); if ( bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) { goto output_aux_info; } nCurINChISegment = DIFL_M; /****************************************** * * Structure (Compound) Header * ******************************************/ if ( bXml ) { /* -- moved to the line above goto output_aux_info; ind += inc* (1+iINChI); */ /* basic title, version */ if ( INCHI_BAS == iINChI ) { inchi_ios_print( output_file, "\n" ); /* empty line */ } tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", SP(ind), x_basic, x_ver, x_curr_ver); if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) { tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); } if ( bAbcNumbers || bCtPredecessors ) { const char *pNumber = ""; const char *pDelim = ""; const char *pCtType = ""; if ( bAbcNumbers && bCtPredecessors ) { pNumber = x_type_short; } else { pNumber = bAbcNumbers? x_type_alpha : x_type_numer; pDelim = (bAbcNumbers && bCtPredecessors)? "-":""; pCtType = bCtPredecessors? x_type_predec:""; } /* type */ tot_len += sprintf(pStr+tot_len, " %s=\"%s%s%s\"", x_type, pNumber, pDelim, pCtType); } sprintf(pStr+tot_len,">"); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } else if ( INCHI_BAS == iINChI ) { /* eliminate empty line in plain text output */ if ( bNoStructLabels ) { ; /* -- removed empty line before InChI --- #ifndef TARGET_API_LIB inchi_ios_print( output_file, "\n" ); #else ; #endif */ } else { if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) { tot_len = sprintf( pStr, "%sStructure: %d", pLF, num_input_struct ); inchi_ios_print( output_file, "%s%s", pStr, pTAB ); } else { tot_len = sprintf( pStr, "%sStructure: %d.%s%s%s%s", pLF, num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue) ); if ( lSdfId ) { tot_len --; tot_len += sprintf( pStr + tot_len, ":%ld", lSdfId ); } inchi_ios_print( output_file, "%s%s", pStr, pTAB ); } } /* inchi_ios_print( output_file, "%s%s=%s", pLF, (FLAG_SORT_PRINT_ReChI_PREFIX & *pSortPrintINChIFlags)? INCHI_REC_NAME : INCHI_NAME, pLF ); */ inchi_ios_print( output_file, "%s%s=%s", pLF, INCHI_NAME, pLF ); } /***************************************************** * * version (10-29-2003) * ****************************************************/ if ( INCHI_BAS == iINChI || !(bINChIOutputOptions & INCHI_OUT_EMBED_REC) /* || !bXml */) { /* xml: only if the first or not embedded; plain: always */ szGetTag( IdentLbl, nTag, bTag1 = IL_VERS, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); /* 10-17-2008 Add 'standard' flag if necessary */ if ( bINChIOutputOptions & INCHI_OUT_STDINCHI ) tot_len += sprintf(pStr + tot_len, "S"); /*if ( bXml ) {*/ /* avoid leading slash in plain output */ if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; /*}*/ inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /***************************************************** * * atoms, connection tables and tautomeric info * ****************************************************/ /******************* constitution: dot-disconnected Hill formulas: */ if ( num_components2[0] || num_components2[1] ) { szGetTag( IdentLbl, nTag, bTag1 = INCHI_REC == iINChI? IL_REC_ : IL_FML_, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len = str_HillFormula(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, num_components, bUseMulipliers); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, 1 ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /**************** semicolon/dot-disconnected connection tables */ szGetTag( IdentLbl, nTag, bTag1 = IL_CONN, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len2 = str_Connections(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); /* current version does not output empty (";;;;") connectivity */ if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-30: never output empty connection table */ tot_len = tot_len2; if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, bPlainTextTags ) ) goto exit_function; /* pStr overfow */ inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /************** hydrogen atoms; do not output empty */ if ( INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) { szGetTag( IdentLbl, nTag, bTag1 = IL_ALLH, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len2 = str_H_atoms(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, ATOM_MODE, TAUT_MODE, num_components, bUseMulipliers); if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-21: never output empty */ tot_len = tot_len2; if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, 1 ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } } bFhTag = 0; repeat_INChI_output: /***************************************************** * charge */ nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_q_CHARGE] ); if ( nSegmAction ) { szGetTag( IdentLbl, nTag, bTag1 = IL_CHRG | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_Charge2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /***************************************************** * removed protons */ if ( iCurTautMode == TAUT_YES && !bSecondNonTautPass ) { nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_p_PROTONS] ); if ( nSegmAction ) { szGetTag( IdentLbl, nTag, bTag1 = IL_PROT | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len += sprintf( pStr + tot_len, "%+d", nNumRemovedProtons ); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } } /************************************************** * * non-isotopic stereo */ { int i; i = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ); i = i; } if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) { /* stereo */ szGetTag( IdentLbl, nTag, bTag1 = IL_STER | bFhTag, szTag1, &bAlways ); if ( bXml ) { str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /* sp2 */ /*if ( bStereoSp2[iCurTautMode] )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) { szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_DBND, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_Sp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp2 */ } } /* sp3 */ /*if ( bStereoSp3[iCurTautMode] )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) { bRelRac = bRelativeStereo[iCurTautMode] || bRacemicStereo[iCurTautMode]; szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_SP3S, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_Sp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp3 */ } } /* bStereoAbsInverted[iCurTautMode] */ /* if ( bStereoAbs[iCurTautMode] ) */ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) { szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_INVS, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_StereoAbsInv(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, num_components); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* stereo-abs-inv */ } } /* stereo type */ /*if ( bRacemicStereo[iCurTautMode] || bRelativeStereo[iCurTautMode] || bStereoAbs[iCurTautMode] )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) { const char *p_stereo = bRelativeStereo[iCurTautMode]? x_rel : bRacemicStereo[iCurTautMode] ? x_rac : x_abs; szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_TYPS, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ } if ( bXml ) { /* close stereo */ ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ } } /**************************************************** * * Isotopic canonical results * ****************************************************/ nCurINChISegment ++; /* switch from M to MI or from F to FI */ /*if ( bIsotopic || !bSecondNonTautPass && bHasIsoH )*/ if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) { /* isotopic #1: composition -- atoms -- do not output in xml if empty */ szGetTag( IdentLbl, nTag, bTag1 = IL_ISOT | bFhTag, szTag1, &bAlways ); if ( bXml ) { str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /* isotopic atoms without mobile H. * Fixed 2004-06-15: always output if not bXml. Note: * Previous condition if( bHasIsotopicAtoms[iCurTautMode] || bIsotopic && !bXml) * did not optput /i in case of only mobile isotopic H */ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) { szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_ATMS, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); /*if ( bHasIsotopicAtoms[iCurTautMode] )*/ if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len2 = str_IsoAtoms(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bAbcNumbers, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; } else { tot_len2 = tot_len; } tot_len = tot_len2; if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /* isotopic #1a: composition -- exchangeable isotopic H (mobile H only) */ /*if ( !bSecondNonTautPass && bHasIsoH )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) { szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_XCGA, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len += MakeIsoHString( num_iso_H, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, &bOverflow); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /*************************************************** * * Isotopic stereo * ***************************************************/ /*if ( bIsotopicStereo[iCurTautMode] )*/ if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) { /* stereo */ szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_STER, szTag2, &bAlways ); if ( bXml ) { str_LineStart( szTag2, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /************************ isotopic #2: sp2 ************************/ /*if ( bIsotopicStereoSp2[iCurTautMode] )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) { szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_DBND, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_IsoSp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* iso sp2 */ } } /************************ isotopic #3: sp3 ************************/ /*if ( bIsotopicStereoSp3[iCurTautMode] )*/ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) { bRelRac = bIsotopicRelativeStereo[iCurTautMode] || bIsotopicRacemicStereo[iCurTautMode]; szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_SP3S, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_IsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* iso-sp3 */ } } /* isotopic #4: abs inverted */ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) { szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_INVS, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len = str_IsoStereoAbsInv(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, num_components); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /* isotopic #5: stereo type. Do not output if it has already been output in non-iso */ if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) { const char *p_stereo = bIsotopicRelativeStereo[iCurTautMode]? x_rel : bIsotopicRacemicStereo[iCurTautMode] ? x_rac : x_abs; szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_TYPS, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; } if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ } if ( bXml ) { /************************ close isotopic stereo ************************/ ind -= inc; if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } else { if ( !bXml ) { /* no isotopic stereo */ if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ } } /* close isotopic */ if ( bXml ) { ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "///" ); /* isotopic composition, sp2, sp3 */ if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "//" ); /* inv or racemic stereo */ } } #if ( CANON_FIXH_TRANS == 1 ) if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass && INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_o_TRANSP] )) { /* find and print non-tautomeric components transposition, if non-trivial */ AT_NUMB *nTrans_n, *nTrans_s; if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) { /* a non-trivial transposition does exist; output start tag */ szGetTag( IdentLbl, nTag, bTag1 = IL_TRNS | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); /* print the transposition, cycle after cycle */ tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, &bOverflow, TAUT_MODE, num_components); bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); /* detected transposition */ *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : FLAG_SORT_PRINT_TRANSPOS_REC; } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } } #endif /************************************************************** At this point the INChI part of the output has been done. If this INChI is tautomeric and non-tautomeric results exist then we need to output non-tautomeric data: fixed H, stereo, isotopic isotopic stereo ***************************************************************/ if ( bOutType == OUT_TN && !bSecondNonTautPass && bNonTautIsIdenticalToTaut && bTautomeric && bNonTautomeric ) { /* Fixed-H layer is empty in the Identifier */ *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : FLAG_SORT_PRINT_NO_NFIX_H_REC; *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : FLAG_SORT_PRINT_NO_IFIX_H_REC; } if ( bOutType == OUT_TN && !bNonTautIsIdenticalToTaut /* added 2004-10-04 Fix16 */ #ifdef OLD_ITEM_DISCOVERY && bTautomeric && bNonTautomeric #endif && INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_f_FORMULA] ) /* special case: removed isolated H(+): */ /* || iCurTautMode == TAUT_YES && num_comp[TAUT_YES] < num_comp[TAUT_NON] && 0 < num_comp[TAUT_NON]*/ ) { /* add the second (non-tautomeric) output */ bOutType = OUT_NONTAUT; /* pick up only non-tautomeric representation of tautomeric */ iCurTautMode = TAUT_NON; pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; bSecondNonTautPass = 1; nCurINChISegment = DIFL_F; num_components = num_comp[iCurTautMode]; /* number of components could change due to removal of isolated H(+) from tautomeric */ bFhTag = IL_FIXH; szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); if ( bXml ) { /* open non-tautomeric */ str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /***** constitution non-taut: dot-disconnected Hill formulas: -- only if different */ szGetTag( IdentLbl, nTag, bTag1 = IL_FMLF | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_f_FORMULA] ); if ( INCHI_SEGM_FILL == nSegmAction ) { tot_len2 = str_HillFormula2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, num_components, bUseMulipliers); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } else { tot_len2 = tot_len; } tot_len = tot_len2; if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ); if ( INCHI_SEGM_FILL == nSegmAction ) { szGetTag( IdentLbl, nTag, bTag1 = IL_HFIX | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); /* open H-fixed */ /* output the second non-tautomeric item: fixed H -- do not output in xml if empty */ tot_len2 = str_FixedH_atoms(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); tot_len = tot_len2; if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; } goto repeat_INChI_output; } else { if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass /* && bTautomeric && bNonTautomeric*/ ) { /* the second (non-taut) output has been done; restore variables */ bOutType = OUT_TN; iCurTautMode = TAUT_YES; pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; bSecondNonTautPass = 0; num_components = num_comp[iCurTautMode]; if ( !bNonTautNonIsoIdentifierNotEmpty ) { /* Fixed-H layer is empty in the Identifier */ *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : FLAG_SORT_PRINT_NO_NFIX_H_REC; } if ( !bNonTautIsoIdentifierNotEmpty ) { /* Fixed-H layer is empty in the Identifier */ *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : FLAG_SORT_PRINT_NO_IFIX_H_REC; } if ( bXml ) { /* close non-tautomeric */ ind -= inc; szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } bFhTag = 0; } } /************************************************ * output INChI of the reconnected structure * ************************************************/ bEmbeddedOutputCalled = 0; if ( bDisconnectedCoord && INCHI_BAS == iINChI && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] ) { int nRet; bEmbeddedOutputCalled = 1; if ( !bXml ) { /* output blank line before /R: in case of bPlainTextCommnts=1 */ inchi_ios_print( output_file, "%s", pLF ); } /* end of disconnected INChI output */ nRet = OutputINChI1( pStr, nStrLen, pINChISortTautAndNonTaut2, INCHI_REC, NULL, 0 /*bDisconnectedCoord*/, bOutputType, bINChIOutputOptions | INCHI_OUT_NO_AUX_INFO, bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, num_components2, num_non_taut2, num_taut2, output_file, log_file, num_input_struct, szSdfLabel, szSdfValue, lSdfId, pSortPrintINChIFlags, save_opt_bits); if ( !nRet ) goto exit_function; /* error */ } if ( bXml ) { /* close INChI identifier (basic) */ ind -= inc; if ( str_LineEnd( x_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { /* save InChI creation options if requested ...*/ if ( !bEmbeddedOutputCalled) { if ( bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { /* ... and not std-InChI output */ if ( 0 == (bINChIOutputOptions & INCHI_OUT_STDINCHI) ) { char let1, let2; GetSaveOptLetters(save_opt_bits, &let1, &let2); inchi_ios_print( output_file, "\\%c%c", let1, let2 ); } } } if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) { /* plain text comment earlier ended with LF */ inchi_ios_print( output_file, "%s%s", (!num_components2[0] && !num_components2[1])? "//":"", /* empty InChI=// */ (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO)? "\n" : pTAB ); /* end of INChI= output */ } } output_aux_info: bFhTag = 0; if( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) { /* output aux info */ /************************************************************* * * Aux info non-isotopic * *************************************************************/ num_components = num_comp[iCurTautMode]; if ( bXml ) { /* aux. info header */ /* empty line if INChI output has been printed */ if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) { inchi_ios_print( output_file, "\n" ); } /* basic.aux-info title, version */ tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", SP(ind), x_aux_basic, x_ver, x_curr_ver ); if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) { tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); } if ( bAbcNumbers ) { /* type */ const char *pNumber = x_type_short; tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_type, pNumber); } sprintf(pStr+tot_len,">"); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) { /* comment */ tot_len = sprintf( pStr, "%s<%s>", SP(ind), x_aux_comm ); inchi_ios_print( output_file, "%s\n", pStr ); } } else { if ( INCHI_BAS == iINChI ) { tot_len = sprintf( pStr, "AuxInfo=" ); /* in wINChI window, separate INChI: from AuxInfo: with blank line */ inchi_ios_print( output_file, "%s%s%s", /* blank line before AuxInfo in winchi window unless it is an annotation */ (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"", pStr, pLF); szGetTag( AuxLbl, nTag, bTag1 = AL_VERS, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); /* avoid leading slash in plain output */ if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( INCHI_REC == iINChI ) { szGetTag( AuxLbl, nTag, bTag1 = AL_REC_, szTag1, &bAlways ); inchi_ios_print( output_file, "%s%s", szTag1, pLF ); } } } /* normalization type */ if ( num_components2[0] || num_components2[1] ) { szGetTag( AuxLbl, nTag, bTag1 = AL_NORM, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len += sprintf( pStr + tot_len, "%d", (bTautomeric && bTautomericOutputAllowed)? bTautomeric : 0); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } repeat_INChI_Aux_output: /************************************************************** * Original atom numbers in order of canonical numbers **************************************************************/ if ( num_components2[0] || num_components2[1] ) { szGetTag( AuxLbl, nTag, bTag1 = (bSecondNonTautPass? AL_FIXN : AL_ANBR) | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); /* original numbering output */ tot_len = str_AuxNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /********************************************** * Symmetry numbers (constit. equivalence) **********************************************/ if ( bAtomEqu[iCurTautMode] ) { /* aux equ atoms */ /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ /* 2. Compare to the previous component if (1) failed to find equivalence */ szGetTag( AuxLbl, nTag, bTag1 = AL_AEQU | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len = str_AuxEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /***************************************************** * Tautomeric groups equivalence *****************************************************/ if ( bTautomericOutputAllowed && bTautomeric && bTautEqu[iCurTautMode] && !bSecondNonTautPass ) { /***************************************************** * Tautomeric groups constitutional equivalence */ /* aux tgroup equ */ szGetTag( AuxLbl, nTag, bTag1 = AL_GEQU | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len = str_AuxTgroupEqu(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bUseMulipliers); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { if ( !bXml && bTautomericOutputAllowed && bTautomeric ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /**************************************************** * Inverted stereo -- sp3 only + canonical numbering ****************************************************/ if ( bInvStereo[iCurTautMode] ) { szGetTag( AuxLbl, nTag, bTag1 = AL_STER | bFhTag, szTag1, &bAlways ); if ( bXml ) { /*************************** inv stereo start tag ****************************/ str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /**************************** inverted sp3 start tag *****************************/ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3I, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len = str_AuxInvSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); /************************************* inverted sp3 canonical numbering **************************************/ if ( bInvStereoOrigNumb[iCurTautMode] ) { szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3N, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len = str_AuxInvSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions); if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } if ( bXml ) { /* close sp3 inv */ ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "//" ); } /* Inverted stereo -- sp3 only + canonical numbering */ } /* omitted undefined/unknown non-isotopic stereo */ if ( bXml ) { if ( bIgn_UU_Sp2[iCurTautMode] || bIgn_UU_Sp3[iCurTautMode] ) { /* */ szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, (bIgn_UU_Sp2[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, (bIgn_UU_Sp3[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); inchi_ios_print( output_file, "%s\n", pStr ); } } /*************************************************************** * * Additional information: charges, radicals, * special valences, coordinates * ***************************************************************/ /************************************************************** * * Aux info isotopic * **************************************************************/ repeat_INChI_Aux_Iso_output: /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ i = bSecondNonTautPass && (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : FLAG_SORT_PRINT_NO_IFIX_H_REC )); if ( bIsotopic && !i && (bIsotopicOrigNumb[iCurTautMode] || bIsotopicAtomEqu[iCurTautMode] || bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] || bInvIsotopicStereo[iCurTautMode] || bXml && ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) ) ) { /*************************************/ /* isotopic aux info header */ /*************************************/ szGetTag( AuxLbl, nTag, bTag1 = AL_ISOT | bFhTag, szTag1, &bAlways ); if ( bXml ) { str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } else { pStr[tot_len = 0] = '\0'; } /***************************************************************** * Original atom numbers in order of isotopic canonical numbers *****************************************************************/ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ISON, szTag2, &bAlways ); if ( bIsotopicOrigNumb[iCurTautMode] ) { tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len = str_AuxIsoNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions); if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { /*if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" );*/ inchi_ios_print( output_file, "%s%s", szTag2, pLF ); /* mark isotopic output */ } } /*************************/ /* Isotopic symmetry */ /*************************/ if ( bIsotopicAtomEqu[iCurTautMode] ) { /* atoms */ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_AEQU, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len = str_AuxIsoEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /********************************/ /* Tautomeric groups, isotopic */ /********************************/ if ( bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] ) { /********************************************/ /* Isotopic tautomeric groups equivalence */ /********************************************/ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_GEQU, szTag2, &bAlways ); tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); tot_len = str_AuxIsoTgroupEqu(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bOmitRepetitions, bUseMulipliers); if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml && bTautomericOutputAllowed && bTautomeric ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } /************************************* * Isotopic inverted stereo *************************************/ if ( bInvIsotopicStereo[iCurTautMode] ) { szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_STER, szTag2, &bAlways ); if ( bXml ) { /************************************ inv isotopic stereo start tag *************************************/ str_LineStart( szTag2, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /************************************* inverted isotopic sp3 start tag **************************************/ szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3I, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); tot_len = str_AuxInvIsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); /********************************************* inverted isotopic sp3 canonical numbering **********************************************/ if ( bInvIsotopicStereoOrigNumb[iCurTautMode] ) { szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3N, szTag3, &bAlways ); tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); tot_len = str_AuxInvIsoSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bSecondNonTautPass, bOmitRepetitions); if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } if ( bXml ) { /* close sp3 inv */ ind -= inc; if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } else { if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "//" ); } } /* totally omitted undefined/unknown isotopic stereo */ if ( bXml ) { if ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) { /* */ szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, (bIgn_UU_Sp2_Iso[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, (bIgn_UU_Sp3_Iso[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); inchi_ios_print( output_file, "%s\n", pStr ); } } if ( bXml ) { /***************** close isotopic ***********************/ ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } /* Aux info isotopic */ #if ( CANON_FIXH_TRANS != 1 ) if ( bSecondNonTautPass ) { /* find and print non-tautomeric components transposition, if non-trivial */ AT_NUMB *nTrans_n, *nTrans_s; if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) { /* a non-trivial transposition does exist; output start tag */ tot_len = str_LineStart( tag=x_aux_trans, NULL, 0, pStr, ind ); /* print the transposition, cycle after cycle */ tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, &bOverflow, TAUT_MODE, num_components); if ( str_LineEnd( bXml? tag:p_aux_at_inv_nbr, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); /* detected transposition */ *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : FLAG_SORT_PRINT_TRANSPOS_REC; } else if ( !bXml ) { if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); } } #endif /************************************************************** At this point the INChI_Aux part of the output has been completed. If this INChI is tautomeric and non-tautomeric results exist then we need to output non-tautomeric auxilialy data (same as above excluding tautomeric information) Currently this is enabled for xml output only ***************************************************************/ if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric && /* Check whether the Fixed-H layer is empty */ (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : FLAG_SORT_PRINT_NO_NFIX_H_REC )) && (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : FLAG_SORT_PRINT_NO_IFIX_H_REC )) ) { bNonTautomeric = 0; /* bNonTautIdentifierNotEmpty == 0 => no fixed H info 02-10-2995 */ } if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric ) { /* add the second (non-tautomeric) output */ bOutType = OUT_NONTAUT; iCurTautMode = TAUT_NON; pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; bSecondNonTautPass = 1; num_components = num_comp[iCurTautMode]; bFhTag = AL_FIXH; if ( bXml ) { szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } else { pStr[tot_len=0] = '\0'; } /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ if ( !(*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : FLAG_SORT_PRINT_NO_NFIX_H_REC )) ) { goto repeat_INChI_Aux_output; } else { goto repeat_INChI_Aux_Iso_output; } } else { if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bTautomeric && bNonTautomeric ) { /* the second (non-taut) output has been done; restore variables */ bOutType = OUT_TN; iCurTautMode = TAUT_YES; pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; bSecondNonTautPass = 0; /* set correct num components for the reversibility info 02-10-2005 */ num_components = num_comp[iCurTautMode]; if ( bXml ) { /* close non-tautomeric */ szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } bFhTag = 0; } } /***************************************/ /* charges, radicals, unusual valences */ /***************************************/ if ( !bSecondNonTautPass && bChargesRadVal[iCurTautMode] ) { /* aux equ atoms */ /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ /* 2. Compare to the previous component if (1) failed to find equivalence */ szGetTag( AuxLbl, nTag, bTag1 = AL_CRV_ | bFhTag, szTag1, &bAlways ); tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); tot_len = str_AuxChargeRadVal(pINChISort, pStr, nStrLen, tot_len, &bOverflow, bOutType, TAUT_MODE, num_components, bUseMulipliers); if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s%s", pStr, pLF ); } /* output the original input structure -- quick fix */ if ( !bSecondNonTautPass && pOrigStruct && pOrigStruct->num_atoms && pOrigStruct->szAtoms && pOrigStruct->szBonds && pOrigStruct->szCoord ) { int length, cur_pos, line_len, last_pos, nMaxLineLen; char *p; nMaxLineLen = inchi_min( 80, nStrLen ); /* restrict line length to 80 characters */ /********************** reversibility info **********************/ szGetTag( AuxLbl, nTag, bTag1 = AL_REVR | bFhTag, szTag1, &bAlways ); if ( bXml ) { str_LineStart( szTag1, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; } /* === atoms === */ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ATMR, szTag2, &bAlways ); if ( bXml ) { str_LineStart( szTag2, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; /* first line indent */ strcpy( pStr, SP(ind)); tot_len = ind; } else { pStr[tot_len = 0] = '\0'; inchi_ios_print( output_file, "%s%s", szTag2, pStr ); } p = pOrigStruct->szAtoms; length = strlen( p ); line_len = nMaxLineLen - tot_len; for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) { if ( length - cur_pos >= line_len ) { last_pos = cur_pos + line_len; /* search backward for the nearest first atom letter (always uppercase) */ while ( cur_pos < last_pos && !isupper( UCINT p[last_pos] ) ) { last_pos --; } } else { last_pos = length; } if ( last_pos > cur_pos ) { memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); pStr[tot_len + last_pos - cur_pos] = '\0'; inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); } else { break; } } if ( bXml ) { ind -= inc; pStr[0] = '\0'; if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { if ( pLF[0] ) { inchi_ios_print( output_file, "%s", pLF ); } } /* === bonds === */ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_BNDR, szTag2, &bAlways ); if ( bXml ) { str_LineStart( szTag2, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; /* first line indent */ strcpy( pStr, SP(ind)); tot_len = ind; } else { pStr[tot_len = 0] = '\0'; inchi_ios_print( output_file, "%s%s", szTag2, pStr ); } p = pOrigStruct->szBonds; length = strlen( p ); line_len = nMaxLineLen - tot_len; for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) { if ( length - cur_pos >= line_len ) { last_pos = cur_pos + line_len - 1; /* search backward for the nearest first bond delimiter ";" */ while ( cur_pos < last_pos && p[last_pos] != ';' ) { last_pos --; } if ( cur_pos < last_pos ) { last_pos ++; /* include ';' at the end of the line */ } } else { last_pos = length; } if ( last_pos > cur_pos ) { memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); pStr[tot_len + last_pos - cur_pos] = '\0'; inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); } else { break; } } if ( bXml ) { ind -= inc; pStr[0] = '\0'; if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { if ( pLF[0] ) { inchi_ios_print( output_file, "%s", pLF ); } } /* === coordinates === */ szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_XYZR, szTag2, &bAlways ); if ( bXml ) { str_LineStart( szTag2, NULL, 0, pStr, ind ); inchi_ios_print( output_file, "%s\n", pStr ); ind += inc; /* first line indent */ strcpy( pStr, SP(ind)); tot_len = ind; } else { pStr[tot_len = 0] = '\0'; inchi_ios_print( output_file, "%s%s", szTag2, pStr ); } p = pOrigStruct->szCoord; length = strlen( p ); line_len = nMaxLineLen - tot_len; for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) { if ( length - cur_pos >= line_len ) { last_pos = cur_pos + line_len - 1; /* search backward for the nearest first coord. delimiter ";" */ while ( cur_pos < last_pos && p[last_pos] != ';' ) { last_pos --; } if ( cur_pos < last_pos ) { last_pos ++; /* include ';' at the end of the line */ } } else { last_pos = length; } if ( last_pos > cur_pos ) { memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); pStr[tot_len + last_pos - cur_pos] = '\0'; inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); } else { break; } } if ( bXml ) { ind -= inc; pStr[0] = '\0'; if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { if ( pLF[0] ) { inchi_ios_print( output_file, "%s", pLF ); } } if ( bXml ) { /*************************** close reversibility info ***************************/ ind -= inc; if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } } /************************************************ * output INChI_Aux of the reconnected structure * ************************************************/ bEmbeddedOutputCalled = 0; if ( bDisconnectedCoord && INCHI_BAS == iINChI && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] && !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) { int nRet; bEmbeddedOutputCalled = 1; if ( !bXml ) { inchi_ios_print( output_file, "%s", pLF ); } nRet = OutputINChI1(pStr, nStrLen, pINChISortTautAndNonTaut2, INCHI_REC, NULL, 0 /*bDisconnectedCoord*/, bOutputType, INCHI_OUT_ONLY_AUX_INFO | bINChIOutputOptions, bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, num_components2, num_non_taut2, num_taut2, output_file, log_file, num_input_struct, szSdfLabel, szSdfValue, lSdfId, pSortPrintINChIFlags, save_opt_bits); if ( !nRet ) goto exit_function; /* error */ } /* close INChI_Aux */ if ( bXml ) { ind -= inc; if ( str_LineEnd( x_aux_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) goto exit_function; inchi_ios_print( output_file, "%s", pStr ); } else { if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) { inchi_ios_print( output_file, "%s\n", (!num_components2[0] && !num_components2[1])? "//":"" ); /* plain text comment earlier ended with LF */ } } /* in wINChI window, separate AuxInfo: from InChIKey: with blank line */ inchi_ios_print( output_file, "%s", (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":""); } /* end of output aux info */ ret = 1; exit_function: if ( bOverflow ) { strcpy( pStr, "Output buffer overflow"); if ( bXml ) { OutputINChIXmlError( output_file, pStr, nStrLen, ind /*, 0*/ /* err number */, pStr, _IS_FATAL ); } else { inchi_ios_print( output_file, "\nFATAL ERROR: %s\n", pStr ); } } /* inchi_free( pStr ); */ return ret; } /* OutputINChI1 */ /***************************************************************/ int str_LineStart( const char *tag, char *tag2, int val2, char *pStr, int ind ) { int tot_len = 0; if ( ind >= 0 ) { if ( ind > 0 ) { /* xml: indent */ memset( pStr + tot_len, ' ', ind ); tot_len += ind; } /* xml: tag */ strcpy( pStr + tot_len, x_line_opening ); strcat( pStr + tot_len, tag ); if ( tag2 ) { tot_len += strlen(pStr + tot_len); tot_len += sprintf( pStr + tot_len, " %s=\"%d\"%s", tag2, val2, x_close_line ); } else { strcat( pStr + tot_len, x_close_line ); tot_len += strlen(pStr + tot_len); } } else { pStr[tot_len] = '\0'; } return tot_len; } /***************************************************************/ int str_LineEnd( const char *tag, int tot_len, int nStrLen, int *bOverflow, char *pStr, int ind, int bPlainTextTags ) { static int add_tag_len = sizeof(x_line_closing)-1 + sizeof(x_close_line)-1; int tag_len; /* check buffer overflow */ if ( *bOverflow ) return 1; if ( ind >= 0 ) { /* xml */ tag_len = ind + add_tag_len + strlen(tag); if ( tot_len + tag_len < nStrLen - 2 ) { /* output " \n" */ tot_len += sprintf( pStr + tot_len, "%s%s%s%s\n", SP(ind), x_line_closing, tag, x_close_line ); } else { *bOverflow += 1; return 1; } } else { /* plain */ pStr[tot_len] = '\0'; /* add zero termination 2004-04-26 */ /* insert plain text tag if: (a) pStr has non-zero length, or (b) ind < -1 */ if ( pStr[0] || ind < -1 ) { tag_len = bPlainTextTags? strlen( tag ):0; if ( tot_len + tag_len < nStrLen - 2 ) { if ( tag_len > 0 ) { /* insert plain text tag */ memmove( pStr+tag_len, pStr, tot_len + 1 ); memcpy( pStr, tag, tag_len ); } } else { *bOverflow += 1; return 1; } }/* else if ( bPlainTextTags == 1 ) { strcpy( pStr, "/" ); }*/ } return 0; } /**********************************************************************************************/ int CleanOrigCoord( MOL_COORD szCoord, int delim ) { #define MIN_BOND_LENGTH (1.0e-6) char szVal[LEN_COORD+1]; MOL_COORD szBuf; char *q; int len, last, fst, dec_pnt, num_zer=0, len_buf = 0, e; int k, i; double coord; for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { memcpy( szVal, szCoord+k, LEN_COORD ); szVal[LEN_COORD] = '\0'; LtrimRtrim(szVal, &len); coord = strtod(szVal, &q); if ( MIN_BOND_LENGTH > fabs(coord) ) { strcpy( szVal, "0" ); len = 1; num_zer ++; } else { len = q - szVal; /* last = (last mantissa digit position + 1) */ if ( (q = strchr(szVal, 'e')) || (q = strchr(szVal, 'E')) || (q = strchr(szVal, 'd')) || (q = strchr(szVal, 'D')) ) { /* floating point */ last = q - szVal; /* remove (+) and leading zeroes from the exponent */ e = (int)strtol( szVal+last+1, &q, 10 ); /* exponent */ if ( e ) { /* new exp; update the length */ len = last+1+sprintf( szVal+last+1, "%d", e ); /* print exp without leading zeroes and '+' */ } else { /* exponent is zero */ len = last; } } else { last = len; } /* fst = (first mantissa digit); fst=1 if the sign is present, otherwise 0 */ fst = (szVal[0]!='.' && !isdigit( UCINT szVal[0] )); /* dec_pnt = (decimal point position) or last */ if ( q = strchr(szVal, '.') ) { dec_pnt = q - szVal; } else { dec_pnt = last; } last -= 1; /* last mantissa digit position */ /* remove trailing zeroes in the range dec_pnt+1..last-1 */ for ( i = last; dec_pnt < i && '0' == szVal[i]; i -- ) ; if ( i == dec_pnt ) { i --; /* remove decimal point, too */ } if ( i < last ) { memmove( szVal+i+1, szVal+last+1, len-last ); len -= last-i; } /* remove leading zeroes */ for ( i = fst; i < len && '0' == szVal[i]; i ++ ) ; if ( i > fst ) { memmove( szVal + fst, szVal+i, len-fst ); len -= i-fst; } } if ( len_buf ) szBuf[len_buf++] = delim; memcpy( szBuf + len_buf, szVal, len ); /* does not copy zero termination*/ len_buf += len; } /* zero termination */ if ( len_buf < (int)sizeof(MOL_COORD) ) { memset( szBuf+len_buf, 0, sizeof(MOL_COORD) - len_buf); } memcpy( szCoord, szBuf, sizeof(MOL_COORD) ); return num_zer; #undef MIN_BOND_LENGTH } /******************************************************************************************/ int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len ) { int j, num_zer, len, cur_len; char *p; MOL_COORD szCurCoord; cur_len = 0; for ( j = *i; j < num_inp_atoms; ) { memcpy( szCurCoord, szMolCoord[j], sizeof(szCurCoord)); num_zer = CleanOrigCoord( szCurCoord, ',' ); if ( NUM_COORD == num_zer ) { len = 0; } else { if ( p = (char *)memchr( szCurCoord, '\0', sizeof(szCurCoord)) ) { len = p - szCurCoord; } else { len = sizeof(szCurCoord); } } if ( len + cur_len + 1 < buf_len ) { if ( len ) { memcpy( szBuf + cur_len, szCurCoord, len * sizeof(szBuf[0]) ); } szBuf[cur_len += len] = ';'; cur_len ++; j ++; } else { break; } } szBuf[cur_len] = '\0'; *i = j; /* next item */ return cur_len; } /******************************************************************************************/ /* number of atoms [c|n] chiral/nonchiral Element #valence +/-[charge>1] .#rad (#rad=1, 2, 3: singlet, doulet, triplet) [.]i#iso_mass [.]{o|e|u|?} atom parity = {1:2:3:4} [.]h[#of 1H>1] [.]d[#of 2H>1] [.]t[#of 3H>1] Note: . occurs only once and only if radical or 1-character element */ int WriteOrigAtoms( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) { int j, k, n, len, len0, cur_len, val, bonds_val, mw, parity, num_trans, is_ok, b_self; static char szIsoH[] = "hdt"; char szCurAtom[32]; AT_NUMB nNeighOrder[MAXVAL], neigh; cur_len = 0; if ( 0 == *i ) { cur_len = sprintf( szBuf, "%d%s", num_inp_atoms, (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? "c" : (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL)? "n" : "" ); } for ( j = *i; j < num_inp_atoms; ) { /* tetrahedral parity treatment */ parity = 0; num_trans = 0; if ( at[j].p_parity ) { /* verify neighbors */ is_ok = 1; b_self = 0; for ( n = 0, k = 0; n < MAX_NUM_STEREO_ATOM_NEIGH; n ++ ) { neigh = at[j].p_orig_at_num[n]-1; if ( is_in_the_list( at[j].neighbor, neigh, at[j].valence ) && at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { /* real neighbor */ nNeighOrder[k ++] = at[j].p_orig_at_num[n]; } else if ( (int)neigh == j && at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { /* central atom is a neighbor */ num_trans = n; /* move this neighbor to 0 position permutation parity */ b_self ++; } else { is_ok = 0; break; } } if ( is_ok && b_self <= 1 && b_self + k == MAX_NUM_STEREO_ATOM_NEIGH ) { num_trans += insertions_sort( nNeighOrder, k, sizeof(nNeighOrder[0]), comp_AT_RANK ); if ( ATOM_PARITY_WELL_DEF( at[j].p_parity ) ) { parity = 2 - (num_trans + at[j].p_parity) % 2; } else if ( ATOM_PARITY_ILL_DEF( at[j].p_parity ) ) { parity = at[j].p_parity; } else { ; /* invalid atom parity */ } } else { ;/* add error message here */ } } len = len0 = strlen( at[j].elname ); memcpy( szCurAtom, at[j].elname, len ); bonds_val = nBondsValenceInpAt( at+j, NULL, NULL ); if ( (val=needed_unusual_el_valence( at[j].el_number, at[j].charge, at[j].radical, at[j].chem_bonds_valence, bonds_val, at[j].num_H, at[j].valence )) || at[j].charge || at[j].radical || at[j].iso_atw_diff || NUM_ISO_H(at,j) || parity ) { /* valence */ if ( val ) { len += sprintf( szCurAtom + len, "%d", val > 0? val : 0 ); } /* charge */ if ( val = at[j].charge ) { szCurAtom[len++] = val>0? '+' : '-'; if ( (val = abs(val)) > 1 ) { len += sprintf( szCurAtom + len, "%d", val ); } } /* radical */ if ( val = at[j].radical ) { len += sprintf(szCurAtom + len, ".%d", val); } /* isotopic shift */ if ( val = at[j].iso_atw_diff ) { mw = get_atw_from_elnum( at[j].el_number ); if ( val == 1 ) val = mw; else if ( val > 0 ) val = mw + val -1; else val = mw + val; len += sprintf( szCurAtom + len, "%si%d", len == len0? ".":"", val ); } /* parity */ if ( parity ) { len += sprintf( szCurAtom + len, "%s%s", len == len0? ".":"", parity == AB_PARITY_ODD? "o" : parity == AB_PARITY_EVEN? "e" : parity == AB_PARITY_UNKN? "u" : parity == AB_PARITY_UNDF? "?" : "" ); } /* implicit isotopic H */ if ( NUM_ISO_H(at,j) ) { for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { if ( val = at[j].num_iso_H[k] ) { len += sprintf( szCurAtom + len, "%s%c", len == len0? ".":"", szIsoH[k] ); if ( val > 1 ) { len += sprintf(szCurAtom + len, "%d", val); } } } } } if ( len + cur_len < buf_len ) { memcpy( szBuf + cur_len, szCurAtom, len ); cur_len += len; j ++; } else { break; } szBuf[cur_len] = '\0'; *i = j; } return cur_len; } /******************************************************************************************/ /* bpA;bpAbpA... b = bond type: ============= w = undefined stereo, double s = single d = double t = triple a = aromatic p = up from the current atom to the neighbor P = uP from the neighbor to the current atom v = undefined stereo Either, single from the current atom to the neighbor V = undefined stereo Either, single from the neighbor to the current atom n = down from the current atom to the neighbor N = dowN from the neighbor to the current atom p = bond parity: ================ - = odd + = even u = unknown ? = undefined = no parity (empty) A = neighbor orig. atom number =============== neighbor orig. atom number < number of the current atom Number of the current atom: 2 until first ";", 3 until 2nd ";", etc. */ /************************************************************************************/ /* output bonds in ascending order of the neighboring atom original numbers */ int WriteOrigBonds( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) { int j, k, k2, kk, len, cur_len, j2=0, bond_stereo, bond_char, bond_parity, bond_parityNM, num_trans; char szCurBonds[7*MAXVAL+2]; /* num_neigh*(1 byte bond type + 2 bytes for bond parity up to 4 digits per neighbor number) + at the end one ';' */ AT_RANK nNeighOrder[MAXVAL]; int chain_len, pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; int chain_len2, pnxt_atom2, pinxt2cur2, pinxt_sb_parity_ord2, m1, m2; int pcur_atom, picur2nxt, picur_sb_parity_ord; cur_len = 0; for ( j = *i; j < num_inp_atoms; ) { len = 0; if ( at[j].valence > 1 ) { for ( k = 0; k < at[j].valence; k ++ ) { nNeighOrder[k] = k; } pn_RankForSort = at[j].neighbor; num_trans = insertions_sort( nNeighOrder, at[j].valence, sizeof(nNeighOrder[0]), CompRank ); } else { num_trans = 0; nNeighOrder[0] = 0; } for ( kk = 0; kk < at[j].valence; kk ++ ) { k = nNeighOrder[kk]; j2 = at[j].neighbor[k]; bond_parity = 0; bond_parityNM = 0; if ( j2 < j ) { bond_stereo = at[j].bond_stereo[k]; switch( at[j].bond_type[k] ) { case BOND_TYPE_SINGLE: switch( bond_stereo ) { case STEREO_SNGL_UP: bond_char = 'p'; break; case -STEREO_SNGL_UP: bond_char = 'P'; break; case STEREO_SNGL_DOWN: bond_char = 'n'; break; case -STEREO_SNGL_DOWN: bond_char = 'N'; break; #if ( FIX_EITHER_STEREO_IN_AUX_INFO == 1 ) case STEREO_SNGL_EITHER: bond_char = 'v'; break; case -STEREO_SNGL_EITHER: bond_char = 'V'; break; #else case STEREO_SNGL_EITHER: case -STEREO_SNGL_EITHER: bond_char = 'v'; break; #endif default: bond_char = 's'; break; } break; case BOND_TYPE_DOUBLE: switch( bond_stereo ) { case STEREO_DBLE_EITHER: case -STEREO_DBLE_EITHER: bond_char = 'w'; break; default: bond_char = 'd'; break; } break; case BOND_TYPE_TRIPLE: bond_char = 't'; break; case BOND_TYPE_ALTERN: bond_char = 'a'; break; default: bond_char = 's'; break; } /* check for allene/cumulene */ k2 = is_in_the_list( at[j2].neighbor, (AT_NUMB)j, at[j2].valence ) - at[j2].neighbor; chain_len = chain_len2 = 0; if ( at[j].sb_parity[0] ) { for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[j].sb_parity[m1]; m1 ++ ) { if ( k == at[j].sb_ord[m1] ) { chain_len = get_opposite_sb_atom( at, j, k, &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); break; } } } if ( at[j2].sb_parity[0] ) { for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[j2].sb_parity[m2]; m2 ++ ) { if ( k2 == at[j2].sb_ord[m2] ) { chain_len2 = get_opposite_sb_atom( at, j2, k2, &pnxt_atom2, &pinxt2cur2, &pinxt_sb_parity_ord2 ); break; } } } if ( chain_len == 1 && chain_len2 == 1 || /* regular stereobond */ chain_len > 1 && j > pnxt_atom ) { /* j is a cumulene endpoint */ int m; pcur_atom = j; /* pcur_atom > pnxt_atom */ picur2nxt = k; picur_sb_parity_ord = -1; for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { if ( at[pcur_atom].sb_ord[m] == k ) { picur_sb_parity_ord = m; break; } } chain_len2 = 0; } else if ( chain_len2 > 1 && j2 > pnxt_atom2 ) { /* j2 is a cumulene endpoint */ int m; pcur_atom = j2; picur2nxt = k2; pnxt_atom = pnxt_atom2; pinxt2cur = pinxt2cur2; pinxt_sb_parity_ord = pinxt_sb_parity_ord2; picur_sb_parity_ord = -1; for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { if ( at[pcur_atom].sb_ord[m] == k2 ) picur_sb_parity_ord = m; } chain_len = chain_len2; chain_len2 = 0; } else { chain_len = chain_len2 = 0; } /*len += sprintf( szCurBonds + len, "%c%d", bond_char, val+1);*/ if ( chain_len ) { /* both atoms belong to a stereo bond */ int kc; int p1, p2, p1NM, p2NM, neigh, neigh1, neigh2, bHasMetal, bWellDef; int bNeighSwitched1, bNeighSwitched2; p1 = SB_PARITY_1(at[pcur_atom].sb_parity[picur_sb_parity_ord]); p1NM = SB_PARITY_2(at[pcur_atom].sb_parity[picur_sb_parity_ord]); p2 = SB_PARITY_1(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); p2NM = SB_PARITY_2(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); bWellDef = ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2); bHasMetal = ATOM_PARITY_WELL_DEF(p1NM) && ATOM_PARITY_WELL_DEF(p2NM); bNeighSwitched1 = bNeighSwitched2 = 0; if ( bWellDef || bHasMetal ) { neigh1 = num_inp_atoms; for ( kc = 0; kc < at[pcur_atom].valence; kc ++ ) { if ( kc == picur2nxt ) continue; neigh = at[pcur_atom].neighbor[kc]; if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) continue; if ( neigh < neigh1 ) neigh1 = neigh; } if ( neigh1 < num_inp_atoms ) { bNeighSwitched1 = (neigh1 != at[pcur_atom].neighbor[(int)at[pcur_atom].sn_ord[picur_sb_parity_ord]]); } else { AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); /* sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; */ } neigh2 = num_inp_atoms; for ( kc = 0; kc < at[pnxt_atom].valence; kc ++ ) { if ( kc == pinxt2cur ) continue; neigh = at[pnxt_atom].neighbor[kc]; if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) continue; if ( neigh < neigh2 ) neigh2 = neigh; } if ( neigh2 < num_inp_atoms ) { bNeighSwitched2 = (neigh2 != at[pnxt_atom].neighbor[(int)at[pnxt_atom].sn_ord[pinxt_sb_parity_ord]]); } else { AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); /* sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; */ } if ( neigh1 < num_inp_atoms && neigh2 < num_inp_atoms ) { if ( ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2) ) { bond_parity = 2 - (p1 + p2 + bNeighSwitched1 + bNeighSwitched2) % 2; } else { bond_parity = inchi_min( p1, p2 ); } if ( bHasMetal ) { bond_parityNM = 2 - (p1NM + p2NM + bNeighSwitched1 + bNeighSwitched2) % 2; } else if ( p1NM && p2NM ) { bond_parityNM = inchi_min( p1NM, p2NM ); } } } else { if ( p1 && p2 ) { bond_parity = inchi_min( p1, p2 ); } if ( p1NM && p2NM ) { bond_parityNM = inchi_min( p1NM, p2NM ); } if ( bond_parityNM && !bond_parity ) { bond_parity = AB_PARITY_UNDF; } } } len += sprintf( szCurBonds + len, "%c%s%s%d", bond_char, (bond_parity == AB_PARITY_ODD)? "-" : (bond_parity == AB_PARITY_EVEN)? "+" : (bond_parity == AB_PARITY_UNKN)? "u" : (bond_parity == AB_PARITY_UNDF)? "?" : "", (bond_parityNM == AB_PARITY_ODD)? "-" : (bond_parityNM == AB_PARITY_EVEN)? "+" : (bond_parityNM == AB_PARITY_UNKN)? "u" : (bond_parityNM == AB_PARITY_UNDF)? "?" : "", j2+1); } } if ( len + cur_len + 2 < buf_len ) { memcpy( szBuf + cur_len, szCurBonds, len ); cur_len += len; szBuf[ cur_len ++ ] = ';'; j ++; } else { break; } } szBuf[cur_len] = '\0'; *i = num_inp_atoms>0? j : 0; return cur_len; } #define ORIG_STR_BUFLEN (7*MAXVAL+2) /* > 7*MAXVAL+2 = 142 */ /******************************************************************************************/ int FillOutOrigStruct( ORIG_ATOM_DATA *orig_inp_data, ORIG_STRUCT *pOrigStruct, STRUCT_DATA *sd ) { char szBuf[ORIG_STR_BUFLEN]; int i, len, len_coord, len_atoms, len_bonds; /* coordinates */ len_coord = i = 0; if (orig_inp_data->szCoord) { while ( len = WriteOrigCoord( orig_inp_data->num_inp_atoms, orig_inp_data->szCoord, &i, szBuf, sizeof(szBuf) )) { len_coord += len; } pOrigStruct->szCoord = (char*) inchi_malloc( (len_coord + 1)*sizeof(pOrigStruct->szCoord[0]) ); i = 0; if ( pOrigStruct->szCoord && len_coord == WriteOrigCoord( orig_inp_data->num_inp_atoms, orig_inp_data->szCoord, &i, pOrigStruct->szCoord, len_coord+1 ) && i == orig_inp_data->num_inp_atoms ) { /* success */ if ( orig_inp_data->szCoord ) { inchi_free( orig_inp_data->szCoord ); orig_inp_data->szCoord = NULL; } } else { return -1; } } /* atoms */ len_atoms = i = 0; while ( len = WriteOrigAtoms( orig_inp_data->num_inp_atoms, orig_inp_data->at, &i, szBuf, sizeof(szBuf), sd)) { len_atoms += len; if ( !orig_inp_data->num_inp_atoms ) break; } pOrigStruct->szAtoms = (char*) inchi_malloc( (len_atoms + 1)*sizeof(pOrigStruct->szAtoms[0]) ); i = 0; if ( pOrigStruct->szAtoms && len_atoms == WriteOrigAtoms( orig_inp_data->num_inp_atoms, orig_inp_data->at, &i, pOrigStruct->szAtoms, len_atoms+1, sd ) && i == orig_inp_data->num_inp_atoms ) { ; /* success */ } else { return -1; } /* bonds */ len_bonds = 0; i = 1; while ( len = WriteOrigBonds( orig_inp_data->num_inp_atoms, orig_inp_data->at, &i, szBuf, sizeof(szBuf), NULL)) { len_bonds += len; if ( !orig_inp_data->num_inp_atoms ) break; } pOrigStruct->szBonds = (char*) inchi_malloc( (len_bonds + 2)*sizeof(pOrigStruct->szBonds[0]) ); i = 1; if ( pOrigStruct->szBonds && len_bonds == WriteOrigBonds( orig_inp_data->num_inp_atoms, orig_inp_data->at, &i, pOrigStruct->szBonds, len_bonds+2, sd ) && i == orig_inp_data->num_inp_atoms ) { ; /* success */ } else { return -1; } pOrigStruct->num_atoms = orig_inp_data->num_inp_atoms; return 0; } /*****************************************************************/ void FreeOrigStruct( ORIG_STRUCT *pOrigStruct) { if ( pOrigStruct ) { if ( pOrigStruct->szAtoms ) inchi_free( pOrigStruct->szAtoms ); if ( pOrigStruct->szBonds ) inchi_free( pOrigStruct->szBonds ); if ( pOrigStruct->szCoord ) inchi_free( pOrigStruct->szCoord ); memset( pOrigStruct, 0, sizeof(*pOrigStruct) ); } } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get the two letters encoding the saved InChI creation options. The first one encodes RecMet/FixedH/SUU/SLUUD options. Each of options is a binary switch {ON,OFF}, so it totals to 2*2*2*2=16 values which are encoded by capital letters ‘A’ through ‘P’. The second character encodes experimental (InChI 1 extension) options KET and 15T. Each of these options is a binary switch ON/OFF, so there are 2*2=4 combinations, currently encoded by ‘A’ through ‘D’. Note that anything but 'A' here would indicate "extended" InChI 1 Also, there is a reservation for future needs: the 2nd memo char may accommodate two more ON/OFF binary options (at 26-base encoding). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2) { const char a2p[]="ABCDEFGHIJKLMNOP"; /* SaveOptBits layout: {unused|unused|Ket|15T|RecMet|FixedH|SUU|SLUUD} */ *let1 = a2p [ (size_t) ( save_opt_bits & 0x0f ) ]; *let2 = a2p [ (size_t) ( (save_opt_bits & 0x30) >> 4 ) ]; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiprt2.c000066400000000000000000001724541271037650300234560ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" #include "ichimain.h" #include "ichimake.h" /*****************************************************************************************/ int Eql_INChI_Stereo( INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ) { int inv1=0, inv2=0, len; if ( !s1 ) { return 0; } #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) #else bRelRac = 0; #endif if( EQL_SP2 == eql1 ) { if ( (len=s1->nNumberOfStereoBonds) > 0 && s1->b_parity && s1->nBondAtom1 && s1->nBondAtom2 ) { if ( !s2 ) { if ( EQL_EXISTS == eql2 ) { /* find whether double bond stereo exists*/ return 1; } return 0; } if ( EQL_SP2 == eql2 && len == s2->nNumberOfStereoBonds && s2->b_parity && s2->nBondAtom1 && s2->nBondAtom2 && !memcmp( s1->nBondAtom1, s2->nBondAtom1, len * sizeof(s1->nBondAtom1[0])) && !memcmp( s1->nBondAtom2, s2->nBondAtom2, len * sizeof(s1->nBondAtom2[0])) && !memcmp( s1->b_parity, s2->b_parity, len * sizeof(s1->b_parity[0])) ) { return 1; } } return 0; } else if ( (eql1 == EQL_SP3 || (inv1 = (eql1 == EQL_SP3_INV))) && (len=s1->nNumberOfStereoCenters) > (bRelRac? 1 : 0) ) { S_CHAR *t_parity1, *t_parity2; AT_NUMB *nNumber1, *nNumber2; if ( inv1 ) { if ( s1->nCompInv2Abs ) { t_parity1 = s1->t_parityInv; nNumber1 = s1->nNumberInv; } else { return 0; } } else { t_parity1 = s1->t_parity; nNumber1 = s1->nNumber; } if ( t_parity1 && nNumber1 ) { if ( !s2 ) { if ( EQL_EXISTS == eql2 && (!inv1 || s1->nCompInv2Abs) ) { /* the 1st sp3 (inverted if requested) stereo exists*/ return 1; } return 0; /* both sp3 do not exist */ } if( (eql2 == EQL_SP3 || (inv2 = (eql2 == EQL_SP3_INV))) && len == s2->nNumberOfStereoCenters ) { if ( inv2 ) { if ( s2->nCompInv2Abs && s1->nCompInv2Abs ) { t_parity2 = s2->t_parityInv; nNumber2 = s2->nNumberInv; } else { /* if one sp3 is inverted then another should have non-trivial inverted stereo */ return 0; } } else { if ( inv1 && !s2->nCompInv2Abs ) { /* if one sp3 is inverted then another should have non-trivial inverted stereo */ return 0; } t_parity2 = s2->t_parity; nNumber2 = s2->nNumber; } if ( t_parity2 && nNumber2 ) { if ( inv1 ^ inv2 ) { int i, num_inv; for ( i = 0, num_inv = 0; i < len; i ++ ) { if ( nNumber1[i] != nNumber2[i] ) break; if ( ATOM_PARITY_WELL_DEF(t_parity1[i]) && ATOM_PARITY_WELL_DEF(t_parity2[i]) ) { if ( 3 == t_parity1[i] + t_parity2[i] ) { num_inv ++; } else { break; } } else if ( t_parity1[i] != t_parity2[i] ) { break; } } return (len == i && num_inv > 0); } else { return !memcmp( t_parity1, t_parity2, len*sizeof(t_parity1[0])) && !memcmp( nNumber1, nNumber2, len*sizeof(nNumber1[0])); } } } } } return 0; } /**********************************************************************************************/ int Eql_INChI_Isotopic( INChI *i1, INChI *i2 ) { int eq = i1 && i2 && !i1->bDeleted && !i2->bDeleted && ( i1->nNumberOfIsotopicAtoms > 0 || i1->nNumberOfIsotopicTGroups > 0 ) && i1->nNumberOfIsotopicAtoms == i2->nNumberOfIsotopicAtoms && i1->nNumberOfIsotopicTGroups == i2->nNumberOfIsotopicTGroups && ( !i1->nNumberOfIsotopicAtoms || i1->IsotopicAtom && i2->IsotopicAtom && !memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms * sizeof(i1->IsotopicAtom[0]) ) ) && ( !i1->nNumberOfIsotopicTGroups || i1->IsotopicTGroup && i2->IsotopicTGroup && !memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups * sizeof(i1->IsotopicAtom[0]) ) ); return eq; } /**********************************************************************************************/ int Eql_INChI_Aux_Equ( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) { int t1=0, t2=0, len; AT_NUMB *n1=NULL, *n2=NULL; if ( !a1 || !a2 ) { return 0; } t1 = (eql1 & EQL_EQU_TG); t2 = (eql2 & EQL_EQU_TG); if ( t1 && t2 ) { if ( (len = a1->nNumberOfTGroups) > 0 && len == a2->nNumberOfTGroups && !a1->bDeleted && !a2->bDeleted ) { if (eql1 & EQL_EQU_ISO) { if ( a1->bIsIsotopic ) { n1 = a1->nConstitEquIsotopicTGroupNumbers; } } else { n1 = a1->nConstitEquTGroupNumbers; } if (eql2 & EQL_EQU_ISO) { if ( a2->bIsIsotopic ) { n2 = a2->nConstitEquIsotopicTGroupNumbers; } } else { n2 = a2->nConstitEquTGroupNumbers; } } } else if ( !t1 && !t2 ) { if ( (len = a1->nNumberOfAtoms) > 0 && len == a2->nNumberOfAtoms && !a1->bDeleted && !a2->bDeleted ) { if (eql1 & EQL_EQU_ISO) { if ( a1->bIsIsotopic ) { n1 = a1->nConstitEquIsotopicNumbers; } } else { n1 = a1->nConstitEquNumbers; } if (eql2 & EQL_EQU_ISO) { if ( a2->bIsIsotopic ) { n2 = a2->nConstitEquIsotopicNumbers; } } else { n2 = a2->nConstitEquNumbers; } } } if ( n1 && n2 && !memcmp(n1, n2, len*sizeof(n1[0])) && bHasEquString( n1, len) ) { return 1; } return 0; } /**********************************************************************************************/ int Eql_INChI_Aux_Num( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) { int len; AT_NUMB *n1=NULL, *n2=NULL; if ( !a1 || !a2 ) { return 0; } if ( (len = a1->nNumberOfAtoms) <= 0 || len != a2->nNumberOfAtoms || a1->bDeleted || a2->bDeleted ) { return 0; } if ( (eql1 & EQL_NUM_ISO) && !a1->bIsIsotopic || (eql2 & EQL_NUM_ISO) && !a2->bIsIsotopic ) { return 0; } switch ( eql1 ) { case EQL_NUM: n1 = a1->nOrigAtNosInCanonOrd; break; case EQL_NUM_ISO: n1 = a1->nIsotopicOrigAtNosInCanonOrd; break; case EQL_NUM_INV: n1 = a1->nOrigAtNosInCanonOrdInv; break; case ( EQL_NUM_INV | EQL_NUM_ISO ): n1 = a1->nIsotopicOrigAtNosInCanonOrdInv; break; default: return 0; } switch ( eql2 ) { case EQL_NUM: n2 = a2->nOrigAtNosInCanonOrd; break; case EQL_NUM_ISO: n2 = a2->nIsotopicOrigAtNosInCanonOrd; break; case EQL_NUM_INV: n2 = a2->nOrigAtNosInCanonOrdInv; break; case ( EQL_NUM_INV | EQL_NUM_ISO ): n2 = a2->nIsotopicOrigAtNosInCanonOrdInv; break; default: return 0; } if ( n1 && n2 && !memcmp( n1, n2, len*sizeof(n1[0])) ) { return 1; } return 0; } /**********************************************************************************************/ int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ) { int i, bFound = 0; if ( OrigInfo && num_atoms > 0 ) { for ( i = 0; !bFound && i < num_atoms; i ++ ) { bFound |= (0 != OrigInfo[i].cCharge) || (0 != OrigInfo[i].cRadical) || (0 != OrigInfo[i].cUnusualValence); } } return bFound; } /**********************************************************************************************/ int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ) { int ret = a1 && a2 && a1->nNumberOfAtoms == a2->nNumberOfAtoms && bHasOrigInfo( a1->OrigInfo, a1->nNumberOfAtoms ) && a2->OrigInfo && !memcmp( a1->OrigInfo, a2->OrigInfo, a1->nNumberOfAtoms * sizeof(a1->OrigInfo[0]) ); return ret; } /**********************************************************************************************/ int bHasEquString( AT_NUMB *LinearCT, int nLenCT ) { /* produce output string; */ int i, k; if ( !LinearCT ) return 0; for ( k = 0; k < nLenCT; k ++ ) { /* find the first equivalence number */ if ( k != (int)LinearCT[k] - 1 ) continue; for ( i = k; i < nLenCT; i ++ ) { if ( k != (int)LinearCT[i]-1 ) continue; if ( k < i ) { return 1; } } } return 0; } /********************************************************************************************/ int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { char szValue[16]; int len = 0, len_delim; if ( mult == 1 || *bOverflow ) return 0; if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len += MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, mult ); } else { len += MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); } len_delim = strlen(szTailingDelim); if ( len + len_delim < (int)sizeof(szValue) ) { strcpy( szValue+len, szTailingDelim ); len += len_delim; if ( len < nLen_szLinearCT ) { strcpy( szLinearCT, szValue ); return len; } } *bOverflow |= 1; return 0; } /********************************************************************************************/ int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) { int len_delim; if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) return 0; len_delim = strlen(szTailingDelim); if ( len_delim < nLen_szLinearCT ) { strcpy( szLinearCT, szTailingDelim ); return len_delim; } *bOverflow |= 1; return 0; } /********************************************************************************************/ int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) { int len = 0, len_delim; char szValue[16]; if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) return 0; if ( mult != 1 ) { len = MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); } len_delim = strlen(szTailingDelim); if ( len_delim + len < nLen_szLinearCT ) { if ( len > 0 ) { memcpy( szLinearCT, szValue, len ); } strcpy( szLinearCT+len, szTailingDelim ); return len + len_delim; } *bOverflow |= 1; return 0; } /********************************************************************************************** * nCtMode = 0: full * 1: censored CT (no orphans) * 2: compressed CT (Abs numbers) **********************************************************************************************/ int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, S_CHAR *nNum_H, int num_atoms, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, i, bOvfl = *bOverflow; char szValue[16]; int nValue, nDelim, num_H; AT_NUMB *nDfsOrderCT = NULL; int bNoNum_H = (NULL == nNum_H); int nNumRingClosures; int bAbcNumbers = (0 != ( nCtMode & CT_MODE_ABC_NUMBERS )); int bPredecessors = (0 != ( nCtMode & CT_MODE_PREDECESSORS )); int bCountRingClosures = bAbcNumbers && bPredecessors && (nCtMode & CT_MODE_ABC_NUM_CLOSURES); if ( nLenCT <= 1 ) { return 0; /* no atoms or a single atom: no connection table */ } /* make array containing connection string data */ if ( !(nDfsOrderCT = GetDfsOrder4CT( LinearCT, nLenCT, nNum_H, num_atoms, nCtMode ) ) ) { (*bOverflow) ++; return 0; } /* add connection table string */ if ( !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 1 ) { strcpy( szLinearCT, "," ); nLen ++; } else { bOvfl = 1; } } if ( !bOvfl ) { nNumRingClosures = 0; for ( i = 0; nDfsOrderCT[i] && nLen < nLen_szLinearCT; i += 3 ) { nValue = (nDfsOrderCT[i] > MAX_ATOMS)? 0 : nDfsOrderCT[i]; num_H = nDfsOrderCT[i+1]? nDfsOrderCT[i+1]-16:0; nDelim = nDfsOrderCT[i+2]; len = 0; /* delimiter */ if ( bPredecessors ) { if ( bCountRingClosures ) { if ( nDelim == '-' && i > 3 && bNoNum_H ) { if ( !nNumRingClosures ) { int j; for ( j = i; nDfsOrderCT[j] && '-' == nDfsOrderCT[j+2]; j += 3 ) { nNumRingClosures ++; } if ( nNumRingClosures ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nNumRingClosures ); } nNumRingClosures --; } else { nNumRingClosures --; } } else { nNumRingClosures = 0; } } else if ( nDelim && !( bAbcNumbers && nDelim == ',' ) ) { if ( nNum_H || i > 3 ) { szValue[len ++] = nDelim; } } } else { if ( nDelim && !( bAbcNumbers && nDelim == '-' ) ) { szValue[len ++] = nDelim; } } if ( bAbcNumbers ) { if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ len += MakeAbcNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); } if ( num_H ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); } } else { if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); } if ( num_H ) { szValue[len] = 'H'; len ++; if ( num_H > 1 ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); } } } if ( 0 <= len && nLen+len < nLen_szLinearCT ) { if ( len ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; } } else { bOvfl = 1; break; } } } *bOverflow |= bOvfl; if ( nDfsOrderCT ) inchi_free( nDfsOrderCT ); return nLen; } /********************************************************************************************** * nCtMode = 0: full * 1: censored CT (no orphans) * 2: compressed CT (Abs numbers) **********************************************************************************************/ int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, i, bLessThanPrev, bOvfl = *bOverflow; AT_NUMB nMax = 0; char szValue[16]; int nValue, bNext = 0; /* add connection table string */ if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 1 ) { strcpy( szLinearCT, "," ); nLen ++; } else { bOvfl = 1; } } if ( !bOvfl ) { for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { bLessThanPrev = 0; if ( !(nCtMode & CT_MODE_NO_ORPHANS) || ((bLessThanPrev=LinearCT[i] < nMax) || i+1 < nLenCT && LinearCT[i+1] < (nMax=LinearCT[i])) ) { nValue = LinearCT[i]; if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len = MakeAbcNumber( szValue, (int)sizeof(szValue), (!bNext && bAddDelim)? ITEM_DELIMETER : NULL, nValue ); } else if ( nCtMode & CT_MODE_NO_ORPHANS ) { /* censored CT */ /* output '-' as a delimiter to show a bonding for decimal output of the connection table */ len = MakeDecNumber( szValue, (int)sizeof(szValue), bLessThanPrev? "-":ITEM_DELIMETER, nValue ); } else { len = MakeDecNumber( szValue, (int)sizeof(szValue), i? ITEM_DELIMETER:NULL, nValue ); } if ( 0 <= len && nLen+len < nLen_szLinearCT ) { if ( len ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; bNext ++; } } else { bOvfl = 1; break; } } } } *bOverflow |= bOvfl; return nLen; } /********************************************************************************************** * nCtMode = 0: decimal * 2: compressed CT (Abs numbers) **********************************************************************************************/ int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ) { #define INIT_MIN_NUM_H (-4) #define INIT_MAX_NUM_H 16 #define INIT_LEN_NUM_H (INIT_MAX_NUM_H - INIT_MIN_NUM_H + 1) /* produce output string; */ int nLen = 0, len, i, iFirst, nVal, bOvfl = *bOverflow; char szValue[32]; const char *pH; int bNext = 0; /* add connection table string */ if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 1 ) { strcpy( szLinearCT, "," ); nLen ++; } else { bOvfl = 1; } } if ( !bOvfl && 0 < nLenCT && LinearCT ) { if ( nCtMode & CT_MODE_EQL_H_TOGETHER ) { int curMinH = INIT_MIN_NUM_H; int curMaxH = INIT_MAX_NUM_H; int curLenH = INIT_LEN_NUM_H; int nInitNumH[INIT_LEN_NUM_H]; int *nNumH = nInitNumH; int numAt, curNumH; int j, bOutOfRange, tot_num_no_H; /* count atoms H */ do { bOutOfRange = 0; tot_num_no_H = 0; /* number of atoms that have no H */ memset( nNumH, 0, curLenH*sizeof(nNumH[0]) ); for ( i = 0; i < nLenCT; i ++ ) { curNumH = LinearCT[i]; if ( curNumH < curMinH ) { curMinH = curNumH; bOutOfRange ++; } else if ( curNumH > curMaxH ) { curMaxH = curNumH; bOutOfRange ++; } else if ( !bOutOfRange ) { nNumH[curNumH-curMinH] ++; } tot_num_no_H += !curNumH; } if ( tot_num_no_H == nLenCT ) { return nLen; /* empty string */ } if ( bOutOfRange ) { /* for debug only */ if ( nNumH != nInitNumH ) { *bOverflow |= 1; inchi_free( nNumH ); return nLen; } /* end debug */ curLenH = curMaxH - curMinH + 1; nNumH = (int*) inchi_malloc( curLenH * sizeof(nNumH[0]) ); if ( !nNumH ) { *bOverflow |= 1; return nLen; } } } while ( bOutOfRange ); /* the loop may be executed 1 or 2 times only */ for ( curNumH = curMinH; curNumH <= curMaxH; curNumH ++ ) { numAt = nNumH[curNumH-curMinH]; /* number of atoms that have curNumH atoms H */ if ( !numAt || !curNumH ) { continue; /* no atom has this number of H or number of H = 0 */ } j = 0; while ( j < nLenCT && numAt ) { if ( curNumH == LinearCT[j] ) { iFirst = ++j; numAt --; for ( ; j < nLenCT && curNumH == LinearCT[j] && numAt; j ++ ) { numAt --; } if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); } else { len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); bNext ++; /* add a delimiter (comma) before all except the first */ } if ( iFirst < j ) { /* output last canonical number */ if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, j ); } else { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", j ); } } if ( !numAt || ( nCtMode & CT_MODE_ABC_NUMBERS ) ) { /* add number of H */ /* output number of H */ nVal = curNumH; if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); } else { pH = nVal > 0? "H":"h"; nVal = abs(nVal); if ( nVal > 1 ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); } else { strcpy( szValue+len, pH ); len ++; } } } /* add to the output */ if ( 0 <= len && nLen+len < nLen_szLinearCT ) { if ( len ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; bNext ++; } } else { bOvfl = 1; break; } } else { j ++; } } } if ( nNumH != nInitNumH ) { inchi_free( nNumH ); } } else { iFirst = 0; for ( i = iFirst+1; i <= nLenCT && nLen < nLen_szLinearCT; i ++ ) { if ( i < nLenCT && LinearCT[i] == LinearCT[iFirst] ) { continue; } /* output identical values located at i = iFirst..i-1 */ if ( LinearCT[iFirst] ) { /* output only non-zero values */ /* first canonical number */ nVal = LinearCT[iFirst]; iFirst ++; if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); } else { len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); } if ( iFirst < i ) { /* output last canonical number */ if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, i ); } else { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", i ); } } /* output number of H */ if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); } else { pH = nVal > 0? "H":"h"; nVal = abs(nVal); if ( nVal > 1 ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); } else { strcpy( szValue+len, pH ); len ++; } } if ( 0 <= len && nLen+len < nLen_szLinearCT ) { if ( len ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; bNext ++; } } else { bOvfl = 1; break; } } iFirst = i; } } } *bOverflow |= bOvfl; return nLen; #undef INIT_MIN_NUM_H #undef INIT_MAX_NUM_H #undef INIT_LEN_NUM_H } /********************************************************************************************** * nCtMode = 0: full * 1: censored CT (no orphans, that CT should have only atoms with neighbors) * 2: compressed CT (Abc numbers) **********************************************************************************************/ int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { if ( !nNum_H || !(nCtMode & CT_MODE_NO_ORPHANS) ) { return MakeCtStringOld( LinearCT, nLenCT, bAddDelim, szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); } else { return MakeCtStringNew( LinearCT, nLenCT, bAddDelim, nNum_H, num_atoms, szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); } } /********************************************************************************************** * nCtMode = 0: full: decimal-only, with parentheses around t-groups * 2: compressed CT: do not add comma before the output string if bAddDelim != 0 * do not add parentheses around t-groups * atom canon numbers an Abc * LinearCT format: * N = number of tautomeric groups * n = number of endpoints + 1 in a tautomeric group #1 * next INCHI_T_NUM_MOVABLE lines (any after the first non-zero): * h = number of hydrogen atoms in the tautomeric group * m = number of negative charges * ... (the rest of the INCHI_T_NUM_MOVABLE has not been established, ignore them) * c(1) = canonical number of the first atom in the t-group * ... * c(n-1) = canonical number of the last atom in the t-group * **********************************************************************************************/ int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, i, bOvfl = *bOverflow; char szValue[16]; const char *p; int nValue, nGroupLen, iGroupOutputCount, bCompressed; /* make tautomer string */ if ( !nLenCT || !LinearCT || !*LinearCT ) { return nLen; } bCompressed = ( nCtMode & CT_MODE_ABC_NUMBERS ); if ( !bCompressed && !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 1+LEN_EXTRA_SPACE ) { strcpy( szLinearCT, COMMA_EXTRA_SPACE); nLen += 1+LEN_EXTRA_SPACE; } else { bOvfl = 1; } } LinearCT ++; /* bypass number of tautomeric groups */ nLenCT --; if ( !bOvfl ) { for ( i = nGroupLen = iGroupOutputCount = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { nValue = (int)LinearCT[i]; if ( nGroupLen == iGroupOutputCount ) { nGroupLen = nValue; iGroupOutputCount = 0; /* group delimiter (uncompressed) */ if ( !bCompressed ) { if ( !i ) { strcpy( szValue, "(" ); len = 1; } else { strcpy( szValue, ")(" ); len = 2; } } else { len = 0; } } else if ( bCompressed && iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { /* compressed canon number in Abc */ len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, nValue ); iGroupOutputCount ++; } else { /* always output number of hydrogen atoms as a decimal */ /* output leading space if: */ /* (a) this is the first output value in compressed mode (i==1 && bCompressed) */ /* (b) this is not the first output value in non-compressed mode ( iGroupOutputCount && !bCompressed) */ if ( bCompressed ) { p = NULL; len = 0; switch( iGroupOutputCount ) { case 0: len = MakeDecNumber( szValue, (int)sizeof(szValue), (i == 1)? ITEM_DELIMETER:NULL, nValue ); break; case 1: p = "-"; break; case 2: p = "+"; break; } if ( p ) { switch( nValue ) { case 0: len = 0; break; case 1: strcpy(szValue, p); len = strlen(szValue); break; default: len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); break; } } } else { if ( iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { /* canonical number of the atom in the tautomeric group */ len = MakeDecNumber( szValue, (int)sizeof(szValue), ITEM_DELIMETER, nValue ); } else { p = NULL; len = 0; if ( nValue ) { switch( iGroupOutputCount ) { case 0: p = "H"; break; case 1: p = "-"; break; case 2: p = "+"; break; } if ( p ) { /* number of hydrogens */ if ( nValue == 1 ) { strcpy(szValue, p); len = strlen(szValue); } else { len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); } } } } } iGroupOutputCount ++; } if ( 0 <= len && nLen+len < nLen_szLinearCT ) { if ( len ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; } } else { bOvfl = 1; break; } } if ( !bOvfl && !bCompressed && i ) { if ( nLen + 1 < nLen_szLinearCT ) { strcpy( szLinearCT+nLen, ")" ); nLen ++; } else { bOvfl = 1; } } } *bOverflow |= bOvfl; return nLen; } /********************************************************************************************** * nCtMode = 0: full * 2: compressed CT * 22+3s3: 22=canon. number; +3=charge; s=singlet (d=doublet, t=triplet, s is omitted if valence=0), 3 = valence * 22+3.3, (charge, valence) 22.3 (valence) 22t3 (triplet, valence) * Ab+3t4: Ab=canon. number; +3=charge or "." t=triplet (or s, d), 4=valence **********************************************************************************************/ int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, k, bAbcNumbers; int bOvfl = *bOverflow; char szValue[32]; int bNext=0; bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); /* add connection table string */ if ( !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 2 ) { strcpy( szLinearCT, ", " ); nLen += 2; } else { bOvfl = 1; } } for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { /* find the next non-empty entry */ if ( OrigInfo[k].cCharge || OrigInfo[k].cRadical || OrigInfo[k].cUnusualValence ) { if ( bAbcNumbers ) { /* 3 items: Ad+3d4 (canon. numb=Ad, charge=+3, doublet, valence = 4 2 items: Ad.d4 Ad+3.4 Ad+3d 1 item: Ad+3 Ad.d Ad4 dot output before radical: no charge, radical is present dot before valence: charge is present, no radical, valence is present */ len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, k+1 ); /* charge */ if ( OrigInfo[k].cCharge ) { if ( OrigInfo[k].cCharge > 0 ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); } else { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); } } /* radical */ if ( OrigInfo[k].cRadical ) { if ( !OrigInfo[k].cCharge ) { szValue[len ++] = '.'; } switch( OrigInfo[k].cRadical ) { case 1: szValue[len ++] = 'd'; break; case 2: szValue[len ++] = 't'; break; default: szValue[len ++] = 'u'; break; } } /* valence */ if ( OrigInfo[k].cUnusualValence ) { if ( OrigInfo[k].cCharge && !OrigInfo[k].cRadical ) { szValue[len ++] = '.'; } len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); } } else { /* 3 items: 22+3d4 (canon. numb=22, charge=+3, doublet, valence = 4 2 items: 22d4 22+3.4 22+3d 1 item: 22+3 22d 22.4 dot output before valence: (a) charge, no radical, valence (b) no charge, no radical, valence that is, whenever valence is present and no radical */ len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext? ITEM_DELIMETER:NULL, k+1 ); /* charge */ if ( OrigInfo[k].cCharge ) { if ( OrigInfo[k].cCharge > 0 ) { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); } else { len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); } } /* radical */ if ( OrigInfo[k].cRadical ) { switch( OrigInfo[k].cRadical ) { case 1: szValue[len ++] = 'd'; break; case 2: szValue[len ++] = 't'; break; default: szValue[len ++] = 'u'; break; } } /* valence */ if ( OrigInfo[k].cUnusualValence ) { if ( !OrigInfo[k].cRadical ) { szValue[len ++] = '.'; } len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); } } } else { len = 0; } if ( len && nLen+len < nLen_szLinearCT ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; bNext ++; } else if ( len ) { bOvfl = 1; break; } } *bOverflow |= bOvfl; return nLen; } /********************************************************************************************** * nCtMode = 0: full * 2: compressed CT **********************************************************************************************/ int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, i, k, bAbcNumbers; int bOvfl = *bOverflow; char szValue[16]; int bNext=0; bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); /* add connection table string */ if ( !bOvfl && bAddDelim ) { if ( nLen_szLinearCT > 2 ) { strcpy( szLinearCT, ", " ); nLen += 2; } else { bOvfl = 1; } } for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { /* find the first equivalence number */ if ( k != (int)LinearCT[k] - 1 ) continue; for ( i = k; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { if ( k != (int)LinearCT[i]-1 ) continue; /* equivalence number: a minimal canon_number out of a group of equivalent atoms */ /* is at canon_number-1 position of each equivalent atom. */ if ( bAbcNumbers ) { len = MakeAbcNumber( szValue, (int)sizeof(szValue), (i==k && bNext)? ITEM_DELIMETER : NULL, i+1 ); } else { len = MakeDecNumber( szValue, (int)sizeof(szValue), (i==k)? "(":ITEM_DELIMETER, i+1 ); } if ( 0 <= len && nLen+len < nLen_szLinearCT ) { strcpy( szLinearCT+nLen, szValue ); nLen += len; bNext ++; } else if ( 0 > len ) { bOvfl = 1; break; } } if ( !bOvfl && !bAbcNumbers ) { if ( nLen + 2 < nLen_szLinearCT ) { strcpy( szLinearCT+nLen, ")" ); nLen ++; } else { bOvfl = 1; } } } *bOverflow |= bOvfl; return nLen; } /********************************************************************************************** * nCtMode = 0: full * 2: compressed CT **********************************************************************************************/ int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, tot_len, ret, i, j, bOvfl = *bOverflow; char szValue[64]; char *p; int nValue; int bAbcNumbers = (nCtMode & CT_MODE_ABC_NUMBERS ); static const char letter[] = "itdh"; static const char *h[] = {"T", "D", "H"}; static const char *sign[] = {"-", "+"}; if ( !bOvfl ) { for ( i = 0; i < nNumberOfIsotopicAtoms && nLen < nLen_szLinearCT; i ++ ) { p = szValue; tot_len = 0; for ( j = 0; j < 5; j ++ ) { len = 0; switch( j ) { case 0: nValue = (int)IsotopicAtom[i].nAtomNumber; break; case 1: nValue = (int)IsotopicAtom[i].nIsoDifference; break; case 2: nValue = (int)IsotopicAtom[i].nNum_T; break; case 3: nValue = (int)IsotopicAtom[i].nNum_D; break; case 4: nValue = (int)IsotopicAtom[i].nNum_H; break; } if ( !j ) { /* atom canonical number */ len = (bAbcNumbers? MakeAbcNumber:MakeDecNumber) ( p, (int)sizeof(szValue)-tot_len, bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), nValue ); } else if ( bAbcNumbers ) { /* Abc output */ switch ( j ) { case 1: /* nIsoDifference */ len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); break; case 2: /* nNum_T */ case 3: /* nNum_D */ case 4: /* nNum_H */ if ( nValue ) { if ( (int)sizeof(szValue) - tot_len > 1 ) { p[len++]=letter[j-1]; if ( 1 == nValue ) { p[len] = '\0'; } else { ret = MakeDecNumber( p+len, (int)sizeof(szValue)-tot_len-len, NULL, nValue ); len = (ret >= 0)? len+ret : ret; } } else { len = -1; /* overflow */ } } } } else if ( nValue ) { if ( j == 1 ) { /* Decimal output */ /* signed isotopic mass difference */ int subtract = (nValue > 0); /* (n = mass difference) > 0 corresponds to nValue = n+1 */ /* subtract 1 from it so that mass difference for 35Cl or 12C is zero */ len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, sign[nValue>=0], abs(nValue-subtract) ); } else { /* hydrogen isotope */ if ( nValue != 1 ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-2], nValue ); } else if ( (int)sizeof(szValue)-tot_len > 1 ) { strcpy( p, h[j-2] ); len = 1; } else { len = -1; /* overflow */ } } } else { continue; /* do not write zeroes */ } if ( len < 0 ) { bOvfl = 1; break; } tot_len += len; p += len; } if ( nLen+tot_len < nLen_szLinearCT ) { memcpy( szLinearCT+nLen, szValue, tot_len+1 ); nLen += tot_len; } else { bOvfl = 1; break; } } } *bOverflow |= bOvfl; return nLen; } /**********************************************************************************************/ int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; AT_NUMB nMax; char szValue[32]; char *p; int nValue; int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); static const char letter[] = "tdh"; static const char *h[] = {"T", "D", "H"}; /* add connection table string */ nMax = 0; if ( !bOvfl ) { for ( i = 0; i < nNumberOfIsotopicTGroups && nLen < nLen_szLinearCT; i ++ ) { p = szValue; tot_len = 0; for ( j = 0; j < 4; j ++ ) { switch( j ) { case 0: nValue = (int)IsotopicTGroup[i].nTGroupNumber; break; case 1: nValue = (int)IsotopicTGroup[i].nNum_T; break; case 2: nValue = (int)IsotopicTGroup[i].nNum_D; break; case 3: nValue = (int)IsotopicTGroup[i].nNum_H; break; } if ( !j ) { /* atom canonical number */ len = (bAbcNumbers?MakeAbcNumber:MakeDecNumber) ( p, (int)sizeof(szValue)-tot_len, bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), nValue ); } else if ( nValue ) { if ( bAbcNumbers ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); if ( len > 0 ) { /* make sure overflow has not happened */ if ( (int)sizeof(szValue)-tot_len-len > 1 ) { p[len++]=letter[j-1]; p[len] = '\0'; } else { len = -1; /* overflow */ } } } else { /* hydrogen isotope */ if ( nValue != 1 ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); } else if ( (int)sizeof(szValue)-tot_len > 1 ) { strcpy( p, h[j-1] ); len = 1; } else { len = -1; /* overflow */ } } } else { continue; /* do not write zeroes */ } if ( len < 0 ) { bOvfl = 1; break; } p += len; tot_len += len; } if ( nLen+tot_len < nLen_szLinearCT ) { memcpy( szLinearCT+nLen, szValue, tot_len+1 ); nLen += tot_len; } else { bOvfl = 1; break; } } } *bOverflow |= bOvfl; return nLen; } /**********************************************************************************************/ int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, tot_len, j, bOvfl = *bOverflow; AT_NUMB nMax; char szValue[32]; char *p; int nValue; int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); static const char letter[] = "tdh"; static const char *h[] = {"T", "D", "H"}; /* add connection table string */ nMax = 0; if ( !bOvfl ) { p = szValue; tot_len = 0; for ( j = 1; j < 4; j ++ ) { nValue = num_iso_H[NUM_H_ISOTOPES-j];/* j: 1=>T, 2=>D, 3=>1H */ if ( nValue ) { if ( bAbcNumbers ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); if ( len > 0 ) { /* make sure overflow has not happened */ if ( (int)sizeof(szValue)-tot_len-len > 1 ) { p[len++]=letter[j-1]; p[len] = '\0'; } else { len = -1; /* overflow */ } } } else { /* hydrogen isotope */ if ( nValue != 1 ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); } else if ( (int)sizeof(szValue)-tot_len > 1 ) { strcpy( p, h[j-1] ); len = 1; } else { len = -1; /* overflow */ } } } else { continue; /* do not write zeroes */ } if ( len < 0 ) { bOvfl = 1; break; } p += len; tot_len += len; } if ( nLen+tot_len < nLen_szLinearCT ) { memcpy( szLinearCT+nLen, szValue, tot_len+1 ); nLen += tot_len; } else { bOvfl = 1; } } *bOverflow |= bOvfl; return nLen; } /**********************************************************************************************/ int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) { /* produce output string; */ int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; char szValue[32]; char *p; int nValue; static const char parity_char[] = "!-+u?"; bAddDelim = 0; if ( !bOvfl ) { for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { p = szValue; tot_len = 0; for ( j = 0; j < 3; j ++ ) { if ( j == 0 && at1 ) nValue = (int)at1[i]; else if ( j == 1 && at2 ) nValue = (int)at2[i]; else if ( j == 2 && parity ) nValue = (int)parity[i]; else continue; if ( nCtMode & CT_MODE_ABC_NUMBERS ) { len = (j==2? MakeDecNumber : MakeAbcNumber)( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); } else { if ( j < 2 ) { len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, tot_len?"-":(i||bAddDelim)?ITEM_DELIMETER:NULL, nValue ); } else if ( tot_len + 1 < (int)sizeof(szValue) ) { *p ++ = (0<=nValue && nValue<=4)? parity_char[nValue]:parity_char[0]; *p = '\0'; len = 1; } else { len = -1; /* Overflow */ } } if ( len < 0 ) { bOvfl = 1; break; } p += len; tot_len += len; } if ( nLen+tot_len < nLen_szLinearCT ) { memcpy( szLinearCT+nLen, szValue, tot_len+1 ); nLen += tot_len; } else { bOvfl = 1; break; } } } *bOverflow |= bOvfl; return nLen; } #ifdef ALPHA_BASE #if ( ALPHA_BASE != 27 ) #error ALPHA_BASE definitions mismatch #endif #else #define ALPHA_BASE 27 #endif #define ALPHA_MINUS '-' #define ALPHA_ZERO_VAL '.' #define ALPHA_ONE 'a' #define ALPHA_ZERO '@' /**********************************************************************************************/ /* Produce an "Alphabetic" number, base 27 (27 digits: 0, a, b, ..., z) */ /* The leading "digit" uppercase, the rest -- lowercase */ /* szString length nStringLen includes 1 byte for zero termination */ /* Return Value: length without zero termination; -1 means not enough room */ /* Note: ASCII-encoding specific implementation */ int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) { char *p = szString; char *q; int nChar; if ( nStringLen < 2 ) return -1; while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { *p ++ = *szLeadingDelim ++; } if ( nStringLen < 2 ) return -1; if ( !nValue ) { *p++ = ALPHA_ZERO_VAL; /* zero value (cannot use 0) */ *p = '\0'; return 1; } if ( nValue < 0 ) { *p++ = ALPHA_MINUS; nStringLen --; nValue = -nValue; } for ( q = p; nValue && --nStringLen; nValue /= ALPHA_BASE ) { if ( nChar = nValue % ALPHA_BASE ) { nChar = ALPHA_ONE + nChar - 1; } else { nChar = ALPHA_ZERO; } *q++ = nChar; } if ( nStringLen <= 0 ) return -1; *q = '\0'; mystrrev( p ); p[0] = toupper(p[0]); return (q - szString); } #if ( READ_INCHI_STRING == 1 ) /*****************************************************/ static long abctol( const char *szString, char **q ); /* keep compiler happy */ long abctol( const char *szString, char **q ) { #define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) long val = 0; long sign = 1; const char *p = szString; if ( *p == ALPHA_MINUS ) { p ++; sign = -1; } if ( *p == ALPHA_ZERO ) { p ++; goto exit_function; } if ( !isupper(UCINT *p) ) { p = szString; goto exit_function; /* not an abc-number */ } val = __MYTOLOWER(*p) - ALPHA_ONE + 1; p ++; while ( *p ) { if ( islower( UCINT *p ) ) { val *= ALPHA_BASE; val += *p - ALPHA_ONE + 1; } else if ( *p == ALPHA_ZERO ) { val *= ALPHA_BASE; } else { break; } p ++; } exit_function: if ( q ) { *q = (char *)p; /* cast deliberately discards const qualifier */ } return val; #undef __MYTOLOWER } /********************************************************/ long inchi_strtol( const char *str, const char **p, int base) { if ( base == ALPHA_BASE ) { return abctol( str, (char **)p ); /* cast deliberately discards const qualifier */ } else { return strtol( str, (char **)p, base ); /* cast deliberately discards const qualifier */ } } #endif #undef ALPHA_BASE #undef ALPHA_MINUS #undef ALPHA_ZERO_VAL #undef ALPHA_ONE #undef ALPHA_ZERO /********************************************************/ double inchi_strtod( const char *str, const char **p ) { return strtod( str, (char **)p ); } /**********************************************************************************************/ /* Produce a decimal number */ /* szString length nStringLen includes 1 byte for zero termination */ /* Return Value: length without zero termination; -1 means not enough room */ int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) { #define DECIMAL_BASE 10 #define DECIMAL_MINUS '-' #define DECIMAL_ZERO_VAL '0' #define DECIMAL_ONE '1' #define DECIMAL_ZERO '0' char *p = szString; char *q; int nChar; if ( nStringLen < 2 ) return -1; while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { *p ++ = *szLeadingDelim ++; } if ( nStringLen < 2 ) return -1; if ( !nValue ) { *p++ = DECIMAL_ZERO_VAL; /* zero value (cannot use 0) */ *p = '\0'; return p-szString; } if ( nValue < 0 ) { *p++ = DECIMAL_MINUS; nStringLen --; nValue = -nValue; } for ( q = p; nValue && --nStringLen; nValue /= DECIMAL_BASE ) { if ( nChar = nValue % DECIMAL_BASE ) { nChar = DECIMAL_ONE + nChar - 1; } else { nChar = DECIMAL_ZERO; } *q++ = nChar; } if ( nStringLen <= 0 ) return -1; *q = '\0'; mystrrev( p ); return (q - szString); #undef DECIMAL_BASE #undef DECIMAL_MINUS #undef DECIMAL_ZERO_VAL #undef DECIMAL_ONE #undef DECIMAL_ZERO } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiprt3.c000066400000000000000000005255441271037650300234610ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichicomp.h" #include "ichimain.h" #include "ichimake.h" /***************************************************************************/ int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp2 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ eq2taut = 0; #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { Stereo = pINChI->Stereo; Stereo_Taut = pINChI_Taut->Stereo; eq2taut = Stereo && Stereo_Taut && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; if ( !eq2taut && !Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ } } #else if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; } #endif if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut stereo has been found to be same as tautomeric */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, Stereo_Prev->b_parity, 0, Stereo_Prev->nNumberOfStereoBonds, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI->Stereo sp2 same as in pINChI_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output the previous one */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* pINChI sp2 info is either different or trivial. Output pINChI_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { /* pINChI_Prev exists and has sp2 info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, Stereo_Prev->b_parity, 0, Stereo_Prev->nNumberOfStereoBonds, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp2 info is not present in pINChI_Prev */ } else if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial sp2 info */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev sp2 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp2 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) #else bRelRac = 0; #endif for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp3 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ eq2taut = 0; #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { Stereo = pINChI->Stereo; Stereo_Taut = pINChI_Taut->Stereo; eq2taut = Stereo && Stereo_Taut && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; if ( !eq2taut && !Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ } } #else if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; } #endif if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut stereo has been found to be same as tautomeric */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { /* non-empty item */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI->Stereo sp3 same as in pINChI_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ /*================ compare sp3 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { /* pINChI_Prev exists and has sp3 info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp3 info is not present in pINChI_Prev */ } else if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial sp3 info. This info has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp3 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare isotopic info to previous component =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } /*========= if bSecondNonTautPass then compare iso non-taut to taut non-iso ========*/ eq2taut = 0; if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = Eql_INChI_Isotopic( pINChI, pINChI_Taut ); eq2taut = eq2taut? (iiNUMB | iitNONTAUT) : 0; } if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut isotopic info has been found to be same as current tautomeric */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { /* non-empty item */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); /* Isotopic atoms */ if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { /* dereferenced bOverflow 2004-06-07 */ tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* Isotopic tautomeric groups */ if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); if ( !bAbcNumbers ) { tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); } } } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI isotopic info to be same as in pINChI_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev isotopic info does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev isotopic info does not exist */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /* might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /*================ compare iso composition to previous =====================*/ /* check whether pINChI and pINChI_Prev have non-zero identical isotopic info */ eq2prev =bUseMulipliers && Eql_INChI_Isotopic( pINChI, pINChI_Prev ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* pINChI isotopic info is either different or empty. Output pINChI_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { /* pINChI_Prev exists and has isotopic info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); /* Isotopic atoms */ if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* Isotopic tautomeric groups */ if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); if ( !bAbcNumbers ) { tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); } } } /* else isotopic info is not present in pINChI_Prev */ } else if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (pINChI_Taut_Prev->nNumberOfIsotopicAtoms > 0 || pINChI_Taut_Prev->nNumberOfIsotopicTGroups > 0) ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial isotopic info */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev isotopic info was output in the main isotopic section */ } else { ; /* pINChI_Taut_Prev exists and has not isotopic info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } /* Fix17: moved here 2004-10-08 */ pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } /* Fix17: moved from here 2004-10-08 pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; */ } return tot_len; } /***************************************************************************/ int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp2 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions ) { /* compare non-tautomeric isotopic to: * a) non-tautomeric non-isotopic * b) tautomeric non-isotopic * c) tautomeric isotopic */ /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && /* non-taut isotopic */ /* non-taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); /* stereo isotopic non-taut = non-taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; } /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && /* non-taut isotopic */ /* taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); /* stereo isotopic non-taut = taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; } /* c) compare non-tautomeric isotopic to tautomeric isotopic */ if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); /* stereo isotopic non-taut = isotopic taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; } #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { /* component has no stereo; check whether it has stereo in the preceding layers */ if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) || !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) && (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } else /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ if ( !bSecondNonTautPass && bOmitRepetitions ) { /* compare tautomeric isotopic to tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && /* taut isotopic */ /* taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); /* stereo isotopic taut = taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) ) { /* component has no MI stereo; check whether it has stereo in the preceding layer M */ if ( (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } } if ( eq2taut ) { /* we may be here only in case of the current layer found equal in another layer the same component */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it before output the current component */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, Stereo_Prev->b_parity, 0, Stereo_Prev->nNumberOfStereoBonds, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ } /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* current layer is different from previously printed layers of the current component */ /* compare the current layer to this layer of the previous component: */ /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ /*================ compare iso sp2 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* the current layer is different from this layer of the previous component */ /* therefore print the current layer */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, Stereo_Prev->b_parity, 0, Stereo_Prev->nNumberOfStereoBonds, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp2 info is not present in pINChI_Prev */ } else /* do not print pINChI_Prev because it either do not exist of have already been printed */ if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial sp2 info */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp2 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /******************************************************************************************/ int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) #else bRelRac = 0; #endif for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp2 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions ) { /* compare non-tautomeric isotopic to: * a) non-tautomeric non-isotopic * b) tautomeric non-isotopic * c) tautomeric isotopic */ /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && /* non-taut isotopic */ /* non-taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); /* stereo isotopic non-taut = non-taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; } /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && /* non-taut isotopic */ /* taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); /* stereo isotopic non-taut = taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; } /* c) compare non-tautomeric isotopic to tautomeric isotopic */ if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); /* stereo isotopic non-taut = isotopic taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; } #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { /* component has no stereo; check whether it has stereo in the preceding layers */ if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) || !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) && (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } else /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ if ( !bSecondNonTautPass && bOmitRepetitions ) { /* compare tautomeric isotopic to tautomeric non-isotopic */ if ( !eq2taut ) { eq2taut = pINChI && /* taut isotopic */ /* taut non-isotopic */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); /* stereo isotopic taut = taut (stereo) */ eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) ) { /* component has no MI stereo; check whether it has stereo in the preceding layer M */ if ( (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } } if ( eq2taut ) { /* we may be here only in case of the current layer found equal in another layer the same component */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it before output the current component */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ } /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* current layer is different from previously printed layers of the current component */ /* compare the current layer to this layer of the previous component: */ /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ /*================ compare iso sp3 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp3 info is not present in pINChI_Prev */ } else /* do not print pINChI_Prev because it either do not exist of have already been printed */ if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial sp2 info */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp3 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI_Aux *pINChI_Aux = NULL, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Aux_Prev = NULL; pINChI_Aux_Taut = NULL; pINChI_Aux_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI_Aux = (i < num_components && (is = is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; } /*================ compare non-iso non-taut equivalence info to non-iso taut ========*/ eq2taut = bSecondNonTautPass && bOmitRepetitions && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Taut, EQL_EQU ); eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut equivalence has been found to be same as tautomeric */ if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { /* previous component exists */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { /* output previous component(s) equivalence since it was found to be non-trivial */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ } } else if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI_Aux->nConstitEquNumbers same as in pINChI_Aux_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ mult = 0; eq2tautPrev = 1; } else if ( eq2tautPrev ) { /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ /*might have been discovered and it is different from pINChI_Aux_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Aux_Prev = pINChI_Aux; pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; mult = 0; } else { /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Prev, EQL_EQU ); if ( eq2prev ) { /* eq. info is same and non-trivial */ mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { /* pINChI_Aux_Prev exists and has equivalence info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ } } else if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ /* and it has non-trivial equivalence info. This info has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ } else { ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Aux_Prev = pINChI_Aux; pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /******************************************************************************************/ int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; /*************** inverted sp3 ****************/ pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp2 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions ) { /* compare non-tautomeric inverted to: * a) tautomeric inverted * b) Inverted(tautomeric) * c) Inverted(non-tautomeric) */ /* a) compare non-tautomeric inverted to tautomeric inverted */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && /* non-taut inverted */ /* taut invertedc */ (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv non-taut = taut (stereo-inv) */ eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT ) : 0; } /* b) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); /* stereo-inv non-taut = Inv(taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV) : 0; } /* c) compare non-tautomeric inverted to Inverted(non-tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && (Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); /* stereo-inv non-taut = Inv(non-taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; } #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && pINChI_Taut && !((Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ))) { if ( (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ } } #endif } else /*========= if not bSecondNonTautPass then compare inv taut stereo to various taut stereo ========*/ if ( !bSecondNonTautPass && bOmitRepetitions ) { /* compare tautomeric inverted to Invetred(tautomeric) */ if ( !eq2taut ) { eq2taut = pINChI && (Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); /* stereo isotopic taut = taut (stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iiEq2INV ) : 0; } } if ( eq2taut ) { /* we may be here only in case of the current layer found equal in another layer the same component */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it before output the current component */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ } /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* current layer is different from previously printed layers of the current component */ /* compare the current layer to this layer of the previous component: */ /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ /*================ compare iso sp3 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && /* do both have stereo? */ (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && /* is their inverted stereo same? */ Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp3 info is not present in pINChI_Prev */ } else /* do not print pINChI_Prev because it either do not exist of have already been printed */ if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp3 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions) { int i, ii, ii2; INCHI_SORT *is, *is0 /*, *is2*/; INChI *pINChI, *pINChI_Taut; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; INChI_Stereo *Stereo, *Stereo_Taut; int eq2taut, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; /************************************************** * specificity of numbering: there is no previous * * component because no repetition is possible * **************************************************/ pINChI = NULL; pINChI_Taut = NULL; pINChI_Aux = NULL; pINChI_Aux_Taut = NULL; pINChI_Aux_Prev = NULL; bNext = 0; is = NULL; is0 = pINChISort; /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i < num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ is=is0+i; pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; /*================ to compare to previously printed =====================*/ if ( bSecondNonTautPass ) { /* component that was printed on the 1st pass */ pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare inv non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions && pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { /* compare non-tautomeric inverted stereo numbering to: * a) tautomeric numbering * b) non-tautomeric numbering * c) tautomeric inverted stereo numbering */ /* a) compare non-tautomeric inverted stereo numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM ); /* stereo-inv numbering non-taut = taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT ) : 0; } /* b) compare non-tautomeric inverted stereo numbering to non-tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); /* stereo-inv numb. non-taut = non-taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; } /* c) compare non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM_INV ); /* stereo-inv numb. non-taut = taut inv stereo numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2INV ) : 0; } } else /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { /* compare tautomeric inverted stereo numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); /* stereo-inv numbering (taut) = taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; } } if ( eq2taut ) { /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { /* current layer is different from previously printed layers of the current component */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms && (Stereo = pINChI->Stereo) && Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs && pINChI_Aux->nOrigAtNosInCanonOrdInv ) { tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrdInv, pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else inv stereo info is not present in pINChI */ } } if ( multPrevEquStr && pPrevEquStr ) { /* the new EqStr of the last item has not been printed; output it now */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } return tot_len; } /***************************************************************************/ int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions) { int i, ii, ii2; INCHI_SORT *is, *is0 /*, *is2*/; INChI *pINChI, *pINChI_Taut; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; int eq2taut, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; /************************************************** * specificity of numbering: there is no previous * * component because no repetition is possible * **************************************************/ pINChI = NULL; /* not used here, for debug only */ pINChI_Taut = NULL; /* not used here, for debug only */ pINChI_Aux = NULL; pINChI_Aux_Taut = NULL; pINChI_Aux_Prev = NULL; bNext = 0; is = NULL; is0 = pINChISort; /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i < num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ is=is0+i; pINChI_Aux = (i < num_components && 0 <= (ii=GET_II(bOutType,is)))? is->pINChI_Aux[ii] : NULL; /*================ to compare to previously printed =====================*/ if ( bSecondNonTautPass ) { pINChI_Aux_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI_Aux[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut numb to other numb ========*/ if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { /* compare non-tautomeric isotopic numbering to: * a) tautomeric numbering * b) non-tautomeric numbering * c) tautomeric isotopic numbering */ /* a) compare non-tautomeric isotopic numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); /* numbering non-taut isotopic = taut numbering */ eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO ) : 0; } /* b) compare non-tautomeric isotopic numbering to non-tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); /* numbering non-taut isotopic = non-taut numbering */ eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2NONTAUT ) : 0; } /* c) compare non-tautomeric isotopic numbering to tautomeric isotopic numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); /* numbering non-taut isotopic = taut isotopic numbering */ eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2ISO ) : 0; } } else /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { /* compare tautomeric isotopic numbering to tautomeric non-isotopic numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); /* stereo-inv numbering (taut) = taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; } } if ( eq2taut ) { /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { /* current layer is different from previously printed layers of the current component */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux && pINChI_Aux->nNumberOfAtoms && pINChI_Aux->bIsIsotopic && pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ) { tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else isotopic numbering is not present in pINChI */ } } if ( multPrevEquStr && pPrevEquStr ) { /* the new EqStr of the last item has not been printed; output it now */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } return tot_len; } /***************************************************************************/ int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Aux = NULL; pINChI_Aux_Prev = NULL; pINChI_Aux_Taut = NULL; pINChI_Aux_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; } /*================ compare iso non-taut equivalence info to non-iso taut ========*/ eq2taut = 0; if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { /************************************************** * compare isotopic non-tautomeric equivalence to: * a) tautomeric * b) non-tautomeric * c) isotopic tautomeric */ if ( !eq2taut ) { /* compare isotopic non-tautomeric equivalence to tautomeric */ eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU ); /* equ non-taut isotopic = tautomeric*/ eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO) : 0; } if ( !eq2taut ) { /* compare isotopic non-tautomeric equivalence to non-tautomeric */ eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); /* equ non-taut isotopic = non-tautomeric*/ eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2NONTAUT) : 0; } if ( !eq2taut ) { /* compare isotopic non-tautomeric equivalence to isotopic tautomeric */ eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU_ISO ); /* equ non-taut isotopic = isotopic tautomeric*/ eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2ISO) : 0; } } else if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { /************************************************** * compare isotopic tautomeric equivalence to: * a) non-isotopic tautomeric */ if ( !eq2taut ) { /* compare isotopic tautomeric equivalence to tautomeric */ eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); /* equ taut-isotopic = tautomeric*/ eq2taut = eq2taut? (iiEQU | iitISO) : 0; } } if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut equivalence has been found to be same as tautomeric */ if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { /* previous component exists */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { /* output previous component(s) equivalence since it was found to be non-trivial */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ } } else if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicNumbers same as in pINChI_Aux_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ mult = 0; eq2tautPrev = 1; } else if ( eq2tautPrev ) { /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ /*might have been discovered and it is different from pINChI_Aux_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Aux_Prev = pINChI_Aux; pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; mult = 0; } else { /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_ISO ); if ( eq2prev ) { /* eq. info is same and non-trivial */ mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { /* pINChI_Aux_Prev exists and has equivalence info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ } } else if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ /* and it has non-trivial equivalence info. This info has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ } else { ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Aux_Prev = pINChI_Aux; pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /******************************************************************************************/ int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; /******************************** inverted isotopic sp3 *********************************/ pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp2 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI->nNumberOfIsotopicAtoms+pINChI->nNumberOfIsotopicTGroups > 0 ) { /* compare non-tautomeric isotopic inverted to: * a) tautomeric inverted * b) *non-tautomeric inverted * c) *isotopic tautomeric inverted * d) Inverted(tautomeric) * e) *Inverted(tautomeric isotopic) * f) Inverted(non-tautomeric) * g) *Inverted(non-tautomeric isotopic) */ /* a) compare non-tautomeric isotopic inverted to tautomeric inverted */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && /* non-taut inverted */ /* taut invertedc */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv isotopic non-taut = taut (stereo-inv) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT ) : 0; } /* b) compare non-tautomeric isotopic inverted to non-tautomeric inverted */ if ( !eq2taut ) { eq2taut = pINChI && /* it is non-taut non-iso stereo */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv isotopic non-taut = non-taut stereo-inv */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2NONTAUT) : 0; } /* c) compare non-tautomeric isotopic inverted to isotopic tautomeric inverted */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && /* non-taut iso. inverted */ /* taut iso. inverted */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv isotopic non-taut = taut iso. stereo-inv */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2ISO ) : 0; } /* d) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv isotopic non-taut = Inv(non-iso taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV) : 0; } /* e) compare non-tautomeric inverted to Inverted(isotopic tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && pINChI_Taut && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); /* stereo-inv isotopic non-taut = Inv(iso taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; } /* f) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && /* it is non-taut non-iso stereo */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); /* stereo-inv isotopic non-taut = Inv(non-taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; } /* g) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric isotopic stereo) */ if ( !eq2taut ) { eq2taut = pINChI && /* it is non-taut non-iso stereo */ (Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); /* stereo-inv isotopic non-taut = Inv( iso non-taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO | iiEq2NONTAUT) : 0; } #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { /* component has no stereo; check whether it has stereo in the preceding layers */ if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) || !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) && (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } else /*========= if not bSecondNonTautPass then compare inv taut stereo to various stereo ========*/ if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && (pINChI->nNumberOfIsotopicAtoms > 0 || pINChI->nNumberOfIsotopicTGroups > 0 || pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) ) { /* compare tautomeric isotopic stereo-inverted to: * a) tautomeric stereo-inverted * b) Inverted(tautomeric stereo) * c) Inverted(tautomeric isotopic stereo) */ /* a) compare tautomeric isotopic stereo-inverted to tautomeric stereo-inverted */ if ( !eq2taut ) { eq2taut = pINChI && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); /* stereo-inv isotopic taut = taut stereo-inv */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO ) : 0; } /* b) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric stereo) */ if ( !eq2taut ) { eq2taut = pINChI && (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); /* stereo-inv isotopic taut = Inv(taut stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV ) : 0; } /* c) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric isotopic stereo) */ if ( !eq2taut ) { eq2taut = pINChI && (Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); /* stereo-inv isotopic taut = Inv(taut iso stereo) */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV | iiEq2ISO ) : 0; } #if ( FIX_EMPTY_LAYER_BUG == 1 ) if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) ) { /* component has no MI stereo; check whether it has stereo in the preceding layer M */ if ( (Stereo_Taut = pINChI->Stereo) && Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) { eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ } } #endif } if ( eq2taut ) { /* we may be here only in case of the current layer found equal in another layer the same component */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it before output the current component */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ } /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* current layer is different from previously printed layers of the current component */ /* compare the current layer to this layer of the previous component: */ /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ /*================ compare iso sp3 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI->nNumberOfIsotopicAtoms + pINChI->nNumberOfIsotopicTGroups > 0 && pINChI_Prev && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 && /* do both have stereo? */ (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && /* is their inverted stereo same? */ Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 ) { if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, 0, Stereo_Prev->nNumberOfStereoCenters, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else sp3 info is not present in pINChI_Prev */ } else /* do not print pINChI_Prev because it either do not exist of have already been printed */ if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp3 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions) { int i, ii, ii2; INCHI_SORT *is, *is0 /*, *is2*/; INChI *pINChI, *pINChI_Taut; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; INChI_Stereo *Stereo, *Stereo_Taut; int eq2taut, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; /************************************************** * specificity of numbering: there is no previous * * component because no repetition is possible * **************************************************/ pINChI = NULL; pINChI_Taut = NULL; pINChI_Aux = NULL; pINChI_Aux_Taut = NULL; pINChI_Aux_Prev = NULL; bNext = 0; is = NULL; /* is2 = NULL;*/ is0 = pINChISort; /* is20 = bSecondNonTautPass? pINChISort2 : NULL;*/ eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i < num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ is=is0+i; pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; /*================ to compare to previously printed =====================*/ if ( bSecondNonTautPass ) { /* component that was printed on the 1st pass */ pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { /* compare isotopic non-tautomeric inverted stereo numbering to: * a) tautomeric numbering * b) non-tautomeric numbering * c) *tautomeric isotopic numbering * d) *non-tautomeric isotopic numbering * e) tautomeric inverted stereo numbering * f) *non-tautomeric inverted stereo numbering * g) tautomeric isotopic inverted stereo numbering */ /* a) compare isotopic non-tautomeric inverted stereo numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); /* stereo-inv isotopic numbering non-taut = taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT ) : 0; } /* b) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); /* stereo-inv isotopic numb. non-taut = non-taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; } /* c) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); /* stereo-inv isotopic numb. non-taut = taut iso numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2ISO ) : 0; } /* d) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric isotopic numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); /* stereo-inv isotopic numb. non-taut = non-taut isotopic numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT | iiEq2ISO) : 0; } /* e) compare isotopic non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && pINChI_Aux_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV ); /* stereo-inv isotopic numbering non-taut = stereo-inv taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV) : 0; } /* f) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric inverted stereo numbering */ if ( !eq2taut ) { eq2taut = (Stereo_Taut = pINChI->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); /* stereo-inv isotopic numbering non-taut = stereo-inv non-taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; } /* g) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic inverted stereo numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV | EQL_NUM_ISO ); /* stereo-inv isotopic numbering non-taut = stereo-inv iso taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; } } else /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { /* compare isotopic tautomeric inverted stereo numbering to: * a) tautomeric numbering * b) tautomeric isotopic numbering * c) tautomeric inverted stereo numbering */ /* a) compare isotopic tautomeric inverted stereo numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); /* stereo-inv isotopic numbering (taut) = taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB ) : 0; } /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric isotopic numbering */ if ( !eq2taut ) { eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); /* stereo-inv isotopic numbering(taut) = isotopic taut numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2ISO ) : 0; } /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ if ( !eq2taut ) { eq2taut = (Stereo_Taut = pINChI->Stereo) && Stereo->nCompInv2Abs && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); /* stereo-inv isotopic numbering (taut) = taut stereo-inv numbering */ eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2INV ) : 0; } } if ( eq2taut ) { /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { /* current layer is different from previously printed layers of the current component */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && pINChI_Aux->nNumberOfAtoms && (Stereo = pINChI->StereoIsotopic) && Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } /* else isotopic inv stereo info is not present in pINChI */ } } if ( multPrevEquStr && pPrevEquStr ) { /* the new EqStr of the last item has not been printed; output it now */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } return tot_len; } /***************************************************************************/ int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bUseMulipliers) { int i, ii; INCHI_SORT *is, *is0; INChI *pINChI, *pINChI_Prev; int mult, eq2prev, bNext; if ( !(is0 = pINChISort) ) { return tot_len; } i = 0; pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; mult = 0; bNext = 0; for ( i++; i <= num_components; i ++ ) { pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; eq2prev = bUseMulipliers && pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, nStrLen-tot_len, bOverflow); } } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } return tot_len; } /***************************************************************************/ int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; int mult, eq2prev, bNext, bEqToTaut, tot_len_inp = tot_len; is = NULL; is2 = NULL; is0 = pINChISort; is20 = pINChISort2; i = 0; pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; pINChI_Taut_Prev = (0 <= (ii2=GET_II(OUT_T1,is20)))? is20->pINChI[ii2] : NULL; mult = 0; bNext = 0; bEqToTaut = 1; bEqToTaut = bEqToTaut && pINChI_Prev && pINChI_Taut_Prev && !pINChI_Taut_Prev->bDeleted && pINChI_Prev->szHillFormula && pINChI_Taut_Prev->szHillFormula && !strcmp(pINChI_Prev->szHillFormula, pINChI_Taut_Prev->szHillFormula); for ( i++; i <= num_components; i ++ ) { pINChI = (i < num_components && (is=is0+i, 0 <= (ii =GET_II(bOutType,is))))? is->pINChI[ii] : NULL; pINChI_Taut = (i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; if ( bEqToTaut && (pINChI || pINChI_Taut) ) { bEqToTaut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && pINChI->szHillFormula && pINChI_Taut->szHillFormula && !strcmp(pINChI->szHillFormula, pINChI_Taut->szHillFormula); } eq2prev = bUseMulipliers && pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, nStrLen-tot_len, bOverflow); } } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } if ( bEqToTaut ) { pStr[tot_len=tot_len_inp] = '\0'; } return tot_len; } /***************************************************************************/ int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) { int i, ii; INCHI_SORT *is, *is0; INChI *pINChI, *pINChI_Prev; int mult, eq2prev, bNext, tot_len_inp, nNumEmpty; if ( !(is0 = pINChISort) ) { return tot_len; } i = 0; pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; is = NULL; mult = 0; bNext = 0; tot_len_inp = tot_len; nNumEmpty = 0; for ( i++; i <= num_components; i ++ ) { pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; eq2prev = bUseMulipliers && pINChI && pINChI_Prev && pINChI->lenConnTable > 1 && pINChI_Prev->lenConnTable==pINChI->lenConnTable && !memcmp( pINChI_Prev->nConnTable, pINChI->nConnTable, pINChI_Prev->lenConnTable*sizeof(pINChI->nConnTable[0]) ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else if ( pINChI_Prev ) { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->lenConnTable > 1 ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeCtStringNew( pINChI_Prev->nConnTable, pINChI_Prev->lenConnTable, 0, NULL, pINChI_Prev->nNumberOfAtoms, pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow); } else { nNumEmpty ++; } } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { tot_len = tot_len_inp; pStr[tot_len] = '\0'; } return tot_len; } /***************************************************************************/ int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, int num_components, int bUseMulipliers) { int i, j, ii, len_H; INCHI_SORT *is, *is0; INChI *pINChI, *pINChI_Prev; int mult, eq2prev, bNext, bNotEmpty, nNumEmpty, tot_len_inp; nNumEmpty = 0; tot_len_inp = tot_len; is0 = pINChISort; is = NULL; i = 0; pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; mult = 0; bNext = 0; for ( i++; i <= num_components; i ++) { pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*========== compare to previous ============*/ eq2prev = bUseMulipliers && pINChI && pINChI_Prev && (pINChI->nNumberOfAtoms > 0 || pINChI->lenTautomer>1) && pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && (!pINChI_Prev->nNumberOfAtoms || !memcmp( pINChI_Prev->nNum_H, pINChI->nNum_H, pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H[0]) ) ) && !CompareTautNonIsoPartOfINChI( pINChI_Prev, pINChI ); if ( eq2prev && pINChI_Prev->lenTautomer <= 1 ) { /* make sure it is not empty */ eq2prev = 0; for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { if ( pINChI_Prev->nNum_H[j] ) { eq2prev = 1; break; } } } if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else if ( pINChI_Prev ) { /* delimiter */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } /* verify non-empty */ bNotEmpty = 0; if ( pINChI_Prev ) { bNotEmpty = (pINChI_Prev->lenTautomer > 1); if ( !bNotEmpty ) { for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { if ( pINChI_Prev->nNum_H[j] ) { bNotEmpty = 1; break; } } } } if ( bNotEmpty ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); /* H-atoms */ tot_len += (len_H = MakeHString( 0, pINChI_Prev->nNum_H, pINChI_Prev->nNumberOfAtoms, pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow )); /* tautomeric groups */ tot_len += MakeTautString( pINChI_Prev->nTautomer, pINChI_Prev->lenTautomer, (0!=len_H), pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { nNumEmpty ++; } } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { tot_len = tot_len_inp; pStr[tot_len] = '\0'; } return tot_len; } /***************************************************************************/ int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components, int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) { int i, ii, ii2; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; int nTotalCharge, nTotalCharge_Prev, nTotalCharge_Taut, nTotalCharge_Taut_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Taut = NULL; pINChI_Prev = NULL; pINChI_Taut_Prev = NULL; mult = 0; bNext = 0; is = NULL; is2 = NULL; is0 = pINChISort; is20 = bSecondNonTautPass? pINChISort2 : NULL; eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare sp3 to previous =====================*/ if ( bSecondNonTautPass ) { /* component that was output on the 1st pass */ pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; } /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ eq2taut = 0; if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { eq2taut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Taut = pINChI_Taut->nTotalCharge) && nTotalCharge == nTotalCharge_Taut; eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; } if ( eq2taut ) { /* we may be here only in case of the second (non-taut) pass */ /* current non-taut stereo has been found to be same as tautomeric */ if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { /* previous component exists; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); } } else if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { /* previous non-taut component exists only in taut list */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } } /* we have found pINChI->nTotalCharge same as in pINChI_Taut */ /* output this (current) equivalence as '*', that is, same as tautomeric */ /* that was printed on the 1st pass. */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ mult = 0; eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ } else if ( eq2tautPrev ) { /* at this point pINChI_Prev does not exist; however, pINChI */ /*might have been discovered and it is different from pINChI_Taut */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; } else { /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ /*================ compare sp3 to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Prev = pINChI_Prev->nTotalCharge) && nTotalCharge == nTotalCharge_Prev; if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { /* pINChI_Prev exists and has charge info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); } /* else charge is not present in pINChI_Prev */ } else if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { if ( nTotalCharge_Taut_Prev = pINChI_Taut_Prev->nTotalCharge ) { /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ /* and it has charge info. This info has already been printed in the main section */ /* tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); */ ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ } else { ; /* pINChI_Taut_Prev exists and has not sp3 info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Prev = pINChI; pINChI_Taut_Prev = pINChI_Taut; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } /***************************************************************************/ int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) { int i, j, ii, nNumEmpty; INCHI_SORT *is, *is0; INChI *pINChI, *pINChI_Prev; int mult, eq2prev, bNext, bNotEmpty, tot_len_inp; is = NULL; is0 = pINChISort; i = 0; pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; mult = 0; bNext = 0; nNumEmpty = 0; tot_len_inp = tot_len; for ( i++; i <= num_components; i ++ ) { /* only non-tautomeric representation of tautomeric */ pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; /*================ compare fixed H to previous =====================*/ eq2prev =bUseMulipliers && pINChI && pINChI_Prev && pINChI->nNumberOfAtoms > 0 && pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && !memcmp( pINChI_Prev->nNum_H_fixed, pINChI->nNum_H_fixed, pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H_fixed[0]) ); if ( eq2prev ) { /* make sure it is not empty */ eq2prev = 0; for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { if ( pINChI_Prev->nNum_H_fixed[j] ) { eq2prev = 1; break; } } } if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* print pINChI_Prev */ /* delimiter */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Prev ) { /* verify it is not empty */ bNotEmpty = 0; for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { if ( pINChI_Prev->nNum_H_fixed[j] ) { bNotEmpty = 1; break; } } if ( bNotEmpty ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); /* H-atoms-fixed */ tot_len += MakeHString( 0, pINChI_Prev->nNum_H_fixed, pINChI_Prev->nNumberOfAtoms, pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow ); } else { nNumEmpty ++; } } } pINChI_Prev = pINChI; mult = 0; /* we do not know whether the item is empty */ } if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { tot_len = tot_len_inp; pStr[tot_len] = '\0'; } return tot_len; } /***************************************************************************/ int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bSecondNonTautPass, int bOmitRepetitions) { int i, ii, ii2; INCHI_SORT *is, *is0 /*, *is2*/; INChI *pINChI, *pINChI_Taut=NULL; INChI_Aux *pINChI_Aux, *pINChI_Aux_Taut=NULL; int eq2taut, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; bNext = 0; /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; is = NULL; if ( !(is0 = pINChISort) ) { return tot_len; } for ( i = 0; i < num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ is=is0+i; pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; /*================ to compare to previously printed =====================*/ if ( bSecondNonTautPass ) { /* component that was printed on the 1st pass */ pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; } eq2taut = 0; /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms > 0 ) { /* compare non-tautomeric numbering to: * a) tautomeric numbering */ /* a) compare non-tautomeric numbering to tautomeric numbering */ if ( !eq2taut ) { eq2taut = pINChI_Taut && !pINChI_Taut->bDeleted && Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM, pINChI_Aux_Taut, EQL_NUM ); /* numbering non-taut = taut numbering */ eq2taut = eq2taut? ( iiNUMB | iitNONTAUT ) : 0; } } if ( eq2taut ) { /* we have found another (previously printed) layer of the current component equal to this layer */ /* output this (current) equivalence mark = EquString(eq2taut) */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { /* current layer is different from previously printed layers of the current component */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms ) { tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrd, pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } } if ( multPrevEquStr && pPrevEquStr ) { /* the new EqStr of the last item has not been printed; output it now */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } return tot_len; } /***************************************************************************/ int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) { int i, ii; INCHI_SORT *is, *is0; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; int mult, eq2prev, bNext; is0 = pINChISort; is = NULL; i = 0; pINChI_Aux_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI_Aux[ii] : NULL; mult = 0; bNext = 0; for ( i++; i <= num_components; i ++ ) { pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG, pINChI_Aux_Prev, EQL_EQU_TG ); if ( eq2prev ) { mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfTGroups && bHasEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } } pINChI_Aux_Prev = pINChI_Aux; mult = 0; /* we do not know whether the item is empty */ } return tot_len; } /***************************************************************************/ int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) { int i, ii; INCHI_SORT *is, *is0; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; int mult, eq2prev, bNext; pINChI_Aux_Prev = NULL; mult = 0; bNext = 0; is = NULL; is0 = pINChISort; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; /* check whether pINChI_Aux and pINChI_Aux_Prev have identical info */ eq2prev = bUseMulipliers && EqlOrigInfo( pINChI_Aux, pINChI_Aux_Prev ); if ( eq2prev ) { /* eq. info is same and non-trivial */ mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else if ( i ) { /* pINChI_Aux info is either different or trivial. Output pINChI_Aux_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { if ( bHasOrigInfo( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms ) ) { /* pINChI_Aux_Prev exists and has orig. info info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeCRVString( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Aux_Prev = pINChI_Aux; mult = 0; /* we do not know whether the item is empty */ } return tot_len; } /******************************************************************************************/ int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components) { int i, ii, ii2, ret; INCHI_SORT *is, *is2, *is0, *is20; INChI *pINChI, *pINChI_Taut; AT_NUMB *nTrans_n = NULL; AT_NUMB *nTrans_s = NULL; ret = 0; is0 = pINChISort; is20 = pINChISort2; /* pass 1: save new non-taut numbering */ for ( i = 0; i < num_components; i ++ ) { is=is0+i; is2=is20+i; pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; if ( pINChI && pINChI->nNumberOfAtoms > 0 && pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && /* different components save equal new ord. numbers: */ is->ord_number != is2->ord_number ) { if ( (nTrans_n && nTrans_s) || (nTrans_n = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_n[0]))) && (nTrans_s = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_s[0]))) ) { /* new ordering number for original non-tautomeric component number is->ord_number */ nTrans_n[is->ord_number] = /*nTrans_t[is2->ord_number] =*/ i+1; } } } if ( nTrans_n && nTrans_s ) { /* pass 2: get new taut numbering, retrieve new non-taut and save the transposition */ for ( i = 0; i < num_components; i ++ ) { is=is0+i; is2=is20+i; pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; if ( pINChI && pINChI->nNumberOfAtoms > 0 && pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && is->ord_number != is2->ord_number && nTrans_n[is2->ord_number] ) { /* nTrans_n[is2->ord_number] is new ordering number of the non-taut representation of the tautomeric component that has new ord number i+1 and orig ordering number is2->ord_number. Old numbers start from 0, new start from 1 */ /* n = nTrans_s[t]: taut component #t is in position #n of the non-taut representation */ nTrans_s[i+1] = nTrans_n[is2->ord_number]; } } *pTrans_n = nTrans_n; *pTrans_s = nTrans_s; ret = 1; } else { if ( nTrans_n ) { inchi_free( nTrans_n ); ret = -1; } if ( nTrans_s ) { inchi_free( nTrans_s ); ret = -1; } } return ret; } /******************************************************************************************/ int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, int *bOverflow, int TAUT_MODE, int num_components) { int i, k, len, j; if ( nTrans_n && nTrans_s ) { /* print the transposition, cycle after cycle */ for ( i = 1; i <= num_components; i ++ ) { if ( nTrans_s[i] ) { /* get one cycle of the transposition */ for ( j = i, len = 0; (k = nTrans_s[j]); j = k, len ++ ) { nTrans_n[len] = j; /* save the transposition */ nTrans_s[j] = 0; /* clear used element to avoid repetitions */ } /* print one cycle of the transposition */ tot_len += MakeDelim( "(", pStr + tot_len, nStrLen-tot_len, bOverflow); tot_len += MakeCtString( nTrans_n, len, 0, NULL, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); } } } if ( nTrans_n ) inchi_free( nTrans_n ); if ( nTrans_s ) inchi_free( nTrans_s ); return tot_len; } /***************************************************************************/ int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components) { int i, j, ii; INCHI_SORT *is, *is0; INChI_Stereo *Stereo; INChI *pINChI; is = NULL; is0 = pINChISort; for ( i = 0; !*bOverflow && i < num_components; i ++ ) { is=is0+i; pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; if ( pINChI && (Stereo = pINChI->Stereo) && (j=Stereo->nCompInv2Abs) ) { tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); } else { tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); } } return tot_len; } /***************************************************************************/ int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int num_components) { int i, j, ii; INCHI_SORT *is, *is0; INChI_Stereo *Stereo; INChI *pINChI; is = NULL; is0 = pINChISort; for ( i = 0; !*bOverflow && i < num_components; i ++ ) { is=is0+i; pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; if ( pINChI && (Stereo = pINChI->StereoIsotopic) && (j=Stereo->nCompInv2Abs) ) { tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); } else { tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); } } return tot_len; } /***************************************************************************/ int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers) { int i, ii; INCHI_SORT *is, *is0; INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; int mult, eq2prev, eq2taut, eq2tautPrev, bNext; const char *pPrevEquStr, *pCurrEquStr; int multPrevEquStr; pINChI_Aux = NULL; pINChI_Aux_Prev = NULL; mult = 0; bNext = 0; is = NULL; is0 = pINChISort; eq2taut = 0; /* equal to non-isotopic equivalence */ eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ pPrevEquStr = NULL; /*, *pCurrEquStr;*/ multPrevEquStr = 0; for ( i = 0; i <= num_components; i ++ ) { /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; /*================ compare iso non-taut equivalence info to non-iso taut ========*/ eq2taut = 0; if ( bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { /************************************************** * compare isotopic tautomeric equivalence to: * a) non-isotopic tautomeric */ /* compare isotopic t-group equivalence to non-isotopic */ eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux, EQL_EQU_TG ); /* equ taut-isotopic = tautomeric, same as for isotopic atom equivalence info*/ eq2taut = eq2taut? (iiEQU | iitISO) : 0; } if ( eq2taut ) { /* current isotopic t-group equivalence has been found to be same as non-isotopic */ if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { /* previous component exists */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { /* output previous component(s) equivalence since it was found to be non-trivial */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and does not have non-trivial t-group equivalence info */ } } /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicTGroupNumbers same as in pINChI_Aux->nConstitEquTGroupNumbers */ pCurrEquStr = EquString(eq2taut); if ( multPrevEquStr && pPrevEquStr ) { if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { multPrevEquStr ++; } else { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } } else { pPrevEquStr = pCurrEquStr; multPrevEquStr = 1; } pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev has already been output */ mult = 0; eq2tautPrev = 1; } else if ( eq2tautPrev ) { /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ /* might have been discovered and it may be different from non-isotopic */ if ( multPrevEquStr && pPrevEquStr ) { /* new EqStr is different; output it */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); pPrevEquStr = NULL; multPrevEquStr = 0; } eq2tautPrev = 0; pINChI_Aux_Prev = pINChI_Aux; mult = 0; } else { /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial isotopic t-group equivalence info */ eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_TG | EQL_EQU_ISO ); if ( eq2prev ) { /* eq. info is same and non-trivial */ mult ++; /* mult = (number of non-empty equal items)-1 */ continue; } else { /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ if ( bNext ++ ) { tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); } if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { /* pINChI_Aux_Prev exists and has equivalence info */ tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); } else { ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ } } #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) else { int stop = 1; /* */ } #endif } pINChI_Aux_Prev = pINChI_Aux; mult = 0; /* we do not know whether the item is empty */ } } return tot_len; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiqueu.c000066400000000000000000001677171271037650300235540ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include /*^^^ */ #include "mode.h" #include "inpdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichi_bns.h" /*^^^ */ /*******************************************************************/ #if ( FIND_RING_SYSTEMS == 1 ) /* { */ /* local prototypes */ int are_alt_bonds( U_CHAR *bonds, int len ); int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos ); int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint); /****************************************** * * Tautomerism in 5- and 6-member rings * ******************************************/ const int NONE = (AT_RANK)~0; /* 1,5 Tautomerism in 6-member alt ring: /=\ /==\ HN C=O <-> N C-OH \=/ \\-// 1,2 Tautomerism in 5-member ring: HN--X N==X | \\ | \ | Z <-> | Z | / | // N==Y HN--Y 1,4 tautomerism in 7-member ring /C==D //C-D O=B \ HO-B \\ | E <-> | E HO-A // O=A / \\G-F \\G-F 1,4 tautomerism in 5-member ring O=B--C O-B==C | \\ | \ | D <-> | D | / | // HO-A==E HO=A--E */ typedef int CHECK_DFS_RING( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat ); CHECK_DFS_RING Check7MembTautRing; CHECK_DFS_RING Check6MembTautRing; CHECK_DFS_RING Check5MembTautRing; #if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ /* DFS simple alt path for 1,5 tautomerism, post v.1 feature */ typedef int CHECK_DFS_PATH( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); CHECK_DFS_PATH Check15TautPath; CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint; int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, int nCycleLen, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); #define BOND_WRONG 64 #define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \ ((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \ BOND_WRONG); #endif /* TAUT_15_NON_RING */ int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, int nCycleLen, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); #if ( REPLACE_ALT_WITH_TAUT == 1 ) #define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS ) #else #define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE ) #endif int bIsCenterPointStrict( inp_ATOM *atom, int iat ) { if ( atom[iat].valence == atom[iat].chem_bonds_valence ) { int endpoint_valence = get_endpoint_valence(atom[iat].el_number); if ( endpoint_valence && (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */ (atom[iat].num_H || atom[iat].charge == -1) || !atom[iat].charge && atom[iat].c_point) ) { return 1; /* may appear to be tautomeric or chargable (this increases chem_bonds_valence), should be explored */ } return 0; } if (atom[iat].valence+1 == atom[iat].chem_bonds_valence && is_centerpoint_elem_strict( atom[iat].el_number ) ) { return 1; } return 0; } /********************************************************************************/ int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nRet; *pnNumEndPoint = 0; *pnNumBondPos = 0; if ( nMaxLenDfsPath <= 7 ) { return -1; /* path is too short */ } nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 7, nDfsPathPos, DfsPath, Check7MembTautRing, bIsCenterPointStrict, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); return nRet; } /********************************************************************************/ int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nRet; *pnNumEndPoint = 0; *pnNumBondPos = 0; if ( nMaxLenDfsPath <= 5 ) { return -1; /* path is too short */ } nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5, nDfsPathPos, DfsPath, Check7MembTautRing, bIsCenterPointStrict, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); return nRet; } /********************************************************************************/ int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nRet; *pnNumEndPoint = 0; *pnNumBondPos = 0; if ( nMaxLenDfsPath <= 5 ) { return -1; /* path is too short */ } nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, -1, -1, 5, nDfsPathPos, DfsPath, Check5MembTautRing, bIsCenterPointStrict, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); return nRet; } /********************************************************************************/ int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nRet; *pnNumEndPoint = 0; *pnNumBondPos = 0; if ( nMaxLenDfsPath <= 7 ) { return -1; /* path is too short */ } nRet = DFS_FindTautInARing( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, -1/*nStartAtomNeighborNeighbor*/, 6 /* nCycleLen*/, nDfsPathPos, DfsPath, Check6MembTautRing, bIsCenterPointStrict, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); return nRet; } #if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ /********************************************************************************/ int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nRet; *pnNumEndPoint = 0; *pnNumBondPos = 0; if ( nMaxLenDfsPath <= 7 ) { return -1; /* path is too short */ } nRet = DFS_FindTautAltPath( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, -1/*nStartAtomNeighborNeighbor*/, 4 /* nCycleLen*/, nDfsPathPos, DfsPath, Check15TautPath, Check15TautPathCenterpoint, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); return nRet; } #endif /********************************************************************************/ /* DFS version */ #define MAX_DFS_DEPTH 16 /********************************************************************************/ int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, int nCycleLen, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { /* Depth First Search */ /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ AT_RANK nMinLenDfsPath; int j, cur_at, nxt_at, prv_at; int nLenDfsPath, nNumFound, ret; AT_RANK nRingSystem; int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; nLenDfsPath=0; nNumFound=0; nCycleLen --; DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ nRingSystem = atom[nStartAtom].nRingSystem; nMinLenDfsPath = 0; if ( nStartAtomNeighbor2 >= 0 ) { nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; } /* add the first neighbor to the 2nd tree position if required */ if ( nStartAtomNeighbor >= 0 ) { j = nStartAtomNeighbor; prv_at = cur_at; cur_at = atom[prv_at].neighbor[j]; DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); #endif DfsPath[nLenDfsPath].bond_pos = j; nLenDfsPath ++; DfsPath[nLenDfsPath].at_no = cur_at; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; nDfsPathPos[cur_at] = nLenDfsPath+1; nMinLenDfsPath ++; if ( nStartAtomNeighborNeighbor >= 0 ) { nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; } } /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ while ( nLenDfsPath >= nMinLenDfsPath ) { j = ++DfsPath[nLenDfsPath].bond_pos; if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); #endif nxt_at = (int)atom[cur_at].neighbor[j]; if ( nxt_at == nDoNotTouchAtom1 || nxt_at == nDoNotTouchAtom2 ) { ; /* ignore */ } else if ( nDfsPathPos[nxt_at] ) { /* found a ring closure or a step backwards */ if ( 1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen ) { /* we have found the cycle; check it */ ret = (*CheckDfsRing)( atom, DfsPath, nLenDfsPath, nStartAtomNeighbor, nStartAtomNeighbor2, nStartAtomNeighborNeighbor, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ); if ( ret < 0 ) { nNumFound = ret; goto clear_path; } nNumFound += ret; } } else if ( !(*CheckCenterPoint)( atom, nxt_at ) ) { ; /* cannot advance to a non-centerpoint; ignore */ } else if ( nLenDfsPath < nCycleLen ) { /* advance */ nLenDfsPath ++; cur_at = nxt_at; DfsPath[nLenDfsPath].at_no = cur_at; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ } } else { /* retract */ nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; nLenDfsPath --; } } clear_path: while ( 0 <= nLenDfsPath ) { nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; nLenDfsPath --; } return nNumFound; } #if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ /********************************************************************************/ int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, int nCycleLen, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPointPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { /* Naive Depth First Search: same atom may be approached along different alt paths */ /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ AT_RANK nMinLenDfsPath; int j, cur_at, nxt_at, prv_at; int nLenDfsPath, nNumFound, ret; AT_RANK nRingSystem; int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; nLenDfsPath=0; nNumFound=0; nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */ DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */ nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ nRingSystem = atom[nStartAtom].nRingSystem; nMinLenDfsPath = 0; /* allow to restart from nStartAtom */ if ( nStartAtomNeighbor2 >= 0 ) { nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; } /* add the first neighbor to the 2nd tree position if required */ if ( nStartAtomNeighbor >= 0 ) { j = nStartAtomNeighbor; prv_at = cur_at; cur_at = atom[prv_at].neighbor[j]; DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); #endif DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ nLenDfsPath ++; DfsPath[nLenDfsPath].at_no = cur_at; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ nMinLenDfsPath ++; /* allow to restart from nStartAtom's neighbor */ if ( nStartAtomNeighborNeighbor >= 0 ) { nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; } } /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ while ( nLenDfsPath >= nMinLenDfsPath ) { j = ++DfsPath[nLenDfsPath].bond_pos; if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); #endif nxt_at = (int)atom[cur_at].neighbor[j]; if ( nxt_at == nDoNotTouchAtom1 || /* forbidden */ nxt_at == nDoNotTouchAtom2 || /* forbidden */ nDfsPathPos[nxt_at] || /* ring closure */ nLenDfsPath && nxt_at == (int)DfsPath[nLenDfsPath-1].at_no /* step backwards */ ) { ; /* ignore nxt_at */ } else if ( nLenDfsPath == nCycleLen && /* 1,5 and at least one of the endpoints is not in a ring */ (atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1) && /* we have found the alt path of the requested length; check it */ /* calling Check15TautPath() */ (ret = (*CheckDfsPath)( atom, DfsPath, nLenDfsPath, j, nStartAtomNeighbor, nStartAtomNeighbor2, nStartAtomNeighborNeighbor, EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, pnNumEndPoint, pnNumBondPos, pBNS, pBD, num_atoms ) ) ) { if ( ret < 0 ) { nNumFound = ret; goto clear_path; /* program error */ } nNumFound += ret; /* success */ } else /* calling Check15TautPathCenterpoint() */ if ( !(*CheckCenterPointPath)( atom, DfsPath, nLenDfsPath, j, pBNS, pBD, num_atoms ) ) { ; /* cannot advance to a non-centerpoint; ignore */ } else if ( nLenDfsPath < nCycleLen ) { /* advance */ nLenDfsPath ++; cur_at = nxt_at; DfsPath[nLenDfsPath].at_no = cur_at; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ } } else { /* retract */ nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; nLenDfsPath --; } } clear_path: while ( 0 <= nLenDfsPath ) { nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; nLenDfsPath --; } return nNumFound; } #endif /* TAUT_15_NON_RING */ /******************************************* * check if bonds are alternating */ int are_alt_bonds( U_CHAR *bonds, int len ) { U_CHAR next_bond; int i, bAnyBond, bTautBondPresent=BOND_ALTERN; if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) { return 0; } next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; if ( bonds[0] == BOND_TAUTOM ) { bTautBondPresent= BOND_TAUTOM; next_bond = 0; } else { next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; } for ( i = 1; i < len; i ++ ) { if ( bonds[i] == BOND_TAUTOM ) { bTautBondPresent = BOND_TAUTOM; bAnyBond = 1; } else { bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS); } if ( next_bond ) { if ( bonds[i] == next_bond || bAnyBond ) { next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; continue; } return 0; } else if ( bonds[i] == BOND_SINGLE ) { next_bond = BOND_DOUBLE; continue; } else if ( bonds[i] == BOND_DOUBLE ) { next_bond = BOND_SINGLE; continue; } else if ( !bAnyBond ) { return 0; } } return !next_bond? bTautBondPresent : (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */ } /********************************************************************************/ int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos ) { int i, j, k, cur_at, nxt_at; /* add opposite direction bonds to BondPosTmp */ for ( j = 0; j < nNumBondPosTmp; j += 2 ) { cur_at = BondPosTmp[j].nAtomNumber; nxt_at = atom[cur_at].neighbor[(int)BondPosTmp[j].neighbor_index]; for ( k = 0; k < atom[nxt_at].valence; k ++ ) { if ( cur_at == atom[nxt_at].neighbor[k] ) { BondPosTmp[j+1].nAtomNumber = nxt_at; BondPosTmp[j+1].neighbor_index = k; break; } } } /* add new tautomeric bonds */ for ( j = 0; j < nNumBondPosTmp; j += 2 ) { for ( i = 0; i < nNumBondPos; i ++ ) { if ( BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber && BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index || BondPos[i].nAtomNumber == BondPosTmp[j+1].nAtomNumber && BondPos[i].neighbor_index == BondPosTmp[j+1].neighbor_index ) { break; /* bond has already been added */ } } if ( i == nNumBondPos ) { if ( i > nMaxNumBondPos ) { return -1; /* overflow */ } BondPos[nNumBondPos ++] = BondPosTmp[j]; } } return nNumBondPos; } /********************************************************************************/ int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint) { int i, j; /* add new endpoints */ for ( j = 0; j < nNumNewEndPoint; j ++ ) { for ( i = 0; i < nNumEndPoint; i ++ ) { if ( EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber ) { break; } } if ( i == nNumEndPoint ) { if ( i > nMaxNumEndPoint ) { return -1; /* overflow */ } EndPoint[nNumEndPoint ++] = EndPointTmp[j]; } } return nNumEndPoint; } /********************************************************************************/ /* 1,4 tautomerism in 7-member ring /C==D //C-D A=DfsPath[0].at_no O=B \ HO-B \\ B=DfsPath[1].at_no | E <-> | E nStartAtomNeighbor2: from A to HO HO-A // O=A / nStartAtomNeighborNeighbor: from B to O \\G-F \\G-F 1,4 tautomerism in 5-member ring O=B--C O-B==C | \\ | \ | D <-> | D | / | // HO-A==E HO=A--E */ /********************************************************************************/ int Check7MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { #define PATH_LEN 8 int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp; int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at; int ret; U_CHAR path_bonds[PATH_LEN+1], bond_type; T_ENDPOINT EndPointTmp[2]; T_BONDPOS BondPosTmp[2*PATH_LEN]; ENDPOINT_INFO eif1, eif2; int nErr=0; if ( nLenDfsPath + 2 > PATH_LEN ) { return -1; /* too long path */ } if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) { return -1; /* wrong call */ } nNumBondPos = *pnNumBondPos; nNumEndPoint = *pnNumEndPoint; nNumBondPosTmp = 0; nNumEndPointTmp = 0; ret = 0; o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor]; o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2]; /* nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H; nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H; */ if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) || !nGetEndpointInfo( atom, o2_at, &eif2 ) ) { return 0; } /* save endpoints */ for ( j = 0; j < 2; j ++ ) { endpoint = j? o2_at : o1_at; if ( !atom[endpoint].endpoint ) { AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); /* nMobile = j? nMobile2 : nMobile1; } else { nMobile = 0; } if ( nMobile ) { EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); EndPointTmp[nNumEndPointTmp].num[0] = nMobile; for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; } */ } else { memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); } EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; EndPointTmp[nNumEndPointTmp].nEquNumber = 0; nNumEndPointTmp ++; } /* extract bonds */ k = (int)DfsPath[1].at_no; bond_type = (atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,k,nStartAtomNeighborNeighbor,bond_type); #endif path_bonds[0] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = k; BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor; nNumBondPosTmp += 2; } for ( i = 1; i <= nLenDfsPath; i ++ ) { bond_type = DfsPath[i].bond_type; path_bonds[i] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; nNumBondPosTmp += 2; } } bond_type = (atom[(int)DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,(int)DfsPath[0].at_no,nStartAtomNeighbor2,bond_type); #endif path_bonds[i++] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no; BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2; nNumBondPosTmp += 2; } if ( !are_alt_bonds( path_bonds, i ) ) { return 0; } /* path_bonds is from at_n1 to at_n2 */ if ( !(j=are_alt_bonds( path_bonds, i )) ) { return 0; } /* j is a bond type of the last bond to o2_at, the first bond from o1_at is 2-j if j=1 or 2 */ /* single bond at o2_at: it should have a mobile atom, o1_at should not */ if ( j == BOND_SINGLE && (!atom[o2_at].endpoint && !eif2.cDonor || !atom[o1_at].endpoint && !eif1.cAcceptor) || /* double bond at o2_at: it should not have a mobile atom, o1_at should */ j == BOND_DOUBLE && (!atom[o2_at].endpoint && !eif2.cAcceptor || !atom[o1_at].endpoint && !eif1.cDonor) ) { return 0; /* bond pattern does not fit */ } nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { *pnNumBondPos = nNumBondPos ; *pnNumEndPoint = nNumEndPoint ; } } if ( ret ) { /* finally check whether the bonds allow moving the hydrogens */ if ( (atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint) ) { nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM ); if ( nErr <= 0 ) return nErr; } } return ret; #undef PATH_LEN } /********************************************************************************/ /* 1,5 Tautomerism in 6-member alt ring: /=\ /==\ N = DfsPath[0].at_no HN C=O <-> N C-OH C = DfsPath[3].at_no \=/ \\-// */ /********************************************************************************/ /* check if a tautomeric 6-member ring has been found */ int Check6MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { #define PATH_LEN 4 int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint; int nNumEndPointTmp, nNumBondPosTmp, o_at, ret; /* int num_taut_endpoints, num_H; */ int middle_pos; int nMobile, endpoint, endpoint_valence, chem_bonds_valence; int nMobile1, endpoint_valence1; /* o_at */ int nMobile2, endpoint_valence2; /* n_at */ int nxt_at; int n_at; U_CHAR path_bonds[2][PATH_LEN+1], bond_type; T_ENDPOINT EndPointTmp[2]; T_BONDPOS BondPosTmp[4*PATH_LEN]; ENDPOINT_INFO eif1, eif2; if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) return -1; /* wrong call */ if ( nLenDfsPath != 5 ) return -1; /* wrong call */ nNumBondPos = *pnNumBondPos; nNumEndPoint = *pnNumEndPoint; nNumBondPosTmp = 0; nNumEndPointTmp = 0; ret = 0; n_at = (int)DfsPath[0].at_no; /* -N= or -NH- atom */ nxt_at = DfsPath[middle_pos = (nLenDfsPath+1)/2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */ if ( atom[nxt_at].valence != 3 #if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) || !atom[nxt_at].bCutVertex #endif ) { return 0; } for ( i = 0; i < atom[nxt_at].valence; i ++ ) { o_at = atom[nxt_at].neighbor[i]; if ( o_at != DfsPath[middle_pos-1].at_no && o_at != DfsPath[middle_pos+1].at_no ) { break; /* >=O or />-OH has been found */ } } if ( i == atom[nxt_at].valence ) { return 0; /* no neighboring atom >=O or />-OH */ } bond_type = (atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,nxt_at,i,bond_type); #endif if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TAUTOM && bond_type != BOND_ALT12NS && bond_type != BOND_ALTERN ) { return 0; } /* check whether the two atoms already belong to one tautomeric group */ #if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) if ( atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint ) { return 0; } #endif /* check =O valence; must be 2 for O, S, Se or 3 for N */ if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) ) { return 0; /* n_at has been checked in MarkTautomerGroups(...) */ } /* if ( 2 != endpoint_valence1 ) return 0; // accept only O, S, Se */ /* check hydrogens/endpoints */ nMobile1 = atom[o_at].num_H + (atom[o_at].charge==-1); if ( bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint ) return 0; /* not needed since nGetEndpointInfo returned non-zero if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 ) return 0; */ if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) { return 0; /* should not happen here */ } nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1); nMobile = 0; /* can mobile group move from o_at to n_at? */ nMobile += (atom[o_at].endpoint || eif1.cDonor) && /* from o_at */ bond_type != BOND_DOUBLE && ( atom[n_at].endpoint || /* to n_at */ eif2.cNeutralBondsValence > atom[n_at].valence ); /* can mobile group move from n_at to o_at? */ nMobile += (atom[n_at].endpoint || eif2.cDonor) && /* from n_at */ (atom[o_at].endpoint || /* to o_at */ eif1.cNeutralBondsValence > atom[o_at].valence ) && bond_type != BOND_SINGLE; if ( !nMobile ) return 0; /* num_H = atom[n_at].num_H + atom[o_at].num_H; num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) { return 0; } */ /* extract -OH bond */ nNumBondPosTmp = 0; path_bonds[0][0] = path_bonds[1][0] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */ BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */ nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ } /* extract other bonds */ /* path_bonds[] contents: O OH OH || | | / \ // \ / \\ || || <--> | || <--> || | \ / \\ / \ // NH N N path[0]: O=NH-=- OH-N... OH.N... path[1] O=NH-=- OH-N... OH.N... bonds are all bonds all bonds single and are either are either double alt or taut alt or taut */ for ( j = 0; j < middle_pos; j ++ ) { for ( i = 0; i < 2; i ++ ) { /* k = i? j : middle_pos-1-j; */ k = i? middle_pos+j : middle_pos-1-j; /* i=0: from O neighbor i=0: down to N, i=1: up to N */ bond_type = DfsPath[k].bond_type; path_bonds[i][j+1] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ } } } if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) { return 0; } /* finally check whether the bonds allow moving the hydrogens */ if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) { int nErr; nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM ); if ( nErr <= 0 ) return nErr; } /* save endpoints */ for ( j = 0; j < 2; j ++ ) { endpoint = j? n_at : /* =N- 2 */ o_at; /* -OH 1 */ if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ endpoint_valence = j? endpoint_valence2 : endpoint_valence1; chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ nMobile = j? nMobile2 : nMobile1; /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ if ( nMobile + chem_bonds_valence != endpoint_valence ) return 0; /* abnormal endpoint valence; ignore. */ AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); /* EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); EndPointTmp[nNumEndPointTmp].num[0] = nMobile; for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; } */ } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); } EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; EndPointTmp[nNumEndPointTmp].nEquNumber = 0; nNumEndPointTmp ++; } /* add collected tautomeric bonds and endpoints to the input/output data */ nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { *pnNumBondPos = nNumBondPos ; *pnNumEndPoint = nNumEndPoint ; } } return ret; #undef PATH_LEN } #if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ /******************************************************************************** Check (1,5) taut alt path centerpoint (unfinished) [add path checking] *********************************************************************************/ int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh]; /* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */ return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at ); } /********************************************************************************/ /* 1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case] AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism 0 1 2 3 4 as well as A or E may be previously detected such a carbon ^ nxt_at | +-- = nLenDfsPath */ /********************************************************************************/ /* check if 1,5 tautomeric path has been found */ int Check15TautPath( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { #define PATH_LEN 4 int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/; int nNumEndPointTmp, nNumBondPosTmp, ret; /* int num_taut_endpoints, num_H; */ int nMobile, endpoint, endpoint_valence, chem_bonds_valence; int nMobile1, endpoint_valence1; /* start atom, at1 */ int nMobile2, endpoint_valence2; /* end atom, at2 */ /*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */ /*int nxt_at;*/ int alt_bonds[2]; U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type; T_ENDPOINT EndPointTmp[2]; T_BONDPOS BondPosTmp[4*PATH_LEN]; ENDPOINT_INFO eif1, eif2/*, eif3*/; if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) return -1; /* wrong call */ if ( nLenDfsPath != 3 ) return -1; /* wrong call */ nNumBondPos = *pnNumBondPos; nNumEndPoint = *pnNumEndPoint; nNumBondPosTmp = 0; nNumEndPointTmp = 0; ret = 0; /*-------add the last atom, nLenDfsPath=4 --*/ j = jNxtNeigh; prv_at = DfsPath[nLenDfsPath].at_no; cur_at = atom[prv_at].neighbor[j]; DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); #if ( FIX_BOND23_IN_TAUT == 1 ) DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); #endif DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ nLenDfsPath ++; DfsPath[nLenDfsPath].at_no = cur_at; DfsPath[nLenDfsPath].bond_type = 0; DfsPath[nLenDfsPath].bond_pos = -1; /*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */ /*------------------------------------------*/ at1 = (int)DfsPath[0].at_no; at2 = (int)DfsPath[nLenDfsPath].at_no; /*at3 = (int)DfsPath[2].at_no;*/ if ( atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint ) { /* start & end already belong to the same taut group */ goto exit_function; /* nothing to do */ } /* check bond types along alt path */ alt_bonds[0] = alt_bonds[1] = 0; for( i = 0; i < nLenDfsPath; i ++ ) { alt_bonds[i%2] |= IS_ALT_OR_DBLBOND(DfsPath[i].bond_type); } if ( (alt_bonds[0] & alt_bonds[1] & (BOND_SINGLE | BOND_DOUBLE)) || (alt_bonds[0] & BOND_WRONG) || (alt_bonds[1] & BOND_WRONG ) ) { goto exit_function; /* incompatible with alt path or wrong bonds */\ } /* check possibly tautomeric endpoints at the ends */ endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 ); endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 ); #ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */ if ( !endpoint_valence1 && !atom[at1].endpoint || !endpoint_valence2 && !atom[at2].endpoint ) goto exit_function; /* at least one of the end atoms cannot be an endpoint */ #endif if ( !endpoint_valence1 || !endpoint_valence2 ) goto exit_function; /* require both endpoints be heteroatoms */ /* check hydrogens/endpoints */ nMobile1 = atom[at1].num_H + (atom[at1].charge==-1); if ( !atom[at1].endpoint ) { if ( (alt_bonds[0] & BOND_SINGLE) && !eif1.cDonor ) goto exit_function; if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor ) goto exit_function; } nMobile2 = atom[at2].num_H + (atom[at2].charge==-1); if ( !atom[at2].endpoint ) { if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor ) goto exit_function; if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor ) goto exit_function; } nMobile = 0; /* can mobile group move from at1=o_at to at2=n_at? */ nMobile += (atom[at1].endpoint || eif1.cDonor) && /* from o_at */ !(alt_bonds[0] & BOND_DOUBLE) && ( atom[at2].endpoint || /* to n_at */ eif2.cNeutralBondsValence > atom[at2].valence ); /* can mobile group move from at2=n_at to at1=o_at? */ nMobile += (atom[at2].endpoint || eif2.cDonor) && /* from n_at */ !(alt_bonds[1] & BOND_DOUBLE) && ( atom[at1].endpoint || /* to o_at */ eif1.cNeutralBondsValence > atom[at1].valence ); if ( !nMobile ) goto exit_function; /* check whether the bonds allow moving the hydrogens between at1 and at2 */ if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) { int nErr; nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM ); if ( nErr <= 0 ) { ret = nErr; goto exit_function; } } /* save tautomeric bonds */ nNumBondPosTmp = 0; for ( k = 0; k < nLenDfsPath; k ++ ) { bond_type = DfsPath[k].bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */ } } /* save endpoints */ for ( j = 0; j < 2; j ++ ) { endpoint = j? at2 : at1; if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ endpoint_valence = j? endpoint_valence2 : endpoint_valence1; chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ nMobile = j? nMobile2 : nMobile1; /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ if ( nMobile + chem_bonds_valence != endpoint_valence ) goto exit_function; /* abnormal endpoint valence; ignore. */ AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); } EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; EndPointTmp[nNumEndPointTmp].nEquNumber = 0; nNumEndPointTmp ++; } /* add collected tautomeric bonds and endpoints to the input/output data */ nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { *pnNumBondPos = nNumBondPos ; *pnNumEndPoint = nNumEndPoint ; } } exit_function: /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/ return ret; #undef PATH_LEN } #endif /* TAUT_15_NON_RING */ /********************************************************************************/ /* 1,4 tautomerism in 5-member ring O=N2-C O-N2=C N1 = DfsPath[0].at_no | \\ | \ N2 = DfsPath[1].at_no | D <-> | D | / | // HO-N1=E HO=N1-E */ /********************************************************************************/ /* check if a tautomeric 5-member ring (pyrazole derivatives) has been found */ int Check5MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ) { #define PATH_LEN 4 int i, j, /*m,*/ nMobile, nMobile1, nMobile2; int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret; int endpoint; int n1_at = (int)DfsPath[0].at_no; int n2_at = (int)DfsPath[1].at_no; U_CHAR path_bonds[PATH_LEN+1], bond_type; T_ENDPOINT EndPointTmp[2]; T_BONDPOS BondPosTmp[2*PATH_LEN]; ENDPOINT_INFO eif1, eif2; /* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */ /* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */ /* check hydrogens/endpoints */ if ( nLenDfsPath != 4 ) { return 0; /* program error */ } if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) return 0; /* program error: wrong call */ nNumBondPos = *pnNumBondPos; nNumEndPoint = *pnNumEndPoint; nNumEndPointTmp = 0; nNumBondPosTmp = 0; ret = 0; if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) || !nGetEndpointInfo( atom, n2_at, &eif2 ) ) { return 0; } nMobile1 = atom[n1_at].num_H + (atom[n1_at].charge==-1); nMobile2 = atom[n2_at].num_H + (atom[n2_at].charge==-1); nMobile = nMobile1 + nMobile2; num_taut_endpoints = (0!=atom[n1_at].endpoint) + (0!=atom[n2_at].endpoint); /* if both N atoms already are endpoints */ /* if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) { return 0; } */ if ( num_taut_endpoints == 0 && nMobile != 1 ) { return 0; } /* finally check whether the bonds allow moving the hydrogens */ if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) { int nErr; nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM ); if ( nErr <= 0 ) return nErr; } /* save endpoints */ for ( j = 0; j < 2; j ++ ) { endpoint = j? n1_at : n2_at; if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; } else { nMobile = 0; } if ( nMobile ) { */ AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); /* EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); EndPointTmp[nNumEndPointTmp].num[0] = nMobile; for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; } */ } else { memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); } EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; EndPointTmp[nNumEndPointTmp].nEquNumber = 0; nNumEndPointTmp ++; } /* extract bonds */ nNumBondPosTmp = 0; for ( i = 1; i <= nLenDfsPath; i ++ ) { bond_type = DfsPath[i].bond_type; path_bonds[i-1] = bond_type; if ( REPLACE_THE_BOND( bond_type ) ) { BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; nNumBondPosTmp += 2; } } /* path_bonds is from at_n2 to at_n1 */ if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) { return 0; } /* i is a bond type of the last bond to at_n1, the first bond from at_n2 is 2-i if i=1 or 2 */ /* single bond at n1_at: it should have a mobile atom, n2_at should not */ if ( i == BOND_SINGLE && (!atom[n1_at].endpoint && !eif1.cDonor || !atom[n2_at].endpoint && !eif2.cAcceptor ) || /* double bond at n1_at: it should not have a mobile atom, n2_at should */ i == BOND_DOUBLE && (!atom[n1_at].endpoint && !eif1.cAcceptor || !atom[n2_at].endpoint && !eif2.cDonor) ) { return 0; /* bond pattern does not fit */ } nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { *pnNumBondPos = nNumBondPos ; *pnNumEndPoint = nNumEndPoint ; } } return ret; #undef PATH_LEN } #endif /* } */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiread.c000066400000000000000000013127301271037650300234740ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include /* #define CHECK_WIN32_VC_HEAP */ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichicomp.h" #include "ichi.h" #include "ichitime.h" #include "util.h" #include "strutil.h" #include "ichi_io.h" /* reverse InChI */ #include "ichimain.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichister.h" #include "strutil.h" #include "ichisize.h" #include "ichiring.h" #include "ichinorm.h" #include "ichierr.h" #include "ichirvrs.h" /*^^^ */ #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_STANDALONE) ) #include "inchi_api.h" #endif /*^^^ */ typedef struct tagLine { char *str; int len; int len_alloc; int c; } SEGM_LINE; #define SEGM_LINE_ADD 128 typedef struct tagOneLinkedBond { AT_NUMB neigh; /* canonical number of a neighbor */ AT_NUMB prev; /* position of the previous neighbor in the list */ } ONE_LINKED_BOND; typedef struct tagLinkedBonds { ONE_LINKED_BOND *pBond; int len; int len_alloc; }LINKED_BONDS; #define LINKED_BOND_ADD 128 typedef enum tagModeProtonIsoExchgH { MODE_PIXH_UNDEFINED, /* 0 */ MODE_PIXH_ADD_TO_FIRST, /* 1 */ MODE_PIXH_ADD_TO_EACH, /* 2 */ MODE_PIXH_ADD_A_PIXH_COMPONENT, /* 3 */ MODE_PIXH_KEEP_TOTALS /* 4 */ } MODE_PIXH; /* local prototypes */ static int GetInChIFormulaNumH(INChI *pInChI, int *nNumH); static int GetInChINumH(INChI *pInChI, int *nNumH); static int GetInChIIsoH(INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES]); static int getInChIChar(INCHI_IOSTREAM *pInp); static int AddInChIChar(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); static int AddLinkedBond(AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB); static int bInChIHasReconnectedMetal(INChI *pInChI); static int SetProtonsAndXchgIsoH(int bInChI2Structure, int bReqSplitOutputInChI, int bReqProtonsForEachComponent, int bReqNonTaut, int bReqStereo, int num_components[INCHI_NUM], MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], InpInChI *OneInput); #if ( FIX_DALKE_BUGS == 1 ) static int SetHillFormFromInChI(InpInChI *OneInput); #endif static int nGetInChISegment(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); static int CopySegment(INChI *pInChITo, INChI *pInChIFrom, int StereoType, int bIsotopicTo, int bIsotopicFrom); static int nFillOutProtonMobileH(INChI *pInChI); static int nProtonCopyIsotopicInfo(INChI *pInChI_to, INChI *pInChI_from); static int CopyAtomNumbers(INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom); static int ParseSegmentFormula(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[]); static int ParseSegmentConnections(const char *str, int bMobileH, INChI **pInpInChI, int *pnNumComponents, int *pbAbc); static int ParseSegmentMobileH(const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[], int *pbAbc); static int ParseSegmentCharge(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[]); static int ParseSegmentProtons(const char *str, int bMobileH, REM_PROTONS nNumProtons[], int nNumComponents[]); static int ParseSegmentSp2(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); static int ParseSegmentSp3(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); static int ParseSegmentSp3m(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[], int state); static int bIsSp3LayerNotEmpty(INChI *pInpInChI[], int bMobileH, int bIso, int nNumComponents); static int ParseSegmentSp3s(const char *str, int bMobileH, INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state); static int ParseSegmentIsoAtoms(const char *str, int bMobileH, INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); static int ParseSegmentIsoExchgH(const char *str, int bMobileH, REM_PROTONS nNumProtons[], int nNumComponents[], int state, int *pbAbc); static int ParseSegmentPerm(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc); #if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) static int bIsoMayBeArranged(int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI); #endif static int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, char **pStr, int *pState, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], int s[INCHI_NUM][TAUT_NUM][2], int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, char **pStr, int *pState, int *nErr, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], int s[INCHI_NUM][TAUT_NUM][2], int bReadCoord, int bInchi2Struct, INCHI_MODE nMode, int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); static int ReadInChICoord(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, int *pState, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM]); static int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, InpInChI *OneInput, int num_components[INCHI_NUM], MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], long num_inp, unsigned char save_opt_bits); static int ParseAuxSegmentVersion(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentNumbers(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc); static int ParseAuxSegmentAtomEqu(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentGroupEqu(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentSp3Inv(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentSp3InvNumbers(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentReverseCRV(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentReverseAtoms(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentReverseBonds(const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state); static int ParseAuxSegmentReverseXYZ(const char *str, int bMobileH, XYZ_COORD **ppXYZ, INChI *pInpInChI[], int ppnNumComponents[], int state); static int AddAuxSegmentCoord(int nRet, XYZ_COORD *pXYZ, int nLenXYZ, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM]); static const char *getInchiStateReadErr(int stat); static const char *getInchiErrName(int nErr); #define SEG_END '/' /* the following 2 definitions are used to allow tab-delimited InChI input - 2008-11-17 DT */ #define INCHI_INP_EOL(X) ((X)=='\n' || (X)=='\r' || (X)=='\t') /*#define INCHI_TOKEN "/\n\r\t"*/ #define INCHI_TOKEN "/\n\r\t\\" typedef enum tagInChI_STATE { /* M */ IST_MOBILE_H_FORMULA, /* 0 */ IST_MOBILE_H_CONNECTIONS, /* 1 */ IST_MOBILE_H, /* 2 */ IST_MOBILE_H_CHARGE, /* 3 */ IST_MOBILE_H_PROTONS, /* 4 */ IST_MOBILE_H_SP2, /* 5 */ IST_MOBILE_H_SP3, /* 6 */ IST_MOBILE_H_SP3_M, /* 7 */ IST_MOBILE_H_SP3_S, /* 8 */ /* Fork */ IST_MOBILE_H_ISO_LAYER_FORK, /* 9 */ /* MI */ IST_MOBILE_H_ISO_ATOMS, /* 10 */ IST_MOBILE_H_ISO_EXCH_H, /* 11 */ IST_MOBILE_H_ISO_SP2, /* 12 */ IST_MOBILE_H_ISO_SP3, /* 13 */ IST_MOBILE_H_ISO_SP3_M, /* 14 */ IST_MOBILE_H_ISO_SP3_S, /* 15 */ /* Fork */ IST_FIXED_H_LAYER_FORK, /* 16 */ /* F */ IST_FIXED_H_FORMULA, /* 17 */ IST_FIXED_H, /* 18 */ IST_FIXED_H_CHARGE, /* 19 */ IST_FIXED_H_SP2, /* 20 */ IST_FIXED_H_SP3, /* 21 */ IST_FIXED_H_SP3_M, /* 22 */ IST_FIXED_H_SP3_S, /* 23 */ IST_FIXED_H_PERMUTATION, /* 24 */ /* Fork */ IST_FIXED_H_ISO_LAYER_FORK, /* 25 */ /* FI */ IST_FIXED_H_ISO_ATOMS, /* 26 */ IST_FIXED_H_ISO_LAYER, /* 27 */ IST_FIXED_H_ISO_SP2, /* 28 */ IST_FIXED_H_ISO_SP3, /* 29 */ IST_FIXED_H_ISO_SP3_M, /* 30 */ IST_FIXED_H_ISO_SP3_S, /* 31 */ IST_FIXED_H_ISO_PERMUTATION, /* 32 */ /* Reconnected */ IST_RECONNECTED_LAYER_FORK, /* 33 */ IST_RECONNECTED_FORMULA, /* 34 */ /* Other reading errors */ IST_MATERIAL_BALANCE_ERROR, /* 35 */ IST_END = -1 }INCHI_STATE; #define IST_HAPPENED_IN_RECMET 100 typedef struct tagInchiReadErrMsg { int stat; const char *msg; } INCHI_READ_ERR_MSG; ICHICONST INCHI_READ_ERR_MSG irErrMsg[] = { /* M */ {IST_MOBILE_H_FORMULA, "MOBILE_H_FORMULA" }, {IST_MOBILE_H_CONNECTIONS, "MOBILE_H_CONNECTIONS" }, {IST_MOBILE_H, "MOBILE_H" }, {IST_MOBILE_H_CHARGE, "MOBILE_H_CHARGE" }, {IST_MOBILE_H_PROTONS, "MOBILE_H_PROTONS" }, {IST_MOBILE_H_SP2, "MOBILE_H_SP2" }, {IST_MOBILE_H_SP3, "MOBILE_H_SP3" }, {IST_MOBILE_H_SP3_M, "MOBILE_H_SP3_/m" }, {IST_MOBILE_H_SP3_S, "MOBILE_H_SP3_/s" }, /* Fork */ {IST_MOBILE_H_ISO_LAYER_FORK, "MOBILE_H_ISO_LAYER_FORK" }, /* MI */ {IST_MOBILE_H_ISO_ATOMS, "MOBILE_H_ISO_ATOMS" }, {IST_MOBILE_H_ISO_EXCH_H, "MOBILE_H_ISO_EXCH_H" }, {IST_MOBILE_H_ISO_SP2, "MOBILE_H_ISO_SP2" }, {IST_MOBILE_H_ISO_SP3, "MOBILE_H_ISO_SP3" }, {IST_MOBILE_H_ISO_SP3_M, "MOBILE_H_ISO_SP3_/m" }, {IST_MOBILE_H_ISO_SP3_S, "MOBILE_H_ISO_SP3_/s" }, /* Fork */ {IST_FIXED_H_LAYER_FORK, "FIXED_H_LAYER_FORK" }, /* F */ {IST_FIXED_H_FORMULA, "FIXED_H_FORMULA" }, {IST_FIXED_H, "FIXED_H" }, {IST_FIXED_H_CHARGE, "FIXED_H_CHARGE" }, {IST_FIXED_H_SP2, "FIXED_H_SP2" }, {IST_FIXED_H_SP3, "FIXED_H_SP3" }, {IST_FIXED_H_SP3_M, "FIXED_H_SP3_/m" }, {IST_FIXED_H_SP3_S, "FIXED_H_SP3_/s" }, {IST_FIXED_H_PERMUTATION, "FIXED_H_PERMUTATION" }, /* Fork */ {IST_FIXED_H_ISO_LAYER_FORK, "FIXED_H_ISO_LAYER_FORK" }, /* FI */ {IST_FIXED_H_ISO_ATOMS, "FIXED_H_ISO_ATOMS" }, {IST_FIXED_H_ISO_LAYER, "FIXED_H_ISO_LAYER" }, {IST_FIXED_H_ISO_SP2, "FIXED_H_ISO_SP2" }, {IST_FIXED_H_ISO_SP3, "FIXED_H_ISO_SP3" }, {IST_FIXED_H_ISO_SP3_M, "FIXED_H_ISO_SP3_m" }, {IST_FIXED_H_ISO_SP3_S, "FIXED_H_ISO_SP3_s" }, {IST_FIXED_H_ISO_PERMUTATION, "FIXED_H_ISO_PERMUTATION" }, /* Reconnected */ {IST_RECONNECTED_LAYER_FORK, "RECONNECTED_LAYER_FORK" }, {IST_RECONNECTED_FORMULA, "RECONNECTED_FORMULA" }, {IST_MATERIAL_BALANCE_ERROR, "MATERIAL_BALANCE" }, {IST_END, "Unknown Error" } }; typedef enum tagCopySegmentType { CPY_SP2, CPY_SP3, CPY_SP3_M, CPY_SP3_S, CPY_ISO_AT } COPY_SEG_TYPE; #define NSTRLEN 64000 #define MAX_MSG_LEN 512 #define MAX_MSG_BUF_LEN 128 /*************************************************************************************/ const char *getInchiStateReadErr(int stat) { int i, bRecMet = 0; static char szMsg[128]; if ( stat >= IST_HAPPENED_IN_RECMET ) { bRecMet = 1; stat -= IST_HAPPENED_IN_RECMET; } for ( i = 0; 0 <= irErrMsg[i].stat && stat != irErrMsg[i].stat; i ++ ) ; sprintf(szMsg, #if ( FIX_DALKE_BUGS == 1 ) "%s%.100s", #else "%s%s", #endif irErrMsg[i].msg, bRecMet? ", Reconnected layer" : ""); return szMsg; } /**************************************************************************************/ const char *getInchiErrName(int nErr) { switch ( nErr ) { case RI_ERR_ALLOC: return "Allocation failed"; case RI_ERR_PROGR: return "Program error"; case RI_ERR_SYNTAX: return "Syntax error"; case RI_ERR_EOL: return "End of line"; } return "Unknown error"; } #if ( FIX_DALKE_BUGS == 1 ) /*****************************************************************************************/ int SetHillFormFromInChI(InpInChI *OneInput) { int iINChI, iTaut, iComp, num_diff; INChI *pINChI; char *szHillFormulaOld; for ( iINChI = 0, num_diff = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( iTaut = TAUT_NON; iTaut < TAUT_NUM; iTaut ++ ) { for ( iComp = 0; iComp < OneInput->nNumComponents[iINChI][iTaut]; iComp ++ ) { pINChI = &OneInput->pInpInChI[iINChI][iTaut][iComp]; if ( !pINChI->nNumberOfAtoms || pINChI->bDeleted || !pINChI->szHillFormula || !pINChI->szHillFormula[0] ) { continue; } szHillFormulaOld = pINChI->szHillFormula; pINChI->szHillFormula = AllocateAndFillHillFormula(pINChI); num_diff += !pINChI->szHillFormula || !pINChI->szHillFormula[0] || strcmp(pINChI->szHillFormula, szHillFormulaOld); inchi_free(szHillFormulaOld); } } } return num_diff; } #endif /********************** main entry point **********************************************/ int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, /* the following are InChI library-specific parameters */ inp_ATOM **at, int *num_at, char *szMsg, int nMsgLen, unsigned long WarningFlags[2][2]) { InpInChI OneInput; int i, j, nReadStatus, ret, nErr, iINChI; char *strHdr=NULL; char *szCurHdr = NULL; int num_components[INCHI_NUM]; int bReqNonTaut = (0 != ((ip_inp->nMode & REQ_MODE_BASIC) && (ip_inp->nMode & REQ_MODE_TAUT))); /* int bReqRecmet = (0 != ((ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD))); */ int bReqStereo = (0 != (ip_inp->nMode & REQ_MODE_STEREO)); int bHasSomeReconnected = 0, bHasSomeFixedH = 0, bHasMetal = 0; int nModeFlagsStereo = 0, bTautFlags = 0; /* InChI creation flags modifications derived from current InChI */ MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM]; NORM_CANON_FLAGS ncFlags; NORM_CANON_FLAGS *pncFlags = &ncFlags; INPUT_PARMS ip_cur, *ip; STRUCT_DATA sd_cur, *sd; int nMessageLen = MAX_MSG_LEN; char szMessage[MAX_MSG_LEN]; int nInitLenMessage; int pState, bStereoType; int bReqProtonsForEachComponent = 0; int bReqSplitOutputInChI = 0; SEGM_LINE Line; SEGM_LINE *pLine = &Line; long ulProcessingTime = 0; inchiTime ulTStart; long num_processed = 0, num_errors = 0; int bPlainTabbedOutput; const char *pTAB; #ifdef TARGET_API_LIB const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); #else const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); #endif const int bReadCoord = bInChI2Structure; long num_inp=0; int bInputInStdFormat=0; int bInputHasSaveOpt=0; unsigned char inp_save_opt_bits=0; unsigned char save_opt_bits=0; ret = 0; nReadStatus = RI_ERR_EOL; memset(szMessage, 0, sizeof(szMessage)); memset(&OneInput, 0, sizeof(OneInput)); memset(pLine, 0, sizeof(pLine[0])); if ( szMsg ) szMsg[0] = '\0'; while( nReadStatus != RI_ERR_EOF ) { for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( OneInput.nNumProtons[iINChI][j].pNumProtons ) { inchi_free(OneInput.nNumProtons[iINChI][j].pNumProtons); OneInput.nNumProtons[iINChI][j].pNumProtons = NULL; } } } memset(&OneInput, 0, sizeof(OneInput)); memset(pncFlags, 0, sizeof(*pncFlags)); bStereoType = 0; ip_cur = *ip_inp; ip = &ip_cur; sd_cur = *sd_inp; sd = &sd_cur; bReqSplitOutputInChI = 0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT); bReqProtonsForEachComponent = bReqSplitOutputInChI && 0 != (READ_INCHI_KEEP_BALANCE_P & ip->bReadInChIOptions); bPlainTabbedOutput = 0 != (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT); #if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) pTAB = bPlainTabbedOutput? "\t" : "\n"; #else pTAB = "\n"; #endif if ( bInChI2Structure ) { #if ( bRELEASE_VERSION == 1 ) bReqNonTaut = 1; /* bReqNonTaut=0 ignores Fixed-H layer in input InChI, for testing only */ #endif /* bReqRecmet = 1; */ bReqStereo = 1; bReqSplitOutputInChI = 1; bReqProtonsForEachComponent = bReqNonTaut; ip->bTautFlags |= (TG_FLAG_DISCONNECT_COORD | TG_FLAG_RECONNECT_COORD); ip->nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_ISO); } /************************************************************************/ /* Read InChI string */ /************************************************************************/ InchiTimeGet(&ulTStart); nReadStatus = InChILine2Data(pInp, pLine, &strHdr, &pState, &nErr, OneInput.pInpInChI, OneInput.nNumComponents, OneInput.nNumProtons, OneInput.s, bReadCoord, bInChI2Structure, ip_inp->nMode, &bInputInStdFormat,&bInputHasSaveOpt,&inp_save_opt_bits); ulProcessingTime += InchiTimeElapsed(&ulTStart); if ( (nReadStatus == RI_ERR_EOL || nReadStatus == RI_ERR_EOF) && !nErr && OneInput.nNumComponents[INCHI_BAS][TAUT_YES] + OneInput.nNumComponents[INCHI_BAS][TAUT_NON] ) { /* InChI has been successfully read */ ret = 0; num_inp ++; bHasSomeReconnected = 0; bHasSomeFixedH = 0; /* Does not allow conversion non-standard->standard */ /* (force target to be non-standard also) */ if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) { if ( !bInputInStdFormat ) /* Input InChI is a non-standard one */ { ip->bINChIOutputOptions &= ~INCHI_OUT_STDINCHI; if ( szCurHdr && szCurHdr[0] ) inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, %s\n", szCurHdr ); else inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, Structure %ld\n", num_inp ); } } if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { if (!bInputHasSaveOpt) { /* Does not allow to create SaveOpt if the source lacks appendix */ ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; if ( szCurHdr && szCurHdr[0] ) inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, %s\n", szCurHdr ); else inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, Structure %ld\n", num_inp ); } else { /* Analyze existing and prepare new SaveOpt appendix */ if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { /* RecMet requested */ if ( 0 != (inp_save_opt_bits & SAVE_OPT_RECMET) ) { save_opt_bits |= SAVE_OPT_RECMET; } else { ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; if ( szCurHdr && szCurHdr[0] ) inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, %s\n", szCurHdr ); else inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, Structure %ld\n", num_inp ); } } if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) { /* FixedH requested */ if ( 0 != (inp_save_opt_bits & SAVE_OPT_FIXEDH) ) save_opt_bits |= SAVE_OPT_FIXEDH; else { ip->nMode &= ~REQ_MODE_BASIC; if ( szCurHdr && szCurHdr[0] ) inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, %s\n", szCurHdr ); else inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, Structure %ld\n", num_inp ); } } /* Copy from source SaveOpt the bits which we do not touch */ /* while converting InChI: SUU SLUUD KET 15T */ if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SUU) ) save_opt_bits |= SAVE_OPT_SUU; if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SLUUD) ) save_opt_bits |= SAVE_OPT_SLUUD; if ( 0 != ( inp_save_opt_bits & SAVE_OPT_KET) ) save_opt_bits |= SAVE_OPT_KET; if ( 0 != ( inp_save_opt_bits & SAVE_OPT_15T) ) save_opt_bits |= SAVE_OPT_15T; /* Check if /SNon requested and turn OFF stereo bits if so */ if ( ! (ip->nMode & REQ_MODE_STEREO) ) { save_opt_bits &= ~SAVE_OPT_SUU; save_opt_bits &= ~SAVE_OPT_SLUUD; } } } #ifndef TARGET_API_LIB /* inchi_ios_eprint(stderr, "%ld: %s\r", num_inp, strHdr? strHdr : ""); inchi_ios_eprint(pLog, "%ld: %s\n", num_inp, strHdr? strHdr : ""); */ if ( !ip->bNoStructLabels && !(bInChI2Structure && (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)) ) { /* Added 2nd item: Do not output this extra line into the output SDfile. 2008-11-17 DCh */ if ( strHdr && strstr(strHdr, "Structure:")) { inchi_ios_print(pOut, "%s%s", strHdr, pTAB); /* output header */ #if ( FIX_DALKE_BUGS == 1 ) #else sprintf(szMessage, "%s (%ld)", strHdr? strHdr : "", num_inp); #endif } else { /* inchi_ios_print(pOut, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "", pTAB); sprintf(szMessage, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "" , pTAB); */ inchi_ios_print(pOut, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name" , pTAB); /* output header */ #if ( FIX_DALKE_BUGS == 1 ) #else sprintf(szMessage, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name", pTAB); #endif } if ( strHdr && strHdr[0] ) { strncpy(ip->szSdfDataHeader, strHdr, sizeof(ip->szSdfDataHeader)); ip->szSdfDataHeader[sizeof(ip->szSdfDataHeader)-1] = '\0'; ip->pSdfLabel = NULL; ip->pSdfValue = ip->szSdfDataHeader; } else { ip->pSdfValue = NULL; ip->szSdfDataHeader[0] = '\0'; } } #if ( FIX_DALKE_BUGS == 1 ) sprintf(szMessage, "%ld: %.400s", num_inp, strHdr? strHdr : ""); #else sprintf(szMessage, "%ld: %s", num_inp, strHdr? strHdr : ""); #endif #endif nInitLenMessage = strlen(szMessage); if ( strHdr ) { szCurHdr = strHdr; strHdr = NULL; } if ( szCurHdr && ip && ip->first_struct_number > 0 ) { /* check whether the structure should be skipped */ static char szStruct[] = "Structure:"; char *pStrNum = strstr(szCurHdr, szStruct); long cur_struct_number; if ( pStrNum ) { pStrNum += sizeof(szStruct)-1; /* -1 takes care of the string terminal zero */ cur_struct_number = inchi_strtol(pStrNum, NULL, 10); if ( cur_struct_number ) { OneInput.num_inp = cur_struct_number; } /* process request to bypass first several InChIs */ if ( cur_struct_number > 0 && cur_struct_number < ip->first_struct_number ) { #if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) inchi_fprintf(stderr, "Skipping %s\r", szMessage); #endif FreeInpInChI(&OneInput); if ( szCurHdr ) { inchi_free(szCurHdr); szCurHdr = NULL; } INCHI_HEAPCHK continue; } } } num_processed ++; /* In case of splitting InChI into separate components */ /* decide whether to keep /p in each component or */ /* output /p and /i/h as a separate component */ /* Note: if InChI is not to be splitted DO NOT create */ /* a separate component for /p, /i/h: it would be a bug*/ InchiTimeGet(&ulTStart); INCHI_HEAPCHK ret = SetProtonsAndXchgIsoH(bInChI2Structure, bReqSplitOutputInChI, bReqProtonsForEachComponent, bReqNonTaut, bReqStereo, num_components, nModeProtonIsoExchgH, &OneInput); INCHI_HEAPCHK if ( ret < 0 ) { num_errors ++; goto exit_error; } sd->num_components[INCHI_BAS] = num_components[INCHI_BAS]; sd->num_components[INCHI_REC] = num_components[INCHI_REC]; /* do we have reconnected InChI ? */ if ( (OneInput.nNumComponents[INCHI_REC][TAUT_YES] || OneInput.nNumComponents[INCHI_REC][TAUT_NON]) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) { /* needed for InChI string output to include reconnected InChI */ sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; bHasSomeReconnected = 1; } /* Do we have fixed H InChI ? */ if ( bReqNonTaut && /*OneInput.nNumComponents[bHasSomeReconnected?INCHI_REC:INCHI_BAS][TAUT_NON]*/ (OneInput.nNumComponents[INCHI_REC][TAUT_NON] || OneInput.nNumComponents[INCHI_BAS][TAUT_NON]) ) { bHasSomeFixedH = 1; } ulProcessingTime += InchiTimeElapsed(&ulTStart); if ( bInChI2Structure && !bInChI2InChI ) { /**********************************************************************/ /* InChi --> Structure */ /**********************************************************************/ int bINChIOutputOptions = #if ( I2S_MODIFY_OUTPUT == 1 ) /* transfer user's InChI output options to serialization 10-12-2007 */ ip_inp->bINChIOutputOptions & ( INCHI_OUT_NO_AUX_INFO | /* do not output Aux Info */ INCHI_OUT_SHORT_AUX_INFO | /* output short version of Aux Info */ INCHI_OUT_ONLY_AUX_INFO | /* output only Aux Info */ /* INCHI_OUT_EMBED_REC |*/ /* embed reconnected INChI into disconnected INChI */ INCHI_OUT_SDFILE_ONLY | /* save input data in a Molfile instead of creating INChI */ INCHI_OUT_XML | /* output xml INChI */ INCHI_OUT_PLAIN_TEXT | /* output plain text INChI */ INCHI_OUT_PLAIN_TEXT_COMMENTS | /* output plain text annotation */ INCHI_OUT_XML_TEXT_COMMENTS | /* output xml text annotation */ /* INCHI_OUT_WINCHI_WINDOW |*/ /* output into wINChI text window */ INCHI_OUT_TABBED_OUTPUT | /* tab-delimited (only for plain text) */ INCHI_OUT_SDFILE_ATOMS_DT | /* SDfile output H isotopes as D and T */ INCHI_OUT_SDFILE_SPLIT | /* Split SDfile into components */ 0); #else 0; #endif SRM srm; /* rules how to handle bonds to metal atoms */ StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM]; /* prepare parameters */ InchiTimeGet(&ulTStart); if ( bInputInStdFormat ) { if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) bINChIOutputOptions |= INCHI_OUT_STDINCHI; } else { if ( ip_inp->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) bINChIOutputOptions |= INCHI_OUT_SAVEOPT; } memset(pStruct, 0, sizeof(pStruct)); /* structure restore parms */ SetUpSrm(&srm); /* eliminate Fixed-H InChI that are exactly same as the corresponding Mobile-H structures */ RemoveFixHInChIIdentical2MobH(&OneInput); /*-- recheck layers after the elimination; get optional stereo flags --*/ ret = DetectInpInchiCreationOptions(&OneInput, &bHasSomeReconnected, &bHasMetal, &bHasSomeFixedH, &nModeFlagsStereo, &bTautFlags); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Error in detecting input InChI options", "; "); num_errors ++; goto dealloc; } if ( bHasSomeFixedH && !bReqNonTaut ) { bHasSomeFixedH = 0; } /*---------------- set stereo flags ---------------------*/ ip->nMode &= ~(REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO | REQ_MODE_CHIR_FLG_STEREO | REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); ip->nMode |= nModeFlagsStereo; /* Remove Phosphine and Arsine Stereo Flags */ ip->bTautFlags &= ~TG_FLAG_PHOSPHINE_STEREO; ip->bTautFlags &= ~TG_FLAG_ARSINE_STEREO; ip->bTautFlags &= ~TG_FLAG_FIX_SP3_BUG; ip->bTautFlags |= bTautFlags; /* mark Disconnected InChI components that are exactly came as Reconnected ones */ /* Disconnected will have a negative number of the reconnected component */ /* Reconnected will have a positive number of the disconnected component */ MarkDisconectedIdenticalToReconnected (&OneInput); /*****************************************************************************/ /* pay attention to: */ /* 1) .nLink < 0 in Disonnected which means InChI is same as in Reconnected */ /* The component in Reconnected has .nLink pointing to the Disconnected; */ /* each .nLink = (1+component index) or -(1+component index) */ /* In the future .nLink>0 in Disconnected shall point to the Reconnectrd */ /* component from which it was created */ /* 2) Currently reversed structures from Disconnected components are created */ /* and abandoned if Reconnected layer exists */ /* 3) Connect/disconnect H depends on the presence of atom/bond parity */ /* The combined Mobile/Fixed-H parity should be set for Fixed-H components*/ /* 4) No comparison of the Disconnected layer is done if Reconnected exists */ /* 5) Reading InChI was not fully tested in case one component has stereo in */ /* both Mobile-H and Fixed-H layers while another component has stereo */ /* only in Mobile-H layer */ /*****************************************************************************/ /* main conversion InChI->Structure for each component and */ /* after that pStruct[iRec][iMobH][iComponent].at2 is the structure, */ /* pStruct[iRec][iMobH][iComponent].RevInChI full InChI for the structure */ /* In case of both Fixed-H and Mobile-H layers the results are in iMobH=0 */ /* In case of only Mobile-H/Main layer the results are in iMobH=1 */ ulProcessingTime += InchiTimeElapsed(&ulTStart); sd->ulStructTime = 0; ret = AllInchiToStructure(ip, sd, num_inp, szCurHdr, &srm, bHasSomeFixedH, pStruct, &OneInput); ulProcessingTime += sd->ulStructTime; InchiTimeGet(&ulTStart); /* ret < 0 is error code; ret > 0 is number of errors */ /* in pStruct[iInchiRec][iMobileH][iComponent].nError */ if ( ret) { /* conversion error */ num_errors ++; goto dealloc; } /* an attempt to fix the numumber of removed protons in case of Mobile-H */ if ( !OneInput.nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons && !OneInput.nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) { ret = AddProtonAndIsoHBalanceToMobHStruct(ip, sd, num_inp, bHasSomeFixedH, szCurHdr, pStruct, &OneInput); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Add/Remove protons error", "; "); num_errors ++; goto dealloc; } } /* compare InChI from the Reversed Structure to the original input InChI */ ret = CompareAllOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, num_inp, szCurHdr); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare error", "; "); num_errors ++; goto dealloc; } ret = CompareAllDisconnectedOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, num_inp, szCurHdr); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare2 error", "; "); num_errors ++; goto dealloc; } if ( WarningFlags ) { for ( i = 0; i < 2; i ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { WarningFlags[i][j] = (unsigned long)OneInput.CompareInchiFlags[i][j]; } } } ulProcessingTime += InchiTimeElapsed(&ulTStart); #ifndef COMPILE_ANSI_ONLY ret = DisplayStructureComponents(ip, sd, num_inp, szCurHdr, &srm, bReqNonTaut, pStruct, &OneInput); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Display structure error", "; "); } #endif InchiTimeGet(&ulTStart); ret = MergeStructureComponents(ip, sd, num_inp, szCurHdr, &srm, bReqNonTaut, pStruct, &OneInput); ulProcessingTime += InchiTimeElapsed(&ulTStart); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Merge Components error", "; "); num_errors ++; goto dealloc; } #ifdef TARGET_API_LIB /*------------- for debug only ------------------- InchiTimeGet(&ulTStart); ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, 0, pOut, pLog, &OneInput, save_opt_bits); ulProcessingTime += InchiTimeElapsed(&ulTStart); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion failed", "; "); goto dealloc; } -------------------------------------------------*/ if ( at && num_at ) { *at = OneInput.atom; *num_at = OneInput.num_atoms; OneInput.atom = NULL; } #else InchiTimeGet(&ulTStart); ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, bINChIOutputOptions, pOut, /*pLog*/ NULL, &OneInput, bHasSomeFixedH, save_opt_bits); ulProcessingTime += InchiTimeElapsed(&ulTStart); if ( ret < 0 ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion error", "; "); num_errors ++; goto dealloc; } #endif if ( szMessage ) { int len; InchiTimeGet(&ulTStart); FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[0]); if ( OneInput.CompareInchiFlags[1][0] || OneInput.CompareInchiFlags[1][1] ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Disconnected: ", "; "); FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[1]); } /* add a metal warning */ if ( bHasMetal && nInitLenMessage < (len=(int)strlen(szMessage)) ) { char szMetal[] = " (Metal compound)"; int shift; if ( len + (int)sizeof(szMetal) > nMessageLen ) { len = nMessageLen - (int)sizeof(szMetal); } shift = nInitLenMessage + (int)sizeof(szMetal) - 1; memmove(szMessage+shift, szMessage + nInitLenMessage, (len-nInitLenMessage)*sizeof(szMessage[0])); memcpy(szMessage + nInitLenMessage, szMetal, sizeof(szMetal)-sizeof(szMessage[0])); szMessage[shift+len-nInitLenMessage] = '\0'; } ulProcessingTime += InchiTimeElapsed(&ulTStart); } ret = 0; /* deallocate */ dealloc: if ( ret ) { if ( ret > 0 ) { int iRec, iMob, iComp, nComp, len; char szTemp[128]; AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed on component(s)", "; "); len = strlen(szMessage); for ( iRec = 0; iRec < INCHI_NUM; iRec ++ ) { for ( iMob = bHasSomeFixedH? TAUT_NON : TAUT_YES; iMob < TAUT_NUM; iMob ++ ) { nComp = OneInput.nNumComponents[iRec][iMob]; if ( !pStruct[iRec][iMob] ) { continue; } for ( iComp = 0; iComp < nComp; iComp ++ ) { if ( pStruct[iRec][iMob][iComp].nError ) { char *szFormula = OneInput.pInpInChI[iRec][iMob][iComp].szHillFormula; sprintf (szTemp, #if ( FIX_DALKE_BUGS == 1 ) " %s%s%d(%.96s)", #else " %s%s%d(%s)", #endif !bHasSomeReconnected? "" : iRec? "R" : "D", !bHasSomeFixedH? "": iMob? "M" : "F", iComp + 1, szFormula? szFormula : "???"); AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, szTemp, NULL); } } } } } else { if ( ret == CT_USER_QUIT_ERR ) { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Terminated by the user*", "; "); } else { AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed*", "; "); } } } InchiTimeGet(&ulTStart); /* print one structure report */ if ( szMsg && nMsgLen > 1 ) { int len = inchi_min( (int)strlen(szMessage), nMsgLen-1); if ( len > 0 ) { memcpy( szMsg, szMessage, len); szMsg[len] = '\0'; } else { szMsg[0] = '\0'; } } if ( nInitLenMessage < (int)strlen(szMessage) ) { inchi_ios_eprint(pLog, "%s\n", szMessage); } #ifndef TARGET_API_LIB else { /*^^^inchi_ios_eprint( stderr, "%s\r", szMessage );*/ inchi_fprintf( stderr, "%s\r", szMessage ); } #endif FreeStrFromINChI( pStruct, OneInput.nNumComponents ); FreeInpInChI( &OneInput ); if ( szCurHdr ) { inchi_free( szCurHdr ); szCurHdr = NULL; } INCHI_HEAPCHK ulProcessingTime += InchiTimeElapsed( &ulTStart ); if ( ret < 0 ) { goto exit_error; } } /* if ( bInChI2Structure && !bInChI2InChI ) */ else if ( !bInChI2Structure && bInChI2InChI ) { /**********************************************************************/ /* InChi --> InChI string(s) */ /**********************************************************************/ int tmp = ip->bNoStructLabels; InchiTimeGet( &ulTStart ); ip->bNoStructLabels = 1; INCHI_HEAPCHK ip->pSdfValue = NULL; ip->pSdfLabel = NULL; #if ( FIX_DALKE_BUGS == 1 ) SetHillFormFromInChI( &OneInput ); #endif ret = OutputInChIAsRequested(pOut, pLog, ip, sd, &OneInput, num_components, nModeProtonIsoExchgH, num_inp, save_opt_bits); #if ( !defined(TARGET_API_LIB) && defined(TARGET_EXE_STANDALONE) ) /*^^^ calculate InChIKey if requested */ /* However, do not calculat/write it if this function is called from within dll */ { char ik_string[256]; /*^^^ Resulting InChIKey string */ int ik_ret=0; /*^^^ InChIKey-calc result code */ int xhash1, xhash2; char szXtra1[65], szXtra2[65]; inchi_ios_flush2(pLog, stderr); /*^^^ post-1.02b addition - correctly treat tabbed output with InChIKey */ if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) if ( ip->bCalcInChIHash != INCHIHASH_NONE ) if (pOut->s.pStr) if (pOut->s.nUsedLength>0) if (pOut->s.pStr[pOut->s.nUsedLength-1]=='\n') /* replace LF with TAB */ pOut->s.pStr[pOut->s.nUsedLength-1] = '\t'; if ( ip->bCalcInChIHash != INCHIHASH_NONE ) { char *buf = NULL; size_t slen = pOut->s.nUsedLength; extract_inchi_substring(&buf, pOut->s.pStr, slen); if (NULL!=buf) { xhash1 = xhash2 = 0; if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) xhash1 = 1; if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) xhash2 = 1; ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, ik_string, szXtra1, szXtra2); inchi_free(buf); } else ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; if (ik_ret==INCHIKEY_OK) { inchi_ios_print(pOut, "InChIKey=%-s\n",ik_string); } else { inchi_ios_print(pLog, "Warning (Could not compute InChIKey: ", num_inp); switch(ik_ret) { case INCHIKEY_UNKNOWN_ERROR: inchi_ios_print(pLog, "unresolved error)"); break; case INCHIKEY_EMPTY_INPUT: inchi_ios_print(pLog, "got an empty string)"); break; case INCHIKEY_INVALID_INCHI_PREFIX: case INCHIKEY_INVALID_INCHI: case INCHIKEY_INVALID_STD_INCHI: inchi_ios_print(pLog, "got non-InChI string)"); break; case INCHIKEY_NOT_ENOUGH_MEMORY: inchi_ios_print(pLog, "not enough memory to treat the string)"); break; default:inchi_ios_print(pLog, "internal program error)"); break; } inchi_ios_print(pLog, " structure #%-lu.\n", num_inp); if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) inchi_ios_print(pOut, "\n"); } /* if (ip->bCalcInChIHash!=INCHIHASH_NONE) */ inchi_ios_flush(pOut); inchi_ios_flush2(pLog, stderr); } else inchi_ios_flush(pOut); } /* calculate InChIKey if requested */ #endif ip->bNoStructLabels = tmp; #ifndef TARGET_API_LIB if ( ret < 0 ) { if ( szCurHdr && szCurHdr[0] ) { inchi_ios_eprint( pLog, "Error %d creating InChI string %s\n", ret, szCurHdr ); } else { inchi_ios_eprint( pLog, "Error %d creating InChI string, Structure %ld\n", ret, num_inp ); } num_errors ++; } #if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) else if ( szCurHdr && szCurHdr[0] ) { inchi_fprintf( stderr, "%s\r", szCurHdr ); } #endif #endif if ( szCurHdr ) { inchi_free( szCurHdr ); szCurHdr = NULL; } INCHI_HEAPCHK ulProcessingTime += InchiTimeElapsed( &ulTStart ); } /* if ( !bInChI2Structure && bInChI2InChI ) */ else { inchi_ios_eprint( pLog, "\nWrong command line options: expected Inch2Struct or Inchi2Inchi\n", num_inp ); break; } if ( nReadStatus == RI_ERR_EOF ) { break; } } /* InChI has been successfully read */ else { /* InChI could not be read */ if ( nReadStatus == RI_ERR_EOF && nErr == 0 && pState == 0 && !strHdr ) { inchi_ios_eprint( pLog, "\nEnd of file detected after structure %ld. \n", num_inp ); } else { /* output InChI parsing error message */ char szHdrSimulation[128]; num_inp ++; sprintf( szHdrSimulation, "Structure: %ld", num_inp ); inchi_ios_eprint( pLog, "\n%s %s (%d) in %s (%d)\n", strHdr? strHdr : szHdrSimulation, getInchiErrName(nErr), nErr, getInchiStateReadErr(pState), pState ); num_errors ++; num_processed ++; } if ( strHdr ) { inchi_free( strHdr ); strHdr = NULL; } if ( szCurHdr ) { inchi_free( szCurHdr ); szCurHdr = NULL; } FreeInpInChI( &OneInput ); } #ifdef TARGET_EXE_STANDALONE #ifndef TARGET_API_LIB inchi_ios_flush(pOut); inchi_ios_flush2(pLog, stderr); #endif #endif #ifdef TARGET_API_LIB break; /* exit after the 1st structure */ #endif } /* while */ exit_error: FreeInpInChI( &OneInput ); if ( strHdr ) { inchi_free( strHdr ); strHdr = NULL; } if ( pLine->str ) { inchi_free( pLine->str ); } if ( szCurHdr ) { inchi_free( szCurHdr ); szCurHdr = NULL; } INCHI_HEAPCHK if ( sd_inp ) { sd_inp->ulStructTime = ulProcessingTime; sd_inp->fPtrStart = num_processed; sd_inp->fPtrEnd = num_errors; } return ret; } /**********************************************************************************************/ int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, InpInChI *OneInput, int num_components[INCHI_NUM], MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], long num_inp, unsigned char save_opt_bits) { int j, k, k1, k2, ret2=0, iINChI, iINChI1, iINChI2; PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; int bReqNonTaut; int bHasSomeReconnected; INPUT_PARMS ip_local; STRUCT_DATA sd_local; INPUT_PARMS *ip = &ip_local; STRUCT_DATA *sd = &sd_local; NORM_CANON_FLAGS ncFlags; NORM_CANON_FLAGS *pncFlags = &ncFlags; const int nStrLen = NSTRLEN; char *pStr = NULL; int nRet1, bSortPrintINChIFlags; int bReqSplitOutputInChI; int nNumOutputComponents; nRet1 = 0; k1 = k2 = 0; memset( pncFlags, 0, sizeof(*pncFlags) ); memset( pINChI, 0, sizeof(pINChI) ); memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); *ip = *ip_inp; *sd = *sd_inp; bHasSomeReconnected = 0; bSortPrintINChIFlags = 0; nNumOutputComponents = 0; bReqNonTaut = (0 != (ip->nMode & REQ_MODE_BASIC)); bReqSplitOutputInChI = (0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT)); INCHI_HEAPCHK if ( num_components[INCHI_BAS] ) { MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_BAS], pINChI_Aux[INCHI_BAS], num_components[INCHI_BAS], num_components[INCHI_BAS], k1); } if ( num_components[INCHI_REC] ) { MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_REC], pINChI_Aux[INCHI_REC], num_components[INCHI_REC], num_components[INCHI_REC], k2); } pStr = (char*) inchi_malloc( nStrLen * sizeof(pStr[0]) ); INCHI_HEAPCHK if ( k1 || k2 || !pStr ) { ret2 = RI_ERR_ALLOC; goto exit_error; } if ( num_components[INCHI_REC] && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) { sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; bHasSomeReconnected = 1; } for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) { for ( k = 0; k < num_components[iINChI]; k ++ ) { /* allocate InChI & AuxInfo */ if ( !(pINChI[iINChI][k][j] = (INChI *) inchi_calloc(1, sizeof(INChI)) ) ) { ret2 = RI_ERR_ALLOC; goto exit_error; } if ( !(pINChI_Aux[iINChI][k][j] = (INChI_Aux *) inchi_calloc(1, sizeof(INChI_Aux)) ) ) { ret2 = RI_ERR_ALLOC; goto exit_error; } /* copy InChI & AuxInfo */ if ( k < OneInput->nNumComponents[iINChI][j] ) { /* copy InChI */ *pINChI[iINChI][k][j] = OneInput->pInpInChI[iINChI][j][k]; memset(&OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); INCHI_HEAPCHK /* take care of protons in AuxInfo */ if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH && j == TAUT_YES ) { pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedProtons; for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedIsotopicH[k1]; } INCHI_HEAPCHK } else if ( !k && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_FIRST || k+1 == OneInput->nNumComponents[iINChI][j] && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT ) { /* add protons and exchangeable isotopic H to the first component's AuxInfo */ pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = OneInput->nNumProtons[iINChI][j].nNumRemovedProtons; for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = OneInput->nNumProtons[iINChI][j].nNumRemovedIsotopicH[k1]; } INCHI_HEAPCHK } else { pINChI_Aux[iINChI][k][j]->bDeleted = pINChI[iINChI][k][j]->bDeleted; } if ( j == TAUT_YES && pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && !pINChI[iINChI][k][j]->nNum_H_fixed ) { /* serializer crashes if it is not allocated */ pINChI[iINChI][k][j]->nNum_H_fixed = (S_CHAR *)inchi_calloc(pINChI[iINChI][k][j]->nNumberOfAtoms+1, sizeof(pINChI[0][0][0]->nNum_H_fixed[0]) ); } if ( j == TAUT_YES && k < OneInput->nNumComponents[iINChI][TAUT_NON] && pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms && !CompareReversedINChI( pINChI[iINChI][k][j], pINChI[iINChI][k][TAUT_NON], NULL, NULL ) ) { pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms = 0; /* eliminate non-taut equal to taut */ } } else { /* extra component, usually it is a Mobile H component */ /* corresponding to a free proton component in Fixed H */ pINChI[iINChI][k][j]->bDeleted = 1; pINChI_Aux[iINChI][k][j]->bDeleted = 1; } } /* k */ } /* if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) */ if ( OneInput->pInpInChI[iINChI][j] ) { INCHI_HEAPCHK inchi_free(OneInput->pInpInChI[iINChI][j]); OneInput->pInpInChI[iINChI][j] = NULL; } } /* j */ } /* iINChI */ if ( bReqSplitOutputInChI ) { if ( bHasSomeReconnected ) { iINChI1 = INCHI_REC; /* only reconnected */ iINChI2 = INCHI_NUM; sd->num_components[INCHI_BAS] = sd->num_components[INCHI_REC]; } else { iINChI1 = 0; /* only disconnected */ iINChI2 = iINChI1+1; } sd->num_components[INCHI_REC] = 0; /* treat reconnected as connected */ nNumOutputComponents = sd->num_components[INCHI_BAS]; } else { iINChI1 = 0; iINChI2 = INCHI_NUM; nNumOutputComponents = 1; } for ( k1 = 0, k2 = (bReqSplitOutputInChI? k1+1 : nNumOutputComponents); k1 < k2 && k1 < nNumOutputComponents; k1=k2, k2 ++ ) { if ( bReqSplitOutputInChI ) { sd->num_components[INCHI_BAS] = 1; sd->num_components[INCHI_REC] = 0; /* additional data */ sd->num_non_taut[INCHI_BAS] = sd->num_taut[INCHI_BAS] = sd->num_non_taut[INCHI_REC] = sd->num_taut[INCHI_REC] = 0; iINChI = iINChI1; for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) { for ( k = k1; k < k2; k ++ ) { /* find where the current processed structure is located */ int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; if ( cur_is_non_taut + cur_is_taut ) { /* count tautomeric and non-tautomeric components of the structures */ /* int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; */ sd->num_non_taut[INCHI_BAS] += cur_is_non_taut; sd->num_taut[INCHI_BAS] += cur_is_taut; } } } INCHI_HEAPCHK } else { sd->num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); sd->num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); /* additional data needed for SortAndPrintINChI() */ for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { sd->num_non_taut[iINChI] = sd->num_taut[iINChI] = 0; for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) { for ( k = k1; k < k2; k ++ ) { /* find where the current processed structure is located */ int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; if ( cur_is_non_taut + cur_is_taut ) { /* count tautomeric and non-tautomeric components of the structures */ /* int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; */ sd->num_non_taut[iINChI] += cur_is_non_taut; sd->num_taut[iINChI] += cur_is_taut; } } } } INCHI_HEAPCHK } if ( bReqSplitOutputInChI ) { /* output components one by one (for splitting input InChI into components) */ PINChI2 *pInChI_2[INCHI_NUM]; PINChI_Aux2 *pInChI_Aux_2[INCHI_NUM]; INChI *pInChI_1[1][2]; INChI_Aux *pInChI_Aux_1[1][2]; memset( pInChI_2, 0, sizeof(pInChI_2) ); memset( pInChI_Aux_2, 0, sizeof(pInChI_Aux_2) ); for ( j = 0; j < TAUT_NUM; j ++ ) { pInChI_1[0][j] = pINChI[iINChI1][k1][j]; pInChI_Aux_1[0][j] = pINChI_Aux[iINChI1][k1][j]; } pInChI_2[INCHI_BAS] = pInChI_1; pInChI_Aux_2[INCHI_BAS] = pInChI_Aux_1; /* make sure purely reconnected InChI is marked as ReChI, not InChI */ if ( bHasSomeReconnected && (bInChIHasReconnectedMetal( pInChI_1[0][TAUT_YES] ) || bInChIHasReconnectedMetal( pInChI_1[0][TAUT_NON] ) ) ) { bSortPrintINChIFlags = FLAG_SORT_PRINT_ReChI_PREFIX; } else { bSortPrintINChIFlags = 0; } INCHI_HEAPCHK nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, sd->num_components, sd->num_non_taut, sd->num_taut, sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, pInChI_2, pInChI_Aux_2, &bSortPrintINChIFlags, save_opt_bits); INCHI_HEAPCHK } else { INCHI_HEAPCHK bSortPrintINChIFlags = 0; nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, sd->num_components, sd->num_non_taut, sd->num_taut, sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, pINChI, pINChI_Aux, &bSortPrintINChIFlags, save_opt_bits); INCHI_HEAPCHK } if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) { break; } } INCHI_HEAPCHK FreeAllINChIArrays( pINChI, pINChI_Aux, num_components ); INCHI_HEAPCHK for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( OneInput->nNumProtons[iINChI][j].pNumProtons ) { inchi_free( OneInput->nNumProtons[iINChI][j].pNumProtons ); OneInput->nNumProtons[iINChI][j].pNumProtons = NULL; } } } INCHI_HEAPCHK if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) { ret2 = RI_ERR_PROGR; } exit_error: if ( pStr ) inchi_free( pStr ); return ret2; } /**************************************************************************************/ int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ) { int i, j, n_vertex, n_neigh, nNumNeigh, bTautAtom, nNumH, nTotNumNeigh, num_atoms; AT_NUMB taut_at_number; nAtNumber -= 1; nNumNeigh = 0; /* number of bonds */ bTautAtom = 0; /* 1 if atom belongs to a Mobile-H group */ nNumH = 0; /* number of terminal neighbors H */ num_atoms = pInChI->nNumberOfAtoms; /* from RestoreAtomConnectionsSetStereo() */ /* Connection table structure: Vert(1) [, Neigh(11), Neigh(12),...], Vert(2) [, Neigh(2,1), Neigh(2,2),...] ... where Neigh(i,1) < Neigh(i,2) <... < Vert(i); Vert(i) < Vert(i+1) */ for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { /* vertex - neighbor connection */ nNumNeigh += ( nAtNumber == n_vertex || nAtNumber == n_neigh ); } else /* n_neigh is the next vertex */ if ( (n_vertex = n_neigh) >= num_atoms ) { return RI_ERR_PROGR; } } /* is atom tautomeric, from GetTgroupInfoFromInChI() */ if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { int itg, len_tg; int tot_len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ j = 1; /* index in pInChI->nTautomer[] */ i = 0; /* index in ti->nEndpointAtomNumber[] */ for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ j += T_GROUP_HDR_LEN; /* skip t-group header */ len_tg -= T_GROUP_HDR_LEN-1; for( ; 0 < len_tg --; j ++, i ++ ) { taut_at_number = pInChI->nTautomer[j]-1; /* Mobile-H group atom number */ bTautAtom += (taut_at_number == nAtNumber); } } if ( i != tot_len_tg ) { return RI_ERR_PROGR; } } /* count hydrogen neighbors */ if ( pInChI->nNum_H ) { nNumH = pInChI->nNum_H[nAtNumber]; } /* conclusion: if not tautomeric then return positive number, otherwise add 1000 */ nTotNumNeigh = nNumNeigh + nNumH; if ( bTautAtom ) { nTotNumNeigh += 1000; } return nTotNumNeigh; } /**************************************************************************************/ int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, int *num_unk_und_SB, int *num_unk_und_SC, int *num_SC_PIII, int *num_SC_AsIII) { static U_CHAR el_number_P=0, el_number_As=0; INChI_Stereo *Stereo; int i, ret; AT_NUMB nAtNumber; U_CHAR el_number; if ( !pInChI->nNumberOfAtoms || pInChI->bDeleted ) { return 0; /* no InChI */ } Stereo = (pInChI->StereoIsotopic && (pInChI->StereoIsotopic->nNumberOfStereoBonds + pInChI->StereoIsotopic->nNumberOfStereoCenters ))? pInChI->StereoIsotopic: (pInChI->Stereo && (pInChI->Stereo->nNumberOfStereoBonds + pInChI->Stereo->nNumberOfStereoCenters ))? pInChI->Stereo : NULL; if ( !Stereo ) { return 1; /* No Stereo */ } /* one-time initialization */ if ( !el_number_P ) { el_number_P = (U_CHAR)get_periodic_table_number( "P" ); el_number_As = (U_CHAR)get_periodic_table_number( "As" ); } /* count SB and cumulenes */ for ( i = 0; i < Stereo->nNumberOfStereoBonds; i ++ ) { if ( ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]) ) { (*num_known_SB) ++; } else { (*num_unk_und_SB) ++; } } /* count SC and allenes */ for ( i = 0; i < Stereo->nNumberOfStereoCenters; i ++ ) { if ( !(nAtNumber = Stereo->nNumber[i]) || nAtNumber > pInChI->nNumberOfAtoms ) { return RI_ERR_PROGR; /* wrong data, should never happen */ } if ( ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]) ) { (*num_known_SC) ++; } else { (*num_unk_und_SC) ++; } el_number = pInChI->nAtom[nAtNumber-1]; if ( el_number != el_number_P && el_number != el_number_As ) { continue; } ret = GetNumNeighborsFromInchi( pInChI, nAtNumber ); if ( ret < 0 ) { return ret; } if ( 3 == ret ) { *num_SC_PIII += (el_number_P == el_number); *num_SC_AsIII += (el_number_As == el_number); } } return 2; /* Has Stereo */ } /**************************************************************************************/ int bInpInchiComponentExists( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) { if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { return 0; } return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && pOneInput->pInpInChI[iInChI][bMobileH] && pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && !pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); } /**************************************************************************************/ int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) { if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { return 0; } return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && pOneInput->pInpInChI[iInChI][bMobileH] && pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); } /**************************************************************************************/ int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) { if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || INCHI_BAS != iInChI && iInChI != INCHI_REC || TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { return 0; } return ( k < pStruct->RevInChI.num_components[iInChI] && pStruct->RevInChI.pINChI[iInChI] && pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && !pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); } /**************************************************************************************/ int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) { if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || INCHI_BAS != iInChI && iInChI != INCHI_REC || TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { return 0; } return ( k < pStruct->RevInChI.num_components[iInChI] && pStruct->RevInChI.pINChI[iInChI] && pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); } /**************************************************************************************/ int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, int *bHasFixedH, int *nModeFlagsStereo, int *bTautFlagsStereo ) { int ret=0, bHasStereo; int nModeFlagsValue=0, bTautFlagsValue; /* stereo flags */ int iInChI, iMobileH, bIso, k, max_components, num_components; INChI *pInChI; int num_known_SB /*Stereo Bonds & Cumulenes >C==C==C==C< */; int num_known_SC /* Stereo Centers & Allenes >C=C=C< */; int num_unk_und_SB, num_unk_und_SC; int num_SC_PIII, num_SC_AsIII; /* has Phosphine or Arsine stereo center(s) */ *bHasReconnected = *bHasFixedH = *nModeFlagsStereo = *bTautFlagsStereo = 0; nModeFlagsValue = bTautFlagsValue = bHasStereo = 0; num_known_SB = num_known_SC = num_unk_und_SB = num_unk_und_SC = num_SC_PIII = num_SC_AsIII = 0; *bHasMetal = 0; for ( iInChI = 0; iInChI < INCHI_NUM; iInChI ++ ) { for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { for ( bIso = 1; !nModeFlagsValue && 0 <= bIso; bIso -- ) { switch( pOneInput->s[iInChI][iMobileH][bIso] ) { case 1: /* SABS */ nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; break; case 2: nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RELATIVE_STEREO; break; case 3: nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RACEMIC_STEREO; } } max_components = pOneInput->pInpInChI[iInChI][iMobileH]? pOneInput->nNumComponents[iInChI][iMobileH] : 0; for ( k = num_components = 0; k < max_components; k ++ ) { pInChI = pOneInput->pInpInChI[iInChI][iMobileH] + k; ret = CountStereoTypes(pInChI, &num_known_SB, &num_known_SC, &num_unk_und_SB, &num_unk_und_SC, &num_SC_PIII, &num_SC_AsIII); if ( ret < 0 ) { return ret; /* error */ } bHasStereo += (ret == 2); if ( (ret > 0) ) { /* ret == 0 => Empty InChI, 1=> No Stereo, 2=> Has Stereo */ num_components ++; *bHasReconnected |= ( iInChI == INCHI_REC ); *bHasFixedH |= ( iMobileH == TAUT_NON ); } *bHasMetal |= bInChIHasReconnectedMetal( pInChI ); } } } if ( (nModeFlagsValue & REQ_MODE_RELATIVE_STEREO) && (nModeFlagsValue & REQ_MODE_RACEMIC_STEREO) ) { return RI_ERR_SYNTAX; } if ( bHasStereo && !nModeFlagsValue ) /* REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ { /* inversion does not change the stereo or no stereo at all */ nModeFlagsValue = REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; /* Abs */ } if ( !num_known_SB && num_unk_und_SB ) { ; /* full SUU option or SB part of it */ } else { nModeFlagsValue |= REQ_MODE_SB_IGN_ALL_UU; /* ignore Unknown/Undefind SB if no well-defined SB exist */ } if ( !num_known_SC && num_unk_und_SC ) { ; /* full SUU option or SB part of it */ } else { nModeFlagsValue |= REQ_MODE_SC_IGN_ALL_UU; /* ignore Unknown/Undefind SC if no well-defined SB exist */ } /* Phosphine and Arsine Stereo */ if ( num_SC_PIII ) { bTautFlagsValue |= TG_FLAG_PHOSPHINE_STEREO; } /* Phosphine and Arsine Stereo */ if ( num_SC_AsIII ) { bTautFlagsValue |= TG_FLAG_ARSINE_STEREO; } *nModeFlagsStereo = nModeFlagsValue; *bTautFlagsStereo = bTautFlagsValue; return 0; } /******************************************************************************************************/ int bInChIHasReconnectedMetal( INChI *pInChI ) { int i; if ( pInChI && !pInChI->bDeleted && pInChI->nNumberOfAtoms && pInChI->nAtom ) { for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { if ( is_el_a_metal( (int)pInChI->nAtom[i] ) ) { if ( pInChI->nNumberOfAtoms > 1 || pInChI->nNum_H && pInChI->nNum_H[0] ) { return 1; } } } } return 0; } /*****************************************************************************************/ int SetProtonsAndXchgIsoH( int bInChI2Structure, int bReqSplitOutputInChI, int bReqProtonsForEachComponent, int bReqNonTaut, int bReqStereo, int num_components[INCHI_NUM], MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], InpInChI *OneInput ) { int j, k, k1, ret2=0, iINChI; int bAvailableProtonsForEachComponent, bAvailableProtonsTotal; INCHI_HEAPCHK num_components[INCHI_BAS] = num_components[INCHI_REC] = 0; for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; /* are totals of /p and/or /i/h available ? */ bAvailableProtonsTotal = 0 != OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { bAvailableProtonsTotal |= 0 !=OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1]; } /* are /p and/or /i/h available for each component ? */ bAvailableProtonsForEachComponent = (NULL != OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); /* decision: add /p to each component, add total to the 1st, add total as one more component */ /* In case of bInChI2Structure just keep totals if not available for each component */ if ( bInChI2Structure ) { nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? MODE_PIXH_ADD_TO_EACH: MODE_PIXH_KEEP_TOTALS; } else if ( !bReqSplitOutputInChI ) { nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? MODE_PIXH_ADD_TO_EACH : MODE_PIXH_ADD_TO_FIRST; } else if ( !bAvailableProtonsForEachComponent ) { nModeProtonIsoExchgH[iINChI] = bAvailableProtonsTotal? MODE_PIXH_ADD_A_PIXH_COMPONENT : MODE_PIXH_ADD_TO_FIRST; } else /* bAvailableProtonsForEachComponent && bReqSplitOutputInChI */ if ( bReqProtonsForEachComponent ) { nModeProtonIsoExchgH[iINChI] = MODE_PIXH_ADD_TO_EACH; } else { nModeProtonIsoExchgH[iINChI] = bReqNonTaut? MODE_PIXH_ADD_TO_EACH : MODE_PIXH_ADD_A_PIXH_COMPONENT; } /* remove unneeded data: protons for each component */ if ( bAvailableProtonsForEachComponent && nModeProtonIsoExchgH[iINChI] != MODE_PIXH_ADD_TO_EACH ) { inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ); OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; bAvailableProtonsForEachComponent = 0; } /* remove unneeded data: total protons all components */ if ( bAvailableProtonsTotal && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH ) { OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons = 0; for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1] = 0; } bAvailableProtonsTotal = 0; } /* remove unneeded data: Fixed-H InChI; no protons data exist for Fixed-H */ if ( !bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { j = TAUT_NON; for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); } inchi_free( OneInput->pInpInChI[iINChI][j] ); OneInput->pInpInChI[iINChI][j] = NULL; OneInput->nNumComponents[iINChI][j] = 0; } #ifdef NEVER /* remove unneeded data: Mobile-H InChI ????? */ if ( bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { j = TAUT_YES; for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); } inchi_free( OneInput->pInpInChI[iINChI][j] ); OneInput->pInpInChI[iINChI][j] = NULL; OneInput->nNumComponents[iINChI][j] = 0; nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; if ( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; } } #endif /* add one more component containing only /p and /i/h */ if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT && OneInput->nNumComponents[iINChI][TAUT_YES] || /* always add one deleted component if no non-taut InChI is available */ bInChI2Structure && !bAvailableProtonsForEachComponent && !OneInput->nNumComponents[iINChI][TAUT_NON] && OneInput->nNumComponents[iINChI][TAUT_YES] ) { int nPrevLen, nLen=0; j = TAUT_YES; nPrevLen = OneInput->nNumComponents[iINChI][j]; for ( k = 0; k < nPrevLen; k ++ ) { nLen += !OneInput->pInpInChI[iINChI][j][k].bDeleted; } if ( nLen == nPrevLen ) { /* add one more component */ INChI *pInChI = (INChI *)inchi_calloc( nLen+1, sizeof(*pInChI) ); if ( !pInChI ) { ret2 = RI_ERR_ALLOC; goto exit_error; } memcpy( pInChI, OneInput->pInpInChI[iINChI][j], nLen*sizeof(*pInChI) ); inchi_free( OneInput->pInpInChI[iINChI][j] ); OneInput->pInpInChI[iINChI][j] = pInChI; } OneInput->nNumComponents[iINChI][j] = nLen+1; for ( k = nLen; k < nPrevLen; k ++ ) { Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); memset( &OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); } /* mark the last component as a proton */ if ( 0 > (ret2 = nFillOutProtonMobileH( OneInput->pInpInChI[iINChI][j]+nLen ) ) ) { goto exit_error; } } INCHI_HEAPCHK /* remove unneeded Stereo and/or Fixed H */ if ( !bReqStereo ) { for ( j = 0; j < TAUT_NUM; j ++ ) { for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { if ( OneInput->pInpInChI[iINChI][j][k].Stereo ) { Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].Stereo); inchi_free( OneInput->pInpInChI[iINChI][j][k].Stereo ); OneInput->pInpInChI[iINChI][j][k].Stereo = NULL; } if ( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ) { Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].StereoIsotopic); inchi_free( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ); OneInput->pInpInChI[iINChI][j][k].StereoIsotopic = NULL; } INCHI_HEAPCHK } } } } num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); exit_error: return ret2; } /******************************************************************************************/ int GetInChIFormulaNumH( INChI *pInChI, int *nNumH ) { /* get number of H including bridging hydrogen atoms */ const char *p, *q; *nNumH = 0; if ( pInChI->szHillFormula ) { for ( p = strchr( pInChI->szHillFormula, 'H'); p; p = strchr(p, 'H') ) { p ++; if ( !islower( UCINT *p ) ) { /* found hydrogen in the formula */ if ( isdigit( UCINT *p ) ) { *nNumH += (int)inchi_strtol( p, &q, 10 ); p = q; } else { *nNumH += 1; } } } } return 0; } /******************************************************************************************/ int GetInChINumH( INChI *pInChI, int *nNumH ) { int i, j, nNumTautGroups, iTautGroup, nTautGroupLen, lenTautomer; *nNumH = 0; for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { *nNumH += ( pInChI->nAtom[i] == EL_NUMBER_H ); /* bridging H */ *nNumH += pInChI->nNum_H[i]; } /* earlier nNum_H_fixed[] should have been added to pInChI->nNum_H[] */ /* if ( pInChI->nNum_H_fixed ) { for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { *nNumH += pInChI->nNum_H_fixed[i]; } } */ if ( pInChI->lenTautomer > 3 && pInChI->nTautomer ) { lenTautomer = pInChI->lenTautomer; j = 0; nNumTautGroups = pInChI->nTautomer[j ++]; for ( iTautGroup = 0; j < lenTautomer && iTautGroup < nNumTautGroups; iTautGroup ++, j += nTautGroupLen ) { nTautGroupLen = pInChI->nTautomer[j]+1; *nNumH += pInChI->nTautomer[j+1]; } if ( iTautGroup != nNumTautGroups || j != lenTautomer ) { return RI_ERR_PROGR; } } if ( pInChI->nNum_H_fixed && (pInChI->lenTautomer || pInChI->nTautomer) ) { return RI_ERR_PROGR; } return 0; } /******************************************************************************************/ int GetInChIIsoH( INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES] ) { int i; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { nNumIsotopicH[i] = 0; } for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { if ( pInChI->IsotopicAtom[i].nIsoDifference > 0 && pInChI->IsotopicAtom[i].nIsoDifference <= NUM_H_ISOTOPES ) { if ( !pInChI->nAtom || !pInChI->IsotopicAtom[i].nAtomNumber || pInChI->IsotopicAtom[i].nAtomNumber > pInChI->nNumberOfAtoms ) { return RI_ERR_PROGR; } if ( pInChI->nAtom[pInChI->IsotopicAtom[i].nAtomNumber-1] == EL_NUMBER_H ) { /* isotopic H in connection table */ nNumIsotopicH[ pInChI->IsotopicAtom[i].nIsoDifference-1 ] ++; } } nNumIsotopicH[0] += pInChI->IsotopicAtom[i].nNum_H; nNumIsotopicH[1] += pInChI->IsotopicAtom[i].nNum_D; nNumIsotopicH[2] += pInChI->IsotopicAtom[i].nNum_T; } return 0; } /******************************************************************************************/ typedef struct tagNumElem { int num; /* int iso; */ } NUM_ELEM; /***************************************************************************************/ int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, char **pStr, int *pState, int *nErr, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], int s[INCHI_NUM][TAUT_NUM][2], int bReadCoord, int bInchi2Struct, INCHI_MODE nMode, int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) { int iINChI, i, j, k, m, len1, len2, ret2 = 0, retAux = 0, stateAux = 0; int ret, tot_charge[INCHI_NUM][TAUT_NUM]; int i1, i2, i3; int kc; NUM_ELEM *num_elem[INCHI_NUM][TAUT_NUM]; #if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ /* (converter does not placed proper stereo to output) */ /* set new stereo type as requested by conversion option */ int target_stereo_type = 1; if (nMode & REQ_MODE_RELATIVE_STEREO) target_stereo_type = 2; else if (nMode & REQ_MODE_RACEMIC_STEREO) target_stereo_type = 3; #endif memset( num_elem, 0, sizeof(num_elem) ); ret = ReadInChILine(pInp, pLine, pStr, pState, pInpInChI, nNumComponents, nNumProtons, s, bStdFormat, bInputHasSaveOpt, inp_save_opt_bits); #if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) /* modify stereo type for layers as requested */ if (target_stereo_type > 1) for (i1=0;i1SAbs, SRac=>SAbs */ s[i1][i2][i3] = target_stereo_type; } #endif *nErr = 0; if ( (ret == RI_ERR_EOL) && nNumComponents[INCHI_BAS][TAUT_YES] + nNumComponents[INCHI_BAS][TAUT_NON] && bReadCoord ) { retAux = ReadInChICoord( pInp, pLine, &stateAux, pInpInChI, nNumComponents ); } if ( (ret == RI_ERR_EOL || ret == RI_ERR_EOF) && nNumComponents[INCHI_BAS][TAUT_YES] + nNumComponents[INCHI_BAS][TAUT_NON] ) { /* post-processing: add omitted layers */ *pState = IST_MATERIAL_BALANCE_ERROR; for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { /* for Mobile/Fixed H (j) ... */ int bIsotopic, bStereoType, bStereoTypeAlt; int nMH2FH_AltInv=0, nFH2iFH_AltInv=0 /*, niMH2iFH_AltInv=0, nMH2iMH_AltInv=0*/; int jAlt = ALT_TAUT(j); INCHI_MODE nFlags = 0, nFlagsAlt = 0; /* get stereo type: ABS, REL, RAC, or nothing */ tot_charge[iINChI][j] = 0; for ( bIsotopic = bStereoType = bStereoTypeAlt = 0; bIsotopic < 2; bIsotopic ++ ) { if ( !bStereoType || bStereoType < s[iINChI][j][bIsotopic] ) { bStereoType = s[iINChI][j][bIsotopic]; } if ( !bStereoTypeAlt || bStereoTypeAlt < s[iINChI][jAlt][bIsotopic] ) { bStereoTypeAlt = s[iINChI][jAlt][bIsotopic]; } nFlags = bStereoType ==2? INCHI_FLAG_REL_STEREO : bStereoType ==3? INCHI_FLAG_RAC_STEREO : 0; nFlagsAlt = bStereoTypeAlt==2? INCHI_FLAG_REL_STEREO : bStereoTypeAlt==3? INCHI_FLAG_RAC_STEREO : 0; } /* set stereo type to each component */ /* add missing nNum_H and nConnTable */ if ( nNumComponents[iINChI][j] ) { num_elem[iINChI][j] = (NUM_ELEM *)inchi_calloc( nElDataLen+1, sizeof(num_elem[0][0][0]) ); if ( !num_elem[iINChI][j] ) { ret2 = RI_ERR_ALLOC; goto exit_function; } } for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { /* for each component k ... */ if ( pInpInChI[iINChI][j] ) { INChI *pInChI = &pInpInChI[iINChI][j][k]; INChI *pInChI_Alt = (knNumberOfAtoms)? pInpInChI[iINChI][jAlt]:NULL;*/ /* 2007-09-25 DT */ pInpInChI[iINChI][jAlt][k].nNumberOfAtoms)? &pInpInChI[iINChI][jAlt][k]:NULL; if ( nFlags ) { pInChI->nFlags |= nFlags; } else if ( j == TAUT_NON && !nFlags && nFlagsAlt ) { pInChI->nFlags |= nFlagsAlt; } /**** add empty immobile H (nNum_H) if it is missing ****/ if ( !pInChI->nNum_H && !(pInChI->nNum_H = (S_CHAR *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nNum_H[0]) ) ) ) { ret2 = RI_ERR_ALLOC; goto exit_function; } /**** add single atom nConnTable if it is missing ****/ if ( !pInChI->nConnTable ) { AT_NUMB *pCT; int lenCT; if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] && (pCT = pInpInChI[iINChI][TAUT_YES][k].nConnTable) && (lenCT = pInpInChI[iINChI][TAUT_YES][k].lenConnTable) > 0) { if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( lenCT+1, sizeof(pInChI->nConnTable[0]) ) ) ) { ret2 = RI_ERR_ALLOC; goto exit_function; } memcpy( pInChI->nConnTable, pCT, lenCT*sizeof(pInChI->nConnTable[0]) ); pInChI->lenConnTable = lenCT; } else { if ( j == TAUT_YES && pInChI->nNumberOfAtoms > 1 ) { *pState = IST_MOBILE_H_CONNECTIONS + (iINChI==INCHI_REC? IST_HAPPENED_IN_RECMET : 0); ret2 = RI_ERR_SYNTAX; goto exit_function; } if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nConnTable[0]) ) ) ) { ret2 = RI_ERR_ALLOC; goto exit_function; } pInChI->lenConnTable = 1; pInChI->nConnTable[0] = 1; } } else if ( pInChI->nConnTable && !pInChI->lenConnTable && pInChI->nNumberOfAtoms == 1 ) { pInChI->nConnTable[0] = 1; pInChI->lenConnTable = 1; } /**** copy charge: Mobile H --> Fixed H; ****/ if ( j == TAUT_NON ) { /* if ( pInChI->nTotalCharge == NO_VALUE_INT ) { pInChI->nTotalCharge = 0; } else */ if ( !pInChI->nTotalCharge && k < nNumComponents[iINChI][TAUT_YES] ) { INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ if ( pAltInChI->nTotalCharge && pAltInChI->nTotalCharge != NO_VALUE_INT ) { pInChI->nTotalCharge = pAltInChI->nTotalCharge; } } } /***** Fixed H: add pInChI->nNum_H_fixed to pInChI->nNum_H ****/ if ( j == TAUT_NON && pInChI->nNum_H && pInChI->nNum_H_fixed ) { for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { pInChI->nNum_H[m] += pInChI->nNum_H_fixed[m]; } } /***** copy isotopic atoms: Mobile H --> Fixed H ******/ if ( j == TAUT_YES && pInChI->nNumberOfIsotopicAtoms && k < nNumComponents[iINChI][TAUT_NON] ) { INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ if ( !pAltInChI->nNumberOfIsotopicAtoms ) { ret2=CopySegment( pAltInChI, pInChI, CPY_ISO_AT, 0, 0); if ( ret2 < 0 ) { goto exit_function; } } } /**** copy coordinates: Mobile H --> Fixed H ******/ if ( j == TAUT_YES && pInChI->IsotopicTGroup && k < nNumComponents[iINChI][TAUT_NON] ) { INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ if ( !pAltInChI->IsotopicTGroup ) { XYZ_COORD *pxyz = (XYZ_COORD *)inchi_calloc( pInChI->nNumberOfAtoms, sizeof(pxyz[0])); if ( pxyz ) { memcpy( pxyz, pInChI->IsotopicTGroup, pInChI->nNumberOfAtoms * sizeof(pxyz[0]) ); pAltInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; } else { ret2 = RI_ERR_ALLOC; goto exit_function; } } } /******************************************************** * * * Restore omitted stereo seqments * * * * order of restoring: * * * * 1. Fixed H (F) -> (FI) Isotopic Fixed H * * 2. Mobile H (M) -> (F) Fixed H * * 3. Isotopic Mobile H (MI) -> (FI) Isotopic Fixed H * * 4. Mobile H (M) -> (MI) Isotopic Mobile H * * * ********************************************************/ /***** (4) copy stereo: Mobile H --> isotopic Mobile H ******/ if ( j == TAUT_YES ) { int bIso = pInChI->nNumberOfIsotopicAtoms || (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters + pInChI->StereoIsotopic->nNumberOfStereoBonds) || pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; /* non-isotopic Mobile H => isotopic Mobile H */ if ( bIso ) { if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { goto exit_function; } if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; } if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; } } } else /* copy sp3 inversion info: non-isotopic Mobile H => isotopic Mobile H */ if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && pInChI->Stereo->nCompInv2Abs ) { if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; } else if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs ) { pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; } } } if ( bIso && pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { goto exit_function; } } } /***** (0) set nCompInv2Abs to Fixed-H *********************************/ if ( j == TAUT_NON ) { if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { /* case of /sN and /t... in non-isotopic Mobile-H, no /s in non-isotopic Fixed-H */ if ( !s[iINChI][j][0] && s[iINChI][jAlt][0]>0 && /* /sN is not present in F and is present in M */ pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters ) { /* inherit from Mobile-H */ /* /s1 in M and MI; /m1 or /m0 in MI; /m. in M; no /m in F. Inherit MI->FI. Added 10-15-2007 */ if ( pInChI_Alt->Stereo->nCompInv2Abs == 0 && /* M: /m. ; means no /m for this component */ pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && /* F: no /m segment for all components */ pInChI_Alt->StereoIsotopic && /* MI: present */ pInChI_Alt->StereoIsotopic->nCompInv2Abs != 0 && pInChI_Alt->StereoIsotopic->nCompInv2Abs != NO_VALUE_INT && /* MI: /m0 or /m1 */ !s[iINChI][j][0] && !s[iINChI][j][1] && /* F, FI: no /s */ s[iINChI][jAlt][0] == 1 && s[iINChI][jAlt][1] == 1 /* M, MI: /s1 and /s1 */ ) { /* copy /m from MI to FI */ if ( 0 > (ret2 = CopySegment( pInChI, pInChI_Alt, CPY_SP3_M, 1, 1)) ) { goto exit_function; } } else /* the following if(){...} was added to fix m1 bug 2007-09-25 DT */ if ( pInChI_Alt->Stereo->nCompInv2Abs != NO_VALUE_INT && s[iINChI][jAlt][0] == 1 ) { pInChI->Stereo->nCompInv2Abs = pInChI_Alt->Stereo->nCompInv2Abs; } else /* M and MI contain /sN and /sN, N=2,3. Added 10-15-2007 */ if ( pInChI_Alt->Stereo->nCompInv2Abs == NO_VALUE_INT && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && !s[iINChI][j][0] && !s[iINChI][j][1] && (s[iINChI][jAlt][0] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && (s[iINChI][jAlt][1] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { int bIso = pInChI->nNumberOfIsotopicAtoms || (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters + pInChI->StereoIsotopic->nNumberOfStereoBonds) || pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; if ( bIso ){ if ( !pInChI_Alt->StereoIsotopic && /* create zero/NULL-initialized pInChI_Alt->StereoIsotopic */ 0 > (ret2 = CopySegment( pInChI_Alt, pInChI_Alt, CPY_SP3_M, 1, -1))) { goto exit_function; } pInChI_Alt->StereoIsotopic->nCompInv2Abs = 2; /* MI: /m1 or /m0 */ pInChI_Alt->Stereo->nCompInv2Abs = 0; /* M: /m. ; no /m for this component */ pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* FI: Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ } else { pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ } } else { pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ } } else /* case of /sN in Isotopic Fixed-H only, /t... in Fixed-H, no /m (2007-08-27 DT) */ if ( !s[iINChI][j][0] && !s[iINChI][jAlt][0] && /* /sN in Fixed-H isotopic only */ (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && /*!(pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters) &&*/ !(pInChI_Alt && pInChI_Alt->StereoIsotopic && pInChI_Alt->StereoIsotopic->nNumberOfStereoCenters) ) { pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ } else { pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; } } } /***** (1) copy stereo: non-isotopic Fixed H --> isotopic Fixed H ******/ if ( j == TAUT_NON ) { int bIso = pInChI->nNumberOfIsotopicAtoms || (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters + pInChI->StereoIsotopic->nNumberOfStereoBonds) || pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; /* non-isotopic Fixed H => isotopic Fixed H */ if ( bIso ) { if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { /* -- replaced 2007-08-27 by (aaa), see below -- DT if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { goto exit_function; } */ /*----------- replacement (aaa) begin 2007-08-27 DT */ if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) ) { goto exit_function; } if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT+1 ) { pInChI->Stereo->nCompInv2Abs = 0; pInChI->StereoIsotopic->nCompInv2Abs = 2; } else if ( !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { goto exit_function; } /*----------- replacement (aaa) end 2007-08-27 DT */ if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; } if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; } } #ifdef NEVER if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && !s[iINChI][j][0] && s[iINChI][j][0]>0 ) { /* copied Rel/Rac stereo to Iso; /s is in Iso /s is not in non-Iso */ /* this means all difference in stereo is in inversion */ if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { pInChI->Stereo->nCompInv2Abs = 0; /* missing */ pInChI->StereoIsotopic->nCompInv2Abs = 2; /* unusual value */ } } #endif } else /* copy sp3 inversion info: non-isotopic Fixed H --> isotopic Fixed H */ if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && pInChI->Stereo->nCompInv2Abs ) { if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; } else if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) { pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; } } } if ( bIso && pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { goto exit_function; } } } /***** copy stereo: Mobile H --> Fixed H ******/ if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] ) { INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ int bIso = pInChI->nNumberOfIsotopicAtoms || (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters + pInChI->StereoIsotopic->nNumberOfStereoBonds) || pAltInChI && ( pAltInChI->nNumberOfIsotopicAtoms || (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters + pAltInChI->StereoIsotopic->nNumberOfStereoBonds) ); int bNo_InChI_t = (!pInChI->Stereo || !pInChI->Stereo->t_parity); int bNo_InChI_m = (!pInChI->Stereo || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs); /* (2) non-isotopic Mobile H => non-isotopic Fixed H */ if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && (!pInChI->Stereo || !pInChI->Stereo->t_parity) ) { #if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) /* (2008-04-02) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ /* (converter skipped empty '/t' or sometimes produced an excess one */ /* check whether t stereo is actually present */ int bHave_t_stereo = 1; if (pInChI->Stereo) bHave_t_stereo = pInChI ->Stereo->nNumberOfStereoCenters; /* account for stereobonds present */ if ( bHave_t_stereo < 1 ) if ( pInChI->Stereo->nNumberOfStereoBonds > 0 ) bHave_t_stereo=1; /* copy stereo anyway ... */ #endif if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 0, 0)) || (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 0, 0)) ) { goto exit_function; } #if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) /* ... correct just copied stereo if applicable */ if ( (s[iINChI][j][0] < 1) && (bHave_t_stereo <1 ) && (pAltInChI->Stereo->nNumberOfStereoCenters > 0) && (s[iINChI][jAlt][0] < 1) ) { /* (2010-02-28) if not all stereo centers are unknown/undefined */ /* at which condition stereo still should present .. */ int all_UU = 1; for (kc=0; kcStereo->nNumberOfStereoCenters; kc++) { if ( (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNKN) && (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNDF) ) { all_UU=0; break; } } if (!all_UU) pInChI->Stereo->nNumberOfStereoCenters = 0; } #endif /* in case of missing nCompInv2Abs, 2005-05-10 */ if ( (pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT) && (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { if ( s[iINChI][jAlt][0] > 0 && s[iINChI][j][0] > 0 ) { /* suppose once in a while only non-taut stereo changes if inverted */ pAltInChI->Stereo->nCompInv2Abs = (++nMH2FH_AltInv)%2? 2:0; pInChI->Stereo->nCompInv2Abs = 2; } else /* Mobile-H: /t.. /sN; Mobile-H isotopic: /sN (n=2 or 3), not /t...; Fixed-H layer is present, has no /t, no /i/t */ /* Mobile-H /sN was caused by another component that would have same /mN in all layers */ /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ /* added 10-11-2007 to fix i2i bug for Rel/Rac stereo */ if ( nNumComponents[iINChI][j] > 1 && bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && /* /sN, N=2 or 3 only in Mobile-H AND Mobile-H isotopic */ (s[iINChI][jAlt][0] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) && (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) { if ( bIso ) { /* create two zero/NULL-initialized isotopic stereo if they do not exist */ if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) /* -- the following will be created later, in TAUT_YES part of the code -- */ || !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1)) ) { goto exit_function; } /* same value = 2 for MI and FI; here we assign only FI */ pInChI->StereoIsotopic->nCompInv2Abs = 2; pInChI->Stereo->nCompInv2Abs = 0; /* -- the following will NOT be assigned later, in TAUT_YES part of the code -- */ pAltInChI->StereoIsotopic->nCompInv2Abs = 2; pAltInChI->Stereo->nCompInv2Abs = 0; /* */ } else { if ( NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs && NO_VALUE_INT == pAltInChI->Stereo->nCompInv2Abs ) { pInChI->Stereo->nCompInv2Abs = 2; pAltInChI->Stereo->nCompInv2Abs = 2; } } } else if ( (s[iINChI][jAlt][0] > 0 || s[iINChI][j][0] > 0) && s[iINChI][j][0] >= 0 ) pInChI->Stereo->nCompInv2Abs = 2; else /* Mobile-H: /t..., no /sN; Mobile-H isotopic: /s2 or /s3, not /t; Fixed-H layer is present, has no /t, no /i/t */ /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ /* added 10-10-2007 to fix i2i bug for Rel/Rac stereo */ if ( bIso && bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && s[iINChI][jAlt][0]==0 && s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && /* /sN, N=2 or 3 only in Mobile-H isotopic */ (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) { /* create two zero/NULL-initialized isotopic stereo if they do not exist */ if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) /* -- the following will be created later, in TAUT_YES part of the code -- */ /*|| !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1))*/ ) { goto exit_function; } /* same value = 2 for MI and FI; here we assign only FI */ pInChI->StereoIsotopic->nCompInv2Abs = 2; pInChI->Stereo->nCompInv2Abs = 0; /* -- the following will be assigned later, in TAUT_YES part of the code -- */ /* pAltInChI->StereoIsotopic->nCompInv2Abs = 2; pAltInChI->Stereo->nCompInv2Abs = 0; */ } else pInChI->Stereo->nCompInv2Abs = 0; if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { pInChI->nFlags |= ((nFlagsAlt|nFlags) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); } } } else /* copy sp3 inversion info: non-isotopic Mobile H => non-isotopic Fixed H */ if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && pAltInChI->Stereo->nCompInv2Abs && (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) ) { if ( !(nFlagsAlt && !nFlags ) || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs ) { /* ??? */ pInChI->Stereo->nCompInv2Abs = pAltInChI->Stereo->nCompInv2Abs; } } /* use same rule to copy stereobonds */ if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds && (!pInChI->Stereo || !pInChI->Stereo->b_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 0, 0)) ) { goto exit_function; } } /* (3) isotopic Mobile H -> isotopic Fixed H */ /* if !FH_Stereo && !MH_Stereo && MH_IsoStereo!=NULL && FH_IsoStereo==NULL */ if ( bIso ) { if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo */ !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo */ (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo */ (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo */ /* copy sp3 iso stereo MI->FI (/t) and, if FH nCompInv2Abs (/m) is missing, copy it, too, MI->FI */ if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 1, 1)) || (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { goto exit_function; } /* in case of missing nCompInv2Abs, Relative or Racemic stereo 2005-05-10 */ if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][jAlt][1]>0? 2 : 0; if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { pInChI->nFlags |= (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); } } } else /* copy sp3 inversion info only: isotopic Mobile H -> isotopic Fixed H */ if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo /t */ !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo /t */ (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo /t */ (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && /* FH_IsoStereo /t */ pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { /* !FH_IsoStereo /m */ /* added 02-09-2006 */ if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { goto exit_function; } } /* use same rule to copy stereobonds */ if ( !(pInChI->Stereo && pInChI->Stereo->b_parity) && !(pAltInChI->Stereo && pAltInChI->Stereo->b_parity) && (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { goto exit_function; } } /* (4) Copy Fixed-H -> isotopic Fixed-H */ /* if FH_Stereo && !MH_IsoStereo && && !FH_IsoStereo */ if ( (pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters) && /* FH_Stereo /t */ !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && /* !MH_IsoStereo /t */ !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo /t */ /* added 10-10-2007 DT: copy MH_Iso /m => FH_Iso /m to fix i2i bug for Abs stereo */ /* InChI string contains: MH(/t...), MH_Iso(/mN, no /t), FH(no /t /m), FH_Iso(no /t /m) */ if ( pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ bNo_InChI_t && NO_VALUE_INT != pAltInChI->StereoIsotopic->nCompInv2Abs && /* undef FH_IsoStereo /m */ !(pInChI->StereoIsotopic && NO_VALUE_INT != pInChI->StereoIsotopic->nCompInv2Abs)) { if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1))) { goto exit_function; } } /* added 05-09-2006: copy sp3 FH=>FH_Iso */ if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { goto exit_function; } /* in case of missing nCompInv2Abs, Relative or Racemic stereo, /sN in Fixed-H, 2005-05-10 */ if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { if ( s[iINChI][j][0] > 0 && s[iINChI][j][1] > 0 ) { /* suppose once in a while only non-taut stereo changes if inverted */ pInChI->StereoIsotopic->nCompInv2Abs = 2; pInChI->Stereo->nCompInv2Abs = (++nFH2iFH_AltInv)%2? 2:0; } else if ( (s[iINChI][j][0] > 0 || s[iINChI][j][1] > 0) && s[iINChI][j][1] >= 0 ) /* ??? != NO_VALUE_INT ??? */ pInChI->StereoIsotopic->nCompInv2Abs = 2; else pInChI->StereoIsotopic->nCompInv2Abs = 0; if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { pInChI->nFlags |= (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); } } } else /* copy sp3 inversion info only: Fixed-H -> isotopic Fixed H */ if ( (pInChI->Stereo && pInChI->Stereo->t_parity) && !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && pInChI->Stereo->nCompInv2Abs && (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { /* added 05-09-2006 */ if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { goto exit_function; } } } if ( bIso && !(pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds) && !(pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds) && (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { goto exit_function; } } } } } /* end of component cycle (k) */ } /* end of Mobile/Fixed H cycle (j) */ /**** replace NO_VALUE_INT with zeroes in all Mobile & Fixed H components ****/ for ( j = 0; j < TAUT_NUM; j ++ ) { for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { if ( pInpInChI[iINChI][j] ) { INChI *pInChI = &pInpInChI[iINChI][j][k]; if ( pInChI->nTotalCharge == NO_VALUE_INT ) { pInChI->nTotalCharge = 0; } if ( pInChI->Stereo && pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { if ( pInChI->Stereo->nNumberOfStereoCenters && pInChI->Stereo->nCompInv2Abs != NO_VALUE_INT ) { pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; } } /* Add special nCompInv2Abs=2 to force /s2 or /s3 in InChI output */ if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && pInChI->Stereo->nNumberOfStereoCenters ) { pInChI->Stereo->nCompInv2Abs = (s[iINChI][j][0]>0 /*|| s[iINChI][j][1]>0*/)? 2 : 0; /* we do not know the real value */ } else { pInChI->Stereo->nCompInv2Abs = 0; } } if ( pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && pInChI->StereoIsotopic->nNumberOfStereoCenters ) { pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; /* we do not know the real value */ } else { pInChI->StereoIsotopic->nCompInv2Abs = 0; } } /* added 02-07-2006 */ if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT || pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { ret2 = RI_ERR_PROGR; goto exit_function; } if ( !pInChI->bDeleted && pInChI->nNumberOfAtoms ) { tot_charge[iINChI][j] += pInChI->nTotalCharge; for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { if ( pInChI->nAtom[m] < EL_NUMBER_H || pInChI->nAtom[m] > nElDataLen ) { ret2 = RI_ERR_PROGR; goto exit_function; } /* all atoms except H */ if ( pInChI->nAtom[m] > EL_NUMBER_H ) { num_elem[iINChI][j][pInChI->nAtom[m]].num ++; } } if ( 0 > (ret2 = GetInChINumH( pInChI, &m ) ) ) { goto exit_function; } num_elem[iINChI][j][EL_NUMBER_H].num += m; } } } } for ( j = 0; j < TAUT_NUM; j ++ ) { for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { if ( pInpInChI[iINChI][j] ) { INChI *pInChI = &pInpInChI[iINChI][j][k]; if ( pInChI->Stereo && !pInChI->Stereo->nNumberOfStereoCenters ) { pInChI->Stereo->nCompInv2Abs = 0; } if ( pInChI->StereoIsotopic && !pInChI->StereoIsotopic->nNumberOfStereoCenters ) { pInChI->StereoIsotopic->nCompInv2Abs = 0; } } } } #if ( FIX_I2I_STEREOCONVERSION_BUG3 == 1 ) /* (2008-04-10) 1=> Fix bug of i2i conversion */ /* (missed repeating /s in FI after F for multi-component case) */ if (nNumComponents[iINChI][TAUT_NON]>1) /* if multi-component */ if ( !s[iINChI][TAUT_YES][0] && !s[iINChI][TAUT_YES][1] )/* if no /s in M, MI */ if ( (s[iINChI][TAUT_NON][0]>1) && (s[iINChI][TAUT_NON][1]>1) ) /* if /srel/srac in both F, FI */ if ( s[iINChI][TAUT_NON][0] == s[iINChI][TAUT_NON][1] ) /* if same stereo in F and FI */ /* we assume that at least one component in F has no actual stereo */ /* and place deliberately 0 to appropriate place */ for ( k = 0; k < nNumComponents[iINChI][TAUT_NON]; k ++ ) { INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; if (pInChI->Stereo->nCompInv2Abs!=0) { pInChI->Stereo->nCompInv2Abs = 0; goto fini; } } fini: ; #endif if ( num_elem[iINChI][TAUT_YES] ) { tot_charge[iINChI][TAUT_YES] += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; num_elem[iINChI][TAUT_YES][EL_NUMBER_H].num += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; } /**** Count H and isotopic H in Mobile and Fixed H represntations of components */ /* if at least one component has Fixed-H layer then all components have Fixed-H */ /* layer; those whose Fixed-H layer is empty have Fixed-H layer same as Mobile-H layer */ if ( nNumComponents[iINChI][TAUT_NON] ) { /* only if both Mobile and Fixed H exist */ int nFormulaH[TAUT_NUM], nNumH[TAUT_NUM], nCharge[TAUT_NUM], nNumIsotopicH[TAUT_NUM][NUM_H_ISOTOPES]; int nRemovedCharge, nRemovedH, nRemovedIsotopicH[NUM_H_ISOTOPES], nFoundRemovedIsoH; int nTotRemovedProtons, nTotRemovedIsotopicH[NUM_H_ISOTOPES], bExists[TAUT_NUM]; INChI *pInChI[TAUT_NUM]; nTotRemovedProtons = 0; memset( nTotRemovedIsotopicH, 0, sizeof(nTotRemovedIsotopicH) ); len2 = inchi_max( nNumComponents[iINChI][TAUT_YES], nNumComponents[iINChI][TAUT_NON] ); for ( k = 0; k < len2; k ++ ) { /* k is a component index */ for ( j = 0; j < TAUT_NUM; j ++ ) { /* j is 0=TAUT_NON or 1=TAUT_YES */ pInChI[j] = NULL; /* initialization 2006-03 */ bExists[j] = (k < nNumComponents[iINChI][j]) && pInpInChI[iINChI][j][k].nNumberOfAtoms && !pInpInChI[iINChI][j][k].bDeleted; } if ( !bExists[TAUT_NON] ) { /* TAUT_YES does not exist for a proton (H+) in TAUT_NON */ ret2 = RI_ERR_SYNTAX; goto exit_function; } /* at this point at least one of Mobile[k] and Fixed[k] real InChI exists */ /* initialize for counting removed protons and isotopic H from kth Mobile-H component */ for ( j = 0; j < TAUT_NUM; j ++ ) { if ( bExists[j] ) { pInChI[j] = &pInpInChI[iINChI][j][k]; /* BC: reading uninit memory (fixed?) */ } nFormulaH[j] = 0; nNumH[j] = 0; nCharge[j] = 0; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumIsotopicH[j][m] = 0; } } /* extract number of H, isotopic H, and charge */ for ( j = 0; j < TAUT_NUM; j ++ ) { if ( !bExists[j] ) continue; if ( 0 > (ret2 = GetInChIFormulaNumH( pInChI[j], &nFormulaH[j] )) || 0 > (ret2 = GetInChINumH( pInChI[j], &nNumH[j] )) || 0 > (ret2 = GetInChIIsoH( pInChI[j], nNumIsotopicH[j] )) ) { goto exit_function; } nCharge[j] = pInChI[j]->nTotalCharge; } for ( j = 0; j < TAUT_NUM; j ++ ) { if ( !bExists[j] ) continue; if ( nFormulaH[j] != nNumH[j] ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } } nFoundRemovedIsoH = 0; nRemovedCharge = nCharge[TAUT_NON] - nCharge[TAUT_YES]; nRemovedH = nNumH[TAUT_NON] - nNumH[TAUT_YES]; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nFoundRemovedIsoH += 0 != (nRemovedIsotopicH[m] = nNumIsotopicH[TAUT_NON][m] - nNumIsotopicH[TAUT_YES][m] ); } if ( nRemovedCharge != nRemovedH ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } if ( nRemovedCharge || nFoundRemovedIsoH ) { COMPONENT_REM_PROTONS *pNumProtons; if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { /* allocate only if needed */ nNumProtons[iINChI][TAUT_YES].pNumProtons = (COMPONENT_REM_PROTONS *) inchi_calloc(len2, sizeof(nNumProtons[0][0].pNumProtons[0])); if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { ret2 = RI_ERR_ALLOC; goto exit_function; } } pNumProtons = nNumProtons[iINChI][TAUT_YES].pNumProtons+k; pNumProtons->nNumRemovedProtons = nRemovedH; nTotRemovedProtons += nRemovedH; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { pNumProtons->nNumRemovedIsotopicH[m] = nRemovedIsotopicH[m]; nTotRemovedIsotopicH[m] += nRemovedIsotopicH[m]; } /* make sure the Mobile-H InChI has nTautomer */ if ( pInChI[TAUT_YES] && bExists[TAUT_YES] ) { if ( !pInChI[TAUT_YES]->lenTautomer ) { pInChI[TAUT_YES]->lenTautomer = 1; } if ( !pInChI[TAUT_YES]->nTautomer ) { pInChI[TAUT_YES]->nTautomer = (AT_NUMB *)inchi_calloc(pInChI[TAUT_YES]->lenTautomer, sizeof(pInChI[0]->nTautomer[0]) ); } } } } if ( nNumProtons[iINChI][TAUT_YES].pNumProtons ) { /* check consistency */ #if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) int iso_diff[NUM_H_ISOTOPES], iso_diff_tot=0; #endif if ( nTotRemovedProtons != nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } #if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { iso_diff[m] = nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m]-nTotRemovedIsotopicH[m]; if ( iso_diff[m] < 0 ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } else { /* InChI-1.02b bug: nTotRemovedIsotopicH[m] < nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] */ /* in non-tautomeric components where D(+) or T(+) was removed from -NH(+)= or =OH(+) */ iso_diff_tot += iso_diff[m]; } } if ( iso_diff_tot ) { if ( 0 > bIsoMayBeArranged( bInchi2Struct, iso_diff, nNumProtons, pInpInChI, nNumComponents, iINChI )) { ret2 = RI_ERR_SYNTAX; goto exit_function; } } #else for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { if ( nTotRemovedIsotopicH[m] != nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } } #endif } } /* make Mobile H and Fixed H InChI arrays have same length */ len2 = len1 = 0; if ( nNumComponents[iINChI][TAUT_YES] < nNumComponents[iINChI][TAUT_NON] ) { j = TAUT_YES; /* less components in Mobile-H layer */ len2 = nNumComponents[iINChI][TAUT_NON]; len1 = nNumComponents[iINChI][TAUT_YES]; } else if ( nNumComponents[iINChI][TAUT_YES] > nNumComponents[iINChI][TAUT_NON] ) { j = TAUT_NON; /* less components in Fixed-H layer */ len2 = nNumComponents[iINChI][TAUT_YES]; len1 = nNumComponents[iINChI][TAUT_NON]; } /* always len1 <= len2; if Mobile-H and Fixed-H have same number of components then len1=len2=0 */ if ( len2 && len1 ) { INChI *pInChI = (INChI *)inchi_calloc( len2, sizeof( pInChI[0] ) ); if ( !pInChI ) { ret2 = RI_ERR_ALLOC; goto exit_function; } memcpy( pInChI, pInpInChI[iINChI][j], len1 * sizeof( pInChI[0] ) ); inchi_free( pInpInChI[iINChI][j] ); pInpInChI[iINChI][j] = pInChI; nNumComponents[iINChI][j] = len2; for ( ; len1 < len2; len1 ++ ) { if ( j == TAUT_YES ) { /* mark added to Mobile H layer components as deleted protons */ if ( 0 > (ret2 = nFillOutProtonMobileH( pInpInChI[iINChI][j]+len1 ) ) ) { goto exit_function; } if ( 0 > (ret2 = nProtonCopyIsotopicInfo( pInpInChI[iINChI][j]+len1/* to */, pInpInChI[iINChI][TAUT_NON]+len1/* from */ ) ) ) { goto exit_function; } } else { /* mark added to Fixed H layer components as empty deleted */ /* this should not happen */ pInChI[len1].bDeleted = 1; } } } } /* end of iINChI cycle */ /* check balances */ for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( i = iINChI; i < INCHI_NUM; i ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { for ( k = j; k < TAUT_NUM; k ++ ) { if ( (iINChI!=i || j !=k) && num_elem[iINChI][j] && num_elem[i][k] ) { if ( tot_charge[iINChI][j] != tot_charge[i][k] ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } for ( m = 0; m <= nElDataLen; m ++ ) { if ( num_elem[iINChI][j][m].num != num_elem[i][k][m].num ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } } /* if ( memcmp( num_elem[iINChI], num_elem[i][k], (nElDataLen+1)*sizeof(num_elem[0][0][0]) ) { ret2 = RI_ERR_SYNTAX; goto exit_function; } */ } } } } } } else { ret2 = ret; } exit_function: for ( i = 0; i < INCHI_NUM; i ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( num_elem[i][j] ) { inchi_free( num_elem[i][j] ); num_elem[i][j] = NULL; } } } *nErr = (ret2 < 0 && ret2 != RI_ERR_EOL)? ret2 : 0; return ret; } /**************************************************************************************/ #if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) #undef TAUT_YES int bIsoMayBeArranged( int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI ) { const int TAUT_YES = 1; int i, k, m, n_found=0, n_found_at_in_component, n_found_H_in_component, i_iso_at, num_iso_H=0, num_iso_H_orig, num_add_iso_H, orig_add_H; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { num_iso_H += iso_diff[m]; } num_iso_H_orig = num_iso_H; for ( k = 0; k < nNumComponents[iINChI][TAUT_YES] && k < nNumComponents[iINChI][TAUT_NON]; k ++ ) { INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; INChI *pInChITaut = &pInpInChI[iINChI][TAUT_YES][k]; if ( pInChITaut->bDeleted || pInChI->bDeleted || pInChITaut->nNumberOfIsotopicAtoms > 0 || pInChITaut->lenTautomer > 1 && pInChITaut->nTautomer && pInChITaut->nTautomer[0] > 0 || NULL == nNumProtons[iINChI][TAUT_YES].pNumProtons || nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons <= 0 || pInChI->nNumberOfIsotopicAtoms > 0 || nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[0] || nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[1] || nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[2] ) { continue; } /* check if fixed-H has isotopic H; count the possibilities */ orig_add_H = nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; n_found_at_in_component = 0; /* number of atoms that may accept isotopic H */ n_found_H_in_component = 0; for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; if ( nNumRemovedH > 0) { n_found_at_in_component ++; n_found_H_in_component += nNumRemovedH; } } if ( n_found_at_in_component > 0 && num_iso_H > 0 && bInchi2Struct ) { pInChI->IsotopicAtom = (INChI_IsotopicAtom *)calloc(inchi_min(n_found_at_in_component, num_iso_H), sizeof(pInChI->IsotopicAtom[0])); } for ( i = 0, i_iso_at = 0; i < pInChI->nNumberOfAtoms; i ++ ) { int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; n_found += nNumRemovedH; /* found H removed in mobile-H layer */ if ( nNumRemovedH > 0 && num_iso_H > 0 && orig_add_H ) { for ( m = 0; m < NUM_H_ISOTOPES && 0 < num_iso_H && 0 < orig_add_H && 0 < nNumRemovedH; m ++ ) { if ( iso_diff[m] > 0 ) { num_add_iso_H = inchi_min( iso_diff[m], nNumRemovedH ); /* atom limit */ if ( num_add_iso_H > orig_add_H ) /* component limit */ num_add_iso_H = orig_add_H; iso_diff[m] -= num_add_iso_H; /* update tot removed single isotope H limit */ num_iso_H -= num_add_iso_H; /* update tot removed isotopic H limit */ orig_add_H -= num_add_iso_H; /* update component limit */ nNumRemovedH -= num_add_iso_H; /* update atom limit */ nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m] += num_add_iso_H; if ( pInChI->IsotopicAtom ) { pInChI->IsotopicAtom[i_iso_at].nAtomNumber = i+1; switch( m ) { case 0: pInChI->IsotopicAtom[i_iso_at].nNum_H += num_add_iso_H; break; case 1: pInChI->IsotopicAtom[i_iso_at].nNum_D += num_add_iso_H; break; case 2: pInChI->IsotopicAtom[i_iso_at].nNum_T += num_add_iso_H; break; } } } } if ( pInChI->IsotopicAtom ) { i_iso_at ++; } } } if ( pInChI->IsotopicAtom && i_iso_at ) { pInChI->nNumberOfIsotopicAtoms = i_iso_at; } } if ( n_found - num_iso_H >= 0 ) { /* Success. Arrange isotopic H between components */ } return n_found - num_iso_H_orig; /* >0 => ambiguous reconstruction, 0 => unambiguous, <0 => impossible */ } #define TAUT_YES 1 #endif /******************************************************************************************************/ typedef enum tagAuxInfoState { AST_VERSION, /* 0 */ AST_MOBILE_H_NUMBERS, /* 1 /N: */ AST_MOBILE_H_ATOM_EQ, /* 2 /E: */ AST_MOBILE_H_GROUP_EQ, /* 3 /gE: */ AST_MOBILE_H_SP3_INV, /* 4 /it: */ AST_MOBILE_H_SP3_INV_NUMBERS, /* 5 /iN: */ AST_MOBILE_H_ISO_LAYER_FORK, /* 6 */ AST_MOBILE_H_ISO_NUMBERS, /* 7 /I: */ AST_MOBILE_H_ISO_ATOM_EQ, /* 8 /E: */ AST_MOBILE_H_ISO_GROUP_EQ, /* 9 /gE: */ AST_MOBILE_H_ISO_SP3_INV, /* 10 /it: */ AST_MOBILE_H_ISO_SP3_INV_NUMBERS, /* 11 /iN: */ AST_FIXED_H_LAYER_FORK, /* 12 */ AST_FIXED_H_NUMBERS, /* 13 /F: */ AST_FIXED_H_ATOM_EQ, /* 14 /E: */ AST_FIXED_H_SP3_INV, /* 15 /it: */ AST_FIXED_H_SP3_INV_NUMBERS, /* 16 /iN: */ AST_FIXED_H_ISO_LAYER_FORK, /* 17 */ AST_FIXED_H_ISO_NUMBERS, /* 18 /I: */ AST_FIXED_H_ISO_ATOM_EQ, /* 19 /E: */ AST_FIXED_H_ISO_SP3_INV, /* 20 /it: */ AST_FIXED_H_ISO_SP3_INV_NUMBERS, /* 21 /iN: */ AST_REVERSE_INFO_CRV, /* 22 /CRV: */ AST_REVERSE_INFO_ATOMS, /* 23 /rA: */ AST_REVERSE_INFO_BONDS, /* 24 /rB: */ AST_REVERSE_INFO_XYZ, /* 25 /rC: */ AST_RECONNECTED_LAYER_FORK, /* 26 /R: */ AST_RECONNECTED_LAYER_NUMBERS /* 27 */ }AUX_INFO_STATE; /************************************************************************************/ int ParseAuxSegmentVersion( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { const char *q; if ( isdigit( UCINT *str ) && (inchi_strtol( str, &q, 10), !*q) ) { return 1; } return RI_ERR_SYNTAX; } /************************************************************************************/ int CopyAtomNumbers( INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom ) { AT_NUMB *pTo, *pFrom; if ( !pInChI_To || !pInChI_From || pInChI_To->bDeleted || pInChI_From->bDeleted || !pInChI_To->nNumberOfAtoms || !pInChI_From->nNumberOfAtoms || pInChI_To->nNumberOfAtoms != pInChI_From->nNumberOfAtoms || !pInChI_From->nPossibleLocationsOfIsotopicH ) { return RI_ERR_PROGR; } if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { pInChI_To->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( 2*pInChI_To->nNumberOfAtoms, sizeof(pInChI_To->nPossibleLocationsOfIsotopicH[0])); if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { return RI_ERR_ALLOC; } } pTo = pInChI_To->nPossibleLocationsOfIsotopicH + (bIsoTo? 0 : pInChI_To->nNumberOfAtoms ); pFrom = pInChI_From->nPossibleLocationsOfIsotopicH + (bIsoFrom? 0 : pInChI_To->nNumberOfAtoms ); if ( pTo == pFrom ) { return RI_ERR_PROGR; } memcpy( pTo, pFrom, pInChI_To->nNumberOfAtoms*sizeof(pTo[0]) ); return 1; } /************************************************************************************/ int ParseAuxSegmentNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) { int bIso = 0, iComponent = 0, nNumComponents, bIso_From, bAltInChIExists; INChI *pInChI = NULL, *pAltInChI = NULL, *pInChI_From = NULL; const char *p, *q, *pStart, *pEnd, *t; static const char mult_type[] = "mnM"; int val, ret, k, mpy_component, num; AT_NUMB *pNumb; int base = 10; /* save isotopic numbering into the first nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ /* save non-isotopic numbering into the second half of nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ switch( state ) { case AST_MOBILE_H_NUMBERS: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "N:", 2 ) ) return 0; break; case AST_FIXED_H_NUMBERS: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "F:", 2 ) ) return 0; break; case AST_MOBILE_H_ISO_NUMBERS: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "I:", 2 ) ) return 0; bIso = 1; break; case AST_FIXED_H_ISO_NUMBERS: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "I:", 2 ) ) return 0; bIso = 1; break; default: return RI_ERR_PROGR; } pStart = str+2; if ( !*pStart ) { return 1; } iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; bAltInChIExists = (NULL != pInpInChI[ALT_TAUT(bMobileH)]); while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } /* check */ if ( !pInpInChI[bMobileH] ) { return 1; /* invalid aux info */ } pInChI = pInpInChI[bMobileH] + iComponent; pAltInChI = pInpInChI[ALT_TAUT(bMobileH)] + iComponent; if ( (isdigit(UCINT *pStart) && 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || (q = pStart, val=1) )&& (t=strchr(mult_type, *q)) && q+1 == pEnd ) { /* process the abbreviation */ pInChI_From = NULL; switch( bMobileH ) { case TAUT_YES: switch ( bIso ) { case 0: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; case 1: if ( *q != 'm' ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* isotopic Mobile-H <-- non-isotopic Mobile H */ pInChI_From = pInChI; bIso_From = 0; break; default: ret = RI_ERR_PROGR; goto exit_function; } break; case TAUT_NON: switch ( *q ) { case 'm': /* same as mobile H */ switch( bIso ) { case 0: /* from Mobile-H not isotopic */ pInChI_From = bAltInChIExists? pAltInChI:NULL; bIso_From = 0; break; case 1: pInChI_From = bAltInChIExists? pAltInChI:NULL;; bIso_From = 1; break; default: ret = RI_ERR_PROGR; goto exit_function; } break; case 'n': /* same as non-isotopic Fixed-H */ switch ( bIso ) { case 0: ret = 1; /*RI_ERR_SYNTAX;*/ goto exit_function; case 1: pInChI_From = pInChI; bIso_From = 0; default: ret = RI_ERR_PROGR; goto exit_function; } break; case 'M': /* same as isotopic Mobile-H */ switch ( bIso ) { case 0: ret = RI_ERR_SYNTAX; goto exit_function; case 1: pInChI_From = bAltInChIExists? pAltInChI:NULL;; bIso_From = 1; break; default: ret = RI_ERR_PROGR; goto exit_function; } break; default: ret = 1; /*RI_ERR_SYNTAX;*/ goto exit_function; } break; } /* copy */ if ( pInChI_From ) { for ( k = 0; k < val; k ++ ) { CopyAtomNumbers( pInChI+k, bIso, pInChI_From+k, bIso_From ); } } mpy_component = val; } else { mpy_component = 1; p = pStart; pNumb = pInChI->nPossibleLocationsOfIsotopicH; if ( !pNumb ) { pNumb = (AT_NUMB *)inchi_calloc( 2*pInChI->nNumberOfAtoms, sizeof(pNumb[0] ) ); if ( !pNumb ) { ret = RI_ERR_ALLOC; goto exit_function; } pInChI->nPossibleLocationsOfIsotopicH = pNumb; } pNumb += bIso? 0 : pInChI->nNumberOfAtoms; if ( pStart < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *pStart)? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; if ( *pbAbc == 1 ) { for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { num = (AT_NUMB)inchi_strtol( p, &q, base ); if ( num <= 0 || p == q ) { ret = RI_ERR_SYNTAX; goto exit_function; } pNumb[k] = (AT_NUMB)num; p = q; if ( p == pEnd ) { break; /* main end of cycle */ } } } else { for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { pNumb[k] = (AT_NUMB)inchi_strtol( p, &q, 10 ); p = q; if ( p == pEnd ) { break; /* main end of cycle */ } else if ( *p != ',' ) { ret = RI_ERR_SYNTAX; goto exit_function; } } } if ( p != pEnd || k+1 != pInChI->nNumberOfAtoms ) { ret = RI_ERR_SYNTAX; goto exit_function; } } iComponent += mpy_component; if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } if ( nNumComponents != iComponent ) { ret = 1; /*RI_ERR_SYNTAX;*/ /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: return ret; } /**********************************************************************************************************/ int ParseAuxSegmentAtomEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_MOBILE_H_ATOM_EQ: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "E:", 2 ) ) return 0; break; case AST_MOBILE_H_ISO_ATOM_EQ: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "E:", 2 ) ) return 0; break; case AST_FIXED_H_ATOM_EQ: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "E:", 2 ) ) return 0; break; case AST_FIXED_H_ISO_ATOM_EQ: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "E:", 2 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentGroupEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_MOBILE_H_GROUP_EQ: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "gE:", 3 ) ) return 0; break; case AST_MOBILE_H_ISO_GROUP_EQ: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "gE:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentSp3Inv( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_MOBILE_H_SP3_INV: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "it:", 3 ) ) return 0; break; case AST_MOBILE_H_ISO_SP3_INV: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "it:", 3 ) ) return 0; break; case AST_FIXED_H_SP3_INV: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "it:", 3 ) ) return 0; break; case AST_FIXED_H_ISO_SP3_INV: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "it:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentSp3InvNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_MOBILE_H_SP3_INV_NUMBERS: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "iN:", 3 ) ) return 0; break; case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: if ( bMobileH != TAUT_YES ) return RI_ERR_PROGR; if ( memcmp( str, "iN:", 3 ) ) return 0; break; case AST_FIXED_H_SP3_INV_NUMBERS: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "iN:", 3 ) ) return 0; break; case AST_FIXED_H_ISO_SP3_INV_NUMBERS: if ( bMobileH != TAUT_NON ) return RI_ERR_PROGR; if ( memcmp( str, "iN:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentReverseCRV( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_REVERSE_INFO_CRV: if ( memcmp( str, "CRV:", 4 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentReverseAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_REVERSE_INFO_ATOMS: if ( memcmp( str, "rA:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentReverseBonds( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { switch( state ) { case AST_REVERSE_INFO_BONDS: if ( memcmp( str, "rB:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } return 1; } /***********************************************************************************************************/ int ParseAuxSegmentReverseXYZ( const char *str, int bMobileH, XYZ_COORD **ppXYZ, INChI *pInpInChI[], int ppnNumComponents[], int state ) { const char *pStart, *p, *q; XYZ_COORD *pXYZ = NULL; int nLenXYZ=0, i, j; switch( state ) { case AST_REVERSE_INFO_XYZ: if ( memcmp( str, "rC:", 3 ) ) return 0; break; default: return RI_ERR_PROGR; } pStart = str+3; /* count coordinates */ for ( p = pStart, nLenXYZ = 0; *p; p ++ ) { nLenXYZ += ( *p == ';' ); } if ( !nLenXYZ ) { return RI_ERR_SYNTAX; } if ( NULL == (pXYZ = (XYZ_COORD *)inchi_calloc( nLenXYZ, sizeof(pXYZ[0]) )) ) { return RI_ERR_ALLOC; } for ( p = pStart, i = 0; *p && i < nLenXYZ; p ++, i ++ ) { for ( j = 0; j < 3; j ++ ) { pXYZ[i].xyz[j] = inchi_strtod( p, &q ); p = q + (*q == ',' ); } if ( *p != ';' ) { break; } } if ( i != nLenXYZ || *p ) { return RI_ERR_SYNTAX; } *ppXYZ = pXYZ; return nLenXYZ+1; } /************************************************************************************/ int AddAuxSegmentCoord( int nRet, XYZ_COORD *pXYZ, int nLenXYZ, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ) { int iINChI, j, k, n, m, numAt[TAUT_NUM], num_at, nNumMissingNumbers = 0, ret = 0; INChI *pInChI = NULL; INChI *pAltInChI = NULL; XYZ_COORD *pxyz; /* propagate numberings */ for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = TAUT_YES; TAUT_NON <= j; j -- ) { for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { int jj = ALT_TAUT(j); pInChI = pInpInChI[iINChI][j] + k; pAltInChI = (k < nNumComponents[iINChI][jj])? pInpInChI[iINChI][jj] + k : NULL; numAt[j] = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; numAt[jj] = ( pAltInChI && !pAltInChI->bDeleted )? pAltInChI->nNumberOfAtoms : 0; switch( j ) { case TAUT_YES: if ( !numAt[j] ) { break; /* component does not exist */ } if ( !pInChI->nPossibleLocationsOfIsotopicH ) { nNumMissingNumbers ++; break; } if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { /* copy from non-isotopic (2nd half of the at. numbers array) to the isotopic (1st half) */ ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); if ( ret < 0 ) { goto exit_function; } } else { inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); pInChI->nPossibleLocationsOfIsotopicH = NULL; nNumMissingNumbers ++; } } break; case TAUT_NON: if ( !numAt[j] ) { break; /* component does not exist */ } if ( !pInChI->nPossibleLocationsOfIsotopicH ) { /* trying to get numbers from Mobile-H component */ if ( !numAt[jj] || !(pAltInChI->nPossibleLocationsOfIsotopicH) ) { nNumMissingNumbers ++; break; } if ( pAltInChI->nPossibleLocationsOfIsotopicH[0] ) { ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 1 ); if ( ret < 0 ) { goto exit_function; } } else if ( pAltInChI->nPossibleLocationsOfIsotopicH[numAt[jj]] ) { ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 0 ); if ( ret < 0 ) { goto exit_function; } } else { /* pAltInChI->nPossibleLocationsOfIsotopicH should have */ /* been deallocated on previous TAUT_YES pass */ ret = RI_ERR_PROGR; goto exit_function; } } else if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { /* copy from non-isotopic to isotopic */ ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); if ( ret < 0 ) { goto exit_function; } } else { inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); pInChI->nPossibleLocationsOfIsotopicH = NULL; nNumMissingNumbers ++; } } break; } } } } /* add coordinates */ for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { pInChI = pInpInChI[iINChI][j] + k; num_at = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; if ( !num_at ) { if ( pInChI->nPossibleLocationsOfIsotopicH ) { inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); pInChI->nPossibleLocationsOfIsotopicH = NULL; } continue; } if ( !pInChI->nPossibleLocationsOfIsotopicH ) { continue; } if ( iINChI == INCHI_BAS && num_at == 1 && pInChI->szHillFormula && !strcmp(pInChI->szHillFormula, "H") && (int)pInChI->nPossibleLocationsOfIsotopicH[0]-1 >= nLenXYZ ) { ; /* a single atom H disconnected from a metal atom has no coordinates */ } else { /* add atom coordinates */ pxyz = (XYZ_COORD *)inchi_calloc( num_at, sizeof(pxyz[0])); if ( !pxyz ) { ret = RI_ERR_ALLOC; goto exit_function; } for ( n = 0; n < num_at; n ++ ) { m = (int)pInChI->nPossibleLocationsOfIsotopicH[n]-1; if ( m < 0 || m >= nLenXYZ ) { inchi_free( pxyz ); ret = RI_ERR_SYNTAX; goto exit_function; } pxyz[n] = pXYZ[m]; } pInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; } inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); pInChI->nPossibleLocationsOfIsotopicH = NULL; } } } ret = nRet; /* normal exit */ exit_function: return ret; } /************************************************************************************/ int ReadInChICoord( INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, int *pState, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ) { int c, fst, ret=RI_ERR_ALLOC; int bMobileH = TAUT_YES, bReconn = INCHI_BAS; const char szToken[] = INCHI_TOKEN; int state=-1, prev_state=-1; XYZ_COORD *pXYZ = NULL; int nLenXYZ = 0; int bAbc = -1; /* initially undefined */ *pState = 0; INCHI_HEAPCHK /* Get "InChI=1/" */ if ( pLine->len ) { c = pLine->c; } else { c = nGetInChISegment( pInp, pLine, szToken ); } if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) { ret = c; pLine->len = 0; goto exit_error; } if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { *pState = -1; pLine->len = 0; ret = RI_ERR_PROGR; goto exit_error; } if ( memcmp(pLine->str, "AuxInfo=", 8) ) { *pState = -1; return c; } state = AST_VERSION; ret = 1; /* means read the next segment */ do { /* read the next segment up to the '/' */ INCHI_HEAPCHK if ( ret < 0 ) { *pState = prev_state; break; } prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); /* prev_state = state;*/ if ( 0 < ret ) { /* read next segment */ if ( c != RI_ERR_EOF && c != SEG_END ) { /* abnormal reading result; should not happen */ while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { /* bypass to the end of line or file */ c = getInChIChar(pInp); } ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ pLine->len = 0; pLine->c = ret; break; } if ( c == RI_ERR_EOF ) { ret = RI_ERR_EOF; /* end of line */ break; } if ( c == SEG_END ) { c = nGetInChISegment( pInp, pLine, szToken ); } if ( c < 0 ) { goto exit_error; /* error */ } if ( !pLine->len ) { ret = RI_ERR_EOL; /* end of line */ break; } fst = UCINT pLine->str[0]; } /* process the seqment */ switch ( state ) { case AST_VERSION: /* Mobile H */ bMobileH = TAUT_YES; ret = ParseAuxSegmentVersion( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_NUMBERS; break; case AST_MOBILE_H_NUMBERS: ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = AST_MOBILE_H_ATOM_EQ; break; case AST_MOBILE_H_ATOM_EQ: ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_GROUP_EQ; break; case AST_MOBILE_H_GROUP_EQ: ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_SP3_INV; break; case AST_MOBILE_H_SP3_INV: ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_SP3_INV_NUMBERS; break; case AST_MOBILE_H_SP3_INV_NUMBERS: ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_ISO_LAYER_FORK; break; case AST_MOBILE_H_ISO_LAYER_FORK: if ( !memcmp( pLine->str, "I:", 2 ) ) { state = AST_MOBILE_H_ISO_NUMBERS; } else if ( !memicmp( pLine->str, "F:", 2 ) ) { state = AST_FIXED_H_NUMBERS; bMobileH = TAUT_NON; } else if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { state = AST_REVERSE_INFO_CRV; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { state = AST_REVERSE_INFO_ATOMS; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { ret = 1; /* read the next segment */ state = AST_VERSION; bMobileH = TAUT_YES; bReconn = INCHI_REC; } else { ret = RI_ERR_SYNTAX; } break; /* Mobile H, isotopic */ case AST_MOBILE_H_ISO_NUMBERS: ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = AST_MOBILE_H_ISO_ATOM_EQ; break; case AST_MOBILE_H_ISO_ATOM_EQ: ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_ISO_GROUP_EQ; break; case AST_MOBILE_H_ISO_GROUP_EQ: ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_ISO_SP3_INV; break; case AST_MOBILE_H_ISO_SP3_INV: ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_MOBILE_H_ISO_SP3_INV_NUMBERS; break; case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_LAYER_FORK; break; case AST_FIXED_H_LAYER_FORK: if ( !memicmp( pLine->str, "F:", 2 ) ) { state = AST_FIXED_H_NUMBERS; bMobileH = TAUT_NON; } else if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { state = AST_REVERSE_INFO_CRV; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { state = AST_REVERSE_INFO_ATOMS; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { ret = 1; /* read the next segment */ state = AST_VERSION; bMobileH = TAUT_YES; bReconn = INCHI_REC; } else { ret = RI_ERR_SYNTAX; } break; case AST_FIXED_H_NUMBERS: ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = AST_FIXED_H_ATOM_EQ; break; case AST_FIXED_H_ATOM_EQ: ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_SP3_INV; break; case AST_FIXED_H_SP3_INV: ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_SP3_INV_NUMBERS; break; case AST_FIXED_H_SP3_INV_NUMBERS: ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_ISO_LAYER_FORK; break; case AST_FIXED_H_ISO_LAYER_FORK: if ( !memcmp( pLine->str, "I:", 2 ) ) { state = AST_FIXED_H_ISO_NUMBERS; } else if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { state = AST_REVERSE_INFO_CRV; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { state = AST_REVERSE_INFO_ATOMS; } else if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { ret = 1; /* read the next segment */ state = AST_VERSION; bMobileH = TAUT_YES; bReconn = INCHI_REC; } else { ret = RI_ERR_SYNTAX; } break; case AST_FIXED_H_ISO_NUMBERS: ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = AST_FIXED_H_ISO_ATOM_EQ; break; case AST_FIXED_H_ISO_ATOM_EQ: ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_SP3_INV; break; case AST_FIXED_H_ISO_SP3_INV: ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_FIXED_H_ISO_SP3_INV_NUMBERS; break; case AST_FIXED_H_ISO_SP3_INV_NUMBERS: ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_REVERSE_INFO_CRV; break; case AST_REVERSE_INFO_CRV: ret = ParseAuxSegmentReverseCRV( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); /* state = (bReconn == INCHI_BAS)? AST_REVERSE_INFO_ATOMS : AST_RECONNECTED_LAYER_FORK;*/ state = AST_REVERSE_INFO_ATOMS; break; case AST_REVERSE_INFO_ATOMS: ret = ParseAuxSegmentReverseAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_REVERSE_INFO_BONDS; break; case AST_REVERSE_INFO_BONDS: ret = ParseAuxSegmentReverseBonds( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_REVERSE_INFO_XYZ; break; case AST_REVERSE_INFO_XYZ: ret = ParseAuxSegmentReverseXYZ( pLine->str, bMobileH, &pXYZ, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = AST_RECONNECTED_LAYER_FORK; if ( ret > 0 ) { nLenXYZ = ret - 1; } break; case AST_RECONNECTED_LAYER_FORK: if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { ret = 1; /* read the next segment */ state = AST_VERSION; bMobileH = TAUT_YES; bReconn = INCHI_REC; } else { ret = RI_ERR_SYNTAX; } break; } } while( c >= 0 ); ret = AddAuxSegmentCoord( ret, pXYZ, nLenXYZ, pInpInChI, nNumComponents ); exit_error: if ( pXYZ ) { inchi_free( pXYZ ); } if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { pLine->len = 0; } return ret; } /******************************************************************************************************/ int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, char **pStr, int *pState, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], int s[INCHI_NUM][TAUT_NUM][2], int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) { int c, fst, ret=RI_ERR_ALLOC, len; int bMobileH = TAUT_YES, bReconn = INCHI_BAS; const char szToken[] = INCHI_TOKEN; char *p; int state=-1, prev_state=-1; int bAbc = -1; /* -1=> undefined, 0=> decimal, 1=> abc (compressed) */ const int len_std_prefix=8; size_t k=0; unsigned char let1, let2; const char a2p[]="ABCDEFGHIJKLMNOP"; /* memset( pLine, 0, sizeof( pLine[0] ) ); */ *pState = 0; next_line: INCHI_HEAPCHK /* Got "InChI=1/" */ if ( pLine->len ) { c = pLine->c; } else { INCHI_HEAPCHK c = nGetInChISegment( pInp, pLine, szToken ); INCHI_HEAPCHK } if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) { ret = c; goto exit_function; } INCHI_HEAPCHK if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF || !(p = strstr(pLine->str, "InChI=1")) ) { if ( pLine->str && pLine->str == strstr ( pLine->str, "Structure" ) ) { if ( *pStr ) { INCHI_HEAPCHK inchi_free( *pStr ); } *pStr = pLine->str; /* bypass to the end of the 'Structure nnn' line */ memset( pLine, 0, sizeof( pLine[0] ) ); while ( c && !INCHI_INP_EOL(c) ) { c = getInChIChar(pInp); } goto next_line; } /* bypass to the end of unrecognized line */ while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { c = getInChIChar(pInp); } pLine->len = 0; INCHI_HEAPCHK goto next_line; } /* Check if got a standard InChI */ if ( ( pLine->len == len_std_prefix ) && (pLine->str[len_std_prefix-1]=='S') ) *bStdFormat=1; else *bStdFormat=0; state=IST_MOBILE_H_FORMULA; ret = 1; /* means read the next segment */ do { /* read the next segment up to the '/' */ INCHI_HEAPCHK if ( ret < 0 ) { *pState = prev_state; break; } prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); if ( 0 < ret ) { /* read next segment */ if ( c != RI_ERR_EOF && c != SEG_END ) { /* abnormal reading result; should not happen */ /* unless we got backslash-SaveOpt */ if ( c=='\\' ) { /* May be SaveOpt */ *bInputHasSaveOpt = 1; } k = 0; while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { /* bypass to the end of line or file */ c = getInChIChar(pInp); k++; if ( k==1 ) let1 = c; else if ( k==2 ) let2 = c; } if ( k != 3) { /* not a valid SaveOpt which must be of two chars */ *bInputHasSaveOpt = 0; let1 = let2 = '\0'; } else { /* may be SaveOpt - analyze the content */ if ( ( let2 >= 'A') && ( let2 <= 'D') ) /* letter-2 OK */ { *bInputHasSaveOpt = 0; *inp_save_opt_bits = 0; for (k=0; k<16; k++) { if ( a2p[k] == let1) /* letter-1 OK */ { *inp_save_opt_bits = (unsigned char) k; *bInputHasSaveOpt = 1; break; } } if ( *bInputHasSaveOpt ) { if ( let2=='B' || let2=='D' ) *inp_save_opt_bits |= SAVE_OPT_15T; if ( let2=='C' || let2=='D' ) *inp_save_opt_bits |= SAVE_OPT_KET; } } } ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ pLine->len = 0; pLine->c = ret; break; /* exit */ } if ( c == RI_ERR_EOF ) { ret = RI_ERR_EOF; /* end of line */ break; } if ( c == SEG_END ) { c = nGetInChISegment( pInp, pLine, szToken ); } if ( c < 0 ) { goto exit_error; /* error */ } if ( !pLine->len ) { ret = RI_ERR_EOL; /* end of line */ break; } fst = UCINT pLine->str[0]; } /* process the seqment */ switch ( state ) { /* Mobile H, M */ /* / */ case IST_MOBILE_H_FORMULA: bMobileH = TAUT_YES; ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); state = IST_MOBILE_H_CONNECTIONS; break; case IST_MOBILE_H_CONNECTIONS: /* /c */ ret = ParseSegmentConnections( pLine->str, bMobileH, &pInpInChI[bReconn][bMobileH], &nNumComponents[bReconn][bMobileH], &bAbc ); state = IST_MOBILE_H; break; case IST_MOBILE_H: /* /h */ ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); state = IST_MOBILE_H_CHARGE; break; case IST_MOBILE_H_CHARGE: /* /q */ ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); state = IST_MOBILE_H_PROTONS; break; case IST_MOBILE_H_PROTONS: /* /p */ ret = ParseSegmentProtons( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn] ); state = IST_MOBILE_H_SP2; break; case IST_MOBILE_H_SP2: /* /b */ ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_SP3; break; case IST_MOBILE_H_SP3: /* t */ ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_SP3_M; break; case IST_MOBILE_H_SP3_M: /* /m */ ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = IST_MOBILE_H_SP3_S; break; case IST_MOBILE_H_SP3_S: /* /s */ ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); state = IST_MOBILE_H_ISO_LAYER_FORK; break; case IST_MOBILE_H_ISO_LAYER_FORK: /* find layer type after M */ ret = 0; switch( pLine->str[0] ) { case 'i': state = IST_MOBILE_H_ISO_ATOMS; /* MI */ break; case 'f': state = IST_FIXED_H_FORMULA; /* F */ break; case 'r': state = IST_RECONNECTED_FORMULA; /* reconnected */ break; default: ret = RI_ERR_SYNTAX; } if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); ret = RI_ERR_SYNTAX; /* empty layer /i or /f or /r at the end of InChI line */ } else if ( !ret && state != IST_MOBILE_H_ISO_ATOMS ) { len = strlen( pLine->str ); if ( len > 1 ) { memmove( pLine->str, pLine->str+1, len ); } else { ret = 1; /* read the next segment */ } } break; /* Mobile H, isotopic, MI */ case IST_MOBILE_H_ISO_ATOMS: /* i */ ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_ISO_EXCH_H; break; case IST_MOBILE_H_ISO_EXCH_H: /* /i/h */ ret = ParseSegmentIsoExchgH( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_ISO_SP2; break; case IST_MOBILE_H_ISO_SP2: /* /i/b */ ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_ISO_SP3; break; case IST_MOBILE_H_ISO_SP3: /* /i/t */ ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_MOBILE_H_ISO_SP3_M; break; case IST_MOBILE_H_ISO_SP3_M: /* /i/m */ ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = IST_MOBILE_H_ISO_SP3_S; break; case IST_MOBILE_H_ISO_SP3_S: /* /i/s */ ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); state = IST_FIXED_H_LAYER_FORK; break; case IST_FIXED_H_LAYER_FORK: /* find layer type after MI */ ret = 0; switch( pLine->str[0] ) { case 'f': state = IST_FIXED_H_FORMULA; /* F */ break; case 'r': state = IST_RECONNECTED_FORMULA; /* reconnected */ break; default: ret = RI_ERR_SYNTAX; } if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); ret = RI_ERR_SYNTAX; /* empty layer /f or /r at the end of InChI line */ } else if ( !ret ) { len = strlen( pLine->str ); if ( len > 1 ) { memmove( pLine->str, pLine->str+1, len ); } else { ret = 1; /* read the next segment */ } } break; /* Fixed H, F */ case IST_FIXED_H_FORMULA: bMobileH = TAUT_NON; ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); state = IST_FIXED_H; break; case IST_FIXED_H: /* /f/h */ ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); state = IST_FIXED_H_CHARGE; break; case IST_FIXED_H_CHARGE: /* /f/q */ ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); state = IST_FIXED_H_SP2; break; case IST_FIXED_H_SP2: /* /f/b */ ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_SP3; break; case IST_FIXED_H_SP3: /* /f/t */ ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_SP3_M; break; case IST_FIXED_H_SP3_M: /* /f/m */ ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = IST_FIXED_H_SP3_S; break; case IST_FIXED_H_SP3_S: /* /f/s */ ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); state = IST_FIXED_H_PERMUTATION; break; case IST_FIXED_H_PERMUTATION: /* /f/o */ ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_ISO_LAYER_FORK; break; case IST_FIXED_H_ISO_LAYER_FORK: /* find layer type after M */ ret = 0; switch( pLine->str[0] ) { case 'i': state = IST_FIXED_H_ISO_ATOMS; /* FI */ break; case 'r': state = IST_RECONNECTED_FORMULA; /* reconnected */ break; default: ret = RI_ERR_SYNTAX; } if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); ret = RI_ERR_SYNTAX; /* empty layer /i or /r at the end of InChI line */ } else if ( !ret && state != IST_FIXED_H_ISO_ATOMS ) { len = strlen( pLine->str ); if ( len > 1 ) { memmove( pLine->str, pLine->str+1, len ); } else { ret = 1; /* read the next segment */ } } break; /* Fixed H, isotopic, FI */ case IST_FIXED_H_ISO_ATOMS: /* /f/i */ ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_ISO_SP2; break; case IST_FIXED_H_ISO_SP2: /* /f/i/b */ ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_ISO_SP3; break; case IST_FIXED_H_ISO_SP3: /* /f/i/t */ ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_FIXED_H_ISO_SP3_M; break; case IST_FIXED_H_ISO_SP3_M: /* /f/i/m */ ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); state = IST_FIXED_H_ISO_SP3_S; break; case IST_FIXED_H_ISO_SP3_S: /* /f/i/s */ ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); state = IST_FIXED_H_ISO_PERMUTATION; break; case IST_FIXED_H_ISO_PERMUTATION: /* /f/i/o */ ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); state = IST_RECONNECTED_LAYER_FORK; break; case IST_RECONNECTED_LAYER_FORK: /* find layer type after FI */ ret = 0; switch( pLine->str[0] ) { case 'r': state = IST_RECONNECTED_FORMULA; /* reconnected */ break; default: ret = RI_ERR_SYNTAX; } if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); ret = RI_ERR_SYNTAX; /* empty layer /r at the end of InChI line */ } else if ( !ret ) { len = strlen( pLine->str ); if ( len > 1 ) { memmove( pLine->str, pLine->str+1, len ); } else { ret = 1; /* read the next segment */ } } break; case IST_RECONNECTED_FORMULA: bReconn = INCHI_REC; bMobileH = TAUT_YES; state = IST_MOBILE_H_FORMULA; break; } } while( c >= 0 ); exit_function:; exit_error:; INCHI_HEAPCHK if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { pLine->len = 0; } return ret; } /****************************************************************************************/ int ParseSegmentIsoExchgH( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int pnNumComponents[], int state, int *pbAbc ) { /* Pass 1: count bonds and find actual numbers of atom */ const char *p, *q, *pStart, *pEnd; int ret=0, num, i, i_prev; static char abc_h[] = "hdt"; if ( str[0] != 'h' ) return 0; pStart = str+1; if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_EXCH_H ) ) { return RI_ERR_PROGR; /* program error */ } if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = pStart; if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ /* compressed: /hNtNdNh where N is a decimal number */ /* uncompressed: /hT[n]D[n]H[n] where n > 1 is a decimal number */ *pbAbc = isdigit( UCINT *p)? 1 : 0; } if ( *pbAbc == 1 ) { i_prev = (int)sizeof(abc_h); while ( p < pEnd ) { num = (int)inchi_strtol( p, &q, 10 ); if ( 0 >= num || p == q || q >= pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = strchr( abc_h, *q); if ( p && (i=p-abc_h) < i_prev ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[i] = (NUM_H)num; p = q+1; i_prev = i; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else { if ( *p == 'T' ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = 1; p ++; if ( isdigit( UCINT p[0]) ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'D' ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = 1; p ++; if ( isdigit( UCINT p[0]) ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'H' ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = 1; p ++; if ( isdigit( UCINT p[0]) ) { nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } } if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentPerm( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) { int nNumComponents, iComponent1, iComponent2, numTrans; const char *p, *q, *pStart, *pEnd, *pPermStart, *pPermEnd; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; /* bMobileH should be TAUT_NON = 0 */ INChI tmp; int base = 10; if ( str[0] != 'o' ) return 0; pStart = str+1; nNumComponents = ppnNumComponents[bMobileH]; if ( !(bMobileH==TAUT_NON && ( state == IST_FIXED_H_PERMUTATION || state == IST_FIXED_H_ISO_PERMUTATION) ) ) { return RI_ERR_PROGR; /* program error */ } if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } else { return RI_ERR_SYNTAX; /* syntax error */ } while( pStart < pEnd ) { /* cycle over components; rearrange Fixed H components in order of Mobile H components */ /* if /o(1,2,3) then reaarange Fixed H components in this way: tmp<-1, 1<-2, 2<-3, 3<-tmp */ if ( *pStart != '(' ) { ret = RI_ERR_SYNTAX; goto exit_function; } pPermStart = pStart + 1; memset( &tmp, 0, sizeof(tmp) ); /* initialization 2006-03 */ if ( !(pPermEnd = strchr( pPermStart, ')' )) || pPermEnd == pPermStart ) { ret = RI_ERR_SYNTAX; goto exit_function; } if ( pPermStart < pPermEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *pPermStart)? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; /* permutation cycle */ if ( *pbAbc == 1 ) { for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q ) { /* get first atom number */ if ( 0 >= (iComponent1 = (int)inchi_strtol( p, &q, base )) || iComponent1 > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( iComponent2 ) { pInChI[iComponent2-1] = pInChI[iComponent1-1]; numTrans ++; } else { tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ } } } else { for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q + (*q==',') ) { /* get first atom number */ if ( !isdigit( UCINT *p ) ) { ret = RI_ERR_SYNTAX; goto exit_function; } if ( !(iComponent1 = (int)inchi_strtol( p, &q, 10 )) || iComponent1 > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( iComponent2 ) { pInChI[iComponent2-1] = pInChI[iComponent1-1]; numTrans ++; } else { tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ } } } pInChI[iComponent2-1] = tmp; if ( !numTrans || p != pPermEnd ) { ret = RI_ERR_SYNTAX; goto exit_function; } else { pStart = p+1; } } ret = 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentIsoAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) { int i, mpy_component, val; int nNumComponents, iComponent, len, iAtom; AT_NUMB nAtom1; const char *p, *q, *t, *pStart, *pEnd, *r; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; INChI *pInChIFrom=NULL; INChI_IsotopicAtom **pIsotopicAtom = NULL; INChI_IsotopicAtom isoAtom; const char mult_type[] = "mnMNe"; const char parity_type[] = "-+TDH"; int bIsoFrom, nCpyType = CPY_ISO_AT; int base = 10; if ( str[0] != 'i' ) return 0; pStart = str+1; iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_ATOMS || bMobileH==TAUT_NON && state == IST_FIXED_H_ISO_ATOMS ) ) { return RI_ERR_PROGR; /* program error */ } if ( !*pStart ) { return nNumComponents+1; /* no isotopic atoms */ } while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); if ( p != q ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #if (FIX_DALKE_BUGS == 1) if ( iComponent + mpy_component > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif p ++; /* move to the 1st character of the component */ } else if ( (isdigit(*pStart) && 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || (q = pStart, val=1))&& (t=strchr(mult_type, *q)) && q+1 == pEnd ) { /* process the abbreviation */ ret = 0; #if (FIX_DALKE_BUGS == 1) if ( iComponent + val > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif bIsoFrom = 0; switch( bMobileH ) { case TAUT_YES: ret = RI_ERR_SYNTAX; break; case TAUT_NON: if ( *q == 'm' ) { /* copy from mobile H to fixed H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; default: ret = RI_ERR_SYNTAX; break; } if ( ret < 0 ) { goto exit_function; } /* copy */ for ( i = 0; i < val; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, 0, bIsoFrom ); if ( !ret ) { ret = RI_ERR_SYNTAX; } if ( ret < 0 ) { goto exit_function; } } iComponent += val; /* continue to the next component(s) */ if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } else { mpy_component = 1; p = pStart; } pStart = p; pIsotopicAtom = &pInChI[iComponent].IsotopicAtom; if ( *pIsotopicAtom ) { ret = RI_ERR_PROGR; /* program error */ goto exit_function; } if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *p)? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; one_more_time: if ( *pbAbc == 1 ) { /* process the componnt: At[+/-Charge]TDH,... */ /* pass 1: find number of stereoatoms */ for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ); if ( !nAtom1 || nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } memset( &isoAtom, 0, sizeof(isoAtom) ); isoAtom.nAtomNumber = nAtom1; isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p, &q, 10 ); /* alway in abc */ if ( p == q ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q; if ( *p == 't' ) { isoAtom.nNum_T = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'd' ) { isoAtom.nNum_D = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'h' ) { isoAtom.nNum_H = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( p > pEnd || !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( *pIsotopicAtom ) { pIsotopicAtom[0][iAtom] = isoAtom; } } } else { /* process the componnt: At[+/-Charge]TDH,... */ /* pass 1: find number of stereoatoms */ for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); p = q; if ( !nAtom1 || nAtom1 > pInChI[iComponent].nNumberOfAtoms || !(r = strchr( parity_type, *p) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } memset( &isoAtom, 0, sizeof(isoAtom) ); isoAtom.nAtomNumber = nAtom1; if ( p[0] == '+' && isdigit( UCINT p[1]) ) { isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p+1, &q, 10 ); if ( isoAtom.nIsoDifference >= 0 ) isoAtom.nIsoDifference ++; p = q; } else if ( p[0] == '-' && isdigit( UCINT p[1]) ) { isoAtom.nIsoDifference = -(NUM_H)inchi_strtol( p+1, &q, 10 ); if ( isoAtom.nIsoDifference == 0 ) isoAtom.nIsoDifference ++; p = q; } if ( *p == 'T' ) { isoAtom.nNum_T = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'D' ) { isoAtom.nNum_D = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( *p == 'H' ) { isoAtom.nNum_H = 1; p ++; if ( isdigit( UCINT *p) ) { isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); p = q; } } if ( !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( p < pEnd ) { if ( *p == ',' ) { p ++; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } if ( *pIsotopicAtom ) { pIsotopicAtom[0][iAtom] = isoAtom; } } } if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( !*pIsotopicAtom ) { /* end of the 1st pass */ len = iAtom; /* memory allocation */ if ( !(*pIsotopicAtom = (INChI_IsotopicAtom *) inchi_calloc( len+1, sizeof(**pIsotopicAtom) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } goto one_more_time; /* goto the 2nd pass */ } else { /* 2nd pass */ if ( len != iAtom ) { ret = RI_ERR_PROGR; /* program error */ goto exit_function; } pInChI[iComponent].nNumberOfIsotopicAtoms = len; } /* multiplier */ for ( i = 1; i < mpy_component; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, 0, 0 ); if ( !ret ) { ret = RI_ERR_SYNTAX; /* syntax error */ } if ( ret < 0 ) { goto exit_function; } } iComponent += mpy_component; if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } if ( nNumComponents != iComponent ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentSp3s( const char *str, int bMobileH, INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state ) { /* Pass 1: count bonds and find actual numbers of atom */ int nNumComponents, iComponent, val; const char *p, *q, *pStart, *pEnd; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; INChI_Stereo **pStereo = NULL; int bIso = (state==IST_MOBILE_H_ISO_SP3_S || state==IST_FIXED_H_ISO_SP3_S); if ( !bIso && state != IST_MOBILE_H_SP3_S && state != IST_FIXED_H_SP3_S ) { return RI_ERR_PROGR; /* program error */ } if ( str[0] != 's' ) return 0; pStart = str+1; iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ if ( !(pEnd = strchr( pStart, '/' )) ){ pEnd = pStart + strlen(pStart); } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = pStart; if ( pEnd == pStart ) { /* create empty sp3 segment */ int len = 0; s[bMobileH][bIso] = NO_VALUE_INT; /* empty */ /* create empty sp3 segment */ for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; if ( !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } pStereo[0]->nCompInv2Abs = 0; /* deliberately empty */ if ( pStereo[0]->nNumberOfStereoCenters ) { ret = RI_ERR_SYNTAX; /* syntax error: "/s" without a digit describes "no stereo" */ goto exit_function; } /* allocate empty sp3 stereo */ if ( !pStereo[0]->t_parity && !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || !pStereo[0]->nNumber && !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { /* cleanup */ if ( pStereo[0]->t_parity ) { INCHI_HEAPCHK inchi_free( pStereo[0]->t_parity ); pStereo[0]->t_parity = NULL; } if ( pStereo[0]->nNumber ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nNumber ); pStereo[0]->nNumber = NULL; } ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } ret = nNumComponents+1; } else { val = (int)inchi_strtol( p, &q, 10 ); if ( q == pEnd && 1 <= val && val <= 3 ) { s[bMobileH][bIso] = val; ret = nNumComponents+1; } else { ret = RI_ERR_SYNTAX; /* syntax error */ } } exit_function: return ret; } /****************************************************************************************/ int bIsSp3LayerNotEmpty( INChI *pInpInChI[], int bMobileH, int bIso, int nNumComponents ) { INChI *pInChI; INChI_Stereo *pStereo; int iComponent, num_not_empty = 0; if ( pInpInChI[bMobileH] ) { for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { pInChI = pInpInChI[bMobileH] + iComponent; if ( pInChI->bDeleted || !pInChI->nNumberOfAtoms ) { continue; } pStereo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; if ( pStereo && pStereo->nNumberOfStereoCenters > 0 && pStereo->nNumber && pStereo->t_parity ) { num_not_empty ++; } } } return num_not_empty; } /****************************************************************************************/ int ParseSegmentSp3m( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) { /* Pass 1: count bonds and find actual numbers of atom */ int nNumComponents, iComponent; const char *p, *pStart, *pEnd; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; INChI_Stereo **pStereo = NULL; int bIso = (state==IST_MOBILE_H_ISO_SP3_M || state==IST_FIXED_H_ISO_SP3_M); if ( !bIso && state != IST_MOBILE_H_SP3_M && state != IST_FIXED_H_SP3_M ) { return RI_ERR_PROGR; /* program error */ } nNumComponents = ppnNumComponents[bMobileH]; if ( str[0] != 'm' ) { /* /m is missing: check whether we have to inherit /m from a preceding stereo layer */ INChI_Stereo *pStereoFrom, *pStereoTo; INChI *pInChIFrom; int nNumCopied = 0, bMobileHFrom=-1, bIsoFrom=-1; if ( bMobileH && !bIso ) { return 0; /* Main non-isotopic cannot inherit: it has no preceding layer */ } else if ( !bMobileH && !bIso ) { /* fixed-H non-isotopic (F) inherits from Mobile-H non-isotopic (M) */ bMobileHFrom = TAUT_YES; bIsoFrom = 0; } else if ( bMobileH && bIso ) { /* Mobile-H isotopic (MI) inherits from Mobile-H non-isotopic (M) */ bMobileHFrom = TAUT_YES; bIsoFrom = 0; } else if ( !bMobileH && bIso ) { /* Fixed-H isotopic (FI) inherits from Fixed-H non-isotopic (F) */ bMobileHFrom = TAUT_NON; bIsoFrom = 0; /* if Sp3 is empty in F as well as in M, then inherit from MI */ if ( !bIsSp3LayerNotEmpty( pInpInChI, TAUT_NON, 0, ppnNumComponents[TAUT_NON /*bMobileH*/] ) /* F */ && !bIsSp3LayerNotEmpty( pInpInChI, TAUT_YES, 0, ppnNumComponents[TAUT_YES /*bMobileH*/] ) /* M */ ) { bMobileHFrom = TAUT_YES; bIsoFrom = 1; } } if ( bMobileHFrom < 0 || bIsoFrom < 0 ) { return RI_ERR_PROGR; } if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[/*bMobileH*/ bMobileHFrom] ) ) { /* nothing to copy; check whether it should have inherited from a preceding layer */ if ( !bMobileHFrom && bIsoFrom || bMobileHFrom && !bIsoFrom ) { /* MI or F inherit stereo from M */ bMobileHFrom = TAUT_YES; bIsoFrom = 0; if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[bMobileHFrom /*bMobileH*/] ) ) { return 0; } } else { return 0; } } nNumComponents = inchi_min( ppnNumComponents[bMobileH], ppnNumComponents[bMobileHFrom] ); for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { pInChIFrom = pInpInChI[bMobileHFrom] + iComponent; pInChI = pInpInChI[bMobileH] + iComponent; if ( pInChIFrom->nNumberOfAtoms > 0 && !pInChIFrom->bDeleted && pInChI->nNumberOfAtoms > 0 && !pInChI->bDeleted ) { pStereoFrom = bIsoFrom? pInChIFrom->StereoIsotopic : pInChIFrom->Stereo; pStereoTo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; if ( pStereoFrom && pStereoTo ) { pStereoTo->nCompInv2Abs = pStereoFrom->nCompInv2Abs; nNumCopied ++; } } } return 0; /* return value > 0 means the non-/m segment has been processed here */ } pStart = str+1; iComponent = 0; /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ if ( !(pEnd = strchr( pStart, '/' )) ) { pEnd = pStart + strlen(pStart); } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = pStart; if ( pEnd == pStart ) { /* create empty sp3 segment */ int len = 0; for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { INChI *pIsoInChI = &pInChI[iComponent]; pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; if ( !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* deliberately empty */ #ifdef NEVER if ( pStereo[0]->nNumberOfStereoCenters ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif /* allocate empty sp3 stereo */ if ( !pStereo[0]->t_parity && !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || !pStereo[0]->nNumber && !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { /* cleanup */ if ( pStereo[0]->t_parity ) { INCHI_HEAPCHK inchi_free( pStereo[0]->t_parity ); pStereo[0]->t_parity = NULL; } if ( pStereo[0]->nNumber ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nNumber ); pStereo[0]->nNumber = NULL; } ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } ret = nNumComponents+1; } else { while( p < pEnd && iComponent < nNumComponents ) { /* cycle over components */ pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; if ( *p != '.' && !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } switch( *p ) { case '1': pStereo[0]->nCompInv2Abs = -1; break; case '0': pStereo[0]->nCompInv2Abs = 1; break; case '.': if ( *pStereo ) { pStereo[0]->nCompInv2Abs = 0; } break; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } iComponent ++; p ++; } if ( p != pEnd || iComponent != nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = nNumComponents+1; } exit_function: return ret; } /****************************************************************************************/ int ParseSegmentSp3( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) { /* Pass 1: count bonds and find actual numbers of atom */ int i, mpy_component, val; int nNumComponents, iComponent, len, iAtom; AT_NUMB nAtom1; int atomParity; const char *p, *q, *t, *pStart, *pEnd, *r; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; INChI *pInChIFrom=NULL; /* INChI_Stereo *Stereo = NULL; INChI_Stereo *StereoOther = NULL; */ INChI_Stereo **pStereo = NULL; const char mult_type[] = "mnMNe"; const char parity_type[] = "-+u?"; int bIsoTo, bIsoFrom, nCpyType = CPY_SP3; int bIso = (state==IST_MOBILE_H_ISO_SP3 || state==IST_FIXED_H_ISO_SP3); int base = 10; if ( !bIso && state != IST_MOBILE_H_SP3 && state != IST_FIXED_H_SP3 ) { return RI_ERR_PROGR; /* program error */ } if ( str[0] != 't' ) return 0; pStart = str+1; iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; if ( !*pStart ) { /* create empty sp3 segment */ int len0 = 0; for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { INChI *pIsoInChI = &pInChI[iComponent]; pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; if ( !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } /* allocate empty sp3 stereo */ if ( !pStereo[0]->b_parity && !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len0+1, sizeof(pStereo[0]->b_parity[0]) ) ) || !pStereo[0]->nBondAtom1 && !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || !pStereo[0]->nBondAtom2 && !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { /* cleanup */ if ( pStereo[0]->b_parity ) { INCHI_HEAPCHK inchi_free( pStereo[0]->b_parity ); pStereo[0]->b_parity = NULL; } if ( pStereo[0]->nBondAtom1 ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nBondAtom1 ); pStereo[0]->nBondAtom1 = NULL; } if ( pStereo[0]->nBondAtom2 ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nBondAtom2 ); pStereo[0]->nBondAtom2 = NULL; } ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } pStereo[0]->nCompInv2Abs = NO_VALUE_INT; } ret = nNumComponents+1; goto exit_function; } while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (isdigit(*pStart) && 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || (q = pStart, val=1))&& (t=strchr(mult_type, *q)) && q+1 == pEnd ) { /* process the abbreviation */ ret = 0; #if (FIX_DALKE_BUGS == 1) if ( iComponent + val > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif switch( bMobileH ) { case TAUT_YES: switch( state ) { case IST_MOBILE_H_ISO_SP3: if ( *q == 'm' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; default: ret = RI_ERR_SYNTAX; break; } break; case TAUT_NON: switch( state ) { case IST_FIXED_H_SP3: if ( *q == 'm' ) { /* copy from mobile H to fixed H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 0; bIsoFrom = 0; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; case IST_FIXED_H_ISO_SP3: if ( *q == 'm' ) { /* copy from mobile H to fixed isotopic H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'M' ) { /* copy from isotopic mobile H to fixed isotopic H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 1; bIsoFrom = 1; } else if ( *q == 'n' ) { /* copy from fixed H to fixed isotopic H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; default: ret = RI_ERR_SYNTAX; break; } break; default: ret = RI_ERR_SYNTAX; break; } if ( ret < 0 ) { goto exit_function; } /* copy */ for ( i = 0; i < val; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); if ( !ret ) { ret = RI_ERR_SYNTAX; /* syntax error */ } if ( ret < 0 ) { goto exit_function; } if ( bIsoFrom >= 0 ) { INChI_Stereo *pStereoTo = bIsoTo? pInChI[iComponent+i].StereoIsotopic : pInChI[iComponent+i].Stereo; if ( pStereoTo ) { pStereoTo->nCompInv2Abs = NO_VALUE_INT; /* in case there in no /m segment after this */ } } } mpy_component = val; goto end_main_cycle; } else /* regular multiplier */ if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); if ( p != q ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p ++; /* move to the 1st character of the component */ } else { mpy_component = 1; p = pStart; } #if (FIX_DALKE_BUGS == 1) if ( iComponent + mpy_component > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif pStart = p; if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *p)? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; /* process the componnt: at1p,at1p,... */ /* pass 1: find number of stereoatoms */ if ( *pbAbc == 1 ) { for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (atomParity = (int)inchi_strtol( p, &p, 10), AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { ; /* okay */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else { for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p == ',') ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); p = q+1; if ( !nAtom1 || nAtom1 > pInChI[iComponent].nNumberOfAtoms || !(r = strchr( parity_type, *q) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } len = iAtom; /* memory allocation */ pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; if ( !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } if ( pStereo[0]->t_parity || pStereo[0]->nNumberOfStereoCenters || pStereo[0]->nNumber ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* allocate sp3 stereo */ if ( !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { /* cleanup */ if ( pStereo[0]->t_parity ) { INCHI_HEAPCHK inchi_free( pStereo[0]->t_parity ); pStereo[0]->t_parity = NULL; } if ( pStereo[0]->nNumber ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nNumber ); pStereo[0]->nNumber = NULL; } ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } /* pass 2: store stereocenters */ if ( *pbAbc == 1 ) { for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (atomParity = (int)inchi_strtol( p, &p, 10), AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { ; /* okay */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } pStereo[0]->t_parity[iAtom] = atomParity; pStereo[0]->nNumber[iAtom] = nAtom1; if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else { for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p==',') ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); if ( !(r = strchr( parity_type, *q) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q+1; atomParity = (r - parity_type) + 1; pStereo[0]->t_parity[iAtom] = atomParity; pStereo[0]->nNumber[iAtom] = nAtom1; if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } pStereo[0]->nNumberOfStereoCenters = iAtom; /*if ( iAtom ) {*/ pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* unknown yet */ /*}*/ if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* multiplier */ for ( i = 1; i < mpy_component; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); if ( !ret ) { ret = RI_ERR_SYNTAX; /* syntax error */ } if ( ret < 0 ) { goto exit_function; } ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, CPY_SP3_M, bIso, bIso ); if ( !ret ) { ret = RI_ERR_SYNTAX; /* syntax error */ } if ( ret < 0 ) { goto exit_function; } } end_main_cycle: iComponent += mpy_component; if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } if ( nNumComponents != iComponent ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentSp2( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) { /* Pass 1: count bonds and find actual numbers of atom */ int i, mpy_component, val; int nNumComponents, iComponent, len, iBond; AT_NUMB nAtom1, nAtom2; int bondParity; const char *p, *q, *t, *pStart, *pEnd, *r; int ret=0; INChI *pInChI = pInpInChI[bMobileH]; INChI *pInChIFrom=NULL; /* INChI_Stereo *Stereo = NULL; INChI_Stereo *StereoOther = NULL; */ INChI_Stereo **pStereo = NULL; const char mult_type[] = "mnMNe"; const char parity_type[] = "-+u?"; int bIsoTo, bIsoFrom, nCpyType = CPY_SP2; int bIso = (state==IST_MOBILE_H_ISO_SP2 || state==IST_FIXED_H_ISO_SP2); int base = 10; if ( !bIso && state != IST_MOBILE_H_SP2 && state != IST_FIXED_H_SP2 ) { return RI_ERR_PROGR; /* program error */ } if ( str[0] != 'b' ) return 0; pStart = str+1; iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; if ( !*pStart ) { /* creaste empty sp3 segment which means no sp3 */ for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { INChI *pIsoInChI = &pInChI[iComponent]; pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; if ( *pStereo && (pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* allocate empty sp3 stereo */ ret = CopySegment( pIsoInChI, NULL, CPY_SP2, bIso, -1); if ( ret < 0 ) { goto exit_function; } } ret = nNumComponents+1; goto exit_function; } while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (isdigit(*pStart) && 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || (q = pStart, val=1))&& (t=strchr(mult_type, *q)) && q+1 == pEnd ) { /* process the abbreviation */ ret = 0; #if (FIX_DALKE_BUGS == 1) if ( iComponent + val > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif switch( bMobileH ) { case TAUT_YES: switch( state ) { case IST_MOBILE_H_ISO_SP2: if ( *q == 'm' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; default: ret = RI_ERR_SYNTAX; break; } break; case TAUT_NON: switch( state ) { case IST_FIXED_H_SP2: if ( *q == 'm' ) { /* copy from mobile H to fixed H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 0; bIsoFrom = 0; } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; case IST_FIXED_H_ISO_SP2: if ( *q == 'm' ) { /* copy from mobile H to fixed isotopic H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'M' ) { /* copy from isotopic mobile H to fixed isotopic H */ pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; bIsoTo = 1; bIsoFrom = 1; } else if ( *q == 'n' ) { /* copy from fixed H to fixed isotopic H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = 0; } else if ( *q == 'e' ) { /* copy from mobile H to isotopic mobile H */ pInChIFrom = pInChI; bIsoTo = 1; bIsoFrom = -1; /* empty */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ } break; default: ret = RI_ERR_SYNTAX; break; } break; default: ret = RI_ERR_SYNTAX; break; } if ( ret < 0 ) { goto exit_function; } /* copy */ for ( i = 0; i < val; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); if ( !ret ) { ret = RI_ERR_SYNTAX; /* syntax error */ } if ( ret < 0 ) { goto exit_function; } } mpy_component = val; goto end_main_cycle; } else /* regular multiplier */ if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); if ( p != q ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p ++; /* move to the 1st character of the component */ } else { mpy_component = 1; p = pStart; } #if (FIX_DALKE_BUGS == 1) if ( iComponent + mpy_component > nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif pStart = p; if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *p)? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; if ( *pbAbc == 1 ) { /* process the componnt: at1-at2p,at1-at2p,... */ /* pass 1: find number of stereobonds */ for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { /* atoms 1, 2, and parity */ if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (bondParity = (int)inchi_strtol( p, &p, 10), AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { ; /* okay */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( nAtom1 <= nAtom2 || nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else { /* process the componnt: at1-at2p,at1-at2p,... */ /* pass 1: find number of stereobonds */ for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); if ( *q != '-' ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q+1; nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); if ( !nAtom1 || !nAtom2 || nAtom1 <= nAtom2 || nAtom1 > pInChI[iComponent].nNumberOfAtoms || !(r = strchr( parity_type, *q) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q+1; } } if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } len = iBond; /* memory allocation */ pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; if ( !*pStereo ) { if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } } if ( pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) { ret = RI_ERR_SYNTAX; /* syntax error: bonds have already been allocated */ goto exit_function; } /* allocate sp2 stereo */ if ( !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { /* cleanup */ if ( pStereo[0]->b_parity ) { INCHI_HEAPCHK inchi_free( pStereo[0]->b_parity ); pStereo[0]->b_parity = NULL; } if ( pStereo[0]->nBondAtom1 ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nBondAtom1 ); pStereo[0]->nBondAtom1 = NULL; } if ( pStereo[0]->nBondAtom2 ) { INCHI_HEAPCHK inchi_free( pStereo[0]->nBondAtom2 ); pStereo[0]->nBondAtom2 = NULL; } INCHI_HEAPCHK ret = RI_ERR_ALLOC; /* memory allocation failed */ goto exit_function; } /* pass 2: store stereobonds */ if ( *pbAbc == 1 ) { for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && (bondParity = (int)inchi_strtol( p, &p, 10), AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { ; /* okay */ } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } pStereo[0]->b_parity[iBond] = bondParity; pStereo[0]->nBondAtom1[iBond] = nAtom1; pStereo[0]->nBondAtom2[iBond] = nAtom2; if ( iBond && !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ goto exit_function; } } } else { for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); if ( *q != '-' ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q+1; nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); if ( !(r = strchr( parity_type, *q) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q+1; bondParity = (r - parity_type) + 1; pStereo[0]->b_parity[iBond] = bondParity; pStereo[0]->nBondAtom1[iBond] = nAtom1; pStereo[0]->nBondAtom2[iBond] = nAtom2; if ( iBond && !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ goto exit_function; } } } pStereo[0]->nNumberOfStereoBonds = iBond; if ( p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* multiplier */ for ( i = 1; i < mpy_component; i ++ ) { ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); if ( ret < 0 ) { goto exit_function; } } end_main_cycle: iComponent += mpy_component; if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } if ( nNumComponents != iComponent ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentProtons( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int ppnNumComponents[] ) { /* Pass 1: count bonds and find actual numbers of atom */ int val; const char *q, *pStart, *pEnd; int ret; if ( str[0] != 'p' ) return 0; pStart = str+1; while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { val = (int)inchi_strtol( pStart+1, &q, 10 ); } else if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { val = -(int)inchi_strtol( pStart+1, &q, 10 ); } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( !val ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } nNumProtons[bMobileH].nNumRemovedProtons = val; if ( *pEnd || q != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } else { break; } } ret = 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentCharge( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[] ) { /* Pass 1: count bonds and find actual numbers of atom */ int i, mpy_component, val; int nNumComponents, iComponent; const char *p, *q, *t, *pStart, *pEnd; int ret; INChI *pInChI = pInpInChI[bMobileH]; const char mult_type[] = "mnMNe"; if ( str[0] != 'q' ) { return 0; } pStart = str+1; iComponent = 0; nNumComponents = ppnNumComponents[bMobileH]; if ( !*pStart && bMobileH == TAUT_NON ) { for ( i = 0; i < nNumComponents; i ++ ) { pInChI[i].nTotalCharge = NO_VALUE_INT; } return nNumComponents+1; } while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (isdigit(UCINT *pStart) && 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || (q = pStart, val=1) )&& (t=strchr(mult_type, *q)) && q+1 == pEnd ) { /* process the abbreviation */ switch( bMobileH ) { case TAUT_YES: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; case TAUT_NON: if ( *q != 'm' || iComponent + val > nNumComponents || iComponent + val > ppnNumComponents[TAUT_YES] ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } for ( i = 0; i < val; i ++ ) { /* avoid 0 which means "omitted" */ pInChI[iComponent+i].nTotalCharge = pInpInChI[TAUT_YES][iComponent+i].nTotalCharge? pInpInChI[TAUT_YES][iComponent+i].nTotalCharge : NO_VALUE_INT; } mpy_component = val; goto end_main_cycle; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } }else if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); if ( p != q ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p ++; } else { mpy_component = 1; p = pStart; } #if ( FIX_DALKE_BUGS == 1 ) if ( mpy_component + iComponent > nNumComponents || mpy_component <= 0 ) { ret = RI_ERR_SYNTAX; /* syntax error: too many components in charge layer */ goto exit_function; } #endif pStart = p; if ( pStart < pEnd ) { if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { val = (int)inchi_strtol( pStart+1, &q, 10 ); pStart = q; } else if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { val = -(int)inchi_strtol( pStart+1, &q, 10 ); pStart = q; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #if ( FIX_DALKE_BUGS == 1 ) if ( val < -256 || val > 256 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif if ( !val ) { if ( pStart != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( bMobileH == TAUT_NON ) { val = NO_VALUE_INT; /* avoid 0 which means "omitted" */ } } } else { val = NO_VALUE_INT; } for ( i = 0; i < mpy_component; i ++ ) { pInChI[iComponent+i].nTotalCharge = val; } end_main_cycle: iComponent += mpy_component; if ( *pEnd ) { pStart = pEnd+1; continue; } else { break; } } if ( nNumComponents != iComponent ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: return ret; } /****************************************************************************************/ int ParseSegmentMobileH( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[], int *pbAbc ) { #define nNum_H( ICOMPONENT ) ((bMobileH==TAUT_YES)? pInChI[ICOMPONENT].nNum_H : pInChI[ICOMPONENT].nNum_H_fixed) /* Pass 1: count bonds and find actual numbers of atom */ int i, mpy_component, num_H, num_Minus, val, num_Atoms, numCtAtoms, tg_alloc_len, len, len2; int num_H_component, num_H_formula, num_taut_H_component, num_H_InChI, ret2; int nNumComponents, iComponent, nNumBonds, lenTautomer, tg_pos_Tautomer, iTGroup; const char *p, *q, *h, *t, *p1, *pTaut, *pStart, *pEnd; AT_NUMB curAtom, nxtAtom; int num_open, state, ret, nAltMobileH = ALT_TAUT(bMobileH); INChI *pInChI = pInpInChI[bMobileH]; INChI *pAltInChI = pInpInChI[nAltMobileH]; int base = 10; num_H = -999; /* impossible value */ num_Minus = -999; /* impossible value */ tg_pos_Tautomer = -999; /* impossible value */ /* number of immobile H is always allocated; immobile H are present in M layer only */ nNumComponents = pnNumComponents[bMobileH]; for ( i = 0; i < nNumComponents; i ++ ) { len = pInChI[i].nNumberOfAtoms; if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { if ( len < pAltInChI[i].nNumberOfAtoms ) { len = pAltInChI[i].nNumberOfAtoms; if ( pInChI[i].nNum_H ) { inchi_free( pInChI[i].nNum_H ); pInChI[i].nNum_H = NULL; } } } len ++; if ( !pInChI[i].nNum_H && /* allocate immobile H segment if it has not been allocated yet */ !(pInChI[i].nNum_H = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H[0]) )) ) { ret = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } /* copy immobile H from Mobile-H layer to Fixed-H layer */ if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { memcpy( pInChI[i].nNum_H, pAltInChI[i].nNum_H, (len-1) * sizeof(pInChI[0].nNum_H[0]) ); } } if ( str[0] != 'h' ) return 0; /* Read Hydrogen info in 1 pass */ pStart = str+1; iComponent = 0; nNumComponents = pnNumComponents[bMobileH]; while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); #if ( FIX_DALKE_BUGS == 1 ) if ( p != q || !isdigit(UCINT *pStart) ) /* prevent non-positive multipliers */ #else if ( p != q ) #endif { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p ++; } else { mpy_component = 1; p = pStart; } pStart = p; /* Pass 1.1 parse a component */ num_open = 0; state='\0'; /* initial state */ nNumBonds = 0; curAtom = 0; numCtAtoms = pInChI[iComponent].nNumberOfAtoms; if ( bMobileH == TAUT_NON && iComponent < pnNumComponents[nAltMobileH] ) { numCtAtoms = pAltInChI[iComponent].nNumberOfAtoms; } if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = (*p == ',' || isupper( UCINT *p))? 1 : 0; } base = (*pbAbc==1)? ALPHA_BASE : 10; /* immobile H */ t = pTaut = (*pbAbc==1)? strchr( p, ',' ) : strchr( p, '(' ); /* locate the first tautomer group character */ if ( t && bMobileH == TAUT_NON ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( !pTaut || pTaut > pEnd ) { pTaut = pEnd; t = NULL; /* found no tautomeric group for this component */ } for ( i = 0; i < mpy_component; i ++ ) { if ( bMobileH == TAUT_NON ) { /* allocate nNum_H_fixed */ if ( pInChI[iComponent+i].nNum_H_fixed ) { ret = RI_ERR_PROGR; /* program error */ goto exit_function; } if ( iComponent+i < pnNumComponents[nAltMobileH] ) { len = inchi_max(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms)+1; } else { len = pInChI[iComponent+i].nNumberOfAtoms + 1; } pInChI[iComponent+i].nNum_H_fixed = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H_fixed[0]) ); if ( !pInChI[iComponent+i].nNum_H_fixed ) { ret = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } /* compare nAtom */ if ( iComponent+i < pnNumComponents[nAltMobileH] ) { len2 = inchi_min(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms); if ( pInChI[iComponent+i].nAtom && len2 ) { /* check */ if ( memcmp( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, len2 * sizeof(pInChI[0].nAtom[0]) ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } /* allocate and copy atom if bridging H are present */ if ( pInChI[iComponent+i].nNumberOfAtoms < pAltInChI[iComponent+i].nNumberOfAtoms ) { if ( pInChI[iComponent+i].nAtom ) inchi_free( pInChI[iComponent+i].nAtom ); if ( !(pInChI[iComponent+i].nAtom = (U_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nAtom[0]) ) ) ) { ret = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } if ( len > 1 ) { memcpy( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, (len-1) * sizeof(pInChI[0].nAtom[0]) ); } /* correct number of atoms including bridging H */ pInChI[iComponent+i].nNumberOfAtoms = pAltInChI[iComponent+i].nNumberOfAtoms; } } } } if ( *pbAbc == 1 ) { /* read numbers of H: XnYn... or XYn... */ p = pStart; tg_alloc_len = 0; num_H_component = num_taut_H_component = 0; while ( p < pTaut ) { /* syntax check: atom number */ if ( !*p || !isupper( UCINT *p ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( curAtom = nxtAtom = (int)inchi_strtol( p, &q, base ) ) { p = q; if ( isupper( UCINT *p ) ) { nxtAtom = (int)inchi_strtol( p, &q, base ); p = q; } } if ( curAtom > nxtAtom || nxtAtom > numCtAtoms || p > pTaut ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* number of H, may be negative */ if ( !(num_H = (int)inchi_strtol( p, &q, 10 )) || q > pTaut ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q; /* set number of H */ for ( i = curAtom; i <= nxtAtom; i ++ ) { nNum_H(iComponent)[i-1] = num_H; num_H_component += num_H; } } if ( p != pTaut ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } else { /* read numbers of H: 1-2,3H2,4,5H3 */ p = pStart; tg_alloc_len = 0; num_H_component = num_taut_H_component = 0; while ( p < pTaut ) { /* syntax check: atom number */ if ( !*p || !isdigit( UCINT *p ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* number of H */ h = p + strcspn( p, "Hh" ); /*h = strchr( p, 'H' );*/ if ( !*h || h >= pTaut ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; /* p = pTaut; h = NULL; break; */ /* no more H found */ } num_H = (*h == 'H')? 1 : (*h == 'h')? -1 : 0; if ( !num_H ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( h[1] && isdigit( UCINT h[1] ) ) { num_H *= (int)inchi_strtol( h+1, &p1, 10 ); } else { p1 = h+1; /* next set of immobile H */ } if ( *p1 == ',' ) { p1 ++; /* next H-subsegment; otherwise (H or ; or end of the segment */ } /* list of atoms that have num_H */ while ( p < h ) { if ( !*p || !isdigit( UCINT *p ) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } nxtAtom = curAtom = (int)inchi_strtol( p, &q, 10 ); if ( *q == '-' ) { nxtAtom = (int)inchi_strtol( q+1, &q, 10 ); } /* consitency check */ if ( !curAtom || curAtom > numCtAtoms || nxtAtom < curAtom || nxtAtom > numCtAtoms ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* set number of H */ for ( i = curAtom; i <= nxtAtom; i ++ ) { nNum_H(iComponent)[i-1] = num_H; num_H_component += num_H; } /* move to the next atom number if any */ p = q; if ( *p == ',' ) { p ++; } } if ( p == h ) { p = p1; } else if ( p == pTaut ) { break; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } INCHI_HEAPCHK /* ) -> (, H, N, [-, N,], AtNum,... AtNum) */ lenTautomer = 0; if ( p = t ) { if ( *pbAbc == 1 ) { /* tautomeric groups: pass 1 */ iTGroup = 0; state = ')'; /* init as if the prev. t-group just ended */ num_Atoms = 0; /* Tautomeric info storage */ /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ if ( *p == ',' ) { /* start t-group */ p ++; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } while ( p < pEnd ) { /* start t-group */ if ( !isdigit( UCINT *p ) || !(num_H = (int)inchi_strtol( p, &q, 10 ) ) || q > pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q; num_Minus = 0; if ( *p == '-' ) { p ++; if ( isdigit( UCINT *p ) ) { num_Minus = (int)inchi_strtol( p, &q, 10 ); p = q; } else { num_Minus = 1; } } if ( p >= pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( !tg_alloc_len ) { /* --- header --- [num_t_groups] --- one t-group: --- [len=group length no including this value] [num_H] [num_(-)] [Endpoint(1),...,Endpoint(len-2)] --- next t-group --- ... Max. size = 1 + 3*max_num_t_groups + max_num_endpoints max_num_t_groups = num_at/2 max_num_endpoints = num_at Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments This does not include zero termination! */ tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; for ( i = 0; i < mpy_component; i ++ ) { pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); if ( !pInChI[iComponent+i].nTautomer ) { ret = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } pInChI[iComponent+i].lenTautomer = 0; } tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ } else { /* next t-group */ tg_pos_Tautomer = lenTautomer; } if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { ret = RI_ERR_PROGR; /* wrong tautomer array length */ goto exit_function; } pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ num_taut_H_component += num_H; while ( p < pEnd && isupper( UCINT *p) ) { /* read list of tautomeric atoms */ val = (int)inchi_strtol( p, &q, base ); if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { ret = RI_ERR_PROGR; /* wrong tautomer array length */ goto exit_function; } num_Atoms ++; pInChI[iComponent].nTautomer[lenTautomer ++] = val; p = q; } if ( !num_Atoms || p < pEnd && !isdigit( UCINT *p) ) { ret = RI_ERR_PROGR; /* wrong tautomer array length */ goto exit_function; } iTGroup ++; pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ pInChI[iComponent].lenTautomer = lenTautomer; } if ( !iTGroup || p != pEnd ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } pInChI[iComponent].nTautomer[0] = iTGroup; } else { /* tautomeric groups: pass 1 */ iTGroup = 0; state = ')'; /* init as if the prev. t-group just ended */ num_Atoms = 0; /* Tautomeric info storage */ /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ while ( p < pEnd ) { /* t-group */ switch ( *p ) { case '(': /* start t-group */ switch ( state ) { case ')': state = *p ++; num_H = 0; num_Minus = 0; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } case ')': /* end t-group */ switch ( state ) { case 'A': /* previuos was atom number */ if ( !tg_alloc_len ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } iTGroup ++; state = *p ++; pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ pInChI[iComponent].lenTautomer = lenTautomer; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } case 'H': /* number of H */ switch ( state ) { case '(': state = *p ++; num_H = 1; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } case '-': /* number of (-) */ switch ( state ) { case 'N': /* previous was number of H */ case 'H': /* previous was H */ state = *p ++; num_Minus = 1; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } case ',': switch ( state ) { case 'N': /* previous was number of H */ case 'H': /* previous was H */ case '-': /* previuos was - */ case 'M': /* previous was number of (-) */ /* the next must be the first tautomeric atom number; save num_H & num_Minus */ if ( num_H <= 0 && num_Minus <= 0 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( !tg_alloc_len ) { /* --- header --- [num_t_groups] --- one t-group: --- [len=group length no including this value] [num_H] [num_(-)] [Endpoint(1),...,Endpoint(len-2)] --- next t-group --- ... Max. size = 1 + 3*max_num_t_groups + max_num_endpoints max_num_t_groups = num_at/2 max_num_endpoints = num_at Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments This does not include zero termination! */ tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; for ( i = 0; i < mpy_component; i ++ ) { pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); if ( !pInChI[iComponent+i].nTautomer ) { ret = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } pInChI[iComponent+i].lenTautomer = 0; } tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ } else { /* next t-group */ tg_pos_Tautomer = lenTautomer; } if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { ret = RI_ERR_PROGR; /* wrong tautomer array length */ goto exit_function; } pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ num_taut_H_component += num_H; state = *p ++; continue; case 'A': /* previuos was atom number */ state = *p ++; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } default: if ( isdigit( UCINT *p ) ) { val = (int)inchi_strtol( p, &q, 10 ); if ( val <= 0 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p = q; switch( state ) { case 'H': num_H = val; state = 'N'; continue; case '-': num_Minus = val; state = 'M'; continue; case ',': if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { ret = RI_ERR_PROGR; /* wrong tautomer array length */ goto exit_function; } num_Atoms ++; pInChI[iComponent].nTautomer[lenTautomer ++] = val; state = 'A'; continue; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } if ( !iTGroup || state != ')' ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } pInChI[iComponent].nTautomer[0] = iTGroup; } } /* check num_H in components; for bMobileH=TAUT_NON, pInChI->nNum_H_fixed[] has not been added to pInChI->nNum_H[] yet */ if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent, &num_H_formula) ) || 0 > ( ret2 = GetInChINumH( pInChI+iComponent, &num_H_InChI ) ) ) { ret = ret2; goto exit_function; } if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* duplicate according to multipolication */ for ( i = 1; i < mpy_component; i ++ ) { memcpy( nNum_H(iComponent+i), nNum_H(iComponent), pInChI[iComponent+i].nNumberOfAtoms * sizeof(nNum_H(0)[0]) ); /* memcpy( pInChI[iComponent+i].nNum_H, pInChI[iComponent].nNum_H, pInChI[iComponent+i].nNumberOfAtoms * sizeof(pInChI[0].nNum_H[0]) ); */ if ( pInChI[iComponent+i].nTautomer && pInChI[iComponent].nTautomer && pInChI[iComponent].lenTautomer ) { memcpy( pInChI[iComponent+i].nTautomer, pInChI[iComponent].nTautomer, pInChI[iComponent].lenTautomer * sizeof(pInChI[0].nTautomer[0]) ); pInChI[iComponent+i].lenTautomer = pInChI[iComponent].lenTautomer; } /* check num_H in components */ if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent+i, &num_H_formula) ) || 0 > ( ret2 = GetInChINumH( pInChI+iComponent+i, &num_H_InChI ) ) ) { ret = ret2; goto exit_function; } if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } /* prepare for the next component */ iComponent += i; if ( *pEnd ) { #if (FIX_DALKE_BUGS == 1) /* prevent crash on extra trailing ';' */ if ( iComponent >= nNumComponents ) { ret = RI_ERR_SYNTAX; /* syntax error: extra component */ goto exit_function; } #endif pStart = pEnd+1; } else break; } if ( nNumComponents != iComponent ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } ret = iComponent + 1; exit_function: INCHI_HEAPCHK return ret; } /****************************************************************************************/ int ParseSegmentConnections( const char *str, int bMobileH, INChI **pInpInChI, int *pnNumComponents, int *pbAbc ) { #define LAST_AT_LEN 256 /* Pass 1: count bonds and find actual numbers of atom */ int i, j, k, m, c, mpy_component; int nNumComponents, iComponent, nNumAtoms, nNumBonds, lenConnTable, iBond; const char *p, *q, *pStart, *pEnd; AT_NUMB last_atom[LAST_AT_LEN], curAtom, maxAtom; int num_open, state, ret, base; INChI *pInChI = *pInpInChI; LINKED_BONDS LB; LINKED_BONDS *pLB = &LB; /* a list of linked lists of bonds, for each atom */ AT_NUMB neighbor[MAXVAL]; int bPrevVersion = -1; iComponent = 0; if ( str[0] != 'c' ) { if ( !pInChI && !*pnNumComponents ) { int lenFormula = 1; /* component has no formula; allocate InChI */ lenConnTable = 0; nNumComponents = 1; /* allocate InChI */ if ( !(pInChI = *pInpInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } /* allocate empty formula */ pInChI[iComponent].szHillFormula = (char *)inchi_calloc( lenFormula+1, sizeof(pInChI[0].szHillFormula[0]) ); if ( !pInChI[iComponent].szHillFormula ) { ret = RI_ERR_ALLOC; /* allocation failure */ goto exit_function; } /* allocate empty connection table */ pInChI[iComponent].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); if ( !pInChI[iComponent].nConnTable ) { ret = RI_ERR_ALLOC; /* allocation failure */ goto exit_function; } pInChI[iComponent].lenConnTable = lenConnTable; *pnNumComponents = nNumComponents; } else { lenConnTable = 1; nNumComponents = *pnNumComponents; for ( i = 0; i < nNumComponents; i ++ ) { /* allocate 1 atom connection table */ if ( pInChI[i].nConnTable ) { inchi_free( pInChI[i].nConnTable ); } pInChI[i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); if ( !pInChI[i].nConnTable ) { ret = RI_ERR_ALLOC; /* allocation failure */ goto exit_function; } pInChI[i].nConnTable[0] = 1; pInChI[i].lenConnTable = lenConnTable; } } return 0; } /* Pass 1. Re-Count atoms, count bonds */ pStart = str+1; nNumComponents = *pnNumComponents; #if (FIX_DALKE_BUGS == 1) /* prevent crash on too many components */ if ( nNumComponents > MAX_ATOMS ) { ret = RI_ERR_SYNTAX; /* syntax error: extra component */ goto exit_function; } #endif memset( pLB, 0, sizeof(pLB[0]) ); while( 1 ) { /* cycle over components */ if ( !(pEnd = strchr( pStart, ';' )) ) { pEnd = pStart + strlen(pStart); } if ( (p = strchr(pStart, '*')) && p < pEnd ) { mpy_component = (int)inchi_strtol( pStart, &q, 10 ); if ( p != q #if (FIX_DALKE_BUGS == 1) || !isdigit( UCINT *pStart ) #endif ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } p ++; } else { mpy_component = 1; p = pStart; } #if (FIX_DALKE_BUGS == 1) if ( iComponent + mpy_component > MAX_ATOMS ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } #endif pStart = p; /* Pass 1.1 parse a component */ num_open = 0; memset( last_atom, 0, sizeof(last_atom) ); state='\0'; /* initial state */ maxAtom = 0; nNumBonds = 0; curAtom = 0; if ( p < pEnd && *pbAbc == -1 ) { /* check if compressed InChI */ *pbAbc = isupper( UCINT *p)? 1 : 0; } base = *pbAbc? ALPHA_BASE : 10; if ( *pbAbc == 1 ) { nNumAtoms = 1; while ( p < pEnd ) { if ( *p == '-' ) { if ( bPrevVersion == -1 ) { /* previous InChI version */ bPrevVersion = 1; } else if ( bPrevVersion != 1 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } nNumAtoms --; p ++; } if ( isdigit( UCINT *p ) ) { if ( bPrevVersion == -1 ) { /* curreny InChI, version 1 */ bPrevVersion = 0; } else if ( bPrevVersion != 0 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } nNumAtoms -= inchi_strtol( p, &p, 10 ); /* bypass digits */ } if ( *p != '-' && ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) ) { nNumAtoms ++; nNumBonds ++; p = q; if ( maxAtom < curAtom ) maxAtom = curAtom; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } if ( maxAtom < nNumAtoms && nNumBonds ) { maxAtom = nNumAtoms; } } else { while ( p < pEnd ) { /* atom number */ c = UCINT *p ++; switch ( c ) { case '(': case ')': case ',': case '-': if ( state != 'N' ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } state = c; num_open += (c=='(') - (c==')'); if ( num_open < 0 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } break; default: if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { p = q; switch( state ) { case '(': case ')': case ',': case '-': nNumBonds ++; case '\0': if ( maxAtom < curAtom ) maxAtom = curAtom; state = 'N'; break; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } break; } } if ( num_open ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; /* syntax error: parentheses do not match */ } } /* Save the results and allocate memory */ nNumAtoms = (int)maxAtom; /* 0 if empty connection table and no bonds present */ lenConnTable = nNumAtoms + nNumBonds; /* connection table format: At1[,Neigh11,Neigh12,...],At2[,Neigh21,Neigh22,...],AtN[NeighN1,NeighN2,...] */ /* where AtK > NeighK1 > NeighK2,...; At(K) < At(K+1); the length = num.atoms + num.bonds */ for ( i = 0; i < mpy_component; i ++ ) { /* check number of atoms: the difference may be due to bridging H */ if ( (j = pInChI[iComponent+i].nNumberOfAtoms) < nNumAtoms ) { /* reallocate */ U_CHAR *nAtomTmp = (U_CHAR *) inchi_malloc( nNumAtoms + 1 ); if ( !nAtomTmp ) { ret = RI_ERR_ALLOC; /* allocation failure */ goto exit_function; } memcpy( nAtomTmp, pInChI[iComponent+i].nAtom, sizeof(nAtomTmp[0])*j); while ( j < nNumAtoms ) { nAtomTmp[j ++] = EL_NUMBER_H; /* bridging H */ } nAtomTmp[j] = '\0'; INCHI_HEAPCHK if ( pInChI[iComponent+i].nAtom ) { inchi_free( pInChI[iComponent+i].nAtom ); } pInChI[iComponent+i].nAtom = nAtomTmp; pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; } else if ( j > nNumAtoms && (lenConnTable || j != 1) ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* allocate connection table */ if ( pInChI[iComponent+i].nConnTable ) { inchi_free( pInChI[iComponent+i].nConnTable ); } if ( !nNumAtoms && !nNumBonds && !lenConnTable ) { lenConnTable = 1; /* one atom, no bonds */ } pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); if ( !pInChI[iComponent+i].nConnTable ) { ret = RI_ERR_ALLOC; /* allocation failure */ goto exit_function; } pInChI[iComponent+i].lenConnTable = lenConnTable; } /* Pass 1.2 parse a component and extract the bonds */ num_open = 0; memset( last_atom, 0, sizeof(last_atom) ); state='\0'; /* initial state */ iBond = 0; p = pStart; pLB->len = 0; if ( *pbAbc == 1 ) { /* compressed */ int num_neigh; num_open = 0; last_atom[num_open] = 2; while ( p < pEnd ) { if ( last_atom[num_open] > maxAtom ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( isupper( UCINT *p ) ) { curAtom = (AT_NUMB)inchi_strtol( p, &q, base ); if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } p = q; if ( bPrevVersion == 1 ) { while ( p < pEnd && *p == '-' ) { p ++; if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } p = q; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else if ( bPrevVersion == 0 && isdigit( *p ) ) { num_neigh = (int)inchi_strtol( p, &q, 10 ); p = q; while( num_neigh -- && p < pEnd ) { if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } p = q; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } last_atom[num_open] ++; } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } } else { while ( p < pEnd ) { /* each atom number except the first means a new bond */ c = UCINT *p ++; switch ( c ) { case '(': case ')': case ',': case '-': switch ( state ) { case 'N': state = c; break; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } break; default: if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { p = q; switch( state ) { case '\0': last_atom[num_open] = curAtom; state = 'N'; break; case '(': if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } if ( ++ num_open >= LAST_AT_LEN ) { ret = RI_ERR_PROGR; /* program error: buffer overflow */ goto exit_function; } last_atom[num_open] = curAtom; state = 'N'; break; case ')': if ( !num_open ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( ret = AddLinkedBond( last_atom[--num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } last_atom[num_open] = curAtom; state = 'N'; break; case ',': if ( !num_open ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } if ( ret = AddLinkedBond( last_atom[num_open-1], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } last_atom[num_open] = curAtom; state = 'N'; break; case '-': if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { goto exit_function; } last_atom[num_open] = curAtom; state = 'N'; break; default: ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } } else { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } break; } } } /* store the bonds in connection table */ if ( lenConnTable > 1 ) { for ( i = 0, m = 0; i < nNumAtoms; i ++ ) { k = 0; if ( j = pLB->pBond[i+1].prev ) { while( k < MAXVAL ) { neighbor[k++] = pLB->pBond[j].neigh; if ( j == i+1 ) break; j = pLB->pBond[j].prev; } } if ( j != i+1 ) { ret = RI_ERR_SYNTAX; /* syntax error */ goto exit_function; } /* sort the neighbors */ insertions_sort_AT_NUMB( neighbor, k ); pInChI[iComponent].nConnTable[m ++] = i+1; /* atom number */ for ( j = 0; j < k && (int)neighbor[j] <= i; j ++ ) { pInChI[iComponent].nConnTable[m ++] = neighbor[j]; } } if ( m != lenConnTable ) { ret = RI_ERR_PROGR; /* program error */ goto exit_function; } } else { pInChI[iComponent].nConnTable[0] = 1; /* single atom */ } /* duplicate if needed */ for ( i = 1; i < mpy_component; i ++ ) { /* if ( pInChI[iComponent+i].nConnTable ) { inchi_free( pInChI[iComponent+i].nConnTable ); } pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); if ( !pInChI[iComponent+i].nConnTable ) { ret = RI_ERR_ALLOC; goto exit_function; } */ if ( !pInChI[iComponent+i].nConnTable || pInChI[iComponent+i].lenConnTable != lenConnTable ) { ret = RI_ERR_PROGR; goto exit_function; } memcpy ( pInChI[iComponent+i].nConnTable, pInChI[iComponent].nConnTable, lenConnTable*sizeof(pInChI[0].nConnTable[0])); } /* prepare for the next connection table */ iComponent += i; if ( *pEnd ) pStart = pEnd+1; else break; } ret = iComponent; exit_function: if ( pLB->pBond ) { INCHI_HEAPCHK inchi_free( pLB->pBond ); } return ret; #undef LAST_AT_LEN } /****************************************************************************************/ int nFillOutProtonMobileH( INChI *pInChI ) { int len = 1; pInChI->bDeleted = 1; /* formula */ if ( !pInChI->szHillFormula && !( pInChI->szHillFormula = (char *) inchi_calloc( len+1, sizeof(pInChI->szHillFormula[0]) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } strcpy( pInChI->szHillFormula, "H" ); pInChI->nNumberOfAtoms = 1; /* atoms */ if ( !pInChI->nAtom && !(pInChI->nAtom = (U_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nAtom[0]) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInChI->nAtom[0] = 1; /* charge */ pInChI->nTotalCharge = 1; /* connection table */ if ( !pInChI->nConnTable && !(pInChI->nConnTable = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nConnTable[0]) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInChI->nConnTable[0] = 1; pInChI->lenConnTable = len; /* tautomer */ if ( !pInChI->nTautomer && !(pInChI->nTautomer = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nTautomer[0]) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } /* nNum_H */ if ( !pInChI->nNum_H && !(pInChI->nNum_H = (S_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nNum_H[0]) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInChI->nNum_H[0] = 0; pInChI->nTautomer[0] = 0; pInChI->lenTautomer = 1; return 0; } /****************************************************************************************/ int nProtonCopyIsotopicInfo( INChI *pInChI_to, INChI *pInChI_from ) { if ( pInChI_from->nNumberOfIsotopicAtoms ) { if ( pInChI_to->nNumberOfIsotopicAtoms && pInChI_from->nNumberOfIsotopicAtoms > pInChI_to->nNumberOfIsotopicAtoms ) { inchi_free( pInChI_to->IsotopicAtom ); pInChI_to->IsotopicAtom = NULL; pInChI_to->nNumberOfIsotopicAtoms = 0; } if ( !pInChI_to->IsotopicAtom && !(pInChI_to->IsotopicAtom = (INChI_IsotopicAtom *)inchi_calloc(pInChI_from->nNumberOfIsotopicAtoms, sizeof(pInChI_to->IsotopicAtom[0]) ) ) ) { return RI_ERR_ALLOC; } pInChI_to->nNumberOfIsotopicAtoms = pInChI_from->nNumberOfIsotopicAtoms; memcpy( pInChI_to->IsotopicAtom, pInChI_from->IsotopicAtom, pInChI_from->nNumberOfIsotopicAtoms * sizeof(pInChI_to->IsotopicAtom[0]) ); } else { if ( pInChI_to->IsotopicAtom ) inchi_free( pInChI_to->IsotopicAtom ); pInChI_to->IsotopicAtom = NULL; pInChI_to->nNumberOfIsotopicAtoms = 0; } return 0; } /****************************************************************************************/ int ParseSegmentFormula( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[] ) { int i, j, mpy_component, mpy_atom, len, el_number; int nNumComponents = 0, iComponent, nNumAtoms, nNumAtomsAndH, iAtom, nNumH, nAltMobileH = ALT_TAUT(bMobileH); const char *p, *q, *e, *pStart, *pEnd; INChI *pInChI; char szEl[3]; nNumAtoms = -999; /* impossible value */ /* Pass 1. Count components */ pStart = str; while( 1 ) { if ( !(pEnd = strchr( pStart, '.' )) ) { pEnd = pStart + strlen(pStart); } p = pStart; if ( isdigit( *p ) ) { mpy_component = (int)inchi_strtol( p, &q, 10 ); p = q; } else { mpy_component = 1; } if ( !mpy_component ) break; if ( !isupper( UCINT *p ) ) { break; /* not a formula layer */ } if ( pEnd == p ) break; /* zero length formula */ nNumComponents += mpy_component; if ( *pEnd ) pStart = pEnd+1; else break; } pnNumComponents[bMobileH] = nNumComponents; #if ( FIX_DALKE_BUGS == 1 ) if ( nNumComponents > MAX_ATOMS ) { return RI_ERR_SYNTAX; /* syntax error */ } #endif /* exit or error check */ if ( !nNumComponents ) { if ( !*pStart || islower( UCINT *pStart ) ) { INCHI_HEAPCHK if ( bMobileH == TAUT_NON && 0 < ( nNumComponents = pnNumComponents[nAltMobileH]) ) { /* allocate InChI */ if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInpInChI[bMobileH] = pInChI; pnNumComponents[bMobileH] = nNumComponents; for ( i = 0; i < nNumComponents; i ++ ) { /* copy number of atoms */ len = pInpInChI[bMobileH][i].nNumberOfAtoms = pInpInChI[nAltMobileH][i].nNumberOfAtoms; /* copy atoms */ len = (len+1)*sizeof(pInpInChI[0][0].nAtom[0]); if ( pInpInChI[bMobileH][i].nAtom ) { inchi_free( pInpInChI[bMobileH][i].nAtom ); } if ( pInpInChI[bMobileH][i].nAtom = (U_CHAR *) inchi_malloc( (len + 1) * sizeof(pInpInChI[0][0].nAtom[0]) ) ) { memcpy(pInpInChI[bMobileH][i].nAtom, pInpInChI[nAltMobileH][i].nAtom, len); pInpInChI[bMobileH][i].nAtom[len] = 0; } else { return RI_ERR_ALLOC; /* alloc failure */ } /* copy Hill formula */ len = strlen( pInpInChI[nAltMobileH][i].szHillFormula)+1; if ( pInpInChI[bMobileH][i].szHillFormula ) { inchi_free( pInpInChI[bMobileH][i].szHillFormula ); } if ( pInpInChI[bMobileH][i].szHillFormula = (char *) inchi_malloc( inchi_max(len,2) ) ) { memcpy( pInpInChI[bMobileH][i].szHillFormula, pInpInChI[nAltMobileH][i].szHillFormula, len); } else { return RI_ERR_ALLOC; /* alloc failure */ } } } else if ( bMobileH == TAUT_YES ) { int ret; /* allocate InChI */ nNumComponents = 1; /* InChI */ pnNumComponents[bMobileH] = nNumComponents; if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInpInChI[bMobileH] = pInChI; ret = nFillOutProtonMobileH( pInChI ); if ( ret < 0 ) { return ret; } } return 0; } return RI_ERR_SYNTAX; /* syntax error */ } if ( *pEnd ) { return RI_ERR_SYNTAX; /* syntax error */ } /* allocate InChI */ if ( !( pInpInChI[bMobileH] = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { return RI_ERR_ALLOC; /* alloc failure */ } pInChI = pInpInChI[bMobileH]; /* Pass 2. Count elements, save formulas and elements */ pStart = str; iComponent = 0; while( 1 ) { if ( !(pEnd = strchr( pStart, '.' )) ) { pEnd = pStart + strlen(pStart); } p = pStart; if ( isdigit( UCINT *p ) ) { mpy_component = (int)inchi_strtol( p, &q, 10 ); p = q; } else { mpy_component = 1; } #if ( FIX_DALKE_BUGS == 1 ) if ( iComponent + mpy_component > MAX_ATOMS ) { return RI_ERR_SYNTAX; /* syntax error */ } #endif len = pEnd-p; for ( i = 0; i < mpy_component; i ++ ) { if ( pInChI[iComponent+i].szHillFormula ) { inchi_free( pInChI[iComponent+i].szHillFormula ); } pInChI[iComponent+i].szHillFormula = (char*) inchi_malloc( inchi_max(len,1)+1 ); memcpy( pInChI[iComponent].szHillFormula, p, len ); pInChI[iComponent+i].szHillFormula[len] = '\0'; if ( !i ) { /* Pass 2.1 Parse formula and count atoms except H */ nNumAtoms = 0; nNumH = 0; nNumAtomsAndH = 0; e = pInChI[iComponent].szHillFormula; while ( *e ) { if ( !isupper( UCINT *e ) ) { return RI_ERR_SYNTAX; } j = 0; szEl[j ++] = *e ++; if ( *e && islower( UCINT *e ) ) szEl[j ++] = *e ++; szEl[j ++] = '\0'; if ( *e && isdigit( UCINT *e ) ) { mpy_atom = (int)inchi_strtol( e, &q, 10 ); e = q; } else { mpy_atom = 1; } if ( !mpy_atom ) { return RI_ERR_SYNTAX; } if ( szEl[0] == 'H' && !szEl[1] ) { nNumH += mpy_atom; continue; /* ignore H in counting number of atoms */ } nNumAtoms += mpy_atom; } #if ( FIX_DALKE_BUGS == 1 ) if ( nNumAtoms > MAX_ATOMS ) { return RI_ERR_SYNTAX; /* syntax error */ } #endif nNumAtomsAndH = nNumAtoms? nNumAtoms : (nNumH > 0); pInChI[iComponent+i].nNumberOfAtoms = nNumAtomsAndH; if ( pInChI[iComponent+i].nAtom ) { inchi_free( pInChI[iComponent+i].nAtom ); } pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc((nNumAtomsAndH+1)*sizeof(pInChI[0].nAtom[0])); if ( !pInChI[iComponent+i].nAtom ) return RI_ERR_ALLOC; /* failed allocation */ /* Pass 2.2 Store elements; this assumes no bridging H. Bridging H will be found in connection table, /c */ iAtom = 0; if ( nNumAtoms > 0 ) { e = pInChI[iComponent+i].szHillFormula; while ( *e ) { if ( !isupper( UCINT *e ) ) { return RI_ERR_SYNTAX; } j = 0; szEl[j ++] = *e ++; if ( *e && islower( UCINT *e ) ) szEl[j ++] = *e ++; szEl[j ++] = '\0'; if ( *e && isdigit( UCINT *e ) ) { mpy_atom = (int)inchi_strtol( e, &q, 10 ); e = q; } else { mpy_atom = 1; } if ( !mpy_atom ) { return RI_ERR_SYNTAX; } if ( szEl[0] == 'H' && !szEl[1] ) continue; /* ignore H */ el_number = get_periodic_table_number( szEl ); if ( el_number == ERR_ELEM ) { return RI_ERR_SYNTAX; /* wrong element */ } while ( mpy_atom -- ) { if ( iAtom >= nNumAtoms ) { return RI_ERR_PROGR; /* program error */ } pInChI[iComponent+i].nAtom[iAtom ++] = (U_CHAR)el_number; } } } else if ( nNumH > 0 ) { pInChI[iComponent+i].nAtom[iAtom ++] = EL_NUMBER_H; nNumAtoms = 1; } pInChI[iComponent+i].nAtom[iAtom] = '\0'; if ( nNumAtoms != iAtom ) { return RI_ERR_PROGR; /* program error */ } } else { /* Copy duplicated formula */ strcpy(pInChI[iComponent+i].szHillFormula, pInChI[iComponent].szHillFormula); /* Copy atoms in the duplicated formula */ pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; if ( pInChI[iComponent+i].nAtom ) { inchi_free( pInChI[iComponent+i].nAtom ); } pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc(nNumAtoms+1); if ( !pInChI[iComponent+i].nAtom ) return RI_ERR_ALLOC; /* failed allocation */ memcpy( pInChI[iComponent+i].nAtom, pInChI[iComponent].nAtom, nNumAtoms+1 ); } } iComponent += i; if ( *pEnd ) { if ( *pEnd != '.' ) { return RI_ERR_SYNTAX; /* syntax error */ } pStart = pEnd+1; } else break; } if ( iComponent != nNumComponents ) { return RI_ERR_PROGR; /* program error */ } if ( bMobileH == TAUT_NON ) { /* at this point the exact number of atoms including bridging H is known from TAUT_YES */ for ( i = 0; i < nNumComponents && i < pnNumComponents[nAltMobileH]; i ++ ) { if ( pInpInChI[bMobileH][i].nNumberOfAtoms < (len=pInpInChI[nAltMobileH][i].nNumberOfAtoms) ) { /* there are bridging H in this component */ if ( pInpInChI[nAltMobileH][i].nAtom ) { U_CHAR *nAtom = (U_CHAR *) inchi_malloc( (len+1) * sizeof(nAtom[0]) ); if ( !nAtom ) { return RI_ERR_ALLOC; } memcpy( nAtom, pInpInChI[nAltMobileH][i].nAtom, len*sizeof(nAtom[0]) ); nAtom[ len ] = 0; if ( pInpInChI[bMobileH][i].nAtom ) { inchi_free( pInpInChI[bMobileH][i].nAtom ); } pInpInChI[bMobileH][i].nAtom = nAtom; } pInpInChI[bMobileH][i].nNumberOfAtoms = len; } } } return nNumComponents+1; } /****************************************************************************************/ int CopySegment( INChI *pInChITo, INChI *pInChIFrom, int SegmentType, int bIsotopicTo, int bIsotopicFrom) { int ret = RI_ERR_ALLOC; int len; if ( SegmentType==CPY_SP2 || SegmentType==CPY_SP3 || SegmentType==CPY_SP3_M || SegmentType==CPY_SP3_S ) { INChI_Stereo **pstereoTo = NULL; INChI_Stereo *stereoFrom = bIsotopicFrom==1? pInChIFrom->StereoIsotopic : bIsotopicFrom==0? pInChIFrom->Stereo : NULL; if ( stereoFrom || bIsotopicFrom < 0 ) { if ( SegmentType==CPY_SP2 ) { if ( bIsotopicFrom < 0 || stereoFrom->b_parity && stereoFrom->nBondAtom1 && stereoFrom->nBondAtom2 ) { len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoBonds; pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; if ( !pstereoTo[0] ) { if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { goto exit_function; } } if ( pstereoTo[0]->nNumberOfStereoBonds > 0 || pstereoTo[0]->b_parity || pstereoTo[0]->nBondAtom1 || pstereoTo[0]->nBondAtom2 ) { ret = RI_ERR_SYNTAX; /* stereo already exists */ goto exit_function; } /* allocate sp2 stereo */ if ( !(pstereoTo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || !(pstereoTo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) || !(pstereoTo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom2[0]) ) ) ) { /* cleanup */ if ( pstereoTo[0]->b_parity ) { INCHI_HEAPCHK inchi_free( pstereoTo[0]->b_parity ); pstereoTo[0]->b_parity = NULL; } if ( pstereoTo[0]->nBondAtom1 ) { INCHI_HEAPCHK inchi_free( pstereoTo[0]->nBondAtom1 ); pstereoTo[0]->nBondAtom1 = NULL; } if ( pstereoTo[0]->nBondAtom2 ) { INCHI_HEAPCHK inchi_free( pstereoTo[0]->nBondAtom2 ); pstereoTo[0]->nBondAtom2 = NULL; } INCHI_HEAPCHK goto exit_function; } /* copy stereo */ if ( bIsotopicFrom >= 0 && len ) { memcpy( pstereoTo[0]->b_parity, stereoFrom->b_parity, (len+1)*sizeof(pstereoTo[0]->b_parity[0]) ); memcpy( pstereoTo[0]->nBondAtom1, stereoFrom->nBondAtom1, (len+1)*sizeof(pstereoTo[0]->nBondAtom1[0]) ); memcpy( pstereoTo[0]->nBondAtom2, stereoFrom->nBondAtom2, (len+1)*sizeof(pstereoTo[0]->nBondAtom2[0]) ); } pstereoTo[0]->nNumberOfStereoBonds = len; return len+1; } else { return 0; } } else if ( SegmentType==CPY_SP3 ) { if ( bIsotopicFrom < 0 || stereoFrom->t_parity && stereoFrom->nNumber ) { len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoCenters; pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; if ( !pstereoTo[0] ) { if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { goto exit_function; } } if ( pstereoTo[0]->nNumberOfStereoCenters > 0 || pstereoTo[0]->t_parity || pstereoTo[0]->nNumber ) { ret = RI_ERR_SYNTAX; /* stereo already exists */ goto exit_function; } /* allocate sp3 stereo */ if ( !(pstereoTo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || !(pstereoTo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) ) { /* cleanup */ if ( pstereoTo[0]->t_parity ) { inchi_free( pstereoTo[0]->t_parity ); pstereoTo[0]->t_parity = NULL; } if ( pstereoTo[0]->nNumber ) { inchi_free( pstereoTo[0]->nNumber ); pstereoTo[0]->nNumber = NULL; } goto exit_function; } /* copy stereo */ if ( bIsotopicFrom >= 0 && len ) { memcpy( pstereoTo[0]->t_parity, stereoFrom->t_parity, (len+1)*sizeof(pstereoTo[0]->t_parity[0]) ); memcpy( pstereoTo[0]->nNumber, stereoFrom->nNumber, (len+1)*sizeof(pstereoTo[0]->nNumber[0]) ); } pstereoTo[0]->nNumberOfStereoCenters = len; return len+1; } else { return 0; } } else if ( SegmentType==CPY_SP3_M ) { pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; if ( !pstereoTo[0] ) { if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { goto exit_function; } } if ( pstereoTo[0]->nCompInv2Abs && NO_VALUE_INT != pstereoTo[0]->nCompInv2Abs ) { ret = RI_ERR_SYNTAX; /* stereo already exists */ goto exit_function; } if ( bIsotopicFrom < 0 ) { pstereoTo[0]->nCompInv2Abs = 0; } else { pstereoTo[0]->nCompInv2Abs = stereoFrom->nCompInv2Abs; } return 1; } else /* use bTrivialInv to save /s1, /s2, /s3 */ if ( SegmentType==CPY_SP3_S ) { pstereoTo = bIsotopicFrom? &pInChITo->StereoIsotopic : &pInChITo->Stereo; if ( !pstereoTo[0] ) { if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { goto exit_function; } } if ( pstereoTo[0]->bTrivialInv ) { ret = RI_ERR_SYNTAX; /* stereo already exists */ goto exit_function; } pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; if ( bIsotopicFrom < 0 ) { pstereoTo[0]->bTrivialInv = 0; } else { pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; } return 1; } } return 0; /* nothing to copy */ } else if ( SegmentType == CPY_ISO_AT ) { int nNumberOfIsotopicAtoms = pInChIFrom->nNumberOfIsotopicAtoms; INChI_IsotopicAtom **pIsotopicAtomTo = NULL; INChI_IsotopicAtom *IsotopicAtomFrom = pInChIFrom->IsotopicAtom; if ( bIsotopicFrom < 0 || IsotopicAtomFrom ) { len = (bIsotopicFrom < 0)? 0 : nNumberOfIsotopicAtoms; pIsotopicAtomTo = &pInChITo->IsotopicAtom; if ( !*pIsotopicAtomTo ) { if ( !( *pIsotopicAtomTo = (INChI_IsotopicAtom *)inchi_calloc( len+1, sizeof(**pIsotopicAtomTo) ) ) ) { goto exit_function; } } if ( pInChITo->nNumberOfIsotopicAtoms ) { ret = RI_ERR_SYNTAX; /* stereo already exists */ goto exit_function; } if ( bIsotopicFrom >= 0 && len ) { memcpy( *pIsotopicAtomTo, IsotopicAtomFrom, (len+1)*sizeof(**pIsotopicAtomTo) ); } pInChITo->nNumberOfIsotopicAtoms = len; return len+1; } return 0; } ret = RI_ERR_PROGR; /* program error */ exit_function: return ret; } /**********************************************************************************/ /* Sort neighbors in ascending order */ int insertions_sort_AT_NUMB( AT_NUMB *base, int num ) { AT_NUMB *i, *j, *pk, tmp; int k, num_trans = 0; for( k=1, pk = base; k < num; k++, pk ++ ) { for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { *j = *i; num_trans ++; } *j = tmp; } return num_trans; } /* read */ int getInChIChar( INCHI_IOSTREAM *pInp ) { if (pInp->type==INCHI_IOSTREAM_STRING) { /* input from string */ if ( pInp->s.nPtr < pInp->s.nUsedLength ) return (int) pInp->s.pStr[pInp->s.nPtr++]; return RI_ERR_EOF; } else { /* input from plain file */ int c; #if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) do { c = getc( pInp->f ); if ( c == EOF ) { c = RI_ERR_EOF; break; } } while( c == '\r' ); #else c = getc( pInp->f ); if ( c == EOF ) { c = RI_ERR_EOF; } #endif return c; } } int AddInChIChar( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) { int c = getInChIChar( pInp ); /* while ( c == '\r' ) { c = getInChIChar( pInp ); } */ INCHI_HEAPCHK if ( Line->len + 2 >= Line->len_alloc ) { char *str = (char *) inchi_calloc( Line->len_alloc + SEGM_LINE_ADD, sizeof(str[0]) ); INCHI_HEAPCHK if ( str ) { if ( Line->len > 0 && Line->str ) { memcpy( str, Line->str, sizeof(str[0]) * Line->len ); Line->len_alloc += SEGM_LINE_ADD; inchi_free( Line->str ); INCHI_HEAPCHK } else { Line->len_alloc += SEGM_LINE_ADD; } Line->str = str; } else { c = RI_ERR_ALLOC; /* fatal error */ goto exit_function; } } INCHI_HEAPCHK if ( c < 0 ) { Line->str[Line->len] = '\0'; INCHI_HEAPCHK c = RI_ERR_SYNTAX; /* fatal error: wrong char */ goto exit_function; } if ( c && strchr( pszToken, c ) ) { Line->str[Line->len] = '\0'; INCHI_HEAPCHK c = -(c+2); goto exit_function; } else if ( !c && !Line->len ) { Line->str[Line->len] = c; INCHI_HEAPCHK } else { Line->str[Line->len ++] = c; INCHI_HEAPCHK } exit_function: INCHI_HEAPCHK return c; } int nGetInChISegment( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) { int c; Line->len = 0; while( 0 < (c = AddInChIChar( pInp, Line, pszToken ) ) ) ; if ( c < - 2 ) { c = -(c+2); } Line->c = c; return c; } /********************************************************************************/ /* add one more bond to the linked lists for both neighbors */ int AddLinkedBond( AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB ) { int nReqLen = inchi_max( 2*num_at+2, pLB->len + 2 ); AT_NUMB prev; if ( pLB->len_alloc <= nReqLen ) { /*int nNewLen = nReqLen + (nReqLen + LINKED_BOND_ADD - 1)%LINKED_BOND_ADD + LINKED_BOND_ADD;*/ int nNewLen = nReqLen - nReqLen%LINKED_BOND_ADD + 2*LINKED_BOND_ADD; ONE_LINKED_BOND *pBond = (ONE_LINKED_BOND *)inchi_calloc( nNewLen, sizeof(pBond[0]) ); if ( !pBond ) return RI_ERR_ALLOC; /* allocation error */ if ( pLB->pBond && pLB->len ) { memcpy( pBond, pLB->pBond, pLB->len*sizeof(pBond[0]) ); } if ( pLB->pBond ) inchi_free( pLB->pBond ); pLB->pBond = pBond; pLB->len_alloc = nNewLen; } if ( !pLB->len ) { pLB->len = num_at+1; memset( pLB->pBond, 0, (num_at+1)*sizeof(pLB->pBond[0]) ); } prev = pLB->pBond[at1].prev; /* position of the last neighbor of at1 in the pLB->pBond */ if ( !prev ) { pLB->pBond[at1].neigh = at2; pLB->pBond[at1].prev = at1; } else { pLB->pBond[pLB->len].neigh = at2; pLB->pBond[pLB->len].prev = prev; pLB->pBond[at1].prev = pLB->len ++; } prev = pLB->pBond[at2].prev; /* position of the last neighbor of at2 in the pLB->pBond */ if ( !prev ) { pLB->pBond[at2].neigh = at1; pLB->pBond[at2].prev = at2; } else { pLB->pBond[pLB->len].neigh = at1; pLB->pBond[pLB->len].prev = prev; pLB->pBond[at2].prev = pLB->len ++; } return 0; } #endif /* READ_INCHI_STRING */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiring.c000066400000000000000000000255631271037650300235240ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "inpdef.h" #include "extr_ct.h" #include "ichiring.h" /* local prototypes */ int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); /*******************************************************************/ /* add to the queue */ int QueueAdd( QUEUE *q, QINT_TYPE *Val ); /* read & remove from the queue */ int QueueGet( QUEUE *q, QINT_TYPE *Val ); /* read from the queue */ int QueueGetAny( QUEUE *q, QINT_TYPE *, int ord ); /* initialize the queue */ int QueueReinit( QUEUE *q ); /* current queue length */ int QueueLength( QUEUE *q ); /* number of used queue internal elements */ int QueueWrittenLength( QUEUE *q ); #if ( QUEUE_QINT == 1 ) /* { */ QUEUE *QueueCreate( int nTotLength, int nSize ) { QUEUE *q = NULL; QINT_TYPE *Val = NULL; if ( nTotLength < 1 || nSize != (int)sizeof(QINT_TYPE) || !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { if ( q ) inchi_free(q); return NULL; } q->Val = Val; /* q->nSize = nSize; */ q->nTotLength = nTotLength; return q; } int QueueAdd( QUEUE *q, QINT_TYPE *Val ) { if ( q && Val && q->nLength < q->nTotLength ) { q->Val[ (q->nFirst + q->nLength) % q->nTotLength ] = *Val; q->nLength ++; return q->nLength; } return -1; } int QueueGet( QUEUE *q, QINT_TYPE *Val ) { if ( q && Val && q->nLength > 0 ) { *Val = q->Val[ q->nFirst ]; /* new: do not allow to overwrite the retrieved value */ q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; q->nLength --; /* -- old -- if ( -- q->nLength ) { q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; } */ return q->nLength; } return -1; } int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) { if ( 0 <= ord && ord < q->nTotLength ) { *Val = q->Val[ ord ]; return 1; /* success */ } else { return -1; /* error */ } } #else /* } QUEUE_QINT == 1 { */ QUEUE *QueueCreate( int nTotLength, int nSize ) { QUEUE *q = NULL; QINT_TYPE *Val = NULL; if ( nTotLength < 1 || nSize < 1 || !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { if ( q ) inchi_free(q); return NULL; } q->Val = Val; q->nSize = nSize; q->nTotLength = nTotLength; return q; } int QueueAdd( QUEUE *q, QINT_TYPE *Val ) { if ( q && Val && q->nLength < q->nTotLength ) { memcpy( (char*)q->Val + ((q->nFirst + q->nLength) % q->nTotLength)*q->nSize, Val, q->nSize); q->nLength ++; return q->nLength; } return -1; } int QueueGet( QUEUE *q, QINT_TYPE *Val ) { if ( q && Val && q->nLength > 0 ) { memcpy( Val, (char*)q->Val + q->nFirst * q->nSize, q->nSize); if ( -- q->nLength ) { q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; } return q->nLength; } return -1; } int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) { if ( 0 <= ord && ord < q->nTotLength ) { memcpy( Val, (char*)q->Val + ord * q->nSize, q->nSize); return 1; /* success */ } else { return -1; /* error */ } } #endif /* } QUEUE_QINT == 1 */ QUEUE *QueueDelete( QUEUE *q ) { if ( q ) { if ( q->Val ) inchi_free(q->Val); inchi_free( q ); } return NULL; } int QueueReinit( QUEUE *q ) { if ( q ) { q->nFirst = 0; q->nLength = 0; /* memset( q->Val, 0, q->nTotLength*sizeof(q->Val[0])); */ /* for debug only */ return q->nTotLength; } return -1; } int QueueLength( QUEUE *q ) { if ( q ) { return q->nLength; } else { return 0; } } int QueueWrittenLength( QUEUE *q ) { if ( q ) { int len = q->nFirst+q->nLength; return (len > q->nTotLength)? q->nTotLength : len; } else { return 0; } } /**********************************************************************************/ /* BFS: Breadth First Search */ int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) { int qLen, i, j; AT_RANK nCurLevel, nRingSize, nMinRingSize=MAX_ATOMS+1; qInt at_no, next; int iat_no, inext; while ( qLen = QueueLength( q ) ) { /* traverse the next level (next outer ring) */ for ( i = 0; i < qLen; i ++ ) { if ( 0 <= QueueGet( q, &at_no ) ) { iat_no = (int)at_no; nCurLevel = nAtomLevel[iat_no] + 1; if ( 2*nCurLevel > nMaxRingSize + 4 ) { /* 2*nCurLevel = nRingSize + 3 + k, k = 0 or 1 */ if ( nMinRingSize < MAX_ATOMS+1 ) { return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; } return 0; /* min. ring size > nMaxRingSize */ } for ( j = 0; j < atom[iat_no].valence; j ++ ) { next = (qInt)atom[iat_no].neighbor[j]; inext = (int)next; if ( !nAtomLevel[inext] ) { /* the at_no neighbor has not been traversed yet. Add it to the queue */ if ( 0 <= QueueAdd( q, &next ) ) { nAtomLevel[inext] = nCurLevel; cSource[inext] = cSource[iat_no]; /* keep the path number */ } else { return -1; /* error */ } } else if ( nAtomLevel[inext]+1 >= nCurLevel && cSource[inext] != cSource[iat_no] /* && cSource[(int)next] != -1 */ ) { /* found a ring closure */ /* debug */ if ( cSource[inext] == -1 ) { return -1; /* error */ } if ( (nRingSize = nAtomLevel[inext] + nCurLevel - 2) < nMinRingSize ) { nMinRingSize = nRingSize; } /* return (nRingSize >= nMaxRingSize)? 0 : nRingSize; */ } } } else { return -1; /* error */ } } } if ( nMinRingSize < MAX_ATOMS+1 ) { return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; } return 0; } /*******************************************************************/ /* Return value: 0: nMaxRingSize < 3 or min. ring size >= nMaxRingSize or not a ring bond (the last is currently impossible: bond is known to belong to a ring system. n>0: min. ring size < nMaxRingSize n<0: error Input: atom[] at_no number of the 1st atom adjacent to the bond neigh_ord ordering number of the bond in question: at[at_no].bond_type[neigh_ord] q queue structure nAtomLevel work array, DFS distance cSource work array, origin mark */ int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) { int nMinRingSize = -1, i; qInt n; int nTotLen; if ( nMaxRingSize < 3 ) { return 0; } QueueReinit( q ); /* mark the starting atom */ nAtomLevel[at_no] = 1; cSource[at_no] = -1; /* add neighbors */ for ( i = 0; i < atom[at_no].valence; i ++ ) { n = (qInt)atom[at_no].neighbor[i]; nAtomLevel[(int)n] = 2; cSource[(int)n] = 1 + (i==neigh_ord); QueueAdd( q, &n ); } nMinRingSize = GetMinRingSize( atom, q, nAtomLevel, cSource, nMaxRingSize ); /* cleanup */ nTotLen = QueueWrittenLength( q ); for ( i = 0; i < nTotLen; i ++ ) { if ( 0 < QueueGetAny( q, &n, i ) ) { nAtomLevel[(int)n] = 0; cSource[(int)n] = 0; } } nAtomLevel[at_no] = 0; cSource[at_no] = 0; /* if ( nAtomLevel ) inchi_free ( nAtomLevel ); if ( cSource ) inchi_free ( cSource ); QueueDelete( q ); */ return nMinRingSize; } /*******************************************************************/ int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ) { AT_NUMB neigh_neigh; int i, j, k, val, val_neigh, neigh; if ( atom[at_no].nNumAtInRingSystem < 3 ) { return 0; } for ( i = 0, val = atom[at_no].valence; i < val; i ++ ) { neigh = (int)atom[at_no].neighbor[i]; if ( atom[at_no].nRingSystem != atom[neigh].nRingSystem ) continue; for ( j = 0, val_neigh = atom[neigh].valence; j < val_neigh; j ++ ) { neigh_neigh = atom[neigh].neighbor[j]; if ( (int)neigh_neigh == at_no ) continue; for ( k = 0; k < val; k ++ ) { if ( atom[at_no].neighbor[k] == neigh_neigh ) { return 1; } } } } return 0; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichiring.h000066400000000000000000000043351271037650300235230ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHIRING_H__ #define __INCHIRING_H__ #define QUEUE_QINT 1 typedef AT_RANK qInt; /* queue optimization: known type */ #if ( QUEUE_QINT == 1 ) #define QINT_TYPE qInt #else #define QINT_TYPE void #endif typedef struct tagQieue { QINT_TYPE *Val; int nTotLength; int nFirst; /* element to remove if nLength > 0 */ int nLength; /* (nFirst + nLength) is next free position */ #if ( QUEUE_QINT != 1 ) int nSize; #endif }QUEUE; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif QUEUE *QueueCreate( int nTotLength, int nSize ); QUEUE *QueueDelete( QUEUE *q ); int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHIRING_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr1.c000066400000000000000000006661451271037650300234660ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*^^^ */ /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" /************************************************************************************************** ChargeStruct fictitios structures MY_CONST CN_LIST cnList[*] ============================================================ bond flow (+) => Positive charge c-group ----------------- (-) => Negative charge c-group Single 0 (+C) => Positive charge group for C, Si, Ge, Sn, Pb Double 1 (-C) => Negative charge group for C, Si, Ge, Sn, Pb Triple 2 (.) => additional one unit of st_cap A) Interpretation: X-(-) or X=(+) => zero charge X=(-) or X-(+) => charge = -1 or +1, respectively B) Information to keep: ordering zero-based number of the edge to (+) or (-) from the Interpretation (A) section vCap = vertex cap vFlow = vertex flow val = number of edges incident to the vertex neigh = 1-based ordering number of the adjacent vertex; 0 => no more adjacent vertices cap = cap of the edge to the adjacent vertex flow = flow of the edge to the adjacent vertex atom (c-point) always has number 1 c-group(s) always are the last vertices always adjacent_neigh_number > vertex_number, that is, neigh > vertex Contribution to the Total Charge: ---------------------------------- edge_cap(+) - edge_flow(+) - edge_flow(-) - Delta(+) - Delta(-) where edge_cap(+) is edge capacity to c-group (+); edge_flow(+) is edge capacity to c-group (?), (?)= (+) or (-); Delta(?) = st_cap(?) - st_floe(?) of the c-group vertex (?), (?)= (+) or (-); ***************************************************************************************************/ /************************************************************************************************** Important: vCap and vFlow Note: since metal charge group (vert. 2-4) MUST be registered before marked with empty the "metal flower" (5-8) all charge group vertex numbers are comments for vertices less than metal flower vertices: (2,3,4) < (5,6,7,8) 1 and 5(M) should This MAY be neded for c-group enumeration. The order is: be set separately t-groups, c-groups, M-flower. All types BNS_VT_M_GROUP allows to avoid duplications. 3(+) || (Metal) || \|/ init charge=0; MAX_METAL_CHARGE = 16 4(-) 5(M) 2 -Fe- CAP(BOND_TO_BNS_VT_M_GROUP) = NUM_BONDS*CAP \ | / | \ | / 1 X(V), V=valence */ MY_CONST C_NODE cnMe[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM,0/**/ ,0/**/,3}, {{ 2, 16,0, 0 },{ 4, 16,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT,16, 16, 2}, {{ 3, 16,0,16 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_C_POS_M, 16, 16, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_C_NEG_M, 0+16, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_M_GROUP, 0/**/ ,0/**/,3}, {{ 1, 3,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; /* #define cn_bits_Me (-1) */ /************************************************************************************************** c=2 5(+.) _____ / (PNPN) 4=====3 |||| init charge=0 c=2 \ / c=1 -N- 2 | |||| 1 X+(V), X(V+1), X+(V+2), X(V+3); V=valence */ MY_CONST C_NODE cnPNPN[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 1,0, 0 },{ 4, 2,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 0 },{ 4, 2,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; /* #define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) */ /************************************************************************************************** 5(+) c=1 // (NPNP) 4=====3 |||| init charge=0 c=1 \ / c=2 -N- 2 | |||| 1 X(V), X+(V+1), X(V+2), X+(V+3); V=valence */ MY_CONST C_NODE cnNPNP[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 2,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; /* #define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) */ /********************* end new ********************************************************************/ /************************************************************************************************** 5(+) // (NPN) 4=====3 ||| init charge=0 \ / -N- 2 | ||| 1 X(V), X+(V+1), X(V+2); V=valence */ MY_CONST C_NODE cnNPN[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; /* #define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) */ /************************************************************************************************** 5(+.) / (PNP) 4=====3 ||| init charge=0 \ / -Cl- 2 /\ ||| 1 X+(V), X(V+1), X+(V+2); V=valence */ MY_CONST C_NODE cnPNP[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; /* #define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) */ /************************************************************************************************** (MNP) \ / init charge=0 N(.) 3(-) 2(+) / \ \ // 1(.) X-(V), X(V+1), X+(V+2); V=valence */ MY_CONST C_NODE cnMNP[3] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 1 },{ 3, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 3 */ }; /* #define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) */ #ifdef NEVER /************************** not used ************************************************************** (PNM) 5(-) 4(+) \\ / \ // B(.) init charge=0 3 2 / \ \\ / 1(.) X+(V), X(V+1), X+(V+2); V=valence */ MY_CONST C_NODE cnPNM[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 0 },{ 3, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ }; #define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) #endif /************************************************************************************************** 4(-) 3(+.) (PNM) \ / ||| init charge=0 2 --P-- ||| / \ 1 X-(V), X(V+1), X+(V+2); V=valence */ MY_CONST C_NODE cnPNM[4] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ }; /* explanaton of vCap: ^ ^ */ /* additional dot:/ \ capacity of the edge to (+) or (-) vertex */ /* #define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) */ /************************************************************************************************** 5(+C) // init charge=0 6(-C) 4 \ / (EN) E=either +1 or -1 3 | || -C(.)- 2 | | 1(.) X-(V), X+(V), X(V+1); V=valence */ MY_CONST C_NODE cnEN[6] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 4, 1,0, 0 },{ 6, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS_C, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ { {BNS_VT_C_NEG_C, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 6 */ }; /* #define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) */ /************************************************************************************************** 5(-) / (NMN) init charge=0 4=====3 ||| \ / -X- 2 /\ ||| 1 X(V), X-(V+1), X(V+2); V=valence */ MY_CONST C_NODE cnNMN[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ }; /* #define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) */ /************************************************************************************************** 4(+) // (NE) E=either +1 or -1 5(-) 3 || \ / -X- init charge=0 2 | || 1 X(V), X+(V+1), X-(V+1); V=valence */ MY_CONST C_NODE cnNE[5] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 3, 1,0, 0 },{ 5, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ }; /* #define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) */ /************************************************************************************************** 6(-) 5(+) \ // (NEN) 4=====3 ||| init charge=0 \ / -X- 2 | ||| 1 X(V), X+(V+1), X-(V+1), X(V+2); V=valence */ MY_CONST C_NODE cnNEN[6] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 6, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 6 */ }; /* #define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) */ /*=======================================================*/ /************************************************************************************************** (NP) || -X- init charge=0 2(+) | || 1 X(V), X+(V+1); V=valence */ MY_CONST C_NODE cnNP[2] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ }; /* #define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) */ /************************************************************************************************** (PN) 3(+.) || init charge=0 [because cap(+)-flow(+)-Delta=1-0-1=0] | -X- 2 | || 1 X+(V), X(V+1); V=valence */ MY_CONST C_NODE cnPN[3] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ }; /* #define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) */ /************************************************************************************************** (NM) 3(-) || init charge=0 | -X- 2 | || 1 X(V), X-(V+1); V=valence */ MY_CONST C_NODE cnNM[3] = { /* vertex type vCap vFlow; val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ }; /* #define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) */ /************************************************************************************************** (MN) | -X- init charge=0 2(-) | | 1(.) X-(V), X(V+1); V=valence */ MY_CONST C_NODE cnMN[2] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ }; /* #define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) */ /************************************************************************************************** (P) | -X- init charge=0 2(+.) | | 1 X+(V); V=valence; all chemical (real) bonds to X have cap=0 */ MY_CONST C_NODE cnP_[2] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 0, 0, 1}, {{ 2, 1,1, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ }; /* #define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) */ #ifdef NEVER /************************************************************************************************** (M) | -X- init charge=-1 on atom 2(-) | | 1(.) X+(V); V=valence; all chemical (real) bonds to X have cap=0 */ MY_CONST C_NODE cnM_[2] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ }; #endif MY_CONST C_NODE cnM_[1] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ }; /* #define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) */ /************************************************************************************************** -X- init charge=0 | 1 X(V); V=valence; */ MY_CONST C_NODE cnN_[1] = { /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ }; /* #define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) */ /**************************************************************************************************/ MY_CONST CN_LIST cnList[] = { {cnPNPN, cn_bits_PNPN, 0, sizeof(cnPNPN)/sizeof(cnPNPN[0])}, /* 0 */ {cnNPNP, cn_bits_NPNP, 0, sizeof(cnNPNP)/sizeof(cnNPNP[0])}, /* 1 */ {cnNPN, cn_bits_NPN, 0, sizeof(cnNPN)/sizeof(cnNPN[0])}, /* 2 */ {cnPNP, cn_bits_PNP, 0, sizeof(cnPNP)/sizeof(cnPNP[0])}, /* 3 */ {cnMNP, cn_bits_MNP, 0, sizeof(cnMNP)/sizeof(cnMNP[0])}, /* 4 */ {cnPNM, cn_bits_PNM, 0, sizeof(cnPNM)/sizeof(cnPNM[0])}, /* 5 */ {cnEN, cn_bits_EN , 0, sizeof(cnEN)/sizeof(cnEN[0])}, /* 6 */ {cnNMN, cn_bits_NMN, 0, sizeof(cnNMN)/sizeof(cnNMN[0])}, /* 7 */ {cnNE, cn_bits_NE , 0, sizeof(cnNE)/sizeof(cnNE[0])}, /* 8 */ {cnNEN, cn_bits_NEN, 0, sizeof(cnNEN)/sizeof(cnNEN[0])}, /* 9 */ {cnNP, cn_bits_NP, 0, sizeof(cnNP)/sizeof(cnNP[0])}, /* 10 */ {cnPN, cn_bits_PN, 0, sizeof(cnPN)/sizeof(cnPN[0])}, /* 11 */ {cnNM, cn_bits_NM, 0, sizeof(cnNM)/sizeof(cnNM[0])}, /* 12 */ {cnMN, cn_bits_MN, 0, sizeof(cnMN)/sizeof(cnMN[0])}, /* 13 */ {cnP_, cn_bits_P_, 0, sizeof(cnP_)/sizeof(cnP_[0])}, /* 14 */ {cnM_, cn_bits_M_, -1, sizeof(cnM_)/sizeof(cnM_[0])}, /* 15 */ {cnN_, cn_bits_N_, 0, sizeof(cnN_)/sizeof(cnN_[0])}, /* 16 */ {cnMe, cn_bits_Me, 0, sizeof(cnMe)/sizeof(cnMe[0])} /* 17 */ }; #define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ int cnListNumEl = (int)(sizeof(cnList)/sizeof(cnList[0])); /**********************************************************************/ void clear_t_group_info( T_GROUP_INFO *ti ) { if ( !ti ) { return; } else { T_GROUP *t_group = ti->t_group; int max_num_t_groups = ti->max_num_t_groups; AT_NUMB *tGroupNumber = ti->tGroupNumber; int num_t_groups = ti->num_t_groups; AT_NUMB *nEndpointAtomNumber = ti->nEndpointAtomNumber; int nNumEndpoints = ti->nNumEndpoints; AT_NUMB *nIsotopicEndpointAtomNumber = ti->nIsotopicEndpointAtomNumber; int nNumIsotopicEndpoints = ti->nNumIsotopicEndpoints; memset( ti, 0, sizeof(*ti) ); if ( t_group ) { memset( t_group, 0, sizeof(t_group[0])*max_num_t_groups ); } else { max_num_t_groups = 0; } if ( tGroupNumber ) { memset( tGroupNumber, 0, sizeof(tGroupNumber[0])*num_t_groups ); } else { num_t_groups = 0; } if ( nEndpointAtomNumber ) { memset( nEndpointAtomNumber, 0, sizeof(nEndpointAtomNumber[0])*nNumEndpoints ); } else { nNumEndpoints = 0; } if ( nIsotopicEndpointAtomNumber ) { memset( nIsotopicEndpointAtomNumber, 0, sizeof(nIsotopicEndpointAtomNumber[0])*nNumIsotopicEndpoints ); } else { nNumIsotopicEndpoints = 0; } ti->t_group = t_group; ti->max_num_t_groups = max_num_t_groups; ti->tGroupNumber = tGroupNumber; ti->num_t_groups = num_t_groups; ti->nEndpointAtomNumber = nEndpointAtomNumber; ti->nNumEndpoints = nNumEndpoints; ti->nIsotopicEndpointAtomNumber = nIsotopicEndpointAtomNumber; ti->nNumIsotopicEndpoints = nNumIsotopicEndpoints; } return; } /******************************************************************************************************/ int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ) { int ret, i, j, k, itg, num_atoms, len_tg, bIso, num_t_groups; AT_NUMB *tGroupNumber = NULL; AT_NUMB *tSymmRank = NULL; AT_NUMB *tiSymmRank = NULL; AT_NUMB *tiGroupNumber = NULL; ret = 0; clear_t_group_info( ti ); if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { num_atoms = pInChI->nNumberOfAtoms; bIso = pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms; num_t_groups = pInChI->nTautomer[0]; len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ /* allocation ti->t_group */ if ( ti->max_num_t_groups != num_atoms/2+1 || !ti->t_group ) { ti->max_num_t_groups = num_atoms/2+1; if ( ti->t_group ) inchi_free( ti->t_group ); ti->t_group = (T_GROUP *)inchi_calloc( ti->max_num_t_groups, sizeof(ti->t_group[0])); } /* allocation ti->tGroupNumber */ if ( ti->num_t_groups != num_t_groups || !ti->tGroupNumber ) { ti->num_t_groups = num_t_groups; if ( ti->tGroupNumber ) inchi_free( ti->tGroupNumber ); ti->tGroupNumber = (AT_NUMB *)inchi_calloc((ti->num_t_groups+1)*TGSO_TOTAL_LEN, sizeof(ti->tGroupNumber[0])); } /* allocation ti->tGroupNumber */ if ( len_tg != ti->nNumEndpoints || !ti->nEndpointAtomNumber ) { ti->nNumEndpoints = len_tg; if ( ti->nEndpointAtomNumber ) inchi_free( ti->nEndpointAtomNumber ); ti->nEndpointAtomNumber = (AT_NUMB *)inchi_calloc(len_tg+1, sizeof(ti->nEndpointAtomNumber[0])); } /* check */ if ( !ti->t_group || !ti->tGroupNumber || !ti->nEndpointAtomNumber ) { ret = RI_ERR_ALLOC; goto exit_function; } tGroupNumber = ti->tGroupNumber; tSymmRank = tGroupNumber + TGSO_SYMM_RANK * ti->num_t_groups; /* equivalence; cannot restore */ tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK * ti->num_t_groups; tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER * ti->num_t_groups; INCHI_HEAPCHK j = 1; /* index in pInChI->nTautomer[] */ i = 0; /* index in ti->nEndpointAtomNumber[] */ for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ ti->t_group[itg].num[0] = pInChI->nTautomer[j+1]+pInChI->nTautomer[j+2]; /* num mobile H & (-) */ ti->t_group[itg].num[1] = pInChI->nTautomer[j+2]; /* num mobile (-) */ tGroupNumber[itg] = tiGroupNumber[itg] = itg; /* index */ ti->t_group[itg].nGroupNumber = /*tSymmRank[itg] = tiSymmRank[itg] =*/ itg+1; /* t-group number */ j += T_GROUP_HDR_LEN; /* skip t-group header */ len_tg -= T_GROUP_HDR_LEN-1; ti->t_group[itg].nNumEndpoints = len_tg; ti->t_group[itg].nFirstEndpointAtNoPos = i; for( ; 0 < len_tg --; j ++, i ++ ) { k = ti->nEndpointAtomNumber[i] = pInChI->nTautomer[j]-1; if ( at ) { at[k].endpoint = itg+1; } if ( endpoint ) { endpoint[k] = itg+1; } } } if ( i != ti->nNumEndpoints ) { ret = RI_ERR_PROGR; } INCHI_HEAPCHK } exit_function: return ret; } /******************************************************************************************************/ int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ) { int num_at = pInChI->nNumberOfAtoms; AT_NUMB *endpoint = *pEndpoint; int itg, i, j, k, len_tg; if ( !endpoint && !(endpoint = (AT_NUMB*) inchi_malloc(num_at * sizeof(endpoint[0]) ) ) ) { return RI_ERR_ALLOC; } memset( endpoint, 0, num_at * sizeof(endpoint[0]) ); if ( pInChI->lenTautomer <= 1 || !pInChI->nTautomer ) { goto exit_function; } j = 1; /* index in pInChI->nTautomer[] */ i = 0; /* index in ti->nEndpointAtomNumber[] */ for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ j += T_GROUP_HDR_LEN; /* skip t-group header */ len_tg -= T_GROUP_HDR_LEN-1; /* ti->t_group[itg].nNumEndpoints = len_tg; */ for( ; 0 < len_tg --; j ++, i ++ ) { k = pInChI->nTautomer[j]-1; endpoint[k] = itg+1; } } exit_function: *pEndpoint = endpoint; return 0; } /************************************************************************************/ int cmp_charge_val( const void *a1, const void *a2 ) { const CHARGE_VAL *p1 = (const CHARGE_VAL *) a1; const CHARGE_VAL *p2 = (const CHARGE_VAL *) a2; int diff; if ( diff = (int)p1->nValence - (int)p2->nValence ) /* smaller valence first */ return diff; if ( diff = abs((int)p1->nCharge) - abs((int)p2->nCharge )) /* smaller abs charge first */ return diff; if ( diff = (int)p2->nCharge - (int)p1->nCharge ) /* (+) first, (-) second */ return diff; return (int)p1->nValenceOrderingNumber - (int)p2->nValenceOrderingNumber; } /************************************************************************************/ int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ) { static const char szEl[] = "N;P;O;S;Se;Te;"; static const char cVal[] = {4,4,3,3, 3, 3, 0}; static char en[8]; static int ne; int i, j, neigh; char *p; if ( !bMobileH || !at[iat].num_H ) { return 1; } if ( !ne ) { /* one time initialization */ const char *b, *e; int len; char elname[ATOM_EL_LEN]; for ( b = szEl; e = strchr( b, ';'); b = e+1 ) { len = e-b; memcpy( elname, b, len ); elname[len] = '\0'; en[ne++] = get_periodic_table_number( elname ); } en[ne] = '\0'; } if ( p = (char *)memchr( en, at[iat].el_number, ne ) ) { i = p - en; /* >B(-)< exception */ if ( at[iat].valence + at[iat].num_H <= cVal[i] ) { for ( j = 0; j < at[iat].valence; j ++ ) { neigh = at[iat].neighbor[j]; if ( at[neigh].valence == 4 && at[neigh].chem_bonds_valence == 4 && !at[neigh].num_H && pVA[neigh].cNumValenceElectrons == 3 && pVA[neigh].cPeriodicRowNumber == 1 ) { return 1; } } return 0; } } return 1; } /************************************************************************************/ int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ) { inp_ATOM *at = atom + iat; int nPeriodicNum = at->el_number; int num_bonds = at->valence; int min_valence = at->valence + at->num_H; /* in fixed-H case treat tautomeric -O as tautomeric to avoid #O(+) */ int bTautomeric = (at->endpoint != 0); int bFixedHTautomeric = !bMobileH && (endpoint && endpoint[iat] && pVA[iat].cNumValenceElectrons == 6 && 1==num_bonds && !at->num_H && !bIsMetal); /* int bIsMetal = is_el_a_metal( nPeriodicNum );*/ int bDoNotAddH = do_not_add_H( nPeriodicNum ); int nPeriod, nNumEqAbsCharges; int nNumValenceEl = get_sp_element_type( nPeriodicNum, &nPeriod ) - 1; int i, j; if ( !len ) return len; insertions_sort( pChargeVal, len, sizeof(pChargeVal[0]), cmp_charge_val ); /* metals -- very preliminary code */ if ( bIsMetal && bDoNotAddH ) { /* keep the 1st found */ return inchi_min( 1, len ); } /* Mobile-H layer cannot have H on positively charged N, P (all IV), O, S, Se, Te (all III) */ /* if ( abs( pChargeVal[0].nCharge ) > 1 && pChargeVal[0].nValence >= min_valence ) { return inchi_min( 1, len ); } */ nNumEqAbsCharges = 0; for ( i = j = 0; i < len && j < (nNumEqAbsCharges? 3+nNumEqAbsCharges:4); i ++ ) { /* for now accept only charge = 0, -1, +1 */ if ( abs( pChargeVal[i].nCharge ) > 1 ) { continue; } if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (min_valence - 1) < pChargeVal[i].nValence ) { continue; /* not more than one triple and the rest - double bonds per atom */ } if ( (bTautomeric || j && bFixedHTautomeric) && pChargeVal[i].nCharge < 0 ) { continue; /* negative charge must be included in the tautomeric group */ } if ( (bTautomeric || bFixedHTautomeric) && pChargeVal[i].nCharge > 0 ) { continue; /* positive charge for now cannot reach a tautomeric group */ } if ( j && !bMayBeACationInMobileHLayer( atom, pVA, iat, bMobileH ) && pChargeVal[i].nCharge > 0 ) { if ( i+1 < len && pChargeVal[i].nValence == pChargeVal[i+1].nValence && pChargeVal[i].nCharge == -pChargeVal[i+1].nCharge ) { /* (-) if exists is always after (+) */ i += 1; /* also skip the next element */ } continue; /* in case of Mobile-H, a hydrogen cannot be on a (+)-charged heteroatom */ } /* accept same valence opposite charges only for C and its group in Periodic Table */ if ( j && !bTautomeric && pChargeVal[i].nValence == pChargeVal[j-1].nValence && pChargeVal[i].nCharge == -pChargeVal[j-1].nCharge ) { if ( nNumValenceEl == VALUE_OCTET/2 && pChargeVal[i].nCharge && !nNumEqAbsCharges ) { pChargeVal[j ++] = pChargeVal[i]; nNumEqAbsCharges ++; } continue; } /* do not accept valence=5 for neutral NHn in case of not Mobile-H 2005-01-26 ???? */ if ( nNumValenceEl == 5 && nPeriod == 1 && at->num_H && j && !bMobileH && pChargeVal[i].nValence == 5 && !pChargeVal[i].nCharge ) { continue; } /* do not accept gaps in allowed valences */ if ( j && pChargeVal[i].nValence > pChargeVal[j-1].nValence+1 ) { break; } pChargeVal[j ++] = pChargeVal[i]; } len = j; if ( !nNumEqAbsCharges && num_bonds < 3 && len == 4 ) { len --; /* prohibit =S# where # is a triple bond */ } return len; } /************************************************************************************ int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray ) pVA->cDoNotAddH pVA->cMetal pVA->cNumValenceElectrons pVA->cPeriodicRowNumber pVA->cInitFreeValences pVA->cnListIndex = index+1 return value: -1 => error 0 => do not know what to do; leave the atom unchanged 1 => success *************************************************************************************/ int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ) { /* #defines from util.c */ #define MIN_ATOM_CHARGE (-2) #define MAX_ATOM_CHARGE 2 #define NEUTRAL_STATE (-MIN_ATOM_CHARGE) #define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) #define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ int i, j, j2, k, k2, charge, cur_charge, num_non_bonding_electrons; int nNumStates, nNumSelectedStates, num_H, num_bonds; int nOctetNeutralValenceExcess, nFirstNeutralValenceExcess; int nFoundNeutralValenceExcess, nFoundNeutralValenceOrdNumber; int nLastFoundValenceOrdNumber, nLastFoundValenceState; int cn_bits, cn_bits_array[5], len_cn_bits_array; inp_ATOM *at = atom+iat; VAL_AT *pVA = pVArray + iat; int nPeriodicNum = at->el_number; int cur_chem_valence, cur_chem_valence_fixed, min_chem_valence, known_chem_valence; int metal_bonds_chem_valence, not_metal_bonds_chem_valence, alt_bonds_delta_valence, bonds_chem_valence, bond_type; CHARGE_VAL ChargeVal[NUM_ATOM_CHARGES*MAX_NUM_VALENCES]; memset( ChargeVal, 0, sizeof(ChargeVal) ); pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); /* InChI never adds H to this atom */ /*pVA->cMetal = is_el_a_metal( nPeriodicNum );*/ /* the atom is a metal */ /* count bonds to metal atoms; metals have already been marked */ metal_bonds_chem_valence = not_metal_bonds_chem_valence = alt_bonds_delta_valence = 0; if ( pVA->cMetal ) { j = at->valence; /* all bonds to metal */ for ( i = k = j2 = k2 = 0; i < at->valence; i ++ ) { bond_type = (at->bond_type[i] & BOND_TYPE_MASK); if ( bond_type <= BOND_TYPE_TRIPLE ) { metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); } else { metal_bonds_chem_valence += BOND_TYPE_SINGLE; k ++; /* count alternating bonds */ } } } else { for ( i = j = j2 = k = k2 = 0; i < at->valence; i ++ ) { bond_type = (at->bond_type[i] & BOND_TYPE_MASK); if ( pVArray[ (int)at->neighbor[i] ].cMetal ) { j ++; /* number of bonds to metal atoms */ if ( bond_type <= BOND_TYPE_TRIPLE ) { metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); } else { metal_bonds_chem_valence += BOND_TYPE_SINGLE; k ++; /* count alternating bonds */ } } else { j2 ++; if ( bond_type <= BOND_TYPE_TRIPLE ) { not_metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); } else { not_metal_bonds_chem_valence += BOND_TYPE_SINGLE; k2 ++; /* count alternating bonds */ } } } } bonds_chem_valence = metal_bonds_chem_valence + not_metal_bonds_chem_valence; if ( at->chem_bonds_valence > bonds_chem_valence ) { if ( at->chem_bonds_valence - bonds_chem_valence > 1 ) { at->chem_bonds_valence = bonds_chem_valence + 1; /* should not happen */ } alt_bonds_delta_valence = at->chem_bonds_valence - bonds_chem_valence; } pVA->cNumBondsToMetal = j; if ( nPeriodicNum == EL_NUMBER_H ) { /* ignore bridging H; ??? later add ??? */ return 0; } num_H = at->num_H; num_bonds = at->valence; if ( !num_bonds && !num_H ) { return 0; /* do not know the answer: isolated atom */ } /* at the beginning all bonds are single */ min_chem_valence = num_bonds + num_H; cur_chem_valence = bonds_chem_valence + alt_bonds_delta_valence + num_H; /* includes double & alternating bond contribution */ /* number of non-bonding electrons in case of all single bonds */ num_non_bonding_electrons = (int)pVA->cNumValenceElectrons - min_chem_valence; /* Octet rule: charge = bonds_valence + NumValenceElectrons - 8 */ charge = min_chem_valence + (int)pVA->cNumValenceElectrons - VALUE_OCTET; /* wrong */ /* typical (ad hoc) minimal neutral valence */ known_chem_valence = ( pVA->cNumValenceElectrons > VALUE_OCTET/2 )? VALUE_OCTET - pVA->cNumValenceElectrons : pVA->cNumValenceElectrons; /* excess of typical valence over all-single-bonds valence */ nOctetNeutralValenceExcess = known_chem_valence - min_chem_valence; /* (NB=num.bonds, NV=neutral valence, NVX=neutral valence excess, LFVS=last found valence state, val.=valence) element NB knownFst octet Last octetNVX firstNVX foundNVX chargeLFVS LFVS valence val. NV>= -B 1 3 3 3 2 2 = 2 +2 >B 2 3 3 3 1 1 = 1 +1 >B- 3 3 3 3 0 0 = 0 0 >B< 4 3 3 3 -1 -1 <> N/A -1 -C 1 4 4 4 3 3 = 3 N/A >C 2 4 4 4 2 2 = 2 +2 (-2) >C- 3 4 4 4 1 1 = 1 +1 (-1) >C< 4 4 4 4 0 0 = 0 0 C(V) 5 4 4 N/A -1 -1 <> N/A N/A -Si 1 4 4 4 3 3 = 3 N/A >Si 2 4 4 4 2 2 = 2 +2 (-2) >Si- 3 4 4 4 1 1 = 1 +1 (-1) >Si- 4 4 4 4 0 0 = 0 0 Si(V) 5 4 4 N/A -1 -1 <> N/A -1 -N 1 3 3 3 2 2 = 2 -2 >N 2 3 3 3 1 1 = 1 -1 >N- 3 3 3 3 0 0 = 0 0 (+2) >N< 4 3 3 5 -1 -1 <> 1 +1 N(V) 5 3 3 5 -2 -2 <> 0 0 N(VI) 6 3 3 N/A -3 -3 <> N/A N/A N(VII) 7 3 3 N/A -4 -4 <> N/A N/A -P 1 3 3 3 2 2 = 2 -2 >P 2 3 3 3 1 1 = 1 -1 >P- 3 3 3 3 0 0 = 0 0 (-2, +2) >P< 4 3 3 5 -1 -1 <> 1 +1 (-1) P(V) 5 3 3 5 -2 -2 <> 0 0 (-2) P(VI) 6 3 3 N/A -3 -3 <> N/A -1 P(VII) 7 3 3 N/A -4 -4 <> N/A -2 P(VIII) 8 3 3 N/A -5 -5 <> N/A N/A -O 1 2 2 2 1 1 = 1 -1 >O 2 2 2 2 0 0 = 0 0 >O- 3 2 2 N/A -1 -1 <> N/A +1 >O< 4 2 2 N/A -2 -2 <> N/A +2 O(V) 5 2 2 N/A -3 -3 <> N/A +1 O(VI) 6 2 2 N/A -4 -4 <> N/A N/A -S 1 2 2 2 1 1 = 1 -1 >S 2 2 2 2 0 0 = 0 0 NPNP - prohibit >S- 3 2 2 4 -1 -1 <> 1 +1 (-1) PNPN >S< 4 2 2 4 -2 -2 <> 0 0 (+2) S(V) 5 2 2 6 -3 -3 <> 1 +1 (-1) S(VI) 6 2 2 6 -4 -4 <> 0 0 S(VII) 7 2 2 N/A -5 -5 <> 0 -1 S(VIII) 8 2 2 N/A -6 -6 <> N/A N/A -F 1 1 1 1 0 0 = 0 0 >F 2 1 1 1 -1 -1 <> N/A +1 >F- 3 1 1 1 -2 -2 <> N/A +2 >F< 4 1 1 1 -3 -3 <> N/A N/A F(V) 5 1 1 1 -4 -4 <> N/A +2 F(VI) 6 1 1 1 -5 -5 <> N/A N/A -Cl 1 1 1 1 0 0 = 0 0 NPNP - prohibit >Cl 2 1 1 3 -1 -1 <> 1 +1 PNPN - prohibit >Cl- 3 1 1 3 -2 -2 <> 0 0 (+2) NPNP >Cl< 4 1 1 5 -3 -3 <> 1 +1 PNPN Cl(V) 5 1 1 5 -4 -4 <> 0 0 Cl(VI) 6 1 1 7 -5 -5 <> 1 +1 Cl(VII) 7 1 1 7 -6 -6 <> 0 0 Cl(VIII) 8 1 1 N/A -7 -7 <> N/A N/A NB = num_bonds+num_H knownFst valence = nFirstNeutralValenceExcess + min_chem_valence octet val. = nOctetNeutralValenceExcess + min_chem_valence Last NV>= = nFoundNeutralValenceExcess + min_chem_valence octetNVX = nOctetNeutralValenceExcess firstNVX = nFirstNeutralValenceExcess foundNVX = nFoundNeutralValenceExcess chargeLFVS = ChargeVal[nLastFoundValenceState].nCharge */ /* minimal known neutral atom valence; different for Sn(2/4), Tl(1/3), Pb(2/4): (known/typical ad hoc) */ known_chem_valence = get_el_valence( nPeriodicNum, 0, 0 ); if ( pSrm->bMetalAddFlower ) { /* bond orders of bonds to metal may be as they are (pSrm->nMetalInitBondOrder==1) or decreased by one (pSrm->nMetalInitBondOrder==0) nMetalInitBondOrder == nMetalMinBondOrder + nMetalInitEdgeFlow */ cur_chem_valence_fixed = cur_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; pVA->cInitValenceToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); pVA->cInitFlowToMetal = pVA->cInitValenceToMetal - pVA->cNumBondsToMetal * pSrm->nMetalMinBondOrder; if ( pVA->cMetal ) { pVA->cInitFreeValences += alt_bonds_delta_valence; } if ( pSrm->nMetalInitEdgeFlow < pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder ) { /* single bond has zero initial flow + 2 radicals at incident atoms */ if ( pVA->cInitFlowToMetal <= pVA->cNumBondsToMetal ) { if ( pVA->cMetal ) { pVA->cInitFreeValences += pVA->cInitFlowToMetal; } pVA->cInitFlowToMetal = 0; } else { if ( pVA->cMetal ) { pVA->cInitFreeValences += pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); } pVA->cInitFlowToMetal -= pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); } } } else { /* treat metal atoms as ordinary non-metal atoms */ cur_chem_valence_fixed = cur_chem_valence; pVA->cInitFlowToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal; pVA->cInitValenceToMetal = metal_bonds_chem_valence; pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; } if ( pVA->cMetal && pSrm->bMetalAddFlower ) { pVA->cnListIndex = cnListIndexMe + 1; /* pVA->cInitOrigValenceToMetal += alt_bonds_delta_valence; pVA->cInitValenceToMetal += alt_bonds_delta_valence; pVA->cInitFreeValences = (pSrm->nMetalInitBondOrder + alt_bonds_delta_valence - (pSrm->nMetalMinBondOrder + pSrm->nMetalInitEdgeFlow)) * pVA->cNumBondsToMetal; */ return 0; /* metal */ } if ( !known_chem_valence ) { /* a noble gas like He, Ne, ... */ pVA->cInitFreeValences = at->chem_bonds_valence - at->valence; return TREAT_ATOM_AS_METAL; /* do not know anything about this atom; needs 2nd pass */ } nFirstNeutralValenceExcess = known_chem_valence - min_chem_valence; nFoundNeutralValenceExcess = NO_VALUE_INT; nFoundNeutralValenceOrdNumber = NO_VALUE_INT; nLastFoundValenceOrdNumber = NO_VALUE_INT; nLastFoundValenceState = NO_VALUE_INT; /* find the lowest known valence >= all-single-bonds valence */ for ( cur_charge = MIN_ATOM_CHARGE, nNumStates = 0; cur_charge <= MAX_ATOM_CHARGE; cur_charge ++ ) { for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { known_chem_valence = get_el_valence( nPeriodicNum, cur_charge, i ); if ( cur_chem_valence_fixed > known_chem_valence || !known_chem_valence ) { continue; /* known valence < all-single-bonds valence */ } if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H < known_chem_valence ) { continue; /* not more than one triple and the rest - double bonds per atom */ } /* keep all found */ ChargeVal[nNumStates].nValence = known_chem_valence; ChargeVal[nNumStates].nCharge = cur_charge; ChargeVal[nNumStates].nValenceOrderingNumber = i; if ( !cur_charge && nFoundNeutralValenceExcess == NO_VALUE_INT ) { /* neutral state; compare to the lowest typical valence */ nFoundNeutralValenceExcess = known_chem_valence - min_chem_valence; nFoundNeutralValenceOrdNumber = i; } if ( min_chem_valence == known_chem_valence ) { if ( nLastFoundValenceState == NO_VALUE_INT ) { /* accept the first found */ nLastFoundValenceState = nNumStates; } else if ( abs( ChargeVal[nLastFoundValenceState].nCharge ) >= abs( cur_charge ) ) { /* accept smaller abs(charge); if abs(charges) are same, accept (+) */ nLastFoundValenceState = nNumStates; } } nNumStates ++; } } /***********************************************************************************/ /* select only appropriate charge & valence so that a suitable ChargeStruct exists */ /***********************************************************************************/ nNumSelectedStates = clean_charge_val( ChargeVal, nNumStates, atom, pVArray, iat, pVA->cMetal, bMobileH, endpoint ); if ( !nNumSelectedStates ) { return TREAT_ATOM_AS_METAL; /* nothing to do */ } /***********************************************************************************/ /* Find an appropriate ChargeStruct index for the ChargeVal found */ /***********************************************************************************/ cn_bits = 0; memset( cn_bits_array, 0, sizeof(cn_bits_array) ); /***** set bits identifying a suitable ChargeStruct ******/ for ( i = len_cn_bits_array = 0; i < nNumSelectedStates && len_cn_bits_array < 4; i ++ ) { switch( ChargeVal[i].nCharge ) { case -1: cn_bits_array[len_cn_bits_array] |= cn_bits_M; /* Minus 1 */ break; case 0: cn_bits_array[len_cn_bits_array] |= cn_bits_N; /* Neutral */ break; case 1: cn_bits_array[len_cn_bits_array] |= cn_bits_P; /* Plus 1 */ break; default: return RI_ERR_PROGR; /* program error */ } if ( i+1 < nNumSelectedStates && ChargeVal[i].nValence == ChargeVal[i+1].nValence && ChargeVal[i].nCharge && ChargeVal[i].nCharge == -ChargeVal[i+1].nCharge ) { ; /* add opposite charge to the same element of cn_bits_array[] */ } else { len_cn_bits_array ++; } } if ( !len_cn_bits_array || len_cn_bits_array > 4 ) { return RI_ERR_PROGR; /* program error */ } /* accommodate added 4-state ChargeStruct: +/- cannot be in case of 4 states */ if ( len_cn_bits_array + 1 == nNumSelectedStates && nNumSelectedStates == 4 ) { len_cn_bits_array --; nNumSelectedStates --; cn_bits_array[len_cn_bits_array] = 0; } /* fix for terminal hydrogenless -C as in isocyano or CO: there is no just cnE_[] ChargeStruct */ if ( len_cn_bits_array == 1 && cn_bits_array[0] == (cn_bits_P | cn_bits_M) && ChargeVal[0].nValence + 1 > BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H ) { cn_bits_array[len_cn_bits_array ++] = cn_bits_N; ChargeVal[nNumSelectedStates].nValence = ChargeVal[nNumSelectedStates-1].nValence; ChargeVal[nNumSelectedStates].nCharge = 0; ChargeVal[nNumSelectedStates].nValenceOrderingNumber = 0; } make_cn_bits: cn_bits = MAKE_CN_BITS(cn_bits_array[0], cn_bits_array[1], cn_bits_array[2], cn_bits_array[3]); /*********** find ChargeStructure **************/ for ( i = 0, j = -1; i < cnListNumEl; i ++ ) { if ( cnList[i].bits == cn_bits ) { j = i; break; /* found */ } } if ( j < 0 ) { /* ChargeStructure was not found */ if ( 1 < len_cn_bits_array && len_cn_bits_array + 1 == nNumSelectedStates ) { /* a pair of opposite charges was combined */ len_cn_bits_array --; cn_bits_array[len_cn_bits_array] = 0; goto make_cn_bits; } else if ( nNumSelectedStates == 4 ) { /* reduce number of states */ len_cn_bits_array --; cn_bits_array[len_cn_bits_array] = 0; nNumSelectedStates --; goto make_cn_bits; } return RI_ERR_PROGR; /* charge structure not found */ } /********** ChargeStructure has been found **********/ pVA->cnListIndex = j+1; /* charge structure index + 1 */ pVA->cInitCharge = cnList[j].nInitialCharge; /********** Calculate "Free Valence" ****************/ #if ( ALLOW_METAL_BOND_ZERO == 1 ) #if ( INIT_METAL_BOND_ZERO == 1 ) if ( pVA->cMetal ) { j = 0; } else { j = ChargeVal[0].nValence - cur_chem_valence_fixed; } #else j = ChargeVal[0].nValence - cur_chem_valence_fixed; #endif #else j = ChargeVal[0].nValence - cur_chem_valence_fixed; #endif if ( j < 0 ) { return RI_ERR_PROGR; /* program error */ } pVA->cInitFreeValences = j; /* number of initial unsatisfied valences; should be combined with */ /* (cap - flow) of vertex=0 in the charge structure[pVA->cnListIndex-1] */ return 1; /* success */ #undef MIN_ATOM_CHARGE #undef MAX_ATOM_CHARGE #undef NEUTRAL_STATE #undef NUM_ATOM_CHARGES #undef MAX_NUM_VALENCES } #ifdef NEVER /******************************************************************************************************/ int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ) { int i, j, charge, chem_valence, known_chem_valence; #define MAX_NUM_VALENCES 5 /* defined in util.c */ memset( pVA, 0, sizeof( pVA[0] ) ); if ( !bonds_valence && !num_H ) return 0; /* do not know the answer */ chem_valence = bonds_valence + num_H; for ( charge = VAL_MIN_CHARGE; charge <= VAL_MAX_CHARGE; charge ++ ) { for ( i = 0, j = 0; i < MAX_NUM_VALENCES, j < VAL_NUMBER; i ++ ) { if ( chem_valence <= (known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) ) ) { if ( !charge ) { pVA->cValence[j][VAL_NEUTR_ORDER] = i+1; } pVA->cValence[j++][charge+VAL_BASE] = known_chem_valence - num_H; } } } pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); pVA->cMetal = is_el_a_metal( nPeriodicNum ); return pVA->cValence[0][VAL_BASE]; /* 0 means do not know the answer */ #undef MAX_NUM_VALENCES } #endif /*********** calculate s or p-element type ************/ int get_sp_element_type( int nPeriodicNumber, int *nRow ) /* num el el neg 1 => H ATYPE_H 1 1 21 2 => Li, Na, K, Rb, Cs, Fr ATYPE_Na 2 1 10 09 08 08 07 3 => Be, Mg, Ca, Sr, Ba, Ra ATYPE_Mg 3 2 15 12 10 10 09 4 => B, Al, Ga, In, Tl ATYPE_B 4 3 20 15 18 17 18 5 => C, Si, Ge, Sn, Pb ATYPE_C 5 4 25 18 18 18 18 6 => N, P, As, Sb, Bi ATYPE_N 6 5 30 21 20 19 19 7 => O, S, Se, Te, Po ATYPE_O 7 6 35 25 24 21 20 8 => F, Cl, Br, I, At ATYPE_Cl 8 7 40 30 28 25 22 number of valence electrons = (type>1)? type-1: type */ { int row = 0, type = 0; if ( nPeriodicNumber == 1 ) { type = 1; /* H: 1 */ row = 0; } else if ( nPeriodicNumber == 2 ) { type = 0; row = 0; } else if ( nPeriodicNumber <= 10 ) { /* Li: 2, Be: 3, B: 4, C: 5, N: 6, O: 7, F: 8, Ne: 9; later subtract 1 */ type = nPeriodicNumber-1; row = 1; } else if ( nPeriodicNumber <= 18 ) { type = nPeriodicNumber - 9; row = 2; } else if ( nPeriodicNumber <= 20 ) { type = nPeriodicNumber - 17; row = 3; } else if ( nPeriodicNumber <= 30 ) { type = 0; row = 3; } else if ( nPeriodicNumber <= 36 ) { type = nPeriodicNumber - 27; row = 3; } else if ( nPeriodicNumber <= 38 ) { type = nPeriodicNumber - 35; row = 4; } else if ( nPeriodicNumber <= 48 ) { type = 0; row = 4; } else if ( nPeriodicNumber <= 54 ) { type = nPeriodicNumber - 45; row = 4; } else if ( nPeriodicNumber <= 56 ) { type = nPeriodicNumber - 53; row = 5; } else if ( nPeriodicNumber <= 80 ) { type = 0; row = 5; } else if ( nPeriodicNumber <= 86 ) { type = nPeriodicNumber - 77; row = 5; } else if ( nPeriodicNumber <= 88 ) { type = nPeriodicNumber - 85; row = 6; } else { type = 0; row = 6; } *nRow = row; return type==9? 0 : type; } /******************************************************************************************************/ int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ) { TC_GROUP *pTCGroup = (TC_GROUP *) inchi_malloc( sizeof(pTCGroup[0])*(pTCGroups->max_tc_groups + nAdd) ); if ( pTCGroup ) { if ( pTCGroups->num_tc_groups ) { memcpy( pTCGroup, pTCGroups->pTCG, sizeof(pTCGroup[0])*pTCGroups->num_tc_groups ); } memset( pTCGroup + pTCGroups->max_tc_groups, 0, sizeof(pTCGroup[0])*nAdd ); if ( pTCGroups->pTCG ) { inchi_free( pTCGroups->pTCG ); } pTCGroups->pTCG = pTCGroup; pTCGroups->max_tc_groups += nAdd; return 0; } return RI_ERR_ALLOC; } /******************************************************************************************************/ int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges) { int i, ret = 0; /* search */ for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { if ( pTCGroups->pTCG[i].type == nGroupType && pTCGroups->pTCG[i].ord_num == nGroupOrdNum ) { break; } } if ( i == pTCGroups->num_tc_groups ) { /* add one more group */ if ( pTCGroups->num_tc_groups == pTCGroups->max_tc_groups ) { ret = ReallocTCGroups( pTCGroups, INC_NUM_TCGROUPS ); if ( ret ) { goto exit_function; } } ret = i+1; /* added new group */ pTCGroups->num_tc_groups ++; pTCGroups->pTCG[i].type = nGroupType; pTCGroups->pTCG[i].ord_num = nGroupOrdNum; } pTCGroups->pTCG[i].num_edges += nNumEdges; pTCGroups->pTCG[i].st_cap += nVertexCap; pTCGroups->pTCG[i].st_flow += nVertexFlow; pTCGroups->pTCG[i].edges_cap += nEdgeCap; pTCGroups->pTCG[i].edges_flow += nEdgeFlow; exit_function: return ret; } /******************************************************************************************************/ int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ) { /* There are 3 sources of cap-flow = number of unsatisfied valences: ----------------------------------------------------------------- 1. pVA[i].cInitFreeValences 2. pCN[0].v.cap - pCN[0].v.flow 3. st[i].chem_bonds_valence - SUM(SINGLE, DOUBLE, TRIPLE bond orders) Reasons: (a) This sum will not include 'ALTERN' bonds (b) until now at[i].chem_bonds_valence was used as a number of satisfied valences. In case of adjacent stereobonds marked as BOND_TYPE_ALTERN the value of at[i].chem_bonds_valence may be = at[i].valence+1. 4. Since tautomerism is defined for a neutral atom, do not add initial flows from the atom to the ChargeStruct CORRECTION: tautomeric endpoints do not have ChargeStruct. */ int j, k, nEdgeCap, bonds_valence, stereo_bond_excess_valence; MY_CONST C_NODE *pCN = pVA[i].cnListIndex>0? cnList[pVA[i].cnListIndex-1].pCN:NULL; /* 1: free valences to reach the minimum known atom valence */ nEdgeCap = pVA[i].cInitFreeValences; /* 2: atom free valence in the ChargeStruct */ if ( pCN ) { nEdgeCap += pCN[0].v.cap - pCN[0].v.flow; /* normally should not happen */ } /* 3: atom free valence due to known from stereochemistry stereogenic bond types */ /* for ( j = 0, bonds_valence = 0; j < at[i].valence; j ++ ) { if ( at[i].bond_type[j] <= BOND_TYPE_TRIPLE ) { bonds_valence += at[i].bond_type[j]; } } */ /* bonds > SINGLE are assumed fixed stereobonds; fixed bond cannot increase t-group edge flow */ for ( stereo_bond_excess_valence=0, j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { k = at[i].sb_ord[j]; if ( at[i].bond_type[k] < BOND_TYPE_TRIPLE ) { stereo_bond_excess_valence += at[i].bond_type[k] - BOND_TYPE_SINGLE; } } /* bonds_valence = (at[i].chem_bonds_valence - bonds_valence) + (bonds_valence -at[i].valence - stereo_bond_excess_valence); */ bonds_valence = (at[i].chem_bonds_valence - at[i].valence) - stereo_bond_excess_valence; /*---- add 1, 2, 3 ----*/ if ( bonds_valence >= 0 ) { nEdgeCap += bonds_valence; } else { nEdgeCap = RI_ERR_PROGR; } return nEdgeCap; } /******************************************************************************************************/ /* If Metal flowers are allowed ( pSrm->bMetalAddFlower != 0), then: */ /* */ /* bond to a metal atom min_bond_order[i] = pSrm->nMetalMinBondOrder */ /* taut endpoint - metal min_bond_order[i] = pSrm->nMetal2EndpointMinBondOrder */ /* single bond to metal atom: initial_bond_order[i] = pSrm->nMetalInitBondOrder */ /* n-order bond to metal atom initial_bond_order[i] = pSrm->nMetalInitBondOrder + n-1 */ /* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetalInitBondOrder */ /* single t-endpoint--atom bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder */ /* n-order t-endpoint--metal bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder+n-1 */ /* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetal2EndpointInitBondOrder*/ /* */ /* Exceptions from simple atom-metal conditions: */ /* 1. Atom is a tautomeric endpoint: use pSrm->nMetal2Endpoint* instead of pSrm->nMetal* */ /* 2. Atom is sp3-stereogenic and pSrm->bFixStereoBonds != 0: use atom-atom rules */ /* 3. Atom has a sp2-stereo and pSrm->bFixStereoBonds != 0: use atom-atom rules */ /* */ /* Atom-atom rules (applies to all atoms if pSrm->bMetalAddFlower=0) */ /* */ /* min_bond_order[i] = BOND_TYPE_SINGLE (BOND_TYPE_SINGLE = 1) */ /* initial_bond_order[i] = bond_type[i] */ /* */ /* General rules: */ /* initial_bond_flow[i] = initial_bond_order[i]-min_bond_order[i] */ /* atom[k] initial_st_cap = at[k].chem_bonds_valence - SUM{i; initial_bond_order[i]} */ /* bond_cap[i] = BOND_TYPE_TRIPLE - min_bond_order[i] */ /* (reason: quadruple and higher order bonds are not allowed) */ /* Exception: in case of metal-atom bond, if pSrm->nMetal2EndpointInitEdgeFlow = 0 AND */ /* pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder = 1 then */ /* reduce bond to metal order by 1 and increase st_cap of both neighbors by 1: */ /* initial_bond_flow[i] --; metal_initial_st_cap += num_bonds; */ /* ==== Note: ONLY the INCREASE is already included in pVA->cInitFreeValences of both atoms */ /* */ /* Notes: initial_st_cap does not include: */ /* 1. atom[k] additional st_cap from ChargeStruct pCN[0].v.cap */ /* 2. pVA[k].cInitFreeValences due to a difference between the smallest known valence and st_cap */ /* */ /* here k=atom at[k] index, */ /* i=bond index; i = 0..at[k].valence; */ /* SUM{i; M[i]} is a sum of M[i] over all i */ /* bond_order[i] = at[k].bond_type[i] >= BOND_TYPE_SINGLE - input bond order */ /******************************************************************************************************/ /***************** new *************************************************************************************/ int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ) { int nFlow, nMaxcap, nMinorder, nInitorder, bNeedsFlower = 0; inp_ATOM *at = atom + iat; int neigh = at->neighbor[ineigh]; int bond_type = at->bond_type[ineigh] & BOND_TYPE_MASK; int nMetal = (0 != pVA[iat].cMetal) + (0 != pVA[neigh].cMetal); int nEndpoint = (0 != at->endpoint) + (0 != atom[neigh].endpoint); int nStereo = (at->p_parity || at->sb_parity[0]) + (atom[neigh].p_parity || atom[neigh].sb_parity[0]); if ( bond_type > BOND_TYPE_TRIPLE ) { bond_type = BOND_TYPE_SINGLE; } /* M=metal, A=non-metal atom, e=endpoint */ if ( nStereo && pSrm->bFixStereoBonds || !nMetal || !pSrm->bMetalAddFlower ) { /* atom-atom rules, no metal atoms involved (1: A-A, A-Ae, Ae-Ae) */ nMinorder = BOND_TYPE_SINGLE; nInitorder = bond_type; nFlow = nInitorder - nMinorder; } else if ( nMetal && !nEndpoint ) { /* M-a, M-M */ /* atom - metal or metal-metal, none of them is an endpoint (2: M-M, M-A) */ nMinorder = pSrm->nMetalMinBondOrder; nInitorder = pSrm->nMetalInitBondOrder + bond_type - BOND_TYPE_SINGLE; nFlow = nInitorder - nMinorder; if ( !pSrm->nMetalInitEdgeFlow && pSrm->nMetalInitBondOrder > pSrm->nMetalMinBondOrder && nFlow > 0 ) { /* reduce initial flow by 1 and increase st_cap on metal by 1 */ nFlow --; } bNeedsFlower = (0 != pVA[iat].cMetal); } else if ( pVA[iat].cMetal && !at->endpoint && !pVA[neigh].cMetal && atom[neigh].endpoint|| pVA[neigh].cMetal && !atom[neigh].endpoint && !pVA[iat].cMetal && at->endpoint ) { /* M-ae */ /* metal connected to a non-metal endpoint (3: M-Ae) */ nMinorder = pSrm->nMetal2EndpointMinBondOrder; nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; nFlow = nInitorder - nMinorder; if ( !pSrm->nMetal2EndpointInitEdgeFlow && pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && nFlow > 0 ) { /* reduce initial flow by 1 and increase st_cap on metal by 1 */ nFlow --; } bNeedsFlower = (0 != pVA[iat].cMetal); } else { /* endpoint is metal => no flower (4: M-Me, Me-Me, Me-A, Me-Ae) */ nMinorder = pSrm->nMetal2EndpointMinBondOrder; nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; nFlow = nInitorder - nMinorder; if ( !pSrm->nMetal2EndpointInitEdgeFlow && pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && nFlow > 0 ) { /* reduce initial flow by 1 and increase st_cap on metal by 1 */ nFlow --; } bNeedsFlower = (pVA[iat].cMetal && !at->endpoint); } nMaxcap = BOND_TYPE_TRIPLE - nMinorder; if ( pnMaxcap ) { *pnMaxcap = nMaxcap; } if ( pnMinorder ) { *pnMinorder = nMinorder; } if ( pbNeedsFlower ) { *pbNeedsFlower = bNeedsFlower; } return nFlow; } /*********** new *******************************************************************************************/ int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ) { int ineigh, bFlower; int nStflow=0, nMaxBondCap, nMinBondOrder, bNeedsFlower = 0; int valence = atom[iat].valence; int nStcap = atom[iat].chem_bonds_valence; int nMGroupEdgeCap = 0, nMGroupEdgeFlow = 0, nFlow; if ( pSrm->bMetalAddFlower ) { nStcap -= pVA[iat].cInitOrigValenceToMetal - pVA[iat].cInitValenceToMetal; } for ( ineigh = 0; ineigh < valence; ineigh ++ ) { nFlow = BondFlowMaxcapMinorder( atom, pVA, pSrm, iat, ineigh, &nMaxBondCap, &nMinBondOrder, &bFlower ); nStflow += nFlow; nStcap -= nMinBondOrder; if ( bFlower ) { bNeedsFlower ++; nMGroupEdgeFlow += nFlow; nMGroupEdgeCap += BOND_TYPE_TRIPLE - nMinBondOrder + pSrm->nMetalMaxCharge_D; } } if ( pnStcap ) { *pnStcap = bNeedsFlower? nStflow : nStcap; /* initially, metal atoms are not radicals */ } if ( pnStflow ) { *pnStflow = nStflow; } if ( pnMGroupEdgeFlow ) { *pnMGroupEdgeFlow = nMGroupEdgeCap - nMGroupEdgeFlow; } if ( pnMGroupEdgeCap ) { *pnMGroupEdgeCap = nMGroupEdgeCap; } return bNeedsFlower; /* number of variable bonds to metal */ } /************************************************************************************** int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, T_GROUP_INFO *ti, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) fills out totals: pTCGroups->num_atoms = number of atoms pTCGroups->num_bonds = number of bonds between atoms pTCGroups->num_tgroups = number of tautomeric groups pTCGroups->num_tgroup_edges = number of edges to tautomeric groups pTCGroups->tgroup_charge = total charge on thautomeric atoms (negative) pTCGroups->num_tc_groups = total number of all groups pTCGroups->nVertices = total number of vertices excluding groups interconnections pTCGroups->nEdges = total number of edges excluding groups interconnections creates entries for the groups and adds to each group: TC_GROUP::type = BNS_VERT_TYPE_TGROUP, BNS_VT_C_POS, BNS_VT_C_NEG, BNS_VT_C_POS_C, BNS_VT_C_NEG_C TC_GROUP::ord_num = ordering number within the type, e.g. t-group number TC_GROUP::st_cap = all from the atoms in ChargeStruct or tautomeric group info. TC_GROUP::st_flow = all from the atoms in ChargeStruct (0 for t-groups). TC_GROUP::num_edges = number of edges to the atoms or ChargeStruct vertices. TC_GROUP::edges_cap = sum of all incoming edge caps; see also nTautEndpointEdgeCap(..). TC_GROUP::edges_flow = sum of all incoming edge flows; 0 for t-groups. TC_GROUP::nVertexNumber - NO FILLED WITH ANYTHING Note: the nDelta = st_cap - st_flow needs to be preserved when adding more vertices Return value: =0 => success <0 => error **************************************************************************************/ int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ) { int i, j, n, k, ret = 0, nBonds, nOtherEdges, nVertices, bMetalAtoms, bNeedsFlower; int nTgroupEdges, nTgroupEdgesFromTg, nTotNegChargInTgroups, cap, flow; MY_CONST C_NODE *pCN = NULL; nVertices = nBonds = nOtherEdges = nTgroupEdges = nTgroupEdgesFromTg = nTotNegChargInTgroups = 0; /* count metal atoms and electrons */ for ( i = 0; i < num_at; i ++ ) { pTCGroups->num_metal_atoms += (pVA[i].cMetal != 0); pTCGroups->num_metal_bonds += pVA[i].cNumBondsToMetal; pTCGroups->total_electrons += at[i].el_number; pTCGroups->total_electrons_metals += pVA[i].cMetal? at[i].el_number : 0; } pTCGroups->total_electrons -= pTCGroups->total_charge; pTCGroups->num_metal_bonds /= 2; /* register tautomeric groups */ for ( i = 0; i < ti->num_t_groups; i ++ ) { ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, ti->t_group[i].nGroupNumber, ti->t_group[i].num[0] /* st_cap */, 0 /* st_flow */, 0 /* edge cap */, 0 /* edge flow */, ti->t_group[i].nNumEndpoints /* num Edges */ ); if ( ret < 0 ) { goto exit_function; } /* edges to tautomeric groups */ nOtherEdges += ti->t_group[i].nNumEndpoints; nTgroupEdgesFromTg += ti->t_group[i].nNumEndpoints; /* total negative charge in t-groups */ nTotNegChargInTgroups += ti->t_group[i].num[1]; if ( ret > 0 ) { /* should always happen since this is the first time this t-group is added */ j = ret-1; pTCGroups->pTCG[j].tg_num_H = ti->t_group[i].num[0] - ti->t_group[i].num[1]; pTCGroups->pTCG[j].tg_num_Minus = ti->t_group[i].num[1]; } } bMetalAtoms = 0; repeat_for_metals: /* count vertices and register ChargeValence groups */ /* for now an atom may belong either to a t-group or to a ChargeValence group, but not to both */ for ( i = 0; i < num_at; i ++ ) { /* number of bonds */ nBonds += at[i].valence; /* Process ChargeStruct vertices and edges */ if ( pVA[i].cnListIndex ) { /* count vertices & edges in the ChargeValence Substructure attached to an atom */ /* Important: unlike inp_ATOM, each edge e appears in pCN[*].e[*] only ONE time */ int len = cnList[j = pVA[i].cnListIndex-1].len; int bits = cnList[j].bits; int type, neigh_type, metal_group_number; pCN = cnList[j].pCN; /* first process all non-metals, after that -- all metals */ if ( (bits != cn_bits_Me) != !bMetalAtoms ) { continue; } metal_group_number = 0; for ( j = 0; j < len; j ++) { type = pCN[j].v.type; /* ChargeStruct vertex type: atom is the first, c-groups are last */ /* process all pCN[j] neighbors */ for ( k = 0; k < MAX_CN_VAL && (n = pCN[j].e[k].neigh); k ++ ) { nOtherEdges ++; /* edges inside ChargeStruct */ n --; /* neighbor vertex position inside cnList[j].pCN */ neigh_type = pCN[n].v.type; /* type of the neighboring atom */ if ( IS_BNS_VT_C_GR(neigh_type) ) { /* register this edge to a CN-group vertex */ cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } if ( ret > 0 ) { /* the group has just been created; add one more edge to (+/-) or supergroup */ ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } nOtherEdges ++; } } if ( IS_BNS_VT_C_GR(type) ) { /* register this edge to a CN-group vertex; normally this does not happen */ cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } if ( ret > 0 ) { /* the group has just been created; add one more edge to (+/-) or supergroup */ ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } nOtherEdges ++; } } } /* end of the current vertex pCN[j] neighbors */ /* process pCN[j] vertex */ if ( type & BNS_VERT_TYPE_ATOM ) { continue; /* do not count regular atoms here */ } if ( IS_BNS_VT_CHRG_STRUCT(type) ) { nVertices ++; continue; } if ( pSrm->bMetalAddFlower && IS_BNS_VT_M_GR( type ) ) { /* special treatment: flow and cap are known as well as structure */ /* initial bond valence to metal is either 0 or 1 */ EdgeFlow nEdgeFlow, nEdgeCap; bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, i, NULL /*pnStcap*/, NULL /*pnStflow*/, &nEdgeCap, &nEdgeFlow ); if ( !bNeedsFlower ) { ret = RI_ERR_PROGR; goto exit_function; } /* GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm, at, pVA, i ); GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm ); */ /* the 1st is the flower base */ /* atom - G0 edge and G0 vertex */ ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, /*pVA[i].cInitFreeValences*/ 0 /* st_cap */, 0 /* st_flow */, (int)nEdgeCap, (int)nEdgeFlow, 1 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } /* count edge atom-G0 */ nOtherEdges ++; if ( ret > 0 ) { /* first time registration: add G0-G1 and G0-G2 edges to G0 */ ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); if ( ret < 0 ) { goto exit_function; } /* first time registration: add G1; it has 3 edges */ ret = RegisterTCGroup( pTCGroups, type, 1 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); if ( ret <= 0 ) { ret = !ret? RI_ERR_PROGR : ret; goto exit_function; } /* first time registration: add G2; it has 3 edges */ ret = RegisterTCGroup( pTCGroups, type, 2 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); if ( ret <= 0 ) { ret = !ret? RI_ERR_PROGR : ret; goto exit_function; } /* first time registration: add G3; it has 2 edges */ ret = RegisterTCGroup( pTCGroups, type, 3 /* ord_num*/, 0 /* st_cap */, 0 /* st_flow */, 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); if ( ret <= 0 ) { ret = !ret? RI_ERR_PROGR : ret; goto exit_function; } /* count added metal flower vertices: G0, G1, G2, G3 */ nVertices += 4; /* count added metal flower edges: C0-C1, C0-C2, C1-C2, C1-C3, C2-C3 */ nOtherEdges += 5; /* add connections of G0 to G1 and G2 */ } continue; } nVertices ++; /* count BNS_VT_C_POS* types; all contain BNS_VERT_TYPE_C_GROUP bit */ if ( !IS_BNS_VT_C_GR(type) ) { /* check */ ret = RI_ERR_PROGR; goto exit_function; } /* add st_cap and st_flow for a charge group */ cap = !bMetalAtoms? pCN[j].v.cap : pCN[j].v.cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[j].v.flow : pCN[j].v.flow? pSrm->nMetalMaxCharge_D : 0; ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, cap /* st-cap*/, flow /* st-flow */, 0 /* edge cap */, 0 /* edge flow */, 0 /* edges already counted */ ); if ( ret < 0 ) { goto exit_function; } } } else { pCN = NULL; } /* count edge caps to t-groups */ if ( at[i].endpoint ) { int nEdgeCap = nTautEndpointEdgeCap( at, pVA, i ); nTgroupEdges ++; if ( nEdgeCap < 0 ) { ret = nEdgeCap; goto exit_function; } /* add number of unsatisfied valences for a t-group; the unknown flow = 0 */ ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, at[i].endpoint, 0 /* st_cap */, 0 /* st_flow */, nEdgeCap /* edge cap */, 0 /* edge flow */, 0 /* t-group edges have already been counted */ ); if ( ret < 0 ) { goto exit_function; } } } if ( !bMetalAtoms && pTCGroups->num_metal_atoms ) { bMetalAtoms = 1; nBonds = 0; /* added 2006-05-15 */ goto repeat_for_metals; } /* count real atoms and bonds */ nBonds /= 2; pTCGroups->num_atoms = num_at; pTCGroups->num_bonds = nBonds; pTCGroups->num_tgroups = ti->num_t_groups; pTCGroups->num_tgroup_edges = nTgroupEdges; pTCGroups->tgroup_charge = -nTotNegChargInTgroups; if ( 0 <= ret && nTgroupEdgesFromTg != nTgroupEdges ) { ret = BNS_PROGRAM_ERR; } nVertices += num_at; /* count other vertices */ nVertices += ti->num_t_groups; nBonds += nOtherEdges; /* return edges and vertices */ pTCGroups->nVertices = nVertices; pTCGroups->nEdges = nBonds; exit_function: return ret; } /**************************************************************** int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) 1. adds BNS_VT_C_POS_ALL and BNS_VT_C_NEG_ALL ONLY if both {TCG_Plus0 and TCG_Plus_C0} and/or {TCG_Minus0 and TCG_Minus_C0} are present, respectively 2. fills pTCGroups->nGroup[]: pTCGroups->nGroup[k] < 0 => does not exist pTCGroups->nGroup[k] = i => the group is pTCGroups->pTCG[i] where group group k = type number TCG_Plus0 BNS_VT_C_POS 0 TCG_Plus1, BNS_VT_C_POS 1 TCG_Minus0, BNS_VT_C_NEG 0 TCG_Minus1, BNS_VT_C_NEG 1 TCG_Plus_C0, BNS_VT_C_POS_C 0 TCG_Plus_C1, BNS_VT_C_POS_C 1 TCG_Minus_C0, BNS_VT_C_NEG_C 0 TCG_Minus_C1, BNS_VT_C_NEG_C 1 TCG_Plus, BNS_VT_C_POS_ALL 0 TCG_Minus, BNS_VT_C_NEG_ALL 0 only groups with number 0 are processed 3. If only one of the groups in pairs mentioned in (1) above is present then pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus0] or pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus_C0]; an additional BNS_VT_C_POS_ALL vertex is not created same for pTCGroups->nGroup[TCG_Minus] and BNS_VT_C_NEG_ALL 4. Adds to these new "supergroups" (TCG_Plus, TCG_Minus) descriptions in pTCGroups->pTCG[k] st_cap, st_flow, edges cap and flow from the corresponding groups {TCG_Plus0 and TCG_Plus_C0}. Same for the Minus groups. Stores indexes k in pTCGroups->nGroup[TCG_Plus], pTCGroups->nGroup[TCG_Minus] ****************************************************************/ int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) { int i, k, n, n1, n2, n3, nNumTg = 0, ret = 0, nNumToConnect; for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { nNumTg ++; continue; /* t-group */ } if ( IS_BNS_VT_C_GR(pTCGroups->pTCG[i].type) || IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { /* ChargeValence (cn) group */ switch( pTCGroups->pTCG[i].type ) { case BNS_VT_C_POS: k = TCG_Plus0; break; case BNS_VT_C_NEG: k = TCG_Minus0; break; case BNS_VT_C_POS_C: k = TCG_Plus_C0; break; case BNS_VT_C_NEG_C: k = TCG_Minus_C0; break; case BNS_VT_C_POS_M: k = TCG_Plus_M0; break; case BNS_VT_C_NEG_M: k = TCG_Minus_M0; break; case BNS_VT_M_GROUP: switch( pTCGroups->pTCG[i].ord_num ) { case 0: k = TCG_MeFlower0; break; case 1: k = TCG_MeFlower1; break; case 2: k = TCG_MeFlower2; break; case 3: k = TCG_MeFlower3; break; default: ret = RI_ERR_PROGR; /* unexpected group type */ goto exit_function; } break; default: ret = RI_ERR_PROGR; /* unexpected group type */ goto exit_function; } if ( pTCGroups->nGroup[k] >= 0 || pTCGroups->pTCG[i].ord_num && !IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { ret = RI_ERR_PROGR; goto exit_function; } pTCGroups->nGroup[k] = i; /* ordering number of the Charge group, starting from 0 */ } } /* add (+) supergroup */ n1 = pTCGroups->nGroup[TCG_Plus0]; n2 = pTCGroups->nGroup[TCG_Plus_C0]; n3 = pTCGroups->nGroup[TCG_Plus_M0]; nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); if ( nNumToConnect ) { /* if both groups are present then add a supergroup */ ret = RegisterTCGroup( pTCGroups, BNS_VT_C_POS_ALL, 0, 0 /* st_cap */, 0 /* st_flow */, 0 /* edge cap */, 0 /* edge flow */, 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); if ( ret <= 0 ) { ret = !ret? RI_ERR_PROGR : ret; goto exit_function; } pTCGroups->nGroup[TCG_Plus] = ret - 1; /* newly added group number */ pTCGroups->nVertices += 2; /* two vertices including itself */ pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ } /* add (-) supergroup */ n1 = pTCGroups->nGroup[TCG_Minus0]; n2 = pTCGroups->nGroup[TCG_Minus_C0]; n3 = pTCGroups->nGroup[TCG_Minus_M0]; nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); if ( nNumToConnect ) { /* if both groups are present then add a supergroup */ ret = RegisterTCGroup( pTCGroups, BNS_VT_C_NEG_ALL, 0, 0 /* st_cap */, 0 /* st_flow */, 0 /* edge cap */, 0 /* edge flow */, 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); if ( ret < 0 ) { goto exit_function; } pTCGroups->nGroup[TCG_Minus] = ret - 1; /* newly added group number */ pTCGroups->nVertices += 2; /* needs two vertices including itself */ pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ } /* add neutralization vertex: (+)-()=(-) connection */ k = pTCGroups->nGroup[TCG_Minus]; n = pTCGroups->nGroup[TCG_Plus]; nNumToConnect = (k>=0) + (n>=0); if ( nNumToConnect ) { pTCGroups->nVertices += 1; pTCGroups->nEdges += nNumToConnect; /* one edge per super-c-group */ } ret = 0; exit_function: return ret; } /*********************************************************************************/ int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) { int ret = 0; inp_ATOM *at = pStruct->at; int num_atoms = pStruct->num_atoms; int tot_st_cap, tot_st_flow; /* ret = ReInitBnStruct( pBNS ); */ if ( pTCGroups->num_tgroups /* tgi && tgi->num_t_groups && tgi->t_group*/ ) { int i, k, endpoint, /*centerpoint,*/ fictpoint; int num_tg = pTCGroups->num_tgroups; int num_edges = pBNS->num_edges; int num_vertices = pBNS->num_vertices; BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev; /* fictitious vertex describing t-group */ BNS_VERTEX *vert_endpoint; BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ int nMaxTGroupNumber = 0; /*ENDPOINT_INFO eif;*/ /* Debug: check overflow */ if ( num_vertices + num_tg >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } if ( num_edges + pTCGroups->num_tgroup_edges >= pBNS->max_edges ) { return BNS_VERT_EDGE_OVFL; } /* find the largest t-group ID */ for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { k = pTCGroups->pTCG[i].ord_num; if ( k <= 0 ) { return BNS_CPOINT_ERR; /* t-group does not have a number or has a wrong number */ } if ( k > pTCGroups->num_tc_groups ) { return BNS_CPOINT_ERR; /* t-group has a wrong number */ } if ( k != nMaxTGroupNumber + 1 ) { return BNS_CPOINT_ERR; /* t-group numbers are not contiguously ascending */ } nMaxTGroupNumber = k; } else { break; /* t-groups are contiguous and first in the list */ } } if ( i != num_tg ) { return BNS_CPOINT_ERR; /* number of t-groups is wrong */ } /* since t-group IDs may be not contiguous, clear all vertices that will be added. all-zeroes-vertex will be ignored by the BNS */ memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); /* initialize new fictitious vertices */ vert_ficpoint_prev = pBNS->vert+num_vertices - 1; tot_st_cap = tot_st_flow = 0; for ( i = 0; i < num_tg; i ++, vert_ficpoint_prev = vert_ficpoint ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the vertex that is being added Note: nGroupNumber are not contiguous */ vert_ficpoint = pBNS->vert+num_vertices + pTCGroups->pTCG[i].ord_num - 1; vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i].num_edges+nMaxAddEdges+BNS_ADD_SUPER_TGROUP; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = pTCGroups->pTCG[i].st_cap; tot_st_cap += pTCGroups->pTCG[i].st_cap; vert_ficpoint->type = pTCGroups->pTCG[i].type; pTCGroups->pTCG[i].nVertexNumber = vert_ficpoint - pBNS->vert; } for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { if ( !at[endpoint].endpoint ) continue; fictpoint = at[endpoint].endpoint + num_vertices - 1; vert_ficpoint = pBNS->vert + fictpoint; /* t-group vertex */ vert_endpoint = pBNS->vert + endpoint; /* endpoint vertex */ /* Debug: check overflow */ if ( fictpoint >= pBNS->max_vertices || num_edges >= pBNS->max_edges || vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { ret = BNS_VERT_EDGE_OVFL; break; } #ifdef NEVER /* obtain donor/acceptor info */ if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { ret = BNS_BOND_ERR; break; } #endif vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; #ifdef NEVER /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { int iedge = vert_endpoint->iedge[k]; if ( !pBNS->edge[iedge].cap ) { /* single bond, possibly between endpoint and centerpoint */ centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); if ( centerpoint < pBNS->num_atoms && pBNS->vert[centerpoint].st_edge.cap >= 1 ) { int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); if (bond_type == BOND_TAUTOM || bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_SINGLE ) { pBNS->edge[iedge].cap = 1; } } } } #endif /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ edge = pBNS->edge + num_edges; edge->cap = vert_endpoint->st_edge.cap - vert_endpoint->st_edge.flow; edge->cap = inchi_min( edge->cap, MAX_TGROUP_EDGE_CAP ); edge->cap = inchi_max( edge->cap, 0 ); edge->flow = 0; edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; #endif #ifdef NEVER /* later include case when the charge change allows the endpoint to become tautomeric */ /* mark endoint having moveable H atom with flow=1 */ /* -- old "no charges" version -- */ /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ /* -- the following line takes charges into account -- */ if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ { /* increment edge flow */ edge->flow ++; /* increment one vertex st-flow & cap */ vert_ficpoint->st_edge.flow ++; vert_ficpoint->st_edge.cap ++; /* increment another vertex st-flow & cap */ vert_endpoint->st_edge.flow ++; vert_endpoint->st_edge.cap ++; } #endif /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ ret = ConnectTwoVertices( vert_endpoint, vert_ficpoint, edge, pBNS, 0 ); if ( IS_BNS_ERROR( ret ) ) { break; } num_edges ++; edge->cap0 = edge->cap; edge->flow0 = edge->flow; pVA[endpoint].nTautGroupEdge = num_edges; /* edge index + 1 */ } pBNS->num_edges = num_edges; pBNS->num_vertices += nMaxTGroupNumber; pBNS->num_t_groups = num_tg; pBNS->tot_st_cap += tot_st_cap; pBNS->tot_st_flow += tot_st_flow; } return ret; } /*****************************************************************************************************/ int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ) { int ip1 = p1 - pBNS->vert; int ip2 = p2 - pBNS->vert; int ie = e - pBNS->edge; /* debug: check bounds */ if ( ip1 >= pBNS->max_vertices || ip1 < 0 || ip2 >= pBNS->max_vertices || ip2 < 0 || ie >= pBNS->max_edges || ie < 0 || (p1->iedge - pBNS->iedge) < 0 || (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || (p2->iedge - pBNS->iedge) < 0 || (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || p1->num_adj_edges >= p1->max_adj_edges || p2->num_adj_edges >= p2->max_adj_edges ) { return BNS_VERT_EDGE_OVFL; } /* clear the edge */ if ( bClearEdge ) { memset( e, 0, sizeof(*e) ); } else if ( e->neighbor1 || e->neighbor12 ) { return BNS_PROGRAM_ERR; } /* connect */ e->neighbor1 = inchi_min( ip1, ip2 ); e->neighbor12 = ip1 ^ ip2; p1->iedge[p1->num_adj_edges] = ie; p2->iedge[p2->num_adj_edges] = ie; e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; return 0; } /*********************************************************************************************************** METAL ATOMS' FLOWER - Provides a source/sink of "free valences" *********************************************************************************************************** c1+...+cn = 2c+dc - total cap and flow of edges to the flower base from metal atoms f1+...+fn = 2f+df they should allow changing bonds to metals from 0-order to triple dc,df = 0 or 1 hence c=3*n, f=0 (initial zero bond order) or n Gi=vertex(M-group) Ci=its st_cap [C3,F3] C0 = F0 = 2c + 2D + dc (st_cap & st_flow) Fi=its st_flow G3 C2 = F2 = c + 2D / \ C1 = F1 = c + 2D + dc-df ci=cap of edge i cx,fx/ \cy,fy C3 = F3 = 0 fi=edge flow / \ Constraints [C2,F2]/ cd,fd \[C1,F1] ----------------- G2--------G1 fa+fb+2f+df=F0=C0 \ / ca = c + 2D (edge cap) fa+fd =F2=C2 ca,fa \ / cb,fb fa = c + D - f (edge flow) fb+fd =C1=F1 \ / fi <= ci G0 [C0,F0] cb = c + 2D + dc ----------------- /\ fb = c + D + dc - (f + df) ci=3, fi=0 or 1 c1,f1 /... \ cn,fn ------------------------------------ / \ cd = c + 2D D is an arbitrary integer > 0 all n Metal atoms: M1 ... Mn fd = f + D it allows to apply C3++ (add st_flow to cancel radicals) For each Mi add cap and flow=cap cx = cy = D D times. to M-charge group fx = fy = 0 -------------------------------------------------------------------------------------- | f=0 | f=c, dc>=df | 0 <= 2f+df <= 2c+dc edge +------------------------+-----------+----------+-------------+------------- | flow | rescap | flow | rescap | flow | rescap ----------+------------+-----------+-----------+----------+-------------+------------- f1+..+fn | df | 2c+dc-df | 2c+df | dc-df | 2f+df | 2c-2f+dc-df fa | c+D | D | D | c+D | c+D-f | c+D fb | c+D+dc-df | D+df | D+dc-df | c+D+df | c+D+dc-f-df| c+D+df fd | D | c+D | c+D | D | f+D | D -------------------------------------------------------------------------------------- ***********************************************************************************************************/ int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) { int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); int vG0, vG1, vG2, vG3; /* M-vertex number */ BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; if ( pTCGroups->num_metal_atoms && pSrm->bMetalAddFlower && *tot_st_cap % 2 && n == 4 ) { vG0 = pTCGroups->pTCG[iG0].nVertexNumber; vG1 = pTCGroups->pTCG[iG1].nVertexNumber; vG2 = pTCGroups->pTCG[iG2].nVertexNumber; vG3 = pTCGroups->pTCG[iG3].nVertexNumber; pG0 = pBNS->vert+vG0; pG1 = pBNS->vert+vG1; pG2 = pBNS->vert+vG2; pG3 = pBNS->vert+vG3; /* add 1 unit to metal flower st_cap */ pG3->st_edge.cap ++; pG3->st_edge.cap0 ++; (*tot_st_cap) ++; return 1; } return 0; } /***********************************************************************************************************/ int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) { int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); int vG0, vG1, vG2, vG3; /* M-vertex number */ int cur_num_edges = *pcur_num_edges; int cur_num_vertices = *pcur_num_vertices; BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; BNS_EDGE *ea=NULL, *eb=NULL, *ed=NULL, *ex=NULL, *ey=NULL, *e; int ia, ib, id, ix, iy; int c, f, dc, df, ca, fa, cb, fb, cd, fd, cx, fx, cy, fy; int C0, F0, C1, F1, C2, F2, C3, F3, D; int ret = 0, i; if ( 0 == n ) { goto exit_function; } if ( 4 != n ) { ret = RI_ERR_PROGR; goto exit_function; } vG0 = pTCGroups->pTCG[iG0].nVertexNumber; vG1 = pTCGroups->pTCG[iG1].nVertexNumber; vG2 = pTCGroups->pTCG[iG2].nVertexNumber; vG3 = pTCGroups->pTCG[iG3].nVertexNumber; pG0 = pBNS->vert+vG0; pG1 = pBNS->vert+vG1; pG2 = pBNS->vert+vG2; pG3 = pBNS->vert+vG3; /* count G0 edges cap and flow (currently only atoms are connected to G0) */ for ( i = 0, c = 0, f = 0; i < pG0->num_adj_edges; i ++ ) { e = pBNS->edge + pG0->iedge[i]; c += e->cap; f += e->flow; } /* consistency checks */ if ( !IS_BNS_VT_M_GR(pTCGroups->pTCG[iG0].type) && (pTCGroups->pTCG[iG0].edges_cap != pG0->st_edge.cap || pTCGroups->pTCG[iG0].edges_flow != pG0->st_edge.flow) ) { ret = RI_ERR_PROGR; goto exit_function; } if ( pTCGroups->pTCG[iG0].edges_cap != c || pTCGroups->pTCG[iG0].edges_flow != f ) { ret = RI_ERR_PROGR; goto exit_function; } /* get new edges */ ea = pBNS->edge + (ia=cur_num_edges++); eb = pBNS->edge + (ib=cur_num_edges++); ed = pBNS->edge + (id=cur_num_edges++); ex = pBNS->edge + (ix=cur_num_edges++); ey = pBNS->edge + (iy=cur_num_edges++); /* connect vertices with edges */ ret = ConnectTwoVertices( pG0, pG1, eb, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } ret = ConnectTwoVertices( pG0, pG2, ea, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } ret = ConnectTwoVertices( pG1, pG2, ed, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } ret = ConnectTwoVertices( pG1, pG3, ey, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } ret = ConnectTwoVertices( pG2, pG3, ex, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } /* calculate caps and flows */ dc = c % 2; c /= 2; df = f % 2; f /= 2; D = pSrm->nMetalFlowerParam_D; C0 = F0 = 2*c + 2*D + dc; C1 = F1 = c + 2*D + dc - df; C2 = F2 = c + 2*D; C3 = F3 = 0; ca = c + 2*D; fa = c + D - f; cb = c + 2*D + dc; fb = c + D + dc - ( f + df ); cd = c + 2*D; fd = f + D; cx = cy = D; fx = fy = 0; /* check overflow */ if ( C0 >= EDGE_FLOW_ST_MASK || F0 >= EDGE_FLOW_ST_MASK || C1 >= EDGE_FLOW_ST_MASK || F1 >= EDGE_FLOW_ST_MASK || C2 >= EDGE_FLOW_ST_MASK || F2 >= EDGE_FLOW_ST_MASK || C3 >= EDGE_FLOW_ST_MASK || F3 >= EDGE_FLOW_ST_MASK ) { return BNS_PROGRAM_ERR; /* cannot handle too large st-cap or st-flow */ } /* set st caps and flows */ SetStCapFlow( pG0, tot_st_flow, tot_st_cap, C0, F0 ); SetStCapFlow( pG1, tot_st_flow, tot_st_cap, C1, F1 ); SetStCapFlow( pG2, tot_st_flow, tot_st_cap, C2, F2 ); SetStCapFlow( pG3, tot_st_flow, tot_st_cap, C3, F3 ); SetEdgeCapFlow( ea, ca, fa ); SetEdgeCapFlow( eb, cb, fb ); SetEdgeCapFlow( ed, cd, fd ); SetEdgeCapFlow( ex, cx, fx ); SetEdgeCapFlow( ey, cy, fy ); *pcur_num_edges = cur_num_edges; *pcur_num_vertices = cur_num_vertices; ret = 0; exit_function: return ret; } /********************************************************************************/ void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ) { e->cap = e->cap0 = edge_cap; e->flow = e->flow0 = edge_flow; } /********************************************************************************* Add cap and flow to an edge Add edge flow to the source vertex st_flow Add edge cap & flow to the destination vertex cap and flow *********************************************************************************/ int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pSrc /*src*/, BNS_VERTEX *pDst/*dest*/, int *tot_st_cap, int *tot_st_flow ) { /* overflow chaeck */ if ( e01->cap < 0 || edge_cap < 0 || (int)e01->cap + edge_cap >= EDGE_FLOW_MASK ) { return BNS_PROGRAM_ERR; } if ( pDst->st_edge.cap < 0 || (int)pDst->st_edge.cap + edge_cap >= EDGE_FLOW_ST_MASK || pDst->st_edge.flow < 0 || (int)pDst->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK || pSrc->st_edge.cap < 0 || pSrc->st_edge.flow < 0 || (int)pSrc->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK ) { return BNS_PROGRAM_ERR; } /* add flow */ e01->cap += edge_cap; e01->flow += edge_flow; e01->cap0 = e01->cap; e01->flow0 = e01->flow; pDst->st_edge.cap += edge_cap; pDst->st_edge.cap0 = pDst->st_edge.cap; *tot_st_cap += edge_cap; pDst->st_edge.flow += edge_flow; pDst->st_edge.flow0 = pDst->st_edge.flow; *tot_st_flow += edge_flow; pSrc->st_edge.flow += edge_flow; pSrc->st_edge.flow0 = pSrc->st_edge.flow; *tot_st_flow += edge_flow; /* pDst->st_edge.cap += e01->cap; pDst->st_edge.cap0 = pDst->st_edge.cap; *tot_st_cap += e01->cap; pDst->st_edge.flow += e01->flow; pDst->st_edge.flow0 = pDst->st_edge.flow; *tot_st_flow += e01->flow; pSrc->st_edge.flow += e01->flow; pSrc->st_edge.flow0 = pSrc->st_edge.flow; *tot_st_flow += e01->flow; */ return 0; } /************************************************************** (+) and (-) group V - connection ================================ BNS_VERT_TYPE__AUX (+/-)-connection (v) st_cap = / \ st_flow = (cap0 - Delta0 - flow0) + (cap1 - Delta1 -flow1) / \ / \ cap = cap1 / \ flow = (cap1 - Delta1 - flow1) / \ (-) (+) st_cap = cap1 / \ / \ st_flow = cap1 - Delta1 /cap0 \ /cap1 \ flow0 flow1 *************************************************************** (+) supergroup Y - connection ============================== (+) BNS_VT_C_POS_ALL (+) supergroup Delta0 | ============== not shown | cap = cap0+cap1 | flow = flow0+flow1-Delta0-Delta1 BNS_VERT_TYPE__AUX (y) <------------------ additional vertex: st_cap = cap0+cap1 / \ st_flow = cap0+cap1 cap=cap0 / \ cap = cap1 flow=cap0-flow0 / \ flow = cap1 - flow1 - Delta1 -Delta0 / \ not-C (+) (+) Carbons st_cap = cap1 BNS_VT_C_POS / \ / \ BNS_VT_C_POS_C st_flow = cap1 - Delta1 / \ / \ totals cap0 cap1 = sum of all cap going up into (+) from atoms or ChargeStruct to (+): flow0 flow1= sum of all flow going up into (+) Delta0 Delta1 = st_cap(+)-st_flow(+) before connection Observations ============ A. Any Delta > 0 on (+) or (-) group decreases total (signed) charge by Delta B. Any alt path from an atom through ChargeStruct to an atom does not change the total charge C. st_flow(+/-) = cap(+)-flow(+)-Delta(+) + cap(-)-flow(-)-Delta(-) = = charge(+) + |max (-) charge| + charge(-) = const (charge conservation) D. To decrease total charge: increase st_cap on (+) or (-) group, including supergroup E. To increase total charge: increase st_cap on any (y) or (v)-connecting vertex F. To cancel charges: 1. Forbid (+/-)-(+) or (+/-)-(-) edge 2. Add delta>0 to (+/-) st_cap 3. Add same delta to (+) or (-) st_cap ****************************************************************/ /************************************************************************************ j2,j3 < j1 < j0 (+/-) <---- next step; if does not exist then / \ st_cap1' := st_flow1' / \ / \ st_cap1' := cap01' pv1 (+)super st_flow1':= cap01'-flow01' = flow02'+flow03' j1 | | cap01' := st_cap0' | flow01':= st_cap0'-flow02'-flow03' | | st_cap0' := ( ) pv0,j0 st_flow0':= cap2+st_cap3 / \ / \ cap03' = cap3 / \ flow03' = cap3 - flow3 - Delta3 / \ st_cap2, st_flow2 (+) (+C) st_cap3' := cap3 pv2,j2 pv3,j3 st_flow3' := cap3-Delta3 / \ / \ Delta3 := st_cap3 - st_flow3 cap2, flow2 cap3, flow3 = sums of incoming **************************************************************************************/ int ConnectSuperCGroup( int nSuperCGroup, int nAddGroups[], int num_add, int *pcur_num_vertices, int *pcur_num_edges, int *tot_st_cap, int *tot_st_flow, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) { BNS_EDGE **e0X = NULL, *e; BNS_VERTEX **pvX = NULL, *pv0=NULL, *pv1=NULL, *pv=NULL; int *jX = NULL, *iX = NULL; int i, j, num_groups, j0, i1, j1, iXX, ret = 0, fst=0; int cur_num_vertices = *pcur_num_vertices; int cur_num_edges = *pcur_num_edges; if ( nSuperCGroup >= 0 ) { i1 = pTCGroups->nGroup[nSuperCGroup]; /* the supergroup */ if ( i1 < 0 ) return 0; } else { i1 = -1; fst = 1; } for ( i = num_groups = 0; i < num_add; i ++ ) { iXX = pTCGroups->nGroup[nAddGroups[i]]; num_groups += (iXX >= 0 && iXX != i1); } if ( num_groups < 1 ) { /* Y connect only 2 or more groups; V connects even 1 group */ return 0; } e0X = (BNS_EDGE **)inchi_calloc( num_groups + 1, sizeof(e0X[0]) ); pvX = (BNS_VERTEX **)inchi_calloc( num_groups + 1, sizeof(pvX[0]) ); jX = (int *)inchi_calloc( num_groups + 1, sizeof(jX[0]) ); iX = (int *)inchi_calloc( num_groups + 1, sizeof(iX[0]) ); if ( !e0X || !pvX || !jX || !iX ) { ret = RI_ERR_ALLOC; goto exit_function; } /* create vert_ficpoint -- central Y-connection vertex */ j0 = cur_num_vertices; pv0 = pBNS->vert + j0; /* center of the Y-connection; has number j0 */ pv0->iedge = (pv0 - 1)->iedge + (pv0 - 1)->max_adj_edges; pv0->max_adj_edges = num_groups + 1 + BNS_ADD_EDGES; /* Y-connection num. edges */ pv0->num_adj_edges = 0; /* nothing connected yet */ pv0->type = BNS_VT_YVCONNECTOR; cur_num_vertices ++; if ( fst == 0 ) { /* find super c-group vertex pv1, number j1 */ jX[0] = j1 = pTCGroups->pTCG[i1].nVertexNumber; iX[0] = i1; pvX[0] = pv1 = pBNS->vert + j1; } /* find other c-group vertices */ for( i = 0, j = 1; i < num_add; i ++ ) { iXX = pTCGroups->nGroup[nAddGroups[i]]; if ( (iXX >= 0) && (iXX != i1) ) { iX[j] = iXX; jX[j] = pTCGroups->pTCG[iXX].nVertexNumber; pvX[j] = pBNS->vert + jX[j]; j ++; } } /* grab (num_groups+1) free edges */ for ( i = fst; i <= num_groups; i ++ ) { e = e0X[i] = pBNS->edge + cur_num_edges; pv = pvX[i]; j = jX[i]; iXX = iX[i]; /* connect all to pv0 */ ret = ConnectTwoVertices( pv0, pv, e, pBNS, 1 ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } if ( i ) { /* from c-group to central Y-connecting vertex of from supergroup to (+/-) vertex */ pTCGroups->pTCG[iX[i]].nForwardEdge = cur_num_edges; } else { /* from central Y-connecting vertex to supergroup */ pTCGroups->pTCG[iX[i]].nBackwardEdge = cur_num_edges; } cur_num_edges ++; } /* set flow and cap for incoming into pv0 edges */ for ( i = 1; i <= num_groups; i ++ ) { int nDelta = pTCGroups->pTCG[iX[i]].st_cap - pTCGroups->pTCG[iX[i]].edges_cap; int edge_cap = pTCGroups->pTCG[iX[i]].edges_cap + nDelta; /* added nDelta */ int edge_flow = pTCGroups->pTCG[iX[i]].edges_cap-pTCGroups->pTCG[iX[i]].edges_flow /*-nDelta*/; ret = AddEdgeFlow( edge_cap, edge_flow, e0X[i], pvX[i]/*src*/, pv0 /* dest*/, tot_st_cap, tot_st_flow ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } } if ( fst == 0 ) { /* set flow and cap for going out of pv0 and into pv1 edge */ int edge_cap = pv0->st_edge.cap; int edge_flow = pv0->st_edge.cap - pv0->st_edge.flow; ret = AddEdgeFlow( pv0->st_edge.cap, pv0->st_edge.cap - pv0->st_edge.flow, e0X[0], pv0/*src*/, pv1 /* dest*/, tot_st_cap, tot_st_flow ); if ( IS_BNS_ERROR( ret ) ) { goto exit_function; } pTCGroups->pTCG[iX[0]].edges_cap += edge_cap; pTCGroups->pTCG[iX[0]].edges_flow += edge_flow; pTCGroups->pTCG[iX[0]].st_cap += edge_cap; pTCGroups->pTCG[iX[0]].st_flow += edge_flow; } else { /* no supergroup => change cap to flow */ *tot_st_cap += pv0->st_edge.flow - pv0->st_edge.cap; pv0->st_edge.cap += pv0->st_edge.flow - pv0->st_edge.cap; pv0->st_edge.cap0 = pv0->st_edge.cap; } *pcur_num_vertices = cur_num_vertices; *pcur_num_edges = cur_num_edges; ret = num_groups; exit_function: if ( e0X ) inchi_free( e0X ); if ( pvX ) inchi_free( pvX ); if ( jX ) inchi_free( jX ); if ( iX ) inchi_free( iX ); return ret; } /*********************************************************************************/ void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) { vert_ficpoint->st_edge.flow += flow; *tot_st_flow += flow; vert_ficpoint->st_edge.cap += cap; *tot_st_cap += cap; vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; } /*********************************************************************************/ void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) { *tot_st_flow += flow - vert_ficpoint->st_edge.flow; vert_ficpoint->st_edge.flow = flow; *tot_st_cap += cap - vert_ficpoint->st_edge.cap; vert_ficpoint->st_edge.cap = cap; vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; } /********************************************************************************* int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) *********************************************************************************/ int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) { int ret = 0, ret1, ret2, ret3, bNeedsFlower; inp_ATOM *at = pStruct->at; int num_atoms = pStruct->num_atoms; /*int num_tg = pTCGroups->num_tgroups;*/ int num_cg = pTCGroups->num_tc_groups - pTCGroups->num_tgroups; int fst_cg_vertex = pBNS->num_vertices; int fst_cg_group = pTCGroups->num_tgroups; int num_vertices = pBNS->num_vertices; int num_edges = pBNS->num_edges; int cg_charge = 0; ICHICONST SRM *pSrm = pStruct->pSrm; /* ret = ReInitBnStruct( pBNS ); */ if ( num_cg > 0 ) { /* if ( cgi && cgi->num_c_groups && cgi->c_group ) */ int i, i1, i2, j, j1, j2, k, k1, k2, n, c_point, c_neigh, cap, flow; int cur_num_vertices, cur_num_edges; BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev, *vert_ficpoint_base; /* fictitious vertex describing charge c-group */ BNS_VERTEX *pv1, *pv2; BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ int nMaxCGroupNumber = 0; MY_CONST C_NODE *pCN; int cn_len, cn_bits, bMetalAtoms; int type; int tot_st_cap, tot_st_flow; int nAddGroups[16]; /* Debug: check overflow */ if ( num_vertices >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } nMaxCGroupNumber = num_cg; /* clear all vertices not used until now */ memset( pBNS->vert+num_vertices, 0, (pBNS->max_vertices - num_vertices)*sizeof(pBNS->vert[0]) ); tot_st_cap = pBNS->tot_st_cap; tot_st_flow = pBNS->tot_st_flow; /*****************************************/ /* initialize new fictitious vertices */ /* representing c-point groups, c-groups */ /*****************************************/ vert_ficpoint_prev = pBNS->vert + fst_cg_vertex - 1; for ( i = 0; i < num_cg; i ++ ) { /* vert_ficpoint-1 is the last vertex; vert_ficpoint is the being added vertex Note: nGroupNumber are not contiguous */ vert_ficpoint = vert_ficpoint_prev + 1; vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i+fst_cg_group].num_edges+nMaxAddEdges; vert_ficpoint->num_adj_edges = 0; vert_ficpoint->st_edge.flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; tot_st_flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; vert_ficpoint->st_edge.cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; tot_st_cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; vert_ficpoint->type = pTCGroups->pTCG[i+fst_cg_group].type; /* save the vertex number */ pTCGroups->pTCG[i+fst_cg_group].nVertexNumber = vert_ficpoint - pBNS->vert; vert_ficpoint_prev = vert_ficpoint; /* keep track of iedges */ } cur_num_vertices = (vert_ficpoint_prev - pBNS->vert) + 1; cur_num_edges = num_edges; /*************************************************************/ /* pass 1: */ /* create ChargeStruct for c-points and connect them to */ /* the vertices representing c-point groups; */ /* set final atom st_cap, st_flow */ /*************************************************************/ for ( c_point = 0; c_point < num_atoms; c_point ++ ) { if ( !(k=pVA[c_point].cnListIndex) ) continue; /* not a c-point */ k --; pCN = cnList[k].pCN; /* pointer to the ChargeStruct */ cn_len = cnList[k].len; /* length of the ChargeStruct */ cn_bits = cnList[k].bits; /* bits: for M-recognition */ /* cn_bits = cnList[k].bits; */ /* ChargeStruct type */ bMetalAtoms = (cn_bits == cn_bits_Me); vert_ficpoint_base = vert_ficpoint_prev; /* add aux vertices after this */ /* create disconnected auxiliary vertices of the at[c_point] ChargeStruct; add to them st_flow & st_cap */ for ( i1 = 0; i1 < cn_len; i1 ++ ) { if ( !IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { continue; } /* the atom is always the first; the attached c-points are always the last */ vert_ficpoint = vert_ficpoint_base + i1; /* i1 = 1, 2,.. less number of attached c-points */ vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; vert_ficpoint->max_adj_edges = pCN[i1].v.valence; /* do not add additional edges to aux vertices */ vert_ficpoint->num_adj_edges = 0; cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; AddStCapFlow( vert_ficpoint, &tot_st_flow, &tot_st_cap, cap, flow ); vert_ficpoint->type = pCN[i1].v.type; /* =BNS_VERT_TYPE__AUX */ vert_ficpoint_prev = vert_ficpoint; /* the last one will be vert_ficpoint for the next c-point */ cur_num_vertices = (vert_ficpoint - pBNS->vert) + 1; if ( vert_ficpoint->iedge + vert_ficpoint->max_adj_edges - pBNS->iedge >= pBNS->max_iedges ) { return BNS_VERT_EDGE_OVFL; } if ( cur_num_vertices >= pBNS->max_vertices ) { return BNS_VERT_EDGE_OVFL; } } /* connect the vertices with new edges, add edge flow and cap */ for ( i1 = 0; i1 < cn_len; i1 ++ ) { pv1 = NULL; k1 = -1; /* find vertex cooresponding to i1 */ if ( pCN[i1].v.type & BNS_VERT_TYPE_ATOM ) { pv1 = pBNS->vert+c_point; /* may be only one atom -- the current c_point at i1==0 */ /* add atom vertex st_cap and st_flow */ cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; AddStCapFlow( pv1, &tot_st_flow, &tot_st_cap, cap, flow ); } else if ( IS_BNS_VT_C_GR(pCN[i1].v.type) ) { /* find c-group vertex by looking for its type */ for( j = 0; j < num_cg; j ++ ) { if ( pCN[i1].v.type == pBNS->vert[fst_cg_vertex + j].type ) { pv1 = pBNS->vert + fst_cg_vertex + j; break; } } /* index of the pTCGroups->pTCG[] */ if ( pv1 ) { k1 = j + fst_cg_group; if ( pTCGroups->pTCG[k1].type != pCN[i1].v.type || pTCGroups->pTCG[k1].ord_num ) { return RI_ERR_PROGR; } } } else if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) ) { k1 = pTCGroups->nGroup[TCG_MeFlower0]; if ( k1 < 0 || pTCGroups->pTCG[k1].type != pCN[i1].v.type || pTCGroups->pTCG[k1].ord_num || !pSrm->bMetalAddFlower ) { return RI_ERR_PROGR; } pv1 = pBNS->vert + pTCGroups->pTCG[k1].nVertexNumber; } else if ( IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { /* aux vertex */ pv1 = vert_ficpoint_base + i1; } if ( !pv1 ) { return BNS_BOND_ERR; } /* connect pairs of vertices with new edges */ for ( k = 0; k < MAX_CN_VAL && (i2=pCN[i1].e[k].neigh); k ++ ) { pv2 = NULL; k2 = -1; i2 --; /* neighbor */ /* find vertex cooresponding to i2 */ if ( pCN[i2].v.type & BNS_VERT_TYPE_ATOM ) { pv2 = pBNS->vert+c_point; cap = !bMetalAtoms? pCN[i2].v.cap : pCN[i2].v.cap? pSrm->nMetalMaxCharge_D : 0; flow = !bMetalAtoms? pCN[i2].v.flow : pCN[i2].v.flow? pSrm->nMetalMaxCharge_D : 0; /* add atom vertex st_cap and st_flow; this normally should not happen */ AddStCapFlow( pv2, &tot_st_flow, &tot_st_cap, cap, flow ); } else if ( IS_BNS_VT_C_GR(pCN[i2].v.type) ) { /* find c-group vertex by looking for its type */ for( j = 0; j < num_cg; j ++ ) { if ( pCN[i2].v.type == pBNS->vert[fst_cg_vertex + j].type ) { pv2 = pBNS->vert + fst_cg_vertex + j; break; } } if ( pv2 ) { k2 = j + fst_cg_group; if ( pTCGroups->pTCG[k2].type != pCN[i2].v.type || pTCGroups->pTCG[k2].ord_num ) { return RI_ERR_PROGR; } } } else if ( IS_BNS_VT_M_GR( pCN[i2].v.type ) ) { k2 = pTCGroups->nGroup[TCG_MeFlower0]; if ( k2 < 0 || pTCGroups->pTCG[k2].type != pCN[i2].v.type || pTCGroups->pTCG[k2].ord_num || !pSrm->bMetalAddFlower ) { return RI_ERR_PROGR; } pv2 = pBNS->vert + pTCGroups->pTCG[k2].nVertexNumber; } else if ( IS_BNS_VT_CHRG_STRUCT(pCN[i2].v.type) ){ pv2 = vert_ficpoint_base + i2; } /* connect pv1 and pv2 */ if ( !pv1 || !pv2 || pv1 == pv2 ) { return BNS_BOND_ERR; } j1 = pv1 - pBNS->vert; j2 = pv2 - pBNS->vert; /* create a new edge connecting pv1 and pv2 */ edge = pBNS->edge + cur_num_edges; if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) && IS_BNS_VT_ATOM( pCN[i2].v.type ) || IS_BNS_VT_M_GR( pCN[i2].v.type ) && IS_BNS_VT_ATOM( pCN[i1].v.type ) ) { /* at[c_point] is a metal or is treated as a metal; connect it to M-group */ /* metal - M-group (i.e. Metal-Flower) edge */ int nStCap, nStFlow; bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, c_point, &nStCap, &nStFlow, &edge->cap, &edge->flow ); /* GetAtomToMCGroupInitEdgeCapFlow( &edge->cap, &edge->flow, pSrm, at, pVA, c_point ); */ if ( !bNeedsFlower ) { return RI_ERR_PROGR; } pVA[c_point].nMetalGroupEdge = cur_num_edges + 1; /* pBNS->vert[c_point].st_edge.cap += edge->flow;*/ /* where was this done ???*/ pBNS->vert[c_point].st_edge.flow += edge->flow; pBNS->vert[c_point].st_edge.cap += edge->flow + pVA[c_point].cInitFreeValences; pBNS->vert[c_point].st_edge.flow0 = pBNS->vert[c_point].st_edge.flow; pBNS->vert[c_point].st_edge.cap0 = pBNS->vert[c_point].st_edge.cap; tot_st_flow += edge->flow; tot_st_cap += edge->flow + pVA[c_point].cInitFreeValences; } else { edge->cap = !bMetalAtoms? pCN[i1].e[k].cap : pCN[i1].e[k].cap? pSrm->nMetalMaxCharge_D : 0; edge->flow = !bMetalAtoms? pCN[i1].e[k].flow : pCN[i1].e[k].flow? pSrm->nMetalMaxCharge_D : 0; } edge->forbidden = pCN[i1].e[k].bForbiddenEdge? BNS_EDGE_FORBIDDEN_MASK : 0; /* c-group incoming edges cap and flow needed in ConnectSuperCGroup() */ /* if ( k1 >= 0 ) { pTCGroups->pTCG[k1].edges_cap += pCN[i1].e[k].cap; pTCGroups->pTCG[k1].edges_flow += pCN[i1].e[k].flow; } if ( k2 >= 0 ) { pTCGroups->pTCG[k2].edges_cap += pCN[i1].e[k].cap; pTCGroups->pTCG[k2].edges_flow += pCN[i1].e[k].flow; } */ edge->pass = 0; #if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) edge->forbidden &= pBNS->edge_forbidden_mask; #endif /* check edge overflow */ if ( pv1->num_adj_edges >= pv1->max_adj_edges || pv2->num_adj_edges >= pv2->max_adj_edges || cur_num_edges >= pBNS->max_edges ) { return BNS_VERT_EDGE_OVFL; } /* connect edge to the incident vertices and increment the counters of neighbors and edges */ ret = ConnectTwoVertices( pv1, pv2, edge, pBNS, 0 ); if ( IS_BNS_ERROR( ret ) ) { return ret; } edge->cap0 = edge->cap; edge->flow0 = edge->flow; /* save the edge index */ type = IS_BNS_VT_C_GR(pv1->type)? pv1->type : IS_BNS_VT_C_GR(pv2->type)? pv2->type : 0; if ( type ) { /* the edge connects to a c-group */ if ( type & BNS_VERT_TYPE_C_NEGATIVE ) { pVA[c_point].nCMinusGroupEdge = cur_num_edges+1; } else { pVA[c_point].nCPlusGroupEdge = cur_num_edges+1; } } cur_num_edges ++; /* end of new edge creation */ } } } /*************************************************************/ /* pass 2: */ /* adjust bond cap, flow from the final atom st_cap, st_flow */ /*************************************************************/ for ( c_point = 0; c_point < num_atoms; c_point ++ ) { int st_cap, st_cap2, max_edge_flow; pv1 = pBNS->vert + c_point; /* atom vertex */ st_cap = pv1->st_edge.cap; for ( k = 0; k < pv1->num_adj_edges; k ++ ) { edge = pBNS->edge + pv1->iedge[k]; /* incident edge */ c_neigh = edge->neighbor12 ^ c_point; /* adjacent vertex */ pv2 = pBNS->vert + c_neigh; if ( c_neigh > c_point || !(pv2->type & BNS_VERT_TYPE_ATOM) ) { continue; } /* adjacent vertex is an atom; the edge is a bond; process each bond only once */ st_cap2 = pv2->st_edge.cap; /* the edge flow <= min( incident atom st_caps) */ max_edge_flow = inchi_min( st_cap, st_cap2 ); /* bond order <= triple bond (flow=2) */ if ( pSrm->bMetalAddFlower && !pSrm->nMetalMinBondOrder && (pVA[c_point].cMetal && pVA[c_point].cNumBondsToMetal || pVA[c_neigh].cMetal && pVA[c_neigh].cNumBondsToMetal) ) { max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP+1 ); } else { max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP ); } if ( at[c_point].bond_type[k] == BOND_TYPE_SINGLE ) { /* the bond has not been changed due to stereo */ edge->cap = edge->cap0 = max_edge_flow; } } } /***********************************************************/ /************** ************/ /************** connect M-flower with new edges ************/ /************** ************/ /***********************************************************/ ret = ConnectMetalFlower(&cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups); if ( ret < 0 ) { goto exit_function; } /***********************************************************/ /************** ************/ /************** add additional vertices & edges ************/ /************** to connect c-groups ************/ /************** ************/ /***********************************************************/ /* (+) supergroup, Y-connection */ k = 0; nAddGroups[k ++] = TCG_Plus0; nAddGroups[k ++] = TCG_Plus_C0; nAddGroups[k ++] = TCG_Plus_M0; ret1 = ConnectSuperCGroup( TCG_Plus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); /* (-) supergroup, Y-connection */ k = 0; nAddGroups[k ++] = TCG_Minus0; nAddGroups[k ++] = TCG_Minus_C0; nAddGroups[k ++] = TCG_Minus_M0; ret2 = ConnectSuperCGroup( TCG_Minus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); /******** connect (+) and (-) ***************/ k = 0; nAddGroups[k ++] = TCG_Plus; nAddGroups[k ++] = TCG_Minus; ret3 = ConnectSuperCGroup( -1, nAddGroups, k, &cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); /* Take care of the full charge */ cg_charge = pTCGroups->total_charge - pTCGroups->tgroup_charge - pTCGroups->charge_on_atoms; ret = 1; if ( ret3 > 0 ) { /* (+) and (-) or at least one of them have been connected */ int nVertPlusMinus = cur_num_vertices - 1; BNS_VERTEX *pVertPlusMinus = pBNS->vert + nVertPlusMinus; BNS_VERTEX *pVertPlus = NULL, *pVertMinus = NULL, *pVert=NULL; BNS_EDGE *pEdgePlus = NULL, *pEdgeMinus = NULL, *pEdge=NULL; n = pTCGroups->nGroup[TCG_Plus] >= 0; /* (+)-supergroup exists */ k = pTCGroups->nGroup[TCG_Minus] >= 0; /* (-)-supergroup exists */ if ( pVertPlusMinus->num_adj_edges == 2 && k+n==2 ) { pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; /* TCG_Plus was the 1st */ pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[1]; /* TCG_Minus was the 2nd */ } else if ( pVertPlusMinus->num_adj_edges == 1 && k+n==1 ) { if ( pTCGroups->nGroup[TCG_Plus] >= 0 ) { pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; } else if ( pTCGroups->nGroup[TCG_Minus] >= 0 ) { pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[0]; } } else if ( k+n ) { /* program error check */ ret = BNS_BOND_ERR; goto exit_function; } if ( pEdgePlus ) { pVertPlus = pBNS->vert + (pEdgePlus->neighbor12 ^ nVertPlusMinus); } if ( pEdgeMinus ) { pVertMinus = pBNS->vert + (pEdgeMinus->neighbor12 ^ nVertPlusMinus); } pVert = pVertPlus? pVertPlus : pVertMinus? pVertMinus : NULL; pEdge = pEdgePlus? pEdgePlus : pEdgeMinus? pEdgeMinus : NULL; if ( pEdgeMinus ) { pTCGroups->nEdgeMinus = pEdgeMinus - pBNS->edge; } if ( pEdgePlus ) { pTCGroups->nEdgePlus = pEdgePlus - pBNS->edge; } if ( pEdge ) { pTCGroups->nEdge4charge = pEdge - pBNS->edge; } /* set total charge */ if ( pVert && pEdge ) { /* do not check rescaps for now */ if ( cg_charge > 0 ) { pVertPlusMinus->st_edge.cap += cg_charge; tot_st_cap += cg_charge; pVertPlusMinus->st_edge.cap0 = pVertPlusMinus->st_edge.cap; } if ( cg_charge < 0 ) { pVert->st_edge.cap -= cg_charge; tot_st_cap -= cg_charge; pVert->st_edge.cap0 = pVert->st_edge.cap; if ( pEdge->cap - pEdge->flow + cg_charge < 0 ) { /* 2006-02-06: increase edge capacity to avoid clogging */ pEdge->cap = pEdge->flow - cg_charge; } } pTCGroups->added_charge = cg_charge; } if ( !cg_charge || (pVert && pEdge) ) { ret = 2; } } AddRadicalToMetal( &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups ); pBNS->num_edges = cur_num_edges; pBNS->num_vertices = cur_num_vertices; pBNS->num_c_groups = num_cg; pBNS->tot_st_cap = tot_st_cap; pBNS->tot_st_flow = tot_st_flow; } exit_function: return ret; } /********************************************************************************/ int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ) { int i, j, n, num_edges, v1 = v+1; for ( i = 0, num_edges = 0; i < len; i ++ ) { for ( j = 0; j < MAX_CN_VAL && (n = pCN[i].e[j].neigh); j ++ ) { num_edges += ( i == v || n == v1 ); } } return num_edges; } /********************************************************************************* BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ) allocate BN_STRUCT that has: pBNS->max_vertices = pTCGroups->nVertices + nMaxAddAtoms pBNS->max_edges = pTCGroups->nEdges + pBNS->max_vertices * (nMaxAddEdges + NUM_KINDS_OF_GROUPS) pBNS->max_iedges = 2*pBNS->max_edges + pTCGroups->nAddIedges pBNS->len_alt_path = pBNS->max_vertices + iALTP_HDR_LEN + 1 + max( pBNS->max_vertices/2, 16 ) pBNS->max_altp = max_altp other members: pBNS->num_atoms = num_atoms; pBNS->num_bonds = num_bonds; pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; pBNS->num_c_groups = 0; pBNS->nMaxAddAtoms = nMaxAddAtoms; pBNS->nMaxAddEdges = nMaxAddEdges; atom vertices and bond edges: --- vertex(atom) --- st_cap = (at[].chem_bonds_valence - at[].valence) + pVA[].cInitFreeValences st_flow = SUM{bond_orders; ALT_BOND counted as SINGLE} - at[].valence --- edge(bond) --- flow = bond_order - 1; for ALT_BOND flow = 0 cap = min(min(st_cap of neighbors),2); for ALT_BOND cap = 1 max number of edges per atom = number of bonds + number of edges to ChargeStruct + 1 (if atom is a tautomeric endpoint) + nMaxAddEdges --- NOTE --- Here are not included nDelta(dots) from ChargeStruct and flow to ChargeStruct *********************************************************************************/ BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ) { inp_ATOM *at = pStruct->at; int num_atoms = pStruct->num_atoms; ICHICONST SRM *pSrm = pStruct->pSrm; BN_STRUCT *pBNS = NULL; BNS_VERTEX *vert; BNS_IEDGE *iedge; int neigh, num_changed_bonds=0; U_CHAR bond_type, bond_mark; int bNeedsFlower1, bNeedsFlower2, min_order; int i, j, k, m, n_edges, num_bonds, num_edges; int f1, f2, c1, c2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; int tot_st_cap, tot_st_flow; int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_iedges, num_altp; /* count vertices */ max_tg = pTCGroups->num_tgroups; /* +1 for a super-tautomeric group */ /* max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; */ max_vertices = pTCGroups->nVertices + nMaxAddAtoms; /* count edges */ num_changed_bonds = 0; num_bonds = pTCGroups->num_bonds; /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ num_edges = pTCGroups->nEdges; /* +max_tg for edges between t-groups and super-tautomeric group */ max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices; max_iedges = 2*max_edges + pTCGroups->nAddIedges; len_alt_path = max_vertices+iALTP_HDR_LEN + 1; /* may overflow if an edge is traversed in 2 directions */ len_alt_path += inchi_max( max_vertices/2, 16 ); /* to avoid the overflow */ if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { return DeAllocateBnStruct( pBNS ); } /* alt path init (standard spell) */ for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { return DeAllocateBnStruct( pBNS ); } ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ /* re-init */ ALTP_DELTA(pBNS->altp[num_altp]) = 0; ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; } pBNS->alt_path = NULL; pBNS->num_altp = 0; pBNS->max_altp = num_altp; /* fill vertices (no connectivity) */ iedge = pBNS->iedge; num_iedges = 0; tot_st_cap = tot_st_flow = 0; for ( i = 0; i < num_atoms; i ++ ) { /* count edges incident to pBNS->vert[i] */ k = at[i].valence + (at[i].endpoint != 0) + (nMaxAddEdges /*+ NUM_KINDS_OF_GROUPS*/); if ( (j = pVA[i].cnListIndex-1) >= 0 ) { /* add number of neighbors in the ChargeStruct */ k += nNumEdgesToCnVertex( cnList[j].pCN, cnList[j].len, 0 ); } /* set max number of edges for the vertex */ pBNS->vert[i].max_adj_edges = k; pBNS->vert[i].iedge = iedge; iedge += k; /* add atom vertex cap */ st_cap = 0; st_flow = 0; bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); /* pVA[i].cNumBondsToMetal = bNeedsFlower1; */ /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ st_cap += c1; st_cap += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; pBNS->vert[i].st_edge.cap = st_cap; /* the 1st time st_cap is set */ pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; tot_st_cap += st_cap; } num_iedges = iedge - pBNS->iedge; if ( max_iedges - num_iedges < (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices ) { return DeAllocateBnStruct( pBNS ); } pBNS->num_atoms = num_atoms; /* number of real atoms */ pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; /* number of added t-groups */ pBNS->num_c_groups = 0; pBNS->nMaxAddAtoms = nMaxAddAtoms; pBNS->nMaxAddEdges = nMaxAddEdges; pBNS->num_vertices = num_atoms; /* current number of vertices, in general a sum of pBNS->num_atoms pBNS->num_t_groups number of c-groups number of auxiliary vertices pBNS->num_added_atoms */ pBNS->max_vertices = max_vertices; pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ pBNS->max_edges = max_edges; pBNS->max_iedges = max_iedges; /* To remove t-groups and added atoms: for ( i = 0; i < pBNS->num_atoms; i ++ ) { for ( j = pBNS->vert[i].num_adj_edges-1; 0 <= j; j -- ) { k = pBNS->edge[pBNS->vert[i].iedge[j]].neighbor12 ^ i; if ( pBNS->vert[k].type & BNS_VERT_TYPE_ATOM ) { pBNS->vert[i].num_adj_edges = j+1; break; } } } pBNS->num_vertices = pBNS->num_atoms; pBNS->num_edges = pBNS->num_bonds; pBNS->num_added_atoms = 0; pBNS->num_t_groups = 0; pBNS->num_added_edges = 0; ALTP_DELTA(pBNS->alt_path) = 0; ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; ALTP_PATH_LEN(pBNS->alt_path) = 0; */ /* add and fill edges and connectivity */ for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { vert = pBNS->vert + i; /* pointer to the ith vertex */ st_cap = 0; st_flow = 0; flag_alt_bond = 0; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; /* find this bond at the neighbor */ for ( k = 0; k < at[neigh].valence; k ++ ) { if ( at[neigh].neighbor[k] == i ) { break; } } bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE ) { /* make unknown bonds single */ bond_type = BOND_SINGLE; at[i].bond_type[j] = bond_mark | bond_type; num_changed_bonds ++; } if ( neigh > i ) { /* this is the first time we encounter this bond */ bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ c1 += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ bNeedsFlower2 = AtomStcapStflow( at, pVA, pSrm, neigh, &c2, &f2, NULL, NULL ); /* GetAtomStCapFlow( at, pVA, pSrm, neigh, &c2, &f2 ); */ c2 += bNeedsFlower2? 0 : pVA[neigh].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ /* at this point -O would have st_cap=st_flow=0 because the lowest valence=1 for charge=-1 */ /* however, if -O belongs to a t-group its cap would be 1, flow = 0 */ /*f1 = MAX_AT_FLOW(at[i]);*/ /*f2 = MAX_AT_FLOW(at[neigh]);*/ edge_flow = BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, &edge_cap, &min_order, NULL); pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); pBNS->edge[n_edges].flow = pBNS->edge[n_edges].flow0 = edge_flow; pBNS->edge[n_edges].cap = pBNS->edge[n_edges].cap0 = edge_cap; pBNS->edge[n_edges].neigh_ord[0] = j; /* iedge to neigh index at vertex[i], i < neigh */ pBNS->edge[n_edges].neigh_ord[1] = k; /* iedge to i index at vertex[neigh], i < neigh */ pBNS->edge[n_edges].pass = 0; pBNS->edge[n_edges].forbidden = 0; /* may be forbidden if edge_flow = 1: stereogenic fixed double bond */ if ( bond_type == BOND_TYPE_DOUBLE ) { /* forbid changing stereogenic double bonds */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { if ( at[i].sb_ord[m] == j ) { pBNS->edge[n_edges].forbidden |= BNS_EDGE_FORBIDDEN_MASK; break; } } } vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; /* same iedge index as neighbor index in at[] */ } else { /* this is the second time we encounter this bond. It was stored at */ int iedge2 = pBNS->vert[neigh].iedge[k]; edge_cap = pBNS->edge[iedge2].cap; edge_flow = pBNS->edge[iedge2].flow; } st_flow += edge_flow; /* st_cap += edge_cap; */ } vert->num_adj_edges = j; /* vert->st_edge.cap = vert->st_edge.cap0 = st_cap; */ vert->st_edge.flow = vert->st_edge.flow0 = st_flow; vert->type = BNS_VERT_TYPE_ATOM; /* tot_st_cap += vert->st_edge.cap; */ tot_st_flow += vert->st_edge.flow; } *pNum_changed_bonds = num_changed_bonds/2; pBNS->num_edges = n_edges; /* number of edges */ pBNS->num_iedges = num_iedges; pBNS->num_added_edges = 0; pBNS->tot_st_cap = tot_st_cap; pBNS->tot_st_flow = tot_st_flow; /* exit_function: */ return pBNS; } /******************************************************************************************************/ void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent ) { int i, j; for ( i = 0; i < num_at; i ++ ) { at[i].endpoint = 0; at[i].component = iComponent; for ( j = 0; j < at[i].valence; j ++ ) { if ( !at[i].bond_type[j] ) { at[i].bond_type[j] = BOND_TYPE_SINGLE; at[i].chem_bonds_valence += BOND_TYPE_SINGLE; } } } } void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ) { int i, j; for ( i = 0; i < num_at; i ++ ) { at[i].component = iComponent; for ( j = 0; j < at[i].valence; j ++ ) { if ( !at[i].bond_type[j] ) { at[i].bond_type[j] = BOND_TYPE_SINGLE; at[i].chem_bonds_valence += BOND_TYPE_SINGLE; } } } } void ClearEndpts(inp_ATOM *at, int num_at ) { int i; for ( i = 0; i < num_at; i ++ ) { at[i].endpoint = 0; } } /******************************************************************************************************/ #define ANY_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) #define GRP_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) typedef struct tagVertexFlow { int type; Vertex v; EdgeIndex e_In; EdgeIndex e_Out; EdgeFlow delta_In; EdgeFlow delta_Out; Vertex bUsed; /* indicates the charge edge belongs to already processed atom */ } VF; #define NUM_VF 3 #define VF_USED_IN 1 #define VF_USED_OUT 2 #define VF_USED_ALL (VF_USED_IN | VF_USED_OUT) int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ); /******************************************************************************************************/ int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ) { int i, v = NO_VERTEX; int ieIn1 = (!(vf->bUsed & VF_USED_IN) && vf->e_In >= 0 && vf->delta_In )? vf->e_In+1 : NO_VERTEX; int ieOut1 = (!(vf->bUsed & VF_USED_OUT) && vf->e_Out >= 0 && vf->delta_Out)? vf->e_Out+1 : NO_VERTEX; int nInitCharge, nPlusFlow, nMinusFlow, nDeltaCharge, nNumDeltaCharge, eCPlus, eCMinus; if ( !(vf->type & BNS_VERT_TYPE_C_GROUP) || (vf->type & BNS_VERT_TYPE_SUPER_TGROUP) || (ieIn1 == NO_VERTEX && ieOut1 == NO_VERTEX ) ) { return 0; } if ( vf->type & BNS_VERT_TYPE_C_NEGATIVE ) { /* negative charge edge */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { if ( pVA[i].nCMinusGroupEdge == ieIn1 || pVA[i].nCMinusGroupEdge == ieOut1 ) { v = i; break; } } } else { /* positive charge edge */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { if ( pVA[i].nCPlusGroupEdge == ieIn1 || pVA[i].nCPlusGroupEdge == ieOut1 ) { v = i; break; } } } if ( v == NO_VERTEX ) return 0; nInitCharge = pVA[v].cInitCharge; nPlusFlow = nMinusFlow = 0; nNumDeltaCharge = 0; if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { nPlusFlow = pBNS->edge[eCPlus].cap - pBNS->edge[eCPlus].flow; } if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { nMinusFlow = -pBNS->edge[eCMinus].flow; } nInitCharge += nPlusFlow + nMinusFlow; nDeltaCharge = 0; if ( !(vf[0].bUsed & VF_USED_OUT) ) { if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { nDeltaCharge -= vf[0].delta_Out; vf[0].bUsed |= VF_USED_OUT; } } if ( !(vf[0].bUsed & VF_USED_IN) ) { if ( vf[0].e_In==eCPlus || vf[0].e_In==eCMinus ) { nDeltaCharge -= vf[0].delta_In; vf[0].bUsed |= VF_USED_IN; } } if ( !nInitCharge && nDeltaCharge ) { nNumDeltaCharge ++; } else if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { nNumDeltaCharge --; } return nNumDeltaCharge; } /******************************************************************************************************/ int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) { int pass, i, j, v0, v1, v2, v, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, ie, err = 0; BNS_EDGE *edge; int nDeltaH, nDeltaCharge, iPrev, nInitCharge, nPlusFlow, nMinusFlow; int nNumDeltaH = 0; int nNumDeltaCharge = 0; int nNumVisitedAtoms = 0; VF vf[NUM_VF+1]; *pnDeltaH = 0; *pnDeltaCharge = 0; *pnNumVisitedAtoms = 0; for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { pBNS->alt_path = pBNS->altp[pass]; v1 = ALTP_START_ATOM(pBNS->alt_path); n = ALTP_PATH_LEN(pBNS->alt_path); delta = ALTP_DELTA(pBNS->alt_path); vLast = ALTP_END_ATOM(pBNS->alt_path); v0 = v2 = NO_VERTEX; memset( vf, 0, sizeof(vf) ); for ( i = 0; i < (int)(sizeof(vf)/sizeof(vf[0])); i ++ ) { vf[i].v = NO_VERTEX; /* = -2 */ vf[i].e_In = NO_VERTEX; vf[i].e_Out = NO_VERTEX; } iPrev = 0; /* add to the queue */ if ( ANY_VERT_TYPE(pBNS->vert[v1].type) ) { if (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM) { nNumVisitedAtoms ++; } vf[2].type = pBNS->vert[v1].type; vf[2].v = v1; iPrev = 2; } nNumDeltaH = 0; nNumDeltaCharge = 0; nNumVisitedAtoms = 0; for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ edge = pBNS->edge + (ie=pBNS->vert[v1].iedge[ineigh1]); /* follow the BN Structure, not the inp_ATOM, to take care of swithching to t-groups, c-groups or other fictitious edges/vertices */ if ( iPrev ) { /* add exit delta and edge */ vf[2].e_Out = ie; vf[2].delta_Out = delta; } v2 = edge->neighbor12 ^ v1; /* next vertex */ if (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM) { nNumVisitedAtoms ++; } if ( (ANY_VERT_TYPE(pBNS->vert[v2].type) || i == n-1) && (vf[0].type & BNS_VERT_TYPE_C_GROUP) && vf[0].bUsed != VF_USED_ALL ) { /* unused vertex is about to be discarded */ nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[0] ); } if ( ANY_VERT_TYPE(pBNS->vert[v2].type) ) { /* shift the queue */ vf[0] = vf[1]; vf[1] = vf[2]; vf[2] = vf[3]; /* make vf[2] empty */ /* add next vertex */ vf[2].v = v2; vf[2].type = pBNS->vert[v2].type; vf[2].e_In = ie; vf[2].delta_In = delta; iPrev = 2; /* indicates a newly added vertex */ } else if ( i == n-1 ) { /* shift the queue */ vf[0] = vf[1]; vf[1] = vf[2]; vf[2] = vf[3]; /* make vf[2] empty */ iPrev = 1; /* indicates the last vertex */ } else { iPrev = 0; /* no new vertex has been added */ } if ( iPrev && (vf[1].type & BNS_VERT_TYPE_ATOM)) { /* a new vertex has just been added and */ /* an atom is in the middle of the queue */ EdgeIndex eCPlus, eCMinus; v = vf[1].v; nInitCharge = pVA[v].cInitCharge; nPlusFlow = nMinusFlow = 0; if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { nPlusFlow = pBNS->edge[eCPlus].cap - pBNS->edge[eCPlus].flow; } if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { nMinusFlow = -pBNS->edge[eCMinus].flow; } nInitCharge += nPlusFlow + nMinusFlow; nDeltaH = nDeltaCharge = 0; if ( vf[0].type & BNS_VERT_TYPE_TGROUP ) { nDeltaH -= delta; } else if ( (vf[0].type & BNS_VERT_TYPE_C_GROUP) && !(vf[0].bUsed & VF_USED_OUT) ) { if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { nDeltaCharge -= vf[0].delta_Out; vf[0].bUsed |= VF_USED_OUT; } } if ( vf[2].type & BNS_VERT_TYPE_TGROUP ) { nDeltaH += delta; } else if ( (vf[2].type & BNS_VERT_TYPE_C_GROUP) && !(vf[2].bUsed & VF_USED_IN) ) { if ( vf[2].e_In==eCPlus || vf[2].e_In==eCMinus ) { nDeltaCharge -= vf[2].delta_In; vf[2].bUsed |= VF_USED_IN; } } if ( !nInitCharge && nDeltaCharge ) { nNumDeltaCharge ++; } else if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { nNumDeltaCharge --; } nNumDeltaH += abs(nDeltaH); /* nNumDeltaCharge += abs(nDeltaCharge); */ vf[1].bUsed = VF_USED_ALL; } } for ( j = 0; j < 3; j ++ ) { nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[j] ); } *pnDeltaH += nNumDeltaH; *pnDeltaCharge += nNumDeltaCharge; *pnNumVisitedAtoms += nNumVisitedAtoms; if ( v2 != vLast ) { err = BNS_PROGRAM_ERR; } } return err? err : ret; } /******************************************************************************************************/ int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) { int bChangeFlow = 0; /* do not change flow */ int delta, ret, ret2, pass; ReInitBnStructAltPaths( pBNS ); pass = 0; pBNS->alt_path = pBNS->altp[pass]; pBNS->num_altp = 0; pBNS->bChangeFlow = 0; delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); if ( delta > 0 ) { pBNS->alt_path = pBNS->altp[pass]; *pvFirst = ALTP_START_ATOM(pBNS->alt_path); *pPathLen = ALTP_PATH_LEN(pBNS->alt_path); *pvLast = ALTP_END_ATOM(pBNS->alt_path); pBNS->num_altp ++; ret2 = EvaluateChargeChanges( pBNS, pVA, pnDeltaH, pnDeltaCharge, pnNumVisitedAtoms ); } else { *pvFirst = NO_VERTEX; *pPathLen = 0; *pvLast = NO_VERTEX; ret2 = 0; } ReInitBnStructAltPaths( pBNS ); ret = ReInitBnData( pBD ); return (delta >= 0 && ret > 0 )? -ret : delta; } /******************************************************************************************************/ int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) { /* run BNS for the first time */ int nTotalDelta = 0, ret = 0; int nDelta; ReInitBnStructAltPaths( pBNS ); do { nDelta = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); if ( IS_BNS_ERROR(nDelta) ) { ret = nDelta; goto exit_function; } nTotalDelta += nDelta; ReInitBnStructAltPaths( pBNS ); ret = ReInitBnData( pBD ); if ( ret > 0 ) { ret = -ret; goto exit_function; } } while( nDelta > 0 && ret == 0 ); pBNS->tot_st_flow += 2*nTotalDelta; ret = nTotalDelta; exit_function: return ret; } /******************************************************************************************************/ int comp_cc_cand( const void *a1, const void *a2 ) { const CC_CAND *p1 = (const CC_CAND *) a1; const CC_CAND *p2 = (const CC_CAND *) a2; int ret; if ( ret = (int)p2->cMetal - (int)p1->cMetal ) return ret; /* metal first */ if ( ret = (int)p2->cNumBondsToMetal - (int)p1->cNumBondsToMetal ) return ret; /* connected to metal first */ if ( ret = (int)p2->cPeriodicRowNumber - (int)p1->cPeriodicRowNumber ) return ret; /* heaviest first */ if ( ret = (int)p2->num_bonds - (int)p1->num_bonds ) return ret; /* more bonds first */ if ( ret = (int)p1->chem_valence - (int)p2->chem_valence ) return ret; /* less bond order first */ if ( !p1->cNumValenceElectrons && p2->cNumValenceElectrons ) return -1; /* no valence electrons first */ if ( !p2->cNumValenceElectrons && p1->cNumValenceElectrons ) return -1; /* no valence electrons first */ if ( (int)p2->cNumValenceElectrons - (int)p1->cNumValenceElectrons ) return ret; /* more valence electrons first */ ret = (int)p2->iat - (int)p1->iat; /* greater canon number first */ return ret; } /***************************************************************************************************** Locate E1=C-E2 where e ev are the edges E1 and E2 are atoms that belong to the same t-group C is an atom that does not belong to any t-group e is a forbidden edge ev is not a forbidden edge Make changes so that: E1(d)-C(d)-E2 where (d) means doublet radical */ /**************************************************************************************************/ int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ) { int type = 0, val; if ( pVA[iat].cNumValenceElectrons == 4 ) { if ( pVA[iat].cPeriodicRowNumber == 1 ) { type |= EL_TYPE_C; } } else if ( pVA[iat].cNumValenceElectrons == 6 ) { if ( pVA[iat].cPeriodicRowNumber == 1 ) { type |= EL_TYPE_O; } else if ( pVA[iat].cPeriodicRowNumber < 5 ) { type |= EL_TYPE_S; } if ( bond_type == BOND_TYPE_SINGLE && (type & (EL_TYPE_O | EL_TYPE_S)) && 1 == nNoMetalBondsValence(at, iat ) && 1 == nNoMetalNumBonds(at, iat) ) { type |= EL_TYPE_OSt; } } else if ( pVA[iat].cNumValenceElectrons == 5 ) { if ( pVA[iat].cPeriodicRowNumber == 1 ) { type |= EL_TYPE_N; } else { type |= EL_TYPE_P; } } else if ( !is_el_a_metal(pVA[iat].cPeriodicNumber) ) { type |= EL_TYPE_X; } /* check for possibility to be a tautomeric endpoint (that is, be a Mobile H site) */ val = get_endpoint_valence( at[iat].el_number ); if ( val && val > at[iat].valence && !at[iat].radical && -1 <= at[iat].charge && at[iat].charge <= 0 && val == at[iat].chem_bonds_valence - at[iat].charge + at[iat].num_H ) { type |= EL_TYPE_PT; } return type; } /*************************************************************************************/ int AllocEdgeList( EDGE_LIST *pEdges, int nLen ) { switch( nLen ) { case EDGE_LIST_FREE: if ( NULL != pEdges->pnEdges ) { inchi_free( pEdges->pnEdges ); } /* fall through */ case EDGE_LIST_CLEAR: memset( pEdges, 0, sizeof(*pEdges) ); break; default: if ( nLen > 0 && nLen != pEdges->num_alloc ) { EdgeIndex *tmp_edges = pEdges->pnEdges; int tmp_num = pEdges->num_edges; pEdges->pnEdges = (EdgeIndex *)inchi_calloc( nLen, sizeof(pEdges->pnEdges[0])); if ( !pEdges->pnEdges ) { return RI_ERR_ALLOC; } tmp_num = inchi_min( tmp_num, nLen ); if ( tmp_edges && tmp_num > 0 ) { memcpy( pEdges->pnEdges, tmp_edges, tmp_num * sizeof(pEdges->pnEdges[0]) ); pEdges->num_edges = tmp_num; } else { pEdges->num_edges = 0; } if ( tmp_edges ) { inchi_free( tmp_edges ); } pEdges->num_alloc = nLen; return 0; } break; } return 0; } /********************************************************************/ int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ) { if ( pEdges->num_alloc == pEdges->num_edges ) { int ret; if ( nAddLen <= 0 ) { return RI_ERR_PROGR; } if ( ret = AllocEdgeList( pEdges, pEdges->num_alloc + nAddLen ) ) { return ret; } } pEdges->pnEdges[pEdges->num_edges ++] = (EdgeIndex)iedge; return 0; } /********************************************************************/ int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ) { int len; if ( 0 <= (len = pEdges->num_edges - index - 1) ) { if ( len ) { memmove( pEdges->pnEdges+index, pEdges->pnEdges+index+1, len*sizeof(pEdges->pnEdges[0])); } pEdges->num_edges --; pEdges->pnEdges[pEdges->num_edges] = 0; return 0; } return -1; } /********************************************************************/ int FindInEdgeList( EDGE_LIST *pEdges, int iedge ) { int i; EdgeIndex ie = iedge; for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { if ( ie == pEdges->pnEdges[i] ) { return i; } } return -1; } /********************************************************************/ int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ) { int i, ret, n = 0; EdgeIndex ie = iedge; for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { if ( ie == pEdges->pnEdges[i] ) { if ( ret = RemoveFromEdgeListByIndex( pEdges, i ) ) { return ret; } n ++; } } return n; } /********************************************************************/ int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ) { int ret = 0; switch( num_at ) { case BFS_Q_FREE: if ( pQ->q ) { pQ->q = QueueDelete( pQ->q ); } if ( pQ->nAtomLevel ) { inchi_free( pQ->nAtomLevel ); } if ( pQ->cSource ) { inchi_free( pQ->cSource ); } /* fall through */ case BFS_Q_CLEAR: memset( pQ, 0, sizeof( *pQ ) ); return 0; default: if ( num_at <= 0 ) { ret = RI_ERR_PROGR; goto exit_function; } if ( num_at > pQ->num_at ) { if ( pQ->num_at ) { AllocBfsQueue( pQ, BFS_Q_FREE, 0 ); } pQ->q = QueueCreate( num_at+1, sizeof(qInt) ); pQ->nAtomLevel = (AT_RANK*)inchi_calloc( sizeof(pQ->nAtomLevel[0]), num_at ); pQ->cSource = (S_CHAR *)inchi_calloc( sizeof(pQ->cSource[0]), num_at ); if ( !pQ->q || !pQ->cSource || !pQ->nAtomLevel ) { ret = RI_ERR_ALLOC; goto exit_function; } pQ->num_at = num_at; } pQ->min_ring_size = min_ring_size; } exit_function: return ret; } /*************************************************************************************/ void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) { int i, mask = ~forbidden_edge_mask; for ( i = 0; i < pEdges->num_edges; i ++ ) { pBNS->edge[pEdges->pnEdges[i]].forbidden &= mask; } } /*************************************************************************************/ void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) { int i; for ( i = 0; i < pEdges->num_edges; i ++ ) { pBNS->edge[pEdges->pnEdges[i]].forbidden |= forbidden_edge_mask; } } /******************************************************************************************************/ void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ) { BNS_EDGE *e; int i; int inv_forbidden_edge_mask = ~forbidden_edge_mask_int; for ( i = 0, e = pBNS->edge; i < pBNS->num_bonds; i ++, e ++ ) { e->forbidden &= inv_forbidden_edge_mask; } } /****************************************************************************************************** upper vc edge / v1[i0]---v0 \ / \ / \ / v1[i1] | | atom */ int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ) { int ret = NO_VERTEX, i, j, k, i0, i1; Vertex v0, v1[3], vc, v_t, v; BNS_EDGE *pe, *pe1[3], *pe_t; BNS_VERTEX *pv0, *pv1[3], *pv_t; if ( nChargeEdge < 0 ) { goto exit_function; } pe = pBNS->edge + nChargeEdge; vc = pe->neighbor1; /* charge vertex */ if ( !IS_BNS_VT_C_GR(pBNS->vert[vc].type) ) { vc = vc ^ pe->neighbor12; } v0 = vc ^ pe->neighbor12; /* ChargeStruct vertex ? */ pv0 = pBNS->vert + v0; if ( IS_BNS_VT_ATOM(pv0->type) ) { goto exit_function; /* no charge flower exists */ } /* 2 edges from v0 */ for ( i = j = 0; i < pv0->num_adj_edges && j < 3; i ++ ) { pe1[j] = pBNS->edge + pv0->iedge[i]; if ( vc != ( v1[j] = pe1[j]->neighbor12 ^ v0 ) && (pv1[j] = pBNS->vert + v1[j], !IS_BNS_VT_ATOM(pv1[j]->type) && !IS_BNS_VT_C_GR(pv1[j]->type)) ) { j ++; } } if ( j != 2 || i != pv0->num_adj_edges ) { goto exit_function; } if ( pv1[1]->num_adj_edges == 2 && pv1[0]->num_adj_edges == 3 ) { i0 = 1; i1 = 0; } else if ( pv1[0]->num_adj_edges == 2 && pv1[1]->num_adj_edges == 3 ) { i0 = 0; i1 = 1; } else { goto exit_function; } /* additional check: traverse edges around v1[i1] */ pv_t = pv1[i1]; v_t = v1[i1]; for ( i = k = 0; i < pv_t->num_adj_edges; i ++ ) { pe_t = pBNS->edge + pv_t->iedge[i]; v = pe_t->neighbor12 ^ v_t; /* v1[i1] neighbor */ if ( v == v0 ) { k += 1; } if ( v == v1[i0] ) { k += 2; } if ( IS_BNS_VT_ATOM(pBNS->vert[v].type) ) { k += 4; } } if ( k != 7 ) { goto exit_function; } ret = pe1[i0] - pBNS->edge; exit_function: return ret; } #if (INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) /******************************************************************************************** input: allocate (num_at+num_deleted_H) atoms in inp_ATOM *at_norm, *at_fixed_bonds_out allocate t_group_info *********************************************************************************************/ int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ) { int i, ret, num_endpoints, nLenTaut; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; /* T_GROUP_INFO tgi; T_GROUP_INFO *t_group_info = &tgi; inp_ATOM *at_fixed_bonds_out = NULL; inp_ATOM *at_norm = NULL; at_norm = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_norm[0]) ); at_fixed_bonds_out = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_fixed_bonds_out[0]) ); if ( !at_norm || !at_fixed_bonds_out ) { if ( at_norm ) inchi_free( at_norm ); if ( at_fixed_bonds_out ) inchi_free( at_fixed_bonds_out ); ret = RI_ERR_ALLOC; goto exit_function; } */ /* call normalization only */ memset( t_group_info, 0, sizeof(t_group_info[0]) ); t_group_info->tni.nNumRemovedExplicitH = pStruct->num_deleted_H; t_group_info->bTautFlags = ip->bTautFlags; t_group_info->bTautFlagsDone = 0; /* (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret < 0 ) { goto exit_function; } #if ( FIND_RING_SYSTEMS == 1 ) ret = MarkRingSystemsInp( at2, num_at, 0 ); if ( ret < 0 ) { goto exit_function; } #endif memcpy( at_norm, at2, len_at * sizeof(at_norm[0]) ); for ( i = 0, num_endpoints = 0; i < num_at; i ++ ) { num_endpoints += (0 != at_norm[i].endpoint); at_norm[i].endpoint = 0; } ret = mark_alt_bonds_and_taut_groups ( at_norm, at_fixed_bonds_out, num_at, t_group_info, NULL /* &inpbTautFlags*/, NULL /*inpbTautFlagsDone*/ ); if ( ret < 0 ) { goto exit_function;/* out of RAM or other normalization problem */ } /* after normalization, t_group_info->t_group[i].num[0] = number of H + number of (-) */ /* t_group_info->t_group[i].num[1] = number of (-) */ /* --- count t-groups, remove (-)-only t-groups, replace -------------------------------*/ /* t_group_info->t_group[i].num[0] with */ /* t_group_info->t_group[i].num[0]-t_group_info->t_group[i].num[1] */ nLenTaut = CountTautomerGroupsInpAt( at_norm, num_at, t_group_info ); ret = nLenTaut; exit_function: return ret; } #endif /******************************************************************************************************/ int MakeOneInChIOutOfStrFromINChI2(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, BN_STRUCT *pBNS, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **t_group_info, inp_ATOM **at_norm, inp_ATOM **at_prep ) { int ret; INPUT_PARMS ip_loc, *ip; STRUCT_DATA sd_loc, *sd; ip_loc = *ip_inp; sd_loc = *sd_inp; ip = &ip_loc; sd = &sd_loc; memset( sd, 0, sizeof(*sd) ); /* create structure out of BNS */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret < 0 ) { goto exit_function;/* out of RAM or other normalization problem */ } pStruct->at = at; ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, at2, at3, pTCGroups ); if ( ret < 0 ) { goto exit_function;/* out of RAM or other normalization problem */ } if ( at_norm ) { *at_norm = pStruct->pOne_norm_data[0]->at; } if ( at_prep ) { if ( pStruct->pOne_norm_data[0]->bTautPreprocessed && pStruct->pOne_norm_data[0]->at_fixed_bonds ) { *at_prep = pStruct->pOne_norm_data[0]->at_fixed_bonds; } else /* get preprocessed structure in case of Fixed-H */ if ( pStruct->iMobileH == TAUT_NON && pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->bTautPreprocessed ) { *at_prep = pStruct->pOne_norm_data[1]->at_fixed_bonds; } else { *at_prep = NULL; } } if ( t_group_info ) { if ( pStruct->iMobileH == TAUT_YES && pStruct->One_ti.num_t_groups && pStruct->One_ti.t_group && pStruct->One_ti.nEndpointAtomNumber ) { *t_group_info = &pStruct->One_ti; } else { *t_group_info = NULL; } } exit_function: return ret; } /******************************************************************************************************/ int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ) { INCHI_MODE bTautFlags = ip->bTautFlags | TG_FLAG_H_ALREADY_REMOVED; INCHI_MODE bTautFlagsDone = 0; /*(ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ INChI *cur_INChI[TAUT_NUM]; INChI_Aux *cur_INChI_Aux[TAUT_NUM]; int i, j, k; int iComponent = pTCGroups->iComponent; int len_at = pStruct->num_atoms + pStruct->num_deleted_H; int num_atoms = pStruct->num_atoms; long ulStructTime; INP_ATOM_DATA InpCurAtData; INP_ATOM_DATA *inp_cur_data; INP_ATOM_DATA InpNormAtData, InpNormTautData; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ int bOrigCoord = 0; int num_at, ret = RI_ERR_PROGR; struct tagInchiTime ulMaxTime; T_GROUP_INFO *t_group_info = NULL; /* initialization */ inp_cur_data = &InpCurAtData; inp_norm_data[TAUT_NON] = &InpNormAtData; inp_norm_data[TAUT_YES] = &InpNormTautData; memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); ulStructTime = sd->ulStructTime; memset( sd, 0, sizeof(*sd) ); /* deallocate old results */ free_t_group_info( &pStruct->One_ti ); for ( k = 0; k < TAUT_NUM; k ++ ) { Free_INChI( &pStruct->pOneINChI[k] ); Free_INChI_Aux( &pStruct->pOneINChI_Aux[k] ); if ( pStruct->pOne_norm_data[k] ) { FreeInpAtomData( pStruct->pOne_norm_data[k] ); inchi_free( pStruct->pOne_norm_data[k] ); pStruct->pOne_norm_data[k] = NULL; } cur_INChI[k] = NULL; cur_INChI_Aux[k] = NULL; } memcpy( at3, at2, sizeof(at3[0])*len_at ); /* prepare the structure */ IncrZeroBondsAndClearEndpts(at3, num_atoms, iComponent+1); CopySt2At( at3, pStruct->st, pStruct->num_atoms ); FixUnkn0DStereoBonds( at3, pStruct->num_atoms); ret = ReconcileAllCmlBondParities( at3, pStruct->num_atoms, 0 ); if ( ret < 0 ) { goto exit_function; } if ( 0 < fix_odd_things( num_atoms, at3, 1, ip->bFixNonUniformDraw ) ) { if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; } /* allocate and set parameters */ inp_cur_data->at = at3; inp_cur_data->num_at = num_atoms; inp_cur_data->num_removed_H = pStruct->num_deleted_H; bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); if ( i = bNumHeterAtomHasIsotopicH( at3, num_atoms ) ) { if ( i & 1 ) { bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; } if ( i & 2 ) { bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; } } memset( &ulMaxTime, 0, sizeof(ulMaxTime)); /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ for ( k = 0; k < TAUT_NUM; k ++ ) { if ( !pStruct->bMobileH || k == pStruct->bMobileH ) { /* pStruct->bMobileH=0: k = 0, 1 => allow allocation of both Fixed-H and Mobile-H InChI pStruct->bMobileH=1: k = 1 only => allow allocation of only Mobile-H InChI */ int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? (ip->nMode & REQ_MODE_ISO):0; if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { /* alloc INChI and INChI_Aux only if ip->nMode allows this */ cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, &inp_cur_data->num_isotopic, nAllocMode ); cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); if ( cur_INChI_Aux[k] ) { cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; } /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at+inp_cur_data->num_removed_H, k ); inp_norm_data[k]->num_removed_H = inp_cur_data->num_removed_H; } else { FreeInpAtomData( inp_norm_data[k] ); } } else { FreeInpAtomData( inp_norm_data[k] ); } } k = pStruct->bMobileH; /* In case of Fixed-H we have to create InChI for both Fixed-H and Mobile-H */ num_at = Create_INChI( cur_INChI, cur_INChI_Aux, NULL/* not used */, inp_cur_data->at, inp_norm_data, inp_cur_data->num_at+inp_cur_data->num_removed_H, ip->nMode, &bTautFlags, &bTautFlagsDone, NULL /* &ulMaxTime*/, &pStruct->One_ti, sd->pStrErrStruct); SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, iComponent+1 ); /* normalization alters structure component number */ /* detect InChI errors */ if ( num_at < 0 ) { ret = num_at; } else if ( cur_INChI[k] && cur_INChI[k]->nErrorCode ) { ret = cur_INChI[k]->nErrorCode; } else if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nErrorCode ) { ret = cur_INChI_Aux[k]->nErrorCode; } else { ret = 0; } /* fill out the output */ if ( !ret ) { int bMobileH = pStruct->bMobileH; if ( bMobileH == TAUT_NON && 0 == cur_INChI[TAUT_NON]->nNumberOfAtoms && 0 < cur_INChI[TAUT_YES]->nNumberOfAtoms ) { /* tautomerism or H(+) removal/addition was not discovered */ bMobileH = TAUT_YES; } pStruct->nChargeRevrs = cur_INChI[TAUT_YES]->nTotalCharge; pStruct->pOneINChI[0] = cur_INChI[bMobileH]; pStruct->pOneINChI_Aux[0] = cur_INChI_Aux[bMobileH]; pStruct->nOneINChI_bMobileH = bMobileH; cur_INChI[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ cur_INChI_Aux[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; /* set correct t-group numbers to endpoints */ t_group_info = &pStruct->One_ti; if ( t_group_info->num_t_groups && t_group_info->t_group && t_group_info->nEndpointAtomNumber ) { inp_ATOM *at_norm = inp_norm_data[TAUT_YES]->at; int num_at_norm = inp_norm_data[TAUT_YES]->num_at; for ( i = 0; i < num_at_norm; i ++ ) { at_norm[i].endpoint = 0; } for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { k = t_group_info->t_group[i].nFirstEndpointAtNoPos; /* add number of mobile (-) to the number of mobile H */ t_group_info->t_group[i].num[0] += t_group_info->t_group[i].num[1]; for ( j = 0; j < t_group_info->t_group[i].nNumEndpoints; j ++, k ++ ) { at_norm[t_group_info->nEndpointAtomNumber[k]].endpoint = t_group_info->t_group[i].nGroupNumber; } } } pStruct->pOne_norm_data[0] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); if ( pStruct->pOne_norm_data[0] ) { memcpy( pStruct->pOne_norm_data[0], inp_norm_data[bMobileH], sizeof(pStruct->pOne_norm_data[0][0])); memset( inp_norm_data[bMobileH], 0, sizeof(*inp_norm_data[0]) ); } else { ret = RI_ERR_ALLOC; } if ( bMobileH == TAUT_NON && cur_INChI[TAUT_YES]->nNumberOfAtoms > 0 ) { int bMobileHalt = ALT_TAUT(bMobileH); /* = TAUT_YES */ pStruct->pOneINChI[1] = cur_INChI[bMobileHalt]; pStruct->pOneINChI_Aux[1] = cur_INChI_Aux[bMobileHalt]; cur_INChI[bMobileHalt] = NULL; cur_INChI_Aux[bMobileHalt] = NULL; pStruct->pOne_norm_data[1] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); if ( pStruct->pOne_norm_data[1] ) { memcpy( pStruct->pOne_norm_data[1], inp_norm_data[bMobileHalt], sizeof(pStruct->pOne_norm_data[0][0])); memset( inp_norm_data[bMobileHalt], 0, sizeof(*inp_norm_data[0]) ); } else { ret = RI_ERR_ALLOC; } } } else { #if ( bRELEASE_VERSION != 1 ) #ifndef TARGET_API_LIB fprintf( stdout, "ERROR: Create_INChI returned %d\n", ret ); #endif #endif } exit_function: /* deallocate unused */ for ( k = 0; k < TAUT_NUM; k ++ ) { Free_INChI( &cur_INChI[k] ); Free_INChI_Aux( &cur_INChI_Aux[k] ); FreeInpAtomData( inp_norm_data[k] ); } sd->ulStructTime = ulStructTime; return ret; } /****************************************************************************************************** Input: at[].num_H = total number of all terminal H connected to the atom at[].num_iso_H[] = numbers of isotopic H among at[].num_H Explicit H are disconnected Calculate InChI with normalization only in MakeOneInChIOutOfStrFromINChI() with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 Output: at[].num_H = number of implicit non-isotopic H connected to the atom at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) Explicit H are connected Calculate InChI with full preprocessing MakeInChIOutOfStrFromINChI2() with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 *******************************************************************************************************/ int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) { int i, j, k, n, m, num_H; int tot_atoms = num_atoms + num_deleted_H; for ( i = num_atoms; i < tot_atoms; i = j ) { k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) ; num_H = j-i; /* number of explicit H for at[k] */ if ( num_H > at[k].num_H ) { return RI_ERR_PROGR; } if ( num_H + at[k].valence > MAXVAL ) { return RI_ERR_SYNTAX; } /* insert links to explicit H before all other links in the connection list */ n = at[k].valence; memmove( at[k].neighbor +num_H, at[k].neighbor, sizeof(at[k].neighbor[0]) * n ); memmove( at[k].bond_stereo+num_H, at[k].bond_stereo, sizeof(at[k].bond_stereo[0]) * n ); memmove( at[k].bond_type +num_H, at[k].bond_type , sizeof(at[k].bond_type[0]) * n ); for ( n = 0; n < num_H; n ++ ) { at[k].neighbor[n] = i + n; at[k].bond_stereo[n] = 0; at[k].bond_type[n] = BOND_TYPE_SINGLE; } for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { at[k].sb_ord[m] += num_H; if ( at[k].sn_ord[m] < 0 ) { for ( n = i; n < j; n ++ ) { if ( at[n].orig_at_number == at[k].sn_orig_at_num[m] ) { at[k].sn_ord[m] = n-i; break; } } if ( n == j ) { return RI_ERR_PROGR; } } else { at[k].sn_ord[m] += num_H; } } at[k].valence += num_H; at[k].chem_bonds_valence += num_H; at[k].num_H -= num_H; /* cannot be negative */ /*memset( at[k].num_iso_H, 0, sizeof(at[0].num_iso_H) );*/ /* attached H must carry all isotopic shifts */ for ( n = i; n < j; n ++ ) { at[n].chem_bonds_valence = BOND_TYPE_SINGLE; } /* isotopic H */ for ( m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { return RI_ERR_PROGR; } if ( 0 >= at[k].num_iso_H[(int)at[m].iso_atw_diff-1] -- ) { return RI_ERR_PROGR; } } } /* subtract isotopic H */ for ( i = 0; i < num_atoms; i ++ ) { for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { at[i].num_H -= at[i].num_iso_H[m]; } if ( 0 > at[i].num_H ) { return RI_ERR_PROGR; } } return tot_atoms; } /****************************************************************************************************** Input: at[].num_H = number of implicit non-isotopic H connected to the atom at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) Explicit H are connected Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 Output: at[].num_H = total number of all terminal H connected to the atom at[].num_iso_H[] = numbers of isotopic H among at[].num_H Explicit H are disconnected Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 *******************************************************************************************************/ int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) { int i, j, k, n, m, num_H, num_iso_H; int tot_atoms = num_atoms + num_deleted_H; /* add implicit isotopic H to total implicit H */ for ( i = 0; i < num_atoms; i ++ ) { for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { at[i].num_H += at[i].num_iso_H[m]; } } for ( i = num_atoms; i < tot_atoms; i = j ) { k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) { at[j].chem_bonds_valence = 0; } num_H = j-i; /* number of explicit H for at[k] */ /* verify correct number of explicit H */ for ( n = 0; n < at[k].valence && at[k].neighbor[n] >= num_atoms; n ++ ) ; if ( n != num_H ) { return RI_ERR_PROGR; } /* remove bonds to explicit H located in front of all other bonds in the connection list */ n = (at[k].valence -= num_H); /* new number of bonds */ at[k].chem_bonds_valence -= num_H; /* new no-H valence */ if ( n ) { memmove( at[k].neighbor, at[k].neighbor + num_H, sizeof(at[k].neighbor[0]) * n ); memmove( at[k].bond_stereo, at[k].bond_stereo + num_H, sizeof(at[k].bond_stereo[0]) * n ); memmove( at[k].bond_type, at[k].bond_type + num_H, sizeof(at[k].bond_type[0]) * n ); } /* clear the 'tails' */ memset( at[k].neighbor+n, 0, sizeof(at[k].neighbor[0]) * num_H ); memset( at[k].bond_stereo+n, 0, sizeof(at[k].bond_stereo[0]) * num_H ); memset( at[k].bond_type+n, 0, sizeof(at[k].bond_type[0]) * num_H ); for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { at[k].sb_ord[m] -= num_H; if ( 0 <= at[k].sn_ord[m] && at[k].sn_ord[m] < num_H ) { at[k].sn_ord[m] = -1; /* disconnected explicit H */ } } /* add explicit isotopic H (already included in num_H) */ for ( num_iso_H = 0, m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { return RI_ERR_PROGR; } at[k].num_iso_H[(int)at[m].iso_atw_diff-1] ++; } at[k].num_H += num_H; /* add all explicit H including isotopic */ } return tot_atoms; } /******************************************************************************************************/ int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, long num_inp ) { char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; int len, ret; /* PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; */ char pStr[256]; INPUT_PARMS local_ip; STRUCT_DATA local_sd; INPUT_PARMS *ip = &local_ip; STRUCT_DATA *sd = &local_sd; ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *prep_inp_data = PrepAtData; *ip = *ip_inp; ip->bDisplay = 0; ip->bDisplayCompositeResults = 0; ip->bDisplayEachComponentINChI = 0; ip->bDisplayIfRestoreWarnings = 0; ip->bINChIOutputOptions = INCHI_OUT_NO_AUX_INFO; /* if ( pStruct->bMobileH ) { ip->nMode &= ~REQ_MODE_BASIC; ip->nMode |= REQ_MODE_TAUT; } else { ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); } */ memset( sd, 0, sizeof(*sd) ); sd->fPtrStart = -1; sd->fPtrEnd = -1; /* if ( ip->nMode & REQ_MODE_STEREO ) { if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; } else { sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; } } */ memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); memset( pStruct->RevInChI.pINChI, 0, sizeof(pStruct->RevInChI.pINChI ) ); memset( pStruct->RevInChI.pINChI_Aux, 0, sizeof(pStruct->RevInChI.pINChI_Aux) ); memset( pStr, 0, sizeof(pStr) ); memset( szTitle, 0, sizeof(szTitle) ); len = sizeof(orig_inp_data->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H); orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); if ( orig_inp_data->at ) { /*memcpy( orig_inp_data->at, pStruct->at2, len );*/ /*ret = ConnectDisconnectedH( orig_inp_data->at, pStruct->num_atoms, pStruct->num_deleted_H );*/ CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); ret = ConnectDisconnectedH( pStruct->at2, pStruct->num_atoms, pStruct->num_deleted_H ); if ( ret < 0 ) { goto exit_error; } orig_inp_data->num_inp_atoms = ret; /* connections changed => reconcile parities even if they were reconciled before */ /* remove t-group markings and increment zero-order bonds, otherwise MakeInChIOutOfStrFromINChI2() woild fail */ /* IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); */ IncrZeroBonds(pStruct->at2, pStruct->num_atoms, iComponent+1); /* CopySt2At() moved to the position before ConnectDisconnectedH() because in case stereo exists only in Mobile-H layer and the processd here component is restored in Fixed-H layer the parities needed by ConnectDisconnectedH() must be there before calling ConnectDisconnectedH() */ /*CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );*/ ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); if ( ret < 0 ) { goto exit_error; } memcpy( orig_inp_data->at, pStruct->at2, len ); ClearEndpts(orig_inp_data->at, pStruct->num_atoms); if ( FixUnkn0DStereoBonds(orig_inp_data->at, pStruct->num_atoms) ) { ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); if ( ret < 0 ) { goto exit_error; } } /* keep endpoint[] markings in at2[] for subsequent add/remove protons */ } else { ret = RI_ERR_ALLOC; goto exit_error; } memset( sd->num_components, 0, sizeof(sd->num_components) ); memset( sd->num_taut, 0, sizeof(sd->num_taut) ); memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); ret = ProcessOneStructure( sd, ip, szTitle, pStruct->RevInChI.pINChI, pStruct->RevInChI.pINChI_Aux, NULL /*inp_file*/, NULL /*log_file*/, NULL /*output_file*/, NULL /*prb_file*/, orig_inp_data, prep_inp_data, num_inp, pStr, sizeof(pStr), 0 /* save_opt_bits */); memcpy(pStruct->RevInChI.num_components, sd->num_components, sizeof(pStruct->RevInChI.num_components) ); memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); pStruct->RevInChI.nRetVal = ret; /* translate returned value */ if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { ret = RI_ERR_PROGR; } else if ( ret == _IS_OKAY ) { ret = 0; } else if ( ret == _IS_WARNING ) { ret = 1; } else { ret = RI_ERR_PROGR; } /* save total charge from Mobile-H layer */ pStruct->nChargeRevrs = 0; if ( ret >= 0 ) { if ( bRevInchiComponentExists( pStruct, INCHI_REC, TAUT_YES, 0 ) ) { pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_REC][0][TAUT_YES]->nTotalCharge; } else if ( bRevInchiComponentExists( pStruct, INCHI_BAS, TAUT_YES, 0 ) ) { pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_BAS][0][TAUT_YES]->nTotalCharge; } } /* free structure data */ FreeOrigAtData( orig_inp_data ); FreeOrigAtData( prep_inp_data ); FreeOrigAtData( prep_inp_data+1 ); exit_error: return ret; } /******************************************************************************************************/ int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, int bINChIOutputOptions, INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, InpInChI *pOneInput, int bHasSomeFixedH, unsigned char save_opt_bits) { char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; int len, ret; /* PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; */ REV_INCHI RevInChI; int nStrLen = INCHI_SEGM_BUFLEN; char *pStr = NULL; INPUT_PARMS local_ip; STRUCT_DATA local_sd; INPUT_PARMS *ip = &local_ip; STRUCT_DATA *sd = &local_sd; ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *prep_inp_data = PrepAtData; *ip = *ip_inp; ip->bNoStructLabels = 1; ip->bDisplay = 0; ip->bDisplayCompositeResults = 0; ip->bDisplayEachComponentINChI = 0; ip->bDisplayIfRestoreWarnings = 0; #if ( I2S_MODIFY_OUTPUT == 1 ) if ( bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); else if ( bINChIOutputOptions & INCHI_OUT_XML ) ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; else if ( bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT ) ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; else if ( bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO | INCHI_OUT_ONLY_AUX_INFO | INCHI_OUT_TABBED_OUTPUT)) ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC | bINChIOutputOptions); else ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC); #else ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC ); #endif if ( bHasSomeFixedH ) { ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); } else { ip->nMode &= ~REQ_MODE_BASIC; ip->nMode |= REQ_MODE_TAUT; } memset( sd, 0, sizeof(*sd) ); sd->fPtrStart = -1; sd->fPtrEnd = -1; /* if ( ip->nMode & REQ_MODE_STEREO ) { if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; } else { sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; } } */ memset( orig_inp_data, 0, sizeof( *orig_inp_data ) ); memset( prep_inp_data, 0, 2*sizeof( *prep_inp_data ) ); memset( RevInChI.pINChI, 0, sizeof(RevInChI.pINChI ) ); memset( RevInChI.pINChI_Aux, 0, sizeof(RevInChI.pINChI_Aux) ); len = sizeof(orig_inp_data->at[0]) * pOneInput->num_atoms; orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); orig_inp_data->szCoord = (MOL_COORD *)inchi_calloc( pOneInput->num_atoms, sizeof(orig_inp_data->szCoord[0])); pStr = (char *)inchi_calloc( nStrLen, sizeof(char) ); if ( orig_inp_data->at && orig_inp_data->szCoord && pStr ) { int i, k; memcpy( orig_inp_data->at, pOneInput->atom, len ); orig_inp_data->num_inp_atoms = pOneInput->num_atoms; ClearEndpts( orig_inp_data->at, orig_inp_data->num_inp_atoms ); /* otherwise fails on CID=450438 */ if ( FixUnkn0DStereoBonds(orig_inp_data->at, orig_inp_data->num_inp_atoms) ) { ret = ReconcileAllCmlBondParities( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0 ); if ( ret < 0 ) { goto exit_error; } } /* To obtain rA,rB,rC in AuxInfo we have to emulate input coordinates; make all of them zeroes */ for ( i = 0; i < pOneInput->num_atoms; i ++ ) { for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { orig_inp_data->szCoord[i][k] = '0'; } } } else { ret = RI_ERR_ALLOC; goto exit_error; } memset( sd->num_components, 0, sizeof(sd->num_components) ); memset( sd->num_taut, 0, sizeof(sd->num_taut) ); memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); memset( szTitle, 0, sizeof(szTitle) ); ret = ProcessOneStructure(sd, ip, szTitle, RevInChI.pINChI, RevInChI.pINChI_Aux, NULL /*inp_file*/, plog /*log_file*/, pout /*output_file*/, NULL /*prb_file*/, orig_inp_data, prep_inp_data, num_inp, pStr, nStrLen, save_opt_bits); memcpy(RevInChI.num_components, sd->num_components, sizeof(RevInChI.num_components) ); /* memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); */ RevInChI.nRetVal = ret; /* translate returned value */ if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { ret = RI_ERR_PROGR; } else if ( ret == _IS_OKAY ) { ret = 0; } else if ( ret == _IS_WARNING ) { ret = 1; } else { ret = RI_ERR_PROGR; } /* free structure data */ FreeOrigAtData( orig_inp_data ); FreeOrigAtData( prep_inp_data ); FreeOrigAtData( prep_inp_data+1 ); FreeAllINChIArrays( RevInChI.pINChI, RevInChI.pINChI_Aux, RevInChI.num_components ); exit_error: if ( pStr ) inchi_free( pStr ); return ret; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr2.c000066400000000000000000011043571271037650300234600ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*^^^ */ /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" /******************************************************************************************************/ void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) { int i; for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].p_parity ) { memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) ); st[i].p_parity = at[i].p_parity; } if ( at[i].sb_parity[0] ) { memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) ); memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) ); memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) ); memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) ); } } } void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) { int i; if ( !st ) { return; } for ( i = 0; i < num_atoms; i ++ ) { if ( st[i].p_parity ) { memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) ); at[i].p_parity = st[i].p_parity; } if ( st[i].sb_parity[0] ) { memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) ); memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) ); memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) ); memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) ); } } } /******************************************************************************************************/ int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH) { inp_ATOM *at = NULL; inp_ATOM_STEREO * st = NULL; int num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity; int nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len; int num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2; INChI_Stereo *pStereo = NULL, *pStereo2 = NULL; AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; num_atoms = pInChI->nNumberOfAtoms; if ( num_atoms <= 0 ) { return 0; } INCHI_HEAPCHK /* atoms */ pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) ); if ( !at ) { ret = RI_ERR_ALLOC; goto exit_function; } pStruct->num_atoms = num_atoms; /* charge */ pStruct->charge = pInChI->nTotalCharge; /* elements, terminal atoms H */ for ( i = 0; i < num_atoms; i ++ ) { at[i].el_number = pInChI->nAtom[i]; if ( GetElementFormulaFromAtNum(UCINT pInChI->nAtom[i], at[i].elname ) ) { ret = RI_ERR_PROGR; goto exit_function; } at[i].orig_at_number = iAtNoOffset + i+1; at[i].orig_compt_at_numb = i + 1; at[i].component = iComponent + 1; num_H = pInChI->nNum_H[i]; /* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] --- if ( pInChI->nNum_H_fixed ) { num_H += pInChI->nNum_H_fixed[i]; } */ at[i].num_H = num_H; } INCHI_HEAPCHK /* connections */ for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { /* vertex - neighbor connection */ jv = at[n_vertex].valence ++; at[n_vertex].neighbor[jv] = n_neigh; at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE; at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv]; /* neighbor - vertex connection */ jn = at[n_neigh].valence ++; at[n_neigh].neighbor[jn] = n_vertex; at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE; at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn]; } else if ( (n_vertex = n_neigh) >= num_atoms ) { ret = RI_ERR_PROGR; goto exit_function; } } INCHI_HEAPCHK /* isotopic atoms */ if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) { for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1; at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference; at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H; at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D; at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T; } pStruct->bIsotopic |= 1; } INCHI_HEAPCHK /* tautomeric groups */ if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) { goto exit_function; } /* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */ if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) { pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup; pInChI->IsotopicTGroup = NULL; } /* stereo */ if ( pInChI->StereoIsotopic && (pInChI->StereoIsotopic->nNumberOfStereoBonds + pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { pStereo = pInChI->StereoIsotopic; } else if ( pInChI->Stereo && (pInChI->Stereo->nNumberOfStereoBonds + pInChI->Stereo->nNumberOfStereoCenters) ) { pStereo = pInChI->Stereo; } else { pStereo = NULL; } /* stereo2: Mobile-H in addition to Fixed-H*/ pStereo2 = NULL; if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) { if ( pInChIMobH->StereoIsotopic && (pInChIMobH->StereoIsotopic->nNumberOfStereoBonds + pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) { pStereo2 = pInChIMobH->StereoIsotopic; } else if ( pInChIMobH->Stereo && (pInChIMobH->Stereo->nNumberOfStereoBonds + pInChIMobH->Stereo->nNumberOfStereoCenters) ) { pStereo2 = pInChIMobH->Stereo; } } INCHI_HEAPCHK num_stereo_bonds = num_stereo_bonds2 = 0; num_stereo_centers = num_stereo_centers2 = 0; /* -- have already been done in the initialization -- iDeletedH = 0; nNumDeletedH = 0; */ if ( pStereo || pStereo2 ) { /* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */ int len1 = pStereo? pStereo->nNumberOfStereoCenters : 0; int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0; int i2, diff, diff2; for ( i = i2 = 0; i < len1 || i2 < len2; ) { if ( i < len1 && i2 < len2 ) { diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2]; if ( diff <= 0 ) { n_vertex = pStereo->nNumber[i]-1; i ++; i2 += !diff; } else { n_vertex = pStereo2->nNumber[i2]-1; num_stereo_centers2 ++; i2 ++; } } else if ( i < len1 ) { n_vertex = pStereo->nNumber[i]-1; i ++; } else { n_vertex = pStereo2->nNumber[i2]-1; num_stereo_centers2 ++; i2 ++; } /* find whether it is an allene */ if ( at[n_vertex].valence == 2 && at[n_vertex].num_H == 0 && bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 && bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 && bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { /* allene */ if ( !at[jv].at_type && at[jv].num_H ) { nNumDeletedH += at[jv].num_H; at[jv].at_type ++; /* H should be added as an explicit H */ } if ( !at[jn].at_type && at[jn].num_H ) { nNumDeletedH += at[jn].num_H; at[jn].at_type ++; /* H should be added as an explicit H */ } } else { /* stereogenic atom - sp3 */ if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { nNumDeletedH += at[n_vertex].num_H; at[n_vertex].at_type ++; /* H should be added as an explicit H */ } } } INCHI_HEAPCHK len1 = pStereo? pStereo->nNumberOfStereoBonds : 0; len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0; for ( i = i2 = 0; i < len1 || i2 < len2; ) { if ( i < len1 && i2 < len2 ) { diff = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2]; diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2]; if ( diff < 0 || diff == 0 && diff2 <= 0) { n_vertex = pStereo->nBondAtom1[i]-1; n_neigh = pStereo->nBondAtom2[i]-1; i ++; i2 += !diff && !diff2; } else { n_vertex = pStereo2->nBondAtom1[i2]-1; n_neigh = pStereo2->nBondAtom2[i2]-1; num_stereo_bonds2 ++; i2 ++; } } else if ( i < len1 ) { n_vertex = pStereo->nBondAtom1[i]-1; n_neigh = pStereo->nBondAtom2[i]-1; i ++; } else { n_vertex = pStereo2->nBondAtom1[i2]-1; n_neigh = pStereo2->nBondAtom2[i2]-1; num_stereo_bonds2 ++; i2 ++; } if ( !is_in_the_list( at[n_vertex].neighbor, (AT_NUMB)n_neigh, at[n_vertex].valence ) ) { /* must be a cumulene */ if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, nCumulene, MAX_CUMULENE_LEN+1 ) ) { ret = RI_ERR_SYNTAX; /* not a cumulene */ goto exit_function; } } if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { nNumDeletedH += at[n_vertex].num_H; at[n_vertex].at_type ++; /* H should be added as an explicit H */ } if ( !at[n_neigh].at_type && at[n_neigh].num_H ) { nNumDeletedH += at[n_neigh].num_H; at[n_neigh].at_type ++; /* H should be added as an explicit H */ } } INCHI_HEAPCHK if ( nNumDeletedH ) { /* add explicit H */ inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) ); if ( !at2 ) { ret = RI_ERR_ALLOC; goto exit_function; } pStruct->num_deleted_H = nNumDeletedH; memcpy( at2, at, num_atoms * sizeof(at2[0]) ); inchi_free( at ); pStruct->at = at = at2; /* fill out deleted H atom info */ for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) { strcpy( at[i].elname, "H" ); at[i].el_number = EL_NUMBER_H; at[i].orig_at_number = iAtNoOffset + i+1; at[i].orig_compt_at_numb = i + 1; at[i].component = iComponent + 1; } /* connect deleted H */ for( i = 0; i < num_atoms; i ++ ) { if ( at[i].at_type == 1 ) { if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } } } INCHI_HEAPCHK } if ( pStereo ) { /* mark stereo centers, they have already been connected the added explicit H, if any */ int bInvertedParity = (pStereo->nCompInv2Abs == -1); for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) { n_vertex = pStereo->nNumber[i]-1; parity = pStereo->t_parity[i]; if ( bInvertedParity ) { parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; } /* find whether it is allene */ if ( at[n_vertex].valence == 2 && at[n_vertex].num_H == 0 && bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && /* allene has exactly 2 double bonds */ (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { /* allene: add explicit H if implicit H are present */ /* iDeletedH = current number of already added explicit H */ /* idelH1 = index in at[] of the explicit H added to atom jv */ if ( at[jv].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { /* index of the stereo atom neighbor */ idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; } if ( at[jn].num_H ) { /* iDeletedH = current number of already added explicit H */ /* idelH2 = index of the explicit H added to atom jn */ if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; } /* allene: set bond types to double */ /* if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { goto exit_function; } */ /* allene: make 0D parity */ ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); if ( ret < 0 ) { goto exit_function; } } else { /* stereogenic sp3 atom */ if ( at[n_vertex].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); if ( ret < 0 ) { goto exit_function; } num_stereo_centers ++; } if ( ret < 0 ) { goto exit_function; } } INCHI_HEAPCHK /* mark stereobonds */ for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) { jv = pStereo->nBondAtom1[i]-1; jn = pStereo->nBondAtom2[i]-1; parity = pStereo->b_parity[i]; if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { /* must be a cumulene */ if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { return RI_ERR_SYNTAX; /* not a cumulene */ } len = MAX_CUMULENE_LEN+1; } else { /* a regular double or alt bond */ nCumulene[0] = jv; nCumulene[1] = jn; len = 1; /* cumulene length is number of bonds, not number of atoms */ } /* cumulene or double bond: add explicit H if implicit H are present */ if ( at[jv].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { /* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */ idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; } if ( at[jn].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; } if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { goto exit_function; } } INCHI_HEAPCHK } /* allocate memory for Mobile-H-only stereo */ if ( num_stereo_centers2 + num_stereo_bonds2 ) { if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) { ret = RI_ERR_ALLOC; goto exit_function; } CopyAt2St( at, st, num_atoms ); } pStruct->st = st; if ( num_stereo_centers2 ) { /* In case of Fixed-H */ /* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */ int bInvertedParity = (pStereo2->nCompInv2Abs == -1); for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) { n_vertex = pStereo2->nNumber[i]-1; parity = pStereo2->t_parity[i]; if ( at[n_vertex].p_parity ) { continue; /* the parity has already been set for Fixed-H */ } if ( bInvertedParity ) { parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; } /* find whether it is allene */ if ( at[n_vertex].valence == 2 && at[n_vertex].num_H == 0 && bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && /* allene has exactly 2 double bonds */ (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { /* allene: add explicit H if implicit H are present */ /* iDeletedH = current number of already added explicit H */ /* idelH1 = index in at[] of the explicit H added to atom jv */ if ( at[jv].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { /* index of the stereo atom neighbor */ idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; } if ( at[jn].num_H ) { /* iDeletedH = current number of already added explicit H */ /* idelH2 = index of the explicit H added to atom jn */ if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; } /* allene: set bond types to double */ /* if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { goto exit_function; } */ /* allene: make 0D parity */ ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); if ( ret < 0 ) { goto exit_function; } } else { /* stereogenic sp3 atom */ if ( at[n_vertex].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); if ( ret < 0 ) { goto exit_function; } num_stereo_centers ++; } if ( ret < 0 ) { goto exit_function; } } } if ( num_stereo_bonds2 ) { /* In case of Fixed-H */ /* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */ for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) { jv = pStereo2->nBondAtom1[i]-1; jn = pStereo2->nBondAtom2[i]-1; parity = pStereo2->b_parity[i]; if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { /* must be a cumulene */ if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { return RI_ERR_SYNTAX; /* not a cumulene */ } len = MAX_CUMULENE_LEN+1; } else { /* a regular double or alt bond */ nCumulene[0] = jv; nCumulene[1] = jn; len = 1; /* cumulene length is number of bonds, not number of atoms */ } /* cumulene or double bond: add explicit H if implicit H are present */ if ( at[jv].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { /* double bond neighbor that has the smallest canonical number */ idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; } if ( at[jn].num_H ) { if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { goto exit_function; } } else { idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; } if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { goto exit_function; } } } ret = num_atoms; exit_function: return ret; } /*************************************************************/ int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ) { AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type; k1 = at[i1].sb_ord[m1]; n1 = i1; nCumulene[nLenCumulene ++] = n1; do { n2 = at[n1].neighbor[k1]; /* next atom */ nCumulene[nLenCumulene ++] = n2; for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) { if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) { /* found the endatom */ goto found; } } if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) { break; /* not a middle cumulene */ } k1 = (at[n2].neighbor[0] == n1); n1 = n2; } while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 && bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) ); return RI_ERR_SYNTAX; /* failed */ found: if ( nLenCumulene == 2 ) { bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */ } else { bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */ } for ( j = 1; j < nLenCumulene; j ++ ) { /* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */ /* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */ if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) { return RI_ERR_PROGR; /* failed */ } } return nLenCumulene; } /******************************************************************************************************/ int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI) { INChI_Stereo *pStereo; inp_ATOM *at = pStruct->at; int num_atoms = pStruct->num_atoms; int i, j, num_stereo_bonds, ret; if ( pInChI->StereoIsotopic && (pInChI->StereoIsotopic->nNumberOfStereoBonds + pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { pStereo = pInChI->StereoIsotopic; } else if ( pInChI->Stereo && (pInChI->Stereo->nNumberOfStereoBonds + pInChI->Stereo->nNumberOfStereoCenters) ) { pStereo = pInChI->Stereo; } else { pStereo = NULL; } /************************ set bond types separately from stereo *******************/ if ( pStereo ) { num_stereo_bonds = 0; for ( i = 0; i < num_atoms; i ++ ) { /* set BOND_TYPE_DOUBLE in allenes and cumulenes */ /* set BOND_TYPE_STEREO in double bond stereo */ for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { num_stereo_bonds ++; if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i, j ) ) ) { goto exit_function; } } } if ( num_stereo_bonds ) { int num_bond_type_stereo; int num_bond_type_altern; AT_NUMB neigh; /* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */ for ( i = 0; i < num_atoms; i ++ ) { num_bond_type_stereo = 0; num_bond_type_altern = 0; for ( j = 0; j < at[i].valence; j ++ ) { num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); } if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) { for ( j = 0; j < at[i].valence; j ++ ) { if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { neigh = at[i].neighbor[j]; /* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */ if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) { goto exit_function; } } } } /* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */ } /* increment at[i].chem_bonds_valence if at[i] has an altern. bond */ /* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment */ /* chem_bonds_valence of the adjacent atoms */ for ( i = 0; i < num_atoms; i ++ ) { num_bond_type_stereo = 0; num_bond_type_altern = 0; for ( j = 0; j < at[i].valence; j ++ ) { num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); } if ( !num_bond_type_stereo && num_bond_type_altern ) { /* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */ at[i].chem_bonds_valence += 1; } else if ( num_bond_type_stereo == 1 ) { /* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */ for ( j = 0; j < at[i].valence; j ++ ) { if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { neigh = at[i].neighbor[j]; /* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */ /* does not change at->chem_bonds_valence */ if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) { goto exit_function; } at[i].chem_bonds_valence ++; at[(int)neigh].chem_bonds_valence ++; } } } else if ( num_bond_type_stereo + num_bond_type_altern ) { /* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */ ret = RI_ERR_PROGR; goto exit_function; } } INCHI_HEAPCHK } } ret = 0; /* success */ exit_function: return ret; } /******************************************************************************************************/ int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ) { int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder; int num_at = pStruct->num_atoms; inp_ATOM *at = pStruct->at; ICHICONST SRM *pSrm = pStruct->pSrm; BNS_VERTEX *pv; BNS_EDGE *pe; int chem_bonds_valence, bond_order; atom_charge = left_charge = 0; for ( i = 0; i < num_at; i ++ ) { pv = pBNS->vert + i; /* bonds */ chem_bonds_valence = 0; for ( j = 0; j < at[i].valence; j ++ ) { pe = pBNS->edge + pv->iedge[j]; BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL ); bond_order = pe->flow + nMinorder; if ( !bAllowZeroBondOrder && !bond_order ) { bond_order = 1; } chem_bonds_valence += bond_order; at[i].bond_type[j] = bond_order; /* BOND_MARK_HIGHLIGHT */ } at[i].chem_bonds_valence = chem_bonds_valence; /* charges (both may be present resulting in zero) */ at[i].charge = pVA[i].cInitCharge; if ( pVA[i].nCMinusGroupEdge ) { pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; if ( charge = pe->flow ) { at[i].charge -= charge; atom_charge -= charge; } } if ( pVA[i].nCPlusGroupEdge ) { pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; if ( charge = pe->cap - pe->flow ) { at[i].charge += charge; atom_charge += charge; } } if ( pv->st_edge.cap > pv->st_edge.flow ) { at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow); } } /* find charge excess */ for ( i = num_at; i < pBNS->num_vertices; i ++ ) { pv = pBNS->vert + i; if ( charge = pv->st_edge.cap - pv->st_edge.flow ) { if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { left_charge -= charge; } else if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) { left_charge += charge; } } } /* tautomeric H and (-) */ for ( i = 0; i < pBNS->num_t_groups; i ++ ) { /* tautomeric groups are first non-atom vertices; order of them is same as in pTCGroups->pTCG[] */ int num_H = pTCGroups->pTCG[i].tg_num_H; int num_Minus = pTCGroups->pTCG[i].tg_num_Minus; int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST); int num_at_add; Vertex vMinus = NO_VERTEX; pv = pBNS->vert + num_at + i; /* t-group vertex */ if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) { return RI_ERR_PROGR; } if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) { vMinus = pTCGroups->pTCG[i].tg_set_Minus-1; num_Minus --; } if ( bMinusFirst ) { for ( j = 0; j < pv->num_adj_edges; j ++ ) { pe = pBNS->edge + pv->iedge[j]; v1 = pe->neighbor1; num_at_add = pe->flow; if ( v1 == vMinus ) { if ( num_at_add ) { at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ num_at_add --; /* no checking num_at_add > 0 for now ??? */ } else { num_Minus ++; /* error ??? */ } vMinus = NO_VERTEX; } if ( num_at_add > 0 ) { /* atom has tautomeric attachment; do not allow =N(-) */ if ( num_Minus && !at[v1].charge && at[v1].valence == at[v1].chem_bonds_valence ) { at[v1].charge --; num_at_add --; num_Minus --; } if ( num_at_add > 0 ) { at[v1].num_H += num_at_add; num_H -= num_at_add; num_at_add = 0; } } at[v1].endpoint = i+1; } if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { return RI_ERR_PROGR; } } else { for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) { pe = pBNS->edge + pv->iedge[j]; v1 = pe->neighbor1; num_at_add = pe->flow; if ( v1 == vMinus ) { if ( num_at_add ) { at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ num_at_add --; /* no checking num_at_add > 0 for now ??? */ } else { num_Minus ++; /* error ??? */ } vMinus = NO_VERTEX; } if ( num_at_add > 0 ) { /* atom has tautomeric attachment; do not allow =N(-) */ if ( num_Minus && !at[v1].charge && at[v1].valence == at[v1].chem_bonds_valence ) { at[v1].charge --; num_at_add --; num_Minus --; } if ( num_at_add > 0 ) { at[v1].num_H += num_at_add; num_H -= num_at_add; num_at_add = 0; } } at[v1].endpoint = i+1; } if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { return RI_ERR_PROGR; } } } return ret; } /******************************************************************************************************/ int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ) { int nOutput = 0; #ifndef TARGET_API_LIB #if ( bRELEASE_VERSION == 0 ) char s[128]; int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0; int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms; int num_at = pStruct->num_atoms; inp_ATOM *at = pStruct->at; BNS_VERTEX *pv; BNS_EDGE *pe; #ifdef _DEBUG int bDebugOutput = 0; bNoRad = 1; bDebugOutput = 1; #endif /* count electrons and metals */ num_electrons = -pTCGroups->total_charge; nNumMetalAtoms = 0; for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { num_electrons += pTCGroups->pTCG[i].tg_num_H; } for ( i = 0; i < num_at; i ++ ) { num_electrons += at[i].el_number + at[i].num_H; nNumMetalAtoms += pVA[i].cMetal; } /* create output string */ sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ", bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms, pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds ); tot_st_flow = tot_st_cap = 0; atom_charge = left_charge = 0; if ( pBNS->num_atoms != num_at ) { fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at); nOutput ++; } /* check edges */ #ifdef _DEBUG if ( bDebugOutput && bNoRad ) { fprintf( stderr, "\n\n------begin------------------------------------------------\n" ); fprintf( stderr, "\n\fedge cap flow v1 v2\n\n" ); /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ } #endif for ( i = 0; i < pBNS->num_edges; i ++ ) { pe = pBNS->edge + i; v1 = pe->neighbor1; v2 = v1 ^ pe->neighbor12; if ( pe->cap < pe->flow || pe->flow < 0 ) { fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d ", s, i, v1, v2, pe->cap, pe->flow ); nOutput ++; } #ifdef _DEBUG if ( bDebugOutput && bNoRad ) { /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 ); } #endif } /* check vertices */ #ifdef _DEBUG if ( bDebugOutput && bNoRad ) { fprintf( stderr, "\n\fvert st-cap st-flow type iedge : neigh\n\n" ); /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ } #endif for ( i = 0; i < pBNS->num_vertices; i ++ ) { pv = pBNS->vert + i; #ifdef _DEBUG if ( bDebugOutput && bNoRad ) { /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ int j; const char *s; char sAtom[6]; switch( pv->type ) { case BNS_VERT_TYPE_ATOM: sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" ); s = sAtom; break; case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT: s = "Endpt"; break; case BNS_VT_C_POS: s = "(+) "; break; case BNS_VT_C_NEG: s = "(-) "; break; case BNS_VT_C_POS_C: s = "(+C) "; break; case BNS_VT_C_NEG_C: s = "(-C) "; break; case BNS_VT_C_POS_M: s = "(+M) "; break; case BNS_VT_C_NEG_M: s = "(-M) "; break; case BNS_VT_C_POS_ALL: s = "(+)Sg"; break; case BNS_VT_C_NEG_ALL: s = "(-)Sg"; break; case BNS_VT_M_GROUP: s = "M-grp"; break; case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP: s = "ChStr"; break; case BNS_VERT_TYPE__AUX: s = "Yconn"; break; case BNS_VERT_TYPE_TGROUP: s = "T-grp"; break; default: s = "Unkn."; break; } fprintf( stderr, "%4d %4d/%-4d %4d/%-4d 0x%03X %5s", i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0, pv->type, s ); for ( j = 0; j < pv->num_adj_edges; j ++ ) { fprintf( stderr, " %2d", pv->iedge[j] ); } fprintf( stderr, ":" ); for ( j = 0; j < pv->num_adj_edges; j ++ ) { pe = pBNS->edge + pv->iedge[j]; fprintf( stderr, " %2d", pe->neighbor12 ^ i ); } fprintf( stderr, "\n" ); } #endif tot_st_flow += pv->st_edge.flow; tot_st_cap += pv->st_edge.cap; if ( pv->num_adj_edges > pv->max_adj_edges ) { fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d ", s, i < num_at? "atom":"vertex", i, pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges ); nOutput ++; } if ( i < num_at ) { /* charge on atoms */ charge = pVA[i].cInitCharge; if ( pVA[i].nCMinusGroupEdge ) { pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; if ( pe->flow > 0 ) { charge -= pe->flow; } } if ( pVA[i].nCPlusGroupEdge ) { pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; if ( pe->cap > pe->flow ) { charge += pe->cap - pe->flow; } } if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) { fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s, i < num_at? "atom":"vertex", i, pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow ); nOutput ++; } else if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) { /* ignore carbonyls */ if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") && !at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) { ; /* C(-)#O(+) structure */ } else { fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s, i < num_at? "atom":"vertex", i, pv->type, at[i].elname, charge ); nOutput ++; } } atom_charge += charge; } else if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) { /* excess charge */ if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { left_charge -= charge; } else if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) { left_charge += charge; } else if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) && 0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) && i == pTCGroups->pTCG[j].nVertexNumber ) { ; /* additional "radical" on metal flower */ } else if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) { /* t-groups before running BFS should have st_cap > st_flow */ fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s, i < num_at? "atom":"vertex", i, pv->type, pv->st_edge.cap, pv->st_edge.flow); nOutput ++; } } if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) { fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d ", s, i < num_at? "atom":"vertex", i, pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow ); nOutput ++; } /* check edge_flow vs. st_flow consistency */ for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) { pe = pBNS->edge + pv->iedge[j]; flow += pe->flow; } if ( flow != pv->st_edge.flow ) { fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d ", s, i < num_at? "atom":"vertex", i, pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow ); nOutput ++; } } #ifdef _DEBUG if ( bDebugOutput && bNoRad ) { fprintf( stderr, "\n------end--------------------------------------------------\n" ); } #endif /* if ( num_electrons %= 2 ) { fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at ); nOutput ++; } */ /* tautomeric groups charge */ for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) { charge -= pTCGroups->pTCG[i].tg_num_Minus; } /* compare */ if ( charge != pTCGroups->tgroup_charge ) { fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved ", s, charge, pTCGroups->tgroup_charge); nOutput ++; } /* add other charges */ charge += atom_charge + left_charge; excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge; if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) { fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d ", s, charge, pTCGroups->total_charge, excess_charge ); nOutput ++; } if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) { fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d ", s, tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap ); nOutput ++; } if ( nOutput ) { fprintf( stdout, "\n" ); } #endif #endif return nOutput; } /******************************************************************************************************/ int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ) { inp_ATOM *cur_H, *cur_at = at+jv; int tot_num_iso_H = NUM_ISO_H(cur_at, 0); int num_H = cur_at->num_H; int iso_H = 0; S_CHAR num_iso_H[NUM_H_ISOTOPES]; int i; if ( !at[jv].at_type ) { return RI_ERR_PROGR; } if ( at[jv].at_type > 1 ) { /* explicit hydrogens have already been added; find them */ for ( i = 0; i < *iDeletedH; i ++ ) { if ( at[num_at + i].neighbor[0] == jv ) { *iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */ return 0; } } return RI_ERR_PROGR; } /* add all explicit H disconnected from at[jv] in order H, 1H, D, T */ *iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */ for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { num_iso_H[i] = at[jv].num_iso_H[i]; } for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) { cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */ cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */ cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE; cur_H->valence ++; if ( num_H > tot_num_iso_H ) { num_H --; if ( num_H != tot_num_iso_H ) { /* may happen when Mobile-H stereo included in Fixed-H processing */ if ( bTwoStereo ) { continue; } else { return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */ } } } else { while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] ) iso_H ++; if ( iso_H < NUM_H_ISOTOPES ) { cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */ num_H --; tot_num_iso_H --; num_iso_H[iso_H] --; if ( num_iso_H[iso_H] ) { return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */ } } else { return RI_ERR_SYNTAX; /* not enough isotopic H */ } } } if ( num_H ) { return RI_ERR_SYNTAX; } at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */ return 0; /* success */ } /******************************************************************************************************/ int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ) /* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */ /* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */ { int i, len, iat, nat; nCumulene[0] = i1; for ( i = 0; i < at[i1].valence; i ++ ) { len = 0; iat = i1; /* current */ nat = at[i1].neighbor[i]; /* next */ if ( len+1 == nMaxLen ) { if ( nat == i2 ) { nCumulene[++len] = nat; return 1; /* success */ } continue; /* check next at[i1] neighbor */ } while ( at[nat].valence == 2 && at[nat].num_H == 0 && bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) { nCumulene[++len] = nat; nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */ if ( len+1 == nMaxLen ) { if ( nat == i2 ) { nCumulene[++len] = nat; return 1; /* success */ } break; /* check next at[i1] neighbor */ } iat = nCumulene[len]; /* new current */ } } return 0; /* failed */ } /******************************************************************************************************/ int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ) { AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence ); AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence ); if ( p1 && p2 ) { int j1 = p1 - at[i1].neighbor; int j2 = p2 - at[i2].neighbor; int bTypePrev = at[i1].bond_type[j1]; at[i1].bond_type[j1] = bType; at[i2].bond_type[j2] = bType; if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE && bType && bType <= BOND_TYPE_TRIPLE ) { at[i1].chem_bonds_valence += bType - bTypePrev; at[i2].chem_bonds_valence += bType - bTypePrev; } return 0; } return RI_ERR_SYNTAX; } /******************************************************************************************************/ int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ) { AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; AT_NUMB *p1, *p2; int m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2; /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ S_CHAR *sb_ord1, *sn_ord1, *sb_parity1; S_CHAR *sb_ord2, *sn_ord2, *sb_parity2; AT_NUMB *sn_orig_at_num1; AT_NUMB *sn_orig_at_num2; if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) { return RI_ERR_SYNTAX; /* not an allene */ } /* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */ if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) && (p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) { sb_ord_m1 = p1 - at[i1].neighbor; /* indes of stereobond in the atom's adjacency list */ sb_ord_m2 = p2 - at[i2].neighbor; } else { return RI_ERR_PROGR; } num_neigh1 = at[i1].valence + at[i1].num_H; num_neigh2 = at[i2].valence + at[i2].num_H; if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH || num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) { return RI_ERR_SYNTAX; } sb_ord1 = st? st[i1].sb_ord : at[i1].sb_ord; sb_ord2 = st? st[i2].sb_ord : at[i2].sb_ord; sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity; sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity; /* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */ for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ ) ; for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ ) ; if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) { return RI_ERR_SYNTAX; } if ( k1 && k2 ) { return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */ } if ( k1 || k2 ) { return RI_ERR_SYNTAX; /* only half of a bond was set */ } sn_ord1 = st? st[i1].sn_ord : at[i1].sn_ord; sn_ord2 = st? st[i2].sn_ord : at[i2].sn_ord; sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num; sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num; /* stereo bond neighbors connection index */ sb_ord1[m1] = sb_ord_m1; sb_ord2[m2] = sb_ord_m2; /* stereo bond end atom neighbors */ sn_orig_at_num1[m1] = at[idelH1].orig_at_number; if ( idelH1 < num_at ) { if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) { sn_ord1[m1] = p1 - at[i1].neighbor; } else { return RI_ERR_PROGR; } } else { sn_ord1[m1] = -1; } sn_orig_at_num2[m2] = at[idelH2].orig_at_number; if ( idelH2 < num_at ) { if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) { sn_ord2[m2] = p2 - at[i2].neighbor; } else { return RI_ERR_PROGR; } } else { sn_ord2[m2] = -1; } if ( ATOM_PARITY_WELL_DEF(parity) ) { /* special case: 2 bonds to sb atom => inverse parity because */ /* InChI parity refers to the lone pair as a neighbor */ int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH); if ( num_inv % 2 ) { parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN; } parity1 = AB_PARITY_EVEN; parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD; } else { parity1 = parity2 = parity; } sb_parity1[m1] = parity1; sb_parity2[m2] = parity2; return 0; } /******************************************************************************************************/ int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ) { int m1=0, m2, i, j, tot_num_neigh; /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ /* Given parity from InChI, the order of stereo center neighbors is: */ /* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */ /* 2. Explicit H: non-isotopic, isotopic in order ofascending atomic mass */ /* Explicit H have already been sorted in this order */ /* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */ /* Normal neighboring atoms have already been sorted in this order */ S_CHAR *p_parity; AT_NUMB *p_orig_at_num; if ( !st || !at[i1].p_parity ) { m1 = 0; p_parity = st? &st[i1].p_parity : &at[i1].p_parity; p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num; tot_num_neigh = at[i1].valence + at[i1].num_H; if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { /* only 3 neighbors: the atom itself is the first neighbor */ p_orig_at_num[m1 ++] = at[i1].orig_at_number; } else if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) { return RI_ERR_PROGR; /* wrong number of members */ } m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence); /* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */ if ( at[i1].num_H ) { for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) { if ( at[j + num_at].neighbor[0] == i1 ) { p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number; } } } if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) { return RI_ERR_PROGR; /* wrong number of members */ } /* stereoneighbors: other than explicit H atoms */ for ( i = 0; i < at[i1].valence; i ++ ) { m2 = at[i1].neighbor[i]; p_orig_at_num[m1 ++] = at[m2].orig_at_number; } *p_parity = parity; } return 0; } #if ( BNS_RAD_SEARCH == 1 ) /******************************************************************************************************/ int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ) { int nNumRad, ret = 0, ret2; int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta; BNS_EDGE *pEdge; BNS_VERTEX *pv; Vertex v1, v2; S_SHORT *pnRad = NULL, *pnDelta = NULL; CC_CAND *pCand = NULL; int cnBits, bAtomRadRemoved = 0; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) { num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; } if ( !num_rad_not_atom ) { goto exit_function; } /****************************************************/ /* */ /* Move radicals from ChargeStruct to atoms */ /* */ /****************************************************/ /* allocate memory to keep track of moved radicals */ pnRad = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0])); pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0])); if ( !pnRad || !pnDelta ) { ret = RI_ERR_ALLOC; goto exit_function; } for ( i = 0; i < pBNS->num_vertices; i ++ ) { pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; } while( 1 ) { /* remove radicals from atoms */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; pBNS->vert[i].st_edge.cap -= pnDelta[i]; bAtomRadRemoved += (0 != pnDelta[i]); } ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT ); if ( !ret ) { break; } if ( ret < 0 ) { goto exit_function; } nNumRad = ret; for ( i = 0; i < nNumRad; i ++ ) { pEdge = pBNS->edge + pBD->RadEdges[i]; v1 = pEdge->neighbor1; v2 = pEdge->neighbor12 ^ v1; pBNS->vert[v1].st_edge.flow -= pEdge->flow; pBNS->vert[v2].st_edge.flow -= pEdge->flow; pBNS->tot_st_flow -= 2*pEdge->flow; pEdge->flow = 0; pEdge->forbidden |= forbidden_mask; pBNS->edge_forbidden_mask |= forbidden_mask; } ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret < 0 ) { goto exit_function; } else { num_moved += ret; } RemoveRadEndpoints( pBNS, pBD, NULL ); if ( ret == 0 ) { break; /* could not move more radicals */ } if ( bAtomRadRemoved ) { /* restore radicals to atoms */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { pBNS->vert[i].st_edge.cap += pnDelta[i]; } bAtomRadRemoved = 0; } } if ( bAtomRadRemoved ) { /* restore radicals to atoms */ for ( i = 0; i < pBNS->num_atoms; i ++ ) { pBNS->vert[i].st_edge.cap += pnDelta[i]; } bAtomRadRemoved = 0; } pBNS->edge_forbidden_mask &= ~forbidden_mask; /****************************************************/ /* */ /* Fix the charges */ /* */ /****************************************************/ if ( num_moved ) { /* find reqired charge */ extra_charge = 0; for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) { if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { extra_charge -= delta; } else if ( BNS_VERT_TYPE__AUX == pv->type ) { extra_charge += delta; } else { ret = RI_ERR_PROGR; goto exit_function; } } } if ( !extra_charge ) { goto exit_function; } /* find differences */ num_candidates = 0; for ( i = 0; i < pBNS->num_vertices; i ++ ) { pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i]; if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { num_candidates ++; } } } if ( num_candidates > 0 ) { pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) ); if ( !pCand ) { ret = RI_ERR_ALLOC; goto exit_function; } /* create atom */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) { if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { pCand[j].iat = i; pCand[j].num_bonds = at2[i].valence; pCand[j].chem_valence = at2[i].chem_bonds_valence; pCand[j].cMetal = pVA[i].cMetal; pCand[j].cNumBondsToMetal = pVA[i].cNumBondsToMetal; pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons; pCand[j].cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; pCand[j].el_number = at2[i].el_number; cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0; while ( cnBits > 0 ) { pCand[j].cNumChargeStates ++; cnBits >>= cn_bits_shift; } j ++; } } if ( j > 1 ) { qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand ); } added_charge = 0; for ( k = 0; k < j; k ++ ) { int rest_of_charge = extra_charge - added_charge; int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k); int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom; pVA[pCand[k].iat].cInitCharge += this_atom_add_charge; added_charge += this_atom_add_charge; if ( this_atom_add_charge ) { for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) { if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { if ( delta + this_atom_add_charge > 0 ) { delta = -this_atom_add_charge; } pv->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; this_atom_add_charge += delta; } else if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) { if ( delta > this_atom_add_charge ) { delta = this_atom_add_charge; } pv->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; this_atom_add_charge -= delta; } } } } } } exit_function: if ( pnRad ) { inchi_free( pnRad ); } if ( pnDelta ) { inchi_free( pnDelta ); } if ( pCand ) { inchi_free( pCand ); } return ret; } #endif /**************************************************************************************************/ typedef struct tagMobileHGroups { AT_NUMB group_number; AT_NUMB atom_number; AT_NUMB atom_type_pVA; S_CHAR ineigh; S_CHAR bond_type; S_CHAR forbidden; /* S_CHAR el_type;*/ S_CHAR endpoint_valence; S_CHAR num_bonds; S_CHAR bonds_valence; S_CHAR num_bonds_non_metal; S_CHAR bonds_valence_non_metal; } MOBILE_GR; typedef struct tagMobileGroupList { AT_NUMB group_number; AT_NUMB num; } MGROUPS; /**************************************************************************************************/ int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, int num_atoms, int forbidden_mask ) { int i, j, k; int centerpoint_type, neigh_type; int num_changes; int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints; int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile; int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt; int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal; int num_bonds, bonds_valence; int inv_forbidden_mask = ~forbidden_mask; MOBILE_GR MobileGr[MAXVAL]; int num_endpoints; MGROUPS MGroups[MAXVAL]; int num_mgroups, num_diff_t_groups; BNS_EDGE *e, *e1, *e2, *ev, *ev1, *ev2; BNS_VERTEX *pv1, *pv2; num_changes = 0; /* search for possible centerpoints */ for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H || at[i].endpoint || at[i].charge || at[i].radical || !is_centerpoint_elem(at[i].el_number) || !(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) || 2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) || 2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val ) ) { continue; } num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0; num_mgroups = num_endpoints = num_diff_t_groups = 0; has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0; ind_forbidden = -1; num_forbidden = 0; num_N = num_O = num_P = num_S = num_OSt = 0; num_bonds_non_metal = nNoMetalNumBonds(at, i); bonds_valence = at[i].chem_bonds_valence; num_bonds = at[i].valence; for ( j = 0; j < at[i].valence; j ++ ) { /* collect neighbors info */ neigh = at[i].neighbor[j]; val = get_endpoint_valence( at[neigh].el_number ); forbidden = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden; bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type); if ( !forbidden && !at[neigh].endpoint ) { /* save forbidden bonds */ if ( is_el_a_metal(at[neigh].el_number) ) { continue; } switch( bond_type ) { case BOND_TYPE_SINGLE: if ( !at[neigh].num_H && at[neigh].charge != -1 ) { continue; /* not a donor */ } break; case BOND_TYPE_DOUBLE: if ( !neigh_type ) { continue; } break; default: continue; } } MobileGr[num_endpoints].atom_number = neigh; MobileGr[num_endpoints].ineigh = j; MobileGr[num_endpoints].bond_type = bond_type; MobileGr[num_endpoints].group_number = at[neigh].endpoint; MobileGr[num_endpoints].endpoint_valence = val; MobileGr[num_endpoints].forbidden = forbidden; MobileGr[num_endpoints].atom_type_pVA = neigh_type; MobileGr[num_endpoints].num_bonds = at[neigh].valence; MobileGr[num_endpoints].bonds_valence = at[neigh].chem_bonds_valence; MobileGr[num_endpoints].num_bonds_non_metal = nNoMetalNumBonds(at, neigh); MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh ); if ( forbidden & forbidden_mask ) { num_forbidden ++; ind_forbidden = num_endpoints; } num_O += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */ num_N += 0 != (neigh_type & EL_TYPE_N) && !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */ num_S += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */ num_P += 0 != (neigh_type & EL_TYPE_P) && !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */ num_OSt += 0 != (neigh_type & EL_TYPE_OSt); num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT); num_donors += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) && (at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint); if ( at[neigh].endpoint ) { num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE); num_donor_endpoints += (bond_type == BOND_TYPE_SINGLE); if ( !tg_number ) { tg_number = at[neigh].endpoint; num_eql_mobile_gr = 1; } else if ( tg_number == at[neigh].endpoint ) { num_eql_mobile_gr ++; } else { num_dif_mobile_gr ++; } } else if ( bond_type == BOND_TYPE_SINGLE && val ) { if ( at[neigh].endpoint ) { has_mobile_H |= 1; has_mobile |= 1; } else { has_mobile_H |= (0 != at[neigh].num_H); has_mobile |= (0 != at[neigh].num_H) || (at[neigh].charge == -1); } } num_endpoints ++; if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) { for ( k = 0; k < num_mgroups; k ++ ) { if ( MGroups[k].group_number == at[neigh].endpoint ) { MGroups[k].num ++; break; } } if ( k == num_mgroups ) { MGroups[k].group_number = at[neigh].endpoint; MGroups[k].num = 1; num_mgroups ++; num_diff_t_groups += (0 != at[neigh].endpoint); } } } if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/ num_eql_mobile_gr == num_endpoints && !num_forbidden || !tg_number && !has_mobile_H ) { continue; /* nothing to do */ } /* case_5_1: */ /***************** determine the case ************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_C) && 2 == num_O && 1 == num_N+num_S && num_OSt && 1 == num_forbidden && 3 == num_eql_mobile_gr ) { /******************************************************** *** InChI Tech. Man., Table 5, case 1 *** ******************************************************** 2 OH OH X = N, S, Se, Te / / f = fixed bond e / / tg = Mobile-H vertex HX---C --> X===C ev2|| f \\ ev1 f \ || \\ \ tg------O OH 1 Problem: XH, O, and O belong to the same Mobile-H group. Fixed bond prevents the correct structure restoration: H cannot migrate from X to O because HX-N bond is fixed Solution: Move H from X to allow XH-C bond change (this unfixes the bond, see SetForbiddenEdges(...) ) *********************************************************/ int jXH = -1, jO1 = -1, jO2 = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && (MobileGr[j].forbidden == forbidden_mask) && MobileGr[j].bond_type == BOND_TYPE_SINGLE && jXH < 0 ) { jXH = j; n ++; } else if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && MobileGr[j].num_bonds_non_metal == 1 && !MobileGr[j].forbidden ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) { jO1 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) { jO2 = j; n ++; } } } if ( n != 3 ) { goto case_5_2; } /* XH-C edge */ e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; /* C=O edge */ ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; /* XH-tg edge */ ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; if ( !ev1->flow || !ev2->flow ) { goto case_5_2; } /* do not remove forbidden edge bit */ e->flow ++; ev1->flow --; ev2->flow --; pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_2: /*********************************************************************/ if ( 3 == num_bonds_non_metal && 5 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_N) && 2 == num_O && 1 == num_N+num_S && 1 == num_forbidden && 3 == num_eql_mobile_gr ) { /******************************************************** *** InChI Tech. Man., Table 5, case 2 *** ******************************************************** O OH X = N, S, Se, Te // / f = fixed bond e // / tg = Mobile-H vertex HX---N --> X===N ev2|| f \\ ev1 f \\ || \\ \\ tg------O O Problem: XH, O, and O belong to the same Mobile-H group. Fixed bond prevents the correct structure restoration: H cannot migrate from X to O because HX-N bond is fixed Solution: Move H from X to allow XH-N bond change (this unfixes the bond, see SetForbiddenEdges(...) ) *********************************************************/ int jXH = -1, jO1 = -1, jO2 = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && (MobileGr[j].forbidden == forbidden_mask) && MobileGr[j].bond_type == BOND_TYPE_SINGLE && jXH < 0 ) { jXH = j; n ++; } else if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && MobileGr[j].bond_type == BOND_TYPE_DOUBLE && MobileGr[j].num_bonds_non_metal == 1 && !MobileGr[j].forbidden ) { if ( jO1 < 0 ) { jO1 = j; n ++; } else if ( jO2 < 0 ) { jO2 = j; n ++; } } } if ( n != 3 ) { goto case_5_4; } /* XH-N edge */ e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; /* N=O edge */ ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; /* XH-tg edge */ ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; if ( !ev1->flow || !ev2->flow ) { goto case_5_4; } /* do not remove forbidden edge bit */ e->flow ++; ev1->flow --; ev2->flow --; pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_4: /*********************************************************************/ if ( 3 == num_bonds_non_metal && 5 == bonds_valence_non_metal && (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && 1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) && 1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr ) { /******************************************************** *** InChI Tech. Man., Table 5, case 4 *** ******************************************************** O = O, S, Se, Te X X X = N, P, As // // f = fixed bond e // ev2 // tg = Mobile-H vertex O===N --> HO---N || f \ ev1 f \\ || \ \\ tg------NH N Problem: O, NH, and possibly X belong to the same Mobile-H group. Fixed bond prevents the correct structure restoration: H cannot migrate from NH to O because O=N bond is fixed Solution: Move H from NH to O to allow O=N bond change (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jO = -1, jNH = -1, jX = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && MobileGr[j].forbidden == forbidden_mask && MobileGr[j].bond_type == BOND_TYPE_DOUBLE && MobileGr[j].num_bonds_non_metal == 1 && jO < 0 ) { jO = j; n ++; } else if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) && !MobileGr[j].forbidden ) { if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) { jNH = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) { jX = j; n ++; } } } if ( n != 3 ) { goto case_5_6; } /* O=N edge */ e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; /* N-NH edge */ ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; /* N=X edge */ ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh]; if ( !e->flow ) { goto case_5_6; } /* do not remove forbidden edge bit */ e->flow --; ev1->flow ++; pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_6: /********* InChI Tech.Man. Table 5, case 6 **************/ if ( 2 == delta_met && 4 == num_bonds_non_metal && 5 == bonds_valence_non_metal && 1 == num_forbidden && 1 < num_eql_mobile_gr && !num_dif_mobile_gr && (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && 1 <= num_N && 2 <= num_N+num_O+num_S && 1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) { int jN = -1, njFix = 0, jFix[4], n = 0; /* centerpoint is N, P, As, Sb input output ----- ------ end po- int 2 X ZH X Z Z=N,O,S,Se,Te [terminal endpoint] \ | \ || \| f f \|| Y---N===N--- Y---N---NH--- e f cen end no bond ter po- fixed po- int int 1 tautomerism O==N--NH is allowed Problem: OH and =N- belong to a Mobile-H group, but forbidden edge e does not allow them to be tautomeric in the restored structure. Solution: 1. Decrement flow in edge e 2. Decrement st_edge flow in N and N connected by e 3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint) 4. Run BNS to establist new flow distribution */ /* fixed bond */ for ( j = 0; j < num_endpoints; j ++ ) { neigh = MobileGr[j].atom_number; if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && MobileGr[j].num_bonds_non_metal == 2 && MobileGr[j].bonds_valence_non_metal == 3 && at[neigh].endpoint && !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && (MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) { jN = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && at[neigh].endpoint ) { if ( MobileGr[j].num_bonds > 1 ) { jFix[njFix ++] = j; } n ++; } } if ( jN < 0 || n < 2 || 1 + njFix == n ) { goto case_5_7; /* nothing to do */ } e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh]; /* fixed edge */ if ( !e->flow ) { goto case_5_7; } e->flow --; pBNS->vert[i].st_edge.flow --; pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; pBNS->tot_st_flow -= 2; for ( j = 0; j < njFix; j ++ ) { /* edges to fix */ ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh]; ev->forbidden |= forbidden_mask; } num_changes ++; continue; } case_5_7: /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 2 == num_O+num_S && 1 == num_OSt && 1 == num_N && 1 == num_forbidden && 3 == num_eql_mobile_gr && MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { /******************************************************** *** InChI Tech. Man., Table 5, case 7 *** ******************************************************** O = O, S, Se, Te OH OH S = S, Se, Te / / f = fixed bond e /ev2 f / tg = Mobile-H vertex HN---S --> N===S X = N or non-endpoint; || f \\ \ ev2|| \\ev1 \ tg------O OH N, O, O Problem: ======= O, NH, OH belong to the same Mobile-H group. Fixed bond prevents the correct structure restoration: H cannot migrate from NH to O because HN-S bond is fixed Solution: Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jO = -1, jNH = -1, jOH = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && MobileGr[j].forbidden == forbidden_mask && MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds_non_metal <= 2 && jNH < 0 ) { jNH = j; n ++; } else if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && !MobileGr[j].forbidden && MobileGr[j].num_bonds_non_metal == 1 ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO < 0 ) { jO = j; n ++; } else if ( jOH < 0 ) { jOH = j; n ++; } } } if ( n != 3 ) { goto case_5_9a; } /* NH-S edge */ e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; /* S=O edge */ ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; /* XH-tg edge */ ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; if ( !ev1->flow || !ev2->flow ) { goto case_5_9a; } /* do not remove forbidden edge bit */ e->flow ++; ev1->flow --; ev2->flow --; pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_9a: /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 1 == num_O+num_S && !num_OSt && 1 <= num_N && 1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr && MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { /******************************************************** *** InChI Tech. Man., Table 5, case 9a *** ******************************************************** O = O, S, Se, Te X X S = S, Se, Te / / f = fixed bond / / tg = Mobile-H vertex HN---S --> N===S X = N or non-endpoint; || \\ e \ -X is not -O(terminal) || f\\ f\ tg------O OH N, N, O or N, O Problem: ================ O, NH belong to the same Mobile-H group. Fixed bond prevents the correct structure restoration: H cannot migrate from NH to O because O=S bond is fixed Solution: Move H from NH to =O to allow O=S bond change by making a terminal -OH (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jO = -1, jNH = -1, jX = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && MobileGr[j].forbidden == forbidden_mask && MobileGr[j].bond_type == BOND_TYPE_DOUBLE && MobileGr[j].num_bonds_non_metal == 1 && jO < 0 ) { jO = j; n ++; } else if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && !MobileGr[j].forbidden && MobileGr[j].bond_type == BOND_TYPE_SINGLE && jNH < 0 ) { jNH = j; n ++; } else if ( jX < 0 ) { jX = j; n ++; } } if ( jO < 0 || jNH < 0 ) { goto case_5_8b_to_9b; } e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh]; if ( !e->flow ) { goto case_5_8b_to_9b; } e->flow --; pBNS->vert[e->neighbor1].st_edge.flow --; pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_8b_to_9b: /* #1 */ /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt && 1 == num_forbidden && 1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && 1 == num_donor_endpoints && 0 == num_acceptor_endpoints && 1 == num_donors && 1 == num_acceptors && MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { /******************************************************** *** InChI Tech. Man., Table 5, case 8b->9b *** ******************************************************** ---> O = O, S, Se, Te X X S = S, Se, Te \ f/ \ f/ f = fixed bond \ ev / C=====Z \ / C-----ZH tg = Mobile-H vertex N===S | | N===S || || X = is N not an endpoint; not \ | | \ || || -X is not terminal -O,-S,-Se,-Te or an \ | e | \|| e || any N, P, As endpoint NH=====tg N------tg is an f N, N, X, fixed single endpoint ===================== Problem: N is not a Mobile-H endpoint, NH is a Mobile-H endpoint Unfixed bond N==S prevents the correct structure restoration: H can migrate from NH to N because N=S bond is not fixed Solution: Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jN = -1, jNH = -1, jX = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && !(MobileGr[j].forbidden & forbidden_mask) ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && !at[MobileGr[j].atom_number].endpoint && jN < 0 ) { jN = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds == 2 && MobileGr[j].bonds_valence == 2 && at[MobileGr[j].atom_number].endpoint && jNH < 0 ) { jNH = j; n ++; } } else if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && MobileGr[j].num_bonds > 1 ) && (MobileGr[j].forbidden & forbidden_mask) && MobileGr[j].bond_type == BOND_TYPE_SINGLE && jX < 0 ) { jX = j; n ++; } } if ( n != 3 ) { goto case_5_8c_to_9c; } e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; if ( !e->flow ) { goto case_5_8c_to_9c; /* should not happen ??? */ } e->flow --; pBNS->vert[e->neighbor1].st_edge.flow --; pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; pBNS->tot_st_flow -= 2; e->forbidden |= forbidden_mask; num_changes ++; continue; } case_5_8c_to_9c: /* #2 */ /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 0 == num_O+num_S && 3 == num_N && 0 == num_P && 1 == num_forbidden && 3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && 2 == num_donors && 1 == num_acceptors && MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { /******************************************************** *** InChI Tech. Man., Table 5, case 8c->9c *** ******************************************************** is an endpoint ---> O = O, S, Se, Te NH2(X) NH2(X) S = S, Se, Te \ / pv1 pv2 \ / f = fixed bond \ 1 / C-----ZH \ / C=====Z tg = Mobile-H vertex N===S || || N===S | | X = is N not an endpoint; is an \f||ev1 ||ev2 \f | | -X is not terminal -O,-S,-Se,-Te or endpoint \|| e || \ | e | any N, P, As 2 N------tg NH=====tg C is a centerpoint of a t-group is an f N, N, X, fixed single endpoint ===================== Problem: N is not a Mobile-H endpoint, NH is a Mobile-H endpoint Unfixed bond N==S prevents the correct structure restoration: H can migrate from NH to N because N=S bond is not fixed Solution: Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jN1 = -1, jN2 = -1, jX = -1, n = 0; EdgeIndex ie, ie1, ie2; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && !(MobileGr[j].forbidden & forbidden_mask) ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && at[MobileGr[j].atom_number].endpoint && jN1 < 0 ) { jN1 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds == 2 && MobileGr[j].bonds_valence == 3 && MobileGr[j].forbidden == forbidden_mask && at[MobileGr[j].atom_number].endpoint && jN2 < 0 ) { jN2 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds <= 2 && MobileGr[j].bonds_valence <= 3 && at[MobileGr[j].atom_number].endpoint && jX < 0 ) { jX = j; n ++; } } } if ( n != 3 ) { goto case_5_9b_to_8b; } e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; if ( e->flow ) { goto case_5_9b_to_8b; /* should not happen ??? */ } pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); ie = e - pBNS->edge; ie1 = ie2 = -1; for ( j = 0; j < pv1->num_adj_edges; j ++ ) { ev1 = pBNS->edge + pv1->iedge[j]; if ( ev1->flow && !ev1->forbidden ) { ie1 = ev1 - pBNS->edge; pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); break; } } for ( j = 0; j < pv2->num_adj_edges; j ++ ) { ev2 = pBNS->edge + pv2->iedge[j]; if ( ev2->flow && !ev2->forbidden ) { ie2 = ev2 - pBNS->edge; pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); break; } } if ( ie1 < 0 || ie2 < 0 ) { goto case_5_9b_to_8b; } e->flow ++; e->forbidden |= forbidden_mask; ev1->flow --; ev2->flow --; pv1->st_edge.flow --; pv2->st_edge.flow --; pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_9b_to_8b: /* #3 */ /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 0 == num_O+num_S && 2 == num_N && 0 == num_P && 1 == num_forbidden && 2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && 1 == num_donor_endpoints && 1 == num_acceptor_endpoints && 1 == num_donors && 1 == num_acceptors && MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) { /******************************************************** *** InChI Tech. Man., Table 5, case 9b->8b *** ******************************************************** ---> O = O, S, Se, Te X is an X S = S, Se, Te \ / endpoint \ / f = fixed bond \ 1ev / C-----ZH \ / C=====Z tg = Mobile-H vertex N===S || || N===S | | X = is N not an endpoint; is an f \ || || f \ | | -X is not terminal -O,-S,-Se,-Te or endpoint 2\|| e || \ | e | any N, P, As N------tg NH=====tg is an f N, N, X, fixed double endpoint ===================== Problem: N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group. Fixed bond N1==S prevents the correct structure restoration: H cannot migrate ZH->N2->N1 because N1=S bond is fixed Solution: Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8) (see fix_special_bonds(...) for details ) *********************************************************/ int jN1 = -1, jN2 = -1, jX = -1, n = 0; EdgeIndex ie, ie1, ie2; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && at[MobileGr[j].atom_number].endpoint && (MobileGr[j].forbidden == forbidden_mask) && jN1 < 0 ) { jN1 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds == 2 && MobileGr[j].bonds_valence == 3 && at[MobileGr[j].atom_number].endpoint && !(MobileGr[j].forbidden & forbidden_mask) && jN2 < 0 ) { jN2 = j; n ++; } } else if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && MobileGr[j].num_bonds > 1 ) && !(MobileGr[j].forbidden & forbidden_mask) && MobileGr[j].bond_type == BOND_TYPE_SINGLE && jX < 0 ) { jX = j; n ++; } } if ( jN1 < 0 || jN2 < 0 ) { goto case_5_9c_to_8c; } ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; if ( e->flow ) { goto case_5_9c_to_8c; /* should not happen ??? */ } pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); ie = e - pBNS->edge; ie1 = ie2 = -1; ev->forbidden &= inv_forbidden_mask; for ( j = 0; j < pv1->num_adj_edges; j ++ ) { ev1 = pBNS->edge + pv1->iedge[j]; if ( ev1->flow && !ev1->forbidden ) { ie1 = ev1 - pBNS->edge; pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); break; } } for ( j = 0; j < pv2->num_adj_edges; j ++ ) { ev2 = pBNS->edge + pv2->iedge[j]; if ( ev2->flow && !ev2->forbidden ) { ie2 = ev2 - pBNS->edge; pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); break; } } if ( ie1 < 0 || ie2 < 0 ) { ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */ goto case_5_9c_to_8c; } e->flow ++; e->forbidden |= forbidden_mask; ev1->flow --; ev2->flow --; pv1->st_edge.flow --; pv2->st_edge.flow --; pBNS->tot_st_flow -= 2; num_changes ++; continue; } case_5_9c_to_8c: /* #4 */ /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 0 == num_O+num_S && 3 == num_N && 0 == num_P && 0 == num_forbidden && 2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */ 3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */ 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && 2 == num_donors && 1 == num_acceptors ) { /******************************************************** *** InChI Tech. Man., Table 5, case 8c->9c *** ******************************************************** is an endpoint ---> O = O, S, Se, Te tg1 NH2(X) NH2(X) S = S, Se, Te \ / pv1 pv2 \ / f = fixed bond \(1) / C=====Z \ / C-----ZH tg = Mobile-H vertex N===S | | N===S || || X = is N not an endpoint; is an \ |ev1 | ev2 \ || || -X is not terminal -O,-S,-Se,-Te or endpoint \ | e | \|| e || any N, P, As tg1 (2)NH=====tg N------tg C is a centerpoint of a t-group is an f N, N, X, fixed single endpoint ===================== tg2 Problem: N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge hence prevents the correct structure restoration: H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed Solution: Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c) (this unfixes the bond, see fix_special_bonds(...) ) *********************************************************/ int jN1 = -1, jN2 = -1, jX = -1, n = 0; /* find t-group that is represented by only one neighbor */ for ( j = 0, k = 0; j < num_mgroups; j ++ ) { if ( 1 == MGroups[k].num && MGroups[k].group_number ) { k = MGroups[k].group_number; break; } } if ( !k ) { goto case_5_9c_to_9d; } for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && at[MobileGr[j].atom_number].endpoint && at[MobileGr[j].atom_number].endpoint != k && jN1 < 0 ) { jN1 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds == 2 && MobileGr[j].bonds_valence == 2 && at[MobileGr[j].atom_number].endpoint == k && jN2 < 0 ) { jN2 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds <= 2 && MobileGr[j].bonds_valence <= 3 && at[MobileGr[j].atom_number].endpoint && at[MobileGr[j].atom_number].endpoint != k && jX < 0 ) { jX = j; n ++; } } } if ( n != 3 ) { goto case_5_9c_to_9d; } e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; if ( !e->flow ) { goto case_5_9c_to_9d; /* should not happen ??? */ } e->flow --; pBNS->vert[e->neighbor1].st_edge.flow --; pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; pBNS->tot_st_flow -= 2; e->forbidden |= forbidden_mask; num_changes ++; continue; } case_5_9c_to_9d: /* #6 */ /*********************************************************************/ if ( 3 == num_bonds_non_metal && 4 == bonds_valence_non_metal && (centerpoint_type == EL_TYPE_S) && 0 == num_O+num_S && 3 == num_N && 0 == num_P && 0 == num_forbidden && 3 == num_mgroups && 2 == num_diff_t_groups && 2 == num_donor_endpoints && 0 == num_acceptor_endpoints && 2 == num_donors && 1 == num_acceptors ) { /******************************************************** *** InChI Tech. Man., Table 5, case 9b->8b *** ******************************************************** 3(X) ---> e2| O = O, S, Se, Te NH---is an N====== S = S, Se, Te \ / endpoint \ / f = fixed bond \ 1 / C=====Z \ f / C-----Z tg = Mobile-H vertex N===S | | N===S || || X = is N not an endpoint; is an ev \ | | ev \ || || -X is not terminal -O,-S,-Se,-Te or endpoint 2\ | e1 | \|| e1 || any N, P, As NH=====tg N------tg is an f N, N, X, fixed double endpoint ===================== Problem: N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group. Fixed bond N1==S prevents the correct structure restoration: H cannot migrate N3->N2->N1 because N1=S bond is fixed Solution: Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c) (see fix_special_bonds(...) for details ) *********************************************************/ int jN1 = -1, jN2 = -1, jX = -1, n = 0; for ( j = 0; j < num_endpoints; j ++ ) { if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && !at[MobileGr[j].atom_number].endpoint && !(MobileGr[j].forbidden & forbidden_mask) && jN1 < 0 ) { jN1 = j; n ++; } else if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && MobileGr[j].num_bonds == 2 && MobileGr[j].bonds_valence <= 3 && at[MobileGr[j].atom_number].endpoint && !(MobileGr[j].forbidden & forbidden_mask) ) { if ( jN2 < 0 ) { jN2 = j; n ++; } else if ( jX < 0 ) { jX = j; n ++; } } } } if ( n != 3 ) { goto case_end; } ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; if ( !e->flow ) { goto case_end; } e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; if ( !e1->flow ) { goto case_end; /* should not happen ??? */ } e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1; if ( !e2->flow ) { goto case_end; /* should not happen ??? */ } /* take care of edge e1 */ e = e1; e->flow --; pBNS->vert[e->neighbor1].st_edge.flow --; pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; pBNS->tot_st_flow -= 2; e->forbidden |= forbidden_mask; num_changes ++; /* take care of edge e2 */ e = e2; e->flow --; pBNS->vert[e->neighbor1].st_edge.flow --; pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; pBNS->tot_st_flow -= 2; e->forbidden |= forbidden_mask; num_changes ++; /* take care of edge ev: do not let it change */ ev->forbidden |= forbidden_mask; continue; } case_end:; } /*exit_function:*/ return num_changes; } /******************************************************************************************************/ /* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0 */ /******************************************************************************************************/ int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ) { int ret, ePlus, eMinus; EDGE_LIST NewlyFixedEdges; BNS_EDGE *pPlus, *pMinus; int i, k1, k2, num_found, num_tot, delta, v1, v2; ret = 0; AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { eMinus = pVA[i].nCMinusGroupEdge - 1; ePlus = pVA[i].nCPlusGroupEdge - 1; num_tot += (eMinus >= 0) + (ePlus >= 0); if ( eMinus >= 0 && ePlus >= 0 ) { pPlus = pBNS->edge + ePlus; pMinus = pBNS->edge + eMinus; if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap-pPlus->flow) > 0 ) { num_found ++; } } } if ( !num_found ) { goto exit_function; } if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) { goto exit_function; } for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { eMinus = pVA[i].nCMinusGroupEdge - 1; ePlus = pVA[i].nCPlusGroupEdge - 1; num_tot += (eMinus >= 0) + (ePlus >= 0); if ( eMinus >= 0 && ePlus >= 0 ) { pPlus = pBNS->edge + ePlus; pMinus = pBNS->edge + eMinus; if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap - pPlus->flow) > 0 ) { /* rearrange */ v1 = pMinus->neighbor1; v2 = pMinus->neighbor12 ^ v1; delta = inchi_min(k1,k2); pMinus->flow -= delta; pBNS->vert[v1].st_edge.flow -= delta; pBNS->vert[v2].st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; } /* fix charges */ pPlus->forbidden |= forbidden_edge_mask; pMinus->forbidden |= forbidden_edge_mask; if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) || (ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) { goto exit_function; } } else if ( eMinus >= 0 ) { /* fix charges */ pMinus = pBNS->edge + eMinus; pMinus->forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) { goto exit_function; } } else if ( ePlus >= 0 ) { /* fix charges */ pPlus = pBNS->edge + ePlus; pPlus->forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) { goto exit_function; } } } for ( i = 0; i < pBNS->num_bonds; i ++ ) { pBNS->edge[i].forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) { goto exit_function; } } ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); if ( ret < 0 ) { goto exit_function; } exit_function: AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { #define FIX_BOND_ADD_ALLOC 128 Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower; BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL; int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX; EDGE_LIST CarbonChargeEdges; EDGE_LIST NewlyFixedEdges; ret = 0; num_changes = 0; bFixedCarbonCharges = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); if ( !pTCGroups->num_metal_atoms || 0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) || 0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) { goto exit_function; } memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } for ( i = 0; i < num_at; i ++ ) { if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) { continue; } peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1; if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) { ret = RI_ERR_PROGR; goto exit_function; } pMeFlower = pBNS->vert + vMeFlower0; for ( j = 0; j < at2[i].valence; j ++ ) { if ( !peMeFlower->flow ) { break; /* cannot do anything */ } if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) { /* found a zero order bond */ if ( !bFixedCarbonCharges ) { /* do not let carbon atoms get charged */ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } bFixedCarbonCharges ++; } peZero = pBNS->edge + pBNS->vert[i].iedge[j]; if ( peZero->flow ) { ret = RI_ERR_PROGR; goto exit_function; } /* fix other edges */ for ( k = 0; k < at2[i].valence; k ++ ) { pe = pBNS->edge + pBNS->vert[i].iedge[k]; if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { goto exit_function; } pe->forbidden |= forbidden_edge_mask; } } /* do not create =N(+)= in a ring or #O(+) terminal */ for ( k = 0; k < num_at; k ++ ) { if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 && at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 && pVA[k].nCPlusGroupEdge > 0 && (pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 && !(pe->forbidden & forbidden_edge_mask)) { if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { goto exit_function; } pe->forbidden |= forbidden_edge_mask; } } /* metal's neighbor connected by a zero-order bond */ pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]); /*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/ for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- ) { peNeighMeigh = pBNS->edge + pNeigh->iedge[k]; if ( !peNeighMeigh->flow ) { continue; } vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh; if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) { /* metal neighbor's neighbor connected by a not-zero-order bond */ pNeighNeigh = pBNS->vert + vNeighMeigh; break; /* found */ } } if ( k < 0 ) { continue; /* neighbor not found */ } peZero->flow ++; peZero->forbidden |= forbidden_edge_mask; peMeFlower->flow --; peNeighMeigh->flow --; pMeFlower->st_edge.flow --; pNeighNeigh->st_edge.flow --; pBNS->tot_st_flow -= 2; /* test */ bSuccess = 0; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh || vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_changes ++; bSuccess = ret; } if ( ret = AddToEdgeList( &NewlyFixedEdges, peZero - pBNS->edge, FIX_BOND_ADD_ALLOC )) { goto exit_function; } } else { peZero->flow --; peZero->forbidden &= inv_forbidden_edge_mask; peMeFlower->flow ++; peNeighMeigh->flow ++; pMeFlower->st_edge.flow ++; pNeighNeigh->st_edge.flow ++; pBNS->tot_st_flow += 2; } RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); NewlyFixedEdges.num_edges = 0; if ( bSuccess ) { /* update at2[] */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } } } } } ret = num_changes; exit_function: RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); return ret; } /*********************************************************************** NH2 NH2 \ \ C==S(+)- => C(+)-S- where NH2 are not tautomeric / / NH2 NH2 ************************************************************************/ int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, k, ret, ret2, cur_success; int delta; EdgeIndex ePlusS, ePlusC, eMinusC, e; BNS_VERTEX *pvS, *pvC, *pv1, *pv2; BNS_EDGE *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe; Vertex vC, vN; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; Vertex vPathStart, vPathEnd, v1, v2; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; EDGE_LIST AllChargeEdges; ret = 0; cur_success = 0; AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* find (NH2)C=S(+) */ for ( i = 0; i < num_at; i ++ ) { if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 2 && (pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow && 0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */ (pe1=pBNS->edge + pvS->iedge[0])->flow + (pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ && pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 && at2[vC].valence == 3 && 0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow && !(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) { /* found >C=S(+)- */ pvC = pBNS->vert + vC; for ( j = k = 0; j < at[vC].valence; j ++ ) { if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) { k ++; /* a single bond from C */ } } if ( k != 2 ) { continue; } for ( j = 0; j < k; j ++ ) { vN = peCN[j]->neighbor12 ^ vC; if ( pVA[vN].cNumValenceElectrons != 5 || pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow || at2[vN].num_H != 2 || at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) { break; /* does not fit the pattern */ } } if ( j != k ) { continue; } /* fix all charges */ if ( !AllChargeEdges.num_edges ) { for ( j = 0; j < num_at; j ++ ) { if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { goto exit_function; } if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { goto exit_function; } } } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pePlusS->forbidden &= ~forbidden_edge_mask; pe = pePlusC; if ( !pe->flow ) continue; delta = 1; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Remover (+)charge from S => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } exit_function: AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) /********* Avoid charge separation on heteroatoms ******************/ { int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges; int vPlusSuper; /* (+)super vertex */ int ePlusSuper; /* edge from vPlusSuper to (+/-) */ int vPlusMinus; /* (+/-) vertex */ int nDeltaPlus1, nDeltaMinus1, delta; BNS_VERTEX *pvPlusSuper, *pvPlusMinus; BNS_EDGE *pEdge; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges; ret = 0; AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); bFixedCarbonCharges = 0; if ( forbidden_stereo_edge_mask ) { for ( i = 0; i < num_at; i ++ ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, pStruct->pbfsq->nAtomLevel, pStruct->pbfsq->cSource, 99 /* max ring size */ ); if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) { goto exit_function; } } } } if ( !FixedLargeRingStereoEdges.num_edges ) { goto exit_function; } else { /* allow stereobonds in rings change */ RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); } } memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* count charges */ num_pos = num_neg = 0; for ( i = 0; i < num_at; i ++ ) { if ( !pVA[i].cMetal && !at2[i].radical ) { num_pos += ( at2[i].charge > 0 ); num_neg += ( at2[i].charge < 0 ); } } num_min = inchi_min( num_pos, num_neg ); if ( num_min && (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at && !(pEdge=pBNS->edge + ePlusSuper)->forbidden ) { vPlusMinus = pEdge->neighbor12 ^ vPlusSuper; pvPlusSuper = pBNS->vert + vPlusSuper; pvPlusMinus = pBNS->vert + vPlusMinus; num_min = inchi_min( num_min, pEdge->flow ); nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { if ( !bFixedCarbonCharges ) { /* 02-02-2006 */ /* do not let carbon atoms get charged */ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } bFixedCarbonCharges ++; } delta = 1; pEdge->forbidden |= forbidden_edge_mask; pBNS->edge_forbidden_mask |= forbidden_edge_mask; for ( i = 0; i < num_min; i += delta ) { /* cancel 1 pair of charges at a time */ /* an attempt to cancel all at once may */ /* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */ pEdge->flow -= delta; pvPlusSuper->st_edge.flow -= delta; pvPlusMinus->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; /* test for charhe cancellation */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus || vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { pEdge->flow += delta; pvPlusSuper->st_edge.flow += delta; pvPlusMinus->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; break; } } num_min -= i; /* how many pairs of charges left */ pEdge->forbidden &= inv_forbidden_edge_mask; } nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { delta = 2; pEdge->forbidden |= forbidden_edge_mask; pBNS->edge_forbidden_mask |= forbidden_edge_mask; for ( i = 0; i < num_min; i += delta ) { /* cancel 2 pairs of opposite charges at a time */ /* 1. test cancellation of a pair of (+) charges */ pvPlusSuper->st_edge.cap += delta; pBNS->tot_st_cap += delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } pvPlusSuper->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) { break; } /* 2. test cancellation of a pair of (-) charges */ pvPlusMinus->st_edge.cap += delta; pBNS->tot_st_cap += delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } pvPlusMinus->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) { break; } /* 3. Actually cancel the pair of charges */ pEdge->flow -= delta; pvPlusSuper->st_edge.flow -= delta; pvPlusMinus->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 2 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } num_min -= i; /* how many pairs of charges left */ pEdge->forbidden &= inv_forbidden_edge_mask; } } memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at; exit_function: if ( bFixedCarbonCharges ) { RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); } if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) { SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); } AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); return ret < 0? ret : num_min; } #if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) /********************** not used *************************************************************************/ int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) /********* Avoid charge separation on heteroatoms ******************/ { int i, k, ret, ret2, num_pos, num_neg, num_min; int vPlusSuper, vMinusSuper; /* (+), (-) super vertices */ int ePlusSuper, eMinusSuper; /* edges from vPlusSuper or vMinusSuper to (+/-) */ int vPlMn; /* (+/-) vertex */ int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */ int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */ int vPlusCarbons, vMinusCarbons; /* (+), (-) carbons vertices */ int ePlusCarbons, eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */ int vPlusMetals, vMinusMetals; /* (+), (-) carbons vertices */ int ePlusMetals, eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */ int eMinusHeteroToSuper; /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */ int v1, v2; int nDeltaPlus1, nDeltaMinus1, delta; BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn; BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons; BNS_VERTEX *pvPlusMetals, *pvMinusMetals; BNS_EDGE *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper; BNS_EDGE *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals; BNS_EDGE *pEdgePlusSuper, *pEdgeMinusSuper; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; ret = 0; memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* (+) */ pEdgePlusSuper = NULL; pvPlusSuper = NULL; if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgePlusSuper = pBNS->edge + ePlusSuper; pvPlusSuper = pBNS->vert + vPlusSuper; } pEdgePlusCarbons = NULL; pvPlusCarbons = NULL; if ( (k = pTCGroups->nGroup[TCG_Plus_C0] ) > 0 && (ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgePlusCarbons = pBNS->edge + ePlusCarbons; pvPlusCarbons = pBNS->vert + vPlusCarbons; } pEdgePlusHeteroat = NULL; pvPlusHeteroat = NULL; if ( (k = pTCGroups->nGroup[TCG_Plus0] ) > 0 && (ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat; pvPlusHeteroat = pBNS->vert + vPlusHeteroat; } pEdgePlusMetals = NULL; pvPlusMetals = NULL; if ( (k = pTCGroups->nGroup[TCG_Plus_M0] ) > 0 && (ePlusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgePlusMetals = pBNS->edge + ePlusMetals; pvPlusMetals = pBNS->vert + vPlusMetals; } /* (-) */ pEdgeMinusSuper = NULL; pvMinusSuper = NULL; if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgeMinusSuper = pBNS->edge + eMinusSuper; pvMinusSuper = pBNS->vert + vMinusSuper; } pEdgeMinusCarbons = NULL; pvMinusCarbons = NULL; if ( (k = pTCGroups->nGroup[TCG_Minus_C0] ) > 0 && (eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgeMinusCarbons = pBNS->edge + eMinusCarbons; pvMinusCarbons = pBNS->vert + vMinusCarbons; } pEdgeMinusHeteroat = NULL; pvMinusHeteroat = NULL; pEdgeMinusHeteroToSuper = NULL; if ( (k = pTCGroups->nGroup[TCG_Minus0] ) > 0 && (eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { BNS_VERTEX *pvYMinusHetero; BNS_EDGE *pe; int vYMinusHetero; pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat; pvMinusHeteroat = pBNS->vert + vMinusHeteroat; /* next edge toward (-)super */ if ( pvMinusSuper ) { vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat; pvYMinusHetero = pBNS->vert + vYMinusHetero; for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) { pe = pBNS->edge + pvYMinusHetero->iedge[i]; if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) { pEdgeMinusHeteroToSuper = pe; eMinusHeteroToSuper = pe - pBNS->edge; break; } } } } pEdgeMinusMetals = NULL; pvMinusMetals = NULL; if ( (k = pTCGroups->nGroup[TCG_Minus_M0] ) > 0 && (eMinusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { pEdgeMinusMetals = pBNS->edge + eMinusMetals; pvMinusMetals = pBNS->vert + vMinusMetals; } /* (+/-) */ pvPlMn = NULL; if ( pEdgePlusSuper ) { vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper; pvPlMn = pBNS->vert + vPlMn; } else if ( pEdgeMinusSuper ) { vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper; pvPlMn = pBNS->vert + vPlMn; } num_pos = num_neg = 0; /***************************************************************/ /* Positive Charges */ /***************************************************************/ if ( pEdgePlusHeteroat && pEdgePlusMetals ) { /* count charges */ for ( i = 0; i < num_at; i ++ ) { if ( !at2[i].radical && at2[i].charge > 0 && (k = pVA[i].nCPlusGroupEdge-1) >= 0 ) { v1 = pBNS->edge[k].neighbor1; v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) { num_pos ++; } } } /* attempt to move (+) from heteroatoms to metal atoms */ num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow ); nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { if ( pEdgePlusSuper ) { pEdgePlusSuper->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusSuper ) { pEdgeMinusSuper->forbidden |= forbidden_edge_mask; } if ( pEdgePlusCarbons ) { pEdgePlusCarbons->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusCarbons ) { pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; } if ( pEdgePlusHeteroat ) { pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusHeteroat ) { pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask; } delta = 1; for ( i = 0; i < num_min; i += delta ) { v1 = pEdgePlusHeteroat->neighbor1; v2 = pEdgePlusHeteroat->neighbor12 ^ v1; pEdgePlusHeteroat->flow -= delta; pBNS->vert[v1].st_edge.flow -= delta; pBNS->vert[v2].st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; /* test for charhe cancellation */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { pEdgePlusHeteroat->flow += delta; pBNS->vert[v1].st_edge.flow += delta; pBNS->vert[v2].st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; break; } } if ( pEdgePlusSuper ) { pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusSuper ) { pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; } if ( pEdgePlusCarbons ) { pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusCarbons ) { pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; } if ( pEdgePlusHeteroat ) { pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusHeteroat ) { pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask; } } } /***************************************************************/ /* Negative Charges */ /***************************************************************/ if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) { /* count charges */ for ( i = 0; i < num_at; i ++ ) { if ( !at2[i].radical && at2[i].charge < 0 && (k = pVA[i].nCMinusGroupEdge-1) >= 0 ) { v1 = pBNS->edge[k].neighbor1; v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) { num_neg ++; } } } if ( num_neg ) { /* attempt to move (+) from heteroatoms to metal atoms */ num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow ); } nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { if ( pEdgePlusSuper ) { pEdgePlusSuper->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusSuper ) { pEdgeMinusSuper->forbidden |= forbidden_edge_mask; } if ( pEdgePlusCarbons ) { pEdgePlusCarbons->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusCarbons ) { pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; } if ( pEdgePlusHeteroat ) { pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; } if ( pEdgeMinusHeteroToSuper ) { pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask; } delta = 1; for ( i = 0; i < num_min; i += delta ) { v1 = pEdgeMinusHeteroToSuper->neighbor1; v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1; pEdgeMinusHeteroToSuper->flow -= delta; pBNS->vert[v1].st_edge.flow -= delta; pBNS->vert[v2].st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; /* test for charhe cancellation */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { pEdgeMinusHeteroToSuper->flow += delta; pBNS->vert[v1].st_edge.flow += delta; pBNS->vert[v2].st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; break; } } if ( pEdgePlusSuper ) { pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusSuper ) { pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; } if ( pEdgePlusCarbons ) { pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusCarbons ) { pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; } if ( pEdgePlusHeteroat ) { pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; } if ( pEdgeMinusHeteroToSuper ) { pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask; } } } exit_function: return ret; } #endif /******************************************************************************************************/ int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pe; int i, j, ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex v1, v2; EDGE_LIST CarbonChargeEdges; ret = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } for ( i = 0; i < num_at && 0 <= ret; i ++ ) { if ( at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].chem_bonds_valence == 2 && at2[i].charge == -1 && at2[i].radical == 0 && pVA[i].cNumValenceElectrons == 5 && /* terminal N(-)=, P, As, Sb, Bi */ pVA[i].nCMinusGroupEdge > 0 && pVA[i].nTautGroupEdge == 0 && at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].chem_bonds_valence == 4 && at2[j].charge == 0 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */ pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { /* found N(-)=C= */ pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */ if ( !pe->flow ) { continue; /* wrong atom ??? Strange... */ } v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; pe->forbidden |= forbidden_edge_mask; /* do not let carbon atoms get charged */ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */ } } exit_function: AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { #define INC_EDGE_LIST 16 Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success; BNS_EDGE *pe; Vertex v1, v2; int i, j, ret2, ret, bIsCarbon; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge; EdgeIndex eNFlowerEdge1; EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges; ret = 0; num_failed = num_success = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */ AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); /* heteroatom charge edges */ AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR ); /* C in C(+)#N(+) charge edges */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* 1st attempt: take care of C(+)#N(+)- => C(-)#N(+)- and remove 2 negative charges */ /* This would produce nDeltaCharge = 2 */ AllocEdgeList( &CarbonChargeEdges, 2*num_at ); for ( i = 0; i < num_at && 0 <= ret; i ++ ) { /* accumulate edges for subsequent fixing them */ bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1); eNFlowerEdge1 = NO_VERTEX; if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge].forbidden ) { if ( bIsCarbon ) { if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } else if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) { if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } } if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNPlusEdge].forbidden ) { if ( bIsCarbon ) { if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } else if ( !pVA[i].cMetal && !at2[i].endpoint ) { if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( pVA[i].cNumValenceElectrons == 5 && NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && !pBNS->edge[eNFlowerEdge1].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { goto exit_function; } } } } if ( bIsCarbon && 0 <= eNMinusEdge && 0 <= eNPlusEdge && at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].radical == 0 && !pBNS->edge[eNMinusEdge].forbidden && pBNS->edge[eNMinusEdge].flow == 0 && !pBNS->edge[eNPlusEdge].forbidden && pBNS->edge[eNPlusEdge].flow == 0 && /* found terminal C(+) */ at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 5 && (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 ) { /* -N(+)- */ #ifdef NEVER /* I have not found a good reason to do this yet */ /* fix (+) charge on -N(+)- as much as C charges are fixed */ if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } /* fix floer edge to prevent N(V) ??? */ if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) && !pBNS->edge[eNFlowerEdge1].flow ) { if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { goto exit_function; } } #endif /* Carbon(+) Carbon(-) ChargeStruct: ChargeStruct: 5(+C) 5(+C) / // 6(-C) 4 6(-C) 4 \ // \\ / 3 3 | | 2 2 || || -C1- -C1- | | 3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge To convert the left pattern to the right one: We need to release these charge edges and decrement edge 3-4 flow to change charge from (+) to (-) */ /* find vertices 4 and 5 */ v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */ v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1; if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) { /* v1 is 5(+C) */ Vertex tmp = v1; v1 = v2; v2 = tmp; } /* v1 should be 4, v2 - 5(+C) */ if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) { continue; /* mismatch */ } /* find edge 3-4 */ eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge]; if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) { continue; } /* save 3 edges: 6-3, 4-5, and 3-4 in this order */ if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) { goto exit_function; } } } /* 1st attempt: move (-) charges from heteroatoms to C(+) */ SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; pe = pBNS->edge + eN34Edge; pe->forbidden |= forbidden_edge_mask; if ( !pe->flow ) { continue; /* already done */ } v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; pe->forbidden &= inv_forbidden_edge_mask; num_failed ++; } } if ( num_failed ) { /* relax conditions: allow all heteroatoms to change charge */ RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; pe = pBNS->edge + eN34Edge; pe->forbidden |= forbidden_edge_mask; if ( !pe->flow ) { continue; /* already done */ } v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */ num_failed ++; } } } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); exit_function: AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE ); return ret; #undef INC_EDGE_LIST } /******************************************************************************************************/ int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { #define INC_EDGE_LIST 16 Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; int num_failed, num_success, n, nDeltaChargeMax, nMetalCharge; BNS_EDGE *pe; Vertex v1, v2; int i, j, k, ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2; EDGE_LIST AllChargeEdges; ret = 0; num_failed = num_success = 0; AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* attepmt #1 N#N(+)-N => N(-)=N(+)=N */ for ( i = 0; i < num_at && 0 <= ret; i ++ ) { if ( at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].radical == 0 && pVA[i].cNumValenceElectrons == 6 && /* terminal -O */ (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 && !pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */ at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 5 && (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 && !pBNS->edge[eNMinusEdge1].forbidden && pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal && (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge2].forbidden && (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNPlusEdge2].forbidden ) { /* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */ /* find all charge edges to fix */ if ( 0 == AllChargeEdges.num_edges ) { for ( n = 0; n < num_at; n ++ ) { if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 && !pBNS->edge[e].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { goto exit_function; } } if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[e].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { goto exit_function; } if ( pVA[n].cNumValenceElectrons == 6 && NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && pBNS->edge[e].flow == 0 ) { if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { goto exit_function; } } } } } nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow) - pBNS->edge[eNMinusEdge2].flow; if ( nMetalCharge == 0 ) { /* change on O is invisible; charge from N(-) goes, charge comes to Metal */ nDeltaChargeMax = 0; } else if ( nMetalCharge == 2 ) { /* charges on Metal and N disappear */ nDeltaChargeMax = -2; } else { /* charge from N disappears */ nDeltaChargeMax = -1; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask; pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask; pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */ v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; num_failed ++; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } ret = num_success; exit_function: AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); return ret; #undef INC_EDGE_LIST } /******************************************************************************************************/ int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { #define INC_EDGE_LIST 16 Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax; BNS_EDGE *pe; Vertex v1, v2; int i, j, k, ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2; EdgeIndex eNFlowerEdge1, eNFlowerEdge2; EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges; ret = 0; num_failed = num_success = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR ); AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */ for ( i = 0; i < num_at && 0 <= ret; i ++ ) { if ( at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].chem_bonds_valence == 3 && at2[i].charge == 0 && at2[i].radical == 0 && pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 && !pBNS->edge[eNMinusEdge].forbidden && at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].chem_bonds_valence == 4 && at2[j].charge == 1 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 5 && (eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 && !pBNS->edge[eNPlusEdge].forbidden && at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && at2[k].num_H == 0 && at2[k].chem_bonds_valence == 3 && pVA[k].cNumValenceElectrons == 5 && (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 && !pBNS->edge[eNPlusEdge2].forbidden && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { /* found N#N(+)-N~ where the last N (at2[k]) may be charged */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */ v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; pe->forbidden |= forbidden_edge_mask; pBNS->edge[eNPlusEdge].forbidden |= forbidden_edge_mask; pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask; if ( !CarbonChargeEdges.num_edges ) { /* do not let carbon atoms get charged */ AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } } else { SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; /* fix charges on N(-)=N(+)=N- */ if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; num_failed ++; } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); pe->forbidden &= inv_forbidden_edge_mask; pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask; pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; } } /* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */ /* This would produce nDeltaCharge >= 2 */ AllChargeEdges.num_edges = 0; AllNNNTermAtoms.num_edges = 0; NNNChargeEdges.num_edges = 0; AllNIIIChargeEdges.num_edges = 0; for ( i = 0; i < num_at && 0 <= ret; i ++ ) { if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } else { eNMinusEdge = -1; } if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNPlusEdge].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) { if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { goto exit_function; } } /* N flower edge */ if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && pBNS->edge[eNFlowerEdge1].flow == 0 && ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { goto exit_function; } } else { eNPlusEdge = -1; } if ( 0 <= eNMinusEdge && at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].radical == 0 && pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 5 && (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge1].forbidden && !pBNS->edge[eNPlusEdge1].forbidden && at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && at2[k].num_H == 0 && at2[k].radical == 0 && pVA[k].cNumValenceElectrons == 5 && (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge2].forbidden && !pBNS->edge[eNPlusEdge2].forbidden && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ /* 1. N(-)=N(+)=N- */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { continue; /* already good */ } /* accumulate terminal atoms of all other NNN */ if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) { goto exit_function; } /* 2. N#N(+)-N= */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* unfix (-) edge on terminal N# */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } continue; } /* 3. N(-)=N-N= */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* unfix (+) edge on middle N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } continue; } /* 4. N#N(+)-N(-)- */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { /* unfix (-) edge on the 1st and 3rd N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } continue; } /* 5. N#N(+)-N(+)# */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } continue; } } } /* try to fix each NNN */ for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) { i = AllNNNTermAtoms.pnEdges[n]; eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ j=at2[i].neighbor[0]; eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1; eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1; k=at2[j].neighbor[at2[j].neighbor[0]==i]; eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1; eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1; /*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/ /* 1. N(-)=N(+)=N- */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); pe = NULL; } else /* 2. N#N(+)-N= */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* decrement triple bond on terminal N# */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; } else /* 3. N(-)=N-N= */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* decrement flow on (+) charge edge of the middle =N- */ pe = pBNS->edge + eNPlusEdge1; } else /* 4. N#N(+)-N(-)- */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { /* decrement triple bond on terminal N# */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; } else /* 5. N#N(+)-N(+)# */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { /* decrement triple bond on terminal N# */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; } else { pe = NULL; /* unknown case */ } if ( pe ) { SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; pe->forbidden |= forbidden_edge_mask; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; /* fix charges on N(-)=N(+)=N- */ RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; num_failed ++; } pe->forbidden &= inv_forbidden_edge_mask; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } /* 3rd attempt */ /* AllChargeEdges.num_edges = 0; AllNNNTermAtoms.num_edges = 0; NNNChargeEdges.num_edges = 0; */ for ( i = 0; i < num_at && 0 <= ret; i ++ ) { eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ if ( 0 <= eNMinusEdge && at2[i].valence == 1 && at2[i].num_H == 0 && at2[i].radical == 0 && pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ at2[j=at2[i].neighbor[0]].valence == 2 && at2[j].num_H == 0 && at2[j].radical == 0 && pVA[j].cNumValenceElectrons == 5 && (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge1].forbidden && !pBNS->edge[eNPlusEdge1].forbidden && at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && at2[k].num_H == 0 && at2[k].radical == 0 && pVA[k].cNumValenceElectrons == 5 && (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && !pBNS->edge[eNMinusEdge2].forbidden && !pBNS->edge[eNPlusEdge2].forbidden && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ NNNChargeEdges.num_edges = 0; eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 ); eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 ); /* 1. N(-)=N(+)=N- */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* fix charges on N(-)=N(+)=N- */ if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } continue; /* already good */ } /* 2. N#N(+)-N= */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* unfix (-) edge on terminal N# */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } pe = pBNS->edge + pBNS->vert[i].iedge[0]; nDeltaChargeMax = 0; nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0; } else /* 3. N(-)=N-N= */ if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { /* unfix (+) edge on middle N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } if ( NO_VERTEX != eNFlowerEdge1 && ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { goto exit_function; } /* decrement flow on (+) charge edge of the middle =N- */ pe = pBNS->edge + eNPlusEdge1; nDeltaChargeMax = 2; } else /* 4. N#N(+)-N(-)- */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { /* unfix (-) edge on the 1st and 3rd N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } /* decrement triple bond on terminal N# */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; nDeltaChargeMax = 0; } else /* 5. N#N(+)-N(+)# */ if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } /* decrement triple bond on terminal N# */ pe = pBNS->edge + pBNS->vert[i].iedge[0]; nDeltaChargeMax = 0; } else { continue; } if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) { if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { goto exit_function; } } if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) { if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) { goto exit_function; } } v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow --; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; pe->forbidden |= forbidden_edge_mask; if ( !CarbonChargeEdges.num_edges ) { /* do not let carbon atoms get charged */ AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } } else { SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); } SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; *pnTotalDelta += ret; num_success ++; /* fix charges on N(-)=N(+)=N- */ if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { goto exit_function; } } else { pe->flow ++; pBNS->vert[v1].st_edge.flow ++; pBNS->vert[v2].st_edge.flow ++; pBNS->tot_st_flow += 2; num_failed ++; } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); pe->forbidden &= inv_forbidden_edge_mask; /*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */ } } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); exit_function: AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE ); AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE ); return ret; #undef INC_EDGE_LIST } /******************************************************************************************************/ int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, k, bForbiddenCarbonCharges, ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EDGE_LIST CarbonChargeEdges; ret = 0; bForbiddenCarbonCharges = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* forbid creation of other N(V) atoms */ /* fix single bonds to metals */ for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cNumValenceElectrons == 5 && 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && 1 == pBNS->edge[k].flow) { pBNS->edge[k].forbidden |= forbidden_edge_mask; } else if ( pVA[i].cMetal ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; } } } } /*------------------------------------------------------------------------------ (+) single line => flow = 0 (+)-(Y)=(+)super 01 // double line => flow = 1 fix-> 01 // <-- fix 1 --- 0 1 === 0 \\ // edge eij connects vertices i < j: \ / 02 12 2 02 <--- edge number: e02 connects vertices v0 12 2(..) <- double 'radical' | v0 and v2 | =N= vertex N has number i =N= | | --------------------------------------------------------------------------------*/ for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical && !(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && pVA[i].nCPlusGroupEdge > 0 ) { Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX; EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01; BNS_VERTEX *pv0, *pv1, *pv2 = NULL; BNS_EDGE *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL; Vertex vPathStart, vPathEnd; int nPathLen; int nDeltaH, nDeltaCharge, nNumVisitedAtoms; iePlus = pVA[i].nCPlusGroupEdge - 1; pePlus = pBNS->edge + iePlus; v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )? (pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1; pv0 = pBNS->vert + v0; for ( j = 0; j < pv0->num_adj_edges; j ++ ) { ie = pv0->iedge[j]; if ( ie == iePlus ) { continue; } pe = pBNS->edge + ie; if ( pe->flow == 1 && v2 == NO_VERTEX ) { /* 0 - 2, edge 02 */ v2 = pe->neighbor12 ^ v0; pv2 = pBNS->vert + v2; ie02 = ie; pe02 = pe; } else if ( pe->flow == 0 && v1 == NO_VERTEX ) { /* 0 - 1, edge 01 */ v1 = pe->neighbor12 ^ v0; pv1 = pBNS->vert + v2; ie01 = ie; pe01 = pe; } else { ret = RI_ERR_PROGR; goto exit_function; } } if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) { ret = RI_ERR_PROGR; goto exit_function; } for ( j = 0; j < pv2->num_adj_edges; j ++ ) { ie = pv2->iedge[j]; pe = pBNS->edge + ie; v = pe->neighbor12 ^ v2; if ( v == v0 || v == i ) { continue; } else if ( v == v1 && pe->flow == 1 ) { /* 1 - 2, edge 12 */ ie12 = ie; pe12 = pe; } else { ret = RI_ERR_PROGR; goto exit_function; } } if ( ie12 == NO_VERTEX ) { ret = RI_ERR_PROGR; goto exit_function; } /* rearrange cap and flow, forbid 2 edges */ pe01->flow = 1; pe12->flow = 0; pe02->flow = 0; pv2->st_edge.flow -= 2; pBNS->tot_st_flow -= 2; pePlus->forbidden |= forbidden_edge_mask; pe01->forbidden |= forbidden_edge_mask; if ( !bForbiddenCarbonCharges ) { if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } bForbiddenCarbonCharges = 1; } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); } else { pe01->flow = 0; pe12->flow = 1; pe02->flow = 1; pv2->st_edge.flow += 2; pBNS->tot_st_flow += 2; } pePlus->forbidden &= inv_forbidden_edge_mask; pe01->forbidden &= inv_forbidden_edge_mask; if ( ret < 0 ) { goto exit_function; } else if ( ret ) { memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } } } } exit_function: /* allow creation of other N(V) atoms */ for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cNumValenceElectrons == 5 && 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && 1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) { pBNS->edge[k].forbidden &= inv_forbidden_edge_mask; } else if ( pVA[i].cMetal ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask; } } } } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EDGE_LIST CarbonChargeEdges, FlowerEdgesList; ret = 0; bForbiddenCarbonCharges = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR ); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* forbid creation of other S(IV) atoms */ /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ for ( i = 0; i < num_at; i ++ ) { if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) && 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && !pBNS->edge[k].forbidden && 6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) { pBNS->edge[k].forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { goto exit_function; } } else if ( pVA[i].cMetal ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { goto exit_function; } } } } else /* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */ /* exactly same if(..) as below */ if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 && at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical && !at2[i].endpoint && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && pBNS->edge[nFlowerEdge].flow > 0 ) { for ( j = 0; j < at2[i].valence; j ++ ) { neigh = at2[i].neighbor[j]; if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) { k = pBNS->vert[i].iedge[j]; if ( !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { goto exit_function; } pBNS->edge[k].forbidden |= forbidden_edge_mask; } } } } } /*------------------------------------------------------------------------------ example: struct #301, | | disconnected porphyrin with four -SO3(-) -S- => =S= | | -------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------- found: super(+)=(Y) super(+)=(Y) \ \ (+) single line => flow = 0 (+) (+) 01 // double line => flow = 1 fix-> 01 // 01 // 1 === 0 triple line => flow = 2 (.)1 --- 0(.) ---> 1 --- 0 \ / edge eij connects vertices i 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && /* 01 is nFlowerEdge */ 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && pBNS->edge[nFlowerEdge].flow > 0 ) { Vertex v1 = NO_VERTEX, v2 = NO_VERTEX; BNS_VERTEX *pv1, *pv2; BNS_EDGE *pe; Vertex vPathStart, vPathEnd; int nPathLen; int nDeltaH, nDeltaCharge, nNumVisitedAtoms; if ( !bForbiddenCarbonCharges ) { if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } bForbiddenCarbonCharges = 1; } delta = 1; pe = pBNS->edge + nFlowerEdge; /* edge 01 */ pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); } else { pe->forbidden &= inv_forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } if ( ret < 0 ) { goto exit_function; } else if ( ret ) { memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } /* store the fixed edge to unfix it upon exit */ if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) { goto exit_function; } } } } exit_function: RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask ); AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE ); return ret; } /****************************************************************************************************** =N(+)=O =N-O(-) => M(q) M(q+2) *******************************************************************************************************/ int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList; Vertex v1, v2; BNS_VERTEX *pv1, *pv2; BNS_EDGE *pe; Vertex vPathStart, vPathEnd; int nPathLen; int nDeltaH, nDeltaCharge, nNumVisitedAtoms; if ( !pTCGroups->num_metal_atoms ) return 0; ret = 0; bForbiddenCarbonCharges = 0; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */ AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */ AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR ); /* N(+)=O edges */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } num_NO = num_M = 0; /* forbid creation of other S(IV) atoms */ /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ for ( i = 0; i < num_at; i ++ ) { if ( !pVA[i].cMetal ) { if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { goto exit_function; } } if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { goto exit_function; } } } else { num_M ++; } /* if ( pVA[i].cMetal ) { if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { goto exit_function; } } if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { goto exit_function; } } } else */ if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 && at2[i].charge == 0 && !at2[i].num_H && 1 == at2[i].valence && 2 == at2[i].chem_bonds_valence && pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 && at2[j].charge == 1 && !at2[j].num_H && 2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) { /* found =N(+)=O */ if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ && (n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) { if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) || (ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) { goto exit_function; } k = pBNS->vert[i].iedge[0]; /* N(+)=O bond */ if ( !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) { goto exit_function; } num_NO ++; } } } } if ( num_M && num_NO ) { SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask ); /* now only N(+), O(-) and metal charges are allowed to change */ for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) { k = NO_EdgeList.pnEdges[i]; delta = 1; pe = pBNS->edge + k; /* edge N(+)=O */ pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); } else { pe->forbidden &= inv_forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } if ( ret < 0 ) { goto exit_function; } } } exit_function: RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE ); AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int nNumFixedEdges, nNumAdjEdges; ret = 0; if ( pTCGroups->num_tgroups ) { memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } #if ( FIND_RING_SYSTEMS == 1 ) ret2 = MarkRingSystemsInp( at2, num_at, 0 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } #endif /* --- forbidden edges --- */ ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask ); if ( ret2 < 0 ) { ret2 = -(ret + 1); } nNumFixedEdges = ret2; ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask ); nNumAdjEdges = ret; if ( ret ) { pBNS->edge_forbidden_mask |= forbidden_edge_mask; ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else { *pnTotalDelta += ret; } } if ( nNumFixedEdges || nNumAdjEdges ) { /* removes this edge mask from ALL edges */ RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask ); } } exit_function: return ret; } /******************************************************************************************************/ /* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, num_fixes, tot_num_fixes = 0; int ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int itg, j, k, n, m; Vertex vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint; Vertex centerpoint_found=NO_VERTEX; BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; BNS_EDGE *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2; BNS_EDGE *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; int tgroup_number, num_endpoints; ret = 0; memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) { num_fixes = 0; for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { pCentp_found=NULL; tgroup_number = pTCGroups->pTCG[itg].ord_num; vtg1 = pTCGroups->pTCG[itg].nVertexNumber; /* taut group vertex index */ ptg1 = pBNS->vert + vtg1; /* taut group vertex */ num_endpoints = pTCGroups->pTCG[itg].num_edges; for ( i = 0; i < num_endpoints; i ++ ) { etg0 = pBNS->edge + ptg1->iedge[i]; /* edge from t-group to endpoint */ endpoint0 = etg0->neighbor12 ^ vtg1; /* taut endpoint vertex index */ pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { /* radical endpoint1 has been detected */ /* find a 1-3 centerpoint that has two or more endpoints */ /* connected to the t-group vertex by edges with flow>0 and */ /* to the centerpoint by edges with flow = 0 */ /* after that: (1) increment etg1 flow to eliminate radical */ /* (2) increment flow on one of the two other edges to the t-group */ /* (3) increment st_cap on the found centerpoint */ /* (4) rerun the BNS and re-create the structure */ break; } } if ( i == num_endpoints ) { continue; } if ( i < num_endpoints ) { /* tautomeric endpoint found; traverse its t-group edges */ for ( j = 0; j < num_endpoints; j ++ ) { if ( i == j ) { continue; /* avoid the already found radical endpoint */ } etg1 = pBNS->edge + ptg1->iedge[j]; /* another edge from t-group to another endpoinr */ endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */ pEndp1 = pBNS->vert + endpoint1; /* another endpoint vertex */ if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { continue; /* one more radical-endpoint! What is going on here??? */ } if ( !etg1->flow ) { continue; /* avoid enpoints that do not have an attachment */ } if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { continue; /* should not happen */ } /* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */ for ( k = 0; k < at2[endpoint1].valence; k ++ ) { ecp1 = pBNS->edge + pEndp1->iedge[k]; if ( ecp1->flow ) { continue; } centerpoint = ecp1->neighbor12 ^ endpoint1; pCentp = pBNS->vert + centerpoint; /* traverse centerpoint edges to find a single bond to the 2nd endpoint */ for ( n = 0; n < at2[centerpoint].valence; n ++ ) { ecp2 = pBNS->edge + pCentp->iedge[n]; if ( ecp2->flow ) { continue; } endpoint2 = ecp2->neighbor12 ^ centerpoint; if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) { continue; /* don't go back: neighbors are in order of ascending ord. numbers */ } pEndp2 = pBNS->vert + endpoint2; if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) { continue; } etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1; if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) { continue; } /* we have found the path: Endp1 Endp1 etg1 // \ ecp1 etg1 / \\ ecp1 etg0 // \ etg0 / \\ Endp0-----tg1 Centp --> Endp0=====tg1 Centp ^ \\ / \\ / radical | etg2 \\ / ecp2 etg2 \\ / ecp2 Endp2 Endp2 */ /* compare centerpoints */ if ( !pCentp_found || /* try to avoid carbons */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1) && pVA[centerpoint_found].cNumValenceElectrons == 4 && pVA[centerpoint_found].cPeriodicRowNumber == 1 || /* try a better non-carbon */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1 ) && (at[centerpoint].valence > at[centerpoint_found].valence || at[centerpoint].valence == at[centerpoint_found].valence && at[centerpoint].el_number > at[centerpoint_found].el_number) ) { pCentp_found = pCentp; etg1_found = etg1; ecp1_found = ecp1; centerpoint_found = centerpoint; break; } } } } } if ( pCentp_found ) { /* ---- (1) */ etg0->flow ++; pEndp0->st_edge.flow ++; /* ---- (2) */ etg1_found->flow --; /* ---- (3) */ ecp1_found->flow ++; /* ---- (4) */ pCentp_found->st_edge.flow ++; pCentp_found->st_edge.cap ++; pBNS->tot_st_flow += 2; pBNS->tot_st_cap += 1; pCentp_found = NULL; num_fixes ++; tot_num_fixes ++; /* #1 Mob-H */ continue; } /* 2nd attempt: increment flow in centerpoint---radical_endpint edge */ if ( i < num_endpoints ) { /* tautomeric endpoint found; traverse its t-group edges */ for ( j = 0; j < num_endpoints; j ++ ) { if ( i == j ) { continue; /* avoid the found radical endpoint */ } etg1 = pBNS->edge + ptg1->iedge[j]; endpoint1 = etg1->neighbor12 ^ vtg1; pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { continue; /* one more radical-endpoint! What is going on here??? */ } if ( !etg1->flow ) { continue; /* avoid enpoints that do not have an attachment */ } if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { continue; /* should not happen */ } /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ for ( k = 0; k < at2[endpoint1].valence; k ++ ) { ecp1 = pBNS->edge + pEndp1->iedge[k]; if ( ecp1->flow ) { continue; } centerpoint = ecp1->neighbor12 ^ endpoint1; pCentp = pBNS->vert + centerpoint; if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) { continue; /* do not set another endpoint's valence = an unusual value */ } /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ ecp2 = NULL; pEndp2 = NULL; for ( n = 0; n < at2[centerpoint].valence; n ++ ) { ecp0 = pBNS->edge + pCentp->iedge[n]; if ( ecp0->flow ) { endpoint2 = ecp0->neighbor12 ^ centerpoint; if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) { continue; /* ignore endpoint2 if it is tautomeric endpoint */ } /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { if ( at[centerpoint].sb_ord[m] == n ) { endpoint2 = NO_VERTEX; break; } } if ( endpoint2 == NO_VERTEX ) { continue; } pEndp2 = pBNS->vert + endpoint2; /* found */ ecp2 = ecp0; break; } } for ( n = 0; n < at[centerpoint].valence; n ++ ) { ecp0 = pBNS->edge + pCentp->iedge[n]; if ( ecp0->flow ) { continue; } if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { continue; } /* Found: Endp2(not endpoint) Endp2(not radical) || | ||ecp2 |ecp2 ecp0 || ecp1 ecp0 | ecp1 Endp0----Centp----Endp1 Endp0====Centp----Endp1 ^ \ / --> \ / radical | \ / \ / \ / \ / etg0 \ / etg1 etg0 \ / etg1 \ / \ / tg1 tg1 */ /* compare centerpoints */ if ( !pCentp_found || /* try to avoid carbons */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1) && pVA[centerpoint_found].cNumValenceElectrons == 4 && pVA[centerpoint_found].cPeriodicRowNumber == 1 || /* try a better non-carbon */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1 ) && (at[centerpoint].valence > at[centerpoint_found].valence || at[centerpoint].valence == at[centerpoint_found].valence && at[centerpoint].el_number > at[centerpoint_found].el_number) ) { pCentp_found = pCentp; etg1_found = etg1; ecp0_found = ecp0; centerpoint_found = centerpoint; pEndp2_found = pEndp2; ecp2_found = ecp2; break; } } } } } if ( pCentp_found ) { ecp0_found->flow ++; if ( ecp0_found->cap < ecp0_found->flow ) { ecp0_found->cap = ecp0_found->flow; } pEndp0->st_edge.flow ++; if ( pEndp2_found && ecp2_found ) { ecp2_found->flow --; pEndp2_found->st_edge.flow --; } else { /* Endp2 not found */ pCentp_found->st_edge.flow ++; pCentp_found->st_edge.cap ++; pBNS->tot_st_flow += 2; /* radical elimination */ pBNS->tot_st_cap += 1; } pCentp_found = NULL; num_fixes ++; tot_num_fixes ++; /* #2 Mob-H */ continue; } /* 3rd attempt: find =C= and move radical to it */ if ( i < num_endpoints ) { int jj, delta, bNotFixed = 1; Vertex vPathStart, vPathEnd, v1, v2; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) { if ( at2[i].endpoint ) { continue; } if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow && 4 == pVA[jj].cNumValenceElectrons && !(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden && !(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden && 1 == ecp0->flow && 1 == ecp1->flow && !at2[(int)at2[i].neighbor[0]].sb_parity[0] && !at2[(int)at2[i].neighbor[1]].sb_parity[0] ) { /* found =C=; make a radical and try to cancel the two radicals */ k = ecp0->neighbor12 ^ jj; if ( at2[k].endpoint ) { ecp0 = ecp1; k = ecp0->neighbor12 ^ jj; if ( at2[k].endpoint ) { continue; } } delta = 1; /* decrement C valence */ pBNS->vert[jj].st_edge.flow -= delta; pBNS->vert[jj].st_edge.cap -= delta; /* decrement bond order */ ecp0->flow -= delta; /* reflect the changes in at2[k] to make it a radical */ pBNS->vert[k].st_edge.flow -= delta; pBNS->tot_st_cap -= delta; pBNS->tot_st_flow -= 2*delta; v1 = endpoint0; v2 = k; ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret2 > 0 ) { num_fixes ++; tot_num_fixes ++; /* #3 Mob-H */ pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */ pBNS->tot_st_cap += delta; pCentp_found = NULL; bNotFixed = 0; /* exit from the cycle */ break; } } else { /* failed */ pBNS->vert[jj].st_edge.flow += delta; pBNS->vert[jj].st_edge.cap += delta; /* decrement bond order */ ecp0->flow += delta; /* reflect the changes in at2[k] to make it a radical */ pBNS->vert[k].st_edge.flow += delta; pBNS->tot_st_cap += delta; pBNS->tot_st_flow += 2*delta; } if ( ret2 < 0 ) { ret = ret2; goto exit_function; } } } } } if ( !num_fixes ) { break; } } ret = tot_num_fixes; exit_function: pStruct->at = at; memcpy( at2, at, len_at*sizeof(at2[0])); return ret; } /******************************************************************************************************/ /* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { #define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1) int i, num_fixes, tot_num_fixes = 0; int ret2, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EDGE_LIST ChargeEdgeList, BondEdgeList; int itg, j, k, n, m, num_endp; Vertex endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint; Vertex centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX; BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; BNS_EDGE *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; int tgroup_number, num_endpoints; ret = 0; if ( pStruct->iMobileH != TAUT_NON ) return ret; AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR); AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR); memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { int iEndpoint = 0; num_fixes = 0; for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { pCentp_found=NULL; tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; for ( i = 0; i < num_endpoints; i ++ ) { endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { /* radical endpoint1 has been detected */ /* find a 1-3 centerpoint that has two or more endpoints */ /* connected to the t-group vertex by edges with flow>0 and */ /* to the centerpoint by edges with flow = 0 */ /* after that: (1) increment etg1 flow to eliminate radical */ /* (2) increment flow on one of the two other edges to the t-group */ /* (3) increment st_cap on the found centerpoint */ /* (4) rerun the BNS and re-create the structure */ break; } } /* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */ pCentp_found = NULL; if ( i < num_endpoints ) { /* tautomeric endpoint found; traverse its t-group edges */ for ( j = 0; j < num_endpoints; j ++ ) { if ( i == j ) { continue; /* avoid the found radical endpoint */ } endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { continue; /* one more radical-endpoint! What is going on here??? */ } if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { continue; /* avoid enpoints that do not have an attachment */ } if ( !pStruct->endpoint[endpoint1] ) { continue; /* should not happen */ } /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { ecp1 = pBNS->edge + pEndp1->iedge[k]; if ( ecp1->flow ) { continue; } centerpoint = ecp1->neighbor12 ^ endpoint1; if ( centerpoint >= pBNS->num_atoms ) { break; /* no more edges to atoms */ } pCentp = pBNS->vert + centerpoint; if ( pStruct->endpoint[centerpoint] ) { continue; /* do not set another endpoint's valence = an unusual value */ } /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ /* 1. Find a double bond to an endpoint */ ecp2 = NULL; pEndp2 = NULL; for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { ecp0 = pBNS->edge + pCentp->iedge[n]; if ( ecp0->flow ) { endpoint2 = ecp0->neighbor12 ^ centerpoint; if ( pStruct->endpoint[endpoint2] /* ??? */ ) { continue; } /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { if ( at[centerpoint].sb_ord[m] == n ) { endpoint2 = NO_VERTEX; break; } } if ( endpoint2 == NO_VERTEX ) { continue; } pEndp2 = pBNS->vert + endpoint2; ecp2 = ecp0; break; } } if ( !ecp2 ) { continue; } /* 2. Find a single bond to an endpoint0 */ for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { ecp0 = pBNS->edge + pCentp->iedge[n]; if ( ecp0->flow ) { continue; } if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { continue; } /* Found: Endp2 Endp2(not radical) || | ||ecp2 |ecp2 ecp0 || ecp1 ecp0 | ecp1 Endp0----Centp----Endp1 Endp0====Centp----Endp1 ^ \ / --> \ / radical | \ / \ / \ / \ / etg0 \ / etg1 etg0 \ / etg1 \ / \ / tg1 tg1 */ /* compare centerpoints */ if ( !pCentp_found || /* try to avoid carbons */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1) && pVA[centerpoint_found].cNumValenceElectrons == 4 && pVA[centerpoint_found].cPeriodicRowNumber == 1 || /* try a better non-carbon */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1 ) && (at[centerpoint].valence > at[centerpoint_found].valence || at[centerpoint].valence == at[centerpoint_found].valence && at[centerpoint].el_number > at[centerpoint_found].el_number) ) { pCentp_found = pCentp; ecp0_found = ecp0; centerpoint_found = centerpoint; pEndp2_found = pEndp2; ecp2_found = ecp2; break; } } } } } /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ /* result: radicals on Endp0 and Centp => run BNS */ if ( pCentp_found ) { /* make ecp0 a double bond, make ecp2 a single bond, remove radical */ ecp0_found->flow ++; if ( ecp0_found->cap < ecp0_found->flow ) { ecp0_found->cap = ecp0_found->flow; } pEndp0->st_edge.flow ++; if ( pEndp2_found && ecp2_found ) { ecp2_found->flow --; pEndp2_found->st_edge.flow --; } else { /* Endp2 not found: only make ecp0 a double bond */ pCentp_found->st_edge.flow ++; pCentp_found->st_edge.cap ++; pBNS->tot_st_flow += 2; /* radical elimination */ pBNS->tot_st_cap += 1; } pCentp_found = NULL; num_fixes ++; /* #2 */ tot_num_fixes ++; continue; } /* 1st attempt */ pCentp_found = NULL; if ( i < num_endpoints ) { /* tautomeric endpoint found; traverse its t-group edges */ for ( j = 0; j < num_endpoints; j ++ ) { if ( i == j ) { continue; /* avoid the found radical endpoint */ } endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { continue; /* one more radical-endpoint! What is going on here??? */ } if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { continue; /* avoid enpoints that do not have an attachment */ } if ( !pStruct->endpoint[endpoint1] ) { continue; /* should not happen */ } /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { ecp1 = pBNS->edge + pEndp1->iedge[k]; if ( ecp1->flow ) { continue; } centerpoint = ecp1->neighbor12 ^ endpoint1; if ( centerpoint >= pBNS->num_atoms ) { break; } pCentp = pBNS->vert + centerpoint; /* traverse centerpoint edges to find the 2nd endpoint */ for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { ecp2 = pBNS->edge + pCentp->iedge[n]; if ( ecp2->flow ) { continue; } endpoint2 = ecp2->neighbor12 ^ centerpoint; if ( endpoint2 >= pBNS->num_atoms ) { break; } if ( !pStruct->endpoint[endpoint2] ) { continue; } pEndp2 = pBNS->vert + endpoint2; if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) { continue; } /* we have found the path: Endp1 has no attachments, Endp2 has. Endp1 Endp1 etg1 // \ ecp1 etg1 / \\ ecp1 etg0 // \ etg0 / \\ Endp0-----tg1 Centp --> Endp0=====tg1 Centp ^ \\ / \\ / radical | etg2 \\ / ecp2 etg2 \\ / ecp2 Endp2 Endp2 */ /* compare centerpoints */ if ( !pCentp_found || /* try to avoid carbons */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1) && pVA[centerpoint_found].cNumValenceElectrons == 4 && pVA[centerpoint_found].cPeriodicRowNumber == 1 || /* try a better non-carbon */ (pVA[centerpoint].cNumValenceElectrons != 4 || pVA[centerpoint].cPeriodicRowNumber != 1 ) && (at[centerpoint].valence > at[centerpoint_found].valence || at[centerpoint].valence == at[centerpoint_found].valence && at[centerpoint].el_number > at[centerpoint_found].el_number) ) { pCentp_found = pCentp; ecp1_found = ecp1; centerpoint_found = centerpoint; break; } } } } } if ( pCentp_found ) { /* create a new radical at the centerpoint and try to cancel them */ int delta = 1, ret3; Vertex vPathStart, vPathEnd, v1, v2; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; pCentp_found->st_edge.cap += delta; pBNS->tot_st_cap += delta; v1 = pCentp_found - pBNS->vert; v2 = pEndp0 - pBNS->vert; ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) { ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret3 > 0 ) { num_fixes ++; tot_num_fixes ++; /* #1 */ pCentp_found = NULL; continue; } } else { pCentp_found->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; } if ( ret3 < 0 ) { ret = ret3; goto exit_function; } } /*---------------------------------------------------------------------------------------- 3rd attempt: add radical to keep ============== u,f=>unfixed, fixed edges (N electrons)%2 (-) (-) (-) | e0/ \\ e1 => u/ \\u => // \ v / \\ ecp1 ecp2 / \\ u f // \ C--X* Y(-)--C==Z C--X* Y(-)--C*--Z --X(-) Y===C---Z* rad. endp not rad. endp rad not rad. endp not Endp0 Endp1 endp endp to endp endp endp Endp2 cancel Note: endpoints X and Y may belong to different t-groups ----------------------------------------------------------------------------------------*/ pCentp_found = NULL; if ( i < num_endpoints ) { int e0, e1; if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) { continue; /* no negative charge on Endp0 is possible */ } /* a radical-tautomeric endpoint found; traverse all endpoints */ for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) { if ( iEndpoint+i == j ) { continue; /* avoid the found radical endpoint */ } endpoint1 = pStruct->ti.nEndpointAtomNumber[j]; pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { continue; /* one more radical-endpoint! What is going on here??? */ } if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) { continue; /* no negative charge on Endp1 */ } if ( !pStruct->endpoint[endpoint1] ) { continue; /* should not happen */ } /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { ecp1 = pBNS->edge + pEndp1->iedge[k]; /* e1C */ if ( ecp1->flow || ecp1->forbidden ) { continue; } centerpoint = ecp1->neighbor12 ^ endpoint1; if ( centerpoint >= pBNS->num_atoms ) { break; /* no more edges to atoms */ } pCentp = pBNS->vert + centerpoint; if ( pStruct->endpoint[centerpoint] ) { continue; /* do not set another endpoint's valence = an unusual value */ } /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ /* 1. Find a double bond to a not endpoint */ ecp2 = NULL; pEndp2 = NULL; for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { ecp0 = pBNS->edge + pCentp->iedge[n]; if ( ecp0->flow && !ecp0->forbidden ) { endpoint2 = ecp0->neighbor12 ^ centerpoint; if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) { continue; } /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { if ( at[centerpoint].sb_ord[m] == n ) { endpoint2 = NO_VERTEX; break; } } if ( endpoint2 == NO_VERTEX ) { continue; } pEndp2 = pBNS->vert + endpoint2; ecp2 = ecp0; /* e2C */ break; } } if ( !ecp2 ) continue; /* compare centerpoints */ if ( !pCentp_found || /* try to find carbons */ !IS_C(endpoint2_found) && IS_C(endpoint2) || IS_C(endpoint2_found) && IS_C(endpoint2) && !IS_C(centerpoint_found) && IS_C(centerpoint) ) { pCentp_found = pCentp; centerpoint_found = centerpoint; endpoint2_found = endpoint2; ecp2_found = ecp2; ecp1_found = ecp1; ecp0_found = pBNS->edge + e0; break; } } } } /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ /* result: radicals on Endp0 and Centp => run BNS */ if ( pCentp_found ) { Vertex vPathStart, vPathEnd, v1, v2; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; int delta; Vertex vEndp0 = ecp0_found->neighbor1; Vertex vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found; Vertex vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found; BNS_EDGE *pe0 = ecp0_found; BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1); pEndp1 = pBNS->vert + vEndp1; pEndp2 = pBNS->vert + vEndp2; pCentp = pCentp_found; if ( !ChargeEdgeList.num_alloc ) { for ( n = 0; n < pStruct->num_atoms; n ++ ) { if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { goto exit_function; } if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { goto exit_function; } } } if ( !BondEdgeList.num_alloc ) { for ( n = 0; n < pBNS->num_bonds; n ++ ) { if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) { goto exit_function; } } } /* fix all bonds and charges */ SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); /* prepare flow for testing */ delta = 1; ecp2_found->flow -= delta; pCentp->st_edge.flow -= delta; pEndp2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; /* unfix edges to be changed */ pe0->forbidden &= inv_forbidden_edge_mask; pe1->forbidden &= inv_forbidden_edge_mask; ecp1_found->forbidden &= inv_forbidden_edge_mask; pBNS->tot_st_cap += delta; v1 = vEndp0; v2 = centerpoint_found; ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret2 > 0 ) { num_fixes ++; tot_num_fixes ++; /* #3 */ pCentp_found = NULL; } } else { /* roll back */ ecp2_found->flow += delta; pCentp->st_edge.flow += delta; pEndp2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } if ( !pCentp_found ) continue; } } if ( !num_fixes ) { break; } } /************ again ***********************************************************/ while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { int iEndpoint = 0; num_fixes = 0; for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { pCentp_found=NULL; tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; for ( i = 0; i < num_endpoints; i ++ ) { endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { /* radical endpoint1 has been detected */ /* find a 1-3 centerpoint that has two or more endpoints */ /* connected to the t-group vertex by edges with flow>0 and */ /* to the centerpoint by edges with flow = 0 */ /* after that: (1) increment etg1 flow to eliminate radical */ /* (2) increment flow on one of the two other edges to the t-group */ /* (3) increment st_cap on the found centerpoint */ /* (4) rerun the BNS and re-create the structure */ break; } } /* 4th attempt */ if ( i < num_endpoints ) { /* tautomeric endpoint found; traverse its t-group edges */ pEndp2_found = NULL; for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) { ecp0 = pBNS->edge + pEndp0->iedge[j]; centerpoint = ecp0->neighbor12 ^ endpoint0; if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) { continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */ } pCentp = pBNS->vert + centerpoint; for ( k = 0; k < pCentp->num_adj_edges; k ++ ) { ecp1 = pBNS->edge + pCentp->iedge[k]; endpoint1 = ecp1->neighbor12 ^ centerpoint; if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) { continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */ } pEndp1 = pBNS->vert + endpoint1; if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) { continue; /* ignore radicals */ } if ( !pEndp2_found || /* try to find carbons */ !IS_C(endpoint2_found) && IS_C(endpoint1) || IS_C(endpoint2_found) && IS_C(endpoint1) && !IS_C(centerpoint_found) && IS_C(centerpoint) ) { pEndp2_found = pEndp1; pCentp_found = pCentp; endpoint2_found = endpoint1; centerpoint_found = centerpoint; ecp1_found = ecp0; ecp2_found = ecp1; } } } if ( pEndp2_found ) { /* move radical from pEndp0 to pEndp2 */ pEndp0->st_edge.flow ++; ecp1_found->flow ++; ecp2_found->flow --; pEndp2_found->st_edge.flow --; pEndp2_found = NULL; pCentp_found = NULL; num_fixes ++; /* #4 */ tot_num_fixes ++; continue; } } } if ( !num_fixes ) { break; } } ret = tot_num_fixes; exit_function: AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE); AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE); pStruct->at = at; memcpy( at2, at, len_at*sizeof(at2[0])); return ret; #undef IS_C } /************************************************************************************************/ /* move (+) charges to >N- and other centerpoints */ int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, neigh, num_endpoints, tg_group=0, num_success; int ret2, ret, delta; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; /* for RunBnsTestOnce */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pEdgePlus, *pEdgeMinus; Vertex v1p, v2p, v1m, v2m; BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m; ret = 0; num_success = 0; /* to simplify, prepare new at[] from pBNS */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ !pVA[i].cMetal && !pVA[i].nTautGroupEdge && !at2[i].num_H && at2[i].valence >= 3 && at2[i].valence == at2[i].chem_bonds_valence && !at2[i].charge && pVA[i].nCPlusGroupEdge > 0 && is_centerpoint_elem( at2[i].el_number ) ) { for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { neigh = at2[i].neighbor[j]; if ( at2[neigh].endpoint ) { if ( !num_endpoints ) { tg_group = at2[neigh].endpoint; } else if ( tg_group != at2[neigh].endpoint ) { break; /* not a centerpoint */ } num_endpoints ++; } } if ( j == at2[i].valence && num_endpoints > 1 ) { /* found possible centerpoint */ pEdgePlus = pBNS->edge + (pVA[i].nCPlusGroupEdge-1); pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL; if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) { continue; } v1p = pEdgePlus->neighbor1; v2p = pEdgePlus->neighbor12 ^ v1p; pv1p = pBNS->vert + v1p; pv2p = pBNS->vert + v2p; if ( pEdgeMinus ) { v1m = pEdgeMinus->neighbor1; v2m = pEdgeMinus->neighbor12 ^ v1m; pv1m = pBNS->vert + v1m; pv2m = pBNS->vert + v2m; } else { v1m = NO_VERTEX; v2m = NO_VERTEX; pv1m = NULL; pv2m = NULL; } ret = 0; /* set new flow to run BNS Search */ if ( delta = pEdgePlus->flow ) { /* positive charge <=> flow=0 on (=) edge */ pEdgePlus->flow -= delta; pv1p->st_edge.flow -= delta; pv2p->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; pEdgePlus->forbidden |= forbidden_edge_mask; if ( pEdgeMinus ) { pEdgeMinus->forbidden |= forbidden_edge_mask; } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p || vPathEnd == v2p && vPathStart == v1p) && nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { ret = 0; pEdgePlus->flow += delta; pv1p->st_edge.flow += delta; pv2p->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } pEdgePlus->forbidden &= inv_forbidden_edge_mask; if ( pEdgeMinus ) { pEdgeMinus->forbidden &= inv_forbidden_edge_mask; } } else if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) { /* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */ pEdgeMinus->flow -= delta; pv1m->st_edge.flow -= delta; pv2m->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; pEdgePlus->forbidden |= forbidden_edge_mask; pEdgeMinus->forbidden |= forbidden_edge_mask; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || vPathEnd == v2m && vPathStart == v1m) && nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { ret = 0; pEdgeMinus->flow += delta; pv1m->st_edge.flow += delta; pv2m->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } pEdgePlus->forbidden &= inv_forbidden_edge_mask; pEdgeMinus->forbidden &= inv_forbidden_edge_mask; } if ( ret ) { num_success ++; memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } } } } } ret = num_success; exit_function: return ret; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr3.c000066400000000000000000010736571271037650300234710ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" #define INC_ADD_EDGE 64 /* local types */ /* types for TgDiffHChgFH */ #define fNumRPosChgH 0 /* number of positive charges on endpoints that have H in at2[] */ #define fNumRPosChgU 1 /* number of positive charges on endpoints that have no H in at2[] */ #define fNumRNegChgO 2 /* number of negative charges on O endpoints */ #define fNumRNegChgN 3 /* number of negative charges on N endpoints */ #define fNumRNeutrlH 4 /* number of neutral endp that have H in at2[] */ #define fNumNPosChgH 5 /* number of positive charges on endpoints that have H in atf[] */ #define fNumNPosChgU 6 /* number of positive charges on endpoints that have no H in atf[] */ #define fNumNNegChgO 7 /* number of negative charges on O endpoints */ #define fNumNNegChgN 8 /* number of negative charges on N endpoints */ #define fNumNNeutrlH 9 /* number of neutral endp that have H in atf[] */ #define fNumAllChgT 10 /* total number of fNum... */ typedef struct tagTgDiffHChgFH { short itg; /* t-group index; endpoint = itg+1 */ short nNumHInchi; /* number of H in t-group from orig. InChI */ short nNumHRevrs; /* number of H in at2[] */ short nNumHNorml; /* number of H in Normalized atfMobile_H_Revrs[] */ short nNumMInchi; /* number of (-) in InChI */ short nNumMRevrs; /* number of (-) in at2[] */ short nNumMNorml; /* number of (-) in atf[] */ short nNumPRevrs; /* number of (+) in at2[] */ short nNumPNorml; /* number of (+) in Normalized atfMobile_H_Revrs[] */ short n[fNumAllChgT]; /* all numbers */ short i[fNumAllChgT]; /* all indices */ } TgDiffHChgFH; /* local prototypes */ static int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], inp_ATOM atf[], AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ); /************************************************************/ int bHas_N_V( inp_ATOM *at2, int num_atoms ) { static U_CHAR el_number_N; int i, num_found = 0; if ( !el_number_N ) { el_number_N = get_periodic_table_number( "N" ); } for ( i = 0; i < num_atoms; i ++ ) { if ( at2[i].el_number == el_number_N && !at2[i].charge && !at2[i].num_H && !at2[i].radical && at2[i].chem_bonds_valence == 5 && (at2[i].valence==3) ) { num_found ++; } } return num_found; } /*************************************************************************************/ int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], inp_ATOM atf[], AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ) { int i, j, iat, itg, itg_prev, num, itg_out, bOverflow; EDGE_LIST IndList; /* type, itg */ TgDiffHChgFH cur_tdhc; AT_NUMB *pEndp0; inp_ATOM *at2i, *atfi; int typeR, typeN, type, ret = 0, nCurIndListLen; AllocEdgeList( &IndList, EDGE_LIST_CLEAR ); pAtomIndList->num_edges = 0; itg_out = 0; bOverflow = 0; memset( tdhc, 0, max_tdhc * sizeof(tdhc[0]) ); for ( itg = 0; itg < ti->num_t_groups; itg ++ ) { memset( &cur_tdhc, 0, sizeof(cur_tdhc) ); cur_tdhc.itg = itg; cur_tdhc.nNumHInchi = ti->t_group[itg].num[0] - ti->t_group[itg].num[1]; cur_tdhc.nNumMInchi = ti->t_group[itg].num[1]; pEndp0 = ti->nEndpointAtomNumber + ti->t_group[itg].nFirstEndpointAtNoPos; nCurIndListLen = IndList.num_edges; for ( j = 0; j < ti->t_group[itg].nNumEndpoints; j ++ ) { i = pEndp0[j]; iat = nCanon2AtnoRevrs[i]; at2i = at2 + iat; atfi = atf + iat; typeR = typeN = -1; if ( at2i->charge == 1 ) { if ( at2i->num_H ) { typeR = fNumRPosChgH; } else { typeR = fNumRPosChgU; } cur_tdhc.nNumPRevrs ++; } else if ( at2i->charge == -1 ) { if ( pVA[iat].cNumValenceElectrons == 6) { typeR = fNumRNegChgO; } else if ( pVA[iat].cNumValenceElectrons == 5) { typeR = fNumRNegChgN; } cur_tdhc.nNumMRevrs ++; } else if ( at2i->num_H && at2i->valence == at2i->chem_bonds_valence ) { typeR = fNumRNeutrlH; } cur_tdhc.nNumHRevrs += at2i->num_H; if ( atfi->charge == 1 ) { if ( atfi->num_H ) { typeN = fNumNPosChgH; } else { typeN = fNumNPosChgU; } cur_tdhc.nNumPNorml ++; } else if ( atfi->charge == -1 ) { if ( pVA[iat].cNumValenceElectrons == 6) { typeN = fNumNNegChgO; } else if ( pVA[iat].cNumValenceElectrons == 5) { typeN = fNumNNegChgN; } cur_tdhc.nNumMNorml ++; } else if ( atfi->num_H && atfi->valence == atfi->chem_bonds_valence ) { typeN = fNumNNeutrlH; } cur_tdhc.nNumHNorml += atfi->num_H; if ( at2[iat].charge < 0 || 0 < pVA[iat].nCPlusGroupEdge ) { if ( typeR >= 0 && ( (ret = AddToEdgeList( &IndList, typeR, INC_ADD_EDGE )) || (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { goto exit_function; } if ( typeN >= 0 && ( (ret = AddToEdgeList( &IndList, typeN, INC_ADD_EDGE )) || (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { goto exit_function; } } } if ( cur_tdhc.nNumHNorml == cur_tdhc.nNumHInchi && cur_tdhc.nNumMNorml == cur_tdhc.nNumMInchi ) { IndList.num_edges = nCurIndListLen; /* t-group seems to be correct */ continue; } if ( itg_out < max_tdhc ) { tdhc[itg_out ++] = cur_tdhc; } else { bOverflow |= 1; IndList.num_edges = nCurIndListLen; break; } } /* fill out atom index list */ if ( itg_out ) { itg_prev = IndList.pnEdges[1]; /* the 1st saved t-group number */ for ( type = 0; type < fNumAllChgT; type ++ ) { j = 0; for ( i = 0; i < itg_out; i ++ ) { num = 0; itg = tdhc[i].itg; tdhc[i].i[type] = -999; /* empty */ while( IndList.pnEdges[j+1] == itg ) { if ( IndList.pnEdges[j] == type ) { if ( !num ++ ) { tdhc[i].i[type] = pAtomIndList->num_edges; } if ( ret = AddToEdgeList( pAtomIndList, IndList.pnEdges[j+2], INC_ADD_EDGE )) { goto exit_function; } } j += 3; } tdhc[i].n[type] = num; } } } ret = itg_out; exit_function: AllocEdgeList( &IndList, EDGE_LIST_FREE ); return ret; /* #undef fNumRPosChgH #undef fNumRPosChgU #undef fNumRNegChgO #undef fNumRNegChgN #undef fNumNPosChgH #undef fNumNPosChgU #undef fNumNNegChgO #undef fNumNNegChgN #undef fNumAllChgT */ } /***********************************************************************************************/ int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) { /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ /* at2 should be the most recently restored atom, Fixed-H */ int i, j, k, delta, num_try, tot_succes, cur_success, ret = 0, bAllowedNFlowerEdges=0, num_zero_ret; CMP2FHINCHI c2i; CMP2FHINCHI *pc2i = &c2i; EDGE_LIST AllChargeEdges, CurrEdges, SFlowerEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; EDGE_LIST AllBondEdges; EdgeIndex e; BNS_EDGE *pe; Vertex v1, v2; BNS_VERTEX *pv1, *pv2; Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; INCHI_HEAPCHK AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &SFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &AllBondEdges, EDGE_LIST_CLEAR ); tot_succes = 0; if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } for ( i = 0; i < pStruct->num_atoms; i ++ ) { if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { goto exit_function; } if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { if ( pBNS->edge[j].forbidden ) { continue; } if ( pBNS->edge[j].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } else { if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } } else /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[i].cNumValenceElectrons == 6 && !pVA[i].cMetal && /* N, P, As */ NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { if ( pBNS->edge[j].forbidden ) { continue; } if ( pBNS->edge[j].flow ) { if ( ret = AddToEdgeList( &SFlowerEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } } } for ( j = 0; j < at2[i].valence; j ++ ) { k = at2[i].neighbor[j]; if ( k < i && !pBNS->edge[e=pBNS->vert[i].iedge[j]].forbidden ) { if ( ret = AddToEdgeList( &AllBondEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } if ( forbidden_stereo_edge_mask ) { for ( i = 0; i < pStruct->num_atoms; i ++ ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, pStruct->pbfsq->nAtomLevel, pStruct->pbfsq->cSource, 99 /* max ring size */ ); if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { goto exit_function; } } } } } INCHI_HEAPCHK if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } INCHI_HEAPCHK if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } INCHI_HEAPCHK if ( !pc2i->bHasDifference || !pc2i->len_c2at && pc2i->nNumTgRevrs == pc2i->nNumTgInChI && pc2i->nNumEndpRevrs == pc2i->nNumRemHInChI && pc2i->nNumEndpRevrs == pc2i->nNumEndpInChI && !pc2i->nNumTgDiffMinus && !pc2i->nNumTgDiffH ) { goto exit_function; /* nothing to do */ } /*goto exit_function;*/ /* debug only*/ if ( pc2i->len_c2at >= 2 ) { /*----------------------------------------------------*/ /* case 01: restored: O=AB-O(-) original: (-)O-AB=O */ /* FixH: 0 -1 -1 0 */ /* MobH: 0 1 1 0 */ /* non-taut non-taut */ /* O = O, S, Se; charged atoms O are not tautomeric */ /* Solution: move (-) from B-O(-) to O=A */ /*----------------------------------------------------*/ int num_DB_O = 0, num_SB_O_Minus = 0, iat; short iat_DB_O[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( pc2i->c2at[i].nValElectr == 6 /* && !pc2i->c2at[i].endptInChI -- mod#1*/ && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( /* orig. InChI info: */ num_SB_O_Minus < MAX_DIFF_FIXH && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == -1 && pc2i->c2at[i].nMobHRevrs == 1 && pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && /* at2 is Fixed-H */ at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { iat_SB_O_Minus[num_SB_O_Minus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* orig. InChI info: */ num_DB_O < MAX_DIFF_FIXH && pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { iat_DB_O[num_DB_O ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; /* remove (-) from AB-O(-) */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-)charge to O=AB => nDeltaCharge == -1 */ /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 01 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 ) { /*--------------------------------------------------------------*/ /* case 02: restored: -O(+)=AB-NH2 original: -O-AB=NH2(+) */ /* FixH: 0 0 0 1 */ /* MobH: 0 2 0 1 */ /* O = P, As, Sb, O, S, Se, F, Cl, Br, I; not taut. in InChI */ /* N = N, O, S, Se, Te; has H; tautomeric or not tautomeric */ /* Solution: move (+) from O(+) to NH2 */ /*--------------------------------------------------------------*/ int num_DB_O_Plus = 0, num_SB_NH = 0, iat; short iat_DB_O_Plus[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; cur_success = 0; num_zero_ret = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =NH2(+), =OH(+) */ num_SB_NH < MAX_DIFF_FIXH && (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI>0 /*== 1 --modification#2*/ && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && /* pc2i->c2at[i].nMobHRevrs == 0 &&*/ pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence ) { iat_SB_NH[num_SB_NH ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=+1, no H, has double bond, P, As, O, S, Se, Te, F, Cl, Br, I */ num_DB_O_Plus < MAX_DIFF_FIXH && at2[iat].charge == 1 && !at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1) && /* in orig.InChI: not an endpoint, has no H */ !pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_DB_O_Plus[num_DB_O_Plus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_DB_O_Plus, num_SB_NH ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; repeat_02_allow_NV: for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { iat = iat_SB_NH[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed charge from O(+) => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 02 */ } } else { num_zero_ret += !ret; pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } if ( num_zero_ret == num_try && !bAllowedNFlowerEdges && NFlowerEdges.num_edges ) { RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); bAllowedNFlowerEdges = 1; goto repeat_02_allow_NV; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); bAllowedNFlowerEdges = 0; } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 && pc2i->nNumTgRevrs == 1 && (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { /*--------------------------------------------------------------*/ /* case 03: restored: -N(-)-AB=O original: -N=AB-O(-) */ /* FixH: 0 0 0 -1 */ /* MobH: 0 0 0 1 */ /* O = O, S, Se; N = N; */ /* restored atoms are tautomeric; original atoms are not taut. */ /* restored struct has 1 t-group; original has less endpoints */ /* and possibly >1 t-groups */ /* Solution: move (-) from N(-) to =O */ /* these atoms are tautomeric in restored structure */ /*--------------------------------------------------------------*/ int num_SB_N_Minus = 0, num_DB_O = 0, iat; short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -O(-) */ num_DB_O < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && /* reversed structure info: */ pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { iat_DB_O[num_DB_O ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ num_SB_N_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* in orig.InChI: not an endpoint, has no H */ /* !pStruct->endpoint[i] && */ /* !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && */ /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_N_Minus[num_SB_N_Minus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_N_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; /* 2006-03-03: changed from CPlusGroupEdge */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-) charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 03 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->nNumTgRevrs == 1 && /* pc2i->nNumRemHInChI < 0 &&*/ (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { /*--------------------------------------------------------------*/ /* case 03a:restored: -N(-)-AB=O original: -N=AB-O(-) */ /* FixH: 0 0 0 0 */ /* MobH: 0 0 0 0 */ /* O = O, S, Se; N = N; taut */ /* restored atoms are tautomeric; original atom is; N may be. */ /* restored struct has 1 t-group; original has less endpoints */ /* and possibly >1 t-groups */ /* Solution: move (-) from N(-) to =O */ /* these atoms are tautomeric in restored structure */ /*--------------------------------------------------------------*/ int num_SB_N_Minus = 0, num_DB_O = 0, iat; short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; S_CHAR *pnFixHInChI = pStruct->fixed_H; cur_success = 0; CurrEdges.num_edges = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ num_SB_N_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* in orig.InChI: may be an endpoint, has no H */ /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_N_Minus[num_SB_N_Minus ++] = iat; } else if ( num_DB_O < MAX_DIFF_FIXH && at2[iat].charge == 0 && /*!at2[iat].num_H &&*/ at2[iat].valence+1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* endpoint in Reconstructed */ (pStruct->endpoint[i] || /* endpoint or H(+) acceptor in original */ pnMobHInChI && pnMobHInChI[i] == 1 && pnFixHInChI && pnFixHInChI[i] == -1 ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_DB_O[num_DB_O ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); /* allow charge transfer to all found =O */ RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_N_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-) charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 03a */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && /* ADP in InChI */ (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { /*--------------------------------------------------------------*/ /* case 04: restored: OH(+)=AB-O- OH- orig. HO-AB=O(+)- OH- */ /* FixH: 1 0 0 1 0 1 */ /* MobH: 0 0 1 0 0 0 */ /* non-taut. taut taut */ /* ADP: one t-group or more endpoints */ /* O(+) = N, P, As, As, O, S, Se; OH = N, O, S, Se, Te */ /* Solution: move (+) from O(+) to NH2 */ /*--------------------------------------------------------------*/ int num_SB_Neutr = 0, num_DB_Charged = 0, iat; short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ num_DB_Charged < MAX_DIFF_FIXH && at2[iat].charge == 1 && at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && /* in orig.InChI: an endpoint, has fixed-H */ pStruct->endpoint[i] && (pStruct->fixed_H && pStruct->fixed_H[i]) && /*!(nMobHInChI && nMobHInChI[i] ) &&*/ /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_DB_Charged[num_DB_Charged ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ num_SB_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 5 ) && /* in orig.InChI: an endpoint, has fixed-H */ /* pStruct->endpoint[i] && */ !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_SB_Neutr[num_SB_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_Neutr && cur_success < num_try; i ++ ) { iat = iat_SB_Neutr[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed charge from O(+) => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 04 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at > 1 ) { /*--------------------------------------------------------------*/ /* case 05: restored: O=AB-NH original:(-)O-AB=NH(+) */ /* FixH: 0 0 -1 1 */ /* MobH: 0 1 1 0 */ /* O = O, S, Se; N = N, O, S, Se, Te; all atoms not tautomeric */ /* Solution: Separate charges */ /*--------------------------------------------------------------*/ int num_DB_O = 0, num_SB_NH = 0, iat; short iat_DB_O[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =NH2(+), =OH(+) */ num_SB_NH < MAX_DIFF_FIXH && (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs && pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && !pc2i->c2at[i].endptRevrs && at2[iat].valence == at2[iat].chem_bonds_valence ) { iat_SB_NH[num_SB_NH ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* orig. InChI info: -O(-) */ num_DB_O < MAX_DIFF_FIXH && (pc2i->c2at[i].nValElectr == 6 ) /* O, S, Se, Te */ && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && !pc2i->c2at[i].endptRevrs && at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { iat_DB_O[num_DB_O ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_DB_O, num_SB_NH ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { iat = iat_SB_NH[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 05 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pStruct->fixed_H && pStruct->endpoint && pc2i->nChargeFixHInChI > 0 && pc2i->nChargeFixHInChI > pc2i->nChargeMobHInChI ) { /*----------------------------------------------------------*/ /* case 06c: restored -NH- or -NH(+) orig: -NH- */ /* Fixed-H 1 1 0 */ /* Mobile-H 0 0 1 */ /* not tautomeric not tautomeric */ /* has adjacent (+) */ /* charges */ /* Solution: move (+) charges to the -NH- unless it already*/ /* N = N, O, S, Se, Te */ /* has (+) charge blocked by adjacent (+) */ /*----------------------------------------------------------*/ int iat; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; */ EDGE_LIST CurChargeEdges; EdgeIndex e2; cur_success = 0; AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); CurrEdges.num_edges = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { /* atoms -NH- from which H(+) were removed by the Normalization in orig. InChI */ iat = pc2i->c2at[i].atomNumber; if ( (pc2i->c2at[i].nValElectr == 6 || pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( /* orig. InChI info: -NH- */ pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && /* was not removed */ /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ at2[iat].num_H && /* at2 is Fixed-H */ at2[iat].valence == at2[iat].chem_bonds_valence ) { if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* find adjacent charged atoms */ iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] || at2[iat].charge != 1 || at2[iat].radical || pVA[iat].cMetal ) { continue; } if ( 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && pVA[iat].cNumValenceElectrons >= 5 ) { /* positively charged atom */ for ( j = 0; j < at2[iat].valence; j ++ ) { if ( at2[k=(int)at2[iat].neighbor[j]].charge == 1 && !pVA[k].cMetal && 0 <= (e2=pVA[k].nCPlusGroupEdge-1) && !pBNS->edge[e2].forbidden && !pBNS->edge[e2].flow) { if ( 0 > FindInEdgeList( &CurrEdges, e ) && 0 > FindInEdgeList( &CurChargeEdges, e ) && ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) ) { goto exit_case_06c; } if ( 0 > FindInEdgeList( &CurrEdges, e2 ) && 0 > FindInEdgeList( &CurChargeEdges, e2 ) && ( ret = AddToEdgeList( &CurChargeEdges, e2, INC_ADD_EDGE ) ) ) { goto exit_case_06c; } } } } } if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { e = CurrEdges.pnEdges[i]; pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 06c */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } exit_case_06c: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); if ( ret < 0 ) { goto exit_function; } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 2 ) { /*------------------------------------------------------------*/ /* case 06d: restored: XH(+)=-AB-NH orig.: XH-=AB=NH(+) */ /* FixH: 1 1 0 0 1 1 */ /* MobH: 0 taut 1 1 taut 0 */ /* */ /* */ /* N = N, O, S, Se; atoms N are not tautomeric in orig InChI */ /* X = N, O, S, Se, Te, F, Cl, Br, I; atom X is non-taut */ /* Solution: move (+) from X to NH */ /*------------------------------------------------------------*/ int iat; /* AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : pStruct->pOne_norm_data[TAUT_NON]->at; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ EDGE_LIST CurChargeEdges; cur_success = 0; AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); CurrEdges.num_edges = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; /* XH(+) */ if ( /* reconstructed: non-taut and (+) */ (pc2i->c2at[i].nMobHRevrs+1 == pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nFixHRevrs > 0 && !pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nAtChargeRevrs == 1 && /* original InChI: non-taut & has H or an endpoint, has Fixed H */ (!pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].nMobHInChI == pc2i->c2at[i].nFixHRevrs || pc2i->c2at[i].nFixHInChI == pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].endptInChI )) && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow) { if (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE )) { goto exit_case_06d; } } else /* -NH- */ if ( /* original InChI: has H and is not an endpoint */ (pc2i->c2at[i].nMobHInChI+1 == pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].nFixHInChI > 0 && !pc2i->c2at[i].endptInChI && pc2i->c2at[i].nAtChargeRevrs == 0 && /* reconstructed InChI: non-taut & has H or an endpoint, has Fixed H */ (!pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nFixHInChI || pc2i->c2at[i].nFixHRevrs == pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].endptRevrs )) && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow) { if (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE )) { goto exit_case_06d; } } } if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { /* detected; attempt to fix */ int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); int bSFlowerEdgesIsForbidden; for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { if ( bSFlowerEdgesIsForbidden ) { /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { e = CurrEdges.pnEdges[i]; pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 06d */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); } } exit_case_06d: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 2 ) { /*--------------------------------------------------------*/ /* case 06: restored: NHn(+)=AB-NHm orig.: NHn-AB=NHm(+) */ /* FixH: 1 0 0 1 */ /* MobH: n-1 m n m-1 */ /* N = N, O, S, Se; atoms N are not tautomeric */ /* Solution: move (+) from NHn(+) to NHn */ /*--------------------------------------------------------*/ int num_DB_NHn_Plus = 0, num_SB_NHm_Neutr = 0, iat; short iat_DB_NHn_Plus[MAX_DIFF_FIXH], iat_SB_NHm_Neutr[MAX_DIFF_FIXH]; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( (pc2i->c2at[i].nValElectr == 6 || pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( /* orig. InChI info: NHm */ num_SB_NHm_Neutr < MAX_DIFF_FIXH && pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 0 &&*/ /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 1 &&*/ pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && /* at2 is Fixed-H */ at2[iat].valence == at2[iat].chem_bonds_valence ) { iat_SB_NHm_Neutr[num_SB_NHm_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* orig. InChI info: */ num_DB_NHn_Plus < MAX_DIFF_FIXH && pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI &&*/ /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 1 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ pc2i->c2at[i].nAtChargeRevrs == 1 && at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence ) { iat_DB_NHn_Plus[num_DB_NHn_Plus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } if ( num_try = inchi_min( num_SB_NHm_Neutr, num_DB_NHn_Plus ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_NHm_Neutr && cur_success < num_try; i ++ ) { iat = iat_SB_NHm_Neutr[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 06 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons && pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { /*----------------------------------------------------------*/ /* case 06a: restored: N'(+)=-AB-NH orig.: N'-=AB=NH(+) */ /* FixH: 0 1 0 1 */ /* MobH: 0 0 0 0 */ /* single t-group multiple t-groups */ /* N = N, O, S, Se; atoms N are not tautomeric */ /* N' = N atom N' is not tautomeric */ /* Solution: move (+) from N' to NH */ /*----------------------------------------------------------*/ int iat; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; */ inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; EDGE_LIST CurChargeEdges; cur_success = 0; AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); CurrEdges.num_edges = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] ) { continue; } /* -NH-, -OH */ if ( pStruct->fixed_H[i] && !nMobHInChI[i] && at2[iat].charge == 0 && at2[iat].radical == 0 && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { goto exit_case_06a; } else /* >N(+)= */ if ( at2[iat].charge == 1 && !at2[iat].num_H && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 0 && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { goto exit_case_06a; } } if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { e = CurrEdges.pnEdges[i]; pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 06a */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } exit_case_06a: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && (pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons || pStruct->nNumRemovedProtonsMobHInChI > pStruct->One_ti.tni.nNumRemovedProtons ) && pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { /*----------------------------------------------------------*/ /* case 06b: restored: X(+)=-AB-NH orig.: X-=AB=NH(+) */ /* FixH: 0 1 1 0 1 */ /* MobH: 0 0 t 0 0 */ /* single t-group multiple t-groups */ /* or no t-groupd */ /* N = N, O, S, Se; atoms N are not tautomeric */ /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ /* Solution: move (+) from X to NH */ /*----------------------------------------------------------*/ int iat; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; */ inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; EDGE_LIST CurChargeEdges; cur_success = 0; AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); CurrEdges.num_edges = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] ) { continue; } /* -NH-, -OH */ if ( pStruct->fixed_H[i] && !nMobHInChI[i] && at2[iat].charge == 0 && at2[iat].radical == 0 && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { goto exit_case_06b; } else /* X(+)= */ if ( at2[iat].charge == 1 && !at2[iat].num_H && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cPeriodicRowNumber == 7) && atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 1 && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { goto exit_case_06b; } } if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { /* detected; attempt to fix */ int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); int bSFlowerEdgesIsForbidden; for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { if ( bSFlowerEdgesIsForbidden ) { /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { e = CurrEdges.pnEdges[i]; pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 06b */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); } } exit_case_06b: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->nNumTgInChI > 1 && (pStruct->nNumRemovedProtonsMobHInChI > 0 || pStruct->ti.tni.nNumRemovedProtons > 0 ) && pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { /*----------------------------------------------------------*/ /* case 06e:restored: XHn(+)=-AB-YHm orig.: XHn-=AB=YHm(+) */ /* FixH: 1 0 1 1 */ /* MobH: 0 1 t t */ /* non-taut atoms multiple t-groups */ /* */ /* 1. orig. t-group has more H on its endpoints counted */ /* in atf and has no (+) on endpoint that has H */ /* 2. orig. t-group has less H on its endpoints counted */ /* in atf and has (+) on endpoint that has H */ /* in reconstructed struct and less H in atf */ /* Solution: move (+) from (2) to atom in (1) that has H */ /* */ /* tg1 reconstr: XHn and more H than in orig t-group */ /* atf: XHn */ /* tg2 reconstr: XHm(+) and less H than in */ /* atf: XH(m-1) orig in t-group */ /* */ /* N = N, O, S, Se; atoms N are not tautomeric */ /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ /* Solution: move (+) from X to NH */ /*----------------------------------------------------------*/ int iat, nNumWrongTg, jjoffs, jj, nNum2RemovePlus, nNum2AddPlus, nNum2MovePlus; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; */ inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at? pStruct->pOne_norm_data[TAUT_YES]->at : NULL; /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ EDGE_LIST CurChargeEdges /* source of (+)*/, EndpList; TgDiffHChgFH tdhc[MAX_DIFF_FIXH]; BNS_VERTEX *pv1n, *pv2n; BNS_EDGE *pe1n, *pe2n; Vertex v1n, v2n; cur_success = 0; AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &EndpList, EDGE_LIST_CLEAR ); CurrEdges.num_edges = 0; /* receptors of (+) */ if ( !atfMobile_H_Revrs ) { goto exit_case_06e; } nNumWrongTg = FillTgDiffHChgFH( tdhc, MAX_DIFF_FIXH, at2, atfMobile_H_Revrs, nCanon2AtnoRevrs, pVA, &pStruct->ti, &EndpList ); if ( nNumWrongTg < 1 ) { goto exit_case_06e; /* for now only transfer (+) from one Mobile-H group to another */ } nNum2RemovePlus = nNum2AddPlus = nNum2MovePlus = 0; for ( i = 0; i < nNumWrongTg; i ++ ) { /* detect t-group that has extra (+) on H */ if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { /* count how many (+) to remove */ /* store XH(+) atom numbers */ int nNumNeeded = inchi_min( tdhc[i].nNumHInchi-tdhc[i].nNumHNorml, tdhc[i].n[fNumRPosChgH]); nNum2RemovePlus += nNumNeeded; jjoffs = tdhc[i].i[ fNumRPosChgH ]; for ( jj = 0; jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { iat = EndpList.pnEdges[ jjoffs + jj ]; e = pVA[iat].nCPlusGroupEdge-1; if ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) { goto exit_case_06e; } } } else /* detect t-group that needs (+) on XH to reduce number of H */ if ( tdhc[i].nNumHInchi < tdhc[i].nNumHNorml && tdhc[i].n[fNumRNeutrlH] ) { /* store XH atom numbers */ int nNumNeeded = inchi_min( tdhc[i].nNumHNorml-tdhc[i].nNumHInchi, tdhc[i].n[fNumRNeutrlH]); nNum2AddPlus += nNumNeeded; jjoffs = tdhc[i].i[ fNumRNeutrlH ]; for ( jj = 0; jj < tdhc[i].n[fNumRNeutrlH]; jj ++ ) { iat = EndpList.pnEdges[ jjoffs + jj ]; e = pVA[iat].nCPlusGroupEdge-1; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_case_06e; } } } } nNum2MovePlus = inchi_min( nNum2RemovePlus, nNum2AddPlus ); if ( CurrEdges.num_edges > 0 && CurChargeEdges.num_edges > 0 ) { for ( i = 0; 0 < nNum2MovePlus && i < nNumWrongTg; i ++ ) { /* detect t-group that has extra (+) on H */ if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { int nNum2Remove = tdhc[i].nNumHInchi - tdhc[i].nNumHNorml; if ( nNum2Remove < tdhc[i].n[fNumRPosChgH] ) { nNum2Remove = tdhc[i].n[fNumRPosChgH]; } /* store XH(+) atom numbers */ jjoffs = tdhc[i].i[ fNumRPosChgH ]; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); for ( jj = 0; 0 < nNum2MovePlus && 0 < nNum2Remove && jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { iat = EndpList.pnEdges[ jjoffs + jj ]; e = pVA[iat].nCPlusGroupEdge-1; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { pe1n = pBNS->edge + pv1->iedge[j]; if ( pe1n->flow && !pe1n->forbidden ) { pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); break; } } if ( j < 0 ) continue; /* not found */ for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { pe2n = pBNS->edge + pv2->iedge[j]; if ( pe2n->flow && !pe2n->forbidden ) { pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); break; } } if ( j < 0 ) continue; /* not found */ delta = 1; pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && (nDeltaCharge == 0 || nDeltaCharge == 1) ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; nNum2Remove --; nNum2MovePlus --; cur_success ++; /* 06e */ } } else { pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_case_06e; } } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } } exit_case_06e: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &EndpList, EDGE_LIST_FREE ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 ) { /*--------------------------------------------------------------*/ /* case 07: restored: O(-)-AB=O original: O=AB-O(-) */ /* FixH: 0 0 0 -1 */ /* MobH: 0 0 0 1 */ /* taut (non-taut) (taut) non-taut */ /* taut (taut) (non-taut) non-taut */ /* O = O, S, Se, Te */ /* Solution: move (-) from O(-)-AB to AB=O */ /*--------------------------------------------------------------*/ int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat; short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -O(-), non-taut */ num_DB_O_Neutr < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence ) { iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ num_SB_O_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* in orig.InChI: not an endpoint, has no H */ /*pStruct->endpoint[i] && -- modificatuion#1 */ !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_O_Minus[num_SB_O_Minus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 07 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 ) { /*--------------------------------------------------------------*/ /* case 07a: restored: O(-)-N(V)B=O original: O=N(V)B-O(-) */ /* FixH: 0 0 0 -1 */ /* MobH: 0 0 0 1 */ /* non-taut (non-taut) non-taut non-taut */ /* non-taut (taut) non-taut non-taut */ /* O = O, S, Se, Te */ /* Solution: move (-) from O(-)-AB to AB=O */ /*--------------------------------------------------------------*/ int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat, iN; short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -O(-), non-taut */ num_DB_O_Neutr < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence ) { iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ num_SB_O_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && /*at_Mobile_H_Revrs && !at_Mobile_H_Revrs[iat].endpoint &&*/ /* in orig.InChI: not an endpoint, has no H */ !pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has N(V) neighbor */ 1 == at2[iat].valence && at2[iN=at2[iat].neighbor[0]].chem_bonds_valence==5 && !at2[iN].charge && pVA[iN].cNumValenceElectrons == 5 && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_O_Minus[num_SB_O_Minus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 07 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( /*(pc2i->len_c2at >= 1 || pc2i->nNumRemHRevrs) &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { /*----------------------------------------------------------------*/ /* case 08: restored: O(-)-AB=N- OH- orig. O=AB-N(-)- OH- */ /* FixH: 1 0 0 0 0 1 */ /* MobH: 0 0 1 0 0 0 */ /* may be taut or not non-taut taut taut taut */ /* ADP: one t-group or more endpoints */ /* O(-) = S, Se, Te; N = N; */ /* Solution: move (-) from O(-) to =N-; avoid stereogenic DB on N */ /*----------------------------------------------------------------*/ int num_DB_N_Neutr = 0, num_SB_O_Minus = 0, iat; short iat_DB_N_Neutr[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=-1, has no H, has single bond, O, S, Se, Te */ num_SB_O_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && /* in orig.InChI: an endpoint, may have fixed-H */ pStruct->endpoint[i] && /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ !(nMobHInChI && nMobHInChI[i] ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_O_Minus[num_SB_O_Minus ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* in restored atom: charge=0, has no H, has double non-stereogenic bond, N */ num_DB_N_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && !at2[iat].sb_parity[0] && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && /* in orig.InChI: an endpoint, has no fixed-H */ pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_DB_N_Neutr[num_DB_N_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_DB_N_Neutr, num_SB_O_Minus ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); /* allow stereobonds in rings change */ if ( forbidden_stereo_edge_mask ) RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Moved (-) charge to =N- => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom (-)O-) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 08 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); if ( forbidden_stereo_edge_mask ) SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 2 ) { /*--------------------------------------------------------*/ /* case 09: restored: NH2(+)=C--NH2 orig.: NH2-C(+)-NH2 */ /* FixH: 2 | 2 0 | 0 */ /* MobH: 0 0 2 2 */ /* N = N, taut taut non-taut non-taut*/ /* Solution: move (+) from NH2(+) to C */ /*--------------------------------------------------------*/ int iat; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && /* orig. InChI info: */ !pc2i->c2at[i].endptInChI && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI && /* reversed structure info: */ pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs && !pc2i->c2at[i].nMobHRevrs && pc2i->c2at[i].nAtChargeRevrs == 1 && at2[iat].valence + 1 == at2[iat].chem_bonds_valence && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { EdgeIndex eNC = NO_VERTEX, eCPlusC; int iNH2, iatC, iatNH2, icNH2; /* found NH2(+)=; locate =C< and find whether it has -NH2 neighbor */ for ( j = 0; j < at2[iat].valence; j ++ ) { if ( at2[iat].bond_type[j] == BOND_TYPE_DOUBLE ) break; } if ( j == at2[iat].valence ) continue; eNC = pBNS->vert[iat].iedge[j]; /* edge NH2(+)=C */ iatC = at2[iat].neighbor[j]; if ( pVA[iatC].cNumValenceElectrons != 4 || pVA[iatC].cMetal || at2[iatC].charge || at2[iatC].valence != 3 || at2[iatC].valence+1 != at2[iatC].chem_bonds_valence || (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) < 0 || pBNS->edge[eCPlusC].forbidden) continue; for ( j = 0; j < at2[iatC].valence; j ++ ) { iatNH2 = at2[iatC].neighbor[j]; if ( iatNH2 == iat || pVA[iatNH2].cNumValenceElectrons != 5 || pVA[iatNH2].cPeriodicRowNumber != 1 || !at2[iatNH2].num_H || at2[iatNH2].charge) continue; icNH2 = pStruct->nAtno2Canon[0][iatNH2]; for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { if ( iatNH2 == pc2i->c2at[iNH2].atomNumber ) break; } if ( iNH2 == pc2i->len_c2at ) continue; if ( (pc2i->c2at[iNH2].nValElectr == 5 && pc2i->c2at[iNH2].nPeriodNum == 1) && /* orig. InChI info: */ !pc2i->c2at[iNH2].endptInChI && pc2i->c2at[iNH2].nFixHInChI == 0 && pc2i->c2at[iNH2].nMobHInChI && /* reversed structure info: */ pc2i->c2at[iNH2].endptRevrs && pc2i->c2at[iNH2].nFixHRevrs && !pc2i->c2at[iNH2].nMobHRevrs && pc2i->c2at[iNH2].nAtChargeRevrs == 0 && at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence ) { /* we have found NH2(+)=, =C<, and bond between them */ if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { goto exit_function; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; pe = pBNS->edge + eNC; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 09 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK pe->forbidden &= forbidden_edge_mask_inv; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ break; } } } } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 2 ) { /*--------------------------------------------------------*/ /* case 10: restored: NH2-X(+)-NH- orig.: NH2(+)=X-NH- */ /* FixH: 0 0 2 1 */ /* MobH: 2 1 0 0 */ /* N = N,O,S,Se,Te non-taut non-taut taut taut */ /* Solution: move (+) from X(+) to NH2 or NH */ /*--------------------------------------------------------*/ int iat; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { if ( pc2i->c2at[i].nValue ) continue; iat = pc2i->c2at[i].atomNumber; if ( (pc2i->c2at[i].nValElectr == 6 || pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && /* orig. InChI info: */ pc2i->c2at[i].endptInChI && pc2i->c2at[i].nFixHInChI && !pc2i->c2at[i].nMobHInChI && /* reversed structure info: */ !pc2i->c2at[i].endptRevrs && !pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs && pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].valence == at2[iat].chem_bonds_valence && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { EdgeIndex eCPlusC, eCPlusNH2, bContinue=1; int iNH2, iatC, iatNH2, icNH2, j1, j2; BNS_EDGE *pe_iat, *pe_iNH2; /* found NH2- locate -X(+) and find whether it has another -NH2 neighbor */ for ( j1 = 0; j1 < at2[iat].valence && bContinue; j1 ++ ) { if ( at2[iat].bond_type[j1] == BOND_TYPE_SINGLE && at2[iatC = at2[iat].neighbor[j1]].charge == 1 && (4 <= pVA[iatC].cNumValenceElectrons && pVA[iatC].cNumValenceElectrons <= 6) && at2[iatC].valence == at2[iatC].chem_bonds_valence && (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusC].forbidden) { /* found a candidate for X; find another NH2 */ for ( j2 = 0; j2 < at2[iatC].valence && bContinue; j2 ++ ) { if ( at2[iatC].bond_type[j2] == BOND_TYPE_SINGLE && iat != (iatNH2 = at2[iatC].neighbor[j2]) && at2[iatNH2].charge == 0 && at2[iatNH2].num_H && (pVA[iatNH2].cNumValenceElectrons==5 || pVA[iatNH2].cNumValenceElectrons==6) && at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence && (eCPlusNH2=pVA[iatNH2].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusNH2].forbidden) { for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { if ( iatNH2 != pc2i->c2at[iNH2].atomNumber || pc2i->c2at[iNH2].nValue ) continue; /* check the second -NH */ icNH2 = pStruct->nAtno2Canon[0][iatNH2]; /* canon number -1 */ if ( /* orig. InChI info: */ pc2i->c2at[iNH2].endptInChI && pc2i->c2at[iNH2].nFixHInChI && !pc2i->c2at[iNH2].nMobHInChI && /* reversed structure info: */ !pc2i->c2at[iNH2].endptRevrs && !pc2i->c2at[iNH2].nFixHRevrs && pc2i->c2at[iNH2].nMobHRevrs && pc2i->c2at[iNH2].nAtChargeRevrs == 0 ) { /* we have found NH-X(+)-NH; remove charge from X(+) */ pe_iat = pBNS->edge + pBNS->vert[iat].iedge[j1]; pe_iNH2 = pBNS->edge + pBNS->vert[iatC].iedge[j2]; /* pick up one of -NH to move (+) to it */ if ( !pe_iat->forbidden && pBNS->edge[e].flow ) { pe = pBNS->edge + e; } else if ( !pe_iNH2->forbidden && pBNS->edge[eCPlusNH2].flow ) { pe = pBNS->edge + eCPlusNH2; } else { continue; /* none of the two -X(+)- bonds may be changed */ } if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { goto exit_function; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /*pe->forbidden |= forbidden_edge_mask;*/ pe->flow -= delta; /* add (+) to -NHm */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 10 */ bContinue = 0; pc2i->c2at[i].nValue = 1; /* mark as used */ pc2i->c2at[iNH2].nValue = 1; /* mark as used */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK /*pe->forbidden &= forbidden_edge_mask_inv;*/ RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ break; } } /* iNH2: pc2i->c2at[iNH2] cycle */ } } /* j2: iatC neighbors cycle */ } } /* j1: iat neighbors cycle */ } } /* i: pc2i->c2at[i] cycle */ if ( cur_success ) { /* for ( i = 0; i < pc2i->len_c2at; i ++ ) { pc2i->c2at[i].nValue = 0; } */ tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( /*pc2i->len_c2at >= 1 &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { /*--------------------------------------------------------------*/ /* case 11: restored: NH(+)=AB-N< OH- orig. NH-AB=N(+)< OH- */ /* FixH: 0 0 0 1 0 1 */ /* MobH: 1 0 1 0 0 0 */ /* non-taut. taut taut */ /* ADP: one t-group or more endpoints */ /* NH(+)= => N, O, S, Se; -N< => N */ /* Solution: move (+) from NH(+) to -N< */ /*--------------------------------------------------------------*/ int num_SB_Neutr = 0, num_DB_Charged = 0, iat; short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; /* search for NH(+)= */ /* search for -N< */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ num_DB_Charged < MAX_DIFF_FIXH && at2[iat].charge == 1 && at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || pVA[iat].cNumValenceElectrons == 6 ) && /* in orig.InChI: an endpoint, has fixed-H */ /*pStruct->endpoint[i] &&*/ (pStruct->fixed_H && pStruct->fixed_H[i]) && /*!(nMobHInChI && nMobHInChI[i] ) &&*/ /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_DB_Charged[num_DB_Charged ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } else if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ num_SB_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 ) && /* in orig.InChI: an endpoint, has fixed-H */ /*pStruct->endpoint[i] &&*/ !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_SB_Neutr[num_SB_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { /* detected; attempt to fix */ BNS_VERTEX *pv1n, *pv2n; BNS_EDGE *pe1n, *pe2n; Vertex v1n, v2n; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { iat = iat_DB_Charged[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { pe1n = pBNS->edge + pv1->iedge[j]; if ( pe1n->flow && !pe1n->forbidden ) { pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); break; } } if ( j < 0 ) continue; /* not found */ for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { pe2n = pBNS->edge + pv2->iedge[j]; if ( pe2n->flow && !pe2n->forbidden ) { pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); break; } } if ( j < 0 ) continue; /* not found */ pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && (nDeltaCharge == 0 || nDeltaCharge == 1) ) { /* before setting flows the structure could be: [NH+ neigh, v1n]=e1n=[NH+,v1]-pe-[+,v2]=e2n=[another at or its chargeStruct] or [NH+ or ChStr, v1n]=pe1n=[NH+ or ChStr, v1]-pe-[+,v2]=pe2n=[at2 or ChStr, v2n] ^ ^ ^ NH+(+)edge | N (+) edge: only | these are not forbidden | hetero (+) vertex After setting flows (* mark radicals, =pe= is forbidden): *[NH+ or ChStr, v1n]-pe1n-[NH+ or ChStr, v1]=pe=[+,v2]-pe2n-[at2 or ChStr, v2n]* ^ ^ ^ NH+(+)edge | N (+) edge: only | these are not forbidden | hetero (+) vertex Flow in pe1n and pe2n will or will not change, depending on the structure. Consider what happens if pe2n changes. It may only increment. If pe2n flow increments then another (+)edge flow dectrements. If [at2 or ChStr, v2n] is at2 then at2 charge would change from (+) to 0, and another N charge would change from 0 to (+), giving tot. change of number of charges (-1)+(+1)=0. However, if [at2 or ChStr, v2n] is ChargeStruct then at2 will not be on the alt path and only the creation of another (+) will be detected. */ /* Removed charge from O(+) => nDeltaCharge == -1 */ /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 11 */ } } else { pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && pc2i->nNumRemHInChI >= -1 && /* 2006-03-03 */ (pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs || pc2i->nNumTgRevrs > 1) /* ADP in InChI */ ) { /*--------------------------------------------------------------*/ /* case 12: restored: O=AB-N< original: (-)O-AB=N(+)< */ /* FixH: 0 0 0 0 */ /* MobH: 0 0 0 0 */ /* non-taut taut */ /* O = O, S, Se, N; N = N; */ /* restored atom O is not tautomeric; original atom O is taut. */ /* original struct has 1 t-group; restored has less endpoints */ /* and/or possibly >1 t-groups */ /* Solution: separate charges between O= and -N< */ /* allow moving charge to N(V) to make it N(IV)(+) */ /*--------------------------------------------------------------*/ int bOnly_N_V = 1; cur_success = 0; while( 1 ) { int num_SB_N_Neutr = 0, num_DB_O = 0, iat, num_N_V=0, bN_V; short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -O(-) */ num_DB_O < MAX_DIFF_FIXH && (pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ || pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 /* N */ ) && pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ !pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && ((pc2i->c2at[i].nValElectr == 6)? (at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2): (pc2i->c2at[i].nValElectr == 5)? (at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3): 0)) { iat_DB_O[num_DB_O ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } } for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; bN_V = 0; if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ num_SB_N_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && (at2[iat].valence == at2[iat].chem_bonds_valence || (bN_V = at2[iat].valence+2 == at2[iat].chem_bonds_valence)) && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && /* in orig.InChI: not an endpoint, has no H */ !pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i]) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( bOnly_N_V && bN_V && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { if ( !num_N_V ) { /* switch to N(V) only mode */ CurrEdges.num_edges = 0; num_SB_N_Neutr = 0; } iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; num_N_V ++; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( !num_N_V ) { iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } /* in addition, permit N(V)=>N(IV)(+) change by allowing charge flower edge change flow */ if ( bN_V && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } } } } if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { /* detected; attempt to fix */ BNS_EDGE *pe_CMinus; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { iat = iat_DB_O[i]; pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; pe_CMinus->forbidden &= forbidden_edge_mask_inv; pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 12 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } break; } else if ( bOnly_N_V ) { bOnly_N_V = 0; } else { break; } } } if ( pc2i->nNumTgDiffMinus /*|| pc2i->nNumTgDiffH */ /* no ADP in InChI needed */ ) { /*--------------------------------------------------------------*/ /* | | */ /* case 13: restored: O=AB=N= original: (-)O-AB-N(+)= */ /* FixH: 0 0 0 0 */ /* MobH: 0 0 0 0 */ /* non-taut taut non-taut */ /* O = O, S, Se, N; N = N, P, ... */ /* t-group in original has same num. endpoints */ /* same num_H and less (-) than in the restored structure */ /* original atom O is tautomeric, N is not taut in both */ /* original struct has 1 t-group; restored has less endpoints */ /* and/or possibly >1 t-groups */ /* Solution: separate charges between O= and -N< */ /* allow moving charge to N(V) to make it N(IV)(+) */ /*--------------------------------------------------------------*/ int itg; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? pStruct->pOneINChI[1]->nNum_H : (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; /* find whether this may help */ for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { if ( pStruct->ti.t_group[itg].nNumEndpoints == pStruct->One_ti.t_group[itg].nNumEndpoints && pStruct->ti.t_group[itg].num[0] - pStruct->ti.t_group[itg].num[1] == pStruct->One_ti.t_group[itg].num[0] - pStruct->One_ti.t_group[itg].num[1] && pStruct->ti.t_group[itg].num[1] > pStruct->One_ti.t_group[itg].num[1]) { /* restored InChI t-group has more (-) and same number of H */ int num_SB_N_Neutr = 0, num_DB_O = 0, iat; short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* orig. InChI info: -O(-) */ num_DB_O < MAX_DIFF_FIXH && (pVA[i].cNumValenceElectrons == 6 /* O, S, Se, Te */ ) && pStruct->endpoint[i] == itg+1 && (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i]) && /* reversed structure info: */ /*!pc2i->c2at[i].endptRevrs &&*/ !(num_Fixed_H_Revrs && num_Fixed_H_Revrs[iat]) && !(pnMobHRevrs && pnMobHRevrs[iat]) && at2[iat].charge == 0 && at2[iat].num_H == 0 && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { iat_DB_O[num_DB_O ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } else if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ num_SB_N_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && /*at2[iat].valence == at2[iat].chem_bonds_valence ||*/ (at2[iat].valence==4 && at2[iat].chem_bonds_valence==5) && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber >= 1 && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && /* in orig.InChI: not an endpoint, has no H */ !pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i]) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { /* detected; attempt to fix */ BNS_EDGE *pe_CMinus; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { iat = iat_DB_O[i]; pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; pe_CMinus->forbidden &= forbidden_edge_mask_inv; pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 13 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } break; }/* else if ( bOnly_N_V ) { bOnly_N_V = 0; } */ break; } } } if ( (pc2i->nNumTgInChI <= 1 && pc2i->nNumRemHInChI > pc2i->nNumRemHRevrs || pc2i->len_c2at) && bHas_N_V( at2, pStruct->num_atoms) ) { /*-----------------------------------------------------------------*/ /* | | */ /* case 14: restored:-N=AB=N=CD-XH original: (-)N-AB-N(+)=CD-XH */ /* FixH: 0 0 0/1 0 1 */ /* MobH: 0 0 1/0 0 0 */ /* non-taut n/t any non any */ /* taut */ /* X = O, S, Se, N; N = N */ /* t-group in original may have more (-) than in restored */ /* same num_H and less (-) than in the restored structure */ /* atom N(V)/N(IV)(+) is not taut in both */ /* The following transformation should be possible: */ /* | | */ /* N=AB=N=CD-XH -> (-)N-AB-N-CD=XH(+) */ /* This allows ADP to remove H(+) from -XH */ /* As the result, the original structure has 0 or 1 t-group */ /* Solution: separate charges between -N(III)= and N(V) */ /*-----------------------------------------------------------------*/ AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; int num_N_V = 0, iat, i1, i2, i3, e1Flower, e1Plus, e2Plus, e2Minus, e3Plus; int max_success = pc2i->nNumRemHInChI - pc2i->nNumRemHRevrs; short iat_N_V_Array[MAX_DIFF_FIXH]; EDGE_LIST iat_X_List, iat_N_III_List; AllocEdgeList( &iat_X_List, EDGE_LIST_CLEAR ); AllocEdgeList( &iat_N_III_List, EDGE_LIST_CLEAR ); cur_success = 0; ret = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; /* search for N(V), 3 bonds */ if ( /* restored structure */ num_N_V < MAX_DIFF_FIXH && at2[iat].chem_bonds_valence == 5 && at2[iat].valence == 3 && !at2[iat].charge && !at2[iat].radical && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && !( at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && !at2[iat].num_H && (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow /* no charge */ && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && !pBNS->edge[j].flow /* neutral, valence=5 */ && /* orig. InChI */ !pStruct->endpoint[i] && !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { iat_N_V_Array[num_N_V ++] = iat; } else /* search for -N= */ if ( /* restored structure */ at2[iat].chem_bonds_valence == 3 && at2[iat].valence == 2 && !at2[iat].charge && !at2[iat].radical && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && !at2[iat].num_H && (e = pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow /* no charge */ && /* orig. InChI */ /*!pStruct->endpoint[i] &&*/ !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { if ( ret = AddToEdgeList( &iat_N_III_List, iat, 32 ) ) { goto exit_case_14; } } else /* search for -OH -NH-, -NH2 */ if ( /* restored structure */ at2[iat].chem_bonds_valence == at2[iat].valence && !at2[iat].charge && !at2[iat].radical && (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || pVA[iat].cNumValenceElectrons == 6 ) && at2[iat].num_H && (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow /* no charge */ && /* orig. InChI */ !(nMobHInChI && nMobHInChI[i]) && pStruct->fixed_H[i] ) { if ( ret = AddToEdgeList( &iat_X_List, iat, 32 ) ) { goto exit_case_14; } } } if ( !max_success ) { max_success = inchi_min( num_N_V, iat_N_III_List.num_edges ); max_success = inchi_min( max_success, iat_X_List.num_edges ); } if ( num_N_V && iat_N_III_List.num_edges && iat_X_List.num_edges ) { for ( i1 = 0; i1 < num_N_V && cur_success < max_success; i1 ++ ) { int iat_N_V = iat_N_V_Array[i1]; if ( NO_VERTEX == iat_N_V || 0 >= (e1Plus = pVA[iat_N_V].nCPlusGroupEdge-1) || NO_VERTEX == (e1Flower = GetChargeFlowerUpperEdge( pBNS, pVA, e1Plus )) || 1 != pBNS->edge[e1Plus].flow || 0 != pBNS->edge[e1Flower].flow ) { continue; } for ( i2 = iat_N_III_List.num_edges-1; 0 <= i2 && cur_success < max_success; i2 -- ) { int iat_N_III = iat_N_III_List.pnEdges[i2]; if ( NO_VERTEX == iat_N_III || 0 >= (e2Minus = pVA[iat_N_III].nCMinusGroupEdge-1) || 0 >= (e2Plus = pVA[iat_N_III].nCPlusGroupEdge-1) || 0 != pBNS->edge[e2Minus].flow || 1 != pBNS->edge[e2Plus].flow ) { /* do not consider this atom anymore */ iat_N_III_List.pnEdges[i2] = NO_VERTEX; continue; } for ( i3 = iat_X_List.num_edges-1; 0 <= i3 && cur_success < max_success; i3 -- ) { int iat_X = iat_X_List.pnEdges[i3]; BNS_VERTEX *pv1n, *pv2n; BNS_EDGE *pe1n, *pe2n, *pe1Plus, *pe2Minus, *pe3Plus; Vertex v1n, v2n; ret = 0; if ( NO_VERTEX == iat_X || 0 >= (e3Plus = pVA[iat_X].nCPlusGroupEdge-1) || 1 != pBNS->edge[e3Plus].flow ) { /* do not consider this atom anymore */ iat_X_List.pnEdges[i3] = NO_VERTEX; continue; } /* all is ready to check whether the following applies: forbid changes of all charges and N,P,... flowers allow to change edges: e2Minus, e3Plus Increment flow in e1Flower The result should be: increase in number of charges by 2 */ pe1Plus = pBNS->edge + e1Plus; /* N(V) positive charge edge */ pe2Minus = pBNS->edge + e2Minus; /* =N- negative charge edge */ pe3Plus = pBNS->edge + e3Plus; /* -XH positive charge edge */ pe = pBNS->edge + e1Flower; /* N(V) flower edge */ pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { pe1n = pBNS->edge + pv1->iedge[j]; if ( pe1n->flow && !pe1n->forbidden && pe1n != pe1Plus ) { pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); break; } } if ( j < 0 ) continue; /* not found -- should not happen */ for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ pe2n = pBNS->edge + pv2->iedge[j]; if ( pe2n->flow && !pe2n->forbidden && pe2n != pe1Plus ) { pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); break; } } if ( j < 0 ) continue; /* not found -- should not happen */ delta = 1; pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); /* allow two charges to change */ pe2Minus->forbidden &= forbidden_edge_mask_inv; pe3Plus->forbidden &= forbidden_edge_mask_inv; /* test #1 */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); INCHI_HEAPCHK if ( ret < 0 ) { goto exit_case_14; } else if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && nDeltaCharge == 2 ) { ; /* success */ } else { ret = 0; } /* restore BNS */ pe2Minus->forbidden |= forbidden_edge_mask; pe3Plus->forbidden |= forbidden_edge_mask; pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; if ( ret == 1 ) { /* test #2: check if charge separation is possible */ pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; /* allow two charges (N(V) and N(III)) to change */ pe2Minus->forbidden &= forbidden_edge_mask_inv; pe1Plus->forbidden &= forbidden_edge_mask_inv; /* test #2 */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && nDeltaCharge == 2 ) { /* success; actually change charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 14 */ } } if ( ret <= 0 ) { /* failed: restore BNS flow */ pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); if ( ret > 0 ) { /* do not repeat for the same atoms */ iat_N_V_Array[i1] = NO_VERTEX; iat_N_III_List.pnEdges[i2] = NO_VERTEX; iat_X_List.pnEdges[i3] = NO_VERTEX; } if ( ret < 0 ) { goto exit_case_14; } if ( ret > 0 ) { break; } } /* i3 cycle */ if ( ret > 0 ) { break; } } /* i2 cycle */ } } exit_case_14: AllocEdgeList( &iat_X_List, EDGE_LIST_FREE ); AllocEdgeList( &iat_N_III_List, EDGE_LIST_FREE ); RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ if ( ret < 0 ) { goto exit_function; } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->nNumTgMRevrs > pc2i->nNumTgMInChI || pc2i->nNumRemHRevrs < pc2i->nNumRemHInChI || pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgInChI <= 1 && pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) { /*--------------------------------------------------------------*/ /* case 15: restored: -(+)O=AB-N< orig: -O-AB=N(+)< */ /* (a) restored t-groups have more (-) than in original InChI */ /* (b) Mobile-H charge: restored > original InChI *and* */ /* removed H: restored < original InChI */ /* (c) restored t-groups have less endpnoits than in orig InChI */ /* O = O, S, Se, Te; N = N */ /* Solution: move (+) from -O(+)= to -N< */ /*--------------------------------------------------------------*/ int num_SB_Neutr = 0, num_DB_Charged = 0, iat; short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; /* search for -O(+)= */ /* search for -N< */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* -O(+)= in restored atom: charge=1, has no H, a double bond */ num_DB_Charged < MAX_DIFF_FIXH && at2[iat].charge == 1 && !at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 ) && /* in orig.InChI: an endpoint, has fixed-H */ /*pStruct->endpoint[i] &&*/ !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_DB_Charged[num_DB_Charged ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } else if ( /* -N< in restored atom: charge=0, has no H, has no double bond, N only */ num_SB_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 ) && /* in orig.InChI: an endpoint, has fixed-H */ /*pStruct->endpoint[i] &&*/ !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_SB_Neutr[num_SB_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { /* detected; attempt to fix */ BNS_VERTEX *pv1n, *pv2n; BNS_EDGE *pe1n, *pe2n; Vertex v1n, v2n; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { iat = iat_DB_Charged[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { pe1n = pBNS->edge + pv1->iedge[j]; if ( pe1n->flow && !pe1n->forbidden ) { pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); break; } } if ( j < 0 ) continue; /* not found */ for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ pe2n = pBNS->edge + pv2->iedge[j]; if ( pe2n->flow && !pe2n->forbidden ) { pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); break; } } if ( j < 0 ) continue; /* not found */ pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && (nDeltaCharge == 0 || nDeltaCharge == 1) ) { /* Moved charge from O(+) to -N< => nDeltaCharge == 1 or 0 if pe2n = -N< charge edge */ /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 15 */ } } else { pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->nNumTgDiffMinus ) { /*----------------------------------------------------------------*/ /* case 16: restored: O=X-NH(-) orig.: O(-)-X=NH */ /* t-group: (H,-) (2H) */ /* O(-) = S, Se, Te; N = N; */ /* Solution: move (-) from O(-) to -NH(-) */ /*----------------------------------------------------------------*/ int num_SB_N_Minus = 0, num_DB_O_Neutr = 0, iat, itg; short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { if ( pStruct->ti.t_group[itg].nNumEndpoints != pStruct->One_ti.t_group[itg].nNumEndpoints || pStruct->ti.t_group[itg].num[1] >= pStruct->One_ti.t_group[itg].num[1] ) { continue; } CurrEdges.num_edges = num_SB_N_Minus = num_DB_O_Neutr = 0; cur_success = 0; for ( j = 0, k = pStruct->One_ti.t_group[itg].nFirstEndpointAtNoPos; j < pStruct->One_ti.t_group[itg].nNumEndpoints; j ++ ) { i = pStruct->One_ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=0, has no H, has double bond, O, S, Se, Te */ num_DB_O_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && /* in orig.InChI: an endpoint, may have fixed-H */ pStruct->endpoint[i] && /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ !(nMobHInChI && nMobHInChI[i] ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* in restored atom: charge=-1, has H, has double bond, N */ num_SB_N_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && /* in orig.InChI: an endpoint, has no fixed-H */ pStruct->endpoint[i] && (pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_SB_N_Minus[num_SB_N_Minus ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } } if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O_Neutr ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); /* allow stereobonds in rings change */ /* if ( forbidden_stereo_edge_mask ) RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); */ delta = 1; for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_N_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /*pe->forbidden |= forbidden_edge_mask;*/ pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Moved (-) charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -NH(-)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 16 */ } } else { pe->forbidden &= forbidden_edge_mask_inv; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); /* if ( forbidden_stereo_edge_mask ) SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); */ } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } } if ( pc2i->nNumRemHInChI < pc2i->nNumRemHRevrs ) { /*--------------------------------------------------------------*/ /* case 17: restored: OH(+)=AB-O- orig. HO-AB=O(+)- */ /* number of removed H: n+m n */ /* OH(+) = N, O, S, Se; -O- = P,As,O,S,Se,Te,F,Cl,Br,I */ /* Solution: move (+) from OH(+) to -O- */ /*--------------------------------------------------------------*/ int num_SB_Neutr = 0, num_DB_Charged = 0, iat; short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ num_DB_Charged < MAX_DIFF_FIXH && at2[iat].charge == 1 && at2[iat].num_H && at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { iat_DB_Charged[num_DB_Charged ++] = iat; /* if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } */ } else if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ num_SB_Neutr < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1 ) && /* in orig.InChI: not an endpoint */ !pStruct->endpoint[i] && !(pStruct->fixed_H && pStruct->fixed_H[i]) && !(nMobHInChI && nMobHInChI[i] ) && /* has (+) edge */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { iat_SB_Neutr[num_SB_Neutr ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { BNS_VERTEX *pv1n, *pv2n; BNS_EDGE *pe1n, *pe2n; Vertex v1n, v2n; num_try = inchi_min( num_try, pc2i->nNumRemHRevrs-pc2i->nNumRemHInChI); /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { iat = iat_DB_Charged[i]; pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; if ( pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { pe1n = pBNS->edge + pv1->iedge[j]; if ( pe1n->flow && !pe1n->forbidden ) { pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); break; } } if ( j < 0 ) continue; /* not found */ for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ pe2n = pBNS->edge + pv2->iedge[j]; if ( pe2n->flow && !pe2n->forbidden ) { pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); break; } } if ( j < 0 ) continue; /* not found */ pe->flow += delta; pe1n->flow -= delta; pe2n->flow -= delta; pv1n->st_edge.flow -= delta; pv2n->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || vPathEnd == v2n && vPathStart == v1n) && (nDeltaCharge == 0 || nDeltaCharge == 1) ) { /* Moved charge from OH(+) to -O- => nDeltaCharge == 1 or 0 if pe2n = -O- charge edge */ /* Flow change on pe (+)charge edge (atom OH(+)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 17 */ } } else { pe->flow -= delta; pe1n->flow += delta; pe2n->flow += delta; pv1n->st_edge.flow += delta; pv2n->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( (pc2i->nNumTgInChI && pStruct->endpoint && pc2i->nNumTgMInChI > pc2i->nNumTgMRevrs && pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs ) ) { /*-----------------------------------------------------------------*/ /* */ /* case 18: restored:-N=AB-X -(-)N-AB-X(+) */ /* FixH: 0 0 0 0 */ /* MobH: 0 0 0 0 */ /* non non taut non */ /* taut taut taut */ /* X = any heteroatom N=N */ /* t-group in original has (Hn,-m) in the restored: (Hn,-m+1) */ /* same num_H and more (-) than in the restored structure */ /* atom X is not taut in both */ /* Solution: separate charges between -N(III)= and X */ /*-----------------------------------------------------------------*/ AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; int iat, e1, itg, max_success; CurrEdges.num_edges = 0; cur_success = 0; ret = 0; /* search for -N= */ for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { if ( pStruct->ti.t_group[itg].nNumEndpoints <= pStruct->One_ti.t_group[itg].nNumEndpoints || pStruct->ti.t_group[itg].num[1] <= pStruct->One_ti.t_group[itg].num[1] ) { continue; } CurrEdges.num_edges = 0; cur_success = 0; for ( j = 0, k = pStruct->ti.t_group[itg].nFirstEndpointAtNoPos; j < pStruct->ti.t_group[itg].nNumEndpoints; j ++ ) { i = pStruct->ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ iat = nCanon2AtnoRevrs[i]; if ( !pStruct->endpoint[i] || !at_Mobile_H_Revrs || at_Mobile_H_Revrs[iat].endpoint || pVA[i].cNumValenceElectrons != 5 || pVA[i].cPeriodicRowNumber != 1 || 2 != at2[iat].valence || at2[iat].num_H || at2[iat].radical || 0 <= (e1=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e1].flow || 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { continue; } /* found -N= */ if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( !(max_success = CurrEdges.num_edges) ) { goto exit_case_18; } /* search for X */ for ( i = 0; i < pStruct->num_atoms && cur_success < max_success; i ++ ) { iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] || !pVA[i].cNumValenceElectrons || pVA[i].cNumValenceElectrons == 4 || at2[iat].num_H || at2[iat].radical || 0 <= (e1=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].flow || 0 > (e=pVA[iat].nCPlusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow != 1 ) { continue; } /* try to move the charge */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); pe = pBNS->edge + e; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); delta = 1; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Created (-) charge on -N= => nDeltaCharge == 1 */ /* Flow change on pe (+)charge edge (atom X) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 18 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } exit_case_18: RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ if ( ret < 0 ) { goto exit_function; } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at >= 1 ) { /*--------------------------------------------------------------*/ /* case 19 restored: M--OH original: M(-)==OH(+) */ /* FixH: metal 0 1 */ /* MobH: 1 0 */ /* O = O, S, Se, Te; not taut. in InChI */ /* In restored structure has H; tautomeric or not tautomeric */ /* Solution: move (+) from -OH to M; charhe on M may vary */ /*--------------------------------------------------------------*/ int iat; EdgeIndex eOHPlus, eMPlus, eMMinus, eOMBond; BNS_EDGE *peOHPlus, *peMPlus, *peMMinus, *peOMBond; int iatMetal, ChargeOnMetal, DeltaChargeExpected; cur_success = 0; num_zero_ret = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =NH2(+), =OH(+) */ (pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && at2[iat].valence == 1 && at2[iat].valence == at2[iat].chem_bonds_valence && /* metal atom */ pVA[iatMetal=at2[iat].neighbor[0]].cMetal && (eMPlus=pVA[iatMetal].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eMPlus].forbidden && (eMMinus=pVA[iatMetal].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[eMMinus].forbidden && !pBNS->edge[eOMBond=pBNS->vert[iat].iedge[0]].forbidden ) { /* -OH charge edges */ if ( ret = AddToEdgeList( &CurrEdges, iat, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( CurrEdges.num_edges ) { /* detected; fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); for ( i = 0; i < CurrEdges.num_edges; i ++ ) { /* v1 is -OH, v2 is adjacent to it Metal */ iat = CurrEdges.pnEdges[i]; iatMetal = at2[iat].neighbor[0]; peOHPlus = pBNS->edge + (eOHPlus = pVA[iat].nCPlusGroupEdge-1); peMPlus = pBNS->edge + (eMPlus = pVA[iatMetal].nCPlusGroupEdge-1); peMMinus = pBNS->edge + (eMMinus = pVA[iatMetal].nCMinusGroupEdge-1); peOMBond = pBNS->edge + (eOMBond =pBNS->vert[iat].iedge[0]); /* remove forbidden edge masks */ peMPlus->forbidden &= forbidden_edge_mask_inv; peMMinus->forbidden &= forbidden_edge_mask_inv; peOMBond->forbidden &= forbidden_edge_mask_inv; ChargeOnMetal = (peMPlus->cap - peMPlus->flow) - peMMinus->flow; if ( 1 == ChargeOnMetal ) { /* We are going to subtract 1 from the charge on Metal */ /* Added (+)charge to -OH is not known to RunBnsTestOnce() */ DeltaChargeExpected = -1; /* charge will become = 0 */ } else if ( 0 == ChargeOnMetal ) { DeltaChargeExpected = 1; /* charge on Metal will be created */ } else { DeltaChargeExpected = 0; } delta = 1; pe = peOHPlus; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; /* remove (-) from AB-O(-) */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == DeltaChargeExpected ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 19 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK /* set forbidden edge masks back */ peMPlus->forbidden |= forbidden_edge_mask; peMMinus->forbidden |= forbidden_edge_mask; peOMBond->forbidden |= forbidden_edge_mask; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } } if ( pc2i->len_c2at > 1 && pc2i->nNumTgRevrs && pc2i->nNumTgInChI) { /*--------------------------------------------------------------*/ /* case 20: restored: O(-)-AB=N- original: O=AB-N(-)- */ /* FixH: 0 0 0 -1 */ /* MobH: 0 0 0 1 */ /* taut non-taut non-taut taut */ /* or taut no H */ /* no H */ /* O = O, S, Se; N = N, O, S, Se, Te; */ /* restored atoms are taut/non-taut; original are opposite. */ /* Solution: move (-) from O(-) to =N- */ /*--------------------------------------------------------------*/ int num_SB_O_Minus = 0, num_DB_N = 0, iat; short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_N[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ cur_success = 0; CurrEdges.num_edges = 0; /* clear current edge list */ for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =O or -N= */ num_DB_N < MAX_DIFF_FIXH && pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow == 0 && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && /* if more than 1 t-group are in orig. InChI then do not move (-) to N */ (pc2i->nNumTgInChI == 1 || pc2i->c2at[i].nValElectr == 6) && /* reversed structure info: */ !pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { iat_DB_N[num_DB_N ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( /* orig. InChI info: -O(-) */ num_SB_O_Minus < MAX_DIFF_FIXH && !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow == 1 && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && pc2i->c2at[i].nValElectr == 6 && /* reversed structure info: */ pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { iat_SB_O_Minus[num_SB_O_Minus ++] = iat; } } if ( !num_DB_N ) { /* search among N that are tautomeric in both cases */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ if ( !pStruct->endpoint[i] ) { continue; } iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ num_DB_N < MAX_DIFF_FIXH && at2[iat].charge == 0 && !at2[iat].num_H && at2[iat].valence + 1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && /* in orig.InChI: an endpoint, has no H */ !(pStruct->fixed_H && pStruct->fixed_H[i]) && /*!(nMobHInChI && nMobHInChI[i] ) &&*/ /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow ) { iat_DB_N[num_DB_N ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } if ( num_try = inchi_min( num_SB_O_Minus, num_DB_N ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-) charge to =N- => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -O(-)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 20 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumTgHInChI && pStruct->endpoint ) { /*--------------------------------------------------------------*/ /* O(-) O */ /* | || */ /* case 21: restored: R=S=O original: R-S=O */ /* | | */ /* O(-) O(-) */ /* All O are taut R is not taut */ /* */ /* In addition, another atom O that should have been tautomeric */ /* or has H(+) added in Mobile-H layer is not like that */ /* O = O, S, Se; S=S, Se, Te */ /* Solution: move (-) from O(-) to =O */ /* these atoms are tautomeric in restored structure */ /*--------------------------------------------------------------*/ int num_SB_O_Minus = 0, num_DB_O = 0, iat, iS; short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_Central[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =O */ num_DB_O < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { iat_DB_O[num_DB_O ++] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ if ( !pStruct->endpoint[i] ) { continue; } iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ num_SB_O_Minus < MAX_DIFF_FIXH && at2[iat].charge == -1 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 6 && (at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && /* in orig.InChI: an endpoint, has no H */ !(pStruct->fixed_H && pStruct->fixed_H[i]) && /*!(nMobHInChI && nMobHInChI[i] ) &&*/ /* has (-) edge */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow ) { int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0, nNumOthers = 0, nNumNegEndp = 0; /* traverse neighbors of the centerpoint iS */ iS = at2[i].neighbor[0]; for ( j = 0; j < num_SB_O_Minus; j ++ ) { if ( iat_Central[j] == iS ) break; } if ( j < num_SB_O_Minus ) { continue; /* have already been there */ } for ( j = 0; j < at[iS].valence; j ++ ) { int bond_type = at2[iS].bond_type[j]; k = at2[iS].neighbor[j]; if ( k == i ) { continue; } if ( pStruct->endpoint[k] == pStruct->endpoint[i] ) { nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); } else if ( bond_type == BOND_TYPE_DOUBLE ) { nNumOtherDB ++; } else if ( bond_type == BOND_TYPE_SINGLE ) { nNumOtherSB ++; } else { nNumOthers ++; } if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { nNumNegEndp ++; } } if ( !nNumTautSB ) { continue; } if ( !( nNumOtherDB && nNumTautDB ) ) { continue; /* ignore */ } iat_SB_O_Minus[num_SB_O_Minus] = iat; iat_Central[num_SB_O_Minus ++] = iS; } } if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { iat = iat_SB_O_Minus[i]; pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-) charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 21 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) { /*--------------------------------------------------------------*/ /* O O */ /* || || */ /* case 21a:restored: R=S-R' =X original: R-S-R' -X(-) */ /* | || */ /* O(-) O(-) */ /* All O and X are taut O and X are not taut */ /* it is possible that X is R */ /* */ /* O = O, S, Se; S=S, Se, Te; X = N, O, S, Se, Te */ /* Solution: move (-) from O(-) to =X */ /* these atoms are tautomeric in restored structure */ /*--------------------------------------------------------------*/ int iat, iS; /* AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; */ inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ EDGE_LIST OtherSO, CentralS, SOMinus, MinusAcceptord; CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &OtherSO, EDGE_LIST_CLEAR ); AllocEdgeList( &CentralS, EDGE_LIST_CLEAR ); AllocEdgeList( &SOMinus, EDGE_LIST_CLEAR ); AllocEdgeList( &MinusAcceptord, EDGE_LIST_CLEAR ); cur_success = 0; if ( !at_Mobile_H_Revrs ) { goto exit_case_21a; } for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -X(-) */ /*num_DB_O < MAX_DIFF_FIXH &&*/ /*pc2i->c2at[i].nValElectr == 6 */ /* O, S, Se, Te */ !pc2i->c2at[i].endptInChI && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ (pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ !at2[iat].num_H ) { if ( pVA[iat].cNumValenceElectrons == 6 && at2[iat].charge == -1 && pBNS->edge[e].flow && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 && pVA[iS=(int)at2[iat].neighbor[0]].cNumValenceElectrons == 6 && pVA[iS].cPeriodicRowNumber > 1 && at2[iS].valence >= 4 ) { /* a candidate for S in -SO2- */ int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0; int nNumOthers = 0, nNumNegEndp = 0, nNumEndpO = 0; /* check whether we have already found it */ if ( 0 <= FindInEdgeList( &CentralS, iS ) ) { continue; } for ( j = 0; j < at[iS].valence; j ++ ) { int bond_type = at2[iS].bond_type[j]; k = at2[iS].neighbor[j]; if ( k == iat ) { continue; } if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && !at2[k].endpoint ) { nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); nNumEndpO += (pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1); } else if ( bond_type == BOND_TYPE_DOUBLE ) { nNumOtherDB ++; } else if ( bond_type == BOND_TYPE_SINGLE ) { nNumOtherSB ++; } else { nNumOthers ++; } if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { nNumNegEndp ++; } } if ( !nNumEndpO ) { continue; } if ( nNumTautSB + nNumTautDB + nNumOtherDB <= nNumEndpO ) { continue; /* ignore */ } /* collect double bond taut =O */ for ( j = 0; j < at[iS].valence; j ++ ) { int bond_type = at2[iS].bond_type[j]; k = at2[iS].neighbor[j]; if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && !at2[k].endpoint && pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1 && 0 <= (e=pVA[k].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden ) { if ( bond_type == BOND_TYPE_DOUBLE && !at2[k].charge && !pBNS->edge[e].flow) { /* charges to be unchanged */ if ( ret = AddToEdgeList( &OtherSO, e, INC_ADD_EDGE ) ) { goto exit_case_21a; } } else if ( bond_type == BOND_TYPE_SINGLE && at2[k].charge == -1 && pBNS->edge[e].flow ) { /* charges to be removed */ if ( ret = AddToEdgeList( &SOMinus, e, INC_ADD_EDGE ) ) { goto exit_case_21a; } } } } if ( ret = AddToEdgeList( &CentralS, iS, INC_ADD_EDGE ) ) { goto exit_case_21a; } } else if ( at2[iat].charge == 0 && !pBNS->edge[e].flow && at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { /* changeable charges */ if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } /* remove unchangeable from changeable */ for ( i = 0; i < OtherSO.num_edges; i ++ ) { RemoveFromEdgeListByValue( &CurrEdges, OtherSO.pnEdges[i] ); } if ( num_try = inchi_min( SOMinus.num_edges, CurrEdges.num_edges ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < SOMinus.num_edges && cur_success < num_try; i ++ ) { pe = pBNS->edge + SOMinus.pnEdges[i]; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /*pe->forbidden |= forbidden_edge_mask;*/ pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-) charge to =O => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 21a */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } exit_case_21a: CurrEdges.num_edges = 0; /* clear current edge list */ AllocEdgeList( &OtherSO, EDGE_LIST_FREE ); AllocEdgeList( &CentralS, EDGE_LIST_FREE ); AllocEdgeList( &SOMinus, EDGE_LIST_FREE ); AllocEdgeList( &MinusAcceptord, EDGE_LIST_FREE ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at ) { /*------------------------------------------------------------------*/ /* case 22: restored: N(-)=N(+)=C...=O orig: N#N-N=...-O(-) */ /* im InChI -O(-) may have H(+) added by Normalization */ /* or may be tautomeric */ /* Solution: move (-) from N(-) to =O */ /* */ /*------------------------------------------------------------------*/ int num_DB_O = 0, iat; short iat_DB_O[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; int iN2, iC; BNS_EDGE *peDB_O_Minus; /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: =O */ num_DB_O < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { iat_DB_O[num_DB_O ++] = iat; } } for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ at2[iat].charge == -1 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && !pVA[iat].cMetal && pVA[iat].cNumValenceElectrons == 5 && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && pVA[iN2=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && at2[iat].bond_type[0] == BOND_TYPE_DOUBLE && at2[iN2].charge == 1 && at2[iN2].valence == 2 && at2[iN2].chem_bonds_valence == 4 && pVA[iC=at2[iN2].neighbor[at2[iN2].neighbor[0]==iN2]].cNumValenceElectrons == 4 ) { if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( num_try = inchi_min( CurrEdges.num_edges, num_DB_O ) ) { /* detected; attempt to fix */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); delta = 1; for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { iat = iat_DB_O[i]; peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); pe = pBNS->edge + pBNS->vert[iat].iedge[0]; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { /* Added (-) charge to =O and removed from =N(-) => nDeltaCharge == 0 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 22 */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } INCHI_HEAPCHK pe->forbidden &= forbidden_edge_mask_inv; peDB_O_Minus->forbidden |= forbidden_edge_mask; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { /*------------------------------------------------------------------*/ /* case 23: -NO2 are to be tautomeric but they are not AND */ /* InChI has a SINGLE tautomeric group */ /* */ /* (-)O (-)O */ /* Solution: convert \ \ */ /* N-X=...-Z(-) => N(+)=X- ...=Z */ /* // / */ /* O (-)O */ /* */ /* O O */ /* or \\ \\ */ /* N-X=...-Z(-) => N=X- ...=Z */ /* // / */ /* O (-)O */ /* */ /* */ /* (a) move (-) from other tautomeric atom to O in O=N-X */ /* or from other atom that has to be tautomeric */ /* but is not */ /* (b) create (+) [ion pair creation] on N as in */ /* */ /* OH OH */ /* / / */ /* -C=N => =C-N(+) */ /* \\ \\ */ /* O O */ /* */ /*------------------------------------------------------------------*/ int num_DB_O = 0, iat; short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; /* inp_ATOM *atfMobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; */ S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? pStruct->pOneINChI[1]->nNum_H : (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H : NULL; int iN, one_success; BNS_EDGE *peDB_O_Minus; int neigh, nNumO, nNumOthers; #define CHG_SET_NOOH 0 #define CHG_SET_WRONG_TAUT 1 #define CHG_SET_TAUT 2 #define CHG_LAST_SET 2 /* the last index in trying */ #define CHG_SET_O_FIXED 3 #define CHG_SET_NUM 4 EDGE_LIST ChangeableEdges[CHG_SET_NUM]; memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ num_DB_O < MAX_DIFF_FIXH && pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && (pc2i->c2at[i].endptInChI /*|| pc2i->c2at[i].nMobHInChI*/) && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ !(pc2i->c2at[i].endptRevrs /*|| pc2i->c2at[i].nMobHRevrs*/) && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && /* find whether it belongs to NO2 */ pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { /* find the second O */ nNumO = nNumOthers = 0; for ( k = 0; k < at2[iN].valence; k ++ ) { neigh = at2[iN].neighbor[k]; if ( neigh == iat ) { continue; } if ( pVA[neigh].cNumValenceElectrons == 6 && pStruct->endpoint[neigh] && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && at2[neigh].valence == 1 && at2[neigh].num_H == 0 && at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { nNumO ++; } else if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && at2[neigh].valence > 1 && at2[neigh].valence < at2[neigh].chem_bonds_valence ) { nNumOthers ++; } } if ( nNumO != 1 || nNumOthers != 1 ) { continue; } for ( k = 0; k < num_DB_O; k ++ ) { if ( iat_NO2[k] == iN ) { break; } } if ( k == num_DB_O ) { iat_NO2[num_DB_O] = iN; iat_DB_O[num_DB_O ++] = iat; } /* save the edge to avoid interference */ if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { goto exit_case_23; } } } if ( num_DB_O ) { /* 1. search for =N(=O)-OH; assume =N(+)(-O(-))(-OH) does not happen */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ /* find O=N(V) */ iat = nCanon2AtnoRevrs[i]; if ( !pStruct->endpoint[i] || pVA[i].cNumValenceElectrons != 6 || at2[iat].valence != 1 || at2[iat].charge || 0 > (e = pVA[iat].nCMinusGroupEdge-1) || at2[iat].num_H + at2[iat].chem_bonds_valence != 2 || pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons != 5 || 0 > (e = pVA[iN].nCPlusGroupEdge-1) || pBNS->edge[e].forbidden || !pBNS->edge[e].flow || at2[iN].charge || at2[iN].valence != 3 || at2[iN].chem_bonds_valence != 5) { continue; } /* find the second O, -OH */ nNumO = nNumOthers = 0; for ( k = 0; k < at2[iN].valence; k ++ ) { neigh = at2[iN].neighbor[k]; if ( neigh == iat ) { continue; } if ( pVA[neigh].cNumValenceElectrons == 6 && pStruct->endpoint[neigh] && at2[neigh].valence == 1 && at2[neigh].num_H == 1 && at2[neigh].radical == 0 && (at2[neigh].charge == 0 ) ) { nNumO ++; } else if ( at2[iN].bond_type[k] == BOND_TYPE_DOUBLE && at2[neigh].valence >= 2 && at2[neigh].valence < at2[neigh].chem_bonds_valence ) { nNumOthers ++; } } if ( nNumO != 1 || nNumOthers != 1 ) { continue; } /* save edges to be changed */ if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ))) { goto exit_case_23; } if ( NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && (( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], j, INC_ADD_EDGE ) ) || ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ))) { goto exit_case_23; } } /* 2. search for (-) atoms that are tautomeric but should not be */ /* or that got H from Normalization but they shouldn't */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( at2[iat].charge == -1 && !pStruct->endpoint[i] && (at_Mobile_H_Revrs && (at_Mobile_H_Revrs[i].endpoint || at2[iat].num_H < at_Mobile_H_Revrs[i].num_H )) ) { if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && ( ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) ) ) { goto exit_case_23; } } else /* negatively charged atom in Reconstructed structure got H(+) from Normalization */ /* and is not tautomeric; in the original structure it is tautomeric */ if ( at2[iat].charge == -1 && pStruct->endpoint[i] && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && (num_Fixed_H_Revrs && num_Fixed_H_Revrs[i] == -1) && (pnMobHRevrs && pnMobHRevrs[i] == 1) && pStruct->fixed_H[i] == 0 ) { if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && ( ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) ) ) { goto exit_case_23; } } } /* 3. Search for (-) atoms that are tautomeric */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] && (at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && at2[iat].charge == -1 /*&& pVA[i].cNumValenceElectrons == 6*/ ) { if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_TAUT], e, INC_ADD_EDGE ) ) ) { goto exit_case_23; } } } /* ------- finally, try to move charges from O=N --------------*/ for ( i = 0; i < num_DB_O; i ++ ) { int nDeltaChargeExpected; one_success = 0; delta = 1; iat = iat_DB_O[i]; peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); pe = pBNS->edge + pBNS->vert[iat].iedge[0]; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { if ( !ChangeableEdges[k].num_edges ) { continue; } nDeltaChargeExpected = (k==CHG_SET_NOOH)? 2 : 0; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); /* allow (-) charge to move to N=O */ peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == nDeltaChargeExpected ) { /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; one_success ++; /* 23 */ } } INCHI_HEAPCHK } cur_success += one_success; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pe->forbidden &= forbidden_edge_mask_inv; if ( !one_success ) { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } } exit_case_23: for ( i = 0; i < CHG_SET_NUM; i ++ ) { AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } #undef CHG_SET_NOOH #undef CHG_SET_WRONG_TAUT #undef CHG_SET_TAUT #undef CHG_LAST_SET #undef CHG_SET_O_FIXED #undef CHG_SET_NUM } if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { /*------------------------------------------------------------------*/ /* case 24: InChI norm. -N(-)-N(+)(IV) => -N=N(V) prevents tauto- */ /* merism on -N(-)- in case of ADP */ /* */ /* Solution: convert N(V)=N- ...=X -> N(IV)(+)-N=...-X(-)*/ /* N(IV)(+)-N(-)-...=X */ /* */ /* Orig InChI taut taut, 1 t-group only(ADP?) */ /* Reconstructed struct non-taut possibly not taut */ /* */ /* Details: 1a. store next to N(V) (+)edge its flower edge */ /* 1b. store next to N(-) edge NO_VERTEX */ /* 2. Release (-) edges of other missing endpoints or */ /* all endpoints if no other is missing */ /* 3. Decrement flow on (+) edge */ /* if flower edge is stored then expect DeltaCharge=2*/ /* otherwise DeltaCharge = 0 */ /*------------------------------------------------------------------*/ int iat; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; int iN, one_success; EdgeIndex ef, e1; BNS_EDGE *pef; #define CHG_SET_MISSED_TAUT 0 #define CHG_SET_OTHER_TAUT_O 1 #define CHG_SET_OTHER_TAUT_N 2 #define CHG_LAST_SET 2 /* the last index in trying */ #define CHG_SET_NN 3 #define CHG_SET_AVOID 4 #define CHG_SET_NUM 5 EDGE_LIST ChangeableEdges[CHG_SET_NUM]; memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( /* orig. InChI info: -N=N(V) */ pc2i->c2at[i].nValElectr == 5 /* N or P */ && (pc2i->c2at[i].endptInChI /* only N */) && (e1=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e1].forbidden && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ !pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3 && /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ (iN = at2[iat].neighbor[at2[iat].bond_type[0] != BOND_TYPE_DOUBLE], pVA[iN].cNumValenceElectrons == 5) && at2[iN].chem_bonds_valence == 5 && at2[iN].charge == 0 && !at2[iN].num_H && !at2[iN].radical && 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e )) { ef = GetChargeFlowerUpperEdge( pBNS, pVA, e ); /* == NO_VERTEX if N(V) has 4 bonds */ if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], ef, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], ef, INC_ADD_EDGE ))) { goto exit_case_24; } /* mark -N= so that (-) will not be moved to it */ if ( 0 <= (e = pVA[iat].nCMinusGroupEdge) && !pBNS->edge[e].forbidden && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { goto exit_case_24; } } else if ( /* orig. InChI info: -N(-)N(IV)(+) */ atf && pc2i->c2at[i].nValElectr == 5 /* N or P */ && pc2i->c2at[i].endptInChI /* only N */ && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && /* reversed structure info: */ !pc2i->c2at[i].endptRevrs && pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 2 && atf[iat].valence == 2 && atf[iat].chem_bonds_valence == 3 && /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ (iN=atf[iat].neighbor[atf[iat].bond_type[0] != BOND_TYPE_DOUBLE], pVA[iN].cNumValenceElectrons == 5) && at2[iN].charge == 1 && /* double bond neighbor */ at2[iN].chem_bonds_valence == 4 && atf[iN].charge == 0 && atf[iN].chem_bonds_valence == 5 && /* InChI normalization created N(V)=N- out of N(IV)(+)-N(-)- */ !at2[iN].num_H && !at2[iN].radical && 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { /* save (-) edge */ if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], NO_VERTEX, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { goto exit_case_24; } } } if ( !ChangeableEdges[CHG_SET_NN].num_edges ) { goto exit_case_24; } /* Collect all relevant tautomeric atoms */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ if ( !pStruct->endpoint[i] ) { continue; } iat = nCanon2AtnoRevrs[i]; if ( at2[iat].charge || at2[iat].radical || at2[iat].valence == at2[iat].chem_bonds_valence ) { continue; /* cannot be an acceptor of (-) */ } if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { continue; } if ( 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { continue; /* has already been used */ } /* missing endpoint */ if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) ) { if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))) { goto exit_case_24; } } else /* endpoint O */ if ( pVA[iat].cNumValenceElectrons == 6 ) { if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_O], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ goto exit_case_24; } } else /* endpoint N */ if ( pVA[iat].cNumValenceElectrons == 5 ) { if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_N], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ goto exit_case_24; } } } /* ------- finally, try to move charges from -N(-)-N(+) or to N(V) --------------*/ for ( i = 0; i < ChangeableEdges[CHG_SET_NN].num_edges; i += 3 ) { int nDeltaChargeExpected; one_success = 0; delta = 1; pe = pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i]; pef = (NO_VERTEX != ChangeableEdges[CHG_SET_NN].pnEdges[i+1])? pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i+1] : NULL; nDeltaChargeExpected = ChangeableEdges[CHG_SET_NN].pnEdges[i+2]; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { if ( !ChangeableEdges[k].num_edges ) { continue; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); /* allow change of N(V) flower edge */ if ( pef ) { pef->forbidden &= forbidden_edge_mask_inv; } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == nDeltaChargeExpected ) { /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; one_success ++; /* 24 */ } } INCHI_HEAPCHK } cur_success += one_success; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); if ( !one_success ) { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } exit_case_24: for ( i = 0; i < CHG_SET_NUM; i ++ ) { AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } #undef CHG_SET_NN #undef CHG_SET_MISSED_TAUT #undef CHG_SET_OTHER_TAUT_O #undef CHG_SET_OTHER_TAUT_N #undef CHG_LAST_SET #undef CHG_SET_AVOID #undef CHG_SET_NUM } /* pStruct->nNumRemovedProtonsMobHInChI == pc2i->nNumRemHInChI */ if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 && pc2i->nNumRemHRevrs > pc2i->nNumRemHInChI && 0 > pc2i->nNumRemHInChI && (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) ) { /*------------------------------------------------------------------*/ /* case 25: Restored InChI does not have 2 or more added protons */ /* possibly taut. endpoints are missing */ /* has -N(-O(-))-O(-) group(s) */ /* Original InChI has only one t-group */ /* */ /* Solution: convert -N(-O(-))-O(-) -> -N(+)(=O)-O(-) */ /* and direct 2(-) to the missing taut atoms*/ /* at first attempt try to move (-) to N only */ /* */ /*------------------------------------------------------------------*/ int iat; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; /* inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; */ int iN, neigh, one_success; EdgeIndex e1, bFirst; BNS_EDGE *pef; #define CHG_SET_MISSED_TAUT_1 0 #define CHG_SET_MISSED_TAUT_ALL 1 #define CHG_SET_OTHER_TAUT_1 2 #define CHG_SET_OTHER_TAUT_ALL 3 #define CHG_LAST_SET 3 /* the last index in trying */ #define CHG_SET_NO_IN_NO2M2 4 #define CHG_SET_AVOID 5 #define CHG_SET_NUM 6 EDGE_LIST ChangeableEdges[CHG_SET_NUM]; memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; /* find all -N(-O(-))-O(-) */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; if ( pStruct->endpoint[i] ) { if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { continue; } bFirst = ( pVA[iat].cNumValenceElectrons == 5 && pc2i->nNumTgInChI == 1 || pVA[iat].cNumValenceElectrons == 6 && pc2i->nNumTgInChI != 1 ); /* many or no t-groups -> try O only first */ /* single t-group -> try only N first */ if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) ) { /* missed tautomeric endpoint */ if ( bFirst && (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_1], e, INC_ADD_EDGE ))) { goto exit_case_25; } if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_ALL], e, INC_ADD_EDGE )) { goto exit_case_25; } } if ( bFirst && (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_1], e, INC_ADD_EDGE ))) { goto exit_case_25; } if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_ALL], e, INC_ADD_EDGE )) { goto exit_case_25; } if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) { goto exit_case_25; } } else if ( at2[iat].valence == 1 && at2[iat].charge == -1 && pVA[iat].cNumValenceElectrons == 6 && pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && /* -O(-) */ !pStruct->endpoint[nAtno2CanonRevrs[iN]] && at2[iN].valence == 3 && at2[iN].chem_bonds_valence == 3 && !at2[iN].charge && !at2[iN].radical && 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && /* NPlus edge */ 0 <= (e1 = pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].forbidden && pBNS->edge[e1].flow && /* OMinus edge */ 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1 )) { /* found >N-O(-) */ int nNumO = 0, nNumOthers = 0; for ( k = 0; k < at2[iN].valence; k ++ ) { neigh = at2[iN].neighbor[k]; if ( neigh == iat ) { continue; } if ( pVA[neigh].cNumValenceElectrons == 6 && !pStruct->endpoint[neigh] && at2[neigh].valence == 1 && at2[neigh].num_H == 0 && at2[neigh].radical == 0 && at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 1 ) { nNumO ++; } else if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && at2[neigh].valence > 1 && at2[neigh].valence < at2[neigh].chem_bonds_valence ) { nNumOthers ++; } } if ( nNumO != 1 && nNumOthers != 1 ) { continue; } /* save charge edges: NPlus first, OMinus second */ if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e1, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE ))) { goto exit_case_25; } } } if ( !ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges || !ChangeableEdges[CHG_SET_OTHER_TAUT_ALL].num_edges ) { goto exit_case_25; } /* ------- finally, try to move charges from -NO2(2-) or to tautomeric endpoints ----*/ for ( i = 0; i < ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges; i += 2 ) { int nDeltaChargeExpected = 3; /* change flow on O(-) to make it neutral; 3 new charges will be created: N(+), and two (-) on InChI endpoints alternatively, if we change flow on N to make N(+) then O(-) will be nutralized (-1 charge) and two (-) charges on taut. endpoints will be created (+2); the total change in this case would be (-1)+(+2) = +1 */ one_success = 0; delta = 1; pe = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i+1]; /* O(-) edge */ pef = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i]; /* >N- (+) edge */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { if ( !ChangeableEdges[k].num_edges ) { continue; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); /* allow change of N(V) flower edge */ pef->forbidden &= forbidden_edge_mask_inv; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == nDeltaChargeExpected ) { /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; one_success ++; /* 24 */ } } INCHI_HEAPCHK } cur_success += one_success; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); if ( !one_success ) { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } exit_case_25: for ( i = 0; i < CHG_SET_NUM; i ++ ) { AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } #undef CHG_SET_NN #undef CHG_SET_MISSED_TAUT #undef CHG_SET_OTHER_TAUT_O #undef CHG_SET_OTHER_TAUT_N #undef CHG_LAST_SET #undef CHG_SET_AVOID #undef CHG_SET_NUM } exit_function: AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &SFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); AllocEdgeList( &AllBondEdges, EDGE_LIST_FREE ); return ret < 0? ret : (pc2i->bHasDifference && tot_succes); } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr4.c000066400000000000000000004446671271037650300234740ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*^^^ */ /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" /*^^^ */ /********************** Forbid carbon charge edges ***********************************/ int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ) { #define MAX_NUM_CARBON_CHARGE_EDGES 2 int nType, i, k, ret; BNS_EDGE *pEdge; if ( ret = AllocEdgeList( pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES ) ) { goto exit_function; } pCarbonChargeEdges->num_edges = 0; for ( i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i ++ ) { switch( i ) { case 0: nType = TCG_Plus_C0; break; case 1: nType = TCG_Minus_C0; break; default: ret = RI_ERR_PROGR; goto exit_function; } if ( (k = pTCGroups->nGroup[nType]) >= 0 ) { k = pTCGroups->pTCG[k].nForwardEdge; if ( k > 0 ) { pEdge = pBNS->edge + k; if ( !(pEdge->forbidden & forbidden_edge_mask) ) { pEdge->forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( pCarbonChargeEdges, k, 0 ) ) { goto exit_function; } } } else { ret = RI_ERR_PROGR; goto exit_function; } } } ret = pCarbonChargeEdges->num_edges; exit_function: return ret; #undef MAX_NUM_CARBON_CHARGE_EDGES } /******************************************************************************************************/ int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ) { int i, j, ret; BNS_EDGE *e; ret = 0; /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */ for ( i = 0; i < num_at; i ++ ) { if ( at[i].valence == 2 && !at[i].num_H && !at[i].endpoint && pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 && pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MNP && pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size ) { e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1); if ( !(e->forbidden & forbidden_edge_mask) ) { e->forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( pNplus2BondsEdges, j, 128 ) ) { goto exit_function; } } } } ret = 0; exit_function: return ret; } /************************************************************************************************* Problem: Formula in InChI from the reversed structure has less H than in the input InChI Solutions: (a) | | -B(-)-NH-=..-=N(+)< => -B(-)-NH(+)=-..=-N< (H is not removed from the ion pair) | | | | (b) >N(+)=-=...-=N-NH => >N-=-...=-N(+)-NH (charge from onium cannot be moved to remove H+) | | *************************************************************************************************/ int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) { int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh; EDGE_LIST NewlyFixedEdges; int ret, i, j; int num_at = pStruct->num_atoms; int inv_forbidden_edge_mask = ~forbidden_edge_mask; /* for RunBnsTestOnce */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { goto exit_function; } for ( i = 0; i < num_at; i ++ ) { if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } } /* extra H has been removed; check non-tautomeric atoms */ for ( i = 0; i < num_at; i ++ ) { if ( !at2[i].endpoint && !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && at2[i].num_H == atf[i].num_H + 1) { /* H was removed from N */ iNH = i; break; } } if ( 0 <= iNH && iNH < num_at ) { /* check neighbors for | | (a) -B(+)- or (b) =N- | | */ for ( j = 0; j < at2[i].valence; j ++ ) { neigh = at2[iNH].neighbor[j]; if ( at2[neigh].valence == 4 ) { if ( at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 && !at2[neigh].radical && !at[neigh].num_H ) { iBPlus = neigh; } } } } if ( 0 <= iNH && iNH < num_at ) { int bond_type_at2; int bond_type_atf; int num_bonds_in_path = 0; int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus; /* the changed bond to the dehydrogenated atom H should have greater order */ /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */ nxt_is_NPlus = 0; do { i = nxt; nxt = NO_VERTEX; delta = -delta; for ( j = 0; j < at2[i].valence; j ++ ) { bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */ bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */ nxt_is_NPlus = 0; if ( (bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) && BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE && !at2[(int)at2[i].neighbor[j]].cFlags ) { prv = i; nxt = at2[i].neighbor[j]; nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 && pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1; at2[i].cFlags |= 1; /* avoid cycling */ num_bonds_in_path ++; if ( delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 && !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 && pVA[prv].nCPlusGroupEdge > 0 ) { iNV = prv; } if ( at2[nxt].charge != atf[nxt].charge ) { if ( (at2[nxt].charge == 1 || atf[nxt].charge == 1) && pVA[nxt].nCPlusGroupEdge > 0 ) { pBNS->edge[pVA[nxt].nCPlusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; } if ( (at2[nxt].charge == -1 || atf[nxt].charge == -1) && pVA[nxt].nCMinusGroupEdge > 0 ) { pBNS->edge[pVA[nxt].nCMinusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; } } break; /* found */ } } } while ( nxt >= 0 && !( nxt_is_NPlus && delta == -1 ) ); for ( i = 0; i < num_at; i ++ ) { at2[i].cFlags = 0; } if ( nxt >= 0 && nxt_is_NPlus && delta == -1 ) { /* a simple alt path from NH-= to =N(+) has been found */ if ( iBPlus || iNV ) { /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */ if ( iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 || iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ) { int ieFlower; BNS_EDGE *pe = pBNS->edge + j, *peFlower = NULL; Vertex v1 = pe->neighbor1; Vertex v2 = v1 ^ pe->neighbor12; BNS_VERTEX *pv1 = pBNS->vert + v1; BNS_VERTEX *pv2 = pBNS->vert + v2; delta = 1; /* prevent conversion of >N(+)= into N(V) neutral */ ieFlower = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[nxt].nCPlusGroupEdge-1 ); if ( ieFlower >= 0 ) { peFlower = pBNS->edge + ieFlower; if ( peFlower->flow == delta ) { peFlower->forbidden |= forbidden_edge_mask; if ( ret = AddToEdgeList( &NewlyFixedEdges, ieFlower, 0 )) { goto exit_function; } } } pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 /* charge moving to this atom disappers*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { ret = 0; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } } } } exit_function: /* remove bond fixation */ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); return ret; } /*********************************************************************************************** X=Y-O(-) => X(-)-Y=O ************************************************************************************************/ int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) { int iNH = NO_VERTEX, neigh, neigh2; EDGE_LIST NewlyFixedEdges; int ret, i, j, k, k2, delta; int num_at = pStruct->num_atoms; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex v1, v2; /* for RunBnsTestOnce */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pe, *pe2; AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { goto exit_function; } /* fix all charges */ for ( i = 0; i < num_at; i ++ ) { if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } } /* H(+) has been added to -O(-); check non-tautomeric atoms */ for ( i = 0; i < num_at; i ++ ) { if ( !(pStruct->bMobileH? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal && at2[i].num_H + 1 == atf[i].num_H && /* normalization added H ??? What would happen in Fixed-H case?*/ (k = pVA[i].nCMinusGroupEdge-1) >= 0 && pBNS->edge[k].flow == 1 && /* atom had (-) charge before preprocessing */ at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */ at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */ pVA[i].cNumValenceElectrons == 6 && /* atom is O, S, Se, Te */ at2[neigh=at2[i].neighbor[0]].chem_bonds_valence > at2[neigh].valence /* atom's single neighbor has multiple bond(s)*/ ) { /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */ iNH = i; for ( j = 0; j < at2[neigh].valence; j ++ ) { neigh2 = at2[neigh].neighbor[j]; if ( neigh2 != iNH && !at2[neigh2].endpoint && !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden && 4 <= pVA[neigh2].cNumValenceElectrons && pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */ (k2 = pVA[neigh2].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */ ) { break; } } if ( j < at2[neigh].valence ) { delta = 1; pe = pBNS->edge + k; /* -O(-) negative charge edge; flow = 1 */ pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */ v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow -= delta; pBNS->vert[v1].st_edge.flow -= delta; pBNS->vert[v2].st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 1 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; } break; } else { /* the attempt has failed; restore the flow */ ret = 0; pe->flow += delta; pBNS->vert[v1].st_edge.flow += delta; pBNS->vert[v2].st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } } } exit_function: /* remove bond fixation */ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); return ret; } #if ( FIX_ADD_PROTON_FOR_ADP == 1 ) /******************************************************************************************************/ int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) { int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2; EDGE_LIST NewlyFixedEdges; int ret, i, j, k, k2, delta; int num_at = pStruct->num_atoms; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex v1, v2; /* for RunBnsTestOnce */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pe, *pe2; ret = 0; /* AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); for ( i = 0; i < num_at; i ++ ) { if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) { pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; ret = 1; break; } } */ return ret; } #endif /****************************************************************************************************** OH OH / / -NH => -NH(+) to eliminate false tautomerism. S(IV) or N(V) or P(V) may be a centerpoint \\ \ O O(-) *******************************************************************************************************/ int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) { EDGE_LIST NewlyFixedEdges; int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2; int num_at = pStruct->num_atoms; int inv_forbidden_edge_mask = ~forbidden_edge_mask; Vertex v1, v2; /* for RunBnsTestOnce */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; BNS_EDGE *pe, *pe2; ret = 0; AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { goto exit_function; } /* fix all charges */ for ( i = 0; i < num_at; i ++ ) { if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { goto exit_function; } pBNS->edge[j].forbidden |= forbidden_edge_mask; } } for ( i = 0; i < picr->num_endp_in1_only; i ++ ) { endpoint1 = picr->endp_in1_only[i]-1; if ( at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence || pVA[endpoint1].nCMinusGroupEdge <= 0 ) { continue; } /* find centerpoint */ for ( j = 0; j < at2[endpoint1].valence; j ++ ) { if ( BOND_TYPE_DOUBLE == ( BOND_TYPE_MASK & at2[endpoint1].bond_type[j] ) ) { centerpoint = at2[endpoint1].neighbor[j]; if ( at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 || !is_centerpoint_elem( at2[centerpoint].el_number ) ) { continue; } /* -- the centerpoint as depicted has no ChargeStruct flower --- m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 ); if ( m < 0 || pBNS->edge[m].flow ) { continue; } */ /* find 2nd endpoint */ for ( k = 0; k < at2[centerpoint].valence; k ++ ) { if ( BOND_TYPE_SINGLE != ( BOND_TYPE_MASK & at2[centerpoint].bond_type[k] ) ) { continue; } endpoint2 = at2[centerpoint].neighbor[k]; if ( !at2[endpoint2].endpoint && atn[endpoint2].endpoint ) { break; } } if ( k == at2[centerpoint].valence ) { continue; } /* the centerpoint and two extra endpoints have been found */ pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1; if ( !pe->flow ) { continue; } pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1; if ( pe2->flow ) { continue; } delta = 1; v1 = pe->neighbor1; v2 = pe->neighbor12 ^ v1; pe->flow -= delta; pBNS->vert[v1].st_edge.flow -= delta; pBNS->vert[v2].st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 1 ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret ) { *pnTotalDelta += ret; } else { ret = RI_ERR_PROGR; } goto exit_function; } else { ret = 0; pe->flow += delta; pBNS->vert[v1].st_edge.flow += delta; pBNS->vert[v2].st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; pe2->forbidden |= forbidden_edge_mask; } } } } exit_function: /* remove bond fixation */ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); return ret; } /*************************************************************************/ int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ) { int i, j, k, len, ret = 0; AT_NUMB *pNum; for ( i = 0; i < TAUT_NUM; i ++ ) { if ( pStruct->pOneINChI_Aux[i] ) { pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd && pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0])? pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd: (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd && pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0])? pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL; } else { pNum = NULL; } if ( pNum ) { len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]); if ( !pStruct->nCanon2Atno[i] && !(pStruct->nCanon2Atno[i] = (AT_NUMB *) inchi_malloc( len )) || !pStruct->nAtno2Canon[i] && !(pStruct->nAtno2Canon[i] = (AT_NUMB *) inchi_malloc( len ))) { ret = RI_ERR_ALLOC; goto exit_function; } INCHI_HEAPCHK memcpy( pStruct->nCanon2Atno[i], pNum, len ); /* ??? the next for(...) fills it out */ INCHI_HEAPCHK for ( j = 0; j < pStruct->num_atoms; j ++ ) { k = pNum[j]-1; /* atom number */ pStruct->nCanon2Atno[i][j] = (AT_NUMB)k; pStruct->nAtno2Canon[i][k] = (AT_NUMB)j; INCHI_HEAPCHK } } else if ( !i ) { ret = RI_ERR_PROGR; goto exit_function; } else { if ( pStruct->nCanon2Atno[i] ) { inchi_free( pStruct->nCanon2Atno[i] ); pStruct->nCanon2Atno[i] = NULL; } INCHI_HEAPCHK if ( pStruct->nAtno2Canon[i] ) { inchi_free( pStruct->nAtno2Canon[i] ); pStruct->nAtno2Canon[i] = NULL; } INCHI_HEAPCHK } } exit_function: return ret; } /*************************************************************************/ int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ) { int ret = 0; /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/ if ( NULL == pStruct->endpoint ) { pStruct->endpoint = (AT_NUMB *)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0])); } else { memset( pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0] ) ); } if ( NULL == pStruct->fixed_H ) { pStruct->fixed_H = (S_CHAR *) inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0])); } if ( !pStruct->endpoint || !pStruct->fixed_H ) { ret = RI_ERR_ALLOC; goto exit_function; } /*--- fill out Mobile/Fixed-H data from the input InChI ---*/ GetTgroupInfoFromInChI( &pStruct->ti, NULL, pStruct->endpoint, pInChI[1] ); if ( pInChI[0]->nNum_H_fixed ) { memcpy( pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); } else { memset( pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); } exit_function: return ret; } /***********************************************************************************************/ int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ) { int ret = 0, i, j; int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? pStruct->pOneINChI[1]->nNum_H : (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H : NULL; int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; memset( pc2i, 0, sizeof(*pc2i) ); pc2i->nNumTgInChI = pStruct->ti.num_t_groups; pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs; pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; pc2i->bHasDifference |= !bFixHRevrsExists; for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; nNumTgMInChI = pStruct->ti.t_group[i].num[1]; nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; if ( pStruct->ti.t_group[i].nNumEndpoints == pStruct->One_ti.t_group[i].nNumEndpoints ) { if ( nNumTgHInChI != nNumTgHRevrs ) { pc2i->nNumTgDiffH ++; } if ( nNumTgMInChI != nNumTgMRevrs ) { pc2i->nNumTgDiffMinus ++; } } pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != pStruct->One_ti.t_group[i].nNumEndpoints; pc2i->nNumTgHInChI += nNumTgHInChI; pc2i->nNumTgMInChI += nNumTgMInChI; pc2i->nNumTgHRevrs += nNumTgHRevrs; pc2i->nNumTgMRevrs += nNumTgMRevrs; } for ( ; i < pStruct->ti.num_t_groups; i ++ ) { nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; nNumTgMInChI = pStruct->ti.t_group[i].num[1]; pc2i->nNumTgHInChI += nNumTgHInChI; pc2i->nNumTgMInChI += nNumTgMInChI; pc2i->bHasDifference |= 1; } for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; pc2i->nNumTgHRevrs += nNumTgHRevrs; pc2i->nNumTgMRevrs += nNumTgMRevrs; pc2i->bHasDifference |= 1; } for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { /* i = original InChI canonical number - 1 */ /* k = atom number from InChI created out of restored Fixed-H structure */ int iCanonRevrs = nAtno2CanonRevrs[i]; int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */ int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; int nFixHInChI = pStruct->fixed_H[i]; int nFixHRevrs = num_Fixed_H_Revrs? num_Fixed_H_Revrs[iCanonRevrs]:0; int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs ) || (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { /* in InChI or reversed InChI atom[i] is not tautomeric */ /* and number of fixed-H on the atom[i] differs */ if ( j >= MAX_DIFF_FIXH ) { ret = RI_ERR_PROGR; goto exit_function; } pc2i->c2at[j].endptInChI = endptInChI; pc2i->c2at[j].endptRevrs = endptRevrs; pc2i->bHasDifference |= !endptInChI != !endptRevrs; pc2i->c2at[j].atomNumber = i; pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; pc2i->c2at[j].nFixHInChI = nFixHInChI; pc2i->c2at[j].nFixHRevrs = nFixHRevrs; pc2i->bHasDifference |= nFixHInChI != nFixHRevrs; pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); pc2i->bHasDifference |= nMobHInChI != nMobHRevrs; pc2i->c2at[j].nNumHRevrs = at2[i].num_H; pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; j ++; } pc2i->nNumEndpInChI += (endptInChI != 0); pc2i->nNumEndpRevrs += (endptRevrs != 0); if ( !pVA[i].cMetal ) { pc2i->nChargeFixHRevrsNonMetal += at2[i].charge; pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].charge : 0; } /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ } pc2i->nChargeFixHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; pc2i->nChargeMobHInChI = pInChI[1]? pInChI[1]->nTotalCharge : 0; pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1]? pStruct->pOneINChI[1]->nTotalCharge : pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs; pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; exit_function: pc2i->len_c2at = j; return ret; } /***********************************************************************************************/ int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ) { int ret = 0, i, j, iat; int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; S_CHAR *pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H : NULL; int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; memset( pc2i, 0, sizeof(*pc2i) ); pc2i->nNumTgInChI = pStruct->ti.num_t_groups; pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/ pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; /*pc2i->bHasDifference |= !bFixHRevrsExists;*/ for ( i = 0; i < pStruct->ti.num_t_groups; i ++ ) { int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos; int jNum = pStruct->ti.t_group[i].nNumEndpoints; int is_N, is_O; for ( j = 0; j < jNum; j ++ ) { iat = pStruct->ti.nEndpointAtomNumber[jFst + j]; is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; is_O = pVA[iat].cNumValenceElectrons == 6; if ( is_N + is_O != 1 ) { return RI_ERR_SYNTAX; } pc2i->nNumTgNInChI += is_N; pc2i->nNumTgOInChI += is_O; if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { /* donor */ if ( is_N ) { /* N */ pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2; pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1; } else { /* O, S, Se, Te */ pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; } } else if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { /* donor */ if ( is_N ) { /* N */ pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; } else { /* O, S, Se, Te */ pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; } } } } for ( i = 0; i < pStruct->One_ti.num_t_groups; i ++ ) { int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos; int jNum = pStruct->One_ti.t_group[i].nNumEndpoints; int is_N, is_O; for ( j = 0; j < jNum; j ++ ) { iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]]; is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; is_O = pVA[iat].cNumValenceElectrons == 6; if ( is_N + is_O != 1 ) { return RI_ERR_PROGR; } pc2i->nNumTgNRevrs += is_N; pc2i->nNumTgORevrs += is_O; if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { /* donor */ if ( is_N ) { /* N */ pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2; pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1; } else { /* O, S, Se, Te */ pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; } } else if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { /* donor */ if ( is_N ) { /* N */ pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; } else { /* O, S, Se, Te */ pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; } } } } for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; nNumTgMInChI = pStruct->ti.t_group[i].num[1]; nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; if ( pStruct->ti.t_group[i].nNumEndpoints == pStruct->One_ti.t_group[i].nNumEndpoints ) { if ( nNumTgHInChI != nNumTgHRevrs ) { pc2i->nNumTgDiffH ++; } if ( nNumTgMInChI != nNumTgMRevrs ) { pc2i->nNumTgDiffMinus ++; } } pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != pStruct->One_ti.t_group[i].nNumEndpoints; pc2i->nNumTgHInChI += nNumTgHInChI; pc2i->nNumTgMInChI += nNumTgMInChI; pc2i->nNumTgHRevrs += nNumTgHRevrs; pc2i->nNumTgMRevrs += nNumTgMRevrs; } for ( ; i < pStruct->ti.num_t_groups; i ++ ) { nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; nNumTgMInChI = pStruct->ti.t_group[i].num[1]; pc2i->nNumTgHInChI += nNumTgHInChI; pc2i->nNumTgMInChI += nNumTgMInChI; pc2i->bHasDifference |= 1; } for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; pc2i->nNumTgHRevrs += nNumTgHRevrs; pc2i->nNumTgMRevrs += nNumTgMRevrs; pc2i->bHasDifference |= 1; } for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { /* i = original InChI canonical number - 1 */ /* k = atom number from InChI created out of restored Fixed-H structure */ int iCanonRevrs = nAtno2CanonRevrs[i]; int endptInChI = at2[i].endpoint; /* endpoint in InChI */ int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; if ( (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { /* in InChI or reversed InChI atom[i] is not tautomeric */ /* and number of fixed-H on the atom[i] differs */ if ( j >= MAX_DIFF_FIXH ) { ret = RI_ERR_PROGR; goto exit_function; } pc2i->c2at[j].endptInChI = endptInChI; pc2i->c2at[j].endptRevrs = endptRevrs; pc2i->bHasDifference |= !endptInChI != !endptRevrs; pc2i->c2at[j].atomNumber = i; pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs); pc2i->c2at[j].nNumHRevrs = at2[i].num_H; pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; j ++; } pc2i->nNumEndpInChI += (endptInChI != 0); pc2i->nNumEndpRevrs += (endptRevrs != 0); if ( !pVA[i].cMetal ) { pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint)? at_Mobile_H_Revrs[i].charge : 0; } /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ } pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge; pc2i->nChargeMobHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; exit_function: pc2i->len_c2at = j; return ret; } /******************************************************************************************************/ int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) { int i; int err; ICR icr, icr2; int num_norm_endpoints, num_endpoints, num_norm_t_groups, num_mobile, num_norm_mobile, ret = 0; #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB const char *szCurHdr = (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue : "???"; int iComponent = pTCGroups->iComponent; #endif #endif T_GROUP_INFO *t_group_info = NULL; inp_ATOM *at_norm = NULL; /* normalized */ inp_ATOM *at_prep = NULL; /* preprocessed */ INCHI_MODE cmpInChI, cmpInChI2; int nDeltaPrev, nDeltaCur; int iOrigInChI, iRevrInChI; /***********************************************************/ /* normalize and create one component InChI */ /***********************************************************/ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep ); if ( ret < 0 ) { #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB fprintf( stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); #endif #endif goto exit_function; } if ( pStruct->bMobileH == TAUT_NON ) { /* these indexes are used to compare Mobile-H InChI */ iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; } else { iOrigInChI = 0; iRevrInChI = 0; } /************************************************************/ /* compare */ /************************************************************/ if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { goto exit_function; } cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } /********** InChI from restored structure has LESS hydrogen atoms ******************************/ if ( (cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1) ) { do { ret = FixLessHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); if ( ret < 0 ) { goto exit_function; } if ( ret ) { /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep ); if ( ret < 0 ) { #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB fprintf( stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp, szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); #endif #endif goto exit_function; } /* compare new InChI to the original InChI */ if ( pStruct->bMobileH == TAUT_NON ) { iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; } else { iRevrInChI = 0; } if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { goto exit_function; } cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); nDeltaPrev = nDeltaCur; nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1; } else { break; } } while( (cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); } /********** InChI from restored structure has MORE hydrogen atoms ******************************/ if ( (cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2) ) { do { ret = FixMoreHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); if ( ret < 0 ) { goto exit_function; } if ( ret ) { /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep ); if ( ret < 0 ) { #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB fprintf( stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp, szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); #endif #endif goto exit_function; } /* compare new InChI to the original InChI */ if ( pStruct->bMobileH == TAUT_NON ) { iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; } else { iRevrInChI = 0; } if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { goto exit_function; } cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); nDeltaPrev = nDeltaCur; nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2; } else { break; } } while( (cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); } /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/ if ( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only) ) { do { ret = FixRemoveExtraTautEndpoints( pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); if ( ret < 0 ) { goto exit_function; } if ( ret ) { /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep ); if ( ret < 0 ) { #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB fprintf( stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp, szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); #endif #endif goto exit_function; } /* compare new InChI to the original InChI */ if ( pStruct->bMobileH == TAUT_NON ) { iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; } else { iRevrInChI = 0; } if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { goto exit_function; } cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); nDeltaPrev = nDeltaCur; nDeltaCur = icr.num_endp_in1_only; } else { break; } } while( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev ); } /************************ case of Fixed-H ******************************************************/ if ( pStruct->bMobileH == TAUT_NON ) { int num_tries = 0; do { if ( 0 > (ret = FixFixedHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep, pInChI, num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, forbidden_stereo_edge_mask) ) ) { goto exit_function; } } while( num_tries ++ < 2 && ret > 0 ); } /************************ case of Fixed-H ******************************************************/ if ( pStruct->bMobileH == TAUT_YES ) { if ( 0 > (ret = FixMobileHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep, pInChI, num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, forbidden_stereo_edge_mask) ) ) { goto exit_function; } } /**********************************************************************************************/ /* stereo */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( &icr2, 0, sizeof(icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } ret = FixRestoredStructureStereo( cmpInChI, &icr, cmpInChI2, &icr2, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, &t_group_info, &at_norm, &at_prep, pInChI, num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, forbidden_stereo_edge_mask); if ( ret < 0 ) { goto exit_function; } #if ( FIX_ADD_PROTON_FOR_ADP == 1 ) /************************ check and fix ADP by adding a proton (dummy) *************************/ if ( cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow ) { ret = FixAddProtonForADP( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); if ( ret < 0 ) { goto exit_function; } } #endif /* moved to MakeOneInChIOutOfStrFromINChI(): pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; */ /* count endpoints */ num_endpoints = 0; num_norm_endpoints = 0; num_norm_t_groups = 0; num_mobile = 0; num_norm_mobile = 0; at_norm = pStruct->pOne_norm_data[0]->at; for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { num_endpoints += pTCGroups->pTCG[i].num_edges; num_mobile += pTCGroups->pTCG[i].tg_num_H + pTCGroups->pTCG[i].tg_num_Minus; } if ( t_group_info ) { /* after canonicalization, t_group_info->t_group[i].num[0] = number of H */ /* t_group_info->t_group[i].num[1] = number of (-) */ for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { if ( t_group_info->t_group[i].num[0] ) { num_norm_t_groups ++; num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints; num_norm_mobile += t_group_info->t_group[i].num[0]+t_group_info->t_group[i].num[1]; } } } #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB if ( num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints ) { /* need aggressive (de)protonation */ /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */ fprintf( stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n", (*ip).pSdfValue, (*pTCGroups).iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', pTCGroups->num_tgroups, num_norm_t_groups, num_endpoints, num_norm_endpoints ); } #endif #endif exit_function: for( i = 0; i < TAUT_NUM; i ++ ) { Free_INChI( &pStruct->pOneINChI[i] ); Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); FreeInpAtomData( pStruct->pOne_norm_data[i] ); if ( pStruct->pOne_norm_data[i] ) { inchi_free( pStruct->pOne_norm_data[i] ); pStruct->pOne_norm_data[i] = NULL; } } free_t_group_info( &pStruct->One_ti ); return ret; } /******************************************************************************************************/ /* Find A=X< where all bonds to X except A=X are marked as stereogenic; temporary allow stereobonds */ /* change and make A=X bonds single */ int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK; int inv_forbidden_edge_stereo = ~forbidden_edge_stereo; int i, k, ne, j1, j2, num_wrong, num_fixed; int ret2, retBNS, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges; BNS_EDGE *pEdge; Vertex v1, v2; BNS_VERTEX *pv1, *pv2; ret = 0; /* to simplify, prepare new at[] from pBNS */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { return ret; } num_wrong = 0; /* find wrong double bonds */ for ( i = 0; i < num_at; i ++ ) { if ( at2[i].valence == 3 && at2[i].chem_bonds_valence - at2[i].valence == 1 && at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && j1 != j2 ) { num_wrong ++; } } if ( !num_wrong ) { return 0; } num_fixed = 0; for ( i = 0; i < pBNS->num_bonds; i ++ ) { pEdge = pBNS->edge + i; if ( pEdge->forbidden & forbidden_edge_stereo ) { num_fixed ++; } } /* there may be no fixed stereo bonds at all, see #87607 */ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR ); /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { goto exit_function; } if ( (ret = AllocEdgeList( &FixedEdges, num_fixed )) || (ret = AllocEdgeList( &WrongEdges, num_wrong )) ) { goto exit_function; } /* collect wrong double bonds and set flow=0 */ for ( i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i ++ ) { if ( at2[i].valence == 3 && at2[i].chem_bonds_valence - at2[i].valence == 1 && at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && j1 != j2 ) { switch ( j1 + j2 ) { case 1: /* 0, 1 */ k = 2; break; case 2: /* 0, 2 */ k = 1; break; case 3: /* 1, 2 */ k = 0; break; default: ret = RI_ERR_PROGR; goto exit_function; } ne = pBNS->vert[i].iedge[k]; pEdge = pBNS->edge + ne; v1 = pEdge->neighbor1; v2 = pEdge->neighbor12 ^ v1; pv1 = pBNS->vert + v1; pv2 = pBNS->vert + v2; if ( !pEdge->flow ) { ret = RI_ERR_PROGR; goto exit_function; } pEdge->flow --; pEdge->forbidden |= forbidden_edge_mask; pv1->st_edge.flow --; pv2->st_edge.flow --; pBNS->tot_st_flow -= 2; if ( ret = AddToEdgeList( &WrongEdges, ne, 0 )) { goto exit_function; } } } /* remove forbidden mark from stereo bonds (unfix stereo bonds) */ for ( i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i ++ ) { pEdge = pBNS->edge + i; if ( pEdge->forbidden & forbidden_edge_stereo ) { pEdge->forbidden &= inv_forbidden_edge_stereo; FixedEdges.pnEdges[FixedEdges.num_edges ++] = i; } } /* Run BNS to move charges and rearrange bond orders */ retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( retBNS < 0 ) { goto exit_function; } else if ( retBNS > 0 ) { *pnTotalDelta += retBNS; } /* remove forbidden_edge_mask and set forbidden_edge_stereo */ RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask ); /* allow carbon charges to change */ RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); /* fix previously unfixed stereo bonds */ SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo ); /* Run BNS again in case not all edge flows are maximal */ ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret2 < 0 ) { goto exit_function; } else if ( ret2 > 0 ) { *pnTotalDelta += retBNS; } ret = retBNS; exit_function: AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &FixedEdges, EDGE_LIST_FREE ); AllocEdgeList( &WrongEdges, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i, j, neigh, num_endpoints, num_success; int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh; int ret2, ret_forbid_edges, ret, delta; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST; int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups); S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL]; int numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh, numOtherDoubleBondOAcceptors=0; EDGE_LIST ChargeListAllExcept_DB_O; BNS_EDGE *pEdgeMinus, *pe; Vertex v1m, v2m; BNS_VERTEX *pv1m, *pv2m; ret = 0; num_success = 0; /* count O(+)H, N(+)H */ /* if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) { goto exit_function; } */ if ( ret = AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR ) ) { goto exit_function; } /* to simplify, prepare new at[] from pBNS */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } #if ( FIND_RING_SYSTEMS == 1 ) ret2 = MarkRingSystemsInp( at2, num_at, 0 ); if ( ret2 < 0 ) { ret = ret2; goto exit_function; } #endif /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */ ret_forbid_edges = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_test ); if ( ret_forbid_edges < 0 ) { ret = ret_forbid_edges; goto exit_function; } for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ !(pVA[i].nTautGroupEdge || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i] ) && !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 && at2[i].valence < at2[i].chem_bonds_valence && is_centerpoint_elem( at2[i].el_number ) ) { is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal)); /* look at the neighbors */ numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0; num_donors_O = num_acceptors_O = 0; num_known_endpoints = num_wrong_neigh = 0; for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { neigh = at2[i].neighbor[j]; if ( (at2[neigh].endpoint || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh]) || at2[neigh].charge > 0 ) { num_known_endpoints ++; continue; } if ( pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test ) { continue; } bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK; if ( bond_type > BOND_TYPE_DOUBLE ) { num_wrong_neigh ++; continue; } if ( at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE ) { break; /* not this case */ } if ( at2[neigh].chem_bonds_valence - at2[neigh].charge != get_endpoint_valence( at2[neigh].el_number ) ) { if ( bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6 ) { DoubleBondNotONeigh[numDoubleBondNotONeigh ++] = j; } continue; } if ( at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE && (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge-1].flow != 1) ) { break; } switch( bond_type ) { case BOND_TYPE_SINGLE: if ( at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0 ) { num_wrong_neigh ++; continue; } num_donors ++; num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); MobileChargeNeigh[numMobileChargeNeigh ++] = j; break; case BOND_TYPE_DOUBLE: if ( at2[neigh].charge ) { num_wrong_neigh ++; continue; } DoubleBondAcceptors[numDoubleBondAcceptors ++] = j; num_acceptors ++; num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); } } if ( j != at2[i].valence || !num_donors || !num_acceptors ) { continue; } /* special case NOn(-) */ if ( is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O) ) { continue; } if ( pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh ) { /* fix all charges except on =O */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; int k, e, num_MovedCharges = 0; if ( !ChargeListAllExcept_DB_O.num_edges ) { numOtherDoubleBondOAcceptors = 0; for ( k = 0; k < num_at; k ++ ) { if ( 1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow && !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden && !((e=pVA[k].nCMinusGroupEdge-1) >= 0 && pBNS->edge[e].flow) && !((e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].flow) && /* 0 == at2[k].charge && */ pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal && pStruct->endpoint && pStruct->endpoint[k] || pStruct->fixed_H && pStruct->fixed_H[k] ) { numOtherDoubleBondOAcceptors ++; /* do not fix this minus edge */ } else if ( (e=pVA[k].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].flow && !pBNS->edge[e].forbidden && ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { goto exit_function; } if ( (e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { goto exit_function; } } } /* fix double bonds to non-O neighbors connected by double bonds; we will try to make these bons single */ for ( k = 0; k < numDoubleBondNotONeigh; k ++ ) { e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; if ( !pBNS->edge[e].forbidden && (ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 ))) { goto exit_function; } } /* attempt to make DoubleBondNotONeigh[] single */ SetForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); for ( k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k ++ ) { pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; delta = 1; if ( pe->flow != delta ) continue; pv1m = pBNS->vert + (v1m = pe->neighbor1); pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m); pv1m->st_edge.flow -= delta; pv2m->st_edge.flow -= delta; pe->flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret < 0 ) { goto exit_function; } if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || vPathEnd == v2m && vPathStart == v1m) && nDeltaCharge == 0 /* (-) moving from one to another atom*/ ) { ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == 1 ) { *pnTotalDelta += ret; num_MovedCharges ++; } else { ret = RI_ERR_PROGR; goto exit_function; } } else { ret = 0; pv1m->st_edge.flow += delta; pv2m->st_edge.flow += delta; pe->flow += delta; pBNS->tot_st_flow += 2*delta; } } RemoveForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); } else if ( !bPossiblyIgnore || !num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O) >=3 ) { /* remove negative charges from the neighbors */ pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */ pBNS->tot_st_cap += num_donors; pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */ for ( j = 0; j < numMobileChargeNeigh; j ++ ) { neigh = at2[i].neighbor[ (int)MobileChargeNeigh[j] ]; pEdgeMinus = pBNS->edge + (pVA[neigh].nCMinusGroupEdge-1); v1m = pEdgeMinus->neighbor1; v2m = pEdgeMinus->neighbor12 ^ v1m; pv1m = pBNS->vert + v1m; pv2m = pBNS->vert + v2m; delta = pEdgeMinus->flow; pv1m->st_edge.flow -= delta; pv2m->st_edge.flow -= delta; if ( IS_BNS_VT_C_GR( pv1m->type ) ) { /* irreversible change to ChargeStruct */ pv1m->st_edge.cap -= delta; } else if ( IS_BNS_VT_C_GR( pv2m->type ) ) { /* irreversible change to ChargeStruct */ pv2m->st_edge.cap -= delta; } else { ret = RI_ERR_PROGR; goto exit_function; } pBNS->tot_st_cap -= delta; pBNS->tot_st_flow -= 2*delta; pEdgeMinus->flow -= delta; } ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else if ( ret == num_donors ) { *pnTotalDelta += ret; num_success ++; /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ } else { ret = RI_ERR_PROGR; goto exit_function; } } } } if ( ret_forbid_edges ) { /* remove the marks */ RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_test ); } ret = num_success; exit_function: AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_FREE ); return ret; } /******************************************************************************************************/ /* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i; int ret2, ret, pass; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; int j, k; int cur_num_edges; BNS_EDGE *e; Vertex v1, v2; EdgeIndex *pFixedEdges; int nNumEdgesToFix; ret = 0; /* to simplify, prepare new at[] from pBNS */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } pFixedEdges = NULL; nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */ cur_num_edges = 0; /* count cur_num_edges only when pass==1; at the end they must be equal */ for ( pass = 0; pass < 2; pass ++ ) { if ( pass ) { /* 2nd pass: allocate edge storage */ if ( !nNumEdgesToFix ) { break; /* nothing to do */ } pFixedEdges = (EdgeIndex *) inchi_malloc(nNumEdgesToFix * sizeof( pFixedEdges[0] ) ); if ( !pFixedEdges ) { ret = RI_ERR_ALLOC; goto exit_function; } } for ( i = 0; i < num_at; i ++ ) { int neigh; if ( pVA[i].cMetal ) { for ( j = 0; j < at2[i].valence; j ++ ) { neigh = at2[i].neighbor[j]; if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 ) { continue; /* ignore carbon */ } if ( at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge && !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0 ) { int cnBits = at2[neigh].charge > 0? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) : MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0); int atBits = cnList[pVA[neigh].cnListIndex-1].bits; for ( k = 0; k < MAX_NUM_CN_BITS-1; k ++, atBits >>= cn_bits_shift ) { /* ??? */ if ( (atBits & cnBits) == cnBits ) { break; } } if ( k == MAX_NUM_CN_BITS-1 ) { continue; } if ( pass == 0 ) { nNumEdgesToFix ++; } else { pFixedEdges[ cur_num_edges ++ ] = pBNS->vert[i].iedge[j]; } } } } } } /* restore the initial structures */ memcpy( at2, at, (num_at + num_deleted_H)*sizeof(at2[0])); if ( nNumEdgesToFix && pFixedEdges ) { if ( nNumEdgesToFix != cur_num_edges ) { ret = RI_ERR_PROGR; goto exit_function; } /* change edge flow, fix the edges, and run BNS */ for ( i = 0; i < nNumEdgesToFix; i ++ ) { e = pBNS->edge + pFixedEdges[i]; v1 = e->neighbor1; v2 = e->neighbor12 ^ v1; e->flow --; e->forbidden |= forbidden_edge_mask; pBNS->vert[v1].st_edge.flow --; pBNS->vert[v2].st_edge.flow --; pBNS->tot_st_flow -= 2; (*pnTotalDelta) -= 2; } /* Run BNS allowing to change any charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else { (*pnTotalDelta) += ret; } /* unfix the edges */ for ( i = 0; i < nNumEdgesToFix; i ++ ) { e = pBNS->edge + pFixedEdges[i]; e->forbidden &= inv_forbidden_edge_mask; } if ( ret < 2 * nNumEdgesToFix ) { /* not all fixes succeeded */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); (*pnNumRunBNS) ++; if ( ret < 0 ) { goto exit_function; } else { (*pnTotalDelta) += ret; } } } if ( pFixedEdges ) { inchi_free( pFixedEdges ); pFixedEdges = NULL; } exit_function: return ret; } /**************************************************************************/ /* In Reconnected structure change 'salt bonds' to 'coordination bonds */ /* for example, M-O-C= -> M(+)-O(-)-C= */ /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ /* However, in this release metal-organic compounds do not get much care */ int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) { int i; int ret2, ret, cur_success; int num_at = pStruct->num_atoms; int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms; int num_deleted_H = pStruct->num_deleted_H; int len_at = num_at + num_deleted_H; int inv_forbidden_edge_mask = ~forbidden_edge_mask; EDGE_LIST AllChargeEdges; int j, k, n; BNS_EDGE *pe, *pePlusMetal, *peMinusO; BNS_VERTEX *pv1, *pv2, *pvO, *pvM; Vertex v1, v2, vPlusMinus; EdgeIndex ie, iePlusMetal, ieMinusO; Vertex vPathStart, vPathEnd; int delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; ret = 0; cur_success = 0; AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); if ( pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder ) { goto exit_function; } /* to simplify, prepare new at[] from pBNS */ memcpy( at2, at, len_at*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; if ( ret2 < 0 ) { ret = ret2; goto exit_function; } for ( i = 0; i < num_at; i ++ ) { if ( bIsMetalSalt( at2, i ) ) { if ( !AllChargeEdges.num_edges ) { /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/ for ( j = 0; j < num_at; j ++ ) { /* all bonds */ for ( k = 0; k < at2[j].valence; k ++ ) { n = at2[j].neighbor[k]; if ( n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden && ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { goto exit_function; } } /* charge edges */ if ( (ie=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && (ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { goto exit_function; } if ( (ie=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { goto exit_function; } } /* taut group edges */ for ( j = 0; j < pTCGroups->num_tgroups; j ++ ) { pv1 = pBNS->vert + (v1=pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */ for ( k = 0; k < pv1->num_adj_edges; k ++ ) { /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */ /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ pv2 = pBNS->vert + (pe = pBNS->edge + (ie=pv1->iedge[k]))->neighbor1; if ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) { goto exit_function; } } } /*---------------------------------------------------------------*/ } /* replace all single bonds to neutral neighbors with zero-order bonds allow neighbor charge change to (-1) and metal atom charge increment +1 */ for ( k = 0; k < at2[i].valence; k ++ ) { n = at2[i].neighbor[k]; pe = pBNS->edge + pBNS->vert[i].iedge[k]; if ( at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE ) { continue; } iePlusMetal = pVA[i].nCPlusGroupEdge-1; ieMinusO = pVA[n].nCMinusGroupEdge-1; if ( pe->flow != 1 || pe->forbidden || iePlusMetal < 0 ) { continue; } pePlusMetal = pBNS->edge + iePlusMetal; if ( pePlusMetal->flow <= 0 ) { continue; /* to add (+) to metal this flow must be decremented */ } if ( ieMinusO >= 0 ) { /* usually does not happen */ peMinusO = pBNS->edge + ieMinusO; if ( peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden ) { continue; } /* decrement bond order to 0 */ delta = 1; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pePlusMetal->forbidden &= inv_forbidden_edge_mask; peMinusO->forbidden &= inv_forbidden_edge_mask; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge > 0*/ ) { /* (+)charge was just moved, no change in number of charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; /* 01 */ } } else { pe->flow += delta; /* roll back */ pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } else if ( NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 ) ) ) { /* manually add (-) charge to O and (+) charge to metal */ /* decrement bond order to 0 */ /*---------------------------------------------------------------------------*/ /* */ /* (+/-)* (+/-) Result: */ /* | || */ /* | || - Added (+) to M */ /* (+)super (+)super - Incremented bond M-O */ /* || | */ /* || => | To make this attachment H, */ /* (Y) (Y) increment */ /* | || pTCGroups->pTCG[itg].tg_num_H */ /* | || */ /* (+)metal (+)hetero Technical details: */ /* \\ \ increase capacities of */ /* M M(+) edges to (+/-) otherwise */ /* | || flow may not be able to */ /* -O* -O-O increase */ /* */ /* After that change M=O bond order from 2 to 0 */ /*---------------------------------------------------------------------------*/ int i1, j1, k1; delta = 1; pvO = pBNS->vert + n; pvM = pBNS->vert + i; /* Increment st_edge.cap on (+/-) vertex */ pBNS->vert[vPlusMinus].st_edge.cap += delta; /* Increment st_edge.cap on O */ pvO->st_edge.cap += delta; /* increment cap on M-O edge */ pe->cap += delta; /* total cap count */ pBNS->tot_st_cap += 2*delta; v1 = vPlusMinus; v2 = n; /* atom O */ /* increase capacities of edges to Y */ for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta; } } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pePlusMetal->forbidden &= inv_forbidden_edge_mask; pe->forbidden &= inv_forbidden_edge_mask; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); cur_success = 0; if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == 1*/ ) { /* Added (+)charge to -N< => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; /* 01 */ } } if ( cur_success ) { /* set bond M=O order = 0 */ if ( pe->flow != 2*delta ) { ret = RI_ERR_PROGR; goto exit_function; } /* reduce pe bond order by 2*delta */ pe->flow -= 2*delta; pvO->st_edge.cap -= 2*delta; pvO->st_edge.flow -= 2*delta; pvM->st_edge.flow -= 2*delta; pvM->st_edge.cap -= 2*delta; pBNS->tot_st_cap -= 3*delta; pBNS->tot_st_flow -= 4*delta; /* fix M-O bond order to zero */ pe->cap -= 2*delta; /* add fixed (-) charge to O */ pVA[n].cInitCharge -= delta; } else { /* failed */ pBNS->vert[vPlusMinus].st_edge.cap -= delta; pvO->st_edge.cap -= delta; /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ pBNS->tot_st_cap -= 2*delta; /* decrease capacities of edges to Y */ for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta; } } } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } } } exit_function: AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); return ret; } #if ( KEEP_METAL_EDGE_FLOW == 1 ) /******************************************************************************************************/ int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ) { int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0; BNS_VERTEX *pVert, *pNeigh; BNS_EDGE *pEdge; /* count carbon-metal edges */ if ( pTCGroups->num_metal_atoms ) { fill_ForbiddenEdgesMetalCarbon: for ( i = 0; i < num_at; i ++ ) { if ( pVA[i].cMetal && pVA[i].cNumBondsToMetal ) { pVert = pBNS->vert + i; for ( j = 0; j < pVert->num_adj_edges; j ++ ) { pEdge = pBNS->edge + pVert->iedge[j]; neigh = pEdge->neighbor12 ^ i; pNeigh = pBNS->vert + neigh; if ( !IS_BNS_VT_ATOM(pNeigh->type) ) continue; if ( at[neigh].endpoint ) continue; if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 && pNeigh->st_edge.cap >= at[neigh].valence+1 ) { if ( pass ) { if ( ret = AddToEdgeList( pMetalCarbonEdges, pVert->iedge[j], 0 ) ) { goto exit_function; } pEdge->forbidden |= forbidden_edge_mask; } else { nNumEdgeMetalCarbon ++; } } } } } if ( !pass && nNumEdgeMetalCarbon ) { if ( ret = AllocEdgeList( pMetalCarbonEdges, nNumEdgeMetalCarbon ) ) { goto exit_function; } pass ++; goto fill_ForbiddenEdgesMetalCarbon; } } exit_function: return ret; } #endif /******************************************************************************************************/ int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH ) { int nNumRunBNS = 0; EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges; int nTotalDelta = 0, ret = 0, tot_num_fixes; inp_ATOM *at = pStruct->at; inp_ATOM *at2 = NULL; /* restored structure */ inp_ATOM *at3 = NULL; /* structure for calculating one InChI */ int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; #ifdef _DEBUG int ret2; #endif #if ( KEEP_METAL_EDGE_FLOW == 1 ) BNS_VERTEX *pVert, *pNeigh; int j, neigh; #endif /* Edge lista initialization */ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_CLEAR ); if ( pStruct->iMobileH == TAUT_NON && ( ret = FillOutExtraFixedHDataInChI( pStruct, pInChI ) ) ) { goto exit_function; } if ( !at2 && !(at2 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at2[0]))) || !at3 && !(at3 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at3[0])))) { return RI_ERR_ALLOC; } if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { goto exit_function; } #if ( KEEP_METAL_EDGE_FLOW == 1 ) /* count edges of -C(IV)< carbons connected to metals */ if ( 0 > (ret = ForbidMetalCarbonEdges( pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { goto exit_function; } #endif if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 6, pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { goto exit_function; } /*********** Run BNS #1: no charge on carbons and =N= ***************/ if ( Nplus2BondsEdges.num_edges ) { /* Run BNS leaving carbon charges unchanged */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); nNumRunBNS ++; if ( ret < 0 ) { goto exit_function; } else { nTotalDelta += ret; } RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); } #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif /*************************** extend min ring size to 8 ****************************/ if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 8, pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { goto exit_function; } if ( Nplus2BondsEdges.num_edges ) { /* Run BNS leaving carbon charges unchanged */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); nNumRunBNS ++; if ( ret < 0 ) { goto exit_function; } else { nTotalDelta += ret; } RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); } #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif /*******************************************************************/ if ( CarbonChargeEdges.num_edges > 0 ) { /* Run BNS leaving carbon charges unchanged */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); nNumRunBNS ++; if ( ret < 0 ) { goto exit_function; } else { nTotalDelta += ret; } RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ); AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); } #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif /*******************************************************************/ if ( MetalCarbonEdges.num_edges > 0 ) { /* Run BNS leaving carbon charges unchanged */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); nNumRunBNS ++; if ( ret < 0 ) { goto exit_function; } else { nTotalDelta += ret; } RemoveForbiddenEdgeMask( pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ); AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); } /*******************************************************************/ /* Run BNS allowing to change any charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); nNumRunBNS ++; if ( ret < 0 ) { goto exit_function; } else { nTotalDelta += ret; } #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif #if ( BNS_RAD_SEARCH == 1 ) /******************************************************************/ /* move unfulfilled 'radicals' from ChargeStruct to atoms */ /* and set change charges of affected atoms to fit total charge */ ret = MoveRadToAtomsAddCharges( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); if ( ret < 0 ) { goto exit_function; } #endif /**************************************************************/ /**************************************************************/ /***** fix restore inconsistencies *****/ /**************************************************************/ /**************************************************************/ #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */ ret = RearrangePlusMinusEdgesFlow( pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* Increment zero order metal bonds to heteroatoms */ /*****************************************************************/ ret = IncrementZeroOrderBondsToHeteroat( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } #ifdef _DEBUG /* debug only */ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); pStruct->at = at2; ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at = at; #endif #if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) /*****************************************************************/ /* move charges from heteroatoms to metal atoms */ /*****************************************************************/ ret = MoveChargeFromHeteroatomsToMetals( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } #endif /*********************************************************************** NH2 NH2 \ \ C==S(+)- => C(+)-S- where NH2 are not tautomeric / / NH2 NH2 ************************************************************************/ ret = MovePlusFromS2DiaminoCarbon( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* Avoid charge separation on heteroatoms */ /*****************************************************************/ ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0); if ( ret < 0 ) { goto exit_function; } if ( ret ) { /*charge separation remains; allow changes of stereobonds in a ring and try again */ ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK); if ( ret < 0 ) { goto exit_function; } } /*****************************************************************/ /* convert N#N(+)-N= into N(-)=N(+)=N- */ /*****************************************************************/ ret = RestoreNNNgroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change) */ /*****************************************************************/ ret = FixMetal_Nminus_Ominus( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* convert N(-)=C= into N#C- - */ /*****************************************************************/ ret = RestoreCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* convert C(+)#N(+)- into C(-)#N(+)- */ /*****************************************************************/ ret = RestoreIsoCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* eliminate =N(V)= if possible */ /* | */ /*****************************************************************/ ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* | | */ /* convert -S- to =S= if possible */ /* | | */ /*****************************************************************/ ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* =N(+)=O =N-O(-) */ /* convert => if possible */ /* Metal(q) Metal(q+2) */ /*****************************************************************/ ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /*****************************************************************/ /* forbidden edges prevents required in InChI tautomerism */ /* incorrectly restored mobile H mix separate tautomeric groups */ /* because an edge may not become forbidden */ /* note: removes this 'forbidden_edge' bit from ALL edges */ /*****************************************************************/ ret = MoveMobileHToAvoidFixedBonds( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ tot_num_fixes = 0; if ( pStruct->iMobileH==TAUT_NON ) { ret = RemoveRadFromMobileHEndpointFixH( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); } else { ret = RemoveRadFromMobileHEndpoint( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); } if ( ret < 0 ) { goto exit_function; } tot_num_fixes += ret; /**************************************************************/ /* make bonds between a charged heteroatom and a metal single */ ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************/ /* move (+) charges to >N- and other centerpoints */ ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* Find A=X< where all bonds to X except A=X are marked as stereogenic */ /* make bonds A=X single */ ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* In Reconnected structure change 'salt bonds' to 'coordination bonds */ /* for example, M-O-C= -> M(+)-O(-)-C= */ /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ /* However, in this release metal-organic compounds do not get much care */ ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* Normalize the structure and compare t-groups and stereobonds */ ret = NormalizeAndCompare(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH, &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK); if ( ret < 0 ) { goto exit_function; } /**************************************************************************/ /* Create InChI out of the restored structure */ /*ret = nTotalDelta;*/ exit_function: pStruct->at = at; pStruct->at2 = at2; at2 = NULL; AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); if ( at2 ) { inchi_free( at2 ); } if ( at3 ) { inchi_free( at3 ); } return ret; } /******************************************************************************************************/ int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ) { int i, j, ret = 0, ret2; /*int nDelta, nTotalDelta;*/ VAL_AT *pVA = NULL; VAL_AT va1; int num_at = pStruct->num_atoms; inp_ATOM *at = pStruct->at; ALL_TC_GROUPS TCGroups; ALL_TC_GROUPS *pTCGroups = &TCGroups; int nAddEdges2eachAtom = 2, nAddVertices = 0; BFS_Q bfsq; /* BNS creation */ BN_STRUCT *pBNS = NULL; BN_DATA *pBD = NULL; int nNum_changed_bonds = 0; int bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals=0; int nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP; memset( pTCGroups, 0, sizeof(pTCGroups[0]) ); for ( i = 0; i < NUM_TCGROUP_TYPES; i ++ ) { pTCGroups->nGroup[i] = TCG_None; /* unassigned */ } pTCGroups->iComponent = iComponent; pTCGroups->iAtNoOffset = iAtNoOffset; if ( num_at == 1 ) { /* single atom -- no bonds to restore */ inp_ATOM *at2 = (inp_ATOM *) inchi_malloc(sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); inp_ATOM *at3 = (inp_ATOM *) inchi_malloc(sizeof(at3[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); pStruct->at2 = at2; at[0].charge = pInChI[0]->nTotalCharge; if ( at2 ) { memcpy( at2, at, sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); } if ( !at2 || !at3 ) { if ( at3 ) inchi_free( at3 ); return RI_ERR_ALLOC; } ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, pStruct->at2, at3, pTCGroups ); /* clean up */ for( i = 0; i < TAUT_NUM; i ++ ) { Free_INChI( &pStruct->pOneINChI[i] ); Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); FreeInpAtomData( pStruct->pOne_norm_data[i] ); if ( pStruct->pOne_norm_data[i] ) { inchi_free( pStruct->pOne_norm_data[i] ); pStruct->pOne_norm_data[i] = NULL; } } free_t_group_info( &pStruct->One_ti ); inchi_free( at3 ); return ret; } AllocBfsQueue( &bfsq, BFS_Q_CLEAR, 0 ); if ( !(pVA = (VAL_AT *) inchi_calloc( num_at, sizeof( pVA[0] ) ) ) ) { ret = RI_ERR_ALLOC; goto exit_function; } pStruct->pVA = pVA; memset( &va1, 0, sizeof(va1) ); pTCGroups->total_charge = pInChI[0]->nTotalCharge; if ( 0 > ( ret = AllocBfsQueue( &bfsq, num_at, 0 /* min ring size undefined */ ) ) ) { goto exit_function; } pStruct->pbfsq = &bfsq; if ( pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 && ( ret = FillOutpStructEndpointFromInChI( pInChI[1], &pStruct->endpoint )) ) { goto exit_function; } /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */ for ( i = 0; i < num_at; i ++ ) { pVA[i].cNumValenceElectrons = get_sp_element_type( at[i].el_number, &j ); pVA[i].cPeriodicRowNumber = j; pVA[i].cPeriodicNumber = at[i].el_number; pVA[i].cNumValenceElectrons --; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1) */ if ( is_el_a_metal( at[i].el_number ) ) { if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { /* treat metal as non-metal if it is stereogenic or has a stereobond */ pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); } else { pVA[i].cMetal = 1; } } if ( at[i].valence == 2 && !at[i].num_H ) { pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring( at, i, 0, bfsq.q, bfsq.nAtomLevel, bfsq.cSource, 99 /* max ring size */ ); } else { pVA[i].cMinRingSize = 0; } } /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */ repeat_for_new_metals: /* set valences for the first time; find ChargeValence structures for each atom */ for ( i = 0; i < num_at; i ++ ) { /* get additional fictitious atoms information */ pVA[i].cInitFreeValences = 0; ret = GetAtomRestoreInfo( at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint ); if ( ret < 0 ) { goto exit_function; } if ( ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal ) { if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { /* treat metal as non-metal if it is stereogenic or has a stereobond */ pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); } else { pVA[i].cMetal = 1; } if ( pVA[i].cMetal ) { bTreatMoreAtomsAsMetals ++; } } pTCGroups->charge_on_atoms += pVA[i].cInitCharge; } if ( bTreatMoreAtomsAsMetals && !bSecondPassNewMetals ) { for ( i = 0; i < num_at; i ++ ) { /* clear all members of pVA[i] except two */ pTCGroups->charge_on_atoms -= pVA[i].cInitCharge; va1.cMetal = pVA[i].cMetal; va1.cMinRingSize = pVA[i].cMinRingSize; va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons; va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; va1.cPeriodicNumber = pVA[i].cPeriodicNumber; pVA[i] = va1; } bSecondPassNewMetals = 1; goto repeat_for_new_metals; } /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */ ret = nCountBnsSizes( at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti, pVA, pStruct->pSrm, pTCGroups ); if ( ret < 0 ) { goto exit_function; } /* find and count groups; add counts of all other vertices to be created */ ret = nAddSuperCGroups( pTCGroups ); if ( ret < 0 ) { goto exit_function; } /* create the BNS and fill it with all real atoms */ pBNS = AllocateAndInitTCGBnStruct( pStruct, pVA, pTCGroups, nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds ); if ( !pBNS ) { ret = BNS_OUT_OF_RAM; goto exit_function; } /* add t-groups to the BNS */ ret = AddTGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); if ( ret < 0 ) { goto exit_function; } /* add c-groups to the BNS; adjust charges */ ret = AddCGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); if ( ret < 0 ) { goto exit_function; } /* allocate BNData */ pBD = AllocateAndInitBnData( pBNS->max_vertices + pBNS->max_vertices/2 ); if ( !pBD ) { ret = BNS_OUT_OF_RAM; goto exit_function; } CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 0 ); /* restore bonds & charges */ ret = RunBnsRestore1( ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH ); if ( ret < 0 ) { goto exit_function; } ret = CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 1 ); #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB if ( ret ) { fprintf( stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); } if ( pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons ) { fprintf( stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ } if ( pStruct->bExtract & EXTRACT_STRUCT_NUMBER ) { fprintf( stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); } #endif #endif { /* create the final structure in pStruct->at2 */ inp_ATOM *at_tmp = pStruct->at; pStruct->at = pStruct->at2; memcpy( pStruct->at, at_tmp, sizeof(pStruct->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H) ); ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); pStruct->at2 = pStruct->at; pStruct->at = at_tmp; if ( ret2 < 0 ) { ret = ret2; } } exit_function: pStruct->pbfsq = NULL; AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); pBD = DeAllocateBnData( pBD ); pBNS = DeAllocateBnStruct( pBNS ); /* if ( pVA ) inchi_free( pVA ); */ if ( pTCGroups->pTCG ) inchi_free( pTCGroups->pTCG ); return ret; } /******************************************************************************************************/ int OneInChI2Atom( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]) { int ret; INPUT_PARMS *ip, ip_loc; ip_loc = *ip_inp; ip = &ip_loc; sd->pStrErrStruct[0] = '\0'; ret = RestoreAtomConnectionsSetStereo( pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]); if ( ret < 0 ) { goto exit_function; } ret = SetStereoBondTypesFrom0DStereo( pStruct, pInChI[0]); if ( ret < 0 ) { goto exit_function; } ret = ReconcileAllCmlBondParities( pStruct->at, pStruct->num_atoms, 0 ); if ( ret < 0 ) { goto exit_function; } /* main InChI restore function */ ret = RestoreAtomMakeBNS( ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH ); #ifndef COMPILE_ANSI_ONLY if ( (pStruct->num_inp_actual>0? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number && ( (/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings ) && pStruct->pXYZ ) ) { inchiTime ulTStart; InchiTimeGet( &ulTStart ); DisplayRestoredComponent( pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr ); sd->ulStructTime -= InchiTimeElapsed( &ulTStart ); /* subtract display time */ } #endif if ( ret < 0 ) { goto exit_function; } if ( (pStruct->num_inp_actual? pStruct->num_inp_actual: num_inp) >= ip->first_struct_number && ret >= 0 ) { /* remove t-group markings and increment zero-order bonds, otherwise MakeInChIOutOfStrFromINChI2() woild fail */ /* --- moved to MakeInChIOutOfStrFromINChI2 --- IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); */ /* include all restored structure features in pStruct->at2 */ /* make full InChI out of pStruct->at2, pStruct->num_atoms */ /***************************************************************************************/ /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */ /***************************************************************************************/ if ( bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES && !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ ip->nMode |= REQ_MODE_BASIC; } ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct, iComponent, iAtNoOffset, num_inp ); if ( ret >= 0 ) { ; } #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB else { fprintf( stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); } #endif #endif } exit_function: return ret; } /********************************************************************************************/ int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ) { inp_ATOM *at = NULL; int i; if ( num_prot <= 0 ) { return 0; } /* allocate */ pStruct->at = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at[0]) ); pStruct->at2 = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at2[0]) ); if ( !pStruct->at || !pStruct->at2 ) { return 0; } /* create protons */ at = pStruct->at; /* fill out proton atom info */ for ( i = 0; i < num_prot; i ++ ) { strcpy( at[i].elname, "H" ); at[i].el_number = EL_NUMBER_H; at[i].orig_at_number = i+1; /* at[i].orig_compt_at_numb = i + 1; at[i].component = i + 1; */ at[i].charge = 1; } memcpy( pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]) ); pStruct->bDeleted = 0; pStruct->num_atoms = num_prot; pStruct->bMobileH = TAUT_YES; pStruct->iMobileH = TAUT_YES; return num_prot; } /********************************************************************************************/ int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, StrFromINChI *pStruct, int num_components, StrFromINChI *pStructR, int num_componentsR, NUM_H *nProtonsToBeRemovedByNormFromRevrs, int *recmet_change_balance ) { /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ int iComp, q, ret = 0; int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed, num_deleted_components; inp_ATOM *at; INPUT_PARMS *ip, ip_loc; int num_prot = *nProtonsToBeRemovedByNormFromRevrs; int delta_recmet_prot, num_prot_prev, bAccumulateChanges=0, nNumProtAddedByRevrs; INChI_Aux *pINChI_Aux; INCHI_MODE bNormalizationFlags; int nChargeRevrs, nChargeInChI; if ( !num_prot ) { return 0; } delta_recmet_prot = 0; num_changed = 0; num_deleted_components = 0; ip_loc = *ip_inp; ip = &ip_loc; /*---------------------------------------------------------------------------------- nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is same as already processed reconnected one Do no preicess it nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; (should not happen) It it is a result of (nLink-1)th Reconeected component disconnection (NOT IMPLEMENTED YET) nLink = 0 => Process this component. It is either a reconnected component, or a result of a disconnection (for now) nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as a disconnected one that will not be processed. Process and save charge delta. -----------------------------------------------------------------------------------*/ for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { bAccumulateChanges = 0; if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { /* check */ q = -(pStruct[iComp].nLink+1); if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { ret = RI_ERR_PROGR; goto exit_function; } continue; /* Disconnected structure component has already been processed as a Reconnected one */ } at = pStruct[iComp].at2; num_atoms = pStruct[iComp].num_atoms; tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); nChargeRevrs = pStruct[iComp].nChargeRevrs; nChargeInChI = pStruct[iComp].nChargeInChI; num_deleted_components += (0 != pStruct[iComp].bDeleted); if ( !at || !num_atoms ) { continue; } /* find whether it is a reconnected structure */ q = bRevInchiComponentExists( pStruct+iComp, INCHI_REC, TAUT_YES, 0 )? INCHI_REC : INCHI_BAS; /* q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; */ pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/ nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs; bNormalizationFlags = pINChI_Aux->bNormalizationFlags; num_tg = pINChI_Aux->nNumberOfTGroups; /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { goto exit_function; } num_prot_prev = num_prot; ret = AddRemoveProtonsRestr( at, num_atoms, &num_prot, nNumProtAddedByRevrs, bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI ); pStruct[iComp].bPostProcessed = ret; num_changed += (ret > 0); if ( ret < 0 ) { goto exit_function; } if ( ret > 0 ) { /* recalculate InChI; it will reconnect at */ StrFromINChI *pStruct1 = pStruct + iComp; INCHI_MODE nMode = ip->nMode; FreeAllINChIArrays( pStruct1->RevInChI.pINChI, pStruct1->RevInChI.pINChI_Aux, pStruct1->RevInChI.num_components ); if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ ip->nMode |= REQ_MODE_BASIC; } /* calls ConnectDisconnectedH(...): subtracts number of implicit iso H from implicit H */ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); ip->nMode = nMode; if ( ret < 0 ) { goto exit_function; } } else { /* reconnect disconnected terminal H and subtracts number of implicit iso H from implicit H */ if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { goto exit_function; } } if ( bAccumulateChanges && recmet_change_balance ) { /* processed Reconnected layer component that is also present in Disconnected layer */ delta_recmet_prot += num_prot - num_prot_prev; } } iComp = num_components-1; if ( !bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted ) { /* add bare protons to the deleted Mobile-H component; undelete the component */ num_prot_prev = num_prot; if ( !MakeProtonComponent( pStruct+iComp, iComp, num_prot ) ) { goto exit_function; } else { /* recalculate InChI; it will reconnect at */ StrFromINChI *pStruct1 = pStruct + iComp; INCHI_MODE nMode = ip->nMode; num_changed ++; num_prot = 0; FreeAllINChIArrays( pStruct1->RevInChI.pINChI, pStruct1->RevInChI.pINChI_Aux, pStruct1->RevInChI.num_components ); if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ ip->nMode |= REQ_MODE_BASIC; } /* Although MakeInChIOutOfStrFromINChI2() calls ConnectDisconnectedH(...) */ /* to subtracts number of implicit iso H from implicit H */ /* this CANNOT have any effect on the deleted H component */ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); ip->nMode = nMode; if ( ret < 0 ) { goto exit_function; } if ( bAccumulateChanges && recmet_change_balance ) { /* processed Reconnected layer component that is also present in Disconnected layer */ delta_recmet_prot += num_prot - num_prot_prev; } } } *nProtonsToBeRemovedByNormFromRevrs = num_prot; if ( recmet_change_balance ) { *recmet_change_balance = delta_recmet_prot; } exit_function: return ret < 0? ret : num_changed; } /**********************************************************************************/ int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, StrFromINChI *pStruct, int num_components, StrFromINChI *pStructR, int num_componentsR, NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ) { /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ int iComp, q, k, ret = 0, bNotEmpty; int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed; inp_ATOM *at; NUM_H num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES]; int bAccumulateChanges; INChI_Aux *pINChI_Aux; INChI *pINChI; INCHI_MODE bNormalizationFlags; INPUT_PARMS *ip, ip_loc; ip_loc = *ip_inp; ip = &ip_loc; memcpy( num_prot, pProtonBalance, sizeof(num_prot) ); for ( bNotEmpty=0, k = 0; k < NUM_H_ISOTOPES; k ++ ) { bNotEmpty |= num_prot[k]; } if ( !bNotEmpty ) { return 0; } memset ( delta_recmet_prot, 0, sizeof(delta_recmet_prot)); num_changed = 0; /*---------------------------------------------------------------------------------- nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is same as already processed reconnected one Do no preicess it nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; (should not happen) It it is a result of (nLink-1)th Reconeected component disconnection (NOT IMPLEMENTED YET) nLink = 0 => Process this component. It is either a reconnected component, or a result of a disconnection (for now) nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as a disconnected one that will not be processed. Process and save charge delta. -----------------------------------------------------------------------------------*/ for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { bAccumulateChanges = 0; if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { /* check */ q = -(pStruct[iComp].nLink+1); if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { ret = RI_ERR_PROGR; goto exit_function; } continue; /* Disconnected structure component has already been processed as a Reconnected one */ } at = pStruct[iComp].at2; num_atoms = pStruct[iComp].num_atoms; tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); if ( !at || !num_atoms ) { continue; } /* find whether it is a reconnected structure */ q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ pINChI = pStruct[iComp].RevInChI.pINChI[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ bNormalizationFlags = pINChI_Aux->bNormalizationFlags; num_tg = pINChI_Aux->nNumberOfTGroups; memcpy( num_prot_prev, num_prot, sizeof(num_prot_prev) ); /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */ ret = AddRemoveIsoProtonsRestr( at, num_atoms, num_prot, num_tg ); pStruct[iComp].bPostProcessed |= ret; num_changed += (ret > 0); if ( ret < 0 ) { goto exit_function; } if ( ret > 0 ) { StrFromINChI *pStruct1 = pStruct+iComp; INCHI_MODE nMode = ip->nMode; /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */ /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { goto exit_function; } FreeAllINChIArrays( pStruct1->RevInChI.pINChI, pStruct1->RevInChI.pINChI_Aux, pStruct1->RevInChI.num_components ); if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ ip->nMode |= REQ_MODE_BASIC; } /* input: disconnected explicit H, output: connected explicit H */ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); ip->nMode = nMode; if ( ret < 0 ) { goto exit_function; } } /* the following was commented out 2007-08-28 by DT. Reason: it's a bug since H must be already connected */ /* else { if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { goto exit_function; } } */ if ( bAccumulateChanges ) { /* processed Reconnected layer component that is also present in Disconnected layer */ for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k]; } } } memcpy ( pProtonBalance, num_prot, sizeof(num_prot) ); if ( recmet_change_balance ) { memcpy ( recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot) ); } exit_function: return ret < 0? ret : num_changed; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr5.c000066400000000000000000001676271271037650300234730ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" #define INC_ADD_EDGE 64 /***********************************************************************************************/ int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ) { int k, ePlusSuper, eMinusSuper, vPlusSuper, vMinusSuper, vPlusMinus1 = NO_VERTEX, vPlusMinus2 = NO_VERTEX; BNS_EDGE *pEdge; if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && !((pEdge=pBNS->edge + ePlusSuper)->forbidden && bCheckForbiddenPlus) ) { vPlusMinus1 = pEdge->neighbor12 ^ vPlusSuper; } if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && !((pEdge=pBNS->edge + eMinusSuper)->forbidden && bCheckForbiddenMinus) ) { vPlusMinus2 = pEdge->neighbor12 ^ eMinusSuper; } if ( bCheckForbiddenPlus && NO_VERTEX == vPlusMinus1 || bCheckForbiddenMinus && NO_VERTEX == vPlusMinus2 ) { return NO_VERTEX; } return (NO_VERTEX != vPlusMinus1)? vPlusMinus1 : vPlusMinus2; } /***********************************************************************************************/ int bIsUnsatCarbonInASmallRing( inp_ATOM *at2, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ) { int j, nCurRingSize, nMinRingSize; if ( min_ring_size < 5 ) { /* =C= in a small ring */ if ( at2[iat].valence == 2 && pVA[iat].cMinRingSize <= 5 && at2[iat].chem_bonds_valence == 4 ) { return 1; } } else { if ( at2[iat].valence == 2 && pVA[iat].cMinRingSize && pVA[iat].cMinRingSize <= min_ring_size && at2[iat].chem_bonds_valence == 3 ) { return 1; } nCurRingSize = nMinRingSize = min_ring_size+1; if ( (at2[iat].valence == 2 || at2[iat].valence == 3) && at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { for ( j = 0; j < at2[iat].valence; j ++ ) { nCurRingSize = is_bond_in_Nmax_memb_ring( at2, iat, j, pbfsq->q, pbfsq->nAtomLevel, pbfsq->cSource, (AT_RANK)nMinRingSize /* max ring size */ ); if ( 0 < nCurRingSize && nCurRingSize < nMinRingSize ) { nMinRingSize = nCurRingSize; } } return (0 <= nCurRingSize)? (nMinRingSize <= min_ring_size) : nCurRingSize; } } return 0; } /***********************************************************************************************/ int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) { /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ /* at2 should be the most recently restored atom, Fixed-H */ int i, j, k, iat, delta, tot_succes, cur_success, ret = 0; CMP2MHINCHI c2i; CMP2MHINCHI *pc2i = &c2i; EDGE_LIST AllChargeEdges, CurrEdges, CurrEdges2, CurrEdges3, TautEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; EDGE_LIST *pEdgeList = NULL; EdgeIndex e; BNS_EDGE *pe; Vertex v1, v2, vPlusMinus; BNS_VERTEX *pv1, *pv2; Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; INCHI_HEAPCHK AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CurrEdges2, EDGE_LIST_CLEAR ); AllocEdgeList( &CurrEdges3, EDGE_LIST_CLEAR ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &TautEdges, EDGE_LIST_CLEAR ); tot_succes = 0; if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } /* taut group edges */ for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { pv1 = pBNS->vert + (v1=pTCGroups->pTCG[i].nVertexNumber); /* t-group vertex */ for ( j = 0; j < pv1->num_adj_edges; j ++ ) { /* e, pe - tautomeric atom edge; pv2 - endpoint vertex */ /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ pv2 = pBNS->vert + (pe = pBNS->edge + (e=pv1->iedge[j]))->neighbor1; if ( ret = AddToEdgeList( &TautEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } /* charge and flower edges */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { goto exit_function; } if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { if ( !pBNS->edge[j].forbidden && pBNS->edge[j].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } else { if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { goto exit_function; } } } } } if ( forbidden_stereo_edge_mask ) { for ( i = 0; i < pStruct->num_atoms; i ++ ) { for ( j = 0; j < at2[i].valence; j ++ ) { if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, pStruct->pbfsq->nAtomLevel, pStruct->pbfsq->cSource, 99 /* max ring size */ ); if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { goto exit_function; } } } } } INCHI_HEAPCHK if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } INCHI_HEAPCHK if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } INCHI_HEAPCHK if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI && !(pTCGroups->pTCG[0].tg_RestoreFlags & TGRF_MINUS_FIRST) ) { /*----------------------------------------------------*/ /* case 01: restored has -O(-) and does not have N(-) */ /* endpoints defined by the original InChI */ /* restored has single taut. group or more */ /* tautomeric endpoints. */ /* Solution: move (-) from endp. -O(-) to endpoints N */ /*----------------------------------------------------*/ pTCGroups->pTCG[0].tg_RestoreFlags |= TGRF_MINUS_FIRST; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { goto exit_function; /* no fixed-H found */ } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI == 0 ) { /*-------------------------------------------------------*/ /* case 02: restored has no -O(-) and does not have N(-) */ /* restored has single taut. group or more */ /* tautomeric endpoints. */ /* Solution: >N-AB=N- => >N(+)=AB-NH- (add H(+)) */ /* Solution: >N-AB=NH => >N(+)=AB-NH2 (add H(+)) */ /* SB_N_III DB_N_III */ /*-------------------------------------------------------*/ int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2; CurrEdges.num_edges = 0; cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = i; if ( pVA[iat].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical ) { if ( num_DB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3 && !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].cap && !pBNS->edge[e].flow ) { /* -N= */ iat_DB_N_III[ num_DB_N_III ++ ] = iat; } else if ( num_DB_N_III < MAX_DIFF_MOBH && 1 == at2[iat].num_H && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].cap && !pBNS->edge[e].flow ) { /* -N= */ iat_DB_N_III[ num_DB_N_III ++ ] = iat; } else if ( num_SB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && at2[iat].valence == 3 && at2[iat].chem_bonds_valence == 3 && (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].cap && pBNS->edge[e].flow) { /* -N< */ iat_SB_N_III[ num_SB_N_III ++ ] = iat; if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } } if ( num_DB_N_III && num_SB_N_III ) { EdgeIndex ieMinus; BNS_EDGE *peMinus; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); for ( i = 0; i < num_DB_N_III && !cur_success; i ++ ) { iat = iat_DB_N_III[ i ]; e = pBNS->edge[k1=pBNS->vert[iat].iedge[0]].flow? k1 : pBNS->edge[k2=pBNS->vert[iat].iedge[1]].flow? k2 : NO_VERTEX; if ( e == NO_VERTEX ) { continue; /* should not happen */ } ieMinus = pVA[iat].nCMinusGroupEdge-1; peMinus = pBNS->edge + ieMinus; pe = pBNS->edge + e; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; /* fix double bond */ peMinus->forbidden &= forbidden_edge_mask_inv; /* allow negative charge */ delta = 1; pe->flow -= delta; /* remove (-) from AB-O(-) */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 01 */ /* eliminate (-) charge and add H */ pv1 = pBNS->vert + (v1 = peMinus->neighbor1); /* atom */ pv2 = pBNS->vert + (v2 = peMinus->neighbor12 ^ v1);/* (=) vertex */ /* effectively eliminate (-) edge by setting its cap=flow= 0 */ peMinus->cap --; peMinus->flow --; pv1->st_edge.cap --; pv1->st_edge.flow --; pv2->st_edge.cap --; pv2->st_edge.flow --; pBNS->tot_st_flow -= 2; pBNS->tot_st_cap -= 2; /* add H */ pStruct->at[iat].num_H ++; /* register total charge increase */ pTCGroups->total_charge ++; pStruct->nNumRemovedProtonsByRevrs -= 1; } } else { pe->forbidden &= forbidden_edge_mask_inv; peMinus->forbidden |= forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } } if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ pc2i->nNumTgMInChI == 0 && pc2i->nNumTgNInChI && pc2i->nNumTgOInChI ) { /*-------------------------------------------------------*/ /* case 03: restored has N and O endpoints, no (-) endp */ /* case 04: original has single taut. group or more */ /* tautomeric endpoints. */ /* Solution: 1. Move taut attachment from O to N */ /* Solution: 2. Replace the attachment with (-) */ /* SB_N_III DB_N_III */ /*-------------------------------------------------------*/ /* int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2, */ int itg, j1, j2, bAction = 0; BNS_VERTEX *pTg, *pvEndp, *pvEndp2, *pvCent; Vertex vEndp, vEndp2, vCent; BNS_EDGE *peTg, *peTg2, *peCent1, *peCent2; EdgeIndex eTg, eTg2; CurrEdges.num_edges = 0; CurrEdges2.num_edges = 0; cur_success = 0; /* 1st attempt: -NH-=O => -N(-)-=O or -N=-OH => -N(-)-=O */ for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); eTg2 = -1; if ( pVA[vEndp].cNumValenceElectrons == 6 && peTg->cap ) { /* endpoint -OH or =O found; search for a possible centerpoint */ for ( j1 = 0; j1 < at2[vEndp].valence && eTg2 < 0; j1 ++ ) { peCent1 = pBNS->edge + pvEndp->iedge[j1]; /* edge from O to a centerpoint */ pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); /* centerpoint */ if ( at2[vCent].endpoint || !peCent1->cap || peCent1->flow + (peTg->cap == peTg->flow) != 1 ) { continue; } /* search for another endpoint, N, around vCent */ for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { peCent2 = pBNS->edge + pvCent->iedge[j2]; pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); if ( !peCent2->cap || peCent2->flow+peCent1->flow != 1 || at2[vEndp2].endpoint != itg+1 || pVA[vEndp2].cNumValenceElectrons != 5 || 0 > (j=pVA[vEndp2].nTautGroupEdge-1) || (peTg2 = pBNS->edge + j)->forbidden || peCent2->flow + (peTg2->cap == peTg2->flow) != 1 ) { continue; } eTg2 = j; break; /* found OH-C=N- or O=C-NH- */ } } } if ( eTg2 >= 0 ) { /*-------------------------------------------- tg tg eTg //\ eTg2 eTg / \\eTg2 // \ / \\ vEndp HO--C==N vEndp2 --> vEndp O==C--NH vEndp2 ^ ^ ^ ^ ^ ^ eCent1 | eCent2 eCent1 | eCent2 vCent vCent additional action: -OH-C=N- => O=C-NH- -------------------------------------------*/ if ( 0 == peTg->cap - peTg->flow && 1 == peTg2->cap - peTg2->flow && 0 == peCent1->flow && 1 == peCent2->flow ) { peTg->flow --; /* 03 prepare */ peTg2->flow ++; peCent2->flow --; peCent1->flow ++; bAction |= 1; /* switched H position */ } if ( 1 == peTg->cap - peTg->flow && 0 == peTg2->cap - peTg2->flow && 1 == peCent1->flow && 0 == peCent2->flow ) { /* replace -NH- with -N(-)- */ pTCGroups->pTCG[itg].tg_num_H --; pTCGroups->pTCG[itg].tg_num_Minus ++; pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; pTCGroups->pTCG[itg].tg_set_Minus = vEndp2+1; pStruct->ti.t_group[itg].num[1] ++; /* increment number of (-), keep number of taut attachments */ pTCGroups->total_charge --; pTCGroups->tgroup_charge --; pStruct->nNumRemovedProtonsByRevrs += 1; bAction |= 2; /* single NH (at2[vEndp2]) replaced with N(-) */ cur_success ++; /* 03/04 */ } } } } if ( 0 == pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOHInChI && !cur_success ) { /* transfer an attachement to N */ for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; for ( i = 0; i < pTg->num_adj_edges; i ++ ) { pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); if ( pVA[vEndp].cNumValenceElectrons == 6 && at2[vEndp].valence == at2[vEndp].chem_bonds_valence && peTg->flow && peTg->flow == peTg->cap ) { /* endpoint -OH found; save the tautomeric group edge */ if ( ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( pVA[vEndp].cNumValenceElectrons == 5 && pVA[vEndp].cPeriodicRowNumber == 1 && at2[vEndp].valence + 1 == at2[vEndp].chem_bonds_valence && peTg->cap && peTg->flow + 1 == peTg->cap ) { /* endpoint -N= or =NH found, check for -N=-OH */ e = -1; for ( j1 = 0; j1 < at2[vEndp].valence && e < 0; j1 ++ ) { peCent1 = pBNS->edge + pvEndp->iedge[j1]; if ( peCent1->flow == 1 ) { /* double bond */ pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); if ( at2[vCent].endpoint ) continue; for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { peCent2 = pBNS->edge + pvCent->iedge[j2]; pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); if ( peCent2->flow || at2[vEndp2].endpoint != itg+1 || pVA[vEndp2].cNumValenceElectrons != 6 || 0 >= (e=pVA[vEndp2].nTautGroupEdge-1) || pBNS->edge[e].forbidden || !pBNS->edge[e].flow ) { e = -1; continue; } /*********************/ /* found -N=X-OH */ /* vEndp ^ vEndp2 */ /* vCent */ /*********************/ /* save this -OH taut edge */ if ( ret = AddToEdgeList( &CurrEdges2, e, INC_ADD_EDGE ) ) { goto exit_function; } break; } } } if ( e < 0 && (ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE )) ) { goto exit_function; } } } } /* rearrange the flows */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); pEdgeList = CurrEdges2.num_edges? &CurrEdges2 : CurrEdges.num_edges? &CurrEdges : NULL; for ( i = 0; pEdgeList && i < pEdgeList->num_edges && !cur_success; i ++ ) { pe = pBNS->edge + pEdgeList->pnEdges[i]; /* pe->flow = 1 <=> -OH */ if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); /* -OH atom */ pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* t-group vertex */ /* locate the t-group */ for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { if ( v2 == pTCGroups->pTCG[itg].nVertexNumber ) { break; } } if ( itg == pTCGroups->num_tgroups ) { /* tgroup not found -- should not happen */ continue; } delta = 1; pe->flow -= delta; /* add one attachment to */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 03 */ /* replace -NH- with -N(-)- */ pTCGroups->pTCG[itg].tg_num_H --; pTCGroups->pTCG[itg].tg_num_Minus ++; pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; pStruct->ti.t_group[itg].num[1] ++; pTCGroups->total_charge --; pTCGroups->tgroup_charge --; pStruct->nNumRemovedProtonsByRevrs += 1; bAction |= 4; /* H in the 1st available NH was replaced with (-) */ } } else { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); } else if ( pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOInChI && !cur_success ) { /* change an attachement to N from H to (-) */ for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { pvEndp2 = pBNS->vert + (vEndp2 = (peTg = pBNS->edge + pTg->iedge[i])->neighbor1); if ( pVA[vEndp2].cNumValenceElectrons == 5 && pVA[vEndp2].cPeriodicRowNumber == 1 && at2[vEndp2].valence == at2[vEndp2].chem_bonds_valence && peTg->flow && peTg->flow == peTg->cap ) { /* endpoint -NHn found; change its charge */ cur_success ++; /* 04 */ /* replace -NH- with -N(-)- */ pTCGroups->pTCG[itg].tg_num_H --; pTCGroups->pTCG[itg].tg_num_Minus ++; pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; pTCGroups->pTCG[itg].tg_set_Minus = vEndp2 + 1; pStruct->ti.t_group[itg].num[1] ++; pTCGroups->total_charge --; pTCGroups->tgroup_charge --; pStruct->nNumRemovedProtonsByRevrs += 1; bAction |= 8; /* manually set (-) charge to NH atom, vEndp2 */ } } } } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( pStruct->One_ti.num_t_groups == 1 && pStruct->One_ti.t_group[0].num[1] ) { /* this method did not work: no alt path from N(-) to =O */ itg = 0; if ( bAction & (8 | 2 ) ) { /* roll back NH -> N(-) replacement; H move from OH to N is not undone */ pTCGroups->pTCG[itg].tg_num_H ++; pTCGroups->pTCG[itg].tg_num_Minus --; pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; pTCGroups->pTCG[itg].tg_set_Minus = 0; pStruct->ti.t_group[itg].num[1] --; pTCGroups->total_charge ++; pTCGroups->tgroup_charge ++; pStruct->nNumRemovedProtonsByRevrs -= 1; cur_success --; } else if ( bAction & 4 ) { pTCGroups->pTCG[itg].tg_num_H ++; pTCGroups->pTCG[itg].tg_num_Minus --; pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; pStruct->ti.t_group[itg].num[1] --; pTCGroups->total_charge ++; pTCGroups->tgroup_charge ++; pStruct->nNumRemovedProtonsByRevrs -= 1; cur_success --; } else { ret = RI_ERR_PROGR; goto exit_function; } /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } } if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ pc2i->nNumTgMInChI == 0 && (pc2i->nNumTgNInChI || pc2i->nNumTgOInChI) && NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 )) ) { /*---------------------------------------------------------------------------*/ /* case 05: restored has N endpoints, no (-) endpoints */ /* original has single taut. group or more */ /* tautomeric endpoints. */ /* Solution: Find -N< and allow (+) charge change */ /* Fix all charges and taut attachments exept */ /* =N- and =O (taut. endpoints) */ /* Increment st_edge.cap on (+/-) vertex => add (+) charge to -N< */ /* Increment tot. charge in other places */ /* Increment t-group st_edge.cap */ /* Run BNS */ /* */ /* (+/-)* (+/-) Result: */ /* | || */ /* | || - Added (+) to -N< */ /* (+)super (+)super - Added attachment point to O */ /* || | */ /* || => | To make this attachment H, */ /* (Y) (Y) increment */ /* | || pTCGroups->pTCG[itg].tg_num_H */ /* | || */ /* (+)hetero (+)hetero Technical details: */ /* \\ \ increase capacities of */ /* N N(+) edges to (+/-) otherwise */ /* | || flow may not be able to */ /* *(t)--O=R. (t)==O-R. increase */ /* */ /* */ /*---------------------------------------------------------------------------*/ int itg; BNS_VERTEX *pTg, *pvEndp; Vertex vEndp, vTg; BNS_EDGE *peTg; EdgeIndex eTg; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; CurrEdges.num_edges = 0; CurrEdges2.num_edges = 0; cur_success = 0; /* find -N< and non-taut =N- or =O */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; /* -N< */ if ( !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical && !at2[iat].num_H && pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && pBNS->edge[e].flow && !pBNS->edge[e].forbidden) { if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( !CurrEdges.num_edges ) { goto exit_case_05; } /* find taut -N= and =O */ for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { CurrEdges2.num_edges = 0; pTg = pBNS->vert + (vTg = pTCGroups->pTCG[itg].nVertexNumber); for ( i = 0; i < pTg->num_adj_edges; i ++ ) { pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); if ( at2[vEndp].charge || at2[vEndp].radical || peTg->cap - peTg->flow != 1 ) { continue; } /* t-group edges to -N= and =O */ if ( ret = AddToEdgeList( &CurrEdges2, eTg, INC_ADD_EDGE ) ) { goto exit_function; } } if ( !CurrEdges2.num_edges ) { goto exit_case_05; } /* fix all charge edges except -N< and all taut. edges except =O and =N- */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); SetForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); delta = 1; /* Increment st_edge.cap on (+/-) vertex */ pBNS->vert[vPlusMinus].st_edge.cap += delta; /* Increment st_edge.cap on t-group */ pTg->st_edge.cap += delta; /* total cap count */ pBNS->tot_st_cap += 2*delta; v1 = vPlusMinus; v2 = vTg; /* increase capacities of edges to Y */ for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { pBNS->edge[pBNS->vert[j].iedge[k]].cap += delta; } } ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (+)charge to -N< => nDeltaCharge == 1 */ /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 01 */ /* update bookkeeping */ pTCGroups->total_charge += delta; pTCGroups->pTCG[itg].edges_cap += delta; pTCGroups->pTCG[itg].tg_num_H += delta; pStruct->nNumRemovedProtonsByRevrs -= delta; } } else { pBNS->vert[vPlusMinus].st_edge.cap -= delta; pTg->st_edge.cap -= delta; /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ pBNS->tot_st_cap -= 2*delta; /* decrease capacities of edges to Y */ for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { pBNS->edge[pBNS->vert[j].iedge[k]].cap -= delta; } } } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); } if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } exit_case_05:; } while ( pc2i->nNumDiffMobH && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { /*----------------------------------------------------*/ /* case 06: restored has extra H attached to -O(-) */ /* while the chrge should be on C, most pro- */ /* bably in a small ring.ut. group or more */ /* tautomeric endpoints. */ /* Solution: move (-) from O to C */ /*----------------------------------------------------*/ int iO, mode; EdgeIndex e2; BNS_EDGE *pe2; cur_success = 0; for ( i = 0; !cur_success && i < pc2i->len_c2at; i ++ ) { if ( pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nMobHInChI + 1 && pc2i->c2at[i].nNumHRevrs == pc2i->c2at[i].nMobHInChI && !pc2i->c2at[i].endptInChI && !pc2i->c2at[i].endptRevrs && at2[iO = pc2i->c2at[i].atomNumber].charge == -1 && 0 <= (e=pVA[iO].nCMinusGroupEdge-1) && (pe=pBNS->edge+e)->flow ) { /* try suitable atoms C */ /* first look for =C= in a small ring */ for( mode = 4; !cur_success && mode <= 8; mode ++ ) { if ( mode == 8 ) mode = 99; for ( iat = 0; !cur_success && iat < pStruct->num_atoms; iat ++ ) { if ( !at2[iat].charge && !at2[iat].radical && pVA[iat].cNumValenceElectrons == 4 && 0 <= (e2=pVA[iat].nCMinusGroupEdge-1) && !(pe2=pBNS->edge+e2)->flow && 0 < bIsUnsatCarbonInASmallRing( at2, pVA, iat, pStruct->pbfsq, mode ) ) { SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); /* allow negative charge on the chosen carbon */ pe2->forbidden &= forbidden_edge_mask_inv; delta = 1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { /* Added (-)charge to unsaturated C => nDeltaCharge == 2 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; cur_success ++; /* 01 */ tot_succes += cur_success; } } else { pe->forbidden |= forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } } } } } if ( cur_success ) { /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } else { break; } } if ( pc2i->len_c2at && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { /*------------------------------------------------------------------*/ /* case 07: -NO2 are to be tautomeric but they are not AND */ /* InChI has a SINGLE tautomeric group */ /* */ /* (-)O (-)O */ /* Solution: convert \ \ */ /* N-X=...-Z(-) => N(+)=X- ...=Z */ /* // / */ /* O (-)O */ /* */ /* O O */ /* or \\ \\ */ /* N-X=...-Z(-) => N=X- ...=Z */ /* // / */ /* O (-)O */ /* */ /* */ /* (a) move (-) from other tautomeric atom to O in O=N-X */ /* or from other atom that has to be tautomeric */ /* but is not */ /* (b) create (+) [ion pair creation] on N as in */ /* */ /* OH OH */ /* / / */ /* -C=N => =C-N(+) */ /* \\ \\ */ /* O O */ /* */ /*------------------------------------------------------------------*/ int num_DB_O = 0; short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; /* AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; */ inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; int iN, one_success; BNS_EDGE *peDB_O_Minus; int neigh, nNumO, nNumOthers; #define CHG_SET_WRONG_TAUT_N 0 #define CHG_SET_WRONG_TAUT_O 1 #define CHG_SET_WRONG_TAUT_ALL 2 #define CHG_LAST_SET 2 /* the last index in trying */ #define CHG_SET_O_FIXED 3 #define CHG_SET_NUM 4 EDGE_LIST ChangeableEdges[CHG_SET_NUM]; memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ /* S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; */ CurrEdges.num_edges = 0; /* clear current edge list */ cur_success = 0; for ( i = 0; i < pStruct->num_atoms; i ++ ) { iat = nCanon2AtnoRevrs[i]; if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ num_DB_O < MAX_DIFF_FIXH && pVA[iat].cNumValenceElectrons == 6 /* O, S, Se, Te */ && (!at2[iat].endpoint /*|| pc2i->c2at[i].nMobHInChI*/) && (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && at2[iat].num_H == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ /* reversed structure info: */ !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) /*|| pc2i->c2at[i].nMobHRevrs*/ && !at2[iat].charge && at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && /* find whether it belongs to NO2 */ pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { /* find the second O */ nNumO = nNumOthers = 0; for ( k = 0; k < at2[iN].valence; k ++ ) { neigh = at2[iN].neighbor[k]; if ( neigh == iat ) { continue; } if ( pVA[neigh].cNumValenceElectrons == 6 && !at2[neigh].endpoint && !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && at2[neigh].valence == 1 && at2[neigh].num_H == 0 && at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { nNumO ++; } else if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && at2[neigh].valence > 1 && at2[neigh].valence < at2[neigh].chem_bonds_valence ) { nNumOthers ++; } } if ( nNumO != 1 || nNumOthers != 1 ) { continue; } for ( k = 0; k < num_DB_O; k ++ ) { if ( iat_NO2[k] == iN ) { break; } } if ( k == num_DB_O ) { iat_NO2[num_DB_O] = iN; iat_DB_O[num_DB_O ++] = iat; } /* save the =O (-)-edge to avoid interference */ if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { goto exit_case_07; } } } if ( num_DB_O ) { /* search for falsely tautomeric negatively charged atoms N and O */ for ( i = 0; i < pc2i->len_c2at; i ++ ) { iat = pc2i->c2at[i].atomNumber; if ( pc2i->c2at[i].endptRevrs && !pc2i->c2at[i].endptInChI && pc2i->c2at[i].nAtChargeRevrs == - 1 && 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) ) { if ( pc2i->c2at[i].nValElectr == 6 ) { if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_O], e, INC_ADD_EDGE ) ) { goto exit_case_07; } } else if ( pc2i->c2at[i].nValElectr == 5 ) { if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_N], e, INC_ADD_EDGE ) ) { goto exit_case_07; } } if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_ALL], e, INC_ADD_EDGE ) ) { goto exit_case_07; } } } /* ------- finally, try to move charges from O=N --------------*/ for ( i = 0; i < num_DB_O; i ++ ) { int nDeltaChargeExpected; one_success = 0; delta = 1; iat = iat_DB_O[i]; peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); pe = pBNS->edge + pBNS->vert[iat].iedge[0]; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { if ( !ChangeableEdges[k].num_edges ) { continue; } nDeltaChargeExpected = 0; SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); /* allow (-) charge to move to N=O */ peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == nDeltaChargeExpected ) { /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { nNumRunBNS ++; one_success ++; /* 07 */ } } INCHI_HEAPCHK } cur_success += one_success; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); pe->forbidden &= forbidden_edge_mask_inv; if ( !one_success ) { pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } } exit_case_07: for ( i = 0; i < CHG_SET_NUM; i ++ ) { AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); } CurrEdges.num_edges = 0; /* clear current edge list */ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } if ( !pc2i->bHasDifference ) { goto exit_function; /* nothing to do */ } } #undef CHG_SET_NOOH #undef CHG_SET_WRONG_TAUT #undef CHG_SET_TAUT #undef CHG_LAST_SET #undef CHG_SET_O_FIXED #undef CHG_SET_NUM } exit_function: AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); AllocEdgeList( &CurrEdges2, EDGE_LIST_FREE ); AllocEdgeList( &CurrEdges3, EDGE_LIST_FREE ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); AllocEdgeList( &TautEdges, EDGE_LIST_FREE ); return ret; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr6.c000066400000000000000000001776121271037650300234670ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include /*#define CHECK_WIN32_VC_HEAP*/ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichi.h" #include "ichitime.h" #include "inpdef.h" #include "ichimain.h" #include "ichierr.h" #include "incomdef.h" #include "ichiring.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichinorm.h" #include "util.h" #include "ichicomp.h" #include "ichister.h" #include "ichi_bns.h" #include "strutil.h" #include "ichirvrs.h" #define INC_ADD_EDGE 64 /***********************************************************************************************/ int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) { /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ /* at2 should be the most recently restored atom, Fixed-H */ int i, j, k, delta, tot_succes, max_success, cur_success, ret = 0; int err, iOrigInChI, iRevrInChI; int j12, v1, v2, e, vRad; BNS_VERTEX *pv1, *pv2, *pvRad; BNS_EDGE *pe, *peRad; EDGE_LIST AllChargeEdges, CurrEdges, NFlowerEdges, OtherNFlowerEdges, FixedStereoEdges, AllRadList; EDGE_LIST TautMinusEdges[2]; /* 0 -> O & O(+), 1=> N & N(+) */ Vertex vPathStart, vPathEnd; int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; INChI_Stereo *pStereoInChI, *pStereo2InChI, *pStereoRevrs, *pStereo2Revrs; /* Stereo */ /* currently being processed layer */ pStereoInChI = (pInChI[0]->StereoIsotopic && pInChI[0]->StereoIsotopic->nNumberOfStereoBonds + pInChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pInChI[0]->StereoIsotopic : pInChI[0]->Stereo; /* mobile-H layer in case of Fixed-H */ pStereo2InChI = (pStruct->bMobileH == TAUT_YES || !pInChI[1] || !pInChI[1]->nNumberOfAtoms || pInChI[1]->bDeleted)? NULL: (pInChI[1]->StereoIsotopic && pInChI[1]->StereoIsotopic->nNumberOfStereoBonds + pInChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pInChI[1]->StereoIsotopic : pInChI[1]->Stereo; /* currently being processed layer */ pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; /* mobile-H layer in case of Fixed-H */ pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; INCHI_HEAPCHK AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &FixedStereoEdges, EDGE_LIST_CLEAR ); AllocEdgeList( &AllRadList, EDGE_LIST_CLEAR ); AllocEdgeList( TautMinusEdges+0, EDGE_LIST_CLEAR ); AllocEdgeList( TautMinusEdges+1, EDGE_LIST_CLEAR ); cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; if ( pStruct->bMobileH == TAUT_NON ) { /* these indexes are used to compare Mobile-H InChI */ iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; } else { iOrigInChI = 0; iRevrInChI = 0; } memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } if ( !(cmpInChI & IDIFF_SB) && !(cmpInChI2 & IDIFF_SB) ) { goto exit_function; } /* need to temporarily remove fixing of stereogenic bonds */ for ( i = 0; i < pStruct->num_atoms; i ++ ) { pv1 = pBNS->vert + i; for ( j = 0; j < at2[i].valence; j ++ ) { pe = pBNS->edge + (e=pv1->iedge[j]); if ( j == pe->neighbor1 ) { /* do not store same bond 2 times */ if ( (pe->forbidden & forbidden_stereo_edge_mask) && (ret = AddToEdgeList( &FixedStereoEdges, e, INC_ADD_EDGE ) ) ) { goto exit_function; } } } } tot_succes = 0; cur_success = 0; if ( (cmpInChI & IDIF_SB_MISS) && (!cmpInChI2 || (cmpInChI2 & IDIF_SB_MISS)) && 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { /*----------------------------------------------------*/ /* case 01: extra stereogenic bond, radical present */ /* X=N-O* => X=N=O and eliminate radical */ /*----------------------------------------------------*/ int aN; BNS_VERTEX *pvO, *pvN; BNS_EDGE *peNO; RemoveForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); for ( i = 0; i < icr->num_sb_in2_only && cur_success < max_success; i ++ ) { j12 = icr->sb_in2_only[i]; pv1 = pBNS->vert + (v1 = pStereoInChI->nBondAtom1[j12]-1); pv2 = pBNS->vert + (v2 = pStereoInChI->nBondAtom2[j12]-1); for ( k = 0; k < at2[v1].valence; k ++ ) { pe = pBNS->edge + (e = pv1->iedge[k]); if ( v2 == (pe->neighbor12 ^ v1) ) break; /* the edge has been found */ } if ( k == at2[v1].valence ) { ret = RI_ERR_SYNTAX; goto exit_function; } /* check v1 */ pv1->st_edge.cap --; pv1->st_edge.flow --; pv2->st_edge.flow --; pe->flow --; /* new radical on v2 */ vRad = NO_VERTEX; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); pv1->st_edge.cap ++; pv1->st_edge.flow ++; pv2->st_edge.flow ++; pe->flow ++; /* remove new radical on v2 */ if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v2 == vPathStart || v2 == vPathEnd) ) { vRad = (v2 == vPathStart)? vPathEnd : vPathStart; } else { pv2->st_edge.cap --; pv2->st_edge.flow --; pv1->st_edge.flow --; pe->flow --; /* new radical on v1 */ vRad = NO_VERTEX; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); pv2->st_edge.cap ++; pv2->st_edge.flow ++; pv1->st_edge.flow ++; pe->flow ++; /* remove new radical on v1 */ if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v1 == vPathStart || v1 == vPathEnd) ) { vRad = (v1 == vPathStart)? vPathEnd : vPathStart; } } if ( vRad == NO_VERTEX ) { continue; /* radical did not affect this bond */ } pvRad = pBNS->vert + vRad; /* detect =N-O* */ if ( pVA[vRad].cNumValenceElectrons == 6 && at2[vRad].valence == 1 && (peRad = pBNS->edge + pvRad->iedge[0])->flow == 0 && pVA[aN = peRad->neighbor12 ^ vRad].cNumValenceElectrons == 5 && at2[aN].valence == 2 ) { /*------------------------------------------------------------ Fix Metal disconnection/normalization inconsistency : disconnected restored R=N(+)-M R=N--M R=N + M R=N + M | -> || -> || -> | O(-) O O O* <- radical The correct R=N + M(+) disconnection | would be this: O(-) --------------------------------------------------------------*/ pvN = pBNS->vert + aN; pvO = pvRad; peNO = peRad; /* N-O* => N=O */ peNO->flow ++; pvO->st_edge.flow ++; pvN->st_edge.cap ++; pvN->st_edge.flow ++; pBNS->tot_st_cap += 1; pBNS->tot_st_flow += 2; cur_success ++; } else { /* all other radicals that affect stereo */ delta = pvRad->st_edge.cap - pvRad->st_edge.flow; pvRad->st_edge.cap -= delta; pBNS->tot_st_cap -= delta; } } /*exit_case_01:*/ SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } } cur_success = 0; if ( !(cmpInChI & IDIF_SB_MISS) && (cmpInChI2 & IDIF_SB_MISS) && icr2->num_sb_in2_only && 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { /*----------------------------------------------------*/ /* case 02: missing stereogenic bond in Mobile-H only */ /* X=N-O* => X=N=O and eliminate radical */ /*----------------------------------------------------*/ int retC, ret2C, retS, ret2S; INCHI_MODE cmpInChI_Prev, cmpInChI2_Prev; ICR icr_Prev, icr2_Prev; /* blind attepmt */ icr_Prev = *icr; icr2_Prev = *icr2; cmpInChI_Prev = cmpInChI; cmpInChI2_Prev = cmpInChI2; for ( i = AllRadList.num_edges = 0; i < pStruct->num_atoms; i ++ ) { if ( pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow == 1 && (ret = AddToEdgeList( &AllRadList, i, INC_ADD_EDGE ) ) ) { goto exit_function; } } for ( i = 0; i < AllRadList.num_edges; i ++ ) { j = AllRadList.pnEdges[i]; pBNS->vert[j].st_edge.cap -= 1; pBNS->tot_st_cap -= 1; } /*-------------------------------------------------*/ /* re-create InChI and see whether it looks better */ /*-------------------------------------------------*/ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } retC = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_CONSTIT ); retS = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_STEREO ); ret2C = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_CONSTIT ); ret2S = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_STEREO ); if ( 0 >= retC && 0 >= retS && 0 >= ret2C && 0 > ret2S ) { ; /* accept */ } else { /* reject */ for ( i = 0; i < AllRadList.num_edges; i ++ ) { j = AllRadList.pnEdges[i]; pBNS->vert[j].st_edge.cap += 1; pBNS->tot_st_cap += 1; } /*-------------------------------------------------*/ /* re-create InChI-- return to previous state */ /*-------------------------------------------------*/ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } /*exit_case_02:;*/ } cur_success = 0; if ( pStruct->bMobileH == TAUT_NON && (cmpInChI & IDIF_SB_EXTRA_UNDF) && pStruct->endpoint ) { /*------------------------------------------------------*/ /* case 03: extra stereogenic bond in Fixed-H only */ /* in Mobile-H this bond is not stereogenic. */ /* Since this bond parity is not known, it is UNDEFINED */ /*------------------------------------------------------*/ int bDone, num_endpoints; TautMinusEdges[0].num_edges = 0; TautMinusEdges[1].num_edges = 0; AllChargeEdges.num_edges = 0; /* in1 => in restored structure; in2 => in original InChI */ for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { j12 = icr->sb_undef_in1_only[i]; pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); if ( pStereo2Revrs ) { /* reject if it is extra in Mobile-H also */ if ( icr2->num_sb_undef_in1_only ) { for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { k = icr2->sb_undef_in1_only[j]; if ( v1 == pStereo2Revrs->nBondAtom1[k] && v2 == pStereo2Revrs->nBondAtom2[k] ) { break; } } if ( j < icr->num_sb_in1_only ) { continue; /* extra stereobond in Mobile H also */ } } } /* reject if it is a stereobond in Mobile-H also */ if ( pStereo2InChI && pStereo2InChI->nNumberOfStereoBonds ) { for ( j = 0; j < pStereo2InChI->nNumberOfStereoBonds; j ++ ) { if ( v1 == pStereo2InChI->nBondAtom1[j] && v2 == pStereo2InChI->nBondAtom1[j] ) { break; } } if ( j < pStereo2InChI->nNumberOfStereoBonds ) { continue; /* ignore this extra stereo bond: it is in Mobile-H */ } } /* find the edge between v1 and v2 */ for ( k = 0; k < at2[v1].valence; k ++ ) { pe = pBNS->edge + (e = pv1->iedge[k]); if ( v2 == (pe->neighbor12 ^ v1) ) break; /* the edge has been found */ } if ( k == at2[v1].valence ) { ret = RI_ERR_SYNTAX; goto exit_function; } /* Fix all charges except negative charges on tautomeric endpoints */ if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { for ( j = 0; j < pStruct->num_atoms; j ++ ) { if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( !pStruct->endpoint[j] ) { if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( pVA[j].cNumValenceElectrons == 6 ) { /* O */ if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { goto exit_function; } } else { /* N */ if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { goto exit_function; } } } if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } } } } } if ( !pe->flow ) continue; /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { /* fix tautomeric charges */ num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; if ( k == 2 ) { /* fix charges on O */ SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); num_endpoints -= (TautMinusEdges+0)->num_edges; } if ( k == 1 ) { SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); num_endpoints -= (TautMinusEdges+1)->num_edges; } if ( num_endpoints >= 2 ) { delta = 1; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ pe->flow -= delta; /* decrement stereobond order */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { /* Negative charge has been moved, no change in number of charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; /* 01 */ bDone = 1; } } else { pe->forbidden &= ~forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } /* unfix tautomeric charges */ if ( k == 2 ) RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); if ( k == 1 ) RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } /*exit_case_03:*/ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } } cur_success = 0; if ( (cmpInChI & IDIF_SB_EXTRA_UNDF) ) { /*------------------------------------------------------*/ /* case 04: extra stereogenic bond */ /* Since this bond parity is not known, it is UNDEFINED */ /*------------------------------------------------------*/ int bDone, num_endpoints; TautMinusEdges[0].num_edges = 0; TautMinusEdges[1].num_edges = 0; AllChargeEdges.num_edges = 0; /* in1 => in restored structure; in2 => in original InChI */ for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { j12 = icr->sb_undef_in1_only[i]; pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); /* find the edge between v1 and v2 */ for ( k = 0; k < at2[v1].valence; k ++ ) { pe = pBNS->edge + (e = pv1->iedge[k]); if ( v2 == (pe->neighbor12 ^ v1) ) break; /* the edge has been found */ } if ( k == at2[v1].valence ) { ret = RI_ERR_SYNTAX; goto exit_function; } if ( pStereo2Revrs ) { /* reject if it is not extra in Mobile-H also */ if ( icr2->num_sb_undef_in1_only ) { for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { k = icr2->sb_undef_in1_only[j]; if ( v1 == pStereo2Revrs->nBondAtom1[k] && v2 == pStereo2Revrs->nBondAtom2[k] ) { break; } } if ( j == icr->num_sb_in1_only ) { continue; /* extra stereobond only in Fixed-H, not in Mobile H also */ } } } /* Fix all charges except negative charges on tautomeric endpoints */ if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { for ( j = 0; j < pStruct->num_atoms; j ++ ) { if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { int bMayBeUnfixed = !at2[j].num_H && !(pStruct->endpoint && pStruct->endpoint[j]); if ( bMayBeUnfixed && pVA[j].cNumValenceElectrons == 6 || pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber > 1 ) { /* O & P */ if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { goto exit_function; } } else if ( bMayBeUnfixed && pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 ) { /* N */ if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { goto exit_function; } } else { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } } } } } if ( !pe->flow ) continue; /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { /* fix positive charges on heteroatoms */ num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; if ( k == 2 ) { /* fix charges on O */ SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); num_endpoints -= (TautMinusEdges+0)->num_edges; } if ( k == 1 ) { /* fix charges on N */ SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); num_endpoints -= (TautMinusEdges+1)->num_edges; } if ( num_endpoints >= 2 ) { delta = 1; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ pe->flow -= delta; /* decrement stereobond order */ pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { /* Negative charge has been moved, no change in number of charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; /* 01 */ bDone = 1; } } else { pe->forbidden &= ~forbidden_edge_mask; pe->flow += delta; pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } } /* unfix tautomeric charges */ if ( k == 2 ) RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); if ( k == 1 ) RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); } /*exit_case_04:*/ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } } cur_success = 0; if ( pStruct->bMobileH == TAUT_YES && (cmpInChI & IDIF_SB_EXTRA_UNDF && !pStruct->ti.num_t_groups) /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { /*----------------------------------------------------------*/ /* case 05: extra stereogenic bond on =NH2(+), (B, Mobile-H)*/ /* H H */ /* original: N(+)=-N< -> N--==N/ */ /* (A) H */ /* double bond is marked as */ /* not stereogenic due to */ /* its change during proton */ /* removal => No Stereo bond */ /* (=NH may be tautomeric) */ /* */ /* H H */ /* original: N=-N(+)< -> N--==N/ */ /* (B) H */ /* double bond was not */ /* changed during proton */ /* In Fixed-H this bond removal => Undef Stereo */ /* may not be stereogenic (=NH is not tautomeric) */ /* (a) due to (+) movement */ /* (b) due to symmetry (2H), even if isotopic */ /* */ /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ /* respectively */ /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ /* to eliminate Undef stereo */ /* Move charge from N to -NH2 to create */ /* Undef Stereo */ /* Since this bond parity is not known, it is UNDEFINED */ /* */ /* Solution: Add H(+) to =NH and move charge to -N= */ /* */ /*----------------------------------------------------------*/ int aN, aC, i1, i2, vPlusMinus; AllChargeEdges.num_edges = 0; /* in1 => in restored structure; in2 => in original InChI */ for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { j12 = icr->sb_undef_in1_only[i]; pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); /* indicators of -NH: */ i1 = at2[v1].valence == 1 && at2[v1].num_H == 1 && !at2[v1].endpoint && pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; i2 = at2[v2].valence == 1 && at2[v2].num_H == 1 && !at2[v2].endpoint && pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; if ( !i1 && !i2 || i1 && i2 ) { continue; } /* find the edge between v1 and v2 */ for ( k = 0; k < at2[v1].valence; k ++ ) { pe = pBNS->edge + (e = pv1->iedge[k]); if ( v2 == (pe->neighbor12 ^ v1) ) break; /* the edge has been found */ } if ( k == at2[v1].valence ) { ret = RI_ERR_SYNTAX; goto exit_function; } if ( pe->flow != 1 ) { continue; /* already charged */ } aN = i1? v1 : v2; /* -NH atom */ aC = i1? v2 : v1; /* neighbor */ /* Replace =NH with -NH2 Create such a charge on some -N< that may be moved to NH2 to remove H(+): transformation: from: HN=C-=-N=(+vert)-Y=(+super)-(+/-) to: 2HN-C*-=-N=(+vert)-Y=(+super)-(+/-)* Run BNS to obtain: 2HN-C=-=N(+)-(+vert)=Y-(+super)=(+/-) */ vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 0 ); if ( NO_VERTEX == vPlusMinus ) { break; /* cannot do anything */ } /* increase edges to -Y-(+/-)-Y- capacities */ delta = 1; for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { i2 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; for ( k = 0; k < pBNS->vert[i2].num_adj_edges; k ++ ) { pBNS->edge[pBNS->vert[i2].iedge[k]].cap += delta; } } /* Fix all charges except (+) on -N< */ if ( !AllChargeEdges.num_edges ) { for ( j = 0; j < pStruct->num_atoms; j ++ ) { if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && !at2[j].num_H && at2[j].valence == 3 && !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { ; /* do not fix -N< or =N(+)< */ } else { /* all others */ if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { goto exit_function; } } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } } } } } /* Make bond to =NH single, add radical to aC */ pe->flow -= delta; /* make single bond */ pBNS->vert[aN].st_edge.flow -= delta; pBNS->vert[aN].st_edge.cap -= delta; /* avoid radical on N */ pBNS->vert[aC].st_edge.flow -= delta; /* create radical on C */ pBNS->vert[vPlusMinus].st_edge.cap += delta; /* create radical on (+/-) */ pBNS->tot_st_flow -= 2*delta; /* fix C-NH bond */ if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { goto exit_function; } /* pBNS->tot_st_cap is unchanged */ /* find all aC edges except pe to fix them */ /* 2. Check whether it would work and do if it would */ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ pe->cap ++; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == vPlusMinus && vPathStart == aC || vPathEnd == aC && vPathStart == vPlusMinus) && nDeltaCharge == 1 ) { /* Negative charge has been moved, no change in number of charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; /* 3. Add H to -NH and register increaded charge */ pStruct->at[aN].num_H ++; pTCGroups->total_charge ++; cur_success ++; /* 01 */ } } else { pe->flow += delta; /* make single bond */ pBNS->vert[aN].st_edge.flow += delta; pBNS->vert[aN].st_edge.cap += delta; /* avoid radical on N */ pBNS->vert[aC].st_edge.flow += delta; /* create radical on C */ pBNS->vert[vPlusMinus].st_edge.cap -= delta; /* create radical on (+/-) */ pBNS->tot_st_flow += 2*delta; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ AllChargeEdges.num_edges --; /* remove pe from the list */ CurrEdges.num_edges = 0; continue; /* should not happen */ } RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ AllChargeEdges.num_edges --; /* remove pe from the list */ CurrEdges.num_edges = 0; } /*exit_case_05:*/ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } } cur_success = 0; if ( pStruct->bMobileH == TAUT_NON && pStereo2Revrs /* added check 2006-04-05 */ && (cmpInChI2 & IDIF_SB_EXTRA_UNDF && !pStruct->ti.num_t_groups) /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { /*----------------------------------------------------------*/ /* case 06: extra stereogenic bond on =NH2(+), (B, Fixed-H) */ /* H H =========== */ /* original: N(+)=-N< -> N--==N(+)< */ /* (A) H H */ /* double bond in Mobile-H */ /* layer has Undef stereo */ /* */ /* */ /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ /* respectively */ /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ /* to eliminate Undef stereo */ /* Move charge from N to -NH2 to create */ /* Undef Stereo */ /* Since this bond parity is not known, it is UNDEFINED */ /* */ /* Solution: Move (+) from -NH2(+) to othe -N< */ /* */ /*----------------------------------------------------------*/ int aN, aC, i1, i2, ePlus; BNS_EDGE *pePlus; AllChargeEdges.num_edges = 0; /* in1 => in restored structure; in2 => in original InChI */ for ( i = 0; i < icr2->num_sb_undef_in1_only; i ++ ) { j12 = icr2->sb_undef_in1_only[i]; pv1 = pBNS->vert + (v1 = pStereo2Revrs->nBondAtom1[j12]-1); pv2 = pBNS->vert + (v2 = pStereo2Revrs->nBondAtom2[j12]-1); /* indicators of -NH: */ i1 = at2[v1].valence == 1 && at2[v1].num_H == 2 && !at2[v1].endpoint && pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; i2 = at2[v2].valence == 1 && at2[v2].num_H == 2 && !at2[v2].endpoint && pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; if ( !i1 && !i2 || i1 && i2 ) { continue; } /* find the edge between v1 and v2 */ for ( k = 0; k < at2[v1].valence; k ++ ) { pe = pBNS->edge + (e = pv1->iedge[k]); if ( v2 == (pe->neighbor12 ^ v1) ) break; /* the edge has been found */ } if ( k == at2[v1].valence ) { ret = RI_ERR_SYNTAX; goto exit_function; } if ( pe->flow != 1 ) { continue; /* already charged */ } aN = i1? v1 : v2; /* -NH atom */ aC = i1? v2 : v1; /* neighbor */ if ( 0 > (ePlus = pVA[aN].nCPlusGroupEdge-1) || (pePlus = pBNS->edge + ePlus)->flow || /* must be (+) charged */ pePlus->forbidden ) { continue; } /* Move (+) from =NH2(+) to some other -N< */ /* Fix all charges except (+) on -N< */ if ( !AllChargeEdges.num_edges ) { for ( j = 0; j < pStruct->num_atoms; j ++ ) { if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && !at2[j].num_H && at2[j].valence == 3 && !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { ; /* do not fix -N< or =N(+)< */ } else { /* all others */ if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { goto exit_function; } } /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { goto exit_function; } } } } } } /* pePlus edge is already fixed; unfix it */ /* To decrement (+) on =NH2(+) decrement its double bond order */ delta = 1; if ( !pe->flow ) continue; pv1 = pBNS->vert + (v1 = pe->neighbor1); pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); delta = 1; pe->flow -= delta; pv1->st_edge.flow -= delta; pv2->st_edge.flow -= delta; pBNS->tot_st_flow -= 2*delta; pe->forbidden |= forbidden_edge_mask; pePlus->forbidden &= ~forbidden_edge_mask; ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { /* (+)charge was just moved, no change in number of charges */ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); if ( ret > 0 ) { (*pnNumRunBNS) ++; cur_success ++; /* 01 */ } } else { pe->flow += delta; /* roll back */ pv1->st_edge.flow += delta; pv2->st_edge.flow += delta; pBNS->tot_st_flow += 2*delta; } pe->forbidden &= ~forbidden_edge_mask; RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ } /*exit_case_06:*/ if ( cur_success ) { tot_succes += cur_success; /* recalculate InChI from the structure */ if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, ppt_group_info, ppat_norm, ppat_prep ) ) ) { goto exit_function; } if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { goto exit_function; } /* if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { goto exit_function; } */ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } cmpInChI2 = 0; memset ( icr2, 0, sizeof(*icr2) ); if ( iRevrInChI || iOrigInChI ) { /* additional mobile-H compare in case of Fixed-H */ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); if ( cmpInChI & IDIF_PROBLEM ) { ret = RI_ERR_PROGR; /* severe restore problem */ goto exit_function; } if ( err ) { ret = RI_ERR_ALLOC; goto exit_function; } } pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? NULL: (pStruct->pOneINChI[1]->StereoIsotopic && pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? pStruct->pOneINChI[1]->StereoIsotopic : pStruct->pOneINChI[1]->Stereo; } } exit_function: SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); AllocEdgeList( &FixedStereoEdges, EDGE_LIST_FREE ); AllocEdgeList( &AllRadList, EDGE_LIST_FREE ); /* eliminate memory leak */ AllocEdgeList( TautMinusEdges+0, EDGE_LIST_FREE ); AllocEdgeList( TautMinusEdges+1, EDGE_LIST_FREE ); return ret; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvr7.c000066400000000000000000003366541271037650300234730ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include /*^^^ */ /* #define CHECK_WIN32_VC_HEAP */ #include "mode.h" #if ( READ_INCHI_STRING == 1 ) #include "ichicomp.h" #include "ichi.h" #include "ichitime.h" #include "ichierr.h" #include "util.h" #include "strutil.h" /* reverse InChI */ #include "ichimain.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichister.h" #include "strutil.h" #include "ichisize.h" #include "ichiring.h" #include "ichinorm.h" #include "ichirvrs.h" #include "inchicmp.h" /******************************************************************************************************/ int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *OneInput) { int iINChI = (bI2A_Flag & I2A_FLAG_RECMET)? INCHI_REC : INCHI_BAS; int bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH)? TAUT_NON : TAUT_YES; INChI *pInChI[TAUT_NUM]; int ret = 0; memset( pInChI, 0, sizeof(pInChI) ); /* disconnected or reconnected */ if ( iINChI == INCHI_REC ) { if ( !OneInput->nNumComponents[iINChI][TAUT_YES] ) { iINChI = INCHI_BAS; } } if ( iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES] ) { return 0; /* component does not exist */ } /* mobile or fixed H */ pStruct->bFixedHExists = 0; if ( bMobileH == TAUT_NON ) { if ( !OneInput->nNumComponents[iINChI][bMobileH] ) { /* only one InChI exists (no mobile H) */ bMobileH = TAUT_YES; } } if ( iComponent >= OneInput->nNumComponents[iINChI][bMobileH] ) { return 0; /* component does not exist */ } /* pointer to the InChI that is going to be reversed */ pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent]; pStruct->bMobileH = bMobileH; pStruct->iINCHI = iINChI; /* deleted component only in case Mobile-H and compound contains only protons */ if ( pInChI[0]->bDeleted ) { return 0; /* deleted component, presumably H(+) */ } if ( bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { pStruct->nNumRemovedProtonsMobHInChI = OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons; } if ( bMobileH == TAUT_NON || bMobileH == TAUT_YES && OneInput->pInpInChI[iINChI][TAUT_NON] && OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 && !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted ) { pStruct->bFixedHExists = 1; } if ( bMobileH == TAUT_NON && iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] && OneInput->pInpInChI[iINChI][TAUT_YES] && OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 && !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted ) { /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */ pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent]; } pStruct->num_inp_actual = OneInput->num_inp; ret = OneInChI2Atom( ip, sd, szCurHdr, num_inp, pStruct, iComponent, iAtNoOffset, bHasSomeFixedH, pInChI); return ret; /* same interpretation as in ProcessOneStructure ??? */ } /*******************************************************************/ void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ) { int iInchiRec, cur_num_comp, k; /* eliminate Fixed-H InChI that are exactly came as the corresponding Mobile-H structures */ for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES], pOneInput->nNumComponents[iInchiRec][TAUT_NON]); for ( k = 0; k < cur_num_comp; k ++ ) { if ( !CompareReversedINChI( pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k, pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, NULL, NULL ) ) { Free_INChI_Members( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k ); memset( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, 0, sizeof(pOneInput->pInpInChI[0][0][0]) ); } } } } /*******************************************************************/ int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ) { /* mark Disconnected InChI components that are exactly came as Reconnected ones */ /* Disconnected will have a negative number of the reconnected component */ /* Reconnected will have a positive number of the disconnected component */ int k1, k2, num_marked = 0; for ( k1 = 0; k1 < inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]); k1 ++ ) { for ( k2 = 0; k2 < inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES], pOneInput->nNumComponents[INCHI_REC][TAUT_NON]); k2 ++ ) { int eqM = ( k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] && k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink && /* already linked */ !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted && pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms && pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms == pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms && !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted && !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k2, pOneInput->pInpInChI[INCHI_BAS][TAUT_YES]+k1, NULL, NULL )); int isF1 = (k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] && 0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted && 0 < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms ); int isF2 = (k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON] && 0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted && 0 < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms ); int eqF = isF1 && isF2 && !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink && pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms && !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_NON]+k2, pOneInput->pInpInChI[INCHI_BAS][TAUT_NON]+k1, NULL, NULL ); if ( eqM && (!isF1 && !isF2 || eqF ) ) { pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2+1); pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink = (k1+1); if ( eqF ) { pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2+1); pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink = (k1+1); } num_marked ++; break; /* equal InChI has been deleted from the disconnected layer, get next k1 */ } } } return num_marked; } /**************************************************************/ void SetUpSrm( SRM *pSrm ) { /* structure restore parms !!!!! */ memset( pSrm, 0, sizeof(pSrm[0]) ); pSrm->bFixStereoBonds = FIX_STEREO_BOND_ORDER; pSrm->nMetal2EndpointMinBondOrder = 1; pSrm->nMetal2EndpointInitEdgeFlow = 0; if ( METAL_FREE_CHARGE_VAL == 1 ) { pSrm->bMetalAddFlower = 1; /* the next 3 parameters: */ /* 0, 0, 0 => all bonds 0, no init radical on metal */ /* 0, 0, 1 => all bonds 0, init radical on metal */ /* 0, 1, 0 => wrong */ /* 0, 1, 1 => all bonds 1, no init radical on metal */ /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */ /* 1, 1, 0 => wrong */ /* 1, 1, 1 => wrong */ pSrm->nMetalMinBondOrder = 0; pSrm->nMetalInitEdgeFlow = 1; pSrm->nMetalInitBondOrder = 1; pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; pSrm->nMetalFlowerParam_D = 16; pSrm->nMetalMaxCharge_D = 16; } else { pSrm->bMetalAddFlower = 0; pSrm->nMetalMinBondOrder = 1; pSrm->nMetalInitEdgeFlow = 0; pSrm->nMetalInitBondOrder = 1; pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; pSrm->nMetalFlowerParam_D = 16; pSrm->nMetalMaxCharge_D = 0; } /* pSrm->nMetalInitBondOrder = pSrm->nMetalMinBondOrder + pSrm->nMetalInitEdgeFlow; */ pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder + pSrm->nMetal2EndpointInitEdgeFlow; } /**************************************************************************************/ int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ) { int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; int *nAtomOffs=NULL, *nDelHOffs=NULL; StrFromINChI *pStruct1; inp_ATOM *at=NULL, *a; ret = 0; pOneInput->num_atoms = 0; /* select highest detail level */ if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { iInchiRec = INCHI_REC; iMobileH = TAUT_NON; } else if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { iInchiRec = INCHI_REC; iMobileH = TAUT_YES; } else if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { iInchiRec = INCHI_BAS; iMobileH = TAUT_NON; } else if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { iInchiRec = INCHI_BAS; iMobileH = TAUT_YES; } else { return 0; /* no components available */ } nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); if ( !nAtomOffs || !nDelHOffs ) { ret = RI_ERR_ALLOC; goto exit_function; } /* count number of atoms and removed H */ tot_just_atoms = tot_removed_H = tot_atoms = 0; iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; nAtomOffs[0] = nDelHOffs[0] = 0; for ( k = 0; k < num_components; k ++ ) { pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : iAlternH>=0 && pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted ) { cur_nA = cur_nH = 0; } else { cur_nA = pStruct1->num_atoms; cur_nH = pStruct1->num_deleted_H; } nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; } tot_just_atoms = nAtomOffs[num_components]; /* shift all H to the end */ for ( k = 0; k <= num_components; k ++ ) { nDelHOffs[k] += tot_just_atoms; } tot_atoms = nDelHOffs[num_components]; /* merge atoms together: 1. Allocate */ if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { ret = RI_ERR_ALLOC; goto exit_function; } if ( !tot_atoms ) { ret = 0; goto exit_function; /* empty structure */ } /* merge atoms together: 2. Copy */ for ( k = 0; k < num_components; k ++ ) { pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : iAlternH>=0 && pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { memcpy( at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]) ); if ( len2 = nDelHOffs[k+1] - nDelHOffs[k] ) { memcpy( at + nDelHOffs[k], pStruct1->at2+len, len2 * sizeof(at[0]) ); } } } /* merge atoms together: 3. Update atom numbers */ icomp = 0; for ( k = 0; k < num_components; k ++ ) { iCurAtomOffs = nAtomOffs[k]; iNxtAtomOffs = nAtomOffs[k+1]; iCurDelHOffs = nDelHOffs[k]; iNxtDelHOffs = nDelHOffs[k+1]; len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ iShiftH = iCurDelHOffs - len; if ( !len ) { continue; } icomp ++; /* current component number */ /* update atoms */ for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { a = at+i; a->endpoint = 0; a->bAmbiguousStereo = 0; a->at_type = 0; a->bCutVertex = 0; a->bUsed0DParity = 0; a->cFlags = 0; a->nBlockSystem = 0; a->nNumAtInRingSystem = 0; a->nRingSystem = 0; for ( j = 0; j < a->valence; j ++ ) { if ( a->neighbor[j] < len ) { a->neighbor[j] += iCurAtomOffs; /* atom */ } else { a->neighbor[j] += iShiftH; /* explicit H */ } } a->orig_at_number += iCurAtomOffs; a->component = icomp; if ( a->p_parity ) { for ( j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { if ( a->p_orig_at_num[j] <= len ) { /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ a->p_orig_at_num[j] += iCurAtomOffs; } else { a->p_orig_at_num[j] += iShiftH; } } } for ( j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j ++ ) { if ( a->sn_orig_at_num[j] <= len ) { /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ a->sn_orig_at_num[j] += iCurAtomOffs; } else { a->sn_orig_at_num[j] += iShiftH; } } } /* update fixed-H */ for ( i = iCurDelHOffs; i < iNxtDelHOffs; i ++ ) { a = at+i; a->neighbor[0] += iCurAtomOffs; a->orig_at_number += iShiftH; } } /* save the results */ pOneInput->atom = at; pOneInput->num_atoms = tot_atoms; at = NULL; exit_function: if ( at ) inchi_free( at ); /* in case of failure */ if ( nAtomOffs ) inchi_free( nAtomOffs ); if ( nDelHOffs ) inchi_free( nDelHOffs ); return ret; } #ifndef COMPILE_ANSI_ONLY static PER_DRAW_PARMS pdp; /******************************************************************************************************/ int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ) { int ret; char szTitle[512]; DRAW_PARMS dp; TBL_DRAW_PARMS tdp; if ( num_at <= 0 ) { return 0; } memset( &dp, 0, sizeof(dp)); memset( &tdp, 0, sizeof(tdp) ); //memset( &pdp, 0, sizeof(pdp) ); dp.sdp.tdp = &tdp; dp.pdp = &pdp; dp.sdp.nFontSize = -9; sprintf( szTitle, "All Components of Restored %s Structure", szCurHdr? szCurHdr : "(No structure name)"); ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, 1 /*int bIsotopic*/, 0 /*bTautomeric*/, NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/, 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); return 0; } /******************************************************************************************************/ int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, int iComponent, int nNumComponents, int bMobileH, const char *szCurHdr ) { int ret, k; int num_at = pStruct->num_atoms; XYZ_COORD *pxyz = pStruct->pXYZ; char szTitle[512]; DRAW_PARMS dp; TBL_DRAW_PARMS tdp; int iInchiRec = pStruct->iInchiRec; int iMobileH = pStruct->iMobileH; INChI **pInChI = NULL; INChI_Aux **pAux = NULL; int nNumRemovedProtons = pAux? pAux[iMobileH]->nNumRemovedProtons : 0; NUM_H *nNumRemovedProtonsIsotopic = pAux? pAux[iMobileH]->nNumRemovedIsotopicH : NULL; if ( num_at <= 0 || !pxyz ) { return 0; } if ( iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0] ) { iInchiRec = 0; } k = iMobileH; if ( !bRevInchiComponentExists( pStruct, iInchiRec, k, 0 ) ) { k = ALT_TAUT(k); } pInChI = pStruct->RevInChI.pINChI[iInchiRec][0]; pAux = pStruct->RevInChI.pINChI_Aux[iInchiRec][0]; memset( &dp, 0, sizeof(dp)); memset( &tdp, 0, sizeof(tdp) ); //memset( &pdp, 0, sizeof(pdp) ); dp.sdp.tdp = &tdp; dp.pdp = &pdp; dp.sdp.nFontSize = -9; sprintf( szTitle, "Restored %s Component %d of %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, nNumComponents, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic, 1 /*int bIsotopic*/, k, pInChI, pAux, 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); return 0; } /******************************************************************************************************/ int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ) { int i, ret; int num_at = pStruct->num_atoms; int num_deleted_H = pStruct->num_deleted_H; inp_ATOM *atom = pStruct->at2; XYZ_COORD *pxyz = pStruct->pXYZ; inp_ATOM *at = NULL; char szTitle[512]; DRAW_PARMS dp; TBL_DRAW_PARMS tdp; if ( !atom || num_at <= 0 || !pxyz ) { return 0; } at = (inp_ATOM *)inchi_calloc( num_at + num_deleted_H, sizeof(at[0]) ); if ( !at ) { return RI_ERR_ALLOC; } memcpy( at, atom, (num_at + num_deleted_H) * sizeof(at[0]) ); for ( i = 0; i < num_at; i ++ ) { at[i].x = pxyz[i].xyz[0]; at[i].y = pxyz[i].xyz[1]; at[i].z = pxyz[i].xyz[2]; } memset( &dp, 0, sizeof(dp)); memset( &tdp, 0, sizeof(tdp) ); //memset( &pdp, 0, sizeof(pdp) ); dp.sdp.tdp = &tdp; dp.pdp = &pdp; dp.sdp.nFontSize = -9; sprintf( szTitle, "DBG Restored %s Component %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, 1 /*int bIsotopic*/, 0 /*bTautomeric*/, &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/, 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); inchi_free( at ); return 0; } /**************************************************************************************/ int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ) { int iInchiRec, iMobileH, iCurMobH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; int *nAtomOffs=NULL, *nDelHOffs=NULL, bNoCoord=0, iNewCoord=0, nNewCoord=0; double x_max=-1.0e16, x_min = 1.0e16, y_max=-1.0e16, y_min=1.0e16, delta = 0.0; StrFromINChI *pStruct1; inp_ATOM *at=NULL, *a; if (!ip->bDisplayCompositeResults && !ip->bDisplay ) { return 0; } ret = 0; pOneInput->num_atoms = 0; /* select highest detail level */ if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { iInchiRec = INCHI_REC; iMobileH = TAUT_NON; } else if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { iInchiRec = INCHI_REC; iMobileH = TAUT_YES; } else if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { iInchiRec = INCHI_BAS; iMobileH = TAUT_NON; } else if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { iInchiRec = INCHI_BAS; iMobileH = TAUT_YES; } else { return 0; /* no components available */ } for ( k = 0; k < num_components; k ++ ) { if ( pStruct[iInchiRec][iMobileH][k].bDeleted ) break; } num_components = k; nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); if ( !nAtomOffs || !nDelHOffs ) { ret = RI_ERR_ALLOC; goto exit_function; } /* count number of atoms and removed H */ tot_just_atoms = tot_removed_H = tot_atoms = 0; iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; nAtomOffs[0] = nDelHOffs[0] = 0; for ( k = 0; k < num_components; k ++ ) { pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : iAlternH>=0 && pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms ) { cur_nA = cur_nH = 0; } else { cur_nA = pStruct1->num_atoms; cur_nH = pStruct1->num_deleted_H; if ( cur_nA && !pStruct1->pXYZ ) { if ( !k ) { ret = 0; /* no coordinates available */ goto exit_function; } else { bNoCoord ++; } } } nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; } tot_just_atoms = nAtomOffs[num_components]; /* shift all H to the end */ for ( k = 0; k <= num_components; k ++ ) { nDelHOffs[k] += tot_just_atoms; } tot_atoms = nDelHOffs[num_components]; /* merge atoms together: 1. Allocate */ if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { ret = RI_ERR_ALLOC; goto exit_function; } if ( !tot_atoms ) { ret = 0; goto exit_function; /* empty structure */ } /* merge atoms together: 2. Copy */ for ( k = 0; k < num_components; k ++ ) { pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iMobileH]+k : iAlternH>=0 && pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iAlternH]+k : NULL; if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { XYZ_COORD *pxyz = pStruct1->pXYZ; len2 = nDelHOffs[k+1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */ iCurAtomOffs = nAtomOffs[k]; a = at + iCurAtomOffs; memcpy( a, pStruct1->at2, (len+len2) * sizeof(at[0]) ); DisconnectedConnectedH( a, len, len2 ); if ( pxyz ) { for ( i = 0; i < len; i ++ ) { a[i].x = pxyz[i].xyz[0]; x_max = inchi_max( x_max, pxyz[i].xyz[0] ); x_min = inchi_min( x_min, pxyz[i].xyz[0] ); a[i].y = pxyz[i].xyz[1]; y_max = inchi_max( y_max, pxyz[i].xyz[1] ); y_min = inchi_min( y_min, pxyz[i].xyz[1] ); a[i].z = pxyz[i].xyz[2]; nNewCoord ++; } } else { if ( !iNewCoord ) { if ( !nNewCoord ) { ret = 0; goto exit_function; /* empty structure */ } delta = inchi_max(x_max - x_min, y_max - y_min); if ( delta == 0.0 ) { delta = 0.5 * (x_max+x_min); if ( delta == 0.0 ) delta = 1.0; } else { delta /= sqrt( (double)(nNewCoord+1) ); } } for ( i = 0; i < len; i ++ ) { a[i].x = x_max + delta; a[i].y = y_max - iNewCoord * delta; a[i].z = 0.0; iNewCoord ++; } if ( pStruct1->pXYZ = (XYZ_COORD *)inchi_calloc(len, sizeof(pStruct1->pXYZ[0]) ) ) { for ( i = 0; i < len; i ++ ) { pStruct1->pXYZ[i].xyz[0] = a[i].x; pStruct1->pXYZ[i].xyz[1] = a[i].y; pStruct1->pXYZ[i].xyz[2] = 0.0; } } } if ( ip->bDisplay || ip->bDisplayCompositeResults && 1 == num_components ) { DisplayOneRestoredComponent( pStruct1, a, k, num_components, iCurMobH, szCurHdr ); } if ( !pxyz && pStruct1->pXYZ ) { inchi_free( pStruct1->pXYZ ); pStruct1->pXYZ = NULL; } } } /* merge atoms together: 3. Update atom numbers */ icomp = 0; if ( ip->bDisplayCompositeResults && num_components > 1 ) { for ( k = 0; k < num_components; k ++ ) { /* display each restored component if requested */ iCurAtomOffs = nAtomOffs[k]; iNxtAtomOffs = nAtomOffs[k+1]; iCurDelHOffs = nDelHOffs[k]; iNxtDelHOffs = nDelHOffs[k+1]; len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ iShiftH = iCurDelHOffs - len; if ( !len ) { continue; } icomp ++; /* current component number */ /* update atoms */ for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { a = at+i; for ( j = 0; j < a->valence; j ++ ) { if ( a->neighbor[j] < len ) { a->neighbor[j] += iCurAtomOffs; /* atom */ } else { ret = RI_ERR_PROGR; /* explicit H */ goto exit_function; } } a->orig_at_number += iCurAtomOffs; } } tot_atoms = nAtomOffs[num_components]; DisplayAllRestoredComponents( at, tot_atoms, szCurHdr ); } exit_function: if ( at ) inchi_free( at ); /* in case of failure */ if ( nAtomOffs ) inchi_free( nAtomOffs ); if ( nDelHOffs ) inchi_free( nDelHOffs ); return ret; } #endif /**************************************************************************************/ int AllInchiToStructure( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bHasSomeFixedH, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ) { int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err; INPUT_PARMS *ip, ip_loc; STRUCT_DATA *sd, sd_loc; long ulProcessingTime = 0; inchiTime ulTStart; InchiTimeGet( &ulTStart ); ip = &ip_loc; *ip = *ip_inp; sd = &sd_loc; memset( sd, 0, sizeof(*sd)); sd->ulStructTime = sd_inp->ulStructTime; ret = 0; num_err = 0; for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { /* Disconnected/Connected */ for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { /* Mobile/Fixed H */ cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH]; if ( !cur_num_comp ) { continue; } /* allocate memory for all existing components */ pStruct[iInchiRec][iMobileH] = (StrFromINChI *)inchi_calloc( cur_num_comp, sizeof(pStruct[0][0][0])); if ( !pStruct[iInchiRec][iMobileH] ) { ret = RI_ERR_ALLOC; goto exit_error; } /* set conversion mode */ bCurI2A_Flag = (iMobileH? 0: I2A_FLAG_FIXEDH) | (iInchiRec? I2A_FLAG_RECMET : 0); if ( iMobileH ) { ip->nMode &= ~REQ_MODE_BASIC; } else { ip->nMode |= REQ_MODE_BASIC; } /* InChI --> structure conversion for all components except duplicated */ for ( k = 0; k < cur_num_comp; k ++ ) { /* components */ if ( !iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms || pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted || pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0 ) { pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted; continue; /* do not create a structure out of an unavailable Fixed-H InChI or out of the one present in Reconnected layer */ #ifdef NEVER /* a wrong attempt to process deleted components here */ if ( pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink ) { continue; /* do not create a structure out of an unavailable Fixed-H InChI or out of the one present in Reconnected layer */ } else if ( iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms && pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted && pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted ) { /* all components are protons */ ; } else { continue; } #endif } if ( bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] && pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms ) { continue; /* do not process Mobile-H if Fixed-H is requested and exists */ } pStruct[iInchiRec][iMobileH][k].pSrm = pSrm; pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec; pStruct[iInchiRec][iMobileH][k].iMobileH = iMobileH; /****************************************************/ /* */ /* Convert InChI of one component into a Structure */ /* */ /****************************************************/ ret = InChI2Atom( ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH]+k, k, 0 /* AtNoOffset*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput ); pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; if ( ret < 0 ) { #if ( bRELEASE_VERSION != 1 ) #ifndef TARGET_API_LIB /* !!! Conversion Error -- Ignore for now !!! */ fprintf( stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n", num_inp, szCurHdr? szCurHdr : "Struct", ret, iInchiRec? 'R':'D', iMobileH? 'M':'F', k+1); #endif #endif if ( ret == CT_USER_QUIT_ERR ) { goto exit_error; } pStruct[iInchiRec][iMobileH][k].nError = ret; ret = 0; /* force to ignore the errors for now !!!! */ num_err ++; } } } } exit_error: ulProcessingTime += InchiTimeElapsed( &ulTStart ); sd->ulStructTime += ulProcessingTime; return ret<0? ret : num_err; } /**************************************************************************************/ int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, char *szCurHdr, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput) { COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM]; int nRemovedByNormFromRevrs[INCHI_NUM]; int nRemovedByRevrs[INCHI_NUM]; int nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs, num_changes = 0; NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES]; int iInchiRec, i, k, k1, ret = 0; int nChargeInChI, nChargeRevrs; if ( bHasSomeFixedH ) { return 0; /* 2005-03-01 */ } /* num protons removed by InChI Normalization from the original structure */ for ( i = 0; i < INCHI_NUM; i ++ ) { nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons; for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k]; } } /* accumulate here num. protons removed by the normalization from the reversed structure */ nRemovedByNormFromRevrs[INCHI_BAS] = nRemovedByNormFromRevrs[INCHI_REC] = 0; nRemovedByRevrs[INCHI_REC] = nRemovedByRevrs[INCHI_BAS] = 0; /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */ for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { for ( k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k ++ ) { if ( !bInpInchiComponentExists( pOneInput, iInchiRec, TAUT_YES, k ) ) { continue; } nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */ nRemovedProtonsByRevrs = 0; /* Num protons removed by the Reconstruction from the Restored Structure */ if ( iInchiRec == INCHI_REC || iInchiRec == INCHI_BAS && (k1=pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0 ) { REV_INCHI *pRevInChI = &pStruct[iInchiRec][TAUT_YES][k].RevInChI; INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/ INChI **pINChI_Revr = pRevInChI->pINChI[iInchiRec][0]; INChI *pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k; nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; nChargeInChI = pINChI_Orig->nTotalCharge; if ( pINChI_Aux2 ) { nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; } nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs; pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs; pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI; } else if ( 0 <= ( k1 = -(1+pStruct[iInchiRec][TAUT_YES][k].nLink) ) ) { REV_INCHI *pRevInChI = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI; INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */ INChI **pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0]; INChI *pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k1; nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; nChargeInChI = pINChI_Orig->nTotalCharge; if ( pINChI_Aux2 ) { nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; } /* this component cannot be disconnected because it is same as in reconnected layer */ nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs; pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs; pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI; } /* how many protons (to be removed by InChI Normalization) to add = (proton balance in InChI} - {number of protons known to be removed by InChI Normalization from Reconstructed structure} */ nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs; nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs; nRemovedByRevrs[iInchiRec] += nRemovedProtonsByRevrs; pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs; } } /* Since fixed-H layer is missing we need to add proton balance to the components */ memset( nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected) ); for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { /* if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { */ int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ if ( iInchiRec==INCHI_BAS ) { /* second pass: common structures have been changed */ nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected; } /* after proton removal InChI is recalculated */ ret = AddRemProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], pOneInput->nNumComponents[iInchiRec][TAUT_YES], bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons, (iInchiRec==INCHI_REC)?&nDeltaFromDisconnected : NULL); if ( ret < 0 ) { goto exit_function; } num_changes += ret; /* } */ } /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */ for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { /* if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { */ int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ if ( iInchiRec==INCHI_BAS ) { /* second pass: common structures have been changed */ for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k]; } } /* after proton removal InChI is recalculated */ ret = AddRemIsoProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], pOneInput->nNumComponents[iInchiRec][TAUT_YES], bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH, (iInchiRec==INCHI_REC)?nIsoDeltaFromDisconnected : NULL); if ( ret < 0 ) { goto exit_function; } num_changes += ret; /* } */ } exit_function: return ret; } /*************************************************************/ void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ) { int iInchiRec, iMobileH, cur_num_comp, k, j; StrFromINChI *pStruct1; for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { cur_num_comp = nNumComponents[iInchiRec][iMobileH]; if ( !cur_num_comp || !(pStruct1=pStruct[iInchiRec][iMobileH]) ) { continue; } for ( k = 0; k < cur_num_comp; k ++ ) { if ( pStruct1[k].at ) { inchi_free(pStruct1[k].at); } if ( pStruct1[k].at2 ) { inchi_free(pStruct1[k].at2); } if ( pStruct1[k].st ) { inchi_free(pStruct1[k].st); } if ( pStruct1[k].pVA ) { inchi_free(pStruct1[k].pVA); } /* if ( pStruct1[k].ti.t_group ) { inchi_free( pStruct1[k].ti.t_group ); } */ if ( pStruct1[k].pXYZ ) { inchi_free(pStruct1[k].pXYZ); } /*==== begin ====*/ free_t_group_info( &pStruct1[k].ti ); if ( pStruct1[k].endpoint ) { inchi_free(pStruct1[k].endpoint); } if ( pStruct1[k].fixed_H ) { inchi_free(pStruct1[k].fixed_H); } for ( j = 0; j < TAUT_NUM; j ++ ) { if ( pStruct1[k].nAtno2Canon[j] ) inchi_free( pStruct1[k].nAtno2Canon[j] ); if ( pStruct1[k].nCanon2Atno[j] ) inchi_free( pStruct1[k].nCanon2Atno[j] ); } /*===== end ======*/ /* free INChI memory */ FreeAllINChIArrays( pStruct1[k].RevInChI.pINChI, pStruct1[k].RevInChI.pINChI_Aux, pStruct1[k].RevInChI.num_components ); #ifdef NEVER /* don't do that: these are just pointers to OneInput structure members */ Free_INChI( &pStruct1[k].pINChI ); Free_INChI_Aux( &pStruct1[k].pINChI_Aux ); if ( pStruct1[k].inp_norm_data ) { FreeInpAtomData( pStruct1[k].inp_norm_data ); inchi_free( pStruct1[k].inp_norm_data ); } #endif } inchi_free(pStruct[iInchiRec][iMobileH]); pStruct[iInchiRec][iMobileH] = NULL; } } } /********************************************************************/ void FreeInpInChI( InpInChI *pOneInput ) { int iINChI, k, j; for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( pOneInput->pInpInChI[iINChI][j] ) { for ( k = 0; k < pOneInput->nNumComponents[iINChI][j]; k ++ ) { Free_INChI_Members( &pOneInput->pInpInChI[iINChI][j][k] ); } inchi_free(pOneInput->pInpInChI[iINChI][j]); pOneInput->pInpInChI[iINChI][j] = NULL; } if ( pOneInput->nNumProtons[iINChI][j].pNumProtons ) { inchi_free( pOneInput->nNumProtons[iINChI][j].pNumProtons ); pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL; } } } if ( pOneInput->atom ) inchi_free(pOneInput->atom); memset( pOneInput, 0, sizeof(*pOneInput) ); } /***********************************************************************************************/ int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, long num_inp, char *szCurHdr) { int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret=0; COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons; INChI *pInChI[TAUT_NUM]; INCHI_MODE CompareInchiFlags[TAUT_NUM]; memset( pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0]) ); memset( &nNumRemovedProtons, 0, sizeof(nNumRemovedProtons) ); /* do we have reconnected InChI ?*/ iInchiRec = INCHI_REC; iMobileH = TAUT_NON; if ( !pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON] ) { iInchiRec = INCHI_BAS; } /* do we have Mobile or Fixed-H ? */ if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut ) { iMobileH = TAUT_YES; /* index for pOneInput */ } /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */ num_components = pOneInput->nNumComponents[iInchiRec][iMobileH]; for ( iComponent = 0; iComponent < num_components; iComponent ++ ) { int bMobileH = iMobileH; pInChI[0] = pInChI[1] = NULL; if ( pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms && !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted ) { /* the requested InChI layer exists */ pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; if ( bMobileH == TAUT_NON ) { pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent]; } } else if ( bMobileH == TAUT_NON && pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms && !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted ) { /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */ bMobileH = TAUT_YES; /* only Mobile-H is available */ pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; } memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); memset( &nCurRemovedProtons, 0, sizeof(nCurRemovedProtons) ); iMobileHpStruct = #if ( bRELEASE_VERSION == 0 ) #ifndef TARGET_API_LIB /* legacy: reproduce old output */ OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent, num_inp, szCurHdr); #endif #endif /* one component comparison result bits */ ret = CompareOneOrigInchiToRevInChI( pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent, num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags); if ( ret >= 0 ) { /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */ nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i]; } /* accumulate compare bits */ for ( i = 0; i < TAUT_NUM; i ++ ) { pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i]; } } else { goto exit_function; } } if ( iMobileH == TAUT_YES ) { if ( pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons ) { ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */ } else { /* num removed protons in orig. InChI num removed protons in restored InChi */ if ( nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons ) { /* restored structure InChI has less or more removed protons */ pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; #if ( bRELEASE_VERSION == 0 ) /* debug output only */ { int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons - nNumRemovedProtons.nNumRemovedProtons; fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n", num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons,num_H_AddedByRevrs); } #endif } for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { if ( nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] ) { pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H; #if ( bRELEASE_VERSION == 0 ) /* debug output only */ { int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] - nNumRemovedProtons.nNumRemovedIsotopicH[i]; fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n", num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', i+1, pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i],num_H_AddedByRevrs); } #endif } } } } exit_function: return ret; } /***********************************************************************************************/ int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bHasSomeFixedH, long num_inp, char *szCurHdr) { int i, k, m, n, iInChI, iMobileH, bMobileH, ifk; int num_components_D, num_components_R; int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R; int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret; int ifInChI, ifMobileH, bfMobileH, nLink; COMPONENT_REM_PROTONS nNumRemovedProtons_D; /* removed from the disconnected layer of the Input InChI */ COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */ COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */ COMPONENT_REM_PROTONS nNumRemovedProtons_R_all; INCHI_MODE CompareInchiFlags[TAUT_NUM]; StrFromINChI *pStruct1; INChI_Aux *pINChI_Aux; INCHI_SORT *pINChISort1 = NULL; /* from reversed structure */ INCHI_SORT *pINChISort2 = NULL; /* original input InChI */ int nNumNonTaut1=0, nNumNonTaut2=0; ret = 0; memset( pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1]) ); /* count components that are not subject to disconnection */ if ( !pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && !pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { return 0; /* nothing to do */ } memset( &nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D) ); memset( &nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R) ); memset( &nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all) ); memset( &nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all) ); memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); num_components_D = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); num_components_R = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES], pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ); /***********************************************************************************************/ /* InpInChI: count fragments -- disconnected components that do not match reconnected */ /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */ /* This segment collects info from the original InChI */ /***********************************************************************************************/ /*---- Original InChI ----*/ num_fragments_D = 0; iInChI = INCHI_BAS; iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; nNumCompHaveSeparateProtons_D = 0; /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */ nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; memcpy( nNumRemovedProtons_D.nNumRemovedIsotopicH, pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH) ); /* total for the disconnected layer */ for ( k = 0; k < num_components_D; k ++ ) { bMobileH = iMobileH; if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { bMobileH = TAUT_YES; } else { continue; /* component is missing ??? */ } } if ( 0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink) ) { /* component in Disconnected layer is linked to the identical one in the Reconnected layer */ if ( pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) { nNumCompHaveSeparateProtons_D ++; nLink = -(1+nLink); nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m]; } } continue; /* same as reconnected */ } /* component in the reconnected layer that was disconnected */ nNumNonTaut2 += (bMobileH == TAUT_NON); if ( pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ) { nNumCompHaveSeparateProtons_D ++; nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m]; } } num_fragments_D ++; /* number of disconnected fragments from original reconnected structure */ } /* in case of Mobile-H components here are the proton totals from the original InChI */ /* nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH, pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) ); */ /****************************************************************************************************/ /* count fragments in reconstructed reconnected structure */ /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */ /* This segment collects info from the reconstructed structure InChI */ /****************************************************************************************************/ /*---- InChI from the reconstructed reconnected structure ----*/ num_fragments_R = 0; iInChI = INCHI_REC; iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; nNumCompHaveSeparateProtons_R = 0; for ( k = 0; k < num_components_R; k ++ ) { bMobileH = iMobileH; if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { bMobileH = TAUT_YES; } else { continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ } } if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { /* this reconstructed reconnected component was NOT DISCONNECTED */ /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */ pStruct1 = pStruct[iInChI][bMobileH]+k; ifMobileH = TAUT_YES; /* Mobile-H Aux_Info contains number removed protons */ ifInChI = INCHI_BAS; /* this component cannot be reconnected */ ifk = 0; /* 0th component since it is InChI of a single component */ /* The statement in the following line is *WRONG*, component number mixed with bMobileH: */ /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */ if ( bRevInchiComponentExists( pStruct1, ifInChI, ifMobileH, ifk ) ) { /* count protons */ pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH]; if ( pINChI_Aux ) { nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; } } } nNumCompHaveSeparateProtons_R += bRevInchiComponentExists( pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk ); continue; /* same as disconnected, has no metal atoms */ } /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */ /* it does not have same component in the disconnected layer */ pStruct1 = pStruct[iInChI][bMobileH]+k; num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; ifInChI = INCHI_BAS; /* disconnected layer */ ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; for ( ifk = 0; ifk < num_fragments; ifk ++ ) { bfMobileH = ifMobileH; if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, ifk ) ) { if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, ifk ) ) { bfMobileH = TAUT_YES; } else { continue; /* fragment does not exist ??? */ } } nNumNonTaut1 += (bfMobileH == TAUT_NON); nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); /* count protons from fragments made by metal disconnection */ pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; if ( pINChI_Aux ) { nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; } } num_fragments_R ++; /* number of disconnected fragments from reconstructed reconnected structure */ } } /*---------------- special treatment of the last reconstructed component -----------------*/ /*---------------- this may contain separate protons added by the reconstruction ---------*/ k = num_components_R - 1; pStruct1 = pStruct[iInChI][iMobileH]+k; if ( iMobileH == TAUT_YES && !bHasSomeFixedH && bInpInchiComponentDeleted( pOneInput, iInChI, iMobileH, k ) && (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]) ) { ifInChI = INCHI_BAS; /* disconnected layer */ ifMobileH = TAUT_YES; for ( ifk = 0; ifk < num_fragments; ifk ++ ) { bfMobileH = ifMobileH; if ( !bRevInchiComponentDeleted( pStruct1, ifInChI, bfMobileH, ifk ) ) { continue; /* fragment does exist ??? Should not happen */ } /* nNumNonTaut1 += (bfMobileH == TAUT_NON); nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); */ /* count protons from fragments made by metal disconnection */ pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; if ( pINChI_Aux ) { nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; } } /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */ } } num_fragments_DR = inchi_max( num_fragments_D, num_fragments_R ); /* in case of correct reconstruction, num_fragments_D, num_fragments_R */ if ( !num_fragments_DR ) { return 0; /* no component was disconnected */ } if ( num_fragments_D != num_fragments_R ) { for ( i = 0; i < TAUT_NUM; i ++ ) { if ( pOneInput->nNumComponents[INCHI_BAS][i] ) { pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM; } } return 1; /* severe error */ } pINChISort1 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0])); pINChISort2 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0])); if ( !pINChISort1 || !pINChISort2 ) { ret = RI_ERR_ALLOC; goto exit_function; } /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */ iInChI = INCHI_BAS; iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; for ( k = n = 0; k < num_components_D; k ++ ) { bMobileH = iMobileH; if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { bMobileH = TAUT_YES; } else { continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ } } if ( 0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { continue; /* same as reconnected */ } /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */ pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k; if ( bMobileH == TAUT_NON && (bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) || bInpInchiComponentDeleted( pOneInput, iInChI, TAUT_YES, k ) ) ) { pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k; } /* the last sort key is a number of removed protons */ pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons? pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0; pINChISort2[n].n1 = k; /* orig. InChI disconnected layer component number */ pINChISort2[n].n2 = -1; /* no fragment index */ n ++; } /* accumulate fragments from the reconstructed structure */ iInChI = INCHI_REC; iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; for ( k = n = 0; k < num_components_R; k ++ ) { bMobileH = iMobileH; if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { bMobileH = TAUT_YES; } else { continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ } } /* the reconstructed structure */ if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { continue; /* same as disconnected, has no metal atoms */ } /* this reconstructed structure was disconnected */ pStruct1 = pStruct[iInChI][bMobileH]+k; num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; ifInChI = INCHI_BAS; ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; for ( i = 0; i < num_fragments; i ++ ) { bfMobileH = ifMobileH; if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { bfMobileH = TAUT_YES; } else { continue; /* component is missing ??? */ } } pINChISort1[n].pINChI[bfMobileH] = pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH]; if ( bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/ ) { pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES]; /* remove Fixed-H InChI if is is identical to Mobile-H */ /* do it exactly same way the identical components were removed from InpInChI */ if ( !CompareReversedINChI( pINChISort1[n].pINChI[bfMobileH], pINChISort1[n].pINChI[TAUT_YES], NULL, NULL ) ) { pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */ } else { pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons; } } pINChISort1[n].n1 = k; /* reconstructed reconnected structure component index */ pINChISort1[n].n2 = i; /* index of a fragment made out of this component */ n ++; } } /* sort fragment InChI before comparing them */ qsort( pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2 ); qsort( pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2 ); /* compare fragments -- components present in disconnected layer only */ for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { INChI *pInChI1[TAUT_NUM]; /* from reversed structure */ INChI *pInChI2[TAUT_NUM]; /* original input InChI */ for ( i = 0; i < TAUT_NUM; i ++ ) { pInChI1[i] = pINChISort1[iComponent].pINChI[i]; pInChI2[i] = pINChISort2[iComponent].pINChI[i]; } CompareTwoPairsOfInChI( pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags ); } if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH ) { if ( nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R ) { /* for each component, compare number removed protons */ /* comparison does not make sense if Disconnected Fixed-H layer is not present */ for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { NUM_H nNumRemovedIsotopicH1[NUM_H_ISOTOPES]; NUM_H nNumRemovedIsotopicH2[NUM_H_ISOTOPES]; memset( nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1) ); memset( nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2) ); /* compare removed protons */ if ( pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ } /* also compare removed isotopic atoms H */ k = pINChISort2[iComponent].n1; /* input InChI, OneInput */ if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons ) { memcpy( nNumRemovedIsotopicH2, pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH, sizeof( nNumRemovedIsotopicH2 ) ); } /* get fragments of reconstructed structure removed protons info */ k = pINChISort1[iComponent].n1; /* restored component number */ i = pINChISort1[iComponent].n2; /* subcomponent number */ iInChI = INCHI_REC; iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; bMobileH = iMobileH; if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { bMobileH = TAUT_YES; } else { goto compare_iso_H; } } if ( pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { continue; /* ret = RI_ERR_PROGR; goto exit_function; */ } pStruct1 = pStruct[iInChI][bMobileH]+k; num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; ifInChI = INCHI_BAS; ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; if ( i < num_fragments ) { bfMobileH = ifMobileH; if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { bfMobileH = TAUT_YES; } else { goto compare_iso_H; } } memcpy( nNumRemovedIsotopicH1, pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH, sizeof( nNumRemovedIsotopicH1 ) ); } compare_iso_H: if ( memcmp( nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof( nNumRemovedIsotopicH1 ) ) ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; } } } } else /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/ { /* compare totals for removed protons and isotopic H */ if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons != nNumRemovedProtons_R.nNumRemovedProtons ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; } if ( memcmp( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH, nNumRemovedProtons_R.nNumRemovedIsotopicH, sizeof( nNumRemovedProtons_R.nNumRemovedIsotopicH ) ) ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; } } if ( !nNumNonTaut1 == !nNumNonTaut2 ) { ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */ } else if ( nNumNonTaut1 ) { /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */ CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT; } else { /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */ CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT; } for ( i = 0; i < TAUT_NUM; i ++ ) { pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i]; } /* compare totals */ if ( nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ } if ( memcmp( nNumRemovedProtons_R.nNumRemovedIsotopicH, nNumRemovedProtons_D.nNumRemovedIsotopicH, sizeof( nNumRemovedProtons_D.nNumRemovedIsotopicH ) ) ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; } exit_function: if ( pINChISort1 ) inchi_free( pINChISort1 ); if ( pINChISort2 ) inchi_free( pINChISort2 ); return ret; } /******************************************************************************************************/ int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], int bMobileH, INCHI_MODE CompareInchiFlags[] ) { int iMobileH, err=0; INCHI_MODE cmp; for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { if ( !pInChI1[iMobileH] != !pInChI2[iMobileH] ) { if ( iMobileH == TAUT_NON && pInChI1[TAUT_YES] && pInChI1[TAUT_YES] ) { CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER; } else { CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER; } continue; } if ( pInChI1[iMobileH] && pInChI2[iMobileH] ) { cmp = CompareReversedINChI3( pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err ); if ( cmp ) { CompareInchiFlags[iMobileH] |= cmp; } } } return err; } /******************************************************************************************************/ int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, long num_inp, char *szCurHdr, COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]) { int ret = pStruct->RevInChI.nRetVal, err=0; INCHI_MODE cmp; if ( ret == _IS_OKAY || ret == _IS_WARNING ) { /* ignore bMobileH for now */ int i, i0, b /* created type */, b0 /* requested type*/, j, k; /* pINChI[iINCHI][iComponent][bTaut] */ /* i0 = requested Rec/Disconnected: 1/0 */ /* i = what InChI creaded out of the restored structure */ /* b0 = requested Mobile/Fixed-H: 1/0 */ /* b = what InChI creaded out of the restored structure */ i = i0 = pStruct->iINCHI; b = b0 = pStruct->iMobileH; if ( i == INCHI_REC && !pStruct->RevInChI.num_components[i] ) { i = INCHI_BAS; } if ( b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] || !pStruct->RevInChI.pINChI[i][0][b] || !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms ) ) { b = TAUT_YES; } if ( pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { return 0; } if ( pStruct->RevInChI.num_components[i] > 1 && !pStruct->RevInChI.pINChI[i][1][b]->bDeleted || pStruct->RevInChI.num_components[i] < 1 ) { CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER; } if ( b != b0 || b != bMobileH || b0 != bMobileH || i > i0 ) { /* do not print messages about TAUT_YES instead of TAUT_NON */ CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER; } if ( pStruct->RevInChI.num_components[i] ) { /* compare InChI from restored structure; '0' in [i][0][b] is the first component */ if ( b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { /* the 1st component is made out of proton(s) and the input component is missing or also a proton */ cmp = 0; } else { cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err ); if ( cmp ) { CompareInchiFlags[bMobileH] |= cmp; } } if ( b == b0 && b == TAUT_NON ) { if ( pStruct->RevInChI.pINChI[i][0][TAUT_YES] && !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted || pInChI[1] && !pInChI[1]->bDeleted ) { /* in addition to fixed-H also compare mobile-H InChI */ cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err ); if ( cmp ) { CompareInchiFlags[TAUT_YES] |= cmp; } } /* compare removed H */ if ( pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons ) { CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; } } memset( nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons) ); for ( k = 0; k < pStruct->RevInChI.num_components[i]; k ++ ) { if ( !k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted ) { /* get removed protons from the 1st component; add othere only if they are deleted protons */ nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons; for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j]; } } } } } else { CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR; } return err; } /*************************************************************************************/ INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) { int ret = 0; int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; if ( (nNumSc1 || nNumSc1) && ( nNumSc1 != nNumSc2 || memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { if ( s1->nNumber[j1] == s2->nNumber[j2] ) { if ( s1->t_parity[j1] == s2->t_parity[j2] ) { num_eq ++; } else { num_dif ++; } j1 ++; j2 ++; } else if ( s1->nNumber[j1] < s2->nNumber[j2] ) { num_in1_only ++; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSc ) { if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; } } j1 ++; } else { num_in2_only ++; if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSc ) { if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; } } j2 ++; } } while ( j1 < nNumSc1 ) { if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } num_in1_only ++; if ( bAddSc ) { if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; } } j1 ++; } while ( j2 < nNumSc2 ) { if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } num_in2_only ++; if ( bAddSc ) { if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; } j2 ++; } if ( num_dif ) { ret |= INCHIDIFF_SC_PARITY; } if ( num_in1_only ) { if ( num_extra_undf ) { ret |= INCHIDIFF_SC_EXTRA_UNDF; } if ( num_in1_only != num_extra_undf ) { ret |= INCHIDIFF_SC_EXTRA; } } if ( num_in2_only ) { if ( num_miss_undf ) { ret |= INCHIDIFF_SC_MISS_UNDF; } if ( num_in2_only != num_miss_undf ) { ret |= INCHIDIFF_SC_MISS; } } } if ( s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */ /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */ } if ( (nNumSb1 || nNumSb2 ) && (nNumSb1 != nNumSb2 || memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { if ( s1->b_parity[j1] == s2->b_parity[j2] ) { num_eq ++; } else { num_dif ++; } j1 ++; j2 ++; } else if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { num_in1_only ++; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; } } j1 ++; } else { num_in2_only ++; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; } } j2 ++; } } while ( j1 < nNumSb1 ) { num_in1_only ++; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { num_extra_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; } } j1 ++; } while ( j2 < nNumSb2 ) { num_in2_only ++; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { num_miss_undf ++; } if ( bAddSb ) { if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; } } j2 ++; } if ( num_dif ) { ret |= INCHIDIFF_SB_PARITY; } if ( num_in1_only ) { if ( num_extra_undf ) { ret |= INCHIDIFF_SB_EXTRA_UNDF; } if ( num_in1_only != num_extra_undf ) { ret |= INCHIDIFF_SB_EXTRA; } } if ( num_in2_only ) { if ( num_miss_undf ) { ret |= INCHIDIFF_SB_MISS_UNDF; } if ( num_in2_only != num_miss_undf ) { ret |= INCHIDIFF_SB_MISS; } } } return ret; } /*********************************************************************************************************/ INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2, int *err ) { INCHI_MODE ret = 0; INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; ICR icr; ICR *picr = &icr; *err = 0; memset( picr, 0, sizeof(*picr) ); if ( i1 == NULL && i2 == NULL ) return 0; if ( (i1 == NULL) ^ (i2 == NULL) ) { ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */ goto exit_function; } if ( i1->nErrorCode == i2->nErrorCode ) { if ( i1->nErrorCode ) { ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */ goto exit_function; } } else { ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */ goto exit_function; } if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { ret |= INCHIDIFF_NUM_AT; goto exit_function; } if ( i1->nNumberOfAtoms > 0 ) { if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { ret |= INCHIDIFF_ATOMS; goto exit_function; } /* INCHIDIFF_NON_TAUT_H, INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */ if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { ret |= INCHIDIFF_POSITION_H; for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; picr->num_diff_pos_H ++; } } } /* fixed H */ if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { int bHasFixedH1 = 0, bHasFixedH2 = 0, i; if ( i1->nNum_H_fixed ) { for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] ) { bHasFixedH1 ++; } } } if ( i2->nNum_H_fixed ) { for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { bHasFixedH2 ++; } } } if ( bHasFixedH1 && !bHasFixedH2 ) { for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] ) { if ( j < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at1_more[j] = i; picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; j ++; } } } picr->num_fixed_H1_more = j; ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */ } else if ( !bHasFixedH1 && bHasFixedH2 ) { for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { if ( i2->nNum_H_fixed[i] ) { if ( j < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at2_more[j] = i; picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; j ++; } } } picr->num_fixed_H2_more = j; ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */ } else if ( bHasFixedH1 && bHasFixedH2 && memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { if ( j1 < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at1_more[j1] = i; picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; j1 ++; } } else if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { if ( j2 < ICR_MAX_DIFF_FIXED_H ) { picr->fixed_H_at2_more[j2] = i; picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; j2 ++; } } } ret |= (j1? INCHIDIFF_MORE_FH:0) | (j2? INCHIDIFF_LESS_FH:0); picr->num_fixed_H1_more = j1; picr->num_fixed_H2_more = j2; } } } /* compare formulas and H */ num_H1 = 0; num_H2 = 0; ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); picr->tot_num_H1 = num_H1; picr->tot_num_H2 = num_H2; if ( ret2 ) { ret |= INCHIDIFF_NUM_EL; goto exit_function; } if ( num_H1 > num_H2 ) { ret |= INCHIDIFF_MORE_H; } if ( num_H1 < num_H2 ) { ret |= INCHIDIFF_LESS_H; } if ( i1->lenConnTable != i2->lenConnTable ) { ret |= INCHIDIFF_CON_LEN; goto exit_function; } else if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { ret |= INCHIDIFF_CON_TBL; goto exit_function; } /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ /* if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { ret |= INCHIDIFF_TAUT_LEN; } */ /* compare number of t-groups */ n1 = i1->lenTautomer? i1->nTautomer[0] : 0; n2 = i2->lenTautomer? i2->nTautomer[0] : 0; if ( !n1 && n2 ) { ret |= INCHIDIFF_NO_TAUT; } else if ( n1 && !n2 ) { ret |= INCHIDIFF_WRONG_TAUT; } else if ( n1 == 1 && n2 > 1 ) { ret |= INCHIDIFF_SINGLE_TG; } else if ( n1 > 1 && n2 == 1 ) { ret |= INCHIDIFF_MULTIPLE_TG; } else if ( n1 != n2 ) { ret |= INCHIDIFF_NUM_TG; } if ( n1 || n2 ) { /* number of endpoints */ int num1 = 0, num2 = 0, num_M1=0, num_M2=0; int len, num_eq, num_in1_only, num_in2_only; AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); num_H1 = num_H2=0; /* collect endpoints, H, (-) */ if ( !pe1 || !pe2 ) { if ( pe1 ) inchi_free( pe1 ); if ( pe2 ) inchi_free( pe2 ); *err = RI_ERR_ALLOC; /* allocation error */ goto exit_function; } for ( m = 1; m < i1->lenTautomer; m += len ) { len = i1->nTautomer[m ++]; num_H1 += i1->nTautomer[m]; num_M1 += i1->nTautomer[m+1]; for ( j = 2; j < len; j ++ ) { pe1[num1 ++] = i1->nTautomer[m + j]; } } for ( m = 1; m < i2->lenTautomer; m += len ) { len = i2->nTautomer[m ++]; num_H2 += i2->nTautomer[m]; num_M2 += i2->nTautomer[m+1]; for ( j = 2; j < len; j ++ ) { pe2[num2 ++] = i2->nTautomer[m + j]; } } picr->num_taut_H1 = num_H1; picr->num_taut_H2 = num_H2; picr->num_taut_M1 = num_M1; picr->num_taut_M2 = num_M2; /* sort endpoints */ insertions_sort_AT_NUMB( pe1, num1 ); insertions_sort_AT_NUMB( pe2, num2 ); /* compare */ /* if ( num1 < num2 ) { ret |= INCHIDIFF_LESS_TG_ENDP; } else if ( num1 > num2 ) { ret |= INCHIDIFF_MORE_TG_ENDP; } */ /* compare all */ num_eq = num_in1_only = num_in2_only = 0; for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { if( pe1[j1] == pe2[j2] ) { j1 ++; j2 ++; num_eq ++; } else if ( pe1[j1] < pe2[j1] ) { if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; } j1 ++; num_in1_only ++; } else { if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; } j2 ++; num_in2_only ++; } } while ( j1 < num1 ) { if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; } j1 ++; num_in1_only ++; } while ( j2 < num2 ) { if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; } j2 ++; num_in2_only ++; } if ( num_in1_only ) { ret |= INCHIDIFF_EXTRA_TG_ENDP; } if ( num_in2_only ) { ret |= INCHIDIFF_MISS_TG_ENDP; } if ( !num_in1_only && !num_in2_only && num_eq ) { ; /* same t-groups endpoints */ } else { ret |= INCHIDIFF_DIFF_TG_ENDP; } inchi_free( pe1 ); inchi_free( pe2 ); } if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && ( i1->lenTautomer != i2->lenTautomer || memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) ret |= INCHIDIFF_TG; if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { ret |= INCHIDIFF_NUM_ISO_AT; } else if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) ret |= INCHIDIFF_ISO_AT; if ( i1->nTotalCharge != i2->nTotalCharge ) ret |= INCHIDIFF_CHARGE; if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { ret |= INCHIDIFF_REM_PROT; } if ( a1 && (!a2 || a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { ret |= INCHIDIFF_REM_ISO_H; } /* if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) return 18; } else if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { return 19; } */ if ( i1->StereoIsotopic && i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { Stereo1 = i1->StereoIsotopic; } else { Stereo1 = i1->Stereo; } if ( i2->StereoIsotopic && i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { Stereo2 = i2->StereoIsotopic; } else { Stereo2 = i2->Stereo; } ret |= CompareReversedStereoINChI3( Stereo1, Stereo2, picr ); exit_function: picr->flags = ret; return ret; } /* message group names */ CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] = { {IDGRP_ERR, " Error:"}, {IDGRP_H, " Hydrogens:"}, {IDGRP_MOB_GRP, " Mobile-H groups:"}, {IDGRP_ISO_AT, " Isotopic:"}, {IDGRP_CHARGE, " Charge(s):"}, {IDGRP_PROTONS, " Proton balance:"}, {IDGRP_ISO_H, " Exchangeable isotopic H:"}, {IDGRP_SC, " Stereo centers/allenes:"}, {IDGRP_SB, " Stereobonds/cumulenes:"}, {IDGRP_HLAYER, " Fixed-H layer:"}, {IDGRP_COMP, " Number of components:"}, {IDGRP_CONV_ERR," Conversion encountered:"}, {IDGRP_ZERO, ""} }; /* messages */ CMP_INCHI_MSG CompareInchiMsgs[] = { {INCHIDIFF_PROBLEM ,IDGRP_ERR, " Wrong result" }, /*0x00000001, severe: at least one InChI does not exist */ {INCHIDIFF_POSITION_H ,IDGRP_H, " Locations or number" }, /*0x00000002, difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */ {INCHIDIFF_MORE_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, extra fixed H */ {INCHIDIFF_LESS_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, missing fixed H */ {INCHIDIFF_MORE_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ {INCHIDIFF_LESS_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ {INCHIDIFF_NO_TAUT ,IDGRP_MOB_GRP, " Missing" }, /*0x00000010, restored structure has no taut groups while the original InChI has some */ {INCHIDIFF_WRONG_TAUT ,IDGRP_MOB_GRP, " Falsely present" }, /*0x00000020, restored has tautomerism while the original does not have it */ {INCHIDIFF_SINGLE_TG ,IDGRP_MOB_GRP, " One instead of multiple" }, /*0x00000040, restored has 1 taut. group while the original InChI has multiple tg */ {INCHIDIFF_MULTIPLE_TG ,IDGRP_MOB_GRP, " Multiple instead of one" }, /*0x00000080, restored has multiple tg while the original InChI has only one tg */ {INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, extra tautomeric endpoint{s} in restored structure */ {INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, one or more tg endpoint is not in the restored structure */ {INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, lists of tg endpoints are different */ {INCHIDIFF_NUM_TG ,IDGRP_MOB_GRP, " Number" }, /*0x00000200, different number of tautomeric groups */ {INCHIDIFF_TG ,IDGRP_MOB_GRP, " Do not match" }, /*0x00000200, different tautomeric groups */ {INCHIDIFF_NUM_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different number of isotopic atoms */ {INCHIDIFF_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different locations/isotopes of isotopic atoms */ {INCHIDIFF_REM_ISO_H ,IDGRP_ISO_H, " Does not match for a component" }, /*0x00000800, isotopic H removed */ {INCHIDIFF_MOB_ISO_H ,IDGRP_ISO_H, " Do not match" }, /*0x00001000, different number of mobile exchangeable isotopic H */ {INCHIDIFF_CHARGE ,IDGRP_CHARGE, " Do not match" }, /*0x00002000, restored structure has different charge */ {INCHIDIFF_REM_PROT ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000, proton{s} removed/added from the restored structure */ {INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match" }, /*0x00008000, different proton balance */ {INCHIDIFF_SC_INV ,IDGRP_SC, " Falsely inverted" }, /*0x00010000, restores structure has different inversion stereocenter mark */ {INCHIDIFF_SC_PARITY ,IDGRP_SC, " Wrong parity" }, /*0x00020000, restored structure has stereoatoms or allenes with different parity */ {INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC, " Extra undefined" }, /*0x00040000, restored structure has extra undefined stereocenter{s} */ {INCHIDIFF_SC_EXTRA ,IDGRP_SC, " Extra known" }, /*0x00080000, restored structure has extra stereocenter{s} */ {INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC, " Missing undefined" }, /*0x00100000, restored structure has not some undefined stereocenter{s} */ {INCHIDIFF_SC_MISS ,IDGRP_SC, " Missing known" }, /*0x00200000, restored structure has not some stereocenters that are not undefined */ {INCHIDIFF_SB_PARITY ,IDGRP_SB, " Wrong parity" }, /*0x00400000, restored structure has stereobonds or cumulenes with different parity */ {INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB, " Extra undefined" }, /*0x00800000, restored structure has extra undefined stereobond{s} */ {INCHIDIFF_SB_EXTRA ,IDGRP_SB, " Missing known" }, /*0x01000000, restored structure has extra stereobond{s} */ {INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB, " Missing undefined" }, /*0x02000000, restored structure has not some undefined stereocenters */ {INCHIDIFF_SB_MISS ,IDGRP_SB, " Missing known" }, /*0x04000000, restored structure has not some stereobonds that are not undefined */ {INCHIDIFF_COMP_HLAYER ,IDGRP_HLAYER, " Missing or extra" }, /*0x08000000, Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ {INCHIDIFF_COMP_NUMBER ,IDGRP_COMP, " Does not match" }, /*0x10000000, wrong number of components */ {INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error" }, /*0x20000000 Restored structure to InChI conversion error */ {INCHIDIFF_ZERO ,IDGRP_ZERO, "" } }; /*************************************************************************/ int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ) { const char ellip[] = "..."; int len = strlen( szAddMsg ); int len_delim = (used_len && szDelim)? strlen(szDelim) : 0; int len_to_copy; if ( len + len_delim + used_len < tot_len ) { if ( len_delim ) { strcpy( szMsg+used_len, szDelim ); used_len += len_delim; } strcpy( szMsg+used_len, szAddMsg ); used_len += len; } else if ( (len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10 ) { if ( len_delim ) { strcpy( szMsg+used_len, szDelim ); used_len += len_delim; } strncpy( szMsg+used_len, szAddMsg, len_to_copy ); used_len += len_to_copy; strcpy( szMsg+used_len, ellip ); used_len += sizeof( ellip ) - 1; } return used_len; } /*************************************************************************/ int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ) { int bMobileH, k, n, len = strlen( szMsg ); int iPrevGrpIdx, iCurGrpIdx, bFound; INCHI_MODE bit; static const char *hdr = " Problems/mismatches:"; char szOneMsg[256]; if ( bits[TAUT_YES] || bits[TAUT_NON] ) { if ( !strstr( szMsg, hdr ) ) { len = AddOneMsg( szMsg, len, nLenMsg, hdr, NULL ); } for ( bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH -- ) { if ( bits[bMobileH] ) { strcpy( szOneMsg, bMobileH==TAUT_YES? " Mobile-H(" : " Fixed-H(" ); len = AddOneMsg( szMsg, len, nLenMsg, szOneMsg, NULL ); } bit = 1; iPrevGrpIdx = -1; do { if ( bit & bits[bMobileH] ) { /* search for the message */ bFound = 0; for ( k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k ++ ) { if ( bit & (INCHI_MODE)CompareInchiMsgs[k].nBit ) { /* message found */ for ( n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n ++ ) { if ( CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID ) { iCurGrpIdx = n; if ( iCurGrpIdx != iPrevGrpIdx ) { if ( iPrevGrpIdx >= 0 ) { len = AddOneMsg( szMsg, len, nLenMsg, ";", NULL ); } len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL ); } len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx? ",":NULL ); iPrevGrpIdx = iCurGrpIdx; bFound = 1; break; } } } } } bit <<= 1; } while ( bit ); if ( bits[bMobileH] ) { len = AddOneMsg( szMsg, len, nLenMsg, ")", NULL ); } } } return len; } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichirvrs.h000066400000000000000000001451751271037650300235700ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __ICHIRVRS_H__ #define __ICHIRVRS_H__ #define ICHICONST const #define RI_ERR_ALLOC (-1) #define RI_ERR_SYNTAX (-2) #define RI_ERR_PROGR (-3) #define RI_ERR_EOL (-4) #define RI_ERR_EOF (0) #define NO_VALUE_INT 9999 #define NOT_READ_INT 9998 /* has not been read yet */ #define VALUE_OCTET 8 /* number of electrons in a full shell */ #define INC_EDGE_LIST_DEFAULT 64 typedef struct tagXYZCoord { double xyz[3]; } XYZ_COORD; typedef struct tagStructRestoreMode { int bMetalAddFlower; /* 1 => allow adjustable metal valence and charge; 0=> use std charge/valence */ /* the following three apply only if bMetalAddFlower = 1 */ int nMetalMinBondOrder; /* edge_flow=f means bond order=cMetalMinBondOrder+f */ int nMetalInitEdgeFlow; /* one bond contribution to metal's (st-cap - st-flow) = */ /* (nMetalInitBondOrder-nMetalMinBondOrder) - nMetalInitEdgeFlow */ int nMetalInitBondOrder; /* >= nMetalMinBondOrder + nMetalInitEdgeFlow */ /* same for metal-endpoint bonds */ int nMetal2EndpointMinBondOrder; int nMetal2EndpointInitBondOrder; int nMetal2EndpointInitEdgeFlow; int nMetalFlowerParam_D; /* additional edge capacity for canceling radicals */ int nMetalMaxCharge_D; /* cap and/or flow for metal charge group */ int bStereoRemovesMetalFlag; /* 1=> treat stereogenic atoms and atoms connected by a stereo bond as non-metals */ int bFixStereoBonds; /* 1=> forbid stereogenic double bonds from changing */ } SRM; typedef struct tagReversedInChI { PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; int num_components[INCHI_NUM]; int nRetVal; } REV_INCHI; /**************************************/ #define BFS_Q_CLEAR (-1) #define BFS_Q_FREE (-2) typedef struct tagBfsQueue { QUEUE *q; AT_RANK *nAtomLevel; S_CHAR *cSource; int num_at; AT_RANK min_ring_size; /* 8 => detect 7-member and smaller rings */ } BFS_Q; /**************************************/ #define EXTRACT_STRUCT_NUMBER 1 /* additional Mobile-H parities to be added to Fixed-H parities */ /* This allows to set parities that exist in Mobile-H layer only */ typedef struct tagInpAtomAddParities { /* cml 0D parities */ S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ /* cml tetrahedral parity */ S_CHAR p_parity; AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* cml bond parities */ S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; -1 means removed explicit H */ /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ } inp_ATOM_STEREO; #define FIX_STEREO_BOND_ORDER 0 /* 1=> fix stereobonds; treat metal as non-metal if it is stereogenic or has a stereobond */ #define METAL_FREE_CHARGE_VAL 1 /* 1=> allow free changing charges/valences of metals; initial bond order=0 or 1 */ #define ALLOW_METAL_BOND_ZERO 1 /* 1=> allow zero flow (bobd order) to metals */ #if ( ALLOW_METAL_BOND_ZERO == 1 ) /* INIT_METAL_BOND_ZERO=1 => INIT_METAL_BOND_FLOW=0 */ #define INIT_METAL_BOND_ZERO 0 /* 1=> initialize zero order bond to metals */ #define INIT_METAL_BOND_FLOW 1 /* 1=> init flow=1, 0 => init. flow = 0 */ #else #define INIT_METAL_BOND_ZERO 0 #define INIT_METAL_BOND_FLOW 0 /* always 0 */ #endif #define I2A_FLAG_FIXEDH 0x0001 #define I2A_FLAG_RECMET 0x0002 #define EL_NUMBER_H 1 #define ATYPE_H 1 #define ATYPE_Na 2 #define ATYPE_Mg 3 #define ATYPE_B 4 #define ATYPE_C 5 #define ATYPE_N 6 #define ATYPE_O 7 #define ATYPE_Cl 8 /* first bonds valence for charge = c is cValence[0][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ /* second bonds valence for charge = c is cValence[1][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ /* total number of valences is 2 = VAL_NUMBER */ /* neutral bond valence orders are cValence[0][VAL_NEUTR_ORDER], cValence[1][VAL_NEUTR_ORDER] */ #define VAL_BASE ( 1) #define VAL_MIN_CHARGE (-1) #define VAL_MAX_CHARGE ( 1) #define VAL_NUMBER ( 2) #define VAL_NEUTR_ORDER (VAL_MAX_CHARGE-VAL_MIN_CHARGE+1) #define VAL_LENGTH (VAL_NEUTR_ORDER+1) #define VAL_NEGAT_CHARGE 0 #define VAL_NEUTR_CHARGE 1 #define VAL_POSIT_CHARGE 2 typedef struct tagAtomIonPrperies { /* char cValence[VAL_NUMBER][VAL_LENGTH]; */ /* ordering numbers of minimal valence, 0-based */ char cDoNotAddH; /* InChI does not add H to this element */ char cMetal; /* the element is a metal */ char cNumBondsToMetal; /* number of bonds to metal */ char cInitFlowToMetal; /* sum of init flow to metal atoms */ char cInitValenceToMetal; /* sum of init adjusted bond orders to metal atoms */ char cInitOrigValenceToMetal; /* sum of init bond orders to metal atoms */ char cMaxFlowToMetal; /* max total edge flow to metal atoms */ char cInitFreeValences; /* number of 'dots' to connect; charges are marked separately */ S_CHAR cInitCharge; /* initial charge on the atom (not included in ChargeStruct */ char cNumValenceElectrons; char cPeriodicRowNumber; char cMinRingSize; /* min ring size for atoms that have 2 bonds only */ U_CHAR cPeriodicNumber; /* number in Periodic Table of elements */ S_CHAR cnListIndex; /* (index in the cnList) + 1; 0 => none */ int nCMinusGroupEdge; /* (index of the edge to the atom's (-) group) + 1 */ int nCPlusGroupEdge; /* (index of the edge to the atom's (+) group) + 1 */ int nMetalGroupEdge; /* index of the edge to the atom's M-group + 1 */ int nTautGroupEdge; /* index of the edge from the atom to the t-group + 1 */ } VAL_AT; /******************************************************************************************************/ #define INI_NUM_TCGROUPS 16 #define INC_NUM_TCGROUPS 16 typedef enum tagTgRestoreFlags { TGRF_MINUS_FIRST = 1 } TGRF; typedef struct tagTCGroup { int type; /* group type */ int ord_num; /* ordering number within the type, typically t-group number */ int num_edges; /* charge group specific */ int st_cap; int st_flow; int edges_cap; int edges_flow; int nVertexNumber; /* group vertex number; 0 = unassigned */ int nForwardEdge; /* edge index: from c-group to central Y-connecting vertex or from supergroup to (+/-) vertex; 0 => unassigned */ int nBackwardEdge; /* edge index: from central Y-connecting vertex to supergroup; 0 => unassigned */ /* tautomeric group specific */ short tg_num_H; /* number of H in a tautomeric group */ short tg_num_Minus; /* negative charge on t-group */ Vertex tg_set_Minus; /* the vertex+1 that has to have (-) */ short tg_RestoreFlags; /* Set (-) to first memberst of a t-group (usually, N) */ } TC_GROUP; typedef enum tagTCGroupTypes { TCG_None = -1, /* so far only ord=0 is used */ /* group type ord */ TCG_Plus0 = 0, /* BNS_VT_C_POS 0 */ TCG_Plus1, /* BNS_VT_C_POS 1 */ TCG_Minus0, /* BNS_VT_C_NEG 0 */ TCG_Minus1, /* BNS_VT_C_NEG 1 */ TCG_Plus_C0, /* BNS_VT_C_POS_C 0 */ TCG_Plus_C1, /* BNS_VT_C_POS_C 1 */ TCG_Minus_C0, /* BNS_VT_C_NEG_C 0 */ TCG_Minus_C1, /* BNS_VT_C_NEG_C 1 */ TCG_Plus_M0, /* BNS_VT_C_POS_M 0 */ TCG_Plus_M1, /* BNS_VT_C_POS_M 1 */ TCG_Minus_M0, /* BNS_VT_C_NEG_M 0 */ TCG_Minus_M1, /* BNS_VT_C_NEG_M 1 */ TCG_MeFlower0, /* BNS_VT_M_GROUP 0 */ /* base */ TCG_MeFlower1, /* BNS_VT_M_GROUP 1 */ TCG_MeFlower2, /* BNS_VT_M_GROUP 2 */ TCG_MeFlower3, /* BNS_VT_M_GROUP 3 */ TCG_Plus, /* BNS_VT_C_POS_ALL 0 */ TCG_Minus, /* BNS_VT_C_NEG_ALL 0 */ NUM_TCGROUP_TYPES /* number of group types */ }TCGR_TYPE; typedef struct tagAllTCGroups { TC_GROUP *pTCG; int num_tc_groups; /* number of charge groups and metal-flower vertices */ int max_tc_groups; /* number of allocated of pTCG[] elements */ int nGroup[NUM_TCGROUP_TYPES]; /* tagTCGroupTypes */ int nVertices; /* total number of vertices */ int nEdges; /* total number of edges */ int nAddIedges; /* additional increase of number of iedges for edge switching to another group */ int num_atoms; /* number of atoms */ int num_bonds; /* number of bonds */ int num_tgroups; /* number t-groups */ int num_tgroup_edges; /* number of edges to t-groups */ /* charges */ int tgroup_charge; /* total charge of all t-groups */ int charge_on_atoms; /* charge permanently sitting on atoms */ int added_charge; /* charge added to the c-groups */ int total_charge; /* total charge of the component */ int total_electrons; /* total number of electrons on all atoms */ int total_electrons_metals; /* total number of electrons on unbonded metals */ int num_metal_atoms; /* number of metal atoms */ int num_metal_bonds; /* number of atom-metal bonds */ /* excess_charge = total_charge - added_charge - tgroup_charge: add to metals etc. */ int nEdge4charge; /* edge used to add charges; neighbor1=supercharge, another = (+/-) vertex */ int nEdgePlus; /* edge to (+) supergroup; 0 means none */ int nEdgeMinus; /* edge to (-) supergroup; 0 means none */ int iComponent; /* component number */ int iAtNoOffset; /* first atom number -- always 0 for now */ } ALL_TC_GROUPS; /**************************************/ #define EDGE_LIST_CLEAR (-1) #define EDGE_LIST_FREE (-2) typedef struct tagEdgeList { int num_alloc; int num_edges; EdgeIndex *pnEdges; } EDGE_LIST; /**************************************/ #define BOND_MARK_STEREO 0x10 #define BOND_TYPE_STEREO (BOND_TYPE_SINGLE | BOND_MARK_STEREO) /* local */ #define RESET_EDGE_FORBIDDEN_MASK 0 #define TREAT_ATOM_AS_METAL 99 /************************************************************************************/ typedef struct tagChargeValence { int nValence; int nCharge; int nValenceOrderingNumber; } CHARGE_VAL; #define MY_CONST const /*************************************************************************************/ typedef struct tagChargeChangeCandidate { Vertex iat; char num_bonds; char chem_valence; char cMetal; char cNumBondsToMetal; char cNumValenceElectrons; char cPeriodicRowNumber; char cNumChargeStates; U_CHAR el_number; } CC_CAND; typedef struct tagOneComponentRemovedAndExchangeableH { NUM_H nNumRemovedProtons; NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered randomly distributed, including removed protons */ } COMPONENT_REM_PROTONS; typedef struct tagRemovedAndExchangeableH { /* totals for Mobile-H layer */ NUM_H nNumRemovedProtons; NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered randomly distributed, including removed protons */ /* for individual components from comparing Fixed-H vs Mobile-H formulas; NULL if not available */ COMPONENT_REM_PROTONS *pNumProtons; } REM_PROTONS; typedef struct tagInputInChI { INChI *pInpInChI[INCHI_NUM][TAUT_NUM]; int nNumComponents[INCHI_NUM][TAUT_NUM]; REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM]; int s[INCHI_NUM][TAUT_NUM][2]; /* s[0=non-iso, 1=iso] = 0,1,2,3 <= regular /s; -1=> "/s" (empty) */ long num_inp; inp_ATOM *atom; /* the whole restored structure made out of all components */ int num_atoms; /* number of atoms including explicit H */ int num_explicit_H; /* number of explicit H in the atom */ INCHI_MODE CompareInchiFlags[INCHI_NUM][TAUT_NUM]; } InpInChI; typedef struct tagStructFromInChI { /* InChI component -> Structure result */ inp_ATOM *at; /* length = num_atoms + num_deleted_H, zero pint struct for BNS->struct conversion */ inp_ATOM_STEREO *st; /* additional stereo that exists only in Mobile-H layer */ inp_ATOM *at2; /* length = num_atoms + num_deleted_H, the conversion result */ /* information from InChI only */ T_GROUP_INFO ti; /* from original InChI[0] if Mobile-H from the beginning or later from InChI[1] if Fixed-H */ AT_NUMB *endpoint; /* from original InChI[1] in case of Fixed-H only */ S_CHAR *fixed_H; /* from original InChI[0] in case of Fixed-H only */ XYZ_COORD *pXYZ; int num_atoms; int num_deleted_H; /* if requested and Fixed-H InChI is available */ int nNumRemovedProtonsMobHInChI; /* number of protons removed from Mobile-H struct in original InChI */ S_CHAR charge; char bIsotopic; /* InChI -> Structure conversion parms and intermediate data */ BN_STRUCT *pBNS; BN_DATA *pBD; ICHICONST SRM *pSrm; /* InChI layer to reverse */ char bMobileH; char iINCHI; char bFixedHExists; /* fixed-H InChI exists or not */ /* InChI -> Struct component -> Full InChI result (both disconnected and connected if exist) */ REV_INCHI RevInChI; int nRemovedProtonsByNormFromRevrs; /* number of H(+) removed by normalization after Struct Restore and before Add/Remove Protons */ int nNumRemovedProtonsByRevrs; /* number of H(+) removed by the reconstruction, before Add/Remove Protons, only from TAUT_YES */ int bExtract; /* for debugging */ /* single component InChI calculation */ INChI *pOneINChI[TAUT_NUM]; /* InChI of restored structure */ INChI_Aux *pOneINChI_Aux[TAUT_NUM]; INP_ATOM_DATA *pOne_norm_data[TAUT_NUM]; /* normalized restored structure */ S_CHAR *pOne_fixed_H; /* !!! from normalized restored structure in case of Fixed-H only */ T_GROUP_INFO One_ti; /* t-groups of normalized canonicalized restored structure */ int nOneINChI_bMobileH; /* type of restored structure InChI */ int nNumRemovedProtons; /* =0 for Fixed-H, = num. removed protons in case of Mobile-H InChI */ /* in case of Fixed-H processing see pStruct->One_ti.tni.nNumRemovedProtons */ AT_NUMB *nAtno2Canon[TAUT_NUM]; /* nAtno2Canon[restored_at_no][*] = (atom canon number in restored struct)-1*/ AT_NUMB *nCanon2Atno[TAUT_NUM]; /* nCanon2Atno[(atom canon number in restored struct)-1][*] = restored_at_no; */ int nError; /* other parms */ char iInchiRec; /* index in the original InChI array */ char iMobileH; /* index in the original InChI array */ char bDeleted; /* InChI component marked as Deleted, means a proton in Mobile-H layer */ /* struct. ordering number "Structure: nnn" if present */ long num_inp_actual; /* utility data */ BFS_Q *pbfsq; VAL_AT *pVA; int nLink; /* same as in INChI */ int bPostProcessed; /* recalculate after add/remove protons */ /* TAUT_YES layer charges */ int nChargeRevrs; /* component charge of the reconstructed structure, TAUT_YES layer */ int nChargeInChI; /* component charge from the original InChI, TAUT_YES layer */ } StrFromINChI; #define EL_TYPE_O 0x0001 #define EL_TYPE_S 0x0002 #define EL_TYPE_N 0x0004 #define EL_TYPE_P 0x0008 #define EL_TYPE_C 0x0010 #define EL_TYPE_X 0x0020 /* any not metal */ #define EL_TYPE_MASK 0x003f #define EL_TYPE_OSt 0x0100 /* terminal -OH, -O(-), -SH, -S(-), ... from fix_special_bonds(...) */ #define EL_TYPE_PT 0x0200 /* may be a tautomeric endpoint */ /* the atom to which the node is attached has number 1; added atoms have numbers 2,3,... */ #define MAX_CN_VAL 3 typedef struct tagVertCapFlow { S_SHORT type; S_CHAR cap; S_CHAR flow; S_CHAR valence; } VCF; typedef struct tagEdgeCapFlow { S_SHORT neigh; S_CHAR cap; S_CHAR bForbiddenEdge; S_CHAR flow; } ECF; typedef struct tagChargeNodes { VCF v; ECF e[MAX_CN_VAL]; } C_NODE; #define cn_bits_N 1 /* Neutral: charge = 0 */ #define cn_bits_P 2 /* Plus 1: charge = +1 */ #define cn_bits_M 4 /* Minus 1: charge = -1 */ #define cn_bits_shift 3 #define MAX_NUM_CN_BITS 4 #define MAKE_CN_BITS(A, B, C, D ) (( (( ((D) << cn_bits_shift | (C)) << cn_bits_shift ) | (B)) << cn_bits_shift ) | (A)) #define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) #define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) #define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) #define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) #define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) #define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) #define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) #define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) #define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) #define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) #define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) #define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) #define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) #define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) #define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) #define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) #define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) #define cn_bits_Me (-1) #define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ extern int cnListNumEl; /* number of elements in cnList[] */ typedef struct tagChargeNodeList { MY_CONST C_NODE *pCN; int bits; int nInitialCharge; int len; } CN_LIST; extern MY_CONST CN_LIST cnList[]; /************************ fixed H comparison ******************************************************/ #define MAX_DIFF_FIXH 256 #define MAX_DIFF_MOBH 256 typedef struct tagAtomsCmpTwoFixedH { AT_NUMB endptInChI; AT_NUMB endptRevrs; AT_NUMB atomNumber; U_CHAR nValElectr; U_CHAR nPeriodNum; S_CHAR nFixHInChI; S_CHAR nFixHRevrs; S_CHAR nMobHInChI; S_CHAR nMobHRevrs; S_CHAR nNumHRevrs; S_CHAR nAtChargeRevrs; S_CHAR nValue; /* flag(s) */ } CMP2FHATOMS; typedef struct tagStructCmpTwoFixedH { CMP2FHATOMS c2at[MAX_DIFF_FIXH]; short len_c2at; short nNumRemHInChI; short nNumRemHRevrs; short nNumTgInChI; short nNumTgRevrs; short nNumEndpInChI; short nNumEndpRevrs; short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ short nNumTgHInChI; /* number of H in orig. InChI t-groups */ short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ short nNumTgHRevrs; /* number of H in reversed structure t-groups */ S_CHAR nChargeFixHInChI; S_CHAR nChargeMobHInChI; S_CHAR nChargeFixHRevrs; S_CHAR nChargeMobHRevrs; S_CHAR nChargeFixHRevrsNonMetal; /* charge does not include charges on metals */ S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals */ char bFixedHLayerExistsRevrs; char bHasDifference; U_CHAR nNumDiffMobH; } CMP2FHINCHI; /************************ Mobile H comparison *********************************************/ typedef struct tagAtomsCmpTwoMobileH { AT_NUMB endptInChI; AT_NUMB endptRevrs; AT_NUMB atomNumber; U_CHAR nValElectr; U_CHAR nPeriodNum; S_CHAR nMobHInChI; /* number of H on the atom in the orig. InChI */ S_CHAR nMobHRevrs; /* number of H on the atom in InChI from the reconstructed structure */ S_CHAR nNumHRevrs; /* number of H on the atom in the being reconstructed structure */ S_CHAR nAtChargeRevrs; S_CHAR nValue; /* flag(s) */ } CMP2MHATOMS; typedef struct tagStructCmpTwoMobileH { CMP2MHATOMS c2at[MAX_DIFF_FIXH]; short len_c2at; short nNumRemHInChI; short nNumRemHRevrs; short nNumTgInChI; short nNumTgRevrs; short nNumEndpInChI; short nNumEndpRevrs; short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ short nNumTgHInChI; /* number of H in orig. InChI t-groups */ short nNumTgOInChI; /* number of tautomeric O,S,Se in orig. InChI t-groups */ short nNumTgNInChI; /* number of tautomeric N in orig. InChI t-groups */ short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ short nNumTgHRevrs; /* number of H in reversed structure t-groups */ short nNumTgORevrs; /* number of tautomeric O,S,Se in reversed structure t-groups */ short nNumTgNRevrs; /* number of tautomeric N in reversed structure t-groups */ short nNumTgOMinusRevrs; /* number of -O(-) on endpoints found in restored structure */ short nNumTgOHRevrs; /* number of -OH on endpoints found in restored structure */ short nNumTgDBORevrs; /* number of =O on endpoints found in restored structure */ short nNumTgNMinusRevrs; /* number of -N(-)- on endpoints found in restored structure */ short nNumTgNHMinusRevrs; /* number of -NH(-) on endpoints found in restored structure */ short nNumTgNHRevrs; /* number of -NH- on endpoints found in restored structure */ short nNumTgNH2Revrs; /* number of -NH2 on endpoints found in restored structure */ short nNumTgDBNHRevrs; /* number of =NH on endpoints found in restored structure */ short nNumTgDBNMinusRevrs; /* number of =N(-) on endpoints found in restored structure */ short nNumTgDBNRevrs; /* number of =N- on endpoints found in restored structure */ short nNumTgOMinusInChI; /* number of -O(-) on endpoints according to original InChI */ short nNumTgOHInChI; /* number of -OH on endpoints according to original InChI */ short nNumTgDBOInChI; /* number of =O on endpoints according to original InChI */ short nNumTgNMinusInChI; /* number of -N(-)- on endpoints according to original InChI */ short nNumTgNHMinusInChI; /* number of -NH(-) on endpoints according to original InChI */ short nNumTgNHInChI; /* number of -NH- on endpoints according to original InChI */ short nNumTgNH2InChI; /* number of -NH2 on endpoints according to original InChI */ short nNumTgDBNHInChI; /* number of =NH on endpoints according to original InChI */ short nNumTgDBNMinusInChI; /* number of =N(-) on endpoints according to original InChI */ short nNumTgDBNInChI; /* number of =N- on endpoints according to original InChI */ S_CHAR nChargeMobHInChI; S_CHAR nChargeMobHRevrs; S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals; later add ion pairs rejection */ char bFixedHLayerExistsRevrs; char bHasDifference; U_CHAR nNumDiffMobH; } CMP2MHINCHI; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int OneInChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]); int get_sp_element_type( int nPeriodicNumber, int *nRow ); int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ); /* local prototypes */ int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ); int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ); int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ); int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ); int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ); int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ); int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ); int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ); int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI); void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH); int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ); int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ); int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ); int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ); int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ); int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pv0 /*src*/, BNS_VERTEX *pv1/*dest*/, int *tot_st_cap, int *tot_st_flow ); void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ); void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); int ConnectSuperCGroup( int nTCG_Plus, int nAddGroups[], int num_add, int *pcur_num_vertices, int *pcur_num_edges, int *tot_st_cap, int *tot_st_flow, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ); int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ); /* int GetAtomBondFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh ); void GetAtomStCapFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pCap, int *pFlow ); int GetAtomToMCGroupInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow, ICHICONST SRM *pSrm, inp_ATOM *at, VAL_AT *pVA, int iat ); void GetAtomToMetalInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow ); */ int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ); int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ); int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ); int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ); int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges); int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ); int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ); int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH); int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ); int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ); int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ); int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ); void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent); void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ); void ClearEndpts(inp_ATOM *at, int num_at ); int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ); int cmp_charge_val( const void *a1, const void *a2 ); int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); int comp_cc_cand( const void *a1, const void *a2 ); int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ); void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ); int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, int num_atoms, int forbidden_mask ); int AllocEdgeList( EDGE_LIST *pEdges, int nLen ); int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ); int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ); int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ); int FindInEdgeList( EDGE_LIST *pEdges, int iedge ); int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ); void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ); int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ); int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ); int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ); int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, long num_inp ); int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ); int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ); int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); /* call InChI normalization only */ int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ); /* create one InChI */ int MakeOneInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **t_group_info, inp_ATOM **at_norm, inp_ATOM **at_prep ); /* fixed-H */ int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ); int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ); int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ti, inp_ATOM **at_norm, inp_ATOM **at_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ); int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ); int bHas_N_V( inp_ATOM *at2, int num_atoms ); int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ); int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, StrFromINChI *pStruct, int num_components, StrFromINChI *pStructR, int num_componentsR, NUM_H *pProtonBalance, int *recmet_change_balance ); int AllInchiToStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ); int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, char *szCurHdr, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput); int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *pOneInput); int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ); void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ); void SetUpSrm( SRM *pSrm ); void FreeInpInChI( InpInChI *pOneInput ); void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ); int OldPrintCompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, long num_inp, char *szCurHdr); int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, long num_inp, char *szCurHdr, COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]); int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], int bMobileH, INCHI_MODE CompareInchiFlags[] ); INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2, int *err ); INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr); int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, long num_inp, char *szCurHdr); int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bHasSomeFixedH, long num_inp, char *szCurHdr); int insertions_sort_AT_NUMB( AT_NUMB *base, int num ); int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, StrFromINChI *pStruct, int num_components, StrFromINChI *pStructR, int num_componentsR, NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ); int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, int bINChIOutputOptions, INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, InpInChI *pOneInput, int bHasSomeFixedH, unsigned char save_opt_bits); int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ); int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ); int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ); void clear_t_group_info( T_GROUP_INFO *ti ); int bInpInchiComponentExists( InpInChI *pOneInput, int iINCHI, int bMobileH, int k ); int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ); int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, int *bHasFixedH, int *sFlag, int *bTautFlag ); int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput ); int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, int iComponent, int nNumComponents, int bMobileH, const char *szCurHdr ); int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ); int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, int *num_unk_und_SB, int *num_unk_und_SC, int *num_SC_PIII, int *num_SC_AsIII); int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ); int bIsUnsatCarbonInASmallRing( inp_ATOM *at, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ); int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ); /* extra configurarion */ #define KEEP_METAL_EDGE_FLOW 0 /* counterexample: mdb0-1738.sdf.txt */ #define MOVE_CHARGES_FROM_HETEREO_TO_METAL 0 /* disabled */ #define FIX_ADD_PROTON_FOR_ADP 0 /* not used */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichisize.h000066400000000000000000000034721271037650300235370ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef ___INCHISIZE_H__ #define ___INCHISIZE_H__ typedef unsigned short AT_NUMB; typedef unsigned short AT_RANK; #define AT_RANK_MASK ((AT_RANK)~0) typedef signed short NUM_H; #define MAX_ATOMS 1024 #define CHAR_MASK 0xFF typedef AT_RANK *pAT_RANK; typedef pAT_RANK *ppAT_RANK; typedef unsigned long INCHI_MODE; #define LEN_COORD 10 #define NUM_COORD 3 typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ #endif /* ___INCHISIZE_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichisort.c000066400000000000000000000530301271037650300235420ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "incomdef.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichicant.h" #include "ichicomn.h" #include "ichicomp.h" #define RET_MAX 32767 /**********************************************************************************/ void inchi_swap ( char *a, char *b, size_t width ) { char tmp; if ( a != b ) while ( width-- ) { tmp = *a; *a++ = *b; *b++ = tmp; } } /**********************************************************************************/ /* Sort by insertions */ int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ) { char *i, *j, *pk = (char*)base; int num_trans = 0; size_t k; for( k=1; k < num; k++, pk += width ) { /*for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width )*/ for( i = j = pk + width; j > (char*)base && (i -= width,(*compare)(i,j)) > 0; j=i ) /* changed to keep BoundsChecker happy 2007-09-24 DT */ { inchi_swap( i, j, width ); num_trans ++; } } return num_trans; } /**********************************************************************************/ /* Sort by insertions */ int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ) { AT_NUMB *i, *j, *pk, tmp; int k, num_trans = 0; for( k=1, pk = base; k < num; k++, pk ++ ) { for( j = (i = pk) + 1, tmp = *j; j > base && (*compare)(i,&tmp) > 0; j=i, i -- ) { *j = *i; num_trans ++; } *j = tmp; } return num_trans; } /**********************************************************************************/ /* Sort neighbors according to ranks in ascending order */ void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ) { AT_NUMB *i, *j, *pk, tmp; AT_RANK rj; /* optimization */ int k, num = (int)*base++; for( k=1, pk = base; k < num; k++, pk ++ ) { for( j = (i = pk) + 1, rj=nRank[(int)*j]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { tmp = *i; *i = *j; *j = tmp; } } } /**********************************************************************************/ /* Sort neighbors according to ranks in ascending order */ int insertions_sort_AT_RANK( AT_RANK *base, int num ) { AT_RANK *i, *j, *pk, tmp; int k, num_trans = 0; for( k=1, pk = base; k < num; k++, pk ++ ) { for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { *j = *i; num_trans ++; } *j = tmp; } return num_trans; } /**********************************************************************************/ /* Sort neighbors according to ranks in ascending order */ int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ) { AT_NUMB *i, *j, *pk, tmp; AT_RANK rj; int k, n, num = (int)*base++; for( k=1, pk = base, n=0; k < num; k++, pk ++ ) { for( j = (i = pk) + 1, rj=nRank[(int)(tmp=*j)]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { *j = *i; n ++; } *j = tmp; } return n; } /**********************************************************************************/ /* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) { AT_NUMB *i, *j, *pk, tmp; int diff; int k, num = (int)*base++; for( k=1, pk = base; k < num; k++, pk ++ ) { for( j = (i = pk) + 1; j > base && /* always j > i */ ( 0 > (diff = (int)nSymmRank[(int)*i] - (int)nSymmRank[(int)*j]) || !diff && nCanonRank[(int)*i] < nCanonRank[(int)*j]); j=i, i -- ) { tmp = *i; *i = *j; *j = tmp; } } } /********************************************************************************************* * * Comparison functions * *********************************************************************************************/ int CompNeighborsAT_NUMBER( const void* a1, const void* a2) { #ifdef CT_NEIGH_INCREASE return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]] - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]]; #else return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]] - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]]; #endif } /**********************************************************************************/ int comp_AT_RANK( const void* a1, const void* a2) { return (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; } /**********************************************************************************/ /* Compare for sorting Ranks only */ int CompRank(const void* a1, const void* a2 ) { int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; return ret; } /**********************************************************************************/ int CompRanksOrd( const void* a1, const void* a2 ) { int ret; ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; if ( !ret ) ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; return ret; } /**********************************************************************************/ int CompAtomInvariants2Only( const void* a1, const void* a2 ) { const ATOM_INVARIANT2 *pAI1 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a1; const ATOM_INVARIANT2 *pAI2 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a2; int i; for ( i = 0; i < AT_INV_BREAK1; i ++ ) { if ( pAI1->val[i] == pAI2->val[i] ) continue; return (int)pAI1->val[i] - (int)pAI2->val[i]; } if ( pAI1->iso_sort_key != pAI2->iso_sort_key ) { return ( pAI1->iso_sort_key > pAI2->iso_sort_key )? 1 : -1; } for ( ; i < AT_INV_LENGTH; i ++ ) { if ( pAI1->val[i] != pAI2->val[i] ) continue; return (int)pAI1->val[i] - (int)pAI2->val[i]; } if ( pAI1->iso_aux_key != pAI2->iso_aux_key ) { return ( pAI1->iso_aux_key > pAI2->iso_aux_key )? 1 : -1; } return 0; } /**********************************************************************************/ int CompAtomInvariants2( const void* a1, const void* a2 ) { /* Warning: the following line may be compiler implementation dependent */ int ret = CompAtomInvariants2Only( a1, a2 ); if ( !ret ) ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; return ret; } /**********************************************************************************/ /* Compare two elements lexicographically */ int CompChemElemLex( const void *a1, const void *a2 ) { return memcmp( a1, a2, 2); } /**********************************************************************************/ /* lexicographic compare */ int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank) { int len1 = (int)*pp1++; int len2 = (int)*pp2++; int len = inchi_min( len1, len2 ); int diff = 0; while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) ; return diff? diff : (len1 - len2); } /**********************************************************************************/ /* lexicographic compare */ int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ) { int len1 = (int)*pp1++; int len2 = (int)*pp2++; int diff = 0; int len; while( 0 < len1 && nRank[pp1[len1-1]] > nMaxAtNeighRank ) { len1 --; } while( 0 < len2 && nRank[pp2[len2-1]] > nMaxAtNeighRank ) { len2 --; } len = inchi_min( len1, len2 ); while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) ; return diff? diff : (len1 - len2); } /**********************************************************************************/ int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ) { return CompareNeighListLex( *op1, *op2, pn_RankForSort); } /**********************************************************************************/ int CompNeighListRanks( const void* a1, const void* a2 ) { int ret; ret = (int)pn_RankForSort[*((const AT_RANK*)a1)] - (int)pn_RankForSort[*((const AT_RANK*)a2)]; if ( !ret ) ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), pNeighList_RankForSort + *((const AT_RANK*)a2) ); return ret; } /**********************************************************************************/ int CompNeighLists( const void* a1, const void* a2 ) { int ret; ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), pNeighList_RankForSort + *((const AT_RANK*)a2) ); return ret; } /**********************************************************************************/ int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ) { int ret; ret = CompareNeighListLexUpToMaxRank( pNeighList_RankForSort[*((const AT_RANK*)a1)], pNeighList_RankForSort[*((const AT_RANK*)a2)], pn_RankForSort, nMaxAtNeighRankForSort ); return ret; } /**********************************************************************************/ int CompNeighListRanksOrd( const void* a1, const void* a2 ) { int ret = CompNeighListRanks( a1, a2 ); if ( !ret ) ret = (int)*((const AT_RANK*)a1) - (int)*((const AT_RANK*)a2); /* keep original order if identical */ return ret; } /**********************************************************************************/ int CompRanksInvOrd( const void* a1, const void* a2 ) { return (int)*(const AT_RANK*)a2 - (int)*(const AT_RANK*)a1; } /**********************************************************************************/ int CompNeighborsRanksCountEql( const void* a1, const void* a2 ) { #ifdef CT_NEIGH_INCREASE int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; #else int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a2] - (int)pn_RankForSort[(int)*(const AT_RANK*)a1]; #endif nNumCompNeighborsRanksCountEql += !ret; return ret; } /**************************************************************************************** * * In this neighbor list the (vertex number) = (canonical number) - 1 * Since LinearCT is sorted so that parents are in ascending order * and all neighbors of a parent are smaller than the parent and are * in ascending order, the neighbors in the NEIGH_LIST are automatically * sorted in ascending order */ NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ) { /* atom numbers in LinearCT are canonical numbers * order: parent[i] > neigh[i][0] < neigh[i][1]... neigh[i+1][0] < ... * parent[i] < parent[i+1] */ int i, j; S_CHAR *valence = NULL; NEIGH_LIST *pp = NULL; AT_NUMB *pAtList = NULL; AT_RANK n_vertex, n_neigh; int err = 1, num_bonds; int length, start; if ( (int)LinearCT[0] > num_atoms ) { goto exit_function; } if ( !(valence = (S_CHAR*)inchi_calloc( num_atoms+1, sizeof(valence[0]) ) ) ) { goto exit_function; } for ( i = 1, num_bonds = 0, n_vertex = LinearCT[0]; i < nLenCT; i ++ ) { if ( (n_neigh = LinearCT[i]) < n_vertex ) { valence[n_neigh] ++; valence[n_vertex] ++; num_bonds += 2; } else if ( (int)(n_vertex = n_neigh) > num_atoms ) { goto exit_function; } } if ( (int)n_vertex != num_atoms ) { goto exit_function; } length = num_bonds + num_atoms + 1; if ( pp = (NEIGH_LIST *) inchi_calloc((num_atoms+1), sizeof(NEIGH_LIST)) ) { if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { /* create empty connection table */ for ( i = 1, length = 0; i <= num_atoms; i ++ ) { start = length; length += (valence[i]+1); pp[i-1] = pAtList + start; pp[i-1][0] = 0; } /* fill out the CT */ for ( i = 1, n_vertex = LinearCT[0]-1; i < nLenCT; i ++ ) { if ( (n_neigh = LinearCT[i]-1) < n_vertex ) { /* vertex - neighbor connection */ j = (int)(++pp[(int)n_vertex][0]); pp[(int)n_vertex][j] = n_neigh; /* neighbor - vertex connection */ j = (int)(++pp[(int)n_neigh][0]); pp[(int)n_neigh][j] = n_vertex; } else if ( (int)(n_vertex = n_neigh) >= num_atoms ) { goto exit_function; } } err = 0; } } exit_function: if ( valence ) { inchi_free( valence ); } if ( err ) { if ( pAtList ) inchi_free( pAtList ); if ( pp ) { inchi_free( pp ); pp = NULL; } } return pp; } /*********************************************************************************** * NEIGH_LIST pp[] is an array of pointers to the lists of neighboring atoms numbers * The first number in each list is a number of neighbors. * In case of bDoubleBondSquare != 0 neighbors connected by the double bond appear 2 times * The first element pp[0] is a pointer to be deallocated to free all the lists. */ NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, int bDoubleBondSquare, T_GROUP_INFO *t_group_info ) { /* +1 to add NULL termination */ NEIGH_LIST *pp = (NEIGH_LIST *) inchi_calloc((num_at_tg+1), sizeof(NEIGH_LIST)); T_GROUP *t_group = NULL; AT_NUMB *nEndpointAtomNumber = NULL; int num_t_groups = 0; int nFirstEndpointAtNoPos; AT_NUMB *pAtList = NULL; int length, start, val, i, j; if ( pp ) { if ( num_at_tg > num_atoms ) { t_group = t_group_info->t_group; num_t_groups = t_group_info->num_t_groups; nEndpointAtomNumber = t_group_info->nEndpointAtomNumber; } if ( !bDoubleBondSquare ) { for ( i = 0, length = 0; i < num_atoms; i ++ ) { length += (int)at[i].valence + (num_t_groups && at[i].endpoint); } length += num_atoms; for ( i = 0; i < num_t_groups; i ++ ) { length += (int)t_group[i].nNumEndpoints; } length += num_t_groups; } else { for ( i = 0, length = 0; i < num_atoms; i ++ ) { val = (int)at[i].valence; for ( j = 0; j < val; j ++ ) { length += 1 + (bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j]); } length += (num_t_groups && at[i].endpoint); } length += num_atoms; for ( i = 0; i < num_t_groups; i ++ ) { length += (int)t_group[i].nNumEndpoints; } length += num_t_groups; } length ++; /* +1 to save number of neighbors */ if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { if ( !bDoubleBondSquare ) { for ( i = 0, length = 0; i < num_atoms; i ++ ) { val = at[i].valence; start = length ++; for ( j = 0; j < val; j ++ ) { pAtList[length ++] = at[i].neighbor[j]; } /* add endpoint */ if (num_t_groups && at[i].endpoint) { pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; } pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ pp[i] = pAtList + start; /* pointer to the */ } } else { for ( i = 0, length = 0; i < num_atoms; i ++ ) { val = at[i].valence; start = length ++; for ( j = 0; j < val; j ++ ) { pAtList[length ++] = at[i].neighbor[j]; if ( bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j] ) { pAtList[length ++] = at[i].neighbor[j]; /* a list of neighbor orig. numbers */ } } /* add endpoint */ if (num_t_groups && at[i].endpoint) { pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; } pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ pp[i] = pAtList + start; /* pointer to the */ } } /* add t-groups */ for ( i = 0; i < num_t_groups; i ++ ) { val = (int)t_group[i].nNumEndpoints; start = length ++; nFirstEndpointAtNoPos = (int)t_group[i].nFirstEndpointAtNoPos; for ( j = 0; j < val; j ++ ) { pAtList[length ++] = nEndpointAtomNumber[nFirstEndpointAtNoPos+j]; } pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ pp[num_atoms+i] = pAtList + start; /* pointer to the */ } } else { inchi_free ( pp ); return NULL; } } return pp; } /**********************************************************************************/ void FreeNeighList( NEIGH_LIST *pp ) { if ( pp ) { if ( pp[0] ) { inchi_free( pp[0] ); } inchi_free( pp ); } } /**********************************************************************************/ int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS) { int i, nRet = -1, nNumRanks=1 /* value does not matter*/; AT_RANK *nPrevRank = *pRankStack ++; AT_RANK *nPrevAtomNumber = *pRankStack ++; AT_RANK *nNewRank = NULL; AT_RANK *nNewAtomNumber = NULL; if ( !pRankStack[0] ) { pRankStack[0] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewRank)); } if ( !pRankStack[1] ) { pRankStack[1] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewAtomNumber)); } if ( !pRankStack[0] || !pRankStack[1] ) return CT_OUT_OF_RAM; /* */ nNewRank = pRankStack[0]; nNewAtomNumber = pRankStack[1]; if ( nNewRank && nNewAtomNumber ) { memcpy( nNewAtomNumber, nPrevAtomNumber, num_atoms*sizeof(nNewAtomNumber[0])); memcpy( nNewRank, nPrevRank, num_atoms*sizeof(nNewRank[0])); for ( i = 1, nRet=0; i < num_atoms; i ++ ) { /* 12-12-2001: replaced Prev... with New... */ if ( nNewRank[(int)nNewAtomNumber[i-1]] == nNewRank[(int)nNewAtomNumber[i]] ) { nNewRank[nNewAtomNumber[i-1]] = (AT_RANK)i; nNumRanks = DifferentiateRanks2( num_atoms, NeighList, nNumRanks, nNewRank, nTempRank, nNewAtomNumber, &pCS->lNumNeighListIter, 1 ); pCS->lNumBreakTies ++; nRet ++; } } } return nRet; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichister.c000066400000000000000000005201721271037650300235360ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #include "ichierr.h" #include "inpdef.h" #include "extr_ct.h" #include "ichister.h" #include "ichiring.h" #include "ichi.h" #include "ichicomp.h" #include "util.h" #define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ #define ZTYPE_NONE 0 #define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ #define ZTYPE_3D 3 #define ZTYPE_EITHER 9999 /* criteria for ill-defined */ #define MIN_ANGLE 0.10 /* 5.73 degrees */ #define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ #define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ #define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ #define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ #define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ #define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ #define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ #define MIN_BOND_LEN 0.000001 #define ZERO_LENGTH MIN_BOND_LEN #define ZERO_FLOAT 1.0e-12 #define BOND_PARITY_UNDEFINED 64 #if ( STEREO_CENTER_BONDS_NORM == 1 ) #define MPY_SINE 1.00 /* was 3.0 */ #define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ #else #define MPY_SINE 3.00 #define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ #endif /* local prototypes */ static int save_a_stereo_bond( int z_prod, int result_action, int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ); static double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); static double len3( const double c[] ); static double len2( const double c[] ); static double* diff3( const double a[], const double b[], double result[] ); static double* add3( const double a[], const double b[], double result[] ); static double* mult3( const double a[], double b, double result[] ); static double* copy3( const double a[], double result[] ); static double* change_sign3( const double a[], double result[] ); static double dot_prod3( const double a[], const double b[] ); static int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); static double* cross_prod3( const double a[], const double b[], double result[] ); static double triple_prod( double a[], double b[], double c[], double *sine_value ); static double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); static int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, int at_2, int i_next_at_2, S_CHAR *z_dir2 ); static int CompDble( const void *a1, const void *a2 ); static int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ); static double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); static int are_4at_in_one_plane( double at_coord[][3], double min_sine); static int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ); static int bIsSuitableHeteroInpAtom( inp_ATOM *at ); static int bIsOxide( inp_ATOM *at, int cur_at ); static int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, int bPointedEdgeStereo, int vABParityUnknown ); static int get_allowed_stereo_bond_type( int bond_type ); static int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ); static int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ); static int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK min_sb_ring_size, int bPointedEdgeStereo, int vABParityUnknown ); static int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); static int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, int bPointedEdgeStereo, int vABParityUnknown ); /* int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, vABParityUnknown ); int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); */ int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ); int comp_AT_NUMB( const void* a1, const void* a2); int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], int nNumExplictAttachments, int bond_parity, int nFlag ); int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ); int GetSbNeighOrigAtNumb( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, AT_NUMB nSbNeighOrigAtNumb[]); int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, int at_1, int i_next_at_1, S_CHAR z_dir1[], int at_2, int i_next_at_2, S_CHAR z_dir2[], int *pparity1, int *pparity2 ); /******************************************************************/ static double *pDoubleForSort; /**********************************************************************************/ int comp_AT_NUMB( const void* a1, const void* a2) { return (int)*(const AT_NUMB*)a1 - (int)*(const AT_NUMB*)a2; } /******************************************************************/ double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) { int stereo_value = at[cur_atom].bond_stereo[neigh_no]; int stereo_type = abs( stereo_value ); int neigh = (int)at[cur_atom].neighbor[neigh_no]; double z = at[neigh].z - at[cur_atom].z; int bFlat; if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { int i; for ( i = 0; i < at[cur_atom].valence; i ++ ) { if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { bFlat = 0; break; } } } if ( bFlat ) { if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ switch( stereo_type ) { /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ case 0: /* No stereo */ *nType = ZTYPE_NONE; break; case STEREO_SNGL_UP: /* 1= Up */ *nType = ZTYPE_UP; break; case STEREO_SNGL_EITHER: /* 4 = Either */ *nType = ZTYPE_EITHER; break; case STEREO_SNGL_DOWN: /* 6 = Down */ *nType = ZTYPE_DOWN; break; default: *nType = ZTYPE_NONE; /* ignore unexpected values */ } if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) *nType = -*nType; } else { *nType = ZTYPE_NONE; /* no stereo */ } } else if ( stereo_type == STEREO_SNGL_EITHER && ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { *nType = ZTYPE_EITHER; } else { *nType = ZTYPE_3D; } return z; } /******************************************************************/ double len3( const double c[] ) { return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); } /******************************************************************/ double len2( const double c[] ) { return sqrt( c[0]*c[0] + c[1]*c[1] ); } /******************************************************************/ double* diff3( const double a[], const double b[], double result[] ) { result[0] = a[0] - b[0]; result[1] = a[1] - b[1]; result[2] = a[2] - b[2]; return result; } /******************************************************************/ double* add3( const double a[], const double b[], double result[] ) { result[0] = a[0] + b[0]; result[1] = a[1] + b[1]; result[2] = a[2] + b[2]; return result; } /******************************************************************/ double* mult3( const double a[], double b, double result[] ) { result[0] = a[0] * b; result[1] = a[1] * b; result[2] = a[2] * b; return result; } /*************************************************************/ double* copy3( const double a[], double result[] ) { result[0] = a[0]; result[1] = a[1]; result[2] = a[2]; return result; } /*************************************************************/ double* change_sign3( const double a[], double result[] ) { result[0] = -a[0]; result[1] = -a[1]; result[2] = -a[2]; return result; } /*************************************************************/ double dot_prod3( const double a[], const double b[] ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } /*************************************************************/ int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) { int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; if ( prod > 100 ) prod = 100; else if ( prod < -100 ) prod = -100; return prod; } /*************************************************************/ double* cross_prod3( const double a[], const double b[], double result[] ) { double tmp[3]; tmp[0] = (a[1]*b[2]-a[2]*b[1]); tmp[1] = -(a[0]*b[2]-a[2]*b[0]); tmp[2] = (a[0]*b[1]-a[1]*b[0]); result[0] = tmp[0]; result[1] = tmp[1]; result[2] = tmp[2]; return result; } /*************************************************************/ double triple_prod( double a[], double b[], double c[], double *sine_value ) { double ab[3], dot_prod_ab_c, abs_c, abs_ab; cross_prod3( a, b, ab ); /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ dot_prod_ab_c = dot_prod3( ab, c ); /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ if ( sine_value ) { abs_c = len3( c ); /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ abs_ab = len3( ab ); /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ } else { *sine_value = 0.0; } } return dot_prod_ab_c; } /*************************************************************/ int CompDble( const void *a1, const void *a2 ) { double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; if ( diff > 0.0 ) return 1; if ( diff < 0.0 ) return -1; return 0; } /*************************************************************/ #define T2D_OKAY 1 #define T2D_WARN 2 #define T2D_UNDF 4 int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ) { /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ const double one_pi = 3.14159265358979323846; /* M_PI */ const double two_pi = 2.0*one_pi; const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH]; volatile double dAngle, dAlpha, dLimit, dBisector; /* 2010-02-10 added 'volatile': workaround ensuring proper behavior for gcc 32-bit */ /* cml-enabled compiles at >=O1 for SID484922 and alike (both lin&win had problems) */ int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; int ret=0; for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) { dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ if ( dAngle < 0.0 ) { dAngle += two_pi; } dBondDirection[i] = dAngle; nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ if ( nBondType[i] > 0 ) { num_Up ++; } else if ( nBondType[i] < 0 ) { num_Dn ++; } nBondOrder[i] = i; } if ( num_Up < num_Dn ) { for ( i = 0; i < nNumNeigh; i ++ ) { nBondType[i] = -nBondType[i]; } inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); } if ( !num_Up ) { return T2D_UNDF; } /* sort according to the bond orientations */ pDoubleForSort = dBondDirection; insertions_sort( nBondOrder, (unsigned) nNumNeigh, sizeof(nBondOrder[0]), CompDble ); /* find the longest contiguous sequence of Up bonds */ if ( num_Up == nNumNeigh ) { /* all bonds are Up */ len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ first_Up = 0; } else { /* at least one bond is not Up */ cur_len_Up = len_Up = bPrev_Up = 0; /* prev. cycle header version --- for ( i = 0; 1; i ++ ) { if ( i >= nNumNeigh && !bPrev_Up ) { break; } ----------} */ /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { if ( bPrev_Up ) { cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ } else { bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ cur_len_Up = 1; cur_first_Up = i % nNumNeigh; } } else if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ if ( cur_len_Up > len_Up ) { first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ len_Up = cur_len_Up; } bPrev_Up = 0; } } } #if ( FIX_2D_STEREO_BORDER_CASE == 1 ) /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ /* have identical angles. In this case switch their order to enlarge the Up sequence */ #define ZERO_ANGLE 0.000001 if ( nNumNeigh - len_Up >= 2 ) { int next1, next2; for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ if ( nBondType[nBondOrder[next2]] > 0 ) { next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; if ( fabs(dAngle) < ZERO_ANGLE ) { inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); len_Up ++; break; } } } } /* check whether the not-Up bond (located before the found first-Up) has */ /* same angle as the Up bond that precedes this not-Up bond */ if ( nNumNeigh - len_Up >= 2 ) { int next1, next2; for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ if ( nBondType[nBondOrder[next2]] > 0 ) { next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; if ( fabs(dAngle) < ZERO_ANGLE ) { inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); first_Up = next1; len_Up ++; break; } } } } #else if ( bFix2DstereoBorderCase ) { /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ /* have identical angles. In this case switch their order to enlarge the Up sequence */ #define ZERO_ANGLE 0.000001 if ( nNumNeigh - len_Up >= 2 ) { int next1, next2; for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ if ( nBondType[nBondOrder[next2]] > 0 ) { next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; if ( fabs(dAngle) < ZERO_ANGLE ) { inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); len_Up ++; break; } } } } /* check whether the not-Up bond (located before the found first-Up) has */ /* same angle as the Up bond that precedes this not-Up bond */ if ( nNumNeigh - len_Up >= 2 ) { int next1, next2; for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ if ( nBondType[nBondOrder[next2]] > 0 ) { next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; if ( fabs(dAngle) < ZERO_ANGLE ) { inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); first_Up = next1; len_Up ++; break; } } } } } #endif /* Turn all the bonds around the center so that */ /* the 1st Up bond has zero radian direction */ dAlpha = dBondDirection[nBondOrder[first_Up]]; for ( i = 0; i < nNumNeigh; i ++ ) { if ( i == nBondOrder[first_Up] ) { dBondDirection[i] = 0.0; } else { dAngle = dBondDirection[i] - dAlpha; if ( dAngle < 0.0 ) { dAngle += two_pi; } dBondDirection[i] = dAngle; } } /******************************************************** * Process particular cases ********************************************************/ if ( nNumNeigh == 3 ) /************************ 3 bonds ************************/ { switch( num_Up ) { case 0: /* 0 Up */ return T2D_UNDF; case 1: /* 1 Up */ if ( num_Dn ) { #ifdef _DEBUG if ( num_Dn != 1 ) /* debug only */ return -1; #endif ret = (T2D_UNDF | T2D_WARN); } else { dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; if ( dAngle < 0.0 ) dAngle += two_pi; if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) { ret = T2D_OKAY; } else { ret = (T2D_UNDF | T2D_WARN); } } break; case 2: /* 2 Up */ if ( num_Dn ) { dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; if ( dAlpha < 0.0 ) dAlpha += two_pi; if ( dAlpha > one_pi - MIN_ANGLE ) { ret = T2D_OKAY; } else if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) { ret = (T2D_UNDF | T2D_WARN); } else { /* angle between 2 Up bonds is between 120 and 180 degrees */ /* direction of the (Alpha angle bisector) + 180 degrees */ dBisector = dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; dBisector+= dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]]; dBisector/= 2.0; dBisector-= one_pi; if ( dBisector < 0.0 ) { dBisector += two_pi; } if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) { /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ dLimit = MIN_ANGLE * 3.0 / 2.0; } else { dLimit = dAlpha * 3.0 / 2.0 - one_pi; } dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; if ( dBisector - dAngle < -dLimit || dBisector - dAngle > dLimit ) { ret = (T2D_UNDF | T2D_WARN); } else { ret = T2D_OKAY; } } } /* if ( num_Dn ) */ else { ret = T2D_OKAY; } break; case 3: /* 3 Up */ ret = T2D_OKAY; break; default:/* other Up */ return -1; } /* eof switch( num_Up ) at nNumNeigh == 3 */ } else if ( nNumNeigh == 4) /******************************* 4 bonds ********************/ { switch( num_Up ) { case 0: /* 0 Up */ return T2D_UNDF; case 1: /* 1 Up */ if ( num_Dn ) { if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) { /* * Up, In Plane, Dn, In Plane. Undefined if angle between * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval * That is, 138.5 to 221.4 degrees; for certainty the interval is * increased by 5.7 degrees at each end to * 134.8 to 227.1 degrees */ dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; if ( dAngle < 0.0 ) { dAngle += two_pi; } if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { ret = (T2D_UNDF | T2D_WARN); } else { ret = T2D_OKAY; } } else { ret = T2D_OKAY; } #ifdef _DEBUG if ( num_Dn != 1 ) /* debug only */ return -1; #endif } else { ret = T2D_OKAY; dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; if ( dAngle < 0.0 ) { dAngle += two_pi; } if ( dAngle < one_pi - MIN_ANGLE ) { ret |= T2D_WARN; } } break; case 2: /* 2 Up */ #if ( FIX_2D_STEREO_BORDER_CASE == 1 ) if ( len_Up == 1 ) { ret = T2D_OKAY; } else { dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; dAngle = fabs(two_pi - dAngle); dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; dAlpha = fabs(dAlpha); if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) { ret = (T2D_OKAY | T2D_WARN); } else { ret = (T2D_UNDF | T2D_WARN); } } #else if ( bFix2DstereoBorderCase ) { /* bug fix */ if ( len_Up == 1 ) { ret = T2D_OKAY; } else { dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; dAngle = fabs(two_pi - dAngle); dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; dAlpha = fabs(dAlpha); if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) { ret = (T2D_OKAY | T2D_WARN); } else { ret = (T2D_UNDF | T2D_WARN); } } } else { /* original InChI v. 1 bug */ if ( cur_len_Up == 1 ) { ret = T2D_OKAY; } else { ret = (T2D_UNDF | T2D_WARN); } } #endif break; case 3: /* 3 Up */ ret = T2D_OKAY; dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; if ( dAngle < 0.0 ) { dAngle += two_pi; } if ( dAngle < one_pi - MIN_ANGLE ) { ret |= T2D_WARN; } break; case 4: /* 4 Up */ ret = (T2D_UNDF | T2D_WARN); break; default:/* other Up */ return -1; /* program error */ } /* eof switch( num_Up ) at nNumNeigh == 4 */ if ( ret == T2D_OKAY ) { /* check whether all bonds are inside a less than 180 degrees sector */ for ( i = 0; i < nNumNeigh; i ++ ) { dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - dBondDirection[nBondOrder[ i % nNumNeigh]]; if ( dAngle < 0.0 ) { dAngle += two_pi; } if ( dAngle < one_pi - MIN_ANGLE ) { ret |= T2D_WARN; break; } } } } /* eof nNumNeigh == 4 */ else /*************************** number of bonds != 3 or 4 ******************/ { return -1; /* error */ } return ret; } /*************************************************************/ double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) { double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; double prod, ret, central_prod[4]; int bLongEdges; if ( !min_sine ) { return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); } ret = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); sine_value = MPY_SINE * fabs( sine_value ); diff3( at_coord[1], at_coord[0], e[2] ); diff3( at_coord[0], at_coord[2], e[1] ); diff3( at_coord[2], at_coord[1], e[0] ); /* lengths of the 6 edges of the tetrahedra */ e03 = len3( at_coord[0] ); /* 1 */ e13 = len3( at_coord[1] ); e23 = len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ e02 = len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ e12 = len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ e01 = len3( e[2] ); /* min & max edge length */ max_edge_len = min_edge_len = e03; if ( min_edge_len > e13 ) min_edge_len = e13; if ( min_edge_len > e01 ) min_edge_len = e01; min_edge_len_NoExplNeigh = min_edge_len; if ( min_edge_len > e23 ) min_edge_len = e23; if ( min_edge_len > e02 ) min_edge_len = e02; if ( min_edge_len > e12 ) min_edge_len = e12; if ( max_edge_len < e13 ) max_edge_len = e13; if ( max_edge_len < e01 ) max_edge_len = e01; max_edge_len_NoExplNeigh = max_edge_len; if ( max_edge_len < e23 ) max_edge_len = e23; if ( max_edge_len < e02 ) max_edge_len = e02; if ( max_edge_len < e12 ) max_edge_len = e12; if ( !bAddedExplicitNeighbor ) { min_edge_len_NoExplNeigh = min_edge_len; max_edge_len_NoExplNeigh = max_edge_len; } bLongEdges = bAddedExplicitNeighbor? ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { if ( min_sine ) { prod = fabs( ret ); /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ /* 4 heights from each of the 4 vertices to the opposite plane */ s0 = prod / len3( cross_prod3( at_coord[1], at_coord[2], tmp ) ); s1 = prod / len3( cross_prod3( at_coord[0], at_coord[2], tmp ) ); s2 = prod / len3( cross_prod3( at_coord[0], at_coord[1], tmp ) ); s3 = prod / len3( cross_prod3( e[0], e[1], tmp ) ); /* abs. value of a sine of an angle between each tetrahedra edge and plane */ /* sine = height / edge length */ if ( (sine_value = s0/e01) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s0/e02) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s0/e03) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s1/e01) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s1/e12) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s1/e13) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s2/e02) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s2/e12) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s2/e23) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s3/e03) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s3/e13) < min_sine_value ) min_sine_value = sine_value; if ( (sine_value = s3/e23) < min_sine_value ) min_sine_value = sine_value; /* actually use triple sine */ *min_sine = sine_value = MPY_SINE * min_sine_value; } if ( bAmbiguous && sine_value >= MIN_SINE ) { /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ int i; diff3( central_at_coord, at_coord[0], tmp ); central_prod[0] = triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); central_prod[1] = triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); central_prod[2] = triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); central_prod[3] = triple_prod( e[2], e[1], tmp, NULL ); for ( i = 0; i <= 3; i ++ ) { if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { *bAmbiguous |= AMBIGUOUS_STEREO; break; } } } #if ( STEREO_CENTER_BONDS_NORM == 1 ) if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { /* possible planar tetragon */ if ( sine_value < MIN_SINE_SQUARE ) { *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ if ( bAmbiguous && !*bAmbiguous ) { *bAmbiguous |= AMBIGUOUS_STEREO; } } } if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ if ( bAmbiguous && !*bAmbiguous ) { *bAmbiguous |= AMBIGUOUS_STEREO; } } #endif } else if ( min_sine ) { *min_sine = sine_value; } return ret; } /*************************************************************/ double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) { double min_sine_value=9999.0, sine_value; double prod=0.0; if ( !min_sine ) { return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); } prod = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); sine_value = fabs( sine_value ); min_sine_value = inchi_min( min_sine_value, sine_value ); prod = triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); sine_value = fabs( sine_value ); min_sine_value = inchi_min( min_sine_value, sine_value ); prod = triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); sine_value = fabs( sine_value ); min_sine_value = inchi_min( min_sine_value, sine_value ); *min_sine = min_sine_value; return prod; } /*************************************************************/ /* Find if point (0,0,0)a and 3 atoms are in one plane */ int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) { double actual_min_sine; double prod; prod = triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); return actual_min_sine <= min_sine; } /*************************************************************/ /* Find if 4 atoms are in one plane */ int are_4at_in_one_plane( double at_coord[][3], double min_sine) { double actual_min_sine, min_actual_min_sine; double coord[3][3], prod; int i, k, j; for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ for ( i = j = 0; i < 4; i ++ ) { if ( i != k ) { diff3( at_coord[i], at_coord[k], coord[j] ); j ++; } } prod = triple_prod_and_min_abs_sine( coord, &actual_min_sine); if ( !k || actual_min_sine < min_actual_min_sine ) { min_actual_min_sine = actual_min_sine; } } return min_actual_min_sine <= min_sine; } /*************************************************************/ int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, int at_2, int i_next_at_2, S_CHAR *z_dir2 ) { inp_ATOM *at1, *at2; double pnt[3][3], len; int i; int ret = 0; at1 = at + at_1; at2 = at + at[at_1].neighbor[i_next_at_1]; pnt[0][0] = at2->x - at1->x; pnt[0][1] = at2->y - at1->y; pnt[0][2] = at2->z - at1->z; at2 = at + at_2; at1 = at + at[at_2].neighbor[i_next_at_2]; pnt[1][0] = at2->x - at1->x; pnt[1][1] = at2->y - at1->y; pnt[1][2] = at2->z - at1->z; /* * resultant pnt vector directions: * * pnt[0] pnt[1] * * [at_1]---->[...] [...]---->[at_2] * * * add3 below: (pnt[0] + pnt[1]) -> pnt[1] */ add3( pnt[0], pnt[1], pnt[1] ); for ( i = 0; i < 3; i ++ ) { pnt[0][i] = (double)z_dir1[i]; pnt[2][i] = (double)z_dir2[i]; } for ( i = 0; i < 3; i ++ ) { len = len3( pnt[i] ); if ( len < MIN_BOND_LEN ) { if ( i == 1 && (at[at_1].bUsed0DParity || at[at_2].bUsed0DParity) ) { pnt[i][0] = 0.0; pnt[i][1] = 1.0; pnt[i][2] = 0.0; len = 1.0; /* standard at_1-->at_2 vector coordinates in case of 0D allene */ } else { goto exit_function; /* too short bond */ } } mult3( pnt[i], 1.0/len, pnt[i] ); } len = 100.0*triple_prod(pnt[0], pnt[1], pnt[2], NULL ); /* * ^ pnt[0] * | The orientation on this diagram * | produces len = -100 * [at_1]------>[at_2] * pnt[1] / * / * / pnt[2] (up from the plane) * v * * Note: len is invariant upon at_1 <--> at_2 transposition because * triple product changes sign upon pnt[0]<-->pnt[2] transposition and * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: * * triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = * triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) * */ ret = len >= 0.0? (int)(floor(len+0.5)) : -(int)(floor(0.5-len)); exit_function: return ret; } /****************************************************************/ #if ( NEW_STEREOCENTER_CHECK == 1 ) /* { */ /********************************************************************************************/ int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ) { /* RequirdNeighType: reqired neighbor types (bitmap): 0 => any neighbors 1 => no terminal hydrogen atom neighbors 2 => no terminal -X and -XH together (don't care about -X, -XH bond type, charge, radical) (X = tautomeric endpoint atom) NumDbleBonds: if non-zero then allow double, alternating and tautomeric bonds */ int i, j, ni, nj, bond_type, num_1s, num_mult, num_other; if ( at[cur_at].endpoint ) { /* tautomeric endpoint cannot be a stereo center */ return 0; } if ( (1 & RequirdNeighType) && at[cur_at].num_H ) { return 0; } if ( 2 & RequirdNeighType ) { for ( i = 0; i < at[cur_at].valence; i ++ ) { ni = (int)at[cur_at].neighbor[i]; if ( at[ni].valence != 1 || !get_endpoint_valence( at[ni].el_number ) ) { continue; } for ( j = i+1; j < at[cur_at].valence; j ++ ) { nj = (int)at[cur_at].neighbor[j]; if ( at[nj].valence != 1 || at[ni].el_number != at[nj].el_number || !get_endpoint_valence( at[nj].el_number ) ) { continue; } /* * if (at[ni].num_H != at[nj].num_H) then the atoms (neighbors of at[cur_at] * are tautomeric endpoints and are indistinguishable => cur_at is not stereogenic * if (at[ni].num_H == at[nj].num_H) then the neighbors are indistinguishable * and cur_at will be found non-sterogenic later * get_endpoint_valence() check will not allow the neighbors to be carbons * Therefore the following "if" is not needed; we may just return 0. */ if ( at[ni].num_H != at[nj].num_H && strcmp(at[ni].elname, "C" ) ) { return 0; /* found -X and -XH neighbors */ } } } } num_1s = num_mult = num_other = 0; for ( i = 0; i < at[cur_at].valence; i ++ ) { bond_type = (at[cur_at].bond_type[i] & ~BOND_MARK_ALL); switch( bond_type ) { case BOND_SINGLE: num_1s ++; break; case BOND_DOUBLE: case BOND_ALTERN: case BOND_TAUTOM: case BOND_ALT12NS: num_mult ++; break; default: num_other ++; break; } } if ( num_other ) { return 0; } if ( NumDbleBonds && NumDbleBonds > num_mult || !NumDbleBonds && at[cur_at].valence != num_1s ) { return 0; } return 1; } /********************************************************************************************/ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) { /************************************************************************************* * current version ************************************************************************************* * Use #define to split the stereocenter description table into parts * to make it easier to read * * --------- 4 single bonds stereocenters ------- * 0 1 2 3 4 5 * * | | | | | | * -C- -Si- -Ge- -Sn- >As[+] >B[-] * | | | | | | */ #define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", #define CCHARGE1 0, 0, 0, 0, 1, -1, #define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, #define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, #define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, #define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, /* * --------------- S, Se stereocenters ---------- * 6 7 8 9 10 11 12 13 * * | | || | | || * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] * | | | | | | | | */ #define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", #define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, #define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, #define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, #define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, #define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, /* * ------------------ N, P stereocenters ----------------- * 14 15 16 17 18 19 20 * * Phosphine Arsine * X---Y * | | \ / | | \ / \ / * =N- >N[+] N >P[+] =P- P As * | | | | | | | */ #define SZELEM3 "N\000","N\000","N\000","P\000","P\000","P\000", "As", #define CCHARGE3 0, 1, 0, 1, 0, 0, 0, #define CNUMBONDSANDH3 4, 4, 3, 4, 4, 3, 3, #define CCHEMVALENCEH3 5, 4, 3, 4, 5, 3, 3, #define CHAS3MEMBRING3 0, 0, 1, 0, 0, 0, 0, #define CREQUIRDNEIGH3 3, 3, 1, 3, 3, 2, 2, #define PHOSPHINE_STEREO 19 /* the number must match Phosphine number in the comments, see above */ #define ARSINE_STEREO 20 /* the number must match Arsine number in the comments, see above */ static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; static S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; static S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; static S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; static S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; static int n = sizeof(szElem)/sizeof(szElem[0]); /* reqired neighbor types (bitmap): 0 => check bonds only 1 => no terminal hydrogen atom neighbors 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) (X = tautomeric endpoint atom) Note: whenever cChemValenceH[] > cNumBondsAndH[] the tautomeric and/or alternating bonds are permitted */ int i, ret = 0; for ( i = 0; i < n; i++ ) { if ( !strcmp( at[cur_at].elname, szElem[i]) && at[cur_at].charge == cCharge[i] && (!at[cur_at].radical || at[cur_at].radical == 1) && at[cur_at].valence +at[cur_at].num_H == cNumBondsAndH[i] && at[cur_at].chem_bonds_valence+at[cur_at].num_H == cChemValenceH[i] && (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { ret = cNumBondsAndH[i]; break; } } if ( i == PHOSPHINE_STEREO && !(bPointedEdgeStereo & PES_BIT_PHOSPHINE_STEREO) ) ret = 0; if ( i == ARSINE_STEREO && !(bPointedEdgeStereo & PES_BIT_ARSINE_STEREO) ) ret = 0; return ret; } #else /* } NEW_STEREOCENTER_CHECK { */ /********************************************************************************************/ int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ) { static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" }; static const S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 }; int i, ret = 0; for ( i = 0; i < sizeof(szElem)/sizeof(szElem[0]); i++ ) { if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { ret = (!radical || radical == RADICAL_SINGLET); break; } } return ret; } #endif /* } NEW_STEREOCENTER_CHECK */ /****************************************************************/ /* used for atoms adjacent to stereogenic bonds only */ int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ) { static const char szElem[][3] = { "N\000" }; static const S_CHAR cCharge[] = { 0, }; int i, ret = 0; for ( i = 0; i < (int)(sizeof(szElem)/sizeof(szElem[0])); i++ ) { if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { ret = ( !radical || radical == RADICAL_SINGLET ); break; } } return ret; } /****************************************************************/ /* used for atoms adjacent to stereogenic bonds only */ int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ) { static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" }; static const S_CHAR cCharge[] = { 0, 0, 0, 0, 1, }; static const int n = sizeof(szElem)/sizeof(szElem[0]); int i, ret = 0; for ( i = 0; i < n; i++ ) { if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { ret = (!radical || radical == RADICAL_SINGLET); break; } } return ret; } /****************************************************************/ /* used for atoms adjacent to stereogenic bonds only */ int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ) { static const char szElem[][3] = { "C\000", "Si", "Ge", }; static const S_CHAR cCharge[] = { 0, 0, 0, }; static const int n = sizeof(szElem)/sizeof(szElem[0]); int i, ret = 0; for ( i = 0; i < n; i++ ) { if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { ret = (!radical || radical == RADICAL_SINGLET); break; } } return ret; } /*****************************************************************/ int bIsSuitableHeteroInpAtom( inp_ATOM *at ) { int val, num_H; if ( 0 == at->charge && (!at->radical || RADICAL_SINGLET == at->radical) && 0 < (val=get_endpoint_valence( at->el_number ) )) { num_H = at->num_H; if ( val == at->chem_bonds_valence + num_H ) { switch( val ) { case 2: /* O */ if ( !num_H && 1 == at->valence ) return 0; /* =O */ break; /* not found */ case 3: /* N */ if ( 1 == at->valence && 1 == num_H || 2 == at->valence && 0 == num_H ) return 1; /* =N- or =NH */ break; /* not found */ } } } return -1; } /****************************************************************/ int bIsOxide( inp_ATOM *at, int cur_at ) { int i, bond_type; inp_ATOM *a = at + cur_at, *an; for ( i = 0; i < a->valence; i ++ ) { bond_type = (a->bond_type[i] &= ~BOND_MARK_ALL); if ( bond_type == BOND_DOUBLE ) { an = at + (int)a->neighbor[i]; if ( 1 == an->valence && !an->charge && !an->num_H && !an->radical && 2 == get_endpoint_valence( an->el_number ) ) { return 1; } } else if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALT12NS ) { an = at + (int)a->neighbor[i]; if ( 1 == an->valence && 2 == get_endpoint_valence( an->el_number ) ) { return 1; } } } return 0; } /****************************************************************/ /* used for atoms adjacent to stereogenic bonds only */ int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ) { static const char szElem[][3] = { "C\000", "Si", "Ge", }; static const S_CHAR cCharge[] = { 0, 0, 0, }; static const int n = sizeof(szElem)/sizeof(szElem[0]); int i, ret = 0; for ( i = 0; i < n; i++ ) { if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { ret = (!radical || radical == RADICAL_SINGLET); break; } } return ret; } /************************************************************************/ int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], int nNumExplictAttachments, int bond_parity, int nFlag ) { int m, last_parity, cur_parity; int i, icur2nxt, icur2neigh, cur_order_parity, nxt_at; AT_NUMB nNextSbAtOrigNumb; /* find atom parities for all valid streobonds incident to at[cur_at] */ for ( m = 0, last_parity = 0; m < MAX_NUM_STEREO_BONDS && at[cur_at].sb_parity[m]; m ++ ) { icur2nxt = icur2neigh = -1; /* ordering number of neighbors in nSbNeighOrigAtNumb[] */ cur_parity = 0; /* parity for mth stereobond incident to the cur_at */ if ( 0 <= at[cur_at].sb_ord[m] && at[cur_at].sb_ord[m] < at[cur_at].valence && 0 <= (nxt_at = at[cur_at].neighbor[(int)at[cur_at].sb_ord[m]]) && at[nxt_at].valence <= MAX_NUM_STEREO_BONDS && /* make sure it is a valid stereobond */ (nNextSbAtOrigNumb = at[nxt_at].orig_at_number) ) { /* since at[cur_at].sn_ord[m] = -1 for explicit H use at[cur_at].sn_orig_at_num[m] */ for ( i = 0; i < nNumExplictAttachments; i ++ ) { if ( at[cur_at].sn_orig_at_num[m] == nSbNeighOrigAtNumb[i] ) { icur2neigh = i; /* neighbor */ } else if ( nNextSbAtOrigNumb == nSbNeighOrigAtNumb[i] ) { icur2nxt = i; /* atom connected by a stereobond */ } } if ( icur2neigh >= 0 && icur2nxt >= 0 ) { if ( ATOM_PARITY_WELL_DEF(at[cur_at].sb_parity[m]) ) { /* parity of at[cur_atom] neighbor permutation to reach this order: { next_atom, neigh_atom, ...} */ cur_order_parity = (icur2nxt + icur2neigh + (icur2nxt > icur2neigh) - 1) % 2; cur_parity = 2 - (cur_order_parity + at[cur_at].sb_parity[m]) % 2; } else { /* unknowm/undef parities do not depend on the neighbor order */ cur_parity = at[cur_at].sb_parity[m]; } } } else { continue; } /* use a well-known parity if available; if not then use preferably the unknown */ if ( !last_parity ) { last_parity = cur_parity; } else if ( last_parity != cur_parity && cur_parity ) { if ( ATOM_PARITY_WELL_DEF(last_parity) ) { if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { last_parity = 0; /* error: all well-defined parities should be same */ break; } } else if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { /* replace unknown/undefined parity with well-known */ last_parity = cur_parity; } else { /* select min unknown/undefined parity (out of AB_PARITY_UNKN and AB_PARITY_UNDF) */ last_parity = inchi_min(cur_parity, last_parity); } } } if ( last_parity ) { bond_parity = last_parity; at[cur_at].bUsed0DParity |= nFlag; /* set flag: used stereobond 0D parity */ } return bond_parity; } /*******************************************************************************************/ int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, int at_1, int i_next_at_1, S_CHAR z_dir1[], int at_2, int i_next_at_2, S_CHAR z_dir2[], int *pparity1, int *pparity2 ) { int k, parity1, parity2, abs_parity1, abs_parity2; int j1, j2, parity_sign; /* AT_NUMB nSbNeighOrigAtNumb1[MAX_NUM_STEREO_BOND_NEIGH], nSbNeighOrigAtNumb2[MAX_NUM_STEREO_BOND_NEIGH]; int nNumExplictAttachments1, nNumExplictAttachments2; */ parity1 = parity2 = AB_PARITY_NONE; j1 = j2 = -1; parity_sign = ( *pparity1 < 0 || *pparity2 < 0 )? -1 : 1; abs_parity1 = abs(*pparity1); abs_parity2 = abs(*pparity2); for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { if ( at[at_1].sb_ord[k] == i_next_at_1 ) { parity1 = at[at_1].sb_parity[k]; j1 = k; } } for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_2].sb_parity[k]; k ++ ) { if ( at[at_2].sb_ord[k] == i_next_at_2 ) { parity2 = at[at_2].sb_parity[k]; j2 = k; } } switch( (j1 >= 0) + 2*(j2 >= 0) ) { case 0: /* the bond has no 0D parity */ *pparity1 = *pparity2 = parity_sign * AB_PARITY_UNDF; return 0; case 1: case 2: /* 0D parity data error */ *pparity1 = *pparity2 = AB_PARITY_NONE; return -1; case 3: /* the bond has 0D parity */ switch ( !(ATOM_PARITY_WELL_DEF( abs_parity1 ) && ATOM_PARITY_WELL_DEF( parity1 )) + 2 * !(ATOM_PARITY_WELL_DEF( abs_parity2 ) && ATOM_PARITY_WELL_DEF( parity2 )) ) { case 0: /* both parities are well-defined; continue */ break; case 1: /* 0D parity not well-defined for at_1 */ *pparity1 = parity_sign * (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : inchi_min(abs_parity1, parity1)); *pparity2 = parity_sign * abs_parity2; return -1; case 2: /* 0D parity not well-defined for at_2 */ *pparity1 = parity_sign * abs_parity1; *pparity2 = parity_sign * (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : inchi_min(abs_parity2, parity2)); return -1; case 3: abs_parity1 = (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : inchi_min(abs_parity1, parity1)); abs_parity2 = (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : inchi_min(abs_parity2, parity2)); *pparity1 = *pparity2 = parity_sign * inchi_min(abs_parity1, abs_parity2); /*return (parity1 == parity2)? 0 : -1;*/ return -1; } break; } /* we are here if both end-atoms of the bond have well-defined 0D parities */ /* nNumExplictAttachments1 = GetSbNeighOrigAtNumb( at, at_1, at_removed_H, num_removed_H, nSbNeighOrigAtNumb1 ); nNumExplictAttachments2 = GetSbNeighOrigAtNumb( at, at_2, at_removed_H, num_removed_H, nSbNeighOrigAtNumb2 ); parity1 = GetHalfStereobond0DParity( at, at_1, nSbNeighOrigAtNumb1, nNumExplictAttachments1, *pparity1, 0 ); parity2 = GetHalfStereobond0DParity( at, at_2, nSbNeighOrigAtNumb2, nNumExplictAttachments2, *pparity2, 0 ); */ *pparity1 = parity_sign * abs_parity1; *pparity2 = parity_sign * abs_parity2; if ( chain_length % 2 ) { /* allene; chain_length = (number of double bonds) - 1 */ /* int zer1 = ( !z_dir1[0] && !z_dir1[1] && !z_dir1[2] ); int zer2 = ( !z_dir2[0] && !z_dir2[1] && !z_dir2[2] ); */ int bWrong_z_dir1 = (0 != (at[at_1].bUsed0DParity & FlagSB_0D)); int bWrong_z_dir2 = (0 != (at[at_2].bUsed0DParity & FlagSB_0D)); if ( bWrong_z_dir1 && bWrong_z_dir2 ) { goto set_default; } else if ( bWrong_z_dir1 || bWrong_z_dir2 ) { double r12[3], zi1[3], zi2[3], abs_r12, abs_zi2; int at_i1, at_i2, j; S_CHAR z_dir[3]; r12[0] = at[at_2].x - at[at_1].x; r12[1] = at[at_2].y - at[at_1].y; r12[2] = at[at_2].z - at[at_1].z; abs_r12 = len3( r12 ); if ( abs_r12 < MIN_BOND_LEN ) { goto set_default; } /* make r12[] point to the atom with 'good' z_dir[] */ if ( bWrong_z_dir1 ) { at_i1 = at_2; /* has good z_dir2[] */ at_i2 = at_1; /* has bad z_dir1[] */ zi1[0] = z_dir2[0]; zi1[1] = z_dir2[1]; zi1[2] = z_dir2[2]; mult3( r12, 1.0/abs_r12, r12 ); /* make length = 1 */ } else { at_i1 = at_1; /* has good z_dir1[] */ at_i2 = at_2; /* has bad z_dir2[] */ zi1[0] = z_dir1[0]; zi1[1] = z_dir1[1]; zi1[2] = z_dir1[2]; mult3( r12, -1.0/abs_r12, r12 ); /* make length = 1 */ } cross_prod3( r12, zi1, zi2 ); abs_zi2 = len3( zi2 ); mult3( zi2, 100.0/abs_zi2, zi2 ); /* make length = 100 */ for ( j = 0; j < 3; j ++ ) { z_dir[j] = (S_CHAR) (zi2[j]>= 0.0? floor(0.5 + zi2[j]) : -floor(0.5 - zi2[j])); /* abs(z_dir) = 100 */ } if ( bWrong_z_dir1 ) { memcpy( z_dir1, z_dir, sizeof(z_dir) ); } else { memcpy( z_dir2, z_dir, sizeof(z_dir) ); } } return 0; set_default: /* z_dir1[] = x-direction; z_dir2[] = z-direction; r12[] = y-direction */ z_dir1[0] = 100; z_dir1[1] = z_dir1[2] = 0; z_dir2[0] = z_dir2[1] = 0; z_dir2[2] = 100; } return 0; } /**********************************************************/ /* without this InChI fails on reconstructed CID=450438 */ /* (isotopic, Unknown SB adjacent to SB with known parity) */ /**********************************************************/ int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at) { int i, m, num=0; /* add usual Unknown stereobond descriptors to each Unknown bond */ for( i = 0; i < num_at; i ++ ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { at[i].bond_stereo[ (int)at[i].sb_ord[m] ] = STEREO_DBLE_EITHER; num ++; } } } #ifdef NEVER if ( num ) { int j; /* how to remove Unknown stereo bond parities */ for( i = 0; i < num_at; i ++ ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { for ( j = m+1; j < MAX_NUM_STEREO_BONDS; j ++ ) { at[i].sb_parity[j-1] = at[i].sb_parity[j]; at[i].sb_ord[j-1] = at[i].sb_ord[j]; at[i].sn_ord[j-1] = at[i].sn_ord[j]; at[i].sn_orig_at_num[j-1] = at[i].sn_orig_at_num[j]; } at[i].sb_parity[j-1] = 0; at[i].sb_ord[j-1] = 0; at[i].sn_ord[j-1] = 0; at[i].sn_orig_at_num[j-1] = 0; } } } } #endif return num; } /*====================================================================================================== half_stereo_bond_parity() General Description: A) find projections of 3 bonds on a reasonable plane defined by a vector z_dir perpendicular to the plane B) calculate parity half_stereo_bond_parity() Detailed Description: 1) Find at_coord[] = vectors from the central atoms to its neighbors 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor (an implicit H or a fictitious atom in case of =NX) coordinates 3) Normalize at_coord[] to unit length 4) Find unit vector pnt[2] perpendicular to the plane containing at_coord[] arrow ends. Even though it is not necessary, make z-coordinate of pnt[2] positive. ** pnt[2] has the new z-axis direction ** 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; Normalize pnt[0] to unit length. ** pnt[0] has the new x-axis direction ** 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); ** pnt[1] has the new y-axis direction ** 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections to a unit length 8) In the new xy-plane find (counterclockwise) angles: tmp1 = (from at_coord[0] to at_coord[1]) tmp2 = (from at_coord[0] to at_coord[2]) 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors are in clockwise order) 10) Calculate z_dir = 100*pnt[2]. Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately opposite directions (that is, their dot-product is negative) then the parity of the stereogenic bond calculated from half-bond-parities should be inverted Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product is invariant with respect to the atom numbering because it does not change upon (1,2) permutation. Stereo ambiguity in case of 2 neighbors: ---------------------------------------- Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad Return values (cases: I=only in case of isotopic H atoms the neighbors are different, N=in case of non-isotopic H atoms the neighbors are different) -4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, I -3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I -2 =-AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I -1 =-AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I 0 = AB_PARITY_NONE => the atom is not adjacent to a stereogenic bond 1 = AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I 2 = AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I 3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I 4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I 5 = AB_PARITY_IISO => atom constitutionally equivalent to this atom may be adjacent to a stereogenic bond, I =====================================================================================================*/ int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, int bPointedEdgeStereo, int vABParityUnknown ) { double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; double temp[3], pnt[3][3]; int j, k, p0, p1, p2, next, bValence3=0, num_z, nType, num_either_single, num_either_double; int nNumExplictAttachments; int bond_parity = AB_PARITY_UNDF; int num_H=0, num_iH, num_eH=0, num_nH=0 /* = num_iso_H[0] */; int num_iso_H[NUM_H_ISOTOPES+1]; int index_H[5]; /* cannot have more than 4 elements: 1 H, 1 1H, 1 D, 1 T atom(s) */ /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ const double one_pi = 3.14159265358979323846; /* M_PI */ const double two_pi = 2.0*one_pi; int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { z_dir[2]=100; } num_H = at[cur_at].num_H; if ( num_H > NUM_H_ISOTOPES ) return 0; /* at least 2 H atoms are isotopically identical */ if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence + num_H || MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence + num_H ) return 0; if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) return 0; if ( !bIgnoreIsotopicH ) { for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { if ( (k = (int)at[cur_at].num_iso_H[j]) > 1 ) { return AB_PARITY_IISO; /* two or more identical isotopic H atoms */ } num_nH -= k; } } /* at this point num_nH = number of non-isotopic H atoms */ if ( num_nH > 1 ) return AB_PARITY_IISO; /* two or more identical non-isotopic H atoms */ if ( num_nH < 0 ) return CT_ISO_H_ERR; /* program error */ /* */ /******************************************************************** * Note. At this point all (implicit and explicit) isotopic * terminal H neighbors are either different or not present. ********************************************************************/ /* locate explicit hydrogen atoms */ /* (at_removed_H are sorted in ascending isotopic H mass order, non-isotopic first) */ memset( num_iso_H, 0, sizeof(num_iso_H) ); if ( at_removed_H && num_removed_H > 0 ) { for ( j = 0; j < num_removed_H; j ++ ) { if ( at_removed_H[j].neighbor[0] == cur_at ) { k = bIgnoreIsotopicH? 0 : at_removed_H[j].iso_atw_diff; if ( 0 <= k && k <= NUM_H_ISOTOPES ) { if ( ++num_iso_H[k] > 1 ) /* num_iso_H[0] = number of non-isotopic H atoms */ return CT_ISO_H_ERR; /* program error in counting hydrogens */ /* */ index_H[num_eH++] = j; } else { return CT_ISO_H_ERR; /* program error */ /* */ } } } num_iH = num_H - num_eH; /* number of implicit non-isotopic and isotopic H atoms */ if ( num_iH > 1 ) { /* more than one implicit H: cannot reconstruct the geometry */ bond_parity = -AB_PARITY_UNDF; goto exit_function; } } else { num_iH = num_H; } /* at this point num_iH = number of implicit non-isotopic and isotopic H atoms */ if ( at[cur_at].valence + num_eH < MIN_NUM_STEREO_BOND_NEIGH ) { /* =NH or =CHD when no explicit H is present */ return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; } bValence3 = bAtomHasValence3( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ); /* * Can one explicit hydrogen be added to make asymmetric configuration? * For now we can add 1 H atom in case of an appropriate geometry if: * (a) one non-isotopic H (even if explicit isotopic H atoms are present), or * (b) one isotopic or non-isotopic H if NO explicit isotopic or non-isotopic H atom is present * This makes sense only in case chem. valence = 4. In case of chem. valence = 3, do not check. */ if ( at[cur_at].valence + num_eH == MIN_NUM_STEREO_BOND_NEIGH && !bValence3 && !(/*(a)*/ 1 == num_nH && !num_iso_H[0] || /*(b)*/ 1 == num_H && !num_eH) ) { goto exit_function; /* return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; */ } /* store neighbors coordinates */ num_z = num_either_single = num_either_double = 0; for ( k = nNumExplictAttachments = 0; k < 2; k ++ ) { switch( k ) { case 0: for ( j = 0; j < num_eH; j ++, nNumExplictAttachments ++ ) { next = index_H[j]; at_coord[nNumExplictAttachments][0] = at_removed_H[next].x - at[cur_at].x; at_coord[nNumExplictAttachments][1] = at_removed_H[next].y - at[cur_at].y; nSbNeighOrigAtNumb[nNumExplictAttachments] = at_removed_H[next].orig_at_number; /* use the fact that (at_removed_H - at) = (number of atoms except removed explicit H) */ z = -get_z_coord( at, (at_removed_H-at)+next, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); switch ( nType ) { case ZTYPE_EITHER: num_either_single ++; /* bond in "Either" direction. */ break; case ZTYPE_UP: case ZTYPE_DOWN: nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ z = len2( at_coord[nNumExplictAttachments] ); /* z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); */ if ( nType == ZTYPE_DOWN ) z = -z; /* no break; here */ case ZTYPE_3D: num_z ++; } at_coord[nNumExplictAttachments][2] = z; } break; case 1: for ( j = 0; j < at[cur_at].valence; j ++, nNumExplictAttachments ++ ) { next = at[cur_at].neighbor[j]; at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; nSbNeighOrigAtNumb[nNumExplictAttachments] = at[next].orig_at_number; z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); switch ( nType ) { case ZTYPE_EITHER: num_either_single ++; /* bond in "Either" direction. */ break; case ZTYPE_UP: case ZTYPE_DOWN: z = len2( at_coord[nNumExplictAttachments] ); /* z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); */ if ( nType == ZTYPE_DOWN ) z = -z; /* no break; here */ case ZTYPE_3D: num_z ++; } at_coord[nNumExplictAttachments][2] = z; } break; } } if ( num_either_single ) { bond_parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* single bond is 'unknown' */ goto exit_function; } /* nNumExplictAttachments is a total number of attachments, including removed explicit terminal hydrogens */ if ( nNumExplictAttachments == 2 ) { /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ /* coord[2][], attached to the cur_at. */ for ( j = 0; j < 3; j ++ ) { at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); } nSbNeighOrigAtNumb[nNumExplictAttachments] = 0; /* implicit H or lone pair */ } for ( j = 0; j < 3; j ++ ) { tmp[j] = len3( at_coord[j] ); } min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { /* all bonds or some of bonds are too short */ if ( at[cur_at].sb_parity[0] ) { /* use bond psrity; the reconciliation in ReconcileAllCmlBondParities() * has made all ways to calculate parity produce same result */ bond_parity = GetHalfStereobond0DParity( at, cur_at, nSbNeighOrigAtNumb, nNumExplictAttachments, bond_parity, FlagSB_0D ); } goto exit_function; } /* normalize lengths to 1 */ for ( j = 0; j < 3; j ++ ) { mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); } /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ for ( j = 0; j < 3; j ++ ) { /* pnt[0..2] = {0-1, 1-2, 2-0} */ tmp[j] = len3(diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); if ( tmp[j] < MIN_SINE ) { goto exit_function; /* angle #i-cur_at-#j is too small */ } mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ } /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ tmp1 = len3( cross_prod3( pnt[0], pnt[1], temp ) ); for (j = 1, k = 0; j < 3; j ++ ) { tmp2 = len3( cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); if ( tmp2 > tmp1 ) { tmp1 = tmp2; k = j; } } /* previously p0=0, p1=1, p2=2 */ p0 = k; p1 = (k+1)%3; p2 = (k+2)%3; tmp[p2] = len3( cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ } /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ min_tmp = dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ tmp[p0] = len3(diff3( at_coord[0], pnt[p0], pnt[p0] )); mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ for ( j = 0; j < 3; j ++ ) { copy3( at_coord[j], temp ); for ( k = 0; k < 3; k ++ ) { at_coord[j][k] = dot_prod3( temp, pnt[(k+p0)%3] ); } /* new xy plane projection length */ tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); /* make new xy plane projection length = 1 */ mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); } s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ if ( s < MIN_SINE && c > 0.5 ) { goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ } c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ s = at_coord[0][1]; /* sine. Should be 0 */ /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; at_coord[j][0] = tmp1; at_coord[j][1] = tmp2; } /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); if ( tmp1 < 0.0 ) tmp1 += two_pi; /* range 0 to 2*pi */ if ( tmp2 < 0.0 ) tmp2 += two_pi; /*----------------------------------- Example 1 \ case tmp1 < tmp2 \ parity is odd \ (counterclockwise) A------- 0 / / 2 / ------------------------------------*/ bond_parity = 2 - ( tmp1 < tmp2 ); for ( j = 0; j < 3; j ++ ) { z_dir[j] = (S_CHAR) (pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ } /* check for ambiguity */ if ( nNumExplictAttachments > 2 ) { min_tmp = inchi_min( tmp1, tmp2 ); max_tmp = inchi_max( tmp1, tmp2 ); if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; } else /* 3D ambiguity 8-28-2002 */ if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; } } else if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ min_tmp = fabs(tmp1 - one_pi); if ( min_tmp < MIN_SINE ) { bond_parity = AB_PARITY_UNDF; /* consider as undefined 10-6-2003 */ } else if ( min_tmp < MIN_ANGLE_DBOND ) { at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; } } /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ /* can be done in 2 transpositions and does not change atom's parity */ exit_function: if ( num_H > 1 && bond_parity > 0 && !(bond_parity & AB_PARITY_0D) /*&& PARITY_WELL_DEF(bond_parity)*/ ) { /* * stereo only if isotopes are counted. Do not inverse * Examples: sign for this: * H D * / / H * ==C or ==CH / * \ ==N (bValence3=1) * D * two explicit one explicit H isotope (D), * isotopic H atoms one implicit H */ bond_parity = -bond_parity; /* refers to isotopically substituted structure only */ } return bond_parity; } /*************************************************************/ int save_a_stereo_bond( int z_prod, int result_action, int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ) { int i1, i2; for ( i1 = 0; i1 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor1[i1]; i1 ++ ) ; for ( i2 = 0; i2 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor2[i2]; i2 ++ ) ; if ( i1 == MAX_NUM_STEREO_BONDS || i2 == MAX_NUM_STEREO_BONDS ) return 0; stereo_bond_parity1[i1] = stereo_bond_parity2[i2] = result_action; stereo_bond_neighbor1[i1] = (AT_NUMB) (at2+1); stereo_bond_ord1[i1] = (S_CHAR)ord1; stereo_bond_neighbor2[i2] = (AT_NUMB) (at1+1); stereo_bond_ord2[i2] = (S_CHAR)ord2; stereo_bond_z_prod1[i1] = stereo_bond_z_prod2[i2] = (S_CHAR)z_prod; return 1; } /***************************************************************/ int get_allowed_stereo_bond_type( int bond_type ) { #if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 0 ) if ( (bond_type & ~BOND_MARK_ALL) == BOND_TAUTOM ) return 0; /* no tautomer bonds allowed */ else #endif #if ( EXCL_ALL_AROM_BOND_PARITY == 1 ) /* { */ /* a stereo bond cannot belong to an aromatic atom */ if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) { return 0; } #else /* } { */ #if ( ADD_6MEMB_AROM_BOND_PARITY == 1 ) /* accept any aromatic bond as a stereo bond */ if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) #else /* accept only aromatic bonds in non-6-member rings */ if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) ) #endif { return BOND_ALTERN; } #endif /* } */ else /* at this point BOND_MARK_ALL bits have been removed from bond_type */ if ( bond_type == BOND_DOUBLE || bond_type == BOND_SINGLE ) { return bond_type; } #if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 1 ) else if ( bond_type == BOND_TAUTOM ) { return BOND_TAUTOM; } #endif return 0; /* wrong bond type */ } /*************************************************************/ int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ) { int i, j, next_at, num_stereo_bonds, bFound; int bond_type, num_2s, num_alt; int num_2s_next, num_alt_next, num_wrong_bonds_1, num_wrong_bonds_2; #if ( N_V_STEREOBONDS == 1 ) int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; #endif if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence+at[cur_at].num_H || MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence+at[cur_at].num_H ) return 0; if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) return 0; /* count bonds and find the second atom on the stereo bond */ num_2s = num_alt = num_wrong_bonds_1 = 0; #if ( N_V_STEREOBONDS == 1 ) num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; if ( 0 == at[cur_at].num_H && 0 == at[cur_at].charge && 0 == at[cur_at].radical && 3 == get_endpoint_valence( at[cur_at].el_number ) ) { if ( 2 == at[cur_at].valence && 3 == at[cur_at].chem_bonds_valence ) { type_N = 1; } else if ( 3 == at[cur_at].valence && 5 == at[cur_at].chem_bonds_valence ) { type_N = 2; /* unfortunately includes >N# */ } } #endif for ( i = 0, num_stereo_bonds = 0; i < at[cur_at].valence; i ++ ) { bFound = 0; next_at = at[cur_at].neighbor[i]; bond_type = get_allowed_stereo_bond_type( (int)at[cur_at].bond_type[i] ); if ( bond_type == BOND_ALTERN ) { num_alt ++; if ( cur_at > next_at && !(nMode & CMODE_NO_ALT_SBONDS) ) bFound = 1; } else if ( bond_type == BOND_DOUBLE ) { num_2s ++; #if ( N_V_STEREOBONDS == 1 ) if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_at )) ) { num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ } #endif if ( cur_at > next_at ) bFound = 1; } else if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { num_wrong_bonds_1 ++; #if ( ONE_BAD_SB_NEIGHBOR == 1 ) if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { return 0; /* wrong bond type */ } else { continue; } #else return 0; /* wrong bond type */ #endif } if ( bFound ) { /* check "next_at" atom on the opposite side of the bond */ if ( MAX_NUM_STEREO_BOND_NEIGH < at[next_at].valence+at[next_at].num_H || MIN_NUM_STEREO_BOND_NEIGH > at[next_at].valence+at[next_at].num_H ) continue; if ( !bCanAtomHaveAStereoBond( at[next_at].elname, at[next_at].charge, at[next_at].radical ) ) continue; /* next atom neighbors */ num_2s_next = num_alt_next = num_wrong_bonds_2 = 0; #if ( N_V_STEREOBONDS == 1 ) num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; if ( 0 == at[next_at].num_H && 0 == at[next_at].charge && 0 == at[next_at].radical && 3 == get_endpoint_valence( at[next_at].el_number ) ) { if ( 2 == at[next_at].valence && 3 == at[next_at].chem_bonds_valence ) { type_N_next = 1; /* -N= */ } else if ( 3 == at[next_at].valence && 5 == at[next_at].chem_bonds_valence ) { type_N_next = 2; /* unfortunately includes >N# */ } } #endif for ( j = 0; j < at[next_at].valence; j ++ ) { bond_type = get_allowed_stereo_bond_type( (int)at[next_at].bond_type[j] ); if ( bond_type == BOND_ALTERN ) num_alt_next ++; else if ( bond_type == BOND_DOUBLE ) { num_2s_next ++; #if ( N_V_STEREOBONDS == 1 ) next_next_at = at[next_at].neighbor[j]; if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ } #endif } else if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { num_wrong_bonds_2 ++; #if ( ONE_BAD_SB_NEIGHBOR == 1 ) if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { break; /* wrong bond type */ } else { continue; } #else break; /* wrong bond type */ #endif } } /* figure out whether the at[cur_at]--at[next_at] bond may not be stereogenic */ #if ( N_V_STEREOBONDS == 1 ) if ( 3 == (type_N | type_N_next) && ( 2 == type_N && !bIsOxide( at, cur_at ) || 2 == type_N_next && !bIsOxide( at, next_at ) ) ) { bFound = 0; } else #endif if ( j < at[next_at].valence || /* at[next_at] has a wrong bond type*/ (num_alt_next>0) + (num_2s_next>0) != 1 /* only one type of stereogenic bond permitted */ ) { bFound = 0; } else if ( 2 < num_2s_next ) { bFound = 0; } else if ( 2 == num_2s_next ) { if ( 2 == at[next_at].valence ) { ; /* only one double bond permitted except cumulenes */ #if ( N_V_STEREOBONDS == 1 ) } else if ( 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && 3 == at[next_at].valence + at[next_at].num_H && 5 == at[next_at].chem_bonds_valence + at[next_at].num_H && 3 == get_endpoint_valence( at[next_at].el_number ) && (!type_N || bIsOxide( at, next_at )) ) { ; /* * found: * * \ / \ / \ / * \ / \ / \ / * N==C or N==C or N==N * // \ // \ // \ * O ^ \ N ^ \ O ^ \ * | | | * | | | * at[next_at] at[next_at] at[next_at] */ #endif } else { bFound = 0; } } } if ( bFound ) { num_stereo_bonds++; } } if ( (num_alt>0) + (num_2s>0) != 1 || !num_stereo_bonds ) return 0; if ( num_2s > 1 ) { #if ( N_V_STEREOBONDS == 1 ) if ( 2 == num_2s && 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && 3 == at[cur_at].valence + at[cur_at].num_H && 5 == at[cur_at].chem_bonds_valence + at[cur_at].num_H && 3 == get_endpoint_valence( at[cur_at].el_number ) ) { ; } else { return 0; } #else return 0; #endif } return num_stereo_bonds; } /*************************************************************/ int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ) { #define AB_NEGATIVE 0x10 #define AB_UNKNOWN 0x20 int nAction; if ( nParity == AB_PARITY_NONE ) return AB_PARITY_NONE; /* Unknown (type 1) in the parity value may come from the 'Either' single bond only */ /* Treat it as a known single bond geometry and unknown (Either) double bond */ if ( nParity == vABParityUnknown /*AB_PARITY_UNKN*/ ) nParity = AB_PARITY_ODD | AB_UNKNOWN; if ( nParity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) nParity = AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE; /* make positive, replace AB_PARITY_EVEN with AB_PARITY_ODD */ if ( nParity < 0 ) nParity = ((nParity == -AB_PARITY_EVEN)? AB_PARITY_ODD : (-nParity)) | AB_NEGATIVE; else if (nParity == AB_PARITY_EVEN) nParity = AB_PARITY_ODD; /* Unknown (type 2): was detected in the double bond attribute */ /* (this 'unknown' came from 'Either' double bond) */ /* Treat both unknowns in the same way */ if ( bUnknown ) nParity |= AB_UNKNOWN; if ( bIsotopic ) { switch ( nParity ) { case AB_PARITY_ODD: case AB_PARITY_ODD | AB_NEGATIVE: nAction = AB_PARITY_CALC; break; case AB_PARITY_ODD | AB_UNKNOWN: case AB_PARITY_UNDF | AB_UNKNOWN: case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: nAction = vABParityUnknown /*AB_PARITY_UNKN*/; break; case AB_PARITY_IISO: case AB_PARITY_IISO | AB_UNKNOWN: nAction = AB_PARITY_NONE; break; case AB_PARITY_UNDF: case AB_PARITY_UNDF | AB_NEGATIVE: nAction = AB_PARITY_UNDF; break; default: nAction = -1; /* program error */ } } else { /* Non-isotopic */ switch ( nParity ) { case AB_PARITY_ODD: nAction = AB_PARITY_CALC; break; case AB_PARITY_ODD | AB_UNKNOWN: case AB_PARITY_UNDF | AB_UNKNOWN: nAction = vABParityUnknown /*AB_PARITY_UNKN*/; break; /* case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: */ case AB_PARITY_UNDF: nAction = AB_PARITY_UNDF; break; case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: case AB_PARITY_ODD | AB_NEGATIVE: case AB_PARITY_IISO: case AB_PARITY_IISO | AB_UNKNOWN: case AB_PARITY_UNDF | AB_NEGATIVE: case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: nAction = AB_PARITY_NONE; break; default: nAction = -1; /* program error */ } } return nAction; #undef AB_NEGATIVE #undef AB_UNKNOWN } /*************************************************************/ int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK min_sb_ring_size, int bPointedEdgeStereo, int vABParityUnknown ) { int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; int bond_type, num_2s_1, num_alt_1; int num_2s_2, num_alt_2; #if ( ONE_BAD_SB_NEIGHBOR == 1 ) int num_wrong_bonds_1, num_wrong_bonds_2; #endif #if ( N_V_STEREOBONDS == 1 ) int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; #endif int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; int chain_length, num_chains, cur_chain_length; int all_at_2[MAX_NUM_STEREO_BONDS]; int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; S_CHAR all_unkn[MAX_NUM_STEREO_BONDS]; int /*at_1_parity, at_2_parity,*/ nUnknown, stop=0; /* at_1_parity = AB_PARITY_NONE; */ /* do not know */ /* check valence */ if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_1].valence+at[at_1].num_H || MIN_NUM_STEREO_BOND_NEIGH > at[at_1].valence+at[at_1].num_H ) return 0; if ( !bCanAtomHaveAStereoBond( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) return 0; if ( at[at_1].c_point ) return 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ /* middle cumulene atoms, for example, =C=, should be ignored here */ /* only atoms at the ends of cumulene chains are considered. */ if ( !at[at_1].num_H && 2 == at[at_1].valence && BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[0] ) && BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[1] ) ) { return 0; } /* count bonds and find the second atom on the stereo bond */ num_2s_1 = num_alt_1 = 0; chain_length = 0; num_chains = 0; #if ( ONE_BAD_SB_NEIGHBOR == 1 ) num_wrong_bonds_1 = 0; #endif #if ( N_V_STEREOBONDS == 1 ) num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; if ( 0 == at[at_1].num_H && 0 == at[at_1].charge && 0 == at[at_1].radical && 3 == get_endpoint_valence( at[at_1].el_number ) ) { if ( 2 == at[at_1].valence && 3 == at[at_1].chem_bonds_valence ) { type_N = 1; } else if ( 3 == at[at_1].valence && 5 == at[at_1].chem_bonds_valence ) { type_N = 2; /* unfortunately includes >N# */ } } #endif for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].valence; i_next_at_1 ++ ) { nUnknown = (at[at_1].bond_stereo[i_next_at_1] == STEREO_DBLE_EITHER); bond_type = get_allowed_stereo_bond_type( (int)at[at_1].bond_type[i_next_at_1] ); at_2 = -1; /* not found */ if ( bond_type == BOND_ALTERN || bond_type == BOND_DOUBLE ) { next_at_1 = at_2 = at[at_1].neighbor[i_next_at_1]; next_at_2 = at_1; } switch ( bond_type ) { case BOND_ALTERN: num_alt_1 ++; #if ( FIND_RING_SYSTEMS == 1 ) if ( at[at_1].nRingSystem != at[at_2].nRingSystem ) continue; /* reject alt. bond connecting different ring systems */ #endif if ( (nMode & CMODE_NO_ALT_SBONDS) || !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { continue; /* reject non-stereogenic bond to neighbor ord. #i_next_at_1 */ } break; case BOND_DOUBLE: /* check for cumulene/allene */ num_2s_1++; cur_chain_length = 0; if ( bCanAtomBeTerminalAllene( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) { /* * Example of cumulene * chain length = 2: >X=C=C=Y< * | | | | * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom * next to at_1= next_at_1 next_at_2 =previous to at_2 * * chain length odd: stereocenter on the middle atom ( 1=> allene ) * chain length even: "long stereogenic bond" */ while ((bAllene = !at[at_2].num_H && at[at_2].valence == 2 && BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[0] ) && BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[1] )) && bCanAtomBeMiddleAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ next_at_2 = at_2; nUnknown += (at[at_2].bond_stereo[k] == STEREO_DBLE_EITHER); at_2 = (int)at[at_2].neighbor[k]; cur_chain_length ++; /* count =C= atoms */ } if ( cur_chain_length ) { num_chains ++; if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || !bCanAtomBeTerminalAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { cur_chain_length = 0; continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ } chain_length = cur_chain_length; /* accept a stereogenic cumulele */ } } #if ( N_V_STEREOBONDS == 1 ) if ( !cur_chain_length && 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + at_2 )) ) { num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ } #endif if ( !cur_chain_length && !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ } break; case BOND_SINGLE: case BOND_TAUTOM: continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ default: #if ( ONE_BAD_SB_NEIGHBOR == 1 ) num_wrong_bonds_1 ++; continue; #else return 0; /* wrong bond type; */ #endif } /* check atom at the opposite end of possibly stereogenic bond */ bFound = (at_2 >= 0 && at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ if ( bFound ) { /* check "at_2" atom on the opposite side of the bond or cumulene chain */ if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_2].valence+at[at_2].num_H || MIN_NUM_STEREO_BOND_NEIGH > at[at_2].valence+at[at_2].num_H ) continue; /* check at_2 neighbors and bonds */ num_2s_2 = num_alt_2 = 0; #if ( N_V_STEREOBONDS == 1 ) num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; if ( 0 == at[at_2].num_H && 0 == at[at_2].charge && 0 == at[at_2].radical && 3 == get_endpoint_valence( at[at_2].el_number ) ) { if ( 2 == at[at_2].valence && 3 == at[at_2].chem_bonds_valence ) { type_N_next = 1; /* -N= */ } else if ( 3 == at[at_2].valence && 5 == at[at_2].chem_bonds_valence ) { type_N_next = 2; /* unfortunately includes >N# */ } } #endif i_next_at_2 = -1; /* unassigned mark */ #if ( ONE_BAD_SB_NEIGHBOR == 1 ) num_wrong_bonds_2 = 0; #endif for ( j = 0; j < at[at_2].valence; j ++ ) { bond_type = get_allowed_stereo_bond_type( (int)at[at_2].bond_type[j] ); if ( !bond_type ) { #if ( ONE_BAD_SB_NEIGHBOR == 1 ) num_wrong_bonds_2 ++; continue; /* this bond type is not allowed to be adjacent to a stereo bond */ #else break; #endif } if ( bond_type == BOND_DOUBLE ) { num_2s_2 ++; #if ( N_V_STEREOBONDS == 1 ) next_next_at = at[at_2].neighbor[j]; if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ } #endif } else { num_alt_2 += ( bond_type == BOND_ALTERN ); } if ( (int)at[at_2].neighbor[j] == next_at_2 ) i_next_at_2 = j; /* assigned */ } if ( #if ( ONE_BAD_SB_NEIGHBOR == 1 ) num_wrong_bonds_2 > 1 || num_wrong_bonds_2 && 2 >= at[at_2].valence || #else j < at[at_2].valence /* "next" has a wrong bond type*/ || #endif (num_alt_2>0) + (num_2s_2>0) != 1 || /* all double XOR all alt bonds only */ /* num_2s_2 > 1 ||*/ /* only one double bond permitted */ i_next_at_2 < 0 /* atom next to the opposite atom not found */ ) { bFound = 0; } else if ( at[at_2].c_point ) { bFound = 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ } else if ( num_2s_2 > 2 ) { bFound = 0; } else #if ( N_V_STEREOBONDS == 1 ) if ( 3 == (type_N | type_N_next) && ( 2 == type_N && !bIsOxide( at, at_1 ) || 2 == type_N_next && !bIsOxide( at, at_2 ) ) ) { bFound = 0; } else #endif if ( 2 == num_2s_2 ) { #if ( N_V_STEREOBONDS == 1 ) if ( !chain_length && 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && 3 == at[at_2].valence + at[at_2].num_H && 5 == at[at_2].chem_bonds_valence + at[at_2].num_H && 3 == get_endpoint_valence( at[at_2].el_number ) && (!type_N || bIsOxide( at, at_2 )) ) { /* * found: * * \ / \ / \ / * \ / \ / \ / * N==C or N==C or N==N * // \ // \ // \ * O ^ \ N ^ \ O ^ \ * | | | * | | | * at[at_2] at[at_2] at[at_2] */ ; } else { bFound = 0; } #else bFound = 0; #endif } if ( chain_length && num_alt_2 ) return 0; /* allow no alt bonds in cumulenes */ } if ( bFound ) { all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ all_at_2[num_stereo_bonds] = at_2; /* at_2 */ all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ /* if ( (at[at_1].bUsed0DParity & 2) || (at[at_2].bUsed0DParity & 2) ) { for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { if ( at[at_1].sb_neigh[k] == i_next_at_1 ) { if ( at[at_1].sb_parity[k] == AB_PARITY_UNKN && !nUnknown ) { all_unkn[num_stereo_bonds] = 1; } break; } } } */ num_stereo_bonds ++; } } if ( num_chains > 1 ) { return 0; /* cannot be more than 1 cumulene chain. */ } #if ( ONE_BAD_SB_NEIGHBOR == 1 ) if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[at_1].valence ) { return 0; /* wrong bond type */ } #endif /* accept only short chains for now */ /* chain_length=1: >C=C=C< tetrahedral center, allene */ /* chain_length=2: >C=C=C=C< stereogenic bond, cumulene */ if ( chain_length && (num_stereo_bonds != 1 || num_alt_1 || chain_length > MAX_CUMULENE_LEN) ) { return 0; } /* we need 1 double bond/chain XOR up to 3 arom. bonds */ /* to have a stereogenic bond */ if ( (num_alt_1>0) + (num_2s_1>0) != 1 || !num_stereo_bonds /*|| num_2s_1 > 1*/ ) return 0; if ( num_2s_1 > 1 ) { #if ( N_V_STEREOBONDS == 1 ) if ( 2 == num_2s_1 && 2 == type_N && 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && 3 == at[at_1].valence + at[at_1].num_H && 5 == at[at_1].chem_bonds_valence + at[at_1].num_H && 3 == get_endpoint_valence( at[at_1].el_number ) ) { ; } else { return 0; } #else return 0; #endif } /* ================== calculate parities ====================== */ /* find possibly stereo bonds and save them */ num_stored_isotopic_stereo_bonds = 0; num_stored_stereo_bonds = 0; for ( k = 0; k < num_stereo_bonds; k ++ ) { int cur_parity, next_parity, abs_cur_parity, abs_next_parity, dot_prod_z; S_CHAR z_dir1[3], z_dir2[3]; /* 3D vectors for half stereo bond parity direction */ int chain_len_bits = MAKE_BITS_CUMULENE_LEN(chain_length); int cur_parity_defined, next_parity_defined; int cur_action, next_action, result_action; at_2 = all_at_2[k]; i_next_at_1 = all_pos_1[k]; #if ( MIN_SB_RING_SIZE > 0 ) if( at[at_1].nRingSystem == at[at_2].nRingSystem ) { /* check min. ring size only if both double bond/cumulene */ /* ending atoms belong to the same ring system */ j = is_bond_in_Nmax_memb_ring( at, at_1, i_next_at_1, q, nAtomLevel, cSource, min_sb_ring_size ); if ( j > 0 ) { continue; } else if ( j < 0 ) { return CT_STEREOBOND_ERROR; } } #endif i_next_at_2 = all_pos_2[k]; nUnknown = all_unkn[k]; memset(z_dir1, 0, sizeof(z_dir1)); memset(z_dir2, 0, sizeof(z_dir2)); /******************************************************************************** * find atom parities (negative means parity due to H-isotopes only) * and half stereo bond parity directions z_dir1, z_dir2. * * Bond can have unknown or undefined parity or no parity because of: * 1. Geometry (poorly defined, cannot calculate, for example linear =C-F * or =CHD with no geometry) -- Undefined parity * H * 2. Identical H atoms (no parity in principle, for example =C< ) * -- No parity H * * 3. The user said double bond stereo is unknown * or at least one of single bonds is in unknown direction * -- Unknown parity * * These 3 cases (see above) are referred below as 1, 2, 3. * Each of the cases may be present or not (2 possibilities) * Total number of combination is 2*2*2=8 * * Since a case when all 3 are not present is a well-defined parity, * we do not consider this case here. Then 2*2*2-1=7 cases are left. * * If several cases are present, list them below separated by "+". * For example, 1+2 means (1) undefined geometry and (2) no parity * is possible because of identical H atoms. * * N) Decision table, Non-isotopic, 2*2*2-1=7 cases: * ================================================= * none : 2+any: 1+2(e.g.=CH2); 1+2+3; 2; 2+3 AB_PARITY_NONE=0 * undefined: 1 AB_PARITY_UNDF * unknown : 1+3; 3 AB_PARITY_UNKN * * I) Decision table, Isotopic, 2*2*2-1=7 cases: * ============================================= * none : none * undefined: 1; 1+2; 1+2+3; 2; 2+3 * unknown : 1+3; 3 * * Note: When defining identical atoms H atoms in case 2, * Isotopic and Non-isotopic cases are different: * N: do NOT take into account the isotopic composition of H atoms * I: DO take into account the isotopic composition of H atoms * (it is assumed that H isotopes are always different) * * half_stereo_bond_parity() returns: * ================================== * Note: half_stereo_bond_parity() is unaware of case 3. * * can't be a half of a stereo bond AB_PARITY_NONE * 1, isotopic & non-isotopic: AB_PARITY_UNDF * 1, isotopic only -AB_PARITY_UNDF * 2, no parity: identical H isotopes AB_PARITY_IISO * 3, 'Either' single bond(s) AB_PARITY_UNKN ??? * 3, 'Either' single bond(s), iso H -AB_PARITY_UNKN ??? * defined parity AB_PARITY_ODD, AB_PARITY_EVEN * defined parity for isotopic only: -AB_PARITY_ODD, -AB_PARITY_EVEN * * Resultant value for the stereo bond parity * ---+-------------------+-------+--------+----------------+ * 3? | half_stereo_bond_ | N or I| case 1,| bond parity | * | parity()= | | 2 or 3 | | * ---+-------------------+-------+--------+----------------+ * ( AB_PARITY_ODD/EVEN) => N&I: - => AB_PARITY_CALC (=6, calc.later) * 3+( AB_PARITY_ODD/EVEN) => N&I: 3 => AB_PARITY_UNKN (=3) * (-AB_PARITY_ODD/EVEN) => N: 2 => AB_PARITY_NONE (=0) * (-AB_PARITY_ODD/EVEN) => I: - => AB_PARITY_CALC * 3+(-AB_PARITY_ODD/EVEN) => N: 2+3 => AB_PARITY_UNDF (=4) * 3+(-AB_PARITY_ODD/EVEN) => I: 3 => AB_PARITY_UNKN * ( AB_PARITY_IISO ) => N: 1+2, 2 => AB_PARITY_NONE (=0) * ( AB_PARITY_IISO ) => I: 1+2, 2 => AB_PARITY_UNDF * 3+( AB_PARITY_IISO ) => N: 1+2+3,2+3=> AB_PARITY_NONE * 3+( AB_PARITY_IISO ) => I: 1+2+3,2+3=> AB_PARITY_UNDF * ( AB_PARITY_UNDF ) => N&I: 1 => AB_PARITY_UNDF * 3+( AB_PARITY_UNDF ) => N&I: 1+3 => AB_PARITY_UNKN * (-AB_PARITY_UNDF ) => N: 1+2 => AB_PARITY_NONE * (-AB_PARITY_UNDF ) => I: 1 => AB_PARITY_UNDF * 3+(-AB_PARITY_UNDF ) => N: 1+2+3 => AB_PARITY_NONE * 3+(-AB_PARITY_UNDF ) => I: 1+3 => AB_PARITY_UNKN * ---+-------------------+-------+--------+----------------+ * If bond parity is undefined because abs(dot_prod_z) < MIN_DOT_PROD * then replace: AB_PARITY_CALC * with: AB_PARITY_UNDF * Joining two half_bond_parity() results: * * * atom1 \ atom2 | AB_PARITY_NONE AB_PARITY_UNKN AB_PARITY_UNDF AB_PARITY_CALC * ----------------+--------------------------------------------------------------- *0=AB_PARITY_NONE | AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE *3=AB_PARITY_UNKN | AB_PARITY_UNKN AB_PARITY_UNKN AB_PARITY_UNKN *4=AB_PARITY_UNDF | AB_PARITY_UNDF AB_PARITY_UNDF *6=AB_PARITY_CALC | AB_PARITY_CALC * * that is, take min out of the two *********************************************************************************/ cur_parity = half_stereo_bond_parity( at, at_1, at_removed_H, num_removed_H, z_dir1, bPointedEdgeStereo, vABParityUnknown ); next_parity = half_stereo_bond_parity( at, at_2, at_removed_H, num_removed_H, z_dir2, bPointedEdgeStereo, vABParityUnknown ); if ( RETURNED_ERROR(cur_parity) || RETURNED_ERROR(next_parity) ) { return CT_CALC_STEREO_ERR; } if ( (at[at_1].bUsed0DParity & FlagSB_0D) || (at[at_1].bUsed0DParity & FlagSB_0D) ) { FixSb0DParities( at, /* at_removed_H, num_removed_H,*/ chain_length, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2, &cur_parity, &next_parity ); } if ( cur_parity == AB_PARITY_NONE || abs(cur_parity) == AB_PARITY_IISO ) { continue; } if ( next_parity == AB_PARITY_NONE || abs(next_parity) == AB_PARITY_IISO ) { continue; } cur_action = half_stereo_bond_action( cur_parity, nUnknown, 0, vABParityUnknown ); /* -1 => program error */ next_action = half_stereo_bond_action( next_parity, nUnknown, 0, vABParityUnknown ); result_action = inchi_min(cur_action, next_action); if ( result_action == -1 ) { stop = 1; /* program error */ } abs_cur_parity = abs( cur_parity ); abs_next_parity = abs( next_parity ); cur_parity_defined = ATOM_PARITY_WELL_DEF(abs_cur_parity); next_parity_defined = ATOM_PARITY_WELL_DEF(abs_next_parity); if ( cur_parity_defined && next_parity_defined ) { /* find how the whole bond parity depend on geometry */ /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ /* can be done only for a well-defined geometry */ /* dot_prod_z = (chain_len_bits & BIT_CUMULENE_CHI)? triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : dot_prodchar3(z_dir1, z_dir2); */ dot_prod_z = (chain_len_bits && BOND_CHAIN_LEN(chain_len_bits)%2)? triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : dot_prodchar3(z_dir1, z_dir2); if ( abs(dot_prod_z) < MIN_DOT_PROD ) { /* The geometry is not well-defined. Eliminate AB_PARITY_CALC */ result_action = inchi_min( result_action, AB_PARITY_UNDF ); } } else { dot_prod_z = 0; } if ( result_action != AB_PARITY_NONE && result_action != -1 ) { /* stereo, no isotopes (only positive) */ if ( cur_parity > 0 && next_parity > 0 ) { if ( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor, out_at[at_1].stereo_bond_ord, out_at[at_1].stereo_bond_z_prod, out_at[at_1].stereo_bond_parity, at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor, out_at[at_2].stereo_bond_ord, out_at[at_2].stereo_bond_z_prod, out_at[at_2].stereo_bond_parity) ) { if ( !out_at[at_1].parity || cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity)) ) { out_at[at_1].parity = cur_parity; memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); } if ( !out_at[at_2].parity || next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity)) ) { out_at[at_2].parity = next_parity; memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); } out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; num_stored_stereo_bonds ++; } } } /* stereo + isotopic (all non-zero) */ cur_action = half_stereo_bond_action( cur_parity, nUnknown, 1, vABParityUnknown ); /* -1 => program error */ next_action = half_stereo_bond_action( next_parity, nUnknown, 1, vABParityUnknown ); result_action = inchi_min(cur_action, next_action); cur_parity = abs_cur_parity; next_parity = abs_next_parity; if ( result_action != AB_PARITY_NONE && result_action != -1 ) { /* stero, isotopic */ if ( cur_parity > 0 && next_parity > 0 ) { if( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor2, out_at[at_1].stereo_bond_ord2, out_at[at_1].stereo_bond_z_prod2, out_at[at_1].stereo_bond_parity2, at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor2, out_at[at_2].stereo_bond_ord2, out_at[at_2].stereo_bond_z_prod2, out_at[at_2].stereo_bond_parity2) ) { if ( !out_at[at_1].parity2 || cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity2)) ) { out_at[at_1].parity2 = cur_parity /*| chain_len_bits*/; if ( !out_at[at_1].parity ) { memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); } } if ( !out_at[at_2].parity2 || /* next line changed from abs(out_at[at_2].parity) 2006-03-05 */ next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity2)) ) { out_at[at_2].parity2 = next_parity /*| chain_len_bits*/; if ( !out_at[at_2].parity ) { memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); } } out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; num_stored_isotopic_stereo_bonds ++; } } } else if ( result_action == -1 ) { stop = 1; /* program error? */ } } if ( stop ) { return CT_CALC_STEREO_ERR; } return /*num_stored_stereo_bonds+*/ num_stored_isotopic_stereo_bonds; } /*********************************************************************/ /* if isotopic H, D, T added, can the atom be a stereo center? */ #if ( NEW_STEREOCENTER_CHECK == 1 ) /* int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at ) */ int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) { int nNumNeigh; if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo )) && at[cur_at].valence + at[cur_at].num_H == nNumNeigh && at[cur_at].num_H <= NUM_H_ISOTOPES ) { return 1; } return 0; } #else int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at ) { int j, ret = 0; if ( bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) && at[cur_at].valence + at[cur_at].num_H == MAX_NUM_STEREO_ATOM_NEIGH && at[cur_at].num_H < MAX_NUM_STEREO_ATOM_NEIGH ) { for ( j = 0, ret=1; ret && j < at[cur_at].valence; j ++ ) { if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { ret = 0; } } } return ret; } #endif /***************************************************************/ int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ) { int parity = AB_PARITY_NONE; if ( at[cur_at].p_parity && (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH) ) { int i, num_trans_inp, num_trans_neigh; AT_NUMB nInpNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; for ( i = 0; i < MAX_NUM_STEREO_ATOM_NEIGH; i ++ ) { nInpNeighOrigAtNumb[i] = at[cur_at].p_orig_at_num[i]; if ( nInpNeighOrigAtNumb[i] == at[cur_at].orig_at_number ) { nInpNeighOrigAtNumb[i] = 0; /* lone pair or explicit H */ } } num_trans_inp = insertions_sort( nInpNeighOrigAtNumb, MAX_NUM_STEREO_ATOM_NEIGH, sizeof(nInpNeighOrigAtNumb[0]), comp_AT_NUMB ); num_trans_neigh = insertions_sort( nSbNeighOrigAtNumb, j1, sizeof(nSbNeighOrigAtNumb[0]), comp_AT_NUMB ); if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { ; /*num_trans_neigh += j1;*/ /* the lone pair or implicit H is implicitly at the top of the list */ } if ( !memcmp( nInpNeighOrigAtNumb + MAX_NUM_STEREO_ATOM_NEIGH-j1, nSbNeighOrigAtNumb, j1*sizeof(AT_NUMB) ) ) { if ( ATOM_PARITY_WELL_DEF(at[cur_at].p_parity) ) { parity = 2 - (num_trans_inp + num_trans_neigh + at[cur_at].p_parity) % 2; } else { parity = at[cur_at].p_parity; } at[cur_at].bUsed0DParity |= nFlag; /* 0D parity used for streocenter parity */ } } return parity; } /*************************************************************** * Get stereo atom parity for the current order of attachments * The result in at[cur_at].parity is valid for previously removed * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] * The return value is a calculated parity. */ #define ADD_EXPLICIT_HYDROGEN_NEIGH 1 #define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, int bPointedEdgeStereo, int vABParityUnknown) { int j, k, next_at, num_z, j1, nType, num_explicit_H, tot_num_iso_H, nMustHaveNumNeigh; int num_explicit_iso_H[NUM_H_ISOTOPES+1]; /* numbers of removed hydrogen atoms */ int index_H[MAX_NUM_STEREO_ATOM_NEIGH]; /* cannot have more than 4 elements: 1 H, 1 D, 1 T atom(s) */ double z, sum_xyz[3], min_sine, triple_product; double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; double bond_len_xy[4], rmax=0.0, rmin=0.0; double at_coord_center[3]; int parity, bAmbiguous = 0, bAddExplicitNeighbor = 0, b2D = 0, n2DTetrahedralAmbiguity = 0; int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; out_at[cur_at].parity = out_at[cur_at].parity2 = out_at[cur_at].stereo_atom_parity = out_at[cur_at].stereo_atom_parity2 = AB_PARITY_NONE; parity = AB_PARITY_NONE; memset(num_explicit_iso_H, 0, sizeof(num_explicit_iso_H)); num_explicit_H = 0; #if ( NEW_STEREOCENTER_CHECK == 1 ) if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo ) ) || at[cur_at].num_H > NUM_H_ISOTOPES ) { goto exit_function; } #else nMustHaveNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH; if ( !bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) || at[cur_at].valence + at[cur_at].num_H != nMustHaveNumNeigh || at[cur_at].num_H > NUM_H_ISOTOPES ) { goto exit_function; } for ( j = 0; j < at[cur_at].valence; j ++ ) { if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { goto exit_function; } } #endif /* numbers of isotopic H atoms */ for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { if ( at[cur_at].num_iso_H[j] > 1 ) { goto exit_function; /* two or more identical hydrogen isotopic neighbors */ } tot_num_iso_H += at[cur_at].num_iso_H[j]; } if ( bIgnoreIsotopicH ) { tot_num_iso_H = 0; /* isotopic H considered subject to exchange => ignore isotopic */ } /* number of non-isotopic H atoms */ if ( at[cur_at].num_H - tot_num_iso_H > 1 ) { goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ } /* count removed explicit terminal hydrogens attached to at[cur_at]. */ /* the result is num_explicit_H. */ /* Removed hydrogens are sorted in increasing isotopic shift order */ if ( at_removed_H && num_removed_H > 0 ) { for ( j = 0; j < num_removed_H; j ++ ) { if ( at_removed_H[j].neighbor[0] == cur_at ) { k = at_removed_H[j].iso_atw_diff; /* iso_atw_diff values: H=>0, 1H=>1, D=2H=>2, T=3H=>3 */ if ( k < 0 || k > NUM_H_ISOTOPES || bIgnoreIsotopicH ) k = 0; /* treat wrong H isotopes as non-isotopic H */ num_explicit_iso_H[k] ++; index_H[num_explicit_H++] = j; } } } /* coordinates initialization */ num_z = 0; sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; at_coord_center[0] = at_coord_center[1] = at_coord_center[2] = 0.0; /* fill out stereo center neighbors coordinates */ /* and obtain the parity from the geometry */ for ( k = 0, j1 = 0; k < 2; k ++ ) { switch( k ) { case 0: /* add coordinates of removed hydrogens */ for ( j = 0; j < num_explicit_H; j ++, j1 ++ ) { next_at = index_H[j]; /* use bond description located at removed_H atom */ /* minus sign at get_z_coord: at_removed_H[] contains bonds TO at[cur_at], not FROM it. */ /* Note: &at[(at_removed_H-at)+ next_at] == &at_removed_H[next_at] */ z = -get_z_coord( at, (at_removed_H-at)+ next_at, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); switch ( nType ) { case ZTYPE_EITHER: parity = vABParityUnknown /*AB_PARITY_UNKN*/ ; /* no parity: bond in "Either" direction. */ goto exit_function; case ZTYPE_UP: case ZTYPE_DOWN: nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ b2D ++; /* no break; here */ case ZTYPE_3D: num_z ++; } nSbNeighOrigAtNumb[j1] = at_removed_H[next_at].orig_at_number; at_coord[j1][0] = at_removed_H[next_at].x-at[cur_at].x; at_coord[j1][1] = at_removed_H[next_at].y-at[cur_at].y; bond_len_xy[j1] = len2(at_coord[j1]); /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ at_coord[j1][2] = (nType==ZTYPE_3D? z : nType==ZTYPE_UP? bond_len_xy[j1] : nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); } break; case 1: /* add all coordinates of other neighboring atoms */ for ( j = 0; j < at[cur_at].valence; j ++, j1 ++ ) { next_at = at[cur_at].neighbor[j]; z = get_z_coord( at, cur_at, j, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); switch ( nType ) { case ZTYPE_EITHER: parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* unknown parity: bond in "Either" direction. */ goto exit_function; case ZTYPE_UP: case ZTYPE_DOWN: b2D ++; case ZTYPE_3D: num_z ++; } nSbNeighOrigAtNumb[j1] = at[next_at].orig_at_number; at_coord[j1][0] = at[next_at].x-at[cur_at].x; at_coord[j1][1] = at[next_at].y-at[cur_at].y; bond_len_xy[j1] = len2(at_coord[j1]); /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ at_coord[j1][2] = (nType==ZTYPE_3D? z : nType==ZTYPE_UP? bond_len_xy[j1] : nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); } break; } } /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].valence+num_explicit_H && MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].valence+num_explicit_H ) { /* not enough geometry data to find the central atom parity */ if ( nMustHaveNumNeigh == at[cur_at].valence+at[cur_at].num_H && at[cur_at].num_H > 1 ) { /* only isotopic parity is possible; no non-isotopic parity */ if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { parity = -vABParityUnknown /*AB_PARITY_UNKN*/; /* the user marked the center as "unknown" */ } else { parity = -AB_PARITY_UNDF; /* not enough geometry; only isotopic parity is possible */ } } else { parity = AB_PARITY_NONE; /* not a stereocenter at all */ } goto exit_function; } /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ for ( j = 0; j < j1; j ++ ) { z = len3( at_coord[j] ); if ( z < MIN_BOND_LEN ) { /* bond length is too small: use 0D parities */ if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { parity = AB_PARITY_UNDF; } goto exit_function; } #if ( STEREO_CENTER_BONDS_NORM == 1 ) else { mult3( at_coord[j], 1.0/z, at_coord[j] ); } #endif rmax = j? inchi_max( rmax, z) : z; rmin = j? inchi_min( rmin, z) : z; } if ( rmin / rmax < MIN_SINE ) { /* bond ratio is too small: use 0D parities */ if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { parity = AB_PARITY_UNDF; } goto exit_function; } for ( j = 0; j < j1; j ++ ) { add3( sum_xyz, at_coord[j], sum_xyz ); } /* here j1 is a number of neighbors including explicit terminal isotopic H */ /* num_explicit_iso_H[0] = number of explicit non-isotopic hydrogen atom neighbors */ j = j1; /* Add Explicit Neighbor */ if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { /* add an explicit neighbor if possible */ if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; } else if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { /* check whether an explicit non-isotopic hydrogen can be added */ /* to an atom that is a stereogenic atom */ if ( 1 == at[cur_at].num_H - num_explicit_H && /* the atom has only one one implicit hydrogen */ 1 == at[cur_at].num_H - tot_num_iso_H ) { /* this hydrogen is non-isotopic */ bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; } } } if ( bAddExplicitNeighbor ) { /*********************************************************** * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H * or a fake 4th neighbor, that is, a lone pair */ if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { goto exit_function; /* the user insists the parity is unknown and the isotopic */ /* composition of the neighbors does not contradict */ } else if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { /* "hydrogen down" rule is needed to resolve an ambiguity */ if ( num_z > 0 ) { bAmbiguous |= AMBIGUOUS_STEREO; } #if ( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ /* Although H should be at the top of the list, add it to the bottom. */ /* This will be taken care of later by inverting parity 1<->2 */ at_coord[j][0] = 0.0; at_coord[j][1] = 0.0; #if ( STEREO_CENTER_BONDS_NORM == 1 ) at_coord[j][2] = -1.0; #else at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; #endif #else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ #if (ALWAYS_SET_STEREO_PARITY == 1) parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ #else /* all 3 bonds are in one plain: try to get 0D parities */ if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { parity = AB_PARITY_UNDF; } /*parity = AB_PARITY_UNDF;*/ /* no parity can be calculated found */ #endif goto exit_function; #endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ } else { /* we have enough information to find implicit hydrogen coordinates */ /* at_coord[j][0] = -sum_x; at_coord[j][1] = -sum_y; at_coord[j][2] = -sum_z; */ copy3( sum_xyz, at_coord[j] ); change_sign3( at_coord[j], at_coord[j] ); z = len3( at_coord[j] ); #if ( FIX_STEREO_SCALING_BUG == 1 ) if ( z > 1.0 ) { rmax *= z; } else { rmin *= z; } #else /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ /* This bug leads to pronouncing legitimate stereogenic atoms */ /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ if ( bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) { /* coordinate scaling bug fixed here */ if ( z > 1.0 ) { rmax *= z; } else { rmin *= z; } } else { /* original InChI v.1 bug */ rmax = inchi_max( rmax, z ); rmin = inchi_min( rmin, z ); } #endif if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { /* the new 4th bond is too short: try to get 0D parities */ if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { parity = AB_PARITY_UNDF; } goto exit_function; } #if ( STEREO_CENTER_BOND4_NORM == 1 ) else { mult3( at_coord[j], 1.0/z, at_coord[j] ); } #endif } } else if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { parity = -AB_PARITY_UNDF; /* isotopic composition of H-neighbors contradicts 'unknown' */ } goto exit_function; } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ if ( num_z == 0 || are_4at_in_one_plane(at_coord, MIN_SINE) ) { /* all four neighours in xy plane: undefined geometry. */ if ( num_z > 0 ) { bAmbiguous |= AMBIGUOUS_STEREO; } if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { #if (ALWAYS_SET_STEREO_PARITY == 1) parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ #else /* all 4 bonds are in one plain: try to get 0D parities */ if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { parity = AB_PARITY_UNDF; } else if ( ATOM_PARITY_WELL_DEF( parity ) ) { bAmbiguous &= ~AMBIGUOUS_STEREO; /* 0D parity has resolved the ambiguity */ } #endif } goto exit_function; } /*********************************************************** * At this point we have 4 neighboring atoms. * check for tetrahedral ambiguity in 2D case */ if ( b2D ) { n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor, (bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) ); if ( 0 < n2DTetrahedralAmbiguity ) { if ( T2D_WARN & n2DTetrahedralAmbiguity ) { bAmbiguous |= AMBIGUOUS_STEREO; } if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { #if (ALWAYS_SET_STEREO_PARITY == 1) parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ #else parity = AB_PARITY_UNDF; /* no parity */ #endif } goto exit_function; } } else if ( n2DTetrahedralAmbiguity < 0 ) { bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ parity = AB_PARITY_UNDF; goto exit_function; } } /************************************************************/ /* Move coordinates origin to the neighbor #0 */ for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { diff3(at_coord[j], at_coord[0], at_coord[j]); } diff3(at_coord_center, at_coord[0], at_coord_center); /* for ( k = 0; k < 3; k++ ) { for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { at_coord[j][k] -= at_coord[0][k]; } at_coord_center[k] -= at_coord[0][k]; } */ /******************************************************** * find the central (cur_at) atom's parity * (orientation of atoms #1-3 when looking from #0) ********************************************************/ triple_product = triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); /* * check for tetrahedral ambiguity -- leave it out for now */ if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { /* Even => sorted in correct order, Odd=>transposed */ parity = triple_product > 0.0? AB_PARITY_EVEN : AB_PARITY_ODD; /* if ( num_explicit_H && at[cur_at].removed_H_parity % 2 ) */ /* odd transposition of the removed implicit H */ /* out_at[cur_at].parity = 3 - out_at[cur_at].parity; */ /* moved; see below */ /* out_at[cur_at].bAmbiguousStereo |= bAmbiguous; */ /* at[cur_at].bAmbiguousStereo |= bAmbiguous; */ /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { parity = 3 - parity; } } else { #if (ALWAYS_SET_STEREO_PARITY == 1) parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ #else if ( num_z > 0 ) { bAmbiguous |= AMBIGUOUS_STEREO; } parity = AB_PARITY_UNDF; /* no parity: 4 bonds are in one plane. */ #endif } exit_function: if ( parity ) { out_at[cur_at].bAmbiguousStereo |= bAmbiguous; at[cur_at].bAmbiguousStereo |= bAmbiguous; } /* non-isotopic parity */ if ( at[cur_at].num_H > 1 || parity <= 0 ) ; /* no non-isotopic parity */ else out_at[cur_at].parity = parity; /* isotopic parity */ if ( parity == -AB_PARITY_UNDF || parity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) parity = -parity; if ( parity < 0 ) parity = AB_PARITY_NONE; out_at[cur_at].parity2 = parity; parity = PARITY_VAL(out_at[cur_at].parity); out_at[cur_at].stereo_atom_parity = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; parity = PARITY_VAL(out_at[cur_at].parity2); out_at[cur_at].stereo_atom_parity2 = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; /* out_at[cur_at].parity2 = out_at[cur_at].parity; // save for stereo + isotopic canon. if ( out_at[cur_at].parity ) { if ( num_explicit_H > 1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 && num_explicit_H ) { // X H X // for example, >C< or >C-D // Y D Y // parity exists for stereo + isotopic atoms canonicalization only out_at[cur_at].parity = 0; } } // returning 0 means this can be an adjacent to a stereogenic bond atom */ return (int)out_at[cur_at].parity2; } #undef ADD_EXPLICIT_HYDROGEN_NEIGH #undef ADD_EXPLICIT_LONE_PAIR_NEIGH /*************************************************************/ int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, int vABParityUnknown ) { int num_3D_stereo_atoms=0; int num_stereo_bonds=0; /* added to fix allene stereo bug reported for FClC=C=CFCl by Burt Leland - 2009-02-05 DT */ int i, is_stereo, num_stereo, max_stereo_atoms=0, max_stereo_bonds=0; QUEUE *q = NULL; AT_RANK *nAtomLevel = NULL; S_CHAR *cSource = NULL; AT_RANK min_sb_ring_size = 0; /********************************************************** * * Note: this parity reflects only relative positions of * the atoms-neighbors and their ordering in the * lists of neighbors. * * To obtain the actual parity, the parity of a number * of neighbors transpositions (to obtain a sorted * list of numbers assigned to the atoms) should be * added. * **********************************************************/ /********************************************************************************* An example of parity=1 for stereogenic center, tetrahedral asymmetric atom (1) | | [C] | | (2)------(0) / / / / (3) Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) (0), (1) and (2) are in the plane (0)-(3) is directed from the plain to the viewer [C] is somewhere between (0), (1), (2), (3) Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; otherwise parity would be 1, or odd. ********************************************************************************** Examples of a stereogenic bond. Notation: [atom number], (index of a neighbor): [1] and [2] are atoms connected by the stereogenic bond numbers in () are indexes of neighbors of [1] or [2]. (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product atom [1] atom [2] [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 (2) (1) 0 transpositions because 0 transpositions because \ / double bond is in 0 posit. double bond is in 0 position [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) / \ (1) (2) result: parity = 2, even result: parity=2, even / \ [6] [5] atom [1] atom [2] [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 (0) (2) 2 transpositions to move 1 transposition to move \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) / \ (1) (0) result: parity = (1+2) result: parity=(1+1) / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even [6] [4] *********************************************************************************** Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this point. They will be used for each numbering in the canonicalization. Note: parity=3 for a stereo atom means entered undefined bond direction parity=4 for an atom means parity cannot be determined from the given geometry ***********************************************************************************/ if ( !at_output || !at ) { return -1; } /* clear stereo descriptors */ for( i = 0; i < num_at; i ++ ) { at_output[i].parity = 0; at_output[i].parity2 = 0; memset(&at_output[i].stereo_bond_neighbor[0], 0, sizeof(at_output[0].stereo_bond_neighbor) ); memset(&at_output[i].stereo_bond_neighbor2[0], 0, sizeof(at_output[0].stereo_bond_neighbor2) ); memset(&at_output[i].stereo_bond_ord[0], 0, sizeof(at_output[0].stereo_bond_ord) ); memset(&at_output[i].stereo_bond_ord2[0], 0, sizeof(at_output[0].stereo_bond_ord2) ); memset(&at_output[i].stereo_bond_z_prod[0], 0, sizeof(at_output[0].stereo_bond_z_prod) ); memset(&at_output[i].stereo_bond_z_prod2[0], 0, sizeof(at_output[0].stereo_bond_z_prod2) ); memset(&at_output[i].stereo_bond_parity[0], 0, sizeof(at_output[0].stereo_bond_parity) ); memset(&at_output[i].stereo_bond_parity2[0], 0, sizeof(at_output[0].stereo_bond_parity2) ); } /* estimate max numbers of stereo atoms and bonds if isotopic H are added */ if ( nMaxNumStereoAtoms || nMaxNumStereoBonds ) { for( i = 0, num_stereo = 0; i < num_at; i ++ ) { int num; num = can_be_a_stereo_atom_with_isotopic_H( at, i, bPointedEdgeStereo ); if ( num ) { max_stereo_atoms += num; } else if ( (num = can_be_a_stereo_bond_with_isotopic_H( at, i, nMode ) ) ) { /* accept cumulenes */ max_stereo_bonds += num; } } if ( nMaxNumStereoAtoms ) *nMaxNumStereoAtoms = max_stereo_atoms; if ( nMaxNumStereoBonds ) *nMaxNumStereoBonds = max_stereo_bonds; } /* calculate stereo descriptors */ #if ( MIN_SB_RING_SIZE > 0 ) min_sb_ring_size = (AT_RANK)(((nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT) & AT_RANK_MASK); if ( min_sb_ring_size >= 3 ) { /* create BFS data structure for finding for each stereo bond its min. ring sizes */ q = QueueCreate( num_at+1, sizeof(qInt) ); nAtomLevel = (AT_RANK*)inchi_calloc(sizeof(nAtomLevel[0]),num_at); cSource = (S_CHAR *)inchi_calloc(sizeof(cSource[0]),num_at); if ( !q || !cSource || !nAtomLevel ) { num_3D_stereo_atoms = CT_OUT_OF_RAM; goto exit_function; } } else { min_sb_ring_size = 2; } #endif /* main cycle: set stereo parities */ for( i = 0, num_stereo = 0; i < num_at; i ++ ) { is_stereo = set_stereo_atom_parity( at_output, at, i, at+num_at, num_removed_H, bPointedEdgeStereo, vABParityUnknown ) ; if ( is_stereo ) { num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); } else { is_stereo = set_stereo_bonds_parity( at_output, at, i, at+num_at, num_removed_H, nMode, q, nAtomLevel, cSource, min_sb_ring_size, bPointedEdgeStereo, vABParityUnknown ); if ( RETURNED_ERROR( is_stereo ) ) { num_3D_stereo_atoms = is_stereo; break; } num_stereo_bonds += (is_stereo != 0); /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ } num_stereo += (is_stereo != 0); is_stereo = is_stereo; } /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ if ( max_stereo_atoms < num_3D_stereo_atoms && nMaxNumStereoAtoms ) *nMaxNumStereoAtoms = num_3D_stereo_atoms; if ( max_stereo_bonds < num_stereo_bonds && nMaxNumStereoBonds ) *nMaxNumStereoBonds = num_stereo_bonds; /* if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) REQ_MODE_SC_IGN_ALL_UU REQ_MODE_SB_IGN_ALL_UU */ #if ( MIN_SB_RING_SIZE > 0 ) if ( q ) { q = QueueDelete( q ); } if ( nAtomLevel ) inchi_free( nAtomLevel ); if ( cSource ) inchi_free( cSource ); exit_function: #endif return num_3D_stereo_atoms; } /***************************************************************** * Functions that disconnect bonds * *=== During Preprocessing === * * RemoveInpAtBond * DisconnectMetalSalt (is not aware of bond parities) * DisconnectAmmoniumSalt * *=== Before Normalization === * * remove_terminal_HDT * *=== During the Normalization === * * AddOrRemoveExplOrImplH * *****************************************************************/ int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ) { int i, ret = 0; S_CHAR *visited = (S_CHAR*) inchi_calloc( num_atoms, sizeof(*visited) ); if ( !visited ) return -1; /* out of RAM */ for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].sb_parity[0] && !visited[i] && !(bDisconnected && is_el_a_metal(at[i].el_number)) ) { if ( ret = ReconcileCmlIncidentBondParities( at, i, -1, visited, bDisconnected ) ) { break; /* error */ } } } inchi_free ( visited ); return ret; } /*****************************************************************/ int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ) { /* visited = 0 or parity => atom has not been visited 10 + parity => currently is on the stack + its final parity 20 + parity => has been visited; is not on the stack anymore + its final parity */ int i, j, nxt_atom, ret = 0, len; int icur2nxt, icur2neigh; /* cur atom neighbors */ int inxt2cur, inxt2neigh; /* next atom neighbors */ int cur_parity, nxt_parity; int cur_order_parity, nxt_order_parity, cur_sb_parity, nxt_sb_parity, bCurMask, bNxtMask; /* !(bDisconnected && is_el_a_metal(at[i].el_number) */ if ( at[cur_atom].valence > MAX_NUM_STEREO_BONDS ) return 0; /* ignore */ if ( !at[cur_atom].sb_parity[0] ) return 1; /* wrong call */ if ( visited[cur_atom] >= 10 ) return 2; /* program error */ cur_parity = visited[cur_atom] % 10; visited[cur_atom] += 10; for ( i = 0; i < MAX_NUM_STEREO_BONDS && at[cur_atom].sb_parity[i]; i ++ ) { icur2nxt = (int)at[cur_atom].sb_ord[i]; len = get_opposite_sb_atom( at, cur_atom, icur2nxt, &nxt_atom, &inxt2cur, &j ); if ( !len ) { return 4; /* could not find the opposite atom: bond parity data error */ } if ( nxt_atom == prev_atom ) continue; if ( visited[nxt_atom] >= 20 ) continue; /* back edge, second visit: ignore */ if ( at[nxt_atom].valence > MAX_NUM_STEREO_BONDS ) continue; /* may be treated only after metal disconnection */ if ( bDisconnected && (at[cur_atom].sb_parity[i] & SB_PARITY_FLAG) ) { cur_sb_parity = (at[cur_atom].sb_parity[i] >> SB_PARITY_SHFT); bCurMask = 3 << SB_PARITY_SHFT; } else { cur_sb_parity = (at[cur_atom].sb_parity[i] & SB_PARITY_MASK); bCurMask = 3; } if ( bDisconnected && (at[nxt_atom].sb_parity[j] & SB_PARITY_FLAG) ) { nxt_sb_parity = (at[nxt_atom].sb_parity[j] >> SB_PARITY_SHFT); bNxtMask = 3 << SB_PARITY_SHFT; } else { nxt_sb_parity = (at[nxt_atom].sb_parity[j] & SB_PARITY_MASK); bNxtMask = 3; } if ( !ATOM_PARITY_WELL_DEF(cur_sb_parity) || !ATOM_PARITY_WELL_DEF(nxt_sb_parity) ) { if ( cur_sb_parity == nxt_sb_parity ) { continue; /*goto move_forward;*/ /* bypass unknown/undefined */ } return 3; /* sb parities do not match: bond parity data error */ } icur2neigh = (int)at[cur_atom].sn_ord[i]; inxt2neigh = (int)at[nxt_atom].sn_ord[j]; /* parity of at[cur_atom].neighbor[] premutation to reach this order: { next_atom, neigh_atom, ...} */ /* 1. move next_atom from position=icur2nxt to position=0 => * icur2nxt permutations * 2. move neigh_atom from position=inxt2neigh+(inxt2cur > inxt2neigh) to position=1 => * inxt2neigh+(inxt2cur > inxt2neigh)-1 permutations. * Note if (inxt2cur > inxt2neigh) then move #1 increments neigh_atom position * Note add 4 because icur2neigh may be negative due to isotopic H removal */ cur_order_parity = (4+icur2nxt + icur2neigh + (icur2neigh > icur2nxt)) % 2; /* same for next atom: */ /* parity of at[nxt_atom].neighbor[] premutation to reach this order: { cur_atom, neigh_atom, ...} */ nxt_order_parity = (4+inxt2cur + inxt2neigh + (inxt2neigh > inxt2cur)) % 2; nxt_parity = visited[nxt_atom] % 10; if ( !cur_parity ) { cur_parity = 2 - (cur_order_parity + cur_sb_parity) % 2; visited[cur_atom] += cur_parity; } else if ( cur_parity != 2 - (cur_order_parity + cur_sb_parity) % 2 ) { /***** reconcile bond parities *****/ /* Each bond parity is split into two values located at the end atoms. For T (trans) the values are (1,1) or (2,2) For C (cis) the values are (1,2) or (2,1) The fact that one pair = another with inverted parities, namely Inv(1,1) = (2,2) and Inv(1,2) = (2,1), allows to simultaneouly invert parities of the current bond end atoms (at[cur_atom].sb_parity[i], at[nxt_atom].sb_parity[j]) so that the final current atom parity cur_parity calculated later in stereochemical canonicalization for each stereobond incident with the current atomis same. Achieving this is called here RECONCILIATION. If at the closure of an aromatic circuit the parities of next atom cannot be reconciled with already calculated then this function returns 5 (error). */ at[cur_atom].sb_parity[i] ^= bCurMask; at[nxt_atom].sb_parity[j] ^= bNxtMask; cur_sb_parity ^= 3; nxt_sb_parity ^= 3; } if ( !nxt_parity ) { nxt_parity = 2 - (nxt_order_parity + nxt_sb_parity) % 2; visited[nxt_atom] += nxt_parity; } else if ( nxt_parity != 2 - (nxt_order_parity + nxt_sb_parity) % 2 ) { return 5; /* algorithm does not work for Mebius-like structures */ } /* move_forward: */ if ( visited[nxt_atom] < 10 ) { ret = ReconcileCmlIncidentBondParities( at, nxt_atom, cur_atom, visited, bDisconnected ); if ( ret ) { break; } } } visited[cur_atom] += 10; /* all bonds incident to the current atom have been processed or an error occurred. */ return ret; } /*****************************************************************/ int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ) { AT_NUMB nxt_atom; int j, len; len = 0; while ( len ++ < 20 ) { /* arbitrarily set cumulene length limit to avoid infinite loop */ nxt_atom = at[cur_atom].neighbor[icur2nxt]; for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[nxt_atom].sb_parity[j]; j ++ ) { if ( cur_atom == at[nxt_atom].neighbor[(int)at[nxt_atom].sb_ord[j]] ) { /* found the opposite atom */ *pnxt_atom = nxt_atom; *pinxt2cur = at[nxt_atom].sb_ord[j]; *pinxt_sb_parity_ord = j; return len; } } if ( j ) { return 0; /* reached atom(s) with stereobond (sb) parity, the opposite atom has not been found */ } if ( at[nxt_atom].valence == 2 && 2*BOND_TYPE_DOUBLE == at[nxt_atom].chem_bonds_valence ) { /* follow cumulene =X= path */ icur2nxt = (at[nxt_atom].neighbor[0] == cur_atom); cur_atom = nxt_atom; } else { return 0; /* neither atom with a sb parity not middle cumulene could be reached */ } } return 0; /* too long chain of cumulene was found */ } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichister.h000066400000000000000000000050371271037650300235410ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHISTER_H__ #define __INCHISTER_H__ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ); int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ); int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ); int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ); int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ); int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, int vABParityUnknown ); int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); #define PES_BIT_POINT_EDGE_STEREO 1 #define PES_BIT_PHOSPHINE_STEREO 2 #define PES_BIT_ARSINE_STEREO 4 #define PES_BIT_FIX_SP3_BUG 8 #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHISTER_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichitaut.c000066400000000000000000006412301271037650300235350ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include "mode.h" #include "inpdef.h" #include "extr_ct.h" #include "inpdef.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicomn.h" #include "ichicomp.h" #include "util.h" #include "ichi_bns.h" /* Local prototypes */ int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ); int CompRankTautomer(const void* a1, const void* a2 ); int RegisterEndPoints( T_GROUP_INFO *t_group_info, /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi , struct BalancedNetworkStructure *pBNS ); int cmpTGroupNumber( const void *a1, const void *a2 ); int comp_candidates( const void *a1, const void *a2 ); int MoveEndpoint( inp_ATOM *at, S_CANDIDATE *s_candidate, AT_NUMB endpoint, AT_NUMB *nTGroupNewNumbers, AT_NUMB *nTGroupPosition, int nNewTGroupOrd, T_GROUP_INFO *t_group_info); int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ); /* Bits for GetChargeType */ #define C_SUBTYPE_CHARGED 0 #define C_SUBTYPE_p_DONOR 1 /* new */ #define C_SUBTYPE_p_ACCEPT 2 /* new */ #define C_SUBTYPE_H_ACCEPT 4 #define C_SUBTYPE_H_DONOR 8 #define C_SUBTYPE_NEUTRAL 16 /* Internal stack array size */ #define MAX_STACK_ARRAY_LEN 127 #define MAX_TGROUP_ARRAY_LEN 127 /* local prototypes */ int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ); int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ); int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ); int CmpCCandidates( const void *a1, const void *a2 ); int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ); int GetSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ); int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ); int MergeSaltTautGroupsBlind( inp_ATOM *at, int s_type, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS ); int ConnectSaltTGroups2SuperTGroup( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, int *nNewTGroupNumber, int *vertSuperTGroup ); int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no); int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ); /*****************************************************************************/ /* Tautomers: Sorting globals */ AT_RANK *pn_tRankForSort; /*****************************************************************************/ /*****************************************************************************/ int is_centerpoint_elem( U_CHAR el_number ) { static U_CHAR el_numb[12]; static int len; int i; if ( !el_numb[0] && !len ) { el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "I" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Cl" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Br" ); } for ( i = 0; i < len; i ++ ) { if ( el_numb[i] == el_number ) { return 1; } } return 0; } #if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ /*****************************************************************************/ int is_centerpoint_elem_KET( U_CHAR el_number ) { static U_CHAR el_numb[1]; static int len; int i; if ( !el_numb[0] && !len ) { el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); } for ( i = 0; i < len; i ++ ) { if ( el_numb[i] == el_number ) { return 1; } } return 0; } #endif /*****************************************************************************/ int is_centerpoint_elem_strict( U_CHAR el_number ) { static U_CHAR el_numb[6]; static int len; int i; if ( !el_numb[0] && !len ) { el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); } for ( i = 0; i < len; i ++ ) { if ( el_numb[i] == el_number ) { return 1; } } return 0; } /*****************************************************************************/ int get_endpoint_valence( U_CHAR el_number ) { static U_CHAR el_numb[6]; static int len, len2; int i; if ( !el_numb[0] && !len ) { el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); len2 = len; el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); } for ( i = 0; i < len; i ++ ) { if ( el_numb[i] == el_number ) { return i < len2? 2 : 3; } } return 0; } /*****************************************************************************/ #if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ /*****************************************************************************/ int get_endpoint_valence_KET( U_CHAR el_number ) { static U_CHAR el_numb[2]; static int len, len2; int i; if ( !el_numb[0] && !len ) { el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); len2 = len; el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); } for ( i = 0; i < len; i ++ ) { if ( el_numb[i] == el_number ) { return i < len2? 2 : 4; } } return 0; } #endif /*****************************************************************************/ /*****************************************************************************/ int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ) { /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ inp_ATOM *at = atom + at_no; int k; int nMobile = (at->charge == -1); if ( bSubtract == 1 ) { /* 1: subtract */ num[1] -= nMobile; nMobile += at->num_H; num[0] -= nMobile; for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { /* T (3H isotope) first because it has higher weight */ num[T_NUM_NO_ISOTOPIC+k] -= at->num_iso_H[NUM_H_ISOTOPES-k-1]; } } else { if ( bSubtract == 2 ) { /* fill */ memset( num, 0, (T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC)*sizeof(num[0]) ); } /* else (0): add */ num[1] += nMobile; nMobile += at->num_H; num[0] += nMobile; for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { /* T (3H isotope) first because it has higher weight */ num[T_NUM_NO_ISOTOPIC+k] += at->num_iso_H[NUM_H_ISOTOPES-k-1]; } } return nMobile; } /*****************************************************************************/ void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ) { /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ inp_ATOM *at = atom + at_no; int nDelta, nAcidic_O; if (at->charge < -1 || at->charge == 1 && !at->c_point || at->charge > 1 ) return; nDelta = ( bSubtract == 1 )? -1 : 1; /* "Acidic" O, S, Se, Te recognition */ if ( at->at_type & ATT_ACIDIC_CO ) { nAcidic_O = nDelta; } else { nAcidic_O = 0; } if ( bSubtract == 2 ) { /* 2: fill, otherwise add */ memset( num_DA, 0, TG_NUM_DA * sizeof(num_DA[0]) ); } if ( at->charge <= 0 && at->valence == at->chem_bonds_valence || /* neutral or negative donor */ at->charge > 0 && at->valence + 1 == at->chem_bonds_valence /* positively charged donor */ ) { if ( at->charge < 0 ) { num_DA[TG_Num_dM] += nDelta; num_DA[TG_Num_dO] += nAcidic_O; } else if ( at->num_H ) { num_DA[TG_Num_dH] += nDelta; num_DA[TG_Num_dO] += nAcidic_O; } } else if ( at->charge <= 0 && at->valence + 1 == at->chem_bonds_valence || at->charge > 0 && at->valence + 2 == at->chem_bonds_valence ) { /* acceptor */ if ( at->charge < 0 ) { num_DA[TG_Num_aM] += nDelta; } else if ( at->num_H ) { num_DA[TG_Num_aH] += nDelta; } else { num_DA[TG_Num_aO] += nAcidic_O; /* acidic O-acceptor has no H or charge */ } } return; } /*****************************************************************************/ int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ) { pEndPoint->nAtomNumber = iat; pEndPoint->nEquNumber = 0; pEndPoint->nGroupNumber = at[iat].endpoint; if ( at[iat].endpoint ) { /* already an endpoint */ memset( pEndPoint->num, 0, sizeof(pEndPoint->num) ); } else { /* not an endpoint yet, make it an endpoint */ AddAtom2num( pEndPoint->num, at, iat, 2 ); /* fill */ AddAtom2DA( pEndPoint->num_DA, at, iat, 2 ); /* nMobile = pEndPoint->num[1] = (at[iat].charge == -1); nMobile = pEndPoint->num[0] = at[iat].num_H + nMobile; for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { pEndPoint->num[T_NUM_NO_ISOTOPIC+k] = at[iat].num_iso_H[NUM_H_ISOTOPES-k-1]; } */ } return 0; } /*****************************************************************************/ int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) { int nEndpointValence; int nMobile; S_CHAR cChargeSubtype; if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) return 0; /* a radical */ if ( !(nEndpointValence = get_endpoint_valence( atom[iat].el_number )) ) return 0; /* not an endpoint */ if ( nEndpointValence <= atom[iat].valence ) return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { /* not a positive charge-point */ if ( nEndpointValence < atom[iat].chem_bonds_valence ) return 0; /* abnormal valence > standard endpoint valence */ nMobile = atom[iat].num_H + (atom[iat].charge == -1); if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) return 0; /* non-standard endpoint valence */ switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { case 0: eif->cDonor = 1; eif->cAcceptor = 0; break; case 1: eif->cDonor = 0; eif->cAcceptor = 1; break; default: return 0; } eif->cMobile = nMobile; eif->cNeutralBondsValence = nEndpointValence-nMobile; eif->cMoveableCharge = 0; #if ( KETO_ENOL_TAUT == 1 ) eif->cKetoEnolCode = 0; #endif return nEndpointValence; } else if ( atom[iat].c_point && 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) ) { /* charge-point */ if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { eif->cDonor = 0; eif->cAcceptor = 1; } else if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { eif->cDonor = 1; eif->cAcceptor = 0; } else { return 0; } eif->cMobile = atom[iat].num_H; eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; eif->cMoveableCharge = atom[iat].charge; #if ( KETO_ENOL_TAUT == 1 ) eif->cKetoEnolCode = 0; #endif return nEndpointValence; } return 0; } /*****************************************************************************/ #if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ /*****************************************************************************/ int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) { int nEndpointValence; int nMobile; S_CHAR cChargeSubtype; /* static U_CHAR el_number_O, el_number_C; if ( !el_number_O ) { el_number_O = (U_CHAR)get_periodic_table_number( "O" ); el_number_C = (U_CHAR)get_periodic_table_number( "C" ); } */ if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) return 0; /* a radical */ if ( !(nEndpointValence = get_endpoint_valence_KET( atom[iat].el_number )) ) return 0; /* not an endpoint; only O and C can be an endpoint for keto-enol tautomerism */ if ( nEndpointValence <= atom[iat].valence ) return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ if ( nEndpointValence == 4 && atom[iat].valence < 2 ) return 0; /* exclude O==C--CH3 <=> HO--C==CH2 */ if ( nEndpointValence == 2 && atom[iat].valence > 1 ) return 0; /* exclude --O--C==CH-- */ if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { /* not a positive charge-point */ if ( nEndpointValence < atom[iat].chem_bonds_valence ) return 0; /* abnormal valence > standard endpoint valence */ nMobile = atom[iat].num_H + (atom[iat].charge == -1); if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) return 0; /* non-standard endpoint valence */ switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { case 0: eif->cDonor = 1; eif->cAcceptor = 0; break; case 1: eif->cDonor = 0; eif->cAcceptor = 1; break; default: return 0; } eif->cMobile = nMobile; eif->cNeutralBondsValence = nEndpointValence-nMobile; eif->cMoveableCharge = 0; eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; return nEndpointValence; } else if ( atom[iat].c_point && 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) ) { /* charge-point; currently only O for keto-enol tautomerism */ if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { eif->cDonor = 0; eif->cAcceptor = 1; } else if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { eif->cDonor = 1; eif->cAcceptor = 0; } else { return 0; } eif->cMobile = atom[iat].num_H; eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; eif->cMoveableCharge = atom[iat].charge; eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; return nEndpointValence; } return 0; } #endif /*****************************************************************************/ /*****************************************************************************/ /* RegisterEndPoints ret>0 => new registration happened, */ /* =0 => no changes, -1 => program error (debug) */ /*****************************************************************************/ int RegisterEndPoints( T_GROUP_INFO *t_group_info, /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, struct BalancedNetworkStructure *pBNS ) { T_GROUP *t_group = t_group_info->t_group; int *pnum_t = &t_group_info->num_t_groups; int max_num_t = t_group_info->max_num_t_groups; int nNumZeroEqu, nNumNewTGroups; AT_NUMB group, prev_group, prev_eqnum, nNextGroupNumber, nLeastGroupNumber; int nNumGroups, num_t, difference; int i, j, k, ret; AT_NUMB nNewTgNumberStackArray[MAX_STACK_ARRAY_LEN+1]; AT_NUMB nGroupNumberStackArray[MAX_STACK_ARRAY_LEN+1]; AT_NUMB nGroupNewNumberStackArray[MAX_STACK_ARRAY_LEN+1]; AT_NUMB *nNewTgNumber = nNewTgNumberStackArray; AT_NUMB *nGroupNumber = nGroupNumberStackArray; AT_NUMB *nGroupNewNumber = nGroupNewNumberStackArray; if ( nNumEndPoints <= 0 ) return 0; /* nothing to do */ num_t = *pnum_t; difference = 0; nNextGroupNumber = 0; nNumZeroEqu = 0; ret = 0; /* find max group number; increment it to obtain next available group number */ for ( i = 0; i < num_t; i ++ ) { if ( nNextGroupNumber < t_group[i].nGroupNumber ) nNextGroupNumber = t_group[i].nGroupNumber; } nNextGroupNumber ++; /* find min non-zero group number nLeastGroupNumber; count zero EndPoint[i].nEquNumber if all EndPoint[i].nGroupNumber are equal and non-zero then exit: nothing to do. */ nLeastGroupNumber = nNextGroupNumber; prev_group = EndPoint[0].nGroupNumber; prev_eqnum = EndPoint[0].nEquNumber; for ( i = j = k = 0; i < nNumEndPoints; i ++ ) { if ( group = EndPoint[i].nGroupNumber ) { if ( group < nLeastGroupNumber ) { nLeastGroupNumber = group; } } j += (prev_group == EndPoint[i].nGroupNumber); /* count endpoints that belong to the 1st group */ k += (prev_eqnum == EndPoint[i].nEquNumber); /* count endpoints that belongo to a group equivalent to the 1st group */ nNumZeroEqu += !EndPoint[i].nEquNumber; /* count endpoints that have been processed by FindAccessibleEndPoints() */ } if ( j == nNumEndPoints && prev_group && k == nNumEndPoints ) { /* all endpoints already belong to one t-group; the last comparison is not needed for now because EndPoint[i].nEquNumber cannot make endpont partitioning finer */ return 0; } nNumNewTGroups = 0; if ( !nNumZeroEqu ) { /* EndPoint[] has been processed by FindAccessibleEndPoints; * equal EndPoint[i].nEquNumber mark endpoints belonging to * the same t-group * Since now the next available t-group number, nNextGroupNumber, * is known,replace fict. IDs assigned by FindAccessibleEndPoints * with correct new t-group numbers. */ for ( i = 0; i < nNumEndPoints; i ++ ) { if ( (group = EndPoint[i].nEquNumber) >= nNextGroupNumber ) { /* replace fict. IDs assigned by FindAccessibleEndPoints() with new t-group numbers */ /* these fict. IDs have values = (num_atoms+1), (num_atoms+2),...; they may be non-contiguous */ for ( j = 0; j < nNumNewTGroups; j ++ ) { if ( group == nGroupNewNumber[j] ) break; } if ( j == nNumNewTGroups ) { /* found new fict. ID = group */ if ( j == MAX_STACK_ARRAY_LEN && nGroupNewNumber == nGroupNewNumberStackArray ) { /* stack array overflow; allocate more memory than may be needed */ nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); if ( !nGroupNewNumber ) { ret = -1; goto exit_function; } memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumNewTGroups*sizeof(nGroupNewNumber[0])); } /* save newly found fict. t-group ID to compare to the next values of EndPoint[].nEquNumber */ nGroupNewNumber[j] = group; nNumNewTGroups ++; } EndPoint[i].nEquNumber = nNextGroupNumber + j; } } /* after this point the values just stored in nGroupNewNumber[] will not be used. However, the obtained nNumNewTGroups value will be used */ } else if ( nNumZeroEqu == nNumEndPoints ) { /* EndPoint[] has NOT been processed by FindAccessibleEndPoints; all atoms and t-groups to which endpoints belong should be merged into a single t-group */ if ( nLeastGroupNumber == nNextGroupNumber ) { /* flag to create a new t-group: none of the found * endpoints belong to an already known t-group */ nNumNewTGroups = 1; /* otherwise 0 */ } /* All EndPoint[*].nEquNumber are zeroes. All endpoints will * belong to one new or old t-group; its ID is nLeastGroupNumber. * Set EndPoint[i].nEquNumber = nLeastGroupNumber; */ for ( i = 0; i < nNumEndPoints; i ++ ) { EndPoint[i].nEquNumber = nLeastGroupNumber; } } else { ret = -1; /* program error: only some of EndPoint[i].nEquNumber are zero */ /* */ goto exit_function; } if ( nNumNewTGroups ) { /* create new nNumNewTGroups t-group(s) */ if ( num_t + nNumNewTGroups > max_num_t ) { ret = -1; /* found too many t-groups */ /* */ goto exit_function; } /* initialize new t-group(s) */ memset( t_group + num_t, 0, nNumNewTGroups * sizeof(t_group[0]) ); for ( i = 0; i < nNumNewTGroups; i ++ ) { t_group[num_t+i].nGroupNumber = nNextGroupNumber + i; } } /* At this point: * EndPoint[i].nGroupNumber == 0 => the endpoint atom does not belong to a t-group yet * EndPoint[i].nGroupNumber > 0 => current t-group ID of the endpoint atom * EndPoint[i].nEquNumber --> new ID of a tautomeric group of this endpoint atom * EndPoint[i].nAtomNumber --> number of the endpoint atom */ nNumGroups = 0; /* counts the groups to be renumbered */ for ( i = j = 0; i < nNumEndPoints; i ++ ) { if ( group = EndPoint[i].nGroupNumber ) { if ( group == EndPoint[i].nEquNumber ) { continue; /* ignore: the endpoint belongs to the same t-group as before */ } /* save information for renumbering of the existing t-groups */ for ( j = 0; j < nNumGroups; j ++ ) { if ( group == nGroupNumber[j] ) { if ( EndPoint[i].nEquNumber != nGroupNewNumber[j] ) { ret = -1; /* program error */ /* */ goto exit_function; } break; } } if ( j == nNumGroups ) { /* discovered a new t-group number; store it together with its nEquNumber */ if ( j == MAX_STACK_ARRAY_LEN ) { if ( nGroupNewNumber == nGroupNewNumberStackArray ) { nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); if ( !nGroupNewNumber ) { ret = -1; goto exit_function; } memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumGroups*sizeof(nGroupNewNumber[0])); } if ( nGroupNumber == nGroupNumberStackArray ) { nGroupNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNumber[0])); if ( !nGroupNumber ) { ret = -1; goto exit_function; } memcpy( nGroupNumber, nGroupNumberStackArray, nNumGroups*sizeof(nGroupNumber[0])); } } nGroupNumber[j] = group; /* old t-group ID */ nGroupNewNumber[j] = EndPoint[i].nEquNumber; /* new t-group ID */ nNumGroups ++; } } else { /* add a new endpoint to the newly created or previously existing t-groups */ group = EndPoint[i].nEquNumber; if ( group >= nNextGroupNumber ) { /* get index of a new t-group from equ number */ j = num_t + group - nNextGroupNumber; /* newly assigned IDs are contiguous */ } else { /* old t-group */ if ( j >= num_t || group != t_group[j].nGroupNumber ) { /* search only if j is not a needed group index */ for ( j = 0; j < num_t; j ++ ) { if ( group == t_group[j].nGroupNumber ) break; } if ( j == num_t ) { ret = -1; /* program error: t-group not found */ /* */ goto exit_function; } } } /* add aton to existing or new t-group */ t_group[j].nNumEndpoints ++; for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) t_group[j].num[k] += EndPoint[i].num[k]; for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) t_group[j].num_DA[k] += EndPoint[i].num_DA[k]; /* mark endpoint */ at[EndPoint[i].nAtomNumber].endpoint = group; difference ++; } } difference += nNumGroups; num_t += nNumNewTGroups; if ( !difference ) { ret = 0; /* nothing to do. Not necessarily a program error: happens if all EndPoint[i].nGroupNumber==EndPoint[i].nEquNumber */ goto exit_function; } if ( nNumGroups ) { /* prepare for renumbering: find max t-group number */ for ( i = 0, nNextGroupNumber = 0; i < num_t; i ++ ) { if ( nNextGroupNumber < t_group[i].nGroupNumber ) { nNextGroupNumber = t_group[i].nGroupNumber; } } } /* renumber and merge t-groups */ for ( i = 0; i < nNumGroups; i ++ ) { int i1, i2; AT_NUMB group1 = nGroupNumber[i]; AT_NUMB group2 = nGroupNewNumber[i]; /* add group1 to group2, then delete group1. */ for ( j = 0, i1 = i2 = -1; j < num_t && (i1 < 0 || i2 < 0); j ++ ) { if ( i1 < 0 && group1 == t_group[j].nGroupNumber ) i1 = j; if ( i2 < 0 && group2 == t_group[j].nGroupNumber ) i2 = j; } if ( i1 < 0 || i2 < 0 ) { ret = -1; /* program error */ /* */ goto exit_function; } /* add t_group[i1] to t_group[i2] and remove t_group[i1] */ for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) t_group[i2].num[k] += t_group[i1].num[k]; for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) t_group[i2].num_DA[k] += t_group[i1].num_DA[k]; t_group[i2].nNumEndpoints += t_group[i1].nNumEndpoints; num_t --; if ( num_t > i1 ) { memmove( t_group+i1, t_group+i1+1, ( num_t - i1)*sizeof(t_group[0]) ); } } if ( nNumGroups ) { /* there are groups to merge */ if ( nNextGroupNumber >= MAX_STACK_ARRAY_LEN ) { nNewTgNumber = (AT_NUMB *) inchi_malloc((nNextGroupNumber+1)*sizeof(*nNewTgNumber)); if ( !nNewTgNumber ) { ret = -1; goto exit_function; /* error: out of RAM */ } } memset( nNewTgNumber, 0, (nNextGroupNumber+1)*sizeof(*nNewTgNumber) ); for ( i = 0; i < num_t; i ++ ) { nNewTgNumber[t_group[i].nGroupNumber] = i+1; /* new t-group numbers */ } for ( j = 0; j < nNumGroups; j ++ ) { if ( !nNewTgNumber[nGroupNumber[j]] && nNewTgNumber[nGroupNewNumber[j]] ) { nNewTgNumber[nGroupNumber[j]] = nNewTgNumber[nGroupNewNumber[j]]; } else { ret = -1; /* program error: all new numbers must have been marked */ goto exit_function; } } /* renumber t-groups */ for ( i = 0; i < num_t; i ++ ) { t_group[i].nGroupNumber = nNewTgNumber[t_group[i].nGroupNumber]; } #if ( bRELEASE_VERSION != 1 ) /* Check: debug only */ for ( i = 1; i < num_t; i ++ ) { if ( 1 != t_group[i].nGroupNumber - t_group[i-1].nGroupNumber ) { ret = -1; /* debug */ goto exit_function; } } #endif /* renumber endpoints */ for ( i = 0; i < num_atoms; i ++ ) { if ( group = at[i].endpoint ) { if ( !(at[i].endpoint = nNewTgNumber[group]) || nNextGroupNumber <= nNewTgNumber[group] ) { ret = -1; /* program error */ goto exit_function; } } } } if ( nNewTgNumber != nNewTgNumberStackArray ) { inchi_free( nNewTgNumber ); nNewTgNumber = nNewTgNumberStackArray; } if ( nGroupNumber != nGroupNumberStackArray ) { inchi_free(nGroupNumber); nGroupNumber = nGroupNumberStackArray; } if ( nGroupNewNumber != nGroupNewNumberStackArray ) { inchi_free( nGroupNewNumber ); nGroupNewNumber = nGroupNewNumberStackArray; } if ( !t_group_info->tGroupNumber ) { t_group_info->tGroupNumber = (AT_NUMB *) inchi_malloc(2*max_num_t*sizeof(t_group_info->tGroupNumber[0])); if ( !t_group_info->tGroupNumber ) { ret = -1; goto exit_function; } } /* fill out t-group index 2004-02-27 */ memset( t_group_info->tGroupNumber, 0, 2*max_num_t*sizeof(t_group_info->tGroupNumber[0]) ); for ( i = 0; i < num_t; i ++ ) { if ( t_group[i].nNumEndpoints && t_group[i].nGroupNumber ) t_group_info->tGroupNumber[t_group[i].nGroupNumber] = i+1; } if ( pBNS && (pBNS->tot_st_cap == pBNS->tot_st_flow || ALWAYS_ADD_TG_ON_THE_FLY) ) { T_GROUP_INFO tgi; int ret_bns; memset( &tgi, 0, sizeof(tgi) ); tgi.num_t_groups = num_t; tgi.t_group = t_group; #if ( KETO_ENOL_TAUT == 1 ) tgi.bTautFlags |= (t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT); /* needed in AddTGroups2BnStruct() */ #endif /* reinitialize BN Structure */ ret_bns = ReInitBnStruct( pBNS, at, num_atoms, 0 ); if ( IS_BNS_ERROR( ret_bns ) ) { return ret_bns; } if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { /* set new charge groups */ ret_bns = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); if ( IS_BNS_ERROR( ret_bns ) ) { return ret_bns; } } /* set new tautomeric groups */ ret_bns = AddTGroups2BnStruct( pBNS, at, num_atoms, &tgi ); if ( IS_BNS_ERROR( ret_bns ) ) { return ret_bns; } } *pnum_t = num_t; return difference; exit_function: if ( nNewTgNumber != nNewTgNumberStackArray ) { inchi_free( nNewTgNumber ); } if ( nGroupNumber != nGroupNumberStackArray ) { inchi_free(nGroupNumber); } if ( nGroupNewNumber != nGroupNewNumberStackArray ) { inchi_free( nGroupNewNumber ); } return ret; } /***************************************************************************** * Change non-alternating and non-tautomeric bonds * (that is, single and double bonds) to tautomeric *****************************************************************************/ int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ) { int k, n; for ( k = n = 0; k < nNumBondPos; k ++ ) { int neighbor_index = BondPos[k].neighbor_index; int center = BondPos[k].nAtomNumber; int bond_mark = at[center].bond_type[neighbor_index]; int bond_type = bond_mark & ~BOND_MARK_ALL; int neighbor; #if ( REPLACE_ALT_WITH_TAUT == 1 ) if ( bond_type != BOND_TAUTOM ) #else if ( bond_type != BOND_ALTERN && bond_type != BOND_TAUTOM ) #endif { int ii; /* change bond type to BOND_TAUTOM presering higher bits marks */ bond_type = (bond_mark & BOND_MARK_ALL) | BOND_TAUTOM; /* change center-neighbor bond */ at[center].bond_type[neighbor_index] = bond_type; neighbor = at[center].neighbor[neighbor_index]; for ( ii = 0; ii < at[neighbor].valence; ii ++ ) { if ( at[neighbor].neighbor[ii] == center ) { /* neighbor-center bond found */ at[neighbor].bond_type[ii] = bond_type; break; } } n ++; } } return n; } /*****************************************************************************/ int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ) { AT_NUMB ri = *pri; AT_NUMB rj = *prj; int i, k; AT_NUMB c_point, endpoint, r; if ( (c_point = at[ri].c_point) && (c_point == at[rj].c_point) && (at[ri].charge == 1 || at[rj].charge == 1) && cgi && cgi->num_c_groups > 0 ) { /* at[ri] and at[rj] belong to the same charge group, at least one is charged */ /* MS VC++ 2005 reports unreachable code here ??? */ for ( k = 0; k < cgi->num_c_groups; k ++ ) { if ( cgi->c_group[k].nGroupNumber == c_point ) { /* cgi->c_group[k] is found to be this charge group */ if ( cgi->c_group[k].num_CPoints - cgi->c_group[k].num[0] < 2 ) { /* Only one neutral in the c-group: we will not be able to neutralize both when looking for the alt path to discover the tautomerism. Therefore we need to find a neutral t-group representative */ /* at[rj] */ if ( endpoint = at[rj].endpoint ) { for ( i = 0; i < nNumEndPoints; i ++ ) { if ( (r=EndPoint[i].nAtomNumber) == *prj ) continue; /* ignore at[*prj] */ if ( at[r].endpoint != endpoint ) continue; /* at[r] does not belong to the same t-group as at[*prj]; ignore the atom */ if ( !at[r].c_point ) { rj = r; /* found a neutral t-group representative */ break; } if ( at[r].c_point != c_point && c_point == at[rj].c_point ) { /* replace only once because of (c_point == at[rj].c_point) condition */ rj = r; } } if ( rj == *prj /*&& at[ri].endpoint*/ ) { /* !!! "&& at[ri].endpoint": only between 2 t-groups 2004-02-27; the change disabled due to undiscovered yet possibility of ambiguity*/ /* no replacement has been found in EndPoint[]; try all atoms in the t-group */ for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].endpoint != endpoint ) continue; if ( i == (int)*prj ) continue; if ( !at[i].c_point ) { rj = (AT_NUMB)i; /* found neutral t-group representative */ break; } if ( at[i].c_point != c_point && c_point == at[rj].c_point ) { /* replace only once */ rj = (AT_NUMB)i; } } } } /* at[ri] */ if ( endpoint = at[ri].endpoint ) { for ( i = 0; i < nNumEndPoints; i ++ ) { if ( (r=EndPoint[i].nAtomNumber) == *pri ) continue; if ( at[r].endpoint != endpoint ) continue; if ( !at[r].c_point ) { ri = r; /* found neutral t-group representative */ break; } if ( at[r].c_point != c_point && c_point == at[ri].c_point && at[r].c_point != at[rj].c_point ) { /* replace only once */ ri = r; } } if ( ri == *pri && at[rj].endpoint ) { /* !!! "&& at[rj].endpoint": only between 2 t-groups 2004-02-27; the change disabled due to undiscovered yet possibility of ambiguity */ for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].endpoint != endpoint ) continue; if ( i == (int)*pri ) continue; if ( !at[i].c_point ) { ri = (AT_NUMB)i; /* found neutral t-group representative */ break; } if ( at[i].c_point != c_point && c_point == at[ri].c_point && at[i].c_point != at[rj].c_point) { /* replace only once */ ri = (AT_NUMB)i; } } } } } } break; } *prj = rj; *pri = ri; } return 0; } /*****************************************************************************/ int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ) { AT_NUMB nTGroupRepresenative[MAXVAL], nTGroupEqu[MAXVAL], nTGEndPointNo[MAXVAL], ri, rj; AT_NUMB nCurTGroupNumber, nMaxTGroupNumber, nNumTgroupNumbers, nMaxEquNumber; int i, j, k, nNumDiffTGroupNumbers = 0, nNumFoundEqu, nErr; if ( *nNumEndPoints != *nNumBondPos ) return 0; /* collect all group numbers. Fill EndPoint[i].nEquNumber */ for ( i = 0; i < *nNumEndPoints; i ++ ) { nCurTGroupNumber = EndPoint[i].nEquNumber = EndPoint[i].nGroupNumber; /* initial equivalence */ if ( nCurTGroupNumber ) { /* found endpoint that already belongs to a t-group */ for ( j = 0; j < nNumDiffTGroupNumbers; j ++ ) { if ( nTGroupEqu[j] == nCurTGroupNumber ) break; } if ( j == nNumDiffTGroupNumbers ) { nTGroupRepresenative[nNumDiffTGroupNumbers] = EndPoint[i].nAtomNumber; nTGroupEqu[nNumDiffTGroupNumbers] = EndPoint[i].nGroupNumber; nTGEndPointNo[nNumDiffTGroupNumbers] = i; nNumDiffTGroupNumbers ++; } } } /* check whether each pair belongs to the same t-group and establish the equivalence(s) */ for ( i = 0, nNumFoundEqu=0; i < nNumDiffTGroupNumbers; i ++ ) { for ( j = i+1; j < nNumDiffTGroupNumbers; j ++ ) { ri = nTGroupRepresenative[i]; rj = nTGroupRepresenative[j]; /* both at[ri] and at[rj] are known to belong to tautomeric groups */ GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); if ( IS_BNS_ERROR(nErr) ) return nErr; if ( 0 == nErr ) continue; /* alt path between at[ri] and at[rj] not found */ nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); for ( k = 0; k < nNumDiffTGroupNumbers; k ++ ) { if ( nTGroupEqu[k]==nMaxTGroupNumber ) { nTGroupEqu[k] = nCurTGroupNumber; nNumFoundEqu ++; } } for ( k = 0; k < *nNumEndPoints; k ++ ) { if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { EndPoint[k].nEquNumber = nCurTGroupNumber; } } } } if ( nNumFoundEqu ) { /* leave in only non-equivalent representatives */ for ( i = 1, k = 0; i < nNumDiffTGroupNumbers; i ++ ) { for ( j = 0; j < i; j ++ ) { if ( nTGroupEqu[j] == nTGroupEqu[i] ) { nTGroupEqu[i] = 0; /* i > j; mark equivalent for removal*/ break; } } } for ( i = j = 0; i < nNumDiffTGroupNumbers; i ++ ) { if ( nTGroupEqu[i] ) { if ( i != j ) { /* remove the marked */ nTGroupEqu[j] = nTGroupEqu[i]; nTGroupRepresenative[j] = nTGroupRepresenative[i]; nTGEndPointNo[j] = nTGEndPointNo[i]; } j ++; } } nNumDiffTGroupNumbers = j; /* number of known t-group representatives */ } /* collect endpoints that have not been assigned to t-groups */ for ( i = 0, j = nNumDiffTGroupNumbers; i < *nNumEndPoints; i ++ ) { if ( EndPoint[i].nEquNumber ) continue; nTGroupEqu[j] = 0; nTGroupRepresenative[j] = EndPoint[i].nAtomNumber; nTGEndPointNo[j] = i; j ++; } nNumTgroupNumbers = j; nMaxEquNumber = num_atoms + 1; /* impossible atom or t-group number */ /* check whether each pair belongs to the same group and establish the equivalence(s) */ for ( i = 0, nNumFoundEqu=0; i < nNumTgroupNumbers; i ++ ) { for ( j = i+1; j < nNumTgroupNumbers; j ++ ) { if ( nTGroupEqu[i] != nTGroupEqu[j] && (i>=nNumDiffTGroupNumbers || j>=nNumDiffTGroupNumbers) || /* equivalence of a t-group and a non-t-group atom */ !nTGroupEqu[i] && !nTGroupEqu[j] /* equivalence of two non-t-group atoms */ ) { ri = nTGroupRepresenative[i]; rj = nTGroupRepresenative[j]; /*------------------------------!!!--------------------------------------------- Explanation why GetNeutralRepsIfNeeded() may need to be changed 2004-02-27 The change has been disabled due to undiscovered yet possibility of ambiguity to search for neutral only among EndPoint[] in case taut-not_taut pairs Counterexample: O=C-NH(+)=C-NH2 1 2 3 Has already been found: 2-3 (+)-charge exchange 1-2 tautomerism (charge removed to 3) Now testing: 2-3 tautomerism. If not commented out, GetNeutralRepsIfNeeded() would replace 2-3 test with 1-3 test because: o Charge group has only one neutral and both 2 and 3 belong to it, therefore we cannot neutralize both; search for neutral representative; o Since 1 and 2 belong to the same t-group and 1 is neutral, test 1-3 instead of 2-3. This breaks our condition: Test tautomeric H movement only between neutral atoms. -----------------------------------------------------------------------------*/ GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); if ( IS_BNS_ERROR(nErr) ) return nErr; if ( nErr <= 0 ) continue; if ( nTGroupEqu[i] && nTGroupEqu[j] ) { /* found equivalence of two t-groups; at least one of them must be a new one */ nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); for ( k = 0; k < nNumTgroupNumbers; k ++ ) { if ( nTGroupEqu[k]==nMaxTGroupNumber ) { nTGroupEqu[k] = nCurTGroupNumber; nNumFoundEqu ++; } } for ( k = 0; k < *nNumEndPoints; k ++ ) { if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { EndPoint[k].nEquNumber = nCurTGroupNumber; } } } else if ( nTGroupEqu[i] ) { /* extend existing t-group */ nTGroupEqu[j] = nTGroupEqu[i]; EndPoint[nTGEndPointNo[j]].nEquNumber = nTGroupEqu[i]; } else if ( nTGroupEqu[j] ) { /* extend existing t-group */ nTGroupEqu[i] = nTGroupEqu[j]; EndPoint[nTGEndPointNo[i]].nEquNumber = nTGroupEqu[j]; } else { /* establis a new t-group */ nTGroupEqu[i] = nTGroupEqu[j] = nMaxEquNumber; /* assign a fict. ID to establish equivalence */ EndPoint[nTGEndPointNo[i]].nEquNumber = EndPoint[nTGEndPointNo[j]].nEquNumber = nMaxEquNumber; nMaxEquNumber ++; } } } } /* eliminate endpoints and bonds that do not belong to t-group(s) (they have not been found connected by an alt path to any other endpoint) */ for ( i = 0, j = 0; i < *nNumEndPoints; i ++ ) { if ( EndPoint[i].nEquNumber ) { #if ( IGNORE_SINGLE_ENDPOINTS == 1 ) /* 1-28-2003 */ for ( k = 0, nNumFoundEqu = 0; k < *nNumEndPoints; k ++ ) { nNumFoundEqu += (EndPoint[i].nEquNumber == EndPoint[k].nEquNumber); } if ( nNumFoundEqu <= 1 ) { /* one time it is equal to itself when i == k above */ /* if EndPoint[i] is not "equivalent" to any other EndPoint then ignore it */ continue; } #endif if ( i != j ) { /* save endpoints that are found to be connected to other endpoints by alt paths */ EndPoint[j] = EndPoint[i]; BondPos[j] = BondPos[i]; } j ++; } } #if ( IGNORE_SINGLE_ENDPOINTS != 1 ) /* 1-28-2003 */ /* Do not allow a centerpoint to have only one tautomeric bond */ /* Hack: we may have only one centerpoint */ /* BondPos[*].nAtomNumber are centerpoints */ if ( j == 1 ) { /* check if there exist other centerpoint neighbors * connected to it by another tautomeric-bond */ for ( i = 0, k = 0; i < at[BondPos[0].nAtomNumber].valence; i ++ ) { k += ( i != BondPos[0].neighbor_index && BOND_TAUTOM == (at[BondPos[0].nAtomNumber].bond_type[i] & ~BOND_MARK_ALL)); } if ( !k ) { j = 0; } } #endif *nNumEndPoints = *nNumBondPos = j; return j; } /*****************************************************************************/ /*#if ( MOVE_CHARGES == 1 ) */ /* { */ /*****************************************************************************/ /**********************************************/ /* */ /* definitions for positive ion recognition */ /* */ /**********************************************/ /*****************************************************************************/ typedef struct tagChargeType { /* meaning see in bCanBeACPoint() */ char elname[3]; S_CHAR charge; S_CHAR neutral_valence; S_CHAR neutral_bonds_valence; /* valence of a neutral atom */ S_CHAR cChangeValence; /* charge increases valence by this value */ S_CHAR cChargeType; /* different types are treated separately */ S_CHAR num_bonds; /* added 02-06-2005 */ } CHARGE_TYPE; CHARGE_TYPE CType[] = { { "N\0", 1, 3, 3, 1, 0, 0 }, { "P\0", 1, 3, 3, 1, 1, 0 }, #if ( ADD_MOVEABLE_O_PLUS == 1 ) { "O\0", 1, 2, 2, 1, 2, 2 }, /* added 02-06-2005 */ { "S\0", 1, 2, 2, 1, 3, 2 }, /* added 03-18-2005 */ { "Se", 1, 2, 2, 1, 4, 2 }, /* added 03-18-2005 */ { "Te", 1, 2, 2, 1, 5, 2 }, /* added 03-18-2005 */ #endif }; /* bits */ #define C_SUBTYPE_CHARGED 0 #define C_SUBTYPE_p_DONOR 1 /* new */ #define C_SUBTYPE_p_ACCEPT 2 /* new */ #define C_SUBTYPE_H_ACCEPT 4 #define C_SUBTYPE_H_DONOR 8 #define C_SUBTYPE_NEUTRAL 16 /* make sure any C_SUBTYPE_CHARGED_... < any C_SUBTYPE_NEUTRAL_... */ /* charged */ #define C_SUBTYPE_CHARGED_NON_TAUT (C_SUBTYPE_CHARGED) #define C_SUBTYPE_CHARGED_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_p_DONOR) #define C_SUBTYPE_CHARGED_H_ACCEPT (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT) #define C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_DONOR) #define C_SUBTYPE_CHARGED_H_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_DONOR |C_SUBTYPE_p_DONOR) /* neutral */ #define C_SUBTYPE_NEUTRAL_NON_TAUT (C_SUBTYPE_NEUTRAL) #define C_SUBTYPE_NEUTRAL_H_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT) #define C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_ACCEPT) #define C_SUBTYPE_NEUTRAL_H_DONOR (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_DONOR) #define NUM_C_TYPES (int)(sizeof( CType )/sizeof(CType[0])) /*****************************************************************************/ /*****************************************************************************/ int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ) { int nChangeValence; int nNumBonds; int nBondsValence; int bNegCharge = (at->charge == -1); /* add fict. bonds to (-) 2004-02-24*/ if ( at->charge == cCharge && at->valence == at->chem_bonds_valence && at->num_H ) { /* proton donors candidates >NH(+)-, >NH2(+), -NH3(+), >OH(+), -OH2(+) */ /* charged, added p-transfer -- 01-28-2004 */ nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ nBondsValence = at->chem_bonds_valence + at->num_H; if ( nBondsValence == neutral_bonds_valence + nChangeValence && nEndpointValence ) { *cChargeSubtype = C_SUBTYPE_CHARGED_p_DONOR; /* ignore Phosphorus p-donors for now */ } return 0; } else if ( at->charge == cCharge && at->valence < at->chem_bonds_valence ) { /* the requirement at->valence < at->chem_bonds_valence rejects candidates >NH(+)-, >NH2(+), -NH3(+), >N(+)<, >OH(+), -OH2(+), >O(+)- Moveable charge requires double bonds; these ions have no double bonds */ /* charged */ nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ nBondsValence = at->chem_bonds_valence + at->num_H; nNumBonds = at->valence + at->num_H; if ( nBondsValence == neutral_bonds_valence + nChangeValence ) { /* known valence */ if ( nNumBonds == neutral_valence ) { /* non-tautomeric: >N(+)=, =O(+)- possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ if ( at->valence == neutral_valence || !nEndpointValence ) { /* non-tautomeric: >N(+)=, =O(+)-; any suitable P+: >P(+)=, =PH(+)-, =PH2(+) */ *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; } else { /* possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ *cChargeSubtype = C_SUBTYPE_CHARGED_H_DONOR; } return 1; } if ( nNumBonds == neutral_valence - 1 ) { /* possibly tutomeric acceptor: =N(+)=, #N(+)-, #NH(+), #O(+) */ if ( nEndpointValence ) { *cChargeSubtype = at->num_H? C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR : C_SUBTYPE_CHARGED_H_ACCEPT; } else { /* =P(+)=, #P(+)-, #PH(+) */ *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; } return 1; /* charge type, charged */ } } } else if ( at->charge == 0 || bNegCharge ) { /* neutral atom or anion, all bonds are single */ nBondsValence = at->chem_bonds_valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ nNumBonds = at->valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ if ( nBondsValence == neutral_bonds_valence ) { if ( nNumBonds == neutral_valence ) { /* only single bonds: >N-, >NH, -NH2, -O-, -OH, >P- >PH -PH2 */ /* >N(-), -NH(-), -O(-). >P(-) -PH(-) */ if ( at->valence == neutral_valence || !nEndpointValence ) { /* >N-, -O-, any P(3 single bonds): >P- >PH -PH2 */ *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; } else if ( at->valence < neutral_valence /*&& nEndpointValence */ ) { /* num_H > 0: >NH -NH2 -OH */ /* num_H = 0: none C_SUBTYPE_NEUTRAL_H_ACCEPT for now */ *cChargeSubtype = at->num_H? C_SUBTYPE_NEUTRAL_H_DONOR: C_SUBTYPE_NEUTRAL_H_ACCEPT; } else { return 0; } return 1; /* charge type, neutral */ } if ( nNumBonds == neutral_valence - 1 ) { /* possibly tautomeric acceptor =N-, =NH, =O or non-taut =P-, =PH */ if ( nEndpointValence ) { /* =N-, =NH, =O */ *cChargeSubtype = C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT; } else { /* =P-, =PH */ *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; } return 1; /* charge type, (+) => neutral */ } } } return 0; } /*****************************************************************************/ int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ) { int i, n; S_CHAR nEndpointValence; inp_ATOM *at = atom + iat; *cChargeSubtype = 0; /* ignore ion pairs and charges != 1 */ if ( abs(at->charge) == 1 ) { for ( i = 0; i < at->valence; i ++ ) { n = at->neighbor[i]; /* allow negatively charged tautomeric neighbors 2004-02-26 */ if ( abs(atom[n].charge + at->charge) < abs(atom[n].charge - at->charge) && !atom[n].endpoint ) { return -1; /* charges have different signs */ } } } else if ( at->charge ) { return -1; /* abs(charge) != 1 */ } /* find candidates */ for ( i = 0; i < NUM_C_TYPES; i ++ ) { if ( !strcmp( at->elname, CType[i].elname ) && (!CType[i].num_bonds || CType[i].num_bonds==at->valence && at->nNumAtInRingSystem >= 5) ) { nEndpointValence = (S_CHAR)get_endpoint_valence(at->el_number ); if ( bCanBeACPoint( at, CType[i].charge, CType[i].cChangeValence, CType[i].neutral_bonds_valence, CType[i].neutral_valence, nEndpointValence, cChargeSubtype ) ) { return CType[i].cChargeType; } } } return -1; } /*****************************************************************************/ int CmpCCandidates( const void *a1, const void *a2 ) { const C_CANDIDATE *c1 = (const C_CANDIDATE *)a1; const C_CANDIDATE *c2 = (const C_CANDIDATE *)a2; int ret; if ( ret = (int)c1->type - (int)c2->type ) return ret; if ( ret = (int)c1->subtype - (int)c2->subtype ) return ret; ret = (int)c1->atnumber - (int)c2->atnumber; return ret; } /*****************************************************************************/ int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ) { int num_c = *pnum_c, i, i1, i2; AT_NUMB nGroupNumber = 0, nNewGroupNumber; if ( at[point1].c_point == at[point2].c_point ) { if ( at[point1].c_point ) return 0; memset( c_group+num_c, 0, sizeof(c_group[0]) ); if ( num_c < max_num_c ) { c_group[num_c].num[0] = CHARGED_CPOINT(at,point1) + CHARGED_CPOINT(at, point2); c_group[num_c].num_CPoints += 2; c_group[num_c].cGroupType = ctype; /* get next available c-group number */ for ( i = 0; i < num_c; i ++ ) { if ( nGroupNumber < c_group[i].nGroupNumber ) nGroupNumber = c_group[i].nGroupNumber; } nGroupNumber ++; c_group[num_c].nGroupNumber = at[point1].c_point = at[point2].c_point = nGroupNumber; *pnum_c = num_c+1; /* count protons */ if ( at[point1].num_H ) { c_group[num_c].num[1] ++; } else if ( at[point2].num_H ) { c_group[num_c].num[1] ++; } else if ( (at[point1].endpoint || at[point2].endpoint) && t_group_info && t_group_info->t_group && t_group_info->num_t_groups ) { /* !!! add later !!! */ } return 1; } return BNS_CPOINT_ERR; /* overflow */ } if ( at[point1].c_point > at[point2].c_point ) { /* make sure at[point1].c_point < at[point2].c_point */ i = point1; point1 = point2; point2 = i; } if ( !at[point1].c_point ) { /* add a new c-endpoint to an existing c-group */ nGroupNumber = at[point2].c_point; for ( i = 0; i < num_c; i ++ ) { if ( nGroupNumber == c_group[i].nGroupNumber ) { at[point1].c_point = at[point2].c_point; c_group[i].num_CPoints ++; c_group[i].num[0] += CHARGED_CPOINT(at,point1); return 1; } } return BNS_CPOINT_ERR; /* program error: c-group not found */ } else { /* merge two c-groups */ nNewGroupNumber = at[point1].c_point; nGroupNumber = at[point2].c_point; for ( i = 0, i1=i2=-1; i < num_c && (i1 < 0 || i2 < 0); i ++ ) { if ( nNewGroupNumber == c_group[i].nGroupNumber ) { i1 = i; continue; } if ( nGroupNumber == c_group[i].nGroupNumber ) { i2 = i; continue; } } if ( i1 < 0 || i2 < 0 ) { return BNS_CPOINT_ERR; /* at least one not found */ } c_group[i1].num[0] += c_group[i2].num[0]; c_group[i1].num_CPoints += c_group[i2].num_CPoints; num_c --; if ( num_c > i2 ) { memmove( c_group+i2, c_group+i2+1, ( num_c - i2)*sizeof(c_group[0]) ); } *pnum_c = num_c; /* renumber c-groups */ for ( i = 0; i < num_c; i ++ ) { if ( c_group[i].nGroupNumber > nGroupNumber ) { c_group[i].nGroupNumber --; } } /* renumber c-points */ for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].c_point > nGroupNumber ) { at[i].c_point --; } else if ( at[i].c_point == nGroupNumber ) { at[i].c_point = nNewGroupNumber; } } return 1; } } /*****************************************************************************/ int MarkChargeGroups(inp_ATOM *at, int num_atoms, C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD) { int nNumChanges = 0; if ( c_group_info && c_group_info->c_candidate && c_group_info->max_num_candidates > 0 ) { int i, i1, i2, i3, j, num_tested; C_CANDIDATE *c_candidate = c_group_info->c_candidate; int nMaxNumCandidates = c_group_info->max_num_candidates; int nNumCandidates = c_group_info->num_candidates; S_CHAR c_type, c_subtype; int iat1, iat2, ret, nDelta; if ( nNumCandidates == -1 ) { nNumCandidates = 0; /* 2004-02-26 they could appear after t-group discovery */ /*return 0;*/ } if ( nNumCandidates == 0 ) { for ( i = 0, nNumCandidates = 0; i < num_atoms; i ++ ) { if ( 0 <= (c_type = GetChargeType( at, i, &c_subtype )) ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } c_candidate[nNumCandidates].atnumber = i; c_candidate[nNumCandidates].type = c_type; c_candidate[nNumCandidates].subtype = c_subtype; nNumCandidates ++; } } if ( nNumCandidates <= 1 ) { c_group_info->num_candidates = -1; /* no candidate exists */ return 0; } } /* sorting keys: (1) atom type (N,P); (2) uncharged=16/charged=0; (3) other; atom-charged-N .... i1 ... atom-charged-N atom-neutral-N .... i2 ... atom-neutral-N atom-charged-P .... i3 ... i1 ... atom-charged-P atom-neutral-P ........... i2 ... atom-neutral-P end. ........... i3 */ qsort(c_candidate, nNumCandidates, sizeof(c_candidate[0]), CmpCCandidates); i1 = 0; num_tested = 0; nDelta = 0; while ( i1 < nNumCandidates ) { /* the the first charged candidate of a new atom type */ for (; i1 < nNumCandidates && (c_candidate[i1].subtype & C_SUBTYPE_NEUTRAL); i1 ++) ; if ( i1 == nNumCandidates ) break; /* not found */ /* bypass other charged candidates of the same atom type */ for ( i2 = i1+1; i2 < nNumCandidates && c_candidate[i2].type == c_candidate[i1].type && !(c_candidate[i2].subtype & C_SUBTYPE_NEUTRAL); i2++ ) ; if ( i2 == nNumCandidates ) break; /* no neutral candidates */ /* find next to the last neutral candidate of the same atom type */ for ( i3 = i2; i3 < nNumCandidates && c_candidate[i3].type == c_candidate[i1].type; i3 ++ ) ; if ( i3 == i2 ) { /* no neutral candidates found */ if ( i2 < nNumCandidates ) { i1 = i3; continue; /* move to the next atom type */ } break; /* nothing more to do */ } /* found charged candidates: i1...i2-1; neutral candidates: i2...i3-1 */ for ( i = i1; i < i2; i ++ ) { iat1 = c_candidate[i].atnumber; for ( j = i2; j < i3; j ++ ) { /* check alt path at[iat1]=-=-...-at[iat2]; at[iat1] is charged, at[iat2] is neutral */ num_tested ++; iat2 = c_candidate[j].atnumber; if ( at[iat1].c_point && at[iat1].c_point == at[iat2].c_point ) continue; ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, iat1, iat2, ALT_PATH_MODE_CHARGE ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (ret & 2); ret = RegisterCPoints( c_group_info->c_group, &c_group_info->num_c_groups, c_group_info->max_num_c_groups, t_group_info, iat1, iat2, c_candidate[i1].type, at, num_atoms ); if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( nDelta ) { goto quick_exit; } } } } i1 = i3; } quick_exit: if ( c_group_info->num_candidates == 0 ) { /* first time: initialize */ c_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ } } return nNumChanges; } /*****************************************************************************/ int GetSaltChargeType(inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ) { static int el_number_C = 0; static int el_number_O = 0; static int el_number_S = 0; static int el_number_Se = 0; static int el_number_Te = 0; /* type (returned value): -1 => ignore 0 => oxygen subtype: 1 = SALT_DONOR_H => has H 2 = SALT_DONOR_Neg => has (-) charge 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily O-atom should be: - a terminal atom - connected to unsaturated, uncharged, non-radical atom C that has chemical valence 4: H-donors: =CH-OH, =C(-X)-OH possible H-acceptors: -CH=O, >C=O H-acceptors are true if O is tautomeric */ int iC, tg, i, type; /* one-time initialization */ if ( !el_number_O ) { el_number_C = get_periodic_table_number( "C" ); el_number_O = get_periodic_table_number( "O" ); el_number_S = get_periodic_table_number( "S" ); el_number_Se = get_periodic_table_number( "Se" ); el_number_Te = get_periodic_table_number( "Te" ); } *s_subtype = 0; /* initialize the output */ /* check whether it is a candidate */ if ( at[at_no].valence != 1 || at[at_no].radical && at[at_no].radical != RADICAL_SINGLET || at[at_no].charge < -1 || at[at_no].charge > 0 && !at[at_no].c_point ) { return -1; } if ( at[at_no].el_number == el_number_O || at[at_no].el_number == el_number_S || at[at_no].el_number == el_number_Se || at[at_no].el_number == el_number_Te ) { type = 0; /* terminal oxygen atom, needs more to be checked... */ } else { type = -1; /* ignore this atom */ } if ( type < 0 || at[at_no].chem_bonds_valence + at[at_no].num_H != get_el_valence(at[at_no].el_number, at[at_no].charge, 0) ) { return -1; /* non-standard valence or not an oxygen */ } iC = at[at_no].neighbor[0]; #if ( SALT_WITH_PROTONS == 1 ) if ( at[iC].el_number != el_number_C || at[iC].chem_bonds_valence + at[iC].num_H != 4 || /* allow =C(H)-OH or -C(H)=O */ at[iC].charge || at[iC].radical && at[iC].radical != RADICAL_SINGLET || at[iC].valence == at[iC].chem_bonds_valence ) { return -1; /* oxigen is connected to a wrong atom */ } #else if ( at[iC].el_number != el_number_C || at[iC].num_H || at[iC].chem_bonds_valence != 4 || /* allow only no H on C */ at[iC].charge || at[iC].radical && at[iC].radical != RADICAL_SINGLET || at[iC].valence == at[iC].chem_bonds_valence ) { return -1; /* oxigen is connected to a wrong atom */ } #endif if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { /* O-atom is in a tautomeric group */ for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { if ( tg == t_group_info->t_group[i].nGroupNumber ) { /* t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges t_group_info->t_group[i].num[1] = number of attached negative charges */ if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { *s_subtype |= SALT_DONOR_H; /* has H */ } if ( t_group_info->t_group[i].num[1] ) { *s_subtype |= SALT_DONOR_Neg; /* has (-) */ } *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ return type; } } return -1; /* error: t-group not found */ } /* O is not not in a tautomeric group */ /* assume valence(O-) < valence(O) < valence(O+) */ if ( at[at_no].charge == -1 ) { *s_subtype |= SALT_DONOR_Neg; /* has (-) */ } if ( at[at_no].charge <= 0 && at[at_no].num_H ) { *s_subtype |= SALT_DONOR_H; /* has H */ } if ( at[at_no].charge == 0 && at[at_no].chem_bonds_valence == 2 ) { *s_subtype |= SALT_ACCEPTOR; } /* since O cannot be a charge point, the following cannot happen: */ if ( at[at_no].charge == 1 && at[at_no].c_point && at[at_no].chem_bonds_valence == 2 && at[at_no].num_H ) { *s_subtype |= SALT_DONOR_H; /* has H */ } return type; } /*****************************************************************************/ int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no) { static int el_number_N = 0; if ( !el_number_N ) { el_number_N = get_periodic_table_number( "N" ); } if ( at[at_no].el_number == el_number_N ) { return 1; } return 0; } /*****************************************************************************/ int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ) { /* static int el_number_C = 0; */ /* static int el_number_N = 0; */ static int el_number_O = 0; static int el_number_S = 0; static int el_number_Se = 0; static int el_number_Te = 0; /* type (returned value): -1 => ignore 1 => not an oxygen subtype: 1 = SALT_DONOR_H => has H 2 = SALT_DONOR_Neg => has (-) charge 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily the atom should be: - a tautomeric endpoint atom - connected to possible centerpoint atom another description of the atom searched here: any possibly tautomeric atom adjacent to a possibly centerpoint that has at least one double bond (possibly if positively charged); if eif.cAcceptor then the bond between the atom and the centerpoint must be possibly double if eif.cAcceptor then the bond must be possibly single Donors that belong to a t-group are also acceptors */ int tg, i, j, type, endpoint_valence, num_centerpoints, bond_type, centerpoint; ENDPOINT_INFO eif; /* one-time initialization */ if ( !el_number_O && !bAccept_O ) { /* el_number_C = get_periodic_table_number( "C" ); */ /* el_number_N = get_periodic_table_number( "N" ); */ el_number_O = get_periodic_table_number( "O" ); el_number_S = get_periodic_table_number( "S" ); el_number_Se = get_periodic_table_number( "Se" ); el_number_Te = get_periodic_table_number( "Te" ); } *s_subtype = 0; /* initialize the output */ if ( !bAccept_O /* only N */ && (at[at_no].el_number == el_number_O || at[at_no].el_number == el_number_S || at[at_no].el_number == el_number_Se || at[at_no].el_number == el_number_Te ) ) { return -1; /* we are not looking for oxygen here */ } type = 1; if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) ) { return -1; /* not a possible endpoint */ } else { /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ /* check whether there is adjacent atom-candidate for a centerpoint */ num_centerpoints = 0; for ( j = 0; j < at[at_no].valence; j ++ ) { bond_type = (int)at[at_no].bond_type[j] & BOND_TYPE_MASK; centerpoint = (int)at[at_no].neighbor[j]; /* a centerpoint candidate */ if ( ( eif.cAcceptor && (bond_type == BOND_DOUBLE || bond_type == BOND_ALTERN || /* possibly double */ bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) || eif.cDonor && (bond_type == BOND_SINGLE || bond_type == BOND_ALTERN || /* possibly single */ bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) ) && (at[centerpoint].chem_bonds_valence > at[centerpoint].valence || /* check for possible endpoint added 2004-02-24 */ at[centerpoint].chem_bonds_valence == at[centerpoint].valence && (at[centerpoint].endpoint || at[centerpoint].c_point) /* tautomerism or charge may increment at[centerpoint].chem_bonds_valence*/ ) && is_centerpoint_elem( at[centerpoint].el_number ) ) { num_centerpoints ++; break; /* at least one possibly centerpoint neighbor has been found */ } } if ( !num_centerpoints ) { return -1; } /* moved here from just after "type = 1;" line 2004-02-26 */ if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { /* atom is in a tautomeric group */ for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { if ( tg == t_group_info->t_group[i].nGroupNumber ) { /* t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges t_group_info->t_group[i].num[1] = number of attached negative charges */ if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { *s_subtype |= SALT_DONOR_H; /* has H */ } if ( t_group_info->t_group[i].num[1] ) { *s_subtype |= SALT_DONOR_Neg; /* has (-) */ } *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ return type; } } return -1; /* error: t-group not found */ } if ( eif.cAcceptor ) { *s_subtype |= SALT_ACCEPTOR; } if ( eif.cDonor ) { if ( at[at_no].charge == -1 ) { *s_subtype |= SALT_DONOR_Neg; /* has (-) */ } if ( at[at_no].num_H ) { *s_subtype |= SALT_DONOR_H; /* has H */ } } } return type; } /*****************************************************************************/ int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ) { static int el_number_C = 0; /* static int el_number_N = 0; */ /* static int el_number_O = 0; */ static int el_number_S = 0; static int el_number_Se = 0; static int el_number_Te = 0; /* type (returned value): -1 => ignore 2 => found: SH proton donor -CH2-SH, >CH-SH, >C< S(-) proton acceptor -CH2-S(-), >CH-S(-), >C< subtype: 1 = SALT_DONOR_H => has H 2 = SALT_DONOR_Neg => has (-) charge 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily non-O-atom should be: - a tautomeric endpoint atom - connected to possible middle point atom */ int type, endpoint_valence, bond_type, centerpoint; ENDPOINT_INFO eif; if ( at[at_no].valence != 1 || at[at_no].chem_bonds_valence != 1 || 1 != (at[at_no].num_H==1) + (at[at_no].charge==-1) ) { return -1; } /* one-time initialization */ if ( !el_number_S ) { el_number_C = get_periodic_table_number( "C" ); /* el_number_N = get_periodic_table_number( "N" ); */ /* el_number_O = get_periodic_table_number( "O" ); */ el_number_S = get_periodic_table_number( "S" ); el_number_Se = get_periodic_table_number( "Se" ); el_number_Te = get_periodic_table_number( "Te" ); } *s_subtype = 0; /* initialize the output */ if ( !(at[at_no].el_number == el_number_S || at[at_no].el_number == el_number_Se || at[at_no].el_number == el_number_Te ) ) { return -1; /* we are not looking for oxygen here */ } type = 2; /* non-tautomeric p-donor or acceptor: C-SH, C-S(-) */ if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) || eif.cMoveableCharge && !at[at_no].c_point || !eif.cDonor || eif.cAcceptor ) { return -1; /* not a possible -SH or -S(-) */ } else { /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ /* check whether there is adjacent atom-candidate for a centerpoint */ centerpoint = (int)at[at_no].neighbor[0]; bond_type = (int)at[at_no].bond_type[0] & BOND_TYPE_MASK; if ( at[centerpoint].el_number != el_number_C || at[centerpoint].charge || at[centerpoint].radical && at[centerpoint].radical != RADICAL_SINGLET || at[centerpoint].valence != at[centerpoint].chem_bonds_valence ) { return -1; /* not a carbon with all single bonds */ } if ( at[at_no].num_H == 1 ) { *s_subtype |= SALT_p_DONOR; } else if ( at[at_no].charge == -1 ) { *s_subtype |= SALT_p_ACCEPTOR; } else { return -1; } } return type; } /********************************************************************************************************/ /* new version: merge all, check alt paths, then unmerge unreachable O-atoms if any */ /* Check for oxygen negative charge-H tautomerism (Salts) allowed long-range tautomerism; more than one H or (-) can be moved, for example: HO-C=C-O(-) O=C-C=O / \ / \ R R R R | | => | | R' R' R' R' \ / \ / O=C-C=O HO-C=C-O(-) To check: | | -add all possible HO-C=, O=C, (-)O-C= (including all containing O t-groups) into one t-group; -temporarily disconnect one of previously not belonging to any t-group O-atoms from the one t-group; -find whether there is an alt path allowing H or (-) to migrate from the temp. disconnected O to any one left in the group. If the alt path does not exist then the temp. disconnected atom does not participate in the H/(-) migrartion and it will be unmarked/unmerged. */ /*****************************************************************************/ int comp_candidates( const void *a1, const void *a2 ) { const S_CANDIDATE *s1 = (const S_CANDIDATE *)a1; const S_CANDIDATE *s2 = (const S_CANDIDATE *)a2; int ret; if ( s1->type >= 0 /* enabled < */ && s2->type < 0 /* disabled */ ) return -1; /* enabled goes first */ if ( s1->type < 0 /* disabled > */ && s2->type >= 0 /* enabled */ ) return 1; if ( s1->endpoint && !s2->endpoint ) return -1; /* tautomeric goes first; only tautomeric may be disabled */ if ( !s1->endpoint && s2->endpoint ) return 1; /* tautomeric goes first; only tautomeric may be disabled */ if ( s1->endpoint && s2->endpoint && (ret = (int)s1->endpoint - (int)s2->endpoint) ) { return ret; } return (int)s1->atnumber - (int)s2->atnumber; } /*****************************************************************************/ int MarkSaltChargeGroups2 ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) { /* BNS_EDGE_FORBIDDEN_TEMP */ #define ALT_PATH_FOUND (MAX_ATOMS+1) #define NO_ENDPOINT (MAX_ATOMS+2) /* the two defines must be different */ #define DISABLE_CANDIDATE 10 #define cPAIR(a,b) cPair[a+b*nNumLeftCandidates] #define ACCEPTOR_PAIR 1 #define DONOR_PAIR 2 int nNumChanges = 0, nNumOtherChanges = 0, nNumAcidicChanges = 0, nTotNumChanges = 0; S_CHAR *cPair = NULL; T_ENDPOINT *EndPoint = NULL; if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { int i, j, i1, j1; S_CANDIDATE *s_candidate = s_group_info->s_candidate; int nMaxNumCandidates = s_group_info->max_num_candidates; int nNumCandidates = s_group_info->num_candidates; int nNumOtherCandidates = s_group_info->num_other_candidates; int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; int nNumLeftCandidates = 0; int nNumMarkedCandidates = 0; int s_type, s_subtype; int ret, nDelta; int bHardAddedRemovedProtons = t_group_info && (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT); int s_subtype_all = 0; int nDonorPairs, nAcceptorPairs, nCurDonorPairs, nCurAcceptorPairs, bAlreadyTested; /* ENDPOINT_INFO eif; */ #if ( IGNORE_TGROUP_WITHOUT_H == 1 ) int bTGroupHasNegativeChargesOnly = 1; #endif /*return 0;*/ /* debug only */ i1 = -1; if ( nNumCandidates <= -2 || !t_group_info || !t_group_info->t_group ) { return 0; } /*************************************************************************/ /* find all candidates including those with differen s_type (other type) */ /*************************************************************************/ for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) { if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || /* -C=O or =C-OH, O = S, Se, Te */ 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) || ( bHardAddedRemovedProtons && 4 == (s_type = bIsHardRemHCandidate( at, i, &s_subtype ) ) ) /* >C-SH, >C-S(-); S=S,Se,Te */ ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; nNumOtherCandidates += (1 == s_type); s_subtype_all |= s_subtype; i1 = i; /* save a representative of a tautomeric group */ } } if ( nNumCandidates <= 1 || /* TG_FLAG_ALLOW_NO_NEGTV_O <=> CHARGED_SALTS_ONLY=0 */ !(s_subtype_all & SALT_ACCEPTOR) || (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? !(s_subtype_all & (SALT_DONOR)): (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates == nNumCandidates )) ) { s_group_info->num_candidates = 0; /* no candidate exists */ return 0; } if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; } /************************************************************************************/ /* Mark redundant candidates so that only one candidate from one t-group is left in */ /************************************************************************************/ for ( i = 0; i < nNumCandidates; i ++ ) { if ( 2 == s_candidate[nNumCandidates].type ) { s_candidate[i].type -= DISABLE_CANDIDATE; /* disable >C-SH candidates */ nNumLeftCandidates ++; /* count rejected */ continue; } if ( s_candidate[i].endpoint ) { for ( j = i-1; 0 <= j; j -- ) { if ( s_candidate[i].endpoint == s_candidate[j].endpoint ) { s_candidate[i].type -= DISABLE_CANDIDATE; /* disable subsequent redundant */ nNumLeftCandidates ++; /* count rejected */ break; } } } } nNumLeftCandidates = nNumCandidates - nNumLeftCandidates; /* subtract num. rejected from the total */ s_group_info->num_candidates = 0; /* reinit next time */ /*********************************************************************/ /* reorder so that all disabled are at the end, tautomeric are first */ /*********************************************************************/ qsort ( s_candidate, nNumCandidates, sizeof(s_candidate[0]), comp_candidates ); cPair = (S_CHAR *)inchi_calloc( nNumLeftCandidates*nNumLeftCandidates, sizeof(cPair[0]) ); if ( !cPair ) { /*printf("BNS_OUT_OF_RAM-6\n");*/ nTotNumChanges = BNS_OUT_OF_RAM; goto quick_exit; } nDonorPairs = nAcceptorPairs = 0; /**********************************************************************/ /* Find whether we have at least one donor pair and one acceptor pair */ /**********************************************************************/ for ( i = 0; i < nNumLeftCandidates; i ++ ) { nCurDonorPairs = nCurAcceptorPairs = 0; for ( j = 0; j <= i; j ++ ) { if ( i == j && !s_candidate[i].endpoint ) { continue; /* same non-taut atom. However, success for i==j means * * that the whole tautomeric group may donate or accept 2H */ } /* check for acceptor pair */ if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_TST ))) { if ( IS_BNS_ERROR( ret ) ) { nTotNumChanges = ret; goto quick_exit; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; /*nNumChanges += (ret & 2);*/ if ( nDelta ) { /* alt path unleashed previously localized radicals and they annihilated */ nNumChanges = 0; nTotNumChanges = BNS_RADICAL_ERR; goto quick_exit; } cPAIR(i,j) |= ACCEPTOR_PAIR; /* the result: mark the pair */ /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ } } /* check for donor pair */ if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_TST ))) { if ( IS_BNS_ERROR( ret ) ) { nTotNumChanges = ret; goto quick_exit; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; /*nNumChanges += (ret & 2);*/ if ( nDelta ) { /* alt path unleashed previously localized radicals and they annihilated */ nNumChanges = 0; nTotNumChanges = BNS_RADICAL_ERR; goto quick_exit; } cPAIR(i,j) |= DONOR_PAIR; /* the result: mark the pair */ /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ } } /* since the results will be used later to change bonds, check only now */ /* when both results for (i,j) have been obtained. */ if ( cPAIR(i,j) & ACCEPTOR_PAIR ) { nCurAcceptorPairs ++; if ( nDonorPairs ) { /* find donor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ for ( i1 = 0; i1 < i; i1 ++ ) { for ( j1 = 0; j1 <= i1; j1 ++ ) { /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ if ( j1 != j && i1 != j && (cPAIR(i1,j1) & DONOR_PAIR) ) { /* both the donor and the acceptor pairs have been found */ goto bFound2Pairs; } } } } } if ( cPAIR(i,j) & DONOR_PAIR ) { nCurDonorPairs ++; if ( nAcceptorPairs ) { /* find acceptor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ for ( i1 = 0; i1 < i; i1 ++ ) { for ( j1 = 0; j1 <= i1; j1 ++ ) { /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ if ( j1 != j && i1 != j && (cPAIR(i1,j1) & ACCEPTOR_PAIR) ) { /* both the donor and the acceptor pairs have been found */ goto bFound2Pairs; } } } } } } nDonorPairs += nCurDonorPairs; nAcceptorPairs += nCurAcceptorPairs; } /* nothing has been found */ nNumChanges = 0; inchi_free( cPair ); cPair = NULL; goto quick_exit; /* both the donor and the acceptor pairs have been found */ bFound2Pairs: /* first, try already found pairs */ i1 = i; j1 = j; /* Find all possible donor and acceptor pairs */ nNumMarkedCandidates = 0; for ( i = 0; i < nNumLeftCandidates; i ++ ) { nCurDonorPairs = nCurAcceptorPairs = 0; for ( j = 0; j <= i; j ++ ) { bAlreadyTested = (i < i1 || i == i1 && j <= j1); if ( bAlreadyTested && (cPAIR(i,j) & ACCEPTOR_PAIR) || !bAlreadyTested ) { /* checking for acceptor pair */ if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_CHG ))) { if ( IS_BNS_ERROR( ret ) ) { nTotNumChanges = ret; goto quick_exit; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (ret & 2); if ( nDelta ) { /* alt path unleashed previously localized radicals and they annihilated */ nNumChanges = 0; nTotNumChanges = BNS_RADICAL_ERR; goto quick_exit; } cPAIR(i,j) |= ACCEPTOR_PAIR; /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ nCurAcceptorPairs += !bAlreadyTested; if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { s_candidate[i].subtype |= SALT_SELECTED; nNumMarkedCandidates ++; if ( !s_candidate[i].endpoint && s_candidate[i].type ) { nNumOtherChanges ++; } else { nNumAcidicChanges ++; } } if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { s_candidate[j].subtype |= SALT_SELECTED; nNumMarkedCandidates ++; if ( !s_candidate[j].endpoint && s_candidate[j].type ) { nNumOtherChanges ++; } else { nNumAcidicChanges ++; } } } } } if ( bAlreadyTested && (cPAIR(i,j) & DONOR_PAIR) || !bAlreadyTested ) { /* checking for donor pair */ if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_CHG ))) { if ( IS_BNS_ERROR( ret ) ) { nTotNumChanges = ret; goto quick_exit; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (ret & 2); if ( nDelta ) { /* alt path unleashed previously localized radicals and they annihilated */ nNumChanges = 0; nTotNumChanges = BNS_RADICAL_ERR; goto quick_exit; } cPAIR(i,j) |= DONOR_PAIR; /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ nCurDonorPairs += !bAlreadyTested; if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { s_candidate[i].subtype |= SALT_SELECTED; nNumMarkedCandidates ++; if ( !s_candidate[i].endpoint && s_candidate[i].type ) { nNumOtherChanges ++; } else { nNumAcidicChanges ++; } } if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { s_candidate[j].subtype |= SALT_SELECTED; nNumMarkedCandidates ++; if ( !s_candidate[j].endpoint && s_candidate[j].type ) { nNumOtherChanges ++; } else { nNumAcidicChanges ++; } } } } } } nDonorPairs += nCurDonorPairs; nAcceptorPairs += nCurAcceptorPairs; } inchi_free( cPair ); cPair = NULL; if ( nNumMarkedCandidates ) { EndPoint = (T_ENDPOINT *)inchi_calloc( nNumMarkedCandidates, sizeof(EndPoint[0])); if ( !EndPoint ) { /*printf("BNS_OUT_OF_RAM-7\n");*/ nTotNumChanges = BNS_OUT_OF_RAM; goto quick_exit; } for ( i = 0, j = 0; i < nNumLeftCandidates; i ++ ) { if ( s_candidate[i].subtype & SALT_SELECTED ) { s_candidate[i].subtype ^= SALT_SELECTED; /* remove the flag */ if ( j < nNumMarkedCandidates ) { i1 = s_candidate[i].atnumber; /* save a representative of the t-group to be created */ AddEndPoint( EndPoint+j, at, i1 ); } j ++; } } if ( j != nNumMarkedCandidates ) { nTotNumChanges = BNS_PROGRAM_ERR; goto quick_exit; } /* merge all marked atoms and their t-groups into one t-group */ ret = RegisterEndPoints( t_group_info, EndPoint, nNumMarkedCandidates, at, num_atoms, c_group_info, pBNS ); if ( ret == -1 ) { ret = BNS_PROGRAM_ERR; } if ( ret < 0 ) { nTotNumChanges = ret; goto quick_exit; } nTotNumChanges += (ret > 0); inchi_free( EndPoint ); EndPoint = NULL; if ( nNumMarkedCandidates ) { for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { s_candidate[i].type += DISABLE_CANDIDATE; j1 = s_candidate[i].atnumber; if ( at[j1].endpoint == at[i1].endpoint ) { if ( !s_candidate[i].endpoint && s_candidate[i].type ) { nNumOtherChanges ++; } else { nNumAcidicChanges ++; } } } } else { for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { s_candidate[i].type += DISABLE_CANDIDATE; } } /* find whether the new t-group have any movable H */ for ( i = 0, bTGroupHasNegativeChargesOnly = 0; i < t_group_info->num_t_groups; i ++ ) { if ( t_group_info->t_group[i].nGroupNumber == at[i1].endpoint && t_group_info->t_group[i].num[0] == t_group_info->t_group[i].num[1] ) { bTGroupHasNegativeChargesOnly = 1; break; } } } nTotNumChanges = ( nTotNumChanges > 0); #if ( IGNORE_TGROUP_WITHOUT_H == 1 ) if ( nTotNumChanges && bTGroupHasNegativeChargesOnly ) { nTotNumChanges = 2; /* means no moveable H has been affected */ } #endif } quick_exit: if ( nNumOtherChanges && nTotNumChanges == 1 ) { nTotNumChanges = 5; /* not only acidic atoms merged */ } if ( cPair ) { inchi_free( cPair ); /*cPair = NULL;*/ } if ( EndPoint ) { inchi_free ( EndPoint ); /*EndPoint = NULL;*/ } return nTotNumChanges; /* 0=>no changes, 1=>new salt tautomerism found, 2=>only new charge tautomerism found */ #undef ALT_PATH_FOUND #undef NO_ENDPOINT } /********************************************************************************************************/ /* regular one-path version: find alt paths then merge */ /* Check for oxygen negative charge-H tautomerism (Salts) allowed long-range tautomerism; only one H or (-) can be moved, for example: HO-C=X-Y=Z-...-C=O => O=C-X=Y-Z=...=C-OH */ #if ( SALT_WITH_PROTONS == 1 ) #define MAX_LOCAL_TGNUM 0 /* was 32; disable since it has not been used */ #if ( MAX_LOCAL_TGNUM > 0 ) typedef struct tagTGroupData { S_SHORT nGroupNumber; /* t-group number from t_group_info->t_group->nGroupNumber */ S_SHORT nGroupIndex; /* TGroupData[nGroupNumber]nGroupIndex = index of t_group in t_group_info */ S_SHORT nDonorM; /* number of endpoint-donors that have negative charge (Minus) */ S_SHORT nDonorH; /* number of endpoint-donors that have only H */ S_SHORT nAccepM; /* number of endpoint-acceptors that have negative charge (Minus) */ S_SHORT nAccepH; /* number of endpoint-acceptors that have H and no negative charge */ S_SHORT nAccep0; /* number of endpoint-acceptors that have no H and no negative charge */ S_SHORT nDonorA; /* number of acidic endpoint-donors */ S_SHORT nAccepS; /* number of acidic endpoint-acceptors */ } TGroupData; #endif /*****************************************************************************/ int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) { int nNumChanges = 0, nTotNumChanges = 0; if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, /*k,*/ num_tested; S_CANDIDATE *s_candidate = s_group_info->s_candidate; int nMaxNumCandidates = s_group_info->max_num_candidates; int nNumCandidates = s_group_info->num_candidates; int nNumOtherCandidates = s_group_info->num_other_candidates; int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; int s_type, s_subtype; int ret, nDelta, /*nMobile,*/ err = 0; int s_subtype_all = 0; int nGroupNumber; T_ENDPOINT EndPoint[2]; #if ( MAX_LOCAL_TGNUM > 0 ) TGroupData tgData[MAX_LOCAL_TGNUM]; TGroupData *ptgData = tgData; #endif int cond1=0,cond2a=0,cond2b=0,cond2c=0,cond2=0; if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { return 0; } /* count t-groups */ for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { if ( nGroupNumber < t_group_info->t_group[i].nGroupNumber ) { nGroupNumber = t_group_info->t_group[i].nGroupNumber; /* max. t-group number */ } } #if ( MAX_LOCAL_TGNUM > 0 ) /* prepare memory */ if ( nGroupNumber >= MAX_LOCAL_TGNUM ) { if ( !( ptgData = (TGroupData*)inchi_calloc( nGroupNumber+1, sizeof(TGroupData) ) ) ) { err = BNS_OUT_OF_RAM; goto quick_exit; } } else { memset( ptgData, 0, sizeof(tgData) ); } ptgData[0].nGroupIndex = -1; /* data for non-tautomeric atoms */ for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { if ( nGroupNumber = t_group_info->t_group[i].nGroupNumber ) { ptgData[nGroupNumber].nGroupIndex = i; ptgData[i].nGroupNumber = nGroupNumber; } } #endif nNumCandidates = 0; /* always recalculate 2004-03-22 */ num_tested = 0; if ( nNumCandidates == 0 ) { for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) { if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || /* -C=O or =C-OH, O = S, Se, Te */ #if ( INCL_NON_SALT_CANDIDATATES == 1 ) 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 )) || /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ #endif 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) /* >C-SH, >C-S(-); S=S,Se,Te */ ) { if ( nNumCandidates >= nMaxNumCandidates ) { err = BNS_VERT_EDGE_OVFL; goto quick_exit; } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; nNumOtherCandidates += (1 == s_type); nNumPOnlyCandidates += (2 == s_type); s_subtype_all |= s_subtype; /*i1 = i;*/ /* save a representative of a tautomeric group */ } } /* for */ /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ #if 0 if ( nNumCandidates <= 1 || !(s_subtype_all & SALT_ACCEPTOR) || (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O)|| (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates)) ) { #endif cond1 = s_subtype_all & SALT_ACCEPTOR; cond2a = t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O; cond2b = t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE; cond2c = t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT; if ( cond2a || cond2b|| cond2c ) cond2 = !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)); else cond2 = !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates; if ( nNumCandidates <= 1 || !cond1 || cond2 /*( ( cond2a || cond2b || cond2c ) ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)) : ( !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates) ) */ ) { s_group_info->num_candidates = -1; /* no candidate exists */ goto quick_exit; } if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; } } else { for ( i = 0; i < nNumCandidates; i ++ ) { i1 = s_candidate[i].atnumber; if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) #if ( INCL_NON_SALT_CANDIDATATES == 1 ) || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) #endif ) { s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i1].endpoint; } } } /* Look for alt paths connecting: SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms */ do { nNumChanges = 0; for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { j1 = s_candidate[i1].atnumber; for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { /* prev. approach: do not test if both candidates are not "salt-type". Disabled 2004-03-18 if ( s_candidate[i1].type && s_candidate[i2].type ) continue; */ j2 = s_candidate[i2].atnumber; if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { continue; } for ( j = 0; j < 2; j ++ ) { if ( j ) { ii1 = i2; /* candidate 1 (donor) ordering number */ ii2 = i1; /* candidate 2 (acceptor) ordering number */ jj1 = j2; /* candidate 1 (donor) atom number */ jj2 = j1; /* candidate 2 (acceptor) atom number */ } else { /* transposition */ ii1 = i1; /* candidate 1 (donor) ordering number */ ii2 = i2; /* candidate 2 (acceptor) ordering number */ jj1 = j1; /* candidate 1 (donor) atom number */ jj2 = j2; /* candidate 2 (acceptor) atom number */ } if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) { ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); num_tested ++; if ( IS_BNS_ERROR( ret ) ) { err = ret; goto quick_exit; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (ret & 2); for ( i = 0; i < 2; i ++ ) { jj = i? jj2 : jj1; AddEndPoint( EndPoint+i, at, jj ); } /* add/merge taut groups and reinit pBNS in the fly */ ret = RegisterEndPoints( t_group_info, EndPoint, 2, at, num_atoms, c_group_info, pBNS ); if ( ret == -1 ) { ret = BNS_PROGRAM_ERR; } if ( ret < 0 ) { err = ret; goto quick_exit; } if ( nDelta ) { err = BNS_RADICAL_ERR; goto quick_exit; } nNumChanges += (ret > 0); break; /* avoid redundant repetition */ } } } } } nTotNumChanges += nNumChanges; } while ( num_tested && nNumChanges ); quick_exit: if ( !err ) { nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ if ( s_group_info->num_candidates == 0 ) { /* first time: initialize */ s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ } } else { nTotNumChanges = err; } #if ( MAX_LOCAL_TGNUM > 0 ) if ( ptgData != tgData ) { inchi_free( ptgData ); } #endif } return nTotNumChanges; } #else /*****************************************************************************/ int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) { int nNumChanges = 0, nTotNumChanges = 0; if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, k, num_tested; S_CANDIDATE *s_candidate = s_group_info->s_candidate; int nMaxNumCandidates = s_group_info->max_num_candidates; int nNumCandidates = s_group_info->num_candidates; int nNumOtherCandidates = s_group_info->num_other_candidates; int s_type, s_subtype; int ret, nDelta, nMobile; int s_subtype_all = 0; T_ENDPOINT EndPoint[2]; if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { return 0; } else if ( nNumCandidates == 0 ) { for ( i = 0, nNumCandidates = nNumOtherCandidates = 0; i < num_atoms; i ++ ) { if ( 0 <= (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; s_subtype_all |= s_subtype; /*i1 = i;*/ /* save a representative of a tautomeric group */ } #if ( INCL_NON_SALT_CANDIDATATES == 1 ) else /* new */ if ( 0 < (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; nNumOtherCandidates ++; s_subtype_all |= s_subtype; } #endif } /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ if ( nNumCandidates <= 1 || nNumOtherCandidates == nNumCandidates || ((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): !(s_subtype_all & SALT_DONOR_Neg)) || !(s_subtype_all & SALT_ACCEPTOR)) { s_group_info->num_candidates = -1; /* no candidate exists */ return 0; } if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; } } else { for ( i = 0; i < nNumCandidates; i ++ ) { i1 = s_candidate[i].atnumber; if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) #if ( INCL_NON_SALT_CANDIDATATES == 1 ) || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) #endif ) { s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i1].endpoint; } } } /* Look for alt paths connecting: SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms */ num_tested = 0; do { nNumChanges = 0; for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { j1 = s_candidate[i1].atnumber; for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { if ( s_candidate[i1].type && s_candidate[i2].type ) continue; /* both candidates are not "salt-type" */ j2 = s_candidate[i2].atnumber; if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { continue; } for ( j = 0; j < 2; j ++ ) { if ( j ) { ii1 = i2; /* candidate 1 (donor) ordering number */ ii2 = i1; /* candidate 2 (acceptor) ordering number */ jj1 = j2; /* candidate 1 (donor) atom number */ jj2 = j1; /* candidate 2 (acceptor) atom number */ } else { /* transposition */ ii1 = i1; /* candidate 1 (donor) ordering number */ ii2 = i2; /* candidate 2 (acceptor) ordering number */ jj1 = j1; /* candidate 1 (donor) atom number */ jj2 = j2; /* candidate 2 (acceptor) atom number */ } if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) { ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); num_tested ++; if ( IS_BNS_ERROR( ret ) ) { return ret; } if ( ret & 1 ) { nDelta = (ret & ~3) >> 2; nNumChanges += (ret & 2); for ( i = 0; i < 2; i ++ ) { jj = i? jj2 : jj1; EndPoint[i].nAtomNumber = jj; EndPoint[i].nEquNumber = 0; EndPoint[i].nGroupNumber = at[jj].endpoint; if ( at[jj].endpoint ) { memset( EndPoint[i].num, 0, sizeof(EndPoint[i].num) ); } else { AddAtom2num( EndPoint[i].num, at, jj, 2 ); /* fill out */ AddAtom2DA( EndPoint[i].num_DA, at, jj, 2 ); /* nMobile = EndPoint[i].num[1] = (at[jj].charge == -1); nMobile = EndPoint[i].num[0] = at[jj].num_H + nMobile; for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { EndPoint[i].num[T_NUM_NO_ISOTOPIC+k] = at[jj].num_iso_H[NUM_H_ISOTOPES-k-1]; } */ } } /* add/merge taut groups and reinit pBNS */ ret = RegisterEndPoints( t_group_info, EndPoint, 2, at, num_atoms, c_group_info, pBNS ); if ( ret < 0 ) { return ret; } nNumChanges += (ret > 0); if ( nDelta ) { goto quick_exit; } break; /* avoid redundant repetition */ } } } } } nTotNumChanges += nNumChanges; } while ( num_tested && nNumChanges ); quick_exit: nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ if ( s_group_info->num_candidates == 0 ) { /* first time: initialize */ s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ } } return nTotNumChanges; } #endif /*****************************************************************************/ int MergeSaltTautGroups( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, struct BalancedNetworkStructure *pBNS ) { /* count candidates to be connected: exclude pure donors that do not belong to any t-group */ AT_NUMB nCurTGroupNumber; int i, j, /*k,*/ ret, iat, /*nMobile,*/ nMinNumEndpoints; int s_subtype_all, s_subtype_taut; int nMaxNumCandidates, nNumCandidates, nNumCandidates2; T_ENDPOINT EndPointStackArray[MAX_STACK_ARRAY_LEN]; /* will be reallocated if too short */ T_ENDPOINT *EndPoint = EndPointStackArray; if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ !t_group_info || !t_group_info->t_group || !c_group_info ) { return 0; } nMinNumEndpoints = 0; nMaxNumCandidates = s_group_info->max_num_candidates; nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ s_subtype_all = s_subtype_taut = 0; /* collect tautomeric acidic O and previously non-tautomeric C-OH, C-SH, C-O(-), C-S(-) */ /* find whether previously found tautomeric atoms have both mobile H and (-) */ if ( 1 || (s_group_info->num_candidates < 0) ) { /* can be only -O(-) and -OH */ int s_type, s_subtype; S_CANDIDATE *s_candidate = s_group_info->s_candidate; for ( i = 0, nNumCandidates = nNumCandidates2 = 0; i < num_atoms; i ++ ) { s_subtype = 0; if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || /* -C=O or =C-OH, O = S, Se, Te */ /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || /* >C-SH, >C-S(-); S=S,Se,Te */ /* other proton donor or acceptor */ bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } if ( at[i].endpoint ) { s_subtype_taut |= s_subtype; } else if ( bDoNotMergeNonTautAtom(at, i) ) { continue; /* ignore non-tautomeric N */ } if ( !( s_subtype & SALT_DONOR_ALL ) || (s_subtype & SALT_ACCEPTOR) && !at[i].endpoint ) { continue; /* do not include non-taut acceptors like -C=O */ } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; s_subtype_all |= s_subtype; } } /* Forced merging occurs upon: =========================== (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) or (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) Allow forced merging in cases: {t-groups} (H, (-)} {H, (-), t-groups} Normal salt merging in cases: (H, (-)} {H, (-), t-groups}, Cannot merge H into t-groups if no (-) is present */ if ( (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) ) { /* force merge even though no negative charges are present */ if ( nNumCandidates <= 1 || (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) && !t_group_info->num_t_groups ) { s_group_info->num_candidates = -1; /* no candidate exists */ return 0; } } else { /* normal salt mode: merge if both -XH and -X(-) are present */ if ( nNumCandidates <= 1 || (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) ) { s_group_info->num_candidates = -1; /* no candidate exists */ return 0; } } /* -- old code -- if ( nNumCandidates <= 1 || (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? !(s_subtype_all & SALT_DONOR_ALL): !(s_subtype_all & SALT_DONOR_Neg2) ) ) { s_group_info->num_candidates = -1; return 0; } */ if ( !(s_subtype_all & (SALT_DONOR_Neg2) ) ) { t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; } s_group_info->num_candidates = nNumCandidates; } for ( i = 0; i < s_group_info->num_candidates; i ++ ) { iat = s_group_info->s_candidate[i].atnumber; if ( (s_group_info->s_candidate[i].subtype & SALT_ACCEPTOR) && !at[iat].endpoint ) { continue; /* should not happen */ } s_subtype_all |= s_group_info->s_candidate[i].subtype; if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { nMinNumEndpoints ++; } nCurTGroupNumber = (int)at[iat].endpoint; } if ( nMinNumEndpoints <= 1 ) { return 0; /* too few endpoints */ } /* make sure we have enough memory */ if ( nMinNumEndpoints > MAX_STACK_ARRAY_LEN ) { if ( !(EndPoint = (T_ENDPOINT *)inchi_calloc( nMinNumEndpoints, sizeof(EndPoint[0]) ) ) ) { /*printf("BNS_OUT_OF_RAM-8\n");*/ return BNS_OUT_OF_RAM; } } nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ for ( i = j = 0; i < s_group_info->num_candidates; i ++ ) { iat = s_group_info->s_candidate[i].atnumber; if ( s_group_info->s_candidate[i].subtype == SALT_ACCEPTOR && !at[iat].endpoint ) { continue; } if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { AddEndPoint( EndPoint+j, at, iat ); j ++; } nCurTGroupNumber = (int)at[iat].endpoint; } ret = RegisterEndPoints( t_group_info, EndPoint, j, at, num_atoms, c_group_info, pBNS ); if ( ret == -1 ) { ret = BNS_PROGRAM_ERR; } if ( EndPoint != EndPointStackArray ) { inchi_free( EndPoint ); } return ret; } /*****************************************************************************/ int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, T_GROUP_INFO *t_group_info) { /* all tautomeric atoms and all possible H+ donors and acceptors that have H */ int i, j, k, n, bHasH, tg, nError=0; int s_subtype_all, s_subtype_taut; int nMaxNumCandidates, nNumCandidates, nNumNonTautCandidates; if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ !t_group_info || !t_group_info->t_group ) { return 0; } nMaxNumCandidates = s_group_info->max_num_candidates; s_subtype_all = s_subtype_taut = 0; memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); if ( 1 || (s_group_info->num_candidates < 0) ) { int s_type, s_subtype; S_CANDIDATE *s_candidate = s_group_info->s_candidate; for ( i = 0, nNumCandidates = nNumNonTautCandidates = 0; i < num_atoms; i ++ ) { s_subtype = 0; s_type = 0; if ( at[i].endpoint ) { if ( (tg = t_group_info->tGroupNumber[at[i].endpoint]) && at[i].endpoint == t_group_info->t_group[tg-=1].nGroupNumber ) { bHasH = (int)t_group_info->t_group[tg].num[0] - (int)t_group_info->t_group[tg].num[1]; } else { nError = BNS_PROGRAM_ERR; break; } } else { bHasH = (int)at[i].num_H; } if ( bHasH && at[i].endpoint || /* tautomeric atoms */ /* non-tautomeric heteroatoms that (a) have H and (b) may be donors of H therefore may exchange isotopic-non-isotopic H */ bHasH && (0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || /* -C=O or =C-OH, O = S, Se, Te */ /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || /* >C-SH, >C-S(-); S=S,Se,Te */ /* other proton donor or acceptor */ bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) || bHasOtherExchangableH (at, i) && ((s_type=3), (s_subtype = SALT_DONOR_H)) ) ) { if ( nNumCandidates >= nMaxNumCandidates ) { return BNS_VERT_EDGE_OVFL; } s_candidate[nNumCandidates].atnumber = i; s_candidate[nNumCandidates].type = s_type; s_candidate[nNumCandidates].subtype = s_subtype; s_candidate[nNumCandidates].endpoint = at[i].endpoint; nNumCandidates ++; nNumNonTautCandidates += !at[i].endpoint; s_subtype_all |= s_subtype; } } if ( nError ) { return nError; } if ( nNumCandidates > 0 ) { t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB *)inchi_calloc( nNumNonTautCandidates+1, sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); t_group_info->nIsotopicEndpointAtomNumber[0] = nNumNonTautCandidates; for ( i = 0, n = 1; i < nNumCandidates; i ++ ) { k = s_candidate[i].atnumber; if ( !at[k].endpoint ) { t_group_info->nIsotopicEndpointAtomNumber[n++] = k; } for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { t_group_info->num_iso_H[j] += at[k].num_iso_H[j]; } at[k].cFlags |= AT_FLAG_ISO_H_POINT; } t_group_info->nNumIsotopicEndpoints = nNumNonTautCandidates+1; } } return nNumCandidates; } /*#else*/ /* } DISCONNECT_SALTS == 0 */ /********************************************************************************** Charges and tautomeric endpoints (N only) ********************************************************************************** H = number of possibly moveable hydrogen atoms C = possibly moveable positive charge - = single bond = = double bond # = triple bond +-----------------------------------------------------------------------------+ |ca-| H | edges to t- | 1 bond | 2 bonds | 3 bonds *) | |se | C | and c-groups | (valence) | (valence) | (valence) | | # | | (edges flow) | | | | +---|------+---------------+----------------+----------------+----------------| | 1 | H=0 | -- (1) | =NH (3) | =N- (3) | >N- (3) | | | C=0 | == | | | | +---|------+---------------+----------------+----------------+----------------| | 2 | H=1 | == (2) | -NH2 (3) | -NH- (3) | none | | | C=0 | == | | | | +---|------+---------------+----------------+----------------+----------------| | 3 | H=0 | -- (0) | #NH(+) (4) | =N(+)= (4) +)| >N(+)= (4) | | | C=1 | -- | (prohibited | | | | | | | by edge cap) | | | +---|------+---------------+----------------+----------------+----------------| | 4 | H=1 | == (1) | =NH2(+) (4) +)| =NH(+)- (4) +)| >NH(+)- (4) | | | C=1 | -- | | | | +---+-------------------------------------------------------------------------+ *) Cannot be a tautomeric endpoint +) The three charged types of atoms [=N(+)=, =NH(+)-, =NH2(+)] should be checked for possible H-tautomerism. Other types in the marked by *) column should not be checked as long as H(+) exchange is not considered tautomeric. Other possibilities: -NH3(+) >NH2(+) >N(+)< cannot be H-tautomeric endpoints. Case #1 (H=0, C=0) and #4 (H=1,C=0) is indistinguishable from the viewpoint of edges flow and capacities except for flow from N to (+) vertex. Without taking precautions H(+) can be transferred from =NH2(+) to =NH, from =NH(+)- to =N-, from >NH(+)- to >N- or to any other appropriate atom that has a lone electron pair and bonds will not change. In this case no bond must be marked as tautomeric. For this reason before attempting to transfer H from one endpoint to another the charges on the two atoms should be set to zero by forcing zero flow from each of atoms to the (+)-vertices if the atoms belong to a c-group. **********************************************************************************/ /***********************************************************************************/ /* MarkTautomerGroups: do not identify positively charged N as endpoints for now */ /***********************************************************************************/ int MarkTautomerGroups( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info , struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) { int i, j, k, m, endpoint_valence, centerpoint, endpoint, bond_type, nMobile, num_changes=0, tot_changes=0; T_ENDPOINT EndPoint[MAXVAL]; T_BONDPOS BondPos[MAXVAL]; AT_NUMB nGroupNumber; int bDiffGroups; int nNumEndPoints, nNumBondPos, nNumPossibleMobile; int bTautBond, bNonTautBond, bAltBond; int nNumDonor, nNumAcceptor, bPossiblyEndpoint; T_GROUP *t_group; int *pnum_t, max_num_t, bIgnoreIsotopic; ENDPOINT_INFO eif1, eif2; int nErr = 0; #define ALLOWED_EDGE(PBNS, IAT,IBOND) ( !(PBNS) || !(PBNS)->edge || !(PBNS)->vert || !(PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].forbidden) #define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) if ( !t_group_info || !(t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__ATOMS) ) return 0; /* initial t_group allocation */ if ( !t_group_info->t_group && !t_group_info->max_num_t_groups ) { INCHI_MODE bTautFlags = t_group_info->bTautFlags; /* save initial setting */ INCHI_MODE bTautFlagsDone = t_group_info->bTautFlagsDone; /* save previous findings, if any */ TNI tni = t_group_info->tni; AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; memset( t_group_info, 0, sizeof(*t_group_info) ); t_group_info->bIgnoreIsotopic = bIgnoreIsotopic; /* restore initial setting */ t_group_info->bTautFlags = bTautFlags; t_group_info->bTautFlagsDone = bTautFlagsDone; t_group_info->tni = tni; t_group_info->tGroupNumber = tGroupNumber; t_group_info->max_num_t_groups = num_atoms/2+1; /* upper limit */ if (!(t_group_info->t_group = (T_GROUP*)inchi_calloc(t_group_info->max_num_t_groups, sizeof(t_group[0])))) { return (t_group_info->max_num_t_groups = -1); /* failed, out of RAM */ } } /* check if t_group_info exists */ if ( !t_group_info->t_group || !t_group_info->max_num_t_groups ) return 0; if ( 0 > t_group_info->max_num_t_groups ) return t_group_info->max_num_t_groups; pnum_t = &t_group_info->num_t_groups; /* number of found tautomer endpoint groups */ t_group = t_group_info->t_group; max_num_t = t_group_info->max_num_t_groups; bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; /* 1-3 tautomers */ for ( i = 0; i < num_atoms; i ++ ) { /* find possible endpoint Z = at[i] */ if ( endpoint_valence = nGetEndpointInfo( at, i, &eif1 ) ) { /* 1st endpoint candidate found. Find centerpoint candidate */ for ( j = 0; j < at[i].valence; j ++ ) { bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); #endif centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ if ( (bond_type == BOND_DOUBLE || bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM) && is_centerpoint_elem( at[centerpoint].el_number ) && ALLOWED_EDGE(pBNS, i, j) ) { /* test a centerpoint candidate. */ /* find all endpoints including at[i] and store them into EndPoint[] */ nNumPossibleMobile = 0; nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ bDiffGroups = -1; /* ignore the first difference */ nNumDonor = nNumAcceptor = 0; for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); #endif bTautBond = bNonTautBond = bAltBond = bPossiblyEndpoint = 0; if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { continue; } else if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { bTautBond = 1; #if ( REPLACE_ALT_WITH_TAUT == 1 ) bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); #endif } else if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) bNonTautBond = 1; else continue; if ( !(endpoint_valence = nGetEndpointInfo( at, endpoint, &eif1 )) ) continue; /* not an endpoint element or can't have mobile groups */ /* save information about the found possible tautomeric endpoint */ /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ nMobile = AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); /* --- why is isitopic info missing ? -- see below nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; */ if ( bNonTautBond ) { m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); nNumDonor += m; bPossiblyEndpoint += m; m = (bond_type == BOND_DOUBLE ); nNumAcceptor += m; bPossiblyEndpoint += m; } else { /* tautomeric or alternating bond */ m = (0 != at[endpoint].endpoint || eif1.cDonor ); nNumDonor += m; bPossiblyEndpoint += m; m = ( at[endpoint].endpoint || eif1.cNeutralBondsValence > at[endpoint].valence ); nNumAcceptor += m; bPossiblyEndpoint += m; } if ( !bPossiblyEndpoint ) continue; EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ EndPoint[nNumEndPoints].nEquNumber = 0; EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; if ( nGroupNumber != at[endpoint].endpoint ) { bDiffGroups ++; nGroupNumber = at[endpoint].endpoint; } /* save positions of all, not only possibly tautomeric bonds */ #if ( REPLACE_ALT_WITH_TAUT != 1 ) if ( bNonTautBond || bAltBond ) { #endif BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ nNumBondPos ++; #if ( REPLACE_ALT_WITH_TAUT != 1 ) } #endif /* mobile group is possible if (a) the endpoint has a mobile group or */ /* (b) the centerpoint is adjacent to another endpoint */ nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); nNumEndPoints ++; } if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor ) { /* * a tautomeric group has been found * * at this point: * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen */ nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM ); if ( IS_BNS_ERROR(nErr) ) { return nErr; } nErr = 0; if ( nNumEndPoints > 0 ) { if ( !nGroupNumber || bDiffGroups > 0 ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto exit_function; tot_changes += (num_changes>0); } if ( nNumBondPos > 0 ) { /* some of the bonds have not been marked as tautomeric yet */ num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); tot_changes += (num_changes>0); } } } } } } } #if ( KETO_ENOL_TAUT == 1 ) /***** post v.1 feature *****/ if ( t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) { /* 1,3 keto-enol tautomerism */ for ( i = 0; i < num_atoms; i ++ ) { /* find possible endpoint Z = at[i] */ if ( endpoint_valence = nGetEndpointInfo_KET( at, i, &eif1 ) ) { /* 1st endpoint candidate found. Find centerpoint candidate */ for ( j = 0; j < at[i].valence; j ++ ) { bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); #endif centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ if ( (bond_type == BOND_DOUBLE || bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM) && is_centerpoint_elem_KET( at[centerpoint].el_number ) && !at[centerpoint].charge && !at[centerpoint].radical && /* only normal carbon is allowed */ 4 == at[centerpoint].chem_bonds_valence + at[centerpoint].num_H && ALLOWED_EDGE(pBNS, i, j) ) { int num_O = 0; int num_C = 0; /* test a centerpoint candidate. */ /* find all endpoints including at[i] and store them into EndPoint[] */ nNumPossibleMobile = 0; nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ bDiffGroups = -1; /* ignore the first difference */ nNumDonor = nNumAcceptor = 0; for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; #if ( FIX_BOND23_IN_TAUT == 1 ) bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); #endif bTautBond = bNonTautBond = bAltBond = bPossiblyEndpoint = 0; if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { continue; } else if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { bTautBond = 1; #if ( REPLACE_ALT_WITH_TAUT == 1 ) bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); #endif } else if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) bNonTautBond = 1; else continue; if ( !(endpoint_valence = nGetEndpointInfo_KET( at, endpoint, &eif2 )) ) { continue; } /* if ( 3 != eif1.cKetoEnolCode + eif2.cKetoEnolCode && endpoint != i ) continue; */ /* save information about the found possible tautomeric endpoint */ /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ nMobile = AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); /* --- why is isitopic info missing ? -- see below nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; */ if ( bNonTautBond ) { m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); nNumDonor += m; bPossiblyEndpoint += m; m = (bond_type == BOND_DOUBLE ); nNumAcceptor += m; bPossiblyEndpoint += m; } else { /* tautomeric or alternating bond */ m = (0 != at[endpoint].endpoint || eif1.cDonor ); nNumDonor += m; bPossiblyEndpoint += m; m = ( at[endpoint].endpoint || eif1.cNeutralBondsValence > at[endpoint].valence ); nNumAcceptor += m; bPossiblyEndpoint += m; } if ( !bPossiblyEndpoint ) continue; num_O += (endpoint_valence == 2); num_C += (endpoint_valence == 4); EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ EndPoint[nNumEndPoints].nEquNumber = 0; EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; if ( nGroupNumber != at[endpoint].endpoint ) { bDiffGroups ++; nGroupNumber = at[endpoint].endpoint; } /* save positions of all, not only possibly tautomeric bonds */ #if ( REPLACE_ALT_WITH_TAUT != 1 ) if ( bNonTautBond || bAltBond ) { #endif BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ nNumBondPos ++; #if ( REPLACE_ALT_WITH_TAUT != 1 ) } #endif /* mobile group is possible if (a) the endpoint has a mobile group or */ /* (b) the centerpoint is adjacent to another endpoint */ nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); nNumEndPoints ++; } if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor && num_O==1 && num_C ) { /* * a tautomeric group has been found * * at this point: * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen */ nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM_KET ); if ( IS_BNS_ERROR(nErr) ) { return nErr; } nErr = 0; if ( nNumEndPoints > 0 ) { if ( !nGroupNumber || bDiffGroups > 0 ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto exit_function; tot_changes += (num_changes>0); } if ( nNumBondPos > 0 ) { /* some of the bonds have not been marked as tautomeric yet */ num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); tot_changes += (num_changes>0); } } } } } } } } #endif /* KETO_ENOL_TAUT */ #if ( TAUT_OTHER == 1 ) /* { */ if ( !tot_changes ) { #define MAX_ALT_PATH_LEN 8 int nMaxLenDfsPath = MAX_ALT_PATH_LEN; int i1, i2; AT_RANK *nDfsPathPos = (AT_RANK *)inchi_calloc( num_atoms, sizeof(nDfsPathPos[0]) ); DFS_PATH DfsPath[MAX_ALT_PATH_LEN]; int ret; if ( !nDfsPathPos || !DfsPath ) { tot_changes = CT_OUT_OF_RAM; /* */ goto free_memory; } #if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ if ( t_group_info->bTautFlags & TG_FLAG_1_5_TAUT ) { /* 1,5 tautomerism; one of the endpoints should no be on a ring */ /* O OH O || | || A--pos- A--pos- A--pos- / sib- // sib- ? / sib- C ly C ly CH ly \\ a <--> \ a <--> \ a B--ring B--ring B--ring | || || NH N N Note: few recent modifications now allow the terminal N be in a ring, too */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { /* find possible endpoint Z = at[i1] */ if ( !(endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) /*|| at[i1].nNumAtInRingSystem > 1*/ ) { continue; /* not a possibly endpoint */ } if ( 1 ) { nNumEndPoints = 0; nNumBondPos = 0; ret = nGet15TautInAltPath( at, i1, nDfsPathPos, DfsPath, nMaxLenDfsPath, EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), BondPos, sizeof(BondPos)/sizeof(BondPos[0]), &nNumEndPoints, &nNumBondPos, pBNS, pBD, num_atoms); if ( ret > 0 ) { if ( nNumEndPoints ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto free_memory; tot_changes += (num_changes > 0); } if ( nNumBondPos ) { tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); } } else if ( IS_BNS_ERROR( ret ) ) { nErr = ret; goto free_memory; } } } } #endif #if ( TAUT_4PYRIDINOL_RINGS == 1 ) /* 6-member rings */ /* O OH OH || | | / \ // \ / \\ || || <--> | || <--> || | \ / \\ / \ // NH N N */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { /* find possible endpoint Z = at[i1] */ if ( 3 != (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) || 2 != at[i1].valence ) { continue; /* not a nitrogen atom or a wrong valence */ } if ( at[i1].nNumAtInRingSystem >= 6 ) { nNumEndPoints = 0; nNumBondPos = 0; ret = nGet15TautIn6MembAltRing( at, i1, nDfsPathPos, DfsPath, nMaxLenDfsPath, EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), BondPos, sizeof(BondPos)/sizeof(BondPos[0]), &nNumEndPoints, &nNumBondPos, pBNS, pBD, num_atoms); if ( ret > 0 ) { if ( nNumEndPoints ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto free_memory; tot_changes += (num_changes > 0); } if ( nNumBondPos ) { tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); } } else if ( IS_BNS_ERROR( ret ) ) { nErr = ret; goto free_memory; } } } #endif /* TAUT_4PYRIDINOL_RINGS */ #if ( TAUT_PYRAZOLE_RINGS == 1 ) /* 5-member rings: Z Z / \\ // \ X Y <--> X Y \\ / \ // N--NH HN--N ^ ^ search for these NH */ /* 5-member rings (pyrazole derivatives): look for the neighboring N */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( 2 == at[i1].valence && at[i1].nNumAtInRingSystem >= 5 && 3 == (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 )) ) { nMobile = at[i1].num_H + (at[i1].charge == -1); for ( j = 0; j < at[i1].valence; j ++ ) { int nMobile2, endpoint_valence2; i2 = at[i1].neighbor[j]; /* may be important */ if ( i2 >= i1 ) continue; /* do not try same pair 2 times */ if ( at[i2].nRingSystem != at[i1].nRingSystem ) continue; bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM && bond_type != BOND_ALT12NS && bond_type != BOND_ALTERN || /* added 1-15-2002 */ 2 != at[i2].valence || 3 != (endpoint_valence2 = nGetEndpointInfo( at, i2, &eif2 ) ) ) { continue; /* not a nitrogen atom or a wrong valence or not a single bond */ } nMobile2 = at[i2].num_H + (at[i2].charge == -1); /* number of mobile groups */ #if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) if ( at[i1].endpoint && at[i1].endpoint == at[i2].endpoint ) continue; /* atoms already belong to the same t-group */ #endif if ( !at[i1].endpoint && !at[i2].endpoint && 1!=nMobile + nMobile2 ) continue; ret = nGet12TautIn5MembAltRing( at, i1, j, nDfsPathPos, DfsPath, nMaxLenDfsPath, EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), BondPos, sizeof(BondPos)/sizeof(BondPos[0]), &nNumEndPoints, &nNumBondPos , pBNS, pBD, num_atoms); if ( ret > 0 ) { if ( nNumEndPoints ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto free_memory; tot_changes += (num_changes > 0); } if ( nNumBondPos ) { tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); } } else if ( IS_BNS_ERROR( ret ) ) { nErr = ret; goto free_memory; } } } } #endif /* TAUT_PYRAZOLE_RINGS */ #if ( TAUT_TROPOLONE_7 == 1 || TAUT_TROPOLONE_5 == 1 ) /* { */ /******************************************************** * A B * | || * 7-member rings (tropolones): look for M=Q--R--ZH, * ^ ^ ^ ^ * endpoint1 i1 i2 endpoint2 * where A-Q-R=B belong to a 7-member alt. (except Q-R bond) ring: ..=A-(Q-R)=B-.. * Bond Q-R should be single or tautomeric or alternating * M=Q and R-ZH should be chain (non-ring) bonds * Same for 5-member rings */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( at[i1].nNumAtInRingSystem >= #if ( TAUT_TROPOLONE_5 == 1 ) 5 #else 7 #endif && bIsCenterPointStrict( at, i1 ) && #if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) at[i1].bCutVertex && #endif at[i1].valence == 3 && !at[i1].endpoint ) { int nMobile1, endpoint1, endpoint1_valence, bond_type1; int nMobile2, endpoint2, endpoint2_valence, bond_type2; for ( j = 0; j < at[i1].valence; j ++ ) { i2 = at[i1].neighbor[j]; /* // may be important if ( i2 > i1 ) continue; // do not try same pair 2 times */ if ( at[i2].nRingSystem != at[i1].nRingSystem || !bIsCenterPointStrict( at, i2 ) || #if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) !at[i2].bCutVertex || #endif at[i2].valence != 3 || at[i2].endpoint ) continue; bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM && bond_type != BOND_ALT12NS && bond_type != BOND_ALTERN ) { continue; /* not a single bond between Q-R */ } /* find endpoints */ for ( k = 0; k < at[i1].valence; k ++ ) { endpoint1 = at[i1].neighbor[k]; if ( endpoint1 == i2 ) continue; /* j == k */ if ( !(endpoint1_valence = nGetEndpointInfo( at, endpoint1, &eif1 ) ) ) continue; /* not an endpoint1 element or can't have mobile groups */ #if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) if ( at[endpoint1].nRingSystem == at[i1].nRingSystem ) continue; #endif nMobile1 = at[endpoint1].num_H + (at[endpoint1].charge == -1); /* number of mobile groups */ if ( nMobile1 + at[endpoint1].chem_bonds_valence != endpoint1_valence ) continue; /* abnormal endpoint1 valence; ignore. */ bond_type1 = (at[i1].bond_type[k] & ~BOND_MARK_ALL); if ( bond_type1 != BOND_SINGLE && bond_type1 != BOND_DOUBLE && bond_type1 != BOND_TAUTOM && bond_type1 != BOND_ALT12NS && bond_type1 != BOND_ALTERN ) continue; for ( m = 0; m < at[i2].valence; m ++ ) { endpoint2 = at[i2].neighbor[m]; if ( endpoint2 == i1 ) continue; if ( !(endpoint2_valence = nGetEndpointInfo( at, endpoint2, &eif2 )) ) continue; /* not an endpoint2 element or can't have mobile groups */ #if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) if ( at[endpoint2].nRingSystem == at[i2].nRingSystem ) continue; #endif nMobile2 = at[endpoint2].num_H + (at[endpoint2].charge == -1); /* number of mobile groups */ bond_type2 = (at[i2].bond_type[m] & ~BOND_MARK_ALL); if ( bond_type2 != BOND_SINGLE && bond_type2 != BOND_DOUBLE && bond_type2 != BOND_TAUTOM && bond_type2 != BOND_ALT12NS && bond_type2 != BOND_ALTERN ) continue; /* final test for possible tautomerism */ nMobile = 0; if ( ALLOWED_EDGE(pBNS, i1, k) && ALLOWED_EDGE(pBNS, i2, m) ) { /* can mobile group move from 1 to 2? */ nMobile += (at[endpoint1].endpoint || nMobile1) && /* from endpoint1 */ (bond_type1 != BOND_DOUBLE) && (at[endpoint2].endpoint || /* to endpoint2 */ eif2.cNeutralBondsValence > at[endpoint2].valence ) && (bond_type2 != BOND_SINGLE); /* can mobile group move from 2 to 1? */ nMobile += (at[endpoint2].endpoint || nMobile2) && /* from endpoint2 */ (bond_type2 != BOND_DOUBLE) && /*changed from BOND_SINGLE 2004-02-26 */ (at[endpoint1].endpoint || /* to endpoint1 */ eif1.cNeutralBondsValence > at[endpoint1].valence ) && (bond_type1 != BOND_SINGLE); } if ( !nMobile ) continue; if ( bond_type1 == bond_type2 && (bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) ) continue; /* -- old -- if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint && 1 != nMobile1 + nMobile2 ) continue; */ /* -- new -- if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint ) { if ( !(bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) || !(bond_type2 == BOND_SINGLE || bond_type2 == BOND_DOUBLE) ) { // at this point bond_type1 != bond_type2 continue; } if ( bond_type1 == BOND_SINGLE && !nMobile1 || bond_type2 == BOND_SINGLE && !nMobile2 || 0 == nMobile1 + nMobile2 ) { continue; } } */ #if ( TAUT_TROPOLONE_7 == 1 ) if ( at[i1].nNumAtInRingSystem >= 7 ) { ret = nGet14TautIn7MembAltRing( at, i1, j, k, m, nDfsPathPos, DfsPath, nMaxLenDfsPath, EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), BondPos, sizeof(BondPos)/sizeof(BondPos[0]), &nNumEndPoints, &nNumBondPos, pBNS, pBD, num_atoms); if ( ret > 0 ) { if ( nNumEndPoints ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto free_memory; tot_changes += (num_changes > 0); } if ( nNumBondPos ) { tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); } } else if ( IS_BNS_ERROR( ret ) ) { nErr = ret; goto free_memory; } } #endif #if ( TAUT_TROPOLONE_5 == 1 ) if ( at[i1].nNumAtInRingSystem >= 5 ) { ret = nGet14TautIn5MembAltRing( at, i1, j, k, m, nDfsPathPos, DfsPath, nMaxLenDfsPath, EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), BondPos, sizeof(BondPos)/sizeof(BondPos[0]), &nNumEndPoints, &nNumBondPos, pBNS, pBD, num_atoms); if ( ret > 0 ) { if ( nNumEndPoints ) { num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); if ( num_changes == -1 ) { nErr = CT_TAUCOUNT_ERR; } if ( num_changes < 0 ) { nErr = num_changes; } if ( nErr ) goto free_memory; tot_changes += (num_changes > 0); } if ( nNumBondPos ) { tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); } } else if ( IS_BNS_ERROR( ret ) ) { nErr = ret; goto free_memory; } } #endif } } } } } #endif /* } TAUT_TROPOLONE */ free_memory: if ( nDfsPathPos ) { inchi_free( nDfsPathPos ); } #undef MAX_ALT_PATH_LEN } #endif /* } FIND_RING_SYSTEMS */ exit_function: return nErr < 0? nErr : tot_changes; } /*****************************************************************************/ int free_t_group_info( T_GROUP_INFO *t_group_info ) { if ( t_group_info ) { if ( t_group_info->t_group ) { inchi_free( t_group_info->t_group ); } if ( t_group_info->nEndpointAtomNumber ) { inchi_free( t_group_info->nEndpointAtomNumber ); } if ( t_group_info->tGroupNumber ) { inchi_free( t_group_info->tGroupNumber ); } if ( t_group_info->nIsotopicEndpointAtomNumber ) { inchi_free( t_group_info->nIsotopicEndpointAtomNumber ); } memset( t_group_info, 0, sizeof(*t_group_info)); } return 0; } /*****************************************************************************/ int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info, T_GROUP_INFO *t_group_info_orig ) { int err = 0, len; free_t_group_info( t_group_info ); if ( t_group_info_orig && t_group_info ) { if ( (len=t_group_info_orig->max_num_t_groups) > 0 ) { if (t_group_info->t_group = (T_GROUP*) inchi_malloc( len * sizeof(t_group_info->t_group[0]))) { memcpy(t_group_info->t_group, t_group_info_orig->t_group, len * sizeof(t_group_info->t_group[0])); } else { err ++; } } if ( (len = t_group_info_orig->nNumEndpoints) > 0 ) { if (t_group_info->nEndpointAtomNumber = (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nEndpointAtomNumber[0]))) { memcpy(t_group_info->nEndpointAtomNumber, t_group_info_orig->nEndpointAtomNumber, len * sizeof(t_group_info->nEndpointAtomNumber[0])); } else { err ++; } } if ( (len = t_group_info_orig->num_t_groups) > 0 ) { if (t_group_info->tGroupNumber = (AT_NUMB*) inchi_malloc( len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0]))) { memcpy(t_group_info->tGroupNumber, t_group_info_orig->tGroupNumber, len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0])); } else { err ++; } } if ( (len = t_group_info_orig->nNumIsotopicEndpoints) > 0 ) { if (t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0]))) { memcpy(t_group_info->nIsotopicEndpointAtomNumber, t_group_info_orig->nIsotopicEndpointAtomNumber, len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); } else { err ++; } } if ( !err ) { t_group_info->nNumEndpoints = t_group_info_orig->nNumEndpoints; t_group_info->num_t_groups = t_group_info_orig->num_t_groups; t_group_info->max_num_t_groups = t_group_info_orig->max_num_t_groups; t_group_info->bIgnoreIsotopic = t_group_info_orig->bIgnoreIsotopic; t_group_info->nNumIsotopicEndpoints = t_group_info_orig->nNumIsotopicEndpoints; t_group_info->tni = t_group_info_orig->tni; /* t_group_info->nNumRemovedExplicitH = t_group_info_orig->nNumRemovedExplicitH; t_group_info->nNumRemovedProtons = t_group_info_orig->nNumRemovedProtons; t_group_info->bNormalizationFlags = t_group_info_orig->bNormalizationFlags; */ /* t_group_info->bHardAddedRemovedProtons = t_group_info_orig->bHardAddedRemovedProtons; t_group_info->bSimpleAddedRemovedProtons = t_group_info_orig->bSimpleAddedRemovedProtons; t_group_info->nNumCanceledCharges = t_group_info_orig->nNumCanceledCharges; */ } t_group_info->bTautFlags = t_group_info_orig->bTautFlags; t_group_info->bTautFlagsDone = t_group_info_orig->bTautFlagsDone; } return err; } /*****************************************************************************/ /* Set tautomer group isotopic sort keys */ /*****************************************************************************/ int set_tautomer_iso_sort_keys( T_GROUP_INFO *t_group_info ) { T_GROUP *t_group; T_GROUP_ISOWT Mult = 1; int i, j, num_t_groups, num_iso_t_groups = 0; if ( !t_group_info || !(t_group = t_group_info->t_group) || 0 >= (num_t_groups = t_group_info->num_t_groups) || t_group_info->nNumIsotopicEndpoints ) return 0; for ( i = 0; i < num_t_groups; i ++ ) { t_group[i].iWeight = 0; j = T_NUM_ISOTOPIC - 1; Mult = 1; do { t_group[i].iWeight += Mult * (T_GROUP_ISOWT)t_group[i].num[T_NUM_NO_ISOTOPIC+j]; } while ( --j >= 0 && (Mult *= T_GROUP_ISOWT_MULT) ); num_iso_t_groups += (t_group[i].iWeight != 0); } return num_iso_t_groups; } /****************************************************************************** * * Fill t_group_info with information necessary to fill out tautomer part * of the linear connection table record. * Note: on input, t_group_info should contain information created by MarkTautomerGroups() * No previous t_group_info adjustment due to throwing out disconnected parts of * the chemical structure is needed. * * Note2: throws out t_groups containing negative charges only (IGNORE_TGROUP_WITHOUT_H==1) * (leave their tautomeric bonds unchanged) * Note3: removes negative charges from other tautomeric groups * and adjust counts of mobile atoms if permitted (REMOVE_TGROUP_CHARGE==1) */ int CountTautomerGroups( sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) { int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; T_GROUP *t_group; int num_t; /* int bIgnoreIsotopic, max_num_t; */ AT_NUMB *nTautomerGroupNumber = NULL; AT_NUMB *nEndpointAtomNumber = NULL; AT_NUMB *tGroupNumber = NULL; if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { return 0; /* empty t-groups */ } num_t = t_group_info->num_t_groups; t_group = t_group_info->t_group; /* max_num_t = t_group_info->max_num_t_groups; bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; */ num_groups_noH = 0; /* the following 2 arrays are to be rebuilt here */ if ( t_group_info->nEndpointAtomNumber ) { inchi_free ( t_group_info->nEndpointAtomNumber ); t_group_info->nEndpointAtomNumber = NULL; } if ( t_group_info->tGroupNumber ) { inchi_free ( t_group_info->tGroupNumber ); t_group_info->tGroupNumber = NULL; } /* find max_t_group */ for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { if ( max_t_group < t_group[i].nGroupNumber ) max_t_group = t_group[i].nGroupNumber; } /* allocate memory for temp storage of numbers of endpoints */ if ( max_t_group && !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { goto err_exit_function; /* program error: out of RAM */ /* */ } /* count endpoints for each tautomer group */ for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { if ( (j = at[i].endpoint) == 0 ) continue; if ( j > max_t_group ) /* debug only */ goto err_exit_function; /* program error */ /* */ nTautomerGroupNumber[j] ++; nNumEndpoints ++; } if ( !nNumEndpoints ) { goto exit_function; /* not a tautomer */ } /* allocate temporary array */ if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { goto err_exit_function; /* program error: out of RAM */ /* */ } /* * Remove missing endpoints from t_group. Since only one * disconnected part is processed, some endpoints groups may have disappeared. * Mark t_groups containing charges only for subsequent removal */ for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { int bNoH = 0, nNumH; nGroupNumber = t_group[i].nGroupNumber; for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { nNumH -= (int)t_group[i].num[j]; } if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] #if ( IGNORE_TGROUP_WITHOUT_H == 1 ) || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ #endif ) { if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { /* the group belongs to another disconnected part of the structure or has only charges */ /* Remove the group */ num_t --; if ( i < num_t ) memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); if ( bNoH ) { /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ nTautomerGroupNumber[(int)nGroupNumber] = 0; num_groups_noH ++; } /*i --;*/ } else { /* different number of endpoints */ goto err_exit_function; /* program error */ /* */ } } else { /* renumber t_group and prepare to renumber at[i].endpoint */ nTautomerGroupNumber[(int)nGroupNumber] = t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ /* get first group atom orig. number position in the nEndpointAtomNumber[] */ /* and in the tautomer endpoint canon numbers part of the connection table */ t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; t_group[i].num[0] = nNumH; #if ( REMOVE_TGROUP_CHARGE == 1 ) t_group[i].num[1] = 0; /* remove only (-) charges */ #endif /* -- wrong condition. Disabled. if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only goto err_exit_function; // program error } */ i ++; } } if ( num_t != nNewGroupNumber ) { /* for debug only */ goto err_exit_function; /* program error */ /* */ } /* check if any tautomer group was left */ if ( !nNewGroupNumber ) { if ( !num_groups_noH ) goto err_exit_function; /* program error: not a tautomer */ /* */ else goto exit_function; } /* * an array for tautomer group sorting later, at the time of storing Connection Table * Later the sorting consists out of 2 steps: * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 * 2) Sort the groups indexes t_group_info->tGroupNumber[] */ if ( !(tGroupNumber= (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { goto err_exit_function; /* out of RAM */ } for ( i = 0; i < nNewGroupNumber; i ++ ) { tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ } /* * renumber endpoint atoms and save their orig. atom * numbers for filling out the tautomer part of the LinearCT. * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] */ for ( i = 0; i < num_atoms; i ++ ) { if ( j = (int)at[i].endpoint ) { j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ if ( nCurrEndpointAtNoPos[j] >= /* debug only */ t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { goto err_exit_function; /* program error */ /* */ } nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; } else { nNumEndpoints --; /* endpoint has been removed */ } } } t_group_info->num_t_groups = nNewGroupNumber; t_group_info->nNumEndpoints = nNumEndpoints; t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ inchi_free ( nTautomerGroupNumber ); inchi_free ( nCurrEndpointAtNoPos ); return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ err_exit_function: ret = CT_TAUCOUNT_ERR; exit_function: /* release allocated memory; set "no tautomeric group" */ if ( nEndpointAtomNumber ) inchi_free ( nEndpointAtomNumber ); if ( nTautomerGroupNumber ) inchi_free ( nTautomerGroupNumber ); if ( tGroupNumber ) inchi_free ( tGroupNumber ); if ( nCurrEndpointAtNoPos ) inchi_free ( nCurrEndpointAtNoPos ); t_group_info->nNumEndpoints = 0; t_group_info->num_t_groups = 0; if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { ret = 1; /* only protons have been (re)moved or neitralization happened */ } return ret; } #if ( READ_INCHI_STRING == 1 ) #if ( INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) /*****************************************************************************/ int CountTautomerGroupsInpAt( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) { int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; T_GROUP *t_group; int num_t; /* int bIgnoreIsotopic, max_num_t; */ AT_NUMB *nTautomerGroupNumber = NULL; AT_NUMB *nEndpointAtomNumber = NULL; AT_NUMB *tGroupNumber = NULL; if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { return 0; /* empty t-groups */ } num_t = t_group_info->num_t_groups; t_group = t_group_info->t_group; /* max_num_t = t_group_info->max_num_t_groups; bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; */ num_groups_noH = 0; /* the following 2 arrays are to be rebuilt here */ if ( t_group_info->nEndpointAtomNumber ) { inchi_free ( t_group_info->nEndpointAtomNumber ); t_group_info->nEndpointAtomNumber = NULL; } if ( t_group_info->tGroupNumber ) { inchi_free ( t_group_info->tGroupNumber ); t_group_info->tGroupNumber = NULL; } /* find max_t_group */ for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { if ( max_t_group < t_group[i].nGroupNumber ) max_t_group = t_group[i].nGroupNumber; } /* allocate memory for temp storage of numbers of endpoints */ if ( max_t_group && !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { goto err_exit_function; /* program error: out of RAM */ /* */ } /* count endpoints for each tautomer group */ for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { if ( (j = at[i].endpoint) == 0 ) continue; if ( j > max_t_group ) /* debug only */ goto err_exit_function; /* program error */ /* */ nTautomerGroupNumber[j] ++; nNumEndpoints ++; } if ( !nNumEndpoints ) { goto exit_function; /* not a tautomer */ } /* allocate temporary array */ if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { goto err_exit_function; /* program error: out of RAM */ /* */ } /* * Remove missing endpoints from t_group. Since only one * disconnected part is processed, some endpoints groups may have disappeared. * Mark t_groups containing charges only for subsequent removal */ for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { int bNoH = 0, nNumH; nGroupNumber = t_group[i].nGroupNumber; for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { nNumH -= (int)t_group[i].num[j]; } if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] #if ( IGNORE_TGROUP_WITHOUT_H == 1 ) || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ #endif ) { if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { /* the group belongs to another disconnected part of the structure or has only charges */ /* Remove the group */ num_t --; if ( i < num_t ) memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); if ( bNoH ) { /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ nTautomerGroupNumber[(int)nGroupNumber] = 0; num_groups_noH ++; } /*i --;*/ } else { /* different number of endpoints */ goto err_exit_function; /* program error */ /* */ } } else { /* renumber t_group and prepare to renumber at[i].endpoint */ nTautomerGroupNumber[(int)nGroupNumber] = t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ /* get first group atom orig. number position in the nEndpointAtomNumber[] */ /* and in the tautomer endpoint canon numbers part of the connection table */ t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; t_group[i].num[0] = nNumH; #if ( REMOVE_TGROUP_CHARGE == 1 ) t_group[i].num[1] = 0; /* remove only (-) charges */ #endif /* -- wrong condition. Disabled. if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only goto err_exit_function; // program error } */ i ++; } } if ( num_t != nNewGroupNumber ) { /* for debug only */ goto err_exit_function; /* program error */ /* */ } /* check if any tautomer group was left */ if ( !nNewGroupNumber ) { if ( !num_groups_noH ) goto err_exit_function; /* program error: not a tautomer */ /* */ else goto exit_function; } /* * an array for tautomer group sorting later, at the time of storing Connection Table * Later the sorting consists out of 2 steps: * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 * 2) Sort the groups indexes t_group_info->tGroupNumber[] */ if ( !(tGroupNumber= (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { goto err_exit_function; /* out of RAM */ } for ( i = 0; i < nNewGroupNumber; i ++ ) { tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ } /* * renumber endpoint atoms and save their orig. atom * numbers for filling out the tautomer part of the LinearCT. * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] */ for ( i = 0; i < num_atoms; i ++ ) { if ( j = (int)at[i].endpoint ) { j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ if ( nCurrEndpointAtNoPos[j] >= /* debug only */ t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { goto err_exit_function; /* program error */ /* */ } nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; } else { nNumEndpoints --; /* endpoint has been removed */ } } } t_group_info->num_t_groups = nNewGroupNumber; t_group_info->nNumEndpoints = nNumEndpoints; t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ inchi_free ( nTautomerGroupNumber ); inchi_free ( nCurrEndpointAtNoPos ); return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ err_exit_function: ret = CT_TAUCOUNT_ERR; exit_function: /* release allocated memory; set "no tautomeric group" */ if ( nEndpointAtomNumber ) inchi_free ( nEndpointAtomNumber ); if ( nTautomerGroupNumber ) inchi_free ( nTautomerGroupNumber ); if ( tGroupNumber ) inchi_free ( tGroupNumber ); if ( nCurrEndpointAtNoPos ) inchi_free ( nCurrEndpointAtNoPos ); t_group_info->nNumEndpoints = 0; t_group_info->num_t_groups = 0; if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { ret = 1; /* only protons have been (re)moved or neitralization happened */ } return ret; } #endif #endif /***************************************************************************** * tautomers: Compare for sorting *****************************************************************************/ /* Compare for sorting Ranks only */ /* Globals: pn_tRankForSort */ int CompRankTautomer(const void* a1, const void* a2 ) { int ret = (int)pn_tRankForSort[(int)(*(const AT_RANK*)a1)] - (int)pn_tRankForSort[(int)(*(const AT_RANK*)a2)]; return ret; } /*****************************************************************************/ int SortTautomerGroupsAndEndpoints( T_GROUP_INFO *t_group_info, int num_atoms, int num_at_tg, AT_RANK *nRank ) { int i, nFirstEndpointAtNoPos, nNumEndpoints; AT_NUMB *nEndpointAtomNumber; int num_t_groups = num_at_tg - num_atoms; T_GROUP *t_group = NULL; /* check if sorting is required */ if ( num_t_groups <= 0 || t_group_info->nNumEndpoints < 2 ) { return 0; /* no tautomer data */ } t_group = t_group_info->t_group; /* sort endpoints within the groups */ for ( i = 0; i < num_t_groups; i ++ ) { if ( t_group[i].nNumEndpoints < 2 ) continue; /* program error; should not happen */ /* */ /* set globals for sorting */ nFirstEndpointAtNoPos = t_group[i].nFirstEndpointAtNoPos; nNumEndpoints = t_group[i].nNumEndpoints; if ( nNumEndpoints + nFirstEndpointAtNoPos > t_group_info->nNumEndpoints ) { /* for debug only */ return CT_TAUCOUNT_ERR; /* program error */ /* */ } nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)nFirstEndpointAtNoPos; pn_tRankForSort = nRank; insertions_sort( nEndpointAtomNumber, nNumEndpoints, sizeof(nEndpointAtomNumber[0]), CompRankTautomer); } /* sort the tautomeric groups according to their ranks only (that is, ignoring the isotopic composition of the mobile groups and ranks of the endpoints) */ if ( t_group_info->num_t_groups > 1 ) { /* set globals for sorting */ /* a hack: the ranks of all tautomeric groups are */ /* located at nRank[num_atoms..num_at_tg-1] */ pn_tRankForSort = nRank+num_atoms; /* sort */ /* ordering numbers to sort : t_group_info->tGroupNumber; */ insertions_sort( t_group_info->tGroupNumber, num_t_groups, sizeof(t_group_info->tGroupNumber[0]), CompRankTautomer); } return t_group_info->num_t_groups; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichitaut.h000066400000000000000000000450411271037650300235400ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHITAUT_H__ #define __INCHITAUT_H__ #include "inpdef.h" #include "ichi_bns.h" /******************************************************* --- Header of tautomers groups --- --- Each entry is AT_TAUTOMER_HDR type --- number of tautomer groups (nNumTautGroups) index of the first tautomer group (#1) ... index of the last tautomer group (#nNumTautGroups) --- end of the Header of tautomers groups description --- --- One endpoint group description --- --- Each entry has AT_TAUTOMER type members --- number of endpoints (0=end of list) number of mobile groups, including number of negative charges (=num(H)+num(-)) number of negative charges number of 1H atoms number of 2H (deuterium) atoms number of 3H (tritium) atoms atom rank #1 (ascending order) ... atom rank #endpoints --- end of the endpoint group description ---- ---------------------------------------------- Note: In the following Linear CT Tautomer descriptions we assume the tautomeric groups and the endpoints within them have been properly sorted --------- Linear CT Tautomer description ----- -- fixed length part, non-isotopic -- number of endpoints = t_group->nNumEndpoints number of mobile atoms = t_group->num[0] ... number of negative charges = t_group->num[T_NUM_NO_ISOTOPIC-1] -- fixed length part, isotopic -- number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] ... number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] -- variable length part -- rank of the first endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos]]; ... rank of the last endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+t_group->nNumEndpoints-1]]; --------- Linear CT Isotopic Tautomer description ----- number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] ... number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] t-group ordering number in the Linear CT Tautomer, starts from 1 ***************************************************************/ #define T_NUM_NO_ISOTOPIC 2 #define T_NUM_ISOTOPIC NUM_H_ISOTOPES /* was 2, now 3 */ #define T_GROUP_HDR_LEN (1+T_NUM_NO_ISOTOPIC /*+T_NUM_ISOTOPIC*/) /* LinearCTTautomer */ typedef AT_NUMB AT_TAUTOMER; /* LinearCTTautomer */ typedef AT_ISO_SORT_KEY T_GROUP_ISOWT; /* must hold value up to T_GROUP_ISOWT_MULT^3-1 */ /* similar to AT_ISO_SORT_KEY */ /* = num_1H + T_GROUP_ISOWT_MULT*(num_D + T_GROUP_ISOWT_MULT*num_T) */ #define T_GROUP_ISOWT_MULT 1024 /* (max. number of identical isotopic hydrogens in a taut. group) + 1 */ /* changed from 256U 9-12-2003 */ /* (similar to AT_ISO_SORT_KEY_MULT ) */ /* note: (long)T_GROUP_ISOWT should always be positive (have zero sign bit) */ typedef struct tagIsotopicTautomerGroup { AT_NUMB tgroup_num; /* ordering number of a tautomer group with isotopes > 0 */ /* union { struct { AT_NUMB num_T; AT_NUMB num_D; AT_NUMB num_1H; }; AT_NUMB num[T_NUM_ISOTOPIC]; }; */ AT_NUMB num[T_NUM_ISOTOPIC]; /* inverted order: num_T, num_D, num_1H */ } AT_ISO_TGROUP; typedef enum tagTG_NumDA { /* 2004-02-26 */ TG_Num_dH, /* number of H donors that have only H (all single bonds) */ TG_Num_dM, /* number of H donors that have (-) (all single bonds) */ TG_Num_aH, /* number of H acceptors that have H and no (-) (+a double bond) */ TG_Num_aM, /* number of H acceptors that have (-) and possibly H (+ one double bond) */ TG_Num_dO, /* number of H donors =C-OH or =C-O(-) */ TG_Num_aO, /* number of H acceptors -C=O */ TG_NUM_DA /* number of elements in an array */ } TGNUMDA; typedef struct tagTautomerGroup { /* union { struct { // T_NUM_NO_ISOTOPIC = 2 elements: AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges AT_RANK num_NegCharges; // T_NUM_ISOTOPIC = 3 elements AT_RANK num_T; // here the isotopic part (num+T_NUM_NO_ISOTOPIC) starts AT_RANK num_D; AT_RANK num_1H; }; AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_ENDPOINT }; */ AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_ENDPOINT */ /* isotopic inv. order: num_T, num_D, num_1H */ AT_RANK num_DA[TG_NUM_DA]; T_GROUP_ISOWT iWeight; /* isotopic "weight" = T_GROUP_ISOWT_MULT*(T_GROUP_ISOWT_MULT*num_T + num_D)+num_1H; */ AT_NUMB nGroupNumber; /* positive tautomer group ID = atom->endpoint */ AT_NUMB nNumEndpoints; /* number of the atom numbers in T_GROUP_INFO::nEndpointAtomNumber[] */ AT_NUMB nFirstEndpointAtNoPos; /* the first index of the atom number in T_GROUP_INFO::nEndpointAtomNumber[] */ } T_GROUP; /* offsets/num_t_groups within T_GROUP_INFO::tGroupNumber */ #define TGSO_CURR_ORDER 0 /* tGroupNumber: current sorting order */ #define TGSO_SYMM_RANK 1 /* tSymmRank: symmetry ranks (no isotopes) = min. ordering number > 0. */ #define TGSO_SYMM_IORDER 2 /* tiGroupNumber: isotopic symmetry rank sorting order */ #define TGSO_SYMM_IRANK 3 /* tiSymmRank: isotopic symmetry ranks */ #define TGSO_TOTAL_LEN 4 /***************************************************/ /* flags for t_group_info->tni.bNormalizationFlags */ /***************************************************/ #define FLAG_PROTON_NPO_SIMPLE_REMOVED 0x0001 #define FLAG_PROTON_NP_HARD_REMOVED 0x0002 #define FLAG_PROTON_AC_SIMPLE_ADDED 0x0004 #define FLAG_PROTON_AC_SIMPLE_REMOVED 0x0008 #define FLAG_PROTON_AC_HARD_REMOVED 0x0010 #define FLAG_PROTON_AC_HARD_ADDED 0x0020 #define FLAG_PROTON_CHARGE_CANCEL 0x0040 #define FLAG_PROTON_SINGLE_REMOVED 0x0080 /* signifies tautomeric structure even though no t-group discovered */ #define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ FLAG_PROTON_NP_HARD_REMOVED | \ FLAG_PROTON_AC_SIMPLE_ADDED | \ FLAG_PROTON_AC_SIMPLE_REMOVED | \ FLAG_PROTON_AC_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_ADDED | \ FLAG_PROTON_SINGLE_REMOVED | \ FLAG_PROTON_CHARGE_CANCEL ) #if ( FIX_N_MINUS_NORN_BUG == 1 ) #define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_ADDED | \ FLAG_PROTON_CHARGE_CANCEL ) #else /* force salt tautomerism exploration */ #define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_REMOVED | \ FLAG_PROTON_AC_HARD_ADDED ) #endif typedef struct tagTautomerNormInfo { NUM_H nNumRemovedExplicitH; /* keeps track of explicit H */ NUM_H nNumRemovedProtons; NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; INCHI_MODE bNormalizationFlags; } TNI; /***************************************************/ /* t_group_info definition */ /***************************************************/ typedef struct tagTautomerGroupsInfo { T_GROUP *t_group; /* max_num_t_groups elements */ AT_NUMB *nEndpointAtomNumber; /* nNumEndpoints elements; also see comments to T_GROUP */ AT_NUMB *tGroupNumber; int nNumEndpoints; int num_t_groups; int max_num_t_groups; int bIgnoreIsotopic; AT_NUMB *nIsotopicEndpointAtomNumber; /* [0]: number of the following atoms; [1...]: non-tautomeric atoms that may have isotopic H */ int nNumIsotopicEndpoints; /* allocated length of nIsotopicEndpointAtomNumber */ NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ TNI tni; INCHI_MODE bTautFlags; INCHI_MODE bTautFlagsDone; } T_GROUP_INFO; #define CANON_FLAG_NO_H_RECANON 0x0001 /* iOther: second canonicalization of the no H structure */ #define CANON_FLAG_NO_TAUT_H_DIFF 0x0002 /* iOther: NoTautH eq. partition differs from NoH */ #define CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF 0x0004 /* iOther: eq. partition in isotopic only non-taut differs from non-isotopic */ #define CANON_FLAG_ISO_TAUT_DIFF 0x0008 /* iBase: isotopic eq. partition in isotopic taut differs from non-isotopic taut */ #define CANON_FLAG_ISO_FIXED_H_DIFF 0x0010 /* iOther: isotopic eq. partition in fixed H non-taut differs from non-isotopic fixed H */ /* Note: rank of tautomer atom #i = Rank[nEndpointAtomNumber[i]] */ /* for each tautomer atom group (t_group) t_group.nFirstEndpointAtNoPos */ /* is the first index of the atom number in nEndpointAtomNumber[] */ typedef struct tagTautomerEndpoint { /* union { struct { AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges AT_RANK num_NegCharges; AT_RANK num_T; AT_RANK num_D; }; AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_GROUP }; */ AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_GROUP */ AT_RANK num_DA[TG_NUM_DA]; AT_NUMB nGroupNumber; AT_NUMB nEquNumber; /* same for endpoints connected by alt paths */ AT_NUMB nAtomNumber; /*AT_NUMB neighbor_index; */ } T_ENDPOINT; typedef struct tagTautomerBondLocation { AT_NUMB nAtomNumber; AT_NUMB neighbor_index; } T_BONDPOS; typedef struct tagEndpointInfo { S_CHAR cMoveableCharge; S_CHAR cNeutralBondsValence; S_CHAR cMobile; S_CHAR cDonor; S_CHAR cAcceptor; S_CHAR cKetoEnolCode; /* 1 => carbon, 2 => oxygen */ /* post v.1 feature */ } ENDPOINT_INFO; /* positive charge group (extended onium) */ #define CHARGED_CPOINT(X,i) ((X)[i].charge==1) typedef struct tagChargeCandidate { AT_NUMB atnumber; S_CHAR type; S_CHAR subtype; } C_CANDIDATE; typedef struct tagChargeGroup { AT_RANK num[2]; /* [0]: number of (+), [1]: number atoms that have H, including H accessible through tautomerism */ AT_RANK num_CPoints; AT_NUMB nGroupNumber; U_CHAR cGroupType; } C_GROUP; typedef struct tagChargeGroupsInfo { C_GROUP *c_group; int num_c_groups; int max_num_c_groups; C_CANDIDATE *c_candidate; int max_num_candidates; int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ } C_GROUP_INFO; /* salts */ typedef struct tagSaltChargeCandidate { AT_NUMB atnumber; S_CHAR type; S_CHAR subtype; AT_NUMB endpoint; /* MAX_ATOMS+1 => found alt path to the candidate */ } S_CANDIDATE; typedef struct tagSaltGroupInfo { S_CANDIDATE *s_candidate; int max_num_candidates; int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ int num_other_candidates; /* num. non-"acidic O" candidates */ int num_p_only_candidates; /* num. non-tautomeric p-donor/acceptor candidates like -CH2-SH */ } S_GROUP_INFO; /********************* ATOM_SIZES *******************************/ /* sizes of a component */ typedef struct tagAtomSizes { /* for tautomeric and non-tautomeric structures */ int nMaxNumStereoAtoms; /* max. number of stereo atoms in isotopic case */ int nMaxNumStereoBonds; /* max. number of stereo bonds in isotopic case */ int num_isotopic_atoms; /* includes atoms that have isotopic tautomeric H */ int nLenCT; int nLenBonds; int nLenIsotopic; int nLenCTAtOnly; int nLenLinearCTStereoDble; /* max. number of stereo bonds in non-isotopic case */ int nLenLinearCTStereoCarb; /* max. number of stereo atoms in non-isotopic case */ /* int bHasIsotopicAtoms; */ int bMayHaveStereo; int bIgnoreIsotopic; /* tautomeric structure only; zeroes in non-tautomeric */ int nLenLinearCTTautomer; int nLenLinearCTIsotopicTautomer; int bHasIsotopicTautGroups; int nLenIsotopicEndpoints; } ATOM_SIZES; typedef struct tagDfsPath { AT_RANK at_no; /*AT_RANK nDfsLevel;*/ U_CHAR bond_type; S_CHAR bond_pos; } DFS_PATH; #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int is_centerpoint_elem( U_CHAR el_number ); int is_centerpoint_elem_strict( U_CHAR el_number ); #if ( KETO_ENOL_TAUT == 1 ) int is_centerpoint_elem_KET( U_CHAR el_number ); #endif int bIsCenterPointStrict( inp_ATOM *atom, int iat ); int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); #if ( KETO_ENOL_TAUT == 1 ) int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); #endif void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ); int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ); int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ); int bHasAcidicHydrogen( inp_ATOM *at, int i ); int bHasOtherExchangableH ( inp_ATOM *at, int i ); int bHasAcidicMinus( inp_ATOM *at, int i ); int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nBfsTreePos, DFS_PATH *DfsPath, int nMaxLenBfsTree, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, AT_RANK *nBfsTreePos, DFS_PATH *DfsPath, int nMaxLenBfsTree, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, T_BONDPOS *BondPos, int nMaxNumBondPos, int *pnNumEndPoint, int *pnNumBondPos, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); #if ( RING2CHAIN == 1 ) int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ); #endif #if ( UNDERIVATIZE == 1 ) int underivatize( ORIG_ATOM_DATA *orig_inp_data ); #endif #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCHITAUT_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ichitime.h000066400000000000000000000057661271037650300235330ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __ICHITIME_H__ #define __ICHITIME_H__ #ifdef COMPILE_ANSI_ONLY #ifdef __FreeBSD__ #include #endif /* get times() */ #ifdef INCHI_USETIMES #include #endif /*#include */ #include typedef struct tagInchiTime { clock_t clockTime; } inchiTime; #else /* Win32 _ftime(): */ #include typedef struct tagInchiTime { unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; signed long overflow expected in 2038 */ long millitime; /* milliseconds */ } inchiTime; #endif #ifdef TARGET_EXE_USING_API #define InchiTimeGet e_InchiTimeGet #define InchiTimeMsecDiff e_InchiTimeMsecDiff #define InchiTimeAddMsec e_InchiTimeAddMsec #define bInchiTimeIsOver e_bInchiTimeIsOver #define InchiTimeElapsed e_InchiTimeElapsed #define FullMaxClock e_FullMaxClock #define HalfMaxClock e_HalfMaxClock #define MaxPositiveClock e_MaxPositiveClock #define MinNegativeClock e_MinNegativeClock #define HalfMaxPositiveClock e_HalfMaxPositiveClock #define HalfMinNegativeClock e_HalfMinNegativeClock #endif #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif void InchiTimeGet( inchiTime *TickEnd ); long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ); void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ); int bInchiTimeIsOver( inchiTime *TickEnd ); long InchiTimeElapsed( inchiTime *TickStart ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __ICHITIME_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ikey_base26.c000066400000000000000000003372371271037650300240370ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChIKey: procedures for base-26 encoding ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #ifdef _MSC_VER #if _MSC_VER > 1000 #pragma warning( disable : 4996 ) #endif #endif #include #include #include #include "ikey_base26.h" /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As the 2^14 (16384) is very close to 26^3 (17576), a triplet of uppercase letters A..Z encodes 14 bits with good efficiency. For speed, we just tabulate triplets below. We should throw away 17576-16384= 1192 triplets. These are 676 triplets starting from 'E', the most frequent letter in English texts (the other 516 are those started at 'T' , "TAA" to "TTV"). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ static char t26[][4] = { "AAA","AAB","AAC","AAD","AAE","AAF","AAG","AAH","AAI","AAJ","AAK","AAL","AAM","AAN","AAO","AAP", "AAQ","AAR","AAS","AAT","AAU","AAV","AAW","AAX","AAY","AAZ","ABA","ABB","ABC","ABD","ABE","ABF", "ABG","ABH","ABI","ABJ","ABK","ABL","ABM","ABN","ABO","ABP","ABQ","ABR","ABS","ABT","ABU","ABV", "ABW","ABX","ABY","ABZ","ACA","ACB","ACC","ACD","ACE","ACF","ACG","ACH","ACI","ACJ","ACK","ACL", "ACM","ACN","ACO","ACP","ACQ","ACR","ACS","ACT","ACU","ACV","ACW","ACX","ACY","ACZ","ADA","ADB", "ADC","ADD","ADE","ADF","ADG","ADH","ADI","ADJ","ADK","ADL","ADM","ADN","ADO","ADP","ADQ","ADR", "ADS","ADT","ADU","ADV","ADW","ADX","ADY","ADZ","AEA","AEB","AEC","AED","AEE","AEF","AEG","AEH", "AEI","AEJ","AEK","AEL","AEM","AEN","AEO","AEP","AEQ","AER","AES","AET","AEU","AEV","AEW","AEX", "AEY","AEZ","AFA","AFB","AFC","AFD","AFE","AFF","AFG","AFH","AFI","AFJ","AFK","AFL","AFM","AFN", "AFO","AFP","AFQ","AFR","AFS","AFT","AFU","AFV","AFW","AFX","AFY","AFZ","AGA","AGB","AGC","AGD", "AGE","AGF","AGG","AGH","AGI","AGJ","AGK","AGL","AGM","AGN","AGO","AGP","AGQ","AGR","AGS","AGT", "AGU","AGV","AGW","AGX","AGY","AGZ","AHA","AHB","AHC","AHD","AHE","AHF","AHG","AHH","AHI","AHJ", "AHK","AHL","AHM","AHN","AHO","AHP","AHQ","AHR","AHS","AHT","AHU","AHV","AHW","AHX","AHY","AHZ", "AIA","AIB","AIC","AID","AIE","AIF","AIG","AIH","AII","AIJ","AIK","AIL","AIM","AIN","AIO","AIP", "AIQ","AIR","AIS","AIT","AIU","AIV","AIW","AIX","AIY","AIZ","AJA","AJB","AJC","AJD","AJE","AJF", "AJG","AJH","AJI","AJJ","AJK","AJL","AJM","AJN","AJO","AJP","AJQ","AJR","AJS","AJT","AJU","AJV", "AJW","AJX","AJY","AJZ","AKA","AKB","AKC","AKD","AKE","AKF","AKG","AKH","AKI","AKJ","AKK","AKL", "AKM","AKN","AKO","AKP","AKQ","AKR","AKS","AKT","AKU","AKV","AKW","AKX","AKY","AKZ","ALA","ALB", "ALC","ALD","ALE","ALF","ALG","ALH","ALI","ALJ","ALK","ALL","ALM","ALN","ALO","ALP","ALQ","ALR", "ALS","ALT","ALU","ALV","ALW","ALX","ALY","ALZ","AMA","AMB","AMC","AMD","AME","AMF","AMG","AMH", "AMI","AMJ","AMK","AML","AMM","AMN","AMO","AMP","AMQ","AMR","AMS","AMT","AMU","AMV","AMW","AMX", "AMY","AMZ","ANA","ANB","ANC","AND","ANE","ANF","ANG","ANH","ANI","ANJ","ANK","ANL","ANM","ANN", "ANO","ANP","ANQ","ANR","ANS","ANT","ANU","ANV","ANW","ANX","ANY","ANZ","AOA","AOB","AOC","AOD", "AOE","AOF","AOG","AOH","AOI","AOJ","AOK","AOL","AOM","AON","AOO","AOP","AOQ","AOR","AOS","AOT", "AOU","AOV","AOW","AOX","AOY","AOZ","APA","APB","APC","APD","APE","APF","APG","APH","API","APJ", "APK","APL","APM","APN","APO","APP","APQ","APR","APS","APT","APU","APV","APW","APX","APY","APZ", "AQA","AQB","AQC","AQD","AQE","AQF","AQG","AQH","AQI","AQJ","AQK","AQL","AQM","AQN","AQO","AQP", "AQQ","AQR","AQS","AQT","AQU","AQV","AQW","AQX","AQY","AQZ","ARA","ARB","ARC","ARD","ARE","ARF", "ARG","ARH","ARI","ARJ","ARK","ARL","ARM","ARN","ARO","ARP","ARQ","ARR","ARS","ART","ARU","ARV", "ARW","ARX","ARY","ARZ","ASA","ASB","ASC","ASD","ASE","ASF","ASG","ASH","ASI","ASJ","ASK","ASL", "ASM","ASN","ASO","ASP","ASQ","ASR","ASS","AST","ASU","ASV","ASW","ASX","ASY","ASZ","ATA","ATB", "ATC","ATD","ATE","ATF","ATG","ATH","ATI","ATJ","ATK","ATL","ATM","ATN","ATO","ATP","ATQ","ATR", "ATS","ATT","ATU","ATV","ATW","ATX","ATY","ATZ","AUA","AUB","AUC","AUD","AUE","AUF","AUG","AUH", "AUI","AUJ","AUK","AUL","AUM","AUN","AUO","AUP","AUQ","AUR","AUS","AUT","AUU","AUV","AUW","AUX", "AUY","AUZ","AVA","AVB","AVC","AVD","AVE","AVF","AVG","AVH","AVI","AVJ","AVK","AVL","AVM","AVN", "AVO","AVP","AVQ","AVR","AVS","AVT","AVU","AVV","AVW","AVX","AVY","AVZ","AWA","AWB","AWC","AWD", "AWE","AWF","AWG","AWH","AWI","AWJ","AWK","AWL","AWM","AWN","AWO","AWP","AWQ","AWR","AWS","AWT", "AWU","AWV","AWW","AWX","AWY","AWZ","AXA","AXB","AXC","AXD","AXE","AXF","AXG","AXH","AXI","AXJ", "AXK","AXL","AXM","AXN","AXO","AXP","AXQ","AXR","AXS","AXT","AXU","AXV","AXW","AXX","AXY","AXZ", "AYA","AYB","AYC","AYD","AYE","AYF","AYG","AYH","AYI","AYJ","AYK","AYL","AYM","AYN","AYO","AYP", "AYQ","AYR","AYS","AYT","AYU","AYV","AYW","AYX","AYY","AYZ","AZA","AZB","AZC","AZD","AZE","AZF", "AZG","AZH","AZI","AZJ","AZK","AZL","AZM","AZN","AZO","AZP","AZQ","AZR","AZS","AZT","AZU","AZV", "AZW","AZX","AZY","AZZ","BAA","BAB","BAC","BAD","BAE","BAF","BAG","BAH","BAI","BAJ","BAK","BAL", "BAM","BAN","BAO","BAP","BAQ","BAR","BAS","BAT","BAU","BAV","BAW","BAX","BAY","BAZ","BBA","BBB", "BBC","BBD","BBE","BBF","BBG","BBH","BBI","BBJ","BBK","BBL","BBM","BBN","BBO","BBP","BBQ","BBR", "BBS","BBT","BBU","BBV","BBW","BBX","BBY","BBZ","BCA","BCB","BCC","BCD","BCE","BCF","BCG","BCH", "BCI","BCJ","BCK","BCL","BCM","BCN","BCO","BCP","BCQ","BCR","BCS","BCT","BCU","BCV","BCW","BCX", "BCY","BCZ","BDA","BDB","BDC","BDD","BDE","BDF","BDG","BDH","BDI","BDJ","BDK","BDL","BDM","BDN", "BDO","BDP","BDQ","BDR","BDS","BDT","BDU","BDV","BDW","BDX","BDY","BDZ","BEA","BEB","BEC","BED", "BEE","BEF","BEG","BEH","BEI","BEJ","BEK","BEL","BEM","BEN","BEO","BEP","BEQ","BER","BES","BET", "BEU","BEV","BEW","BEX","BEY","BEZ","BFA","BFB","BFC","BFD","BFE","BFF","BFG","BFH","BFI","BFJ", "BFK","BFL","BFM","BFN","BFO","BFP","BFQ","BFR","BFS","BFT","BFU","BFV","BFW","BFX","BFY","BFZ", "BGA","BGB","BGC","BGD","BGE","BGF","BGG","BGH","BGI","BGJ","BGK","BGL","BGM","BGN","BGO","BGP", "BGQ","BGR","BGS","BGT","BGU","BGV","BGW","BGX","BGY","BGZ","BHA","BHB","BHC","BHD","BHE","BHF", "BHG","BHH","BHI","BHJ","BHK","BHL","BHM","BHN","BHO","BHP","BHQ","BHR","BHS","BHT","BHU","BHV", "BHW","BHX","BHY","BHZ","BIA","BIB","BIC","BID","BIE","BIF","BIG","BIH","BII","BIJ","BIK","BIL", "BIM","BIN","BIO","BIP","BIQ","BIR","BIS","BIT","BIU","BIV","BIW","BIX","BIY","BIZ","BJA","BJB", "BJC","BJD","BJE","BJF","BJG","BJH","BJI","BJJ","BJK","BJL","BJM","BJN","BJO","BJP","BJQ","BJR", "BJS","BJT","BJU","BJV","BJW","BJX","BJY","BJZ","BKA","BKB","BKC","BKD","BKE","BKF","BKG","BKH", "BKI","BKJ","BKK","BKL","BKM","BKN","BKO","BKP","BKQ","BKR","BKS","BKT","BKU","BKV","BKW","BKX", "BKY","BKZ","BLA","BLB","BLC","BLD","BLE","BLF","BLG","BLH","BLI","BLJ","BLK","BLL","BLM","BLN", "BLO","BLP","BLQ","BLR","BLS","BLT","BLU","BLV","BLW","BLX","BLY","BLZ","BMA","BMB","BMC","BMD", "BME","BMF","BMG","BMH","BMI","BMJ","BMK","BML","BMM","BMN","BMO","BMP","BMQ","BMR","BMS","BMT", "BMU","BMV","BMW","BMX","BMY","BMZ","BNA","BNB","BNC","BND","BNE","BNF","BNG","BNH","BNI","BNJ", "BNK","BNL","BNM","BNN","BNO","BNP","BNQ","BNR","BNS","BNT","BNU","BNV","BNW","BNX","BNY","BNZ", "BOA","BOB","BOC","BOD","BOE","BOF","BOG","BOH","BOI","BOJ","BOK","BOL","BOM","BON","BOO","BOP", "BOQ","BOR","BOS","BOT","BOU","BOV","BOW","BOX","BOY","BOZ","BPA","BPB","BPC","BPD","BPE","BPF", "BPG","BPH","BPI","BPJ","BPK","BPL","BPM","BPN","BPO","BPP","BPQ","BPR","BPS","BPT","BPU","BPV", "BPW","BPX","BPY","BPZ","BQA","BQB","BQC","BQD","BQE","BQF","BQG","BQH","BQI","BQJ","BQK","BQL", "BQM","BQN","BQO","BQP","BQQ","BQR","BQS","BQT","BQU","BQV","BQW","BQX","BQY","BQZ","BRA","BRB", "BRC","BRD","BRE","BRF","BRG","BRH","BRI","BRJ","BRK","BRL","BRM","BRN","BRO","BRP","BRQ","BRR", "BRS","BRT","BRU","BRV","BRW","BRX","BRY","BRZ","BSA","BSB","BSC","BSD","BSE","BSF","BSG","BSH", "BSI","BSJ","BSK","BSL","BSM","BSN","BSO","BSP","BSQ","BSR","BSS","BST","BSU","BSV","BSW","BSX", "BSY","BSZ","BTA","BTB","BTC","BTD","BTE","BTF","BTG","BTH","BTI","BTJ","BTK","BTL","BTM","BTN", "BTO","BTP","BTQ","BTR","BTS","BTT","BTU","BTV","BTW","BTX","BTY","BTZ","BUA","BUB","BUC","BUD", "BUE","BUF","BUG","BUH","BUI","BUJ","BUK","BUL","BUM","BUN","BUO","BUP","BUQ","BUR","BUS","BUT", "BUU","BUV","BUW","BUX","BUY","BUZ","BVA","BVB","BVC","BVD","BVE","BVF","BVG","BVH","BVI","BVJ", "BVK","BVL","BVM","BVN","BVO","BVP","BVQ","BVR","BVS","BVT","BVU","BVV","BVW","BVX","BVY","BVZ", "BWA","BWB","BWC","BWD","BWE","BWF","BWG","BWH","BWI","BWJ","BWK","BWL","BWM","BWN","BWO","BWP", "BWQ","BWR","BWS","BWT","BWU","BWV","BWW","BWX","BWY","BWZ","BXA","BXB","BXC","BXD","BXE","BXF", "BXG","BXH","BXI","BXJ","BXK","BXL","BXM","BXN","BXO","BXP","BXQ","BXR","BXS","BXT","BXU","BXV", "BXW","BXX","BXY","BXZ","BYA","BYB","BYC","BYD","BYE","BYF","BYG","BYH","BYI","BYJ","BYK","BYL", "BYM","BYN","BYO","BYP","BYQ","BYR","BYS","BYT","BYU","BYV","BYW","BYX","BYY","BYZ","BZA","BZB", "BZC","BZD","BZE","BZF","BZG","BZH","BZI","BZJ","BZK","BZL","BZM","BZN","BZO","BZP","BZQ","BZR", "BZS","BZT","BZU","BZV","BZW","BZX","BZY","BZZ","CAA","CAB","CAC","CAD","CAE","CAF","CAG","CAH", "CAI","CAJ","CAK","CAL","CAM","CAN","CAO","CAP","CAQ","CAR","CAS","CAT","CAU","CAV","CAW","CAX", "CAY","CAZ","CBA","CBB","CBC","CBD","CBE","CBF","CBG","CBH","CBI","CBJ","CBK","CBL","CBM","CBN", "CBO","CBP","CBQ","CBR","CBS","CBT","CBU","CBV","CBW","CBX","CBY","CBZ","CCA","CCB","CCC","CCD", "CCE","CCF","CCG","CCH","CCI","CCJ","CCK","CCL","CCM","CCN","CCO","CCP","CCQ","CCR","CCS","CCT", "CCU","CCV","CCW","CCX","CCY","CCZ","CDA","CDB","CDC","CDD","CDE","CDF","CDG","CDH","CDI","CDJ", "CDK","CDL","CDM","CDN","CDO","CDP","CDQ","CDR","CDS","CDT","CDU","CDV","CDW","CDX","CDY","CDZ", "CEA","CEB","CEC","CED","CEE","CEF","CEG","CEH","CEI","CEJ","CEK","CEL","CEM","CEN","CEO","CEP", "CEQ","CER","CES","CET","CEU","CEV","CEW","CEX","CEY","CEZ","CFA","CFB","CFC","CFD","CFE","CFF", "CFG","CFH","CFI","CFJ","CFK","CFL","CFM","CFN","CFO","CFP","CFQ","CFR","CFS","CFT","CFU","CFV", "CFW","CFX","CFY","CFZ","CGA","CGB","CGC","CGD","CGE","CGF","CGG","CGH","CGI","CGJ","CGK","CGL", "CGM","CGN","CGO","CGP","CGQ","CGR","CGS","CGT","CGU","CGV","CGW","CGX","CGY","CGZ","CHA","CHB", "CHC","CHD","CHE","CHF","CHG","CHH","CHI","CHJ","CHK","CHL","CHM","CHN","CHO","CHP","CHQ","CHR", "CHS","CHT","CHU","CHV","CHW","CHX","CHY","CHZ","CIA","CIB","CIC","CID","CIE","CIF","CIG","CIH", "CII","CIJ","CIK","CIL","CIM","CIN","CIO","CIP","CIQ","CIR","CIS","CIT","CIU","CIV","CIW","CIX", "CIY","CIZ","CJA","CJB","CJC","CJD","CJE","CJF","CJG","CJH","CJI","CJJ","CJK","CJL","CJM","CJN", "CJO","CJP","CJQ","CJR","CJS","CJT","CJU","CJV","CJW","CJX","CJY","CJZ","CKA","CKB","CKC","CKD", "CKE","CKF","CKG","CKH","CKI","CKJ","CKK","CKL","CKM","CKN","CKO","CKP","CKQ","CKR","CKS","CKT", "CKU","CKV","CKW","CKX","CKY","CKZ","CLA","CLB","CLC","CLD","CLE","CLF","CLG","CLH","CLI","CLJ", "CLK","CLL","CLM","CLN","CLO","CLP","CLQ","CLR","CLS","CLT","CLU","CLV","CLW","CLX","CLY","CLZ", "CMA","CMB","CMC","CMD","CME","CMF","CMG","CMH","CMI","CMJ","CMK","CML","CMM","CMN","CMO","CMP", "CMQ","CMR","CMS","CMT","CMU","CMV","CMW","CMX","CMY","CMZ","CNA","CNB","CNC","CND","CNE","CNF", "CNG","CNH","CNI","CNJ","CNK","CNL","CNM","CNN","CNO","CNP","CNQ","CNR","CNS","CNT","CNU","CNV", "CNW","CNX","CNY","CNZ","COA","COB","COC","COD","COE","COF","COG","COH","COI","COJ","COK","COL", "COM","CON","COO","COP","COQ","COR","COS","COT","COU","COV","COW","COX","COY","COZ","CPA","CPB", "CPC","CPD","CPE","CPF","CPG","CPH","CPI","CPJ","CPK","CPL","CPM","CPN","CPO","CPP","CPQ","CPR", "CPS","CPT","CPU","CPV","CPW","CPX","CPY","CPZ","CQA","CQB","CQC","CQD","CQE","CQF","CQG","CQH", "CQI","CQJ","CQK","CQL","CQM","CQN","CQO","CQP","CQQ","CQR","CQS","CQT","CQU","CQV","CQW","CQX", "CQY","CQZ","CRA","CRB","CRC","CRD","CRE","CRF","CRG","CRH","CRI","CRJ","CRK","CRL","CRM","CRN", "CRO","CRP","CRQ","CRR","CRS","CRT","CRU","CRV","CRW","CRX","CRY","CRZ","CSA","CSB","CSC","CSD", "CSE","CSF","CSG","CSH","CSI","CSJ","CSK","CSL","CSM","CSN","CSO","CSP","CSQ","CSR","CSS","CST", "CSU","CSV","CSW","CSX","CSY","CSZ","CTA","CTB","CTC","CTD","CTE","CTF","CTG","CTH","CTI","CTJ", "CTK","CTL","CTM","CTN","CTO","CTP","CTQ","CTR","CTS","CTT","CTU","CTV","CTW","CTX","CTY","CTZ", "CUA","CUB","CUC","CUD","CUE","CUF","CUG","CUH","CUI","CUJ","CUK","CUL","CUM","CUN","CUO","CUP", "CUQ","CUR","CUS","CUT","CUU","CUV","CUW","CUX","CUY","CUZ","CVA","CVB","CVC","CVD","CVE","CVF", "CVG","CVH","CVI","CVJ","CVK","CVL","CVM","CVN","CVO","CVP","CVQ","CVR","CVS","CVT","CVU","CVV", "CVW","CVX","CVY","CVZ","CWA","CWB","CWC","CWD","CWE","CWF","CWG","CWH","CWI","CWJ","CWK","CWL", "CWM","CWN","CWO","CWP","CWQ","CWR","CWS","CWT","CWU","CWV","CWW","CWX","CWY","CWZ","CXA","CXB", "CXC","CXD","CXE","CXF","CXG","CXH","CXI","CXJ","CXK","CXL","CXM","CXN","CXO","CXP","CXQ","CXR", "CXS","CXT","CXU","CXV","CXW","CXX","CXY","CXZ","CYA","CYB","CYC","CYD","CYE","CYF","CYG","CYH", "CYI","CYJ","CYK","CYL","CYM","CYN","CYO","CYP","CYQ","CYR","CYS","CYT","CYU","CYV","CYW","CYX", "CYY","CYZ","CZA","CZB","CZC","CZD","CZE","CZF","CZG","CZH","CZI","CZJ","CZK","CZL","CZM","CZN", "CZO","CZP","CZQ","CZR","CZS","CZT","CZU","CZV","CZW","CZX","CZY","CZZ","DAA","DAB","DAC","DAD", "DAE","DAF","DAG","DAH","DAI","DAJ","DAK","DAL","DAM","DAN","DAO","DAP","DAQ","DAR","DAS","DAT", "DAU","DAV","DAW","DAX","DAY","DAZ","DBA","DBB","DBC","DBD","DBE","DBF","DBG","DBH","DBI","DBJ", "DBK","DBL","DBM","DBN","DBO","DBP","DBQ","DBR","DBS","DBT","DBU","DBV","DBW","DBX","DBY","DBZ", "DCA","DCB","DCC","DCD","DCE","DCF","DCG","DCH","DCI","DCJ","DCK","DCL","DCM","DCN","DCO","DCP", "DCQ","DCR","DCS","DCT","DCU","DCV","DCW","DCX","DCY","DCZ","DDA","DDB","DDC","DDD","DDE","DDF", "DDG","DDH","DDI","DDJ","DDK","DDL","DDM","DDN","DDO","DDP","DDQ","DDR","DDS","DDT","DDU","DDV", "DDW","DDX","DDY","DDZ","DEA","DEB","DEC","DED","DEE","DEF","DEG","DEH","DEI","DEJ","DEK","DEL", "DEM","DEN","DEO","DEP","DEQ","DER","DES","DET","DEU","DEV","DEW","DEX","DEY","DEZ","DFA","DFB", "DFC","DFD","DFE","DFF","DFG","DFH","DFI","DFJ","DFK","DFL","DFM","DFN","DFO","DFP","DFQ","DFR", "DFS","DFT","DFU","DFV","DFW","DFX","DFY","DFZ","DGA","DGB","DGC","DGD","DGE","DGF","DGG","DGH", "DGI","DGJ","DGK","DGL","DGM","DGN","DGO","DGP","DGQ","DGR","DGS","DGT","DGU","DGV","DGW","DGX", "DGY","DGZ","DHA","DHB","DHC","DHD","DHE","DHF","DHG","DHH","DHI","DHJ","DHK","DHL","DHM","DHN", "DHO","DHP","DHQ","DHR","DHS","DHT","DHU","DHV","DHW","DHX","DHY","DHZ","DIA","DIB","DIC","DID", "DIE","DIF","DIG","DIH","DII","DIJ","DIK","DIL","DIM","DIN","DIO","DIP","DIQ","DIR","DIS","DIT", "DIU","DIV","DIW","DIX","DIY","DIZ","DJA","DJB","DJC","DJD","DJE","DJF","DJG","DJH","DJI","DJJ", "DJK","DJL","DJM","DJN","DJO","DJP","DJQ","DJR","DJS","DJT","DJU","DJV","DJW","DJX","DJY","DJZ", "DKA","DKB","DKC","DKD","DKE","DKF","DKG","DKH","DKI","DKJ","DKK","DKL","DKM","DKN","DKO","DKP", "DKQ","DKR","DKS","DKT","DKU","DKV","DKW","DKX","DKY","DKZ","DLA","DLB","DLC","DLD","DLE","DLF", "DLG","DLH","DLI","DLJ","DLK","DLL","DLM","DLN","DLO","DLP","DLQ","DLR","DLS","DLT","DLU","DLV", "DLW","DLX","DLY","DLZ","DMA","DMB","DMC","DMD","DME","DMF","DMG","DMH","DMI","DMJ","DMK","DML", "DMM","DMN","DMO","DMP","DMQ","DMR","DMS","DMT","DMU","DMV","DMW","DMX","DMY","DMZ","DNA","DNB", "DNC","DND","DNE","DNF","DNG","DNH","DNI","DNJ","DNK","DNL","DNM","DNN","DNO","DNP","DNQ","DNR", "DNS","DNT","DNU","DNV","DNW","DNX","DNY","DNZ","DOA","DOB","DOC","DOD","DOE","DOF","DOG","DOH", "DOI","DOJ","DOK","DOL","DOM","DON","DOO","DOP","DOQ","DOR","DOS","DOT","DOU","DOV","DOW","DOX", "DOY","DOZ","DPA","DPB","DPC","DPD","DPE","DPF","DPG","DPH","DPI","DPJ","DPK","DPL","DPM","DPN", "DPO","DPP","DPQ","DPR","DPS","DPT","DPU","DPV","DPW","DPX","DPY","DPZ","DQA","DQB","DQC","DQD", "DQE","DQF","DQG","DQH","DQI","DQJ","DQK","DQL","DQM","DQN","DQO","DQP","DQQ","DQR","DQS","DQT", "DQU","DQV","DQW","DQX","DQY","DQZ","DRA","DRB","DRC","DRD","DRE","DRF","DRG","DRH","DRI","DRJ", "DRK","DRL","DRM","DRN","DRO","DRP","DRQ","DRR","DRS","DRT","DRU","DRV","DRW","DRX","DRY","DRZ", "DSA","DSB","DSC","DSD","DSE","DSF","DSG","DSH","DSI","DSJ","DSK","DSL","DSM","DSN","DSO","DSP", "DSQ","DSR","DSS","DST","DSU","DSV","DSW","DSX","DSY","DSZ","DTA","DTB","DTC","DTD","DTE","DTF", "DTG","DTH","DTI","DTJ","DTK","DTL","DTM","DTN","DTO","DTP","DTQ","DTR","DTS","DTT","DTU","DTV", "DTW","DTX","DTY","DTZ","DUA","DUB","DUC","DUD","DUE","DUF","DUG","DUH","DUI","DUJ","DUK","DUL", "DUM","DUN","DUO","DUP","DUQ","DUR","DUS","DUT","DUU","DUV","DUW","DUX","DUY","DUZ","DVA","DVB", "DVC","DVD","DVE","DVF","DVG","DVH","DVI","DVJ","DVK","DVL","DVM","DVN","DVO","DVP","DVQ","DVR", "DVS","DVT","DVU","DVV","DVW","DVX","DVY","DVZ","DWA","DWB","DWC","DWD","DWE","DWF","DWG","DWH", "DWI","DWJ","DWK","DWL","DWM","DWN","DWO","DWP","DWQ","DWR","DWS","DWT","DWU","DWV","DWW","DWX", "DWY","DWZ","DXA","DXB","DXC","DXD","DXE","DXF","DXG","DXH","DXI","DXJ","DXK","DXL","DXM","DXN", "DXO","DXP","DXQ","DXR","DXS","DXT","DXU","DXV","DXW","DXX","DXY","DXZ","DYA","DYB","DYC","DYD", "DYE","DYF","DYG","DYH","DYI","DYJ","DYK","DYL","DYM","DYN","DYO","DYP","DYQ","DYR","DYS","DYT", "DYU","DYV","DYW","DYX","DYY","DYZ","DZA","DZB","DZC","DZD","DZE","DZF","DZG","DZH","DZI","DZJ", "DZK","DZL","DZM","DZN","DZO","DZP","DZQ","DZR","DZS","DZT","DZU","DZV","DZW","DZX","DZY","DZZ", /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E-starteds are intentionally omitted ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ "FAA","FAB","FAC","FAD","FAE","FAF","FAG","FAH","FAI","FAJ","FAK","FAL", "FAM","FAN","FAO","FAP","FAQ","FAR","FAS","FAT","FAU","FAV","FAW","FAX","FAY","FAZ","FBA","FBB", "FBC","FBD","FBE","FBF","FBG","FBH","FBI","FBJ","FBK","FBL","FBM","FBN","FBO","FBP","FBQ","FBR", "FBS","FBT","FBU","FBV","FBW","FBX","FBY","FBZ","FCA","FCB","FCC","FCD","FCE","FCF","FCG","FCH", "FCI","FCJ","FCK","FCL","FCM","FCN","FCO","FCP","FCQ","FCR","FCS","FCT","FCU","FCV","FCW","FCX", "FCY","FCZ","FDA","FDB","FDC","FDD","FDE","FDF","FDG","FDH","FDI","FDJ","FDK","FDL","FDM","FDN", "FDO","FDP","FDQ","FDR","FDS","FDT","FDU","FDV","FDW","FDX","FDY","FDZ","FEA","FEB","FEC","FED", "FEE","FEF","FEG","FEH","FEI","FEJ","FEK","FEL","FEM","FEN","FEO","FEP","FEQ","FER","FES","FET", "FEU","FEV","FEW","FEX","FEY","FEZ","FFA","FFB","FFC","FFD","FFE","FFF","FFG","FFH","FFI","FFJ", "FFK","FFL","FFM","FFN","FFO","FFP","FFQ","FFR","FFS","FFT","FFU","FFV","FFW","FFX","FFY","FFZ", "FGA","FGB","FGC","FGD","FGE","FGF","FGG","FGH","FGI","FGJ","FGK","FGL","FGM","FGN","FGO","FGP", "FGQ","FGR","FGS","FGT","FGU","FGV","FGW","FGX","FGY","FGZ","FHA","FHB","FHC","FHD","FHE","FHF", "FHG","FHH","FHI","FHJ","FHK","FHL","FHM","FHN","FHO","FHP","FHQ","FHR","FHS","FHT","FHU","FHV", "FHW","FHX","FHY","FHZ","FIA","FIB","FIC","FID","FIE","FIF","FIG","FIH","FII","FIJ","FIK","FIL", "FIM","FIN","FIO","FIP","FIQ","FIR","FIS","FIT","FIU","FIV","FIW","FIX","FIY","FIZ","FJA","FJB", "FJC","FJD","FJE","FJF","FJG","FJH","FJI","FJJ","FJK","FJL","FJM","FJN","FJO","FJP","FJQ","FJR", "FJS","FJT","FJU","FJV","FJW","FJX","FJY","FJZ","FKA","FKB","FKC","FKD","FKE","FKF","FKG","FKH", "FKI","FKJ","FKK","FKL","FKM","FKN","FKO","FKP","FKQ","FKR","FKS","FKT","FKU","FKV","FKW","FKX", "FKY","FKZ","FLA","FLB","FLC","FLD","FLE","FLF","FLG","FLH","FLI","FLJ","FLK","FLL","FLM","FLN", "FLO","FLP","FLQ","FLR","FLS","FLT","FLU","FLV","FLW","FLX","FLY","FLZ","FMA","FMB","FMC","FMD", "FME","FMF","FMG","FMH","FMI","FMJ","FMK","FML","FMM","FMN","FMO","FMP","FMQ","FMR","FMS","FMT", "FMU","FMV","FMW","FMX","FMY","FMZ","FNA","FNB","FNC","FND","FNE","FNF","FNG","FNH","FNI","FNJ", "FNK","FNL","FNM","FNN","FNO","FNP","FNQ","FNR","FNS","FNT","FNU","FNV","FNW","FNX","FNY","FNZ", "FOA","FOB","FOC","FOD","FOE","FOF","FOG","FOH","FOI","FOJ","FOK","FOL","FOM","FON","FOO","FOP", "FOQ","FOR","FOS","FOT","FOU","FOV","FOW","FOX","FOY","FOZ","FPA","FPB","FPC","FPD","FPE","FPF", "FPG","FPH","FPI","FPJ","FPK","FPL","FPM","FPN","FPO","FPP","FPQ","FPR","FPS","FPT","FPU","FPV", "FPW","FPX","FPY","FPZ","FQA","FQB","FQC","FQD","FQE","FQF","FQG","FQH","FQI","FQJ","FQK","FQL", "FQM","FQN","FQO","FQP","FQQ","FQR","FQS","FQT","FQU","FQV","FQW","FQX","FQY","FQZ","FRA","FRB", "FRC","FRD","FRE","FRF","FRG","FRH","FRI","FRJ","FRK","FRL","FRM","FRN","FRO","FRP","FRQ","FRR", "FRS","FRT","FRU","FRV","FRW","FRX","FRY","FRZ","FSA","FSB","FSC","FSD","FSE","FSF","FSG","FSH", "FSI","FSJ","FSK","FSL","FSM","FSN","FSO","FSP","FSQ","FSR","FSS","FST","FSU","FSV","FSW","FSX", "FSY","FSZ","FTA","FTB","FTC","FTD","FTE","FTF","FTG","FTH","FTI","FTJ","FTK","FTL","FTM","FTN", "FTO","FTP","FTQ","FTR","FTS","FTT","FTU","FTV","FTW","FTX","FTY","FTZ","FUA","FUB","FUC","FUD", "FUE","FUF","FUG","FUH","FUI","FUJ","FUK","FUL","FUM","FUN","FUO","FUP","FUQ","FUR","FUS","FUT", "FUU","FUV","FUW","FUX","FUY","FUZ","FVA","FVB","FVC","FVD","FVE","FVF","FVG","FVH","FVI","FVJ", "FVK","FVL","FVM","FVN","FVO","FVP","FVQ","FVR","FVS","FVT","FVU","FVV","FVW","FVX","FVY","FVZ", "FWA","FWB","FWC","FWD","FWE","FWF","FWG","FWH","FWI","FWJ","FWK","FWL","FWM","FWN","FWO","FWP", "FWQ","FWR","FWS","FWT","FWU","FWV","FWW","FWX","FWY","FWZ","FXA","FXB","FXC","FXD","FXE","FXF", "FXG","FXH","FXI","FXJ","FXK","FXL","FXM","FXN","FXO","FXP","FXQ","FXR","FXS","FXT","FXU","FXV", "FXW","FXX","FXY","FXZ","FYA","FYB","FYC","FYD","FYE","FYF","FYG","FYH","FYI","FYJ","FYK","FYL", "FYM","FYN","FYO","FYP","FYQ","FYR","FYS","FYT","FYU","FYV","FYW","FYX","FYY","FYZ","FZA","FZB", "FZC","FZD","FZE","FZF","FZG","FZH","FZI","FZJ","FZK","FZL","FZM","FZN","FZO","FZP","FZQ","FZR", "FZS","FZT","FZU","FZV","FZW","FZX","FZY","FZZ","GAA","GAB","GAC","GAD","GAE","GAF","GAG","GAH", "GAI","GAJ","GAK","GAL","GAM","GAN","GAO","GAP","GAQ","GAR","GAS","GAT","GAU","GAV","GAW","GAX", "GAY","GAZ","GBA","GBB","GBC","GBD","GBE","GBF","GBG","GBH","GBI","GBJ","GBK","GBL","GBM","GBN", "GBO","GBP","GBQ","GBR","GBS","GBT","GBU","GBV","GBW","GBX","GBY","GBZ","GCA","GCB","GCC","GCD", "GCE","GCF","GCG","GCH","GCI","GCJ","GCK","GCL","GCM","GCN","GCO","GCP","GCQ","GCR","GCS","GCT", "GCU","GCV","GCW","GCX","GCY","GCZ","GDA","GDB","GDC","GDD","GDE","GDF","GDG","GDH","GDI","GDJ", "GDK","GDL","GDM","GDN","GDO","GDP","GDQ","GDR","GDS","GDT","GDU","GDV","GDW","GDX","GDY","GDZ", "GEA","GEB","GEC","GED","GEE","GEF","GEG","GEH","GEI","GEJ","GEK","GEL","GEM","GEN","GEO","GEP", "GEQ","GER","GES","GET","GEU","GEV","GEW","GEX","GEY","GEZ","GFA","GFB","GFC","GFD","GFE","GFF", "GFG","GFH","GFI","GFJ","GFK","GFL","GFM","GFN","GFO","GFP","GFQ","GFR","GFS","GFT","GFU","GFV", "GFW","GFX","GFY","GFZ","GGA","GGB","GGC","GGD","GGE","GGF","GGG","GGH","GGI","GGJ","GGK","GGL", "GGM","GGN","GGO","GGP","GGQ","GGR","GGS","GGT","GGU","GGV","GGW","GGX","GGY","GGZ","GHA","GHB", "GHC","GHD","GHE","GHF","GHG","GHH","GHI","GHJ","GHK","GHL","GHM","GHN","GHO","GHP","GHQ","GHR", "GHS","GHT","GHU","GHV","GHW","GHX","GHY","GHZ","GIA","GIB","GIC","GID","GIE","GIF","GIG","GIH", "GII","GIJ","GIK","GIL","GIM","GIN","GIO","GIP","GIQ","GIR","GIS","GIT","GIU","GIV","GIW","GIX", "GIY","GIZ","GJA","GJB","GJC","GJD","GJE","GJF","GJG","GJH","GJI","GJJ","GJK","GJL","GJM","GJN", "GJO","GJP","GJQ","GJR","GJS","GJT","GJU","GJV","GJW","GJX","GJY","GJZ","GKA","GKB","GKC","GKD", "GKE","GKF","GKG","GKH","GKI","GKJ","GKK","GKL","GKM","GKN","GKO","GKP","GKQ","GKR","GKS","GKT", "GKU","GKV","GKW","GKX","GKY","GKZ","GLA","GLB","GLC","GLD","GLE","GLF","GLG","GLH","GLI","GLJ", "GLK","GLL","GLM","GLN","GLO","GLP","GLQ","GLR","GLS","GLT","GLU","GLV","GLW","GLX","GLY","GLZ", "GMA","GMB","GMC","GMD","GME","GMF","GMG","GMH","GMI","GMJ","GMK","GML","GMM","GMN","GMO","GMP", "GMQ","GMR","GMS","GMT","GMU","GMV","GMW","GMX","GMY","GMZ","GNA","GNB","GNC","GND","GNE","GNF", "GNG","GNH","GNI","GNJ","GNK","GNL","GNM","GNN","GNO","GNP","GNQ","GNR","GNS","GNT","GNU","GNV", "GNW","GNX","GNY","GNZ","GOA","GOB","GOC","GOD","GOE","GOF","GOG","GOH","GOI","GOJ","GOK","GOL", "GOM","GON","GOO","GOP","GOQ","GOR","GOS","GOT","GOU","GOV","GOW","GOX","GOY","GOZ","GPA","GPB", "GPC","GPD","GPE","GPF","GPG","GPH","GPI","GPJ","GPK","GPL","GPM","GPN","GPO","GPP","GPQ","GPR", "GPS","GPT","GPU","GPV","GPW","GPX","GPY","GPZ","GQA","GQB","GQC","GQD","GQE","GQF","GQG","GQH", "GQI","GQJ","GQK","GQL","GQM","GQN","GQO","GQP","GQQ","GQR","GQS","GQT","GQU","GQV","GQW","GQX", "GQY","GQZ","GRA","GRB","GRC","GRD","GRE","GRF","GRG","GRH","GRI","GRJ","GRK","GRL","GRM","GRN", "GRO","GRP","GRQ","GRR","GRS","GRT","GRU","GRV","GRW","GRX","GRY","GRZ","GSA","GSB","GSC","GSD", "GSE","GSF","GSG","GSH","GSI","GSJ","GSK","GSL","GSM","GSN","GSO","GSP","GSQ","GSR","GSS","GST", "GSU","GSV","GSW","GSX","GSY","GSZ","GTA","GTB","GTC","GTD","GTE","GTF","GTG","GTH","GTI","GTJ", "GTK","GTL","GTM","GTN","GTO","GTP","GTQ","GTR","GTS","GTT","GTU","GTV","GTW","GTX","GTY","GTZ", "GUA","GUB","GUC","GUD","GUE","GUF","GUG","GUH","GUI","GUJ","GUK","GUL","GUM","GUN","GUO","GUP", "GUQ","GUR","GUS","GUT","GUU","GUV","GUW","GUX","GUY","GUZ","GVA","GVB","GVC","GVD","GVE","GVF", "GVG","GVH","GVI","GVJ","GVK","GVL","GVM","GVN","GVO","GVP","GVQ","GVR","GVS","GVT","GVU","GVV", "GVW","GVX","GVY","GVZ","GWA","GWB","GWC","GWD","GWE","GWF","GWG","GWH","GWI","GWJ","GWK","GWL", "GWM","GWN","GWO","GWP","GWQ","GWR","GWS","GWT","GWU","GWV","GWW","GWX","GWY","GWZ","GXA","GXB", "GXC","GXD","GXE","GXF","GXG","GXH","GXI","GXJ","GXK","GXL","GXM","GXN","GXO","GXP","GXQ","GXR", "GXS","GXT","GXU","GXV","GXW","GXX","GXY","GXZ","GYA","GYB","GYC","GYD","GYE","GYF","GYG","GYH", "GYI","GYJ","GYK","GYL","GYM","GYN","GYO","GYP","GYQ","GYR","GYS","GYT","GYU","GYV","GYW","GYX", "GYY","GYZ","GZA","GZB","GZC","GZD","GZE","GZF","GZG","GZH","GZI","GZJ","GZK","GZL","GZM","GZN", "GZO","GZP","GZQ","GZR","GZS","GZT","GZU","GZV","GZW","GZX","GZY","GZZ","HAA","HAB","HAC","HAD", "HAE","HAF","HAG","HAH","HAI","HAJ","HAK","HAL","HAM","HAN","HAO","HAP","HAQ","HAR","HAS","HAT", "HAU","HAV","HAW","HAX","HAY","HAZ","HBA","HBB","HBC","HBD","HBE","HBF","HBG","HBH","HBI","HBJ", "HBK","HBL","HBM","HBN","HBO","HBP","HBQ","HBR","HBS","HBT","HBU","HBV","HBW","HBX","HBY","HBZ", "HCA","HCB","HCC","HCD","HCE","HCF","HCG","HCH","HCI","HCJ","HCK","HCL","HCM","HCN","HCO","HCP", "HCQ","HCR","HCS","HCT","HCU","HCV","HCW","HCX","HCY","HCZ","HDA","HDB","HDC","HDD","HDE","HDF", "HDG","HDH","HDI","HDJ","HDK","HDL","HDM","HDN","HDO","HDP","HDQ","HDR","HDS","HDT","HDU","HDV", "HDW","HDX","HDY","HDZ","HEA","HEB","HEC","HED","HEE","HEF","HEG","HEH","HEI","HEJ","HEK","HEL", "HEM","HEN","HEO","HEP","HEQ","HER","HES","HET","HEU","HEV","HEW","HEX","HEY","HEZ","HFA","HFB", "HFC","HFD","HFE","HFF","HFG","HFH","HFI","HFJ","HFK","HFL","HFM","HFN","HFO","HFP","HFQ","HFR", "HFS","HFT","HFU","HFV","HFW","HFX","HFY","HFZ","HGA","HGB","HGC","HGD","HGE","HGF","HGG","HGH", "HGI","HGJ","HGK","HGL","HGM","HGN","HGO","HGP","HGQ","HGR","HGS","HGT","HGU","HGV","HGW","HGX", "HGY","HGZ","HHA","HHB","HHC","HHD","HHE","HHF","HHG","HHH","HHI","HHJ","HHK","HHL","HHM","HHN", "HHO","HHP","HHQ","HHR","HHS","HHT","HHU","HHV","HHW","HHX","HHY","HHZ","HIA","HIB","HIC","HID", "HIE","HIF","HIG","HIH","HII","HIJ","HIK","HIL","HIM","HIN","HIO","HIP","HIQ","HIR","HIS","HIT", "HIU","HIV","HIW","HIX","HIY","HIZ","HJA","HJB","HJC","HJD","HJE","HJF","HJG","HJH","HJI","HJJ", "HJK","HJL","HJM","HJN","HJO","HJP","HJQ","HJR","HJS","HJT","HJU","HJV","HJW","HJX","HJY","HJZ", "HKA","HKB","HKC","HKD","HKE","HKF","HKG","HKH","HKI","HKJ","HKK","HKL","HKM","HKN","HKO","HKP", "HKQ","HKR","HKS","HKT","HKU","HKV","HKW","HKX","HKY","HKZ","HLA","HLB","HLC","HLD","HLE","HLF", "HLG","HLH","HLI","HLJ","HLK","HLL","HLM","HLN","HLO","HLP","HLQ","HLR","HLS","HLT","HLU","HLV", "HLW","HLX","HLY","HLZ","HMA","HMB","HMC","HMD","HME","HMF","HMG","HMH","HMI","HMJ","HMK","HML", "HMM","HMN","HMO","HMP","HMQ","HMR","HMS","HMT","HMU","HMV","HMW","HMX","HMY","HMZ","HNA","HNB", "HNC","HND","HNE","HNF","HNG","HNH","HNI","HNJ","HNK","HNL","HNM","HNN","HNO","HNP","HNQ","HNR", "HNS","HNT","HNU","HNV","HNW","HNX","HNY","HNZ","HOA","HOB","HOC","HOD","HOE","HOF","HOG","HOH", "HOI","HOJ","HOK","HOL","HOM","HON","HOO","HOP","HOQ","HOR","HOS","HOT","HOU","HOV","HOW","HOX", "HOY","HOZ","HPA","HPB","HPC","HPD","HPE","HPF","HPG","HPH","HPI","HPJ","HPK","HPL","HPM","HPN", "HPO","HPP","HPQ","HPR","HPS","HPT","HPU","HPV","HPW","HPX","HPY","HPZ","HQA","HQB","HQC","HQD", "HQE","HQF","HQG","HQH","HQI","HQJ","HQK","HQL","HQM","HQN","HQO","HQP","HQQ","HQR","HQS","HQT", "HQU","HQV","HQW","HQX","HQY","HQZ","HRA","HRB","HRC","HRD","HRE","HRF","HRG","HRH","HRI","HRJ", "HRK","HRL","HRM","HRN","HRO","HRP","HRQ","HRR","HRS","HRT","HRU","HRV","HRW","HRX","HRY","HRZ", "HSA","HSB","HSC","HSD","HSE","HSF","HSG","HSH","HSI","HSJ","HSK","HSL","HSM","HSN","HSO","HSP", "HSQ","HSR","HSS","HST","HSU","HSV","HSW","HSX","HSY","HSZ","HTA","HTB","HTC","HTD","HTE","HTF", "HTG","HTH","HTI","HTJ","HTK","HTL","HTM","HTN","HTO","HTP","HTQ","HTR","HTS","HTT","HTU","HTV", "HTW","HTX","HTY","HTZ","HUA","HUB","HUC","HUD","HUE","HUF","HUG","HUH","HUI","HUJ","HUK","HUL", "HUM","HUN","HUO","HUP","HUQ","HUR","HUS","HUT","HUU","HUV","HUW","HUX","HUY","HUZ","HVA","HVB", "HVC","HVD","HVE","HVF","HVG","HVH","HVI","HVJ","HVK","HVL","HVM","HVN","HVO","HVP","HVQ","HVR", "HVS","HVT","HVU","HVV","HVW","HVX","HVY","HVZ","HWA","HWB","HWC","HWD","HWE","HWF","HWG","HWH", "HWI","HWJ","HWK","HWL","HWM","HWN","HWO","HWP","HWQ","HWR","HWS","HWT","HWU","HWV","HWW","HWX", "HWY","HWZ","HXA","HXB","HXC","HXD","HXE","HXF","HXG","HXH","HXI","HXJ","HXK","HXL","HXM","HXN", "HXO","HXP","HXQ","HXR","HXS","HXT","HXU","HXV","HXW","HXX","HXY","HXZ","HYA","HYB","HYC","HYD", "HYE","HYF","HYG","HYH","HYI","HYJ","HYK","HYL","HYM","HYN","HYO","HYP","HYQ","HYR","HYS","HYT", "HYU","HYV","HYW","HYX","HYY","HYZ","HZA","HZB","HZC","HZD","HZE","HZF","HZG","HZH","HZI","HZJ", "HZK","HZL","HZM","HZN","HZO","HZP","HZQ","HZR","HZS","HZT","HZU","HZV","HZW","HZX","HZY","HZZ", "IAA","IAB","IAC","IAD","IAE","IAF","IAG","IAH","IAI","IAJ","IAK","IAL","IAM","IAN","IAO","IAP", "IAQ","IAR","IAS","IAT","IAU","IAV","IAW","IAX","IAY","IAZ","IBA","IBB","IBC","IBD","IBE","IBF", "IBG","IBH","IBI","IBJ","IBK","IBL","IBM","IBN","IBO","IBP","IBQ","IBR","IBS","IBT","IBU","IBV", "IBW","IBX","IBY","IBZ","ICA","ICB","ICC","ICD","ICE","ICF","ICG","ICH","ICI","ICJ","ICK","ICL", "ICM","ICN","ICO","ICP","ICQ","ICR","ICS","ICT","ICU","ICV","ICW","ICX","ICY","ICZ","IDA","IDB", "IDC","IDD","IDE","IDF","IDG","IDH","IDI","IDJ","IDK","IDL","IDM","IDN","IDO","IDP","IDQ","IDR", "IDS","IDT","IDU","IDV","IDW","IDX","IDY","IDZ","IEA","IEB","IEC","IED","IEE","IEF","IEG","IEH", "IEI","IEJ","IEK","IEL","IEM","IEN","IEO","IEP","IEQ","IER","IES","IET","IEU","IEV","IEW","IEX", "IEY","IEZ","IFA","IFB","IFC","IFD","IFE","IFF","IFG","IFH","IFI","IFJ","IFK","IFL","IFM","IFN", "IFO","IFP","IFQ","IFR","IFS","IFT","IFU","IFV","IFW","IFX","IFY","IFZ","IGA","IGB","IGC","IGD", "IGE","IGF","IGG","IGH","IGI","IGJ","IGK","IGL","IGM","IGN","IGO","IGP","IGQ","IGR","IGS","IGT", "IGU","IGV","IGW","IGX","IGY","IGZ","IHA","IHB","IHC","IHD","IHE","IHF","IHG","IHH","IHI","IHJ", "IHK","IHL","IHM","IHN","IHO","IHP","IHQ","IHR","IHS","IHT","IHU","IHV","IHW","IHX","IHY","IHZ", "IIA","IIB","IIC","IID","IIE","IIF","IIG","IIH","III","IIJ","IIK","IIL","IIM","IIN","IIO","IIP", "IIQ","IIR","IIS","IIT","IIU","IIV","IIW","IIX","IIY","IIZ","IJA","IJB","IJC","IJD","IJE","IJF", "IJG","IJH","IJI","IJJ","IJK","IJL","IJM","IJN","IJO","IJP","IJQ","IJR","IJS","IJT","IJU","IJV", "IJW","IJX","IJY","IJZ","IKA","IKB","IKC","IKD","IKE","IKF","IKG","IKH","IKI","IKJ","IKK","IKL", "IKM","IKN","IKO","IKP","IKQ","IKR","IKS","IKT","IKU","IKV","IKW","IKX","IKY","IKZ","ILA","ILB", "ILC","ILD","ILE","ILF","ILG","ILH","ILI","ILJ","ILK","ILL","ILM","ILN","ILO","ILP","ILQ","ILR", "ILS","ILT","ILU","ILV","ILW","ILX","ILY","ILZ","IMA","IMB","IMC","IMD","IME","IMF","IMG","IMH", "IMI","IMJ","IMK","IML","IMM","IMN","IMO","IMP","IMQ","IMR","IMS","IMT","IMU","IMV","IMW","IMX", "IMY","IMZ","INA","INB","INC","IND","INE","INF","ING","INH","INI","INJ","INK","INL","INM","INN", "INO","INP","INQ","INR","INS","INT","INU","INV","INW","INX","INY","INZ","IOA","IOB","IOC","IOD", "IOE","IOF","IOG","IOH","IOI","IOJ","IOK","IOL","IOM","ION","IOO","IOP","IOQ","IOR","IOS","IOT", "IOU","IOV","IOW","IOX","IOY","IOZ","IPA","IPB","IPC","IPD","IPE","IPF","IPG","IPH","IPI","IPJ", "IPK","IPL","IPM","IPN","IPO","IPP","IPQ","IPR","IPS","IPT","IPU","IPV","IPW","IPX","IPY","IPZ", "IQA","IQB","IQC","IQD","IQE","IQF","IQG","IQH","IQI","IQJ","IQK","IQL","IQM","IQN","IQO","IQP", "IQQ","IQR","IQS","IQT","IQU","IQV","IQW","IQX","IQY","IQZ","IRA","IRB","IRC","IRD","IRE","IRF", "IRG","IRH","IRI","IRJ","IRK","IRL","IRM","IRN","IRO","IRP","IRQ","IRR","IRS","IRT","IRU","IRV", "IRW","IRX","IRY","IRZ","ISA","ISB","ISC","ISD","ISE","ISF","ISG","ISH","ISI","ISJ","ISK","ISL", "ISM","ISN","ISO","ISP","ISQ","ISR","ISS","IST","ISU","ISV","ISW","ISX","ISY","ISZ","ITA","ITB", "ITC","ITD","ITE","ITF","ITG","ITH","ITI","ITJ","ITK","ITL","ITM","ITN","ITO","ITP","ITQ","ITR", "ITS","ITT","ITU","ITV","ITW","ITX","ITY","ITZ","IUA","IUB","IUC","IUD","IUE","IUF","IUG","IUH", "IUI","IUJ","IUK","IUL","IUM","IUN","IUO","IUP","IUQ","IUR","IUS","IUT","IUU","IUV","IUW","IUX", "IUY","IUZ","IVA","IVB","IVC","IVD","IVE","IVF","IVG","IVH","IVI","IVJ","IVK","IVL","IVM","IVN", "IVO","IVP","IVQ","IVR","IVS","IVT","IVU","IVV","IVW","IVX","IVY","IVZ","IWA","IWB","IWC","IWD", "IWE","IWF","IWG","IWH","IWI","IWJ","IWK","IWL","IWM","IWN","IWO","IWP","IWQ","IWR","IWS","IWT", "IWU","IWV","IWW","IWX","IWY","IWZ","IXA","IXB","IXC","IXD","IXE","IXF","IXG","IXH","IXI","IXJ", "IXK","IXL","IXM","IXN","IXO","IXP","IXQ","IXR","IXS","IXT","IXU","IXV","IXW","IXX","IXY","IXZ", "IYA","IYB","IYC","IYD","IYE","IYF","IYG","IYH","IYI","IYJ","IYK","IYL","IYM","IYN","IYO","IYP", "IYQ","IYR","IYS","IYT","IYU","IYV","IYW","IYX","IYY","IYZ","IZA","IZB","IZC","IZD","IZE","IZF", "IZG","IZH","IZI","IZJ","IZK","IZL","IZM","IZN","IZO","IZP","IZQ","IZR","IZS","IZT","IZU","IZV", "IZW","IZX","IZY","IZZ","JAA","JAB","JAC","JAD","JAE","JAF","JAG","JAH","JAI","JAJ","JAK","JAL", "JAM","JAN","JAO","JAP","JAQ","JAR","JAS","JAT","JAU","JAV","JAW","JAX","JAY","JAZ","JBA","JBB", "JBC","JBD","JBE","JBF","JBG","JBH","JBI","JBJ","JBK","JBL","JBM","JBN","JBO","JBP","JBQ","JBR", "JBS","JBT","JBU","JBV","JBW","JBX","JBY","JBZ","JCA","JCB","JCC","JCD","JCE","JCF","JCG","JCH", "JCI","JCJ","JCK","JCL","JCM","JCN","JCO","JCP","JCQ","JCR","JCS","JCT","JCU","JCV","JCW","JCX", "JCY","JCZ","JDA","JDB","JDC","JDD","JDE","JDF","JDG","JDH","JDI","JDJ","JDK","JDL","JDM","JDN", "JDO","JDP","JDQ","JDR","JDS","JDT","JDU","JDV","JDW","JDX","JDY","JDZ","JEA","JEB","JEC","JED", "JEE","JEF","JEG","JEH","JEI","JEJ","JEK","JEL","JEM","JEN","JEO","JEP","JEQ","JER","JES","JET", "JEU","JEV","JEW","JEX","JEY","JEZ","JFA","JFB","JFC","JFD","JFE","JFF","JFG","JFH","JFI","JFJ", "JFK","JFL","JFM","JFN","JFO","JFP","JFQ","JFR","JFS","JFT","JFU","JFV","JFW","JFX","JFY","JFZ", "JGA","JGB","JGC","JGD","JGE","JGF","JGG","JGH","JGI","JGJ","JGK","JGL","JGM","JGN","JGO","JGP", "JGQ","JGR","JGS","JGT","JGU","JGV","JGW","JGX","JGY","JGZ","JHA","JHB","JHC","JHD","JHE","JHF", "JHG","JHH","JHI","JHJ","JHK","JHL","JHM","JHN","JHO","JHP","JHQ","JHR","JHS","JHT","JHU","JHV", "JHW","JHX","JHY","JHZ","JIA","JIB","JIC","JID","JIE","JIF","JIG","JIH","JII","JIJ","JIK","JIL", "JIM","JIN","JIO","JIP","JIQ","JIR","JIS","JIT","JIU","JIV","JIW","JIX","JIY","JIZ","JJA","JJB", "JJC","JJD","JJE","JJF","JJG","JJH","JJI","JJJ","JJK","JJL","JJM","JJN","JJO","JJP","JJQ","JJR", "JJS","JJT","JJU","JJV","JJW","JJX","JJY","JJZ","JKA","JKB","JKC","JKD","JKE","JKF","JKG","JKH", "JKI","JKJ","JKK","JKL","JKM","JKN","JKO","JKP","JKQ","JKR","JKS","JKT","JKU","JKV","JKW","JKX", "JKY","JKZ","JLA","JLB","JLC","JLD","JLE","JLF","JLG","JLH","JLI","JLJ","JLK","JLL","JLM","JLN", "JLO","JLP","JLQ","JLR","JLS","JLT","JLU","JLV","JLW","JLX","JLY","JLZ","JMA","JMB","JMC","JMD", "JME","JMF","JMG","JMH","JMI","JMJ","JMK","JML","JMM","JMN","JMO","JMP","JMQ","JMR","JMS","JMT", "JMU","JMV","JMW","JMX","JMY","JMZ","JNA","JNB","JNC","JND","JNE","JNF","JNG","JNH","JNI","JNJ", "JNK","JNL","JNM","JNN","JNO","JNP","JNQ","JNR","JNS","JNT","JNU","JNV","JNW","JNX","JNY","JNZ", "JOA","JOB","JOC","JOD","JOE","JOF","JOG","JOH","JOI","JOJ","JOK","JOL","JOM","JON","JOO","JOP", "JOQ","JOR","JOS","JOT","JOU","JOV","JOW","JOX","JOY","JOZ","JPA","JPB","JPC","JPD","JPE","JPF", "JPG","JPH","JPI","JPJ","JPK","JPL","JPM","JPN","JPO","JPP","JPQ","JPR","JPS","JPT","JPU","JPV", "JPW","JPX","JPY","JPZ","JQA","JQB","JQC","JQD","JQE","JQF","JQG","JQH","JQI","JQJ","JQK","JQL", "JQM","JQN","JQO","JQP","JQQ","JQR","JQS","JQT","JQU","JQV","JQW","JQX","JQY","JQZ","JRA","JRB", "JRC","JRD","JRE","JRF","JRG","JRH","JRI","JRJ","JRK","JRL","JRM","JRN","JRO","JRP","JRQ","JRR", "JRS","JRT","JRU","JRV","JRW","JRX","JRY","JRZ","JSA","JSB","JSC","JSD","JSE","JSF","JSG","JSH", "JSI","JSJ","JSK","JSL","JSM","JSN","JSO","JSP","JSQ","JSR","JSS","JST","JSU","JSV","JSW","JSX", "JSY","JSZ","JTA","JTB","JTC","JTD","JTE","JTF","JTG","JTH","JTI","JTJ","JTK","JTL","JTM","JTN", "JTO","JTP","JTQ","JTR","JTS","JTT","JTU","JTV","JTW","JTX","JTY","JTZ","JUA","JUB","JUC","JUD", "JUE","JUF","JUG","JUH","JUI","JUJ","JUK","JUL","JUM","JUN","JUO","JUP","JUQ","JUR","JUS","JUT", "JUU","JUV","JUW","JUX","JUY","JUZ","JVA","JVB","JVC","JVD","JVE","JVF","JVG","JVH","JVI","JVJ", "JVK","JVL","JVM","JVN","JVO","JVP","JVQ","JVR","JVS","JVT","JVU","JVV","JVW","JVX","JVY","JVZ", "JWA","JWB","JWC","JWD","JWE","JWF","JWG","JWH","JWI","JWJ","JWK","JWL","JWM","JWN","JWO","JWP", "JWQ","JWR","JWS","JWT","JWU","JWV","JWW","JWX","JWY","JWZ","JXA","JXB","JXC","JXD","JXE","JXF", "JXG","JXH","JXI","JXJ","JXK","JXL","JXM","JXN","JXO","JXP","JXQ","JXR","JXS","JXT","JXU","JXV", "JXW","JXX","JXY","JXZ","JYA","JYB","JYC","JYD","JYE","JYF","JYG","JYH","JYI","JYJ","JYK","JYL", "JYM","JYN","JYO","JYP","JYQ","JYR","JYS","JYT","JYU","JYV","JYW","JYX","JYY","JYZ","JZA","JZB", "JZC","JZD","JZE","JZF","JZG","JZH","JZI","JZJ","JZK","JZL","JZM","JZN","JZO","JZP","JZQ","JZR", "JZS","JZT","JZU","JZV","JZW","JZX","JZY","JZZ","KAA","KAB","KAC","KAD","KAE","KAF","KAG","KAH", "KAI","KAJ","KAK","KAL","KAM","KAN","KAO","KAP","KAQ","KAR","KAS","KAT","KAU","KAV","KAW","KAX", "KAY","KAZ","KBA","KBB","KBC","KBD","KBE","KBF","KBG","KBH","KBI","KBJ","KBK","KBL","KBM","KBN", "KBO","KBP","KBQ","KBR","KBS","KBT","KBU","KBV","KBW","KBX","KBY","KBZ","KCA","KCB","KCC","KCD", "KCE","KCF","KCG","KCH","KCI","KCJ","KCK","KCL","KCM","KCN","KCO","KCP","KCQ","KCR","KCS","KCT", "KCU","KCV","KCW","KCX","KCY","KCZ","KDA","KDB","KDC","KDD","KDE","KDF","KDG","KDH","KDI","KDJ", "KDK","KDL","KDM","KDN","KDO","KDP","KDQ","KDR","KDS","KDT","KDU","KDV","KDW","KDX","KDY","KDZ", "KEA","KEB","KEC","KED","KEE","KEF","KEG","KEH","KEI","KEJ","KEK","KEL","KEM","KEN","KEO","KEP", "KEQ","KER","KES","KET","KEU","KEV","KEW","KEX","KEY","KEZ","KFA","KFB","KFC","KFD","KFE","KFF", "KFG","KFH","KFI","KFJ","KFK","KFL","KFM","KFN","KFO","KFP","KFQ","KFR","KFS","KFT","KFU","KFV", "KFW","KFX","KFY","KFZ","KGA","KGB","KGC","KGD","KGE","KGF","KGG","KGH","KGI","KGJ","KGK","KGL", "KGM","KGN","KGO","KGP","KGQ","KGR","KGS","KGT","KGU","KGV","KGW","KGX","KGY","KGZ","KHA","KHB", "KHC","KHD","KHE","KHF","KHG","KHH","KHI","KHJ","KHK","KHL","KHM","KHN","KHO","KHP","KHQ","KHR", "KHS","KHT","KHU","KHV","KHW","KHX","KHY","KHZ","KIA","KIB","KIC","KID","KIE","KIF","KIG","KIH", "KII","KIJ","KIK","KIL","KIM","KIN","KIO","KIP","KIQ","KIR","KIS","KIT","KIU","KIV","KIW","KIX", "KIY","KIZ","KJA","KJB","KJC","KJD","KJE","KJF","KJG","KJH","KJI","KJJ","KJK","KJL","KJM","KJN", "KJO","KJP","KJQ","KJR","KJS","KJT","KJU","KJV","KJW","KJX","KJY","KJZ","KKA","KKB","KKC","KKD", "KKE","KKF","KKG","KKH","KKI","KKJ","KKK","KKL","KKM","KKN","KKO","KKP","KKQ","KKR","KKS","KKT", "KKU","KKV","KKW","KKX","KKY","KKZ","KLA","KLB","KLC","KLD","KLE","KLF","KLG","KLH","KLI","KLJ", "KLK","KLL","KLM","KLN","KLO","KLP","KLQ","KLR","KLS","KLT","KLU","KLV","KLW","KLX","KLY","KLZ", "KMA","KMB","KMC","KMD","KME","KMF","KMG","KMH","KMI","KMJ","KMK","KML","KMM","KMN","KMO","KMP", "KMQ","KMR","KMS","KMT","KMU","KMV","KMW","KMX","KMY","KMZ","KNA","KNB","KNC","KND","KNE","KNF", "KNG","KNH","KNI","KNJ","KNK","KNL","KNM","KNN","KNO","KNP","KNQ","KNR","KNS","KNT","KNU","KNV", "KNW","KNX","KNY","KNZ","KOA","KOB","KOC","KOD","KOE","KOF","KOG","KOH","KOI","KOJ","KOK","KOL", "KOM","KON","KOO","KOP","KOQ","KOR","KOS","KOT","KOU","KOV","KOW","KOX","KOY","KOZ","KPA","KPB", "KPC","KPD","KPE","KPF","KPG","KPH","KPI","KPJ","KPK","KPL","KPM","KPN","KPO","KPP","KPQ","KPR", "KPS","KPT","KPU","KPV","KPW","KPX","KPY","KPZ","KQA","KQB","KQC","KQD","KQE","KQF","KQG","KQH", "KQI","KQJ","KQK","KQL","KQM","KQN","KQO","KQP","KQQ","KQR","KQS","KQT","KQU","KQV","KQW","KQX", "KQY","KQZ","KRA","KRB","KRC","KRD","KRE","KRF","KRG","KRH","KRI","KRJ","KRK","KRL","KRM","KRN", "KRO","KRP","KRQ","KRR","KRS","KRT","KRU","KRV","KRW","KRX","KRY","KRZ","KSA","KSB","KSC","KSD", "KSE","KSF","KSG","KSH","KSI","KSJ","KSK","KSL","KSM","KSN","KSO","KSP","KSQ","KSR","KSS","KST", "KSU","KSV","KSW","KSX","KSY","KSZ","KTA","KTB","KTC","KTD","KTE","KTF","KTG","KTH","KTI","KTJ", "KTK","KTL","KTM","KTN","KTO","KTP","KTQ","KTR","KTS","KTT","KTU","KTV","KTW","KTX","KTY","KTZ", "KUA","KUB","KUC","KUD","KUE","KUF","KUG","KUH","KUI","KUJ","KUK","KUL","KUM","KUN","KUO","KUP", "KUQ","KUR","KUS","KUT","KUU","KUV","KUW","KUX","KUY","KUZ","KVA","KVB","KVC","KVD","KVE","KVF", "KVG","KVH","KVI","KVJ","KVK","KVL","KVM","KVN","KVO","KVP","KVQ","KVR","KVS","KVT","KVU","KVV", "KVW","KVX","KVY","KVZ","KWA","KWB","KWC","KWD","KWE","KWF","KWG","KWH","KWI","KWJ","KWK","KWL", "KWM","KWN","KWO","KWP","KWQ","KWR","KWS","KWT","KWU","KWV","KWW","KWX","KWY","KWZ","KXA","KXB", "KXC","KXD","KXE","KXF","KXG","KXH","KXI","KXJ","KXK","KXL","KXM","KXN","KXO","KXP","KXQ","KXR", "KXS","KXT","KXU","KXV","KXW","KXX","KXY","KXZ","KYA","KYB","KYC","KYD","KYE","KYF","KYG","KYH", "KYI","KYJ","KYK","KYL","KYM","KYN","KYO","KYP","KYQ","KYR","KYS","KYT","KYU","KYV","KYW","KYX", "KYY","KYZ","KZA","KZB","KZC","KZD","KZE","KZF","KZG","KZH","KZI","KZJ","KZK","KZL","KZM","KZN", "KZO","KZP","KZQ","KZR","KZS","KZT","KZU","KZV","KZW","KZX","KZY","KZZ","LAA","LAB","LAC","LAD", "LAE","LAF","LAG","LAH","LAI","LAJ","LAK","LAL","LAM","LAN","LAO","LAP","LAQ","LAR","LAS","LAT", "LAU","LAV","LAW","LAX","LAY","LAZ","LBA","LBB","LBC","LBD","LBE","LBF","LBG","LBH","LBI","LBJ", "LBK","LBL","LBM","LBN","LBO","LBP","LBQ","LBR","LBS","LBT","LBU","LBV","LBW","LBX","LBY","LBZ", "LCA","LCB","LCC","LCD","LCE","LCF","LCG","LCH","LCI","LCJ","LCK","LCL","LCM","LCN","LCO","LCP", "LCQ","LCR","LCS","LCT","LCU","LCV","LCW","LCX","LCY","LCZ","LDA","LDB","LDC","LDD","LDE","LDF", "LDG","LDH","LDI","LDJ","LDK","LDL","LDM","LDN","LDO","LDP","LDQ","LDR","LDS","LDT","LDU","LDV", "LDW","LDX","LDY","LDZ","LEA","LEB","LEC","LED","LEE","LEF","LEG","LEH","LEI","LEJ","LEK","LEL", "LEM","LEN","LEO","LEP","LEQ","LER","LES","LET","LEU","LEV","LEW","LEX","LEY","LEZ","LFA","LFB", "LFC","LFD","LFE","LFF","LFG","LFH","LFI","LFJ","LFK","LFL","LFM","LFN","LFO","LFP","LFQ","LFR", "LFS","LFT","LFU","LFV","LFW","LFX","LFY","LFZ","LGA","LGB","LGC","LGD","LGE","LGF","LGG","LGH", "LGI","LGJ","LGK","LGL","LGM","LGN","LGO","LGP","LGQ","LGR","LGS","LGT","LGU","LGV","LGW","LGX", "LGY","LGZ","LHA","LHB","LHC","LHD","LHE","LHF","LHG","LHH","LHI","LHJ","LHK","LHL","LHM","LHN", "LHO","LHP","LHQ","LHR","LHS","LHT","LHU","LHV","LHW","LHX","LHY","LHZ","LIA","LIB","LIC","LID", "LIE","LIF","LIG","LIH","LII","LIJ","LIK","LIL","LIM","LIN","LIO","LIP","LIQ","LIR","LIS","LIT", "LIU","LIV","LIW","LIX","LIY","LIZ","LJA","LJB","LJC","LJD","LJE","LJF","LJG","LJH","LJI","LJJ", "LJK","LJL","LJM","LJN","LJO","LJP","LJQ","LJR","LJS","LJT","LJU","LJV","LJW","LJX","LJY","LJZ", "LKA","LKB","LKC","LKD","LKE","LKF","LKG","LKH","LKI","LKJ","LKK","LKL","LKM","LKN","LKO","LKP", "LKQ","LKR","LKS","LKT","LKU","LKV","LKW","LKX","LKY","LKZ","LLA","LLB","LLC","LLD","LLE","LLF", "LLG","LLH","LLI","LLJ","LLK","LLL","LLM","LLN","LLO","LLP","LLQ","LLR","LLS","LLT","LLU","LLV", "LLW","LLX","LLY","LLZ","LMA","LMB","LMC","LMD","LME","LMF","LMG","LMH","LMI","LMJ","LMK","LML", "LMM","LMN","LMO","LMP","LMQ","LMR","LMS","LMT","LMU","LMV","LMW","LMX","LMY","LMZ","LNA","LNB", "LNC","LND","LNE","LNF","LNG","LNH","LNI","LNJ","LNK","LNL","LNM","LNN","LNO","LNP","LNQ","LNR", "LNS","LNT","LNU","LNV","LNW","LNX","LNY","LNZ","LOA","LOB","LOC","LOD","LOE","LOF","LOG","LOH", "LOI","LOJ","LOK","LOL","LOM","LON","LOO","LOP","LOQ","LOR","LOS","LOT","LOU","LOV","LOW","LOX", "LOY","LOZ","LPA","LPB","LPC","LPD","LPE","LPF","LPG","LPH","LPI","LPJ","LPK","LPL","LPM","LPN", "LPO","LPP","LPQ","LPR","LPS","LPT","LPU","LPV","LPW","LPX","LPY","LPZ","LQA","LQB","LQC","LQD", "LQE","LQF","LQG","LQH","LQI","LQJ","LQK","LQL","LQM","LQN","LQO","LQP","LQQ","LQR","LQS","LQT", "LQU","LQV","LQW","LQX","LQY","LQZ","LRA","LRB","LRC","LRD","LRE","LRF","LRG","LRH","LRI","LRJ", "LRK","LRL","LRM","LRN","LRO","LRP","LRQ","LRR","LRS","LRT","LRU","LRV","LRW","LRX","LRY","LRZ", "LSA","LSB","LSC","LSD","LSE","LSF","LSG","LSH","LSI","LSJ","LSK","LSL","LSM","LSN","LSO","LSP", "LSQ","LSR","LSS","LST","LSU","LSV","LSW","LSX","LSY","LSZ","LTA","LTB","LTC","LTD","LTE","LTF", "LTG","LTH","LTI","LTJ","LTK","LTL","LTM","LTN","LTO","LTP","LTQ","LTR","LTS","LTT","LTU","LTV", "LTW","LTX","LTY","LTZ","LUA","LUB","LUC","LUD","LUE","LUF","LUG","LUH","LUI","LUJ","LUK","LUL", "LUM","LUN","LUO","LUP","LUQ","LUR","LUS","LUT","LUU","LUV","LUW","LUX","LUY","LUZ","LVA","LVB", "LVC","LVD","LVE","LVF","LVG","LVH","LVI","LVJ","LVK","LVL","LVM","LVN","LVO","LVP","LVQ","LVR", "LVS","LVT","LVU","LVV","LVW","LVX","LVY","LVZ","LWA","LWB","LWC","LWD","LWE","LWF","LWG","LWH", "LWI","LWJ","LWK","LWL","LWM","LWN","LWO","LWP","LWQ","LWR","LWS","LWT","LWU","LWV","LWW","LWX", "LWY","LWZ","LXA","LXB","LXC","LXD","LXE","LXF","LXG","LXH","LXI","LXJ","LXK","LXL","LXM","LXN", "LXO","LXP","LXQ","LXR","LXS","LXT","LXU","LXV","LXW","LXX","LXY","LXZ","LYA","LYB","LYC","LYD", "LYE","LYF","LYG","LYH","LYI","LYJ","LYK","LYL","LYM","LYN","LYO","LYP","LYQ","LYR","LYS","LYT", "LYU","LYV","LYW","LYX","LYY","LYZ","LZA","LZB","LZC","LZD","LZE","LZF","LZG","LZH","LZI","LZJ", "LZK","LZL","LZM","LZN","LZO","LZP","LZQ","LZR","LZS","LZT","LZU","LZV","LZW","LZX","LZY","LZZ", "MAA","MAB","MAC","MAD","MAE","MAF","MAG","MAH","MAI","MAJ","MAK","MAL","MAM","MAN","MAO","MAP", "MAQ","MAR","MAS","MAT","MAU","MAV","MAW","MAX","MAY","MAZ","MBA","MBB","MBC","MBD","MBE","MBF", "MBG","MBH","MBI","MBJ","MBK","MBL","MBM","MBN","MBO","MBP","MBQ","MBR","MBS","MBT","MBU","MBV", "MBW","MBX","MBY","MBZ","MCA","MCB","MCC","MCD","MCE","MCF","MCG","MCH","MCI","MCJ","MCK","MCL", "MCM","MCN","MCO","MCP","MCQ","MCR","MCS","MCT","MCU","MCV","MCW","MCX","MCY","MCZ","MDA","MDB", "MDC","MDD","MDE","MDF","MDG","MDH","MDI","MDJ","MDK","MDL","MDM","MDN","MDO","MDP","MDQ","MDR", "MDS","MDT","MDU","MDV","MDW","MDX","MDY","MDZ","MEA","MEB","MEC","MED","MEE","MEF","MEG","MEH", "MEI","MEJ","MEK","MEL","MEM","MEN","MEO","MEP","MEQ","MER","MES","MET","MEU","MEV","MEW","MEX", "MEY","MEZ","MFA","MFB","MFC","MFD","MFE","MFF","MFG","MFH","MFI","MFJ","MFK","MFL","MFM","MFN", "MFO","MFP","MFQ","MFR","MFS","MFT","MFU","MFV","MFW","MFX","MFY","MFZ","MGA","MGB","MGC","MGD", "MGE","MGF","MGG","MGH","MGI","MGJ","MGK","MGL","MGM","MGN","MGO","MGP","MGQ","MGR","MGS","MGT", "MGU","MGV","MGW","MGX","MGY","MGZ","MHA","MHB","MHC","MHD","MHE","MHF","MHG","MHH","MHI","MHJ", "MHK","MHL","MHM","MHN","MHO","MHP","MHQ","MHR","MHS","MHT","MHU","MHV","MHW","MHX","MHY","MHZ", "MIA","MIB","MIC","MID","MIE","MIF","MIG","MIH","MII","MIJ","MIK","MIL","MIM","MIN","MIO","MIP", "MIQ","MIR","MIS","MIT","MIU","MIV","MIW","MIX","MIY","MIZ","MJA","MJB","MJC","MJD","MJE","MJF", "MJG","MJH","MJI","MJJ","MJK","MJL","MJM","MJN","MJO","MJP","MJQ","MJR","MJS","MJT","MJU","MJV", "MJW","MJX","MJY","MJZ","MKA","MKB","MKC","MKD","MKE","MKF","MKG","MKH","MKI","MKJ","MKK","MKL", "MKM","MKN","MKO","MKP","MKQ","MKR","MKS","MKT","MKU","MKV","MKW","MKX","MKY","MKZ","MLA","MLB", "MLC","MLD","MLE","MLF","MLG","MLH","MLI","MLJ","MLK","MLL","MLM","MLN","MLO","MLP","MLQ","MLR", "MLS","MLT","MLU","MLV","MLW","MLX","MLY","MLZ","MMA","MMB","MMC","MMD","MME","MMF","MMG","MMH", "MMI","MMJ","MMK","MML","MMM","MMN","MMO","MMP","MMQ","MMR","MMS","MMT","MMU","MMV","MMW","MMX", "MMY","MMZ","MNA","MNB","MNC","MND","MNE","MNF","MNG","MNH","MNI","MNJ","MNK","MNL","MNM","MNN", "MNO","MNP","MNQ","MNR","MNS","MNT","MNU","MNV","MNW","MNX","MNY","MNZ","MOA","MOB","MOC","MOD", "MOE","MOF","MOG","MOH","MOI","MOJ","MOK","MOL","MOM","MON","MOO","MOP","MOQ","MOR","MOS","MOT", "MOU","MOV","MOW","MOX","MOY","MOZ","MPA","MPB","MPC","MPD","MPE","MPF","MPG","MPH","MPI","MPJ", "MPK","MPL","MPM","MPN","MPO","MPP","MPQ","MPR","MPS","MPT","MPU","MPV","MPW","MPX","MPY","MPZ", "MQA","MQB","MQC","MQD","MQE","MQF","MQG","MQH","MQI","MQJ","MQK","MQL","MQM","MQN","MQO","MQP", "MQQ","MQR","MQS","MQT","MQU","MQV","MQW","MQX","MQY","MQZ","MRA","MRB","MRC","MRD","MRE","MRF", "MRG","MRH","MRI","MRJ","MRK","MRL","MRM","MRN","MRO","MRP","MRQ","MRR","MRS","MRT","MRU","MRV", "MRW","MRX","MRY","MRZ","MSA","MSB","MSC","MSD","MSE","MSF","MSG","MSH","MSI","MSJ","MSK","MSL", "MSM","MSN","MSO","MSP","MSQ","MSR","MSS","MST","MSU","MSV","MSW","MSX","MSY","MSZ","MTA","MTB", "MTC","MTD","MTE","MTF","MTG","MTH","MTI","MTJ","MTK","MTL","MTM","MTN","MTO","MTP","MTQ","MTR", "MTS","MTT","MTU","MTV","MTW","MTX","MTY","MTZ","MUA","MUB","MUC","MUD","MUE","MUF","MUG","MUH", "MUI","MUJ","MUK","MUL","MUM","MUN","MUO","MUP","MUQ","MUR","MUS","MUT","MUU","MUV","MUW","MUX", "MUY","MUZ","MVA","MVB","MVC","MVD","MVE","MVF","MVG","MVH","MVI","MVJ","MVK","MVL","MVM","MVN", "MVO","MVP","MVQ","MVR","MVS","MVT","MVU","MVV","MVW","MVX","MVY","MVZ","MWA","MWB","MWC","MWD", "MWE","MWF","MWG","MWH","MWI","MWJ","MWK","MWL","MWM","MWN","MWO","MWP","MWQ","MWR","MWS","MWT", "MWU","MWV","MWW","MWX","MWY","MWZ","MXA","MXB","MXC","MXD","MXE","MXF","MXG","MXH","MXI","MXJ", "MXK","MXL","MXM","MXN","MXO","MXP","MXQ","MXR","MXS","MXT","MXU","MXV","MXW","MXX","MXY","MXZ", "MYA","MYB","MYC","MYD","MYE","MYF","MYG","MYH","MYI","MYJ","MYK","MYL","MYM","MYN","MYO","MYP", "MYQ","MYR","MYS","MYT","MYU","MYV","MYW","MYX","MYY","MYZ","MZA","MZB","MZC","MZD","MZE","MZF", "MZG","MZH","MZI","MZJ","MZK","MZL","MZM","MZN","MZO","MZP","MZQ","MZR","MZS","MZT","MZU","MZV", "MZW","MZX","MZY","MZZ","NAA","NAB","NAC","NAD","NAE","NAF","NAG","NAH","NAI","NAJ","NAK","NAL", "NAM","NAN","NAO","NAP","NAQ","NAR","NAS","NAT","NAU","NAV","NAW","NAX","NAY","NAZ","NBA","NBB", "NBC","NBD","NBE","NBF","NBG","NBH","NBI","NBJ","NBK","NBL","NBM","NBN","NBO","NBP","NBQ","NBR", "NBS","NBT","NBU","NBV","NBW","NBX","NBY","NBZ","NCA","NCB","NCC","NCD","NCE","NCF","NCG","NCH", "NCI","NCJ","NCK","NCL","NCM","NCN","NCO","NCP","NCQ","NCR","NCS","NCT","NCU","NCV","NCW","NCX", "NCY","NCZ","NDA","NDB","NDC","NDD","NDE","NDF","NDG","NDH","NDI","NDJ","NDK","NDL","NDM","NDN", "NDO","NDP","NDQ","NDR","NDS","NDT","NDU","NDV","NDW","NDX","NDY","NDZ","NEA","NEB","NEC","NED", "NEE","NEF","NEG","NEH","NEI","NEJ","NEK","NEL","NEM","NEN","NEO","NEP","NEQ","NER","NES","NET", "NEU","NEV","NEW","NEX","NEY","NEZ","NFA","NFB","NFC","NFD","NFE","NFF","NFG","NFH","NFI","NFJ", "NFK","NFL","NFM","NFN","NFO","NFP","NFQ","NFR","NFS","NFT","NFU","NFV","NFW","NFX","NFY","NFZ", "NGA","NGB","NGC","NGD","NGE","NGF","NGG","NGH","NGI","NGJ","NGK","NGL","NGM","NGN","NGO","NGP", "NGQ","NGR","NGS","NGT","NGU","NGV","NGW","NGX","NGY","NGZ","NHA","NHB","NHC","NHD","NHE","NHF", "NHG","NHH","NHI","NHJ","NHK","NHL","NHM","NHN","NHO","NHP","NHQ","NHR","NHS","NHT","NHU","NHV", "NHW","NHX","NHY","NHZ","NIA","NIB","NIC","NID","NIE","NIF","NIG","NIH","NII","NIJ","NIK","NIL", "NIM","NIN","NIO","NIP","NIQ","NIR","NIS","NIT","NIU","NIV","NIW","NIX","NIY","NIZ","NJA","NJB", "NJC","NJD","NJE","NJF","NJG","NJH","NJI","NJJ","NJK","NJL","NJM","NJN","NJO","NJP","NJQ","NJR", "NJS","NJT","NJU","NJV","NJW","NJX","NJY","NJZ","NKA","NKB","NKC","NKD","NKE","NKF","NKG","NKH", "NKI","NKJ","NKK","NKL","NKM","NKN","NKO","NKP","NKQ","NKR","NKS","NKT","NKU","NKV","NKW","NKX", "NKY","NKZ","NLA","NLB","NLC","NLD","NLE","NLF","NLG","NLH","NLI","NLJ","NLK","NLL","NLM","NLN", "NLO","NLP","NLQ","NLR","NLS","NLT","NLU","NLV","NLW","NLX","NLY","NLZ","NMA","NMB","NMC","NMD", "NME","NMF","NMG","NMH","NMI","NMJ","NMK","NML","NMM","NMN","NMO","NMP","NMQ","NMR","NMS","NMT", "NMU","NMV","NMW","NMX","NMY","NMZ","NNA","NNB","NNC","NND","NNE","NNF","NNG","NNH","NNI","NNJ", "NNK","NNL","NNM","NNN","NNO","NNP","NNQ","NNR","NNS","NNT","NNU","NNV","NNW","NNX","NNY","NNZ", "NOA","NOB","NOC","NOD","NOE","NOF","NOG","NOH","NOI","NOJ","NOK","NOL","NOM","NON","NOO","NOP", "NOQ","NOR","NOS","NOT","NOU","NOV","NOW","NOX","NOY","NOZ","NPA","NPB","NPC","NPD","NPE","NPF", "NPG","NPH","NPI","NPJ","NPK","NPL","NPM","NPN","NPO","NPP","NPQ","NPR","NPS","NPT","NPU","NPV", "NPW","NPX","NPY","NPZ","NQA","NQB","NQC","NQD","NQE","NQF","NQG","NQH","NQI","NQJ","NQK","NQL", "NQM","NQN","NQO","NQP","NQQ","NQR","NQS","NQT","NQU","NQV","NQW","NQX","NQY","NQZ","NRA","NRB", "NRC","NRD","NRE","NRF","NRG","NRH","NRI","NRJ","NRK","NRL","NRM","NRN","NRO","NRP","NRQ","NRR", "NRS","NRT","NRU","NRV","NRW","NRX","NRY","NRZ","NSA","NSB","NSC","NSD","NSE","NSF","NSG","NSH", "NSI","NSJ","NSK","NSL","NSM","NSN","NSO","NSP","NSQ","NSR","NSS","NST","NSU","NSV","NSW","NSX", "NSY","NSZ","NTA","NTB","NTC","NTD","NTE","NTF","NTG","NTH","NTI","NTJ","NTK","NTL","NTM","NTN", "NTO","NTP","NTQ","NTR","NTS","NTT","NTU","NTV","NTW","NTX","NTY","NTZ","NUA","NUB","NUC","NUD", "NUE","NUF","NUG","NUH","NUI","NUJ","NUK","NUL","NUM","NUN","NUO","NUP","NUQ","NUR","NUS","NUT", "NUU","NUV","NUW","NUX","NUY","NUZ","NVA","NVB","NVC","NVD","NVE","NVF","NVG","NVH","NVI","NVJ", "NVK","NVL","NVM","NVN","NVO","NVP","NVQ","NVR","NVS","NVT","NVU","NVV","NVW","NVX","NVY","NVZ", "NWA","NWB","NWC","NWD","NWE","NWF","NWG","NWH","NWI","NWJ","NWK","NWL","NWM","NWN","NWO","NWP", "NWQ","NWR","NWS","NWT","NWU","NWV","NWW","NWX","NWY","NWZ","NXA","NXB","NXC","NXD","NXE","NXF", "NXG","NXH","NXI","NXJ","NXK","NXL","NXM","NXN","NXO","NXP","NXQ","NXR","NXS","NXT","NXU","NXV", "NXW","NXX","NXY","NXZ","NYA","NYB","NYC","NYD","NYE","NYF","NYG","NYH","NYI","NYJ","NYK","NYL", "NYM","NYN","NYO","NYP","NYQ","NYR","NYS","NYT","NYU","NYV","NYW","NYX","NYY","NYZ","NZA","NZB", "NZC","NZD","NZE","NZF","NZG","NZH","NZI","NZJ","NZK","NZL","NZM","NZN","NZO","NZP","NZQ","NZR", "NZS","NZT","NZU","NZV","NZW","NZX","NZY","NZZ","OAA","OAB","OAC","OAD","OAE","OAF","OAG","OAH", "OAI","OAJ","OAK","OAL","OAM","OAN","OAO","OAP","OAQ","OAR","OAS","OAT","OAU","OAV","OAW","OAX", "OAY","OAZ","OBA","OBB","OBC","OBD","OBE","OBF","OBG","OBH","OBI","OBJ","OBK","OBL","OBM","OBN", "OBO","OBP","OBQ","OBR","OBS","OBT","OBU","OBV","OBW","OBX","OBY","OBZ","OCA","OCB","OCC","OCD", "OCE","OCF","OCG","OCH","OCI","OCJ","OCK","OCL","OCM","OCN","OCO","OCP","OCQ","OCR","OCS","OCT", "OCU","OCV","OCW","OCX","OCY","OCZ","ODA","ODB","ODC","ODD","ODE","ODF","ODG","ODH","ODI","ODJ", "ODK","ODL","ODM","ODN","ODO","ODP","ODQ","ODR","ODS","ODT","ODU","ODV","ODW","ODX","ODY","ODZ", "OEA","OEB","OEC","OED","OEE","OEF","OEG","OEH","OEI","OEJ","OEK","OEL","OEM","OEN","OEO","OEP", "OEQ","OER","OES","OET","OEU","OEV","OEW","OEX","OEY","OEZ","OFA","OFB","OFC","OFD","OFE","OFF", "OFG","OFH","OFI","OFJ","OFK","OFL","OFM","OFN","OFO","OFP","OFQ","OFR","OFS","OFT","OFU","OFV", "OFW","OFX","OFY","OFZ","OGA","OGB","OGC","OGD","OGE","OGF","OGG","OGH","OGI","OGJ","OGK","OGL", "OGM","OGN","OGO","OGP","OGQ","OGR","OGS","OGT","OGU","OGV","OGW","OGX","OGY","OGZ","OHA","OHB", "OHC","OHD","OHE","OHF","OHG","OHH","OHI","OHJ","OHK","OHL","OHM","OHN","OHO","OHP","OHQ","OHR", "OHS","OHT","OHU","OHV","OHW","OHX","OHY","OHZ","OIA","OIB","OIC","OID","OIE","OIF","OIG","OIH", "OII","OIJ","OIK","OIL","OIM","OIN","OIO","OIP","OIQ","OIR","OIS","OIT","OIU","OIV","OIW","OIX", "OIY","OIZ","OJA","OJB","OJC","OJD","OJE","OJF","OJG","OJH","OJI","OJJ","OJK","OJL","OJM","OJN", "OJO","OJP","OJQ","OJR","OJS","OJT","OJU","OJV","OJW","OJX","OJY","OJZ","OKA","OKB","OKC","OKD", "OKE","OKF","OKG","OKH","OKI","OKJ","OKK","OKL","OKM","OKN","OKO","OKP","OKQ","OKR","OKS","OKT", "OKU","OKV","OKW","OKX","OKY","OKZ","OLA","OLB","OLC","OLD","OLE","OLF","OLG","OLH","OLI","OLJ", "OLK","OLL","OLM","OLN","OLO","OLP","OLQ","OLR","OLS","OLT","OLU","OLV","OLW","OLX","OLY","OLZ", "OMA","OMB","OMC","OMD","OME","OMF","OMG","OMH","OMI","OMJ","OMK","OML","OMM","OMN","OMO","OMP", "OMQ","OMR","OMS","OMT","OMU","OMV","OMW","OMX","OMY","OMZ","ONA","ONB","ONC","OND","ONE","ONF", "ONG","ONH","ONI","ONJ","ONK","ONL","ONM","ONN","ONO","ONP","ONQ","ONR","ONS","ONT","ONU","ONV", "ONW","ONX","ONY","ONZ","OOA","OOB","OOC","OOD","OOE","OOF","OOG","OOH","OOI","OOJ","OOK","OOL", "OOM","OON","OOO","OOP","OOQ","OOR","OOS","OOT","OOU","OOV","OOW","OOX","OOY","OOZ","OPA","OPB", "OPC","OPD","OPE","OPF","OPG","OPH","OPI","OPJ","OPK","OPL","OPM","OPN","OPO","OPP","OPQ","OPR", "OPS","OPT","OPU","OPV","OPW","OPX","OPY","OPZ","OQA","OQB","OQC","OQD","OQE","OQF","OQG","OQH", "OQI","OQJ","OQK","OQL","OQM","OQN","OQO","OQP","OQQ","OQR","OQS","OQT","OQU","OQV","OQW","OQX", "OQY","OQZ","ORA","ORB","ORC","ORD","ORE","ORF","ORG","ORH","ORI","ORJ","ORK","ORL","ORM","ORN", "ORO","ORP","ORQ","ORR","ORS","ORT","ORU","ORV","ORW","ORX","ORY","ORZ","OSA","OSB","OSC","OSD", "OSE","OSF","OSG","OSH","OSI","OSJ","OSK","OSL","OSM","OSN","OSO","OSP","OSQ","OSR","OSS","OST", "OSU","OSV","OSW","OSX","OSY","OSZ","OTA","OTB","OTC","OTD","OTE","OTF","OTG","OTH","OTI","OTJ", "OTK","OTL","OTM","OTN","OTO","OTP","OTQ","OTR","OTS","OTT","OTU","OTV","OTW","OTX","OTY","OTZ", "OUA","OUB","OUC","OUD","OUE","OUF","OUG","OUH","OUI","OUJ","OUK","OUL","OUM","OUN","OUO","OUP", "OUQ","OUR","OUS","OUT","OUU","OUV","OUW","OUX","OUY","OUZ","OVA","OVB","OVC","OVD","OVE","OVF", "OVG","OVH","OVI","OVJ","OVK","OVL","OVM","OVN","OVO","OVP","OVQ","OVR","OVS","OVT","OVU","OVV", "OVW","OVX","OVY","OVZ","OWA","OWB","OWC","OWD","OWE","OWF","OWG","OWH","OWI","OWJ","OWK","OWL", "OWM","OWN","OWO","OWP","OWQ","OWR","OWS","OWT","OWU","OWV","OWW","OWX","OWY","OWZ","OXA","OXB", "OXC","OXD","OXE","OXF","OXG","OXH","OXI","OXJ","OXK","OXL","OXM","OXN","OXO","OXP","OXQ","OXR", "OXS","OXT","OXU","OXV","OXW","OXX","OXY","OXZ","OYA","OYB","OYC","OYD","OYE","OYF","OYG","OYH", "OYI","OYJ","OYK","OYL","OYM","OYN","OYO","OYP","OYQ","OYR","OYS","OYT","OYU","OYV","OYW","OYX", "OYY","OYZ","OZA","OZB","OZC","OZD","OZE","OZF","OZG","OZH","OZI","OZJ","OZK","OZL","OZM","OZN", "OZO","OZP","OZQ","OZR","OZS","OZT","OZU","OZV","OZW","OZX","OZY","OZZ","PAA","PAB","PAC","PAD", "PAE","PAF","PAG","PAH","PAI","PAJ","PAK","PAL","PAM","PAN","PAO","PAP","PAQ","PAR","PAS","PAT", "PAU","PAV","PAW","PAX","PAY","PAZ","PBA","PBB","PBC","PBD","PBE","PBF","PBG","PBH","PBI","PBJ", "PBK","PBL","PBM","PBN","PBO","PBP","PBQ","PBR","PBS","PBT","PBU","PBV","PBW","PBX","PBY","PBZ", "PCA","PCB","PCC","PCD","PCE","PCF","PCG","PCH","PCI","PCJ","PCK","PCL","PCM","PCN","PCO","PCP", "PCQ","PCR","PCS","PCT","PCU","PCV","PCW","PCX","PCY","PCZ","PDA","PDB","PDC","PDD","PDE","PDF", "PDG","PDH","PDI","PDJ","PDK","PDL","PDM","PDN","PDO","PDP","PDQ","PDR","PDS","PDT","PDU","PDV", "PDW","PDX","PDY","PDZ","PEA","PEB","PEC","PED","PEE","PEF","PEG","PEH","PEI","PEJ","PEK","PEL", "PEM","PEN","PEO","PEP","PEQ","PER","PES","PET","PEU","PEV","PEW","PEX","PEY","PEZ","PFA","PFB", "PFC","PFD","PFE","PFF","PFG","PFH","PFI","PFJ","PFK","PFL","PFM","PFN","PFO","PFP","PFQ","PFR", "PFS","PFT","PFU","PFV","PFW","PFX","PFY","PFZ","PGA","PGB","PGC","PGD","PGE","PGF","PGG","PGH", "PGI","PGJ","PGK","PGL","PGM","PGN","PGO","PGP","PGQ","PGR","PGS","PGT","PGU","PGV","PGW","PGX", "PGY","PGZ","PHA","PHB","PHC","PHD","PHE","PHF","PHG","PHH","PHI","PHJ","PHK","PHL","PHM","PHN", "PHO","PHP","PHQ","PHR","PHS","PHT","PHU","PHV","PHW","PHX","PHY","PHZ","PIA","PIB","PIC","PID", "PIE","PIF","PIG","PIH","PII","PIJ","PIK","PIL","PIM","PIN","PIO","PIP","PIQ","PIR","PIS","PIT", "PIU","PIV","PIW","PIX","PIY","PIZ","PJA","PJB","PJC","PJD","PJE","PJF","PJG","PJH","PJI","PJJ", "PJK","PJL","PJM","PJN","PJO","PJP","PJQ","PJR","PJS","PJT","PJU","PJV","PJW","PJX","PJY","PJZ", "PKA","PKB","PKC","PKD","PKE","PKF","PKG","PKH","PKI","PKJ","PKK","PKL","PKM","PKN","PKO","PKP", "PKQ","PKR","PKS","PKT","PKU","PKV","PKW","PKX","PKY","PKZ","PLA","PLB","PLC","PLD","PLE","PLF", "PLG","PLH","PLI","PLJ","PLK","PLL","PLM","PLN","PLO","PLP","PLQ","PLR","PLS","PLT","PLU","PLV", "PLW","PLX","PLY","PLZ","PMA","PMB","PMC","PMD","PME","PMF","PMG","PMH","PMI","PMJ","PMK","PML", "PMM","PMN","PMO","PMP","PMQ","PMR","PMS","PMT","PMU","PMV","PMW","PMX","PMY","PMZ","PNA","PNB", "PNC","PND","PNE","PNF","PNG","PNH","PNI","PNJ","PNK","PNL","PNM","PNN","PNO","PNP","PNQ","PNR", "PNS","PNT","PNU","PNV","PNW","PNX","PNY","PNZ","POA","POB","POC","POD","POE","POF","POG","POH", "POI","POJ","POK","POL","POM","PON","POO","POP","POQ","POR","POS","POT","POU","POV","POW","POX", "POY","POZ","PPA","PPB","PPC","PPD","PPE","PPF","PPG","PPH","PPI","PPJ","PPK","PPL","PPM","PPN", "PPO","PPP","PPQ","PPR","PPS","PPT","PPU","PPV","PPW","PPX","PPY","PPZ","PQA","PQB","PQC","PQD", "PQE","PQF","PQG","PQH","PQI","PQJ","PQK","PQL","PQM","PQN","PQO","PQP","PQQ","PQR","PQS","PQT", "PQU","PQV","PQW","PQX","PQY","PQZ","PRA","PRB","PRC","PRD","PRE","PRF","PRG","PRH","PRI","PRJ", "PRK","PRL","PRM","PRN","PRO","PRP","PRQ","PRR","PRS","PRT","PRU","PRV","PRW","PRX","PRY","PRZ", "PSA","PSB","PSC","PSD","PSE","PSF","PSG","PSH","PSI","PSJ","PSK","PSL","PSM","PSN","PSO","PSP", "PSQ","PSR","PSS","PST","PSU","PSV","PSW","PSX","PSY","PSZ","PTA","PTB","PTC","PTD","PTE","PTF", "PTG","PTH","PTI","PTJ","PTK","PTL","PTM","PTN","PTO","PTP","PTQ","PTR","PTS","PTT","PTU","PTV", "PTW","PTX","PTY","PTZ","PUA","PUB","PUC","PUD","PUE","PUF","PUG","PUH","PUI","PUJ","PUK","PUL", "PUM","PUN","PUO","PUP","PUQ","PUR","PUS","PUT","PUU","PUV","PUW","PUX","PUY","PUZ","PVA","PVB", "PVC","PVD","PVE","PVF","PVG","PVH","PVI","PVJ","PVK","PVL","PVM","PVN","PVO","PVP","PVQ","PVR", "PVS","PVT","PVU","PVV","PVW","PVX","PVY","PVZ","PWA","PWB","PWC","PWD","PWE","PWF","PWG","PWH", "PWI","PWJ","PWK","PWL","PWM","PWN","PWO","PWP","PWQ","PWR","PWS","PWT","PWU","PWV","PWW","PWX", "PWY","PWZ","PXA","PXB","PXC","PXD","PXE","PXF","PXG","PXH","PXI","PXJ","PXK","PXL","PXM","PXN", "PXO","PXP","PXQ","PXR","PXS","PXT","PXU","PXV","PXW","PXX","PXY","PXZ","PYA","PYB","PYC","PYD", "PYE","PYF","PYG","PYH","PYI","PYJ","PYK","PYL","PYM","PYN","PYO","PYP","PYQ","PYR","PYS","PYT", "PYU","PYV","PYW","PYX","PYY","PYZ","PZA","PZB","PZC","PZD","PZE","PZF","PZG","PZH","PZI","PZJ", "PZK","PZL","PZM","PZN","PZO","PZP","PZQ","PZR","PZS","PZT","PZU","PZV","PZW","PZX","PZY","PZZ", "QAA","QAB","QAC","QAD","QAE","QAF","QAG","QAH","QAI","QAJ","QAK","QAL","QAM","QAN","QAO","QAP", "QAQ","QAR","QAS","QAT","QAU","QAV","QAW","QAX","QAY","QAZ","QBA","QBB","QBC","QBD","QBE","QBF", "QBG","QBH","QBI","QBJ","QBK","QBL","QBM","QBN","QBO","QBP","QBQ","QBR","QBS","QBT","QBU","QBV", "QBW","QBX","QBY","QBZ","QCA","QCB","QCC","QCD","QCE","QCF","QCG","QCH","QCI","QCJ","QCK","QCL", "QCM","QCN","QCO","QCP","QCQ","QCR","QCS","QCT","QCU","QCV","QCW","QCX","QCY","QCZ","QDA","QDB", "QDC","QDD","QDE","QDF","QDG","QDH","QDI","QDJ","QDK","QDL","QDM","QDN","QDO","QDP","QDQ","QDR", "QDS","QDT","QDU","QDV","QDW","QDX","QDY","QDZ","QEA","QEB","QEC","QED","QEE","QEF","QEG","QEH", "QEI","QEJ","QEK","QEL","QEM","QEN","QEO","QEP","QEQ","QER","QES","QET","QEU","QEV","QEW","QEX", "QEY","QEZ","QFA","QFB","QFC","QFD","QFE","QFF","QFG","QFH","QFI","QFJ","QFK","QFL","QFM","QFN", "QFO","QFP","QFQ","QFR","QFS","QFT","QFU","QFV","QFW","QFX","QFY","QFZ","QGA","QGB","QGC","QGD", "QGE","QGF","QGG","QGH","QGI","QGJ","QGK","QGL","QGM","QGN","QGO","QGP","QGQ","QGR","QGS","QGT", "QGU","QGV","QGW","QGX","QGY","QGZ","QHA","QHB","QHC","QHD","QHE","QHF","QHG","QHH","QHI","QHJ", "QHK","QHL","QHM","QHN","QHO","QHP","QHQ","QHR","QHS","QHT","QHU","QHV","QHW","QHX","QHY","QHZ", "QIA","QIB","QIC","QID","QIE","QIF","QIG","QIH","QII","QIJ","QIK","QIL","QIM","QIN","QIO","QIP", "QIQ","QIR","QIS","QIT","QIU","QIV","QIW","QIX","QIY","QIZ","QJA","QJB","QJC","QJD","QJE","QJF", "QJG","QJH","QJI","QJJ","QJK","QJL","QJM","QJN","QJO","QJP","QJQ","QJR","QJS","QJT","QJU","QJV", "QJW","QJX","QJY","QJZ","QKA","QKB","QKC","QKD","QKE","QKF","QKG","QKH","QKI","QKJ","QKK","QKL", "QKM","QKN","QKO","QKP","QKQ","QKR","QKS","QKT","QKU","QKV","QKW","QKX","QKY","QKZ","QLA","QLB", "QLC","QLD","QLE","QLF","QLG","QLH","QLI","QLJ","QLK","QLL","QLM","QLN","QLO","QLP","QLQ","QLR", "QLS","QLT","QLU","QLV","QLW","QLX","QLY","QLZ","QMA","QMB","QMC","QMD","QME","QMF","QMG","QMH", "QMI","QMJ","QMK","QML","QMM","QMN","QMO","QMP","QMQ","QMR","QMS","QMT","QMU","QMV","QMW","QMX", "QMY","QMZ","QNA","QNB","QNC","QND","QNE","QNF","QNG","QNH","QNI","QNJ","QNK","QNL","QNM","QNN", "QNO","QNP","QNQ","QNR","QNS","QNT","QNU","QNV","QNW","QNX","QNY","QNZ","QOA","QOB","QOC","QOD", "QOE","QOF","QOG","QOH","QOI","QOJ","QOK","QOL","QOM","QON","QOO","QOP","QOQ","QOR","QOS","QOT", "QOU","QOV","QOW","QOX","QOY","QOZ","QPA","QPB","QPC","QPD","QPE","QPF","QPG","QPH","QPI","QPJ", "QPK","QPL","QPM","QPN","QPO","QPP","QPQ","QPR","QPS","QPT","QPU","QPV","QPW","QPX","QPY","QPZ", "QQA","QQB","QQC","QQD","QQE","QQF","QQG","QQH","QQI","QQJ","QQK","QQL","QQM","QQN","QQO","QQP", "QQQ","QQR","QQS","QQT","QQU","QQV","QQW","QQX","QQY","QQZ","QRA","QRB","QRC","QRD","QRE","QRF", "QRG","QRH","QRI","QRJ","QRK","QRL","QRM","QRN","QRO","QRP","QRQ","QRR","QRS","QRT","QRU","QRV", "QRW","QRX","QRY","QRZ","QSA","QSB","QSC","QSD","QSE","QSF","QSG","QSH","QSI","QSJ","QSK","QSL", "QSM","QSN","QSO","QSP","QSQ","QSR","QSS","QST","QSU","QSV","QSW","QSX","QSY","QSZ","QTA","QTB", "QTC","QTD","QTE","QTF","QTG","QTH","QTI","QTJ","QTK","QTL","QTM","QTN","QTO","QTP","QTQ","QTR", "QTS","QTT","QTU","QTV","QTW","QTX","QTY","QTZ","QUA","QUB","QUC","QUD","QUE","QUF","QUG","QUH", "QUI","QUJ","QUK","QUL","QUM","QUN","QUO","QUP","QUQ","QUR","QUS","QUT","QUU","QUV","QUW","QUX", "QUY","QUZ","QVA","QVB","QVC","QVD","QVE","QVF","QVG","QVH","QVI","QVJ","QVK","QVL","QVM","QVN", "QVO","QVP","QVQ","QVR","QVS","QVT","QVU","QVV","QVW","QVX","QVY","QVZ","QWA","QWB","QWC","QWD", "QWE","QWF","QWG","QWH","QWI","QWJ","QWK","QWL","QWM","QWN","QWO","QWP","QWQ","QWR","QWS","QWT", "QWU","QWV","QWW","QWX","QWY","QWZ","QXA","QXB","QXC","QXD","QXE","QXF","QXG","QXH","QXI","QXJ", "QXK","QXL","QXM","QXN","QXO","QXP","QXQ","QXR","QXS","QXT","QXU","QXV","QXW","QXX","QXY","QXZ", "QYA","QYB","QYC","QYD","QYE","QYF","QYG","QYH","QYI","QYJ","QYK","QYL","QYM","QYN","QYO","QYP", "QYQ","QYR","QYS","QYT","QYU","QYV","QYW","QYX","QYY","QYZ","QZA","QZB","QZC","QZD","QZE","QZF", "QZG","QZH","QZI","QZJ","QZK","QZL","QZM","QZN","QZO","QZP","QZQ","QZR","QZS","QZT","QZU","QZV", "QZW","QZX","QZY","QZZ","RAA","RAB","RAC","RAD","RAE","RAF","RAG","RAH","RAI","RAJ","RAK","RAL", "RAM","RAN","RAO","RAP","RAQ","RAR","RAS","RAT","RAU","RAV","RAW","RAX","RAY","RAZ","RBA","RBB", "RBC","RBD","RBE","RBF","RBG","RBH","RBI","RBJ","RBK","RBL","RBM","RBN","RBO","RBP","RBQ","RBR", "RBS","RBT","RBU","RBV","RBW","RBX","RBY","RBZ","RCA","RCB","RCC","RCD","RCE","RCF","RCG","RCH", "RCI","RCJ","RCK","RCL","RCM","RCN","RCO","RCP","RCQ","RCR","RCS","RCT","RCU","RCV","RCW","RCX", "RCY","RCZ","RDA","RDB","RDC","RDD","RDE","RDF","RDG","RDH","RDI","RDJ","RDK","RDL","RDM","RDN", "RDO","RDP","RDQ","RDR","RDS","RDT","RDU","RDV","RDW","RDX","RDY","RDZ","REA","REB","REC","RED", "REE","REF","REG","REH","REI","REJ","REK","REL","REM","REN","REO","REP","REQ","RER","RES","RET", "REU","REV","REW","REX","REY","REZ","RFA","RFB","RFC","RFD","RFE","RFF","RFG","RFH","RFI","RFJ", "RFK","RFL","RFM","RFN","RFO","RFP","RFQ","RFR","RFS","RFT","RFU","RFV","RFW","RFX","RFY","RFZ", "RGA","RGB","RGC","RGD","RGE","RGF","RGG","RGH","RGI","RGJ","RGK","RGL","RGM","RGN","RGO","RGP", "RGQ","RGR","RGS","RGT","RGU","RGV","RGW","RGX","RGY","RGZ","RHA","RHB","RHC","RHD","RHE","RHF", "RHG","RHH","RHI","RHJ","RHK","RHL","RHM","RHN","RHO","RHP","RHQ","RHR","RHS","RHT","RHU","RHV", "RHW","RHX","RHY","RHZ","RIA","RIB","RIC","RID","RIE","RIF","RIG","RIH","RII","RIJ","RIK","RIL", "RIM","RIN","RIO","RIP","RIQ","RIR","RIS","RIT","RIU","RIV","RIW","RIX","RIY","RIZ","RJA","RJB", "RJC","RJD","RJE","RJF","RJG","RJH","RJI","RJJ","RJK","RJL","RJM","RJN","RJO","RJP","RJQ","RJR", "RJS","RJT","RJU","RJV","RJW","RJX","RJY","RJZ","RKA","RKB","RKC","RKD","RKE","RKF","RKG","RKH", "RKI","RKJ","RKK","RKL","RKM","RKN","RKO","RKP","RKQ","RKR","RKS","RKT","RKU","RKV","RKW","RKX", "RKY","RKZ","RLA","RLB","RLC","RLD","RLE","RLF","RLG","RLH","RLI","RLJ","RLK","RLL","RLM","RLN", "RLO","RLP","RLQ","RLR","RLS","RLT","RLU","RLV","RLW","RLX","RLY","RLZ","RMA","RMB","RMC","RMD", "RME","RMF","RMG","RMH","RMI","RMJ","RMK","RML","RMM","RMN","RMO","RMP","RMQ","RMR","RMS","RMT", "RMU","RMV","RMW","RMX","RMY","RMZ","RNA","RNB","RNC","RND","RNE","RNF","RNG","RNH","RNI","RNJ", "RNK","RNL","RNM","RNN","RNO","RNP","RNQ","RNR","RNS","RNT","RNU","RNV","RNW","RNX","RNY","RNZ", "ROA","ROB","ROC","ROD","ROE","ROF","ROG","ROH","ROI","ROJ","ROK","ROL","ROM","RON","ROO","ROP", "ROQ","ROR","ROS","ROT","ROU","ROV","ROW","ROX","ROY","ROZ","RPA","RPB","RPC","RPD","RPE","RPF", "RPG","RPH","RPI","RPJ","RPK","RPL","RPM","RPN","RPO","RPP","RPQ","RPR","RPS","RPT","RPU","RPV", "RPW","RPX","RPY","RPZ","RQA","RQB","RQC","RQD","RQE","RQF","RQG","RQH","RQI","RQJ","RQK","RQL", "RQM","RQN","RQO","RQP","RQQ","RQR","RQS","RQT","RQU","RQV","RQW","RQX","RQY","RQZ","RRA","RRB", "RRC","RRD","RRE","RRF","RRG","RRH","RRI","RRJ","RRK","RRL","RRM","RRN","RRO","RRP","RRQ","RRR", "RRS","RRT","RRU","RRV","RRW","RRX","RRY","RRZ","RSA","RSB","RSC","RSD","RSE","RSF","RSG","RSH", "RSI","RSJ","RSK","RSL","RSM","RSN","RSO","RSP","RSQ","RSR","RSS","RST","RSU","RSV","RSW","RSX", "RSY","RSZ","RTA","RTB","RTC","RTD","RTE","RTF","RTG","RTH","RTI","RTJ","RTK","RTL","RTM","RTN", "RTO","RTP","RTQ","RTR","RTS","RTT","RTU","RTV","RTW","RTX","RTY","RTZ","RUA","RUB","RUC","RUD", "RUE","RUF","RUG","RUH","RUI","RUJ","RUK","RUL","RUM","RUN","RUO","RUP","RUQ","RUR","RUS","RUT", "RUU","RUV","RUW","RUX","RUY","RUZ","RVA","RVB","RVC","RVD","RVE","RVF","RVG","RVH","RVI","RVJ", "RVK","RVL","RVM","RVN","RVO","RVP","RVQ","RVR","RVS","RVT","RVU","RVV","RVW","RVX","RVY","RVZ", "RWA","RWB","RWC","RWD","RWE","RWF","RWG","RWH","RWI","RWJ","RWK","RWL","RWM","RWN","RWO","RWP", "RWQ","RWR","RWS","RWT","RWU","RWV","RWW","RWX","RWY","RWZ","RXA","RXB","RXC","RXD","RXE","RXF", "RXG","RXH","RXI","RXJ","RXK","RXL","RXM","RXN","RXO","RXP","RXQ","RXR","RXS","RXT","RXU","RXV", "RXW","RXX","RXY","RXZ","RYA","RYB","RYC","RYD","RYE","RYF","RYG","RYH","RYI","RYJ","RYK","RYL", "RYM","RYN","RYO","RYP","RYQ","RYR","RYS","RYT","RYU","RYV","RYW","RYX","RYY","RYZ","RZA","RZB", "RZC","RZD","RZE","RZF","RZG","RZH","RZI","RZJ","RZK","RZL","RZM","RZN","RZO","RZP","RZQ","RZR", "RZS","RZT","RZU","RZV","RZW","RZX","RZY","RZZ","SAA","SAB","SAC","SAD","SAE","SAF","SAG","SAH", "SAI","SAJ","SAK","SAL","SAM","SAN","SAO","SAP","SAQ","SAR","SAS","SAT","SAU","SAV","SAW","SAX", "SAY","SAZ","SBA","SBB","SBC","SBD","SBE","SBF","SBG","SBH","SBI","SBJ","SBK","SBL","SBM","SBN", "SBO","SBP","SBQ","SBR","SBS","SBT","SBU","SBV","SBW","SBX","SBY","SBZ","SCA","SCB","SCC","SCD", "SCE","SCF","SCG","SCH","SCI","SCJ","SCK","SCL","SCM","SCN","SCO","SCP","SCQ","SCR","SCS","SCT", "SCU","SCV","SCW","SCX","SCY","SCZ","SDA","SDB","SDC","SDD","SDE","SDF","SDG","SDH","SDI","SDJ", "SDK","SDL","SDM","SDN","SDO","SDP","SDQ","SDR","SDS","SDT","SDU","SDV","SDW","SDX","SDY","SDZ", "SEA","SEB","SEC","SED","SEE","SEF","SEG","SEH","SEI","SEJ","SEK","SEL","SEM","SEN","SEO","SEP", "SEQ","SER","SES","SET","SEU","SEV","SEW","SEX","SEY","SEZ","SFA","SFB","SFC","SFD","SFE","SFF", "SFG","SFH","SFI","SFJ","SFK","SFL","SFM","SFN","SFO","SFP","SFQ","SFR","SFS","SFT","SFU","SFV", "SFW","SFX","SFY","SFZ","SGA","SGB","SGC","SGD","SGE","SGF","SGG","SGH","SGI","SGJ","SGK","SGL", "SGM","SGN","SGO","SGP","SGQ","SGR","SGS","SGT","SGU","SGV","SGW","SGX","SGY","SGZ","SHA","SHB", "SHC","SHD","SHE","SHF","SHG","SHH","SHI","SHJ","SHK","SHL","SHM","SHN","SHO","SHP","SHQ","SHR", "SHS","SHT","SHU","SHV","SHW","SHX","SHY","SHZ","SIA","SIB","SIC","SID","SIE","SIF","SIG","SIH", "SII","SIJ","SIK","SIL","SIM","SIN","SIO","SIP","SIQ","SIR","SIS","SIT","SIU","SIV","SIW","SIX", "SIY","SIZ","SJA","SJB","SJC","SJD","SJE","SJF","SJG","SJH","SJI","SJJ","SJK","SJL","SJM","SJN", "SJO","SJP","SJQ","SJR","SJS","SJT","SJU","SJV","SJW","SJX","SJY","SJZ","SKA","SKB","SKC","SKD", "SKE","SKF","SKG","SKH","SKI","SKJ","SKK","SKL","SKM","SKN","SKO","SKP","SKQ","SKR","SKS","SKT", "SKU","SKV","SKW","SKX","SKY","SKZ","SLA","SLB","SLC","SLD","SLE","SLF","SLG","SLH","SLI","SLJ", "SLK","SLL","SLM","SLN","SLO","SLP","SLQ","SLR","SLS","SLT","SLU","SLV","SLW","SLX","SLY","SLZ", "SMA","SMB","SMC","SMD","SME","SMF","SMG","SMH","SMI","SMJ","SMK","SML","SMM","SMN","SMO","SMP", "SMQ","SMR","SMS","SMT","SMU","SMV","SMW","SMX","SMY","SMZ","SNA","SNB","SNC","SND","SNE","SNF", "SNG","SNH","SNI","SNJ","SNK","SNL","SNM","SNN","SNO","SNP","SNQ","SNR","SNS","SNT","SNU","SNV", "SNW","SNX","SNY","SNZ","SOA","SOB","SOC","SOD","SOE","SOF","SOG","SOH","SOI","SOJ","SOK","SOL", "SOM","SON","SOO","SOP","SOQ","SOR","SOS","SOT","SOU","SOV","SOW","SOX","SOY","SOZ","SPA","SPB", "SPC","SPD","SPE","SPF","SPG","SPH","SPI","SPJ","SPK","SPL","SPM","SPN","SPO","SPP","SPQ","SPR", "SPS","SPT","SPU","SPV","SPW","SPX","SPY","SPZ","SQA","SQB","SQC","SQD","SQE","SQF","SQG","SQH", "SQI","SQJ","SQK","SQL","SQM","SQN","SQO","SQP","SQQ","SQR","SQS","SQT","SQU","SQV","SQW","SQX", "SQY","SQZ","SRA","SRB","SRC","SRD","SRE","SRF","SRG","SRH","SRI","SRJ","SRK","SRL","SRM","SRN", "SRO","SRP","SRQ","SRR","SRS","SRT","SRU","SRV","SRW","SRX","SRY","SRZ","SSA","SSB","SSC","SSD", "SSE","SSF","SSG","SSH","SSI","SSJ","SSK","SSL","SSM","SSN","SSO","SSP","SSQ","SSR","SSS","SST", "SSU","SSV","SSW","SSX","SSY","SSZ","STA","STB","STC","STD","STE","STF","STG","STH","STI","STJ", "STK","STL","STM","STN","STO","STP","STQ","STR","STS","STT","STU","STV","STW","STX","STY","STZ", "SUA","SUB","SUC","SUD","SUE","SUF","SUG","SUH","SUI","SUJ","SUK","SUL","SUM","SUN","SUO","SUP", "SUQ","SUR","SUS","SUT","SUU","SUV","SUW","SUX","SUY","SUZ","SVA","SVB","SVC","SVD","SVE","SVF", "SVG","SVH","SVI","SVJ","SVK","SVL","SVM","SVN","SVO","SVP","SVQ","SVR","SVS","SVT","SVU","SVV", "SVW","SVX","SVY","SVZ","SWA","SWB","SWC","SWD","SWE","SWF","SWG","SWH","SWI","SWJ","SWK","SWL", "SWM","SWN","SWO","SWP","SWQ","SWR","SWS","SWT","SWU","SWV","SWW","SWX","SWY","SWZ","SXA","SXB", "SXC","SXD","SXE","SXF","SXG","SXH","SXI","SXJ","SXK","SXL","SXM","SXN","SXO","SXP","SXQ","SXR", "SXS","SXT","SXU","SXV","SXW","SXX","SXY","SXZ","SYA","SYB","SYC","SYD","SYE","SYF","SYG","SYH", "SYI","SYJ","SYK","SYL","SYM","SYN","SYO","SYP","SYQ","SYR","SYS","SYT","SYU","SYV","SYW","SYX", "SYY","SYZ","SZA","SZB","SZC","SZD","SZE","SZF","SZG","SZH","SZI","SZJ","SZK","SZL","SZM","SZN", "SZO","SZP","SZQ","SZR","SZS","SZT","SZU","SZV","SZW","SZX","SZY","SZZ", /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TAA to TTV - 516 triplets - intentionally omitted ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ "TTW","TTX","TTY","TTZ","TUA","TUB","TUC","TUD","TUE","TUF","TUG","TUH","TUI","TUJ","TUK","TUL", "TUM","TUN","TUO","TUP","TUQ","TUR","TUS","TUT","TUU","TUV","TUW","TUX","TUY","TUZ","TVA","TVB", "TVC","TVD","TVE","TVF","TVG","TVH","TVI","TVJ","TVK","TVL","TVM","TVN","TVO","TVP","TVQ","TVR", "TVS","TVT","TVU","TVV","TVW","TVX","TVY","TVZ","TWA","TWB","TWC","TWD","TWE","TWF","TWG","TWH", "TWI","TWJ","TWK","TWL","TWM","TWN","TWO","TWP","TWQ","TWR","TWS","TWT","TWU","TWV","TWW","TWX", "TWY","TWZ","TXA","TXB","TXC","TXD","TXE","TXF","TXG","TXH","TXI","TXJ","TXK","TXL","TXM","TXN", "TXO","TXP","TXQ","TXR","TXS","TXT","TXU","TXV","TXW","TXX","TXY","TXZ","TYA","TYB","TYC","TYD", "TYE","TYF","TYG","TYH","TYI","TYJ","TYK","TYL","TYM","TYN","TYO","TYP","TYQ","TYR","TYS","TYT", "TYU","TYV","TYW","TYX","TYY","TYZ","TZA","TZB","TZC","TZD","TZE","TZF","TZG","TZH","TZI","TZJ", "TZK","TZL","TZM","TZN","TZO","TZP","TZQ","TZR","TZS","TZT","TZU","TZV","TZW","TZX","TZY","TZZ", "UAA","UAB","UAC","UAD","UAE","UAF","UAG","UAH","UAI","UAJ","UAK","UAL","UAM","UAN","UAO","UAP", "UAQ","UAR","UAS","UAT","UAU","UAV","UAW","UAX","UAY","UAZ","UBA","UBB","UBC","UBD","UBE","UBF", "UBG","UBH","UBI","UBJ","UBK","UBL","UBM","UBN","UBO","UBP","UBQ","UBR","UBS","UBT","UBU","UBV", "UBW","UBX","UBY","UBZ","UCA","UCB","UCC","UCD","UCE","UCF","UCG","UCH","UCI","UCJ","UCK","UCL", "UCM","UCN","UCO","UCP","UCQ","UCR","UCS","UCT","UCU","UCV","UCW","UCX","UCY","UCZ","UDA","UDB", "UDC","UDD","UDE","UDF","UDG","UDH","UDI","UDJ","UDK","UDL","UDM","UDN","UDO","UDP","UDQ","UDR", "UDS","UDT","UDU","UDV","UDW","UDX","UDY","UDZ","UEA","UEB","UEC","UED","UEE","UEF","UEG","UEH", "UEI","UEJ","UEK","UEL","UEM","UEN","UEO","UEP","UEQ","UER","UES","UET","UEU","UEV","UEW","UEX", "UEY","UEZ","UFA","UFB","UFC","UFD","UFE","UFF","UFG","UFH","UFI","UFJ","UFK","UFL","UFM","UFN", "UFO","UFP","UFQ","UFR","UFS","UFT","UFU","UFV","UFW","UFX","UFY","UFZ","UGA","UGB","UGC","UGD", "UGE","UGF","UGG","UGH","UGI","UGJ","UGK","UGL","UGM","UGN","UGO","UGP","UGQ","UGR","UGS","UGT", "UGU","UGV","UGW","UGX","UGY","UGZ","UHA","UHB","UHC","UHD","UHE","UHF","UHG","UHH","UHI","UHJ", "UHK","UHL","UHM","UHN","UHO","UHP","UHQ","UHR","UHS","UHT","UHU","UHV","UHW","UHX","UHY","UHZ", "UIA","UIB","UIC","UID","UIE","UIF","UIG","UIH","UII","UIJ","UIK","UIL","UIM","UIN","UIO","UIP", "UIQ","UIR","UIS","UIT","UIU","UIV","UIW","UIX","UIY","UIZ","UJA","UJB","UJC","UJD","UJE","UJF", "UJG","UJH","UJI","UJJ","UJK","UJL","UJM","UJN","UJO","UJP","UJQ","UJR","UJS","UJT","UJU","UJV", "UJW","UJX","UJY","UJZ","UKA","UKB","UKC","UKD","UKE","UKF","UKG","UKH","UKI","UKJ","UKK","UKL", "UKM","UKN","UKO","UKP","UKQ","UKR","UKS","UKT","UKU","UKV","UKW","UKX","UKY","UKZ","ULA","ULB", "ULC","ULD","ULE","ULF","ULG","ULH","ULI","ULJ","ULK","ULL","ULM","ULN","ULO","ULP","ULQ","ULR", "ULS","ULT","ULU","ULV","ULW","ULX","ULY","ULZ","UMA","UMB","UMC","UMD","UME","UMF","UMG","UMH", "UMI","UMJ","UMK","UML","UMM","UMN","UMO","UMP","UMQ","UMR","UMS","UMT","UMU","UMV","UMW","UMX", "UMY","UMZ","UNA","UNB","UNC","UND","UNE","UNF","UNG","UNH","UNI","UNJ","UNK","UNL","UNM","UNN", "UNO","UNP","UNQ","UNR","UNS","UNT","UNU","UNV","UNW","UNX","UNY","UNZ","UOA","UOB","UOC","UOD", "UOE","UOF","UOG","UOH","UOI","UOJ","UOK","UOL","UOM","UON","UOO","UOP","UOQ","UOR","UOS","UOT", "UOU","UOV","UOW","UOX","UOY","UOZ","UPA","UPB","UPC","UPD","UPE","UPF","UPG","UPH","UPI","UPJ", "UPK","UPL","UPM","UPN","UPO","UPP","UPQ","UPR","UPS","UPT","UPU","UPV","UPW","UPX","UPY","UPZ", "UQA","UQB","UQC","UQD","UQE","UQF","UQG","UQH","UQI","UQJ","UQK","UQL","UQM","UQN","UQO","UQP", "UQQ","UQR","UQS","UQT","UQU","UQV","UQW","UQX","UQY","UQZ","URA","URB","URC","URD","URE","URF", "URG","URH","URI","URJ","URK","URL","URM","URN","URO","URP","URQ","URR","URS","URT","URU","URV", "URW","URX","URY","URZ","USA","USB","USC","USD","USE","USF","USG","USH","USI","USJ","USK","USL", "USM","USN","USO","USP","USQ","USR","USS","UST","USU","USV","USW","USX","USY","USZ","UTA","UTB", "UTC","UTD","UTE","UTF","UTG","UTH","UTI","UTJ","UTK","UTL","UTM","UTN","UTO","UTP","UTQ","UTR", "UTS","UTT","UTU","UTV","UTW","UTX","UTY","UTZ","UUA","UUB","UUC","UUD","UUE","UUF","UUG","UUH", "UUI","UUJ","UUK","UUL","UUM","UUN","UUO","UUP","UUQ","UUR","UUS","UUT","UUU","UUV","UUW","UUX", "UUY","UUZ","UVA","UVB","UVC","UVD","UVE","UVF","UVG","UVH","UVI","UVJ","UVK","UVL","UVM","UVN", "UVO","UVP","UVQ","UVR","UVS","UVT","UVU","UVV","UVW","UVX","UVY","UVZ","UWA","UWB","UWC","UWD", "UWE","UWF","UWG","UWH","UWI","UWJ","UWK","UWL","UWM","UWN","UWO","UWP","UWQ","UWR","UWS","UWT", "UWU","UWV","UWW","UWX","UWY","UWZ","UXA","UXB","UXC","UXD","UXE","UXF","UXG","UXH","UXI","UXJ", "UXK","UXL","UXM","UXN","UXO","UXP","UXQ","UXR","UXS","UXT","UXU","UXV","UXW","UXX","UXY","UXZ", "UYA","UYB","UYC","UYD","UYE","UYF","UYG","UYH","UYI","UYJ","UYK","UYL","UYM","UYN","UYO","UYP", "UYQ","UYR","UYS","UYT","UYU","UYV","UYW","UYX","UYY","UYZ","UZA","UZB","UZC","UZD","UZE","UZF", "UZG","UZH","UZI","UZJ","UZK","UZL","UZM","UZN","UZO","UZP","UZQ","UZR","UZS","UZT","UZU","UZV", "UZW","UZX","UZY","UZZ","VAA","VAB","VAC","VAD","VAE","VAF","VAG","VAH","VAI","VAJ","VAK","VAL", "VAM","VAN","VAO","VAP","VAQ","VAR","VAS","VAT","VAU","VAV","VAW","VAX","VAY","VAZ","VBA","VBB", "VBC","VBD","VBE","VBF","VBG","VBH","VBI","VBJ","VBK","VBL","VBM","VBN","VBO","VBP","VBQ","VBR", "VBS","VBT","VBU","VBV","VBW","VBX","VBY","VBZ","VCA","VCB","VCC","VCD","VCE","VCF","VCG","VCH", "VCI","VCJ","VCK","VCL","VCM","VCN","VCO","VCP","VCQ","VCR","VCS","VCT","VCU","VCV","VCW","VCX", "VCY","VCZ","VDA","VDB","VDC","VDD","VDE","VDF","VDG","VDH","VDI","VDJ","VDK","VDL","VDM","VDN", "VDO","VDP","VDQ","VDR","VDS","VDT","VDU","VDV","VDW","VDX","VDY","VDZ","VEA","VEB","VEC","VED", "VEE","VEF","VEG","VEH","VEI","VEJ","VEK","VEL","VEM","VEN","VEO","VEP","VEQ","VER","VES","VET", "VEU","VEV","VEW","VEX","VEY","VEZ","VFA","VFB","VFC","VFD","VFE","VFF","VFG","VFH","VFI","VFJ", "VFK","VFL","VFM","VFN","VFO","VFP","VFQ","VFR","VFS","VFT","VFU","VFV","VFW","VFX","VFY","VFZ", "VGA","VGB","VGC","VGD","VGE","VGF","VGG","VGH","VGI","VGJ","VGK","VGL","VGM","VGN","VGO","VGP", "VGQ","VGR","VGS","VGT","VGU","VGV","VGW","VGX","VGY","VGZ","VHA","VHB","VHC","VHD","VHE","VHF", "VHG","VHH","VHI","VHJ","VHK","VHL","VHM","VHN","VHO","VHP","VHQ","VHR","VHS","VHT","VHU","VHV", "VHW","VHX","VHY","VHZ","VIA","VIB","VIC","VID","VIE","VIF","VIG","VIH","VII","VIJ","VIK","VIL", "VIM","VIN","VIO","VIP","VIQ","VIR","VIS","VIT","VIU","VIV","VIW","VIX","VIY","VIZ","VJA","VJB", "VJC","VJD","VJE","VJF","VJG","VJH","VJI","VJJ","VJK","VJL","VJM","VJN","VJO","VJP","VJQ","VJR", "VJS","VJT","VJU","VJV","VJW","VJX","VJY","VJZ","VKA","VKB","VKC","VKD","VKE","VKF","VKG","VKH", "VKI","VKJ","VKK","VKL","VKM","VKN","VKO","VKP","VKQ","VKR","VKS","VKT","VKU","VKV","VKW","VKX", "VKY","VKZ","VLA","VLB","VLC","VLD","VLE","VLF","VLG","VLH","VLI","VLJ","VLK","VLL","VLM","VLN", "VLO","VLP","VLQ","VLR","VLS","VLT","VLU","VLV","VLW","VLX","VLY","VLZ","VMA","VMB","VMC","VMD", "VME","VMF","VMG","VMH","VMI","VMJ","VMK","VML","VMM","VMN","VMO","VMP","VMQ","VMR","VMS","VMT", "VMU","VMV","VMW","VMX","VMY","VMZ","VNA","VNB","VNC","VND","VNE","VNF","VNG","VNH","VNI","VNJ", "VNK","VNL","VNM","VNN","VNO","VNP","VNQ","VNR","VNS","VNT","VNU","VNV","VNW","VNX","VNY","VNZ", "VOA","VOB","VOC","VOD","VOE","VOF","VOG","VOH","VOI","VOJ","VOK","VOL","VOM","VON","VOO","VOP", "VOQ","VOR","VOS","VOT","VOU","VOV","VOW","VOX","VOY","VOZ","VPA","VPB","VPC","VPD","VPE","VPF", "VPG","VPH","VPI","VPJ","VPK","VPL","VPM","VPN","VPO","VPP","VPQ","VPR","VPS","VPT","VPU","VPV", "VPW","VPX","VPY","VPZ","VQA","VQB","VQC","VQD","VQE","VQF","VQG","VQH","VQI","VQJ","VQK","VQL", "VQM","VQN","VQO","VQP","VQQ","VQR","VQS","VQT","VQU","VQV","VQW","VQX","VQY","VQZ","VRA","VRB", "VRC","VRD","VRE","VRF","VRG","VRH","VRI","VRJ","VRK","VRL","VRM","VRN","VRO","VRP","VRQ","VRR", "VRS","VRT","VRU","VRV","VRW","VRX","VRY","VRZ","VSA","VSB","VSC","VSD","VSE","VSF","VSG","VSH", "VSI","VSJ","VSK","VSL","VSM","VSN","VSO","VSP","VSQ","VSR","VSS","VST","VSU","VSV","VSW","VSX", "VSY","VSZ","VTA","VTB","VTC","VTD","VTE","VTF","VTG","VTH","VTI","VTJ","VTK","VTL","VTM","VTN", "VTO","VTP","VTQ","VTR","VTS","VTT","VTU","VTV","VTW","VTX","VTY","VTZ","VUA","VUB","VUC","VUD", "VUE","VUF","VUG","VUH","VUI","VUJ","VUK","VUL","VUM","VUN","VUO","VUP","VUQ","VUR","VUS","VUT", "VUU","VUV","VUW","VUX","VUY","VUZ","VVA","VVB","VVC","VVD","VVE","VVF","VVG","VVH","VVI","VVJ", "VVK","VVL","VVM","VVN","VVO","VVP","VVQ","VVR","VVS","VVT","VVU","VVV","VVW","VVX","VVY","VVZ", "VWA","VWB","VWC","VWD","VWE","VWF","VWG","VWH","VWI","VWJ","VWK","VWL","VWM","VWN","VWO","VWP", "VWQ","VWR","VWS","VWT","VWU","VWV","VWW","VWX","VWY","VWZ","VXA","VXB","VXC","VXD","VXE","VXF", "VXG","VXH","VXI","VXJ","VXK","VXL","VXM","VXN","VXO","VXP","VXQ","VXR","VXS","VXT","VXU","VXV", "VXW","VXX","VXY","VXZ","VYA","VYB","VYC","VYD","VYE","VYF","VYG","VYH","VYI","VYJ","VYK","VYL", "VYM","VYN","VYO","VYP","VYQ","VYR","VYS","VYT","VYU","VYV","VYW","VYX","VYY","VYZ","VZA","VZB", "VZC","VZD","VZE","VZF","VZG","VZH","VZI","VZJ","VZK","VZL","VZM","VZN","VZO","VZP","VZQ","VZR", "VZS","VZT","VZU","VZV","VZW","VZX","VZY","VZZ","WAA","WAB","WAC","WAD","WAE","WAF","WAG","WAH", "WAI","WAJ","WAK","WAL","WAM","WAN","WAO","WAP","WAQ","WAR","WAS","WAT","WAU","WAV","WAW","WAX", "WAY","WAZ","WBA","WBB","WBC","WBD","WBE","WBF","WBG","WBH","WBI","WBJ","WBK","WBL","WBM","WBN", "WBO","WBP","WBQ","WBR","WBS","WBT","WBU","WBV","WBW","WBX","WBY","WBZ","WCA","WCB","WCC","WCD", "WCE","WCF","WCG","WCH","WCI","WCJ","WCK","WCL","WCM","WCN","WCO","WCP","WCQ","WCR","WCS","WCT", "WCU","WCV","WCW","WCX","WCY","WCZ","WDA","WDB","WDC","WDD","WDE","WDF","WDG","WDH","WDI","WDJ", "WDK","WDL","WDM","WDN","WDO","WDP","WDQ","WDR","WDS","WDT","WDU","WDV","WDW","WDX","WDY","WDZ", "WEA","WEB","WEC","WED","WEE","WEF","WEG","WEH","WEI","WEJ","WEK","WEL","WEM","WEN","WEO","WEP", "WEQ","WER","WES","WET","WEU","WEV","WEW","WEX","WEY","WEZ","WFA","WFB","WFC","WFD","WFE","WFF", "WFG","WFH","WFI","WFJ","WFK","WFL","WFM","WFN","WFO","WFP","WFQ","WFR","WFS","WFT","WFU","WFV", "WFW","WFX","WFY","WFZ","WGA","WGB","WGC","WGD","WGE","WGF","WGG","WGH","WGI","WGJ","WGK","WGL", "WGM","WGN","WGO","WGP","WGQ","WGR","WGS","WGT","WGU","WGV","WGW","WGX","WGY","WGZ","WHA","WHB", "WHC","WHD","WHE","WHF","WHG","WHH","WHI","WHJ","WHK","WHL","WHM","WHN","WHO","WHP","WHQ","WHR", "WHS","WHT","WHU","WHV","WHW","WHX","WHY","WHZ","WIA","WIB","WIC","WID","WIE","WIF","WIG","WIH", "WII","WIJ","WIK","WIL","WIM","WIN","WIO","WIP","WIQ","WIR","WIS","WIT","WIU","WIV","WIW","WIX", "WIY","WIZ","WJA","WJB","WJC","WJD","WJE","WJF","WJG","WJH","WJI","WJJ","WJK","WJL","WJM","WJN", "WJO","WJP","WJQ","WJR","WJS","WJT","WJU","WJV","WJW","WJX","WJY","WJZ","WKA","WKB","WKC","WKD", "WKE","WKF","WKG","WKH","WKI","WKJ","WKK","WKL","WKM","WKN","WKO","WKP","WKQ","WKR","WKS","WKT", "WKU","WKV","WKW","WKX","WKY","WKZ","WLA","WLB","WLC","WLD","WLE","WLF","WLG","WLH","WLI","WLJ", "WLK","WLL","WLM","WLN","WLO","WLP","WLQ","WLR","WLS","WLT","WLU","WLV","WLW","WLX","WLY","WLZ", "WMA","WMB","WMC","WMD","WME","WMF","WMG","WMH","WMI","WMJ","WMK","WML","WMM","WMN","WMO","WMP", "WMQ","WMR","WMS","WMT","WMU","WMV","WMW","WMX","WMY","WMZ","WNA","WNB","WNC","WND","WNE","WNF", "WNG","WNH","WNI","WNJ","WNK","WNL","WNM","WNN","WNO","WNP","WNQ","WNR","WNS","WNT","WNU","WNV", "WNW","WNX","WNY","WNZ","WOA","WOB","WOC","WOD","WOE","WOF","WOG","WOH","WOI","WOJ","WOK","WOL", "WOM","WON","WOO","WOP","WOQ","WOR","WOS","WOT","WOU","WOV","WOW","WOX","WOY","WOZ","WPA","WPB", "WPC","WPD","WPE","WPF","WPG","WPH","WPI","WPJ","WPK","WPL","WPM","WPN","WPO","WPP","WPQ","WPR", "WPS","WPT","WPU","WPV","WPW","WPX","WPY","WPZ","WQA","WQB","WQC","WQD","WQE","WQF","WQG","WQH", "WQI","WQJ","WQK","WQL","WQM","WQN","WQO","WQP","WQQ","WQR","WQS","WQT","WQU","WQV","WQW","WQX", "WQY","WQZ","WRA","WRB","WRC","WRD","WRE","WRF","WRG","WRH","WRI","WRJ","WRK","WRL","WRM","WRN", "WRO","WRP","WRQ","WRR","WRS","WRT","WRU","WRV","WRW","WRX","WRY","WRZ","WSA","WSB","WSC","WSD", "WSE","WSF","WSG","WSH","WSI","WSJ","WSK","WSL","WSM","WSN","WSO","WSP","WSQ","WSR","WSS","WST", "WSU","WSV","WSW","WSX","WSY","WSZ","WTA","WTB","WTC","WTD","WTE","WTF","WTG","WTH","WTI","WTJ", "WTK","WTL","WTM","WTN","WTO","WTP","WTQ","WTR","WTS","WTT","WTU","WTV","WTW","WTX","WTY","WTZ", "WUA","WUB","WUC","WUD","WUE","WUF","WUG","WUH","WUI","WUJ","WUK","WUL","WUM","WUN","WUO","WUP", "WUQ","WUR","WUS","WUT","WUU","WUV","WUW","WUX","WUY","WUZ","WVA","WVB","WVC","WVD","WVE","WVF", "WVG","WVH","WVI","WVJ","WVK","WVL","WVM","WVN","WVO","WVP","WVQ","WVR","WVS","WVT","WVU","WVV", "WVW","WVX","WVY","WVZ","WWA","WWB","WWC","WWD","WWE","WWF","WWG","WWH","WWI","WWJ","WWK","WWL", "WWM","WWN","WWO","WWP","WWQ","WWR","WWS","WWT","WWU","WWV","WWW","WWX","WWY","WWZ","WXA","WXB", "WXC","WXD","WXE","WXF","WXG","WXH","WXI","WXJ","WXK","WXL","WXM","WXN","WXO","WXP","WXQ","WXR", "WXS","WXT","WXU","WXV","WXW","WXX","WXY","WXZ","WYA","WYB","WYC","WYD","WYE","WYF","WYG","WYH", "WYI","WYJ","WYK","WYL","WYM","WYN","WYO","WYP","WYQ","WYR","WYS","WYT","WYU","WYV","WYW","WYX", "WYY","WYZ","WZA","WZB","WZC","WZD","WZE","WZF","WZG","WZH","WZI","WZJ","WZK","WZL","WZM","WZN", "WZO","WZP","WZQ","WZR","WZS","WZT","WZU","WZV","WZW","WZX","WZY","WZZ","XAA","XAB","XAC","XAD", "XAE","XAF","XAG","XAH","XAI","XAJ","XAK","XAL","XAM","XAN","XAO","XAP","XAQ","XAR","XAS","XAT", "XAU","XAV","XAW","XAX","XAY","XAZ","XBA","XBB","XBC","XBD","XBE","XBF","XBG","XBH","XBI","XBJ", "XBK","XBL","XBM","XBN","XBO","XBP","XBQ","XBR","XBS","XBT","XBU","XBV","XBW","XBX","XBY","XBZ", "XCA","XCB","XCC","XCD","XCE","XCF","XCG","XCH","XCI","XCJ","XCK","XCL","XCM","XCN","XCO","XCP", "XCQ","XCR","XCS","XCT","XCU","XCV","XCW","XCX","XCY","XCZ","XDA","XDB","XDC","XDD","XDE","XDF", "XDG","XDH","XDI","XDJ","XDK","XDL","XDM","XDN","XDO","XDP","XDQ","XDR","XDS","XDT","XDU","XDV", "XDW","XDX","XDY","XDZ","XEA","XEB","XEC","XED","XEE","XEF","XEG","XEH","XEI","XEJ","XEK","XEL", "XEM","XEN","XEO","XEP","XEQ","XER","XES","XET","XEU","XEV","XEW","XEX","XEY","XEZ","XFA","XFB", "XFC","XFD","XFE","XFF","XFG","XFH","XFI","XFJ","XFK","XFL","XFM","XFN","XFO","XFP","XFQ","XFR", "XFS","XFT","XFU","XFV","XFW","XFX","XFY","XFZ","XGA","XGB","XGC","XGD","XGE","XGF","XGG","XGH", "XGI","XGJ","XGK","XGL","XGM","XGN","XGO","XGP","XGQ","XGR","XGS","XGT","XGU","XGV","XGW","XGX", "XGY","XGZ","XHA","XHB","XHC","XHD","XHE","XHF","XHG","XHH","XHI","XHJ","XHK","XHL","XHM","XHN", "XHO","XHP","XHQ","XHR","XHS","XHT","XHU","XHV","XHW","XHX","XHY","XHZ","XIA","XIB","XIC","XID", "XIE","XIF","XIG","XIH","XII","XIJ","XIK","XIL","XIM","XIN","XIO","XIP","XIQ","XIR","XIS","XIT", "XIU","XIV","XIW","XIX","XIY","XIZ","XJA","XJB","XJC","XJD","XJE","XJF","XJG","XJH","XJI","XJJ", "XJK","XJL","XJM","XJN","XJO","XJP","XJQ","XJR","XJS","XJT","XJU","XJV","XJW","XJX","XJY","XJZ", "XKA","XKB","XKC","XKD","XKE","XKF","XKG","XKH","XKI","XKJ","XKK","XKL","XKM","XKN","XKO","XKP", "XKQ","XKR","XKS","XKT","XKU","XKV","XKW","XKX","XKY","XKZ","XLA","XLB","XLC","XLD","XLE","XLF", "XLG","XLH","XLI","XLJ","XLK","XLL","XLM","XLN","XLO","XLP","XLQ","XLR","XLS","XLT","XLU","XLV", "XLW","XLX","XLY","XLZ","XMA","XMB","XMC","XMD","XME","XMF","XMG","XMH","XMI","XMJ","XMK","XML", "XMM","XMN","XMO","XMP","XMQ","XMR","XMS","XMT","XMU","XMV","XMW","XMX","XMY","XMZ","XNA","XNB", "XNC","XND","XNE","XNF","XNG","XNH","XNI","XNJ","XNK","XNL","XNM","XNN","XNO","XNP","XNQ","XNR", "XNS","XNT","XNU","XNV","XNW","XNX","XNY","XNZ","XOA","XOB","XOC","XOD","XOE","XOF","XOG","XOH", "XOI","XOJ","XOK","XOL","XOM","XON","XOO","XOP","XOQ","XOR","XOS","XOT","XOU","XOV","XOW","XOX", "XOY","XOZ","XPA","XPB","XPC","XPD","XPE","XPF","XPG","XPH","XPI","XPJ","XPK","XPL","XPM","XPN", "XPO","XPP","XPQ","XPR","XPS","XPT","XPU","XPV","XPW","XPX","XPY","XPZ","XQA","XQB","XQC","XQD", "XQE","XQF","XQG","XQH","XQI","XQJ","XQK","XQL","XQM","XQN","XQO","XQP","XQQ","XQR","XQS","XQT", "XQU","XQV","XQW","XQX","XQY","XQZ","XRA","XRB","XRC","XRD","XRE","XRF","XRG","XRH","XRI","XRJ", "XRK","XRL","XRM","XRN","XRO","XRP","XRQ","XRR","XRS","XRT","XRU","XRV","XRW","XRX","XRY","XRZ", "XSA","XSB","XSC","XSD","XSE","XSF","XSG","XSH","XSI","XSJ","XSK","XSL","XSM","XSN","XSO","XSP", "XSQ","XSR","XSS","XST","XSU","XSV","XSW","XSX","XSY","XSZ","XTA","XTB","XTC","XTD","XTE","XTF", "XTG","XTH","XTI","XTJ","XTK","XTL","XTM","XTN","XTO","XTP","XTQ","XTR","XTS","XTT","XTU","XTV", "XTW","XTX","XTY","XTZ","XUA","XUB","XUC","XUD","XUE","XUF","XUG","XUH","XUI","XUJ","XUK","XUL", "XUM","XUN","XUO","XUP","XUQ","XUR","XUS","XUT","XUU","XUV","XUW","XUX","XUY","XUZ","XVA","XVB", "XVC","XVD","XVE","XVF","XVG","XVH","XVI","XVJ","XVK","XVL","XVM","XVN","XVO","XVP","XVQ","XVR", "XVS","XVT","XVU","XVV","XVW","XVX","XVY","XVZ","XWA","XWB","XWC","XWD","XWE","XWF","XWG","XWH", "XWI","XWJ","XWK","XWL","XWM","XWN","XWO","XWP","XWQ","XWR","XWS","XWT","XWU","XWV","XWW","XWX", "XWY","XWZ","XXA","XXB","XXC","XXD","XXE","XXF","XXG","XXH","XXI","XXJ","XXK","XXL","XXM","XXN", "XXO","XXP","XXQ","XXR","XXS","XXT","XXU","XXV","XXW","XXX","XXY","XXZ","XYA","XYB","XYC","XYD", "XYE","XYF","XYG","XYH","XYI","XYJ","XYK","XYL","XYM","XYN","XYO","XYP","XYQ","XYR","XYS","XYT", "XYU","XYV","XYW","XYX","XYY","XYZ","XZA","XZB","XZC","XZD","XZE","XZF","XZG","XZH","XZI","XZJ", "XZK","XZL","XZM","XZN","XZO","XZP","XZQ","XZR","XZS","XZT","XZU","XZV","XZW","XZX","XZY","XZZ", "YAA","YAB","YAC","YAD", "YAE","YAF","YAG","YAH","YAI","YAJ","YAK","YAL","YAM","YAN","YAO","YAP","YAQ","YAR","YAS","YAT", "YAU","YAV","YAW","YAX","YAY","YAZ","YBA","YBB","YBC","YBD","YBE","YBF","YBG","YBH","YBI","YBJ", "YBK","YBL","YBM","YBN","YBO","YBP","YBQ","YBR","YBS","YBT","YBU","YBV","YBW","YBX","YBY","YBZ", "YCA","YCB","YCC","YCD","YCE","YCF","YCG","YCH","YCI","YCJ","YCK","YCL","YCM","YCN","YCO","YCP", "YCQ","YCR","YCS","YCT","YCU","YCV","YCW","YCX","YCY","YCZ","YDA","YDB","YDC","YDD","YDE","YDF", "YDG","YDH","YDI","YDJ","YDK","YDL","YDM","YDN","YDO","YDP","YDQ","YDR","YDS","YDT","YDU","YDV", "YDW","YDX","YDY","YDZ","YEA","YEB","YEC","YED","YEE","YEF","YEG","YEH","YEI","YEJ","YEK","YEL", "YEM","YEN","YEO","YEP","YEQ","YER","YES","YET","YEU","YEV","YEW","YEX","YEY","YEZ","YFA","YFB", "YFC","YFD","YFE","YFF","YFG","YFH","YFI","YFJ","YFK","YFL","YFM","YFN","YFO","YFP","YFQ","YFR", "YFS","YFT","YFU","YFV","YFW","YFX","YFY","YFZ","YGA","YGB","YGC","YGD","YGE","YGF","YGG","YGH", "YGI","YGJ","YGK","YGL","YGM","YGN","YGO","YGP","YGQ","YGR","YGS","YGT","YGU","YGV","YGW","YGX", "YGY","YGZ","YHA","YHB","YHC","YHD","YHE","YHF","YHG","YHH","YHI","YHJ","YHK","YHL","YHM","YHN", "YHO","YHP","YHQ","YHR","YHS","YHT","YHU","YHV","YHW","YHX","YHY","YHZ","YIA","YIB","YIC","YID", "YIE","YIF","YIG","YIH","YII","YIJ","YIK","YIL","YIM","YIN","YIO","YIP","YIQ","YIR","YIS","YIT", "YIU","YIV","YIW","YIX","YIY","YIZ","YJA","YJB","YJC","YJD","YJE","YJF","YJG","YJH","YJI","YJJ", "YJK","YJL","YJM","YJN","YJO","YJP","YJQ","YJR","YJS","YJT","YJU","YJV","YJW","YJX","YJY","YJZ", "YKA","YKB","YKC","YKD","YKE","YKF","YKG","YKH","YKI","YKJ","YKK","YKL","YKM","YKN","YKO","YKP", "YKQ","YKR","YKS","YKT","YKU","YKV","YKW","YKX","YKY","YKZ","YLA","YLB","YLC","YLD","YLE","YLF", "YLG","YLH","YLI","YLJ","YLK","YLL","YLM","YLN","YLO","YLP","YLQ","YLR","YLS","YLT","YLU","YLV", "YLW","YLX","YLY","YLZ","YMA","YMB","YMC","YMD","YME","YMF","YMG","YMH","YMI","YMJ","YMK","YML", "YMM","YMN","YMO","YMP","YMQ","YMR","YMS","YMT","YMU","YMV","YMW","YMX","YMY","YMZ","YNA","YNB", "YNC","YND","YNE","YNF","YNG","YNH","YNI","YNJ","YNK","YNL","YNM","YNN","YNO","YNP","YNQ","YNR", "YNS","YNT","YNU","YNV","YNW","YNX","YNY","YNZ","YOA","YOB","YOC","YOD","YOE","YOF","YOG","YOH", "YOI","YOJ","YOK","YOL","YOM","YON","YOO","YOP","YOQ","YOR","YOS","YOT","YOU","YOV","YOW","YOX", "YOY","YOZ","YPA","YPB","YPC","YPD","YPE","YPF","YPG","YPH","YPI","YPJ","YPK","YPL","YPM","YPN", "YPO","YPP","YPQ","YPR","YPS","YPT","YPU","YPV","YPW","YPX","YPY","YPZ","YQA","YQB","YQC","YQD", "YQE","YQF","YQG","YQH","YQI","YQJ","YQK","YQL","YQM","YQN","YQO","YQP","YQQ","YQR","YQS","YQT", "YQU","YQV","YQW","YQX","YQY","YQZ","YRA","YRB","YRC","YRD","YRE","YRF","YRG","YRH","YRI","YRJ", "YRK","YRL","YRM","YRN","YRO","YRP","YRQ","YRR","YRS","YRT","YRU","YRV","YRW","YRX","YRY","YRZ", "YSA","YSB","YSC","YSD","YSE","YSF","YSG","YSH","YSI","YSJ","YSK","YSL","YSM","YSN","YSO","YSP", "YSQ","YSR","YSS","YST","YSU","YSV","YSW","YSX","YSY","YSZ","YTA","YTB","YTC","YTD","YTE","YTF", "YTG","YTH","YTI","YTJ","YTK","YTL","YTM","YTN","YTO","YTP","YTQ","YTR","YTS","YTT","YTU","YTV", "YTW","YTX","YTY","YTZ","YUA","YUB","YUC","YUD","YUE","YUF","YUG","YUH","YUI","YUJ","YUK","YUL", "YUM","YUN","YUO","YUP","YUQ","YUR","YUS","YUT","YUU","YUV","YUW","YUX","YUY","YUZ","YVA","YVB", "YVC","YVD","YVE","YVF","YVG","YVH","YVI","YVJ","YVK","YVL","YVM","YVN","YVO","YVP","YVQ","YVR", "YVS","YVT","YVU","YVV","YVW","YVX","YVY","YVZ","YWA","YWB","YWC","YWD","YWE","YWF","YWG","YWH", "YWI","YWJ","YWK","YWL","YWM","YWN","YWO","YWP","YWQ","YWR","YWS","YWT","YWU","YWV","YWW","YWX", "YWY","YWZ","YXA","YXB","YXC","YXD","YXE","YXF","YXG","YXH","YXI","YXJ","YXK","YXL","YXM","YXN", "YXO","YXP","YXQ","YXR","YXS","YXT","YXU","YXV","YXW","YXX","YXY","YXZ","YYA","YYB","YYC","YYD", "YYE","YYF","YYG","YYH","YYI","YYJ","YYK","YYL","YYM","YYN","YYO","YYP","YYQ","YYR","YYS","YYT", "YYU","YYV","YYW","YYX","YYY","YYZ","YZA","YZB","YZC","YZD","YZE","YZF","YZG","YZH","YZI","YZJ", "YZK","YZL","YZM","YZN","YZO","YZP","YZQ","YZR","YZS","YZT","YZU","YZV","YZW","YZX","YZY","YZZ", "ZAA","ZAB","ZAC","ZAD", "ZAE","ZAF","ZAG","ZAH","ZAI","ZAJ","ZAK","ZAL","ZAM","ZAN","ZAO","ZAP","ZAQ","ZAR","ZAS","ZAT", "ZAU","ZAV","ZAW","ZAX","ZAY","ZAZ","ZBA","ZBB","ZBC","ZBD","ZBE","ZBF","ZBG","ZBH","ZBI","ZBJ", "ZBK","ZBL","ZBM","ZBN","ZBO","ZBP","ZBQ","ZBR","ZBS","ZBT","ZBU","ZBV","ZBW","ZBX","ZBY","ZBZ", "ZCA","ZCB","ZCC","ZCD","ZCE","ZCF","ZCG","ZCH","ZCI","ZCJ","ZCK","ZCL","ZCM","ZCN","ZCO","ZCP", "ZCQ","ZCR","ZCS","ZCT","ZCU","ZCV","ZCW","ZCX","ZCY","ZCZ","ZDA","ZDB","ZDC","ZDD","ZDE","ZDF", "ZDG","ZDH","ZDI","ZDJ","ZDK","ZDL","ZDM","ZDN","ZDO","ZDP","ZDQ","ZDR","ZDS","ZDT","ZDU","ZDV", "ZDW","ZDX","ZDY","ZDZ","ZEA","ZEB","ZEC","ZED","ZEE","ZEF","ZEG","ZEH","ZEI","ZEJ","ZEK","ZEL", "ZEM","ZEN","ZEO","ZEP","ZEQ","ZER","ZES","ZET","ZEU","ZEV","ZEW","ZEX","ZEY","ZEZ","ZFA","ZFB", "ZFC","ZFD","ZFE","ZFF","ZFG","ZFH","ZFI","ZFJ","ZFK","ZFL","ZFM","ZFN","ZFO","ZFP","ZFQ","ZFR", "ZFS","ZFT","ZFU","ZFV","ZFW","ZFX","ZFY","ZFZ","ZGA","ZGB","ZGC","ZGD","ZGE","ZGF","ZGG","ZGH", "ZGI","ZGJ","ZGK","ZGL","ZGM","ZGN","ZGO","ZGP","ZGQ","ZGR","ZGS","ZGT","ZGU","ZGV","ZGW","ZGX", "ZGY","ZGZ","ZHA","ZHB","ZHC","ZHD","ZHE","ZHF","ZHG","ZHH","ZHI","ZHJ","ZHK","ZHL","ZHM","ZHN", "ZHO","ZHP","ZHQ","ZHR","ZHS","ZHT","ZHU","ZHV","ZHW","ZHX","ZHY","ZHZ","ZIA","ZIB","ZIC","ZID", "ZIE","ZIF","ZIG","ZIH","ZII","ZIJ","ZIK","ZIL","ZIM","ZIN","ZIO","ZIP","ZIQ","ZIR","ZIS","ZIT", "ZIU","ZIV","ZIW","ZIX","ZIY","ZIZ","ZJA","ZJB","ZJC","ZJD","ZJE","ZJF","ZJG","ZJH","ZJI","ZJJ", "ZJK","ZJL","ZJM","ZJN","ZJO","ZJP","ZJQ","ZJR","ZJS","ZJT","ZJU","ZJV","ZJW","ZJX","ZJY","ZJZ", "ZKA","ZKB","ZKC","ZKD","ZKE","ZKF","ZKG","ZKH","ZKI","ZKJ","ZKK","ZKL","ZKM","ZKN","ZKO","ZKP", "ZKQ","ZKR","ZKS","ZKT","ZKU","ZKV","ZKW","ZKX","ZKY","ZKZ","ZLA","ZLB","ZLC","ZLD","ZLE","ZLF", "ZLG","ZLH","ZLI","ZLJ","ZLK","ZLL","ZLM","ZLN","ZLO","ZLP","ZLQ","ZLR","ZLS","ZLT","ZLU","ZLV", "ZLW","ZLX","ZLY","ZLZ","ZMA","ZMB","ZMC","ZMD","ZME","ZMF","ZMG","ZMH","ZMI","ZMJ","ZMK","ZML", "ZMM","ZMN","ZMO","ZMP","ZMQ","ZMR","ZMS","ZMT","ZMU","ZMV","ZMW","ZMX","ZMY","ZMZ","ZNA","ZNB", "ZNC","ZND","ZNE","ZNF","ZNG","ZNH","ZNI","ZNJ","ZNK","ZNL","ZNM","ZNN","ZNO","ZNP","ZNQ","ZNR", "ZNS","ZNT","ZNU","ZNV","ZNW","ZNX","ZNY","ZNZ","ZOA","ZOB","ZOC","ZOD","ZOE","ZOF","ZOG","ZOH", "ZOI","ZOJ","ZOK","ZOL","ZOM","ZON","ZOO","ZOP","ZOQ","ZOR","ZOS","ZOT","ZOU","ZOV","ZOW","ZOX", "ZOY","ZOZ","ZPA","ZPB","ZPC","ZPD","ZPE","ZPF","ZPG","ZPH","ZPI","ZPJ","ZPK","ZPL","ZPM","ZPN", "ZPO","ZPP","ZPQ","ZPR","ZPS","ZPT","ZPU","ZPV","ZPW","ZPX","ZPY","ZPZ","ZQA","ZQB","ZQC","ZQD", "ZQE","ZQF","ZQG","ZQH","ZQI","ZQJ","ZQK","ZQL","ZQM","ZQN","ZQO","ZQP","ZQQ","ZQR","ZQS","ZQT", "ZQU","ZQV","ZQW","ZQX","ZQY","ZQZ","ZRA","ZRB","ZRC","ZRD","ZRE","ZRF","ZRG","ZRH","ZRI","ZRJ", "ZRK","ZRL","ZRM","ZRN","ZRO","ZRP","ZRQ","ZRR","ZRS","ZRT","ZRU","ZRV","ZRW","ZRX","ZRY","ZRZ", "ZSA","ZSB","ZSC","ZSD","ZSE","ZSF","ZSG","ZSH","ZSI","ZSJ","ZSK","ZSL","ZSM","ZSN","ZSO","ZSP", "ZSQ","ZSR","ZSS","ZST","ZSU","ZSV","ZSW","ZSX","ZSY","ZSZ","ZTA","ZTB","ZTC","ZTD","ZTE","ZTF", "ZTG","ZTH","ZTI","ZTJ","ZTK","ZTL","ZTM","ZTN","ZTO","ZTP","ZTQ","ZTR","ZTS","ZTT","ZTU","ZTV", "ZTW","ZTX","ZTY","ZTZ","ZUA","ZUB","ZUC","ZUD","ZUE","ZUF","ZUG","ZUH","ZUI","ZUJ","ZUK","ZUL", "ZUM","ZUN","ZUO","ZUP","ZUQ","ZUR","ZUS","ZUT","ZUU","ZUV","ZUW","ZUX","ZUY","ZUZ","ZVA","ZVB", "ZVC","ZVD","ZVE","ZVF","ZVG","ZVH","ZVI","ZVJ","ZVK","ZVL","ZVM","ZVN","ZVO","ZVP","ZVQ","ZVR", "ZVS","ZVT","ZVU","ZVV","ZVW","ZVX","ZVY","ZVZ","ZWA","ZWB","ZWC","ZWD","ZWE","ZWF","ZWG","ZWH", "ZWI","ZWJ","ZWK","ZWL","ZWM","ZWN","ZWO","ZWP","ZWQ","ZWR","ZWS","ZWT","ZWU","ZWV","ZWW","ZWX", "ZWY","ZWZ","ZXA","ZXB","ZXC","ZXD","ZXE","ZXF","ZXG","ZXH","ZXI","ZXJ","ZXK","ZXL","ZXM","ZXN", "ZXO","ZXP","ZXQ","ZXR","ZXS","ZXT","ZXU","ZXV","ZXW","ZXX","ZXY","ZXZ","ZYA","ZYB","ZYC","ZYD", "ZYE","ZYF","ZYG","ZYH","ZYI","ZYJ","ZYK","ZYL","ZYM","ZYN","ZYO","ZYP","ZYQ","ZYR","ZYS","ZYT", "ZYU","ZYV","ZYW","ZYX","ZYY","ZYZ","ZZA","ZZB","ZZC","ZZD","ZZE","ZZF","ZZG","ZZH","ZZI","ZZJ", "ZZK","ZZL","ZZM","ZZN","ZZO","ZZP","ZZQ","ZZR","ZZS","ZZT","ZZU","ZZV","ZZW","ZZX","ZZY","ZZZ" }; /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ static char d26[][3] = { "AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK","AL","AM","AN","AO","AP", "AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA","BB","BC","BD","BE","BF", "BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ","BR","BS","BT","BU","BV", "BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG","CH","CI","CJ","CK","CL", "CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV","CW","CX","CY","CZ","DA","DB", "DC","DD","DE","DF","DG","DH","DI","DJ","DK","DL","DM","DN","DO","DP","DQ","DR", "DS","DT","DU","DV","DW","DX","DY","DZ","EA","EB","EC","ED","EE","EF","EG","EH", "EI","EJ","EK","EL","EM","EN","EO","EP","EQ","ER","ES","ET","EU","EV","EW","EX", "EY","EZ","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM","FN", "FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","GA","GB","GC","GD", "GE","GF","GG","GH","GI","GJ","GK","GL","GM","GN","GO","GP","GQ","GR","GS","GT", "GU","GV","GW","GX","GY","GZ","HA","HB","HC","HD","HE","HF","HG","HH","HI","HJ", "HK","HL","HM","HN","HO","HP","HQ","HR","HS","HT","HU","HV","HW","HX","HY","HZ", "IA","IB","IC","ID","IE","IF","IG","IH","II","IJ","IK","IL","IM","IN","IO","IP", "IQ","IR","IS","IT","IU","IV","IW","IX","IY","IZ","JA","JB","JC","JD","JE","JF", "JG","JH","JI","JJ","JK","JL","JM","JN","JO","JP","JQ","JR","JS","JT","JU","JV", "JW","JX","JY","JZ","KA","KB","KC","KD","KE","KF","KG","KH","KI","KJ","KK","KL", "KM","KN","KO","KP","KQ","KR","KS","KT","KU","KV","KW","KX","KY","KZ","LA","LB", "LC","LD","LE","LF","LG","LH","LI","LJ","LK","LL","LM","LN","LO","LP","LQ","LR", "LS","LT","LU","LV","LW","LX","LY","LZ","MA","MB","MC","MD","ME","MF","MG","MH", "MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX", "MY","MZ","NA","NB","NC","ND","NE","NF","NG","NH","NI","NJ","NK","NL","NM","NN", "NO","NP","NQ","NR","NS","NT","NU","NV","NW","NX","NY","NZ","OA","OB","OC","OD", "OE","OF","OG","OH","OI","OJ","OK","OL","OM","ON","OO","OP","OQ","OR","OS","OT", "OU","OV","OW","OX","OY","OZ","PA","PB","PC","PD","PE","PF","PG","PH","PI","PJ", "PK","PL","PM","PN","PO","PP","PQ","PR","PS","PT","PU","PV","PW","PX","PY","PZ", "QA","QB","QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL","QM","QN","QO","QP", "QQ","QR","QS","QT","QU","QV","QW","QX","QY","QZ","RA","RB","RC","RD","RE","RF", "RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT","RU","RV", "RW","RX","RY","RZ","SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL", "SM","SN","SO","SP","SQ","SR","SS","ST","SU","SV","SW","SX","SY","SZ","TA","TB", "TC","TD","TE","TF","TG","TH","TI","TJ","TK","TL","TM","TN","TO","TP","TQ","TR", "TS","TT","TU","TV","TW","TX","TY","TZ","UA","UB","UC","UD","UE","UF","UG","UH", "UI","UJ","UK","UL","UM","UN","UO","UP","UQ","UR","US","UT","UU","UV","UW","UX", "UY","UZ","VA","VB","VC","VD","VE","VF","VG","VH","VI","VJ","VK","VL","VM","VN", "VO","VP","VQ","VR","VS","VT","VU","VV","VW","VX","VY","VZ","WA","WB","WC","WD", "WE","WF","WG","WH","WI","WJ","WK","WL","WM","WN","WO","WP","WQ","WR","WS","WT", "WU","WV","WW","WX","WY","WZ","XA","XB","XC","XD","XE","XF","XG","XH","XI","XJ", "XK","XL","XM","XN","XO","XP","XQ","XR","XS","XT","XU","XV","XW","XX","XY","XZ", "YA","YB","YC","YD","YE","YF","YG","YH","YI","YJ","YK","YL","YM","YN","YO","YP", "YQ","YR","YS","YT","YU","YV","YW","YX","YY","YZ","ZA","ZB","ZC","ZD","ZE","ZF", "ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR","ZS","ZT","ZU","ZV", "ZW","ZX","ZY","ZZ" }; /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Also tabulate 26 base-26 chars. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ static const char *c26 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* added const 2007-09-26 DT */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Weight scheme for check character . ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #define N_UNIQUE_WEIGHTS 12 static int weights_for_checksum[N_UNIQUE_WEIGHTS] = { 1,3,5,7,9,11,15,17,19,21,23,25 }; /*^^^ co-primes with 26 which are < 26 */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_1(const unsigned char *a) { UINT32 b0, b1,h; b0 = (UINT32) a[0]; /* 1111 1111 */ #ifndef FIX_BASE26_ENC_BUG b1 = (UINT32) ( a[1] & 0x3f ); /* 0011 1111 */ h = (UINT32) ( b0 | b1 << 8 ); #else b1 = (UINT32) ( a[1] & 0xfc ); /* 1111 1100 */ h = (UINT32) ( b0 << 8 | b1 ) >> 2; #endif return t26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 2nd 14-bit triplet (bits 14..27) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_2(const unsigned char *a) { UINT32 b0, b1, b2, h; #ifndef FIX_BASE26_ENC_BUG b0 = (UINT32) ( a[1] & 0xc0); /* 1100 0000 */ b1 = (UINT32) ( a[2] ); /* 1111 1111 */ b2 = (UINT32) ( a[3] & 0x0f ); /* 0000 1111 */ h = (UINT32)( b0 | b1 << 8 | b2 << 16 ) >> 6 ; #else b0 = (UINT32) ( a[1] & 0x03); /* 0000 0011 */ b1 = (UINT32) ( a[2] ); /* 1111 1111 */ b2 = (UINT32) ( a[3] & 0xf0 ); /* 1111 0000 */ h = (UINT32)( b0 << 16 | b1 << 8 | b2 ) >> 4 ; #endif return t26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 3rd 14-bit triplet (bits 28..41) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_3(const unsigned char *a) { UINT32 b0, b1, b2, h; #ifndef FIX_BASE26_ENC_BUG b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ b1 = (UINT32) ( a[4] ); /* 1111 1111 */ b2 = (UINT32) ( a[5] & 0x03 ); /* 0000 0011 */ h = (UINT32) ( b0 | b1 << 8 | b2 << 16 ) >> 4 ; #else b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ b1 = (UINT32) ( a[4] ); /* 1111 1111 */ b2 = (UINT32) ( a[5] & 0xc0 ); /* 1100 0000 */ h = (UINT32) ( b0 << 16 | b1 << 8 | b2 ) >> 6 ; #endif return t26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 4th 14-bit triplet (bits 42..55) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_4(const unsigned char *a) { UINT32 b0, b1, h; #ifndef FIX_BASE26_ENC_BUG b0 = (UINT32) ( a[5] & 0xfc); /* 1111 1100 */ b1 = (UINT32) ( a[6] ); /* 1111 1111 */ h = (UINT32) ( b0 | b1 << 8 ) >> 2 ; #else b0 = (UINT32) ( a[5] & 0x3f); /* 0011 1111 */ b1 = (UINT32) ( a[6] ); /* 1111 1111 */ h = (UINT32) ( b0 << 8 | b1 ) ; #endif return t26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tail dublets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a4 a3 a2 a1 a0 28-36: 0001 1111 1111 0000 0000 0000 0000 0000 0000 0000 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get dublet (bits 28..36) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_dublet_for_bits_28_to_36(unsigned char *a) { UINT32 b0, b1, h; #ifndef FIX_BASE26_ENC_BUG b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ b1 = (UINT32) ( a[4] & 0x1f ); /* 0001 1111 */ h = (UINT32)( b0 | b1 << 8 ) >> 4 ; #else b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ b1 = (UINT32) ( a[4] & 0xf8 ); /* 1111 1000 */ h = (UINT32)( b0 << 8 | b1 ) >> 3 ; #endif return d26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a9 a8 a7 56-64: 0000 0000 0000 0001 1111 1111 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get dublet (bits 56..64) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_dublet_for_bits_56_to_64(unsigned char *a) { UINT32 b0, b1, h; #ifndef FIX_BASE26_ENC_BUG b0 = (UINT32) ( a[7] ); /* 1111 1111 */ b1 = (UINT32) ( a[8] & 0x01 ); /* 0000 0001 */ h = (UINT32)( b0 | b1 << 8 ); #else b0 = (UINT32) ( a[7] ); /* 1111 1111 */ b1 = (UINT32) ( a[8] & 0x80 ); /* 1000 0000 */ h = (UINT32)( b0 << 8 | b1 ) >> 7; #endif return d26[h]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Calculate check character A..Z for the string. NB: ignore delimiter dashes. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ char base26_checksum(const char *str) { size_t slen, j, jj=0, checksum = 0; char c; slen = strlen(str); for (j=0; j < slen; j++) { c = str[j]; if (c=='-') continue; checksum+= weights_for_checksum[jj]*c; jj++; if (jj > N_UNIQUE_WEIGHTS - 1) jj = 0; } return c26[checksum%26]; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get hash extension in hexadecimal representation for the major block. Len(extension) = 256 - 65 = 191 bit. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra) { unsigned char c; int i, j, start_byte=8; #ifndef FIX_BASE26_ENC_BUG c = a[start_byte] & 0xfe ; /* 1111 1110 */ #else c = a[start_byte] & 0x7f ; /* 0111 1111 */ #endif j = sprintf(szXtra,"%02x", c); for( i = start_byte+1; i < 32; i++ ) j+= sprintf(szXtra+j,"%02x", a[i]); } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get hash extension in hexadecimal representation for the minor block. Len(extension) = 256 - 37 = 219 bit. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra) { unsigned char c; int i, j, start_byte=4; #ifndef FIX_BASE26_ENC_BUG c = a[start_byte] & 0xe0 ; /* 1110 0000 */ #else c = a[start_byte] & 0x07 ; /* 0000 0111 */ #endif j = sprintf(szXtra,"%02x", c); for( i = start_byte+1; i < 32; i++ ) j+= sprintf(szXtra+j,"%02x", a[i]); } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ikey_base26.h000066400000000000000000000117321271037650300240310ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __IKEY_BASE26_H__ #define __IKEY_BASE26_H__ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Base-26 encoding procedures. 'Base26' characters here are considered to be uppercase English letters 'A..Z' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* Uncomment the next line to fix base-26 encoding bug */ /*#define FIX_BASE26_ENC_BUG 1*/ typedef unsigned int UINT32; typedef unsigned short int UINT16; #ifdef __cplusplus extern "C" { #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_1(const unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 2nd 14-bit triplet (bits 14..27) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_2(const unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 3rd 14-bit triplet (bits 28..41) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_3(const unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get a character representing 4th 14-bit triplet (bits 42..55) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_triplet_4(const unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tail dublets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get dublet (bits 28..36) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_dublet_for_bits_28_to_36(unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get dublet (bits 56..64) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ const char* base26_dublet_for_bits_56_to_64(unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Calculate check character for the string. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ char base26_checksum(const char *str); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get hash extension in hexadecimal representation for the major block. Len(extension) = 256 - 65 = 191 bit. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get hash extension in hexadecimal representation for the minor block. Len(extension) = 256 - 37 = 219 bit. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Used instead of isupper() to avoid locale interference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #define isbase26(_c) ( ((unsigned)(_c) >= 'A') && ((unsigned)(_c) <= 'Z') ) #ifdef __cplusplus } #endif #endif /*^^^ __IKEY_BASE26_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/ikey_dll.c000066400000000000000000000423401271037650300235140ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChIKey: calculation of hash for InChI string Uses truncated SHA-256 function. SHA-256 implementation: Copyright (C) Brainspark B.V., see files sha2.c, sha2.h. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #ifdef _MSC_VER #if _MSC_VER > 1000 #pragma warning( disable : 4996 ) #endif #endif #include #include #include #include #include #include "sha2.h" #include "ikey_base26.h" #include "ichisize.h" #include "mode.h" #include "inchi_api.h" #include "util.h" /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Local options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #define INCHIKEY_DEBUG 0 /* 2 */ #define INCHIKEY_FLAG_OK 0 #define INCHIKEY_NOT_VALID_FLAG 1 enum { MINOUTLENGTH=256 }; /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Local functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void fprint_digest(FILE* fw, const char *header, unsigned char *a); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EXPORTED FUNCTIONS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey) { if ( strlen(szINCHISource) < LEN_INCHI_STRING_PREFIX+3 ) return INCHIKEY_INVALID_STD_INCHI; if (szINCHISource[LEN_INCHI_STRING_PREFIX+1]!='S') return INCHIKEY_INVALID_STD_INCHI; return GetINCHIKeyFromINCHI(szINCHISource, 0, 0, szINCHIKey, (char *) NULL, (char *) NULL); } EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, const int xtra1, const int xtra2, char* szINCHIKey, char* szXtra1, char* szXtra2) { int ret = INCHIKEY_OK; int ret1 = INCHIKEY_OK; int cn; size_t slen, i, j, jproto=0, ncp, pos_slash1=0; char *str = NULL, *smajor = NULL, *sminor = NULL, *sproto=NULL, *stmp = NULL, tmp[MINOUTLENGTH]; unsigned char digest_major[32], digest_minor[32]; char flagstd = 'S', /* standard key */ flagnonstd = 'N', /* non-standard key */ flagver = 'A', /* InChI v. 1 */ flagproto = 'N'; /* no [de]protonization , by default */ int nprotons; /* Protonization encoding: N 0 O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 A < -12 or > +12 */ static const char *pplus = "OPQRSTUVWXYZ"; static const char *pminus = "MLKJIHGFEDCB"; int bStdFormat = 0; size_t bytelen = 32; /* Check if input is a valid InChI string */ /* .. non-empty */ if (szINCHISource==NULL) return INCHIKEY_EMPTY_INPUT; slen = strlen(szINCHISource); /* .. has valid prefix */ if (slen standard InChIKey */ bStdFormat = 1; pos_slash1++; } /* .. has trailing slash in the right place */ if (szINCHISource[pos_slash1]!='/') return INCHIKEY_INVALID_INCHI_PREFIX; /* .. the rest of source string contains at least one a..Z0.9 or slash */ /* TODO: improve/add full string check */ if (!isalnum(szINCHISource[pos_slash1+1] ) && ( szINCHISource[pos_slash1+1]!='/' ) ) return INCHIKEY_INVALID_INCHI; /*^^^ Ok. Will use a local copy of the source. */ extract_inchi_substring(&str, szINCHISource, slen); if (NULL==str) { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } slen = strlen(str); /*^^^ Make buffers. */ smajor = (char*) inchi_calloc( slen+1, sizeof(char)); if (NULL==smajor) { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } sminor = (char*) inchi_calloc( 2*slen + 2, sizeof(char)); /* we may double the length ... */ if (NULL==sminor) { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } stmp = (char*) inchi_calloc( slen+1, sizeof(char)); if (NULL==stmp) { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } sproto = (char*) inchi_calloc( slen+1, sizeof(char)); if (NULL==sproto) { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } szINCHIKey[0] = '\0'; /*^^^ Extract the major block. */ smajor[0] = '\0'; for (j = pos_slash1 + 1; j < slen-1; j++) { if (str[j]=='/') { cn = str[j+1]; switch (cn) { /* anything allowed from a major part */ case 'c': case 'h': case 'q': continue; /* "/p"; protons now go to to special string, not to minor hash */ case 'p': jproto = j; continue; /* "/f", "/r" : may not occur in stdInChI */ case 'f': case 'r': if ( bStdFormat ) { ret = INCHIKEY_INVALID_STD_INCHI; goto fin; } break; /* anything allowed from a minor part */ default: break; } break; } } j++; if (j==slen) j++; else j--; if (jproto) ncp = jproto - pos_slash1 - 1; else ncp = j - pos_slash1 - 1; /*^^^ Trim 'InChI=1[S]/' */ memcpy(smajor,str+pos_slash1+1, ncp*sizeof(str[0])); smajor[ncp]='\0'; /* Treat protonization */ if (jproto) { /* 2009-01-07 fix bug/typo: assigned incorrect length to the protonation segment of /* source string ( was sproto[ncp]='\0'; should be sproto[lenproto]='\0'; ) */ int lenproto = j - (int) jproto; if (lenproto<3) { /* empty "/p", should not occur */ ret = INCHIKEY_INVALID_INCHI; goto fin; } memcpy(sproto,str+pos_slash1+ncp+1, lenproto*sizeof(str[0])); sproto[lenproto]='\0'; nprotons = strtol( sproto+2, NULL, 10 ); if (nprotons > 0) { if (nprotons > 12) flagproto = 'A'; else flagproto = pplus[nprotons-1]; } else if (nprotons < 0) { if (nprotons < -12) flagproto = 'A'; else flagproto = pminus[-nprotons-1]; } else { /* should never occur */ ret = INCHIKEY_INVALID_STD_INCHI; goto fin; } } /*^^^ Extract the minor block. */ if (j != slen+1) /*^^^ check that something exists at right.*/ { ncp = slen-j; memcpy(sminor,str+j, (ncp)*sizeof(str[0])); sminor[ncp]='\0'; } else sminor[0]='\0'; #if INCHIKEY_DEBUG fprintf(stdout,"Source: {%-s}\n",str); fprintf(stdout,"SMajor: {%-s}\n",smajor); fprintf(stdout,"SMinor: {%-s}\n",sminor); fprintf(stdout,"SProto: {%-s}\n",sproto); #endif /*^^^ Compute and compose the InChIKey string. */ /*^^^ Major hash sub-string. */ for( i = 0; i < 32; i++ ) digest_major[i] = 0; sha2_csum( (unsigned char *) smajor, (int) strlen(smajor), digest_major ); sprintf(tmp,"%-.3s%-.3s%-.3s%-.3s%-.2s", base26_triplet_1(digest_major), base26_triplet_2(digest_major), base26_triplet_3(digest_major), base26_triplet_4(digest_major), base26_dublet_for_bits_56_to_64(digest_major)); strcat(szINCHIKey, tmp); #if (INCHIKEY_DEBUG>1) fprint_digest(stderr, "Major hash, full SHA-256",digest_major); #endif /*^^^ Minor hash sub-string. */ for( i = 0; i < 32; i++ ) digest_minor[i] = 0; slen = strlen(sminor); if ((slen>0)&&(slen<255)) { strcpy(stmp, sminor); strcpy(sminor+slen,stmp); } sha2_csum( (unsigned char *) sminor, (int) strlen(sminor), digest_minor ); #if (INCHIKEY_DEBUG>1) fprint_digest(stderr, "Minor hash, full SHA-256",digest_minor); #endif strcat(szINCHIKey, "-"); sprintf(tmp,"%-.3s%-.3s%-.2s", base26_triplet_1(digest_minor), base26_triplet_2(digest_minor), base26_dublet_for_bits_28_to_36(digest_minor)); strcat(szINCHIKey, tmp); /* Append a standard/non-standard flag */ slen = strlen(szINCHIKey); if ( bStdFormat ) szINCHIKey[slen] = flagstd; else szINCHIKey[slen] = flagnonstd; /*^^^ Append InChI v.1 flag */ szINCHIKey[slen+1] = flagver; /*^^^ Append dash */ szINCHIKey[slen+2] = '-'; /*^^^ Append protonization flag */ szINCHIKey[slen+3] = flagproto; szINCHIKey[slen+4] = '\0'; #if INCHIKEY_DEBUG fprintf(stdout,"szINCHIKey: {%-s}\n",szINCHIKey); #endif /* Hash extensions */ if ( xtra1 && szXtra1 ) { get_xtra_hash_major_hex(digest_major, szXtra1); #if INCHIKEY_DEBUG fprintf(stderr,"XHash1=%-s\n",szXtra1); fprintf(stderr,"j=%-d\n",j); #endif } if ( xtra2 && szXtra2 ) { get_xtra_hash_minor_hex(digest_minor, szXtra2); #if INCHIKEY_DEBUG fprintf(stderr,"XHash2=%-s\n",szXtra2); fprintf(stderr,"j=%-d\n",j); #endif } fin:if (NULL!=str) inchi_free(str); if (NULL!=smajor) inchi_free(smajor); if (NULL!=sminor) inchi_free(sminor); if (NULL!=stmp) inchi_free(stmp); if (NULL!=sproto) inchi_free(sproto); if ( (ret==INCHIKEY_OK) && (ret1!=INCHIKEY_OK) ) ret = ret1; return ret; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Check if the string represents valid InChIKey. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey) { size_t slen, j; slen = strlen(szINCHIKey); /*^^^ Proper length is 27 */ if (slen != 27) return INCHIKEY_INVALID_LENGTH; /*^^^ Should have dash in 14-th position */ if (szINCHIKey[14] !='-') return INCHIKEY_INVALID_LAYOUT; /*^^^ Should have dash in 25-th position */ if (szINCHIKey[25] !='-') return INCHIKEY_INVALID_LAYOUT; /*^^^ All other should be uppercase */ for (j = 0; j < 14; j++) if ( !isbase26(szINCHIKey[j]) ) return INCHIKEY_INVALID_LAYOUT; /* first block */ for (j = 15; j < 25; j++) if ( !isbase26(szINCHIKey[j]) ) return INCHIKEY_INVALID_LAYOUT; /* second block */ if ( !isbase26(szINCHIKey[26]) ) return INCHIKEY_INVALID_LAYOUT; /* (de)protonation flag */ /*^^^ No 'E' may appear in 0,3,6,and 9 positions of the 1st block ... */ for (j=0; j <10; j+=3) if (szINCHIKey[j]=='E') return INCHIKEY_INVALID_LAYOUT; /*^^^ ... and 0 and 3 pos. of the second block. */ for (j=15; j <19; j+=3) if (szINCHIKey[j]=='E') return INCHIKEY_INVALID_LAYOUT; /*^^^ Check for version (only 1 allowed) */ if (szINCHIKey[24] !='A') return INCHIKEY_INVALID_VERSION; /*^^^ Check for standard-ness */ if (szINCHIKey[23] == 'S') return INCHIKEY_VALID_STANDARD; else if (szINCHIKey[23] == 'N') return INCHIKEY_VALID_NON_STANDARD; else return INCHIKEY_INVALID_LAYOUT; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void fprint_digest(FILE* fw, const char *header, unsigned char *a) { size_t i, bytelen = 32; fprintf(fw,"%s\n", header); for( i = 0; i < bytelen; i++ ) fprintf(fw,"%02x ", a[i]); fprintf(fw,"\n" ); } /********************************************************************/ #if ( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) /* Win32 & MS VC ++, compile and link as a DLL */ /*********************************************************/ /* C calling conventions export from Win32 dll */ /*********************************************************/ /* prototypes */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict); int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, const int xtra1,const int xtra2, char* szINCHIKey, char* szXtra1, char* szXtra2); int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export without cdecl_ prefixes */ /********************************************************/ int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey) { return GetStdINCHIKeyFromStdINCHI(szINCHISource, szINCHIKey); } /********************************************************/ int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict) { return CheckINCHIKey(szINCHIKey); } /********************************************************/ int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, const int xtra1,const int xtra2, char* szINCHIKey, char* szXtra1, char* szXtra2) { return GetINCHIKeyFromINCHI(szINCHISource, xtra1, xtra2, szINCHIKey, szXtra1, szXtra2); } #endif #if ( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) #include /*********************************************************/ /* Pacal calling conventions export from Win32 dll */ /*********************************************************/ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* prototypes */ int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export without PASCAL pasc_ prefixes */ /********************************************************/ int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey) { return CheckINCHIKey(szINCHIKey); } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_api.h000066400000000000000000001563161271037650300236610ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INHCH_API_H__ #define __INHCH_API_H__ /*^^^ Post-1.02b fix - thanks to David Foss */ #ifndef FIND_RING_SYSTEMS #define FIND_RING_SYSTEMS 1 #endif #ifndef FIND_RINS_SYSTEMS_DISTANCES #define FIND_RINS_SYSTEMS_DISTANCES 0 #endif /* radical definitions */ typedef enum tagINCHIRadical { INCHI_RADICAL_NONE = 0, INCHI_RADICAL_SINGLET = 1, INCHI_RADICAL_DOUBLET = 2, INCHI_RADICAL_TRIPLET = 3 } inchi_Radical; /* bond type definitions */ typedef enum tagINCHIBondType { INCHI_BOND_TYPE_NONE = 0, INCHI_BOND_TYPE_SINGLE = 1, INCHI_BOND_TYPE_DOUBLE = 2, INCHI_BOND_TYPE_TRIPLE = 3, INCHI_BOND_TYPE_ALTERN = 4 /* avoid by all means */ } inchi_BondType; /* 2D stereo definitions */ typedef enum tagINCHIBondStereo2D { /* stereocenter-related; positive: the sharp end points to this atom */ INCHI_BOND_STEREO_NONE = 0, INCHI_BOND_STEREO_SINGLE_1UP = 1, INCHI_BOND_STEREO_SINGLE_1EITHER = 4, INCHI_BOND_STEREO_SINGLE_1DOWN = 6, /* stereocenter-related; negative: the sharp end points to the opposite atom */ INCHI_BOND_STEREO_SINGLE_2UP = -1, INCHI_BOND_STEREO_SINGLE_2EITHER = -4, INCHI_BOND_STEREO_SINGLE_2DOWN = -6, /* stereobond-related */ INCHI_BOND_STEREO_DOUBLE_EITHER = 3 /* unknown stereobond geometry */ } inchi_BondStereo2D; /************************************************************************* * Notes on using INCHI_BOND_STEREO_SINGLE_* from inchi_BondStereo2D * * * * These stereo markings are used by InChI to characterize a stereogenic * * atom if and only if all neighbors of this atom have same z-coordinate * * as this atom (that is, in case of 2D fragment). * * The only exception is INCHI_BOND_STEREO_SINGLE_?EITHER marking which * * always assigns to the atom an "unknown" parity (u). * * * * Note the behavior which is default for InChI software v.1.04/03/02std * * (at -NEWPSOFF option is not supplied) 2D stereo interpretation: * * only bonds that have sharp end pointing to the stereogenic atom are * * considered as being out of plane and only sharp ends of * * INCHI_BOND_STEREO_SINGLE_?EITHER bonds are considered to determine * * whether the stereochemistry is unknown. * *************************************************************************/ /* sizes definitions */ #define MAXVAL 20 /* max number of bonds per atom */ #define ATOM_EL_LEN 6 /* length of ASCIIZ element symbol field */ #define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, D, T */ #define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ /* (isotopic mass - average atomic mass) */ #define ISOTOPIC_SHIFT_MAX 100 /* max abs(isotopic mass - average atomic mass) */ #ifndef INCHI_US_CHAR_DEF typedef signed char S_CHAR; typedef unsigned char U_CHAR; #define INCHI_US_CHAR_DEF #endif #ifndef INCHI_US_SHORT_DEF typedef signed short S_SHORT; typedef unsigned short U_SHORT; #define INCHI_US_SHORT_DEF #endif typedef S_SHORT AT_NUM; /* atom number; starts from 0 */ /************************************************* * * * A T O M S a n d C O N N E C T I V I T Y * * *************************************************/ typedef struct tagInchiAtom { /* atom coordinates */ double x; double y; double z; /* connectivity */ AT_NUM neighbor[MAXVAL]; /* adjacency list: ordering numbers of */ /* the adjacent atoms, >= 0 */ S_CHAR bond_type[MAXVAL]; /* inchi_BondType */ /* 2D stereo */ S_CHAR bond_stereo[MAXVAL]; /* inchi_BondStereo2D; negative if the */ /* sharp end points to opposite atom */ /* other atom properties */ char elname[ATOM_EL_LEN]; /* zero-terminated chemical element name:*/ /* "H", "Si", etc. */ AT_NUM num_bonds; /* number of neighbors, bond types and bond*/ /* stereo in the adjacency list */ S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; /* implicit hydrogen atoms */ /* [0]: number of implicit non-isotopic H (exception: num_iso_H[0]=-1 means INCHI adds implicit H automatically), [1]: number of implicit isotopic 1H (protium), [2]: number of implicit 2H (deuterium), [3]: number of implicit 3H (tritium) */ AT_NUM isotopic_mass; /* 0 => non-isotopic; isotopic mass or */ /* ISOTOPIC_SHIFT_FLAG + mass - (average atomic mass) */ S_CHAR radical; /* inchi_Radical */ S_CHAR charge; /* positive or negative; 0 => no charge */ }inchi_Atom; /******************************************************************* * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below) * start from zero; max. ordering number is (num_atoms-1). * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]] * by a bond that has type atom[i].bond_type[j] and 2D stereo type * atom[i].bond_stereo[j] (in case of no stereo * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE) * Index j is in the range 0 <= j <= (atom[i].num_bonds-1) * 3. Any connection (represented by atom[i].neighbor[j], * atom[i].bond_type[j], and atom[i].bond_stereo[j]) * should be present in one or both adjacency list: * if k = atom[i].neighbor[j] then i may or may not be present in * atom[k].neighbor[] list. For example, the adjacency lists may be * populated with only such neighbors that atom[i].neighbor[j] < i * All elements of an adjacency list must be different, that is, * a bond must be specified in an adjacency list only once. * 4. in Molfiles usually * (number of implicit H) = Valence - SUM(bond_type[]) * 5. Seemingly illogical order of the inchi_Atom members was * chosen in an attempt to avoid alignment problems when * accessing inchi_Atom from unrelated to C programming * languages such as Visual Basic. *******************************************************************/ /******************************************************************* 0D Stereo Parity and Type definitions ******************************************************************* Note: ===== o Below #A is the ordering number of atom A, starting from 0 o See parity values corresponding to 'o', 'e', and 'u' in inchi_StereoParity0D definition below) ============================================= stereogenic bond >A=B< or cumulene >A=C=C=B< ============================================= neighbor[4] : {#X,#A,#B,#Y} in this order X central_atom : NO_ATOM \ X Y type : INCHI_StereoType_DoubleBond A==B \ / \ A==B Y parity= 'e' parity= 'o' unknown parity = 'u' Limitations: ============ o Atoms A and B in cumulenes MUST be connected by a chain of double bonds; atoms A and B in a stereogenic 'double bond' may be connected by a double, single, or alternating bond. o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused aromatic structure). o Multiple stereogenic bonds incident to any given atom should either all except possibly one have (possibly different) defined parities ('o' or 'e') or should all have an unknown parity 'u'. Note on parities of alternating stereobonds =========================================== D--E In large rings (see Fig. 1, all // \\ atoms are C) all alternating bonds B--C F--G are treated as stereogenic. // \\ To avoid "undefined" bond parities A H for bonds BC, DE, FG, HI, JK, LM, AN \ / it is recommended to mark them with N==M J==I parities. \ / L==K Fig. 1 Such a marking will make the stereochemical layer unambiguous and it will be different from the B--C F--G stereochemical layer of the second // \\ // \\ structure (Fig. 2). A D--E H \ / N==M J==I By default, double and alternating \ / bonds in 8-member and greater rings L==K Fig. 2 are treated by InChI as stereogenic. ============================================= tetrahedral atom ============================================= 4 neighbors X neighbor[4] : {#W, #X, #Y, #Z} | central_atom: #A W--A--Y type : INCHI_StereoType_Tetrahedral | Z parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' 3 neighbors Y Y neighbor[4] : {#A, #X, #Y, #Z} / / central_atom: #A X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral \ \ Z Z parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e', otherwise 'o' unknown parity = 'u' Example (see AXYZ above): if A is above the plane XYZ then parity = 'e' This approach may be used also in case of an implicit H attached to A. ============================================= allene ============================================= X Y neighbor[4] : {#X,#A,#B,#Y} \ / central_atom : #C A=C=B type : INCHI_StereoType_Allene Y X | | when seen from A along A=C=B: X-A Y-A parity: 'e' 'o' parity: if A, B, Y are clockwise when seen from X then parity is 'e', otherwise 'o' unknown parity = 'u' Example (see XACBY above): if X on the diagram is above the plane ABY then parity is 'o' Limitations =========== o Atoms A and B in allenes MUST be connected by a chain of double bonds; ============================================== Note. Correspondence to CML 0D stereo parities ============================================== a list of 4 atoms corresponds to CML atomRefs4 tetrahedral atom ================ CML atomParity > 0 <=> INCHI_PARITY_EVEN CML atomParity < 0 <=> INCHI_PARITY_ODD | 1 1 1 1 | where xW is x-coordinate of | xW xX xY xZ | atom W, etc. (xyz is a CML atomParity = determinant | yW yX yY yZ | 'right-handed' Cartesian | zW zX xY zZ | coordinate system) allene (not yet defined in CML) =============================== the parity corresponds to the sign of the following determinant in exactly same way as for tetrahedral atoms: | 1 1 1 1 | where bonds and neighbor[4] array are | xX xA xB xY | same as defined above for allenes | yX yA yB yY | Obviously, the parity is same for | zX zA xB zY | {#X,#A,#B,#Y} and {#Y,#B,#A,#X} because of the even number of column permutations. stereogenic double bond and (not yet defined in CML) cumulenes ============================================================== CML 'C' (cis) <=> INCHI_PARITY_ODD CML 'T' (trans) <=> INCHI_PARITY_EVEN How InChI uses 0D parities ========================== 1. 0D parities are used if all atom coordinates are zeroes. In addition to that: 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if: 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms. 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true: 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03 3c. The four neighbors are almost in one plane or the central atom and its only 3 explicit neighbors are almost in one plane Notes on 0D parities and 'undefined' stereogenic elements ========================================================= If 0D parity is to be used according to 1-3 but CH3 CH3 has not been provided then the corresponding \ / stereogenic element is considered 'undefined'. C=CH / For example, if in the structure (Fig. 3) H the explicit H has been moved so that it Fig. 3 has same coordinates as atom >C= (that is, the length of the bond H-C became zero) then the double bond is assigned 'undefined' CH3 CH3 parity which by default is omitted from the \ / Identifier. CH=CH However, the structure on Fig. 4 will have double Fig. 4 bond parity 'o' and its parity in the Identifier is (-). Notes on 0D parities in structures containing metals ==================================================== Since InChI disconnects bonds to metals the 0D parities upon the disconnection may change in several different ways: 1) previously non-stereogenic bond may become stereogenic: \ / \ / CH==CH disconnection CH==CH \ / ======> M M before the disconnection: after the disconnection: atoms C have valence=5 and the double bond may become the double bond is not stereogenic recognized as stereogenic 2) previously stereogenic bond may become non-stereogenic: M M(+) \ / / N==C disconnection (-)N==C \ ======> \ 3) Oddball structures, usually resulting from projecting 3D structures on the plane, may contain fragment like that depicted on Fig. 5: M A M A |\ / B / B | X / disconnection / / |/ \ / ======> / / C===C C===C Fig. 5 (X stands for bond intersection) A-C=C-B parity is A-C=C-B parity is trans (e) cis (o) or undefined because the bond because C valence = 3, orientation is same not 4. as on Fig, 6 below: A M \ / Removal of M from the structure C===C on Fig. 5 changes the geometry from trans / \ to cis. M' B Removal of M and M' from the structure Fig. 6 on Fig. 6 does not change the A-C=C-B geometry: it is trans. To resolve the problem InChI API accepts the second parity corresponding to the metal-disconnected structure. To store both bond parities use left shift by 3 bits: inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3) In case when only disconnected structure parity exists set ParityOfConnected = INCHI_PARITY_UNDEFINED. This is the only case when INCHI_PARITY_UNDEFINED parity may be fed to the InChI. In cases when the bond parity in a disconnected structure exists and differs from the parity in the connected structure the atoms A and B should be non-metals. ****************************************************************************/ #define NO_ATOM (-1) /* non-existent (central) atom */ /* 0D parity types */ typedef enum tagINCHIStereoType0D { INCHI_StereoType_None = 0, INCHI_StereoType_DoubleBond = 1, INCHI_StereoType_Tetrahedral = 2, INCHI_StereoType_Allene = 3 } inchi_StereoType0D; /* 0D parities */ typedef enum tagINCHIStereoParity0D { INCHI_PARITY_NONE = 0, INCHI_PARITY_ODD = 1, /* 'o' */ INCHI_PARITY_EVEN = 2, /* 'e' */ INCHI_PARITY_UNKNOWN = 3, /* 'u' */ /* (see also readinch.c) used in: Extract0DParities, INChITo_Atom */ INCHI_PARITY_UNDEFINED = 4 /* '?' -- should not be used; however, see Note above */ } inchi_StereoParity0D; /************************************************* * * * 0D - S T E R E O (if no coordinates given) * * *************************************************/ typedef struct tagINCHIStereo0D { AT_NUM neighbor[4]; /* 4 atoms always */ AT_NUM central_atom; /* central tetrahedral atom or a central */ /* atom of allene; otherwise NO_ATOM */ S_CHAR type; /* inchi_StereoType0D */ S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ }inchi_Stereo0D; /************************************************* * * * I N C h I D L L I n p u t * * *************************************************/ /* Structure -> InChI, GetINCHI() / GetStdINCHI() */ typedef struct tagINCHI_Input { /* the caller is responsible for the data allocation and deallocation */ inchi_Atom *atom; /* array of num_atoms elements */ inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ char *szOptions; /* InChI options: space-delimited; each is preceded by */ /* '/' or '-' depending on OS and compiler */ AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ AT_NUM num_stereo0D; /* number of 0D stereo elements */ }inchi_Input; /* InChI -> Structure, GetStructFromINCHI()/GetStructFromStdINCHI() */ typedef struct tagINCHI_InputINCHI { /* the caller is responsible for the data allocation and deallocation */ char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ char *szOptions; /* InChI options: space-delimited; each is preceded by */ /* '/' or '-' depending on OS and compiler */ } inchi_InputINCHI; /************************************************* * * * I N C h I D L L O u t p u t * * *************************************************/ /* Structure -> InChI */ typedef struct tagINCHI_Output { /* zero-terminated C-strings allocated by GetStdINCHI() */ /* to deallocate all of them call FreeStdINCHI() (see below) */ char *szInChI; /* InChI ASCIIZ string */ char *szAuxInfo; /* Aux info ASCIIZ string */ char *szMessage; /* Error/warning ASCIIZ message */ char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ /* of recognized options and possibly an Error/warning message */ } inchi_Output; /* InChI -> Structure */ typedef struct tagINCHI_OutputStruct { /* 4 pointers are allocated by GetStructFromINCHI()/GetStructFromStdINCHI() */ /* to deallocate all of them call FreeStructFromStdINCHI()/FreeStructFromStdINCHI() */ inchi_Atom *atom; /* array of num_atoms elements */ inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ AT_NUM num_stereo0D; /* number of 0D stereo elements */ char *szMessage; /* Error/warning ASCIIZ message */ char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ /* of recognized options and possibly an Error/warning message */ unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ /* [x][y]: x=0 => Reconnected if present in InChI otherwise Disconnected/Normal x=1 => Disconnected layer if Reconnected layer is present y=1 => Main layer or Mobile-H y=0 => Fixed-H layer */ }inchi_OutputStruct; /************************************************* * * * I N C h I D L L I n t e r f a c e * * *************************************************/ #if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) /* Win32 & MS VC ++, compile and link as a DLL */ #ifdef _USRDLL /* InChI library dll */ #define INCHI_API __declspec(dllexport) #define EXPIMP_TEMPLATE #define INCHI_DECL __stdcall #else /* calling the InChI dll program */ #define INCHI_API __declspec(dllimport) #define EXPIMP_TEMPLATE extern #define INCHI_DECL __stdcall #endif #else /* create a statically linked InChI library or link to an executable */ #define INCHI_API #define EXPIMP_TEMPLATE #define INCHI_DECL #endif /*^^^ Return codes for GetINCHI GetStdINCHI Get_inchi_Input_FromAuxInfo Get_std_inchi_Input_FromAuxInfo GetStructFromINCHI GetStructFromStdINCHI */ typedef enum tagRetValGetINCHI { inchi_Ret_SKIP = -2, /* not used in InChI library */ inchi_Ret_EOF = -1, /* no structural data has been provided */ inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ inchi_Ret_ERROR = 2, /* Error: no InChI has been created */ inchi_Ret_FATAL = 3, /* Severe error: no InChI has been created (typically, memory allocation failure) */ inchi_Ret_UNKNOWN = 4, /* Unknown program error */ inchi_Ret_BUSY = 5 /* Previuos call to InChI has not returned yet */ } RetValGetINCHI; /*^^^ Return codes for CheckINCHI */ typedef enum tagRetValCheckINCHI { INCHI_VALID_STANDARD = 0, INCHI_VALID_NON_STANDARD = -1, INCHI_INVALID_PREFIX = 1, INCHI_INVALID_VERSION = 2, INCHI_INVALID_LAYOUT = 3, INCHI_FAIL_I2I = 4 } RetValCheckINCHI; /* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*^^^ InChI PREFIX */ #define INCHI_STRING_PREFIX "InChI=" #define LEN_INCHI_STRING_PREFIX 6 /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Format: Standard InChI starts with: InChI=1S/ Non-standard one with: InChI=1/ Empty std InChI: InChI=1S// Empty InChI: InChI=1// AuxInfo=1// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* EXPORTED FUNCTIONS */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetINCHI / GetStdINCHI inchi_Input is created by the user; strings in inchi_Output are allocated and deallocated by InChI inchi_Output does not need to be initilized out to zeroes; see FreeNCHI()/FreeSTDINCHI() on how to deallocate it Valid options for GetINCHI: (use - instead of / for O.S. other than MS Windows) Structure perception (compatible with stdInChI) /NEWPSOFF /DoNotAddH /SNon Stereo interpretation (lead to generation of non-standard InChI) /SRel /SRac /SUCF /ChiralFlagON /ChiralFlagOFF InChI creation options (lead to generation of non-standard InChI) /SUU /SLUUD /FixedH /RecMet /KET /15T GetINCHI produces standard InChI if no InChI creation/stereo modification options are specified. Inveresely, if any of SUU/SLUUD/RecMet/FixedH/Ket/15T/SRel/SRac/SUCF options are specified, generated InChI will be non-standard one. GetStdINCHI produces standard InChI only. The valid structure perception options are: /NEWPSOFF /DoNotAddH /SNon Other options are: /AuxNone Omit auxiliary information (default: Include) /Wnumber Set time-out per structure in seconds; W0 means unlimited In InChI library the default value is unlimited /OutputSDF Output SDfile instead of InChI /WarnOnEmptyStructure Warn and produce empty InChI for empty structure /SaveOpt Save custom InChI creation options (non-standard InChI) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FreeINCHI / FreeStdINCHI should be called to deallocate char* pointers obtained from each GetINCHI /GetStdINCHI call ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI ( inchi_Output *out ); EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI ( inchi_Output *out ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetStringLength helper: get string length ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStringLength( char *p ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetStructFromINCHI / GetStructFromStdINCHI inchi_Inputinchi_InputINCHI is created by the user; pointers in inchi_OutputStruct are allocated and deallocated by InChI inchi_OutputStruct does not need to be initilized out to zeroes; see FreeStructFromStdINCHI() on how to deallocate it Option /Inchi2Struct is not needed for GetStructFromINCHI()/GetStructFromStdINCHI() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FreeStructFromINCHI / FreeStructFromStdINCHI should be called to deallocate pointers obtained from each GetStructFromStdINCHI / GetStructFromINCHI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ); EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetINCHIfromINCHI GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it Note: there is no explicit tool to conversion from/to standard InChI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /***************************************************************** * * * C o n v e r s i o n: InChI AuxInfo string => inchi_Input * * *****************************************************************/ #ifndef STR_ERR_LEN #define STR_ERR_LEN 256 #endif typedef struct tagInchiInpData { inchi_Input *pInp; /* a pointer to pInp that has all items 0 or NULL */ int bChiral; /* 1 => the structure was marked as chiral, 2=> not chiral, 0=> not marked */ char szErrMsg[STR_ERR_LEN]; } InchiInpData; /* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get_inchi_Input_FromAuxInfo / Get_std_inchi_Input_FromAuxInfo Input: szInchiAuxInfo: contains ASCIIZ string of InChI output for a single structure or only the AuxInfo line bDoNotAddH: if 0 then InChI will be allowed to add implicit H bDiffUnkUndfStereo if not 0, use different labels for unknown and undefined stereo pInchiInp: should have a valid pointer pInchiInp->pInp to an empty (all members = 0) inchi_Input structure Output: pInchiInp: The following members of pInp may be filled during the call: atom, num_atoms, stereo0D, num_stereo0D Return value: see RetValGetINCHI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Free_inchi_Input / Free_std_inchi_Input To deallocate and write zeroes into the changed members of pInchiInp->pInp call Free_std_inchi_Input( inchi_Input *pInp ) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ); EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ CheckINCHI Check if the string represents valid InChI/standard InChI. Input: szINCHI source InChI strict if 0, just briefly check for proper layout (prefix, version, etc.) The result may not be strict. If not 0, try to perform InChI2InChI conversion and returns success if a resulting InChI string exactly match source. The result may be 'false alarm' due to imperfectness of conversion. Returns: success/errors codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChIKey API InChIKey description The InChIKey is a character signature based on a hash code of the InChI string. Standard InChIKey is produced out of standard InChI. Non-standard InChIKey is produced out of non-standard InChI. AAAAAAAAAAAAAA-BBBBBBBBCD-P InChIKey layout is as follows: AAAAAAAAAAAAAA First block (14 letters) Encodes molecular skeleton (connectivity) BBBBBBBB Second block (8 letters) Encodes tautomers, stereochemistry, isotopomers, reconnected layer C 'S' for standard 'N' for non-standard D InChI version ('A' for 1) P - (de)protonation flag Protonization encoding: N 0 O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 A < -12 or > +12 All symbols except delimiter (dash, that is, minus) are uppercase English letters representing a "base 26" encoding. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^ Return codes for key generation procedure */ #define INCHIKEY_OK 0 #define INCHIKEY_UNKNOWN_ERROR 1 #define INCHIKEY_EMPTY_INPUT 2 #define INCHIKEY_INVALID_INCHI_PREFIX 3 #define INCHIKEY_NOT_ENOUGH_MEMORY 4 #define INCHIKEY_INVALID_INCHI 20 #define INCHIKEY_INVALID_STD_INCHI 21 /*^^^ Return codes for CheckINCHIKey */ typedef enum tagRetValGetINCHIKey { INCHIKEY_VALID_STANDARD = 0, INCHIKEY_VALID_NON_STANDARD = -1, INCHIKEY_INVALID_LENGTH = 1, INCHIKEY_INVALID_LAYOUT = 2, INCHIKEY_INVALID_VERSION = 3 } RetValCheckINCHIKeyv; /* EXPORTED FUNCTIONS */ /* To compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetINCHIKeyFromINCHI Calculate InChIKey by InChI string. Input: szINCHISource source InChI string xtra1 =1 calculate hash extension (up to 256 bits; 1st block) xtra2 =1 calculate hash extension (up to 256 bits; 2nd block) Output: szINCHIKey InChIKey string The user-supplied buffer szINCHIKey should be at least 28 bytes long. szXtra1 hash extension (up to 256 bits; 1st block) string Caller should allocate space for 64 characters + trailing NULL szXtra2 hash extension (up to 256 bits; 2nd block) string Caller should allocate space for 64 characters + trailing NULL Returns: success/errors codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, const int xtra1, const int xtra2, char* szINCHIKey, char* szXtra1, char* szXtra2); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GetStdINCHIKeyFromStdINCHI "Standard" counterpart For compatibility with v. 1.02std, no extra hash calculation is allowed. To calculate extra hash(es), use GetINCHIKeyFromINCHI with stdInChI as input. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ CheckINCHIKey Check if the string represents valid InChIKey. Input: szINCHIKey source InChIKey string Returns: success/errors codes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modularized InChI generation API Note. Functions with STDINCHIGEN prefix are retained for compatibility with v. 1.02std ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^ Data structures holding intermediate (normalization) results */ #ifndef MAX_NUM_STEREO_ATOM_NEIGH #define MAX_NUM_STEREO_ATOM_NEIGH 4 #endif #ifndef MAX_NUM_STEREO_BONDS #define MAX_NUM_STEREO_BONDS 3 #endif #ifndef INCHI_NUM #define INCHI_NUM 2 /* = array size; member indexes: */ #endif typedef unsigned short AT_NUMBR; typedef signed short NUM_HS; typedef unsigned long INCHI_MODES; typedef struct tagNormAtom { char elname[ATOM_EL_LEN]; /* chem. element name */ U_CHAR el_number; /* number of the element in the Periodic Table */ AT_NUMBR neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the NORM_ATOM array */ AT_NUMBR orig_at_number; /* original atom number, starts from 1 */ AT_NUMBR orig_compt_at_numb; /* atom number within a component before terminal H removal */ S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down (this atom is at the pointing wedge) negative => on the opposite side of the wedge; 3=Either double bond */ U_CHAR bond_type[MAXVAL]; /* 1=single, 2=double, 3=triple, 4=1/2 (bond order is 1 or 2) */ /* 5=1/2/3, 6=1/3, 7=2/3, 8=tautomeric, 9=1/2 non-stereogenic */ S_CHAR valence; /* number of bonds = number of neighbors not greater than MAXVAL */ S_CHAR chem_bonds_valence; /* sum of bond types (1,2,3); type 4 needs special treatment */ S_CHAR num_H; /* number of adjacent implicit hydrogen atoms including D and T */ S_CHAR num_iso_H[NUM_H_ISOTOPES];/* number of adjacent implicit 1H(protium), 2H(D), 3H(T) < 16 */ S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ /* >0 => (isotopic mass) - (rounded average atomic mass) + 1 */ /* <0 => (isotopic mass) - (rounded average atomic mass) */ S_CHAR charge; /* charge */ S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ S_CHAR bAmbiguousStereo; /* flag of detected stereo ambiguity */ S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT: atom may have exchangeable isotopic H */ AT_NUMBR at_type; /* ATT_NONE, ATT_ACIDIC, etc. See InChI normalization code */ AT_NUMBR component; /* number of the structure component > 0 */ AT_NUMBR endpoint; /* id of a tautomeric group */ AT_NUMBR c_point; /* id of a positive charge group */ double x; /* x coordinate */ double y; /* y coordinate */ double z; /* x coordinate */ /*--------- 0D parities ----------*/ S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ /*----- tetrahedral stereo parity */ S_CHAR p_parity; /* tetrahedral (sp3) cml parity */ AT_NUMBR p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* orig_at_number of each neighbor > 0; 0=> no neighbor */ /*----- stereo bond (SB) parities */ S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of another end of this SB, starts from 0 */ S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of a bond that is not this SB; starts from 0; -1 means the neighbor is a removed explicit H */ /* atoms on both ends of a stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; /* parities of stereobonds (sp2) incident to this atom */ AT_NUMBR sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig_at_number of sn_ord[] neighbor > 0 */ #if ( FIND_RING_SYSTEMS == 1 ) S_CHAR bCutVertex; /* is the atom a cut-vertex or not */ AT_NUMBR nRingSystem; /* starts from 1; number of a ring system */ AT_NUMBR nNumAtInRingSystem; /* number of atoms in a ring system to which this at belongs */ AT_NUMBR nBlockSystem; /* ambiguous if the atom is a cut-vertex: better apply this to bonds */ #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) AT_NUMBR nDistanceFromTerminal; /* not used */ #endif #endif } NORM_ATOM; typedef struct tagNormAtomData { NORM_ATOM *at; /* atom list */ NORM_ATOM *at_fixed_bonds; /* atom list with added or removed protons only */ int num_at; /* number of atoms except removed terminal H */ int num_removed_H; /* number of removed H; at[] has (num_at+num_removed_H) elements */ int num_bonds; int num_isotopic; /* number of isotopic atoms */ int bExists; /* for internal use */ int bDeleted; /* for internal use */ int bHasIsotopicLayer; int bTautomeric; int bTautPreprocessed; /* for internal use */ int nNumRemovedProtons; NUM_HS nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ NUM_HS num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ INCHI_MODES bTautFlags; /* for internal use */ INCHI_MODES bTautFlagsDone; /* for internal use */ INCHI_MODES bNormalizationFlags;/* for internal use */ } NORM_ATOMS; typedef struct tagINCHIGEN_DATA { char pStrErrStruct[STR_ERR_LEN]; /* intermediate log (warning/error report) */ int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ /* index=0 => disconnected, 1 => reconnected structure */ /*^^^ The results of normalization stage */ /*^^^ for each member of pair disconnected/reconnected structures: */ NORM_ATOMS *NormAtomsNontaut[INCHI_NUM]; NORM_ATOMS *NormAtomsTaut[INCHI_NUM]; } INCHIGEN_DATA; /*^^^ InChI Generator Handle */ typedef void* INCHIGEN_HANDLE; /* EXPORTED FUNCTIONS */ /* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_Create / STDINCHIGEN_Create InChI Generator: create generator Returns handle of generator object or NULL on failure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void); EXPIMP_TEMPLATE INCHI_API INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_Setup / STDINCHIGEN_Setup InChI Generator: setup ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_DoNormalization / STDINCHIGEN_DoNormalization InChI Generator: structure normalization stage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_DoCanonicalization / STDINCHIGEN_DoCanonicalization InChI Generator: structure canonicalization stage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoCanonicalization (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoCanonicalization (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization InChI Generator: InChI serialization stage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization InChI Generator: reset stage (use before get next structure) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization InChI Generator: destroy generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prototypes for C calling conventions: int GetINCHI( inchi_Input *inp, inchi_Output *out ); int GetStdINCHI( inchi_Input *inp, inchi_Output *out ); void FreeINCHI( inchi_Output *out ); void FreeStdINCHI( inchi_Output *out ); int GetStringLength( char *p ); int Get_inchi_Input_FromAuxInfo ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); int Get_std_inchi_Input_FromAuxInfo ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo,InchiInpData *pInchiInp ); void Free_inchi_Input( inchi_Input *pInp ); void Free_std_inchi_Input( inchi_Input *pInp ); int GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); int GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); void FreeStructFromStdINCHI( inchi_OutputStruct *out ); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Win32 Dumpbin export information ordinal hint RVA name cdecl 1 0 000B7EAB CheckINCHI 2 1 000B7221 CheckINCHIKey 3 2 000B7C62 FreeINCHI 4 3 000B7B04 FreeStdINCHI 5 4 000B72B7 FreeStructFromINCHI 6 5 000B7BC2 FreeStructFromStdINCHI 7 6 000B7E33 Free_inchi_Input 8 7 000B7C58 Free_std_inchi_Input 9 8 000B727B GetINCHI 10 9 000B75B4 GetINCHIKeyFromINCHI 11 A 000B757D GetINCHIfromINCHI 12 B 000B8211 GetStdINCHI 13 C 000B7F0A GetStdINCHIKeyFromStdINCHI 14 D 000B77CB GetStringLength 15 E 000B7CA3 GetStructFromINCHI 16 F 000B778A GetStructFromStdINCHI 17 10 000B7DAC Get_inchi_Input_FromAuxInfo 18 11 000B7D6B Get_std_inchi_Input_FromAuxInfo 19 12 000B7D6B INCHIGEN_Create 20 13 000B7F2D INCHIGEN_Destroy 21 14 000B7F23 INCHIGEN_DoCanonicalization 22 15 000B7F23 INCHIGEN_DoNormalization 23 16 000B714A INCHIGEN_DoSerialization 24 17 000B7FCD INCHIGEN_Reset 25 18 000B7FCD INCHIGEN_Setup 26 19 000B7EA6 STDINCHIGEN_Create 27 1A 000B7EA6 STDINCHIGEN_Destroy 28 1B 000B711D STDINCHIGEN_DoCanonicalization 29 1C 000B7073 STDINCHIGEN_DoNormalization 30 1D 000B7FC3 STDINCHIGEN_DoSerialization 31 1E 000B7668 STDINCHIGEN_Reset 32 1F 000B7438 STDINCHIGEN_Setup __stdcall or PASCAL 33 20 000B7DFC _CheckINCHI@8 34 21 000B7802 _CheckINCHIKey@4 35 22 000B7F73 _FreeINCHI@4 36 23 000B7F82 _FreeStdINCHI@4 37 24 000B75E1 _FreeStructFromINCHI@4 38 25 000B7B81 _FreeStructFromStdINCHI@4 39 26 000B7B86 _Free_inchi_Input@4 40 27 000B7A96 _Free_std_inchi_Input@4 41 28 000B7B5E _GetINCHI@8 42 29 000B7285 _GetINCHIKeyFromINCHI@24 43 2A 000B758C _GetINCHIfromINCHI@8 44 2B 000B7CDA _GetStdINCHI@8 45 2C 000B7979 _GetStdINCHIKeyFromStdINCHI@8 46 2D 000B7BA4 _GetStringLength@4 47 2E 000B70A5 _GetStructFromINCHI@8 48 2F 000B79B0 _GetStructFromStdINCHI@8 49 30 000B8022 _Get_inchi_Input_FromAuxInfo@16 50 31 000B76E0 _Get_std_inchi_Input_FromAuxInfo@12 51 32 000B7230 _INCHIGEN_Create@0 52 33 000B760E _INCHIGEN_Destroy@4 53 34 000B7087 _INCHIGEN_DoCanonicalization@8 54 35 000B70B4 _INCHIGEN_DoNormalization@8 55 36 000B72D5 _INCHIGEN_DoSerialization@12 56 37 000B7FE1 _INCHIGEN_Reset@12 57 38 000B7163 _INCHIGEN_Setup@12 58 39 000B7159 _STDINCHIGEN_Create@0 59 3A 000B78A7 _STDINCHIGEN_Destroy@4 60 3B 000B72F3 _STDINCHIGEN_DoCanonicalization@8 61 3C 000B737A _STDINCHIGEN_DoNormalization@8 62 3D 000B7B72 _STDINCHIGEN_DoSerialization@12 63 3E 000B7654 _STDINCHIGEN_Reset@12 64 3F 000B75FF _STDINCHIGEN_Setup@12 Note. Currently there is no callback function for aborting, progress, etc. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #endif /* __INHCH_API_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll.c000066400000000000000000002702651271037650300236560ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include "mode.h" #include #include #include #include #include #include #include #include #include #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "ichierr.h" #include "ichimain.h" #include "extr_ct.h" #include "ichi_io.h" #include "ichicomp.h" #include "inchi_api.h" /************************************************************************* * * Local prototypes * *************************************************************************/ int SetAtomProperties( inp_ATOM *at, MOL_COORD *szCoord, inchi_Atom *ati, int a1, int *nDim, char *pStrErr, int *err ); int SetBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int j, int nNumAtoms, int *nNumBonds, char *pStrErr, int *err ); int SetAtomAndBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int bDoNotAddH, char *pStrErr, int *err ); void SetNumImplicitH(inp_ATOM* at, int num_atoms); int Extract0DParities(inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, int num_stereo0D, char *pStrErr, int *err, int vABParityUnknown); int parse_options_string ( char *cmd, const char *argv[], int maxargs ); int InpAtom0DToInchiAtom( inp_ATOM *at, int num_atoms, inchi_OutputStruct *outStruct ); int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, inchi_Input *inp, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ); static int GetINCHI1(inchi_Input *inp, inchi_Output *out, int bStdFormat); /*************************************************************************/ int bInterrupted = 0; /******************************************************************** * * INCHI API: DEALLOCATE INCHI OUTPUT * ********************************************************************/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI( inchi_Output *out ) { if ( out->szInChI ) { inchi_free( out->szInChI ); } if ( out->szLog ) { inchi_free( out->szLog ); } if ( out->szMessage ) { inchi_free( out->szMessage ); } memset( out, 0, sizeof(*out) ); } EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI( inchi_Output *out ) { FreeINCHI( out ); } EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ) { FreeStructFromINCHI( out ); } /*******************************************************************/ EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ) { if ( out->atom ) { inchi_free( out->atom ); } if ( out->stereo0D ) { inchi_free( out->stereo0D ); } if ( out->szLog ) { inchi_free( out->szLog ); } if ( out->szMessage ) { inchi_free( out->szMessage ); } memset( out, 0, sizeof(*out) ); } /********************************************************************/ #define INCHI_MAX_NUM_ARG 32 /******************************************************************** * * INCHI API: MAIN ENTRY POINT * ********************************************************************/ int bLibInchiSemaphore = 0; EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ) { return GetINCHI1( inp, out, 1 ); } EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ) { return GetINCHI1( inp, out, 0 ); } static int GetINCHI1(inchi_Input *inp, inchi_Output *out, int bStdFormat) { STRUCT_DATA struct_data; STRUCT_DATA *sd = &struct_data; char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; int i; long num_inp, num_err; char szSdfDataValue[MAX_SDF_VALUE+1]; PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; unsigned long ulDisplTime = 0; /* infinite, milliseconds */ unsigned long ulTotalProcessingTime = 0; INPUT_PARMS inp_parms; INPUT_PARMS *ip = &inp_parms; ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ ORIG_ATOM_DATA *prep_inp_data = PrepAtData; int bReleaseVersion = bRELEASE_VERSION; const int nStrLen = 64000; char *pStr = NULL; int nRet = 0, nRet1; STRUCT_FPTRS *pStructPtrs = NULL; #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) int num_repeat = REPEAT_ALL; #endif const char *argv[INCHI_MAX_NUM_ARG+1]; int argc; char *szOptions = NULL; INCHI_IOSTREAM inchi_file[3], *output_file = inchi_file, *log_file = inchi_file+1; INCHI_IOSTREAM prb_file0, *prb_file = &prb_file0; if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ return inchi_Ret_BUSY; } bLibInchiSemaphore = 1; #if( TRACE_MEMORY_LEAKS == 1 ) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif #if ( !defined(__STDC__) || __STDC__ != 1 ) /* turn on floating point exceptions */ { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif #endif szTitle[0] = '\0'; #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) repeat: inchi_ios_close(output_file); inchi_ios_close(log_file); inchi_ios_close(prb_file); pStr = NULL; #endif /*^^^ Initialize internal for this function output streams as string buffers */ inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(prb_file, INCHI_IOSTREAM_STRING, NULL); num_inp = 0; num_err = 0; sd->bUserQuit = 0; /* clear original input structure */ memset( pINChI, 0, sizeof(pINChI ) ); memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); memset( sd, 0, sizeof(*sd) ); memset( ip, 0, sizeof(*ip) ); memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); if ( !out ) { nRet = _IS_ERROR; goto exit_function; } memset( out, 0, sizeof(*out) ); /* options */ if ( inp && inp->szOptions ) { szOptions = (char*)inchi_malloc( strlen(inp->szOptions) + 1 ); if ( szOptions ) { strcpy( szOptions, inp->szOptions ); argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); } else { nRet = _IS_FATAL; goto translate_RetVal; /* emergency exit */ } } else { argc = 1; argv[0] = ""; argv[1] = NULL; } if ( argc == 1 #ifdef TARGET_API_LIB && (!inp || inp->num_atoms <= 0 || !inp->atom) #endif || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { HelpCommandLineParms(log_file); out->szLog = log_file->s.pStr; memset( log_file, 0, sizeof(*log_file) ); nRet = _IS_EOF; goto translate_RetVal; } nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); if ( szOptions ) { inchi_free( szOptions ); szOptions = NULL; } /* INChI DLL specific */ ip->bNoStructLabels = 1; if ( 0 > nRet1 ) { nRet = _IS_FATAL; goto exit_function; } if ( ip->bNoStructLabels ) { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; } else if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE ) { /* the input may contain both the header and the label of the structure */ if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; } /* Ensure standardness */ if ( bStdFormat ) { if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; } if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; } if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) { ip->nMode &= ~REQ_MODE_BASIC; } if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); } if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); } if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); } if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) { ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; } if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) { ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; } if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) { ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; } if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) { ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; } /* And anyway... */ ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; } /* */ PrintInputParms( log_file, ip ); if ( !(pStr = (char*)inchi_malloc(nStrLen))) { inchi_ios_eprint( log_file, "Cannot allocate output buffer. Terminating\n"); goto exit_function; } pStr[0] = '\0'; /**********************************************************************************************/ /* Main cycle */ /* read input structures and create their INChI */ ulTotalProcessingTime = 0; if ( pStructPtrs ) { memset ( pStructPtrs, 0, sizeof(pStructPtrs[0]) ); } /* === possible improvement: convert inp to orig_inp_data ==== */ if ( !sd->bUserQuit && !bInterrupted ) { if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) { nRet = _IS_EOF; /* simulate end of file */ goto exit_function; } nRet = ExtractOneStructure( sd, ip, szTitle, inp, log_file, output_file, prb_file, orig_inp_data, &num_inp, pStr, nStrLen ); if ( pStructPtrs ) { pStructPtrs->cur_fptr ++; } #ifndef TARGET_API_LIB if ( sd->bUserQuit ) { break; } #endif switch ( nRet ) { case _IS_FATAL: num_err ++; goto exit_function; case _IS_EOF: goto exit_function; case _IS_ERROR: num_err ++; goto exit_function; #ifndef TARGET_API_LIB case _IS_SKIP: continue; #endif } /* create INChI for each connected component of the structure and optionally display them */ /* output INChI for the whole structure */ nRet1 = ProcessOneStructure( sd, ip, szTitle, pINChI, pINChI_Aux, NULL, /* inp_file is not necessary as all input is already saved in 'ip' */ log_file, output_file, prb_file, orig_inp_data, prep_inp_data, num_inp, pStr, nStrLen, 0 /* save_opt_bits */); /* free INChI memory */ FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); /* free structure data */ FreeOrigAtData( orig_inp_data ); FreeOrigAtData( prep_inp_data ); FreeOrigAtData( prep_inp_data+1 ); ulTotalProcessingTime += sd->ulStructTime; nRet = inchi_max(nRet, nRet1); switch ( nRet ) { case _IS_FATAL: /* num_err ++; */ goto exit_function; case _IS_ERROR: ; /* num_err ++; */ #ifndef TARGET_API_LIB continue; #endif } } exit_function: if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%d.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->bXmlStructStarted = -1; /* do not repeat same message */ } } if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && ip->bXmlStarted ) { OutputINChIXmlRootEndTag( output_file ); ip->bXmlStarted = 0; } /* avoid memory leaks in case of fatal error */ if ( pStructPtrs && pStructPtrs->fptr ) { inchi_free( pStructPtrs->fptr ); } /* free INChI memory */ FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); /* free structure data */ FreeOrigAtData( orig_inp_data ); FreeOrigAtData( prep_inp_data ); FreeOrigAtData( prep_inp_data+1 ); #if( ADD_CMLPP == 1 ) /* BILLY 8/6/04 */ /* free CML memory */ FreeCml (); FreeCmlDoc( 1 ); #endif if ( pStr ) { inchi_free( pStr ); } for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( ip->path[i] ) { inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ ip->path[i] = NULL; } } SetBitFree( ); #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) if ( num_repeat-- > 0 ) { goto repeat; } #endif /* output */ if ( sd->pStrErrStruct[0] ) { if ( out && (out->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) { strcpy( out->szMessage, sd->pStrErrStruct ); } } if ( output_file->s.pStr && output_file->s.nUsedLength > 0 && out ) { char *p; out->szInChI = output_file->s.pStr; out->szAuxInfo = NULL; if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) { if ( !memcmp( p, "\nAuxInfo", 8 ) ) { *p = '\0'; /* remove LF after INChI */ out->szAuxInfo = p+1; /* save pointer to AuxInfo */ } else if ( out->szAuxInfo || !p[1]) { /* remove LF after aux info or from the last char */ *p = '\0'; break; } } output_file->s.pStr = NULL; } if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ } if ( out ) { out->szLog = log_file->s.pStr; log_file->s.pStr = NULL; } } translate_RetVal: /* Close inernal I/O streams */ inchi_ios_close(log_file); inchi_ios_close(output_file); inchi_ios_close(prb_file); switch (nRet) { case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ case _IS_OKAY : nRet = inchi_Ret_OKAY ; break; /* Success; break; no errors or warnings */ case _IS_WARNING: nRet = inchi_Ret_WARNING; break; /* Success; break; warning(s) issued */ case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ case _IS_UNKNOWN: default : nRet = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ } bLibInchiSemaphore = 0; return nRet; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Check if the string represents valid InChI/standard InChI. Input: szINCHI source InChI strict if 0, just quickly check for proper layout (prefix, version, etc.) The result may not be strict. If not 0, try to perform InChI2InChI conversion and returns success if a resulting InChI string exactly match source. The result may be 'false alarm' due to imperfect algorithm of conversion. Returns: success/errors codes */ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict) { int ret=INCHI_VALID_NON_STANDARD; int ret_i2i; inchi_InputINCHI inchi_inp; inchi_Output inchi_out; size_t slen, pos_slash1=0; char *str = NULL; size_t i; size_t slen0; char pp; /* .. non-empty */ if (szINCHI==NULL) return INCHI_INVALID_PREFIX; slen = strlen(szINCHI); /* .. has valid prefix */ if (slen standard InChIKey */ ret = INCHI_VALID_STANDARD; pos_slash1++; } /* .. has trailing slash in the right place */ if (szINCHI[pos_slash1]!='/') return INCHI_INVALID_LAYOUT; /* .. the rest of source string contains valid literals */ #if 0 if (!isalnum(szINCHI[pos_slash1+1] ) && ( szINCHI[pos_slash1+1]!='/' ) ) return INCHI_INVALID_LAYOUT; #endif /* Treat possible SaveOpt letters */ slen0 = slen; if ( (szINCHI[slen-3]=='\\') && (szINCHI[slen-2] >= 'A') && (szINCHI[slen-2] <='Z') && (szINCHI[slen-1] >= 'A') && (szINCHI[slen-1] <='Z') ) slen0 = slen -3; for (i=pos_slash1+1; i= 'A' && pp <='Z') continue; if (pp >= 'a' && pp <='z') continue; if (pp >= '0' && pp <='9') continue; switch ( pp ) { case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case ';': case '=': case '?': case '@': continue; default: break; } return INCHI_INVALID_LAYOUT; } if ( strict ) { char opts[]="?FixedH ?RecMet ?SUU ?SLUUD"; extract_inchi_substring(&str, szINCHI, slen); if (NULL==str) { ret = INCHI_FAIL_I2I; goto fin; } inchi_inp.szInChI = str; opts[0] = opts[8] = opts[16] = opts[21] = INCHI_OPTION_PREFX; inchi_inp.szOptions = opts; ret_i2i = GetINCHIfromINCHI(&inchi_inp, &inchi_out); if ( ((ret_i2i!=inchi_Ret_OKAY) && (ret_i2i!=inchi_Ret_WARNING)) || !inchi_out.szInChI ) { ret = INCHI_FAIL_I2I; } else { if (strcmp(inchi_inp.szInChI, inchi_out.szInChI)) { ret = INCHI_FAIL_I2I; } } } fin:if ( strict ) { if (NULL!=str) inchi_free(str); } return ret; } /*************************************************************************/ /******************************** from readmol.c *************************/ /*************************************************************************/ int AddMOLfileError( char *pStrErr, const char *szMsg ) { if ( pStrErr && szMsg && szMsg[0] ) { int lenStrErr = strlen( pStrErr ); int lenMsg = strlen( szMsg ); char *p = strstr( pStrErr, szMsg ); if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && (p+lenMsg == pStrErr+lenStrErr || p[lenMsg] == ';' && p[lenMsg+1] == ' ' || p[lenMsg-1]==':' && p[lenMsg]==' ') ) { return 1; /* reject duplicates */ } if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { /* enough room to add */ if (lenStrErr > 0) { if ( pStrErr[lenStrErr-1] != ':' ) { strcat( pStrErr, ";" ); } strcat( pStrErr, " " ); } strcat( pStrErr, szMsg ); return 1; } /* no room */ if ( strstr( pStrErr, "..." ) ) { return 0; /* no room mark has already been set */ } if ( lenStrErr + 3 < STR_ERR_LEN ) { strcat( pStrErr, "..." ); } } return 0; } /****************************************************************/ int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) { return 0; /* dummy */ } /****************************************************************/ /************************** from mol2atom.c *********************/ /****************************************************************/ void SetNumImplicitH(inp_ATOM* at, int num_atoms) { int bNonMetal; int a1/*, n1*/; /* special valences */ for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ ) { for ( a1 = 0; a1 < num_atoms; a1 ++ ) { int bHasMetalNeighbor /*, j*/; if ( bNonMetal != is_el_a_metal( at[a1].el_number ) ) { continue; /* first process all metals, after that all non-metals */ } bHasMetalNeighbor = 0; /*********************************************************************** * Set number of hydrogen atoms */ at[a1].num_H = get_num_H( at[a1].elname, at[a1].num_H, at[a1].num_iso_H, at[a1].charge, at[a1].radical, at[a1].chem_bonds_valence, 0, /* instead of valence entered by the user: it does not exist here*/ (at[a1].at_type & 1) /* bAliased */, !(at[a1].at_type & 2) /* bDoNotAddH */, bHasMetalNeighbor ); at[a1].at_type = 0; } } } /******************************************************************************************************/ void FreeInpAtom( inp_ATOM **at ) { if ( at && *at ) { inchi_free( *at ); *at = NULL; } } /******************************************************************************************************/ inp_ATOM *CreateInpAtom( int num_atoms ) { /* void *p = inchi_calloc(num_atoms, sizeof(inp_ATOM) ); if ( p == (void*)0x009143A8 ) { int stop = 1; } return (inp_ATOM* )p; */ return (inp_ATOM* ) inchi_calloc(num_atoms, sizeof(inp_ATOM) ); } /******************************************************************************************************/ void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ) { if ( inp_at_data ) { if ( inp_at_data->at ) { FreeInpAtom( &inp_at_data->at ); } if ( inp_at_data->at_fixed_bonds ) { FreeInpAtom( &inp_at_data->at_fixed_bonds ); } memset( inp_at_data, 0, sizeof(*inp_at_data) ); } } /******************************************************************************************************/ int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ) { FreeInpAtomData( inp_at_data ); if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && (!create_at_fixed_bonds || (inp_at_data->at_fixed_bonds = CreateInpAtom( num_atoms) ) ) ) { inp_at_data->num_at = num_atoms; return 1; } FreeInpAtomData( inp_at_data ); return 0; } /******************************************************************************************************/ void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ) { FreeInpAtom( &inp_at_data->at ); if ( inp_at_data->nOffsetAtAndH ) inchi_free( inp_at_data->nOffsetAtAndH ); memset( inp_at_data, 0, sizeof(*inp_at_data) ); } /******************************************************************************************************/ #if( TEST_RENUMB_ATOMS == 1 ) /* { */ /******************************************************************************************************/ int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ) { int ret = 1; if ( !dest_inp_at_data->at || dest_inp_at_data->num_at != src_inp_at_data->num_at ) { ret = CreateInpAtomData( dest_inp_at_data, src_inp_at_data->num_at, (NULL != src_inp_at_data->at_fixed_bonds) ); } else { inp_ATOM *at = dest_inp_at_data->at; /* save ptr to already allocated memory */ inp_ATOM *at2 = dest_inp_at_data->at_fixed_bonds; *dest_inp_at_data = *src_inp_at_data; /* copy all other (scalar) data */ dest_inp_at_data->at = at; /* restore ptr to already allocated memory */ dest_inp_at_data->at_fixed_bonds = at2; } if ( ret ) { memcpy( dest_inp_at_data->at, src_inp_at_data->at, src_inp_at_data->num_at*sizeof(dest_inp_at_data->at[0]) ); if ( dest_inp_at_data->at_fixed_bonds && src_inp_at_data->at_fixed_bonds ) { memcpy( dest_inp_at_data->at_fixed_bonds, src_inp_at_data->at_fixed_bonds, src_inp_at_data->num_at*sizeof(dest_inp_at_data->at_fixed_bonds[0]) ); } } return ret; } /******************************************************************************************************/ void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ) { int j, n, m, val; #if( TEST_RENUMB_NEIGH == 1 ) int i, k; #endif int num_atoms = src_inp_at_data->num_at; inp_ATOM *dest_at = dest_inp_at_data->at; for ( n = 0; n < num_atoms; n ++ ) { m = new_ord[n]; dest_at[m] = src_inp_at_data->at[n]; dest_at[m].orig_compt_at_numb = (AT_NUMB)(m+1); /* new ordering number within the component */ val = dest_at[m].valence; for ( j = 0; j < val; j ++ ) { dest_at[m].neighbor[j] = new_ord[dest_at[m].neighbor[j]]; } #if( TEST_RENUMB_NEIGH == 1 ) for ( i = 0; i < 3*val; i ++ ) { j = (rand() * val) / (RAND_MAX+1); k = (rand() * val) / (RAND_MAX+1); if ( j >= val || k >= val || j == k ) { continue; } inchi_swap( (char*)&dest_at[m].neighbor[j], (char*)&dest_at[m].neighbor[k], sizeof(dest_at[0].neighbor[0]) ); inchi_swap( (char*)&dest_at[m].bond_stereo[j], (char*)&dest_at[m].bond_stereo[k], sizeof(dest_at[0].bond_stereo[0]) ); inchi_swap( (char*)&dest_at[m].bond_type[j], (char*)&dest_at[m].bond_type[k], sizeof(dest_at[0].bond_type[0]) ); /* adjust stereo bond links */ if ( dest_at[m].sb_parity[0] ) { int a; for ( a = 0; a < MAX_NUM_STEREO_BONDS && dest_at[m].sb_parity[a]; a ++ ) { if ( k == (int)dest_at[m].sb_ord[a] ) { dest_at[m].sb_ord[a] = j; } else if ( j == (int)dest_at[m].sb_ord[a] ) { dest_at[m].sb_ord[a] = k; } if ( k == (int)dest_at[m].sn_ord[a] ) { dest_at[m].sn_ord[a] = j; } else if ( j == (int)dest_at[m].sn_ord[a] ) { dest_at[m].sn_ord[a] = k; } } } } #endif } } /******************************************************************************************************/ void MakeNewOrd( int num_atoms, AT_RANK *new_ord ) { int i, j, k; for ( i = 0; i < 3*num_atoms; i ++ ) { j = (rand() * num_atoms) / (RAND_MAX+1); k = (rand() * num_atoms) / (RAND_MAX+1); if ( j >= num_atoms || k >= num_atoms || j == k ) { continue; } inchi_swap( (char*)&new_ord[j], (char*)&new_ord[k], sizeof(new_ord[0]) ); } } #endif /* } TEST_RENUMB_ATOMS == 1 */ /**********************************************************************************/ void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ) { if ( !orig_at_data ) return; FreeInpAtom( &orig_at_data->at ); if ( NULL != orig_at_data->nCurAtLen ) { inchi_free( orig_at_data->nCurAtLen ); } if ( NULL != orig_at_data->nOldCompNumber ) { inchi_free( orig_at_data->nOldCompNumber ); } if ( NULL != orig_at_data->szCoord ) { inchi_free( orig_at_data->szCoord ); } if ( NULL != orig_at_data->nEquLabels ) { inchi_free( orig_at_data->nEquLabels ); } if ( NULL != orig_at_data->nSortedOrder ) { inchi_free( orig_at_data->nSortedOrder ); } memset( orig_at_data, 0, sizeof(*orig_at_data) ); } /********************************************************************/ #define REPEAT_ALL 0 /********************************************************************/ int parse_options_string ( char *cmd, const char *argv[], int maxargs ) { char *p; char *pArgCurChar; int bInsideQuotes; int bCopyCharToArg; int nNumBackSlashes; int i; i = 0; argv[i++] = ""; /* zeroth argument is not used */ p = cmd; bInsideQuotes = 0; /* arguments, one by one */ while( i < maxargs-1 ) { /* bypass spaces */ while ( *p == ' ' || *p == '\t' ) p ++; if ( !*p ) break; /* scan an argument */ argv[i++] = pArgCurChar = p; /* store preliminary ptr to arg */ while ( 1 ) { bCopyCharToArg = 1; nNumBackSlashes = 0; while (*p == '\\') { ++p; ++nNumBackSlashes; } /* each pair of backslashes => one backslash; one more backslash => literal quote */ if ( *p == '\"' ) { /* one " found */ if ( nNumBackSlashes % 2 == 0 ) { if (bInsideQuotes) { if (*(p+1) == '\"') { p++; } else { bCopyCharToArg = 0; } } else { bCopyCharToArg = 0; } bInsideQuotes = !bInsideQuotes; } nNumBackSlashes /= 2; /* divide nNumBackSlashes by two */ } while (nNumBackSlashes--) { *pArgCurChar++ = '\\'; } if (!*p) { break; } if (!bInsideQuotes && (*p == ' ' || *p == '\t')) { p ++; /* move to the next char because this char may become * zero due to *pArgCurChar++ = '\0'; line below */ break; } if (bCopyCharToArg) { *pArgCurChar++ = *p; } ++p; } *pArgCurChar++ = '\0'; /* argument zero termination */ } /* The last argument is NULL */ argv[i] = NULL; return i; } /*****************************************************************/ #define MIN_BOND_LENGTH (1.0e-6) int SetAtomProperties( inp_ATOM *at, MOL_COORD *szCoord, inchi_Atom *ati, int a1, int *nDim, char *pStrErr, int *err ) { S_CHAR cRadical; /* element, check later */ strcpy( at[a1].elname, ati[a1].elname ); /* charge */ at[a1].charge = ati[a1].charge; /* radical */ switch ( ati[a1].radical ) { case INCHI_RADICAL_NONE: cRadical = 0; break; case INCHI_RADICAL_SINGLET: #if( SINGLET_IS_TRIPLET == 1) /* 'singlet' means two electrons make a lone pair instead of 2 bonds*/ /* its effect on valence is same as the effect of a triplet */ cRadical = RADICAL_TRIPLET; #else cRadical = RADICAL_SINGLET; #endif break; case INCHI_RADICAL_DOUBLET: cRadical = RADICAL_DOUBLET; break; case INCHI_RADICAL_TRIPLET: cRadical = RADICAL_TRIPLET; break; default: { char szRadicalType[16]; int nRad = ati[a1].radical; while ( nRad > RADICAL_TRIPLET ) { nRad -= 2; } sprintf( szRadicalType, "%d->%d", ati[a1].radical, nRad ); MOLFILE_ERR_SET (*err, 0, "Radical center type replaced:"); MOLFILE_ERR_SET (*err, 0, szRadicalType); cRadical = nRad; if ( nRad < 0 ) { *err |= 8; /* Unrecognized Radical replaced with non-radical */ } } break; } at[a1].radical = cRadical; /* coordinates */ at[a1].x = ati[a1].x; at[a1].y = ati[a1].y; at[a1].z = ati[a1].z; if ( szCoord ) { /* store text coordinates */ char str[32]; MOL_COORD * coord_p = szCoord + a1; WriteCoord( str, ati[a1].x ); memcpy( *coord_p, str, 10 ); WriteCoord( str, ati[a1].y ); memcpy( *coord_p+10, str, 10 ); WriteCoord( str, ati[a1].z ); memcpy( *coord_p+20, str, 10 ); } if ( MIN_BOND_LENGTH < fabs(ati[a1].x) || MIN_BOND_LENGTH < fabs(ati[a1].y) || MIN_BOND_LENGTH < fabs(ati[a1].z) ) { if ( MIN_BOND_LENGTH < fabs(ati[a1].z) ) { *nDim |= 3; } else { *nDim |= 2; } } /* orig. at. number */ at[a1].orig_at_number = a1+1; return 0; #undef MIN_BOND_LENGTH } /*********************************************************************/ int SetBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int j, int nNumAtoms, int *nNumBonds, char *pStrErr, int *err ) { int a2; S_CHAR cBondType, cStereoType1, cStereoType2; AT_NUMB *p1, *p2; int n1, n2; /* bond type */ switch( ati[a1].bond_type[j] ) { case INCHI_BOND_TYPE_SINGLE: cBondType = BOND_TYPE_SINGLE; break; case INCHI_BOND_TYPE_DOUBLE: cBondType = BOND_TYPE_DOUBLE; break; case INCHI_BOND_TYPE_TRIPLE: cBondType = BOND_TYPE_TRIPLE; break; case INCHI_BOND_TYPE_ALTERN: cBondType = BOND_TYPE_ALTERN; break; default: { char szBondType[16]; sprintf( szBondType, "%d", ati[a1].bond_type[j] ); MOLFILE_ERR_SET (*err, 0, "Unrecognized bond type:"); MOLFILE_ERR_SET (*err, 0, szBondType); *err |= 8; /* Unrecognized Bond type replaced with single bond */ cBondType = BOND_TYPE_SINGLE; } break; } /* 2D stereo */ switch( ati[a1].bond_stereo[j] ) { /* stereocenter-related; positive: the sharp end points to this atom */ case INCHI_BOND_STEREO_NONE: cStereoType1 = 0; cStereoType2 = 0; break; case INCHI_BOND_STEREO_SINGLE_1UP: cStereoType1 = STEREO_SNGL_UP; cStereoType2 = -STEREO_SNGL_UP; break; case INCHI_BOND_STEREO_SINGLE_1EITHER: cStereoType1 = STEREO_SNGL_EITHER; cStereoType2 = -STEREO_SNGL_EITHER; break; case INCHI_BOND_STEREO_SINGLE_1DOWN: cStereoType1 = STEREO_SNGL_DOWN; cStereoType2 = -STEREO_SNGL_DOWN; break; /* stereocenter-related; negative: the sharp end points to the opposite atom */ case INCHI_BOND_STEREO_SINGLE_2UP: cStereoType1 = -STEREO_SNGL_UP; cStereoType2 = STEREO_SNGL_UP; break; case INCHI_BOND_STEREO_SINGLE_2EITHER: cStereoType1 = -STEREO_SNGL_EITHER; cStereoType2 = STEREO_SNGL_EITHER; break; case INCHI_BOND_STEREO_SINGLE_2DOWN: cStereoType1 = -STEREO_SNGL_DOWN; cStereoType2 = STEREO_SNGL_DOWN; break; /* stereobond-related */ case INCHI_BOND_STEREO_DOUBLE_EITHER: case -INCHI_BOND_STEREO_DOUBLE_EITHER: cStereoType1 = STEREO_DBLE_EITHER; cStereoType2 = STEREO_DBLE_EITHER; break; default: { char szBondType[16]; sprintf( szBondType, "%d", ati[a1].bond_stereo[j] ); MOLFILE_ERR_SET (*err, 0, "Unrecognized bond stereo:"); MOLFILE_ERR_SET (*err, 0, szBondType); *err |= 8; /* Unrecognized Bond stereo replaced with non-stereo bond */ cStereoType1 = 0; cStereoType2 = 0; } break; } /* neighbor */ if ( ati[a1].neighbor[j] < 0 || ati[a1].neighbor[j] >= nNumAtoms ) { *err |= 1; /* bond for impossible atom number(s); ignored */ MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); goto err_exit; } a2 = (AT_NUMB) ati[a1].neighbor[j]; if ( a2 == a1 ) { *err |= 1; /* bond for impossible atom number(s); ignored */ MOLFILE_ERR_SET (*err, 0, "Atom has a bond to itself"); goto err_exit; } /* consistency check; locate the bond in the opposite atom */ p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence ); p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); if ( p1 && p2 ) { n1 = (p1 - at[a1].neighbor); n2 = (p2 - at[a2].neighbor); if ( n1+1 < at[a1].valence && is_in_the_list( at[a1].neighbor+n1+1, (AT_NUMB)a2, at[a1].valence-n1-1 ) || n2+1 < at[a2].valence && is_in_the_list( at[a2].neighbor+n2+1, (AT_NUMB)a1, at[a2].valence-n2-1 ) ) { MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); *err |= 2; /* multiple bonds between atoms */ } else if ( n1 < at[a1].valence && n2 < at[a2].valence && cBondType == at[a2].bond_type[n2] && cBondType == at[a1].bond_type[n1] && cStereoType1 == at[a1].bond_stereo[n1] && cStereoType2 == at[a2].bond_stereo[n2] ) { /*MOLFILE_ERR_SET (*err, 0, "Duplicated bond(s) between two atoms");*/ } else { MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); *err |= 2; /* multiple bonds between atoms */ } } else if ( (p1 || p2) && (p1 || at[a1].valence < MAXVAL) && (p2 || at[a2].valence < MAXVAL) ) { n1 = p1? (p1 - at[a1].neighbor) : at[a1].valence ++; n2 = p2? (p2 - at[a2].neighbor) : at[a2].valence ++; /* the bond is present in one atom only: possibly program error */ if ( p1 && (cBondType != at[a1].bond_type[n1] || at[a1].bond_stereo[n1] != cStereoType1 )|| p2 && (cBondType != at[a2].bond_type[n2] || at[a2].bond_stereo[n2] != cStereoType2 ) ) { MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); *err |= 2; /* multiple bonds between atoms */ } else { MOLFILE_ERR_SET (*err, 0, "Duplicated bond(s) between two atoms"); /* warning */ } } else if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL ) { n1 = at[a1].valence ++; n2 = at[a2].valence ++; (*nNumBonds) ++; } else { char szMsg[64]; *err |= 4; /* too large number of bonds. Some bonds ignored. */ sprintf( szMsg, "Atom '%s' has more than %d bonds", at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); MOLFILE_ERR_SET (*err, 0, szMsg); goto err_exit; } /* store the connection */ /* bond type */ at[a1].bond_type[n1] = at[a2].bond_type[n2] = cBondType; /* connection */ at[a1].neighbor[n1] = (AT_NUMB)a2; at[a2].neighbor[n2] = (AT_NUMB)a1; /* stereo */ at[a1].bond_stereo[n1] = cStereoType1; /* >0: the wedge (pointed) end is at this atom */ at[a2].bond_stereo[n2] = cStereoType2; /* <0: the wedge (pointed) end is at the opposite atom */ return 0; err_exit: return 1; } /******************************************************************/ int SetAtomAndBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int bDoNotAddH, char *pStrErr, int *err ) { int valence, chem_valence, num_alt_bonds, j, n1; int nRadical, nCharge; static int el_number_H = 0; if ( !el_number_H ) { el_number_H = get_periodic_table_number( "H" ); } nRadical = nCharge = 0; valence = at[a1].valence; chem_valence = num_alt_bonds = 0; for ( j = 0; j < valence; j ++ ) { if ( at[a1].bond_type[j] <= BOND_TYPE_TRIPLE ) { chem_valence += at[a1].bond_type[j]; } else { num_alt_bonds ++; } } switch( num_alt_bonds ) { case 0: break; case 2: chem_valence += 3; /* -C= */ break; case 3: chem_valence += 4; /* >C= */ break; default: { char szMsg[64]; *err |= 8; /* wrong number of alt. bonds */ sprintf( szMsg, "Atom '%s' has %d alternating bonds", at[a1].elname, num_alt_bonds ); MOLFILE_ERR_SET (*err, 0, szMsg); } break; } at[a1].chem_bonds_valence = chem_valence; /* aliased hydrogen atoms */ if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) { /* Case when elname contains more than 1 element: extract number of H if possible */ if ( extract_ChargeRadical( at[a1].elname, &nRadical, &nCharge ) ) { if ( nRadical && at[a1].radical && nRadical != at[a1].radical || nCharge && at[a1].charge && nCharge != at[a1].charge ) { MOLFILE_ERR_SET (*err, 0, "Ignored charge/radical redefinition:"); MOLFILE_ERR_SET (*err, 0, ati[a1].elname); } else { if ( nRadical ) at[a1].radical = nRadical; if ( nCharge ) at[a1].charge = nCharge; } } at[a1].num_H = extract_H_atoms( at[a1].elname, at[a1].num_iso_H ); if ( !at[a1].elname[0] && NUMH(at, a1) ) { /* alias contains only H. Added 2004-07-21, fixed 2004-07-22 * move the heaviest isotope to the "central atom" * Note: this must be consistent with H-H treatment in remove_terminal_HDT() */ strcpy( at[a1].elname, "H" ); if ( NUM_ISO_H(at,a1) ) { for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- ) { if ( at[a1].num_iso_H[j] ) { at[a1].num_iso_H[j] --; at[a1].iso_atw_diff = 1 + j; break; } } } else { at[a1].num_H --; } } if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) { n1 = 0; } if ( n1 ) { at[a1].at_type |= 1; /* "Aliased" atom: data in the element name */ MOLFILE_ERR_SET (*err, 0, "Parsed compound atom(s):"); MOLFILE_ERR_SET (*err, 0, ati[a1].elname); } } at[a1].el_number = (U_CHAR) n1; if ( !n1 ) { *err |= 64; /* Unrecognized aromatic bond(s) replaced with single */ MOLFILE_ERR_SET (*err, 0, "Unknown element(s):"); MOLFILE_ERR_SET (*err, 0, at[a1].elname); } else /* replace explicit D or T with isotopic H (added 2003-06-02) */ if ( el_number_H == n1 && !at[a1].iso_atw_diff ) { switch( at[a1].elname[0] ) { case 'D': at[a1].iso_atw_diff = 2; mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); break; case 'T': at[a1].iso_atw_diff = 3; mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); break; case 'H': if ( 1 <= ati[a1].isotopic_mass ) { AT_NUM iso_atw_diff; if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) { /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; } else { /* ati[a1].isotopic_mass is isotopic mass */ iso_atw_diff = get_atw_from_elnum( (int) at[a1].el_number ); iso_atw_diff = ati[a1].isotopic_mass - iso_atw_diff; } if ( iso_atw_diff >= 0 ) iso_atw_diff ++; /* reproduce Bug04: allowed non-terminal H heavier than T */ if ( 1 <= iso_atw_diff && (at[a1].valence != 1 || iso_atw_diff <= NUM_H_ISOTOPES) ) { at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; } } } } else /* isotopic shift */ if ( ati[a1].isotopic_mass ) { AT_NUM iso_atw_diff; if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) { /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; } else { /* ati[a1].isotopic_mass is isotopic mass */ iso_atw_diff = get_atw_from_elnum( (int) at[a1].el_number ); iso_atw_diff = ati[a1].isotopic_mass - iso_atw_diff; } if ( iso_atw_diff >= 0 ) iso_atw_diff ++; at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; } /* add implicit hydrogen atoms flag */ if ( ati[a1].num_iso_H[0] == -1 ) { if ( !bDoNotAddH ) { at[a1].at_type |= 2; /* user requested to add H */ } } else { at[a1].num_H = ati[a1].num_iso_H[0]; } for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { at[a1].num_iso_H[j] = ati[a1].num_iso_H[j+1]; } if ( num_alt_bonds ) { /* atom has aromatic bonds AND the chemical valence is not known */ int num_H = NUMH(at, a1); int chem_valence_alt = at[a1].chem_bonds_valence + num_H; int bUnusualValenceArom = detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, at[a1].radical, chem_valence_alt, num_H, at[a1].valence ); int bUnusualValenceNoArom = detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, at[a1].radical, chem_valence_alt-1, num_H, at[a1].valence ); if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( at, a1) ) { /* typically NH in 5-member aromatic ring */ at[a1].chem_bonds_valence --; } } return 0; } /****************************************************************************************/ int InpAtom0DToInchiAtom( inp_ATOM *at, int num_atoms, inchi_OutputStruct *outStruct ) { int num_stereo_centers, num_stereo_bonds, num_stereo0D, i, m, m1, m2, n, ret=0; /* count stereobonds, allenes. cumulenes. and stereoatoms */ num_stereo_centers = num_stereo_bonds = ret = 0; outStruct->atom = NULL; outStruct->num_atoms = 0; outStruct->stereo0D = NULL; outStruct->num_stereo0D = 0; for ( i = 0; i < num_atoms; i ++ ) { if ( at[i].p_parity ) { /* stereocenter */ num_stereo_centers ++; } else { for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) ; num_stereo_bonds += m; } } num_stereo_bonds /= 2; num_stereo0D = num_stereo_bonds + num_stereo_centers; if ( num_atoms > 0 ) { outStruct->atom = (inchi_Atom *)inchi_calloc( num_atoms, sizeof( outStruct->atom[0] ) ); } outStruct->num_atoms = num_atoms; if ( num_stereo0D > 0 ) { outStruct->stereo0D = (inchi_Stereo0D *)inchi_calloc( num_stereo0D, sizeof(outStruct->stereo0D[0])); } if ( num_atoms && !outStruct->atom || num_stereo0D > 0 && !outStruct->stereo0D ) { /* allocation failed */ ret = -1; goto exit_function; } /* copy atom properties */ for ( i = 0; i < num_atoms; i ++ ) { outStruct->atom[i].num_bonds = at[i].valence; for ( m = 0; m < at[i].valence; m ++ ) { outStruct->atom[i].bond_type[m] = at[i].bond_type[m]; outStruct->atom[i].neighbor[m] = at[i].neighbor[m]; } outStruct->atom[i].charge = at[i].charge; memcpy( outStruct->atom[i].elname, at[i].elname, ATOM_EL_LEN ); if ( at[i].iso_atw_diff ) { outStruct->atom[i].isotopic_mass = ISOTOPIC_SHIFT_FLAG + (at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff); } outStruct->atom[i].num_iso_H[0] = at[i].num_H; for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { outStruct->atom[i].num_iso_H[m+1] = at[i].num_iso_H[m]; } outStruct->atom[i].radical = at[i].radical; } /* stereo */ for ( i = n = 0; i < num_atoms; i ++ ) { if ( at[i].p_parity ) { if ( n < num_stereo0D ) { outStruct->stereo0D[n].central_atom = i; outStruct->stereo0D[n].parity = at[i].p_parity; outStruct->stereo0D[n].type = INCHI_StereoType_Tetrahedral; for ( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { outStruct->stereo0D[n].neighbor[m] = at[i].p_orig_at_num[m] - 1; } n ++; } else { ret |= 1; break; } } else { for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m1]; m1 ++ ) { /* find the opposite atom at the other end of double bond, allene, or cumulene */ int chain[12], len = 0, nxt_neigh, nxt, cur; cur = chain[len++] = i; nxt_neigh = at[cur].sb_ord[m1]; do { /* add next atom */ chain[len ++] = nxt = at[cur].neighbor[nxt_neigh]; nxt_neigh = (at[nxt].neighbor[0] == cur); cur = nxt; /* find nxt_neigh */ } while ( !at[cur].sb_parity[0] && len < 12 && at[cur].valence == 2 ); if ( at[cur].sb_parity[0] && len <= 4 && i < cur /* count bonds only one time */ ) { /* double bond, cumulene, or allene has been found */ for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[cur].sb_parity[m2]; m2 ++ ) { if ( chain[len-2] == at[cur].neighbor[(int)at[cur].sb_ord[m2]] ) { if ( n < num_stereo0D ) { int parity1 = at[i].sb_parity[m1]; int parity2 = at[cur].sb_parity[m2]; int parity; if ( (INCHI_PARITY_ODD == parity1 || INCHI_PARITY_EVEN == parity1) && (INCHI_PARITY_ODD == parity2 || INCHI_PARITY_EVEN == parity2) ) { /* well-defined parity */ parity = (parity1==parity2)? INCHI_PARITY_EVEN : INCHI_PARITY_ODD; } else { parity = inchi_max(parity1, parity2); } outStruct->stereo0D[n].central_atom = (len==3)? chain[1] : NO_ATOM; outStruct->stereo0D[n].parity = parity; outStruct->stereo0D[n].type = len == 3? INCHI_StereoType_Allene : INCHI_StereoType_DoubleBond; outStruct->stereo0D[n].neighbor[0] = at[i].sn_orig_at_num[m1]-1; outStruct->stereo0D[n].neighbor[1] = i; outStruct->stereo0D[n].neighbor[2] = cur; outStruct->stereo0D[n].neighbor[3] = at[cur].sn_orig_at_num[m2] - 1; n ++; } else { ret |= 1; } break; } } } } } } outStruct->num_stereo0D = n; exit_function: if ( ret < 0 ) { if ( outStruct->atom ) inchi_free( outStruct->atom ); if ( outStruct->stereo0D ) inchi_free( outStruct->stereo0D ); outStruct->atom = NULL; outStruct->stereo0D = NULL; outStruct->num_atoms = 0; outStruct->num_stereo0D = 0; } return ret; } /****************************************************************************************/ int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, inchi_Input *inp, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ) { int *err = &sd->nStructReadError; char *pStrErr = sd->pStrErrStruct; inp_ATOM *at = NULL; MOL_COORD *szCoord = NULL; inchi_Atom *ati = NULL; int nNumAtoms = 0; int a1, j, valence, nDim, nNumBonds, nRet = 0; /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ int vABParityUnknown = AB_PARITY_UNDF; if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) { /* Make labels for unknown and undefined stereo different */ vABParityUnknown = AB_PARITY_UNKN; } /******************************************************** * * Extract the structure * ********************************************************/ FreeOrigAtData( orig_inp_data ); nDim = 0; nNumBonds = 0; if ( !inp || (nNumAtoms = inp->num_atoms) <= 0 || !(ati = inp->atom) ) { MOLFILE_ERR_SET (*err, 0, "Empty structure"); *err = 98; goto err_exit; } if ( nNumAtoms >= MAX_ATOMS ) { MOLFILE_ERR_SET (*err, 0, "Too many atoms"); *err = 70; orig_inp_data->num_inp_atoms = -1; goto err_exit; } at = (inp_ATOM *) inchi_calloc( nNumAtoms, sizeof(at[0]) ); szCoord = (MOL_COORD *) inchi_calloc (inchi_max(nNumAtoms, 1), sizeof (MOL_COORD)); if ( !at || !szCoord ) { MOLFILE_ERR_SET (*err, 0, "Out of RAM"); *err = -1; goto err_exit; } /******************************************************** * * Extract typical for Molfile structural data * ********************************************************/ /* extract atoms and bonds */ for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) { /* extract atoms */ SetAtomProperties( at, szCoord, ati, a1, &nDim, pStrErr, err ); if ( *err ) { goto err_exit; } /* extract connections */ valence = ati[a1].num_bonds; for ( j = 0; j < valence; j ++ ) { SetBondProperties( at, ati, a1, j, nNumAtoms, &nNumBonds, pStrErr, err ); } if ( *err ) { goto err_exit; } } orig_inp_data->num_inp_atoms = nNumAtoms; orig_inp_data->num_inp_bonds = nNumBonds; orig_inp_data->num_dimensions = nDim; /* extract elements, chemical valences, implicit H, isotopic shifts */ for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) { /* set temp flags in at[a1].at_type (1: data in atom name; 2: request to add H) */ SetAtomAndBondProperties( at, ati, a1, ip->bDoNotAddH, pStrErr, err ); if ( *err ) { goto err_exit; } } /* clear temp flags in at[].at_type; add implicit H */ SetNumImplicitH( at, nNumAtoms ); if ( *err ) { goto err_exit; } /******************************************************** * * Extract the 0D parities (typical for CML) * ********************************************************/ Extract0DParities(at, nNumAtoms, inp->stereo0D, inp->num_stereo0D, pStrErr, err, vABParityUnknown); if ( *err ) { goto err_exit; } orig_inp_data->at = at; at = NULL; orig_inp_data->num_dimensions = nDim; orig_inp_data->num_inp_atoms = nNumAtoms; orig_inp_data->num_inp_bonds = nNumBonds; orig_inp_data->szCoord = szCoord; szCoord = NULL; /* chiral flag */ /* ***************************************************************************** * Chiral flags are set in: * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code (here) *******************************************************************************/ if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { /* absolute stereo */ ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ } else /*if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL )*/ { /* relative stereo */ ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO); ip->nMode |= REQ_MODE_RELATIVE_STEREO; sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ } } else if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ } else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ } *num_inp += 1; err_exit: if ( at ) inchi_free( at ); if ( szCoord ) inchi_free( szCoord ); nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_NO_WARN, NULL, log_file, output_file, prb_file, orig_inp_data, num_inp, pStr, nStrLen ); return nRet; } /********************************************************/ int INCHI_DECL GetStringLength( char *p ) { if ( p ) { return strlen(p); } else { return 0; } } #define MAX_MSG_LEN 512 /* GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes */ /* It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer */ /* Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers */ /* To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed */ /* inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI */ /* inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it */ /*************************************************************/ int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ) { STRUCT_DATA struct_data; STRUCT_DATA *sd = &struct_data; static char szMainOption[] = " ?InChI2InChI"; int i; char szSdfDataValue[MAX_SDF_VALUE+1]; unsigned long ulDisplTime = 0; /* infinite, milliseconds */ INPUT_PARMS inp_parms; INPUT_PARMS *ip = &inp_parms; int bReleaseVersion = bRELEASE_VERSION; int nRet = 0, nRet1; #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) int num_repeat = REPEAT_ALL; #endif const char *argv[INCHI_MAX_NUM_ARG+1]; int argc; char *szOptions = NULL; INCHI_IOSTREAM inchi_file[3], *output_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ return inchi_Ret_BUSY; } bLibInchiSemaphore = 1; #if( TRACE_MEMORY_LEAKS == 1 ) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif /* turn on floating point exceptions */ #if ( !defined(__STDC__) || __STDC__ != 1 ) { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif #endif memset( out, 0, sizeof(*out) ); #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) repeat: FreeINCHI( out ); inchi_ios_close(output_file); inchi_ios_close(log_file); inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ #endif /*^^^ Initialize internal for this function I/O streams as string buffers */ inchi_ios_init(input_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); sd->bUserQuit = 0; /* clear original input structure */ /*^^^ memset( inchi_file, 0, sizeof(inchi_file) ); */ memset( sd, 0, sizeof(*sd) ); memset( ip, 0, sizeof(*ip) ); memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); szMainOption[1] = INCHI_OPTION_PREFX; if ( !inpInChI ) { nRet = _IS_ERROR; goto exit_function; } /* options */ if ( inpInChI ) { int opt_len = (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1; szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); if ( szOptions ) { if ( inpInChI->szOptions ) { strcpy( szOptions, inpInChI->szOptions ); } strcat( szOptions, szMainOption ); argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); } else { nRet = _IS_FATAL; goto translate_RetVal; /* emergency exit */ } } else { argc = 1; argv[0] = ""; argv[1] = NULL; } if ( argc == 1 #ifdef TARGET_API_LIB && (!inpInChI || !inpInChI->szInChI) #endif || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { HelpCommandLineParms(log_file); out->szLog = log_file->s.pStr; memset( log_file, 0, sizeof(*log_file) ); nRet = _IS_EOF; goto translate_RetVal; } nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); if ( szOptions ) { /* argv pointed to strings in szOptions */ inchi_free( szOptions ); szOptions = NULL; } /* INChI DLL specific */ ip->bNoStructLabels = 1; if ( 0 > nRet1 ) { goto exit_function; } if ( ip->bNoStructLabels ) { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; } else if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI ) { /* the input may contain both the header and the label of the structure */ if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; } if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) { inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); ip->nInputType = INPUT_INCHI; } PrintInputParms( log_file, ip ); /*********************************/ /* InChI -> Structure conversion */ /*********************************/ /* input_file simulation */ input_file->s.pStr = inpInChI->szInChI; input_file->s.nUsedLength = strlen(input_file->s.pStr)+1; input_file->s.nAllocatedLength = input_file->s.nUsedLength; input_file->s.nPtr = 0; /* buffer for the message */ out->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(out->szMessage[0])); if ( !out->szMessage ) { inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); nRet = -1; } else { nRet = ReadWriteInChI( input_file, output_file, log_file, ip, sd, NULL, NULL, out->szMessage, MAX_MSG_LEN, NULL /*out->WarningFlags*/ ); } if ( nRet >= 0 && output_file->s.pStr ) { /* success */ char *p; out->szInChI = output_file->s.pStr; out->szAuxInfo = NULL; for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) { if ( !memcmp( p, "\nAuxInfo", 8 ) ) { *p = '\0'; /* remove LF after INChI */ out->szAuxInfo = p+1; /* save pointer to AuxInfo */ } else if ( out->szAuxInfo || !p[1]) { /* remove LF after aux info or from the last char */ *p = '\0'; break; } } output_file->s.pStr = NULL; } /* out->szLog = log_file->pStr; log_file->pStr = NULL; */ exit_function:; #if( ADD_CMLPP == 1 ) /* BILLY 8/6/04 */ /* free CML memory */ FreeCml (); FreeCmlDoc( 1 ); #endif for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( ip->path[i] ) { inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ ip->path[i] = NULL; } } SetBitFree( ); #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) if ( num_repeat-- > 0 ) { goto repeat; } #endif #ifdef TARGET_API_LIB /* output */ if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ } if ( out ) { out->szLog = log_file->s.pStr; log_file->s.pStr = NULL; } } #endif translate_RetVal: /* Close internal output streams */ inchi_ios_close(output_file); inchi_ios_close(log_file); inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ switch (nRet) { case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ default : /* if ( !outStruct->atom || !outStruct->num_atoms ) { nRet = inchi_Ret_EOF; } else { int m,n,t=0; for ( m=0; m < 2; m ++ ) { for ( n=0; n < 2; n ++ ) { if ( outStruct->WarningFlags[m][n] ) { t ++; } } } nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; } */ break; } bLibInchiSemaphore = 0; return nRet; } EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) { if ( ( inpInChI ) && ( inpInChI->szInChI ) && ( strlen(inpInChI->szInChI) >= LEN_INCHI_STRING_PREFIX+3 ) && ( inpInChI->szInChI[LEN_INCHI_STRING_PREFIX+1] == 'S' ) ) /* brief check indicated valid std input (more checks in GetStructFromINCHI) */ return GetStructFromINCHI( inpInChI, outStruct ); else /* non-std or just invalid input */ return inchi_Ret_ERROR; } /*************************************************************/ EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) { STRUCT_DATA struct_data; STRUCT_DATA *sd = &struct_data; INCHI_IOSTREAM inchi_file[3]; INCHI_IOSTREAM *output_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; static char szMainOption[] = " ?InChI2Struct"; int i; char szSdfDataValue[MAX_SDF_VALUE+1]; unsigned long ulDisplTime = 0; /* infinite, milliseconds */ INPUT_PARMS inp_parms; INPUT_PARMS *ip = &inp_parms; int bReleaseVersion = bRELEASE_VERSION; int nRet = 0, nRet1; int bStdFormat = 0; /* conversion result */ inp_ATOM *at=NULL; int num_at = 0; #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) int num_repeat = REPEAT_ALL; #endif const char *argv[INCHI_MAX_NUM_ARG+1]; int argc; char *szOptions = NULL; if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ return inchi_Ret_BUSY; } #if 0 /* moved to after call to CheckINCHI - Marc 2010 */ bLibInchiSemaphore = 1; #endif #if( TRACE_MEMORY_LEAKS == 1 ) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif /* turn on floating point exceptions */ #if ( !defined(__STDC__) || __STDC__ != 1 ) { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif #endif memset( outStruct, 0, sizeof(*outStruct) ); #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) repeat: FreeStructFromINCHI( &outStruct ); inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ inchi_ios_close(output_file); inchi_ios_close(log_file); #endif sd->bUserQuit = 0; /*^^^ Initialize internal for this function I/O streams as string buffers */ inchi_ios_init(input_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); /* clear original input structure */ memset( sd, 0, sizeof(*sd) ); memset( ip, 0, sizeof(*ip) ); memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); szMainOption[1] = INCHI_OPTION_PREFX; if ( !inpInChI ) { nRet = _IS_ERROR; goto exit_function; } /* options */ if ( inpInChI /*&& inpInChI->szOptions*/ ) { /* fix bug discovered by Burt Leland 2008-12-23 */ int opt_len = (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1; szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); if ( szOptions ) { if ( inpInChI->szOptions ) /* fix bug discovered by Burt Leland 2008-12-23 */ strcpy( szOptions, inpInChI->szOptions ); strcat( szOptions, szMainOption ); argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); } else { nRet = _IS_FATAL; goto translate_RetVal; /* emergency exit */ } } else { argc = 1; argv[0] = ""; argv[1] = NULL; } if ( argc == 1 #ifdef TARGET_API_LIB && (!inpInChI || !inpInChI->szInChI) #endif || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { HelpCommandLineParms(log_file); outStruct->szLog = log_file->s.pStr; nRet = _IS_EOF; goto translate_RetVal; } nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); if ( szOptions ) { /* argv pointed to strings in szOptions */ inchi_free( szOptions ); szOptions = NULL; } /* INChI DLL specific */ ip->bNoStructLabels = 1; if ( 0 > nRet1 ) { goto exit_function; } if ( ip->bNoStructLabels ) { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; } else if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI ) { /* the input may contain both the header and the label of the structure */ if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; } if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) { inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); ip->nInputType = INPUT_INCHI; } if ( !inpInChI->szInChI ) { nRet = _IS_ERROR; goto exit_function; } else { const int strict=0; /* do not use strict mode, it may be too alarmous */ nRet = CheckINCHI(inpInChI->szInChI, strict); if (nRet == INCHI_VALID_STANDARD) { bStdFormat = 1; } else if (nRet == INCHI_VALID_NON_STANDARD) { ; } else { nRet = _IS_ERROR; goto exit_function; } } if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ return inchi_Ret_BUSY; } bLibInchiSemaphore = 1; PrintInputParms( log_file, ip ); /*********************************/ /* InChI -> Structure conversion */ /*********************************/ /* input_file simulation */ input_file->s.pStr = inpInChI->szInChI; input_file->s.nUsedLength = strlen(input_file->s.pStr)+1; input_file->s.nAllocatedLength = input_file->s.nUsedLength; input_file->s.nPtr = 0; /* buffer for the message */ outStruct->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(outStruct->szMessage[0])); if ( !outStruct->szMessage ) { inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); nRet = -1; } else { nRet = ReadWriteInChI( input_file, output_file, log_file, ip, sd, &at, &num_at, outStruct->szMessage, MAX_MSG_LEN, outStruct->WarningFlags ); } if ( nRet >= 0 && at && num_at ) { /* success */ nRet = InpAtom0DToInchiAtom( at, num_at, outStruct ); if ( at ) { inchi_free( at ); at = NULL; } if ( nRet < 0 ) { inchi_ios_eprint( log_file, "Final structure conversion failed\n" ); } } outStruct->szLog = log_file->s.pStr; exit_function:; #if( ADD_CMLPP == 1 ) /* BILLY 8/6/04 */ /* free CML memory */ FreeCml (); FreeCmlDoc( 1 ); #endif for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( ip->path[i] ) { inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ ip->path[i] = NULL; } } SetBitFree( ); #if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) if ( num_repeat-- > 0 ) { goto repeat; } #endif #ifdef TARGET_API_LIB /* output */ if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ } if ( outStruct ) { outStruct->szLog = log_file->s.pStr; log_file->s.pStr = NULL; } } #endif translate_RetVal: /* Close internal I/O streams */ inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ inchi_ios_close(output_file); inchi_ios_close(log_file); switch (nRet) { case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ default : if ( !outStruct->atom || !outStruct->num_atoms ) { nRet = inchi_Ret_EOF; } else { int m,n,t=0; for ( m=0; m < 2; m ++ ) { for ( n=0; n < 2; n ++ ) { if ( outStruct->WarningFlags[m][n] ) { t ++; } } } nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; } break; } bLibInchiSemaphore = 0; return nRet; } /********************************************************************/ #if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) /* Win32 & MS VC ++, compile and link as a DLL */ /*********************************************************/ /* C calling conventions export from Win32 dll */ /*********************************************************/ /* prototypes */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ); int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); void cdecl_FreeINCHI( inchi_Output *out ); void cdecl_FreeStdINCHI( inchi_Output *out ); int cdecl_GetStringLength( char *p ); int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ); void cdecl_Free_inchi_Input( inchi_Input *pInp ); void cdecl_Free_std_inchi_Input( inchi_Input *pInp ); int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ); void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ); int cdecl_CheckINCHI(const char *szINCHI, const int strict); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export without cdecl_ prefixes */ /********************************************************/ int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ) { return GetINCHI( inp, out ); } /********************************************************/ int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) { return GetStdINCHI( inp, out ); } /********************************************************/ void cdecl_FreeINCHI( inchi_Output *out ) { FreeINCHI( out ); } /********************************************************/ void cdecl_FreeStdINCHI( inchi_Output *out ) { FreeStdINCHI( out ); } /********************************************************/ int cdecl_GetStringLength( char *p ) { return GetStringLength( p ); } /********************************************************/ int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ) { return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, pInchiInp ); } /********************************************************/ /********************************************************/ int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ) { return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, pInchiInp ); } /********************************************************/ void cdecl_Free_std_inchi_Input( inchi_Input *pInp ) { Free_std_inchi_Input( pInp ); } /********************************************************/ void cdecl_Free_inchi_Input( inchi_Input *pInp ) { Free_inchi_Input( pInp ); } /********************************************************/ int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) { return GetStructFromINCHI( inpInChI, outStruct ); } /********************************************************//********************************************************/ int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) { return GetStructFromStdINCHI( inpInChI, outStruct ); } /********************************************************/ void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ) { FreeStructFromINCHI( outStruct ); } /********************************************************/ int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ) { return GetINCHIfromINCHI( inpInChI, out ); } /********************************************************/ void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ) { FreeStructFromStdINCHI( outStruct ); } /********************************************************/ int cdecl_CheckINCHI(const char *szINCHI, const int strict) { return CheckINCHI( szINCHI, strict ); } #endif #if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) #include /*********************************************************/ /* Pacal calling conventions export from Win32 dll */ /*********************************************************/ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* prototypes */ int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ); int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); void PASCAL pasc_FreeINCHI( inchi_Output *out ); void PASCAL pasc_FreeStdINCHI( inchi_Output *out ); int PASCAL pasc_GetStringLength( char *p ); int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ); int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ); void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ); void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ); void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ); int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export without PASCAL pasc_ prefixes */ /********************************************************/ int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ) { return GetINCHI( inp, out ); } /********************************************************/ int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) { return GetStdINCHI( inp, out ); } /********************************************************/ void PASCAL pasc_FreeINCHI( inchi_Output *out ) { FreeINCHI( out ); } /********************************************************/ void PASCAL pasc_FreeStdINCHI( inchi_Output *out ) { FreeStdINCHI( out ); } /********************************************************/ int PASCAL pasc_GetStringLength( char *p ) { return GetStringLength( p ); } /********************************************************/ int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ) { return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, pInchiInp ); } /********************************************************/ int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, int bDoNotAddH, InchiInpData *pInchiInp ) { return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, pInchiInp ); } /********************************************************/ void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ) { Free_inchi_Input( pInp ); } /********************************************************/ void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ) { Free_std_inchi_Input( pInp ); } /********************************************************/ void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ) { FreeStructFromINCHI( out ); } /********************************************************/ void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ) { FreeStructFromStdINCHI( out ); } /********************************************************//********************************************************/ int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) { return GetStructFromINCHI( inp, out ); } /********************************************************//********************************************************/ int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) { return GetStructFromStdINCHI( inp, out ); } /********************************************************/ int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict) { return CheckINCHI( szINCHI, strict ); } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll_a.c000066400000000000000000001561411271037650300241520ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChI - API 1.02 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #include #include #include #include #include #include #include #include #include #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "ichierr.h" #include "ichimain.h" #include "extr_ct.h" #include "ichi_io.h" #include "ichicomp.h" #include "ichitaut.h" #include "ichinorm.h" #include "ichisize.h" #include "mode.h" #include "inchi_api.h" #include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Local prototypes. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int parse_options_string ( char *cmd, const char *argv[], int maxargs ); int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, inchi_Input *pInp, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ); int NormOneStructureINChI(INCHIGEN_DATA *pGenData, INCHIGEN_CONTROL * HGen, int iINChI, INCHI_IOSTREAM *inp_file); int CanonOneStructureINChI(INCHIGEN_CONTROL *HGen, int iINChI, INCHI_IOSTREAM *inp_file); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChI Generator: create generator Returns handle of generator object or NULL on failure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void) { return INCHIGEN_Create(); } INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void) { INCHIGEN_CONTROL * HGen = NULL; HGen = (INCHIGEN_CONTROL *)inchi_malloc( sizeof(INCHIGEN_CONTROL) ); if (!HGen) return (INCHIGEN_HANDLE) NULL; memset(HGen, 0, sizeof(INCHIGEN_CONTROL)); /*^^^ Set/init aliases */ memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); HGen->ulTotalProcessingTime = 0; HGen->num_err = 0; HGen->num_inp = 0; HGen->szTitle[0] = '\0'; HGen->pStr = (char*) inchi_malloc(PSTR_BUFFER_SIZE); if (!HGen->pStr) { inchi_free(HGen); return (INCHIGEN_HANDLE) NULL; } HGen->pStr[0] = '\0'; /*^^^ Initialize output streams as string buffers */ inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_STRING, NULL); memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); return (INCHIGEN_HANDLE) HGen; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChI Generator: initialization stage (accepts a specific structure) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; INPUT_PARMS *ip = &(HGen->InpParms); STRUCT_DATA *sd = &(HGen->StructData); int retcode = inchi_Ret_OKAY; int force_std=0; retcode = INCHIGEN_Setup(_HGen, pGenData, pInp); /* Ensure standardness */ if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SaveOpt ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "RecMet ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) { ip->nMode &= ~REQ_MODE_BASIC; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "FixedH ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SREL ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SRAC ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) { ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SUCF ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) { ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SLUUD ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) { ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "SUU ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) { ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "KET ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) { ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; /* if ( !force_std ) { AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); force_std = 1; } AddMOLfileError(sd->pStrErrStruct, "15T ignored"); sd->nErrorType = _IS_WARNING; */ retcode = _IS_WARNING; } /* And anyway... */ ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); return retcode; } int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { int retcode = inchi_Ret_OKAY; INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); STRUCT_DATA *sd = &(HGen->StructData); INPUT_PARMS *ip = &(HGen->InpParms); INCHI_IOSTREAM *log_file = HGen->inchi_file+1; INCHI_IOSTREAM prbstr, *prb_file=&prbstr; const char *argv[INCHI_MAX_NUM_ARG+1]; int argc; char *szOptions = NULL; char szSdfDataValue[MAX_SDF_VALUE+1]; int bReleaseVersion = bRELEASE_VERSION; unsigned long ulDisplTime = 0; /* infinite, milliseconds */ int p; /*^^^ Make allocs/inits */ if (!pGenData) { retcode = _IS_ERROR; goto ret; } memset(pGenData, 0, sizeof(*pGenData)); /*^^^ Parse 'command-line' options and fill internal INPUT_PARMS structure */ if ( pInp && pInp->szOptions ) { szOptions = (char*)inchi_malloc( strlen(pInp->szOptions) + 1 ); if (!szOptions) return _IS_FATAL; /*^^^ Not enough memory.... */ else { /*^^^ Parse. */ strcpy( szOptions, pInp->szOptions ); argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); } } else { /*^^^ Got NULL options string or NULL 'pInp', will use defaults. */ argc = 1; argv[0] = ""; argv[1] = NULL; } if ( argc == 1 #ifdef TARGET_API_LIB && (!pInp || pInp->num_atoms <= 0 || !pInp->atom) #endif || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { HelpCommandLineParms(log_file); memset( log_file, 0, sizeof(*log_file) ); return _IS_EOF; } memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); /*^^^ Decrypt. */ /*^^^ NB: ReadCommandLineParms resides in _header_ file, ichiparm.h */ retcode = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); if (szOptions) inchi_free( szOptions ); /* INChI DLL specific */ ip->bNoStructLabels = 1; if ( 0 > retcode) goto ret; if ( ip->bNoStructLabels ) { ip->pSdfLabel = NULL; ip->pSdfValue = NULL; } else if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE ) { /* the input may contain both the header and the label of the structure */ if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; } if (retcode!=inchi_Ret_OKAY) goto ret; PrintInputParms( log_file, ip); /*^^^ Extract the structure */ retcode = ExtractOneStructure( sd, ip, HGen->szTitle, pInp, log_file, HGen->inchi_file, /* output_file */ prb_file, orig_inp_data, &(HGen->num_inp), HGen->pStr, PSTR_BUFFER_SIZE ); ret:switch (retcode) { case _IS_OKAY : retcode = inchi_Ret_OKAY ; HGen->init_passed = 1; break; /* Success; break; no errors or warnings */ case _IS_ERROR : (HGen->num_err)++; retcode = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ case _IS_FATAL : (HGen->num_err)++; retcode = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ case _IS_SKIP : retcode = inchi_Ret_SKIP ; break; /* not used in INChI dll */ case _IS_EOF : retcode = inchi_Ret_EOF ; break; /* no structural data has been provided */ case _IS_WARNING: retcode = inchi_Ret_WARNING; HGen->init_passed = 1; break; /* Success; break; warning(s) issued */ case _IS_UNKNOWN: default : retcode = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ } if (!pGenData) { strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); for (p=0; p < INCHI_NUM; p++) pGenData->num_components[p] = sd->num_components[p]; } return retcode; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get normalized form of the structure. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen,INCHIGEN_DATA * pGenData) { return INCHIGEN_DoNormalization( HGen, pGenData); } int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData) { int nRet=0, nRet1=0; /* int maxINChI=0; */ INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; INPUT_PARMS *ip = &(HGen->InpParms); STRUCT_DATA *sd = &(HGen->StructData); NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); INCHI_IOSTREAM *output_file = HGen->inchi_file; INCHI_IOSTREAM inpstr, *inp_file = &inpstr; ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); ORIG_STRUCT *pOrigStruct = NULL; int k; #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) int ret1=0, ret2=0; #endif /*^^^ Set debug output */ #if (TRACE_MEMORY_LEAKS == 1) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif #if ( !defined(__STDC__) || __STDC__ != 1 ) /* turn on floating point exceptions */ { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ #endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ if (HGen->init_passed==0) { AddMOLfileError(sd->pStrErrStruct, "InChI generator not initialized"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; goto exit_function; } inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); sd->bUserQuitComponent = 0; sd->bUserQuitComponentDisplay = 0; memset( HGen->composite_norm_data, 0, sizeof(HGen->composite_norm_data) ); memset( pncFlags, 0, sizeof(*pncFlags) ); /* for testing only */ #if( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0 ); #endif #if( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) { long num_inp2 = HGen->num_inp; AddMOLfileError(sd->pStrErrStruct, "Underivatization error"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); goto exit_function; /* output only if derivatives found */ } #endif /* UNDERIVATIZE == 1 */ #if( RING2CHAIN == 1 ) /***** post v.1 feature *****/ if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) { long num_inp2 = HGen->num_inp; AddMOLfileError(sd->pStrErrStruct, "Ring to chain error"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); goto exit_function; /* output only if derivatives found */ } #endif /* RING2CHAIN == 1 */ #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) { goto exit_function; /* output only if derivatives or ring/chain found */ } #endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ /***** output MOLfile ***************/ if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { char szNumber[32]; int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ ret1a = sprintf(szNumber, "Structure #%ld", HGen->num_inp); ret2a = WriteOrigAtomDataToSDfile( orig_inp_data, output_file, szNumber, NULL, (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); goto exit_function; } /******* create full reversibility information **************/ if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) { pOrigStruct = &(HGen->OrigStruct); memset( pOrigStruct, 0, sizeof(*pOrigStruct)); if ( FillOutOrigStruct( orig_inp_data, pOrigStruct, sd ) ) { AddMOLfileError(sd->pStrErrStruct, "Cannot interpret reversibility information"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; } } sd->bUserQuit = 0; if (sd->bUserQuit) goto exit_function; /*^^^ Normalize the whole disconnected or original structure */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { nRet1 = NormOneStructureINChI( pGenData, HGen, INCHI_BAS, inp_file); nRet = inchi_max(nRet, nRet1); } /* if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) maxINChI = 1; */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR && (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { /* Normalize the whole reconnected structure */ nRet1 = NormOneStructureINChI( pGenData, HGen, INCHI_REC, inp_file); nRet = inchi_max(nRet, nRet1); /* if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) maxINChI = 2; */ } exit_function: if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) HGen->norm_passed = 1; for (k=0; k < INCHI_NUM; k++) pGenData->num_components[k] = sd->num_components[k]; /*^^^ issue normalization warnings */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { int ic, istruct, itaut, nc[2]; int warn_prot=0, warn_neutr=0; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ nc[0] = pGenData->num_components[0]; nc[1] = pGenData->num_components[1]; for (istruct=0; istruct<2; istruct++) { if (nc[istruct]>0) { for (ic=0; ic < nc[istruct]; ic++) { inp_norm_data[0] = &(HGen->InpNormAtData[istruct][ic]); inp_norm_data[1] = &(HGen->InpNormTautData[istruct][ic]); for (itaut=0;itaut<2;itaut++) { if (NULL!=inp_norm_data[itaut]) { if ( inp_norm_data[itaut]->bTautomeric ) { if (inp_norm_data[itaut]->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) if (warn_prot==0) { warn_prot++; AddMOLfileError(sd->pStrErrStruct, "Proton(s) added/removed"); } if (inp_norm_data[itaut]->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) if (warn_neutr==0) { warn_neutr++; AddMOLfileError(sd->pStrErrStruct, "Charges neutralized"); } } } } /* itaut */ } } } } strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); make_norm_atoms_from_inp_atoms(pGenData, HGen); return nRet; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get canonicalized form of the structure. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int INCHI_DECL STDINCHIGEN_DoCanonicalization (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData) { return INCHIGEN_DoCanonicalization(HGen, pGenData ) ; } int INCHI_DECL INCHIGEN_DoCanonicalization (INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ) { int nRet = 0, nRet1 /*, maxINChI=0*/; INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; STRUCT_DATA *sd = &(HGen->StructData); INPUT_PARMS *ip = &(HGen->InpParms); INCHI_IOSTREAM *output_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; INCHI_IOSTREAM prbstr, *prb_file=&prbstr; INCHI_IOSTREAM inpstr, *inp_file = &inpstr; ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); int k; /*^^^ Set debug output */ #if (TRACE_MEMORY_LEAKS == 1) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif #if ( !defined(__STDC__) || __STDC__ != 1 ) /* turn on floating point exceptions */ { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ #endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ if (HGen->norm_passed==0) { AddMOLfileError(sd->pStrErrStruct, "Got non-normalized structure"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; goto exit_function; } inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); sd->bUserQuit = 0; if (sd->bUserQuit) goto exit_function; /* create INChI for each connected component of the structure and optionally display them */ /* output INChI for the whole structure */ /* create INChI for each connected component of the structure and optionally display them */ /* create INChI for the whole disconnected or original structure */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { nRet1 = CanonOneStructureINChI(HGen, INCHI_BAS, inp_file); nRet = inchi_max(nRet, nRet1); } /* if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) maxINChI = 1; */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR && (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { /* create INChI for the whole reconnected structure */ nRet1 = CanonOneStructureINChI(HGen, INCHI_REC, inp_file); nRet = inchi_max(nRet, nRet1); /* if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) maxINChI = 2; */ } if (nRet != _IS_FATAL && nRet != _IS_ERROR) { if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && (ip->nMode & REQ_MODE_STEREO) && !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && !bIsStructChiral( HGen->pINChI, sd->num_components ) ) { AddMOLfileError(sd->pStrErrStruct, "Not chiral"); } /*************************************/ /* Output err/warn messages */ /*************************************/ if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) { /* if successful then returns 0, otherwise returns _IS_FATAL */ /* extract the structure if requested */ nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, HGen->num_inp, inp_file, log_file, output_file, prb_file, HGen->pStr, PSTR_BUFFER_SIZE); nRet = inchi_max(nRet, nRet1); } } switch (nRet) { case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ case _IS_OKAY : nRet = inchi_Ret_OKAY ; HGen->canon_passed = 1; break; /* Success; break; no errors or warnings */ case _IS_WARNING: nRet = inchi_Ret_WARNING; HGen->canon_passed = 1; break; /* Success; break; warning(s) issued */ case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ case _IS_UNKNOWN: default : nRet = inchi_Ret_UNKNOWN; break; /* Unknown program error */ } exit_function: strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); for (k=0; k < INCHI_NUM; k++) pGenData->num_components[k] = sd->num_components[k]; return nRet; } /*^^^ INCHIGEN_DoCanonicalization */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Get serialized form (InChI string). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults) { return INCHIGEN_DoSerialization(HGen, pGenData, pResults); } int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults) { int nRet=0, nRet1=0, i, k; INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; INPUT_PARMS *ip = &(HGen->InpParms); INCHI_IOSTREAM *output_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; INCHI_IOSTREAM inpstr, *inp_file = &inpstr; INCHI_IOSTREAM prbstr, *prb_file=&prbstr; STRUCT_DATA *sd = &(HGen->StructData); NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); ORIG_STRUCT *pOrigStruct = &(HGen->OrigStruct); int bSortPrintINChIFlags=0; unsigned char save_opt_bits=0; int retcode = 0; /*^^^ Post-1.02b - added initialization of pResults to 0; thanks to David Foss */ memset(pResults, 0, sizeof(*pResults)); pResults->szLog = log_file->s.pStr; inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); /*^^^ Set debug output */ #if (TRACE_MEMORY_LEAKS == 1) _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); /* for execution outside the VC++ debugger uncomment one of the following two */ #ifdef MY_REPORT_FILE _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); #else _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); #endif #if ( !defined(__STDC__) || __STDC__ != 1 ) /* turn on floating point exceptions */ { /* Get the default control word. */ int cw = _controlfp( 0,0 ); /* Set the exception masks OFF, turn exceptions on. */ /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); /* Set the control word. */ _controlfp( cw, MCW_EM ); } #endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ #endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ /*****************************/ if (HGen->canon_passed==0) { AddMOLfileError(sd->pStrErrStruct, "Got non-canonicalized structure"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; retcode = _IS_ERROR; goto frees; } /************************************************/ /* sort and print INChI for the whole structure */ /************************************************/ /* Prepare SaveOpt bits */ if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) save_opt_bits |= SAVE_OPT_RECMET; if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) save_opt_bits |= SAVE_OPT_FIXEDH; if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) save_opt_bits |= SAVE_OPT_SLUUD; if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) save_opt_bits |= SAVE_OPT_SUU; if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) save_opt_bits |= SAVE_OPT_KET; if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) save_opt_bits |= SAVE_OPT_15T; } nRet = SortAndPrintINChI(output_file, HGen->pStr, PSTR_BUFFER_SIZE, log_file, ip, orig_inp_data, prep_inp_data, HGen->composite_norm_data, pOrigStruct, sd->num_components, sd->num_non_taut, sd->num_taut, sd->bTautFlags, sd->bTautFlagsDone, pncFlags, HGen->num_inp, HGen->pINChI, HGen->pINChI_Aux, &bSortPrintINChIFlags, save_opt_bits); /* XML struct end tag */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { if ( !OutputINChIXmlStructEndTag( output_file, HGen->pStr, PSTR_BUFFER_SIZE, 1 ) ) { inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", HGen->num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->bXmlStructStarted = -1; /* do not repeat same message */ nRet = _IS_FATAL; } else { sd->bXmlStructStarted = 0; /* do not continue xml output for this structure */ } } if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { /* Special mode: extract all good MOLfiles into the problem file * Do not extract any MOLfile that could not be processed (option /PGO) */ if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); } #if( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) else if ( prb_file->f && (bSortPrintINChIFlags & ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) ) { CopyMOLfile(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); } #endif } for ( i = 0; i < INCHI_NUM; i ++ ) { for ( k = 0; k < TAUT_NUM+1; k ++ ) { FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); } } /*****************************/ /*^^^ Prepare output message(s). */ /*^^^ Error/warning. */ if ( sd->pStrErrStruct[0] ) if ( pGenData && (pResults->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) strcpy( pResults->szMessage, sd->pStrErrStruct ); /*^^^ InChI, AuxInfo (go to pResults->szInChI, pResults->szAuxInfo) */ if ( output_file->s.pStr && output_file->s.nUsedLength > 0 && pGenData ) { char *p; pResults->szInChI = output_file->s.pStr; pResults->szAuxInfo = NULL; if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ for ( p = strchr(pResults->szInChI, '\n'); p; p = strchr(p+1, '\n') ) { if ( !memcmp( p, "\nAuxInfo", 8 ) ) { *p = '\0'; /* remove LF after INChI */ pResults->szAuxInfo = p+1; /* save pointer to AuxInfo */ } else if ( pResults->szAuxInfo || !p[1]) { /* remove LF after aux info or from the last char */ *p = '\0'; break; } } output_file->s.pStr = NULL; } /*^^^ Log message. */ if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ if ( pGenData ) { pResults->szLog = log_file->s.pStr; log_file->s.pStr = NULL; } } if ( output_file->s.pStr ) {inchi_free( output_file->s.pStr ); output_file->s.pStr = NULL;} if ( log_file->s.pStr ) {inchi_free( log_file->s.pStr ); log_file->s.pStr = NULL;} HGen->ulTotalProcessingTime += sd->ulStructTime; nRet = inchi_max(nRet, nRet1); switch ( nRet ) { case _IS_FATAL: case _IS_ERROR: HGen->num_err ++; } /*^^^ XML-related. */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { if ( !OutputINChIXmlStructEndTag( output_file, HGen->pStr, PSTR_BUFFER_SIZE, 1 ) ) { inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%d.%s%s%s%s Terminating.\n", HGen->num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->bXmlStructStarted = -1; /* do not repeat same message */ } } if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && ip->bXmlStarted ) { OutputINChIXmlRootEndTag( output_file ); ip->bXmlStarted = 0; } frees: /*^^^ Free all. */ /* free INChI memory */ /* FreeAllINChIArrays(HGen->pINChI, HGen->pINChI_Aux, sd->num_components ); */ #if( ADD_CMLPP == 1 ) /* BILLY 8/6/04 */ /* free CML memory */ FreeCml (); FreeCmlDoc( 1 ); #endif for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( ip->path[i] ) { inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ ip->path[i] = NULL; } } SetBitFree( ); strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); for (k=0; k < INCHI_NUM; k++) pGenData->num_components[k] = sd->num_components[k]; return retcode; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChI Generator: reset stage (use before get next structure) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults) { INCHIGEN_Reset(HGen, pGenData, pResults); } void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults) { int i, k, nc; INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; if ( pResults->szInChI ) inchi_free( pResults->szInChI ); if ( pResults->szLog ) inchi_free( pResults->szLog ); if ( pResults->szMessage ) inchi_free( pResults->szMessage ); /* Free all data associated with components of disconn/conn structures */ if (NULL!=HGen) { /*^^^ Re-initialize output streams/string buffers */ inchi_ios_close(&(HGen->inchi_file[0])); inchi_ios_close(&(HGen->inchi_file[1])); inchi_ios_close(&(HGen->inchi_file[2])); inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_STRING, NULL); inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_STRING, NULL); if (HGen->pStr ) memset(HGen->pStr, 0, sizeof( (*HGen->pStr) )); for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { if ( HGen->InpParms.path[i] ) { inchi_free( (char*) HGen->InpParms.path[i] ); /* cast deliberately discards 'const' qualifier */ HGen->InpParms.path[i] = NULL; } } memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); FreeOrigAtData( &(HGen->OrigInpData) ); memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); FreeOrigAtData( &(HGen->PrepInpData[0])); FreeOrigAtData( &(HGen->PrepInpData[1])); memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); FreeOrigStruct( &(HGen->OrigStruct)); memset(&(HGen->OrigStruct), 0, sizeof( HGen->OrigStruct ) ); for ( i = 0; i < INCHI_NUM; i ++ ) for ( k = 0; k < TAUT_NUM+1; k ++ ) FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); for ( k = 0; k < INCHI_NUM; k++) { nc = HGen->StructData.num_components[k]; if ( HGen->InpCurAtData[k] ) { for ( i = 0; i < nc; i ++ ) FreeInpAtomData( &(HGen->InpCurAtData[k][i]) ); inchi_free(HGen->InpCurAtData[k]); HGen->InpCurAtData[k] = NULL; } if ( HGen->cti[k] ) { if ( (HGen->cti[k])->at[TAUT_YES] ) { inchi_free( (HGen->cti[k])->at[TAUT_YES] ); (HGen->cti[k])->at[TAUT_YES] = NULL; } if ( (HGen->cti[k])->at[TAUT_NON] ) { inchi_free( (HGen->cti[k])->at[TAUT_NON] ); (HGen->cti[k])->at[TAUT_NON] = NULL; } if (&((HGen->cti[k])->vt_group_info)) free_t_group_info(&((HGen->cti[k])->vt_group_info)); if (&((HGen->cti[k])->vt_group_info_orig)) free_t_group_info(&((HGen->cti[k])->vt_group_info_orig)); inchi_free(HGen->cti[k]); HGen->cti[k] = NULL; } } for ( k = 0; k < INCHI_NUM; k++) { nc = HGen->StructData.num_components[k]; if ( HGen->InpNormAtData[k] ) { for ( i = 0; i < nc; i ++ ) FreeInpAtomData( &(HGen->InpNormAtData[k][i]) ); inchi_free(HGen->InpNormAtData[k]); HGen->InpNormAtData[k] = NULL; } if ( HGen->InpNormTautData[k] ) { for ( i = 0; i < nc; i ++ ) FreeInpAtomData( &(HGen->InpNormTautData[k][i]) ); inchi_free(HGen->InpNormTautData[k]); HGen->InpNormTautData[k] = NULL; } if ( pGenData->NormAtomsTaut[k] ) { /* for ( i = 0; i < nc; i ++ ) FreeInpAtomData( &(pGenData->NormAtomsTaut[k][i]) ); */ inchi_free(pGenData->NormAtomsTaut[k]); pGenData->NormAtomsTaut[k] = NULL; } if ( pGenData->NormAtomsNontaut[k] ) { /* for ( i = 0; i < nc; i ++ ) FreeInpAtomData( &(pGenData->NormAtomsNontaut[k][i]) ); */ inchi_free(pGenData->NormAtomsNontaut[k]); pGenData->NormAtomsNontaut[k] = NULL; } } /* free INChI memory */ FreeAllINChIArrays( HGen->pINChI, HGen->pINChI_Aux, HGen->StructData.num_components ); memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); HGen->szTitle[0] = '\0'; } memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); memset( pResults, 0, sizeof(*pResults) ); memset( pGenData , 0, sizeof(*pGenData) ); return; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ InChI Generator: destroy generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) { INCHIGEN_Destroy(HGen) ; } void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE _HGen) { INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; if (NULL!=HGen) { if ( HGen->pStr ) inchi_free(HGen->pStr); inchi_ios_close(&(HGen->inchi_file[0])); inchi_ios_close(&(HGen->inchi_file[1])); inchi_ios_close(&(HGen->inchi_file[2])); inchi_free(HGen); } } /********************************************************************/ #if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) /* Win32 & MS VC ++, compile and link as a DLL */ /*********************************************************/ /* C calling conventions export from Win32 dll */ /*********************************************************/ /* prototypes */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void); INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void); int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int cdecl_INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); int cdecl_STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export withou cdecl_ prefixes */ /********************************************************/ INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void) { return INCHIGEN_Create( ); } /********************************************************/ INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void) { return STDINCHIGEN_Create( ); } /********************************************************/ int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { return INCHIGEN_Setup( HGen, pGenData, pInp ); } /********************************************************/ int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { return STDINCHIGEN_Setup( HGen, pGenData, pInp ); } /********************************************************/ int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return INCHIGEN_DoNormalization( HGen, pGenData ); } /********************************************************/ int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return STDINCHIGEN_DoNormalization( HGen, pGenData ); } /********************************************************/ int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return INCHIGEN_DoCanonicalization( HGen, pGenData ); }/********************************************************/ int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); } /********************************************************/ int cdecl_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) { return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); } /********************************************************/ int cdecl_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) { return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); } /********************************************************/ void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) { INCHIGEN_Reset( HGen, pGenData, pResults ); } /********************************************************/ void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) { STDINCHIGEN_Reset( HGen, pGenData, pResults ); } /********************************************************/ void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) { INCHIGEN_Destroy( HGen ); } /********************************************************/ void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) { STDINCHIGEN_Destroy( HGen ); } #endif #if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) #include /*********************************************************/ /* Pacal calling conventions export from Win32 dll */ /*********************************************************/ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* prototypes */ /********************************************************/ INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void); INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void); int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif /* implementation */ /* libinchi.def provides export without PASCAL pasc_ prefixes */ /********************************************************/ INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void) { return INCHIGEN_Create( ); } /********************************************************/ INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void) { return STDINCHIGEN_Create( ); } /********************************************************/ int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { return INCHIGEN_Setup( HGen, pGenData, pInp); } /********************************************************/ int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) { return STDINCHIGEN_Setup( HGen, pGenData, pInp); } /********************************************************/ int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return INCHIGEN_DoNormalization( HGen, pGenData ); }/********************************************************/ int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return STDINCHIGEN_DoNormalization( HGen, pGenData ); } /********************************************************/ int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return INCHIGEN_DoCanonicalization( HGen, pGenData ); }/********************************************************/ int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) { return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); } /********************************************************/ int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) { return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); }/********************************************************/ int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) { return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); } /********************************************************/ void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) { INCHIGEN_Reset( HGen, pGenData, pResults ); } /********************************************************/ void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) { STDINCHIGEN_Reset( HGen, pGenData, pResults ); } /********************************************************/ void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen) { INCHIGEN_Destroy( HGen); } /********************************************************/ void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) { STDINCHIGEN_Destroy( HGen); } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll_a.h000066400000000000000000000120751271037650300241540ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHI_DLL_A_H__ #define __INCHI_DLL_A_H__ /*^^^ */ #include "ichicant.h" typedef struct tagCOMPONENT_TREAT_INFO { int n1; int n2; int num_atoms; int num_at_tg; int num_deleted_H; int num_deleted_H_taut; INCHI_MODE nMode; T_GROUP_INFO vt_group_info; T_GROUP_INFO vt_group_info_orig; ATOM_SIZES s[TAUT_NUM]; BCN Bcn; int bHasIsotopicAtoms; int bMayHaveStereo; int num_taut_at; int bPointedEdgeStereo; int vABParityUnknown; /* actual value of constant for unknown parity (2009-12-10 ) */ INCHI_MODE bTautFlags; INCHI_MODE bTautFlagsDone; INCHI_MODE nUserMode; sp_ATOM *at[TAUT_NUM]; inp_ATOM *out_at; int fix_isofixedh; /*^^^ 04-12-2008 */ int fix_termhchrg; /*^^^ 07-06-2008 */ } COMPONENT_TREAT_INFO; typedef struct tagINCHIGEN_CONTROL { int init_passed; int norm_passed; int canon_passed; INPUT_PARMS InpParms; unsigned long ulTotalProcessingTime; char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; char *pStr; long num_err; long num_inp; ORIG_STRUCT OrigStruct; ORIG_ATOM_DATA OrigInpData; /*^^^ For the whole structure: */ STRUCT_DATA StructData; /*^^^ For each member of pair disconnected/reconnected structures: */ ORIG_ATOM_DATA PrepInpData[INCHI_NUM]; /*^^^ INCHI_NUM=2; 0 disconnected/original 1 reconnected */ INP_ATOM_DATA *InpCurAtData[INCHI_NUM]; INP_ATOM_DATA *InpNormAtData[INCHI_NUM]; INP_ATOM_DATA *InpNormTautData[INCHI_NUM]; COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; /*^^^ TAUT_NUM=2; 0 non-tautomeric 1 tautomeric 2 intermediate tautomeric */ NORM_CANON_FLAGS ncFlags; /*^^^ For each connected component of structures: */ PINChI2 *pINChI[INCHI_NUM]; PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; /*^^^ For each connected component of structures: */ COMPONENT_TREAT_INFO *cti[INCHI_NUM]; /*^^^ Placed at the end intentionally */ INCHI_IOSTREAM inchi_file[3]; } INCHIGEN_CONTROL; /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Exported functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ #if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) /* Win32 & MS VC ++, compile and link as a DLL */ #ifdef _USRDLL /* InChI library dll */ #define INCHI_API __declspec(dllexport) #define EXPIMP_TEMPLATE #define INCHI_DECL __stdcall #else /* calling the InChI dll program */ #define INCHI_API __declspec(dllimport) #define EXPIMP_TEMPLATE extern #define INCHI_DECL __stdcall #endif #else /* create a statically linked InChI library or link to an executable */ #define INCHI_API #define EXPIMP_TEMPLATE #define INCHI_DECL #endif /* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*^^^ Local functions */ void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl); #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #define PSTR_BUFFER_SIZE 64000 #define INCHI_MAX_NUM_ARG 32 #endif /* __INCHI_DLL_A_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll_a2.c000066400000000000000000003231071271037650300242320ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include /* #include */ #include #include /*^^^ */ #include "mode.h" #include "ichitime.h" #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "ichidrp.h" #include "ichierr.h" #include "ichimain.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichi_io.h" #include "ichinorm.h" #include "ichicant.h" #include "ichicano.h" #include "ichicomn.h" #include "ichimake.h" #include "ichister.h" /*^^^ */ #ifdef INCHI_LIB #include "ichi_lib.h" #endif #include "ichicomp.h" #if( ADD_CMLPP == 1 ) #include "readcml.hpp" #include "debug.h" #endif /*^^^ */ /* for DisplayTheWholeStructure() */ #define COMP_ORIG_0_MAIN 0x0001 #define COMP_ORIG_0_RECN 0x0002 #define COMP_PREP_0_MAIN 0x0004 #define COMP_PREP_0_RECN 0x0008 #define COMP_ORIG_1_MAIN 0x0010 #define COMP_ORIG_1_RECN 0x0020 #include "ichisize.h" #include "mode.h" #include "inchi_api.h" #include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd); /*^^^ */ int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); int CheckCanonNumberingCorrectness( int num_atoms, int num_at_tg, sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, char *pStrErrStruct ); int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, int num_components); int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic , AT_STEREO_CARB *LinearCTStereoCarbInv , AT_STEREO_DBLE *LinearCTStereoDbleInv , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ); int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); int FillOutINChIReducedWarn( INChI *pINChI, INChI_Aux *pINChI_Aux, int num_atoms, int num_at_tg, int num_removed_H, sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, INCHI_MODE nUserMode, char *pStrErrStruct ); int NormOneStructureINChI(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file); int CanonOneStructureINChI(INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file); int NormOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i); int CanonOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i); int Normalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], int num_inp_at, INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, COMPONENT_TREAT_INFO *cti); int Canonicalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, INP_ATOM_DATA *out_norm_data[2], struct tagInchiTime *ulMaxTime, T_GROUP_INFO *ti_out, char *pStrErrStruct, COMPONENT_TREAT_INFO *cti); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int NormOneStructureINChI(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file) { int k, i, j, nRet = 0; int nStrLen = PSTR_BUFFER_SIZE; STRUCT_DATA *sd = &(genctl->StructData); INPUT_PARMS *ip = &(genctl->InpParms); ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); ORIG_ATOM_DATA *orig_inp_data = &(genctl->OrigInpData); INCHI_IOSTREAM *output_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; INCHI_IOSTREAM prbstr, *prb_file=&prbstr; PINChI2 **pINChI2 = genctl->pINChI; PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); INP_ATOM_DATA *inp_cur_data = NULL; long num_inp = genctl->num_inp; char *pStr = genctl->pStr; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; inchiTime ulTStart; /*^^^ To save intermediate data... */ COMP_ATOM_DATA *composite_norm_data = genctl->composite_norm_data[iINChI]; INP_ATOM_DATA2 *all_inp_norm_data = NULL; memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); /* if ( orig_inp_data is NOT empty AND prep_inp_data[0] IS empty ) then: 1. copy orig_inp_data --> prep_inp_data[0] 2. fix odd things in prep_inp_data[0] 3. if( orig_inp_data->bDisconnectSalts ) then -- disconnect salts in prep_inp_data[0] 4. move protons to neutralize charges on heteroatoms 5. if( orig_inp_data->bDisconnectCoord ) then -- copy prep_inp_data[0] --> prep_inp_data[1] -- disconnect metals in prep_inp_data[0] [ This all is done in PreprocessOneStructure() ] iINChI = 0 ========= (normal/disconnected layer) 1. normalize prep_inp_data[0] in inp_norm_data[0,1] 2. create INChI[ iINChI ] out of inp_norm_data[0,1] iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 ================================================= (reconnected layer) 1. normalize prep_inp_data[1] in inp_norm_data[0,1] 2. create INChI[ iINChI ] out of inp_norm_data[0,1] */ ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { ; } else if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { return 0; /* nothing to do */ } if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { return 0; } /* m = iINChI; */ /* orig_inp_data index */ if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { AddMOLfileError(sd->pStrErrStruct, "Fatal undetermined program error"); sd->nStructReadError = 97; nRet = sd->nErrorType = _IS_FATAL; goto exit_function; } /******************************************************************* * * * * * Whole structure preprocessing: 1st step of the normalization * * * * Happen only on the first call to CreateOneStructureINChI() * * * * * *******************************************************************/ if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) { /* the structure has not been preprocessed */ if ( ip->msec_MaxTime ) { InchiTimeGet( &ulTStart ); } PreprocessOneStructure( sd, ip, orig_inp_data, prep_inp_data ); pncFlags->bTautFlags[iINChI][TAUT_YES] = pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; switch (sd->nErrorType) { case _IS_ERROR: case _IS_FATAL: /* error message */ nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp, pStr, nStrLen ); goto exit_function; } } /*^^^ To save intermediate data... */ if ( prep_inp_data[iINChI].num_components > 1) { all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); } /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ /* assign values to sd->num_components[] */ MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); if ( k ) { AddMOLfileError(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } /*^^^ Allocate */ /*^^^ visible */ gendata->NormAtomsNontaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); gendata->NormAtomsTaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); /*^^^ invisible */ genctl->InpNormAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); genctl->InpNormTautData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); genctl->InpCurAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); genctl->cti[iINChI] = (COMPONENT_TREAT_INFO *)inchi_calloc( sd->num_components[iINChI], sizeof(COMPONENT_TREAT_INFO)); memset (genctl->cti[iINChI], 0, sd->num_components[iINChI]*sizeof(COMPONENT_TREAT_INFO)); /*^^^ Second normalization step - component by component */ for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) { if (ip->msec_MaxTime) InchiTimeGet( &ulTStart ); inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); /* a) allocate memory and extract current component */ nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, i, num_inp, pStr, nStrLen ); if (ip->msec_MaxTime) ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); switch (nRet) { case _IS_ERROR: case _IS_FATAL: goto exit_cycle; } /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); nRet = NormOneComponentINChI(genctl, iINChI, i); /*^^^ To save intermediate data... */ if ( all_inp_norm_data ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( inp_norm_data[j]->bExists ) { all_inp_norm_data[i][j] = *inp_norm_data[j]; memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); } } } if (nRet) { nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, inp_file, log_file, output_file, prb_file,pStr, nStrLen ); break; } } exit_cycle: switch ( nRet ) { case _IS_FATAL: case _IS_ERROR: break; default: /*^^^ To save intermediate data... */ if ( all_inp_norm_data ) { int res = CreateCompositeNormAtom( composite_norm_data, all_inp_norm_data, prep_inp_data[iINChI].num_components); } break; } /*^^^ When saving intermediate data - avoid memory leaks in case of error */ if ( all_inp_norm_data ) { for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { FreeInpAtomData( &all_inp_norm_data[i][k] ); } } inchi_free( all_inp_norm_data ); all_inp_norm_data = NULL; } exit_function: return nRet; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int CanonOneStructureINChI(INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file) { int i, /*m,*/ nRet = 0; STRUCT_DATA *sd = &(genctl->StructData); INPUT_PARMS *ip = &(genctl->InpParms); INCHI_IOSTREAM *output_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; INCHI_IOSTREAM prbstr, *prb_file=&prbstr; ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); PINChI2 **pINChI2 = genctl->pINChI; PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; long num_inp = genctl->num_inp; char *pStr = genctl->pStr; int nStrLen = PSTR_BUFFER_SIZE; INP_ATOM_DATA *inp_cur_data = NULL; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; inchiTime ulTStart; inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); for (i = 0; i < TAUT_NUM; i ++) /* initialize in case no InChI to generate 2008-12-23 DT */ inp_norm_data[i]=NULL; /**************************************************************************/ /* */ /* */ /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ /* */ /* */ /* O N E B Y O N E */ /* */ /* */ /**************************************************************************/ for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) { if ( ip->msec_MaxTime ) InchiTimeGet( &ulTStart ); /*****************************************************/ /* a) allocate memory and extract current component */ /*****************************************************/ inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, i, num_inp, pStr, nStrLen ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); switch ( nRet ) { case _IS_ERROR: case _IS_FATAL: goto exit_cycle; } #ifndef TARGET_API_LIB /* console request: Display the component? */ if ( ip->bDisplay && inp_file != stdin ) { if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) { sd->bUserQuitComponent = 1; break; } } #endif /*******************************************************************************/ /* */ /* C A N O N I C A L I Z A T I O N */ /* */ /* (both tautomeric and non-tautomeric if requested) */ /* */ /*******************************************************************************/ /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ /*******************************************************************************/ inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); nRet = CanonOneComponentINChI(genctl, iINChI, i); if ( nRet ) { nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, inp_file, log_file, output_file, prb_file,pStr, nStrLen ); break; } } /**************************************************************************/ /* */ /* */ /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ /* */ /* C O M P O N E N T S O N E B Y O N E */ /* */ /* */ /**************************************************************************/ exit_cycle: switch ( nRet ) { case _IS_FATAL: case _IS_ERROR: break; default: break; } for (i = 0; i < TAUT_NUM; i ++) FreeInpAtomData( inp_norm_data[i] ); return nRet; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int NormOneComponentINChI(INCHIGEN_CONTROL * genctl, int iINChI, int i) { STRUCT_DATA *sd = &(genctl->StructData); INPUT_PARMS *ip = &(genctl->InpParms); PINChI2 **pINChI2 = genctl->pINChI; PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; int k, num_at, ret = 0; int bOrigCoord; INCHI_MODE bTautFlags = ip->bTautFlags; INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); long lElapsedTime; /* PINChI2 *pINChI = pINChI2[iINChI]; PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; */ PINChI2 *pINChI = NULL; PINChI_Aux2 *pINChI_Aux = NULL; INChI *cur_INChI[TAUT_NUM]; INChI_Aux *cur_INChI_Aux[TAUT_NUM]; /* pINChI2[m=iINChI-1][j< prep_inp_data[m].num_components][TAUT_NON] */ INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ INP_ATOM_DATA *inp_cur_data = NULL; COMPONENT_TREAT_INFO *cti = NULL; inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); cti = &(genctl->cti[iINChI][i]); inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); pINChI = pINChI2[iINChI]; pINChI_Aux = pINChI_Aux2[iINChI]; InchiTimeGet( &ulTStart ); bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); for ( k = 0; k < TAUT_NUM; k ++ ) { cur_INChI[k] = pINChI[i][k]; cur_INChI_Aux[k] = pINChI_Aux[i][k]; } /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ for ( k = 0; k < TAUT_NUM; k ++ ) { int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? (ip->nMode & REQ_MODE_ISO):0; if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { /* alloc INChI and INChI_Aux */ cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, &inp_cur_data->num_isotopic, nAllocMode ); cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); if ( cur_INChI_Aux[k] ) cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); } else FreeInpAtomData( inp_norm_data[k] ); } lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= lElapsedTime; sd->ulStructTime += lElapsedTime; /****************************************************** * * Get one component canonical numberings, etc. * ******************************************************/ /* * Create_INChI() return value: * num_at <= 0: error code * num_at > 0: number of atoms (excluding terminal hydrogen atoms) * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric */ InchiTimeGet( &ulTStart ); if ( ip->msec_MaxTime ) { ulTEnd = ulTStart; pulTEnd = &ulTEnd; if ( ip->msec_LeftTime > 0 ) InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); } cti->nUserMode = ip->nMode; /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ cti->vABParityUnknown = AB_PARITY_UNDF; if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) { /* Make labels for unknown and undefined stereo different */ cti->vABParityUnknown = AB_PARITY_UNKN; } num_at = Normalization_step( cur_INChI, cur_INChI_Aux, inp_cur_data->at, inp_norm_data, inp_cur_data->num_at, &bTautFlags, &bTautFlagsDone, cti); SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ for ( k = 0; k < TAUT_NUM; k ++ ) { if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) { pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; } } /* Detect errors */ if ( num_at < 0 ) sd->nErrorCode = num_at; else if ( num_at == 0 ) sd->nErrorCode = -1; else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) /* non-tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) /* tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; /* detect and store stereo warnings */ if ( !sd->nErrorCode ) GetProcessingWarnings(cur_INChI, inp_norm_data, sd); lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= lElapsedTime; sd->ulStructTime += lElapsedTime; #ifndef TARGET_API_LIB /* Display the results */ if ( ip->bDisplay ) eat_keyboard_input(); #endif /* a) No matter what happened save the allocated INChI pointers */ /* save the INChI of the current component */ InchiTimeGet( &ulTStart ); for ( k = 0; k < TAUT_NUM; k ++ ) { pINChI[i][k] = cur_INChI[k]; pINChI_Aux[i][k] = cur_INChI_Aux[k]; cur_INChI[k] = NULL; cur_INChI_Aux[k] = NULL; } /* b) Count one component structure and/or INChI results only if there was no error */ /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ if ( !sd->nErrorCode ) { /* find where the current processed structure is located */ int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; if ( cur_is_non_taut + cur_is_taut ) { /* count tautomeric and non-tautomeric components of the structures */ int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; int j; sd->num_non_taut[iINChI] += cur_is_non_taut; sd->num_taut[iINChI] += cur_is_taut; for ( j = j1; j <= j2; j ++ ) { int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || pINChI[i][j]->nNumberOfIsotopicTGroups || pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); if ( j == TAUT_YES ) { bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); } inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; } } } if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) ret = _IS_FATAL; else if ( sd->nErrorCode ) ret = _IS_ERROR; lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= lElapsedTime; sd->ulStructTime += lElapsedTime; return ret; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int CanonOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i) { STRUCT_DATA *sd = &(genctl->StructData); INPUT_PARMS *ip = &(genctl->InpParms); PINChI2 **pINChI2 = genctl->pINChI; PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; int k, num_at, ret = 0; INChI *cur_INChI[TAUT_NUM]; INChI_Aux *cur_INChI_Aux[TAUT_NUM]; long lElapsedTime; /* PINChI2 *pINChI = pINChI2[iINChI]; PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; */ PINChI2 *pINChI = NULL; PINChI_Aux2 *pINChI_Aux = NULL; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ INP_ATOM_DATA *inp_cur_data = NULL; COMPONENT_TREAT_INFO *cti = NULL; inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); cti = &(genctl->cti[iINChI][i]); inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); pINChI = pINChI2[iINChI]; pINChI_Aux = pINChI_Aux2[iINChI]; InchiTimeGet( &ulTStart ); for ( k = 0; k < TAUT_NUM; k ++ ) { cur_INChI[k] = pINChI[i][k]; cur_INChI_Aux[k] = pINChI_Aux[i][k]; } lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= lElapsedTime; } sd->ulStructTime += lElapsedTime; /****************************************************** * * Get one component canonical numberings, etc. * ******************************************************/ /* * Create_INChI() return value: * num_at <= 0: error code * num_at > 0: number of atoms (excluding terminal hydrogen atoms) * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric */ InchiTimeGet( &ulTStart ); if ( ip->msec_MaxTime ) { ulTEnd = ulTStart; pulTEnd = &ulTEnd; if ( ip->msec_LeftTime > 0 ) { InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); } } num_at = Canonicalization_step(cur_INChI, cur_INChI_Aux, inp_norm_data, pulTEnd, NULL, sd->pStrErrStruct, cti); num_at = cti->num_atoms; SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ for ( k = 0; k < TAUT_NUM; k ++ ) { if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) { pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; } } /* Detect errors */ if ( num_at < 0 ) sd->nErrorCode = num_at; else if ( num_at == 0 ) sd->nErrorCode = -1; else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) /* non-tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) /* tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; /* detect and store stereo warnings */ if ( !sd->nErrorCode ) GetProcessingWarnings(cur_INChI, inp_norm_data, sd); lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= lElapsedTime; sd->ulStructTime += lElapsedTime; #ifndef TARGET_API_LIB /* Display the results */ if ( ip->bDisplay ) eat_keyboard_input(); #endif /* a) No matter what happened save the allocated INChI pointers */ /* save the INChI of the current component */ InchiTimeGet( &ulTStart ); for ( k = 0; k < TAUT_NUM; k ++ ) { pINChI[i][k] = cur_INChI[k]; pINChI_Aux[i][k] = cur_INChI_Aux[k]; cur_INChI[k] = NULL; cur_INChI_Aux[k] = NULL; } /* b) Count one component structure and/or INChI results only if there was no error */ /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ if ( !sd->nErrorCode ) { /* find where the current processed structure is located */ int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; if ( cur_is_non_taut + cur_is_taut ) { /* count tautomeric and non-tautomeric components of the structures */ int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; int j; sd->num_non_taut[iINChI] += cur_is_non_taut; sd->num_taut[iINChI] += cur_is_taut; for ( j = j1; j <= j2; j ++ ) { int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || pINChI[i][j]->nNumberOfIsotopicTGroups || pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); if ( j == TAUT_YES ) { bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); } inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; } } } if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) ret = _IS_FATAL; else if ( sd->nErrorCode ) ret = _IS_ERROR; lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) ip->msec_LeftTime -= lElapsedTime; sd->ulStructTime += lElapsedTime; return ret; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int Normalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], int num_inp_at, INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, COMPONENT_TREAT_INFO *z) { int i,ret=0; T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); BCN *pBCN = &(z->Bcn); /*^^^ */ z->fix_isofixedh = 0; z->fix_termhchrg = 0; /*^^^ */ #if( FIX_ISO_FIXEDH_BUG == 1 ) if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) z->fix_isofixedh = 1; #endif #if( FIX_TERM_H_CHRG_BUG == 1 ) if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) z->fix_termhchrg = 1; #endif z->bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); z->bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); z->bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); z->out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ /* Init: internal structs */ memset( z->s, 0, sizeof(z->s) ); if ( pBCN ) memset( pBCN, 0, sizeof( pBCN[0] ) ); memset( t_group_info, 0, sizeof(*t_group_info) ); memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); /* Allocate: at[] */ for ( i = 0; i < TAUT_NUM; i ++ ) { if ( out_norm_data[i]->at ) { z->at[i] = (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*(z->at[0])) ); if ( !z->at[i] ) ret = -1; } else z->at[i] = NULL; } if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) { ret = -1; goto exit_function; } /* the first struct to process: tautomeric if exists else non-tautomeric */ z->out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; /* copy the input structure to be normalized to the buffer for the normalization data */ memcpy( z->out_at, inp_at, num_inp_at*sizeof(z->out_at[0]) ); /* tautomeric groups setting */ t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ t_group_info->bTautFlags = *pbTautFlags; t_group_info->bTautFlagsDone = *pbTautFlagsDone; /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ if ( TG_FLAG_H_ALREADY_REMOVED & z->bTautFlags ) { INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; if ( out_norm_data1 ) { z->num_at_tg = z->num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; z->num_deleted_H = out_norm_data1->num_removed_H; t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; } else { ret = -1; goto exit_function; } } else { z->num_at_tg = z->num_atoms = remove_terminal_HDT( num_inp_at, z->out_at, z->fix_termhchrg); z->num_deleted_H = num_inp_at - z->num_atoms; t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; add_DT_to_num_H( z->num_atoms, z->out_at ); } /* fix_odd_things( z->num_atoms, z->out_at );*/ #if( FIND_RING_SYSTEMS == 1 ) MarkRingSystemsInp( z->out_at, z->num_atoms, 0 ); #endif /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ if ( z->out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) memcpy( out_norm_data[TAUT_YES]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); if ( z->out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) memcpy( out_norm_data[TAUT_NON]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); /******************************************************************************* * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal * set stereo parities in taut_at[], non_taut_at[] * obtain max. lenghts of the name stereo parts * Ignore absence/presence of isotopic stereo for now * mark isotopic atoms *******************************************************************************/ if ( out_norm_data[TAUT_YES]->at && z->at[TAUT_YES] ) { /* final normalization of possibly tautomeric structure */ ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_YES]->at, out_norm_data[TAUT_YES]->at_fixed_bonds, z->num_atoms, t_group_info, NULL, NULL ); if ( ret < 0 ) goto exit_function;/* out of RAM or other normalization problem */ z->num_taut_at = ret; /* number of atoms without removed H? */ z->num_deleted_H_taut = t_group_info->tni.nNumRemovedExplicitH; out_norm_data[TAUT_YES]->num_at = z->num_atoms + z->num_deleted_H_taut; /* protons might have been removed */ out_norm_data[TAUT_YES]->num_removed_H = z->num_deleted_H_taut; out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; } /* mark deleted isolated tautomeric H(+) */ if ( z->num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) { out_norm_data[TAUT_YES]->bDeleted = 1; FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); } else if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && out_norm_data[TAUT_YES]->at_fixed_bonds) { out_norm_data[TAUT_YES]->bTautPreprocessed = 1; } out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; /* create internal sp_ATOM at[] out of out_norm_data[]->at */ inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, z->at[TAUT_YES] ); /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ ret = set_stereo_parity( out_norm_data[TAUT_YES]->at, z->at[TAUT_YES], z->num_taut_at, z->num_deleted_H_taut, &(z->s[TAUT_YES].nMaxNumStereoAtoms), &(z->s[TAUT_YES].nMaxNumStereoBonds), z->nUserMode, z->bPointedEdgeStereo, z->vABParityUnknown ); if ( RETURNED_ERROR(ret) ) goto exit_function; /* stereo bond error */ z->s[TAUT_YES].bMayHaveStereo = (z->s[TAUT_YES].nMaxNumStereoAtoms || z->s[TAUT_YES].nMaxNumStereoBonds); /* * mark isotopic atoms and atoms that have non-tautomeric * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) */ z->s[TAUT_YES].num_isotopic_atoms = set_atom_iso_sort_keys( z->num_taut_at, z->at[TAUT_YES], t_group_info, &(z->s[TAUT_YES].bHasIsotopicTautGroups) ); /************************************************************************** * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) * structure for canonicalizaton: ************************************************************************** * remove t-groups that have no H, * remove charges from t-groups if requested * renumber t-groups and find final t_group_info->num_t_groups * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] * calculate length of the t-group part of the connection table **************************************************************************/ z->s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( z->at[TAUT_YES], z->num_taut_at, t_group_info ); if ( RETURNED_ERROR(z->s[TAUT_YES].nLenLinearCTTautomer) ) { /* added error treatment 9-11-2003 */ ret = z->s[TAUT_YES].nLenLinearCTTautomer; goto exit_function; /* error has happened; no breakpoint here z->s[TAUT_YES].nLenLinearCTTautomer = 0; */ } else if ( z->s[TAUT_YES].nLenLinearCTTautomer > 0 ) { z->num_at_tg = z->num_taut_at+t_group_info->num_t_groups; /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); /* mark isotopic tautomer groups: calculate t_group->iWeight */ z->s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); if ( z->s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) { /* ??? -error cannot happen- error has happened; no breakpoint here */ z->s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; } out_norm_data[TAUT_YES]->bTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; } /* new variable: z->s[TAUT_YES].nLenCT introduced 7-22-2002 */ GetCanonLengths( z->num_taut_at, z->at[TAUT_YES], &(z->s[TAUT_YES]), t_group_info ); } /* end of: final normalization of possibly tautomeric structure */ if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && z->at[TAUT_NON] && !z->s[TAUT_YES].nLenLinearCTTautomer ) { /* the structure is non-tautomeric: use tautomeric treatment results only for it */ inchi_free( z->at[TAUT_NON] ); z->at[TAUT_NON] = NULL; } else if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && !z->at[TAUT_NON] && z->at[TAUT_YES] && !z->s[TAUT_YES].nLenLinearCTTautomer ) { /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ out_norm_data[TAUT_YES]->bTautomeric = 0; } else if ( out_norm_data[TAUT_NON]->at && z->at[TAUT_NON] ) { /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ ret = mark_alt_bonds_and_taut_groups (out_norm_data[TAUT_NON]->at, NULL, z->num_atoms, NULL, &(z->bTautFlags), &(z->bTautFlagsDone) ); if ( ret < 0 ) goto exit_function; /* out of RAM or other normalization problem */ out_norm_data[TAUT_NON]->num_at = z->num_atoms + z->num_deleted_H; out_norm_data[TAUT_NON]->num_removed_H = z->num_deleted_H; out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; out_norm_data[TAUT_NON]->bNormalizationFlags = 0; /* create internal sp_ATOM at[] out of out_norm_data[]->at */ inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, z->at[TAUT_NON] ); /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ ret = set_stereo_parity( out_norm_data[TAUT_NON]->at, z->at[TAUT_NON], z->num_atoms, z->num_deleted_H, &(z->s[TAUT_NON].nMaxNumStereoAtoms), &(z->s[TAUT_NON].nMaxNumStereoBonds), z->nUserMode, z->bPointedEdgeStereo, z->vABParityUnknown ); if ( RETURNED_ERROR( ret ) ) goto exit_function; /* stereo bond error */ z->s[TAUT_NON].bMayHaveStereo = (z->s[TAUT_NON].nMaxNumStereoAtoms || z->s[TAUT_NON].nMaxNumStereoBonds); /* * mark isotopic atoms and atoms that have non-tautomeric * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) */ z->s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( z->num_atoms, z->at[TAUT_NON], NULL, NULL ); GetCanonLengths( z->num_atoms, z->at[TAUT_NON], &(z->s[TAUT_NON]), NULL); out_norm_data[TAUT_NON]->bTautomeric = 0; } /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ /**********************************************************/ /* common */ z->bMayHaveStereo = z->s[TAUT_YES].bMayHaveStereo || z->s[TAUT_NON].bMayHaveStereo; z->bHasIsotopicAtoms = z->s[TAUT_NON].num_isotopic_atoms > 0 || z->s[TAUT_NON].bHasIsotopicTautGroups > 0 || z->s[TAUT_YES].num_isotopic_atoms > 0 || z->s[TAUT_YES].bHasIsotopicTautGroups > 0 ; /*^^^ */ if (z->fix_isofixedh) /* 2008-03-21 DT */ z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || z->s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; /*^^^ */ z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || z->s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); /* Set mode */ /* default mode */ if ( !(z->nUserMode & REQ_MODE_DEFAULT) ) { z->nUserMode |= REQ_MODE_DEFAULT; } /* adjust the mode to the reality */ if ( ( z->nUserMode & REQ_MODE_ISO ) && !z->bHasIsotopicAtoms ) { z->nUserMode ^= REQ_MODE_ISO; z->nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ } if ( (z->nUserMode & REQ_MODE_STEREO) && ( z->nUserMode & REQ_MODE_ISO ) ) { z->nUserMode |= REQ_MODE_ISO_STEREO; } if ( (z->nUserMode & REQ_MODE_STEREO) && !( z->nUserMode & REQ_MODE_NON_ISO ) ) { z->nUserMode ^= REQ_MODE_STEREO; } if ( !z->bMayHaveStereo ) { if ( z->nUserMode & REQ_MODE_STEREO ) z->nUserMode ^= REQ_MODE_STEREO; if ( z->nUserMode & REQ_MODE_ISO_STEREO ) z->nUserMode ^= REQ_MODE_ISO_STEREO; } if ( (z->nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !z->at[TAUT_NON]) ) { z->nUserMode ^= REQ_MODE_BASIC; } if ( (z->nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !z->at[TAUT_YES]) ) { z->nUserMode ^= REQ_MODE_TAUT; } /* Set n1, n2 according to the mode */ switch ((int)z->nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) { case REQ_MODE_BASIC: z->n1 = TAUT_NON; z->n2 = TAUT_NON; break; case REQ_MODE_TAUT: z->n1 = TAUT_YES; z->n2 = TAUT_YES; break; case (REQ_MODE_BASIC | REQ_MODE_TAUT): z->n1 = TAUT_NON; z->n2 = TAUT_YES; break; default: /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ ret = -3; goto exit_function; } if ( ret == 0 ) ret = z->num_atoms; /* treat the results later */ exit_function: return ret; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ int Canonicalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, INP_ATOM_DATA *out_norm_data[2], struct tagInchiTime *ulMaxTime, T_GROUP_INFO *ti_out, char *pStrErrStruct, COMPONENT_TREAT_INFO *z) { int i,ret=0, ret2=0; T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); CANON_STAT CS, CS2; CANON_STAT *pCS = &CS; CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ BCN *pBCN = &(z->Bcn); INChI *pINChI=NULL; /* added initialization 2006-03 */ INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ /************************************************************ * * * Obtain all non-stereo canonical numberings * * * ************************************************************/ if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) { /* added for special non-isotopic test mode 2004-10-04 */ if ( t_group_info ) { t_group_info->bIgnoreIsotopic = 1; if ( t_group_info->nIsotopicEndpointAtomNumber ) { t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); } memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); } for ( i = 0; i < TAUT_NUM; i ++ ) { z->s[i].bHasIsotopicTautGroups = 0; z->s[i].bIgnoreIsotopic = 1; z->s[i].nLenIsotopic = 0; z->s[i].nLenIsotopicEndpoints = 0; z->s[i].nLenLinearCTIsotopicTautomer = 0; z->s[i].num_isotopic_atoms = 0; } z->bHasIsotopicAtoms = 0; } ret = GetBaseCanonRanking( z->num_atoms, z->num_at_tg, z->at, t_group_info, z->s, pBCN, ulMaxTime, z->fix_isofixedh ); if ( ret < 0 ) goto exit_function; /* program error */ /* added for special non-isotopic test mode 2004-10-04 */ if ( !pBCN->ftcn[z->n1].PartitionCt.Rank ) z->n1 = ALT_TAUT(z->n1); if ( !pBCN->ftcn[z->n2].PartitionCt.Rank ) z->n2 = ALT_TAUT(z->n2); if ( z->n1 > z->n2 ) { ret = CT_TAUCOUNT_ERR; goto exit_function; /* program error */ } /************************************************************ * * * Obtain stereo canonical numberings * * * ************************************************************/ for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) { memset( pCS, 0, sizeof(*pCS) ); switch (i) { case TAUT_NON: /* non-tautomeric */ z->nMode = 0; z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; z->nMode |= (z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; /* 2010-01-12 */ z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; } if ( ret= AllocateCS( pCS, z->num_atoms, z->num_atoms, z->s[TAUT_NON].nLenCT, z->s[TAUT_NON].nLenCTAtOnly, z->s[TAUT_NON].nLenLinearCTStereoDble, z->s[TAUT_NON].nMaxNumStereoBonds, z->s[TAUT_NON].nLenLinearCTStereoCarb, z->s[TAUT_NON].nMaxNumStereoAtoms, 0, 0, z->s[TAUT_NON].nLenIsotopic, z->nMode, pBCN ) ) { goto exit_function; } *pCS2 = *pCS; break; case TAUT_YES: /* tautomeric */ z->nMode = 0; z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; z->nMode |= (z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; /* 2010-01-12 */ z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; } if ( ret= AllocateCS( pCS, z->num_atoms, z->num_at_tg, z->s[TAUT_YES].nLenCT, z->s[TAUT_YES].nLenCTAtOnly, z->s[TAUT_YES].nLenLinearCTStereoDble, z->s[TAUT_YES].nMaxNumStereoBonds, z->s[TAUT_YES].nLenLinearCTStereoCarb, z->s[TAUT_YES].nMaxNumStereoAtoms, z->s[TAUT_YES].nLenLinearCTTautomer, z->s[TAUT_YES].nLenLinearCTIsotopicTautomer, z->s[TAUT_YES].nLenIsotopic, z->nMode, pBCN ) ) { goto exit_function; } *pCS2 = *pCS; break; } /* switch () */ /* settings */ pCS->lNumDecreasedCT = -1; pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ pCS->bIgnoreIsotopic = !((z->s[TAUT_NON].num_isotopic_atoms || z->s[TAUT_YES].num_isotopic_atoms || z->s[TAUT_YES].bHasIsotopicTautGroups) || (z->nUserMode & REQ_MODE_NON_ISO) || !(z->nUserMode & REQ_MODE_ISO)); if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ if ( i == TAUT_YES ) { /* tautomeric */ pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ pCS->t_group_info->bIgnoreIsotopic = !(z->s[TAUT_YES].bHasIsotopicTautGroups || (z->nUserMode & REQ_MODE_NON_ISO) || !(z->nUserMode & REQ_MODE_ISO)); if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ } pCS->ulTimeOutTime = pBCN->ulTimeOutTime; /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== * * Mode Bits Description * '0' c 0 Only one connection table canonicalization * '1' C 1 Recalculate CT using fixed nSymmRank * '2' i 1|2 Isotopic canonicalization (internal) * '3' I 1|2|4 Isotopic canonicalization (output) * '4' s 1|8 Stereo canonicalization * '5' S 1|2|4|16 Stereo isotopic canonicalization * '6' A 1|2|4|8|16 Output All */ /*************************************** The last canonicalization step ***************************************/ if ( pBCN ) { /* USE_CANON2 == 1 */ pCS->NeighList = NULL; pCS->pBCN = pBCN; ret = Canon_INChI( z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS, z->nMode, i); } else { /* old way */ pCS->NeighList = CreateNeighList( z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); pCS->pBCN = NULL; ret = Canon_INChI( z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS, z->nMode, i); } pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ pINChI_Aux = ppINChI_Aux[i]; if ( ret <= 0 ) { /***************************************/ /* failure in Canon_INChI() */ /***************************************/ pINChI->nErrorCode = ret; pINChI_Aux->nErrorCode = ret; } else { /***************************************/ /* success Canon_INChI() */ /* save canonicalization results in */ /* pINChI and pINChI_Aux */ /***************************************/ pINChI->nErrorCode = 0; pINChI_Aux->nErrorCode = 0; pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; pINChI_Aux->nCanonFlags = pCS->nCanonFlags; pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; /* may return an error or a warning */ ret = FillOutINChIReducedWarn( pINChI, pINChI_Aux, z->num_atoms, i?z->num_at_tg:z->num_atoms, i?z->num_deleted_H_taut:z->num_deleted_H, z->at[i], out_norm_data[i]->at, pCS, i, z->nUserMode, pStrErrStruct ); if ( RETURNED_ERROR( ret ) ) { /* failure in FillOutINChI() */ pINChI->nErrorCode = ret; pINChI_Aux->nErrorCode = ret; } else { /****************************/ /* success in FillOutINChI() */ /****************************/ /* mark non-tautomeric representation as having another, tautomeric representation */ if ( pINChI_Aux && z->s[TAUT_YES].nLenLinearCTTautomer ) pINChI_Aux->bIsTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; ret2 = CheckCanonNumberingCorrectness(z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS, i, pStrErrStruct ); if (ret2) { pINChI->nErrorCode = ret2; pINChI_Aux->nErrorCode = ret2; ret = ret2; } } /* success in FillOutINChI */ } /* success Canon_INChI */ FreeNeighList( pCS->NeighList ); DeAllocateCS( pCS2 ); pINChI = NULL; /* avoid dangling pointers */ pINChI_Aux = NULL; /* avoid dangling pointers */ } /* for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) */ if ( ret == 0 ) ret = z->num_atoms; exit_function: DeAllocBCN( pBCN ); if ( z->at[TAUT_YES] ) { inchi_free( z->at[TAUT_YES] ); z->at[TAUT_YES] = NULL; } if ( z->at[TAUT_NON] ) { inchi_free( z->at[TAUT_NON] ); z->at[TAUT_NON] = NULL; } if ( ti_out ) *ti_out = *t_group_info; else { free_t_group_info( t_group_info ); t_group_info = NULL; } free_t_group_info( t_group_info_orig ); return ret; } /****************************************************************************/ int CreateCompositeNormAtom(COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, int num_components) { int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; int ret = 0, indicator = 1; inp_ATOM *at, *at_from; memset( num_comp, 0, sizeof(num_comp) ); memset( num_taut, 0, sizeof(num_taut) ); memset( num_del, 0, sizeof(num_taut) ); /* count taut and non-taut components */ for ( j = 0; j < TAUT_NUM; j ++ ) { num_comp[j] = num_taut[j] = 0; for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bExists ) { num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); num_comp[j] ++; num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); } } } /* count intermediate taut structure components */ if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { /* num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; */ for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bExists && (all_inp_norm_data[i][j].bDeleted || all_inp_norm_data[i][j].bTautomeric && all_inp_norm_data[i][j].at_fixed_bonds && all_inp_norm_data[i][j].bTautPreprocessed) ) { num_comp[TAUT_INI] ++; } } } /* count atoms and allocate composite atom data */ for ( jj = 0; jj <= TAUT_INI; jj ++ ) { num_at[jj] = num_inp_at[jj] = 0; j = inchi_min (jj, TAUT_YES); if ( num_comp[jj] ) { for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bDeleted ) continue; /* find k = the normaized structure index */ if ( jj == TAUT_INI ) { if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && !all_inp_norm_data[i][j].bDeleted ) { k = ALT_TAUT(j); } else if ( all_inp_norm_data[i][j].bExists ) { k = j; } else { continue; } } else { if ( all_inp_norm_data[i][j].bExists ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { k = ALT_TAUT(j); } else { continue; } } num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; } if ( num_inp_at[jj] ) { if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) goto exit_error; composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; } } } /* fill out composite atom */ for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { j = inchi_min (jj, TAUT_YES); if ( num_comp[jj] ) { tot_num_at = 0; tot_num_H = 0; for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bDeleted ) { composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; } continue; } nNumRemovedProtons = 0; k = TAUT_NUM; /* find k = the normaized structure index */ if ( jj == TAUT_INI ) { if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { k = ALT_TAUT(j); } else if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { k = j; } else { continue; } } else { if ( all_inp_norm_data[i][j].bExists ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { k = ALT_TAUT(j); } else { continue; } } /* copy main atoms */ cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ if ( (tot_num_at + cur_num_at) > num_at[jj] || (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { goto exit_error; /* miscount */ } at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ /* shift neighbors of main atoms */ for ( n = 0; n < cur_num_at; n ++, at ++ ) { for ( m = 0; m < at->valence; m ++ ) { at->neighbor[m] += tot_num_at; } } /* copy explicit H */ if ( cur_num_H ) { at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ memcpy( at, at_from+cur_num_at, sizeof(composite_norm_data[0].at[0]) * cur_num_H ); /* shift neighbors of explicit H atoms */ for ( n = 0; n < cur_num_H; n ++, at ++ ) { for ( m = 0; m < at->valence; m ++ ) { at->neighbor[m] += tot_num_at; } } } /* composite counts */ composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; } /* composite_norm_data[j].num_at += cur_num_at + cur_num_H; composite_norm_data[j].num_removed_H += cur_num_H; */ /* total count */ tot_num_at += cur_num_at; tot_num_H += cur_num_H; /* offset for the next component */ if ( composite_norm_data[jj].nOffsetAtAndH ) { composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; } } if ( tot_num_at != num_at[jj] || num_at[jj] + tot_num_H != num_inp_at[jj] ) { goto exit_error; /* miscount */ } composite_norm_data[jj].bExists = (tot_num_at>0); ret |= indicator; } } return ret; exit_error: return ret; } int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ) { FreeCompAtomData( inp_at_data ); if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && (num_components <= 1 || bIntermediateTaut || (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1))))) { inp_at_data->num_at = num_atoms; inp_at_data->num_components = (num_components>1)? num_components : 0; return 1; } FreeCompAtomData( inp_at_data ); return 0; } /**********************************************************************************************/ int FillOutINChIReducedWarn( INChI *pINChI, INChI_Aux *pINChI_Aux, int num_atoms, int num_at_tg, int num_removed_H, sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, INCHI_MODE nUserMode, char *pStrErrStruct ) { int i, j, m, n, g, len, ii, ret=0; AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; T_GROUP_INFO *t_group_info = pCS->t_group_info; T_GROUP *t_group; int nErrorCode = 0; AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; AT_RANK nMinOrd; INChI_Stereo *Stereo; int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; INCHI_MODE nStereoUnmarkMode; /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ /* Check for warnings */ if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { nErrorCode |= WARN_FAILED_STEREO; } if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { nErrorCode |= WARN_FAILED_ISOTOPIC; } if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; } pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ if ( !pCanonRankAtoms || !pSortOrd ) { nErrorCode = 0; ret = CT_OUT_OF_RAM; /* */ pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; goto exit_function; } /* total charge */ for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { n += at[i].charge; } pINChI->nTotalCharge = n; /* number of atoms */ pINChI->nNumberOfAtoms = num_atoms; pINChI_Aux->nNumberOfAtoms = num_atoms; /* removed protons and detachable isotopic H */ if ( bTautomeric && t_group_info ) { pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; } if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; } /*^^^ if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { AddMOLfileError(pStrErrStruct, "Proton(s) added/removed"); } if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { AddMOLfileError(pStrErrStruct, "Charges neutralized"); } ^^^*/ } /* abs or rel stereo may establish one of two canonical numberings */ if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && pCS->nLenCanonOrdStereo > 0 && (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv ) { pCanonRank = pCanonRankAtoms; pCanonOrd = pCS->nCanonOrdStereo; pCanonRankInv = pSortOrd; pCanonOrdInv = pCS->nCanonOrdStereoInv; Stereo = pINChI->Stereo; for ( i = 0; i < num_at_tg; i ++ ) { pCanonRankInv[pCanonOrdInv[i]] = pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } /********************************************************************/ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ /********************************************************************/ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ , pCS->LinearCTStereoCarbInv , pCS->LinearCTStereoDbleInv , pCanonOrdInv, pCanonRankInv ); if ( Stereo->t_parityInv && Stereo->nNumberInv ) { if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { pINChI->nFlags |= INCHI_FLAG_REL_STEREO; } if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; } if ( Stereo->nCompInv2Abs ) { if ( Stereo->nCompInv2Abs == -1 ) { /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ AT_NUMB *nNumberInv = Stereo->nNumberInv; S_CHAR *t_parityInv = Stereo->t_parityInv; Stereo->nNumberInv = Stereo->nNumber; Stereo->t_parityInv = Stereo->t_parity; Stereo->nNumber = nNumberInv; Stereo->t_parity = t_parityInv; /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ } } } for ( i = 0; i < num_atoms; i ++ ) { pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } if ( bUseNumberingInv ) { /* switch ptrs back to avoid confusion */ switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); /* save inverted stereo ranks & order because it represents the smallest (relative) */ memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); /* change pCS->nCanonOrdStereo[] to inverted: */ memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); } pCanonRankInv = NULL; pCanonOrdInv = NULL; pOrigNosInCanonOrd = NULL; } else { /*------------------------------ no stereo */ pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; pCanonRank = pCanonRankAtoms; pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_atoms; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } for ( ; i < num_at_tg; i ++ ) { pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } } } /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ if ( pINChI_Aux->OrigInfo ) { /* charges, radicals, valences */ for ( i = 0; i < num_atoms; i ++ ) { ii = pCanonOrd[i]; if ( norm_at[ii].valence || norm_at[ii].num_H ) { pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : norm_at[ii].radical? 3 : 0 ; pINChI_Aux->OrigInfo[i].cUnusualValence = get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); } else { /* charge of a single atom component is in the INChI; valence = 0 is standard */ pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : norm_at[ii].radical? 3 : 0 ; } } } /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ pSymmRank = pCS->nSymmRank; if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { for ( i = 0; i < num_atoms; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ pSortOrd[i] = i; } for ( ; i < num_at_tg; i ++ ) { pSortOrd[i] = MAX_ATOMS; /* for debugging only */ } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent atoms: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ } /* at this point j == i */ } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ } nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ } } } else { nErrorCode |= ERR_NO_CANON_RESULTS; ret = -1; /* program error; no breakpoint here */ goto exit_function; } /* atomic numbers from the Periodic Table */ for ( i = 0; i < num_atoms; i ++ ) { pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; } /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { nErrorCode |= ERR_NO_CANON_RESULTS; ret = -2; goto exit_function; } memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; /* tautomeric group(s) canonical representation */ len = 0; if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) { /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; /* number of tautomeric groups */ pINChI->nTautomer[len ++] = (AT_NUMB)n; /* store each tautomeric group, one by one */ for ( i = 0; i < n; i ++ ) { g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; /* Num(H), Num(-) */ for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) pINChI->nTautomer[len ++] = t_group->num[j]; for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) pINChI->nTautomer[len ++] = 0; /* should not happen */ /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ for ( j = (int)t_group->nFirstEndpointAtNoPos, m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ } } pINChI->lenTautomer = len; pINChI_Aux->nNumberOfTGroups = n; } else { pINChI->lenTautomer = 0; pINChI_Aux->nNumberOfTGroups = 0; if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { /* only protons (re)moved or added */ pINChI->lenTautomer = 1; pINChI->nTautomer[0] = 0; } } /* number of H (excluding tautomeric) */ if ( pCS->nNum_H ) { for ( i = 0; i < num_atoms; i ++ ) { pINChI->nNum_H[i] = pCS->nNum_H[i]; } } /* number of fixed H (tautomeric H in non-tautomeric representation) */ if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { for ( i = 0; i < num_atoms; i ++ ) { pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; } } /*********************************************************** * tautomeric group(s) numbering and symmetry; * should not depend on switching to rel. stereo numbering */ if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; pSymmRank = pCS->nSymmRankTaut; if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { for ( i = 0; i < n; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; /* make is start from 1, not from zero */ if ( j - i > 1 ) { /* found a sequence of more than one equivalent t-groups: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; } nMinOrd = pSortOrd[j]; /* at the end j == n */ } } } } /* Allocate and fill Hill formula */ if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { nErrorCode = 0; ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; goto exit_function; } if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); } } /*************************/ /* mark ambiguous stereo */ /*************************/ MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); /************************************************************************ * * isotopic part */ /* abs or rel stereo may establish one of two canonical numberings */ if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && pCS->nLenCanonOrdIsotopicStereo > 0 && (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv ) { /* found isotopic stereo */ pCanonRank = pCanonRankAtoms; pCanonOrd = pCS->nCanonOrdIsotopicStereo; pCanonRankInv = pSortOrd; pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; Stereo = pINChI->StereoIsotopic; for ( i = 0; i < num_at_tg; i ++ ) { pCanonRankInv[pCanonOrdInv[i]] = pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } /********************************************************************/ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ /********************************************************************/ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble , pCanonOrd, pCanonRank, at, 1 /* isotopic */ , pCS->LinearCTIsotopicStereoCarbInv , pCS->LinearCTIsotopicStereoDbleInv , pCanonOrdInv, pCanonRankInv ); if ( Stereo->t_parityInv && Stereo->nNumberInv ) { if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { pINChI->nFlags |= INCHI_FLAG_REL_STEREO; } if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; } if ( Stereo->nCompInv2Abs ) { if ( Stereo->nCompInv2Abs == -1 ) { /* switch pointers so that the stereo becomes the smallest (relative) */ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ AT_NUMB *nNumberInv = Stereo->nNumberInv; S_CHAR *t_parityInv = Stereo->t_parityInv; Stereo->nNumberInv = Stereo->nNumber; Stereo->t_parityInv = Stereo->t_parity; Stereo->nNumber = nNumberInv; Stereo->t_parity = t_parityInv; switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); bUseIsotopicNumberingInv = 1; } } } for ( i = 0; i < num_atoms; i ++ ) { pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } if ( bUseIsotopicNumberingInv ) { switch_ptrs( &pCanonRank, &pCanonRankInv ); switch_ptrs( &pCanonOrd, &pCanonOrdInv ); memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); } pCanonRankInv = NULL; pCanonOrdInv = NULL; pOrigNosInCanonOrd = NULL; } else { /* no isotopic stereo */ pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; pCanonRank = pCanonRankAtoms; pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; if ( pCanonOrd && pCanonRank ) { for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; } for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); } } } /*pCanonOrdIso = pCanonOrd;*/ pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; pSymmRank = pCS->nSymmRankIsotopic; if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { for ( i = 0; i < num_atoms; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; pSortOrd[i] = i; } for ( ; i < num_at_tg; i ++ ) { pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent atoms: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ } nMinOrd = pSortOrd[j]; } } } else { goto exit_function; /* no isotopic info available */ } /* isotopic atoms */ n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; for ( i = 0; i < n; i ++ ) { pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; } /* isotopic tautomeric groups */ n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; for ( i = 0; i < n; i ++ ) { pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; } /* atoms that may exchange isotopic H-atoms */ if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { for ( i = 0, j = 1; i < num_atoms; i ++ ) { if ( pCS->nExchgIsoH[i] ) { pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ } } pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ } if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); } } /* mark ambiguous stereo */ MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); /*********************************************************** * isotopic tautomeric group(s) numbering and symmetry; * should not depend on switching to rel. stereo numbering */ if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && t_group_info && t_group_info->num_t_groups > 0 ) { n = t_group_info->num_t_groups; pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : pCS->nLenCanonOrdIsotopicTaut > 0? (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; pSymmRank = pCS->nSymmRankIsotopicTaut; if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { for ( i = 0; i < n; i ++ ) { pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; pSortOrd[i] = i; } pn_RankForSort = pConstitEquNumb; qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { nMinOrd ++; if ( j - i > 1 ) { /* found a sequence of equivalent t-groups: i..j-1 */ while ( i < j ) { pConstitEquNumb[pSortOrd[i++]] = nMinOrd; } } else { pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ } nMinOrd = pSortOrd[j]; /* at the end j = n */ } } } } exit_function: if ( pCanonRankAtoms ) inchi_free( pCanonRankAtoms ); if ( pSortOrd ) inchi_free( pSortOrd ); pINChI->nErrorCode |= nErrorCode; pINChI_Aux->nErrorCode |= nErrorCode; return ret; } /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl) { /*^^^ TODO: make a full copy (with allocs) of atom arrays */ size_t t1; int k; for ( k = 0; k < INCHI_NUM; k++) { if (NULL!=genctl->InpNormAtData[k]) { t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); memcpy(gendata->NormAtomsNontaut[k], genctl->InpNormAtData[k], t1); } if (NULL!=genctl->InpNormTautData[k]) { t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); memcpy(gendata->NormAtomsTaut[k], genctl->InpNormTautData[k], t1); } } } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll_main.c000066400000000000000000000034431271037650300246520ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* inchi_dll_main.c : Defines the entry point for the DLL application. */ #if defined(_WIN32) && defined(_USRDLL) && defined(_DEBUG) && !(defined(__STDC__) && __STDC__ == 1) #include "inchi_dll_main.h" int INCHI_DLLMAIN_TYPE DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } #else int dummy_inchi_dll_main=0; /* avoid empty module to keep C compiler happy */ #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchi_dll_main.h000066400000000000000000000034061271037650300246560ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHI_DLL_MAIN_H__ #define __INCHI_DLL_MAIN_H__ #if _MSC_VER > 1000 #pragma once #endif /* _MSC_VER > 1000 */ #if defined(_WIN32) && defined(_MSC_VER) && defined(_USRDLL) /*#define WIN32_LEAN_AND_MEAN */ /* Exclude rarely-used stuff from Windows headers */ #include #define INCHI_DLLMAIN_TYPE APIENTRY #else /* not a Win32 DLL under MS VC++ */ #define INCHI_DLLMAIN_TYPE #endif #endif /* __INCHI_DLL_MAIN_H__ */Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inchicmp.h000066400000000000000000000166701271037650300235260ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __INCHICMP_H__ #define __INCHICMP_H__ typedef enum tagInchiCompareDiffBits { INCHIDIFF_ZERO = 0x00000000, INCHIDIFF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ INCHIDIFF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ INCHIDIFF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ INCHIDIFF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ INCHIDIFF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ INCHIDIFF_CON_TBL = 0x00000001, /* severe: different connection tables */ INCHIDIFF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ INCHIDIFF_MORE_FH = 0x00000004, /* extra fixed H */ INCHIDIFF_LESS_FH = 0x00000004, /* missing fixed H */ INCHIDIFF_MORE_H = 0x00000008, /* formulas differ in number of H */ INCHIDIFF_LESS_H = 0x00000008, /* formulas differ in number of H */ INCHIDIFF_NO_TAUT = 0x00000010, /* restored structure has no taut groups while the original InChI has some */ INCHIDIFF_WRONG_TAUT = 0x00000020, /* restored has tautomerism while the original does not have it */ INCHIDIFF_SINGLE_TG = 0x00000040, /* restored has 1 taut. group while the original InChI has multiple tg */ INCHIDIFF_MULTIPLE_TG = 0x00000080, /* restored has multiple tg while the original InChI has only one tg */ INCHIDIFF_EXTRA_TG_ENDP = 0x00000100, /* extra tautomeric endpoint(s) in restored structure */ INCHIDIFF_MISS_TG_ENDP = 0x00000100, /* one or more tg endpoint is not in the restored structure */ INCHIDIFF_DIFF_TG_ENDP = 0x00000100, /* lists of tg endpoints are different */ INCHIDIFF_NUM_TG = 0x00000200, /* different number of tautomeric groups */ INCHIDIFF_TG = 0x00000200, /* different tautomeric groups */ INCHIDIFF_NUM_ISO_AT = 0x00000400, /* ?severe: restored struct. has different number of isotopic atoms */ INCHIDIFF_ISO_AT = 0x00000400, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ INCHIDIFF_REM_ISO_H = 0x00000800, /* isotopic H removed */ INCHIDIFF_MOB_ISO_H = 0x00001000, /* different number of mobile exchangeable isotopic H */ INCHIDIFF_CHARGE = 0x00002000, /* restored structure has different charge */ INCHIDIFF_REM_PROT = 0x00004000, /* proton(s) removed/added from the restored structure */ INCHIDIFF_MOBH_PROTONS = 0x00008000, /* different proton balance */ INCHIDIFF_SC_INV = 0x00010000, /* restores structure has different inversion stereocenter mark */ INCHIDIFF_SC_PARITY = 0x00020000, /* restored structure has stereoatoms or allenes with different parity */ INCHIDIFF_SC_EXTRA_UNDF = 0x00040000, /* restored structure has extra undefined stereocenter(s) */ INCHIDIFF_SC_EXTRA = 0x00080000, /* restored structure has extra stereocenter(s) */ INCHIDIFF_SC_MISS_UNDF = 0x00100000, /* restored structure has not some undefined stereocenter(s) */ INCHIDIFF_SC_MISS = 0x00200000, /* restored structure has not some stereocenters that are not undefined */ INCHIDIFF_SB_PARITY = 0x00400000, /* restored structure has stereobonds or cumulenes with different parity */ INCHIDIFF_SB_EXTRA_UNDF = 0x00800000, /* restored structure has extra undefined stereobond(s) */ INCHIDIFF_SB_EXTRA = 0x01000000, /* restored structure has extra stereobond(s) */ INCHIDIFF_SB_MISS_UNDF = 0x02000000, /* restored structure has not some undefined stereocenters */ INCHIDIFF_SB_MISS = 0x04000000, /* restored structure has not some stereobonds that are not undefined */ INCHIDIFF_COMP_HLAYER = 0x08000000, /* Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ INCHIDIFF_COMP_NUMBER = 0x10000000, /* wrong number of components */ INCHIDIFF_STR2INCHI_ERR = 0x20000000 /* Restored structure to InChI conversion error */ /* reserved 0x40000000 0x80000000 */ } INCHIDIFF; typedef enum tagtagCompareInchiMsgGroupID { IDGRP_ZERO = 0, IDGRP_ERR = 1, IDGRP_H = 2, IDGRP_MOB_GRP = 3, IDGRP_ISO_AT = 4, IDGRP_CHARGE = 5, IDGRP_PROTONS = 6, IDGRP_ISO_H = 7, IDGRP_SC = 8, IDGRP_SB = 9, IDGRP_HLAYER =10, IDGRP_COMP =11, IDGRP_CONV_ERR =12 } CMP_INCHI_MSG_GROUP_ID; typedef struct tagCompareInchiMsg { INCHIDIFF nBit; CMP_INCHI_MSG_GROUP_ID nGroupID; const char *szMsg; } CMP_INCHI_MSG; typedef struct tagCompareInchiMsgGroup { CMP_INCHI_MSG_GROUP_ID nGroupID; const char *szGroupName; } CMP_INCHI_MSG_GROUP; #define INCHIDIFF_SB (INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF | INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) #define INCHIDIFF_SC (INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS) #define INCHIDIFF_CONSTIT (INCHIDIFF_POSITION_H | INCHIDIFF_MORE_FH | INCHIDIFF_LESS_FH | INCHIDIFF_MORE_H | INCHIDIFF_LESS_H |\ INCHIDIFF_NO_TAUT | INCHIDIFF_WRONG_TAUT | INCHIDIFF_SINGLE_TG | INCHIDIFF_MULTIPLE_TG | \ INCHIDIFF_NUM_TG | INCHIDIFF_EXTRA_TG_ENDP | INCHIDIFF_MISS_TG_ENDP | INCHIDIFF_TG | \ INCHIDIFF_NUM_ISO_AT | INCHIDIFF_ISO_AT | INCHIDIFF_CHARGE | INCHIDIFF_REM_PROT | INCHIDIFF_REM_ISO_H |\ INCHIDIFF_DIFF_TG_ENDP) #define INCHIDIFF_STEREO (INCHIDIFF_SC_INV | INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | \ INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS | INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF |\ INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) #endif /* __INCHICMP_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/incomdef.h000066400000000000000000000130521271037650300235070ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Former COMDEF.H Renamed 06/12/07 to avoid occassional conflict with Microsoft's COMDEF.H ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* common definitions -- do not change */ #ifndef __INCOMDEF_H__ #define __INCOMDEF_H__ #include "ichisize.h" /* SDF treatment */ #define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ #define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ /* size resrictions */ #define ATOM_EL_LEN 6 /* length of atom name string including zero termination */ #define ATOM_INFO_LEN 36 /* inf_ATOM output string ^123Al^+2H12..(+)/999/999/999/999: 32 chars */ #define MAXVAL 20 /* max number of bonds per atom */ #define MAX_STEREO_BONDS 3 /* max number of stereogenic bonds per atom */ #define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, deuterium, tritium */ #define ATW_H 1 /* hydrogen atomic weight */ /* input bond type definition */ #define MIN_INPUT_BOND_TYPE 1 #define MAX_INPUT_BOND_TYPE 4 #define BOND_TYPE_SINGLE 1 #define BOND_TYPE_DOUBLE 2 #define BOND_TYPE_TRIPLE 3 #define BOND_TYPE_ALTERN 4 #define STEREO_SNGL_UP 1 #define STEREO_SNGL_EITHER 4 #define STEREO_SNGL_DOWN 6 #define STEREO_DBLE_EITHER 3 /* MOlfile */ #define INPUT_STEREO_SNGL_UP 1 #define INPUT_STEREO_SNGL_EITHER 4 #define INPUT_STEREO_SNGL_DOWN 6 #define INPUT_STEREO_DBLE_EITHER 3 /* #define BOND_MARK_ODD 0x10 #define BOND_MARK_EVEN 0x20 */ #define BOND_MARK_PARITY 0x30 #define BOND_MARK_HIGHLIGHT 0x40 /* highlight equivalent components */ #define BOND_MARK_ODD '-' #define BOND_MARK_EVEN '+' #define BOND_MARK_UNDF '?' #define BOND_MARK_UNKN 'u' #define BOND_MARK_ERR '*' #define SALT_DONOR_H 1 #define SALT_DONOR_Neg 2 #define SALT_ACCEPTOR 4 #define SALT_p_DONOR 8 /* >C-SH */ #define SALT_p_ACCEPTOR 16 /* >C-S(-) */ #define SALT_DONOR_ALL (SALT_DONOR_Neg | SALT_DONOR_H | SALT_p_ACCEPTOR | SALT_p_DONOR) #define SALT_DONOR_Neg2 (SALT_DONOR_Neg | SALT_p_ACCEPTOR) #define SALT_DONOR_H2 (SALT_DONOR_H | SALT_p_DONOR) #define SALT_DONOR (SALT_DONOR_Neg | SALT_DONOR_H) #define SALT_SELECTED 32 /* radical definitions */ #define RADICAL_SINGLET 1 #define RADICAL_DOUBLET 2 #define RADICAL_TRIPLET 3 /* metal definition */ #define METAL 1 /* definition of an element: lowest valence */ #define METAL2 3 /* definition of an element: lowest and next to it valence */ #define IS_METAL 3 /* metal bitmap */ /* isotopic shift */ #define ZERO_ATW_DIFF 127 /* mark mass of the most abundant isotope */ /* other types */ #define UCINT (int)(unsigned char) #ifndef INCHI_US_CHAR_DEF typedef signed char S_CHAR; typedef unsigned char U_CHAR; #define INCHI_US_CHAR_DEF #endif #ifndef INCHI_US_SHORT_DEF typedef signed short S_SHORT; typedef unsigned short U_SHORT; #define INCHI_US_SHORT_DEF #endif /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif #define STR_ERR_LEN 256 int AddMOLfileError( char *pStrErr, const char *szMsg ); /* allocator */ #ifndef inchi_malloc void *inchi_malloc(size_t c); #endif #ifndef inchi_calloc void *inchi_calloc(size_t c, size_t n); #endif #ifndef inchi_free void inchi_free(void *p); #endif /* sorting etc */ void inchi_swap ( char *a, char *b, size_t width ); int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ); #define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin #define MOLFILE_ERR_SET(err, new_err, msg) \ if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INCOMDEF_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/inpdef.h000066400000000000000000000371441271037650300232000ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* input/output format */ #ifndef __INPDEF_H__ #define __INPDEF_H__ /*^^^ */ #include "mode.h" #include "incomdef.h" #include "ichidrp.h" /*^^^ */ #define bDrawingLabelLeftShift endpoint /* for drawing only */ typedef S_SHORT ST_CAP_FLOW; /* inp_ATOM::at_type */ #define ATT_NONE 0x0000 #define ATT_ACIDIC_CO 0x0001 #define ATT_ACIDIC_S 0x0002 #define ATT_OO 0x0004 #define ATT_ZOO 0x0008 #define ATT_NO 0x0010 #define ATT_N_O 0x0020 #define ATT_ATOM_N 0x0040 #define ATT_ATOM_P 0x0080 #define ATT_OTHER_NEG_O 0x0100 #define ATT_OTHER_ZO 0x0200 /* -Z=O or =Z=O */ #define ATT_OH_MINUS 0x0400 /* OH(-), O=O,S,Se,Te */ #define ATT_O_PLUS 0x0800 /* -OH2(+), =OH(+), -OH(+)-, OH3(+), =O(+)-, etc; O=O,S,Se,Te */ #define ATT_PROTON 0x1000 #define ATT_HalAnion 0x2000 #define ATT_HalAcid 0x4000 #if ( FIX_NP_MINUS_BUG == 1 ) #define ATT_NP_MINUS_V23 0x8000 /* =N(-) or =P(-) where = previously was triple */ #endif #define AT_FLAG_ISO_H_POINT 0x01 /* may have isotopic H */ #define PERIODIC_NUMBER_H 1 #ifndef NUMH #define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) #define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) #endif #define FlagSC_0D 1 /* bUsed0DParity */ #define FlagSB_0D 2 /* bUsed0DParity */ #define SB_PARITY_FLAG 0x38 /* mask for disconnected metal parity if it is different */ #define SB_PARITY_SHFT 3 /* number of right shift bits to get disconnected metal parity */ #define SB_PARITY_MASK 0x07 #define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ #define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ typedef struct tagInputAtom { char elname[ATOM_EL_LEN]; /* chem. element name */ U_CHAR el_number; /* number of the element in the Periodic Table */ AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ AT_NUMB orig_at_number; /* original atom number */ AT_NUMB orig_compt_at_numb; /* atom number within the component before terminal H removal */ S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down; this atom is at the pointing wedge, negative => on the opposite side; 3=Either double bond */ U_CHAR bond_type[MAXVAL]; /* 1..4; 4="aromatic", should be discouraged on input */ S_CHAR valence; /* number of bonds = number of neighbors */ S_CHAR chem_bonds_valence; /* sum of bond types (type 4 needs special treatment) */ S_CHAR num_H; /* number of implicit hydrogens including D and T */ S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* number of implicit 1H, 2H(D), 3H(T) < 16 */ S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ /* >0 => (mass) - (mass of the most abundant isotope) + 1 */ /* <0 => (mass) - (mass of the most abundant isotope) */ S_CHAR charge; /* charge */ S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ S_CHAR bAmbiguousStereo; S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ AT_NUMB at_type; /* ATT_NONE, ATT_ACIDIC */ AT_NUMB component; /* number of the structure component > 0 */ AT_NUMB endpoint; /* id of a tautomeric group */ AT_NUMB c_point; /* id of a positive charge group */ double x; double y; double z; /* cml 0D parities */ S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ /* cml tetrahedral parity */ S_CHAR p_parity; AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* cml bond parities */ S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; -1 means removed explicit H */ /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ #if ( FIND_RING_SYSTEMS == 1 ) S_CHAR bCutVertex; AT_NUMB nRingSystem; AT_NUMB nNumAtInRingSystem; AT_NUMB nBlockSystem; #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) AT_NUMB nDistanceFromTerminal; /* terminal atom or ring system has 1, next has 2, etc. */ #endif #endif } inp_ATOM; typedef struct tagOrigAtom { /* initially filled out by MolfileToOrigAtom */ /* may be changed by disconnecting salts and disconnecting metals */ inp_ATOM *at; int num_dimensions; int num_inp_bonds; int num_inp_atoms; /* may be changed by disconnecting salts and disconnecting metals */ int num_components; /* set by MarkDisconnectedComponents() and disconnecting metals */ int bDisconnectSalts; /* whether salt disconnection is possible */ int bDisconnectCoord; /* 0 if no disconnection needed else (Num Implicit H to disconnect)+1 */ #if ( bRELEASE_VERSION == 0 ) int bExtract; #endif AT_NUMB *nCurAtLen; /* has max_num_components elements */ AT_NUMB *nOldCompNumber; /* 0 or component number in previous numbering */ int nNumEquSets; /* number of found component equivalence sets */ AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ AT_NUMB *nSortedOrder; /* num_components elements, values = 1..num_components; only if num_components > 1 */ int bSavedInINCHI_LIB[INCHI_NUM]; int bPreprocessed[INCHI_NUM]; MOL_COORD *szCoord; } ORIG_ATOM_DATA; typedef struct tagOriginalStruct { int num_atoms; char *szAtoms; char *szBonds; char *szCoord; } ORIG_STRUCT; typedef struct tagAtomParmsForDrawing { char at_string[ATOM_INFO_LEN]; int DrawingLabelLeftShift; int DrawingLabelLength; AT_NUMB nCanonNbr; /* if zero then do not use all data for the atom */ AT_NUMB nCanonEquNbr; AT_NUMB nTautGroupCanonNbr; AT_NUMB nTautGroupEquNbr; S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ #ifdef DISPLAY_DEBUG_DATA int nDebugData; #endif S_CHAR cHighlightTheAtom; S_CHAR cStereoCenterParity; S_CHAR cStereoBondParity[MAX_STEREO_BONDS]; S_CHAR cStereoBondWarning[MAX_STEREO_BONDS]; S_CHAR cStereoBondNumber[MAX_STEREO_BONDS]; } inf_ATOM; #define INF_STEREO_ABS 0x0001 #define INF_STEREO_REL 0x0002 #define INF_STEREO_RAC 0x0004 #define INF_STEREO_NORM 0x0008 #define INF_STEREO_INV 0x0010 #define INF_STEREO 0x0020 #define INF_STEREO_ABS_REL_RAC (INF_STEREO_ABS | INF_STEREO_REL | INF_STEREO_RAC) #define INF_STEREO_NORM_INV (INF_STEREO_NORM | INF_STEREO_INV) #define MAX_LEN_REMOVED_PROTONS 128 typedef struct tagInfoAtomData { inf_ATOM *at; int num_at; AT_NUMB StereoFlags; AT_NUMB num_components; AT_NUMB *pStereoFlags; int nNumRemovedProtons; int num_removed_iso_H; /* number of exchangable isotopic H */ NUM_H num_iso_H[NUM_H_ISOTOPES]; /* number of exchangable isotopic H */ char szRemovedProtons[MAX_LEN_REMOVED_PROTONS]; } INF_ATOM_DATA; typedef struct tagInputAtomData { inp_ATOM *at; inp_ATOM *at_fixed_bonds; /* tautomeric case, added or removed H */ int num_at; int num_removed_H; int num_bonds; int num_isotopic; int bExists; int bDeleted; int bHasIsotopicLayer; int bTautomeric; int bTautPreprocessed; int nNumRemovedProtons; NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ INCHI_MODE bTautFlags; INCHI_MODE bTautFlagsDone; INCHI_MODE bNormalizationFlags; } INP_ATOM_DATA; typedef INP_ATOM_DATA INP_ATOM_DATA2[TAUT_NUM]; typedef struct tagNormCanonFlags { INCHI_MODE bTautFlags[INCHI_NUM][TAUT_NUM]; INCHI_MODE bTautFlagsDone[INCHI_NUM][TAUT_NUM]; INCHI_MODE bNormalizationFlags[INCHI_NUM][TAUT_NUM]; int nCanonFlags[INCHI_NUM][TAUT_NUM]; } NORM_CANON_FLAGS; typedef struct tagCompositeAtomData { inp_ATOM *at; int num_at; int num_removed_H; int num_bonds; int num_isotopic; int bExists; int bDeleted; /* unused */ int bHasIsotopicLayer; int bTautomeric; int nNumRemovedProtons; NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ AT_NUMB *nOffsetAtAndH; int num_components; } COMP_ATOM_DATA; /* typedef COMP_ATOM_DATA COMP_ATOM_DATA3[TAUT_NUM+1]; */ #define ADD_LEN_STRUCT_FPTRS 100 /* allocation increments */ typedef long INCHI_FPTR; typedef struct tagStructFptrs { INCHI_FPTR *fptr; /* input: fptr[cur_fptr] = file pointer to the structure to read */ /* output: fptr[cur_fptr+1] = file pointer to the next structure or EOF */ int len_fptr; /* allocated length of fptr */ int cur_fptr; /* input: k-1 to read the kth struct, k = 1, 2, 3,...; left unchanged; struct number := cur_fptr+1 */ int max_fptr; /* length of the filled out portion of fptr */ } STRUCT_FPTRS; #define FLAG_INP_AT_CHIRAL 1 #define FLAG_INP_AT_NONCHIRAL 2 #define FLAG_SET_INP_AT_CHIRAL 4 #define FLAG_SET_INP_AT_NONCHIRAL 8 /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int MolfileToInpAtom( FILE *inp_molfile, int bDoNotAddH, inp_ATOM **at, MOL_COORD **szCoord, int max_num_at, int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); int MolfileToOrigAtom( FILE *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, int bGetOrigCoord, int bDoNotAddH, const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); int INChIToOrigAtom( INCHI_IOSTREAM *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, int bGetOrigCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ); int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ); int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ); int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ); /* FIX_ADJ_RAD == 1 */ int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ); int post_fix_odd_things( int num_atoms, inp_ATOM *at ); int remove_ion_pairs( int num_atoms, inp_ATOM *at ); int bFoundFeature( inp_ATOM *at, int num_atoms ); int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long nNumb); void FreeInpAtom( inp_ATOM **at ); void FreeInfAtom( inf_ATOM **at ); void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ); void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ); void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ); void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ); int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at); inf_ATOM *CreateInfAtom( int num_atoms ); inp_ATOM *CreateInpAtom( int num_atoms ); int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, const INF_ATOM_DATA *inf_at_data_from); int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ); int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ); #ifndef COMPILE_ANSI_ONLY int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp ); #endif void PrintFileName( const char *fmt, FILE *output_file, const char *szFname ); void MySleep( unsigned long ms ); #ifndef __ICHITIME_H__ struct tagInchiTime; int bInchiTimeIsOver( struct tagInchiTime *TickEnd ); #endif int get_endpoint_valence( U_CHAR el_number ); #if ( KETO_ENOL_TAUT == 1 ) int get_endpoint_valence_KET( U_CHAR el_number ); #endif #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ); void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ); void MakeNewOrd( int num_atoms, AT_RANK *new_ord ); #endif int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ); /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __INPDEF_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/lreadmol.h000066400000000000000000001524021271037650300235250ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* local prototypes */ int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, const char *pSdfLabel, char *pSdfValue, char *pStrErr ); MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, int bGetOrigCoord, int *err, char *pStrErr ); static int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr); static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ); static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ); static int identify_sdf_label( char* inp_line, const char *pSdfLabel ); static long extract_cas_rn( char *line ); static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ); static int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr); static int RemoveNonPrintable( char *line ); /******/ #ifndef MOLFILE_ERR_FIN #define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin #endif #ifndef MOLFILE_ERR_SET #define MOLFILE_ERR_SET(err, new_err, msg) \ if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) #endif /*************************************************************************/ int AddMOLfileError( char *pStrErr, const char *szMsg ) { if ( pStrErr && szMsg && szMsg[0] ) { int lenStrErr = strlen( pStrErr ); int lenMsg = strlen( szMsg ); char *p = strstr( pStrErr, szMsg ); if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && (p+lenMsg == pStrErr+lenStrErr || p[lenMsg] == ';' && p[lenMsg+1] == ' ' || p[lenMsg-1]==':' && p[lenMsg]==' ') ) { return 1; /* reject duplicates */ } if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { /* enough room to add */ if (lenStrErr > 0) { if ( pStrErr[lenStrErr-1] != ':' ) { strcat( pStrErr, ";" ); } strcat( pStrErr, " " ); } strcat( pStrErr, szMsg ); return 1; } /* no room */ if ( strstr( pStrErr, "..." ) ) { return 0; /* no room mark has already been set */ } if ( lenStrErr + 3 < STR_ERR_LEN ) { strcat( pStrErr, "..." ); } } return 0; } /*************** static **********************************************************/ int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ) { int i, c; /* required len >= 0; dest must have at least len+1 bytes */ if ( len > 0 ) strncpy( dest, source, len ); dest[len]='\0'; len = ( len > 0 )? (int)strlen( dest) : 0; for ( i = (len-1); i >= 0 && 0 != (c = source[i]) && isspace(UCINT c); i-- ) ; *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ return len; /* number of actually processed bytes; zero termination not included */ } /************* static ************************************************************/ int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr) { /* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero, * that is actual length of the string pointed by 'data' * should be at least field_len+1 bytes. * For numerical data 'field_len' is length of input data field * For numerical integral data field_len <= 0 means read up to first * non-numeric character as strtod() does ("free format") * 2. return value: for MOL_STRING_DATA: number of bytes excluding trailing zero * for all others: 1=success; 0 = empty; -1= error * 3. on exit *line_ptr points to the next byte after the last entered */ char *p = *line_ptr, *q, *p_end; int i, ret=1, c, len; long ldata; double ddata; switch( data_type ) { case MOL_STRING_DATA: for ( i= 0; i < field_len && 0 != (c = p[i]) && isspace(UCINT c); i++ ) /* pass by all leading spaces */ ; len = mol_copy_check_empty( (char*)data, &p[i], field_len-i, &q ); ret = ( q - (char*)data );/* actual data length */ *q = '\0'; /* add zero termination to data if it is not there yet*/ *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ break; case MOL_CHAR_INT_DATA: case MOL_SHORT_INT_DATA: case MOL_LONG_INT_DATA: { /* block start */ char str[MOL_MAX_VALUE_LEN+1]; ldata = 0L; if ( field_len > MOL_MAX_VALUE_LEN ) { ret = -1; }else if ( field_len > 0 ) { /* fixed length */ *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) ); *q = '\0'; if ( !len || !(q-str) ) { /* empty string */ ret = 0; }else if ( (ldata=strtol(str,&p_end,10), p_end != q) ){ /* wrong data: incompletely interpreted */ ret = -1; } }else{ /* free format: field_len <= 0 */ ldata = strtol( p, &p_end, 10 ); *line_ptr += ( len = p_end - p ); if ( len == 0 ){ ret = 0; } } switch( data_type ) { case MOL_CHAR_INT_DATA: if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ){ /* from || to &&: 11-19-96 */ *(S_CHAR*)data = (S_CHAR)ldata; }else{ *(S_CHAR*)data = (S_CHAR)0; ret = -1; } break; case MOL_SHORT_INT_DATA: if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ){ *(S_SHORT*)data = (S_SHORT)ldata; }else{ *(S_SHORT*)data = (S_SHORT)0; ret = -1; } break; case MOL_LONG_INT_DATA: if ( LONG_MIN < ldata && ldata < LONG_MAX ){ *(long*)data = (long)ldata; }else{ *(long*)data = 0L; ret = -1; } break; default: ret=-1; } } /* block end */ break; case MOL_DOUBLE_DATA: case MOL_FLOAT_DATA: { /* block start */ char str[MOL_MAX_VALUE_LEN+1]; if ( field_len > MOL_MAX_VALUE_LEN ) { ret = -1; ddata = 0.0; }else if ( field_len > 0 ) { *line_ptr += (len = mol_copy_check_empty( str, p, field_len, &q )); *q = '\0'; if ( !len || !(q-str) ) { /* empty string */ ddata = 0.0; ret = 0; }else if ( (ddata=strtod(str,&p_end), p_end != q) ){ /* wrong data */ ret = -1; } }else{ /* free format */ ddata = strtod( p, &p_end ); *line_ptr += ( len = p_end - p ); if ( len == 0 ){ ret = 0; } } switch(data_type){ case MOL_DOUBLE_DATA: if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ){ /* replaced ldata with ddata 6-30-98 DCh */ *(double*)data = ddata; }else{ *(double*)data = 0.0; ret = -1; } break; case MOL_FLOAT_DATA: if ( fabs(ddata) <= (double)FLT_MIN ) { *(float*)data = 0.0; }else if ( fabs(ddata) >= (double)FLT_MAX ) { *(float*)data = 0.0; ret = -1; }else{ *(float*)data = (float)ddata; } break; } } /* block end */ break; case MOL_JUMP_TO_RIGHT: for ( i = 0; i < field_len && p[i]; i++ ) ; *line_ptr += i; ret = i; break; default: ret = -1; } return ret; } /************* static ************************************************************/ int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr) { /* All input lines can have are up 80 characters */ /* Header Block */ char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */ int err = 0, len; const int line_len = sizeof(line); char *p; /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */ /*------------ header line #1: name ----------------*/ if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ err = 1; /* can't read the input file line */ /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */ goto err_fin; } remove_one_lf( line ); /* -- Disabled to relax strictness: allow > 80 chars names. if ( line[MOLFILEMAXLINELEN] ){ err = 2; // too long line goto err_fin; } */ len = mol_read_datum( hdr->szMoleculeName, sizeof(hdr->szMoleculeName)-1, MOL_STRING_DATA, &p ); /*----------- header line #2 -----------------------*/ if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ err = 3; /* can't read the input file line */ /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */ goto err_fin; } remove_one_lf( line ); /* -- Disabled to relax strictness: allow > 80 chars names. if ( line[MOLFILEMAXLINELEN] ){ err = 4; // too long input file line goto err_fin; } */ len = mol_read_datum( hdr->szUserInitials, sizeof(hdr->szUserInitials)-1, MOL_STRING_DATA, &p ); len = mol_read_datum( hdr->szProgramName, sizeof(hdr->szProgramName)-1, MOL_STRING_DATA, &p ); /*------------ Relax strictness -----------------------*/ len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p ); len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p ); len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p ); len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p ); len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p ); len = mol_read_datum( hdr->szDimCode, sizeof(hdr->szDimCode)-1, MOL_STRING_DATA, &p ); len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p ); len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p ); len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p ); len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p ); /* save the whole line 2 */ p = line; len = mol_read_datum( hdr->szMoleculeLine2, sizeof(hdr->szMoleculeLine2)-1, MOL_STRING_DATA, &p ); /*------------ header line #3: comment ----------------*/ if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ err = 7; /* can't read the line */ /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */ goto err_fin; } remove_one_lf( line ); /* -- Disabled to relax strictness: allow > 80 chars comments. if ( line[MOLFILEMAXLINELEN] ){ err = 8; // too long line goto err_fin; } */ len = mol_read_datum( hdr->szComment, sizeof(hdr->szComment)-1, MOL_STRING_DATA, &p ); err_fin: return err; } /********** static *****************************************************/ int RemoveNonPrintable( char *line ) { int i, c, num = 0; if ( line ) { for ( i = 0; c = UCINT line[i]; i ++ ) { /* assuming ASCII charset */ if ( c < ' ' || c >= 0x7F ) { line[i] = '.'; num ++; } } } return num; } /************** static *************************************************/ int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ) { char *p; char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); int err = 0, len; if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ MOLFILE_ERR_FIN (err, 1, err_fin, "Cannot read counts line"); /* can't read the input file line */ } remove_one_lf( line ); if ( line[MOLFILEMAXLINELEN] ){ MOLFILE_ERR_SET (err, 0, "Too long counts line"); /* too long input file line */ } if ( 0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p ) #if ( MOL_QUERY == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif || 0 > mol_read_datum( NULL, /*obsolete*/ 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p ) #if ( MOL_CPSS == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ) ){ err = 3; /* can't interpret counts line */ MOLFILE_ERR_SET (err, 3, "Cannot interpret counts line:"); /* too long input file line */ RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); goto err_fin; } len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof(ctab->csCurrentCtabVersion)-1, MOL_STRING_DATA, &p ); err_fin: return err; } /************ static *************************************************************/ int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) { char *p; char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); S_SHORT i, chg; static S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; /* 0 1 2 3 4 5 6 7 */ /* if ( NULL == ctab->MolAtom ){ err = 1; goto err_fin; // internal error: MolAtom structure has not been allocated } */ for ( i = 0; i < ctab->nNumberOfAtoms; i++ ) { if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ if ( !err ) { MOLFILE_ERR_SET (err, 2, "Cannot read atom block line"); } break; } remove_one_lf( line ); if ( line[MOLFILEMAXLINELEN] ){ MOLFILE_ERR_SET (err, 0, "Too long atom block line"); } if ( err ) { if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = -abs(err); break; } continue; /* bypass the rest of the Atom block */ } if ( NULL != ctab->szCoord ) { mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */ } if ( NULL != ctab->MolAtom ) { if ( 0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p ) || 0 > mol_read_datum( NULL, /* undescribed in article*/ 1, MOL_JUMP_TO_RIGHT, &p ) || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */ #ifdef TARGET_EXE_USING_API || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p ) #else || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_CHAR_INT_DATA, &p ) #endif || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p ) #if ( MOL_QUERY == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ) ) { err = 4; MOLFILE_ERR_SET (err, 4, "Cannot interpret atom block line:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = -abs(err); break; } continue; /* can't interpret a first half of atom block line */ } if ( 2 == strlen(ctab->MolAtom[i].szAtomSymbol) && isupper(UCINT ctab->MolAtom[i].szAtomSymbol[1])) ctab->MolAtom[i].szAtomSymbol[1] = (char)tolower(UCINT ctab->MolAtom[i].szAtomSymbol[1]); /* 5-4-99 DCh*/ if ( (chg = (S_SHORT) ctab->MolAtom[i].cCharge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) { /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */ ctab->MolAtom[i].cCharge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ ctab->MolAtom[i].cRadical = 0; }else if ( 'R' == (chg = charge_val[chg]) ){ ctab->MolAtom[i].cCharge = 0; ctab->MolAtom[i].cRadical = RADICAL_DOUBLET; }else{ ctab->MolAtom[i].cCharge = (S_CHAR)chg; /* actual charge value */ ctab->MolAtom[i].cRadical = 0; } #ifdef TARGET_EXE_USING_API if ( ctab->MolAtom[i].cMassDifference ) { /* e_ReadMOL.c specific */ ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG; } #endif if ( #if ( MOL_CPSS == MOL_PRESENT ) 0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p ) #else 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif #if ( MOL_REACT == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif #if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif ){ err = 5; /* can't interpret a second half of atom block line */ MOLFILE_ERR_SET (err, 5, "Cannot interpret atom block line:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = -abs(err); break; } continue; } } } /* err_fin: */ return err; } /************ static *************************************************************/ int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) { char *p; char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); S_SHORT i; /* if ( NULL == ctab->MolBond ){ err = 1; goto err_fin; // internal error: memory has not been allocated for MolBond structure } */ for ( i = 0; i < ctab->nNumberOfBonds; i++ ) { if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ if ( !err ) { MOLFILE_ERR_SET (err, 2, "Cannot read bond block line"); } break; } remove_one_lf( line ); if ( line[MOLFILEMAXLINELEN] ){ err = err? err : 3; /* too long input file line */ } if ( err ) { if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = -abs(err); break; } continue; } if ( ctab->MolBond ) { if ( 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p ) || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p ) #if ( MOL_QUERY == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */ #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif #if ( MOL_REACT == MOL_PRESENT ) || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p ) #else || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) #endif ){ if ( !err ) { /* can't interpret bonds block line */ MOLFILE_ERR_SET (err, 4, "Cannot interpret bond block line:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); } if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = -abs(err); break; } } } } /* err_fin: */ return err; } /********** static ***************************************************************/ int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) { /* just pass by all stext enties without attemp to interpret */ char *p; char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); S_SHORT i; for ( i = 0; i < 2*ctab->nNumberOfStextEntries; i++ ) { if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ if ( !err ) { MOLFILE_ERR_FIN (err, 2, err_fin, "Cannot read STEXT block line"); } break; /* can't read the input file line */ } /* remove_one_lf( line ); if ( line[MOLFILEMAXLINELEN] ){ MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line"); // too long input file line } */ } err_fin: return err; } /************ static *************************************************************/ int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ) { enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; char *p; char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; S_SHORT i, j; char charM[2]; char szBlank[3]; char szType[4]; S_SHORT skip_lines=0; S_SHORT num_entries; S_SHORT num_atoms = ctab->nNumberOfAtoms; int charge_encountered = 0; int radical_encountered = 0; int isotope_encountered = 0; /* if ( NULL == ctab->MolAtom ){ err = 1; goto err_fin; internal error: memory has not been allocated for MolAtom structure } */ for ( i = 0; ctab->csCurrentCtabVersion[0]? 1 : (i < ctab->nNumberOfPropertyLines); i++ ) { /* the last line should be M END */ /* ctab->csCurrentCtabVersion[0] == 0: exactly ctab->nNumberOfPropertyLines lines including M END */ /* ctab->csCurrentCtabVersion[0] != 0: read until M END line was encountered */ if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ if ( !err ) { MOLFILE_ERR_SET (err, 2, "Cannot read properties block line"); } goto err_fin; } remove_one_lf( line ); if ( line[MOLFILEMAXLINELEN] ){ MOLFILE_ERR_SET (err, 3, "Too long properties block line"); continue; } if ( skip_lines > 0 ) { skip_lines --; continue; } /* alias. */ if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) { int len; nMultiLineMode = MULTI_LINE_MODE_NO_MODE; if ( 0 >= (len=normalize_name( p )) ) { nAtomNumber = 0; continue; } if( 0 < len && len < (int)(sizeof(ctab->MolAtom->szAtomSymbol)) ) { int nCharge, nRad; MOL_ATOM* MolAtom = ctab->MolAtom + nAtomNumber-1; /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */ /* extract radicals & charges */ extract_ChargeRadical( p, &nRad, &nCharge ); /* Aliased atom cannot have charge, radical & mass difference */ /* in the atom table or "M CHG", "M RAD", "M ISO" */ /* if ( nCharge ) */ MolAtom->cCharge = (S_CHAR)nCharge; /* if ( nRad ) */ MolAtom->cRadical = (char)nRad; if ( 1 == len && 'D' == p[0] ) { /* H isotope */ p[0] = 'H'; #ifdef TARGET_EXE_USING_API MolAtom->cMassDifference=(1 + ISOTOPIC_SHIFT_FLAG); #else MolAtom->cMassDifference=1; #endif } else if ( 1 == len && 'T' == p[0] ) { /* H isotope */ p[0] = 'H'; #ifdef TARGET_EXE_USING_API MolAtom->cMassDifference=(2 + ISOTOPIC_SHIFT_FLAG); #else MolAtom->cMassDifference=2; #endif } else MolAtom->cMassDifference=0; if ( strlen(p) < sizeof(ctab->MolAtom[0].szAtomSymbol) ) { strcpy(MolAtom->szAtomSymbol, p); } else { strcpy(MolAtom->szAtomSymbol, "???"); } MolAtom->cAtomAliasedFlag ++; } skip_lines = 0; nAtomNumber = 0; continue; } if ( 1 != mol_read_datum( charM, sizeof(charM) - 1, MOL_STRING_DATA, &p ) || 0 != mol_read_datum( szBlank, sizeof(szBlank) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */ || 0 >= mol_read_datum( szType, sizeof(szType) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */ ) { if ( !strcmp( line, SDF_END_OF_DATA ) ) { err = err? -abs(err): -4; break; } continue; /* ignore because cannot recognize */ } if ( charM[0] == 'V' ){ skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ continue; } if ( charM[0] == 'G' ){ skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ continue; } if ( charM[0] == 'A' ) { if ( NULL != ctab->MolAtom && 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && nAtomNumber <= ctab->nNumberOfAtoms ){ /* Atom Alias [ISIS/Desktop] two-line property */ nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; continue; } else { nAtomNumber = 0; skip_lines = 1; continue; } } if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ if ( 0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ) ) { skip_lines = 0; } continue; } if ( charM[0] != 'M' ) {/* cannot recognize a line */ continue; } if ( !strcmp( szType, "REG" ) ) { int len; p = p + strspn( p, " " ); len = strcspn( p, " " ); len = inchi_min( len, MOL_MAX_VALUE_LEN ); mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p ); continue; } if ( !strcmp( szType, "END" ) ){ if ( ctab->csCurrentCtabVersion[0] ) break; /* end of property lines */ continue; } if ( NULL == ctab->MolAtom ) continue; /* ignore because the user requested to bypass all this stuff */ /*----------------------------------- charge: Generic */ if ( !strcmp( szType, "CHG" ) && 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && 1 <= num_entries && num_entries <= 8 ) { S_SHORT atoms[8]; S_SHORT charges[8]; if ( !charge_encountered && !radical_encountered ) { /* first charge or radical record clears all Atom Block */ /* entered charge and radical data to zeroes */ charge_encountered = -1; } for ( j = 0; j < num_entries; j++ ) { if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) || atoms[j] <= 0 || atoms[j] > num_atoms || charges[j] < -15 || charges[j] > 15 ) { goto charge_error; } } if ( charge_encountered == -1 ) { for ( j = 0; j < num_atoms; j++ ) { if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms.*/ ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; } charge_encountered = 1; } for ( j = 0; j < num_entries; j++ ) { if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) /* do not change aliased atoms.*/ ctab->MolAtom[atoms[j]-1].cCharge = (S_CHAR)charges[j]; } continue; charge_error: MOLFILE_ERR_SET (err, 0, "Charge not recognized:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); continue; /* ignore for now */ } /*-------------------------------------- radical: Generic */ if ( !strcmp( szType, "RAD" ) && 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && 1 <= num_entries && num_entries <= 8 ) { S_SHORT atoms[8]; S_SHORT radicals[8]; if ( !charge_encountered && !radical_encountered ) { /* first charge or radical record clears all Atom Block */ /* entered charge and radical data to zeroes */ radical_encountered = -1; } for ( j = 0; j < num_entries; j++ ) { if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) || atoms[j] <= 0 || atoms[j] > num_atoms || radicals[j] < 0 || radicals[j] > 3 ) { goto radical_error; } } if ( radical_encountered == -1 ) { for ( j = 0; j < num_atoms; j++ ) { if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms. 5-3-99 DCh */ ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; } radical_encountered = 1; } for ( j = 0; j < num_entries; j++ ) { if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) { /* do not change aliased atoms. 5-3-99 DCh */ ctab->MolAtom[atoms[j]-1].cRadical = (S_CHAR)radicals[j]; } } continue; radical_error: MOLFILE_ERR_SET (err, 0, "Radical not recognized:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); continue; /* ignore error for now */ } /*-------------------------------------- isotope: Generic */ if ( !strcmp( szType, "ISO" ) && 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && 1 <= num_entries && num_entries <= 8 ) { S_SHORT atoms[8]; S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ if ( !isotope_encountered ) { /* first charge or radical record clears all Atom Block */ /* entered charge and radical data to zeroes */ isotope_encountered = -1; } for ( j = 0; j < num_entries; j++ ) { if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || 0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) || atoms[j] <= 0 || atoms[j] > num_atoms /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) { /* goto isotope_error; */ atoms[j] = -1; /* flag error */ MOLFILE_ERR_SET (err, 0, "Isotopic data not recognized:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); continue; /* ignore isotopic error for now */ } } if ( isotope_encountered == -1 ) { for ( j = 0; j < num_atoms; j++ ) { /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/ /* clear even aliased atoms */ ctab->MolAtom[j].cMassDifference = 0; } isotope_encountered = 1; } for ( j = 0; j < num_entries; j++ ) { if ( atoms[j] <= 0 ) continue; /* ignore isotopic error for now */ if ( 1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */) { char *at = ctab->MolAtom[atoms[j]-1].szAtomSymbol; if ( at[1] || at[0] != 'D' && at[0] != 'T' ) { /* D & T cannot have ISO */ /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ #ifdef TARGET_EXE_USING_API /*^^^ Check added 5-10-2008 - IPl */ if (iso_mass[j] > 0) /* According to MDL specification, p.12, only a positive integer is allowed. And yes, there appeared some MOL/SD files contaning here a negative value. This manifested in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results. */ ctab->MolAtom[atoms[j]-1].cMassDifference = iso_mass[j]; /* mass, not difference */ #else int atw, atw_diff; /*^^^ NB: According to MDL specification, difference should be in [-18; +12] range, not in [-19; +19] as is checked below. */ if ( (atw = get_atw( at )) && abs( atw_diff = (int)iso_mass[j] - atw ) < 20 ) { ctab->MolAtom[atoms[j]-1].cMassDifference = (char)(atw_diff? atw_diff : ZERO_ATW_DIFF); } #endif } } } continue; } } err_fin: return err; } /************ global *************************************************************/ MOL_DATA* delete_mol_data( MOL_DATA* mol_data ) { if ( mol_data ) { if ( mol_data->ctab.MolAtom ) inchi_free( mol_data->ctab.MolAtom ); if ( mol_data->ctab.MolBond ) inchi_free( mol_data->ctab.MolBond ); if ( mol_data->ctab.szCoord ) inchi_free( mol_data->ctab.szCoord ); inchi_free( mol_data ); mol_data = NULL; } return mol_data; } /************* global ************************************************************/ /* Comletely ingnore STEXT block, queries, and 3D features */ MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, int bGetOrigCoord, int *err, char *pStrErr ) { MOL_DATA* mol_data = NULL; int ret = 0, prev_ret, bEndOfData = 0; int bReadAll = ( OnlyHeaderBlock == NULL ); MOL_CTAB ctab, *pCtab = NULL; MOL_HEADER_BLOCK *pHdr = NULL; *err = 0; if ( bReadAll ) { if ( NULL == ( mol_data = ( MOL_DATA* )inchi_calloc( 1, sizeof(MOL_DATA) ) ) ){ ret = 1; /* can't allocate mol_data structure */ AddMOLfileError( pStrErr, "Out of RAM" ); goto err_fin; } pHdr = &mol_data->hdr; pCtab = &mol_data->ctab; } else { pHdr = OnlyHeaderBlock; pCtab = OnlyCtab? OnlyCtab : &ctab; memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); memset( pCtab, 0, sizeof( MOL_CTAB ) ); } pCtab->MolBond = NULL; pCtab->MolAtom = NULL; pCtab->szCoord = NULL; if ( 0 != ( ret = mol_read_hdr(pHdr, inp, pStrErr) ) ){ ret += 10; goto err_fin; /* most probably end of file */ } if ( 0 != ( ret = mol_read_counts_line( pCtab , inp, pStrErr) ) ){ ret += 20; goto err_fin; } if ( bReadAll ) { if ( NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_ATOM)) ) ){ ret = 2; /* can't allocate MolAtom structure */ MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); } if ( bGetOrigCoord && NULL == ( mol_data->ctab.szCoord = (MOL_COORD*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_COORD)) ) ){ ret = 2; /* can't allocate MolAtom structure */ MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); } } if ( 0 != ( ret = read_atom_block(pCtab, inp, ret, pStrErr) ) ){ if ( ret < 0 ) { ret = -ret; bEndOfData = 1; } ret += 30; /* goto err_fin; */ } if ( bReadAll && ret < 30 ) { if ( !bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfBonds,1), sizeof(MOL_BONDS)) ) ){ ret = 3; /* can't allocate MolBond structure */ MOLFILE_ERR_FIN (ret, 3, err_fin, "Out of RAM"); } } prev_ret = ret; if ( !bEndOfData && 0 != ( ret = read_bonds_block(pCtab, inp, ret, pStrErr) ) ){ if ( ret < 0 ) { ret = -ret; bEndOfData = 1; } ret = prev_ret? prev_ret : ret + 40; } prev_ret = ret; if ( !bEndOfData && 0 != ( ret = read_stext_block(pCtab, inp, ret, pStrErr) ) ){ ret = prev_ret? prev_ret : ret + 50; } prev_ret = ret; if ( !bEndOfData && 0 != ( ret = read_properties_block(pCtab, pHdr, inp, ret, pStrErr) ) ){ if ( ret < 0 ) { ret = -ret; bEndOfData = 1; } ret = prev_ret? prev_ret : ret + 60; } err_fin: *err = bEndOfData? -ret : ret; if ( bReadAll ) { if ( ret ) mol_data = delete_mol_data( mol_data ); /* delete all results */ return mol_data; } else { if ( ret ) return NULL; else return (MOL_DATA*)OnlyHeaderBlock; } } /******************************************************************/ static const char sdf_data_hdr_name[] = "NAME"; static const char sdf_data_hdr_comm[] = "COMMENT"; enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS , SDF_DATA_HEADER_USER, SDF_DATA_LINE , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK }; /********** static ********************************************************/ long extract_cas_rn( char *line ) { int i, j; i = line[0] == '-'? 1 : 0; for ( j = i; line[i]; i ++ ) { if ( isdigit( UCINT line[i] ) ) { line[j++] = line[i]; } else if ( line[i] != '-' ) { break; } } line[j] = '\0'; return strtol( line, NULL, 10 ); } /********** static ********************************************************/ int identify_sdf_label( char* inp_line, const char *pSdfLabel ) { char line[MOLFILEMAXLINELEN]; char *p, *q; int i, j, len; if ( (p = strchr( inp_line, '<' )) && (q = strchr( p, '>' )) && (len = q-p-1) > 0 && len < (int)sizeof(line) ) { memcpy( line, p+1, len ); line[len] = '\0'; for ( i = 0; isspace( UCINT line[i] ); i ++ ) ; for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) ; len = j-i+1; p = line+i; if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !memicmp( p, pSdfLabel, len ) ) return SDF_DATA_HEADER_USER; if ( len == sizeof(sdf_data_hdr_name)-1 && !memicmp( p, sdf_data_hdr_name, len ) ) return SDF_DATA_HEADER_NAME; if ( len == sizeof(sdf_data_hdr_comm)-1 && !memicmp( p, sdf_data_hdr_comm, len ) ) return SDF_DATA_HEADER_COMMENT; if ( !memicmp( p, "CAS", 3 ) ) return SDF_DATA_HEADER_CAS; } return SDF_DATA_HEADER; } /************* global *****************************************************/ int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, const char *pSdfLabel, char *pSdfValue, char *pStrErr ) { char line[MOLFILEINPLINELEN]; const int line_len = sizeof(line); int err = 0; int current_state = SDF_START; int n_blank_lines = 0; int n_lines = 0; char* p = NULL; int bNeedsName = name && lname > 0 && !name[0]; int bNeedsComm = comment && lcomment > 0 && !comment[0]; int bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue; int bNeedsCASrn = 0; int bCASrnIsUser = 0; if ( cas_reg_no != NULL ) { bNeedsCASrn = 1; *cas_reg_no = 0; bCASrnIsUser = (bNeedsUser && !memicmp(pSdfLabel,"CAS", 3)); } while ( err == 0 && current_state !=SDF_END_OF_DATA_BLOCK && NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ) ) { if ( !n_lines && !memcmp(line, "M END", 6) ) { continue; /* allow subtle errors */ } n_lines++; remove_trailing_spaces( line ); if ( line[MOLFILEMAXLINELEN] ){ if ( current_state != SDF_DATA_HEADER && current_state != SDF_DATA_LINE && current_state != SDF_DATA_HEADER_NAME && current_state != SDF_DATA_HEADER_USER && current_state != SDF_DATA_HEADER_COMMENT ) { line[MOLFILEMAXLINELEN] = '\0'; if ( !prev_err ) { MOLFILE_ERR_SET (err, 0, "Too long SData line truncated"); } } else { /* allow long lines in SDF data. 9-29-00 DCh */ line[MOLFILEMAXLINELEN] = '\0'; } } n_blank_lines += ( *line == '\0' ); switch( current_state ) { case SDF_START: case SDF_END_OF_DATA_ITEM: case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ if ( 0 == strcmp( line, SDF_END_OF_DATA ) ) { current_state = SDF_END_OF_DATA_BLOCK; } else if ( '>' == *line ) { current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser )? identify_sdf_label(line, pSdfLabel) : SDF_DATA_HEADER; }else if ( *line == '\0' ) { /* Added 9-25-97 DCh */ /* Relax the strictness: Allow more than 1 empty line. */ current_state=SDF_EMPTY_LINE; } else if ( !prev_err ) { MOLFILE_ERR_SET (err, 3, "Unexpected SData header line:"); RemoveNonPrintable( line ); AddMOLfileError(pStrErr, line); /* unexpected contents of data header line */ } else { err = 3; } break; case SDF_DATA_HEADER_NAME: if ( bNeedsName && 0 < normalize_name( line ) ) { bNeedsName = 0; mystrncpy( name, line, lname ); } goto got_data_line; case SDF_DATA_HEADER_COMMENT: if ( bNeedsComm && 0 < normalize_name( line ) ) { bNeedsComm = 0; mystrncpy( comment, line, lcomment ); } goto got_data_line; case SDF_DATA_HEADER_USER: if ( bNeedsUser && 0 < normalize_name( line ) ) { bNeedsUser = 0; mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); if ( bCASrnIsUser && bNeedsCASrn ) { *cas_reg_no = extract_cas_rn( line ); bNeedsCASrn = (0 == *cas_reg_no); } } goto got_data_line; case SDF_DATA_HEADER_CAS: if ( bNeedsCASrn && 0 < normalize_name( line ) ) { *cas_reg_no = extract_cas_rn( line ); bNeedsCASrn = (0 == *cas_reg_no); } goto got_data_line; case SDF_DATA_HEADER: case SDF_DATA_LINE: got_data_line: current_state = *line? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM; break; } } if ( 0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p ) ; /* err = 4; */ /* unexpected end of file: missing $$$$ */ else if (err && ( n_blank_lines == n_lines && *line == '\0' ) ) err = 5; /* empty lines -- do not know when this can happen */ if ( err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p ) { /* bypass up to $$$$ */ while ( ( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ) ) ; if ( p ) { err = 9; /* bypassed to $$$$; non-fatal */ AddMOLfileError(pStrErr, "Bypassing to next structure"); } } return err; } /**************** global **************************************************/ MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, int bGetOrigCoord, char *pname, int lname, long *Id, const char *pSdfLabel, char *pSdfValue, int *err, char *pStrErr ) { MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr ); int err_bypass_sdf = 0; if ( pname && lname ) { pname[0] = '\0'; } if ( Id ) { *Id = 0L; /* ignore for now */ } /* if ( mol_data && !*err ) { */ if ( *err < 0 ) { *err = -*err; /* end of data encountered */ } else { err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr ); if ( err_bypass_sdf ) { *err = err_bypass_sdf; /* important to continue to the next good structure */ } } /* } */ return mol_data; } /******************* global *********************************************************/ int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) { char line[MOLFILEINPLINELEN], *p; long fPtr; int ret = 1; char szNumber[32]; if ( inp_file && prb_file && fPtrStart >= 0L && fPtrEnd > fPtrStart && 0 == fseek( inp_file, fPtrStart, SEEK_SET ) ) { while ( fPtrEnd > (fPtr = ftell(inp_file)) && fPtr >= 0L && inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) { line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ if ( fPtr == fPtrStart && lNumb ) { int len; LtrimRtrim( line, &len ); len = sprintf( szNumber, "#%ld%s", lNumb, len?"/":"" ); mystrncpy( line+len, line, sizeof(line)-len-1 ); memcpy( line, szNumber, len ); } if ( !strchr(line, '\n') ) { p = line+strlen(line); p[0] = '\n'; p[1] = '\0'; } fputs( line, prb_file ); } ret = fseek( inp_file, fPtrEnd, SEEK_SET ); } return ret; } Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/mode.h000066400000000000000000001341241271037650300226530ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __MODE_H__ #define __MODE_H__ #include /*******************/ /* */ /* BUILD TARGETS */ /* */ /*******************/ /* Valid targets are: TARGET_EXE_STANDALONE Stand-alone executable inchi-1[.exe] TARGET_API_LIB Library (libinchi) for using InChI API described in inchi_api.h TARGET_EXE_USING_API Executable (INCHI_MAIN) which uses API library (e.g., libinchi.dll) TARGET_LIB_FOR_WINCHI library for wInChI Select and uncomment one from the list below. */ /* #define TARGET_EXE_STANDALONE 1 */ #define TARGET_API_LIB /* #define TARGET_EXE_USING_API */ /* #define TARGET_LIB_FOR_WINCHI 1 */ /****************************/ /* */ /* BUILD OPTIONS/FEATURES */ /* */ /****************************/ /* Possible options are: BUILD_LINK_AS_DLL Link library as a Win32 DLL or to eliminate stricmp duplication (use with TARGET_API_LIB or TARGET_EXE_USING_API) BUILD_WITH_ENG_OPTIONS Expose engineering options BUILD_WITH_AMI Turns on AMI (Allow Multiple Inputs) mode for standalone executable Select and uncomment whichever are necessary from the list below. */ /* #define BUILD_LINK_AS_DLL */ /* #define BUILD_WITH_ENG_OPTIONS 1 */ #ifndef BUILD_WITH_AMI /* this allows BUILD_WITH_AMI be #defined in a makefile */ /* #define BUILD_WITH_AMI 1 */ #endif /* NB: AMI mode is only for stand-alone executable */ #ifndef TARGET_EXE_STANDALONE #ifdef BUILD_WITH_AMI #undef BUILD_WITH_AMI #endif #endif /* CML input is not supported started from v. 1.04 */ /* set ADD_CMLPPP to zero to override possble makefile define */ #define ADD_CMLPP 0 #if 0 /* obsolete */ #ifndef ADD_CMLPP /* this allows ADD_CMLPP be #defined in a makefile */ #define ADD_CMLPP 1 #endif #if ( ADD_CMLPP == 1 ) #ifdef USE_CMLPPDLL /* 1200 is VC++ 6.0 version, 1300 is VC++ .NET; USE_CMLPPDLL may be #defined in a makefile*/ #if ( defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1200 ) #define MSC_DELAY_LOAD_CMLPPDLL #endif #endif #endif #endif /*****************************/ /* */ /* COMPILE OPTIONS/FEATURES */ /* */ /*****************************/ /* Possible options are: COMPILE_ANSI_ONLY Unconditionally force ANSI-89 C, no Win32 specific code COMPILE_ADD_NON_ANSI_FUNCTIONS Use with COMPILE_ANSI_ONLY to add stricmp(), etc., see util.c COMPILE_ALL_CPP allow C++ compilation/linkage of functions prototyped in .h files MS VC compiler pragmas Select and uncomment whichever are necessary from the list below. */ /* #define COMPILE_ANSI_ONLY */ #if ( !defined(_MSC_VER) || defined(TARGET_API_LIB)) /* non-Microsoft GNU C, BCC, etc. compilers */ #ifndef COMPILE_ANSI_ONLY #define COMPILE_ANSI_ONLY #endif #endif #ifdef COMPILE_ANSI_ONLY /*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ #endif /* #define COMPILE_ALL_CPP */ #ifdef _MSC_VER /* ========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== C4706: assignment within conditional expression C4127: conditional expression is constant C4244: '=' : conversion from 'int ' to '???', possible loss of data C4267: '=' : conversion from 'size_t' to 'int', possible loss of data C4701: local variable '???' may be used without having been initialized (removed) C4514: unreferenced inline/local function has been removed (C++) C4100: 'identifier' : unreferenced formal parameter C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information C4996: 'identifier' was declared deprecated ======================================================================== */ #pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) #endif /* TARGET_ID_STRING */ #define TARGET_ID_STRING ", Software version 1.04 (API Library) Build of September 9, 2011" #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /*********************/ /* */ /* INCHI ALGORITHM */ /* */ /*********************/ #define INCHI_VERSION "1" #if 0 /* obsolete */ /*#define INCHI_VERSION "0.9Beta" */ /*#define INCHI_VERSION "0.91Beta" */ /* 10-10-2002: sent to Jonathan Goodman */ /*#define INCHI_VERSION "0.92Beta" */ /* 11-15-2002: added Hill notation; sent to S.Heller & S.Stein */ /*#define INCHI_VERSION "0.93Beta" */ /* 12-09-2002: Fixed isotopic canon. bug & chiralanes; sent to S.Heller & A. McNaught */ /*#define INCHI_VERSION "0.931Beta" */ /* Non-BNS without salts released to PMR 01-2003 */ /*#define INCHI_VERSION "0.932Beta" */ /* Released to CAS 04-01-2003: * - Improved taut. definitions as compared to 01-2003; * - fixed bug: non-isotopic components' stereo missing from isotopic stereo * - fixed bug: couldn't properly read Unix files (EOL = LF instead of CR/LF) * (effective only for MS VC++, Borland and MinGW/GCC compiles that accept "rb" mode of fopen) * DJGPP/GCC does not seem to need this fix. */ /*==== Release version ===*/ /*#define INCHI_VERSION "0.94Beta" */ /* 02-27-2003: Balanced network search to find alt paths and non-stereo bonds; Implemented salts disconnection; added (-) to taut groups */ /*#define INCHI_VERSION "1.12Beta" */ /* 1.12: 07-06-2004: sort order: No H formula,..; Pointed end stereo ON, Aggressive (de)protonation OFF */ /* 1.11: 05-19-2004: annotated plain text output, fixed bugs */ /* 1.1: 04-08-2004: variable protonation version */ /* 1.01: 12-23-2003 protected bonds, isotopic canonicalization in GetBaseCanonRanking() */ /* 1.02: 01-26-2004 fixed new isotopic tgroup canon bug, molfile merge bug */ /*#define INCHI_VERSION "1.0RC"*/ /* 02-07-2005 v1.0 Release Candidate */ #endif #define INCHI_NAME "InChI" #if 0 /* obsolete */ #define INCHI_REC_NAME "ReChI" #endif #define INCHI_NAM_VER_DELIM "=" #ifdef _WIN32 #define INCHI_OPTION_PREFX '/' #define INCHI_PATH_DELIM '\\' #else #define INCHI_OPTION_PREFX '-' #define INCHI_PATH_DELIM '/' #endif #define INCHI_ALT_OPT_PREFIX '-' #define INCHI_ACD_LABS_PREFIX '-' #define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ #ifndef bRELEASE_VERSION #define bRELEASE_VERSION 0 /* 0=> debug version */ #endif /* display (non-canonical) c-groups, display orig at numbers */ #if ( bRELEASE_VERSION == 1 ) #define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ #define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ #else #define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ #define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ #endif #if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) #define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT #endif /* BUG FIXES */ /**************************/ /* bug fixes in v1.00 */ /**************************/ #define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ #define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ #define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ #define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ #define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ #define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ #define FIX_READ_AUX_MEM_LEAK 1 #define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ #define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ #define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ /*******************************/ /* bug fixes in post-v1.00 */ /*******************************/ #define FIX_ODD_THINGS_REM_Plus_BUG 0 #define FIX_N_MINUS_NORN_BUG 0 #define FIX_CANCEL_CHARGE_COUNT_BUG 0 #define FIX_2D_STEREO_BORDER_CASE 0 #define FIX_REM_ION_PAIRS_Si_BUG 0 #define FIX_STEREO_SCALING_BUG 0 #define FIX_EMPTY_LAYER_BUG 0 #define FIX_EITHER_DB_AS_NONSTEREO 0 #define FIX_BOND23_IN_TAUT 0 #define FIX_TACN_POSSIBLE_BUG 0 #define FIX_KEEP_H_ON_NH_ANION 0 #define FIX_AVOID_ADP 0 /* may change InChI */ #define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ /* changes InChI for isothiocyanate */ #define FIX_CPOINT_BOND_CAP2 0 /*******************************/ /* bug fixes in post-v1.02b */ /*******************************/ #define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ #define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ #define FIX_DALKE_BUGS 1 #define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ #define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ #define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ #define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ /* (missed repeating /s in FI after F for multi-component case) */ #define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ /* fix bug: in some cases (dependent on ordering numbers), moving a charge from terminal H to heavy atom resulted in neutralizing H but not adjusting charge of heavy atom */ #define FIX_AROM_RADICAL 1 /* (2011-05-09) 1=> Fix bug which leads for different InChI */ /* on atomic permitations for systems containing radical at */ /* atom in aromatic ring */ #if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) #define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ #else #define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ #endif #define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ /**************************/ /* additions to v1.00 */ /**************************/ #define FIX_ADJ_RAD 0 #define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ #define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ #define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ #ifdef TARGET_LIB_FOR_WINCHI #define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ #else #define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ #endif /****************************************************/ /* disabled extra external calls to InChI algorithm */ /****************************************************/ #define INCLUDE_NORMALIZATION_ENTRY_POINT 0 /**************************/ /* Normalization settings */ /**************************/ /* post version 1 features */ #define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ #define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ /* v.1.04 : still experimental but may be exposed (set to 1) */ #define UNDERIVATIZE 0 /* split to possible underivatized fragments */ #define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ /* post-2004-04-27 features */ #define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ #define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ #define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ /* current new (with respect to v1.12 Beta) preprocessing */ #define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ #define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ #define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ #define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ #define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ /* for testing */ #define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) * 1=> testing mode only: remove ion pairs from the original structure * to save the changes in the output Molfile (/OutputSDF) or AuxInfo * NIP=No Ion Pairs */ /* salts treatment */ #define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ #define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & * remove unreachable, * 0=> old version: test only before merging into one t-group */ #define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if * no negative charge(s) present */ #define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ #define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ #define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ #define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ #define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ #define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ #define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ #define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ /* recently added -- begin */ #define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and * other possible endpoints */ #define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ #define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ #define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during * 'hard' proton removal */ #define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ /* recently added -- end */ #define DISCONNECT_METALS 1 /* make main layer disconnected */ #define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. * compound disconnection */ #define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ #define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected * structure that are same as in the connected one */ #define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or * initial struct. output */ #define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; * 0=> separate output */ #define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ /* constants and array sizes */ #define INCHI_NUM 2 /* = array size; member indexes: */ #define INCHI_BAS 0 /* 0 => disconnected or normal */ #define INCHI_REC 1 /* 1 => reconnected */ #define TAUT_NUM 2 /* = array size; member indexes: */ #define TAUT_NON 0 /* 0 => normal structure */ #define TAUT_YES 1 /* 1 => tautomeric */ #define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ #define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ /* INChI output modes */ #define OUT_N1 0 /* non-tautomeric only */ #define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ #define OUT_NT 2 /* only non-taut representations of tautomeric */ #define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; separately output non-taut representations of tautomeric if present */ #define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ /* OUT_TN = OUT_T1 + OUT_NT */ /* torture test */ #define TEST_RENUMB_ATOMS 0 /* 1 => heavy duty test by multiple renumbering of atoms */ #define TEST_RENUMB_NEIGH 1 /* 1 => randomly permutate neighbors */ #define TEST_RENUMB_SWITCH 0 /* 1 => display & output another (different) picture */ #define TEST_RENUMB_ATOMS_SAVE_LONGEST 0 /* 1 => save the component with largest processing time into the problem file */ /* stereo */ #define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ #define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ #define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ #define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ #define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ /* (of the tested atom) has ill-defined stereo parity and another has any */ /* stereo parity then set the parity of the tested atom to ill-defined value. */ #define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ /* 0=> allow other definitions (below) to be active */ #define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ /* more stereo settings */ #define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ #define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ #define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ #define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ #define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ #define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer values, avg bond len=20 */ /* recent stereo */ #define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ #define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ #define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ #define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ /* temporary fix */ #define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds its effect on valence is same as the effect of a triplet */ /* defug: find structures where canonical partition is different from equitable */ #define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ /* Debug: definitions for the extraction of the structures to the problem file */ /* definition of the flags for structure extraction to the problem file (for debugging and non-standard searching) */ #define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 #define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 #define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 #define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 #define EXTR_REMOVE_PARITY_WARNING 0x000010 #define EXTR_SALT_WAS_DISCONNECTED 0x000020 #define EXTR_SALT_PROTON_MOVED 0x000040 #define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 #define EXTR_METAL_WAS_DISCONNECTED 0x000100 #define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 #define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ #define EXTR_UNUSUAL_VALENCES 0x000800 #define EXTR_HAS_METAL_ATOM 0x001000 #define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ #define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ #define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ #define EXTR_HAS_FEATURE 0x010000 /* found a feature */ #define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ #define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ /* define conditions of structure extraction to the problem file */ #define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ #define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ #define ENTITY_REFS_IN_XML_MESSAGES 1 /* 1=> replace ' " < > & in error/warning messages with xml entity references */ /* added tautomeric structures */ #define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ #define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ #define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ #define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ /* limitation on tautomerism detection: */ #define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check them to find more alt bonds (new) 1=> ignore and do not check (old mode) */ #define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ /* (except pyrazole, where is no tautomeric attachment) */ /* 0=> allow taut. attachments from same ring system. Default=1 */ #define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ /* Needed for 5- and 6-member ring tautomers and in other places */ #define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ #define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ #define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ /* instead of ranks */ /* consistency */ #if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) #undef bOUTPUT_ONE_STRUCT_TIME #define bOUTPUT_ONE_STRUCT_TIME 0 #endif /* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ #if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) #ifdef FIND_RING_SYSTEMS #undef FIND_RING_SYSTEMS #endif #define FIND_RING_SYSTEMS 1 #endif /* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ #if ( FIND_RING_SYSTEMS != 1 ) #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) #undef FIND_RINS_SYSTEMS_DISTANCES #define FIND_RINS_SYSTEMS_DISTANCES 0 #endif #endif /* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ #if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) #if ( USE_DISTANCES_FOR_RANKING == 1 ) #undef USE_DISTANCES_FOR_RANKING #define USE_DISTANCES_FOR_RANKING 0 #endif #if ( DISPLAY_RING_SYSTEMS == 1 ) #undef DISPLAY_RING_SYSTEMS #define DISPLAY_RING_SYSTEMS 0 #endif #endif #if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) #define TAUT_OTHER 1 #else #define TAUT_OTHER 0 #endif #define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ /* then add "down" hydrogen to obtain sterecenter oparity */ /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ /* are in XY plane */ #define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ /* even though it has adjacent tautomeric atom(s) */ #define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ #if ( DISCONNECT_SALTS == 1 ) #define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ #else #define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ #endif #if ( REMOVE_TGROUP_CHARGE == 1 ) #define INCHI_T_NUM_MOVABLE 1 #else #define INCHI_T_NUM_MOVABLE 2 #endif /******************************************/ /* define canonicalization modes here */ /******************************************/ #define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ #define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ #define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in taut H-fixed canonicalization; 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ /************************ questionable behavior ************************/ #define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ /* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ #define CMODE_CT 0x000001 #define CMODE_ISO 0x000002 #define CMODE_ISO_OUT 0x000004 /* obsolete ? */ #define CMODE_STEREO 0x000008 #define CMODE_ISO_STEREO 0x000010 #define CMODE_TAUT 0x000020 #define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ #define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ #define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ /* new 10-10-2003 */ #define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ #define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ #define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ #define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ /* end of 10-10-2003 */ /* external definitions */ #define CANON_MODE_CT (CMODE_CT) #define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) #define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) #define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) #define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) #define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ /************************************************* * from d_norm.c */ /* implemented definitions for CT_ATOMID */ #define CT_ATOMID_DONTINCLUDE 1 #define CT_ATOMID_IS_INITRANK 2 #define CT_ATOMID_IS_CURRANK 3 /*************************************** * canonicalization settings I ***************************************/ #define CANON_TAUTOMERS 1 /* 1=> process tautomers */ #define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ #define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ #define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ #define CT_SMALLEST /* minimal CT */ #define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ #define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ #define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ #define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ /* dependent definitions due to settings */ #ifdef CT_SMALLEST #define CT_GREATER_THAN > #define CT_INITVALUE ~0 #define BEST_PARITY 1 /* odd */ #define WORSE_PARITY 2 #else #define CT_GREATER_THAN < #define CT_INITVALUE 0 #define BEST_PARITY 2 /* even */ #define WORSE_PARITY 1 #endif #ifdef CT_NEIGH_SMALLER #define CT_NEIGH_SMALLER_THAN < #else #define CT_NEIGH_SMALLER_THAN > #endif /* verify corectness of dependent settings */ #if !defined( CT_ATOMID ) #error You have to #define CT_ATOMID #else #if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) #error CT_DELIMITER should be #defined if CT_ATOMID is not included #endif #endif /*************************************** * canonicalization settings II ***************************************/ /* from extr_ct.h */ #define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ #define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ #define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ /* 0 => include arom bonds parities according to */ /* ADD_6MEMB_AROM_BOND_PARITY definition */ #if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) #define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ /* 0 => only those arom bonds which do not belong to */ /* 6-member arom rings are stereo bonds */ #else #define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ #endif #define CML_NUM_AT_IN_ATREF4 4 #define MAX_NUM_STEREO_BONDS 3 #define MAX_NUM_STEREO_BOND_NEIGH 3 #define MIN_NUM_STEREO_BOND_NEIGH 2 #define MAX_NUM_STEREO_ATOM_NEIGH 4 #define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ #if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ #ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS #undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS #define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 #endif #ifdef EXCL_ALL_AROM_BOND_PARITY #undef EXCL_ALL_AROM_BOND_PARITY #define EXCL_ALL_AROM_BOND_PARITY 1 #endif #ifdef ADD_6MEMB_AROM_BOND_PARITY #undef ADD_6MEMB_AROM_BOND_PARITY #define ADD_6MEMB_AROM_BOND_PARITY 0 #endif #endif /* } ONLY_DOUBLE_BOND_STEREO */ /* dependent definitions due to settings */ #if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) #undef DOUBLE_BOND_NEIGH_LIST #define DOUBLE_BOND_NEIGH_LIST 0 #endif /************************************* * Drawing */ #define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ /******************************************************/ /* C O M M O N D E F I N I T I O N S */ /******************************************************/ /* input bTautFlags flags */ #define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ #define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ #define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ #define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ #define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ #define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ #define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ #define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ TG_FLAG_TEST_TAUT__SALTS| \ TG_FLAG_TEST_TAUT2_SALTS| \ TG_FLAG_MERGE_TAUT_SALTS) #define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ #define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ #define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ #define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ #define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ #define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ #define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ #if ( FIX_ADJ_RAD == 1 ) #define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ #endif #define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ #define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ #define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ #define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ #define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ #define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ /*^^^ FB2 */ #define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ #define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ /* output bTautFlags flags */ #define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ #define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 #define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 #define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ #define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 #define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ #define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 #define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ #define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ TG_FLAG_TEST_TAUT2_SALTS_DONE | \ TG_FLAG_MERGE_TAUT_SALTS_DONE ) #define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ #define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ #define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ #define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ #define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ #define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ #define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ #define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ #if ( FIX_ADJ_RAD == 1 ) #define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 #endif #if ( READ_INCHI_STRING == 1 ) #define READ_INCHI_OUTPUT_INCHI 0x00000001 #define READ_INCHI_SPLIT_OUTPUT 0x00000002 #define READ_INCHI_KEEP_BALANCE_P 0x00000004 #define READ_INCHI_TO_STRUCTURE 0x00000008 #endif /*********/ /* */ /* I/O */ /* */ /*********/ typedef struct tagOutputString { char *pStr; int nAllocatedLength; int nUsedLength; int nPtr; } INCHI_OUTPUT; typedef struct tagOutputStream { /* output is directed either to resizable string buffer: */ INCHI_OUTPUT s; /* or to the plain file: */ FILE* f; int type; } INCHI_IOSTREAM; /* INCHI_IOSTREAM.type values */ #define INCHI_IOSTREAM_NONE 0 #define INCHI_IOSTREAM_STRING 1 #define INCHI_IOSTREAM_FILE 2 /***********/ /* */ /* DEBUG */ /* */ /***********/ #if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) /* debug: memory leaks tracking */ #ifndef TARGET_LIB_FOR_WINCHI #ifndef DO_NOT_TRACE_MEMORY_LEAKS #define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ #else #define TRACE_MEMORY_LEAKS 0 #endif #else #define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ #endif #else /* not MSC and not Debug */ #define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ #endif /* memory leaks tracking */ #define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ #if ( TRACE_MEMORY_LEAKS == 1 ) #ifdef _DEBUG #define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) #define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) #define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) #ifdef TARGET_EXE_USING_API /* INChI_MAIN specific */ #define e_inchi_malloc(a) inchi_malloc(a) #define e_inchi_calloc(a,b) inchi_calloc(a,b) #define e_inchi_free(a) inchi_free(a) #endif /*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ #include /* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ #ifdef CHECK_WIN32_VC_HEAP /* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ #undef INCHI_HEAPCHK #define INCHI_HEAPCHK \ do { \ int tmp = _crtDbgFlag; \ _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ _ASSERT( _CrtCheckMemory( ) ); \ _crtDbgFlag = tmp; \ } while(0); /* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ /* #include #define INCHI_HEAPCHK \ do {\ int heapstatus = _heapchk(); \ _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ } while(0); */ #endif #else #undef TRACE_MEMORY_LEAKS #define TRACE_MEMORY_LEAKS 0 #endif /* _DEBUG */ #endif /* TRACE_MEMORY_LEAKS */ /***********/ /* */ /* ALLOC */ /* */ /***********/ #ifdef TARGET_EXE_USING_API /* INChI_MAIN specific */ #ifndef inchi_malloc #define inchi_malloc e_inchi_malloc #endif #ifndef inchi_calloc #define inchi_calloc e_inchi_calloc #endif #ifndef inchi_free #define inchi_free e_inchi_free #endif #ifndef e_inchi_malloc #define e_inchi_malloc malloc #endif #ifndef e_inchi_calloc #define e_inchi_calloc calloc #endif #ifndef e_inchi_free #define e_inchi_free(X) do{ if(X) free(X); }while(0) #endif #else /* not TARGET_EXE_USING_API */ #ifndef inchi_malloc #define inchi_malloc malloc #endif #ifndef inchi_calloc #define inchi_calloc calloc #endif #ifndef inchi_free #define inchi_free(X) do{ if(X) free(X); }while(0) #endif #endif /* TARGET_EXE_USING_API */ /* allocation/deallocation */ #define USE_ALLOCA 0 #if ( USE_ALLOCA == 1 ) #define qmalloc(X) _alloca(X) #define qfree(X) do{(X)=NULL;}while(0) #else #define qmalloc(X) inchi_malloc(X) #define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) #endif #if ( defined(_MSC_VER) && _MSC_VER >= 800 ) #define fast_alloc(X) _alloca(X) #define fast_free(X) #else #define fast_alloc(X) inchi_malloc(X) #define fast_free(X) inchi_free(X) #endif #define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) /* rellocation */ #define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ do { \ if( (LEN1) <= (LEN2) ) {\ PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ if ( newPTR1 && newPTR2 ) { \ if ( (PTR1) && (LEN1) > 0 ) \ (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ if ( (PTR2) && (LEN1) > 0 ) \ (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ if ( PTR1 ) \ inchi_free(PTR1); \ if ( PTR2 ) \ inchi_free(PTR2); \ (PTR1) = newPTR1; \ (PTR2) = newPTR2; \ (LEN1) = (LEN2); \ (ERR) = 0; \ } else { \ (ERR) = 1; \ } \ } else { (ERR) = 0; } \ } while(0) #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __MODE_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/runichi.c000066400000000000000000006030031271037650300233600ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include /* #include */ #include #include #include "mode.h" /* moved from below, suggestion by David Mosenkis */ #include "ichitime.h" #ifndef COMPILE_ANSI_ONLY #include #endif #include "inpdef.h" #include "ichi.h" #include "strutil.h" #include "util.h" #include "ichidrp.h" #include "ichierr.h" #include "ichimain.h" #include "extr_ct.h" #include "ichitaut.h" #include "ichi_io.h" #ifdef TARGET_LIB_FOR_WINCHI #include "ichi_lib.h" #endif #include "inchi_api.h" #include "ichicomp.h" #if ( ADD_CMLPP == 1 ) #include "readcml.hpp" #include "debug.h" #endif /* for DisplayTheWholeStructure() */ #define COMP_ORIG_0_MAIN 0x0001 #define COMP_ORIG_0_RECN 0x0002 #define COMP_PREP_0_MAIN 0x0004 #define COMP_PREP_0_RECN 0x0008 #define COMP_ORIG_1_MAIN 0x0010 #define COMP_ORIG_1_RECN 0x0020 /* local prototypes */ int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct); int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd); int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ); int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ); int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ); int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ); int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ); /* callback */ int (*ConsoleQuit)(void) = NULL; /* Console user issued CTRL+C etc. */ int (*UserAction)(void) = NULL; /* callback */ #ifdef TARGET_LIB_FOR_WINCHI void (*FWPRINT) (const char * format, va_list argptr )=NULL; void (*DRAWDATA) ( struct DrawData * pDrawData) = NULL; int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ) = NULL; struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ) = NULL; #endif #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ /************************************************/ /* atoms renumbering -- for testing only */ /************************************************/ typedef struct tagRenumbData { PINChI2 ren_INChI2[1]; PINChI_Aux2 ren_INChI_Aux[1]; INP_ATOM_DATA orig_inp_cur_data; INP_ATOM_DATA saved_inp_cur_data; #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) INP_ATOM_DATA longest_inp_cur_data; #endif INP_ATOM_DATA ren_inp_norm_data1, ren_inp_norm_data2; INP_ATOM_DATA *ren_inp_norm_data[2]; int ren_counter; int num_taut, num_non_taut, num_taut0, num_non_taut0; AT_RANK *new_ord; int nRet2, c1, c2, nComp, bRenumbErr; unsigned long ulCurTimeNorm0, ulCurTimeCanon0, ulCurTimeNorm1, ulCurTimeCanon1; unsigned long ulCurTimeNorm, ulCurTimeCanon, ulMaxTimeNorm, ulMaxTimeCanon; unsigned long ulMaxTime, ulCurTime, ulCurTime0, ulCurTime1; #if ( bRELEASE_VERSION == 0 ) int bExtract; #endif } RENUMB_DATA; int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ); int RenumberingTestUninit( RENUMB_DATA *pRenumbData ); int RenumberingTest( PINChI2 *pICh, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, int i, long num_inp, NORM_CANON_FLAGS *pncFlags); /* int RenumberingTest( INChI *pINChI[][TAUT_NUM], INChI_Aux *pINChI_Aux[][TAUT_NUM], int iINChI, RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, int i, long num_inp); */ #endif /* } TEST_RENUMB_ATOMS */ #ifndef COMPILE_ANSI_ONLY /********************************************************************/ void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, INCHI_MODE nMode, int bShowIsotopic, int indx ) { TBL_DRAW_PARMS *tdp = sdp->tdp; char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; int i, j; INChI_Stereo *Stereo; int bShowTaut = (cur_INChI && cur_INChI[indx]->lenTautomer > 0)? 1 : 0; #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) int bRelRac = 0 != (nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO )); #endif if ( !cur_INChI || !cur_INChI_Aux ) { sdp->tdp->bDrawTbl = 0; sdp->bOrigAtom = 1; return; } /* Displayed */ ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; /* ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; */ i = indx; if ( cur_INChI[i] ) { Stereo = bShowIsotopic? cur_INChI[i]->StereoIsotopic : cur_INChI[i]->Stereo; } else { Stereo = NULL; } #if ( REL_RAC_STEREO_IGN_1_SC == 1 ) if ( Stereo && ( 0 < Stereo->nNumberOfStereoBonds || 0 < Stereo->nNumberOfStereoCenters-bRelRac ) ) { ReqShownFound[ilSHOWN][itSTEREO] = 'S'; if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { ReqShownFound[ilSHOWN][itSTEREO] = '\0'; } else if ( Stereo->nNumberOfStereoCenters >= 2 ) { ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ } } #else /* REL_RAC_STEREO_IGN_1_SC == 0 */ if ( Stereo && ( Stereo->nNumberOfStereoBonds || Stereo->nNumberOfStereoCenters ) ) { ReqShownFound[ilSHOWN][itSTEREO] = 'S'; if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { /* if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { ReqShownFound[ilSHOWN][itSTEREO] = '\0'; } else if ( Stereo->nNumberOfStereoCenters >= 2 ) { */ ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ /* } */ } #endif /* REL_RAC_STEREO_IGN_1_SC */ } else { ReqShownFound[ilSHOWN][itSTEREO] = '\0'; } /* ReqShownFound[ilSHOWN][itSTEREO] = (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) : (cur_INChI[i] && cur_INChI[i]->Stereo && (cur_INChI[i]->Stereo->nNumberOfStereoBonds || cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) ) ? 'S':'\0'; */ /* remove zeroes between chars */ for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; } } i = j; for ( ; i < TDP_NUM_PAR; i ++ ) { ReqShownFound[ilSHOWN][i] = '\0'; } sdp->tdp->bDrawTbl = j? 1 : 0; sdp->bOrigAtom = 0; } /********************************************************************/ void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ) { TBL_DRAW_PARMS *tdp = sdp->tdp; char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; int i, j; /* Displayed */ ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; /* ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; */ if ( StereoFlags & INF_STEREO ) { ReqShownFound[ilSHOWN][itSTEREO] = 'S'; if ( (StereoFlags & INF_STEREO_INV) && ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { if (StereoFlags & (INF_STEREO_REL | INF_STEREO_RAC) ) { ReqShownFound[ilSHOWN][itSTEREO] = 's'; } else { ReqShownFound[ilSHOWN][itSTEREO] = '\0'; /* shown Inverted stereo */ } } } else { ReqShownFound[ilSHOWN][itSTEREO] = '\0'; } /* ReqShownFound[ilSHOWN][itSTEREO] = (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) : (cur_INChI[i] && cur_INChI[i]->Stereo && (cur_INChI[i]->Stereo->nNumberOfStereoBonds || cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) ) ? 'S':'\0'; */ /* remove zeroes between chars */ for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; } } i = j; for ( ; i < TDP_NUM_PAR; i ++ ) { ReqShownFound[ilSHOWN][i] = '\0'; } sdp->tdp->bDrawTbl = j? 1 : 0; sdp->bOrigAtom = 0; } #endif /* IchiParm.c was here */ /*******************************************************************/ #ifndef COMPILE_ANSI_ONLY #ifndef TARGET_LIB_FOR_WINCHI /*******************************************************************/ int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int j /*bTautomeric*/, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) { INF_ATOM_DATA inf_data = {NULL,}; int err = -1; if ( CreateInfoAtomData( &inf_data, num_at, 1 ) ) { err = 0; FillOutInfAtom( at, &inf_data, num_at, num_removed_H, bAdd_DT_to_num_H, nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, cur_INChI?cur_INChI[j]:NULL, cur_INChI_Aux?cur_INChI_Aux[j]:NULL, bAbcNumbers, nMode); FillTableParms( &dp->sdp, cur_INChI, cur_INChI_Aux, nMode, bIsotopic, j ); err = DisplayInputStructure( szTitle, at, &inf_data, num_at, dp ); FreeInfoAtomData( &inf_data ); } return err; } /*******************************************************************/ int DisplayCompositeStructure( COMP_ATOM_DATA *composite_norm_data, int bIsotopic, int bTautomeric, PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) { INF_ATOM_DATA inf_data; int err = -1, ret; memset( &inf_data, 0, sizeof(inf_data) ); if ( CreateInfoAtomData( &inf_data, (composite_norm_data+bTautomeric)->num_at, (composite_norm_data+bTautomeric)->num_components ) ) { ret = FillOutCompositeCanonInfAtom(composite_norm_data, &inf_data, bIsotopic, bTautomeric, pINChI2, pINChI_Aux2, bAbcNumbers, nMode); if ( !ret ) { goto exit_function; /* error */ } if ( bTautomeric == TAUT_INI ) { /* FillOutInfAtom( (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, (composite_norm_data+bTautomeric)->num_removed_H, bAdd_DT_to_num_H, (composite_norm_data+bTautomeric)->nNumRemovedProtons, (composite_norm_data+bTautomeric)->nNumRemovedProtonsIsotopic, bIsotopic, NULL, NULL, bAbcNumbers, nMode); */ ; } else { /* real check for tautomeric components 02-04-2005 */ int m, nNumTautComponents = 0; if ( 1 == bTautomeric ) { for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { if ( !pINChI2[m][TAUT_YES] ) continue; if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) nNumTautComponents ++; } } FillCompositeTableParms( &dp->sdp, inf_data.StereoFlags, nMode, bIsotopic, nNumTautComponents ); } err = DisplayInputStructure( szTitle, (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, dp ); FreeInfoAtomData( &inf_data ); } exit_function: return err; } #endif #endif /************************************************/ const char *ErrMsg( int nErrorCode ) { const char *p; static char szErrMsg[64]; switch( nErrorCode ) { case 0: p = ""; break; case CT_OVERFLOW: p = "ARRAY OVERFLOW"; break; case CT_LEN_MISMATCH: p = "LENGTH_MISMATCH"; break; case CT_OUT_OF_RAM: p = "Out of RAM"; break; case CT_RANKING_ERR: p = "RANKING_ERR"; break; case CT_ISOCOUNT_ERR: p = "ISOCOUNT_ERR"; break; case CT_TAUCOUNT_ERR: p = "TAUCOUNT_ERR"; break; case CT_ISOTAUCOUNT_ERR: p = "ISOTAUCOUNT_ERR"; break; case CT_MAPCOUNT_ERR: p = "MAPCOUNT_ERR"; break; case CT_TIMEOUT_ERR: p = "Time limit exceeded"; break; case CT_ISO_H_ERR: p = "ISO_H_ERR"; break; case CT_STEREOCOUNT_ERR: p = "STEREOCOUNT_ERR"; break; case CT_ATOMCOUNT_ERR: p = "ATOMCOUNT_ERR"; break; case CT_STEREOBOND_ERROR: p = "STEREOBOND_ERR"; break; case CT_USER_QUIT_ERR: p = "User requested termination"; break; case CT_REMOVE_STEREO_ERR: p = "REMOVE_STEREO_ERR"; break; case CT_CALC_STEREO_ERR: p = "CALC_STEREO_ERR"; break; case CT_STEREO_CANON_ERR: p = "STEREO_CANON_ERR"; break; case CT_CANON_ERR: p = "CANON_ERR"; break; case CT_WRONG_FORMULA: p = "Wrong or missing chemical formula"; break; /*case CT_CANON_ERR2: p = "CT_CANON_ERR2"; break;*/ case CT_UNKNOWN_ERR: p = "UNKNOWN_ERR"; break; case BNS_RADICAL_ERR: p = "Cannot process free radical center"; break; case BNS_ALTBOND_ERR: p = "Cannot process aromatic bonds"; break; default: if ( nErrorCode > CT_UNKNOWN_ERR ) { sprintf( szErrMsg, "No description(%d)", nErrorCode ); p = szErrMsg; } else { sprintf( szErrMsg, "UNKNOWN_ERR(%d)", CT_UNKNOWN_ERR - nErrorCode ); p = szErrMsg; } break; } return p; } /***********************************************************************************/ #ifndef COMPILE_ANSI_ONLY /* { */ /***********************************************************************************/ int SaveEquComponentsInfoAndSortOrder ( int iINChI, INCHI_SORT *pINChISort[TAUT_NUM], int *num_components, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1], int bCompareComponents ) { int nRet = 0, i, k, nNumDeleted; /* equivalent components and sorting order */ /* bCompareComponents: bit = 1 => compare */ /* bit = 2 => compare non-isotopic */ /* bit = 4 => compare non-tautomeric */ int bCompareIsotopic, bCompareTaut, bCompareAlt; ORIG_ATOM_DATA *inp_data = NULL; if ( num_components[iINChI] <= 1 ) return 0; #ifdef TARGET_LIB_FOR_WINCHI if ( !DRAWDATA ) return 0; #endif if ( !(bCompareComponents & CMP_COMPONENTS) ) return 0; bCompareIsotopic = !(bCompareComponents & CMP_COMPONENTS_NONISO); bCompareTaut = (bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; bCompareAlt = ALT_TAUT(bCompareTaut); if ( num_components[iINChI] > 1 ) { if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { inp_data = prep_inp_data+iINChI; } else if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { inp_data = orig_inp_data; } else { inp_data = NULL; } if ( inp_data && !inp_data->nEquLabels && !prep_inp_data[iINChI].nSortedOrder ) { int i1, i2, nSet; AT_NUMB nAtNo; AT_NUMB nNumAtoms = (AT_NUMB)inp_data->num_inp_atoms; if ( (prep_inp_data[iINChI].nSortedOrder = (AT_NUMB *)inchi_calloc(num_components[iINChI]+1, sizeof(prep_inp_data[0].nSortedOrder[0])))) { inp_data->nNumEquSets = 0; for ( i1 = 0, nSet = 0; i1 < num_components[iINChI]; i1 = i2 ) { nNumDeleted = (pINChISort[bCompareTaut][i1].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i1].pINChI[bCompareTaut]->bDeleted); for ( i2 = i1+1; i2 < num_components[iINChI]; i2 ++ ) { /* isotopic/non-isotopic comparison does not separate equivalent components */ if ( CompINChI2( pINChISort[bCompareTaut]+i1, pINChISort[bCompareTaut]+i2, bCompareTaut, bCompareIsotopic ) ) { break; } else { nNumDeleted += (pINChISort[bCompareTaut][i2].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i2].pINChI[bCompareTaut]->bDeleted); } } if ( i2 - i1 - nNumDeleted > 1 ) { if ( inp_data->nEquLabels || (inp_data->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms+1, sizeof(inp_data->nEquLabels[0]))) ) { nSet ++; /* found i2-i1 equivalent components && memory has been allocated */ for ( i = i1; i < i2; i ++ ) { INChI_Aux *pINChI_Aux; if (pINChISort[bCompareTaut][i].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i].pINChI[bCompareTaut]->bDeleted) continue; pINChI_Aux = (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut] && pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]->nNumberOfAtoms)? pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]: (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt] && pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]->nNumberOfAtoms)? pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]: (INChI_Aux *)NULL; if ( pINChI_Aux && pINChI_Aux->nOrigAtNosInCanonOrd ) { for ( k = 0; k < pINChI_Aux->nNumberOfAtoms; k ++ ) { if ( (nAtNo = pINChI_Aux->nOrigAtNosInCanonOrd[k]) && nAtNo <= nNumAtoms ) { inp_data->nEquLabels[nAtNo-1] = nSet; } } } } } else { return CT_OUT_OF_RAM; } } } nRet |= nSet? 1:0; } else { return CT_OUT_OF_RAM; } inp_data->nNumEquSets = nSet; /* output order */ prep_inp_data[iINChI].nSortedOrder[0] = 0; for ( i1 = 0; i1 < num_components[iINChI]; i1 ++ ) { prep_inp_data[iINChI].nSortedOrder[i1+1] = pINChISort[TAUT_YES][i1].ord_number+1; } #ifdef TARGET_LIB_FOR_WINCHI /* { */ if ( DRAWDATA && GET_DRAWDATA && inp_data->nNumEquSets > 0 && inp_data->nEquLabels ) { int nType = inp_data->bPreprocessed[iINChI]? COMPONENT_ORIGINAL_PREPROCESSED : COMPONENT_ORIGINAL; struct DrawData *pDrawData = GET_DRAWDATA( 0, nType, iINChI); if ( pDrawData && pDrawData->pWindowData && !pDrawData->pWindowData->nEquLabels ) { /* copy equivalence data from inp_data to pDrawData->pWindowData */ if ( inp_data->nEquLabels && (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, sizeof(inp_data->nEquLabels[0])))) { memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; pDrawData->pWindowData->nCurEquLabel = 0; } } } #endif /* } TARGET_LIB_FOR_WINCHI */ } } return nRet; } /************************************************************************************************/ int DisplayTheWholeCompositeStructure( INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int iINChI, PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1] ) { ORIG_ATOM_DATA *inp_data = NULL; int jj, j, k, err = 0, nNumIntermediateTaut = 0, bDisplayTaut; char szTitle[256]; int nNumTautComponents, m; int bCompareIsotopic = !(ip->bCompareComponents & CMP_COMPONENTS_NONISO); int bCompareTaut = (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; if ( ip->bCompareComponents & CMP_COMPONENTS ) { if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { inp_data = prep_inp_data+iINChI; } else if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { inp_data = orig_inp_data; } } /************************************************************************** * display from one up to 4 structure pictures-results for all components * * Enable buttons: * * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * * inp_norm_data[0]->bHasIsotopicLayer * * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * * inp_norm_data[1]->bHasIsotopicLayer * **************************************************************************/ for ( jj = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && jj <= TAUT_INI; jj ++ ) { /*for ( j = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && j <= TAUT_INI; j ++ )*/ j = (jj==0)? TAUT_NON : (jj==1)? TAUT_INI : (jj==2)? TAUT_YES : -1; if ( j < 0 ) continue; if ( composite_norm_data[j].bExists && composite_norm_data[j].num_components > 1 ) { bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !j)? -1 : j; nNumTautComponents = 0; if ( bDisplayTaut ) { /* find whether the structure is actually tautomeric */ for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { if ( !pINChI2[m][TAUT_YES] ) continue; if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) nNumTautComponents ++; } } for ( k = 0; k <= composite_norm_data[j].bHasIsotopicLayer && !sd->bUserQuitComponentDisplay; k ++ ) { /* added number of components, added another format for a single component case - DCh */ int bMobileH = (bDisplayTaut>0 && nNumTautComponents); sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", j == TAUT_INI? "Preprocessed":"Result for", num_inp, bMobileH? ", mobile H": bDisplayTaut==0?", fixed H":"", /*j? ", mobile H":", fixed H",*/ k? ", isotopic":"", SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); #ifndef TARGET_LIB_FOR_WINCHI /****** Display composite Result structure **************/ nNumIntermediateTaut += (j == TAUT_INI ); /* display TAUT_INI (preprocessed) only once */ if ( j != TAUT_INI || nNumIntermediateTaut == 1 ) { err = DisplayCompositeStructure( composite_norm_data, j==TAUT_INI? 1:k /* bIsotopic*/, j/*tautomeric*/, j==TAUT_INI? NULL:pINChI2, j==TAUT_INI? NULL:pINChI_Aux2, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); } if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { break; } if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && !sd->bUserQuitComponentDisplay && ((j == bCompareTaut || bCompareTaut && j == TAUT_INI) || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && (k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) ) { AT_NUMB nEquSet; int bDisplaySaved = ip->bDisplay; /****** Display Equ Sets of composite Result structure **************/ for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { sprintf( szTitle, "Equ set %d of %d, %s Structure #%ld%s%s.%s%s%s%s%s", nEquSet, inp_data->nNumEquSets, j == TAUT_INI? "Preprocessed":"Result for", num_inp, (bDisplayTaut>0 && nNumTautComponents)? ", mobile H": bDisplayTaut==0?", fixed H":"", /*j? ", mobile H":", fixed H",*/ k? ", isotopic":"", SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); ip->dp.nEquLabels = inp_data->nEquLabels; ip->dp.nCurEquLabel = nEquSet; ip->dp.nNumEquSets = inp_data->nNumEquSets; ip->bDisplay = 1; /* force display if it was not requested */ err = DisplayCompositeStructure( composite_norm_data, k, j, pINChI2, pINChI_Aux2, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); ip->dp.nEquLabels = NULL; ip->dp.nCurEquLabel = 0; ip->dp.nNumEquSets = 0; ip->bDisplay = bDisplaySaved; /* restore display option */ if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { break; } } } #else if(DRAWDATA && j <= TAUT_YES) { struct DrawData vDrawData; vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, pINChI2, pINChI_Aux2, ip->bAbcNumbers, &ip->dp, ip->nMode); /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ if( vDrawData.pWindowData != NULL ) { int nType; vDrawData.nComponent = 0; if( j == 0 ) nType = (k == 0) ? COMPONENT_BN: COMPONENT_BI; else nType = (k == 0) ? COMPONENT_TN: COMPONENT_TI; vDrawData.nType = nType; vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ vDrawData.szTitle = _strdup(szTitle); vDrawData.pWindowData->szTitle = _strdup(szTitle); if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && (j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && (k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) && (vDrawData.pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, sizeof(inp_data->nEquLabels[0])))) { memcpy( vDrawData.pWindowData->nEquLabels, inp_data->nEquLabels, inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); vDrawData.pWindowData->nNumEquSets = inp_data->nNumEquSets; vDrawData.pWindowData->nCurEquLabel = 0; } DRAWDATA(&vDrawData); } } else if(DRAWDATA && GET_DRAWDATA && j == TAUT_INI) { struct DrawData vDrawData; struct DrawData *pDrawData; if ( !(ip->bCompareComponents & CMP_COMPONENTS) || (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) || !k != !composite_norm_data[j].bHasIsotopicLayer ) { continue; } /* vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, pINChI2, pINChI_Aux2, ip->bAbcNumbers, &ip->dp, ip->nMode); */ vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, 1 /*k*/, j, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode); if( vDrawData.pWindowData != NULL ) { int nType = COMPONENT_ORIGINAL_PREPROCESSED; pDrawData = GET_DRAWDATA( 0, nType, iINChI); if ( pDrawData ) { FreeDrawData( pDrawData ); pDrawData->pWindowData = vDrawData.pWindowData; vDrawData.pWindowData = NULL; } else { pDrawData = &vDrawData; } /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ pDrawData->nComponent = 0; pDrawData->nType = nType; pDrawData->bReconnected = iINChI; /* 0=>main; 1=>reconnected */ pDrawData->szTitle = _strdup(szTitle); pDrawData->pWindowData->szTitle = _strdup(szTitle); if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && /*(j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) &&*/ /*(k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) &&*/ (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, sizeof(inp_data->nEquLabels[0])))) { memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; pDrawData->pWindowData->nCurEquLabel = 0; } if ( pDrawData == &vDrawData ) { DRAWDATA(pDrawData); /* there was no prepocessed structure */ } } } #endif } } } return err; } #endif /* }COMPILE_ANSI_ONLY */ /***********************************************************************************/ /* pINChI[INCHI_BAS] refers to either disconnected or original structure; */ /* num_components[INCHI_BAS] > 0 if there was input structure */ /***********************************************************************************/ /* pINChI[INCHI_REC] refers to the reconnected structure, */ /* and only if the input structure has been disconnected, that is,*/ /* num_components[INCHI_REC] > 0 */ /***********************************************************************************/ int SortAndPrintINChI(INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, INCHI_IOSTREAM *log_file, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], NORM_CANON_FLAGS *pncFlags, long num_inp, PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int *pSortPrintINChIFlags, unsigned char save_opt_bits) { INCHI_SORT *pINChISort[INCHI_NUM][TAUT_NUM]; int j, i, k, k1, ret, iINChI, max_num_components; INCHI_MODE nMode; int bDisconnectedCoord = (0 != (bTautFlagsDone[0] & TG_FLAG_DISCONNECT_COORD_DONE)); int bINChIOutputOptions0, bCurOption, bINChIOutputOptionsCur, bEmbedReconnected, bAnnInXmlBrackets; static const char szAnnHdr[] = "InChI ANNOTATED CONTENTS"; int ikflag = 0; ret = 1; for ( i = 0; i < INCHI_NUM; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { bTautFlags[i] |= pncFlags->bTautFlags[i][k]; bTautFlagsDone[i] |= pncFlags->bTautFlagsDone[i][k]; } } nMode = ip->nMode; if ( !(nMode & (REQ_MODE_BASIC|REQ_MODE_TAUT)) ) { nMode |= (REQ_MODE_BASIC|REQ_MODE_TAUT); } max_num_components = 0; for ( j = 0; j < INCHI_NUM; j ++ ) { if ( max_num_components < num_components[j] ) max_num_components = num_components[j]; } if ( max_num_components <= 0 ) max_num_components = 1; for ( j = 0, i = 0; j < INCHI_NUM; j ++ ) { if ( num_components[j] ) { for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { pINChISort[j][k1] = (INCHI_SORT *)inchi_calloc(max_num_components, sizeof(pINChISort[0][0][0]) ); i += !pINChISort[j][k1]; /* number of failed allocatons */ } } else { for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { pINChISort[j][k1] = NULL; /* keep BC happy */ } } } if ( i ) { ret = CT_OUT_OF_RAM; goto exit_function; } for ( j = 0; j < INCHI_NUM; j ++ ) { if ( !num_components[j] ) { continue; } iINChI = j; #if ( OUTPUT_CONNECTED_METAL_ONLY == 1 ) /* test: output connected as the only one INChI */ if ( INCHI_BAS == j && num_components[INCHI_REC] ) { j = INCHI_REC; } #endif /*j = INCHI_BAS; <- for debug only */ /* for only normal or disconnected coord compounds */ /* (j=0=INCHI_BAS => normal or disconnected, j=1=INCHI_REC => reconnected */ for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { for ( i = 0; i < num_components[j]; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { pINChISort[j][k1][i].pINChI[k] = pINChI[j][i][k]; pINChISort[j][k1][i].pINChI_Aux[k] = pINChI_Aux[j][i][k]; } pINChISort[j][k1][i].ord_number = i; } } /* sort component INChIs */ for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { switch ( k1 ) { case TAUT_NON: qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChINonTaut2 ); break; case TAUT_YES: qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChITaut2 ); break; } } #ifndef COMPILE_ANSI_ONLY /* find equivalent and wINChI display order; use requested in ip->bCompareComponents comparison */ ret = SaveEquComponentsInfoAndSortOrder ( iINChI, pINChISort[j], num_components, orig_inp_data, prep_inp_data, #if ( FIX_DALKE_BUGS == 1 ) composite_norm_data? composite_norm_data[j]:NULL, #else composite_norm_data[j], #endif ip->bCompareComponents ); if ( RETURNED_ERROR( ret ) ) { ret = 0; goto exit_function; } else { ret = 1; } #endif } if ( !( ip->bINChIOutputOptions & INCHI_OUT_PRINT_OPTIONS ) ) { /* prepare InChI from the structures obtained by reversing InChI for returning to the caller */ for ( j = 0; j < INCHI_NUM; j ++ ) { if ( !num_components[j] ) { continue; } /* pINChI[iINCHI][iComponent][bTaut] */ /* j = disconnected/connected */ /* k1 = sort order for Mobile or Fixed H */ k1 = TAUT_YES; /* in Mobile H order */ /* store components in Mobile H order */ for ( i = 0; i < num_components[j]; i ++ ) { if ( pINChISort[j][k1][i].pINChI[TAUT_NON] && !pINChISort[j][k1][i].pINChI[TAUT_YES] ) { /* make sure Mobile-H is always present */ for ( k = 0; k < TAUT_NUM; k ++ ) { pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[ALT_TAUT(k)]; pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[ALT_TAUT(k)]; } } else { for ( k = 0; k < TAUT_NUM; k ++ ) { pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[k]; pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[k]; } } } } } else { /* print inchi string(s) */ bINChIOutputOptions0 = ip->bINChIOutputOptions & ~INCHI_OUT_PRINT_OPTIONS; bEmbedReconnected = ip->bINChIOutputOptions & INCHI_OUT_EMBED_REC; for ( i = 0; i < 4; i ++ ) { switch( i ) { case 0: bCurOption = INCHI_OUT_XML; break; case 1: bCurOption = INCHI_OUT_PLAIN_TEXT; break; case 2: bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; break; case 3: bCurOption = INCHI_OUT_XML_TEXT_COMMENTS; break; default: continue; } if ( ip->bINChIOutputOptions & bCurOption ) { bAnnInXmlBrackets = 0; if ( i == 1 ) { ;/*bEmbedReconnected = 0;*/ } if ( i == 3 ) { bCurOption = INCHI_OUT_XML; /* xml output as annotation */ } bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; switch ( i ) { case 0: case 1: /* output INChI */ bINChIOutputOptionsCur |= bEmbedReconnected; break; case 2: case 3: /* output annotation */ bAnnInXmlBrackets = (i == 2 && (ip->bINChIOutputOptions & INCHI_OUT_XML )); if ( bAnnInXmlBrackets ) { inchi_ios_print( output_file, "\n<%s>\n", szAnnHdr ); } else { inchi_ios_print( output_file, "\n==== %s ====\n", szAnnHdr ); } bINChIOutputOptionsCur |= bEmbedReconnected; bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; break; default: continue; } #ifdef TARGET_LIB_FOR_WINCHI if ( ikflag==0 ) output_file->type = INCHI_IOSTREAM_STRING; #endif ret &= OutputINChI2(pStr, nStrLen, pINChISort, INCHI_BAS /*iINChI*/, pOrigStruct, bDisconnectedCoord, OUT_TN, bINChIOutputOptionsCur, 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), ip->bAbcNumbers, ip->bCtPredecessors, ip->bNoStructLabels, num_components, num_non_taut, num_taut, output_file, log_file, num_inp, ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, pSortPrintINChIFlags, save_opt_bits); if ( ret && !(bINChIOutputOptionsCur & INCHI_OUT_EMBED_REC) ) { ret &= OutputINChI2(pStr, nStrLen, pINChISort, INCHI_REC /*iINChI*/, pOrigStruct, bDisconnectedCoord, OUT_TN, bINChIOutputOptionsCur, 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), ip->bAbcNumbers, ip->bCtPredecessors, ip->bNoStructLabels, num_components, num_non_taut, num_taut, output_file, log_file, num_inp, ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, pSortPrintINChIFlags, save_opt_bits); } #ifdef TARGET_LIB_FOR_WINCHI /* always calculate InChIKey */ ikflag++; if (ikflag==1) { if (ret) { char ik_string[256]; /*^^^ Resulting InChIKey string */ int ik_ret=0; /*^^^ InChIKey-calc result code */ int xhash1, xhash2; char szXtra1[256], szXtra2[256]; size_t slen = output_file->s.nUsedLength; char *buf = NULL; extract_inchi_substring(&buf, output_file->s.pStr, slen); inchi_ios_flush(output_file); output_file->type = INCHI_IOSTREAM_FILE; /* calculate and print InChIKey */ if (NULL!=buf) { xhash1 = xhash2 = 0; if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) xhash1 = 1; if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) xhash2 = 1; ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, ik_string, szXtra1, szXtra2); inchi_free(buf); } else ik_ret = 3; if (ik_ret==INCHIKEY_OK) { /* NB: correctly treat tabbed output with InChIKey & hash extensions */ char csep = '\n'; if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) csep = '\t'; inchi_ios_print(output_file, "InChIKey=%-s",ik_string); if ( xhash1 ) inchi_ios_print(output_file, "%cXHash1=%-s",csep,szXtra1); if ( xhash2 ) inchi_ios_print(output_file, "%cXHash2=%-s",csep,szXtra2); inchi_ios_print(output_file, "\n"); } else { inchi_ios_print(log_file, "Warning (Could not compute InChIKey: ", num_inp); } /*inchi_ios_flush(output_file); inchi_ios_flush2(log_file, stderr);*/ } else { inchi_ios_flush(output_file); output_file->type = INCHI_IOSTREAM_FILE; } } #endif if ( bAnnInXmlBrackets ) { inchi_ios_print( output_file, "\n\n", szAnnHdr ); } if ( !ret ) { break; } } } } exit_function: for ( j = 0; j < INCHI_NUM; j ++ ) { for ( k1 = 0, i = 0; k1 < TAUT_NUM; k1 ++ ) { if ( pINChISort[j][k1] ) { inchi_free( pINChISort[j][k1] ); } } } ret = ret? 0 : _IS_FATAL; return ret; } /**********************************************************************************/ void FreeAllINChIArrays( PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int num_components[INCHI_NUM] ) { int k; for ( k = 0; k < INCHI_NUM; k ++ ) { FreeINChIArrays( pINChI[k], pINChI_Aux[k], num_components[k] ); num_components[k] = 0; if ( pINChI[k] ) { inchi_free( pINChI[k] ); pINChI[k] = NULL; } if ( pINChI_Aux[k] ) { inchi_free( pINChI_Aux[k] ); pINChI_Aux[k] = NULL; } } } /**********************************************************************************/ void FreeINChIArrays( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components ) { int i, k; /* release allocated memory */ if ( pINChI ) { for ( i = 0; i < num_components; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { Free_INChI( &pINChI[i][k] ); /* inchi_free( pINChI[i][k] ); pINChI[i][k] = NULL; */ } } } if ( pINChI_Aux ) { for ( i = 0; i < num_components; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { Free_INChI_Aux( &pINChI_Aux[i][k] ); /* inchi_free( pINChI_Aux[i][k] ); pINChI_Aux[i][k] = NULL; */ } } } } /********************************************** * output " L=V" or " L missing" or "" * The fprintf format string must contain %s%s%s%s */ const char gsMissing[] = "is missing"; const char gsEmpty[] = ""; const char gsSpace[] = " "; const char gsEqual[] = "="; #ifndef TARGET_API_LIB /*********************************************************************************************************/ void SplitTime( unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ) { *mseconds = (int)(ulTotalTime % 1000); ulTotalTime /= 1000; *seconds = (int)(ulTotalTime % 60); ulTotalTime /= 60; *minutes = (int)(ulTotalTime % 60); ulTotalTime /= 60; *hours = (int)(ulTotalTime); } /*********************************************************************************************************/ int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, /* for CML:*/ int inp_index, int *out_index ) { inchiTime ulTStart; int nRet = 0, nRet2 = 0; int bGetOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); INCHI_MODE InpAtomFlags = 0; /* reading Molfile may set FLAG_INP_AT_CHIRAL bit */ /* vABParityUnknown holds actual value of an internal constant signifying */ /* unknown parity: either the same as for undefined parity (default==standard) */ /* or a specific one (non-std; requested by SLUUD switch). */ int vABParityUnknown = AB_PARITY_UNDF; if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) { /* Make labels for unknown and undefined stereo different */ vABParityUnknown = AB_PARITY_UNKN; } memset( sd, 0, sizeof(*sd) ); switch ( ip->nInputType ) { case INPUT_MOLFILE: case INPUT_SDFILE: if ( orig_inp_data ) { if ( ip->pSdfValue && ip->pSdfValue[0] ) { /* Added 07-29-2003 to avoid inheriting exact value from prev. structure and to make reference to a (bad) structure with unknown ID Value */ char *p, *q; /* q shadows prev declaration of const char *q */ int n; if ( (p = strrchr( ip->pSdfValue, '+' )) && '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { sprintf( p+1, "%d]", n+1 ); } else { strcat( ip->pSdfValue, " [+1]" ); } } InchiTimeGet( &ulTStart ); sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); /* read the original structure */ nRet2 = MolfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, bGetOrigCoord, ip->bDoNotAddH, ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &ip->lMolfileNumber, &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0; if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) ip->lMolfileNumber = 0; sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); sd->ulStructTime += InchiTimeElapsed( &ulTStart ); #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= orig_inp_data->bExtract; #endif /* 2004-11-16: added Molfile Chiral Flag Mode */ /* ***************************************************************************** * Chiral flags are set in: * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI (here) * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code *******************************************************************************/ /* 1. Highest precedence: Chiral Flag set by the user */ if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ } else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ } else if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; } /* save requested flags in the AuxInfo */ sd->bChiralFlag &= ~( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); sd->bChiralFlag |= InpAtomFlags & ( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); /* quick fix: modify ip->nMode on the fly */ /* 2. The user requested both Stereo AND Chiral flag */ if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { /* structure has chiral flag or the user said it is chiral */ ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ } else { ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; ip->nMode |= REQ_MODE_RELATIVE_STEREO; sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as explicitly not chiral */ } } } else { /* read the next original structure */ int nStructReadError=0; if ( !ip->bMergeAllInputStructures ) { nRet2 = MolfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { return _IS_EOF; } } else { return _IS_EOF; } } break; case INPUT_INCHI_XML: case INPUT_INCHI_PLAIN: if ( orig_inp_data ) { if ( ip->pSdfValue && ip->pSdfValue[0] ) { /* Added 07-29-2003 to avoid inheriting exact value from prev. structure and to make reference to a (bad) structure with unknown ID Value */ char *p, *q; int n; if ( (p = strrchr( ip->pSdfValue, '+' )) && '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { sprintf( p+1, "%d]", n+1 ); } else { strcat( ip->pSdfValue, " [+1]" ); } } InchiTimeGet( &ulTStart ); sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); /* read the original structure */ nRet2 = INChIToOrigAtom( inp_file, orig_inp_data, ip->bMergeAllInputStructures, bGetOrigCoord, ip->bDoNotAddH, vABParityUnknown, ip->nInputType, ip->pSdfLabel, ip->pSdfValue, &ip->lMolfileNumber, &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); sd->ulStructTime += InchiTimeElapsed( &ulTStart ); #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= orig_inp_data->bExtract; #endif /* 2004-11-16: added Molfile Chiral Flag Mode */ if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ } else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ } else if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; } sd->bChiralFlag |= InpAtomFlags; /* copy chiral flag to AuxInfo */ /* quick fix: modify ip->nMode on the fly */ if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); } else { ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; ip->nMode |= REQ_MODE_RELATIVE_STEREO; } } } else { /* read the next original structure */ int nStructReadError=0; if ( !ip->bMergeAllInputStructures ) { nRet2 = INChIToOrigAtom( inp_file, NULL, 0, 0, 0, 0, ip->nInputType, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { return _IS_EOF; } } else { return _IS_EOF; } } break; #if ( ADD_CMLPP == 1 ) /* BILLY 8/6/04 */ case INPUT_CMLFILE: if ( orig_inp_data ) { InchiTimeGet( &ulTStart ); /* if ( inp_index >= 0 ) { sd->fPtrStart = inp_index; } else { sd->fPtrStart = GetCmlStructIndex(); } */ sd->fPtrStart = -1; /* disable "CopyMOLfile() for CML input files */ sd->fPtrEnd = -1; /* read the original structure */ nRet = CmlfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, bGetOrigCoord, ip->bDoNotAddH, inp_index, out_index, ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &sd->nStructReadError, sd->pStrErrStruct ); sd->ulStructTime += InchiTimeElapsed( &ulTStart ); #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= orig_inp_data->bExtract; #endif } else { /* read the next original structure */ int nStructReadError=0; if ( !ip->bMergeAllInputStructures ) { nRet2 = CmlfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, inp_index, out_index, NULL, NULL, NULL, &nStructReadError, NULL ); if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { return _IS_EOF; } } else { return _IS_EOF; } } break; #endif default: nRet = _IS_FATAL; /* wrong file type */ } return nRet; } #endif /*****************************************************************************************************/ int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ) { int nRet = _IS_OKAY; /* End of file */ if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) { if ( sd->pStrErrStruct[0] ) { inchi_ios_eprint( log_file, "%s inp structure #%ld: End of file.%s%s%s%s \n", sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } inchi_ios_eprint( log_file, "End of file detected after structure #%ld. \n", *num_inp-1 ); nRet = _IS_EOF; goto exit_function; /* end of file */ } /*(*num_inp) ++;*/ /* Skipping the structures */ if ( *num_inp < ip->first_struct_number ) { #if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) /*^^^ #ifndef TARGET_API_LIB */ if ( log_file->f != stderr ) { inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); } #endif nRet = sd->nErrorType = _IS_SKIP; goto exit_function; } sd->nErrorType = GetInpStructErrorType( ip, sd->nStructReadError, sd->pStrErrStruct, orig_inp_data->num_inp_atoms ); /* init xml output */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !ip->bXmlStarted ) { OutputINChIXmlRootStartTag( output_file ); ip->bXmlStarted ++; } /* init xml structure block */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !sd->bXmlStructStarted ) { if ( !OutputINChIXmlStructStartTag( output_file, pStr, 1, nStrLen, ip->bNoStructLabels, *num_inp, ip->pSdfLabel, ip->pSdfValue ) ) { inchi_ios_eprint( log_file, "Cannot create start xml tag for structure #%ld.%s%s%s%s Terminating.\n", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->bXmlStructStarted = -1; nRet = _IS_FATAL; goto exit_function; } sd->bXmlStructStarted ++; } /* Fatal error */ if ( sd->nErrorType == _IS_FATAL ) { if ( nLogMask & LOG_MASK_FATAL ) inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); #if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); } #endif /* goto exit_function; */ } /* Non-fatal errors: do not produce INChI */ if ( sd->nErrorType == _IS_ERROR ) { /* 70 => too many atoms */ if ( nLogMask & LOG_MASK_ERR ) inchi_ios_eprint( log_file, "Error %d (no %s; %s) inp structure #%ld.%s%s%s%s\n", sd->nStructReadError, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)?"Molfile":INCHI_NAME, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); #if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); } #endif } /* Warnings: try to produce INChI */ if ( sd->nErrorType == _IS_WARNING ) { if ( nLogMask & LOG_MASK_WARN ) inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } /* xml error/warning processing; close xml struct block if error */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) #ifdef TARGET_LIB_FOR_WINCHI || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) #endif ) { if ( sd->nErrorType != _IS_OKAY && sd->nErrorType != _IS_WARNING ) { sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nStructReadError,*/ sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, *num_inp, ip, pStr, nStrLen ); } } exit_function: if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) { nRet = sd->nErrorType; } return nRet; } /******************************************************************************************************/ int GetOneComponent( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, char *pStr, int nStrLen ) { inchiTime ulTStart; InchiTimeGet( &ulTStart ); CreateInpAtomData( inp_cur_data, orig_inp_data->nCurAtLen[i], 0 ); inp_cur_data->num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i+1, inp_cur_data->at ); sd->ulStructTime += InchiTimeElapsed( &ulTStart ); /* error processing */ if ( inp_cur_data->num_at <= 0 || orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at ) { /* log error message */ AddMOLfileError(sd->pStrErrStruct, "Cannot extract Component"); inchi_ios_eprint( log_file, "%s #%d structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); sd->nErrorCode = inp_cur_data->num_at < 0? inp_cur_data->num_at : (orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at)? CT_ATOMCOUNT_ERR : CT_UNKNOWN_ERR; /* num_err ++; */ sd->nErrorType = _IS_ERROR; if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) #ifdef TARGET_LIB_FOR_WINCHI || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) #endif ) { /* xml error message */ sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); } } return sd->nErrorType; } /*******************************************************************************************/ int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct) { int j; int nAmbiguousStereoAtoms, nAmbiguousStereoBonds; nAmbiguousStereoAtoms = 0; nAmbiguousStereoBonds = 0; if ( inp_norm_data->at ) { for ( j = 0; j < pINChI->nNumberOfAtoms; j ++ ) { if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_ATOM | AMBIGUOUS_STEREO_ATOM_ISO) ) { nAmbiguousStereoAtoms ++; } if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_BOND | AMBIGUOUS_STEREO_BOND_ISO) ) { nAmbiguousStereoBonds ++; } } if ( nAmbiguousStereoAtoms ) { AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); AddMOLfileError(pStrErrStruct, "center(s)"); } if ( nAmbiguousStereoBonds ) { AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); AddMOLfileError(pStrErrStruct, "bond(s)"); } } return (nAmbiguousStereoAtoms || nAmbiguousStereoBonds); } /*******************************************************************************************/ int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd) { int i, ret = 0; for (i = 0; i < TAUT_NUM; i ++ ) { if ( cur_INChI[i] && cur_INChI[i]->nNumberOfAtoms>0 ) { ret |= GetProcessingWarningsOneINChI(cur_INChI[i], inp_norm_data[i], sd->pStrErrStruct); } } return ret; } /*******************************************************************************************/ int CreateOneComponentINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, int i, long num_inp, INP_ATOM_DATA **inp_norm_data, NORM_CANON_FLAGS *pncFlags, INCHI_IOSTREAM *log_file ) { inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; int k, num_at, ret = 0; int bOrigCoord; INCHI_MODE bTautFlags = ip->bTautFlags; INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); INChI *cur_INChI[TAUT_NUM]; INChI_Aux *cur_INChI_Aux[TAUT_NUM]; long lElapsedTime; /* PINChI2 *pINChI = pINChI2[iINChI]; PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; */ InchiTimeGet( &ulTStart ); bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); for ( k = 0; k < TAUT_NUM; k ++ ) { cur_INChI[k] = NULL; cur_INChI_Aux[k] = NULL; } /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ for ( k = 0; k < TAUT_NUM; k ++ ) { int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? (ip->nMode & REQ_MODE_ISO):0; if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { /* alloc INChI and INChI_Aux */ cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, &inp_cur_data->num_isotopic, nAllocMode ); cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); if ( cur_INChI_Aux[k] ) { cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; } /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); } else { FreeInpAtomData( inp_norm_data[k] ); } } lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= lElapsedTime; } sd->ulStructTime += lElapsedTime; /*^^^#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) #if ( TEST_RENUMB_ATOMS != 1 ) /* log file / console output */ if ( log_file->f && log_file->f != stderr ) { /* NULL log_file now ignored. 11-23-2005 */ if ( ip->bDisplay ) inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); else inchi_fprintf( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } #endif #endif /****************************************************** * * Get one component canonical numberings, etc. * ******************************************************/ /* * Create_INChI() return value: * num_at <= 0: error code * num_at > 0: number of atoms (excluding terminal hydrogen atoms) * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric */ InchiTimeGet( &ulTStart ); if ( ip->msec_MaxTime ) { ulTEnd = ulTStart; pulTEnd = &ulTEnd; if ( ip->msec_LeftTime > 0 ) { InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); } } num_at = Create_INChI( cur_INChI, cur_INChI_Aux, orig_inp_data/* not used */, inp_cur_data->at, inp_norm_data, inp_cur_data->num_at, ip->nMode, &bTautFlags, &bTautFlagsDone, pulTEnd, NULL, sd->pStrErrStruct); SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ for ( k = 0; k < TAUT_NUM; k ++ ) { if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) { pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; } } /* Detect errors */ if ( num_at < 0 ) { sd->nErrorCode = num_at; } else if ( num_at == 0 ) { sd->nErrorCode = -1; } else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) { /* non-tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; } else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) { /* tautomeric error */ sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; } #if ( bRELEASE_VERSION == 0 ) if ( cur_INChI[TAUT_NON] ) sd->bExtract |= cur_INChI[TAUT_NON]->bExtract; if ( cur_INChI[TAUT_YES] ) sd->bExtract |= cur_INChI[TAUT_YES]->bExtract; if ( (TG_FLAG_TEST_TAUT3_SALTS_DONE & bTautFlagsDone) ) { sd->bExtract |= EXTR_TEST_TAUT3_SALTS_DONE; } #endif /* detect and store stereo warnings */ if ( !sd->nErrorCode ) { GetProcessingWarnings(cur_INChI, inp_norm_data, sd); } lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= lElapsedTime; } sd->ulStructTime += lElapsedTime; #ifndef TARGET_API_LIB /* Display the results */ if ( ip->bDisplay ) eat_keyboard_input(); #endif /* a) No matter what happened save the allocated INChI pointers */ /* save the INChI of the current component */ InchiTimeGet( &ulTStart ); for ( k = 0; k < TAUT_NUM; k ++ ) { pINChI[i][k] = cur_INChI[k]; pINChI_Aux[i][k] = cur_INChI_Aux[k]; cur_INChI[k] = NULL; cur_INChI_Aux[k] = NULL; } /* b) Count one component structure and/or INChI results only if there was no error */ /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ if ( !sd->nErrorCode ) { /* find where the current processed structure is located */ int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; /* sd->bTautFlags[iINChI] |= bTautFlags; sd->bTautFlagsDone[iINChI] |= bTautFlagsDone; */ if ( cur_is_non_taut + cur_is_taut ) { /* count tautomeric and non-tautomeric components of the structures */ int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; int j; sd->num_non_taut[iINChI] += cur_is_non_taut; sd->num_taut[iINChI] += cur_is_taut; for ( j = j1; j <= j2; j ++ ) { int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || pINChI[i][j]->nNumberOfIsotopicTGroups || pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); if ( j == TAUT_YES ) { bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); } inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; /*inp_norm_data[j]->num_removed_H = inp_norm_data[j]->num_at - num_at;*/ } } } /* return (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : sd->nErrorCode? _IS_ERROR : 0; */ if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) { ret = _IS_FATAL; } else if ( sd->nErrorCode ) { ret = _IS_ERROR; } lElapsedTime = InchiTimeElapsed( &ulTStart ); if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= lElapsedTime; } sd->ulStructTime += lElapsedTime; return ret; } /****************************************************************************************************/ int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ char *pStr, int nStrLen ) { if ( sd->nErrorCode ) { AddMOLfileError(sd->pStrErrStruct, ErrMsg(sd->nErrorCode) ); inchi_ios_eprint( log_file, "Error %d (%s) structure #%ld component %d.%s%s%s%s\n", sd->nErrorCode, sd->pStrErrStruct, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->nErrorType = (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : _IS_ERROR; if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) #ifdef TARGET_LIB_FOR_WINCHI || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) #endif ) { sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); /* save the problem structure */ if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); } } else { /* save the problem structure */ if ( sd->nErrorCode && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); } } } /*^^^ #ifndef TARGET_API_LIB */ #if ( !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) /* print the logfile record */ if ( log_file->f && log_file->f != stderr && (sd->ulStructTime >= 1000 || sd->nErrorCode) ) { fprintf( log_file->f, "%10lu msec structure #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", sd->ulStructTime, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); } #endif return sd->nErrorType; } /****************************************************************************************************/ int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, long num_inp, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ char *pStr, int nStrLen ) { #if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS || EXTR_MASK) ) if ( EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS) ) { char szMsg[64]; sprintf( szMsg, "ExtractStruct.code=0x%X", sd->bExtract); AddMOLfileError(sd->pStrErrStruct, szMsg); } #endif if ( !sd->nErrorCode && sd->pStrErrStruct[0] ) { inchi_ios_eprint( log_file, "Warning (%s) structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->nErrorType = _IS_WARNING; if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) #ifdef TARGET_LIB_FOR_WINCHI || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) #endif ) { sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); } /* save the structure as a problem structure if requested */ if ( ip->bSaveWarningStructsAsProblem && !ip->bSaveAllGoodStructsAsProblem && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); } #if ( bRELEASE_VERSION == 0 ) /* otherwise extract the structure as a problem structure if requested */ else if ( (EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS)) && !ip->bSaveAllGoodStructsAsProblem && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); } #endif } #if ( bRELEASE_VERSION != 1 && bOUTPUT_ONE_STRUCT_TIME == 1 ) #ifndef TARGET_API_LIB if ( log_file && log_file != stderr ) { fprintf( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); } #else if ( log_file ) { inchi_ios_eprint( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); } #endif #endif return sd->nErrorType; } /*******************************************************************************************/ int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ) { inp_ATOM *at = NULL; AT_NUMB *nCurAtLen = NULL; AT_NUMB *nOldCompNumber = NULL; if ( new_orig_atom->at && new_orig_atom->num_inp_atoms >= orig_atom->num_inp_atoms ) { at = new_orig_atom->at; } else { at = (inp_ATOM *)inchi_calloc(orig_atom->num_inp_atoms+1, sizeof(at[0])); } if ( new_orig_atom->nOldCompNumber && new_orig_atom->num_components >= orig_atom->num_components ) { nCurAtLen = new_orig_atom->nCurAtLen; } else { nCurAtLen = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nCurAtLen[0])); } if ( new_orig_atom->nCurAtLen && new_orig_atom->num_components >= orig_atom->num_components ) { nOldCompNumber = new_orig_atom->nOldCompNumber; } else { nOldCompNumber = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nOldCompNumber[0])); } if ( at && nCurAtLen && nOldCompNumber ) { /* copy */ if ( orig_atom->at ) memcpy( at, orig_atom->at, orig_atom->num_inp_atoms * sizeof(new_orig_atom->at[0]) ); if ( orig_atom->nCurAtLen ) memcpy( nCurAtLen, orig_atom->nCurAtLen, orig_atom->num_components*sizeof(nCurAtLen[0]) ); if ( orig_atom->nOldCompNumber ) memcpy( nOldCompNumber, orig_atom->nOldCompNumber, orig_atom->num_components*sizeof(nOldCompNumber[0]) ); /* deallocate */ if ( new_orig_atom->at && new_orig_atom->at != at ) inchi_free( new_orig_atom->at ); if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) inchi_free( new_orig_atom->nCurAtLen ); if ( new_orig_atom->nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) inchi_free( new_orig_atom->nOldCompNumber ); *new_orig_atom = *orig_atom; new_orig_atom->at = at; new_orig_atom->nCurAtLen = nCurAtLen; new_orig_atom->nOldCompNumber = nOldCompNumber; /* data that are not to be copied */ new_orig_atom->nNumEquSets = 0; memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); /* arrays that are not to be copied */ new_orig_atom->szCoord = NULL; new_orig_atom->nEquLabels = NULL; new_orig_atom->nSortedOrder = NULL; return 0; } /* deallocate */ if ( at && new_orig_atom->at != at ) inchi_free( at ); if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) inchi_free( nCurAtLen ); if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) inchi_free( nOldCompNumber ); return -1; /* failed */ } #ifndef TARGET_API_LIB /*******************************************************************************************/ int GetOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen, STRUCT_FPTRS *struct_fptrs ) { int nRet, inp_index, out_index, bUseFptr = (NULL != struct_fptrs); FreeOrigAtData( orig_inp_data ); /* FreeOrigAtData( orig_inp_data + 1 ); FreeOrigAtData( orig_inp_data + 2 ); */ /* added for TARGET_LIB_FOR_WINCHI early EOF detection */ inp_index = -1; out_index = -1; if ( struct_fptrs ) { if ( inp_file->f == stdin ) { return _IS_FATAL; } if ( ip->nInputType == INPUT_CMLFILE ) { bUseFptr = 0; } /* initially allocate or increase length of struct_fptrs->fptr array */ if ( !struct_fptrs->fptr || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { INCHI_FPTR *new_fptr = (INCHI_FPTR *)inchi_calloc( struct_fptrs->len_fptr + ADD_LEN_STRUCT_FPTRS, sizeof(new_fptr[0]) ); if ( new_fptr ) { if ( struct_fptrs->fptr ) { if ( struct_fptrs->len_fptr ) { memcpy( new_fptr, struct_fptrs->fptr, struct_fptrs->len_fptr*sizeof(new_fptr[0])); } inchi_free( struct_fptrs->fptr ); } else { struct_fptrs->len_fptr = 0; struct_fptrs->cur_fptr = 0; struct_fptrs->max_fptr = 0; } struct_fptrs->len_fptr += ADD_LEN_STRUCT_FPTRS; struct_fptrs->fptr = new_fptr; } else { return _IS_FATAL; /* new_fptr allocation error */ } } if ( struct_fptrs->fptr[struct_fptrs->cur_fptr] == EOF ) { return _IS_EOF; } else { if ( bUseFptr ) { if( fseek( inp_file->f, struct_fptrs->fptr[struct_fptrs->cur_fptr], SEEK_SET) ) { return _IS_FATAL; } if ( struct_fptrs->cur_fptr && struct_fptrs->max_fptr <= struct_fptrs->cur_fptr ) { return _IS_FATAL; } } else { inp_index = struct_fptrs->fptr[struct_fptrs->cur_fptr]; out_index = EOF; } } *num_inp = struct_fptrs->cur_fptr; /* set structure count */ } nRet = ReadTheStructure( sd, ip, inp_file, orig_inp_data, inp_index, &out_index ); if ( !nRet ) { /***************************************************** * In case of no error output structure xml start tag * output read the structure errors and warnings *****************************************************/ if ( ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) { if ( ip->lMolfileNumber ) { *num_inp = ip->lMolfileNumber; } else { *num_inp += 1; } } else { *num_inp += 1; } nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, orig_inp_data, num_inp, pStr, nStrLen ); } /************************************************************/ /* added for TARGET_LIB_FOR_WINCHI: look ahead for end of file detection */ /************************************************************/ if ( struct_fptrs && struct_fptrs->fptr && struct_fptrs->fptr[struct_fptrs->cur_fptr+1] <= 0 ) { int nRet2 = 0; INCHI_FPTR next_fptr; STRUCT_DATA sd2; if ( nRet != _IS_EOF && nRet != _IS_FATAL ) { if ( inp_file->f == stdin || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { return _IS_FATAL; } /* get next structure fptr */ if ( bUseFptr ) { next_fptr = ftell( inp_file->f ); } else { inp_index = out_index; out_index = EOF; } /* read the next structure */ nRet2 = ReadTheStructure( &sd2, ip, inp_file, NULL, inp_index, &out_index ); /* restore fptr to the next structure */ if ( bUseFptr ) { if ( next_fptr != -1L ) { fseek( inp_file->f, next_fptr, SEEK_SET); } } #if ( ADD_CMLPP == 1 ) else { if ( inp_index >= 0 ) { SetCmlStructIndex( inp_index ); /* so far nothing to do */ } } #endif } else { /* treat current fatal error as end of file */ struct_fptrs->fptr[struct_fptrs->cur_fptr] = EOF; } /* next is end of file or fatal */ if ( nRet == _IS_EOF || nRet == _IS_FATAL || nRet2 == _IS_EOF || nRet2 == _IS_FATAL ) { struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = EOF; } else { struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = bUseFptr? sd->fPtrEnd : inp_index; } /* update struct_fptrs->max_fptr */ if ( struct_fptrs->max_fptr <= struct_fptrs->cur_fptr+1 ) { struct_fptrs->max_fptr = struct_fptrs->cur_fptr+2; } } switch ( nRet ) { case _IS_EOF: *num_inp -= 1; case _IS_FATAL: case _IS_ERROR: case _IS_SKIP: goto exit_function; } /* if ( !orig_inp_data->num_dimensions ) { AddMOLfileError(sd->pStrErrStruct, "0D"); */ /* 0D-structure: no coordinates } */ exit_function: return nRet; } #endif #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ /************************************************************************************************/ int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ) { int j; pRenumbData->ren_inp_norm_data[0] = &pRenumbData->ren_inp_norm_data1; pRenumbData->ren_inp_norm_data[1] = &pRenumbData->ren_inp_norm_data2; memset( pRenumbData->ren_INChI2, 0, sizeof( pRenumbData->ren_INChI2 )); memset( pRenumbData->ren_INChI_Aux, 0, sizeof( pRenumbData->ren_INChI_Aux )); memset( &pRenumbData->orig_inp_cur_data, 0, sizeof( pRenumbData->orig_inp_cur_data )); memset( &pRenumbData->saved_inp_cur_data, 0, sizeof( pRenumbData->saved_inp_cur_data )); memset( pRenumbData->ren_inp_norm_data[0], 0, sizeof( *pRenumbData->ren_inp_norm_data[0] )); memset( pRenumbData->ren_inp_norm_data[1], 0, sizeof( *pRenumbData->ren_inp_norm_data[1] )); #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) memset( &pRenumbData->longest_inp_cur_data, 0, sizeof(pRenumbData->longest_inp_cur_data)); #endif CopyInpAtomData( &pRenumbData->orig_inp_cur_data, inp_cur_data ); pRenumbData->ren_counter = pRenumbData->orig_inp_cur_data.num_at * pRenumbData->orig_inp_cur_data.num_at; srand(1); /* for reproducibility */ rand(); /* shift to avoid prev. sequences */ pRenumbData->nComp = 0; /*ren_counter = 29;*/ pRenumbData->new_ord = (AT_RANK *)inchi_calloc( pRenumbData->orig_inp_cur_data.num_at, sizeof(pRenumbData->new_ord[0]) ); if ( pRenumbData->new_ord ) { for ( j = 0; j < pRenumbData->orig_inp_cur_data.num_at; j ++ ) { pRenumbData->new_ord[j] = (AT_RANK)j; } return 0; } return -1; /* out of RAM */ } /************************************************************************************************/ int RenumberingTestUninit( RENUMB_DATA *pRenumbData ) { FreeInpAtomData( &pRenumbData->orig_inp_cur_data ); #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); #endif inchi_free( pRenumbData->new_ord ); return 0; } /************************************************************************************************/ int RenumberingTest( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, int i, long num_inp, NORM_CANON_FLAGS *pncFlags) { int k, bLongerTime; CopyInpAtomData( &pRenumbData->saved_inp_cur_data, inp_cur_data ); pRenumbData->nRet2 = 0; pRenumbData->num_taut0 = sd->num_taut[iINChI]; pRenumbData->num_non_taut0 = sd->num_non_taut[iINChI]; pRenumbData->ulMaxTime = 0; while ( -- pRenumbData->ren_counter >= 0 && !pRenumbData->nRet2 ) { pRenumbData->nComp ++; MakeNewOrd( pRenumbData->orig_inp_cur_data.num_at, pRenumbData->new_ord ); RenumbInpAtomData( inp_cur_data /* output*/, &pRenumbData->orig_inp_cur_data/* input*/, pRenumbData->new_ord/* input*/ ); #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); #endif if ( 470 == pRenumbData->nComp ) { int stop = 1; /* debug only */ } pRenumbData->nRet2 = CreateOneComponentINChI( sd, ip, inp_cur_data, NULL /*orig_inp_data*/, pRenumbData->ren_INChI2, pRenumbData->ren_INChI_Aux, iINChI, 0, num_inp, pRenumbData->ren_inp_norm_data, pncFlags, log_file ); /* CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI2[iINChI], pINChI_Aux2[iINChI], iINChI, i, num_inp, inp_norm_data, log_file ); */ if ( !pRenumbData->nRet2 ) { pRenumbData->c1 = CompareINChI( pINChI[i][TAUT_NON], pRenumbData->ren_INChI2[0][TAUT_NON], pINChI_Aux[i][TAUT_NON], pRenumbData->ren_INChI_Aux[0][TAUT_NON]); pRenumbData->c2 = CompareINChI( pINChI[i][TAUT_YES], pRenumbData->ren_INChI2[0][TAUT_YES], pINChI_Aux[i][TAUT_YES], pRenumbData->ren_INChI_Aux[0][TAUT_YES]); if ( pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2 ) { /****** the renumbering result is different ******/ inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%d component %d.%s%s%s%s\n", pRenumbData->c1, pRenumbData->c2, pRenumbData->nComp, pRenumbData->nRet2, INCHI_NAME, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); for ( k = 0; k < pRenumbData->orig_inp_cur_data.num_at; k ++ ) { inchi_ios_eprint( log_file, " %d", (int)pRenumbData->new_ord[k] ); } inchi_ios_eprint( log_file, "\n" ); pRenumbData->ren_counter = 0; /* force exit */ pRenumbData->bRenumbErr = 1000*pRenumbData->c2 + pRenumbData->c1; #if ( TEST_RENUMB_SWITCH == 1 ) CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); if ( pRenumbData->longest_inp_cur_data.at ) { for ( k = 0; k < pRenumbData->longest_inp_cur_data.num_at; k ++ ) { pRenumbData->longest_inp_cur_data.at[k].orig_at_number = k+1; /* display new atom numbers */ } } #endif } #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) /* output time per this component */ inchi_ios_eprint( stderr, "\rComp#%d str#%ld/%d%s%s%s%s Ren %d/%d n(%lu:%lu)c(%lu:%lu)...\r", i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime, pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime); #endif /* make sure the max. time is not overwritten */ pRenumbData->ulCurTime0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? (pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime + pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime) : 0; pRenumbData->ulCurTime1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? (pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime + pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime) : 0; pRenumbData->ulCurTime = inchi_max( pRenumbData->ulCurTime0, pRenumbData->ulCurTime1 ); pRenumbData->ulCurTimeCanon0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime : 0; pRenumbData->ulCurTimeCanon1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime : 0; pRenumbData->ulCurTimeCanon = inchi_max( pRenumbData->ulCurTimeCanon0, pRenumbData->ulCurTimeCanon1); pRenumbData->ulCurTimeNorm0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0; pRenumbData->ulCurTimeNorm1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0; pRenumbData->ulCurTimeNorm = inchi_max( pRenumbData->ulCurTimeNorm0, pRenumbData->ulCurTimeNorm1); bLongerTime = 0; if ( pRenumbData->ulCurTime > pRenumbData->ulMaxTime ) { pRenumbData->ulMaxTime = pRenumbData->ulCurTime; bLongerTime = 1; } if ( pRenumbData->ulMaxTimeCanon > pRenumbData->ulCurTimeCanon ) { pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; bLongerTime = 1; } if ( pRenumbData->ulMaxTimeNorm > pRenumbData->ulCurTimeCanon ) { pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; bLongerTime = 1; } #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) if ( bLongerTime || TEST_RENUMB_SWITCH == 1 && (pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2) ) { char szLine[512]; char szValue[512]; inchi_ios_eprint( stderr, "\n" ); sprintf( szLine, "Comp#%d str#%ld/%d%s%s%s%s Ren %d/%d n=%lu:%lu c=%lu:%lu", i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); sprintf( szValue, "%s (c%d/s%ld/i%d, r%d/%d n=%lu:%lu c=%lu:%lu)", (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue:"unk", i+1, num_inp, iINChI, pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); WriteToSDfile( &pRenumbData->longest_inp_cur_data, prb_file, szLine, NULL, ip->pSdfLabel, szValue ); } #endif #if ( TEST_RENUMB_SWITCH == 1 ) if ( pRenumbData->c1 || pRenumbData->c2 || !pRenumbData->ren_counter ) { inchi_swap( (char*)&pINChI[i][TAUT_NON], (char*)&pRenumbData->ren_INChI2[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI2[0][0]) ); inchi_swap( (char*)&pINChI[i][TAUT_YES], (char*)&pRenumbData->ren_INChI2[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI2[0][0]) ); inchi_swap( (char*)&pINChI_Aux[i][TAUT_NON], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); inchi_swap( (char*)&pINChI_Aux[i][TAUT_YES], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); } #endif } for ( k = 0; k < TAUT_NUM; k ++ ) { if ( pRenumbData->ren_INChI2[0][k] ) { Free_INChI(&pRenumbData->ren_INChI2[0][k]); /* inchi_free(pRenumbData->ren_INChI2[0][k]); pRenumbData->ren_INChI2[0][k] = NULL; */ } if ( pRenumbData->ren_INChI_Aux[0][k] ) { Free_INChI_Aux(&pRenumbData->ren_INChI_Aux[0][k]); /* inchi_free(pRenumbData->ren_INChI_Aux[0][k]); pRenumbData->ren_INChI_Aux[0][k] = NULL; */ } } } /* eliminate overcounting due to multiple renumberings/recalculations */ pRenumbData->num_taut = sd->num_taut[iINChI] - pRenumbData->num_taut0; pRenumbData->num_non_taut = sd->num_non_taut[iINChI] - pRenumbData->num_non_taut0; sd->num_taut[iINChI] = pRenumbData->num_taut0; sd->num_non_taut[iINChI] = pRenumbData->num_non_taut0; if ( pRenumbData->num_taut % pRenumbData->nComp || pRenumbData->num_non_taut % pRenumbData->nComp ) { inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%ld component %d.%s%s%s%s\n", pRenumbData->num_non_taut % pRenumbData->nComp, pRenumbData->num_taut % pRenumbData->nComp, pRenumbData->nComp, 333, INCHI_NAME, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } #if ( TEST_RENUMB_SWITCH == 1 ) /* { */ CopyInpAtomData( inp_norm_data[TAUT_NON], pRenumbData->ren_inp_norm_data[TAUT_NON] ); CopyInpAtomData( inp_norm_data[TAUT_YES], pRenumbData->ren_inp_norm_data[TAUT_YES] ); /* renumbered input structure */ #ifndef COMPILE_ANSI_ONLY /* { */ if ( /*ip->bDisplayEachComponentINChI &&*/ !pRenumbData->nRet2 ) { int err, len; /* err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL. 1, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); */ err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); if ( pRenumbData->c1 || pRenumbData->c2 ) { len = strlen(szTitle); strcat( szTitle, " (Renumbered)" ); err = DisplayStructure( pRenumbData->longest_inp_cur_data.at, pRenumbData->longest_inp_cur_data.num_at, 0, 1, 0, NULL, 1, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); szTitle[len] = '\0'; } sd->bUserQuitComponentDisplay = (err==ESC_KEY); if ( !err ) { inchi_ios_eprint( stderr, "Cannot display the structure\n"); } } #endif /* } COMPILE_ANSI_ONLY */ #else /* } TEST_RENUMB_SWITCH { */ CopyInpAtomData( inp_cur_data, &pRenumbData->saved_inp_cur_data ); #endif /* } TEST_RENUMB_SWITCH */ FreeInpAtomData( &pRenumbData->saved_inp_cur_data ); FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_NON] ); FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_YES] ); #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); #endif return pRenumbData->nRet2; } #endif /* } TEST_RENUMB_ATOMS */ /****************************************************************************/ int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ) { int i, val, num_found = 0; char msg[32]; int len, num_H; inp_ATOM *at = ( orig_at_data && orig_at_data->num_inp_atoms > 0 )? orig_at_data->at : NULL; if ( at ) { for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i ++ ) { num_H = bAddIsoH? NUMH(at,i) : at[i].num_H; val = detect_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, at[i].chem_bonds_valence, num_H, at[i].valence ); if ( val ) { num_found ++; /* produce message */ AddMOLfileError(pStrErrStruct, "Accepted unusual valence(s):"); len = sprintf( msg, "%s", at[i].elname ); if ( at[i].charge ) { len += sprintf( msg+len, "%+d", at[i].charge ); } if ( at[i].radical ) { len += sprintf( msg + len, ",%s", at[i].radical == RADICAL_SINGLET? "s" : at[i].radical == RADICAL_DOUBLET? "d" : at[i].radical == RADICAL_TRIPLET? "t" : "?" ); } len += sprintf( msg + len, "(%d)", val ); AddMOLfileError(pStrErrStruct, msg); } } } return num_found; } /***************************************************************************/ int PreprocessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ) { int i; INCHI_MODE bTautFlags = 0; INCHI_MODE bTautFlagsDone = 0; /*************************************************/ /* 1. copy orig_inp_data --> prep_inp_data */ /*************************************************/ if ( 0 > DuplicateOrigAtom( prep_inp_data, orig_inp_data ) ) { AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } #if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) if ( bHasMetalAtom( orig_inp_data ) ) { sd->bExtract |= EXTR_HAS_METAL_ATOM; } #endif /*************************************************/ /* 2. fix odd things in prep_inp_data */ /*************************************************/ if ( 0 < fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at, /*0*/ip->bTautFlags & TG_FLAG_FIX_SP3_BUG, ip->bFixNonUniformDraw ) ) { /* changed 2010-03-17 DT */ AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; } #if ( FIX_ADJ_RAD == 1 ) if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) { if ( 0 < FixAdjacentRadicals( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ADJ_RADICALS_DONE; } } #endif #if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS & EXTR_HAS_FEATURE) ) if ( bFoundFeature( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { sd->bExtract |= EXTR_HAS_FEATURE; } #endif /******************************************************************* * Find whether the structure can be disconnected or is a salt *******************************************************************/ /* needs salt disconnection? */ if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) { prep_inp_data->bDisconnectSalts = (0 < DisconnectSalts( prep_inp_data, 0 )); } else { prep_inp_data->bDisconnectSalts = 0; } /* needs metal disconnection? */ if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) { i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD)); bMayDisconnectMetals( prep_inp_data, i, &bTautFlagsDone ); /* changes prep_inp_data->bDisconnectCoord */ sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* whether any disconnection has been rejected because of the metal proper valence */ #if ( bRELEASE_VERSION == 0 ) if ( i && (bTautFlagsDone & TG_FLAG_CHECK_VALENCE_COORD_DONE) ) { sd->bExtract |= EXTR_METAL_WAS_NOT_DISCONNECTED; } #endif } else { prep_inp_data->bDisconnectCoord = 0; } orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts; orig_inp_data->bDisconnectCoord = prep_inp_data->bDisconnectCoord; /*************************************************/ /* 3. if( orig_inp_data->bDisconnectSalts ) then */ /* -- disconnect salts in prep_inp_data */ /*************************************************/ if ( ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) && prep_inp_data->bDisconnectSalts && 0 < (i=DisconnectSalts( prep_inp_data, 1 )) ) { AddMOLfileError(sd->pStrErrStruct, "Salt was disconnected"); sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_SALTS_DONE; if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 0 ) ) { char szErrCode[16]; sprintf( szErrCode, "%d", i); AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); AddMOLfileError( sd->pStrErrStruct, szErrCode ); } #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= EXTR_SALT_WAS_DISCONNECTED; #endif } else { prep_inp_data->bDisconnectSalts = 0; } /***********************************************************/ /* mark the (disconnected) components in prep_inp_data */ /***********************************************************/ prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); if ( prep_inp_data->num_components < 0 ) { AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } /***********************************************************/ /* Detect isotopic H on heteroatoms -- necessary condition */ /* for global isotopic tautomerism */ /***********************************************************/ if ( i = bNumHeterAtomHasIsotopicH( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { if ( i & 1 ) { sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; } if ( i & 2 ) { sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; } } /****************************************************************************/ /* 4a. Detect unusual valences */ /* should be called before metal disconnection */ /****************************************************************************/ if ( bCheckUnusualValences( prep_inp_data, 1, sd->pStrErrStruct ) ) { #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= EXTR_UNUSUAL_VALENCES; #else ; #endif } /***********************************************************/ /* 5. if( orig_inp_data->bDisconnectCoord ) then */ /* -- copy prep_inp_data --> prep_inp_data+1 */ /* -- disconnect metals in prep_inp_data */ /***********************************************************/ if ( prep_inp_data->bDisconnectCoord ) { prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); if ( prep_inp_data->num_components < 0 ) { AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } /* save Reconnected structure in prep_inp_data+1 if requested */ if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { if ( 0 > DuplicateOrigAtom( prep_inp_data+1, prep_inp_data ) ) { AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } sd->bTautFlags[INCHI_REC] = sd->bTautFlags[INCHI_BAS]; sd->bTautFlagsDone[INCHI_REC] = sd->bTautFlagsDone[INCHI_BAS]; { /* remove "parity undefined in disconnected structure" flag from reconnected structure */ int k, m, p; inp_ATOM *at = (prep_inp_data+1)->at; int num_at = (prep_inp_data+1)->num_inp_atoms; for ( k = 0; k < num_at; k ++ ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { at[k].sb_parity[m] &= SB_PARITY_MASK; } } } } /* make Disconnected structure in prep_inp_data */ i = (0 != ( ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD )); /* prep_inp_data->bDisconnectCoord > 1 means add prep_inp_data->bDisconnectCoord-1 explicit H atoms */ if ( 0 < (i = DisconnectMetals( prep_inp_data, i, &bTautFlagsDone ) ) ) { AddMOLfileError(sd->pStrErrStruct, "Metal was disconnected"); sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE; if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } #if ( bRELEASE_VERSION == 0 ) sd->bExtract |= EXTR_METAL_WAS_DISCONNECTED; #endif /* last parm=1 means find link between unchanged by Metal Disconnection components */ prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 1 ); if ( prep_inp_data->num_components < 0 ) { AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } { /* set parities for the disconnected structure */ int k, m, p; inp_ATOM *at = (prep_inp_data)->at; int num_at = (prep_inp_data)->num_inp_atoms; for ( k = 0; k < num_at; k ++ ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { if ( p & SB_PARITY_FLAG ) { at[k].sb_parity[m] = (p >> SB_PARITY_SHFT) & SB_PARITY_MASK; } } } } if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 1 ) ) { char szErrCode[16]; sprintf( szErrCode, "%d", i); AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); AddMOLfileError( sd->pStrErrStruct, szErrCode ); } #if ( REMOVE_ION_PAIRS_DISC_STRU == 1 ) if ( 0 < remove_ion_pairs( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; } #endif /* if prep_inp_data->nOldCompNumber[i] = iINChI+1 > 0 then component #(i+1) in prep_inp_data is identical to component #(iINChI+1) in prep_inp_data+1 */ } else if ( i < 0 ) { AddMOLfileError(sd->pStrErrStruct, "Cannot disconnect metal error"); sd->nStructReadError = i; sd->nErrorType = _IS_ERROR; goto exit_function; } } else { /* remove "disconnected structure parities" from the structure */ int k, m, p; inp_ATOM *at = (prep_inp_data)->at; int num_at = (prep_inp_data)->num_inp_atoms; for ( k = 0; k < num_at; k ++ ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { at[k].sb_parity[m] &= SB_PARITY_MASK; } } } exit_function: if ( sd->nErrorType < _IS_ERROR && prep_inp_data ) { if ( 0 < post_fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; } if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (prep_inp_data+1)->at && (prep_inp_data+1)->num_inp_atoms > 0 ) { if ( 0 < post_fix_odd_things( (prep_inp_data+1)->num_inp_atoms, (prep_inp_data+1)->at ) ) { AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); if ( sd->nErrorType < _IS_WARNING ) { sd->nErrorType = _IS_WARNING; } sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; } } } sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ return sd->nErrorType; } #ifndef COMPILE_ANSI_ONLY /* { */ /************************************************************************************************/ int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ) { int bDisplayEqu = 0; #ifndef TARGET_LIB_FOR_WINCHI /* Displaying equivalent input structures when disconnection has been done: */ /* in case of TARGET_LIB_FOR_WINCHI equivalence info is always unknown here and bOriginalReconnected=0 */ int bOriginalReconnected = iINChI < 0 && orig_inp_data && orig_inp_data->nEquLabels && (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); const char *lpszType = bOriginalReconnected? " (Reconnected)" : (iINChI < 0 )? "" : (iINChI == INCHI_BAS )? " (Preprocessed)" : (iINChI == INCHI_REC )? " (Reconnected)" : ""; int err = 0; /* Display the original structure */ bDisplayEqu = bShowStruct && ip->bDisplay && ip->dp.nEquLabels && 0 < ip->dp.nCurEquLabel && ip->dp.nCurEquLabel <= ip->dp.nNumEquSets; #else if(!DRAWDATA || !DRAWDATA_EXISTS) return 0; #endif #ifndef TARGET_API_LIB /******************************************************************** * Ask the user whether to process the input structure or quit */ if ( ip->bDisplay && inp_file->f != stdin ) { if ( user_quit(bDisplayEqu?"Enter=Display identical components, Esc=Stop ?" : "Enter=Display, Esc=Stop ?", ip->ulDisplTime) ) { sd->bUserQuit = 1; goto exit_function; } } #endif /****************************************************** * Display the whole input structure in console app */ /*^^^ #ifndef TARGET_LIB_FOR_WINCHI */ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined(TARGET_EXE_STANDALONE) ) if ( bShowStruct && ip->bDisplay ) { if ( bDisplayEqu ) { sprintf( szTitle, " Equ Set %d of %d, Input Structure #%ld.%s%s%s%s%s", ip->dp.nCurEquLabel, ip->dp.nNumEquSets, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); } else { sprintf( szTitle, "Input Structure #%ld.%s%s%s%s%s", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); } err = DisplayStructure( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); sd->bUserQuitComponent = (err==ESC_KEY); if ( !err ) { inchi_fprintf( stderr, "Cannot display the structure\n"); } } if( !bDisplayEqu ) { /* console output progress report */ if ( ip->bDisplay && !sd->bUserQuitComponent ) { if ( iINChI == 1 ) { if ( ip->bDisplay ) inchi_ios_eprint( log_file, "Processing (rec) structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); else inchi_fprintf( stderr, "Processing (rec) structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } else { if ( ip->bDisplay ) inchi_ios_eprint( log_file, "Processing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); else inchi_fprintf( stderr, "Processing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } } } #endif /****************************************************** * Store the whole input structure in GUI application */ #ifdef TARGET_LIB_FOR_WINCHI if ( ip->bDisplay && bINCHI_LIB_Flag ) #else if ( (ip->bDisplay || (ip->bCompareComponents & CMP_COMPONENTS)) && bINCHI_LIB_Flag ) #endif { int bBit, k, bReconnected, nComponent, bPreprocessed; for ( bBit = 1, k = 0; k < 8; k ++, bBit <<= 1 ) { /****************************************************************************** * bReconnected = k%2 (0 or 1) * nComponent = k/4 (0 or 1) * bPreprocessed = (k/2)%2 (0 or 1) ******************************************************************************/ if ( !(bINCHI_LIB_Flag & bBit) ) { continue; } bReconnected = k%2; nComponent = k/4; bPreprocessed = ((k/2)%2); sprintf( szTitle, "%s Structure #%ld.%s%s%s%s", bPreprocessed? "Preprocessed" : bReconnected? "Reconnected" : "Input", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); #ifdef TARGET_LIB_FOR_WINCHI if(DRAWDATA && DRAWDATA_EXISTS) { struct DrawData vDrawData; int nType = bPreprocessed? COMPONENT_ORIGINAL_PREPROCESSED : COMPONENT_ORIGINAL; if ( DRAWDATA_EXISTS( nComponent, bPreprocessed, bReconnected ) ) { sd->nErrorType = _IS_FATAL; sd->nErrorCode = CT_UNKNOWN_ERR; return -1; } vDrawData.pWindowData = CreateWinData_( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, 1, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode ); if( vDrawData.pWindowData != NULL ) { vDrawData.nComponent = nComponent; vDrawData.nType = nType; /* COMPONENT_ORIGINAL or COMPONENT_ORIGINAL_PREPROCESSED */ vDrawData.bReconnected = bReconnected; /* 0=>main; 1=>reconnected */ vDrawData.pWindowData->szTitle = _strdup(szTitle); vDrawData.szTitle = _strdup(szTitle); DRAWDATA(&vDrawData); if ( !nComponent ) { /* keep track of saved INCHI_LIB data */ orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; } } } #else if ( !nComponent ) { /* keep track of saved INCHI_LIB data */ orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; } #endif } } exit_function: return sd->bUserQuit; } #endif /* } COMPILE_ANSI_ONLY */ /************************************************************************************************/ int ProcessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, long num_inp, char *pStr, int nStrLen, unsigned char save_opt_bits) { int nRet = 0, nRet1, i, k, maxINChI=0; COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; /* [0]:non-taut, [1]:taut, [2]:intermediate taut struct */ NORM_CANON_FLAGS ncFlags; NORM_CANON_FLAGS *pncFlags = &ncFlags; ORIG_STRUCT OrigStruct; ORIG_STRUCT *pOrigStruct = NULL; int bSortPrintINChIFlags=0; #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) int ret1=0, ret2=0; #endif sd->bUserQuitComponent = 0; sd->bUserQuitComponentDisplay = 0; memset( composite_norm_data, 0, sizeof(composite_norm_data) ); memset( pncFlags, 0, sizeof(*pncFlags) ); /* ip->msec_LeftTime = ip->msec_MaxTime; */ /* start timeout countdown */ /* for testing only */ #if ( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0, ip->bFixNonUniformDraw ); #endif #if ( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) { long num_inp2 = num_inp; AddMOLfileError(sd->pStrErrStruct, "Underivatization error"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp2, pStr, nStrLen ); goto exit_function; /* output only if derivatives found */ } #endif /* UNDERIVATIZE == 1 */ #if ( RING2CHAIN == 1 ) /***** post v.1 feature *****/ if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) { long num_inp2 = num_inp; AddMOLfileError(sd->pStrErrStruct, "Ring to chain error"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp2, pStr, nStrLen ); goto exit_function; /* output only if derivatives found */ } #endif /* RING2CHAIN == 1 */ #if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) { goto exit_function; /* output only if derivatives or ring/chain found */ } #endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ /***** output MOLfile ***************/ if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { char szNumber[32]; int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ /*^^^ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) #if ( TEST_RENUMB_ATOMS != 1 ) /* log file / console output */ if ( log_file->f != stderr ) { if ( ip->bDisplay ) inchi_ios_eprint( log_file, "Writing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); else inchi_fprintf( stderr, "Writing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } #endif #endif ret1a = sprintf( szNumber, "Structure #%ld", num_inp ); ret2a = WriteOrigAtomDataToSDfile( orig_inp_data, output_file, szNumber, NULL, (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); goto exit_function; } /******* create full reversibility information **************/ if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) { pOrigStruct = &OrigStruct; memset( pOrigStruct, 0, sizeof(*pOrigStruct)); if ( FillOutOrigStruct( orig_inp_data, pOrigStruct, sd ) ) { AddMOLfileError(sd->pStrErrStruct, "Cannot interpret reversibility information"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; nRet = _IS_ERROR; } } /* create INChI for each connected component of the structure and optionally display them */ /* create INChI for the whole disconnected or original structure */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_BAS, inp_file, log_file, output_file, prb_file, orig_inp_data, prep_inp_data, composite_norm_data, num_inp, pStr, nStrLen, pncFlags ); nRet = inchi_max(nRet, nRet1); } if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { maxINChI = 1; } if ( nRet != _IS_FATAL && nRet != _IS_ERROR && (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { /* create INChI for the whole reconnected structure */ nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_REC, inp_file, log_file, output_file, prb_file, orig_inp_data, prep_inp_data, composite_norm_data,num_inp, pStr, nStrLen, pncFlags); nRet = inchi_max(nRet, nRet1); if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { maxINChI = 2; } } if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && (ip->nMode & REQ_MODE_STEREO) && !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && !bIsStructChiral( pINChI, sd->num_components ) ) { AddMOLfileError(sd->pStrErrStruct, "Not chiral"); } /*************************************/ /* Output err/warn messages */ /*************************************/ if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) { /* if successful then returns 0, otherwise returns _IS_FATAL */ /* exctract the structure if requested */ nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, num_inp, inp_file, log_file, output_file, prb_file,pStr, nStrLen ); nRet = inchi_max(nRet, nRet1); } } /************************************************/ /* sort and print INChI for the whole structure */ /************************************************/ if ( ip->nInputType != INPUT_INCHI ) { /* Prepare SaveOpt bits */ save_opt_bits = 0; if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) { if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) save_opt_bits |= SAVE_OPT_RECMET; if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) save_opt_bits |= SAVE_OPT_FIXEDH; if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) save_opt_bits |= SAVE_OPT_SLUUD; if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) save_opt_bits |= SAVE_OPT_SUU; if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) save_opt_bits |= SAVE_OPT_KET; if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) save_opt_bits |= SAVE_OPT_15T; /* Check if /SNon requested and turn OFF stereo bits if so */ if ( ! (ip->nMode & REQ_MODE_STEREO) ) { save_opt_bits &= ~SAVE_OPT_SUU; save_opt_bits &= ~SAVE_OPT_SLUUD; } } } if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { nRet1 = SortAndPrintINChI(output_file, pStr, nStrLen, log_file, ip, orig_inp_data, prep_inp_data, composite_norm_data, pOrigStruct, sd->num_components, sd->num_non_taut, sd->num_taut, sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, pINChI, pINChI_Aux, &bSortPrintINChIFlags, save_opt_bits); nRet = inchi_max(nRet, nRet1); } #ifndef COMPILE_ANSI_ONLY /* { */ /* display equivalent components on original or preprocessed structure(s) */ #ifndef TARGET_LIB_FOR_WINCHI if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ (ip->bCompareComponents & CMP_COMPONENTS) && !sd->bUserQuit && !sd->bUserQuitComponent ) { int j, ret, ord; int bDisplaySaved = ip->bDisplay; ORIG_ATOM_DATA *inp_data; AT_NUMB nEquSet; for ( ord = -1; ord < INCHI_NUM; ord ++ ) { switch( ord ) { case -1: j = INCHI_BAS; /* preprocessed non-tautomeric */ break; case 0: j = INCHI_REC; /* preprocessed tautomeric */ break; case 1: j = -1; /* original input */ break; default: continue; } inp_data = j < 0? orig_inp_data : prep_inp_data+j; if ( inp_data && inp_data->num_inp_atoms && inp_data->at && inp_data->nEquLabels && inp_data->nNumEquSets ) { for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { ip->dp.nEquLabels = inp_data->nEquLabels; ip->dp.nCurEquLabel = nEquSet; ip->dp.nNumEquSets = inp_data->nNumEquSets; ip->bDisplay = 1; /* force display if it was not requested */ ret = DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, inp_data, num_inp, j, 1 /*bShowStructure*/, 0 ); ip->dp.nEquLabels = NULL; ip->dp.nCurEquLabel = 0; ip->dp.nNumEquSets = 0; ip->bDisplay = bDisplaySaved; /* restore display option */ if ( ret ) { /* user pressed Esc */ goto exit_loop; } } } } exit_loop:; } #endif /* display composite results and equivalent components on composite results */ if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ ip->bDisplayCompositeResults ) { int iINChI; for ( iINChI = 0; iINChI < maxINChI && !sd->bUserQuitComponentDisplay; iINChI ++ ) { DisplayTheWholeCompositeStructure( ip, sd, num_inp, iINChI, pINChI[iINChI], pINChI_Aux[iINChI], orig_inp_data, prep_inp_data, composite_norm_data[iINChI] ); } #ifndef TARGET_LIB_FOR_WINCHI if( !ip->bDisplay && sd->bUserQuitComponentDisplay ) { sd->bUserQuit = 1; } #endif } #endif /* } COMPILE_ANSI_ONLY */ /* XML struct end tag */ if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); sd->bXmlStructStarted = -1; /* do not repeat same message */ nRet = _IS_FATAL; } else { sd->bXmlStructStarted = 0; /* do not continue xml output for this structure */ } } if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { /* Special mode: extract all good MOLfiles into the problem file * Do not extract any MOLfile that could not be processed (option /PGO) */ if ( prb_file && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); } #if ( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) else if ( prb_file->f && (bSortPrintINChIFlags & ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) ) { CopyMOLfile(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); } #endif } for ( i = 0; i < INCHI_NUM; i ++ ) { for ( k = 0; k < TAUT_NUM+1; k ++ ) { FreeCompAtomData( &composite_norm_data[i][k] ); } } FreeOrigStruct( pOrigStruct); /* FreeInpAtomData( inp_cur_data ); FreeInpAtomData( inp_norm_data[0] ); FreeInpAtomData( inp_norm_data[1] ); */ exit_function: return nRet; } /************************************************************************************************/ int bIsStructChiral( PINChI2 *pINChI2[INCHI_NUM], int num_components[] ) { int i, j, k; INChI *pINChI; INChI_Stereo *Stereo; for ( j = 0; j < INCHI_NUM; j ++ ) { /* disconnected / reconnected */ if ( !num_components[j] ) { continue; } for ( i = 0; i < num_components[j]; i ++ ) { /* component */ for ( k = 0; k < TAUT_NUM; k ++ ) { /* mobile/immobile H */ if ( (pINChI = pINChI2[j][i][k]) && !pINChI->bDeleted && pINChI->nNumberOfAtoms > 0 ) { if ( (Stereo = pINChI->Stereo) && Stereo->t_parity && Stereo->nNumberOfStereoCenters > 0 && Stereo->nCompInv2Abs ) { return 1; /* inversion changed stereo */ } if ( (Stereo = pINChI->StereoIsotopic) && Stereo->t_parity && Stereo->nNumberOfStereoCenters > 0 && Stereo->nCompInv2Abs ) { return 1; /* inversion changed stereo */ } } } } } return 0; } /************************************************************************************************/ int CreateOneStructureINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, PINChI2 *pINChI2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], int iINChI, INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], long num_inp, char *pStr, int nStrLen, NORM_CANON_FLAGS *pncFlags ) { int i, j, k, /*m,*/ nRet = 0; #ifndef TARGET_LIB_FOR_WINCHI int n; #ifndef COMPILE_ANSI_ONLY int err; #endif #endif PINChI2 *pINChI = NULL; PINChI_Aux2 *pINChI_Aux = NULL; INP_ATOM_DATA InpCurAtData; INP_ATOM_DATA *inp_cur_data; INP_ATOM_DATA InpNormAtData, InpNormTautData; INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; inchiTime ulTStart; #ifndef COMPILE_ANSI_ONLY int bShowStructure = 0; int bStructurePreprocessed = 0; /* All changes except disconnection */ int bStructureDisconnected = 0; int bAlsoOutputReconnected = 0, bINCHI_LIB_Flag = 0; COMP_ATOM_DATA *composite_norm_data = composite_norm_data2[iINChI]; INP_ATOM_DATA2 *all_inp_norm_data = NULL; #endif /* if ( orig_inp_data is NOT empty AND prep_inp_data[0] IS empty ) then: 1. copy orig_inp_data --> prep_inp_data[0] 2. fix odd things in prep_inp_data[0] 3. if( orig_inp_data->bDisconnectSalts ) then -- disconnect salts in prep_inp_data[0] 4. move protons to neutralize charges on heteroatoms 5. if( orig_inp_data->bDisconnectCoord ) then -- copy prep_inp_data[0] --> prep_inp_data[1] -- disconnect metals in prep_inp_data[0] [ This all is done in PreprocessOneStructure() ] iINChI = 0 ========= (normal/disconnected layer) 1. normalize prep_inp_data[0] in inp_norm_data[0,1] 2. create INChI[ iINChI ] out of inp_norm_data[0,1] iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 ================================================= (reconnected layer) 1. normalize prep_inp_data[1] in inp_norm_data[0,1] 2. create INChI[ iINChI ] out of inp_norm_data[0,1] */ #if ( TEST_RENUMB_ATOMS == 1 ) RENUMB_DATA RenumbData; RENUMB_DATA *pRenumbData = &RenumbData; #endif ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ #if ( TEST_RENUMB_ATOMS == 1 ) memset( pRenumbData, 0, sizeof(*pRenumbData) ); #endif inp_cur_data = &InpCurAtData; inp_norm_data[TAUT_NON] = &InpNormAtData; inp_norm_data[TAUT_YES] = &InpNormTautData; memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); #ifndef COMPILE_ANSI_ONLY memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); #endif if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { ; } else if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { return 0; /* nothing to do */ } if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { return 0; } /* m = iINChI; */ /* orig_inp_data index */ if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { AddMOLfileError(sd->pStrErrStruct, "Fatal undetermined program error"); sd->nStructReadError = 97; nRet = sd->nErrorType = _IS_FATAL; goto exit_function; } /******************************************************************* * * * * * Whole structure preprocessing: 1st step of the normalization * * * * Happen only on the first call to CreateOneStructureINChI() * * * * * *******************************************************************/ if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) { /* the structure has not been preprocessed */ if ( ip->msec_MaxTime ) { InchiTimeGet( &ulTStart ); } PreprocessOneStructure( sd, ip, orig_inp_data, prep_inp_data ); pncFlags->bTautFlags[iINChI][TAUT_YES] = pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; #ifndef COMPILE_ANSI_ONLY /* in this location the call happens once for each input structure, before preprocessing */ bStructurePreprocessed = (0 != (sd->bTautFlagsDone[INCHI_BAS] & ( TG_FLAG_MOVE_HPLUS2NEUTR_DONE | TG_FLAG_DISCONNECT_SALTS_DONE | TG_FLAG_MOVE_POS_CHARGES_DONE | TG_FLAG_FIX_ODD_THINGS_DONE ))); bStructureDisconnected = (0 != (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE)); bShowStructure = ( bStructurePreprocessed || bStructureDisconnected || prep_inp_data[0].num_components > 1); /* sd->bTautFlags[] contains output flags ip->bTautFlags contains input flags */ bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); bINCHI_LIB_Flag = 0; /*************** output structures to TARGET_LIB_FOR_WINCHI conditions ********************* * * Send to TARGET_LIB_FOR_WINCHI: * * type component conditions * * COMPONENT_ORIGINAL #0: (num_components > 1) * COMPONENT_ORIGINAL_PREPROCESSED #0: (num_components > 1) && (preprocessed) * COMPONENT_ORIGINAL #1: (num_components = 1) && (preprocessed) * * Flags explanation: * MAIN => iINChI=0, RECN => iINChI=1 (Reconnected) * ORIG => Original, PREP => Preprocessed * * Possible flags: k * * COMP_ORIG_0_MAIN 0x0001 0 COMPONENT_ORIGINAL, bMain, component #0 * COMP_ORIG_0_RECN 0x0002 1 COMPONENT_ORIGINAL, bRecn, component #0 * * COMP_PREP_0_MAIN 0x0004 2 COMPONENT_ORIGINAL_PREPROCESSED, bMain, component #0 * COMP_PREP_0_RECN 0x0008 3 COMPONENT_ORIGINAL_PREPROCESSED, bRecn, component #0 * * COMP_ORIG_1_MAIN 0x0010 4 COMPONENT_ORIGINAL, bMain, component #1 * COMP_ORIG_1_RECN 0x0020 5 COMPONENT_ORIGINAL, bRecn, component #1 * * bReconnected = k%2 (0 or 1) * nComponent = k/4 (0 or 1) * bPreprocessed = (k/2)%2 (0 or 1) * ******************************************************************************/ /* Original -> Main, component #0, Original */ if ( prep_inp_data[INCHI_BAS].num_components > 1 ) { bINCHI_LIB_Flag |= COMP_ORIG_0_MAIN; } else /* Original -> Main, component #1, Original */ if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { bINCHI_LIB_Flag |= COMP_ORIG_1_MAIN; /* preprocessed will be added when output canonicalization results */ } if ( bAlsoOutputReconnected ) { /* Original -> Reconnected, component #0, Original */ if ( prep_inp_data[INCHI_REC].num_components > 1 ) { bINCHI_LIB_Flag |= COMP_ORIG_0_RECN; } else /* Original -> Reconnected, component #1, Original */ if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { bINCHI_LIB_Flag |= COMP_ORIG_1_RECN; /* preprocessed will be added when output canonicalization results */ } } if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); } /* display the ORIGINAL, UN-PREPROCESSED structure */ if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, orig_inp_data, num_inp, -1, bShowStructure, bINCHI_LIB_Flag ) ) { goto exit_function; } #endif switch (sd->nErrorType) { case _IS_ERROR: case _IS_FATAL: /* error message */ nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, prep_inp_data, &num_inp, pStr, nStrLen ); goto exit_cycle; } } /* tranfer flags from INChI_Aux to sd */ #ifndef COMPILE_ANSI_ONLY /* { */ /******************************************/ /* Displaying the structures */ /* Only under WIN32 */ /******************************************/ if ( ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && prep_inp_data[iINChI].num_components > 1) { all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); } /* Display the input structure AFTER PREPROCESSING */ switch ( iINChI ) { case INCHI_BAS: /*------------ Possibly disconnected structure -------------------*/ bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( TG_FLAG_MOVE_HPLUS2NEUTR_DONE | TG_FLAG_DISCONNECT_SALTS_DONE | TG_FLAG_MOVE_POS_CHARGES_DONE | TG_FLAG_MOVE_CHARGE_COORD_DONE | TG_FLAG_DISCONNECT_COORD_DONE | TG_FLAG_FIX_ODD_THINGS_DONE )); bINCHI_LIB_Flag = 0; /* Preprocessed/Main -> Main, component #0, Preprocessed */ if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { bINCHI_LIB_Flag |= COMP_PREP_0_MAIN; } bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1); break; case INCHI_REC: /*------------ Reconnected structure ------------------------------*/ bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); if ( !bAlsoOutputReconnected ) { break; } bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( TG_FLAG_MOVE_HPLUS2NEUTR_DONE | TG_FLAG_DISCONNECT_SALTS_DONE | TG_FLAG_MOVE_POS_CHARGES_DONE | TG_FLAG_FIX_ODD_THINGS_DONE )); bINCHI_LIB_Flag = 0; /* Preprocessed/Reconnected -> Reconnected, component #0, Preprocessed */ if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { bINCHI_LIB_Flag |= COMP_PREP_0_RECN; } bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1 ); break; default: bShowStructure = 0; } if ( prep_inp_data[iINChI].num_inp_atoms > 0 ) { if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, prep_inp_data+iINChI, num_inp, iINChI, bShowStructure, bINCHI_LIB_Flag ) ) { goto exit_function; } } #endif /* } ifndef COMPILE_ANSI_ONLY */ /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ /* assign values to sd->num_components[] */ MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); if ( k ) { AddMOLfileError(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); sd->nStructReadError = 99; sd->nErrorType = _IS_FATAL; goto exit_function; } pINChI = pINChI2[iINChI]; pINChI_Aux = pINChI_Aux2[iINChI]; /**************************************************************************/ /* */ /* */ /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ /* */ /* */ /* O N E B Y O N E */ /* */ /* */ /**************************************************************************/ for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) { if ( ip->msec_MaxTime ) { InchiTimeGet( &ulTStart ); } #ifndef TARGET_LIB_FOR_WINCHI /* { */ #if ( bREUSE_INCHI == 1 ) if ( iINChI == INCHI_REC && (!ip->bDisplay && !ip->bDisplayCompositeResults && !(ip->bCompareComponents & CMP_COMPONENTS) || sd->bUserQuitComponentDisplay) ) { /* reconnected structure (06-20-2005: added "&& !ip->bDisplayCompositeResults" to display composite structure) */ int m = iINChI-1; /* find whether we have already calculated this INChI in basic (disconnected) layer */ for ( j = n = 0; j < prep_inp_data[m].num_components; j ++ ) { if ( i+1 == prep_inp_data[m].nOldCompNumber[j] && (pINChI2[m][j][TAUT_NON] || pINChI2[m][j][TAUT_YES]) ) { /* yes, we have already done this */ if ( !n++ ) { memcpy( pINChI +i, pINChI2 [m]+j, sizeof(pINChI[0])); memcpy( pINChI_Aux+i, pINChI_Aux2[m]+j, sizeof(pINChI_Aux[0])); for ( k = 0; k < TAUT_NUM; k ++ ) { if ( pINChI[i][k] ) { pINChI[i][k]->nRefCount ++; if ( pINChI[i][k]->nNumberOfAtoms > 0 ) { switch( k ) { case TAUT_NON: sd->num_non_taut[iINChI] ++; break; case TAUT_YES: if ( pINChI[i][k]->lenTautomer > 0 ) { sd->num_taut[iINChI] ++; } else if ( !pINChI[i][TAUT_NON] || !pINChI[i][TAUT_NON]->nNumberOfAtoms ) { sd->num_non_taut[iINChI] ++; } break; } } } if ( pINChI_Aux[i][k] ) { pINChI_Aux[i][k]->nRefCount ++; } } } } } if ( n == 1 ) { continue; } if ( n > 1 ) { /* ith component is equivalent to more than one another component */ AddMOLfileError(sd->pStrErrStruct, "Cannot distinguish components"); sd->nStructReadError = 99; sd->nErrorType = _IS_ERROR; goto exit_function; } } #endif #endif /* } TARGET_LIB_FOR_WINCHI */ /*****************************************************/ /* a) allocate memory and extract current component */ /*****************************************************/ nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, i, num_inp, pStr, nStrLen ); if ( ip->msec_MaxTime ) { ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); } switch ( nRet ) { case _IS_ERROR: case _IS_FATAL: goto exit_cycle; } #ifndef TARGET_API_LIB /* console request: Display the component? */ if ( ip->bDisplay && inp_file->f != stdin ) { if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) { sd->bUserQuitComponent = 1; break; } } #endif #ifndef COMPILE_ANSI_ONLY /* { */ /* b) Display the extracted original component structure */ if ( inp_cur_data->at && ip->bDisplay && !sd->bUserQuitComponentDisplay ) { if ( cur_prep_inp_data->num_components == 1 ) { sprintf( szTitle, "%sInput Structure #%ld.%s%s%s%s%s", bStructurePreprocessed? "Preprocessed ":"", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); } else { sprintf( szTitle, "Component #%d of %d, Input Structure #%ld.%s%s%s%s%s", i+1, cur_prep_inp_data->num_components, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); } #ifndef TARGET_LIB_FOR_WINCHI err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); sd->bUserQuitComponentDisplay = (err==ESC_KEY); if ( !err ) { inchi_fprintf( stderr, "Cannot display the structure\n"); } #else if(DRAWDATA && DRAWDATA_EXISTS) { struct DrawData vDrawData; int nType = COMPONENT_ORIGINAL; vDrawData.pWindowData = CreateWinData_( inp_cur_data->at, inp_cur_data->num_at, 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, 1 /* display isotopic if present */, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode ); if( vDrawData.pWindowData != NULL ) { if ( DRAWDATA_EXISTS ( i+1, nType, iINChI ) ) { /* i = component number */ nType = COMPONENT_ORIGINAL_PREPROCESSED; } vDrawData.nComponent = i+1; vDrawData.nType = nType; vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ vDrawData.szTitle = _strdup(szTitle); vDrawData.pWindowData->szTitle = _strdup(szTitle); DRAWDATA(&vDrawData); } } #endif } #endif /* } COMPILE_ANSI_ONLY */ #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ /****************************************************************************/ /* R E N U M B E R I N G (testing only) Part I STARTS here */ /****************************************************************************/ RenumberingTestInit( pRenumbData, inp_cur_data ); if ( log_file != stderr ) { if ( ip->bDisplay ) inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); else inchi_ios_eprint( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); } /****************************************************************************/ /* R E N U M B E R I N G (testing only) Part I ENDS here */ /****************************************************************************/ #endif /* } TEST_RENUMB_ATOMS */ /*******************************************************************************/ /* */ /* N O R M A L I Z A T I O N a n d C A N O N I C A L I Z A T I O N */ /* */ /* (both tautomeric and non-tautomeric if requested) */ /* */ /*******************************************************************************/ /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ /*******************************************************************************/ nRet = CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, iINChI, i, num_inp, inp_norm_data, pncFlags, log_file ); #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ /****************************************************************************/ /* R E N U M B E R I N G (testing only) Part II STARTS here */ /****************************************************************************/ if ( !nRet ) { nRet = RenumberingTest( pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, orig_inp_data, iINChI, pRenumbData, inp_cur_data, inp_norm_data, sd, ip, szTitle, log_file, prb_file, i, num_inp, pncFlags); } RenumberingTestUninit( pRenumbData ); /****************************************************************************/ /* R E N U M B E R I N G (testing only) Part II ENDS here */ /****************************************************************************/ #endif /* } TEST_RENUMB_ATOMS */ /* d) Display one component structure and/or INChI results only if there was no error */ #ifndef COMPILE_ANSI_ONLY /* { */ if ( !nRet ) { /* output one component INChI to the stdout if requested */ /* if ( ip->bDisplayEachComponentINChI ) { int cur_num_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); int cur_num_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); if ( ip->bDisplayEachComponentINChI && cur_num_non_taut + cur_num_taut ) { SortAndPrintINChI(stdout, pStr, nStrLen, NULL, ip, 1, cur_num_non_taut, cur_num_taut, num_inp, pINChI+i, pINChI_Aux+i, save_opt_bits); } } */ /************************************************************************** * display from one up to 4 structure pictures-results for each component * * Enable buttons: * * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * * inp_norm_data[0]->bHasIsotopicLayer * * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * * inp_norm_data[1]->bHasIsotopicLayer * **************************************************************************/ int bIsotopic, bTautomeric, bDisplayTaut, bHasIsotopicLayer, bFixedBondsTaut, m_max, m, nNumDisplayedFixedBondTaut=0; for ( j = 0; ip->bDisplay && !sd->bUserQuitComponentDisplay && j < TAUT_NUM; j ++ ) { if ( inp_norm_data[j]->bExists && !inp_norm_data[j]->bDeleted ) { bTautomeric = (pINChI[i][j]->lenTautomer > 0); /* same as (inp_norm_data[j]->bTautomeric > 0) */ /* if requested tautomeric and no tautmerism found then do not say mobile or fixed H. 2004-10-27 */ bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !bTautomeric)? -1 : bTautomeric; bHasIsotopicLayer = (inp_norm_data[j]->bHasIsotopicLayer > 0); for ( k = 0; k <= bHasIsotopicLayer; k ++ ) { bIsotopic = (k > 0); m_max = inp_norm_data[j]->at_fixed_bonds && inp_norm_data[j]->bTautPreprocessed? 1 : 0; for ( m = m_max; 0 <= m; m -- ) { bFixedBondsTaut = (m>0); nNumDisplayedFixedBondTaut += bFixedBondsTaut; /* display only one time */ /* added number of components, added another format for a single component case - DCh */ if ( cur_prep_inp_data->num_components > 1 ) { sprintf( szTitle, "%s Component #%d of %d, Structure #%ld%s%s.%s%s%s%s%s", bFixedBondsTaut? "Preprocessed":"Result for", i+1, cur_prep_inp_data->num_components, num_inp, bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", bIsotopic? ", isotopic":"", SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); } else { sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", bFixedBondsTaut? "Preprocessed":"Result for", num_inp, bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", bIsotopic? ", isotopic":"", SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); } #ifndef TARGET_LIB_FOR_WINCHI if ( bFixedBondsTaut && nNumDisplayedFixedBondTaut != 1 ) continue; if ( bFixedBondsTaut ) { err = DisplayStructure( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, inp_norm_data[j]->num_removed_H, 0 /*bAdd_DT_to_num_H*/, inp_norm_data[j]->nNumRemovedProtons, inp_norm_data[j]->nNumRemovedProtonsIsotopic, bHasIsotopicLayer, j, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); } else { err = DisplayStructure( inp_norm_data[j]->at, inp_norm_data[j]->num_at, 0, 0 /*bAdd_DT_to_num_H*/, 0, NULL, k, j, pINChI[i], pINChI_Aux[i], ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); } if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { break; } #else if(DRAWDATA && !bFixedBondsTaut) { struct DrawData vDrawData; vDrawData.pWindowData = CreateWinData_( inp_norm_data[j]->at, inp_norm_data[j]->num_at, 0, 0 /* bAdd_DT_to_num_H */, 0, NULL, k, j, pINChI[i], pINChI_Aux[i], ip->bAbcNumbers, &ip->dp, ip->nMode ); if( vDrawData.pWindowData != NULL ) { int nType; vDrawData.nComponent = i+1; if( bTautomeric == 0 ) nType = (bIsotopic == 0) ? COMPONENT_BN: COMPONENT_BI; else nType = (bIsotopic == 0) ? COMPONENT_TN: COMPONENT_TI; vDrawData.nType = nType; vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ vDrawData.szTitle = _strdup(szTitle); vDrawData.pWindowData->szTitle = _strdup(szTitle); DRAWDATA(&vDrawData); } } else if(DRAWDATA && bFixedBondsTaut) { struct DrawData vDrawData; if ( (ip->bCompareComponents & CMP_COMPONENTS) && !(ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) && !bIsotopic == !inp_norm_data[j]->bHasIsotopicLayer ) { vDrawData.pWindowData = CreateWinData_( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, inp_norm_data[j]->num_removed_H, 0 /* bAdd_DT_to_num_H */, inp_norm_data[j]->nNumRemovedProtons, inp_norm_data[j]->nNumRemovedProtonsIsotopic, k, j, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode ); } else { continue; } if( vDrawData.pWindowData != NULL ) { vDrawData.nComponent = i+1; vDrawData.nType = COMPONENT_ORIGINAL_PREPROCESSED; vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ vDrawData.szTitle = _strdup(szTitle); vDrawData.pWindowData->szTitle = _strdup(szTitle); DRAWDATA(&vDrawData); } } #endif } } } } /* save normalized components for composite display */ if ( ip->bDisplayCompositeResults && all_inp_norm_data ) { for ( j = 0; j < TAUT_NUM; j ++ ) { if ( inp_norm_data[j]->bExists ) { all_inp_norm_data[i][j] = *inp_norm_data[j]; memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); } } } } #endif /* } COMPILE_ANSI_ONLY */ if ( nRet ) { nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, inp_file, log_file, output_file, prb_file,pStr, nStrLen ); break; } } /**************************************************************************/ /* */ /* */ /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ /* */ /* C O M P O N E N T S O N E B Y O N E */ /* */ /* */ /**************************************************************************/ exit_cycle: #if ( TEST_RENUMB_ATOMS == 1 ) /* { */ if ( pRenumbData->bRenumbErr && (!nRet || nRet==_IS_WARNING) ) { sd->nErrorCode = pRenumbData->bRenumbErr; nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, -1, num_inp, inp_file, log_file, output_file, prb_file,pStr, nStrLen ); /* nRet = _IS_ERROR; */ sd->nErrorCode = 0; nRet = 0; } #endif /* } TEST_RENUMB_ATOMS */ switch ( nRet ) { case _IS_FATAL: case _IS_ERROR: break; default: #ifndef COMPILE_ANSI_ONLY /* { */ /* composite results picture(s) */ if ( all_inp_norm_data ) { int res = CreateCompositeNormAtom( composite_norm_data, all_inp_norm_data, pINChI, pINChI_Aux, prep_inp_data[iINChI].num_components, ip->nMode ); /* for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { FreeInpAtomData( &all_inp_norm_data[i][k] ); } } inchi_free( all_inp_norm_data ); all_inp_norm_data = NULL; */ } #endif /* } COMPILE_ANSI_ONLY */ break; } #ifndef COMPILE_ANSI_ONLY /* { */ /* avoid memory leaks in case of error */ if ( all_inp_norm_data ) { for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { for ( k = 0; k < TAUT_NUM; k ++ ) { FreeInpAtomData( &all_inp_norm_data[i][k] ); } } inchi_free( all_inp_norm_data ); all_inp_norm_data = NULL; } #endif /* } COMPILE_ANSI_ONLY */ FreeInpAtomData( inp_cur_data ); for ( i = 0; i < TAUT_NUM; i ++ ) { FreeInpAtomData( inp_norm_data[i] ); } exit_function: return nRet; } #ifndef COMPILE_ANSI_ONLY /* { */ /****************************************************************************/ int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ) { int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; int ret = 0, indicator = 1; inp_ATOM *at, *at_from; memset( num_comp, 0, sizeof(num_comp) ); memset( num_taut, 0, sizeof(num_taut) ); memset( num_del, 0, sizeof(num_taut) ); /* count taut and non-taut components */ for ( j = 0; j < TAUT_NUM; j ++ ) { num_comp[j] = num_taut[j] = 0; for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bExists ) { num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); num_comp[j] ++; num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); } } } /* count intermediate taut structure components */ if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { /* num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; */ for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bExists && (all_inp_norm_data[i][j].bDeleted || all_inp_norm_data[i][j].bTautomeric && all_inp_norm_data[i][j].at_fixed_bonds && all_inp_norm_data[i][j].bTautPreprocessed) ) { num_comp[TAUT_INI] ++; } } } /* count atoms and allocate composite atom data */ for ( jj = 0; jj <= TAUT_INI; jj ++ ) { num_at[jj] = num_inp_at[jj] = 0; j = inchi_min (jj, TAUT_YES); if ( num_comp[jj] ) { for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bDeleted ) continue; /* find k = the normaized structure index */ if ( jj == TAUT_INI ) { if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && !all_inp_norm_data[i][j].bDeleted ) { k = ALT_TAUT(j); } else if ( all_inp_norm_data[i][j].bExists ) { k = j; } else { continue; } } else { if ( all_inp_norm_data[i][j].bExists ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { k = ALT_TAUT(j); } else { continue; } } num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; } if ( num_inp_at[jj] ) { if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) goto exit_error; composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; } } } /* fill out composite atom */ for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { j = inchi_min (jj, TAUT_YES); if ( num_comp[jj] ) { tot_num_at = 0; tot_num_H = 0; for ( i = 0; i < num_components; i ++ ) { if ( all_inp_norm_data[i][j].bDeleted ) { composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; } continue; } nNumRemovedProtons = 0; k = TAUT_NUM; /* find k = the normaized structure index */ if ( jj == TAUT_INI ) { if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { k = ALT_TAUT(j); } else if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { k = j; } else { continue; } } else { if ( all_inp_norm_data[i][j].bExists ) { k = j; } else if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { k = ALT_TAUT(j); } else { continue; } } /* copy main atoms */ cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ if ( (tot_num_at + cur_num_at) > num_at[jj] || (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { goto exit_error; /* miscount */ } at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ /* shift neighbors of main atoms */ for ( n = 0; n < cur_num_at; n ++, at ++ ) { for ( m = 0; m < at->valence; m ++ ) { at->neighbor[m] += tot_num_at; } } /* copy explicit H */ if ( cur_num_H ) { at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ memcpy( at, at_from+cur_num_at, sizeof(composite_norm_data[0].at[0]) * cur_num_H ); /* shift neighbors of explicit H atoms */ for ( n = 0; n < cur_num_H; n ++, at ++ ) { for ( m = 0; m < at->valence; m ++ ) { at->neighbor[m] += tot_num_at; } } } /* composite counts */ composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; } /* composite_norm_data[j].num_at += cur_num_at + cur_num_H; composite_norm_data[j].num_removed_H += cur_num_H; */ /* total count */ tot_num_at += cur_num_at; tot_num_H += cur_num_H; /* offset for the next component */ if ( composite_norm_data[jj].nOffsetAtAndH ) { composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; } } if ( tot_num_at != num_at[jj] || num_at[jj] + tot_num_H != num_inp_at[jj] ) { goto exit_error; /* miscount */ } composite_norm_data[jj].bExists = (tot_num_at>0); ret |= indicator; } } return ret; exit_error: return ret; } #endif /* } COMPILE_ANSI_ONLY */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/sha2.c000066400000000000000000000326251271037650300225620ustar00rootroot00000000000000/* * FIPS-180-2 compliant SHA-256 implementation * * Copyright (C) Brainspark B.V. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* * The SHA-256 standard was published by NIST in 2002. * * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf */ #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 #endif #include #include #include "sha2.h" /* * 32-bit integer manipulation macros (big endian) */ #ifndef GET_UINT32_BE #define GET_UINT32_BE(n,b,i) \ { \ (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ | ( (unsigned long) (b)[(i) + 1] << 16 ) \ | ( (unsigned long) (b)[(i) + 2] << 8 ) \ | ( (unsigned long) (b)[(i) + 3] ); \ } #endif #ifndef PUT_UINT32_BE #define PUT_UINT32_BE(n,b,i) \ { \ (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ (b)[(i) + 3] = (unsigned char) ( (n) ); \ } #endif /* * SHA-2 context setup */ void sha2_starts( sha2_context *ctx ) { ctx->total[0] = 0; ctx->total[1] = 0; ctx->state[0] = 0x6A09E667; ctx->state[1] = 0xBB67AE85; ctx->state[2] = 0x3C6EF372; ctx->state[3] = 0xA54FF53A; ctx->state[4] = 0x510E527F; ctx->state[5] = 0x9B05688C; ctx->state[6] = 0x1F83D9AB; ctx->state[7] = 0x5BE0CD19; } static void sha2_process( sha2_context *ctx, unsigned char data[64] ) { unsigned long temp1, temp2, W[64]; unsigned long A, B, C, D, E, F, G, H; GET_UINT32_BE( W[0], data, 0 ); GET_UINT32_BE( W[1], data, 4 ); GET_UINT32_BE( W[2], data, 8 ); GET_UINT32_BE( W[3], data, 12 ); GET_UINT32_BE( W[4], data, 16 ); GET_UINT32_BE( W[5], data, 20 ); GET_UINT32_BE( W[6], data, 24 ); GET_UINT32_BE( W[7], data, 28 ); GET_UINT32_BE( W[8], data, 32 ); GET_UINT32_BE( W[9], data, 36 ); GET_UINT32_BE( W[10], data, 40 ); GET_UINT32_BE( W[11], data, 44 ); GET_UINT32_BE( W[12], data, 48 ); GET_UINT32_BE( W[13], data, 52 ); GET_UINT32_BE( W[14], data, 56 ); GET_UINT32_BE( W[15], data, 60 ); #define SHR(x,n) ((x & 0xFFFFFFFF) >> n) #define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) #define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) #define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) #define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) #define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) #define F0(x,y,z) ((x & y) | (z & (x | y))) #define F1(x,y,z) (z ^ (x & (y ^ z))) #define R(t) \ ( \ W[t] = S1(W[t - 2]) + W[t - 7] + \ S0(W[t - 15]) + W[t - 16] \ ) #define P(a,b,c,d,e,f,g,h,x,K) \ { \ temp1 = h + S3(e) + F1(e,f,g) + K + x; \ temp2 = S2(a) + F0(a,b,c); \ d += temp1; h = temp1 + temp2; \ } A = ctx->state[0]; B = ctx->state[1]; C = ctx->state[2]; D = ctx->state[3]; E = ctx->state[4]; F = ctx->state[5]; G = ctx->state[6]; H = ctx->state[7]; P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); ctx->state[0] += A; ctx->state[1] += B; ctx->state[2] += C; ctx->state[3] += D; ctx->state[4] += E; ctx->state[5] += F; ctx->state[6] += G; ctx->state[7] += H; } /* * SHA-2 process buffer */ void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ) { int fill; unsigned long left; if( ilen <= 0 ) return; left = ctx->total[0] & 0x3F; fill = 64 - left; ctx->total[0] += ilen; ctx->total[0] &= 0xFFFFFFFF; if( ctx->total[0] < (unsigned long) ilen ) ctx->total[1]++; if( left && ilen >= fill ) { memcpy( (void *) (ctx->buffer + left), (void *) input, fill ); sha2_process( ctx, ctx->buffer ); input += fill; ilen -= fill; left = 0; } while( ilen >= 64 ) { sha2_process( ctx, input ); input += 64; ilen -= 64; } if( ilen > 0 ) { memcpy( (void *) (ctx->buffer + left), (void *) input, ilen ); } } static const unsigned char sha2_padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * SHA-2 final digest */ void sha2_finish( sha2_context *ctx, unsigned char output[32] ) { unsigned long last, padn; unsigned long high, low; unsigned char msglen[8]; high = ( ctx->total[0] >> 29 ) | ( ctx->total[1] << 3 ); low = ( ctx->total[0] << 3 ); PUT_UINT32_BE( high, msglen, 0 ); PUT_UINT32_BE( low, msglen, 4 ); last = ctx->total[0] & 0x3F; padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); sha2_update( ctx, (unsigned char *) sha2_padding, padn ); sha2_update( ctx, msglen, 8 ); PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[2], output, 8 ); PUT_UINT32_BE( ctx->state[3], output, 12 ); PUT_UINT32_BE( ctx->state[4], output, 16 ); PUT_UINT32_BE( ctx->state[5], output, 20 ); PUT_UINT32_BE( ctx->state[6], output, 24 ); PUT_UINT32_BE( ctx->state[7], output, 28 ); } /* * Output = SHA-2( file contents ) */ int sha2_file( char *path, unsigned char output[32] ) { FILE *f; size_t n; sha2_context ctx; unsigned char buf[1024]; if( ( f = fopen( path, "rb" ) ) == NULL ) return( 1 ); sha2_starts( &ctx ); while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) sha2_update( &ctx, buf, (int) n ); sha2_finish( &ctx, output ); fclose( f ); return( 0 ); } /* * Output = SHA-2( input buffer ) */ void sha2_csum( unsigned char *input, int ilen, unsigned char output[32] ) { sha2_context ctx; sha2_starts( &ctx ); sha2_update( &ctx, input, ilen ); sha2_finish( &ctx, output ); } /* * Output = HMAC-SHA-2( input buffer, hmac key ) */ void sha2_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen, unsigned char output[32] ) { int i; sha2_context ctx; unsigned char k_ipad[64]; unsigned char k_opad[64]; unsigned char tmpbuf[32]; memset( k_ipad, 0x36, 64 ); memset( k_opad, 0x5C, 64 ); for( i = 0; i < keylen; i++ ) { if( i >= 64 ) break; k_ipad[i] ^= key[i]; k_opad[i] ^= key[i]; } sha2_starts( &ctx ); sha2_update( &ctx, k_ipad, 64 ); sha2_update( &ctx, input, ilen ); sha2_finish( &ctx, tmpbuf ); sha2_starts( &ctx ); sha2_update( &ctx, k_opad, 64 ); sha2_update( &ctx, tmpbuf, 32 ); sha2_finish( &ctx, output ); memset( k_ipad, 0, 64 ); memset( k_opad, 0, 64 ); memset( tmpbuf, 0, 32 ); memset( &ctx, 0, sizeof( sha2_context ) ); } #ifdef SELF_TEST /* * FIPS-180-2 test vectors */ static const char sha2_test_str[3][57] = { { "abc" }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, { "" } }; static const unsigned char sha2_test_sum[3][32] = { { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } }; /* * Checkup routine */ int sha2_self_test( void ) { int i, j; unsigned char buf[1000]; unsigned char sha2sum[32]; sha2_context ctx; for( i = 0; i < 3; i++ ) { printf( " SHA-256 test #%d: ", i + 1 ); sha2_starts( &ctx ); if( i < 2 ) sha2_update( &ctx, (unsigned char *) sha2_test_str[i], strlen( sha2_test_str[i] ) ); else { memset( buf, 'a', 1000 ); for( j = 0; j < 1000; j++ ) sha2_update( &ctx, buf, 1000 ); } sha2_finish( &ctx, sha2sum ); if( memcmp( sha2sum, sha2_test_sum[i], 20 ) != 0 ) { printf( "failed\n" ); return( 1 ); } printf( "passed\n" ); } printf( "\n" ); return( 0 ); } #else int sha2_self_test( void ) { return( 0 ); } #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/sha2.h000066400000000000000000000065311271037650300225640ustar00rootroot00000000000000/** * \file sha2.h */ /* * FIPS-180-2 compliant SHA-256 implementation * * Copyright (C) Brainspark B.V. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ /* * The SHA-256 standard was published by NIST in 2002. * * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf */ #ifndef _SHA2_H #define _SHA2_H #ifdef __cplusplus extern "C" { #endif /** * \brief SHA-256 context structure */ typedef struct { unsigned long total[2]; /*!< number of bytes processed */ unsigned long state[8]; /*!< intermediate digest state */ unsigned char buffer[64]; /*!< data block being processed */ } sha2_context; /** * \brief SHA-256 context setup * * \param ctx SHA-256 context to be initialized */ void sha2_starts( sha2_context *ctx ); /** * \brief SHA-256 process buffer * * \param ctx SHA-256 context * \param input buffer holding the data * \param ilen length of the input data */ void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ); /** * \brief SHA-256 final digest * * \param ctx SHA-256 context * \param output SHA-256 checksum result */ void sha2_finish( sha2_context *ctx, unsigned char output[32] ); /** * \brief Output = SHA-256( input buffer ) * * \param input buffer holding the data * \param ilen length of the input data * \param output SHA-256 checksum result */ void sha2_csum( unsigned char *input, int ilen, unsigned char output[32] ); /** * \brief Output = SHA-256( file contents ) * * \param path input file name * \param output SHA-256 checksum result * \return 0 if successful, or 1 if fopen failed */ int sha2_file( char *path, unsigned char output[32] ); /** * \brief Output = HMAC-SHA-256( input buffer, hmac key ) * * \param key HMAC secret key * \param keylen length of the HMAC key * \param input buffer holding the data * \param ilen length of the input data * \param output HMAC-SHA-256 result */ void sha2_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen, unsigned char output[32] ); /** * \brief Checkup routine * * \return 0 if successful, or 1 if the test failed */ int sha2_self_test( void ); #ifdef __cplusplus } #endif #endif /* sha2.h */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/strutil.c000066400000000000000000006123241271037650300234330ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include #include "mode.h" #include "inpdef.h" #include "util.h" #include "ichi.h" #include "strutil.h" #include "ichierr.h" #include "ichicomp.h" #include "extr_ct.h" #include "ichister.h" #include "ichi_io.h" #define FIX_P_IV_Plus_O_Minus /* added fix to remove_ion_pairs() -- 2010-03-17 DT */ /* local prototypes */ int cmp_components( const void *a1, const void *a2 ); /*int mark_one_struct_component( inp_ATOM* at, int j, AT_NUMB *mark, AT_NUMB num_disconnected_components );*/ INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds); int RemoveInpAtBond( inp_ATOM *at, int iat, int k ); int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents); int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ); int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ); int DisconnectAmmoniumSalt ( inp_ATOM *at, int i, int iO, int k, S_CHAR *num_explicit_H ); /*int bIsMetalSalt( inp_ATOM *at, int i ); - moved to strutil,h */ int DisconnectMetalSalt( inp_ATOM *at, int i ); int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence); int get_iat_number( int el_number, const int el_num[], int el_num_len ); int tot_unsat( int unsat[] ); int max_unsat( int unsat[] ); double dist3D( inp_ATOM *at1, inp_ATOM *at2 ); double dist2D( inp_ATOM *at1, inp_ATOM *at2 ); double dist_from_segm( double x, double y, double x1, double y1, double x2, double y2); int segments_intersect( double x11, double y11, double x12, double y12, /* segment #1 */ double x21, double y21, double x22, double y22 ); double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents, double min_dist[], int num_segm ); int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM); int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2); #ifndef NUMH #define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) #define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) #endif /************************************************************************/ int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2) { int i, neigh1, num_rad1=0, num_rad2=0; inp_ATOM *a = at+i1, *b; if ( RADICAL_DOUBLET != a->radical ) return -1; for ( i = 0; i < a->valence; i ++ ) { b = at + (neigh1 = (int)a->neighbor[i]); if ( RADICAL_DOUBLET == b->radical ) { num_rad1 ++; *ineigh1 = i; } } if ( 1 == num_rad1 ) { a = at + (neigh1 = (int)a->neighbor[*ineigh1]); for ( i = 0; i < a->valence; i ++ ) { b = at +(int)a->neighbor[i]; if ( RADICAL_DOUBLET == b->radical ) { num_rad2 ++; *ineigh2 = i; } } if ( 1 == num_rad2 ) { return neigh1; } } return -1; } /************************************************************************/ int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ) { /* 0 1 2 3 4 5 6 7 8 9 */ static const char el[] = "N;P;As;Sb;O;S;Se;Te;"; /* 8 elements + C, Si */ static U_CHAR en[10]; /* same number: 8 elements */ static int ne=0, ne2; /* will be 8 and 10 */ static int el_number_P; static int el_number_H; static int el_number_C; static int el_number_O; static int el_number_Si; #define FIRST_NEIGHB2 4 #define FIRST_CENTER2 5 #define NUM_CENTERS_N 4 int i1, i2, k1, k2, c, num_changes = 0; char elname[ATOM_EL_LEN]; /* constants for element numbers */ enum elems { dNone, dCl=17,dBr=35,dI=53,dAt=85,dO=8,dS=16,dSe=34,dTe=52, dP=15, dC=6, dN=7 } ; if (bFixNonUniformDraw) { /* Correct non-uniformly drawn oxoanions and amidinium cations. */ { /* For central halogen, apply the following correftion rules: O O(-) || | O=Hal(-)=O ===> O=Hal=O || || O O (perchlorate, etc.) O O(-) || | Hal(-)=O ===> Hal=O || || O O (chlorate, etc.) O O(-) || | Hal(-)=O ===> Hal=O (chlorite, etc.) Hal(-)=O ===> Hal-O(-) (hypochlorite, etc.) For halcogenes (S, Se, Te) Y Y(-) || | RnX(-) ===> RnX if: 1) (X = S, Y = O) || (X = Se, Y = S, O) || (X = Te, Y = O, S, Se) 2) valence of X exceeds 6, in initially drawn form So the following is corrected: O O(-) || | O=S(-)-R ===> O=S-R || || O O or O O- || | F5Te(-) ===> F5Te but the following remains unchanged: O || O=S(-)-R The central atom (of IUPAC Group 16-17) is shown as negative but it contains double bond(s) to terminal atom of greater electronegativity (of Group 16). The central atom is halcogen (S,Se,Te) in highest oxidation state or halogen. Fix: move negative charge to terminal atom and change double bond to a single one. Eligible central atom Eligible terminal atom at double bond's end Cl O Br O I O [At S,Se,Te] S O Se O,S Te O, S, Se Comments: 1. Central atoms of Groups 13-15 are not considered. 2. Pauling electronegativities are: F(3.98) > O(3.44) > Cl (3.16) > N (3.04) > Br(2.96) > I(2.66) > S(2.58) > Se(2.55) > At (2.2) > Te(2.1) */ static U_CHAR allowed_elnums_center_halogen[] = {dCl, dBr, dI, dAt} ; static U_CHAR allowed_elnums_center_halcogen[] = {dS, dSe, dTe} ; int en_center; int i, j, k; for (i=0; i 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; continue; } /* From same-element candidates, select one with less isotopic mass (arbitrary choice). */ else if (en_term==min_en) { iso = at[j].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; if ( iso =0) { at[i].charge = 0; at[jj].charge = -1; at[i].bond_type[kk] = BOND_TYPE_SINGLE; at[jj].bond_type[0] = BOND_TYPE_SINGLE; at[i].bond_stereo[kk] = at[jj].bond_stereo[0] = 0; at[i].chem_bonds_valence--; at[jj].chem_bonds_valence--; num_changes ++; } } } /* end of search for candidate centers. */ } /* end of correcting oxoanions */ /* Correct non-uniformly drawn amidinium cations. */ { /* Amidines include carboxamidines RC(=NR)NR2, sulfinamidines RS(=NR)NR2 and phosphinamidines, R2P(=NR)NR2. NR NR | | R"-Y-NHR' ===> R"-Y=N(+)HR' (+) Y = C, S, P Fix: move positive charge to nitrogen and change single bond to a double one. Comment: Fix is applied only if at least one of R's at N is hydrogen (otherwise we have just a '+' delocalization which is already recognized). */ static U_CHAR allowed_elnums_center[] = {dC, dS, dP} ; int en_center; int i, j, k, jj, kk; int mismatch = 0, nuH=0, nuN = 0, nitrogens[MAXVAL]; for (i=0; i 3 ) || ( at[j].chem_bonds_valence > 3 ) ) { mismatch = 1; break; } nuH+= NUMH(at,j); nuN++; if (jj<0) { jj = j; kk = k; } } } /* If OK, apply changes. */ if (mismatch) continue; if (nuN!=2) continue; if (nuH<1) continue; if (jj>=0) { at[i].charge = 0; at[jj].charge = 1; at[i].bond_type[kk] = BOND_TYPE_DOUBLE; for ( k1 = 0; k1 < at[jj].valence && i != at[jj].neighbor[k1]; k1 ++ ) ; at[jj].bond_type[k1] = BOND_TYPE_DOUBLE; at[i].chem_bonds_valence++; at[jj].chem_bonds_valence++; /* NB: do nothing with wedge stereo bonds (retain wedge) */ num_changes ++; } } /* end of search for candidate centers. */ } /* end of correcting amidiniums */ } /*( if (bFixNonUniformDraw) */ if ( !ne ) { /* one time initialization */ const char *b, *e; int len; for ( b = el; e = strchr( b, ';'); b = e+1 ) { len = e-b; memcpy( elname, b, len ); elname[len] = '\0'; en[ne++] = get_periodic_table_number( elname ); } ne2 = ne; el_number_P = get_periodic_table_number( "P" ); el_number_H = get_periodic_table_number( "H" ); el_number_O = get_periodic_table_number( "O" ); en[ne2++] = el_number_C = get_periodic_table_number( "C" ); en[ne2++] = el_number_Si = get_periodic_table_number( "Si" ); } /* H(-)-X -> H-X(-); H(+)-X -> H-X(+) */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( 1 == at[i1].valence && 1 == abs(at[i1].charge) && (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && BOND_TYPE_SINGLE == at[i1].bond_type[0] && el_number_H == at[i1].el_number && el_number_H != at[i2=(int)at[i1].neighbor[0]].el_number && !NUMH(at,i1) && !NUMH(at,i2) ) { at[i2].charge += at[i1].charge; at[i1].charge = 0; } } /* replace XHm(-)--Y==XHn(+) with XHm==Y--XHn, (n>=0 ,m>=0, X=N,P,As,Sb,O,S,Se,Te) */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( 1 != at[i1].charge || at[i1].radical && RADICAL_SINGLET != at[i1].radical || at[i1].chem_bonds_valence == at[i1].valence || !memchr(en, at[i1].el_number, ne) || get_el_valence( at[i1].el_number, at[i1].charge, 0 ) != at[i1].chem_bonds_valence+NUMH(at,i1) ) { continue; } /* found a candidate at[i1] for X in XHn(+) */ if ( 1 == at[i1].valence && BOND_TYPE_DOUBLE == at[i1].bond_type[0] ) { c = (int)at[i1].neighbor[0]; for ( k2 = 0; k2 < at[c].valence; k2 ++ ) { i2 = at[c].neighbor[k2]; if ( 1 == at[i2].valence && -1 == at[i2].charge && at[i2].el_number == at[i1].el_number && /* exact match */ (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && BOND_TYPE_SINGLE == at[i2].bond_type[0] && /*memchr(en, at[i2].el_number, ne) &&*/ get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) { /* found both X(-) and X(+); change bonds and remove charges */ for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) ; at[i1].charge = at[i2].charge = 0; at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; at[i1].chem_bonds_valence --; at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; at[i2].chem_bonds_valence ++; num_changes ++; break; } } } else { /* explicit H case: detect H-neighbors and Y */ int ineigh, neigh, i1_c, i2_c, num_H_i1, num_H_i2; for ( ineigh = 0, num_H_i1 = 0, i1_c = -1; ineigh < at[i1].valence; ineigh ++ ) { neigh = at[i1].neighbor[ineigh]; if ( at[neigh].el_number == el_number_H ) { if ( at[neigh].chem_bonds_valence == 1 && (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) { num_H_i1 ++; /* found H-neighbor */ } else { break; /* wrong neighbor */ } } else if ( at[i1].bond_type[ineigh] == BOND_TYPE_DOUBLE ) { /* found a candidate for Y; bond must be double */ i1_c = ineigh; c = neigh; } } if ( i1_c < 0 || num_H_i1 + 1 != at[i1].valence ) { continue; } for ( k2 = 0; k2 < at[c].valence; k2 ++ ) { i2 = at[c].neighbor[k2]; if (-1 == at[i2].charge && at[i2].el_number == at[i1].el_number && /* exact match */ (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) { for ( ineigh = 0, num_H_i2 = 0, i2_c = -1; ineigh < at[i2].valence; ineigh ++ ) { neigh = at[i2].neighbor[ineigh]; if ( at[neigh].el_number == el_number_H ) { if ( at[neigh].chem_bonds_valence == 1 && (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) { num_H_i2 ++; /* found H-neighbor */ } else { break; /* wrong neighbor */ } } else if ( c == neigh && at[i2].bond_type[ineigh] == BOND_TYPE_SINGLE ) { i2_c = ineigh; /* position of Y neighbor; bond must be single */ } else { break; } } if ( num_H_i2 + (i2_c >= 0) != at[i2].valence ) { continue; } /* found both X(-) and X(+); change bonds and remove charges */ for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) ; at[i1].charge = at[i2].charge = 0; at[i1].bond_type[i1_c] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; at[i1].chem_bonds_valence --; at[i2].bond_type[i2_c] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; at[i2].chem_bonds_valence ++; num_changes ++; break; } } } } /* Replace X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) \ | \ || >Y++ with >Y Y=S,Se,Te -- central cation (CENTER2) / | / || X- X Y valence=4, original Y bond valence = 4 --- the following case of P is processed separately in remove_ion_pairs() --- therefire, it has been disabled here, see #ifndef FIX_P_IV_Plus_O_Minus -- 2010-03-17 DT X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) \ | \ || >P+ with >P / | / | X- X- Y valence=4, original Y bond valence = 4 */ for ( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( 1 == at[i1].valence && -1 == at[i1].charge && (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && !NUMH(at,i1) && BOND_TYPE_SINGLE == at[i1].bond_type[0] && memchr( en+FIRST_NEIGHB2, at[i1].el_number, ne-FIRST_NEIGHB2 ) ) { int charge, i; /* found a candidate for X */ c = (int)at[i1].neighbor[0]; /* candidate for Y */ if ( ((charge=2) == at[c].charge && memchr( en+FIRST_CENTER2, at[c].el_number, ne-FIRST_CENTER2) #ifndef FIX_P_IV_Plus_O_Minus || (charge=1) == at[c].charge && el_number_P==at[c].el_number #endif ) && 4 == at[c].valence && (0 == at[c].radical || RADICAL_SINGLET == at[c].radical ) && at[c].valence == at[c].chem_bonds_valence && !NUMH(at,c) ) { ; /* accept */ } else { continue; /* ignore at[i1] */ } for ( k2 = 0; k2 < at[c].valence; k2 ++ ) { i2 = at[c].neighbor[k2]; if ( i2 == i1 ) { continue; } if ( 1 == at[i2].valence && -1 == at[i2].charge && memchr( en+FIRST_NEIGHB2, at[i2].el_number, ne-FIRST_NEIGHB2 ) && /*at[i2].el_number == at[i1].el_number &&*/ /* exact match */ (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && !NUMH(at,i2) && BOND_TYPE_SINGLE == at[i2].bond_type[0] ) { /* found both X(-) and X(-); change bonds and remove charges */ for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) ; for ( i = 0; i < charge; i ++ ) { /* in case of P it does not matter which X atom is neutralized because of tautomerism. However, neutral central atom is important for the neutralization of the components */ switch ( i ) { case 0: at[i1].charge ++; /* = 0; changed 2010-03-17 DT*/ at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_DOUBLE; at[i1].bond_stereo[0] = at[c].bond_stereo[k1] = 0; at[i1].chem_bonds_valence ++; at[c].chem_bonds_valence ++; if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT*/ num_changes ++; break; case 1: at[i2].charge ++; /*= 0; changed 2010-03-17 DT*/ at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; at[i2].bond_stereo[0] = at[c].bond_stereo[k2] = 0; at[i2].chem_bonds_valence ++; at[c].chem_bonds_valence ++; if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT */ num_changes ++; break; } } /* -- removed -- 2010-03-17 DT #if ( FIX_ODD_THINGS_REM_Plus_BUG == 1 ) at[c].charge -= charge; #else if ( bFixBug ) { at[c].charge -= charge; } #endif */ break; } } } } /* A(doublet)-B(doublet) -> A=B (A and B have no other doublet neighbors) */ /* A(doublet)=B(doublet) -> A#B (A and B have no other doublet neighbors) */ for( i1 = 0; i1 < num_atoms; i1 ++ ) { if ( RADICAL_DOUBLET == at[i1].radical && 0 <= (i2=the_only_doublet_neigh(at, i1, &k1, &k2)) ) { if ( at[i1].bond_type[k1] <= BOND_TYPE_DOUBLE ) { at[i1].bond_type[k1] ++; at[i1].chem_bonds_valence ++; at[i2].bond_type[k2] ++; at[i2].chem_bonds_valence ++; at[i1].radical = 0; at[i2].radical = 0; } } } #if ( REMOVE_ION_PAIRS_EARLY == 1 ) num_changes += remove_ion_pairs( num_atoms, at ); #endif return num_changes; } /************************************************************************/ int post_fix_odd_things( int num_atoms, inp_ATOM *at ) { int num_changes = 0; /* currently does nothing */ return num_changes; } /************************************************************************/ int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM) { int i, n_OM, n_OM_best, best_value, cur_value, diff; int num_best; if ( 1 == num_OM ) { return ord_OM[0]; } if ( 1 > num_OM ) { return -1; } /* select neighbors with min. number of bonds */ num_best = 1; n_OM = (int)at[at_no].neighbor[ord_OM[0]]; best_value = (int)at[n_OM].valence; /* compare number of bonds; move indexes of the best neighbors to the first elements of ord_OM[] */ for ( i = 1; i < num_OM; i ++ ) { n_OM = at[at_no].neighbor[ord_OM[i]]; cur_value = (int)at[n_OM].valence; diff = cur_value - best_value; if ( diff < 0 ) { n_OM_best = n_OM; best_value = cur_value; ord_OM[0] = ord_OM[i]; num_best = 1; } else if ( diff == 0 ) { /* was '=', pointed by WDI */ ord_OM[num_best ++] = ord_OM[i]; } } num_OM = num_best; if ( 1 == num_OM ) { return ord_OM[0]; } /* select neighbors with min. periodic numbers */ num_best = 1; n_OM = (int)at[at_no].neighbor[ord_OM[0]]; best_value = (int)at[n_OM].el_number; /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ for ( i = 1; i < num_OM; i ++ ) { n_OM = at[at_no].neighbor[ord_OM[i]]; cur_value = (int)at[n_OM].el_number; diff = cur_value - best_value; if ( diff < 0 ) { n_OM_best = n_OM; best_value = cur_value; ord_OM[0] = ord_OM[i]; num_best = 1; } else if ( diff == 0 ) { /* was '=', pointed by WDI */ ord_OM[num_best ++] = ord_OM[i]; } } num_OM = num_best; if ( 1 == num_OM ) { return ord_OM[0]; } /* if neighbors are not terminal atoms then reject */ if ( 1 < at[n_OM].valence ) { return -1; } /* if neighbors are terminal atoms then the one without isotope or with lightest isotope */ num_best = 1; n_OM = (int)at[at_no].neighbor[ord_OM[0]]; best_value = (int)at[n_OM].iso_atw_diff; /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ for ( i = 1; i < num_OM; i ++ ) { n_OM = at[at_no].neighbor[ord_OM[i]]; cur_value = (int)at[n_OM].el_number; diff = cur_value - best_value; if ( (!cur_value && best_value) || diff < 0 ) { n_OM_best = n_OM; best_value = cur_value; ord_OM[0] = ord_OM[i]; num_best = 1; } else if ( diff == 0 ) { /* was '=', pointed by WDI */ ord_OM[num_best ++] = ord_OM[i]; } } num_OM = num_best; if ( 1 == num_OM ) { return ord_OM[0]; } /* return any */ return ord_OM[0]; } /************************************************************************/ /* the bonds are fixed in fix_special_bonds() */ int remove_ion_pairs( int num_atoms, inp_ATOM *at ) { int num_changes = 0; /* 0 1 2 3 4 5 6 7 8 9 8 9 */ #if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ #else static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ #endif static char en[12]; /* same number: 8 elements */ static int ne=0; /* will be 8 and 10 */ #define ELEM_N_FST 0 #define ELEM_N_LEN 4 #define ELEM_O_FST 4 #define ELEM_O_LEN 4 #define ELEM_C_FST 8 #define ELEM_C_LEN 2 #define MAX_NEIGH 6 int i, n, n2, i1, i2, i3, i4, type, chrg; int num_C_II=0, num_C_plus=0, num_C_minus=0, num_N_plus=0, num_N_minus=0, num_O_plus=0, num_O_minus=0, num_All; #ifdef FIX_P_IV_Plus_O_Minus int num_P_IV_plus=0; /* added 2010-03-17 DT */ #endif inp_ATOM *a; char elname[ATOM_EL_LEN], *p; if ( !ne ) { /* one time initialization */ const char *b, *e; int len; for ( b = el; e = strchr( b, ';'); b = e+1 ) { len = e-b; memcpy( elname, b, len ); elname[len] = '\0'; en[ne++] = get_periodic_table_number( elname ); } en[ne] = '\0'; } /****** count candidates ********/ for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { if ( 1 == (chrg=a->charge) || -1 == chrg ) { if ( p = (char*)memchr( en, a->el_number, ne) ) { n = p - en; if ( n >= ELEM_C_FST ) { if ( chrg > 0 ) num_C_plus ++; else num_C_minus ++; } else if ( n >= ELEM_O_FST ) { if ( chrg > 0 ) num_O_plus ++; else num_O_minus ++; } else { if ( chrg > 0 ) num_N_plus ++; else num_N_minus ++; #ifdef FIX_P_IV_Plus_O_Minus num_P_IV_plus += n > 0 && chrg == 1 && a->valence == 4 && a->chem_bonds_valence == 4; /* added 2010-03-17 DT */ #endif } } } else if ( !chrg && a->chem_bonds_valence + NUMH(a, 0) == 2 && get_el_valence( a->el_number, 0, 0 ) == 4 && NULL != memchr( en+ELEM_C_FST, a->el_number, ELEM_C_LEN) ) { num_C_II ++; } } num_All = num_C_II + num_C_plus + num_C_minus + num_N_plus + num_N_minus + num_O_plus + num_O_minus; /* do not add num_P_IV_plus ! -- 2010-03-17 DT */ if ( !num_All ) { return 0; } /**************************************************************************/ /*************************** Terminal ion pairs ***************************/ /**************************************************************************/ /*------------------------------------------------------------------------- Pair type 1 N=N,P,As,Sb; O=O,S,Se,Te =========== X X if X is another -O(-) then neutralize O(-) | | that has the smallest periodic table number O=N(+)-O(-) => O=N=O i n --------------------------------------------------------------------------*/ for ( type = 1; type <= 18; type ++ ) { if ( (!type || 1 == type) ) { for ( i = 0; i < num_atoms && 0 < num_N_plus && 0 < num_O_minus; i ++ ) { if ( 1 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { int num_OM = 0, ord_OM[3]; /* -O(-) */ int num_O = 0; /* =O */ int num_O_other = 0; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && -1 == at[n].charge ) { ord_OM[num_OM ++] = i1; } else if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && 0 == at[n].charge ) { num_O ++; } else { num_O_other ++; } } } if ( num_OM > 0 && num_O > 0 && !num_O_other && 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { /* remove charges and increase bond order */ n = at[i].neighbor[i1]; i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].charge --; at[n].charge ++; at[i].radical = 0; at[n].radical = 0; num_changes ++; num_N_plus --; num_O_minus --; num_All -= 2; } } } #ifdef FIX_P_IV_Plus_O_Minus /*------------------------------------------------------------------------- Pair type 1a P=P,As,Sb; O=O,S,Se,Te -- added 2010-03-17 ============= X X if X, Y, or Z is another -O(-) then neutralize O(-) | | that has the smallest periodic table number Y-P(+)-O(-) => Y-P=O |i n | Z Z --------------------------------------------------------------------------*/ for ( i = 0; i < num_atoms && 0 < num_P_IV_plus /*&& 0 < num_N_plus*/ && 0 < num_O_minus; i ++ ) { if ( 1 == at[i].charge && 4 == nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && NULL != memchr( en+ELEM_N_FST+1, at[i].el_number, ELEM_N_LEN-1) ) { int num_OM = 0, ord_OM[4]; /* -O(-) */ /*int num_O = 0;*/ /* =O */ int num_O_other = 0; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && -1 == at[n].charge ) { ord_OM[num_OM ++] = i1; /* } if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && 0 == at[n].charge ) { num_O ++; */ } else { num_O_other ++; } } } if ( num_OM > 0 /*&& num_O > 0 && !num_O_other*/ && 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { /* remove charges and increase bond order */ n = at[i].neighbor[i1]; i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].charge --; at[n].charge ++; at[i].radical = 0; at[n].radical = 0; num_changes ++; num_N_plus --; num_O_minus --; num_P_IV_plus --; num_All -= 2; } } } #endif /* FIX_P_IV_Plus_O_Minus */ } /*------------------------------------------------------------------------- Terminal pair types: 2,3,4,5,6,7,8,9 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si ==================================== type # 2 2: O=N-C(II)- => O=N#C- N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si 3 9: O=O(+)-C(-)(III) => O=O=C(IV) 4 3: O(-)-N(+)(IV) => O=N(V) (input structure has at least 1 double bond) 5 4: O(-)-O(+)(III) => O=O(IV) 6 8: O(-)-O-C(+)(III) => O=O=C(IV) 7 5: N(-)=N(+)(IV) => N#N(V) allow terminal H on N(-) 8 6: N(-)=O(+)(III) => N#O- 9 7: N(-)=C(+)(III) => N#C- --------------------------------------------------------------------------*/ if ( !type || 2 <= type && type <= 9 ) { for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { if ( 0 == at[i].charge && 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && 0 <= ( i1 = nNoMetalNeighIndex(at, i)) && at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { /* terminal O= */ n = at[i].neighbor[i1]; if ( (!type || type == 2) && 0 < num_C_II ) { /* avoid alternating bonds */ if ( 0 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) && 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { /* i2 = index of opposite to at[i] neighbor of at[n] */ /*i2 = (at[n].neighbor[0] == i);*/ n2 = at[n].neighbor[i2]; if ( 0 == at[n2].charge && 2 == at[n2].valence && 2 == at[n2].chem_bonds_valence && 0 == num_of_H( at, n2 ) && NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { /* i n n2 */ /* found O=N-C(II)- */ /* convert O=N-C(II)- => O=N#C- */ i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ at[ n].chem_bonds_valence = 5; /* N */ at[n2].chem_bonds_valence = 4; /* C */ at[ n].bond_type[i2] = BOND_TYPE_TRIPLE; at[n2].bond_type[i3] = BOND_TYPE_TRIPLE; at[n2].radical = 0; num_changes ++; num_C_II --; num_All --; continue; } } } if ( (!type || type == 3) && 0 < num_O_plus && 0 < num_C_minus ) { if ( 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { /* found O=O(+)- */ /* i2 = index of opposite to at[i] neighbor of at[n] */ /*i2 = (at[n].neighbor[0] == i);*/ n2 = at[n].neighbor[i2]; if ( -1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { /* i n n2 */ /* found found O=O(+)-C(-)(III) */ /* convert O=O(+)-C(-)(III) => O=O=C(IV) */ i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ at[ n].charge --; at[n2].charge ++; at[ n].chem_bonds_valence += 1; /* =O- => =O= */ at[n2].chem_bonds_valence += 1; /* -C => =C */ at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; num_changes ++; num_O_plus --; num_C_minus --; num_All -= 2; continue; } } } } else if ( -1 == at[i].charge && 0 < num_O_minus + num_N_minus && 0 < num_N_plus + num_O_plus + num_C_plus && 1 == nNoMetalNumBonds(at, i) && 1 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && 0 <= (i1 = nNoMetalNeighIndex( at, i )) && at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { /* terminal O(-)- */ n = at[i].neighbor[i1]; if ( (!type || type == 4) && 0 < num_O_minus && 0 < num_N_plus && /* O(-)-N(+)(IV) */ 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) /* except >O(+)- */ ) { /* found O(-)-N(+)(IV) */ /* convert O(-)-N(+)(IV) => O=N(V) */ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ at[i].charge ++; at[n].charge --; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_O_minus --; num_N_plus --; num_All -= 2; continue; } if ( (!type || type == 5) && 0 < num_O_minus && 0 < num_O_plus &&/* O(-)-O(+)(III) */ 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) /* except >O(+)- */ ) { /* found O(+)(III) */ /* convert O(-)-O(+)(III) => O=O(IV) */ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ at[i].charge ++; at[n].charge --; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_O_minus --; num_O_plus --; num_All -= 2; continue; } /* i n n2 */ if ( (!type || type == 6) && /* O(-)-O-C(+)(III) */ 0 < num_O_minus && 0 < num_C_plus && 0 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && 0 <= (i2=nNoMetalOtherNeighIndex( at, n, i )) && at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { /* found O(-)-O- */ /* i2 = index of opposite to at[i] neighbor of at[n] */ /*i2 = (at[n].neighbor[0] == i);*/ n2 = at[n].neighbor[i2]; if ( 1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { /* i n n2 */ /* found O(-)-O-C(+)(III) */ /* convert O(-)-O-C(+)(III) => O=O=C(IV) */ /*i3 = (at[n2].neighbor[0] != n);*/ /* i3 = index of at[n] neighbor of at[n2] */ i3 = is_in_the_list( at[n2].neighbor, (AT_NUMB)n, at[n2].valence ) - at[n2].neighbor; /*i4 = index of at[i] in the adjacency list of at[n] */ i4 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[ i].charge ++; at[n2].charge --; at[ i].chem_bonds_valence += 1; /* O- => O= */ at[ n].chem_bonds_valence += 2; /* -O- => =O= */ at[n2].chem_bonds_valence += 1; /* -C => =C */ at[ i].bond_type[i1] = BOND_TYPE_DOUBLE; at[ n].bond_type[i4] = BOND_TYPE_DOUBLE; at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; num_changes ++; num_O_minus --; num_C_plus --; num_All -= 2; continue; } } } else if ( -1 == at[i].charge && 0 < num_N_minus && 0 < num_N_plus+num_O_plus+num_C_plus && 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i)+NUMH(at, i) && /*0 == num_of_H( at, i ) &&*/ NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && 0 <= (i1 = nNoMetalNeighIndex( at, i )) && at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { /* terminal N(-)= */ n = at[i].neighbor[i1 = 0]; if ( (!type || type == 7) && 0 < num_N_plus && /* N(-)=N(+)(IV) */ 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { /* found N(-)-N(+)(IV) */ /* convert N(-)=N(+)(IV) => N#N(V) */ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ at[i].charge ++; at[n].charge --; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_N_minus --; num_N_plus --; num_All -= 2; continue; } if ( (!type || type == 8) && 0 < num_O_plus && /* N(-)=O(+)(III) */ 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) ) { /* found N(-)-O(+)(III) */ /* convert N(-)=O(+)(III) => N#O(IV)- */ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ at[i].charge ++; at[n].charge --; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_N_minus --; num_O_plus --; num_All -= 2; continue; } if ( (!type || type == 9) && 0 < num_C_plus && /* N(-)=C(+)(III) */ 1 == at[n].charge && 2 == at[n].valence && 3 == at[n].chem_bonds_valence && 0 == num_of_H( at, n ) && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { /* found N(-)=C(+)(III) */ /* convert N(-)=C(+)(III) => N#C(IV)- */ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ at[i].charge ++; at[n].charge --; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_N_minus --; num_C_plus --; num_All -= 2; continue; } } } } /**************************************************************************/ /*********************** NON-Terminal ion pairs ***************************/ /**************************************************************************/ /*------------------------------------------------------------------------- Non-Terminal pair types: 10,11,12,13,14 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si ======================================== 10: N(+)(IV)-C(-)(III) => N(V)=C(IV) (N has 3 or 2 bonds) 11: N(+)(IV)=C(-)(III) => N(V)#C(IV) (N has 3 or 2 bonds) 12: N(+)(IV)-N(-)(II) => N(V)=N(III) (allow terminal H on N(-)) 13: -O(+)-C(-)(III) => -O=C- 14: -O(+)=C(-)(III) => -O#C- 15: O(+)(III)-N(-)(II) => O(IV)=N(III) (allow terminal H on N(-)) --------------------------------------------------------------------------*/ if ( !type || 10 <= type && type <= 15 ) { for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { if ( 1 == at[i].charge && 0 < num_N_plus + num_O_plus && 0 < num_C_minus + num_N_minus && 4 >= nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { /* found non-terminal N(+)(IV) */ if ( (!type || 10 == type) && 0 < num_N_plus && 0 < num_C_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && /*0 == at[n].num_H &&*/ at[i].bond_type[i1] == BOND_TYPE_SINGLE && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { /* found N(+)(IV)-C(-)(III); prepare conversion to N(V)=C(IV) */ num_neigh ++; pos_neigh = i1; } } i1=pos_neigh; if ( 1 == num_neigh && at[i].bond_type[i1] <= BOND_TYPE_TRIPLE && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_C_minus --; num_N_plus --; num_All -= 2; continue; } } if ( (!type || 11 == type) && 0 < num_N_plus && 0 < num_C_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && /*0 == at[n].num_H &&*/ at[i].bond_type[i1] == BOND_TYPE_DOUBLE && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { /* found N(+)(IV)=C(-)(III); prepare conversion to N(V)#C(IV) */ num_neigh ++; pos_neigh = i1; } } if ( 1 == num_neigh && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_C_minus --; num_N_plus --; num_All -= 2; continue; } } if ( !type || 12 == type && 0 < num_N_plus && 0 < num_N_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && /*0 == num_of_H( at, n ) &&*/ at[i].bond_type[i1] == BOND_TYPE_SINGLE && NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { /* found N(+)(IV)=N(-)(II); prepare conversion to N(V)#N(III) */ num_neigh ++; pos_neigh = i1; } } if ( 1 == num_neigh && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_N_minus --; num_N_plus --; num_All -= 2; continue; } } } else if ( 1 == at[i].charge && 0 < num_O_plus && 0 < num_C_minus + num_N_minus && 3 >= nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { /* found non-terminal O(+)(III) */ if ( (!type || 13 == type) && 0 < num_C_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && /*0 == at[n].num_H &&*/ at[i].bond_type[i1] == BOND_TYPE_SINGLE && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { /* found O(+)(III)-C(-)(II); prepare conversion to O(IV)=C(IV) */ num_neigh ++; pos_neigh = i1; } } if ( 1 == num_neigh && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_C_minus --; num_O_plus --; num_All -= 2; continue; } } if ( (!type || 14 == type) && 0 < num_C_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && /*0 == at[n].num_H &&*/ at[i].bond_type[i1] == BOND_TYPE_DOUBLE && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { /* found O(+)(III)=C(-)(III); prepare conversion to O(IV)#C(IV) */ num_neigh ++; pos_neigh = i1; } } if ( 1 == num_neigh && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_C_minus --; num_O_plus --; num_All -= 2; continue; } } if ( (!type || 15 == type) && 0 < num_N_minus ) { int num_neigh = 0, pos_neigh = -1; for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { n = at[i].neighbor[i1]; if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && /*0 == num_of_H( at, n ) &&*/ at[i].bond_type[i1] == BOND_TYPE_SINGLE && NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { /* found O(+)(III)=N(-)(II); prepare conversion to O(IV)#N(III) */ num_neigh ++; pos_neigh = i1; } } if ( 1 == num_neigh && !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && !has_other_ion_neigh( at, n, i, en, ne )) { /*n = at[i].neighbor[i1=pos_neigh];*/ i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].charge --; at[n].charge ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; num_changes ++; num_N_minus --; num_O_plus --; num_All -= 2; continue; } } } } } /**************************************************************************/ /*********************** NON-Terminal ion triples *************************/ /**************************************************************************/ /*------------------------------------------------------------------------- Non-Terminal triple types: 16, 17, 18 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si ======================================== 16: C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) (allow terminal H on N(-)) | | 17: C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) 18: C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) (may have two or no charges) C(IV)=N-C(II) => C(IV)=N#C(IV) */ if ( (!type || 16 == type) && 0 < num_C_plus && 0 < num_N_minus ) { int m[2], j[2], k; for ( i = 0; i < num_atoms; i ++ ) { if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && at[m[0]=at[i].neighbor[j[0]]].charge && 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] )) && 0 == at[m[0]].charge + at[m[1]=at[i].neighbor[j[1]]].charge && 5 >= nNoMetalBondsValence(at, m[0]) + nNoMetalBondsValence(at, m[1]) && /*5 >= at[m[0]].chem_bonds_valence + at[m[1]].chem_bonds_valence &&*/ NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { /* found non-terminal A(+)-O-B(-); chem_bond_val of A+B <= 5 */ int n_N=-1, n_C=-1, i_C=-1; for ( k = 0; k < 2; k ++ ) { n = m[k]; if ( -1 == at[n].charge && 2 == nNoMetalNumBonds(at, n)+NUMH(at, n) && /*0 == num_of_H( at, n ) &&*/ NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { n_N = n; } else if ( 1 == at[n].charge && 3 == at[n].chem_bonds_valence+NUMH(at,n) && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { n_C = n; i_C = k; } } if ( n_C < 0 || n_N < 0 || has_other_ion_in_sphere_2(at, n_C, n_N, en, ne ) || has_other_ion_in_sphere_2(at, n_N, n_C, en, ne ) ) { continue; } /* C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) */ for ( k = 0; k < 2; k ++ ) { n = k? n_C : n_N; i1 = k? j[i_C] : j[1-i_C]; i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[n].charge += (k? -1:1); } num_changes ++; num_N_minus --; num_C_plus --; num_All -= 2; } } } if ( (!type || 17 == type) && 0 < num_C_plus && 0 < num_C_minus ) { int m[3], c[3], j[3], k; for ( i = 0; i < num_atoms; i ++ ) { if ( 0 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && 0 <= ( j[0] = nNoMetalNeighIndex(at, i) ) && 0 <= ( j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] ) ) && 0 <= ( j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = at[i].neighbor[j[1]] ) ) && 1 == !(c[0]=at[m[0]].charge) + !(c[1]=at[m[1]].charge) + !(c[2]=at[m[2]=at[i].neighbor[j[2]]].charge) && 0 == c[0] + c[1] + c[2] && 2 == (3== (c[0]? at[m[0]].chem_bonds_valence+NUMH(at,m[0]):0)) + (3== (c[1]? at[m[1]].chem_bonds_valence+NUMH(at,m[1]):0)) + (3== (c[2]? at[m[2]].chem_bonds_valence+NUMH(at,m[2]):0)) && NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { /* found non-terminal A(+)-O-B(-) */ int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ for ( k = 0; k < 3; k ++ ) { if ( c[k] ) { n = m[k]; if ( -1 == at[n].charge && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { n_Cm = n; i_Cm = k; } else if ( 1 == at[n].charge && NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { n_Cp = n; i_Cp = k; } } } if ( n_Cp < 0 || n_Cm < 0 || has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { continue; } /* | | */ /* C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) */ for ( k = 0; k < 2; k ++ ) { n = k? n_Cp : n_Cm; i1 = k? j[i_Cp] : j[i_Cm]; i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].bond_type[i1] ++; at[n].bond_type[i2] ++; at[i].chem_bonds_valence ++; at[n].chem_bonds_valence ++; at[n].charge += (k? -1:1); } num_changes ++; num_C_minus --; num_C_plus --; num_All -= 2; } } } if ( (!type || 18 == type) && (0 < num_C_plus && 0 < num_C_minus || 0 < num_C_II) ) { int m[2], v[2], j[2], k; for ( i = 0; i < num_atoms; i ++ ) { if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && 0 == num_of_H( at, i ) && 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] )) && 0 == at[m[0]].charge +at[m[1]=at[i].neighbor[j[1]]].charge && 6 == (v[0]=at[m[0]].chem_bonds_valence+NUMH(at,m[0])) +(v[1]=at[m[1]].chem_bonds_valence+NUMH(at,m[1])) && 2 >= abs(v[0]-v[1]) && NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && NULL != memchr( en+ELEM_C_FST, at[m[0]].el_number, ELEM_C_LEN) && NULL != memchr( en+ELEM_C_FST, at[m[1]].el_number, ELEM_C_LEN) ) { /* n_Cm i n_Cp */ /* found non-terminal C(-)(III)-N=C(+)(III) or C(IV)=N-C(II): Cm-N-Cp */ /* convert to C(IV)=N#C(IV) */ int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ for ( k = 0; k < 2; k ++ ) { n = m[k]; if ( v[k] == 4 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_SINGLE ) { n_Cm = n; i_Cm = k; } else if ( v[k] == 2 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_DOUBLE ) { n_Cp = n; i_Cp = k; } } if ( n_Cp < 0 || n_Cm < 0 || at[n_Cp].valence+NUMH(at,n_Cp) != 2 ) { continue; /* guarantees at[n_Cp].valence <= 2 */ } if ( v[i_Cp] == 2 || !at[n_Cp].charge ) { if ( at[n_Cp].valence == 2 ) { /* neighbor of at[n_Cp] opposite to at[i] */ k = at[n_Cp].neighbor[at[n_Cp].neighbor[0]==i]; if ( NULL != memchr( en+ELEM_N_FST, at[k].el_number, ELEM_N_LEN) ) { continue; } } } else if ( at[n_Cp].charge ) { if ( has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { continue; } } else { continue; /* unknown case */ } /* */ /* C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) */ /* C(IV)=N-C(II) => C(IV)=N#C(IV) */ if ( at[n_Cp].charge ) { num_C_minus --; num_C_plus --; num_All -= 2; } else { num_C_II --; num_All --; } for ( k = 0; k < 2; k ++ ) { n = k? n_Cp : n_Cm; i3 = k? i_Cp : i_Cm; /* added to fix the bug */ /*i1 = k? j[i_Cp] : j[i_Cm];*/ /* replaced with next line */ i1 = j[i3]; if ( v[i3 /*was i1*/] < 4 ) { /* WDI found a bug here: bounds violation */ int delta = 4 - v[i3 /*was i1*/]; i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; at[i].bond_type[i1] += delta; at[n].bond_type[i2] += delta; at[i].chem_bonds_valence += delta; at[n].chem_bonds_valence += delta; at[n].charge = 0; at[n].radical = 0; } } at[i].charge = 0; at[i].radical = 0; num_changes ++; } } } } return num_changes; } /*#if ( DISCONNECT_SALTS == 1 )*/ /* { */ /*************************************************************************************************/ int RemoveInpAtBond( inp_ATOM *atom, int iat, int k ) { int i, j, m, m2, k2; inp_ATOM *at = atom + iat; inp_ATOM *at2 = NULL; int val = at->valence - 1; if ( val >= 0 ) { int bond = at->bond_type[k]; if ( bond > BOND_TYPE_TRIPLE ) bond = BOND_TYPE_SINGLE; /* added 08-06-2003 */ /* update CML tetrahedral atom parity. */ if ( at->p_parity ) { for( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { if ( at->p_orig_at_num[m] == at->orig_at_number ) { at->p_parity = 0; break; /* only 3 bonds are present; removing one bond removes stereo */ } } if ( at->p_parity /* at->valence == MAX_NUM_STEREO_ATOM_NEIGH*/ ) { for ( m = 0; m < at->valence; m ++ ) { if ( atom[(int)at->neighbor[k]].orig_at_number == at->p_orig_at_num[m] ) { break; } } if ( m < at->valence ) { at->p_orig_at_num[m] = at->orig_at_number; } else { at->p_parity = 0; /* wrong neighbors: at->neighbor[k] is not in the list of a stereo neighbors */ } } } /* update CML stereogenic bond parities; at this point no removed explicit H exist yet */ if ( at->sb_parity[0] ) { for ( m = 0; m < MAX_NUM_STEREO_BONDS && at->sb_parity[m]; ) { if ( k == at->sb_ord[m] || k == at->sn_ord[m] && val < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { /* !!! FLAW: does take into account removed H !!! */ /* stereogenic bond is being removed OR */ /* remove stereogenic bond because its only neighbor is being removed */ int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); if ( len ) { i = pinxt_sb_parity_ord; at2 = atom + pnxt_atom; k2 = pinxt2cur; } else { i = MAX_NUM_STEREO_BONDS; } /* at2 = atom + at->neighbor[ (int)at->sb_ord[m] ]; for ( i = 0; i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i]; i ++ ) { if ( iat == at2->neighbor[ (int)at2->sb_ord[i] ] ) break; } */ if ( i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i] ) { m2 = i; /* remove bond parity from at */ if ( m < MAX_NUM_STEREO_BONDS-1 ) { memmove( at->sb_parity+m, at->sb_parity+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_parity[0])); memmove( at->sb_ord+m, at->sb_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_ord[0])); memmove( at->sn_ord+m, at->sn_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_ord[0])); memmove( at->sn_orig_at_num+m, at->sn_orig_at_num+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_orig_at_num[0])); } at->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; at->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; at->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; at->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; /* remove bond parity from at2 */ if ( m2 < MAX_NUM_STEREO_BONDS-1 ) { memmove( at2->sb_parity+m2, at2->sb_parity+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_parity[0])); memmove( at2->sb_ord+m2, at2->sb_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_ord[0])); memmove( at2->sn_ord+m2, at2->sn_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_ord[0])); memmove( at2->sn_orig_at_num+m2, at2->sn_orig_at_num+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_orig_at_num[0])); } at2->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; at2->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; at2->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; at2->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; /* do not increment m here because the array elements have been shifted */ } else { m ++; /* program error: inconsistent stereobond parity */ } } else if ( k == at->sn_ord[m] ) { /* stereogenic bond neighbor is being removed; another neighbor remains */ /* !!! FLAW: does take into account removed H !!! */ for ( j = 0, i = -1; j < at->valence; j ++ ) { if ( j != k && j != at->sb_ord[m] ) { i = j; break; } } /* i is the position of the neighbor that will become a new neighbor */ /*************************************************************************** * at->sb_parity[m] is the direction (EVEN=clockwise, ODD=counterclockwise) * from stereobond to the neighbor. If the neighbor is removed then * the parity should invert, otherwise it should be unchanged. ***************************************************************************/ if ( i < 0 ) { /* no alternative neighbor is available */ if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m] ) ) { /* parity cannot be not well-defined anymore */ int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); if ( len > 0 ) { atom[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = at->sb_parity[m] = AB_PARITY_UNDF; } #ifdef _DEBUG else { int stop = 1; /* sb parities error */ } #endif } at->sn_ord[m] = -99; /* sb neighbor has been disconnected */ at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ at->sn_orig_at_num[m] = 0; } else if ( i < at->valence ) { /* choose another stereogenic bond neighbor, its ord. number is i before bond removal */ if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { /* ALL WRONG: 'move' previous stereo bond neighbor to the last position (pos. 2 out of 0,1,2) */ /* the parity of the transpositions is (2 - at->sn_ord[m])%2 = at->sn_ord[m] % 2 */ /* and replace the neighbor with another; the contribution to the parity is 1 */ /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + at->sn_ord[m] + 1 ) % 2;*/ /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k + i + (i > k) + (i > at->sb_ord[m]) ) % 2;*/ /*=== parity should be INVERTED ===*/ at->sb_parity[m] = 3 - at->sb_parity[m]; } at->sn_ord[m] = i - (i > k); /* ord. number shifted because preceding bond is removed */ at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ at->sn_orig_at_num[m] = atom[(int)at->neighbor[i]].orig_at_number; /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + 1 ) % 2;*/ } else { at->sb_parity[m] = 0; /* program error: inconsistent stereobond parity */ } m ++; } else { /* removing another neighbor, k: first move it to the last position (pos. 2 out of 0,1,2) */ if ( k < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k ) % 2;*/ /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + (at->sn_ord[m] > k) + (at->sb_ord[m] > k) ) % 2;*/ ;/*==== Parity should remain UNCHANGED ===*/ } if ( at->sb_ord[m] > k ) { at->sb_ord[m] --; } if ( at->sn_ord[m] > k ) { at->sn_ord[m] --; } m ++; } } } if ( k < val ) { memmove( at->neighbor+k, at->neighbor+k+1, sizeof(at->neighbor[0])*(val-k) ); memmove( at->bond_stereo+k, at->bond_stereo+k+1, sizeof(at->bond_stereo[0])*(val-k) ); memmove( at->bond_type+k, at->bond_type+k+1, sizeof(at->bond_type[0])*(val-k) ); } at->neighbor[val] = 0; at->bond_stereo[val] = 0; at->bond_type[val] = 0; at->valence = val; at->chem_bonds_valence -= bond; return 1; } return 0; } /*************************************************************************************************/ int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ) { int neigh, i, ret = 0; int component; neigh = at[iat].neighbor[neigh_ord]; for ( i = 0; i < at[neigh].valence; i ++ ) { if ( iat == (int)at[neigh].neighbor[i] ) break; } if ( i < at[neigh].valence ) { ret += RemoveInpAtBond( at, iat, neigh_ord ); ret += RemoveInpAtBond( at, neigh, i ); if ( nOldCompNumber && ret ) { if ( component = at[iat].component ) { nOldCompNumber[component-1] = 0; } if ( component = at[neigh].component ) { nOldCompNumber[component-1] = 0; } } } return (ret == 2); } /*************************************************************************************************/ int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ) { /* NH4(+charge)-O(-charge)-C -> NH3 + HO-C; any charge including 0, any C except charged or radical */ /* F, Cl, Br, I */ static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0, el_number_N=0; static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; int num_H, num_non_iso_H, num_impl_iso_H, bDisconnect = 1; int j, val, neigh, iO=-1, iC, k=-1; if ( 0 == el_number_C ) { /* one time initialization */ el_number_C = get_periodic_table_number( "C" ); el_number_O = get_periodic_table_number( "O" ); el_number_H = get_periodic_table_number( "H" ); el_number_N = get_periodic_table_number( "N" ); el_number_F = get_periodic_table_number( "F" ); el_number_Cl= get_periodic_table_number( "Cl" ); el_number_Br= get_periodic_table_number( "Br" ); el_number_I = get_periodic_table_number( "I" ); } if ( at[i].el_number != el_number_N ) return 0; /* check for NH4-O-C... -> NH3 + HO-C... */ val = at[i].valence; num_impl_iso_H = NUM_ISO_H(at,i); num_non_iso_H = at[i].num_H; num_H = num_non_iso_H + num_impl_iso_H; if ( val + num_H == 5 ) { int num_O = 0; memset( num_explicit_H, 0, (NUM_H_ISOTOPES+1)*sizeof(num_explicit_H[0]) ); for ( j = 0; j < val; j ++ ) { /* looking for O: H4N-O-C... */ neigh = at[i].neighbor[j]; if ( at[neigh].num_H || at[neigh].charge && (at[neigh].el_number != el_number_O || at[neigh].charge + at[i].charge) || at[neigh].radical && at[neigh].radical != RADICAL_SINGLET ) { bDisconnect = 0; break; /* reject */ } if ( at[neigh].el_number == el_number_H && at[neigh].valence == 1 && !at[neigh].charge && !at[neigh].radical ) { num_H ++; /* at this point at[].num_H does not include explicit H count */ num_non_iso_H += (0==at[neigh].iso_atw_diff); num_explicit_H[at[neigh].iso_atw_diff] ++; /* explicit H on N */ } else if ( at[neigh].el_number == el_number_O && at[neigh].valence == 2 && !num_O ) { num_O ++; /* found O: N-O- */ iO = neigh; k = j; iC = at[iO].neighbor[at[iO].neighbor[0] == i]; if ( at[iC].el_number != el_number_C || /* at[iC].num_H || at[iC].chem_bonds_valence != 4 || */ at[iC].charge || at[iC].radical && at[iC].radical != RADICAL_SINGLET /*|| at[iC].valence == at[iC].chem_bonds_valence*/ ) { bDisconnect = 0; break; /* reject */ } } else if ( (at[neigh].el_number == el_number_F || at[neigh].el_number == el_number_Cl || at[neigh].el_number == el_number_Br || at[neigh].el_number == el_number_I ) && at[neigh].valence == 1 && at[neigh].chem_bonds_valence == 1 && !at[neigh].charge && !NUMH(at,neigh) && !num_O ) { num_O ++; /* found O: N-O- */ iO = neigh; k = j; iC = -1; } else { bDisconnect = 0; break; /* reject */ } } if ( bDisconnect && (num_O != 1 || num_H != 4) ) { bDisconnect = 0; /* reject */ } } else { bDisconnect = 0; } if ( bDisconnect ) { *piO = iO; *pk = k; } return bDisconnect; } /*************************************************************************************************/ int DisconnectAmmoniumSalt ( inp_ATOM *at, int iN, int iO, int k, S_CHAR *num_explicit_H ) { /* disconnect NH4-O from O */ /* Note: iO = at[iN].neighbor[k], at[iN] is N, at[iO].neighbor[0] is either N=at[iN] or C=at[iC] */ int nMove_H_iso_diff = -1; /* do not move explicit H */ int j, neigh, iso_diff, neigh_pos; static U_CHAR el_number_H = 0; int val = at[iN].valence; if ( !el_number_H ) { el_number_H = get_periodic_table_number( "H" ); } if ( at[iN].charge && !(at[iN].charge + at[iO].charge) ) { at[iN].charge = at[iO].charge = 0; /* remove charges */ } neigh_pos = (at[iO].valence == 2)? (at[iO].neighbor[1] == iN) : 0; /* position of at[iN] in the neigh list of iO */ /* disconnect bond O-N */ RemoveInpAtBond( at, iO, neigh_pos ); RemoveInpAtBond( at, iN, k ); val --; /* move 1 H from NH4 to O- or Cl */ /* find non-isotopic or the lightest isotopic H to move from N to O */ for ( iso_diff = 0; iso_diff <= NUM_H_ISOTOPES; iso_diff ++ ) { if ( !iso_diff ) { /* find non-isotopic H */ if ( at[iN].num_H ) { at[iN].num_H --; /* move non-isotopic implicit H */ at[iO].num_H ++; break; } else if ( num_explicit_H[0] ) { nMove_H_iso_diff = 0; /* flag: move explicit non-isotopic H */ break; } } else { /* find isotopic H */ if ( at[iN].num_iso_H[iso_diff] ) { at[iN].num_iso_H[iso_diff] --; /* move implicit isotopic H, atw = 1 */ at[iO].num_iso_H[iso_diff] ++; break; } else if ( num_explicit_H[iso_diff] ) { nMove_H_iso_diff = iso_diff; /* flag: move explicit isotopic H, atw = 1 */ break; } } } if ( nMove_H_iso_diff >= 0 ) { /* move explicit H, it is isotopic if nMove_H_iso_diff > 0 */ double dist2_H_O, min_dist2_H_O = -1.0; int jH = -1, iH = -1; for ( j = 0; j < val; j ++ ) { /* looking H in N-H such that H-O is shortest */ neigh = at[iN].neighbor[j]; if ( at[neigh].el_number == el_number_H && at[neigh].iso_atw_diff == nMove_H_iso_diff ) { dist2_H_O = (at[neigh].x - at[iO].x) * (at[neigh].x - at[iO].x) + (at[neigh].y - at[iO].y) * (at[neigh].y - at[iO].y) + (at[neigh].z - at[iO].z) * (at[neigh].z - at[iO].z); if ( min_dist2_H_O < 0.0 || min_dist2_H_O > dist2_H_O ) { min_dist2_H_O = dist2_H_O; iH = neigh; jH = j; } } } /* reconnect; bonds do not need changes except stereo */ neigh_pos = at[iO].valence; at[iO].neighbor[neigh_pos] = iH; at[iO].bond_stereo[neigh_pos] = 0; at[iO].bond_type[neigh_pos] = at[iH].bond_type[0]; at[iO].chem_bonds_valence += at[iH].bond_type[0]; at[iO].valence ++; at[iH].neighbor[0] = iO; at[iH].bond_stereo[0] = 0; /* disconnect H from N */ RemoveInpAtBond( at, iN, jH ); val --; if ( k > jH ) { k --; } } return 1; } /*************************************************************************************************/ int bIsMetalSalt( inp_ATOM *at, int i ) { int type, val, k, iO, iC, j, neigh; int bDisconnect = 1; static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0; static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; if ( 0 == el_number_C ) { /* one time initialization */ el_number_C = get_periodic_table_number( "C" ); el_number_O = get_periodic_table_number( "O" ); el_number_H = get_periodic_table_number( "H" ); el_number_F = get_periodic_table_number( "F" ); el_number_Cl= get_periodic_table_number( "Cl" ); el_number_Br= get_periodic_table_number( "Br" ); el_number_I = get_periodic_table_number( "I" ); } /* check for a metal atom: metal atom should be connected and be a metal */ if ( !(val = at[i].valence) || !(type = get_el_type( at[i].el_number )) || !(type & IS_METAL) ) { bDisconnect = 0; /* reject */ } else /* metal atom should not have adjacent H or multiple bonds or radical */ if ( at[i].num_H ) { bDisconnect = 0; /* reject */ } else /* check valence */ if ( at[i].charge == 0 && ( (type & 1) && val == get_el_valence( at[i].el_number, 0, 0 ) || (type & 2) && val == get_el_valence( at[i].el_number, 0, 1 ) ) || at[i].charge > 0 && (type & 1) && val == get_el_valence( at[i].el_number, at[i].charge, 0 ) ) { ; /* accept */ } else { bDisconnect = 0; /* reject */ } if ( bDisconnect ) { /************************************************************************* * | * * check M neighbors. Disconnect if all neighbors are M-O-C# or M-O-C= * * | * *************************************************************************/ for ( k = 0; k < at[i].valence; k ++ ) { iO = at[i].neighbor[k]; /* halogenide 2004-07-08 */ if ( (at[iO].el_number == el_number_F || at[iO].el_number == el_number_Cl || at[iO].el_number == el_number_Br || at[iO].el_number == el_number_I ) && at[iO].valence == 1 && at[iO].chem_bonds_valence == 1 && !at[iO].charge && !(at[iO].radical && at[iO].radical != RADICAL_SINGLET) && !NUMH(at,iO) ) { ; /* found */ } else { /* -O-C= */ if ( at[iO].el_number != el_number_O || NUMH(at, iO) || at[iO].valence != 2 || at[iO].charge || at[iO].radical && at[iO].radical != RADICAL_SINGLET || at[iO].valence != at[iO].chem_bonds_valence ) { bDisconnect = 0; /* reject */ break; } iC = at[iO].neighbor[at[iO].neighbor[0] == i]; if ( at[iC].el_number != el_number_C || at[iC].num_H || at[iC].chem_bonds_valence != 4 || at[iC].charge || at[iC].radical && at[iC].radical != RADICAL_SINGLET || at[iC].valence == at[iC].chem_bonds_valence ) { bDisconnect = 0; /* reject */ break; } for ( j = 0; j < at[iC].valence; j ++ ) { neigh = at[iC].neighbor[j]; if ( at[neigh].el_number == el_number_H ) { break; } } if ( j != at[iC].valence ) { bDisconnect = 0; /* reject */ break; } } } } return bDisconnect; } /*************************************************************************************************/ int DisconnectMetalSalt( inp_ATOM *at, int i ) { int k, iO; /* disconnect metal atom or ion at[i] */ for ( k = 0; k < at[i].valence; k ++ ) { iO = at[i].neighbor[k]; if ( at[iO].valence == 2 ) { if ( at[iO].neighbor[0] == i ) { /* assuming atom O always has 2 bonds */ /* copy the remaining neighbor to the 0 position */ at[iO].neighbor[0] = at[iO].neighbor[1]; at[iO].bond_stereo[0] = at[iO].bond_stereo[1]; at[iO].bond_type[0] = at[iO].bond_type[1]; } /* clear neighbor at position 1 */ at[iO].neighbor[1] = 0; at[iO].bond_stereo[1] = 0; at[iO].bond_type[1] = 0; } else { /* clear neighbor at position 1 */ at[iO].neighbor[0] = 0; at[iO].bond_stereo[0] = 0; at[iO].bond_type[0] = 0; } /* make O negatively charged */ at[iO].charge = -1; /* reduce O valence to account for the removed single bond */ at[iO].valence --; at[iO].chem_bonds_valence --; /* clear metal neighbor (O) */ at[i].neighbor[k] = 0; at[i].bond_stereo[k] = 0; at[i].bond_type[k] = 0; /* add a positive charge to the metal */ at[i].charge ++; } /* set metal valence to zero because it has been disconnected */ at[i].valence = 0; at[i].chem_bonds_valence = 0; return k; } /*************************************************************************************************/ int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ) { int i, k, iO, num_changes, val; S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; inp_ATOM *at = orig_inp_data->at; int num_at = orig_inp_data->num_inp_atoms; /* check each atom */ for ( i = 0, num_changes = 0; i < num_at; i ++ ) { if ( !(val = at[i].valence) || /* disconnected atom */ val != at[i].chem_bonds_valence || /* a bond has higher multiplicity than 1 */ at[i].radical && at[i].radical != RADICAL_SINGLET /* radical */ ) { continue; /* reject */ } if ( bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { if ( bDisconnect ) { DisconnectAmmoniumSalt ( at, i, iO, k, num_explicit_H ); orig_inp_data->num_inp_bonds --; } /* count disconnected atoms */ num_changes ++; } else if ( bIsMetalSalt( at, i ) ) { if ( bDisconnect ) { k = DisconnectMetalSalt( at, i ); orig_inp_data->num_inp_bonds -= k; } num_changes ++; } } return num_changes; } /*****************************************************************************/ /* Important: Salt disconnection is independent from coord. disconnection: */ /* because different atoms are disconnected. */ /* However, sal disconnection may need to be rerun after metal disconnection */ /* because metal disconnection may make certain atoms be eligible for salt */ /* disconnection */ /*****************************************************************************/ int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence) { int type, at_valence, num_H; /* if ( !at[i].valence ) */ if ( !(type = get_el_type( at[i].el_number )) || !(type & IS_METAL ) ) { return 0; } num_H = NUMH(at,i); at_valence = num_H + at[i].chem_bonds_valence; if ( !at_valence ) { return 0; /* nothing to disconnect */ } if ( bCheckMetalValence ) { if ( abs(at[i].charge) > 1 ) { return 1; /* multiple charges */ } for ( i = 0; i < 2 && (i & type); i ++ ) { if ( at_valence == get_el_valence( at[i].el_number, at[i].charge, i ) ) { return 2; /* atom has normal valence */ } } } return 1; } /*****************************************************************************/ int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) { int i, j, k, iO, num_changes, val, bRadOrMultBonds, num_impl_H = 0; S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; inp_ATOM *at = orig_inp_data->at; int num_at = orig_inp_data->num_inp_atoms; int *nNumImplH = &orig_inp_data->bDisconnectCoord; /* check each atom */ for ( i = 0, num_changes = 0; i < num_at; i ++ ) { if ( !(val = at[i].valence) && !NUMH(at,i) ) { continue; /* disconnected atom */ } bRadOrMultBonds = (val == 0) || (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { ; } else if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { ; } else if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { num_impl_H += NUMH(at,i); num_changes ++; } else if ( 2 == j && bTautFlagsDone ) { *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; } } if ( nNumImplH ) *nNumImplH = num_changes? num_impl_H+1 : 0; return num_changes; } /*****************************************************************************/ #if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ) { int i; inp_ATOM *at; if ( orig_inp_data && (at = orig_inp_data->at) ) { int num_at = orig_inp_data->num_inp_atoms; /* check each atom */ for ( i = 0; i < num_at; i ++ ) { if ( IS_METAL & get_el_type( at[i].el_number ) ) { return 1; } } } return 0; } #endif /***************************************************************************** { "F", 19, 19, 18.998403220, 0 , 0, {{0,}, {0,}, {1,}, {2,}, {3,5}, },}, { "Cl", 35, 35, 34.968852730, 0 , 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,}, },}, { "Br", 80, 79, 78.918336100, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, },}, { "I", 127, 127, 126.904500000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, { "At", 210, 210, 209.987100000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, { "N", 14, 14, 14.003074000, 0 , 0, {{1,}, {2,}, {3,5}, {4,}, {3,}, },}, { "P", 31, 31, 30.973762000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, { "As", 75, 75, 74.921594200, 0 , 0, {{0,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, { "Sb", 122, 121, 120.903800000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,}, },}, { "O", 16, 16, 15.994914630, 0 , 0, {{0,}, {1,}, {2,}, {3,5,}, {4,}, },}, { "S", 32, 32, 31.972070700, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,}, },}, { "Se", 79, 80, 79.916519600, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, },}, { "Te", 128, 130, 129.906200000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, { "Po", 209, 209, 208.982400000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, { "B", 11, 11, 11.009300000, 0 , 0, {{3,}, {4,}, {3,}, {2,}, {1,}, },}, *****************************************************************************/ int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) /*inp_ATOM *atom, int num_atoms, int nNumExplH, int *new_num_atoms */ { int i, j, k, n, iO, num_changes, val, bRadOrMultBonds; int num_impl_H, num_at, err, num_disconnected; S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; static char elnumber_Heteroat[16] = {'\0', }; static int num_halogens; inp_ATOM *at = NULL; S_CHAR *bMetal = NULL; inp_ATOM *atom = orig_inp_data->at; int num_atoms = orig_inp_data->num_inp_atoms; int nNumExplH = (orig_inp_data->bDisconnectCoord > 0)? orig_inp_data->bDisconnectCoord - 1 : 0; AT_NUMB *nOldCompNumber = orig_inp_data->nOldCompNumber; err = 0; num_impl_H = 0; num_at = num_atoms; num_disconnected = 0; if ( !(at = (inp_ATOM *)inchi_calloc( num_at + nNumExplH, sizeof(at[0] ) )) || !(bMetal = ( S_CHAR *)inchi_calloc( num_at + nNumExplH, sizeof(bMetal[0]) )) ) { err = 1; goto exit_function; } if (!elnumber_Heteroat[0] ) { i = 0; /* halogens */ elnumber_Heteroat[i++] = (char)get_periodic_table_number( "F" ); /* 0 */ elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Cl" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Br" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "I" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "At" ); /* 4 */ num_halogens = i; /* other non-metal */ elnumber_Heteroat[i++] = (char)get_periodic_table_number( "N" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "P" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "As" ); /*elnumber_Heteroat[i++] = get_periodic_table_number( "Sb" );*/ /* metal 10-28-2003 */ elnumber_Heteroat[i++] = (char)get_periodic_table_number( "O" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "S" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Se" ); elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Te" ); /*elnumber_Heteroat[i++] = get_periodic_table_number( "Po" );*/ /* metal 10-28-2003 */ elnumber_Heteroat[i++] = (char)get_periodic_table_number( "B" ); elnumber_Heteroat[i++] = 0; } memcpy( at, atom, num_atoms * sizeof(at[0]) ); /* check each atom, mark metals */ for ( i = 0, k = 0, num_changes = 0; i < num_atoms; i ++ ) { if ( !(val = at[i].valence) && !NUMH(at,i) ) { continue; /* disconnected atom */ } bRadOrMultBonds = (val == 0) || (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { ; } else if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { ; } else if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { num_impl_H += (k = NUMH(at,i)); bMetal[i] = 1+k; num_changes ++; } else if ( 2 == j && bTautFlagsDone ) { *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; } } if ( num_impl_H != nNumExplH ) { err = 2; goto exit_function; } /* replace implicit H atoms with explicit H atoms */ for ( i = 0; i < num_atoms && 0 < num_impl_H; i ++ ) { if ( bMetal[i] <= 1 ) { continue; } for ( k = 0; k < NUM_H_ISOTOPES+1; k ++ ) { n = k? at[i].num_iso_H[k-1] : at[i].num_H; for ( j = 0; j < n; j ++ ) { if ( num_at >= num_atoms + nNumExplH ) { err = 3; goto exit_function; } at[num_at].elname[0] = 'H'; at[num_at].el_number = get_periodic_table_number(at[num_at].elname); at[num_at].iso_atw_diff = k; at[num_at].component = at[i].component; move_explicit_Hcation(at, num_at+1, i, num_at, 1); at[num_at].orig_at_number = num_at+1; num_at ++; num_impl_H --; bMetal[i] --; if ( k ) { at[i].num_iso_H[k-1] --; } else { at[i].num_H --; } } } if ( bMetal[i] != 1 ) { err = 4; goto exit_function; } } if ( num_at != num_atoms + nNumExplH ) { err = 5; goto exit_function; } /* disconnect metal - ligand bonds */ for ( i = 0; i < num_atoms; i ++ ) { if ( !bMetal[i] ) { continue; } /* disconnect metal atom M Note: Defect in case of bridging ligands: M M M M M M(+) \ / will be transformed to , not to N(+) N(+) N(-) / \ / \ / \ R R R R R R Non-bridging are OK: M R M(+) R \ / / N(+) ---> N / \ / \ R R R R */ for ( j = at[i].valence-1; 0 <= j; j -- ) { if ( j < at[i].valence && !bMetal[ (int)at[i].neighbor[j] ] ) { /* do not break metal-metal bond here */ num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, num_halogens, num_atoms, i, j, bTautFlagsDone ); } } } /* disconnect metal-metal bonds */ for ( i = 0; i < num_atoms; i ++ ) { if ( !bMetal[i] ) { continue; } for ( j = at[i].valence-1; 0 <= j; j -- ) { if ( j < at[i].valence && bMetal[ (int)at[i].neighbor[j] ] ) { /* break metal-metal bond here */ num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, num_halogens, num_atoms, i, j, bTautFlagsDone ); } } } exit_function: if ( !num_disconnected ) { err = 6; } if ( at && err ) { inchi_free( at ); at = NULL; } if ( atom && at ) { /* changed if ( at ) to if ( atom && at ) 2004-04-03 */ inchi_free( atom ); atom = NULL; } if ( bMetal ) inchi_free( bMetal ); if ( at ) { orig_inp_data->at = at; orig_inp_data->num_inp_atoms = num_at; } return err? -err : num_disconnected; } /*****************************************************************************/ int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ) { int i, j, iLigand, neigh, val; int metal_neigh_ord[MAXVAL], num_neigh_arom_bonds[MAXVAL]; int num_metal_neigh, num_disconnections; int num_del_arom_bonds, num_tot_arom_bonds, new_charge; char *p; iLigand = at[iMetal].neighbor[jLigand]; num_metal_neigh = 0; num_disconnections = 0; num_del_arom_bonds = num_tot_arom_bonds = 0; /* find bonds to disconnect */ for ( i = 0; i < at[iLigand].valence; i ++ ) { num_neigh_arom_bonds[i] = 0; neigh = (int)at[iLigand].neighbor[i]; if ( neigh < num_atoms && bMetal[ neigh ] ) { metal_neigh_ord[ num_metal_neigh ++ ] = i; if ( at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE ) { /* aromatic bond */ for ( j = 0; j < at[neigh].valence; j ++ ) { num_neigh_arom_bonds[i] += ( at[neigh].bond_type[j] > BOND_TYPE_TRIPLE ); } num_del_arom_bonds ++; } } num_tot_arom_bonds += (at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE); } /* Disconnect */ if ( num_del_arom_bonds ) { /* fix chem_valence of the ligand and its neighbors in case of disconnecting arom. bonds */ /* because in this case special care should be taken of updating at[].chem_bonds_valence */ for ( i = 0; i < num_metal_neigh; i ++ ) { j = metal_neigh_ord[i]; if ( num_neigh_arom_bonds[j] ) { neigh = at[iLigand].neighbor[j]; at[neigh].chem_bonds_valence -= num_neigh_arom_bonds[j]/2 - (num_neigh_arom_bonds[j]-1)/2; } } at[iLigand].chem_bonds_valence -= num_tot_arom_bonds/2 - (num_tot_arom_bonds-num_del_arom_bonds)/2; } /* disconnect in reverse order, otherwise the metal_neigh_ord[i] becomes invalid after the first disconnection */ for ( i = num_metal_neigh-1; 0 <= i; i -- ) { num_disconnections += DisconnectInpAtBond( at, nOldCompNumber, iLigand, metal_neigh_ord[i] ); } /* attempt to change ligand charge to make its valence 'natural' */ i = num_tot_arom_bonds - num_del_arom_bonds; if ( i && i != 2 && i != 3 || at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET || !(p = strchr( elnumber_Heteroat, at[iLigand].el_number ) ) ) { goto exit_function; /* non-standard atom */ } val = at[iLigand].chem_bonds_valence + NUMH(at, iLigand); new_charge = MAX_ATOMS; /* impossible value */ if ( !val ) { if ( p - elnumber_Heteroat < num_halogens ) { new_charge = -1; } } else { for ( i = -1; i <= 1; i ++ ) { if ( val == get_el_valence( at[iLigand].el_number, i, 0 ) ) { new_charge = i; /* found charge that fits chem. valence */ break; } } } if ( new_charge != MAX_ATOMS ) { if ( (new_charge != at[iLigand].charge || (at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET)) && 1 == num_metal_neigh ) { if ( 1 == new_charge && 4 == val && 2 == at[iLigand].valence && 4 == at[iLigand].chem_bonds_valence && at[iLigand].bond_type[0] == at[iLigand].bond_type[1] ) { ; /* do not add +1 charge to disconnected =N=, etc. 2004-10-27 */ } else { if ( bTautFlagsDone && new_charge != at[iLigand].charge ) { *bTautFlagsDone |= TG_FLAG_MOVE_CHARGE_COORD_DONE; } at[iMetal].charge -= new_charge - at[iLigand].charge; at[iLigand].charge = new_charge; /*at[iLigand].radical = 0;*/ } } } exit_function: return num_disconnections; /* ret;*/ } /****************************************************************************************/ double dist3D( inp_ATOM *at1, inp_ATOM *at2 ) { double dx = at1->x - at2->x; double dy = at1->y - at2->y; double dz = at1->z - at2->z; return sqrt( dx*dx+dy*dy+dz*dz ); } /****************************************************************************************/ #define MIN_BOND_LENGTH (1.0e-6) #define MIN_COS (1.0e-6) #define MIN_BOND_LENGTH2 (MIN_BOND_LENGTH*MIN_BOND_LENGTH) #define MAX_BOND_LENGTH (1.0e30) /****************************************************************************************/ double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents, double min_dist[], int num_segm ) { /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ const double one_pi = 3.14159265358979323846; /* M_PI */ const double two_pi = 2.0*one_pi; const double f_step = two_pi / num_segm; const double h_step = f_step/2.0; int i, j, k, kk, ki, kn, n, num_bonds; double xi, yi, xn, yn, cross_prod_in, dot_prod_in, xni, yni, rni, tni, rmin; double fi, fk, fn, ft, rt, rk, ri, rn, c, ave_bond_len; for ( i = 0; i < num_segm; i ++ ) { min_dist[i] = MAX_BOND_LENGTH; /* more than any distance */ } num_bonds = 0; ave_bond_len = 0.0; for ( i = 0; i < num_at; i ++ ) { if ( i != iat && i != iat_H && (bInAllComponents || at[i].component == at[iat].component) ) { for ( j = 0; j < at[i].valence; j ++ ) { n = at[i].neighbor[j]; if ( (n > i && n != iat) || n == iat_H ) continue; #if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) if ( n == iat ) { int stop = 1; /* */ } #endif xi = at[i].x - at[iat].x; /* ri; i != iat */ yi = at[i].y - at[iat].y; xn = at[n].x - at[iat].x; /* rn; possibly n == iat */ yn = at[n].y - at[iat].y; cross_prod_in = xi*yn - xn*yi; /* ((r(i)-r(iat)) x (r(n)-r(iat)) */ if ( cross_prod_in < -0.01*MIN_BOND_LENGTH2 ) { /* make sure the r(i)->r(n) vector is counterclockwise around at[iat] */ inchi_swap( (char*)&xi, (char*)&xn, sizeof(xi) ); inchi_swap( (char*)&yi, (char*)&yn, sizeof(yi) ); cross_prod_in = -cross_prod_in; } xni = xn - xi; /* r(n)->r(i) */ yni = yn - yi; rni = xni*xni + yni*yni; if ( rni > 0.01*MIN_BOND_LENGTH2 ) { /* vector length |ri->rn| is not too small */ /* arrowhead of the vector r(t) = ri + (rn-ri)*t; 0 <= t <= 1 points to the bond ri->rn */ /* r(tni) is perpendicular to the bond ri->rn so that min|r(t)| = r(tni) = |tni|*rni */ tni = -(xni*xi + yni*yi)/rni; /* find min. distance from n-i bond to at[iat] */ if ( tni < 0.0 ) { rmin = sqrt( xi*xi + yi*yi ); } else if ( tni > 1.0 ) { rmin = sqrt( xn*xn + yn*yn ); } else { rmin = sqrt(tni*tni*rni); } ave_bond_len += sqrt( rni ); num_bonds ++; } else { /* zero length i-n bond */ tni = 0.5; /* fake */ rmin = sqrt( xi*xi + yi*yi ); /* arbitrarily choose one */ } if ( rmin >= 0.1*MIN_BOND_LENGTH ) { /* at[iat] does not belong to at[i]-at[n] bond */ int bCalc_rt = 1; fi = atan2( yi, xi ); fn = (n == iat)? fi : atan2( yn, xn ); if ( fi > fn ) { /* make sure fn - fi >= 0 */ fn += two_pi; } if ( fi < 0.0 ) { fi += two_pi; fn += two_pi; } ki = (int)floor((fi+h_step)/f_step); /* cast does not match function type */ kn = (int)floor((fn+h_step)/f_step); /* the bond may affect several segments */ for ( k = ki; k <= kn; k ++ ) { kk = k % num_segm; if ( min_dist[kk] < rmin ) continue; if ( bCalc_rt ) { if ( n == iat ) { ft = fi; rt = rmin; } else { double xt, yt; xt = xi + xni*tni; yt = yi + yni*tni; ft = atan2( yt, xt ); rt = sqrt(xt*xt + yt*yt); } bCalc_rt = 0; } fk = f_step * kk; c = fabs(cos( fk - ft )); if ( c < MIN_COS ) c = MIN_COS; rk = rt / c; if ( min_dist[kk] > rk ) { min_dist[kk] = rk; } } } else { /* rmin < 0.1*MIN_BOND_LENGTH */ ri = xi*xi + yi*yi; rn = xn*xn + yn*yn; if ( ri > MIN_BOND_LENGTH2 && rn > MIN_BOND_LENGTH2 ) { dot_prod_in = xn*xi + yn*yi; /* a very short bond */ if ( dot_prod_in > 0.01*MIN_BOND_LENGTH2 ) { /* bond does not cross at[iat] */ double fyixi = atan2( yi, xi ); if ( fyixi < 0.0 ) fyixi += two_pi; kk = (int)floor((fyixi+h_step)/f_step) % num_segm; if ( min_dist[kk] > rmin ) { min_dist[kk] = rmin; } } else if ( dot_prod_in < -0.01*MIN_BOND_LENGTH2 ) { /* bond does cross at[iat] */ double fyixi = atan2( yi, xi ); if ( fyixi < 0.0 ) fyixi += two_pi; kk = (int)floor((fyixi+h_step)/f_step) % num_segm; if ( min_dist[kk] > rmin ) { min_dist[kk] = rmin; } fyixi += one_pi; kk = (int)floor((fyixi+h_step)/f_step) % num_segm; if ( min_dist[kk] > rmin ) { min_dist[kk] = rmin; } } else { ; /* error, should not happen */ } } else if ( ri <= MIN_BOND_LENGTH2 && rn <= MIN_BOND_LENGTH2 ) { /* a very short bond coincides with at[iat]; ignore */ ; } else { /* one end of the bond coincides with at[iat] */ fi = ri>rn? atan2( yi, xi) : atan2( yn, xn ); if ( fi < 0.0 ) fi += two_pi; kk = (int)floor((fi+h_step)/f_step) % num_segm; if ( min_dist[kk] > rmin ) { min_dist[kk] = rmin; } } } } } } if ( num_bonds ) { return ave_bond_len / (double)num_bonds; } else { return 0.0; } } /****************************************************************************************/ int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents) { #define NUM_SEGM 20 /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ const double one_pi = 3.14159265358979323846; /* M_PI */ const double two_pi = 2.0*one_pi; const double f_step = two_pi / NUM_SEGM; const double h_step = f_step/2.0; double min_dist[NUM_SEGM]; int nB, i, k, kk, next, val; double r, r0, xd, yd, zd, xr, yr, zr, ave_bond_len; /*double step = 4.0*atan(1.0)/NUM_SEGM;*/ /* find at[iat] neighbors coordinates */ xd=yd=zd=0.0; if ( at[iat].valence ) { for ( i = 0, nB=0, r = 0.0; i < at[iat].valence; i ++ ) { next = at[iat].neighbor[i]; xd += at[next].x; yd += at[next].y; zd += at[next].z; r += dist3D( at+iat, at+next ); nB ++; } xd /= (double)nB; yd /= (double)nB; zd /= (double)nB; r /= (double)nB; r0 = sqrt((double)(xd-at[iat].x)*(xd-at[iat].x) + (double)(yd-at[iat].y)*(yd-at[iat].y)); } else { if ( at[iat_H].valence ) { r = dist3D( at+iat_H, at+ (int)at[iat_H].neighbor[0] ); } else { r = 0.0; } r0 = 0.0; } ave_bond_len = GetMinDistDistribution( at, num_at, iat, iat_H, bInAllComponents, min_dist, NUM_SEGM ); if ( r < MIN_BOND_LENGTH && ave_bond_len > MIN_BOND_LENGTH ) { r = ave_bond_len; /* ave_bond_len = 0.0 may mean that it is 0D structure */ } if ( r > MIN_BOND_LENGTH ) { /* process non-zero bond lengths */ double f; if ( 10.0*r0 < r ) { xr = -r; /* arbitrary */ yr = 0.0; zr = 0.0; } else { /* if ( r0 < MIN_BOND_LENGTH ) { r0 = 1.0; } */ xr = r * ( at[iat].x - xd )/r0; yr = r * ( at[iat].y - yd )/r0; /* length = r */ zr = r * ( at[iat].z - zd )/r0; /* -- test: opposire direction -- xr = -r * ( at[iat].x - xd )/r0; yr = -r * ( at[iat].y - yd )/r0; zr = -r * ( at[iat].z - zd )/r0; */ if ( xr*xr + yr*yr < 0.04*r*r ) { xr = -r; yr = 0.0; } } r = sqrt( xr*xr + yr*yr ); f = atan2( yr, xr ); if ( f < 0.0 ) f += two_pi; kk = (int)floor((f+h_step)/f_step) % NUM_SEGM; /* cast does not match function type by design */ if ( min_dist[kk] < 1.5* r ) { double dist = 1.5*r; int start=-1, len=0, start_max=-1, len_max=0; again: /* look for longest kk interval with min_dist[kk] >= dist */ for ( k = 0, start = 0, len = 0, len_max = 0; k < 2*NUM_SEGM; k ++ ) { kk = k % NUM_SEGM; if ( min_dist[kk] >= dist ) { if ( !len ++) { start = k; } } else { if ( len > len_max ) { len_max = len; start_max = start; } len = 0; } } if ( !len_max ) { if ( dist > 0.1*r ) { dist *= 0.75; goto again; } else { goto done; /* do it anyway */ } } else { /* found a good sector */ f = f_step * (start_max + (double)(len_max - 1)/2.0); r0 = dist / 1.5; xr = r0 * cos(f); yr = r0 * sin(f); zr = zr/r*r0; } } } else { xr = yr = zr = 0; } done: if ( at[iat_H].valence ) { /* disconnect H */ next = at[iat_H].neighbor[0]; for ( i = 0; i < at[next].valence; i ++ ) { if ( at[next].neighbor[i] == iat_H ) { RemoveInpAtBond( at, next, i ); i = 0; /* success */ break; } } } else { /* isolated H+ cation */ next = iat_H; i = 0; at[iat_H].valence = 1; at[iat_H].chem_bonds_valence = 1; at[iat_H].bond_type[0] = BOND_TYPE_SINGLE; } if ( 0 == i /*i < at[next].valence*/ ) { /* move charge */ if ( at[next].charge > 0 && at[iat].charge < 0 ) { at[next].charge --; at[iat].charge ++; } /* connect H to at[iat] */ val = at[iat].valence; at[iat].neighbor[val] = iat_H; at[iat].bond_type[val] = at[iat_H].bond_type[0]; at[iat].bond_stereo[val] = 0; at[iat].chem_bonds_valence += at[iat_H].bond_type[0]; at[iat].valence = val+1; at[iat_H].component = at[iat].component; at[iat_H].neighbor[0] = iat; at[iat_H].bond_stereo[0] = 0; /* possible loss of stereo info */ at[iat_H].x = at[iat].x + xr; at[iat_H].y = at[iat].y + yr; at[iat_H].z = at[iat].z + zr; return 1; /* success */ } return 0; /* failed */ } /****************************************************************************************/ int get_iat_number( int el_number, const int el_num[], int el_num_len ) { int i; for ( i = 0; i < el_num_len; i ++ ) { if ( el_num[i] == el_number ) return i; } return -1; } /*#endif*/ /* } DISCONNECT_SALTS */ typedef enum tagIonAtomType { IAT_H=0, IAT_C, IAT_N, IAT_P, IAT_O, IAT_S, IAT_Se, IAT_Te, IAT_F, IAT_Cl, IAT_Br, IAT_I, IAT_MAX } ION_ATOM_TYPE; #if ( READ_INCHI_STRING == 1 ) /****************************************************************************************/ int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ) { inp_ATOM *at = atom + iat, *at2; static int el_num[IAT_MAX]; int j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, cur_num_iso_H; if ( !el_num[IAT_H]) { el_num[IAT_H ] = get_periodic_table_number( "H" ); el_num[IAT_C ] = get_periodic_table_number( "C" ); el_num[IAT_N ] = get_periodic_table_number( "N" ); el_num[IAT_P ] = get_periodic_table_number( "P" ); el_num[IAT_O ] = get_periodic_table_number( "O" ); el_num[IAT_S ] = get_periodic_table_number( "S" ); el_num[IAT_Se] = get_periodic_table_number( "Se"); el_num[IAT_Te] = get_periodic_table_number( "Te"); el_num[IAT_F ] = get_periodic_table_number( "F" ); el_num[IAT_Cl] = get_periodic_table_number( "Cl"); el_num[IAT_Br] = get_periodic_table_number( "Br"); el_num[IAT_I ] = get_periodic_table_number( "I" ); } if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { return 0; } if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { return 0; } val = -1; switch( iat_numb ) { case IAT_N: case IAT_P: is_N = 1; val = 3+at->charge; break; case IAT_O: case IAT_S: case IAT_Se: case IAT_Te: is_O = 1; val = 2+at->charge; break; case IAT_F: case IAT_Cl: case IAT_Br: case IAT_I: if ( at->charge == 0 ) { is_Cl = 1; /* isolated HCl */ val = 1; } break; case IAT_H: if ( at->valence == 0 && at->charge == 1 ) { is_H = 1; /* isolated proton */ val = 0; } } if ( val < 0 ) { return 0; } num_H = NUMH(at,0); if ( val != at->chem_bonds_valence + num_H ) { return 0; } if ( is_H ) { return 2; /* H atom */ } else { cur_num_iso_H = 0; for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { at2 = atom + (int)at->neighbor[j]; if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { return 0; /* adjacent charged/radical atoms: do not neutralizate */ } } } return 1; } #endif /****************************************************************************************/ int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ) { static int el_num[IAT_MAX]; int i, j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, num_iso_H, cur_num_iso_H, num_iso_atoms; inp_ATOM *at, *at2; /* one time initialization */ if ( !el_num[IAT_H]) { el_num[IAT_H ] = get_periodic_table_number( "H" ); el_num[IAT_C ] = get_periodic_table_number( "C" ); el_num[IAT_N ] = get_periodic_table_number( "N" ); el_num[IAT_P ] = get_periodic_table_number( "P" ); el_num[IAT_O ] = get_periodic_table_number( "O" ); el_num[IAT_S ] = get_periodic_table_number( "S" ); el_num[IAT_Se] = get_periodic_table_number( "Se"); el_num[IAT_Te] = get_periodic_table_number( "Te"); el_num[IAT_F ] = get_periodic_table_number( "F" ); el_num[IAT_Cl] = get_periodic_table_number( "Cl"); el_num[IAT_Br] = get_periodic_table_number( "Br"); el_num[IAT_I ] = get_periodic_table_number( "I" ); } num_iso_H = 0; num_iso_atoms = 0; for ( i = 0, at = atom; i < num_atoms; i ++, at ++ ) { num_iso_atoms += ( at->iso_atw_diff != 0 || NUM_ISO_H(at,0) ); /* isotopic atoms and implicit isotopic H */ if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { continue; } if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { continue; } val = -1; switch( iat_numb ) { case IAT_N: case IAT_P: is_N = 1; val = 3+at->charge; break; case IAT_O: case IAT_S: case IAT_Se: case IAT_Te: is_O = 1; val = 2+at->charge; break; case IAT_F: case IAT_Cl: case IAT_Br: case IAT_I: if ( at->charge == 0 ) { is_Cl = 1; /* isolated HCl */ val = 1; } break; case IAT_H: if ( at->valence == 0 && at->charge == 1 ) { is_H = 1; /* isolated proton */ val = 0; } } if ( val < 0 ) { continue; } num_H = NUMH(at,0); if ( val != at->chem_bonds_valence + num_H ) { continue; } if ( is_H ) { bAccept = 1; cur_num_iso_H = (at->iso_atw_diff != 0); } else { cur_num_iso_H = 0; for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { at2 = atom + (int)at->neighbor[j]; if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { bAccept = 0; /* adjacent charged/radical atoms: do not neutralizate */ break; } else if ( at2->el_number == el_num[IAT_H ] && at2->valence == 1 && at2->iso_atw_diff ) { cur_num_iso_H ++; /* isotopic explicit H */ } } if ( bAccept ) { num_iso_atoms -= cur_num_iso_H; /* avoid counting explicit H as isotopic atom */ cur_num_iso_H += NUM_ISO_H(at,0); } } num_iso_H += (bAccept && cur_num_iso_H); /* number of acceptable heteroatoms that have isotopic H */ } return ((num_iso_H? 1:0) | (num_iso_atoms? 2:0)); } /****************************************************/ /* Mark and count disconnected structure components */ /* by Depth-first searching each component */ /****************************************************/ int cmp_components( const void *a1, const void *a2 ) { int ret; AT_NUMB n1; AT_NUMB n2; n1 = ((const AT_NUMB *)a1)[0]; /* number of atoms in the component -- descending order */ n2 = ((const AT_NUMB *)a2)[0]; if ( ret = (int)n2 - (int)n1 ) { return ret; } /* stable sort */ n1 = ((const AT_NUMB *)a1)[1]; /* component ordering number -- ascending order */ n2 = ((const AT_NUMB *)a2)[1]; ret = (int)n1 - (int)n2; return ret; } /*************************************************************************************************/ int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ) { typedef AT_NUMB AT_TRIPLE[3]; inp_ATOM *at = orig_at_data->at; int num_at = orig_at_data->num_inp_atoms; AT_NUMB *nCurAtLen = NULL; AT_NUMB *nNewCompNumber = NULL; AT_NUMB *nPrevAtom = NULL; S_CHAR *iNeigh = NULL; AT_NUMB *nOldCompNumber = NULL; int i, j, num_components, ret; int new_comp_no; AT_NUMB old_comp_no, another_comp_no, no_component; /* component_nbr[i][0] = number of atoms in the component i-1 * component_nbr[i][1] = original component number (id-1) = i * after sorting: * component_nbr[j][2] = new number of component #(component_nbr[i][1]+1) */ AT_TRIPLE *component_nbr = NULL; /* initialize */ if ( bProcessOldCompNumbers && !orig_at_data->nOldCompNumber ) { bProcessOldCompNumbers = 0; } num_components = 0; /* for ( j = 0; j < num_at; j ++ ) { at[j].component = 0; } */ ret = -1; if ( !num_at ) { return 0; } if ( !( nNewCompNumber = (AT_NUMB *) inchi_calloc( num_at, sizeof(nNewCompNumber[0]) ) ) || /* for non-recursive DFS only: */ !( nPrevAtom = (AT_NUMB *) inchi_calloc( num_at, sizeof(nPrevAtom[0]) ) ) || !( iNeigh = (S_CHAR *) inchi_calloc( num_at, sizeof(iNeigh[0]) ) )) { goto exit_function; } /* mark and count; avoid deep DFS recursion: it may make verifying software unhappy */ /* nNewCompNumber[i] will contain new component number for atoms at[i], i=0..num_at-1 */ for ( j = 0; j < num_at; j++ ) { if ( !nNewCompNumber[j] ) { /* mark starting with at[j] */ int fst_at, nxt_at, cur_at = j; num_components ++; /* first time at at[j] */ nNewCompNumber[fst_at = cur_at] = (AT_NUMB) num_components; /* find next neighbor */ while ( 1 ) { if ( iNeigh[cur_at] < at[cur_at].valence ) { nxt_at = at[cur_at].neighbor[(int)iNeigh[cur_at] ++]; if ( !nNewCompNumber[nxt_at] ) { /* forward edge: found new atom */ nNewCompNumber[nxt_at] = (AT_NUMB) num_components; nPrevAtom[nxt_at] = (AT_NUMB) cur_at; cur_at = nxt_at; } } else if ( cur_at == fst_at ) { break; /* done */ } else { cur_at = nPrevAtom[cur_at]; /* retract */ } } } } inchi_free( nPrevAtom ); nPrevAtom = NULL; inchi_free( iNeigh ); iNeigh = NULL; /* Allocate more memory */ i = inchi_max( num_components, orig_at_data->num_components ); if ( !(nCurAtLen = (AT_NUMB *) inchi_calloc( num_components+1, sizeof(nCurAtLen[0]) ) ) || !(nOldCompNumber = (AT_NUMB *) inchi_calloc( i +1, sizeof(nOldCompNumber[0]) ) ) || !(component_nbr = (AT_TRIPLE *) inchi_calloc( num_components+1, sizeof(component_nbr[0]) ) ) ) { goto exit_function; } /* count atoms per component and renumber the components */ for ( i = 0; i < num_components; i ++ ) { component_nbr[i][0] = 0; /* number of atoms in the component */ component_nbr[i][1] = i; /* component ordering number */ } for ( j = 0; j < num_at; j ++ ) { component_nbr[(int)nNewCompNumber[j]-1][0] ++; /* count atoms in each component */ } /* sort key: number of atoms; order: descending */ qsort( (void*)component_nbr[0], num_components, sizeof(component_nbr[0]), cmp_components); /* invert the transposition */ for ( i = 0; i < num_components; i ++ ) { nCurAtLen[i] = component_nbr[i][0]; component_nbr[ component_nbr[i][1] ][2] = i+1; } /* renumber the components so that the component with the greatest number of atoms is the first */ no_component = num_at+1; for ( j = 0; j < num_at; j ++ ) { /* new component number for at[j] */ new_comp_no = component_nbr[(int)nNewCompNumber[j]-1][2]-1; /* starts from 0 */ if ( bProcessOldCompNumbers ) { /* old component number for at[j] */ old_comp_no = at[j].component; /* fill out nOldCompNumber[]; initially it contains zeroes */ if ( !old_comp_no ) { nOldCompNumber[new_comp_no] = no_component; /* atom did not have component number */ } else if ( nOldCompNumber[new_comp_no] != old_comp_no ) { if ( !nOldCompNumber[new_comp_no] ) { nOldCompNumber[new_comp_no] = old_comp_no; } else { /* at[j] moved from old comp #old_comp_no to old comp #nOldCompNumber[new_comp_no] Both components cannot be equal to any current component */ another_comp_no = nOldCompNumber[new_comp_no]; for ( i = 0; i < num_components; i ++ ) { if ( nOldCompNumber[i] == old_comp_no || nOldCompNumber[i] == another_comp_no ) { nOldCompNumber[i] = no_component; } } /* nOldCompNumber[new_comp_no] = num_at+1; */ } } } /* orig_at_data->nOldCompNumber */ at[j].component = new_comp_no+1; /* starts from 1 */ } if ( bProcessOldCompNumbers ) { for ( j = 0; j < num_components; j ++ ) { if ( nOldCompNumber[j] == no_component ) { /* the component has atom from another component */ nOldCompNumber[j] = 0; } else if ( nOldCompNumber[j] && !orig_at_data->nOldCompNumber[nOldCompNumber[j]-1] ) { /* the component has changed in the previous processing */ nOldCompNumber[j] = 0; } } } else { for ( j = 0; j < num_components; j ++ ) { nOldCompNumber[j] = j + 1; } } ret = num_components; exit_function: if ( nNewCompNumber ) inchi_free( nNewCompNumber ); if ( component_nbr ) inchi_free( component_nbr ); if ( ret < 0 ) { if ( nPrevAtom ) { inchi_free( nPrevAtom ); nPrevAtom = NULL; } if ( iNeigh ) { inchi_free( iNeigh ); iNeigh = NULL; } if ( nCurAtLen ) { inchi_free( nCurAtLen ); nCurAtLen = NULL; } if ( nOldCompNumber ) { inchi_free( nOldCompNumber ); nOldCompNumber = NULL; } num_components = ret; } /* avoid memory leaks */ if ( orig_at_data->nCurAtLen ) inchi_free ( orig_at_data->nCurAtLen ); if ( orig_at_data->nOldCompNumber ) inchi_free ( orig_at_data->nOldCompNumber ); orig_at_data->nCurAtLen = nCurAtLen; orig_at_data->nOldCompNumber = nOldCompNumber; orig_at_data->num_components = num_components; return ret; /* number of disconnected components; 1=>single connected structure*/ } /******************************************************************************/ /* Extract one (connected) component */ /******************************************************************************/ int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ) { int i, j, num_component_at; AT_NUMB *number; if ( NULL == (number = (AT_NUMB*)inchi_calloc(num_at, sizeof(AT_NUMB)))){ return CT_OUT_OF_RAM; /* out of memory */ /* */ } /* copy atoms */ for ( i = 0, num_component_at = 0; i < num_at; i ++ ) { if ( at[i].component == component_number ) { number[i] = num_component_at; component_at[num_component_at ++] = at[i]; } } /* renumber neighbors */ for ( i = 0; i < num_component_at; i ++ ) { component_at[i].orig_compt_at_numb = (AT_NUMB)(i + 1); for ( j = 0; j < component_at[i].valence; j ++ ) { component_at[i].neighbor[j] = number[(int)component_at[i].neighbor[j]]; } } inchi_free( number ); return num_component_at; } /****************************************************************/ int SetConnectedComponentNumber( inp_ATOM *at, int num_at, int component_number ) { int i; for ( i = 0; i < num_at; i ++ ) { at[i].component = (AT_NUMB)component_number; } return 0; } /****************************************************************/ int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ) { if ( pINChI_Stereo ) { qzfree( pINChI_Stereo->nNumber ); qzfree( pINChI_Stereo->t_parity ); qzfree( pINChI_Stereo->nNumberInv ); qzfree( pINChI_Stereo->t_parityInv ); qzfree( pINChI_Stereo->nBondAtom1 ); qzfree( pINChI_Stereo->nBondAtom2 ); qzfree( pINChI_Stereo->b_parity ); } return 0; } /****************************************************************/ INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds) { INChI_Stereo *pINChI_Stereo = (INChI_Stereo *)inchi_calloc(1, sizeof(INChI_Stereo)); if ( pINChI_Stereo ) { if ( num_at && (pINChI_Stereo->nNumber = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumber[0]))) && (pINChI_Stereo->t_parity = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parity[0]))) && (pINChI_Stereo->nNumberInv = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumberInv[0]))) && (pINChI_Stereo->t_parityInv = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parityInv[0]))) ) { ; } else if ( num_at ) { goto out_of_RAM; } if ( num_bonds && (pINChI_Stereo->nBondAtom1 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom1[0]))) && (pINChI_Stereo->nBondAtom2 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom2[0]))) && (pINChI_Stereo->b_parity =(S_CHAR *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->b_parity[0]))) ) { ; } else if ( num_bonds ) { goto out_of_RAM; } return pINChI_Stereo; out_of_RAM: Free_INChI_Stereo( pINChI_Stereo ); qzfree( pINChI_Stereo ); } return NULL; } /****************************************************************/ int Free_INChI(INChI **ppINChI) { INChI *pINChI; if ( pINChI = *ppINChI ) { #if ( bREUSE_INCHI == 1 ) if ( pINChI->nRefCount -- > 0 ) return 1; #endif Free_INChI_Members(pINChI); qzfree( pINChI ); *ppINChI = NULL; } return 0; } /****************************************************************/ int Free_INChI_Members(INChI *pINChI) { if ( pINChI ) { Free_INChI_Stereo(pINChI->Stereo ); Free_INChI_Stereo(pINChI->StereoIsotopic ); qzfree(pINChI->nAtom ); qzfree(pINChI->nConnTable ); qzfree(pINChI->nTautomer ); qzfree(pINChI->nNum_H ); qzfree(pINChI->nNum_H_fixed ); qzfree(pINChI->IsotopicAtom ); qzfree(pINChI->IsotopicTGroup ); qzfree(pINChI->nPossibleLocationsOfIsotopicH); qzfree(pINChI->Stereo ); qzfree(pINChI->StereoIsotopic ); qzfree(pINChI->szHillFormula ); } return 0; } /****************************************************************/ INChI *Alloc_INChI( inp_ATOM *at, int num_at, int *found_num_bonds, int *found_num_isotopic, int nAllocMode ) { int i, num_bonds, num_isotopic_atoms; INChI *pINChI; int bIsotopic = (nAllocMode & REQ_MODE_ISO); /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ if ( num_at <= 0 || NULL == (pINChI = (INChI *)inchi_calloc( 1, sizeof(INChI)))) { return NULL; } for ( i = 0, num_bonds = 0, num_isotopic_atoms = 0; i < num_at; i ++ ) { num_bonds += at[i].valence; /* if ( bIsotopic ) { */ num_isotopic_atoms += (0 != at[i].iso_atw_diff || !strcmp(at[i].elname, "D") || !strcmp(at[i].elname, "T") || at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2]); /* } */ } num_bonds /= 2; *found_num_bonds = num_bonds; *found_num_isotopic = num_isotopic_atoms; if ( (pINChI->nAtom = (U_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nAtom[0]))) && (pINChI->nConnTable = (AT_NUMB*)inchi_calloc( num_at+num_bonds, sizeof(pINChI->nConnTable[0]))) && (pINChI->nTautomer = (AT_NUMB*)inchi_calloc( ((3+INCHI_T_NUM_MOVABLE)*num_at)/2+1, sizeof(pINChI->nTautomer[0]))) && (pINChI->nNum_H = (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H[0]))) && (pINChI->nNum_H_fixed= (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H_fixed[0]))) ) { ; /* nTautomer length: max. number of tautomeric groups is num_at/2 1 word -> number of t-groups each group has: 1 word -> number of endpoints+INCHI_T_NUM_MOVABLE INCHI_T_NUM_MOVABLE words -> number(s) of moveable attachments numbers of endpoints words -> canon. numbers max. occurs if each t-group has 2 atoms (num_at/2 t-groups) and all atoms belong to t-groups (num_at endpoints) Total: 1 + (number of t-groups)*(1+INCHI_T_NUM_MOVABLE) + (number of endpoints) <= 1 + (num_at/2) * (1+INCHI_T_NUM_MOVABLE) + num_at <= 1 + (3+INCHI_T_NUM_MOVABLE)*num_at/2 words. */ } else { goto out_of_RAM; } pINChI->szHillFormula = NULL; /* the length is unknown */ if ( bIsotopic ) { if ( num_isotopic_atoms && (pINChI->IsotopicAtom = (INChI_IsotopicAtom *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicAtom) )) && (pINChI->IsotopicTGroup = (INChI_IsotopicTGroup *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicTGroup) )) ) { ; } else if ( num_isotopic_atoms ) { goto out_of_RAM; } if ( !(pINChI->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(pINChI->nPossibleLocationsOfIsotopicH[0]) ) ) ) { goto out_of_RAM; } } if ((pINChI->Stereo = Alloc_INChI_Stereo(num_at, num_bonds)) ) { ; } else { goto out_of_RAM; } if ( bIsotopic ) { if ((pINChI->StereoIsotopic = Alloc_INChI_Stereo(num_at, num_bonds)) ) { ; } else { goto out_of_RAM; } } return pINChI; out_of_RAM: if ( pINChI ) { Free_INChI(&pINChI); /* inchi_free(pINChI); */ } return NULL; } /****************************************************************/ int Free_INChI_Aux( INChI_Aux **ppINChI_Aux ) { INChI_Aux *pINChI_Aux = *ppINChI_Aux; if ( pINChI_Aux ) { #if ( bREUSE_INCHI == 1 ) if ( pINChI_Aux->nRefCount -- > 0 ) return 1; #endif qzfree( pINChI_Aux->nOrigAtNosInCanonOrd ); qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ); qzfree( pINChI_Aux->nOrigAtNosInCanonOrdInv ); qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ); qzfree( pINChI_Aux->szOrigCoord ); qzfree( pINChI_Aux->OrigInfo ); /* qzfree( pINChI_Aux->nOriginalAtomNumber ); qzfree( pINChI_Aux->nCanonicalTGroupNumbers ); qzfree( pINChI_Aux->nIsotopicCanonicalTGroupNumbers); qzfree( pINChI_Aux->nTautomer ); qzfree( pINChI_Aux->nNontautomericCanonicalNumbers ); qzfree( pINChI_Aux->nIsotopicCanonicalNumbers ); qzfree( pINChI_Aux->nNontautomericIsotopicCanonicalNumbers ); qzfree( pINChI_Aux->nNontautomericEquNumbers ); qzfree( pINChI_Aux->nNontautomericIsotopicEquNumbers ); */ qzfree( pINChI_Aux->nConstitEquNumbers ); qzfree( pINChI_Aux->nConstitEquTGroupNumbers ); qzfree( pINChI_Aux->nConstitEquIsotopicNumbers ); qzfree( pINChI_Aux->nConstitEquIsotopicTGroupNumbers ); qzfree( pINChI_Aux ); *ppINChI_Aux = NULL; } return 0; } /****************************************************************/ INChI_Aux *Alloc_INChI_Aux( int num_at, int num_isotopic_atoms, int nAllocMode, int bOrigCoord ) { INChI_Aux *pINChI_Aux; int bIsotopic = (nAllocMode & REQ_MODE_ISO); int num_at_tg = num_at + num_at/2; /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ if ( num_at <= 0 || NULL == (pINChI_Aux = (INChI_Aux *)inchi_calloc(sizeof(INChI_Aux), 1))) { return NULL; } if ( (pINChI_Aux->nOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && (pINChI_Aux->nOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && (pINChI_Aux->nConstitEquNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquNumbers[0]), num_at_tg)) ) { ; } else { goto out_of_RAM; } if ( num_at > 1 && (pINChI_Aux->nConstitEquTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquTGroupNumbers[0]), num_at/2+1)) ) { ; } else if ( num_at > 1 ) { goto out_of_RAM; } if ( num_at > 0 ) { pINChI_Aux->OrigInfo = (ORIG_INFO *)inchi_calloc(sizeof(pINChI_Aux->OrigInfo[0]), num_at); if ( !pINChI_Aux->OrigInfo ) goto out_of_RAM; } if ( bOrigCoord && num_at > 0 ) { pINChI_Aux->szOrigCoord = (MOL_COORD *)inchi_calloc(sizeof(pINChI_Aux->szOrigCoord[0]), num_at); if ( !pINChI_Aux->szOrigCoord ) goto out_of_RAM; } if ( bIsotopic ) { if ( /*num_isotopic_atoms &&*/ (pINChI_Aux->nIsotopicOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && (pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && (pINChI_Aux->nConstitEquIsotopicNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicNumbers[0]), num_at_tg)) ) { ; } else if ( num_isotopic_atoms ) { goto out_of_RAM; } if ( /*num_isotopic_atoms && num_at > 1 &&*/ (pINChI_Aux->nConstitEquIsotopicTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicTGroupNumbers[0]), num_at/2+1)) ) { ; } else if ( num_isotopic_atoms && num_at > 1 ) { goto out_of_RAM; } } return pINChI_Aux; out_of_RAM: if ( pINChI_Aux ) { Free_INChI_Aux(&pINChI_Aux); /* inchi_free(pINChI_Aux); */ } return NULL; } /***********************************************************************************/ #define IS_DEUTERIUM(i) (!strcmp( at[i].elname, "D" ) || at[i].iso_atw_diff == 2 && !strcmp( at[i].elname, "H" )) #define IS_TRITIUM(i) (!strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff == 3 && !strcmp( at[i].elname, "H" )) #define ABNORMAL_ISO(i) (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ) #define ABNORMAL_CHG(i) (abs(at[i].charge) > 3) #define ABNORMAL_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) #define ANY_ISO(i, X) ((X)? (at[i].iso_atw_diff && !IS_DEUTERIUM(i) && !IS_TRITIUM(i)) :\ (at[i].iso_atw_diff || IS_DEUTERIUM(i) || IS_TRITIUM(i))) #define ANY_CHG(i) (0 != at[i].charge) #define ANY_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) #define NORMAL_ISO(i, X) (ANY_ISO(i, X) && !ABNORMAL_ISO(i)) /* needs additional M CHG. M RAD, M ISO line */ /* due to ISIS/Draw feature always include M RAD for any radical */ #define ABNORMAL_AT(i) ( at[i].radical || abs(at[i].charge) > 3 || \ ABNORMAL_ISO(i) ) /* always add M ISO, M RAD, M CHG; Except: (bAtomsDT && D or T) */ #define ADD_LINE_AT(i) ( at[i].charge || \ at[i].radical || \ at[i].iso_atw_diff && (bAtomsDT? (at[i].iso_atw_diff != 1 || strcmp(at[i].elname, "H")) : 1) ) #define ALIASED_AT(i) (0 < NUM_ISO_H(at, i)) /***********************************************************************************/ #if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, const char *szLabel, const char *szValue) { int i, j, k, num_bonds=0, ret=0, bAtomsDT = 1 /* treat D, T as normal atoms */, bV2000 = 0 /*V2000 Molfile */; int bAtomNeedsAlias; int flag_bad_charge=0, flag_bad_iso=0, nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; /*sp_ATOM *at; */ /*float fzero=0.0F;*/ double x, y, z; int bNext /*, s*/; const inp_ATOM *at = inp_at_data->at_fixed_bonds? inp_at_data->at_fixed_bonds : inp_at_data->at; int num_atoms = inp_at_data->num_at; /*at = species->atom;*/ /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ { /* block start */ char strLocName[82]; memset(strLocName, 0, sizeof(strLocName) ); if ( name && *name ) { strncpy( strLocName, name, 80 ); } inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); } /* block end */ /**********************************************************************/ /** **/ /** Important: Atoms with alias cannot have charge, radical, or **/ /** isotope differences. **/ /** **/ /** Atoms with alias cannot be abnormal. **/ /** **/ /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ /** **/ /**********************************************************************/ /* F10.5 F12.5 I6 IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ /*^^^ inchi_ios_print_nodisplay( fcb," -%s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ inchi_ios_print_nodisplay( fcb, /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ " InChIV10 \n"); /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ { /*block start*/ char strLocName[82]; memset(strLocName, 0, sizeof(strLocName) ); if ( comment && *comment ) { strncpy( strLocName, comment, 80 ); } inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); } /*block end*/ for (i=0; i< num_atoms; i++) num_bonds += at[i].valence; num_bonds /= 2; /*find if we need "M CHG", "M RAD", "M ISO" */ for (i=0, nNumAddLines = 0; i < num_atoms; i++) { if ( bAtomNeedsAlias = ALIASED_AT(i) ) { nNumAliasLines += 2 * bAtomNeedsAlias; } else { nNumNecessaryIsoLines += ABNORMAL_ISO(i); nNumNecessaryChgLines += ABNORMAL_CHG(i); nNumNecessaryRadLines += ABNORMAL_RAD(i); nNumIsoLines += ANY_ISO(i, bAtomsDT); nNumChargeLines += ANY_CHG(i); nNumRadicalLines += ANY_RAD(i); } } if ( !bV2000 ) { if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { nNumRadicalLines = 0; nNumChargeLines = 0; } if ( !nNumNecessaryIsoLines ) { nNumIsoLines = 0; } } /* count additional M lines*/ nNumChargeLines = ( nNumChargeLines + 7 ) / 8; nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; nNumIsoLines = ( nNumIsoLines + 7 ) / 8; nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ if ( nNumAddLines || bV2000 ) { nNumAddLines += 1; /* add 1 for "M END" line*/ } /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0 0 0 0 0 0 0%3d%s\n",num_atoms, num_bonds, nNumAddLines,nNumAddLines?" V2000":""); /* atoms block*/ for (i=0; i < num_atoms; i++) { char elname[ATOM_EL_LEN]; int iso = 0; int charge = 0; int valence = 0; int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; bAtomNeedsAlias = ALIASED_AT(i); /* Has implicit D and/or T neighbors */ memset( elname, 0, sizeof(elname) ); if ( bAtomNeedsAlias ) { /* alias */ strcpy ( elname, "C" ); } else { /* isotope*/ if ( nIsotopeH ) { strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); } else { strncpy ( elname, at[i].elname, sizeof(elname)-1 ); } if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { /* charge*/ /* Only atoms without alias can be here*/ switch ( at[i].charge ) { case 3: charge = 1; break; case 2: charge = 2; break; case 1: charge = 3; break; case -1: charge = 5; break; case -2: charge = 6; break; case -3: charge = 7; break; case 0: charge = 0; break; default: flag_bad_charge = 1; break; }; } /* radical*/ if ( ANY_RAD(i) && !ANY_CHG(i) ) { if ( at[i].radical == RADICAL_DOUBLET ) { charge = 4; } } } /* allow isotopic shift for aliased atoms */ if ( NORMAL_ISO(i, bAtomsDT) ) { iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: at[i].iso_atw_diff < 0? at[i].iso_atw_diff : nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); } x = at[i].x; y = at[i].y; z = at[i].z; if( at[i].num_H > 0 ) { for ( j = 0, valence = 0; j < at[i].valence; j++ ) { switch( k = at[i].bond_type[j] ) { /* fixed valence calculation 12-23-99 DCh.*/ case 2: case 3: valence += 2*k; break; case 4: valence += 3; break; default: valence += 2; } } valence = valence/2 + at[i].num_H; } else /* Added 07-09-2003 DCh*/ if ( at[i].chem_bonds_valence > 0 ) { valence = at[i].chem_bonds_valence; } else /* Added 07-09-2003 DCh*/ if ( !at[i].valence && !at[i].num_H && !at[i].chem_bonds_valence ) { valence = 15; } /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); /* reflect image against x-axis; when transforming MOLfile back to STDATA in mol_to_stdata(...), make one more reflection to restore original orientation. Reason: in MS Search y-axis is directed from top to bottom, while in MOLfile y-axis goes from bottom to top. */ } bNext = 0; /* debug only*/ /* bonds*/ for (i=0; i< num_atoms; i++) { for (j=0; j 1 ) { sprintf( str_m + strlen(str_m), "%d", num_H ); } } if ( num_H = at[i].num_iso_H[1] ) { /* deuterium */ strcat( str_m, "D" ); if ( num_H > 1 ) { sprintf( str_m + strlen(str_m), "%d", num_H ); } } if ( num_H = at[i].num_iso_H[2] ) { /* Tritium */ strcat( str_m, "T" ); if ( num_H > 1 ) { sprintf( str_m + strlen(str_m), "%d", num_H ); } } /* Add charge to the Alias */ if ( at[i].charge){ strcat(str_m, at[i].charge>0? "+" : "-"); if ( 1 < (j=abs(at[i].charge)) ) sprintf( str_m+strlen(str_m), "%d", j ); } /* Add radical to the Alias */ switch( at[i].radical ) { case RADICAL_SINGLET: strcat( str_m, ":" ); break; case RADICAL_DOUBLET: strcat( str_m, "^" ); break; case RADICAL_TRIPLET: strcat( str_m, "^^" ); break; } inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); num_m ++; } } if ( num_m != nNumAliasLines ) { /* error in lines counting*/ ret ++; } } /* charges*/ str_m[0] = 0; num_m = 0; if ( nNumChargeLines ) { for (i=0; i < num_atoms; i++) { if ( ANY_CHG(i) && !ALIASED_AT(i) ) { sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); strcat( str_m, entry ); num_m ++; } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } /* radicals*/ str_m[0] = 0; num_m = 0; if ( nNumRadicalLines ) { for (i=0; i < num_atoms; i++) { if ( ANY_RAD(i) && !ALIASED_AT(i) ) { int radical = (at[i].radical==RADICAL_SINGLET || at[i].radical==RADICAL_DOUBLET || at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; if ( radical ) { sprintf( entry, " %3d %3d", i+1, radical ); strcat( str_m, entry ); num_m ++; } } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } /* isotopes*/ str_m[0] = 0; num_m = 0; if ( nNumIsoLines ) { int el_num, iso; for (i=0; i < num_atoms; i++) { if ( ANY_ISO(i,bAtomsDT) && !ALIASED_AT(i) ) { if ( IS_DEUTERIUM(i) ) { iso = 1; el_num = 1; } else if ( IS_TRITIUM(i) ) { iso = 2; el_num = 1; } else { iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; el_num = at[i].el_number; } iso += get_atw_from_elnum( el_num ); sprintf( entry, " %3d %3d", i+1, iso ); strcat( str_m, entry ); num_m ++; } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } inchi_ios_print_nodisplay( fcb, "M END\n" ); } if ( szValue && szValue[0] ) { if ( szLabel && szLabel[0] ) { inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); } else { inchi_ios_print_nodisplay( fcb, "> \n" ); } inchi_ios_print_nodisplay( fcb, "%s\n\n", szValue ); } inchi_ios_print_nodisplay(fcb, "$$$$\n"); return ret; } #endif /***************************************************************************************************/ int WriteOrigAtomDataToSDfile(const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, const char* comment, int bChiralFlag, int bAtomsDT, const char *szLabel, const char *szValue) { int i, j, k, num_bonds=0, ret=0; int bAtomNeedsAlias; int flag_bad_charge=0, flag_bad_iso = 0; int nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; int bV2000 = SDF_OUTPUT_V2000; double x, y, z; int bNext /*, s*/; const inp_ATOM *at = inp_at_data->at; int num_atoms = inp_at_data->num_inp_atoms; /*at = species->atom;*/ /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ { /* block start */ char strLocName[82]; memset(strLocName, 0, sizeof(strLocName) ); if ( name && *name ) { strncpy( strLocName, name, 80 ); /* --- debug only --- if ( strstr( name, "#3959" ) ) { int stop = 1; } */ } inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); } /* block end */ /**********************************************************************/ /** **/ /** Important: Atoms with alias cannot have charge, radical **/ /** isotope differences are allowed **/ /** **/ /** Atoms with alias cannot be abnormal. **/ /** **/ /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ /** **/ /** Output aliased atoms if they have implicit D or T **/ /** **/ /**********************************************************************/ /* F10.5 F12.5 I6 IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ /*^^^ inchi_ios_print_nodisplay( fcb," %s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ inchi_ios_print_nodisplay( fcb, /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ " InChIV10 \n"); /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ { /*block start*/ char strLocName[82]; memset(strLocName, 0, sizeof(strLocName) ); if ( comment && *comment ) { strncpy( strLocName, comment, 80 ); } inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); } /*block end*/ for (i=0; i< num_atoms; i++) num_bonds += at[i].valence; num_bonds /= 2; /*find if we need "M CHG" and "M RAD"*/ for (i=0; i < num_atoms; i++) { if ( bAtomNeedsAlias = ALIASED_AT(i) ) { /* has isotopic implicit D or T; ignoring pure 1H */ nNumAliasLines += 2 * bAtomNeedsAlias; } else { /* abnormal means atom needs CHG, RAD, or ISO entry */ /* nNumAddLines += ABNORMAL_AT(i); */ /* nNumIso += ( 0 == strcmp( at[i].elname, "D" ) || ( 0 == strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff ) ); */ /* nNumAddIso += at[i].iso_atw_diff && (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ); */ nNumNecessaryIsoLines += ABNORMAL_ISO(i); nNumNecessaryChgLines += ABNORMAL_CHG(i); nNumNecessaryRadLines += ABNORMAL_RAD(i); nNumIsoLines += ANY_ISO(i, bAtomsDT); nNumChargeLines += ANY_CHG(i); nNumRadicalLines += ANY_RAD(i); } } nNumChargeLines = ( nNumChargeLines + 7 ) / 8; nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; nNumIsoLines = ( nNumIsoLines + 7 ) / 8; if ( !bV2000 ) { if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { nNumRadicalLines = 0; nNumChargeLines = 0; } if ( !nNumNecessaryIsoLines ) { nNumIsoLines = 0; } } /* recalculate number of added lines */ nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ if ( nNumAddLines || bV2000 ) { nNumAddLines += 1; /* add 1 for "M END" line*/ } /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0%3d 0 0 0 0 0%3d%s\n", num_atoms, num_bonds, bChiralFlag?1:0, nNumAddLines,nNumAddLines?" V2000":""); /* atoms block*/ for (i=0; i < num_atoms; i++) { char elname[ATOM_EL_LEN] = "\0\0\0\0\0"; int iso = 0; int charge = 0; int valence = 0; int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; int bonds_val; bAtomNeedsAlias = ALIASED_AT(i); memset( elname, 0, sizeof(elname) ); if ( bAtomNeedsAlias ) { /* alias */ strcpy ( elname, "C" ); } else { /* isotope*/ if ( nIsotopeH ) { strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); } else { strncpy ( elname, at[i].elname, sizeof(elname)-1 ); } if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { /* charge*/ /* Only atoms without alias can be here*/ switch ( at[i].charge ) { case 3: charge = 1; break; case 2: charge = 2; break; case 1: charge = 3; break; case -1: charge = 5; break; case -2: charge = 6; break; case -3: charge = 7; break; case 0: charge = 0; break; default: flag_bad_charge = 1; break; }; } /* radical*/ if ( ANY_RAD(i) && !ANY_CHG(i) ) { if ( at[i].radical == RADICAL_DOUBLET ) { charge = 4; } } } /* allow isotopic shift for aliased atoms */ if ( NORMAL_ISO(i, bAtomsDT) ) { iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: at[i].iso_atw_diff < 0? at[i].iso_atw_diff : nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); } x = at[i].x; y = at[i].y; z = at[i].z; /* valence -- set only if needed */ bonds_val = nBondsValenceInpAt( at+i, NULL, NULL ); valence=needed_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, at[i].chem_bonds_valence, bonds_val, NUMH(at, i), at[i].valence ); if ( valence < 0 ) { valence = 15; /* means no bonds nor H */ } /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); /* reflect image against x-axis; when transforming MOLfile back to STDATA in mol_to_stdata(...), make one more reflection to restore original orientation. Reason: in MS Search y-axis is directed from top to bottom, while in MOLfile y-axis goes from bottom to top. */ } bNext = 0; /* debug only*/ /* bonds*/ for (i=0; i< num_atoms; i++) { for (j=0; j0? "+" : "-"); if ( 1 < (j=abs(at[i].charge)) ) { len += sprintf( str_m+len, "%d", j ); } } /* Add radical to the Alias */ if ( at[i].radical == RADICAL_SINGLET ) { len += sprintf( str_m+len, "%s", ":" ); } else if ( at[i].radical == RADICAL_DOUBLET ) { len += sprintf( str_m+len, "%s", "^" ); } else if ( at[i].radical == RADICAL_TRIPLET ) { len += sprintf( str_m+len, "%s", "^^" ); } inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); num_m ++; } } if ( num_m != nNumAliasLines ) { /* error in lines counting*/ ret ++; } } /* charges*/ str_m[0] = 0; num_m = 0; if ( nNumChargeLines ) { for (i=0; i < num_atoms; i++) { if ( at[i].charge && !ALIASED_AT(i) ) { sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); strcat( str_m, entry ); num_m ++; } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } /* radicals*/ str_m[0] = 0; num_m = 0; if ( nNumRadicalLines ) { for (i=0; i < num_atoms; i++) { if ( at[i].radical && !ALIASED_AT(i) ) { int radical = (at[i].radical==RADICAL_SINGLET || at[i].radical==RADICAL_DOUBLET || at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; if ( radical ) { sprintf( entry, " %3d %3d", i+1, radical ); strcat( str_m, entry ); num_m ++; } } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } /* isotopes*/ str_m[0] = 0; num_m = 0; if ( nNumIsoLines ) { int el_num, iso; for (i=0; i < num_atoms; i++) { /* if ( 0 == strcmp( at[i].elname, "D" ) ) { sprintf( entry, " %3d %3d", i+1, 2 ); strcat( str_m, entry ); num_m ++; } else if ( 0 == strcmp( at[i].elname, "T" ) ) { sprintf( entry, " %3d %3d", i+1, 3 ); strcat( str_m, entry ); num_m ++; } else if ( k = at[i].iso_atw_diff ) { int mw = get_atw_from_elnum( at[i].el_number ); mw += (k > 0)? k-1 : k; sprintf( entry, " %3d %3d", i+1, mw ); strcat( str_m, entry ); num_m ++; } */ if ( ANY_ISO(i, bAtomsDT) && !ALIASED_AT(i) ) { if ( IS_DEUTERIUM(i) ) { iso = 1; el_num = 1; } else if ( IS_TRITIUM(i) ) { iso = 2; el_num = 1; } else { iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; el_num = at[i].el_number; } iso += get_atw_from_elnum( el_num ); sprintf( entry, " %3d %3d", i+1, iso ); strcat( str_m, entry ); num_m ++; } if ( i == num_atoms-1 && num_m || num_m == 8 ) { inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); str_m[0] = 0; num_m = 0; } } } inchi_ios_print_nodisplay( fcb, "M END\n" ); } if ( szValue && szValue[0] ) { if ( szLabel && szLabel[0] ) { inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); } else { inchi_ios_print_nodisplay( fcb, "> \n" ); } inchi_ios_print_nodisplay( fcb, " %s\n\n", szValue ); } inchi_ios_print_nodisplay(fcb, "$$$$\n"); return ret; } #if ( FIX_ADJ_RAD == 1 ) /*************************************************************************/ int FixNextRadicals( int cur_at, inp_ATOM *at ); int FixNextRadicals( int cur_at, inp_ATOM *at ) { int j, neigh, num_found = 0; for ( j = 0; j < at[cur_at].valence; j ++ ) { neigh = at[cur_at].neighbor[j]; if ( at[neigh].radical == RADICAL_DOUBLET ) { at[neigh].radical = 0; num_found ++; num_found += FixNextRadicals( neigh, at ); } } return num_found; } /*************************************************************************/ int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ) { int i, j; char *bVisited = NULL; int nNumFound = 0, neigh, cur_found; for ( i = 0; i < num_inp_atoms; i ++ ) { if ( at[i].radical == RADICAL_DOUBLET ) { cur_found = 1; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( at[neigh].radical == RADICAL_DOUBLET ) { cur_found ++; } } if ( cur_found >= 3 ) { nNumFound ++; at[i].radical = 0; nNumFound += FixNextRadicals( i, at ); } } } return nNumFound; } #endif #ifdef COMPILE_ANSI_ONLY #ifndef TARGET_API_LIB /* #include #include "inpdef.h" */ void PrintFileName( const char *fmt, FILE *output_file, /* INCHI_IOSTREAM *output_file, */ const char *szFname ) { inchi_print_nodisplay( output_file, fmt, szFname ); } #endif #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/strutil.h000066400000000000000000000472401271037650300234370ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __STRUTIL_H__ #define __STRUTIL_H__ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif /* forward declaration */ struct tagTautomerGroupsInfo; int ExtractConnectedComponent(inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ); int SetConnectedComponentNumber(inp_ATOM *at, int num_at, int component_number ); INChI *Alloc_INChI(inp_ATOM *at, int num_at, int *found_num_bonds, int *found_num_isotopic, int nAllocMode ); int Free_INChI(INChI **ppINChI); int Free_INChI_Members(INChI *pINChI); int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ); INChI_Aux *Alloc_INChI_Aux(int num_at, int num_isotopic_atoms, int nAllocMode, int bOrigData ); int Free_INChI_Aux(INChI_Aux **ppINChI_Aux ); int Create_INChI(INChI **ppINChI, INChI_Aux **ppINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, inp_ATOM *inp_at, INP_ATOM_DATA *inp_norm_data[2], int num_inp_at, INCHI_MODE nUserMode, INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, struct tagInchiTime *ulMaxTime, struct tagTautomerGroupsInfo *ti_out, char *pStrErrStruct); int FillOutInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode ); int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, INF_ATOM_DATA *inf_norm_at_data, int bIsotopic, int bTautomeric, PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, INCHI_MODE nMode); #if ( FIX_DALKE_BUGS == 1 ) char *AllocateAndFillHillFormula(INChI *pINChI ); #endif typedef enum tagInchiDiffBits { IDIF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ IDIF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ IDIF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ IDIF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ IDIF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ IDIF_CON_TBL = 0x00000001, /* severe: different connection tables */ IDIF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ IDIF_MORE_FH = 0x00000004, /* extra fixed H */ IDIF_LESS_FH = 0x00000008, /* missing fixed H */ IDIF_MORE_H = 0x00000010, /* formulas differ in number of H */ IDIF_LESS_H = 0x00000020, /* formulas differ in number of H */ /*IDIF_TAUT_LEN = 0x00000008,*/ /* different lengths of tautomer lists */ IDIF_NO_TAUT = 0x00000040, /* restored structure has no taut groups while the original InChI has some */ IDIF_WRONG_TAUT = 0x00000080, /* restored has tautomerism while the original does not have it */ IDIF_SINGLE_TG = 0x00000100, /* restored has 1 taut. group while the original InChI has multiple tg */ IDIF_MULTIPLE_TG = 0x00000200, /* restored has multiple tg while the original InChI has only one tg */ IDIF_NUM_TG = 0x00000400, /* different number of tautomeric groups */ /*IDIF_LESS_TG_ENDP = 0x00000200,*/ /* restores structure has less taut. endpoints */ /*IDIF_MORE_TG_ENDP = 0x00000400,*/ /* restores structure has more taut. endpoints */ IDIF_EXTRA_TG_ENDP = 0x00000800, /* extra tautomeric endpoint(s) in restored structure */ IDIF_MISS_TG_ENDP = 0x00001000, /* one or more tg endpoint is not in the restored structure */ IDIF_DIFF_TG_ENDP = 0x00002000, /* lists of tg endpoints are different */ IDIF_TG = 0x00004000, /* different tautomeric groups */ IDIF_NUM_ISO_AT = 0x00008000, /* ?severe: restored struct. has different number of isotopic atoms */ IDIF_ISO_AT = 0x00010000, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ IDIF_CHARGE = 0x00020000, /* restored structure has different charge */ IDIF_REM_PROT = 0x00040000, /* proton(s) removed/added from the restored structure */ IDIF_REM_ISO_H = 0x00080000, /* isotopic H removed */ IDIF_SC_INV = 0x00100000, /* restores structure has different inversion stereocenter mark */ IDIF_SC_PARITY = 0x00200000, /* restored structure has stereoatoms or allenes with different parity */ IDIF_SC_EXTRA_UNDF = 0x00400000, /* restored structure has extra undefined stereocenter(s) */ IDIF_SC_EXTRA = 0x00800000, /* restored structure has extra stereocenter(s) */ IDIF_SC_MISS_UNDF = 0x01000000, /* restored structure has not some undefined stereocenter(s) */ IDIF_SC_MISS = 0x02000000, /* restored structure has not some stereocenters that are not undefined */ IDIF_SB_PARITY = 0x04000000, /* restored structure has stereobonds or cumulenes with different parity */ IDIF_SB_EXTRA_UNDF = 0x08000000, /* restored structure has extra undefined stereobond(s) */ IDIF_SB_EXTRA = 0x10000000, /* restored structure has extra stereobond(s) */ IDIF_SB_MISS_UNDF = 0x20000000, /* restored structure has not some undefined stereocenters */ IDIF_SB_MISS = 0x40000000 /* restored structure has not some stereobonds that are not undefined */ } IDIF; #define IDIFF_SB (IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF | IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) #define IDIFF_SC (IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | IDIF_SC_MISS_UNDF | IDIF_SC_MISS) #define IDIFF_CONSTIT (IDIF_POSITION_H | IDIF_MORE_FH | IDIF_LESS_FH | IDIF_MORE_H | IDIF_LESS_H |\ IDIF_NO_TAUT | IDIF_WRONG_TAUT | IDIF_SINGLE_TG | IDIF_MULTIPLE_TG | \ IDIF_NUM_TG | IDIF_EXTRA_TG_ENDP | IDIF_MISS_TG_ENDP | IDIF_TG | \ IDIF_NUM_ISO_AT | IDIF_ISO_AT | IDIF_CHARGE | IDIF_REM_PROT | IDIF_REM_ISO_H |\ IDIF_DIFF_TG_ENDP) #define IDIFF_STEREO (IDIF_SC_INV | IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | \ IDIF_SC_MISS_UNDF | IDIF_SC_MISS | IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF |\ IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) /*************************************************************************************/ #define ICR_MAX_ENDP_IN1_ONLY 32 #define ICR_MAX_ENDP_IN2_ONLY 32 #define ICR_MAX_DIFF_FIXED_H 32 #define ICR_MAX_SB_IN1_ONLY 32 #define ICR_MAX_SB_IN2_ONLY 32 #define ICR_MAX_SC_IN1_ONLY 32 #define ICR_MAX_SC_IN2_ONLY 32 #define ICR_MAX_SB_UNDF 32 #define ICR_MAX_SC_UNDF 32 typedef struct tagInChICompareResults { INCHI_MODE flags; int tot_num_H1; int tot_num_H2; int num_taut_H1; int num_taut_H2; int num_taut_M1; int num_taut_M2; /* 1 => InChI from reversed struct. 2 => input InChI */ AT_NUMB endp_in1_only[ICR_MAX_ENDP_IN1_ONLY]; /* endpoint canonical number = index+1 */ int num_endp_in1_only; AT_NUMB endp_in2_only[ICR_MAX_ENDP_IN2_ONLY]; /* endpoint canonical number = index+1 */ int num_endp_in2_only; AT_NUMB diff_pos_H_at[ICR_MAX_DIFF_FIXED_H]; /* non-tautomeric H */ S_CHAR diff_pos_H_nH[ICR_MAX_DIFF_FIXED_H]; int num_diff_pos_H; AT_NUMB fixed_H_at1_more[ICR_MAX_DIFF_FIXED_H]; /* extra fixed_H */ S_CHAR fixed_H_nH1_more[ICR_MAX_DIFF_FIXED_H]; int num_fixed_H1_more; AT_NUMB fixed_H_at2_more[ICR_MAX_DIFF_FIXED_H]; /* missed fixed_H */ S_CHAR fixed_H_nH2_more[ICR_MAX_DIFF_FIXED_H]; int num_fixed_H2_more; AT_NUMB sc_in1_only[ICR_MAX_SC_IN1_ONLY]; int num_sc_in1_only; AT_NUMB sc_in2_only[ICR_MAX_SC_IN2_ONLY]; int num_sc_in2_only; AT_NUMB sb_in1_only[ICR_MAX_SB_IN1_ONLY]; int num_sb_in1_only; AT_NUMB sb_in2_only[ICR_MAX_SB_IN2_ONLY]; int num_sb_in2_only; AT_NUMB sb_undef_in1_only[ICR_MAX_SC_UNDF]; int num_sb_undef_in1_only; AT_NUMB sb_undef_in2_only[ICR_MAX_SC_UNDF]; int num_sb_undef_in2_only; AT_NUMB sc_undef_in1_only[ICR_MAX_SB_UNDF]; int num_sc_undef_in1_only; AT_NUMB sc_undef_in2_only[ICR_MAX_SB_UNDF]; int num_sc_undef_in2_only; } ICR; /* tagInChICompareResults */ INCHI_MODE CompareReversedINChI2(INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2, ICR *picr, int *err ); int CompareIcr(ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ); int CompareReversedINChI(INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); const char *CompareReversedInchiMsg(int code); #define EQL_EXISTS 1 #define EQL_SP3 2 #define EQL_SP3_INV 4 #define EQL_SP2 8 int Eql_INChI_Stereo(INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ); int Eql_INChI_Isotopic(INChI *i1, INChI *i2 ); #define EQL_EQU 0 #define EQL_EQU_TG 1 #define EQL_EQU_ISO 2 int Eql_INChI_Aux_Equ(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); #define EQL_NUM 0 #define EQL_NUM_INV 1 #define EQL_NUM_ISO 2 int Eql_INChI_Aux_Num(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); int bHasEquString(AT_NUMB *LinearCT, int nLenCT ); int CompINChINonTaut2(const void *p1, const void *p2); int CompINChITaut2(const void *p1, const void *p2); int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic); int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic); typedef enum tagDiffINChISegments { /* r = repetitive, n = non-repetitive */ DIFS_f_FORMULA, /* 0 r; fixed-H <-> mobile-H */ DIFS_c_CONNECT, /* 1 n; connection table; mobile-H only */ DIFS_h_H_ATOMS, /* 2 n; hydrogen atoms: mobile-H and Fixed-H; have different meanings */ DIFS_q_CHARGE, /* 3 r; charge; fixed-H <-> mobile-H */ DIFS_p_PROTONS, /* 4 n; protons; mobile-H only */ DIFS_b_SBONDS, /* 5 r: stereobonds: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ DIFS_t_SATOMS, /* 6 r: stereoatoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ DIFS_m_SP3INV, /* 7 r: stereo-abs-inv: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ DIFS_s_STYPE, /* 8 r: stereo-type: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ DIFS_i_IATOMS, /* 9 r: isotopic atoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ DIFS_o_TRANSP, /* 10 n: Fixed-H transposition */ DIFS_idf_LENGTH, /* 11 length of the array relevant to the INChI Identifier */ /* later elements referring to AuxInfo may be added */ DIFS_LENGTH = DIFS_idf_LENGTH /* length of the array */ } DIF_SEGMENTS; typedef enum tagDiffINChILayers { DIFL_M, /* 0 main layer */ DIFL_MI, /* 1 main isotopic */ DIFL_F, /* 2 fixed-H */ DIFL_FI, /* 3 fixed-H isotopic */ DIFL_LENGTH /* number of layers */ } DIF_LAYERS; /* Value meaning */ typedef enum tagMarkDiff { DIFV_BOTH_EMPTY = 0, /* both this and the component in the preceding namesake segment are empty */ DIFV_EQL2PRECED = 1, /* equal to the component in the preceding namesake segment */ DIFV_NEQ2PRECED = 2, /* different from the component in the preceding namesake segment */ DIFV_IS_EMPTY = 4, /* is empty while the preceding namesake segment is not empty */ DIFV_FI_EQ_MI = 8, /* FI stereo component is equal to the component in the MI namesake segment */ /* while M and F components are empty */ /* decision_F = bitmask: bits that should not be present */ /* decision_T = bitmask: at least one of the bits should be present */ /* decision = true if( !( BITS & decision_F ) && ( BITS & decision_F ) ) */ DIFV_OUTPUT_EMPTY_T = (DIFV_IS_EMPTY), /* bits present for empty segment output */ DIFV_OUTPUT_EMPTY_F = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI), /* bits NOT present */ DIFV_OUTPUT_OMIT_F = (DIFV_NEQ2PRECED | DIFV_IS_EMPTY), /* bits NOT present for omitting */ DIFV_OUTPUT_FILL_T = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI) } DIF_VALUES; typedef enum tagINChISegmAction { INCHI_SEGM_OMIT = 0, INCHI_SEGM_FILL = 1, /* the value is used in str_LineEnd() */ INCHI_SEGM_EMPTY = 2 /* the value is used in str_LineEnd() */ } INCHI_SEGM_ACTION; int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ); int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ); int INChI_SegmentAction( char cDifSegs ); #define FLAG_SORT_PRINT_TRANSPOS_BAS 1 /* transposition in the main InChI layer */ #define FLAG_SORT_PRINT_TRANSPOS_REC 2 /* transposition in the reconnected InChI layer */ #define FLAG_SORT_PRINT_NO_NFIX_H_BAS 4 /* no fixed H non-isotopic in the main InChI layer */ #define FLAG_SORT_PRINT_NO_NFIX_H_REC 8 /* no fixed H non-isotopic in the reconnected InChI layer */ #define FLAG_SORT_PRINT_NO_IFIX_H_BAS 16 /* no fixed H isotopic in the main InChI layer */ #define FLAG_SORT_PRINT_NO_IFIX_H_REC 32 /* no fixed H isotopic in the the reconnected InChI layer */ #define FLAG_SORT_PRINT_ReChI_PREFIX 64 /* Output ReChI instead of InChI */ int OutputINChI1(char *pStr, int nStrLen, INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], int iINChI, ORIG_STRUCT *pOrigStruct, int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, int num_components2[], int num_non_taut2[], int num_taut2[], INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, int num_input_struct, const char *szSdfLabel, const char *szSdfValue, long lSdfId, int *pSortPrintINChIFlags, unsigned char save_opt_bits); int OutputINChI2(char *pStr, int nStrLen, INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], int iINChI, ORIG_STRUCT *pOrigStruct, int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, int num_components2[], int num_non_taut2[], int num_taut2[], INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, int num_input_struct, const char *szSdfLabel, const char *szSdfValue, long lSdfId, int *pSortPrintINChIFlags, unsigned char save_opt_bits); int SaveEquComponentsInfoAndSortOrder(int iINChI, INCHI_SORT *pINChISort[TAUT_NUM], int *num_components, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, COMP_ATOM_DATA composite_norm_data[TAUT_NUM], int bCompareComponents ); int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ); int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ); int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, /*int nErrorNumber,*/ char *szErrorText, int bError ); int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, char *pErrorText, int bError ); int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, int nStrLen, int bNoStructLabels, int num_input_struct, const char *szSdfLabel, const char *szSdfValue ); int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ); int GetInpStructErrorType(INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ char *pStrErrStruct, int nErrorType, int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ); int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ); int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, const char *szLabel, const char *szValue ); int WriteOrigAtomDataToSDfile( const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, const char* comment, int bChiral, int bAtomsDT, const char *szLabel, const char *szValue); int bIsMetalSalt( inp_ATOM *at, int i ); extern const char gsMissing[]; extern const char gsEmpty[]; extern const char gsSpace[]; extern const char gsEqual[]; /* #define gsMissing "is missing" #define gsEmpty "" #define gsSpace " " #define gsEqual "=" */ /* format string for SDF_LBL_VAL(L,V): %s%s%s%s (four strings) */ /*#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((L)&&(L)[0])?((V)&&(V)[0]?V:gsMissing):gsEmpty*/ #define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?gsMissing:gsEmpty #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __STRUTIL_H__ */ Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/util.c000066400000000000000000001411721271037650300227000ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #include #include #include #include #include "mode.h" #include "incomdef.h" #include "inpdef.h" #include "util.h" #include "extr_ct.h" #include "ichicomp.h" #define MIN_ATOM_CHARGE (-2) #define MAX_ATOM_CHARGE 2 #define NEUTRAL_STATE (-MIN_ATOM_CHARGE) #define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) #define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ typedef struct tagElData { const char *szElName; int nAtMass; /* Avg. atomic mass from the Periodic Chart of the Elements (Fisher cat. no. 05-702-10) */ int nNormAtMass; /* Atomic mass of the most abundant isotope */ double dAtMass; /* exact mw of the most abundant isotope */ int nType; /* METAL or METAL2 */ int nElNegPauling10; /* Pauling electronegativity x 10; 0 => unknown */ int bDoNotAddH; /* InChI does not add implicit H to atoms that have bDoNotAddH != 0 */ S_CHAR cValence[NUM_ATOM_CHARGES][MAX_NUM_VALENCES]; } ELDATA; /* 2004=05-10: Added valences {1,3,5,7,} for As(2-) */ const ELDATA ElData[] = { /* avg norm El No -------- Valence(s) of an ion or neutral atom -------------*/ /* mw mass exact mw type neg H -2 -1 0 +1 +2 */ { "H", 1, 1, 1.007825035, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "D", 2, 2, 2.014101778, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "T", 3, 3, 3.016049268, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "He", 4, 4, 4.002600000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "Li", 7, 7, 7.016000000, METAL , 10, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Be", 9, 9, 9.012180000, METAL , 15, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "B", 11, 11, 11.009300000, 0 , 20, 0, {{3,}, {4,}, {3,}, {2,}, {1,} }}, { "C", 12, 12, 12.000000000, 0 , 25, 0, {{2,}, {3,}, {4,}, {3,}, {2,} }}, { "N", 14, 14, 14.003074000, 0 , 30, 0, {{1,}, {2,}, {3,5}, {4,}, {3,} }}, { "O", 16, 16, 15.994914630, 0 , 35, 0, {{0,}, {1,}, {2,}, {3,5,}, {4,} }}, { "F", 19, 19, 18.998403220, 0 , 40, 0, {{0,}, {0,}, {1,}, {2,}, {3,5} }}, { "Ne", 20, 20, 19.992440000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "Na", 23, 23, 22.989770000, METAL , 9, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Mg", 24, 24, 23.985000000, METAL , 12, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "Al", 27, 27, 26.981540000, METAL , 15, 0, {{3,5,}, {4,}, {3,}, {2,}, {1,} }}, { "Si", 28, 28, 27.976927100, 0 , 18, 0, {{2,}, {3,5}, {4,}, {3,}, {2,} }}, { "P", 31, 31, 30.973762000, 0 , 21, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, { "S", 32, 32, 31.972070700, 0 , 25, 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,} }}, { "Cl", 35, 35, 34.968852730, 0 , 30, 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,} }}, { "Ar", 40, 40, 39.962400000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "K", 39, 39, 38.963700000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Ca", 40, 40, 39.962600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "Sc", 45, 45, 44.955910000, METAL , 13, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Ti", 48, 48, 47.947950000, METAL , 15, 1, {{0,}, {0,}, {3,4}, {0,}, {0,} }}, { "V", 51, 51, 50.943960000, METAL , 16, 1, {{0,}, {0,}, {2,3,4,5,}, {0,}, {0,} }}, { "Cr", 52, 52, 51.940500000, METAL , 16, 1, {{0,}, {0,}, {2,3,6,}, {0,}, {0,} }}, { "Mn", 55, 55, 54.938050000, METAL2, 15, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, { "Fe", 56, 56, 55.934900000, METAL2, 18, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, { "Co", 59, 59, 58.933200000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Ni", 59, 58, 57.935300000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Cu", 64, 63, 62.929600000, METAL , 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, { "Zn", 65, 64, 63.929147000, METAL , 16, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, { "Ga", 70, 69, 68.925600000, METAL , 18, 0, {{3,5,}, {4,}, {3,}, {0,}, {1,} }}, { "Ge", 73, 74, 73.921177400, 0 , 18, 0, {{2,4,6,}, {3,5,}, {4,}, {3,}, {0,} }}, { "As", 75, 75, 74.921594200, 0 , 20, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, { "Se", 79, 80, 79.916519600, 0 , 24, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,} }}, { "Br", 80, 79, 78.918336100, 0 , 28, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,} }}, { "Kr", 84, 84, 83.911500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "Rb", 85, 85, 84.911800000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Sr", 88, 88, 87.905600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "Y", 89, 89, 88.905860000, METAL , 12, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Zr", 91, 90, 89.904700000, METAL , 14, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, { "Nb", 93, 93, 92.906400000, METAL , 16, 1, {{0,}, {0,}, {3,5,}, {0,}, {0,} }}, { "Mo", 96, 98, 97.905400000, METAL , 18, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Tc", 98, 98, 97.907200000, METAL , 19, 1, {{0,}, {0,}, {7,}, {0,}, {0,} }}, { "Ru", 101, 102, 101.904300000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, { "Rh", 103, 103, 102.905500000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,}, {0,}, {0,} }}, { "Pd", 106, 106, 105.903500000, METAL , 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, { "Ag", 108, 107, 106.905100000, METAL , 19, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Cd", 112, 114, 113.903400000, METAL , 17, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, { "In", 115, 115, 114.903900000, METAL , 17, 0, {{3,5,}, {2,4,}, {3,}, {0,}, {1,} }}, { "Sn", 119, 120, 119.902200000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, { "Sb", 122, 121, 120.903800000, METAL, 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, { "Te", 128, 130, 129.906200000, 0 , 21, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, { "I", 127, 127, 126.904500000, 0 , 25, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, { "Xe", 131, 132, 131.904100000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "Cs", 133, 133, 132.905430000, METAL , 7, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Ba", 137, 138, 137.905200000, METAL , 9, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "La", 139, 139, 138.906360000, METAL , 11, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Ce", 140, 140, 139.905400000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, { "Pr", 141, 141, 140.907660000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, { "Nd", 144, 142, 141.907719000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Pm", 145, 145, 144.912800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Sm", 150, 152, 151.919700000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Eu", 152, 153, 152.921200000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Gd", 157, 158, 157.924099000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Tb", 159, 159, 158.925350000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, { "Dy", 163, 164, 163.929200000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, /* mw rounding uncertain */ { "Ho", 165, 165, 164.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Er", 167, 166, 165.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Tm", 169, 169, 168.934230000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Yb", 173, 174, 173.938900000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, { "Lu", 175, 175, 174.940800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Hf", 178, 180, 179.946600000, METAL , 13, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, { "Ta", 181, 181, 180.948010000, METAL , 15, 1, {{0,}, {0,}, {5,}, {0,}, {0,} }}, { "W", 184, 184, 183.951000000, METAL2, 17, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Re", 186, 187, 186.955800000, METAL2, 19, 1, {{0,}, {0,}, {2,4,6,7,}, {0,}, {0,} }}, { "Os", 190, 192, 191.961500000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, { "Ir", 192, 193, 192.962900000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, { "Pt", 195, 195, 194.964800000, METAL2, 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, { "Au", 197, 197, 196.966560000, METAL , 24, 1, {{0,}, {0,}, {1,3,}, {0,}, {0,} }}, { "Hg", 201, 202, 201.970617000, METAL2, 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, { "Tl", 204, 205, 204.974400000, METAL2, 18, 0, {{3,5,}, {2,4,}, {1,3,}, {0,}, {0,} }}, { "Pb", 207, 208, 207.976627000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, { "Bi", 209, 209, 208.980390000, METAL , 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, { "Po", 209, 209, 208.982400000, METAL2, 20, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, { "At", 210, 210, 209.987100000, 0 , 22, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, { "Rn", 222, 222, 222.017500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, { "Fr", 223, 223, 223.019700000, METAL , 0, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Ra", 226, 226, 226.025410000, METAL , 0, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, { "Ac", 227, 227, 227.027750000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Th", 232, 232, 232.038050000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, { "Pa", 231, 231, 231.035880000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,}, {0,}, {0,} }}, { "U", 238, 238, 238.050790000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Np", 237, 237, 237.048170000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Pu", 244, 244, 244.064200000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Am", 243, 243, 243.061370000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, { "Cm", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Bk", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, { "Cf", 251, 251, 251.079600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Es", 252, 252, 252.082800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Fm", 257, 257, 257.095100000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "Md", 258, 258, 258.098600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, { "No", 259, 259, 259.100900000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Lr", 260, 260, 260.105400000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, { "Rf", 261, 261, 261.108700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /*^^^ Added in v. 1.04 */ /* Reference: M. E. WIESER AND T. B. COPLEN. Atomic weights of the elements 2009 (IUPAC Technical Report). Pure Appl. Chem., Vol. 83, No. 2, pp. 359–396, 2011. When available, the mass is given for isotope with the longest half-life. */ /* 105 dubnium Db */ /* ? Like: Ta */ { "Db", 268, 268, 268.125000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 106 seaborgium Sg */ /* ? Like: W */ { "Sg", 271, 271, 271.133000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 107 bohrium Bh */ /* ? Like: Re */ { "Bh", 267, 267, 267.127700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 108 hassium Hs */ /* ? Like: Os */ { "Hs", 277, 277, 277.150000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 109 meitnerium Mt */ /* ? Like: Ir */ { "Mt", 276, 276, 276.151000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 110 darmstadtium Ds */ /* ? Like: Pt */ { "Ds", 281, 281, 281.162000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 111 roentgenium Rg */ /* ? Like: Au */ { "Rg", 280, 280, 280.164000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /* 112 copernicium Cn */ /* ? Like: Hg */ { "Cn", 285, 285, 285.174000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, /*^^^ End of added in v. 1.04 */ #ifdef INCHI_ZFRAG { "Zu", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, //single bond fragment { "Zv", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, //double bond fragment { "Zw", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, //triple bond fragment { "Zx", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, //aromatic bond fragment #endif { "", 0, 0, 0.000000000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, }; /* #ifdef __cplusplus } #endif */ int ERR_ELEM = 255; int nElDataLen = sizeof(ElData)/sizeof(ElData[0])-1; /***********************************************************************************/ int GetElementFormulaFromAtNum(int nAtNum, char *szElement ) { nAtNum -= 1; if ( 0 < nAtNum ) nAtNum += 2; /* bypass D, T */ if ( 0 <= nAtNum && nAtNum < nElDataLen ) { strcpy( szElement, ElData[nAtNum].szElName ); return 0; } strcpy( szElement, "??" ); return -1; } /***********************************************************************************/ int get_el_number( const char* elname ) { int i; const char *p; for ( i = 0; (p=ElData[i].szElName)[0] && strcmp( p, elname ); i++ ) ; return p[0]? i : ERR_ELEM; } /***********************************************************************************/ int get_periodic_table_number( const char* elname ) { int num; num = get_el_number( elname ); if ( num < ERR_ELEM ) num = inchi_max(1, num-1); return num; } /***********************************************************************************/ int do_not_add_H( int nPeriodicNum ) { return ElData[nPeriodicNum>1? nPeriodicNum+1:0].bDoNotAddH; } /***********************************************************************************/ int get_el_valence( int nPeriodicNum, int charge, int val_num ) { if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || val_num >= MAX_NUM_VALENCES ) return 0; return ElData[nPeriodicNum>1? nPeriodicNum+1:0].cValence[NEUTRAL_STATE+charge][val_num]; } /*********************************************************************************** * output valence needed to unumbiguosly reconstruct bonds ***********************************************************************************/ int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) { int i, num_found, chem_valence, rad_adj, known_chem_valence, exact_found; if ( !num_bonds && !num_H ) return 0; if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { if ( bonds_valence == num_bonds ) return 0; /* all single bonds */ return bonds_valence; } if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) return 0; chem_valence = bonds_valence + num_H; rad_adj = 0; num_found = 0; exact_found = 0; /* take into account radical */ if (radical==RADICAL_DOUBLET) rad_adj = 1; else if (radical==RADICAL_TRIPLET ) rad_adj = 2; for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) && num_bonds <= known_chem_valence && known_chem_valence <= chem_valence ) { num_found ++; if ( known_chem_valence == chem_valence ) { exact_found = 1; break; } } } return (exact_found && 1 == num_found)? 0 : chem_valence; } /*********************************************************************************** * output valence needed to unumbiguosly reconstruct number of H ***********************************************************************************/ int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int actual_bonds_valence, int num_H, int num_bonds ) { int i, num_found, num_found_known, chem_valence, rad_adj, known_chem_valence, exact_found; int num_H_expected; char szElement[4]; /* if ( !num_bonds && !num_H ) return 0; */ if ( num_bonds && !GetElementFormulaFromAtNum(nPeriodicNum, szElement ) ) { num_H_expected = get_num_H( szElement, 0, NULL, charge, radical, actual_bonds_valence, 0,0,0,0 ); } else { num_H_expected = num_H; } chem_valence = bonds_valence + num_H; if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || !get_el_valence( nPeriodicNum, charge, 0 ) || do_not_add_H( nPeriodicNum ) || bonds_valence != actual_bonds_valence || num_H_expected != num_H ) { if ( !num_H && !num_H_expected && bonds_valence == actual_bonds_valence ) return 0; /* no H */ return chem_valence; /* needs to add H-atoms */ } /* take into account radical */ if (radical==RADICAL_DOUBLET) rad_adj = 1; else if (radical==RADICAL_TRIPLET ) rad_adj = 2; else rad_adj = 0; num_found_known = 0; num_found = 0; exact_found = 0; for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )) && bonds_valence <= (known_chem_valence -= rad_adj) ) { /* found known valence that fits without H */ num_found_known ++; if ( known_chem_valence <= chem_valence ) { /* known valence is large enough to accommodate (implicit) H */ num_found ++; } if ( known_chem_valence == chem_valence ) { exact_found = 1; break; } } } return (exact_found && 1 == num_found && 1 == num_found_known)? 0 : chem_valence? chem_valence : -1 /* needs zero */; } /*********************************************************************************** * output valence that does not fit any known valences ***********************************************************************************/ int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) { int i, chem_valence, rad_adj, known_chem_valence; if ( !num_bonds && !num_H ) return 0; if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { if ( bonds_valence == num_bonds ) return 0; /* all single bonds */ return bonds_valence; } if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) return 0; chem_valence = bonds_valence + num_H; rad_adj = 0; /* take into account radical */ if (radical==RADICAL_DOUBLET) rad_adj = 1; else if (radical==RADICAL_TRIPLET || radical==RADICAL_SINGLET ) rad_adj = 2; for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) ) { if ( known_chem_valence == chem_valence ) { return 0; } } } return chem_valence; } /***********************************************************************************/ int get_el_type( int nPeriodicNum ) { return ElData[nPeriodicNum+1].nType; } /***********************************************************************************/ int is_el_a_metal( int nPeriodicNum ) { return 0!=(ElData[nPeriodicNum+1].nType & IS_METAL); } /******************************************************************************************************/ /*#ifndef TARGET_API_LIB*/ int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) { char *q, *r, *p; int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; p = elname; /* extract radicals & charges */ while ( q = strpbrk( p, "+-^" ) ) { switch ( *q ) { case '+': case '-': for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { nVal += (nLastSign = nSign); charge_len ++; } if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ nVal += nLastSign * (nSign-1); } charge_len = r - q; nCharge += nVal; break; /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ case '^': nRad = 1; /* doublet here is 1. See below */ charge_len = 1; for ( k = 1; q[0] == q[k]; k++ ) { nRad ++; charge_len ++; } break; } memmove( q, q+charge_len, strlen(q+charge_len)+1 ); } len = (int) strlen(p); /* radical */ if ( (q = strrchr( p, ':' )) && !q[1]) { nRad = RADICAL_SINGLET; q[0] = '\0'; len --; } else { while( (q = strrchr( p, '.' )) && !q[1] ) { nRad ++; q[0] = '\0'; len --; } nRad = nRad == 1? RADICAL_DOUBLET : nRad == 2? RADICAL_TRIPLET : 0; } *pnRadical = nRad; *pnCharge = nCharge; return ( nRad || nCharge ); } /*#endif*/ /****************************************************************/ int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) { int i, len, c, k, num_H, val; char *q; i = 0; num_H = 0; len = (int)strlen(elname); c = UCINT elname[0]; while ( i < len ) { switch ( c ) { case 'H': k = 0; break; case 'D': k = 1; break; case 'T': k = 2; break; default: k = -1; break; } q = elname+i+1; /* pointer to the next to elname[i] character */ c = UCINT q[0]; if ( k >= 0 && !islower( c ) ) { /* found a hydrogen */ if ( isdigit( c ) ) { val = (int)strtol( q, &q, 10 ); /* q = pointer to the next to number of hydrogen atom(s) character */ } else { val = 1; } if ( k ) { num_iso_H[k] += val; } else { num_H += val; } /* remove the hydrogen atom from the string */ len -= (q-elname)-i; memmove( elname+i, q, len + 1 ); /* c = UCINT elname[i]; */ } else { i ++; } c = UCINT elname[i]; /* moved here 11-04-2002 */ } return num_H; } /***********************************************************************************/ int get_num_H (const char* elname, int inp_num_H, S_CHAR inp_num_iso_H[], int charge, int radical, int chem_bonds_valence, int atom_input_valence, int bAliased, int bDoNotAddH, int bHasMetalNeighbor ) { int val, i, el_number, num_H = 0, num_iso_H; static int el_number_N = 0, el_number_S, el_number_O, el_number_C; if ( !el_number_N ) { el_number_N = get_el_number( "N" ); el_number_S = get_el_number( "S" ); el_number_O = get_el_number( "O" ); el_number_C = get_el_number( "C" ); } /* atom_input_valence (cValence) cannot be specified in case of */ /* aliased MOLFile atom with known inp_num_H or inp_num_iso_H[] */ if ( bAliased ) { num_H = inp_num_H; } else if ( atom_input_valence && (atom_input_valence !=15 || chem_bonds_valence) ) { num_H = inchi_max( 0, atom_input_valence - chem_bonds_valence ); } else if ( atom_input_valence == 15 && !chem_bonds_valence ) { num_H = 0; } else if ( MIN_ATOM_CHARGE <= charge && MAX_ATOM_CHARGE >= charge && ERR_ELEM != (el_number = get_el_number( elname ) ) && !ElData[el_number].bDoNotAddH && !bDoNotAddH ) { /* add hydrogen atoms according to standard element valence */ if ( radical && radical != RADICAL_SINGLET ) { if ( val = ElData[el_number].cValence[NEUTRAL_STATE+charge][0] ) { val -= (radical==RADICAL_DOUBLET)? 1 : (radical==RADICAL_SINGLET || radical==RADICAL_TRIPLET )? 2 : val; /* if unknown radical then do not add H */ num_H = inchi_max( 0, val - chem_bonds_valence ); } } else { /* find the smallest valence that is greater than the sum of the chemical bond valences */ for ( i = 0; (val=ElData[el_number].cValence[NEUTRAL_STATE+charge][i]) && val < chem_bonds_valence; i++ ) ; /* special case: do not add H to N(IV), S(III), S+(II), S-(II) */ /* S ions added 2004-05-10 */ if ( el_number == el_number_N && !charge && !radical && val == 5 ) val = 3; else /* if ( el_number == el_number_N && !charge && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor ) val = 2; else */ if ( el_number == el_number_S && !charge && !radical && val == 4 && chem_bonds_valence == 3 ) val = 3; else if ( bHasMetalNeighbor && el_number != el_number_C && val > 0 ) { val --; } /* if ( (el_number == el_number_S || el_number == el_number_O) && abs(charge)==1 && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor ) val = 2; else */ num_H = inchi_max( 0, val - chem_bonds_valence ); } num_iso_H = 0; if ( inp_num_iso_H ) { for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { num_iso_H += inp_num_iso_H[i]; } } /* should not happen because atom here is not aliased */ if ( num_iso_H ) { if ( num_H >= num_iso_H ) { num_H -= num_iso_H; } else { num_H = inp_num_H; /* as requested in the alias */ /* num_H = (num_iso_H - num_H) % 2; */ /* keep unchanged parity of the total number of H atoms */ } } /* should not happen because atom here is not aliased */ if ( inp_num_H > num_H ) { num_H = inp_num_H; /* as requested in the alias */ /* num_H = inp_num_H + (inp_num_H - num_H)%2; */ /* keep unchanged parity of the number of non-isotopic H atoms */ } } else { num_H = inp_num_H; } return num_H; } /***********************************************************************************/ int get_atw_from_elnum( int nAtNum ) { nAtNum -= 1; if ( 0 < nAtNum ) nAtNum += 2; /* bypass D, T */ if ( 0 <= nAtNum && nAtNum < nElDataLen ) { return (int)ElData[nAtNum].nAtMass; } return 0; } /***********************************************************************************/ /* int get_mw(char elname[]) { int i; for (i=0; i 0 ) { memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); i -= n; len -= n; } n = -1; } } if ( n == len ) /* empty line */ name[len=0] = '\0'; else if ( ++n && n <= len ) { len -= n; name[len] = '\0'; } return len; } #endif /* ifndef TARGET_API_LIB */ /************************************************/ #ifndef inchi_malloc void *inchi_malloc(size_t c) { return malloc(c); } #endif #ifndef inchi_calloc void *inchi_calloc(size_t c, size_t n) { return calloc(c,n); } #endif #ifndef inchi_free void inchi_free(void *p) { if(p) { free(p); /*added check if zero*/ } } #endif #ifndef TARGET_API_LIB /*************************************************************************/ void remove_trailing_spaces( char* p ) { int len; for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) ; p[++len] = '\0'; } /*************************************************************************/ void remove_one_lf( char* p) { size_t len; if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ){ p[len-1] = '\0'; if ( len >= 2 && p[len-2] == '\r' ) p[len-2] = '\0'; } } #endif /* ifndef TARGET_API_LIB */ /***************************************************************************/ /* Copies up to maxlen characters INCLUDING end null from source to target */ /* Fills out the rest of the target with null bytes */ int mystrncpy(char *target,const char *source,unsigned maxlen) { /* protected from non-zero-terminated source and overlapped target/source. 7-9-99 DCh. */ const char *p; unsigned len; if (target==NULL || maxlen == 0 || source == NULL) return 0; if ( p = (const char*)memchr(source, 0, maxlen) ) { len = p-source; /* maxlen does not include the found zero termination */ } else { len = maxlen-1; /* reduced length does not include one more byte for zero termination */ } if ( len ) memmove( target, source, len ); /* target[len] = '\0'; */ memset( target+len, 0, maxlen-len); /* zero termination */ return 1; } /************************************************************************/ /* Remove leading and trailing white spaces */ char* LtrimRtrim( char *p, int* nLen ) { int i, len=0; if ( p && (len = (int) strlen( p )) ) { for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) ; if ( i ) (memmove)( p, p+i, (len -= i)+1 ); for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len-- ) ; p[len] = '\0'; } if ( nLen ) *nLen = len; return p; } /*************************************************************************/ AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ) { for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) ; return nPathLen? pathAtom : NULL; } /******************************************************************************************************/ int nBondsValToMetal( inp_ATOM* at, int iat ) { int i, neigh, bond_type, nVal2Metal = 0; inp_ATOM* a = at + iat; for ( i = 0; i < a->valence; i ++ ) { neigh = a->neighbor[i]; if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { bond_type = a->bond_type[i]; if ( bond_type <= BOND_TYPE_TRIPLE ) { nVal2Metal += bond_type; } else { return -1; /* bond to metal order is not well defined */ } } } return nVal2Metal; } /************************************************************************/ int num_of_H( inp_ATOM *at, int iat ) { static int el_number_H; int i, n, num_explicit_H = 0; inp_ATOM *a = at + iat; if ( !el_number_H ) el_number_H = get_periodic_table_number( "H" ); for ( i = 0; i < a->valence; i ++ ) { n = a->neighbor[i]; num_explicit_H += ( 1 == at[n].valence && el_number_H == at[n].el_number ); } return num_explicit_H+NUMH(at,iat); } /************************************************************************/ int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) { int charge = at[iat_ion_neigh].charge; int i, neigh; for ( i = 0; i < at[iat].valence; i ++ ) { neigh = at[iat].neighbor[i]; if ( neigh != iat_ion_neigh && at[neigh].charge == charge && NULL != memchr( el, at[neigh].el_number, el_len ) ) { return 1; } } return 0; } /************************************************************************/ /* BFS r=2 */ int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) { #define MAXQ 16 AT_NUMB q[MAXQ]; int lenq=0, lenq2, dist = 0, i = 0, iq, neigh, j, nRet=0; q[lenq++] = iat; at[iat].cFlags = 1; iq = 0; dist = 1; /* use at->cFlags as an indicator */ while ( dist <= 2 ) { for ( lenq2 = lenq; iq < lenq2; iq ++ ) { i = q[iq]; for ( j = 0; j < at[i].valence; j ++ ) { neigh = at[i].neighbor[j]; if ( !at[neigh].cFlags && at[neigh].valence <= 3 && NULL != memchr( el, at[neigh].el_number, el_len ) ) { q[lenq ++] = neigh; at[neigh].cFlags = 1; if ( neigh != iat_ion_neigh && at[iat_ion_neigh].charge == at[neigh].charge ) { nRet ++; } } } } dist ++; } for ( iq = 0; iq < lenq; iq ++ ) { i = q[iq]; at[i].cFlags = 0; } return nRet; } /************************************************************************/ int nNoMetalNumBonds( inp_ATOM *at, int at_no ) { inp_ATOM *a = at + at_no; int num_H = NUMH(a, 0); int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); int i; if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { int valence_to_metal = 0; int num_bonds_to_metal = 0; for ( i = 0; i < a->valence; i ++ ) { if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { return a->valence; /* fall back */ } num_bonds_to_metal ++; valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); } } if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { /* removing bonds to metal produces standard valence */ return a->valence - num_bonds_to_metal; } } #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) else if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { int valence_to_metal = 0; int num_bonds_to_metal = 0; for ( i = 0; i < a->valence; i ++ ) { if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { return a->valence; /* fall back */ } num_bonds_to_metal ++; valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); } } if ( 1 == valence_to_metal ) { /* removing bonds to metal produces standard valence */ return a->valence - num_bonds_to_metal; } } #endif return a->valence; } /************************************************************************/ int nNoMetalBondsValence( inp_ATOM *at, int at_no ) { inp_ATOM *a = at + at_no; int num_H = NUMH(a, 0); int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); int i; if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { int valence_to_metal = 0; /*int num_bonds_to_metal = 0;*/ for ( i = 0; i < a->valence; i ++ ) { if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { return a->valence; /* fall back */ } /*num_bonds_to_metal ++;*/ valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); } } if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { /* removing bonds to metal produces standard valence */ return a->chem_bonds_valence - valence_to_metal; } } #if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) else if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { int valence_to_metal = 0; /*int num_bonds_to_metal = 0;*/ for ( i = 0; i < a->valence; i ++ ) { if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { return a->valence; /* fall back */ } /*num_bonds_to_metal ++;*/ valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); } } if ( 1 == valence_to_metal ) { /* removing bonds to metal produces standard valence */ return a->chem_bonds_valence - valence_to_metal; } } #endif return a->chem_bonds_valence; } /************************************************************************/ int nNoMetalNeighIndex( inp_ATOM *at, int at_no ) { inp_ATOM *a = at + at_no; int i; for ( i = 0; i < a->valence; i ++ ) { if ( !is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { return i; } } return -1; } /************************************************************************/ int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ) { inp_ATOM *a = at + at_no; int i, neigh; for ( i = 0; i < a->valence; i ++ ) { neigh = (int)a->neighbor[i]; if ( neigh != cur_neigh && !is_el_a_metal( at[neigh].el_number ) ) { return i; } } return -1; } /************************************************************************/ int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ) { inp_ATOM *a = at + at_no; int i, neigh; for ( i = 0; i < a->valence; i ++ ) { neigh = (int)a->neighbor[i]; if ( neigh != cur_neigh && neigh != cur_neigh2 && !is_el_a_metal( at[neigh].el_number ) ) { return i; } } return -1; } #ifndef COMPILE_ANSI_ONLY /**************************************************************************/ int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ) { int i, j, len, num; len = 0; if ( nNumRemovedProtons ) { len = sprintf ( szRemovedProtons, "Proton balance: %c %d H+", nNumRemovedProtons>=0? '+':'-', abs(nNumRemovedProtons) ); } if ( bIsotopic && (nNumRemovedProtonsIsotopic || nNumExchgIsotopicH) ) { for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { num = (nNumExchgIsotopicH? nNumExchgIsotopicH[i]:0) + (nNumRemovedProtonsIsotopic? nNumRemovedProtonsIsotopic[i]:0); if ( num ) { len += sprintf( szRemovedProtons+len, "%s %d^%dH", j? ", ":" [ removed ", num, i+1); j ++; } } if ( j ) { len += sprintf( szRemovedProtons+len, " ]" ); if ( num_removed_iso_H ) *num_removed_iso_H = j; } } if ( !len ) { szRemovedProtons[0] = '\0'; } return len; } #endif /* According to http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:inchi/ An InChI identifier may contain the following characters: A-Z a-z 0-9 ()*+,-./;=?@ Here we consider any character not conforming this specification as a whitespace which marks the end of the InChI string. For example: "InChI=1/Ar%" "InChI=1/Ar\n" "InChI=1/Ar\r\t" all will be trimmed to "InChI=1/Ar" */ /**************************************************************************/ void extract_inchi_substring(char ** buf, const char *str, size_t slen) { size_t i; char *p, pp; *buf = NULL; if (str==NULL) return; if (strlen(str)<1) return; p = strstr(str, "InChI="); if (NULL==p) return; for (i=0; i= 'A' && pp <='Z') continue; if (pp >= 'a' && pp <='z') continue; if (pp >= '0' && pp <='9') continue; switch ( pp ) { case '(': case ')': case '*': case '+': case ',': case '-': case '.': case '/': case ';': case '=': case '?': case '@': continue; default: break; } break; } *buf = (char*) inchi_calloc(i+1, sizeof(char)); memcpy(*buf, p, i); (*buf)[i] = '\0'; return; } #ifdef COMPILE_ANSI_ONLY /*************************************************************************/ /************* non-ANSI functions ****************/ /*************************************************************************/ #define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) #if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) /* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ int memicmp ( const void * p1, const void * p2, size_t length ) { const U_CHAR *s1 = (const U_CHAR*)p1; const U_CHAR *s2 = (const U_CHAR*)p2; while ( length-- ) { if ( *s1 == *s2 || __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { s1 ++; s2 ++; } else { return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); } } return 0; } /*************************************************************************/ int stricmp( const char *s1, const char *s2 ) { while ( *s1 ) { if ( *s1 == *s2 || __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { s1 ++; s2 ++; } else { return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); } } if ( *s2 ) return -1; return 0; } /*************************************************************************/ char *_strnset( char *s, int val, size_t length ) { char *ps = s; while (length-- && *ps) *ps++ = (char)val; return s; } /*************************************************************************/ char *_strdup( const char *string ) { char *p = NULL; if ( string ) { size_t length = strlen( string ); p = (char *) inchi_malloc( length + 1 ); if ( p ) { strcpy( p, string ); } } return p; } #endif #endif Indigo-indigo-1.2.3/third_party/inchi/inchi_dll/util.h000066400000000000000000000106571271037650300227100ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ #ifndef __UTIL_H__ #define __UTIL_H__ #include "inpdef.h" /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus extern "C" { #endif #endif int get_atw(const char *elname); int get_atw_from_elnum( int nAtNum ); int get_num_H (const char* elname, int inp_num_H, S_CHAR num_iso_H[], int charge, int radical, int chem_bonds_valence, int atom_input_valence, int bAliased, int bDoNotAddH, int bHasMetalNeighbor ); int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); int normalize_name( char* name ); int mystrncpy(char *target,const char *source,unsigned maxlen); char* LtrimRtrim( char *p, int* nLen ); void remove_trailing_spaces( char* p ); void remove_one_lf( char* p); void mystrrev( char *p ); #define ALPHA_BASE 27 long inchi_strtol( const char *str, const char **p, int base); double inchi_strtod( const char *str, const char **p ); AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); int get_periodic_table_number( const char* elname ); int is_el_a_metal( int nPeriodicNum ); int get_el_valence( int nPeriodicNum, int charge, int val_num ); int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int actual_bonds_val, int num_H, int num_bonds ); int get_el_type( int nPeriodicNum ); int get_el_number( const char* elname ); int do_not_add_H( int nPeriodicNum ); int GetElementFormulaFromAtNum(int nAtNum, char *szElement ); int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ); /* ion pairs and fixing bonds */ int num_of_H( inp_ATOM *at, int iat ); int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); int nNoMetalNumBonds( inp_ATOM *at, int at_no ); int nNoMetalBondsValence( inp_ATOM *at, int at_no ); int nNoMetalNeighIndex( inp_ATOM *at, int at_no ); int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ); int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ); void extract_inchi_substring(char ** buf, const char *str, size_t slen); /* mol2atom.c */ int nBondsValToMetal( inp_ATOM* at, int iat ); /* ichi_bns.c */ int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ); int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ); /* IChICan2.c */ int SetBitFree( void ); void WriteCoord( char *str, double x ); extern int ERR_ELEM; extern int nElDataLen; /* BILLY 8/6/04 */ #ifndef COMPILE_ALL_CPP #ifdef __cplusplus } #endif #endif #endif /* __UTIL_H__*/ Indigo-indigo-1.2.3/third_party/inchi/readme.txt000066400000000000000000000115741271037650300216320ustar00rootroot00000000000000/* * International Chemical Identifier (InChI) * Version 1 * Software version 1.04 * September 9, 2011 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). * Originally developed at NIST. Modifications and additions by IUPAC * and the InChI Trust. * * IUPAC/InChI-Trust Licence No.1.0 for the * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust * Licence for the International Chemical Identifier (InChI) Software * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") * for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust * c/o FIZ CHEMIE Berlin * * Franklinstrasse 11 * 10587 Berlin * GERMANY * * or email to: ulrich@inchi-trust.org. * */ This directory contains code, VC++ projects and gcc makefiles of InChI generation library with API (Win32 - libinchi.dll; Linux - libinchi.so) and demo applications which call libinchi. The library produces both standard and non-standard InChI/InChIKey. The library does not provide any support for graphic user interface (GUI). It is not designed to work in a multithreaded environment and should be called from only one thread at a time. Besides the InChI library itself, this directory contains examples of using previously available and newly added InChI software library functionality (inchi_main and make_inchi.py). Note that the demo programs are samples which are not supposed to be used for the production. ========= FILES ========= readme.txt This file inchi_dll SUB-DIRECTORY Contains InChI Library source code inchi_main SUB-DIRECTORY Contains ANSI-C demo application source to call InChI Library libinchi.dll under Microsoft Windows or libinchi.so under Linux or Unix. vc9 SUB-DIRECTORY inchi_dll SUB-DIRECTORY Contains MS Visual C++ 2008 project to build dynamically linked library libinchi.dll under Windows. inchi_main SUB-DIRECTORY Contains MS Visual C++ 2008 project to build both dynamically linked library libinchi.dll and the testing application inchi_main.exe under MS Windows (both library and executable are placed into sub-directory vc9/inchi_dll/Release). gcc_so_makefile SUB-DIRECTORY Contains a gcc makefile for INCHI_MAIN + INCHI_DLL code to create a InChI library as a shared object (Linux) or dll (Windows) dynamically linked to the main program result SUB-DIRECTORY Contains shared object libinchi.so.1.04.00.gz and demo application inchi_main.gz (for Linux) python_sample SUB-DIRECTORY Contains Python demo application calling InChI Library functions. Notes on inchi_main demo application ------------------------------------ Defining CREATE_INCHI_STEP_BY_STEP in e_mode.h makes program use the modularized interface to InChI generation process. Modularized interface is used by default; commenting out the line containing the #define makes the program use software version 1.01 ("classic") interface to InChI generation process. If the demo application is compiled with CREATE_INCHI_STEP_BY_STEP option, an additional defining of OUTPUT_NORMALIZATION_DATA in e_mode.h makes the program output the intermediate (normalization) data into the log file. The related data structures are described in header files inchi_api.h; their use is exemplified in e_ichimain_a.c file. Note that including the output of the intermediate (normalization) data may produce a very long log file. Please notice that /D "BUILD_LINK_AS_DLL" Visual C ++ compiler option is necessary to create and link the dll and the testing executable with Microsoft Visual C++ under Win32. ========= LINKS ========= IUPAC http://www.iupac.org/inchi InChI Trust http://www.inchi-trust.org InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss Indigo-indigo-1.2.3/third_party/libpng-src/000077500000000000000000000000001271037650300205725ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/libpng-src/CMakeLists.txt000066400000000000000000000022541271037650300233350ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(PNG C) if(USE_SYSTEM_PNG) find_package(PNG) if (NOT PNG_FOUND) MESSAGE(FATAL_ERROR "Cannot find system png library") endif() endif() if (NOT PNG_FOUND) message(STATUS "Using local LibPNG library") include_directories(include) set(PNG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) aux_source_directory(src PNG_SOURCES) if (NOT ZLIB_FOUND) message(STATUS "Finding zlib for libpng") # TODO: if zlib is local that use it too find_package(ZLIB) endif() include_directories(${ZLib_HEADERS_DIR}) list(REMOVE_ITEM PNG_SOURCES src/pngtest.c) # To remove GLIBC_2.11 dependency set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0") add_library(png STATIC ${PNG_SOURCES}) if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4267") endif() set_target_properties(png PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET png PROPERTY FOLDER "third_party") if (NOT NO_STATIC) pack_static(png) endif() endif() Indigo-indigo-1.2.3/third_party/libpng-src/LICENSE000066400000000000000000000103331271037650300215770ustar00rootroot00000000000000 This copy of the libpng notices is provided for your convenience. In case of any discrepancy between this copy and the notices in the file png.h that is included in the libpng distribution, the latter shall prevail. COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: If you modify libpng you may insert additional notices immediately following this sentence. This code is released under the libpng license. libpng versions 1.2.6, August 15, 2004, through 1.4.4, September 23, 2010, are Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.2.5 with the following individual added to the list of Contributing Authors Cosmin Truta libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.0.6 with the following individuals added to the list of Contributing Authors Simon-Pierre Cadieux Eric S. Raymond Gilles Vollant and with the following additions to the disclaimer: There is no warranty against interference with your enjoyment of the library or against infringement. There is no warranty that our efforts or the library will fulfill any of your particular purposes or needs. This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user. libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-0.96, with the following individuals added to the list of Contributing Authors: Tom Lane Glenn Randers-Pehrson Willem van Schaik libpng versions 0.89, June 1996, through 0.96, May 1997, are Copyright (c) 1996, 1997 Andreas Dilger Distributed according to the same disclaimer and license as libpng-0.88, with the following individuals added to the list of Contributing Authors: John Bowler Kevin Bracey Sam Bushell Magnus Holmgren Greg Roelofs Tom Tanner libpng versions 0.5, May 1995, through 0.88, January 1996, are Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. For the purposes of this copyright and license, "Contributing Authors" is defined as the following set of individuals: Andreas Dilger Dave Martindale Guy Eric Schalnat Paul Schmidt Tim Wegner The PNG Reference Library is supplied "AS IS". The Contributing Authors and Group 42, Inc. disclaim all warranties, expressed or implied, including, without limitation, the warranties of merchantability and of fitness for any purpose. The Contributing Authors and Group 42, Inc. assume no liability for direct, indirect, incidental, special, exemplary, or consequential damages, which may result from the use of the PNG Reference Library, even if advised of the possibility of such damage. Permission is hereby granted to use, copy, modify, and distribute this source code, or portions hereof, for any purpose, without fee, subject to the following restrictions: 1. The origin of this source code must not be misrepresented. 2. Altered versions must be plainly marked as such and must not be misrepresented as being the original source. 3. This Copyright notice may not be removed or altered from any source or altered source distribution. The Contributing Authors and Group 42, Inc. specifically permit, without fee, and encourage the use of this source code as a component to supporting the PNG file format in commercial products. If you use this source code in a product, acknowledgment is not required but would be appreciated. A "png_get_copyright" function is available, for convenient use in "about" boxes and the like: printf("%s",png_get_copyright(NULL)); Also, the PNG logo (in PNG format, of course) is supplied in the files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a certification mark of the Open Source Initiative. Glenn Randers-Pehrson glennrp at users.sourceforge.net September 23, 2010 Indigo-indigo-1.2.3/third_party/libpng-src/README000066400000000000000000000325721271037650300214630ustar00rootroot00000000000000README for libpng version 1.4.4 - September 23, 2010 (shared library 14.0) See the note about version numbers near the top of png.h See INSTALL for instructions on how to install libpng. Libpng comes in several distribution formats. Get libpng-*.tar.gz, libpng-*.tar.xz or libpng-*.tar.bz2 if you want UNIX-style line endings in the text files, or lpng*.zip if you want DOS-style line endings. Version 0.89 was the first official release of libpng. Don't let the fact that it's the first release fool you. The libpng library has been in extensive use and testing since mid-1995. By late 1997 it had finally gotten to the stage where there hadn't been significant changes to the API in some time, and people have a bad feeling about libraries with versions < 1.0. Version 1.0.0 was released in March 1998. **** Note that some of the changes to the png_info structure render this version of the library binary incompatible with libpng-0.89 or earlier versions if you are using a shared library. The type of the "filler" parameter for png_set_filler() has changed from png_byte to png_uint_32, which will affect shared-library applications that use this function. To avoid problems with changes to the internals of png_info_struct, new APIs have been made available in 0.95 to avoid direct application access to info_ptr. These functions are the png_set_ and png_get_ functions. These functions should be used when accessing/storing the info_struct data, rather than manipulating it directly, to avoid such problems in the future. It is important to note that the APIs do not make current programs that access the info struct directly incompatible with the new library. However, it is strongly suggested that new programs use the new APIs (as shown in example.c and pngtest.c), and older programs be converted to the new format, to facilitate upgrades in the future. **** Additions since 0.90 include the ability to compile libpng as a Windows DLL, and new APIs for accessing data in the info struct. Experimental functions include the ability to set weighting and cost factors for row filter selection, direct reads of integers from buffers on big-endian processors that support misaligned data access, faster methods of doing alpha composition, and more accurate 16->8 bit color conversion. The additions since 0.89 include the ability to read from a PNG stream which has had some (or all) of the signature bytes read by the calling application. This also allows the reading of embedded PNG streams that do not have the PNG file signature. As well, it is now possible to set the library action on the detection of chunk CRC errors. It is possible to set different actions based on whether the CRC error occurred in a critical or an ancillary chunk. The changes made to the library, and bugs fixed are based on discussions on the PNG-implement mailing list and not on material submitted privately to Guy, Andreas, or Glenn. They will forward any good suggestions to the list. For a detailed description on using libpng, read libpng.txt. For examples of libpng in a program, see example.c and pngtest.c. For usage information and restrictions (what little they are) on libpng, see png.h. For a description on using zlib (the compression library used by libpng) and zlib's restrictions, see zlib.h I have included a general makefile, as well as several machine and compiler specific ones, but you may have to modify one for your own needs. You should use zlib 1.0.4 or later to run this, but it MAY work with versions as old as zlib 0.95. Even so, there are bugs in older zlib versions which can cause the output of invalid compression streams for some images. You will definitely need zlib 1.0.4 or later if you are taking advantage of the MS-DOS "far" structure allocation for the small and medium memory models. You should also note that zlib is a compression library that is useful for more things than just PNG files. You can use zlib as a drop-in replacement for fread() and fwrite() if you are so inclined. zlib should be available at the same place that libpng is, or at. ftp://ftp.info-zip.org/pub/infozip/zlib You may also want a copy of the PNG specification. It is available as an RFC, a W3C Recommendation, and an ISO/IEC Standard. You can find these at http://www.libpng.org/pub/png/documents/ This code is currently being archived at libpng.sf.net in the [DOWNLOAD] area, and on CompuServe, Lib 20 (PNG SUPPORT) at GO GRAPHSUP. If you can't find it in any of those places, e-mail me, and I'll help you find it. If you have any code changes, requests, problems, etc., please e-mail them to me. Also, I'd appreciate any make files or project files, and any modifications you needed to make to get libpng to compile, along with a #define variable to tell what compiler/system you are on. If you needed to add transformations to libpng, or wish libpng would provide the image in a different way, drop me a note (and code, if possible), so I can consider supporting the transformation. Finally, if you get any warning messages when compiling libpng (note: not zlib), and they are easy to fix, I'd appreciate the fix. Please mention "libpng" somewhere in the subject line. Thanks. This release was created and will be supported by myself (of course based in a large way on Guy's and Andreas' earlier work), and the PNG development group. Send comments/corrections/commendations to png-mng-implement at lists.sourceforge.net (subscription required; visit https://lists.sourceforge.net/lists/listinfo/png-mng-implement to subscribe) or to glennrp at users.sourceforge.net You can't reach Guy, the original libpng author, at the addresses given in previous versions of this document. He and Andreas will read mail addressed to the png-implement list, however. Please do not send general questions about PNG. Send them to the (png-list at ccrc.wustl.edu, subscription required, write to majordomo at ccrc.wustl.edu with "subscribe png-list" in your message). On the other hand, please do not send libpng questions to that address, send them to me or to the png-implement list. I'll get them in the end anyway. If you have a question about something in the PNG specification that is related to using libpng, send it to me. Send me any questions that start with "I was using libpng, and ...". If in doubt, send questions to me. I'll bounce them to others, if necessary. Please do not send suggestions on how to change PNG. We have been discussing PNG for nine years now, and it is official and finished. If you have suggestions for libpng, however, I'll gladly listen. Even if your suggestion is not used immediately, it may be used later. Files in this distribution: ANNOUNCE => Announcement of this version, with recent changes CHANGES => Description of changes between libpng versions KNOWNBUG => List of known bugs and deficiencies LICENSE => License to use and redistribute libpng README => This file TODO => Things not implemented in the current library Y2KINFO => Statement of Y2K compliance example.c => Example code for using libpng functions libpng.3 => manual page for libpng (includes libpng.txt) libpng.txt => Description of libpng and its functions libpngpf.3 => manual page for libpng's private functions png.5 => manual page for the PNG format png.c => Basic interface functions common to library png.h => Library function and interface declarations pngconf.h => System specific library configuration pngerror.c => Error/warning message I/O functions pngget.c => Functions for retrieving info from struct pngmem.c => Memory handling functions pngbar.png => PNG logo, 88x31 pngnow.png => PNG logo, 98x31 pngpread.c => Progressive reading functions pngread.c => Read data/helper high-level functions pngrio.c => Lowest-level data read I/O functions pngrtran.c => Read data transformation functions pngrutil.c => Read data utility functions pngset.c => Functions for storing data into the info_struct pngtest.c => Library test program pngtest.png => Library test sample image pngtrans.c => Common data transformation functions pngwio.c => Lowest-level write I/O functions pngwrite.c => High-level write functions pngwtran.c => Write data transformations pngwutil.c => Write utility functions contrib => Contributions gregbook => source code for PNG reading and writing, from Greg Roelofs' "PNG: The Definitive Guide", O'Reilly, 1999 msvctest => Builds and runs pngtest using a MSVC workspace pngminus => Simple pnm2png and png2pnm programs pngsuite => Test images visupng => Contains a MSVC workspace for VisualPng projects => Contains project files and workspaces for building a DLL c5builder => Contains a Borland workspace for building libpng and zlib visualc6 => Contains a Microsoft Visual C++ (MSVC) workspace for building libpng and zlib scripts => Directory containing scripts for building libpng: descrip.mms => VMS makefile for MMS or MMK makefile.std => Generic UNIX makefile (cc, creates static libpng.a) makefile.elf => Linux/ELF makefile symbol versioning, (gcc, creates libpng14.so.14.1.4.4) makefile.linux => Linux/ELF makefile (gcc, creates libpng14.so.14.1.4.4) makefile.gcc => Generic makefile (gcc, creates static libpng.a) makefile.knr => Archaic UNIX Makefile that converts files with ansi2knr (Requires ansi2knr.c from ftp://ftp.cs.wisc.edu/ghost) makefile.aix => AIX makefile makefile.cygwin => Cygwin/gcc makefile makefile.darwin => Darwin makefile makefile.dec => DEC Alpha UNIX makefile makefile.freebsd => FreeBSD makefile makefile.hpgcc => HPUX makefile using gcc makefile.hpux => HPUX (10.20 and 11.00) makefile makefile.hp64 => HPUX (10.20 and 11.00) makefile, 64 bit makefile.ibmc => IBM C/C++ version 3.x for Win32 and OS/2 (static) makefile.intel => Intel C/C++ version 4.0 and later makefile.mingw => Mingw/gcc makefile makefile.netbsd => NetBSD/cc makefile, makes libpng.so. makefile.ne14bsd => NetBSD/cc makefile, makes libpng14.so makefile.openbsd => OpenBSD makefile makefile.sgi => Silicon Graphics IRIX (cc, creates static lib) makefile.sggcc => Silicon Graphics (gcc, creates libpng14.so.14.1.4.4) makefile.sunos => Sun makefile makefile.solaris => Solaris 2.X makefile (gcc, creates libpng14.so.14.1.4.4) makefile.so9 => Solaris 9 makefile (gcc, creates libpng14.so.14.1.4.4) makefile.32sunu => Sun Ultra 32-bit makefile makefile.64sunu => Sun Ultra 64-bit makefile makefile.sco => For SCO OSr5 ELF and Unixware 7 with Native cc makefile.mips => MIPS makefile makefile.acorn => Acorn makefile makefile.amiga => Amiga makefile smakefile.ppc => AMIGA smakefile for SAS C V6.58/7.00 PPC compiler (Requires SCOPTIONS, copied from scripts/SCOPTIONS.ppc) makefile.atari => Atari makefile makefile.beos => BEOS makefile for X86 makefile.bor => Borland makefile (uses bcc) makefile.bc32 => 32-bit Borland C++ (all modules compiled in C mode) makefile.tc3 => Turbo C 3.0 makefile makefile.dj2 => DJGPP 2 makefile makefile.msc => Microsoft C makefile makefile.vcwin32 => makefile for Microsoft Visual C++ 4.0 and later (does not use assembler code) makefile.os2 => OS/2 Makefile (gcc and emx, requires pngos2.def) png32ce.def => module definition for makefile.cegccg pngos2.def => OS/2 module definition file used by makefile.os2 pngwin.def => module definition file used by makefile.cygwin and makefile.mingw makefile.watcom => Watcom 10a+ Makefile, 32-bit flat memory model makevms.com => VMS build script SCOPTIONS.ppc => Used with smakefile.ppc Good luck, and happy coding. -Glenn Randers-Pehrson (current maintainer, since 1998) Internet: glennrp at users.sourceforge.net -Andreas Eric Dilger (former maintainer, 1996-1997) Internet: adilger at enel.ucalgary.ca Web: http://www-mddsp.enel.ucalgary.ca/People/adilger/ -Guy Eric Schalnat (original author and former maintainer, 1995-1996) (formerly of Group 42, Inc) Internet: gschal at infinet.com Indigo-indigo-1.2.3/third_party/libpng-src/include/000077500000000000000000000000001271037650300222155ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/libpng-src/include/png.h000066400000000000000000003527521271037650300231700ustar00rootroot00000000000000 /* png.h - header file for PNG reference library * * libpng version 1.4.4 - September 23, 2010 * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license (See LICENSE, below) * * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.4.4 - September 23, 2010: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: * * Due to various miscommunications, unforeseen code incompatibilities * and occasional factors outside the authors' control, version numbering * on the library has not always been consistent and straightforward. * The following table summarizes matters since version 0.89c, which was * the first widely used release: * * source png.h png.h shared-lib * version string int version * ------- ------ ----- ---------- * 0.89c "1.0 beta 3" 0.89 89 1.0.89 * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] * 0.97c 0.97 97 2.0.97 * 0.98 0.98 98 2.0.98 * 0.99 0.99 98 2.0.99 * 0.99a-m 0.99 99 2.0.99 * 1.00 1.00 100 2.1.0 [100 should be 10000] * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] * 1.0.1 png.h string is 10001 2.1.0 * 1.0.1a-e identical to the 10002 from here on, the shared library * 1.0.2 source version) 10002 is 2.V where V is the source code * 1.0.2a-b 10003 version, except as noted. * 1.0.3 10003 * 1.0.3a-d 10004 * 1.0.4 10004 * 1.0.4a-f 10005 * 1.0.5 (+ 2 patches) 10005 * 1.0.5a-d 10006 * 1.0.5e-r 10100 (not source compatible) * 1.0.5s-v 10006 (not binary compatible) * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) * 1.0.6d-f 10007 (still binary incompatible) * 1.0.6g 10007 * 1.0.6h 10007 10.6h (testing xy.z so-numbering) * 1.0.6i 10007 10.6i * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) * 1.0.7 1 10007 (still compatible) * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 * 1.0.8rc1 1 10008 2.1.0.8rc1 * 1.0.8 1 10008 2.1.0.8 * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 * 1.0.9rc1 1 10009 2.1.0.9rc1 * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 * 1.0.9rc2 1 10009 2.1.0.9rc2 * 1.0.9 1 10009 2.1.0.9 * 1.0.10beta1 1 10010 2.1.0.10beta1 * 1.0.10rc1 1 10010 2.1.0.10rc1 * 1.0.10 1 10010 2.1.0.10 * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 * 1.0.11rc1 1 10011 2.1.0.11rc1 * 1.0.11 1 10011 2.1.0.11 * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 * 1.0.12rc1 2 10012 2.1.0.12rc1 * 1.0.12 2 10012 2.1.0.12 * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 * 1.2.0rc1 3 10200 3.1.2.0rc1 * 1.2.0 3 10200 3.1.2.0 * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 * 1.2.1 3 10201 3.1.2.1 * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 * 1.0.13 10 10013 10.so.0.1.0.13 * 1.2.2 12 10202 12.so.0.1.2.2 * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 * 1.2.3 12 10203 12.so.0.1.2.3 * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 * 1.0.14 10 10014 10.so.0.1.0.14 * 1.2.4 13 10204 12.so.0.1.2.4 * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 * 1.0.15 10 10015 10.so.0.1.0.15 * 1.2.5 13 10205 12.so.0.1.2.5 * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 * 1.0.16 10 10016 10.so.0.1.0.16 * 1.2.6 13 10206 12.so.0.1.2.6 * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 * 1.0.17 10 10017 12.so.0.1.0.17 * 1.2.7 13 10207 12.so.0.1.2.7 * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 * 1.0.18 10 10018 12.so.0.1.0.18 * 1.2.8 13 10208 12.so.0.1.2.8 * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 * 1.2.9beta4-11 13 10209 12.so.0.9[.0] * 1.2.9rc1 13 10209 12.so.0.9[.0] * 1.2.9 13 10209 12.so.0.9[.0] * 1.2.10beta1-7 13 10210 12.so.0.10[.0] * 1.2.10rc1-2 13 10210 12.so.0.10[.0] * 1.2.10 13 10210 12.so.0.10[.0] * 1.4.0beta1-5 14 10400 14.so.0.0[.0] * 1.2.11beta1-4 13 10211 12.so.0.11[.0] * 1.4.0beta7-8 14 10400 14.so.0.0[.0] * 1.2.11 13 10211 12.so.0.11[.0] * 1.2.12 13 10212 12.so.0.12[.0] * 1.4.0beta9-14 14 10400 14.so.0.0[.0] * 1.2.13 13 10213 12.so.0.13[.0] * 1.4.0beta15-36 14 10400 14.so.0.0[.0] * 1.4.0beta37-87 14 10400 14.so.14.0[.0] * 1.4.0rc01 14 10400 14.so.14.0[.0] * 1.4.0beta88-109 14 10400 14.so.14.0[.0] * 1.4.0rc02-08 14 10400 14.so.14.0[.0] * 1.4.0 14 10400 14.so.14.0[.0] * 1.4.1beta01-03 14 10401 14.so.14.1[.0] * 1.4.1rc01 14 10401 14.so.14.1[.0] * 1.4.1beta04-12 14 10401 14.so.14.1[.0] * 1.4.1rc02-04 14 10401 14.so.14.1[.0] * 1.4.1 14 10401 14.so.14.1[.0] * 1.4.2beta01 14 10402 14.so.14.2[.0] * 1.4.2rc02-06 14 10402 14.so.14.2[.0] * 1.4.2 14 10402 14.so.14.2[.0] * 1.4.3beta01-05 14 10403 14.so.14.3[.0] * 1.4.3rc01-03 14 10403 14.so.14.3[.0] * 1.4.3 14 10403 14.so.14.3[.0] * 1.4.4beta01-08 14 10404 14.so.14.4[.0] * 1.4.4rc01-06 14 10404 14.so.14.4[.0] * * Henceforth the source version will match the shared-library major * and minor numbers; the shared-library major version number will be * used for changes in backward compatibility, as it is intended. The * PNG_LIBPNG_VER macro, which is not used within libpng but is available * for applications, is an unsigned integer of the form xyyzz corresponding * to the source version x.y.z (leading zeros in y and z). Beta versions * were given the previous public release number plus a letter, until * version 1.0.6j; from then on they were given the upcoming public * release number plus "betaNN" or "rcN". * * Binary incompatibility exists only when applications make direct access * to the info_ptr or png_ptr members through png.h, and the compiled * application is loaded with a different version of the library. * * DLLNUM will change each time there are forward or backward changes * in binary compatibility (e.g., when a new feature is added). * * See libpng.txt or libpng.3 for more information. The PNG specification * is available as a W3C Recommendation and as an ISO Specification, * defines should NOT be changed. */ #define PNG_INFO_gAMA 0x0001 #define PNG_INFO_sBIT 0x0002 #define PNG_INFO_cHRM 0x0004 #define PNG_INFO_PLTE 0x0008 #define PNG_INFO_tRNS 0x0010 #define PNG_INFO_bKGD 0x0020 #define PNG_INFO_hIST 0x0040 #define PNG_INFO_pHYs 0x0080 #define PNG_INFO_oFFs 0x0100 #define PNG_INFO_tIME 0x0200 #define PNG_INFO_pCAL 0x0400 #define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ #define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ #define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ #define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ #define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using * the routines for other purposes. */ typedef struct png_row_info_struct { png_uint_32 width; /* width of row */ png_size_t rowbytes; /* number of bytes in row */ png_byte color_type; /* color type of row */ png_byte bit_depth; /* bit depth of row */ png_byte channels; /* number of channels (1, 2, 3, or 4) */ png_byte pixel_depth; /* bits per pixel (depth * channels) */ } png_row_info; typedef png_row_info FAR * png_row_infop; typedef png_row_info FAR * FAR * png_row_infopp; /* These are the function types for the I/O functions and for the functions * that allow the user to override the default I/O functions with his or her * own. The png_error_ptr type should match that of user-supplied warning * and error functions, while the png_rw_ptr type should match that of the * user read/write data functions. */ typedef struct png_struct_def png_struct; typedef png_struct FAR * png_structp; typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, int)); typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, int)); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, png_uint_32, int)); #endif #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, png_row_infop, png_bytep)); #endif #ifdef PNG_USER_CHUNKS_SUPPORTED typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); #endif #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); #endif #ifdef PNG_SETJMP_SUPPORTED /* This must match the function definition in , and the * application must include this before png.h to obtain the definition * of jmp_buf. */ typedef void (PNGAPI *png_longjmp_ptr) PNGARG((jmp_buf, int)); #endif /* Transform masks for the high-level interface */ #define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ #define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ #define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ #define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ #define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ #define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ #define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ #define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ #define PNG_TRANSFORM_BGR 0x0080 /* read and write */ #define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ #define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ #define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ #define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ /* Added to libpng-1.2.34 */ #define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER #define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ /* Added to libpng-1.4.0 */ #define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ /* Flags for MNG supported features */ #define PNG_FLAG_MNG_EMPTY_PLTE 0x01 #define PNG_FLAG_MNG_FILTER_64 0x04 #define PNG_ALL_MNG_FEATURES 0x05 typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_alloc_size_t)); typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); /* The structure that holds the information to read and write PNG files. * The only people who need to care about what is inside of this are the * people who will be modifying the library for their own special needs. * It should NOT be accessed directly by an application, except to store * the jmp_buf. */ struct png_struct_def { #ifdef PNG_SETJMP_SUPPORTED jmp_buf jmpbuf PNG_DEPSTRUCT; /* used in png_error */ png_longjmp_ptr longjmp_fn PNG_DEPSTRUCT;/* setjmp non-local goto function. */ #endif png_error_ptr error_fn PNG_DEPSTRUCT; /* function for printing errors and aborting */ png_error_ptr warning_fn PNG_DEPSTRUCT; /* function for printing warnings */ png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for error functions */ png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing output data */ png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading input data */ png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct for I/O functions */ #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED png_user_transform_ptr read_user_transform_fn PNG_DEPSTRUCT; /* user read transform */ #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED png_user_transform_ptr write_user_transform_fn PNG_DEPSTRUCT; /* user write transform */ #endif /* These were added in libpng-1.0.2 */ #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) png_voidp user_transform_ptr PNG_DEPSTRUCT; /* user supplied struct for user transform */ png_byte user_transform_depth PNG_DEPSTRUCT; /* bit depth of user transformed pixels */ png_byte user_transform_channels PNG_DEPSTRUCT; /* channels in user transformed pixels */ #endif #endif png_uint_32 mode PNG_DEPSTRUCT; /* tells us where we are in the PNG file */ png_uint_32 flags PNG_DEPSTRUCT; /* flags indicating various things to libpng */ png_uint_32 transformations PNG_DEPSTRUCT; /* which transformations to perform */ z_stream zstream PNG_DEPSTRUCT; /* pointer to decompression structure (below) */ png_bytep zbuf PNG_DEPSTRUCT; /* buffer for zlib */ png_size_t zbuf_size PNG_DEPSTRUCT; /* size of zbuf */ int zlib_level PNG_DEPSTRUCT; /* holds zlib compression level */ int zlib_method PNG_DEPSTRUCT; /* holds zlib compression method */ int zlib_window_bits PNG_DEPSTRUCT; /* holds zlib compression window bits */ int zlib_mem_level PNG_DEPSTRUCT; /* holds zlib compression memory level */ int zlib_strategy PNG_DEPSTRUCT; /* holds zlib compression strategy */ png_uint_32 width PNG_DEPSTRUCT; /* width of image in pixels */ png_uint_32 height PNG_DEPSTRUCT; /* height of image in pixels */ png_uint_32 num_rows PNG_DEPSTRUCT; /* number of rows in current pass */ png_uint_32 usr_width PNG_DEPSTRUCT; /* width of row at start of write */ png_size_t rowbytes PNG_DEPSTRUCT; /* size of row in bytes */ #if 0 /* Replaced with the following in libpng-1.4.1 */ png_size_t irowbytes PNG_DEPSTRUCT; #endif /* Added in libpng-1.4.1 */ #ifdef PNG_USER_LIMITS_SUPPORTED /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk * can occupy when decompressed. 0 means unlimited. * We will change the typedef from png_size_t to png_alloc_size_t * in libpng-1.6.0 */ png_alloc_size_t user_chunk_malloc_max PNG_DEPSTRUCT; #endif png_uint_32 iwidth PNG_DEPSTRUCT; /* width of current interlaced row in pixels */ png_uint_32 row_number PNG_DEPSTRUCT; /* current row in interlace pass */ png_bytep prev_row PNG_DEPSTRUCT; /* buffer to save previous (unfiltered) row */ png_bytep row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ png_bytep sub_row PNG_DEPSTRUCT; /* buffer to save "sub" row when filtering */ png_bytep up_row PNG_DEPSTRUCT; /* buffer to save "up" row when filtering */ png_bytep avg_row PNG_DEPSTRUCT; /* buffer to save "avg" row when filtering */ png_bytep paeth_row PNG_DEPSTRUCT; /* buffer to save "Paeth" row when filtering */ png_row_info row_info PNG_DEPSTRUCT; /* used for transformation routines */ png_uint_32 idat_size PNG_DEPSTRUCT; /* current IDAT size for read */ png_uint_32 crc PNG_DEPSTRUCT; /* current chunk CRC value */ png_colorp palette PNG_DEPSTRUCT; /* palette from the input file */ png_uint_16 num_palette PNG_DEPSTRUCT; /* number of color entries in palette */ png_uint_16 num_trans PNG_DEPSTRUCT; /* number of transparency values */ png_byte chunk_name[5] PNG_DEPSTRUCT; /* null-terminated name of current chunk */ png_byte compression PNG_DEPSTRUCT; /* file compression type (always 0) */ png_byte filter PNG_DEPSTRUCT; /* file filter type (always 0) */ png_byte interlaced PNG_DEPSTRUCT; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ png_byte pass PNG_DEPSTRUCT; /* current interlace pass (0 - 6) */ png_byte do_filter PNG_DEPSTRUCT; /* row filter flags (see PNG_FILTER_ below ) */ png_byte color_type PNG_DEPSTRUCT; /* color type of file */ png_byte bit_depth PNG_DEPSTRUCT; /* bit depth of file */ png_byte usr_bit_depth PNG_DEPSTRUCT; /* bit depth of users row */ png_byte pixel_depth PNG_DEPSTRUCT; /* number of bits per pixel */ png_byte channels PNG_DEPSTRUCT; /* number of channels in file */ png_byte usr_channels PNG_DEPSTRUCT; /* channels at start of write */ png_byte sig_bytes PNG_DEPSTRUCT; /* magic bytes read/written from start of file */ #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) png_uint_16 filler PNG_DEPSTRUCT; /* filler bytes for pixel expansion */ #endif #ifdef PNG_bKGD_SUPPORTED png_byte background_gamma_type PNG_DEPSTRUCT; # ifdef PNG_FLOATING_POINT_SUPPORTED float background_gamma PNG_DEPSTRUCT; # endif png_color_16 background PNG_DEPSTRUCT; /* background color in screen gamma space */ #ifdef PNG_READ_GAMMA_SUPPORTED png_color_16 background_1 PNG_DEPSTRUCT; /* background normalized to gamma 1.0 */ #endif #endif /* PNG_bKGD_SUPPORTED */ #ifdef PNG_WRITE_FLUSH_SUPPORTED png_flush_ptr output_flush_fn PNG_DEPSTRUCT; /* Function for flushing output */ png_uint_32 flush_dist PNG_DEPSTRUCT; /* how many rows apart to flush, 0 - no flush */ png_uint_32 flush_rows PNG_DEPSTRUCT; /* number of rows written since last flush */ #endif #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) int gamma_shift PNG_DEPSTRUCT; /* number of "insignificant" bits 16-bit gamma */ #ifdef PNG_FLOATING_POINT_SUPPORTED float gamma PNG_DEPSTRUCT; /* file gamma value */ float screen_gamma PNG_DEPSTRUCT; /* screen gamma value (display_exponent) */ #endif #endif #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) png_bytep gamma_table PNG_DEPSTRUCT; /* gamma table for 8-bit depth files */ png_bytep gamma_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ png_bytep gamma_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ png_uint_16pp gamma_16_table PNG_DEPSTRUCT; /* gamma table for 16-bit depth files */ png_uint_16pp gamma_16_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ png_uint_16pp gamma_16_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ #endif #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) png_color_8 sig_bit PNG_DEPSTRUCT; /* significant bits in each available channel */ #endif #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) png_color_8 shift PNG_DEPSTRUCT; /* shift for significant bit tranformation */ #endif #if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) png_bytep trans_alpha PNG_DEPSTRUCT; /* alpha values for paletted files */ png_color_16 trans_color PNG_DEPSTRUCT; /* transparent color for non-paletted files */ #endif png_read_status_ptr read_row_fn PNG_DEPSTRUCT; /* called after each row is decoded */ png_write_status_ptr write_row_fn PNG_DEPSTRUCT; /* called after each row is encoded */ #ifdef PNG_PROGRESSIVE_READ_SUPPORTED png_progressive_info_ptr info_fn PNG_DEPSTRUCT; /* called after header data fully read */ png_progressive_row_ptr row_fn PNG_DEPSTRUCT; /* called after each prog. row is decoded */ png_progressive_end_ptr end_fn PNG_DEPSTRUCT; /* called after image is complete */ png_bytep save_buffer_ptr PNG_DEPSTRUCT; /* current location in save_buffer */ png_bytep save_buffer PNG_DEPSTRUCT; /* buffer for previously read data */ png_bytep current_buffer_ptr PNG_DEPSTRUCT; /* current location in current_buffer */ png_bytep current_buffer PNG_DEPSTRUCT; /* buffer for recently used data */ png_uint_32 push_length PNG_DEPSTRUCT; /* size of current input chunk */ png_uint_32 skip_length PNG_DEPSTRUCT; /* bytes to skip in input data */ png_size_t save_buffer_size PNG_DEPSTRUCT; /* amount of data now in save_buffer */ png_size_t save_buffer_max PNG_DEPSTRUCT; /* total size of save_buffer */ png_size_t buffer_size PNG_DEPSTRUCT; /* total amount of available input data */ png_size_t current_buffer_size PNG_DEPSTRUCT; /* amount of data now in current_buffer */ int process_mode PNG_DEPSTRUCT; /* what push library is currently doing */ int cur_palette PNG_DEPSTRUCT; /* current push library palette index */ # ifdef PNG_TEXT_SUPPORTED png_size_t current_text_size PNG_DEPSTRUCT; /* current size of text input data */ png_size_t current_text_left PNG_DEPSTRUCT; /* how much text left to read in input */ png_charp current_text PNG_DEPSTRUCT; /* current text chunk buffer */ png_charp current_text_ptr PNG_DEPSTRUCT; /* current location in current_text */ # endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ #if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) /* For the Borland special 64K segment handler */ png_bytepp offset_table_ptr PNG_DEPSTRUCT; png_bytep offset_table PNG_DEPSTRUCT; png_uint_16 offset_table_number PNG_DEPSTRUCT; png_uint_16 offset_table_count PNG_DEPSTRUCT; png_uint_16 offset_table_count_free PNG_DEPSTRUCT; #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED png_bytep palette_lookup PNG_DEPSTRUCT; /* lookup table for quantizing */ png_bytep quantize_index PNG_DEPSTRUCT; /* index translation for palette files */ #endif #if defined(PNG_READ_QUANTIZE_SUPPORTED) || defined(PNG_hIST_SUPPORTED) png_uint_16p hist PNG_DEPSTRUCT; /* histogram */ #endif #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED png_byte heuristic_method PNG_DEPSTRUCT; /* heuristic for row filter selection */ png_byte num_prev_filters PNG_DEPSTRUCT; /* number of weights for previous rows */ png_bytep prev_filters PNG_DEPSTRUCT; /* filter type(s) of previous row(s) */ png_uint_16p filter_weights PNG_DEPSTRUCT; /* weight(s) for previous line(s) */ png_uint_16p inv_filter_weights PNG_DEPSTRUCT; /* 1/weight(s) for previous line(s) */ png_uint_16p filter_costs PNG_DEPSTRUCT; /* relative filter calculation cost */ png_uint_16p inv_filter_costs PNG_DEPSTRUCT; /* 1/relative filter calculation cost */ #endif #ifdef PNG_TIME_RFC1123_SUPPORTED png_charp time_buffer PNG_DEPSTRUCT; /* String to hold RFC 1123 time text */ #endif /* New members added in libpng-1.0.6 */ png_uint_32 free_me PNG_DEPSTRUCT; /* flags items libpng is responsible for freeing */ #ifdef PNG_USER_CHUNKS_SUPPORTED png_voidp user_chunk_ptr PNG_DEPSTRUCT; png_user_chunk_ptr read_user_chunk_fn PNG_DEPSTRUCT; /* user read chunk handler */ #endif #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED int num_chunk_list PNG_DEPSTRUCT; png_bytep chunk_list PNG_DEPSTRUCT; #endif /* New members added in libpng-1.0.3 */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte rgb_to_gray_status PNG_DEPSTRUCT; /* These were changed from png_byte in libpng-1.0.6 */ png_uint_16 rgb_to_gray_red_coeff PNG_DEPSTRUCT; png_uint_16 rgb_to_gray_green_coeff PNG_DEPSTRUCT; png_uint_16 rgb_to_gray_blue_coeff PNG_DEPSTRUCT; #endif /* New member added in libpng-1.0.4 (renamed in 1.0.9) */ #if defined(PNG_MNG_FEATURES_SUPPORTED) || \ defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) /* Changed from png_byte to png_uint_32 at version 1.2.0 */ png_uint_32 mng_features_permitted PNG_DEPSTRUCT; #endif /* New member added in libpng-1.0.7 */ #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) png_fixed_point int_gamma PNG_DEPSTRUCT; #endif /* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ #ifdef PNG_MNG_FEATURES_SUPPORTED png_byte filter_type PNG_DEPSTRUCT; #endif /* New members added in libpng-1.2.0 */ /* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ #ifdef PNG_USER_MEM_SUPPORTED png_voidp mem_ptr PNG_DEPSTRUCT; /* user supplied struct for mem functions */ png_malloc_ptr malloc_fn PNG_DEPSTRUCT; /* function for allocating memory */ png_free_ptr free_fn PNG_DEPSTRUCT; /* function for freeing memory */ #endif /* New member added in libpng-1.0.13 and 1.2.0 */ png_bytep big_row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ png_bytep quantize_sort PNG_DEPSTRUCT; /* working sort array */ png_bytep index_to_palette PNG_DEPSTRUCT; /* where the original index currently is in the palette */ png_bytep palette_to_index PNG_DEPSTRUCT; /* which original index points to this palette color */ #endif /* New members added in libpng-1.0.16 and 1.2.6 */ png_byte compression_type PNG_DEPSTRUCT; #ifdef PNG_USER_LIMITS_SUPPORTED png_uint_32 user_width_max PNG_DEPSTRUCT; png_uint_32 user_height_max PNG_DEPSTRUCT; /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown * chunks that can be stored (0 means unlimited). */ png_uint_32 user_chunk_cache_max PNG_DEPSTRUCT; #endif /* New member added in libpng-1.0.25 and 1.2.17 */ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED /* Storage for unknown chunk that the library doesn't recognize. */ png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; #endif /* New members added in libpng-1.2.26 */ png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; png_uint_32 old_prev_row_size PNG_DEPSTRUCT; /* New member added in libpng-1.2.30 */ png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ #ifdef PNG_IO_STATE_SUPPORTED /* New member added in libpng-1.4.0 */ png_uint_32 io_state PNG_DEPSTRUCT; #endif }; /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ typedef png_structp version_1_4_4; typedef png_struct FAR * FAR * png_structpp; /* Here are the function definitions most commonly used. This is not * the place to find out how to use libpng. See libpng.txt for the * full explanation, see example.c for the summary. This just provides * a simple one line description of the use of each function. */ /* Returns the version number of the library */ PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); /* Tell lib we have already handled the first magic bytes. * Handling more than 8 bytes from the beginning of the file is an error. */ PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, int num_bytes)); /* Check sig[start] through sig[start + num_to_check - 1] to see if it's a * PNG file. Returns zero if the supplied bytes match the 8-byte PNG * signature, and non-zero otherwise. Having num_to_check == 0 or * start > 7 will always fail (ie return non-zero). */ PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, png_size_t num_to_check)); /* Simple signature checking function. This is the same as calling * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). */ #define png_check_sig(sig,n) !png_sig_cmp((sig), 0, (n)) /* Allocate and initialize png_ptr struct for reading, and any other memory. */ PNG_EXPORT(png_structp,png_create_read_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; /* Allocate and initialize png_ptr struct for writing, and any other memory */ PNG_EXPORT(png_structp,png_create_write_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; PNG_EXPORT(png_size_t,png_get_compression_buffer_size) PNGARG((png_structp png_ptr)); PNG_EXPORT(void,png_set_compression_buffer_size) PNGARG((png_structp png_ptr, png_size_t size)); /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp * match up. */ #ifdef PNG_SETJMP_SUPPORTED /* This function returns the jmp_buf built in to *png_ptr. It must be * supplied with an appropriate 'longjmp' function to use on that jmp_buf * unless the default error function is overridden in which case NULL is * acceptable. The size of the jmp_buf is checked against the actual size * allocated by the library - the call will return NULL on a mismatch * indicating an ABI mismatch. */ PNG_EXPORT(jmp_buf*, png_set_longjmp_fn) PNGARG((png_structp png_ptr, png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); # define png_jmpbuf(png_ptr) \ (*png_set_longjmp_fn((png_ptr), longjmp, sizeof (jmp_buf))) #else # define png_jmpbuf(png_ptr) \ (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) #endif #ifdef PNG_READ_SUPPORTED /* Reset the compression stream */ PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); #endif /* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ #ifdef PNG_USER_MEM_SUPPORTED PNG_EXPORT(png_structp,png_create_read_struct_2) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; PNG_EXPORT(png_structp,png_create_write_struct_2) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; #endif /* Write the PNG file signature. */ PNG_EXPORT(void,png_write_sig) PNGARG((png_structp png_ptr)); /* Write a PNG chunk - size, type, (optional) data, CRC. */ PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, png_bytep chunk_name, png_bytep data, png_size_t length)); /* Write the start of a PNG chunk - length and chunk name. */ PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, png_bytep chunk_name, png_uint_32 length)); /* Write the data of a PNG chunk started with png_write_chunk_start(). */ PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); /* Finish a chunk started with png_write_chunk_start() (includes CRC). */ PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); /* Allocate and initialize the info structure */ PNG_EXPORT(png_infop,png_create_info_struct) PNGARG((png_structp png_ptr)) PNG_ALLOCATED; PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, png_size_t png_info_struct_size)); /* Writes all the PNG information before the image. */ PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the information before the actual image data. */ PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif #ifdef PNG_TIME_RFC1123_SUPPORTED PNG_EXPORT(png_charp,png_convert_to_rfc1123) PNGARG((png_structp png_ptr, png_timep ptime)); #endif #ifdef PNG_CONVERT_tIME_SUPPORTED /* Convert from a struct tm to png_time */ PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, struct tm FAR * ttime)); /* Convert from time_t to png_time. Uses gmtime() */ PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, time_t ttime)); #endif /* PNG_CONVERT_tIME_SUPPORTED */ #ifdef PNG_READ_EXPAND_SUPPORTED /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) /* Use blue, green, red order for pixels. */ PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Expand the grayscale to 24-bit RGB if necessary. */ PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB to grayscale. */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, int error_action, double red, double green )); #endif PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, int error_action, png_fixed_point red, png_fixed_point green )); PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp png_ptr)); #endif PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, png_colorp palette)); #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) /* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, png_uint_32 filler, int flags)); /* The values of the PNG_FILLER_ defines should NOT be changed */ #define PNG_FILLER_BEFORE 0 #define PNG_FILLER_AFTER 1 /* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, png_uint_32 filler, int flags)); #endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Swap bytes in 16-bit depth files. */ PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) /* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ defined(PNG_WRITE_PACKSWAP_SUPPORTED) /* Swap packing order of pixels in bytes. */ PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) /* Converts files to legal bit depths. */ PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, png_color_8p true_bits)); #endif #if defined(PNG_READ_INTERLACING_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) /* Have the code handle the interlacing. Returns the number of passes. */ PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) /* Invert monochrome files */ PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_READ_BACKGROUND_SUPPORTED /* Handle alpha and tRNS by replacing with a background color. */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, png_color_16p background_color, int background_gamma_code, int need_expand, double background_gamma)); #endif #define PNG_BACKGROUND_GAMMA_UNKNOWN 0 #define PNG_BACKGROUND_GAMMA_SCREEN 1 #define PNG_BACKGROUND_GAMMA_FILE 2 #define PNG_BACKGROUND_GAMMA_UNIQUE 3 #endif #ifdef PNG_READ_16_TO_8_SUPPORTED /* Strip the second byte of information from a 16-bit depth file. */ PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED /* Turn on quantizing, and reduce the palette to the number of colors * available. Prior to libpng-1.4.2, this was png_set_dither(). */ PNG_EXPORT(void,png_set_quantize) PNGARG((png_structp png_ptr, png_colorp palette, int num_palette, int maximum_colors, png_uint_16p histogram, int full_quantize)); #endif /* This migration aid will be removed from libpng-1.5.0 */ #define png_set_dither png_set_quantize #ifdef PNG_READ_GAMMA_SUPPORTED /* Handle gamma correction. Screen_gamma=(display_exponent) */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, double screen_gamma, double default_file_gamma)); #endif #endif #ifdef PNG_WRITE_FLUSH_SUPPORTED /* Set how many lines between output flushes - 0 for no flushing */ PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); /* Flush the current PNG output buffer */ PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); #endif /* Optional update palette with requested transformations */ PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); /* Optional call to update the users info structure */ PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read one or more rows of image data. */ PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); #endif #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read a row of data. */ PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, png_bytep row, png_bytep display_row)); #endif #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the whole image into memory at once. */ PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, png_bytepp image)); #endif /* Write a row of image data */ PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, png_bytep row)); /* Write a few rows of image data */ PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, png_bytepp row, png_uint_32 num_rows)); /* Write the image data */ PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, png_bytepp image)); /* Write the end of the PNG file. */ PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the end of the PNG file. */ PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif /* Free any memory associated with the png_info_struct */ PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, png_infopp info_ptr_ptr)); /* Free any memory associated with the png_struct and the png_info_structs */ PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); /* Free any memory associated with the png_struct and the png_info_structs */ PNG_EXPORT(void,png_destroy_write_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); /* Set the libpng method of handling chunk CRC errors */ PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, int crit_action, int ancil_action)); /* Values for png_set_crc_action() to say how to handle CRC errors in * ancillary and critical chunks, and whether to use the data contained * therein. Note that it is impossible to "discard" data in a critical * chunk. For versions prior to 0.90, the action was always error/quit, * whereas in version 0.90 and later, the action for CRC errors in ancillary * chunks is warn/discard. These values should NOT be changed. * * value action:critical action:ancillary */ #define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ #define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ #define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ #define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ #define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ #define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ /* These functions give the user control over the scan-line filtering in * libpng and the compression methods used by zlib. These functions are * mainly useful for testing, as the defaults should work with most users. * Those users who are tight on memory or want faster performance at the * expense of compression can modify them. See the compression library * header file (zlib.h) for an explination of the compression functions. */ /* Set the filtering method(s) used by libpng. Currently, the only valid * value for "method" is 0. */ PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, int filters)); /* Flags for png_set_filter() to say which filters to use. The flags * are chosen so that they don't conflict with real filter types * below, in case they are supplied instead of the #defined constants. * These values should NOT be changed. */ #define PNG_NO_FILTERS 0x00 #define PNG_FILTER_NONE 0x08 #define PNG_FILTER_SUB 0x10 #define PNG_FILTER_UP 0x20 #define PNG_FILTER_AVG 0x40 #define PNG_FILTER_PAETH 0x80 #define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ PNG_FILTER_AVG | PNG_FILTER_PAETH) /* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. * These defines should NOT be changed. */ #define PNG_FILTER_VALUE_NONE 0 #define PNG_FILTER_VALUE_SUB 1 #define PNG_FILTER_VALUE_UP 2 #define PNG_FILTER_VALUE_AVG 3 #define PNG_FILTER_VALUE_PAETH 4 #define PNG_FILTER_VALUE_LAST 5 #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ /* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ * defines, either the default (minimum-sum-of-absolute-differences), or * the experimental method (weighted-minimum-sum-of-absolute-differences). * * Weights are factors >= 1.0, indicating how important it is to keep the * filter type consistent between rows. Larger numbers mean the current * filter is that many times as likely to be the same as the "num_weights" * previous filters. This is cumulative for each previous row with a weight. * There needs to be "num_weights" values in "filter_weights", or it can be * NULL if the weights aren't being specified. Weights have no influence on * the selection of the first row filter. Well chosen weights can (in theory) * improve the compression for a given image. * * Costs are factors >= 1.0 indicating the relative decoding costs of a * filter type. Higher costs indicate more decoding expense, and are * therefore less likely to be selected over a filter with lower computational * costs. There needs to be a value in "filter_costs" for each valid filter * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't * setting the costs. Costs try to improve the speed of decompression without * unduly increasing the compressed image size. * * A negative weight or cost indicates the default value is to be used, and * values in the range [0.0, 1.0) indicate the value is to remain unchanged. * The default values for both weights and costs are currently 1.0, but may * change if good general weighting/cost heuristics can be found. If both * the weights and costs are set to 1.0, this degenerates the WEIGHTED method * to the UNWEIGHTED method, but with added encoding time/computation. */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, int heuristic_method, int num_weights, png_doublep filter_weights, png_doublep filter_costs)); #endif #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ /* Heuristic used for row filter selection. These defines should NOT be * changed. */ #define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ #define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ #define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ #define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ /* Set the library compression level. Currently, valid values range from * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 * (0 - no compression, 9 - "maximal" compression). Note that tests have * shown that zlib compression levels 3-6 usually perform as well as level 9 * for PNG images, and do considerably fewer caclulations. In the future, * these values may not correspond directly to the zlib compression levels. */ PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, int level)); PNG_EXPORT(void,png_set_compression_mem_level) PNGARG((png_structp png_ptr, int mem_level)); PNG_EXPORT(void,png_set_compression_strategy) PNGARG((png_structp png_ptr, int strategy)); PNG_EXPORT(void,png_set_compression_window_bits) PNGARG((png_structp png_ptr, int window_bits)); PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, int method)); /* These next functions are called for input/output, memory, and error * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, * and call standard C I/O routines such as fread(), fwrite(), and * fprintf(). These functions can be made to use other I/O routines * at run time for those applications that need to handle I/O in a * different manner by calling png_set_???_fn(). See libpng.txt for * more information. */ #ifdef PNG_STDIO_SUPPORTED /* Initialize the input/output for the PNG file to the default functions. */ PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); #endif /* Replace the (error and abort), and warning functions with user * supplied functions. If no messages are to be printed you must still * write and use replacement functions. The replacement error_fn should * still do a longjmp to the last setjmp location if you are using this * method of error handling. If error_fn or warning_fn is NULL, the * default function will be used. */ PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); /* Return the user pointer associated with the error functions */ PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); /* Replace the default data output functions with a user supplied one(s). * If buffered output is not used, then output_flush_fn can be set to NULL. * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time * output_flush_fn will be ignored (and thus can be NULL). * It is probably a mistake to use NULL for output_flush_fn if * write_data_fn is not also NULL unless you have built libpng with * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's * default flush function, which uses the standard *FILE structure, will * be used. */ PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); /* Replace the default data input function with a user supplied one. */ PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn)); /* Return the user pointer associated with the I/O functions */ PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, png_read_status_ptr read_row_fn)); PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, png_write_status_ptr write_row_fn)); #ifdef PNG_USER_MEM_SUPPORTED /* Replace the default memory allocation functions with user supplied one(s). */ PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); /* Return the user pointer associated with the memory functions */ PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp png_ptr, png_user_transform_ptr read_user_transform_fn)); #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp png_ptr, png_user_transform_ptr write_user_transform_fn)); #endif #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp png_ptr, png_voidp user_transform_ptr, int user_transform_depth, int user_transform_channels)); /* Return the user pointer associated with the user transform functions */ PNG_EXPORT(png_voidp,png_get_user_transform_ptr) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_USER_CHUNKS_SUPPORTED PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp png_ptr)); #endif #ifdef PNG_PROGRESSIVE_READ_SUPPORTED /* Sets the function callbacks for the push reader, and a pointer to a * user-defined structure available to the callback functions. */ PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); /* Returns the user pointer associated with the push read functions */ PNG_EXPORT(png_voidp,png_get_progressive_ptr) PNGARG((png_structp png_ptr)); /* Function to be called when data becomes available */ PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); /* Function that combines rows. Not very much different than the * png_combine_row() call. Is this even used????? */ PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, png_bytep old_row, png_bytep new_row)); #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, png_alloc_size_t size)) PNG_ALLOCATED; /* Added at libpng version 1.4.0 */ PNG_EXPORT(png_voidp,png_calloc) PNGARG((png_structp png_ptr, png_alloc_size_t size)) PNG_ALLOCATED; /* Added at libpng version 1.2.4 */ PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, png_alloc_size_t size)) PNG_ALLOCATED; /* Frees a pointer allocated by png_malloc() */ PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); /* Free data that was allocated internally */ PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 free_me, int num)); /* Reassign responsibility for freeing existing data, whether allocated * by libpng or by the application */ PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, png_infop info_ptr, int freer, png_uint_32 mask)); /* Assignments for png_data_freer */ #define PNG_DESTROY_WILL_FREE_DATA 1 #define PNG_SET_WILL_FREE_DATA 1 #define PNG_USER_WILL_FREE_DATA 2 /* Flags for png_ptr->free_me and info_ptr->free_me */ #define PNG_FREE_HIST 0x0008 #define PNG_FREE_ICCP 0x0010 #define PNG_FREE_SPLT 0x0020 #define PNG_FREE_ROWS 0x0040 #define PNG_FREE_PCAL 0x0080 #define PNG_FREE_SCAL 0x0100 #define PNG_FREE_UNKN 0x0200 #define PNG_FREE_LIST 0x0400 #define PNG_FREE_PLTE 0x1000 #define PNG_FREE_TRNS 0x2000 #define PNG_FREE_TEXT 0x4000 #define PNG_FREE_ALL 0x7fff #define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ #ifdef PNG_USER_MEM_SUPPORTED PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, png_alloc_size_t size)) PNG_ALLOCATED; PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, png_voidp ptr)); #endif #ifndef PNG_NO_ERROR_TEXT /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, png_const_charp error_message)) PNG_NORETURN; /* The same, but the chunk name is prepended to the error string. */ PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, png_const_charp error_message)) PNG_NORETURN; #else /* Fatal error in PNG image of libpng - can't continue */ PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)) PNG_NORETURN; #endif /* Non-fatal error in libpng. Can continue, but may have a problem. */ PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, png_const_charp warning_message)); /* Non-fatal error in libpng, chunk name is prepended to message. */ PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, png_const_charp warning_message)); #ifdef PNG_BENIGN_ERRORS_SUPPORTED /* Benign error in libpng. Can continue, but may have a problem. * User can choose whether to handle as a fatal error or as a warning. */ PNG_EXPORT(void,png_benign_error) PNGARG((png_structp png_ptr, png_const_charp warning_message)); /* Same, chunk name is prepended to message. */ PNG_EXPORT(void,png_chunk_benign_error) PNGARG((png_structp png_ptr, png_const_charp warning_message)); PNG_EXPORT(void,png_set_benign_errors) PNGARG((png_structp png_ptr, int allowed)); #endif /* The png_set_ functions are for storing values in the png_info_struct. * Similarly, the png_get_ calls are used to read values from the * png_info_struct, either storing the parameters in the passed variables, or * setting pointers into the png_info_struct where the data is stored. The * png_get_ functions return a non-zero value if the data was available * in info_ptr, or return zero and do not change any of the parameters if the * data was not available. * * These functions should be used instead of directly accessing png_info * to avoid problems with future changes in the size and internal layout of * png_info_struct. */ /* Returns "flag" if chunk data is valid in info_ptr. */ PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 flag)); /* Returns number of bytes needed to hold a transformed row. */ PNG_EXPORT(png_size_t,png_get_rowbytes) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_INFO_IMAGE_SUPPORTED /* Returns row_pointers, which is an array of pointers to scanlines that was * returned from png_read_png(). */ PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Set row_pointers, which is an array of pointers to scanlines for use * by png_write_png(). */ PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers)); #endif /* Returns number of color channels in image. */ PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_EASY_ACCESS_SUPPORTED /* Returns image width in pixels. */ PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image height in pixels. */ PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image bit_depth. */ PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image color_type. */ PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image filter_type. */ PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image interlace_type. */ PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image compression_type. */ PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns image resolution in pixels per meter, from pHYs chunk data. */ PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Returns pixel aspect ratio, computed from pHYs chunk data. */ #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif /* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif /* PNG_EASY_ACCESS_SUPPORTED */ /* Returns pointer to signature string read from PNG header */ PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_bKGD_SUPPORTED PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, png_infop info_ptr, png_color_16p *background)); #endif #ifdef PNG_bKGD_SUPPORTED PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, png_infop info_ptr, png_color_16p background)); #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, png_infop info_ptr, double *white_x, double *white_y, double *red_x, double *red_y, double *green_x, double *green_y, double *blue_x, double *blue_y)); #endif #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)); #endif #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, png_infop info_ptr, double white_x, double white_y, double red_x, double red_y, double green_x, double green_y, double blue_x, double blue_y)); #endif #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, png_fixed_point int_blue_y)); #endif #endif #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, png_infop info_ptr, double *file_gamma)); #endif PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, png_infop info_ptr, png_fixed_point *int_file_gamma)); #endif #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, png_infop info_ptr, double file_gamma)); #endif PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, png_infop info_ptr, png_fixed_point int_file_gamma)); #endif #ifdef PNG_hIST_SUPPORTED PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist)); #endif #ifdef PNG_hIST_SUPPORTED PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_16p hist)); #endif PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method)); PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_method, int compression_method, int filter_method)); #ifdef PNG_oFFs_SUPPORTED PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)); #endif #ifdef PNG_oFFs_SUPPORTED PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, int unit_type)); #endif #ifdef PNG_pCAL_SUPPORTED PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, png_charp *units, png_charpp *params)); #endif #ifdef PNG_pCAL_SUPPORTED PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)); #endif #ifdef PNG_pHYs_SUPPORTED PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); #endif #ifdef PNG_pHYs_SUPPORTED PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); #endif PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, png_infop info_ptr, png_colorp *palette, int *num_palette)); PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, png_infop info_ptr, png_colorp palette, int num_palette)); #ifdef PNG_sBIT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit)); #endif #ifdef PNG_sBIT_SUPPORTED PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, png_infop info_ptr, png_color_8p sig_bit)); #endif #ifdef PNG_sRGB_SUPPORTED PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, png_infop info_ptr, int *intent)); #endif #ifdef PNG_sRGB_SUPPORTED PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, png_infop info_ptr, int intent)); PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, png_infop info_ptr, int intent)); #endif #ifdef PNG_iCCP_SUPPORTED PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, png_infop info_ptr, png_charpp name, int *compression_type, png_charpp profile, png_uint_32 *proflen)); /* Note to maintainer: profile should be png_bytepp */ #endif #ifdef PNG_iCCP_SUPPORTED PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, png_infop info_ptr, png_charp name, int compression_type, png_charp profile, png_uint_32 proflen)); /* Note to maintainer: profile should be png_bytep */ #endif #ifdef PNG_sPLT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, png_infop info_ptr, png_sPLT_tpp entries)); #endif #ifdef PNG_sPLT_SUPPORTED PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, png_infop info_ptr, png_sPLT_tp entries, int nentries)); #endif #ifdef PNG_TEXT_SUPPORTED /* png_get_text also returns the number of text chunks in *num_text */ PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, int *num_text)); #endif /* Note while png_set_text() will accept a structure whose text, * language, and translated keywords are NULL pointers, the structure * returned by png_get_text will always contain regular * zero-terminated C strings. They might be empty strings but * they will never be NULL pointers. */ #ifdef PNG_TEXT_SUPPORTED PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, int num_text)); #endif #ifdef PNG_tIME_SUPPORTED PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, png_infop info_ptr, png_timep *mod_time)); #endif #ifdef PNG_tIME_SUPPORTED PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, png_infop info_ptr, png_timep mod_time)); #endif #ifdef PNG_tRNS_SUPPORTED PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, png_infop info_ptr, png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)); #endif #ifdef PNG_tRNS_SUPPORTED PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, png_infop info_ptr, png_bytep trans_alpha, int num_trans, png_color_16p trans_color)); #endif #ifdef PNG_tRNS_SUPPORTED #endif #ifdef PNG_sCAL_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, png_infop info_ptr, int *unit, double *width, double *height)); #else #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); #endif #endif #endif /* PNG_sCAL_SUPPORTED */ #ifdef PNG_sCAL_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, png_infop info_ptr, int unit, double width, double height)); #else #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); #endif #endif #endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED /* Provide a list of chunks and how they are to be handled, if the built-in handling or default unknown chunk handling is not desired. Any chunks not listed will be handled in the default manner. The IHDR and IEND chunks must not be listed. keep = 0: follow default behaviour = 1: do not keep = 2: keep only if safe-to-copy = 3: keep even if unsafe-to-copy */ PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp png_ptr, int keep, png_bytep chunk_list, int num_chunks)); PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep chunk_name)); #endif #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); PNG_EXPORT(void, png_set_unknown_chunk_location) PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); #endif /* Png_free_data() will turn off the "valid" flag for anything it frees. * If you need to turn it off for a chunk that your application has freed, * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, png_infop info_ptr, int mask)); #ifdef PNG_INFO_IMAGE_SUPPORTED /* The "params" pointer is currently not used and is for future expansion. */ PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, png_infop info_ptr, int transforms, png_voidp params)); PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, png_infop info_ptr, int transforms, png_voidp params)); #endif PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); #ifdef PNG_MNG_FEATURES_SUPPORTED PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp png_ptr, png_uint_32 mng_features_permitted)); #endif /* For use in png_set_keep_unknown, added to version 1.2.6 */ #define PNG_HANDLE_CHUNK_AS_DEFAULT 0 #define PNG_HANDLE_CHUNK_NEVER 1 #define PNG_HANDLE_CHUNK_IF_SAFE 2 #define PNG_HANDLE_CHUNK_ALWAYS 3 /* Strip the prepended error numbers ("#nnn ") from error and warning * messages before passing them to the error or warning handler. */ #ifdef PNG_ERROR_NUMBERS_SUPPORTED PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp png_ptr, png_uint_32 strip_mode)); #endif /* Added in libpng-1.2.6 */ #ifdef PNG_SET_USER_LIMITS_SUPPORTED PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp png_ptr)); PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp png_ptr)); /* Added in libpng-1.4.0 */ PNG_EXPORT(void,png_set_chunk_cache_max) PNGARG((png_structp png_ptr, png_uint_32 user_chunk_cache_max)); PNG_EXPORT(png_uint_32,png_get_chunk_cache_max) PNGARG((png_structp png_ptr)); /* Added in libpng-1.4.1 */ PNG_EXPORT(void,png_set_chunk_malloc_max) PNGARG((png_structp png_ptr, png_alloc_size_t user_chunk_cache_max)); PNG_EXPORT(png_alloc_size_t,png_get_chunk_malloc_max) PNGARG((png_structp png_ptr)); #endif #if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) PNG_EXPORT(png_uint_32,png_get_pixels_per_inch) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_uint_32,png_get_x_pixels_per_inch) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(png_uint_32,png_get_y_pixels_per_inch) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(float,png_get_x_offset_inches) PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXPORT(float,png_get_y_offset_inches) PNGARG((png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_pHYs_SUPPORTED PNG_EXPORT(png_uint_32,png_get_pHYs_dpi) PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); #endif /* PNG_pHYs_SUPPORTED */ #endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ /* Added in libpng-1.4.0 */ #ifdef PNG_IO_STATE_SUPPORTED PNG_EXPORT(png_uint_32,png_get_io_state) PNGARG((png_structp png_ptr)); PNG_EXPORT(png_bytep,png_get_io_chunk_name) PNGARG((png_structp png_ptr)); /* The flags returned by png_get_io_state() are the following: */ #define PNG_IO_NONE 0x0000 /* no I/O at this moment */ #define PNG_IO_READING 0x0001 /* currently reading */ #define PNG_IO_WRITING 0x0002 /* currently writing */ #define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ #define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ #define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ #define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ #define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ #define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ #endif /* ?PNG_IO_STATE_SUPPORTED */ /* Maintainer: Put new public prototypes here ^, in libpng.3, and project * defs */ #ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED /* With these routines we avoid an integer divide, which will be slower on * most machines. However, it does take more operations than the corresponding * divide method, so it may be slower on a few RISC systems. There are two * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. * * Note that the rounding factors are NOT supposed to be the same! 128 and * 32768 are correct for the NODIV code; 127 and 32767 are correct for the * standard method. * * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] */ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ # define png_composite(composite, fg, alpha, bg) \ { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ * (png_uint_16)(alpha) \ + (png_uint_16)(bg)*(png_uint_16)(255 \ - (png_uint_16)(alpha)) + (png_uint_16)128); \ (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } # define png_composite_16(composite, fg, alpha, bg) \ { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ * (png_uint_32)(alpha) \ + (png_uint_32)(bg)*(png_uint_32)(65535L \ - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } #else /* Standard method using integer division */ # define png_composite(composite, fg, alpha, bg) \ (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ (png_uint_16)127) / 255) # define png_composite_16(composite, fg, alpha, bg) \ (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ (png_uint_32)32767) / (png_uint_32)65535L) #endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ #ifdef PNG_USE_READ_MACROS /* Inline macros to do direct reads of bytes from the input buffer. * The png_get_int_32() routine assumes we are using two's complement * format for negative values, which is almost certainly true. */ # define png_get_uint_32(buf) \ (((png_uint_32)(*(buf)) << 24) + \ ((png_uint_32)(*((buf) + 1)) << 16) + \ ((png_uint_32)(*((buf) + 2)) << 8) + \ ((png_uint_32)(*((buf) + 3)))) # define png_get_uint_16(buf) \ (((png_uint_32)(*(buf)) << 8) + \ ((png_uint_32)(*((buf) + 1)))) # define png_get_int_32(buf) \ ((png_int_32)((*(buf) & 0x80) \ ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffff)+1)) \ : (png_int_32)png_get_uint_32(buf))) #else PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); #ifdef PNG_GET_INT_32_SUPPORTED PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); #endif #endif PNG_EXPORT(png_uint_32,png_get_uint_31) PNGARG((png_structp png_ptr, png_bytep buf)); /* No png_get_int_16 -- may be added if there's a real need for it. */ /* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ PNG_EXPORT(void,png_save_uint_32) PNGARG((png_bytep buf, png_uint_32 i)); PNG_EXPORT(void,png_save_int_32) PNGARG((png_bytep buf, png_int_32 i)); /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, * just to avoid potential problems on pre-ANSI C compilers. */ PNG_EXPORT(void,png_save_uint_16) PNGARG((png_bytep buf, unsigned int i)); /* No png_save_int_16 -- may be added if there's a real need for it. */ /* ************************************************************************* */ /* Various modes of operation. Note that after an init, mode is set to * zero automatically when the structure is created. */ #define PNG_HAVE_IHDR 0x01 #define PNG_HAVE_PLTE 0x02 #define PNG_HAVE_IDAT 0x04 #define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ #define PNG_HAVE_IEND 0x10 #define PNG_HAVE_gAMA 0x20 #define PNG_HAVE_cHRM 0x40 #ifdef __cplusplus } #endif #endif /* PNG_VERSION_INFO_ONLY */ /* Do not put anything past this line */ #endif /* PNG_H */ Indigo-indigo-1.2.3/third_party/libpng-src/include/pngconf.h000066400000000000000000001354271271037650300240340ustar00rootroot00000000000000 /* pngconf.h - machine configurable file for libpng * * libpng version 1.4.4 - September 23, 2010 * For conditions of distribution and use, see copyright notice in png.h * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * */ /* Any machine specific code is near the front of this file, so if you * are configuring libpng for a machine, you may want to read the section * starting here down to where it starts to typedef png_color, png_text, * and png_info. */ #ifndef PNGCONF_H #define PNGCONF_H #ifndef PNG_NO_LIMITS_H # include #endif /* Added at libpng-1.2.9 */ /* config.h is created by and PNG_CONFIGURE_LIBPNG is set by the "configure" * script. */ #ifdef PNG_CONFIGURE_LIBPNG # ifdef HAVE_CONFIG_H # include "config.h" # endif #endif /* * Added at libpng-1.2.8 * * PNG_USER_CONFIG has to be defined on the compiler command line. This * includes the resource compiler for Windows DLL configurations. */ #ifdef PNG_USER_CONFIG # include "pngusr.h" # ifndef PNG_USER_PRIVATEBUILD # define PNG_USER_PRIVATEBUILD # endif #endif /* * If you create a private DLL you should define in "pngusr.h" the following: * #define PNG_USER_PRIVATEBUILD * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." * #define PNG_USER_DLLFNAME_POSTFIX * e.g. // private DLL "libpng14gx.dll" * #define PNG_USER_DLLFNAME_POSTFIX "gx" * * The following macros are also at your disposal if you want to complete the * DLL VERSIONINFO structure. * - PNG_USER_VERSIONINFO_COMMENTS * - PNG_USER_VERSIONINFO_COMPANYNAME * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS */ #ifdef __STDC__ # ifdef SPECIALBUILD # pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") # endif # ifdef PRIVATEBUILD # pragma message("PRIVATEBUILD is deprecated.\ Use PNG_USER_PRIVATEBUILD instead.") # define PNG_USER_PRIVATEBUILD PRIVATEBUILD # endif #endif /* __STDC__ */ /* End of material added to libpng-1.2.8 */ #ifndef PNG_VERSION_INFO_ONLY /* This is the size of the compression buffer, and thus the size of * an IDAT chunk. Make this whatever size you feel is best for your * machine. One of these will be allocated per png_struct. When this * is full, it writes the data to the disk, and does some other * calculations. Making this an extremely small size will slow * the library down, but you may want to experiment to determine * where it becomes significant, if you are concerned with memory * usage. Note that zlib allocates at least 32Kb also. For readers, * this describes the size of the buffer available to read the data in. * Unless this gets smaller than the size of a row (compressed), * it should not make much difference how big this is. */ #ifndef PNG_ZBUF_SIZE # define PNG_ZBUF_SIZE 8192 #endif /* Enable if you want a write-only libpng */ #ifndef PNG_NO_READ_SUPPORTED # define PNG_READ_SUPPORTED #endif /* Enable if you want a read-only libpng */ #ifndef PNG_NO_WRITE_SUPPORTED # define PNG_WRITE_SUPPORTED #endif /* Enabled in 1.4.0. */ #ifdef PNG_ALLOW_BENIGN_ERRORS # define png_benign_error png_warning # define png_chunk_benign_error png_chunk_warning #else # ifndef PNG_BENIGN_ERRORS_SUPPORTED # define png_benign_error png_error # define png_chunk_benign_error png_chunk_error # endif #endif /* Added at libpng version 1.4.0 */ #if !defined(PNG_NO_WARNINGS) && !defined(PNG_WARNINGS_SUPPORTED) # define PNG_WARNINGS_SUPPORTED #endif /* Added at libpng version 1.4.0 */ #if !defined(PNG_NO_ERROR_TEXT) && !defined(PNG_ERROR_TEXT_SUPPORTED) # define PNG_ERROR_TEXT_SUPPORTED #endif /* Added at libpng version 1.4.0 */ #if !defined(PNG_NO_CHECK_cHRM) && !defined(PNG_CHECK_cHRM_SUPPORTED) # define PNG_CHECK_cHRM_SUPPORTED #endif /* Added at libpng version 1.4.0 */ #if !defined(PNG_NO_ALIGNED_MEMORY) && !defined(PNG_ALIGNED_MEMORY_SUPPORTED) # define PNG_ALIGNED_MEMORY_SUPPORTED #endif /* Enabled by default in 1.2.0. You can disable this if you don't need to support PNGs that are embedded in MNG datastreams */ #ifndef PNG_NO_MNG_FEATURES # ifndef PNG_MNG_FEATURES_SUPPORTED # define PNG_MNG_FEATURES_SUPPORTED # endif #endif /* Added at libpng version 1.4.0 */ #ifndef PNG_NO_FLOATING_POINT_SUPPORTED # ifndef PNG_FLOATING_POINT_SUPPORTED # define PNG_FLOATING_POINT_SUPPORTED # endif #endif /* Added at libpng-1.4.0beta49 for testing (this test is no longer used in libpng and png_calloc() is always present) */ #define PNG_CALLOC_SUPPORTED /* If you are running on a machine where you cannot allocate more * than 64K of memory at once, uncomment this. While libpng will not * normally need that much memory in a chunk (unless you load up a very * large file), zlib needs to know how big of a chunk it can use, and * libpng thus makes sure to check any memory allocation to verify it * will fit into memory. #define PNG_MAX_MALLOC_64K */ #if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) # define PNG_MAX_MALLOC_64K #endif /* Special munging to support doing things the 'cygwin' way: * 'Normal' png-on-win32 defines/defaults: * PNG_BUILD_DLL -- building dll * PNG_USE_DLL -- building an application, linking to dll * (no define) -- building static library, or building an * application and linking to the static lib * 'Cygwin' defines/defaults: * PNG_BUILD_DLL -- (ignored) building the dll * (no define) -- (ignored) building an application, linking to the dll * PNG_STATIC -- (ignored) building the static lib, or building an * application that links to the static lib. * ALL_STATIC -- (ignored) building various static libs, or building an * application that links to the static libs. * Thus, * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and * this bit of #ifdefs will define the 'correct' config variables based on * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but * unnecessary. * * Also, the precedence order is: * ALL_STATIC (since we can't #undef something outside our namespace) * PNG_BUILD_DLL * PNG_STATIC * (nothing) == PNG_USE_DLL * * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent * of auto-import in binutils, we no longer need to worry about * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes * to __declspec() stuff. However, we DO need to worry about * PNG_BUILD_DLL and PNG_STATIC because those change some defaults * such as CONSOLE_IO. */ #ifdef __CYGWIN__ # ifdef ALL_STATIC # ifdef PNG_BUILD_DLL # undef PNG_BUILD_DLL # endif # ifdef PNG_USE_DLL # undef PNG_USE_DLL # endif # ifdef PNG_DLL # undef PNG_DLL # endif # ifndef PNG_STATIC # define PNG_STATIC # endif # else # ifdef PNG_BUILD_DLL # ifdef PNG_STATIC # undef PNG_STATIC # endif # ifdef PNG_USE_DLL # undef PNG_USE_DLL # endif # ifndef PNG_DLL # define PNG_DLL # endif # else # ifdef PNG_STATIC # ifdef PNG_USE_DLL # undef PNG_USE_DLL # endif # ifdef PNG_DLL # undef PNG_DLL # endif # else # ifndef PNG_USE_DLL # define PNG_USE_DLL # endif # ifndef PNG_DLL # define PNG_DLL # endif # endif # endif # endif #endif /* This protects us against compilers that run on a windowing system * and thus don't have or would rather us not use the stdio types: * stdin, stdout, and stderr. The only one currently used is stderr * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will * prevent these from being compiled and used. #defining PNG_NO_STDIO * will also prevent these, plus will prevent the entire set of stdio * macros and functions (FILE *, printf, etc.) from being compiled and used, * unless (PNG_DEBUG > 0) has been #defined. * * #define PNG_NO_CONSOLE_IO * #define PNG_NO_STDIO */ #ifdef _WIN32_WCE # define PNG_NO_CONSOLE_IO # define PNG_NO_STDIO # define PNG_NO_TIME_RFC1123 # ifdef PNG_DEBUG # undef PNG_DEBUG # endif #endif #if !defined(PNG_NO_STDIO) && !defined(PNG_STDIO_SUPPORTED) # define PNG_STDIO_SUPPORTED #endif #ifdef PNG_BUILD_DLL # if !defined(PNG_CONSOLE_IO_SUPPORTED) && !defined(PNG_NO_CONSOLE_IO) # define PNG_NO_CONSOLE_IO # endif #endif # ifdef PNG_NO_STDIO # ifndef PNG_NO_CONSOLE_IO # define PNG_NO_CONSOLE_IO # endif # ifdef PNG_DEBUG # if (PNG_DEBUG > 0) # include # endif # endif # else # include # endif #if !(defined PNG_NO_CONSOLE_IO) && !defined(PNG_CONSOLE_IO_SUPPORTED) # define PNG_CONSOLE_IO_SUPPORTED #endif /* This macro protects us against machines that don't have function * prototypes (ie K&R style headers). If your compiler does not handle * function prototypes, define this macro and use the included ansi2knr. * I've always been able to use _NO_PROTO as the indicator, but you may * need to drag the empty declaration out in front of here, or change the * ifdef to suit your own needs. */ #ifndef PNGARG #ifdef OF /* zlib prototype munger */ # define PNGARG(arglist) OF(arglist) #else #ifdef _NO_PROTO # define PNGARG(arglist) () #else # define PNGARG(arglist) arglist #endif /* _NO_PROTO */ #endif /* OF */ #endif /* PNGARG */ /* Try to determine if we are compiling on a Mac. Note that testing for * just __MWERKS__ is not good enough, because the Codewarrior is now used * on non-Mac platforms. */ #ifndef MACOS # if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) # define MACOS # endif #endif /* Enough people need this for various reasons to include it here */ #if !defined(MACOS) && !defined(RISCOS) # include #endif /* PNG_SETJMP_NOT_SUPPORTED and PNG_NO_SETJMP_SUPPORTED are deprecated. */ #if !defined(PNG_NO_SETJMP) && \ !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) # define PNG_SETJMP_SUPPORTED #endif #ifdef PNG_SETJMP_SUPPORTED /* This is an attempt to force a single setjmp behaviour on Linux. If * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. * * You can bypass this test if you know that your application uses exactly * the same setjmp.h that was included when libpng was built. Only define * PNG_SKIP_SETJMP_CHECK while building your application, prior to the * application's '#include "png.h"'. Don't define PNG_SKIP_SETJMP_CHECK * while building a separate libpng library for general use. */ # ifndef PNG_SKIP_SETJMP_CHECK # ifdef __linux__ # ifdef _BSD_SOURCE # define PNG_SAVE_BSD_SOURCE # undef _BSD_SOURCE # endif # ifdef _SETJMP_H /* If you encounter a compiler error here, see the explanation * near the end of INSTALL. */ __pngconf.h__ in libpng already includes setjmp.h; __dont__ include it again.; # endif # endif /* __linux__ */ # endif /* PNG_SKIP_SETJMP_CHECK */ /* Include setjmp.h for error handling */ # include # ifdef __linux__ # ifdef PNG_SAVE_BSD_SOURCE # ifdef _BSD_SOURCE # undef _BSD_SOURCE # endif # define _BSD_SOURCE # undef PNG_SAVE_BSD_SOURCE # endif # endif /* __linux__ */ #endif /* PNG_SETJMP_SUPPORTED */ #ifdef BSD # include #else # include #endif /* Other defines for things like memory and the like can go here. */ /* This controls how fine the quantizing gets. As this allocates * a largish chunk of memory (32K), those who are not as concerned * with quantizing quality can decrease some or all of these. */ /* Prior to libpng-1.4.2, these were PNG_DITHER_*_BITS * These migration aids will be removed from libpng-1.5.0. */ #ifdef PNG_DITHER_RED_BITS # define PNG_QUANTIZE_RED_BITS PNG_DITHER_RED_BITS #endif #ifdef PNG_DITHER_GREEN_BITS # define PNG_QUANTIZE_GREEN_BITS PNG_DITHER_GREEN_BITS #endif #ifdef PNG_DITHER_BLUE_BITS # define PNG_QUANTIZE_BLUE_BITS PNG_DITHER_BLUE_BITS #endif #ifndef PNG_QUANTIZE_RED_BITS # define PNG_QUANTIZE_RED_BITS 5 #endif #ifndef PNG_QUANTIZE_GREEN_BITS # define PNG_QUANTIZE_GREEN_BITS 5 #endif #ifndef PNG_QUANTIZE_BLUE_BITS # define PNG_QUANTIZE_BLUE_BITS 5 #endif /* This controls how fine the gamma correction becomes when you * are only interested in 8 bits anyway. Increasing this value * results in more memory being used, and more pow() functions * being called to fill in the gamma tables. Don't set this value * less then 8, and even that may not work (I haven't tested it). */ #ifndef PNG_MAX_GAMMA_8 # define PNG_MAX_GAMMA_8 11 #endif /* This controls how much a difference in gamma we can tolerate before * we actually start doing gamma conversion. */ #ifndef PNG_GAMMA_THRESHOLD # define PNG_GAMMA_THRESHOLD 0.05 #endif /* The following uses const char * instead of char * for error * and warning message functions, so some compilers won't complain. * If you do not want to use const, define PNG_NO_CONST. */ #ifndef PNG_CONST # ifndef PNG_NO_CONST # define PNG_CONST const # else # define PNG_CONST # endif #endif /* The following defines give you the ability to remove code from the * library that you will not be using. I wish I could figure out how to * automate this, but I can't do that without making it seriously hard * on the users. So if you are not using an ability, change the #define * to an #undef, or pass in PNG_NO_feature and that part of the library * will not be compiled. * If your linker can't find a function, you may want to make sure the * ability is defined here. Some of these depend upon some others being * defined. I haven't figured out all the interactions here, so you may * have to experiment awhile to get everything to compile. If you are * creating or using a shared library, you probably shouldn't touch this, * as it will affect the size of the structures, and this will cause bad * things to happen if the library and/or application ever change. */ /* Any features you will not be using can be undef'ed here */ /* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user * to turn it off with PNG_NO_READ|WRITE_TRANSFORMS on the compile line, * then pick and choose which ones to define without having to edit this * file. It is safe to use the PNG_NO_READ|WRITE_TRANSFORMS * if you only want to have a png-compliant reader/writer but don't need * any of the extra transformations. This saves about 80 kbytes in a * typical installation of the library. (PNG_NO_* form added in version * 1.0.1c, for consistency; PNG_*_TRANSFORMS_NOT_SUPPORTED deprecated in * 1.4.0) */ /* Ignore attempt to turn off both floating and fixed point support */ #if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ !defined(PNG_NO_FIXED_POINT_SUPPORTED) # define PNG_FIXED_POINT_SUPPORTED #endif #ifdef PNG_READ_SUPPORTED /* PNG_READ_TRANSFORMS_NOT_SUPPORTED is deprecated. */ #if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ !defined(PNG_NO_READ_TRANSFORMS) # define PNG_READ_TRANSFORMS_SUPPORTED #endif #ifdef PNG_READ_TRANSFORMS_SUPPORTED # ifndef PNG_NO_READ_EXPAND # define PNG_READ_EXPAND_SUPPORTED # endif # ifndef PNG_NO_READ_SHIFT # define PNG_READ_SHIFT_SUPPORTED # endif # ifndef PNG_NO_READ_PACK # define PNG_READ_PACK_SUPPORTED # endif # ifndef PNG_NO_READ_BGR # define PNG_READ_BGR_SUPPORTED # endif # ifndef PNG_NO_READ_SWAP # define PNG_READ_SWAP_SUPPORTED # endif # ifndef PNG_NO_READ_PACKSWAP # define PNG_READ_PACKSWAP_SUPPORTED # endif # ifndef PNG_NO_READ_INVERT # define PNG_READ_INVERT_SUPPORTED # endif # ifndef PNG_NO_READ_QUANTIZE /* Prior to libpng-1.4.0 this was PNG_READ_DITHER_SUPPORTED */ # ifndef PNG_NO_READ_DITHER /* This migration aid will be removed */ # define PNG_READ_QUANTIZE_SUPPORTED # endif # endif # ifndef PNG_NO_READ_BACKGROUND # define PNG_READ_BACKGROUND_SUPPORTED # endif # ifndef PNG_NO_READ_16_TO_8 # define PNG_READ_16_TO_8_SUPPORTED # endif # ifndef PNG_NO_READ_FILLER # define PNG_READ_FILLER_SUPPORTED # endif # ifndef PNG_NO_READ_GAMMA # define PNG_READ_GAMMA_SUPPORTED # endif # ifndef PNG_NO_READ_GRAY_TO_RGB # define PNG_READ_GRAY_TO_RGB_SUPPORTED # endif # ifndef PNG_NO_READ_SWAP_ALPHA # define PNG_READ_SWAP_ALPHA_SUPPORTED # endif # ifndef PNG_NO_READ_INVERT_ALPHA # define PNG_READ_INVERT_ALPHA_SUPPORTED # endif # ifndef PNG_NO_READ_STRIP_ALPHA # define PNG_READ_STRIP_ALPHA_SUPPORTED # endif # ifndef PNG_NO_READ_USER_TRANSFORM # define PNG_READ_USER_TRANSFORM_SUPPORTED # endif # ifndef PNG_NO_READ_RGB_TO_GRAY # define PNG_READ_RGB_TO_GRAY_SUPPORTED # endif #endif /* PNG_READ_TRANSFORMS_SUPPORTED */ /* PNG_PROGRESSIVE_READ_NOT_SUPPORTED is deprecated. */ #if !defined(PNG_NO_PROGRESSIVE_READ) && \ !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ # define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ #endif /* about interlacing capability! You'll */ /* still have interlacing unless you change the following define: */ #define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ /* PNG_NO_SEQUENTIAL_READ_SUPPORTED is deprecated. */ #if !defined(PNG_NO_SEQUENTIAL_READ) && \ !defined(PNG_SEQUENTIAL_READ_SUPPORTED) && \ !defined(PNG_NO_SEQUENTIAL_READ_SUPPORTED) # define PNG_SEQUENTIAL_READ_SUPPORTED #endif #ifndef PNG_NO_READ_COMPOSITE_NODIV # ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ # define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ # endif #endif #if !defined(PNG_NO_GET_INT_32) || defined(PNG_READ_oFFS_SUPPORTED) || \ defined(PNG_READ_pCAL_SUPPORTED) # ifndef PNG_GET_INT_32_SUPPORTED # define PNG_GET_INT_32_SUPPORTED # endif #endif #endif /* PNG_READ_SUPPORTED */ #ifdef PNG_WRITE_SUPPORTED /* PNG_WRITE_TRANSFORMS_NOT_SUPPORTED is deprecated. */ #if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ !defined(PNG_NO_WRITE_TRANSFORMS) # define PNG_WRITE_TRANSFORMS_SUPPORTED #endif #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED # ifndef PNG_NO_WRITE_SHIFT # define PNG_WRITE_SHIFT_SUPPORTED # endif # ifndef PNG_NO_WRITE_PACK # define PNG_WRITE_PACK_SUPPORTED # endif # ifndef PNG_NO_WRITE_BGR # define PNG_WRITE_BGR_SUPPORTED # endif # ifndef PNG_NO_WRITE_SWAP # define PNG_WRITE_SWAP_SUPPORTED # endif # ifndef PNG_NO_WRITE_PACKSWAP # define PNG_WRITE_PACKSWAP_SUPPORTED # endif # ifndef PNG_NO_WRITE_INVERT # define PNG_WRITE_INVERT_SUPPORTED # endif # ifndef PNG_NO_WRITE_FILLER # define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ # endif # ifndef PNG_NO_WRITE_SWAP_ALPHA # define PNG_WRITE_SWAP_ALPHA_SUPPORTED # endif # ifndef PNG_NO_WRITE_INVERT_ALPHA # define PNG_WRITE_INVERT_ALPHA_SUPPORTED # endif # ifndef PNG_NO_WRITE_USER_TRANSFORM # define PNG_WRITE_USER_TRANSFORM_SUPPORTED # endif #endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ #if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ !defined(PNG_WRITE_INTERLACING_SUPPORTED) /* This is not required for PNG-compliant encoders, but can cause * trouble if left undefined */ # define PNG_WRITE_INTERLACING_SUPPORTED #endif #if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ !defined(PNG_WRITE_WEIGHTED_FILTER) && \ defined(PNG_FLOATING_POINT_SUPPORTED) # define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #endif #ifndef PNG_NO_WRITE_FLUSH # define PNG_WRITE_FLUSH_SUPPORTED #endif #if !defined(PNG_NO_SAVE_INT_32) || defined(PNG_WRITE_oFFS_SUPPORTED) || \ defined(PNG_WRITE_pCAL_SUPPORTED) # ifndef PNG_SAVE_INT_32_SUPPORTED # define PNG_SAVE_INT_32_SUPPORTED # endif #endif #endif /* PNG_WRITE_SUPPORTED */ #define PNG_NO_ERROR_NUMBERS #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) # ifndef PNG_NO_USER_TRANSFORM_PTR # define PNG_USER_TRANSFORM_PTR_SUPPORTED # endif #endif #if defined(PNG_STDIO_SUPPORTED) && !defined(PNG_TIME_RFC1123_SUPPORTED) # define PNG_TIME_RFC1123_SUPPORTED #endif /* This adds extra functions in pngget.c for accessing data from the * info pointer (added in version 0.99) * png_get_image_width() * png_get_image_height() * png_get_bit_depth() * png_get_color_type() * png_get_compression_type() * png_get_filter_type() * png_get_interlace_type() * png_get_pixel_aspect_ratio() * png_get_pixels_per_meter() * png_get_x_offset_pixels() * png_get_y_offset_pixels() * png_get_x_offset_microns() * png_get_y_offset_microns() */ #if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) # define PNG_EASY_ACCESS_SUPPORTED #endif /* Added at libpng-1.2.0 */ #if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) # define PNG_USER_MEM_SUPPORTED #endif /* Added at libpng-1.2.6 */ #ifndef PNG_NO_SET_USER_LIMITS # ifndef PNG_SET_USER_LIMITS_SUPPORTED # define PNG_SET_USER_LIMITS_SUPPORTED # endif /* Feature added at libpng-1.4.0, this flag added at 1.4.1 */ # ifndef PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED # define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED # endif /* Feature added at libpng-1.4.1, this flag added at 1.4.1 */ # ifndef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED # define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED # endif #endif /* Added at libpng-1.2.43 */ #ifndef PNG_USER_LIMITS_SUPPORTED # ifndef PNG_NO_USER_LIMITS # define PNG_USER_LIMITS_SUPPORTED # endif #endif /* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGs no matter * how large, set these two limits to 0x7fffffffL */ #ifndef PNG_USER_WIDTH_MAX # define PNG_USER_WIDTH_MAX 1000000L #endif #ifndef PNG_USER_HEIGHT_MAX # define PNG_USER_HEIGHT_MAX 1000000L #endif /* Added at libpng-1.2.43. To accept all valid PNGs no matter * how large, set these two limits to 0. */ #ifndef PNG_USER_CHUNK_CACHE_MAX # define PNG_USER_CHUNK_CACHE_MAX 0 #endif /* Added at libpng-1.2.43 */ #ifndef PNG_USER_CHUNK_MALLOC_MAX # define PNG_USER_CHUNK_MALLOC_MAX 0 #endif /* Added at libpng-1.4.0 */ #if !defined(PNG_NO_IO_STATE) && !defined(PNG_IO_STATE_SUPPORTED) # define PNG_IO_STATE_SUPPORTED #endif #ifndef PNG_LITERAL_SHARP # define PNG_LITERAL_SHARP 0x23 #endif #ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b #endif #ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET # define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d #endif #ifndef PNG_STRING_NEWLINE #define PNG_STRING_NEWLINE "\n" #endif /* These are currently experimental features, define them if you want */ /* Very little testing */ /* #ifdef PNG_READ_SUPPORTED # ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED # define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED # endif #endif */ /* This is only for PowerPC big-endian and 680x0 systems */ /* some testing */ /* #ifndef PNG_READ_BIG_ENDIAN_SUPPORTED # define PNG_READ_BIG_ENDIAN_SUPPORTED #endif */ #if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) # define PNG_USE_READ_MACROS #endif /* Buggy compilers (e.g., gcc 2.7.2.2) need PNG_NO_POINTER_INDEXING */ #if !defined(PNG_NO_POINTER_INDEXING) && \ !defined(PNG_POINTER_INDEXING_SUPPORTED) # define PNG_POINTER_INDEXING_SUPPORTED #endif /* Any chunks you are not interested in, you can undef here. The * ones that allocate memory may be expecially important (hIST, * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info * a bit smaller. */ /* The size of the png_text structure changed in libpng-1.0.6 when * iTXt support was added. iTXt support was turned off by default through * libpng-1.2.x, to support old apps that malloc the png_text structure * instead of calling png_set_text() and letting libpng malloc it. It * was turned on by default in libpng-1.4.0. */ /* PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ #if defined(PNG_READ_SUPPORTED) && \ !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ !defined(PNG_NO_READ_ANCILLARY_CHUNKS) # define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED #endif /* PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED is deprecated. */ #if defined(PNG_WRITE_SUPPORTED) && \ !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) # define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED #endif #ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED #ifdef PNG_NO_READ_TEXT # define PNG_NO_READ_iTXt # define PNG_NO_READ_tEXt # define PNG_NO_READ_zTXt #endif #ifndef PNG_NO_READ_bKGD # define PNG_READ_bKGD_SUPPORTED # define PNG_bKGD_SUPPORTED #endif #ifndef PNG_NO_READ_cHRM # define PNG_READ_cHRM_SUPPORTED # define PNG_cHRM_SUPPORTED #endif #ifndef PNG_NO_READ_gAMA # define PNG_READ_gAMA_SUPPORTED # define PNG_gAMA_SUPPORTED #endif #ifndef PNG_NO_READ_hIST # define PNG_READ_hIST_SUPPORTED # define PNG_hIST_SUPPORTED #endif #ifndef PNG_NO_READ_iCCP # define PNG_READ_iCCP_SUPPORTED # define PNG_iCCP_SUPPORTED #endif #ifndef PNG_NO_READ_iTXt # ifndef PNG_READ_iTXt_SUPPORTED # define PNG_READ_iTXt_SUPPORTED # endif # ifndef PNG_iTXt_SUPPORTED # define PNG_iTXt_SUPPORTED # endif #endif #ifndef PNG_NO_READ_oFFs # define PNG_READ_oFFs_SUPPORTED # define PNG_oFFs_SUPPORTED #endif #ifndef PNG_NO_READ_pCAL # define PNG_READ_pCAL_SUPPORTED # define PNG_pCAL_SUPPORTED #endif #ifndef PNG_NO_READ_sCAL # define PNG_READ_sCAL_SUPPORTED # define PNG_sCAL_SUPPORTED #endif #ifndef PNG_NO_READ_pHYs # define PNG_READ_pHYs_SUPPORTED # define PNG_pHYs_SUPPORTED #endif #ifndef PNG_NO_READ_sBIT # define PNG_READ_sBIT_SUPPORTED # define PNG_sBIT_SUPPORTED #endif #ifndef PNG_NO_READ_sPLT # define PNG_READ_sPLT_SUPPORTED # define PNG_sPLT_SUPPORTED #endif #ifndef PNG_NO_READ_sRGB # define PNG_READ_sRGB_SUPPORTED # define PNG_sRGB_SUPPORTED #endif #ifndef PNG_NO_READ_tEXt # define PNG_READ_tEXt_SUPPORTED # define PNG_tEXt_SUPPORTED #endif #ifndef PNG_NO_READ_tIME # define PNG_READ_tIME_SUPPORTED # define PNG_tIME_SUPPORTED #endif #ifndef PNG_NO_READ_tRNS # define PNG_READ_tRNS_SUPPORTED # define PNG_tRNS_SUPPORTED #endif #ifndef PNG_NO_READ_zTXt # define PNG_READ_zTXt_SUPPORTED # define PNG_zTXt_SUPPORTED #endif #ifndef PNG_NO_READ_OPT_PLTE # define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ #endif /* optional PLTE chunk in RGB and RGBA images */ #if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ defined(PNG_READ_zTXt_SUPPORTED) # define PNG_READ_TEXT_SUPPORTED # define PNG_TEXT_SUPPORTED #endif #endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ #ifndef PNG_NO_READ_UNKNOWN_CHUNKS # ifndef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED # define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED # endif # ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED # define PNG_UNKNOWN_CHUNKS_SUPPORTED # endif # ifndef PNG_READ_USER_CHUNKS_SUPPORTED # define PNG_READ_USER_CHUNKS_SUPPORTED # endif #endif #ifndef PNG_NO_READ_USER_CHUNKS # ifndef PNG_READ_USER_CHUNKS_SUPPORTED # define PNG_READ_USER_CHUNKS_SUPPORTED # endif # ifndef PNG_USER_CHUNKS_SUPPORTED # define PNG_USER_CHUNKS_SUPPORTED # endif #endif #ifndef PNG_NO_HANDLE_AS_UNKNOWN # ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED # define PNG_HANDLE_AS_UNKNOWN_SUPPORTED # endif #endif #ifdef PNG_WRITE_SUPPORTED #ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED #ifdef PNG_NO_WRITE_TEXT # define PNG_NO_WRITE_iTXt # define PNG_NO_WRITE_tEXt # define PNG_NO_WRITE_zTXt #endif #ifndef PNG_NO_WRITE_bKGD # define PNG_WRITE_bKGD_SUPPORTED # ifndef PNG_bKGD_SUPPORTED # define PNG_bKGD_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_cHRM # define PNG_WRITE_cHRM_SUPPORTED # ifndef PNG_cHRM_SUPPORTED # define PNG_cHRM_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_gAMA # define PNG_WRITE_gAMA_SUPPORTED # ifndef PNG_gAMA_SUPPORTED # define PNG_gAMA_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_hIST # define PNG_WRITE_hIST_SUPPORTED # ifndef PNG_hIST_SUPPORTED # define PNG_hIST_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_iCCP # define PNG_WRITE_iCCP_SUPPORTED # ifndef PNG_iCCP_SUPPORTED # define PNG_iCCP_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_iTXt # ifndef PNG_WRITE_iTXt_SUPPORTED # define PNG_WRITE_iTXt_SUPPORTED # endif # ifndef PNG_iTXt_SUPPORTED # define PNG_iTXt_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_oFFs # define PNG_WRITE_oFFs_SUPPORTED # ifndef PNG_oFFs_SUPPORTED # define PNG_oFFs_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_pCAL # define PNG_WRITE_pCAL_SUPPORTED # ifndef PNG_pCAL_SUPPORTED # define PNG_pCAL_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_sCAL # define PNG_WRITE_sCAL_SUPPORTED # ifndef PNG_sCAL_SUPPORTED # define PNG_sCAL_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_pHYs # define PNG_WRITE_pHYs_SUPPORTED # ifndef PNG_pHYs_SUPPORTED # define PNG_pHYs_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_sBIT # define PNG_WRITE_sBIT_SUPPORTED # ifndef PNG_sBIT_SUPPORTED # define PNG_sBIT_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_sPLT # define PNG_WRITE_sPLT_SUPPORTED # ifndef PNG_sPLT_SUPPORTED # define PNG_sPLT_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_sRGB # define PNG_WRITE_sRGB_SUPPORTED # ifndef PNG_sRGB_SUPPORTED # define PNG_sRGB_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_tEXt # define PNG_WRITE_tEXt_SUPPORTED # ifndef PNG_tEXt_SUPPORTED # define PNG_tEXt_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_tIME # define PNG_WRITE_tIME_SUPPORTED # ifndef PNG_tIME_SUPPORTED # define PNG_tIME_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_tRNS # define PNG_WRITE_tRNS_SUPPORTED # ifndef PNG_tRNS_SUPPORTED # define PNG_tRNS_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_zTXt # define PNG_WRITE_zTXt_SUPPORTED # ifndef PNG_zTXt_SUPPORTED # define PNG_zTXt_SUPPORTED # endif #endif #if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ defined(PNG_WRITE_zTXt_SUPPORTED) # define PNG_WRITE_TEXT_SUPPORTED # ifndef PNG_TEXT_SUPPORTED # define PNG_TEXT_SUPPORTED # endif #endif #ifdef PNG_WRITE_tIME_SUPPORTED # ifndef PNG_NO_CONVERT_tIME # ifndef _WIN32_WCE /* The "tm" structure is not supported on WindowsCE */ # ifndef PNG_CONVERT_tIME_SUPPORTED # define PNG_CONVERT_tIME_SUPPORTED # endif # endif # endif #endif #endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ #ifndef PNG_NO_WRITE_FILTER # ifndef PNG_WRITE_FILTER_SUPPORTED # define PNG_WRITE_FILTER_SUPPORTED # endif #endif #ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS # define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED # ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED # define PNG_UNKNOWN_CHUNKS_SUPPORTED # endif #endif #ifndef PNG_NO_HANDLE_AS_UNKNOWN # ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED # define PNG_HANDLE_AS_UNKNOWN_SUPPORTED # endif #endif #endif /* PNG_WRITE_SUPPORTED */ /* Turn this off to disable png_read_png() and * png_write_png() and leave the row_pointers member * out of the info structure. */ #ifndef PNG_NO_INFO_IMAGE # define PNG_INFO_IMAGE_SUPPORTED #endif /* Need the time information for converting tIME chunks */ #ifdef PNG_CONVERT_tIME_SUPPORTED /* "time.h" functions are not supported on WindowsCE */ # include #endif /* Some typedefs to get us started. These should be safe on most of the * common platforms. The typedefs should be at least as large as the * numbers suggest (a png_uint_32 must be at least 32 bits long), but they * don't have to be exactly that size. Some compilers dislike passing * unsigned shorts as function parameters, so you may be better off using * unsigned int for png_uint_16. */ #if defined(INT_MAX) && (INT_MAX > 0x7ffffffeL) typedef unsigned int png_uint_32; typedef int png_int_32; #else typedef unsigned long png_uint_32; typedef long png_int_32; #endif typedef unsigned short png_uint_16; typedef short png_int_16; typedef unsigned char png_byte; #ifdef PNG_NO_SIZE_T typedef unsigned int png_size_t; #else typedef size_t png_size_t; #endif #define png_sizeof(x) (sizeof (x)) /* The following is needed for medium model support. It cannot be in the * pngpriv.h header. Needs modification for other compilers besides * MSC. Model independent support declares all arrays and pointers to be * large using the far keyword. The zlib version used must also support * model independent data. As of version zlib 1.0.4, the necessary changes * have been made in zlib. The USE_FAR_KEYWORD define triggers other * changes that are needed. (Tim Wegner) */ /* Separate compiler dependencies (problem here is that zlib.h always * defines FAR. (SJT) */ #ifdef __BORLANDC__ # if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) # define LDATA 1 # else # define LDATA 0 # endif /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ # if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) # define PNG_MAX_MALLOC_64K # if (LDATA != 1) # ifndef FAR # define FAR __far # endif # define USE_FAR_KEYWORD # endif /* LDATA != 1 */ /* Possibly useful for moving data out of default segment. * Uncomment it if you want. Could also define FARDATA as * const if your compiler supports it. (SJT) # define FARDATA FAR */ # endif /* __WIN32__, __FLAT__, __CYGWIN__ */ #endif /* __BORLANDC__ */ /* Suggest testing for specific compiler first before testing for * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, * making reliance oncertain keywords suspect. (SJT) */ /* MSC Medium model */ #ifdef FAR # ifdef M_I86MM # define USE_FAR_KEYWORD # define FARDATA FAR # include # endif #endif /* SJT: default case */ #ifndef FAR # define FAR #endif /* At this point FAR is always defined */ #ifndef FARDATA # define FARDATA #endif /* Typedef for floating-point numbers that are converted to fixed-point with a multiple of 100,000, e.g., int_gamma */ typedef png_int_32 png_fixed_point; /* Add typedefs for pointers */ typedef void FAR * png_voidp; typedef png_byte FAR * png_bytep; typedef png_uint_32 FAR * png_uint_32p; typedef png_int_32 FAR * png_int_32p; typedef png_uint_16 FAR * png_uint_16p; typedef png_int_16 FAR * png_int_16p; typedef PNG_CONST char FAR * png_const_charp; typedef char FAR * png_charp; typedef png_fixed_point FAR * png_fixed_point_p; #ifndef PNG_NO_STDIO typedef FILE * png_FILE_p; #endif #ifdef PNG_FLOATING_POINT_SUPPORTED typedef double FAR * png_doublep; #endif /* Pointers to pointers; i.e. arrays */ typedef png_byte FAR * FAR * png_bytepp; typedef png_uint_32 FAR * FAR * png_uint_32pp; typedef png_int_32 FAR * FAR * png_int_32pp; typedef png_uint_16 FAR * FAR * png_uint_16pp; typedef png_int_16 FAR * FAR * png_int_16pp; typedef PNG_CONST char FAR * FAR * png_const_charpp; typedef char FAR * FAR * png_charpp; typedef png_fixed_point FAR * FAR * png_fixed_point_pp; #ifdef PNG_FLOATING_POINT_SUPPORTED typedef double FAR * FAR * png_doublepp; #endif /* Pointers to pointers to pointers; i.e., pointer to array */ typedef char FAR * FAR * FAR * png_charppp; /* Define PNG_BUILD_DLL if the module being built is a Windows * LIBPNG DLL. * * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. * It is equivalent to Microsoft predefined macro _DLL that is * automatically defined when you compile using the share * version of the CRT (C Run-Time library) * * The cygwin mods make this behavior a little different: * Define PNG_BUILD_DLL if you are building a dll for use with cygwin * Define PNG_STATIC if you are building a static library for use with cygwin, * -or- if you are building an application that you want to link to the * static library. * PNG_USE_DLL is defined by default (no user action needed) unless one of * the other flags is defined. */ #if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) # define PNG_DLL #endif /* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", * you may get warnings regarding the linkage of png_zalloc and png_zfree. * Don't ignore those warnings; you must also reset the default calling * convention in your compiler to match your PNGAPI, and you must build * zlib and your applications the same way you build libpng. */ #ifdef __CYGWIN__ # undef PNGAPI # define PNGAPI __cdecl # undef PNG_IMPEXP # define PNG_IMPEXP #endif #ifdef __WATCOMC__ # ifndef PNGAPI # define PNGAPI # endif #endif #if defined(__MINGW32__) && !defined(PNG_MODULEDEF) # ifndef PNG_NO_MODULEDEF # define PNG_NO_MODULEDEF # endif #endif #if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) # define PNG_IMPEXP #endif #if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ (( defined(_Windows) || defined(_WINDOWS) || \ defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) # ifndef PNGAPI # if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) # define PNGAPI __cdecl # else # define PNGAPI _cdecl # endif # endif # if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) # define PNG_IMPEXP # endif # ifndef PNG_IMPEXP # define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol # define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol /* Borland/Microsoft */ # if defined(_MSC_VER) || defined(__BORLANDC__) # if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) # define PNG_EXPORT PNG_EXPORT_TYPE1 # else # define PNG_EXPORT PNG_EXPORT_TYPE2 # ifdef PNG_BUILD_DLL # define PNG_IMPEXP __export # else # define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in VC++ */ # endif /* Exists in Borland C++ for C++ classes (== huge) */ # endif # endif # ifndef PNG_IMPEXP # ifdef PNG_BUILD_DLL # define PNG_IMPEXP __declspec(dllexport) # else # define PNG_IMPEXP __declspec(dllimport) # endif # endif # endif /* PNG_IMPEXP */ #else /* !(DLL || non-cygwin WINDOWS) */ # if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) # ifndef PNGAPI # define PNGAPI _System # endif # else # if 0 /* ... other platforms, with other meanings */ # endif # endif #endif #ifndef PNGAPI # define PNGAPI #endif #ifndef PNG_IMPEXP # define PNG_IMPEXP #endif #ifdef PNG_BUILDSYMS # ifndef PNG_EXPORT # define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END # endif #endif #ifndef PNG_EXPORT # define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol #endif #define PNG_USE_LOCAL_ARRAYS /* Not used in libpng, defined for legacy apps */ /* Support for compiler specific function attributes. These are used * so that where compiler support is available incorrect use of API * functions in png.h will generate compiler warnings. * * Added at libpng-1.2.41. */ #ifndef PNG_NO_PEDANTIC_WARNINGS # ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED # define PNG_PEDANTIC_WARNINGS_SUPPORTED # endif #endif #ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED /* Support for compiler specific function attributes. These are used * so that where compiler support is available incorrect use of API * functions in png.h will generate compiler warnings. Added at libpng * version 1.2.41. */ # ifdef __GNUC__ # ifndef PNG_USE_RESULT # define PNG_USE_RESULT __attribute__((__warn_unused_result__)) # endif # ifndef PNG_NORETURN # define PNG_NORETURN __attribute__((__noreturn__)) # endif # ifndef PNG_ALLOCATED # define PNG_ALLOCATED __attribute__((__malloc__)) # endif /* This specifically protects structure members that should only be * accessed from within the library, therefore should be empty during * a library build. */ # ifndef PNG_DEPRECATED # define PNG_DEPRECATED __attribute__((__deprecated__)) # endif # ifndef PNG_DEPSTRUCT # define PNG_DEPSTRUCT __attribute__((__deprecated__)) # endif # ifndef PNG_PRIVATE # if 0 /* Doesn't work so we use deprecated instead*/ # define PNG_PRIVATE \ __attribute__((warning("This function is not exported by libpng."))) # else # define PNG_PRIVATE \ __attribute__((__deprecated__)) # endif # endif /* PNG_PRIVATE */ # endif /* __GNUC__ */ #endif /* PNG_PEDANTIC_WARNINGS */ #ifndef PNG_DEPRECATED # define PNG_DEPRECATED /* Use of this function is deprecated */ #endif #ifndef PNG_USE_RESULT # define PNG_USE_RESULT /* The result of this function must be checked */ #endif #ifndef PNG_NORETURN # define PNG_NORETURN /* This function does not return */ #endif #ifndef PNG_ALLOCATED # define PNG_ALLOCATED /* The result of the function is new memory */ #endif #ifndef PNG_DEPSTRUCT # define PNG_DEPSTRUCT /* Access to this struct member is deprecated */ #endif #ifndef PNG_PRIVATE # define PNG_PRIVATE /* This is a private libpng function */ #endif /* Users may want to use these so they are not private. Any library * functions that are passed far data must be model-independent. */ /* memory model/platform independent fns */ #ifndef PNG_ABORT # if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) # define PNG_ABORT() ExitProcess(0) # else # define PNG_ABORT() abort() # endif #endif #ifdef USE_FAR_KEYWORD /* Use this to make far-to-near assignments */ # define CHECK 1 # define NOCHECK 0 # define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) # define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) # define png_strcpy _fstrcpy # define png_strncpy _fstrncpy /* Added to v 1.2.6 */ # define png_strlen _fstrlen # define png_memcmp _fmemcmp /* SJT: added */ # define png_memcpy _fmemcpy # define png_memset _fmemset # define png_sprintf sprintf #else # if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) # /* Favor Windows over C runtime fns */ # define CVT_PTR(ptr) (ptr) # define CVT_PTR_NOCHECK(ptr) (ptr) # define png_strcpy lstrcpyA # define png_strncpy lstrcpynA # define png_strlen lstrlenA # define png_memcmp memcmp # define png_memcpy CopyMemory # define png_memset memset # define png_sprintf wsprintfA # else # define CVT_PTR(ptr) (ptr) # define CVT_PTR_NOCHECK(ptr) (ptr) # define png_strcpy strcpy # define png_strncpy strncpy /* Added to v 1.2.6 */ # define png_strlen strlen # define png_memcmp memcmp /* SJT: added */ # define png_memcpy memcpy # define png_memset memset # define png_sprintf sprintf # endif #endif #ifndef PNG_NO_SNPRINTF # ifdef _MSC_VER # define png_snprintf _snprintf /* Added to v 1.2.19 */ # define png_snprintf2 _snprintf # define png_snprintf6 _snprintf # else # define png_snprintf snprintf /* Added to v 1.2.19 */ # define png_snprintf2 snprintf # define png_snprintf6 snprintf # endif #else /* You don't have or don't want to use snprintf(). Caution: Using * sprintf instead of snprintf exposes your application to accidental * or malevolent buffer overflows. If you don't have snprintf() * as a general rule you should provide one (you can get one from * Portable OpenSSH). */ # define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1) # define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2) # define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) #endif /* png_alloc_size_t is guaranteed to be no smaller than png_size_t, * and no smaller than png_uint_32. Casts from png_size_t or png_uint_32 * to png_alloc_size_t are not necessary; in fact, it is recommended * not to use them at all so that the compiler can complain when something * turns out to be problematic. * Casts in the other direction (from png_alloc_size_t to png_size_t or * png_uint_32) should be explicitly applied; however, we do not expect * to encounter practical situations that require such conversions. */ #if defined(__TURBOC__) && !defined(__FLAT__) typedef unsigned long png_alloc_size_t; #else # if defined(_MSC_VER) && defined(MAXSEG_64K) typedef unsigned long png_alloc_size_t; # else /* This is an attempt to detect an old Windows system where (int) is * actually 16 bits, in that case png_malloc must have an argument with a * bigger size to accomodate the requirements of the library. */ # if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) && \ (!defined(INT_MAX) || INT_MAX <= 0x7ffffffeL) typedef DWORD png_alloc_size_t; # else typedef png_size_t png_alloc_size_t; # endif # endif #endif /* End of memory model/platform independent support */ /* Just a little check that someone hasn't tried to define something * contradictory. */ #if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) # undef PNG_ZBUF_SIZE # define PNG_ZBUF_SIZE 65536L #endif /* Added at libpng-1.2.8 */ #endif /* PNG_VERSION_INFO_ONLY */ #endif /* PNGCONF_H */ Indigo-indigo-1.2.3/third_party/libpng-src/src/000077500000000000000000000000001271037650300213615ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/libpng-src/src/png.c000066400000000000000000000657701271037650300223300ustar00rootroot00000000000000 /* png.c - location for general purpose libpng functions * * Last changed in libpng 1.4.2 [May 6, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ #define PNG_NO_EXTERN #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ typedef version_1_4_4 Your_png_h_is_not_version_1_4_4; /* Version information for C files. This had better match the version * string defined in png.h. */ /* Tells libpng that we have already handled the first "num_bytes" bytes * of the PNG file signature. If the PNG data is embedded into another * stream we can set num_bytes = 8 so that libpng will not attempt to read * or write any of the magic bytes before it starts on the IHDR. */ #ifdef PNG_READ_SUPPORTED void PNGAPI png_set_sig_bytes(png_structp png_ptr, int num_bytes) { png_debug(1, "in png_set_sig_bytes"); if (png_ptr == NULL) return; if (num_bytes > 8) png_error(png_ptr, "Too many bytes for PNG signature"); png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); } /* Checks whether the supplied bytes match the PNG signature. We allow * checking less than the full 8-byte signature so that those apps that * already read the first few bytes of a file to determine the file type * can simply check the remaining bytes for extra assurance. Returns * an integer less than, equal to, or greater than zero if sig is found, * respectively, to be less than, to match, or be greater than the correct * PNG signature (this is the same behaviour as strcmp, memcmp, etc). */ int PNGAPI png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) { png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; if (num_to_check > 8) num_to_check = 8; else if (num_to_check < 1) return (-1); if (start > 7) return (-1); if (start + num_to_check > 8) num_to_check = 8 - start; return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); } #endif /* PNG_READ_SUPPORTED */ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) /* Function to allocate memory for zlib and clear it to 0. */ voidpf /* PRIVATE */ png_zalloc(voidpf png_ptr, uInt items, uInt size) { png_voidp ptr; png_structp p=(png_structp)png_ptr; png_uint_32 save_flags=p->flags; png_alloc_size_t num_bytes; if (png_ptr == NULL) return (NULL); if (items > PNG_UINT_32_MAX/size) { png_warning (p, "Potential overflow in png_zalloc()"); return (NULL); } num_bytes = (png_alloc_size_t)items * size; p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); p->flags=save_flags; return ((voidpf)ptr); } /* Function to free memory for zlib */ void /* PRIVATE */ png_zfree(voidpf png_ptr, voidpf ptr) { png_free((png_structp)png_ptr, (png_voidp)ptr); } /* Reset the CRC variable to 32 bits of 1's. Care must be taken * in case CRC is > 32 bits to leave the top bits 0. */ void /* PRIVATE */ png_reset_crc(png_structp png_ptr) { png_ptr->crc = crc32(0, Z_NULL, 0); } /* Calculate the CRC over a section of data. We can only pass as * much data to this routine as the largest single buffer size. We * also check that this data will actually be used before going to the * trouble of calculating it. */ void /* PRIVATE */ png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) { int need_crc = 1; if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ { if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) need_crc = 0; } else /* critical */ { if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) need_crc = 0; } if (need_crc) png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); } /* Allocate the memory for an info_struct for the application. We don't * really need the png_ptr, but it could potentially be useful in the * future. This should be used in favour of malloc(png_sizeof(png_info)) * and png_info_init() so that applications that want to use a shared * libpng don't have to be recompiled if png_info changes size. */ png_infop PNGAPI png_create_info_struct(png_structp png_ptr) { png_infop info_ptr; png_debug(1, "in png_create_info_struct"); if (png_ptr == NULL) return (NULL); #ifdef PNG_USER_MEM_SUPPORTED info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, png_ptr->malloc_fn, png_ptr->mem_ptr); #else info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); #endif if (info_ptr != NULL) png_info_init_3(&info_ptr, png_sizeof(png_info)); return (info_ptr); } /* This function frees the memory associated with a single info struct. * Normally, one would use either png_destroy_read_struct() or * png_destroy_write_struct() to free an info struct, but this may be * useful for some applications. */ void PNGAPI png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) { png_infop info_ptr = NULL; png_debug(1, "in png_destroy_info_struct"); if (png_ptr == NULL) return; if (info_ptr_ptr != NULL) info_ptr = *info_ptr_ptr; if (info_ptr != NULL) { png_info_destroy(png_ptr, info_ptr); #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, png_ptr->mem_ptr); #else png_destroy_struct((png_voidp)info_ptr); #endif *info_ptr_ptr = NULL; } } /* Initialize the info structure. This is now an internal function (0.89) * and applications using it are urged to use png_create_info_struct() * instead. */ void PNGAPI png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) { png_infop info_ptr = *ptr_ptr; png_debug(1, "in png_info_init_3"); if (info_ptr == NULL) return; if (png_sizeof(png_info) > png_info_struct_size) { png_destroy_struct(info_ptr); info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); *ptr_ptr = info_ptr; } /* Set everything to 0 */ png_memset(info_ptr, 0, png_sizeof(png_info)); } void PNGAPI png_data_freer(png_structp png_ptr, png_infop info_ptr, int freer, png_uint_32 mask) { png_debug(1, "in png_data_freer"); if (png_ptr == NULL || info_ptr == NULL) return; if (freer == PNG_DESTROY_WILL_FREE_DATA) info_ptr->free_me |= mask; else if (freer == PNG_USER_WILL_FREE_DATA) info_ptr->free_me &= ~mask; else png_warning(png_ptr, "Unknown freer parameter in png_data_freer"); } void PNGAPI png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, int num) { png_debug(1, "in png_free_data"); if (png_ptr == NULL || info_ptr == NULL) return; #ifdef PNG_TEXT_SUPPORTED /* Free text item num or (if num == -1) all text items */ if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) { if (num != -1) { if (info_ptr->text && info_ptr->text[num].key) { png_free(png_ptr, info_ptr->text[num].key); info_ptr->text[num].key = NULL; } } else { int i; for (i = 0; i < info_ptr->num_text; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); png_free(png_ptr, info_ptr->text); info_ptr->text = NULL; info_ptr->num_text=0; } } #endif #ifdef PNG_tRNS_SUPPORTED /* Free any tRNS entry */ if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) { png_free(png_ptr, info_ptr->trans_alpha); info_ptr->trans_alpha = NULL; info_ptr->valid &= ~PNG_INFO_tRNS; } #endif #ifdef PNG_sCAL_SUPPORTED /* Free any sCAL entry */ if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) { #if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) png_free(png_ptr, info_ptr->scal_s_width); png_free(png_ptr, info_ptr->scal_s_height); info_ptr->scal_s_width = NULL; info_ptr->scal_s_height = NULL; #endif info_ptr->valid &= ~PNG_INFO_sCAL; } #endif #ifdef PNG_pCAL_SUPPORTED /* Free any pCAL entry */ if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) { png_free(png_ptr, info_ptr->pcal_purpose); png_free(png_ptr, info_ptr->pcal_units); info_ptr->pcal_purpose = NULL; info_ptr->pcal_units = NULL; if (info_ptr->pcal_params != NULL) { int i; for (i = 0; i < (int)info_ptr->pcal_nparams; i++) { png_free(png_ptr, info_ptr->pcal_params[i]); info_ptr->pcal_params[i] = NULL; } png_free(png_ptr, info_ptr->pcal_params); info_ptr->pcal_params = NULL; } info_ptr->valid &= ~PNG_INFO_pCAL; } #endif #ifdef PNG_iCCP_SUPPORTED /* Free any iCCP entry */ if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) { png_free(png_ptr, info_ptr->iccp_name); png_free(png_ptr, info_ptr->iccp_profile); info_ptr->iccp_name = NULL; info_ptr->iccp_profile = NULL; info_ptr->valid &= ~PNG_INFO_iCCP; } #endif #ifdef PNG_sPLT_SUPPORTED /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) { if (num != -1) { if (info_ptr->splt_palettes) { png_free(png_ptr, info_ptr->splt_palettes[num].name); png_free(png_ptr, info_ptr->splt_palettes[num].entries); info_ptr->splt_palettes[num].name = NULL; info_ptr->splt_palettes[num].entries = NULL; } } else { if (info_ptr->splt_palettes_num) { int i; for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); png_free(png_ptr, info_ptr->splt_palettes); info_ptr->splt_palettes = NULL; info_ptr->splt_palettes_num = 0; } info_ptr->valid &= ~PNG_INFO_sPLT; } } #endif #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED if (png_ptr->unknown_chunk.data) { png_free(png_ptr, png_ptr->unknown_chunk.data); png_ptr->unknown_chunk.data = NULL; } if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) { if (num != -1) { if (info_ptr->unknown_chunks) { png_free(png_ptr, info_ptr->unknown_chunks[num].data); info_ptr->unknown_chunks[num].data = NULL; } } else { int i; if (info_ptr->unknown_chunks_num) { for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); png_free(png_ptr, info_ptr->unknown_chunks); info_ptr->unknown_chunks = NULL; info_ptr->unknown_chunks_num = 0; } } } #endif #ifdef PNG_hIST_SUPPORTED /* Free any hIST entry */ if ((mask & PNG_FREE_HIST) & info_ptr->free_me) { png_free(png_ptr, info_ptr->hist); info_ptr->hist = NULL; info_ptr->valid &= ~PNG_INFO_hIST; } #endif /* Free any PLTE entry that was internally allocated */ if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) { png_zfree(png_ptr, info_ptr->palette); info_ptr->palette = NULL; info_ptr->valid &= ~PNG_INFO_PLTE; info_ptr->num_palette = 0; } #ifdef PNG_INFO_IMAGE_SUPPORTED /* Free any image bits attached to the info structure */ if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) { if (info_ptr->row_pointers) { int row; for (row = 0; row < (int)info_ptr->height; row++) { png_free(png_ptr, info_ptr->row_pointers[row]); info_ptr->row_pointers[row] = NULL; } png_free(png_ptr, info_ptr->row_pointers); info_ptr->row_pointers = NULL; } info_ptr->valid &= ~PNG_INFO_IDAT; } #endif if (num == -1) info_ptr->free_me &= ~mask; else info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); } /* This is an internal routine to free any memory that the info struct is * pointing to before re-using it or freeing the struct itself. Recall * that png_free() checks for NULL pointers for us. */ void /* PRIVATE */ png_info_destroy(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_info_destroy"); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED if (png_ptr->num_chunk_list) { png_free(png_ptr, png_ptr->chunk_list); png_ptr->chunk_list = NULL; png_ptr->num_chunk_list = 0; } #endif png_info_init_3(&info_ptr, png_sizeof(png_info)); } #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ /* This function returns a pointer to the io_ptr associated with the user * functions. The application should free any memory associated with this * pointer before png_write_destroy() or png_read_destroy() are called. */ png_voidp PNGAPI png_get_io_ptr(png_structp png_ptr) { if (png_ptr == NULL) return (NULL); return (png_ptr->io_ptr); } #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #ifdef PNG_STDIO_SUPPORTED /* Initialize the default input/output functions for the PNG file. If you * use your own read or write routines, you can call either png_set_read_fn() * or png_set_write_fn() instead of png_init_io(). If you have defined * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't * necessarily available. */ void PNGAPI png_init_io(png_structp png_ptr, png_FILE_p fp) { png_debug(1, "in png_init_io"); if (png_ptr == NULL) return; png_ptr->io_ptr = (png_voidp)fp; } #endif #ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert the supplied time into an RFC 1123 string suitable for use in * a "Creation Time" or other text-based time string. */ png_charp PNGAPI png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) { static PNG_CONST char short_months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; if (png_ptr == NULL) return (NULL); if (png_ptr->time_buffer == NULL) { png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* png_sizeof(char))); } #ifdef USE_FAR_KEYWORD { char near_time_buf[29]; png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000", ptime->day % 32, short_months[(ptime->month - 1) % 12], ptime->year, ptime->hour % 24, ptime->minute % 60, ptime->second % 61); png_memcpy(png_ptr->time_buffer, near_time_buf, 29*png_sizeof(char)); } #else png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000", ptime->day % 32, short_months[(ptime->month - 1) % 12], ptime->year, ptime->hour % 24, ptime->minute % 60, ptime->second % 61); #endif return ((png_charp)png_ptr->time_buffer); } #endif /* PNG_TIME_RFC1123_SUPPORTED */ #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ png_charp PNGAPI png_get_copyright(png_structp png_ptr) { png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ #ifdef PNG_STRING_COPYRIGHT return PNG_STRING_COPYRIGHT #else #ifdef __STDC__ return ((png_charp) PNG_STRING_NEWLINE \ "libpng version 1.4.4 - September 23, 2010" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2010 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE); #else return ((png_charp) "libpng version 1.4.4 - September 23, 2010\ Copyright (c) 1998-2010 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."); #endif #endif } /* The following return the library version as a short string in the * format 1.0.0 through 99.99.99zz. To get the version of *.h files * used with your application, print out PNG_LIBPNG_VER_STRING, which * is defined in png.h. * Note: now there is no difference between png_get_libpng_ver() and * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, * it is guaranteed that png.c uses the correct version of png.h. */ png_charp PNGAPI png_get_libpng_ver(png_structp png_ptr) { /* Version of *.c files used when building libpng */ png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ return ((png_charp) PNG_LIBPNG_VER_STRING); } png_charp PNGAPI png_get_header_ver(png_structp png_ptr) { /* Version of *.h files used when building libpng */ png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ return ((png_charp) PNG_LIBPNG_VER_STRING); } png_charp PNGAPI png_get_header_version(png_structp png_ptr) { /* Returns longer string containing both version and date */ png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ #ifdef __STDC__ return ((png_charp) PNG_HEADER_VERSION_STRING #ifndef PNG_READ_SUPPORTED " (NO READ SUPPORT)" #endif PNG_STRING_NEWLINE); #else return ((png_charp) PNG_HEADER_VERSION_STRING); #endif } #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED int PNGAPI png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) { /* Check chunk_name and return "keep" value if it's on the list, else 0 */ int i; png_bytep p; if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) return 0; p = png_ptr->chunk_list + png_ptr->num_chunk_list*5 - 5; for (i = png_ptr->num_chunk_list; i; i--, p -= 5) if (!png_memcmp(chunk_name, p, 4)) return ((int)*(p + 4)); return 0; } #endif #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ #ifdef PNG_READ_SUPPORTED /* This function, added to libpng-1.0.6g, is untested. */ int PNGAPI png_reset_zstream(png_structp png_ptr) { if (png_ptr == NULL) return Z_STREAM_ERROR; return (inflateReset(&png_ptr->zstream)); } #endif /* PNG_READ_SUPPORTED */ /* This function was added to libpng-1.0.7 */ png_uint_32 PNGAPI png_access_version_number(void) { /* Version of *.c files used when building libpng */ return((png_uint_32) PNG_LIBPNG_VER); } #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #ifdef PNG_SIZE_T /* Added at libpng version 1.2.6 */ PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); png_size_t PNGAPI png_convert_size(size_t size) { if (size > (png_size_t)-1) PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ return ((png_size_t)size); } #endif /* PNG_SIZE_T */ /* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_CHECK_cHRM_SUPPORTED /* * Multiply two 32-bit numbers, V1 and V2, using 32-bit * arithmetic, to produce a 64 bit result in the HI/LO words. * * A B * x C D * ------ * AD || BD * AC || CB || 0 * * where A and B are the high and low 16-bit words of V1, * C and D are the 16-bit words of V2, AD is the product of * A and D, and X || Y is (X << 16) + Y. */ void /* PRIVATE */ png_64bit_product (long v1, long v2, unsigned long *hi_product, unsigned long *lo_product) { int a, b, c, d; long lo, hi, x, y; a = (v1 >> 16) & 0xffff; b = v1 & 0xffff; c = (v2 >> 16) & 0xffff; d = v2 & 0xffff; lo = b * d; /* BD */ x = a * d + c * b; /* AD + CB */ y = ((lo >> 16) & 0xffff) + x; lo = (lo & 0xffff) | ((y & 0xffff) << 16); hi = (y >> 16) & 0xffff; hi += a * c; /* AC */ *hi_product = (unsigned long)hi; *lo_product = (unsigned long)lo; } int /* PRIVATE */ png_check_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { int ret = 1; unsigned long xy_hi,xy_lo,yx_hi,yx_lo; png_debug(1, "in function png_check_cHRM_fixed"); if (png_ptr == NULL) return 0; if (white_x < 0 || white_y <= 0 || red_x < 0 || red_y < 0 || green_x < 0 || green_y < 0 || blue_x < 0 || blue_y < 0) { png_warning(png_ptr, "Ignoring attempt to set negative chromaticity value"); ret = 0; } if (white_x > (png_fixed_point) PNG_UINT_31_MAX || white_y > (png_fixed_point) PNG_UINT_31_MAX || red_x > (png_fixed_point) PNG_UINT_31_MAX || red_y > (png_fixed_point) PNG_UINT_31_MAX || green_x > (png_fixed_point) PNG_UINT_31_MAX || green_y > (png_fixed_point) PNG_UINT_31_MAX || blue_x > (png_fixed_point) PNG_UINT_31_MAX || blue_y > (png_fixed_point) PNG_UINT_31_MAX ) { png_warning(png_ptr, "Ignoring attempt to set chromaticity value exceeding 21474.83"); ret = 0; } if (white_x > 100000L - white_y) { png_warning(png_ptr, "Invalid cHRM white point"); ret = 0; } if (red_x > 100000L - red_y) { png_warning(png_ptr, "Invalid cHRM red point"); ret = 0; } if (green_x > 100000L - green_y) { png_warning(png_ptr, "Invalid cHRM green point"); ret = 0; } if (blue_x > 100000L - blue_y) { png_warning(png_ptr, "Invalid cHRM blue point"); ret = 0; } png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo); png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo); if (xy_hi == yx_hi && xy_lo == yx_lo) { png_warning(png_ptr, "Ignoring attempt to set cHRM RGB triangle with zero area"); ret = 0; } return ret; } #endif /* PNG_CHECK_cHRM_SUPPORTED */ #endif /* PNG_cHRM_SUPPORTED */ void /* PRIVATE */ png_check_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type) { int error = 0; /* Check for width and height valid values */ if (width == 0) { png_warning(png_ptr, "Image width is zero in IHDR"); error = 1; } if (height == 0) { png_warning(png_ptr, "Image height is zero in IHDR"); error = 1; } #ifdef PNG_SET_USER_LIMITS_SUPPORTED if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX) #else if (width > PNG_USER_WIDTH_MAX) #endif { png_warning(png_ptr, "Image width exceeds user limit in IHDR"); error = 1; } #ifdef PNG_SET_USER_LIMITS_SUPPORTED if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX) #else if (height > PNG_USER_HEIGHT_MAX) #endif { png_warning(png_ptr, "Image height exceeds user limit in IHDR"); error = 1; } if (width > PNG_UINT_31_MAX) { png_warning(png_ptr, "Invalid image width in IHDR"); error = 1; } if ( height > PNG_UINT_31_MAX) { png_warning(png_ptr, "Invalid image height in IHDR"); error = 1; } if ( width > (PNG_UINT_32_MAX >> 3) /* 8-byte RGBA pixels */ - 64 /* bigrowbuf hack */ - 1 /* filter byte */ - 7*8 /* rounding of width to multiple of 8 pixels */ - 8) /* extra max_pixel_depth pad */ png_warning(png_ptr, "Width is too large for libpng to process pixels"); /* Check other values */ if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8 && bit_depth != 16) { png_warning(png_ptr, "Invalid bit depth in IHDR"); error = 1; } if (color_type < 0 || color_type == 1 || color_type == 5 || color_type > 6) { png_warning(png_ptr, "Invalid color type in IHDR"); error = 1; } if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || ((color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) { png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); error = 1; } if (interlace_type >= PNG_INTERLACE_LAST) { png_warning(png_ptr, "Unknown interlace method in IHDR"); error = 1; } if (compression_type != PNG_COMPRESSION_TYPE_BASE) { png_warning(png_ptr, "Unknown compression method in IHDR"); error = 1; } #ifdef PNG_MNG_FEATURES_SUPPORTED /* Accept filter_method 64 (intrapixel differencing) only if * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and * 2. Libpng did not read a PNG signature (this filter_method is only * used in PNG datastreams that are embedded in MNG datastreams) and * 3. The application called png_permit_mng_features with a mask that * included PNG_FLAG_MNG_FILTER_64 and * 4. The filter_method is 64 and * 5. The color_type is RGB or RGBA */ if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && png_ptr->mng_features_permitted) png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); if (filter_type != PNG_FILTER_TYPE_BASE) { if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA))) { png_warning(png_ptr, "Unknown filter method in IHDR"); error = 1; } if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) { png_warning(png_ptr, "Invalid filter method in IHDR"); error = 1; } } #else if (filter_type != PNG_FILTER_TYPE_BASE) { png_warning(png_ptr, "Unknown filter method in IHDR"); error = 1; } #endif if (error == 1) png_error(png_ptr, "Invalid IHDR data"); } #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngerror.c000066400000000000000000000300401271037650300233600ustar00rootroot00000000000000 /* pngerror.c - stub functions for i/o and memory allocation * * Last changed in libpng 1.4.0 [January 3, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file provides a location for all error handling. Users who * need special error handling are expected to write replacement functions * and use png_set_error_fn() to use those functions. See the instructions * at each function. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #include "pngpriv.h" static void /* PRIVATE */ png_default_error PNGARG((png_structp png_ptr, png_const_charp error_message)) PNG_NORETURN; #ifdef PNG_WARNINGS_SUPPORTED static void /* PRIVATE */ png_default_warning PNGARG((png_structp png_ptr, png_const_charp warning_message)); #endif /* PNG_WARNINGS_SUPPORTED */ /* This function is called whenever there is a fatal error. This function * should not be changed. If there is a need to handle errors differently, * you should supply a replacement error function and use png_set_error_fn() * to replace the error function at run-time. */ #ifdef PNG_ERROR_TEXT_SUPPORTED void PNGAPI png_error(png_structp png_ptr, png_const_charp error_message) { #ifdef PNG_ERROR_NUMBERS_SUPPORTED char msg[16]; if (png_ptr != NULL) { if (png_ptr->flags& (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) { if (*error_message == PNG_LITERAL_SHARP) { /* Strip "#nnnn " from beginning of error message. */ int offset; for (offset = 1; offset<15; offset++) if (error_message[offset] == ' ') break; if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) { int i; for (i = 0; i < offset - 1; i++) msg[i] = error_message[i + 1]; msg[i - 1] = '\0'; error_message = msg; } else error_message += offset; } else { if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) { msg[0] = '0'; msg[1] = '\0'; error_message = msg; } } } } #endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_ptr, error_message); /* If the custom handler doesn't exist, or if it returns, use the default handler, which will not return. */ png_default_error(png_ptr, error_message); } #else void PNGAPI png_err(png_structp png_ptr) { if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_ptr, '\0'); /* If the custom handler doesn't exist, or if it returns, use the default handler, which will not return. */ png_default_error(png_ptr, '\0'); } #endif /* PNG_ERROR_TEXT_SUPPORTED */ #ifdef PNG_WARNINGS_SUPPORTED /* This function is called whenever there is a non-fatal error. This function * should not be changed. If there is a need to handle warnings differently, * you should supply a replacement warning function and use * png_set_error_fn() to replace the warning function at run-time. */ void PNGAPI png_warning(png_structp png_ptr, png_const_charp warning_message) { int offset = 0; if (png_ptr != NULL) { #ifdef PNG_ERROR_NUMBERS_SUPPORTED if (png_ptr->flags& (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) #endif { if (*warning_message == PNG_LITERAL_SHARP) { for (offset = 1; offset < 15; offset++) if (warning_message[offset] == ' ') break; } } } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_ptr, warning_message + offset); else png_default_warning(png_ptr, warning_message + offset); } #endif /* PNG_WARNINGS_SUPPORTED */ #ifdef PNG_BENIGN_ERRORS_SUPPORTED void PNGAPI png_benign_error(png_structp png_ptr, png_const_charp error_message) { if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) png_warning(png_ptr, error_message); else png_error(png_ptr, error_message); } #endif /* These utilities are used internally to build an error message that relates * to the current chunk. The chunk name comes from png_ptr->chunk_name, * this is used to prefix the message. The message is limited in length * to 63 bytes, the name characters are output as hex digits wrapped in [] * if the character is invalid. */ #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) static PNG_CONST char png_digit[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; #define PNG_MAX_ERROR_TEXT 64 #if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) static void /* PRIVATE */ png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp error_message) { int iout = 0, iin = 0; while (iin < 4) { int c = png_ptr->chunk_name[iin++]; if (isnonalpha(c)) { buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; buffer[iout++] = png_digit[(c & 0xf0) >> 4]; buffer[iout++] = png_digit[c & 0x0f]; buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; } else { buffer[iout++] = (png_byte)c; } } if (error_message == NULL) buffer[iout] = '\0'; else { buffer[iout++] = ':'; buffer[iout++] = ' '; png_memcpy(buffer + iout, error_message, PNG_MAX_ERROR_TEXT); buffer[iout + PNG_MAX_ERROR_TEXT - 1] = '\0'; } } #ifdef PNG_READ_SUPPORTED void PNGAPI png_chunk_error(png_structp png_ptr, png_const_charp error_message) { char msg[18+PNG_MAX_ERROR_TEXT]; if (png_ptr == NULL) png_error(png_ptr, error_message); else { png_format_buffer(png_ptr, msg, error_message); png_error(png_ptr, msg); } } #endif /* PNG_READ_SUPPORTED */ #endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ #ifdef PNG_WARNINGS_SUPPORTED void PNGAPI png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) { char msg[18+PNG_MAX_ERROR_TEXT]; if (png_ptr == NULL) png_warning(png_ptr, warning_message); else { png_format_buffer(png_ptr, msg, warning_message); png_warning(png_ptr, msg); } } #endif /* PNG_WARNINGS_SUPPORTED */ #ifdef PNG_READ_SUPPORTED #ifdef PNG_BENIGN_ERRORS_SUPPORTED void PNGAPI png_chunk_benign_error(png_structp png_ptr, png_const_charp error_message) { if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) png_chunk_warning(png_ptr, error_message); else png_chunk_error(png_ptr, error_message); } #endif #endif /* PNG_READ_SUPPORTED */ #ifdef PNG_SETJMP_SUPPORTED /* This API only exists if ANSI-C style error handling is used, * otherwise it is necessary for png_default_error to be overridden. */ jmp_buf* PNGAPI png_set_longjmp_fn(png_structp png_ptr, png_longjmp_ptr longjmp_fn, size_t jmp_buf_size) { if (png_ptr == NULL || jmp_buf_size != png_sizeof(jmp_buf)) return NULL; png_ptr->longjmp_fn = longjmp_fn; return &png_ptr->jmpbuf; } #endif /* This is the default error handling function. Note that replacements for * this function MUST NOT RETURN, or the program will likely crash. This * function is used by default, or if the program supplies NULL for the * error function pointer in png_set_error_fn(). */ static void /* PRIVATE */ png_default_error(png_structp png_ptr, png_const_charp error_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED #ifdef PNG_ERROR_NUMBERS_SUPPORTED if (*error_message == PNG_LITERAL_SHARP) { /* Strip "#nnnn " from beginning of error message. */ int offset; char error_number[16]; for (offset = 0; offset<15; offset++) { error_number[offset] = error_message[offset + 1]; if (error_message[offset] == ' ') break; } if ((offset > 1) && (offset < 15)) { error_number[offset - 1] = '\0'; fprintf(stderr, "libpng error no. %s: %s", error_number, error_message + offset + 1); fprintf(stderr, PNG_STRING_NEWLINE); } else { fprintf(stderr, "libpng error: %s, offset=%d", error_message, offset); fprintf(stderr, PNG_STRING_NEWLINE); } } else #endif { fprintf(stderr, "libpng error: %s", error_message); fprintf(stderr, PNG_STRING_NEWLINE); } #endif #ifdef PNG_SETJMP_SUPPORTED if (png_ptr && png_ptr->longjmp_fn) { # ifdef USE_FAR_KEYWORD { jmp_buf jmpbuf; png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); png_ptr->longjmp_fn(jmpbuf, 1); } # else png_ptr->longjmp_fn(png_ptr->jmpbuf, 1); # endif } #endif /* Here if not setjmp support or if png_ptr is null. */ PNG_ABORT(); #ifndef PNG_CONSOLE_IO_SUPPORTED error_message = error_message; /* Make compiler happy */ #endif } #ifdef PNG_WARNINGS_SUPPORTED /* This function is called when there is a warning, but the library thinks * it can continue anyway. Replacement functions don't have to do anything * here if you don't want them to. In the default configuration, png_ptr is * not used, but it is passed in case it may be useful. */ static void /* PRIVATE */ png_default_warning(png_structp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED # ifdef PNG_ERROR_NUMBERS_SUPPORTED if (*warning_message == PNG_LITERAL_SHARP) { int offset; char warning_number[16]; for (offset = 0; offset < 15; offset++) { warning_number[offset] = warning_message[offset + 1]; if (warning_message[offset] == ' ') break; } if ((offset > 1) && (offset < 15)) { warning_number[offset + 1] = '\0'; fprintf(stderr, "libpng warning no. %s: %s", warning_number, warning_message + offset); fprintf(stderr, PNG_STRING_NEWLINE); } else { fprintf(stderr, "libpng warning: %s", warning_message); fprintf(stderr, PNG_STRING_NEWLINE); } } else # endif { fprintf(stderr, "libpng warning: %s", warning_message); fprintf(stderr, PNG_STRING_NEWLINE); } #else warning_message = warning_message; /* Make compiler happy */ #endif png_ptr = png_ptr; /* Make compiler happy */ } #endif /* PNG_WARNINGS_SUPPORTED */ /* This function is called when the application wants to use another method * of handling errors and warnings. Note that the error function MUST NOT * return to the calling routine or serious problems will occur. The return * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) */ void PNGAPI png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn) { if (png_ptr == NULL) return; png_ptr->error_ptr = error_ptr; png_ptr->error_fn = error_fn; png_ptr->warning_fn = warning_fn; } /* This function returns a pointer to the error_ptr associated with the user * functions. The application should free any memory associated with this * pointer before png_write_destroy and png_read_destroy are called. */ png_voidp PNGAPI png_get_error_ptr(png_structp png_ptr) { if (png_ptr == NULL) return NULL; return ((png_voidp)png_ptr->error_ptr); } #ifdef PNG_ERROR_NUMBERS_SUPPORTED void PNGAPI png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) { if (png_ptr != NULL) { png_ptr->flags &= ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); } } #endif #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngget.c000066400000000000000000000576531271037650300230310ustar00rootroot00000000000000 /* pngget.c - retrieval of values from info struct * * Last changed in libpng 1.4.2 [May 6, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #include "pngpriv.h" png_uint_32 PNGAPI png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->valid & flag); else return(0); } png_size_t PNGAPI png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->rowbytes); else return(0); } #ifdef PNG_INFO_IMAGE_SUPPORTED png_bytepp PNGAPI png_get_rows(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->row_pointers); else return(0); } #endif #ifdef PNG_EASY_ACCESS_SUPPORTED /* Easy access to info, added in libpng-0.99 */ png_uint_32 PNGAPI png_get_image_width(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->width; return (0); } png_uint_32 PNGAPI png_get_image_height(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->height; return (0); } png_byte PNGAPI png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->bit_depth; return (0); } png_byte PNGAPI png_get_color_type(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->color_type; return (0); } png_byte PNGAPI png_get_filter_type(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->filter_type; return (0); } png_byte PNGAPI png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->interlace_type; return (0); } png_byte PNGAPI png_get_compression_type(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return info_ptr->compression_type; return (0); } png_uint_32 PNGAPI png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) { png_debug1(1, "in %s retrieval function", "png_get_x_pixels_per_meter"); if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) return (0); else return (info_ptr->x_pixels_per_unit); } #else return (0); #endif return (0); } png_uint_32 PNGAPI png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) { png_debug1(1, "in %s retrieval function", "png_get_y_pixels_per_meter"); if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) return (0); else return (info_ptr->y_pixels_per_unit); } #else return (0); #endif return (0); } png_uint_32 PNGAPI png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) { png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER || info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) return (0); else return (info_ptr->x_pixels_per_unit); } #else return (0); #endif return (0); } #ifdef PNG_FLOATING_POINT_SUPPORTED float PNGAPI png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) { png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); if (info_ptr->x_pixels_per_unit == 0) return ((float)0.0); else return ((float)((float)info_ptr->y_pixels_per_unit /(float)info_ptr->x_pixels_per_unit)); } #else return (0.0); #endif return ((float)0.0); } #endif png_int_32 PNGAPI png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) { png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) return (0); else return (info_ptr->x_offset); } #else return (0); #endif return (0); } png_int_32 PNGAPI png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) { png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) return (0); else return (info_ptr->y_offset); } #else return (0); #endif return (0); } png_int_32 PNGAPI png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) { png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) return (0); else return (info_ptr->x_offset); } #else return (0); #endif return (0); } png_int_32 PNGAPI png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) #ifdef PNG_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) { png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) return (0); else return (info_ptr->y_offset); } #else return (0); #endif return (0); } #if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) png_uint_32 PNGAPI png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) { return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) *.0254 +.5)); } png_uint_32 PNGAPI png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) { return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) *.0254 +.5)); } png_uint_32 PNGAPI png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) { return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) *.0254 +.5)); } float PNGAPI png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) { return ((float)png_get_x_offset_microns(png_ptr, info_ptr) *.00003937); } float PNGAPI png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) { return ((float)png_get_y_offset_microns(png_ptr, info_ptr) *.00003937); } #ifdef PNG_pHYs_SUPPORTED png_uint_32 PNGAPI png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) { png_uint_32 retval = 0; if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { png_debug1(1, "in %s retrieval function", "pHYs"); if (res_x != NULL) { *res_x = info_ptr->x_pixels_per_unit; retval |= PNG_INFO_pHYs; } if (res_y != NULL) { *res_y = info_ptr->y_pixels_per_unit; retval |= PNG_INFO_pHYs; } if (unit_type != NULL) { *unit_type = (int)info_ptr->phys_unit_type; retval |= PNG_INFO_pHYs; if (*unit_type == 1) { if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); } } } return (retval); } #endif /* PNG_pHYs_SUPPORTED */ #endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ /* png_get_channels really belongs in here, too, but it's been around longer */ #endif /* PNG_EASY_ACCESS_SUPPORTED */ png_byte PNGAPI png_get_channels(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->channels); else return (0); } png_bytep PNGAPI png_get_signature(png_structp png_ptr, png_infop info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->signature); else return (NULL); } #ifdef PNG_bKGD_SUPPORTED png_uint_32 PNGAPI png_get_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p *background) { if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) && background != NULL) { png_debug1(1, "in %s retrieval function", "bKGD"); *background = &(info_ptr->background); return (PNG_INFO_bKGD); } return (0); } #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_cHRM(png_structp png_ptr, png_infop info_ptr, double *white_x, double *white_y, double *red_x, double *red_y, double *green_x, double *green_y, double *blue_x, double *blue_y) { if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) { png_debug1(1, "in %s retrieval function", "cHRM"); if (white_x != NULL) *white_x = (double)info_ptr->x_white; if (white_y != NULL) *white_y = (double)info_ptr->y_white; if (red_x != NULL) *red_x = (double)info_ptr->x_red; if (red_y != NULL) *red_y = (double)info_ptr->y_red; if (green_x != NULL) *green_x = (double)info_ptr->x_green; if (green_y != NULL) *green_y = (double)info_ptr->y_green; if (blue_x != NULL) *blue_x = (double)info_ptr->x_blue; if (blue_y != NULL) *blue_y = (double)info_ptr->y_blue; return (PNG_INFO_cHRM); } return (0); } #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_uint_32 PNGAPI png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, png_fixed_point *blue_x, png_fixed_point *blue_y) { png_debug1(1, "in %s retrieval function", "cHRM"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) { if (white_x != NULL) *white_x = info_ptr->int_x_white; if (white_y != NULL) *white_y = info_ptr->int_y_white; if (red_x != NULL) *red_x = info_ptr->int_x_red; if (red_y != NULL) *red_y = info_ptr->int_y_red; if (green_x != NULL) *green_x = info_ptr->int_x_green; if (green_y != NULL) *green_y = info_ptr->int_y_green; if (blue_x != NULL) *blue_x = info_ptr->int_x_blue; if (blue_y != NULL) *blue_y = info_ptr->int_y_blue; return (PNG_INFO_cHRM); } return (0); } #endif #endif #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) { png_debug1(1, "in %s retrieval function", "gAMA"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) && file_gamma != NULL) { *file_gamma = (double)info_ptr->gamma; return (PNG_INFO_gAMA); } return (0); } #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_uint_32 PNGAPI png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point *int_file_gamma) { png_debug1(1, "in %s retrieval function", "gAMA"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) && int_file_gamma != NULL) { *int_file_gamma = info_ptr->int_gamma; return (PNG_INFO_gAMA); } return (0); } #endif #endif #ifdef PNG_sRGB_SUPPORTED png_uint_32 PNGAPI png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) { png_debug1(1, "in %s retrieval function", "sRGB"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) && file_srgb_intent != NULL) { *file_srgb_intent = (int)info_ptr->srgb_intent; return (PNG_INFO_sRGB); } return (0); } #endif #ifdef PNG_iCCP_SUPPORTED png_uint_32 PNGAPI png_get_iCCP(png_structp png_ptr, png_infop info_ptr, png_charpp name, int *compression_type, png_charpp profile, png_uint_32 *proflen) { png_debug1(1, "in %s retrieval function", "iCCP"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) && name != NULL && profile != NULL && proflen != NULL) { *name = info_ptr->iccp_name; *profile = info_ptr->iccp_profile; /* Compression_type is a dummy so the API won't have to change * if we introduce multiple compression types later. */ *proflen = (int)info_ptr->iccp_proflen; *compression_type = (int)info_ptr->iccp_compression; return (PNG_INFO_iCCP); } return (0); } #endif #ifdef PNG_sPLT_SUPPORTED png_uint_32 PNGAPI png_get_sPLT(png_structp png_ptr, png_infop info_ptr, png_sPLT_tpp spalettes) { if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) { *spalettes = info_ptr->splt_palettes; return ((png_uint_32)info_ptr->splt_palettes_num); } return (0); } #endif #ifdef PNG_hIST_SUPPORTED png_uint_32 PNGAPI png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) { png_debug1(1, "in %s retrieval function", "hIST"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) && hist != NULL) { *hist = info_ptr->hist; return (PNG_INFO_hIST); } return (0); } #endif png_uint_32 PNGAPI png_get_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_type, int *compression_type, int *filter_type) { png_debug1(1, "in %s retrieval function", "IHDR"); if (png_ptr == NULL || info_ptr == NULL || width == NULL || height == NULL || bit_depth == NULL || color_type == NULL) return (0); *width = info_ptr->width; *height = info_ptr->height; *bit_depth = info_ptr->bit_depth; *color_type = info_ptr->color_type; if (compression_type != NULL) *compression_type = info_ptr->compression_type; if (filter_type != NULL) *filter_type = info_ptr->filter_type; if (interlace_type != NULL) *interlace_type = info_ptr->interlace_type; /* This is redundant if we can be sure that the info_ptr values were all * assigned in png_set_IHDR(). We do the check anyhow in case an * application has ignored our advice not to mess with the members * of info_ptr directly. */ png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, info_ptr->compression_type, info_ptr->filter_type); return (1); } #ifdef PNG_oFFs_SUPPORTED png_uint_32 PNGAPI png_get_oFFs(png_structp png_ptr, png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) { png_debug1(1, "in %s retrieval function", "oFFs"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) && offset_x != NULL && offset_y != NULL && unit_type != NULL) { *offset_x = info_ptr->x_offset; *offset_y = info_ptr->y_offset; *unit_type = (int)info_ptr->offset_unit_type; return (PNG_INFO_oFFs); } return (0); } #endif #ifdef PNG_pCAL_SUPPORTED png_uint_32 PNGAPI png_get_pCAL(png_structp png_ptr, png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, png_charp *units, png_charpp *params) { png_debug1(1, "in %s retrieval function", "pCAL"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && nparams != NULL && units != NULL && params != NULL) { *purpose = info_ptr->pcal_purpose; *X0 = info_ptr->pcal_X0; *X1 = info_ptr->pcal_X1; *type = (int)info_ptr->pcal_type; *nparams = (int)info_ptr->pcal_nparams; *units = info_ptr->pcal_units; *params = info_ptr->pcal_params; return (PNG_INFO_pCAL); } return (0); } #endif #ifdef PNG_sCAL_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI png_get_sCAL(png_structp png_ptr, png_infop info_ptr, int *unit, double *width, double *height) { if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { *unit = info_ptr->scal_unit; *width = info_ptr->scal_pixel_width; *height = info_ptr->scal_pixel_height; return (PNG_INFO_sCAL); } return(0); } #else #ifdef PNG_FIXED_POINT_SUPPORTED png_uint_32 PNGAPI png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, int *unit, png_charpp width, png_charpp height) { if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { *unit = info_ptr->scal_unit; *width = info_ptr->scal_s_width; *height = info_ptr->scal_s_height; return (PNG_INFO_sCAL); } return(0); } #endif #endif #endif #ifdef PNG_pHYs_SUPPORTED png_uint_32 PNGAPI png_get_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) { png_uint_32 retval = 0; png_debug1(1, "in %s retrieval function", "pHYs"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { if (res_x != NULL) { *res_x = info_ptr->x_pixels_per_unit; retval |= PNG_INFO_pHYs; } if (res_y != NULL) { *res_y = info_ptr->y_pixels_per_unit; retval |= PNG_INFO_pHYs; } if (unit_type != NULL) { *unit_type = (int)info_ptr->phys_unit_type; retval |= PNG_INFO_pHYs; } } return (retval); } #endif png_uint_32 PNGAPI png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, int *num_palette) { png_debug1(1, "in %s retrieval function", "PLTE"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) && palette != NULL) { *palette = info_ptr->palette; *num_palette = info_ptr->num_palette; png_debug1(3, "num_palette = %d", *num_palette); return (PNG_INFO_PLTE); } return (0); } #ifdef PNG_sBIT_SUPPORTED png_uint_32 PNGAPI png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) { png_debug1(1, "in %s retrieval function", "sBIT"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) && sig_bit != NULL) { *sig_bit = &(info_ptr->sig_bit); return (PNG_INFO_sBIT); } return (0); } #endif #ifdef PNG_TEXT_SUPPORTED png_uint_32 PNGAPI png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, int *num_text) { if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) { png_debug1(1, "in %s retrieval function", (png_ptr->chunk_name[0] == '\0' ? "text" : (png_const_charp)png_ptr->chunk_name)); if (text_ptr != NULL) *text_ptr = info_ptr->text; if (num_text != NULL) *num_text = info_ptr->num_text; return ((png_uint_32)info_ptr->num_text); } if (num_text != NULL) *num_text = 0; return(0); } #endif #ifdef PNG_tIME_SUPPORTED png_uint_32 PNGAPI png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) { png_debug1(1, "in %s retrieval function", "tIME"); if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) && mod_time != NULL) { *mod_time = &(info_ptr->mod_time); return (PNG_INFO_tIME); } return (0); } #endif #ifdef PNG_tRNS_SUPPORTED png_uint_32 PNGAPI png_get_tRNS(png_structp png_ptr, png_infop info_ptr, png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) { png_uint_32 retval = 0; if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { png_debug1(1, "in %s retrieval function", "tRNS"); if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (trans_alpha != NULL) { *trans_alpha = info_ptr->trans_alpha; retval |= PNG_INFO_tRNS; } if (trans_color != NULL) *trans_color = &(info_ptr->trans_color); } else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ { if (trans_color != NULL) { *trans_color = &(info_ptr->trans_color); retval |= PNG_INFO_tRNS; } if (trans_alpha != NULL) *trans_alpha = NULL; } if (num_trans != NULL) { *num_trans = info_ptr->num_trans; retval |= PNG_INFO_tRNS; } } return (retval); } #endif #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED png_uint_32 PNGAPI png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, png_unknown_chunkpp unknowns) { if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) { *unknowns = info_ptr->unknown_chunks; return ((png_uint_32)info_ptr->unknown_chunks_num); } return (0); } #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte PNGAPI png_get_rgb_to_gray_status (png_structp png_ptr) { return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); } #endif #ifdef PNG_USER_CHUNKS_SUPPORTED png_voidp PNGAPI png_get_user_chunk_ptr(png_structp png_ptr) { return (png_ptr? png_ptr->user_chunk_ptr : NULL); } #endif png_size_t PNGAPI png_get_compression_buffer_size(png_structp png_ptr) { return (png_ptr ? png_ptr->zbuf_size : 0L); } #ifdef PNG_SET_USER_LIMITS_SUPPORTED /* These functions were added to libpng 1.2.6 and were enabled * by default in libpng-1.4.0 */ png_uint_32 PNGAPI png_get_user_width_max (png_structp png_ptr) { return (png_ptr? png_ptr->user_width_max : 0); } png_uint_32 PNGAPI png_get_user_height_max (png_structp png_ptr) { return (png_ptr? png_ptr->user_height_max : 0); } /* This function was added to libpng 1.4.0 */ png_uint_32 PNGAPI png_get_chunk_cache_max (png_structp png_ptr) { return (png_ptr? png_ptr->user_chunk_cache_max : 0); } /* This function was added to libpng 1.4.1 */ png_alloc_size_t PNGAPI png_get_chunk_malloc_max (png_structp png_ptr) { return (png_ptr? png_ptr->user_chunk_malloc_max : 0); } #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ /* These functions were added to libpng 1.4.0 */ #ifdef PNG_IO_STATE_SUPPORTED png_uint_32 PNGAPI png_get_io_state (png_structp png_ptr) { return png_ptr->io_state; } png_bytep PNGAPI png_get_io_chunk_name (png_structp png_ptr) { return png_ptr->chunk_name; } #endif /* ?PNG_IO_STATE_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngmem.c000066400000000000000000000420201271037650300230060ustar00rootroot00000000000000 /* pngmem.c - stub functions for memory allocation * * Last changed in libpng 1.4.2 [May 6, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file provides a location for all memory allocation. Users who * need special memory handling are expected to supply replacement * functions for png_malloc() and png_free(), and to use * png_create_read_struct_2() and png_create_write_struct_2() to * identify the replacement functions. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #include "pngpriv.h" /* Borland DOS special memory handler */ #if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) /* If you change this, be sure to change the one in png.h also */ /* Allocate memory for a png_struct. The malloc and memset can be replaced by a single call to calloc() if this is thought to improve performance. */ png_voidp /* PRIVATE */ png_create_struct(int type) { #ifdef PNG_USER_MEM_SUPPORTED return (png_create_struct_2(type, NULL, NULL)); } /* Alternate version of png_create_struct, for use with user-defined malloc. */ png_voidp /* PRIVATE */ png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) { #endif /* PNG_USER_MEM_SUPPORTED */ png_size_t size; png_voidp struct_ptr; if (type == PNG_STRUCT_INFO) size = png_sizeof(png_info); else if (type == PNG_STRUCT_PNG) size = png_sizeof(png_struct); else return (png_get_copyright(NULL)); #ifdef PNG_USER_MEM_SUPPORTED if (malloc_fn != NULL) { png_struct dummy_struct; png_structp png_ptr = &dummy_struct; png_ptr->mem_ptr=mem_ptr; struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); } else #endif /* PNG_USER_MEM_SUPPORTED */ struct_ptr = (png_voidp)farmalloc(size); if (struct_ptr != NULL) png_memset(struct_ptr, 0, size); return (struct_ptr); } /* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct(png_voidp struct_ptr) { #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2(struct_ptr, NULL, NULL); } /* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, png_voidp mem_ptr) { #endif if (struct_ptr != NULL) { #ifdef PNG_USER_MEM_SUPPORTED if (free_fn != NULL) { png_struct dummy_struct; png_structp png_ptr = &dummy_struct; png_ptr->mem_ptr=mem_ptr; (*(free_fn))(png_ptr, struct_ptr); return; } #endif /* PNG_USER_MEM_SUPPORTED */ farfree (struct_ptr); } } /* Allocate memory. For reasonable files, size should never exceed * 64K. However, zlib may allocate more then 64K if you don't tell * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. * * Borland seems to have a problem in DOS mode for exactly 64K. * It gives you a segment with an offset of 8 (perhaps to store its * memory stuff). zlib doesn't like this at all, so we have to * detect and deal with it. This code should not be needed in * Windows or OS/2 modes, and only in 16 bit mode. This code has * been updated by Alexander Lehmann for version 0.89 to waste less * memory. * * Note that we can't use png_size_t for the "size" declaration, * since on some systems a png_size_t is a 16-bit quantity, and as a * result, we would be truncating potentially larger memory requests * (which should cause a fatal error) and introducing major problems. */ png_voidp PNGAPI png_calloc(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; ret = (png_malloc(png_ptr, size)); if (ret != NULL) png_memset(ret,0,(png_size_t)size); return (ret); } png_voidp PNGAPI png_malloc(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; if (png_ptr == NULL || size == 0) return (NULL); #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr->malloc_fn != NULL) ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); else ret = (png_malloc_default(png_ptr, size)); if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out of memory"); return (ret); } png_voidp PNGAPI png_malloc_default(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; #endif /* PNG_USER_MEM_SUPPORTED */ if (png_ptr == NULL || size == 0) return (NULL); #ifdef PNG_MAX_MALLOC_64K if (size > (png_uint_32)65536L) { png_warning(png_ptr, "Cannot Allocate > 64K"); ret = NULL; } else #endif if (size != (size_t)size) ret = NULL; else if (size == (png_uint_32)65536L) { if (png_ptr->offset_table == NULL) { /* Try to see if we need to do any of this fancy stuff */ ret = farmalloc(size); if (ret == NULL || ((png_size_t)ret & 0xffff)) { int num_blocks; png_uint_32 total_size; png_bytep table; int i; png_byte huge * hptr; if (ret != NULL) { farfree(ret); ret = NULL; } if (png_ptr->zlib_window_bits > 14) num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); else num_blocks = 1; if (png_ptr->zlib_mem_level >= 7) num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); else num_blocks++; total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; table = farmalloc(total_size); if (table == NULL) { #ifndef PNG_USER_MEM_SUPPORTED if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out Of Memory"); /* Note "O", "M" */ else png_warning(png_ptr, "Out Of Memory"); #endif return (NULL); } if ((png_size_t)table & 0xfff0) { #ifndef PNG_USER_MEM_SUPPORTED if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Farmalloc didn't return normalized pointer"); else png_warning(png_ptr, "Farmalloc didn't return normalized pointer"); #endif return (NULL); } png_ptr->offset_table = table; png_ptr->offset_table_ptr = farmalloc(num_blocks * png_sizeof(png_bytep)); if (png_ptr->offset_table_ptr == NULL) { #ifndef PNG_USER_MEM_SUPPORTED if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out Of memory"); /* Note "O", "m" */ else png_warning(png_ptr, "Out Of memory"); #endif return (NULL); } hptr = (png_byte huge *)table; if ((png_size_t)hptr & 0xf) { hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ } for (i = 0; i < num_blocks; i++) { png_ptr->offset_table_ptr[i] = (png_bytep)hptr; hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ } png_ptr->offset_table_number = num_blocks; png_ptr->offset_table_count = 0; png_ptr->offset_table_count_free = 0; } } if (png_ptr->offset_table_count >= png_ptr->offset_table_number) { #ifndef PNG_USER_MEM_SUPPORTED if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out of Memory"); /* Note "o" and "M" */ else png_warning(png_ptr, "Out of Memory"); #endif return (NULL); } ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; } else ret = farmalloc(size); #ifndef PNG_USER_MEM_SUPPORTED if (ret == NULL) { if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out of memory"); /* Note "o" and "m" */ else png_warning(png_ptr, "Out of memory"); /* Note "o" and "m" */ } #endif return (ret); } /* Free a pointer allocated by png_malloc(). In the default * configuration, png_ptr is not used, but is passed in case it * is needed. If ptr is NULL, return without taking any action. */ void PNGAPI png_free(png_structp png_ptr, png_voidp ptr) { if (png_ptr == NULL || ptr == NULL) return; #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr->free_fn != NULL) { (*(png_ptr->free_fn))(png_ptr, ptr); return; } else png_free_default(png_ptr, ptr); } void PNGAPI png_free_default(png_structp png_ptr, png_voidp ptr) { #endif /* PNG_USER_MEM_SUPPORTED */ if (png_ptr == NULL || ptr == NULL) return; if (png_ptr->offset_table != NULL) { int i; for (i = 0; i < png_ptr->offset_table_count; i++) { if (ptr == png_ptr->offset_table_ptr[i]) { ptr = NULL; png_ptr->offset_table_count_free++; break; } } if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) { farfree(png_ptr->offset_table); farfree(png_ptr->offset_table_ptr); png_ptr->offset_table = NULL; png_ptr->offset_table_ptr = NULL; } } if (ptr != NULL) { farfree(ptr); } } #else /* Not the Borland DOS special memory handler */ /* Allocate memory for a png_struct or a png_info. The malloc and memset can be replaced by a single call to calloc() if this is thought to improve performance noticably. */ png_voidp /* PRIVATE */ png_create_struct(int type) { #ifdef PNG_USER_MEM_SUPPORTED return (png_create_struct_2(type, NULL, NULL)); } /* Allocate memory for a png_struct or a png_info. The malloc and memset can be replaced by a single call to calloc() if this is thought to improve performance noticably. */ png_voidp /* PRIVATE */ png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) { #endif /* PNG_USER_MEM_SUPPORTED */ png_size_t size; png_voidp struct_ptr; if (type == PNG_STRUCT_INFO) size = png_sizeof(png_info); else if (type == PNG_STRUCT_PNG) size = png_sizeof(png_struct); else return (NULL); #ifdef PNG_USER_MEM_SUPPORTED if (malloc_fn != NULL) { png_struct dummy_struct; png_structp png_ptr = &dummy_struct; png_ptr->mem_ptr=mem_ptr; struct_ptr = (*(malloc_fn))(png_ptr, size); if (struct_ptr != NULL) png_memset(struct_ptr, 0, size); return (struct_ptr); } #endif /* PNG_USER_MEM_SUPPORTED */ #if defined(__TURBOC__) && !defined(__FLAT__) struct_ptr = (png_voidp)farmalloc(size); #else # if defined(_MSC_VER) && defined(MAXSEG_64K) struct_ptr = (png_voidp)halloc(size, 1); # else struct_ptr = (png_voidp)malloc(size); # endif #endif if (struct_ptr != NULL) png_memset(struct_ptr, 0, size); return (struct_ptr); } /* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct(png_voidp struct_ptr) { #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2(struct_ptr, NULL, NULL); } /* Free memory allocated by a png_create_struct() call */ void /* PRIVATE */ png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, png_voidp mem_ptr) { #endif /* PNG_USER_MEM_SUPPORTED */ if (struct_ptr != NULL) { #ifdef PNG_USER_MEM_SUPPORTED if (free_fn != NULL) { png_struct dummy_struct; png_structp png_ptr = &dummy_struct; png_ptr->mem_ptr=mem_ptr; (*(free_fn))(png_ptr, struct_ptr); return; } #endif /* PNG_USER_MEM_SUPPORTED */ #if defined(__TURBOC__) && !defined(__FLAT__) farfree(struct_ptr); #else # if defined(_MSC_VER) && defined(MAXSEG_64K) hfree(struct_ptr); # else free(struct_ptr); # endif #endif } } /* Allocate memory. For reasonable files, size should never exceed * 64K. However, zlib may allocate more then 64K if you don't tell * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. */ png_voidp PNGAPI png_calloc(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; ret = (png_malloc(png_ptr, size)); if (ret != NULL) png_memset(ret,0,(png_size_t)size); return (ret); } png_voidp PNGAPI png_malloc(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr == NULL || size == 0) return (NULL); if (png_ptr->malloc_fn != NULL) ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); else ret = (png_malloc_default(png_ptr, size)); if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out of Memory"); return (ret); } png_voidp PNGAPI png_malloc_default(png_structp png_ptr, png_alloc_size_t size) { png_voidp ret; #endif /* PNG_USER_MEM_SUPPORTED */ if (png_ptr == NULL || size == 0) return (NULL); #ifdef PNG_MAX_MALLOC_64K if (size > (png_uint_32)65536L) { #ifndef PNG_USER_MEM_SUPPORTED if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Cannot Allocate > 64K"); else #endif return NULL; } #endif /* Check for overflow */ #if defined(__TURBOC__) && !defined(__FLAT__) if (size != (unsigned long)size) ret = NULL; else ret = farmalloc(size); #else # if defined(_MSC_VER) && defined(MAXSEG_64K) if (size != (unsigned long)size) ret = NULL; else ret = halloc(size, 1); # else if (size != (size_t)size) ret = NULL; else ret = malloc((size_t)size); # endif #endif #ifndef PNG_USER_MEM_SUPPORTED if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) png_error(png_ptr, "Out of Memory"); #endif return (ret); } /* Free a pointer allocated by png_malloc(). If ptr is NULL, return * without taking any action. */ void PNGAPI png_free(png_structp png_ptr, png_voidp ptr) { if (png_ptr == NULL || ptr == NULL) return; #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr->free_fn != NULL) { (*(png_ptr->free_fn))(png_ptr, ptr); return; } else png_free_default(png_ptr, ptr); } void PNGAPI png_free_default(png_structp png_ptr, png_voidp ptr) { if (png_ptr == NULL || ptr == NULL) return; #endif /* PNG_USER_MEM_SUPPORTED */ #if defined(__TURBOC__) && !defined(__FLAT__) farfree(ptr); #else # if defined(_MSC_VER) && defined(MAXSEG_64K) hfree(ptr); # else free(ptr); # endif #endif } #endif /* Not Borland DOS special memory handler */ /* This function was added at libpng version 1.2.3. The png_malloc_warn() * function will set up png_malloc() to issue a png_warning and return NULL * instead of issuing a png_error, if it fails to allocate the requested * memory. */ png_voidp PNGAPI png_malloc_warn(png_structp png_ptr, png_alloc_size_t size) { png_voidp ptr; png_uint_32 save_flags; if (png_ptr == NULL) return (NULL); save_flags = png_ptr->flags; png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); png_ptr->flags=save_flags; return(ptr); } #ifdef PNG_USER_MEM_SUPPORTED /* This function is called when the application wants to use another method * of allocating and freeing memory. */ void PNGAPI png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn) { if (png_ptr != NULL) { png_ptr->mem_ptr = mem_ptr; png_ptr->malloc_fn = malloc_fn; png_ptr->free_fn = free_fn; } } /* This function returns a pointer to the mem_ptr associated with the user * functions. The application should free any memory associated with this * pointer before png_write_destroy and png_read_destroy are called. */ png_voidp PNGAPI png_get_mem_ptr(png_structp png_ptr) { if (png_ptr == NULL) return (NULL); return ((png_voidp)png_ptr->mem_ptr); } #endif /* PNG_USER_MEM_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngpread.c000066400000000000000000001420731271037650300233340ustar00rootroot00000000000000 /* pngpread.c - read a png file in push mode * * Last changed in libpng 1.4.3 [June 26, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_PROGRESSIVE_READ_SUPPORTED #include "pngpriv.h" /* Push model modes */ #define PNG_READ_SIG_MODE 0 #define PNG_READ_CHUNK_MODE 1 #define PNG_READ_IDAT_MODE 2 #define PNG_SKIP_MODE 3 #define PNG_READ_tEXt_MODE 4 #define PNG_READ_zTXt_MODE 5 #define PNG_READ_DONE_MODE 6 #define PNG_READ_iTXt_MODE 7 #define PNG_ERROR_MODE 8 void PNGAPI png_process_data(png_structp png_ptr, png_infop info_ptr, png_bytep buffer, png_size_t buffer_size) { if (png_ptr == NULL || info_ptr == NULL) return; png_push_restore_buffer(png_ptr, buffer, buffer_size); while (png_ptr->buffer_size) { png_process_some_data(png_ptr, info_ptr); } } /* What we do with the incoming data depends on what we were previously * doing before we ran out of data... */ void /* PRIVATE */ png_process_some_data(png_structp png_ptr, png_infop info_ptr) { if (png_ptr == NULL) return; switch (png_ptr->process_mode) { case PNG_READ_SIG_MODE: { png_push_read_sig(png_ptr, info_ptr); break; } case PNG_READ_CHUNK_MODE: { png_push_read_chunk(png_ptr, info_ptr); break; } case PNG_READ_IDAT_MODE: { png_push_read_IDAT(png_ptr); break; } #ifdef PNG_READ_tEXt_SUPPORTED case PNG_READ_tEXt_MODE: { png_push_read_tEXt(png_ptr, info_ptr); break; } #endif #ifdef PNG_READ_zTXt_SUPPORTED case PNG_READ_zTXt_MODE: { png_push_read_zTXt(png_ptr, info_ptr); break; } #endif #ifdef PNG_READ_iTXt_SUPPORTED case PNG_READ_iTXt_MODE: { png_push_read_iTXt(png_ptr, info_ptr); break; } #endif case PNG_SKIP_MODE: { png_push_crc_finish(png_ptr); break; } default: { png_ptr->buffer_size = 0; break; } } } /* Read any remaining signature bytes from the stream and compare them with * the correct PNG signature. It is possible that this routine is called * with bytes already read from the signature, either because they have been * checked by the calling application, or because of multiple calls to this * routine. */ void /* PRIVATE */ png_push_read_sig(png_structp png_ptr, png_infop info_ptr) { png_size_t num_checked = png_ptr->sig_bytes, num_to_check = 8 - num_checked; if (png_ptr->buffer_size < num_to_check) { num_to_check = png_ptr->buffer_size; } png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) { if (num_checked < 4 && png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) png_error(png_ptr, "Not a PNG file"); else png_error(png_ptr, "PNG file corrupted by ASCII conversion"); } else { if (png_ptr->sig_bytes >= 8) { png_ptr->process_mode = PNG_READ_CHUNK_MODE; } } } void /* PRIVATE */ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) { PNG_IHDR; PNG_IDAT; PNG_IEND; PNG_PLTE; #ifdef PNG_READ_bKGD_SUPPORTED PNG_bKGD; #endif #ifdef PNG_READ_cHRM_SUPPORTED PNG_cHRM; #endif #ifdef PNG_READ_gAMA_SUPPORTED PNG_gAMA; #endif #ifdef PNG_READ_hIST_SUPPORTED PNG_hIST; #endif #ifdef PNG_READ_iCCP_SUPPORTED PNG_iCCP; #endif #ifdef PNG_READ_iTXt_SUPPORTED PNG_iTXt; #endif #ifdef PNG_READ_oFFs_SUPPORTED PNG_oFFs; #endif #ifdef PNG_READ_pCAL_SUPPORTED PNG_pCAL; #endif #ifdef PNG_READ_pHYs_SUPPORTED PNG_pHYs; #endif #ifdef PNG_READ_sBIT_SUPPORTED PNG_sBIT; #endif #ifdef PNG_READ_sCAL_SUPPORTED PNG_sCAL; #endif #ifdef PNG_READ_sRGB_SUPPORTED PNG_sRGB; #endif #ifdef PNG_READ_sPLT_SUPPORTED PNG_sPLT; #endif #ifdef PNG_READ_tEXt_SUPPORTED PNG_tEXt; #endif #ifdef PNG_READ_tIME_SUPPORTED PNG_tIME; #endif #ifdef PNG_READ_tRNS_SUPPORTED PNG_tRNS; #endif #ifdef PNG_READ_zTXt_SUPPORTED PNG_zTXt; #endif /* First we make sure we have enough data for the 4 byte chunk name * and the 4 byte chunk length before proceeding with decoding the * chunk data. To fully decode each of these chunks, we also make * sure we have enough data in the buffer for the 4 byte CRC at the * end of every chunk (except IDAT, which is handled separately). */ if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) { png_byte chunk_length[4]; if (png_ptr->buffer_size < 8) { png_push_save_buffer(png_ptr); return; } png_push_fill_buffer(png_ptr, chunk_length, 4); png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); png_reset_crc(png_ptr); png_crc_read(png_ptr, png_ptr->chunk_name, 4); png_check_chunk_name(png_ptr, png_ptr->chunk_name); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; } if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) if (png_ptr->mode & PNG_AFTER_IDAT) png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) { if (png_ptr->push_length != 13) png_error(png_ptr, "Invalid IHDR length"); if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); } else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); png_ptr->process_mode = PNG_READ_DONE_MODE; png_push_have_end(png_ptr, info_ptr); } #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) png_ptr->mode |= PNG_HAVE_IDAT; png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) png_ptr->mode |= PNG_HAVE_PLTE; else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) { if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before IDAT"); else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && !(png_ptr->mode & PNG_HAVE_PLTE)) png_error(png_ptr, "Missing PLTE before IDAT"); } } #endif else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); } else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) { /* If we reach an IDAT chunk, this means we have read all of the * header chunks, and we can start reading the image (or if this * is called after the image has been read - we have an error). */ if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before IDAT"); else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && !(png_ptr->mode & PNG_HAVE_PLTE)) png_error(png_ptr, "Missing PLTE before IDAT"); if (png_ptr->mode & PNG_HAVE_IDAT) { if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) if (png_ptr->push_length == 0) return; if (png_ptr->mode & PNG_AFTER_IDAT) png_benign_error(png_ptr, "Too many IDATs found"); } png_ptr->idat_size = png_ptr->push_length; png_ptr->mode |= PNG_HAVE_IDAT; png_ptr->process_mode = PNG_READ_IDAT_MODE; png_push_have_info(png_ptr, info_ptr); png_ptr->zstream.avail_out = (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; png_ptr->zstream.next_out = png_ptr->row_buf; return; } #ifdef PNG_READ_gAMA_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_sBIT_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_cHRM_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_sRGB_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_iCCP_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_sPLT_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_tRNS_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_bKGD_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_hIST_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_pHYs_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_oFFs_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_pCAL_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_sCAL_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_tIME_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_tEXt_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_zTXt_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); } #endif #ifdef PNG_READ_iTXt_SUPPORTED else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); } #endif else { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); } png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; } void /* PRIVATE */ png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) { png_ptr->process_mode = PNG_SKIP_MODE; png_ptr->skip_length = skip; } void /* PRIVATE */ png_push_crc_finish(png_structp png_ptr) { if (png_ptr->skip_length && png_ptr->save_buffer_size) { png_size_t save_size; if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) save_size = (png_size_t)png_ptr->skip_length; else save_size = png_ptr->save_buffer_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); png_ptr->skip_length -= save_size; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } if (png_ptr->skip_length && png_ptr->current_buffer_size) { png_size_t save_size; if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) save_size = (png_size_t)png_ptr->skip_length; else save_size = png_ptr->current_buffer_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); png_ptr->skip_length -= save_size; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; } if (!png_ptr->skip_length) { if (png_ptr->buffer_size < 4) { png_push_save_buffer(png_ptr); return; } png_crc_finish(png_ptr, 0); png_ptr->process_mode = PNG_READ_CHUNK_MODE; } } void PNGAPI png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) { png_bytep ptr; if (png_ptr == NULL) return; ptr = buffer; if (png_ptr->save_buffer_size) { png_size_t save_size; if (length < png_ptr->save_buffer_size) save_size = length; else save_size = png_ptr->save_buffer_size; png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); length -= save_size; ptr += save_size; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } if (length && png_ptr->current_buffer_size) { png_size_t save_size; if (length < png_ptr->current_buffer_size) save_size = length; else save_size = png_ptr->current_buffer_size; png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; } } void /* PRIVATE */ png_push_save_buffer(png_structp png_ptr) { if (png_ptr->save_buffer_size) { if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) { png_size_t i, istop; png_bytep sp; png_bytep dp; istop = png_ptr->save_buffer_size; for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; i < istop; i++, sp++, dp++) { *dp = *sp; } } } if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > png_ptr->save_buffer_max) { png_size_t new_max; png_bytep old_buffer; if (png_ptr->save_buffer_size > PNG_SIZE_MAX - (png_ptr->current_buffer_size + 256)) { png_error(png_ptr, "Potential overflow of save_buffer"); } new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; old_buffer = png_ptr->save_buffer; png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, (png_size_t)new_max); if (png_ptr->save_buffer == NULL) { png_free(png_ptr, old_buffer); png_error(png_ptr, "Insufficient memory for save_buffer"); } png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); png_free(png_ptr, old_buffer); png_ptr->save_buffer_max = new_max; } if (png_ptr->current_buffer_size) { png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); png_ptr->save_buffer_size += png_ptr->current_buffer_size; png_ptr->current_buffer_size = 0; } png_ptr->save_buffer_ptr = png_ptr->save_buffer; png_ptr->buffer_size = 0; } void /* PRIVATE */ png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, png_size_t buffer_length) { png_ptr->current_buffer = buffer; png_ptr->current_buffer_size = buffer_length; png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; png_ptr->current_buffer_ptr = png_ptr->current_buffer; } void /* PRIVATE */ png_push_read_IDAT(png_structp png_ptr) { PNG_IDAT; if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) { png_byte chunk_length[4]; if (png_ptr->buffer_size < 8) { png_push_save_buffer(png_ptr); return; } png_push_fill_buffer(png_ptr, chunk_length, 4); png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); png_reset_crc(png_ptr); png_crc_read(png_ptr, png_ptr->chunk_name, 4); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) { png_ptr->process_mode = PNG_READ_CHUNK_MODE; if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) png_error(png_ptr, "Not enough compressed data"); return; } png_ptr->idat_size = png_ptr->push_length; } if (png_ptr->idat_size && png_ptr->save_buffer_size) { png_size_t save_size; if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) { save_size = (png_size_t)png_ptr->idat_size; /* Check for overflow */ if ((png_uint_32)save_size != png_ptr->idat_size) png_error(png_ptr, "save_size overflowed in pngpread"); } else save_size = png_ptr->save_buffer_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); png_ptr->idat_size -= save_size; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } if (png_ptr->idat_size && png_ptr->current_buffer_size) { png_size_t save_size; if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) { save_size = (png_size_t)png_ptr->idat_size; /* Check for overflow */ if ((png_uint_32)save_size != png_ptr->idat_size) png_error(png_ptr, "save_size overflowed in pngpread"); } else save_size = png_ptr->current_buffer_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); png_ptr->idat_size -= save_size; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; } if (!png_ptr->idat_size) { if (png_ptr->buffer_size < 4) { png_push_save_buffer(png_ptr); return; } png_crc_finish(png_ptr, 0); png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; png_ptr->mode |= PNG_AFTER_IDAT; } } void /* PRIVATE */ png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, png_size_t buffer_length) { /* The caller checks for a non-zero buffer length. */ if (!(buffer_length > 0) || buffer == NULL) png_error(png_ptr, "No IDAT data (internal error)"); /* This routine must process all the data it has been given * before returning, calling the row callback as required to * handle the uncompressed results. */ png_ptr->zstream.next_in = buffer; png_ptr->zstream.avail_in = (uInt)buffer_length; /* Keep going until the decompressed data is all processed * or the stream marked as finished. */ while (png_ptr->zstream.avail_in > 0 && !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) { int ret; /* We have data for zlib, but we must check that zlib * has somewhere to put the results. It doesn't matter * if we don't expect any results -- it may be the input * data is just the LZ end code. */ if (!(png_ptr->zstream.avail_out > 0)) { png_ptr->zstream.avail_out = (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; png_ptr->zstream.next_out = png_ptr->row_buf; } /* Using Z_SYNC_FLUSH here means that an unterminated * LZ stream can still be handled (a stream with a missing * end code), otherwise (Z_NO_FLUSH) a future zlib * implementation might defer output and, therefore, * change the current behavior. (See comments in inflate.c * for why this doesn't happen at present with zlib 1.2.5.) */ ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); /* Check for any failure before proceeding. */ if (ret != Z_OK && ret != Z_STREAM_END) { /* Terminate the decompression. */ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; /* This may be a truncated stream (missing or * damaged end code). Treat that as a warning. */ if (png_ptr->row_number >= png_ptr->num_rows || png_ptr->pass > 6) png_warning(png_ptr, "Truncated compressed data in IDAT"); else png_error(png_ptr, "Decompression error in IDAT"); /* Skip the check on unprocessed input */ return; } /* Did inflate output any data? */ if (png_ptr->zstream.next_out != png_ptr->row_buf) { /* Is this unexpected data after the last row? * If it is, artificially terminate the LZ output * here. */ if (png_ptr->row_number >= png_ptr->num_rows || png_ptr->pass > 6) { /* Extra data. */ png_warning(png_ptr, "Extra compressed data in IDAT"); png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; /* Do no more processing; skip the unprocessed * input check below. */ return; } /* Do we have a complete row? */ if (png_ptr->zstream.avail_out == 0) png_push_process_row(png_ptr); } /* And check for the end of the stream. */ if (ret == Z_STREAM_END) png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; } /* All the data should have been processed, if anything * is left at this point we have bytes of IDAT data * after the zlib end code. */ if (png_ptr->zstream.avail_in > 0) png_warning(png_ptr, "Extra compression data"); } void /* PRIVATE */ png_push_process_row(png_structp png_ptr) { png_ptr->row_info.color_type = png_ptr->color_type; png_ptr->row_info.width = png_ptr->iwidth; png_ptr->row_info.channels = png_ptr->channels; png_ptr->row_info.bit_depth = png_ptr->bit_depth; png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->row_info.width); png_read_filter_row(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->prev_row + 1, (int)(png_ptr->row_buf[0])); png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1); if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) png_do_read_transformations(png_ptr); #ifdef PNG_READ_INTERLACING_SUPPORTED /* Blow up interlaced rows to full size */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { if (png_ptr->pass < 6) /* old interface (pre-1.0.9): png_do_read_interlace(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); */ png_do_read_interlace(png_ptr); switch (png_ptr->pass) { case 0: { int i; for (i = 0; i < 8 && png_ptr->pass == 0; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ } if (png_ptr->pass == 2) /* Pass 1 might be empty */ { for (i = 0; i < 4 && png_ptr->pass == 2; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } if (png_ptr->pass == 4 && png_ptr->height <= 4) { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } if (png_ptr->pass == 6 && png_ptr->height <= 4) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } break; } case 1: { int i; for (i = 0; i < 8 && png_ptr->pass == 1; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } if (png_ptr->pass == 2) /* Skip top 4 generated rows */ { for (i = 0; i < 4 && png_ptr->pass == 2; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } break; } case 2: { int i; for (i = 0; i < 4 && png_ptr->pass == 2; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } for (i = 0; i < 4 && png_ptr->pass == 2; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } if (png_ptr->pass == 4) /* Pass 3 might be empty */ { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } break; } case 3: { int i; for (i = 0; i < 4 && png_ptr->pass == 3; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } if (png_ptr->pass == 4) /* Skip top two generated rows */ { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } break; } case 4: { int i; for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } if (png_ptr->pass == 6) /* Pass 5 might be empty */ { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } break; } case 5: { int i; for (i = 0; i < 2 && png_ptr->pass == 5; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } if (png_ptr->pass == 6) /* Skip top generated row */ { png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } break; } case 6: { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); if (png_ptr->pass != 6) break; png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } } else #endif { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } } void /* PRIVATE */ png_read_push_finish_row(png_structp png_ptr) { /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; /* Start of interlace block in the y direction */ PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; /* Offset to next interlace block in the y direction */ PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; /* Height of interlace block. This is not currently used - if you need * it, uncomment it here and in png.h PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; */ png_ptr->row_number++; if (png_ptr->row_number < png_ptr->num_rows) return; #ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { png_ptr->row_number = 0; png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); do { png_ptr->pass++; if ((png_ptr->pass == 1 && png_ptr->width < 5) || (png_ptr->pass == 3 && png_ptr->width < 3) || (png_ptr->pass == 5 && png_ptr->width < 2)) png_ptr->pass++; if (png_ptr->pass > 7) png_ptr->pass--; if (png_ptr->pass >= 7) break; png_ptr->iwidth = (png_ptr->width + png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; if (png_ptr->transformations & PNG_INTERLACE) break; png_ptr->num_rows = (png_ptr->height + png_pass_yinc[png_ptr->pass] - 1 - png_pass_ystart[png_ptr->pass]) / png_pass_yinc[png_ptr->pass]; } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); } #endif /* PNG_READ_INTERLACING_SUPPORTED */ } #ifdef PNG_READ_tEXt_SUPPORTED void /* PRIVATE */ png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) { png_error(png_ptr, "Out of place tEXt"); info_ptr = info_ptr; /* To quiet some compiler warnings */ } #ifdef PNG_MAX_MALLOC_64K png_ptr->skip_length = 0; /* This may not be necessary */ if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ { png_warning(png_ptr, "tEXt chunk too large to fit in memory"); png_ptr->skip_length = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_ptr->current_text = (png_charp)png_malloc(png_ptr, (png_size_t)(length + 1)); png_ptr->current_text[length] = '\0'; png_ptr->current_text_ptr = png_ptr->current_text; png_ptr->current_text_size = (png_size_t)length; png_ptr->current_text_left = (png_size_t)length; png_ptr->process_mode = PNG_READ_tEXt_MODE; } void /* PRIVATE */ png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) { if (png_ptr->buffer_size && png_ptr->current_text_left) { png_size_t text_size; if (png_ptr->buffer_size < png_ptr->current_text_left) text_size = png_ptr->buffer_size; else text_size = png_ptr->current_text_left; png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); png_ptr->current_text_left -= text_size; png_ptr->current_text_ptr += text_size; } if (!(png_ptr->current_text_left)) { png_textp text_ptr; png_charp text; png_charp key; int ret; if (png_ptr->buffer_size < 4) { png_push_save_buffer(png_ptr); return; } png_push_crc_finish(png_ptr); #ifdef PNG_MAX_MALLOC_64K if (png_ptr->skip_length) return; #endif key = png_ptr->current_text; for (text = key; *text; text++) /* Empty loop */ ; if (text < key + png_ptr->current_text_size) text++; text_ptr = (png_textp)png_malloc(png_ptr, png_sizeof(png_text)); text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; text_ptr->key = key; #ifdef PNG_iTXt_SUPPORTED text_ptr->lang = NULL; text_ptr->lang_key = NULL; #endif text_ptr->text = text; ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_free(png_ptr, key); png_free(png_ptr, text_ptr); png_ptr->current_text = NULL; if (ret) png_warning(png_ptr, "Insufficient memory to store text chunk"); } } #endif #ifdef PNG_READ_zTXt_SUPPORTED void /* PRIVATE */ png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) { png_error(png_ptr, "Out of place zTXt"); info_ptr = info_ptr; /* To quiet some compiler warnings */ } #ifdef PNG_MAX_MALLOC_64K /* We can't handle zTXt chunks > 64K, since we don't have enough space * to be able to store the uncompressed data. Actually, the threshold * is probably around 32K, but it isn't as definite as 64K is. */ if (length > (png_uint_32)65535L) { png_warning(png_ptr, "zTXt chunk too large to fit in memory"); png_push_crc_skip(png_ptr, length); return; } #endif png_ptr->current_text = (png_charp)png_malloc(png_ptr, (png_size_t)(length + 1)); png_ptr->current_text[length] = '\0'; png_ptr->current_text_ptr = png_ptr->current_text; png_ptr->current_text_size = (png_size_t)length; png_ptr->current_text_left = (png_size_t)length; png_ptr->process_mode = PNG_READ_zTXt_MODE; } void /* PRIVATE */ png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) { if (png_ptr->buffer_size && png_ptr->current_text_left) { png_size_t text_size; if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) text_size = png_ptr->buffer_size; else text_size = png_ptr->current_text_left; png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); png_ptr->current_text_left -= text_size; png_ptr->current_text_ptr += text_size; } if (!(png_ptr->current_text_left)) { png_textp text_ptr; png_charp text; png_charp key; int ret; png_size_t text_size, key_size; if (png_ptr->buffer_size < 4) { png_push_save_buffer(png_ptr); return; } png_push_crc_finish(png_ptr); key = png_ptr->current_text; for (text = key; *text; text++) /* Empty loop */ ; /* zTXt can't have zero text */ if (text >= key + png_ptr->current_text_size) { png_ptr->current_text = NULL; png_free(png_ptr, key); return; } text++; if (*text != PNG_TEXT_COMPRESSION_zTXt) /* Check compression byte */ { png_ptr->current_text = NULL; png_free(png_ptr, key); return; } text++; png_ptr->zstream.next_in = (png_bytep )text; png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - (text - key)); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; key_size = text - key; text_size = 0; text = NULL; ret = Z_STREAM_END; while (png_ptr->zstream.avail_in) { ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) { inflateReset(&png_ptr->zstream); png_ptr->zstream.avail_in = 0; png_ptr->current_text = NULL; png_free(png_ptr, key); png_free(png_ptr, text); return; } if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) { if (text == NULL) { text = (png_charp)png_malloc(png_ptr, (png_ptr->zbuf_size - png_ptr->zstream.avail_out + key_size + 1)); png_memcpy(text + key_size, png_ptr->zbuf, png_ptr->zbuf_size - png_ptr->zstream.avail_out); png_memcpy(text, key, key_size); text_size = key_size + png_ptr->zbuf_size - png_ptr->zstream.avail_out; *(text + text_size) = '\0'; } else { png_charp tmp; tmp = text; text = (png_charp)png_malloc(png_ptr, text_size + (png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); png_memcpy(text, tmp, text_size); png_free(png_ptr, tmp); png_memcpy(text + text_size, png_ptr->zbuf, png_ptr->zbuf_size - png_ptr->zstream.avail_out); text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; *(text + text_size) = '\0'; } if (ret != Z_STREAM_END) { png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; } } else { break; } if (ret == Z_STREAM_END) break; } inflateReset(&png_ptr->zstream); png_ptr->zstream.avail_in = 0; if (ret != Z_STREAM_END) { png_ptr->current_text = NULL; png_free(png_ptr, key); png_free(png_ptr, text); return; } png_ptr->current_text = NULL; png_free(png_ptr, key); key = text; text += key_size; text_ptr = (png_textp)png_malloc(png_ptr, png_sizeof(png_text)); text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; text_ptr->key = key; #ifdef PNG_iTXt_SUPPORTED text_ptr->lang = NULL; text_ptr->lang_key = NULL; #endif text_ptr->text = text; ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_free(png_ptr, key); png_free(png_ptr, text_ptr); if (ret) png_warning(png_ptr, "Insufficient memory to store text chunk"); } } #endif #ifdef PNG_READ_iTXt_SUPPORTED void /* PRIVATE */ png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) { png_error(png_ptr, "Out of place iTXt"); info_ptr = info_ptr; /* To quiet some compiler warnings */ } #ifdef PNG_MAX_MALLOC_64K png_ptr->skip_length = 0; /* This may not be necessary */ if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ { png_warning(png_ptr, "iTXt chunk too large to fit in memory"); png_ptr->skip_length = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_ptr->current_text = (png_charp)png_malloc(png_ptr, (png_size_t)(length + 1)); png_ptr->current_text[length] = '\0'; png_ptr->current_text_ptr = png_ptr->current_text; png_ptr->current_text_size = (png_size_t)length; png_ptr->current_text_left = (png_size_t)length; png_ptr->process_mode = PNG_READ_iTXt_MODE; } void /* PRIVATE */ png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) { if (png_ptr->buffer_size && png_ptr->current_text_left) { png_size_t text_size; if (png_ptr->buffer_size < png_ptr->current_text_left) text_size = png_ptr->buffer_size; else text_size = png_ptr->current_text_left; png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); png_ptr->current_text_left -= text_size; png_ptr->current_text_ptr += text_size; } if (!(png_ptr->current_text_left)) { png_textp text_ptr; png_charp key; int comp_flag; png_charp lang; png_charp lang_key; png_charp text; int ret; if (png_ptr->buffer_size < 4) { png_push_save_buffer(png_ptr); return; } png_push_crc_finish(png_ptr); #ifdef PNG_MAX_MALLOC_64K if (png_ptr->skip_length) return; #endif key = png_ptr->current_text; for (lang = key; *lang; lang++) /* Empty loop */ ; if (lang < key + png_ptr->current_text_size - 3) lang++; comp_flag = *lang++; lang++; /* Skip comp_type, always zero */ for (lang_key = lang; *lang_key; lang_key++) /* Empty loop */ ; lang_key++; /* Skip NUL separator */ text=lang_key; if (lang_key < key + png_ptr->current_text_size - 1) { for (; *text; text++) /* Empty loop */ ; } if (text < key + png_ptr->current_text_size) text++; text_ptr = (png_textp)png_malloc(png_ptr, png_sizeof(png_text)); text_ptr->compression = comp_flag + 2; text_ptr->key = key; text_ptr->lang = lang; text_ptr->lang_key = lang_key; text_ptr->text = text; text_ptr->text_length = 0; text_ptr->itxt_length = png_strlen(text); ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_ptr->current_text = NULL; png_free(png_ptr, text_ptr); if (ret) png_warning(png_ptr, "Insufficient memory to store iTXt chunk"); } } #endif /* This function is called when we haven't found a handler for this * chunk. If there isn't a problem with the chunk itself (ie a bad chunk * name or a critical chunk), the chunk is (currently) silently ignored. */ void /* PRIVATE */ png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_uint_32 skip = 0; if (!(png_ptr->chunk_name[0] & 0x20)) { #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != PNG_HANDLE_CHUNK_ALWAYS #ifdef PNG_READ_USER_CHUNKS_SUPPORTED && png_ptr->read_user_chunk_fn == NULL #endif ) #endif png_chunk_error(png_ptr, "unknown critical chunk"); info_ptr = info_ptr; /* To quiet some compiler warnings */ } #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) { #ifdef PNG_MAX_MALLOC_64K if (length > (png_uint_32)65535L) { png_warning(png_ptr, "unknown chunk too large to fit in memory"); skip = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_memcpy((png_charp)png_ptr->unknown_chunk.name, (png_charp)png_ptr->chunk_name, png_sizeof(png_ptr->unknown_chunk.name)); png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name) - 1] = '\0'; png_ptr->unknown_chunk.size = (png_size_t)length; if (length == 0) png_ptr->unknown_chunk.data = NULL; else { png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, (png_size_t)length); png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); } #ifdef PNG_READ_USER_CHUNKS_SUPPORTED if (png_ptr->read_user_chunk_fn != NULL) { /* Callback to user unknown chunk handler */ int ret; ret = (*(png_ptr->read_user_chunk_fn)) (png_ptr, &png_ptr->unknown_chunk); if (ret < 0) png_chunk_error(png_ptr, "error in user chunk"); if (ret == 0) { if (!(png_ptr->chunk_name[0] & 0x20)) if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != PNG_HANDLE_CHUNK_ALWAYS) png_chunk_error(png_ptr, "unknown critical chunk"); png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); } } else #endif png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); png_free(png_ptr, png_ptr->unknown_chunk.data); png_ptr->unknown_chunk.data = NULL; } else #endif skip=length; png_push_crc_skip(png_ptr, skip); } void /* PRIVATE */ png_push_have_info(png_structp png_ptr, png_infop info_ptr) { if (png_ptr->info_fn != NULL) (*(png_ptr->info_fn))(png_ptr, info_ptr); } void /* PRIVATE */ png_push_have_end(png_structp png_ptr, png_infop info_ptr) { if (png_ptr->end_fn != NULL) (*(png_ptr->end_fn))(png_ptr, info_ptr); } void /* PRIVATE */ png_push_have_row(png_structp png_ptr, png_bytep row) { if (png_ptr->row_fn != NULL) (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, (int)png_ptr->pass); } void PNGAPI png_progressive_combine_row (png_structp png_ptr, png_bytep old_row, png_bytep new_row) { PNG_CONST int FARDATA png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; if (png_ptr == NULL) return; if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); } void PNGAPI png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn) { if (png_ptr == NULL) return; png_ptr->info_fn = info_fn; png_ptr->row_fn = row_fn; png_ptr->end_fn = end_fn; png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); } png_voidp PNGAPI png_get_progressive_ptr(png_structp png_ptr) { if (png_ptr == NULL) return (NULL); return png_ptr->io_ptr; } #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngpriv.h000066400000000000000000001064711271037650300232300ustar00rootroot00000000000000 /* pngpriv.h - private declarations for use inside libpng * * libpng version 1.4.4 - September 23, 2010 * For conditions of distribution and use, see copyright notice in png.h * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ /* The symbols declared in this file (including the functions declared * as PNG_EXTERN) are PRIVATE. They are not part of the libpng public * interface, and are not recommended for use by regular applications. * Some of them may become public in the future; others may stay private, * change in an incompatible way, or even disappear. * Although the libpng users are not forbidden to include this header, * they should be well aware of the issues that may arise from doing so. */ #ifndef PNGPRIV_H #define PNGPRIV_H #ifndef PNG_VERSION_INFO_ONLY #include #ifndef PNG_EXTERN /* The functions exported by PNG_EXTERN are internal functions, which * aren't usually used outside the library (as far as I know), so it is * debatable if they should be exported at all. In the future, when it * is possible to have run-time registry of chunk-handling functions, * some of these will be made available again. # define PNG_EXTERN extern */ # define PNG_EXTERN #endif /* Other defines specific to compilers can go here. Try to keep * them inside an appropriate ifdef/endif pair for portability. */ #ifdef PNG_FLOATING_POINT_SUPPORTED # ifdef MACOS /* We need to check that hasn't already been included earlier * as it seems it doesn't agree with , yet we should really use * if possible. */ # if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) # include # endif # else # include # endif # if defined(_AMIGA) && defined(__SASC) && defined(_M68881) /* Amiga SAS/C: We must include builtin FPU functions when compiling using * MATH=68881 */ # include # endif #endif /* Codewarrior on NT has linking problems without this. */ #if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) # define PNG_ALWAYS_EXTERN #endif /* This provides the non-ANSI (far) memory allocation routines. */ #if defined(__TURBOC__) && defined(__MSDOS__) # include # include #endif #if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \ defined(_WIN32) || defined(__WIN32__) # include /* defines _WINDOWS_ macro */ #endif /* Various modes of operation. Note that after an init, mode is set to * zero automatically when the structure is created. */ #define PNG_HAVE_IHDR 0x01 #define PNG_HAVE_PLTE 0x02 #define PNG_HAVE_IDAT 0x04 #define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ #define PNG_HAVE_IEND 0x10 #define PNG_HAVE_gAMA 0x20 #define PNG_HAVE_cHRM 0x40 #define PNG_HAVE_sRGB 0x80 #define PNG_HAVE_CHUNK_HEADER 0x100 #define PNG_WROTE_tIME 0x200 #define PNG_WROTE_INFO_BEFORE_PLTE 0x400 #define PNG_BACKGROUND_IS_GRAY 0x800 #define PNG_HAVE_PNG_SIGNATURE 0x1000 #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ /* Flags for the transformations the PNG library does on the image data */ #define PNG_BGR 0x0001 #define PNG_INTERLACE 0x0002 #define PNG_PACK 0x0004 #define PNG_SHIFT 0x0008 #define PNG_SWAP_BYTES 0x0010 #define PNG_INVERT_MONO 0x0020 #define PNG_QUANTIZE 0x0040 /* formerly PNG_DITHER */ #define PNG_BACKGROUND 0x0080 #define PNG_BACKGROUND_EXPAND 0x0100 /* 0x0200 unused */ #define PNG_16_TO_8 0x0400 #define PNG_RGBA 0x0800 #define PNG_EXPAND 0x1000 #define PNG_GAMMA 0x2000 #define PNG_GRAY_TO_RGB 0x4000 #define PNG_FILLER 0x8000L #define PNG_PACKSWAP 0x10000L #define PNG_SWAP_ALPHA 0x20000L #define PNG_STRIP_ALPHA 0x40000L #define PNG_INVERT_ALPHA 0x80000L #define PNG_USER_TRANSFORM 0x100000L #define PNG_RGB_TO_GRAY_ERR 0x200000L #define PNG_RGB_TO_GRAY_WARN 0x400000L #define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ /* 0x800000L Unused */ #define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ #define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ /* 0x4000000L unused */ /* 0x8000000L unused */ /* 0x10000000L unused */ /* 0x20000000L unused */ /* 0x40000000L unused */ /* Flags for png_create_struct */ #define PNG_STRUCT_PNG 0x0001 #define PNG_STRUCT_INFO 0x0002 /* Scaling factor for filter heuristic weighting calculations */ #define PNG_WEIGHT_SHIFT 8 #define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) #define PNG_COST_SHIFT 3 #define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) /* Flags for the png_ptr->flags rather than declaring a byte for each one */ #define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 #define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 #define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 #define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 #define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 #define PNG_FLAG_ZLIB_FINISHED 0x0020 #define PNG_FLAG_ROW_INIT 0x0040 #define PNG_FLAG_FILLER_AFTER 0x0080 #define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 #define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 #define PNG_FLAG_CRC_CRITICAL_USE 0x0400 #define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 /* 0x1000 unused */ /* 0x2000 unused */ /* 0x4000 unused */ #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L #define PNG_FLAG_LIBRARY_MISMATCH 0x20000L #define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L #define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L #define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ #define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ #define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000L /* Added to libpng-1.4.0 */ /* 0x1000000L unused */ /* 0x2000000L unused */ /* 0x4000000L unused */ /* 0x8000000L unused */ /* 0x10000000L unused */ /* 0x20000000L unused */ /* 0x40000000L unused */ #define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ PNG_FLAG_CRC_ANCILLARY_NOWARN) #define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ PNG_FLAG_CRC_CRITICAL_IGNORE) #define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ PNG_FLAG_CRC_CRITICAL_MASK) /* Save typing and make code easier to understand */ #define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ abs((int)((c1).green) - (int)((c2).green)) + \ abs((int)((c1).blue) - (int)((c2).blue))) /* Added to libpng-1.2.6 JB */ #define PNG_ROWBYTES(pixel_bits, width) \ ((pixel_bits) >= 8 ? \ ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) /* PNG_OUT_OF_RANGE returns true if value is outside the range * ideal-delta..ideal+delta. Each argument is evaluated twice. * "ideal" and "delta" should be constants, normally simple * integers, "value" a variable. Added to libpng-1.2.6 JB */ #define PNG_OUT_OF_RANGE(value, ideal, delta) \ ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) /* Constant strings for known chunk types. If you need to add a chunk, * define the name here, and add an invocation of the macro wherever it's * needed. */ #define PNG_IHDR PNG_CONST png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} #define PNG_IDAT PNG_CONST png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} #define PNG_IEND PNG_CONST png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} #define PNG_PLTE PNG_CONST png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} #define PNG_bKGD PNG_CONST png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} #define PNG_cHRM PNG_CONST png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} #define PNG_gAMA PNG_CONST png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} #define PNG_hIST PNG_CONST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} #define PNG_iCCP PNG_CONST png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} #define PNG_iTXt PNG_CONST png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} #define PNG_oFFs PNG_CONST png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} #define PNG_pCAL PNG_CONST png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} #define PNG_sCAL PNG_CONST png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} #define PNG_pHYs PNG_CONST png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} #define PNG_sBIT PNG_CONST png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} #define PNG_sPLT PNG_CONST png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} #define PNG_sRGB PNG_CONST png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} #define PNG_sTER PNG_CONST png_byte png_sTER[5] = {115, 84, 69, 82, '\0'} #define PNG_tEXt PNG_CONST png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} #define PNG_tIME PNG_CONST png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} #define PNG_tRNS PNG_CONST png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} #define PNG_zTXt PNG_CONST png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} /* Inhibit C++ name-mangling for libpng functions but not for system calls. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* These functions are used internally in the code. They generally * shouldn't be used unless you are writing code to add or replace some * functionality in libpng. More information about most functions can * be found in the files where the functions are located. */ /* Allocate memory for an internal libpng struct */ PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); /* Free memory from internal libpng struct */ PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)); PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, png_free_ptr free_fn, png_voidp mem_ptr)); /* Free any memory that info_ptr points to and reset struct. */ PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, png_infop info_ptr)); /* Function to allocate memory for zlib. PNGAPI is disallowed. */ PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); /* Function to free memory for zlib. PNGAPI is disallowed. */ PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); /* Next four functions are used internally as callbacks. PNGAPI is required * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, png_bytep buffer, png_size_t length)); #endif PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); #ifdef PNG_WRITE_FLUSH_SUPPORTED #ifdef PNG_STDIO_SUPPORTED PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); #endif #endif /* Reset the CRC variable */ PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); /* Write the "data" buffer to whatever output you are using */ PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); /* Read the chunk header (length + type name) */ PNG_EXTERN png_uint_32 png_read_chunk_header PNGARG((png_structp png_ptr)); /* Read data from whatever input you are using into the "data" buffer */ PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); /* Read bytes into buf, and update png_ptr->crc */ PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, png_size_t length)); /* Decompress data in a chunk that uses compression */ #if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, int comp_type, png_size_t chunklength, png_size_t prefix_length, png_size_t *data_length)); #endif /* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); /* Read the CRC from the file and compare it to the libpng calculated CRC */ PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); /* Calculate the CRC over a section of data. Note that we are only * passing a maximum of 64K on systems that have this as a memory limit, * since this is the maximum buffer size we can specify. */ PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, png_size_t length)); #ifdef PNG_WRITE_FLUSH_SUPPORTED PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); #endif /* Write various chunks */ /* Write the IHDR chunk, and update the png_struct with the necessary * information. */ PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int compression_method, int filter_method, int interlace_method)); PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, png_uint_32 num_pal)); PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)); PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); #ifdef PNG_WRITE_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); #endif #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point file_gamma)); #endif #endif #ifdef PNG_WRITE_sBIT_SUPPORTED PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, int color_type)); #endif #ifdef PNG_WRITE_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, double white_x, double white_y, double red_x, double red_y, double green_x, double green_y, double blue_x, double blue_y)); #endif PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, png_fixed_point int_blue_y)); #endif #ifdef PNG_WRITE_sRGB_SUPPORTED PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, int intent)); #endif #ifdef PNG_WRITE_iCCP_SUPPORTED PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, png_charp name, int compression_type, png_charp profile, int proflen)); /* Note to maintainer: profile should be png_bytep */ #endif #ifdef PNG_WRITE_sPLT_SUPPORTED PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, png_sPLT_tp palette)); #endif #ifdef PNG_WRITE_tRNS_SUPPORTED PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, png_color_16p values, int number, int color_type)); #endif #ifdef PNG_WRITE_bKGD_SUPPORTED PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, png_color_16p values, int color_type)); #endif #ifdef PNG_WRITE_hIST_SUPPORTED PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, int num_hist)); #endif #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, png_charp key, png_charpp new_key)); #endif #ifdef PNG_WRITE_tEXt_SUPPORTED PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, png_charp text, png_size_t text_len)); #endif #ifdef PNG_WRITE_zTXt_SUPPORTED PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, png_charp text, png_size_t text_len, int compression)); #endif #ifdef PNG_WRITE_iTXt_SUPPORTED PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, int compression, png_charp key, png_charp lang, png_charp lang_key, png_charp text)); #endif #ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, int num_text)); #endif #ifdef PNG_WRITE_oFFs_SUPPORTED PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, int unit_type)); #endif #ifdef PNG_WRITE_pCAL_SUPPORTED PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)); #endif #ifdef PNG_WRITE_pHYs_SUPPORTED PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, int unit_type)); #endif #ifdef PNG_WRITE_tIME_SUPPORTED PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, png_timep mod_time)); #endif #ifdef PNG_WRITE_sCAL_SUPPORTED #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, int unit, double width, double height)); #else #ifdef PNG_FIXED_POINT_SUPPORTED PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, int unit, png_charp width, png_charp height)); #endif #endif #endif /* Called when finished processing a row of data */ PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); /* Internal use only. Called before first row of data */ PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); #ifdef PNG_READ_GAMMA_SUPPORTED PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr, png_byte bit_depth)); #endif /* Combine a row of data, dealing with alpha, etc. if requested */ PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, int mask)); #ifdef PNG_READ_INTERLACING_SUPPORTED /* Expand an interlaced row */ /* OLD pre-1.0.9 interface: PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, png_bytep row, int pass, png_uint_32 transformations)); */ PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); #endif /* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Grab pixels out of a row for an interlaced pass */ PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, png_bytep row, int pass)); #endif /* Unfilter a row */ PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); /* Choose the best filter to use and filter the row data */ PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, png_row_infop row_info)); /* Write out the filtered row. */ PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, png_bytep filtered_row)); /* Finish a row while reading, dealing with interlacing passes, etc. */ PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); /* Initialize the row buffers, etc. */ PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); /* Optional call to update the users info structure */ PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, png_infop info_ptr)); /* These are the functions that do the transformations */ #ifdef PNG_READ_FILLER_SUPPORTED PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, png_bytep row, png_uint_32 filler, png_uint_32 flags)); #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, png_bytep row)); #endif #if defined(PNG_WRITE_FILLER_SUPPORTED) || \ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, png_bytep row, png_uint_32 flags)); #endif #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ defined(PNG_WRITE_PACKSWAP_SUPPORTED) PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_PACK_SUPPORTED PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_SHIFT_SUPPORTED PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, png_color_8p sig_bits)); #endif #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_16_TO_8_SUPPORTED PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED PNG_EXTERN void png_do_quantize PNGARG((png_row_infop row_info, png_bytep row, png_bytep palette_lookup, png_bytep quantize_lookup)); # ifdef PNG_CORRECT_PALETTE_SUPPORTED PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, png_colorp palette, int num_palette)); # endif #endif #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); #endif #ifdef PNG_WRITE_PACK_SUPPORTED PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)); #endif #ifdef PNG_WRITE_SHIFT_SUPPORTED PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, png_color_8p bit_depth)); #endif #ifdef PNG_READ_BACKGROUND_SUPPORTED #ifdef PNG_READ_GAMMA_SUPPORTED PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, png_color_16p trans_color, png_color_16p background, png_color_16p background_1, png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, png_uint_16pp gamma_16_to_1, int gamma_shift)); #else PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, png_color_16p trans_color, png_color_16p background)); #endif #endif #ifdef PNG_READ_GAMMA_SUPPORTED PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, png_bytep gamma_table, png_uint_16pp gamma_16_table, int gamma_shift)); #endif #ifdef PNG_READ_EXPAND_SUPPORTED PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, png_bytep row, png_color_16p trans_value)); #endif /* The following decodes the appropriate chunks, and does error correction, * then calls the appropriate callback for the chunk if it is valid. */ /* Decode the IHDR chunk */ PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #ifdef PNG_READ_bKGD_SUPPORTED PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_cHRM_SUPPORTED PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_gAMA_SUPPORTED PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_hIST_SUPPORTED PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_iCCP_SUPPORTED PNG_EXTERN void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif /* PNG_READ_iCCP_SUPPORTED */ #ifdef PNG_READ_iTXt_SUPPORTED PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_oFFs_SUPPORTED PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_pCAL_SUPPORTED PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_pHYs_SUPPORTED PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_sBIT_SUPPORTED PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_sCAL_SUPPORTED PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_sPLT_SUPPORTED PNG_EXTERN void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif /* PNG_READ_sPLT_SUPPORTED */ #ifdef PNG_READ_sRGB_SUPPORTED PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_tEXt_SUPPORTED PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_tIME_SUPPORTED PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_tRNS_SUPPORTED PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif #ifdef PNG_READ_zTXt_SUPPORTED PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); #endif PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, png_bytep chunk_name)); /* Handle the transformations for reading and writing */ PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, png_uint_32 length)); PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, png_bytep buffer, png_size_t buffer_length)); PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, png_bytep buffer, png_size_t buffer_length)); PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, png_infop info_ptr)); PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); #ifdef PNG_READ_tEXt_SUPPORTED PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif #ifdef PNG_READ_zTXt_SUPPORTED PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif #ifdef PNG_READ_iTXt_SUPPORTED PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, png_uint_32 length)); PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr)); #endif #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ #ifdef PNG_MNG_FEATURES_SUPPORTED PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, png_bytep row)); PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, png_bytep row)); #endif /* Added at libpng version 1.4.0 */ #ifdef PNG_cHRM_SUPPORTED PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, png_fixed_point int_blue_y)); #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_CHECK_cHRM_SUPPORTED /* Added at libpng version 1.2.34 and 1.4.0 */ PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2, unsigned long *hi_product, unsigned long *lo_product)); #endif #endif /* Added at libpng version 1.4.0 */ PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type)); /* Free all memory used by the read (old method - NOT DLL EXPORTED) */ PNG_EXTERN void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr)); /* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ PNG_EXTERN void png_write_destroy PNGARG((png_structp png_ptr)); #ifdef USE_FAR_KEYWORD /* memory model conversion function */ PNG_EXTERN void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, int check)); #endif /* USE_FAR_KEYWORD */ /* Define PNG_DEBUG at compile time for debugging information. Higher * numbers for PNG_DEBUG mean more debugging information. This has * only been added since version 0.95 so it is not implemented throughout * libpng yet, but more support will be added as needed. */ #ifdef PNG_DEBUG #if (PNG_DEBUG > 0) #if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) #include #if (PNG_DEBUG > 1) #ifndef _DEBUG # define _DEBUG #endif #ifndef png_debug #define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) #endif #ifndef png_debug1 #define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) #endif #ifndef png_debug2 #define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) #endif #endif #else /* PNG_DEBUG_FILE || !_MSC_VER */ #ifndef PNG_DEBUG_FILE #define PNG_DEBUG_FILE stderr #endif /* PNG_DEBUG_FILE */ #if (PNG_DEBUG > 1) /* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on * non-ISO compilers */ # ifdef __STDC__ # ifndef png_debug # define png_debug(l,m) \ { \ int num_tabs=l; \ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ } # endif # ifndef png_debug1 # define png_debug1(l,m,p1) \ { \ int num_tabs=l; \ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ } # endif # ifndef png_debug2 # define png_debug2(l,m,p1,p2) \ { \ int num_tabs=l; \ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ } # endif # else /* __STDC __ */ # ifndef png_debug # define png_debug(l,m) \ { \ int num_tabs=l; \ char format[256]; \ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ m,PNG_STRING_NEWLINE); \ fprintf(PNG_DEBUG_FILE,format); \ } # endif # ifndef png_debug1 # define png_debug1(l,m,p1) \ { \ int num_tabs=l; \ char format[256]; \ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ m,PNG_STRING_NEWLINE); \ fprintf(PNG_DEBUG_FILE,format,p1); \ } # endif # ifndef png_debug2 # define png_debug2(l,m,p1,p2) \ { \ int num_tabs=l; \ char format[256]; \ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ m,PNG_STRING_NEWLINE); \ fprintf(PNG_DEBUG_FILE,format,p1,p2); \ } # endif # endif /* __STDC __ */ #endif /* (PNG_DEBUG > 1) */ #endif /* _MSC_VER */ #endif /* (PNG_DEBUG > 0) */ #endif /* PNG_DEBUG */ #ifndef png_debug #define png_debug(l, m) #endif #ifndef png_debug1 #define png_debug1(l, m, p1) #endif #ifndef png_debug2 #define png_debug2(l, m, p1, p2) #endif /* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ #ifdef __cplusplus } #endif #endif /* PNG_VERSION_INFO_ONLY */ #endif /* PNGPRIV_H */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngread.c000066400000000000000000001245731271037650300231610ustar00rootroot00000000000000 /* pngread.c - read a PNG file * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file contains routines that an application calls directly to * read a PNG file or stream. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_READ_SUPPORTED #include "pngpriv.h" /* Create a PNG structure for reading, and allocate any memory needed. */ png_structp PNGAPI png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn) { #ifdef PNG_USER_MEM_SUPPORTED return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, warn_fn, NULL, NULL, NULL)); } /* Alternate create PNG structure for reading, and allocate any memory * needed. */ png_structp PNGAPI png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn) { #endif /* PNG_USER_MEM_SUPPORTED */ #ifdef PNG_SETJMP_SUPPORTED volatile #endif png_structp png_ptr; volatile int png_cleanup_needed = 0; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; #endif #endif int i; png_debug(1, "in png_create_read_struct"); #ifdef PNG_USER_MEM_SUPPORTED png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, malloc_fn, mem_ptr); #else png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); #endif if (png_ptr == NULL) return (NULL); /* Added at libpng-1.2.6 */ #ifdef PNG_USER_LIMITS_SUPPORTED png_ptr->user_width_max = PNG_USER_WIDTH_MAX; png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; # ifdef PNG_USER_CHUNK_CACHE_MAX /* Added at libpng-1.2.43 and 1.4.0 */ png_ptr->user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; # endif # ifdef PNG_SET_USER_CHUNK_MALLOC_MAX /* Added at libpng-1.2.43 and 1.4.1 */ png_ptr->user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; # endif #endif #ifdef PNG_SETJMP_SUPPORTED /* Applications that neglect to set up their own setjmp() and then encounter a png_error() will longjmp here. Since the jmpbuf is then meaningless we abort instead of returning. */ #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(png_ptr))) /* Sets longjmp to match setjmp */ #endif PNG_ABORT(); #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, png_sizeof(jmp_buf)); #endif #endif /* PNG_SETJMP_SUPPORTED */ #ifdef PNG_USER_MEM_SUPPORTED png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); #endif png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); if (user_png_ver) { i = 0; do { if (user_png_ver[i] != png_libpng_ver[i]) png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; } while (png_libpng_ver[i++]); } else png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) { /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so * we must recompile any applications that use any older library version. * For versions after libpng 1.0, we will be compatible, so we need * only check the first digit. */ if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || (user_png_ver[0] == '0' && user_png_ver[2] < '9')) { #ifdef PNG_STDIO_SUPPORTED char msg[80]; if (user_png_ver) { png_snprintf(msg, 80, "Application was compiled with png.h from libpng-%.20s", user_png_ver); png_warning(png_ptr, msg); } png_snprintf(msg, 80, "Application is running with png.c from libpng-%.20s", png_libpng_ver); png_warning(png_ptr, msg); #endif #ifdef PNG_ERROR_NUMBERS_SUPPORTED png_ptr->flags = 0; #endif png_warning(png_ptr, "Incompatible libpng version in application and library"); png_cleanup_needed = 1; } } if (!png_cleanup_needed) { /* Initialize zbuf - compression buffer */ png_ptr->zbuf_size = PNG_ZBUF_SIZE; png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, png_ptr->zbuf_size); if (png_ptr->zbuf == NULL) png_cleanup_needed = 1; } png_ptr->zstream.zalloc = png_zalloc; png_ptr->zstream.zfree = png_zfree; png_ptr->zstream.opaque = (voidpf)png_ptr; if (!png_cleanup_needed) { switch (inflateInit(&png_ptr->zstream)) { case Z_OK: /* Do nothing */ break; case Z_MEM_ERROR: case Z_STREAM_ERROR: png_warning(png_ptr, "zlib memory error"); png_cleanup_needed = 1; break; case Z_VERSION_ERROR: png_warning(png_ptr, "zlib version error"); png_cleanup_needed = 1; break; default: png_warning(png_ptr, "Unknown zlib error"); png_cleanup_needed = 1; } } if (png_cleanup_needed) { /* Clean up PNG structure and deallocate any memory. */ png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf = NULL; #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)png_ptr); #endif return (NULL); } png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; png_set_read_fn(png_ptr, NULL, NULL); return (png_ptr); } #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the information before the actual image data. This has been * changed in v0.90 to allow reading a file that already has the magic * bytes read from the stream. You can tell libpng how many bytes have * been read from the beginning of the stream (up to the maximum of 8) * via png_set_sig_bytes(), and we will only check the remaining bytes * here. The application can then have access to the signature bytes we * read if it is determined that this isn't a valid PNG file. */ void PNGAPI png_read_info(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_read_info"); if (png_ptr == NULL || info_ptr == NULL) return; /* If we haven't checked all of the PNG signature bytes, do so now. */ if (png_ptr->sig_bytes < 8) { png_size_t num_checked = png_ptr->sig_bytes, num_to_check = 8 - num_checked; #ifdef PNG_IO_STATE_SUPPORTED png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE; #endif png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); png_ptr->sig_bytes = 8; if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) { if (num_checked < 4 && png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) png_error(png_ptr, "Not a PNG file"); else png_error(png_ptr, "PNG file corrupted by ASCII conversion"); } if (num_checked < 3) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } for (;;) { PNG_IHDR; PNG_IDAT; PNG_IEND; PNG_PLTE; #ifdef PNG_READ_bKGD_SUPPORTED PNG_bKGD; #endif #ifdef PNG_READ_cHRM_SUPPORTED PNG_cHRM; #endif #ifdef PNG_READ_gAMA_SUPPORTED PNG_gAMA; #endif #ifdef PNG_READ_hIST_SUPPORTED PNG_hIST; #endif #ifdef PNG_READ_iCCP_SUPPORTED PNG_iCCP; #endif #ifdef PNG_READ_iTXt_SUPPORTED PNG_iTXt; #endif #ifdef PNG_READ_oFFs_SUPPORTED PNG_oFFs; #endif #ifdef PNG_READ_pCAL_SUPPORTED PNG_pCAL; #endif #ifdef PNG_READ_pHYs_SUPPORTED PNG_pHYs; #endif #ifdef PNG_READ_sBIT_SUPPORTED PNG_sBIT; #endif #ifdef PNG_READ_sCAL_SUPPORTED PNG_sCAL; #endif #ifdef PNG_READ_sPLT_SUPPORTED PNG_sPLT; #endif #ifdef PNG_READ_sRGB_SUPPORTED PNG_sRGB; #endif #ifdef PNG_READ_tEXt_SUPPORTED PNG_tEXt; #endif #ifdef PNG_READ_tIME_SUPPORTED PNG_tIME; #endif #ifdef PNG_READ_tRNS_SUPPORTED PNG_tRNS; #endif #ifdef PNG_READ_zTXt_SUPPORTED PNG_zTXt; #endif png_uint_32 length = png_read_chunk_header(png_ptr); PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; /* This should be a binary subdivision search or a hash for * matching the chunk name rather than a linear search. */ if (!png_memcmp(chunk_name, png_IDAT, 4)) if (png_ptr->mode & PNG_AFTER_IDAT) png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; if (!png_memcmp(chunk_name, png_IHDR, 4)) png_handle_IHDR(png_ptr, info_ptr, length); else if (!png_memcmp(chunk_name, png_IEND, 4)) png_handle_IEND(png_ptr, info_ptr, length); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else if (png_handle_as_unknown(png_ptr, chunk_name)) { if (!png_memcmp(chunk_name, png_IDAT, 4)) png_ptr->mode |= PNG_HAVE_IDAT; png_handle_unknown(png_ptr, info_ptr, length); if (!png_memcmp(chunk_name, png_PLTE, 4)) png_ptr->mode |= PNG_HAVE_PLTE; else if (!png_memcmp(chunk_name, png_IDAT, 4)) { if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before IDAT"); else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && !(png_ptr->mode & PNG_HAVE_PLTE)) png_error(png_ptr, "Missing PLTE before IDAT"); break; } } #endif else if (!png_memcmp(chunk_name, png_PLTE, 4)) png_handle_PLTE(png_ptr, info_ptr, length); else if (!png_memcmp(chunk_name, png_IDAT, 4)) { if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before IDAT"); else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && !(png_ptr->mode & PNG_HAVE_PLTE)) png_error(png_ptr, "Missing PLTE before IDAT"); png_ptr->idat_size = length; png_ptr->mode |= PNG_HAVE_IDAT; break; } #ifdef PNG_READ_bKGD_SUPPORTED else if (!png_memcmp(chunk_name, png_bKGD, 4)) png_handle_bKGD(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_cHRM_SUPPORTED else if (!png_memcmp(chunk_name, png_cHRM, 4)) png_handle_cHRM(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_gAMA_SUPPORTED else if (!png_memcmp(chunk_name, png_gAMA, 4)) png_handle_gAMA(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_hIST_SUPPORTED else if (!png_memcmp(chunk_name, png_hIST, 4)) png_handle_hIST(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_oFFs_SUPPORTED else if (!png_memcmp(chunk_name, png_oFFs, 4)) png_handle_oFFs(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_pCAL_SUPPORTED else if (!png_memcmp(chunk_name, png_pCAL, 4)) png_handle_pCAL(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sCAL_SUPPORTED else if (!png_memcmp(chunk_name, png_sCAL, 4)) png_handle_sCAL(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_pHYs_SUPPORTED else if (!png_memcmp(chunk_name, png_pHYs, 4)) png_handle_pHYs(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sBIT_SUPPORTED else if (!png_memcmp(chunk_name, png_sBIT, 4)) png_handle_sBIT(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sRGB_SUPPORTED else if (!png_memcmp(chunk_name, png_sRGB, 4)) png_handle_sRGB(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_iCCP_SUPPORTED else if (!png_memcmp(chunk_name, png_iCCP, 4)) png_handle_iCCP(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sPLT_SUPPORTED else if (!png_memcmp(chunk_name, png_sPLT, 4)) png_handle_sPLT(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tEXt_SUPPORTED else if (!png_memcmp(chunk_name, png_tEXt, 4)) png_handle_tEXt(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tIME_SUPPORTED else if (!png_memcmp(chunk_name, png_tIME, 4)) png_handle_tIME(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tRNS_SUPPORTED else if (!png_memcmp(chunk_name, png_tRNS, 4)) png_handle_tRNS(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_zTXt_SUPPORTED else if (!png_memcmp(chunk_name, png_zTXt, 4)) png_handle_zTXt(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_iTXt_SUPPORTED else if (!png_memcmp(chunk_name, png_iTXt, 4)) png_handle_iTXt(png_ptr, info_ptr, length); #endif else png_handle_unknown(png_ptr, info_ptr, length); } } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ /* Optional call to update the users info_ptr structure */ void PNGAPI png_read_update_info(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_read_update_info"); if (png_ptr == NULL) return; if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) png_read_start_row(png_ptr); else png_warning(png_ptr, "Ignoring extra png_read_update_info() call; row buffer not reallocated"); png_read_transform_info(png_ptr, info_ptr); } #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Initialize palette, background, etc, after transformations * are set, but before any reading takes place. This allows * the user to obtain a gamma-corrected palette, for example. * If the user doesn't call this, we will do it ourselves. */ void PNGAPI png_start_read_image(png_structp png_ptr) { png_debug(1, "in png_start_read_image"); if (png_ptr == NULL) return; if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) png_read_start_row(png_ptr); } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED void PNGAPI png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) { PNG_IDAT; PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; int ret; if (png_ptr == NULL) return; png_debug2(1, "in png_read_row (row %lu, pass %d)", (unsigned long) png_ptr->row_number, png_ptr->pass); if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) png_read_start_row(png_ptr); if (png_ptr->row_number == 0 && png_ptr->pass == 0) { /* Check for transforms that have been set but were defined out */ #if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) if (png_ptr->transformations & PNG_INVERT_MONO) png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) if (png_ptr->transformations & PNG_FILLER) png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ !defined(PNG_READ_PACKSWAP_SUPPORTED) if (png_ptr->transformations & PNG_PACKSWAP) png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) if (png_ptr->transformations & PNG_PACK) png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) if (png_ptr->transformations & PNG_SHIFT) png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) if (png_ptr->transformations & PNG_BGR) png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); #endif #if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) if (png_ptr->transformations & PNG_SWAP_BYTES) png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); #endif } #ifdef PNG_READ_INTERLACING_SUPPORTED /* If interlaced and we do not need a new row, combine row and return */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { switch (png_ptr->pass) { case 0: if (png_ptr->row_number & 0x07) { if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 1: if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) { if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 2: if ((png_ptr->row_number & 0x07) != 4) { if (dsp_row != NULL && (png_ptr->row_number & 4)) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 3: if ((png_ptr->row_number & 3) || png_ptr->width < 3) { if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 4: if ((png_ptr->row_number & 3) != 2) { if (dsp_row != NULL && (png_ptr->row_number & 2)) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 5: if ((png_ptr->row_number & 1) || png_ptr->width < 2) { if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); png_read_finish_row(png_ptr); return; } break; case 6: if (!(png_ptr->row_number & 1)) { png_read_finish_row(png_ptr); return; } break; } } #endif if (!(png_ptr->mode & PNG_HAVE_IDAT)) png_error(png_ptr, "Invalid attempt to read row data"); png_ptr->zstream.next_out = png_ptr->row_buf; png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); do { if (!(png_ptr->zstream.avail_in)) { while (!png_ptr->idat_size) { png_crc_finish(png_ptr, 0); png_ptr->idat_size = png_read_chunk_header(png_ptr); if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) png_error(png_ptr, "Not enough image data"); } png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_in = png_ptr->zbuf; if (png_ptr->zbuf_size > png_ptr->idat_size) png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; png_crc_read(png_ptr, png_ptr->zbuf, (png_size_t)png_ptr->zstream.avail_in); png_ptr->idat_size -= png_ptr->zstream.avail_in; } ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); if (ret == Z_STREAM_END) { if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || png_ptr->idat_size) png_benign_error(png_ptr, "Extra compressed data"); png_ptr->mode |= PNG_AFTER_IDAT; png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; break; } if (ret != Z_OK) png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : "Decompression error"); } while (png_ptr->zstream.avail_out); png_ptr->row_info.color_type = png_ptr->color_type; png_ptr->row_info.width = png_ptr->iwidth; png_ptr->row_info.channels = png_ptr->channels; png_ptr->row_info.bit_depth = png_ptr->bit_depth; png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->row_info.width); if (png_ptr->row_buf[0]) png_read_filter_row(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->prev_row + 1, (int)(png_ptr->row_buf[0])); png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1); #ifdef PNG_MNG_FEATURES_SUPPORTED if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) { /* Intrapixel differencing */ png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); } #endif if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) png_do_read_transformations(png_ptr); #ifdef PNG_READ_INTERLACING_SUPPORTED /* Blow up interlaced rows to full size */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { if (png_ptr->pass < 6) /* Old interface (pre-1.0.9): * png_do_read_interlace(&(png_ptr->row_info), * png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); */ png_do_read_interlace(png_ptr); if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]); if (row != NULL) png_combine_row(png_ptr, row, png_pass_mask[png_ptr->pass]); } else #endif { if (row != NULL) png_combine_row(png_ptr, row, 0xff); if (dsp_row != NULL) png_combine_row(png_ptr, dsp_row, 0xff); } png_read_finish_row(png_ptr); if (png_ptr->read_row_fn != NULL) (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read one or more rows of image data. If the image is interlaced, * and png_set_interlace_handling() has been called, the rows need to * contain the contents of the rows from the previous pass. If the * image has alpha or transparency, and png_handle_alpha()[*] has been * called, the rows contents must be initialized to the contents of the * screen. * * "row" holds the actual image, and pixels are placed in it * as they arrive. If the image is displayed after each pass, it will * appear to "sparkle" in. "display_row" can be used to display a * "chunky" progressive image, with finer detail added as it becomes * available. If you do not want this "chunky" display, you may pass * NULL for display_row. If you do not want the sparkle display, and * you have not called png_handle_alpha(), you may pass NULL for rows. * If you have called png_handle_alpha(), and the image has either an * alpha channel or a transparency chunk, you must provide a buffer for * rows. In this case, you do not have to provide a display_row buffer * also, but you may. If the image is not interlaced, or if you have * not called png_set_interlace_handling(), the display_row buffer will * be ignored, so pass NULL to it. * * [*] png_handle_alpha() does not exist yet, as of this version of libpng */ void PNGAPI png_read_rows(png_structp png_ptr, png_bytepp row, png_bytepp display_row, png_uint_32 num_rows) { png_uint_32 i; png_bytepp rp; png_bytepp dp; png_debug(1, "in png_read_rows"); if (png_ptr == NULL) return; rp = row; dp = display_row; if (rp != NULL && dp != NULL) for (i = 0; i < num_rows; i++) { png_bytep rptr = *rp++; png_bytep dptr = *dp++; png_read_row(png_ptr, rptr, dptr); } else if (rp != NULL) for (i = 0; i < num_rows; i++) { png_bytep rptr = *rp; png_read_row(png_ptr, rptr, NULL); rp++; } else if (dp != NULL) for (i = 0; i < num_rows; i++) { png_bytep dptr = *dp; png_read_row(png_ptr, NULL, dptr); dp++; } } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the entire image. If the image has an alpha channel or a tRNS * chunk, and you have called png_handle_alpha()[*], you will need to * initialize the image to the current image that PNG will be overlaying. * We set the num_rows again here, in case it was incorrectly set in * png_read_start_row() by a call to png_read_update_info() or * png_start_read_image() if png_set_interlace_handling() wasn't called * prior to either of these functions like it should have been. You can * only call this function once. If you desire to have an image for * each pass of a interlaced image, use png_read_rows() instead. * * [*] png_handle_alpha() does not exist yet, as of this version of libpng */ void PNGAPI png_read_image(png_structp png_ptr, png_bytepp image) { png_uint_32 i, image_height; int pass, j; png_bytepp rp; png_debug(1, "in png_read_image"); if (png_ptr == NULL) return; #ifdef PNG_READ_INTERLACING_SUPPORTED pass = png_set_interlace_handling(png_ptr); #else if (png_ptr->interlaced) png_error(png_ptr, "Cannot read interlaced image -- interlace handler disabled"); pass = 1; #endif image_height=png_ptr->height; png_ptr->num_rows = image_height; /* Make sure this is set correctly */ for (j = 0; j < pass; j++) { rp = image; for (i = 0; i < image_height; i++) { png_read_row(png_ptr, *rp, NULL); rp++; } } } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the end of the PNG file. Will not read past the end of the * file, will verify the end is accurate, and will read any comments * or time information at the end of the file, if info is not NULL. */ void PNGAPI png_read_end(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_read_end"); if (png_ptr == NULL) return; png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ do { PNG_IHDR; PNG_IDAT; PNG_IEND; PNG_PLTE; #ifdef PNG_READ_bKGD_SUPPORTED PNG_bKGD; #endif #ifdef PNG_READ_cHRM_SUPPORTED PNG_cHRM; #endif #ifdef PNG_READ_gAMA_SUPPORTED PNG_gAMA; #endif #ifdef PNG_READ_hIST_SUPPORTED PNG_hIST; #endif #ifdef PNG_READ_iCCP_SUPPORTED PNG_iCCP; #endif #ifdef PNG_READ_iTXt_SUPPORTED PNG_iTXt; #endif #ifdef PNG_READ_oFFs_SUPPORTED PNG_oFFs; #endif #ifdef PNG_READ_pCAL_SUPPORTED PNG_pCAL; #endif #ifdef PNG_READ_pHYs_SUPPORTED PNG_pHYs; #endif #ifdef PNG_READ_sBIT_SUPPORTED PNG_sBIT; #endif #ifdef PNG_READ_sCAL_SUPPORTED PNG_sCAL; #endif #ifdef PNG_READ_sPLT_SUPPORTED PNG_sPLT; #endif #ifdef PNG_READ_sRGB_SUPPORTED PNG_sRGB; #endif #ifdef PNG_READ_tEXt_SUPPORTED PNG_tEXt; #endif #ifdef PNG_READ_tIME_SUPPORTED PNG_tIME; #endif #ifdef PNG_READ_tRNS_SUPPORTED PNG_tRNS; #endif #ifdef PNG_READ_zTXt_SUPPORTED PNG_zTXt; #endif png_uint_32 length = png_read_chunk_header(png_ptr); PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; if (!png_memcmp(chunk_name, png_IHDR, 4)) png_handle_IHDR(png_ptr, info_ptr, length); else if (!png_memcmp(chunk_name, png_IEND, 4)) png_handle_IEND(png_ptr, info_ptr, length); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED else if (png_handle_as_unknown(png_ptr, chunk_name)) { if (!png_memcmp(chunk_name, png_IDAT, 4)) { if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) png_benign_error(png_ptr, "Too many IDATs found"); } png_handle_unknown(png_ptr, info_ptr, length); if (!png_memcmp(chunk_name, png_PLTE, 4)) png_ptr->mode |= PNG_HAVE_PLTE; } #endif else if (!png_memcmp(chunk_name, png_IDAT, 4)) { /* Zero length IDATs are legal after the last IDAT has been * read, but not after other chunks have been read. */ if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) png_benign_error(png_ptr, "Too many IDATs found"); png_crc_finish(png_ptr, length); } else if (!png_memcmp(chunk_name, png_PLTE, 4)) png_handle_PLTE(png_ptr, info_ptr, length); #ifdef PNG_READ_bKGD_SUPPORTED else if (!png_memcmp(chunk_name, png_bKGD, 4)) png_handle_bKGD(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_cHRM_SUPPORTED else if (!png_memcmp(chunk_name, png_cHRM, 4)) png_handle_cHRM(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_gAMA_SUPPORTED else if (!png_memcmp(chunk_name, png_gAMA, 4)) png_handle_gAMA(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_hIST_SUPPORTED else if (!png_memcmp(chunk_name, png_hIST, 4)) png_handle_hIST(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_oFFs_SUPPORTED else if (!png_memcmp(chunk_name, png_oFFs, 4)) png_handle_oFFs(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_pCAL_SUPPORTED else if (!png_memcmp(chunk_name, png_pCAL, 4)) png_handle_pCAL(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sCAL_SUPPORTED else if (!png_memcmp(chunk_name, png_sCAL, 4)) png_handle_sCAL(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_pHYs_SUPPORTED else if (!png_memcmp(chunk_name, png_pHYs, 4)) png_handle_pHYs(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sBIT_SUPPORTED else if (!png_memcmp(chunk_name, png_sBIT, 4)) png_handle_sBIT(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sRGB_SUPPORTED else if (!png_memcmp(chunk_name, png_sRGB, 4)) png_handle_sRGB(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_iCCP_SUPPORTED else if (!png_memcmp(chunk_name, png_iCCP, 4)) png_handle_iCCP(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_sPLT_SUPPORTED else if (!png_memcmp(chunk_name, png_sPLT, 4)) png_handle_sPLT(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tEXt_SUPPORTED else if (!png_memcmp(chunk_name, png_tEXt, 4)) png_handle_tEXt(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tIME_SUPPORTED else if (!png_memcmp(chunk_name, png_tIME, 4)) png_handle_tIME(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_tRNS_SUPPORTED else if (!png_memcmp(chunk_name, png_tRNS, 4)) png_handle_tRNS(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_zTXt_SUPPORTED else if (!png_memcmp(chunk_name, png_zTXt, 4)) png_handle_zTXt(png_ptr, info_ptr, length); #endif #ifdef PNG_READ_iTXt_SUPPORTED else if (!png_memcmp(chunk_name, png_iTXt, 4)) png_handle_iTXt(png_ptr, info_ptr, length); #endif else png_handle_unknown(png_ptr, info_ptr, length); } while (!(png_ptr->mode & PNG_HAVE_IEND)); } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ /* Free all memory used by the read */ void PNGAPI png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr) { png_structp png_ptr = NULL; png_infop info_ptr = NULL, end_info_ptr = NULL; #ifdef PNG_USER_MEM_SUPPORTED png_free_ptr free_fn = NULL; png_voidp mem_ptr = NULL; #endif png_debug(1, "in png_destroy_read_struct"); if (png_ptr_ptr != NULL) png_ptr = *png_ptr_ptr; if (png_ptr == NULL) return; #ifdef PNG_USER_MEM_SUPPORTED free_fn = png_ptr->free_fn; mem_ptr = png_ptr->mem_ptr; #endif if (info_ptr_ptr != NULL) info_ptr = *info_ptr_ptr; if (end_info_ptr_ptr != NULL) end_info_ptr = *end_info_ptr_ptr; png_read_destroy(png_ptr, info_ptr, end_info_ptr); if (info_ptr != NULL) { #ifdef PNG_TEXT_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); #endif #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)info_ptr); #endif *info_ptr_ptr = NULL; } if (end_info_ptr != NULL) { #ifdef PNG_READ_TEXT_SUPPORTED png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); #endif #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)end_info_ptr); #endif *end_info_ptr_ptr = NULL; } if (png_ptr != NULL) { #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)png_ptr); #endif *png_ptr_ptr = NULL; } } /* Free all memory used by the read (old method) */ void /* PRIVATE */ png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) { #ifdef PNG_SETJMP_SUPPORTED jmp_buf tmp_jmp; #endif png_error_ptr error_fn; png_error_ptr warning_fn; png_voidp error_ptr; #ifdef PNG_USER_MEM_SUPPORTED png_free_ptr free_fn; #endif png_debug(1, "in png_read_destroy"); if (info_ptr != NULL) png_info_destroy(png_ptr, info_ptr); if (end_info_ptr != NULL) png_info_destroy(png_ptr, end_info_ptr); png_free(png_ptr, png_ptr->zbuf); png_free(png_ptr, png_ptr->big_row_buf); png_free(png_ptr, png_ptr->prev_row); png_free(png_ptr, png_ptr->chunkdata); #ifdef PNG_READ_QUANTIZE_SUPPORTED png_free(png_ptr, png_ptr->palette_lookup); png_free(png_ptr, png_ptr->quantize_index); #endif #ifdef PNG_READ_GAMMA_SUPPORTED png_free(png_ptr, png_ptr->gamma_table); #endif #ifdef PNG_READ_BACKGROUND_SUPPORTED png_free(png_ptr, png_ptr->gamma_from_1); png_free(png_ptr, png_ptr->gamma_to_1); #endif if (png_ptr->free_me & PNG_FREE_PLTE) png_zfree(png_ptr, png_ptr->palette); png_ptr->free_me &= ~PNG_FREE_PLTE; #if defined(PNG_tRNS_SUPPORTED) || \ defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr->free_me & PNG_FREE_TRNS) png_free(png_ptr, png_ptr->trans_alpha); png_ptr->free_me &= ~PNG_FREE_TRNS; #endif #ifdef PNG_READ_hIST_SUPPORTED if (png_ptr->free_me & PNG_FREE_HIST) png_free(png_ptr, png_ptr->hist); png_ptr->free_me &= ~PNG_FREE_HIST; #endif #ifdef PNG_READ_GAMMA_SUPPORTED if (png_ptr->gamma_16_table != NULL) { int i; int istop = (1 << (8 - png_ptr->gamma_shift)); for (i = 0; i < istop; i++) { png_free(png_ptr, png_ptr->gamma_16_table[i]); } png_free(png_ptr, png_ptr->gamma_16_table); } #ifdef PNG_READ_BACKGROUND_SUPPORTED if (png_ptr->gamma_16_from_1 != NULL) { int i; int istop = (1 << (8 - png_ptr->gamma_shift)); for (i = 0; i < istop; i++) { png_free(png_ptr, png_ptr->gamma_16_from_1[i]); } png_free(png_ptr, png_ptr->gamma_16_from_1); } if (png_ptr->gamma_16_to_1 != NULL) { int i; int istop = (1 << (8 - png_ptr->gamma_shift)); for (i = 0; i < istop; i++) { png_free(png_ptr, png_ptr->gamma_16_to_1[i]); } png_free(png_ptr, png_ptr->gamma_16_to_1); } #endif #endif #ifdef PNG_TIME_RFC1123_SUPPORTED png_free(png_ptr, png_ptr->time_buffer); #endif inflateEnd(&png_ptr->zstream); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED png_free(png_ptr, png_ptr->save_buffer); #endif #ifdef PNG_PROGRESSIVE_READ_SUPPORTED #ifdef PNG_TEXT_SUPPORTED png_free(png_ptr, png_ptr->current_text); #endif /* PNG_TEXT_SUPPORTED */ #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ /* Save the important info out of the png_struct, in case it is * being used again. */ #ifdef PNG_SETJMP_SUPPORTED png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); #endif error_fn = png_ptr->error_fn; warning_fn = png_ptr->warning_fn; error_ptr = png_ptr->error_ptr; #ifdef PNG_USER_MEM_SUPPORTED free_fn = png_ptr->free_fn; #endif png_memset(png_ptr, 0, png_sizeof(png_struct)); png_ptr->error_fn = error_fn; png_ptr->warning_fn = warning_fn; png_ptr->error_ptr = error_ptr; #ifdef PNG_USER_MEM_SUPPORTED png_ptr->free_fn = free_fn; #endif #ifdef PNG_SETJMP_SUPPORTED png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); #endif } void PNGAPI png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) { if (png_ptr == NULL) return; png_ptr->read_row_fn = read_row_fn; } #ifdef PNG_SEQUENTIAL_READ_SUPPORTED #ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI png_read_png(png_structp png_ptr, png_infop info_ptr, int transforms, voidp params) { int row; if (png_ptr == NULL) return; /* png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) png_error(png_ptr, "Image is too high to process with png_read_png()"); /* -------------- image transformations start here ------------------- */ #ifdef PNG_READ_16_TO_8_SUPPORTED /* Tell libpng to strip 16 bit/color files down to 8 bits per color. */ if (transforms & PNG_TRANSFORM_STRIP_16) png_set_strip_16(png_ptr); #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED /* Strip alpha bytes from the input data without combining with * the background (not recommended). */ if (transforms & PNG_TRANSFORM_STRIP_ALPHA) png_set_strip_alpha(png_ptr); #endif #if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ if (transforms & PNG_TRANSFORM_PACKING) png_set_packing(png_ptr); #endif #ifdef PNG_READ_PACKSWAP_SUPPORTED /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ if (transforms & PNG_TRANSFORM_PACKSWAP) png_set_packswap(png_ptr); #endif #ifdef PNG_READ_EXPAND_SUPPORTED /* Expand paletted colors into true RGB triplets * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel * Expand paletted or RGB images with transparency to full alpha * channels so the data will be available as RGBA quartets. */ if (transforms & PNG_TRANSFORM_EXPAND) if ((png_ptr->bit_depth < 8) || (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) png_set_expand(png_ptr); #endif /* We don't handle background color or gamma transformation or quantizing. */ #ifdef PNG_READ_INVERT_SUPPORTED /* Invert monochrome files to have 0 as white and 1 as black */ if (transforms & PNG_TRANSFORM_INVERT_MONO) png_set_invert_mono(png_ptr); #endif #ifdef PNG_READ_SHIFT_SUPPORTED /* If you want to shift the pixel values from the range [0,255] or * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: */ if ((transforms & PNG_TRANSFORM_SHIFT) && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) { png_color_8p sig_bit; png_get_sBIT(png_ptr, info_ptr, &sig_bit); png_set_shift(png_ptr, sig_bit); } #endif #ifdef PNG_READ_BGR_SUPPORTED /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ if (transforms & PNG_TRANSFORM_BGR) png_set_bgr(png_ptr); #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ if (transforms & PNG_TRANSFORM_SWAP_ALPHA) png_set_swap_alpha(png_ptr); #endif #ifdef PNG_READ_SWAP_SUPPORTED /* Swap bytes of 16 bit files to least significant byte first */ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) png_set_swap(png_ptr); #endif /* Added at libpng-1.2.41 */ #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED /* Invert the alpha channel from opacity to transparency */ if (transforms & PNG_TRANSFORM_INVERT_ALPHA) png_set_invert_alpha(png_ptr); #endif /* Added at libpng-1.2.41 */ #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Expand grayscale image to RGB */ if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) png_set_gray_to_rgb(png_ptr); #endif /* We don't handle adding filler bytes */ /* Optional call to gamma correct and add the background to the palette * and update info structure. REQUIRED if you are expecting libpng to * update the palette for you (i.e., you selected such a transform above). */ png_read_update_info(png_ptr, info_ptr); /* -------------- image transformations end here ------------------- */ png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); if (info_ptr->row_pointers == NULL) { png_uint_32 iptr; info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, info_ptr->height * png_sizeof(png_bytep)); for (iptr=0; iptrheight; iptr++) info_ptr->row_pointers[iptr] = NULL; info_ptr->free_me |= PNG_FREE_ROWS; for (row = 0; row < (int)info_ptr->height; row++) info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); } png_read_image(png_ptr, info_ptr->row_pointers); info_ptr->valid |= PNG_INFO_IDAT; /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); transforms = transforms; /* Quiet compiler warnings */ params = params; } #endif /* PNG_INFO_IMAGE_SUPPORTED */ #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ #endif /* PNG_READ_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngrio.c000066400000000000000000000125461271037650300230330ustar00rootroot00000000000000 /* pngrio.c - functions for data input * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file provides a location for all input. Users who need * special handling are expected to write a function that has the same * arguments as this and performs a similar function, but that possibly * has a different input method. Note that you shouldn't change this * function, but rather write a replacement function and then make * libpng use it at run time with png_set_read_fn(...). */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_READ_SUPPORTED #include "pngpriv.h" /* Read the data from whatever input you are using. The default routine * reads from a file pointer. Note that this routine sometimes gets called * with very small lengths, so you should implement some kind of simple * buffering if you are using unbuffered reads. This should never be asked * to read more then 64K on a 16 bit machine. */ void /* PRIVATE */ png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_debug1(4, "reading %d bytes", (int)length); if (png_ptr->read_data_fn != NULL) (*(png_ptr->read_data_fn))(png_ptr, data, length); else png_error(png_ptr, "Call to NULL read function"); } #ifdef PNG_STDIO_SUPPORTED /* This is the function that does the actual reading of data. If you are * not reading from a standard C stream, you should create a replacement * read_data function and use it at run time with png_set_read_fn(), rather * than changing the library. */ #ifndef USE_FAR_KEYWORD void PNGAPI png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; if (png_ptr == NULL) return; /* fread() returns 0 on error, so it is OK to store this in a png_size_t * instead of an int, which is what fread() actually returns. */ check = fread(data, 1, length, (png_FILE_p)png_ptr->io_ptr); if (check != length) png_error(png_ptr, "Read Error"); } #else /* This is the model-independent version. Since the standard I/O library can't handle far buffers in the medium and small models, we have to copy the data. */ #define NEAR_BUF_SIZE 1024 #define MIN(a,b) (a <= b ? a : b) static void PNGAPI png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; png_byte *n_data; png_FILE_p io_ptr; if (png_ptr == NULL) return; /* Check if data really is near. If so, use usual code. */ n_data = (png_byte *)CVT_PTR_NOCHECK(data); io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); if ((png_bytep)n_data == data) { check = fread(n_data, 1, length, io_ptr); } else { png_byte buf[NEAR_BUF_SIZE]; png_size_t read, remaining, err; check = 0; remaining = length; do { read = MIN(NEAR_BUF_SIZE, remaining); err = fread(buf, 1, read, io_ptr); png_memcpy(data, buf, read); /* copy far buffer to near buffer */ if (err != read) break; else check += err; data += read; remaining -= read; } while (remaining != 0); } if ((png_uint_32)check != (png_uint_32)length) png_error(png_ptr, "read Error"); } #endif #endif /* This function allows the application to supply a new input function * for libpng if standard C streams aren't being used. * * This function takes as its arguments: * png_ptr - pointer to a png input data structure * io_ptr - pointer to user supplied structure containing info about * the input functions. May be NULL. * read_data_fn - pointer to a new input function that takes as its * arguments a pointer to a png_struct, a pointer to * a location where input data can be stored, and a 32-bit * unsigned int that is the number of bytes to be read. * To exit and output any fatal error messages the new write * function should call png_error(png_ptr, "Error msg"). * May be NULL, in which case libpng's default function will * be used. */ void PNGAPI png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn) { if (png_ptr == NULL) return; png_ptr->io_ptr = io_ptr; #ifdef PNG_STDIO_SUPPORTED if (read_data_fn != NULL) png_ptr->read_data_fn = read_data_fn; else png_ptr->read_data_fn = png_default_read_data; #else png_ptr->read_data_fn = read_data_fn; #endif /* It is an error to write to a read device */ if (png_ptr->write_data_fn != NULL) { png_ptr->write_data_fn = NULL; png_warning(png_ptr, "It's an error to set both read_data_fn and write_data_fn in the "); png_warning(png_ptr, "same structure. Resetting write_data_fn to NULL"); } #ifdef PNG_WRITE_FLUSH_SUPPORTED png_ptr->output_flush_fn = NULL; #endif } #endif /* PNG_READ_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngrtran.c000066400000000000000000004322601271037650300233670ustar00rootroot00000000000000 /* pngrtran.c - transforms the data in a row for PNG readers * * Last changed in libpng 1.4.2 [May 6, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file contains functions optionally called by an application * in order to tell libpng how to handle data when reading a PNG. * Transformations that are used in both reading and writing are * in pngtrans.c. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_READ_SUPPORTED #include "pngpriv.h" /* Set the action on getting a CRC error for an ancillary or critical chunk. */ void PNGAPI png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) { png_debug(1, "in png_set_crc_action"); if (png_ptr == NULL) return; /* Tell libpng how we react to CRC errors in critical chunks */ switch (crit_action) { case PNG_CRC_NO_CHANGE: /* Leave setting as is */ break; case PNG_CRC_WARN_USE: /* Warn/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; break; case PNG_CRC_QUIET_USE: /* Quiet/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | PNG_FLAG_CRC_CRITICAL_IGNORE; break; case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ png_warning(png_ptr, "Can't discard critical data on CRC error"); case PNG_CRC_ERROR_QUIT: /* Error/quit */ case PNG_CRC_DEFAULT: default: png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; break; } /* Tell libpng how we react to CRC errors in ancillary chunks */ switch (ancil_action) { case PNG_CRC_NO_CHANGE: /* Leave setting as is */ break; case PNG_CRC_WARN_USE: /* Warn/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; break; case PNG_CRC_QUIET_USE: /* Quiet/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN; break; case PNG_CRC_ERROR_QUIT: /* Error/quit */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; break; case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ case PNG_CRC_DEFAULT: default: png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; break; } } #if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ defined(PNG_FLOATING_POINT_SUPPORTED) /* Handle alpha and tRNS via a background color */ void PNGAPI png_set_background(png_structp png_ptr, png_color_16p background_color, int background_gamma_code, int need_expand, double background_gamma) { png_debug(1, "in png_set_background"); if (png_ptr == NULL) return; if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) { png_warning(png_ptr, "Application must supply a known background gamma"); return; } png_ptr->transformations |= PNG_BACKGROUND; png_memcpy(&(png_ptr->background), background_color, png_sizeof(png_color_16)); png_ptr->background_gamma = (float)background_gamma; png_ptr->background_gamma_type = (png_byte)(background_gamma_code); png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); } #endif #ifdef PNG_READ_16_TO_8_SUPPORTED /* Strip 16 bit depth files to 8 bit depth */ void PNGAPI png_set_strip_16(png_structp png_ptr) { png_debug(1, "in png_set_strip_16"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_16_TO_8; } #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED void PNGAPI png_set_strip_alpha(png_structp png_ptr) { png_debug(1, "in png_set_strip_alpha"); if (png_ptr == NULL) return; png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; } #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED /* Quantize file to 8 bit. Supply a palette, the current number * of elements in the palette, the maximum number of elements * allowed, and a histogram if possible. If the current number * of colors is greater then the maximum number, the palette will be * modified to fit in the maximum number. "full_quantize" indicates * whether we need a quantizeing cube set up for RGB images, or if we * simply are reducing the number of colors in a paletted image. */ typedef struct png_dsort_struct { struct png_dsort_struct FAR * next; png_byte left; png_byte right; } png_dsort; typedef png_dsort FAR * png_dsortp; typedef png_dsort FAR * FAR * png_dsortpp; void PNGAPI png_set_quantize(png_structp png_ptr, png_colorp palette, int num_palette, int maximum_colors, png_uint_16p histogram, int full_quantize) { png_debug(1, "in png_set_quantize"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_QUANTIZE; if (!full_quantize) { int i; png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_palette * png_sizeof(png_byte))); for (i = 0; i < num_palette; i++) png_ptr->quantize_index[i] = (png_byte)i; } if (num_palette > maximum_colors) { if (histogram != NULL) { /* This is easy enough, just throw out the least used colors. * Perhaps not the best solution, but good enough. */ int i; /* Initialize an array to sort colors */ png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_palette * png_sizeof(png_byte))); /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) png_ptr->quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted * out enough colors. Note that we don't care about * sorting all the colors, just finding which are * least used. */ for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ int j; done = 1; for (j = 0; j < i; j++) { if (histogram[png_ptr->quantize_sort[j]] < histogram[png_ptr->quantize_sort[j + 1]]) { png_byte t; t = png_ptr->quantize_sort[j]; png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; png_ptr->quantize_sort[j + 1] = t; done = 0; } } if (done) break; } /* Swap the palette around, and set up a table, if necessary */ if (full_quantize) { int j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { if ((int)png_ptr->quantize_sort[i] >= maximum_colors) { do j--; while ((int)png_ptr->quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } } } else { int j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. */ for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ if ((int)png_ptr->quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; while ((int)png_ptr->quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; palette[i] = tmp_color; /* Indicate where the color went */ png_ptr->quantize_index[j] = (png_byte)i; png_ptr->quantize_index[i] = (png_byte)j; } } /* Find closest color for those colors we are not using */ for (i = 0; i < num_palette; i++) { if ((int)png_ptr->quantize_index[i] >= maximum_colors) { int min_d, k, min_k, d_index; /* Find the closest color to one we threw out */ d_index = png_ptr->quantize_index[i]; min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); for (k = 1, min_k = 0; k < maximum_colors; k++) { int d; d = PNG_COLOR_DIST(palette[d_index], palette[k]); if (d < min_d) { min_d = d; min_k = k; } } /* Point to closest color */ png_ptr->quantize_index[i] = (png_byte)min_k; } } } png_free(png_ptr, png_ptr->quantize_sort); png_ptr->quantize_sort = NULL; } else { /* This is much harder to do simply (and quickly). Perhaps * we need to go through a median cut routine, but those * don't always behave themselves with only a few colors * as input. So we will just find the closest two colors, * and throw out one of them (chosen somewhat randomly). * [We don't understand this at all, so if someone wants to * work on improving it, be our guest - AED, GRP] */ int i; int max_d; int num_new_palette; png_dsortp t; png_dsortpp hash; t = NULL; /* Initialize palette index arrays */ png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_palette * png_sizeof(png_byte))); png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_palette * png_sizeof(png_byte))); /* Initialize the sort array */ for (i = 0; i < num_palette; i++) { png_ptr->index_to_palette[i] = (png_byte)i; png_ptr->palette_to_index[i] = (png_byte)i; } hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * png_sizeof(png_dsortp))); num_new_palette = num_palette; /* Initial wild guess at how far apart the farthest pixel * pair we will be eliminating will be. Larger * numbers mean more areas will be allocated, Smaller * numbers run the risk of not saving enough data, and * having to do this all over again. * * I have not done extensive checking on this number. */ max_d = 96; while (num_new_palette > maximum_colors) { for (i = 0; i < num_new_palette - 1; i++) { int j; for (j = i + 1; j < num_new_palette; j++) { int d; d = PNG_COLOR_DIST(palette[i], palette[j]); if (d <= max_d) { t = (png_dsortp)png_malloc_warn(png_ptr, (png_uint_32)(png_sizeof(png_dsort))); if (t == NULL) break; t->next = hash[d]; t->left = (png_byte)i; t->right = (png_byte)j; hash[d] = t; } } if (t == NULL) break; } if (t != NULL) for (i = 0; i <= max_d; i++) { if (hash[i] != NULL) { png_dsortp p; for (p = hash[i]; p; p = p->next) { if ((int)png_ptr->index_to_palette[p->left] < num_new_palette && (int)png_ptr->index_to_palette[p->right] < num_new_palette) { int j, next_j; if (num_new_palette & 0x01) { j = p->left; next_j = p->right; } else { j = p->right; next_j = p->left; } num_new_palette--; palette[png_ptr->index_to_palette[j]] = palette[num_new_palette]; if (!full_quantize) { int k; for (k = 0; k < num_palette; k++) { if (png_ptr->quantize_index[k] == png_ptr->index_to_palette[j]) png_ptr->quantize_index[k] = png_ptr->index_to_palette[next_j]; if ((int)png_ptr->quantize_index[k] == num_new_palette) png_ptr->quantize_index[k] = png_ptr->index_to_palette[j]; } } png_ptr->index_to_palette[png_ptr->palette_to_index [num_new_palette]] = png_ptr->index_to_palette[j]; png_ptr->palette_to_index[png_ptr->index_to_palette[j]] = png_ptr->palette_to_index[num_new_palette]; png_ptr->index_to_palette[j] = (png_byte)num_new_palette; png_ptr->palette_to_index[num_new_palette] = (png_byte)j; } if (num_new_palette <= maximum_colors) break; } if (num_new_palette <= maximum_colors) break; } } for (i = 0; i < 769; i++) { if (hash[i] != NULL) { png_dsortp p = hash[i]; while (p) { t = p->next; png_free(png_ptr, p); p = t; } } hash[i] = 0; } max_d += 96; } png_free(png_ptr, hash); png_free(png_ptr, png_ptr->palette_to_index); png_free(png_ptr, png_ptr->index_to_palette); png_ptr->palette_to_index = NULL; png_ptr->index_to_palette = NULL; } num_palette = maximum_colors; } if (png_ptr->palette == NULL) { png_ptr->palette = palette; } png_ptr->num_palette = (png_uint_16)num_palette; if (full_quantize) { int i; png_bytep distance; int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS; int num_red = (1 << PNG_QUANTIZE_RED_BITS); int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); png_size_t num_entries = ((png_size_t)1 << total_bits); png_ptr->palette_lookup = (png_bytep )png_calloc(png_ptr, (png_uint_32)(num_entries * png_sizeof(png_byte))); distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * png_sizeof(png_byte))); png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); for (i = 0; i < num_palette; i++) { int ir, ig, ib; int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); for (ir = 0; ir < num_red; ir++) { /* int dr = abs(ir - r); */ int dr = ((ir > r) ? ir - r : r - ir); int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + PNG_QUANTIZE_GREEN_BITS)); for (ig = 0; ig < num_green; ig++) { /* int dg = abs(ig - g); */ int dg = ((ig > g) ? ig - g : g - ig); int dt = dr + dg; int dm = ((dr > dg) ? dr : dg); int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); for (ib = 0; ib < num_blue; ib++) { int d_index = index_g | ib; /* int db = abs(ib - b); */ int db = ((ib > b) ? ib - b : b - ib); int dmax = ((dm > db) ? dm : db); int d = dmax + dt + db; if (d < (int)distance[d_index]) { distance[d_index] = (png_byte)d; png_ptr->palette_lookup[d_index] = (png_byte)i; } } } } } png_free(png_ptr, distance); } } #endif /* PNG_READ_QUANTIZE_SUPPORTED */ #if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) /* Transform the image from the file_gamma to the screen_gamma. We * only do transformations on images where the file_gamma and screen_gamma * are not close reciprocals, otherwise it slows things down slightly, and * also needlessly introduces small errors. * * We will turn off gamma transformation later if no semitransparent entries * are present in the tRNS array for palette images. We can't do it here * because we don't necessarily have the tRNS chunk yet. */ void PNGAPI png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) { png_debug(1, "in png_set_gamma"); if (png_ptr == NULL) return; if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) png_ptr->transformations |= PNG_GAMMA; png_ptr->gamma = (float)file_gamma; png_ptr->screen_gamma = (float)scrn_gamma; } #endif #ifdef PNG_READ_EXPAND_SUPPORTED /* Expand paletted images to RGB, expand grayscale images of * less than 8-bit depth to 8-bit depth, and expand tRNS chunks * to alpha channels. */ void PNGAPI png_set_expand(png_structp png_ptr) { png_debug(1, "in png_set_expand"); if (png_ptr == NULL) return; png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); png_ptr->flags &= ~PNG_FLAG_ROW_INIT; } /* GRR 19990627: the following three functions currently are identical * to png_set_expand(). However, it is entirely reasonable that someone * might wish to expand an indexed image to RGB but *not* expand a single, * fully transparent palette entry to a full alpha channel--perhaps instead * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace * the transparent color with a particular RGB value, or drop tRNS entirely. * IOW, a future version of the library may make the transformations flag * a bit more fine-grained, with separate bits for each of these three * functions. * * More to the point, these functions make it obvious what libpng will be * doing, whereas "expand" can (and does) mean any number of things. * * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified * to expand only the sample depth but not to expand the tRNS to alpha * and its name was changed to png_set_expand_gray_1_2_4_to_8(). */ /* Expand paletted images to RGB. */ void PNGAPI png_set_palette_to_rgb(png_structp png_ptr) { png_debug(1, "in png_set_palette_to_rgb"); if (png_ptr == NULL) return; png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); png_ptr->flags &= ~PNG_FLAG_ROW_INIT; } /* Expand grayscale images of less than 8-bit depth to 8 bits. */ void PNGAPI png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) { png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_EXPAND; png_ptr->flags &= ~PNG_FLAG_ROW_INIT; } /* Expand tRNS chunks to alpha channels. */ void PNGAPI png_set_tRNS_to_alpha(png_structp png_ptr) { png_debug(1, "in png_set_tRNS_to_alpha"); png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); png_ptr->flags &= ~PNG_FLAG_ROW_INIT; } #endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED void PNGAPI png_set_gray_to_rgb(png_structp png_ptr) { png_debug(1, "in png_set_gray_to_rgb"); png_ptr->transformations |= PNG_GRAY_TO_RGB; png_ptr->flags &= ~PNG_FLAG_ROW_INIT; } #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED /* Convert a RGB image to a grayscale of the same width. This allows us, * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. */ void PNGAPI png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, double green) { int red_fixed = (int)((float)red*100000.0 + 0.5); int green_fixed = (int)((float)green*100000.0 + 0.5); if (png_ptr == NULL) return; png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); } #endif void PNGAPI png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, png_fixed_point red, png_fixed_point green) { png_debug(1, "in png_set_rgb_to_gray"); if (png_ptr == NULL) return; switch(error_action) { case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; break; case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; break; case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; } if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) #ifdef PNG_READ_EXPAND_SUPPORTED png_ptr->transformations |= PNG_EXPAND; #else { png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); png_ptr->transformations &= ~PNG_RGB_TO_GRAY; } #endif { png_uint_16 red_int, green_int; if (red < 0 || green < 0) { red_int = 6968; /* .212671 * 32768 + .5 */ green_int = 23434; /* .715160 * 32768 + .5 */ } else if (red + green < 100000L) { red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); } else { png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); red_int = 6968; green_int = 23434; } png_ptr->rgb_to_gray_red_coeff = red_int; png_ptr->rgb_to_gray_green_coeff = green_int; png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768 - red_int - green_int); } } #endif #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) void PNGAPI png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr read_user_transform_fn) { png_debug(1, "in png_set_read_user_transform_fn"); if (png_ptr == NULL) return; #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED png_ptr->transformations |= PNG_USER_TRANSFORM; png_ptr->read_user_transform_fn = read_user_transform_fn; #endif } #endif /* Initialize everything needed for the read. This includes modifying * the palette. */ void /* PRIVATE */ png_init_read_transformations(png_structp png_ptr) { png_debug(1, "in png_init_read_transformations"); { #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_SHIFT_SUPPORTED) || \ defined(PNG_READ_GAMMA_SUPPORTED) int color_type = png_ptr->color_type; #endif #if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Detect gray background and attempt to enable optimization * for gray --> RGB case * * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or * RGB_ALPHA (in which case need_expand is superfluous anyway), the * background color might actually be gray yet not be flagged as such. * This is not a problem for the current code, which uses * PNG_BACKGROUND_IS_GRAY only to decide when to do the * png_do_gray_to_rgb() transformation. */ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && !(color_type & PNG_COLOR_MASK_COLOR)) { png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; } else if ((png_ptr->transformations & PNG_BACKGROUND) && !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && (png_ptr->transformations & PNG_GRAY_TO_RGB) && png_ptr->background.red == png_ptr->background.green && png_ptr->background.red == png_ptr->background.blue) { png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; png_ptr->background.gray = png_ptr->background.red; } #endif if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && (png_ptr->transformations & PNG_EXPAND)) { if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ { /* Expand background and tRNS chunks */ switch (png_ptr->bit_depth) { case 1: png_ptr->background.gray *= (png_uint_16)0xff; png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray; if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) { png_ptr->trans_color.gray *= (png_uint_16)0xff; png_ptr->trans_color.red = png_ptr->trans_color.green = png_ptr->trans_color.blue = png_ptr->trans_color.gray; } break; case 2: png_ptr->background.gray *= (png_uint_16)0x55; png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray; if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) { png_ptr->trans_color.gray *= (png_uint_16)0x55; png_ptr->trans_color.red = png_ptr->trans_color.green = png_ptr->trans_color.blue = png_ptr->trans_color.gray; } break; case 4: png_ptr->background.gray *= (png_uint_16)0x11; png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray; if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) { png_ptr->trans_color.gray *= (png_uint_16)0x11; png_ptr->trans_color.red = png_ptr->trans_color.green = png_ptr->trans_color.blue = png_ptr->trans_color.gray; } break; case 8: case 16: png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray; break; } } else if (color_type == PNG_COLOR_TYPE_PALETTE) { png_ptr->background.red = png_ptr->palette[png_ptr->background.index].red; png_ptr->background.green = png_ptr->palette[png_ptr->background.index].green; png_ptr->background.blue = png_ptr->palette[png_ptr->background.index].blue; #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_INVERT_ALPHA) { #ifdef PNG_READ_EXPAND_SUPPORTED if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) #endif { /* Invert the alpha channel (in tRNS) unless the pixels are * going to be expanded, in which case leave it for later */ int i, istop; istop=(int)png_ptr->num_trans; for (i=0; itrans_alpha[i] = (png_byte)(255 - png_ptr->trans_alpha[i]); } } #endif } } #endif #if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) png_ptr->background_1 = png_ptr->background; #endif #if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) < PNG_GAMMA_THRESHOLD)) { int i, k; k=0; for (i=0; inum_trans; i++) { if (png_ptr->trans_alpha[i] != 0 && png_ptr->trans_alpha[i] != 0xff) k=1; /* Partial transparency is present */ } if (k == 0) png_ptr->transformations &= ~PNG_GAMMA; } if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && png_ptr->gamma != 0.0) { png_build_gamma_table(png_ptr, png_ptr->bit_depth); #ifdef PNG_READ_BACKGROUND_SUPPORTED if (png_ptr->transformations & PNG_BACKGROUND) { if (color_type == PNG_COLOR_TYPE_PALETTE) { /* Could skip if no transparency */ png_color back, back_1; png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i; if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) { back.red = png_ptr->gamma_table[png_ptr->background.red]; back.green = png_ptr->gamma_table[png_ptr->background.green]; back.blue = png_ptr->gamma_table[png_ptr->background.blue]; back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; } else { double g, gs; switch (png_ptr->background_gamma_type) { case PNG_BACKGROUND_GAMMA_SCREEN: g = (png_ptr->screen_gamma); gs = 1.0; break; case PNG_BACKGROUND_GAMMA_FILE: g = 1.0 / (png_ptr->gamma); gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); break; case PNG_BACKGROUND_GAMMA_UNIQUE: g = 1.0 / (png_ptr->background_gamma); gs = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); break; default: g = 1.0; /* back_1 */ gs = 1.0; /* back */ } if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) { back.red = (png_byte)png_ptr->background.red; back.green = (png_byte)png_ptr->background.green; back.blue = (png_byte)png_ptr->background.blue; } else { back.red = (png_byte)(pow( (double)png_ptr->background.red/255.0, gs) * 255.0 + .5); back.green = (png_byte)(pow( (double)png_ptr->background.green/255.0, gs) * 255.0 + .5); back.blue = (png_byte)(pow( (double)png_ptr->background.blue/255.0, gs) * 255.0 + .5); } back_1.red = (png_byte)(pow( (double)png_ptr->background.red/255.0, g) * 255.0 + .5); back_1.green = (png_byte)(pow( (double)png_ptr->background.green/255.0, g) * 255.0 + .5); back_1.blue = (png_byte)(pow( (double)png_ptr->background.blue/255.0, g) * 255.0 + .5); } for (i = 0; i < num_palette; i++) { if (i < (int)png_ptr->num_trans && png_ptr->trans_alpha[i] != 0xff) { if (png_ptr->trans_alpha[i] == 0) { palette[i] = back; } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { png_byte v, w; v = png_ptr->gamma_to_1[palette[i].red]; png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); palette[i].red = png_ptr->gamma_from_1[w]; v = png_ptr->gamma_to_1[palette[i].green]; png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); palette[i].green = png_ptr->gamma_from_1[w]; v = png_ptr->gamma_to_1[palette[i].blue]; png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); palette[i].blue = png_ptr->gamma_from_1[w]; } } else { palette[i].red = png_ptr->gamma_table[palette[i].red]; palette[i].green = png_ptr->gamma_table[palette[i].green]; palette[i].blue = png_ptr->gamma_table[palette[i].blue]; } } /* Prevent the transformations being done again, and make sure * that the now spurious alpha channel is stripped - the code * has just reduced background composition and gamma correction * to a simple alpha channel strip. */ png_ptr->transformations &= ~PNG_BACKGROUND; png_ptr->transformations &= ~PNG_GAMMA; png_ptr->transformations |= PNG_STRIP_ALPHA; } /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ else /* color_type != PNG_COLOR_TYPE_PALETTE */ { double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); double g = 1.0; double gs = 1.0; switch (png_ptr->background_gamma_type) { case PNG_BACKGROUND_GAMMA_SCREEN: g = (png_ptr->screen_gamma); gs = 1.0; break; case PNG_BACKGROUND_GAMMA_FILE: g = 1.0 / (png_ptr->gamma); gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); break; case PNG_BACKGROUND_GAMMA_UNIQUE: g = 1.0 / (png_ptr->background_gamma); gs = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); break; } png_ptr->background_1.gray = (png_uint_16)(pow( (double)png_ptr->background.gray / m, g) * m + .5); png_ptr->background.gray = (png_uint_16)(pow( (double)png_ptr->background.gray / m, gs) * m + .5); if ((png_ptr->background.red != png_ptr->background.green) || (png_ptr->background.red != png_ptr->background.blue) || (png_ptr->background.red != png_ptr->background.gray)) { /* RGB or RGBA with color background */ png_ptr->background_1.red = (png_uint_16)(pow( (double)png_ptr->background.red / m, g) * m + .5); png_ptr->background_1.green = (png_uint_16)(pow( (double)png_ptr->background.green / m, g) * m + .5); png_ptr->background_1.blue = (png_uint_16)(pow( (double)png_ptr->background.blue / m, g) * m + .5); png_ptr->background.red = (png_uint_16)(pow( (double)png_ptr->background.red / m, gs) * m + .5); png_ptr->background.green = (png_uint_16)(pow( (double)png_ptr->background.green / m, gs) * m + .5); png_ptr->background.blue = (png_uint_16)(pow( (double)png_ptr->background.blue / m, gs) * m + .5); } else { /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ png_ptr->background_1.red = png_ptr->background_1.green = png_ptr->background_1.blue = png_ptr->background_1.gray; png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray; } } } else /* Transformation does not include PNG_BACKGROUND */ #endif /* PNG_READ_BACKGROUND_SUPPORTED */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i; for (i = 0; i < num_palette; i++) { palette[i].red = png_ptr->gamma_table[palette[i].red]; palette[i].green = png_ptr->gamma_table[palette[i].green]; palette[i].blue = png_ptr->gamma_table[palette[i].blue]; } /* Done the gamma correction. */ png_ptr->transformations &= ~PNG_GAMMA; } } #ifdef PNG_READ_BACKGROUND_SUPPORTED else #endif #endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ #ifdef PNG_READ_BACKGROUND_SUPPORTED /* No GAMMA transformation */ if ((png_ptr->transformations & PNG_BACKGROUND) && (color_type == PNG_COLOR_TYPE_PALETTE)) { int i; int istop = (int)png_ptr->num_trans; png_color back; png_colorp palette = png_ptr->palette; back.red = (png_byte)png_ptr->background.red; back.green = (png_byte)png_ptr->background.green; back.blue = (png_byte)png_ptr->background.blue; for (i = 0; i < istop; i++) { if (png_ptr->trans_alpha[i] == 0) { palette[i] = back; } else if (png_ptr->trans_alpha[i] != 0xff) { /* The png_composite() macro is defined in png.h */ png_composite(palette[i].red, palette[i].red, png_ptr->trans_alpha[i], back.red); png_composite(palette[i].green, palette[i].green, png_ptr->trans_alpha[i], back.green); png_composite(palette[i].blue, palette[i].blue, png_ptr->trans_alpha[i], back.blue); } } /* Handled alpha, still need to strip the channel. */ png_ptr->transformations &= ~PNG_BACKGROUND; png_ptr->transformations |= PNG_STRIP_ALPHA; } #endif /* PNG_READ_BACKGROUND_SUPPORTED */ #ifdef PNG_READ_SHIFT_SUPPORTED if ((png_ptr->transformations & PNG_SHIFT) && (color_type == PNG_COLOR_TYPE_PALETTE)) { png_uint_16 i; png_uint_16 istop = png_ptr->num_palette; int sr = 8 - png_ptr->sig_bit.red; int sg = 8 - png_ptr->sig_bit.green; int sb = 8 - png_ptr->sig_bit.blue; if (sr < 0 || sr > 8) sr = 0; if (sg < 0 || sg > 8) sg = 0; if (sb < 0 || sb > 8) sb = 0; for (i = 0; i < istop; i++) { png_ptr->palette[i].red >>= sr; png_ptr->palette[i].green >>= sg; png_ptr->palette[i].blue >>= sb; } } #endif /* PNG_READ_SHIFT_SUPPORTED */ } #if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ && !defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr) return; #endif } /* Modify the info structure to reflect the transformations. The * info should be updated so a PNG file could be written with it, * assuming the transformations result in valid PNG data. */ void /* PRIVATE */ png_read_transform_info(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_read_transform_info"); #ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; else info_ptr->color_type = PNG_COLOR_TYPE_RGB; info_ptr->bit_depth = 8; info_ptr->num_trans = 0; } else { if (png_ptr->num_trans) { if (png_ptr->transformations & PNG_EXPAND_tRNS) info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; } if (info_ptr->bit_depth < 8) info_ptr->bit_depth = 8; info_ptr->num_trans = 0; } } #endif #ifdef PNG_READ_BACKGROUND_SUPPORTED if (png_ptr->transformations & PNG_BACKGROUND) { info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; info_ptr->num_trans = 0; info_ptr->background = png_ptr->background; } #endif #ifdef PNG_READ_GAMMA_SUPPORTED if (png_ptr->transformations & PNG_GAMMA) { #ifdef PNG_FLOATING_POINT_SUPPORTED info_ptr->gamma = png_ptr->gamma; #endif #ifdef PNG_FIXED_POINT_SUPPORTED info_ptr->int_gamma = png_ptr->int_gamma; #endif } #endif #ifdef PNG_READ_16_TO_8_SUPPORTED if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) info_ptr->bit_depth = 8; #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED if (png_ptr->transformations & PNG_GRAY_TO_RGB) info_ptr->color_type |= PNG_COLOR_MASK_COLOR; #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED if (png_ptr->transformations & PNG_RGB_TO_GRAY) info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED if (png_ptr->transformations & PNG_QUANTIZE) { if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && png_ptr->palette_lookup && info_ptr->bit_depth == 8) { info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; } } #endif #ifdef PNG_READ_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) info_ptr->bit_depth = 8; #endif if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) info_ptr->channels = 1; else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) info_ptr->channels = 3; else info_ptr->channels = 1; #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; #endif if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) info_ptr->channels++; #ifdef PNG_READ_FILLER_SUPPORTED /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ if ((png_ptr->transformations & PNG_FILLER) && ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) { info_ptr->channels++; /* If adding a true alpha channel not just filler */ if (png_ptr->transformations & PNG_ADD_ALPHA) info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; } #endif #if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) if (png_ptr->transformations & PNG_USER_TRANSFORM) { if (info_ptr->bit_depth < png_ptr->user_transform_depth) info_ptr->bit_depth = png_ptr->user_transform_depth; if (info_ptr->channels < png_ptr->user_transform_channels) info_ptr->channels = png_ptr->user_transform_channels; } #endif info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); #ifndef PNG_READ_EXPAND_SUPPORTED if (png_ptr) return; #endif } /* Transform the row. The order of transformations is significant, * and is very touchy. If you add a transformation, take care to * decide how it fits in with the other transformations here. */ void /* PRIVATE */ png_do_read_transformations(png_structp png_ptr) { png_debug(1, "in png_do_read_transformations"); if (png_ptr->row_buf == NULL) { #ifdef PNG_STDIO_SUPPORTED char msg[50]; png_snprintf2(msg, 50, "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number, png_ptr->pass); png_error(png_ptr, msg); #else png_error(png_ptr, "NULL row buffer"); #endif } #ifdef PNG_WARN_UNINITIALIZED_ROW if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) /* Application has failed to call either png_read_start_image() * or png_read_update_info() after setting transforms that expand * pixels. This check added to libpng-1.2.19 */ #if (PNG_WARN_UNINITIALIZED_ROW==1) png_error(png_ptr, "Uninitialized row"); #else png_warning(png_ptr, "Uninitialized row"); #endif #endif #ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) { png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); } else { if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, &(png_ptr->trans_color)); else png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, NULL); } } #endif #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED if (png_ptr->transformations & PNG_RGB_TO_GRAY) { int rgb_error = png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); if (rgb_error) { png_ptr->rgb_to_gray_status=1; if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == PNG_RGB_TO_GRAY_WARN) png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == PNG_RGB_TO_GRAY_ERR) png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); } } #endif /* From Andreas Dilger e-mail to png-implement, 26 March 1998: * * In most cases, the "simple transparency" should be done prior to doing * gray-to-RGB, or you will have to test 3x as many bytes to check if a * pixel is transparent. You would also need to make sure that the * transparency information is upgraded to RGB. * * To summarize, the current flow is: * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite * with background "in place" if transparent, * convert to RGB if necessary * - Gray + alpha -> composite with gray background and remove alpha bytes, * convert to RGB if necessary * * To support RGB backgrounds for gray images we need: * - Gray + simple transparency -> convert to RGB + simple transparency, * compare 3 or 6 bytes and composite with * background "in place" if transparent * (3x compare/pixel compared to doing * composite with gray bkgrnd) * - Gray + alpha -> convert to RGB + alpha, composite with background and * remove alpha bytes (3x float * operations/pixel compared with composite * on gray background) * * Greg's change will do this. The reason it wasn't done before is for * performance, as this increases the per-pixel operations. If we would check * in advance if the background was gray or RGB, and position the gray-to-RGB * transform appropriately, then it would save a lot of work/time. */ #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* If gray -> RGB, do so now only if background is non-gray; else do later * for performance reasons */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_BACKGROUND_SUPPORTED if ((png_ptr->transformations & PNG_BACKGROUND) && ((png_ptr->num_trans != 0 ) || (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, &(png_ptr->trans_color), &(png_ptr->background) #ifdef PNG_READ_GAMMA_SUPPORTED , &(png_ptr->background_1), png_ptr->gamma_table, png_ptr->gamma_from_1, png_ptr->gamma_to_1, png_ptr->gamma_16_table, png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, png_ptr->gamma_shift #endif ); #endif #ifdef PNG_READ_GAMMA_SUPPORTED if ((png_ptr->transformations & PNG_GAMMA) && #ifdef PNG_READ_BACKGROUND_SUPPORTED !((png_ptr->transformations & PNG_BACKGROUND) && ((png_ptr->num_trans != 0) || (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && #endif (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->gamma_table, png_ptr->gamma_16_table, png_ptr->gamma_shift); #endif #ifdef PNG_READ_16_TO_8_SUPPORTED if (png_ptr->transformations & PNG_16_TO_8) png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED if (png_ptr->transformations & PNG_QUANTIZE) { png_do_quantize((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->palette_lookup, png_ptr->quantize_index); if (png_ptr->row_info.rowbytes == (png_uint_32)0) png_error(png_ptr, "png_do_quantize returned rowbytes=0"); } #endif #ifdef PNG_READ_INVERT_SUPPORTED if (png_ptr->transformations & PNG_INVERT_MONO) png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SHIFT_SUPPORTED if (png_ptr->transformations & PNG_SHIFT) png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, &(png_ptr->shift)); #endif #ifdef PNG_READ_PACK_SUPPORTED if (png_ptr->transformations & PNG_PACK) png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_BGR_SUPPORTED if (png_ptr->transformations & PNG_BGR) png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* If gray -> RGB, do so now only if we did not do so above */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_FILLER_SUPPORTED if (png_ptr->transformations & PNG_FILLER) png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, (png_uint_32)png_ptr->filler, png_ptr->flags); #endif #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_INVERT_ALPHA) png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_SWAP_ALPHA) png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_SWAP_SUPPORTED if (png_ptr->transformations & PNG_SWAP_BYTES) png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED if (png_ptr->transformations & PNG_USER_TRANSFORM) { if (png_ptr->read_user_transform_fn != NULL) (*(png_ptr->read_user_transform_fn)) /* User read transform function */ (png_ptr, /* png_ptr */ &(png_ptr->row_info), /* row_info: */ /* png_uint_32 width; width of row */ /* png_uint_32 rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ /* png_byte bit_depth; bit depth of samples */ /* png_byte channels; number of channels (1-4) */ /* png_byte pixel_depth; bits per pixel (depth*channels) */ png_ptr->row_buf + 1); /* start of pixel data for row */ #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED if (png_ptr->user_transform_depth) png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; if (png_ptr->user_transform_channels) png_ptr->row_info.channels = png_ptr->user_transform_channels; #endif png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * png_ptr->row_info.channels); png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->row_info.width); } #endif } #ifdef PNG_READ_PACK_SUPPORTED /* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, * without changing the actual values. Thus, if you had a row with * a bit depth of 1, you would end up with bytes that only contained * the numbers 0 or 1. If you would rather they contain 0 and 255, use * png_do_shift() after this. */ void /* PRIVATE */ png_do_unpack(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_unpack"); if (row_info->bit_depth < 8) { png_uint_32 i; png_uint_32 row_width=row_info->width; switch (row_info->bit_depth) { case 1: { png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); png_bytep dp = row + (png_size_t)row_width - 1; png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x01); if (shift == 7) { shift = 0; sp--; } else shift++; dp--; } break; } case 2: { png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); png_bytep dp = row + (png_size_t)row_width - 1; png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x03); if (shift == 6) { shift = 0; sp--; } else shift += 2; dp--; } break; } case 4: { png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); png_bytep dp = row + (png_size_t)row_width - 1; png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x0f); if (shift == 4) { shift = 0; sp--; } else shift = 4; dp--; } break; } } row_info->bit_depth = 8; row_info->pixel_depth = (png_byte)(8 * row_info->channels); row_info->rowbytes = row_width * row_info->channels; } } #endif #ifdef PNG_READ_SHIFT_SUPPORTED /* Reverse the effects of png_do_shift. This routine merely shifts the * pixels back to their significant bits values. Thus, if you have * a row of bit depth 8, but only 5 are significant, this will shift * the values back to 0 through 31. */ void /* PRIVATE */ png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) { png_debug(1, "in png_do_unshift"); if ( row_info->color_type != PNG_COLOR_TYPE_PALETTE) { int shift[4]; int channels = 0; int c; png_uint_16 value = 0; png_uint_32 row_width = row_info->width; if (row_info->color_type & PNG_COLOR_MASK_COLOR) { shift[channels++] = row_info->bit_depth - sig_bits->red; shift[channels++] = row_info->bit_depth - sig_bits->green; shift[channels++] = row_info->bit_depth - sig_bits->blue; } else { shift[channels++] = row_info->bit_depth - sig_bits->gray; } if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { shift[channels++] = row_info->bit_depth - sig_bits->alpha; } for (c = 0; c < channels; c++) { if (shift[c] <= 0) shift[c] = 0; else value = 1; } if (!value) return; switch (row_info->bit_depth) { case 2: { png_bytep bp; png_uint_32 i; png_uint_32 istop = row_info->rowbytes; for (bp = row, i = 0; i < istop; i++) { *bp >>= 1; *bp++ &= 0x55; } break; } case 4: { png_bytep bp = row; png_uint_32 i; png_uint_32 istop = row_info->rowbytes; png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | (png_byte)((int)0xf >> shift[0])); for (i = 0; i < istop; i++) { *bp >>= shift[0]; *bp++ &= mask; } break; } case 8: { png_bytep bp = row; png_uint_32 i; png_uint_32 istop = row_width * channels; for (i = 0; i < istop; i++) { *bp++ >>= shift[i%channels]; } break; } case 16: { png_bytep bp = row; png_uint_32 i; png_uint_32 istop = channels * row_width; for (i = 0; i < istop; i++) { value = (png_uint_16)((*bp << 8) + *(bp + 1)); value >>= shift[i%channels]; *bp++ = (png_byte)(value >> 8); *bp++ = (png_byte)(value & 0xff); } break; } } } } #endif #ifdef PNG_READ_16_TO_8_SUPPORTED /* Chop rows of bit depth 16 down to 8 */ void /* PRIVATE */ png_do_chop(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_chop"); if (row_info->bit_depth == 16) { png_bytep sp = row; png_bytep dp = row; png_uint_32 i; png_uint_32 istop = row_info->width * row_info->channels; for (i = 0; i> 8)) >> 8; * * Approximate calculation with shift/add instead of multiply/divide: * *dp = ((((png_uint_32)(*sp) << 8) | * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; * * What we actually do to avoid extra shifting and conversion: */ *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); #else /* Simply discard the low order byte */ *dp = *sp; #endif } row_info->bit_depth = 8; row_info->pixel_depth = (png_byte)(8 * row_info->channels); row_info->rowbytes = row_info->width * row_info->channels; } } #endif #ifdef PNG_READ_SWAP_ALPHA_SUPPORTED void /* PRIVATE */ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_read_swap_alpha"); { png_uint_32 row_width = row_info->width; if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { /* This converts from RGBA to ARGB */ if (row_info->bit_depth == 8) { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_byte save; png_uint_32 i; for (i = 0; i < row_width; i++) { save = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = save; } } /* This converts from RRGGBBAA to AARRGGBB */ else { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_byte save[2]; png_uint_32 i; for (i = 0; i < row_width; i++) { save[0] = *(--sp); save[1] = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = save[0]; *(--dp) = save[1]; } } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { /* This converts from GA to AG */ if (row_info->bit_depth == 8) { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_byte save; png_uint_32 i; for (i = 0; i < row_width; i++) { save = *(--sp); *(--dp) = *(--sp); *(--dp) = save; } } /* This converts from GGAA to AAGG */ else { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_byte save[2]; png_uint_32 i; for (i = 0; i < row_width; i++) { save[0] = *(--sp); save[1] = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = save[0]; *(--dp) = save[1]; } } } } } #endif #ifdef PNG_READ_INVERT_ALPHA_SUPPORTED void /* PRIVATE */ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_read_invert_alpha"); { png_uint_32 row_width = row_info->width; if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { /* This inverts the alpha channel in RGBA */ if (row_info->bit_depth == 8) { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_uint_32 i; for (i = 0; i < row_width; i++) { *(--dp) = (png_byte)(255 - *(--sp)); /* This does nothing: *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); We can replace it with: */ sp-=3; dp=sp; } } /* This inverts the alpha channel in RRGGBBAA */ else { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_uint_32 i; for (i = 0; i < row_width; i++) { *(--dp) = (png_byte)(255 - *(--sp)); *(--dp) = (png_byte)(255 - *(--sp)); /* This does nothing: *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); We can replace it with: */ sp-=6; dp=sp; } } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { /* This inverts the alpha channel in GA */ if (row_info->bit_depth == 8) { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_uint_32 i; for (i = 0; i < row_width; i++) { *(--dp) = (png_byte)(255 - *(--sp)); *(--dp) = *(--sp); } } /* This inverts the alpha channel in GGAA */ else { png_bytep sp = row + row_info->rowbytes; png_bytep dp = sp; png_uint_32 i; for (i = 0; i < row_width; i++) { *(--dp) = (png_byte)(255 - *(--sp)); *(--dp) = (png_byte)(255 - *(--sp)); /* *(--dp) = *(--sp); *(--dp) = *(--sp); */ sp-=2; dp=sp; } } } } } #endif #ifdef PNG_READ_FILLER_SUPPORTED /* Add filler channel if we have RGB color */ void /* PRIVATE */ png_do_read_filler(png_row_infop row_info, png_bytep row, png_uint_32 filler, png_uint_32 flags) { png_uint_32 i; png_uint_32 row_width = row_info->width; png_byte hi_filler = (png_byte)((filler>>8) & 0xff); png_byte lo_filler = (png_byte)(filler & 0xff); png_debug(1, "in png_do_read_filler"); if ( row_info->color_type == PNG_COLOR_TYPE_GRAY) { if (row_info->bit_depth == 8) { /* This changes the data from G to GX */ if (flags & PNG_FLAG_FILLER_AFTER) { png_bytep sp = row + (png_size_t)row_width; png_bytep dp = sp + (png_size_t)row_width; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; *(--dp) = *(--sp); } *(--dp) = lo_filler; row_info->channels = 2; row_info->pixel_depth = 16; row_info->rowbytes = row_width * 2; } /* This changes the data from G to XG */ else { png_bytep sp = row + (png_size_t)row_width; png_bytep dp = sp + (png_size_t)row_width; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); *(--dp) = lo_filler; } row_info->channels = 2; row_info->pixel_depth = 16; row_info->rowbytes = row_width * 2; } } else if (row_info->bit_depth == 16) { /* This changes the data from GG to GGXX */ if (flags & PNG_FLAG_FILLER_AFTER) { png_bytep sp = row + (png_size_t)row_width * 2; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 1; i < row_width; i++) { *(--dp) = hi_filler; *(--dp) = lo_filler; *(--dp) = *(--sp); *(--dp) = *(--sp); } *(--dp) = hi_filler; *(--dp) = lo_filler; row_info->channels = 2; row_info->pixel_depth = 32; row_info->rowbytes = row_width * 4; } /* This changes the data from GG to XXGG */ else { png_bytep sp = row + (png_size_t)row_width * 2; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = hi_filler; *(--dp) = lo_filler; } row_info->channels = 2; row_info->pixel_depth = 32; row_info->rowbytes = row_width * 4; } } } /* COLOR_TYPE == GRAY */ else if (row_info->color_type == PNG_COLOR_TYPE_RGB) { if (row_info->bit_depth == 8) { /* This changes the data from RGB to RGBX */ if (flags & PNG_FLAG_FILLER_AFTER) { png_bytep sp = row + (png_size_t)row_width * 3; png_bytep dp = sp + (png_size_t)row_width; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); } *(--dp) = lo_filler; row_info->channels = 4; row_info->pixel_depth = 32; row_info->rowbytes = row_width * 4; } /* This changes the data from RGB to XRGB */ else { png_bytep sp = row + (png_size_t)row_width * 3; png_bytep dp = sp + (png_size_t)row_width; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = lo_filler; } row_info->channels = 4; row_info->pixel_depth = 32; row_info->rowbytes = row_width * 4; } } else if (row_info->bit_depth == 16) { /* This changes the data from RRGGBB to RRGGBBXX */ if (flags & PNG_FLAG_FILLER_AFTER) { png_bytep sp = row + (png_size_t)row_width * 6; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 1; i < row_width; i++) { *(--dp) = hi_filler; *(--dp) = lo_filler; *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); } *(--dp) = hi_filler; *(--dp) = lo_filler; row_info->channels = 4; row_info->pixel_depth = 64; row_info->rowbytes = row_width * 8; } /* This changes the data from RRGGBB to XXRRGGBB */ else { png_bytep sp = row + (png_size_t)row_width * 6; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = *(--sp); *(--dp) = hi_filler; *(--dp) = lo_filler; } row_info->channels = 4; row_info->pixel_depth = 64; row_info->rowbytes = row_width * 8; } } } /* COLOR_TYPE == RGB */ } #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED /* Expand grayscale files to RGB, with or without alpha */ void /* PRIVATE */ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) { png_uint_32 i; png_uint_32 row_width = row_info->width; png_debug(1, "in png_do_gray_to_rgb"); if (row_info->bit_depth >= 8 && !(row_info->color_type & PNG_COLOR_MASK_COLOR)) { if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { if (row_info->bit_depth == 8) { png_bytep sp = row + (png_size_t)row_width - 1; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(dp--) = *sp; *(dp--) = *sp; *(dp--) = *(sp--); } } else { png_bytep sp = row + (png_size_t)row_width * 2 - 1; png_bytep dp = sp + (png_size_t)row_width * 4; for (i = 0; i < row_width; i++) { *(dp--) = *sp; *(dp--) = *(sp - 1); *(dp--) = *sp; *(dp--) = *(sp - 1); *(dp--) = *(sp--); *(dp--) = *(sp--); } } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (row_info->bit_depth == 8) { png_bytep sp = row + (png_size_t)row_width * 2 - 1; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(dp--) = *(sp--); *(dp--) = *sp; *(dp--) = *sp; *(dp--) = *(sp--); } } else { png_bytep sp = row + (png_size_t)row_width * 4 - 1; png_bytep dp = sp + (png_size_t)row_width * 4; for (i = 0; i < row_width; i++) { *(dp--) = *(sp--); *(dp--) = *(sp--); *(dp--) = *sp; *(dp--) = *(sp - 1); *(dp--) = *sp; *(dp--) = *(sp - 1); *(dp--) = *(sp--); *(dp--) = *(sp--); } } } row_info->channels += (png_byte)2; row_info->color_type |= PNG_COLOR_MASK_COLOR; row_info->pixel_depth = (png_byte)(row_info->channels * row_info->bit_depth); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } #endif #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB files to grayscale, with or without alpha * using the equation given in Poynton's ColorFAQ at * (THIS LINK IS DEAD June 2008) * New link: * * Charles Poynton poynton at poynton.com * * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B * * We approximate this with * * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B * * which can be expressed with integers as * * Y = (6969 * R + 23434 * G + 2365 * B)/32768 * * The calculation is to be done in a linear colorspace. * * Other integer coefficents can be used via png_set_rgb_to_gray(). */ int /* PRIVATE */ png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) { png_uint_32 i; png_uint_32 row_width = row_info->width; int rgb_error = 0; png_debug(1, "in png_do_rgb_to_gray"); if ( (row_info->color_type & PNG_COLOR_MASK_COLOR)) { png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; if (row_info->color_type == PNG_COLOR_TYPE_RGB) { if (row_info->bit_depth == 8) { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_byte red = png_ptr->gamma_to_1[*(sp++)]; png_byte green = png_ptr->gamma_to_1[*(sp++)]; png_byte blue = png_ptr->gamma_to_1[*(sp++)]; if (red != green || red != blue) { rgb_error |= 1; *(dp++) = png_ptr->gamma_from_1[ (rc*red + gc*green + bc*blue)>>15]; } else *(dp++) = *(sp - 1); } } else #endif { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_byte red = *(sp++); png_byte green = *(sp++); png_byte blue = *(sp++); if (red != green || red != blue) { rgb_error |= 1; *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); } else *(dp++) = *(sp - 1); } } } else /* RGB bit_depth == 16 */ { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_uint_16 red, green, blue, w; red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; if (red == green && red == blue) w = red; else { png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> png_ptr->gamma_shift][red>>8]; png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> png_ptr->gamma_shift][green>>8]; png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> png_ptr->gamma_shift][blue>>8]; png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + bc*blue_1)>>15); w = png_ptr->gamma_16_from_1[(gray16&0xff) >> png_ptr->gamma_shift][gray16 >> 8]; rgb_error |= 1; } *(dp++) = (png_byte)((w>>8) & 0xff); *(dp++) = (png_byte)(w & 0xff); } } else #endif { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_uint_16 red, green, blue, gray16; red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; if (red != green || red != blue) rgb_error |= 1; gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); *(dp++) = (png_byte)((gray16>>8) & 0xff); *(dp++) = (png_byte)(gray16 & 0xff); } } } } if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { if (row_info->bit_depth == 8) { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_byte red = png_ptr->gamma_to_1[*(sp++)]; png_byte green = png_ptr->gamma_to_1[*(sp++)]; png_byte blue = png_ptr->gamma_to_1[*(sp++)]; if (red != green || red != blue) rgb_error |= 1; *(dp++) = png_ptr->gamma_from_1 [(rc*red + gc*green + bc*blue)>>15]; *(dp++) = *(sp++); /* alpha */ } } else #endif { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_byte red = *(sp++); png_byte green = *(sp++); png_byte blue = *(sp++); if (red != green || red != blue) rgb_error |= 1; *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); *(dp++) = *(sp++); /* alpha */ } } } else /* RGBA bit_depth == 16 */ { #if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_uint_16 red, green, blue, w; red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; if (red == green && red == blue) w = red; else { png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> png_ptr->gamma_shift][red>>8]; png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> png_ptr->gamma_shift][green>>8]; png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> png_ptr->gamma_shift][blue>>8]; png_uint_16 gray16 = (png_uint_16)((rc * red_1 + gc * green_1 + bc * blue_1)>>15); w = png_ptr->gamma_16_from_1[(gray16&0xff) >> png_ptr->gamma_shift][gray16 >> 8]; rgb_error |= 1; } *(dp++) = (png_byte)((w>>8) & 0xff); *(dp++) = (png_byte)(w & 0xff); *(dp++) = *(sp++); /* alpha */ *(dp++) = *(sp++); } } else #endif { png_bytep sp = row; png_bytep dp = row; for (i = 0; i < row_width; i++) { png_uint_16 red, green, blue, gray16; red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; if (red != green || red != blue) rgb_error |= 1; gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); *(dp++) = (png_byte)((gray16>>8) & 0xff); *(dp++) = (png_byte)(gray16 & 0xff); *(dp++) = *(sp++); /* alpha */ *(dp++) = *(sp++); } } } } row_info->channels -= (png_byte)2; row_info->color_type &= ~PNG_COLOR_MASK_COLOR; row_info->pixel_depth = (png_byte)(row_info->channels * row_info->bit_depth); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } return rgb_error; } #endif /* Build a grayscale palette. Palette is assumed to be 1 << bit_depth * large of png_color. This lets grayscale images be treated as * paletted. Most useful for gamma correction and simplification * of code. */ void PNGAPI png_build_grayscale_palette(int bit_depth, png_colorp palette) { int num_palette; int color_inc; int i; int v; png_debug(1, "in png_do_build_grayscale_palette"); if (palette == NULL) return; switch (bit_depth) { case 1: num_palette = 2; color_inc = 0xff; break; case 2: num_palette = 4; color_inc = 0x55; break; case 4: num_palette = 16; color_inc = 0x11; break; case 8: num_palette = 256; color_inc = 1; break; default: num_palette = 0; color_inc = 0; break; } for (i = 0, v = 0; i < num_palette; i++, v += color_inc) { palette[i].red = (png_byte)v; palette[i].green = (png_byte)v; palette[i].blue = (png_byte)v; } } #ifdef PNG_READ_BACKGROUND_SUPPORTED /* Replace any alpha or transparency with the supplied background color. * "background" is already in the screen gamma, while "background_1" is * at a gamma of 1.0. Paletted files have already been taken care of. */ void /* PRIVATE */ png_do_background(png_row_infop row_info, png_bytep row, png_color_16p trans_color, png_color_16p background #ifdef PNG_READ_GAMMA_SUPPORTED , png_color_16p background_1, png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, png_uint_16pp gamma_16_to_1, int gamma_shift #endif ) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; int shift; png_debug(1, "in png_do_background"); if (background != NULL && (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_color))) { switch (row_info->color_type) { case PNG_COLOR_TYPE_GRAY: { switch (row_info->bit_depth) { case 1: { sp = row; shift = 7; for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x01) == trans_color->gray) { *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); *sp |= (png_byte)(background->gray << shift); } if (!shift) { shift = 7; sp++; } else shift--; } break; } case 2: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; shift = 6; for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x03) == trans_color->gray) { *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); *sp |= (png_byte)(background->gray << shift); } else { png_byte p = (png_byte)((*sp >> shift) & 0x03); png_byte g = (png_byte)((gamma_table [p | (p << 2) | (p << 4) | (p << 6)] >> 6) & 0x03); *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); *sp |= (png_byte)(g << shift); } if (!shift) { shift = 6; sp++; } else shift -= 2; } } else #endif { sp = row; shift = 6; for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x03) == trans_color->gray) { *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); *sp |= (png_byte)(background->gray << shift); } if (!shift) { shift = 6; sp++; } else shift -= 2; } } break; } case 4: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; shift = 4; for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x0f) == trans_color->gray) { *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); *sp |= (png_byte)(background->gray << shift); } else { png_byte p = (png_byte)((*sp >> shift) & 0x0f); png_byte g = (png_byte)((gamma_table[p | (p << 4)] >> 4) & 0x0f); *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); *sp |= (png_byte)(g << shift); } if (!shift) { shift = 4; sp++; } else shift -= 4; } } else #endif { sp = row; shift = 4; for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x0f) == trans_color->gray) { *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); *sp |= (png_byte)(background->gray << shift); } if (!shift) { shift = 4; sp++; } else shift -= 4; } } break; } case 8: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; for (i = 0; i < row_width; i++, sp++) { if (*sp == trans_color->gray) { *sp = (png_byte)background->gray; } else { *sp = gamma_table[*sp]; } } } else #endif { sp = row; for (i = 0; i < row_width; i++, sp++) { if (*sp == trans_color->gray) { *sp = (png_byte)background->gray; } } } break; } case 16: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; for (i = 0; i < row_width; i++, sp += 2) { png_uint_16 v; v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); if (v == trans_color->gray) { /* Background is already in screen gamma */ *sp = (png_byte)((background->gray >> 8) & 0xff); *(sp + 1) = (png_byte)(background->gray & 0xff); } else { v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); } } } else #endif { sp = row; for (i = 0; i < row_width; i++, sp += 2) { png_uint_16 v; v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); if (v == trans_color->gray) { *sp = (png_byte)((background->gray >> 8) & 0xff); *(sp + 1) = (png_byte)(background->gray & 0xff); } } } break; } } break; } case PNG_COLOR_TYPE_RGB: { if (row_info->bit_depth == 8) { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; for (i = 0; i < row_width; i++, sp += 3) { if (*sp == trans_color->red && *(sp + 1) == trans_color->green && *(sp + 2) == trans_color->blue) { *sp = (png_byte)background->red; *(sp + 1) = (png_byte)background->green; *(sp + 2) = (png_byte)background->blue; } else { *sp = gamma_table[*sp]; *(sp + 1) = gamma_table[*(sp + 1)]; *(sp + 2) = gamma_table[*(sp + 2)]; } } } else #endif { sp = row; for (i = 0; i < row_width; i++, sp += 3) { if (*sp == trans_color->red && *(sp + 1) == trans_color->green && *(sp + 2) == trans_color->blue) { *sp = (png_byte)background->red; *(sp + 1) = (png_byte)background->green; *(sp + 2) = (png_byte)background->blue; } } } } else /* if (row_info->bit_depth == 16) */ { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; for (i = 0; i < row_width; i++, sp += 6) { png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); if (r == trans_color->red && g == trans_color->green && b == trans_color->blue) { /* Background is already in screen gamma */ *sp = (png_byte)((background->red >> 8) & 0xff); *(sp + 1) = (png_byte)(background->red & 0xff); *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); *(sp + 3) = (png_byte)(background->green & 0xff); *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); *(sp + 5) = (png_byte)(background->blue & 0xff); } else { png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; *(sp + 2) = (png_byte)((v >> 8) & 0xff); *(sp + 3) = (png_byte)(v & 0xff); v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; *(sp + 4) = (png_byte)((v >> 8) & 0xff); *(sp + 5) = (png_byte)(v & 0xff); } } } else #endif { sp = row; for (i = 0; i < row_width; i++, sp += 6) { png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); if (r == trans_color->red && g == trans_color->green && b == trans_color->blue) { *sp = (png_byte)((background->red >> 8) & 0xff); *(sp + 1) = (png_byte)(background->red & 0xff); *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); *(sp + 3) = (png_byte)(background->green & 0xff); *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); *(sp + 5) = (png_byte)(background->blue & 0xff); } } } } break; } case PNG_COLOR_TYPE_GRAY_ALPHA: { if (row_info->bit_depth == 8) { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 2, dp++) { png_uint_16 a = *(sp + 1); if (a == 0xff) { *dp = gamma_table[*sp]; } else if (a == 0) { /* Background is already in screen gamma */ *dp = (png_byte)background->gray; } else { png_byte v, w; v = gamma_to_1[*sp]; png_composite(w, v, a, background_1->gray); *dp = gamma_from_1[w]; } } } else #endif { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 2, dp++) { png_byte a = *(sp + 1); if (a == 0xff) { *dp = *sp; } #ifdef PNG_READ_GAMMA_SUPPORTED else if (a == 0) { *dp = (png_byte)background->gray; } else { png_composite(*dp, *sp, a, background_1->gray); } #else *dp = (png_byte)background->gray; #endif } } } else /* if (png_ptr->bit_depth == 16) */ { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 4, dp += 2) { png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); if (a == (png_uint_16)0xffff) { png_uint_16 v; v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *dp = (png_byte)((v >> 8) & 0xff); *(dp + 1) = (png_byte)(v & 0xff); } #ifdef PNG_READ_GAMMA_SUPPORTED else if (a == 0) #else else #endif { /* Background is already in screen gamma */ *dp = (png_byte)((background->gray >> 8) & 0xff); *(dp + 1) = (png_byte)(background->gray & 0xff); } #ifdef PNG_READ_GAMMA_SUPPORTED else { png_uint_16 g, v, w; g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; png_composite_16(v, g, a, background_1->gray); w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; *dp = (png_byte)((w >> 8) & 0xff); *(dp + 1) = (png_byte)(w & 0xff); } #endif } } else #endif { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 4, dp += 2) { png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); if (a == (png_uint_16)0xffff) { png_memcpy(dp, sp, 2); } #ifdef PNG_READ_GAMMA_SUPPORTED else if (a == 0) #else else #endif { *dp = (png_byte)((background->gray >> 8) & 0xff); *(dp + 1) = (png_byte)(background->gray & 0xff); } #ifdef PNG_READ_GAMMA_SUPPORTED else { png_uint_16 g, v; g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); png_composite_16(v, g, a, background_1->gray); *dp = (png_byte)((v >> 8) & 0xff); *(dp + 1) = (png_byte)(v & 0xff); } #endif } } } break; } case PNG_COLOR_TYPE_RGB_ALPHA: { if (row_info->bit_depth == 8) { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 4, dp += 3) { png_byte a = *(sp + 3); if (a == 0xff) { *dp = gamma_table[*sp]; *(dp + 1) = gamma_table[*(sp + 1)]; *(dp + 2) = gamma_table[*(sp + 2)]; } else if (a == 0) { /* Background is already in screen gamma */ *dp = (png_byte)background->red; *(dp + 1) = (png_byte)background->green; *(dp + 2) = (png_byte)background->blue; } else { png_byte v, w; v = gamma_to_1[*sp]; png_composite(w, v, a, background_1->red); *dp = gamma_from_1[w]; v = gamma_to_1[*(sp + 1)]; png_composite(w, v, a, background_1->green); *(dp + 1) = gamma_from_1[w]; v = gamma_to_1[*(sp + 2)]; png_composite(w, v, a, background_1->blue); *(dp + 2) = gamma_from_1[w]; } } } else #endif { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 4, dp += 3) { png_byte a = *(sp + 3); if (a == 0xff) { *dp = *sp; *(dp + 1) = *(sp + 1); *(dp + 2) = *(sp + 2); } else if (a == 0) { *dp = (png_byte)background->red; *(dp + 1) = (png_byte)background->green; *(dp + 2) = (png_byte)background->blue; } else { png_composite(*dp, *sp, a, background->red); png_composite(*(dp + 1), *(sp + 1), a, background->green); png_composite(*(dp + 2), *(sp + 2), a, background->blue); } } } } else /* if (row_info->bit_depth == 16) */ { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 8, dp += 6) { png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) << 8) + (png_uint_16)(*(sp + 7))); if (a == (png_uint_16)0xffff) { png_uint_16 v; v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *dp = (png_byte)((v >> 8) & 0xff); *(dp + 1) = (png_byte)(v & 0xff); v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; *(dp + 2) = (png_byte)((v >> 8) & 0xff); *(dp + 3) = (png_byte)(v & 0xff); v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; *(dp + 4) = (png_byte)((v >> 8) & 0xff); *(dp + 5) = (png_byte)(v & 0xff); } else if (a == 0) { /* Background is already in screen gamma */ *dp = (png_byte)((background->red >> 8) & 0xff); *(dp + 1) = (png_byte)(background->red & 0xff); *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); *(dp + 3) = (png_byte)(background->green & 0xff); *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); *(dp + 5) = (png_byte)(background->blue & 0xff); } else { png_uint_16 v, w, x; v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; png_composite_16(w, v, a, background_1->red); x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; *dp = (png_byte)((x >> 8) & 0xff); *(dp + 1) = (png_byte)(x & 0xff); v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; png_composite_16(w, v, a, background_1->green); x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; *(dp + 2) = (png_byte)((x >> 8) & 0xff); *(dp + 3) = (png_byte)(x & 0xff); v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; png_composite_16(w, v, a, background_1->blue); x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; *(dp + 4) = (png_byte)((x >> 8) & 0xff); *(dp + 5) = (png_byte)(x & 0xff); } } } else #endif { sp = row; dp = row; for (i = 0; i < row_width; i++, sp += 8, dp += 6) { png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) << 8) + (png_uint_16)(*(sp + 7))); if (a == (png_uint_16)0xffff) { png_memcpy(dp, sp, 6); } else if (a == 0) { *dp = (png_byte)((background->red >> 8) & 0xff); *(dp + 1) = (png_byte)(background->red & 0xff); *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); *(dp + 3) = (png_byte)(background->green & 0xff); *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); *(dp + 5) = (png_byte)(background->blue & 0xff); } else { png_uint_16 v; png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + *(sp + 3)); png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + *(sp + 5)); png_composite_16(v, r, a, background->red); *dp = (png_byte)((v >> 8) & 0xff); *(dp + 1) = (png_byte)(v & 0xff); png_composite_16(v, g, a, background->green); *(dp + 2) = (png_byte)((v >> 8) & 0xff); *(dp + 3) = (png_byte)(v & 0xff); png_composite_16(v, b, a, background->blue); *(dp + 4) = (png_byte)((v >> 8) & 0xff); *(dp + 5) = (png_byte)(v & 0xff); } } } } break; } } if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; row_info->channels--; row_info->pixel_depth = (png_byte)(row_info->channels * row_info->bit_depth); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } } #endif #ifdef PNG_READ_GAMMA_SUPPORTED /* Gamma correct the image, avoiding the alpha channel. Make sure * you do this after you deal with the transparency issue on grayscale * or RGB images. If your bit depth is 8, use gamma_table, if it * is 16, use gamma_16_table and gamma_shift. Build these with * build_gamma_table(). */ void /* PRIVATE */ png_do_gamma(png_row_infop row_info, png_bytep row, png_bytep gamma_table, png_uint_16pp gamma_16_table, int gamma_shift) { png_bytep sp; png_uint_32 i; png_uint_32 row_width=row_info->width; png_debug(1, "in png_do_gamma"); if ( ((row_info->bit_depth <= 8 && gamma_table != NULL) || (row_info->bit_depth == 16 && gamma_16_table != NULL))) { switch (row_info->color_type) { case PNG_COLOR_TYPE_RGB: { if (row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp++; *sp = gamma_table[*sp]; sp++; *sp = gamma_table[*sp]; sp++; } } else /* if (row_info->bit_depth == 16) */ { sp = row; for (i = 0; i < row_width; i++) { png_uint_16 v; v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; } } break; } case PNG_COLOR_TYPE_RGB_ALPHA: { if (row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp++; *sp = gamma_table[*sp]; sp++; *sp = gamma_table[*sp]; sp++; sp++; } } else /* if (row_info->bit_depth == 16) */ { sp = row; for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 4; } } break; } case PNG_COLOR_TYPE_GRAY_ALPHA: { if (row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp += 2; } } else /* if (row_info->bit_depth == 16) */ { sp = row; for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 4; } } break; } case PNG_COLOR_TYPE_GRAY: { if (row_info->bit_depth == 2) { sp = row; for (i = 0; i < row_width; i += 4) { int a = *sp & 0xc0; int b = *sp & 0x30; int c = *sp & 0x0c; int d = *sp & 0x03; *sp = (png_byte)( ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); sp++; } } if (row_info->bit_depth == 4) { sp = row; for (i = 0; i < row_width; i += 2) { int msb = *sp & 0xf0; int lsb = *sp & 0x0f; *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); sp++; } } else if (row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp++; } } else if (row_info->bit_depth == 16) { sp = row; for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; } } break; } } } } #endif #ifdef PNG_READ_EXPAND_SUPPORTED /* Expands a palette row to an RGB or RGBA row depending * upon whether you supply trans and num_trans. */ void /* PRIVATE */ png_do_expand_palette(png_row_infop row_info, png_bytep row, png_colorp palette, png_bytep trans_alpha, int num_trans) { int shift, value; png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; png_debug(1, "in png_do_expand_palette"); if ( row_info->color_type == PNG_COLOR_TYPE_PALETTE) { if (row_info->bit_depth < 8) { switch (row_info->bit_depth) { case 1: { sp = row + (png_size_t)((row_width - 1) >> 3); dp = row + (png_size_t)row_width - 1; shift = 7 - (int)((row_width + 7) & 0x07); for (i = 0; i < row_width; i++) { if ((*sp >> shift) & 0x01) *dp = 1; else *dp = 0; if (shift == 7) { shift = 0; sp--; } else shift++; dp--; } break; } case 2: { sp = row + (png_size_t)((row_width - 1) >> 2); dp = row + (png_size_t)row_width - 1; shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); for (i = 0; i < row_width; i++) { value = (*sp >> shift) & 0x03; *dp = (png_byte)value; if (shift == 6) { shift = 0; sp--; } else shift += 2; dp--; } break; } case 4: { sp = row + (png_size_t)((row_width - 1) >> 1); dp = row + (png_size_t)row_width - 1; shift = (int)((row_width & 0x01) << 2); for (i = 0; i < row_width; i++) { value = (*sp >> shift) & 0x0f; *dp = (png_byte)value; if (shift == 4) { shift = 0; sp--; } else shift += 4; dp--; } break; } } row_info->bit_depth = 8; row_info->pixel_depth = 8; row_info->rowbytes = row_width; } switch (row_info->bit_depth) { case 8: { if (trans_alpha != NULL) { sp = row + (png_size_t)row_width - 1; dp = row + (png_size_t)(row_width << 2) - 1; for (i = 0; i < row_width; i++) { if ((int)(*sp) >= num_trans) *dp-- = 0xff; else *dp-- = trans_alpha[*sp]; *dp-- = palette[*sp].blue; *dp-- = palette[*sp].green; *dp-- = palette[*sp].red; sp--; } row_info->bit_depth = 8; row_info->pixel_depth = 32; row_info->rowbytes = row_width * 4; row_info->color_type = 6; row_info->channels = 4; } else { sp = row + (png_size_t)row_width - 1; dp = row + (png_size_t)(row_width * 3) - 1; for (i = 0; i < row_width; i++) { *dp-- = palette[*sp].blue; *dp-- = palette[*sp].green; *dp-- = palette[*sp].red; sp--; } row_info->bit_depth = 8; row_info->pixel_depth = 24; row_info->rowbytes = row_width * 3; row_info->color_type = 2; row_info->channels = 3; } break; } } } } /* If the bit depth < 8, it is expanded to 8. Also, if the already * expanded transparency value is supplied, an alpha channel is built. */ void /* PRIVATE */ png_do_expand(png_row_infop row_info, png_bytep row, png_color_16p trans_value) { int shift, value; png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; png_debug(1, "in png_do_expand"); { if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); if (row_info->bit_depth < 8) { switch (row_info->bit_depth) { case 1: { gray = (png_uint_16)((gray&0x01)*0xff); sp = row + (png_size_t)((row_width - 1) >> 3); dp = row + (png_size_t)row_width - 1; shift = 7 - (int)((row_width + 7) & 0x07); for (i = 0; i < row_width; i++) { if ((*sp >> shift) & 0x01) *dp = 0xff; else *dp = 0; if (shift == 7) { shift = 0; sp--; } else shift++; dp--; } break; } case 2: { gray = (png_uint_16)((gray&0x03)*0x55); sp = row + (png_size_t)((row_width - 1) >> 2); dp = row + (png_size_t)row_width - 1; shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); for (i = 0; i < row_width; i++) { value = (*sp >> shift) & 0x03; *dp = (png_byte)(value | (value << 2) | (value << 4) | (value << 6)); if (shift == 6) { shift = 0; sp--; } else shift += 2; dp--; } break; } case 4: { gray = (png_uint_16)((gray&0x0f)*0x11); sp = row + (png_size_t)((row_width - 1) >> 1); dp = row + (png_size_t)row_width - 1; shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); for (i = 0; i < row_width; i++) { value = (*sp >> shift) & 0x0f; *dp = (png_byte)(value | (value << 4)); if (shift == 4) { shift = 0; sp--; } else shift = 4; dp--; } break; } } row_info->bit_depth = 8; row_info->pixel_depth = 8; row_info->rowbytes = row_width; } if (trans_value != NULL) { if (row_info->bit_depth == 8) { gray = gray & 0xff; sp = row + (png_size_t)row_width - 1; dp = row + (png_size_t)(row_width << 1) - 1; for (i = 0; i < row_width; i++) { if (*sp == gray) *dp-- = 0; else *dp-- = 0xff; *dp-- = *sp--; } } else if (row_info->bit_depth == 16) { png_byte gray_high = (gray >> 8) & 0xff; png_byte gray_low = gray & 0xff; sp = row + row_info->rowbytes - 1; dp = row + (row_info->rowbytes << 1) - 1; for (i = 0; i < row_width; i++) { if (*(sp - 1) == gray_high && *(sp) == gray_low) { *dp-- = 0; *dp-- = 0; } else { *dp-- = 0xff; *dp-- = 0xff; } *dp-- = *sp--; *dp-- = *sp--; } } row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; row_info->channels = 2; row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) { if (row_info->bit_depth == 8) { png_byte red = trans_value->red & 0xff; png_byte green = trans_value->green & 0xff; png_byte blue = trans_value->blue & 0xff; sp = row + (png_size_t)row_info->rowbytes - 1; dp = row + (png_size_t)(row_width << 2) - 1; for (i = 0; i < row_width; i++) { if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) *dp-- = 0; else *dp-- = 0xff; *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; } } else if (row_info->bit_depth == 16) { png_byte red_high = (trans_value->red >> 8) & 0xff; png_byte green_high = (trans_value->green >> 8) & 0xff; png_byte blue_high = (trans_value->blue >> 8) & 0xff; png_byte red_low = trans_value->red & 0xff; png_byte green_low = trans_value->green & 0xff; png_byte blue_low = trans_value->blue & 0xff; sp = row + row_info->rowbytes - 1; dp = row + (png_size_t)(row_width << 3) - 1; for (i = 0; i < row_width; i++) { if (*(sp - 5) == red_high && *(sp - 4) == red_low && *(sp - 3) == green_high && *(sp - 2) == green_low && *(sp - 1) == blue_high && *(sp ) == blue_low) { *dp-- = 0; *dp-- = 0; } else { *dp-- = 0xff; *dp-- = 0xff; } *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; } } row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; row_info->channels = 4; row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } } #endif #ifdef PNG_READ_QUANTIZE_SUPPORTED void /* PRIVATE */ png_do_quantize(png_row_infop row_info, png_bytep row, png_bytep palette_lookup, png_bytep quantize_lookup) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; png_debug(1, "in png_do_quantize"); { if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup && row_info->bit_depth == 8) { int r, g, b, p; sp = row; dp = row; for (i = 0; i < row_width; i++) { r = *sp++; g = *sp++; b = *sp++; /* This looks real messy, but the compiler will reduce * it down to a reasonable formula. For example, with * 5 bits per color, we get: * p = (((r >> 3) & 0x1f) << 10) | * (((g >> 3) & 0x1f) << 5) | * ((b >> 3) & 0x1f); */ p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << (PNG_QUANTIZE_BLUE_BITS)) | ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); *dp++ = palette_lookup[p]; } row_info->color_type = PNG_COLOR_TYPE_PALETTE; row_info->channels = 1; row_info->pixel_depth = row_info->bit_depth; row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && palette_lookup != NULL && row_info->bit_depth == 8) { int r, g, b, p; sp = row; dp = row; for (i = 0; i < row_width; i++) { r = *sp++; g = *sp++; b = *sp++; sp++; p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << (PNG_QUANTIZE_BLUE_BITS)) | ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); *dp++ = palette_lookup[p]; } row_info->color_type = PNG_COLOR_TYPE_PALETTE; row_info->channels = 1; row_info->pixel_depth = row_info->bit_depth; row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && quantize_lookup && row_info->bit_depth == 8) { sp = row; for (i = 0; i < row_width; i++, sp++) { *sp = quantize_lookup[*sp]; } } } } #endif #ifdef PNG_FLOATING_POINT_SUPPORTED #ifdef PNG_READ_GAMMA_SUPPORTED static PNG_CONST int png_gamma_shift[] = {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; /* We build the 8- or 16-bit gamma tables here. Note that for 16-bit * tables, we don't make a full table if we are reducing to 8-bit in * the future. Note also how the gamma_16 tables are segmented so that * we don't need to allocate > 64K chunks for a full 16-bit table. * * See the PNG extensions document for an integer algorithm for creating * the gamma tables. Maybe we will implement that here someday. * * We should only reach this point if * * the file_gamma is known (i.e., the gAMA or sRGB chunk is present, * or the application has provided a file_gamma) * * AND * { * the screen_gamma is known * * OR * * RGB_to_gray transformation is being performed * } * * AND * { * the screen_gamma is different from the reciprocal of the * file_gamma by more than the specified threshold * * OR * * a background color has been specified and the file_gamma * and screen_gamma are not 1.0, within the specified threshold. * } */ void /* PRIVATE */ png_build_gamma_table(png_structp png_ptr, png_byte bit_depth) { png_debug(1, "in png_build_gamma_table"); if (bit_depth <= 8) { int i; double g; if (png_ptr->screen_gamma > .000001) g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); else g = 1.0; png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, (png_uint_32)256); for (i = 0; i < 256; i++) { png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, g) * 255.0 + .5); } #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) { g = 1.0 / (png_ptr->gamma); png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, (png_uint_32)256); for (i = 0; i < 256; i++) { png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, g) * 255.0 + .5); } png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, (png_uint_32)256); if (png_ptr->screen_gamma > 0.000001) g = 1.0 / png_ptr->screen_gamma; else g = png_ptr->gamma; /* Probably doing rgb_to_gray */ for (i = 0; i < 256; i++) { png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, g) * 255.0 + .5); } } #endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ } else { double g; int i, j, shift, num; int sig_bit; png_uint_32 ig; if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) { sig_bit = (int)png_ptr->sig_bit.red; if ((int)png_ptr->sig_bit.green > sig_bit) sig_bit = png_ptr->sig_bit.green; if ((int)png_ptr->sig_bit.blue > sig_bit) sig_bit = png_ptr->sig_bit.blue; } else { sig_bit = (int)png_ptr->sig_bit.gray; } if (sig_bit > 0) shift = 16 - sig_bit; else shift = 0; if (png_ptr->transformations & PNG_16_TO_8) { if (shift < (16 - PNG_MAX_GAMMA_8)) shift = (16 - PNG_MAX_GAMMA_8); } if (shift > 8) shift = 8; if (shift < 0) shift = 0; png_ptr->gamma_shift = (png_byte)shift; num = (1 << (8 - shift)); if (png_ptr->screen_gamma > .000001) g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); else g = 1.0; png_ptr->gamma_16_table = (png_uint_16pp)png_calloc(png_ptr, (png_uint_32)(num * png_sizeof(png_uint_16p))); if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) { double fin, fout; png_uint_32 last, max; for (i = 0; i < num; i++) { png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(256 * png_sizeof(png_uint_16))); } g = 1.0 / g; last = 0; for (i = 0; i < 256; i++) { fout = ((double)i + 0.5) / 256.0; fin = pow(fout, g); max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); while (last <= max) { png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] [(int)(last >> (8 - shift))] = (png_uint_16)( (png_uint_16)i | ((png_uint_16)i << 8)); last++; } } while (last < ((png_uint_32)num << 8)) { png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] [(int)(last >> (8 - shift))] = (png_uint_16)65535L; last++; } } else { for (i = 0; i < num; i++) { png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(256 * png_sizeof(png_uint_16))); ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); for (j = 0; j < 256; j++) { png_ptr->gamma_16_table[i][j] = (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / 65535.0, g) * 65535.0 + .5); } } } #if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) { g = 1.0 / (png_ptr->gamma); png_ptr->gamma_16_to_1 = (png_uint_16pp)png_calloc(png_ptr, (png_uint_32)(num * png_sizeof(png_uint_16p ))); for (i = 0; i < num; i++) { png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(256 * png_sizeof(png_uint_16))); ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); for (j = 0; j < 256; j++) { png_ptr->gamma_16_to_1[i][j] = (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / 65535.0, g) * 65535.0 + .5); } } if (png_ptr->screen_gamma > 0.000001) g = 1.0 / png_ptr->screen_gamma; else g = png_ptr->gamma; /* Probably doing rgb_to_gray */ png_ptr->gamma_16_from_1 = (png_uint_16pp)png_calloc(png_ptr, (png_uint_32)(num * png_sizeof(png_uint_16p))); for (i = 0; i < num; i++) { png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(256 * png_sizeof(png_uint_16))); ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); for (j = 0; j < 256; j++) { png_ptr->gamma_16_from_1[i][j] = (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / 65535.0, g) * 65535.0 + .5); } } } #endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ } } #endif /* To do: install integer version of png_build_gamma_table here */ #endif #ifdef PNG_MNG_FEATURES_SUPPORTED /* Undoes intrapixel differencing */ void /* PRIVATE */ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_read_intrapixel"); if ( (row_info->color_type & PNG_COLOR_MASK_COLOR)) { int bytes_per_pixel; png_uint_32 row_width = row_info->width; if (row_info->bit_depth == 8) { png_bytep rp; png_uint_32 i; if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 3; else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 4; else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); } } else if (row_info->bit_depth == 16) { png_bytep rp; png_uint_32 i; if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 6; else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 8; else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL); png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL); *(rp ) = (png_byte)((red >> 8) & 0xff); *(rp+1) = (png_byte)(red & 0xff); *(rp+4) = (png_byte)((blue >> 8) & 0xff); *(rp+5) = (png_byte)(blue & 0xff); } } } } #endif /* PNG_MNG_FEATURES_SUPPORTED */ #endif /* PNG_READ_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngrutil.c000066400000000000000000003063411271037650300234000ustar00rootroot00000000000000 /* pngrutil.c - utilities to read a PNG file * * Last changed in libpng 1.4.4 [August 26, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file contains routines that are only called from within * libpng itself during the course of reading an image. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_READ_SUPPORTED #include "pngpriv.h" # define png_strtod(p,a,b) strtod(a,b) png_uint_32 PNGAPI png_get_uint_31(png_structp png_ptr, png_bytep buf) { png_uint_32 i = png_get_uint_32(buf); if (i > PNG_UINT_31_MAX) png_error(png_ptr, "PNG unsigned integer out of range"); return (i); } #ifndef PNG_USE_READ_MACROS /* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ png_uint_32 (PNGAPI png_get_uint_32)(png_bytep buf) { png_uint_32 i = ((png_uint_32)(*(buf )) << 24) + ((png_uint_32)(*(buf + 1)) << 16) + ((png_uint_32)(*(buf + 2)) << 8) + ((png_uint_32)(*(buf + 3)) ) ; return (i); } /* Grab a signed 32-bit integer from a buffer in big-endian format. The * data is stored in the PNG file in two's complement format and there * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore * the following code does a two's complement to native conversion. */ png_int_32 (PNGAPI png_get_int_32)(png_bytep buf) { png_uint_32 u = png_get_uint_32(buf); if ((u & 0x80000000) == 0) /* non-negative */ return u; u = (u ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ return -(png_int_32)u; } /* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ png_uint_16 (PNGAPI png_get_uint_16)(png_bytep buf) { png_uint_16 i = ((png_uint_32)(*buf) << 8) + ((png_uint_32)(*(buf + 1))); return (i); } #endif /* PNG_USE_READ_MACROS */ /* Read the chunk header (length + type name). * Put the type name into png_ptr->chunk_name, and return the length. */ png_uint_32 /* PRIVATE */ png_read_chunk_header(png_structp png_ptr) { png_byte buf[8]; png_uint_32 length; #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that the chunk header is being read. * PNG_IO_CHUNK_HDR requires a single I/O call. */ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; #endif /* Read the length and the chunk name */ png_read_data(png_ptr, buf, 8); length = png_get_uint_31(png_ptr, buf); /* Put the chunk name into png_ptr->chunk_name */ png_memcpy(png_ptr->chunk_name, buf + 4, 4); png_debug2(0, "Reading %s chunk, length = %lu", png_ptr->chunk_name, length); /* Reset the crc and run it over the chunk name */ png_reset_crc(png_ptr); png_calculate_crc(png_ptr, png_ptr->chunk_name, 4); /* Check to see if chunk name is valid */ png_check_chunk_name(png_ptr, png_ptr->chunk_name); #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that chunk data will (possibly) be read. * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. */ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; #endif return length; } /* Read data, and (optionally) run it through the CRC. */ void /* PRIVATE */ png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) { if (png_ptr == NULL) return; png_read_data(png_ptr, buf, length); png_calculate_crc(png_ptr, buf, length); } /* Optionally skip data and then check the CRC. Depending on whether we * are reading a ancillary or critical chunk, and how the program has set * things up, we may calculate the CRC on the data and print a message. * Returns '1' if there was a CRC error, '0' otherwise. */ int /* PRIVATE */ png_crc_finish(png_structp png_ptr, png_uint_32 skip) { png_size_t i; png_size_t istop = png_ptr->zbuf_size; for (i = (png_size_t)skip; i > istop; i -= istop) { png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); } if (i) { png_crc_read(png_ptr, png_ptr->zbuf, i); } if (png_crc_error(png_ptr)) { if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) { png_chunk_warning(png_ptr, "CRC error"); } else { png_chunk_benign_error(png_ptr, "CRC error"); return (0); } return (1); } return (0); } /* Compare the CRC stored in the PNG file with that calculated by libpng from * the data it has read thus far. */ int /* PRIVATE */ png_crc_error(png_structp png_ptr) { png_byte crc_bytes[4]; png_uint_32 crc; int need_crc = 1; if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ { if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) need_crc = 0; } else /* critical */ { if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) need_crc = 0; } #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that the chunk CRC is being read */ /* PNG_IO_CHUNK_CRC requires the I/O to be done at once */ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; #endif png_read_data(png_ptr, crc_bytes, 4); if (need_crc) { crc = png_get_uint_32(crc_bytes); return ((int)(crc != png_ptr->crc)); } else return (0); } #if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ defined(PNG_READ_iCCP_SUPPORTED) static png_size_t png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size, png_bytep output, png_size_t output_size) { png_size_t count = 0; png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */ png_ptr->zstream.avail_in = size; while (1) { int ret, avail; /* Reset the output buffer each time round - we empty it * after every inflate call. */ png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = png_ptr->zbuf_size; ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out; /* First copy/count any new output - but only if we didn't * get an error code. */ if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0) { if (output != 0 && output_size > count) { int copy = output_size - count; if (avail < copy) copy = avail; png_memcpy(output + count, png_ptr->zbuf, copy); } count += avail; } if (ret == Z_OK) continue; /* Termination conditions - always reset the zstream, it * must be left in inflateInit state. */ png_ptr->zstream.avail_in = 0; inflateReset(&png_ptr->zstream); if (ret == Z_STREAM_END) return count; /* NOTE: may be zero. */ /* Now handle the error codes - the API always returns 0 * and the error message is dumped into the uncompressed * buffer if available. */ { PNG_CONST char *msg; if (png_ptr->zstream.msg != 0) msg = png_ptr->zstream.msg; else { #ifdef PNG_STDIO_SUPPORTED char umsg[52]; switch (ret) { case Z_BUF_ERROR: msg = "Buffer error in compressed datastream in %s chunk"; break; case Z_DATA_ERROR: msg = "Data error in compressed datastream in %s chunk"; break; default: msg = "Incomplete compressed datastream in %s chunk"; break; } png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name); msg = umsg; #else msg = "Damaged compressed datastream in chunk other than IDAT"; #endif } png_warning(png_ptr, msg); } /* 0 means an error - notice that this code simple ignores * zero length compressed chunks as a result. */ return 0; } } /* * Decompress trailing data in a chunk. The assumption is that chunkdata * points at an allocated area holding the contents of a chunk with a * trailing compressed part. What we get back is an allocated area * holding the original prefix part and an uncompressed version of the * trailing part (the malloc area passed in is freed). */ void /* PRIVATE */ png_decompress_chunk(png_structp png_ptr, int comp_type, png_size_t chunklength, png_size_t prefix_size, png_size_t *newlength) { /* The caller should guarantee this */ if (prefix_size > chunklength) { /* The recovery is to delete the chunk. */ png_warning(png_ptr, "invalid chunklength"); prefix_size = 0; /* To delete everything */ } else if (comp_type == PNG_COMPRESSION_TYPE_BASE) { png_size_t expanded_size = png_inflate(png_ptr, (png_bytep)(png_ptr->chunkdata + prefix_size), chunklength - prefix_size, 0/*output*/, 0/*output size*/); /* Now check the limits on this chunk - if the limit fails the * compressed data will be removed, the prefix will remain. */ #ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED if (png_ptr->user_chunk_malloc_max && (prefix_size + expanded_size >= png_ptr->user_chunk_malloc_max - 1)) #else # ifdef PNG_USER_CHUNK_MALLOC_MAX if ((PNG_USER_CHUNK_MALLOC_MAX > 0) && prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1) # endif #endif png_warning(png_ptr, "Exceeded size limit while expanding chunk"); /* If the size is zero either there was an error and a message * has already been output (warning) or the size really is zero * and we have nothing to do - the code will exit through the * error case below. */ #if defined(PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED) || \ defined(PNG_USER_CHUNK_MALLOC_MAX) else #endif if (expanded_size > 0) { /* Success (maybe) - really uncompress the chunk. */ png_size_t new_size = 0; png_charp text = png_malloc_warn(png_ptr, prefix_size + expanded_size + 1); if (text != NULL) { png_memcpy(text, png_ptr->chunkdata, prefix_size); new_size = png_inflate(png_ptr, (png_bytep)(png_ptr->chunkdata + prefix_size), chunklength - prefix_size, (png_bytep)(text + prefix_size), expanded_size); text[prefix_size + expanded_size] = 0; /* just in case */ if (new_size == expanded_size) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = text; *newlength = prefix_size + expanded_size; return; /* The success return! */ } png_warning(png_ptr, "png_inflate logic error"); png_free(png_ptr, text); } else png_warning(png_ptr, "Not enough memory to decompress chunk"); } } else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ { #ifdef PNG_STDIO_SUPPORTED char umsg[50]; png_snprintf(umsg, sizeof umsg, "Unknown zTXt compression type %d", comp_type); png_warning(png_ptr, umsg); #else png_warning(png_ptr, "Unknown zTXt compression type"); #endif /* The recovery is to simply drop the data. */ } /* Generic error return - leave the prefix, delete the compressed * data, reallocate the chunkdata to remove the potentially large * amount of compressed data. */ { png_charp text = png_malloc_warn(png_ptr, prefix_size + 1); if (text != NULL) { if (prefix_size > 0) png_memcpy(text, png_ptr->chunkdata, prefix_size); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = text; /* This is an extra zero in the 'uncompressed' part. */ *(png_ptr->chunkdata + prefix_size) = 0x00; } /* Ignore a malloc error here - it is safe. */ } *newlength = prefix_size; } #endif /* Read and check the IDHR chunk */ void /* PRIVATE */ png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte buf[13]; png_uint_32 width, height; int bit_depth, color_type, compression_type, filter_type; int interlace_type; png_debug(1, "in png_handle_IHDR"); if (png_ptr->mode & PNG_HAVE_IHDR) png_error(png_ptr, "Out of place IHDR"); /* Check the length */ if (length != 13) png_error(png_ptr, "Invalid IHDR chunk"); png_ptr->mode |= PNG_HAVE_IHDR; png_crc_read(png_ptr, buf, 13); png_crc_finish(png_ptr, 0); width = png_get_uint_31(png_ptr, buf); height = png_get_uint_31(png_ptr, buf + 4); bit_depth = buf[8]; color_type = buf[9]; compression_type = buf[10]; filter_type = buf[11]; interlace_type = buf[12]; /* Set internal variables */ png_ptr->width = width; png_ptr->height = height; png_ptr->bit_depth = (png_byte)bit_depth; png_ptr->interlaced = (png_byte)interlace_type; png_ptr->color_type = (png_byte)color_type; #ifdef PNG_MNG_FEATURES_SUPPORTED png_ptr->filter_type = (png_byte)filter_type; #endif png_ptr->compression_type = (png_byte)compression_type; /* Find number of channels */ switch (png_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_PALETTE: png_ptr->channels = 1; break; case PNG_COLOR_TYPE_RGB: png_ptr->channels = 3; break; case PNG_COLOR_TYPE_GRAY_ALPHA: png_ptr->channels = 2; break; case PNG_COLOR_TYPE_RGB_ALPHA: png_ptr->channels = 4; break; } /* Set up other useful info */ png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels); png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); png_debug1(3, "channels = %d", png_ptr->channels); png_debug1(3, "rowbytes = %lu", png_ptr->rowbytes); png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_type); } /* Read and check the palette */ void /* PRIVATE */ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_color palette[PNG_MAX_PALETTE_LENGTH]; int num, i; #ifdef PNG_POINTER_INDEXING_SUPPORTED png_colorp pal_ptr; #endif png_debug(1, "in png_handle_PLTE"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before PLTE"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid PLTE after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) png_error(png_ptr, "Duplicate PLTE chunk"); png_ptr->mode |= PNG_HAVE_PLTE; if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) { png_warning(png_ptr, "Ignoring PLTE chunk in grayscale PNG"); png_crc_finish(png_ptr, length); return; } #ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) { png_crc_finish(png_ptr, length); return; } #endif if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) { if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) { png_warning(png_ptr, "Invalid palette chunk"); png_crc_finish(png_ptr, length); return; } else { png_error(png_ptr, "Invalid palette chunk"); } } num = (int)length / 3; #ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) { png_byte buf[3]; png_crc_read(png_ptr, buf, 3); pal_ptr->red = buf[0]; pal_ptr->green = buf[1]; pal_ptr->blue = buf[2]; } #else for (i = 0; i < num; i++) { png_byte buf[3]; png_crc_read(png_ptr, buf, 3); /* Don't depend upon png_color being any order */ palette[i].red = buf[0]; palette[i].green = buf[1]; palette[i].blue = buf[2]; } #endif /* If we actually NEED the PLTE chunk (ie for a paletted image), we do * whatever the normal CRC configuration tells us. However, if we * have an RGB image, the PLTE can be considered ancillary, so * we will act as though it is. */ #ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) #endif { png_crc_finish(png_ptr, 0); } #ifndef PNG_READ_OPT_PLTE_SUPPORTED else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ { /* If we don't want to use the data from an ancillary chunk, we have two options: an error abort, or a warning and we ignore the data in this chunk (which should be OK, since it's considered ancillary for a RGB or RGBA image). */ if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) { if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) { png_chunk_benign_error(png_ptr, "CRC error"); } else { png_chunk_warning(png_ptr, "CRC error"); return; } } /* Otherwise, we (optionally) emit a warning and use the chunk. */ else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) { png_chunk_warning(png_ptr, "CRC error"); } } #endif png_set_PLTE(png_ptr, info_ptr, palette, num); #ifdef PNG_READ_tRNS_SUPPORTED if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { if (png_ptr->num_trans > (png_uint_16)num) { png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); png_ptr->num_trans = (png_uint_16)num; } if (info_ptr->num_trans > (png_uint_16)num) { png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); info_ptr->num_trans = (png_uint_16)num; } } } #endif } void /* PRIVATE */ png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_debug(1, "in png_handle_IEND"); if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) { png_error(png_ptr, "No image in file"); } png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); if (length != 0) { png_warning(png_ptr, "Incorrect IEND chunk length"); } png_crc_finish(png_ptr, length); info_ptr = info_ptr; /* Quiet compiler warnings about unused info_ptr */ } #ifdef PNG_READ_gAMA_SUPPORTED void /* PRIVATE */ png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_fixed_point igamma; #ifdef PNG_FLOATING_POINT_SUPPORTED float file_gamma; #endif png_byte buf[4]; png_debug(1, "in png_handle_gAMA"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before gAMA"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid gAMA after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) /* Should be an error, but we can cope with it */ png_warning(png_ptr, "Out of place gAMA chunk"); if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) #ifdef PNG_READ_sRGB_SUPPORTED && !(info_ptr->valid & PNG_INFO_sRGB) #endif ) { png_warning(png_ptr, "Duplicate gAMA chunk"); png_crc_finish(png_ptr, length); return; } if (length != 4) { png_warning(png_ptr, "Incorrect gAMA chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 4); if (png_crc_finish(png_ptr, 0)) return; igamma = (png_fixed_point)png_get_uint_32(buf); /* Check for zero gamma */ if (igamma == 0) { png_warning(png_ptr, "Ignoring gAMA chunk with gamma=0"); return; } #ifdef PNG_READ_sRGB_SUPPORTED if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) { png_warning(png_ptr, "Ignoring incorrect gAMA value when sRGB is also present"); #ifdef PNG_CONSOLE_IO_SUPPORTED fprintf(stderr, "gamma = (%d/100000)", (int)igamma); #endif return; } #endif /* PNG_READ_sRGB_SUPPORTED */ #ifdef PNG_FLOATING_POINT_SUPPORTED file_gamma = (float)igamma / (float)100000.0; # ifdef PNG_READ_GAMMA_SUPPORTED png_ptr->gamma = file_gamma; # endif png_set_gAMA(png_ptr, info_ptr, file_gamma); #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_set_gAMA_fixed(png_ptr, info_ptr, igamma); #endif } #endif #ifdef PNG_READ_sBIT_SUPPORTED void /* PRIVATE */ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_size_t truelen; png_byte buf[4]; png_debug(1, "in png_handle_sBIT"); buf[0] = buf[1] = buf[2] = buf[3] = 0; if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before sBIT"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid sBIT after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) { /* Should be an error, but we can cope with it */ png_warning(png_ptr, "Out of place sBIT chunk"); } if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) { png_warning(png_ptr, "Duplicate sBIT chunk"); png_crc_finish(png_ptr, length); return; } if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) truelen = 3; else truelen = (png_size_t)png_ptr->channels; if (length != truelen || length > 4) { png_warning(png_ptr, "Incorrect sBIT chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0)) return; if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) { png_ptr->sig_bit.red = buf[0]; png_ptr->sig_bit.green = buf[1]; png_ptr->sig_bit.blue = buf[2]; png_ptr->sig_bit.alpha = buf[3]; } else { png_ptr->sig_bit.gray = buf[0]; png_ptr->sig_bit.red = buf[0]; png_ptr->sig_bit.green = buf[0]; png_ptr->sig_bit.blue = buf[0]; png_ptr->sig_bit.alpha = buf[1]; } png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); } #endif #ifdef PNG_READ_cHRM_SUPPORTED void /* PRIVATE */ png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte buf[32]; #ifdef PNG_FLOATING_POINT_SUPPORTED float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; #endif png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, int_y_green, int_x_blue, int_y_blue; png_uint_32 uint_x, uint_y; png_debug(1, "in png_handle_cHRM"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before cHRM"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid cHRM after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) /* Should be an error, but we can cope with it */ png_warning(png_ptr, "Missing PLTE before cHRM"); if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) #ifdef PNG_READ_sRGB_SUPPORTED && !(info_ptr->valid & PNG_INFO_sRGB) #endif ) { png_warning(png_ptr, "Duplicate cHRM chunk"); png_crc_finish(png_ptr, length); return; } if (length != 32) { png_warning(png_ptr, "Incorrect cHRM chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 32); if (png_crc_finish(png_ptr, 0)) return; uint_x = png_get_uint_32(buf); uint_y = png_get_uint_32(buf + 4); int_x_white = (png_fixed_point)uint_x; int_y_white = (png_fixed_point)uint_y; uint_x = png_get_uint_32(buf + 8); uint_y = png_get_uint_32(buf + 12); int_x_red = (png_fixed_point)uint_x; int_y_red = (png_fixed_point)uint_y; uint_x = png_get_uint_32(buf + 16); uint_y = png_get_uint_32(buf + 20); int_x_green = (png_fixed_point)uint_x; int_y_green = (png_fixed_point)uint_y; uint_x = png_get_uint_32(buf + 24); uint_y = png_get_uint_32(buf + 28); int_x_blue = (png_fixed_point)uint_x; int_y_blue = (png_fixed_point)uint_y; #ifdef PNG_FLOATING_POINT_SUPPORTED white_x = (float)int_x_white / (float)100000.0; white_y = (float)int_y_white / (float)100000.0; red_x = (float)int_x_red / (float)100000.0; red_y = (float)int_y_red / (float)100000.0; green_x = (float)int_x_green / (float)100000.0; green_y = (float)int_y_green / (float)100000.0; blue_x = (float)int_x_blue / (float)100000.0; blue_y = (float)int_y_blue / (float)100000.0; #endif #ifdef PNG_READ_sRGB_SUPPORTED if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) { if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) { png_warning(png_ptr, "Ignoring incorrect cHRM value when sRGB is also present"); #ifdef PNG_CONSOLE_IO_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED fprintf(stderr, "wx=%f, wy=%f, rx=%f, ry=%f\n", white_x, white_y, red_x, red_y); fprintf(stderr, "gx=%f, gy=%f, bx=%f, by=%f\n", green_x, green_y, blue_x, blue_y); #else fprintf(stderr, "wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", (long)int_x_white, (long)int_y_white, (long)int_x_red, (long)int_y_red); fprintf(stderr, "gx=%ld, gy=%ld, bx=%ld, by=%ld\n", (long)int_x_green, (long)int_y_green, (long)int_x_blue, (long)int_y_blue); #endif #endif /* PNG_CONSOLE_IO_SUPPORTED */ } return; } #endif /* PNG_READ_sRGB_SUPPORTED */ #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_set_cHRM_fixed(png_ptr, info_ptr, int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, int_y_green, int_x_blue, int_y_blue); #endif } #endif #ifdef PNG_READ_sRGB_SUPPORTED void /* PRIVATE */ png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { int intent; png_byte buf[1]; png_debug(1, "in png_handle_sRGB"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before sRGB"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid sRGB after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) /* Should be an error, but we can cope with it */ png_warning(png_ptr, "Out of place sRGB chunk"); if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) { png_warning(png_ptr, "Duplicate sRGB chunk"); png_crc_finish(png_ptr, length); return; } if (length != 1) { png_warning(png_ptr, "Incorrect sRGB chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 1); if (png_crc_finish(png_ptr, 0)) return; intent = buf[0]; /* Check for bad intent */ if (intent >= PNG_sRGB_INTENT_LAST) { png_warning(png_ptr, "Unknown sRGB intent"); return; } #if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) { png_fixed_point igamma; #ifdef PNG_FIXED_POINT_SUPPORTED igamma=info_ptr->int_gamma; #else # ifdef PNG_FLOATING_POINT_SUPPORTED igamma=(png_fixed_point)(info_ptr->gamma * 100000.); # endif #endif if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) { png_warning(png_ptr, "Ignoring incorrect gAMA value when sRGB is also present"); #ifdef PNG_CONSOLE_IO_SUPPORTED # ifdef PNG_FIXED_POINT_SUPPORTED fprintf(stderr, "incorrect gamma=(%d/100000)\n", (int)png_ptr->int_gamma); # else # ifdef PNG_FLOATING_POINT_SUPPORTED fprintf(stderr, "incorrect gamma=%f\n", png_ptr->gamma); # endif # endif #endif } } #endif /* PNG_READ_gAMA_SUPPORTED */ #ifdef PNG_READ_cHRM_SUPPORTED #ifdef PNG_FIXED_POINT_SUPPORTED if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) { png_warning(png_ptr, "Ignoring incorrect cHRM value when sRGB is also present"); } #endif /* PNG_FIXED_POINT_SUPPORTED */ #endif /* PNG_READ_cHRM_SUPPORTED */ png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); } #endif /* PNG_READ_sRGB_SUPPORTED */ #ifdef PNG_READ_iCCP_SUPPORTED void /* PRIVATE */ png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) /* Note: this does not properly handle chunks that are > 64K under DOS */ { png_byte compression_type; png_bytep pC; png_charp profile; png_uint_32 skip = 0; png_uint_32 profile_size, profile_length; png_size_t slength, prefix_length, data_length; png_debug(1, "in png_handle_iCCP"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before iCCP"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid iCCP after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->mode & PNG_HAVE_PLTE) /* Should be an error, but we can cope with it */ png_warning(png_ptr, "Out of place iCCP chunk"); if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) { png_warning(png_ptr, "Duplicate iCCP chunk"); png_crc_finish(png_ptr, length); return; } #ifdef PNG_MAX_MALLOC_64K if (length > (png_uint_32)65535L) { png_warning(png_ptr, "iCCP chunk too large to fit in memory"); skip = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, skip)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; for (profile = png_ptr->chunkdata; *profile; profile++) /* Empty loop to find end of name */ ; ++profile; /* There should be at least one zero (the compression type byte) * following the separator, and we should be on it */ if ( profile >= png_ptr->chunkdata + slength - 1) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "Malformed iCCP chunk"); return; } /* Compression_type should always be zero */ compression_type = *profile++; if (compression_type) { png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); compression_type = 0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 wrote nonzero) */ } prefix_length = profile - png_ptr->chunkdata; png_decompress_chunk(png_ptr, compression_type, slength, prefix_length, &data_length); profile_length = data_length - prefix_length; if ( prefix_length > data_length || profile_length < 4) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "Profile size field missing from iCCP chunk"); return; } /* Check the profile_size recorded in the first 32 bits of the ICC profile */ pC = (png_bytep)(png_ptr->chunkdata + prefix_length); profile_size = ((*(pC ))<<24) | ((*(pC + 1))<<16) | ((*(pC + 2))<< 8) | ((*(pC + 3)) ); if (profile_size < profile_length) profile_length = profile_size; if (profile_size > profile_length) { #ifdef PNG_STDIO_SUPPORTED char umsg[50]; #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "Ignoring truncated iCCP profile"); #ifdef PNG_STDIO_SUPPORTED png_snprintf(umsg, 50, "declared profile size = %lu", (unsigned long)profile_size); png_warning(png_ptr, umsg); png_snprintf(umsg, 50, "actual profile length = %lu", (unsigned long)profile_length); png_warning(png_ptr, umsg); #endif return; } png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata, compression_type, png_ptr->chunkdata + prefix_length, profile_length); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; } #endif /* PNG_READ_iCCP_SUPPORTED */ #ifdef PNG_READ_sPLT_SUPPORTED void /* PRIVATE */ png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) /* Note: this does not properly handle chunks that are > 64K under DOS */ { png_bytep entry_start; png_sPLT_t new_palette; #ifdef PNG_POINTER_INDEXING_SUPPORTED png_sPLT_entryp pp; #endif int data_length, entry_size, i; png_uint_32 skip = 0; png_size_t slength; png_debug(1, "in png_handle_sPLT"); #ifdef PNG_USER_LIMITS_SUPPORTED if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); return; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for sPLT"); png_crc_finish(png_ptr, length); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before sPLT"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid sPLT after IDAT"); png_crc_finish(png_ptr, length); return; } #ifdef PNG_MAX_MALLOC_64K if (length > (png_uint_32)65535L) { png_warning(png_ptr, "sPLT chunk too large to fit in memory"); skip = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, skip)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; entry_start++) /* Empty loop to find end of name */ ; ++entry_start; /* A sample depth should follow the separator, and we should be on it */ if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "malformed sPLT chunk"); return; } new_palette.depth = *entry_start++; entry_size = (new_palette.depth == 8 ? 6 : 10); data_length = (slength - (entry_start - (png_bytep)png_ptr->chunkdata)); /* Integrity-check the data length */ if (data_length % entry_size) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "sPLT chunk has bad length"); return; } new_palette.nentries = (png_int_32) ( data_length / entry_size); if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / png_sizeof(png_sPLT_entry))) { png_warning(png_ptr, "sPLT chunk too long"); return; } new_palette.entries = (png_sPLT_entryp)png_malloc_warn( png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); if (new_palette.entries == NULL) { png_warning(png_ptr, "sPLT chunk requires too much memory"); return; } #ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { pp = new_palette.entries + i; if (new_palette.depth == 8) { pp->red = *entry_start++; pp->green = *entry_start++; pp->blue = *entry_start++; pp->alpha = *entry_start++; } else { pp->red = png_get_uint_16(entry_start); entry_start += 2; pp->green = png_get_uint_16(entry_start); entry_start += 2; pp->blue = png_get_uint_16(entry_start); entry_start += 2; pp->alpha = png_get_uint_16(entry_start); entry_start += 2; } pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } #else pp = new_palette.entries; for (i = 0; i < new_palette.nentries; i++) { if (new_palette.depth == 8) { pp[i].red = *entry_start++; pp[i].green = *entry_start++; pp[i].blue = *entry_start++; pp[i].alpha = *entry_start++; } else { pp[i].red = png_get_uint_16(entry_start); entry_start += 2; pp[i].green = png_get_uint_16(entry_start); entry_start += 2; pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; } pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } #endif /* Discard all chunk data except the name and stash that */ new_palette.name = png_ptr->chunkdata; png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_free(png_ptr, new_palette.entries); } #endif /* PNG_READ_sPLT_SUPPORTED */ #ifdef PNG_READ_tRNS_SUPPORTED void /* PRIVATE */ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_tRNS"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before tRNS"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid tRNS after IDAT"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { png_warning(png_ptr, "Duplicate tRNS chunk"); png_crc_finish(png_ptr, length); return; } if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { png_byte buf[2]; if (length != 2) { png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 2); png_ptr->num_trans = 1; png_ptr->trans_color.gray = png_get_uint_16(buf); } else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { png_byte buf[6]; if (length != 6) { png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, (png_size_t)length); png_ptr->num_trans = 1; png_ptr->trans_color.red = png_get_uint_16(buf); png_ptr->trans_color.green = png_get_uint_16(buf + 2); png_ptr->trans_color.blue = png_get_uint_16(buf + 4); } else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (!(png_ptr->mode & PNG_HAVE_PLTE)) { /* Should be an error, but we can cope with it. */ png_warning(png_ptr, "Missing PLTE before tRNS"); } if (length > (png_uint_32)png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH) { png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); return; } if (length == 0) { png_warning(png_ptr, "Zero length tRNS chunk"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, readbuf, (png_size_t)length); png_ptr->num_trans = (png_uint_16)length; } else { png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); png_crc_finish(png_ptr, length); return; } if (png_crc_finish(png_ptr, 0)) { png_ptr->num_trans = 0; return; } png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, &(png_ptr->trans_color)); } #endif #ifdef PNG_READ_bKGD_SUPPORTED void /* PRIVATE */ png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_size_t truelen; png_byte buf[6]; png_debug(1, "in png_handle_bKGD"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before bKGD"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid bKGD after IDAT"); png_crc_finish(png_ptr, length); return; } else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && !(png_ptr->mode & PNG_HAVE_PLTE)) { png_warning(png_ptr, "Missing PLTE before bKGD"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) { png_warning(png_ptr, "Duplicate bKGD chunk"); png_crc_finish(png_ptr, length); return; } if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) truelen = 1; else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) truelen = 6; else truelen = 2; if (length != truelen) { png_warning(png_ptr, "Incorrect bKGD chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, truelen); if (png_crc_finish(png_ptr, 0)) return; /* We convert the index value into RGB components so that we can allow * arbitrary RGB values for background when we have transparency, and * so it is easy to determine the RGB values of the background color * from the info_ptr struct. */ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { png_ptr->background.index = buf[0]; if (info_ptr && info_ptr->num_palette) { if (buf[0] >= info_ptr->num_palette) { png_warning(png_ptr, "Incorrect bKGD chunk index value"); return; } png_ptr->background.red = (png_uint_16)png_ptr->palette[buf[0]].red; png_ptr->background.green = (png_uint_16)png_ptr->palette[buf[0]].green; png_ptr->background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue; } } else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ { png_ptr->background.red = png_ptr->background.green = png_ptr->background.blue = png_ptr->background.gray = png_get_uint_16(buf); } else { png_ptr->background.red = png_get_uint_16(buf); png_ptr->background.green = png_get_uint_16(buf + 2); png_ptr->background.blue = png_get_uint_16(buf + 4); } png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); } #endif #ifdef PNG_READ_hIST_SUPPORTED void /* PRIVATE */ png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { unsigned int num, i; png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; png_debug(1, "in png_handle_hIST"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before hIST"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid hIST after IDAT"); png_crc_finish(png_ptr, length); return; } else if (!(png_ptr->mode & PNG_HAVE_PLTE)) { png_warning(png_ptr, "Missing PLTE before hIST"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) { png_warning(png_ptr, "Duplicate hIST chunk"); png_crc_finish(png_ptr, length); return; } num = length / 2 ; if (num != (unsigned int) png_ptr->num_palette || num > (unsigned int) PNG_MAX_PALETTE_LENGTH) { png_warning(png_ptr, "Incorrect hIST chunk length"); png_crc_finish(png_ptr, length); return; } for (i = 0; i < num; i++) { png_byte buf[2]; png_crc_read(png_ptr, buf, 2); readbuf[i] = png_get_uint_16(buf); } if (png_crc_finish(png_ptr, 0)) return; png_set_hIST(png_ptr, info_ptr, readbuf); } #endif #ifdef PNG_READ_pHYs_SUPPORTED void /* PRIVATE */ png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte buf[9]; png_uint_32 res_x, res_y; int unit_type; png_debug(1, "in png_handle_pHYs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before pHYs"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid pHYs after IDAT"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { png_warning(png_ptr, "Duplicate pHYs chunk"); png_crc_finish(png_ptr, length); return; } if (length != 9) { png_warning(png_ptr, "Incorrect pHYs chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0)) return; res_x = png_get_uint_32(buf); res_y = png_get_uint_32(buf + 4); unit_type = buf[8]; png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); } #endif #ifdef PNG_READ_oFFs_SUPPORTED void /* PRIVATE */ png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte buf[9]; png_int_32 offset_x, offset_y; int unit_type; png_debug(1, "in png_handle_oFFs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before oFFs"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid oFFs after IDAT"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { png_warning(png_ptr, "Duplicate oFFs chunk"); png_crc_finish(png_ptr, length); return; } if (length != 9) { png_warning(png_ptr, "Incorrect oFFs chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 9); if (png_crc_finish(png_ptr, 0)) return; offset_x = png_get_int_32(buf); offset_y = png_get_int_32(buf + 4); unit_type = buf[8]; png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); } #endif #ifdef PNG_READ_pCAL_SUPPORTED /* Read the pCAL chunk (described in the PNG Extensions document) */ void /* PRIVATE */ png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_int_32 X0, X1; png_byte type, nparams; png_charp buf, units, endptr; png_charpp params; png_size_t slength; int i; png_debug(1, "in png_handle_pCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before pCAL"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid pCAL after IDAT"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) { png_warning(png_ptr, "Duplicate pCAL chunk"); png_crc_finish(png_ptr, length); return; } png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)", length + 1); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); if (png_ptr->chunkdata == NULL) { png_warning(png_ptr, "No memory for pCAL purpose"); return; } slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, 0)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ png_debug(3, "Finding end of pCAL purpose string"); for (buf = png_ptr->chunkdata; *buf; buf++) /* Empty loop */ ; endptr = png_ptr->chunkdata + slength; /* We need to have at least 12 bytes after the purpose string in order to get the parameter information. */ if (endptr <= buf + 12) { png_warning(png_ptr, "Invalid pCAL data"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); X0 = png_get_int_32((png_bytep)buf+1); X1 = png_get_int_32((png_bytep)buf+5); type = buf[9]; nparams = buf[10]; units = buf + 11; png_debug(3, "Checking pCAL equation type and number of parameters"); /* Check that we have the right number of parameters for known equation types. */ if ((type == PNG_EQUATION_LINEAR && nparams != 2) || (type == PNG_EQUATION_BASE_E && nparams != 3) || (type == PNG_EQUATION_ARBITRARY && nparams != 3) || (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) { png_warning(png_ptr, "Invalid pCAL parameters for equation type"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } else if (type >= PNG_EQUATION_LAST) { png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); } for (buf = units; *buf; buf++) /* Empty loop to move past the units string. */ ; png_debug(3, "Allocating pCAL parameters array"); params = (png_charpp)png_malloc_warn(png_ptr, (png_size_t)(nparams * png_sizeof(png_charp))); if (params == NULL) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_warning(png_ptr, "No memory for pCAL params"); return; } /* Get pointers to the start of each parameter string. */ for (i = 0; i < (int)nparams; i++) { buf++; /* Skip the null string terminator from previous parameter. */ png_debug1(3, "Reading pCAL parameter %d", i); for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) /* Empty loop to move past each parameter string */ ; /* Make sure we haven't run out of data yet */ if (buf > endptr) { png_warning(png_ptr, "Invalid pCAL data"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_free(png_ptr, params); return; } } png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams, units, params); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_free(png_ptr, params); } #endif #ifdef PNG_READ_sCAL_SUPPORTED /* Read the sCAL chunk */ void /* PRIVATE */ png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_charp ep; #ifdef PNG_FLOATING_POINT_SUPPORTED double width, height; png_charp vp; #else #ifdef PNG_FIXED_POINT_SUPPORTED png_charp swidth, sheight; #endif #endif png_size_t slength; png_debug(1, "in png_handle_sCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before sCAL"); else if (png_ptr->mode & PNG_HAVE_IDAT) { png_warning(png_ptr, "Invalid sCAL after IDAT"); png_crc_finish(png_ptr, length); return; } else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { png_warning(png_ptr, "Duplicate sCAL chunk"); png_crc_finish(png_ptr, length); return; } png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)", length + 1); png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); if (png_ptr->chunkdata == NULL) { png_warning(png_ptr, "Out of memory while processing sCAL chunk"); png_crc_finish(png_ptr, length); return; } slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, 0)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ ep = png_ptr->chunkdata + 1; /* Skip unit byte */ #ifdef PNG_FLOATING_POINT_SUPPORTED width = png_strtod(png_ptr, ep, &vp); if (*vp) { png_warning(png_ptr, "malformed width string in sCAL chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } #else #ifdef PNG_FIXED_POINT_SUPPORTED swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); if (swidth == NULL) { png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_memcpy(swidth, ep, png_strlen(ep)); #endif #endif for (ep = png_ptr->chunkdata; *ep; ep++) /* Empty loop */ ; ep++; if (png_ptr->chunkdata + slength < ep) { png_warning(png_ptr, "Truncated sCAL chunk"); #if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) png_free(png_ptr, swidth); #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } #ifdef PNG_FLOATING_POINT_SUPPORTED height = png_strtod(png_ptr, ep, &vp); if (*vp) { png_warning(png_ptr, "malformed height string in sCAL chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } #else #ifdef PNG_FIXED_POINT_SUPPORTED sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); if (sheight == NULL) { png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_free(png_ptr, swidth); return; } png_memcpy(sheight, ep, png_strlen(ep)); #endif #endif if (png_ptr->chunkdata + slength < ep #ifdef PNG_FLOATING_POINT_SUPPORTED || width <= 0. || height <= 0. #endif ) { png_warning(png_ptr, "Invalid sCAL data"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; #if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) png_free(png_ptr, swidth); png_free(png_ptr, sheight); #endif return; } #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_sCAL(png_ptr, info_ptr, png_ptr->chunkdata[0], width, height); #else #ifdef PNG_FIXED_POINT_SUPPORTED png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0], swidth, sheight); #endif #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; #if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) png_free(png_ptr, swidth); png_free(png_ptr, sheight); #endif } #endif #ifdef PNG_READ_tIME_SUPPORTED void /* PRIVATE */ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_byte buf[7]; png_time mod_time; png_debug(1, "in png_handle_tIME"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Out of place tIME chunk"); else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) { png_warning(png_ptr, "Duplicate tIME chunk"); png_crc_finish(png_ptr, length); return; } if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; if (length != 7) { png_warning(png_ptr, "Incorrect tIME chunk length"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, 7); if (png_crc_finish(png_ptr, 0)) return; mod_time.second = buf[6]; mod_time.minute = buf[5]; mod_time.hour = buf[4]; mod_time.day = buf[3]; mod_time.month = buf[2]; mod_time.year = png_get_uint_16(buf); png_set_tIME(png_ptr, info_ptr, &mod_time); } #endif #ifdef PNG_READ_tEXt_SUPPORTED /* Note: this does not properly handle chunks that are > 64K under DOS */ void /* PRIVATE */ png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_textp text_ptr; png_charp key; png_charp text; png_uint_32 skip = 0; png_size_t slength; int ret; png_debug(1, "in png_handle_tEXt"); #ifdef PNG_USER_LIMITS_SUPPORTED if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); return; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for tEXt"); png_crc_finish(png_ptr, length); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before tEXt"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; #ifdef PNG_MAX_MALLOC_64K if (length > (png_uint_32)65535L) { png_warning(png_ptr, "tEXt chunk too large to fit in memory"); skip = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); if (png_ptr->chunkdata == NULL) { png_warning(png_ptr, "No memory to process text chunk"); return; } slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, skip)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } key = png_ptr->chunkdata; key[slength] = 0x00; for (text = key; *text; text++) /* Empty loop to find end of key */ ; if (text != key + slength) text++; text_ptr = (png_textp)png_malloc_warn(png_ptr, png_sizeof(png_text)); if (text_ptr == NULL) { png_warning(png_ptr, "Not enough memory to process text chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; text_ptr->key = key; #ifdef PNG_iTXt_SUPPORTED text_ptr->lang = NULL; text_ptr->lang_key = NULL; text_ptr->itxt_length = 0; #endif text_ptr->text = text; text_ptr->text_length = png_strlen(text); ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; png_free(png_ptr, text_ptr); if (ret) png_warning(png_ptr, "Insufficient memory to process text chunk"); } #endif #ifdef PNG_READ_zTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ void /* PRIVATE */ png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_textp text_ptr; png_charp text; int comp_type; int ret; png_size_t slength, prefix_len, data_len; png_debug(1, "in png_handle_zTXt"); #ifdef PNG_USER_LIMITS_SUPPORTED if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); return; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for zTXt"); png_crc_finish(png_ptr, length); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before zTXt"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; #ifdef PNG_MAX_MALLOC_64K /* We will no doubt have problems with chunks even half this size, but there is no hard and fast rule to tell us where to stop. */ if (length > (png_uint_32)65535L) { png_warning(png_ptr, "zTXt chunk too large to fit in memory"); png_crc_finish(png_ptr, length); return; } #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); if (png_ptr->chunkdata == NULL) { png_warning(png_ptr, "Out of memory processing zTXt chunk"); return; } slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, 0)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; for (text = png_ptr->chunkdata; *text; text++) /* Empty loop */ ; /* zTXt must have some text after the chunkdataword */ if (text >= png_ptr->chunkdata + slength - 2) { png_warning(png_ptr, "Truncated zTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } else { comp_type = *(++text); if (comp_type != PNG_TEXT_COMPRESSION_zTXt) { png_warning(png_ptr, "Unknown compression type in zTXt chunk"); comp_type = PNG_TEXT_COMPRESSION_zTXt; } text++; /* Skip the compression_method byte */ } prefix_len = text - png_ptr->chunkdata; png_decompress_chunk(png_ptr, comp_type, (png_size_t)length, prefix_len, &data_len); text_ptr = (png_textp)png_malloc_warn(png_ptr, png_sizeof(png_text)); if (text_ptr == NULL) { png_warning(png_ptr, "Not enough memory to process zTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } text_ptr->compression = comp_type; text_ptr->key = png_ptr->chunkdata; #ifdef PNG_iTXt_SUPPORTED text_ptr->lang = NULL; text_ptr->lang_key = NULL; text_ptr->itxt_length = 0; #endif text_ptr->text = png_ptr->chunkdata + prefix_len; text_ptr->text_length = data_len; ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_free(png_ptr, text_ptr); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; if (ret) png_error(png_ptr, "Insufficient memory to store zTXt chunk"); } #endif #ifdef PNG_READ_iTXt_SUPPORTED /* Note: this does not correctly handle chunks that are > 64K under DOS */ void /* PRIVATE */ png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_textp text_ptr; png_charp key, lang, text, lang_key; int comp_flag; int comp_type = 0; int ret; png_size_t slength, prefix_len, data_len; png_debug(1, "in png_handle_iTXt"); #ifdef PNG_USER_LIMITS_SUPPORTED if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); return; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for iTXt"); png_crc_finish(png_ptr, length); return; } } #endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) png_error(png_ptr, "Missing IHDR before iTXt"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; #ifdef PNG_MAX_MALLOC_64K /* We will no doubt have problems with chunks even half this size, but there is no hard and fast rule to tell us where to stop. */ if (length > (png_uint_32)65535L) { png_warning(png_ptr, "iTXt chunk too large to fit in memory"); png_crc_finish(png_ptr, length); return; } #endif png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); if (png_ptr->chunkdata == NULL) { png_warning(png_ptr, "No memory to process iTXt chunk"); return; } slength = (png_size_t)length; png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); if (png_crc_finish(png_ptr, 0)) { png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } png_ptr->chunkdata[slength] = 0x00; for (lang = png_ptr->chunkdata; *lang; lang++) /* Empty loop */ ; lang++; /* Skip NUL separator */ /* iTXt must have a language tag (possibly empty), two compression bytes, * translated keyword (possibly empty), and possibly some text after the * keyword */ if (lang >= png_ptr->chunkdata + slength - 3) { png_warning(png_ptr, "Truncated iTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } else { comp_flag = *lang++; comp_type = *lang++; } for (lang_key = lang; *lang_key; lang_key++) /* Empty loop */ ; lang_key++; /* Skip NUL separator */ if (lang_key >= png_ptr->chunkdata + slength) { png_warning(png_ptr, "Truncated iTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } for (text = lang_key; *text; text++) /* Empty loop */ ; text++; /* Skip NUL separator */ if (text >= png_ptr->chunkdata + slength) { png_warning(png_ptr, "Malformed iTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } prefix_len = text - png_ptr->chunkdata; key=png_ptr->chunkdata; if (comp_flag) png_decompress_chunk(png_ptr, comp_type, (size_t)length, prefix_len, &data_len); else data_len = png_strlen(png_ptr->chunkdata + prefix_len); text_ptr = (png_textp)png_malloc_warn(png_ptr, png_sizeof(png_text)); if (text_ptr == NULL) { png_warning(png_ptr, "Not enough memory to process iTXt chunk"); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; return; } text_ptr->compression = (int)comp_flag + 1; text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key); text_ptr->lang = png_ptr->chunkdata + (lang - key); text_ptr->itxt_length = data_len; text_ptr->text_length = 0; text_ptr->key = png_ptr->chunkdata; text_ptr->text = png_ptr->chunkdata + prefix_len; ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); png_free(png_ptr, text_ptr); png_free(png_ptr, png_ptr->chunkdata); png_ptr->chunkdata = NULL; if (ret) png_error(png_ptr, "Insufficient memory to store iTXt chunk"); } #endif /* This function is called when we haven't found a handler for a chunk. If there isn't a problem with the chunk itself (ie bad chunk name, CRC, or a critical chunk), the chunk is silently ignored -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which case it will be saved away to be written out later. */ void /* PRIVATE */ png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) { png_uint_32 skip = 0; png_debug(1, "in png_handle_unknown"); #ifdef PNG_USER_LIMITS_SUPPORTED if (png_ptr->user_chunk_cache_max != 0) { if (png_ptr->user_chunk_cache_max == 1) { png_crc_finish(png_ptr, length); return; } if (--png_ptr->user_chunk_cache_max == 1) { png_warning(png_ptr, "No space in chunk cache for unknown chunk"); png_crc_finish(png_ptr, length); return; } } #endif if (png_ptr->mode & PNG_HAVE_IDAT) { PNG_IDAT; if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* Not an IDAT */ png_ptr->mode |= PNG_AFTER_IDAT; } if (!(png_ptr->chunk_name[0] & 0x20)) { #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != PNG_HANDLE_CHUNK_ALWAYS #ifdef PNG_READ_USER_CHUNKS_SUPPORTED && png_ptr->read_user_chunk_fn == NULL #endif ) #endif png_chunk_error(png_ptr, "unknown critical chunk"); } #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) #ifdef PNG_READ_USER_CHUNKS_SUPPORTED || (png_ptr->read_user_chunk_fn != NULL) #endif ) { #ifdef PNG_MAX_MALLOC_64K if (length > (png_uint_32)65535L) { png_warning(png_ptr, "unknown chunk too large to fit in memory"); skip = length - (png_uint_32)65535L; length = (png_uint_32)65535L; } #endif png_memcpy((png_charp)png_ptr->unknown_chunk.name, (png_charp)png_ptr->chunk_name, png_sizeof(png_ptr->unknown_chunk.name)); png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] = '\0'; png_ptr->unknown_chunk.size = (png_size_t)length; if (length == 0) png_ptr->unknown_chunk.data = NULL; else { png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); } #ifdef PNG_READ_USER_CHUNKS_SUPPORTED if (png_ptr->read_user_chunk_fn != NULL) { /* Callback to user unknown chunk handler */ int ret; ret = (*(png_ptr->read_user_chunk_fn)) (png_ptr, &png_ptr->unknown_chunk); if (ret < 0) png_chunk_error(png_ptr, "error in user chunk"); if (ret == 0) { if (!(png_ptr->chunk_name[0] & 0x20)) #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != PNG_HANDLE_CHUNK_ALWAYS) #endif png_chunk_error(png_ptr, "unknown critical chunk"); png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); } } else #endif png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); png_free(png_ptr, png_ptr->unknown_chunk.data); png_ptr->unknown_chunk.data = NULL; } else #endif skip = length; png_crc_finish(png_ptr, skip); #ifndef PNG_READ_USER_CHUNKS_SUPPORTED info_ptr = info_ptr; /* Quiet compiler warnings about unused info_ptr */ #endif } /* This function is called to verify that a chunk name is valid. This function can't have the "critical chunk check" incorporated into it, since in the future we will need to be able to call user functions to handle unknown critical chunks after we check that the chunk name itself is valid. */ #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) void /* PRIVATE */ png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) { png_debug(1, "in png_check_chunk_name"); if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) { png_chunk_error(png_ptr, "invalid chunk type"); } } /* Combines the row recently read in with the existing pixels in the row. This routine takes care of alpha and transparency if requested. This routine also handles the two methods of progressive display of interlaced images, depending on the mask value. The mask value describes which pixels are to be combined with the row. The pattern always repeats every 8 pixels, so just 8 bits are needed. A one indicates the pixel is to be combined, a zero indicates the pixel is to be skipped. This is in addition to any alpha or transparency value associated with the pixel. If you want all pixels to be combined, pass 0xff (255) in mask. */ void /* PRIVATE */ png_combine_row(png_structp png_ptr, png_bytep row, int mask) { png_debug(1, "in png_combine_row"); if (mask == 0xff) { png_memcpy(row, png_ptr->row_buf + 1, PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); } else { switch (png_ptr->row_info.pixel_depth) { case 1: { png_bytep sp = png_ptr->row_buf + 1; png_bytep dp = row; int s_inc, s_start, s_end; int m = 0x80; int shift; png_uint_32 i; png_uint_32 row_width = png_ptr->width; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) { s_start = 0; s_end = 7; s_inc = 1; } else #endif { s_start = 7; s_end = 0; s_inc = -1; } shift = s_start; for (i = 0; i < row_width; i++) { if (m & mask) { int value; value = (*sp >> shift) & 0x01; *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); *dp |= (png_byte)(value << shift); } if (shift == s_end) { shift = s_start; sp++; dp++; } else shift += s_inc; if (m == 1) m = 0x80; else m >>= 1; } break; } case 2: { png_bytep sp = png_ptr->row_buf + 1; png_bytep dp = row; int s_start, s_end, s_inc; int m = 0x80; int shift; png_uint_32 i; png_uint_32 row_width = png_ptr->width; int value; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) { s_start = 0; s_end = 6; s_inc = 2; } else #endif { s_start = 6; s_end = 0; s_inc = -2; } shift = s_start; for (i = 0; i < row_width; i++) { if (m & mask) { value = (*sp >> shift) & 0x03; *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); *dp |= (png_byte)(value << shift); } if (shift == s_end) { shift = s_start; sp++; dp++; } else shift += s_inc; if (m == 1) m = 0x80; else m >>= 1; } break; } case 4: { png_bytep sp = png_ptr->row_buf + 1; png_bytep dp = row; int s_start, s_end, s_inc; int m = 0x80; int shift; png_uint_32 i; png_uint_32 row_width = png_ptr->width; int value; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) { s_start = 0; s_end = 4; s_inc = 4; } else #endif { s_start = 4; s_end = 0; s_inc = -4; } shift = s_start; for (i = 0; i < row_width; i++) { if (m & mask) { value = (*sp >> shift) & 0xf; *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); *dp |= (png_byte)(value << shift); } if (shift == s_end) { shift = s_start; sp++; dp++; } else shift += s_inc; if (m == 1) m = 0x80; else m >>= 1; } break; } default: { png_bytep sp = png_ptr->row_buf + 1; png_bytep dp = row; png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); png_uint_32 i; png_uint_32 row_width = png_ptr->width; png_byte m = 0x80; for (i = 0; i < row_width; i++) { if (m & mask) { png_memcpy(dp, sp, pixel_bytes); } sp += pixel_bytes; dp += pixel_bytes; if (m == 1) m = 0x80; else m >>= 1; } break; } } } } #ifdef PNG_READ_INTERLACING_SUPPORTED /* OLD pre-1.0.9 interface: void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, png_uint_32 transformations) */ void /* PRIVATE */ png_do_read_interlace(png_structp png_ptr) { png_row_infop row_info = &(png_ptr->row_info); png_bytep row = png_ptr->row_buf + 1; int pass = png_ptr->pass; png_uint_32 transformations = png_ptr->transformations; /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Offset to next interlace block */ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; png_debug(1, "in png_do_read_interlace"); if (row != NULL && row_info != NULL) { png_uint_32 final_width; final_width = row_info->width * png_pass_inc[pass]; switch (row_info->pixel_depth) { case 1: { png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); int sshift, dshift; int s_start, s_end, s_inc; int jstop = png_pass_inc[pass]; png_byte v; png_uint_32 i; int j; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)((row_info->width + 7) & 0x07); dshift = (int)((final_width + 7) & 0x07); s_start = 7; s_end = 0; s_inc = -1; } else #endif { sshift = 7 - (int)((row_info->width + 7) & 0x07); dshift = 7 - (int)((final_width + 7) & 0x07); s_start = 0; s_end = 7; s_inc = 1; } for (i = 0; i < row_info->width; i++) { v = (png_byte)((*sp >> sshift) & 0x01); for (j = 0; j < jstop; j++) { *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); *dp |= (png_byte)(v << dshift); if (dshift == s_end) { dshift = s_start; dp--; } else dshift += s_inc; } if (sshift == s_end) { sshift = s_start; sp--; } else sshift += s_inc; } break; } case 2: { png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); int sshift, dshift; int s_start, s_end, s_inc; int jstop = png_pass_inc[pass]; png_uint_32 i; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)(((row_info->width + 3) & 0x03) << 1); dshift = (int)(((final_width + 3) & 0x03) << 1); s_start = 6; s_end = 0; s_inc = -2; } else #endif { sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); s_start = 0; s_end = 6; s_inc = 2; } for (i = 0; i < row_info->width; i++) { png_byte v; int j; v = (png_byte)((*sp >> sshift) & 0x03); for (j = 0; j < jstop; j++) { *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); *dp |= (png_byte)(v << dshift); if (dshift == s_end) { dshift = s_start; dp--; } else dshift += s_inc; } if (sshift == s_end) { sshift = s_start; sp--; } else sshift += s_inc; } break; } case 4: { png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); int sshift, dshift; int s_start, s_end, s_inc; png_uint_32 i; int jstop = png_pass_inc[pass]; #ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)(((row_info->width + 1) & 0x01) << 2); dshift = (int)(((final_width + 1) & 0x01) << 2); s_start = 4; s_end = 0; s_inc = -4; } else #endif { sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); s_start = 0; s_end = 4; s_inc = 4; } for (i = 0; i < row_info->width; i++) { png_byte v = (png_byte)((*sp >> sshift) & 0xf); int j; for (j = 0; j < jstop; j++) { *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); *dp |= (png_byte)(v << dshift); if (dshift == s_end) { dshift = s_start; dp--; } else dshift += s_inc; } if (sshift == s_end) { sshift = s_start; sp--; } else sshift += s_inc; } break; } default: { png_size_t pixel_bytes = (row_info->pixel_depth >> 3); png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; int jstop = png_pass_inc[pass]; png_uint_32 i; for (i = 0; i < row_info->width; i++) { png_byte v[8]; int j; png_memcpy(v, sp, pixel_bytes); for (j = 0; j < jstop; j++) { png_memcpy(dp, v, pixel_bytes); dp -= pixel_bytes; } sp -= pixel_bytes; } break; } } row_info->width = final_width; row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); } #ifndef PNG_READ_PACKSWAP_SUPPORTED transformations = transformations; /* Silence compiler warning */ #endif } #endif /* PNG_READ_INTERLACING_SUPPORTED */ void /* PRIVATE */ png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter) { png_debug(1, "in png_read_filter_row"); png_debug2(2, "row = %lu, filter = %d", png_ptr->row_number, filter); switch (filter) { case PNG_FILTER_VALUE_NONE: break; case PNG_FILTER_VALUE_SUB: { png_uint_32 i; png_uint_32 istop = row_info->rowbytes; png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; png_bytep rp = row + bpp; png_bytep lp = row; for (i = bpp; i < istop; i++) { *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); rp++; } break; } case PNG_FILTER_VALUE_UP: { png_uint_32 i; png_uint_32 istop = row_info->rowbytes; png_bytep rp = row; png_bytep pp = prev_row; for (i = 0; i < istop; i++) { *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); rp++; } break; } case PNG_FILTER_VALUE_AVG: { png_uint_32 i; png_bytep rp = row; png_bytep pp = prev_row; png_bytep lp = row; png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; png_uint_32 istop = row_info->rowbytes - bpp; for (i = 0; i < bpp; i++) { *rp = (png_byte)(((int)(*rp) + ((int)(*pp++) / 2 )) & 0xff); rp++; } for (i = 0; i < istop; i++) { *rp = (png_byte)(((int)(*rp) + (int)(*pp++ + *lp++) / 2 ) & 0xff); rp++; } break; } case PNG_FILTER_VALUE_PAETH: { png_uint_32 i; png_bytep rp = row; png_bytep pp = prev_row; png_bytep lp = row; png_bytep cp = prev_row; png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; png_uint_32 istop=row_info->rowbytes - bpp; for (i = 0; i < bpp; i++) { *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); rp++; } for (i = 0; i < istop; i++) /* Use leftover rp,pp */ { int a, b, c, pa, pb, pc, p; a = *lp++; b = *pp++; c = *cp++; p = b - c; pc = a - c; #ifdef PNG_USE_ABS pa = abs(p); pb = abs(pc); pc = abs(p + pc); #else pa = p < 0 ? -p : p; pb = pc < 0 ? -pc : pc; pc = (p + pc) < 0 ? -(p + pc) : p + pc; #endif /* if (pa <= pb && pa <= pc) p = a; else if (pb <= pc) p = b; else p = c; */ p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; *rp = (png_byte)(((int)(*rp) + p) & 0xff); rp++; } break; } default: png_warning(png_ptr, "Ignoring bad adaptive filter type"); *row = 0; break; } } #ifdef PNG_SEQUENTIAL_READ_SUPPORTED void /* PRIVATE */ png_read_finish_row(png_structp png_ptr) { #ifdef PNG_READ_INTERLACING_SUPPORTED /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; /* Start of interlace block in the y direction */ PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; /* Offset to next interlace block in the y direction */ PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif /* PNG_READ_INTERLACING_SUPPORTED */ png_debug(1, "in png_read_finish_row"); png_ptr->row_number++; if (png_ptr->row_number < png_ptr->num_rows) return; #ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { png_ptr->row_number = 0; png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); do { png_ptr->pass++; if (png_ptr->pass >= 7) break; png_ptr->iwidth = (png_ptr->width + png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; if (!(png_ptr->transformations & PNG_INTERLACE)) { png_ptr->num_rows = (png_ptr->height + png_pass_yinc[png_ptr->pass] - 1 - png_pass_ystart[png_ptr->pass]) / png_pass_yinc[png_ptr->pass]; if (!(png_ptr->num_rows)) continue; } else /* if (png_ptr->transformations & PNG_INTERLACE) */ break; } while (png_ptr->iwidth == 0); if (png_ptr->pass < 7) return; } #endif /* PNG_READ_INTERLACING_SUPPORTED */ if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) { PNG_IDAT; char extra; int ret; png_ptr->zstream.next_out = (Byte *)&extra; png_ptr->zstream.avail_out = (uInt)1; for (;;) { if (!(png_ptr->zstream.avail_in)) { while (!png_ptr->idat_size) { png_byte chunk_length[4]; png_crc_finish(png_ptr, 0); png_read_data(png_ptr, chunk_length, 4); png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); png_reset_crc(png_ptr); png_crc_read(png_ptr, png_ptr->chunk_name, 4); if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) png_error(png_ptr, "Not enough image data"); } png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_in = png_ptr->zbuf; if (png_ptr->zbuf_size > png_ptr->idat_size) png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); png_ptr->idat_size -= png_ptr->zstream.avail_in; } ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); if (ret == Z_STREAM_END) { if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || png_ptr->idat_size) png_warning(png_ptr, "Extra compressed data"); png_ptr->mode |= PNG_AFTER_IDAT; png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; break; } if (ret != Z_OK) png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : "Decompression Error"); if (!(png_ptr->zstream.avail_out)) { png_warning(png_ptr, "Extra compressed data"); png_ptr->mode |= PNG_AFTER_IDAT; png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; break; } } png_ptr->zstream.avail_out = 0; } if (png_ptr->idat_size || png_ptr->zstream.avail_in) png_warning(png_ptr, "Extra compression data"); inflateReset(&png_ptr->zstream); png_ptr->mode |= PNG_AFTER_IDAT; } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ void /* PRIVATE */ png_read_start_row(png_structp png_ptr) { #ifdef PNG_READ_INTERLACING_SUPPORTED /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; /* Start of interlace block in the y direction */ PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; /* Offset to next interlace block in the y direction */ PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif int max_pixel_depth; png_size_t row_bytes; png_debug(1, "in png_read_start_row"); png_ptr->zstream.avail_in = 0; png_init_read_transformations(png_ptr); #ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { if (!(png_ptr->transformations & PNG_INTERLACE)) png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - png_pass_ystart[0]) / png_pass_yinc[0]; else png_ptr->num_rows = png_ptr->height; png_ptr->iwidth = (png_ptr->width + png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; } else #endif /* PNG_READ_INTERLACING_SUPPORTED */ { png_ptr->num_rows = png_ptr->height; png_ptr->iwidth = png_ptr->width; } max_pixel_depth = png_ptr->pixel_depth; #ifdef PNG_READ_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) max_pixel_depth = 8; #endif #ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (png_ptr->num_trans) max_pixel_depth = 32; else max_pixel_depth = 24; } else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { if (max_pixel_depth < 8) max_pixel_depth = 8; if (png_ptr->num_trans) max_pixel_depth *= 2; } else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { if (png_ptr->num_trans) { max_pixel_depth *= 4; max_pixel_depth /= 3; } } } #endif #ifdef PNG_READ_FILLER_SUPPORTED if (png_ptr->transformations & (PNG_FILLER)) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) max_pixel_depth = 32; else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { if (max_pixel_depth <= 8) max_pixel_depth = 16; else max_pixel_depth = 32; } else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { if (max_pixel_depth <= 32) max_pixel_depth = 32; else max_pixel_depth = 64; } } #endif #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED if (png_ptr->transformations & PNG_GRAY_TO_RGB) { if ( #ifdef PNG_READ_EXPAND_SUPPORTED (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || #endif #ifdef PNG_READ_FILLER_SUPPORTED (png_ptr->transformations & (PNG_FILLER)) || #endif png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (max_pixel_depth <= 16) max_pixel_depth = 32; else max_pixel_depth = 64; } else { if (max_pixel_depth <= 8) { if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) max_pixel_depth = 32; else max_pixel_depth = 24; } else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) max_pixel_depth = 64; else max_pixel_depth = 48; } } #endif #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) if (png_ptr->transformations & PNG_USER_TRANSFORM) { int user_pixel_depth = png_ptr->user_transform_depth* png_ptr->user_transform_channels; if (user_pixel_depth > max_pixel_depth) max_pixel_depth=user_pixel_depth; } #endif /* Align the width on the next larger 8 pixels. Mainly used * for interlacing */ row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); /* Calculate the maximum bytes needed, adding a byte and a pixel * for safety's sake */ row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + 1 + ((max_pixel_depth + 7) >> 3); #ifdef PNG_MAX_MALLOC_64K if (row_bytes > (png_uint_32)65536L) png_error(png_ptr, "This image requires a row greater than 64KB"); #endif if (row_bytes + 48 > png_ptr->old_big_row_buf_size) { png_free(png_ptr, png_ptr->big_row_buf); if (png_ptr->interlaced) png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, row_bytes + 48); else png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); png_ptr->old_big_row_buf_size = row_bytes + 48; #ifdef PNG_ALIGNED_MEMORY_SUPPORTED /* Use 16-byte aligned memory for row_buf with at least 16 bytes * of padding before and after row_buf. */ png_ptr->row_buf = png_ptr->big_row_buf + 32 - (((png_alloc_size_t)&(png_ptr->big_row_buf[0]) + 15) % 16); png_ptr->old_big_row_buf_size = row_bytes + 48; #else /* Use 32 bytes of padding before and 16 bytes after row_buf. */ png_ptr->row_buf = png_ptr->big_row_buf + 32; #endif png_ptr->old_big_row_buf_size = row_bytes + 48; } #ifdef PNG_MAX_MALLOC_64K if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) png_error(png_ptr, "This image requires a row greater than 64KB"); #endif if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1)) png_error(png_ptr, "Row has too many bytes to allocate in memory"); if (png_ptr->rowbytes + 1 > png_ptr->old_prev_row_size) { png_free(png_ptr, png_ptr->prev_row); png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( png_ptr->rowbytes + 1)); png_ptr->old_prev_row_size = png_ptr->rowbytes + 1; } png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); png_debug1(3, "width = %lu,", png_ptr->width); png_debug1(3, "height = %lu,", png_ptr->height); png_debug1(3, "iwidth = %lu,", png_ptr->iwidth); png_debug1(3, "num_rows = %lu,", png_ptr->num_rows); png_debug1(3, "rowbytes = %lu,", png_ptr->rowbytes); png_debug1(3, "irowbytes = %lu", PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); png_ptr->flags |= PNG_FLAG_ROW_INIT; } #endif /* PNG_READ_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngset.c000066400000000000000000001055431271037650300230350ustar00rootroot00000000000000 /* pngset.c - storage of image information into info struct * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * The functions here are used during reads to store data from the file * into the info struct, and during writes to store application data * into the info struct for writing into the file. This abstracts the * info struct and allows us to change the structure in the future. */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #include "pngpriv.h" #ifdef PNG_bKGD_SUPPORTED void PNGAPI png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) { png_debug1(1, "in %s storage function", "bKGD"); if (png_ptr == NULL || info_ptr == NULL) return; png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); info_ptr->valid |= PNG_INFO_bKGD; } #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_cHRM(png_structp png_ptr, png_infop info_ptr, double white_x, double white_y, double red_x, double red_y, double green_x, double green_y, double blue_x, double blue_y) { png_debug1(1, "in %s storage function", "cHRM"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->x_white = (float)white_x; info_ptr->y_white = (float)white_y; info_ptr->x_red = (float)red_x; info_ptr->y_red = (float)red_y; info_ptr->x_green = (float)green_x; info_ptr->y_green = (float)green_y; info_ptr->x_blue = (float)blue_x; info_ptr->y_blue = (float)blue_y; #ifdef PNG_FIXED_POINT_SUPPORTED info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); #endif info_ptr->valid |= PNG_INFO_cHRM; } #endif /* PNG_FLOATING_POINT_SUPPORTED */ #ifdef PNG_FIXED_POINT_SUPPORTED void PNGAPI png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { png_debug1(1, "in %s storage function", "cHRM fixed"); if (png_ptr == NULL || info_ptr == NULL) return; #ifdef PNG_CHECK_cHRM_SUPPORTED if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) #endif { info_ptr->int_x_white = white_x; info_ptr->int_y_white = white_y; info_ptr->int_x_red = red_x; info_ptr->int_y_red = red_y; info_ptr->int_x_green = green_x; info_ptr->int_y_green = green_y; info_ptr->int_x_blue = blue_x; info_ptr->int_y_blue = blue_y; #ifdef PNG_FLOATING_POINT_SUPPORTED info_ptr->x_white = (float)(white_x/100000.); info_ptr->y_white = (float)(white_y/100000.); info_ptr->x_red = (float)( red_x/100000.); info_ptr->y_red = (float)( red_y/100000.); info_ptr->x_green = (float)(green_x/100000.); info_ptr->y_green = (float)(green_y/100000.); info_ptr->x_blue = (float)( blue_x/100000.); info_ptr->y_blue = (float)( blue_y/100000.); #endif info_ptr->valid |= PNG_INFO_cHRM; } } #endif /* PNG_FIXED_POINT_SUPPORTED */ #endif /* PNG_cHRM_SUPPORTED */ #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) { double png_gamma; png_debug1(1, "in %s storage function", "gAMA"); if (png_ptr == NULL || info_ptr == NULL) return; /* Check for overflow */ if (file_gamma > 21474.83) { png_warning(png_ptr, "Limiting gamma to 21474.83"); png_gamma=21474.83; } else png_gamma = file_gamma; info_ptr->gamma = (float)png_gamma; #ifdef PNG_FIXED_POINT_SUPPORTED info_ptr->int_gamma = (int)(png_gamma*100000.+.5); #endif info_ptr->valid |= PNG_INFO_gAMA; if (png_gamma == 0.0) png_warning(png_ptr, "Setting gamma=0"); } #endif void PNGAPI png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point int_gamma) { png_fixed_point png_gamma; png_debug1(1, "in %s storage function", "gAMA"); if (png_ptr == NULL || info_ptr == NULL) return; if (int_gamma > (png_fixed_point)PNG_UINT_31_MAX) { png_warning(png_ptr, "Limiting gamma to 21474.83"); png_gamma=PNG_UINT_31_MAX; } else { if (int_gamma < 0) { png_warning(png_ptr, "Setting negative gamma to zero"); png_gamma = 0; } else png_gamma = int_gamma; } #ifdef PNG_FLOATING_POINT_SUPPORTED info_ptr->gamma = (float)(png_gamma/100000.); #endif #ifdef PNG_FIXED_POINT_SUPPORTED info_ptr->int_gamma = png_gamma; #endif info_ptr->valid |= PNG_INFO_gAMA; if (png_gamma == 0) png_warning(png_ptr, "Setting gamma=0"); } #endif #ifdef PNG_hIST_SUPPORTED void PNGAPI png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) { int i; png_debug1(1, "in %s storage function", "hIST"); if (png_ptr == NULL || info_ptr == NULL) return; if (info_ptr->num_palette == 0 || info_ptr->num_palette > PNG_MAX_PALETTE_LENGTH) { png_warning(png_ptr, "Invalid palette size, hIST allocation skipped"); return; } png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in * version 1.2.1 */ png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16)); if (png_ptr->hist == NULL) { png_warning(png_ptr, "Insufficient memory for hIST chunk data"); return; } for (i = 0; i < info_ptr->num_palette; i++) png_ptr->hist[i] = hist[i]; info_ptr->hist = png_ptr->hist; info_ptr->valid |= PNG_INFO_hIST; info_ptr->free_me |= PNG_FREE_HIST; } #endif void PNGAPI png_set_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type) { png_debug1(1, "in %s storage function", "IHDR"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->width = width; info_ptr->height = height; info_ptr->bit_depth = (png_byte)bit_depth; info_ptr->color_type = (png_byte)color_type; info_ptr->compression_type = (png_byte)compression_type; info_ptr->filter_type = (png_byte)filter_type; info_ptr->interlace_type = (png_byte)interlace_type; png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, info_ptr->compression_type, info_ptr->filter_type); if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) info_ptr->channels = 1; else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) info_ptr->channels = 3; else info_ptr->channels = 1; if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) info_ptr->channels++; info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); /* Check for potential overflow */ if (width > (PNG_UINT_32_MAX >> 3) /* 8-byte RGBA pixels */ - 64 /* bigrowbuf hack */ - 1 /* filter byte */ - 7*8 /* rounding of width to multiple of 8 pixels */ - 8) /* extra max_pixel_depth pad */ info_ptr->rowbytes = 0; else info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); } #ifdef PNG_oFFs_SUPPORTED void PNGAPI png_set_oFFs(png_structp png_ptr, png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, int unit_type) { png_debug1(1, "in %s storage function", "oFFs"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->x_offset = offset_x; info_ptr->y_offset = offset_y; info_ptr->offset_unit_type = (png_byte)unit_type; info_ptr->valid |= PNG_INFO_oFFs; } #endif #ifdef PNG_pCAL_SUPPORTED void PNGAPI png_set_pCAL(png_structp png_ptr, png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) { png_size_t length; int i; png_debug1(1, "in %s storage function", "pCAL"); if (png_ptr == NULL || info_ptr == NULL) return; length = png_strlen(purpose) + 1; png_debug1(3, "allocating purpose for info (%lu bytes)", (unsigned long)length); info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); if (info_ptr->pcal_purpose == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL purpose"); return; } png_memcpy(info_ptr->pcal_purpose, purpose, length); png_debug(3, "storing X0, X1, type, and nparams in info"); info_ptr->pcal_X0 = X0; info_ptr->pcal_X1 = X1; info_ptr->pcal_type = (png_byte)type; info_ptr->pcal_nparams = (png_byte)nparams; length = png_strlen(units) + 1; png_debug1(3, "allocating units for info (%lu bytes)", (unsigned long)length); info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); if (info_ptr->pcal_units == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL units"); return; } png_memcpy(info_ptr->pcal_units, units, length); info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, (png_size_t)((nparams + 1) * png_sizeof(png_charp))); if (info_ptr->pcal_params == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL params"); return; } png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp)); for (i = 0; i < nparams; i++) { length = png_strlen(params[i]) + 1; png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, (unsigned long)length); info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); if (info_ptr->pcal_params[i] == NULL) { png_warning(png_ptr, "Insufficient memory for pCAL parameter"); return; } png_memcpy(info_ptr->pcal_params[i], params[i], length); } info_ptr->valid |= PNG_INFO_pCAL; info_ptr->free_me |= PNG_FREE_PCAL; } #endif #if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) #ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI png_set_sCAL(png_structp png_ptr, png_infop info_ptr, int unit, double width, double height) { png_debug1(1, "in %s storage function", "sCAL"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->scal_unit = (png_byte)unit; info_ptr->scal_pixel_width = width; info_ptr->scal_pixel_height = height; info_ptr->valid |= PNG_INFO_sCAL; } #else #ifdef PNG_FIXED_POINT_SUPPORTED void PNGAPI png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, int unit, png_charp swidth, png_charp sheight) { png_size_t length; png_debug1(1, "in %s storage function", "sCAL"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->scal_unit = (png_byte)unit; length = png_strlen(swidth) + 1; png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)length); info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); if (info_ptr->scal_s_width == NULL) { png_warning(png_ptr, "Memory allocation failed while processing sCAL"); return; } png_memcpy(info_ptr->scal_s_width, swidth, length); length = png_strlen(sheight) + 1; png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)length); info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); if (info_ptr->scal_s_height == NULL) { png_free (png_ptr, info_ptr->scal_s_width); info_ptr->scal_s_width = NULL; png_warning(png_ptr, "Memory allocation failed while processing sCAL"); return; } png_memcpy(info_ptr->scal_s_height, sheight, length); info_ptr->valid |= PNG_INFO_sCAL; info_ptr->free_me |= PNG_FREE_SCAL; } #endif #endif #endif #ifdef PNG_pHYs_SUPPORTED void PNGAPI png_set_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type) { png_debug1(1, "in %s storage function", "pHYs"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->x_pixels_per_unit = res_x; info_ptr->y_pixels_per_unit = res_y; info_ptr->phys_unit_type = (png_byte)unit_type; info_ptr->valid |= PNG_INFO_pHYs; } #endif void PNGAPI png_set_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp palette, int num_palette) { png_debug1(1, "in %s storage function", "PLTE"); if (png_ptr == NULL || info_ptr == NULL) return; if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) { if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Invalid palette length"); else { png_warning(png_ptr, "Invalid palette length"); return; } } /* It may not actually be necessary to set png_ptr->palette here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. */ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead * of num_palette entries, in case of an invalid PNG file that has * too-large sample values. */ png_ptr->palette = (png_colorp)png_calloc(png_ptr, PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); info_ptr->palette = png_ptr->palette; info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; info_ptr->free_me |= PNG_FREE_PLTE; info_ptr->valid |= PNG_INFO_PLTE; } #ifdef PNG_sBIT_SUPPORTED void PNGAPI png_set_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p sig_bit) { png_debug1(1, "in %s storage function", "sBIT"); if (png_ptr == NULL || info_ptr == NULL) return; png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8)); info_ptr->valid |= PNG_INFO_sBIT; } #endif #ifdef PNG_sRGB_SUPPORTED void PNGAPI png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) { png_debug1(1, "in %s storage function", "sRGB"); if (png_ptr == NULL || info_ptr == NULL) return; info_ptr->srgb_intent = (png_byte)intent; info_ptr->valid |= PNG_INFO_sRGB; } void PNGAPI png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, int intent) { #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED float file_gamma; #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_fixed_point int_file_gamma; #endif #endif #ifdef PNG_cHRM_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; #endif png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y; #endif png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); if (png_ptr == NULL || info_ptr == NULL) return; png_set_sRGB(png_ptr, info_ptr, intent); #ifdef PNG_gAMA_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED file_gamma = (float).45455; png_set_gAMA(png_ptr, info_ptr, file_gamma); #endif #ifdef PNG_FIXED_POINT_SUPPORTED int_file_gamma = 45455L; png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); #endif #endif #ifdef PNG_cHRM_SUPPORTED int_white_x = 31270L; int_white_y = 32900L; int_red_x = 64000L; int_red_y = 33000L; int_green_x = 30000L; int_green_y = 60000L; int_blue_x = 15000L; int_blue_y = 6000L; #ifdef PNG_FLOATING_POINT_SUPPORTED white_x = (float).3127; white_y = (float).3290; red_x = (float).64; red_y = (float).33; green_x = (float).30; green_y = (float).60; blue_x = (float).15; blue_y = (float).06; #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_set_cHRM_fixed(png_ptr, info_ptr, int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y); #endif #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); #endif #endif /* cHRM */ } #endif /* sRGB */ #ifdef PNG_iCCP_SUPPORTED void PNGAPI png_set_iCCP(png_structp png_ptr, png_infop info_ptr, png_charp name, int compression_type, png_charp profile, png_uint_32 proflen) { png_charp new_iccp_name; png_charp new_iccp_profile; png_uint_32 length; png_debug1(1, "in %s storage function", "iCCP"); if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; length = png_strlen(name)+1; new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); if (new_iccp_name == NULL) { png_warning(png_ptr, "Insufficient memory to process iCCP chunk"); return; } png_memcpy(new_iccp_name, name, length); new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); if (new_iccp_profile == NULL) { png_free (png_ptr, new_iccp_name); png_warning(png_ptr, "Insufficient memory to process iCCP profile"); return; } png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; /* Compression is always zero but is here so the API and info structure * does not have to change if we introduce multiple compression types */ info_ptr->iccp_compression = (png_byte)compression_type; info_ptr->free_me |= PNG_FREE_ICCP; info_ptr->valid |= PNG_INFO_iCCP; } #endif #ifdef PNG_TEXT_SUPPORTED void PNGAPI png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, int num_text) { int ret; ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); if (ret) png_error(png_ptr, "Insufficient memory to store text"); } int /* PRIVATE */ png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, int num_text) { int i; png_debug1(1, "in %s storage function", ((png_ptr == NULL || png_ptr->chunk_name[0] == '\0') ? "text" : (png_const_charp)png_ptr->chunk_name)); if (png_ptr == NULL || info_ptr == NULL || num_text == 0) return(0); /* Make sure we have enough space in the "text" array in info_struct * to hold all of the incoming text_ptr objects. */ if (info_ptr->num_text + num_text > info_ptr->max_text) { if (info_ptr->text != NULL) { png_textp old_text; int old_max; old_max = info_ptr->max_text; info_ptr->max_text = info_ptr->num_text + num_text + 8; old_text = info_ptr->text; info_ptr->text = (png_textp)png_malloc_warn(png_ptr, (png_size_t)(info_ptr->max_text * png_sizeof(png_text))); if (info_ptr->text == NULL) { png_free(png_ptr, old_text); return(1); } png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * png_sizeof(png_text))); png_free(png_ptr, old_text); } else { info_ptr->max_text = num_text + 8; info_ptr->num_text = 0; info_ptr->text = (png_textp)png_malloc_warn(png_ptr, (png_size_t)(info_ptr->max_text * png_sizeof(png_text))); if (info_ptr->text == NULL) return(1); info_ptr->free_me |= PNG_FREE_TEXT; } png_debug1(3, "allocated %d entries for info_ptr->text", info_ptr->max_text); } for (i = 0; i < num_text; i++) { png_size_t text_length, key_len; png_size_t lang_len, lang_key_len; png_textp textp = &(info_ptr->text[info_ptr->num_text]); if (text_ptr[i].key == NULL) continue; key_len = png_strlen(text_ptr[i].key); if (text_ptr[i].compression <= 0) { lang_len = 0; lang_key_len = 0; } else #ifdef PNG_iTXt_SUPPORTED { /* Set iTXt data */ if (text_ptr[i].lang != NULL) lang_len = png_strlen(text_ptr[i].lang); else lang_len = 0; if (text_ptr[i].lang_key != NULL) lang_key_len = png_strlen(text_ptr[i].lang_key); else lang_key_len = 0; } #else /* PNG_iTXt_SUPPORTED */ { png_warning(png_ptr, "iTXt chunk not supported"); continue; } #endif if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') { text_length = 0; #ifdef PNG_iTXt_SUPPORTED if (text_ptr[i].compression > 0) textp->compression = PNG_ITXT_COMPRESSION_NONE; else #endif textp->compression = PNG_TEXT_COMPRESSION_NONE; } else { text_length = png_strlen(text_ptr[i].text); textp->compression = text_ptr[i].compression; } textp->key = (png_charp)png_malloc_warn(png_ptr, (png_size_t) (key_len + text_length + lang_len + lang_key_len + 4)); if (textp->key == NULL) return(1); png_debug2(2, "Allocated %lu bytes at %x in png_set_text", (unsigned long)(png_uint_32) (key_len + lang_len + lang_key_len + text_length + 4), (int)textp->key); png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len)); *(textp->key + key_len) = '\0'; #ifdef PNG_iTXt_SUPPORTED if (text_ptr[i].compression > 0) { textp->lang = textp->key + key_len + 1; png_memcpy(textp->lang, text_ptr[i].lang, lang_len); *(textp->lang + lang_len) = '\0'; textp->lang_key = textp->lang + lang_len + 1; png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); *(textp->lang_key + lang_key_len) = '\0'; textp->text = textp->lang_key + lang_key_len + 1; } else #endif { #ifdef PNG_iTXt_SUPPORTED textp->lang=NULL; textp->lang_key=NULL; #endif textp->text = textp->key + key_len + 1; } if (text_length) png_memcpy(textp->text, text_ptr[i].text, (png_size_t)(text_length)); *(textp->text + text_length) = '\0'; #ifdef PNG_iTXt_SUPPORTED if (textp->compression > 0) { textp->text_length = 0; textp->itxt_length = text_length; } else #endif { textp->text_length = text_length; #ifdef PNG_iTXt_SUPPORTED textp->itxt_length = 0; #endif } info_ptr->num_text++; png_debug1(3, "transferred text chunk %d", info_ptr->num_text); } return(0); } #endif #ifdef PNG_tIME_SUPPORTED void PNGAPI png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) { png_debug1(1, "in %s storage function", "tIME"); if (png_ptr == NULL || info_ptr == NULL || (png_ptr->mode & PNG_WROTE_tIME)) return; png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time)); info_ptr->valid |= PNG_INFO_tIME; } #endif #ifdef PNG_tRNS_SUPPORTED void PNGAPI png_set_tRNS(png_structp png_ptr, png_infop info_ptr, png_bytep trans_alpha, int num_trans, png_color_16p trans_color) { png_debug1(1, "in %s storage function", "tRNS"); if (png_ptr == NULL || info_ptr == NULL) return; if (trans_alpha != NULL) { /* It may not actually be necessary to set png_ptr->trans_alpha here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ png_ptr->trans_alpha = info_ptr->trans_alpha = (png_bytep)png_malloc(png_ptr, (png_size_t)PNG_MAX_PALETTE_LENGTH); if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) png_memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); } if (trans_color != NULL) { int sample_max = (1 << info_ptr->bit_depth); if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && (int)trans_color->gray > sample_max) || (info_ptr->color_type == PNG_COLOR_TYPE_RGB && ((int)trans_color->red > sample_max || (int)trans_color->green > sample_max || (int)trans_color->blue > sample_max))) png_warning(png_ptr, "tRNS chunk has out-of-range samples for bit_depth"); png_memcpy(&(info_ptr->trans_color), trans_color, png_sizeof(png_color_16)); if (num_trans == 0) num_trans = 1; } info_ptr->num_trans = (png_uint_16)num_trans; if (num_trans != 0) { info_ptr->valid |= PNG_INFO_tRNS; info_ptr->free_me |= PNG_FREE_TRNS; } } #endif #ifdef PNG_sPLT_SUPPORTED void PNGAPI png_set_sPLT(png_structp png_ptr, png_infop info_ptr, png_sPLT_tp entries, int nentries) /* * entries - array of png_sPLT_t structures * to be added to the list of palettes * in the info structure. * nentries - number of palette structures to be * added. */ { png_sPLT_tp np; int i; if (png_ptr == NULL || info_ptr == NULL) return; np = (png_sPLT_tp)png_malloc_warn(png_ptr, (info_ptr->splt_palettes_num + nentries) * (png_size_t)png_sizeof(png_sPLT_t)); if (np == NULL) { png_warning(png_ptr, "No memory for sPLT palettes"); return; } png_memcpy(np, info_ptr->splt_palettes, info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); png_free(png_ptr, info_ptr->splt_palettes); info_ptr->splt_palettes=NULL; for (i = 0; i < nentries; i++) { png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; png_sPLT_tp from = entries + i; png_uint_32 length; length = png_strlen(from->name) + 1; to->name = (png_charp)png_malloc_warn(png_ptr, (png_size_t)length); if (to->name == NULL) { png_warning(png_ptr, "Out of memory while processing sPLT chunk"); continue; } png_memcpy(to->name, from->name, length); to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, (png_size_t)(from->nentries * png_sizeof(png_sPLT_entry))); if (to->entries == NULL) { png_warning(png_ptr, "Out of memory while processing sPLT chunk"); png_free(png_ptr, to->name); to->name = NULL; continue; } png_memcpy(to->entries, from->entries, from->nentries * png_sizeof(png_sPLT_entry)); to->nentries = from->nentries; to->depth = from->depth; } info_ptr->splt_palettes = np; info_ptr->splt_palettes_num += nentries; info_ptr->valid |= PNG_INFO_sPLT; info_ptr->free_me |= PNG_FREE_SPLT; } #endif /* PNG_sPLT_SUPPORTED */ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED void PNGAPI png_set_unknown_chunks(png_structp png_ptr, png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) { png_unknown_chunkp np; int i; if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) return; np = (png_unknown_chunkp)png_malloc_warn(png_ptr, (png_size_t)((info_ptr->unknown_chunks_num + num_unknowns) * png_sizeof(png_unknown_chunk))); if (np == NULL) { png_warning(png_ptr, "Out of memory while processing unknown chunk"); return; } png_memcpy(np, info_ptr->unknown_chunks, info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); png_free(png_ptr, info_ptr->unknown_chunks); info_ptr->unknown_chunks = NULL; for (i = 0; i < num_unknowns; i++) { png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; png_unknown_chunkp from = unknowns + i; png_memcpy((png_charp)to->name, (png_charp)from->name, png_sizeof(from->name)); to->name[png_sizeof(to->name)-1] = '\0'; to->size = from->size; /* Note our location in the read or write sequence */ to->location = (png_byte)(png_ptr->mode & 0xff); if (from->size == 0) to->data=NULL; else { to->data = (png_bytep)png_malloc_warn(png_ptr, (png_size_t)from->size); if (to->data == NULL) { png_warning(png_ptr, "Out of memory while processing unknown chunk"); to->size = 0; } else png_memcpy(to->data, from->data, from->size); } } info_ptr->unknown_chunks = np; info_ptr->unknown_chunks_num += num_unknowns; info_ptr->free_me |= PNG_FREE_UNKN; } void PNGAPI png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, int chunk, int location) { if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < (int)info_ptr->unknown_chunks_num) info_ptr->unknown_chunks[chunk].location = (png_byte)location; } #endif #ifdef PNG_MNG_FEATURES_SUPPORTED png_uint_32 PNGAPI png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) { png_debug(1, "in png_permit_mng_features"); if (png_ptr == NULL) return (png_uint_32)0; png_ptr->mng_features_permitted = (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); return (png_uint_32)png_ptr->mng_features_permitted; } #endif #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED void PNGAPI png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep chunk_list, int num_chunks) { png_bytep new_list, p; int i, old_num_chunks; if (png_ptr == NULL) return; if (num_chunks == 0) { if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; else png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; if (keep == PNG_HANDLE_CHUNK_ALWAYS) png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; else png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; return; } if (chunk_list == NULL) return; old_num_chunks = png_ptr->num_chunk_list; new_list=(png_bytep)png_malloc(png_ptr, (png_size_t) (5*(num_chunks + old_num_chunks))); if (png_ptr->chunk_list != NULL) { png_memcpy(new_list, png_ptr->chunk_list, (png_size_t)(5*old_num_chunks)); png_free(png_ptr, png_ptr->chunk_list); png_ptr->chunk_list=NULL; } png_memcpy(new_list + 5*old_num_chunks, chunk_list, (png_size_t)(5*num_chunks)); for (p = new_list + 5*old_num_chunks + 4, i = 0; inum_chunk_list = old_num_chunks + num_chunks; png_ptr->chunk_list = new_list; png_ptr->free_me |= PNG_FREE_LIST; } #endif #ifdef PNG_READ_USER_CHUNKS_SUPPORTED void PNGAPI png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn) { png_debug(1, "in png_set_read_user_chunk_fn"); if (png_ptr == NULL) return; png_ptr->read_user_chunk_fn = read_user_chunk_fn; png_ptr->user_chunk_ptr = user_chunk_ptr; } #endif #ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) { png_debug1(1, "in %s storage function", "rows"); if (png_ptr == NULL || info_ptr == NULL) return; if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); info_ptr->row_pointers = row_pointers; if (row_pointers) info_ptr->valid |= PNG_INFO_IDAT; } #endif void PNGAPI png_set_compression_buffer_size(png_structp png_ptr, png_size_t size) { if (png_ptr == NULL) return; png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf_size = size; png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; } void PNGAPI png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) { if (png_ptr && info_ptr) info_ptr->valid &= ~mask; } #ifdef PNG_SET_USER_LIMITS_SUPPORTED /* This function was added to libpng 1.2.6 */ void PNGAPI png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max) { /* Images with dimensions larger than these limits will be * rejected by png_set_IHDR(). To accept any PNG datastream * regardless of dimensions, set both limits to 0x7ffffffL. */ if (png_ptr == NULL) return; png_ptr->user_width_max = user_width_max; png_ptr->user_height_max = user_height_max; } /* This function was added to libpng 1.4.0 */ void PNGAPI png_set_chunk_cache_max (png_structp png_ptr, png_uint_32 user_chunk_cache_max) { if (png_ptr) png_ptr->user_chunk_cache_max = user_chunk_cache_max; } /* This function was added to libpng 1.4.1 */ void PNGAPI png_set_chunk_malloc_max (png_structp png_ptr, png_alloc_size_t user_chunk_malloc_max) { if (png_ptr) png_ptr->user_chunk_malloc_max = (png_size_t)user_chunk_malloc_max; } #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ #ifdef PNG_BENIGN_ERRORS_SUPPORTED void PNGAPI png_set_benign_errors(png_structp png_ptr, int allowed) { png_debug(1, "in png_set_benign_errors"); if (allowed) png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; else png_ptr->flags &= ~PNG_FLAG_BENIGN_ERRORS_WARN; } #endif /* PNG_BENIGN_ERRORS_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngtest.c000066400000000000000000001425531271037650300232230ustar00rootroot00000000000000 /* pngtest.c - a simple test program to test libpng * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This program reads in a PNG image, writes it out again, and then * compares the two files. If the files are identical, this shows that * the basic chunk handling, filtering, and (de)compression code is working * properly. It does not currently test all of the transforms, although * it probably should. * * The program will report "FAIL" in certain legitimate cases: * 1) when the compression level or filter selection method is changed. * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks * exist in the input file. * 4) others not listed here... * In these cases, it is best to check with another tool such as "pngcheck" * to see what the differences between the two files are. * * If a filename is given on the command-line, then this file is used * for the input, rather than the default "pngtest.png". This allows * testing a wide variety of files easily. You can also test a number * of files at once by typing "pngtest -m file1.png file2.png ..." */ #include "png.h" #include "pngpriv.h" # include # include # define FCLOSE(file) fclose(file) #ifndef PNG_STDIO_SUPPORTED typedef FILE * png_FILE_p; #endif /* Makes pngtest verbose so we can find problems (needs to be before png.h) */ #ifndef PNG_DEBUG # define PNG_DEBUG 0 #endif #if !PNG_DEBUG # define SINGLE_ROWBUF_ALLOC /* Makes buffer overruns easier to nail */ #endif /* Turn on CPU timing #define PNGTEST_TIMING */ #ifndef PNG_FLOATING_POINT_SUPPORTED #undef PNGTEST_TIMING #endif #ifdef PNGTEST_TIMING static float t_start, t_stop, t_decode, t_encode, t_misc; #include #endif #ifdef PNG_TIME_RFC1123_SUPPORTED #define PNG_tIME_STRING_LENGTH 29 static int tIME_chunk_present = 0; static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present"; #endif static int verbose = 0; int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); #ifdef __TURBOC__ #include #endif /* Defined so I can write to a file on gui/windowing platforms */ /* #define STDERR stderr */ #define STDERR stdout /* For DOS */ /* In case a system header (e.g., on AIX) defined jmpbuf */ #ifdef jmpbuf # undef jmpbuf #endif /* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ #ifndef png_jmpbuf # define png_jmpbuf(png_ptr) png_ptr->jmpbuf #endif /* Example of using row callbacks to make a simple progress meter */ static int status_pass = 1; static int status_dots_requested = 0; static int status_dots = 1; void read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); void read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) { if (png_ptr == NULL || row_number > PNG_UINT_31_MAX) return; if (status_pass != pass) { fprintf(stdout, "\n Pass %d: ", pass); status_pass = pass; status_dots = 31; } status_dots--; if (status_dots == 0) { fprintf(stdout, "\n "); status_dots=30; } fprintf(stdout, "r"); } void write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); void write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) { if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; fprintf(stdout, "w"); } #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED /* Example of using user transform callback (we don't transform anything, * but merely examine the row filters. We set this to 256 rather than * 5 in case illegal filter values are present.) */ static png_uint_32 filters_used[256]; void count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data); void count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) { if (png_ptr != NULL && row_info != NULL) ++filters_used[*(data - 1)]; } #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED /* Example of using user transform callback (we don't transform anything, * but merely count the zero samples) */ static png_uint_32 zero_samples; void count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data); void count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) { png_bytep dp = data; if (png_ptr == NULL)return; /* Contents of row_info: * png_uint_32 width width of row * png_uint_32 rowbytes number of bytes in row * png_byte color_type color type of pixels * png_byte bit_depth bit depth of samples * png_byte channels number of channels (1-4) * png_byte pixel_depth bits per pixel (depth*channels) */ /* Counts the number of zero samples (or zero pixels if color_type is 3 */ if (row_info->color_type == 0 || row_info->color_type == 3) { int pos = 0; png_uint_32 n, nstop; for (n = 0, nstop=row_info->width; nbit_depth == 1) { if (((*dp << pos++ ) & 0x80) == 0) zero_samples++; if (pos == 8) { pos = 0; dp++; } } if (row_info->bit_depth == 2) { if (((*dp << (pos+=2)) & 0xc0) == 0) zero_samples++; if (pos == 8) { pos = 0; dp++; } } if (row_info->bit_depth == 4) { if (((*dp << (pos+=4)) & 0xf0) == 0) zero_samples++; if (pos == 8) { pos = 0; dp++; } } if (row_info->bit_depth == 8) if (*dp++ == 0) zero_samples++; if (row_info->bit_depth == 16) { if ((*dp | *(dp+1)) == 0) zero_samples++; dp+=2; } } } else /* Other color types */ { png_uint_32 n, nstop; int channel; int color_channels = row_info->channels; if (row_info->color_type > 3)color_channels--; for (n = 0, nstop=row_info->width; nbit_depth == 8) if (*dp++ == 0) zero_samples++; if (row_info->bit_depth == 16) { if ((*dp | *(dp+1)) == 0) zero_samples++; dp+=2; } } if (row_info->color_type > 3) { dp++; if (row_info->bit_depth == 16) dp++; } } } } #endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ static int wrote_question = 0; #ifndef PNG_STDIO_SUPPORTED /* START of code to validate stdio-free compilation */ /* These copies of the default read/write functions come from pngrio.c and * pngwio.c. They allow "don't include stdio" testing of the library. * This is the function that does the actual reading of data. If you are * not reading from a standard C stream, you should create a replacement * read_data function and use it at run time with png_set_read_fn(), rather * than changing the library. */ #ifndef USE_FAR_KEYWORD static void pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check = 0; png_voidp io_ptr; /* fread() returns 0 on error, so it is OK to store this in a png_size_t * instead of an int, which is what fread() actually returns. */ io_ptr = png_get_io_ptr(png_ptr); if (io_ptr != NULL) { check = fread(data, 1, length, (png_FILE_p)io_ptr); } if (check != length) { png_error(png_ptr, "Read Error!"); } } #else /* This is the model-independent version. Since the standard I/O library can't handle far buffers in the medium and small models, we have to copy the data. */ #define NEAR_BUF_SIZE 1024 #define MIN(a,b) (a <= b ? a : b) static void pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; png_byte *n_data; png_FILE_p io_ptr; /* Check if data really is near. If so, use usual code. */ n_data = (png_byte *)CVT_PTR_NOCHECK(data); io_ptr = (png_FILE_p)CVT_PTR(png_get_io_ptr(png_ptr)); if ((png_bytep)n_data == data) { check = fread(n_data, 1, length, io_ptr); } else { png_byte buf[NEAR_BUF_SIZE]; png_size_t read, remaining, err; check = 0; remaining = length; do { read = MIN(NEAR_BUF_SIZE, remaining); err = fread(buf, 1, 1, io_ptr); png_memcpy(data, buf, read); /* Copy far buffer to near buffer */ if (err != read) break; else check += err; data += read; remaining -= read; } while (remaining != 0); } if (check != length) png_error(png_ptr, "read Error"); } #endif /* USE_FAR_KEYWORD */ #ifdef PNG_WRITE_FLUSH_SUPPORTED static void pngtest_flush(png_structp png_ptr) { /* Do nothing; fflush() is said to be just a waste of energy. */ png_ptr = png_ptr; /* Stifle compiler warning */ } #endif /* This is the function that does the actual writing of data. If you are * not writing to a standard C stream, you should create a replacement * write_data function and use it at run time with png_set_write_fn(), rather * than changing the library. */ #ifndef USE_FAR_KEYWORD static void pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; png_FILE_p io_ptr; io_ptr = (png_FILE_p)CVT_PTR(png_get_io_ptr(png_ptr)); check = fwrite(data, 1, length, io_ptr); if (check != length) { png_error(png_ptr, "Write Error"); } } #else /* This is the model-independent version. Since the standard I/O library can't handle far buffers in the medium and small models, we have to copy the data. */ #define NEAR_BUF_SIZE 1024 #define MIN(a,b) (a <= b ? a : b) static void pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ png_FILE_p io_ptr; /* Check if data really is near. If so, use usual code. */ near_data = (png_byte *)CVT_PTR_NOCHECK(data); io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); if ((png_bytep)near_data == data) { check = fwrite(near_data, 1, length, io_ptr); } else { png_byte buf[NEAR_BUF_SIZE]; png_size_t written, remaining, err; check = 0; remaining = length; do { written = MIN(NEAR_BUF_SIZE, remaining); png_memcpy(buf, data, written); /* Copy far buffer to near buffer */ err = fwrite(buf, 1, written, io_ptr); if (err != written) break; else check += err; data += written; remaining -= written; } while (remaining != 0); } if (check != length) { png_error(png_ptr, "Write Error"); } } #endif /* USE_FAR_KEYWORD */ /* This function is called when there is a warning, but the library thinks * it can continue anyway. Replacement functions don't have to do anything * here if you don't want to. In the default configuration, png_ptr is * not used, but it is passed in case it may be useful. */ static void pngtest_warning(png_structp png_ptr, png_const_charp message) { PNG_CONST char *name = "UNKNOWN (ERROR!)"; char *test; test = png_get_error_ptr(png_ptr); if (test == NULL) fprintf(STDERR, "%s: libpng warning: %s\n", name, message); else fprintf(STDERR, "%s: libpng warning: %s\n", test, message); } /* This is the default error handling function. Note that replacements for * this function MUST NOT RETURN, or the program will likely crash. This * function is used by default, or if the program supplies NULL for the * error function pointer in png_set_error_fn(). */ static void pngtest_error(png_structp png_ptr, png_const_charp message) { pngtest_warning(png_ptr, message); /* We can return because png_error calls the default handler, which is * actually OK in this case. */ } #endif /* !PNG_STDIO_SUPPORTED */ /* END of code to validate stdio-free compilation */ /* START of code to validate memory allocation and deallocation */ #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG /* Allocate memory. For reasonable files, size should never exceed * 64K. However, zlib may allocate more then 64K if you don't tell * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. * * This piece of code can be compiled to validate max 64K allocations * by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ typedef struct memory_information { png_alloc_size_t size; png_voidp pointer; struct memory_information FAR *next; } memory_information; typedef memory_information FAR *memory_infop; static memory_infop pinformation = NULL; static int current_allocation = 0; static int maximum_allocation = 0; static int total_allocation = 0; static int num_allocations = 0; png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_alloc_size_t size)); void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); png_voidp png_debug_malloc(png_structp png_ptr, png_alloc_size_t size) { /* png_malloc has already tested for NULL; png_create_struct calls * png_debug_malloc directly, with png_ptr == NULL which is OK */ if (size == 0) return (NULL); /* This calls the library allocator twice, once to get the requested buffer and once to get a new free list entry. */ { /* Disable malloc_fn and free_fn */ memory_infop pinfo; png_set_mem_fn(png_ptr, NULL, NULL, NULL); pinfo = (memory_infop)png_malloc(png_ptr, png_sizeof(*pinfo)); pinfo->size = size; current_allocation += size; total_allocation += size; num_allocations ++; if (current_allocation > maximum_allocation) maximum_allocation = current_allocation; pinfo->pointer = png_malloc(png_ptr, size); /* Restore malloc_fn and free_fn */ png_set_mem_fn(png_ptr, NULL, png_debug_malloc, png_debug_free); if (size != 0 && pinfo->pointer == NULL) { current_allocation -= size; total_allocation -= size; png_error(png_ptr, "out of memory in pngtest->png_debug_malloc"); } pinfo->next = pinformation; pinformation = pinfo; /* Make sure the caller isn't assuming zeroed memory. */ png_memset(pinfo->pointer, 0xdd, pinfo->size); if (verbose) printf("png_malloc %lu bytes at %x\n", (unsigned long)size, pinfo->pointer); return (png_voidp)(pinfo->pointer); } } /* Free a pointer. It is removed from the list at the same time. */ void png_debug_free(png_structp png_ptr, png_voidp ptr) { if (png_ptr == NULL) fprintf(STDERR, "NULL pointer to png_debug_free.\n"); if (ptr == 0) { #if 0 /* This happens all the time. */ fprintf(STDERR, "WARNING: freeing NULL pointer\n"); #endif return; } /* Unlink the element from the list. */ { memory_infop FAR *ppinfo = &pinformation; for (;;) { memory_infop pinfo = *ppinfo; if (pinfo->pointer == ptr) { *ppinfo = pinfo->next; current_allocation -= pinfo->size; if (current_allocation < 0) fprintf(STDERR, "Duplicate free of memory\n"); /* We must free the list element too, but first kill the memory that is to be freed. */ png_memset(ptr, 0x55, pinfo->size); png_free_default(png_ptr, pinfo); pinfo = NULL; break; } if (pinfo->next == NULL) { fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); break; } ppinfo = &pinfo->next; } } /* Finally free the data. */ if (verbose) printf("Freeing %x\n", ptr); png_free_default(png_ptr, ptr); ptr = NULL; } #endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ /* END of code to test memory allocation/deallocation */ /* Demonstration of user chunk support of the sTER and vpAg chunks */ #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED /* (sTER is a public chunk not yet known by libpng. vpAg is a private chunk used in ImageMagick to store "virtual page" size). */ static png_uint_32 user_chunk_data[4]; /* 0: sTER mode + 1 * 1: vpAg width * 2: vpAg height * 3: vpAg units */ static int read_user_chunk_callback(png_struct *png_ptr, png_unknown_chunkp chunk) { png_uint_32 *my_user_chunk_data; /* Return one of the following: * return (-n); chunk had an error * return (0); did not recognize * return (n); success * * The unknown chunk structure contains the chunk data: * png_byte name[5]; * png_byte *data; * png_size_t size; * * Note that libpng has already taken care of the CRC handling. */ if (chunk->name[0] == 115 && chunk->name[1] == 84 && /* s T */ chunk->name[2] == 69 && chunk->name[3] == 82) /* E R */ { /* Found sTER chunk */ if (chunk->size != 1) return (-1); /* Error return */ if (chunk->data[0] != 0 && chunk->data[0] != 1) return (-1); /* Invalid mode */ my_user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr); my_user_chunk_data[0]=chunk->data[0]+1; return (1); } if (chunk->name[0] != 118 || chunk->name[1] != 112 || /* v p */ chunk->name[2] != 65 || chunk->name[3] != 103) /* A g */ return (0); /* Did not recognize */ /* Found ImageMagick vpAg chunk */ if (chunk->size != 9) return (-1); /* Error return */ my_user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr); my_user_chunk_data[1]=png_get_uint_31(png_ptr, chunk->data); my_user_chunk_data[2]=png_get_uint_31(png_ptr, chunk->data + 4); my_user_chunk_data[3]=(png_uint_32)chunk->data[8]; return (1); } #endif /* END of code to demonstrate user chunk support */ /* Test one file */ int test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) { static png_FILE_p fpin; static png_FILE_p fpout; /* "static" prevents setjmp corruption */ png_structp read_ptr; png_infop read_info_ptr, end_info_ptr; #ifdef PNG_WRITE_SUPPORTED png_structp write_ptr; png_infop write_info_ptr; png_infop write_end_info_ptr; #else png_structp write_ptr = NULL; png_infop write_info_ptr = NULL; png_infop write_end_info_ptr = NULL; #endif png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; int num_pass, pass; int bit_depth, color_type; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; #endif #endif char inbuf[256], outbuf[256]; row_buf = NULL; if ((fpin = fopen(inname, "rb")) == NULL) { fprintf(STDERR, "Could not find input file %s\n", inname); return (1); } if ((fpout = fopen(outname, "wb")) == NULL) { fprintf(STDERR, "Could not open output file %s\n", outname); FCLOSE(fpin); return (1); } png_debug(0, "Allocating read and write structures"); #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); #else read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif #ifndef PNG_STDIO_SUPPORTED png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, pngtest_warning); #endif #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED user_chunk_data[0] = 0; user_chunk_data[1] = 0; user_chunk_data[2] = 0; user_chunk_data[3] = 0; png_set_read_user_chunk_fn(read_ptr, user_chunk_data, read_user_chunk_callback); #endif #ifdef PNG_WRITE_SUPPORTED #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, png_debug_malloc, png_debug_free); #else write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); #endif #ifndef PNG_STDIO_SUPPORTED png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, pngtest_warning); #endif #endif png_debug(0, "Allocating read_info, write_info and end_info structures"); read_info_ptr = png_create_info_struct(read_ptr); end_info_ptr = png_create_info_struct(read_ptr); #ifdef PNG_WRITE_SUPPORTED write_info_ptr = png_create_info_struct(write_ptr); write_end_info_ptr = png_create_info_struct(write_ptr); #endif #ifdef PNG_SETJMP_SUPPORTED png_debug(0, "Setting jmpbuf for read struct"); #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(read_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); png_free(read_ptr, row_buf); row_buf = NULL; png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); #ifdef PNG_WRITE_SUPPORTED png_destroy_info_struct(write_ptr, &write_end_info_ptr); png_destroy_write_struct(&write_ptr, &write_info_ptr); #endif FCLOSE(fpin); FCLOSE(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(read_ptr), jmpbuf, png_sizeof(jmp_buf)); #endif #ifdef PNG_WRITE_SUPPORTED png_debug(0, "Setting jmpbuf for write struct"); #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(write_ptr))) #endif { fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); png_destroy_info_struct(write_ptr, &write_end_info_ptr); #ifdef PNG_WRITE_SUPPORTED png_destroy_write_struct(&write_ptr, &write_info_ptr); #endif FCLOSE(fpin); FCLOSE(fpout); return (1); } #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(write_ptr), jmpbuf, png_sizeof(jmp_buf)); #endif #endif #endif png_debug(0, "Initializing input and output streams"); #ifdef PNG_STDIO_SUPPORTED png_init_io(read_ptr, fpin); # ifdef PNG_WRITE_SUPPORTED png_init_io(write_ptr, fpout); # endif #else png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); # ifdef PNG_WRITE_SUPPORTED png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, # ifdef PNG_WRITE_FLUSH_SUPPORTED pngtest_flush); # else NULL); # endif # endif #endif if (status_dots_requested == 1) { #ifdef PNG_WRITE_SUPPORTED png_set_write_status_fn(write_ptr, write_row_callback); #endif png_set_read_status_fn(read_ptr, read_row_callback); } else { #ifdef PNG_WRITE_SUPPORTED png_set_write_status_fn(write_ptr, NULL); #endif png_set_read_status_fn(read_ptr, NULL); } #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED { int i; for (i = 0; i<256; i++) filters_used[i] = 0; png_set_read_user_transform_fn(read_ptr, count_filters); } #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED zero_samples = 0; png_set_write_user_transform_fn(write_ptr, count_zero_samples); #endif #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED # ifndef PNG_HANDLE_CHUNK_ALWAYS # define PNG_HANDLE_CHUNK_ALWAYS 3 # endif png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED # ifndef PNG_HANDLE_CHUNK_IF_SAFE # define PNG_HANDLE_CHUNK_IF_SAFE 2 # endif png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, NULL, 0); #endif png_debug(0, "Reading info struct"); png_read_info(read_ptr, read_info_ptr); png_debug(0, "Transferring info struct"); { int interlace_type, compression_type, filter_type; if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type)) { png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, #ifdef PNG_WRITE_INTERLACING_SUPPORTED color_type, interlace_type, compression_type, filter_type); #else color_type, PNG_INTERLACE_NONE, compression_type, filter_type); #endif } } #ifdef PNG_FIXED_POINT_SUPPORTED #ifdef PNG_cHRM_SUPPORTED { png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) { png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); } } #endif #ifdef PNG_gAMA_SUPPORTED { png_fixed_point gamma; if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); } #endif #else /* Use floating point versions */ #ifdef PNG_FLOATING_POINT_SUPPORTED #ifdef PNG_cHRM_SUPPORTED { double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) { png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); } } #endif #ifdef PNG_gAMA_SUPPORTED { double gamma; if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) png_set_gAMA(write_ptr, write_info_ptr, gamma); } #endif #endif /* Floating point */ #endif /* Fixed point */ #ifdef PNG_iCCP_SUPPORTED { png_charp name; png_charp profile; png_uint_32 proflen; int compression_type; if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, &profile, &proflen)) { png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, profile, proflen); } } #endif #ifdef PNG_sRGB_SUPPORTED { int intent; if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) png_set_sRGB(write_ptr, write_info_ptr, intent); } #endif { png_colorp palette; int num_palette; if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); } #ifdef PNG_bKGD_SUPPORTED { png_color_16p background; if (png_get_bKGD(read_ptr, read_info_ptr, &background)) { png_set_bKGD(write_ptr, write_info_ptr, background); } } #endif #ifdef PNG_hIST_SUPPORTED { png_uint_16p hist; if (png_get_hIST(read_ptr, read_info_ptr, &hist)) png_set_hIST(write_ptr, write_info_ptr, hist); } #endif #ifdef PNG_oFFs_SUPPORTED { png_int_32 offset_x, offset_y; int unit_type; if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, &unit_type)) { png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); } } #endif #ifdef PNG_pCAL_SUPPORTED { png_charp purpose, units; png_charpp params; png_int_32 X0, X1; int type, nparams; if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, &nparams, &units, ¶ms)) { png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, nparams, units, params); } } #endif #ifdef PNG_pHYs_SUPPORTED { png_uint_32 res_x, res_y; int unit_type; if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); } #endif #ifdef PNG_sBIT_SUPPORTED { png_color_8p sig_bit; if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) png_set_sBIT(write_ptr, write_info_ptr, sig_bit); } #endif #ifdef PNG_sCAL_SUPPORTED #ifdef PNG_FLOATING_POINT_SUPPORTED { int unit; double scal_width, scal_height; if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, &scal_height)) { png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); } } #else #ifdef PNG_FIXED_POINT_SUPPORTED { int unit; png_charp scal_width, scal_height; if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, &scal_height)) { png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); } } #endif #endif #endif #ifdef PNG_TEXT_SUPPORTED { png_textp text_ptr; int num_text; if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) { png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text); png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); } } #endif #ifdef PNG_tIME_SUPPORTED { png_timep mod_time; if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) { png_set_tIME(write_ptr, write_info_ptr, mod_time); #ifdef PNG_TIME_RFC1123_SUPPORTED /* We have to use png_memcpy instead of "=" because the string * pointed to by png_convert_to_rfc1123() gets free'ed before * we use it. */ png_memcpy(tIME_string, png_convert_to_rfc1123(read_ptr, mod_time), png_sizeof(tIME_string)); tIME_string[png_sizeof(tIME_string) - 1] = '\0'; tIME_chunk_present++; #endif /* PNG_TIME_RFC1123_SUPPORTED */ } } #endif #ifdef PNG_tRNS_SUPPORTED { png_bytep trans_alpha; int num_trans; png_color_16p trans_color; if (png_get_tRNS(read_ptr, read_info_ptr, &trans_alpha, &num_trans, &trans_color)) { int sample_max = (1 << bit_depth); /* libpng doesn't reject a tRNS chunk with out-of-range samples */ if (!((color_type == PNG_COLOR_TYPE_GRAY && (int)trans_color->gray > sample_max) || (color_type == PNG_COLOR_TYPE_RGB && ((int)trans_color->red > sample_max || (int)trans_color->green > sample_max || (int)trans_color->blue > sample_max)))) png_set_tRNS(write_ptr, write_info_ptr, trans_alpha, num_trans, trans_color); } } #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED { png_unknown_chunkp unknowns; int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr, &unknowns); if (num_unknowns) { png_size_t i; png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, num_unknowns); /* Copy the locations from the read_info_ptr. The automatically * generated locations in write_info_ptr are wrong because we * haven't written anything yet. */ for (i = 0; i < (png_size_t)num_unknowns; i++) png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, unknowns[i].location); } } #endif #ifdef PNG_WRITE_SUPPORTED png_debug(0, "Writing info struct"); /* If we wanted, we could write info in two steps: * png_write_info_before_PLTE(write_ptr, write_info_ptr); */ png_write_info(write_ptr, write_info_ptr); #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED if (user_chunk_data[0] != 0) { png_byte png_sTER[5] = {115, 84, 69, 82, '\0'}; unsigned char ster_chunk_data[1]; if (verbose) fprintf(STDERR, "\n stereo mode = %lu\n", (unsigned long)(user_chunk_data[0] - 1)); ster_chunk_data[0]=(unsigned char)(user_chunk_data[0] - 1); png_write_chunk(write_ptr, png_sTER, ster_chunk_data, 1); } if (user_chunk_data[1] != 0 || user_chunk_data[2] != 0) { png_byte png_vpAg[5] = {118, 112, 65, 103, '\0'}; unsigned char vpag_chunk_data[9]; if (verbose) fprintf(STDERR, " vpAg = %lu x %lu, units = %lu\n", (unsigned long)user_chunk_data[1], (unsigned long)user_chunk_data[2], (unsigned long)user_chunk_data[3]); png_save_uint_32(vpag_chunk_data, user_chunk_data[1]); png_save_uint_32(vpag_chunk_data + 4, user_chunk_data[2]); vpag_chunk_data[8] = (unsigned char)(user_chunk_data[3] & 0xff); png_write_chunk(write_ptr, png_vpAg, vpag_chunk_data, 9); } #endif #endif #ifdef SINGLE_ROWBUF_ALLOC png_debug(0, "Allocating row buffer..."); row_buf = (png_bytep)png_malloc(read_ptr, png_get_rowbytes(read_ptr, read_info_ptr)); png_debug1(0, "0x%08lx", (unsigned long)row_buf); #endif /* SINGLE_ROWBUF_ALLOC */ png_debug(0, "Writing row data"); #if defined(PNG_READ_INTERLACING_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) num_pass = png_set_interlace_handling(read_ptr); # ifdef PNG_WRITE_SUPPORTED png_set_interlace_handling(write_ptr); # endif #else num_pass = 1; #endif #ifdef PNGTEST_TIMING t_stop = (float)clock(); t_misc += (t_stop - t_start); t_start = t_stop; #endif for (pass = 0; pass < num_pass; pass++) { png_debug1(0, "Writing row data for pass %d", pass); for (y = 0; y < height; y++) { #ifndef SINGLE_ROWBUF_ALLOC png_debug2(0, "Allocating row buffer (pass %d, y = %ld)...", pass, y); row_buf = (png_bytep)png_malloc(read_ptr, png_get_rowbytes(read_ptr, read_info_ptr)); png_debug2(0, "0x%08lx (%ld bytes)", (unsigned long)row_buf, png_get_rowbytes(read_ptr, read_info_ptr)); #endif /* !SINGLE_ROWBUF_ALLOC */ png_read_rows(read_ptr, (png_bytepp)&row_buf, NULL, 1); #ifdef PNG_WRITE_SUPPORTED #ifdef PNGTEST_TIMING t_stop = (float)clock(); t_decode += (t_stop - t_start); t_start = t_stop; #endif png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); #ifdef PNGTEST_TIMING t_stop = (float)clock(); t_encode += (t_stop - t_start); t_start = t_stop; #endif #endif /* PNG_WRITE_SUPPORTED */ #ifndef SINGLE_ROWBUF_ALLOC png_debug2(0, "Freeing row buffer (pass %d, y = %ld)", pass, y); png_free(read_ptr, row_buf); row_buf = NULL; #endif /* !SINGLE_ROWBUF_ALLOC */ } } #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); #endif png_debug(0, "Reading and writing end_info data"); png_read_end(read_ptr, end_info_ptr); #ifdef PNG_TEXT_SUPPORTED { png_textp text_ptr; int num_text; if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) { png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text); png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text); } } #endif #ifdef PNG_tIME_SUPPORTED { png_timep mod_time; if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) { png_set_tIME(write_ptr, write_end_info_ptr, mod_time); #ifdef PNG_TIME_RFC1123_SUPPORTED /* We have to use png_memcpy instead of "=" because the string pointed to by png_convert_to_rfc1123() gets free'ed before we use it */ png_memcpy(tIME_string, png_convert_to_rfc1123(read_ptr, mod_time), png_sizeof(tIME_string)); tIME_string[png_sizeof(tIME_string) - 1] = '\0'; tIME_chunk_present++; #endif /* PNG_TIME_RFC1123_SUPPORTED */ } } #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED { png_unknown_chunkp unknowns; int num_unknowns; num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr, &unknowns); if (num_unknowns) { png_size_t i; png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, num_unknowns); /* Copy the locations from the read_info_ptr. The automatically * generated locations in write_end_info_ptr are wrong because we * haven't written the end_info yet. */ for (i = 0; i < (png_size_t)num_unknowns; i++) png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, unknowns[i].location); } } #endif #ifdef PNG_WRITE_SUPPORTED png_write_end(write_ptr, write_end_info_ptr); #endif #ifdef PNG_EASY_ACCESS_SUPPORTED if (verbose) { png_uint_32 iwidth, iheight; iwidth = png_get_image_width(write_ptr, write_info_ptr); iheight = png_get_image_height(write_ptr, write_info_ptr); fprintf(STDERR, "\n Image width = %lu, height = %lu\n", (unsigned long)iwidth, (unsigned long)iheight); } #endif png_debug(0, "Destroying data structs"); #ifdef SINGLE_ROWBUF_ALLOC png_debug(1, "destroying row_buf for read_ptr"); png_free(read_ptr, row_buf); row_buf = NULL; #endif /* SINGLE_ROWBUF_ALLOC */ png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr"); png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); #ifdef PNG_WRITE_SUPPORTED png_debug(1, "destroying write_end_info_ptr"); png_destroy_info_struct(write_ptr, &write_end_info_ptr); png_debug(1, "destroying write_ptr, write_info_ptr"); png_destroy_write_struct(&write_ptr, &write_info_ptr); #endif png_debug(0, "Destruction complete."); FCLOSE(fpin); FCLOSE(fpout); png_debug(0, "Opening files for comparison"); if ((fpin = fopen(inname, "rb")) == NULL) { fprintf(STDERR, "Could not find file %s\n", inname); return (1); } if ((fpout = fopen(outname, "rb")) == NULL) { fprintf(STDERR, "Could not find file %s\n", outname); FCLOSE(fpin); return (1); } for (;;) { png_size_t num_in, num_out; num_in = fread(inbuf, 1, 1, fpin); num_out = fread(outbuf, 1, 1, fpout); if (num_in != num_out) { fprintf(STDERR, "\nFiles %s and %s are of a different size\n", inname, outname); if (wrote_question == 0) { fprintf(STDERR, " Was %s written with the same maximum IDAT chunk size (%d bytes),", inname, PNG_ZBUF_SIZE); fprintf(STDERR, "\n filtering heuristic (libpng default), compression"); fprintf(STDERR, " level (zlib default),\n and zlib version (%s)?\n\n", ZLIB_VERSION); wrote_question = 1; } FCLOSE(fpin); FCLOSE(fpout); return (0); } if (!num_in) break; if (png_memcmp(inbuf, outbuf, num_in)) { fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname); if (wrote_question == 0) { fprintf(STDERR, " Was %s written with the same maximum IDAT chunk size (%d bytes),", inname, PNG_ZBUF_SIZE); fprintf(STDERR, "\n filtering heuristic (libpng default), compression"); fprintf(STDERR, " level (zlib default),\n and zlib version (%s)?\n\n", ZLIB_VERSION); wrote_question = 1; } FCLOSE(fpin); FCLOSE(fpout); return (0); } } FCLOSE(fpin); FCLOSE(fpout); return (0); } /* Input and output filenames */ #ifdef RISCOS static PNG_CONST char *inname = "pngtest/png"; static PNG_CONST char *outname = "pngout/png"; #else static PNG_CONST char *inname = "pngtest.png"; static PNG_CONST char *outname = "pngout.png"; #endif int main(int argc, char *argv[]) { int multiple = 0; int ierror = 0; fprintf(STDERR, "\n Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); fprintf(STDERR, "%s", png_get_copyright(NULL)); /* Show the version of libpng used in building the library */ fprintf(STDERR, " library (%lu):%s", (unsigned long)png_access_version_number(), png_get_header_version(NULL)); /* Show the version of libpng used in building the application */ fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, PNG_HEADER_VERSION_STRING); fprintf(STDERR, " sizeof(png_struct)=%ld, sizeof(png_info)=%ld\n", (long)png_sizeof(png_struct), (long)png_sizeof(png_info)); /* Do some consistency checking on the memory allocation settings, I'm * not sure this matters, but it is nice to know, the first of these * tests should be impossible because of the way the macros are set * in pngconf.h */ #if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); #endif /* I think the following can happen. */ #if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); #endif if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) { fprintf(STDERR, "Warning: versions are different between png.h and png.c\n"); fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); ++ierror; } if (argc > 1) { if (strcmp(argv[1], "-m") == 0) { multiple = 1; status_dots_requested = 0; } else if (strcmp(argv[1], "-mv") == 0 || strcmp(argv[1], "-vm") == 0 ) { multiple = 1; verbose = 1; status_dots_requested = 1; } else if (strcmp(argv[1], "-v") == 0) { verbose = 1; status_dots_requested = 1; inname = argv[2]; } else { inname = argv[1]; status_dots_requested = 0; } } if (!multiple && argc == 3 + verbose) outname = argv[2 + verbose]; if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2)) { fprintf(STDERR, "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", argv[0], argv[0]); fprintf(STDERR, " reads/writes one PNG file (without -m) or multiple files (-m)\n"); fprintf(STDERR, " with -m %s is used as a temporary file\n", outname); exit(1); } if (multiple) { int i; #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG int allocation_now = current_allocation; #endif for (i=2; isize, (unsigned int) pinfo->pointer); pinfo = pinfo->next; } } #endif } #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG fprintf(STDERR, " Current memory allocation: %10d bytes\n", current_allocation); fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", maximum_allocation); fprintf(STDERR, " Total memory allocation: %10d bytes\n", total_allocation); fprintf(STDERR, " Number of allocations: %10d\n", num_allocations); #endif } else { int i; for (i = 0; i<3; ++i) { int kerror; #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG int allocation_now = current_allocation; #endif if (i == 1) status_dots_requested = 1; else if (verbose == 0)status_dots_requested = 0; if (i == 0 || verbose == 1 || ierror != 0) fprintf(STDERR, "\n Testing %s:", inname); kerror = test_one_file(inname, outname); if (kerror == 0) { if (verbose == 1 || i == 2) { #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED int k; #endif #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED fprintf(STDERR, "\n PASS (%lu zero samples)\n", (unsigned long)zero_samples); #else fprintf(STDERR, " PASS\n"); #endif #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED for (k = 0; k<256; k++) if (filters_used[k]) fprintf(STDERR, " Filter %d was used %lu times\n", k, (unsigned long)filters_used[k]); #endif #ifdef PNG_TIME_RFC1123_SUPPORTED if (tIME_chunk_present != 0) fprintf(STDERR, " tIME = %s\n", tIME_string); #endif /* PNG_TIME_RFC1123_SUPPORTED */ } } else { if (verbose == 0 && i != 2) fprintf(STDERR, "\n Testing %s:", inname); fprintf(STDERR, " FAIL\n"); ierror += kerror; } #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG if (allocation_now != current_allocation) fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", current_allocation - allocation_now); if (current_allocation != 0) { memory_infop pinfo = pinformation; fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", current_allocation); while (pinfo != NULL) { fprintf(STDERR, " %lu bytes at %x\n", (unsigned long)pinfo->size, (unsigned int)pinfo->pointer); pinfo = pinfo->next; } } #endif } #if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG fprintf(STDERR, " Current memory allocation: %10d bytes\n", current_allocation); fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", maximum_allocation); fprintf(STDERR, " Total memory allocation: %10d bytes\n", total_allocation); fprintf(STDERR, " Number of allocations: %10d\n", num_allocations); #endif } #ifdef PNGTEST_TIMING t_stop = (float)clock(); t_misc += (t_stop - t_start); t_start = t_stop; fprintf(STDERR, " CPU time used = %.3f seconds", (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); fprintf(STDERR, " (decoding %.3f,\n", t_decode/(float)CLOCKS_PER_SEC); fprintf(STDERR, " encoding %.3f ,", t_encode/(float)CLOCKS_PER_SEC); fprintf(STDERR, " other %.3f seconds)\n\n", t_misc/(float)CLOCKS_PER_SEC); #endif if (ierror == 0) fprintf(STDERR, " libpng passes test\n"); else fprintf(STDERR, " libpng FAILS test\n"); return (int)(ierror != 0); } /* Generate a compiler error if there is an old png.h in the search path. */ typedef version_1_4_4 your_png_h_is_not_version_1_4_4; Indigo-indigo-1.2.3/third_party/libpng-src/src/pngtrans.c000066400000000000000000000515161271037650300233710ustar00rootroot00000000000000 /* pngtrans.c - transforms the data in a row (used by both readers and writers) * * Last changed in libpng 1.4.2 [April 29, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) #include "pngpriv.h" #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) /* Turn on BGR-to-RGB mapping */ void PNGAPI png_set_bgr(png_structp png_ptr) { png_debug(1, "in png_set_bgr"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_BGR; } #endif #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Turn on 16 bit byte swapping */ void PNGAPI png_set_swap(png_structp png_ptr) { png_debug(1, "in png_set_swap"); if (png_ptr == NULL) return; if (png_ptr->bit_depth == 16) png_ptr->transformations |= PNG_SWAP_BYTES; } #endif #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) /* Turn on pixel packing */ void PNGAPI png_set_packing(png_structp png_ptr) { png_debug(1, "in png_set_packing"); if (png_ptr == NULL) return; if (png_ptr->bit_depth < 8) { png_ptr->transformations |= PNG_PACK; png_ptr->usr_bit_depth = 8; } } #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) /* Turn on packed pixel swapping */ void PNGAPI png_set_packswap(png_structp png_ptr) { png_debug(1, "in png_set_packswap"); if (png_ptr == NULL) return; if (png_ptr->bit_depth < 8) png_ptr->transformations |= PNG_PACKSWAP; } #endif #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) void PNGAPI png_set_shift(png_structp png_ptr, png_color_8p true_bits) { png_debug(1, "in png_set_shift"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_SHIFT; png_ptr->shift = *true_bits; } #endif #if defined(PNG_READ_INTERLACING_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) int PNGAPI png_set_interlace_handling(png_structp png_ptr) { png_debug(1, "in png_set_interlace handling"); if (png_ptr && png_ptr->interlaced) { png_ptr->transformations |= PNG_INTERLACE; return (7); } return (1); } #endif #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) /* Add a filler byte on read, or remove a filler or alpha byte on write. * The filler type has changed in v0.95 to allow future 2-byte fillers * for 48-bit input data, as well as to avoid problems with some compilers * that don't like bytes as parameters. */ void PNGAPI png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) { png_debug(1, "in png_set_filler"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_FILLER; png_ptr->filler = (png_uint_16)filler; if (filler_loc == PNG_FILLER_AFTER) png_ptr->flags |= PNG_FLAG_FILLER_AFTER; else png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; /* This should probably go in the "do_read_filler" routine. * I attempted to do that in libpng-1.0.1a but that caused problems * so I restored it in libpng-1.0.2a */ if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { png_ptr->usr_channels = 4; } /* Also I added this in libpng-1.0.2a (what happens when we expand * a less-than-8-bit grayscale to GA? */ if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) { png_ptr->usr_channels = 2; } } /* Added to libpng-1.2.7 */ void PNGAPI png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) { png_debug(1, "in png_set_add_alpha"); if (png_ptr == NULL) return; png_set_filler(png_ptr, filler, filler_loc); png_ptr->transformations |= PNG_ADD_ALPHA; } #endif #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) void PNGAPI png_set_swap_alpha(png_structp png_ptr) { png_debug(1, "in png_set_swap_alpha"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_SWAP_ALPHA; } #endif #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) void PNGAPI png_set_invert_alpha(png_structp png_ptr) { png_debug(1, "in png_set_invert_alpha"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_INVERT_ALPHA; } #endif #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) void PNGAPI png_set_invert_mono(png_structp png_ptr) { png_debug(1, "in png_set_invert_mono"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_INVERT_MONO; } /* Invert monochrome grayscale data */ void /* PRIVATE */ png_do_invert(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_invert"); /* This test removed from libpng version 1.0.13 and 1.2.0: * if (row_info->bit_depth == 1 && */ if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { png_bytep rp = row; png_uint_32 i; png_uint_32 istop = row_info->rowbytes; for (i = 0; i < istop; i++) { *rp = (png_byte)(~(*rp)); rp++; } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && row_info->bit_depth == 8) { png_bytep rp = row; png_uint_32 i; png_uint_32 istop = row_info->rowbytes; for (i = 0; i < istop; i+=2) { *rp = (png_byte)(~(*rp)); rp+=2; } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && row_info->bit_depth == 16) { png_bytep rp = row; png_uint_32 i; png_uint_32 istop = row_info->rowbytes; for (i = 0; i < istop; i+=4) { *rp = (png_byte)(~(*rp)); *(rp+1) = (png_byte)(~(*(rp+1))); rp+=4; } } } #endif #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Swaps byte order on 16 bit depth images */ void /* PRIVATE */ png_do_swap(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_swap"); if ( row_info->bit_depth == 16) { png_bytep rp = row; png_uint_32 i; png_uint_32 istop= row_info->width * row_info->channels; for (i = 0; i < istop; i++, rp += 2) { png_byte t = *rp; *rp = *(rp + 1); *(rp + 1) = t; } } } #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) static PNG_CONST png_byte onebppswaptable[256] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF }; static PNG_CONST png_byte twobppswaptable[256] = { 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF }; static PNG_CONST png_byte fourbppswaptable[256] = { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF }; /* Swaps pixel packing order within bytes */ void /* PRIVATE */ png_do_packswap(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_packswap"); if ( row_info->bit_depth < 8) { png_bytep rp, end, table; end = row + row_info->rowbytes; if (row_info->bit_depth == 1) table = (png_bytep)onebppswaptable; else if (row_info->bit_depth == 2) table = (png_bytep)twobppswaptable; else if (row_info->bit_depth == 4) table = (png_bytep)fourbppswaptable; else return; for (rp = row; rp < end; rp++) *rp = table[*rp]; } } #endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ #if defined(PNG_WRITE_FILLER_SUPPORTED) || \ defined(PNG_READ_STRIP_ALPHA_SUPPORTED) /* Remove filler or alpha byte(s) */ void /* PRIVATE */ png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) { png_debug(1, "in png_do_strip_filler"); { png_bytep sp=row; png_bytep dp=row; png_uint_32 row_width=row_info->width; png_uint_32 i; if ((row_info->color_type == PNG_COLOR_TYPE_RGB || (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && (flags & PNG_FLAG_STRIP_ALPHA))) && row_info->channels == 4) { if (row_info->bit_depth == 8) { /* This converts from RGBX or RGBA to RGB */ if (flags & PNG_FLAG_FILLER_AFTER) { dp+=3; sp+=4; for (i = 1; i < row_width; i++) { *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; sp++; } } /* This converts from XRGB or ARGB to RGB */ else { for (i = 0; i < row_width; i++) { sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; } } row_info->pixel_depth = 24; row_info->rowbytes = row_width * 3; } else /* if (row_info->bit_depth == 16) */ { if (flags & PNG_FLAG_FILLER_AFTER) { /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ sp += 8; dp += 6; for (i = 1; i < row_width; i++) { /* This could be (although png_memcpy is probably slower): png_memcpy(dp, sp, 6); sp += 8; dp += 6; */ *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; sp += 2; } } else { /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ for (i = 0; i < row_width; i++) { /* This could be (although png_memcpy is probably slower): png_memcpy(dp, sp, 6); sp += 8; dp += 6; */ sp+=2; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; } } row_info->pixel_depth = 48; row_info->rowbytes = row_width * 6; } row_info->channels = 3; } else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && (flags & PNG_FLAG_STRIP_ALPHA))) && row_info->channels == 2) { if (row_info->bit_depth == 8) { /* This converts from GX or GA to G */ if (flags & PNG_FLAG_FILLER_AFTER) { for (i = 0; i < row_width; i++) { *dp++ = *sp++; sp++; } } /* This converts from XG or AG to G */ else { for (i = 0; i < row_width; i++) { sp++; *dp++ = *sp++; } } row_info->pixel_depth = 8; row_info->rowbytes = row_width; } else /* if (row_info->bit_depth == 16) */ { if (flags & PNG_FLAG_FILLER_AFTER) { /* This converts from GGXX or GGAA to GG */ sp += 4; dp += 2; for (i = 1; i < row_width; i++) { *dp++ = *sp++; *dp++ = *sp++; sp += 2; } } else { /* This converts from XXGG or AAGG to GG */ for (i = 0; i < row_width; i++) { sp += 2; *dp++ = *sp++; *dp++ = *sp++; } } row_info->pixel_depth = 16; row_info->rowbytes = row_width * 2; } row_info->channels = 1; } if (flags & PNG_FLAG_STRIP_ALPHA) row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; } } #endif #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) /* Swaps red and blue bytes within a pixel */ void /* PRIVATE */ png_do_bgr(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_bgr"); if ( (row_info->color_type & PNG_COLOR_MASK_COLOR)) { png_uint_32 row_width = row_info->width; if (row_info->bit_depth == 8) { if (row_info->color_type == PNG_COLOR_TYPE_RGB) { png_bytep rp; png_uint_32 i; for (i = 0, rp = row; i < row_width; i++, rp += 3) { png_byte save = *rp; *rp = *(rp + 2); *(rp + 2) = save; } } else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_bytep rp; png_uint_32 i; for (i = 0, rp = row; i < row_width; i++, rp += 4) { png_byte save = *rp; *rp = *(rp + 2); *(rp + 2) = save; } } } else if (row_info->bit_depth == 16) { if (row_info->color_type == PNG_COLOR_TYPE_RGB) { png_bytep rp; png_uint_32 i; for (i = 0, rp = row; i < row_width; i++, rp += 6) { png_byte save = *rp; *rp = *(rp + 4); *(rp + 4) = save; save = *(rp + 1); *(rp + 1) = *(rp + 5); *(rp + 5) = save; } } else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_bytep rp; png_uint_32 i; for (i = 0, rp = row; i < row_width; i++, rp += 8) { png_byte save = *rp; *rp = *(rp + 4); *(rp + 4) = save; save = *(rp + 1); *(rp + 1) = *(rp + 5); *(rp + 5) = save; } } } } } #endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) void PNGAPI png_set_user_transform_info(png_structp png_ptr, png_voidp user_transform_ptr, int user_transform_depth, int user_transform_channels) { png_debug(1, "in png_set_user_transform_info"); if (png_ptr == NULL) return; #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED png_ptr->user_transform_ptr = user_transform_ptr; png_ptr->user_transform_depth = (png_byte)user_transform_depth; png_ptr->user_transform_channels = (png_byte)user_transform_channels; #else if (user_transform_ptr || user_transform_depth || user_transform_channels) png_warning(png_ptr, "This version of libpng does not support user transform info"); #endif } /* This function returns a pointer to the user_transform_ptr associated with * the user transform functions. The application should free any memory * associated with this pointer before png_write_destroy and png_read_destroy * are called. */ png_voidp PNGAPI png_get_user_transform_ptr(png_structp png_ptr) { if (png_ptr == NULL) return (NULL); #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED return ((png_voidp)png_ptr->user_transform_ptr); #else return (NULL); #endif } #endif /* PNG_READ_USER_TRANSFORM_SUPPORTED || PNG_WRITE_USER_TRANSFORM_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngwio.c000066400000000000000000000173211271037650300230340ustar00rootroot00000000000000 /* pngwio.c - functions for data output * * Last changed in libpng 1.4.0 [January 3, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h * * This file provides a location for all output. Users who need * special handling are expected to write functions that have the same * arguments as these and perform similar functions, but that possibly * use different output methods. Note that you shouldn't change these * functions, but rather write replacement functions and then change * them at run time with png_set_write_fn(...). */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_WRITE_SUPPORTED #include "pngpriv.h" /* Write the data to whatever output you are using. The default routine * writes to a file pointer. Note that this routine sometimes gets called * with very small lengths, so you should implement some kind of simple * buffering if you are using unbuffered writes. This should never be asked * to write more than 64K on a 16 bit machine. */ void /* PRIVATE */ png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { if (png_ptr->write_data_fn != NULL ) (*(png_ptr->write_data_fn))(png_ptr, data, length); else png_error(png_ptr, "Call to NULL write function"); } #ifdef PNG_STDIO_SUPPORTED /* This is the function that does the actual writing of data. If you are * not writing to a standard C stream, you should create a replacement * write_data function and use it at run time with png_set_write_fn(), rather * than changing the library. */ #ifndef USE_FAR_KEYWORD void PNGAPI png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_uint_32 check; if (png_ptr == NULL) return; check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Write Error"); } #else /* This is the model-independent version. Since the standard I/O library * can't handle far buffers in the medium and small models, we have to copy * the data. */ #define NEAR_BUF_SIZE 1024 #define MIN(a,b) (a <= b ? a : b) void PNGAPI png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_uint_32 check; png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ png_FILE_p io_ptr; if (png_ptr == NULL) return; /* Check if data really is near. If so, use usual code. */ near_data = (png_byte *)CVT_PTR_NOCHECK(data); io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); if ((png_bytep)near_data == data) { check = fwrite(near_data, 1, length, io_ptr); } else { png_byte buf[NEAR_BUF_SIZE]; png_size_t written, remaining, err; check = 0; remaining = length; do { written = MIN(NEAR_BUF_SIZE, remaining); png_memcpy(buf, data, written); /* Copy far buffer to near buffer */ err = fwrite(buf, 1, written, io_ptr); if (err != written) break; else check += err; data += written; remaining -= written; } while (remaining != 0); } if (check != length) png_error(png_ptr, "Write Error"); } #endif #endif /* This function is called to output any data pending writing (normally * to disk). After png_flush is called, there should be no data pending * writing in any buffers. */ #ifdef PNG_WRITE_FLUSH_SUPPORTED void /* PRIVATE */ png_flush(png_structp png_ptr) { if (png_ptr->output_flush_fn != NULL) (*(png_ptr->output_flush_fn))(png_ptr); } #ifdef PNG_STDIO_SUPPORTED void PNGAPI png_default_flush(png_structp png_ptr) { png_FILE_p io_ptr; if (png_ptr == NULL) return; io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); fflush(io_ptr); } #endif #endif /* This function allows the application to supply new output functions for * libpng if standard C streams aren't being used. * * This function takes as its arguments: * png_ptr - pointer to a png output data structure * io_ptr - pointer to user supplied structure containing info about * the output functions. May be NULL. * write_data_fn - pointer to a new output function that takes as its * arguments a pointer to a png_struct, a pointer to * data to be written, and a 32-bit unsigned int that is * the number of bytes to be written. The new write * function should call png_error(png_ptr, "Error msg") * to exit and output any fatal error messages. May be * NULL, in which case libpng's default function will * be used. * flush_data_fn - pointer to a new flush function that takes as its * arguments a pointer to a png_struct. After a call to * the flush function, there should be no data in any buffers * or pending transmission. If the output method doesn't do * any buffering of output, a function prototype must still be * supplied although it doesn't have to do anything. If * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile * time, output_flush_fn will be ignored, although it must be * supplied for compatibility. May be NULL, in which case * libpng's default function will be used, if * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not * a good idea if io_ptr does not point to a standard * *FILE structure. */ void PNGAPI png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) { if (png_ptr == NULL) return; png_ptr->io_ptr = io_ptr; #ifdef PNG_STDIO_SUPPORTED if (write_data_fn != NULL) png_ptr->write_data_fn = write_data_fn; else png_ptr->write_data_fn = png_default_write_data; #else png_ptr->write_data_fn = write_data_fn; #endif #ifdef PNG_WRITE_FLUSH_SUPPORTED #ifdef PNG_STDIO_SUPPORTED if (output_flush_fn != NULL) png_ptr->output_flush_fn = output_flush_fn; else png_ptr->output_flush_fn = png_default_flush; #else png_ptr->output_flush_fn = output_flush_fn; #endif #endif /* PNG_WRITE_FLUSH_SUPPORTED */ /* It is an error to read while writing a png file */ if (png_ptr->read_data_fn != NULL) { png_ptr->read_data_fn = NULL; png_warning(png_ptr, "Attempted to set both read_data_fn and write_data_fn in"); png_warning(png_ptr, "the same structure. Resetting read_data_fn to NULL"); } } #ifdef USE_FAR_KEYWORD #ifdef _MSC_VER void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) { void *near_ptr; void FAR *far_ptr; FP_OFF(near_ptr) = FP_OFF(ptr); far_ptr = (void FAR *)near_ptr; if (check != 0) if (FP_SEG(ptr) != FP_SEG(far_ptr)) png_error(png_ptr, "segment lost in conversion"); return(near_ptr); } # else void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) { void *near_ptr; void FAR *far_ptr; near_ptr = (void FAR *)ptr; far_ptr = (void FAR *)near_ptr; if (check != 0) if (far_ptr != ptr) png_error(png_ptr, "segment lost in conversion"); return(near_ptr); } # endif # endif #endif /* PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngwrite.c000066400000000000000000001351651271037650300233770ustar00rootroot00000000000000 /* pngwrite.c - general routines to write a PNG file * * Last changed in libpng 1.4.0 [January 3, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ /* Get internal access to png.h */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_WRITE_SUPPORTED #include "pngpriv.h" /* Writes all the PNG information. This is the suggested way to use the * library. If you have a new chunk to add, make a function to write it, * and put it in the correct location here. If you want the chunk written * after the image data, put it in png_write_end(). I strongly encourage * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing * the chunk, as that will keep the code from breaking if you want to just * write a plain PNG file. If you have long comments, I suggest writing * them in png_write_end(), and compressing them. */ void PNGAPI png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_write_info_before_PLTE"); if (png_ptr == NULL || info_ptr == NULL) return; if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) { /* Write PNG signature */ png_write_sig(png_ptr); #ifdef PNG_MNG_FEATURES_SUPPORTED if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ (png_ptr->mng_features_permitted)) { png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); png_ptr->mng_features_permitted = 0; } #endif /* Write IHDR information. */ png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, info_ptr->filter_type, #ifdef PNG_WRITE_INTERLACING_SUPPORTED info_ptr->interlace_type); #else 0); #endif /* The rest of these check to see if the valid field has the appropriate * flag set, and if it does, writes the chunk. */ #ifdef PNG_WRITE_gAMA_SUPPORTED if (info_ptr->valid & PNG_INFO_gAMA) { # ifdef PNG_FLOATING_POINT_SUPPORTED png_write_gAMA(png_ptr, info_ptr->gamma); #else #ifdef PNG_FIXED_POINT_SUPPORTED png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); # endif #endif } #endif #ifdef PNG_WRITE_sRGB_SUPPORTED if (info_ptr->valid & PNG_INFO_sRGB) png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); #endif #ifdef PNG_WRITE_iCCP_SUPPORTED if (info_ptr->valid & PNG_INFO_iCCP) png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); #endif #ifdef PNG_WRITE_sBIT_SUPPORTED if (info_ptr->valid & PNG_INFO_sBIT) png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); #endif #ifdef PNG_WRITE_cHRM_SUPPORTED if (info_ptr->valid & PNG_INFO_cHRM) { #ifdef PNG_FLOATING_POINT_SUPPORTED png_write_cHRM(png_ptr, info_ptr->x_white, info_ptr->y_white, info_ptr->x_red, info_ptr->y_red, info_ptr->x_green, info_ptr->y_green, info_ptr->x_blue, info_ptr->y_blue); #else # ifdef PNG_FIXED_POINT_SUPPORTED png_write_cHRM_fixed(png_ptr, info_ptr->int_x_white, info_ptr->int_y_white, info_ptr->int_x_red, info_ptr->int_y_red, info_ptr->int_x_green, info_ptr->int_y_green, info_ptr->int_x_blue, info_ptr->int_y_blue); # endif #endif } #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED if (info_ptr->unknown_chunks_num) { png_unknown_chunk *up; png_debug(5, "writing extra chunks"); for (up = info_ptr->unknown_chunks; up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; up++) { int keep = png_handle_as_unknown(png_ptr, up->name); if (keep != PNG_HANDLE_CHUNK_NEVER && up->location && !(up->location & PNG_HAVE_PLTE) && !(up->location & PNG_HAVE_IDAT) && ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) { if (up->size == 0) png_warning(png_ptr, "Writing zero-length unknown chunk"); png_write_chunk(png_ptr, up->name, up->data, up->size); } } } #endif png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; } } void PNGAPI png_write_info(png_structp png_ptr, png_infop info_ptr) { #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) int i; #endif png_debug(1, "in png_write_info"); if (png_ptr == NULL || info_ptr == NULL) return; png_write_info_before_PLTE(png_ptr, info_ptr); if (info_ptr->valid & PNG_INFO_PLTE) png_write_PLTE(png_ptr, info_ptr->palette, (png_uint_32)info_ptr->num_palette); else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Valid palette required for paletted images"); #ifdef PNG_WRITE_tRNS_SUPPORTED if (info_ptr->valid & PNG_INFO_tRNS) { #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED /* Invert the alpha channel (in tRNS) */ if ((png_ptr->transformations & PNG_INVERT_ALPHA) && info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { int j; for (j = 0; j<(int)info_ptr->num_trans; j++) info_ptr->trans_alpha[j] = (png_byte)(255 - info_ptr->trans_alpha[j]); } #endif png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), info_ptr->num_trans, info_ptr->color_type); } #endif #ifdef PNG_WRITE_bKGD_SUPPORTED if (info_ptr->valid & PNG_INFO_bKGD) png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); #endif #ifdef PNG_WRITE_hIST_SUPPORTED if (info_ptr->valid & PNG_INFO_hIST) png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); #endif #ifdef PNG_WRITE_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, info_ptr->offset_unit_type); #endif #ifdef PNG_WRITE_pCAL_SUPPORTED if (info_ptr->valid & PNG_INFO_pCAL) png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, info_ptr->pcal_units, info_ptr->pcal_params); #endif #ifdef PNG_sCAL_SUPPORTED if (info_ptr->valid & PNG_INFO_sCAL) #ifdef PNG_WRITE_sCAL_SUPPORTED #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); #else /* !FLOATING_POINT */ #ifdef PNG_FIXED_POINT_SUPPORTED png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, info_ptr->scal_s_width, info_ptr->scal_s_height); #endif /* FIXED_POINT */ #endif /* FLOATING_POINT */ #else /* !WRITE_sCAL */ png_warning(png_ptr, "png_write_sCAL not supported; sCAL chunk not written"); #endif /* WRITE_sCAL */ #endif /* sCAL */ #ifdef PNG_WRITE_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); #endif /* pHYs */ #ifdef PNG_WRITE_tIME_SUPPORTED if (info_ptr->valid & PNG_INFO_tIME) { png_write_tIME(png_ptr, &(info_ptr->mod_time)); png_ptr->mode |= PNG_WROTE_tIME; } #endif /* tIME */ #ifdef PNG_WRITE_sPLT_SUPPORTED if (info_ptr->valid & PNG_INFO_sPLT) for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); #endif /* sPLT */ #ifdef PNG_WRITE_TEXT_SUPPORTED /* Check to see if we need to write text chunks */ for (i = 0; i < info_ptr->num_text; i++) { png_debug2(2, "Writing header text chunk %d, type %d", i, info_ptr->text[i].compression); /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0) { #ifdef PNG_WRITE_iTXt_SUPPORTED /* Write international chunk */ png_write_iTXt(png_ptr, info_ptr->text[i].compression, info_ptr->text[i].key, info_ptr->text[i].lang, info_ptr->text[i].lang_key, info_ptr->text[i].text); #else png_warning(png_ptr, "Unable to write international text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; } /* If we want a compressed text chunk */ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) { #ifdef PNG_WRITE_zTXt_SUPPORTED /* Write compressed chunk */ png_write_zTXt(png_ptr, info_ptr->text[i].key, info_ptr->text[i].text, 0, info_ptr->text[i].compression); #else png_warning(png_ptr, "Unable to write compressed text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; } else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) { #ifdef PNG_WRITE_tEXt_SUPPORTED /* Write uncompressed chunk */ png_write_tEXt(png_ptr, info_ptr->text[i].key, info_ptr->text[i].text, 0); /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; #else /* Can't get here */ png_warning(png_ptr, "Unable to write uncompressed text"); #endif } } #endif /* tEXt */ #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED if (info_ptr->unknown_chunks_num) { png_unknown_chunk *up; png_debug(5, "writing extra chunks"); for (up = info_ptr->unknown_chunks; up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; up++) { int keep = png_handle_as_unknown(png_ptr, up->name); if (keep != PNG_HANDLE_CHUNK_NEVER && up->location && (up->location & PNG_HAVE_PLTE) && !(up->location & PNG_HAVE_IDAT) && ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) { png_write_chunk(png_ptr, up->name, up->data, up->size); } } } #endif } /* Writes the end of the PNG file. If you don't want to write comments or * time information, you can pass NULL for info. If you already wrote these * in png_write_info(), do not write them again here. If you have long * comments, I suggest writing them here, and compressing them. */ void PNGAPI png_write_end(png_structp png_ptr, png_infop info_ptr) { png_debug(1, "in png_write_end"); if (png_ptr == NULL) return; if (!(png_ptr->mode & PNG_HAVE_IDAT)) png_error(png_ptr, "No IDATs written into file"); /* See if user wants us to write information chunks */ if (info_ptr != NULL) { #ifdef PNG_WRITE_TEXT_SUPPORTED int i; /* local index variable */ #endif #ifdef PNG_WRITE_tIME_SUPPORTED /* Check to see if user has supplied a time chunk */ if ((info_ptr->valid & PNG_INFO_tIME) && !(png_ptr->mode & PNG_WROTE_tIME)) png_write_tIME(png_ptr, &(info_ptr->mod_time)); #endif #ifdef PNG_WRITE_TEXT_SUPPORTED /* Loop through comment chunks */ for (i = 0; i < info_ptr->num_text; i++) { png_debug2(2, "Writing trailer text chunk %d, type %d", i, info_ptr->text[i].compression); /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0) { #ifdef PNG_WRITE_iTXt_SUPPORTED /* Write international chunk */ png_write_iTXt(png_ptr, info_ptr->text[i].compression, info_ptr->text[i].key, info_ptr->text[i].lang, info_ptr->text[i].lang_key, info_ptr->text[i].text); #else png_warning(png_ptr, "Unable to write international text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; } else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) { #ifdef PNG_WRITE_zTXt_SUPPORTED /* Write compressed chunk */ png_write_zTXt(png_ptr, info_ptr->text[i].key, info_ptr->text[i].text, 0, info_ptr->text[i].compression); #else png_warning(png_ptr, "Unable to write compressed text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; } else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) { #ifdef PNG_WRITE_tEXt_SUPPORTED /* Write uncompressed chunk */ png_write_tEXt(png_ptr, info_ptr->text[i].key, info_ptr->text[i].text, 0); #else png_warning(png_ptr, "Unable to write uncompressed text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; } } #endif #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED if (info_ptr->unknown_chunks_num) { png_unknown_chunk *up; png_debug(5, "writing extra chunks"); for (up = info_ptr->unknown_chunks; up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; up++) { int keep = png_handle_as_unknown(png_ptr, up->name); if (keep != PNG_HANDLE_CHUNK_NEVER && up->location && (up->location & PNG_AFTER_IDAT) && ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) { png_write_chunk(png_ptr, up->name, up->data, up->size); } } } #endif } png_ptr->mode |= PNG_AFTER_IDAT; /* Write end of PNG file */ png_write_IEND(png_ptr); /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, * and restored again in libpng-1.2.30, may cause some applications that * do not set png_ptr->output_flush_fn to crash. If your application * experiences a problem, please try building libpng with * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to * png-mng-implement at lists.sf.net . */ #ifdef PNG_WRITE_FLUSH_SUPPORTED # ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED png_flush(png_ptr); # endif #endif } #ifdef PNG_CONVERT_tIME_SUPPORTED /* "tm" structure is not supported on WindowsCE */ void PNGAPI png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) { png_debug(1, "in png_convert_from_struct_tm"); ptime->year = (png_uint_16)(1900 + ttime->tm_year); ptime->month = (png_byte)(ttime->tm_mon + 1); ptime->day = (png_byte)ttime->tm_mday; ptime->hour = (png_byte)ttime->tm_hour; ptime->minute = (png_byte)ttime->tm_min; ptime->second = (png_byte)ttime->tm_sec; } void PNGAPI png_convert_from_time_t(png_timep ptime, time_t ttime) { struct tm *tbuf; png_debug(1, "in png_convert_from_time_t"); tbuf = gmtime(&ttime); png_convert_from_struct_tm(ptime, tbuf); } #endif /* Initialize png_ptr structure, and allocate any memory needed */ png_structp PNGAPI png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn) { #ifdef PNG_USER_MEM_SUPPORTED return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, warn_fn, NULL, NULL, NULL)); } /* Alternate initialize png_ptr structure, and allocate any memory needed */ png_structp PNGAPI png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn) { #endif /* PNG_USER_MEM_SUPPORTED */ volatile int png_cleanup_needed = 0; #ifdef PNG_SETJMP_SUPPORTED volatile #endif png_structp png_ptr; #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; #endif #endif int i; png_debug(1, "in png_create_write_struct"); #ifdef PNG_USER_MEM_SUPPORTED png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); #else png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); #endif /* PNG_USER_MEM_SUPPORTED */ if (png_ptr == NULL) return (NULL); /* Added at libpng-1.2.6 */ #ifdef PNG_SET_USER_LIMITS_SUPPORTED png_ptr->user_width_max = PNG_USER_WIDTH_MAX; png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; #endif #ifdef PNG_SETJMP_SUPPORTED /* Applications that neglect to set up their own setjmp() and then encounter a png_error() will longjmp here. Since the jmpbuf is then meaningless we abort instead of returning. */ #ifdef USE_FAR_KEYWORD if (setjmp(jmpbuf)) #else if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */ #endif #ifdef USE_FAR_KEYWORD png_memcpy(png_jmpbuf(png_ptr), jmpbuf, png_sizeof(jmp_buf)); #endif PNG_ABORT(); #endif #ifdef PNG_USER_MEM_SUPPORTED png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); #endif /* PNG_USER_MEM_SUPPORTED */ png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); if (user_png_ver) { i = 0; do { if (user_png_ver[i] != png_libpng_ver[i]) png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; } while (png_libpng_ver[i++]); } if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) { /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so * we must recompile any applications that use any older library version. * For versions after libpng 1.0, we will be compatible, so we need * only check the first digit. */ if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || (user_png_ver[0] == '0' && user_png_ver[2] < '9')) { #ifdef PNG_STDIO_SUPPORTED char msg[80]; if (user_png_ver) { png_snprintf(msg, 80, "Application was compiled with png.h from libpng-%.20s", user_png_ver); png_warning(png_ptr, msg); } png_snprintf(msg, 80, "Application is running with png.c from libpng-%.20s", png_libpng_ver); png_warning(png_ptr, msg); #endif #ifdef PNG_ERROR_NUMBERS_SUPPORTED png_ptr->flags = 0; #endif png_warning(png_ptr, "Incompatible libpng version in application and library"); png_cleanup_needed = 1; } } /* Initialize zbuf - compression buffer */ png_ptr->zbuf_size = PNG_ZBUF_SIZE; if (!png_cleanup_needed) { png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, png_ptr->zbuf_size); if (png_ptr->zbuf == NULL) png_cleanup_needed = 1; } if (png_cleanup_needed) { /* Clean up PNG structure and deallocate any memory. */ png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf = NULL; #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)png_ptr); #endif return (NULL); } png_set_write_fn(png_ptr, NULL, NULL, NULL); #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, 1, NULL, NULL); #endif return (png_ptr); } /* Write a few rows of image data. If the image is interlaced, * either you will have to write the 7 sub images, or, if you * have called png_set_interlace_handling(), you will have to * "write" the image seven times. */ void PNGAPI png_write_rows(png_structp png_ptr, png_bytepp row, png_uint_32 num_rows) { png_uint_32 i; /* row counter */ png_bytepp rp; /* row pointer */ png_debug(1, "in png_write_rows"); if (png_ptr == NULL) return; /* Loop through the rows */ for (i = 0, rp = row; i < num_rows; i++, rp++) { png_write_row(png_ptr, *rp); } } /* Write the image. You only need to call this function once, even * if you are writing an interlaced image. */ void PNGAPI png_write_image(png_structp png_ptr, png_bytepp image) { png_uint_32 i; /* row index */ int pass, num_pass; /* pass variables */ png_bytepp rp; /* points to current row */ if (png_ptr == NULL) return; png_debug(1, "in png_write_image"); #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Initialize interlace handling. If image is not interlaced, * this will set pass to 1 */ num_pass = png_set_interlace_handling(png_ptr); #else num_pass = 1; #endif /* Loop through passes */ for (pass = 0; pass < num_pass; pass++) { /* Loop through image */ for (i = 0, rp = image; i < png_ptr->height; i++, rp++) { png_write_row(png_ptr, *rp); } } } /* Called by user to write a row of image data */ void PNGAPI png_write_row(png_structp png_ptr, png_bytep row) { if (png_ptr == NULL) return; png_debug2(1, "in png_write_row (row %ld, pass %d)", png_ptr->row_number, png_ptr->pass); /* Initialize transformations and other stuff if first time */ if (png_ptr->row_number == 0 && png_ptr->pass == 0) { /* Make sure we wrote the header info */ if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) png_error(png_ptr, "png_write_info was never called before png_write_row"); /* Check for transforms that have been set but were defined out */ #if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) if (png_ptr->transformations & PNG_INVERT_MONO) png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) if (png_ptr->transformations & PNG_FILLER) png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ defined(PNG_READ_PACKSWAP_SUPPORTED) if (png_ptr->transformations & PNG_PACKSWAP) png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) if (png_ptr->transformations & PNG_PACK) png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) if (png_ptr->transformations & PNG_SHIFT) png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) if (png_ptr->transformations & PNG_BGR) png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); #endif #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) if (png_ptr->transformations & PNG_SWAP_BYTES) png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); #endif png_write_start_row(png_ptr); } #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* If interlaced and not interested in row, return */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { switch (png_ptr->pass) { case 0: if (png_ptr->row_number & 0x07) { png_write_finish_row(png_ptr); return; } break; case 1: if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) { png_write_finish_row(png_ptr); return; } break; case 2: if ((png_ptr->row_number & 0x07) != 4) { png_write_finish_row(png_ptr); return; } break; case 3: if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) { png_write_finish_row(png_ptr); return; } break; case 4: if ((png_ptr->row_number & 0x03) != 2) { png_write_finish_row(png_ptr); return; } break; case 5: if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) { png_write_finish_row(png_ptr); return; } break; case 6: if (!(png_ptr->row_number & 0x01)) { png_write_finish_row(png_ptr); return; } break; } } #endif /* Set up row info for transformations */ png_ptr->row_info.color_type = png_ptr->color_type; png_ptr->row_info.width = png_ptr->usr_width; png_ptr->row_info.channels = png_ptr->usr_channels; png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * png_ptr->row_info.channels); png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->row_info.width); png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type); png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width); png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels); png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth); png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth); png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes); /* Copy user's row into buffer, leaving room for filter byte. */ png_memcpy(png_ptr->row_buf + 1, row, png_ptr->row_info.rowbytes); #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Handle interlacing */ if (png_ptr->interlaced && png_ptr->pass < 6 && (png_ptr->transformations & PNG_INTERLACE)) { png_do_write_interlace(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->pass); /* This should always get caught above, but still ... */ if (!(png_ptr->row_info.width)) { png_write_finish_row(png_ptr); return; } } #endif /* Handle other transformations */ if (png_ptr->transformations) png_do_write_transformations(png_ptr); #ifdef PNG_MNG_FEATURES_SUPPORTED /* Write filter_method 64 (intrapixel differencing) only if * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and * 2. Libpng did not write a PNG signature (this filter_method is only * used in PNG datastreams that are embedded in MNG datastreams) and * 3. The application called png_permit_mng_features with a mask that * included PNG_FLAG_MNG_FILTER_64 and * 4. The filter_method is 64 and * 5. The color_type is RGB or RGBA */ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) { /* Intrapixel differencing */ png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); } #endif /* Find a filter if necessary, filter the row and write it out. */ png_write_find_filter(png_ptr, &(png_ptr->row_info)); if (png_ptr->write_row_fn != NULL) (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); } #ifdef PNG_WRITE_FLUSH_SUPPORTED /* Set the automatic flush interval or 0 to turn flushing off */ void PNGAPI png_set_flush(png_structp png_ptr, int nrows) { png_debug(1, "in png_set_flush"); if (png_ptr == NULL) return; png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); } /* Flush the current output buffers now */ void PNGAPI png_write_flush(png_structp png_ptr) { int wrote_IDAT; png_debug(1, "in png_write_flush"); if (png_ptr == NULL) return; /* We have already written out all of the data */ if (png_ptr->row_number >= png_ptr->num_rows) return; do { int ret; /* Compress the data */ ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); wrote_IDAT = 0; /* Check for compression errors */ if (ret != Z_OK) { if (png_ptr->zstream.msg != NULL) png_error(png_ptr, png_ptr->zstream.msg); else png_error(png_ptr, "zlib error"); } if (!(png_ptr->zstream.avail_out)) { /* Write the IDAT and reset the zlib output buffer */ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; wrote_IDAT = 1; } } while(wrote_IDAT == 1); /* If there is any data left to be output, write it into a new IDAT */ if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) { /* Write the IDAT and reset the zlib output buffer */ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - png_ptr->zstream.avail_out); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; } png_ptr->flush_rows = 0; png_flush(png_ptr); } #endif /* PNG_WRITE_FLUSH_SUPPORTED */ /* Free all memory used by the write */ void PNGAPI png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; #ifdef PNG_USER_MEM_SUPPORTED png_free_ptr free_fn = NULL; png_voidp mem_ptr = NULL; #endif png_debug(1, "in png_destroy_write_struct"); if (png_ptr_ptr != NULL) { png_ptr = *png_ptr_ptr; #ifdef PNG_USER_MEM_SUPPORTED free_fn = png_ptr->free_fn; mem_ptr = png_ptr->mem_ptr; #endif } #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr != NULL) { free_fn = png_ptr->free_fn; mem_ptr = png_ptr->mem_ptr; } #endif if (info_ptr_ptr != NULL) info_ptr = *info_ptr_ptr; if (info_ptr != NULL) { if (png_ptr != NULL) { png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED if (png_ptr->num_chunk_list) { png_free(png_ptr, png_ptr->chunk_list); png_ptr->num_chunk_list = 0; } #endif } #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)info_ptr); #endif *info_ptr_ptr = NULL; } if (png_ptr != NULL) { png_write_destroy(png_ptr); #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); #else png_destroy_struct((png_voidp)png_ptr); #endif *png_ptr_ptr = NULL; } } /* Free any memory used in png_ptr struct (old method) */ void /* PRIVATE */ png_write_destroy(png_structp png_ptr) { #ifdef PNG_SETJMP_SUPPORTED jmp_buf tmp_jmp; /* Save jump buffer */ #endif png_error_ptr error_fn; png_error_ptr warning_fn; png_voidp error_ptr; #ifdef PNG_USER_MEM_SUPPORTED png_free_ptr free_fn; #endif png_debug(1, "in png_write_destroy"); /* Free any memory zlib uses */ deflateEnd(&png_ptr->zstream); /* Free our memory. png_free checks NULL for us. */ png_free(png_ptr, png_ptr->zbuf); png_free(png_ptr, png_ptr->row_buf); #ifdef PNG_WRITE_FILTER_SUPPORTED png_free(png_ptr, png_ptr->prev_row); png_free(png_ptr, png_ptr->sub_row); png_free(png_ptr, png_ptr->up_row); png_free(png_ptr, png_ptr->avg_row); png_free(png_ptr, png_ptr->paeth_row); #endif #ifdef PNG_TIME_RFC1123_SUPPORTED png_free(png_ptr, png_ptr->time_buffer); #endif #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED png_free(png_ptr, png_ptr->prev_filters); png_free(png_ptr, png_ptr->filter_weights); png_free(png_ptr, png_ptr->inv_filter_weights); png_free(png_ptr, png_ptr->filter_costs); png_free(png_ptr, png_ptr->inv_filter_costs); #endif #ifdef PNG_SETJMP_SUPPORTED /* Reset structure */ png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); #endif error_fn = png_ptr->error_fn; warning_fn = png_ptr->warning_fn; error_ptr = png_ptr->error_ptr; #ifdef PNG_USER_MEM_SUPPORTED free_fn = png_ptr->free_fn; #endif png_memset(png_ptr, 0, png_sizeof(png_struct)); png_ptr->error_fn = error_fn; png_ptr->warning_fn = warning_fn; png_ptr->error_ptr = error_ptr; #ifdef PNG_USER_MEM_SUPPORTED png_ptr->free_fn = free_fn; #endif #ifdef PNG_SETJMP_SUPPORTED png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); #endif } /* Allow the application to select one or more row filters to use. */ void PNGAPI png_set_filter(png_structp png_ptr, int method, int filters) { png_debug(1, "in png_set_filter"); if (png_ptr == NULL) return; #ifdef PNG_MNG_FEATURES_SUPPORTED if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && (method == PNG_INTRAPIXEL_DIFFERENCING)) method = PNG_FILTER_TYPE_BASE; #endif if (method == PNG_FILTER_TYPE_BASE) { switch (filters & (PNG_ALL_FILTERS | 0x07)) { #ifdef PNG_WRITE_FILTER_SUPPORTED case 5: case 6: case 7: png_warning(png_ptr, "Unknown row filter for method 0"); #endif /* PNG_WRITE_FILTER_SUPPORTED */ case PNG_FILTER_VALUE_NONE: png_ptr->do_filter = PNG_FILTER_NONE; break; #ifdef PNG_WRITE_FILTER_SUPPORTED case PNG_FILTER_VALUE_SUB: png_ptr->do_filter = PNG_FILTER_SUB; break; case PNG_FILTER_VALUE_UP: png_ptr->do_filter = PNG_FILTER_UP; break; case PNG_FILTER_VALUE_AVG: png_ptr->do_filter = PNG_FILTER_AVG; break; case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter = PNG_FILTER_PAETH; break; default: png_ptr->do_filter = (png_byte)filters; break; #else default: png_warning(png_ptr, "Unknown row filter for method 0"); #endif /* PNG_WRITE_FILTER_SUPPORTED */ } /* If we have allocated the row_buf, this means we have already started * with the image and we should have allocated all of the filter buffers * that have been selected. If prev_row isn't already allocated, then * it is too late to start using the filters that need it, since we * will be missing the data in the previous row. If an application * wants to start and stop using particular filters during compression, * it should start out with all of the filters, and then add and * remove them after the start of compression. */ if (png_ptr->row_buf != NULL) { #ifdef PNG_WRITE_FILTER_SUPPORTED if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) { png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, (png_ptr->rowbytes + 1)); png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; } if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) { if (png_ptr->prev_row == NULL) { png_warning(png_ptr, "Can't add Up filter after starting"); png_ptr->do_filter &= ~PNG_FILTER_UP; } else { png_ptr->up_row = (png_bytep)png_malloc(png_ptr, (png_ptr->rowbytes + 1)); png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; } } if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) { if (png_ptr->prev_row == NULL) { png_warning(png_ptr, "Can't add Average filter after starting"); png_ptr->do_filter &= ~PNG_FILTER_AVG; } else { png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, (png_ptr->rowbytes + 1)); png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; } } if ((png_ptr->do_filter & PNG_FILTER_PAETH) && png_ptr->paeth_row == NULL) { if (png_ptr->prev_row == NULL) { png_warning(png_ptr, "Can't add Paeth filter after starting"); png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); } else { png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, (png_ptr->rowbytes + 1)); png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; } } if (png_ptr->do_filter == PNG_NO_FILTERS) #endif /* PNG_WRITE_FILTER_SUPPORTED */ png_ptr->do_filter = PNG_FILTER_NONE; } } else png_error(png_ptr, "Unknown custom filter method"); } /* This allows us to influence the way in which libpng chooses the "best" * filter for the current scanline. While the "minimum-sum-of-absolute- * differences metric is relatively fast and effective, there is some * question as to whether it can be improved upon by trying to keep the * filtered data going to zlib more consistent, hopefully resulting in * better compression. */ #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ void PNGAPI png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, int num_weights, png_doublep filter_weights, png_doublep filter_costs) { int i; png_debug(1, "in png_set_filter_heuristics"); if (png_ptr == NULL) return; if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) { png_warning(png_ptr, "Unknown filter heuristic method"); return; } if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) { heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; } if (num_weights < 0 || filter_weights == NULL || heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) { num_weights = 0; } png_ptr->num_prev_filters = (png_byte)num_weights; png_ptr->heuristic_method = (png_byte)heuristic_method; if (num_weights > 0) { if (png_ptr->prev_filters == NULL) { png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, (png_uint_32)(png_sizeof(png_byte) * num_weights)); /* To make sure that the weighting starts out fairly */ for (i = 0; i < num_weights; i++) { png_ptr->prev_filters[i] = 255; } } if (png_ptr->filter_weights == NULL) { png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); for (i = 0; i < num_weights; i++) { png_ptr->inv_filter_weights[i] = png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; } } for (i = 0; i < num_weights; i++) { if (filter_weights[i] < 0.0) { png_ptr->inv_filter_weights[i] = png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; } else { png_ptr->inv_filter_weights[i] = (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); png_ptr->filter_weights[i] = (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); } } } /* If, in the future, there are other filter methods, this would * need to be based on png_ptr->filter. */ if (png_ptr->filter_costs == NULL) { png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) { png_ptr->inv_filter_costs[i] = png_ptr->filter_costs[i] = PNG_COST_FACTOR; } } /* Here is where we set the relative costs of the different filters. We * should take the desired compression level into account when setting * the costs, so that Paeth, for instance, has a high relative cost at low * compression levels, while it has a lower relative cost at higher * compression settings. The filter types are in order of increasing * relative cost, so it would be possible to do this with an algorithm. */ for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) { if (filter_costs == NULL || filter_costs[i] < 0.0) { png_ptr->inv_filter_costs[i] = png_ptr->filter_costs[i] = PNG_COST_FACTOR; } else if (filter_costs[i] >= 1.0) { png_ptr->inv_filter_costs[i] = (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); png_ptr->filter_costs[i] = (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); } } } #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ void PNGAPI png_set_compression_level(png_structp png_ptr, int level) { png_debug(1, "in png_set_compression_level"); if (png_ptr == NULL) return; png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; png_ptr->zlib_level = level; } void PNGAPI png_set_compression_mem_level(png_structp png_ptr, int mem_level) { png_debug(1, "in png_set_compression_mem_level"); if (png_ptr == NULL) return; png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; png_ptr->zlib_mem_level = mem_level; } void PNGAPI png_set_compression_strategy(png_structp png_ptr, int strategy) { png_debug(1, "in png_set_compression_strategy"); if (png_ptr == NULL) return; png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; png_ptr->zlib_strategy = strategy; } void PNGAPI png_set_compression_window_bits(png_structp png_ptr, int window_bits) { if (png_ptr == NULL) return; if (window_bits > 15) png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); else if (window_bits < 8) png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); #ifndef WBITS_8_OK /* Avoid libpng bug with 256-byte windows */ if (window_bits == 8) { png_warning(png_ptr, "Compression window is being reset to 512"); window_bits = 9; } #endif png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; png_ptr->zlib_window_bits = window_bits; } void PNGAPI png_set_compression_method(png_structp png_ptr, int method) { png_debug(1, "in png_set_compression_method"); if (png_ptr == NULL) return; if (method != 8) png_warning(png_ptr, "Only compression method 8 is supported by PNG"); png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; png_ptr->zlib_method = method; } void PNGAPI png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) { if (png_ptr == NULL) return; png_ptr->write_row_fn = write_row_fn; } #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED void PNGAPI png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr write_user_transform_fn) { png_debug(1, "in png_set_write_user_transform_fn"); if (png_ptr == NULL) return; png_ptr->transformations |= PNG_USER_TRANSFORM; png_ptr->write_user_transform_fn = write_user_transform_fn; } #endif #ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI png_write_png(png_structp png_ptr, png_infop info_ptr, int transforms, voidp params) { if (png_ptr == NULL || info_ptr == NULL) return; /* Write the file header information. */ png_write_info(png_ptr, info_ptr); /* ------ these transformations don't touch the info structure ------- */ #ifdef PNG_WRITE_INVERT_SUPPORTED /* Invert monochrome pixels */ if (transforms & PNG_TRANSFORM_INVERT_MONO) png_set_invert_mono(png_ptr); #endif #ifdef PNG_WRITE_SHIFT_SUPPORTED /* Shift the pixels up to a legal bit depth and fill in * as appropriate to correctly scale the image. */ if ((transforms & PNG_TRANSFORM_SHIFT) && (info_ptr->valid & PNG_INFO_sBIT)) png_set_shift(png_ptr, &info_ptr->sig_bit); #endif #ifdef PNG_WRITE_PACK_SUPPORTED /* Pack pixels into bytes */ if (transforms & PNG_TRANSFORM_PACKING) png_set_packing(png_ptr); #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED /* Swap location of alpha bytes from ARGB to RGBA */ if (transforms & PNG_TRANSFORM_SWAP_ALPHA) png_set_swap_alpha(png_ptr); #endif #ifdef PNG_WRITE_FILLER_SUPPORTED /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */ if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); #endif #ifdef PNG_WRITE_BGR_SUPPORTED /* Flip BGR pixels to RGB */ if (transforms & PNG_TRANSFORM_BGR) png_set_bgr(png_ptr); #endif #ifdef PNG_WRITE_SWAP_SUPPORTED /* Swap bytes of 16-bit files to most significant byte first */ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) png_set_swap(png_ptr); #endif #ifdef PNG_WRITE_PACKSWAP_SUPPORTED /* Swap bits of 1, 2, 4 bit packed pixel formats */ if (transforms & PNG_TRANSFORM_PACKSWAP) png_set_packswap(png_ptr); #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED /* Invert the alpha channel from opacity to transparency */ if (transforms & PNG_TRANSFORM_INVERT_ALPHA) png_set_invert_alpha(png_ptr); #endif /* ----------------------- end of transformations ------------------- */ /* Write the bits */ if (info_ptr->valid & PNG_INFO_IDAT) png_write_image(png_ptr, info_ptr->row_pointers); /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); transforms = transforms; /* Quiet compiler warnings */ params = params; } #endif #endif /* PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngwtran.c000066400000000000000000000420661271037650300233750ustar00rootroot00000000000000 /* pngwtran.c - transforms the data in a row for PNG writers * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_WRITE_SUPPORTED #include "pngpriv.h" /* Transform the data according to the user's wishes. The order of * transformations is significant. */ void /* PRIVATE */ png_do_write_transformations(png_structp png_ptr) { png_debug(1, "in png_do_write_transformations"); if (png_ptr == NULL) return; #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED if (png_ptr->transformations & PNG_USER_TRANSFORM) if (png_ptr->write_user_transform_fn != NULL) (*(png_ptr->write_user_transform_fn)) /* User write transform function */ (png_ptr, /* png_ptr */ &(png_ptr->row_info), /* row_info: */ /* png_uint_32 width; width of row */ /* png_uint_32 rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ /* png_byte bit_depth; bit depth of samples */ /* png_byte channels; number of channels (1-4) */ /* png_byte pixel_depth; bits per pixel (depth*channels) */ png_ptr->row_buf + 1); /* start of pixel data for row */ #endif #ifdef PNG_WRITE_FILLER_SUPPORTED if (png_ptr->transformations & PNG_FILLER) png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->flags); #endif #ifdef PNG_WRITE_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_PACK_SUPPORTED if (png_ptr->transformations & PNG_PACK) png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, (png_uint_32)png_ptr->bit_depth); #endif #ifdef PNG_WRITE_SWAP_SUPPORTED if (png_ptr->transformations & PNG_SWAP_BYTES) png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_SHIFT_SUPPORTED if (png_ptr->transformations & PNG_SHIFT) png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, &(png_ptr->shift)); #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_SWAP_ALPHA) png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_INVERT_ALPHA) png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_BGR_SUPPORTED if (png_ptr->transformations & PNG_BGR) png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #ifdef PNG_WRITE_INVERT_SUPPORTED if (png_ptr->transformations & PNG_INVERT_MONO) png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif } #ifdef PNG_WRITE_PACK_SUPPORTED /* Pack pixels into bytes. Pass the true bit depth in bit_depth. The * row_info bit depth should be 8 (one pixel per byte). The channels * should be 1 (this only happens on grayscale and paletted images). */ void /* PRIVATE */ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) { png_debug(1, "in png_do_pack"); if (row_info->bit_depth == 8 && row_info->channels == 1) { switch ((int)bit_depth) { case 1: { png_bytep sp, dp; int mask, v; png_uint_32 i; png_uint_32 row_width = row_info->width; sp = row; dp = row; mask = 0x80; v = 0; for (i = 0; i < row_width; i++) { if (*sp != 0) v |= mask; sp++; if (mask > 1) mask >>= 1; else { mask = 0x80; *dp = (png_byte)v; dp++; v = 0; } } if (mask != 0x80) *dp = (png_byte)v; break; } case 2: { png_bytep sp, dp; int shift, v; png_uint_32 i; png_uint_32 row_width = row_info->width; sp = row; dp = row; shift = 6; v = 0; for (i = 0; i < row_width; i++) { png_byte value; value = (png_byte)(*sp & 0x03); v |= (value << shift); if (shift == 0) { shift = 6; *dp = (png_byte)v; dp++; v = 0; } else shift -= 2; sp++; } if (shift != 6) *dp = (png_byte)v; break; } case 4: { png_bytep sp, dp; int shift, v; png_uint_32 i; png_uint_32 row_width = row_info->width; sp = row; dp = row; shift = 4; v = 0; for (i = 0; i < row_width; i++) { png_byte value; value = (png_byte)(*sp & 0x0f); v |= (value << shift); if (shift == 0) { shift = 4; *dp = (png_byte)v; dp++; v = 0; } else shift -= 4; sp++; } if (shift != 4) *dp = (png_byte)v; break; } } row_info->bit_depth = (png_byte)bit_depth; row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); } } #endif #ifdef PNG_WRITE_SHIFT_SUPPORTED /* Shift pixel values to take advantage of whole range. Pass the * true number of bits in bit_depth. The row should be packed * according to row_info->bit_depth. Thus, if you had a row of * bit depth 4, but the pixels only had values from 0 to 7, you * would pass 3 as bit_depth, and this routine would translate the * data to 0 to 15. */ void /* PRIVATE */ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) { png_debug(1, "in png_do_shift"); if ( row_info->color_type != PNG_COLOR_TYPE_PALETTE) { int shift_start[4], shift_dec[4]; int channels = 0; if (row_info->color_type & PNG_COLOR_MASK_COLOR) { shift_start[channels] = row_info->bit_depth - bit_depth->red; shift_dec[channels] = bit_depth->red; channels++; shift_start[channels] = row_info->bit_depth - bit_depth->green; shift_dec[channels] = bit_depth->green; channels++; shift_start[channels] = row_info->bit_depth - bit_depth->blue; shift_dec[channels] = bit_depth->blue; channels++; } else { shift_start[channels] = row_info->bit_depth - bit_depth->gray; shift_dec[channels] = bit_depth->gray; channels++; } if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { shift_start[channels] = row_info->bit_depth - bit_depth->alpha; shift_dec[channels] = bit_depth->alpha; channels++; } /* With low row depths, could only be grayscale, so one channel */ if (row_info->bit_depth < 8) { png_bytep bp = row; png_uint_32 i; png_byte mask; png_uint_32 row_bytes = row_info->rowbytes; if (bit_depth->gray == 1 && row_info->bit_depth == 2) mask = 0x55; else if (row_info->bit_depth == 4 && bit_depth->gray == 3) mask = 0x11; else mask = 0xff; for (i = 0; i < row_bytes; i++, bp++) { png_uint_16 v; int j; v = *bp; *bp = 0; for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) { if (j > 0) *bp |= (png_byte)((v << j) & 0xff); else *bp |= (png_byte)((v >> (-j)) & mask); } } } else if (row_info->bit_depth == 8) { png_bytep bp = row; png_uint_32 i; png_uint_32 istop = channels * row_info->width; for (i = 0; i < istop; i++, bp++) { png_uint_16 v; int j; int c = (int)(i%channels); v = *bp; *bp = 0; for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) *bp |= (png_byte)((v << j) & 0xff); else *bp |= (png_byte)((v >> (-j)) & 0xff); } } } else { png_bytep bp; png_uint_32 i; png_uint_32 istop = channels * row_info->width; for (bp = row, i = 0; i < istop; i++) { int c = (int)(i%channels); png_uint_16 value, v; int j; v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); value = 0; for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); else value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); } *bp++ = (png_byte)(value >> 8); *bp++ = (png_byte)(value & 0xff); } } } } #endif #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED void /* PRIVATE */ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_write_swap_alpha"); { if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { /* This converts from ARGB to RGBA */ if (row_info->bit_depth == 8) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { png_byte save = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = save; } } /* This converts from AARRGGBB to RRGGBBAA */ else { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { png_byte save[2]; save[0] = *(sp++); save[1] = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = save[0]; *(dp++) = save[1]; } } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { /* This converts from AG to GA */ if (row_info->bit_depth == 8) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { png_byte save = *(sp++); *(dp++) = *(sp++); *(dp++) = save; } } /* This converts from AAGG to GGAA */ else { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { png_byte save[2]; save[0] = *(sp++); save[1] = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = save[0]; *(dp++) = save[1]; } } } } } #endif #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED void /* PRIVATE */ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_write_invert_alpha"); { if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { /* This inverts the alpha channel in RGBA */ if (row_info->bit_depth == 8) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); */ sp+=3; dp = sp; *(dp++) = (png_byte)(255 - *(sp++)); } } /* This inverts the alpha channel in RRGGBBAA */ else { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); */ sp+=6; dp = sp; *(dp++) = (png_byte)(255 - *(sp++)); *(dp++) = (png_byte)(255 - *(sp++)); } } } else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { /* This inverts the alpha channel in GA */ if (row_info->bit_depth == 8) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { *(dp++) = *(sp++); *(dp++) = (png_byte)(255 - *(sp++)); } } /* This inverts the alpha channel in GGAA */ else { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); */ sp+=2; dp = sp; *(dp++) = (png_byte)(255 - *(sp++)); *(dp++) = (png_byte)(255 - *(sp++)); } } } } } #endif #ifdef PNG_MNG_FEATURES_SUPPORTED /* Undoes intrapixel differencing */ void /* PRIVATE */ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) { png_debug(1, "in png_do_write_intrapixel"); if ( (row_info->color_type & PNG_COLOR_MASK_COLOR)) { int bytes_per_pixel; png_uint_32 row_width = row_info->width; if (row_info->bit_depth == 8) { png_bytep rp; png_uint_32 i; if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 3; else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 4; else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { *(rp) = (png_byte)((*rp - *(rp+1))&0xff); *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); } } else if (row_info->bit_depth == 16) { png_bytep rp; png_uint_32 i; if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 6; else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 8; else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); *(rp ) = (png_byte)((red >> 8) & 0xff); *(rp+1) = (png_byte)(red & 0xff); *(rp+4) = (png_byte)((blue >> 8) & 0xff); *(rp+5) = (png_byte)(blue & 0xff); } } } } #endif /* PNG_MNG_FEATURES_SUPPORTED */ #endif /* PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/libpng-src/src/pngwutil.c000066400000000000000000002523071271037650300234070ustar00rootroot00000000000000 /* pngwutil.c - utilities to write a PNG file * * Last changed in libpng 1.4.1 [February 25, 2010] * Copyright (c) 1998-2010 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * * This code is released under the libpng license. * For conditions of distribution and use, see the disclaimer * and license in png.h */ #define PNG_NO_PEDANTIC_WARNINGS #include "png.h" #ifdef PNG_WRITE_SUPPORTED #include "pngpriv.h" /* Place a 32-bit number into a buffer in PNG byte order. We work * with unsigned numbers for convenience, although one supported * ancillary chunk uses signed (two's complement) numbers. */ void PNGAPI png_save_uint_32(png_bytep buf, png_uint_32 i) { buf[0] = (png_byte)((i >> 24) & 0xff); buf[1] = (png_byte)((i >> 16) & 0xff); buf[2] = (png_byte)((i >> 8) & 0xff); buf[3] = (png_byte)(i & 0xff); } #ifdef PNG_SAVE_INT_32_SUPPORTED /* The png_save_int_32 function assumes integers are stored in two's * complement format. If this isn't the case, then this routine needs to * be modified to write data in two's complement format. */ void PNGAPI png_save_int_32(png_bytep buf, png_int_32 i) { buf[0] = (png_byte)((i >> 24) & 0xff); buf[1] = (png_byte)((i >> 16) & 0xff); buf[2] = (png_byte)((i >> 8) & 0xff); buf[3] = (png_byte)(i & 0xff); } #endif /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, * just to avoid potential problems on pre-ANSI C compilers. */ void PNGAPI png_save_uint_16(png_bytep buf, unsigned int i) { buf[0] = (png_byte)((i >> 8) & 0xff); buf[1] = (png_byte)(i & 0xff); } /* Simple function to write the signature. If we have already written * the magic bytes of the signature, or more likely, the PNG stream is * being embedded into another stream and doesn't need its own signature, * we should call png_set_sig_bytes() to tell libpng how many of the * bytes have already been written. */ void PNGAPI png_write_sig(png_structp png_ptr) { png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that the signature is being written */ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE; #endif /* Write the rest of the 8 byte signature */ png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], (png_size_t)(8 - png_ptr->sig_bytes)); if (png_ptr->sig_bytes < 3) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; } /* Write a PNG chunk all at once. The type is an array of ASCII characters * representing the chunk name. The array must be at least 4 bytes in * length, and does not need to be null terminated. To be safe, pass the * pre-defined chunk names here, and if you need a new one, define it * where the others are defined. The length is the length of the data. * All the data must be present. If that is not possible, use the * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() * functions instead. */ void PNGAPI png_write_chunk(png_structp png_ptr, png_bytep chunk_name, png_bytep data, png_size_t length) { if (png_ptr == NULL) return; png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); png_write_chunk_data(png_ptr, data, (png_size_t)length); png_write_chunk_end(png_ptr); } /* Write the start of a PNG chunk. The type is the chunk type. * The total_length is the sum of the lengths of all the data you will be * passing in png_write_chunk_data(). */ void PNGAPI png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, png_uint_32 length) { png_byte buf[8]; png_debug2(0, "Writing %s chunk, length = %lu", chunk_name, (unsigned long)length); if (png_ptr == NULL) return; #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that the chunk header is being written. * PNG_IO_CHUNK_HDR requires a single I/O call. */ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR; #endif /* Write the length and the chunk name */ png_save_uint_32(buf, length); png_memcpy(buf + 4, chunk_name, 4); png_write_data(png_ptr, buf, (png_size_t)8); /* Put the chunk name into png_ptr->chunk_name */ png_memcpy(png_ptr->chunk_name, chunk_name, 4); /* Reset the crc and run it over the chunk name */ png_reset_crc(png_ptr); png_calculate_crc(png_ptr, chunk_name, 4); #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that chunk data will (possibly) be written. * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. */ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA; #endif } /* Write the data of a PNG chunk started with png_write_chunk_start(). * Note that multiple calls to this function are allowed, and that the * sum of the lengths from these calls *must* add up to the total_length * given to png_write_chunk_start(). */ void PNGAPI png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) { /* Write the data, and run the CRC over it */ if (png_ptr == NULL) return; if (data != NULL && length > 0) { png_write_data(png_ptr, data, length); /* Update the CRC after writing the data, * in case that the user I/O routine alters it. */ png_calculate_crc(png_ptr, data, length); } } /* Finish a chunk started with png_write_chunk_start(). */ void PNGAPI png_write_chunk_end(png_structp png_ptr) { png_byte buf[4]; if (png_ptr == NULL) return; #ifdef PNG_IO_STATE_SUPPORTED /* Inform the I/O callback that the chunk CRC is being written. * PNG_IO_CHUNK_CRC requires a single I/O function call. */ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC; #endif /* Write the crc in a single operation */ png_save_uint_32(buf, png_ptr->crc); png_write_data(png_ptr, buf, (png_size_t)4); } #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) /* This pair of functions encapsulates the operation of (a) compressing a * text string, and (b) issuing it later as a series of chunk data writes. * The compression_state structure is shared context for these functions * set up by the caller in order to make the whole mess thread-safe. */ typedef struct { char *input; /* The uncompressed input data */ int input_len; /* Its length */ int num_output_ptr; /* Number of output pointers used */ int max_output_ptr; /* Size of output_ptr */ png_charpp output_ptr; /* Array of pointers to output */ } compression_state; /* Compress given text into storage in the png_ptr structure */ static int /* PRIVATE */ png_text_compress(png_structp png_ptr, png_charp text, png_size_t text_len, int compression, compression_state *comp) { int ret; comp->num_output_ptr = 0; comp->max_output_ptr = 0; comp->output_ptr = NULL; comp->input = NULL; comp->input_len = 0; /* We may just want to pass the text right through */ if (compression == PNG_TEXT_COMPRESSION_NONE) { comp->input = text; comp->input_len = text_len; return((int)text_len); } if (compression >= PNG_TEXT_COMPRESSION_LAST) { #ifdef PNG_STDIO_SUPPORTED char msg[50]; png_snprintf(msg, 50, "Unknown compression type %d", compression); png_warning(png_ptr, msg); #else png_warning(png_ptr, "Unknown compression type"); #endif } /* We can't write the chunk until we find out how much data we have, * which means we need to run the compressor first and save the * output. This shouldn't be a problem, as the vast majority of * comments should be reasonable, but we will set up an array of * malloc'd pointers to be sure. * * If we knew the application was well behaved, we could simplify this * greatly by assuming we can always malloc an output buffer large * enough to hold the compressed text ((1001 * text_len / 1000) + 12) * and malloc this directly. The only time this would be a bad idea is * if we can't malloc more than 64K and we have 64K of random input * data, or if the input string is incredibly large (although this * wouldn't cause a failure, just a slowdown due to swapping). */ /* Set up the compression buffers */ png_ptr->zstream.avail_in = (uInt)text_len; png_ptr->zstream.next_in = (Bytef *)text; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; /* This is the same compression loop as in png_write_row() */ do { /* Compress the data */ ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); if (ret != Z_OK) { /* Error */ if (png_ptr->zstream.msg != NULL) png_error(png_ptr, png_ptr->zstream.msg); else png_error(png_ptr, "zlib error"); } /* Check to see if we need more room */ if (!(png_ptr->zstream.avail_out)) { /* Make sure the output array has room */ if (comp->num_output_ptr >= comp->max_output_ptr) { int old_max; old_max = comp->max_output_ptr; comp->max_output_ptr = comp->num_output_ptr + 4; if (comp->output_ptr != NULL) { png_charpp old_ptr; old_ptr = comp->output_ptr; comp->output_ptr = (png_charpp)png_malloc(png_ptr, (png_alloc_size_t) (comp->max_output_ptr * png_sizeof(png_charpp))); png_memcpy(comp->output_ptr, old_ptr, old_max * png_sizeof(png_charp)); png_free(png_ptr, old_ptr); } else comp->output_ptr = (png_charpp)png_malloc(png_ptr, (png_alloc_size_t) (comp->max_output_ptr * png_sizeof(png_charp))); } /* Save the data */ comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, (png_alloc_size_t)png_ptr->zbuf_size); png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, png_ptr->zbuf_size); comp->num_output_ptr++; /* and reset the buffer */ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_out = png_ptr->zbuf; } /* Continue until we don't have any more to compress */ } while (png_ptr->zstream.avail_in); /* Finish the compression */ do { /* Tell zlib we are finished */ ret = deflate(&png_ptr->zstream, Z_FINISH); if (ret == Z_OK) { /* Check to see if we need more room */ if (!(png_ptr->zstream.avail_out)) { /* Check to make sure our output array has room */ if (comp->num_output_ptr >= comp->max_output_ptr) { int old_max; old_max = comp->max_output_ptr; comp->max_output_ptr = comp->num_output_ptr + 4; if (comp->output_ptr != NULL) { png_charpp old_ptr; old_ptr = comp->output_ptr; /* This could be optimized to realloc() */ comp->output_ptr = (png_charpp)png_malloc(png_ptr, (png_alloc_size_t)(comp->max_output_ptr * png_sizeof(png_charp))); png_memcpy(comp->output_ptr, old_ptr, old_max * png_sizeof(png_charp)); png_free(png_ptr, old_ptr); } else comp->output_ptr = (png_charpp)png_malloc(png_ptr, (png_alloc_size_t)(comp->max_output_ptr * png_sizeof(png_charp))); } /* Save the data */ comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, (png_alloc_size_t)png_ptr->zbuf_size); png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, png_ptr->zbuf_size); comp->num_output_ptr++; /* and reset the buffer pointers */ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_out = png_ptr->zbuf; } } else if (ret != Z_STREAM_END) { /* We got an error */ if (png_ptr->zstream.msg != NULL) png_error(png_ptr, png_ptr->zstream.msg); else png_error(png_ptr, "zlib error"); } } while (ret != Z_STREAM_END); /* Text length is number of buffers plus last buffer */ text_len = png_ptr->zbuf_size * comp->num_output_ptr; if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; return((int)text_len); } /* Ship the compressed text out via chunk writes */ static void /* PRIVATE */ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) { int i; /* Handle the no-compression case */ if (comp->input) { png_write_chunk_data(png_ptr, (png_bytep)comp->input, (png_size_t)comp->input_len); return; } /* Write saved output buffers, if any */ for (i = 0; i < comp->num_output_ptr; i++) { png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i], (png_size_t)png_ptr->zbuf_size); png_free(png_ptr, comp->output_ptr[i]); } if (comp->max_output_ptr != 0) png_free(png_ptr, comp->output_ptr); /* Write anything left in zbuf */ if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) png_write_chunk_data(png_ptr, png_ptr->zbuf, (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); /* Reset zlib for another zTXt/iTXt or image data */ deflateReset(&png_ptr->zstream); png_ptr->zstream.data_type = Z_BINARY; } #endif /* Write the IHDR chunk, and update the png_struct with the necessary * information. Note that the rest of this code depends upon this * information being correct. */ void /* PRIVATE */ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int compression_type, int filter_type, int interlace_type) { PNG_IHDR; int ret; png_byte buf[13]; /* Buffer to store the IHDR info */ png_debug(1, "in png_write_IHDR"); /* Check that we have valid input data from the application info */ switch (color_type) { case PNG_COLOR_TYPE_GRAY: switch (bit_depth) { case 1: case 2: case 4: case 8: case 16: png_ptr->channels = 1; break; default: png_error(png_ptr, "Invalid bit depth for grayscale image"); } break; case PNG_COLOR_TYPE_RGB: if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for RGB image"); png_ptr->channels = 3; break; case PNG_COLOR_TYPE_PALETTE: switch (bit_depth) { case 1: case 2: case 4: case 8: png_ptr->channels = 1; break; default: png_error(png_ptr, "Invalid bit depth for paletted image"); } break; case PNG_COLOR_TYPE_GRAY_ALPHA: if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); png_ptr->channels = 2; break; case PNG_COLOR_TYPE_RGB_ALPHA: if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for RGBA image"); png_ptr->channels = 4; break; default: png_error(png_ptr, "Invalid image color type specified"); } if (compression_type != PNG_COMPRESSION_TYPE_BASE) { png_warning(png_ptr, "Invalid compression type specified"); compression_type = PNG_COMPRESSION_TYPE_BASE; } /* Write filter_method 64 (intrapixel differencing) only if * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and * 2. Libpng did not write a PNG signature (this filter_method is only * used in PNG datastreams that are embedded in MNG datastreams) and * 3. The application called png_permit_mng_features with a mask that * included PNG_FLAG_MNG_FILTER_64 and * 4. The filter_method is 64 and * 5. The color_type is RGB or RGBA */ if ( #ifdef PNG_MNG_FEATURES_SUPPORTED !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) && (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && #endif filter_type != PNG_FILTER_TYPE_BASE) { png_warning(png_ptr, "Invalid filter type specified"); filter_type = PNG_FILTER_TYPE_BASE; } #ifdef PNG_WRITE_INTERLACING_SUPPORTED if (interlace_type != PNG_INTERLACE_NONE && interlace_type != PNG_INTERLACE_ADAM7) { png_warning(png_ptr, "Invalid interlace type specified"); interlace_type = PNG_INTERLACE_ADAM7; } #else interlace_type=PNG_INTERLACE_NONE; #endif /* Save the relevent information */ png_ptr->bit_depth = (png_byte)bit_depth; png_ptr->color_type = (png_byte)color_type; png_ptr->interlaced = (png_byte)interlace_type; #ifdef PNG_MNG_FEATURES_SUPPORTED png_ptr->filter_type = (png_byte)filter_type; #endif png_ptr->compression_type = (png_byte)compression_type; png_ptr->width = width; png_ptr->height = height; png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); /* Set the usr info, so any transformations can modify it */ png_ptr->usr_width = png_ptr->width; png_ptr->usr_bit_depth = png_ptr->bit_depth; png_ptr->usr_channels = png_ptr->channels; /* Pack the header information into the buffer */ png_save_uint_32(buf, width); png_save_uint_32(buf + 4, height); buf[8] = (png_byte)bit_depth; buf[9] = (png_byte)color_type; buf[10] = (png_byte)compression_type; buf[11] = (png_byte)filter_type; buf[12] = (png_byte)interlace_type; /* Write the chunk */ png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); /* Initialize zlib with PNG info */ png_ptr->zstream.zalloc = png_zalloc; png_ptr->zstream.zfree = png_zfree; png_ptr->zstream.opaque = (voidpf)png_ptr; if (!(png_ptr->do_filter)) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || png_ptr->bit_depth < 8) png_ptr->do_filter = PNG_FILTER_NONE; else png_ptr->do_filter = PNG_ALL_FILTERS; } if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) { if (png_ptr->do_filter != PNG_FILTER_NONE) png_ptr->zlib_strategy = Z_FILTERED; else png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; } if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) png_ptr->zlib_mem_level = 8; if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) png_ptr->zlib_window_bits = 15; if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) png_ptr->zlib_method = 8; ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, png_ptr->zlib_method, png_ptr->zlib_window_bits, png_ptr->zlib_mem_level, png_ptr->zlib_strategy); if (ret != Z_OK) { if (ret == Z_VERSION_ERROR) png_error(png_ptr, "zlib failed to initialize compressor -- version error"); if (ret == Z_STREAM_ERROR) png_error(png_ptr, "zlib failed to initialize compressor -- stream error"); if (ret == Z_MEM_ERROR) png_error(png_ptr, "zlib failed to initialize compressor -- mem error"); png_error(png_ptr, "zlib failed to initialize compressor"); } png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; /* libpng is not interested in zstream.data_type */ /* Set it to a predefined value, to avoid its evaluation inside zlib */ png_ptr->zstream.data_type = Z_BINARY; png_ptr->mode = PNG_HAVE_IHDR; } /* Write the palette. We are careful not to trust png_color to be in the * correct order for PNG, so people can redefine it to any convenient * structure. */ void /* PRIVATE */ png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) { PNG_PLTE; png_uint_32 i; png_colorp pal_ptr; png_byte buf[3]; png_debug(1, "in png_write_PLTE"); if (( #ifdef PNG_MNG_FEATURES_SUPPORTED !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && #endif num_pal == 0) || num_pal > 256) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { png_error(png_ptr, "Invalid number of colors in palette"); } else { png_warning(png_ptr, "Invalid number of colors in palette"); return; } } if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) { png_warning(png_ptr, "Ignoring request to write a PLTE chunk in grayscale PNG"); return; } png_ptr->num_palette = (png_uint_16)num_pal; png_debug1(3, "num_palette = %d", png_ptr->num_palette); png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, (png_uint_32)(num_pal * 3)); #ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) { buf[0] = pal_ptr->red; buf[1] = pal_ptr->green; buf[2] = pal_ptr->blue; png_write_chunk_data(png_ptr, buf, (png_size_t)3); } #else /* This is a little slower but some buggy compilers need to do this * instead */ pal_ptr=palette; for (i = 0; i < num_pal; i++) { buf[0] = pal_ptr[i].red; buf[1] = pal_ptr[i].green; buf[2] = pal_ptr[i].blue; png_write_chunk_data(png_ptr, buf, (png_size_t)3); } #endif png_write_chunk_end(png_ptr); png_ptr->mode |= PNG_HAVE_PLTE; } /* Write an IDAT chunk */ void /* PRIVATE */ png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) { PNG_IDAT; png_debug(1, "in png_write_IDAT"); /* Optimize the CMF field in the zlib stream. */ /* This hack of the zlib stream is compliant to the stream specification. */ if (!(png_ptr->mode & PNG_HAVE_IDAT) && png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) { unsigned int z_cmf = data[0]; /* zlib compression method and flags */ if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) { /* Avoid memory underflows and multiplication overflows. * * The conditions below are practically always satisfied; * however, they still must be checked. */ if (length >= 2 && png_ptr->height < 16384 && png_ptr->width < 16384) { png_uint_32 uncompressed_idat_size = png_ptr->height * ((png_ptr->width * png_ptr->channels * png_ptr->bit_depth + 15) >> 3); unsigned int z_cinfo = z_cmf >> 4; unsigned int half_z_window_size = 1 << (z_cinfo + 7); while (uncompressed_idat_size <= half_z_window_size && half_z_window_size >= 256) { z_cinfo--; half_z_window_size >>= 1; } z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); if (data[0] != (png_byte)z_cmf) { data[0] = (png_byte)z_cmf; data[1] &= 0xe0; data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); } } } else png_error(png_ptr, "Invalid zlib compression method or flags in IDAT"); } png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); png_ptr->mode |= PNG_HAVE_IDAT; } /* Write an IEND chunk */ void /* PRIVATE */ png_write_IEND(png_structp png_ptr) { PNG_IEND; png_debug(1, "in png_write_IEND"); png_write_chunk(png_ptr, (png_bytep)png_IEND, NULL, (png_size_t)0); png_ptr->mode |= PNG_HAVE_IEND; } #ifdef PNG_WRITE_gAMA_SUPPORTED /* Write a gAMA chunk */ #ifdef PNG_FLOATING_POINT_SUPPORTED void /* PRIVATE */ png_write_gAMA(png_structp png_ptr, double file_gamma) { PNG_gAMA; png_uint_32 igamma; png_byte buf[4]; png_debug(1, "in png_write_gAMA"); /* file_gamma is saved in 1/100,000ths */ igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); png_save_uint_32(buf, igamma); png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); } #endif #ifdef PNG_FIXED_POINT_SUPPORTED void /* PRIVATE */ png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) { PNG_gAMA; png_byte buf[4]; png_debug(1, "in png_write_gAMA"); /* file_gamma is saved in 1/100,000ths */ png_save_uint_32(buf, (png_uint_32)file_gamma); png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); } #endif #endif #ifdef PNG_WRITE_sRGB_SUPPORTED /* Write a sRGB chunk */ void /* PRIVATE */ png_write_sRGB(png_structp png_ptr, int srgb_intent) { PNG_sRGB; png_byte buf[1]; png_debug(1, "in png_write_sRGB"); if (srgb_intent >= PNG_sRGB_INTENT_LAST) png_warning(png_ptr, "Invalid sRGB rendering intent specified"); buf[0]=(png_byte)srgb_intent; png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); } #endif #ifdef PNG_WRITE_iCCP_SUPPORTED /* Write an iCCP chunk */ void /* PRIVATE */ png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, png_charp profile, int profile_len) { PNG_iCCP; png_size_t name_len; png_charp new_name; compression_state comp; int embedded_profile_len = 0; png_debug(1, "in png_write_iCCP"); comp.num_output_ptr = 0; comp.max_output_ptr = 0; comp.output_ptr = NULL; comp.input = NULL; comp.input_len = 0; if ((name_len = png_check_keyword(png_ptr, name, &new_name)) == 0) return; if (compression_type != PNG_COMPRESSION_TYPE_BASE) png_warning(png_ptr, "Unknown compression type in iCCP chunk"); if (profile == NULL) profile_len = 0; if (profile_len > 3) embedded_profile_len = ((*( (png_bytep)profile ))<<24) | ((*( (png_bytep)profile + 1))<<16) | ((*( (png_bytep)profile + 2))<< 8) | ((*( (png_bytep)profile + 3)) ); if (embedded_profile_len < 0) { png_warning(png_ptr, "Embedded profile length in iCCP chunk is negative"); png_free(png_ptr, new_name); return; } if (profile_len < embedded_profile_len) { png_warning(png_ptr, "Embedded profile length too large in iCCP chunk"); png_free(png_ptr, new_name); return; } if (profile_len > embedded_profile_len) { png_warning(png_ptr, "Truncating profile to actual length in iCCP chunk"); profile_len = embedded_profile_len; } if (profile_len) profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp); /* Make sure we include the NULL after the name and the compression type */ png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, (png_uint_32)(name_len + profile_len + 2)); new_name[name_len + 1] = 0x00; png_write_chunk_data(png_ptr, (png_bytep)new_name, (png_size_t)(name_len + 2)); if (profile_len) png_write_compressed_data_out(png_ptr, &comp); png_write_chunk_end(png_ptr); png_free(png_ptr, new_name); } #endif #ifdef PNG_WRITE_sPLT_SUPPORTED /* Write a sPLT chunk */ void /* PRIVATE */ png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) { PNG_sPLT; png_size_t name_len; png_charp new_name; png_byte entrybuf[10]; png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); png_size_t palette_size = entry_size * spalette->nentries; png_sPLT_entryp ep; #ifndef PNG_POINTER_INDEXING_SUPPORTED int i; #endif png_debug(1, "in png_write_sPLT"); if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0) return; /* Make sure we include the NULL after the name */ png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, (png_uint_32)(name_len + 2 + palette_size)); png_write_chunk_data(png_ptr, (png_bytep)new_name, (png_size_t)(name_len + 1)); png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1); /* Loop through each palette entry, writing appropriately */ #ifdef PNG_POINTER_INDEXING_SUPPORTED for (ep = spalette->entries; epentries + spalette->nentries; ep++) { if (spalette->depth == 8) { entrybuf[0] = (png_byte)ep->red; entrybuf[1] = (png_byte)ep->green; entrybuf[2] = (png_byte)ep->blue; entrybuf[3] = (png_byte)ep->alpha; png_save_uint_16(entrybuf + 4, ep->frequency); } else { png_save_uint_16(entrybuf + 0, ep->red); png_save_uint_16(entrybuf + 2, ep->green); png_save_uint_16(entrybuf + 4, ep->blue); png_save_uint_16(entrybuf + 6, ep->alpha); png_save_uint_16(entrybuf + 8, ep->frequency); } png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); } #else ep=spalette->entries; for (i=0; i>spalette->nentries; i++) { if (spalette->depth == 8) { entrybuf[0] = (png_byte)ep[i].red; entrybuf[1] = (png_byte)ep[i].green; entrybuf[2] = (png_byte)ep[i].blue; entrybuf[3] = (png_byte)ep[i].alpha; png_save_uint_16(entrybuf + 4, ep[i].frequency); } else { png_save_uint_16(entrybuf + 0, ep[i].red); png_save_uint_16(entrybuf + 2, ep[i].green); png_save_uint_16(entrybuf + 4, ep[i].blue); png_save_uint_16(entrybuf + 6, ep[i].alpha); png_save_uint_16(entrybuf + 8, ep[i].frequency); } png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); } #endif png_write_chunk_end(png_ptr); png_free(png_ptr, new_name); } #endif #ifdef PNG_WRITE_sBIT_SUPPORTED /* Write the sBIT chunk */ void /* PRIVATE */ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) { PNG_sBIT; png_byte buf[4]; png_size_t size; png_debug(1, "in png_write_sBIT"); /* Make sure we don't depend upon the order of PNG_COLOR_8 */ if (color_type & PNG_COLOR_MASK_COLOR) { png_byte maxbits; maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : png_ptr->usr_bit_depth); if (sbit->red == 0 || sbit->red > maxbits || sbit->green == 0 || sbit->green > maxbits || sbit->blue == 0 || sbit->blue > maxbits) { png_warning(png_ptr, "Invalid sBIT depth specified"); return; } buf[0] = sbit->red; buf[1] = sbit->green; buf[2] = sbit->blue; size = 3; } else { if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) { png_warning(png_ptr, "Invalid sBIT depth specified"); return; } buf[0] = sbit->gray; size = 1; } if (color_type & PNG_COLOR_MASK_ALPHA) { if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) { png_warning(png_ptr, "Invalid sBIT depth specified"); return; } buf[size++] = sbit->alpha; } png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); } #endif #ifdef PNG_WRITE_cHRM_SUPPORTED /* Write the cHRM chunk */ #ifdef PNG_FLOATING_POINT_SUPPORTED void /* PRIVATE */ png_write_cHRM(png_structp png_ptr, double white_x, double white_y, double red_x, double red_y, double green_x, double green_y, double blue_x, double blue_y) { PNG_cHRM; png_byte buf[32]; png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y; png_debug(1, "in png_write_cHRM"); int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5); int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5); int_red_x = (png_uint_32)(red_x * 100000.0 + 0.5); int_red_y = (png_uint_32)(red_y * 100000.0 + 0.5); int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5); int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5); int_blue_x = (png_uint_32)(blue_x * 100000.0 + 0.5); int_blue_y = (png_uint_32)(blue_y * 100000.0 + 0.5); #ifdef PNG_CHECK_cHRM_SUPPORTED if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y)) #endif { /* Each value is saved in 1/100,000ths */ png_save_uint_32(buf, int_white_x); png_save_uint_32(buf + 4, int_white_y); png_save_uint_32(buf + 8, int_red_x); png_save_uint_32(buf + 12, int_red_y); png_save_uint_32(buf + 16, int_green_x); png_save_uint_32(buf + 20, int_green_y); png_save_uint_32(buf + 24, int_blue_x); png_save_uint_32(buf + 28, int_blue_y); png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); } } #endif #ifdef PNG_FIXED_POINT_SUPPORTED void /* PRIVATE */ png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, png_fixed_point blue_y) { PNG_cHRM; png_byte buf[32]; png_debug(1, "in png_write_cHRM"); /* Each value is saved in 1/100,000ths */ #ifdef PNG_CHECK_cHRM_SUPPORTED if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) #endif { png_save_uint_32(buf, (png_uint_32)white_x); png_save_uint_32(buf + 4, (png_uint_32)white_y); png_save_uint_32(buf + 8, (png_uint_32)red_x); png_save_uint_32(buf + 12, (png_uint_32)red_y); png_save_uint_32(buf + 16, (png_uint_32)green_x); png_save_uint_32(buf + 20, (png_uint_32)green_y); png_save_uint_32(buf + 24, (png_uint_32)blue_x); png_save_uint_32(buf + 28, (png_uint_32)blue_y); png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); } } #endif #endif #ifdef PNG_WRITE_tRNS_SUPPORTED /* Write the tRNS chunk */ void /* PRIVATE */ png_write_tRNS(png_structp png_ptr, png_bytep trans_alpha, png_color_16p tran, int num_trans, int color_type) { PNG_tRNS; png_byte buf[6]; png_debug(1, "in png_write_tRNS"); if (color_type == PNG_COLOR_TYPE_PALETTE) { if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) { png_warning(png_ptr, "Invalid number of transparent colors specified"); return; } /* Write the chunk out as it is */ png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans_alpha, (png_size_t)num_trans); } else if (color_type == PNG_COLOR_TYPE_GRAY) { /* One 16 bit value */ if (tran->gray >= (1 << png_ptr->bit_depth)) { png_warning(png_ptr, "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); return; } png_save_uint_16(buf, tran->gray); png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); } else if (color_type == PNG_COLOR_TYPE_RGB) { /* Three 16 bit values */ png_save_uint_16(buf, tran->red); png_save_uint_16(buf + 2, tran->green); png_save_uint_16(buf + 4, tran->blue); if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) { png_warning(png_ptr, "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); return; } png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); } else { png_warning(png_ptr, "Can't write tRNS with an alpha channel"); } } #endif #ifdef PNG_WRITE_bKGD_SUPPORTED /* Write the background chunk */ void /* PRIVATE */ png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) { PNG_bKGD; png_byte buf[6]; png_debug(1, "in png_write_bKGD"); if (color_type == PNG_COLOR_TYPE_PALETTE) { if ( #ifdef PNG_MNG_FEATURES_SUPPORTED (png_ptr->num_palette || (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && #endif back->index >= png_ptr->num_palette) { png_warning(png_ptr, "Invalid background palette index"); return; } buf[0] = back->index; png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); } else if (color_type & PNG_COLOR_MASK_COLOR) { png_save_uint_16(buf, back->red); png_save_uint_16(buf + 2, back->green); png_save_uint_16(buf + 4, back->blue); if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) { png_warning(png_ptr, "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); return; } png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); } else { if (back->gray >= (1 << png_ptr->bit_depth)) { png_warning(png_ptr, "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); return; } png_save_uint_16(buf, back->gray); png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); } } #endif #ifdef PNG_WRITE_hIST_SUPPORTED /* Write the histogram */ void /* PRIVATE */ png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) { PNG_hIST; int i; png_byte buf[3]; png_debug(1, "in png_write_hIST"); if (num_hist > (int)png_ptr->num_palette) { png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, png_ptr->num_palette); png_warning(png_ptr, "Invalid number of histogram entries specified"); return; } png_write_chunk_start(png_ptr, (png_bytep)png_hIST, (png_uint_32)(num_hist * 2)); for (i = 0; i < num_hist; i++) { png_save_uint_16(buf, hist[i]); png_write_chunk_data(png_ptr, buf, (png_size_t)2); } png_write_chunk_end(png_ptr); } #endif #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) /* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, * and if invalid, correct the keyword rather than discarding the entire * chunk. The PNG 1.0 specification requires keywords 1-79 characters in * length, forbids leading or trailing whitespace, multiple internal spaces, * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. * * The new_key is allocated to hold the corrected keyword and must be freed * by the calling routine. This avoids problems with trying to write to * static keywords without having to have duplicate copies of the strings. */ png_size_t /* PRIVATE */ png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) { png_size_t key_len; png_charp kp, dp; int kflag; int kwarn=0; png_debug(1, "in png_check_keyword"); *new_key = NULL; if (key == NULL || (key_len = png_strlen(key)) == 0) { png_warning(png_ptr, "zero length keyword"); return ((png_size_t)0); } png_debug1(2, "Keyword to be checked is '%s'", key); *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); if (*new_key == NULL) { png_warning(png_ptr, "Out of memory while procesing keyword"); return ((png_size_t)0); } /* Replace non-printing characters with a blank and print a warning */ for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) { if ((png_byte)*kp < 0x20 || ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) { #ifdef PNG_STDIO_SUPPORTED char msg[40]; png_snprintf(msg, 40, "invalid keyword character 0x%02X", (png_byte)*kp); png_warning(png_ptr, msg); #else png_warning(png_ptr, "invalid character in keyword"); #endif *dp = ' '; } else { *dp = *kp; } } *dp = '\0'; /* Remove any trailing white space. */ kp = *new_key + key_len - 1; if (*kp == ' ') { png_warning(png_ptr, "trailing spaces removed from keyword"); while (*kp == ' ') { *(kp--) = '\0'; key_len--; } } /* Remove any leading white space. */ kp = *new_key; if (*kp == ' ') { png_warning(png_ptr, "leading spaces removed from keyword"); while (*kp == ' ') { kp++; key_len--; } } png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); /* Remove multiple internal spaces. */ for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) { if (*kp == ' ' && kflag == 0) { *(dp++) = *kp; kflag = 1; } else if (*kp == ' ') { key_len--; kwarn=1; } else { *(dp++) = *kp; kflag = 0; } } *dp = '\0'; if (kwarn) png_warning(png_ptr, "extra interior spaces removed from keyword"); if (key_len == 0) { png_free(png_ptr, *new_key); png_warning(png_ptr, "Zero length keyword"); } if (key_len > 79) { png_warning(png_ptr, "keyword length must be 1 - 79 characters"); (*new_key)[79] = '\0'; key_len = 79; } return (key_len); } #endif #ifdef PNG_WRITE_tEXt_SUPPORTED /* Write a tEXt chunk */ void /* PRIVATE */ png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, png_size_t text_len) { PNG_tEXt; png_size_t key_len; png_charp new_key; png_debug(1, "in png_write_tEXt"); if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) return; if (text == NULL || *text == '\0') text_len = 0; else text_len = png_strlen(text); /* Make sure we include the 0 after the key */ png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, (png_uint_32)(key_len + text_len + 1)); /* * We leave it to the application to meet PNG-1.0 requirements on the * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. */ png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1)); if (text_len) png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len); png_write_chunk_end(png_ptr); png_free(png_ptr, new_key); } #endif #ifdef PNG_WRITE_zTXt_SUPPORTED /* Write a compressed text chunk */ void /* PRIVATE */ png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, png_size_t text_len, int compression) { PNG_zTXt; png_size_t key_len; char buf[1]; png_charp new_key; compression_state comp; png_debug(1, "in png_write_zTXt"); comp.num_output_ptr = 0; comp.max_output_ptr = 0; comp.output_ptr = NULL; comp.input = NULL; comp.input_len = 0; if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) { png_free(png_ptr, new_key); return; } if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) { png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); png_free(png_ptr, new_key); return; } text_len = png_strlen(text); /* Compute the compressed data; do it now for the length */ text_len = png_text_compress(png_ptr, text, text_len, compression, &comp); /* Write start of chunk */ png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, (png_uint_32)(key_len+text_len + 2)); /* Write key */ png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1)); png_free(png_ptr, new_key); buf[0] = (png_byte)compression; /* Write compression */ png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); /* Write the compressed data */ png_write_compressed_data_out(png_ptr, &comp); /* Close the chunk */ png_write_chunk_end(png_ptr); } #endif #ifdef PNG_WRITE_iTXt_SUPPORTED /* Write an iTXt chunk */ void /* PRIVATE */ png_write_iTXt(png_structp png_ptr, int compression, png_charp key, png_charp lang, png_charp lang_key, png_charp text) { PNG_iTXt; png_size_t lang_len, key_len, lang_key_len, text_len; png_charp new_lang; png_charp new_key = NULL; png_byte cbuf[2]; compression_state comp; png_debug(1, "in png_write_iTXt"); comp.num_output_ptr = 0; comp.max_output_ptr = 0; comp.output_ptr = NULL; comp.input = NULL; if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) return; if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) { png_warning(png_ptr, "Empty language field in iTXt chunk"); new_lang = NULL; lang_len = 0; } if (lang_key == NULL) lang_key_len = 0; else lang_key_len = png_strlen(lang_key); if (text == NULL) text_len = 0; else text_len = png_strlen(text); /* Compute the compressed data; do it now for the length */ text_len = png_text_compress(png_ptr, text, text_len, compression-2, &comp); /* Make sure we include the compression flag, the compression byte, * and the NULs after the key, lang, and lang_key parts */ png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, (png_uint_32)( 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + key_len + lang_len + lang_key_len + text_len)); /* We leave it to the application to meet PNG-1.0 requirements on the * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. */ png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1)); /* Set the compression flag */ if (compression == PNG_ITXT_COMPRESSION_NONE || \ compression == PNG_TEXT_COMPRESSION_NONE) cbuf[0] = 0; else /* compression == PNG_ITXT_COMPRESSION_zTXt */ cbuf[0] = 1; /* Set the compression method */ cbuf[1] = 0; png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); cbuf[0] = 0; png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), (png_size_t)(lang_len + 1)); png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), (png_size_t)(lang_key_len + 1)); png_write_compressed_data_out(png_ptr, &comp); png_write_chunk_end(png_ptr); png_free(png_ptr, new_key); png_free(png_ptr, new_lang); } #endif #ifdef PNG_WRITE_oFFs_SUPPORTED /* Write the oFFs chunk */ void /* PRIVATE */ png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, int unit_type) { PNG_oFFs; png_byte buf[9]; png_debug(1, "in png_write_oFFs"); if (unit_type >= PNG_OFFSET_LAST) png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); png_save_int_32(buf, x_offset); png_save_int_32(buf + 4, y_offset); buf[8] = (png_byte)unit_type; png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); } #endif #ifdef PNG_WRITE_pCAL_SUPPORTED /* Write the pCAL chunk (described in the PNG extensions document) */ void /* PRIVATE */ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) { PNG_pCAL; png_size_t purpose_len, units_len, total_len; png_uint_32p params_len; png_byte buf[10]; png_charp new_purpose; int i; png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); if (type >= PNG_EQUATION_LAST) png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); png_debug1(3, "pCAL units length = %d", (int)units_len); total_len = purpose_len + units_len + 10; params_len = (png_uint_32p)png_malloc(png_ptr, (png_alloc_size_t)(nparams * png_sizeof(png_uint_32))); /* Find the length of each parameter, making sure we don't count the null terminator for the last parameter. */ for (i = 0; i < nparams; i++) { params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); png_debug2(3, "pCAL parameter %d length = %lu", i, (unsigned long) params_len[i]); total_len += (png_size_t)params_len[i]; } png_debug1(3, "pCAL total length = %d", (int)total_len); png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); png_write_chunk_data(png_ptr, (png_bytep)new_purpose, (png_size_t)purpose_len); png_save_int_32(buf, X0); png_save_int_32(buf + 4, X1); buf[8] = (png_byte)type; buf[9] = (png_byte)nparams; png_write_chunk_data(png_ptr, buf, (png_size_t)10); png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); png_free(png_ptr, new_purpose); for (i = 0; i < nparams; i++) { png_write_chunk_data(png_ptr, (png_bytep)params[i], (png_size_t)params_len[i]); } png_free(png_ptr, params_len); png_write_chunk_end(png_ptr); } #endif #ifdef PNG_WRITE_sCAL_SUPPORTED /* Write the sCAL chunk */ #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) void /* PRIVATE */ png_write_sCAL(png_structp png_ptr, int unit, double width, double height) { PNG_sCAL; char buf[64]; png_size_t total_len; png_debug(1, "in png_write_sCAL"); buf[0] = (char)unit; png_snprintf(buf + 1, 63, "%12.12e", width); total_len = 1 + png_strlen(buf + 1) + 1; png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); total_len += png_strlen(buf + total_len); png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); } #else #ifdef PNG_FIXED_POINT_SUPPORTED void /* PRIVATE */ png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, png_charp height) { PNG_sCAL; png_byte buf[64]; png_size_t wlen, hlen, total_len; png_debug(1, "in png_write_sCAL_s"); wlen = png_strlen(width); hlen = png_strlen(height); total_len = wlen + hlen + 2; if (total_len > 64) { png_warning(png_ptr, "Can't write sCAL (buffer too small)"); return; } buf[0] = (png_byte)unit; png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); } #endif #endif #endif #ifdef PNG_WRITE_pHYs_SUPPORTED /* Write the pHYs chunk */ void /* PRIVATE */ png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, int unit_type) { PNG_pHYs; png_byte buf[9]; png_debug(1, "in png_write_pHYs"); if (unit_type >= PNG_RESOLUTION_LAST) png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); png_save_uint_32(buf, x_pixels_per_unit); png_save_uint_32(buf + 4, y_pixels_per_unit); buf[8] = (png_byte)unit_type; png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); } #endif #ifdef PNG_WRITE_tIME_SUPPORTED /* Write the tIME chunk. Use either png_convert_from_struct_tm() * or png_convert_from_time_t(), or fill in the structure yourself. */ void /* PRIVATE */ png_write_tIME(png_structp png_ptr, png_timep mod_time) { PNG_tIME; png_byte buf[7]; png_debug(1, "in png_write_tIME"); if (mod_time->month > 12 || mod_time->month < 1 || mod_time->day > 31 || mod_time->day < 1 || mod_time->hour > 23 || mod_time->second > 60) { png_warning(png_ptr, "Invalid time specified for tIME chunk"); return; } png_save_uint_16(buf, mod_time->year); buf[2] = mod_time->month; buf[3] = mod_time->day; buf[4] = mod_time->hour; buf[5] = mod_time->minute; buf[6] = mod_time->second; png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); } #endif /* Initializes the row writing capability of libpng */ void /* PRIVATE */ png_write_start_row(png_structp png_ptr) { #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; /* Start of interlace block in the y direction */ int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; /* Offset to next interlace block in the y direction */ int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif png_size_t buf_size; png_debug(1, "in png_write_start_row"); buf_size = (png_size_t)(PNG_ROWBYTES( png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1); /* Set up row buffer */ png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)buf_size); png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; #ifdef PNG_WRITE_FILTER_SUPPORTED /* Set up filtering buffer, if using this filter */ if (png_ptr->do_filter & PNG_FILTER_SUB) { png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(png_ptr->rowbytes + 1)); png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; } /* We only need to keep the previous row if we are using one of these. */ if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) { /* Set up previous row buffer */ png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, (png_alloc_size_t)buf_size); if (png_ptr->do_filter & PNG_FILTER_UP) { png_ptr->up_row = (png_bytep)png_malloc(png_ptr, (png_size_t)(png_ptr->rowbytes + 1)); png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; } if (png_ptr->do_filter & PNG_FILTER_AVG) { png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)(png_ptr->rowbytes + 1)); png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; } if (png_ptr->do_filter & PNG_FILTER_PAETH) { png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, (png_size_t)(png_ptr->rowbytes + 1)); png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; } } #endif /* PNG_WRITE_FILTER_SUPPORTED */ #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* If interlaced, we need to set up width and height of pass */ if (png_ptr->interlaced) { if (!(png_ptr->transformations & PNG_INTERLACE)) { png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - png_pass_ystart[0]) / png_pass_yinc[0]; png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - png_pass_start[0]) / png_pass_inc[0]; } else { png_ptr->num_rows = png_ptr->height; png_ptr->usr_width = png_ptr->width; } } else #endif { png_ptr->num_rows = png_ptr->height; png_ptr->usr_width = png_ptr->width; } png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; png_ptr->zstream.next_out = png_ptr->zbuf; } /* Internal use only. Called when finished processing a row of data. */ void /* PRIVATE */ png_write_finish_row(png_structp png_ptr) { #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; /* Start of interlace block in the y direction */ int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; /* Offset to next interlace block in the y direction */ int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif int ret; png_debug(1, "in png_write_finish_row"); /* Next row */ png_ptr->row_number++; /* See if we are done */ if (png_ptr->row_number < png_ptr->num_rows) return; #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* If interlaced, go to next pass */ if (png_ptr->interlaced) { png_ptr->row_number = 0; if (png_ptr->transformations & PNG_INTERLACE) { png_ptr->pass++; } else { /* Loop until we find a non-zero width or height pass */ do { png_ptr->pass++; if (png_ptr->pass >= 7) break; png_ptr->usr_width = (png_ptr->width + png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; png_ptr->num_rows = (png_ptr->height + png_pass_yinc[png_ptr->pass] - 1 - png_pass_ystart[png_ptr->pass]) / png_pass_yinc[png_ptr->pass]; if (png_ptr->transformations & PNG_INTERLACE) break; } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); } /* Reset the row above the image for the next pass */ if (png_ptr->pass < 7) { if (png_ptr->prev_row != NULL) png_memset(png_ptr->prev_row, 0, (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* png_ptr->usr_bit_depth, png_ptr->width)) + 1); return; } } #endif /* If we get here, we've just written the last row, so we need to flush the compressor */ do { /* Tell the compressor we are done */ ret = deflate(&png_ptr->zstream, Z_FINISH); /* Check for an error */ if (ret == Z_OK) { /* Check to see if we need more room */ if (!(png_ptr->zstream.avail_out)) { png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; } } else if (ret != Z_STREAM_END) { if (png_ptr->zstream.msg != NULL) png_error(png_ptr, png_ptr->zstream.msg); else png_error(png_ptr, "zlib error"); } } while (ret != Z_STREAM_END); /* Write any extra space */ if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) { png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - png_ptr->zstream.avail_out); } deflateReset(&png_ptr->zstream); png_ptr->zstream.data_type = Z_BINARY; } #ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Pick out the correct pixels for the interlace pass. * The basic idea here is to go through the row with a source * pointer and a destination pointer (sp and dp), and copy the * correct pixels for the pass. As the row gets compacted, * sp will always be >= dp, so we should never overwrite anything. * See the default: case for the easiest code to understand. */ void /* PRIVATE */ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) { /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ /* Start of interlace block */ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; /* Offset to next interlace block */ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; png_debug(1, "in png_do_write_interlace"); /* We don't have to do anything on the last pass (6) */ if (pass < 6) { /* Each pixel depth is handled separately */ switch (row_info->pixel_depth) { case 1: { png_bytep sp; png_bytep dp; int shift; int d; int value; png_uint_32 i; png_uint_32 row_width = row_info->width; dp = row; d = 0; shift = 7; for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { sp = row + (png_size_t)(i >> 3); value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; d |= (value << shift); if (shift == 0) { shift = 7; *dp++ = (png_byte)d; d = 0; } else shift--; } if (shift != 7) *dp = (png_byte)d; break; } case 2: { png_bytep sp; png_bytep dp; int shift; int d; int value; png_uint_32 i; png_uint_32 row_width = row_info->width; dp = row; shift = 6; d = 0; for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { sp = row + (png_size_t)(i >> 2); value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; d |= (value << shift); if (shift == 0) { shift = 6; *dp++ = (png_byte)d; d = 0; } else shift -= 2; } if (shift != 6) *dp = (png_byte)d; break; } case 4: { png_bytep sp; png_bytep dp; int shift; int d; int value; png_uint_32 i; png_uint_32 row_width = row_info->width; dp = row; shift = 4; d = 0; for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { sp = row + (png_size_t)(i >> 1); value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; d |= (value << shift); if (shift == 0) { shift = 4; *dp++ = (png_byte)d; d = 0; } else shift -= 4; } if (shift != 4) *dp = (png_byte)d; break; } default: { png_bytep sp; png_bytep dp; png_uint_32 i; png_uint_32 row_width = row_info->width; png_size_t pixel_bytes; /* Start at the beginning */ dp = row; /* Find out how many bytes each pixel takes up */ pixel_bytes = (row_info->pixel_depth >> 3); /* Loop through the row, only looking at the pixels that matter */ for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { /* Find out where the original pixel is */ sp = row + (png_size_t)i * pixel_bytes; /* Move the pixel */ if (dp != sp) png_memcpy(dp, sp, pixel_bytes); /* Next pixel */ dp += pixel_bytes; } break; } } /* Set new row width */ row_info->width = (row_info->width + png_pass_inc[pass] - 1 - png_pass_start[pass]) / png_pass_inc[pass]; row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); } } #endif /* This filters the row, chooses which filter to use, if it has not already * been specified by the application, and then writes the row out with the * chosen filter. */ #define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) #define PNG_HISHIFT 10 #define PNG_LOMASK ((png_uint_32)0xffffL) #define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) void /* PRIVATE */ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) { png_bytep best_row; #ifdef PNG_WRITE_FILTER_SUPPORTED png_bytep prev_row, row_buf; png_uint_32 mins, bpp; png_byte filter_to_do = png_ptr->do_filter; png_uint_32 row_bytes = row_info->rowbytes; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED int num_p_filters = (int)png_ptr->num_prev_filters; #endif png_debug(1, "in png_write_find_filter"); #ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) { /* These will never be selected so we need not test them. */ filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); } #endif /* Find out how many bytes offset each pixel is */ bpp = (row_info->pixel_depth + 7) >> 3; prev_row = png_ptr->prev_row; #endif best_row = png_ptr->row_buf; #ifdef PNG_WRITE_FILTER_SUPPORTED row_buf = best_row; mins = PNG_MAXSUM; /* The prediction method we use is to find which method provides the * smallest value when summing the absolute values of the distances * from zero, using anything >= 128 as negative numbers. This is known * as the "minimum sum of absolute differences" heuristic. Other * heuristics are the "weighted minimum sum of absolute differences" * (experimental and can in theory improve compression), and the "zlib * predictive" method (not implemented yet), which does test compressions * of lines using different filter methods, and then chooses the * (series of) filter(s) that give minimum compressed data size (VERY * computationally expensive). * * GRR 980525: consider also * (1) minimum sum of absolute differences from running average (i.e., * keep running sum of non-absolute differences & count of bytes) * [track dispersion, too? restart average if dispersion too large?] * (1b) minimum sum of absolute differences from sliding average, probably * with window size <= deflate window (usually 32K) * (2) minimum sum of squared differences from zero or running average * (i.e., ~ root-mean-square approach) */ /* We don't need to test the 'no filter' case if this is the only filter * that has been chosen, as it doesn't actually do anything to the data. */ if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE) { png_bytep rp; png_uint_32 sum = 0; png_uint_32 i; int v; for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) { v = *rp; sum += (v < 128) ? v : 256 - v; } #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { png_uint_32 sumhi, sumlo; int j; sumlo = sum & PNG_LOMASK; sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ /* Reduce the sum if we match any of the previous rows */ for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; sumhi = (sumhi * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } /* Factor in the cost of this filter (this is here for completeness, * but it makes no sense to have a "cost" for the NONE filter, as * it has the minimum possible computational cost - none). */ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> PNG_COST_SHIFT; sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; else sum = (sumhi << PNG_HISHIFT) + sumlo; } #endif mins = sum; } /* Sub filter */ if (filter_to_do == PNG_FILTER_SUB) /* It's the only filter so no testing is needed */ { png_bytep rp, lp, dp; png_uint_32 i; for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; i++, rp++, dp++) { *dp = *rp; } for (lp = row_buf + 1; i < row_bytes; i++, rp++, lp++, dp++) { *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); } best_row = png_ptr->sub_row; } else if (filter_to_do & PNG_FILTER_SUB) { png_bytep rp, dp, lp; png_uint_32 sum = 0, lmins = mins; png_uint_32 i; int v; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* We temporarily increase the "minimum sum" by the factor we * would reduce the sum of this filter, so that we can do the * early exit comparison without scaling the sum each time. */ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 lmhi, lmlo; lmlo = lmins & PNG_LOMASK; lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> PNG_COST_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; i++, rp++, dp++) { v = *dp = *rp; sum += (v < 128) ? v : 256 - v; } for (lp = row_buf + 1; i < row_bytes; i++, rp++, lp++, dp++) { v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); sum += (v < 128) ? v : 256 - v; if (sum > lmins) /* We are already worse, don't continue. */ break; } #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 sumhi, sumlo; sumlo = sum & PNG_LOMASK; sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) { sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> PNG_COST_SHIFT; sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; else sum = (sumhi << PNG_HISHIFT) + sumlo; } #endif if (sum < mins) { mins = sum; best_row = png_ptr->sub_row; } } /* Up filter */ if (filter_to_do == PNG_FILTER_UP) { png_bytep rp, dp, pp; png_uint_32 i; for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, pp = prev_row + 1; i < row_bytes; i++, rp++, pp++, dp++) { *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); } best_row = png_ptr->up_row; } else if (filter_to_do & PNG_FILTER_UP) { png_bytep rp, dp, pp; png_uint_32 sum = 0, lmins = mins; png_uint_32 i; int v; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 lmhi, lmlo; lmlo = lmins & PNG_LOMASK; lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> PNG_COST_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, pp = prev_row + 1; i < row_bytes; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); sum += (v < 128) ? v : 256 - v; if (sum > lmins) /* We are already worse, don't continue. */ break; } #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 sumhi, sumlo; sumlo = sum & PNG_LOMASK; sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; sumhi = (sumhi * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> PNG_COST_SHIFT; sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; else sum = (sumhi << PNG_HISHIFT) + sumlo; } #endif if (sum < mins) { mins = sum; best_row = png_ptr->up_row; } } /* Avg filter */ if (filter_to_do == PNG_FILTER_AVG) { png_bytep rp, dp, pp, lp; png_uint_32 i; for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, pp = prev_row + 1; i < bpp; i++) { *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); } for (lp = row_buf + 1; i < row_bytes; i++) { *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); } best_row = png_ptr->avg_row; } else if (filter_to_do & PNG_FILTER_AVG) { png_bytep rp, dp, pp, lp; png_uint_32 sum = 0, lmins = mins; png_uint_32 i; int v; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 lmhi, lmlo; lmlo = lmins & PNG_LOMASK; lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> PNG_COST_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, pp = prev_row + 1; i < bpp; i++) { v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); sum += (v < 128) ? v : 256 - v; } for (lp = row_buf + 1; i < row_bytes; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); sum += (v < 128) ? v : 256 - v; if (sum > lmins) /* We are already worse, don't continue. */ break; } #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 sumhi, sumlo; sumlo = sum & PNG_LOMASK; sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; sumhi = (sumhi * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> PNG_COST_SHIFT; sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; else sum = (sumhi << PNG_HISHIFT) + sumlo; } #endif if (sum < mins) { mins = sum; best_row = png_ptr->avg_row; } } /* Paeth filter */ if (filter_to_do == PNG_FILTER_PAETH) { png_bytep rp, dp, pp, cp, lp; png_uint_32 i; for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, pp = prev_row + 1; i < bpp; i++) { *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); } for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) { int a, b, c, pa, pb, pc, p; b = *pp++; c = *cp++; a = *lp++; p = b - c; pc = a - c; #ifdef PNG_USE_ABS pa = abs(p); pb = abs(pc); pc = abs(p + pc); #else pa = p < 0 ? -p : p; pb = pc < 0 ? -pc : pc; pc = (p + pc) < 0 ? -(p + pc) : p + pc; #endif p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); } best_row = png_ptr->paeth_row; } else if (filter_to_do & PNG_FILTER_PAETH) { png_bytep rp, dp, pp, cp, lp; png_uint_32 sum = 0, lmins = mins; png_uint_32 i; int v; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 lmhi, lmlo; lmlo = lmins & PNG_LOMASK; lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> PNG_COST_SHIFT; lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, pp = prev_row + 1; i < bpp; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); sum += (v < 128) ? v : 256 - v; } for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) { int a, b, c, pa, pb, pc, p; b = *pp++; c = *cp++; a = *lp++; #ifndef PNG_SLOW_PAETH p = b - c; pc = a - c; #ifdef PNG_USE_ABS pa = abs(p); pb = abs(pc); pc = abs(p + pc); #else pa = p < 0 ? -p : p; pb = pc < 0 ? -pc : pc; pc = (p + pc) < 0 ? -(p + pc) : p + pc; #endif p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; #else /* PNG_SLOW_PAETH */ p = a + b - c; pa = abs(p - a); pb = abs(p - b); pc = abs(p - c); if (pa <= pb && pa <= pc) p = a; else if (pb <= pc) p = b; else p = c; #endif /* PNG_SLOW_PAETH */ v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); sum += (v < 128) ? v : 256 - v; if (sum > lmins) /* We are already worse, don't continue. */ break; } #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; png_uint_32 sumhi, sumlo; sumlo = sum & PNG_LOMASK; sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; for (j = 0; j < num_p_filters; j++) { if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; sumhi = (sumhi * png_ptr->filter_weights[j]) >> PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> PNG_COST_SHIFT; sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; else sum = (sumhi << PNG_HISHIFT) + sumlo; } #endif if (sum < mins) { best_row = png_ptr->paeth_row; } } #endif /* PNG_WRITE_FILTER_SUPPORTED */ /* Do the actual writing of the filtered row data from the chosen filter. */ png_write_filtered_row(png_ptr, best_row); #ifdef PNG_WRITE_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* Save the type of filter we picked this time for future calculations */ if (png_ptr->num_prev_filters > 0) { int j; for (j = 1; j < num_p_filters; j++) { png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; } png_ptr->prev_filters[j] = best_row[0]; } #endif #endif /* PNG_WRITE_FILTER_SUPPORTED */ } /* Do the actual writing of a previously filtered row. */ void /* PRIVATE */ png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) { png_debug(1, "in png_write_filtered_row"); png_debug1(2, "filter = %d", filtered_row[0]); /* Set up the zlib input buffer */ png_ptr->zstream.next_in = filtered_row; png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; /* Repeat until we have compressed all the data */ do { int ret; /* Return of zlib */ /* Compress the data */ ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); /* Check for compression errors */ if (ret != Z_OK) { if (png_ptr->zstream.msg != NULL) png_error(png_ptr, png_ptr->zstream.msg); else png_error(png_ptr, "zlib error"); } /* See if it is time to write another IDAT */ if (!(png_ptr->zstream.avail_out)) { /* Write the IDAT and reset the zlib output buffer */ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); png_ptr->zstream.next_out = png_ptr->zbuf; png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; } /* Repeat until all data has been compressed */ } while (png_ptr->zstream.avail_in); /* Swap the current and previous rows */ if (png_ptr->prev_row != NULL) { png_bytep tptr; tptr = png_ptr->prev_row; png_ptr->prev_row = png_ptr->row_buf; png_ptr->row_buf = tptr; } /* Finish row - updates counters and flushes zlib if last row */ png_write_finish_row(png_ptr); #ifdef PNG_WRITE_FLUSH_SUPPORTED png_ptr->flush_rows++; if (png_ptr->flush_dist > 0 && png_ptr->flush_rows >= png_ptr->flush_dist) { png_write_flush(png_ptr); } #endif } #endif /* PNG_WRITE_SUPPORTED */ Indigo-indigo-1.2.3/third_party/oci/000077500000000000000000000000001271037650300173045ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/oci/CMakeLists.txt000066400000000000000000000017721271037650300220530ustar00rootroot00000000000000# Include this file only once PROJECT(OCI) if (USE_SYSTEM_OCI) find_package(OCI) if (NOT ${OCI_FOUND}) MESSAGE(FATAL_ERROR "Cannot find system OCI library") endif() endif() if (NOT OCI_FOUND) message(STATUS "Using local OCI library") set(OCI_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) if(MSVC) if(SUBSYSTEM_NAME MATCHES "x86") set(OCI_LIBRARY_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib_generated/lib32) set(OCI_LIBRARY ${OCI_LIBRARY_DIRS}/oci32.lib) elseif(SUBSYSTEM_NAME MATCHES "x64") set(OCI_LIBRARY_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib_generated/lib64) set(OCI_LIBRARY ${OCI_LIBRARY_DIRS}/oci64.lib) else() message(FATAL_ERROR "Unknown SUBSYSTEM_NAME ${SUBSYSTEM_NAME}") endif() elseif(APPLE) set(OCI_LIBRARY_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib64) set(OCI_LIBRARY ${OCI_LIBRARY_DIRS}/liborasdk.dylib) elseif(UNIX) endif() set(OCI_FOUND YES) endif() Indigo-indigo-1.2.3/third_party/oci/lib_generated/000077500000000000000000000000001271037650300220705ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/oci/lib_generated/oci.def000066400000000000000000000010521271037650300233200ustar00rootroot00000000000000EXPORTS OCIEnvCreate OCILogon OCILobOpen OCILobWrite OCILobDisableBuffering OCILobEnableBuffering OCILobGetLength OCILobClose OCILobFreeTemporary OCILobTrim OCILobCreateTemporary OCIHandleAlloc OCIRawAllocSize OCIRawResize OCIRawPtr OCIRawAssignBytes OCIDescriptorFree OCIDescriptorAlloc OCIBindByName OCIAttrGet OCIStmtFetch2 OCIStmtExecute OCIDefineByPos OCIStmtPrepare ociepgoe OCILobFlushBuffer OCILobRead OCIHandleFree ociepmsg OCIErrorGet OCINumberFromReal ociepacm OCINumberFromInt OCINumberToReal OCINumberToInt OCIStringAssignText OCICollAppend Indigo-indigo-1.2.3/third_party/oci/lib_generated/readme.txt000066400000000000000000000006571271037650300240760ustar00rootroot00000000000000Bingo uses only a subset of a methods from OCI library. Oracle oci32.lib and oci64.lib are wrappers for oci.dll dynamic library. To avoid usage of Oracle binary code, such libraries were generated manually from a list of used functions. To generate library files run the following commands in the Visual Studio Command Prompt: lib /def:oci.def /out:lib32/oci32.lib /machine:x86 lib /def:oci.def /out:lib64/oci64.lib /machine:x64 Indigo-indigo-1.2.3/third_party/tinyxml/000077500000000000000000000000001271037650300202365ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/tinyxml/CMakeLists.txt000066400000000000000000000014461271037650300230030ustar00rootroot00000000000000PROJECT(TinyXML) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../common/cmake/) if(USE_LOCAL_TINYXML) set(TINYXML_FOUND "NO") message(STATUS "Using local TinyXML") else() find_package(TINYXML) endif() if (NOT TINYXML_FOUND) file (GLOB TinyXML_src src/*.c* src/*.def) file (GLOB TinyXML_headers include/*.h*) set(TinyXML_HEADERS_DIR ${TinyXML_SOURCE_DIR}/include) include_directories(${TinyXML_HEADERS_DIR}) add_library(tinyxml STATIC ${TinyXML_src} ${TinyXML_headers}) set_target_properties(tinyxml PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") set_property(TARGET tinyxml PROPERTY FOLDER "third_party") if (NOT NO_STATIC) pack_static(tinyxml) endif() else() set(TinyXML_HEADERS_DIR ${TINYXML_INCLUDE_DIRS}) endif() Indigo-indigo-1.2.3/third_party/tinyxml/include/000077500000000000000000000000001271037650300216615ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/tinyxml/include/tinystr.h000066400000000000000000000211171271037650300235500ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original file by Yves Berquin. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. * * - completely rewritten. compact, clean, and fast implementation. * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) * - fixed reserve() to work as per specification. * - fixed buggy compares operator==(), operator<(), and operator>() * - fixed operator+=() to take a const ref argument, following spec. * - added "copy" constructor with length, and most compare operators. * - added swap(), clear(), size(), capacity(), operator+(). */ #ifndef TIXML_USE_STL #ifndef TIXML_STRING_INCLUDED #define TIXML_STRING_INCLUDED #include #include /* The support for explicit isn't that universal, and it isn't really required - it is used to check that the TiXmlString class isn't incorrectly used. Be nice to old compilers and macro it here: */ #if defined(_MSC_VER) && (_MSC_VER >= 1200 ) // Microsoft visual studio, version 6 and higher. #define TIXML_EXPLICIT explicit #elif defined(__GNUC__) && (__GNUC__ >= 3 ) // GCC version 3 and higher.s #define TIXML_EXPLICIT explicit #else #define TIXML_EXPLICIT #endif /* TiXmlString is an emulation of a subset of the std::string template. Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. Only the member functions relevant to the TinyXML project have been implemented. The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase a string and there's no more room, we allocate a buffer twice as big as we need. */ class TiXmlString { public : // The size type used typedef size_t size_type; // Error value for find primitive static const size_type npos; // = -1; // TiXmlString empty constructor TiXmlString () : rep_(&nullrep_) { } // TiXmlString copy constructor TiXmlString ( const TiXmlString & copy) : rep_(0) { init(copy.length()); memcpy(start(), copy.data(), length()); } // TiXmlString constructor, based on a string TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) { init( static_cast( strlen(copy) )); memcpy(start(), copy, length()); } // TiXmlString constructor, based on a string TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) { init(len); memcpy(start(), str, len); } // TiXmlString destructor ~TiXmlString () { quit(); } // = operator TiXmlString& operator = (const char * copy) { return assign( copy, (size_type)strlen(copy)); } // = operator TiXmlString& operator = (const TiXmlString & copy) { return assign(copy.start(), copy.length()); } // += operator. Maps to append TiXmlString& operator += (const char * suffix) { return append(suffix, static_cast( strlen(suffix) )); } // += operator. Maps to append TiXmlString& operator += (char single) { return append(&single, 1); } // += operator. Maps to append TiXmlString& operator += (const TiXmlString & suffix) { return append(suffix.data(), suffix.length()); } // Convert a TiXmlString into a null-terminated char * const char * c_str () const { return rep_->str; } // Convert a TiXmlString into a char * (need not be null terminated). const char * data () const { return rep_->str; } // Return the length of a TiXmlString size_type length () const { return rep_->size; } // Alias for length() size_type size () const { return rep_->size; } // Checks if a TiXmlString is empty bool empty () const { return rep_->size == 0; } // Return capacity of string size_type capacity () const { return rep_->capacity; } // single char extraction const char& at (size_type index) const { assert( index < length() ); return rep_->str[ index ]; } // [] operator char& operator [] (size_type index) const { assert( index < length() ); return rep_->str[ index ]; } // find a char in a string. Return TiXmlString::npos if not found size_type find (char lookup) const { return find(lookup, 0); } // find a char in a string from an offset. Return TiXmlString::npos if not found size_type find (char tofind, size_type offset) const { if (offset >= length()) return npos; for (const char* p = c_str() + offset; *p != '\0'; ++p) { if (*p == tofind) return static_cast< size_type >( p - c_str() ); } return npos; } void clear () { //Lee: //The original was just too strange, though correct: // TiXmlString().swap(*this); //Instead use the quit & re-init: quit(); init(0,0); } /* Function to reserve a big amount of data when we know we'll need it. Be aware that this function DOES NOT clear the content of the TiXmlString if any exists. */ void reserve (size_type cap); TiXmlString& assign (const char* str, size_type len); TiXmlString& append (const char* str, size_type len); void swap (TiXmlString& other) { Rep* r = rep_; rep_ = other.rep_; other.rep_ = r; } private: void init(size_type sz) { init(sz, sz); } void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } char* start() const { return rep_->str; } char* finish() const { return rep_->str + rep_->size; } struct Rep { size_type size, capacity; char str[1]; }; void init(size_type sz, size_type cap) { if (cap) { // Lee: the original form: // rep_ = static_cast(operator new(sizeof(Rep) + cap)); // doesn't work in some cases of new being overloaded. Switching // to the normal allocation, although use an 'int' for systems // that are overly picky about structure alignment. const size_type bytesNeeded = sizeof(Rep) + cap; const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); rep_ = reinterpret_cast( new int[ intsNeeded ] ); rep_->str[ rep_->size = sz ] = '\0'; rep_->capacity = cap; } else { rep_ = &nullrep_; } } void quit() { if (rep_ != &nullrep_) { // The rep_ is really an array of ints. (see the allocator, above). // Cast it back before delete, so the compiler won't incorrectly call destructors. delete [] ( reinterpret_cast( rep_ ) ); } } Rep * rep_; static Rep nullrep_; } ; inline bool operator == (const TiXmlString & a, const TiXmlString & b) { return ( a.length() == b.length() ) // optimization on some platforms && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare } inline bool operator < (const TiXmlString & a, const TiXmlString & b) { return strcmp(a.c_str(), b.c_str()) < 0; } inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); TiXmlString operator + (const TiXmlString & a, const char* b); TiXmlString operator + (const char* a, const TiXmlString & b); /* TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. Only the operators that we need for TinyXML have been developped. */ class TiXmlOutStream : public TiXmlString { public : // TiXmlOutStream << operator. TiXmlOutStream & operator << (const TiXmlString & in) { *this += in; return *this; } // TiXmlOutStream << operator. TiXmlOutStream & operator << (const char * in) { *this += in; return *this; } } ; #endif // TIXML_STRING_INCLUDED #endif // TIXML_USE_STL Indigo-indigo-1.2.3/third_party/tinyxml/include/tinyxml.h000066400000000000000000001754741271037650300235600ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYXML_INCLUDED #define TINYXML_INCLUDED #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4530 ) #pragma warning( disable : 4786 ) #endif #include #include #include #include #include // Help out windows: #if defined( _DEBUG ) && !defined( DEBUG ) #define DEBUG #endif #ifdef TIXML_USE_STL #include #include #include #define TIXML_STRING std::string #else #include "tinystr.h" #define TIXML_STRING TiXmlString #endif // Deprecated library function hell. Compilers want to use the // new safe versions. This probably doesn't fully address the problem, // but it gets closer. There are too many compilers for me to fully // test. If you get compilation troubles, undefine TIXML_SAFE #define TIXML_SAFE #ifdef TIXML_SAFE #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) // Microsoft visual studio, version 2005 and higher. #define TIXML_SNPRINTF _snprintf_s #define TIXML_SSCANF sscanf_s #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) // Microsoft visual studio, version 6 and higher. //#pragma message( "Using _sn* functions." ) #define TIXML_SNPRINTF _snprintf #define TIXML_SSCANF sscanf #elif defined(__GNUC__) && (__GNUC__ >= 3 ) // GCC version 3 and higher.s //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #else #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #endif #endif class TiXmlDocument; class TiXmlElement; class TiXmlComment; class TiXmlUnknown; class TiXmlAttribute; class TiXmlText; class TiXmlDeclaration; class TiXmlParsingData; const int TIXML_MAJOR_VERSION = 2; const int TIXML_MINOR_VERSION = 6; const int TIXML_PATCH_VERSION = 1; /* Internal structure for tracking location of items in the XML file. */ struct TiXmlCursor { TiXmlCursor() { Clear(); } void Clear() { row = col = -1; } int row; // 0 based. int col; // 0 based. }; /** Implements the interface to the "Visitor pattern" (see the Accept() method.) If you call the Accept() method, it requires being passed a TiXmlVisitor class to handle callbacks. For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves are simply called with Visit(). If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its sibilings will be Visited. All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you. Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. You should never change the document from a callback. @sa TiXmlNode::Accept() */ class TiXmlVisitor { public: virtual ~TiXmlVisitor() {} /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } /// Visit a declaration virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } /// Visit a text node virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } /// Visit a comment node virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } /// Visit an unknow node virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } }; // Only used by Attribute::Query functions enum { TIXML_SUCCESS, TIXML_NO_ATTRIBUTE, TIXML_WRONG_TYPE }; // Used by the parsing routines. enum TiXmlEncoding { TIXML_ENCODING_UNKNOWN, TIXML_ENCODING_UTF8, TIXML_ENCODING_LEGACY }; const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; /** TiXmlBase is a base class for every class in TinyXml. It does little except to establish that TinyXml classes can be printed and provide some utility functions. In XML, the document and elements can contain other elements and other types of nodes. @verbatim A Document can contain: Element (container or leaf) Comment (leaf) Unknown (leaf) Declaration( leaf ) An Element can contain: Element (container or leaf) Text (leaf) Attributes (not on tree) Comment (leaf) Unknown (leaf) A Decleration contains: Attributes (not on tree) @endverbatim */ class TiXmlBase { friend class TiXmlNode; friend class TiXmlElement; friend class TiXmlDocument; public: TiXmlBase() : userData(0) {} virtual ~TiXmlBase() {} /** All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.) Either or both cfile and str can be null. This is a formatted print, and will insert tabs and newlines. (For an unformatted stream, use the << operator.) */ virtual void Print( FILE* cfile, int depth ) const = 0; /** The world does not agree on whether white space should be kept or not. In order to make everyone happy, these global, static functions are provided to set whether or not TinyXml will condense all white space into a single space or not. The default is to condense. Note changing this value is not thread safe. */ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } /// Return the current white space setting. static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } /** Return the position, in the original source file, of this node or attribute. The row and column are 1-based. (That is the first row and first column is 1,1). If the returns values are 0 or less, then the parser does not have a row and column value. Generally, the row and column value will be set when the TiXmlDocument::Load(), TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set when the DOM was created from operator>>. The values reflect the initial load. Once the DOM is modified programmatically (by adding or changing nodes and attributes) the new values will NOT update to reflect changes in the document. There is a minor performance cost to computing the row and column. Computation can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. @sa TiXmlDocument::SetTabSize() */ int Row() const { return location.row + 1; } int Column() const { return location.col + 1; } ///< See Row() void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. // Table that returs, for a given lead byte, the total number of bytes // in the UTF-8 sequence. static const int utf8ByteTable[256]; virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, or they will be transformed into entities! */ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); enum { TIXML_NO_ERROR = 0, TIXML_ERROR, TIXML_ERROR_OPENING_FILE, TIXML_ERROR_PARSING_ELEMENT, TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, TIXML_ERROR_READING_ELEMENT_VALUE, TIXML_ERROR_READING_ATTRIBUTES, TIXML_ERROR_PARSING_EMPTY, TIXML_ERROR_READING_END_TAG, TIXML_ERROR_PARSING_UNKNOWN, TIXML_ERROR_PARSING_COMMENT, TIXML_ERROR_PARSING_DECLARATION, TIXML_ERROR_DOCUMENT_EMPTY, TIXML_ERROR_EMBEDDED_NULL, TIXML_ERROR_PARSING_CDATA, TIXML_ERROR_DOCUMENT_TOP_ONLY, TIXML_ERROR_STRING_COUNT }; protected: static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); inline static bool IsWhiteSpace( char c ) { return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); } inline static bool IsWhiteSpace( int c ) { if ( c < 256 ) return IsWhiteSpace( (char) c ); return false; // Again, only truly correct for English/Latin...but usually works. } #ifdef TIXML_USE_STL static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); #endif /* Reads an XML name into the string provided. Returns a pointer just past the last character of the name, or 0 if the function has an error. */ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); /* Reads text. Returns a pointer past the given end tag. Wickedly complex options, but it keeps the (sensitive) code in one place. */ static const char* ReadText( const char* in, // where to start TIXML_STRING* text, // the string read bool ignoreWhiteSpace, // whether to keep the white space const char* endTag, // what ends this text bool ignoreCase, // whether to ignore case in the end tag TiXmlEncoding encoding ); // the current encoding // If an entity has been found, transform it into a character. static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); // Get a character, while interpreting entities. // The length can be from 0 to 4 bytes. inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) { assert( p ); if ( encoding == TIXML_ENCODING_UTF8 ) { *length = utf8ByteTable[ *((const unsigned char*)p) ]; assert( *length >= 0 && *length < 5 ); } else { *length = 1; } if ( *length == 1 ) { if ( *p == '&' ) return GetEntity( p, _value, length, encoding ); *_value = *p; return p+1; } else if ( *length ) { //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), // and the null terminator isn't needed for( int i=0; p[i] && i<*length; ++i ) { _value[i] = p[i]; } return p + (*length); } else { // Not valid text. return 0; } } // Return true if the next characters in the stream are any of the endTag sequences. // Ignore case only works for english, and should only be relied on when comparing // to English words: StringEqual( p, "version", true ) is fine. static bool StringEqual( const char* p, const char* endTag, bool ignoreCase, TiXmlEncoding encoding ); static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; TiXmlCursor location; /// Field containing a generic user pointer void* userData; // None of these methods are reliable for any language except English. // Good for approximation, not great for accuracy. static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); inline static int ToLower( int v, TiXmlEncoding encoding ) { if ( encoding == TIXML_ENCODING_UTF8 ) { if ( v < 128 ) return tolower( v ); return v; } else { return tolower( v ); } } static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); private: TiXmlBase( const TiXmlBase& ); // not implemented. void operator=( const TiXmlBase& base ); // not allowed. struct Entity { const char* str; unsigned int strLength; char chr; }; enum { NUM_ENTITY = 5, MAX_ENTITY_LENGTH = 6 }; static Entity entity[ NUM_ENTITY ]; static bool condenseWhiteSpace; }; /** The parent class for everything in the Document Object Model. (Except for attributes). Nodes have siblings, a parent, and children. A node can be in a document, or stand on its own. The type of a TiXmlNode can be queried, and it can be cast to its more defined type. */ class TiXmlNode : public TiXmlBase { friend class TiXmlDocument; friend class TiXmlElement; public: #ifdef TIXML_USE_STL /** An input stream operator, for every class. Tolerant of newlines and formatting, but doesn't expect them. */ friend std::istream& operator >> (std::istream& in, TiXmlNode& base); /** An output stream operator, for every class. Note that this outputs without any newlines or formatting, as opposed to Print(), which includes tabs and new lines. The operator<< and operator>> are not completely symmetric. Writing a node to a stream is very well defined. You'll get a nice stream of output, without any extra whitespace or newlines. But reading is not as well defined. (As it always is.) If you create a TiXmlElement (for example) and read that from an input stream, the text needs to define an element or junk will result. This is true of all input streams, but it's worth keeping in mind. A TiXmlDocument will read nodes until it reads a root element, and all the children of that root element. */ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); /// Appends the XML node or attribute to a std::string. friend std::string& operator<< (std::string& out, const TiXmlNode& base ); #endif /** The types of XML nodes supported by TinyXml. (All the unsupported types are picked up by UNKNOWN.) */ enum NodeType { TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, TINYXML_UNKNOWN, TINYXML_TEXT, TINYXML_DECLARATION, TINYXML_TYPECOUNT }; virtual ~TiXmlNode(); /** The meaning of 'value' changes for the specific type of TiXmlNode. @verbatim Document: filename of the xml file Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim The subclasses will wrap this function. */ const char *Value() const { return value.c_str (); } #ifdef TIXML_USE_STL /** Return Value() as a std::string. If you only use STL, this is more efficient than calling Value(). Only available in STL mode. */ const std::string& ValueStr() const { return value; } #endif const TIXML_STRING& ValueTStr() const { return value; } /** Changes the value of the node. Defined as: @verbatim Document: filename of the xml file Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim */ void SetValue(const char * _value) { value = _value;} #ifdef TIXML_USE_STL /// STL std::string form. void SetValue( const std::string& _value ) { value = _value; } #endif /// Delete all the children of this node. Does not affect 'this'. void Clear(); /// One step up the DOM. TiXmlNode* Parent() { return parent; } const TiXmlNode* Parent() const { return parent; } const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. TiXmlNode* FirstChild() { return firstChild; } const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. /// The first child of this node with the matching 'value'. Will be null if none found. TiXmlNode* FirstChild( const char * _value ) { // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) // call the method, cast the return back to non-const. return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); } const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. TiXmlNode* LastChild() { return lastChild; } const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. TiXmlNode* LastChild( const char * _value ) { return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); } #ifdef TIXML_USE_STL const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. #endif /** An alternate way to walk the children of a node. One way to iterate over nodes is: @verbatim for( child = parent->FirstChild(); child; child = child->NextSibling() ) @endverbatim IterateChildren does the same thing with the syntax: @verbatim child = 0; while( child = parent->IterateChildren( child ) ) @endverbatim IterateChildren takes the previous child as input and finds the next one. If the previous child is null, it returns the first. IterateChildren will return null when done. */ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; TiXmlNode* IterateChildren( const TiXmlNode* previous ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); } /// This flavor of IterateChildren searches for children with a particular 'value' const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); } #ifdef TIXML_USE_STL const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. #endif /** Add a new node related to this. Adds a child past the LastChild. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); /** Add a new node related to this. Adds a child past the LastChild. NOTE: the node to be added is passed by pointer, and will be henceforth owned (and deleted) by tinyXml. This method is efficient and avoids an extra copy, but should be used with care as it uses a different memory model than the other insert functions. @sa InsertEndChild */ TiXmlNode* LinkEndChild( TiXmlNode* addThis ); /** Add a new node related to this. Adds a child before the specified child. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); /** Add a new node related to this. Adds a child after the specified child. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); /** Replace a child of this node. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); /// Delete a child of this node. bool RemoveChild( TiXmlNode* removeThis ); /// Navigate to a sibling node. const TiXmlNode* PreviousSibling() const { return prev; } TiXmlNode* PreviousSibling() { return prev; } /// Navigate to a sibling node. const TiXmlNode* PreviousSibling( const char * ) const; TiXmlNode* PreviousSibling( const char *_prev ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); } #ifdef TIXML_USE_STL const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. #endif /// Navigate to a sibling node. const TiXmlNode* NextSibling() const { return next; } TiXmlNode* NextSibling() { return next; } /// Navigate to a sibling node with the given 'value'. const TiXmlNode* NextSibling( const char * ) const; TiXmlNode* NextSibling( const char* _next ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); } /** Convenience function to get through elements. Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. */ const TiXmlElement* NextSiblingElement() const; TiXmlElement* NextSiblingElement() { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); } /** Convenience function to get through elements. Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. */ const TiXmlElement* NextSiblingElement( const char * ) const; TiXmlElement* NextSiblingElement( const char *_next ) { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); } #ifdef TIXML_USE_STL const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. #endif /// Convenience function to get through elements. const TiXmlElement* FirstChildElement() const; TiXmlElement* FirstChildElement() { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); } /// Convenience function to get through elements. const TiXmlElement* FirstChildElement( const char * _value ) const; TiXmlElement* FirstChildElement( const char * _value ) { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); } #ifdef TIXML_USE_STL const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. #endif /** Query the type (as an enumerated value, above) of this node. The possible types are: DOCUMENT, ELEMENT, COMMENT, UNKNOWN, TEXT, and DECLARATION. */ int Type() const { return type; } /** Return a pointer to the Document this node lives in. Returns null if not in a document. */ const TiXmlDocument* GetDocument() const; TiXmlDocument* GetDocument() { return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); } /// Returns true if this node has no children. bool NoChildren() const { return !firstChild; } virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. /** Create an exact duplicate of this node and return it. The memory must be deleted by the caller. */ virtual TiXmlNode* Clone() const = 0; /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the XML tree will be conditionally visited and the host will be called back via the TiXmlVisitor interface. This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML is unchanged by using this interface versus any other.) The interface has been based on ideas from: - http://www.saxproject.org/ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern Which are both good references for "visiting". An example of using Accept(): @verbatim TiXmlPrinter printer; tinyxmlDoc.Accept( &printer ); const char* xmlcstr = printer.CStr(); @endverbatim */ virtual bool Accept( TiXmlVisitor* visitor ) const = 0; protected: TiXmlNode( NodeType _type ); // Copy to the allocated object. Shared functionality between Clone, Copy constructor, // and the assignment operator. void CopyTo( TiXmlNode* target ) const; #ifdef TIXML_USE_STL // The real work of the input operator. virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; #endif // Figure out what is at *p, and parse it. Returns null if it is not an xml node. TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); TiXmlNode* parent; NodeType type; TiXmlNode* firstChild; TiXmlNode* lastChild; TIXML_STRING value; TiXmlNode* prev; TiXmlNode* next; private: TiXmlNode( const TiXmlNode& ); // not implemented. void operator=( const TiXmlNode& base ); // not allowed. }; /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @note The attributes are not TiXmlNodes, since they are not part of the tinyXML document object model. There are other suggested ways to look at this problem. */ class TiXmlAttribute : public TiXmlBase { friend class TiXmlAttributeSet; public: /// Construct an empty attribute. TiXmlAttribute() : TiXmlBase() { document = 0; prev = next = 0; } #ifdef TIXML_USE_STL /// std::string constructor. TiXmlAttribute( const std::string& _name, const std::string& _value ) { name = _name; value = _value; document = 0; prev = next = 0; } #endif /// Construct an attribute with a name and value. TiXmlAttribute( const char * _name, const char * _value ) { name = _name; value = _value; document = 0; prev = next = 0; } const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. #ifdef TIXML_USE_STL const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. #endif int IntValue() const; ///< Return the value of this attribute, converted to an integer. double DoubleValue() const; ///< Return the value of this attribute, converted to a double. // Get the tinyxml string representation const TIXML_STRING& NameTStr() const { return name; } /** QueryIntValue examines the value string. It is an alternative to the IntValue() method with richer error checking. If the value is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE. A specialized but useful call. Note that for success it returns 0, which is the opposite of almost all other TinyXml calls. */ int QueryIntValue( int* _value ) const; /// QueryDoubleValue examines the value string. See QueryIntValue(). int QueryDoubleValue( double* _value ) const; void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. void SetValue( const char* _value ) { value = _value; } ///< Set the value. void SetIntValue( int _value ); ///< Set the value from an integer. void SetDoubleValue( double _value ); ///< Set the value from a double. #ifdef TIXML_USE_STL /// STL std::string form. void SetName( const std::string& _name ) { name = _name; } /// STL std::string form. void SetValue( const std::string& _value ) { value = _value; } #endif /// Get the next sibling attribute in the DOM. Returns null at end. const TiXmlAttribute* Next() const; TiXmlAttribute* Next() { return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); } /// Get the previous sibling attribute in the DOM. Returns null at beginning. const TiXmlAttribute* Previous() const; TiXmlAttribute* Previous() { return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); } bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } /* Attribute parsing starts: first letter of the name returns: the next char after the value end quote */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); // Prints this Attribute to a FILE stream. virtual void Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; // [internal use] // Set the document pointer so the attribute can report errors. void SetDocument( TiXmlDocument* doc ) { document = doc; } private: TiXmlAttribute( const TiXmlAttribute& ); // not implemented. void operator=( const TiXmlAttribute& base ); // not allowed. TiXmlDocument* document; // A pointer back to a document, for error reporting. TIXML_STRING name; TIXML_STRING value; TiXmlAttribute* prev; TiXmlAttribute* next; }; /* A class used to manage a group of attributes. It is only used internally, both by the ELEMENT and the DECLARATION. The set can be changed transparent to the Element and Declaration classes that use it, but NOT transparent to the Attribute which has to implement a next() and previous() method. Which makes it a bit problematic and prevents the use of STL. This version is implemented with circular lists because: - I like circular lists - it demonstrates some independence from the (typical) doubly linked list. */ class TiXmlAttributeSet { public: TiXmlAttributeSet(); ~TiXmlAttributeSet(); void Add( TiXmlAttribute* attribute ); void Remove( TiXmlAttribute* attribute ); const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } TiXmlAttribute* Find( const char* _name ) const; TiXmlAttribute* FindOrCreate( const char* _name ); # ifdef TIXML_USE_STL TiXmlAttribute* Find( const std::string& _name ) const; TiXmlAttribute* FindOrCreate( const std::string& _name ); # endif private: //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), //*ME: this class must be also use a hidden/disabled copy-constructor !!! TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) TiXmlAttribute sentinel; }; /** The element is a container class. It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. */ class TiXmlElement : public TiXmlNode { public: /// Construct an element. TiXmlElement (const char * in_value); #ifdef TIXML_USE_STL /// std::string constructor. TiXmlElement( const std::string& _value ); #endif TiXmlElement( const TiXmlElement& ); void operator=( const TiXmlElement& base ); virtual ~TiXmlElement(); /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. */ const char* Attribute( const char* name ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. If the attribute exists and can be converted to an integer, the integer value will be put in the return 'i', if 'i' is non-null. */ const char* Attribute( const char* name, int* i ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. If the attribute exists and can be converted to an double, the double value will be put in the return 'd', if 'd' is non-null. */ const char* Attribute( const char* name, double* d ) const; /** QueryIntAttribute examines the attribute - it is an alternative to the Attribute() method with richer error checking. If the attribute is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE. If the attribute does not exist, then TIXML_NO_ATTRIBUTE is returned. */ int QueryIntAttribute( const char* name, int* _value ) const; /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). int QueryDoubleAttribute( const char* name, double* _value ) const; /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). int QueryFloatAttribute( const char* name, float* _value ) const { double d; int result = QueryDoubleAttribute( name, &d ); if ( result == TIXML_SUCCESS ) { *_value = (float)d; } return result; } #ifdef TIXML_USE_STL /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). int QueryStringAttribute( const char* name, std::string* _value ) const { const char* cstr = Attribute( name ); if ( cstr ) { *_value = std::string( cstr ); return TIXML_SUCCESS; } return TIXML_NO_ATTRIBUTE; } /** Template form of the attribute query which will try to read the attribute into the specified type. Very easy, very powerful, but be careful to make sure to call this with the correct type. NOTE: This method doesn't work correctly for 'string' types that contain spaces. @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE */ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; std::stringstream sstream( node->ValueStr() ); sstream >> *outValue; if ( !sstream.fail() ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int QueryValueAttribute( const std::string& name, std::string* outValue ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; *outValue = node->ValueStr(); return TIXML_SUCCESS; } #endif /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetAttribute( const char* name, const char * _value ); #ifdef TIXML_USE_STL const std::string* Attribute( const std::string& name ) const; const std::string* Attribute( const std::string& name, int* i ) const; const std::string* Attribute( const std::string& name, double* d ) const; int QueryIntAttribute( const std::string& name, int* _value ) const; int QueryDoubleAttribute( const std::string& name, double* _value ) const; /// STL std::string form. void SetAttribute( const std::string& name, const std::string& _value ); ///< STL std::string form. void SetAttribute( const std::string& name, int _value ); ///< STL std::string form. void SetDoubleAttribute( const std::string& name, double value ); #endif /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetAttribute( const char * name, int value ); /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetDoubleAttribute( const char * name, double value ); /** Deletes an attribute with the given name. */ void RemoveAttribute( const char * name ); #ifdef TIXML_USE_STL void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. #endif const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } /** Convenience function for easy access to the text inside an element. Although easy and concise, GetText() is limited compared to getting the TiXmlText child and accessing it directly. If the first child of 'this' is a TiXmlText, the GetText() returns the character string of the Text node, else null is returned. This is a convenient method for getting the text of simple contained text: @verbatim This is text const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then the value of str would be null. The first child node isn't a text node, it is another element. From this XML: @verbatim This is text @endverbatim GetText() will return "This is ". WARNING: GetText() accesses a child node - don't become confused with the similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are safe type casts on the referenced node. */ const char* GetText() const; /// Creates a new Element and returns it - the returned element is a copy. virtual TiXmlNode* Clone() const; // Print the Element to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /* Attribtue parsing starts: next char past '<' returns: next char past '>' */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlElement* target ) const; void ClearThis(); // like clear, but initializes 'this' object as well // Used to be public [internal use] #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif /* [internal use] Reads the "value" of the element -- another element, or text. This should terminate with the current end tag. */ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); private: TiXmlAttributeSet attributeSet; }; /** An XML comment. */ class TiXmlComment : public TiXmlNode { public: /// Constructs an empty comment. TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} /// Construct a comment from text. TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { SetValue( _value ); } TiXmlComment( const TiXmlComment& ); void operator=( const TiXmlComment& base ); virtual ~TiXmlComment() {} /// Returns a copy of this Comment. virtual TiXmlNode* Clone() const; // Write this Comment to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /* Attribtue parsing starts: at the ! of the !-- returns: next char past '>' */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlComment* target ) const; // used to be public #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif // virtual void StreamOut( TIXML_OSTREAM * out ) const; private: }; /** XML text. A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCDATA() and query it with CDATA(). */ class TiXmlText : public TiXmlNode { friend class TiXmlElement; public: /** Constructor for text element. By default, it is treated as normal, encoded text. If you want it be output as a CDATA text element, set the parameter _cdata to 'true' */ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) { SetValue( initValue ); cdata = false; } virtual ~TiXmlText() {} #ifdef TIXML_USE_STL /// Constructor. TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) { SetValue( initValue ); cdata = false; } #endif TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } void operator=( const TiXmlText& base ) { base.CopyTo( this ); } // Write this text object to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /// Queries whether this represents text using a CDATA section. bool CDATA() const { return cdata; } /// Turns on or off a CDATA representation of text. void SetCDATA( bool _cdata ) { cdata = _cdata; } virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected : /// [internal use] Creates a new Element and returns it. virtual TiXmlNode* Clone() const; void CopyTo( TiXmlText* target ) const; bool Blank() const; // returns true if all white space and new lines // [internal use] #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: bool cdata; // true if this should be input and output as a CDATA style text element }; /** In correct XML the declaration is the first entry in the file. @verbatim @endverbatim TinyXml will happily read or write files without a declaration, however. There are 3 possible attributes to the declaration: version, encoding, and standalone. Note: In this version of the code, the attributes are handled as special cases, not generic attributes, simply because there can only be at most 3 and they are always the same. */ class TiXmlDeclaration : public TiXmlNode { public: /// Construct an empty declaration. TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} #ifdef TIXML_USE_STL /// Constructor. TiXmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ); #endif /// Construct. TiXmlDeclaration( const char* _version, const char* _encoding, const char* _standalone ); TiXmlDeclaration( const TiXmlDeclaration& copy ); void operator=( const TiXmlDeclaration& copy ); virtual ~TiXmlDeclaration() {} /// Version. Will return an empty string if none was found. const char *Version() const { return version.c_str (); } /// Encoding. Will return an empty string if none was found. const char *Encoding() const { return encoding.c_str (); } /// Is this a standalone document? const char *Standalone() const { return standalone.c_str (); } /// Creates a copy of this Declaration and returns it. virtual TiXmlNode* Clone() const; // Print this declaration to a FILE stream. virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; virtual void Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlDeclaration* target ) const; // used to be public #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: TIXML_STRING version; TIXML_STRING encoding; TIXML_STRING standalone; }; /** Any tag that tinyXml doesn't recognize is saved as an unknown. It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved. DTD tags get thrown into TiXmlUnknowns. */ class TiXmlUnknown : public TiXmlNode { public: TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} virtual ~TiXmlUnknown() {} TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } /// Creates a copy of this Unknown and returns it. virtual TiXmlNode* Clone() const; // Print this Unknown to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected: void CopyTo( TiXmlUnknown* target ) const; #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: }; /** Always the top level node. A document binds together all the XML pieces. It can be saved, loaded, and printed to the screen. The 'value' of a document node is the xml file name. */ class TiXmlDocument : public TiXmlNode { public: /// Create an empty document, that has no name. TiXmlDocument(); /// Create a document with a name. The name of the document is also the filename of the xml. TiXmlDocument( const char * documentName ); #ifdef TIXML_USE_STL /// Constructor. TiXmlDocument( const std::string& documentName ); #endif TiXmlDocument( const TiXmlDocument& copy ); void operator=( const TiXmlDocument& copy ); virtual ~TiXmlDocument() {} /** Load a file using the current document value. Returns true if successful. Will delete any existing document data before loading. */ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the current document value. Returns true if successful. bool SaveFile() const; /// Load a file using the given filename. Returns true if successful. bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the given filename. Returns true if successful. bool SaveFile( const char * filename ) const; /** Load a file using the given FILE*. Returns true if successful. Note that this method doesn't stream - the entire object pointed at by the FILE* will be interpreted as an XML file. TinyXML doesn't stream in XML from the current file location. Streaming may be added in the future. */ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the given FILE*. Returns true if successful. bool SaveFile( FILE* ) const; #ifdef TIXML_USE_STL bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. { return LoadFile( filename.c_str(), encoding ); } bool SaveFile( const std::string& filename ) const ///< STL std::string version. { return SaveFile( filename.c_str() ); } #endif /** Parse the given null terminated block of xml data. Passing in an encoding to this method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml to use that encoding, regardless of what TinyXml might otherwise try to detect. */ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /** Get the root element -- the only top level element -- of the document. In well formed XML, there should only be one. TinyXml is tolerant of multiple elements at the document level. */ const TiXmlElement* RootElement() const { return FirstChildElement(); } TiXmlElement* RootElement() { return FirstChildElement(); } /** If an error occurs, Error will be set to true. Also, - The ErrorId() will contain the integer identifier of the error (not generally useful) - The ErrorDesc() method will return the name of the error. (very useful) - The ErrorRow() and ErrorCol() will return the location of the error (if known) */ bool Error() const { return error; } /// Contains a textual (english) description of the error if one occurs. const char * ErrorDesc() const { return errorDesc.c_str (); } /** Generally, you probably want the error string ( ErrorDesc() ). But if you prefer the ErrorId, this function will fetch it. */ int ErrorId() const { return errorId; } /** Returns the location (if known) of the error. The first column is column 1, and the first row is row 1. A value of 0 means the row and column wasn't applicable (memory errors, for example, have no row/column) or the parser lost the error. (An error in the error reporting, in that case.) @sa SetTabSize, Row, Column */ int ErrorRow() const { return errorLocation.row+1; } int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) to report the correct values for row and column. It does not change the output or input in any way. By calling this method, with a tab size greater than 0, the row and column of each node and attribute is stored when the file is loaded. Very useful for tracking the DOM back in to the source file. The tab size is required for calculating the location of nodes. If not set, the default of 4 is used. The tabsize is set per document. Setting the tabsize to 0 disables row/column tracking. Note that row and column tracking is not supported when using operator>>. The tab size needs to be enabled before the parse or load. Correct usage: @verbatim TiXmlDocument doc; doc.SetTabSize( 8 ); doc.Load( "myfile.xml" ); @endverbatim @sa Row, Column */ void SetTabSize( int _tabsize ) { tabsize = _tabsize; } int TabSize() const { return tabsize; } /** If you have handled the error, it can be reset with this call. The error state is automatically cleared if you Parse a new XML block. */ void ClearError() { error = false; errorId = 0; errorDesc = ""; errorLocation.row = errorLocation.col = 0; //errorLocation.last = 0; } /** Write the document to standard out using formatted printing ("pretty print"). */ void Print() const { Print( stdout, 0 ); } /* Write the document to a string using formatted printing ("pretty print"). This will allocate a character array (new char[]) and return it as a pointer. The calling code pust call delete[] on the return char* to avoid a memory leak. */ //char* PrintToMemory() const; /// Print this Document to a FILE stream. virtual void Print( FILE* cfile, int depth = 0 ) const; // [internal use] void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected : // [internal use] virtual TiXmlNode* Clone() const; #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: void CopyTo( TiXmlDocument* target ) const; bool error; int errorId; TIXML_STRING errorDesc; int tabsize; TiXmlCursor errorLocation; bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. }; /** A TiXmlHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml DOM structure. It is a separate utility class. Take an example: @verbatim @endverbatim Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like: @verbatim TiXmlElement* root = document.FirstChildElement( "Document" ); if ( root ) { TiXmlElement* element = root->FirstChildElement( "Element" ); if ( element ) { TiXmlElement* child = element->FirstChildElement( "Child" ); if ( child ) { TiXmlElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity of such code. A TiXmlHandle checks for null pointers so it is perfectly safe and correct to use: @verbatim TiXmlHandle docHandle( &document ); TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); if ( child2 ) { // do something useful @endverbatim Which is MUCH more concise and useful. It is also safe to copy handles - internally they are nothing more than node pointers. @verbatim TiXmlHandle handleCopy = handle; @endverbatim What they should not be used for is iteration: @verbatim int i=0; while ( true ) { TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); if ( !child ) break; // do something ++i; } @endverbatim It seems reasonable, but it is in fact two embedded while loops. The Child method is a linear walk to find the element, so this code would iterate much more than it needs to. Instead, prefer: @verbatim TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); for( child; child; child=child->NextSiblingElement() ) { // do something } @endverbatim */ class TiXmlHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } /// Copy constructor TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } /// Return a handle to the first child node. TiXmlHandle FirstChild() const; /// Return a handle to the first child node with the given name. TiXmlHandle FirstChild( const char * value ) const; /// Return a handle to the first child element. TiXmlHandle FirstChildElement() const; /// Return a handle to the first child element with the given name. TiXmlHandle FirstChildElement( const char * value ) const; /** Return a handle to the "index" child with the given name. The first child is 0, the second 1, etc. */ TiXmlHandle Child( const char* value, int index ) const; /** Return a handle to the "index" child. The first child is 0, the second 1, etc. */ TiXmlHandle Child( int index ) const; /** Return a handle to the "index" child element with the given name. The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. */ TiXmlHandle ChildElement( const char* value, int index ) const; /** Return a handle to the "index" child element. The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. */ TiXmlHandle ChildElement( int index ) const; #ifdef TIXML_USE_STL TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } #endif /** Return the handle as a TiXmlNode. This may return null. */ TiXmlNode* ToNode() const { return node; } /** Return the handle as a TiXmlElement. This may return null. */ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } /** Return the handle as a TiXmlText. This may return null. */ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } /** Return the handle as a TiXmlUnknown. This may return null. */ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } /** @deprecated use ToNode. Return the handle as a TiXmlNode. This may return null. */ TiXmlNode* Node() const { return ToNode(); } /** @deprecated use ToElement. Return the handle as a TiXmlElement. This may return null. */ TiXmlElement* Element() const { return ToElement(); } /** @deprecated use ToText() Return the handle as a TiXmlText. This may return null. */ TiXmlText* Text() const { return ToText(); } /** @deprecated use ToUnknown() Return the handle as a TiXmlUnknown. This may return null. */ TiXmlUnknown* Unknown() const { return ToUnknown(); } private: TiXmlNode* node; }; /** Print to memory functionality. The TiXmlPrinter is useful when you need to: -# Print to memory (especially in non-STL mode) -# Control formatting (line endings, etc.) When constructed, the TiXmlPrinter is in its default "pretty printing" mode. Before calling Accept() you can call methods to control the printing of the XML document. After TiXmlNode::Accept() is called, the printed document can be accessed via the CStr(), Str(), and Size() methods. TiXmlPrinter uses the Visitor API. @verbatim TiXmlPrinter printer; printer.SetIndent( "\t" ); doc.Accept( &printer ); fprintf( stdout, "%s", printer.CStr() ); @endverbatim */ class TiXmlPrinter : public TiXmlVisitor { public: TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), buffer(), indent( " " ), lineBreak( "\n" ) {} virtual bool VisitEnter( const TiXmlDocument& doc ); virtual bool VisitExit( const TiXmlDocument& doc ); virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); virtual bool VisitExit( const TiXmlElement& element ); virtual bool Visit( const TiXmlDeclaration& declaration ); virtual bool Visit( const TiXmlText& text ); virtual bool Visit( const TiXmlComment& comment ); virtual bool Visit( const TiXmlUnknown& unknown ); /** Set the indent characters for printing. By default 4 spaces but tab (\t) is also useful, or null/empty string for no indentation. */ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } /// Query the indention string. const char* Indent() { return indent.c_str(); } /** Set the line breaking string. By default set to newline (\n). Some operating systems prefer other characters, or can be set to the null/empty string for no indenation. */ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } /// Query the current line breaking string. const char* LineBreak() { return lineBreak.c_str(); } /** Switch over to "stream printing" which is the most dense formatting without linebreaks. Common when the XML is needed for network transmission. */ void SetStreamPrinting() { indent = ""; lineBreak = ""; } /// Return the result. const char* CStr() { return buffer.c_str(); } /// Return the length of the result string. size_t Size() { return buffer.size(); } #ifdef TIXML_USE_STL /// Return the result. const std::string& Str() { return buffer; } #endif private: void DoIndent() { for( int i=0; i TinyXML TinyXML is a simple, small, C++ XML parser that can be easily integrated into other programs.

What it does.

In brief, TinyXML parses an XML document, and builds from that a Document Object Model (DOM) that can be read, modified, and saved. XML stands for "eXtensible Markup Language." It allows you to create your own document markups. Where HTML does a very good job of marking documents for browsers, XML allows you to define any kind of document markup, for example a document that describes a "to do" list for an organizer application. XML is a very structured and convenient format. All those random file formats created to store application data can all be replaced with XML. One parser for everything. The best place for the complete, correct, and quite frankly hard to read spec is at http://www.w3.org/TR/2004/REC-xml-20040204/. An intro to XML (that I really like) can be found at http://skew.org/xml/tutorial. There are different ways to access and interact with XML data. TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed into a C++ objects that can be browsed and manipulated, and then written to disk or another output stream. You can also construct an XML document from scratch with C++ objects and write this to disk or another output stream. TinyXML is designed to be easy and fast to learn. It is two headers and four cpp files. Simply add these to your project and off you go. There is an example file - xmltest.cpp - to get you started. TinyXML is released under the ZLib license, so you can use it in open source or commercial code. The details of the license are at the top of every source file. TinyXML attempts to be a flexible parser, but with truly correct and compliant XML output. TinyXML should compile on any reasonably C++ compliant system. It does not rely on exceptions or RTTI. It can be compiled with or without STL support. TinyXML fully supports the UTF-8 encoding, and the first 64k character entities.

What it doesn't do.

TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs (eXtensible Stylesheet Language.) There are other parsers out there (check out www.sourceforge.org, search for XML) that are much more fully featured. But they are also much bigger, take longer to set up in your project, have a higher learning curve, and often have a more restrictive license. If you are working with browsers or have more complete XML needs, TinyXML is not the parser for you. The following DTD syntax will not parse at this time in TinyXML: @verbatim ]> @endverbatim because TinyXML sees this as a !DOCTYPE node with an illegally embedded !ELEMENT node. This may be addressed in the future.

Tutorials.

For the impatient, here is a tutorial to get you going. A great way to get started, but it is worth your time to read this (very short) manual completely. - @subpage tutorial0

Code Status.

TinyXML is mature, tested code. It is very stable. If you find bugs, please file a bug report on the sourceforge web site (www.sourceforge.net/projects/tinyxml). We'll get them straightened out as soon as possible. There are some areas of improvement; please check sourceforge if you are interested in working on TinyXML.

Related Projects

TinyXML projects you may find useful! (Descriptions provided by the projects.)
  • TinyXPath (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint XPath syntax decoder, written in C++.
  • TinyXML++ (http://code.google.com/p/ticpp/). TinyXML++ is a completely new interface to TinyXML that uses MANY of the C++ strengths. Templates, exceptions, and much better error handling.

Features

Using STL

TinyXML can be compiled to use or not use STL. When using STL, TinyXML uses the std::string class, and fully supports std::istream, std::ostream, operator<<, and operator>>. Many API methods have both 'const char*' and 'const std::string&' forms. When STL support is compiled out, no STL files are included whatsoever. All the string classes are implemented by TinyXML itself. API methods all use the 'const char*' form for input. Use the compile time #define: TIXML_USE_STL to compile one version or the other. This can be passed by the compiler, or set as the first line of "tinyxml.h". Note: If compiling the test code in Linux, setting the environment variable TINYXML_USE_STL=YES/NO will control STL compilation. In the Windows project file, STL and non STL targets are provided. In your project, It's probably easiest to add the line "#define TIXML_USE_STL" as the first line of tinyxml.h.

UTF-8

TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML also supports "legacy mode" - the encoding used before UTF-8 support and probably best described as "extended ascii". Normally, TinyXML will try to detect the correct encoding and use it. However, by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML can be forced to always use one encoding. TinyXML will assume Legacy Mode until one of the following occurs:
  1. If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) begin the file or data stream, TinyXML will read it as UTF-8.
  2. If the declaration tag is read, and it has an encoding="UTF-8", then TinyXML will read it as UTF-8.
  3. If the declaration tag is read, and it has no encoding specified, then TinyXML will read it as UTF-8.
  4. If the declaration tag is read, and it has an encoding="something else", then TinyXML will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's not clear what that mode does exactly, but old content should keep working.
  5. Until one of the above criteria is met, TinyXML runs in Legacy Mode.
What happens if the encoding is incorrectly set or detected? TinyXML will try to read and pass through text seen as improperly encoded. You may get some strange results or mangled characters. You may want to force TinyXML to the correct mode. You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may force it to TIXML_ENCODING_UTF8 with the same technique. For English users, using English XML, UTF-8 is the same as low-ASCII. You don't need to be aware of UTF-8 or change your code in any way. You can think of UTF-8 as a "superset" of ASCII. UTF-8 is not a double byte format - but it is a standard encoding of Unicode! TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time. It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding of unicode. This is a source of confusion. For "high-ascii" languages - everything not English, pretty much - TinyXML can handle all languages, at the same time, as long as the XML is encoded in UTF-8. That can be a little tricky, older programs and operating systems tend to use the "default" or "traditional" code page. Many apps (and almost all modern ones) can output UTF-8, but older or stubborn (or just broken) ones still output text in the default code page. For example, Japanese systems traditionally use SHIFT-JIS encoding. Text encoded as SHIFT-JIS can not be read by TinyXML. A good text editor can import SHIFT-JIS and then save as UTF-8. The Skew.org link does a great job covering the encoding issue. The test file "utf8test.xml" is an XML containing English, Spanish, Russian, and Simplified Chinese. (Hopefully they are translated correctly). The file "utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that if you don't have the correct fonts (Simplified Chinese or Russian) on your system, you won't see output that matches the GIF file even if you can parse it correctly. Also note that (at least on my Windows machine) console output is in a Western code page, so that Print() or printf() cannot correctly display the file. This is not a bug in TinyXML - just an OS issue. No data is lost or destroyed by TinyXML. The console just doesn't render UTF-8.

Entities

TinyXML recognizes the pre-defined "character entities", meaning special characters. Namely: @verbatim & & < < > > " " ' ' @endverbatim These are recognized when the XML document is read, and translated to there UTF-8 equivalents. For instance, text with the XML of: @verbatim Far & Away @endverbatim will have the Value() of "Far & Away" when queried from the TiXmlText object, and will be written back to the XML stream/file as an ampersand. Older versions of TinyXML "preserved" character entities, but the newer versions will translate them into characters. Additionally, any character can be specified by its Unicode code point: The syntax " " or " " are both to the non-breaking space characher.

Printing

TinyXML can print output in several different ways that all have strengths and limitations. - Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout. - "Pretty prints", but you don't have control over printing options. - The output is streamed directly to the FILE object, so there is no memory overhead in the TinyXML code. - used by Print() and SaveFile() - operator<<. Output to a c++ stream. - Integrates with standart C++ iostreams. - Outputs in "network printing" mode without line breaks. Good for network transmission and moving XML between C++ objects, but hard for a human to read. - TiXmlPrinter. Output to a std::string or memory buffer. - API is less concise - Future printing options will be put here. - Printing may change slightly in future versions as it is refined and expanded.

Streams

With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well as C (FILE*) streams. There are some differences that you may need to be aware of. C style output: - based on FILE* - the Print() and SaveFile() methods Generates formatted output, with plenty of white space, intended to be as human-readable as possible. They are very fast, and tolerant of ill formed XML documents. For example, an XML document that contains 2 root elements and 2 declarations, will still print. C style input: - based on FILE* - the Parse() and LoadFile() methods A fast, tolerant read. Use whenever you don't need the C++ streams. C++ style output: - based on std::ostream - operator<< Generates condensed output, intended for network transmission rather than readability. Depending on your system's implementation of the ostream class, these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: a document should contain the correct one root element. Additional root level elements will not be streamed out. C++ style input: - based on std::istream - operator>> Reads XML from a stream, making it useful for network transmission. The tricky part is knowing when the XML document is complete, since there will almost certainly be other data in the stream. TinyXML will assume the XML data is complete after it reads the root element. Put another way, documents that are ill-constructed with more than one root element will not read correctly. Also note that operator>> is somewhat slower than Parse, due to both implementation of the STL and limitations of TinyXML.

White space

The world simply does not agree on whether white space should be kept, or condensed. For example, pretend the '_' is a space, and look at "Hello____world". HTML, and at least some XML parsers, will interpret this as "Hello_world". They condense white space. Some XML parsers do not, and will leave it as "Hello____world". (Remember to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become Hello___world. It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. The default is to condense white space. If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) before making any calls to Parse XML data, and I don't recommend changing it after it has been set.

Handles

Where browsing an XML document in a robust way, it is important to check for null returns from method calls. An error safe implementation can generate a lot of code like: @verbatim TiXmlElement* root = document.FirstChildElement( "Document" ); if ( root ) { TiXmlElement* element = root->FirstChildElement( "Element" ); if ( element ) { TiXmlElement* child = element->FirstChildElement( "Child" ); if ( child ) { TiXmlElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim Handles have been introduced to clean this up. Using the TiXmlHandle class, the previous code reduces to: @verbatim TiXmlHandle docHandle( &document ); TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); if ( child2 ) { // do something useful @endverbatim Which is much easier to deal with. See TiXmlHandle for more information.

Row and Column tracking

Being able to track nodes and attributes back to their origin location in source files can be very important for some applications. Additionally, knowing where parsing errors occured in the original source can be very time saving. TinyXML can tracks the row and column origin of all nodes and attributes in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return the origin of the node in the source text. The correct tabs can be configured in TiXmlDocument::SetTabSize().

Using and Installing

To Compile and Run xmltest: A Linux Makefile and a Windows Visual C++ .dsw file is provided. Simply compile and run. It will write the file demotest.xml to your disk and generate output on the screen. It also tests walking the DOM by printing out the number of nodes found using different techniques. The Linux makefile is very generic and runs on many systems - it is currently tested on mingw and MacOSX. You do not need to run 'make depend'. The dependecies have been hard coded.

Windows project file for VC6

  • tinyxml: tinyxml library, non-STL
  • tinyxmlSTL: tinyxml library, STL
  • tinyXmlTest: test app, non-STL
  • tinyXmlTestSTL: test app, STL

Makefile

At the top of the makefile you can set: PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in the makefile. In the tinyxml directory, type "make clean" then "make". The executable file 'xmltest' will be created.

To Use in an Application:

Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your project or make file. That's it! It should compile on any reasonably compliant C++ system. You do not need to enable exceptions or RTTI for TinyXML.

How TinyXML works.

An example is probably the best way to go. Take: @verbatim Go to the Toy store! Do bills @endverbatim Its not much of a To Do list, but it will do. To read this file (say "demo.xml") you would create a document, and parse it in: @verbatim TiXmlDocument doc( "demo.xml" ); doc.LoadFile(); @endverbatim And its ready to go. Now lets look at some lines and how they relate to the DOM. @verbatim @endverbatim The first line is a declaration, and gets turned into the TiXmlDeclaration class. It will be the first child of the document node. This is the only directive/special tag parsed by TinyXML. Generally directive tags are stored in TiXmlUnknown so the commands wont be lost when it is saved back to disk. @verbatim @endverbatim A comment. Will become a TiXmlComment object. @verbatim @endverbatim The "ToDo" tag defines a TiXmlElement object. This one does not have any attributes, but does contain 2 other elements. @verbatim @endverbatim Creates another TiXmlElement which is a child of the "ToDo" element. This element has 1 attribute, with the name "priority" and the value "1". @verbatim Go to the @endverbatim A TiXmlText. This is a leaf node and cannot contain other nodes. It is a child of the "Item" TiXmlElement. @verbatim @endverbatim Another TiXmlElement, this one a child of the "Item" element. Etc. Looking at the entire object tree, you end up with: @verbatim TiXmlDocument "demo.xml" TiXmlDeclaration "version='1.0'" "standalone=no" TiXmlComment " Our to do list data" TiXmlElement "ToDo" TiXmlElement "Item" Attribtutes: priority = 1 TiXmlText "Go to the " TiXmlElement "bold" TiXmlText "Toy store!" TiXmlElement "Item" Attributes: priority=2 TiXmlText "Do bills" @endverbatim

Documentation

The documentation is build with Doxygen, using the 'dox' configuration file.

License

TinyXML is released under the zlib license: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.

References

The World Wide Web Consortium is the definitive standard body for XML, and their web pages contain huge amounts of information. The definitive spec: http://www.w3.org/TR/2004/REC-xml-20040204/ I also recommend "XML Pocket Reference" by Robert Eckstein and published by OReilly...the book that got the whole thing started.

Contributors, Contacts, and a Brief History

Thanks very much to everyone who sends suggestions, bugs, ideas, and encouragement. It all helps, and makes this project fun. A special thanks to the contributors on the web pages that keep it lively. So many people have sent in bugs and ideas, that rather than list here we try to give credit due in the "changes.txt" file. TinyXML was originally written by Lee Thomason. (Often the "I" still in the documentation.) Lee reviews changes and releases new versions, with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community. We appreciate your suggestions, and would love to know if you use TinyXML. Hopefully you will enjoy it and find it useful. Please post questions, comments, file bugs, or contact us at: www.sourceforge.net/projects/tinyxml Lee Thomason, Yves Berquin, Andrew Ellerton */ Indigo-indigo-1.2.3/third_party/tinyxml/src/000077500000000000000000000000001271037650300210255ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/tinyxml/src/tinystr.cpp000066400000000000000000000050521271037650300232470ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original file by Yves Berquin. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. */ #ifndef TIXML_USE_STL #include "tinystr.h" // Error value for find primitive const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); // Null rep. TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; void TiXmlString::reserve (size_type cap) { if (cap > capacity()) { TiXmlString tmp; tmp.init(length(), cap); memcpy(tmp.start(), data(), length()); swap(tmp); } } TiXmlString& TiXmlString::assign(const char* str, size_type len) { size_type cap = capacity(); if (len > cap || cap > 3*(len + 8)) { TiXmlString tmp; tmp.init(len); memcpy(tmp.start(), str, len); swap(tmp); } else { memmove(start(), str, len); set_size(len); } return *this; } TiXmlString& TiXmlString::append(const char* str, size_type len) { size_type newsize = length() + len; if (newsize > capacity()) { reserve (newsize + capacity()); } memmove(finish(), str, len); set_size(newsize); return *this; } TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) { TiXmlString tmp; tmp.reserve(a.length() + b.length()); tmp += a; tmp += b; return tmp; } TiXmlString operator + (const TiXmlString & a, const char* b) { TiXmlString tmp; TiXmlString::size_type b_len = static_cast( strlen(b) ); tmp.reserve(a.length() + b_len); tmp += a; tmp.append(b, b_len); return tmp; } TiXmlString operator + (const char* a, const TiXmlString & b) { TiXmlString tmp; TiXmlString::size_type a_len = static_cast( strlen(a) ); tmp.reserve(a_len + b.length()); tmp.append(a, a_len); tmp += b; return tmp; } #endif // TIXML_USE_STL Indigo-indigo-1.2.3/third_party/tinyxml/src/tinyxml.cpp000066400000000000000000001071231271037650300232410ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #ifdef TIXML_USE_STL #include #include #endif #include "tinyxml.h" FILE* TiXmlFOpen( const char* filename, const char* mode ); bool TiXmlBase::condenseWhiteSpace = true; // Microsoft compiler security FILE* TiXmlFOpen( const char* filename, const char* mode ) { #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) FILE* fp = 0; errno_t err = fopen_s( &fp, filename, mode ); if ( !err && fp ) return fp; return 0; #else return fopen( filename, mode ); #endif } void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) { int i=0; while( i<(int)str.length() ) { unsigned char c = (unsigned char) str[i]; if ( c == '&' && i < ( (int)str.length() - 2 ) && str[i+1] == '#' && str[i+2] == 'x' ) { // Hexadecimal character reference. // Pass through unchanged. // © -- copyright symbol, for example. // // The -1 is a bug fix from Rob Laveaux. It keeps // an overflow from happening if there is no ';'. // There are actually 2 ways to exit this loop - // while fails (error case) and break (semicolon found). // However, there is no mechanism (currently) for // this function to return an error. while ( i<(int)str.length()-1 ) { outString->append( str.c_str() + i, 1 ); ++i; if ( str[i] == ';' ) break; } } else if ( c == '&' ) { outString->append( entity[0].str, entity[0].strLength ); ++i; } else if ( c == '<' ) { outString->append( entity[1].str, entity[1].strLength ); ++i; } else if ( c == '>' ) { outString->append( entity[2].str, entity[2].strLength ); ++i; } else if ( c == '\"' ) { outString->append( entity[3].str, entity[3].strLength ); ++i; } else if ( c == '\'' ) { outString->append( entity[4].str, entity[4].strLength ); ++i; } else if ( c < 32 ) { // Easy pass at non-alpha/numeric/symbol // Below 32 is symbolic. char buf[ 32 ]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); #else sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); #endif //*ME: warning C4267: convert 'size_t' to 'int' //*ME: Int-Cast to make compiler happy ... outString->append( buf, (int)strlen( buf ) ); ++i; } else { //char realc = (char) c; //outString->append( &realc, 1 ); *outString += (char) c; // somewhat more efficient function call. ++i; } } } TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() { parent = 0; type = _type; firstChild = 0; lastChild = 0; prev = 0; next = 0; } TiXmlNode::~TiXmlNode() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } } void TiXmlNode::CopyTo( TiXmlNode* target ) const { target->SetValue (value.c_str() ); target->userData = userData; target->location = location; } void TiXmlNode::Clear() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } firstChild = 0; lastChild = 0; } TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) { assert( node->parent == 0 || node->parent == this ); assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) { delete node; if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } node->parent = this; node->prev = lastChild; node->next = 0; if ( lastChild ) lastChild->next = node; else firstChild = node; // it was an empty list. lastChild = node; return node; } TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) { if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; return LinkEndChild( node ); } TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) { if ( !beforeThis || beforeThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->next = beforeThis; node->prev = beforeThis->prev; if ( beforeThis->prev ) { beforeThis->prev->next = node; } else { assert( firstChild == beforeThis ); firstChild = node; } beforeThis->prev = node; return node; } TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) { if ( !afterThis || afterThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->prev = afterThis; node->next = afterThis->next; if ( afterThis->next ) { afterThis->next->prev = node; } else { assert( lastChild == afterThis ); lastChild = node; } afterThis->next = node; return node; } TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) { if ( !replaceThis ) return 0; if ( replaceThis->parent != this ) return 0; if ( withThis.ToDocument() ) { // A document can never be a child. Thanks to Noam. TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = withThis.Clone(); if ( !node ) return 0; node->next = replaceThis->next; node->prev = replaceThis->prev; if ( replaceThis->next ) replaceThis->next->prev = node; else lastChild = node; if ( replaceThis->prev ) replaceThis->prev->next = node; else firstChild = node; delete replaceThis; node->parent = this; return node; } bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) { if ( !removeThis ) { return false; } if ( removeThis->parent != this ) { assert( 0 ); return false; } if ( removeThis->next ) removeThis->next->prev = removeThis->prev; else lastChild = removeThis->prev; if ( removeThis->prev ) removeThis->prev->next = removeThis->next; else firstChild = removeThis->next; delete removeThis; return true; } const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const { const TiXmlNode* node; for ( node = firstChild; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const { const TiXmlNode* node; for ( node = lastChild; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild(); } else { assert( previous->parent == this ); return previous->NextSibling(); } } const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild( val ); } else { assert( previous->parent == this ); return previous->NextSibling( val ); } } const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const { const TiXmlNode* node; for ( node = next; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const { const TiXmlNode* node; for ( node = prev; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } void TiXmlElement::RemoveAttribute( const char * name ) { #ifdef TIXML_USE_STL TIXML_STRING str( name ); TiXmlAttribute* node = attributeSet.Find( str ); #else TiXmlAttribute* node = attributeSet.Find( name ); #endif if ( node ) { attributeSet.Remove( node ); delete node; } } const TiXmlElement* TiXmlNode::FirstChildElement() const { const TiXmlNode* node; for ( node = FirstChild(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const { const TiXmlNode* node; for ( node = FirstChild( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement() const { const TiXmlNode* node; for ( node = NextSibling(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const { const TiXmlNode* node; for ( node = NextSibling( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlDocument* TiXmlNode::GetDocument() const { const TiXmlNode* node; for( node = this; node; node = node->parent ) { if ( node->ToDocument() ) return node->ToDocument(); } return 0; } TiXmlElement::TiXmlElement (const char * _value) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #ifdef TIXML_USE_STL TiXmlElement::TiXmlElement( const std::string& _value ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #endif TiXmlElement::TiXmlElement( const TiXmlElement& copy) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; copy.CopyTo( this ); } void TiXmlElement::operator=( const TiXmlElement& base ) { ClearThis(); base.CopyTo( this ); } TiXmlElement::~TiXmlElement() { ClearThis(); } void TiXmlElement::ClearThis() { Clear(); while( attributeSet.First() ) { TiXmlAttribute* node = attributeSet.First(); attributeSet.Remove( node ); delete node; } } const char* TiXmlElement::Attribute( const char* name ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( node ) return node->Value(); return 0; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( attrib ) return &attrib->ValueStr(); return 0; } #endif const char* TiXmlElement::Attribute( const char* name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #endif const char* TiXmlElement::Attribute( const char* name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #endif int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } #ifdef TIXML_USE_STL int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } #endif int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #ifdef TIXML_USE_STL int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #endif void TiXmlElement::SetAttribute( const char * name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #endif void TiXmlElement::SetDoubleAttribute( const char * name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #endif void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); if ( attrib ) { attrib->SetValue( cvalue ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); if ( attrib ) { attrib->SetValue( _value ); } } #endif void TiXmlElement::Print( FILE* cfile, int depth ) const { int i; assert( cfile ); for ( i=0; iNext() ) { fprintf( cfile, " " ); attrib->Print( cfile, depth ); } // There are 3 different formatting approaches: // 1) An element without children is printed as a node // 2) An element with only a text child is printed as text // 3) An element with children is printed on multiple lines. TiXmlNode* node; if ( !firstChild ) { fprintf( cfile, " />" ); } else if ( firstChild == lastChild && firstChild->ToText() ) { fprintf( cfile, ">" ); firstChild->Print( cfile, depth + 1 ); fprintf( cfile, "", value.c_str() ); } else { fprintf( cfile, ">" ); for ( node = firstChild; node; node=node->NextSibling() ) { if ( !node->ToText() ) { fprintf( cfile, "\n" ); } node->Print( cfile, depth+1 ); } fprintf( cfile, "\n" ); for( i=0; i", value.c_str() ); } } void TiXmlElement::CopyTo( TiXmlElement* target ) const { // superclass: TiXmlNode::CopyTo( target ); // Element class: // Clone the attributes, then clone the children. const TiXmlAttribute* attribute = 0; for( attribute = attributeSet.First(); attribute; attribute = attribute->Next() ) { target->SetAttribute( attribute->Name(), attribute->Value() ); } TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this, attributeSet.First() ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } TiXmlNode* TiXmlElement::Clone() const { TiXmlElement* clone = new TiXmlElement( Value() ); if ( !clone ) return 0; CopyTo( clone ); return clone; } const char* TiXmlElement::GetText() const { const TiXmlNode* child = this->FirstChild(); if ( child ) { const TiXmlText* childText = child->ToText(); if ( childText ) { return childText->Value(); } } return 0; } TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; ClearError(); } TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #ifdef TIXML_USE_STL TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #endif TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { copy.CopyTo( this ); } void TiXmlDocument::operator=( const TiXmlDocument& copy ) { Clear(); copy.CopyTo( this ); } bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) { return LoadFile( Value(), encoding ); } bool TiXmlDocument::SaveFile() const { return SaveFile( Value() ); } bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) { TIXML_STRING filename( _filename ); value = filename; // reading in binary mode so that tinyxml can normalize the EOL FILE* file = TiXmlFOpen( value.c_str (), "rb" ); if ( file ) { bool result = LoadFile( file, encoding ); fclose( file ); return result; } else { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } } bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) { if ( !file ) { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Delete the existing data: Clear(); location.Clear(); // Get the file size, so we can pre-allocate the string. HUGE speed impact. long length = 0; fseek( file, 0, SEEK_END ); length = ftell( file ); fseek( file, 0, SEEK_SET ); // Strange case, but good to handle up front. if ( length <= 0 ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Subtle bug here. TinyXml did use fgets. But from the XML spec: // 2.11 End-of-Line Handling // // // ...the XML processor MUST behave as if it normalized all line breaks in external // parsed entities (including the document entity) on input, before parsing, by translating // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to // a single #xA character. // // // It is not clear fgets does that, and certainly isn't clear it works cross platform. // Generally, you expect fgets to translate from the convention of the OS to the c/unix // convention, and not work generally. /* while( fgets( buf, sizeof(buf), file ) ) { data += buf; } */ char* buf = new char[ length+1 ]; buf[0] = 0; if ( fread( buf, length, 1, file ) != 1 ) { delete [] buf; SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Process the buffer in place to normalize new lines. (See comment above.) // Copies from the 'p' to 'q' pointer, where p can advance faster if // a newline-carriage return is hit. // // Wikipedia: // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 const char* p = buf; // the read head char* q = buf; // the write head const char CR = 0x0d; const char LF = 0x0a; buf[length] = 0; while( *p ) { assert( p < (buf+length) ); assert( q <= (buf+length) ); assert( q <= p ); if ( *p == CR ) { *q++ = LF; p++; if ( *p == LF ) { // check for CR+LF (and skip LF) p++; } } else { *q++ = *p++; } } assert( q <= (buf+length) ); *q = 0; Parse( buf, 0, encoding ); delete [] buf; return !Error(); } bool TiXmlDocument::SaveFile( const char * filename ) const { // The old c stuff lives on... FILE* fp = TiXmlFOpen( filename, "w" ); if ( fp ) { bool result = SaveFile( fp ); fclose( fp ); return result; } return false; } bool TiXmlDocument::SaveFile( FILE* fp ) const { if ( useMicrosoftBOM ) { const unsigned char TIXML_UTF_LEAD_0 = 0xefU; const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; fputc( TIXML_UTF_LEAD_0, fp ); fputc( TIXML_UTF_LEAD_1, fp ); fputc( TIXML_UTF_LEAD_2, fp ); } Print( fp, 0 ); return (ferror(fp) == 0); } void TiXmlDocument::CopyTo( TiXmlDocument* target ) const { TiXmlNode::CopyTo( target ); target->error = error; target->errorId = errorId; target->errorDesc = errorDesc; target->tabsize = tabsize; target->errorLocation = errorLocation; target->useMicrosoftBOM = useMicrosoftBOM; TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } TiXmlNode* TiXmlDocument::Clone() const { TiXmlDocument* clone = new TiXmlDocument(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlDocument::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { node->Print( cfile, depth ); fprintf( cfile, "\n" ); } } bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } const TiXmlAttribute* TiXmlAttribute::Next() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } /* TiXmlAttribute* TiXmlAttribute::Next() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } */ const TiXmlAttribute* TiXmlAttribute::Previous() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } /* TiXmlAttribute* TiXmlAttribute::Previous() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } */ void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { TIXML_STRING n, v; EncodeString( name, &n ); EncodeString( value, &v ); if (value.find ('\"') == TIXML_STRING::npos) { if ( cfile ) { fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; } } else { if ( cfile ) { fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; } } } int TiXmlAttribute::QueryIntValue( int* ival ) const { if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int TiXmlAttribute::QueryDoubleValue( double* dval ) const { if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } void TiXmlAttribute::SetIntValue( int _value ) { char buf [64]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); #else sprintf (buf, "%d", _value); #endif SetValue (buf); } void TiXmlAttribute::SetDoubleValue( double _value ) { char buf [256]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); #else sprintf (buf, "%g", _value); #endif SetValue (buf); } int TiXmlAttribute::IntValue() const { return atoi (value.c_str ()); } double TiXmlAttribute::DoubleValue() const { return atof (value.c_str ()); } TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { copy.CopyTo( this ); } void TiXmlComment::operator=( const TiXmlComment& base ) { Clear(); base.CopyTo( this ); } void TiXmlComment::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( int i=0; i", value.c_str() ); } void TiXmlComment::CopyTo( TiXmlComment* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlComment::Clone() const { TiXmlComment* clone = new TiXmlComment(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlText::Print( FILE* cfile, int depth ) const { assert( cfile ); if ( cdata ) { int i; fprintf( cfile, "\n" ); for ( i=0; i\n", value.c_str() ); // unformatted output } else { TIXML_STRING buffer; EncodeString( value, &buffer ); fprintf( cfile, "%s", buffer.c_str() ); } } void TiXmlText::CopyTo( TiXmlText* target ) const { TiXmlNode::CopyTo( target ); target->cdata = cdata; } bool TiXmlText::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlText::Clone() const { TiXmlText* clone = 0; clone = new TiXmlText( "" ); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlDeclaration::TiXmlDeclaration( const char * _version, const char * _encoding, const char * _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #ifdef TIXML_USE_STL TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #endif TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { copy.CopyTo( this ); } void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) { Clear(); copy.CopyTo( this ); } void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { if ( cfile ) fprintf( cfile, "" ); if ( str ) (*str) += "?>"; } void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const { TiXmlNode::CopyTo( target ); target->version = version; target->encoding = encoding; target->standalone = standalone; } bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlDeclaration::Clone() const { TiXmlDeclaration* clone = new TiXmlDeclaration(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlUnknown::Print( FILE* cfile, int depth ) const { for ( int i=0; i", value.c_str() ); } void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlUnknown::Clone() const { TiXmlUnknown* clone = new TiXmlUnknown(); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlAttributeSet::TiXmlAttributeSet() { sentinel.next = &sentinel; sentinel.prev = &sentinel; } TiXmlAttributeSet::~TiXmlAttributeSet() { assert( sentinel.next == &sentinel ); assert( sentinel.prev == &sentinel ); } void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) { #ifdef TIXML_USE_STL assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. #else assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. #endif addMe->next = &sentinel; addMe->prev = sentinel.prev; sentinel.prev->next = addMe; sentinel.prev = addMe; } void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) { TiXmlAttribute* node; for( node = sentinel.next; node != &sentinel; node = node->next ) { if ( node == removeMe ) { node->prev->next = node->next; node->next->prev = node->prev; node->next = 0; node->prev = 0; return; } } assert( 0 ); // we tried to remove a non-linked attribute. } #ifdef TIXML_USE_STL TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( node->name == name ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #endif TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( strcmp( node->name.c_str(), name ) == 0 ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #ifdef TIXML_USE_STL std::istream& operator>> (std::istream & in, TiXmlNode & base) { TIXML_STRING tag; tag.reserve( 8 * 1000 ); base.StreamIn( &in, &tag ); base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); return in; } #endif #ifdef TIXML_USE_STL std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out << printer.Str(); return out; } std::string& operator<< (std::string& out, const TiXmlNode& base ) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out.append( printer.Str() ); return out; } #endif TiXmlHandle TiXmlHandle::FirstChild() const { if ( node ) { TiXmlNode* child = node->FirstChild(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const { if ( node ) { TiXmlNode* child = node->FirstChild( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement() const { if ( node ) { TiXmlElement* child = node->FirstChildElement(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const { if ( node ) { TiXmlElement* child = node->FirstChildElement( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild(); for ( i=0; child && iNextSibling(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild( value ); for ( i=0; child && iNextSibling( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement(); for ( i=0; child && iNextSiblingElement(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement( value ); for ( i=0; child && iNextSiblingElement( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { DoIndent(); buffer += "<"; buffer += element.Value(); for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) { buffer += " "; attrib->Print( 0, 0, &buffer ); } if ( !element.FirstChild() ) { buffer += " />"; DoLineBreak(); } else { buffer += ">"; if ( element.FirstChild()->ToText() && element.LastChild() == element.FirstChild() && element.FirstChild()->ToText()->CDATA() == false ) { simpleTextPrint = true; // no DoLineBreak()! } else { DoLineBreak(); } } ++depth; return true; } bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) { --depth; if ( !element.FirstChild() ) { // nothing. } else { if ( simpleTextPrint ) { simpleTextPrint = false; } else { DoIndent(); } buffer += ""; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlText& text ) { if ( text.CDATA() ) { DoIndent(); buffer += ""; DoLineBreak(); } else if ( simpleTextPrint ) { TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; } else { DoIndent(); TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) { DoIndent(); declaration.Print( 0, 0, &buffer ); DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlComment& comment ) { DoIndent(); buffer += ""; DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) { DoIndent(); buffer += "<"; buffer += unknown.Value(); buffer += ">"; DoLineBreak(); return true; } Indigo-indigo-1.2.3/third_party/tinyxml/src/tinyxmlerror.cpp000066400000000000000000000033641271037650300243150ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "tinyxml.h" // The goal of the seperate error file is to make the first // step towards localization. tinyxml (currently) only supports // english error messages, but the could now be translated. // // It also cleans up the code a bit. // const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = { "No error", "Error", "Failed to open file", "Error parsing Element.", "Failed to read Element name", "Error reading Element value.", "Error reading Attributes.", "Error: empty tag.", "Error reading end tag.", "Error parsing Unknown.", "Error parsing Comment.", "Error parsing Declaration.", "Error document empty.", "Error null (0) or unexpected EOF found in input stream.", "Error parsing CDATA.", "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", }; Indigo-indigo-1.2.3/third_party/tinyxml/src/tinyxmlparser.cpp000066400000000000000000001105111271037650300244510ustar00rootroot00000000000000/* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #include #include "tinyxml.h" //#define DEBUG_PARSER #if defined( DEBUG_PARSER ) # if defined( DEBUG ) && defined( _MSC_VER ) # include # define TIXML_LOG OutputDebugString # else # define TIXML_LOG printf # endif #endif // Note tha "PutString" hardcodes the same list. This // is less flexible than it appears. Changing the entries // or order will break putstring. TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = { { "&", 5, '&' }, { "<", 4, '<' }, { ">", 4, '>' }, { """, 6, '\"' }, { "'", 6, '\'' } }; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // Including the basic of this table, which determines the #bytes in the // sequence from the lead byte. 1 placed for invalid sequences -- // although the result will be junk, pass it through as much as possible. // Beware of the non-characters in UTF-8: // ef bb bf (Microsoft "lead bytes") // ef bf be // ef bf bf const unsigned char TIXML_UTF_LEAD_0 = 0xefU; const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; const int TiXmlBase::utf8ByteTable[256] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid }; void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) *length = 1; else if ( input < 0x800 ) *length = 2; else if ( input < 0x10000 ) *length = 3; else if ( input < 0x200000 ) *length = 4; else { *length = 0; return; } // This code won't covert this correctly anyway. output += *length; // Scary scary fall throughs. switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); } } /*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalpha( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalpha( anyByte ); // } } /*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalnum( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalnum( anyByte ); // } } class TiXmlParsingData { friend class TiXmlDocument; public: void Stamp( const char* now, TiXmlEncoding encoding ); const TiXmlCursor& Cursor() { return cursor; } private: // Only used by the document! TiXmlParsingData( const char* start, int _tabsize, int row, int col ) { assert( start ); stamp = start; tabsize = _tabsize; cursor.row = row; cursor.col = col; } TiXmlCursor cursor; const char* stamp; int tabsize; }; void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) { assert( now ); // Do nothing if the tabsize is 0. if ( tabsize < 1 ) { return; } // Get the current row, column. int row = cursor.row; int col = cursor.col; const char* p = stamp; assert( p ); while ( p < now ) { // Treat p as unsigned, so we have a happy compiler. const unsigned char* pU = (const unsigned char*)p; // Code contributed by Fletcher Dunn: (modified by lee) switch (*pU) { case 0: // We *should* never get here, but in case we do, don't // advance past the terminating null character, ever return; case '\r': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \r\n sequence, and treat this as a single character if (*p == '\n') { ++p; } break; case '\n': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \n\r sequence, and treat this as a single // character. (Yes, this bizarre thing does occur still // on some arcane platforms...) if (*p == '\r') { ++p; } break; case '\t': // Eat the character ++p; // Skip to next tab stop col = (col / tabsize + 1) * tabsize; break; case TIXML_UTF_LEAD_0: if ( encoding == TIXML_ENCODING_UTF8 ) { if ( *(p+1) && *(p+2) ) { // In these cases, don't advance the column. These are // 0-width spaces. if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) p += 3; else { p +=3; ++col; } // A normal character. } } else { ++p; ++col; } break; default: if ( encoding == TIXML_ENCODING_UTF8 ) { // Eat the 1 to 4 byte utf8 character. int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; if ( step == 0 ) step = 1; // Error case from bad encoding, but handle gracefully. p += step; // Just advance one column, of course. ++col; } else { ++p; ++col; } break; } } cursor.row = row; cursor.col = col; assert( cursor.row >= -1 ); assert( cursor.col >= -1 ); stamp = p; assert( stamp ); } const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) { if ( !p || !*p ) { return 0; } if ( encoding == TIXML_ENCODING_UTF8 ) { while ( *p ) { const unsigned char* pU = (const unsigned char*)p; // Skip the stupid Microsoft UTF-8 Byte order marks if ( *(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbeU ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbfU ) { p += 3; continue; } if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. ++p; else break; } } else { while ( *p && IsWhiteSpace( *p ) ) ++p; } return p; } #ifdef TIXML_USE_STL /*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) { for( ;; ) { if ( !in->good() ) return false; int c = in->peek(); // At this scope, we can't get to a document. So fail silently. if ( !IsWhiteSpace( c ) || c <= 0 ) return true; *tag += (char) in->get(); } } /*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) { //assert( character > 0 && character < 128 ); // else it won't work in utf-8 while ( in->good() ) { int c = in->peek(); if ( c == character ) return true; if ( c <= 0 ) // Silent failure: can't get document at this scope return false; in->get(); *tag += (char) c; } return false; } #endif // One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The // "assign" optimization removes over 10% of the execution time. // const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) { // Oddly, not supported on some comilers, //name->clear(); // So use this: *name = ""; assert( p ); // Names start with letters or underscores. // Of course, in unicode, tinyxml has no idea what a letter *is*. The // algorithm is generous. // // After that, they can be letters, underscores, numbers, // hyphens, or colons. (Colons are valid ony for namespaces, // but tinyxml can't tell namespaces from names.) if ( p && *p && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) { const char* start = p; while( p && *p && ( IsAlphaNum( (unsigned char ) *p, encoding ) || *p == '_' || *p == '-' || *p == '.' || *p == ':' ) ) { //(*name) += *p; // expensive ++p; } if ( p-start > 0 ) { name->assign( start, p-start ); } return p; } return 0; } const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) { // Presume an entity, and pull it out. TIXML_STRING ent; int i; *length = 0; if ( *(p+1) && *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; ptrdiff_t delta = 0; unsigned mult = 1; if ( *(p+2) == 'x' ) { // Hexadecimal. if ( !*(p+3) ) return 0; const char* q = p+3; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != 'x' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else if ( *q >= 'a' && *q <= 'f' ) ucs += mult * (*q - 'a' + 10); else if ( *q >= 'A' && *q <= 'F' ) ucs += mult * (*q - 'A' + 10 ); else return 0; mult *= 16; --q; } } else { // Decimal. if ( !*(p+2) ) return 0; const char* q = p+2; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else return 0; mult *= 10; --q; } } if ( encoding == TIXML_ENCODING_UTF8 ) { // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); } else { *value = (char)ucs; *length = 1; } return p + delta + 1; } // Now try to match it. for( i=0; iappend( cArr, len ); } } else { bool whitespace = false; // Remove leading white space: p = SkipWhiteSpace( p, encoding ); while ( p && *p && !StringEqual( p, endTag, caseInsensitive, encoding ) ) { if ( *p == '\r' || *p == '\n' ) { whitespace = true; ++p; } else if ( IsWhiteSpace( *p ) ) { whitespace = true; ++p; } else { // If we've found whitespace, add it before the // new character. Any whitespace just becomes a space. if ( whitespace ) { (*text) += ' '; whitespace = false; } int len; char cArr[4] = { 0, 0, 0, 0 }; p = GetChar( p, cArr, &len, encoding ); if ( len == 1 ) (*text) += cArr[0]; // more efficient else text->append( cArr, len ); } } } if ( p && *p ) p += strlen( endTag ); return p; } #ifdef TIXML_USE_STL void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) { // The basic issue with a document is that we don't know what we're // streaming. Read something presumed to be a tag (and hope), then // identify it, and call the appropriate stream method on the tag. // // This "pre-streaming" will never read the closing ">" so the // sub-tag can orient itself. if ( !StreamTo( in, '<', tag ) ) { SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } while ( in->good() ) { int tagIndex = (int) tag->length(); while ( in->good() && in->peek() != '>' ) { int c = in->get(); if ( c <= 0 ) { SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); break; } (*tag) += (char) c; } if ( in->good() ) { // We now have something we presume to be a node of // some sort. Identify it, and call the node to // continue streaming. TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); if ( node ) { node->StreamIn( in, tag ); bool isElement = node->ToElement() != 0; delete node; node = 0; // If this is the root element, we're done. Parsing will be // done by the >> operator. if ( isElement ) { return; } } else { SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } } } // We should have returned sooner. SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); } #endif const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) { ClearError(); // Parse away, at the document level. Since a document // contains nothing but other tags, most of what happens // here is skipping white space. if ( !p || !*p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } // Note that, for a document, this needs to come // before the while space skip, so that parsing // starts from the pointer we are given. location.Clear(); if ( prevData ) { location.row = prevData->cursor.row; location.col = prevData->cursor.col; } else { location.row = 0; location.col = 0; } TiXmlParsingData data( p, TabSize(), location.row, location.col ); location = data.Cursor(); if ( encoding == TIXML_ENCODING_UNKNOWN ) { // Check for the Microsoft UTF-8 lead bytes. const unsigned char* pU = (const unsigned char*)p; if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) { encoding = TIXML_ENCODING_UTF8; useMicrosoftBOM = true; } } p = SkipWhiteSpace( p, encoding ); if ( !p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } while ( p && *p ) { TiXmlNode* node = Identify( p, encoding ); if ( node ) { p = node->Parse( p, &data, encoding ); LinkEndChild( node ); } else { break; } // Did we get encoding info? if ( encoding == TIXML_ENCODING_UNKNOWN && node->ToDeclaration() ) { TiXmlDeclaration* dec = node->ToDeclaration(); const char* enc = dec->Encoding(); assert( enc ); if ( *enc == 0 ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice else encoding = TIXML_ENCODING_LEGACY; } p = SkipWhiteSpace( p, encoding ); } // Was this empty? if ( !firstChild ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); return 0; } // All is well. return p; } void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) { // The first error in a chain is more accurate - don't set again! if ( error ) return; assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); error = true; errorId = err; errorDesc = errorString[ errorId ]; errorLocation.Clear(); if ( pError && data ) { data->Stamp( pError, encoding ); errorLocation = data->Cursor(); } } TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) { TiXmlNode* returnNode = 0; p = SkipWhiteSpace( p, encoding ); if( !p || !*p || *p != '<' ) { return 0; } p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { return 0; } // What is this thing? // - Elements start with a letter or underscore, but xml is reserved. // - Comments: "; if ( !StringEqual( p, startTag, false, encoding ) ) { document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); return 0; } p += strlen( startTag ); // [ 1475201 ] TinyXML parses entities in comments // Oops - ReadText doesn't work, because we don't want to parse the entities. // p = ReadText( p, &value, false, endTag, false, encoding ); // // from the XML spec: /* [Definition: Comments may appear anywhere in a document outside other markup; in addition, they may appear within the document type declaration at places allowed by the grammar. They are not part of the document's character data; an XML processor MAY, but need not, make it possible for an application to retrieve the text of comments. For compatibility, the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity references MUST NOT be recognized within comments. An example of a comment: */ value = ""; // Keep all the white space. while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) { value.append( p, 1 ); ++p; } if ( p && *p ) p += strlen( endTag ); return p; } const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) { p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) return 0; if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } // Read the name, the '=' and the value. const char* pErr = p; p = ReadName( p, &name, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); return 0; } p = SkipWhiteSpace( p, encoding ); if ( !p || !*p || *p != '=' ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } ++p; // skip '=' p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } const char* end; const char SINGLE_QUOTE = '\''; const char DOUBLE_QUOTE = '\"'; if ( *p == SINGLE_QUOTE ) { ++p; end = "\'"; // single quote in string p = ReadText( p, &value, false, end, false, encoding ); } else if ( *p == DOUBLE_QUOTE ) { ++p; end = "\""; // double quote in string p = ReadText( p, &value, false, end, false, encoding ); } else { // All attribute values should be in single or double quotes. // But this is such a common error that the parser will try // its best, even without them. value = ""; while ( p && *p // existence && !IsWhiteSpace( *p ) // whitespace && *p != '/' && *p != '>' ) // tag end { if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { // [ 1451649 ] Attribute values with trailing quotes not handled correctly // We did not have an opening quote but seem to have a // closing one. Give up and throw an error. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } value += *p; ++p; } } return p; } #ifdef TIXML_USE_STL void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) { while ( in->good() ) { int c = in->peek(); if ( !cdata && (c == '<' ) ) { return; } if ( c <= 0 ) { TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; in->get(); // "commits" the peek made above if ( cdata && c == '>' && tag->size() >= 3 ) { size_t len = tag->size(); if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { // terminator of cdata. return; } } } } #endif const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) { value = ""; TiXmlDocument* document = GetDocument(); if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } const char* const startTag = ""; if ( cdata || StringEqual( p, startTag, false, encoding ) ) { cdata = true; if ( !StringEqual( p, startTag, false, encoding ) ) { document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); return 0; } p += strlen( startTag ); // Keep all the white space, ignore the encoding, etc. while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) { value += *p; ++p; } TIXML_STRING dummy; p = ReadText( p, &dummy, false, endTag, false, encoding ); return p; } else { bool ignoreWhite = true; const char* end = "<"; p = ReadText( p, &value, ignoreWhite, end, false, encoding ); if ( p ) return p-1; // don't truncate the '<' return 0; } } #ifdef TIXML_USE_STL void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) { while ( in->good() ) { int c = in->get(); if ( c <= 0 ) { TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; if ( c == '>' ) { // All is well. return; } } } #endif const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) { p = SkipWhiteSpace( p, _encoding ); // Find the beginning, find the end, and look for // the stuff in-between. TiXmlDocument* document = GetDocument(); if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); return 0; } if ( data ) { data->Stamp( p, _encoding ); location = data->Cursor(); } p += 5; version = ""; encoding = ""; standalone = ""; while ( p && *p ) { if ( *p == '>' ) { ++p; return p; } p = SkipWhiteSpace( p, _encoding ); if ( StringEqual( p, "version", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); version = attrib.Value(); } else if ( StringEqual( p, "encoding", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); encoding = attrib.Value(); } else if ( StringEqual( p, "standalone", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); standalone = attrib.Value(); } else { // Read over whatever it is. while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) ++p; } } return 0; } bool TiXmlText::Blank() const { for ( unsigned i=0; i, or to Gilles Vollant for the Windows DLL version. The zlib home page is http://zlib.net/ . Before reporting a problem, please check this site to verify that you have the latest version of zlib; otherwise get the latest version and check whether the problem still exists or not. PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at http://marknelson.us/1997/01/01/zlib-engine/ . The changes made in version 1.2.5 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . zlib is available in Java using the java.util.zip package, documented at http://java.sun.com/developer/technicalArticles/Programming/compression/ . A Perl interface to zlib written by Paul Marquess is available at CPAN (Comprehensive Perl Archive Network) sites, including http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . A Python interface to zlib written by A.M. Kuchling is available in Python 1.5 and later versions, see http://www.python.org/doc/lib/module-zlib.html . zlib is built into tcl: http://wiki.tcl.tk/4610 . An experimental package to read and write files in .zip format, written on top of zlib by Gilles Vollant , is available in the contrib/minizip directory of zlib. Notes for some targets: - For Windows DLL versions, please see win32/DLL_FAQ.txt - For 64-bit Irix, deflate.c must be compiled without any optimization. With -O, one libpng test fails. The test works in 32 bit mode (with the -n32 compiler flag). The compiler bug has been reported to SGI. - zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works when compiled with cc. - On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is necessary to get gzprintf working correctly. This is done by configure. - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with other compilers. Use "make test" to check your compiler. - gzdopen is not supported on RISCOS or BEOS. - For PalmOs, see http://palmzlib.sourceforge.net/ Acknowledgments: The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: (C) 1995-2010 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. Indigo-indigo-1.2.3/third_party/zlib-src/include/000077500000000000000000000000001271037650300217025ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/zlib-src/include/crc32.h000066400000000000000000000735501271037650300230010ustar00rootroot00000000000000/* crc32.h -- tables for rapid CRC calculation * Generated automatically by crc32.c */ local const unsigned long FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, 0x2d02ef8dUL #ifdef BYFOUR }, { 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, 0x9324fd72UL }, { 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, 0xbe9834edUL }, { 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, 0xde0506f1UL }, { 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, 0x8def022dUL }, { 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, 0x72fd2493UL }, { 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, 0xed3498beUL }, { 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, 0xf10605deUL #endif } }; Indigo-indigo-1.2.3/third_party/zlib-src/include/deflate.h000066400000000000000000000306001271037650300234560ustar00rootroot00000000000000/* deflate.h -- internal compression state * Copyright (C) 1995-2010 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef DEFLATE_H #define DEFLATE_H #include "zutil.h" /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define INIT_STATE 42 #define EXTRA_STATE 69 #define NAME_STATE 73 #define COMMENT_STATE 91 #define HCRC_STATE 103 #define BUSY_STATE 113 #define FINISH_STATE 666 /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ uInt pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ uInt gzindex; /* where in extra, name, or comment */ Byte method; /* STORED (for zip only) or DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to supress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ uchf *l_buf; /* buffer for literals or lengths */ uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt last_lit; /* running index in l_buf */ ushf *d_buf; /* Buffer for distances. To simplify the code, d_buf and l_buf have * the same number of elements. To use different lengths, an extra flag * array would be necessary. */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ int last_eob_len; /* bit length of EOB code for last block */ #ifdef DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ ulg high_water; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ #define WIN_INIT MAX_MATCH /* Number of bytes after end of data in window to initialize in order to avoid memory checker errors from longest match routines */ /* in trees.c */ void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. _dist_code[256] and _dist_code[257] are never * used. */ #ifndef DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) extern uch ZLIB_INTERNAL _length_code[]; extern uch ZLIB_INTERNAL _dist_code[]; #else extern const uch ZLIB_INTERNAL _length_code[]; extern const uch ZLIB_INTERNAL _dist_code[]; #endif # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->d_buf[s->last_lit] = 0; \ s->l_buf[s->last_lit++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (length); \ ush dist = (distance); \ s->d_buf[s->last_lit] = dist; \ s->l_buf[s->last_lit++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->last_lit == s->lit_bufsize-1); \ } #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ flush = _tr_tally(s, distance, length) #endif #endif /* DEFLATE_H */ Indigo-indigo-1.2.3/third_party/zlib-src/include/gzguts.h000066400000000000000000000111051271037650300233740ustar00rootroot00000000000000/* gzguts.h -- zlib internal header definitions for gz* operations * Copyright (C) 2004, 2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #ifdef _LARGEFILE64_SOURCE # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif # ifdef _FILE_OFFSET_BITS # undef _FILE_OFFSET_BITS # endif #endif #if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include #include "zlib.h" #ifdef STDC # include # include # include #endif #include #ifdef NO_DEFLATE /* for compatibility with old definition */ # define NO_GZCOMPRESS #endif #ifdef _MSC_VER # include # define vsnprintf _vsnprintf #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ /* gz* functions always use library allocation functions */ #ifndef STDC extern voidp malloc OF((uInt size)); extern void free OF((voidpf ptr)); #endif /* get errno and strerror definition */ #if defined UNDER_CE # include # define zstrerror() gz_strwinerror((DWORD)GetLastError()) #else # ifdef STDC # include # define zstrerror() strerror(errno) # else # define zstrerror() "stdio error (consult errno)" # endif #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); #endif /* default i/o buffer size -- double this for output when reading */ #define GZBUFSIZE 8192 /* gzip modes, also provide a little integrity check on the passed structure */ #define GZ_NONE 0 #define GZ_READ 7247 #define GZ_WRITE 31153 #define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ /* values for gz_state how */ #define LOOK 0 /* look for a gzip header */ #define COPY 1 /* copy input directly */ #define GZIP 2 /* decompress a gzip stream */ /* internal gzip file state data structure */ typedef struct { /* used for both reading and writing */ int mode; /* see gzip modes above */ int fd; /* file descriptor */ char *path; /* path or fd for error messages */ z_off64_t pos; /* current position in uncompressed data */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned char *in; /* input buffer */ unsigned char *out; /* output buffer (double-sized when reading) */ unsigned char *next; /* next output data to deliver or write */ /* just for reading */ unsigned have; /* amount of output data unused at next */ int eof; /* true if end of input file reached */ z_off64_t start; /* where the gzip data started, for rewinding */ z_off64_t raw; /* where the raw data started, for seeking */ int how; /* 0: get header, 1: copy, 2: decompress */ int direct; /* true if last read direct, false if gzip */ /* just for writing */ int level; /* compression level */ int strategy; /* compression strategy */ /* seek request */ z_off64_t skip; /* amount to skip (already rewound if backwards) */ int seek; /* true if seek request pending */ /* error information */ int err; /* error code */ char *msg; /* error message */ /* zlib inflate or deflate stream */ z_stream strm; /* stream structure in-place (not a pointer) */ } gz_state; typedef gz_state FAR *gz_statep; /* shared functions */ void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ #ifdef INT_MAX # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) #else unsigned ZLIB_INTERNAL gz_intmax OF((void)); # define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) #endif Indigo-indigo-1.2.3/third_party/zlib-src/include/inffast.h000066400000000000000000000006531271037650300235110ustar00rootroot00000000000000/* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); Indigo-indigo-1.2.3/third_party/zlib-src/include/inffixed.h000066400000000000000000000143071271037650300236540ustar00rootroot00000000000000 /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; Indigo-indigo-1.2.3/third_party/zlib-src/include/inflate.h000066400000000000000000000143771271037650300235110ustar00rootroot00000000000000/* inflate.h -- internal inflate state definition * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* define NO_GZIP when compiling if you want to disable gzip header and trailer decoding by inflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip decoding should be left enabled. */ #ifndef NO_GZIP # define GUNZIP #endif /* Possible inflate modes between inflate() calls */ typedef enum { HEAD, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN_, /* i: same as LEN below, but only first time in */ LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) or (raw) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE (raw) -> TYPEDO Read deflate blocks: TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK STORED -> COPY_ -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN_ LEN_ -> LEN Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* state maintained between inflate() calls. Approximately 10K bytes. */ struct inflate_state { inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ gz_headerp head; /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ int back; /* bits back of last unprocessed length/lit */ unsigned was; /* initial length of match */ }; Indigo-indigo-1.2.3/third_party/zlib-src/include/inftrees.h000066400000000000000000000055601271037650300237000ustar00rootroot00000000000000/* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribtution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work)); Indigo-indigo-1.2.3/third_party/zlib-src/include/trees.h000066400000000000000000000204301271037650300231740ustar00rootroot00000000000000/* header created automatically with -DGEN_TREES_H */ local const ct_data static_ltree[L_CODES+2] = { {{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, {{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, {{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, {{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, {{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, {{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, {{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, {{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, {{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, {{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, {{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, {{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, {{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, {{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, {{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, {{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, {{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, {{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, {{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, {{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, {{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, {{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, {{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, {{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, {{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, {{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, {{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, {{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, {{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, {{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, {{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, {{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, {{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, {{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, {{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, {{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, {{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, {{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, {{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, {{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, {{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, {{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, {{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, {{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, {{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, {{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, {{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, {{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, {{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, {{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, {{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, {{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, {{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, {{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, {{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, {{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, {{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, {{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} }; local const ct_data static_dtree[D_CODES] = { {{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, {{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, {{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, {{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, {{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; local const int base_length[LENGTH_CODES] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 }; local const int base_dist[D_CODES] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 }; Indigo-indigo-1.2.3/third_party/zlib-src/include/zconf.h000066400000000000000000000320771271037650300232030ustar00rootroot00000000000000/* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ /* all linked symbols */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzgetc z_gzgetc # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzwrite z_gzwrite # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit_ z_inflateBackInit_ # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetHeader z_inflateGetHeader # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # define uncompress z_uncompress # define zError z_zError # define zcalloc z_zcalloc # define zcfree z_zcfree # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # define gzFile z_gzFile # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif /* Some Mac compilers merge all .h files incorrectly: */ #if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) # define NO_DUMMY_DECL #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef STDC # include /* for off_t */ #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_* and off_t */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif #endif #ifndef SEEK_SET # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 # define z_off64_t off64_t #else # define z_off64_t z_off_t #endif #if defined(__OS400__) # define NO_vsnprintf #endif #if defined(__MVS__) # define NO_vsnprintf #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ Indigo-indigo-1.2.3/third_party/zlib-src/include/zlib.h000066400000000000000000002333141271037650300230210ustar00rootroot00000000000000/* zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.5, April 19th, 2010 Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.2.5" #define ZLIB_VERNUM 0x1250 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 5 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total nb of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total nb of bytes output so far */ char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field (though see inflate()) */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed code block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least the value returned by deflateBound (see below). If deflate does not return Z_STREAM_END, then it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so far (that is, total_in bytes). deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. If next_in is not Z_NULL and avail_in is large enough (the exact value depends on the compression method), inflateInit determines the compression method from the zlib header and allocates all data structures accordingly; otherwise the allocation will be deferred to the first call of inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. Also to assist in this, on return inflate() will set strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all the uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH is never required, but can be used to inform inflate that a faster approach may be used for the single inflate() call. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the only effect of the flush parameter in this implementation is on the return value of inflate(), as noted below, or when it returns early because Z_BLOCK or Z_TREES is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the adler32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the adler32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained, so applications that need that information should instead use raw inflate, see inflateInit2() below, or inflateBack() and perform their own processing of the gzip header and trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is desired. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute an adler32 check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to 255 (unknown). If a gzip stream is being written, strm->adler is a crc32 instead of an adler32. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. This function must be called immediately after deflateInit, deflateInit2 or deflateReset, before any call of deflate. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the adler32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The adler32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the adler32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if the compression method is bsort). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2. This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the old level (and may be flushed); the new level will take effect only at the next call of deflate(). Before the call of deflateParams, the stream state must be set as for a call of deflate(), since the currently available input may have to be compressed and flushed. In particular, strm->avail_out must be non-zero. deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if strm->avail_out was zero. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain)); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an adler32 or a crc32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a crc32 instead of an adler32. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the adler32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called immediately after inflateInit2() or inflateReset() and before any call of inflate() to set the dictionary. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a full flush point (see above the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate all the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, int windowBits)); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int bits, int value)); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above or -1 << 16 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, unsigned char FAR *window)); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the paramaters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is more efficient than inflate() for file i/o applications in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. This function trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the normal behavior of inflate(), which expects either a zlib or gzip header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero--buf is ignored in that case--and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed buffer. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef voidp gzFile; /* opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) Also "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); /* gzdopen associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* Set the internal buffer size used by this library's functions. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Two buffers are allocated, either both of the specified size when writing, or one of the specified size and the other twice that size when reading. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not opened for writing. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file was not in gzip format, gzread copies the given number of bytes into the buffer. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream, or failing that, reading the rest of the input file directly without decompression. The entire input file will be read if gzread is called until it returns less than the requested len. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or 0 in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len == 1, the string is terminated with a null character. If no characters are read due to an end-of-file or len < 1, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* Push one character back onto the stream to be read as the first character on the next read. At least one character of push-back is allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatented gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence)); Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ /* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); Returns the current offset in the file being read or written. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* Returns true (1) if the end-of-file indicator has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. This state can change from false to true while reading the input file if the end of a gzip stream is reached, but is followed by data that is not another gzip stream. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* Clears the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); #define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) #define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) #define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, sizeof(z_stream)) #define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) #define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, sizeof(z_stream)) /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif #if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # ifdef _LARGEFILE64_SOURCE ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif /* hack for buggy compilers */ #if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) struct internal_state {int dummy;}; #endif /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); #ifdef __cplusplus } #endif #endif /* ZLIB_H */ Indigo-indigo-1.2.3/third_party/zlib-src/include/zutil.h000066400000000000000000000157611271037650300232340ustar00rootroot00000000000000/* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef ZUTIL_H #define ZUTIL_H #if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include "zlib.h" #ifdef STDC # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif # include # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ return (strm->msg = (char*)ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 # if defined(__TURBOC__) || defined(__BORLANDC__) # if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) /* Allow compilation with ANSI keywords only enabled */ void _Cdecl farfree( void *block ); void *_Cdecl farmalloc( unsigned long nbytes ); # else # include # endif # else /* MSC or DJGPP */ # include # endif #endif #ifdef AMIGA # define OS_CODE 0x01 #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 0x02 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 0x05 #endif #ifdef OS2 # define OS_CODE 0x06 # ifdef M_I86 # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 0x07 # if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include /* for fdopen */ # else # ifndef fdopen # define fdopen(fd,mode) NULL /* No fdopen() */ # endif # endif #endif #ifdef TOPS20 # define OS_CODE 0x0a #endif #ifdef WIN32 # ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ # define OS_CODE 0x0b # endif #endif #ifdef __50SERIES /* Prime/PRIMOS */ # define OS_CODE 0x0f #endif #if defined(_BEOS_) || defined(RISCOS) # define fdopen(fd,mode) NULL /* No fdopen() */ #endif #if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX # if defined(_WIN32_WCE) # define fdopen(fd,mode) NULL /* No fdopen() */ # ifndef _PTRDIFF_T_DEFINED typedef int ptrdiff_t; # define _PTRDIFF_T_DEFINED # endif # else # define fdopen(fd,type) _fdopen(fd,type) # endif #endif #if defined(__BORLANDC__) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); #endif /* common defaults */ #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(__CYGWIN__) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #ifndef HAVE_VSNPRINTF # ifdef MSDOS /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), but for now we just assume it doesn't. */ # define NO_vsnprintf # endif # ifdef __TURBOC__ # define NO_vsnprintf # endif # ifdef WIN32 /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ # if !defined(vsnprintf) && !defined(NO_vsnprintf) # if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) # define vsnprintf _vsnprintf # endif # endif # endif # ifdef __SASC # define NO_vsnprintf # endif #endif #ifdef VMS # define NO_vsnprintf #endif #if defined(pyr) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). * The __SC__ check is for Symantec. */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); #endif /* Diagnostic functions */ #ifdef DEBUG # include extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error OF((char *m)); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} # define Tracevv(x) {if (z_verbose>1) fprintf x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, unsigned size)); void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} #endif /* ZUTIL_H */ Indigo-indigo-1.2.3/third_party/zlib-src/src/000077500000000000000000000000001271037650300210465ustar00rootroot00000000000000Indigo-indigo-1.2.3/third_party/zlib-src/src/adler32.c000066400000000000000000000116701271037650300224530ustar00rootroot00000000000000/* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-2007 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #define local static local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); #define BASE 65521UL /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* use NO_DIVIDE if your processor does not do division in hardware */ #ifdef NO_DIVIDE # define MOD(a) \ do { \ if (a >= (BASE << 16)) a -= (BASE << 16); \ if (a >= (BASE << 15)) a -= (BASE << 15); \ if (a >= (BASE << 14)) a -= (BASE << 14); \ if (a >= (BASE << 13)) a -= (BASE << 13); \ if (a >= (BASE << 12)) a -= (BASE << 12); \ if (a >= (BASE << 11)) a -= (BASE << 11); \ if (a >= (BASE << 10)) a -= (BASE << 10); \ if (a >= (BASE << 9)) a -= (BASE << 9); \ if (a >= (BASE << 8)) a -= (BASE << 8); \ if (a >= (BASE << 7)) a -= (BASE << 7); \ if (a >= (BASE << 6)) a -= (BASE << 6); \ if (a >= (BASE << 5)) a -= (BASE << 5); \ if (a >= (BASE << 4)) a -= (BASE << 4); \ if (a >= (BASE << 3)) a -= (BASE << 3); \ if (a >= (BASE << 2)) a -= (BASE << 2); \ if (a >= (BASE << 1)) a -= (BASE << 1); \ if (a >= BASE) a -= BASE; \ } while (0) # define MOD4(a) \ do { \ if (a >= (BASE << 4)) a -= (BASE << 4); \ if (a >= (BASE << 3)) a -= (BASE << 3); \ if (a >= (BASE << 2)) a -= (BASE << 2); \ if (a >= (BASE << 1)) a -= (BASE << 1); \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE # define MOD4(a) a %= BASE #endif /* ========================================================================= */ uLong ZEXPORT adler32(adler, buf, len) uLong adler; const Bytef *buf; uInt len; { unsigned long sum2; unsigned n; /* split Adler-32 into component sums */ sum2 = (adler >> 16) & 0xffff; adler &= 0xffff; /* in case user likes doing a byte at a time, keep it fast */ if (len == 1) { adler += buf[0]; if (adler >= BASE) adler -= BASE; sum2 += adler; if (sum2 >= BASE) sum2 -= BASE; return adler | (sum2 << 16); } /* initial Adler-32 value (deferred check for len == 1 speed) */ if (buf == Z_NULL) return 1L; /* in case short lengths are provided, keep it somewhat fast */ if (len < 16) { while (len--) { adler += *buf++; sum2 += adler; } if (adler >= BASE) adler -= BASE; MOD4(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } /* do length NMAX blocks -- requires just one modulo operation */ while (len >= NMAX) { len -= NMAX; n = NMAX / 16; /* NMAX is divisible by 16 */ do { DO16(buf); /* 16 sums unrolled */ buf += 16; } while (--n); MOD(adler); MOD(sum2); } /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; DO16(buf); buf += 16; } while (len--) { adler += *buf++; sum2 += adler; } MOD(adler); MOD(sum2); } /* return recombined sums */ return adler | (sum2 << 16); } /* ========================================================================= */ local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { unsigned long sum1; unsigned long sum2; unsigned rem; /* the derivation of this formula is left as an exercise for the reader */ rem = (unsigned)(len2 % BASE); sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32_combine(adler1, adler2, len2) uLong adler1; uLong adler2; z_off_t len2; { return adler32_combine_(adler1, adler2, len2); } uLong ZEXPORT adler32_combine64(adler1, adler2, len2) uLong adler1; uLong adler2; z_off64_t len2; { return adler32_combine_(adler1, adler2, len2); } Indigo-indigo-1.2.3/third_party/zlib-src/src/compress.c000066400000000000000000000047301271037650300230510ustar00rootroot00000000000000/* compress.c -- compress a memory buffer * Copyright (C) 1995-2005 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; int level; { z_stream stream; int err; stream.next_in = (Bytef*)source; stream.avail_in = (uInt)sourceLen; #ifdef MAXSEG_64K /* Check for source > 64K on 16-bit machine: */ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; #endif stream.next_out = dest; stream.avail_out = (uInt)*destLen; if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = deflateInit(&stream, level); if (err != Z_OK) return err; err = deflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { deflateEnd(&stream); return err == Z_OK ? Z_BUF_ERROR : err; } *destLen = stream.total_out; err = deflateEnd(&stream); return err; } /* =========================================================================== */ int ZEXPORT compress (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } /* =========================================================================== If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ uLong ZEXPORT compressBound (sourceLen) uLong sourceLen; { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; } Indigo-indigo-1.2.3/third_party/zlib-src/src/crc32.c000066400000000000000000000325611271037650300221350ustar00rootroot00000000000000/* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2006, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing * tables for updating the shift register in one step with three exclusive-ors * instead of four steps with four exclusive-ors. This results in about a * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. */ /* @(#) $Id$ */ /* Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore protection on the static variables used to control the first-use generation of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). */ #ifdef MAKECRCH # include # ifndef DYNAMIC_CRC_TABLE # define DYNAMIC_CRC_TABLE # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ #include "zutil.h" /* for STDC and FAR definitions */ #define local static /* Find a four-byte integer type for crc32_little() and crc32_big(). */ #ifndef NOBYFOUR # ifdef STDC /* need ANSI C limits.h to determine sizes */ # include # define BYFOUR # if (UINT_MAX == 0xffffffffUL) typedef unsigned int u4; # else # if (ULONG_MAX == 0xffffffffUL) typedef unsigned long u4; # else # if (USHRT_MAX == 0xffffffffUL) typedef unsigned short u4; # else # undef BYFOUR /* can't find a four-byte integer type! */ # endif # endif # endif # endif /* STDC */ #endif /* !NOBYFOUR */ /* Definitions for doing the crc four data bytes at a time. */ #ifdef BYFOUR # define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ (((w)&0xff00)<<8)+(((w)&0xff)<<24)) local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, unsigned)); local unsigned long crc32_big OF((unsigned long, const unsigned char FAR *, unsigned)); # define TBLS 8 #else # define TBLS 1 #endif /* BYFOUR */ /* Local functions for crc concatenation */ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; local unsigned long FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH local void write_table OF((FILE *, const unsigned long FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. Polynomials over GF(2) are represented in binary, one bit per coefficient, with the lowest powers in the most significant bit. Then adding polynomials is just exclusive-or, and multiplying a polynomial by x is a right shift by one. If we call the above polynomial p, and represent a byte as the polynomial q, also with the lowest power in the most significant bit (so the byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, where a mod b means the remainder after dividing a by b. This calculation is done using the shift-register method of multiplying and taking the remainder. The register is initialized to zero, and for each incoming bit, x^32 is added mod p to the register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x (which is shifting right by one and adding x^32 mod p if the bit shifted out is a one). We start with the highest power (least significant bit) of q and repeat for all eight bits of q. The first table is simply the CRC of all possible eight bit values. This is all the information needed to generate CRCs on data a byte at a time for all combinations of CRC register values and incoming bytes. The remaining tables allow for word-at-a-time CRC calculation for both big-endian and little- endian machines, where a word is four bytes. */ local void make_crc_table() { unsigned long c; int n, k; unsigned long poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; /* See if another task is already doing this (not thread-safe, but better than nothing -- significantly reduces duration of vulnerability in case the advice about DYNAMIC_CRC_TABLE is ignored) */ if (first) { first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ poly = 0UL; for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) poly |= 1UL << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { c = (unsigned long)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; } #ifdef BYFOUR /* generate crc for each value followed by one, two, and three zeros, and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; crc_table[4][n] = REV(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; crc_table[k + 4][n] = REV(c); } } #endif /* BYFOUR */ crc_table_empty = 0; } else { /* not first */ /* wait for the other guy to finish (not efficient, but rare) */ while (crc_table_empty) ; } #ifdef MAKECRCH /* write out CRC tables to crc32.h */ { FILE *out; out = fopen("crc32.h", "w"); if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); fprintf(out, "local const unsigned long FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR fprintf(out, "#ifdef BYFOUR\n"); for (k = 1; k < 8; k++) { fprintf(out, " },\n {\n"); write_table(out, crc_table[k]); } fprintf(out, "#endif\n"); # endif /* BYFOUR */ fprintf(out, " }\n};\n"); fclose(out); } #endif /* MAKECRCH */ } #ifdef MAKECRCH local void write_table(out, table) FILE *out; const unsigned long FAR *table; { int n; for (n = 0; n < 256; n++) fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ #else /* !DYNAMIC_CRC_TABLE */ /* ======================================================================== * Tables of CRC-32s of all single-byte values, made by make_crc_table(). */ #include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ /* ========================================================================= * This function can be used by asm versions of crc32() */ const unsigned long FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ return (const unsigned long FAR *)crc_table; } /* ========================================================================= */ #define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 /* ========================================================================= */ unsigned long ZEXPORT crc32(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; uInt len; { if (buf == Z_NULL) return 0UL; #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { u4 endian; endian = 1; if (*((unsigned char *)(&endian))) return crc32_little(crc, buf, len); else return crc32_big(crc, buf, len); } #endif /* BYFOUR */ crc = crc ^ 0xffffffffUL; while (len >= 8) { DO8; len -= 8; } if (len) do { DO1; } while (--len); return crc ^ 0xffffffffUL; } #ifdef BYFOUR /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] #define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 /* ========================================================================= */ local unsigned long crc32_little(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; unsigned len; { register u4 c; register const u4 FAR *buf4; c = (u4)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } buf4 = (const u4 FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; } while (len >= 4) { DOLIT4; len -= 4; } buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); } while (--len); c = ~c; return (unsigned long)c; } /* ========================================================================= */ #define DOBIG4 c ^= *++buf4; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] #define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 /* ========================================================================= */ local unsigned long crc32_big(crc, buf, len) unsigned long crc; const unsigned char FAR *buf; unsigned len; { register u4 c; register const u4 FAR *buf4; c = REV((u4)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } buf4 = (const u4 FAR *)(const void FAR *)buf; buf4--; while (len >= 32) { DOBIG32; len -= 32; } while (len >= 4) { DOBIG4; len -= 4; } buf4++; buf = (const unsigned char FAR *)buf4; if (len) do { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; return (unsigned long)(REV(c)); } #endif /* BYFOUR */ #define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ /* ========================================================================= */ local unsigned long gf2_matrix_times(mat, vec) unsigned long *mat; unsigned long vec; { unsigned long sum; sum = 0; while (vec) { if (vec & 1) sum ^= *mat; vec >>= 1; mat++; } return sum; } /* ========================================================================= */ local void gf2_matrix_square(square, mat) unsigned long *square; unsigned long *mat; { int n; for (n = 0; n < GF2_DIM; n++) square[n] = gf2_matrix_times(mat, mat[n]); } /* ========================================================================= */ local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ /* degenerate case (also disallow negative lengths) */ if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; row <<= 1; } /* put operator for two zero bits in even */ gf2_matrix_square(even, odd); /* put operator for four zero bits in odd */ gf2_matrix_square(odd, even); /* apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) */ do { /* apply zeros operator for this bit of len2 */ gf2_matrix_square(even, odd); if (len2 & 1) crc1 = gf2_matrix_times(even, crc1); len2 >>= 1; /* if no more bits set, then done */ if (len2 == 0) break; /* another iteration of the loop with odd and even swapped */ gf2_matrix_square(odd, even); if (len2 & 1) crc1 = gf2_matrix_times(odd, crc1); len2 >>= 1; /* if no more bits set, then done */ } while (len2 != 0); /* return combined crc */ crc1 ^= crc2; return crc1; } /* ========================================================================= */ uLong ZEXPORT crc32_combine(crc1, crc2, len2) uLong crc1; uLong crc2; z_off_t len2; { return crc32_combine_(crc1, crc2, len2); } uLong ZEXPORT crc32_combine64(crc1, crc2, len2) uLong crc1; uLong crc2; z_off64_t len2; { return crc32_combine_(crc1, crc2, len2); } Indigo-indigo-1.2.3/third_party/zlib-src/src/deflate.c000066400000000000000000002046301271037650300226230ustar00rootroot00000000000000/* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in http://www.ietf.org/rfc/rfc1951.txt * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* =========================================================================== * Function prototypes. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func) OF((deflate_state *s, int flush)); /* Compression function. Returns the block state after the call. */ local void fill_window OF((deflate_state *s)); local block_state deflate_stored OF((deflate_state *s, int flush)); local block_state deflate_fast OF((deflate_state *s, int flush)); #ifndef FASTEST local block_state deflate_slow OF((deflate_state *s, int flush)); #endif local block_state deflate_rle OF((deflate_state *s, int flush)); local block_state deflate_huff OF((deflate_state *s, int flush)); local void lm_init OF((deflate_state *s)); local void putShortMSB OF((deflate_state *s, uInt b)); local void flush_pending OF((z_streamp strm)); local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ uInt longest_match OF((deflate_state *s, IPos cur_match)); #else local uInt longest_match OF((deflate_state *s, IPos cur_match)); #endif #ifdef DEBUG local void check_match OF((deflate_state *s, IPos start, IPos match, int length)); #endif /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; #ifdef FASTEST local const config configuration_table[2] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ #else local const config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ #endif /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ #define EQUAL 0 /* result of memcmp for equal strings */ #ifndef NO_DUMMY_DECL struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ #endif /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive * input characters, so that a running hash key can be computed from the * previous key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. * IN assertion: all calls to to INSERT_STRING are made with consecutive * input characters and the first MIN_MATCH bytes of str are valid * (except for the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #else #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #endif /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ s->head[s->hash_size-1] = NIL; \ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* ========================================================================= */ int ZEXPORT deflateInit_(strm, level, version, stream_size) z_streamp strm; int level; const char *version; int stream_size; { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size) z_streamp strm; int level; int method; int windowBits; int memLevel; int strategy; const char *version; int stream_size; { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; ushf *overlay; /* We overlay pending_buf and d_buf+l_buf. This works since the average * output size for (length,distance) codes is <= 24 bits. */ if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { strm->zalloc = zcalloc; strm->opaque = (voidpf)0; } if (strm->zfree == (free_func)0) strm->zfree = zcfree; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; windowBits = -windowBits; } #ifdef GZIP else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->wrap = wrap; s->gzhead = Z_NULL; s->w_bits = windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } s->d_buf = overlay + s->lit_bufsize/sizeof(ush); s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= */ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { deflate_state *s; uInt length = dictLength; uInt n; IPos hash_head = 0; if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || strm->state->wrap == 2 || (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) return Z_STREAM_ERROR; s = strm->state; if (s->wrap) strm->adler = adler32(strm->adler, dictionary, dictLength); if (length < MIN_MATCH) return Z_OK; if (length > s->w_size) { length = s->w_size; dictionary += dictLength - length; /* use the tail of the dictionary */ } zmemcpy(s->window, dictionary, length); s->strstart = length; s->block_start = (long)length; /* Insert all strings in the hash table (except for the last two bytes). * s->lookahead stays null, so s->ins_h will be recomputed at the next * call of fill_window. */ s->ins_h = s->window[0]; UPDATE_HASH(s, s->ins_h, s->window[1]); for (n = 0; n <= length - MIN_MATCH; n++) { INSERT_STRING(s, n, hash_head); } if (hash_head) hash_head = 0; /* to make compiler happy */ return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateReset (strm) z_streamp strm; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { return Z_STREAM_ERROR; } strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } s->status = s->wrap ? INIT_STATE : BUSY_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); s->last_flush = Z_NO_FLUSH; _tr_init(s); lm_init(s); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; { if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; if (strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; strm->state->bi_valid = bits; strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateParams(strm, level, strategy) z_streamp strm; int level; int strategy; { deflate_state *s; compress_func func; int err = Z_OK; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && strm->total_in != 0) { /* Flush the last buffer: */ err = deflate(strm, Z_BLOCK); } if (s->level != level) { s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return err; } /* ========================================================================= */ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) z_streamp strm; int good_length; int max_lazy; int nice_length; int max_chain; { deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; s = strm->state; s->good_match = good_length; s->max_lazy_match = max_lazy; s->nice_match = nice_length; s->max_chain_length = max_chain; return Z_OK; } /* ========================================================================= * For the default windowBits of 15 and memLevel of 8, this function returns * a close to exact, as well as small, upper bound on the compressed size. * They are coded as constants here for a reason--if the #define's are * changed, then this function needs to be changed as well. The return * value for 15 and 8 only works for those exact settings. * * For any setting other than those defaults for windowBits and memLevel, * the value returned is a conservative worst case for the maximum expansion * resulting from using fixed blocks instead of stored blocks, which deflate * can emit on compressed data for some combinations of the parameters. * * This function could be more sophisticated to provide closer upper bounds for * every combination of windowBits and memLevel. But even the conservative * upper bound of about 14% expansion does not seem onerous for output buffer * allocation. */ uLong ZEXPORT deflateBound(strm, sourceLen) z_streamp strm; uLong sourceLen; { deflate_state *s; uLong complen, wraplen; Bytef *str; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; /* if can't get parameters, return conservative bound plus zlib wrapper */ if (strm == Z_NULL || strm->state == Z_NULL) return complen + 6; /* compute wrapper length */ s = strm->state; switch (s->wrap) { case 0: /* raw deflate */ wraplen = 0; break; case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; if (str != Z_NULL) do { wraplen++; } while (*str++); if (s->gzhead->hcrc) wraplen += 2; } break; default: /* for compiler happiness */ wraplen = 6; } /* if not default parameters, return conservative bound */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) return complen + wraplen; /* default settings: return tight bound for that case */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB (s, b) deflate_state *s; uInt b; { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output goes * through this function so some applications may wish to modify it * to avoid allocating a large strm->next_out buffer and copying into it. * (See also read_buf()). */ local void flush_pending(strm) z_streamp strm; { unsigned len = strm->state->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; zmemcpy(strm->next_out, strm->state->pending_out, len); strm->next_out += len; strm->state->pending_out += len; strm->total_out += len; strm->avail_out -= len; strm->state->pending -= len; if (strm->state->pending == 0) { strm->state->pending_out = strm->state->pending_buf; } } /* ========================================================================= */ int ZEXPORT deflate (strm, flush) z_streamp strm; int flush; { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; /* Write the header */ if (s->status == INIT_STATE) { #ifdef GZIP if (s->wrap == 2) { strm->adler = crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s->status = BUSY_STATE; } else { put_byte(s, (s->gzhead->text ? 1 : 0) + (s->gzhead->hcrc ? 2 : 0) + (s->gzhead->extra == Z_NULL ? 0 : 4) + (s->gzhead->name == Z_NULL ? 0 : 8) + (s->gzhead->comment == Z_NULL ? 0 : 16) ); put_byte(s, (Byte)(s->gzhead->time & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } if (s->gzhead->hcrc) strm->adler = crc32(strm->adler, s->pending_buf, s->pending); s->gzindex = 0; s->status = EXTRA_STATE; } } else #endif { uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) level_flags = 0; else if (s->level < 6) level_flags = 1; else if (s->level == 6) level_flags = 2; else level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); s->status = BUSY_STATE; putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = adler32(0L, Z_NULL, 0); } } #ifdef GZIP if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) break; } put_byte(s, s->gzhead->extra[s->gzindex]); s->gzindex++; } if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (s->gzindex == s->gzhead->extra_len) { s->gzindex = 0; s->status = NAME_STATE; } } else s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) { val = 1; break; } } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (val == 0) { s->gzindex = 0; s->status = COMMENT_STATE; } } else s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); flush_pending(strm); beg = s->pending; if (s->pending == s->pending_buf_size) { val = 1; break; } } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); if (s->gzhead->hcrc && s->pending > beg) strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); if (val == 0) s->status = HCRC_STATE; } else s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { if (s->pending + 2 > s->pending_buf_size) flush_pending(strm); if (s->pending + 2 <= s->pending_buf_size) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); strm->adler = crc32(0L, Z_NULL, 0); s->status = BUSY_STATE; } } else s->status = BUSY_STATE; } #endif /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm->avail_in == 0 && flush <= old_flush && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : (s->strategy == Z_RLE ? deflate_rle(s, flush) : (*(configuration_table[s->level].func))(s, flush)); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; } } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } Assert(strm->avail_out > 0, "bug2"); if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; /* Write the trailer */ #ifdef GZIP if (s->wrap == 2) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); put_byte(s, (Byte)(strm->total_in & 0xff)); put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); } else #endif { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int ZEXPORT deflateEnd (strm) z_streamp strm; { int status; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; status = strm->state->status; if (status != INIT_STATE && status != EXTRA_STATE && status != NAME_STATE && status != COMMENT_STATE && status != HCRC_STATE && status != BUSY_STATE && status != FINISH_STATE) { return Z_STREAM_ERROR; } /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ int ZEXPORT deflateCopy (dest, source) z_streamp dest; z_streamp source; { #ifdef MAXSEG_64K return Z_STREAM_ERROR; #else deflate_state *ds; deflate_state *ss; ushf *overlay; if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { return Z_STREAM_ERROR; } ss = source->state; zmemcpy(dest, source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy(ds, ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); ds->pending_buf = (uchf *) overlay; if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; #endif /* MAXSEG_64K */ } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local int read_buf(strm, buf, size) z_streamp strm; Bytef *buf; unsigned size; { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; if (strm->state->wrap == 1) { strm->adler = adler32(strm->adler, strm->next_in, len); } #ifdef GZIP else if (strm->state->wrap == 2) { strm->adler = crc32(strm->adler, strm->next_in, len); } #endif zmemcpy(buf, strm->next_in, len); strm->next_in += len; strm->total_in += len; return (int)len; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init (s) deflate_state *s; { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; #ifndef FASTEST #ifdef ASMV match_init(); /* initialize the asm code */ #endif #endif } #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ #ifndef ASMV /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan+best_len-1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len-1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match+best_len-1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart+3, +5, ... up to strstart+257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && *(ushf*)(scan+=2) == *(ushf*)(match+=2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window+strstart+257 */ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len-1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan+best_len-1); #else scan_end1 = scan[best_len-1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } #endif /* ASMV */ #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ local uInt longest_match(s, cur_match) deflate_state *s; IPos cur_match; /* current match */ { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Return failure if the match length is less than 2: */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; /* The check at best_len-1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match += 2; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); if (len < MIN_MATCH) return MIN_MATCH - 1; s->match_start = cur_match; return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } #endif /* FASTEST */ #ifdef DEBUG /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(s, start, match, length) deflate_state *s; IPos start, match; int length; { /* check that the match is indeed a match */ if (zmemcmp(s->window + match, s->window + start, length) != EQUAL) { fprintf(stderr, " start %u, match %u, length %d\n", start, match, length); do { fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); } while (--length != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start-match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* DEBUG */ /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(s) deflate_state *s; { register unsigned n, m; register Posf *p; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (sizeof(int) <= 2) { if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if * strstart == 0 && lookahead == 1 (input done a byte at time) */ more--; } } /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s->strstart >= wsize+MAX_DIST(s)) { zmemcpy(s->window, s->window+wsize, (unsigned)wsize); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; /* Slide the hash table (could be avoided with 32 bit values at the expense of memory usage). We slide even when level == 0 to keep the hash table consistent if we switch back to level > 0 later. (Using level 0 permanently is not an optimal usage of zlib, so we don't care about this pathological case.) */ n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); } while (--n); n = wsize; #ifndef FASTEST p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m-wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); #endif more += wsize; } if (s->strm->avail_in == 0) return; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead >= MIN_MATCH) { s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ if (s->high_water < s->window_size) { ulg curr = s->strstart + (ulg)(s->lookahead); ulg init; if (s->high_water < curr) { /* Previous high water mark below current data -- zero WIN_INIT * bytes or up to end of window, whichever is less. */ init = s->window_size - curr; if (init > WIN_INIT) init = WIN_INIT; zmemzero(s->window + curr, (unsigned)init); s->high_water = curr + init; } else if (s->high_water < (ulg)curr + WIN_INIT) { /* High water mark at or above current data, but below current data * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up * to end of window, whichever is less. */ init = (ulg)curr + WIN_INIT - s->high_water; if (init > s->window_size - s->high_water) init = s->window_size - s->high_water; zmemzero(s->window + s->high_water, (unsigned)init); s->high_water += init; } } } /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, last) { \ FLUSH_BLOCK_ONLY(s, last); \ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * This function does not insert new strings in the dictionary since * uncompressible data is probably not useful. This function is used * only for the level=0 compression option. * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ local block_state deflate_stored(s, flush) deflate_state *s; int flush; { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: */ ulg max_block_size = 0xffff; ulg max_start; if (max_block_size > s->pending_buf_size - 5) { max_block_size = s->pending_buf_size - 5; } /* Copy as much as possible from input to output: */ for (;;) { /* Fill the window as much as possible: */ if (s->lookahead <= 1) { Assert(s->strstart < s->w_size+MAX_DIST(s) || s->block_start >= (long)s->w_size, "slide too late"); fill_window(s); if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; if (s->lookahead == 0) break; /* flush the current block */ } Assert(s->block_start >= 0L, "block gone"); s->strstart += s->lookahead; s->lookahead = 0; /* Emit a stored block if pending_buf will be full: */ max_start = s->block_start + max_block_size; if (s->strstart == 0 || (ulg)s->strstart >= max_start) { /* strstart == 0 is possible when wraparound on 16-bit machine */ s->lookahead = (uInt)(s->strstart - max_start); s->strstart = (uInt)max_start; FLUSH_BLOCK(s, 0); } /* Flush if we may have to slide, otherwise block_start may become * negative and the data will be gone: */ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { FLUSH_BLOCK(s, 0); } } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); _tr_tally_dist(s, s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ #ifndef FASTEST if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else #endif { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } #ifndef FASTEST /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(s, flush) deflate_state *s; int flush; { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart+2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR) #endif )) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart-1, s->prev_match, s->prev_length); _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length-1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart-1])); _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } #endif /* FASTEST */ /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ local block_state deflate_rle(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest encodable run. */ if (s->lookahead < MAX_MATCH) { fill_window(s); if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ s->match_length = 0; if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; prev = *scan; if (prev == *++scan && prev == *++scan && prev == *++scan) { strend = s->window + s->strstart + MAX_MATCH; do { } while (prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); s->match_length = MAX_MATCH - (int)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->strstart - 1, s->match_length); _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; s->strstart += s->match_length; s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ local block_state deflate_huff(s, flush) deflate_state *s; int flush; { int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s->lookahead == 0) { fill_window(s); if (s->lookahead == 0) { if (flush == Z_NO_FLUSH) return need_more; break; /* flush the current block */ } } /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit (s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } FLUSH_BLOCK(s, flush == Z_FINISH); return flush == Z_FINISH ? finish_done : block_done; } Indigo-indigo-1.2.3/third_party/zlib-src/src/gzclose.c000066400000000000000000000012461271037650300226630ustar00rootroot00000000000000/* gzclose.c -- zlib gzclose() function * Copyright (C) 2004, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ int ZEXPORT gzclose(file) gzFile file; { #ifndef NO_GZCOMPRESS gz_statep state; if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); #else return gzclose_r(file); #endif } Indigo-indigo-1.2.3/third_party/zlib-src/src/gzlib.c000066400000000000000000000333671271037650300223350ustar00rootroot00000000000000/* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 # define LSEEK lseek64 #else # define LSEEK lseek #endif /* Local functions */ local void gz_reset OF((gz_statep)); local gzFile gz_open OF((const char *, int, const char *)); #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message string and return a pointer to it. Typically, the values for ERROR come from GetLastError. The string pointed to shall not be modified by the application, but may be overwritten by a subsequent call to gz_strwinerror The gz_strwinerror function does not change the current setting of GetLastError. */ char ZLIB_INTERNAL *gz_strwinerror (error) DWORD error; { static char buf[1024]; wchar_t *msgbuf; DWORD lasterr = GetLastError(); DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, 0, /* Default language */ (LPVOID)&msgbuf, 0, NULL); if (chars != 0) { /* If there is an \r\n appended, zap it. */ if (chars >= 2 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { chars -= 2; msgbuf[chars] = 0; } if (chars > sizeof (buf) - 1) { chars = sizeof (buf) - 1; msgbuf[chars] = 0; } wcstombs(buf, msgbuf, chars + 1); LocalFree(msgbuf); } else { sprintf(buf, "unknown win32 error (%ld)", error); } SetLastError(lasterr); return buf; } #endif /* UNDER_CE */ /* Reset gzip file state */ local void gz_reset(state) gz_statep state; { if (state->mode == GZ_READ) { /* for reading ... */ state->have = 0; /* no output data available */ state->eof = 0; /* not at end of file */ state->how = LOOK; /* look for gzip header */ state->direct = 1; /* default for empty file */ } state->seek = 0; /* no seek request pending */ gz_error(state, Z_OK, NULL); /* clear error */ state->pos = 0; /* no uncompressed data yet */ state->strm.avail_in = 0; /* no input data yet */ } /* Open a gzip file either by name or file descriptor. */ local gzFile gz_open(path, fd, mode) const char *path; int fd; const char *mode; { gz_statep state; /* allocate gzFile structure to return */ state = malloc(sizeof(gz_state)); if (state == NULL) return NULL; state->size = 0; /* no buffers allocated yet */ state->want = GZBUFSIZE; /* requested buffer size */ state->msg = NULL; /* no error message yet */ /* interpret mode */ state->mode = GZ_NONE; state->level = Z_DEFAULT_COMPRESSION; state->strategy = Z_DEFAULT_STRATEGY; while (*mode) { if (*mode >= '0' && *mode <= '9') state->level = *mode - '0'; else switch (*mode) { case 'r': state->mode = GZ_READ; break; #ifndef NO_GZCOMPRESS case 'w': state->mode = GZ_WRITE; break; case 'a': state->mode = GZ_APPEND; break; #endif case '+': /* can't read and write at the same time */ free(state); return NULL; case 'b': /* ignore -- will request binary anyway */ break; case 'f': state->strategy = Z_FILTERED; break; case 'h': state->strategy = Z_HUFFMAN_ONLY; break; case 'R': state->strategy = Z_RLE; break; case 'F': state->strategy = Z_FIXED; default: /* could consider as an error, but just ignore */ ; } mode++; } /* must provide an "r", "w", or "a" */ if (state->mode == GZ_NONE) { free(state); return NULL; } /* save the path name for error messages */ state->path = malloc(strlen(path) + 1); if (state->path == NULL) { free(state); return NULL; } strcpy(state->path, path); /* open the file with the appropriate mode (or just use fd) */ state->fd = fd != -1 ? fd : open(path, #ifdef O_LARGEFILE O_LARGEFILE | #endif #ifdef O_BINARY O_BINARY | #endif (state->mode == GZ_READ ? O_RDONLY : (O_WRONLY | O_CREAT | ( state->mode == GZ_WRITE ? O_TRUNC : O_APPEND))), 0666); if (state->fd == -1) { free(state->path); free(state); return NULL; } if (state->mode == GZ_APPEND) state->mode = GZ_WRITE; /* simplify later checks */ /* save the current position for rewinding (only if reading) */ if (state->mode == GZ_READ) { state->start = LSEEK(state->fd, 0, SEEK_CUR); if (state->start == -1) state->start = 0; } /* initialize stream */ gz_reset(state); /* return stream */ return (gzFile)state; } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen(path, mode) const char *path; const char *mode; { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen64(path, mode) const char *path; const char *mode; { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzdopen(fd, mode) int fd; const char *mode; { char *path; /* identifier for error messages */ gzFile gz; if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) return NULL; sprintf(path, "", fd); /* for debugging */ gz = gz_open(path, fd, mode); free(path); return gz; } /* -- see zlib.h -- */ int ZEXPORT gzbuffer(file, size) gzFile file; unsigned size; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* make sure we haven't already allocated memory */ if (state->size != 0) return -1; /* check and set requested size */ if (size == 0) return -1; state->want = size; return 0; } /* -- see zlib.h -- */ int ZEXPORT gzrewind(file) gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || state->err != Z_OK) return -1; /* back up and start over */ if (LSEEK(state->fd, state->start, SEEK_SET) == -1) return -1; gz_reset(state); return 0; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzseek64(file, offset, whence) gzFile file; z_off64_t offset; int whence; { unsigned n; z_off64_t ret; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* check that there's no error */ if (state->err != Z_OK) return -1; /* can only seek from start or relative to current position */ if (whence != SEEK_SET && whence != SEEK_CUR) return -1; /* normalize offset to a SEEK_CUR specification */ if (whence == SEEK_SET) offset -= state->pos; else if (state->seek) offset += state->skip; state->seek = 0; /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->pos + offset >= state->raw) { ret = LSEEK(state->fd, offset - state->have, SEEK_CUR); if (ret == -1) return -1; state->have = 0; state->eof = 0; state->seek = 0; gz_error(state, Z_OK, NULL); state->strm.avail_in = 0; state->pos += offset; return state->pos; } /* calculate skip amount, rewinding if needed for back seek when reading */ if (offset < 0) { if (state->mode != GZ_READ) /* writing -- can't go backwards */ return -1; offset += state->pos; if (offset < 0) /* before start of file! */ return -1; if (gzrewind(file) == -1) /* rewind, then skip to offset */ return -1; } /* if reading, skip what's in output buffer (one less gzgetc() check) */ if (state->mode == GZ_READ) { n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? (unsigned)offset : state->have; state->have -= n; state->next += n; state->pos += n; offset -= n; } /* request skip (if not zero) */ if (offset) { state->seek = 1; state->skip = offset; } return state->pos + offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzseek(file, offset, whence) gzFile file; z_off_t offset; int whence; { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gztell64(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* return position */ return state->pos + (state->seek ? state->skip : 0); } /* -- see zlib.h -- */ z_off_t ZEXPORT gztell(file) gzFile file; { z_off64_t ret; ret = gztell64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzoffset64(file) gzFile file; { z_off64_t offset; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* compute and return effective offset in file */ offset = LSEEK(state->fd, 0, SEEK_CUR); if (offset == -1) return -1; if (state->mode == GZ_READ) /* reading */ offset -= state->strm.avail_in; /* don't count buffered input */ return offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzoffset(file) gzFile file; { z_off64_t ret; ret = gzoffset64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ int ZEXPORT gzeof(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return 0; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return 0; /* return end-of-file state */ return state->mode == GZ_READ ? (state->eof && state->strm.avail_in == 0 && state->have == 0) : 0; } /* -- see zlib.h -- */ const char * ZEXPORT gzerror(file, errnum) gzFile file; int *errnum; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return NULL; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return NULL; /* return error information */ if (errnum != NULL) *errnum = state->err; return state->msg == NULL ? "" : state->msg; } /* -- see zlib.h -- */ void ZEXPORT gzclearerr(file) gzFile file; { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return; /* clear error and end-of-file */ if (state->mode == GZ_READ) state->eof = 0; gz_error(state, Z_OK, NULL); } /* Create an error message in allocated memory and set state->err and state->msg accordingly. Free any previous error message already there. Do not try to free or allocate space if the error is Z_MEM_ERROR (out of memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ void ZLIB_INTERNAL gz_error(state, err, msg) gz_statep state; int err; const char *msg; { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) free(state->msg); state->msg = NULL; } /* set error code, and if no message, then done */ state->err = err; if (msg == NULL) return; /* for an out of memory error, save as static string */ if (err == Z_MEM_ERROR) { state->msg = (char *)msg; return; } /* construct error message with path */ if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { state->err = Z_MEM_ERROR; state->msg = (char *)"out of memory"; return; } strcpy(state->msg, state->path); strcat(state->msg, ": "); strcat(state->msg, msg); return; } #ifndef INT_MAX /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ unsigned ZLIB_INTERNAL gz_intmax() { unsigned p, q; p = 1; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; } #endif Indigo-indigo-1.2.3/third_party/zlib-src/src/gzread.c000066400000000000000000000501361271037650300224730ustar00rootroot00000000000000/* gzread.c -- zlib functions for reading gzip files * Copyright (C) 2004, 2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Local functions */ local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); local int gz_avail OF((gz_statep)); local int gz_next4 OF((gz_statep, unsigned long *)); local int gz_head OF((gz_statep)); local int gz_decomp OF((gz_statep)); local int gz_make OF((gz_statep)); local int gz_skip OF((gz_statep, z_off64_t)); /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ local int gz_load(state, buf, len, have) gz_statep state; unsigned char *buf; unsigned len; unsigned *have; { int ret; *have = 0; do { ret = read(state->fd, buf + *have, len - *have); if (ret <= 0) break; *have += ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } if (ret == 0) state->eof = 1; return 0; } /* Load up input buffer and set eof flag if last data loaded -- return -1 on error, 0 otherwise. Note that the eof flag is set when the end of the input file is reached, even though there may be unused data in the buffer. Once that data has been used, no more attempts will be made to read the file. gz_avail() assumes that strm->avail_in == 0. */ local int gz_avail(state) gz_statep state; { z_streamp strm = &(state->strm); if (state->err != Z_OK) return -1; if (state->eof == 0) { if (gz_load(state, state->in, state->size, (unsigned *)&(strm->avail_in)) == -1) return -1; strm->next_in = state->in; } return 0; } /* Get next byte from input, or -1 if end or error. */ #define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \ (strm->avail_in == 0 ? -1 : \ (strm->avail_in--, *(strm->next_in)++))) /* Get a four-byte little-endian integer and return 0 on success and the value in *ret. Otherwise -1 is returned and *ret is not modified. */ local int gz_next4(state, ret) gz_statep state; unsigned long *ret; { int ch; unsigned long val; z_streamp strm = &(state->strm); val = NEXT(); val += (unsigned)NEXT() << 8; val += (unsigned long)NEXT() << 16; ch = NEXT(); if (ch == -1) return -1; val += (unsigned long)ch << 24; *ret = val; return 0; } /* Look for gzip header, set up for inflate or copy. state->have must be zero. If this is the first time in, allocate required memory. state->how will be left unchanged if there is no more input data available, will be set to COPY if there is no gzip header and direct copying will be performed, or it will be set to GZIP for decompression, and the gzip header will be skipped so that the next available input data is the raw deflate stream. If direct copying, then leftover input data from the input buffer will be copied to the output buffer. In that case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state and the check value will be initialized. gz_head() will return 0 on success or -1 on failure. Failures may include read errors or gzip header errors. */ local int gz_head(state) gz_statep state; { z_streamp strm = &(state->strm); int flags; unsigned len; /* allocate read buffers and inflate memory */ if (state->size == 0) { /* allocate buffers */ state->in = malloc(state->want); state->out = malloc(state->want << 1); if (state->in == NULL || state->out == NULL) { if (state->out != NULL) free(state->out); if (state->in != NULL) free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } state->size = state->want; /* allocate inflate memory */ state->strm.zalloc = Z_NULL; state->strm.zfree = Z_NULL; state->strm.opaque = Z_NULL; state->strm.avail_in = 0; state->strm.next_in = Z_NULL; if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ free(state->out); free(state->in); state->size = 0; gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } } /* get some data in the input buffer */ if (strm->avail_in == 0) { if (gz_avail(state) == -1) return -1; if (strm->avail_in == 0) return 0; } /* look for the gzip magic header bytes 31 and 139 */ if (strm->next_in[0] == 31) { strm->avail_in--; strm->next_in++; if (strm->avail_in == 0 && gz_avail(state) == -1) return -1; if (strm->avail_in && strm->next_in[0] == 139) { /* we have a gzip header, woo hoo! */ strm->avail_in--; strm->next_in++; /* skip rest of header */ if (NEXT() != 8) { /* compression method */ gz_error(state, Z_DATA_ERROR, "unknown compression method"); return -1; } flags = NEXT(); if (flags & 0xe0) { /* reserved flag bits */ gz_error(state, Z_DATA_ERROR, "unknown header flags set"); return -1; } NEXT(); /* modification time */ NEXT(); NEXT(); NEXT(); NEXT(); /* extra flags */ NEXT(); /* operating system */ if (flags & 4) { /* extra field */ len = (unsigned)NEXT(); len += (unsigned)NEXT() << 8; while (len--) if (NEXT() < 0) break; } if (flags & 8) /* file name */ while (NEXT() > 0) ; if (flags & 16) /* comment */ while (NEXT() > 0) ; if (flags & 2) { /* header crc */ NEXT(); NEXT(); } /* an unexpected end of file is not checked for here -- it will be noticed on the first request for uncompressed data */ /* set up for decompression */ inflateReset(strm); strm->adler = crc32(0L, Z_NULL, 0); state->how = GZIP; state->direct = 0; return 0; } else { /* not a gzip file -- save first byte (31) and fall to raw i/o */ state->out[0] = 31; state->have = 1; } } /* doing raw i/o, save start of raw data for seeking, copy any leftover input to output -- this assumes that the output buffer is larger than the input buffer, which also assures space for gzungetc() */ state->raw = state->pos; state->next = state->out; if (strm->avail_in) { memcpy(state->next + state->have, strm->next_in, strm->avail_in); state->have += strm->avail_in; strm->avail_in = 0; } state->how = COPY; state->direct = 1; return 0; } /* Decompress from input to the provided next_out and avail_out in the state. If the end of the compressed data is reached, then verify the gzip trailer check value and length (modulo 2^32). state->have and state->next are set to point to the just decompressed data, and the crc is updated. If the trailer is verified, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->have is depleted. Returns 0 on success, -1 on failure. Failures may include invalid compressed data or a failed gzip trailer verification. */ local int gz_decomp(state) gz_statep state; { int ret; unsigned had; unsigned long crc, len; z_streamp strm = &(state->strm); /* fill output buffer up to end of deflate stream */ had = strm->avail_out; do { /* get more input for inflate() */ if (strm->avail_in == 0 && gz_avail(state) == -1) return -1; if (strm->avail_in == 0) { gz_error(state, Z_DATA_ERROR, "unexpected end of file"); return -1; } /* decompress and handle errors */ ret = inflate(strm, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { gz_error(state, Z_STREAM_ERROR, "internal error: inflate stream corrupt"); return -1; } if (ret == Z_MEM_ERROR) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ gz_error(state, Z_DATA_ERROR, strm->msg == NULL ? "compressed data error" : strm->msg); return -1; } } while (strm->avail_out && ret != Z_STREAM_END); /* update available output and crc check value */ state->have = had - strm->avail_out; state->next = strm->next_out - state->have; strm->adler = crc32(strm->adler, state->next, state->have); /* check gzip trailer if at end of deflate stream */ if (ret == Z_STREAM_END) { if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { gz_error(state, Z_DATA_ERROR, "unexpected end of file"); return -1; } if (crc != strm->adler) { gz_error(state, Z_DATA_ERROR, "incorrect data check"); return -1; } if (len != (strm->total_out & 0xffffffffL)) { gz_error(state, Z_DATA_ERROR, "incorrect length check"); return -1; } state->how = LOOK; /* ready for next stream, once have is 0 (leave state->direct unchanged to remember how) */ } /* good decompression */ return 0; } /* Make data and put in the output buffer. Assumes that state->have == 0. Data is either copied from the input file or decompressed from the input file depending on state->how. If state->how is LOOK, then a gzip header is looked for (and skipped if found) to determine wither to copy or decompress. Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ local int gz_make(state) gz_statep state; { z_streamp strm = &(state->strm); if (state->how == LOOK) { /* look for gzip header */ if (gz_head(state) == -1) return -1; if (state->have) /* got some data from gz_head() */ return 0; } if (state->how == COPY) { /* straight copy */ if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1) return -1; state->next = state->out; } else if (state->how == GZIP) { /* decompress */ strm->avail_out = state->size << 1; strm->next_out = state->out; if (gz_decomp(state) == -1) return -1; } return 0; } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ local int gz_skip(state, len) gz_statep state; z_off64_t len; { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ while (len) /* skip over whatever is in output buffer */ if (state->have) { n = GT_OFF(state->have) || (z_off64_t)state->have > len ? (unsigned)len : state->have; state->have -= n; state->next += n; state->pos += n; len -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && state->strm.avail_in == 0) break; /* need more data to skip -- load up output buffer */ else { /* get more output, looking for header if required */ if (gz_make(state) == -1) return -1; } return 0; } /* -- see zlib.h -- */ int ZEXPORT gzread(file, buf, len) gzFile file; voidp buf; unsigned len; { unsigned got, n; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || state->err != Z_OK) return -1; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids the flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); return -1; } /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return -1; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { /* first just try copying data from the output buffer */ if (state->have) { n = state->have > len ? len : state->have; memcpy(buf, state->next, n); state->next += n; state->have -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && strm->avail_in == 0) break; /* need output data -- for small len or new stream load up our output buffer */ else if (state->how == LOOK || len < (state->size << 1)) { /* get more output, looking for header if required */ if (gz_make(state) == -1) return -1; continue; /* no progress yet -- go back to memcpy() above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ } /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ if (gz_load(state, buf, len, &n) == -1) return -1; } /* large len -- decompress directly into user buffer */ else { /* state->how == GZIP */ strm->avail_out = len; strm->next_out = buf; if (gz_decomp(state) == -1) return -1; n = state->have; state->have = 0; } /* update progress */ len -= n; buf = (char *)buf + n; got += n; state->pos += n; } while (len); /* return number of bytes read into user buffer (will fit in int) */ return (int)got; } /* -- see zlib.h -- */ int ZEXPORT gzgetc(file) gzFile file; { int ret; unsigned char buf[1]; gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || state->err != Z_OK) return -1; /* try output buffer (no need to check for skip request) */ if (state->have) { state->have--; state->pos++; return *(state->next)++; } /* nothing there -- try gzread() */ ret = gzread(file, buf, 1); return ret < 1 ? -1 : buf[0]; } /* -- see zlib.h -- */ int ZEXPORT gzungetc(c, file) int c; gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || state->err != Z_OK) return -1; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return -1; } /* can't push EOF */ if (c < 0) return -1; /* if output buffer empty, put byte at end (allows more pushing) */ if (state->have == 0) { state->have = 1; state->next = state->out + (state->size << 1) - 1; state->next[0] = c; state->pos--; return c; } /* if no room, give up (must have already done a gzungetc()) */ if (state->have == (state->size << 1)) { gz_error(state, Z_BUF_ERROR, "out of room to push characters"); return -1; } /* slide output data if needed and insert byte before existing data */ if (state->next == state->out) { unsigned char *src = state->out + state->have; unsigned char *dest = state->out + (state->size << 1); while (src > state->out) *--dest = *--src; state->next = dest; } state->have++; state->next--; state->next[0] = c; state->pos--; return c; } /* -- see zlib.h -- */ char * ZEXPORT gzgets(file, buf, len) gzFile file; char *buf; int len; { unsigned left, n; char *str; unsigned char *eol; gz_statep state; /* check parameters and get internal structure */ if (file == NULL || buf == NULL || len < 1) return NULL; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || state->err != Z_OK) return NULL; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return NULL; } /* copy output bytes up to new line or len - 1, whichever comes first -- append a terminating zero to the string (we don't check for a zero in the contents, let the user worry about that) */ str = buf; left = (unsigned)len - 1; if (left) do { /* assure that something is in the output buffer */ if (state->have == 0) { if (gz_make(state) == -1) return NULL; /* error */ if (state->have == 0) { /* end of file */ if (buf == str) /* got bupkus */ return NULL; break; /* got something -- return it */ } } /* look for end-of-line in current output buffer */ n = state->have > left ? left : state->have; eol = memchr(state->next, '\n', n); if (eol != NULL) n = (unsigned)(eol - state->next) + 1; /* copy through end-of-line, or remainder if not found */ memcpy(buf, state->next, n); state->have -= n; state->next += n; state->pos += n; left -= n; buf += n; } while (left && eol == NULL); /* found end-of-line or out of space -- terminate string and return it */ buf[0] = 0; return str; } /* -- see zlib.h -- */ int ZEXPORT gzdirect(file) gzFile file; { gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* check that we're reading */ if (state->mode != GZ_READ) return 0; /* if the state is not known, but we can find out, then do so (this is mainly for right after a gzopen() or gzdopen()) */ if (state->how == LOOK && state->have == 0) (void)gz_head(state); /* return 1 if reading direct, 0 if decompressing a gzip stream */ return state->direct; } /* -- see zlib.h -- */ int ZEXPORT gzclose_r(file) gzFile file; { int ret; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're reading */ if (state->mode != GZ_READ) return Z_STREAM_ERROR; /* free memory and close file */ if (state->size) { inflateEnd(&(state->strm)); free(state->out); free(state->in); } gz_error(state, Z_OK, NULL); free(state->path); ret = close(state->fd); free(state); return ret ? Z_ERRNO : Z_OK; } Indigo-indigo-1.2.3/third_party/zlib-src/src/gzwrite.c000066400000000000000000000344121271037650300227110ustar00rootroot00000000000000/* gzwrite.c -- zlib functions for writing gzip files * Copyright (C) 2004, 2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Local functions */ local int gz_init OF((gz_statep)); local int gz_comp OF((gz_statep, int)); local int gz_zero OF((gz_statep, z_off64_t)); /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on failure or 0 on success. */ local int gz_init(state) gz_statep state; { int ret; z_streamp strm = &(state->strm); /* allocate input and output buffers */ state->in = malloc(state->want); state->out = malloc(state->want); if (state->in == NULL || state->out == NULL) { if (state->out != NULL) free(state->out); if (state->in != NULL) free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* allocate deflate memory, set up for gzip compression */ strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; ret = deflateInit2(strm, state->level, Z_DEFLATED, 15 + 16, 8, state->strategy); if (ret != Z_OK) { free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* mark state as initialized */ state->size = state->want; /* initialize write buffer */ strm->avail_out = state->size; strm->next_out = state->out; state->next = strm->next_out; return 0; } /* Compress whatever is at avail_in and next_in and write to the output file. Return -1 if there is an error writing to the output file, otherwise 0. flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. */ local int gz_comp(state, flush) gz_statep state; int flush; { int ret, got; unsigned have; z_streamp strm = &(state->strm); /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return -1; /* run deflate() on provided input until it produces no more output */ ret = Z_OK; do { /* write out current buffer contents if full, or if flushing, but if doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { have = (unsigned)(strm->next_out - state->next); if (have && ((got = write(state->fd, state->next, have)) < 0 || (unsigned)got != have)) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } if (strm->avail_out == 0) { strm->avail_out = state->size; strm->next_out = state->out; } state->next = strm->next_out; } /* compress */ have = strm->avail_out; ret = deflate(strm, flush); if (ret == Z_STREAM_ERROR) { gz_error(state, Z_STREAM_ERROR, "internal error: deflate stream corrupt"); return -1; } have -= strm->avail_out; } while (have); /* if that completed a deflate stream, allow another to start */ if (flush == Z_FINISH) deflateReset(strm); /* all done, no errors */ return 0; } /* Compress len zeros to output. Return -1 on error, 0 on success. */ local int gz_zero(state, len) gz_statep state; z_off64_t len; { int first; unsigned n; z_streamp strm = &(state->strm); /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return -1; /* compress len zeros (len guaranteed > 0) */ first = 1; while (len) { n = GT_OFF(state->size) || (z_off64_t)state->size > len ? (unsigned)len : state->size; if (first) { memset(state->in, 0, n); first = 0; } strm->avail_in = n; strm->next_in = state->in; state->pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) return -1; len -= n; } return 0; } /* -- see zlib.h -- */ int ZEXPORT gzwrite(file, buf, len) gzFile file; voidpc buf; unsigned len; { unsigned put = len; unsigned n; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids the flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); return 0; } /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* for small len, copy to input buffer, otherwise compress directly */ if (len < state->size) { /* copy to input buffer, compress when full */ do { if (strm->avail_in == 0) strm->next_in = state->in; n = state->size - strm->avail_in; if (n > len) n = len; memcpy(strm->next_in + strm->avail_in, buf, n); strm->avail_in += n; state->pos += n; buf = (char *)buf + n; len -= n; if (len && gz_comp(state, Z_NO_FLUSH) == -1) return 0; } while (len); } else { /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ strm->avail_in = len; strm->next_in = (voidp)buf; state->pos += len; if (gz_comp(state, Z_NO_FLUSH) == -1) return 0; } /* input was all buffered or compressed (put will fit in int) */ return (int)put; } /* -- see zlib.h -- */ int ZEXPORT gzputc(file, c) gzFile file; int c; { unsigned char buf[1]; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return -1; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* try writing to input buffer for speed (state->size == 0 if buffer not initialized) */ if (strm->avail_in < state->size) { if (strm->avail_in == 0) strm->next_in = state->in; strm->next_in[strm->avail_in++] = c; state->pos++; return c; } /* no room in buffer or not initialized, use gz_write() */ buf[0] = c; if (gzwrite(file, buf, 1) != 1) return -1; return c; } /* -- see zlib.h -- */ int ZEXPORT gzputs(file, str) gzFile file; const char *str; { int ret; unsigned len; /* write string */ len = (unsigned)strlen(str); ret = gzwrite(file, str, len); return ret == 0 && len != 0 ? -1 : ret; } #ifdef STDC #include /* -- see zlib.h -- */ int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) { int size, len; gz_statep state; z_streamp strm; va_list va; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* do the printf() into the input buffer, put length in len */ size = (int)(state->size); state->in[size - 1] = 0; va_start(va, format); #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void (void)vsprintf(state->in, format, va); va_end(va); for (len = 0; len < size; len++) if (state->in[len] == 0) break; # else len = vsprintf(state->in, format, va); va_end(va); # endif #else # ifdef HAS_vsnprintf_void (void)vsnprintf(state->in, size, format, va); va_end(va); len = strlen(state->in); # else len = vsnprintf((char *)(state->in), size, format, va); va_end(va); # endif #endif /* check that printf() results fit in buffer */ if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) return 0; /* update buffer and position, defer compression until needed */ strm->avail_in = (unsigned)len; strm->next_in = state->in; state->pos += len; return len; } #else /* !STDC */ /* -- see zlib.h -- */ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) gzFile file; const char *format; int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; { int size, len; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* do the printf() into the input buffer, put length in len */ size = (int)(state->size); state->in[size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) if (state->in[len] == 0) break; # else len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); len = strlen(state->in); # else len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) return 0; /* update buffer and position, defer compression until needed */ strm->avail_in = (unsigned)len; strm->next_in = state->in; state->pos += len; return len; } #endif /* -- see zlib.h -- */ int ZEXPORT gzflush(file, flush) gzFile file; int flush; { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* check flush parameter */ if (flush < 0 || flush > Z_FINISH) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* compress remaining data with requested flush */ gz_comp(state, flush); return state->err; } /* -- see zlib.h -- */ int ZEXPORT gzsetparams(file, level, strategy) gzFile file; int level; int strategy; { gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ if (level == state->level && strategy == state->strategy) return Z_OK; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* change compression parameters for subsequent input */ if (state->size) { /* flush previous input with previous parameters before changing */ if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) return state->err; deflateParams(strm, level, strategy); } state->level = level; state->strategy = strategy; return Z_OK; } /* -- see zlib.h -- */ int ZEXPORT gzclose_w(file) gzFile file; { int ret = 0; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing */ if (state->mode != GZ_WRITE) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; ret += gz_zero(state, state->skip); } /* flush, free memory, and close file */ ret += gz_comp(state, Z_FINISH); (void)deflateEnd(&(state->strm)); free(state->out); free(state->in); gz_error(state, Z_OK, NULL); free(state->path); ret += close(state->fd); free(state); return ret ? Z_ERRNO : Z_OK; } Indigo-indigo-1.2.3/third_party/zlib-src/src/infback.c000066400000000000000000000541361271037650300226200ustar00rootroot00000000000000/* infback.c -- inflate using a call-back interface * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* This code is largely copied from inflate.c. Normally either infback.o or inflate.o would be linked into an application--not both. The interface with inffast.c is retained so that optimized assembler-coded versions of inflate_fast() can be used with either inflate.c or infback.c. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. windowBits is in the range 8..15, and window is a user-supplied window and output buffer that is 2**windowBits bytes. */ int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) z_streamp strm; int windowBits; unsigned char FAR *window; const char *version; int stream_size; { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL || window == Z_NULL || windowBits < 8 || windowBits > 15) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { strm->zalloc = zcalloc; strm->opaque = (voidpf)0; } if (strm->zfree == (free_func)0) strm->zfree = zcfree; state = (struct inflate_state FAR *)ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->dmax = 32768U; state->wbits = windowBits; state->wsize = 1U << windowBits; state->window = window; state->wnext = 0; state->whave = 0; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } /* Macros for inflateBack(): */ /* Load returned state from inflate_fast() */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Set state from registers for inflate_fast() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Assure that some input is available. If input is requested, but denied, then return a Z_BUF_ERROR from inflateBack(). */ #define PULL() \ do { \ if (have == 0) { \ have = in(in_desc, &next); \ if (have == 0) { \ next = Z_NULL; \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflateBack() with an error if there is no input available. */ #define PULLBYTE() \ do { \ PULL(); \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflateBack() with an error. */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Assure that some output space is available, by writing out the window if it's full. If the write fails, return from inflateBack() with a Z_BUF_ERROR. */ #define ROOM() \ do { \ if (left == 0) { \ put = state->window; \ left = state->wsize; \ state->whave = left; \ if (out(out_desc, put, left)) { \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* strm provides the memory allocation functions and window buffer on input, and provides information on the unused input on return. For Z_DATA_ERROR returns, strm will also provide an error message. in() and out() are the call-back input and output functions. When inflateBack() needs more input, it calls in(). When inflateBack() has filled the window with output, or when it completes with data in the window, it calls out() to write out the data. The application must not change the provided input until in() is called again or inflateBack() returns. The application must not change the window/output buffer until inflateBack() returns. in() and out() are called with a descriptor parameter provided in the inflateBack() call. This parameter can be a structure that provides the information required to do the read or write, as well as accumulated information on the input and output such as totals and check values. in() should return zero on failure. out() should return non-zero on failure. If either in() or out() fails, than inflateBack() returns a Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it was in() or out() that caused in the error. Otherwise, inflateBack() returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format error, or Z_MEM_ERROR if it could not allocate memory for the state. inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) z_streamp strm; in_func in; void FAR *in_desc; out_func out; void FAR *out_desc; { struct inflate_state FAR *state; unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Check that the strm exists and that the state was initialized */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* Reset the state */ strm->msg = Z_NULL; state->mode = TYPE; state->last = 0; state->whave = 0; next = strm->next_in; have = next != Z_NULL ? strm->avail_in : 0; hold = 0; bits = 0; put = state->window; left = state->wsize; /* Inflate until end of block marked as last */ for (;;) switch (state->mode) { case TYPE: /* determine and dispatch block type */ if (state->last) { BYTEBITS(); state->mode = DONE; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN; /* decode codes */ break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: /* get and verify stored block length */ BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); /* copy stored block from input to output */ while (state->length != 0) { copy = state->length; PULL(); ROOM(); if (copy > have) copy = have; if (copy > left) copy = left; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: /* get dynamic table entries descriptor */ NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); /* get code length code lengths (not a typo) */ state->have = 0; while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); /* get length and distance code code lengths */ state->have = 0; while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { NEEDBITS(here.bits); DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = (unsigned)(state->lens[state->have - 1]); copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (code const FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN; case LEN: /* use inflate_fast() if we have enough input and output */ if (have >= 6 && left >= 258) { RESTORE(); if (state->whave < state->wsize) state->whave = state->wsize - left; inflate_fast(strm, state->wsize); LOAD(); break; } /* get a literal, length, or end-of-block code */ for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); state->length = (unsigned)here.val; /* process literal */ if (here.op == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); ROOM(); *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; } /* process end of block */ if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } /* invalid code */ if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } /* length code -- get extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); } Tracevv((stderr, "inflate: length %u\n", state->length)); /* get distance code */ for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; /* get distance extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); } if (state->offset > state->wsize - (state->whave < state->wsize ? left : 0)) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } Tracevv((stderr, "inflate: distance %u\n", state->offset)); /* copy match from window to output */ do { ROOM(); copy = state->wsize - state->offset; if (copy < left) { from = put + copy; copy = left - copy; } else { from = put - state->offset; copy = left; } if (copy > state->length) copy = state->length; state->length -= copy; left -= copy; do { *put++ = *from++; } while (--copy); } while (state->length != 0); break; case DONE: /* inflate stream terminated properly -- write leftover output */ ret = Z_STREAM_END; if (left < state->wsize) { if (out(out_desc, state->window, state->wsize - left)) ret = Z_BUF_ERROR; } goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; default: /* can't happen, but makes compilers happy */ ret = Z_STREAM_ERROR; goto inf_leave; } /* Return unused input */ inf_leave: strm->next_in = next; strm->avail_in = have; return ret; } int ZEXPORT inflateBackEnd(strm) z_streamp strm; { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } Indigo-indigo-1.2.3/third_party/zlib-src/src/inffast.c000066400000000000000000000321771271037650300226560ustar00rootroot00000000000000/* inffast.c -- fast decoding * Copyright (C) 1995-2008, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifndef ASMINF /* Allow machine dependent optimization for post-increment or pre-increment. Based on testing to date, Pre-increment preferred for: - PowerPC G3 (Adler) - MIPS R5000 (Randers-Pehrson) Post-increment preferred for: - none No measurable difference: - Pentium III (Anderson) - M68060 (Nikl) */ #ifdef POSTINC # define OFF 0 # define PUP(a) *(a)++ #else # define OFF 1 # define PUP(a) *++(a) #endif /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ void ZLIB_INTERNAL inflate_fast(strm, start) z_streamp strm; unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; unsigned char FAR *in; /* local strm->next_in */ unsigned char FAR *last; /* while in < last, enough input available */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const FAR *lcode; /* local strm->lencode */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char FAR *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; in = strm->next_in - OFF; last = in + (strm->avail_in - 5); out = strm->next_out - OFF; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } here = lcode[hold & lmask]; dolen: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op == 0) { /* literal */ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); PUP(out) = (unsigned char)(here.val); } else if (op & 16) { /* length base */ len = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } here = dcode[hold & dmask]; dodist: op = (unsigned)(here.bits); hold >>= op; bits -= op; op = (unsigned)(here.op); if (op & 16) { /* distance base */ dist = (unsigned)(here.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; Tracevv((stderr, "inflate: distance %u\n", dist)); op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { PUP(out) = 0; } while (--len); continue; } len -= op - whave; do { PUP(out) = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { PUP(out) = PUP(from); } while (--len); continue; } #endif } from = window - OFF; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = window - OFF; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { PUP(out) = PUP(from); PUP(out) = PUP(from); PUP(out) = PUP(from); len -= 3; } if (len) { PUP(out) = PUP(from); if (len > 1) PUP(out) = PUP(from); } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ PUP(out) = PUP(from); PUP(out) = PUP(from); PUP(out) = PUP(from); len -= 3; } while (len > 2); if (len) { PUP(out) = PUP(from); if (len > 1) PUP(out) = PUP(from); } } } else if ((op & 64) == 0) { /* 2nd level distance code */ here = dcode[here.val + (hold & ((1U << op) - 1))]; goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ here = lcode[here.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in + OFF; strm->next_out = out + OFF; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ Indigo-indigo-1.2.3/third_party/zlib-src/src/inflate.c000066400000000000000000001466171271037650300226530ustar00rootroot00000000000000/* inflate.c -- zlib decompression * Copyright (C) 1995-2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * Change history: * * 1.2.beta0 24 Nov 2002 * - First version -- complete rewrite of inflate to simplify code, avoid * creation of window when not needed, minimize use of window when it is * needed, make inffast.c even faster, implement gzip decoding, and to * improve code readability and style over the previous zlib inflate code * * 1.2.beta1 25 Nov 2002 * - Use pointers for available input and output checking in inffast.c * - Remove input and output counters in inffast.c * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 * - Remove unnecessary second byte pull from length extra in inffast.c * - Unroll direct copy to three copies per loop in inffast.c * * 1.2.beta2 4 Dec 2002 * - Change external routine names to reduce potential conflicts * - Correct filename to inffixed.h for fixed tables in inflate.c * - Make hbuf[] unsigned char to match parameter type in inflate.c * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) * to avoid negation problem on Alphas (64 bit) in inflate.c * * 1.2.beta3 22 Dec 2002 * - Add comments on state->bits assertion in inffast.c * - Add comments on op field in inftrees.h * - Fix bug in reuse of allocated window after inflateReset() * - Remove bit fields--back to byte structure for speed * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths * - Change post-increments to pre-increments in inflate_fast(), PPC biased? * - Add compile time option, POSTINC, to use post-increments instead (Intel?) * - Make MATCH copy in inflate() much faster for when inflate_fast() not used * - Use local copies of stream next and avail values, as well as local bit * buffer and bit count in inflate()--for speed when inflate_fast() not used * * 1.2.beta4 1 Jan 2003 * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings * - Move a comment on output buffer sizes from inffast.c to inflate.c * - Add comments in inffast.c to introduce the inflate_fast() routine * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new * source file infback.c to provide a call-back interface to inflate for * programs like gzip and unzip -- uses window as output buffer to avoid * window copying * * 1.2.beta5 1 Jan 2003 * - Improved inflateBack() interface to allow the caller to provide initial * input in strm. * - Fixed stored blocks bug in inflateBack() * * 1.2.beta6 4 Jan 2003 * - Added comments in inffast.c on effectiveness of POSTINC * - Typecasting all around to reduce compiler warnings * - Changed loops from while (1) or do {} while (1) to for (;;), again to * make compilers happy * - Changed type of window in inflateBackInit() to unsigned char * * * 1.2.beta7 27 Jan 2003 * - Changed many types to unsigned or unsigned short to avoid warnings * - Added inflateCopy() function * * 1.2.0 9 Mar 2003 * - Changed inflateBack() interface to provide separate opaque descriptors * for the in() and out() functions * - Changed inflateBack() argument and in_func typedef to swap the length * and buffer address return values for the input function * - Check next_in and next_out for Z_NULL on entry to inflate() * * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef MAKEFIXED # ifndef BUILDFIXED # define BUILDFIXED # endif #endif /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); local int updatewindow OF((z_streamp strm, unsigned out)); #ifdef BUILDFIXED void makefixed OF((void)); #endif local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, unsigned len)); int ZEXPORT inflateReset(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; strm->adler = 1; /* to support ill-conceived Java test suite */ state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; state->wsize = 0; state->whave = 0; state->wnext = 0; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; int windowBits; { int wrap; struct inflate_state FAR *state; /* get the state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 1; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; #endif } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) return Z_STREAM_ERROR; if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; } /* update state and reset the rest of it */ state->wrap = wrap; state->wbits = (unsigned)windowBits; return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) z_streamp strm; int windowBits; const char *version; int stream_size; { int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { strm->zalloc = zcalloc; strm->opaque = (voidpf)0; } if (strm->zfree == (free_func)0) strm->zfree = zcfree; state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->window = Z_NULL; ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; } return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) z_streamp strm; const char *version; int stream_size; { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } int ZEXPORT inflatePrime(strm, bits, value) z_streamp strm; int bits; int value; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; state->hold += value << state->bits; state->bits += bits; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(state) struct inflate_state FAR *state; { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } #ifdef MAKEFIXED #include /* Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also defines BUILDFIXED, so the tables are built on the fly. makefixed() writes those tables to stdout, which would be piped to inffixed.h. A small program can simply call makefixed to do this: void makefixed(void); int main(void) { makefixed(); return 0; } Then that can be linked with zlib built with MAKEFIXED defined and run: a.out > inffixed.h */ void makefixed() { unsigned low, size; struct inflate_state state; fixedtables(&state); puts(" /* inffixed.h -- table for decoding fixed codes"); puts(" * Generated automatically by makefixed()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, state.distcode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ local int updatewindow(strm, out) z_streamp strm; unsigned out; { struct inflate_state FAR *state; unsigned copy, dist; state = (struct inflate_state FAR *)strm->state; /* if it hasn't been done already, allocate space for the window */ if (state->window == Z_NULL) { state->window = (unsigned char FAR *) ZALLOC(strm, 1U << state->wbits, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; } /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ copy = out - strm->avail_out; if (copy >= state->wsize) { zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); copy -= dist; if (copy) { zmemcpy(state->window, strm->next_out - copy, copy); state->wnext = copy; state->whave = state->wsize; } else { state->wnext += dist; if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } return 0; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #ifdef GUNZIP # define UPDATE(check, buf, len) \ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) #else # define UPDATE(check, buf, len) adler32(check, buf, len) #endif /* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ check = crc32(check, hbuf, 2); \ } while (0) # define CRC4(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ hbuf[2] = (unsigned char)((word) >> 16); \ hbuf[3] = (unsigned char)((word) >> 24); \ check = crc32(check, hbuf, 4); \ } while (0) #endif /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Reverse the bytes in a 32-bit value */ #define REVERSE(q) \ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ int ZEXPORT inflate(strm, flush) z_streamp strm; int flush; { struct inflate_state FAR *state; unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ #ifdef GUNZIP unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ #endif static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); state->mode = FLAGS; break; } state->flags = 0; /* expect zlib header */ if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ #else if ( #endif ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; else if (len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; #ifdef GUNZIP case FLAGS: NEEDBITS(16); state->flags = (int)(hold); if ((state->flags & 0xff) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } if (state->flags & 0xe000) { strm->msg = (char *)"unknown header flags set"; state->mode = BAD; break; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); state->mode = TIME; case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; if (state->flags & 0x0200) CRC4(state->check, hold); INITBITS(); state->mode = OS; case OS: NEEDBITS(16); if (state->head != Z_NULL) { state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; case EXLEN: if (state->flags & 0x0400) { NEEDBITS(16); state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; if (state->flags & 0x0200) CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) state->head->extra = Z_NULL; state->mode = EXTRA; case EXTRA: if (state->flags & 0x0400) { copy = state->length; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && state->head->extra != Z_NULL) { len = state->head->extra_len - state->length; zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; state->length -= copy; } if (state->length) goto inf_leave; } state->length = 0; state->mode = NAME; case NAME: if (state->flags & 0x0800) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) state->head->name[state->length++] = len; } while (len && copy < have); if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->name = Z_NULL; state->length = 0; state->mode = COMMENT; case COMMENT: if (state->flags & 0x1000) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) state->head->comment[state->length++] = len; } while (len && copy < have); if (state->flags & 0x0200) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->comment = Z_NULL; state->mode = HCRC; case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); if (hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; } INITBITS(); } if (state->head != Z_NULL) { state->head->hcrc = (int)((state->flags >> 9) & 1); state->head->done = 1; } strm->adler = state->check = crc32(0L, Z_NULL, 0); state->mode = TYPE; break; #endif case DICTID: NEEDBITS(32); strm->adler = state->check = REVERSE(hold); INITBITS(); state->mode = DICT; case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; case TYPE: if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN_; /* decode codes */ if (flush == Z_TREES) { DROPBITS(2); goto inf_leave; } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); state->mode = COPY_; if (flush == Z_TREES) goto inf_leave; case COPY_: state->mode = COPY; case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); state->have = 0; state->mode = LENLENS; case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); state->have = 0; state->mode = CODELENS; case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { NEEDBITS(here.bits); DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (code const FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN_; if (flush == Z_TREES) goto inf_leave; case LEN_: state->mode = LEN; case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); if (state->mode == TYPE) state->back = -1; break; } state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->back = -1; state->mode = TYPE; break; } if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); state->was = state->length; state->mode = DIST; case DIST: for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR Trace((stderr, "inflate.c too far\n")); copy -= state->whave; if (copy > state->length) copy = state->length; if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = 0; } while (--copy); if (state->length == 0) state->mode = LEN; break; #endif } if (copy > state->wnext) { copy -= state->wnext; from = state->window + (state->wsize - copy); } else from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if (out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; if (( #ifdef GUNZIP state->flags ? hold : #endif REVERSE(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: check matches trailer\n")); } #ifdef GUNZIP state->mode = LENGTH; case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); if (hold != (state->total & 0xffffffffUL)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: length matches trailer\n")); } #endif state->mode = DONE; case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ inf_leave: RESTORE(); if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) if (updatewindow(strm, out)) { state->mode = MEM; return Z_MEM_ERROR; } in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if (state->wrap && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int ZEXPORT inflateEnd(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; unsigned long id; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; /* check for correct dictionary id */ if (state->mode == DICT) { id = adler32(0L, Z_NULL, 0); id = adler32(id, dictionary, dictLength); if (id != state->check) return Z_DATA_ERROR; } /* copy dictionary to window */ if (updatewindow(strm, strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } if (dictLength > state->wsize) { zmemcpy(state->window, dictionary + dictLength - state->wsize, state->wsize); state->whave = state->wsize; } else { zmemcpy(state->window + state->wsize - dictLength, dictionary, dictLength); state->whave = dictLength; } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } int ZEXPORT inflateGetHeader(strm, head) z_streamp strm; gz_headerp head; { struct inflate_state FAR *state; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; /* save header structure */ state->head = head; head->done = 0; return Z_OK; } /* Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found or when out of input. When called, *have is the number of pattern bytes found in order so far, in 0..3. On return *have is updated to the new state. If on return *have equals four, then the pattern was found and the return value is how many bytes were read including the last byte of the pattern. If *have is less than four, then the pattern has not been found yet and the return value is len. In the latter case, syncsearch() can be called again with more data and the *have state. *have is initialized to zero for the first call. */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; unsigned char FAR *buf; unsigned len; { unsigned got; unsigned next; got = *have; next = 0; while (next < len && got < 4) { if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) got++; else if (buf[next]) got = 0; else got = 4 - got; next++; } *have = got; return next; } int ZEXPORT inflateSync(strm) z_streamp strm; { unsigned len; /* number of bytes to look at or looked at */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; /* check parameters */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; state->hold <<= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { buf[len++] = (unsigned char)(state->hold); state->hold >>= 8; state->bits -= 8; } state->have = 0; syncsearch(&(state->have), buf, len); } /* search available input */ len = syncsearch(&(state->have), strm->next_in, strm->avail_in); strm->avail_in -= len; strm->next_in += len; strm->total_in += len; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; state->mode = TYPE; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ int ZEXPORT inflateSyncPoint(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } int ZEXPORT inflateCopy(dest, source) z_streamp dest; z_streamp source; { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; unsigned wsize; /* check input */ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; /* allocate space */ copy = (struct inflate_state FAR *) ZALLOC(source, 1, sizeof(struct inflate_state)); if (copy == Z_NULL) return Z_MEM_ERROR; window = Z_NULL; if (state->window != Z_NULL) { window = (unsigned char FAR *) ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); if (window == Z_NULL) { ZFREE(source, copy); return Z_MEM_ERROR; } } /* copy state */ zmemcpy(dest, source, sizeof(z_stream)); zmemcpy(copy, state, sizeof(struct inflate_state)); if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); copy->distcode = copy->codes + (state->distcode - state->codes); } copy->next = copy->codes + (state->next - state->codes); if (window != Z_NULL) { wsize = 1U << state->wbits; zmemcpy(window, state->window, wsize); } copy->window = window; dest->state = (struct internal_state FAR *)copy; return Z_OK; } int ZEXPORT inflateUndermine(strm, subvert) z_streamp strm; int subvert; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->sane = !subvert; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR return Z_OK; #else state->sane = 1; return Z_DATA_ERROR; #endif } long ZEXPORT inflateMark(strm) z_streamp strm; { struct inflate_state FAR *state; if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; state = (struct inflate_state FAR *)strm->state; return ((long)(state->back) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } Indigo-indigo-1.2.3/third_party/zlib-src/src/inftrees.c000066400000000000000000000327111271037650300230350ustar00rootroot00000000000000/* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 const char inflate_copyright[] = " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) codetype type; unsigned short FAR *lens; unsigned codes; code FAR * FAR *table; unsigned FAR *bits; unsigned short FAR *work; { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ int end; /* use base and extra for symbol > end */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)1; here.val = (unsigned short)0; *(*table)++ = here; /* make a table to force an error */ *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ end = 19; break; case LENS: base = lbase; base -= 257; extra = lext; extra -= 257; end = 256; break; default: /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used >= ENOUGH_LENS) || (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); if ((int)(work[sym]) < end) { here.op = (unsigned char)0; here.val = work[sym]; } else if ((int)(work[sym]) > end) { here.op = (unsigned char)(extra[work[sym]]); here.val = base[work[sym]]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ here.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used >= ENOUGH_LENS) || (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* Fill in rest of table for incomplete codes. This loop is similar to the loop above in incrementing huff for table indices. It is assumed that len is equal to curr + drop, so there is no loop needed to increment through high index bits. When the current sub-table is filled, the loop drops back to the root table to fill in any remaining entries there. */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)(len - drop); here.val = (unsigned short)0; while (huff != 0) { /* when done with sub-table, drop back to root table */ if (drop != 0 && (huff & mask) != low) { drop = 0; len = root; next = *table; here.bits = (unsigned char)len; } /* put invalid code marker in table */ next[huff >> drop] = here; /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; } /* set return parameters */ *table += used; *bits = root; return 0; } Indigo-indigo-1.2.3/third_party/zlib-src/src/trees.c000066400000000000000000001302721271037650300223410ustar00rootroot00000000000000/* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2010 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* @(#) $Id$ */ /* #define GEN_TREES_H */ #include "deflate.h" #ifdef DEBUG # include #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; local const int extra_dbits[D_CODES] /* extra bits for each distance code */ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ #define Buf_size (8 * 2*sizeof(char)) /* Number of bits used within bi_buf. (bi_buf might be implemented on * more than 16 bits on some systems.) */ /* =========================================================================== * Local data. These are initialized only once. */ #define DIST_CODE_LEN 512 /* see definition of array dist_code below */ #if defined(GEN_TREES_H) || !defined(STDC) /* non ANSI compilers may not accept trees.h */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ uch _dist_code[DIST_CODE_LEN]; /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ uch _length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ #else # include "trees.h" #endif /* GEN_TREES_H */ struct static_tree_desc_s { const ct_data *static_tree; /* static tree or NULL */ const intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; local static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Local (static) routines in this file. */ local void tr_static_init OF((void)); local void init_block OF((deflate_state *s)); local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); local void build_tree OF((deflate_state *s, tree_desc *desc)); local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); local void compress_block OF((deflate_state *s, ct_data *ltree, ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); local void bi_flush OF((deflate_state *s)); local void copy_block OF((deflate_state *s, charf *buf, unsigned len, int header)); #ifdef GEN_TREES_H local void gen_trees_header OF((void)); #endif #ifndef DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef DEBUG local void send_bits OF((deflate_state *s, int value, int length)); local void send_bits(s, value, length) deflate_state *s; int value; /* value to send */ int length; /* number of bits */ { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } #else /* !DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* DEBUG */ /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. */ local void tr_static_init() { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ #ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1< dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { _dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; # ifdef GEN_TREES_H gen_trees_header(); # endif #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } /* =========================================================================== * Genererate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef DEBUG # include # endif # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width)-1 ? ",\n" : ", ")) void gen_trees_header() { FILE *header = fopen("trees.h", "w"); int i; Assert (header != NULL, "Can't open trees.h"); fprintf(header, "/* header created automatically with -DGEN_TREES_H */\n\n"); fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); for (i = 0; i < L_CODES+2; i++) { fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); } fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } fprintf(header, "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); } fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); for (i = 0; i < LENGTH_CODES; i++) { fprintf(header, "%1u%s", base_length[i], SEPARATOR(i, LENGTH_CODES-1, 20)); } fprintf(header, "local const int base_dist[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "%5u%s", base_dist[i], SEPARATOR(i, D_CODES-1, 10)); } fclose(header); } #endif /* GEN_TREES_H */ /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void ZLIB_INTERNAL _tr_init(s) deflate_state *s; { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; s->last_eob_len = 8; /* enough lookahead for inflate */ #ifdef DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } /* =========================================================================== * Initialize a new block. */ local void init_block(s) deflate_state *s; { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->last_lit = s->matches = 0; } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(s, tree, k) deflate_state *s; ct_data *tree; /* the tree to restore */ int k; /* node to move down */ { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(s, desc) deflate_state *s; tree_desc *desc; /* the tree descriptor */ { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; const intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max+1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n-base]; f = tree[n].Freq; s->opt_len += (ulg)f * (bits + xbits); if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); } if (overflow == 0) return; Trace((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length-1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((long)bits - (long)tree[m].Len) *(long)tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes (tree, max_code, bl_count) ct_data *tree; /* the tree to decorate */ int max_code; /* largest code with non zero frequency */ ushf *bl_count; /* number of codes at each bit length */ { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ ush code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { next_code[bits] = code = (code + bl_count[bits-1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? s->depth[n] : s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code+1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree (s, tree, max_code) deflate_state *s; ct_data *tree; /* the tree to be scanned */ int max_code; /* and its largest code of non zero frequency */ { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code+1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n+1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(s) deflate_state *s; { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*(max_blindex+1) + 5+5+4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(s, lcodes, dcodes, blcodes) deflate_state *s; int lcodes, dcodes, blcodes; /* number of codes for each tree */ { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes-1, 5); send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ #ifdef DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; #endif copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. * The current inflate code requires 9 bits of lookahead. If the * last two codes for the previous block (real code plus EOB) were coded * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode * the last real code. In this case we send two empty static blocks instead * of one. (There are no problems if the previous block is stored or fixed.) * To simplify the code, we assume the worst case of last real code encoded * on one bit only. */ void ZLIB_INTERNAL _tr_align(s) deflate_state *s; { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); /* Of the 10 bits for the empty block, we have already sent * (10 - bi_valid) bits. The lookahead for the last real code (before * the EOB of the previous block) was thus at least one plus the length * of the EOB plus what we have just sent of the empty static block. */ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef DEBUG s->compressed_len += 10L; #endif bi_flush(s); } s->last_eob_len = 7; } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) deflate_state *s; charf *buf; /* input block, or NULL if too old */ ulg stored_len; /* length of input block */ int last; /* one if this is the last block for a file */ { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is binary or text */ if (s->strm->data_type == Z_UNKNOWN) s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s->opt_len+3+7)>>3; static_lenb = (s->static_len+3+7)>>3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->last_lit)); if (static_lenb <= opt_lenb) opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len+4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); #ifdef FORCE_STATIC } else if (static_lenb >= 0) { /* force static trees */ #else } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+last, 3); compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); #ifdef DEBUG s->compressed_len += 3 + s->static_len; #endif } else { send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); #ifdef DEBUG s->compressed_len += 3 + s->opt_len; #endif } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); #ifdef DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, s->compressed_len-7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally (s, dist, lc) deflate_state *s; unsigned dist; /* distance of matched string */ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ { s->d_buf[s->last_lit] = (ush)dist; s->l_buf[s->last_lit++] = (uch)lc; if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } #ifdef TRUNCATE_BLOCK /* Try to guess if it is profitable to stop the current block here */ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { /* Compute an upper bound for the compressed length */ ulg out_length = (ulg)s->last_lit*8L; ulg in_length = (ulg)((long)s->strstart - s->block_start); int dcode; for (dcode = 0; dcode < D_CODES; dcode++) { out_length += (ulg)s->dyn_dtree[dcode].Freq * (5L+extra_dbits[dcode]); } out_length >>= 3; Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", s->last_lit, in_length, out_length, 100L - out_length*100L/in_length)); if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; } #endif return (s->last_lit == s->lit_bufsize-1); /* We avoid equality with lit_bufsize because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(s, ltree, dtree) deflate_state *s; ct_data *ltree; /* literal tree */ ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned lx = 0; /* running index in l_buf */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->last_lit != 0) do { dist = s->d_buf[lx]; lc = s->l_buf[lx++]; if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code+LITERALS+1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, "pendingBuf overflow"); } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); s->last_eob_len = ltree[END_BLOCK].Len; } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "black list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ local int detect_data_type(s) deflate_state *s; { /* black_mask is the bit mask of black-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ unsigned long black_mask = 0xf3ffc07fUL; int n; /* Check for non-textual ("black-listed") bytes. */ for (n = 0; n <= 31; n++, black_mask >>= 1) if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) return Z_BINARY; /* Check for textual ("white-listed") bytes. */ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 || s->dyn_ltree[13].Freq != 0) return Z_TEXT; for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) return Z_TEXT; /* There are no "black-listed" or "white-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(code, len) unsigned code; /* the value to invert */ int len; /* its bit length */ { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(s) deflate_state *s; { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(s) deflate_state *s; { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef DEBUG s->bits_sent = (s->bits_sent+7) & ~7; #endif } /* =========================================================================== * Copy a stored block, storing first the length and its * one's complement if requested. */ local void copy_block(s, buf, len, header) deflate_state *s; charf *buf; /* the input data */ unsigned len; /* its length */ int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ s->last_eob_len = 8; /* enough lookahead for inflate */ if (header) { put_short(s, (ush)len); put_short(s, (ush)~len); #ifdef DEBUG s->bits_sent += 2*16; #endif } #ifdef DEBUG s->bits_sent += (ulg)len<<3; #endif while (len--) { put_byte(s, *buf++); } } Indigo-indigo-1.2.3/third_party/zlib-src/src/uncompr.c000066400000000000000000000037121271037650300227000ustar00rootroot00000000000000/* uncompr.c -- decompress a memory buffer * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the compressed buffer. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted. */ int ZEXPORT uncompress (dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { z_stream stream; int err; stream.next_in = (Bytef*)source; stream.avail_in = (uInt)sourceLen; /* Check for source > 64K on 16-bit machine: */ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; stream.next_out = dest; stream.avail_out = (uInt)*destLen; if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; err = inflateInit(&stream); if (err != Z_OK) return err; err = inflate(&stream, Z_FINISH); if (err != Z_STREAM_END) { inflateEnd(&stream); if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) return Z_DATA_ERROR; return err; } *destLen = stream.total_out; err = inflateEnd(&stream); return err; } Indigo-indigo-1.2.3/third_party/zlib-src/src/zlib.def000066400000000000000000000022041271037650300224640ustar00rootroot00000000000000LIBRARY ; zlib data compression library EXPORTS ; basic functions zlibVersion deflate deflateEnd inflate inflateEnd ; advanced functions deflateSetDictionary deflateCopy deflateReset deflateParams deflateTune deflateBound deflatePrime deflateSetHeader inflateSetDictionary inflateSync inflateCopy inflateReset inflateReset2 inflatePrime inflateMark inflateGetHeader inflateBack inflateBackEnd zlibCompileFlags ; utility functions compress compress2 compressBound uncompress gzopen gzdopen gzbuffer gzsetparams gzread gzwrite gzprintf gzputs gzgets gzputc gzgetc gzungetc gzflush gzseek gzrewind gztell gzoffset gzeof gzdirect gzclose gzclose_r gzclose_w gzerror gzclearerr ; checksum functions adler32 crc32 adler32_combine crc32_combine ; various hacks, don't look :) deflateInit_ deflateInit2_ inflateInit_ inflateInit2_ inflateBackInit_ zError inflateSyncPoint get_crc_table inflateUndermine Indigo-indigo-1.2.3/third_party/zlib-src/src/zutil.c000066400000000000000000000162211271037650300223630ustar00rootroot00000000000000/* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #ifndef NO_DUMMY_DECL struct internal_state {int dummy;}; /* for buggy compilers */ #endif const char * const z_errmsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ "file error", /* Z_ERRNO (-1) */ "stream error", /* Z_STREAM_ERROR (-2) */ "data error", /* Z_DATA_ERROR (-3) */ "insufficient memory", /* Z_MEM_ERROR (-4) */ "buffer error", /* Z_BUF_ERROR (-5) */ "incompatible version",/* Z_VERSION_ERROR (-6) */ ""}; const char * ZEXPORT zlibVersion() { return ZLIB_VERSION; } uLong ZEXPORT zlibCompileFlags() { uLong flags; flags = 0; switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; default: flags += 3 << 6; } #ifdef DEBUG flags += 1 << 8; #endif #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif #ifdef ZLIB_WINAPI flags += 1 << 10; #endif #ifdef BUILDFIXED flags += 1 << 12; #endif #ifdef DYNAMIC_CRC_TABLE flags += 1 << 13; #endif #ifdef NO_GZCOMPRESS flags += 1L << 16; #endif #ifdef NO_GZIP flags += 1L << 17; #endif #ifdef PKZIP_BUG_WORKAROUND flags += 1L << 20; #endif #ifdef FASTEST flags += 1L << 21; #endif #ifdef STDC # ifdef NO_vsnprintf flags += 1L << 25; # ifdef HAS_vsprintf_void flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void flags += 1L << 26; # endif # endif #else flags += 1L << 24; # ifdef NO_snprintf flags += 1L << 25; # ifdef HAS_sprintf_void flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void flags += 1L << 26; # endif # endif #endif return flags; } #ifdef DEBUG # ifndef verbose # define verbose 0 # endif int ZLIB_INTERNAL z_verbose = verbose; void ZLIB_INTERNAL z_error (m) char *m; { fprintf(stderr, "%s\n", m); exit(1); } #endif /* exported to allow conversion of error code to string for compress() and * uncompress() */ const char * ZEXPORT zError(err) int err; { return ERR_MSG(err); } #if defined(_WIN32_WCE) /* The Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ int errno = 0; #endif #ifndef HAVE_MEMCPY void ZLIB_INTERNAL zmemcpy(dest, source, len) Bytef* dest; const Bytef* source; uInt len; { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int ZLIB_INTERNAL zmemcmp(s1, s2, len) const Bytef* s1; const Bytef* s2; uInt len; { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void ZLIB_INTERNAL zmemzero(dest, len) Bytef* dest; uInt len; { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifdef SYS16BIT #ifdef __TURBOC__ /* Turbo C in 16-bit mode */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) { voidpf buf = opaque; /* just to make some compilers happy */ ulg bsize = (ulg)items*size; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { int n; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } ptr = opaque; /* just to make some compilers happy */ Assert(0, "zcfree: ptr not found"); } #endif /* __TURBOC__ */ #ifdef M_I86 /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER <= 600)) # define _halloc halloc # define _hfree hfree #endif voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) { if (opaque) opaque = 0; /* to make compiler happy */ return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) { if (opaque) opaque = 0; /* to make compiler happy */ _hfree(ptr); } #endif /* M_I86 */ #endif /* SYS16BIT */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp malloc OF((uInt size)); extern voidp calloc OF((uInt items, uInt size)); extern void free OF((voidpf ptr)); #endif voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) voidpf opaque; unsigned items; unsigned size; { if (opaque) items += size - size; /* make compiler happy */ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } void ZLIB_INTERNAL zcfree (opaque, ptr) voidpf opaque; voidpf ptr; { free(ptr); if (opaque) return; /* make compiler happy */ } #endif /* MY_ZCALLOC */ Indigo-indigo-1.2.3/utils/000077500000000000000000000000001271037650300153415ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/000077500000000000000000000000001271037650300171065ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/LICENSE.GPL000066400000000000000000001004441271037650300205370ustar00rootroot00000000000000 ChemDiff is Copyright (C) of EPAM Systems 2009-2011. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License below for more details. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS Indigo-indigo-1.2.3/utils/chemdiff/META-INF/000077500000000000000000000000001271037650300202465ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/META-INF/chemdiff-splash.png000066400000000000000000000225431271037650300240170ustar00rootroot00000000000000‰PNG  IHDR,¹ÕfsRGB®ÎégAMA± üa pHYsÃÃÇo¨d$øIDATx^í t•ÕµÇYµµÃkí°VçÖ¶¾W[׳­¾Z±­bë¬UKkë<¼ç¬u®ólªeÕY;ˆ`@fPP„ 32C ̄„àûÁI>ÎýÆóM7÷rwÖ]nÎÙçœÿwþß>gï}öéõ€ü‚@"ЋÖ?’A Å‹g ŒU«Vuttd¡¹ÖÕ«kGª/)©?> Í…j C!¶ïÎ& ×®]ÛÚÚš6¦µµÐOµÒ^WW7vlÚ-†’/$ WAÎ& 7lØÐÜÜœ6¬[ËÊÚëë­Vj‡O»ÅPò…„¡à*ˆÂ‹-ÊÚ87oÞÜØØ˜vsMï¿ß¡µÂŠt×Îi7j._HhŽU¡”\¶lYÖ†ÚÐÐP¯é¨dÛ]½zµȆ°éƒ,áÕ$ÛPLiB˜îƒÕW®\¹k×®, ¬½½}áÂ… ,زeK²Í±Ä3g޾Ðmœ4©qÊ”íK–Ô ¼³¡!ÙæbJÆp¬ŽÙ™þjmýúõÖº·¦¦fîܹIQqÅŠ¼Gœfçöím›7çàæàCéá.A T[[[Jý@ò¼yó0RÚäWWWó=ÿÜnSSdÞ¾}»%•îJÈÈM¤Q1 wÔÖ®/)©èß¿âßÿ^ýÖ[›§OïLÎÕÓÑÚŠÀå/½´èé§=ûìŠ×^«ž9sWggœÁïlkÛôÞ{uóçÇRPu™»Ë—/ÇLºcÇŽd¾fÍë+ú"ñ"ÛîÒ¥Kñ:êµ5þüìø!ÃöV/‚„õ ̸úê‘ßýî€^½lŸAŸýìû¿ûœl‹ºÚnon^úâ‹ãŽ9fÀ:åùÒ—J/ºhÕàÁÑØ^rì±J&MÄ«Ðêvvv²´ƒ3‰hEøŒ¢Ãc£¢¢¡Vܺu+…u#/¶š›6m2i+£L÷f¸£¡¡³½?íliiš1cÛìÙ»öü3#Ö/Z4ù¬³œÜp~3ø‹_¬ iz‚W‹ûõ£¢‰üâ#¬ÖÌ\&ˆl)-µ$<ì0“*RFGÀ¢"v”ÈÈ £–,Y¶:T„·þZ‘wÚU—ŒÛH·Ã6× “+*T­mååíÕÕxùkGŒØÙÜŒ‡£¦¨¨³¥%¬L“òÁ$„Top€ÎÿxIïÞ¨¦ÒK/Ò·ïðCµñgâ‰'n]±Â¤ù“N=U¯Þ¿W¯Q?øAée—;í¶Y7Ý4í ‡~õ«¶å×^Û¶u«‰|ÊÐI½zÕûïV”b:˜jX ²ä «[ZZPSqœÊu‰oƒïõ3ă~”öìZV¬°Hؼxqû–-uÅÅ–(ì:ï¼M²­.|ê©7ºŸÐcÊïÏ$f‹eÚÈþ¡_¿ßþ¶5Ý‹þã?ê.ôo»iÕªQ‡nUöÍo.}î¹V7¯Qý‡Îè¡7?õ)«0ovƒ×\xðÁ: yw¤cÈd–«½¢áF«¢¢")¯£ÒŠh&5–ÕuëÖé°oܸ{RçÊŽëFŒh?ž·ê¡C!aͰaz5o½•ƃö#áÌ[n±¦ï˜ÿ&ø÷ cûö9wÝe©M4Xs&LzõÖ-[†~ùËJ><_ðè£A6€më׿{öÙV—Þ;çœ@Kú²W^Qåy)¨_ÞüÄ'P¿i@Y82•VdyéCE|t(ÀmÛ¶% KUUºnöìÙºXºÓ¬ª¤Áºmsæ¨ßw¬[×RQQ?z´ÕÖÁúqã’Ž’æIÂ5#GZÓýí“Njkj2l¾nîÜÁŸûœª‹¢kuX¢•œÉgœÑ%ÿÀW d(œbKŸÞêëRÿŠc>Zþð‰',­+æs´}JBE´ Tç^¯DZq‚Á†«?éþƘ-vÔ×7•–*!mµÎŸß^SS¿'Ú[;r$ÿŒÙ„kuwB¹a_ûšš¾,AëOÿ®lž6mÀA©ê®Ë¿¥/¼ÐE¤ý÷ß8iRØÍ»÷^‹‡8K¼ª£º»4í´TU-zæõO´zØ¥¼h!µWTZÕ=²“ÍZ”¶ôjügÔÉ®oÊ%KLËžð½Î††æ©S·CÎÔÍÝI8óæ›Õ|ò•¯˜›@tÖ ®$°“´ma¸µ»›}ÇѰÃ`£äc/õò"~p㪠‹XZi©©±üuóæEkWj¹" ¨%*++³î‡,šcUZ»~ýê *Ö®]´dɇA;²hcw!!“õýöSÓwÍСÑäRËZpÎ{ðA]þw%|Ø·¾Å62šüí›7}ò“J{E§v˜ƒ9DX×}~ [®úfÆu×EkWjù Á+OH˜}K÷¦M›6sæÌY³fñß”hïBB|j¦â㎌–,ÁôRôéO×dn£K~úS%?¬GÑÖ™e/¿Œ¹óŒk'ÙgªV°Y.þ &¨/‰.01®Æ¾ÔMœ‡ÎÀ·4Z$;ÿ…„{ÓhÂ…„ÓÎ?_ÍTü1›dïnÓu[+*ºöiûíçe³1oÔ'œ _¥jhÞ=÷XéÏðo|C}_1p yCR2×ÀIhO³çX˜PƒŠ„6§HLÉVu; ™¦ƒ?ÿy5M ”IªKΚ!CQ³þéܛü—ßõÂóx@u`ÂñÇ'>:˜5U‹•¾´òÁ@Bþ›R‹vâÙSs”pMÃfýö¯~õvŸ>&Ÿßûž’?çî»Mä7oØÀ‚sâ '„úŒþþ÷U+èCZ!"|ÒÉ'+ c~ô£.UÜ«W£Û“^I™G“ Ή,tìŒ3 “:leë¶„ ‹«9Ê<6!Qݪ|؇$Lä[kã°ò»¶EE´ât¾ûEp×]&}29ˆÑs¶Ñô:YVVƶåhJ»P; -ßÚè#Ž0Çš¬]V(ž ?Mäë®ùPòw[_9„ƒQ´2íâ‹]ëb7Šv,äçR&U°Äfí  žOHˆ&L<HAd'!;(5_™ † ¶TWsZoÓäÉ&ŸéW\¡äϾóNCùµsæ˜H¶Êà‹WMà'TMìloç,….ÄÚ÷®Óâ’ û#År¤¢R‡ƒQ5 ïJv'!öÆAŸùŒšÄìÇ ¥˜Ã&©„³4¯e^’È+xÕ'Ø•óªÑ2.%s ¬¥f$`ÂÃ8Ñá>й¸(°²¨ ÇS¯šÄ̳~Ü8=ÌÚ2üà?  gÕ‰ËÙR^®¿ðÉ'Uç‰õ6†ß®êþûopô3§&c¡v&k$$_¶x˜Ò.$´Œø“Ï<3N«¸~ìcÌõ<¢Ëád­"@¨¸mgOÔ!Âât'„%œóþ·b>þxœaJÝžB k$d€Ó§OG¦4Rb¸·œlqò²”^~¹"ÛÔ .Ð{ÏÉ@õ}ñxÉkØÛ7n´ÂÖªËÊT1Î:*Éü)ð؇=‡í4%pElªd“„„Åò“ÒpܸqٌͩG«F„¶±‚X§m‡Ù‰Me-ªäs8‚pª ¥•ü~–Nl¨/9˜(–Z4ưX^ äÙ$!–XN §„€; ñZ1ܤ‡Û6¿¡_ÿºâ1ÓÎê„t«¿²˜\3bDXùÖ¡$ªcöTÕ9ªË]%ÖúÒ_²¥«9“¶R¾Çˆ´&rŸ±ÍpL$ruÿŠž‡z<öX—é"¤S›ªƒ'€ôÎ<=á¸ãºätP(ED «c3n¸ÁÎQ]õ½a˜YǪ*d͉œ'.¥#b¨N-¾³iNlØò)vϼ€_z έ[ÓýSNi28*¶¾¸xð¾`±k£wbrÉXGÝѺœ`lJ…€®ÓhˆAÓOBXîÁEý«ùøI*¥zKT€y-)Ùãìêèh™7¯yÞ¼¬]í’^”œ ±šL9÷\‹‡¨ §·…lz÷ÝI§ŸnÆY·nÌÿGµ{ÕªeRã,?'î›×®uÖ 3ÿá‡-;­¡T'-95ºš>ð@‚̧;ν¥yu)Ù#pÇ`Í!~ßý ™Ó¿á«0̤q‚‘ເlk„}‘zpÀþû[ìⲪ•_u¬À)G¦‰É§¦Óƒ²‘áÂäñ4­YCu]8¿£!Ë®¼rÖí·óÁ²êL©Hë¶RÖ!ú÷~û[“v­2Çí%Ew(ìz®pçDNІt2ªñ©œÅêê(¨Èáú©ˆ½Uœw”¶qTX^5alÿda‰!'ìþЍ+óš¿üßùÎÚQ£œOߊåØnع1å¼óT£›§N [WÊ÷Õ™™Á¬r´"Ùþ ¦œæ¨ˆ¥4>IÌeDBFéÊ¢"æ«•IÍÆŽ>‘3Âk± û=îŸÀñ`KÚe89ø`âx¸÷ÂË_‚¿†Ú îU†UU¨V Q”9‚@]æU»õÝïe²!ÂÃD²lTŸ¸mN-z YQQ¿|&8øWè'ÿ¥ SZ pì€ãyDš{ß}¤išwÿý•o¾ib³1ì"¹a0ç`êÜ}!ÌÓOãRg¿'g Ñ+b;6mR™ù©/.Vžqó6 ä ˆS%b5Œ'±U*ÒIˆ&$IùB“°@³ 3Ç Q/Ié¹ú³]ËÉM²}~P/è±hÉð!0çw¹b-Ôð¡"9  µ¢º( ’sD˜”­¦{ÂP’‚@ö`óÆ& ƒ„~LqæwØÄ1s+*ú+au{ÔÚµkuÆŠ&Ìþ„‘“GÝ‚“ý‹C~“P‚/¡¥I|²JÚßÖÂÀ÷:²þÄÊ ÷ô+ÜBÂä'„HìY¸ú¡q¯£—XþA *z]¯gŠŠßè†/BÂ8PKÝœF€ý!ºNÂCT39…º]Ôp/a¨ÉÇÃ|ÿ#ùBÂØJ•'¾úÊ+'Lä¯}~y<7ÖØºK×ÿõ//Þýç;/üãùê¯ iô¥^´ä,_¶ìÐo|sBÉxëˆýøc±5„Ý©÷1?ûeïãœFaJ>õÄ·ßz+­ ™2ÐU¯Îëã'ÿ}¤ÅC!aü9—¸R";kQÐ0ÉîN£ŽüºÎPü0°Îº:œ2‹->îgÇÚÊC­žqwÐû‘‡²ÉGòïÎÝ{âÕW^½ùÆï¸ýv«Ø]wüyz÷9zëËÇóým·ÜŠ®sv{«ëv7òÅdœ ‡„03/æt2A0Ò$â4é6!ç„’àNB¦æÜ9]×ÛZzð¾ûÿýÏÚ¾„CÖ¿Œ@Bôí/z÷VgFø¹ü’K -§<<ü™Î¥ïïÏí;Þ¼,è{¶Ëi: Y`ÛÊ M¦Ñ¾W†/“ø`1f w'!Û³cŽ:ªÿë¯;ý¿îÓguæ=Gô›Uë×_o#áìY³–.Y§z‹ý”­«&¤úý÷Þ;jäH¥Ä/øÃù…U+ã<ªÿ{Ùå6ŒV®Xyú)§ªÕòoÏ>Û™Š ÈÆ’nÀ@ÚÕ%@Ÿõ?ª“|,þ'þ$D`N!À:N_ʥݷ@;§uÊ-ºéäm›Ù½zŒó^8Hû—]a#ám7ÝÂÞÏ;o¿m§ Ù³ýiOÚ²Eä^ýöÜZñÏ×þÁï69<üð¯÷W_Â^}íª¾„„èOö·Gÿø'o ÊÐÕüñýÃU'ù|0cFÚÏCäç¨ÁháÝÑ:˜.ÀEÕ•¸µ±Ñjþ7gžµh¡ýÞBÖ¢Lb Ãî w³bÇV¤,£Ù1ª„ǬHi‘_.½èbÛÚZ™Uàó{ï¾ÇgâĉGþð›6³–£˜yNùõI6 “,G£Íª|¯Å†y•µQÄ%!½úÊ+ñRX=î÷ìß°ˆÚpÙ%—`)OB$ÜpÝõÐ~c­„Ï:ý ÌY§žt²­Q¼#}Ï9çïýúY´1>½˜¾'d“‰‚Õÿ*$ÌÚDÌ©†x×§tç®ë0#’Ðò¢`Éísü äÕ°¤³=þ¸Ÿ³ß³¾A ¢¬l»Ç†%jÝqÛmF£ý³¸Ø.ÚFxþyçÙ®À͈åVwé$IJ*ÖÑœ"COu†’^þBç ¢5Û¯N胡垻î†KÌc›\|žëCˆ9„’Îvdtûƒÿü/v€V£4wءߙ”™ÌgUe%ûUgÁ ×^§ëm„>íäS–iÑhÂÿwØ5W]¥>?`÷šôÔ,‘vSE‹eØ£†qú…„´‡¾f²b–ôq§p0 éµÁÅ"êsx„¸rŸ#¡-º/òËFQ ®Vf ¡ú–Vô!Øþ©$[?N+nè¥n.#ÍìÝ™2$v4—§Šô--8ì—–h‡\ü„þ¹§„„Y{ÒP!¸DL°¯óxžEÚØ1cø`Üwò/Eé´iêãê^#šTý•ÐP×ךε.‡ mkBöÐþ!¬‡‰)%f¶œ§6ÁÚê§k7«Üò²2¬ANÏ}%ø`DTÏ"Mªþ>ãuׄ߀W g,þxÏ,ÂzIÄæ3O=Íçå÷F][-áîÓÁ_ sÁWÅê󿀶žû›³mˆŠÁÓuȼóö;0Ïࢠ’×"9„m%ñ¯`YUý|îïÏÙþÊÏ¡ú“?AjÏÎ i=kds9Š>óÉ_ÊÝIH0—~ªÈ $tºìõbÐ:ÖpÍ•W¡jlB0êœuÚiú—Ä ©P5ýLJ„°‹˜Uÿ3)Q¯GK î¬=xi(wȦa¯žD¤; ‰’±N¹Š„?ú¨îr°BB=&Í9|Ø0CòvᨇëQF]B ¯»úšÜ™Ò“¬!ÞeƒÎ!À@Îׇ^ŽBT™ëq^% ²ëcÅkžKâç8áç¿`oêìËÑûî¹G}?×¢s—è¥ Ë¶ʹ²'ôê'ÂÏ8õT–£µ¾emfHCYC€lh&©é ôÐñ´ŽBN b²~Ý:gW !núK.¼ˆ3ŠòÐ36lGÿä(L;®ƒÁ¡‡ÊU.+NÀ·³˜ ‰žsîñœÕ!!ÝPý|ì‘GœØX¾ðÜóD¥žÈ‰<6’6x¹]O'Þ.¶F~à!Ym0"'àlÂÏE±;ÁÄСœ-tšUÌ—£÷Þ}7Gì½ÆÆ–LºewÍIøW_ûËÃB濵ª³jç<±ó”F |)§@´O®¡?ȨoYìQ¼$ÿ:u*Ö6Sú?ØOxÅ¥—ZIb,ÄÍIˆ">á¿ô2Ÿ`5aöÓ]t¦ëòÀKòvùù±½W†$d\ìNÕq ùÙ·€¤!ÅîÈÂQáïÁ‹ …WBaŒÿÓ§OŸ1cFYYÝàÀ N²2ËÏ<õ4[œ4=3'!…¯¿æÚñ%%®ãá}€/„!.×>ÖQÆyBÿ5¤9 ‰M%"<2èR1÷@õ1ïKKK-â1y`K²TTù} ³®DCΜ9sÖ¬Y0«¼¼Ü…„ôŒCèìôprVˆølW?á­7ÝÌ‚P}œQ—º‹‚LzòÛÓb±J5ÙaIÈ89vLL6ûC²N‘wùK„„ø9­~² Ð[aýyÓ 7’T#irœéªrbIM ™yÏtgÒ;weLoŒ¥8-âkEä `MºD@)ú@:æ® QÖ‡'i…WŽl-ŒÇúqòž¼/úZz­HwßÅá‘φÁPK¥·ðú¡«K&4Ãé*§q•÷œÞO[H +ÖÃDM›:5ké·Lž”I Ö{è5PP>b•VŒLE•Q?Ô,B3ÃCôaðž0)8DŽ 5x½â„€x0mÃ/ú™XŸn 9Ðfd…±Úñï9Ô L©æµ£cB¬M i(°‚BhE?~ø=0Õ’­gP‘å¢ 9O‡ŒéífcfHÙA@m´X੽–Ú¢£µÎVHiE¯×(”°Í Ã"&åsÝgbÊË-∟ÐI]mÍNE2&âô‹oÈQP swJIÏltt_%¿kçÎÖî@Ȧòòúñã¹)[ÆÎâŽsææŒ ) D%¢YyrƒÓI‘E9+ SD¥‹@uQ‘j7C}ñî<Î5ƒu칯¡eåÊúqãÔ_¹"g ÿé¡hEÓÂÃP6“†„„&(I™œ@ V;dSW\ÜVUµ}áB«g %%­­ü‡éÂRêqܦô°DlòÔÎʳ~ìØº’’ú’’‡Úéœm³g·ec$߃>˜x ¿0'%2SA CŽÉB” ¡ÕRݘ1mm©4¬ åŽÍÀˆå°}†ELÊ÷u£öÞ~W³ç°ÚÑ£[ÈÔÚÚ<{vc÷]´©ö¬Á‰¿¦úÈDx’l--Å.ª$6wg…Ç$³mÆŒ¶DÍ•>†(Ã$G%.ŠdÑiû<diðOÑ„@“*…‹a¢µÃ¢#$ ‹˜”/\°‹rHXmŸã‚ÐFMª( pŽŸ˜Û6ø„„:ŸdØ X”ˆm"㜹Þ#H³ª ã 'u ¸G©Wþ˜ÈX #C' 4!ÖQuå}‚.{!aÁÍ$pd°Çpˆ‰ðQT¢kÑh’…„Ñp“Z…ˆ S,GùI0F\HXˆ“IÆ µ%y¹ƒãÃL´§ µ be”‹‚L6 Þf)š° g• >\|]±fÍÚªª ÕÕë“;6%$ õ¤p¡#Ð:kÖÖñãÆŒéØ¼9),„„I!)rö}'Oní>®QËÆúúDÆ\Ø$,{°¯^ê3­,Ï; Eˆ/eӺ߆6º½CNxòkXâ:;:&LHDº°K ™O‘„¬_Ø·ë)Øè¶B U8Rw¼+éš°µ²²Ù÷r ó¶÷º¿ý©%šPMçÒÎI†Ýß<nÉn ¯¶)èßëAm ßú.´-HC6çYÉÍͤÕh­¨ ÍiM·VäÞ8gíñ:æ= Í&ãY:&_kB?ÝâÊF3¬2Åö-Úê1Ñ×?¸W îÌC6cVèRänß²E¯F.`n> › ‘„¤ñ†ÃùLÂõEŠùé«:ôcʯ ¡IhfÊ2$¡¾„ÑÕ +†¡ gû)„¢"…¹Zu1oI˜±,Ùó*uy×Ú§—í.$Ü3t”Ü— ^KÖVûú›Ô[[vñ)Táls°«=ÂÙüµ"G1P€‚[ÌOÚu`Àtï&sW#$4'¡ ‚¼F,Ðeàž3Ta£æÓ*D ²ñ;¨œ æ.'½U‚àò‘„™6˜À×§ñžç'šÐmÒiB­¢}}¸‘ó›é¡xªpZü !*ZZÈM†„€ëõÕõÚùGÂLKLà.Â2Ñ„¡5a·BÌt*Äx ¡xªp²¤[*²þ„z3úõÚyG 5ëœiš7³ø¥û´zHzXM¸weaÀ¢£ŠW¡ ÷š^Í⊰®ì¶]¯g$LP –—&t¸=<ÍžÚÝrk 1öœ2 ë¦ù=SßC”£]zD"¡Í»èº)ÐŒg™Ý0´Çª1†*œcÌËìùi8å¼^;¿H˜ñ< ¦WÀ#q!¡Óèº× b@!ô“ b~r´.u1ßA?-¸$Ã’áÌÕ¿—ÿK$" Õ6»[º5áIBŸ®ê¡3Jf¨Â9MB²sØÙż"aæk>æZÔ© ý¦{×l à¡YäóÌÄ@% x‹ùQI˜©£\‡hÂL‘Ãõ%‘W$ô YŠøþóâŒ>ŸleBÄõÛ îfn.餵i†¾ ‹öžÉp–Ú˜ééˆLÂô×– 2ž$ÔžW¨m^¨Â'EŠÕ*++]s´å 3&¨¡gÂR—ï&Öh#¸6Sæ -DË5Ó¬KîÒ_y/ƒ£“0£·N’ 3§fR×ÔÝBB¯-–Ž_Ƥw]kì2߀¹,ní$ôR_Žõ§Çâ<¸ç†3îo3½Î÷—05BÕ0Ìä÷ž0C$® }xÃ,“tQNækûŒE©á"Ù£XtM˜±­dÉÔæš(1Ÿ4aª$ °µú¿ÔCÐ÷e?`Ì]Y¹ëUcQÎI&š0ˆxúß90¡‡ŒZšêõeŽñZTí 5˾ƒyLBÙ’˜µ-™g T|"¡ñR-Œî¿Ïø €Sgø6!M„þ†ß÷‚ñF.Å=aéU¬£™S®½½}ÕªUyNÂ̦TÂÖ¼Þl î “QÎ9@ רÛÂDHèxÖ®A3ù´'ÜmÔÐݱ½ iÂŒ^Å ¦3îRÏ“0`CÈÓ:HÈù‰|ׄ¶ ɸ+Rã0ŸBÈ ÚC‹êqœ):·íX}þYG÷ô8Ò±z©o<ãƒ^êaœõþ44îR“ÐhI"$ÜGIè81˜˜¤ ˆ=ìœc&`>™(‚ õ÷¼ a¦KÆ{="$ÜgIhÛºçYËýÞ™ î“]ÞªÇ9OBûé>?ó˜p&¡ûik7;Ýž Í^ ñ¹KB·#‹1œ«ÝÓ3ÔJ>TaãÕG6 î{½ˆ…ŽSq°ÔxÆí »:Òaè¶6îRJ{BÏ;!ù” ¬b¢ ÷I…}P¦I¸c§Á7™OŽõ¤{"pß›Øò‚„¦nÐB)·P…³©àŒÛ"íšlÅóÌOè5XéA·šÌÕ¤yIUÞëíhF2mHׄ¾|× =ÛKÄ”{ÖC2‹A÷´Å_ì»1z¦`uu5Y;;;9ÍÄ/$_³ú±°gp•V3Hv¨³ŽJœ¨@+ª/…„f(J)A œ]B﹦–A^}}=TF‚V* A°òä:Šªªª ‚¢ ’‚@x87wíÚeRU4¡ JRF0E€•'ëO×ô^"„„¦àJ9A ý‰ÀÂb5‡HJ Á¨»¯ñCu”M4©"d  ß}!aÐ¤Š Ð…€óîëÐ #€&UÝpÅ’k@vXt„„a“ò‚Ànp?$Â@D eJ pMA–0hREØÀ¢E‹BH˜Œ"¤åh!>usN! $Ì©Ç!)DdOXˆO]ÆœSˆ&Ì©Ç!)D–/_žÈ°Å0“Œ"¤¨¬¬ô:­ !a(¸¤° °"f¸í,>"BÂøŠ„E`Æ ÍÍÍñ/$Œ¡H(PH]ÑÔÔðBÂøŠ„E€Óƒuuuñ/$Œ¡H(PÈ *…LBÂ@2ìø°%¡S|9BÂøŠ„E ¥¥eݺuñ/$Œ¡H(PÚÛÛW¯^ðBÂøŠ„E€s½+W®Œ?x!a| EBá"°lÙ²øƒÆÇP$.‰Äp wÉÈã#0wîÜøB„„ñ1 …‹@MMM䜿jBÂÂ@2ò¤Àe‡ŠB¤„È)t"SQHXèSGÆŸ,ŠŠ,SÍŠͱ’’‚€)›7oæ‚´ÚÚZ“ BB”¤Œ ¥©($Œ®ÔÌàØ!ž /*¶¶¶ ÍÁ”’‚@t8oV´?\µjÕÒ¥K…„Ña•š‚@X "{ņ†àœ9sÔÁ|!aX¥¼ nÕÖÓ% ã*õ˜ c(Õ¸ ã"(õ˜ c(Õ¸ ã"(õ˜ c(Õ¸ ã"(õ˜ c(Õ¸ ã"(õ˜ c(Õ¸ ã"(õ˜ c(Õ¸ ã"(õ˜ c(Õ¸t‘ÿÉ ôÿæ2í}â±IEND®B`‚Indigo-indigo-1.2.3/utils/chemdiff/build.xml000066400000000000000000000136241271037650300207350ustar00rootroot00000000000000 Builds and runs the project chemdiff. Indigo-indigo-1.2.3/utils/chemdiff/chemdiff.sh000077500000000000000000000003431271037650300212120ustar00rootroot00000000000000#!/bin/sh JAVA_OPTS="-Xss10m -Dawt.useSystemAAFontSettings=on" #Following symlinks path=$0 while [ -h "$path" ]; do path=$(ls -ld $path | sed -r 's/^.*->.//') done java $JAVA_OPTS -jar $(dirname $path)/chemdiff.jar "$@" Indigo-indigo-1.2.3/utils/chemdiff/chemdiff_installer.nsi000066400000000000000000000115161271037650300234470ustar00rootroot00000000000000!define WEB_SITE "http://epam.com/opensource/indigo/chemdiff" !define APP_NAME "ChemDiff" !define COMP_NAME "EPAM Systems" !define COPYRIGHT "EPAM Systems © 2010" !define DESCRIPTION "ChemDiff is an Indigo-based utility for visual comparison of two files containing multiple structures." SetCompressor /SOLID lzma !define MULTIUSER_EXECUTIONLEVEL Highest !define MULTIUSER_MUI !define MULTIUSER_INSTALLMODE_COMMANDLINE !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME "" !define MULTIUSER_INSTALLMODE_INSTDIR "EPAM Systems\${APP_NAME}" !include "MultiUser.nsh" !include "MUI2.nsh" ;-------------------------------- ;General ;Name and file Name "${APP_NAME}" OutFile "chemdiff-${VERSION}-installer.exe" ;-------------------------------- ;Variables Var StartMenuFolder ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING ;-------------------------------- ;Language Selection Dialog Settings ;Remember the installer language !define MUI_LANGDLL_REGISTRY_ROOT "SHCTX" !define MUI_LANGDLL_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "LICENSE.GPL" !insertmacro MULTIUSER_PAGE_INSTALLMODE !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_DEFAULTFOLDER "EPAM Systems\${APP_NAME}" !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES Function finishpageaction CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\launch.bat" FunctionEnd !define MUI_FINISHPAGE_RUN $INSTDIR\launch.bat !define MUI_FINISHPAGE_SHOWREADME "" !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" !define MUI_FINISHPAGE_SHOWREADME_FUNCTION finishpageaction !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;first language is the default language ;-------------------------------- ;Installer Sections Section "${APP_NAME}" SetOutPath "$INSTDIR\lib\" File /r "lib\" SetOutPath $INSTDIR File "chemdiff.jar" File "launch.bat" SetOutPath "$INSTDIR\examples\" File /r "examples\" ;%NSIS_INSTALL_FILES ;Store installation folder WriteRegStr SHCTX "Software\EPAM Systems\${APP_NAME}" "" $INSTDIR ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APP_NAME}.lnk" "$INSTDIR\launch.bat" "" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" !ifdef WEB_SITE WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Website.lnk" "$INSTDIR\${APP_NAME} website.url" !endif !insertmacro MUI_STARTMENU_WRITE_END SectionEnd ;-------------------------------- ;Installer Functions Function .onInit !insertmacro MULTIUSER_INIT FunctionEnd ;-------------------------------- ;This function and example function call can be used to recursively delete empty parent folders of a given folder. Function un.RMDirUP !define RMDirUP "!insertmacro RMDirUPCall" !macro RMDirUPCall _PATH push '${_PATH}' Call un.RMDirUP !macroend ; $0 - current folder ClearErrors Exch $0 ;DetailPrint "ASDF - $0\.." RMDir "$0\.." IfErrors Skip ${RMDirUP} "$0\.." Skip: Pop $0 FunctionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" RMDir /r $INSTDIR !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\${APP_NAME}.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" !ifdef WEB_SITE Delete "$SMPROGRAMS\$StartMenuFolder\Website.lnk" !endif RMDir "$SMPROGRAMS\$StartMenuFolder" ${RMDirUP} "$SMPROGRAMS\$StartMenuFolder" Delete "$DESKTOP\${APP_NAME}.lnk" DeleteRegKey SHCTX "Software\EPAM Systems\${APP_NAME}" DeleteRegKey /ifempty SHCTX "Software\EPAM Systems" SectionEnd ;-------------------------------- ;Uninstaller Functions Function un.onInit !insertmacro MULTIUSER_UNINIT FunctionEnd Indigo-indigo-1.2.3/utils/chemdiff/examples/000077500000000000000000000000001271037650300207245ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/examples/normalization.sdf000066400000000000000000000255721271037650300243230ustar00rootroot00000000000000 Mrv0540 05181117402D 3 2 0 0 0 0 999 V2000 -1.0607 2.5339 0.0000 O 0 3 0 0 0 0 0 0 0 0 0 0 -1.6441 1.9506 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -0.4774 1.9506 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 2 0 0 0 0 M CHG 2 1 1 2 -1 M ISO 1 3 16 M END $$$$ Mrv0540 05181117402D 3 2 0 0 0 0 999 V2000 -1.0607 2.5339 0.0000 O 0 3 0 0 0 0 0 0 0 0 0 0 -1.6441 1.9506 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.4774 1.9506 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 M CHG 2 1 1 3 -1 M ISO 1 3 16 M END $$$$ Mrv0540 05181117402D 2 1 0 0 0 0 999 V2000 -3.1821 0.7071 0.0000 O 0 3 0 0 0 0 0 0 0 0 0 0 -2.3571 0.7071 0.0000 N 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117402D 2 1 0 0 0 0 999 V2000 -3.1821 0.7071 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.3571 0.7071 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 M END $$$$ Mrv0540 05181117412D 2 1 0 0 0 0 999 V2000 -3.1821 0.7071 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.3571 0.7071 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 -1 2 1 M END $$$$ Mrv0540 05181117442D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117442D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 M END $$$$ Mrv0540 05181117442D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 M END $$$$ Mrv0540 05181117452D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 N 0 5 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117452D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 -1 2 1 M END $$$$ Mrv0540 05181117462D 2 1 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 N 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117472D 5 4 0 0 0 0 999 V2000 -3.8304 3.5946 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 -3.0054 3.5946 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 -3.0054 2.7696 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0054 4.4196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1804 3.5946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 2 4 1 0 0 0 0 2 5 1 0 0 0 0 M CHG 2 1 -1 2 1 M END $$$$ Mrv0540 05181117482D 3 2 0 0 0 0 999 V2000 -1.4437 2.7696 0.0000 C 0 2 0 0 0 0 0 0 0 0 0 0 -0.6187 2.7696 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.2687 2.7696 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 M CHG 3 1 2 2 -1 3 -1 M END $$$$ Mrv0540 05181117492D 3 2 0 0 0 0 999 V2000 -1.4437 2.7696 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6187 2.7696 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.2687 2.7696 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 2 0 0 0 0 M END $$$$ Mrv0540 05181117492D 4 3 0 0 0 0 999 V2000 -2.3571 -0.0589 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 -1.5321 -0.0589 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.7696 0.6555 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.7696 -0.7734 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 2 0 0 0 0 1 4 2 0 0 0 0 M END $$$$ Mrv0540 05181117492D 4 3 0 0 0 0 999 V2000 -2.3571 -0.0589 0.0000 S 0 3 0 0 0 0 0 0 0 0 0 0 -1.5321 -0.0589 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.7696 0.6555 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.7696 -0.7734 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 2 0 0 0 0 1 4 2 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117492D 4 3 0 0 0 0 999 V2000 -2.3571 -0.0589 0.0000 S 0 2 0 0 0 0 0 0 0 0 0 0 -1.5321 -0.0589 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.7696 0.6555 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.7696 -0.7734 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 2 0 0 0 0 M CHG 3 1 2 2 -1 3 -1 M END $$$$ Mrv0540 05181117502D 4 3 0 0 0 0 999 V2000 -2.3571 -0.0589 0.0000 S 0 1 0 0 0 0 0 0 0 0 0 0 -1.5321 -0.0589 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.7696 0.6555 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -2.7696 -0.7734 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 4 1 3 2 -1 3 -1 4 -1 M END $$$$ Mrv0540 05181117512D 2 1 0 0 0 0 999 V2000 -4.0366 3.5357 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.2116 3.5357 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 M END $$$$ Mrv0540 05181117512D 2 1 0 0 0 0 999 V2000 -4.0366 3.5357 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -3.2116 3.5357 0.0000 N 0 5 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181117522D 2 1 0 0 0 0 999 V2000 -4.0366 3.5357 0.0000 C 0 2 0 0 0 0 0 0 0 0 0 0 -3.2116 3.5357 0.0000 N 0 6 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M CHG 2 1 2 2 -2 M END $$$$ Mrv0540 05181118022D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 05181118022D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 05181118022D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 3 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181118022D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 5 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 2 1 -1 2 1 M END $$$$ Mrv0540 05181118032D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 3 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181118032D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 P 0 5 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 2 1 -1 2 1 M END $$$$ Mrv0540 05181118042D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 2 1 1 2 -1 M END $$$$ Mrv0540 05181118042D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 1 1 1 M END $$$$ Mrv0540 05181118052D 4 3 0 0 0 0 999 V2000 -2.2393 3.0348 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.4143 3.0348 0.0000 C 0 3 0 0 0 0 0 0 0 0 0 0 -2.6518 3.7493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.6518 2.3204 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M CHG 1 2 1 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/set1.sdf000066400000000000000000000220721271037650300223010ustar00rootroot00000000000000D+Z -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 8 7 0 0 0 M V30 BEGIN ATOM M V30 1 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 2 O 0.000000 -1.600000 0.000000 0 CHG=0 M V30 3 N 1.385641 0.800000 0.000000 0 CHG=0 M V30 4 C -4.156921 0.799999 0.000000 0 CHG=0 M V30 5 C -2.771281 -0.000000 0.000000 0 CHG=0 M V30 6 C -1.385641 0.800000 0.000000 0 CHG=0 M V30 7 C 2.771281 -0.000000 0.000000 0 CHG=0 M V30 8 C 4.156922 0.800000 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 7 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 1 4 5 M V30 5 2 5 6 M V30 6 1 7 8 M V30 7 1 1 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ D+X -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 11 11 0 0 0 M V30 BEGIN ATOM M V30 1 C 2.756100 0.289678 0.000000 0 CHG=0 M V30 2 O 2.923346 -1.301557 0.000000 0 CHG=0 M V30 3 N 1.294427 0.940457 0.000000 0 CHG=0 M V30 4 C 6.806628 1.519812 0.000000 0 CHG=0 M V30 5 C 5.512200 0.579356 0.000000 0 CHG=0 M V30 6 C 4.050527 1.230134 0.000000 0 CHG=0 M V30 7 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 8 C -0.000000 -1.600000 0.000000 0 CHG=0 M V30 9 C -1.521691 0.494427 0.000000 0 CHG=0 M V30 10 C -2.462147 -0.800000 0.000000 0 CHG=0 M V30 11 C -1.521690 -2.094427 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 7 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 1 4 5 M V30 5 2 5 6 M V30 6 1 7 9 M V30 7 1 8 7 M V30 8 1 8 11 M V30 9 1 9 10 M V30 10 1 10 11 M V30 11 1 1 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ D+Y -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 10 9 0 0 0 M V30 BEGIN ATOM M V30 1 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 2 O 0.000000 -1.600000 0.000000 0 CHG=0 M V30 3 N 1.385641 0.800000 0.000000 0 CHG=0 M V30 4 C -4.156921 0.799999 0.000000 0 CHG=0 M V30 5 C -2.771281 -0.000000 0.000000 0 CHG=0 M V30 6 C -1.385641 0.800000 0.000000 0 CHG=0 M V30 7 C 2.771281 -1.600000 0.000000 0 CHG=0 M V30 8 C 2.771281 -0.000000 0.000000 0 CHG=0 M V30 9 C 4.156922 0.800000 0.000000 0 CHG=0 M V30 10 C 5.542562 0.000000 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 8 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 1 4 5 M V30 5 2 5 6 M V30 6 1 8 7 CFG=3 M V30 7 1 8 9 M V30 8 1 9 10 M V30 9 1 1 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(1 8) M V30 END COLLECTION M V30 END CTAB M END $$$$ E+Z -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 10 9 0 0 0 M V30 BEGIN ATOM M V30 1 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 2 O 0.000000 -1.600000 0.000000 0 CHG=0 M V30 3 N -1.385641 0.800000 0.000000 0 CHG=0 M V30 4 C 1.385641 0.800000 0.000000 0 CHG=0 M V30 5 C 2.771281 -0.000000 0.000000 0 CHG=0 M V30 6 C 4.156922 0.800000 0.000000 0 CHG=0 M V30 7 O 4.156922 2.400000 0.000000 0 CHG=0 M V30 8 O 5.542562 0.000000 0.000000 0 CHG=0 M V30 9 C -2.771281 -0.000000 0.000000 0 CHG=0 M V30 10 C -4.156921 0.799999 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 9 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 1 6 7 M V30 7 2 6 8 M V30 8 1 9 10 M V30 9 1 1 4 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ E+X -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 13 13 0 0 0 M V30 BEGIN ATOM M V30 1 C 2.531692 -1.127181 0.000000 0 CHG=0 M V30 2 O 1.880913 -2.588854 0.000000 0 CHG=0 M V30 3 N 1.591235 0.167246 0.000000 0 CHG=0 M V30 4 C 4.122927 -0.959936 0.000000 0 CHG=0 M V30 5 C 5.063383 -2.254364 0.000000 0 CHG=0 M V30 6 C 6.654618 -2.087118 0.000000 0 CHG=0 M V30 7 O 7.305397 -0.625445 0.000000 0 CHG=0 M V30 8 O 7.595075 -3.381546 0.000000 0 CHG=0 M V30 9 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 10 C -0.800000 -1.385641 0.000000 0 CHG=0 M V30 11 C -1.070609 1.189031 0.000000 0 CHG=0 M V30 12 C -2.532282 0.538253 0.000000 0 CHG=0 M V30 13 C -2.365036 -1.052982 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 9 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 1 6 7 M V30 7 2 6 8 M V30 8 1 9 11 M V30 9 1 10 9 M V30 10 1 10 13 M V30 11 1 11 12 M V30 12 1 12 13 M V30 13 1 1 4 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ E+Y -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 12 11 0 0 0 M V30 BEGIN ATOM M V30 1 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 2 O 0.000000 -1.600000 0.000000 0 CHG=0 M V30 3 N 1.385641 0.800000 0.000000 0 CHG=0 M V30 4 C -1.385641 0.800000 0.000000 0 CHG=0 M V30 5 C -2.771281 -0.000000 0.000000 0 CHG=0 M V30 6 C -4.156921 0.799999 0.000000 0 CHG=0 M V30 7 O -4.156922 2.399999 0.000000 0 CHG=0 M V30 8 O -5.542562 -0.000001 0.000000 0 CHG=0 M V30 9 C 2.771281 -1.600000 0.000000 0 CHG=0 M V30 10 C 2.771281 -0.000000 0.000000 0 CHG=0 M V30 11 C 4.156922 0.800000 0.000000 0 CHG=0 M V30 12 C 5.542562 0.000000 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 10 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 1 6 7 M V30 7 2 6 8 M V30 8 1 10 9 CFG=3 M V30 9 1 10 11 M V30 10 1 11 12 M V30 11 1 1 4 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(1 10) M V30 END COLLECTION M V30 END CTAB M END $$$$ J+Z -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 13 13 0 0 0 M V30 BEGIN ATOM M V30 1 C -2.400000 -1.385641 0.000000 0 CHG=0 M V30 2 O -1.600000 -2.771281 0.000000 0 CHG=0 M V30 3 N -4.000001 -1.385641 0.000000 0 CHG=0 M V30 4 C 2.400000 1.385641 0.000000 0 CHG=0 M V30 5 C 3.200000 -0.000000 0.000000 0 CHG=0 M V30 6 C 2.400000 -1.385641 0.000000 0 CHG=0 M V30 7 C 0.800000 -1.385641 0.000000 0 CHG=0 M V30 8 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 9 C 0.800000 1.385641 0.000000 0 CHG=0 M V30 10 C -1.600000 0.000000 0.000000 0 CHG=0 M V30 11 C -2.400000 1.385641 0.000000 0 CHG=0 M V30 12 C -4.800001 -2.771281 0.000000 0 CHG=0 M V30 13 C -6.400000 -2.771281 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 12 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 2 6 7 M V30 7 1 7 8 M V30 8 2 8 9 M V30 9 1 9 4 M V30 10 1 8 10 M V30 11 1 10 11 CFG=3 M V30 12 1 12 13 M V30 13 1 1 10 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STERAC1 ATOMS=(1 10) M V30 END COLLECTION M V30 END CTAB M END $$$$ J+X -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 16 17 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.385641 -0.800000 0.000000 0 CHG=0 M V30 2 O -1.385641 -2.400000 0.000000 0 CHG=0 M V30 3 N -2.771281 0.000000 0.000000 0 CHG=0 M V30 4 C 4.156922 -0.800000 0.000000 0 CHG=0 M V30 5 C 4.156922 -2.400001 0.000000 0 CHG=0 M V30 6 C 2.771281 -3.200001 0.000000 0 CHG=0 M V30 7 C 1.385641 -2.400001 0.000000 0 CHG=0 M V30 8 C 1.385641 -0.800000 0.000000 0 CHG=0 M V30 9 C 2.771281 0.000000 0.000000 0 CHG=0 M V30 10 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 11 C 0.000000 1.600000 0.000000 0 CHG=0 M V30 12 C -4.156922 -0.800000 0.000000 0 CHG=0 M V30 13 C -4.324167 -2.391235 0.000000 0 CHG=0 M V30 14 C -5.618595 -0.149222 0.000000 0 CHG=0 M V30 15 C -6.689204 -1.338254 0.000000 0 CHG=0 M V30 16 C -5.889204 -2.723894 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 12 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 2 6 7 M V30 7 1 7 8 M V30 8 2 8 9 M V30 9 1 9 4 M V30 10 1 8 10 M V30 11 1 10 11 CFG=3 M V30 12 1 12 14 M V30 13 1 13 12 M V30 14 1 13 16 M V30 15 1 14 15 M V30 16 1 15 16 M V30 17 1 1 10 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STERAC1 ATOMS=(1 10) M V30 END COLLECTION M V30 END CTAB M END $$$$ J+Y -INDIGO-12211018562D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 15 15 0 0 0 M V30 BEGIN ATOM M V30 1 C -2.771281 0.000000 0.000000 0 CHG=0 M V30 2 O -2.771281 -1.600000 0.000000 0 CHG=0 M V30 3 N -4.156922 0.800000 0.000000 0 CHG=0 M V30 4 C 2.771282 -0.000000 0.000000 0 CHG=0 M V30 5 C 2.771282 -1.600001 0.000000 0 CHG=0 M V30 6 C 1.385641 -2.400000 0.000000 0 CHG=0 M V30 7 C 0.000000 -1.600000 0.000000 0 CHG=0 M V30 8 C 0.000000 0.000000 0.000000 0 CHG=0 M V30 9 C 1.385641 0.800000 0.000000 0 CHG=0 M V30 10 C -1.385641 0.800000 0.000000 0 CHG=0 M V30 11 C -1.385640 2.400000 0.000000 0 CHG=0 M V30 12 C -5.542563 -1.599999 0.000000 0 CHG=0 M V30 13 C -5.542563 0.000000 0.000000 0 CHG=0 M V30 14 C -6.928204 0.800000 0.000000 0 CHG=0 M V30 15 C -8.313845 0.000000 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 13 M V30 2 2 1 2 M V30 3 1 1 3 M V30 4 2 4 5 M V30 5 1 5 6 M V30 6 2 6 7 M V30 7 1 7 8 M V30 8 2 8 9 M V30 9 1 9 4 M V30 10 1 8 10 M V30 11 1 10 11 CFG=3 M V30 12 1 13 12 CFG=1 M V30 13 1 13 14 M V30 14 1 14 15 M V30 15 1 1 10 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STERAC1 ATOMS=(1 10) M V30 MDLV30/STEABS ATOMS=(1 13) M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/set2.smi000066400000000000000000000003451271037650300223150ustar00rootroot00000000000000CCNC(=O)\C=C\C C\C=C\C(=O)NC1CCCC1 CC[C@H](C)NC(=O)\C=C\C CCNC(=O)\C=C\C(O)=O OC(=O)\C=C\C(=O)NC1CCCC1 CC[C@H](C)NC(=O)\C=C\C(O)=O CCNC(=O)[C@H](C)C1=CC=CC=C1 C[C@@H](C(=O)NC1CCCC1)C1=CC=CC=C1 CC[C@H](C)NC(=O)[C@H](C)C1=CC=CC=C1 Indigo-indigo-1.2.3/utils/chemdiff/examples/set3.sdf000066400000000000000000000014671271037650300223100ustar00rootroot00000000000000 Mrv0540 12281004172D 8 8 0 0 0 0 999 V2000 -1.3554 1.5911 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0698 1.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0698 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3554 -0.0589 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6409 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6409 1.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3554 2.4161 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 0.0736 1.5911 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 6 2 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 4 5 2 0 0 0 0 5 6 1 0 0 0 0 1 7 1 0 0 0 0 6 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/set4.sdf000066400000000000000000000014671271037650300223110ustar00rootroot00000000000000 Mrv0540 12281004172D 8 8 0 0 0 0 999 V2000 -1.3554 1.5911 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0698 1.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0698 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3554 -0.0589 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6409 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6409 1.1786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3554 2.4161 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 0.0736 1.5911 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 6 1 0 0 0 0 2 3 1 0 0 0 0 3 4 2 0 0 0 0 4 5 1 0 0 0 0 5 6 2 0 0 0 0 1 7 1 0 0 0 0 6 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/set5_left.sdf000066400000000000000000000100041271037650300233070ustar00rootroot00000000000000 Mrv0540 05211112502D 4 3 0 0 0 0 999 V2000 -1.6500 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.8250 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -0.3609 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0625 1.0680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 05231121062D 3 2 0 0 0 0 999 V2000 -4.5080 1.0312 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -3.6830 1.0312 0.0000 C 0 2 0 0 0 0 0 0 0 0 0 0 -2.8580 1.0312 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M CHG 3 1 -1 2 2 3 -1 M END $$$$ Mrv0540 05231121052D 3 2 0 0 0 0 999 V2000 -4.5080 1.0312 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -3.6830 1.0312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8580 1.0312 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 2 0 0 0 0 M END $$$$ Mrv0540 05231121092D 9 10 0 0 0 0 999 V2000 -3.7714 1.8563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4859 1.4438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4859 0.6187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7714 0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2868 1.7394 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0570 1.4438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0570 0.6187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2169 0.4064 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.7676 1.0983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 4 7 2 0 0 0 0 1 6 2 0 0 0 0 5 6 1 0 0 0 0 7 8 1 0 0 0 0 8 9 1 0 0 0 0 5 9 2 0 0 0 0 6 7 1 0 0 0 0 M END $$$$ Mrv0540 05231121122D 12 12 0 0 0 0 999 V2000 -2.9170 1.7089 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -3.6314 1.2964 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -3.6314 0.4714 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 -2.9170 0.0589 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -2.2025 0.4714 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 -2.2025 1.2964 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 -2.9170 2.5339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4880 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4880 0.0589 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.9170 -0.7661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.3459 0.0589 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.3459 1.7089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 6 1 0 0 0 0 1 6 1 0 0 0 0 1 7 1 1 0 0 0 6 8 1 6 0 0 0 5 9 1 1 0 0 0 4 10 1 6 0 0 0 3 11 1 1 0 0 0 2 12 1 6 0 0 0 M END $$$$ Mrv0540 05231121122D 8 8 0 0 0 0 999 V2000 -4.0955 1.1491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.8100 0.7366 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.8100 -0.0884 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.0955 -0.5009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3811 -0.0884 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.3811 0.7366 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.5245 1.1491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.0955 -1.3259 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 4 5 2 0 0 0 0 5 6 1 0 0 0 0 1 6 2 0 0 0 0 2 7 1 0 0 0 0 4 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/set5_right.sdf000066400000000000000000000104631271037650300235030ustar00rootroot00000000000000 Mrv0540 05211112502D 4 3 0 0 0 0 999 V2000 -0.5760 1.2121 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.4010 1.2121 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.1635 0.4976 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8135 0.4976 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 2 4 1 0 0 0 0 M END $$$$ Mrv0540 05231121062D 3 2 0 0 0 0 999 V2000 -4.5080 1.0312 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 -3.6830 1.0312 0.0000 C 0 2 0 0 0 0 0 0 0 0 0 0 -2.8580 1.0312 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 M CHG 3 1 -1 2 2 3 -1 M END $$$$ Mrv0540 05231121102D 16 17 0 0 0 0 999 V2000 -3.7714 1.8563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4859 1.4438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.4859 0.6187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7714 0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2868 1.7394 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0570 1.4438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.0570 0.6187 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2169 0.4064 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.7676 1.0983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.2004 1.8563 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -3.7714 2.6813 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -5.2004 0.2062 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -3.7714 -0.6188 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -2.0732 2.5363 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -0.9437 1.1414 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.9158 -0.3617 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 4 0 0 0 0 2 3 4 0 0 0 0 3 4 4 0 0 0 0 4 7 4 0 0 0 0 1 6 4 0 0 0 0 5 6 4 0 0 0 0 7 8 4 0 0 0 0 8 9 4 0 0 0 0 5 9 4 0 0 0 0 6 7 4 0 0 0 0 2 10 1 0 0 0 0 1 11 1 0 0 0 0 3 12 1 0 0 0 0 4 13 1 0 0 0 0 5 14 1 0 0 0 0 9 15 1 0 0 0 0 8 16 1 0 0 0 0 M END $$$$ Mrv0540 05231121122D 12 12 0 0 0 0 999 V2000 2.1434 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4289 0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4289 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1434 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7145 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7145 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7145 -0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7145 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7145 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7145 2.0625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 3 5 1 0 0 0 0 5 6 1 0 0 0 0 5 7 1 0 0 0 0 7 8 1 0 0 0 0 7 9 1 0 0 0 0 9 10 1 0 0 0 0 9 11 1 0 0 0 0 2 11 1 0 0 0 0 11 12 1 0 0 0 0 M END $$$$ Mrv0540 05231121132D 8 8 0 0 0 0 999 V2000 -3.3460 0.0424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7585 0.7568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5835 0.7568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.9960 0.0424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -4.5835 -0.6721 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.7585 -0.6721 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -5.8210 0.0424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5210 0.0424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 4 5 2 0 0 0 0 5 6 1 0 0 0 0 1 6 2 0 0 0 0 4 7 1 0 0 0 0 1 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/examples/test_stereo.sdf000066400000000000000000000077261271037650300237760ustar00rootroot00000000000000 Mrv0540 04081117462D 1 0 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 M END $$$$ Mrv0540 04081117462D 1 0 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 M END $$$$ Mrv0540 04081117462D 1 0 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 M END $$$$ Mrv0540 04081117462D 4 3 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.3259 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.9134 -0.3609 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5634 1.0680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 04081117462D 4 3 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.3259 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.9134 -0.3609 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5634 1.0680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 04081117462D 4 3 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.3259 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0312 1.2302 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5634 1.0680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 04081117462D 4 3 0 0 0 0 999 V2000 -2.1509 0.3536 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.3259 0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0312 1.2302 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5634 1.0680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 3 0 0 0 2 3 1 0 0 0 0 1 4 1 0 0 0 0 M END $$$$ Mrv0540 04081117472D 6 5 0 0 0 0 999 V2000 -1.1196 0.7071 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -0.4052 1.1196 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 0.3093 0.7071 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.8341 1.1196 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.4052 1.9446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1196 -0.1179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 1 0 0 0 1 4 1 1 0 0 0 2 5 1 0 0 0 0 1 6 1 0 0 0 0 M END $$$$ Mrv0540 04081117472D 6 5 0 0 0 0 999 V2000 -1.1196 0.7071 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -0.4052 1.1196 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 0.3093 0.7071 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.8341 1.1196 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.4052 1.9446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1196 -0.1179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 6 0 0 0 1 4 1 1 0 0 0 2 5 1 0 0 0 0 1 6 1 0 0 0 0 M END $$$$ Mrv0540 04081117472D 6 5 0 0 0 0 999 V2000 -1.1196 0.7071 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -0.4052 1.1196 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 0.3093 0.7071 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.8341 1.1196 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -0.4052 1.9446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1196 -0.1179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 4 0 0 0 1 4 1 1 0 0 0 2 5 1 0 0 0 0 1 6 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/chemdiff/launch.bat000066400000000000000000000000551271037650300210500ustar00rootroot00000000000000start javaw -jar -Xss10m %0\..\chemdiff.jar Indigo-indigo-1.2.3/utils/chemdiff/nbproject/000077500000000000000000000000001271037650300210745ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/nbproject/build-impl.xml000066400000000000000000002301121271037650300236530ustar00rootroot00000000000000 Must set src.src.dir Must set build.dir Must set dist.dir Must set build.classes.dir Must set dist.javadoc.dir Must set build.test.classes.dir Must set build.test.results.dir Must set build.classes.excludes Must set dist.jar Must set javac.includes No tests executed. Must set JVM to use for profiling in profiler.info.jvm Must set profiler agent JVM arguments in profiler.info.jvmargs.agent Must select some files in the IDE or set javac.includes To run this application from the command line without Ant, try: java -jar "${dist.jar.resolved}" Must select one file in the IDE or set run.class Must select one file in the IDE or set run.class Must select one file in the IDE or set debug.class Must select one file in the IDE or set debug.class Must set fix.includes This target only works when run from inside the NetBeans IDE. Must select one file in the IDE or set profile.class This target only works when run from inside the NetBeans IDE. This target only works when run from inside the NetBeans IDE. This target only works when run from inside the NetBeans IDE. Must select one file in the IDE or set run.class Must select some files in the IDE or set test.includes Must select one file in the IDE or set run.class Must select one file in the IDE or set applet.url Must select some files in the IDE or set javac.includes Some tests failed; see details above. Must select some files in the IDE or set test.includes Some tests failed; see details above. Must select some files in the IDE or set test.class Must select some method in the IDE or set test.method Some tests failed; see details above. Must select one file in the IDE or set test.class Must select one file in the IDE or set test.class Must select some method in the IDE or set test.method Must select one file in the IDE or set applet.url Must select one file in the IDE or set applet.url Indigo-indigo-1.2.3/utils/chemdiff/nbproject/genfiles.properties000066400000000000000000000012041271037650300250030ustar00rootroot00000000000000build.xml.data.CRC32=fa735e82 build.xml.script.CRC32=11557795 build.xml.stylesheet.CRC32=28e38971@1.50.1.46 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=fa735e82 nbproject/build-impl.xml.script.CRC32=adc0114b nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 nbproject/profiler-build-impl.xml.data.CRC32=0b85334c nbproject/profiler-build-impl.xml.script.CRC32=abda56ed nbproject/profiler-build-impl.xml.stylesheet.CRC32=f10cf54c@1.11.1 Indigo-indigo-1.2.3/utils/chemdiff/nbproject/project.properties000066400000000000000000000060311271037650300246600ustar00rootroot00000000000000annotation.processing.enabled=true annotation.processing.enabled.in.editor=false annotation.processing.processors.list= annotation.processing.run.all.processors=true application.homepage=http://epam.com/opensource/indigo/chemdiff application.splash=META-INF/chemdiff-splash.png application.title=ChemDiff application.vendor=EPAM Systems build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated build.generated.sources.dir=${build.dir}/generated-sources # Only compile against the classpath explicitly listed here: build.sysclasspath=ignore build.test.classes.dir=${build.dir}/test/classes build.test.results.dir=${build.dir}/test/results # Uncomment to specify the preferred debugger connection transport: #debug.transport=dt_socket debug.classpath=\ ${run.classpath} debug.test.classpath=\ ${run.test.classpath} # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/chemdiff.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= file.reference.indigo-renderer.jar=..\\..\\dist\\indigo-java\\indigo-renderer.jar file.reference.indigo.jar=..\\..\\dist\\indigo-java\\indigo.jar file.reference.jna.jar=../../common/jna/jna.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=false jar.index=${jnlp.enabled} javac.classpath=\ ${reference.common-controls.jar}:\ ${file.reference.jna.jar}:\ ${file.reference.indigo-renderer.jar}:\ ${file.reference.indigo.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false javac.processorpath=\ ${javac.classpath} javac.source=1.5 javac.target=1.5 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir} javadoc.additionalparam= javadoc.author=false javadoc.encoding=${source.encoding} javadoc.noindex=false javadoc.nonavbar=false javadoc.notree=false javadoc.private=false javadoc.splitindex=true javadoc.use=true javadoc.version=false javadoc.windowtitle= jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" jnlp.codebase.type=no.codebase jnlp.descriptor=application jnlp.enabled=false jnlp.mixed.code=default jnlp.offline-allowed=false jnlp.signed=false jnlp.signing= jnlp.signing.alias= jnlp.signing.keystore= main.class=com.epam.indigo.chemdiff.Main manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false platform.active=default_platform project.common-controls=../../common/java/common-controls reference.common-controls.jar=${project.common-controls}/dist/common-controls.jar run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} # Space-separated list of JVM arguments used when running the project # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value # or test-sys-prop.name=value to set system properties for unit tests): run.jvmargs=-Xss20m run.test.classpath=\ ${javac.test.classpath}:\ ${build.test.classes.dir} source.encoding=UTF-8 src.src.dir=src Indigo-indigo-1.2.3/utils/chemdiff/nbproject/project.xml000066400000000000000000000015441271037650300232700ustar00rootroot00000000000000 org.netbeans.modules.java.j2seproject chemdiff common-controls jar jar clean jar Indigo-indigo-1.2.3/utils/chemdiff/src/000077500000000000000000000000001271037650300176755ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/src/com/000077500000000000000000000000001271037650300204535ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/000077500000000000000000000000001271037650300213755ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/000077500000000000000000000000001271037650300226465ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/000077500000000000000000000000001271037650300244135ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/CanonicalCodeGenerator.java000066400000000000000000000067721271037650300316230ustar00rootroot00000000000000package com.epam.indigo.chemdiff; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoException; import com.epam.indigo.IndigoObject; import com.epam.indigo.controls.IndigoCheckedException; import com.epam.indigo.controls.MoleculeItem; public class CanonicalCodeGenerator { private volatile CompareOptions _compare_options; private Indigo _cached_indigo; private IndigoObject _cached_charge_pattern; public CanonicalCodeGenerator (CompareOptions compare_options) { this._compare_options = compare_options; } private IndigoObject getChargePattern (IndigoObject mol) { if (mol.getIndigo() != _cached_indigo) { _cached_indigo = mol.getIndigo(); _cached_charge_pattern = _cached_indigo.loadSmarts( "[#6,#15,#16;+,++,+++]-,=[#6,#7,#8;-,--,---]"); } return _cached_charge_pattern; } private int _unseparateCharges (IndigoObject mol) { // At first check if charge configutation exists IndigoObject charge_pattern = getChargePattern(mol); IndigoObject pos_q_atom = charge_pattern.getAtom(0); IndigoObject neg_q_atom = charge_pattern.getAtom(1); IndigoObject q_bond = charge_pattern.getBond(0); int cnt = 0, prev_cnt; IndigoObject match_iter; do { IndigoObject matcher = mol.getIndigo().substructureMatcher(mol); match_iter = matcher.iterateMatches(charge_pattern); prev_cnt = cnt; for (IndigoObject match: match_iter) { IndigoObject pos_atom = match.mapAtom(pos_q_atom); IndigoObject neg_atom = match.mapAtom(neg_q_atom); IndigoObject bond = match.mapBond(q_bond); pos_atom.setCharge(pos_atom.charge() - 1); neg_atom.setCharge(neg_atom.charge() + 1); bond.setBondOrder(bond.bondOrder() + 1); cnt++; } } while (prev_cnt != cnt); return cnt; } public IndigoObject createPreparedObject (MoleculeItem object) throws IndigoCheckedException { Indigo indigo = object.getIndigo(); synchronized (indigo) { indigo.setOption("ignore-stereochemistry-errors", _compare_options.getStereocentersIgnoreFlag()); IndigoObject copy_to_modify = object.getObjectCopy(); return createClonedPreparedObject(copy_to_modify); } } private IndigoObject createClonedPreparedObject (IndigoObject copy_to_modify) throws IndigoCheckedException { try { if (_compare_options.getUnseparateChargesFlag()) _unseparateCharges(copy_to_modify); if (_compare_options.getAromFlag()) copy_to_modify.aromatize(); if (_compare_options.getCisTransIgnoreFlag()) copy_to_modify.clearCisTrans(); if (_compare_options.getStereocentersIgnoreFlag()) copy_to_modify.clearStereocenters(); return copy_to_modify; } catch (IndigoException ex) { throw new IndigoCheckedException(ex.getMessage(), ex); } } public IndigoObject createPreparedObject (IndigoObject object) throws IndigoCheckedException { return createClonedPreparedObject(object.clone()); } public String generate (MoleculeItem object) throws IndigoCheckedException { try { return createPreparedObject(object).canonicalSmiles(); } catch (IndigoException ex) { throw new IndigoCheckedException(ex.getMessage(), ex); } } } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/CompareOptions.java000066400000000000000000000027441271037650300302270ustar00rootroot00000000000000package com.epam.indigo.chemdiff; public class CompareOptions { private boolean _arom_flag = true; private boolean _cistrans_ignore_flag = false; private boolean _stereocenters_ignore_flag = false; private boolean _unseparate_charges = true; public CompareOptions (boolean arom_flag, boolean cistrans_ignore_flag, boolean stereocenters_ignore_flag, boolean unseparate_charges) { this._arom_flag = arom_flag; this._cistrans_ignore_flag = cistrans_ignore_flag; this._stereocenters_ignore_flag = stereocenters_ignore_flag; this._unseparate_charges = unseparate_charges; } public void setAromFlag (boolean arom_flag) { this._arom_flag = arom_flag; } public void setCisTransIgnoreFlag (boolean cistrans_ignore_flag) { this._cistrans_ignore_flag = cistrans_ignore_flag; } public void setStereocentersIgnoreFlag (boolean stereocenters_ignore_flag) { this._stereocenters_ignore_flag = stereocenters_ignore_flag; } public void setUnseparateChargesFlag (boolean unseparate_charges) { this._unseparate_charges = unseparate_charges; } public boolean getAromFlag () { return _arom_flag; } public boolean getCisTransIgnoreFlag () { return _cistrans_ignore_flag; } public boolean getStereocentersIgnoreFlag () { return _stereocenters_ignore_flag; } public boolean getUnseparateChargesFlag () { return _unseparate_charges; } } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/Main.java000066400000000000000000000024211271037650300261410ustar00rootroot00000000000000package com.epam.indigo.chemdiff; import com.epam.indigo.controls.MessageBox; import java.io.PrintWriter; import java.io.StringWriter; import javax.swing.UIManager; public class Main { public static void main (String[] args) throws InterruptedException { try { /* * Enumerate look and feel and set it for testing LookAndFeelInfo[] installedLookAndFeels = UIManager.getInstalledLookAndFeels(); for (LookAndFeelInfo info: installedLookAndFeels) System.out.println(info.getClassName()); */ //UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { System.out.println("Error setting native LAF: " + e); } try { MainFrame mf = new MainFrame(); mf.setLocationRelativeTo(null); mf.setVisible(true); } catch (Throwable err) { StringWriter sw = new StringWriter(); err.printStackTrace(new PrintWriter(sw)); String error_as_string = sw.toString(); MessageBox.show(null, error_as_string, "Error", MessageBox.ICON_ERROR); System.exit(0); } } } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MainFrame.form000066400000000000000000000455321271037650300271500ustar00rootroot00000000000000 Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MainFrame.java000066400000000000000000000666761271037650300271420ustar00rootroot00000000000000package com.epam.indigo.chemdiff; import com.epam.indigo.controls.Global; import com.epam.indigo.controls.MoleculeItem; import com.epam.indigo.controls.CommonUtils; import com.epam.indigo.controls.IndigoCheckedException; import com.epam.indigo.controls.MessageBox; import com.epam.indigo.controls.MolSaver; import com.epam.indigo.controls.ProgressStatusDialog; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URISyntaxException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import javax.swing.SwingWorker; public class MainFrame extends javax.swing.JFrame { public static final int LARGE_HEIGHT = 250; private static final int COMPACT_HEIGHT = 70; private static final int MEDIUM_HEIGHT = 160; CompareOptions compare_options; CanonicalCodeGenerator csmiles_generator; MolSaver mol_saver1; MolSaver mol_saver2; /** Creates new form MainFrame */ public MainFrame () { initComponents(); Global.indigo.setOption("render-margins", "5,2"); Global.indigo.setOption("treat-x-as-pseudoatom", "true"); Global.indigo.setOption("ignore-noncritical-query-features", "true"); Global.indigo.setOption("render-coloring", true); Global.indigo.setOption("render-comment-font-size", "14"); Global.indigo.setOption("render-bond-length", "70"); allocateCompareOptions(); setTitle("ChemDiff"); setRowHeight(MEDIUM_HEIGHT); } private void allocateCompareOptions () { compare_options = new CompareOptions(aromatizer_check.getState(), cistrans_check.getState(), stereocenters_check.getState(), unseparate_charges_check.getState()); csmiles_generator = new CanonicalCodeGenerator(compare_options); out_table1.setCanonicalCodeGenerator(csmiles_generator); out_table2.setCanonicalCodeGenerator(csmiles_generator); out_table_common.setCanonicalCodeGenerator(csmiles_generator); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; tabbed_panel = new javax.swing.JTabbedPane(); in_tab = new javax.swing.JPanel(); in_table_1 = new com.epam.indigo.controls.MoleculesInputTable(); in_table_2 = new com.epam.indigo.controls.MoleculesInputTable(); compare_button = new javax.swing.JButton(); out_tab = new javax.swing.JPanel(); out_table_common = new com.epam.indigo.chemdiff.MultiMoleculeOutputTable(); out_table1 = new com.epam.indigo.chemdiff.MultiMoleculeOutputTable(); out_table2 = new com.epam.indigo.chemdiff.MultiMoleculeOutputTable(); main_menu_bar = new javax.swing.JMenuBar(); menu_file = new javax.swing.JMenu(); load1_mi = new javax.swing.JMenuItem(); load2_mi = new javax.swing.JMenuItem(); jSeparator1 = new javax.swing.JPopupMenu.Separator(); exit_mi = new javax.swing.JMenuItem(); menu_view = new javax.swing.JMenu(); jMenu1 = new javax.swing.JMenu(); menu_view_compact = new javax.swing.JCheckBoxMenuItem(); menu_view_medium = new javax.swing.JCheckBoxMenuItem(); menu_view_large = new javax.swing.JCheckBoxMenuItem(); menu_options = new javax.swing.JMenu(); aromatizer_check = new javax.swing.JCheckBoxMenuItem(); stereocenters_check = new javax.swing.JCheckBoxMenuItem(); cistrans_check = new javax.swing.JCheckBoxMenuItem(); merge_duplicates_check = new javax.swing.JCheckBoxMenuItem(); unseparate_charges_check = new javax.swing.JCheckBoxMenuItem(); show_duplicates_on_top_check = new javax.swing.JCheckBoxMenuItem(); show_invalid_on_top_check = new javax.swing.JCheckBoxMenuItem(); menu_help = new javax.swing.JMenu(); online_help = new javax.swing.JMenuItem(); about_mi = new javax.swing.JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); tabbed_panel.setPreferredSize(new java.awt.Dimension(811, 910)); in_tab.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); in_tab.setPreferredSize(new java.awt.Dimension(660, 760)); in_tab.setLayout(new java.awt.GridBagLayout()); in_table_1.setTitle("First file"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; in_tab.add(in_table_1, gridBagConstraints); in_table_2.setTitle("Second file"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; in_tab.add(in_table_2, gridBagConstraints); compare_button.setText("Compare"); compare_button.setMargin(new java.awt.Insets(5, 30, 5, 30)); compare_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { compare_buttonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; in_tab.add(compare_button, gridBagConstraints); tabbed_panel.addTab("input", in_tab); out_tab.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); out_tab.setLayout(new java.awt.GridBagLayout()); out_table_common.setMinimumSize(new java.awt.Dimension(175, 179)); out_table_common.setTitle("Common"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; out_tab.add(out_table_common, gridBagConstraints); out_table1.setIdColumnCount(1); out_table1.setMinimumSize(new java.awt.Dimension(135, 179)); out_table1.setTitle("Unique in 1st"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; out_tab.add(out_table1, gridBagConstraints); out_table2.setIdColumnCount(1); out_table2.setMinimumSize(new java.awt.Dimension(135, 179)); out_table2.setTitle("Unique in 2nd"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; out_tab.add(out_table2, gridBagConstraints); tabbed_panel.addTab("output", out_tab); menu_file.setText("File"); menu_file.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { load_secondActionPerformed(evt); } }); load1_mi.setText("Load first set"); load1_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { load1_miActionPerformed(evt); } }); menu_file.add(load1_mi); load2_mi.setText("Load second set"); load2_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { load2_miActionPerformed(evt); } }); menu_file.add(load2_mi); menu_file.add(jSeparator1); exit_mi.setText("Exit"); exit_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { exit_miActionPerformed(evt); } }); menu_file.add(exit_mi); main_menu_bar.add(menu_file); menu_view.setText("View"); jMenu1.setText("Layout size"); menu_view_compact.setText("Compact"); menu_view_compact.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { onLayoutSizeChanged(evt); } }); jMenu1.add(menu_view_compact); menu_view_medium.setSelected(true); menu_view_medium.setText("Medium"); menu_view_medium.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { onLayoutSizeChanged(evt); } }); jMenu1.add(menu_view_medium); menu_view_large.setText("Large"); menu_view_large.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { onLayoutSizeChanged(evt); } }); jMenu1.add(menu_view_large); menu_view.add(jMenu1); main_menu_bar.add(menu_view); menu_options.setText("Options"); aromatizer_check.setSelected(true); aromatizer_check.setText("Aromatize molecules"); aromatizer_check.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { aromatizer_checkStateChanged(evt); } }); menu_options.add(aromatizer_check); stereocenters_check.setText("Ignore stereocenters"); stereocenters_check.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { stereocenters_checkStateChanged(evt); } }); menu_options.add(stereocenters_check); cistrans_check.setText("Ignore cis-trans bonds"); cistrans_check.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { cistrans_checkStateChanged(evt); } }); menu_options.add(cistrans_check); merge_duplicates_check.setSelected(true); merge_duplicates_check.setText("Merge duplicate molecules"); merge_duplicates_check.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); menu_options.add(merge_duplicates_check); unseparate_charges_check.setSelected(true); unseparate_charges_check.setText("Dipole to covalent bond"); unseparate_charges_check.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { unseparate_charges_checkStateChanged(evt); } }); menu_options.add(unseparate_charges_check); show_duplicates_on_top_check.setSelected(true); show_duplicates_on_top_check.setText("Show duplicates on top"); menu_options.add(show_duplicates_on_top_check); show_invalid_on_top_check.setSelected(true); show_invalid_on_top_check.setText("Show invalid molecules on top"); menu_options.add(show_invalid_on_top_check); main_menu_bar.add(menu_options); menu_help.setText("Help"); online_help.setText("Online Help"); online_help.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { online_helpActionPerformed(evt); } }); menu_help.add(online_help); about_mi.setText("About"); about_mi.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { about_miActionPerformed(evt); } }); menu_help.add(about_mi); main_menu_bar.add(menu_help); setJMenuBar(main_menu_bar); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(tabbed_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 793, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(tabbed_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 534, Short.MAX_VALUE) ); pack(); }// //GEN-END:initComponents private void load_secondActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_load_secondActionPerformed in_table_2.openLoadingDialog(); }//GEN-LAST:event_load_secondActionPerformed private void exit_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exit_miActionPerformed dispose(); }//GEN-LAST:event_exit_miActionPerformed private void about_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_about_miActionPerformed CommonUtils.showAboutDialog(this, "ChemDiff", "http://epam.com/opensource/indigo/chemdiff"); }//GEN-LAST:event_about_miActionPerformed private void compare_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_compare_buttonActionPerformed final ProgressStatusDialog dlg = new ProgressStatusDialog(this, true); // Update molecules properties SwingWorker compare_molecules = new SwingWorker() { private int max_steps = 3; private int cur_step = 0; private int mols_error_count = 0; private void setStepProgress (int progress) { setProgress((100 * cur_step + progress) / max_steps); } private HashMap getCanonicalMap (ArrayList mols) { HashMap map = new HashMap(); HashMap same_canonical_size = new HashMap(); boolean merge = merge_duplicates_check.getState(); int processed = 0; for (MoleculeItem m : mols) { setStepProgress(100 * processed / mols.size()); processed++; if ((processed % 10000) == 0) { System.gc(); System.out.println(String.format("Indigo objects count after %d molecules: %d", processed, Global.indigo.countReferences())); } if (isCancelled()) return null; String can_smiles, src_can_smiles; String error_msg = null; try { src_can_smiles = csmiles_generator.generate(m); } catch (IndigoCheckedException ex) { error_msg = ex.getMessage(); // Generate unique error message and save it as a smiles src_can_smiles = String.format("Error #%d: %s", mols_error_count, error_msg); mols_error_count++; } can_smiles = src_can_smiles; // Check if this smiles already presents in the map if (map.containsKey(can_smiles)) { Integer count = same_canonical_size.get(can_smiles); count++; if (merge) { // Add this molecules to the existing group map.get(can_smiles).getGroup(0).add(m); continue; } else { // Add serial number to the canonical smiles can_smiles = can_smiles + " $" + count.toString(); } } MultipleMoleculeItem mul_item = new MultipleMoleculeItem(m, csmiles_generator); mul_item.setErrorMessageToRender(error_msg); mul_item.setCanonicalCode(src_can_smiles); map.put(can_smiles, mul_item); same_canonical_size.put(can_smiles, 1); } System.out.println(String.format("Indigo objects count: %d", Global.indigo.countReferences())); return map; } @Override protected Void doInBackground () throws Exception { ArrayList set1 = in_table_1.getMolecules(); ArrayList set2 = in_table_2.getMolecules(); // Create canonical code mappings cur_step = 0; dlg.setStepName("Preparing the first set"); HashMap map1 = getCanonicalMap(set1); cur_step = 1; dlg.setStepName("Preparing the second set"); HashMap map2 = getCanonicalMap(set2); if (isCancelled()) return null; setProgress(10); // Intersect and find difference Set keys1 = map1.keySet(); Set keys2 = map2.keySet(); Set unique1_keys = new HashSet(keys1); unique1_keys.removeAll(keys2); Set unique2_keys = new HashSet(keys2); unique2_keys.removeAll(keys1); Set common_keys = new HashSet(keys1); common_keys.removeAll(unique1_keys); setProgress(100); ArrayList common_mols = new ArrayList(); ArrayList unique1_mols = new ArrayList(); ArrayList unique2_mols = new ArrayList(); // Create list with common molecules for (String common_key : common_keys) { MultipleMoleculeItem common_mol = new MultipleMoleculeItem(2, csmiles_generator); // Add molecules from the first group common_mol.getGroup(0).addAll(map1.get(common_key).getGroup(0)); // Add molecules from the second group common_mol.getGroup(1).addAll(map2.get(common_key).getGroup(0)); common_mol.setCanonicalCode(map1.get(common_key).getCanonicalCode()); common_mols.add(common_mol); } // Create lists with unique molecules for (String key1 : unique1_keys) unique1_mols.add(map1.get(key1)); for (String key2 : unique2_keys) unique2_mols.add(map2.get(key2)); final boolean sort_by_size = show_duplicates_on_top_check.getState(); final boolean show_invalid_on_top = show_invalid_on_top_check.getState(); Comparator comparator = new Comparator() { public int compare (MultipleMoleculeItem o1, MultipleMoleculeItem o2) { if (show_invalid_on_top) { Boolean b1 = (o1.getErrorMessageToRender() != null); Boolean b2 = (o2.getErrorMessageToRender() != null); if (b1 != b2) return b2.compareTo(b1); } if (sort_by_size) { int s1 = o1.getGroup(0).size(); if (o1.getGroupCount() > 1) s1 += o1.getGroup(1).size(); int s2 = o2.getGroup(0).size(); if (o2.getGroupCount() > 1) s2 += o2.getGroup(1).size(); if (s1 != s2) return s2 - s1; } String id1 = o1.getId(0), id2 = o2.getId(0); Integer len1 = id1.length(), len2 = id2.length(); if (len1 != len2) return len1.compareTo(len2); return id1.compareTo(id2); } }; if (isCancelled()) return null; Collections.sort(unique1_mols, comparator); Collections.sort(unique2_mols, comparator); Collections.sort(common_mols, comparator); cur_step = 2; dlg.setStepName("Comparing the molecules"); setStepProgress(25); out_table1.setMolecules(unique1_mols); setStepProgress(50); out_table2.setMolecules(unique2_mols); setStepProgress(75); out_table_common.setMolecules(common_mols); setStepProgress(100); if (isCancelled()) return null; tabbed_panel.setSelectedIndex(1); allocateCompareOptions(); return null; } }; dlg.setTitle("Comparing the molecules..."); dlg.executeSwingWorker(compare_molecules); boolean cancelled = false; try { // Check if work wasn't aborted compare_molecules.get(); } catch (InterruptedException ex) { cancelled = true; } catch (ExecutionException ex) { StringWriter sw = new StringWriter(); ex.printStackTrace(new PrintWriter(sw)); String error_as_string = sw.toString(); MessageBox.show(null, error_as_string, "Error", MessageBox.ICON_ERROR); cancelled = true; } if (cancelled) { out_table1.clear(); out_table2.clear(); out_table_common.clear(); } }//GEN-LAST:event_compare_buttonActionPerformed private void aromatizer_checkStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_aromatizer_checkStateChanged compare_options.setAromFlag(aromatizer_check.getState()); }//GEN-LAST:event_aromatizer_checkStateChanged private void stereocenters_checkStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_stereocenters_checkStateChanged compare_options.setStereocentersIgnoreFlag(stereocenters_check.getState()); }//GEN-LAST:event_stereocenters_checkStateChanged private void cistrans_checkStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_cistrans_checkStateChanged compare_options.setCisTransIgnoreFlag(cistrans_check.getState()); }//GEN-LAST:event_cistrans_checkStateChanged private void unseparate_charges_checkStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_unseparate_charges_checkStateChanged compare_options.setUnseparateChargesFlag(unseparate_charges_check.getState()); }//GEN-LAST:event_unseparate_charges_checkStateChanged private void online_helpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_online_helpActionPerformed try { java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); java.net.URI uri = new java.net.URI("http://epam.com/opensource/indigo/chemdiff"); desktop.browse(uri); } catch (URISyntaxException ex) { Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex); } }//GEN-LAST:event_online_helpActionPerformed private void onLayoutSizeChanged(java.awt.event.ActionEvent evt)//GEN-FIRST:event_onLayoutSizeChanged {//GEN-HEADEREND:event_onLayoutSizeChanged menu_view_compact.setState(false); menu_view_medium.setState(false); menu_view_large.setState(false); if (evt.getSource() == menu_view_compact) { setRowHeight(COMPACT_HEIGHT); menu_view_compact.setState(true); } else if (evt.getSource() == menu_view_medium) { setRowHeight(MEDIUM_HEIGHT); menu_view_medium.setState(true); } else { setRowHeight(LARGE_HEIGHT); menu_view_large.setState(true); } }//GEN-LAST:event_onLayoutSizeChanged private void load1_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_load1_miActionPerformed in_table_1.clear(); in_table_1.openLoadingDialog(); }//GEN-LAST:event_load1_miActionPerformed private void load2_miActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_load2_miActionPerformed in_table_2.clear(); in_table_2.openLoadingDialog(); }//GEN-LAST:event_load2_miActionPerformed private void setRowHeight (int height) { in_table_1.setRowHeight(height); in_table_2.setRowHeight(height); out_table_common.setRowHeight(height); out_table1.setRowHeight(height); out_table2.setRowHeight(height); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem about_mi; private javax.swing.JCheckBoxMenuItem aromatizer_check; private javax.swing.JCheckBoxMenuItem cistrans_check; private javax.swing.JButton compare_button; private javax.swing.JMenuItem exit_mi; private javax.swing.JPanel in_tab; private com.epam.indigo.controls.MoleculesInputTable in_table_1; private com.epam.indigo.controls.MoleculesInputTable in_table_2; private javax.swing.JMenu jMenu1; private javax.swing.JPopupMenu.Separator jSeparator1; private javax.swing.JMenuItem load1_mi; private javax.swing.JMenuItem load2_mi; private javax.swing.JMenuBar main_menu_bar; private javax.swing.JMenu menu_file; private javax.swing.JMenu menu_help; private javax.swing.JMenu menu_options; private javax.swing.JMenu menu_view; private javax.swing.JCheckBoxMenuItem menu_view_compact; private javax.swing.JCheckBoxMenuItem menu_view_large; private javax.swing.JCheckBoxMenuItem menu_view_medium; private javax.swing.JCheckBoxMenuItem merge_duplicates_check; private javax.swing.JMenuItem online_help; private javax.swing.JPanel out_tab; private com.epam.indigo.chemdiff.MultiMoleculeOutputTable out_table1; private com.epam.indigo.chemdiff.MultiMoleculeOutputTable out_table2; private com.epam.indigo.chemdiff.MultiMoleculeOutputTable out_table_common; private javax.swing.JCheckBoxMenuItem show_duplicates_on_top_check; private javax.swing.JCheckBoxMenuItem show_invalid_on_top_check; private javax.swing.JCheckBoxMenuItem stereocenters_check; private javax.swing.JTabbedPane tabbed_panel; private javax.swing.JCheckBoxMenuItem unseparate_charges_check; // End of variables declaration//GEN-END:variables } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MultiMoleculeOutputTable.java000066400000000000000000000106761271037650300322410ustar00rootroot00000000000000/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * MoleculeOutputTable.java * * Created on Mar 5, 2011, 10:02:17 PM */ package com.epam.indigo.chemdiff; import com.epam.indigo.IndigoException; import com.epam.indigo.IndigoObject; import com.epam.indigo.controls.*; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; /** * * @author achurinov */ public class MultiMoleculeOutputTable extends MoleculeOutputTable { private String _common_canonical_code; private CanonicalCodeGenerator _canonical_generator; public MultiMoleculeOutputTable() { } public void setCommonCanonicalCode(String code) { _common_canonical_code = code; } public void setCanonicalCodeGenerator(CanonicalCodeGenerator canonical_generator) { _canonical_generator = canonical_generator; } private void showSingleMolecule(RenderableObjectWithId item, boolean normalized, String canonical_code) { Frame parent = (Frame) getTopLevelAncestor(); IndigoObject obj = item.getRenderableObject(); if (obj == null) { String message = String.format("Exception:\n%s", item.getErrorMessageToRender()); MessageBox.show(parent, message, "Error during loading this molecule", MessageBox.ICON_ERROR); return; } if (normalized) { try { obj = _canonical_generator.createPreparedObject(obj); } catch (IndigoCheckedException ex) { MessageBox.show(parent, ex.getMessage(), "Error during normalizing this molecule", MessageBox.ICON_ERROR); return; } try { obj.markEitherCisTrans(); } catch (IndigoException ex) { } } // Show details window for single molecule SingleIndigoObjectWindow details = new SingleIndigoObjectWindow(parent, obj, item.getIndigoRenderer(), false); if (item.getErrorMessageToRender() != null) { details.setInformationMessage(item.getErrorMessageToRender()); } else { details.setInformationMessage(canonical_code); } String title = item.getId(0); if (normalized) { title += " (normalized)"; } details.setTitle(title); details.setVisible(true); } @Override protected void showMolecule(RenderableObjectWithId item) { Frame parent = (Frame) getTopLevelAncestor(); MoleculeItem single = null; MultipleMoleculeItem mul_item = null; String canonical_code; if (item instanceof MoleculeItem) { single = (MoleculeItem) item; canonical_code = _common_canonical_code; } else { mul_item = (MultipleMoleculeItem) item; if (mul_item.isSingleMolecule()) { single = mul_item.getGroup(0).get(0); } canonical_code = mul_item.getCanonicalCode(); } if (single != null) { showSingleMolecule(item, false, canonical_code); } else { // Show window with multiple molecules MultipleMoleculeWindow details = new MultipleMoleculeWindow(parent, mul_item); details.setCommonCanonicalCode(canonical_code); details.setRowHeight(getRowHeight()); details.setCanonicalCodeGenerator(_canonical_generator); details.setVisible(true); } } @Override protected void addAdditionalItemsToPopup(JPopupMenu _popup_menu, TableCellMouseEvent evt) { final TableCellMouseEvent evt_final = evt; JMenuItem show_normalized_mi = new JMenuItem("Show normalized molecule"); show_normalized_mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { RenderableObjectWithId item = _molecules.get(evt_final.row); String canonical_code; if (item instanceof MoleculeItem) { canonical_code = _common_canonical_code; } else { canonical_code = ((MultipleMoleculeItem) item).getCanonicalCode(); } showSingleMolecule(item, true, canonical_code); } }); _popup_menu.add(show_normalized_mi); } } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MultipleMoleculeItem.java000066400000000000000000000075151271037650300313660ustar00rootroot00000000000000package com.epam.indigo.chemdiff; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; import com.epam.indigo.chemdiff.CanonicalCodeGenerator; import com.epam.indigo.controls.Global; import com.epam.indigo.controls.IndigoCheckedException; import com.epam.indigo.controls.MoleculeItem; import com.epam.indigo.controls.RenderableObject; import com.epam.indigo.controls.RenderableObjectWithId; import java.util.ArrayList; public class MultipleMoleculeItem extends RenderableObjectWithId { private int _group_count; private ArrayList> _groups; private CanonicalCodeGenerator _canonical_generator; private String _canonical_code; public MultipleMoleculeItem(int group_count, CanonicalCodeGenerator canonical_generator) { _group_count = group_count; _groups = new ArrayList>(); for (int i = 0; i < _group_count; i++) _groups.add(new ArrayList()); _canonical_generator = canonical_generator; } public MultipleMoleculeItem (MoleculeItem mol, CanonicalCodeGenerator canonical_generator) { this(1, canonical_generator); _getGroup(0).add(mol); } public ArrayList getGroup(int group_index) { return _getGroup(group_index); } private ArrayList _getGroup(int group_index) { return _groups.get(group_index); } public int getGroupCount () { return _group_count; } @Override public String getId() { if (isSingleMolecule()) return getId(0); throw new RuntimeException("getId() has no implementation"); } @Override public String getId(int group_index) { // Merge ids from corresponding group StringBuilder sb = new StringBuilder(); for (MoleculeItem item: getGroup(group_index)) { if (sb.length() > 0) sb.append("\n"); sb.append(item.getId()); } return sb.toString(); } public boolean isSingleMolecule () { return getGroupCount() == 1 && getGroup(0).size() == 1; } @Override public IndigoObject getRenderableObject() { // TODO: set name too... RenderableObject rend_obj = _groups.get(0).get(0); if (isSingleMolecule()) { IndigoObject obj = rend_obj.getRenderableObject(); if (obj == null) setErrorMessageToRender(rend_obj.getErrorMessageToRender()); return obj; } // For rendering pick any object from this group // and prepare by the canonical code generator MoleculeItem m = _groups.get(0).get(0); IndigoObject obj_prepared; try { obj_prepared = _canonical_generator.createPreparedObject(m); } catch (IndigoCheckedException ex) { setErrorMessageToRender(ex.getMessage()); return null; } obj_prepared.markEitherCisTrans(); // Clear properties obj_prepared.clearProperties(); if (getGroupCount() == 1) obj_prepared.setProperty("Id", getId(0)); else { obj_prepared.setProperty("Id1", getId(0)); obj_prepared.setProperty("Id2", getId(1)); } return obj_prepared; } @Override public Indigo getIndigo () { return Global.indigo; } @Override public IndigoRenderer getIndigoRenderer () { return Global.indigo_renderer; } public IndigoObject getObjectCopy () throws IndigoCheckedException { IndigoObject obj = getRenderableObject(); if (obj == null) throw new IndigoCheckedException(getErrorMessageToRender()); return obj; } public String getCanonicalCode () { return _canonical_code; } public void setCanonicalCode (String code) { _canonical_code = code; } } Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MultipleMoleculeWindow.form000066400000000000000000000106451271037650300317570ustar00rootroot00000000000000
Indigo-indigo-1.2.3/utils/chemdiff/src/com/epam/indigo/chemdiff/MultipleMoleculeWindow.java000066400000000000000000000112441271037650300317310ustar00rootroot00000000000000/* * To change this template, choose Tools | Templates * and open the template in the editor. */ /* * MultipleMoleculeWindow.java * * Created on May 11, 2011, 4:34:43 PM */ package com.epam.indigo.chemdiff; import java.awt.Frame; /** * * @author rybalkin */ public class MultipleMoleculeWindow extends javax.swing.JFrame { /** Creates new form MultipleMoleculeWindow */ public MultipleMoleculeWindow (Frame parent, MultipleMoleculeItem mol) { initComponents(); output_table1.setMolecules(mol.getGroup(0)); if (mol.getGroupCount() == 1) { output_panel.remove(output_table2); output_table1.setTitle("Molecules"); setSize(getWidth() / 2, getHeight()); } else output_table2.setMolecules(mol.getGroup(1)); setLocationRelativeTo(parent); } void setCommonCanonicalCode (String code) { canonical_code_field.setText(code); output_table1.setCommonCanonicalCode(code); output_table2.setCommonCanonicalCode(code); } void setRowHeight (int height) { output_table1.setRowHeight(height); output_table2.setRowHeight(height); } public void setCanonicalCodeGenerator (CanonicalCodeGenerator canonical_generator) { output_table1.setCanonicalCodeGenerator(canonical_generator); output_table2.setCanonicalCodeGenerator(canonical_generator); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { jPanel2 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); canonical_code_field = new javax.swing.JTextField(); output_panel = new javax.swing.JPanel(); output_table1 = new com.epam.indigo.chemdiff.MultiMoleculeOutputTable(); output_table2 = new com.epam.indigo.chemdiff.MultiMoleculeOutputTable(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Molecules within a group"); getContentPane().setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.PAGE_AXIS)); jLabel1.setText("Canonical code:"); canonical_code_field.setEditable(false); canonical_code_field.setMaximumSize(new java.awt.Dimension(2147483647, 20)); javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(canonical_code_field, javax.swing.GroupLayout.DEFAULT_SIZE, 574, Short.MAX_VALUE) .addContainerGap()) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addGap(5, 5, 5) .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(canonical_code_field, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1))) ); getContentPane().add(jPanel2); output_panel.setLayout(new javax.swing.BoxLayout(output_panel, javax.swing.BoxLayout.LINE_AXIS)); output_table1.setIdColumnCount(1); output_table1.setTitle("From the first set"); output_panel.add(output_table1); output_table2.setIdColumnCount(1); output_table2.setTitle("From the second set"); output_panel.add(output_table2); getContentPane().add(output_panel); pack(); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField canonical_code_field; private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel2; private javax.swing.JPanel output_panel; private com.epam.indigo.chemdiff.MultiMoleculeOutputTable output_table1; private com.epam.indigo.chemdiff.MultiMoleculeOutputTable output_table2; // End of variables declaration//GEN-END:variables } Indigo-indigo-1.2.3/utils/indigo-cano/000077500000000000000000000000001271037650300175305ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/indigo-cano/CMakeLists.txt000066400000000000000000000011051271037650300222650ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(IndigoCano) include(DefineTest) include_directories(../../api) add_executable(indigo-cano main.c) target_link_libraries(indigo-cano indigo) if (UNIX) set_target_properties(indigo-cano PROPERTIES LINK_FLAGS "-pthread") endif() pack_executable(indigo-cano) add_test(NAME cano-simple-test COMMAND indigo-cano - "[C@@H](N)(C)C(=O)O") add_test(NAME cano-layered-test COMMAND indigo-cano - "[C@@H](N)(C)C(=O)O" -layered) add_test(NAME cano-option-test COMMAND indigo-cano - "[C@@H](N)(C)C(=O)O" -no-arom -no-cistrans -no-tetrahedral) Indigo-indigo-1.2.3/utils/indigo-cano/main.c000066400000000000000000000124351271037650300206250ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ // // This is a command line utility for producing canonical SMILES // or layered code for molecules in MOL or SDF format // #include #include #include #include "indigo.h" void onError (const char *message, void *context) { fflush(stdout); fprintf(stderr, "%s\n", message); fflush(stderr); exit(-1); } void usage () { printf( "Usage:\n" " indigo-cano filename.{mol,smi,cml,sdf,sdf.gz,rdf,rdf.gz} [parameters]\n" " indigo-cano - SMILES [parameters]\n" "Parameters:\n" " -smiles Output canonical SMILES (default)\n" " -layered Output canonical layered code\n" " -id ID field in SDF file\n" " -no-arom Do not aromatize molecules\n" " -no-tetrahedral Ignore tetrahedral stereocenters\n" " -no-cistrans Ignore cis-trans bonds information\n" "Examples:\n" " indigo-cano infile.sdf\n" " indigo-cano infile.sdf.gz -id molregno > results.txt\n" " indigo-cano infile.smi -layered -no-cistrans\n" " indigo-cano - 'NC1C=CC(O)=CC=1'\n" ); } int processMolecule (int mol, int smiles, int no_arom, int no_cistrans, int no_tetra) { if (no_cistrans) if (indigoClearCisTrans(mol) < 0) return -1; if (no_tetra) if (!indigoClearStereocenters(mol)) return -1; if (smiles && !no_arom) if (indigoAromatize(mol) < 0) return -1; if (smiles) { const char *res = indigoCanonicalSmiles(mol); if (res == 0) return -1; printf("%s\n", res); } else { const char *res = indigoLayeredCode(mol); if (res == 0) return -1; printf("%s\n", res); } return 1; } int main (int argc, char *argv[]) { int smiles = 1; int no_cistrans = 0; int no_tetra = 0; int no_arom = 0; int i = 2; const char *idfield = 0; const char *filename = 0; const char *ext = 0; if (argc < 2) { usage(); return -1; } if (strcmp(argv[1], "-") != 0) filename = argv[1]; else if (argc >= 3 && strcmp(argv[1], "-") == 0) i = 3; else { usage(); return -1; } while (i < argc) { if (strcmp(argv[i], "-smiles") == 0) smiles = 1; else if (strcmp(argv[i], "-layered") == 0) smiles = 0; else if (strcmp(argv[i], "-no-cistrans") == 0) no_cistrans = 1; else if (strcmp(argv[i], "-no-arom") == 0) no_arom = 1; else if (strcmp(argv[i], "-no-tetrahedral") == 0) no_tetra= 1; else if (strcmp(argv[i], "-id") == 0) { if (++i >= argc) { fprintf(stderr, "expecting an identifier after -id\n"); return -1; } idfield = argv[i]; } else { fprintf(stderr, "unknown parameter: %s\n", argv[i]); return -1; } i++; } indigoSetErrorHandler(onError, 0); if (filename == 0) { int mol = indigoLoadMoleculeFromString(argv[2]); processMolecule(mol, smiles, no_arom, no_cistrans, no_tetra); indigoFree(mol); return 0; } if (strlen(filename) > 4 && filename[strlen(filename) - 4] == '.') ext = filename + strlen(filename) - 3; else if (strlen(filename) > 7 && filename[strlen(filename) - 7] == '.') ext = filename + strlen(filename) - 6; else { fprintf(stderr, "input file format not recognized\n"); return -1; } if (strcmp(ext, "mol") == 0) { int mol = indigoLoadMoleculeFromFile(filename); processMolecule(mol, smiles, no_arom, no_cistrans, no_tetra); indigoFree(mol); return 0; } else if (strcmp(ext, "cml") == 0 || strcmp(ext, "sdf") == 0 || strcmp(ext, "sdf.gz") == 0 || strcmp(ext, "rdf") == 0 || strcmp(ext, "rdf.gz") == 0 || strcmp(ext, "smi") == 0 || strcmp(ext, "smi.gz") == 0) { int item, iter; if ((strstr(ext, "cml") != NULL)) iter = indigoIterateCMLFile(filename); else if (strstr(ext, "sdf") != NULL) iter = indigoIterateSDFile(filename); else if (strstr(ext, "rdf") != NULL) iter = indigoIterateRDFile(filename); else iter = indigoIterateSmilesFile(filename); while ((item = indigoNext(iter))) { indigoSetErrorHandler(0, 0); if (processMolecule(item, smiles, no_arom, no_cistrans, no_tetra) == -1) printf("%s\n", indigoGetLastError()); indigoSetErrorHandler(onError, 0); indigoFree(item); } indigoFree(iter); } else { fprintf(stderr, "input file format not recognized\n"); return -1; } return 0; } Indigo-indigo-1.2.3/utils/indigo-deco/000077500000000000000000000000001271037650300175225ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/indigo-deco/CMakeLists.txt000066400000000000000000000005671271037650300222720ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(IndigoDeco) include(DefineTest) include_directories(../../api ../../common) add_executable(indigo-deco main.c) target_link_libraries(indigo-deco indigo) if (UNIX) set_target_properties(indigo-deco PROPERTIES LINK_FLAGS "-pthread") endif() pack_executable(indigo-deco) add_test(NAME deco-simple-test COMMAND indigo-deco -h) Indigo-indigo-1.2.3/utils/indigo-deco/main.c000066400000000000000000000270001271037650300206110ustar00rootroot00000000000000 /**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include #include "indigo.h" #include "base_c/os_dir.h" void onError (const char *message, void *context) { fflush(stdout); fprintf(stderr, "%s\n", message); fflush(stderr); exit(-1); } void _replaceSlashes (char *str) { while (*str != 0) { if (*str == '\\') *str = '/'; str++; } } void _handleInputFile (const char *path, int structures) { if ((strlen(path) > 4 && strcmp(path + strlen(path) - 4, ".sdf") == 0) || (strlen(path) > 7 && strcmp(path + strlen(path) - 7, ".sdf.gz") == 0)) { int item, iter = indigoIterateSDFile(path); while ((item = indigoNext(iter))) { indigoArrayAdd(structures, item); indigoFree(item); } indigoFree(iter); } else if ((strlen(path) > 4 && strcmp(path + strlen(path) - 4, ".rdf") == 0) || (strlen(path) > 7 && strcmp(path + strlen(path) - 7, ".rdf.gz") == 0)) { int item, iter = indigoIterateRDFile(path); while ((item = indigoNext(iter))) { indigoArrayAdd(structures, item); indigoFree(item); } indigoFree(iter); } else if ((strlen(path) > 4 && strcmp(path + strlen(path) - 4, ".smi") == 0) || (strlen(path) > 7 && strcmp(path + strlen(path) - 7, ".smi.gz") == 0)) { int item, iter = indigoIterateSmilesFile(path); while ((item = indigoNext(iter))) { indigoArrayAdd(structures, item); indigoFree(item); } indigoFree(iter); } else if ((strlen(path) > 4 && strcmp(path + strlen(path) - 4, ".cml") == 0)) { int item, iter = indigoIterateCMLFile(path); while ((item = indigoNext(iter))) { indigoArrayAdd(structures, item); indigoFree(item); } indigoFree(iter); } else { int item = indigoLoadMoleculeFromFile(path); indigoArrayAdd(structures, item); indigoFree(item); } } void _printHelpMessage () { printf( "Usage:\n indigo-deco files [options]\n" "Perfoms molecule scaffold detection and R-group deconvolution\n" "Accepted formats are: Molfile, SDFile, RDFile, SMILES, CML\n" "Options:\n" "-h print this help message\n" "-a calculate approximate scaffold (default is exact)\n" "-s write maximum found scaffold to molfile\n" "-S write all found scaffolds to SD-file\n" "-l do not calculate scaffold, but load it from file\n" "-sr write scaffold with R-sites to a file\n" "-o write resulting highlighted molecules to file\n" "-r write resulting molecules with separated r-groups to file\n" "-na no aromatic consideration\n" "-- marks end of options\n" "\nExamples:\n\n" "indigo-deco *.mol -o hl.sdf -s scaf.sdf\n" " read molecules from molfiles in the current directory\n" " save maximum found scaffold to scaf.mol\n" " save highlighted molecules to hl.sdf\n" "indigo-deco structure.mol many.sdf -s scaf.mol -S allscafs.sdf -r rg.sdf \n" " read one molecule from structure.mol and multiple molecules from many.sdf\n" " save molecules with r-rgoups to rg.sdf\n" " save all found scaffolds to allscafs.sdf\n" "indigo-deco *.smi -d readyscaf.mol -o hl.sdf\n" " read multiple molecules from every SMILES file in the current directory\n" " read scaffold from readyscaf.mol\n" " save highlighted molecules to hl.sdf\n" ); } int main (int argc, const char** argv) { int i; int done_with_options = 0; int approximate = 0; int scaffold = 0; int aromatic = 1; const char *outfile_hl = 0; const char *outfile_rg = 0; const char *outfile_maxscaf = 0; const char *outfile_allscafs = 0; const char *outfile_scaf_r = 0; int deco = 0; int structures = 0; indigoSetErrorHandler(onError, 0); printf("R-Group deconvolution utility, powered by Indigo API version %s\n", indigoVersion()); structures = indigoCreateArray(); indigoSetOptionBool("treat-x-as-pseudoatom", 1); indigoSetOptionBool("ignore-stereochemistry-errors", 1); for (i = 1; i < argc; i++) { if (!done_with_options && argv[i][0] == '-') { if (strcmp(argv[i], "--") == 0) done_with_options = 1; else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "/?") == 0 || strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0) { _printHelpMessage(); return 0; } else if (strcmp(argv[i], "-a") == 0) approximate = 1; else if (strcmp(argv[i], "-l") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -l\n"); return -1; } scaffold = indigoLoadMoleculeFromFile(argv[i]); } else if (strcmp(argv[i], "-o") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -o\n"); return -1; } outfile_hl = argv[i]; } else if (strcmp(argv[i], "-r") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -r\n"); return -1; } outfile_rg = argv[i]; } else if (strcmp(argv[i], "-s") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -s\n"); return -1; } outfile_maxscaf = argv[i]; } else if (strcmp(argv[i], "-sr") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -sr\n"); return -1; } outfile_scaf_r = argv[i]; } else if (strcmp(argv[i], "-S") == 0) { if (++i == argc) { fprintf(stderr, "expecting filename after -S\n"); return -1; } outfile_allscafs = argv[i]; } else if (strcmp(argv[i], "-na") == 0) aromatic = 0; else { fprintf(stderr, "Unknown option: %s", argv[i]); _printHelpMessage(); return -1; } } else { char dirname[1024]; char errbuf[1024]; const char *filename = 0; int k; for (k = (int)strlen(argv[i]) - 1; k >= 0; k--) if (argv[i][k] == '/' || argv[i][k] == '\\') break; if (k == -1) strncpy(dirname, ".", sizeof(dirname)); else if (k == 0) { dirname[0] = argv[i][0]; dirname[1] = 0; } else if (k == strlen(argv[i]) - 1) { fprintf(stderr, "can not handle filenames ending with a slash\n"); return -1; } else if (k > sizeof(dirname) - 1) { fprintf(stderr, "filename too long\n"); return -1; } else { memcpy(dirname, argv[i], k); dirname[k] = 0; } _replaceSlashes(dirname); filename = argv[i] + k + 1; { OsDirIter dir_iter; int rc = osDirSearch(dirname, filename, &dir_iter); if (rc == OS_DIR_OK) { int count = 0; while ((rc = osDirNext(&dir_iter)) == OS_DIR_OK) { _replaceSlashes(dir_iter.path); _handleInputFile(dir_iter.path, structures); count++; } if (rc != OS_DIR_END) { fprintf(stderr, "%s\n", osDirLastError(errbuf, sizeof(errbuf))); return -1; } if (count == 0) { fprintf(stderr, "can not find %s in directory %s\n", filename, dirname); return -1; } } else { fprintf(stderr, "%s\n", osDirLastError(errbuf, sizeof(errbuf))); return -1; } } } } if (indigoCount(structures) < 1) { fprintf(stderr, "no input structures\n"); _printHelpMessage(); return -1; } printf("got %d input structures\n", indigoCount(structures)); indigoSetOptionBool("deconvolution-aromatization", aromatic); if (scaffold == 0) { printf("calculating scaffold... "); fflush(stdout); if (approximate) scaffold = indigoExtractCommonScaffold(structures, "approximate"); else scaffold = indigoExtractCommonScaffold(structures, "exact"); printf("done\n"); fflush(stdout); } if (outfile_maxscaf != 0) { printf("saving the scaffold to %s\n", outfile_maxscaf); indigoSaveMolfileToFile(scaffold, outfile_maxscaf); } if (outfile_allscafs != 0) { int output = indigoWriteFile(outfile_allscafs); int allscafs = indigoAllScaffolds(scaffold); int item, iter = indigoIterateArray(allscafs); printf("saving all obtained scaffolds (%d total) to %s\n", indigoCount(allscafs), outfile_allscafs); while ((item = indigoNext(iter))) { indigoSdfAppend(output, item); indigoFree(item); } indigoFree(iter); indigoFree(output); } if (outfile_hl == 0 && outfile_rg == 0 && outfile_scaf_r == 0) { printf("none of -o, -r, -sr specified, nothing left to do\n"); return 0; } printf("decomposing the structures... "); fflush(stdout); deco = indigoDecomposeMolecules(scaffold, structures); printf("done\n"); fflush(stdout); if (outfile_scaf_r != 0) { int sr = indigoDecomposedMoleculeScaffold(deco); indigoLayout(sr); printf("saving the scaffold with R-sites to %s\n", outfile_scaf_r); indigoSaveMolfileToFile(sr, outfile_scaf_r); } if (outfile_hl != 0) { int output = indigoWriteFile(outfile_hl); int item, iter = indigoIterateDecomposedMolecules(deco); printf("saving the highlighted structures to %s\n", outfile_hl); while ((item = indigoNext(iter))) { indigoSdfAppend(output, indigoDecomposedMoleculeHighlighted(item)); indigoFree(item); } indigoFree(iter); indigoFree(output); } if (outfile_rg != 0) { int output = indigoWriteFile(outfile_rg); int item, iter = indigoIterateDecomposedMolecules(deco); printf("saving the structures with R-groups to %s\n", outfile_rg); while ((item = indigoNext(iter))) { indigoSdfAppend(output, indigoDecomposedMoleculeWithRGroups(item)); indigoFree(item); } indigoFree(iter); indigoFree(output); } return 0; }; Indigo-indigo-1.2.3/utils/indigo-depict/000077500000000000000000000000001271037650300200605ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/indigo-depict/CMakeLists.txt000066400000000000000000000020531271037650300226200ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(IndigoDepict) include(DefineTest) include_directories(../../api ../../api/plugins/renderer ../../common) include_directories(${IndigoRenderer_SOURCE_DIR} ${Indigo_SOURCE_DIR} ${Indigo_SOURCE_DIR}/src ${Cairo_SOURCE_DIR} ${Common_SOURCE_DIR} ${Common_SOURCE_DIR}/..) add_executable(indigo-depict main.c) if(APPLE) INCLUDE(GetSystemVersion) INCLUDE(MacFrameworks) FIND_FRAMEWORK(ApplicationServices) target_link_libraries(indigo-depict ${FRAMEWORK_ApplicationServices}) SET_TARGET_PROPERTIES(indigo-depict PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Xlinker -framework -Xlinker ApplicationServices") endif() target_link_libraries(indigo-depict indigo-renderer indigo cairo png pixman) if (UNIX) set_target_properties(indigo-depict PROPERTIES LINK_FLAGS "-pthread") endif() pack_executable(indigo-depict) add_test(NAME depict-simple-test COMMAND indigo-depict - "[CH2:1]=[CH:2][CH2:1][CH2:3][C:4](C)[CH2:3]" output.png) add_test(NAME depict-query-test COMMAND indigo-depict - "CC.[O-][*-]([O-])=O" query.png -query) Indigo-indigo-1.2.3/utils/indigo-depict/main.c000066400000000000000000000705501271037650300211570ustar00rootroot00000000000000/**************************************************************************** * Copyright (C) 2009-2015 EPAM Systems * * This file is part of Indigo toolkit. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.GPL included in the * packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ #include #include #include #include #include "base_c/defs.h" #include "indigo.h" #include "indigo-renderer.h" void usage (void) { fprintf(stderr, "Usage:\n" " indigo-depict infile.{mol,rxn,cml,smi} outfile.{png,svg,pdf} [parameters]\n" " indigo-depict infile.{sdf,rdf,cml,smi} outfile_%%s.{png,svg,pdf} [parameters]\n" " indigo-depict infile.{sdf,rdf}.gz outfile_%%s.{png,svg,pdf} [parameters]\n" " indigo-depict infile.smi outfile.{mol,rxn,cml} [parameters]\n" " indigo-depict infile.smi outfile.{sdf,rdf,cml} [parameters]\n" " indigo-depict - SMILES outfile.{png,svg,pdf} [parameters]\n" " indigo-depict - SMILES outfile.{mol,rxn,cml} [parameters]\n" "\nParameters:\n" "-w \n" " Picture width in pixels\n" "-h \n" " Picture height in pixels\n" "-bond \n" " Average bond length in pixels\n" "-margins \n" " Horizontal and vertical margins, in pixels. No margins by default\n" "-thickness \n" " Set relative thickness factor. Default is 1.0\n" "-linewidth \n" " Set bond line width factor. Default is 1.0\n" "-label \n" " Set atom label display mode (default is terminal-hetero)\n" "-hydro \n" " Show implicit hydrogens (default is on)\n" "-[de]arom\n" " Force [de]aromatization\n" "-stereo \n" " Stereogroups display mode (default is 'old')\n" "-cdbwsa\n" " Center double bonds which have an adjacent stereo bond (disabled by default)\n" "-query\n" " Treat the input as a query molecule or reaction (disabled by default)\n" "-smarts\n" " Treat the input as a SMARTS query molecule or reaction (disabled by default)\n" "-id \n" " SDF/RDF field to be put in place of '%%s' in the names of saved files\n" " (default is molecule/reaction number)\n" "-catalysts [above|above-and-below]\n" " Reaction catalysts placement w.r.t. the arrow (default is above and below\n" "-comment \n" " Text comment to be put above the molecule or reaction. No default value\n" "-commentoffset \n" " Vertical space (in pixels) between the comment and the structure\n" "-commentfield \n" " Use specified SDF/RDF field as a comment\n" "-commentname\n" " Use molecule/reaction name as a comment\n" "-commentsize \n" " Text comment font size factor relative to bond thickness (default 6)\n" "-commentpos \n" " Text comment position (bottom by default)\n" "-commentalign <0..1>\n" " Text comment alignment, a float value: 0 = left, 0.5 = center, 1 = right\n" "-coloring \n" " Enable/disable coloring (enabled by default)\n" "-hlthick\n" " Enable highlighting with thick lines and bold characters\n" "-hlcolor \n" " Enable highlighting with color. Component values must be in range [0..255]\n" "-bgcolor \n" " Set the background color. Component values must be in range [0..255]\n" "-basecolor \n" " Set the default foreground color. Component values must be in range [0..255]\n" "-aamcolor \n" " Set the color of AAM indices. Component values must be in range [0..255]\n" "-dsgcolor \n" " Set the color of data SGroups. Component values must be in range [0..255]\n" "-commentcolor \n" " Set the color of the comment. Component values must be in range [0..255]\n" "-atomnumbers\n" " Show atom numbers (for debugging purposes only)\n" "-bondnumbers\n" " Show bond numbers (for debugging purposes only)\n" "-onebased\n" " Start atom and bond indices from one (default is from zero)\n" "-help\n" " Print this help message\n" "\n" "Examples:\n" " indigo-depict infile.mol outfile.png -coloring off -arom\n" " indigo-depict database.sdf molecule_%%s.png -id cdbregno -thickness 1.1\n" " indigo-depict database.smi database.sdf\n" " indigo-depict - \"CC.[O-][*-]([O-])=O\" query.png -query\n" " indigo-depict - \"OCO>>CC(C)N\" reaction.rxn\n" ); } #define USAGE() do { usage(); return -1; } while (0) #define ERROR(str) { fprintf(stderr, str); return -1; } int parseColor (char *argv[], int i, float *rr, float *gg, float *bb) { int r, g, b; if (sscanf(argv[i + 1], "%d", &r) != 1 || r < 0 || r > 255) { fprintf(stderr, "%s is not a valid color index\n", argv[i + 1]); return -1; } if (sscanf(argv[i + 2], "%d", &g) != 1 || g < 0 || g > 255) { fprintf(stderr, "%s is not a valid color index\n", argv[i + 2]); return -1; } if (sscanf(argv[i + 3], "%d", &b) != 1 || b < 0 || b > 255) { fprintf(stderr, "%s is not a valid color index\n", argv[i + 3]); return -1; } *rr = r / 255.f; *gg = g / 255.f; *bb = b / 255.f; return 0; } int _isMultiline (const char *filename, int *is_reaction) { FILE *f = fopen(filename, "rt"); int c; *is_reaction = 0; if (f == NULL) { fprintf(stderr, "Can not open %s for reading\n", filename); return -1; } while ((c = fgetc(f)) != EOF) { if (c == '>') *is_reaction = 1; if (c == '\n') break; } if (c == EOF) return 0; while ((c = fgetc(f)) != EOF) { if (!isspace(c)) return 1; } return 0; } int _isReaction (const char *smiles) { return strchr(smiles, '>') != NULL; } int _isMultipleCML (const char *filename, int *reaction) { int iter = indigoIterateCMLFile(filename); *reaction = 0; if (indigoHasNext(iter)) { int item = indigoNext(iter); if (strstr(indigoRawData(item), "mode = MODE_SINGLE_REACTION; p->string_to_load = argv[2]; } else { p->mode = MODE_SINGLE_MOLECULE; p->string_to_load = argv[2]; } if (argc <= 3) USAGE(); i = 3; } else { p->infile_ext[0] = 0; if (strlen(argv[1]) >= 4 && argv[1][strlen(argv[1]) - 4] == '.') { p->infile_ext[3] = 0; strncpy(p->infile_ext, argv[1] + strlen(argv[1]) - 3, 3); } else if (strlen(argv[1]) > 7 && argv[1][strlen(argv[1]) - 7] == '.') { p->infile_ext[6] = 0; strncpy(p->infile_ext, argv[1] + strlen(argv[1]) - 6, 6); } p->file_to_load = argv[1]; if (strcasecmp(p->infile_ext, "mol") == 0) p->mode = MODE_SINGLE_MOLECULE; else if (strcasecmp(p->infile_ext, "rxn") == 0) p->mode = MODE_SINGLE_REACTION; else if (strcasecmp(p->infile_ext, "smi") == 0) { int reaction; if (_isMultiline(argv[1], &reaction)) p->mode = MODE_MULTILINE_SMILES; else { if (reaction) p->mode = MODE_SINGLE_REACTION; else p->mode = MODE_SINGLE_MOLECULE; } } else if (strcasecmp(p->infile_ext, "cml") == 0) { int reaction; if (_isMultipleCML(argv[1], &reaction)) p->mode = MODE_MULTIPLE_CML; else { if (reaction) p->mode = MODE_SINGLE_REACTION; else p->mode = MODE_SINGLE_MOLECULE; } } else if (strcasecmp(p->infile_ext, "sdf") == 0 || strcasecmp(p->infile_ext, "sdf.gz") == 0) p->mode = MODE_SDF; else if (strcasecmp(p->infile_ext, "rdf") == 0 || strcasecmp(p->infile_ext, "rdf.gz") == 0) p->mode = MODE_RDF; else USAGE(); i = 2; } p->outfile = argv[i++]; if (strlen(p->outfile) < 5 || p->outfile[strlen(p->outfile) - 4] != '.') USAGE(); p->outfile_ext[3] = 0; strncpy(p->outfile_ext, p->outfile + strlen(p->outfile) - 3, 3); indigoSetOptionBool("treat-x-as-pseudoatom", 1); indigoSetOptionBool("render-coloring", 1); indigoSetOptionBool("render-highlight-color-enabled", 1); for (; i < argc; i++) { if (strcmp(argv[i], "-w") == 0) { if (++i == argc) { fprintf(stderr, "expecting number after -w\n"); return -1; } if (sscanf(argv[i], "%d", &p->width) != 1 || p->width <= 0) { fprintf(stderr, "%s is not a valid width\n", argv[i]); return -1; } } else if (strcmp(argv[i], "-h") == 0) { if (++i == argc) { fprintf(stderr, "expecting number after -h\n"); return -1; } if (sscanf(argv[i], "%d", &p->height) != 1 || p->height <= 0) { fprintf(stderr, "%s is not a valid height\n", argv[i]); return -1; } } else if (strcmp(argv[i], "-margins") == 0) { int horz, vert; if (i + 2 >= argc) { fprintf(stderr, "expecting two numbers after -margins\n"); return -1; } if (sscanf(argv[i + 1], "%d", &horz) != 1 || horz < 0) { fprintf(stderr, "%s is not a valid horizontal margin\n", argv[i]); return -1; } if (sscanf(argv[i + 2], "%d", &vert) != 1 || vert < 0) { fprintf(stderr, "%s is not a valid vertical margin\n", argv[i + 1]); return -1; } indigoSetOptionXY("render-margins", horz, vert); i += 2; } else if (strcmp(argv[i], "-thickness") == 0) { float rt; if (++i == argc) { fprintf(stderr, "expecting number after -thickness\n"); return -1; } if (sscanf(argv[i], "%f", &rt) != 1 || rt < 0) { fprintf(stderr, "%s is not a valid relative thickness\n", argv[i]); return -1; } indigoSetOptionFloat("render-relative-thickness", rt); } else if (strcmp(argv[i], "-linewidth") == 0) { float rt; if (++i == argc) { fprintf(stderr, "expecting number after -linewidth\n"); return -1; } if (sscanf(argv[i], "%f", &rt) != 1 || rt < 0) { fprintf(stderr, "%s is not a valid line width value\n", argv[i]); return -1; } indigoSetOptionFloat("render-bond-line-width", rt); } else if (strcmp(argv[i], "-bond") == 0) { if (++i == argc) { fprintf(stderr, "expecting number after -bond\n"); return -1; } if (sscanf(argv[i], "%d", &p->bond) != 1 || p->bond <= 0) { fprintf(stderr, "%s is not a valid bond length\n", argv[i]); return -1; } } else if (strcmp(argv[i], "-coloring") == 0) { if (++i == argc) { fprintf(stderr, "expecting 'on' or 'off' after -coloring\n"); return -1; } if (strcasecmp(argv[i], "on") == 0) indigoSetOptionBool("render-coloring", 1); else if (strcasecmp(argv[i], "off") == 0) indigoSetOptionBool("render-coloring", 0); else { fprintf(stderr, "expecting 'on' or 'off' after -coloring\n"); return -1; } } else if (strcmp(argv[i], "-hlthick") == 0) { indigoSetOptionBool("render-highlight-thickness-enabled", 1); } else if (strcmp(argv[i], "-hlcolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -hlcolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionBool("render-highlight-color-enabled", 1); indigoSetOptionColor("render-highlight-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-bgcolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -bgcolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionColor("render-background-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-basecolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -basecolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionColor("render-base-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-aamcolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -aamcolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionColor("render-aam-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-dsgcolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -aamcolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionColor("render-data-sgroup-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-hydro") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -hydro\n"); return -1; } indigoSetOption("render-implicit-hydrogens-visible", argv[i]); p->hydro_set = 1; } else if (strcmp(argv[i], "-label") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -label\n"); return -1; } indigoSetOption("render-label-mode", argv[i]); } else if (strcmp(argv[i], "-arom") == 0) { p->aromatization = AROM; } else if (strcmp(argv[i], "-dearom") == 0) { p->aromatization = DEAROM; } else if (strcmp(argv[i], "-stereo") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -stereo\n"); return -1; } indigoSetOption("render-stereo-style", argv[i]); } else if (strcmp(argv[i], "-cdbwsa") == 0) { indigoSetOptionBool("render-center-double-bond-when-stereo-adjacent", 1); } else if (strcmp(argv[i], "-query") == 0) { p->query_set = 1; } else if (strcmp(argv[i], "-smarts") == 0) { p->smarts_set = 1; } else if (strcmp(argv[i], "-id") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -id\n"); return -1; } p->id = argv[i]; } else if (strcmp(argv[i], "-catalysts") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -catalysts\n"); return -1; } indigoSetOption("render-catalysts-placement", argv[i]); } else if (strcmp(argv[i], "-comment") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -comment\n"); return -1; } p->comment = argv[i]; } else if (strcmp(argv[i], "-commentoffset") == 0) { int offset; if (++i >= argc) { fprintf(stderr, "expecting an integer after -commentoffset\n"); return -1; } if (sscanf(argv[i], "%d", &offset) != 1 || offset < 0) { fprintf(stderr, "%s is not a valid comment offset\n", argv[i]); return -1; } indigoSetOptionInt("render-comment-offset", offset); } else if (strcmp(argv[i], "-commentfield") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -commentfield\n"); return -1; } p->comment_field = argv[i]; } else if (strcmp(argv[i], "-commentname") == 0) { p->comment_name = 1; } else if (strcmp(argv[i], "-commentsize") == 0) { int commentsize; if (++i == argc) { fprintf(stderr, "expecting number after -commentsize\n"); return -1; } if (sscanf(argv[i], "%d", &commentsize) != 1 || commentsize <= 0) { fprintf(stderr, "%s is not a valid font size\n", argv[i]); return -1; } indigoSetOptionFloat("render-comment-font-size", (float)commentsize); } else if (strcmp(argv[i], "-commentcolor") == 0) { float r, g, b; if (i + 3 >= argc) { fprintf(stderr, "expecting 3 numbers after -commentcolor\n"); return -1; } if (parseColor(argv, i, &r, &g, &b) != 0) return -1; indigoSetOptionColor("render-comment-color", r, g, b); i += 3; } else if (strcmp(argv[i], "-commentalign") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -commentalign\n"); return -1; } indigoSetOption("render-comment-alignment", argv[i]); } else if (strcmp(argv[i], "-commentpos") == 0) { if (++i == argc) { fprintf(stderr, "expecting an identifier after -commentpos\n"); return -1; } indigoSetOption("render-comment-position", argv[i]); } else if (strcmp(argv[i], "-atomnumbers") == 0) indigoSetOptionBool("render-atom-ids-visible", 1); else if (strcmp(argv[i], "-bondnumbers") == 0) indigoSetOptionBool("render-bond-ids-visible", 1); else if (strcmp(argv[i], "-onebased") == 0) indigoSetOptionBool("render-atom-bond-ids-from-one", 1); else if (strcmp(argv[i], "-help") == 0) { usage(); return 0; } else { fprintf(stderr, "unknown option: %s\n", argv[i]); return -1; } } if (p->bond > 0) indigoSetOptionFloat("render-bond-length", (float)p->bond); if (p->width > 0) indigoSetOptionInt("render-image-width", p->width); if (p->height > 0) indigoSetOptionInt("render-image-height", p->height); if (p->hydro_set && p->query_set) { fprintf(stderr, "-hydro conflicts with -query (implicit hydrogens do not exist in queries)\n"); } return 0; } void _setComment (int obj, Params *p) { if (p->comment_name) { const char *name = indigoName(obj); if (name != NULL && *name != 0) { indigoSetOption("render-comment", name); return; } } if (p->comment_field != NULL) { if (indigoHasProperty(obj, p->comment_field)) { const char *prop = indigoGetProperty(obj, p->comment_field); if (prop != NULL && *prop != 0) { indigoSetOption("render-comment", prop); return; } } } if (p->comment != NULL && *p->comment != 0) { indigoSetOption("render-comment", p->comment); return; } } int main (int argc, char *argv[]) { Params p; int obj = -1, reader = -1, writer = -1; int i = 0; char number[100]; char outfilename[4096]; const char *id; p.width = p.height = p.bond = p.mode = -1; p.id = p.string_to_load = p.file_to_load = NULL; p.hydro_set = p.query_set = p.smarts_set = 0; p.aromatization = NONE; p.comment_field = NULL; p.comment = NULL; p.comment_name = 0; if (argc <= 2) USAGE(); indigoSetErrorHandler(onError, 0); indigoSetOption("ignore-stereochemistry-errors", "on"); if (parseParams(&p, argc, argv) < 0) return -1; p.out_ext = OEXT_OTHER; if (strcmp(p.outfile_ext, "mol") == 0) p.out_ext = OEXT_MOL; else if (strcmp(p.outfile_ext, "sdf") == 0) p.out_ext = OEXT_SDF; else if (strcmp(p.outfile_ext, "rxn") == 0) p.out_ext = OEXT_RXN; else if (strcmp(p.outfile_ext, "rdf") == 0) p.out_ext = OEXT_RDF; else if (strcmp(p.outfile_ext, "cml") == 0) p.out_ext = OEXT_CML; // guess whether to layout or render by extension p.action = ACTION_LAYOUT; if (p.out_ext == OEXT_OTHER) { indigoSetOption("render-output-format", p.outfile_ext); p.action = ACTION_RENDER; } // read in the input reader = (p.file_to_load != NULL) ? indigoReadFile(p.file_to_load) : indigoReadString(p.string_to_load); if (p.mode == MODE_SINGLE_MOLECULE) { if (p.id != NULL) ERROR("on single input, setting '-id' is not allowed\n"); if (p.out_ext == OEXT_RXN) ERROR("reaction output specified for molecule input\n"); if (p.smarts_set) obj = indigoLoadSmarts(reader); else if (p.query_set) obj = indigoLoadQueryMolecule(reader); else obj = indigoLoadMolecule(reader); _prepare(obj, p.aromatization); if (p.action == ACTION_LAYOUT) { indigoLayout(obj); if (p.out_ext == OEXT_MOL) indigoSaveMolfileToFile(obj, p.outfile); else indigoSaveCmlToFile(obj, p.outfile); } else { _setComment(obj, &p); renderToFile(obj, p.outfile); } } else if (p.mode == MODE_SINGLE_REACTION) { if (p.id != NULL) ERROR("on single input, setting '-id' is not allowed\n"); if (p.out_ext == OEXT_MOL) ERROR("molecule output specified for reaction input\n"); if (p.smarts_set) obj = indigoLoadReactionSmarts(reader); else if (p.query_set) obj = indigoLoadQueryReaction(reader); else obj = indigoLoadReaction(reader); _prepare(obj, p.aromatization); if (p.action == ACTION_LAYOUT) { indigoLayout(obj); if (p.out_ext == OEXT_CML) indigoSaveCmlToFile(obj, p.outfile); else indigoSaveRxnfileToFile(obj, p.outfile); } else { _setComment(obj, &p); renderToFile(obj, p.outfile); } } else { int item; int have_percent_s = (strstr(p.outfile, "%s") != NULL); if (p.mode == MODE_MULTILINE_SMILES) obj = indigoIterateSmiles(reader); else if (p.mode == MODE_SDF) obj = indigoIterateSDF(reader); else if (p.mode == MODE_MULTIPLE_CML) obj = indigoIterateCML(reader); else if (p.mode == MODE_RDF) obj = indigoIterateRDF(reader); else { fprintf(stderr, "internal error: wrong branch\n"); return -1; } if ((p.out_ext == OEXT_MOL || p.out_ext == OEXT_RXN || p.out_ext == OEXT_OTHER) && !have_percent_s) ERROR("on multiple output, output file name must have '%%s'\n"); if (p.out_ext == OEXT_SDF || p.out_ext == OEXT_RDF || (p.out_ext == OEXT_CML && !have_percent_s)) { writer = indigoWriteFile(p.outfile); if (p.out_ext == OEXT_RDF) indigoRdfHeader(writer); if (p.out_ext == OEXT_CML) indigoCmlHeader(writer); } i = -1; while ((item = indigoNext(obj))) { int rc; ++i; if (writer > 0) printf("saving item #%d... ", i); else { if (p.id) { if (!indigoHasProperty(item, p.id)) { fprintf(stderr, "item #%d does not have %s, skipping\n", i, p.id); continue; } id = indigoGetProperty(item, p.id); snprintf(outfilename, sizeof(outfilename), p.outfile, id); } else { snprintf(number, sizeof(number), "%d", i); snprintf(outfilename, sizeof(outfilename), p.outfile, number); } printf("saving %s... ", outfilename); } indigoSetErrorHandler(0, 0); if (_prepare(item, p.aromatization) < 0) { printf("%s\n", indigoGetLastError()); indigoSetErrorHandler(onError, 0); continue; } if (p.action == ACTION_LAYOUT) { if (indigoLayout(item) < 0) { printf("%s\n", indigoGetLastError()); indigoSetErrorHandler(onError, 0); continue; } } if (writer > 0) { if (p.out_ext == OEXT_SDF) rc = indigoSdfAppend(writer, item); else if (p.out_ext == OEXT_RDF) rc = indigoRdfAppend(writer, item); else rc = indigoCmlAppend(writer, item); } else { if (p.action == ACTION_LAYOUT) { if (p.out_ext == OEXT_MOL) rc = indigoSaveMolfileToFile(item, outfilename); else if (p.out_ext == OEXT_RXN) rc = indigoSaveRxnfileToFile(item, outfilename); else ERROR("extension unexpected"); } else { _setComment(item, &p); rc = indigoRenderToFile(item, outfilename); } } if (rc < 0) { printf("%s\n", indigoGetLastError()); indigoSetErrorHandler(onError, 0); continue; } indigoFree(item); indigoSetErrorHandler(onError, 0); printf("\n"); } if (writer > 0) { if (p.out_ext == OEXT_CML) indigoCmlFooter(writer); indigoFree(writer); } } indigoFree(reader); indigoFree(obj); return 0; } Indigo-indigo-1.2.3/utils/legio/000077500000000000000000000000001271037650300164405ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/LICENSE.GPL000066400000000000000000001004411271037650300200660ustar00rootroot00000000000000 Legio is Copyright (C) of EPAM Systems 2009-2011. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License below for more details. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS Indigo-indigo-1.2.3/utils/legio/META-INF/000077500000000000000000000000001271037650300176005ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/META-INF/legio-splash.png000066400000000000000000000270761271037650300227110ustar00rootroot00000000000000‰PNG  IHDR,¹ÕfgAMA± üa pHYsÂÂ(J€tEXtSoftwarePaint.NET v3.5.100ôr¡-ºIDATx^í tžU™Ç{DtÆqgÎqAeFÄ3¨0  P6ÍYD†MA–aSì¤,*´´¥¥[Ú¦-´¥{BÛ4-M÷}M›½iš4I[ç×>íõæ¾Û}·/_’›“SÂ÷ÝûÜç>ïý¿Ï½Ïvû=ä~œœºUýýOîÇIà°–.]Z0a¬[·®³³³Ãµ­__7fLCiiÃĉ.ÖÐ0–ÄzãB‚pãÆmmmyË´³®øÉ(õõõãÇç=b,ú„±ÄÕ'„[¶liiiÉ[¬;çÎíhhP£Ô™÷ˆ±è;ÆWŸh\UUU°ynß¾½©©)ïášgÌèÔFaGºïÞ¼µ§ï@h/«¾ÒrÅŠ›jcccƒ¦£²wýúõBaó»ï*â5ƒe;PJj„)Ø »¯^½zÿþý˜XGGÇ’%K/^¼cÇŽl‡c‹[QQ¡ot›¦Liš>}÷²eµÃ†ímlÌv¸”ÔS °vGìÍ·¶yófµï­­­]¸paVP\µjïïƒÙ»{wûöíEøÀ‹ð¡t3K@ÕÞÞžP®¬¬ÄHiЯ©©ásþMgξì\=mm\ù UÏ w66îëè૽­­Íåå»,Øðóø±aCUÕÔ .ðbÃûɰø‡µ1MOàjé€t´¡_rÜq5š™ËF";fÏV”G}´M×F—€‚"v”Ä’AG-[¶,nw nõ"ï´«N· ¶ã×’׬‘^»ÊÊ:jjðò×µ·¥Gí!ûZ[ãÒ´i B@õÚû߯#dð_üEé)§ šf_{íôK/yÔQ~&ó›;W­²~OSÓ”sÏÕ»¿Ú¯ß˜/~qöu×-¸ë®ùwÜ1몫F|ò“Fƒ²ÿ¸}çNú´I½{õŒ–]3]˜jØ ²å‹«[[[QSiœ€Ê÷ ‰oƒÏõ 3À~´OöìZW­R lYº´cÇŽú’E »NÓÛo'£Þ+„Kž~úµÃ›Oà1ýòËYı ¢Mœ õ/ÿ¢–û¿ýÛ†%KÂÇn^·nÌ1Ǩ.o~æ3ËŸ{®ÍÏkÔðÞ{‹~ùË×?üaÕ˜·@‡Åk ¬þЇtòîÈCŽ}„&«\Ί–­5kÖdåu­ˆfQcYÝ´i“.ö­[·bOJã\Ù³uký¨Q'ãV3b ¬}óM}ˆÚ7ÞÈãA‡pÞwªå;î+_ átîÞ]qß}Jm¢ÁZºŠIïÞ¶cLj\èƒóÅ=Öeصyó´‹.R,½sñÅ‘–ô/½$íy)ȯÿå_¢~óeß¡)Z‘íeñÑ¡wíÚ•­Xª««Ñu ,ÐÉÂFJ³ªPu»**äï=›6µ®YÓ0v¬ë`Ä ÙNG¨‚pÃèÑj¹¿uöÙíÍÍ–Ã×/\8ì#‘¾(º6%ZèLýö·Ñ?òÈuC‡Z§ÙòßþV1ƾ4¼ãøNÆï=ù¤ÒºÎY¾FŒHF—^jÃYùðÃ:üïBüÍþgŽ‘ÉèïÞ¾}È_ý•Ðá¬è% sèG?* 6ÎÖ+Ÿ”ßrK²q]¯ $ð $–' ,€¾…½Y³fÍ›7oþüùü›ì}@ˆOBV*>îÄ2¢cã²e˜^†üõ_×v=F—žx¢ÐëQ4˜Yñ⋘[0Ïø2É9SFÁ>¤\ü[&M’‰.°1®¦™¾ë›«pzßò‘@pì@ü 1öæ1„g]q…¬Tü)‡äìnèºkÖ:§½ï}A6ûACÂÙðUÊ@•< ÂÏÈOZ>_3x°ý@®e±I'¡e;G°` T-ITZ|þåÝwß„ü›Óˆ&ñìÉ%\Ó’a¬ßúÖ·Þ:óL›ßQŸÿ¼Ð¯¸ÿ~ú-[¶°áœ|Ʊ~Ç~á 2 úQˆŸrÎ9BaÜ—¿|H÷ë×ä—ðbÕkÓíÀd‚s¢l`ƒ-//f•le°m‚°qéRY£¬c›Õ-íãþ’$aC_íãÒ?tì2„Q|ƒÎ¼î»Ï†צ%@ôœ2š“sçÎåXÈv4§S¨ Bå[û¥/ÙÌŠ´&uÊŠ…ô§ }Ý5‹þëËG?Jb£Ìºúj߾ؒ¥eØpîÚä*,±KÄó Ñ„™‰ˆLr‚’õʵbkM ÙzÛ¦Nµùó£ ý÷ÞkI¿®¢Â†²jƒ/^†ÀO(Cìíè —B'¢Î½›´¸$K~\³"‘@VQ©‘ÓÁ(Š„qƒ×#)ûƒ{ãпùYÄœÇ,©Ø7Ã&)Ä9@Ú÷²oIdŒ ^ v%?CØ EËž¸kYT(XiFVA 8L":VY i<õ2$fžÍ&èaÖÊðƒÿ0n4œ1 º—³£¬Lÿ|ÉSO óD†LÃï¡ êGìNúYT‹±¯2S0R/Û 8ÌIÒ> TFü©ßùNšQq þÀXë‹}T§Cf­ Vܶ—Iò ,NwB(âäO„3¯b–<ñDšiº¾Ý%‚ Ι3M˜ÓL}@ˆá^9ÙÒÔe™}ýõ¶™W^©sOf |^r챑¹HAÓÞ½u« [«™;Wš‘ë(”ù*2íCEÏa;ÍI¸Žl®($ ‹å'§éøp㚓ՌG«F‚±±‚¨l`#™ØTö¢BŸTàÄé‚– øý26äCó#É‚Rc K‘í]ƒb“@!Aˆ%–Œáœ$àB¼…*†›òqÇæà7âŸþIð@Ì´·;!Ýò-›É £FÅ¥¯’’èŽÙSº“ªK®U†SVºšœŒ¸<¸öÝ.EkóŒm†4‘ÄÝÃ;&õ.~üñC¦‹˜Nmlª*,Oô^žžtꩇèðƒ±%0cå·Ý¦ˆ“ª+Ÿ[†Б}¬t¡jNâ:q9=G6R5¹ÕÏ÷MƆQO1’=ûaå-È[WËýíþý›-RÅ6—” ûØÇº¶Æ¡–ŒJuGë’ÁØU ]§Ðƒ¦gB(÷`Õ¯~e?ŠJ ·DØ÷r-»]û;;[++[*+ vµK~Qra Äj2ý’KQN›n Ù6mÚ”óÏWqÖm7.üQصj•ÔÈå'ã¾eãFo/Ì0‹yDyØ… ¥:h©©qhè#$xÀ~‰¸ã=[Úww-»EÜ1X;|8Éïþ aþ7r5†™<2 ¾‹¨¶FØ¥q„BPU­ìÆAN9*ML=ï<4 6§yúëÄù 9÷†æß}7¿XV½%ÝÈRIôï|÷»6ãª6]Òí]‰îX²ë¾ÆõZœ5)ó©D(ó“šÅruP$¹>C(bïAÕE×elÊ«fÆø_6–r➯ˆ P•×Âéúìg7Žã}ú*:”´Ý¸kcú÷¾'ƒnŸ93n_×¾[$PÓµ2˜ú_R+²å5å5ÿE,¥é¡Ha)+21Z¯2„õª*©h!õ‰šA›ÕHÑpÞãþ F™ÐC†“}ˆ8î½ò—ே±NƒV†ÕÕ¨V Q‘LºE"ú®Wí6~/S fReƒ¢úÄm“µ4e¢~ùL,áà_Oþe[ªH; =D¤…?ÿ9eš*ñ‹µ¯¿nc³±d‘Ú0˜s0u¸æ™gp©sÞs¹–Òë#ÍölÛ&•ùi()‘ ϸyrÀH©1„ZF‡SØ*a¢ )ÒA}€Ø ì#ÙM³È%@¡^ŠÒsõg‡V“›bûü ^ÐcÉŠá`òw¹b-Öô"5 ,µ¢\ÈI¦d«í™0C®±“@á%ÀáCüXâ¬ï¸ˆSÖ,(†+a¹=jãÆ:b&,ü‚q#f/t fLÎWlùL >–6ñÉR´?½­…‰ ½÷:²ÿÄÊ öô+ÜD„Ù/G±{%ÀˆÀ5ˆ{½ÄöH„@1èzí4³(*¼Á†œ ‚„iDíúµ8¢Á$8Dy‹SÈí¢–g¹S%%ôÃSòÈÖuéaà|ˆír}šbÝ{½v³¢Jb¤™Ç0É;šE*êSà[k¼g³<˜¶©î@˜‡äÍ¢–:°`üÙT ÷!–œŠ/½ðÂK/¾8uÊoûì!¯¿>ð·Ï—Œ»§mwJ¥& :T~§¾nвêCõÇšÕ‡êûOŸ6¬-EŠAG¼1\ßÇ£Öß™:Í;ÖÌ3Êæv©4£Ú,^´H;zŒ·r+ÇwãêÉ‚='7P%€/Þò¦áôŒÙT ÷áo~ýìßÿþ¸±%Ææ©§ ‚³ÏüæË/½4yÒd¾=ó§scÁ. þøûßðþÿ¾÷ª\!ß*2è ÏTtV®XqÔ§?3©t¢ú`?ñøÿaw:夯}ã”S½FaZ>ýä“wÿìgŒeÚW½;¯¯þûq ‡„é×\ À•…Ù‹" ›Jáþ <þ¸/£ë|ŠÔ©«Ãi³´jé©_;Ùhw6ÆÝïGùKƒ>”/»äÏ9/¿ôòOo¿ýž»ïVÍî»ç¿çΣWŽ9’Ïïºógè:/ÀðÍ7Ýî†B¾úÊ.ô€döˆuã˜ÌPi2q Ú°„M(Ä9!üAÈÒ\Xqè:bc¤‡þ‹?¼òŠñ!>l˜þa¢oO;åÉáçúk®EXh9ñðð-4½[ßË/¹tQeå{‹_z‘Ïi:Ù`mm–QïkCHMŽÉ|²˜‚"+…ûƒãÙIÇÿêÿèõoœuæ™ë»ÞsßìZo¿õV„ æÏ_¾l¿5;Ì,[_MH÷_<øà˜Ñ£E‰_ùýð»VÆxTÿóºë ­^µúüþçÊnù»]ä-Ň ÌÁ6@ ãêá׎ÿa’_…ÿÌŸ„#XT`§oåòæ-ÒhÅ£rçOî8ç›g+û”OòÞ hÿëº ¼ëŽ;9ûñûö[oó !g¶Ÿ,AŽˆüÁ‹`ÀÁ[+^ùÝÿñ·AçÑGyí¯Ê‡ W߻ʇ€ýÉùö„¯|õ¡]t5ßÂ/}áa’ßwËËó~Ž~1H5˜,¼;ó‘Uá"\XQ‰;›šÔð~炪%æ½…ìEYÄãž  bÏv¤l£91JÁcv¤ŒÈ×þðjco-fðüδwøëÖ®å¼ê"¸íÇ·èz[!Ï;§ÿ -ZMxÌç¾ùÆå÷‘‡L¯Iw­7n®Àb7Õ0 ?I@Èxèk+fÉw ‰‰hÈ .ÑäâÊCNŒ„¶è¾TÀC,E]Pðµ2cÕO°Œ¢OÁø_¡¬~¼VÜ4¢w}‹Y…¬ÞY)ÃÅŽóRq¼å%’ýò"í¡‹Ÿ0¼ö”aÁž…¨ˆ$¹EÌWÒüæ²I?n¿÷½8ÆK1{Ö,ùõu¯M*ßêû PÓ»×% ÒØr†éd?LL)1kŒåÍÚDÖŠOß ov¹esçb Âp™÷•áƒq¤ºW…¡$ø‡Ì×_~m^5‚ž±øã=÷¢ë%›¿~ú~_øç¨k5î><|K˜ ¾ro¼,VŸ× 68»ä‹ @Åàé;hÞ{÷=˜gpQI‡k‘ÂFKü+XV…Ïçþ÷9ã[Žˆx>˜ÝŸzâIÂP»we¸Ñ &BnGÑg!õK™²? æÒ³Š¼¢„^—½Þ ª´†›o¸UcÁ¨sÁyçéƒ&¡júOA1«á9)€Ñ GK î‚=x7PñH †¼zá‘þ $JFeù .Ÿxì1Ýå B=&Í9òÍ7-AÈÛ…TßTFB$o¹éæâYŽ“‚I ¿Ë½Sä×ÇÞŽT™o:¯Ð„œú8Dñã['GiBüg|ý4Φ^&ØŽþüäsü¸½§Ä MHX¶(ç;I@È™0ˆOˆûÜsَօʨ`+à T0 P ͦb&ü€Àð@ë(à!Kˆ“Í›6yY„¸é¯¹ê‡üz£Øh =ãÀvÂWÇ´ã;z¨\ÁðÜÙsøö6 !ÑsÞ3ž·; „ áóñGõ6à`ùüs¿%*@ðLî0Èä±9"yK/·oxæãbkäRÕ#"qÞ!Â\ LŒAn¡×¬b¿}ðþûI±šG2IºåtíAø/ÿîy,RdáÛQÕ];ùÄÞ,Hú®A• Ì;s ýAE}e±GñRüwæÌ™XC8Léoüh?ᮽV‰Q·!ŠøŒÓ¾d>ÁjÂê‡]t¦ïö HòvùúɧDî(,Aȼ8Jº†ûéÝ”!ÅîÈÂQîÁK,Š ‚ÂÿçÌ™S^^>wî\Ø a'YYåß9÷<#NÎìAHã[oþñÄÒRßùð>À†‚oƒë(#Ÿ0|iBbS‰O,t×±ø%€êcÝÏž=[ÅZ²…¢ÔS )(̾ 9oÞ¼ùó烬²²2ÂIèœôp’+D|¶¯Ÿðgwü” ¡üz£.u•2ôâ1ÆÓb³J5ÙqAÈêÇ‹{ê¾è{Ep´#=pG@=&C/)oô«“K'4Cv•׸Ê{NçÓ©a'À~˜¨ Y3g¬ü–Írm²’û=tެTYÑŠ‰¡(õc­"438DFŸ ³‡£ã$P0 ðzÅ ð@ Ú†?ôœØ6Ðh3ªÂY;áœÝÈ’jAÇ1s ,ØÂpB솀FàÇG–Z28Šlm H> 0¥·Ã°+ÃQ ÈA‹ žœµäˆJL6:G!ÑŠA'),:¾Q(q‡s Œ+1×¾x%p '¦¬L!7@ú‚Nrµ5?:©˜ˆÓ/½!GDé@X¼KÊqfH óðUòû÷îm;Ù\VÖ0q"7eKc àœÁ!î8omÎÄ"¨D#;O20ÈNJLÊÛÑ0Ca:RùJ fÈ7CCÉ:εC‡v¼¯¡uõê† ä[®ÈÆž=”ŒQ4-8Œe³±ÈÐFJ®MQH NK²©/)i¯®Þ½d‰â¬±´t_[ÿ‹ÃŒra9qœ¶szXŽlö¨9’gÃøñõ¥¥ ¥¥­lµìœ] ´w ÆÈžƒ?ý O`æþ„y<)G3 tÑ„£G³å@¨Fª7n_{{.kD¹c32b9.„q%æÚw›êÇüùö»Úƒ·€ÕÛJ ¤¶¶– šßE›+T Î<ýÂ0×Gæˆg)³gcŠ-‡«Âc’ÙU^Þž©¹2„iˆ2ÌrVÎE‘­4µ^/ª4„—NK § Íué» L4²¢v\é8Æ•˜kßw%€]”$ubµCÒHÇ0Ð\—>*‚Ȇã'eĶ!>Â>ºžÜ´H€`Q"¶‰ŒóÖzO@Muq L#=×·oIìAT?&±,}D·òå—_í×Oý&nOé¨Ï÷ÝŸü¤§°]x>Ñ„XGåÊû ]ö„„r ´Ä3ö’˜E%úVµ¤ã΄тrš0ZF}² , ¶£üd#î4¡Ó„NÚ¾Qd#Jñ(j§Ov†™0¹÷5Mh»û|;beÄEA%› o³tšÐiÂ>-kpñõš 6VWo©©Ùœ]Ú”¡¡õt É'œ?çĉãÆunßž•<³ZK½ŸNÓÔ©m‡Ó5êHhlhÈd΄ÝÂö;×üÖYg ûØÇÄ9è Kó›Ý[¶Ä}®t¡#Ý!"ÔF}4Ÿ´f÷ª6XbDŽÍŒøú‡?,#òGbþãηÛ×®Fß‹§bæÌL˜q ,(÷¶·WÜ{¯B‹ þž}Í5muu6O·³µj¾DØ@Eè¨6¥'Ÿì¥lï'Ø3~ðƒ ås䇱ä׆úŠø¾ÎÎÆI“2˰p diŽ<ê¨ð,ߎøÔ§šnVUìZR“˜L@¸}ÆŒðׇ…f4Îd]¶­]Ûz¹…=ç„!Ê hÙ PmPCp‹Ú’§ŸNÂmS§Ú3/-éb¿{D˽--”Õh[³†2§µ‡µ"·ð¦ÉµÇëè@X² túéú"æÅ&°uÇžSbMYÙ„OÔÛZ:ú®Î²›oöRÛs¸ìBã²eAÛÔdÛQ^†ä4˱VñÏÐFŽ4ô<]"õyÀžÁ$u‡;?8ùŠZÀÜ|·"…)ã † ÂU¯¼¢cfÎõ×sœó],e½å²¼ÍjÊËõ6%Çç{««¨ðî“pÚ…ê#–Ýt“ïÛùJoÉ«§'Â,ϱ Hc.‡–s!KSYAY ‘i hýpÕî©c †U´eˆEdì!€pÇœ9:‘Hþi ·§{²5ÝC{ήIÅ@®&è@˜;7O˜ %h´±|¾Ý¿¿ê²nèPÅ–Í›õ%yîÒ„tLBTðï})䥣Ÿ~yeôP8¥a›*Tã÷nPÉ æ.'2Ap„¹ƒpæUW)Ø ›G»©¤Du¡»ÞLª¯Æ{l$5´ @¨«qßí±—‡µƒéÊ<èdÉ|Oo•VDr“!!àú¤äzmÂÜA¨/bŽs6 «µºZ_ÄzÝ$c iýD„ƆvçêÕ6üÈ·œµ åžØ(²ÿ:óúõÚ„ù‚°yÝ:}÷º,—‘nSˆêE`Š"¸uòdj+Lì¢Ð=°d3œ´!j'hGmO¤7µÄ¡®ì6®×v Ì„õ‹Åu¯yÛëšDÿâ6ˬ&¡Lã«EƒÐÕ¯ ܱá¶·¡> 9PÞëµóa·„ÊúÂáJÿÖrs¨ów;ªƒ°=BtsŽ¡ÈªÁd{eè@Ø“@꺄‘Î ]”„Þ…EQ ß·˜aO¡n°NÚkæbh¹víZßm„ù‚ЈnI¿zÊ™PÓsÛQyî˜I}Kw;æ Bcio ‚«nu,fë¨î˜±ä3ýªÈ)ª†aÆ ­S¶…žb¸Dò—Àõ¯w‰k˜1ü„–ÙÆ«ÇÒŠ9÷žÞ€Jm¾…&ÌWB]7ÖWÜ¿ÍJÂÙ&™uõÕGR™ Ò1nÄ QfºË1.1eÄ ï >1cáü»îÒu8!æn/ª–¦Q ¤„V€ÊÖ0ÃìÐ <  ?öA¡²Fù—tøò[o5Âe‚üãFΞäé«ÌzÈBÜ·L2&ˬg.Î$£/¸ŽŽŽuZ°;†¡Ña‚àO¯Ý»Ž#É’2”|«ÆŒ>P2"¬ñwF&¤Õû¯·7ò šqgÂè3a$Z¼ |cY«ªìk=… P8fCkC6zöSb2¢«¶–þAþ„ÛŽZ‰1M(‡W 0K½P›£Ô«€ò-:32"VÝÑ)çŸþV¢njo­;jµzBiõÒÖiÂÂiB5’TàžvÑEÃ?ñ µ )U†cpËĉA5 ‚®Tà&’F!Eµµ¦•+¥K$Æ"C«šßzn ŠC'Añô+»Qp ìA+KVãb,˱­®p ì£+Bw¸Xêî]„Ý+ÿlFÇäÃnsñ£R,Xù$BHs¶Ôc¾«§OφG%‘‰­È:ÅÍëÕcM9F,,²éö6vœ‹¢—ÊÅuÔ!2*Њò¡a\ß(M@‹ ë®ÇLîzÏ·´ ,544EÂîz:iÇ%*…Ø”HN<í´æµkÓæúÇ—;O®£¨¶(÷ì@_ºÅÔƒã®?¢Uôà,7rƒ<ÙLÅÄlâ…¼A¸ÿ~›9;ÚHɵq°•;OöŸ¾ôA$m…ëÚ9 DJ@¿p"²±³ŽÚ‹Èµtˆ–€Ü}"º©§…Ó„ „æº8 t‘€~÷uÑ8&šëâ$pHÞ»¯ˆÆ0Ð’w‰›+˜|$×ÓZ‰ W,ùd[|¨a. lX²„üTe4—:¶|—¹LÚSÐgZäÝîRC%Ö|qÙÛRZЇ=n¯ö‰y–:QéÓš²¢“‰L,´>a·ru Àý :cQE=¿ rP©¼"°ïbùl/h{ND,öí#[&æ9+ðdE'r¦6 ,´NJRR¿y} ÆØ°j´É„ªïû #Gªl7 ¹/0@ÂcÝq—`>Þ. žM&ãz‰dÂÄ|fž¬è$žHÊŽ)AXUU•’éž%åâ”»ï&[ÕÌ‹Üf21EÄÐ+ϬÀ“lŸ¸=µ” ,ºí¨”…FÝ•ÊD4’ƒ3ý²ËŒ}9÷%-.Š‚?ø_#GN‰0HB´¤%HX–”0“€f¢½túª/Å­ ¤&)²dœQ½[;…ç„VWì1(tŒÇ ·´á+•Ü@2»> £V¯UDjçÍS}9?SبV&¼qz¡"½$Â~}·£ô%`M1ŒX Î:Ï–àa^ÌBÎö"4#NÑás¾•é3´ñdh£$±¢æåŠ6Jæ0LÑG—YYT.+£ð­1)ïÛ¶qéRÅrào=ŒVÚë¿Â§|ÎÜ%4WÈT7ž; ”eÿvÿþöÈ÷¶ÌLJ¥÷ð[‡¸•Á¸cኬ_£ê¦ÈeÅÀF E5yüjÁ)"zÒ€ôõ€õìž ê—]*úzizžVÐ1Xí½C@hTÈ—!˜¦~Ó‹ðFùP5} È³ôòL‰á ì }bBßyI‰BGgL‰¼é«-ˆ1¦¯7“îÞ’“<_ úƒ0@è[­þUpêŒåaŒä[΄òøxU“íúd˺Ù1w.O_þš¶úm>J^ä†KQ½†Å‹eE²!5äùR‚¨Y>DÛQywJ@š©ÚGJ#Žï=þ8]èHw/owiÃçÂ?†}ý±É ÓŸÖ¦’ù"2ã%Õzb-¢ ’Á˳¬¤¤äp%¯9ýEnBA‚<ÑBò>ÒÓóÕJÕeK™”*¥¡n ªÚ¼TO›&ï ý©0¼èá‡E (UùÍËNº3}™ëÇxÐê]#¢XòôÓr8‚š0¦›a|·£j±±»‘§ ÿr¹•ñÊ@ÊÆD=©dPÌLФbån³@¥—qõ¤º˜R­`‘‹q»òõªJ°¨ }½·I]j¥ºƒ@hèviB”¸¥Œ’qE8‘Ç£ë/e¡ áõ‡G_ád 4®v0xæ[Y¬Æf^]«¤F±¡ÌK¿dWÝʦd+tQ?ƒ0º¼•š’ŒÀ5* ëo‘-ºÈ›šQô92wïÛVÎ^]H颠»|¨îK¡Áƒô5d»`øp>L¹…Ûî!»A¯Äå9ÉÓU«SmG½j„œãÑÈ”¾^Ÿ˜ìæe_ç«U„ŽQžL<ò'ãê‹Ø¡Z(ÞâK²ÂÔÔ¼ÈâÙ—+ųZ‘‘ ÞÀRx!p¡ƒÚ7Æ5¶Er—›÷z)z1G¾Roa‘q½¶¼m {„1¨¼ØVÔWVÍ"„ÞB¢Hõ·ê´Ûoç“ô Ó™PæKÊžÍ×e,{!u¦ò.hïûÕ„ÞRê½(1Ìè+,„wyÞ[&MZpÏ=ꘪÏÎ!,ñ‰¯“Ê%Èôùýê¶mЧö aª0”z:‘ áM—F_<Èô}ÕÊ–o‘,c¿ Bã`‰q…í¥lqÕO½6|ÙPè»ñ!ùŸ(½ù^jåÀ`¼ÃÂÙ Y ¾ 4ä"KÐëxô¡ïÛAo™„ud aˆ«=1ÃÓííAÂ[†ÀO¾2@h<,Y ÆË:„ðÉy’*XÆèºÅ(ÕCÞÚ²“zëüócOuÌ „²g·ŽÊi»8ˆ« Ó€0D†oGá}öÊ8ñµƒD¢½Ûà¼5¡~_‡%8ÁWAÙ5ŒXÆ‹)RæB›]’Á§<šÄ ”îØr¤ø€B£ÚjÆ!¤Ä'¶P±‹.ûýï‹„be ÷Š1PnC΄²ÿ6΄i@èݸËîBì“iByŠÌÚ0^ó`d¢ C΄\1M_ãLèݸ<ËbõK¨›}í5¡š—Í™Ð{ 0¶£²KâéG®×<@¨ÅL*o,„Ò^,1ò|·¯_9©È™iBF ˜a™Ê¹Q=³Hë¨:Êûú?bmG¡`¬'±(£s2i ¶å²žB@ˆÄ­£!¼ùjï ý¦î'´¡ZdÆù‚õ§ÃÉòL( Ä{u¶ªÝ¨LüYPδ^÷º<—Ä D,"yîS/¿b&„¾wh3MÖḋ< NäUeüêB³!Ôˆõó-Xl„FeBF”êýÕÕ€riJ3±s†ƒ«ŒšÈÎêêM›6Eb,²AÆ ”‡G0qzÊ2ÎÞXMÅ™(ZÆŽê󉵥1ë@…#a„ê™ÅÕ„2_Þ¬*yÓçs9sê;",ïê µiÜ85ËØÑHÊKˆG1Õò/a¨-)Q»ÊûoB™š;ÊÔŒ¦ö ÞTì(Ìø gBF„[}"|oX2I…³¢œ#A¨„‰Ù¢££c}± #ï8 ô ˆÕãy½«µ@üÄSË^&fÅut(~ ÈvTYøW¬X‘žgÂô2tz¿Ä–Á¿¢ÕÆ>“”BÂÞ¿€Ü ÓK@|ò‹‰U…û.\¸0=qÂô2tz¿ˆ¬C#yázfsmmmâš¿Jj„½¹æ-\öi è@˜÷rôûŠCѰ¯,7ÏÂH@ È6Õ~8B{Y¹–N¶ؾ};¤Õ¬Oùã@)"×ÀI ¡D+FBÑ0¡|]7'K pc6žŒ (¶µµ9ZJÒ5sH%ò-Њõõõ:•uëÖ-_¾Ü0•d]g'XŠœQ€ÍÍÍtw Œ%CרI  p«öʃ%påÇ0™:Ni$à@˜Fz®¯“@p Ì@ˆŽ„“@ 8¦‘žëë$3¢#á$F„i¤çú: d  „èH8 ¤‘€aé¹¾NHÀ0!:Ni$à@˜Fz®¯“@p Ì@ˆŽ„“@ 8¦‘žëë$3¢#á$F„i¤çú: d C ä?îÇIÀI »$ðÿƒ™žâIáIEND®B`‚Indigo-indigo-1.2.3/utils/legio/build.xml000066400000000000000000000133751271037650300202720ustar00rootroot00000000000000 Builds, tests, and runs the project legio. Indigo-indigo-1.2.3/utils/legio/examples/000077500000000000000000000000001271037650300202565ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/1_amides_stereo/000077500000000000000000000000001271037650300233215ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/1_amides_stereo/legio_am_acids.sdf000066400000000000000000000044141271037650300267410ustar00rootroot00000000000000D -INDIGO-09101021542D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.790100 0.400300 0.000000 0 CHG=0 M V30 2 C -1.075500 0.812800 0.000000 0 CHG=0 M V30 3 C -0.361000 0.400300 0.000000 0 CHG=0 M V30 4 C 0.353500 0.812800 0.000000 0 CHG=0 M V30 5 O 1.068000 0.400300 0.000000 0 CHG=0 M V30 6 O 0.353500 1.637900 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 2 2 3 M V30 3 1 3 4 M V30 4 1 4 5 M V30 5 2 4 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ E -INDIGO-09101021542D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 8 7 0 0 0 M V30 BEGIN ATOM M V30 1 C 0.140800 1.888200 0.000000 0 CHG=0 M V30 2 C -0.684200 1.888200 0.000000 0 CHG=0 M V30 3 C -1.096700 1.173700 0.000000 0 CHG=0 M V30 4 O -1.921700 1.173700 0.000000 0 CHG=0 M V30 5 O -0.684200 0.459200 0.000000 0 CHG=0 M V30 6 C 0.553300 1.173700 0.000000 0 CHG=0 M V30 7 O 1.378300 1.173700 0.000000 0 CHG=0 M V30 8 O 0.140800 0.459200 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 2 1 2 M V30 2 1 2 3 M V30 3 1 3 4 M V30 4 2 3 5 M V30 5 1 1 6 M V30 6 1 6 7 M V30 7 2 6 8 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ J -INDIGO-09101021542D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 11 11 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.792800 -0.533300 0.000000 0 CHG=0 M V30 2 C -1.794000 -1.360600 0.000000 0 CHG=0 M V30 3 C -1.079200 -1.773400 0.000000 0 CHG=0 M V30 4 C -0.362900 -1.360100 0.000000 0 CHG=0 M V30 5 C -0.365800 -0.529700 0.000000 0 CHG=0 M V30 6 C -1.081000 -0.120600 0.000000 0 CHG=0 M V30 7 C 0.347100 -0.114600 0.000000 0 CHG=0 M V30 8 C 1.063100 -0.524300 0.000000 0 CHG=0 M V30 9 O 1.066200 -1.349200 0.000000 0 CHG=0 M V30 10 O 1.775900 -0.109200 0.000000 0 CHG=0 M V30 11 C 0.344000 0.710400 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 2 1 2 M V30 2 1 2 3 M V30 3 2 3 4 M V30 4 1 4 5 M V30 5 2 5 6 M V30 6 1 6 1 M V30 7 1 5 7 M V30 8 1 7 8 M V30 9 2 8 9 M V30 10 1 8 10 M V30 11 1 7 11 CFG=1 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STERAC1 ATOMS=(1 7) M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/1_amides_stereo/legio_am_amines.sdf000066400000000000000000000031321271037650300271260ustar00rootroot00000000000000Z -INDIGO-09101021532D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 3 2 0 0 0 M V30 BEGIN ATOM M V30 1 N -1.790100 0.400300 0.000000 0 CHG=0 M V30 2 C -1.075500 0.812800 0.000000 0 CHG=0 M V30 3 C -0.361000 0.400300 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ X -INDIGO-09101021532D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 6 0 0 0 M V30 BEGIN ATOM M V30 1 N -1.790100 0.400300 0.000000 0 CHG=0 M V30 2 C -1.075500 0.812800 0.000000 0 CHG=0 M V30 3 C -0.361000 0.400300 0.000000 0 CHG=0 M V30 4 C -0.904000 1.619700 0.000000 0 CHG=0 M V30 5 C -0.083500 1.706000 0.000000 0 CHG=0 M V30 6 C 0.252000 0.952400 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 4 M V30 3 1 3 2 M V30 4 1 3 6 M V30 5 1 4 5 M V30 6 1 5 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ Y -INDIGO-09101021532D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C 6.050000 -5.675000 0.000000 0 CHG=0 M V30 2 C 6.766000 -5.261600 0.000000 0 CHG=0 M V30 3 C 6.766000 -4.434800 0.000000 0 CHG=0 M V30 4 N 7.482000 -5.675000 0.000000 0 CHG=0 M V30 5 C 6.050000 -4.021500 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 3 5 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(1 2) M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/1_amides_stereo/legio_am_rxn.rxn000066400000000000000000000024011271037650300265120ustar00rootroot00000000000000$RXN MDL-Draw 100220091934 2 1 $MOL MDL-Draw10020919342D 4 3 0 0 0 0 0 0 0 0999 V2000 3.9042 -3.3125 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 4.6167 -2.8958 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6167 -2.0708 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5.3292 -3.3083 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 M RGP 1 1 1 M END $MOL MDL-Draw10020919342D 2 1 0 0 0 0 0 0 0 0999 V2000 7.0917 -3.2167 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 7.8042 -2.8000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M RGP 1 2 2 M END $MOL MDL-Draw10020919342D 5 4 0 0 0 0 0 0 0 0999 V2000 10.5292 -3.2167 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 11.2417 -2.8000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.2417 -1.9750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 11.9542 -3.2125 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 12.6667 -2.7958 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 5 1 0 0 0 0 M RGP 2 1 1 5 2 M END Indigo-indigo-1.2.3/utils/legio/examples/2_diels-alder/000077500000000000000000000000001271037650300226645ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/2_diels-alder/legio_cisd_rxn.rxn000066400000000000000000000035351271037650300264130ustar00rootroot00000000000000$RXN ISIS 072920021253 2 1 $MOL -ISIS- 07290212532D 4 3 0 0 0 0 0 0 0 0999 V2000 -2.7833 -0.9042 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0 -3.5000 -1.3125 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 -3.5042 -2.1375 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 -2.7917 -2.5500 0.0000 C 0 0 0 0 0 0 0 0 0 4 0 0 2 3 1 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 M END $MOL -ISIS- 07290212532D 5 4 0 0 0 0 0 0 0 0999 V2000 0.3750 -2.1250 0.0000 C 0 0 0 0 1 0 0 0 0 5 0 0 0.3708 -1.3000 0.0000 C 0 0 0 0 1 0 0 0 0 6 0 0 1.0833 -0.8833 0.0000 A 0 0 0 0 0 0 0 0 0 7 0 0 1.0792 -0.0583 0.0000 O 0 0 0 0 0 0 0 0 0 8 0 0 1.0917 -2.5292 0.0000 C 0 0 0 0 0 0 0 0 0 9 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 2 3 1 0 0 0 0 1 5 1 0 0 0 0 M END $MOL -ISIS- 07290212532D 9 9 0 0 0 0 0 0 0 0999 V2000 5.2750 -1.1875 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 5.2750 -2.0125 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 5.9870 -2.4208 0.0000 C 0 0 0 0 0 0 0 0 0 4 0 0 6.6990 -2.0125 0.0000 C 0 0 2 0 0 0 0 0 0 5 0 0 6.6990 -1.1875 0.0000 C 0 0 2 0 0 0 0 0 0 6 0 0 5.9870 -0.7708 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0 7.4083 -0.7708 0.0000 A 0 0 0 0 0 0 0 0 0 7 0 0 7.4042 0.0542 0.0000 O 0 0 0 0 0 0 0 0 0 8 0 0 7.4083 -2.4250 0.0000 C 0 0 0 0 0 0 0 0 0 9 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 6 1 0 0 0 0 1 2 2 0 0 0 0 5 7 1 6 0 0 0 1 6 1 0 0 0 0 7 8 2 0 0 0 0 2 3 1 0 0 0 0 4 9 1 6 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/2_diels-alder/legio_dienes.sdf000066400000000000000000000125471271037650300260210ustar00rootroot000000000000001,3 BUTADIENE Marvin 07211021102D 4 3 0 0 0 0 999 V2000 -0.4083 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -1.4500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3000 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3042 -0.2083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 3 2 0 0 0 0 1 2 1 0 0 0 0 1 4 2 0 0 0 0 M END $$$$ 1,3 trans-PENTADIENE Marvin 07211021102D 5 4 0 0 0 0 999 V2000 -0.4083 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -1.4500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3000 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3042 -0.2083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3000 0.6167 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 3 2 0 0 0 0 1 2 1 0 0 0 0 1 4 2 0 0 0 0 4 5 1 0 0 0 0 M END $$$$ 1,3 cis-PENTADIENE Marvin 07211021102D 5 4 0 0 0 0 999 V2000 -0.4087 -0.6256 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4129 -1.4515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3003 -1.8644 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3045 -0.2085 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0010 -0.5005 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 3 2 0 0 0 0 1 2 1 0 0 0 0 1 4 2 0 0 0 0 4 5 1 0 0 0 0 M END $$$$ 2-trans,4-cis HEXADIENE Marvin 07211021102D 6 5 0 0 0 0 999 V2000 -0.4083 -0.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4125 -1.4500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3000 -1.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3042 -0.2083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0000 -0.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.2958 -2.6875 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 4 2 0 0 0 0 4 5 1 0 0 0 0 2 3 2 0 0 0 0 3 6 1 0 0 0 0 M END $$$$ 2-cis,4-cis HEXADIENE Marvin 07211021102D 6 5 0 0 0 0 999 V2000 -0.4091 -0.6263 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4134 -1.4530 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3006 -1.8664 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3048 -0.2087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0021 -0.5010 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9687 -1.5031 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 4 2 0 0 0 0 4 5 1 0 0 0 0 2 3 2 0 0 0 0 3 6 1 0 0 0 0 M END $$$$ CYCLOPENTADIENE Marvin 07211021102D 5 5 0 0 0 0 999 V2000 -1.6209 -1.4042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.7959 -1.4042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.5391 -0.6201 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.2084 -0.1333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8734 -0.6201 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 1 2 0 0 0 0 M END $$$$ 1,3 CYCLOHEXADIENE Marvin 07211021102D 6 6 0 0 0 0 999 V2000 -1.8458 -1.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.8458 -2.0750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1338 -2.4833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4218 -2.0750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4218 -1.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.1338 -0.8333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 6 1 0 0 0 0 2 3 1 0 0 0 0 3 4 2 0 0 0 0 4 5 1 0 0 0 0 5 6 1 0 0 0 0 M END $$$$ Marvin 07211021102D 6 5 0 0 0 0 999 V2000 1.7102 -4.1811 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7090 -5.0081 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4234 -5.4206 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1395 -5.0076 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.1366 -4.1775 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.4216 -3.7685 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 2 3 1 0 0 0 0 5 6 2 0 0 0 0 6 1 1 0 0 0 0 M END $$$$ 3-ETHOXY 1, 3-trans PENTADIENE Marvin 07211021102D 8 7 0 0 0 0 999 V2000 -0.6583 -1.2792 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6625 -2.1042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0500 -2.5167 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0542 -0.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.0500 -0.0375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.3750 -0.8667 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.0917 -1.2750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.8083 -0.8625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4 5 1 0 0 0 0 2 3 2 0 0 0 0 1 6 1 0 0 0 0 1 2 1 0 0 0 0 6 7 1 0 0 0 0 1 4 2 0 0 0 0 7 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/2_diels-alder/legio_dienophiles.sdf000066400000000000000000000165251271037650300270550ustar00rootroot00000000000000p-BENZOQUINONE Marvin 07211021112D 8 8 0 0 0 0 999 V2000 0.1542 -1.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1542 -1.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8662 -2.2333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5782 -1.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.5782 -1.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8662 -0.5833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8662 0.2417 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.8662 -3.0583 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 6 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 2 0 0 0 0 5 6 1 0 0 0 0 6 7 2 0 0 0 0 1 2 2 0 0 0 0 3 8 2 0 0 0 0 M END $$$$ MALEIC ANHYDRIDE Marvin 07211021112D 7 7 0 0 0 0 999 V2000 -0.7167 -1.6542 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1083 -1.6542 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3651 -0.8701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3042 -0.3833 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.9692 -0.8701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7540 -0.6155 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1.1501 -0.6162 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 1 1 0 0 0 0 1 2 2 0 0 0 0 5 6 2 0 0 0 0 3 7 2 0 0 0 0 M END $$$$ ACROLEIN Marvin 07211021112D 5 4 0 0 0 0 999 V2000 4.7542 -4.2459 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7500 -3.4209 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4625 -3.0042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4583 -2.1792 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 6.1750 -3.4167 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 3 5 1 0 0 0 0 M END $$$$ CYCLOPENTENONE Marvin 07211021112D 6 6 0 0 0 0 999 V2000 2.2958 -0.9375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.5526 -0.1534 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8833 0.3333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4708 -0.9375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.2196 -0.1548 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.8811 1.1583 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 3 5 1 0 0 0 0 4 5 2 0 0 0 0 4 1 1 0 0 0 0 1 2 1 0 0 0 0 3 6 2 0 0 0 0 M END $$$$ beta-NITROSTYRENE Marvin 07211021112D 11 11 0 0 0 0 999 V2000 0.4665 -1.9367 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4623 -1.1120 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1745 -0.6955 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1703 0.1291 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 0.4540 0.5415 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 1.8825 0.5456 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.2480 -2.3508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.2441 -3.1746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.4727 -3.5842 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1871 -3.1637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.1798 -2.3413 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4 6 2 0 0 0 0 1 2 1 0 0 0 0 1 7 2 0 0 0 0 3 4 1 0 0 0 0 7 8 1 0 0 0 0 8 9 2 0 0 0 0 4 5 1 0 0 0 0 9 10 1 0 0 0 0 2 3 2 0 0 0 0 10 11 2 0 0 0 0 11 1 1 0 0 0 0 M CHG 2 4 1 5 -1 M END $$$$ 1,4-NAPTHOQUINONE Marvin 07211021112D 12 13 0 0 0 0 999 V2000 3.5795 -2.0192 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2945 -1.6068 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.2916 -0.7780 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.5777 -0.3697 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8662 -1.6073 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.8704 -0.7816 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1614 -0.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4437 -0.7744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.4396 -1.6001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1531 -2.0188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2.1667 0.4557 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 2.1501 -2.8420 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5 1 1 0 0 0 0 2 3 1 0 0 0 0 3 4 2 0 0 0 0 4 6 1 0 0 0 0 5 10 1 0 0 0 0 6 7 1 0 0 0 0 7 8 1 0 0 0 0 8 9 2 0 0 0 0 9 10 1 0 0 0 0 5 6 2 0 0 0 0 7 11 2 0 0 0 0 1 2 2 0 0 0 0 10 12 2 0 0 0 0 M END $$$$ FLUORODIMETHYLACROLEIN Marvin 07211021112D 8 7 0 0 0 0 999 V2000 4.7625 -2.9375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7584 -3.7625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4750 -2.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4709 -1.6958 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.0459 -2.5250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4709 -4.1750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.1834 -2.9292 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 4.0417 -4.1708 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 1 5 1 0 0 0 0 1 3 1 0 0 0 0 2 6 1 0 0 0 0 3 7 1 0 0 0 0 2 8 1 0 0 0 0 M END $$$$ DIMETHYLACROLEIN Marvin 07211021112D 7 6 0 0 0 0 999 V2000 5.1083 -2.9375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.1042 -3.7625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8208 -2.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8167 -1.6958 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.3917 -2.5250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.8167 -4.1750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.5292 -2.9292 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 1 5 1 0 0 0 0 1 3 1 0 0 0 0 2 6 1 0 0 0 0 3 7 1 0 0 0 0 M END $$$$ DIMETHYLACROLEIN-H Marvin 07211021112D 8 7 0 0 0 0 999 V2000 4.7625 -2.9375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7584 -3.7625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4750 -2.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4709 -1.6958 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.0459 -2.5250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4709 -4.1750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.1834 -2.9292 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 4.0417 -4.1708 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 3 4 2 0 0 0 0 1 5 1 0 0 0 0 1 3 1 0 0 0 0 2 6 1 0 0 0 0 3 7 1 0 0 0 0 2 8 1 0 0 0 0 M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/3_symmetrical/000077500000000000000000000000001271037650300230315ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/3_symmetrical/legio_modes_mon_1.sdf000066400000000000000000000024131271037650300271060ustar00rootroot00000000000000 -INDIGO-09101022032D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 4 3 0 0 0 M V30 BEGIN ATOM M V30 1 C 3.168200 -4.989300 0.000000 0 CHG=0 M V30 2 C 3.882700 -4.576800 0.000000 0 CHG=0 M V30 3 O 3.882700 -3.751700 0.000000 0 CHG=0 M V30 4 Cl 4.597200 -4.989300 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 2 2 3 M V30 3 1 2 4 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ -INDIGO-09101022032D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 9 9 0 0 0 M V30 BEGIN ATOM M V30 1 C 3.168200 -4.989300 0.000000 0 CHG=0 M V30 2 C 3.882700 -4.576800 0.000000 0 CHG=0 M V30 3 O 3.882700 -3.751700 0.000000 0 CHG=0 M V30 4 Cl 4.597100 -4.989300 0.000000 0 CHG=0 M V30 5 C 2.453700 -4.576800 0.000000 0 CHG=0 M V30 6 C 1.739300 -4.989300 0.000000 0 CHG=0 M V30 7 C 1.739300 -5.814300 0.000000 0 CHG=0 M V30 8 C 2.453700 -6.226800 0.000000 0 CHG=0 M V30 9 C 3.168200 -5.814300 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 2 2 3 M V30 3 1 2 4 M V30 4 1 9 1 M V30 5 1 5 1 M V30 6 1 5 6 M V30 7 1 6 7 M V30 8 1 7 8 M V30 9 1 8 9 M V30 END BOND M V30 BEGIN COLLECTION M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/3_symmetrical/legio_modes_mon_2.mol000066400000000000000000000022301271037650300271170ustar00rootroot00000000000000 SMMXDraw06041010492D 12 12 0 0 1 0 0 0 0 0999 V2000 5.4340 -4.6616 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 5.4340 -5.4884 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 6.1500 -5.9018 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 6.8660 -5.4884 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 6.8660 -4.6616 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 6.1500 -4.2482 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 6.1500 -3.4215 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 7.5820 -5.9018 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.7180 -5.9018 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 7.5820 -4.2482 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 6.1500 -6.7285 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.7180 -4.2482 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5 6 1 0 0 0 0 4 5 1 0 0 0 0 3 4 1 0 0 0 0 2 3 1 0 0 0 0 1 2 1 0 0 0 0 1 6 1 0 0 0 0 6 7 1 1 0 0 0 4 8 1 1 0 0 0 2 9 1 1 0 0 0 5 10 1 6 0 0 0 3 11 1 6 0 0 0 1 12 1 6 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/3_symmetrical/legio_modes_rxn.rxn000066400000000000000000000024651271037650300267460ustar00rootroot00000000000000$RXN Marv SMMXDraw 060320101437 2 1 $MOL SMMXDraw06031014372D 4 3 1 0 0 0 0 0 0 0999 V2000 4.4000 -4.6750 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 5.1160 -4.2616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.1160 -3.4348 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5.8320 -4.6750 0.0000 L 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 F 3 17 35 53 M RGP 1 1 1 M ALS 4 3 F Cl Br I M END $MOL SMMXDraw06031014372D 2 1 0 0 0 0 0 0 0 0999 V2000 7.9000 -4.2750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 8.6160 -3.8616 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M RGP 1 2 2 M END $MOL SMMXDraw06031014372D 5 4 0 0 0 0 0 0 0 0999 V2000 11.8250 -4.2000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 12.5410 -3.7866 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.5410 -2.9598 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 13.2570 -4.2000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 13.9730 -3.7866 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 5 1 0 0 0 0 M RGP 2 1 1 5 2 M END Indigo-indigo-1.2.3/utils/legio/examples/4_intramol/000077500000000000000000000000001271037650300223265ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/4_intramol/legio_intr_mon.mol000066400000000000000000000014631271037650300260470ustar00rootroot00000000000000I Marvin 05141016002D 8 8 0 0 0 0 999 V2000 -1.7900 0.4002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0756 0.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3610 0.4002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.3534 0.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.0680 0.4003 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.3534 1.6379 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.5002 1.4000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.5046 0.8128 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 4 6 2 0 0 0 0 2 7 1 0 0 0 0 7 3 1 0 0 0 0 1 8 1 0 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/4_intramol/legio_intr_rxn.rxn000066400000000000000000000024011271037650300260760ustar00rootroot00000000000000$RXN MDL-Draw 100220091934 2 1 $MOL MDL-Draw10020919342D 4 3 0 0 0 0 0 0 0 0999 V2000 3.9042 -3.3125 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 4.6167 -2.8958 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6167 -2.0708 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5.3292 -3.3083 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 M RGP 1 1 1 M END $MOL MDL-Draw10020919342D 2 1 0 0 0 0 0 0 0 0999 V2000 7.0917 -3.2167 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 7.8042 -2.8000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M RGP 1 2 2 M END $MOL MDL-Draw10020919342D 5 4 0 0 0 0 0 0 0 0999 V2000 10.5292 -3.2167 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 11.2417 -2.8000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 11.2417 -1.9750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 11.9542 -3.2125 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 12.6667 -2.7958 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 5 1 0 0 0 0 M RGP 2 1 1 5 2 M END Indigo-indigo-1.2.3/utils/legio/examples/5_glucose_mult/000077500000000000000000000000001271037650300232045ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/5_glucose_mult/legio_mult_accl.mol000066400000000000000000000006421271037650300270410ustar00rootroot00000000000000 SMMXDraw06031014332D 4 3 0 0 0 0 0 0 0 0999 V2000 3.1750 -5.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8910 -4.5866 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 3.8910 -3.7598 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 4.6070 -5.0000 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/5_glucose_mult/legio_mult_glucose.mol000066400000000000000000000022421271037650300275760ustar00rootroot00000000000000 Marvin 02060713222D 12 12 0 0 1 0 999 V2000 0.0000 -0.8250 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 0.0000 -1.6500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.7145 -0.4125 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -1.4289 -0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.7145 0.4125 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 0.7145 0.4125 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 1.4289 0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1.4289 -0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.0000 0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.7145 -0.4125 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 -1.4289 0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.1434 0.4125 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5 3 1 0 0 0 0 5 9 1 0 0 0 0 3 1 1 0 0 0 0 9 6 1 0 0 0 0 1 10 1 0 0 0 0 6 10 1 0 0 0 0 1 2 1 1 0 0 0 3 4 1 6 0 0 0 5 11 1 1 0 0 0 6 7 1 1 0 0 0 10 8 1 6 0 0 0 11 12 1 0 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/5_glucose_mult/legio_mult_rxn.rxn000066400000000000000000000024651271037650300267730ustar00rootroot00000000000000$RXN Marv SMMXDraw 060320101437 2 1 $MOL SMMXDraw06031014372D 4 3 1 0 0 0 0 0 0 0999 V2000 4.4000 -4.6750 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 5.1160 -4.2616 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.1160 -3.4348 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5.8320 -4.6750 0.0000 L 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 F 3 17 35 53 M RGP 1 1 1 M ALS 4 3 F Cl Br I M END $MOL SMMXDraw06031014372D 2 1 0 0 0 0 0 0 0 0999 V2000 7.9000 -4.2750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 8.6160 -3.8616 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 M RGP 1 2 2 M END $MOL SMMXDraw06031014372D 5 4 0 0 0 0 0 0 0 0999 V2000 11.8250 -4.2000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 12.5410 -3.7866 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 12.5410 -2.9598 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 13.2570 -4.2000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 13.9730 -3.7866 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 4 5 1 0 0 0 0 M RGP 2 1 1 5 2 M END Indigo-indigo-1.2.3/utils/legio/examples/6_tetra_stereo/000077500000000000000000000000001271037650300232035ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/6_tetra_stereo/legio_sc_mon.sdf000066400000000000000000000057251271037650300263470ustar00rootroot00000000000000 Marvin 05141018532D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.9859 -2.0176 0 0 M V30 2 C -1.1894 -0.6996 0 0 CFG=2 M V30 3 N 0.3503 -0.7304 0 0 M V30 4 Cl -1.9325 0.6492 0 0 M V30 5 S 1.1469 0.5876 0 0 M V30 6 C 1.0935 -2.0792 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 3 5 M V30 5 1 3 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(1 2) M V30 END COLLECTION M V30 END CTAB M END $$$$ Marvin 05141018542D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.9564 -2.0523 0 0 M V30 2 C -1.1895 -0.7168 0 0 CFG=1 M V30 3 N 0.3505 -0.7132 0 0 M V30 4 Cl -1.9627 0.615 0 0 M V30 5 S 1.1173 0.6223 0 0 M V30 6 C 1.1236 -2.045 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=3 M V30 4 1 3 5 M V30 5 1 3 6 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(1 2) M V30 END COLLECTION M V30 END CTAB M END $$$$ Marvin 05141018542D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.9839 -2.02 0 0 M V30 2 C -1.1894 -0.7008 0 0 CFG=2 M V30 3 N 0.3503 -0.7292 0 0 CFG=1 M V30 4 Cl -1.9346 0.6469 0 0 M V30 5 S 1.1448 0.59 0 0 M V30 6 C 1.0956 -2.0769 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 3 5 M V30 5 1 3 6 CFG=1 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEREL2 ATOMS=(1 3) M V30 MDLV30/STERAC1 ATOMS=(1 2) M V30 END COLLECTION M V30 END CTAB M END $$$$ Marvin 05141018552D 6 5 0 0 0 0 999 V2000 -1.0651 -1.0794 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.6372 -0.3741 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.1877 -0.3920 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -1.0340 0.3492 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 0.6156 0.3133 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 0.5845 -1.1153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 2 4 1 0 0 0 0 3 5 1 0 0 0 0 3 6 1 0 0 0 0 M END $$$$ -INDIGO-09101022132D 0 0 0 0 0 0 0 0 0 0 0 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.983900 -2.020000 0.000000 0 CHG=0 M V30 2 C -1.189400 -0.700800 0.000000 0 CHG=0 M V30 3 N 0.350300 -0.729200 0.000000 0 CHG=0 M V30 4 Cl -1.934600 0.646900 0.000000 0 CHG=0 M V30 5 S 1.144800 0.590000 0.000000 0 CHG=0 M V30 6 C 1.095600 -2.076900 0.000000 0 CHG=0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 3 5 M V30 5 1 3 6 CFG=1 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STERAC1 ATOMS=(1 2) M V30 MDLV30/STERAC2 ATOMS=(1 3) M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/6_tetra_stereo/legio_sc_rxn.rxn000066400000000000000000000024401271037650300264070ustar00rootroot00000000000000$RXN Marvin 051401101853 1 1 $MOL Marvin 05141018532D 6 5 0 0 0 0 999 V2000 -3.4896 -0.7332 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 -3.0936 -0.0095 0.0000 C 0 0 2 0 0 0 0 0 0 1 0 0 -2.2689 0.0095 0.0000 N 0 0 0 0 0 0 0 0 0 3 0 0 -3.5225 0.6953 0.0000 Cl 0 0 0 0 0 0 0 0 0 2 0 0 -1.8729 0.7332 0.0000 S 0 0 0 0 0 0 0 0 0 4 0 0 -1.8400 -0.6953 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 0 1 2 1 0 0 0 0 2 4 1 1 0 0 0 2 3 1 0 0 0 0 3 6 1 0 0 0 0 3 5 1 0 0 0 0 M RGP 1 1 1 M END $MOL Marvin 05141018532D 6 5 0 0 0 0 999 V2000 2.6850 -0.7188 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 3.0938 -0.0022 0.0000 C 0 0 1 0 0 0 0 0 0 1 0 0 3.9187 0.0022 0.0000 N 0 0 0 0 0 0 0 0 0 3 0 0 2.6775 0.7101 0.0000 Cl 0 0 0 0 0 0 0 0 0 2 0 0 4.3275 0.7188 0.0000 S 0 0 0 0 0 0 0 0 0 4 0 0 4.3350 -0.7101 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 0 1 2 1 0 0 0 0 2 4 1 6 0 0 0 2 3 1 0 0 0 0 3 6 1 0 0 0 0 3 5 1 0 0 0 0 M RGP 1 1 1 M END Indigo-indigo-1.2.3/utils/legio/examples/7_cistrans_stereo/000077500000000000000000000000001271037650300237135ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/7_cistrans_stereo/legio_cs_mon.sdf000066400000000000000000000027711271037650300270550ustar00rootroot00000000000000 Marvin 05141019232D 4 3 0 0 0 0 999 V2000 -2.7107 -0.5304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2982 0.1841 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -3.5357 -0.5304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9482 -1.2448 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 2 0 0 0 0 3 4 1 0 0 0 0 M END $$$$ Marvin 05141019232D 4 3 0 0 0 0 999 V2000 -2.7107 0.1185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.2982 -0.5959 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -3.5357 0.1185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.9482 -0.5959 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 1 3 2 0 0 0 0 3 4 1 0 0 0 0 M END $$$$ Marvin 05141019232D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 8 7 0 0 0 M V30 BEGIN ATOM M V30 1 C -5.06 -0.9901 0 0 CFG=2 M V30 2 C -3.9711 0.0989 0 0 M V30 3 C -2.4311 0.0989 0 0 M V30 4 N -1.3421 -0.9901 0 0 CFG=1 M V30 5 O -6.149 0.0989 0 0 M V30 6 Cl -5.06 -2.5301 0 0 M V30 7 S -0.2531 0.0989 0 0 M V30 8 C -1.3421 -2.5301 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 2 2 3 M V30 3 1 3 4 M V30 4 1 1 5 M V30 5 1 1 6 CFG=1 M V30 6 1 4 7 M V30 7 1 4 8 CFG=1 M V30 END BOND M V30 BEGIN COLLECTION M V30 MDLV30/STEABS ATOMS=(2 1 4) M V30 END COLLECTION M V30 END CTAB M END $$$$ Indigo-indigo-1.2.3/utils/legio/examples/7_cistrans_stereo/legio_cs_rxn.rxn000066400000000000000000000017001271037650300271150ustar00rootroot00000000000000$RXN Marvin 032701101618 1 1 $MOL Marvin 03271016182D 4 3 0 0 0 0 999 V2000 -4.2723 -1.2375 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 -3.6890 -0.6541 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0 -2.8640 -0.6541 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 -2.2806 -0.0708 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 M RGP 2 1 1 4 2 M END $MOL Marvin 03271016182D 4 3 0 0 0 0 999 V2000 1.6795 -0.2946 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 2.3218 -0.9488 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0 3.1468 -0.9488 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 3.7301 -0.1768 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 3 4 1 0 0 0 0 M RGP 2 1 1 4 2 M END Indigo-indigo-1.2.3/utils/legio/examples/8_peptides/000077500000000000000000000000001271037650300223225ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/8_peptides/legio_peps_glycine.mol000066400000000000000000000010101271037650300266630ustar00rootroot00000000000000 Marvin 01110618342D 5 4 0 0 1 0 999 V2000 0.2847 0.0007 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.4298 -0.4118 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.9992 -0.4118 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.2847 0.8257 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -1.1442 0.0007 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 4 1 2 0 0 0 0 3 1 1 0 0 0 0 2 5 1 0 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/8_peptides/legio_peps_lysine.mol000066400000000000000000000017241271037650300265500ustar00rootroot00000000000000 Marvin 06140612002D 10 9 0 0 0 0 999 V2000 2.5006 -0.2062 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 0.3572 0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3572 -0.2063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.0717 0.2062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -1.7862 -0.2063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.5006 0.2062 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1.7862 0.2063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.7862 1.0313 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1.0717 -0.2063 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 1.0717 -1.0313 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 7 1 0 0 0 0 9 7 1 0 0 0 0 2 9 1 0 0 0 0 3 2 1 0 0 0 0 4 3 1 0 0 0 0 5 4 1 0 0 0 0 6 5 1 0 0 0 0 7 8 2 0 0 0 0 9 10 1 1 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/8_peptides/legio_peps_rxn.rxn000066400000000000000000000037771271037650300261060ustar00rootroot00000000000000$RXN Marvin 081601101040 2 1 $MOL Marvin 08161010402D 5 4 0 0 0 0 999 V2000 7.6246 -6.4477 0.0000 N 0 0 0 0 0 0 0 0 0 1 0 0 8.3391 -6.8602 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 9.0535 -6.4477 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 9.7680 -6.8602 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 9.0535 -5.6227 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 3 5 2 0 0 0 0 M RGP 1 2 1 M END $MOL Marvin 08161010402D 5 4 0 0 0 0 999 V2000 12.0535 -6.5359 0.0000 N 0 0 0 0 0 0 0 0 0 4 0 0 12.7679 -6.9485 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 13.4824 -6.5359 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 14.1969 -6.9485 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 13.4824 -5.7109 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 3 5 2 0 0 0 0 M RGP 1 2 2 M END $MOL Marvin 08161010402D 9 8 0 0 0 0 999 V2000 17.3421 -6.5858 0.0000 N 0 0 0 0 0 0 0 0 0 1 0 0 18.0566 -6.9983 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 18.7710 -6.5858 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0 19.4855 -6.9983 0.0000 N 0 0 0 0 0 0 0 0 0 4 0 0 18.7710 -5.7608 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 20.2000 -6.5858 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 20.9144 -6.9983 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 21.6289 -6.5858 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 20.9144 -7.8234 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 3 5 2 0 0 0 0 4 6 1 0 0 0 0 6 7 1 0 0 0 0 7 8 1 0 0 0 0 7 9 2 0 0 0 0 M RGP 2 2 1 6 2 M END Indigo-indigo-1.2.3/utils/legio/examples/9_Ugi4cc/000077500000000000000000000000001271037650300216245ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/examples/9_Ugi4cc/legio_ugi_amac.mol000066400000000000000000000016041271037650300252620ustar00rootroot00000000000000 SMMXDraw09011019332D 9 9 0 0 1 0 0 0 0 0999 V2000 5.0500 -7.1280 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.3815 -6.6423 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6368 -5.8563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4632 -5.8563 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 5.7185 -6.6423 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 5.8764 -5.1406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.5168 -6.8561 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 5.4632 -4.4250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 6.7027 -5.1406 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5 1 1 0 0 0 0 4 5 1 0 0 0 0 3 4 1 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 4 6 1 1 0 0 0 5 7 1 1 0 0 0 6 8 2 0 0 0 0 6 9 1 0 0 0 0 M END Indigo-indigo-1.2.3/utils/legio/examples/9_Ugi4cc/legio_ugi_iso.mol000066400000000000000000000015021271037650300251500ustar00rootroot00000000000000 SMMXDraw09011019362D 8 8 0 0 0 0 0 0 0 0999 V2000 4.0340 -5.6366 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.0340 -6.4634 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7500 -6.8768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4660 -6.4634 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.4660 -5.6366 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.7500 -5.2232 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 4.7500 -4.3965 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 4.7250 -3.5000 0.0000 C 0 5 0 0 0 0 0 0 0 0 0 0 5 6 1 0 0 0 0 4 5 1 0 0 0 0 3 4 1 0 0 0 0 2 3 1 0 0 0 0 1 2 1 0 0 0 0 1 6 1 0 0 0 0 6 7 1 0 0 0 0 7 8 3 0 0 0 0 M CHG 2 7 1 8 -1 M END Indigo-indigo-1.2.3/utils/legio/examples/9_Ugi4cc/legio_ugi_ketone.mol000066400000000000000000000021261271037650300256460ustar00rootroot00000000000000 SMMXDraw09011019342D 11 11 0 0 0 0 0 0 0 0999 V2000 4.6594 -5.8112 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.0891 -5.8108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3756 -5.3980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 6.0891 -6.6378 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6594 -6.6415 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3774 -7.0505 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 5.3756 -4.5712 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 4.6595 -4.1578 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 5.3774 -7.8768 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 6.0930 -8.2900 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 4.6618 -8.2900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 6 4 1 0 0 0 0 5 6 2 0 0 0 0 2 3 1 0 0 0 0 1 5 1 0 0 0 0 4 2 2 0 0 0 0 3 1 2 0 0 0 0 3 7 1 0 0 0 0 7 8 2 0 0 0 0 6 9 1 0 0 0 0 9 10 1 0 0 0 0 9 11 2 0 0 0 0 M CHG 2 9 1 10 -1 M END Indigo-indigo-1.2.3/utils/legio/examples/9_Ugi4cc/legio_ugi_rxn.rxn000066400000000000000000000053251271037650300252140ustar00rootroot00000000000000$RXN SMMXDraw 090120101948 4 1 $MOL SMMXDraw09011019482D 4 3 0 0 0 0 0 0 0 0999 V2000 2.9910 -5.3866 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0 2.9910 -4.5598 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 3.7070 -5.8000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 2.2750 -5.8000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 0 0 0 0 1 3 1 0 0 0 0 1 4 1 0 0 0 0 M RGP 2 3 2 4 1 M END $MOL SMMXDraw09011019482D 2 1 0 0 0 0 0 0 0 0999 V2000 5.7750 -5.2000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 6.4910 -4.7866 0.0000 N 0 0 0 0 0 0 0 0 0 2 0 0 1 2 1 0 0 0 0 M RGP 1 1 3 M END $MOL SMMXDraw09011019482D 4 3 0 0 0 0 0 0 0 0999 V2000 8.6500 -5.5250 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 9.3660 -5.1116 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 9.3660 -4.2848 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 10.0820 -5.5250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 2 0 0 0 0 2 4 1 0 0 0 0 M RGP 1 1 4 M END $MOL SMMXDraw09011019482D 3 2 0 0 0 0 0 0 0 0999 V2000 11.8000 -5.0750 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 12.5160 -4.6616 0.0000 N 0 3 0 0 0 0 0 0 0 4 0 0 13.2320 -4.2482 0.0000 C 0 5 0 0 0 0 0 0 0 5 0 0 1 2 1 0 0 0 0 2 3 3 0 0 0 0 M CHG 2 2 1 3 -1 M RGP 1 1 5 M END $MOL SMMXDraw09011019482D 12 11 0 0 0 0 0 0 0 0999 V2000 16.3250 -5.0000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 17.0410 -4.5866 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0 17.7570 -5.0000 0.0000 N 0 0 3 0 0 0 0 0 0 2 0 0 18.4730 -4.5866 0.0000 C 0 0 3 0 0 0 0 0 0 1 0 0 19.1890 -5.0000 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 0 19.9050 -4.5866 0.0000 N 0 0 3 0 0 0 0 0 0 4 0 0 20.6210 -5.0000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 17.0410 -3.7598 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 17.7570 -5.8268 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 18.1730 -3.8098 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 19.1750 -3.9000 0.0000 R# 0 0 0 0 0 0 0 0 0 0 0 0 19.1890 -5.8268 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 3 4 1 0 0 0 0 4 5 1 0 0 0 0 5 6 1 0 0 0 0 6 7 1 0 0 0 0 2 8 2 0 0 0 0 3 9 1 0 0 0 0 4 10 1 0 0 0 0 4 11 1 0 0 0 0 5 12 2 0 0 0 0 M RGP 5 1 4 7 5 9 3 10 1 11 2 M END Indigo-indigo-1.2.3/utils/legio/launch.bat000066400000000000000000000000431271037650300203770ustar00rootroot00000000000000start javaw -jar -Xss10m legio.jar Indigo-indigo-1.2.3/utils/legio/legio.sh000077500000000000000000000003411271037650300200740ustar00rootroot00000000000000#!/bin/sh JAVA_OPTS="-Xss10m -Dawt.useSystemAAFontSettings=on" #Following symlinks path=$0 while [ -h "$path" ]; do path=$(ls -ld $path | sed -r 's/^.*->.//') done java $JAVA_OPTS -jar $(dirname $path)/legio.jar "$@" Indigo-indigo-1.2.3/utils/legio/legio_installer.nsi000066400000000000000000000115051271037650300223310ustar00rootroot00000000000000!define WEB_SITE "http://epam.com/opensource/indigo/legio" !define APP_NAME "Legio" !define COMP_NAME "EPAM Systems" !define COPYRIGHT "EPAM Systems © 2010" !define DESCRIPTION "Legio is an Indigo-based GUI application that exposes the combinatorial chemistry capabilities of Indigo." SetCompressor /SOLID lzma !define MULTIUSER_EXECUTIONLEVEL Highest !define MULTIUSER_MUI !define MULTIUSER_INSTALLMODE_COMMANDLINE !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME "" !define MULTIUSER_INSTALLMODE_INSTDIR "EPAM Systems\${APP_NAME}" !include "MultiUser.nsh" !include "MUI2.nsh" ;-------------------------------- ;General ;Name and file Name "${APP_NAME}" OutFile "legio-${VERSION}-installer.exe" ;-------------------------------- ;Variables Var StartMenuFolder ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING ;-------------------------------- ;Language Selection Dialog Settings ;Remember the installer language !define MUI_LANGDLL_REGISTRY_ROOT "SHCTX" !define MUI_LANGDLL_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "LICENSE.GPL" !insertmacro MULTIUSER_PAGE_INSTALLMODE !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_DEFAULTFOLDER "EPAM Systems\${APP_NAME}" !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\EPAM Systems\${APP_NAME}" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES Function finishpageaction CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\launch.bat" FunctionEnd !define MUI_FINISHPAGE_RUN $INSTDIR\launch.bat !define MUI_FINISHPAGE_SHOWREADME "" !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" !define MUI_FINISHPAGE_SHOWREADME_FUNCTION finishpageaction !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;first language is the default language ;-------------------------------- ;Installer Sections Section "${APP_NAME}" SetOutPath "$INSTDIR\lib\" File /r "lib\" SetOutPath "$INSTDIR\examples\" File /r "examples\" SetOutPath $INSTDIR File "legio.jar" File "launch.bat" ;%NSIS_INSTALL_FILES ;Store installation folder WriteRegStr SHCTX "Software\EPAM Systems\${APP_NAME}" "" $INSTDIR ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\${APP_NAME}.lnk" "$INSTDIR\launch.bat" "" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" !ifdef WEB_SITE WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Website.lnk" "$INSTDIR\${APP_NAME} website.url" !endif !insertmacro MUI_STARTMENU_WRITE_END SectionEnd ;-------------------------------- ;Installer Functions Function .onInit !insertmacro MULTIUSER_INIT FunctionEnd ;-------------------------------- ;This function and example function call can be used to recursively delete empty parent folders of a given folder. Function un.RMDirUP !define RMDirUP "!insertmacro RMDirUPCall" !macro RMDirUPCall _PATH push '${_PATH}' Call un.RMDirUP !macroend ; $0 - current folder ClearErrors Exch $0 ;DetailPrint "ASDF - $0\.." RMDir "$0\.." IfErrors Skip ${RMDirUP} "$0\.." Skip: Pop $0 FunctionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" RMDir /r $INSTDIR !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\${APP_NAME}.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" !ifdef WEB_SITE Delete "$SMPROGRAMS\$StartMenuFolder\Website.lnk" !endif RMDir "$SMPROGRAMS\$StartMenuFolder" ${RMDirUP} "$SMPROGRAMS\$StartMenuFolder" Delete "$DESKTOP\${APP_NAME}.lnk" DeleteRegKey SHCTX "Software\EPAM Systems\${APP_NAME}" DeleteRegKey /ifempty SHCTX "Software\EPAM Systems" SectionEnd ;-------------------------------- ;Uninstaller Functions Function un.onInit !insertmacro MULTIUSER_UNINIT FunctionEnd Indigo-indigo-1.2.3/utils/legio/nbproject/000077500000000000000000000000001271037650300204265ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/nbproject/build-impl.xml000066400000000000000000001555261271037650300232240ustar00rootroot00000000000000 Must set src.src.dir Must set build.dir Must set dist.dir Must set build.classes.dir Must set dist.javadoc.dir Must set build.test.classes.dir Must set build.test.results.dir Must set build.classes.excludes Must set dist.jar Must set javac.includes Must set JVM to use for profiling in profiler.info.jvm Must set profiler agent JVM arguments in profiler.info.jvmargs.agent Must select some files in the IDE or set javac.includes To run this application from the command line without Ant, try: java -cp "${run.classpath.with.dist.jar}" ${main.class} To run this application from the command line without Ant, try: java -jar "${dist.jar.resolved}" Must select one file in the IDE or set run.class Must select one file in the IDE or set run.class Must select one file in the IDE or set debug.class Must select one file in the IDE or set debug.class Must set fix.includes Must select one file in the IDE or set profile.class Must select some files in the IDE or set javac.includes Some tests failed; see details above. Must select some files in the IDE or set test.includes Some tests failed; see details above. Must select one file in the IDE or set test.class Must select one file in the IDE or set applet.url Must select one file in the IDE or set applet.url Indigo-indigo-1.2.3/utils/legio/nbproject/genfiles.properties000066400000000000000000000007231271037650300243420ustar00rootroot00000000000000build.xml.data.CRC32=4ece4b00 build.xml.script.CRC32=74f2d670 build.xml.stylesheet.CRC32=28e38971@1.50.3.46 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=4c8dbe2e nbproject/build-impl.xml.script.CRC32=e8edad58 nbproject/build-impl.xml.stylesheet.CRC32=fcddb364@1.50.3.46 Indigo-indigo-1.2.3/utils/legio/nbproject/project.properties000066400000000000000000000060121271037650300242110ustar00rootroot00000000000000annotation.processing.enabled=true annotation.processing.enabled.in.editor=false annotation.processing.processors.list= annotation.processing.run.all.processors=true application.homepage=http://epam.com/opensource/indigo/legio application.splash=META-INF/legio-splash.png application.title=Legio application.vendor=EPAM Systems build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated build.generated.sources.dir=${build.dir}/generated-sources # Only compile against the classpath explicitly listed here: build.sysclasspath=ignore build.test.classes.dir=${build.dir}/test/classes build.test.results.dir=${build.dir}/test/results # Uncomment to specify the preferred debugger connection transport: #debug.transport=dt_socket debug.classpath=\ ${run.classpath} debug.test.classpath=\ ${run.test.classpath} # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/legio.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= file.reference.indigo-renderer.jar=..\\..\\dist\\indigo-java\\indigo-renderer.jar file.reference.indigo.jar=..\\..\\dist\\indigo-java\\indigo.jar file.reference.jna.jar=../../common/jna/jna.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=false jar.index=${jnlp.enabled} javac.classpath=\ ${reference.common-controls.jar}:\ ${file.reference.jna.jar}:\ ${file.reference.indigo-renderer.jar}:\ ${file.reference.indigo.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false javac.processorpath=\ ${javac.classpath} javac.source=1.5 javac.target=1.5 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir} javadoc.additionalparam= javadoc.author=false javadoc.encoding=${source.encoding} javadoc.noindex=false javadoc.nonavbar=false javadoc.notree=false javadoc.private=false javadoc.splitindex=true javadoc.use=true javadoc.version=false javadoc.windowtitle= jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" jnlp.codebase.type=no.codebase jnlp.descriptor=application jnlp.enabled=false jnlp.mixed.code=default jnlp.offline-allowed=false jnlp.signed=false jnlp.signing= jnlp.signing.alias= jnlp.signing.keystore= main.class=com.epam.indigo.legio.Main manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false platform.active=default_platform project.common-controls=../../common/java/common-controls reference.common-controls.jar=${project.common-controls}/dist/common-controls.jar run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} # Space-separated list of JVM arguments used when running the project # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value # or test-sys-prop.name=value to set system properties for unit tests): run.jvmargs=-Xss20m run.test.classpath=\ ${javac.test.classpath}:\ ${build.test.classes.dir} source.encoding=UTF-8 src.src.dir=src Indigo-indigo-1.2.3/utils/legio/nbproject/project.xml000066400000000000000000000015411271037650300226170ustar00rootroot00000000000000 org.netbeans.modules.java.j2seproject legio common-controls jar jar clean jar Indigo-indigo-1.2.3/utils/legio/src/000077500000000000000000000000001271037650300172275ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/src/com/000077500000000000000000000000001271037650300200055ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/src/com/epam/000077500000000000000000000000001271037650300207275ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/000077500000000000000000000000001271037650300222005ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/legio/000077500000000000000000000000001271037650300232775ustar00rootroot00000000000000Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/legio/LegioData.java000066400000000000000000000073671271037650300260100ustar00rootroot00000000000000package com.epam.indigo.legio; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.controls.IndigoCheckedException; import com.epam.indigo.controls.IndigoObjectWrapper; import java.io.IOException; import java.util.ArrayList; public class LegioData { IndigoObject reaction; IndigoObject monomers_table; private IndigoObject output_reactions; Indigo indigo; public LegioData( Indigo cur_indigo ) { indigo = cur_indigo; monomers_table = indigo.createArray(); output_reactions = indigo.createArray(); } public void clearReactantMonomers( int reactant_idx ) { IndigoObject reactant_monomers = monomers_table.at(reactant_idx); reactant_monomers.clear(); } public int getProductsCount() { return output_reactions.count(); } public IndigoObject getOutReaction( int idx ) { if (idx >= output_reactions.count()) return null; return output_reactions.at(idx); } public IndigoObject getOutProduct( int idx ) { if (idx >= output_reactions.count()) return null; IndigoObject rxn = output_reactions.at(idx); for (IndigoObject iterr : rxn.iterateProducts()) { return iterr; } return null; } public String getOutReactionString( int idx ) { if (idx >= output_reactions.count()) return null; return output_reactions.at(idx).rxnfile(); } public IndigoObject getReaction () { return reaction; } public String getOutProductString( int idx ) { if (idx >= output_reactions.count()) return null; IndigoObject rxn = output_reactions.at(idx); for (IndigoObject iterr : rxn.iterateProducts()) { return iterr.molfile(); } return null; } public int getMonomersCount( int reactant_idx ) { return monomers_table.at(reactant_idx).count(); } public IndigoObject getMonomer( int reactant_idx, int mon_idx ) { return monomers_table.at(reactant_idx).at(mon_idx); } public String getMonomerString( int reactant_idx, int mon_idx ) { if (reactant_idx >= monomers_table.count()) return null; if (mon_idx >= monomers_table.at(reactant_idx).count()) return null; return monomers_table.at(reactant_idx).at(mon_idx).molfile(); } public void setMonomers ( int reatcnt_idx, ArrayList mols) throws IndigoCheckedException { monomers_table.at(reatcnt_idx).clear(); for (IndigoObjectWrapper iterr : mols) { monomers_table.at(reatcnt_idx).arrayAdd(iterr.getObjectCopy()); } } public void setReactionFromFile( String rxn_path ) { if (rxn_path.toLowerCase().endsWith("sma") || rxn_path.toLowerCase().endsWith("smarts")) reaction = indigo.loadReactionSmartsFromFile(rxn_path); else reaction = indigo.loadQueryReactionFromFile(rxn_path); for (int i = 0; i < reaction.countReactants(); i++) monomers_table.arrayAdd(indigo.createArray()); } public int getReactantsCount() { if (reaction == null) return 0; return reaction.countReactants(); } public void clear() { monomers_table.clear(); output_reactions.clear(); } public void react() throws IOException { output_reactions = indigo.reactionProductEnumerate(reaction, monomers_table); for ( int i = 0; i < output_reactions.count(); i++) { for (IndigoObject iterr : output_reactions.at(i).iterateReactants()) { iterr.layout(); } for (IndigoObject iterr : output_reactions.at(i).iterateProducts()) { iterr.layout(); } } } } Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/legio/Main.java000066400000000000000000000016021271037650300250250ustar00rootroot00000000000000package com.epam.indigo.legio; import com.epam.indigo.controls.MessageBox; import java.io.PrintWriter; import java.io.StringWriter; import javax.swing.UIManager; public class Main { public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { System.out.println("Error setting native LAF: " + e); } try { MainFrame mf = new MainFrame(); mf.setLocationRelativeTo(null); mf.setVisible(true); } catch (Throwable err) { StringWriter sw = new StringWriter(); err.printStackTrace(new PrintWriter(sw)); String error_as_string = sw.toString(); MessageBox.show(null, error_as_string, "Error", MessageBox.ICON_ERROR); System.exit(0); } } } Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/legio/MainFrame.form000066400000000000000000000543101271037650300260260ustar00rootroot00000000000000
Indigo-indigo-1.2.3/utils/legio/src/com/epam/indigo/legio/MainFrame.java000066400000000000000000000747211271037650300260140ustar00rootroot00000000000000package com.epam.indigo.legio; import com.epam.indigo.Indigo; import com.epam.indigo.IndigoObject; import com.epam.indigo.IndigoRenderer; import com.epam.indigo.controls.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import javax.swing.GroupLayout.Group; import javax.swing.*; public class MainFrame extends javax.swing.JFrame { public static final int LARGE_HEIGHT = 250; private static final int COMPACT_HEIGHT = 70; private static final int MEDIUM_HEIGHT = 160; ArrayList mon_panels; int reactants_count; LegioData legio; Indigo indigo; IndigoRenderer indigo_renderer; int active_row_height; static int mon_panel_idx; public static void setNativeLookAndFeel() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { System.out.println("Error setting native LAF: " + e); } } public static String getPathToJarfileDir(Class classToUse) { String url = classToUse.getResource("/" + classToUse.getName().replaceAll("\\.", "/") + ".class").toString(); url = url.substring(4).replaceFirst("/[^/]+\\.jar!.*$", "/"); try { File dir = new File(new URL(url).toURI()); url = dir.getAbsolutePath(); } catch (MalformedURLException mue) { System.err.println(mue.getMessage()); url = null; } catch (URISyntaxException ue) { System.err.println(ue.getMessage()); url = null; } return url; } /* Creates new form MainFrame */ public MainFrame() { indigo = Global.indigo; indigo_renderer = Global.indigo_renderer; mon_panels = new ArrayList(); legio = new LegioData(indigo); indigo.setOption("filename-encoding", "UTF-8"); indigo.setOption("render-margins", "5,2"); initComponents(); //rct_view.init(indigo, indigo_renderer); //products_panel.init(indigo, indigo_renderer, legio); max_products_label.setEnabled(false); max_products_text_field.setEnabled(false); max_steps_label.setEnabled(false); max_steps_text_field.setEnabled(false); react_button.setEnabled(false); is_multistep_reactions_check.setEnabled(false); is_one_tube_check.setEnabled(false); is_self_react_check.setEnabled(false); reaction_path_label.setEditable(false); setRowHeight(MEDIUM_HEIGHT); setTitle("Legio"); } /** This method is called from within the constructor to * initialize the form. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { tabbed_panel = new javax.swing.JTabbedPane(); in_tab = new javax.swing.JPanel(); split_panel = new javax.swing.JSplitPane(); rct_part = new javax.swing.JPanel(); reaction_label = new javax.swing.JLabel(); reaction_button = new javax.swing.JButton(); reaction_path_label = new javax.swing.JTextField(); rct_view = new com.epam.indigo.controls.IndigoObjectViewPanel(); mons_part = new javax.swing.JPanel(); enumeration_panel = new javax.swing.JPanel(); is_multistep_reactions_check = new java.awt.Checkbox(); react_button = new javax.swing.JButton(); max_products_text_field = new javax.swing.JTextField(); max_products_label = new javax.swing.JLabel(); max_steps_label = new javax.swing.JLabel(); max_steps_text_field = new javax.swing.JTextField(); is_one_tube_check = new java.awt.Checkbox(); is_self_react_check = new java.awt.Checkbox(); out_tab = new javax.swing.JPanel(); products_panel = new com.epam.indigo.controls.MoleculeOutputTable(); menu_bar = new javax.swing.JMenuBar(); file_menu = new javax.swing.JMenu(); exit_menu_item = new javax.swing.JMenuItem(); menu_view = new javax.swing.JMenu(); jMenu1 = new javax.swing.JMenu(); menu_view_compact = new javax.swing.JCheckBoxMenuItem(); menu_view_medium = new javax.swing.JCheckBoxMenuItem(); menu_view_large = new javax.swing.JCheckBoxMenuItem(); help_menu = new javax.swing.JMenu(); about_menu_item = new javax.swing.JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); split_panel.setDividerLocation(300); split_panel.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); rct_part.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); reaction_label.setText("Reaction:"); reaction_button.setText("Open"); reaction_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { reaction_buttonActionPerformed(evt); } }); javax.swing.GroupLayout rct_viewLayout = new javax.swing.GroupLayout(rct_view); rct_view.setLayout(rct_viewLayout); rct_viewLayout.setHorizontalGroup( rct_viewLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 930, Short.MAX_VALUE) ); rct_viewLayout.setVerticalGroup( rct_viewLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 260, Short.MAX_VALUE) ); javax.swing.GroupLayout rct_partLayout = new javax.swing.GroupLayout(rct_part); rct_part.setLayout(rct_partLayout); rct_partLayout.setHorizontalGroup( rct_partLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(rct_partLayout.createSequentialGroup() .addContainerGap() .addComponent(reaction_label) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(reaction_button) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(reaction_path_label, javax.swing.GroupLayout.DEFAULT_SIZE, 773, Short.MAX_VALUE) .addContainerGap(18, Short.MAX_VALUE)) .addComponent(rct_view, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); rct_partLayout.setVerticalGroup( rct_partLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(rct_partLayout.createSequentialGroup() .addGroup(rct_partLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(reaction_label) .addComponent(reaction_button, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(reaction_path_label)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rct_view, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); split_panel.setTopComponent(rct_part); javax.swing.GroupLayout mons_partLayout = new javax.swing.GroupLayout(mons_part); mons_part.setLayout(mons_partLayout); mons_partLayout.setHorizontalGroup( mons_partLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 932, Short.MAX_VALUE) ); mons_partLayout.setVerticalGroup( mons_partLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 297, Short.MAX_VALUE) ); split_panel.setRightComponent(mons_part); enumeration_panel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); enumeration_panel.setPreferredSize(new java.awt.Dimension(777, 50)); is_multistep_reactions_check.setLabel("multistep reactions"); is_multistep_reactions_check.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { is_multistep_reactions_checkItemStateChanged(evt); } }); react_button.setText("Enumerate"); react_button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { react_buttonActionPerformed(evt); } }); max_products_text_field.setText("1000"); max_products_text_field.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { max_products_text_fieldActionPerformed(evt); } }); max_products_label.setText("Maximum products:"); max_steps_label.setText("Maximum number of steps:"); max_steps_text_field.setText("3"); is_one_tube_check.setLabel("one tube"); is_one_tube_check.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { is_one_tube_checkItemStateChanged(evt); } }); is_self_react_check.setLabel("self reactions"); is_self_react_check.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { is_self_react_checkItemStateChanged(evt); } }); javax.swing.GroupLayout enumeration_panelLayout = new javax.swing.GroupLayout(enumeration_panel); enumeration_panel.setLayout(enumeration_panelLayout); enumeration_panelLayout.setHorizontalGroup( enumeration_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(enumeration_panelLayout.createSequentialGroup() .addContainerGap() .addComponent(max_steps_label) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(max_steps_text_field, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(max_products_label) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(max_products_text_field, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(21, 21, 21) .addComponent(is_multistep_reactions_check, javax.swing.GroupLayout.PREFERRED_SIZE, 123, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(is_one_tube_check, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(is_self_react_check, javax.swing.GroupLayout.PREFERRED_SIZE, 93, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 87, Short.MAX_VALUE) .addComponent(react_button, javax.swing.GroupLayout.PREFERRED_SIZE, 132, javax.swing.GroupLayout.PREFERRED_SIZE)) ); enumeration_panelLayout.setVerticalGroup( enumeration_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(enumeration_panelLayout.createSequentialGroup() .addGroup(enumeration_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(react_button, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(enumeration_panelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(max_steps_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(max_steps_text_field, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(max_products_label, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(max_products_text_field, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(is_self_react_check, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(is_one_tube_check, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(is_multistep_reactions_check, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); javax.swing.GroupLayout in_tabLayout = new javax.swing.GroupLayout(in_tab); in_tab.setLayout(in_tabLayout); in_tabLayout.setHorizontalGroup( in_tabLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(split_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 942, Short.MAX_VALUE) .addGroup(in_tabLayout.createSequentialGroup() .addComponent(enumeration_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 924, Short.MAX_VALUE) .addGap(10, 10, 10)) ); in_tabLayout.setVerticalGroup( in_tabLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, in_tabLayout.createSequentialGroup() .addComponent(split_panel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(enumeration_panel, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)) ); tabbed_panel.addTab("Reaction", in_tab); out_tab.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); products_panel.setIdColumnCount(1); products_panel.setReactionsContentType(true); javax.swing.GroupLayout out_tabLayout = new javax.swing.GroupLayout(out_tab); out_tab.setLayout(out_tabLayout); out_tabLayout.setHorizontalGroup( out_tabLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(products_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 924, Short.MAX_VALUE) ); out_tabLayout.setVerticalGroup( out_tabLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(products_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 638, Short.MAX_VALUE) ); tabbed_panel.addTab("Products", out_tab); file_menu.setText("File"); exit_menu_item.setText("Exit"); exit_menu_item.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { exit_menu_itemActionPerformed(evt); } }); file_menu.add(exit_menu_item); menu_bar.add(file_menu); menu_view.setText("View"); jMenu1.setText("Layout size"); menu_view_compact.setText("Compact"); menu_view_compact.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { menu_view_compactActionPerformed(evt); } }); jMenu1.add(menu_view_compact); menu_view_medium.setSelected(true); menu_view_medium.setText("Medium"); menu_view_medium.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { menu_view_mediumActionPerformed(evt); } }); jMenu1.add(menu_view_medium); menu_view_large.setText("Large"); menu_view_large.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { menu_view_largeActionPerformed(evt); } }); jMenu1.add(menu_view_large); menu_view.add(jMenu1); menu_bar.add(menu_view); help_menu.setText("Help"); about_menu_item.setText("About"); about_menu_item.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { about_menu_itemActionPerformed(evt); } }); help_menu.add(about_menu_item); menu_bar.add(help_menu); setJMenuBar(menu_bar); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(tabbed_panel, javax.swing.GroupLayout.DEFAULT_SIZE, 939, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(tabbed_panel) ); pack(); }// //GEN-END:initComponents private void reaction_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reaction_buttonActionPerformed try { FileOpener fopener = new FileOpener(); fopener.addExtension("rxn", "rdf", "cml", "sma", "smarts"); if (fopener.openFile("Open") == null) { return; } File choosed_file = fopener.getFile(); GlobalParams.getInstance().dir_path = choosed_file.getParent(); String file_path = choosed_file.getAbsolutePath(); reaction_path_label.setText(file_path); try { legio.setReactionFromFile(file_path); } catch (Exception ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); return; } rct_view.setIndigoObject(legio.getReaction(), indigo_renderer); int old_reactants_count = reactants_count; reactants_count = legio.getReactantsCount(); int min_count; if (old_reactants_count <= reactants_count) { min_count = old_reactants_count; for (int i = old_reactants_count; i < reactants_count; i++) { MoleculesInputTable input_table = new MoleculesInputTable(); input_table.setUseProxyObjects(false); input_table.setRowHeight(active_row_height); mon_panels.add(input_table); } } else { min_count = reactants_count; for (int i = old_reactants_count - 1; i >= reactants_count; i--) { legio.clearReactantMonomers(i); mon_panels.remove(i); } } mons_part.removeAll(); /* * Setting layout */ GroupLayout gl_mc = new GroupLayout(mons_part); mons_part.setLayout(gl_mc); gl_mc.setAutoCreateGaps(true); Group h_group = gl_mc.createSequentialGroup(); Group v_group = gl_mc.createParallelGroup(GroupLayout.Alignment.LEADING); for (mon_panel_idx = 0; mon_panel_idx < reactants_count; mon_panel_idx++) { h_group.addComponent(mon_panels.get(mon_panel_idx), GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE); v_group.addComponent(mon_panels.get(mon_panel_idx), GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE); } gl_mc.setHorizontalGroup(h_group); gl_mc.setVerticalGroup(v_group); products_panel.clear(); mons_part.updateUI(); react_button.setEnabled(true); is_multistep_reactions_check.setEnabled(true); is_one_tube_check.setEnabled(true); is_self_react_check.setEnabled(true); max_products_label.setEnabled(true); max_products_text_field.setEnabled(true); } catch (Exception ex) { StringWriter sw = new StringWriter(); ex.printStackTrace(new PrintWriter(sw)); String error_as_string = sw.toString(); MessageBox.show(null, error_as_string, "Error", MessageBox.ICON_ERROR); } }//GEN-LAST:event_reaction_buttonActionPerformed private void react_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_react_buttonActionPerformed try { int max_depth = Integer.parseInt(max_steps_text_field.getText()); indigo.setOption("rpe-max-depth", max_depth); int max_pr_count = Integer.parseInt(max_products_text_field.getText()); indigo.setOption("rpe-max-products-count", max_pr_count); indigo.setOption("rpe-multistep-reactions", is_multistep_reactions_check.getState()); if (is_one_tube_check.getState()) indigo.setOption("rpe-mode", "one-tube"); else indigo.setOption("rpe-mode", "grid"); indigo.setOption("rpe-self-reaction", is_self_react_check.getState()); // Add reactans for (int i = 0; i < reactants_count; i++) { ArrayList mols = mon_panels.get(i).getMolecules(); legio.setMonomers(i, mols); } legio.react(); ArrayList mol_objects = new ArrayList(); int mol_cnt = 0; for (int i = 0; i < legio.getProductsCount(); i++) { IndigoObject mol = legio.getOutReaction(i); IndigoObjectWrapper wrapper = new PureIndigoObject(mol); mol_cnt++; mol_objects.add(new MoleculeItem(wrapper, String.format("#%d", mol_cnt))); } products_panel.setMolecules(mol_objects); tabbed_panel.setSelectedIndex(1); } catch (Exception ex) { JOptionPane.showMessageDialog((JFrame) (out_tab.getTopLevelAncestor()), ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } }//GEN-LAST:event_react_buttonActionPerformed private void is_multistep_reactions_checkItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_is_multistep_reactions_checkItemStateChanged max_steps_label.setEnabled(is_multistep_reactions_check.getState()); max_steps_text_field.setEnabled(is_multistep_reactions_check.getState()); }//GEN-LAST:event_is_multistep_reactions_checkItemStateChanged private void is_one_tube_checkItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_is_one_tube_checkItemStateChanged // TODO add your handling code here: }//GEN-LAST:event_is_one_tube_checkItemStateChanged private void is_self_react_checkItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_is_self_react_checkItemStateChanged // TODO add your handling code here: }//GEN-LAST:event_is_self_react_checkItemStateChanged private void max_products_text_fieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_max_products_text_fieldActionPerformed // TODO add your handling code here: }//GEN-LAST:event_max_products_text_fieldActionPerformed private void exit_menu_itemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exit_menu_itemActionPerformed dispose(); }//GEN-LAST:event_exit_menu_itemActionPerformed private void about_menu_itemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_about_menu_itemActionPerformed CommonUtils.showAboutDialog(this, "Legio", "http://epam.com/opensource/indigo/legio"); }//GEN-LAST:event_about_menu_itemActionPerformed private void menu_view_compactActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menu_view_compactActionPerformed onLayoutSizeChanged(evt); }//GEN-LAST:event_menu_view_compactActionPerformed private void menu_view_mediumActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menu_view_mediumActionPerformed onLayoutSizeChanged(evt); }//GEN-LAST:event_menu_view_mediumActionPerformed private void menu_view_largeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menu_view_largeActionPerformed onLayoutSizeChanged(evt); }//GEN-LAST:event_menu_view_largeActionPerformed private void onLayoutSizeChanged(java.awt.event.ActionEvent evt) { menu_view_compact.setState(false); menu_view_medium.setState(false); menu_view_large.setState(false); if (evt.getSource() == menu_view_compact) { setRowHeight(COMPACT_HEIGHT); menu_view_compact.setState(true); } else if (evt.getSource() == menu_view_medium) { setRowHeight(MEDIUM_HEIGHT); menu_view_medium.setState(true); } else { setRowHeight(LARGE_HEIGHT); menu_view_large.setState(true); } } private void setRowHeight(int height) { products_panel.setRowHeight(height); for (MoleculesInputTable panel: mon_panels) panel.setRowHeight(height); active_row_height = height; } class SaveProductsEventListener implements ActionListener { public void actionPerformed(ActionEvent event) { try { JFileChooser file_chooser = new JFileChooser(); MolFileFilter sdf_ff = new MolFileFilter(); sdf_ff.addExtension("sdf"); sdf_ff.addExtension("sd"); file_chooser.setFileFilter(sdf_ff); file_chooser.setApproveButtonText("Save"); file_chooser.setCurrentDirectory(new File(GlobalParams.getInstance().dir_path)); int ret_val = file_chooser.showSaveDialog(out_tab); File out_file = file_chooser.getSelectedFile(); if ((out_file == null) || (ret_val != JFileChooser.APPROVE_OPTION)) { return; } GlobalParams.getInstance().dir_path = out_file.getParent(); String out_file_path = out_file.getPath(); if (!out_file_path.endsWith(".sdf") && !out_file_path.endsWith(".sd")) { out_file_path += ".sdf"; } FileWriter out_fstream = new FileWriter(out_file_path); for (int i = 0; i < legio.getProductsCount(); i++) { String product_str = legio.getOutProductString(i); out_fstream.write(product_str); out_fstream.write("$$$$\n"); } out_fstream.close(); } catch (Exception ex) { JOptionPane.showMessageDialog((JFrame) (out_tab.getTopLevelAncestor()), ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } class SaveReactionsEventListener implements ActionListener { public void actionPerformed(ActionEvent event) { try { JFileChooser file_chooser = new JFileChooser(); MolFileFilter sdf_ff = new MolFileFilter(); sdf_ff.addExtension("rdf"); file_chooser.setFileFilter(sdf_ff); file_chooser.setApproveButtonText("Save"); file_chooser.setCurrentDirectory(new File(GlobalParams.getInstance().dir_path)); int ret_val = file_chooser.showSaveDialog(out_tab); File out_file = file_chooser.getSelectedFile(); if ((out_file == null) || (ret_val != JFileChooser.APPROVE_OPTION)) { return; } GlobalParams.getInstance().dir_path = out_file.getParent(); String out_file_path = out_file.getPath(); if (!out_file_path.endsWith(".rdf")) { out_file_path += ".rdf"; } FileWriter out_fstream = new FileWriter(out_file_path); out_fstream.write("$RDFILE 1\n$DATM 1\n"); for (int i = 0; i < legio.getProductsCount(); i++) { out_fstream.write("$RFMT\n"); String reaction_str = legio.getOutReactionString(i); out_fstream.write(reaction_str); } out_fstream.close(); } catch (Exception ex) { JOptionPane.showMessageDialog((JFrame) (out_tab.getTopLevelAncestor()), ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem about_menu_item; private javax.swing.JPanel enumeration_panel; private javax.swing.JMenuItem exit_menu_item; private javax.swing.JMenu file_menu; private javax.swing.JMenu help_menu; private javax.swing.JPanel in_tab; private java.awt.Checkbox is_multistep_reactions_check; private java.awt.Checkbox is_one_tube_check; private java.awt.Checkbox is_self_react_check; private javax.swing.JMenu jMenu1; private javax.swing.JLabel max_products_label; private javax.swing.JTextField max_products_text_field; private javax.swing.JLabel max_steps_label; private javax.swing.JTextField max_steps_text_field; private javax.swing.JMenuBar menu_bar; private javax.swing.JMenu menu_view; private javax.swing.JCheckBoxMenuItem menu_view_compact; private javax.swing.JCheckBoxMenuItem menu_view_large; private javax.swing.JCheckBoxMenuItem menu_view_medium; private javax.swing.JPanel mons_part; private javax.swing.JPanel out_tab; private com.epam.indigo.controls.MoleculeOutputTable products_panel; private javax.swing.JPanel rct_part; private com.epam.indigo.controls.IndigoObjectViewPanel rct_view; private javax.swing.JButton react_button; private javax.swing.JButton reaction_button; private javax.swing.JLabel reaction_label; private javax.swing.JTextField reaction_path_label; private javax.swing.JSplitPane split_panel; private javax.swing.JTabbedPane tabbed_panel; // End of variables declaration//GEN-END:variables }